commit GetMethod context, interpreter and selector

This commit is contained in:
Michael Heilmann 2013-09-17 06:57:03 +02:00
parent 51d4b0e462
commit 6c5ef65d5f
4 changed files with 545 additions and 0 deletions

View File

@ -0,0 +1,116 @@
/* Copyright (c) 2013 Michael Heilmann */
package com.ibm.wala.analysis.reflection.ext;
import com.ibm.wala.analysis.typeInference.PointType;
import com.ibm.wala.analysis.typeInference.TypeAbstraction;
import com.ibm.wala.classLoader.IClass;
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.propagation.ConstantKey;
import com.ibm.wala.ipa.callgraph.propagation.FilteredPointerKey;
/**
* @brief
* A context which may only be used if the following is true:
* - The method to be interpreted is either
* {@link java.lang.Class#getMethod(String, Class...)} or
* {@link java.lang.Class#getDeclaredMethod(String, Class...)}.
* - The type of the "this" argument is known.
* - The value of the first argument (the method name) is a constant.
* @author
* Michael Heilmann
*/
public class GetMethodContext implements Context {
/**
* @brief
* The type abstraction.
*/
private final TypeAbstraction type;
/**
* @brief
* The method name.
*/
private final ConstantKey name;
/**
* @brief
* Construct this GetMethodContext.
* @param type
* The type.
* @param name
* The name of the method.
*/
public GetMethodContext(TypeAbstraction type,ConstantKey name) {
if (type == null) {
throw new IllegalArgumentException("null == type");
}
this.type = type;
if (name == null) {
throw new IllegalArgumentException("null == name");
}
this.name = name;
}
@Override
public ContextItem get(ContextKey name) {
if (name == ContextKey.RECEIVER) {
return type;
} else if (name == ContextKey.PARAMETERS[0]) {
if (type instanceof PointType) {
IClass cls = ((PointType) type).getIClass();
return new FilteredPointerKey.SingleClassFilter(cls);
} else {
return null;
}
} else if (name == ContextKey.PARAMETERS[1]) {
return new FilteredPointerKey.SingleClassFilter(this.name.getConcreteType());
} else {
return null;
}
}
@Override
public String toString() {
return "GetMethodContext<" + type + ", " + name + ">";
}
@Override
public int hashCode() {
return 6367 * type.hashCode() * name.hashCode();
}
@Override
public boolean equals(Object obj) {
if (obj == null) {
return false;
}
if (getClass().equals(obj.getClass())) {
GetMethodContext other = (GetMethodContext) obj;
return type.equals(other.type) && name.equals(other.name);
} else {
return false;
}
}
/**
* @brief
* Get the type.
* @return
* The type.
*/
public TypeAbstraction getType() {
return type;
}
/**
* @brief
* Get the name.
* @return
* The name.
*/
public String getName() {
return (String)name.getValue();
}
}

View File

