com.ibm.wala.cast.js.callgraph.fieldbased.OptimisticCallgraphBuilder Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of com.ibm.wala.cast.js Show documentation
Show all versions of com.ibm.wala.cast.js Show documentation
T. J. Watson Libraries for Analysis
The newest version!
/*
* Copyright (c) 2013 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 com.ibm.wala.cast.js.callgraph.fieldbased.flowgraph.FlowGraph;
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.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.AnalysisOptions;
import com.ibm.wala.ipa.callgraph.IAnalysisCacheView;
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.intset.OrdinalSet;
import java.util.Set;
/**
* 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,
IAnalysisCacheView iAnalysisCacheView,
boolean supportFullPointerAnalysis) {
super(cha, options, iAnalysisCacheView, supportFullPointerAnalysis);
handleCallApply =
options instanceof JSAnalysisOptions && ((JSAnalysisOptions) options).handleCallApply();
}
@Override
public FlowGraph buildFlowGraph(IProgressMonitor monitor) throws CancelException {
FlowGraph flowgraph = flowGraphFactory();
// keep track of which call edges we already know about
Set> 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> newEdges = this.extractCallGraphEdges(flowgraph, monitor);
for (Pair 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);
// 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")
|| edge.snd.getFullName().equals("Lprologue.js/Function_prototype_apply"))) {
addReflectiveCallEdge(flowgraph, edge.fst, monitor);
}
}
}
}
return flowgraph;
}
// add flow corresponding to a new call edge
private static void addEdge(FlowGraph flowgraph, CallVertex c, FuncVertex callee) {
VertexFactory factory = flowgraph.getVertexFactory();
JavaScriptInvoke invk = c.getInstruction();
FuncVertex caller = c.getCaller();
int offset = 0;
if (invk.getDeclaredTarget()
.getSelector()
.equals(JavaScriptMethods.ctorReference.getSelector())) {
offset = 1;
}
for (int i = 0; i < invk.getNumberOfPositionalParameters(); ++i) {
// only flow receiver into 'this' if invk is, in fact, a method call
flowgraph.addEdge(
factory.makeVarVertex(caller, invk.getUse(i)), factory.makeArgVertex(callee));
// if(i != 1 || !invk.getDeclaredTarget().getSelector().equals(AstMethodReference.fnSelector))
flowgraph.addEdge(
factory.makeVarVertex(caller, invk.getUse(i)),
factory.makeParamVertex(callee, i + offset));
}
// 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 static 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 realCallees = flowgraph.getReachingSet(receiverVertex, monitor);
System.err.println("callees " + realCallees + " for " + caller);
for (FuncVertex realCallee : realCallees) {
// flow from arguments to parameters
for (int i = 2; i < invk.getNumberOfPositionalParameters(); ++i)
flowgraph.addEdge(
factory.makeVarVertex(caller, invk.getUse(i)),
factory.makeParamVertex(realCallee, i - 1));
// flow from return vertex to result vertex
flowgraph.addEdge(
factory.makeRetVertex(realCallee), factory.makeVarVertex(caller, invk.getDef()));
}
}
}