initial implementation of handling Function.call(). also, fix previous change that caused some tests to fail

git-svn-id: https://wala.svn.sourceforge.net/svnroot/wala/trunk@4316 f5eafffb-2e1d-0410-98e4-8ec43c5233c4
This commit is contained in:
msridhar1 2012-01-06 21:21:26 +00:00
parent c815519025
commit b042d61a67
6 changed files with 225 additions and 77 deletions

View File

@ -1,5 +1,12 @@
function foo(x) { return x; }
var z = foo;
function bar(x) { return x + 1; }
if (p > 3) {
z = foo;
} else {
z = bar;
}
z.call(null, 3);

View File

@ -12,8 +12,11 @@ package com.ibm.wala.cast.js.test;
import java.io.IOException;
import java.util.Iterator;
import java.util.List;
import java.util.Set;
import junit.framework.Assert;
import org.junit.Test;
import com.ibm.wala.ipa.callgraph.CGNode;
@ -27,6 +30,7 @@ import com.ibm.wala.ssa.SSAInstruction;
import com.ibm.wala.util.CancelException;
import com.ibm.wala.util.collections.HashSetFactory;
import com.ibm.wala.util.collections.IVector;
import com.ibm.wala.util.collections.Iterator2Collection;
import com.ibm.wala.util.collections.Pair;
import com.ibm.wala.util.collections.SparseVector;
import com.ibm.wala.util.intset.IntSetAction;
@ -50,7 +54,8 @@ public abstract class TestSimpleCallGraphShape extends TestJSCallGraphShape {
new Object[] { "tests/simple.js/fib", new String[] { "tests/simple.js/fib" } },
new Object[] { "tests/simple.js/weirder", new String[] { "prologue.js/abs" } } };
@Test public void testSimple() throws IOException, IllegalArgumentException, CancelException {
@Test
public void testSimple() throws IOException, IllegalArgumentException, CancelException {
CallGraph CG = Util.makeScriptCG("tests", "simple.js");
verifyGraphAssertions(CG, assertionsForSimple);
}
@ -63,7 +68,8 @@ public abstract class TestSimpleCallGraphShape extends TestJSCallGraphShape {
new String[] { "tests/objects.js/something", "tests/objects.js/objects_are_fun/nothing" } },
new Object[] { "tests/objects.js/objects_are_fun", new String[] { "tests/objects.js/other", "tests/objects.js/whatever" } } };
@Test public void testObjects() throws IOException, IllegalArgumentException, CancelException {
@Test
public void testObjects() throws IOException, IllegalArgumentException, CancelException {
CallGraph CG = Util.makeScriptCG("tests", "objects.js");
verifyGraphAssertions(CG, assertionsForObjects);
}
@ -77,20 +83,25 @@ public abstract class TestSimpleCallGraphShape extends TestJSCallGraphShape {
"tests/inherit.js/sharedClassObject/Rectangle/area" } },
/*
* new Object[]{"tests/inherit.js/objectMasquerading", new
* String[]{"ctor:tests/inherit.js/objectMasquerading/Rectangle"}}, new Object[]{"tests/inherit.js/sharedClassObject",
* new String[]{"ctor:tests/inherit.js/sharedClassObject/Rectangle"}},
* String[]{"ctor:tests/inherit.js/objectMasquerading/Rectangle"}}, new
* Object[]{"tests/inherit.js/sharedClassObject", new
* String[]{"ctor:tests/inherit.js/sharedClassObject/Rectangle"}},
*/
};
@Test public void testInherit() throws IOException, IllegalArgumentException, CancelException {
@Test
public void testInherit() throws IOException, IllegalArgumentException, CancelException {
CallGraph CG = Util.makeScriptCG("tests", "inherit.js");
verifyGraphAssertions(CG, assertionsForInherit);
}
private static final Object[][] assertionsForNewfn = new Object[][] { new Object[] { ROOT, new String[] { "tests/newfn.js" } },
new Object[] { "tests/newfn.js", new String[] { "suffix:ctor$1/_fromctor", "suffix:ctor$2/_fromctor", "suffix:ctor$3/_fromctor" } } };
private static final Object[][] assertionsForNewfn = new Object[][] {
new Object[] { ROOT, new String[] { "tests/newfn.js" } },
new Object[] { "tests/newfn.js",
new String[] { "suffix:ctor$1/_fromctor", "suffix:ctor$2/_fromctor", "suffix:ctor$3/_fromctor" } } };
@Test public void testNewfn() throws IOException, IllegalArgumentException, CancelException {
@Test
public void testNewfn() throws IOException, IllegalArgumentException, CancelException {
CallGraph CG = Util.makeScriptCG("tests", "newfn.js");
verifyGraphAssertions(CG, assertionsForNewfn);
}
@ -102,7 +113,8 @@ public abstract class TestSimpleCallGraphShape extends TestJSCallGraphShape {
new String[] { "tests/control-flow.js/testSwitch", "tests/control-flow.js/testDoWhile",
"tests/control-flow.js/testWhile", "tests/control-flow.js/testFor", "tests/control-flow.js/testReturn" } } };
@Test public void testControlflow() throws IOException, IllegalArgumentException, CancelException {
@Test
public void testControlflow() throws IOException, IllegalArgumentException, CancelException {
CallGraph CG = Util.makeScriptCG("tests", "control-flow.js");
verifyGraphAssertions(CG, assertionsForControlflow);
}
@ -115,7 +127,8 @@ public abstract class TestSimpleCallGraphShape extends TestJSCallGraphShape {
"tests/more-control-flow.js/testDoWhile", "tests/more-control-flow.js/testWhile",
"tests/more-control-flow.js/testFor", "tests/more-control-flow.js/testReturn" } } };
@Test public void testMoreControlflow() throws IOException, IllegalArgumentException, CancelException {
@Test
public void testMoreControlflow() throws IOException, IllegalArgumentException, CancelException {
CallGraph CG = Util.makeScriptCG("tests", "more-control-flow.js");
verifyGraphAssertions(CG, assertionsForMoreControlflow);
}
@ -124,7 +137,8 @@ public abstract class TestSimpleCallGraphShape extends TestJSCallGraphShape {
new Object[] { "tests/forin.js", new String[] { "tests/forin.js/testForIn" } },
new Object[] { "tests/forin.js/testForIn", new String[] { "tests/forin.js/testForIn1", "tests/forin.js/testForIn2" } } };
@Test public void testForin() throws IOException, IllegalArgumentException, CancelException {
@Test
public void testForin() throws IOException, IllegalArgumentException, CancelException {
CallGraph CG = Util.makeScriptCG("tests", "forin.js");
verifyGraphAssertions(CG, assertionsForForin);
}
@ -141,7 +155,8 @@ public abstract class TestSimpleCallGraphShape extends TestJSCallGraphShape {
new Object[] { "tests/simple-lexical.js/outer/indirect",
new String[] { "tests/simple-lexical.js/outer/inner", "tests/simple-lexical.js/outer/inner3" } } };
@Test public void testSimpleLexical() throws IOException, IllegalArgumentException, CancelException {
@Test
public void testSimpleLexical() throws IOException, IllegalArgumentException, CancelException {
CallGraph CG = Util.makeScriptCG("tests", "simple-lexical.js");
verifyGraphAssertions(CG, assertionsForSimpleLexical);
}
@ -157,7 +172,8 @@ public abstract class TestSimpleCallGraphShape extends TestJSCallGraphShape {
new Object[] { "tests/try.js/tryCatchFinally",
new String[] { "tests/try.js/targetOne", "tests/try.js/targetTwo", "tests/try.js/three", "tests/try.js/two" } } };
@Test public void testTry() throws IOException, IllegalArgumentException, CancelException {
@Test
public void testTry() throws IOException, IllegalArgumentException, CancelException {
CallGraph CG = Util.makeScriptCG("tests", "try.js", true);
verifyGraphAssertions(CG, assertionsForTry);
}
@ -166,7 +182,8 @@ public abstract class TestSimpleCallGraphShape extends TestJSCallGraphShape {
new Object[] { ROOT, new String[] { "tests/string-op.js" } },
new Object[] { "tests/string-op.js", new String[] { "tests/string-op.js/getOp", "tests/string-op.js/plusNum" } } };
@Test public void testStringOp() throws IOException, IllegalArgumentException, CancelException {
@Test
public void testStringOp() throws IOException, IllegalArgumentException, CancelException {
PropagationCallGraphBuilder B = Util.makeScriptCGBuilder("tests", "string-op.js");
B.getOptions().setTraceStringConstants(true);
CallGraph CG = B.makeCallGraph(B.getOptions());
@ -177,9 +194,11 @@ public abstract class TestSimpleCallGraphShape extends TestJSCallGraphShape {
new Object[] { ROOT, new String[] { "tests/upward.js" } },
new Object[] {
"tests/upward.js",
new String[] { "tests/upward.js/Obj/setit", "tests/upward.js/Obj/getit", "tests/upward.js/tester1", "tests/upward.js/tester2" } } };
new String[] { "tests/upward.js/Obj/setit", "tests/upward.js/Obj/getit", "tests/upward.js/tester1",
"tests/upward.js/tester2" } } };
@Test public void testUpward() throws IOException, IllegalArgumentException, CancelException {
@Test
public void testUpward() throws IOException, IllegalArgumentException, CancelException {
CallGraph CG = Util.makeScriptCG("tests", "upward.js");
verifyGraphAssertions(CG, assertionsForUpward);
}
@ -188,116 +207,123 @@ public abstract class TestSimpleCallGraphShape extends TestJSCallGraphShape {
new Object[] { ROOT, new String[] { "tests/string-prims.js" } },
new Object[] { "tests/string-prims.js", new String[] { "prologue.js/stringSplit", "prologue.js/toUpperCase" } } };
@Test public void testStringPrims() throws IOException, IllegalArgumentException, CancelException {
@Test
public void testStringPrims() throws IOException, IllegalArgumentException, CancelException {
PropagationCallGraphBuilder B = Util.makeScriptCGBuilder("tests", "string-prims.js");
B.getOptions().setTraceStringConstants(true);
CallGraph CG = B.makeCallGraph(B.getOptions());
verifyGraphAssertions(CG, assertionsForStringPrims);
}
private static final Object[][] assertionsForNested = new Object[][] {
new Object[] { ROOT, new String[] { "tests/nested.js" } },
new Object[] { "tests/nested.js",
new String[] { "tests/nested.js/f",
"tests/nested.js/f/ff",
"tests/nested.js/f/ff/fff" }
}
};
@Test public void testNested() throws IOException, IllegalArgumentException, CancelException {
private static final Object[][] assertionsForNested = new Object[][] { new Object[] { ROOT, new String[] { "tests/nested.js" } },
new Object[] { "tests/nested.js", new String[] { "tests/nested.js/f", "tests/nested.js/f/ff", "tests/nested.js/f/ff/fff" } } };
@Test
public void testNested() throws IOException, IllegalArgumentException, CancelException {
PropagationCallGraphBuilder B = Util.makeScriptCGBuilder("tests", "nested.js");
CallGraph CG = B.makeCallGraph(B.getOptions());
verifyGraphAssertions(CG, assertionsForNested);
}
private static final Object[][] assertionsForInstanceof = new Object[][] {
new Object[] { ROOT, new String[] { "tests/instanceof.js" } }
};
@Test public void testInstanceof() throws IOException, IllegalArgumentException, CancelException {
private static final Object[][] assertionsForInstanceof = new Object[][] { new Object[] { ROOT,
new String[] { "tests/instanceof.js" } } };
@Test
public void testInstanceof() throws IOException, IllegalArgumentException, CancelException {
PropagationCallGraphBuilder B = Util.makeScriptCGBuilder("tests", "instanceof.js");
CallGraph CG = B.makeCallGraph(B.getOptions());
verifyGraphAssertions(CG, assertionsForInstanceof);
}
/*
private static final Object[][] assertionsForWith = new Object[][] {
new Object[] { ROOT, new String[] { "tests/with.js" } }
};
@Test public void testWith() throws IOException, IllegalArgumentException, CancelException {
PropagationCallGraphBuilder B = Util.makeScriptCGBuilder("tests", "with.js");
CallGraph CG = B.makeCallGraph(B.getOptions());
verifyGraphAssertions(CG, assertionsForWith);
}
*/
@Test public void testCrash1() throws IOException, IllegalArgumentException, CancelException {
* private static final Object[][] assertionsForWith = new Object[][] { new
* Object[] { ROOT, new String[] { "tests/with.js" } } };
*
* @Test public void testWith() throws IOException, IllegalArgumentException,
* CancelException { PropagationCallGraphBuilder B =
* Util.makeScriptCGBuilder("tests", "with.js"); CallGraph CG =
* B.makeCallGraph(B.getOptions()); verifyGraphAssertions(CG,
* assertionsForWith); }
*/
@Test
public void testCrash1() throws IOException, IllegalArgumentException, CancelException {
CallGraph CG = Util.makeScriptCG("tests", "crash1.js");
verifyGraphAssertions(CG, null);
}
@Test public void testCrash2() throws IOException, IllegalArgumentException, CancelException {
@Test
public void testCrash2() throws IOException, IllegalArgumentException, CancelException {
CallGraph CG = Util.makeScriptCG("tests", "crash2.js");
verifyGraphAssertions(CG, null);
}
@Test public void testLexicalCtor() throws IOException, IllegalArgumentException, CancelException {
@Test
public void testLexicalCtor() throws IOException, IllegalArgumentException, CancelException {
CallGraph CG = Util.makeScriptCG("tests", "lexical-ctor.js");
verifyGraphAssertions(CG, null);
}
private static final Object[][] assertionsForMultivar = new Object[][] {
new Object[] { ROOT, new String[] { "tests/multivar.js" } },
new Object[] { "tests/multivar.js",
new String[] { "tests/multivar.js/a",
"tests/multivar.js/bf",
"tests/multivar.js/c"
}
}
};
new Object[] { ROOT, new String[] { "tests/multivar.js" } },
new Object[] { "tests/multivar.js", new String[] { "tests/multivar.js/a", "tests/multivar.js/bf", "tests/multivar.js/c" } } };
@Test public void testMultivar() throws IOException, IllegalArgumentException, CancelException {
@Test
public void testMultivar() throws IOException, IllegalArgumentException, CancelException {
CallGraph CG = Util.makeScriptCG("tests", "multivar.js");
verifyGraphAssertions(CG, assertionsForMultivar);
}
private static final Object[][] assertionsForPrototypeContamination = new Object[][] {
new Object[] { ROOT, new String[] { "tests/prototype_contamination_bug.js" } },
new Object[] { "suffix:test1",
new String[] { "suffix:foo_of_A"} },
new Object[] { "suffix:test2",
new String[] { "suffix:foo_of_B"} }
};
new Object[] { ROOT, new String[] { "tests/prototype_contamination_bug.js" } },
new Object[] { "suffix:test1", new String[] { "suffix:foo_of_A" } },
new Object[] { "suffix:test2", new String[] { "suffix:foo_of_B" } } };
@Test public void testProtoypeContamination() throws IOException, IllegalArgumentException, CancelException {
@Test
public void testProtoypeContamination() throws IOException, IllegalArgumentException, CancelException {
CallGraph CG = Util.makeScriptCG("tests", "prototype_contamination_bug.js");
verifyGraphAssertions(CG, assertionsForPrototypeContamination);
verifyNoEdges(CG, "suffix:test1", "suffix:foo_of_B");
verifyNoEdges(CG, "suffix:test2", "suffix:foo_of_A");
}
@Test public void testStackOverflowOnSsaConversionBug() throws IOException, IllegalArgumentException, CancelException {
@Test
public void testStackOverflowOnSsaConversionBug() throws IOException, IllegalArgumentException, CancelException {
Util.makeScriptCG("tests", "stack_overflow_on_ssa_conversion.js");
// all we need is for it to finish building CG successfully.
}
@Test public void testExtJSSwitch() throws IOException, IllegalArgumentException, CancelException {
@Test
public void testExtJSSwitch() throws IOException, IllegalArgumentException, CancelException {
Util.makeScriptCG("tests", "extjs_switch.js");
// all we need is for it to finish building CG successfully.
}
@Test public void testFunctionDotCall() throws IOException, IllegalArgumentException, CancelException {
Util.makeScriptCG("tests", "function_call.js");
// TODO assert that foo() is reachable
private static final Object[][] assertionsForFunctionDotCall = new Object[][] { new Object[] { "suffix:call4",
new String[] { "suffix:foo", "suffix:bar" } } };
@Test
public void testFunctionDotCall() throws IOException, IllegalArgumentException, CancelException {
CallGraph cg = Util.makeScriptCG("tests", "function_call.js");
for (CGNode n : cg) {
if (n.getMethod().getName().toString().equals("call4")) {
Assert.assertEquals(2, cg.getSuccNodeCount(n));
// ugh
List<CGNode> succs = Iterator2Collection.toList(cg.getSuccNodes(n));
Assert
.assertEquals(
"[Node: <Code body of function Ltests/function_call.js/foo> Context: Everywhere, Node: <Code body of function Ltests/function_call.js/bar> Context: Everywhere]",
succs.toString());
}
}
}
@Test public void testFunctionDotApply() throws IOException, IllegalArgumentException, CancelException {
@Test
public void testFunctionDotApply() throws IOException, IllegalArgumentException, CancelException {
Util.makeScriptCG("tests", "function_apply.js");
// TODO assert that bar() is reachable
}
protected IVector<Set<Pair<CGNode, Integer>>> computeIkIdToVns(PointerAnalysis pa) {
// Created by reversing the points to mapping for local pointer keys.

View File

@ -43,8 +43,8 @@ public abstract class TestSimplePageCallGraphShape extends TestJSCallGraphShape
@Before
public void config() {
JSSourceExtractor.USE_TEMP_NAME = true;
JSSourceExtractor.DELETE_UPON_EXIT = false;
JSSourceExtractor.USE_TEMP_NAME = false;
JSSourceExtractor.DELETE_UPON_EXIT = true;
}
private static final Object[][] assertionsForPage1 = new Object[][] {

View File

@ -36,7 +36,7 @@ public class JSZeroOrOneXCFABuilder extends JSCFABuilder {
SSAContextInterpreter contextInterpreter = makeDefaultContextInterpreters(appContextInterpreter, options, cha);
setContextInterpreter(contextInterpreter);
options.setSelector(new JavaScriptConstructTargetSelector(cha, options.getMethodTargetSelector()));
options.setSelector(new JavaScriptFunctionCallApplyTargetSelector(cha, new JavaScriptConstructTargetSelector(cha, options.getMethodTargetSelector())));
options.setSelector(new LoadFileTargetSelector(options.getMethodTargetSelector(), this));
ContextSelector def = new DefaultContextSelector(options, cha);

View File

@ -0,0 +1,115 @@
package com.ibm.wala.cast.js.ipa.callgraph;
import java.util.Arrays;
import com.ibm.wala.cast.js.ipa.summaries.JavaScriptSummarizedFunction;
import com.ibm.wala.cast.js.ipa.summaries.JavaScriptSummary;
import com.ibm.wala.cast.js.loader.JSCallSiteReference;
import com.ibm.wala.cast.js.loader.JavaScriptLoader;
import com.ibm.wala.cast.js.ssa.JSInstructionFactory;
import com.ibm.wala.cast.js.types.JavaScriptMethods;
import com.ibm.wala.cast.js.types.JavaScriptTypes;
import com.ibm.wala.cast.loader.AstFunctionClass;
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.MethodTargetSelector;
import com.ibm.wala.ipa.cha.IClassHierarchy;
import com.ibm.wala.ssa.IR;
import com.ibm.wala.ssa.SSAAbstractInvokeInstruction;
import com.ibm.wala.types.Descriptor;
import com.ibm.wala.types.MethodReference;
import com.ibm.wala.util.strings.Atom;
/**
* Generate IR to model Function.call() and Function.apply()
*
* @see <a
* href="https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Function/Apply">MDN
* Function.apply() docs</a>
* @see <a
* href="https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Function/Call">MDN
* Function.call() docs</a>
*
* @author manu
*
*/
public class JavaScriptFunctionCallApplyTargetSelector implements MethodTargetSelector {
private final IClassHierarchy cha;
private final MethodTargetSelector base;
public JavaScriptFunctionCallApplyTargetSelector(IClassHierarchy cha, MethodTargetSelector base) {
this.cha = cha;
this.base = base;
}
/*
* (non-Javadoc)
*
* @see
* com.ibm.wala.ipa.callgraph.MethodTargetSelector#getCalleeTarget(com.ibm
* .wala.ipa.callgraph.CGNode, com.ibm.wala.classLoader.CallSiteReference,
* com.ibm.wala.classLoader.IClass)
*/
@Override
public IMethod getCalleeTarget(CGNode caller, CallSiteReference site, IClass receiver) {
if (cha.isSubclassOf(receiver, cha.lookupClass(JavaScriptTypes.CodeBody))) {
// TODO better way to do these tests
String s = receiver.toString();
if (s.equals("function Lprologue.js/functionCall")) {
return getFunctionCallTarget(caller, site, receiver);
} else if (s.equals("function Lprologue.js/functionApply")) {
return getFunctionApplyTarget(caller, site, receiver);
}
}
return base.getCalleeTarget(caller, site, receiver);
}
private IMethod getFunctionApplyTarget(CGNode caller, CallSiteReference site, IClass receiver) {
System.err.println("TODO: handle Function.apply()");
return base.getCalleeTarget(caller, site, receiver);
}
/**
* generate a synthetic method modeling the invocation of Function.call() at the site
* @param caller
* @param site
* @param receiver
* @return
*/
private IMethod getFunctionCallTarget(CGNode caller, CallSiteReference site, IClass receiver) {
JSInstructionFactory insts = (JSInstructionFactory)receiver.getClassLoader().getInstructionFactory();
IR callerIR = caller.getIR();
SSAAbstractInvokeInstruction callStmts[] = callerIR.getCalls(site);
assert callStmts.length == 1;
int nargs = callStmts[0].getNumberOfParameters();
Atom atom = Atom.findOrCreateUnicodeAtom("call" + nargs);
Descriptor desc = Descriptor.findOrCreateUTF8(JavaScriptLoader.JS, "()LRoot;");
MethodReference ref = MethodReference.findOrCreate(receiver.getReference(), atom , desc );
JavaScriptSummary S = new JavaScriptSummary(ref, nargs);
// generate invocation instruction for the real method being invoked
int resultVal = nargs + 2;
CallSiteReference cs = new JSCallSiteReference(S.getNextProgramCounter());
int[] params = new int[nargs-2];
for (int i = 0; i < params.length; i++) {
// add 3 to skip v1 (which points to Function.call() itself) and v2 (the real function being invoked)
params[i] = i+3;
}
// function being invoked is in v2
S.addStatement(insts.Invoke(2, resultVal, params , resultVal+1, site));
S.getNextProgramCounter();
S.addStatement(insts.ReturnInstruction(resultVal, false));
S.getNextProgramCounter();
JavaScriptSummarizedFunction t = new JavaScriptSummarizedFunction(ref, S, receiver);
return t;
}
}

View File

@ -72,7 +72,7 @@ public class Util {
}
public static void dumpCG(PointerAnalysis PA, CallGraph CG) {
if (true) return;
for (Iterator x = CG.iterator(); x.hasNext();) {
CGNode N = (CGNode) x.next();
System.err.println("\ncallees of node " + N.getMethod() + " " + N.getGraphNodeId());