javadoc and formatting

git-svn-id: https://wala.svn.sourceforge.net/svnroot/wala/trunk@4149 f5eafffb-2e1d-0410-98e4-8ec43c5233c4
This commit is contained in:
msridhar1 2011-04-26 18:44:49 +00:00
parent f46aef537a
commit 3fffba2c87
5 changed files with 272 additions and 169 deletions

View File

@ -12,14 +12,37 @@ import com.ibm.wala.cast.tree.impl.CAstRewriter;
import com.ibm.wala.classLoader.CallSiteReference;
import com.ibm.wala.util.collections.Pair;
/**
* transforms each synchronized block to execute under a conditional test
* calling some method m(), where the block is duplicated in both the if and
* else branches. The transformation enables a static analysis to separately
* analyze the synchronized block for true and false return values from m().
*
* See "Finding Concurrency-Related Bugs using Random Isolation," Kidd et al.,
* VMCAI'09, Section 3
*/
public class SynchronizedBlockDuplicator extends
CAstRewriter<CAstRewriter.RewriteContext<SynchronizedBlockDuplicator.UnwindKey>, SynchronizedBlockDuplicator.UnwindKey> {
/**
* key type used for cloning the synchronized blocks and the true and false
* branches of the introduced conditional
*/
class UnwindKey implements CAstRewriter.CopyKey<UnwindKey> {
/**
* are we on the true or false branch?
*/
private boolean testDirection;
/**
* the AST node representing the synchronized block
*/
private CAstNode syncNode;
/**
* key associated with the {@link RewriteContext context} of the parent AST
* node of the synchronized block
*/
private UnwindKey rest;
private UnwindKey(boolean testDirection, CAstNode syncNode, UnwindKey rest) {
@ -48,6 +71,10 @@ public class SynchronizedBlockDuplicator extends
// private static final boolean DEBUG = false;
/**
* method to be invoked in the conditional test (program counter is ignored?
* --MS)
*/
private final CallSiteReference f;
public SynchronizedBlockDuplicator(CAst Ast, boolean recursive, CallSiteReference f) {
@ -59,6 +86,9 @@ public class SynchronizedBlockDuplicator extends
return rewrite(original);
}
/**
* context used for nodes not contained in a synchronized block
*/
private static class RootContext implements RewriteContext<UnwindKey> {
public UnwindKey key() {
@ -66,11 +96,23 @@ public class SynchronizedBlockDuplicator extends
}
}
/**
* context used within synchronized blocks
*/
class SyncContext implements RewriteContext<UnwindKey> {
/**
* context used for the parent AST node of the synchronized block
*/
private final CAstRewriter.RewriteContext<UnwindKey> parent;
/**
* are we on the true or false branch of the introduced conditional?
*/
private final boolean testDirection;
/**
* the AST node representing the synchronized block
*/
private final CAstNode syncNode;
private SyncContext(boolean testDirection, CAstNode syncNode, RewriteContext<UnwindKey> parent) {
@ -83,6 +125,10 @@ public class SynchronizedBlockDuplicator extends
return new UnwindKey(testDirection, syncNode, parent.key());
}
/**
* is n our synchronized block node or the synchronized block node of a
* parent?
*/
private boolean containsNode(CAstNode n) {
if (n == syncNode) {
return true;
@ -108,6 +154,10 @@ public class SynchronizedBlockDuplicator extends
}
}
/**
* does root represent a synchronized block? if so, return the variable whose
* lock is acquired. otherwise, return <code>null</code>
*/
private String isSynchronizedOnVar(CAstNode root) {
if (root.getKind() == CAstNode.UNWIND) {
CAstNode unwindBody = root.getChild(0);
@ -136,22 +186,30 @@ public class SynchronizedBlockDuplicator extends
return null;
}
protected CAstNode copyNodes(CAstNode n, RewriteContext<UnwindKey> c, Map<Pair<CAstNode,UnwindKey>, CAstNode> nodeMap) {
protected CAstNode copyNodes(CAstNode n, RewriteContext<UnwindKey> c, Map<Pair<CAstNode, UnwindKey>, CAstNode> nodeMap) {
String varName;
// don't copy operators or constants (presumably since they are immutable?)
if (n instanceof CAstOperator) {
return n;
} else if (n.getValue() != null) {
return Ast.makeConstant(n.getValue());
} else if (!contains(c, n) && (varName = isSynchronizedOnVar(n)) != null) {
CAstNode test = Ast.makeNode(CAstNode.CALL, Ast.makeNode(CAstNode.VOID), Ast.makeConstant(f), Ast.makeNode(CAstNode.VAR, Ast
.makeConstant(varName)));
// we call contains() above since we pass n to copyNodes() below for the
// true and false branches of the conditional, and in those recursive
// calls we want n to be copied normally
return Ast.makeNode(CAstNode.IF_STMT, test, copyNodes(n, new SyncContext(true, n, c), nodeMap), copyNodes(n, new SyncContext(
false, n, c), nodeMap));
// the conditional test
CAstNode test = Ast.makeNode(CAstNode.CALL, Ast.makeNode(CAstNode.VOID), Ast.makeConstant(f),
Ast.makeNode(CAstNode.VAR, Ast.makeConstant(varName)));
// the new if conditional
return Ast.makeNode(CAstNode.IF_STMT, test, copyNodes(n, new SyncContext(true, n, c), nodeMap),
copyNodes(n, new SyncContext(false, n, c), nodeMap));
} else {
// invoke copyNodes() on the children with context c, ensuring, e.g., that
// the body of a synchronized block gets cloned
CAstNode[] newChildren = new CAstNode[n.getChildCount()];
for (int i = 0; i < newChildren.length; i++)
newChildren[i] = copyNodes(n.getChild(i), c, nodeMap);

View File

@ -49,15 +49,15 @@ public class JSAstTranslator extends AstTranslator {
return false;
}
protected boolean hasImplicitGlobals() {
return true;
}
protected boolean treatGlobalsAsLexicallyScoped() {
protected boolean hasImplicitGlobals() {
return true;
}
protected boolean useLocalValuesForLexicalVars() {
protected boolean treatGlobalsAsLexicallyScoped() {
return true;
}
protected boolean useLocalValuesForLexicalVars() {
return true;
}
@ -72,185 +72,158 @@ public class JSAstTranslator extends AstTranslator {
private void addDefinedCheck(CAstNode n, WalkContext context, int readVn) {
context.cfg().addPreNode(n);
context.cfg().addInstruction(((JSInstructionFactory)insts).CheckReference(readVn));
context.cfg().addInstruction(((JSInstructionFactory) insts).CheckReference(readVn));
CAstNode target = context.getControlFlow().getTarget(n, JavaScriptTypes.ReferenceError);
if (target != null) {
context.cfg().addPreEdge(n, target, true);
} else {
context.cfg().addPreEdgeToExit(n, true);
}
context.cfg().newBlock(true);
context.cfg().newBlock(true);
}
protected int doLexicallyScopedRead(CAstNode n, WalkContext context, String name) {
int readVn = super.doLexicallyScopedRead(n, context, name);
addDefinedCheck(n, context, readVn);
return readVn;
}
protected int doGlobalRead(CAstNode n, WalkContext context, String name) {
int readVn = super.doGlobalRead(n, context, name);
if (! ("undefined".equals(name) || "$$undefined".equals(name))) {
if (!("undefined".equals(name) || "$$undefined".equals(name))) {
addDefinedCheck(n, context, readVn);
}
return readVn;
}
protected boolean defineType(CAstEntity type, WalkContext wc) {
Assertions.UNREACHABLE("JavaScript doesn't have types. I suggest you look elsewhere for your amusement.");
return false;
Assertions.UNREACHABLE("JavaScript doesn't have types. I suggest you look elsewhere for your amusement.");
return false;
}
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, numb-nuts!");
}
protected String composeEntityName(WalkContext parent, CAstEntity f) {
if (f.getKind() == CAstEntity.SCRIPT_ENTITY)
return f.getName();
else
return parent.getName() + "/" + f.getName();
}
if (f.getKind() == CAstEntity.SCRIPT_ENTITY)
return f.getName();
else
return parent.getName() + "/" + f.getName();
}
protected void declareFunction(CAstEntity N, WalkContext context) {
String fnName = composeEntityName(context, N);
if (N.getKind() == CAstEntity.SCRIPT_ENTITY) {
((JavaScriptLoader)loader).defineScriptType("L"+fnName, N.getPosition());
((JavaScriptLoader) loader).defineScriptType("L" + fnName, N.getPosition());
} else if (N.getKind() == CAstEntity.FUNCTION_ENTITY) {
((JavaScriptLoader)loader).defineFunctionType("L"+fnName, N.getPosition());
((JavaScriptLoader) loader).defineFunctionType("L" + fnName, N.getPosition());
} else {
Assertions.UNREACHABLE();
}
}
protected void defineFunction(CAstEntity N,
WalkContext definingContext,
AbstractCFG cfg,
SymbolTable symtab,
boolean hasCatchBlock,
TypeReference[][] caughtTypes,
boolean hasMonitorOp,
AstLexicalInformation LI,
DebuggingInformation debugInfo)
{
protected void defineFunction(CAstEntity N, WalkContext definingContext, AbstractCFG cfg, SymbolTable symtab,
boolean hasCatchBlock, TypeReference[][] caughtTypes, boolean hasMonitorOp, AstLexicalInformation LI,
DebuggingInformation debugInfo) {
if (DEBUG)
System.err.println(("\n\nAdding code for " + N));
String fnName = composeEntityName(definingContext, N);
if (DEBUG)
System.err.println(cfg);
symtab.getNullConstant();
symtab.getConstant("arguments");
symtab.getConstant("length");
for(int i = 0; i < 20; i++) {
for (int i = 0; i < 20; i++) {
symtab.getConstant(i);
}
((JavaScriptLoader)loader).defineCodeBodyCode("L"+fnName,
cfg,
symtab,
hasCatchBlock,
caughtTypes,
hasMonitorOp,
LI,
debugInfo);
((JavaScriptLoader) loader).defineCodeBodyCode("L" + fnName, cfg, symtab, hasCatchBlock, caughtTypes, hasMonitorOp, LI,
debugInfo);
}
protected void doThrow(WalkContext context, int exception) {
context.cfg().addInstruction(insts.ThrowInstruction(exception));
}
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 : 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());
context.cfg().newBlock( true );
context.cfg().newBlock(true);
if (context.getControlFlow().getTarget(call, null) != null)
context.cfg().addPreEdge(call, context.getControlFlow().getTarget(call, null), true);
else
context.cfg().addPreEdgeToExit( call, true );
context.cfg().addPreEdgeToExit(call, true);
}
protected void doNewObject(WalkContext context, CAstNode newNode, int result, Object type, int[] arguments) {
assert arguments == null;
TypeReference typeRef =
TypeReference.findOrCreate(
JavaScriptTypes.jsLoader,
TypeName.string2TypeName( "L" + type ));
TypeReference typeRef = TypeReference.findOrCreate(JavaScriptTypes.jsLoader, TypeName.string2TypeName("L" + type));
context.cfg().addInstruction(
insts.NewInstruction(
result,
NewSiteReference.make(
context.cfg().getCurrentInstruction(),
typeRef)));
insts.NewInstruction(result, NewSiteReference.make(context.cfg().getCurrentInstruction(), typeRef)));
}
protected void doMaterializeFunction(CAstNode n, WalkContext context, int result, int exception, CAstEntity fn) {
int nm = context.currentScope().getConstantValue("L"+composeEntityName(context, fn));
int nm = context.currentScope().getConstantValue("L" + composeEntityName(context, fn));
int tmp = super.doGlobalRead(n, context, "Function");
context.cfg().addInstruction(
((JSInstructionFactory)insts).Invoke(tmp, result, new int[]{ nm }, exception,
new JSCallSiteReference(
JavaScriptMethods.ctorReference,
context.cfg().getCurrentInstruction())));
((JSInstructionFactory) insts).Invoke(tmp, result, new int[] { nm }, exception, new JSCallSiteReference(
JavaScriptMethods.ctorReference, context.cfg().getCurrentInstruction())));
}
public void doArrayRead(WalkContext context, int result, int arrayValue, CAstNode arrayRef, int[] dimValues) {
Assertions.UNREACHABLE("JSAstTranslator.doArrayRead() called!");
Assertions.UNREACHABLE("JSAstTranslator.doArrayRead() called!");
}
public void doArrayWrite(WalkContext context, int arrayValue, CAstNode arrayRef, int[] dimValues, int rval) {
Assertions.UNREACHABLE("JSAstTranslator.doArrayWrite() called!");
Assertions.UNREACHABLE("JSAstTranslator.doArrayWrite() called!");
}
protected void doFieldRead(WalkContext context, int result, int receiver, CAstNode elt, CAstNode parent) {
this.visit(elt, context, this);
int x = context.currentScope().allocateTempValue();
context.cfg().addInstruction(((JSInstructionFactory)insts).AssignInstruction(x, receiver));
context.cfg().addInstruction(((JSInstructionFactory) insts).AssignInstruction(x, receiver));
if (elt.getKind()==CAstNode.CONSTANT && elt.getValue() instanceof String) {
String field = (String)elt.getValue();
if (elt.getKind() == CAstNode.CONSTANT && elt.getValue() instanceof String) {
String field = (String) elt.getValue();
// symtab needs to have this value
context.currentScope().getConstantValue(field);
context.cfg().addInstruction(
((JSInstructionFactory)insts).GetInstruction(result, x, field));
context.cfg().addInstruction(((JSInstructionFactory) insts).GetInstruction(result, x, field));
} else {
context.cfg().addInstruction(
((JSInstructionFactory)insts).PropertyRead(result, x, getValue(elt) ));
context.cfg().addInstruction(((JSInstructionFactory) insts).PropertyRead(result, x, getValue(elt)));
}
if (context.getControlFlow().getMappedNodes().contains(parent)) {
context.cfg().addPreNode(parent, context.getUnwindState());
context.cfg().newBlock( true );
context.cfg().newBlock(true);
if (context.getControlFlow().getTarget(parent, JavaScriptTypes.TypeError) != null)
context.cfg().addPreEdge(parent, context.getControlFlow().getTarget(parent, JavaScriptTypes.TypeError), true);
else
context.cfg().addPreEdgeToExit( parent, true );
context.cfg().addPreEdgeToExit(parent, true);
}
}
protected void doFieldWrite(WalkContext context, int receiver, CAstNode elt, CAstNode parent, int rval) {
this.visit(elt, context, this);
if (elt.getKind() == CAstNode.CONSTANT && elt.getValue() instanceof String)
{
String field = (String)elt.getValue();
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);
SSAPutInstruction put = ((JSInstructionFactory) insts).PutInstruction(receiver, rval, field);
try {
assert field.equals(put.getDeclaredField().getName().toUnicodeString());
} catch (UTFDataFormatException e) {
@ -258,131 +231,108 @@ public class JSAstTranslator extends AstTranslator {
}
context.cfg().addInstruction(put);
} else {
context.cfg().addInstruction(
((JSInstructionFactory)insts).PropertyWrite(receiver, getValue(elt), rval));
context.cfg().addInstruction(((JSInstructionFactory) insts).PropertyWrite(receiver, getValue(elt), rval));
}
}
private void
doPrimitiveNew(WalkContext context, int resultVal, String typeName)
{
private void doPrimitiveNew(WalkContext context, int resultVal, String typeName) {
doNewObject(context, null, resultVal, typeName + "Object", null);
int rval = context.currentScope().getConstantValue(typeName);
context.currentScope().getConstantValue("class");
context.cfg().addInstruction(
((JSInstructionFactory)insts).PutInstruction(resultVal, rval, "class"));
}
context.cfg().addInstruction(((JSInstructionFactory) insts).PutInstruction(resultVal, rval, "class"));
}
protected void doPrimitive(int resultVal, WalkContext context, CAstNode primitiveCall) {
try {
String name = (String)primitiveCall.getChild(0).getValue();
String name = (String) primitiveCall.getChild(0).getValue();
if (name.equals("GlobalNaN")) {
context.cfg().addInstruction(
((JSInstructionFactory)insts).AssignInstruction(
resultVal,
context.currentScope().getConstantValue(new Float(Float.NaN))));
context.cfg().addInstruction(
((JSInstructionFactory) insts).AssignInstruction(resultVal,
context.currentScope().getConstantValue(new Float(Float.NaN))));
} else if (name.equals("GlobalInfinity")) {
context.cfg().addInstruction(
((JSInstructionFactory)insts).AssignInstruction(
resultVal,
context.currentScope().getConstantValue(new Float(Float.POSITIVE_INFINITY))));
((JSInstructionFactory) insts).AssignInstruction(resultVal,
context.currentScope().getConstantValue(new Float(Float.POSITIVE_INFINITY))));
} else if (name.equals("MathE")) {
context.cfg().addInstruction(
((JSInstructionFactory)insts).AssignInstruction(
resultVal,
context.currentScope().getConstantValue(new Double(Math.E))));
((JSInstructionFactory) insts)
.AssignInstruction(resultVal, context.currentScope().getConstantValue(new Double(Math.E))));
} else if (name.equals("MathPI")) {
context.cfg().addInstruction(
((JSInstructionFactory)insts).AssignInstruction(
resultVal,
context.currentScope().getConstantValue(new Double(Math.PI))));
((JSInstructionFactory) insts).AssignInstruction(resultVal, context.currentScope()
.getConstantValue(new Double(Math.PI))));
} else if (name.equals("MathSQRT1_2")) {
context.cfg().addInstruction(
((JSInstructionFactory)insts).AssignInstruction(
resultVal,
context.currentScope().getConstantValue(new Double(Math.sqrt(.5)))));
((JSInstructionFactory) insts).AssignInstruction(resultVal,
context.currentScope().getConstantValue(new Double(Math.sqrt(.5)))));
} else if (name.equals("MathSQRT2")) {
context.cfg().addInstruction(
((JSInstructionFactory)insts).AssignInstruction(
resultVal,
context.currentScope().getConstantValue(new Double(Math.sqrt(2)))));
((JSInstructionFactory) insts).AssignInstruction(resultVal,
context.currentScope().getConstantValue(new Double(Math.sqrt(2)))));
} else if (name.equals("MathLN2")) {
context.cfg().addInstruction(
((JSInstructionFactory)insts).AssignInstruction(
resultVal,
context.currentScope().getConstantValue(new Double(Math.log(2)))));
((JSInstructionFactory) insts).AssignInstruction(resultVal,
context.currentScope().getConstantValue(new Double(Math.log(2)))));
} else if (name.equals("MathLN10")) {
context.cfg().addInstruction(
((JSInstructionFactory)insts).AssignInstruction(
resultVal,
context.currentScope().getConstantValue(new Double(Math.log(10)))));
((JSInstructionFactory) insts).AssignInstruction(resultVal,
context.currentScope().getConstantValue(new Double(Math.log(10)))));
} else if (name.equals("NewObject")) {
doNewObject(context, null, resultVal, "Object", null);
doNewObject(context, null, resultVal, "Object", null);
} else if (name.equals("NewArray")) {
doNewObject(context, null, resultVal, "Array", null);
doNewObject(context, null, resultVal, "Array", null);
} else if (name.equals("NewString")) {
doPrimitiveNew(context, resultVal, "String");
doPrimitiveNew(context, resultVal, "String");
} else if (name.equals("NewNumber")) {
doPrimitiveNew(context, resultVal, "Number");
doPrimitiveNew(context, resultVal, "Number");
} else if (name.equals("NewRegExp")) {
doPrimitiveNew(context, resultVal, "RegExp");
doPrimitiveNew(context, resultVal, "RegExp");
} else if (name.equals("NewFunction")) {
doNewObject(context, null, resultVal, "Function", null);
doNewObject(context, null, resultVal, "Function", null);
} else if (name.equals("NewUndefined")) {
doNewObject(context, null, resultVal, "Undefined", null);
doNewObject(context, null, resultVal, "Undefined", null);
} else {
context.cfg().addInstruction(
((JSInstructionFactory)insts).AssignInstruction(
resultVal,
context.currentScope().getConstantValue( null )));
context.cfg().addInstruction(
((JSInstructionFactory) insts).AssignInstruction(resultVal, context.currentScope().getConstantValue(null)));
}
} catch (ClassCastException e) {
throw new RuntimeException("Cannot translate primitive " + primitiveCall.getChild(0).getValue());
}
}
protected void doIsFieldDefined(WalkContext context,
int result,
int ref,
CAstNode f)
{
protected void doIsFieldDefined(WalkContext context, int result, int ref, CAstNode f) {
if (f.getKind() == CAstNode.CONSTANT && f.getValue() instanceof String) {
String field = (String) f.getValue();
FieldReference fieldRef =
FieldReference.findOrCreate(
JavaScriptTypes.Root,
Atom.findOrCreateUnicodeAtom((String)field),
JavaScriptTypes.Root);
context.cfg()
.addInstruction(((JSInstructionFactory)insts).IsDefinedInstruction(result, ref, fieldRef));
FieldReference fieldRef = FieldReference.findOrCreate(JavaScriptTypes.Root, Atom.findOrCreateUnicodeAtom((String) field),
JavaScriptTypes.Root);
context.cfg().addInstruction(((JSInstructionFactory) insts).IsDefinedInstruction(result, ref, fieldRef));
} else {
context.cfg()
.addInstruction(((JSInstructionFactory)insts).IsDefinedInstruction(result, ref, getValue(f)));
context.cfg().addInstruction(((JSInstructionFactory) insts).IsDefinedInstruction(result, ref, getValue(f)));
}
}
protected boolean visitInstanceOf(CAstNode n, Context c, CAstVisitor visitor) {
WalkContext context = (WalkContext)c;
WalkContext context = (WalkContext) c;
int result = context.currentScope().allocateTempValue();
setValue(n, result);
return false;
}
protected void leaveInstanceOf(CAstNode n, Context c, CAstVisitor visitor) {
WalkContext context = (WalkContext)c;
WalkContext context = (WalkContext) c;
int result = getValue(n);
visit(n.getChild(0), context, visitor);
int value = getValue(n.getChild(0));
@ -390,7 +340,7 @@ public class JSAstTranslator extends AstTranslator {
int type = getValue(n.getChild(1));
context.cfg().addInstruction(new JavaScriptInstanceOf(result, value, type));
}
}
protected void doPrologue(WalkContext context) {
super.doPrologue(context);
@ -398,12 +348,11 @@ public class JSAstTranslator extends AstTranslator {
doNewObject(context, null, tempVal, "Array", null);
CAstSymbol args = new CAstSymbolImpl("arguments");
context.currentScope().declare(args, tempVal);
context.cfg().addInstruction(
((JSInstructionFactory)insts).PutInstruction(1, tempVal, "arguments"));
context.cfg().addInstruction(((JSInstructionFactory) insts).PutInstruction(1, tempVal, "arguments"));
}
protected boolean doVisit(CAstNode n, Context cntxt, CAstVisitor visitor) {
WalkContext context = (WalkContext)cntxt;
WalkContext context = (WalkContext) cntxt;
switch (n.getKind()) {
case CAstNode.TYPE_OF: {
int result = context.currentScope().allocateTempValue();
@ -411,20 +360,19 @@ public class JSAstTranslator extends AstTranslator {
this.visit(n.getChild(0), context, this);
int ref = getValue(n.getChild(0));
context.cfg().addInstruction(
((JSInstructionFactory)insts).TypeOfInstruction(result, ref));
context.cfg().addInstruction(((JSInstructionFactory) insts).TypeOfInstruction(result, ref));
setValue(n, result);
return true;
}
case JavaScriptCAstNode.ENTER_WITH:
case JavaScriptCAstNode.EXIT_WITH: {
this.visit(n.getChild(0), context, this);
int ref = getValue(n.getChild(0));
context.cfg().addInstruction(((JSInstructionFactory)insts).WithRegion(ref, n.getKind()==JavaScriptCAstNode.ENTER_WITH));
context.cfg().addInstruction(((JSInstructionFactory) insts).WithRegion(ref, n.getKind() == JavaScriptCAstNode.ENTER_WITH));
return true;
}

View File

@ -132,23 +132,50 @@ public abstract class AstTranslator extends CAstVisitor implements ArrayOpHandle
*/
protected abstract String composeEntityName(WalkContext parent, CAstEntity f);
/**
* generate IR for a CAst throw expression, updating context.cfg()
*/
protected abstract void doThrow(WalkContext context, int exception);
/**
* generate IR for a CAst array read, updating context.cfg()
*/
public abstract void doArrayRead(WalkContext context, int result, int arrayValue, CAstNode arrayRef, int[] dimValues);
/**
* generate IR for a CAst array write, updating context.cfg()
*/
public abstract void doArrayWrite(WalkContext context, int arrayValue, CAstNode arrayRef, int[] dimValues, int rval);
/**
* generate IR for a CAst field read, updating context.cfg()
*/
protected abstract void doFieldRead(WalkContext context, int result, int receiver, CAstNode elt, CAstNode parent);
/**
* generate IR for a CAst field write, updating context.cfg()
*/
protected abstract void doFieldWrite(WalkContext context, int receiver, CAstNode elt, CAstNode parent, int rval);
/**
* generate IR for a CAst function expression, updating context.cfg()
*/
protected abstract void doMaterializeFunction(CAstNode node, WalkContext context, int result, int exception, CAstEntity fn);
/**
* generate IR for a CAst new expression, updating context.cfg()
*/
protected abstract void doNewObject(WalkContext context, CAstNode newNode, int result, Object type, int[] arguments);
/**
* generate IR for a CAst method call expression, updating context.cfg()
*/
protected abstract void doCall(WalkContext context, CAstNode call, int result, int exception, CAstNode name, int receiver,
int[] arguments);
/**
* used to generate instructions for array operations; defaults to this
*/
private ArrayOpHandler arrayOpHandler;
protected boolean isExceptionLabel(Object label) {
@ -186,12 +213,18 @@ public abstract class AstTranslator extends CAstVisitor implements ArrayOpHandle
Assertions.UNREACHABLE();
}
/**
* generate prologue code for each function body
*/
protected void doPrologue(WalkContext context) {
if (useLocalValuesForLexicalVars()) {
context.cfg().addInstruction(new AstLexicalRead(new Access[0]));
}
}
/**
* generate IR for call modeling creation of primitive value, updating context.cfg()
*/
protected abstract void doPrimitive(int resultVal, WalkContext context, CAstNode primitiveCall);
protected int doLocalRead(WalkContext context, String name) {
@ -1085,6 +1118,9 @@ public abstract class AstTranslator extends CAstVisitor implements ArrayOpHandle
Object defaultInitValue();
}
/**
* a scope in the symbol table build during AST traversal
*/
public interface Scope {
int type();

View File

@ -8,11 +8,18 @@ import com.ibm.wala.cast.tree.CAstNode;
import com.ibm.wala.util.collections.Pair;
import com.ibm.wala.util.debug.Assertions;
/**
* abstract base class for {@link CAstRewriter}s that do no cloning of nodes
*
*/
public abstract class CAstBasicRewriter
extends CAstRewriter<CAstBasicRewriter.NonCopyingContext,
CAstBasicRewriter.NoKey>
{
/**
* context indicating that no cloning is being performed
*/
public static class NonCopyingContext implements CAstRewriter.RewriteContext<NoKey> {
private final Map nodeMap = new HashMap();
@ -26,6 +33,9 @@ public abstract class CAstBasicRewriter
}
/**
* key indicating that no duplication is being performed
*/
public static class NoKey implements CAstRewriter.CopyKey<NoKey> {
private NoKey() {
Assertions.UNREACHABLE();

View File

@ -40,30 +40,47 @@ import com.ibm.wala.util.collections.Pair;
* type of the RewriteContext used when traversing the original CAst
* during the rewrite operation
* @param <K>
* a key used to ease cloning of partial ASTs. When rewriting an AST,
* sub-classes maintain a mapping from (original node, key) pairs
* (where key is of type K) to new nodes; see
* {@link #copyNodes(CAstNode, RewriteContext, Map)}
*/
public abstract class CAstRewriter<C extends CAstRewriter.RewriteContext<K>, K extends CAstRewriter.CopyKey<K>> {
protected static final boolean DEBUG = false;
/**
* interface to be implemented by keys used for cloning sub-trees during the
* rewrite
*/
public interface CopyKey<Self extends CopyKey> {
int hashCode();
boolean equals(Object o);
/**
* keys have parent pointers, useful for when nesting cloning must occur
* (e.g., unrolling of nested loops)
*/
Self parent();
};
/**
* interface to be implemented by contexts used while traversing the AST
*/
public interface RewriteContext<K extends CopyKey> {
/**
* get the cloning key for this context
*/
K key();
};
/**
* represents a rewritten CAst
*
*/
public interface Rewrite {
@ -96,22 +113,43 @@ public abstract class CAstRewriter<C extends CAstRewriter.RewriteContext<K>, K e
}
/**
* rewrite the CAst rooted at root, updating nodeMap with a mapping from
* (original node,key) pairs to new nodes. Return the node to which root was
* mapped by the call.
* rewrite the CAst rooted at root under some context, returning the node at
* the root of the rewritten tree. mutate nodeMap in the process, indicating
* how (original node, copy key) pairs are mapped to nodes in the rewritten
* tree.
*/
protected abstract CAstNode copyNodes(CAstNode root, C context, Map<Pair<CAstNode, K>, CAstNode> nodeMap);
/**
* in {@link #copyFlow(Map, CAstControlFlowMap, CAstSourcePositionMap)}, if
* the source of some original CFG edge is replicated, but we find no replica
* for the target, what node should be the target of the CFG edge in the
* rewritten AST? By default, just uses the original target.
*
*/
protected CAstNode flowOutTo(Map<Pair<CAstNode, K>, CAstNode> nodeMap, CAstNode oldSource, Object label, CAstNode oldTarget,
CAstControlFlowMap orig, CAstSourcePositionMap src) {
return oldTarget;
}
/**
* create a control-flow map for the rewritten tree, given the mapping from
* (original node, copy key) pairs ot new nodes and the original control-flow
* map.
*/
private CAstControlFlowMap copyFlow(Map<Pair<CAstNode, K>, CAstNode> nodeMap, CAstControlFlowMap orig,
CAstSourcePositionMap newSrc) {
Set<CAstNode> mappedOutsideNodes = HashSetFactory.make(1);
// the new control-flow map
final CAstControlFlowRecorder newMap = new CAstControlFlowRecorder(newSrc);
// tracks which CAstNodes not present in nodeMap's key set (under any copy
// key) are added as targets of CFG edges
// via a call to flowOutTo() (see below); used to ensure these nodes are
// only mapped to themselves once in newMap
final Set<CAstNode> mappedOutsideNodes = HashSetFactory.make(1);
// all edge targets in new control-flow map; must all be mapped to
// themselves
Set<CAstNode> allNewTargetNodes = HashSetFactory.make(1);
CAstControlFlowRecorder newMap = new CAstControlFlowRecorder(newSrc);
Collection<CAstNode> oldSources = orig.getMappedNodes();
for (Iterator<Entry<Pair<CAstNode, K>, CAstNode>> NS = nodeMap.entrySet().iterator(); NS.hasNext();) {
@ -144,6 +182,8 @@ public abstract class CAstRewriter<C extends CAstRewriter.RewriteContext<K>, K e
System.err.println(("old: " + origLabel + " --> " + CAstPrinter.print(oldTarget)));
}
// try to find a k in key's parent chain such that (oldTarget, k) is
// in nodeMap's key set
Pair targetKey;
CopyKey k = key;
do {
@ -172,6 +212,10 @@ public abstract class CAstRewriter<C extends CAstRewriter.RewriteContext<K>, K e
allNewTargetNodes.add(newTarget);
} else {
// could not discover target of CFG edge in nodeMap under any key related to the current source key.
// the edge might have been deleted, or it may end at a node above the root where we were
// rewriting
// ask flowOutTo() to just choose a target
newTarget = flowOutTo(nodeMap, oldSource, origLabel, oldTarget, orig, newSrc);
allNewTargetNodes.add(newTarget);
newMap.add(newSource, newTarget, newLabel);
@ -260,7 +304,7 @@ public abstract class CAstRewriter<C extends CAstRewriter.RewriteContext<K>, K e
for (Iterator<Map.Entry<CAstNode, Collection<CAstEntity>>> keys = children.entrySet().iterator(); keys.hasNext();) {
Map.Entry<CAstNode, Collection<CAstEntity>> entry = keys.next();
CAstNode key = entry.getKey();
if (!(key instanceof CAstNode)) {
if (key == null) {
Set<CAstEntity> newEntities = new LinkedHashSet<CAstEntity>();
newChildren.put(key, newEntities);
for (Iterator oldEntities = ((Collection) entry.getValue()).iterator(); oldEntities.hasNext();) {
@ -272,6 +316,9 @@ public abstract class CAstRewriter<C extends CAstRewriter.RewriteContext<K>, K e
return newChildren;
}
/**
* rewrite the CAst sub-tree rooted at root
*/
public Rewrite rewrite(CAstNode root, final CAstControlFlowMap cfg, final CAstSourcePositionMap pos, final CAstNodeTypeMap types,
final Map<CAstNode, Collection<CAstEntity>> children) {
final Map<Pair<CAstNode, K>, CAstNode> nodes = HashMapFactory.make();
@ -315,6 +362,10 @@ public abstract class CAstRewriter<C extends CAstRewriter.RewriteContext<K>, K e
};
}
/**
* perform the rewrite on a {@link CAstEntity}, returning the new
* {@link CAstEntity} as the result
*/
public CAstEntity rewrite(final CAstEntity root) {
if (root.getAST() != null) {