@ -0,0 +1,251 @@
/* Copyright (c) 2013 Michael Heilmann */
package com.ibm.wala.analysis.reflection.ext;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
import java.util.Map;
import com.ibm.wala.cfg.ControlFlowGraph;
import com.ibm.wala.cfg.InducedCFG;
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.propagation.SSAContextInterpreter;
import com.ibm.wala.ipa.summaries.SyntheticIR;
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.SSAInstruction;
import com.ibm.wala.ssa.SSAInstructionFactory;
import com.ibm.wala.ssa.SSAOptions;
import com.ibm.wala.ssa.SSAReturnInstruction;
import com.ibm.wala.types.FieldReference;
import com.ibm.wala.types.MethodReference;
import com.ibm.wala.types.TypeReference;
import com.ibm.wala.util.collections.EmptyIterator;
import com.ibm.wala.util.collections.HashMapFactory;
import com.ibm.wala.util.collections.HashSetFactory;
import com.ibm.wala.util.collections.NonNullSingletonIterator;
import com.ibm.wala.util.debug.Assertions;
import com.ibm.wala.util.strings.Atom;
/**
* @brief
* Understands {@link com.ibm.wala.analysis.reflection.ext.GetMethodContext}.
* @author
* Michael Heilmann
*/
public class GetMethodContextInterpreter implements SSAContextInterpreter {
/**
* TODO
* MH: Hard-code those in {@link com.ibm.wala.types.TypeReference}?
*/
public final static MethodReference GET_METHOD = MethodReference.findOrCreate(TypeReference.JavaLangClass, "getMethod",
"(Ljava/lang/String;[Ljava/lang/Class;)Ljava/lang/reflect/Method;");
/**
* TODO
* MH: Hard-code those in {@link com.ibm.wala.types.TypeReference}?
*/
public final static MethodReference GET_DECLARED_METHOD = MethodReference.findOrCreate(TypeReference.JavaLangClass,
"getDeclaredMethod", "(Ljava/lang/String;[Ljava/lang/Class;)Ljava/lang/reflect/Method;");
private static final boolean DEBUG = false;
/*
* @see com.ibm.wala.ipa.callgraph.propagation.SSAContextInterpreter#getIR(com.ibm.wala.ipa.callgraph.CGNode)
*/
@Override
public IR getIR(CGNode node) {
if (node == null) {
throw new IllegalArgumentException("node is null");
}
assert understands(node);
if (DEBUG) {
System.err.println("generating IR for " + node);
}
IMethod method = node.getMethod();
GetMethodContext context = (GetMethodContext) node.getContext();
Map<Integer,ConstantValue> constants = HashMapFactory.make();
if (method.getReference().equals(GET_METHOD)) {
Atom name = Atom.findOrCreateAsciiAtom(context.getName());
SSAInstruction instrs[] = makeGetMethodStatements(context,constants,name);
return new SyntheticIR(method, context, new InducedCFG(instrs, method, context), instrs, SSAOptions.defaultOptions(), constants);
}
if (method.getReference().equals(GET_DECLARED_METHOD)) {
Atom name = Atom.findOrCreateAsciiAtom(context.getName());
SSAInstruction instrs[] = makeGetDeclaredMethodStatements(context,constants,name);
return new SyntheticIR(method, context, new InducedCFG(instrs, method, context), instrs, SSAOptions.defaultOptions(), constants);
}
Assertions.UNREACHABLE("Unexpected method " + node);
return null;
}
/*
* @see com.ibm.wala.ipa.callgraph.propagation.SSAContextInterpreter#getNumberOfStatements(com.ibm.wala.ipa.callgraph.CGNode)
*/
@Override
public int getNumberOfStatements(CGNode node) {
assert understands(node);
return getIR(node).getInstructions().length;
}
/*
* @see com.ibm.wala.ipa.callgraph.propagation.rta.RTAContextInterpreter#understands(com.ibm.wala.ipa.callgraph.CGNode)
*/
@Override
public boolean understands(CGNode node) {
if (node == null) {
throw new IllegalArgumentException("node is null");
}
if (!(node.getContext() instanceof GetMethodContext)) {
return false;
}
MethodReference mRef = node.getMethod().getReference();
return mRef.equals(GET_METHOD) || mRef.equals(GET_DECLARED_METHOD);
}
@Override
public Iterator<NewSiteReference> iterateNewSites(CGNode node) {
if (node == null) {
throw new IllegalArgumentException("node is null");
}
assert understands(node);
GetMethodContext context = (GetMethodContext) node.getContext();
TypeReference tr = context.getType().getTypeReference();
if (tr != null) {
return new NonNullSingletonIterator<NewSiteReference>(NewSiteReference.make(0, tr));
}
return EmptyIterator.instance();
}
@Override
public Iterator<CallSiteReference> iterateCallSites(CGNode node) {
assert understands(node);
return EmptyIterator.instance();
}
/**
* Get all non-constructor, non-class-initializer methods declared by a class
* if their name is equal to the specified name.
*/
private Collection<IMethod> getDeclaredNormalMethods(IClass cls,Atom name) {
Collection<IMethod> result = HashSetFactory.make();
for (IMethod m : cls.getDeclaredMethods()) {
if (!m.isInit() && !m.isClinit() && m.getSelector().getName().equals(name)) {
result.add(m);
}
}
return result;
}
/**
* Get all non-constructor, non-class-initializer methods declared by a class
* and all its superclasses if their name is equal to the specifed name.
*/
private Collection<IMethod> getAllNormalPublicMethods(IClass cls,Atom name) {
Collection<IMethod> result = HashSetFactory.make();
Collection<IMethod> allMethods = null;
allMethods = cls.getAllMethods();
for (IMethod m : allMethods) {
if (!m.isInit() && !m.isClinit() && m.isPublic() && m.getSelector().getName().equals(name)) {
result.add(m);
}
}
return result;
}
/**
* Create statements for methods like getMethod() and getDeclaredMethod(),
* which return a single method. This creates a return statement for each
* possible return value, each of which is a {@link ConstantValue} for an
* {@link IMethod}.
*
* @param returnValues the possible return values for this method.
*/
private SSAInstruction[] getParticularMethodStatements
(
MethodReference ref,
Collection<IMethod> returnValues,
GetMethodContext context,
Map<Integer, ConstantValue> constants
) {
ArrayList<SSAInstruction> statements = new ArrayList<SSAInstruction>();
int nextLocal = ref.getNumberOfParameters() + 2;
IClass cls = context.getType().getType();
SSAInstructionFactory insts = context.getType().getType().getClassLoader().getInstructionFactory();
if (cls != null) {
for (IMethod m : returnValues) {
int c = nextLocal++;
constants.put(c, new ConstantValue(m));
SSAReturnInstruction R = insts.ReturnInstruction(c, false);
statements.add(R);
}
} else {
// SJF: This is incorrect. TODO: fix and enable.
// SSAThrowInstruction t = insts.ThrowInstruction(retValue);
// statements.add(t);
}
SSAInstruction[] result = new SSAInstruction[statements.size()];
Iterator<SSAInstruction> it = statements.iterator();
for (int i = 0; i < result.length; i++) {
result[i] = it.next();
}
return result;
}
private SSAInstruction[] makeGetMethodStatements
(
GetMethodContext context,
Map<Integer,ConstantValue> constants,
Atom name
) {
IClass cls = context.getType().getType();
if (cls == null) {
return getParticularMethodStatements(GET_METHOD, null, context, constants);
} else {
return getParticularMethodStatements(GET_METHOD, getAllNormalPublicMethods(cls,name), context, constants);
}
}
/**
* create statements for getDeclaredMethod()
*/
private SSAInstruction[] makeGetDeclaredMethodStatements(GetMethodContext context, Map<Integer, ConstantValue> constants,Atom name) {
IClass cls = context.getType().getType();
if (cls == null) {
return getParticularMethodStatements(GET_DECLARED_METHOD, null, context, constants);
} else {
return getParticularMethodStatements(GET_DECLARED_METHOD, getDeclaredNormalMethods(cls,name), context, constants);
}
}
@Override
public boolean recordFactoryType(CGNode node, IClass klass) {
return false;
}
@Override
public Iterator<FieldReference> iterateFieldsRead(CGNode node) {
return EmptyIterator.instance();
}
@Override
public Iterator<FieldReference> iterateFieldsWritten(CGNode node) {
return EmptyIterator.instance();
}
@Override
public ControlFlowGraph<SSAInstruction,ISSABasicBlock> getCFG(CGNode N) {
return getIR(N).getControlFlowGraph();
}
@Override
public DefUse getDU(CGNode node) {
return new DefUse(getIR(node));
}
}

