package dk.brics.jspointers.test.instrument;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import dk.brics.jscontrolflow.Function;
import dk.brics.jscontrolflow.Statement;
import dk.brics.jscontrolflow.analysis.reachdef.ReachingDefinitions;
import dk.brics.jscontrolflow.analysis.reachdef.VariableDefinition;
import dk.brics.jscontrolflow.scope.Scope;
import dk.brics.jscontrolflow.statements.Assignment;
import dk.brics.jscontrolflow.statements.CreateFunction;
import dk.brics.jscontrolflow.statements.InvokeStatement;
import dk.brics.jsparser.analysis.Analysis;
import dk.brics.jsparser.analysis.DepthFirstAdapter;
import dk.brics.jsparser.node.AArrayLiteralExp;
import dk.brics.jsparser.node.AFunctionDeclStmt;
import dk.brics.jsparser.node.AFunctionExp;
import dk.brics.jsparser.node.AInvokeExp;
import dk.brics.jsparser.node.ANewExp;
import dk.brics.jsparser.node.AObjectLiteralExp;
import dk.brics.jsparser.node.ARegexpExp;
import dk.brics.jsparser.node.IExpOrStmt;
import dk.brics.jsparser.node.IFunction;
import dk.brics.jsparser.node.IScopeBlockNode;
import dk.brics.jsparser.node.NodeInterface;
import dk.brics.jsparser.node.PExp;
import dk.brics.jsparser.node.Start;
import dk.brics.jspointers.Main;
import dk.brics.jspointers.Main.InputFile;
import dk.brics.jspointers.dataflow.AbstractFlowNodeVisitor;
import dk.brics.jspointers.dataflow.IInvocationFlowNode;
import dk.brics.jspointers.dataflow.InitializeFunctionNode;
import dk.brics.jspointers.dataflow.InvokeNode;
import dk.brics.jspointers.dataflow.LoadAndInvokeNode;
import dk.brics.jspointers.dataflow.OutputPoint;
import dk.brics.jspointers.lattice.contexts.Context;
import dk.brics.jspointers.lattice.contexts.NullContext;
import dk.brics.jspointers.lattice.keys.NamedPropertyKey;
import dk.brics.jspointers.lattice.keys.VariableKey;
import dk.brics.jspointers.lattice.values.AllocObjectValue;
import dk.brics.jspointers.lattice.values.ArgumentsArrayValue;
import dk.brics.jspointers.lattice.values.FunctionPrototypeValue;
import dk.brics.jspointers.lattice.values.GlobalObjectValue;
import dk.brics.jspointers.lattice.values.ObjectValue;
import dk.brics.jspointers.lattice.values.UserFunctionValue;
import dk.brics.jspointers.parametric.StatementAllocSite;
import dk.brics.jsutil.MultiMap;
public class JsPointersInstrumentData implements InstrumentData {
private Main main;
private Map<Statement,AllocSite> stmt2alloc = new HashMap<Statement, AllocSite>();
public JsPointersInstrumentData(final Main main) {
this.main = main;
visitUserCode(new DepthFirstAdapter() {
@Override
public void inAObjectLiteralExp(AObjectLiteralExp node) {
doAlloc(node, new ObjLiteralAllocSite(node));
}
@Override
public void inANewExp(ANewExp node) {
doAlloc(node, new NewExpAllocSite(node));
}
@Override
public void inAArrayLiteralExp(AArrayLiteralExp node) {
doAlloc(node, new ArrayLiteralAllocSite(node));
}
@Override
public void inARegexpExp(ARegexpExp node) {
doAlloc(node, new RegExpAllocSite(node));
}
@Override
public void inAFunctionExp(AFunctionExp node) {
handleFunction(node);
}
@Override
public void inAFunctionDeclStmt(AFunctionDeclStmt node) {
handleFunction(node);
}
void handleFunction(IFunction func) {
for (CreateFunction stmt : main.getAstBinding().getFunctions(func)) {
stmt2alloc.put(stmt, new FunctionAllocSite(func));
}
}
void doAlloc(PExp exp, AllocSite site) {
for (Assignment stmt : main.getAstBinding().getAllocation(exp)) {
stmt2alloc.put(stmt, site);
}
}
});
}
void visitUserCode(Analysis v) {
for (InputFile file : main.getUserFiles()) {
file.getAst().apply(v);
}
}
@Override
public boolean isNative(NodeInterface node) {
Start start = node.getAncestor(Start.class);
for (InputFile in : main.getHarness()) {
if (in.getAst() == start)
return true;
}
return false;
}
@Override
public Set<IFunction> getTargets(AInvokeExp invoke) {
final Set<IFunction> result = new HashSet<IFunction>();
for (InvokeStatement stmt : main.getAstBinding().getInvokeStatements(invoke)) {
for (IInvocationFlowNode flow : main.getDataflowBinding().getInvoke(stmt)) {
flow.apply(new AbstractFlowNodeVisitor() {
@Override
public void caseInvoke(InvokeNode node) {
visitValues(main.lookupIns(node.getFunc(), UserFunctionValue.class));
}
@Override
public void caseLoadAndInvoke(LoadAndInvokeNode node) {
Set<ObjectValue> directRcv = main.lookupIns(node.getBase(), ObjectValue.class);
Set<ObjectValue> base = main.getAllPrototypes(directRcv, true);
for (ObjectValue obj : base) {
visitValues(main.lookupSens(new NamedPropertyKey(obj, node.getProperty()), UserFunctionValue.class));
visitValues(main.lookupSens(obj.getDynamicStoreProperty(), UserFunctionValue.class));
}
}
private void visitValues(Set<UserFunctionValue> funcs) {
for (UserFunctionValue uf : funcs) {
result.add(main.getAstBinding().getFunctionNode(uf.getFunction()));
}
}
});
}
}
return result;
}
@Override
public Set<AllocSite> getResultAllocationSites(PExp exp) {
return getAllocSites(main.objects(exp));
}
@Override
public Set<AllocSite> getVariableAllocationSites(IScopeBlockNode scope, String var, IExpOrStmt afterThisGuy) {
IFunction func = scope.getAncestor(IFunction.class);
if (func == null) {
return getAllocSites(main.lookupSens(new NamedPropertyKey(GlobalObjectValue.Instance, var), ObjectValue.class));
} else {
Set<AllocSite> result = new HashSet<AllocSite>();
Scope scop = main.getAstBinding().getScope(scope);
Function function = main.getAstBinding().getFunction(func);
ReachingDefinitions reachingDefinitions = main.getDataflowBinding().getReachingDefinitions(function);
for (Statement stmt : main.getAstBinding().getCompletionPoint(afterThisGuy)) {
for (VariableDefinition def : reachingDefinitions.getReachingDefinitions(stmt, var, scop)) {
for (OutputPoint op : main.getDataflowBinding().getDefinitions().getView(def)) {
result.addAll(getAllocSites(main.lookupIns(op, ObjectValue.class)));
}
}
}
result.addAll(getAllocSites(main.lookupIns(new VariableKey(var, main.getAstBinding().getScope(scope), NullContext.Instance), ObjectValue.class)));
return result;
}
}
@Override
public Set<Start> getAst() {
Set<Start> asts = new HashSet<Start>();
for (InputFile file : main.getAllInputs()) {
asts.add(file.getAst());
}
return asts;
}
public Set<AllocSite> getAllocSites(Set<? extends ObjectValue> objects) {
Set<AllocSite> result = new HashSet<AllocSite>();
for (ObjectValue obj : objects) {
if (obj instanceof AllocObjectValue) {
AllocObjectValue al = (AllocObjectValue) obj;
StatementAllocSite site = (StatementAllocSite) al.getAllocsite();
if (stmt2alloc.containsKey(site.getStatement())) {
result.add(stmt2alloc.get(site.getStatement()));
}
}
else if (obj instanceof UserFunctionValue) {
UserFunctionValue uf = (UserFunctionValue) obj;
result.add(new FunctionAllocSite(main.getAstBinding().getFunctionNode(uf.getFunction())));
}
else if (obj instanceof FunctionPrototypeValue) {
FunctionPrototypeValue fp = (FunctionPrototypeValue) obj;
if (fp.getFunction() instanceof UserFunctionValue) {
UserFunctionValue uf = (UserFunctionValue) fp.getFunction();
result.add(new FunctionProtoAllocSite(main.getAstBinding().getFunctionNode(uf.getFunction())));
}
}
else if (obj instanceof ArgumentsArrayValue) {
ArgumentsArrayValue av = (ArgumentsArrayValue) obj;
result.add(new ArgumentsArrayAllocSite(main.getAstBinding().getFunctionNode(av.getFunction())));
}
}
return result;
}
private Set<ObjectValue> getObjectsAllocatedAt(AllocSite site) {
final Set<ObjectValue> result = new HashSet<ObjectValue>();
return site.apply(new AllocSiteAnswerVisitor<Set<ObjectValue>>() {
@Override
public Set<ObjectValue> caseArguments(ArgumentsArrayAllocSite site) {
Function function = main.getAstBinding().getFunction(site.getExp());
for (Context ctx : main.getFunctionReachableContexts().getView(function)) {
result.add(new ArgumentsArrayValue(function, ctx));
}
return result;
}
@Override
public Set<ObjectValue> caseArrayLiteral(ArrayLiteralAllocSite site) {
return main.objects(site.getExp());
}
@Override
public Set<ObjectValue> caseFunction(FunctionAllocSite site) {
for (CreateFunction stm : main.getAstBinding().getFunctions(site.getExp())) {
for (InitializeFunctionNode flow : main.getDataflowBinding().getFunctionNodes(stm)) {
result.addAll(main.lookupIns(flow.getResult(), ObjectValue.class));
}
}
return result;
}
@Override
public Set<ObjectValue> caseFunctionProto(FunctionProtoAllocSite site) {
for (CreateFunction stm : main.getAstBinding().getFunctions(site.getExp())) {
for (InitializeFunctionNode flow : main.getDataflowBinding().getFunctionNodes(stm)) {
for (UserFunctionValue uf : main.lookupIns(flow.getResult(), UserFunctionValue.class)) {
result.add(uf.getFunctionPrototype());
}
}
}
return result;
}
@Override
public Set<ObjectValue> caseNewExp(NewExpAllocSite site) {
for (InvokeStatement stm : main.getAstBinding().getInvokeStatements(site.getExp())) {
for (IInvocationFlowNode flow : main.getDataflowBinding().getInvoke(stm)) {
result.addAll(main.lookupIns(flow.getBase(), ObjectValue.class));
}
}
return result;
}
@Override
public Set<ObjectValue> caseObjLiteral(ObjLiteralAllocSite site) {
return main.objects(site.getExp());
}
@Override
public Set<ObjectValue> caseRegExp(RegExpAllocSite site) {
return main.objects(site.getExp());
}
});
}
@Override
public Set<AllocSite> getPrototypeOf(AllocSite site) {
return getAllocSites(main.getDirectPrototypes(getObjectsAllocatedAt(site)));
}
@Override
public MultiMap<String, AllocSite> getPointsTo(AllocSite site) {
MultiMap<String,AllocSite> result = new MultiMap<String, AllocSite>();
for (ObjectValue obj : getObjectsAllocatedAt(site)) {
addProperties(result, obj);
}
return result;
}
private void addProperties(MultiMap<String, AllocSite> result, ObjectValue obj) {
for (String prty : main.getKnownPropertyNames()) {
result.addAll(prty, getAllocSites(main.lookupSens(new NamedPropertyKey(obj, prty), ObjectValue.class)));
result.addAll(prty, getAllocSites(main.lookupSens(obj.getDynamicStoreProperty(), ObjectValue.class)));
}
}
@Override
public Set<AllocSite> getArgumentAllocationSites(IFunction function, int argIndex) {
Function func = main.getAstBinding().getFunction(function);
if (func.getParameterNames().size() < argIndex)
throw new IllegalArgumentException("Can only query declared parameters");
return getAllocSites(main.lookupIns(new VariableKey(func.getParameterNames().get(argIndex), func, NullContext.Instance), ObjectValue.class));
}
@Override
public Set<AllocSite> getThisAllocationSites(IFunction function) {
Function func = main.getAstBinding().getFunction(function);
return getAllocSites(main.lookupIns(new VariableKey("this", func, NullContext.Instance), ObjectValue.class));
}
}