2013-05-22 23:04:25 +00:00
|
|
|
/******************************************************************************
|
|
|
|
* Copyright (c) 2002 - 2012 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.callgraph.fieldbased;
|
|
|
|
|
|
|
|
import java.util.Iterator;
|
|
|
|
import java.util.Set;
|
|
|
|
|
|
|
|
import com.ibm.wala.cast.ipa.callgraph.AstContextInsensitiveSSAContextInterpreter;
|
|
|
|
import com.ibm.wala.cast.js.callgraph.fieldbased.flowgraph.FlowGraph;
|
2013-08-05 18:35:41 +00:00
|
|
|
import com.ibm.wala.cast.js.callgraph.fieldbased.flowgraph.FlowGraphBuilder;
|
2013-05-22 23:04:25 +00:00
|
|
|
import com.ibm.wala.cast.js.callgraph.fieldbased.flowgraph.vertices.CallVertex;
|
|
|
|
import com.ibm.wala.cast.js.callgraph.fieldbased.flowgraph.vertices.FuncVertex;
|
2014-10-20 02:44:03 +00:00
|
|
|
import com.ibm.wala.cast.js.callgraph.fieldbased.flowgraph.vertices.ObjectVertex;
|
2013-05-22 23:04:25 +00:00
|
|
|
import com.ibm.wala.cast.js.callgraph.fieldbased.flowgraph.vertices.VarVertex;
|
|
|
|
import com.ibm.wala.cast.js.callgraph.fieldbased.flowgraph.vertices.VertexFactory;
|
|
|
|
import com.ibm.wala.cast.js.ipa.callgraph.JSAnalysisOptions;
|
|
|
|
import com.ibm.wala.cast.js.ipa.callgraph.JSCallGraph;
|
|
|
|
import com.ibm.wala.cast.js.ipa.callgraph.JavaScriptConstructTargetSelector;
|
|
|
|
import com.ibm.wala.cast.js.ipa.callgraph.JavaScriptFunctionDotCallTargetSelector;
|
2014-05-28 17:35:40 +00:00
|
|
|
import com.ibm.wala.cast.js.ipa.summaries.JavaScriptConstructorFunctions;
|
|
|
|
import com.ibm.wala.cast.js.ipa.summaries.JavaScriptConstructorFunctions.JavaScriptConstructor;
|
|
|
|
import com.ibm.wala.cast.js.types.JavaScriptMethods;
|
2013-05-22 23:04:25 +00:00
|
|
|
import com.ibm.wala.cast.types.AstMethodReference;
|
|
|
|
import com.ibm.wala.classLoader.CallSiteReference;
|
|
|
|
import com.ibm.wala.classLoader.IClass;
|
|
|
|
import com.ibm.wala.classLoader.IMethod;
|
|
|
|
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.Context;
|
|
|
|
import com.ibm.wala.ipa.callgraph.Entrypoint;
|
|
|
|
import com.ibm.wala.ipa.callgraph.MethodTargetSelector;
|
|
|
|
import com.ibm.wala.ipa.callgraph.impl.AbstractRootMethod;
|
|
|
|
import com.ibm.wala.ipa.callgraph.impl.ContextInsensitiveSelector;
|
|
|
|
import com.ibm.wala.ipa.callgraph.impl.Everywhere;
|
2014-02-09 03:50:32 +00:00
|
|
|
import com.ibm.wala.ipa.callgraph.propagation.PointerAnalysis;
|
2013-05-22 23:04:25 +00:00
|
|
|
import com.ibm.wala.ipa.callgraph.propagation.cfa.DefaultSSAInterpreter;
|
|
|
|
import com.ibm.wala.ipa.callgraph.propagation.cfa.DelegatingSSAContextInterpreter;
|
|
|
|
import com.ibm.wala.ipa.callgraph.propagation.cfa.nCFAContextSelector;
|
|
|
|
import com.ibm.wala.ipa.cha.IClassHierarchy;
|
|
|
|
import com.ibm.wala.ssa.SSAAbstractInvokeInstruction;
|
|
|
|
import com.ibm.wala.util.CancelException;
|
2014-02-09 03:50:32 +00:00
|
|
|
import com.ibm.wala.util.MonitorUtil;
|
2013-05-22 23:04:25 +00:00
|
|
|
import com.ibm.wala.util.MonitorUtil.IProgressMonitor;
|
|
|
|
import com.ibm.wala.util.collections.HashSetFactory;
|
|
|
|
import com.ibm.wala.util.collections.Pair;
|
2014-02-09 03:50:32 +00:00
|
|
|
import com.ibm.wala.util.intset.OrdinalSet;
|
2013-05-22 23:04:25 +00:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Abstract call graph builder class for building a call graph from a field-based flow graph.
|
|
|
|
* The algorithm for building the flow graph is left unspecified, and is implemented differently
|
|
|
|
* by subclasses.
|
|
|
|
*
|
|
|
|
* @author mschaefer
|
|
|
|
*
|
|
|
|
*/
|
|
|
|
public abstract class FieldBasedCallGraphBuilder {
|
|
|
|
// class hierarchy of the program to be analysed
|
|
|
|
protected final IClassHierarchy cha;
|
|
|
|
|
|
|
|
// standard call graph machinery
|
|
|
|
protected final AnalysisOptions options;
|
|
|
|
protected final AnalysisCache cache;
|
2014-05-28 17:35:40 +00:00
|
|
|
protected final JavaScriptConstructorFunctions constructors;
|
2013-05-22 23:04:25 +00:00
|
|
|
protected final MethodTargetSelector targetSelector;
|
2014-10-20 02:44:03 +00:00
|
|
|
protected final boolean supportFullPointerAnalysis;
|
2013-05-22 23:04:25 +00:00
|
|
|
|
|
|
|
private static final boolean LOG_TIMINGS = true;
|
|
|
|
|
2014-10-20 02:44:03 +00:00
|
|
|
public FieldBasedCallGraphBuilder(IClassHierarchy cha, AnalysisOptions options, AnalysisCache cache, boolean supportFullPointerAnalysis) {
|
2013-05-22 23:04:25 +00:00
|
|
|
this.cha = cha;
|
|
|
|
this.options = options;
|
|
|
|
this.cache = cache;
|
2014-05-28 17:35:40 +00:00
|
|
|
this.constructors = new JavaScriptConstructorFunctions(cha);
|
|
|
|
this.targetSelector = setupMethodTargetSelector(cha, constructors, options);
|
2014-10-20 02:44:03 +00:00
|
|
|
this.supportFullPointerAnalysis = supportFullPointerAnalysis;
|
2013-05-22 23:04:25 +00:00
|
|
|
}
|
|
|
|
|
2014-05-28 17:35:40 +00:00
|
|
|
private MethodTargetSelector setupMethodTargetSelector(IClassHierarchy cha, JavaScriptConstructorFunctions constructors2, AnalysisOptions options) {
|
|
|
|
MethodTargetSelector result = new JavaScriptConstructTargetSelector(constructors2, options.getMethodTargetSelector());
|
2013-05-22 23:04:25 +00:00
|
|
|
if (options instanceof JSAnalysisOptions && ((JSAnalysisOptions)options).handleCallApply()) {
|
|
|
|
// TODO handle Function.prototype.apply
|
|
|
|
result = new JavaScriptFunctionDotCallTargetSelector(result);
|
|
|
|
}
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
2014-10-20 02:44:03 +00:00
|
|
|
protected FlowGraph flowGraphFactory() {
|
|
|
|
FlowGraphBuilder builder = new FlowGraphBuilder(cha, cache, supportFullPointerAnalysis);
|
2013-08-05 18:35:41 +00:00
|
|
|
return builder.buildFlowGraph();
|
|
|
|
}
|
|
|
|
|
2013-05-22 23:04:25 +00:00
|
|
|
/**
|
|
|
|
* Build a flow graph for the program to be analysed.
|
|
|
|
*/
|
2014-10-20 02:44:03 +00:00
|
|
|
public abstract FlowGraph buildFlowGraph(IProgressMonitor monitor) throws CancelException;
|
2013-05-22 23:04:25 +00:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Main entry point: builds a flow graph, then extracts a call graph and returns it.
|
|
|
|
*/
|
2014-10-20 02:44:03 +00:00
|
|
|
public Pair<JSCallGraph,PointerAnalysis<ObjectVertex>> buildCallGraph(Iterable<Entrypoint> eps, IProgressMonitor monitor) throws CancelException {
|
2013-05-22 23:04:25 +00:00
|
|
|
long fgBegin, fgEnd, cgBegin, cgEnd;
|
|
|
|
|
|
|
|
if(LOG_TIMINGS) fgBegin = System.currentTimeMillis();
|
2014-02-09 03:50:32 +00:00
|
|
|
|
|
|
|
MonitorUtil.beginTask(monitor, "flow graph", 1);
|
2014-10-20 02:44:03 +00:00
|
|
|
FlowGraph flowGraph = buildFlowGraph(monitor);
|
2014-02-09 03:50:32 +00:00
|
|
|
MonitorUtil.done(monitor);
|
2013-05-22 23:04:25 +00:00
|
|
|
|
|
|
|
if(LOG_TIMINGS) {
|
|
|
|
fgEnd = System.currentTimeMillis();
|
|
|
|
System.out.println("flow graph construction took " + (fgEnd-fgBegin)/1000.0 + " seconds");
|
|
|
|
cgBegin = System.currentTimeMillis();
|
|
|
|
}
|
|
|
|
|
2014-02-09 03:50:32 +00:00
|
|
|
MonitorUtil.beginTask(monitor, "extract call graph", 1);
|
2013-08-05 18:35:41 +00:00
|
|
|
JSCallGraph cg = extract(flowGraph, eps, monitor);
|
2014-02-09 03:50:32 +00:00
|
|
|
MonitorUtil.done(monitor);
|
2013-05-22 23:04:25 +00:00
|
|
|
|
|
|
|
if(LOG_TIMINGS) {
|
|
|
|
cgEnd = System.currentTimeMillis();
|
|
|
|
System.out.println("call graph extraction took " + (cgEnd-cgBegin)/1000.0 + " seconds");
|
|
|
|
}
|
|
|
|
|
2014-10-22 15:27:48 +00:00
|
|
|
return Pair.make(cg,flowGraph.getPointerAnalysis(cg, monitor));
|
2013-05-22 23:04:25 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Extract a call graph from a given flow graph.
|
|
|
|
*/
|
|
|
|
@SuppressWarnings("deprecation")
|
2013-08-05 18:35:41 +00:00
|
|
|
protected JSCallGraph extract(FlowGraph flowgraph, Iterable<Entrypoint> eps, IProgressMonitor monitor) throws CancelException {
|
2014-02-09 03:50:32 +00:00
|
|
|
// set up call graph
|
2013-05-22 23:04:25 +00:00
|
|
|
final JSCallGraph cg = new JSCallGraph(cha, options, cache);
|
|
|
|
cg.init();
|
|
|
|
cg.setInterpreter(new DelegatingSSAContextInterpreter(new AstContextInsensitiveSSAContextInterpreter(options, cache), new DefaultSSAInterpreter(options, cache)));
|
2013-08-05 18:35:41 +00:00
|
|
|
|
|
|
|
// set up call edges from fake root to all script nodes
|
2013-05-22 23:04:25 +00:00
|
|
|
AbstractRootMethod fakeRootMethod = (AbstractRootMethod)cg.getFakeRootNode().getMethod();
|
|
|
|
CGNode fakeRootNode = cg.findOrCreateNode(fakeRootMethod, Everywhere.EVERYWHERE);
|
|
|
|
for(Iterator<Entrypoint> iter = eps.iterator(); iter.hasNext();) {
|
|
|
|
Entrypoint ep = iter.next();
|
|
|
|
CGNode nd = cg.findOrCreateNode(ep.getMethod(), Everywhere.EVERYWHERE);
|
|
|
|
SSAAbstractInvokeInstruction invk = ep.addCall(fakeRootMethod);
|
|
|
|
fakeRootNode.addTarget(invk.getCallSite(), nd);
|
|
|
|
}
|
|
|
|
// register the fake root as the "true" entrypoint
|
|
|
|
cg.registerEntrypoint(fakeRootNode);
|
|
|
|
|
|
|
|
// now add genuine call edges
|
|
|
|
Set<Pair<CallVertex, FuncVertex>> edges = extractCallGraphEdges(flowgraph, monitor);
|
|
|
|
|
2013-08-05 18:35:41 +00:00
|
|
|
for (Pair<CallVertex, FuncVertex> edge : edges) {
|
|
|
|
CallVertex callVertex = edge.fst;
|
|
|
|
FuncVertex targetVertex = edge.snd;
|
2014-02-09 03:50:32 +00:00
|
|
|
IClass kaller = callVertex.getCaller().getConcreteType();
|
2013-08-05 18:35:41 +00:00
|
|
|
CGNode caller = cg.findOrCreateNode(kaller.getMethod(AstMethodReference.fnSelector), Everywhere.EVERYWHERE);
|
|
|
|
CallSiteReference site = callVertex.getSite();
|
2014-02-09 03:50:32 +00:00
|
|
|
IMethod target = targetSelector.getCalleeTarget(caller, site, targetVertex.getConcreteType());
|
2013-08-05 18:35:41 +00:00
|
|
|
boolean isFunctionPrototypeCall = target != null
|
|
|
|
&& target.getName().toString().startsWith(JavaScriptFunctionDotCallTargetSelector.SYNTHETIC_CALL_METHOD_PREFIX);
|
2014-05-28 17:35:40 +00:00
|
|
|
|
2013-08-05 18:35:41 +00:00
|
|
|
if (isFunctionPrototypeCall) {
|
|
|
|
handleFunctionPrototypeCallInvocation(flowgraph, monitor, cg, callVertex, caller, site, target);
|
|
|
|
} else {
|
2014-05-28 17:35:40 +00:00
|
|
|
addEdgeToJSCallGraph(cg, site, target, caller);
|
|
|
|
|
|
|
|
if (target instanceof JavaScriptConstructor) {
|
|
|
|
IMethod fun = ((JavaScriptConstructor)target).constructedType().getMethod(AstMethodReference.fnSelector);
|
|
|
|
CGNode ctorCaller = cg.findOrCreateNode(target, Everywhere.EVERYWHERE);
|
|
|
|
|
|
|
|
CallSiteReference ref = null;
|
|
|
|
Iterator<CallSiteReference> sites = ctorCaller.iterateCallSites();
|
|
|
|
while(sites.hasNext()) {
|
|
|
|
CallSiteReference r = sites.next();
|
|
|
|
if (r.getDeclaredTarget().getSelector().equals(AstMethodReference.fnSelector)) {
|
|
|
|
ref = r;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (ref != null) {
|
|
|
|
addEdgeToJSCallGraph(cg, ref, fun, ctorCaller);
|
|
|
|
}
|
|
|
|
}
|
2013-08-05 18:35:41 +00:00
|
|
|
}
|
|
|
|
}
|
2013-05-22 23:04:25 +00:00
|
|
|
|
|
|
|
return cg;
|
|
|
|
}
|
|
|
|
|
2013-08-05 18:35:41 +00:00
|
|
|
private boolean handleFunctionPrototypeCallInvocation(FlowGraph flowgraph, IProgressMonitor monitor, final JSCallGraph cg,
|
2013-05-22 23:04:25 +00:00
|
|
|
CallVertex callVertex, CGNode caller, CallSiteReference site,
|
|
|
|
IMethod target) throws CancelException {
|
|
|
|
// use to get 1-level of call string for Function.prototype.call, to
|
|
|
|
// preserve the precision of the field-based call graph
|
|
|
|
final nCFAContextSelector functionPrototypeCallSelector = new nCFAContextSelector(1, new ContextInsensitiveSelector());
|
|
|
|
Context calleeContext = functionPrototypeCallSelector.getCalleeTarget(caller, site, target, null);
|
2013-08-05 18:35:41 +00:00
|
|
|
boolean ret = addCGEdgeWithContext(cg, site, target, caller, calleeContext);
|
2013-05-22 23:04:25 +00:00
|
|
|
CGNode functionPrototypeCallNode = cg.findOrCreateNode(target, calleeContext);
|
|
|
|
// need to create nodes for reflective targets of call, and then add them
|
|
|
|
// as callees of the synthetic method
|
2014-02-09 03:50:32 +00:00
|
|
|
OrdinalSet<FuncVertex> reflectiveTargets = getReflectiveTargets(flowgraph, callVertex, monitor);
|
2014-05-28 17:35:40 +00:00
|
|
|
System.err.println("adding callees " + reflectiveTargets + " for " + caller);
|
2013-05-22 23:04:25 +00:00
|
|
|
// there should only be one call site in the synthetic method
|
|
|
|
CallSiteReference reflectiveCallSite = functionPrototypeCallNode.getIR().iterateCallSites().next();
|
|
|
|
for (FuncVertex f : reflectiveTargets) {
|
2014-02-09 03:50:32 +00:00
|
|
|
IMethod reflectiveTgtMethod = targetSelector.getCalleeTarget(functionPrototypeCallNode, reflectiveCallSite, f.getConcreteType());
|
2013-08-05 18:35:41 +00:00
|
|
|
ret |= addEdgeToJSCallGraph(cg, reflectiveCallSite, reflectiveTgtMethod, functionPrototypeCallNode);
|
2013-05-22 23:04:25 +00:00
|
|
|
}
|
2013-08-05 18:35:41 +00:00
|
|
|
return ret;
|
2013-05-22 23:04:25 +00:00
|
|
|
}
|
|
|
|
|
2013-08-05 18:35:41 +00:00
|
|
|
private boolean addEdgeToJSCallGraph(final JSCallGraph cg, CallSiteReference site, IMethod target, CGNode caller)
|
2013-05-22 23:04:25 +00:00
|
|
|
throws CancelException {
|
|
|
|
return addCGEdgeWithContext(cg, site, target, caller, Everywhere.EVERYWHERE);
|
|
|
|
}
|
|
|
|
|
2013-08-05 18:35:41 +00:00
|
|
|
Set<IClass> constructedTypes = HashSetFactory.make();
|
|
|
|
|
2013-05-22 23:04:25 +00:00
|
|
|
Everywhere targetContext = Everywhere.EVERYWHERE;
|
|
|
|
@SuppressWarnings("deprecation")
|
2013-08-05 18:35:41 +00:00
|
|
|
private boolean addCGEdgeWithContext(final JSCallGraph cg, CallSiteReference site, IMethod target, CGNode caller,
|
2013-05-22 23:04:25 +00:00
|
|
|
Context targetContext) throws CancelException {
|
2013-08-05 18:35:41 +00:00
|
|
|
boolean ret = false;
|
2013-05-22 23:04:25 +00:00
|
|
|
if(target != null) {
|
|
|
|
CGNode callee = cg.findOrCreateNode(target, targetContext);
|
|
|
|
// add nodes first, to be on the safe side
|
2013-08-05 18:35:41 +00:00
|
|
|
cg.addNode(caller);
|
|
|
|
cg.addNode(callee);
|
2013-05-22 23:04:25 +00:00
|
|
|
// add callee as successor of caller
|
2013-08-05 18:35:41 +00:00
|
|
|
ret = !cg.getPossibleTargets(caller, site).contains(callee);
|
|
|
|
if (ret) {
|
|
|
|
cg.addEdge(caller, callee);
|
|
|
|
caller.addTarget(site, callee);
|
|
|
|
}
|
2013-05-22 23:04:25 +00:00
|
|
|
}
|
2013-08-05 18:35:41 +00:00
|
|
|
return ret;
|
2013-05-22 23:04:25 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Given a callVertex that can invoke Function.prototype.call, get the
|
|
|
|
* FuncVertex nodes for the reflectively-invoked methods
|
|
|
|
* @throws CancelException
|
|
|
|
*/
|
2014-02-09 03:50:32 +00:00
|
|
|
private OrdinalSet<FuncVertex> getReflectiveTargets(FlowGraph flowGraph, CallVertex callVertex, IProgressMonitor monitor) throws CancelException {
|
2013-05-22 23:04:25 +00:00
|
|
|
SSAAbstractInvokeInstruction invoke = callVertex.getInstruction();
|
|
|
|
VarVertex functionParam = flowGraph.getVertexFactory().makeVarVertex(callVertex.getCaller(), invoke.getUse(1));
|
2014-02-09 03:50:32 +00:00
|
|
|
return flowGraph.getReachingSet(functionParam, monitor);
|
2013-05-22 23:04:25 +00:00
|
|
|
}
|
|
|
|
|
2014-05-28 17:35:40 +00:00
|
|
|
private OrdinalSet<FuncVertex> getConstructorTargets(FlowGraph flowGraph, CallVertex callVertex, IProgressMonitor monitor) throws CancelException {
|
|
|
|
SSAAbstractInvokeInstruction invoke = callVertex.getInstruction();
|
|
|
|
assert invoke.getDeclaredTarget().getName().equals(JavaScriptMethods.ctorAtom);
|
|
|
|
VarVertex objectParam = flowGraph.getVertexFactory().makeVarVertex(callVertex.getCaller(), invoke.getUse(0));
|
|
|
|
return flowGraph.getReachingSet(objectParam, monitor);
|
|
|
|
}
|
|
|
|
|
2013-05-22 23:04:25 +00:00
|
|
|
/**
|
|
|
|
* Extract call edges from the flow graph into high-level representation.
|
|
|
|
*/
|
|
|
|
protected Set<Pair<CallVertex, FuncVertex>> extractCallGraphEdges(FlowGraph flowgraph, IProgressMonitor monitor) throws CancelException {
|
|
|
|
VertexFactory factory = flowgraph.getVertexFactory();
|
|
|
|
final Set<Pair<CallVertex, FuncVertex>> result = HashSetFactory.make();
|
|
|
|
|
|
|
|
// find all pairs <call, func> such that call is reachable from func in the flow graph
|
|
|
|
for(final CallVertex callVertex : factory.getCallVertices()) {
|
2014-02-09 03:50:32 +00:00
|
|
|
for(FuncVertex funcVertex : flowgraph.getReachingSet(callVertex, monitor)) {
|
|
|
|
result.add(Pair.make(callVertex, funcVertex));
|
2013-05-22 23:04:25 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
}
|