diff --git a/com.ibm.wala.core.tests/src/com/ibm/wala/core/tests/callGraph/AcyclicCallGraphTest.java b/com.ibm.wala.core.tests/src/com/ibm/wala/core/tests/callGraph/AcyclicCallGraphTest.java new file mode 100644 index 000000000..453ec67ab --- /dev/null +++ b/com.ibm.wala.core.tests/src/com/ibm/wala/core/tests/callGraph/AcyclicCallGraphTest.java @@ -0,0 +1,61 @@ +package com.ibm.wala.core.tests.callGraph; + +import java.io.IOException; +import java.util.Iterator; +import java.util.Map; +import java.util.Set; + +import org.junit.Assert; +import org.junit.Test; + +import com.ibm.wala.core.tests.util.TestConstants; +import com.ibm.wala.core.tests.util.WalaTestCase; +import com.ibm.wala.ipa.callgraph.AnalysisCache; +import com.ibm.wala.ipa.callgraph.AnalysisOptions; +import com.ibm.wala.ipa.callgraph.AnalysisScope; +import com.ibm.wala.ipa.callgraph.CGNode; +import com.ibm.wala.ipa.callgraph.CallGraph; +import com.ibm.wala.ipa.callgraph.Entrypoint; +import com.ibm.wala.ipa.callgraph.pruned.PrunedCallGraph; +import com.ibm.wala.ipa.cha.ClassHierarchy; +import com.ibm.wala.ipa.cha.ClassHierarchyException; +import com.ibm.wala.util.CancelException; +import com.ibm.wala.util.collections.HashMapFactory; +import com.ibm.wala.util.collections.HashSetFactory; +import com.ibm.wala.util.collections.Iterator2Collection; +import com.ibm.wala.util.graph.Acyclic; +import com.ibm.wala.util.intset.IBinaryNaturalRelation; +import com.ibm.wala.util.intset.IntPair; + +public class AcyclicCallGraphTest extends WalaTestCase { + + @Test public void testNList() throws ClassHierarchyException, IllegalArgumentException, CancelException, IOException { + AnalysisScope scope = CallGraphTestUtil.makeJ2SEAnalysisScope(TestConstants.WALA_TESTDATA, + CallGraphTestUtil.REGRESSION_EXCLUSIONS); + ClassHierarchy cha = ClassHierarchy.make(scope); + Iterable entrypoints = com.ibm.wala.ipa.callgraph.impl.Util.makeMainEntrypoints(scope, cha, + "Lrecurse/NList"); + AnalysisOptions options = CallGraphTestUtil.makeAnalysisOptions(scope, entrypoints); + + CallGraph cg = CallGraphTestUtil.buildZeroCFA(options, new AnalysisCache(), cha, scope, false); + + IBinaryNaturalRelation backEdges = Acyclic.computeBackEdges(cg, cg.getFakeRootNode()); + + Assert.assertTrue("NList should have cycles", backEdges.iterator().hasNext()); + + Map> cgBackEdges = HashMapFactory.make(); + for (Iterator ps = backEdges.iterator(); ps.hasNext(); ) { + IntPair p = ps.next(); + CGNode src = cg.getNode(p.getX()); + if (!cgBackEdges.containsKey(src)) { + cgBackEdges.put(src, HashSetFactory.make()); + } + cgBackEdges.get(src).add(cg.getNode(p.getY())); + } + + PrunedCallGraph pcg = new PrunedCallGraph(cg, Iterator2Collection.toSet(cg.iterator()), cgBackEdges); + + Assert.assertTrue("cycles should be gone", !Acyclic.computeBackEdges(pcg, pcg.getFakeRootNode()).iterator().hasNext()); + } + +} diff --git a/com.ibm.wala.core/src/com/ibm/wala/ipa/callgraph/pruned/PrunedCallGraph.java b/com.ibm.wala.core/src/com/ibm/wala/ipa/callgraph/pruned/PrunedCallGraph.java index ff47be790..a4d20fa1c 100644 --- a/com.ibm.wala.core/src/com/ibm/wala/ipa/callgraph/pruned/PrunedCallGraph.java +++ b/com.ibm.wala.core/src/com/ibm/wala/ipa/callgraph/pruned/PrunedCallGraph.java @@ -12,9 +12,11 @@ package com.ibm.wala.ipa.callgraph.pruned; import java.util.Collection; +import java.util.Collections; import java.util.HashSet; import java.util.Iterator; import java.util.LinkedList; +import java.util.Map; import java.util.Set; import com.ibm.wala.classLoader.CallSiteReference; @@ -31,15 +33,22 @@ public class PrunedCallGraph implements CallGraph { private CallGraph cg; private Set keep; + private Map> remove = Collections.emptyMap(); public PrunedCallGraph(CallGraph cg, Set keep) { this.cg = cg; this.keep = keep; } + public PrunedCallGraph(CallGraph cg, Set keep, Map> remove) { + this(cg, keep); + this.remove = remove; + } + public void removeNodeAndEdges(CGNode n) throws UnsupportedOperationException { cg.removeNodeAndEdges(n); keep.remove(n); + remove.remove(n); } public Iterator iterator() { @@ -67,18 +76,23 @@ public class PrunedCallGraph implements CallGraph { public void removeNode(CGNode n) throws UnsupportedOperationException { cg.removeNode(n); keep.remove(n); + remove.remove(n); } public boolean containsNode(CGNode n) { return cg.containsNode(n) && keep.contains(n); } + private boolean removedEdge(CGNode src, CGNode target) { + return remove.containsKey(src) && remove.get(src).contains(target); + } + public Iterator getPredNodes(CGNode n) { Iterator tmp = cg.getPredNodes(n); Collection col = new LinkedList(); while (tmp.hasNext()) { CGNode no = tmp.next(); - if (keep.contains(no)) { + if (keep.contains(no) && !removedEdge(no, n)) { col.add(no); } } @@ -91,7 +105,7 @@ public class PrunedCallGraph implements CallGraph { int cnt = 0; while (tmp.hasNext()) { CGNode no = tmp.next(); - if (keep.contains(no)) { + if (keep.contains(no) && !removedEdge(no, n)) { cnt++; } } @@ -104,7 +118,7 @@ public class PrunedCallGraph implements CallGraph { Collection col = new LinkedList(); while (tmp.hasNext()) { CGNode no = tmp.next(); - if (keep.contains(no)) { + if (keep.contains(no) && !removedEdge(n, no)) { col.add(no); } } @@ -118,7 +132,7 @@ public class PrunedCallGraph implements CallGraph { int cnt = 0; while (tmp.hasNext()) { CGNode no = tmp.next(); - if (keep.contains(no)) { + if (keep.contains(no) && !removedEdge(n, no)) { cnt++; } } @@ -154,7 +168,7 @@ public class PrunedCallGraph implements CallGraph { public boolean hasEdge(CGNode src, CGNode dst) { - return cg.hasEdge(src, dst) && keep.contains(src) && keep.contains(dst); + return cg.hasEdge(src, dst) && keep.contains(src) && keep.contains(dst) && !removedEdge(src, dst); } @@ -203,7 +217,9 @@ public class PrunedCallGraph implements CallGraph { IntSet tmp = cg.getSuccNodeNumbers(node); BitVectorIntSet kp = new BitVectorIntSet(); for (CGNode n : keep) { - kp.add(getNumber(n)); + if (!removedEdge(node, n)) { + kp.add(getNumber(n)); + } } return tmp.intersection(kp); } @@ -219,7 +235,9 @@ public class PrunedCallGraph implements CallGraph { IntSet tmp = cg.getPredNodeNumbers(node); BitVectorIntSet kp = new BitVectorIntSet(); for (CGNode n : keep) { - kp.add(getNumber(n)); + if (!removedEdge(n, node)) { + kp.add(getNumber(n)); + } } return tmp.intersection(kp); } @@ -287,7 +305,7 @@ public class PrunedCallGraph implements CallGraph { Set tmp = cg.getPossibleTargets(node, site); Set ret = new HashSet(); for (CGNode n : tmp) { - if (keep.contains(n)) { + if (keep.contains(n) && !removedEdge(node, n)) { ret.add(n); } } @@ -304,7 +322,7 @@ public class PrunedCallGraph implements CallGraph { public Iterator getPossibleSites(CGNode src, CGNode target) { - if (!(keep.contains(src) && keep.contains(target))){ + if (!(keep.contains(src) && keep.contains(target)) || removedEdge(src, target)){ return null; } return cg.getPossibleSites(src, target);