Changes motivated by analysis issues in JavaScript. The main change is that calls of the form f.x() in JavaScript are now analyzed like 'method calls' so that the 'this' pointer in the receiver functions can be filtered based upon the types that actually have the method being invoked. This requires much more dynamic filtering than in a language like Java, since properties like 'x' are simply properties that happen to hold functions, and so can be assigned in a first-class manner. Thus, the filtering needs to handle variance in both the types and the values of their properties; this is implemented as multiple levels of abstract object directed side effect equations in the dataflow system.

git-svn-id: https://wala.svn.sourceforge.net/svnroot/wala/trunk@4535 f5eafffb-2e1d-0410-98e4-8ec43c5233c4
This commit is contained in:
dolby-oss 2012-03-01 02:45:51 +00:00
parent 99564d6e06
commit a9ec87f360
42 changed files with 886 additions and 291 deletions

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -1,4 +1,4 @@
<?xml version="1.0" encoding="UTF-8"?>
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<launchConfiguration type="org.eclipse.jdt.junit.launchconfig">
<stringAttribute key="bad_container_name" value="/com.ibm.wala.cast.java.polyglottest/launchers"/>
<listAttribute key="org.eclipse.debug.core.MAPPED_RESOURCE_PATHS">
@ -11,7 +11,9 @@
<booleanAttribute key="org.eclipse.jdt.junit.KEEPRUNNING_ATTR" value="false"/>
<stringAttribute key="org.eclipse.jdt.junit.TESTNAME" value=""/>
<stringAttribute key="org.eclipse.jdt.junit.TEST_KIND" value="org.eclipse.jdt.junit.loader.junit4"/>
<stringAttribute key="org.eclipse.jdt.launching.JRE_CONTAINER" value="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.launching.macosx.MacOSXType/java 1.6"/>
<stringAttribute key="org.eclipse.jdt.launching.MAIN_TYPE" value="com.ibm.wala.cast.java.test.PolyglotJavaIRTests"/>
<stringAttribute key="org.eclipse.jdt.launching.PROJECT_ATTR" value="com.ibm.wala.cast.java.polyglot.test"/>
<stringAttribute key="org.eclipse.jdt.launching.VM_ARGUMENTS" value="-ea -Xmx512M"/>
<stringAttribute key="org.eclipse.jdt.launching.WORKING_DIRECTORY" value="${workspace_loc:com.ibm.wala.cast.java.test.data}"/>
</launchConfiguration>

View File

