add general recursion bounding via an additional context selector. Also, completely eliminate dependence on contexts for correct pointer analysis with new lexical scheme.
git-svn-id: https://wala.svn.sourceforge.net/svnroot/wala/trunk@4496 f5eafffb-2e1d-0410-98e4-8ec43c5233c4
This commit is contained in:
parent
954a1f091b
commit
965896c210
|
@ -11,6 +11,7 @@ import junit.framework.Assert;
|
|||
|
||||
import org.eclipse.core.runtime.NullProgressMonitor;
|
||||
|
||||
import com.ibm.wala.cast.ir.translator.AstTranslator;
|
||||
import com.ibm.wala.cast.js.ipa.callgraph.ForInContextSelector;
|
||||
import com.ibm.wala.cast.js.ipa.callgraph.JSCFABuilder;
|
||||
import com.ibm.wala.cast.js.ipa.callgraph.JavaScriptFunctionDotCallTargetSelector;
|
||||
|
@ -144,7 +145,7 @@ public class HTMLCGBuilder {
|
|||
}
|
||||
// suppress debug output
|
||||
JavaScriptFunctionDotCallTargetSelector.WARN_ABOUT_IMPRECISE_CALLGRAPH = false;
|
||||
CGBuilderResult res = buildHTMLCG(src, timeout, true, CGBuilderType.ZERO_ONE_CFA_PRECISE_LEXICAL);
|
||||
CGBuilderResult res = buildHTMLCG(src, timeout, true, AstTranslator.NEW_LEXICAL ? CGBuilderType.ONE_CFA_PRECISE_LEXICAL : CGBuilderType.ZERO_ONE_CFA);
|
||||
if(res.construction_time == -1)
|
||||
System.out.println("TIMED OUT");
|
||||
else
|
||||
|
|
|
@ -83,6 +83,9 @@ public class JSZeroOrOneXCFABuilder extends JSCFABuilder {
|
|||
if (doOneCFA) {
|
||||
contextSelector = new nCFAContextSelector(1, contextSelector);
|
||||
}
|
||||
if (AstTranslator.NEW_LEXICAL) {
|
||||
contextSelector = new RecursionBoundContextSelector(contextSelector, 3);
|
||||
}
|
||||
setContextSelector(contextSelector);
|
||||
}
|
||||
|
||||
|
|
|
@ -15,6 +15,7 @@ import com.ibm.wala.ssa.ConstantValue;
|
|||
import com.ibm.wala.ssa.DefUse;
|
||||
import com.ibm.wala.ssa.IR;
|
||||
import com.ibm.wala.types.MethodReference;
|
||||
import com.ibm.wala.types.TypeName;
|
||||
|
||||
/**
|
||||
* TODO cache generated IRs
|
||||
|
@ -25,20 +26,24 @@ import com.ibm.wala.types.MethodReference;
|
|||
*/
|
||||
public class JavaScriptFunctionApplyContextInterpreter extends AstContextInsensitiveSSAContextInterpreter {
|
||||
|
||||
private static final TypeName APPLY_TYPE_NAME = TypeName.findOrCreate("Lprologue.js/functionApply");
|
||||
|
||||
public JavaScriptFunctionApplyContextInterpreter(AnalysisOptions options, AnalysisCache cache) {
|
||||
super(options, cache);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean understands(CGNode node) {
|
||||
return node.getContext().get(JavaScriptFunctionApplyContextSelector.APPLY_NON_NULL_ARGS) != null;
|
||||
return node.getMethod().getDeclaringClass().getName().equals(APPLY_TYPE_NAME);
|
||||
}
|
||||
|
||||
@Override
|
||||
public IR getIR(CGNode node) {
|
||||
assert understands(node);
|
||||
BooleanContextItem isNonNullArray = (BooleanContextItem) node.getContext().get(JavaScriptFunctionApplyContextSelector.APPLY_NON_NULL_ARGS);
|
||||
if (isNonNullArray.val) {
|
||||
// isNonNullArray can be null if, e.g., due to recursion bounding we have no
|
||||
// information on the arguments parameter
|
||||
if (isNonNullArray == null || isNonNullArray.val) {
|
||||
return makeIRForArgList(node);
|
||||
} else {
|
||||
return makeIRForNoArgList(node);
|
||||
|
|
|
@ -10,16 +10,25 @@
|
|||
*****************************************************************************/
|
||||
package com.ibm.wala.cast.js.ipa.callgraph;
|
||||
|
||||
import java.security.KeyStore.Builder;
|
||||
import java.util.Collection;
|
||||
|
||||
import com.ibm.wala.cast.ipa.callgraph.ScopeMappingInstanceKeys;
|
||||
import com.ibm.wala.cast.js.loader.JavaScriptLoader;
|
||||
import com.ibm.wala.cast.js.loader.JavaScriptLoader.JavaScriptMethodObject;
|
||||
import com.ibm.wala.cast.js.types.JavaScriptTypes;
|
||||
import com.ibm.wala.cast.loader.AstMethod.LexicalParent;
|
||||
import com.ibm.wala.cast.types.AstMethodReference;
|
||||
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.propagation.InstanceKey;
|
||||
import com.ibm.wala.ipa.callgraph.propagation.InstanceKeyFactory;
|
||||
import com.ibm.wala.ipa.callgraph.propagation.PropagationCallGraphBuilder;
|
||||
import com.ibm.wala.ipa.cha.IClassHierarchy;
|
||||
import com.ibm.wala.types.MethodReference;
|
||||
import com.ibm.wala.types.TypeReference;
|
||||
import com.ibm.wala.util.collections.Pair;
|
||||
|
||||
public class JavaScriptScopeMappingInstanceKeys extends ScopeMappingInstanceKeys {
|
||||
|
||||
|
@ -50,4 +59,19 @@ public class JavaScriptScopeMappingInstanceKeys extends ScopeMappingInstanceKeys
|
|||
getParents(base).length > 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Collection<CGNode> getConstructorCallers(ScopeMappingInstanceKey smik, Pair<String, String> name) {
|
||||
final Collection<CGNode> result = super.getConstructorCallers(smik, name);
|
||||
if (result == null) {
|
||||
IClassHierarchy cha = smik.getCreator().getClassHierarchy();
|
||||
MethodReference ref = MethodReference.findOrCreate(JavaScriptLoader.JS, TypeReference.findOrCreate(cha.getLoaders()[0].getReference(), name.snd), AstMethodReference.fnAtomStr, AstMethodReference.fnDesc.toString());
|
||||
final IMethod method = cha.resolveMethod(ref);
|
||||
if (method != null) {
|
||||
return builder.getCallGraph().getNodes(method.getReference());
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
|
|
@ -0,0 +1,89 @@
|
|||
package com.ibm.wala.cast.js.ipa.callgraph;
|
||||
|
||||
import com.ibm.wala.analysis.reflection.InstanceKeyWithNode;
|
||||
import com.ibm.wala.cast.ipa.callgraph.ScopeMappingInstanceKeys.ScopeMappingInstanceKey;
|
||||
import com.ibm.wala.classLoader.CallSiteReference;
|
||||
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.ContextKey;
|
||||
import com.ibm.wala.ipa.callgraph.ContextSelector;
|
||||
import com.ibm.wala.ipa.callgraph.impl.Everywhere;
|
||||
import com.ibm.wala.ipa.callgraph.propagation.FilteredPointerKey;
|
||||
import com.ibm.wala.ipa.callgraph.propagation.FilteredPointerKey.SingleInstanceFilter;
|
||||
import com.ibm.wala.ipa.callgraph.propagation.InstanceKey;
|
||||
import com.ibm.wala.util.intset.IntSet;
|
||||
|
||||
/**
|
||||
* A context selector that attempts to detect recursion beyond some depth in a
|
||||
* base selector. If such recursion is detected, the base selector's context is
|
||||
* replaced with {@link Everywhere#EVERYWHERE}.
|
||||
*/
|
||||
public class RecursionBoundContextSelector implements ContextSelector {
|
||||
|
||||
private final ContextSelector base;
|
||||
|
||||
private final int recursionBound;
|
||||
|
||||
/**
|
||||
* the highest parameter index that we'll check . this is a HACK. ideally,
|
||||
* given a context, we'd have some way to know all the {@link ContextKey}s
|
||||
* that it knows about.
|
||||
*
|
||||
* @see ContextKey#PARAMETERS
|
||||
*/
|
||||
private static final int MAX_INTERESTING_PARAM = 5;
|
||||
|
||||
/**
|
||||
* @param base
|
||||
* @param recursionBound
|
||||
* bound on recursion depth, with the top level of the context
|
||||
* returned by the base selector being depth 0. The
|
||||
* {@link Everywhere#EVERYWHERE} context is returned if the base
|
||||
* context <em>exceeds</em> this bound.
|
||||
*/
|
||||
public RecursionBoundContextSelector(ContextSelector base, int recursionBound) {
|
||||
this.base = base;
|
||||
this.recursionBound = recursionBound;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Context getCalleeTarget(CGNode caller, CallSiteReference site, IMethod callee, InstanceKey[] actualParameters) {
|
||||
Context baseContext = base.getCalleeTarget(caller, site, callee, actualParameters);
|
||||
// TODO somehow k-limit more smartly?
|
||||
return exceedsRecursionBound(baseContext, 0) ? Everywhere.EVERYWHERE : baseContext;
|
||||
}
|
||||
|
||||
private boolean exceedsRecursionBound(Context baseContext, int curLevel) {
|
||||
if (curLevel > recursionBound) {
|
||||
return true;
|
||||
}
|
||||
// we just do a case analysis here. we might have to add cases later to
|
||||
// account for new types of context / recursion.
|
||||
CGNode callerNode = (CGNode) baseContext.get(ContextKey.CALLER);
|
||||
if (callerNode != null && exceedsRecursionBound(callerNode.getContext(), curLevel + 1)) {
|
||||
return true;
|
||||
}
|
||||
for (int i = 0; i < MAX_INTERESTING_PARAM; i++) {
|
||||
FilteredPointerKey.SingleInstanceFilter filter = (SingleInstanceFilter) baseContext.get(ContextKey.PARAMETERS[i]);
|
||||
if (filter != null) {
|
||||
InstanceKey ik = filter.getInstance();
|
||||
if (ik instanceof ScopeMappingInstanceKey) {
|
||||
ik = ((ScopeMappingInstanceKey) ik).getBase();
|
||||
}
|
||||
if (ik instanceof InstanceKeyWithNode) {
|
||||
if (exceedsRecursionBound(((InstanceKeyWithNode) ik).getNode().getContext(), curLevel + 1)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public IntSet getRelevantParameters(CGNode caller, CallSiteReference site) {
|
||||
return base.getRelevantParameters(caller, site);
|
||||
}
|
||||
|
||||
}
|
|
@ -58,5 +58,11 @@ public class ArgumentInstanceContext implements Context {
|
|||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "ArgumentInstanceContext [base=" + base + ", index=" + index + ", instanceKey=" + instanceKey + "]";
|
||||
}
|
||||
|
||||
|
||||
}
|
|
@ -51,7 +51,7 @@ abstract public class ScopeMappingInstanceKeys implements InstanceKeyFactory {
|
|||
*/
|
||||
protected abstract boolean needsScopeMappingKey(InstanceKey base);
|
||||
|
||||
private final PropagationCallGraphBuilder builder;
|
||||
protected final PropagationCallGraphBuilder builder;
|
||||
|
||||
private final InstanceKeyFactory basic;
|
||||
|
||||
|
@ -100,8 +100,8 @@ abstract public class ScopeMappingInstanceKeys implements InstanceKeyFactory {
|
|||
*/
|
||||
Iterator<CGNode> getFunargNodes(Pair<String, String> name) {
|
||||
if (AstTranslator.NEW_LEXICAL) {
|
||||
Collection<CGNode> constructorCallers = getConstructorCallers();
|
||||
assert !constructorCallers.isEmpty() : "no callers for constructor";
|
||||
Collection<CGNode> constructorCallers = getConstructorCallers(this, name);
|
||||
assert constructorCallers != null && !constructorCallers.isEmpty() : "no callers for constructor";
|
||||
Iterator<CGNode> result = EmptyIterator.instance();
|
||||
for (CGNode callerOfConstructor : constructorCallers) {
|
||||
if (callerOfConstructor.getMethod().getReference().getDeclaringClass().getName().toString().equals(name.snd)){
|
||||
|
@ -159,20 +159,7 @@ abstract public class ScopeMappingInstanceKeys implements InstanceKeyFactory {
|
|||
}
|
||||
}
|
||||
|
||||
private Collection<CGNode> getConstructorCallers() {
|
||||
final Context creatorContext = creator.getContext();
|
||||
CGNode callerOfConstructor = (CGNode) creatorContext.get(ContextKey.CALLER);
|
||||
if (callerOfConstructor != null) {
|
||||
return Collections.singleton(callerOfConstructor);
|
||||
} else {
|
||||
CallString cs = (CallString) creatorContext.get(CallStringContextSelector.CALL_STRING);
|
||||
assert cs != null : "unexpected context " + creatorContext;
|
||||
IMethod[] methods = cs.getMethods();
|
||||
assert methods.length == 1;
|
||||
IMethod m = methods[0];
|
||||
return builder.getCallGraph().getNodes(m.getReference());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public int hashCode() {
|
||||
return base.hashCode() * creator.hashCode();
|
||||
|
@ -205,6 +192,22 @@ abstract public class ScopeMappingInstanceKeys implements InstanceKeyFactory {
|
|||
}
|
||||
}
|
||||
|
||||
protected Collection<CGNode> getConstructorCallers(ScopeMappingInstanceKey smik, Pair<String, String> name) {
|
||||
final Context creatorContext = smik.getCreator().getContext();
|
||||
CGNode callerOfConstructor = (CGNode) creatorContext.get(ContextKey.CALLER);
|
||||
if (callerOfConstructor != null) {
|
||||
return Collections.singleton(callerOfConstructor);
|
||||
} else {
|
||||
CallString cs = (CallString) creatorContext.get(CallStringContextSelector.CALL_STRING);
|
||||
if (cs != null) {
|
||||
IMethod[] methods = cs.getMethods();
|
||||
assert methods.length == 1;
|
||||
IMethod m = methods[0];
|
||||
return builder.getCallGraph().getNodes(m.getReference());
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
public InstanceKey getInstanceKeyForMultiNewArray(CGNode node, NewSiteReference allocation, int dim) {
|
||||
return basic.getInstanceKeyForMultiNewArray(node, allocation, dim);
|
||||
}
|
||||
|
|
|
@ -36,6 +36,10 @@ public interface ContextKey {
|
|||
public final static ContextKey RECEIVER = new ContextKey() {
|
||||
};
|
||||
|
||||
/**
|
||||
* context key representing some parameter index, useful, e.g. for CPA-style
|
||||
* context-sensitivity policies.
|
||||
*/
|
||||
public static class ParameterKey implements ContextKey {
|
||||
public final int index;
|
||||
|
||||
|
@ -45,6 +49,9 @@ public interface ContextKey {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Generally useful constants for possible parameter indices
|
||||
*/
|
||||
public static final ContextKey PARAMETERS[] = new ContextKey[]{
|
||||
new ParameterKey(0),
|
||||
new ParameterKey(1),
|
||||
|
|
Loading…
Reference in New Issue