View File

@ -0,0 +1,157 @@
/* Copyright (c) 2013 Michael Heilmann */
package com.ibm.wala.analysis.reflection.ext;
import java.util.Collection;
import com.ibm.wala.analysis.reflection.JavaLangClassContextInterpreter;
import com.ibm.wala.analysis.typeInference.PointType;
import com.ibm.wala.classLoader.CallSiteReference;
import com.ibm.wala.classLoader.IClass;
import com.ibm.wala.classLoader.IMethod;
import com.ibm.wala.ipa.callgraph.CGNode;
import com.ibm.wala.ipa.callgraph.Context;
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.cha.IClassHierarchy;
import com.ibm.wala.ssa.IR;
import com.ibm.wala.ssa.SSAAbstractInvokeInstruction;
import com.ibm.wala.ssa.SymbolTable;
import com.ibm.wala.types.MethodReference;
import com.ibm.wala.types.TypeReference;
import com.ibm.wala.util.collections.HashSetFactory;
import com.ibm.wala.util.intset.EmptyIntSet;
import com.ibm.wala.util.intset.IntSet;
import com.ibm.wala.util.intset.IntSetUtil;
/**
* @brief
* Produces {@link com.ibm.wala.analysis.reflection.ext.GetMethodContext} if the following is true:
* - The method to be interpreted is either
* {@link java.lang.Class#getMethod(String, Class...)} or
* {@link java.lang.Class#getDeclaredMethod(String, Class...)}.
* - The type of the "this" argument is known.
* - The value of the first argument (the method name) is a constant.
* @author
* Michael Heilmann
*/
public class GetMethodContextSelector implements ContextSelector {
/**
* @brief
* If @a true, debug information is emitted.
*/
protected static final boolean DEBUG = false;
/**
* @brief
* If
* - the {@link CallSiteReference} invokes either {@link java.lang.Class#getMethod}
* or {@link java.lang.Class#getDeclaredMethod},
* - and the receiver is a type constant and
* - the first argument is a constant,
* then return a {@link GetMethodContextSelector}.
*/
@Override
public Context getCalleeTarget(CGNode caller,CallSiteReference site,IMethod callee,InstanceKey[] receiver) {
if (receiver != null && receiver.length > 0 && mayUnderstand(caller, site, callee, receiver[0])) {
if (DEBUG) {
System.out.print("site := " + site + ", receiver := " + receiver[0]);
}
// If the first argument is a constant ...
IR ir = caller.getIR();
SymbolTable symbolTable = ir.getSymbolTable();
SSAAbstractInvokeInstruction[] invokeInstructions = caller.getIR().getCalls(site);
if (invokeInstructions.length != 1) {
return null;
}
int use = invokeInstructions[0].getUse(1);
if (symbolTable.isStringConstant(invokeInstructions[0].getUse(1))) {
String sym = symbolTable.getStringValue(use);
if (DEBUG) {
System.out.println(invokeInstructions);
System.out.println(", with constant := `" + sym + "`");
for (InstanceKey instanceKey:receiver) {
System.out.println(" " + instanceKey);
}
}
// ... return an GetMethdContext.
ConstantKey ck = makeConstantKey(caller.getClassHierarchy(),sym);
System.out.println(ck);
return new GetMethodContext(new PointType(getTypeConstant(receiver[0])),ck);
}
if (DEBUG) {
System.out.println(", with constant := no");
}
// Otherwise, return null.
return null;
}
return null;
}
/**
* @brief
* If @a instance is a ConstantKey and its value is an instance of IClass,
* return that value. Otherwise, return @a null.
*/
private IClass getTypeConstant(InstanceKey instance) {
if (instance instanceof ConstantKey) {
ConstantKey c = (ConstantKey) instance;
if (c.getValue() instanceof IClass) {
return (IClass) c.getValue();
}
}
return null;
}
/**
* @brief
* Create a constant key for a string.
* @param cha
* The class hierarchy.
* @param str
* The string.
* @return
* The constant key.
*/
protected static ConstantKey<String> makeConstantKey(IClassHierarchy cha,String str) {
IClass cls = cha.lookupClass(TypeReference.JavaLangString);
ConstantKey<String> ck = new ConstantKey<String>(str,cls);
return ck;
}
private static final Collection<MethodReference> UNDERSTOOD_METHOD_REFS = HashSetFactory.make();
static {
UNDERSTOOD_METHOD_REFS.add(JavaLangClassContextInterpreter.GET_METHOD);
UNDERSTOOD_METHOD_REFS.add(JavaLangClassContextInterpreter.GET_DECLARED_METHOD);
}
/**
* @brief
* This object might understand a dispatch to
* {@link java.lang.Class#getMethod(String, Class...)}
* or
* {@link java.lang.Class#getDeclaredMethod}
* when the receiver is a type constant.
*/
private boolean mayUnderstand(CGNode caller,CallSiteReference site,IMethod targetMethod,InstanceKey instance) {
return UNDERSTOOD_METHOD_REFS.contains(targetMethod.getReference())
&& getTypeConstant(instance) != null;
}
/**
* TODO
* MH: Shouldn't be the first TWO parameters be relevant?
*/
private static final IntSet thisParameter = IntSetUtil.make(new int[]{0});
@Override
public IntSet getRelevantParameters(CGNode caller, CallSiteReference site) {
if (site.isDispatch() || site.getDeclaredTarget().getNumberOfParameters() > 0) {
return thisParameter;
} else {
return EmptyIntSet.instance;
}
}
}

View File

@ -0,0 +1,21 @@
/**
* This package provides a context, a context interpreter and a context selector
* to handle the special case of calls to {@link java.lang.Class#getMethod} and
* {@link java.lang.Class#getDeclaredMethod} with higher precision than usual.
*
* The context interpreter and context selector should both be placed in front
* of {@link com.ibm.wala.analysis.reflection.JavaLangClassContextInterpreter}
* and {@link com.ibm.wala.analysis.reflection.JavaLangClassContextSelector} in
* order to work.
*
* Problem description:
* {@link com.ibm.wala.analysis.reflection.JavaLangClassContextInterpreter} and
* and {@link com.ibm.wala.analysis.reflection.JavaLangClassContextSelector} ignore
* the chance to optimize the set of methods returned if the first argument to
* {@link java.lang.Class#getMethod} or {@link java.lang.Class#getDeclaredMethod}
* is a string constant.
* @author
* Michael Heilmann
*
*/
package com.ibm.wala.analysis.reflection.ext;