@ -27,6 +27,7 @@ import java.util.jar.JarFile;
import org.junit.Assert;
import com.ibm.wala.cast.ir.translator.AstTranslator;
import com.ibm.wala.cast.java.client.JavaSourceAnalysisEngine;
import com.ibm.wala.cast.java.ipa.callgraph.JavaSourceAnalysisScope;
import com.ibm.wala.classLoader.IClass;
@ -282,6 +283,9 @@ public abstract class IRTests {
public Pair runTest(Collection<String> sources, List<String> libs, String[] mainClassDescriptors, List<? extends IRAssertion> ca,
boolean assertReachable) {
try {
boolean currentState = AstTranslator.NEW_LEXICAL;
AstTranslator.NEW_LEXICAL = false;
JavaSourceAnalysisEngine engine = getAnalysisEngine(mainClassDescriptors);
populateScope(engine, sources, libs);
@ -297,6 +301,8 @@ public abstract class IRTests {
IRAssertion.check(callGraph);
}
AstTranslator.NEW_LEXICAL = currentState;
return Pair.make(callGraph, engine.getPointerAnalysis());
} catch (Exception e) {

View File

@ -272,7 +272,7 @@ public class AstJavaSSAPropagationCallGraphBuilder extends AstSSAPropagationCall
}
public void visitJavaInvoke(AstJavaInvokeInstruction instruction) {
visitInvokeInternal(instruction);
visitInvokeInternal(instruction, new DefaultInvariantComputer());
}
}

View File

@ -2,8 +2,8 @@
<classpath>
<classpathentry kind="src" path="src"/>
<classpathentry kind="src" path="tests"/>
<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.6"/>
<classpathentry kind="con" path="org.eclipse.pde.core.requiredPlugins"/>
<classpathentry kind="lib" path="lib/htmlparser-1.3.1.jar"/>
<classpathentry kind="con" path="org.eclipse.pde.core.requiredPlugins"/>
<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.6"/>
<classpathentry kind="output" path="bin"/>
</classpath>

View File

@ -14,13 +14,9 @@ import java.io.IOException;
import java.util.LinkedList;
import java.util.List;
import com.ibm.wala.cast.ir.translator.TranslatorToCAst;
import com.ibm.wala.cast.js.translator.PropertyReadExpander.ExpanderKey;
import com.ibm.wala.cast.tree.CAst;
import com.ibm.wala.cast.tree.CAstEntity;
import com.ibm.wala.cast.tree.impl.CAstImpl;
import com.ibm.wala.cast.tree.impl.CAstRewriter;
import com.ibm.wala.cast.tree.impl.CAstRewriter.CopyKey;
import com.ibm.wala.cast.tree.impl.CAstRewriter.RewriteContext;
import com.ibm.wala.cast.tree.impl.CAstRewriterFactory;
@ -35,12 +31,7 @@ public class CAstRhinoTranslator implements TranslatorToCAst {
public CAstRhinoTranslator(SourceModule M, boolean replicateForDoLoops) {
this.M = M;
this.replicateForDoLoops = replicateForDoLoops;
this.addRewriter(new CAstRewriterFactory<PropertyReadExpander.RewriteContext, ExpanderKey>() {
public CAstRewriter<PropertyReadExpander.RewriteContext, ExpanderKey> createCAstRewriter(CAst ast) {
return new PropertyReadExpander(ast);
}
}, true);
}
}
public <C extends RewriteContext<K>, K extends CopyKey<K>> void addRewriter(CAstRewriterFactory<C, K> factory, boolean prepend) {
if(prepend)

View File

@ -168,13 +168,12 @@ public class RhinoToAstTranslator {
}
}
private static class BaseCollectingContext extends JavaScriptTranslatorToCAst.BaseCollectingContext<WalkContext, Node> implements WalkContext {
private static class MemberDestructuringContext extends JavaScriptTranslatorToCAst.MemberDestructuringContext<WalkContext, Node> implements WalkContext {
protected MemberDestructuringContext(WalkContext parent, Node initialBaseFor, int operationIndex) {
super(parent, initialBaseFor, operationIndex);
}
BaseCollectingContext(WalkContext parent, Node initialBaseFor,
String baseVar) {
super(parent, initialBaseFor, baseVar);
}
}
private static class BreakContext extends JavaScriptTranslatorToCAst.BreakContext<WalkContext, Node> implements WalkContext {
@ -201,6 +200,22 @@ public class RhinoToAstTranslator {
}
private String operationReceiverName(int operationIndex) {
return "$$destructure$rcvr" + operationIndex;
}
private CAstNode operationReceiverVar(int operationIndex) {
return Ast.makeNode(CAstNode.VAR, Ast.makeConstant(operationReceiverName(operationIndex)));
}
private String operationElementName(int operationIndex) {
return "$$destructure$elt" + operationIndex;
}
private CAstNode operationElementVar(int operationIndex) {
return Ast.makeNode(CAstNode.VAR, Ast.makeConstant(operationElementName(operationIndex)));
}
private CAstNode translateOpcode(int nodeType) {
switch (nodeType) {
case Token.POS:
@ -327,7 +342,7 @@ public class RhinoToAstTranslator {
int i = 0;
CAstNode arguments[] = new CAstNode[nargs];
arguments[i++] = fun;
assert callee.equals(STANDARD_CALL_FN_NAME) || callee.equals(CTOR_CALL_FN_NAME);
// assert callee.equals(STANDARD_CALL_FN_NAME) || callee.equals(CTOR_CALL_FN_NAME);
arguments[i++] = Ast.makeConstant(callee);
if (thisptr != null)
arguments[i++] = thisptr;
@ -718,19 +733,20 @@ public class RhinoToAstTranslator {
}
private CAstNode visitObjectRead(AstNode n, AstNode objAst, CAstNode elt, WalkContext context) {
CAstNode obj = visit(objAst, context);
String baseVar = context.getBaseVarIfRelevant(n);
CAstNode get, result;
if (baseVar != null) {
result = Ast.makeNode(CAstNode.BLOCK_EXPR,
Ast.makeNode(CAstNode.ASSIGN, Ast.makeNode(CAstNode.VAR, Ast.makeConstant(baseVar)), obj),
get = Ast.makeNode(CAstNode.OBJECT_REF, Ast.makeNode(CAstNode.VAR, Ast.makeConstant(baseVar)), elt));
} else {
result = get = Ast.makeNode(CAstNode.OBJECT_REF, obj, elt);
}
CAstNode get, result;
int operationIndex = context.setOperation(n);
if (context.getCatchTarget() != null) {
CAstNode obj = visit(objAst, context);
if (operationIndex != -1) {
get = null;
result = Ast.makeNode(CAstNode.BLOCK_EXPR,
Ast.makeNode(CAstNode.ASSIGN, operationReceiverVar(operationIndex), obj),
Ast.makeNode(CAstNode.ASSIGN, operationElementVar(operationIndex), elt));
} else {
result = get = Ast.makeNode(CAstNode.OBJECT_REF, obj, elt);
}
if (get != null && context.getCatchTarget() != null) {
context.cfg().map(get, get);
context.cfg().add(get, context.getCatchTarget(), JavaScriptTypes.TypeError);
}
@ -864,27 +880,34 @@ public class RhinoToAstTranslator {
return args;
}
private static final String baseVarName = "$$-base-$$";
@Override
public CAstNode visitFunctionCall(FunctionCall n, WalkContext context) {
if (!isPrimitiveCall(context, n)) {
CAstNode base = Ast.makeNode(CAstNode.VAR, Ast.makeConstant(baseVarName));
AstNode callee = n.getTarget();
WalkContext child = new BaseCollectingContext(context, callee, baseVarName);
int thisBaseVarNum = ++baseVarNum;
WalkContext child = new MemberDestructuringContext(context, callee, thisBaseVarNum);
CAstNode fun = visit(callee, child);
// the first actual parameter appearing within the parentheses of the
// call (i.e., possibly excluding the 'this' parameter)
CAstNode[] args = gatherCallArguments(n, context);
if (child.foundBase(callee))
return Ast.makeNode(
CAstNode.LOCAL_SCOPE,
Ast.makeNode(CAstNode.BLOCK_EXPR,
Ast.makeNode(CAstNode.DECL_STMT, Ast.makeConstant(new CAstSymbolImpl(baseVarName)), Ast.makeConstant(null)),
makeCall(fun, base, args, context)));
else
return makeCall(fun, makeVarRef(JSSSAPropagationCallGraphBuilder.GLOBAL_OBJ_VAR_NAME), args, context);
if (child.foundMemberOperation(callee))
return
Ast.makeNode(CAstNode.LOCAL_SCOPE,
Ast.makeNode(CAstNode.BLOCK_EXPR,
Ast.makeNode(CAstNode.DECL_STMT,
Ast.makeConstant(new CAstSymbolImpl(operationReceiverName(thisBaseVarNum))),
Ast.makeConstant(null)),
Ast.makeNode(CAstNode.DECL_STMT,
Ast.makeConstant(new CAstSymbolImpl(operationElementName(thisBaseVarNum))),
Ast.makeConstant(null)),
fun,
makeCall(operationElementVar(thisBaseVarNum), operationReceiverVar(thisBaseVarNum), args, context, "dispatch")));
else {
CAstNode globalRef = makeVarRef(JSSSAPropagationCallGraphBuilder.GLOBAL_OBJ_VAR_NAME);
context.cfg().map(globalRef, globalRef);
return makeCall(fun, globalRef, args, context);
}
} else {
return Ast.makeNode(CAstNode.PRIMITIVE, gatherCallArguments(n, context));
}
@ -992,7 +1015,9 @@ public class RhinoToAstTranslator {
switch (node.getType()) {
case Token.THIS: {
if (arg.top() instanceof ScriptNode && !(arg.top() instanceof FunctionNode)) {
return makeVarRef(JSSSAPropagationCallGraphBuilder.GLOBAL_OBJ_VAR_NAME);
CAstNode globalRef = makeVarRef(JSSSAPropagationCallGraphBuilder.GLOBAL_OBJ_VAR_NAME);
arg.cfg().map(globalRef, globalRef);
return globalRef;
} else {
return Ast.makeNode(CAstNode.VAR, Ast.makeConstant("this"));
}
@ -2249,6 +2274,8 @@ private CAstNode[] walkChildren(final Node n, WalkContext context) {
private int anonymousCounter = 0;
private int baseVarNum = 0;
private final DoLoopTranslator doLoopTranslator;
public RhinoToAstTranslator(CAst Ast, SourceModule M, String scriptName, boolean replicateForDoLoops) {

View File

@ -0,0 +1,33 @@
var left = {
inner: function left_inner(x) {
return x+1;
},
outer: function left_outer(x) {
return this.inner(x+1);
}
};
var right = {
inner: function right_inner(x) {
return Math.abs(x);
},
outer: function right_outer(x) {
return this.inner(-x);
}
};
var x = 3;
if (x > Math.random()) {
x = left;
} else {
x = right;
}
x.outer(7);

View File

@ -111,7 +111,7 @@ public class JSCallGraphBuilderUtil extends com.ibm.wala.cast.js.ipa.callgraph.J
IllegalArgumentException, CancelException {
PropagationCallGraphBuilder b = makeScriptCGBuilder(dir, name, builderType);
CallGraph CG = b.makeCallGraph(b.getOptions());
dumpCG(b.getPointerAnalysis(), CG);
// dumpCG(b.getPointerAnalysis(), CG);
return CG;
}
@ -119,7 +119,7 @@ public class JSCallGraphBuilderUtil extends com.ibm.wala.cast.js.ipa.callgraph.J
CancelException {
PropagationCallGraphBuilder b = makeCGBuilder(makeLoaders(), scripts, builderType, irFactory);
CallGraph CG = b.makeCallGraph(b.getOptions());
dumpCG(b.getPointerAnalysis(), CG);
// dumpCG(b.getPointerAnalysis(), CG);
return CG;
}

View File

@ -19,6 +19,8 @@ import junit.framework.Assert;
import org.junit.Test;
import com.ibm.wala.cast.js.ipa.callgraph.JSCFABuilder;
import com.ibm.wala.cast.js.ipa.callgraph.JSCallGraphUtil;
import com.ibm.wala.ipa.callgraph.CGNode;
import com.ibm.wala.ipa.callgraph.CallGraph;
import com.ibm.wala.ipa.callgraph.propagation.InstanceKey;
@ -151,7 +153,10 @@ public abstract class TestSimpleCallGraphShape extends TestJSCallGraphShape {
@Test
public void testForin() throws IOException, IllegalArgumentException, CancelException {
CallGraph CG = JSCallGraphBuilderUtil.makeScriptCG("tests", "forin.js");
JSCFABuilder B = JSCallGraphBuilderUtil.makeScriptCGBuilder("tests", "forin.js");
CallGraph CG = B.makeCallGraph(B.getOptions());
JSCallGraphUtil.AVOID_DUMP = false;
JSCallGraphUtil.dumpCG(B.getPointerAnalysis(), CG);
verifyGraphAssertions(CG, assertionsForForin);
}
@ -242,6 +247,8 @@ public abstract class TestSimpleCallGraphShape extends TestJSCallGraphShape {
PropagationCallGraphBuilder B = JSCallGraphBuilderUtil.makeScriptCGBuilder("tests", "string-prims.js");
B.getOptions().setTraceStringConstants(true);
CallGraph CG = B.makeCallGraph(B.getOptions());
JSCallGraphUtil.AVOID_DUMP = false;
JSCallGraphUtil.dumpCG(B.getPointerAnalysis(), CG);
verifyGraphAssertions(CG, assertionsForStringPrims);
}
@ -439,7 +446,10 @@ public abstract class TestSimpleCallGraphShape extends TestJSCallGraphShape {
@Test
public void testReturnThis() throws IOException, IllegalArgumentException, CancelException {
CallGraph CG = JSCallGraphBuilderUtil.makeScriptCG("tests", "return_this.js");
PropagationCallGraphBuilder B = JSCallGraphBuilderUtil.makeScriptCGBuilder("tests", "return_this.js");
CallGraph CG = B.makeCallGraph(B.getOptions());
JSCallGraphUtil.AVOID_DUMP = false;
JSCallGraphUtil.dumpCG(B.getPointerAnalysis(), CG);
verifyGraphAssertions(CG, assertionsForReturnThis);
}
@ -522,6 +532,22 @@ public abstract class TestSimpleCallGraphShape extends TestJSCallGraphShape {
verifyGraphAssertions(CG, assertionsForNestedParamAssign);
}
private static final Object[][] assertionsForDispatch = new Object[][] {
new Object[] { ROOT, new String[] { "tests/dispatch.js" } },
new Object[] { "tests/dispatch.js", new String[] { "tests/dispatch.js/left_outer", "tests/dispatch.js/right_outer" } },
new Object[] { "tests/dispatch.js/left_outer", new String[]{ "tests/dispatch.js/left_inner" } },
new Object[] { "tests/dispatch.js/right_outer", new String[]{ "tests/dispatch.js/right_inner" } }
};
@Test
public void testDispatch() throws IOException, IllegalArgumentException, CancelException {
PropagationCallGraphBuilder B = JSCallGraphBuilderUtil.makeScriptCGBuilder("tests", "dispatch.js");
CallGraph CG = B.makeCallGraph(B.getOptions());
JSCallGraphUtil.AVOID_DUMP = false;
JSCallGraphUtil.dumpCG(B.getPointerAnalysis(), CG);
verifyGraphAssertions(CG, assertionsForDispatch);
}
protected IVector<Set<Pair<CGNode, Integer>>> computeIkIdToVns(PointerAnalysis pa) {
// Created by reversing the points to mapping for local pointer keys.

View File

@ -2,8 +2,8 @@
<classpath>
<classpathentry kind="src" path="source"/>
<classpathentry kind="src" path="dat"/>
<classpathentry kind="lib" path="lib/jericho-html-3.2.jar"/>
<classpathentry kind="con" path="org.eclipse.pde.core.requiredPlugins"/>
<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.6"/>
<classpathentry kind="lib" path="lib/jericho-html-3.2.jar"/>
<classpathentry kind="output" path="bin"/>
</classpath>

View File

@ -3,11 +3,16 @@ primitive = new Primitives();
// core definitions needed to make anything work, even what follows
Object = primitive("NewObject");
Function = primitive("NewFunction");
Array = primitive("NewArray");
String = primitive("NewString");
Number = primitive("NewNumber");
RegExp = primitive("NewRegExp");
var local_function = primitive("NewFunction");
Function = local_function;
var local_array = primitive("NewArray");
Array = local_array;
var local_string = primitive("NewString");
String = local_string;
var local_number = primitive("NewNumber");
Number = local_number;
var local_regexp = primitive("NewRegExp");
RegExp = local_regexp;
/************************************************************************/
/* Global properties, see spec 15.1 */
@ -104,7 +109,7 @@ Object.prototype = {
/* Function properties, see spec 15.3 */
/************************************************************************/
Function.prototype = {
local_function.prototype = {
constructor: Function,
@ -124,15 +129,15 @@ Function.prototype = {
}
};
Function.__proto__ = Function.prototype;
local_function.__proto__ = Function.prototype;
/************************************************************************/
/* Array properties, see spec 15.4 */
/************************************************************************/
Array.__proto__ = Function.prototype;
local_array.__proto__ = Function.prototype;
Array.prototype = {
local_array.prototype = {
__proto__: Object.prototype,
@ -251,9 +256,9 @@ Array.prototype = {
/* String properties, see spec 15.4 */
/************************************************************************/
String.__proto__ = Function.prototype;
local_string.__proto__ = Function.prototype;
String.prototype = {
local_string.prototype = {
__proto__: Object.prototype,
@ -329,9 +334,9 @@ String.prototype = {
/* Number properties, see spec 15.7 */
/************************************************************************/
Number.__proto__ = Function.prototype;
local_number.__proto__ = Function.prototype;
Number.prototype = {
local_number.prototype = {
__proto__: Object.prototype,
@ -419,9 +424,9 @@ Math = {
/* RegExp properties, see spec 15.10 */
/************************************************************************/
RegExp.__proto__ = Function.prototype;
local_regexp.__proto__ = Function.prototype;
RegExp.prototype = {
local_regexp.prototype = {
__proto__: Object.prototype,

View File

@ -22,6 +22,8 @@ import com.ibm.wala.cast.js.ssa.JavaScriptPropertyRead;
import com.ibm.wala.cast.js.ssa.JavaScriptPropertyWrite;
import com.ibm.wala.cast.js.ssa.JavaScriptTypeOfInstruction;
import com.ibm.wala.cast.js.ssa.JavaScriptWithRegion;
import com.ibm.wala.cast.js.ssa.PrototypeLookup;
import com.ibm.wala.cast.js.ssa.SetPrototype;
import com.ibm.wala.cast.js.types.JavaScriptTypes;
import com.ibm.wala.fixpoint.IVariable;
import com.ibm.wala.ipa.cha.IClassHierarchy;
@ -36,7 +38,7 @@ public class JSTypeInference extends AstTypeInference {
}
protected void initialize() {
class JSTypeOperatorFactory extends AstTypeOperatorFactory implements com.ibm.wala.cast.js.ssa.InstructionVisitor {
class JSTypeOperatorFactory extends AstTypeOperatorFactory implements com.ibm.wala.cast.js.ssa.JSInstructionVisitor {
public void visitJavaScriptInvoke(JavaScriptInvoke inst) {
result = new DeclaredTypeOperator(new ConeType(cha.getRootClass()));
}
@ -61,6 +63,15 @@ public class JSTypeInference extends AstTypeInference {
public void visitWithRegion(JavaScriptWithRegion instruction) {
}
@Override
public void visitSetPrototype(SetPrototype instruction) {
}
@Override
public void visitPrototypeLookup(PrototypeLookup instruction) {
result = new DeclaredTypeOperator(new ConeType(cha.getRootClass()));
}
}
;

View File

@ -11,7 +11,7 @@
package com.ibm.wala.cast.js.cfg;
import com.ibm.wala.cast.ir.cfg.AstInducedCFG;
import com.ibm.wala.cast.js.ssa.InstructionVisitor;
import com.ibm.wala.cast.js.ssa.JSInstructionVisitor;
import com.ibm.wala.cast.js.ssa.JavaScriptCheckReference;
import com.ibm.wala.cast.js.ssa.JavaScriptInstanceOf;
import com.ibm.wala.cast.js.ssa.JavaScriptInvoke;
@ -19,6 +19,8 @@ import com.ibm.wala.cast.js.ssa.JavaScriptPropertyRead;
import com.ibm.wala.cast.js.ssa.JavaScriptPropertyWrite;
import com.ibm.wala.cast.js.ssa.JavaScriptTypeOfInstruction;
import com.ibm.wala.cast.js.ssa.JavaScriptWithRegion;
import com.ibm.wala.cast.js.ssa.PrototypeLookup;
import com.ibm.wala.cast.js.ssa.SetPrototype;
import com.ibm.wala.classLoader.IMethod;
import com.ibm.wala.ipa.callgraph.Context;
import com.ibm.wala.ssa.SSAInstruction;
@ -29,7 +31,7 @@ public class JSInducedCFG extends AstInducedCFG {
super(instructions, method, context);
}
class JSPEIVisitor extends AstPEIVisitor implements InstructionVisitor {
class JSPEIVisitor extends AstPEIVisitor implements JSInstructionVisitor {
JSPEIVisitor(boolean[] r) {
super(r);
@ -59,9 +61,17 @@ public class JSInducedCFG extends AstInducedCFG {
public void visitWithRegion(JavaScriptWithRegion instruction) {
}
@Override
public void visitSetPrototype(SetPrototype instruction) {
}
@Override
public void visitPrototypeLookup(PrototypeLookup instruction) {
}
}
class JSBranchVisitor extends AstBranchVisitor implements InstructionVisitor {
class JSBranchVisitor extends AstBranchVisitor implements JSInstructionVisitor {
JSBranchVisitor(boolean[] r) {
super(r);
@ -87,6 +97,14 @@ public class JSInducedCFG extends AstInducedCFG {
public void visitWithRegion(JavaScriptWithRegion instruction) {
}
@Override
public void visitSetPrototype(SetPrototype instruction) {
}
@Override
public void visitPrototypeLookup(PrototypeLookup instruction) {
}
}
protected BranchVisitor makeBranchVisitor(boolean[] r) {

View File

@ -1,8 +1,9 @@
package com.ibm.wala.cast.js.ipa.callgraph;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.regex.*;
import java.util.regex.Pattern;
import com.ibm.wala.cast.ipa.callgraph.AstContextInsensitiveSSAContextInterpreter;
import com.ibm.wala.cast.ir.ssa.AstIRFactory;
@ -17,6 +18,8 @@ import com.ibm.wala.cast.tree.CAstEntity;
import com.ibm.wala.cast.tree.CAstNode;
import com.ibm.wala.cast.tree.impl.CAstBasicRewriter;
import com.ibm.wala.cast.tree.impl.CAstImpl;
import com.ibm.wala.cast.util.CAstPattern;
import com.ibm.wala.cast.util.CAstPattern.Segments;
import com.ibm.wala.cfg.AbstractCFG;
import com.ibm.wala.cfg.ControlFlowGraph;
import com.ibm.wala.classLoader.CallSiteReference;
@ -36,12 +39,13 @@ import com.ibm.wala.ssa.SSAAbstractInvokeInstruction;
import com.ibm.wala.ssa.SSAOptions;
import com.ibm.wala.ssa.SymbolTable;
import com.ibm.wala.types.TypeReference;
import com.ibm.wala.util.collections.HashMapFactory;
import com.ibm.wala.util.collections.Pair;
import com.ibm.wala.util.intset.IntSet;
public class ArgumentSpecialization {
private static final Pattern baseNameRegex = Pattern.compile("[$-]*base[$-]*[0-9]*");
private static final Pattern baseNameRegex = Pattern.compile("[$]+destructuring[$]rcvr*[0-9]*");
public static class ArgumentSpecializationContextIntepreter extends AstContextInsensitiveSSAContextInterpreter {
@ -136,6 +140,12 @@ public class ArgumentSpecialization {
}
public static class ArgumentCountIRFactory extends AstIRFactory.AstDefaultIRFactory {
private static final CAstPattern directAccessPattern = CAstPattern.parse("|(ARRAY_REF(VAR(\"arguments\"),<value>*)||OBJECT_REF(VAR(\"arguments\"),<value>*))|");
private static final CAstPattern destructuredAccessPattern = CAstPattern.parse("BLOCK_EXPR(ASSIGN(VAR(/[$][$]destructure[$]rcvr[0-9]+/),VAR(\"arguments\")),ASSIGN(VAR(<name>/[$][$]destructure[$]elt[0-9]+/),<value>*))");
private static final CAstPattern destructuredCallPattern = CAstPattern.parse("CALL(VAR(<name>/[$][$]destructure[$]elt[0-9]+/),\"dispatch\",VAR(<thisptr>/[$][$]destructure[$]rcvr[0-9]+/),<args>**)");
private final SSAOptions defaultOptions;
public ArgumentCountIRFactory(SSAOptions defaultOptions) {
@ -155,78 +165,21 @@ public class ArgumentSpecialization {
final Retranslatable m = (Retranslatable)method;
if (v != null) {
final JavaScriptLoader myloader = (JavaScriptLoader) method.getDeclaringClass().getClassLoader();
class FixedArgumentsRewriter extends CAstBasicRewriter {
private final CAstEntity e = m.getEntity();
private final CAstEntity e;
Map<String, CAstNode> argRefs = HashMapFactory.make();
public FixedArgumentsRewriter(CAst Ast) {
super(Ast, false);
}
private boolean isNamedVar(CAstNode n, String name) {
if (n.getKind() == CAstNode.VAR) {
String nm = (String) n.getChild(0).getValue();
return nm.equals(name);
}
return false;
}
private boolean isNamedVar(CAstNode n, Pattern namePattern) {
if (n.getKind() == CAstNode.VAR) {
String nm = (String) n.getChild(0).getValue();
return namePattern.matcher(nm).matches();
}
return false;
}
private Object getIndexFromArgumentRef(CAstNode n) {
if (n.getKind() == CAstNode.OBJECT_REF || n.getKind() == CAstNode.ARRAY_REF) {
if (isNamedVar(n.getChild(0), "arguments")) {
return n.getChild(1).getValue();
}
}
return null;
}
private Object getIndexFromBaseVar(CAstNode n) {
if (n.getKind() == CAstNode.BLOCK_EXPR) {
if (n.getChildCount() == 2) {
CAstNode c1 = n.getChild(0);
if (c1.getKind() == CAstNode.ASSIGN) {
if (isNamedVar(c1.getChild(0), baseNameRegex)) {
if (isNamedVar(c1.getChild(1), "arguments")) {
CAstNode c2 = n.getChild(1);
if (c2.getKind() == CAstNode.OBJECT_REF || c2.getKind() == CAstNode.ARRAY_REF) {
if (isNamedVar(c2.getChild(0), baseNameRegex)) {
return c2.getChild(1).getValue();
}
}
}
}
}
}
}
return null;
}
private Object getStaticArgumentIndex(CAstNode n) {
Object x = getIndexFromArgumentRef(n);
if (x != null) {
return x;
} else {
return getIndexFromBaseVar(n);
this.e = m.getEntity();
for(Segments s : CAstPattern.findAll(destructuredAccessPattern, m.getEntity())) {
argRefs.put(s.getSingle("name").getValue().toString(), s.getSingle("value"));
}
}
private CAstNode handleArgumentRef(CAstNode n) {
Object x = getStaticArgumentIndex(n);
Object x = n.getValue();
if (x != null) {
if (x instanceof Number && ((Number)x).intValue() < v.getValue()-2) {
int arg = ((Number)x).intValue() + 2;
@ -250,18 +203,31 @@ public class ArgumentSpecialization {
Map<Pair<CAstNode, NoKey>, CAstNode> nodeMap)
{
CAstNode result = null;
if (root.getKind() == CAstNode.ARRAY_REF
|| root.getKind() == CAstNode.OBJECT_REF
|| root.getKind() == CAstNode.BLOCK_EXPR)
{
result = handleArgumentRef(root);
Segments s;
if ((s = CAstPattern.match(directAccessPattern, root)) != null) {
result = handleArgumentRef(s.getSingle("value"));
} else if ((s = CAstPattern.match(destructuredCallPattern, root)) != null) {
if (argRefs.containsKey(s.getSingle("name").getValue().toString())) {
List<CAstNode> x = new ArrayList<CAstNode>();
CAstNode ref = handleArgumentRef(argRefs.get(s.getSingle("name").getValue().toString()));
if (ref != null) {
x.add(ref);
x.add(Ast.makeConstant("do"));
x.add(Ast.makeNode(CAstNode.VAR, Ast.makeConstant("arguments")));
for (CAstNode c : s.getMultiple("args")) {
x.add(copyNodes(c, cfg, context, nodeMap));
}
result = Ast.makeNode(CAstNode.CALL, x.toArray(new CAstNode[ x.size() ]));
}
}
} else if (root.getKind() == CAstNode.CONSTANT) {
result = Ast.makeConstant(root.getValue());
} else if (root.getKind() == CAstNode.OPERATOR) {
result = root;
}
if (result == null) {
@ -280,7 +246,6 @@ public class ArgumentSpecialization {
nodeMap.put(Pair.make(root, context.key()), result);
return result;
}
}

View File

@ -10,6 +10,7 @@
*****************************************************************************/
package com.ibm.wala.cast.js.ipa.callgraph;
import java.io.UnsupportedEncodingException;
import java.net.URL;
import java.util.Set;
@ -21,7 +22,7 @@ import com.ibm.wala.cast.ir.ssa.AstIsDefinedInstruction;
import com.ibm.wala.cast.ir.ssa.EachElementHasNextInstruction;
import com.ibm.wala.cast.js.analysis.typeInference.JSTypeInference;
import com.ibm.wala.cast.js.ipa.callgraph.JSSSAPropagationCallGraphBuilder.JSPointerAnalysisImpl.JSImplicitPointsToSetVisitor;
import com.ibm.wala.cast.js.ssa.InstructionVisitor;
import com.ibm.wala.cast.js.ssa.JSInstructionVisitor;
import com.ibm.wala.cast.js.ssa.JavaScriptCheckReference;
import com.ibm.wala.cast.js.ssa.JavaScriptInstanceOf;
import com.ibm.wala.cast.js.ssa.JavaScriptInvoke;
@ -29,6 +30,9 @@ import com.ibm.wala.cast.js.ssa.JavaScriptPropertyRead;
import com.ibm.wala.cast.js.ssa.JavaScriptPropertyWrite;
import com.ibm.wala.cast.js.ssa.JavaScriptTypeOfInstruction;
import com.ibm.wala.cast.js.ssa.JavaScriptWithRegion;
import com.ibm.wala.cast.js.ssa.PrototypeLookup;
import com.ibm.wala.cast.js.ssa.SetPrototype;
import com.ibm.wala.cast.js.types.JavaScriptMethods;
import com.ibm.wala.cast.js.types.JavaScriptTypes;
import com.ibm.wala.cast.loader.AstMethod;
import com.ibm.wala.cast.tree.CAstSourcePositionMap.Position;
@ -36,11 +40,13 @@ import com.ibm.wala.classLoader.IClass;
import com.ibm.wala.classLoader.IField;
import com.ibm.wala.classLoader.IMethod;
import com.ibm.wala.fixpoint.AbstractOperator;
import com.ibm.wala.fixpoint.UnaryOperator;
import com.ibm.wala.ipa.callgraph.AnalysisCache;
import com.ibm.wala.ipa.callgraph.AnalysisOptions;
import com.ibm.wala.ipa.callgraph.CGNode;
import com.ibm.wala.ipa.callgraph.CallGraph;
import com.ibm.wala.ipa.callgraph.impl.ExplicitCallGraph;
import com.ibm.wala.ipa.callgraph.propagation.AbstractFieldPointerKey;
import com.ibm.wala.ipa.callgraph.propagation.ConcreteTypeKey;
import com.ibm.wala.ipa.callgraph.propagation.ConstantKey;
import com.ibm.wala.ipa.callgraph.propagation.FilteredPointerKey;
@ -191,7 +197,7 @@ public class JSSSAPropagationCallGraphBuilder extends AstSSAPropagationCallGraph
system.newConstraint(exceptionVar, assignOperator, e);
}
public static class JSInterestingVisitor extends AstInterestingVisitor implements com.ibm.wala.cast.js.ssa.InstructionVisitor {
public static class JSInterestingVisitor extends AstInterestingVisitor implements com.ibm.wala.cast.js.ssa.JSInstructionVisitor {
public JSInterestingVisitor(int vn) {
super(vn);
}
@ -227,6 +233,16 @@ public class JSSSAPropagationCallGraphBuilder extends AstSSAPropagationCallGraph
public void visitWithRegion(JavaScriptWithRegion instruction) {
}
@Override
public void visitSetPrototype(SetPrototype instruction) {
bingo = true;
}
@Override
public void visitPrototypeLookup(PrototypeLookup instruction) {
bingo = true;
}
}
protected InterestingVisitor makeInterestingVisitor(CGNode node, int vn) {
@ -247,7 +263,7 @@ public class JSSSAPropagationCallGraphBuilder extends AstSSAPropagationCallGraph
}
public static class JSImplicitPointsToSetVisitor extends AstImplicitPointsToSetVisitor implements
com.ibm.wala.cast.js.ssa.InstructionVisitor {
com.ibm.wala.cast.js.ssa.JSInstructionVisitor {
public JSImplicitPointsToSetVisitor(AstPointerAnalysisImpl analysis, LocalPointerKey lpk) {
super(analysis, lpk);
@ -301,6 +317,14 @@ public class JSSSAPropagationCallGraphBuilder extends AstSSAPropagationCallGraph
pointsToSet = new OrdinalSet<InstanceKey>(S, analysis.getInstanceKeyMapping());
}
@Override
public void visitSetPrototype(SetPrototype instruction) {
}
@Override
public void visitPrototypeLookup(PrototypeLookup instruction) {
}
};
protected ImplicitPointsToSetVisitor makeImplicitPointsToVisitor(LocalPointerKey lpk) {
@ -346,7 +370,7 @@ public class JSSSAPropagationCallGraphBuilder extends AstSSAPropagationCallGraph
return null;
}
public static class JSConstraintVisitor extends AstConstraintVisitor implements InstructionVisitor {
public static class JSConstraintVisitor extends AstConstraintVisitor implements JSInstructionVisitor {
public JSConstraintVisitor(AstSSAPropagationCallGraphBuilder builder, CGNode node) {
super(builder, node);
@ -479,9 +503,135 @@ public class JSSSAPropagationCallGraphBuilder extends AstSSAPropagationCallGraph
}
public void visitJavaScriptInvoke(JavaScriptInvoke instruction) {
visitInvokeInternal(instruction);
if (instruction.getDeclaredTarget().equals(JavaScriptMethods.dispatchReference)) {
handleJavascriptDispatch(instruction);
} else {
visitInvokeInternal(instruction, new DefaultInvariantComputer());
}
}
private void handleJavascriptDispatch(final JavaScriptInvoke instruction, final InstanceKey receiverType) {
int functionVn = instruction.getFunction();
ReflectedFieldAction fieldDispatchAction = new ReflectedFieldAction() {
@Override
public void action(final AbstractFieldPointerKey fieldKey) {
class FieldValueDispatch extends UnaryOperator<PointsToSetVariable> {
private JavaScriptInvoke getInstruction() { return instruction; }
private InstanceKey getReceiver() { return receiverType; }
private AbstractFieldPointerKey getProperty() { return fieldKey; }
@Override
public byte evaluate(PointsToSetVariable lhs, PointsToSetVariable ptrs) {
if (ptrs.getValue() != null) {
ptrs.getValue().foreach(new IntSetAction() {
@Override
public void act(int x) {
final InstanceKey functionObj = system.getInstanceKey(x);
visitInvokeInternal(instruction, new DefaultInvariantComputer() {
@Override
public InstanceKey[][] computeInvariantParameters(SSAAbstractInvokeInstruction call) {
InstanceKey[][] x = super.computeInvariantParameters(call);
if (x == null) {
x = new InstanceKey[call.getNumberOfUses()][];
}
x[0] = new InstanceKey[]{ functionObj };
x[1] = new InstanceKey[]{ receiverType };
return x;
}
});
}
});
}
return NOT_CHANGED;
}
@Override
public int hashCode() {
return instruction.hashCode() * fieldKey.hashCode() * receiverType.hashCode();
}
@Override
public boolean equals(Object o) {
return o instanceof FieldValueDispatch &&
((FieldValueDispatch)o).getInstruction() == instruction &&
((FieldValueDispatch)o).getProperty().equals(fieldKey) &&
((FieldValueDispatch)o).getReceiver().equals(receiverType);
}
@Override
public String toString() {
return "sub-dispatch for " + instruction + ": " + receiverType + ", " + fieldKey;
}
};
system.newSideEffect(new FieldValueDispatch(), fieldKey);
}
@Override
public void dump(AbstractFieldPointerKey fieldKey, boolean constObj, boolean constProp) {
System.err.println("dispatch to " + receiverType + "." + fieldKey + " for " + instruction);
}
};
TransitivePrototypeKey prototypeObjs = new TransitivePrototypeKey(receiverType);
InstanceKey[] objKeys = new InstanceKey[]{ receiverType };
if (contentsAreInvariant(symbolTable, du, functionVn)) {
InstanceKey[] fieldsKeys = getInvariantContents(functionVn);
newFieldOperationObjectAndFieldConstant(true, fieldDispatchAction, objKeys, fieldsKeys);
newFieldOperationOnlyFieldConstant(true, fieldDispatchAction, prototypeObjs, fieldsKeys);
} else {
PointerKey fieldKey = getPointerKeyForLocal(functionVn);
newFieldOperationOnlyObjectConstant(true, fieldDispatchAction, fieldKey, objKeys);
newFieldFullOperation(true, fieldDispatchAction, prototypeObjs, fieldKey);
}
}
private void handleJavascriptDispatch(final JavaScriptInvoke instruction) {
int receiverVn = instruction.getUse(1);
PointerKey receiverKey = getPointerKeyForLocal(receiverVn);
if (contentsAreInvariant(symbolTable, du, receiverVn)) {
system.recordImplicitPointsToSet(receiverKey);
InstanceKey[] ik = getInvariantContents(receiverVn);
for (int i = 0; i < ik.length; i++) {
handleJavascriptDispatch(instruction, ik[i]);
}
} else {
class ReceiverForDispatchOp extends UnaryOperator<PointsToSetVariable> {
private JavaScriptInvoke getInstruction() {
return instruction;
}
@Override
public byte evaluate(PointsToSetVariable lhs, PointsToSetVariable rhs) {
if (rhs.getValue() != null) {
rhs.getValue().foreach(new IntSetAction() {
@Override
public void act(int x) {
InstanceKey ik = system.getInstanceKey(x);
handleJavascriptDispatch(instruction, ik);
}
});
}
return NOT_CHANGED;
}
@Override
public int hashCode() {
return instruction.hashCode();
}
@Override
public boolean equals(Object o) {
return o instanceof ReceiverForDispatchOp && ((ReceiverForDispatchOp)o).getInstruction()==getInstruction();
}
@Override
public String toString() {
return "receiver for dispatch: " + instruction;
}
}
system.newSideEffect(new ReceiverForDispatchOp(), receiverKey);
}
}
// ///////////////////////////////////////////////////////////////////////////
//
// string manipulation handling for binary operators
@ -637,6 +787,98 @@ public class JSSSAPropagationCallGraphBuilder extends AstSSAPropagationCallGraph
// TODO Auto-generated method stub
}
private final UnaryOperator<PointsToSetVariable> transitivePrototypeOp = new UnaryOperator<PointsToSetVariable>() {
@Override
public byte evaluate(final PointsToSetVariable lhs, PointsToSetVariable rhs) {
class Op implements IntSetAction {
private boolean changed = false;
@Override
public void act(int x) {
InstanceKey protoObj = system.getInstanceKey(x);
PointerKey protoObjKey = new TransitivePrototypeKey(protoObj);
changed |= system.newStatement(lhs, assignOperator, system.findOrCreatePointsToSet(protoObjKey), true, true);
}
}
if (rhs.getValue() != null) {
Op op = new Op();
rhs.getValue().foreach(op);
return (op.changed? CHANGED: NOT_CHANGED);
}
return NOT_CHANGED;
}
@Override
public int hashCode() {
return -896435647;
}
@Override
public boolean equals(Object o) {
return o == this;
}
@Override
public String toString() {
return "transitivePrototypeOp";
}
};
private final FieldReference prototypeRef;
{
FieldReference x = null;
try {
byte[] utf8 = "__proto__".getBytes("UTF-8");
x = FieldReference.findOrCreate(JavaScriptTypes.Root, Atom.findOrCreate(utf8, 0, utf8.length), JavaScriptTypes.Root);
} catch (UnsupportedEncodingException e) {
assert false;
}
prototypeRef = x;
}
@Override
public void visitSetPrototype(SetPrototype instruction) {
visitPutInternal(instruction.getUse(1), instruction.getUse(0), false, prototypeRef);
assert contentsAreInvariant(symbolTable, du, instruction.getUse(0));
if (contentsAreInvariant(symbolTable, du, instruction.getUse(1))) {
for(InstanceKey newObj : getInvariantContents(instruction.getUse(0))) {
PointerKey newObjKey = new TransitivePrototypeKey(newObj);
for(InstanceKey protoObj : getInvariantContents(instruction.getUse(1))) {
system.newConstraint(newObjKey, protoObj);
system.newConstraint(newObjKey, assignOperator, new TransitivePrototypeKey(protoObj));
}
}
} else {
for(InstanceKey newObj : getInvariantContents(instruction.getUse(0))) {
PointerKey newObjKey = new TransitivePrototypeKey(newObj);
system.newConstraint(newObjKey, assignOperator, getPointerKeyForLocal(instruction.getUse(1)));
system.newConstraint(newObjKey, transitivePrototypeOp, getPointerKeyForLocal(instruction.getUse(1)));
}
}
}
@Override
public void visitPrototypeLookup(PrototypeLookup instruction) {
if (contentsAreInvariant(symbolTable, du, instruction.getUse(0))) {
for(InstanceKey rhsObj : getInvariantContents(instruction.getUse(0))) {
// property can come from object itself...
system.newConstraint(getPointerKeyForLocal(instruction.getDef(0)), rhsObj);
// ...or prototype objects
system.newConstraint(getPointerKeyForLocal(instruction.getDef(0)), assignOperator, new TransitivePrototypeKey(rhsObj));
}
} else {
// property can come from object itself...
system.newConstraint(getPointerKeyForLocal(instruction.getDef(0)), assignOperator, getPointerKeyForLocal(instruction.getUse(0)));
// ...or prototype objects
system.newConstraint(getPointerKeyForLocal(instruction.getDef(0)), transitivePrototypeOp, getPointerKeyForLocal(instruction.getUse(0)));
}
}
}
// ///////////////////////////////////////////////////////////////////////////

View File

@ -92,7 +92,8 @@ public class JavaScriptConstructTargetSelector implements MethodTargetSelector {
S.addStatement(insts.NewInstruction(5, NewSiteReference.make(S.getNextProgramCounter(), cls.getReference())));
S.addStatement(insts.PutInstruction(5, 4, "__proto__"));
S.addStatement(insts.SetPrototype(5, 4));
//S.addStatement(insts.PutInstruction(5, 4, "__proto__"));
S.getNextProgramCounter();
S.addConstant(new Integer(8), new ConstantValue(value));
@ -102,6 +103,8 @@ public class JavaScriptConstructTargetSelector implements MethodTargetSelector {
S.addStatement(insts.ReturnInstruction(5, false));
S.getNextProgramCounter();
//S.addConstant(9, new ConstantValue("__proto__"));
return new JavaScriptConstructor(ref, S, cls);
}
@ -115,7 +118,8 @@ public class JavaScriptConstructTargetSelector implements MethodTargetSelector {
S.addStatement(insts.NewInstruction(6, NewSiteReference.make(S.getNextProgramCounter(), cls.getReference())));
S.addStatement(insts.PutInstruction(6, 5, "__proto__"));
S.addStatement(insts.SetPrototype(6, 5));
//S.addStatement(insts.PutInstruction(6, 5, "__proto__"));
S.getNextProgramCounter();
S.addStatement(insts.PutInstruction(6, 2, "$value"));
@ -123,7 +127,9 @@ public class JavaScriptConstructTargetSelector implements MethodTargetSelector {
S.addStatement(insts.ReturnInstruction(6, false));
S.getNextProgramCounter();
//S.addConstant(7, new ConstantValue("__proto__"));
return new JavaScriptConstructor(ref, S, cls);
}
@ -155,12 +161,15 @@ public class JavaScriptConstructTargetSelector implements MethodTargetSelector {
S.addStatement(insts.NewInstruction(5, NewSiteReference.make(S.getNextProgramCounter(), JavaScriptTypes.Object)));
S.addStatement(insts.PutInstruction(5, 4, "__proto__"));
S.addStatement(insts.SetPrototype(5, 4));
//S.addStatement(insts.PutInstruction(5, 4, "__proto__"));
S.getNextProgramCounter();
S.addStatement(insts.ReturnInstruction(5, false));
S.getNextProgramCounter();
//S.addConstant(6, new ConstantValue("__proto__"));
return new JavaScriptConstructor(ref, S, cls);
}
@ -212,7 +221,8 @@ public class JavaScriptConstructTargetSelector implements MethodTargetSelector {
S.addStatement(insts.NewInstruction(6, NewSiteReference.make(S.getNextProgramCounter(), JavaScriptTypes.Array)));
S.addStatement(insts.PutInstruction(6, 5, "__proto__"));
S.addStatement(insts.SetPrototype(6, 5));
//S.addStatement(insts.PutInstruction(6, 5, "__proto__"));
S.getNextProgramCounter();
S.addStatement(insts.PutInstruction(6, 2, "length"));
@ -220,7 +230,9 @@ public class JavaScriptConstructTargetSelector implements MethodTargetSelector {
S.addStatement(insts.ReturnInstruction(6, false));
S.getNextProgramCounter();
//S.addConstant(7, new ConstantValue("__proto__"));
return new JavaScriptConstructor(ref, S, cls);
}
@ -237,7 +249,8 @@ public class JavaScriptConstructTargetSelector implements MethodTargetSelector {
insts.NewInstruction(nargs + 5, NewSiteReference.make(S.getNextProgramCounter(),
JavaScriptTypes.Array)));
S.addStatement(insts.PutInstruction(nargs + 5, nargs + 4, "__proto__"));
S.addStatement(insts.SetPrototype(nargs + 5, nargs + 4));
//S.addStatement(insts.PutInstruction(nargs + 5, nargs + 4, "__proto__"));
S.getNextProgramCounter();
S.addConstant(new Integer(nargs + 7), new ConstantValue(nargs));
@ -254,6 +267,8 @@ public class JavaScriptConstructTargetSelector implements MethodTargetSelector {
S.addStatement(insts.ReturnInstruction(5, false));
S.getNextProgramCounter();
//S.addConstant(vn, new ConstantValue("__proto__"));
return new JavaScriptConstructor(ref, S, cls);
}
@ -362,7 +377,8 @@ public class JavaScriptConstructTargetSelector implements MethodTargetSelector {
S.addStatement(insts.NewInstruction(7, NewSiteReference.make(S.getNextProgramCounter(), JavaScriptTypes.Object)));
S.addStatement(insts.PutInstruction(5, 4, "__proto__"));
S.addStatement(insts.SetPrototype(5, 4));
//S.addStatement(insts.PutInstruction(5, 4, "__proto__"));
S.getNextProgramCounter();
S.addStatement(insts.PutInstruction(5, 7, "prototype"));
@ -375,6 +391,8 @@ public class JavaScriptConstructTargetSelector implements MethodTargetSelector {
S.addStatement(insts.ReturnInstruction(5, false));
S.getNextProgramCounter();
//S.addConstant(8, new ConstantValue("__proto__"));
if (receiver != cls)
return record(tableKey, new JavaScriptConstructor(ref, S, receiver, "(" + cls.getReference().getName() + ")"));
else
@ -465,7 +483,6 @@ public class JavaScriptConstructTargetSelector implements MethodTargetSelector {
MethodReference ref = JavaScriptMethods.makeCtorReference(cls.getReference());
JavaScriptSummary S = new JavaScriptSummary(ref, nargs + 1);
S.addStatement(insts.GetInstruction(nargs + 4, 1, "prototype"));
S.getNextProgramCounter();
@ -474,7 +491,8 @@ public class JavaScriptConstructTargetSelector implements MethodTargetSelector {
NewSiteReference.make(S.getNextProgramCounter(),
JavaScriptTypes.Object)));
S.addStatement(insts.PutInstruction(nargs + 5, nargs + 4, "__proto__"));
S.addStatement(insts.SetPrototype(nargs + 5, nargs + 4));
//S.addStatement(insts.PutInstruction(nargs + 5, nargs + 4, "__proto__"));
S.getNextProgramCounter();
CallSiteReference cs = new JSCallSiteReference(S.getNextProgramCounter());
@ -490,6 +508,8 @@ public class JavaScriptConstructTargetSelector implements MethodTargetSelector {
S.addStatement(insts.ReturnInstruction(nargs + 5, false));
S.getNextProgramCounter();
//S.addConstant(nargs + 9, new ConstantValue("__proto__"));
return record(key, new JavaScriptConstructor(ref, S, cls));
}

View File

@ -0,0 +1,38 @@
/******************************************************************************
* 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.cast.js.ipa.callgraph;
import com.ibm.wala.ipa.callgraph.propagation.AbstractFieldPointerKey;
import com.ibm.wala.ipa.callgraph.propagation.InstanceKey;
public class TransitivePrototypeKey extends AbstractFieldPointerKey {
public String getName() {
return "transitive prototype of "+getInstanceKey().toString();
}
public TransitivePrototypeKey(InstanceKey object) {
super(object);
}
public boolean equals(Object x) {
return (x instanceof TransitivePrototypeKey) &&
((TransitivePrototypeKey)x).getInstanceKey().equals(getInstanceKey());
}
public int hashCode() {
return getInstanceKey().hashCode();
}
public String toString() {
return "<proto:" + getName() + ">";
}
}

View File

@ -45,6 +45,8 @@ import com.ibm.wala.cast.js.ssa.JavaScriptPropertyRead;
import com.ibm.wala.cast.js.ssa.JavaScriptPropertyWrite;
import com.ibm.wala.cast.js.ssa.JavaScriptTypeOfInstruction;
import com.ibm.wala.cast.js.ssa.JavaScriptWithRegion;
import com.ibm.wala.cast.js.ssa.PrototypeLookup;
import com.ibm.wala.cast.js.ssa.SetPrototype;
import com.ibm.wala.cast.js.translator.JSAstTranslator;
import com.ibm.wala.cast.js.translator.JavaScriptTranslatorFactory;
import com.ibm.wala.cast.js.types.JavaScriptTypes;
@ -517,6 +519,16 @@ public class JavaScriptLoader extends CAstAbstractModuleLoader {
throw new UnsupportedOperationException();
}
@Override
public PrototypeLookup PrototypeLookup(int lval, int object) {
return new PrototypeLookup(lval, object);
}
@Override
public SetPrototype SetPrototype(int object, int prototype) {
return new SetPrototype(object, prototype);
}
};
}

View File

@ -12,9 +12,9 @@ package com.ibm.wala.cast.js.ssa;
import com.ibm.wala.cast.ir.ssa.AstAbstractInstructionVisitor;
public class AbstractInstructionVisitor
public class JSAbstractInstructionVisitor
extends AstAbstractInstructionVisitor
implements InstructionVisitor
implements JSInstructionVisitor
{
public void visitJavaScriptInvoke(JavaScriptInvoke instruction) {
@ -45,5 +45,15 @@ public class AbstractInstructionVisitor
}
@Override
public void visitSetPrototype(SetPrototype instruction) {
}
@Override
public void visitPrototypeLookup(PrototypeLookup instruction) {
}
}

View File

@ -32,4 +32,8 @@ public interface JSInstructionFactory extends AstInstructionFactory {
JavaScriptWithRegion WithRegion(int expr, boolean isEnter);
PrototypeLookup PrototypeLookup(int lval, int object);
SetPrototype SetPrototype(int object, int prototype);
}

View File

@ -12,7 +12,7 @@ package com.ibm.wala.cast.js.ssa;
import com.ibm.wala.cast.ir.ssa.AstInstructionVisitor;
public interface InstructionVisitor extends AstInstructionVisitor {
public interface JSInstructionVisitor extends AstInstructionVisitor {
public void visitJavaScriptInvoke(JavaScriptInvoke instruction);
@ -28,5 +28,9 @@ public interface InstructionVisitor extends AstInstructionVisitor {
public void visitCheckRef(JavaScriptCheckReference instruction);
public void visitSetPrototype(SetPrototype instruction);
public void visitPrototypeLookup(PrototypeLookup instruction);
}

View File

@ -43,7 +43,7 @@ public class JavaScriptCheckReference extends SSAInstruction {
@Override
public void visit(IVisitor v) {
((InstructionVisitor)v).visitCheckRef(this);
((JSInstructionVisitor)v).visitCheckRef(this);
}
public boolean isPEI() {

View File

@ -56,7 +56,7 @@ public class JavaScriptInstanceOf extends SSAInstruction {
@Override
public void visit(IVisitor v) {
((InstructionVisitor)v).visitJavaScriptInstanceOf(this);
((JSInstructionVisitor)v).visitJavaScriptInstanceOf(this);
}
public int getNumberOfDefs() {

View File

@ -104,6 +104,8 @@ public class JavaScriptInvoke extends AbstractLexicalInvoke {
}
if (site.getDeclaredTarget().equals(JavaScriptMethods.ctorReference))
s.append("construct ");
else if (site.getDeclaredTarget().equals(JavaScriptMethods.dispatchReference))
s.append("dispatch ");
else
s.append("invoke ");
s.append(getValueString(symbolTable, function));
@ -151,8 +153,8 @@ public class JavaScriptInvoke extends AbstractLexicalInvoke {
* @see com.ibm.domo.ssa.Instruction#visit(Visitor)
*/
public void visit(IVisitor v) {
assert v instanceof InstructionVisitor;
((InstructionVisitor) v).visitJavaScriptInvoke(this);
assert v instanceof JSInstructionVisitor;
((JSInstructionVisitor) v).visitJavaScriptInvoke(this);
}
public int getNumberOfParameters() {

View File

@ -49,7 +49,7 @@ public class JavaScriptPropertyRead extends AbstractReflectiveGet {
* @see com.ibm.domo.ssa.SSAInstruction#visit(com.ibm.domo.ssa.SSAInstruction.Visitor)
*/
public void visit(IVisitor v) {
assert v instanceof InstructionVisitor;
((InstructionVisitor)v).visitJavaScriptPropertyRead(this);
assert v instanceof JSInstructionVisitor;
((JSInstructionVisitor)v).visitJavaScriptPropertyRead(this);
}
}

View File

@ -37,8 +37,8 @@ public class JavaScriptPropertyWrite extends AbstractReflectivePut {
* @see com.ibm.domo.ssa.Instruction#visit(Visitor)
*/
public void visit(IVisitor v) {
assert v instanceof InstructionVisitor;
((InstructionVisitor) v).visitJavaScriptPropertyWrite(this);
assert v instanceof JSInstructionVisitor;
((JSInstructionVisitor) v).visitJavaScriptPropertyWrite(this);
}
/*

View File

@ -33,7 +33,7 @@ public class JavaScriptTypeOfInstruction extends SSAAbstractUnaryInstruction {
}
public void visit(IVisitor v) {
((InstructionVisitor) v).visitTypeOf(this);
((JSInstructionVisitor) v).visitTypeOf(this);
}
public Collection<TypeReference> getExceptionTypes() {

View File

@ -43,7 +43,7 @@ public class JavaScriptWithRegion extends SSAInstruction {
@Override
public void visit(IVisitor v) {
((InstructionVisitor)v).visitWithRegion(this);
((JSInstructionVisitor)v).visitWithRegion(this);
}
public int getNumberOfUses() {

View File

@ -0,0 +1,29 @@
package com.ibm.wala.cast.js.ssa;
import com.ibm.wala.ssa.SSAAbstractUnaryInstruction;
import com.ibm.wala.ssa.SSAInstruction;
import com.ibm.wala.ssa.SSAInstructionFactory;
import com.ibm.wala.ssa.SymbolTable;
public class PrototypeLookup extends SSAAbstractUnaryInstruction {
public PrototypeLookup(int result, int val) {
super(result, val);
}
@Override
public SSAInstruction copyForSSA(SSAInstructionFactory insts, int[] defs, int[] uses) {
return ((JSInstructionFactory)insts).PrototypeLookup((defs != null ? defs[0] : getDef(0)), (uses != null ? uses[0] : getUse(0)));
}
@Override
public String toString(SymbolTable symbolTable) {
return getValueString(symbolTable, getDef(0)) + " = prototype_values(" + getValueString(symbolTable, getUse(0)) + ")";
}
@Override
public void visit(IVisitor v) {
((JSInstructionVisitor)v).visitPrototypeLookup(this);
}
}

View File

@ -0,0 +1,56 @@
package com.ibm.wala.cast.js.ssa;
import com.ibm.wala.ssa.SSAInstruction;
import com.ibm.wala.ssa.SSAInstructionFactory;
import com.ibm.wala.ssa.SymbolTable;
public class SetPrototype extends SSAInstruction {
private final int object;
private final int prototype;
public SetPrototype(int object, int prototype) {
this.object = object;
this.prototype = prototype;
}
@Override
public int getNumberOfUses() {
return 2;
}
@Override
public int getUse(int j) throws UnsupportedOperationException {
assert j >= 0 && j <= 1;
return (j==0)? object: prototype;
}
@Override
public SSAInstruction copyForSSA(SSAInstructionFactory insts, int[] defs, int[] uses) {
return ((JSInstructionFactory)insts).SetPrototype((uses != null ? uses[0] : object), (uses != null ? uses[1] : prototype));
}
@Override
public String toString(SymbolTable symbolTable) {
return "set_prototype(" + getValueString(symbolTable, object) + ", " + getValueString(symbolTable, prototype) + ")";
}
@Override
public void visit(IVisitor v) {
((JSInstructionVisitor)v).visitSetPrototype(this);
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + object;
result = prime * result + prototype;
return result;
}
@Override
public boolean isFallThrough() {
return true;
}
}

View File

@ -49,6 +49,10 @@ public class JSAstTranslator extends AstTranslator {
super(loader);
}
private boolean isPrologueScript(WalkContext context) {
return JavaScriptLoader.bootstrapFileNames.contains( context.getModule().getName() );
}
protected boolean useDefaultInitValues() {
return false;
}
@ -112,7 +116,7 @@ public class JSAstTranslator extends AstTranslator {
}
protected void defineField(CAstEntity topEntity, WalkContext wc, CAstEntity n) {
Assertions.UNREACHABLE("JavaScript doesn't have fields, numb-nuts!");
Assertions.UNREACHABLE("JavaScript doesn't have fields");
}
protected String composeEntityName(WalkContext parent, CAstEntity f) {
@ -152,12 +156,14 @@ public class JSAstTranslator extends AstTranslator {
}
protected void doCall(WalkContext context, CAstNode call, int result, int exception, CAstNode name, int receiver, int[] arguments) {
MethodReference ref = name.getValue().equals("ctor") ? JavaScriptMethods.ctorReference : AstMethodReference
.fnReference(JavaScriptTypes.CodeBody);
MethodReference ref =
name.getValue().equals("ctor") ? JavaScriptMethods.ctorReference
: name.getValue().equals("dispatch") ? JavaScriptMethods.dispatchReference
: AstMethodReference.fnReference(JavaScriptTypes.CodeBody);
context.cfg().addInstruction(
((JSInstructionFactory) insts).Invoke(receiver, result, arguments, exception, new JSCallSiteReference(ref, context.cfg()
.getCurrentInstruction())));
((JSInstructionFactory) insts).Invoke(receiver, result, arguments, exception,
new JSCallSiteReference(ref, context.cfg().getCurrentInstruction())));
context.cfg().addPreNode(call, context.getUnwindState());
@ -202,6 +208,8 @@ public class JSAstTranslator extends AstTranslator {
context.cfg().addInstruction(((JSInstructionFactory) insts).AssignInstruction(x, receiver));
context.cfg().addInstruction(((JSInstructionFactory) insts).PrototypeLookup(x, x));
if (elt.getKind() == CAstNode.CONSTANT && elt.getValue() instanceof String) {
String field = (String) elt.getValue();
// symtab needs to have this value
@ -228,14 +236,18 @@ public class JSAstTranslator extends AstTranslator {
this.visit(elt, context, this);
if (elt.getKind() == CAstNode.CONSTANT && elt.getValue() instanceof String) {
String field = (String) elt.getValue();
context.currentScope().getConstantValue(field);
SSAPutInstruction put = ((JSInstructionFactory) insts).PutInstruction(receiver, rval, field);
try {
assert field.equals(put.getDeclaredField().getName().toUnicodeString());
} catch (UTFDataFormatException e) {
Assertions.UNREACHABLE();
if (isPrologueScript(context) && "__proto__".equals(field)) {
context.cfg().addInstruction(((JSInstructionFactory) insts).SetPrototype(receiver, rval));
} else {
context.currentScope().getConstantValue(field);
SSAPutInstruction put = ((JSInstructionFactory) insts).PutInstruction(receiver, rval, field);
try {
assert field.equals(put.getDeclaredField().getName().toUnicodeString());
} catch (UTFDataFormatException e) {
Assertions.UNREACHABLE();
}
context.cfg().addInstruction(put);
}
context.cfg().addInstruction(put);
} else {
context.cfg().addInstruction(((JSInstructionFactory) insts).PropertyWrite(receiver, context.getValue(elt), rval));
}

View File

@ -35,11 +35,11 @@ public interface JavaScriptTranslatorToCAst extends TranslatorToCAst {
CAstNode getCatchTarget();
String getBaseVarIfRelevant(T node);
int setOperation(T node);
boolean foundBase(T node);
boolean foundMemberOperation(T node);
void updateBase(T from, T to);
void copyOperation(T from, T to);
}
@ -66,11 +66,21 @@ public interface JavaScriptTranslatorToCAst extends TranslatorToCAst {
return null;
}
public String getBaseVarIfRelevant(T node) { return null; }
@Override
public int setOperation(T node) {
return -1;
}
public boolean foundBase(T node) { return false; }
@Override
public boolean foundMemberOperation(T node) {
return false;
}
@Override
public void copyOperation(T from, T to) {
Assertions.UNREACHABLE();
}
public void updateBase(T from, T to) { }
}
class DelegatingContext<C extends WalkContext<C, T>, T> extends TranslatorToCAst.DelegatingContext<C, T> implements WalkContext<C,T> {
@ -99,16 +109,19 @@ public interface JavaScriptTranslatorToCAst extends TranslatorToCAst {
return parent.getCatchTarget();
}
public String getBaseVarIfRelevant(T node) {
return parent.getBaseVarIfRelevant(node);
@Override
public int setOperation(T node) {
return parent.setOperation(node);
}
public boolean foundBase(T node) {
return parent.foundBase(node);
@Override
public boolean foundMemberOperation(T node) {
return parent.foundMemberOperation(node);
}
public void updateBase(T from, T to) {
parent.updateBase(from, to);
@Override
public void copyOperation(T from, T to) {
parent.copyOperation(from, to);
}
}
@ -220,7 +233,7 @@ public interface JavaScriptTranslatorToCAst extends TranslatorToCAst {
* 'this' parameter in baseVar, and then to use baseVar as the actual argument
* sub-node for the CAst call node
*/
public class BaseCollectingContext<C extends WalkContext<C, T>, T> extends DelegatingContext<C,T> {
public class MemberDestructuringContext<C extends WalkContext<C, T>, T> extends DelegatingContext<C,T> {
/**
* node for which we actually care about what the base pointer is. this
@ -229,49 +242,33 @@ public interface JavaScriptTranslatorToCAst extends TranslatorToCAst {
*/
private final Set<T> baseFor = new HashSet<T>();
/**
* the variable to be used to store the value of the expression passed as
* the 'this' parameter
*/
private final String baseVar;
private int operationIndex;
/**
* have we discovered a value to be passed as the 'this' parameter?
*/
private boolean foundBase = false;
protected BaseCollectingContext(C parent, T initialBaseFor, String baseVar) {
protected MemberDestructuringContext(C parent, T initialBaseFor, int operationIndex) {
super(parent);
baseFor.add( initialBaseFor );
this.baseVar = baseVar;
this.operationIndex = operationIndex;
}
/**
* if node is one that we care about, return baseVar, and as a side effect
* set foundBase to true. Otherwise, return <code>null</code>.
*/
@Override
public String getBaseVarIfRelevant(T node) {
public int setOperation(T node) {
if (baseFor.contains( node )) {
foundBase = true;
return baseVar;
return operationIndex;
} else {
return null;
return -1;
}
}
@Override
public boolean foundBase(T node) {
public boolean foundMemberOperation(T node) {
return foundBase;
}
/**
* if we currently care about the base pointer of from, switch to searching
* for the base pointer of to. Used for cases like comma expressions: if we
* have (x,y.f)(), we want to assign y to baseVar
*/
@Override
public void updateBase(T from, T to) {
public void copyOperation(T from, T to) {
if (baseFor.contains(from)) baseFor.add(to);
}
}

View File

@ -30,4 +30,11 @@ public class JavaScriptMethods extends AstMethodReference {
return MethodReference.findOrCreate(cls, ctorAtom, ctorDesc);
}
public final static String dispatchAtomStr = "dispatch";
public final static Atom dispatchAtom = Atom.findOrCreateUnicodeAtom(dispatchAtomStr);
public final static String dispatchDescStr = "()LRoot;";
public final static Descriptor dispatchDesc = Descriptor.findOrCreateUTF8(JavaScriptLoader.JS, dispatchDescStr);
public final static MethodReference dispatchReference =
MethodReference.findOrCreate(JavaScriptTypes.CodeBody, dispatchAtom, dispatchDesc);
}

View File

@ -516,8 +516,8 @@ public abstract class AstSSAPropagationCallGraphBuilder extends SSAPropagationCa
}
protected void visitInvokeInternal(final SSAAbstractInvokeInstruction instruction) {
super.visitInvokeInternal(instruction);
protected void visitInvokeInternal(final SSAAbstractInvokeInstruction instruction, InvariantComputer invs) {
super.visitInvokeInternal(instruction, invs);
if (instruction instanceof AbstractLexicalInvoke) {
AbstractLexicalInvoke I = (AbstractLexicalInvoke) instruction;
for (int wi = 0; wi < I.getNumberOfDefs(); wi++) {
@ -981,7 +981,7 @@ public abstract class AstSSAPropagationCallGraphBuilder extends SSAPropagationCa
//
// /////////////////////////////////////////////////////////////////////////
private interface ReflectedFieldAction {
protected interface ReflectedFieldAction {
void action(AbstractFieldPointerKey fieldKey);
void dump(AbstractFieldPointerKey fieldKey, boolean constObj, boolean constProp);
@ -1090,7 +1090,7 @@ public abstract class AstSSAPropagationCallGraphBuilder extends SSAPropagationCa
}
private void newFieldFullOperation(final boolean isLoadOperation, final ReflectedFieldAction action, PointerKey objKey,
protected void newFieldFullOperation(final boolean isLoadOperation, final ReflectedFieldAction action, PointerKey objKey,
final PointerKey fieldKey) {
system.newSideEffect(new AbstractOperator<PointsToSetVariable>() {
private final MutableIntSet doneReceiver = IntSetUtil.make();
@ -1148,7 +1148,7 @@ public abstract class AstSSAPropagationCallGraphBuilder extends SSAPropagationCa
}, objKey, fieldKey);
}
private void newFieldOperationOnlyFieldConstant(final boolean isLoadOperation, final ReflectedFieldAction action,
protected void newFieldOperationOnlyFieldConstant(final boolean isLoadOperation, final ReflectedFieldAction action,
final PointerKey objKey, final InstanceKey[] fieldsKeys) {
system.newSideEffect(new UnaryOperator<PointsToSetVariable>() {
public byte evaluate(PointsToSetVariable lhs, PointsToSetVariable rhs) {
@ -1198,7 +1198,7 @@ public abstract class AstSSAPropagationCallGraphBuilder extends SSAPropagationCa
}, objKey);
}
private void newFieldOperationOnlyObjectConstant(final boolean isLoadOperation, final ReflectedFieldAction action,
protected void newFieldOperationOnlyObjectConstant(final boolean isLoadOperation, final ReflectedFieldAction action,
final PointerKey fieldKey, final InstanceKey[] objKeys) {
if (!isLoadOperation) {
for (int o = 0; o < objKeys.length; o++) {
@ -1245,7 +1245,7 @@ public abstract class AstSSAPropagationCallGraphBuilder extends SSAPropagationCa
}, fieldKey);
}
private void newFieldOperationObjectAndFieldConstant(final boolean isLoadOperation, final ReflectedFieldAction action,
protected void newFieldOperationObjectAndFieldConstant(final boolean isLoadOperation, final ReflectedFieldAction action,
final InstanceKey[] objKeys, InstanceKey[] fieldsKeys) {
for (int o = 0; o < objKeys.length; o++) {
PointerKey objCatalog = getPointerKeyForObjectCatalog(objKeys[o]);

View File

@ -95,7 +95,7 @@ public abstract class AstTranslator extends CAstVisitor<AstTranslator.WalkContex
/**
* set to true to use new handling of lexical scoping
*/
public final static boolean NEW_LEXICAL = true;
public static boolean NEW_LEXICAL = true;
/**

View File

@ -739,6 +739,7 @@ public abstract class CAstVisitor<C extends CAstVisitor.Context> {
case CAstNode.PRIMITIVE: {
if (visitor.visitPrimitive(n, context, visitor))
break;
visitor.visitAllChildren(n, context, visitor);
visitor.leavePrimitive(n, context, visitor);
break;
}

View File

@ -12,14 +12,20 @@ package com.ibm.wala.cast.util;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.TreeMap;
import java.util.regex.Pattern;
import com.ibm.wala.cast.tree.CAstEntity;
import com.ibm.wala.cast.tree.CAstNode;
import com.ibm.wala.cast.tree.visit.CAstVisitor;
import com.ibm.wala.cast.tree.visit.CAstVisitor.Context;
import com.ibm.wala.util.collections.HashMapFactory;
import com.ibm.wala.util.collections.HashSetFactory;
import com.ibm.wala.util.debug.Assertions;
public class CAstPattern {
@ -38,14 +44,14 @@ public class CAstPattern {
private final static int OPTIONAL_PATTERN_KIND = -5;
private final static int REFERENCE_PATTERN_KIND = -6;
private final static int IGNORE_KIND = -99;
private final String name;
private final int kind;
private final String value;
private final Object value;
private final CAstPattern[] children;
@ -59,16 +65,16 @@ public class CAstPattern {
}
@SuppressWarnings("unchecked")
public List<Object> getMultiple(String name) {
public List<CAstNode> getMultiple(String name) {
if (!containsKey(name)) {
return Collections.emptyList();
} else {
Object o = get(name);
if (o instanceof CAstNode) {
return Collections.singletonList(o);
return Collections.singletonList((CAstNode)o);
} else {
assert o instanceof List;
return (List<Object>) o;
return (List<CAstNode>) o;
}
}
}
@ -114,7 +120,7 @@ public class CAstPattern {
this.references = null;
}
public CAstPattern(String name, String value) {
public CAstPattern(String name, Object value) {
this.name = name;
this.kind = IGNORE_KIND;
this.value = value;
@ -140,6 +146,8 @@ public class CAstPattern {
if (value != null) {
if (kind == REFERENCE_PATTERN_KIND) {
sb.append("ref:").append(value);
} else if (value instanceof Pattern) {
sb.append("/").append(value).append("/");
} else {
sb.append("literal:").append(value);
}
@ -296,8 +304,12 @@ public class CAstPattern {
return false;
} else {
if ((value == null) ? tree.getKind() != kind : (tree.getKind() != CAstNode.CONSTANT || !value.equals(tree.getValue()
.toString()))) {
if ((value == null) ? tree.getKind() != kind :
(tree.getKind() != CAstNode.CONSTANT ||
(value instanceof Pattern
? !((Pattern)value).matcher(tree.getValue().toString()).matches()
: !value.equals(tree.getValue().toString()))))
{
if (DEBUG_MATCH) {
System.err.println("match failed (b)");
}
@ -364,6 +376,29 @@ public class CAstPattern {
}
}
public static Collection<Segments> findAll(final CAstPattern p, final CAstEntity e) {
final Collection<Segments> result = HashSetFactory.make();
CAstVisitor<Context> v = new CAstVisitor<Context>() {
@Override
public void leaveNode(CAstNode n, Context c, CAstVisitor visitor) {
Segments s = match(p, n);
if (s != null) {
result.add(s);
}
}
};
v.visit(e.getAST(), new Context() {
public CAstEntity top() {
return e;
}
}, v);
return result;
}
private static class Parser {
private final Map<String, CAstPattern> namedPatterns = HashMapFactory.make();
@ -414,6 +449,11 @@ public class CAstPattern {
end = strEnd + 1;
result = new CAstPattern(name, patternString.substring(start + 1, strEnd));
} else if (patternString.charAt(start) == '/') {
int strEnd = patternString.indexOf('/', start + 1);
end = strEnd + 1;
result = new CAstPattern(name, Pattern.compile(patternString.substring(start + 1, strEnd)));
} else if (patternString.startsWith("**", start)) {
end = start + 2;
result = new CAstPattern(name, CHILDREN_KIND, null);

View File

@ -203,6 +203,10 @@ public class PropagationSystem extends DefaultFixedPointSolver<PointsToSetVariab
return instanceKeys.getMappedObject(i);
}
public int getInstanceIndex(InstanceKey ik) {
return instanceKeys.getMappedIndex(ik);
}
/**
* TODO: optimize; this may be inefficient;
*
@ -340,7 +344,7 @@ public class PropagationSystem extends DefaultFixedPointSolver<PointsToSetVariab
return newStatement(L, op, R, true, true);
}
public void newConstraint(PointerKey lhs, AbstractOperator<PointsToSetVariable> op, PointerKey rhs) {
public boolean newConstraint(PointerKey lhs, AbstractOperator<PointsToSetVariable> op, PointerKey rhs) {
if (lhs == null) {
throw new IllegalArgumentException("lhs null");
}
@ -357,10 +361,10 @@ public class PropagationSystem extends DefaultFixedPointSolver<PointsToSetVariab
assert !pointsToMap.isUnified(rhs);
PointsToSetVariable L = findOrCreatePointsToSet(lhs);
PointsToSetVariable R = findOrCreatePointsToSet(rhs);
newStatement(L, op, new PointsToSetVariable[] { R }, true, true);
return newStatement(L, op, new PointsToSetVariable[] { R }, true, true);
}
public void newConstraint(PointerKey lhs, AbstractOperator<PointsToSetVariable> op, PointerKey rhs1, PointerKey rhs2) {
public boolean newConstraint(PointerKey lhs, AbstractOperator<PointsToSetVariable> op, PointerKey rhs1, PointerKey rhs2) {
if (lhs == null) {
throw new IllegalArgumentException("null lhs");
}
@ -382,7 +386,7 @@ public class PropagationSystem extends DefaultFixedPointSolver<PointsToSetVariab
PointsToSetVariable L = findOrCreatePointsToSet(lhs);
PointsToSetVariable R1 = findOrCreatePointsToSet(rhs1);
PointsToSetVariable R2 = findOrCreatePointsToSet(rhs2);
newStatement(L, op, R1, R2, true, true);
return newStatement(L, op, R1, R2, true, true);
}
/**

View File

@ -11,6 +11,7 @@
package com.ibm.wala.ipa.callgraph.propagation;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
@ -37,6 +38,7 @@ import com.ibm.wala.ipa.callgraph.ContextSelector;
import com.ibm.wala.ipa.callgraph.impl.AbstractRootMethod;
import com.ibm.wala.ipa.callgraph.impl.ExplicitCallGraph;
import com.ibm.wala.ipa.callgraph.impl.FakeRootMethod;
import com.ibm.wala.ipa.callgraph.propagation.SSAPropagationCallGraphBuilder.ConstraintVisitor.InvariantComputer;
import com.ibm.wala.ipa.cha.IClassHierarchy;
import com.ibm.wala.shrikeBT.ConditionalBranchInstruction;
import com.ibm.wala.shrikeBT.IInvokeInstruction;
@ -1020,10 +1022,10 @@ public abstract class SSAPropagationCallGraphBuilder extends PropagationCallGrap
*/
@Override
public void visitInvoke(SSAInvokeInstruction instruction) {
visitInvokeInternal(instruction);
visitInvokeInternal(instruction, new DefaultInvariantComputer());
}
protected void visitInvokeInternal(final SSAAbstractInvokeInstruction instruction) {
protected void visitInvokeInternal(final SSAAbstractInvokeInstruction instruction, InvariantComputer invs) {
if (DEBUG) {
System.err.println("visitInvoke: " + instruction);
}
@ -1033,9 +1035,10 @@ public abstract class SSAPropagationCallGraphBuilder extends PropagationCallGrap
uniqueCatch = getBuilder().getUniqueCatchKey(instruction, ir, node);
}
InstanceKey[][] invariantParameters = invs.computeInvariantParameters(instruction);
if (instruction.getCallSite().isStatic()) {
for (CGNode n : getBuilder().getTargetsForCall(node, instruction.getCallSite())) {
getBuilder().processResolvedCall(node, instruction, n, computeInvariantParameters(instruction), uniqueCatch);
for (CGNode n : getBuilder().getTargetsForCall(node, instruction.getCallSite(), invariantParameters)) {
getBuilder().processResolvedCall(node, instruction, n, invariantParameters, uniqueCatch);
if (DEBUG) {
System.err.println("visitInvoke class init " + n);
}
@ -1045,7 +1048,7 @@ public abstract class SSAPropagationCallGraphBuilder extends PropagationCallGrap
}
} else {
// Add a side effect that will fire when we determine a value
// for the receiver. This side effect will create a new node
// for a dispatch parameter. This side effect will create a new node
// and new constraints based on the new callee context.
IntSet params = getBuilder().getContextSelector().getRelevantParameters(node, instruction.getCallSite());
if (! params.contains(0)) {
@ -1061,8 +1064,8 @@ public abstract class SSAPropagationCallGraphBuilder extends PropagationCallGrap
});
if (contentsAreInvariant(symbolTable, du, vns)) {
for(CGNode n : getBuilder().getTargetsForCall(node, instruction.getCallSite())) {
getBuilder().processResolvedCall(node, instruction, n, computeInvariantParameters(instruction), uniqueCatch);
for(CGNode n : getBuilder().getTargetsForCall(node, instruction.getCallSite(), invariantParameters)) {
getBuilder().processResolvedCall(node, instruction, n, invariantParameters, uniqueCatch);
// side effect of invoke: may call class initializer
processClassInitializer(n.getMethod().getDeclaringClass());
}
@ -1074,14 +1077,14 @@ public abstract class SSAPropagationCallGraphBuilder extends PropagationCallGrap
final List<PointerKey> pks = new ArrayList<PointerKey>(params.size());
params.foreach(new IntSetAction() {
public void act(int x) {
if (! contentsAreInvariant(symbolTable, du, instruction.getUse(x))) {
if (!contentsAreInvariant(symbolTable, du, instruction.getUse(x))) {
pks.add(getBuilder().getPointerKeyForLocal(node, instruction.getUse(x)));
}
}
});
DispatchOperator dispatchOperator = getBuilder().new DispatchOperator(instruction, node,
computeInvariantParameters(instruction), uniqueCatch, params);
invariantParameters, uniqueCatch, params);
system.newSideEffect(dispatchOperator, pks.toArray(new PointerKey[pks.size()]));
}
}
@ -1326,13 +1329,20 @@ public abstract class SSAPropagationCallGraphBuilder extends PropagationCallGrap
basicBlock = block;
}
protected interface InvariantComputer {
InstanceKey[][] computeInvariantParameters(SSAAbstractInvokeInstruction call);
}
public class DefaultInvariantComputer implements InvariantComputer {
/**
* Side effect: records invariant parameters as implicit points-to-sets.
*
* @return if non-null, then result[i] holds the set of instance keys which may be passed as the ith parameter. (which must be
* invariant)
*/
protected InstanceKey[][] computeInvariantParameters(SSAAbstractInvokeInstruction call) {
public InstanceKey[][] computeInvariantParameters(SSAAbstractInvokeInstruction call) {
InstanceKey[][] constParams = null;
for (int i = 0; i < call.getNumberOfUses(); i++) {
// not sure how getUse(i) <= 0 .. dead code?
@ -1352,7 +1362,8 @@ public abstract class SSAPropagationCallGraphBuilder extends PropagationCallGrap
}
return constParams;
}
}
@Override
public void visitLoadMetadata(SSALoadMetadataInstruction instruction) {
PointerKey def = getPointerKeyForLocal(instruction.getDef());
@ -1592,10 +1603,17 @@ public abstract class SSAPropagationCallGraphBuilder extends PropagationCallGrap
public byte evaluate(PointsToSetVariable lhs, final PointsToSetVariable[] rhs) {
assert dispatchIndices.length >= rhs.length : "bad operator at " + call;
final MutableBoolean sideEffect = new MutableBoolean();
// NOTE: we allow the value of points-to set variables other than rhs[0]
// to be null. rhs[0] is the receiver, so to avoid an exception it must be
// non-empty, but empty points-to sets in other slots are allowed.
final MutableIntSet receiverVals = rhs[0].getValue();
final MutableIntSet receiverVals;
if (constParams != null && constParams[0] != null) {
receiverVals = IntSetUtil.make();
for(InstanceKey ik : constParams[0]) {
receiverVals.add(system.getInstanceIndex(ik));
}
} else {
receiverVals = rhs[0].getValue();
}
if (receiverVals == null) {
// this constraint was put on the work list, probably by
// initialization,
@ -1618,7 +1636,7 @@ public abstract class SSAPropagationCallGraphBuilder extends PropagationCallGrap
boolean newReceiver = !receiverVals.isSubset(previousPtrs[0]);
// keep separate rhsIndex, since it doesn't advance for constant
// parameters
int rhsIndex = 1;
int rhsIndex = (constParams != null && constParams[0] != null)? 0: 1;
// we start at index 1 since we need to handle the receiver specially; see
// below
for (int index = 1; index < dispatchIndices.length; index++) {
@ -1738,7 +1756,7 @@ public abstract class SSAPropagationCallGraphBuilder extends PropagationCallGrap
// instanceof is OK because this class is final
if (o instanceof DispatchOperator) {
DispatchOperator other = (DispatchOperator) o;
return node.equals(other.node) && call.equals(other.call);
return node.equals(other.node) && call.equals(other.call) && Arrays.deepEquals(constParams, other.constParams);
} else {
return false;
}
@ -1752,7 +1770,7 @@ public abstract class SSAPropagationCallGraphBuilder extends PropagationCallGrap
}
}
protected void iterateCrossProduct(final CGNode caller, final CallSiteReference site, IntSet parameters, final VoidFunction<InstanceKey[]> f) {
protected void iterateCrossProduct(final CGNode caller, final CallSiteReference site, IntSet parameters, final InstanceKey[][] invariants, final VoidFunction<InstanceKey[]> f) {
final IR ir = caller.getIR();
final int params[] = IntSetUtil.toArray(parameters);
for (final SSAAbstractInvokeInstruction call : ir.getCalls(site)) {
@ -1765,21 +1783,20 @@ public abstract class SSAPropagationCallGraphBuilder extends PropagationCallGrap
final int p = params[pi];
int vn = call.getUse(p);
PointerKey var = getPointerKeyForLocal(caller, vn);
if (contentsAreInvariant(ir.getSymbolTable(), caller.getDU(), vn)) {
system.recordImplicitPointsToSet(var);
InstanceKey[] ik = getInvariantContents(ir.getSymbolTable(), caller.getDU(), caller, vn, SSAPropagationCallGraphBuilder.this);
if (ik != null && ik.length > 0) {
for (int i = 0; i < ik.length; i++) {
system.findOrCreateIndexForInstanceKey(ik[i]);
keys[p] = ik[i];
rec(pi+1);
InstanceKey[] ik = invariants!=null? invariants[p]: null;
if (ik != null) {
if (ik.length > 0) {
for (int i = 0; i < ik.length; i++) {
system.findOrCreateIndexForInstanceKey(ik[i]);
keys[p] = ik[i];
rec(pi+1);
}
} else {
if (!site.isDispatch() || p != 0) {
keys[p] = null;
rec(pi+1);
}
}
} else {
if (!site.isDispatch() || p != 0) {
keys[p] = null;
rec(pi+1);
}
}
} else {
IntSet s = system.findOrCreatePointsToSet(var).getValue();
if (s != null && !s.isEmpty()) {
@ -1802,7 +1819,7 @@ public abstract class SSAPropagationCallGraphBuilder extends PropagationCallGrap
}
}
protected Set<CGNode> getTargetsForCall(final CGNode caller, final CallSiteReference site) {
protected Set<CGNode> getTargetsForCall(final CGNode caller, final CallSiteReference site, InstanceKey[][] invs) {
IntSet params = contextSelector.getRelevantParameters(caller, site);
if (!site.isStatic() && !params.contains(0)) {
params = IntSetUtil.makeMutableCopy(params);
@ -1821,7 +1838,7 @@ public abstract class SSAPropagationCallGraphBuilder extends PropagationCallGrap
}
}
};
iterateCrossProduct(caller, site, params, f);
iterateCrossProduct(caller, site, params, invs, f);
return targets;
}

View File

@ -265,7 +265,7 @@ public abstract class AbstractFixedPointSolver<T extends IVariable<?>> implement
* @param operator the step operator
* @throws IllegalArgumentException if lhs is null
*/
public void newStatement(final T lhs, final NullaryOperator<T> operator, final boolean toWorkList, final boolean eager) {
public boolean newStatement(final T lhs, final NullaryOperator<T> operator, final boolean toWorkList, final boolean eager) {
if (lhs == null) {
throw new IllegalArgumentException("lhs is null");
}
@ -273,12 +273,13 @@ public abstract class AbstractFixedPointSolver<T extends IVariable<?>> implement
lhs.setOrderNumber(nextOrderNumber++);
final NullaryStatement<T> s = new BasicNullaryStatement<T>(lhs, operator);
if (getFixedPointSystem().containsStatement(s)) {
return;
return false;
}
nCreated++;
getFixedPointSystem().addStatement(s);
incorporateNewStatement(toWorkList, eager, s);
topologicalCounter++;
return true;
}
@SuppressWarnings("unchecked")
@ -366,12 +367,12 @@ public abstract class AbstractFixedPointSolver<T extends IVariable<?>> implement
* @param op1 first operand on the rhs
* @param op2 second operand on the rhs
*/
public void newStatement(T lhs, AbstractOperator<T> operator, T op1, T op2, boolean toWorkList, boolean eager) {
public boolean newStatement(T lhs, AbstractOperator<T> operator, T op1, T op2, boolean toWorkList, boolean eager) {
// add to the list of graph
GeneralStatement<T> s = new Statement(lhs, operator, op1, op2);
if (getFixedPointSystem().containsStatement(s)) {
return;
return false;
}
if (lhs != null) {
lhs.setOrderNumber(nextOrderNumber++);
@ -380,6 +381,7 @@ public abstract class AbstractFixedPointSolver<T extends IVariable<?>> implement
getFixedPointSystem().addStatement(s);
incorporateNewStatement(toWorkList, eager, s);
topologicalCounter++;
return true;
}
/**
@ -392,7 +394,7 @@ public abstract class AbstractFixedPointSolver<T extends IVariable<?>> implement
* @param op3 third operand on the rhs
* @throws IllegalArgumentException if lhs is null
*/
public void newStatement(T lhs, AbstractOperator<T> operator, T op1, T op2, T op3, boolean toWorkList, boolean eager) {
public boolean newStatement(T lhs, AbstractOperator<T> operator, T op1, T op2, T op3, boolean toWorkList, boolean eager) {
if (lhs == null) {
throw new IllegalArgumentException("lhs is null");
}
@ -401,13 +403,14 @@ public abstract class AbstractFixedPointSolver<T extends IVariable<?>> implement
GeneralStatement<T> s = new Statement(lhs, operator, op1, op2, op3);
if (getFixedPointSystem().containsStatement(s)) {
nextOrderNumber--;
return;
return false;
}
nCreated++;
getFixedPointSystem().addStatement(s);
incorporateNewStatement(toWorkList, eager, s);
topologicalCounter++;
return true;
}
/**
@ -417,19 +420,20 @@ public abstract class AbstractFixedPointSolver<T extends IVariable<?>> implement
* @param operator the operator
* @param rhs the operands on the rhs
*/
public void newStatement(T lhs, AbstractOperator<T> operator, T[] rhs, boolean toWorkList, boolean eager) {
public boolean newStatement(T lhs, AbstractOperator<T> operator, T[] rhs, boolean toWorkList, boolean eager) {
// add to the list of graph
if (lhs != null)
lhs.setOrderNumber(nextOrderNumber++);
GeneralStatement<T> s = new Statement(lhs, operator, rhs);
if (getFixedPointSystem().containsStatement(s)) {
nextOrderNumber--;
return;
return false;
}
nCreated++;
getFixedPointSystem().addStatement(s);
incorporateNewStatement(toWorkList, eager, s);
topologicalCounter++;
return true;
}
/**