Fix prototype contamination bug (described in added test).
When someone writes: MyFunction.prototype.myMethod = ... we want "myMethod" to be accessible only for objects of type "MyFunction". - Change how generated ctors look like - now they have a private prototype object into which methods can be added - Change the PropertyReadExpander to have a different handling when reading the "prototype" field, avoid performing a loop on the prototype chain, so that the points to set of fetched field will be accurate (and allow us to set only the fields of it) git-svn-id: https://wala.svn.sourceforge.net/svnroot/wala/trunk@3959 f5eafffb-2e1d-0410-98e4-8ec43c5233c4
This commit is contained in:
parent
a0fb3b089f
commit
38f8a44044
|
@ -0,0 +1,28 @@
|
|||
function A(){
|
||||
|
||||
}
|
||||
A.prototype.foo = function foo_of_A(){
|
||||
console.log("foo_of_A");
|
||||
}
|
||||
|
||||
function B(){
|
||||
|
||||
}
|
||||
B.prototype.foo = function foo_of_B(){
|
||||
console.log("foo_of_B");
|
||||
}
|
||||
|
||||
function test1(){
|
||||
var a = new A
|
||||
console.log("calling foo_of_A");
|
||||
a.foo()
|
||||
}
|
||||
|
||||
function test2(){
|
||||
var b = new B
|
||||
console.log("calling foo_of_B");
|
||||
b.foo()
|
||||
}
|
||||
|
||||
test1()
|
||||
test2()
|
|
@ -243,4 +243,21 @@ public abstract class TestSimpleCallGraphShape extends TestJSCallGraphShape {
|
|||
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"} }
|
||||
};
|
||||
|
||||
@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");
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
|
|
@ -349,11 +349,11 @@ public class JavaScriptConstructTargetSelector implements MethodTargetSelector {
|
|||
|
||||
S.addStatement(insts.NewInstruction(5, NewSiteReference.make(S.getNextProgramCounter(), cls.getReference())));
|
||||
|
||||
S.addStatement(insts.PutInstruction(5, 4, "prototype"));
|
||||
S.getNextProgramCounter();
|
||||
|
||||
S.addStatement(insts.NewInstruction(7, NewSiteReference.make(S.getNextProgramCounter(), JavaScriptTypes.Object)));
|
||||
|
||||
S.addStatement(insts.PutInstruction(7, 4, "prototype"));
|
||||
S.getNextProgramCounter();
|
||||
|
||||
S.addStatement(insts.PutInstruction(5, 7, "prototype"));
|
||||
S.getNextProgramCounter();
|
||||
|
||||
|
|
|
@ -81,17 +81,36 @@ public class PropertyReadExpander extends CAstRewriter<PropertyReadExpander.Rewr
|
|||
private CAstNode makeConstRead(CAstNode receiver, CAstNode element, RewriteContext context) {
|
||||
String receiverTemp = TEMP_NAME + (readTempCounter++);
|
||||
String elt = (String) element.getValue();
|
||||
|
||||
if (context.inAssignment()) {
|
||||
context.setAssign(Ast.makeNode(CAstNode.VAR, Ast.makeConstant(receiverTemp)), Ast.makeConstant(elt));
|
||||
if (elt.equals("prototype")){
|
||||
return Ast.makeNode(CAstNode.BLOCK_EXPR,
|
||||
Ast.makeNode(CAstNode.OBJECT_REF,
|
||||
receiver,
|
||||
Ast.makeConstant("prototype")));
|
||||
} else {
|
||||
|
||||
if (context.inAssignment()) {
|
||||
context.setAssign(Ast.makeNode(CAstNode.VAR, Ast.makeConstant(receiverTemp)), Ast.makeConstant(elt));
|
||||
}
|
||||
|
||||
return Ast.makeNode(CAstNode.BLOCK_EXPR,
|
||||
Ast.makeNode(CAstNode.DECL_STMT,
|
||||
Ast.makeConstant(new InternalCAstSymbol(receiverTemp,false, false)),
|
||||
receiver),
|
||||
Ast.makeNode(CAstNode.LOOP,
|
||||
Ast.makeNode(CAstNode.UNARY_EXPR,
|
||||
CAstOperator.OP_NOT,
|
||||
Ast.makeNode(CAstNode.IS_DEFINED_EXPR,
|
||||
Ast.makeNode(CAstNode.VAR, Ast.makeConstant(receiverTemp)),
|
||||
Ast.makeConstant(elt))),
|
||||
Ast.makeNode(CAstNode.ASSIGN,
|
||||
Ast.makeNode(CAstNode.VAR, Ast.makeConstant(receiverTemp)),
|
||||
Ast.makeNode(CAstNode.OBJECT_REF,
|
||||
Ast.makeNode(CAstNode.VAR, Ast.makeConstant(receiverTemp)),
|
||||
Ast.makeConstant("prototype")))),
|
||||
Ast.makeNode(CAstNode.OBJECT_REF,
|
||||
Ast.makeNode(CAstNode.VAR, Ast.makeConstant(receiverTemp)),
|
||||
Ast.makeConstant(elt)));
|
||||
}
|
||||
|
||||
return Ast.makeNode(CAstNode.BLOCK_EXPR, Ast.makeNode(CAstNode.DECL_STMT, Ast.makeConstant(new InternalCAstSymbol(receiverTemp,
|
||||
false, false)), receiver), Ast.makeNode(CAstNode.LOOP, Ast.makeNode(CAstNode.UNARY_EXPR, CAstOperator.OP_NOT, Ast.makeNode(
|
||||
CAstNode.IS_DEFINED_EXPR, Ast.makeNode(CAstNode.VAR, Ast.makeConstant(receiverTemp)), Ast.makeConstant(elt))), Ast
|
||||
.makeNode(CAstNode.ASSIGN, Ast.makeNode(CAstNode.VAR, Ast.makeConstant(receiverTemp)), Ast.makeNode(CAstNode.OBJECT_REF,
|
||||
Ast.makeNode(CAstNode.VAR, Ast.makeConstant(receiverTemp)), Ast.makeConstant("prototype")))), Ast.makeNode(
|
||||
CAstNode.OBJECT_REF, Ast.makeNode(CAstNode.VAR, Ast.makeConstant(receiverTemp)), Ast.makeConstant(elt)));
|
||||
}
|
||||
|
||||
private CAstNode makeVarRead(CAstNode receiver, CAstNode element, RewriteContext context) {
|
||||
|
|
|
@ -104,6 +104,29 @@ public abstract class TestCallGraphShape extends WalaTestCase {
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Verifies that non of the nodes that match the source description has an edge to any of the nodes that match the destination
|
||||
* description. (Used for checking for false connections in the callgraph)
|
||||
*
|
||||
* @param CG
|
||||
* @param sourceDescription
|
||||
* @param destDescription
|
||||
*/
|
||||
protected void verifyNoEdges(CallGraph CG, String sourceDescription, String destDescription) {
|
||||
Collection sources = getNodes(CG, sourceDescription);
|
||||
Collection dests = getNodes(CG, destDescription);
|
||||
for (Object source : sources) {
|
||||
for (Object dest : dests) {
|
||||
for (Iterator<CGNode> i = CG.getSuccNodes((CGNode) source); i.hasNext();) {
|
||||
if (i.next().equals(dest)) {
|
||||
Assert.fail("Found a link from " + source + " to " + dest);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected static final Object ROOT = new Object();
|
||||
|
||||
protected abstract Collection getNodes(CallGraph CG, String functionIdentifier);
|
||||
|
|
Loading…
Reference in New Issue