121 lines
5.2 KiB
Java
121 lines
5.2 KiB
Java
|
package com.ibm.wala.cast.js.callgraph.fieldbased;
|
||
|
|
||
|
import java.util.Set;
|
||
|
|
||
|
import com.ibm.wala.cast.js.callgraph.fieldbased.flowgraph.FlowGraph;
|
||
|
import com.ibm.wala.cast.js.callgraph.fieldbased.flowgraph.FlowGraphBuilder;
|
||
|
import com.ibm.wala.cast.js.callgraph.fieldbased.flowgraph.vertices.CallVertex;
|
||
|
import com.ibm.wala.cast.js.callgraph.fieldbased.flowgraph.vertices.FuncVertex;
|
||
|
import com.ibm.wala.cast.js.callgraph.fieldbased.flowgraph.vertices.VarVertex;
|
||
|
import com.ibm.wala.cast.js.callgraph.fieldbased.flowgraph.vertices.Vertex;
|
||
|
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.ssa.JavaScriptInvoke;
|
||
|
import com.ibm.wala.cast.js.types.JavaScriptMethods;
|
||
|
import com.ibm.wala.ipa.callgraph.AnalysisCache;
|
||
|
import com.ibm.wala.ipa.callgraph.AnalysisOptions;
|
||
|
import com.ibm.wala.ipa.cha.IClassHierarchy;
|
||
|
import com.ibm.wala.util.CancelException;
|
||
|
import com.ibm.wala.util.MonitorUtil;
|
||
|
import com.ibm.wala.util.MonitorUtil.IProgressMonitor;
|
||
|
import com.ibm.wala.util.collections.HashSetFactory;
|
||
|
import com.ibm.wala.util.collections.Pair;
|
||
|
import com.ibm.wala.util.collections.Util;
|
||
|
import com.ibm.wala.util.intset.OrdinalSet;
|
||
|
|
||
|
/**
|
||
|
* Optimistic call graph builder that propagates inter-procedural data flow iteratively as
|
||
|
* call edges are discovered. Slower, but potentially more sound than {@link PessimisticCallGraphBuilder}.
|
||
|
*
|
||
|
* @author mschaefer
|
||
|
*
|
||
|
*/
|
||
|
public class OptimisticCallgraphBuilder extends FieldBasedCallGraphBuilder {
|
||
|
/** The maximum number of iterations to perform. */
|
||
|
public int ITERATION_CUTOFF = Integer.MAX_VALUE;
|
||
|
|
||
|
private final boolean handleCallApply;
|
||
|
|
||
|
public OptimisticCallgraphBuilder(IClassHierarchy cha, AnalysisOptions options, AnalysisCache cache) {
|
||
|
super(cha, options, cache);
|
||
|
handleCallApply = options instanceof JSAnalysisOptions && ((JSAnalysisOptions)options).handleCallApply();
|
||
|
}
|
||
|
|
||
|
@Override
|
||
|
public FlowGraph buildFlowGraph(IProgressMonitor monitor) throws CancelException {
|
||
|
FlowGraphBuilder builder = new FlowGraphBuilder(cha, cache);
|
||
|
FlowGraph flowgraph = builder.buildFlowGraph();
|
||
|
|
||
|
// keep track of which call edges we already know about
|
||
|
Set<Pair<CallVertex, FuncVertex>> knownEdges = HashSetFactory.make();
|
||
|
|
||
|
// flag for fixpoint iteration
|
||
|
boolean changed = true;
|
||
|
int iter = 0;
|
||
|
|
||
|
while(iter++ < ITERATION_CUTOFF && changed) {
|
||
|
MonitorUtil.throwExceptionIfCanceled(monitor);
|
||
|
changed = false;
|
||
|
|
||
|
// extract all call edges from the flow graph
|
||
|
Set<Pair<CallVertex, FuncVertex>> newEdges = this.extractCallGraphEdges(flowgraph, monitor);
|
||
|
|
||
|
for(Pair<CallVertex, FuncVertex> edge : newEdges) {
|
||
|
MonitorUtil.throwExceptionIfCanceled(monitor);
|
||
|
|
||
|
// set changed to true if this is a new edge
|
||
|
boolean newEdge = knownEdges.add(edge);
|
||
|
changed = changed || newEdge;
|
||
|
|
||
|
if(newEdge) {
|
||
|
// handle it
|
||
|
addEdge(flowgraph, edge.fst, edge.snd, monitor);
|
||
|
|
||
|
// special handling of invocations of Function.prototype.call
|
||
|
// TODO: since we've just added some edges to the flow graph, its transitive closure will be
|
||
|
// recomputed here, which is slow and unnecessary
|
||
|
if(handleCallApply && edge.snd.getFullName().equals("Lprologue.js/Function_prototype_call"))
|
||
|
addReflectiveCallEdge(flowgraph, edge.fst, monitor);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return flowgraph;
|
||
|
}
|
||
|
|
||
|
// add flow corresponding to a new call edge
|
||
|
private void addEdge(FlowGraph flowgraph, CallVertex c, FuncVertex callee, IProgressMonitor monitor) throws CancelException {
|
||
|
VertexFactory factory = flowgraph.getVertexFactory();
|
||
|
JavaScriptInvoke invk = c.getInstruction();
|
||
|
FuncVertex caller = c.getCaller();
|
||
|
|
||
|
for(int i=1;i<invk.getNumberOfParameters();++i)
|
||
|
// only flow receiver into 'this' if invk is, in fact, a method call
|
||
|
if(i > 1 || invk.getDeclaredTarget().equals(JavaScriptMethods.dispatchReference))
|
||
|
flowgraph.addEdge(factory.makeVarVertex(caller, invk.getUse(i)), factory.makeParamVertex(callee, i-1));
|
||
|
|
||
|
// flow from return vertex to result vertex
|
||
|
flowgraph.addEdge(factory.makeRetVertex(callee), factory.makeVarVertex(caller, invk.getDef()));
|
||
|
}
|
||
|
|
||
|
// add data flow corresponding to a reflective invocation via Function.prototype.call
|
||
|
// NB: for f.call(...), f will _not_ appear as a call target, but the appropriate argument and return data flow will be set up
|
||
|
private void addReflectiveCallEdge(FlowGraph flowgraph, CallVertex c, IProgressMonitor monitor) throws CancelException {
|
||
|
VertexFactory factory = flowgraph.getVertexFactory();
|
||
|
FuncVertex caller = c.getCaller();
|
||
|
JavaScriptInvoke invk = c.getInstruction();
|
||
|
|
||
|
VarVertex receiverVertex = factory.makeVarVertex(caller, invk.getUse(1));
|
||
|
OrdinalSet<Vertex> reachingSet = flowgraph.getReachingSet(receiverVertex, monitor);
|
||
|
Set<FuncVertex> realCallees = Util.filterByType(reachingSet, FuncVertex.class);
|
||
|
for(FuncVertex realCallee: realCallees) {
|
||
|
// flow from arguments to parameters
|
||
|
for(int i=2;i<invk.getNumberOfParameters();++i)
|
||
|
flowgraph.addEdge(factory.makeVarVertex(caller, invk.getUse(i)), factory.makeParamVertex(realCallee, i-2));
|
||
|
|
||
|
// flow from return vertex to result vertex
|
||
|
flowgraph.addEdge(factory.makeRetVertex(realCallee), factory.makeVarVertex(caller, invk.getDef()));
|
||
|
}
|
||
|
}
|
||
|
}
|