345 lines
13 KiB
Java
345 lines
13 KiB
Java
/*******************************************************************************
|
|
* Copyright (c) 2002 - 2006 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.ipa.callgraph.propagation.cfa;
|
|
|
|
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.AllocationSiteInNode;
|
|
import com.ibm.wala.ipa.callgraph.propagation.ContainerUtil;
|
|
import com.ibm.wala.ipa.callgraph.propagation.InstanceKey;
|
|
import com.ibm.wala.ipa.callgraph.propagation.ReceiverInstanceContext;
|
|
import com.ibm.wala.ipa.cha.IClassHierarchy;
|
|
import com.ibm.wala.types.ClassLoaderReference;
|
|
import com.ibm.wala.types.Descriptor;
|
|
import com.ibm.wala.types.MethodReference;
|
|
import com.ibm.wala.types.TypeName;
|
|
import com.ibm.wala.types.TypeReference;
|
|
import com.ibm.wala.util.debug.Assertions;
|
|
import com.ibm.wala.util.intset.EmptyIntSet;
|
|
import com.ibm.wala.util.intset.IntSet;
|
|
import com.ibm.wala.util.intset.IntSetUtil;
|
|
import com.ibm.wala.util.strings.Atom;
|
|
|
|
/**
|
|
* This context selector returns a context customized for the {@link InstanceKey} of the receiver if
|
|
* <ul>
|
|
* <li>receiver is a container, or</li>
|
|
* was allocated in a node whose context was a {@link ReceiverInstanceContext}, and the type is interesting according to a delegate
|
|
* {@link ZeroXInstanceKeys}
|
|
* </ul>
|
|
*
|
|
* Additionally, we add one level of call string context to a few well-known static factory methods from the standard libraries.
|
|
*/
|
|
public class ContainerContextSelector implements ContextSelector {
|
|
|
|
private final static boolean DEBUG = false;
|
|
|
|
private final static TypeName SyntheticSystemName = TypeName.string2TypeName("Lcom/ibm/wala/model/java/lang/System");
|
|
|
|
public final static TypeReference SyntheticSystem = TypeReference.findOrCreate(ClassLoaderReference.Primordial,
|
|
SyntheticSystemName);
|
|
|
|
public final static TypeReference JavaUtilHashtable = TypeReference.findOrCreate(ClassLoaderReference.Primordial,
|
|
"Ljava/util/Hashtable");
|
|
|
|
public final static Atom arraycopyAtom = Atom.findOrCreateUnicodeAtom("arraycopy");
|
|
|
|
private final static Descriptor arraycopyDesc = Descriptor.findOrCreateUTF8("(Ljava/lang/Object;Ljava/lang/Object;)V");
|
|
|
|
public final static MethodReference synthArraycopy = MethodReference.findOrCreate(SyntheticSystem, arraycopyAtom, arraycopyDesc);
|
|
|
|
private final static TypeReference Arrays = TypeReference.findOrCreate(ClassLoaderReference.Primordial, "Ljava/util/Arrays");
|
|
|
|
private final static Atom asList = Atom.findOrCreateUnicodeAtom("asList");
|
|
|
|
private final static Atom copyOf = Atom.findOrCreateUnicodeAtom("copyOf");
|
|
|
|
private final static Atom copyOfRange = Atom.findOrCreateUnicodeAtom("copyOfRange");
|
|
|
|
private final static Atom toString = Atom.findOrCreateUnicodeAtom("toString");
|
|
|
|
private final static MethodReference StringValueOf = MethodReference.findOrCreate(TypeReference.JavaLangString, "valueOf",
|
|
"(Ljava/lang/Object;)Ljava/lang/String;");
|
|
|
|
private final static MethodReference HashtableNewEntry = MethodReference.findOrCreate(JavaUtilHashtable, "newEntry",
|
|
"(Ljava/lang/Object;Ljava/lang/Object;I)Ljava/util/Hashtable$Entry;");
|
|
|
|
/**
|
|
* The governing class hierarchy.
|
|
*/
|
|
private final IClassHierarchy cha;
|
|
|
|
/**
|
|
* An object that determines object naming policy
|
|
*/
|
|
private final ZeroXInstanceKeys delegate;
|
|
|
|
/**
|
|
* @param cha governing class hierarchy
|
|
* @param delegate object which determines which classes are "interesting"
|
|
*/
|
|
public ContainerContextSelector(IClassHierarchy cha, ZeroXInstanceKeys delegate) {
|
|
this.cha = cha;
|
|
this.delegate = delegate;
|
|
if (delegate == null) {
|
|
throw new IllegalArgumentException("null delegate");
|
|
}
|
|
}
|
|
|
|
/*
|
|
* @see com.ibm.wala.ipa.callgraph.ContextSelector#getCalleeTarget(com.ibm.wala.ipa.callgraph.CGNode,
|
|
* com.ibm.wala.classLoader.CallSiteReference, com.ibm.wala.classLoader.IMethod,
|
|
* com.ibm.wala.ipa.callgraph.propagation.InstanceKey)
|
|
*/
|
|
public Context getCalleeTarget(CGNode caller, CallSiteReference site, IMethod callee, InstanceKey[] keys) {
|
|
if (DEBUG) {
|
|
System.err.println("ContainerContextSelector: getCalleeTarget " + callee);
|
|
}
|
|
InstanceKey receiver = null;
|
|
if (keys != null && keys.length > 0 && keys[0] != null) {
|
|
receiver = keys[0];
|
|
}
|
|
if (receiver != null && mayUnderstand(caller, site, callee, receiver)) {
|
|
if (DEBUG) {
|
|
System.err.println("May Understand: " + callee + " recv " + receiver);
|
|
}
|
|
if (isWellKnownStaticFactory(callee.getReference())) {
|
|
return new CallerSiteContext(caller, site);
|
|
} else {
|
|
if (receiver == null) {
|
|
Assertions.UNREACHABLE("null receiver for " + site);
|
|
}
|
|
return new ReceiverInstanceContext(receiver);
|
|
}
|
|
} else {
|
|
return null;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Does m represent a static factory method we know about from the standard libraries, that we usually wish to model with one
|
|
* level of call-string context?
|
|
*/
|
|
public static boolean isWellKnownStaticFactory(MethodReference m) {
|
|
if (isArrayCopyingMethod(m)) {
|
|
return true;
|
|
}
|
|
if (isArrayToStringMethod(m)) {
|
|
return true;
|
|
}
|
|
if (m.equals(StringValueOf)) {
|
|
return true;
|
|
}
|
|
if (m.equals(HashtableNewEntry)) {
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
/**
|
|
* Does m represent a library method that copies arrays?
|
|
*/
|
|
public static boolean isArrayCopyingMethod(MethodReference m) {
|
|
if (m == null) {
|
|
throw new IllegalArgumentException("null m");
|
|
}
|
|
if (m.getDeclaringClass().equals(TypeReference.JavaLangSystem)) {
|
|
if (m.getName().toString().equals("arraycopy")) {
|
|
return true;
|
|
}
|
|
}
|
|
if (m.equals(synthArraycopy)) {
|
|
return true;
|
|
}
|
|
if (isArrayCopyMethod(m)) {
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
/**
|
|
* return true iff m represents one of the well-known methods in java.lang.reflect.Arrays that do some sort of arraycopy
|
|
*/
|
|
private static boolean isArrayCopyMethod(MethodReference m) {
|
|
if (m.getDeclaringClass().equals(Arrays)) {
|
|
if (m.getName().equals(asList) || m.getName().equals(copyOf) || m.getName().equals(copyOfRange)) {
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
/**
|
|
* return true iff m represents one of the well-known methods in java.lang.reflect.Arrays that do toString() on an array
|
|
*/
|
|
private static boolean isArrayToStringMethod(MethodReference m) {
|
|
if (m.getDeclaringClass().equals(Arrays)) {
|
|
if (m.getName().equals(toString)) {
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
/**
|
|
* This method walks recursively up the definition of a context C, to see if the chain of contexts that give rise to C a) includes
|
|
* the method M. or b) includes the method in which the receiver was allocated
|
|
*
|
|
* @return the matching context if found, null otherwise
|
|
*/
|
|
public static Context findRecursiveMatchingContext(IMethod M, Context C, InstanceKey receiver) {
|
|
if (DEBUG) {
|
|
System.err.println("findRecursiveMatchingContext for " + M + " in context " + C + " receiver " + receiver);
|
|
}
|
|
Context result = findRecursiveMatchingContext(M, C);
|
|
if (result != null) {
|
|
return result;
|
|
} else {
|
|
if (receiver instanceof AllocationSiteInNode) {
|
|
AllocationSiteInNode a = (AllocationSiteInNode) receiver;
|
|
IMethod m = a.getNode().getMethod();
|
|
return findRecursiveMatchingContext(m, C);
|
|
} else {
|
|
return null;
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* This method walks recursively up the definition of a context C, to see if the chain of contexts that give rise to C includes
|
|
* the method M.
|
|
*
|
|
* If C is a ReceiverInstanceContext, Let N be the node that allocated C.instance. If N.method == M, return N. Else return
|
|
* findRecursiveMatchingContext(M, N.context) Else return null
|
|
*/
|
|
public static CGNode findNodeRecursiveMatchingContext(IMethod m, Context c) {
|
|
if (DEBUG) {
|
|
System.err.println("findNodeRecursiveMatchingContext " + m + " in context " + c);
|
|
}
|
|
if (c instanceof ReceiverInstanceContext) {
|
|
ReceiverInstanceContext ric = (ReceiverInstanceContext) c;
|
|
if (!(ric.getReceiver() instanceof AllocationSiteInNode)) {
|
|
return null;
|
|
}
|
|
AllocationSiteInNode i = (AllocationSiteInNode) ric.getReceiver();
|
|
CGNode n = i.getNode();
|
|
if (n.getMethod().equals(m)) {
|
|
return n;
|
|
} else {
|
|
return findNodeRecursiveMatchingContext(m, n.getContext());
|
|
}
|
|
} else if (c instanceof CallerContext) {
|
|
CallerContext cc = (CallerContext) c;
|
|
CGNode n = cc.getCaller();
|
|
if (n.getMethod().equals(m)) {
|
|
return n;
|
|
} else {
|
|
return findNodeRecursiveMatchingContext(m, n.getContext());
|
|
}
|
|
} else {
|
|
return null;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* This method walks recursively up the definition of a context C, to see if the chain of contexts that give rise to C includes
|
|
* the method M.
|
|
*
|
|
* If C is a ReceiverInstanceContext, Let N be the node that allocated C.instance. If N.method == M, return N.context. Else return
|
|
* findRecursiveMatchingContext(M, N.context) Else return null
|
|
*/
|
|
public static Context findRecursiveMatchingContext(IMethod M, Context C) {
|
|
CGNode n = findNodeRecursiveMatchingContext(M, C);
|
|
return (n == null) ? null : n.getContext();
|
|
}
|
|
|
|
public boolean mayUnderstand(CGNode caller, CallSiteReference site, IMethod targetMethod, InstanceKey receiver) {
|
|
if (targetMethod == null) {
|
|
throw new IllegalArgumentException("targetMethod is null");
|
|
}
|
|
if (isWellKnownStaticFactory(targetMethod.getReference())) {
|
|
return true;
|
|
} else {
|
|
if (site.isStatic()) {
|
|
return false;
|
|
}
|
|
if (targetMethod.getDeclaringClass().getReference().equals(TypeReference.JavaLangObject)) {
|
|
// ramp down context: assuming methods on java.lang.Object don't cause pollution
|
|
// important for containers that invoke reflection
|
|
return false;
|
|
}
|
|
if (isContainer(targetMethod.getDeclaringClass())) {
|
|
return true;
|
|
}
|
|
|
|
if (receiver == null) {
|
|
// any possible receiver. However, we will only handle this call
|
|
// if the concrete receiver type is interesting.
|
|
IClass klass = targetMethod.getDeclaringClass();
|
|
int n = cha.getNumberOfImmediateSubclasses(klass);
|
|
if (n > 0) {
|
|
// the receiver is not "effectively final".
|
|
// give up and assume we might see an interesting subclass.
|
|
return true;
|
|
}
|
|
// only one possible receiver class
|
|
if (delegate.isInteresting(klass)) {
|
|
// we may create a receiver instance context for this call
|
|
return true;
|
|
} else {
|
|
// we will never create a receiver instance context for this call
|
|
return false;
|
|
}
|
|
}
|
|
if (!delegate.isInteresting(receiver.getConcreteType())) {
|
|
return false;
|
|
}
|
|
if (receiver instanceof AllocationSiteInNode) {
|
|
AllocationSiteInNode I = (AllocationSiteInNode) receiver;
|
|
CGNode N = I.getNode();
|
|
if (N.getContext() instanceof ReceiverInstanceContext) {
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @return true iff C is a container class
|
|
*/
|
|
protected boolean isContainer(IClass C) {
|
|
if (DEBUG) {
|
|
System.err.println("isContainer? " + C + " " + ContainerUtil.isContainer(C));
|
|
}
|
|
return ContainerUtil.isContainer(C);
|
|
}
|
|
|
|
protected IClassHierarchy getClassHierarchy() {
|
|
return cha;
|
|
}
|
|
|
|
private static final IntSet thisParameter = IntSetUtil.make(new int[]{0});
|
|
|
|
public IntSet getRelevantParameters(CGNode caller, CallSiteReference site) {
|
|
if (site.isDispatch() || site.getDeclaredTarget().getNumberOfParameters() > 0) {
|
|
return thisParameter;
|
|
} else {
|
|
return EmptyIntSet.instance;
|
|
}
|
|
}
|
|
|
|
}
|