311 lines
11 KiB
Java
311 lines
11 KiB
Java
/*******************************************************************************
|
|
* Copyright (c) 2007 IBM Corporation.
|
|
* All rights reserved. This program and the accompanying materials
|
|
* are made available under the terms of the Eclipse Public License v1.0
|
|
* which accompanies this distribution, and is available at
|
|
* http://www.eclipse.org/legal/epl-v10.html
|
|
*
|
|
* Contributors:
|
|
* IBM Corporation - initial API and implementation
|
|
*******************************************************************************/
|
|
package com.ibm.wala.analysis.reflection.java7;
|
|
|
|
import java.lang.ref.SoftReference;
|
|
import java.util.Iterator;
|
|
import java.util.Map;
|
|
|
|
import com.ibm.wala.cfg.ControlFlowGraph;
|
|
import com.ibm.wala.classLoader.CallSiteReference;
|
|
import com.ibm.wala.classLoader.IClass;
|
|
import com.ibm.wala.classLoader.IMethod;
|
|
import com.ibm.wala.classLoader.NewSiteReference;
|
|
import com.ibm.wala.ipa.callgraph.CGNode;
|
|
import com.ibm.wala.ipa.callgraph.Context;
|
|
import com.ibm.wala.ipa.callgraph.ContextItem;
|
|
import com.ibm.wala.ipa.callgraph.ContextKey;
|
|
import com.ibm.wala.ipa.callgraph.ContextSelector;
|
|
import com.ibm.wala.ipa.callgraph.propagation.ConstantKey;
|
|
import com.ibm.wala.ipa.callgraph.propagation.InstanceKey;
|
|
import com.ibm.wala.ipa.callgraph.propagation.SSAContextInterpreter;
|
|
import com.ibm.wala.ipa.summaries.MethodSummary;
|
|
import com.ibm.wala.ipa.summaries.SummarizedMethod;
|
|
import com.ibm.wala.shrikeBT.IInvokeInstruction.Dispatch;
|
|
import com.ibm.wala.ssa.ConstantValue;
|
|
import com.ibm.wala.ssa.DefUse;
|
|
import com.ibm.wala.ssa.IR;
|
|
import com.ibm.wala.ssa.ISSABasicBlock;
|
|
import com.ibm.wala.ssa.SSAFieldAccessInstruction;
|
|
import com.ibm.wala.ssa.SSAGetInstruction;
|
|
import com.ibm.wala.ssa.SSAInstruction;
|
|
import com.ibm.wala.ssa.SSAInstructionFactory;
|
|
import com.ibm.wala.ssa.SSAOptions;
|
|
import com.ibm.wala.ssa.SSAPutInstruction;
|
|
import com.ibm.wala.types.FieldReference;
|
|
import com.ibm.wala.types.MethodReference;
|
|
import com.ibm.wala.types.TypeReference;
|
|
import com.ibm.wala.util.collections.Filter;
|
|
import com.ibm.wala.util.collections.FilterIterator;
|
|
import com.ibm.wala.util.collections.HashMapFactory;
|
|
import com.ibm.wala.util.collections.MapIterator;
|
|
import com.ibm.wala.util.functions.Function;
|
|
import com.ibm.wala.util.intset.IntSet;
|
|
import com.ibm.wala.util.intset.IntSetUtil;
|
|
|
|
@SuppressWarnings("deprecation")
|
|
public class MethodHandles {
|
|
|
|
private static final IntSet self = IntSetUtil.make(new int[0]);
|
|
|
|
private static ContextKey METHOD_KEY = new ContextKey() {
|
|
@Override
|
|
public String toString() {
|
|
return "METHOD_KEY";
|
|
}
|
|
};
|
|
|
|
public static class MethodItem implements ContextItem {
|
|
private final MethodReference method;
|
|
|
|
public MethodItem(MethodReference method) {
|
|
super();
|
|
this.method = method;
|
|
}
|
|
|
|
public MethodReference getMethod() {
|
|
return method;
|
|
}
|
|
|
|
@Override
|
|
public int hashCode() {
|
|
final int prime = 31;
|
|
int result = 1;
|
|
result = prime * result + ((method == null) ? 0 : method.hashCode());
|
|
return result;
|
|
}
|
|
|
|
@Override
|
|
public boolean equals(Object obj) {
|
|
if (this == obj)
|
|
return true;
|
|
if (obj == null)
|
|
return false;
|
|
if (getClass() != obj.getClass())
|
|
return false;
|
|
MethodItem other = (MethodItem) obj;
|
|
if (method == null) {
|
|
if (other.method != null)
|
|
return false;
|
|
} else if (!method.equals(other.method))
|
|
return false;
|
|
return true;
|
|
}
|
|
}
|
|
|
|
public static class MethodContext implements Context {
|
|
private final Context base;
|
|
private final MethodReference method;
|
|
|
|
public MethodContext(Context base, MethodReference method) {
|
|
this.base = base;
|
|
this.method = method;
|
|
}
|
|
|
|
@Override
|
|
public ContextItem get(ContextKey name) {
|
|
if (METHOD_KEY.equals(name)) {
|
|
return new MethodItem(method);
|
|
} else {
|
|
return base.get(name);
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public int hashCode() {
|
|
final int prime = 31;
|
|
int result = 1;
|
|
result = prime * result + ((base == null) ? 0 : base.hashCode());
|
|
result = prime * result + ((method == null) ? 0 : method.hashCode());
|
|
return result;
|
|
}
|
|
|
|
@Override
|
|
public boolean equals(Object obj) {
|
|
if (this == obj)
|
|
return true;
|
|
if (obj == null)
|
|
return false;
|
|
if (getClass() != obj.getClass())
|
|
return false;
|
|
MethodContext other = (MethodContext) obj;
|
|
if (base == null) {
|
|
if (other.base != null)
|
|
return false;
|
|
} else if (!base.equals(other.base))
|
|
return false;
|
|
if (method == null) {
|
|
if (other.method != null)
|
|
return false;
|
|
} else if (!method.equals(other.method))
|
|
return false;
|
|
return true;
|
|
}
|
|
|
|
@Override
|
|
public String toString() {
|
|
return "ctxt:" + method.getName();
|
|
}
|
|
}
|
|
|
|
public static class ContextSelectorImpl implements ContextSelector {
|
|
private final ContextSelector base;
|
|
|
|
public ContextSelectorImpl(ContextSelector base) {
|
|
this.base = base;
|
|
}
|
|
|
|
@Override
|
|
public Context getCalleeTarget(CGNode caller, CallSiteReference site, IMethod callee, InstanceKey[] actualParameters) {
|
|
Context baseContext = base.getCalleeTarget(caller, site, callee, actualParameters);
|
|
if ((isInvoke(callee) || isType(callee)) && callee.getDeclaringClass().getReference().equals(TypeReference.JavaLangInvokeMethodHandle)) {
|
|
if (actualParameters != null && actualParameters.length > 0) {
|
|
InstanceKey selfKey = actualParameters[0];
|
|
if (selfKey instanceof ConstantKey && ((ConstantKey)selfKey).getConcreteType().getReference().equals(TypeReference.JavaLangInvokeMethodHandle)) {
|
|
MethodReference ref = ((IMethod) ((ConstantKey)selfKey).getValue()).getReference();
|
|
return new MethodContext(baseContext, ref);
|
|
}
|
|
}
|
|
}
|
|
return baseContext;
|
|
}
|
|
|
|
@Override
|
|
public IntSet getRelevantParameters(CGNode caller, CallSiteReference site) {
|
|
return self;
|
|
}
|
|
}
|
|
|
|
private static boolean isInvoke(IMethod node) {
|
|
return node.getName().toString().startsWith("invoke");
|
|
}
|
|
|
|
private static boolean isType(IMethod node) {
|
|
return node.getName().toString().equals("type");
|
|
}
|
|
|
|
private static boolean isInvoke(CGNode node) {
|
|
return isInvoke(node.getMethod());
|
|
}
|
|
|
|
private static boolean isType(CGNode node) {
|
|
return isType(node.getMethod());
|
|
}
|
|
|
|
public static class ContextInterpreterImpl implements SSAContextInterpreter {
|
|
private final Map<CGNode, SoftReference<IR>> irs = HashMapFactory.make();
|
|
|
|
@Override
|
|
public Iterator<NewSiteReference> iterateNewSites(CGNode node) {
|
|
return getIR(node).iterateNewSites();
|
|
}
|
|
|
|
public Iterator<FieldReference> iterateFields(CGNode node, Filter<SSAInstruction> filter) {
|
|
return
|
|
new MapIterator<SSAInstruction,FieldReference>(
|
|
new FilterIterator<SSAInstruction>(getIR(node).iterateNormalInstructions(), filter),
|
|
new Function<SSAInstruction,FieldReference>() {
|
|
@Override
|
|
public FieldReference apply(SSAInstruction object) {
|
|
return ((SSAFieldAccessInstruction)object).getDeclaredField();
|
|
}
|
|
});
|
|
}
|
|
|
|
@Override
|
|
public Iterator<FieldReference> iterateFieldsRead(CGNode node) {
|
|
return iterateFields(node, new Filter<SSAInstruction>() {
|
|
@Override
|
|
public boolean accepts(SSAInstruction o) {
|
|
return o instanceof SSAGetInstruction;
|
|
}
|
|
});
|
|
}
|
|
|
|
@Override
|
|
public Iterator<FieldReference> iterateFieldsWritten(CGNode node) {
|
|
return iterateFields(node, new Filter<SSAInstruction>() {
|
|
@Override
|
|
public boolean accepts(SSAInstruction o) {
|
|
return o instanceof SSAPutInstruction;
|
|
}
|
|
});
|
|
}
|
|
|
|
@Override
|
|
public boolean recordFactoryType(CGNode node, IClass klass) {
|
|
return false;
|
|
}
|
|
|
|
@Override
|
|
public boolean understands(CGNode node) {
|
|
return (isInvoke(node) || isType(node)) && node.getContext() instanceof MethodContext;
|
|
}
|
|
|
|
@Override
|
|
public Iterator<CallSiteReference> iterateCallSites(CGNode node) {
|
|
return getIR(node).iterateCallSites();
|
|
}
|
|
|
|
@Override
|
|
public IR getIR(CGNode node) {
|
|
if (!irs.containsKey(node) || irs.get(node).get() == null) {
|
|
MethodSummary code = new MethodSummary(node.getMethod().getReference());
|
|
SummarizedMethod m = new SummarizedMethod(node.getMethod().getReference(), code, node.getMethod().getDeclaringClass());
|
|
SSAInstructionFactory insts = node.getMethod().getDeclaringClass().getClassLoader().getLanguage().instructionFactory();
|
|
assert node.getContext() instanceof MethodContext;
|
|
MethodReference ref = ((MethodContext)node.getContext()).method;
|
|
boolean isStatic = node.getClassHierarchy().resolveMethod(ref).isStatic();
|
|
if (isInvoke(node)) {
|
|
String name = node.getMethod().getName().toString();
|
|
if ("invokeWithArguments".equals(name)) {
|
|
int nargs = ref.getNumberOfParameters();
|
|
int params[] = new int[nargs];
|
|
for(int i = 0; i < nargs; i++) {
|
|
code.addConstant(i+nargs+3, new ConstantValue(i));
|
|
code.addStatement(insts.ArrayLoadInstruction(code.getNextProgramCounter(), i+3, 1, i+nargs+3, TypeReference.JavaLangObject));
|
|
params[i] = i+3;
|
|
}
|
|
CallSiteReference site = CallSiteReference.make(nargs+1, ref, isStatic? Dispatch.STATIC: Dispatch.SPECIAL);
|
|
code.addStatement(insts.InvokeInstruction(code.getNextProgramCounter(), 2*nargs+3, params, 2*nargs+4, site));
|
|
code.addStatement(insts.ReturnInstruction(code.getNextProgramCounter(), 2*nargs+3, false));
|
|
} else {
|
|
int nargs = node.getMethod().getNumberOfParameters();
|
|
}
|
|
} else {
|
|
assert isType(node);
|
|
code.addStatement(insts.LoadMetadataInstruction(code.getNextProgramCounter(), 2, TypeReference.JavaLangInvokeMethodType, ref.getDescriptor()));
|
|
code.addStatement(insts.ReturnInstruction(code.getNextProgramCounter(), 2, false));
|
|
}
|
|
irs.put(node, new SoftReference<IR>(m.makeIR(node.getContext(), SSAOptions.defaultOptions())));
|
|
}
|
|
|
|
return irs.get(node).get();
|
|
}
|
|
|
|
@Override
|
|
public DefUse getDU(CGNode node) {
|
|
return new DefUse(getIR(node));
|
|
}
|
|
|
|
@Override
|
|
public int getNumberOfStatements(CGNode node) {
|
|
return getIR(node).getInstructions().length;
|
|
}
|
|
|
|
@Override
|
|
public ControlFlowGraph<SSAInstruction, ISSABasicBlock> getCFG(CGNode n) {
|
|
return getIR(n).getControlFlowGraph();
|
|
}
|
|
|
|
}
|
|
}
|