Skip to content

Commit

Permalink
Improved Enhanced Bounds Implementation with enhanced memory utilizat…
Browse files Browse the repository at this point in the history
…ion and better handling of reflective vertices (#1395)

This pull request includes changes to several files related to the
implementation of a bounded call graph strategy and improvements in
memory utilization and reflective vertices handling.

FieldBasedCallGraphBuilder.java: Introduces support for reflective call
vertices for invocations of call and apply, enhancing the handling of
reflective vertices.
WorklistBasedOptimisticCallgraphBuilder.java: Implements enhancements
for bounded call graph construction, including optimizations for better
memory utilization and efficiency.
bounded.js: Updates test scenarios to reflect changes in bounded call
behavior, ensuring accurate testing of bounded call graph functionality.
TestBoundedFieldBasedCG.java: Updates the assertions for bounded call
tests, ensuring accurate behavior in different scenarios.
  • Loading branch information
MadhuNimmo committed May 1, 2024
1 parent 18a521c commit ae27048
Show file tree
Hide file tree
Showing 4 changed files with 83 additions and 30 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -17,42 +17,42 @@
import org.junit.jupiter.api.Test;

public class TestBoundedFieldBasedCG extends AbstractFieldBasedTest {
private static final Object[][] assertionsForBound1JS =
private static final Object[][] assertionsForBound0JS =
new Object[][] {
new Object[] {ROOT, new String[] {"suffix:bounded.js"}},
new Object[] {"suffix:bounded.js", new String[] {"suffix:y", "!suffix:x"}},
new Object[] {"suffix:bounded.js", new String[] {"suffix:y", "!suffix:x", "suffix:call"}},
new Object[] {"suffix:call", new String[] {"!suffix:m"}}
};

@Test
public void testBound1Worklist() throws WalaException, Error, CancelException {
public void testBound0Worklist() throws WalaException, Error, CancelException {
runBoundedTest(
"tests/fieldbased/bounded.js", assertionsForBound1JS, BuilderType.OPTIMISTIC_WORKLIST, 1);
"tests/fieldbased/bounded.js", assertionsForBound0JS, BuilderType.OPTIMISTIC_WORKLIST, 0);
}

private static final Object[][] assertionsForBound2JS =
private static final Object[][] assertionsForBound1JS =
new Object[][] {
new Object[] {ROOT, new String[] {"suffix:bounded.js"}},
new Object[] {"suffix:bounded.js", new String[] {"suffix:x", "suffix:y"}},
new Object[] {"suffix:bounded.js", new String[] {"suffix:x", "suffix:y", "suffix:call"}},
new Object[] {"suffix:call", new String[] {"!suffix:m"}}
};

@Test
public void testBound2Worklist() throws WalaException, Error, CancelException {
public void testBound1Worklist() throws WalaException, Error, CancelException {
runBoundedTest(
"tests/fieldbased/bounded.js", assertionsForBound2JS, BuilderType.OPTIMISTIC_WORKLIST, 2);
"tests/fieldbased/bounded.js", assertionsForBound1JS, BuilderType.OPTIMISTIC_WORKLIST, 1);
}

private static final Object[][] assertionsForBound3JS =
private static final Object[][] assertionsForBound2JS =
new Object[][] {
new Object[] {ROOT, new String[] {"suffix:bounded.js"}},
new Object[] {"suffix:bounded.js", new String[] {"suffix:x", "suffix:y", "suffix:call"}},
new Object[] {"suffix:call", new String[] {"suffix:m"}},
};

@Test
public void testBound3Worklist() throws WalaException, Error, CancelException {
public void testBound2Worklist() throws WalaException, Error, CancelException {
runBoundedTest(
"tests/fieldbased/bounded.js", assertionsForBound3JS, BuilderType.OPTIMISTIC_WORKLIST, 3);
"tests/fieldbased/bounded.js", assertionsForBound2JS, BuilderType.OPTIMISTIC_WORKLIST, 2);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
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.loader.JavaScriptLoader;
import com.ibm.wala.cast.js.ssa.JavaScriptInvoke;
import com.ibm.wala.cast.js.types.JavaScriptMethods;
import com.ibm.wala.cast.types.AstMethodReference;
import com.ibm.wala.classLoader.CallSiteReference;
Expand Down Expand Up @@ -369,6 +370,19 @@ public Set<Pair<CallVertex, FuncVertex>> extractCallGraphEdges(
for (final CallVertex callVertex : factory.getCallVertices()) {
for (FuncVertex funcVertex : flowgraph.getReachingSet(callVertex, monitor)) {
result.add(Pair.make(callVertex, funcVertex));
// add ReflectiveCall vertices for invocations of call and apply
String fullName = funcVertex.getFullName();
if (options instanceof JSAnalysisOptions
&& ((JSAnalysisOptions) options).handleCallApply()
&& (fullName.equals("Lprologue.js/Function_prototype_call")
|| fullName.equals("Lprologue.js/Function_prototype_apply"))) {
JavaScriptInvoke invk = callVertex.getInstruction();
VarVertex reflectiveCalleeVertex =
factory.makeVarVertex(callVertex.getCaller(), invk.getUse(1));
flowgraph.addEdge(
reflectiveCalleeVertex,
factory.makeReflectiveCallVertex(callVertex.getCaller(), invk));
}
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,11 @@
import com.ibm.wala.util.collections.Iterator2Iterable;
import com.ibm.wala.util.collections.MapUtil;
import com.ibm.wala.util.collections.Pair;
import com.ibm.wala.util.intset.IntIterator;
import com.ibm.wala.util.intset.MutableIntSet;
import com.ibm.wala.util.intset.MutableMapping;
import com.ibm.wala.util.intset.MutableSharedBitVectorIntSet;
import com.ibm.wala.util.intset.OrdinalSetMapping;
import java.util.Map;
import java.util.Set;

Expand Down Expand Up @@ -73,12 +78,25 @@ public FlowGraph buildFlowGraph(IProgressMonitor monitor) throws CancelException
return builder.buildFlowGraph();
}

private static MutableIntSet findOrCreateMutableIntSet(Map<Vertex, MutableIntSet> M, Vertex v) {
if (M == null) {
throw new IllegalArgumentException("M is null");
}
MutableIntSet mis = M.get(v);
if (mis == null) {
mis = new MutableSharedBitVectorIntSet();
M.put(v, mis);
}
return mis;
}

@Override
public Set<Pair<CallVertex, FuncVertex>> extractCallGraphEdges(
FlowGraph flowgraph, IProgressMonitor monitor) throws CancelException {
VertexFactory factory = flowgraph.getVertexFactory();
Set<Vertex> worklist = HashSetFactory.make();
Map<Vertex, Set<FuncVertex>> reachingFunctions = HashMapFactory.make();
OrdinalSetMapping<FuncVertex> mapping = new MutableMapping<>(new FuncVertex[100]);
Map<Vertex, MutableIntSet> reachingFunctions = HashMapFactory.make();
Map<VarVertex, Pair<JavaScriptInvoke, Boolean>> reflectiveCalleeVertices =
HashMapFactory.make();
/* maps to maintain the list of reachable calls that are yet to be processed * */
Expand All @@ -89,7 +107,8 @@ public Set<Pair<CallVertex, FuncVertex>> extractCallGraphEdges(
if (v instanceof FuncVertex) {
FuncVertex fv = (FuncVertex) v;
worklist.add(fv);
MapUtil.findOrCreateSet(reachingFunctions, fv).add(fv);
int mappedVal = mapping.add(fv);
findOrCreateMutableIntSet(reachingFunctions, fv).add(mappedVal);
}
}
int cnt = 0;
Expand All @@ -101,15 +120,19 @@ public Set<Pair<CallVertex, FuncVertex>> extractCallGraphEdges(
&& (!worklist.isEmpty()
|| !pendingCallWorklist.isEmpty()
|| !pendingReflectiveCallWorklist.isEmpty()))
|| cnt < bound) {
|| (cnt <= bound
&& (!worklist.isEmpty()
|| !pendingCallWorklist.isEmpty()
|| !pendingReflectiveCallWorklist.isEmpty()))) {
if (worklist.isEmpty()) {
processPendingCallWorklist(
flowgraph,
pendingCallWorklist,
factory,
reachingFunctions,
reflectiveCalleeVertices,
worklist);
worklist,
mapping);
processPendingReflectiveCallWorklist(
flowgraph, pendingReflectiveCallWorklist, reflectiveCalleeVertices, worklist);
pendingCallWorklist.clear();
Expand All @@ -120,22 +143,26 @@ public Set<Pair<CallVertex, FuncVertex>> extractCallGraphEdges(

Vertex v = worklist.iterator().next();
worklist.remove(v);
Set<FuncVertex> vReach = MapUtil.findOrCreateSet(reachingFunctions, v);
MutableIntSet vReach = findOrCreateMutableIntSet(reachingFunctions, v);
for (Vertex w : Iterator2Iterable.make(flowgraph.getSucc(v))) {
MonitorUtil.throwExceptionIfCanceled(monitor);

Set<FuncVertex> wReach = MapUtil.findOrCreateSet(reachingFunctions, w);
MutableIntSet wReach = findOrCreateMutableIntSet(reachingFunctions, w);
boolean changed = false;
if (w instanceof CallVertex) {
for (FuncVertex fv : vReach) {
if (wReach.add(fv)) {
IntIterator mappedFuncs = vReach.intIterator();
while (mappedFuncs.hasNext()) {
FuncVertex fv = mapping.getMappedObject(mappedFuncs.next());
if (wReach.add(mapping.getMappedIndex(fv))) {
changed = true;
MapUtil.findOrCreateSet(pendingCallWorklist, w).add(fv);
}
}
} else if (handleCallApply && reflectiveCalleeVertices.containsKey(w)) {
for (FuncVertex fv : vReach) {
if (wReach.add(fv)) {
IntIterator mappedFuncs = vReach.intIterator();
while (mappedFuncs.hasNext()) {
FuncVertex fv = mapping.getMappedObject(mappedFuncs.next());
if (wReach.add(mapping.getMappedIndex(fv))) {
changed = true;
MapUtil.findOrCreateSet(pendingReflectiveCallWorklist, (VarVertex) w).add(fv);
}
Expand All @@ -150,11 +177,18 @@ public Set<Pair<CallVertex, FuncVertex>> extractCallGraphEdges(
cnt += 1;
}

System.out.println("The last executed bound was : " + cnt);

Set<Pair<CallVertex, FuncVertex>> res = HashSetFactory.make();
for (Map.Entry<Vertex, Set<FuncVertex>> entry : reachingFunctions.entrySet()) {
for (Map.Entry<Vertex, MutableIntSet> entry : reachingFunctions.entrySet()) {
final Vertex v = entry.getKey();
if (v instanceof CallVertex)
for (FuncVertex fv : entry.getValue()) res.add(Pair.make((CallVertex) v, fv));
if (v instanceof CallVertex) {
IntIterator mapped = entry.getValue().intIterator();
while (mapped.hasNext()) {
FuncVertex fv = mapping.getMappedObject(mapped.next());
res.add(Pair.make((CallVertex) v, fv));
}
}
}
return res;
}
Expand All @@ -163,9 +197,10 @@ public void processPendingCallWorklist(
FlowGraph flowgraph,
Map<Vertex, Set<FuncVertex>> pendingCallWorklist,
VertexFactory factory,
Map<Vertex, Set<FuncVertex>> reachingFunctions,
Map<Vertex, MutableIntSet> reachingFunctions,
Map<VarVertex, Pair<JavaScriptInvoke, Boolean>> reflectiveCalleeVertices,
Set<Vertex> worklist) {
Set<Vertex> worklist,
OrdinalSetMapping<FuncVertex> mapping) {
for (Map.Entry<Vertex, Set<FuncVertex>> entry : pendingCallWorklist.entrySet()) {
CallVertex callVertex = (CallVertex) entry.getKey();
for (FuncVertex fv : entry.getValue()) {
Expand All @@ -183,8 +218,12 @@ public void processPendingCallWorklist(
// we only add dataflow edges for Function.prototype.call
boolean isCall = fullName.equals("Lprologue.js/Function_prototype_call");
reflectiveCalleeVertices.put(reflectiveCalleeVertex, Pair.make(invk, isCall));
for (FuncVertex fw : MapUtil.findOrCreateSet(reachingFunctions, reflectiveCalleeVertex))
IntIterator reflectiveCalleeMapped =
findOrCreateMutableIntSet(reachingFunctions, reflectiveCalleeVertex).intIterator();
while (reflectiveCalleeMapped.hasNext()) {
FuncVertex fw = mapping.getMappedObject(reflectiveCalleeMapped.next());
addReflectiveCallEdge(flowgraph, reflectiveCalleeVertex, invk, fw, worklist, isCall);
}
}
}
}
Expand Down
6 changes: 3 additions & 3 deletions cast/js/src/test/resources/tests/fieldbased/bounded.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,6 @@ var m = function m() { return "hi"; } // 1
var x = function x() { return m; } // 2
var y = function y() { return x; } // 3
var z = y; // 4
var n = z(); // 5 -- bound 1
var o = n() // 6 -- bound 2
o.call(this); // 7 -- bound 3
var n = z(); // 5 -- bound 0
var o = n() // 6 -- bound 1
o.call(this); // 7 -- bound 2

0 comments on commit ae27048

Please sign in to comment.