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:
msridhar1 2012-02-17 20:23:21 +00:00
parent 954a1f091b
commit 965896c210
8 changed files with 158 additions and 20 deletions

View File

@ -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

View File

@ -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);
}

View File

@ -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);

View File

@ -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;
}
}

View File

@ -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);
}
}

View File

@ -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 + "]";
}
}

View File

@ -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);
}

View File

@ -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),