diff --git a/docs/index.html b/docs/index.html index f2ddcff..87b0719 100644 --- a/docs/index.html +++ b/docs/index.html @@ -118,7 +118,6 @@

Dongjie He, Jingbo Lu, and Jingling Xue Qilin: A New Framework for Supporting Fine-Grained Context-Sensitivity in Java Pointer Analysis, 36th European Conference on Object-Oriented Programming (ECOOP'22)

Dongjie He, Jingbo Lu, Yaoqing Gao, and Jingling Xue Selecting Context-Sensitivity Modularly for Accelerating Object-Sensitive Pointer Analysis, IEEE Transactions on Software Engineering (TSE'22)

Dongjie He, Jingbo Lu, and Jingling Xue Context Debloating for Object-Sensitive Pointer Analysis, 36th IEEE/ACM International Conference on Automated Software Engineering (ASE'21)

-

Jingbo Lu, Dongjie He, and Jingling Xue Selective Context-Sensitivity for k-CFA with CFL-Reachability, 28th International Static Analysis Symposium (SAS'21)

Dongjie He, Jingbo Lu, Yaoqing Gao, and Jingling Xue Accelerating Object-Sensitive Pointer Analysis by Exploiting Object Containment and Reachability, 35th European Conference on Object-Oriented Programming (ECOOP'21)

Jingbo Lu, Dongjie He, and Jingling Xue Eagle: CFL-Reachability-based Precision-Preserving Acceleration of Object-Sensitive Pointer Analysis, ACM Transactions on Software Engineering and Methodology (TOSEM 2021)

Jingbo Lu and Jingling Xue Precision-Preserving Yet Fast Object-Sensitive Pointer Analysis with Partial Context Sensitivity. ACM SIGPLAN Conference on Object-Oriented Programming, Systems, Languages, and Applications (OOPSLA'19)

diff --git a/qilin.core/src/qilin/core/solver/Solver.java b/qilin.core/src/qilin/core/solver/Solver.java index d4b587b..c49a1e9 100644 --- a/qilin.core/src/qilin/core/solver/Solver.java +++ b/qilin.core/src/qilin/core/solver/Solver.java @@ -284,7 +284,7 @@ protected void propagatePTS(final ValNode pointer, PointsToSetInternal other) { P2SetVisitor p2SetVisitor = new P2SetVisitor() { @Override public void visit(Node n) { - if(addTo.add(n)) { + if (addTo.add(n)) { returnValue = true; } } diff --git a/qilin.core/src/qilin/stat/SimplifiedEvaluator.java b/qilin.core/src/qilin/stat/SimplifiedEvaluator.java new file mode 100644 index 0000000..4085143 --- /dev/null +++ b/qilin.core/src/qilin/stat/SimplifiedEvaluator.java @@ -0,0 +1,192 @@ +package qilin.stat; + +import qilin.core.PTA; +import qilin.core.builder.FakeMainFactory; +import qilin.core.builder.MethodNodeFactory; +import qilin.core.pag.*; +import qilin.core.sets.P2SetVisitor; +import qilin.core.sets.PointsToSetInternal; +import qilin.util.PTAUtils; +import qilin.util.Stopwatch; +import soot.*; +import soot.jimple.*; +import soot.jimple.toolkits.callgraph.CallGraph; +import soot.jimple.toolkits.callgraph.Edge; + +import java.util.*; + +public class SimplifiedEvaluator implements IEvaluator { + protected final PTA pta; + protected final Exporter exporter; + protected Stopwatch stopwatch; + + public SimplifiedEvaluator(PTA pta) { + this.pta = pta; + exporter = new Exporter(); + } + + @Override + public void begin() { + stopwatch = Stopwatch.newAndStart("PTA evaluator"); + } + + @Override + public void end() { + stopwatch.stop(); + exporter.collectMetric("Time (sec):", String.valueOf(((double) stopwatch.elapsed()))); + exporter.collectMetric("#Reachable Method (CI):", String.valueOf(pta.getNakedReachableMethods().size() - 1)); + CallGraph ciCallGraph = pta.getCallGraph(); + exporter.collectMetric("#Call Edge(CI):", String.valueOf(ciCallGraph.size() - FakeMainFactory.implicitCallEdges)); + + CallGraph callGraph = pta.getCallGraph(); + + // loop over all reachable method's statement to find casts, local + // references, virtual call sites + Set reachableMethods = new HashSet<>(); + for (MethodOrMethodContext momc : pta.getCgb().getReachableMethods()) { + final SootMethod sm = momc.method(); + reachableMethods.add(sm); + } + int totalPolyCalls = 0; + int totalCastsMayFail = 0; + for (SootMethod sm : reachableMethods) { + // All the statements in the method + for (Unit unit : PTAUtils.getMethodBody(sm).getUnits()) { + Stmt st = (Stmt) unit; + // virtual calls + if (st.containsInvokeExpr()) { + InvokeExpr ie = st.getInvokeExpr(); + if (!(ie instanceof StaticInvokeExpr)) { + // Virtual, Special or Instance + // have to check target soot method, cannot just + // count edges + Set targets = new HashSet<>(); + for (Iterator it = callGraph.edgesOutOf(st); it.hasNext(); ) + targets.add(it.next().tgt()); + if (targets.size() > 1) { + totalPolyCalls++; + } + } + } else if (st instanceof AssignStmt) { + Value rhs = ((AssignStmt) st).getRightOp(); + Value lhs = ((AssignStmt) st).getLeftOp(); + if (rhs instanceof CastExpr && lhs.getType() instanceof RefLikeType) { + final Type targetType = ((CastExpr) rhs).getCastType(); + Value v = ((CastExpr) rhs).getOp(); + if (!(v instanceof Local)) { + continue; + } + boolean fails = false; + Set pts = new HashSet<>(); + ((PointsToSetInternal) pta.reachingObjects((Local) v)).mapToCIPointsToSet().forall(new P2SetVisitor() { + @Override + public void visit(Node n) { + pts.add(n); + } + }); + for (Node n : pts) { + if (fails) { + break; + } + fails = !PTAUtils.castNeverFails(n.getType(), targetType); + } + if (fails) { + totalCastsMayFail++; + } + } + } + } + } + exporter.collectMetric("#May Fail Cast (Total):", String.valueOf(totalCastsMayFail)); + exporter.collectMetric("#Virtual Call Site(Polymorphic):", String.valueOf(totalPolyCalls)); + + ptsStat(); + } + + private void ptsStat() { + int ptsCntNoNative = 0; + int varCntNoNative = 0; + // locals exclude Exceptions + for (Local local : pta.getPag().getLocalPointers()) { + try { + LocalVarNode lvn = pta.getPag().findLocalVarNode(local); + if (local.toString().contains("intermediate/")) { + continue; + } + mLocalVarNodes.add(lvn); + } catch (Exception e) { + e.printStackTrace(); + } + } + + // stat avg pts. + for (SootMethod sm : pta.getNakedReachableMethods()) { + MethodPAG mpag = pta.getPag().getMethodPAG(sm); + MethodNodeFactory mnf = mpag.nodeFactory(); + if (!sm.isStatic()) { + mLocalVarNodes.add((LocalVarNode) mnf.caseThis()); + } + for (int i = 0; i < sm.getParameterCount(); ++i) { + Type mType = sm.getParameterType(i); + if (mType instanceof RefLikeType) { + mLocalVarNodes.add((LocalVarNode) mnf.caseParm(i)); + } + } + } + Set tmp = new HashSet<>(); + for (LocalVarNode lvn : mLocalVarNodes) { + SootMethod sm = lvn.getMethod(); + if (PTAUtils.isFakeMainMethod(sm)) { + tmp.add(lvn); + continue; + } + PointsToSetInternal cpts = (PointsToSetInternal) pta.reachingObjects(lvn); + final Set callocSites = getPointsToNewExpr(cpts); + if (callocSites.size() > 0) { + if (!handledNatives.contains(sm.toString())) { + ptsCntNoNative += callocSites.size(); + varCntNoNative++; + } + } else { + tmp.add(lvn); + } + } + mLocalVarNodes.removeAll(tmp); + + exporter.collectMetric("#Avg Points-to Target without Native Var(CI):", String.valueOf(((double) ptsCntNoNative) / (varCntNoNative))); + } + + private final Set handledNatives = Set.of( + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "" + ); + + private final Set mLocalVarNodes = new HashSet<>(); + + protected Set getPointsToNewExpr(PointsToSetInternal pts) { + final Set allocSites = new HashSet<>(); + pts.forall(new P2SetVisitor() { + public void visit(Node n) { + allocSites.add(((AllocNode) n).getNewExpr()); + } + }); + return allocSites; + } + + @Override + public String toString() { + return exporter.report(); + } +} diff --git a/qilin.microben/src/qilin/microben/core/reflog/ArrayNewInstance.log b/qilin.microben/src/qilin/microben/core/reflog/ArrayNewInstance.log new file mode 100644 index 0000000..8287576 --- /dev/null +++ b/qilin.microben/src/qilin/microben/core/reflog/ArrayNewInstance.log @@ -0,0 +1 @@ +Array.newInstance;int[];qilin.microben.core.reflog.ArrayNewInstance.main;9;; \ No newline at end of file diff --git a/qilin.microben/src/qilin/microben/core/reflog/ClassForName.log b/qilin.microben/src/qilin/microben/core/reflog/ClassForName.log new file mode 100644 index 0000000..e921273 --- /dev/null +++ b/qilin.microben/src/qilin/microben/core/reflog/ClassForName.log @@ -0,0 +1,3 @@ +Class.forName;qilin.microben.core.reflog.ClassForName;qilin.microben.core.reflog.ClassForName.main;9;; +Class.forName;qilin.microben.core.reflog.ClassForName;qilin.microben.core.reflog.ClassForName.main;11;; +Class.forName;java.lang.Object;qilin.microben.core.reflog.ClassForName.main;13;; \ No newline at end of file diff --git a/qilin.microben/src/qilin/microben/core/reflog/ClassForName1.log b/qilin.microben/src/qilin/microben/core/reflog/ClassForName1.log new file mode 100644 index 0000000..8bcddb9 --- /dev/null +++ b/qilin.microben/src/qilin/microben/core/reflog/ClassForName1.log @@ -0,0 +1,3 @@ +Class.forName;qilin.microben.core.reflog.ClassForName1;qilin.microben.core.reflog.ClassForName1.main;9;; +Class.forName;qilin.microben.core.reflog.ClassForName1;qilin.microben.core.reflog.ClassForName1.main;11;; +Class.forName;java.lang.Object;qilin.microben.core.reflog.ClassForName1.main;13;; \ No newline at end of file diff --git a/qilin.microben/src/qilin/microben/core/reflog/ClassNewInstance.log b/qilin.microben/src/qilin/microben/core/reflog/ClassNewInstance.log new file mode 100644 index 0000000..7e2427b --- /dev/null +++ b/qilin.microben/src/qilin/microben/core/reflog/ClassNewInstance.log @@ -0,0 +1 @@ +Class.newInstance;qilin.microben.core.reflog.ClassNewInstance;qilin.microben.core.reflog.ClassNewInstance.main;10;; \ No newline at end of file diff --git a/qilin.microben/src/qilin/microben/core/reflog/ConstructorNewInstance.log b/qilin.microben/src/qilin/microben/core/reflog/ConstructorNewInstance.log new file mode 100644 index 0000000..9b7e5b3 --- /dev/null +++ b/qilin.microben/src/qilin/microben/core/reflog/ConstructorNewInstance.log @@ -0,0 +1 @@ +Constructor.newInstance;(java.lang.Object)>;qilin.microben.core.reflog.ConstructorNewInstance.main;17;; \ No newline at end of file diff --git a/qilin.microben/src/qilin/microben/core/reflog/DoopRefBug.log b/qilin.microben/src/qilin/microben/core/reflog/DoopRefBug.log new file mode 100644 index 0000000..c569689 --- /dev/null +++ b/qilin.microben/src/qilin/microben/core/reflog/DoopRefBug.log @@ -0,0 +1 @@ +Field.get*;;qilin.microben.core.reflog.DoopRefBug.main;15;; \ No newline at end of file diff --git a/qilin.microben/src/qilin/microben/core/reflog/FieldGet.log b/qilin.microben/src/qilin/microben/core/reflog/FieldGet.log new file mode 100644 index 0000000..aa9f40b --- /dev/null +++ b/qilin.microben/src/qilin/microben/core/reflog/FieldGet.log @@ -0,0 +1 @@ +Field.get*;;qilin.microben.core.reflog.FieldGet.main;12;; \ No newline at end of file diff --git a/qilin.microben/src/qilin/microben/core/reflog/FieldGetStatic.log b/qilin.microben/src/qilin/microben/core/reflog/FieldGetStatic.log new file mode 100644 index 0000000..fee1c56 --- /dev/null +++ b/qilin.microben/src/qilin/microben/core/reflog/FieldGetStatic.log @@ -0,0 +1 @@ +Field.get*;;qilin.microben.core.reflog.FieldGetStatic.main;11;; \ No newline at end of file diff --git a/qilin.microben/src/qilin/microben/core/reflog/FieldSet.log b/qilin.microben/src/qilin/microben/core/reflog/FieldSet.log new file mode 100644 index 0000000..1203dc0 --- /dev/null +++ b/qilin.microben/src/qilin/microben/core/reflog/FieldSet.log @@ -0,0 +1,2 @@ +Field.set*;;qilin.microben.core.reflog.FieldSet.main;13;; +Field.set*;;qilin.microben.core.reflog.FieldSet.main;14;; \ No newline at end of file diff --git a/qilin.microben/src/qilin/microben/core/reflog/FieldSetStatic.log b/qilin.microben/src/qilin/microben/core/reflog/FieldSetStatic.log new file mode 100644 index 0000000..cdbad95 --- /dev/null +++ b/qilin.microben/src/qilin/microben/core/reflog/FieldSetStatic.log @@ -0,0 +1 @@ +Field.set*;;qilin.microben.core.reflog.FieldSetStatic.main;10;; \ No newline at end of file diff --git a/qilin.microben/src/qilin/microben/core/reflog/MethodInvoke.log b/qilin.microben/src/qilin/microben/core/reflog/MethodInvoke.log new file mode 100644 index 0000000..8e57aba --- /dev/null +++ b/qilin.microben/src/qilin/microben/core/reflog/MethodInvoke.log @@ -0,0 +1 @@ +Method.invoke;;qilin.microben.core.reflog.MethodInvoke.main;8;; \ No newline at end of file diff --git a/qilin.microben/src/qilin/microben/core/reflog/MethodInvokeStatic.log b/qilin.microben/src/qilin/microben/core/reflog/MethodInvokeStatic.log new file mode 100644 index 0000000..5be755e --- /dev/null +++ b/qilin.microben/src/qilin/microben/core/reflog/MethodInvokeStatic.log @@ -0,0 +1 @@ +Method.invoke;;qilin.microben.core.reflog.MethodInvokeStatic.main;15;; \ No newline at end of file diff --git a/qilin.pta/src/qilin/pta/StagedPTA.java b/qilin.pta/src/qilin/pta/StagedPTA.java index 44a7cc7..91c491a 100644 --- a/qilin.pta/src/qilin/pta/StagedPTA.java +++ b/qilin.pta/src/qilin/pta/StagedPTA.java @@ -7,6 +7,12 @@ * This class gives a structure for such kinds of analyses. * */ public abstract class StagedPTA extends BasePTA { + protected BasePTA prePTA; + + public BasePTA getPrePTA() { + return this.prePTA; + } + protected abstract void preAnalysis(); protected void mainAnalysis() { diff --git a/qilin.pta/src/qilin/pta/toolkits/bean/main/Bean.java b/qilin.pta/src/qilin/pta/toolkits/bean/Bean.java similarity index 91% rename from qilin.pta/src/qilin/pta/toolkits/bean/main/Bean.java rename to qilin.pta/src/qilin/pta/toolkits/bean/Bean.java index 7f9fc7b..abbc0ec 100644 --- a/qilin.pta/src/qilin/pta/toolkits/bean/main/Bean.java +++ b/qilin.pta/src/qilin/pta/toolkits/bean/Bean.java @@ -16,14 +16,12 @@ * . */ -package qilin.pta.toolkits.bean.main; +package qilin.pta.toolkits.bean; import qilin.core.PTA; import qilin.core.context.ContextElements; import qilin.core.pag.AllocNode; -import qilin.pta.toolkits.bean.oag.OAG; -import qilin.pta.toolkits.bean.oag.context.ContextSelector; -import qilin.pta.toolkits.bean.oag.context.RepresentativeContextSelector; +import qilin.pta.toolkits.common.OAG; import qilin.util.ANSIColor; import qilin.util.Pair; import qilin.util.Stopwatch; @@ -60,8 +58,7 @@ public static void run(PTA pta, Map>> be * Should be generalized for k >= 3. * */ private static void writeContext(ContextSelector cs, OAG oag, Map>> beanNexCtxMap) { - oag.allNodes().forEach(node -> { - AllocNode allocator = node.getHeap(); + oag.allNodes().forEach(allocator -> { Set ctxs = cs.contextsOf(allocator); for (ContextElements ctx : ctxs) { AllocNode allocHctx = (AllocNode) ctx.get(0); diff --git a/qilin.pta/src/qilin/pta/toolkits/bean/oag/context/ContextSelector.java b/qilin.pta/src/qilin/pta/toolkits/bean/ContextSelector.java similarity index 96% rename from qilin.pta/src/qilin/pta/toolkits/bean/oag/context/ContextSelector.java rename to qilin.pta/src/qilin/pta/toolkits/bean/ContextSelector.java index a27db30..a3ee426 100644 --- a/qilin.pta/src/qilin/pta/toolkits/bean/oag/context/ContextSelector.java +++ b/qilin.pta/src/qilin/pta/toolkits/bean/ContextSelector.java @@ -16,11 +16,11 @@ * along with this program. If not, see . */ -package qilin.pta.toolkits.bean.oag.context; +package qilin.pta.toolkits.bean; import qilin.core.context.ContextElements; import qilin.core.pag.AllocNode; -import qilin.pta.toolkits.bean.oag.OAG; +import qilin.pta.toolkits.common.OAG; import qilin.util.Pair; import java.util.HashMap; diff --git a/qilin.pta/src/qilin/pta/toolkits/bean/oag/context/RepresentativeContextSelector.java b/qilin.pta/src/qilin/pta/toolkits/bean/RepresentativeContextSelector.java similarity index 80% rename from qilin.pta/src/qilin/pta/toolkits/bean/oag/context/RepresentativeContextSelector.java rename to qilin.pta/src/qilin/pta/toolkits/bean/RepresentativeContextSelector.java index 1f5f776..95f6acf 100644 --- a/qilin.pta/src/qilin/pta/toolkits/bean/oag/context/RepresentativeContextSelector.java +++ b/qilin.pta/src/qilin/pta/toolkits/bean/RepresentativeContextSelector.java @@ -16,13 +16,12 @@ * along with this program. If not, see . */ -package qilin.pta.toolkits.bean.oag.context; +package qilin.pta.toolkits.bean; import qilin.core.context.ContextElements; import qilin.core.pag.AllocNode; import qilin.parm.ctxcons.CtxConstructor; -import qilin.pta.toolkits.bean.oag.Node; -import qilin.pta.toolkits.bean.oag.OAG; +import qilin.pta.toolkits.common.OAG; import qilin.util.Triple; import java.util.*; @@ -45,27 +44,26 @@ protected void selectContext(OAG oag) { mergeContextMap(selectContext(oag, node)); }); oag.allNodes().forEach(node -> { - if (!contextMap.containsKey(node.getHeap())) { + if (!contextMap.containsKey(node)) { mergeContextMap(selectContext(oag, node)); } }); } - private Map> selectContext(OAG oag, Node dest) { + private Map> selectContext(OAG oag, AllocNode dest) { Map> tempContextMap = new HashMap<>(); - Queue> worklist = new LinkedList<>(); + Queue> worklist = new LinkedList<>(); initialWorkList(worklist, oag, dest); while (!worklist.isEmpty()) { - Triple triple = worklist.poll(); - Node node = triple.getFirst(); + Triple triple = worklist.poll(); + AllocNode heap = triple.getFirst(); ContextElements ctx = triple.getSecond(); boolean split = triple.getThird(); - AllocNode heap = node.getHeap(); if (!tempContextMap.containsKey(heap)) { tempContextMap.put(heap, new HashSet<>()); } if (tempContextMap.get(heap).add(ctx)) { - Set reachSuccs = selectReachNodes(oag.getSuccsOf(node), dest, oag); + Set reachSuccs = selectReachNodes(oag.getSuccsOf(heap), dest, oag); final boolean isFork = reachSuccs.size() > 1; reachSuccs.forEach(succ -> { final boolean isJoinSucc = oag.getInDegreeOf(succ) > 1; @@ -82,7 +80,7 @@ private Map> selectContext(OAG oag, Node dest) { } Set ctxs = tempContextMap.get(succ); if (ctxs == null || !ctxs.contains(newCtx)) { - addAllocation(ctx, heap, newCtx, succ.getHeap()); + addAllocation(ctx, heap, newCtx, succ); worklist.add(new Triple<>(succ, newCtx, succSplit)); } }); @@ -109,16 +107,16 @@ private void mergeContextMap(Map> anoContextMap) * @param oag * @return */ - private Set selectReachNodes(Set nodes, Node dest, OAG oag) { + private Set selectReachNodes(Collection nodes, AllocNode dest, OAG oag) { return nodes.stream().filter(node -> oag.reaches(node, dest)).collect(Collectors.toSet()); } - private void initialWorkList(Queue> worklist, OAG oag, Node node) { - Set reachRoots = selectReachNodes(oag.rootNodes(), node, oag); + private void initialWorkList(Queue> worklist, OAG oag, AllocNode node) { + Set reachRoots = selectReachNodes(oag.rootNodes(), node, oag); boolean split = reachRoots.size() > 1; ContextElements emptyCtx = (ContextElements) CtxConstructor.emptyContext; reachRoots.forEach(root -> - worklist.add(new Triple<>(root, ContextElements.newContext(emptyCtx, root.getHeap(), depth), split))); + worklist.add(new Triple<>(root, ContextElements.newContext(emptyCtx, root, depth), split))); } } diff --git a/qilin.pta/src/qilin/pta/toolkits/bean/oag/Node.java b/qilin.pta/src/qilin/pta/toolkits/bean/oag/Node.java deleted file mode 100644 index 5eca233..0000000 --- a/qilin.pta/src/qilin/pta/toolkits/bean/oag/Node.java +++ /dev/null @@ -1,108 +0,0 @@ -/* Bean - Making k-Object-Sensitive Pointer Analysis More Precise with Still k-Limiting - * - * Copyright (C) 2016 Tian Tan, Yue Li, Jingling Xue - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package qilin.pta.toolkits.bean.oag; - -import qilin.core.pag.AllocNode; - -import java.util.HashSet; -import java.util.Set; - -public class Node { - - /** - * The heap that this node represents. - */ - private final AllocNode heap; - private final Set preds = new HashSet<>(); - private final Set succs = new HashSet<>(); - private int indegree = 0; - private int outdegree = 0; - private Set reachableNodes = null; - - public Node(AllocNode heap) { - this.heap = heap; - } - - public AllocNode getHeap() { - return heap; - } - - @Override - public int hashCode() { - return heap.hashCode(); - } - - @Override - public boolean equals(Object obj) { - if (this == obj) { - return true; - } - if (!(obj instanceof Node anoNode)) { - return false; - } - return heap.equals(anoNode.heap); - } - - @Override - public String toString() { - return heap.toString(); - } - - public Set getPreds() { - return preds; - } - - public Set getSuccs() { - return succs; - } - - int getInDegree() { - return indegree; - } - - int getOutDegree() { - return outdegree; - } - - public void addSucc(Node succNode) { - if (succs.add(succNode)) { - incOutDegree(); - } - if (succNode.getPreds().add(this)) { - succNode.incInDegree(); - } - } - - Set getReachableNodes() { - return reachableNodes; - } - - void setReachableNodes(Set reachableNodes) { - this.reachableNodes = reachableNodes; - } - - private void incInDegree() { - ++indegree; - } - - private void incOutDegree() { - ++outdegree; - } - -} diff --git a/qilin.pta/src/qilin/pta/toolkits/bean/oag/OAG.java b/qilin.pta/src/qilin/pta/toolkits/bean/oag/OAG.java deleted file mode 100644 index a3ade47..0000000 --- a/qilin.pta/src/qilin/pta/toolkits/bean/oag/OAG.java +++ /dev/null @@ -1,245 +0,0 @@ -/* Qilin - a Java Pointer Analysis Framework - * Copyright (C) 2021-2030 Qilin developers - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Lesser General Public License as - * published by the Free Software Foundation, either version 3.0 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Lesser Public License for more details. - * - * You should have received a copy of the GNU General Lesser Public - * License along with this program. If not, see - * . - */ - -package qilin.pta.toolkits.bean.oag; - -import qilin.core.PTA; -import qilin.core.builder.MethodNodeFactory; -import qilin.core.pag.AllocNode; -import qilin.core.pag.LocalVarNode; -import qilin.core.pag.MethodPAG; -import qilin.core.sets.P2SetVisitor; -import qilin.core.sets.PointsToSetInternal; -import qilin.util.PTAUtils; -import soot.SootMethod; -import soot.util.queue.QueueReader; - -import java.io.FileOutputStream; -import java.io.IOException; -import java.io.PrintStream; -import java.util.*; -import java.util.stream.Collectors; - -/** - * Implementation of Object Allocation Graph (OAG). - */ -public class OAG { - private final PTA pta; - private final Set nodes = new HashSet<>(); - private Set rootNodes; - private Set tailNodes; - /** - * map a heap to its corresponding node in the OAG - */ - private final Map heap2node = new HashMap<>(); - - public OAG(PTA prePta) { - this.pta = prePta; - } - - public void build() { - buildOAG(); - computeRootNodes(); - computeTailNodes(); - } - - public Set allNodes() { - return nodes; - } - - public Set rootNodes() { - return rootNodes; - } - - public Set tailNodes() { - return tailNodes; - } - - public Set getPredsOf(Node n) { - return n.getPreds(); - } - - public Set getSuccsOf(Node n) { - return n.getSuccs(); - } - - public int getInDegreeOf(Node n) { - return n.getInDegree(); - } - - public int getOutDegreeOf(Node n) { - return n.getOutDegree(); - } - - /** - * @param source - * @param dest - * @return whether there is a path from source to target in the OAG - */ - public boolean reaches(Node source, Node dest) { - Set reachableNodes = source.getReachableNodes(); - if (reachableNodes == null) { - reachableNodes = computeReachableNodes(source); - source.setReachableNodes(reachableNodes); - } - return reachableNodes.contains(dest); - } - - /** - * @param source - * @return the nodes in OAG which can be reached from source - */ - public Set computeReachableNodes(Node source) { - Set reachableNodes = new HashSet<>(); - Stack stack = new Stack<>(); - stack.push(source); - while (!stack.isEmpty()) { - Node node = stack.pop(); - if (reachableNodes.add(node)) { - stack.addAll(node.getSuccs()); - } - } - return reachableNodes; - } - - protected void buildOAG() { - Map> pts = PTAUtils.calcStaticThisPTS(this.pta); - for (SootMethod method : this.pta.getNakedReachableMethods()) { - if (method.isPhantom()) { - continue; - } - MethodPAG srcmpag = pta.getPag().getMethodPAG(method); - MethodNodeFactory srcnf = srcmpag.nodeFactory(); - LocalVarNode thisRef = (LocalVarNode) srcnf.caseThis(); - QueueReader reader = srcmpag.getInternalReader().clone(); - while (reader.hasNext()) { - qilin.core.pag.Node from = reader.next(), to = reader.next(); - if (from instanceof AllocNode) { - Node tgt = addNode((AllocNode) from); - if (PTAUtils.isFakeMainMethod(method)) { - // special treatment for fake main - Node src = addNode(pta.getRootNode()); - addEdge(src, tgt); - } else if (method.isStatic()) { - pts.getOrDefault(thisRef, Collections.emptySet()).forEach(a -> { - Node src = addNode(a); - addEdge(src, tgt); - }); - } else { - PointsToSetInternal thisPts = PTAUtils.fetchInsensitivePointsToResult(pta, thisRef); - thisPts.forall(new P2SetVisitor() { - @Override - public void visit(qilin.core.pag.Node n) { - AllocNode a = (AllocNode) n; - Node src = addNode(a); - addEdge(src, tgt); - } - }); - } - } - } - } - } - - /** - * Add a node which represents the given heap object to the OAG. If the - * corresponding node already exists, then return the existing node. - * - * @param heap the one represented by the added node in the graph - * @return the new added node - */ - private Node addNode(AllocNode heap) { - Node node = heap2node.get(heap); - if (node == null) { - node = new Node(heap); - heap2node.put(heap, node); - nodes.add(node); - } - return node; - } - - /** - * Add a directed object allocation edge to the OAG. - */ - protected void addEdge(Node src, Node tgt) { - src.addSucc(tgt); - } - - private void computeRootNodes() { - rootNodes = nodes.stream().filter(node -> node.getInDegree() == 0).collect(Collectors.toSet()); - } - - private void computeTailNodes() { - tailNodes = nodes.stream().filter(node -> node.getOutDegree() == 0).collect(Collectors.toSet()); - } - - /* - * utilities - * */ - public void dumpToDot() { - Vector vec = constructOAGDotString(); - for (int idx = 0; idx < vec.size(); ++idx) { - String fileName = "a" + idx + ".dot"; - try { - FileOutputStream fos = new FileOutputStream(fileName); - PrintStream ps = new PrintStream(fos); - ps.println(vec.get(idx)); - ps.close(); - fos.close(); - } catch (IOException e) { - e.printStackTrace(); - } - } - } - - private int index = 0; - private final Map node2Id = new HashMap<>(); - - private int getNodeID(Object node) { - if (node2Id.containsKey(node)) { - return node2Id.get(node); - } else { - node2Id.put(node, index++); - return node2Id.get(node); - } - } - - private Vector constructOAGDotString() { - Vector vector = new Vector<>(); - rootNodes().stream().filter(x -> !tailNodes().contains(x)).forEach(root -> { - Set reachs = computeReachableNodes(root); - StringBuilder dumpString = new StringBuilder(); - dumpString.append("digraph G {\n"); - for (Node x : reachs) { - x.getSuccs().forEach(y -> { - dumpString.append(addEdgeString(x, y)); - }); - } - dumpString.append("}"); - vector.add(dumpString.toString()); - }); - node2Id.forEach((k, v) -> { - System.out.println("\tID " + v + ":" + ((Node) k).getHeap().toString()); - }); - return vector; - } - - private String addEdgeString(Node x, Node y) { - return "\t" + getNodeID(x) + " -> " + getNodeID(y) + ";\n"; - } -} diff --git a/qilin.pta/src/qilin/pta/toolkits/common/FieldPointstoGraph.java b/qilin.pta/src/qilin/pta/toolkits/common/FieldPointstoGraph.java new file mode 100644 index 0000000..9c22ba8 --- /dev/null +++ b/qilin.pta/src/qilin/pta/toolkits/common/FieldPointstoGraph.java @@ -0,0 +1,107 @@ +/* Qilin - a Java Pointer Analysis Framework + * Copyright (C) 2021-2030 Qilin developers + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3.0 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * . + */ + +package qilin.pta.toolkits.common; + +import qilin.core.PTA; +import qilin.core.pag.AllocNode; +import qilin.core.pag.Node; +import qilin.core.pag.PAG; +import qilin.core.pag.SparkField; +import qilin.core.sets.P2SetVisitor; + +import java.util.*; + +public class FieldPointstoGraph { + private final Map>> pointsTo = new HashMap<>(); + private final Map>> pointedBy = new HashMap<>(); + + public FieldPointstoGraph(PTA pta) { + buildFPG(pta.getPag()); + } + + private void buildFPG(PAG pag) { + pag.getAllocNodes().forEach(this::insertObj); + pag.getContextFields().forEach(contextField -> { + AllocNode base = contextField.getBase(); + if (base.getMethod() == null) { + return; + } + SparkField field = contextField.getField(); + contextField.getP2Set().mapToCIPointsToSet().forall(new P2SetVisitor() { + @Override + public void visit(Node n) { + insertFPT(base, field, (AllocNode) n); + } + }); + }); + } + + public Set getAllObjs() { + return pointsTo.keySet(); + } + + public Set outFieldsOf(AllocNode baseObj) { + return pointsTo.getOrDefault(baseObj, Collections.emptyMap()).keySet(); + } + + public Set inFieldsOf(AllocNode obj) { + return pointedBy.get(obj).keySet(); + } + + public Set pointsTo(AllocNode baseObj, SparkField field) { + return pointsTo.get(baseObj).get(field); + } + + public Set pointedBy(AllocNode obj, SparkField field) { + return pointedBy.get(obj).get(field); + } + + public boolean hasFieldPointer(AllocNode obj, SparkField field) { + return pointsTo.get(obj).containsKey(field); + } + + + private void insertObj(AllocNode obj) { + pointsTo.computeIfAbsent(obj, k -> new HashMap<>()); + pointedBy.computeIfAbsent(obj, k -> new HashMap<>()); + } + + /** + * Insert field points-to relation. + * + * @param baseObj the base object + * @param field a field of `baseObj' + * @param obj the object pointed by `field' + */ + private void insertFPT(AllocNode baseObj, SparkField field, AllocNode obj) { + insertPointsTo(baseObj, field, obj); + insertPointedBy(baseObj, field, obj); + } + + private void insertPointsTo(AllocNode baseObj, SparkField field, AllocNode obj) { + Map> fpt = pointsTo.computeIfAbsent(baseObj, k -> new HashMap<>()); + fpt.computeIfAbsent(field, k -> new HashSet<>()).add(obj); + } + + private void insertPointedBy(AllocNode baseObj, SparkField field, AllocNode obj) { + Map> fpb = pointedBy.computeIfAbsent(obj, k -> new HashMap<>()); + fpb.computeIfAbsent(field, k -> new HashSet<>()).add(baseObj); + } + +} diff --git a/qilin.pta/src/qilin/pta/toolkits/common/OAG.java b/qilin.pta/src/qilin/pta/toolkits/common/OAG.java new file mode 100644 index 0000000..4d3f13a --- /dev/null +++ b/qilin.pta/src/qilin/pta/toolkits/common/OAG.java @@ -0,0 +1,155 @@ +/* Qilin - a Java Pointer Analysis Framework + * Copyright (C) 2021-2030 Qilin developers + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3.0 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * . + */ + +package qilin.pta.toolkits.common; + +import qilin.core.PTA; +import qilin.core.builder.MethodNodeFactory; +import qilin.core.pag.AllocNode; +import qilin.core.pag.LocalVarNode; +import qilin.core.pag.MethodPAG; +import qilin.core.sets.P2SetVisitor; +import qilin.core.sets.PointsToSetInternal; +import qilin.util.PTAUtils; +import qilin.util.graph.DirectedGraph; +import soot.SootMethod; +import soot.util.queue.QueueReader; + +import java.util.*; + +/** + * Implementation of Object Allocation Graph (OAG). + */ +public class OAG implements DirectedGraph { + protected final PTA pta; + protected final Map> successors; + protected final Map> predecessors; + private final Set nodes = new HashSet<>(); + private Collection rootNodes; + private Collection tailNodes; + + public OAG(PTA prePta) { + this.pta = prePta; + this.predecessors = new HashMap<>(); + this.successors = new HashMap<>(); + } + + public void build() { + buildOAG(); + rootNodes = computeRootNodes(); + tailNodes = computeTailNodes(); + } + + @Override + public Collection allNodes() { + return nodes; + } + + @Override + public Collection predsOf(AllocNode p) { + return getPredsOf(p); + } + + @Override + public Collection succsOf(AllocNode p) { + return getSuccsOf(p); + } + + public Collection rootNodes() { + return rootNodes; + } + + public Collection tailNodes() { + return tailNodes; + } + + public Set getPredsOf(AllocNode n) { + return predecessors.getOrDefault(n, Collections.emptySet()); + } + + public Set getSuccsOf(AllocNode n) { + return successors.getOrDefault(n, Collections.emptySet()); + } + + public int getInDegreeOf(AllocNode n) { + return getPredsOf(n).size(); + } + + /** + * @param source + * @param dest + * @return whether there is a path from source to target in the OAG + */ + Map> reachableMap = new HashMap<>(); + + public boolean reaches(AllocNode source, AllocNode dest) { + Collection reachableNodes = reachableMap.get(source); + if (reachableNodes == null) { + reachableNodes = computeReachableNodes(source); + reachableMap.put(source, reachableNodes); + } + return reachableNodes.contains(dest); + } + + protected void buildOAG() { + Map> pts = PTAUtils.calcStaticThisPTS(this.pta); + for (SootMethod method : this.pta.getNakedReachableMethods()) { + if (method.isPhantom()) { + continue; + } + MethodPAG srcmpag = pta.getPag().getMethodPAG(method); + MethodNodeFactory srcnf = srcmpag.nodeFactory(); + LocalVarNode thisRef = (LocalVarNode) srcnf.caseThis(); + QueueReader reader = srcmpag.getInternalReader().clone(); + while (reader.hasNext()) { + qilin.core.pag.Node from = reader.next(), to = reader.next(); + if (from instanceof AllocNode tgt) { + if (PTAUtils.isFakeMainMethod(method)) { + // special treatment for fake main + AllocNode src = pta.getRootNode(); + addEdge(src, tgt); + } else if (method.isStatic()) { + pts.getOrDefault(thisRef, Collections.emptySet()).forEach(src -> { + addEdge(src, tgt); + }); + } else { + PointsToSetInternal thisPts = PTAUtils.fetchInsensitivePointsToResult(pta, thisRef); + thisPts.forall(new P2SetVisitor() { + @Override + public void visit(qilin.core.pag.Node n) { + AllocNode src = (AllocNode) n; + addEdge(src, tgt); + } + }); + } + } + } + } + } + + /** + * Add a directed object allocation edge to the OAG. + */ + protected void addEdge(AllocNode src, AllocNode tgt) { + nodes.add(src); + nodes.add(tgt); + this.predecessors.computeIfAbsent(tgt, k -> new HashSet<>()).add(src); + this.successors.computeIfAbsent(src, k -> new HashSet<>()).add(tgt); + } + +} diff --git a/qilin.pta/src/qilin/pta/toolkits/common/ToolUtil.java b/qilin.pta/src/qilin/pta/toolkits/common/ToolUtil.java new file mode 100644 index 0000000..9feee73 --- /dev/null +++ b/qilin.pta/src/qilin/pta/toolkits/common/ToolUtil.java @@ -0,0 +1,98 @@ +/* Qilin - a Java Pointer Analysis Framework + * Copyright (C) 2021-2030 Qilin developers + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3.0 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * . + */ + +package qilin.pta.toolkits.common; + +import qilin.core.PTA; +import qilin.core.PTAScene; +import qilin.core.builder.MethodNodeFactory; +import qilin.core.pag.AllocNode; +import qilin.core.pag.Node; +import qilin.core.pag.PAG; +import qilin.core.pag.VarNode; +import qilin.core.sets.P2SetVisitor; +import qilin.core.sets.PointsToSetInternal; +import soot.RefType; +import soot.SootClass; +import soot.SootMethod; + +import java.util.Collections; +import java.util.HashSet; +import java.util.Set; + +public class ToolUtil { + public static VarNode getThis(PAG pag, SootMethod m) { + MethodNodeFactory mthdNF = pag.getMethodPAG(m).nodeFactory(); + return mthdNF.caseThis(); + } + + public static Set getParameters(PAG pag, SootMethod m) { + MethodNodeFactory mthdNF = pag.getMethodPAG(m).nodeFactory(); + Set ret = new HashSet<>(); + for (int i = 0; i < m.getParameterCount(); ++i) { + if (m.getParameterType(i) instanceof RefType) { + qilin.core.pag.VarNode param = mthdNF.caseParm(i); + ret.add(param); + } + } + return ret; + } + + public static Set getRetVars(PAG pag, SootMethod m) { + MethodNodeFactory mthdNF = pag.getMethodPAG(m).nodeFactory(); + if (m.getReturnType() instanceof RefType) { + VarNode ret = mthdNF.caseRet(); + return Collections.singleton(ret); + } + return Collections.emptySet(); + } + + public static Set pointsToSetOf(final PTA pta, final VarNode var) { + Set ret = new HashSet<>(); + PointsToSetInternal pts = (PointsToSetInternal) pta.reachingObjects(var); + pts.mapToCIPointsToSet().forall(new P2SetVisitor() { + @Override + public void visit(Node n) { + ret.add((AllocNode) n); + } + }); + return ret; + } + + public static int pointsToSetSizeOf(final PTA pta, VarNode var) { + return pointsToSetOf(pta, var).size(); + } + + /** + * @param pInner potential inner class + * @param pOuter potential outer class + * @return whether pInner is an inner class of pOuter + */ + public static boolean isInnerType(final RefType pInner, RefType pOuter) { + final String pInnerStr = pInner.toString(); + while (!pInnerStr.startsWith(pOuter.toString() + "$")) { + SootClass sc = pOuter.getSootClass(); + if (sc.hasSuperclass()) { + pOuter = sc.getSuperclass().getType(); + } else { + return false; + } + } + return true; + } +} diff --git a/qilin.pta/src/qilin/pta/toolkits/conch/AbstractConch.java b/qilin.pta/src/qilin/pta/toolkits/conch/AbstractConch.java index 0dd6fc6..3e9d307 100644 --- a/qilin.pta/src/qilin/pta/toolkits/conch/AbstractConch.java +++ b/qilin.pta/src/qilin/pta/toolkits/conch/AbstractConch.java @@ -224,8 +224,8 @@ protected boolean hasLoadOn(AllocNode heap, SparkField field) { protected boolean hasStoreOn(AllocNode heap, SparkField field) { Map>> f2bs = o2nonThisFStores.getOrDefault(heap, Collections.emptyMap()); - Set> loadBases = f2bs.getOrDefault(field, Collections.emptySet()); - if (!loadBases.isEmpty()) { + Set> storeBases = f2bs.getOrDefault(field, Collections.emptySet()); + if (!storeBases.isEmpty()) { return true; } for (SootMethod method : invokedMethods.getOrDefault(heap, Collections.emptySet())) { diff --git a/qilin.pta/src/qilin/pta/toolkits/conch/AbstractPAG.java b/qilin.pta/src/qilin/pta/toolkits/conch/AbstractPAG.java index 036ae3a..a577086 100644 --- a/qilin.pta/src/qilin/pta/toolkits/conch/AbstractPAG.java +++ b/qilin.pta/src/qilin/pta/toolkits/conch/AbstractPAG.java @@ -87,7 +87,9 @@ else if (to instanceof FieldRefNode fr) { } // local-global } else if (from instanceof AllocNode) { - this.addNewEdge((AllocNode) from, (LocalVarNode) to); + if (to instanceof LocalVarNode) { + this.addNewEdge((AllocNode) from, (LocalVarNode) to); + } // GlobalVarNode } else if (from instanceof FieldRefNode fr) { this.addLoadEdge((LocalVarNode) fr.getBase(), (LocalVarNode) to); } // global-local diff --git a/qilin.pta/src/qilin/pta/toolkits/conch/Conch.java b/qilin.pta/src/qilin/pta/toolkits/conch/Conch.java index baa228d..4770a76 100644 --- a/qilin.pta/src/qilin/pta/toolkits/conch/Conch.java +++ b/qilin.pta/src/qilin/pta/toolkits/conch/Conch.java @@ -341,7 +341,7 @@ private void classifyForRemain(Set unknownyet) { boolean existUnknown = false; Set tos = new HashSet<>(); for (SparkField sf : ifs) { - final PointsToSetInternal ret = pta.getSetFactory().newSet(heap.getType(), pta.getPag()); + final PointsToSetInternal ret = pta.getSetFactory().newSet(heap.getType(), pag); ret.add(heap); PointsToSetInternal pts = (PointsToSetInternal) pta.reachingObjectsInternal(ret, sf); Set tmp = new HashSet<>(); diff --git a/qilin.pta/src/qilin/pta/toolkits/eagle/Eagle.java b/qilin.pta/src/qilin/pta/toolkits/eagle/Eagle.java index ca3cddb..dd8be9f 100644 --- a/qilin.pta/src/qilin/pta/toolkits/eagle/Eagle.java +++ b/qilin.pta/src/qilin/pta/toolkits/eagle/Eagle.java @@ -265,7 +265,7 @@ public void buildGraph(PTA prePTA) { if (method.isPhantom()) { continue; } - MethodPAG srcmpag = prePTA.getPag().getMethodPAG(method); + MethodPAG srcmpag = prePAG.getMethodPAG(method); MethodNodeFactory srcnf = srcmpag.nodeFactory(); LocalVarNode thisRef = (LocalVarNode) srcnf.caseThis(); // add local edges @@ -284,7 +284,9 @@ else if (to instanceof FieldRefNode fr) { } // local-global } else if (from instanceof AllocNode) { - this.addNewEdge((AllocNode) from, (LocalVarNode) to); + if (to instanceof LocalVarNode) { + this.addNewEdge((AllocNode) from, (LocalVarNode) to); + } // GlobalVarNode } else if (from instanceof FieldRefNode fr) { this.addLoadEdge((LocalVarNode) fr.getBase(), (LocalVarNode) to); } // global-local @@ -304,7 +306,7 @@ else if (to instanceof FieldRefNode fr) { } } LocalVarNode mret = method.getReturnType() instanceof RefLikeType ? (LocalVarNode) srcnf.caseRet() : null; - LocalVarNode throwFinal = prePTA.getPag().findLocalVarNode(new Parm(method, PointsToAnalysis.THROW_NODE)); + LocalVarNode throwFinal = prePAG.findLocalVarNode(new Parm(method, PointsToAnalysis.THROW_NODE)); if (method.isStatic()) { pts.getOrDefault(thisRef, Collections.emptySet()).forEach(a -> { addParamEdges(a, thisRef, parms, mret, throwFinal); diff --git a/qilin.pta/src/qilin/pta/toolkits/mahjong/HeapAbstraction.java b/qilin.pta/src/qilin/pta/toolkits/mahjong/HeapAbstraction.java index 7f3f42c..4c0bfa3 100644 --- a/qilin.pta/src/qilin/pta/toolkits/mahjong/HeapAbstraction.java +++ b/qilin.pta/src/qilin/pta/toolkits/mahjong/HeapAbstraction.java @@ -6,7 +6,7 @@ import qilin.pta.toolkits.mahjong.automata.DFAEquivalenceChecker; import qilin.pta.toolkits.mahjong.automata.DFAFactory; import qilin.pta.toolkits.mahjong.automata.DFAState; -import qilin.pta.toolkits.mahjong.fpg.FieldPointstoGraph; +import qilin.pta.toolkits.common.FieldPointstoGraph; import qilin.util.UnionFindSet; import soot.Type; diff --git a/qilin.pta/src/qilin/pta/toolkits/mahjong/Mahjong.java b/qilin.pta/src/qilin/pta/toolkits/mahjong/Mahjong.java index ab2c388..4e1eedb 100644 --- a/qilin.pta/src/qilin/pta/toolkits/mahjong/Mahjong.java +++ b/qilin.pta/src/qilin/pta/toolkits/mahjong/Mahjong.java @@ -2,9 +2,7 @@ import qilin.core.PTA; import qilin.core.pag.AllocNode; -import qilin.pta.toolkits.mahjong.fpg.FieldPointstoGraph; -import qilin.pta.toolkits.mahjong.pta.PTAProvider; -import qilin.pta.toolkits.mahjong.pta.PTAProviderImpl; +import qilin.pta.toolkits.common.FieldPointstoGraph; import qilin.util.Stopwatch; import java.util.Collection; @@ -15,7 +13,6 @@ * @author Yue Li */ public class Mahjong { - public static void run(PTA pta, Map heapModelMap) { FieldPointstoGraph fpg = buildFPG(pta); System.out.print("Creating heap abstraction ... "); @@ -33,8 +30,7 @@ public static void run(PTA pta, Map heapModelMap) { public static FieldPointstoGraph buildFPG(PTA pta) { System.out.print("Building FPG (Field Points-to Graph) ... "); Stopwatch fpgTimer = Stopwatch.newAndStart("FPG Construction"); - PTAProvider fptProvider = new PTAProviderImpl(pta); - FieldPointstoGraph fpg = new FieldPointstoGraph(fptProvider); + FieldPointstoGraph fpg = new FieldPointstoGraph(pta); fpgTimer.stop(); System.out.println(fpgTimer); return fpg; diff --git a/qilin.pta/src/qilin/pta/toolkits/mahjong/automata/DFAFactory.java b/qilin.pta/src/qilin/pta/toolkits/mahjong/automata/DFAFactory.java index fc80e2e..aa40d1b 100644 --- a/qilin.pta/src/qilin/pta/toolkits/mahjong/automata/DFAFactory.java +++ b/qilin.pta/src/qilin/pta/toolkits/mahjong/automata/DFAFactory.java @@ -2,7 +2,7 @@ import qilin.core.pag.AllocNode; import qilin.core.pag.SparkField; -import qilin.pta.toolkits.mahjong.fpg.FieldPointstoGraph; +import qilin.pta.toolkits.common.FieldPointstoGraph; import soot.Type; import java.util.*; diff --git a/qilin.pta/src/qilin/pta/toolkits/mahjong/automata/NFA.java b/qilin.pta/src/qilin/pta/toolkits/mahjong/automata/NFA.java index 1ffe2be..cbc4858 100644 --- a/qilin.pta/src/qilin/pta/toolkits/mahjong/automata/NFA.java +++ b/qilin.pta/src/qilin/pta/toolkits/mahjong/automata/NFA.java @@ -2,7 +2,7 @@ import qilin.core.pag.AllocNode; import qilin.core.pag.SparkField; -import qilin.pta.toolkits.mahjong.fpg.FieldPointstoGraph; +import qilin.pta.toolkits.common.FieldPointstoGraph; import soot.Type; import java.util.Collections; diff --git a/qilin.pta/src/qilin/pta/toolkits/mahjong/pta/PTAProviderImpl.java b/qilin.pta/src/qilin/pta/toolkits/mahjong/pta/PTAProviderImpl.java deleted file mode 100644 index 5d23590..0000000 --- a/qilin.pta/src/qilin/pta/toolkits/mahjong/pta/PTAProviderImpl.java +++ /dev/null @@ -1,138 +0,0 @@ -package qilin.pta.toolkits.mahjong.pta; - -import qilin.core.PTA; -import qilin.core.builder.MethodNodeFactory; -import qilin.core.pag.AllocNode; -import qilin.core.pag.Node; -import qilin.core.pag.SparkField; -import qilin.core.pag.VarNode; -import qilin.core.sets.P2SetVisitor; -import qilin.core.sets.PointsToSetInternal; -import qilin.util.Triple; -import soot.SootMethod; -import soot.Type; - -import java.util.*; - -public class PTAProviderImpl implements PTAProvider { - - private final PTA prePTA; - - private Map> obj2invokedMethods; - private Map> typeObjects; - - public PTAProviderImpl(PTA prePTA) { - this.prePTA = prePTA; - } - - @Override - public Iterator objIterator() { - return new ObjIterator(); - } - - @Override - public Iterator> fptIterator() { - return new FPTIterator(); - } - - private class ObjIterator implements Iterator { - - private final Iterator objIter; - - private ObjIterator() { - objIter = prePTA.getPag().getAllocNodes().iterator(); - } - - @Override - public boolean hasNext() { - return objIter.hasNext(); - } - - @Override - public AllocNode next() { - if (hasNext()) { - return objIter.next(); - } else { - throw new NoSuchElementException(); - } - } - - } - - private class FPTIterator implements Iterator> { - - private final Iterator> fptIter; - - private FPTIterator() { - List> list = new ArrayList<>(); - prePTA.getPag().getContextFields().forEach(contextField -> { - AllocNode base = contextField.getBase(); - if (base.getMethod() == null) { - return; - } - SparkField field = contextField.getField(); - contextField.getP2Set().mapToCIPointsToSet().forall(new P2SetVisitor() { - @Override - public void visit(Node n) { - list.add(new Triple<>(base, field, (AllocNode) n)); - } - }); - }); - fptIter = list.iterator(); - } - - @Override - public boolean hasNext() { - return fptIter.hasNext(); - } - - @Override - public Triple next() { - if (fptIter.hasNext()) { - Triple fpt = fptIter.next(); - return new Triple<>(fpt.getFirst(), fpt.getSecond(), fpt.getThird()); - } else { - throw new NoSuchElementException(); - } - } - } - - public Set pointsToSetOf(final VarNode var) { - Set ret = new HashSet<>(); - PointsToSetInternal pts = (PointsToSetInternal) prePTA.reachingObjects(var); - pts.mapToCIPointsToSet().forall(new P2SetVisitor() { - @Override - public void visit(Node n) { - ret.add((AllocNode) n); - } - }); - return ret; - } - - @Override - public Set invokedMethodsOn(AllocNode heap) { - if (obj2invokedMethods == null) { - this.obj2invokedMethods = new HashMap<>(); - prePTA.getNakedReachableMethods().stream().filter(m -> !m.isStatic()).forEach(instMtd -> { - MethodNodeFactory mthdNF = prePTA.getPag().getMethodPAG(instMtd).nodeFactory(); - VarNode thisVar = mthdNF.caseThis(); - pointsToSetOf(thisVar).forEach(obj -> { - obj2invokedMethods.computeIfAbsent(obj, k -> new HashSet<>()).add(instMtd); - }); - }); - } - return obj2invokedMethods.getOrDefault(heap, Collections.emptySet()); - } - - @Override - public Set objectsOfType(final Type type) { - if (typeObjects == null) { - typeObjects = new HashMap<>(); - objIterator().forEachRemaining(obj -> { - this.typeObjects.computeIfAbsent(obj.getType(), k -> new HashSet<>()).add(obj); - }); - } - return this.typeObjects.getOrDefault(type, Collections.emptySet()); - } - -} diff --git a/qilin.pta/src/qilin/pta/toolkits/selectx/L.java b/qilin.pta/src/qilin/pta/toolkits/selectx/L.java index 36a483d..4998e58 100644 --- a/qilin.pta/src/qilin/pta/toolkits/selectx/L.java +++ b/qilin.pta/src/qilin/pta/toolkits/selectx/L.java @@ -99,8 +99,8 @@ public Stream forwardTargets() { } /* - * inverse operation: inv(l-) = l+ and inv(l+) = l-. - * */ + * inverse operation: inv(l-) = l+ and inv(l+) = l-. + * */ public L inv() { return L.v((LocalVarNode) sparkNode, !positive); } diff --git a/qilin.pta/src/qilin/pta/toolkits/selectx/Selectx.java b/qilin.pta/src/qilin/pta/toolkits/selectx/Selectx.java index 1017ec8..9b041cc 100644 --- a/qilin.pta/src/qilin/pta/toolkits/selectx/Selectx.java +++ b/qilin.pta/src/qilin/pta/toolkits/selectx/Selectx.java @@ -241,7 +241,9 @@ private void buildGraph() { this.addStaticStoreEdge((LocalVarNode) from, (GlobalVarNode) to); } } else if (from instanceof AllocNode) { - this.addNewEdge((AllocNode) from, (LocalVarNode) to); + if (to instanceof LocalVarNode) { + this.addNewEdge((AllocNode) from, (LocalVarNode) to); + } // GlobalVarNode } else if (from instanceof FieldRefNode fr) { // load edge is treated as assign. this.addAssignEdge((LocalVarNode) fr.getBase(), (LocalVarNode) to); @@ -303,7 +305,7 @@ private void buildGraph() { this.addExitEdge(ret, retDest, callSite); } LocalVarNode stmtThrowNode = prePAG.makeInvokeStmtThrowVarNode(s, method); - LocalVarNode throwFinal = prePTA.getPag().findLocalVarNode(new Parm(tgtmtd, PointsToAnalysis.THROW_NODE)); + LocalVarNode throwFinal = prePAG.findLocalVarNode(new Parm(tgtmtd, PointsToAnalysis.THROW_NODE)); if (throwFinal != null) { this.addExitEdge(throwFinal, stmtThrowNode, callSite); } diff --git a/qilin.pta/src/qilin/pta/toolkits/turner/AbstractMVFG.java b/qilin.pta/src/qilin/pta/toolkits/turner/AbstractMVFG.java index d9a6a03..6795dc4 100644 --- a/qilin.pta/src/qilin/pta/toolkits/turner/AbstractMVFG.java +++ b/qilin.pta/src/qilin/pta/toolkits/turner/AbstractMVFG.java @@ -115,9 +115,8 @@ protected void addLoadEdge(LocalVarNode base, LocalVarNode to) { } protected void buildVFG() { - PAG prePAG = prePTA.getPag(); CallGraph callGraph = prePTA.getCallGraph(); - MethodPAG srcmpag = prePAG.getMethodPAG(method); + MethodPAG srcmpag = prePTA.getPag().getMethodPAG(method); MethodNodeFactory srcnf = srcmpag.nodeFactory(); LocalVarNode thisRef = (LocalVarNode) srcnf.caseThis(); QueueReader reader = srcmpag.getInternalReader().clone(); @@ -131,10 +130,12 @@ else if (to instanceof FieldRefNode fr) { } // local-global } else if (from instanceof AllocNode) { - this.addNewEdge((AllocNode) from, (LocalVarNode) to); - if (hg.isCSLikely((AllocNode) from)) { - this.addCSLikelyEdge((AllocNode) from); - } + if (to instanceof LocalVarNode) { + this.addNewEdge((AllocNode) from, (LocalVarNode) to); + if (hg.isCSLikely((AllocNode) from)) { + this.addCSLikelyEdge((AllocNode) from); + } + } // GlobalVarNode } else if (from instanceof FieldRefNode fr) { this.addLoadEdge((LocalVarNode) fr.getBase(), (LocalVarNode) to); } // global-local @@ -163,12 +164,12 @@ else if (to instanceof FieldRefNode fr) { if (s instanceof AssignStmt) { Value dest = ((AssignStmt) s).getLeftOp(); if (dest.getType() instanceof RefLikeType) { - retDest = prePAG.findLocalVarNode(dest); + retDest = prePTA.getPag().findLocalVarNode(dest); } } LocalVarNode receiver; if (ie instanceof InstanceInvokeExpr iie) { - receiver = prePAG.findLocalVarNode(iie.getBase()); + receiver = prePTA.getPag().findLocalVarNode(iie.getBase()); } else { // static call receiver = thisRef; @@ -184,7 +185,7 @@ else if (to instanceof FieldRefNode fr) { for (int i = 0; i < numArgs; i++) { if (args[i] == null) continue; - ValNode argNode = prePAG.findValNode(args[i]); + ValNode argNode = prePTA.getPag().findValNode(args[i]); if (argNode instanceof LocalVarNode && satisfyAddingStoreCondition(i, targets)) { this.addStoreEdge((LocalVarNode) argNode, receiver); } @@ -195,7 +196,7 @@ else if (to instanceof FieldRefNode fr) { } } if (statisfyAddingLoadCondition(targets)) { - LocalVarNode stmtThrowNode = prePAG.makeInvokeStmtThrowVarNode(s, method); + LocalVarNode stmtThrowNode = prePTA.getPag().makeInvokeStmtThrowVarNode(s, method); this.addLoadEdge(receiver, stmtThrowNode); } if (satisfyAddingStoreCondition(PointsToAnalysis.THIS_NODE, targets)) { diff --git a/qilin.pta/src/qilin/pta/toolkits/zipper/Main.java b/qilin.pta/src/qilin/pta/toolkits/zipper/Main.java index 3f01bc0..60e16a6 100644 --- a/qilin.pta/src/qilin/pta/toolkits/zipper/Main.java +++ b/qilin.pta/src/qilin/pta/toolkits/zipper/Main.java @@ -20,8 +20,6 @@ import qilin.core.PTA; import qilin.pta.toolkits.zipper.analysis.Zipper; -import qilin.pta.toolkits.zipper.pta.PointsToAnalysis; -import qilin.pta.toolkits.zipper.pta.WrapperedPointsToAnalysis; import qilin.util.ANSIColor; import qilin.util.Stopwatch; import soot.SootMethod; @@ -31,16 +29,15 @@ public class Main { - public static void run(PTA prePTA, Set zipperPCMOutput) { + public static void run(PTA pta, Set zipperPCMOutput) { int numThreads = Runtime.getRuntime().availableProcessors(); Global.setThread(numThreads); Global.setExpress(false); String zipperStr = Global.isExpress() ? "Zipper-e" : "Zipper"; - final PointsToAnalysis pta = readPointsToAnalysis(prePTA); System.out.println(ANSIColor.BOLD + ANSIColor.YELLOW + zipperStr + " starts ..." + ANSIColor.RESET); String flows = Global.getFlow() != null ? Global.getFlow() : "Direct+Wrapped+Unwrapped"; System.out.println("Precision loss patterns: " + ANSIColor.BOLD + ANSIColor.GREEN + flows + ANSIColor.RESET); - Zipper.outputNumberOfClasses(pta); + Zipper.outputNumberOfClasses(pta.getPag()); Stopwatch zipperTimer = Stopwatch.newAndStart("Zipper Timer"); Zipper zipper = new Zipper(pta); Set pcm = zipper.analyze(); @@ -55,18 +52,6 @@ public static void run(PTA prePTA, Set zipperPCMOutput) { writeZipperResults(pcm, zipperPCMOutput); } - public static PointsToAnalysis readPointsToAnalysis(PTA prePTA) { - final Stopwatch ptaTimer = Stopwatch.newAndStart("Points-to Analysis Timer"); - System.out.println("Reading points-to analysis results ... "); - final PointsToAnalysis pta = new WrapperedPointsToAnalysis(prePTA); - ptaTimer.stop(); - System.out.print(ANSIColor.BOLD + ANSIColor.YELLOW + "Reading time: " + ANSIColor.RESET); - System.out.print(ANSIColor.BOLD + ANSIColor.GREEN); - System.out.printf("%.2fs", ptaTimer.elapsed()); - System.out.println(ANSIColor.RESET); - return pta; - } - private static void writeZipperResults(final Set results, final Set outputSet) { results.stream().sorted(Comparator.comparing(Object::toString)).forEach(outputSet::add); } diff --git a/qilin.pta/src/qilin/pta/toolkits/zipper/analysis/PotentialContextElement.java b/qilin.pta/src/qilin/pta/toolkits/zipper/analysis/PotentialContextElement.java index c0d2354..6c581cc 100644 --- a/qilin.pta/src/qilin/pta/toolkits/zipper/analysis/PotentialContextElement.java +++ b/qilin.pta/src/qilin/pta/toolkits/zipper/analysis/PotentialContextElement.java @@ -1,8 +1,12 @@ package qilin.pta.toolkits.zipper.analysis; +import qilin.core.PTA; +import qilin.core.builder.MethodNodeFactory; import qilin.core.pag.AllocNode; +import qilin.core.pag.VarNode; +import qilin.pta.toolkits.common.OAG; +import qilin.pta.toolkits.common.ToolUtil; import qilin.pta.toolkits.zipper.Global; -import qilin.pta.toolkits.zipper.pta.PointsToAnalysis; import qilin.util.collect.SetFactory; import qilin.util.graph.MergedNode; import qilin.util.graph.SCCMergedGraph; @@ -11,6 +15,7 @@ import soot.Type; import java.util.*; +import java.util.concurrent.ConcurrentHashMap; /** * For each object o, this class compute the set of methods @@ -20,18 +25,27 @@ * set of objects which could potentially be its context element. */ public class PotentialContextElement { - private final PointsToAnalysis pta; + private final PTA pta; // This map maps each object to the methods invoked on it. // For instance methods, they are the methods whose receiver is the object. // For static methods, they are the methods reachable from instance methods. private Map> invokedMethods; + private Map> obj2invokedMethods; private final Map> typePCEMethods; private final Map> pceOfMap; - PotentialContextElement(final PointsToAnalysis pta, final ObjectAllocationGraph oag) { - this.typePCEMethods = new HashMap<>(); - this.pceOfMap = new HashMap<>(); + private final OAG oag; + private final Map> typeAllocatees; + private final Map> allocateeMap; + + PotentialContextElement(final PTA pta, final OAG oag) { this.pta = pta; + this.oag = oag; + this.typePCEMethods = new ConcurrentHashMap<>(); + this.obj2invokedMethods = new ConcurrentHashMap<>(); + this.pceOfMap = new ConcurrentHashMap<>(); + this.typeAllocatees = new ConcurrentHashMap<>(); + this.allocateeMap = new ConcurrentHashMap<>(); this.init(oag); } @@ -45,31 +59,71 @@ public Set PCEMethodsOf(final AllocNode obj) { */ public Set PCEMethodsOf(final Type type) { if (!this.typePCEMethods.containsKey(type)) { - final Set methods = new HashSet<>(); - this.pta.objectsOfType(type).forEach(obj -> methods.addAll(this.PCEMethodsOf(obj))); + final Set methods = ConcurrentHashMap.newKeySet(); + pta.getPag().getAllocNodes().stream(). + filter(o -> o.getType().equals(type)). + forEach(obj -> methods.addAll(this.PCEMethodsOf(obj))); this.typePCEMethods.put(type, methods); } - return this.typePCEMethods.get(type); + return this.typePCEMethods.getOrDefault(type, Collections.emptySet()); } /** * Compute PCE methods for each objects. */ - private void init(final ObjectAllocationGraph oag) { + private void init(final OAG oag) { final SCCMergedGraph mg = new SCCMergedGraph<>(oag); final TopologicalSorter> topoSorter = new TopologicalSorter<>(); final SetFactory setFactory = new SetFactory<>(); + final SetFactory setFactory2 = new SetFactory<>(); + this.buildMethodsInvokedOnObjects(); this.invokedMethods = new HashMap<>(); topoSorter.sort(mg, true).forEach(node -> { - final Set methods = setFactory.get(this.getPCEMethods(node, mg)); + final Set methods = ConcurrentHashMap.newKeySet(); + methods.addAll(setFactory.get(this.getPCEMethods(node, mg))); + final Set allocatees = ConcurrentHashMap.newKeySet(); + allocatees.addAll(setFactory2.get(this.getAllocatees(node, mg))); node.getContent().forEach(obj -> { pceOfMap.put(obj, methods); + allocateeMap.put(obj, allocatees); }); }); this.invokedMethods = null; if (Global.isDebug()) { this.computePCEObjects(); } + + oag.allNodes().forEach(obj -> { + final Type type = obj.getType(); + this.typeAllocatees.putIfAbsent(type, new HashSet<>()); + this.typeAllocatees.get(type).addAll(this.allocateesOf(obj)); + }); + } + + private Set getAllocatees(final MergedNode node, final SCCMergedGraph mg) { + final Set allocatees = new HashSet<>(); + mg.succsOf(node).forEach(n -> { + // direct allocatees + allocatees.addAll(n.getContent()); + // indirect allocatees, here, it does not require to traverse all heaps in n.getContent() + // because of lines 104-107. + final AllocNode o = n.getContent().iterator().next(); + allocatees.addAll(this.allocateesOf(o)); + }); + final AllocNode obj = node.getContent().iterator().next(); + if (node.getContent().size() > 1 || oag.succsOf(obj).contains(obj)) { + // The merged node is a true SCC + allocatees.addAll(node.getContent()); + } + return allocatees; + } + + private Set allocateesOf(final AllocNode obj) { + return this.allocateeMap.getOrDefault(obj, Collections.emptySet()); + } + + public Set allocateesOf(final Type type) { + return this.typeAllocatees.getOrDefault(type, Collections.emptySet()); } private Set getPCEMethods(final MergedNode node, final SCCMergedGraph mg) { @@ -82,14 +136,34 @@ private Set getPCEMethods(final MergedNode node, final SC return methods; } + public Set methodsInvokedOn(final AllocNode obj) { + return this.obj2invokedMethods.getOrDefault(obj, Collections.emptySet()); + } + + private void buildMethodsInvokedOnObjects() { + this.obj2invokedMethods = new HashMap<>(); + pta.getNakedReachableMethods().stream().filter(m -> !m.isStatic()).forEach(instMtd -> { + MethodNodeFactory mthdNF = pta.getPag().getMethodPAG(instMtd).nodeFactory(); + VarNode thisVar = mthdNF.caseThis(); + ToolUtil.pointsToSetOf(pta, thisVar).forEach(obj -> { + obj2invokedMethods.computeIfAbsent(obj, k -> new HashSet<>()).add(instMtd); + }); + }); + } + private Set invokedMethodsOf(final AllocNode obj) { if (!this.invokedMethods.containsKey(obj)) { final Set methods = new HashSet<>(); - final Queue queue = new LinkedList<>(this.pta.methodsInvokedOn(obj)); + final Queue queue = new LinkedList<>(this.methodsInvokedOn(obj)); while (!queue.isEmpty()) { final SootMethod method = queue.poll(); methods.add(method); - this.pta.calleesOf(method).stream().filter(m -> m.isStatic() && !methods.contains(m)).forEach(queue::offer); + pta.getCallGraph().edgesOutOf(method).forEachRemaining(edge -> { + SootMethod callee = edge.getTgt().method(); + if (callee.isStatic() && !methods.contains(callee)) { + queue.offer(callee); + } + }); } this.invokedMethods.put(obj, methods); } @@ -98,7 +172,7 @@ private Set invokedMethodsOf(final AllocNode obj) { private void computePCEObjects() { final Map> pceObjs = new HashMap<>(); - this.pta.allObjects().forEach(obj -> this.PCEMethodsOf(obj).forEach(method -> { + pta.getPag().getAllocNodes().forEach(obj -> this.PCEMethodsOf(obj).forEach(method -> { if (!pceObjs.containsKey(method)) { pceObjs.put(method, new HashSet<>()); } diff --git a/qilin.pta/src/qilin/pta/toolkits/zipper/analysis/Zipper.java b/qilin.pta/src/qilin/pta/toolkits/zipper/analysis/Zipper.java index 2ae8a3f..ee07553 100644 --- a/qilin.pta/src/qilin/pta/toolkits/zipper/analysis/Zipper.java +++ b/qilin.pta/src/qilin/pta/toolkits/zipper/analysis/Zipper.java @@ -1,15 +1,16 @@ package qilin.pta.toolkits.zipper.analysis; -import qilin.core.pag.AllocNode; +import qilin.core.PTA; +import qilin.core.pag.*; +import qilin.pta.toolkits.common.OAG; +import qilin.pta.toolkits.common.ToolUtil; import qilin.pta.toolkits.zipper.Global; import qilin.pta.toolkits.zipper.flowgraph.FlowAnalysis; -import qilin.pta.toolkits.zipper.flowgraph.InstanceFieldNode; -import qilin.pta.toolkits.zipper.flowgraph.Node; import qilin.pta.toolkits.zipper.flowgraph.ObjectFlowGraph; -import qilin.pta.toolkits.zipper.pta.PointsToAnalysis; import qilin.util.ANSIColor; import qilin.util.Stopwatch; import qilin.util.graph.ConcurrentDirectedGraphImpl; +import soot.RefType; import soot.SootMethod; import soot.Type; @@ -28,31 +29,28 @@ * in the program being analyzed. */ public class Zipper { - private final PointsToAnalysis pta; - private final ObjectAllocationGraph oag; + private final PTA pta; private final PotentialContextElement pce; private final ObjectFlowGraph ofg; - private final InnerClassChecker innerClsChecker; private final AtomicInteger analyzedClasses = new AtomicInteger(0); private final AtomicInteger totalPFGNodes = new AtomicInteger(0); private final AtomicInteger totalPFGEdges = new AtomicInteger(0); - // private final DirectedGraphImpl overallPFG = new DirectedGraphImpl<>(); private final ConcurrentDirectedGraphImpl overallPFG = new ConcurrentDirectedGraphImpl<>(); private final Map methodPts; private final Map> pcmMap = new ConcurrentHashMap<>(1024); - public Zipper(PointsToAnalysis pta) { + public Zipper(PTA pta) { this.pta = pta; - this.oag = new ObjectAllocationGraph(pta); + OAG oag = new OAG(pta); + oag.build(); System.out.println("#OAG:" + oag.allNodes().size()); this.pce = new PotentialContextElement(pta, oag); - this.innerClsChecker = new InnerClassChecker(pta); - this.ofg = buildObjectFlowGraph(pta); + this.ofg = buildObjectFlowGraph(); this.methodPts = getMethodPointsToSize(pta); } - public static void outputNumberOfClasses(PointsToAnalysis pta) { - int nrClasses = (int) pta.allObjects().stream() + public static void outputNumberOfClasses(PAG pag) { + int nrClasses = (int) pag.getAllocNodes().stream() .map(AllocNode::getType) .distinct() .count(); @@ -72,7 +70,7 @@ public int numberOfOverallPFGEdges() { return nrEdges; } - public static ObjectFlowGraph buildObjectFlowGraph(PointsToAnalysis pta) { + public ObjectFlowGraph buildObjectFlowGraph() { Stopwatch ofgTimer = Stopwatch.newAndStart("Object Flow Graph Timer"); System.out.println("Building OFG (Object Flow Graph) ... "); ObjectFlowGraph ofg = new ObjectFlowGraph(pta); @@ -100,10 +98,12 @@ public static void outputObjectFlowGraphSize(ObjectFlowGraph ofg) { public Set analyze() { reset(); System.out.println("Building PFGs (Pollution Flow Graphs) and computing precision-critical methods ..."); - List types = pta.allObjects().stream() + List types = pta.getPag().getAllocNodes().stream() .map(AllocNode::getType) .distinct() .sorted(Comparator.comparing(Type::toString)) + .filter(t -> t instanceof RefType) + .map(t -> (RefType) t) .collect(Collectors.toList()); if (Global.getThread() == Global.UNDEFINE) { computePCM(types); @@ -126,16 +126,16 @@ public Set analyze() { return pcm; } - private void computePCM(List types) { - FlowAnalysis fa = new FlowAnalysis(pta, oag, pce, ofg); + private void computePCM(List types) { + FlowAnalysis fa = new FlowAnalysis(pta, pce, ofg); types.forEach(type -> analyze(type, fa)); } - private void computePCMConcurrent(List types, int nThread) { + private void computePCMConcurrent(List types, int nThread) { ExecutorService executorService = Executors.newFixedThreadPool(nThread); types.forEach(type -> executorService.execute(() -> { - FlowAnalysis fa = new FlowAnalysis(pta, oag, pce, ofg); + FlowAnalysis fa = new FlowAnalysis(pta, pce, ofg); analyze(type, fa); })); executorService.shutdown(); @@ -151,43 +151,44 @@ private void computePCMConcurrent(List types, int nThread) { * @param fa Compute the set of precision-critical methods for a class/type and add these methods * to the pcm collection. */ - private void analyze(Type type, FlowAnalysis fa) { + private void analyze(RefType type, FlowAnalysis fa) { if (Global.isDebug()) { System.out.println("----------------------------------------"); } // System.out.println(color(YELLOW, "Zipper: analyzing ") + type); // Obtain all methods of type (including inherited methods) - Set ms = pta.objectsOfType(type).stream() - .map(pta::methodsInvokedOn) + Set ms = pta.getPag().getAllocNodes().stream(). + filter(o -> o.getType().equals(type)) + .map(pce::methodsInvokedOn) .flatMap(Collection::stream) .collect(Collectors.toSet()); // Obtain IN methods Set inms = ms.stream() .filter(m -> !m.isPrivate()) - .filter(m -> pta.getParameters(m).stream() - .anyMatch(p -> !pta.pointsToSetOf(p).isEmpty())) + .filter(m -> ToolUtil.getParameters(pta.getPag(), m).stream() + .anyMatch(p -> !ToolUtil.pointsToSetOf(pta, p).isEmpty())) .collect(Collectors.toSet()); // Obtain OUT methods Set outms = new HashSet<>(); ms.stream() .filter(m -> !m.isPrivate()) - .filter(m -> pta.getRetVars(m).stream() - .anyMatch(r -> !pta.pointsToSetOf(r).isEmpty())) + .filter(m -> ToolUtil.getRetVars(pta.getPag(), m).stream() + .anyMatch(r -> !ToolUtil.pointsToSetOf(pta, r).isEmpty())) .forEach(outms::add); // OUT methods of inner classes and special access$ methods // are also considered as the OUT methods of current type pce.PCEMethodsOf(type).stream() .filter(m -> !m.isPrivate() && !m.isStatic()) - .filter(m -> innerClsChecker.isInnerClass( - pta.declaringTypeOf(m), type)) + .filter(m -> ToolUtil.isInnerType( + m.getDeclaringClass().getType(), type)) .forEach(outms::add); pce.PCEMethodsOf(type).stream() .filter(m -> !m.isPrivate() && !m.isStatic()) - .filter(m -> pta.declaringTypeOf(m).equals(type) + .filter(m -> m.getDeclaringClass().getType().equals(type) && m.toString().contains("access$")) .forEach(outms::add); @@ -216,15 +217,6 @@ private void analyze(Type type, FlowAnalysis fa) { fa.clear(); } -// private void mergeSinglePFG(DirectedGraphImpl pfg) { -// for (Node node : pfg.allNodes()) { -// this.overallPFG.addNode(node); -// for (Node succ : pfg.succsOf(node)) { -// this.overallPFG.addEdge(node, succ); -// } -// } -// } - private void mergeSinglePFG(ConcurrentDirectedGraphImpl pfg) { for (Node node : pfg.allNodes()) { this.overallPFG.addNode(node); @@ -258,23 +250,30 @@ private Set collectAllPrecisionCriticalMethods( private int computePCMThreshold() { // Use points-to size of whole program as denominator - return (int) (Global.getExpressThreshold() * - pta.totalPointsToSetSize()); + int totalPTSSize = 0; + for (ValNode var : pta.getPag().getValNodeNumberer()) { + if (var instanceof VarNode varNode) { + Collection pts = ToolUtil.pointsToSetOf(pta, varNode); + totalPTSSize += pts.size(); + } + } + return (int) (Global.getExpressThreshold() * totalPTSSize); } private Set getPrecisionCriticalMethods(Type type, Set nodes) { return nodes.stream() .map(this::node2ContainingMethod) + .filter(Objects::nonNull) .filter(pce.PCEMethodsOf(type)::contains) .collect(Collectors.toSet()); } private SootMethod node2ContainingMethod(Node node) { - if (node instanceof qilin.pta.toolkits.zipper.flowgraph.VarNode varNode) { - return pta.declaringMethodOf(varNode.getVar()); + if (node instanceof LocalVarNode lvn) { + return lvn.getMethod(); } else { - InstanceFieldNode ifNode = (InstanceFieldNode) node; - return pta.containingMethodOf(ifNode.getBase()); + ContextField ctxField = (ContextField) node; + return ctxField.getBase().getMethod(); } } @@ -285,15 +284,21 @@ private void reset() { pcmMap.clear(); } - private Map getMethodPointsToSize(PointsToAnalysis pta) { + private Map getMethodPointsToSize(PTA prePTA) { Map results = new HashMap<>(); - pta.reachableMethods().forEach(m -> - results.put(m, - pta.variablesDeclaredIn(m) - .stream() - .mapToInt(pta::pointsToSetSizeOf) - .sum()) - ); + for (ValNode valnode : prePTA.getPag().getValNodeNumberer()) { + if (!(valnode instanceof LocalVarNode lvn)) { + continue; + } + SootMethod inMethod = lvn.getMethod(); + int ptSize = ToolUtil.pointsToSetSizeOf(prePTA, lvn); + if (results.containsKey(inMethod)) { + int oldValue = results.get(inMethod); + results.replace(inMethod, oldValue, oldValue + ptSize); + } else { + results.put(inMethod, ptSize); + } + } return results; } diff --git a/qilin.pta/src/qilin/pta/toolkits/zipper/flowgraph/Edge.java b/qilin.pta/src/qilin/pta/toolkits/zipper/flowgraph/Edge.java index f557328..4bad078 100644 --- a/qilin.pta/src/qilin/pta/toolkits/zipper/flowgraph/Edge.java +++ b/qilin.pta/src/qilin/pta/toolkits/zipper/flowgraph/Edge.java @@ -1,5 +1,7 @@ package qilin.pta.toolkits.zipper.flowgraph; +import qilin.core.pag.Node; + import java.util.Objects; public class Edge { diff --git a/qilin.pta/src/qilin/pta/toolkits/zipper/flowgraph/FlowAnalysis.java b/qilin.pta/src/qilin/pta/toolkits/zipper/flowgraph/FlowAnalysis.java index 7830678..f37dd0e 100644 --- a/qilin.pta/src/qilin/pta/toolkits/zipper/flowgraph/FlowAnalysis.java +++ b/qilin.pta/src/qilin/pta/toolkits/zipper/flowgraph/FlowAnalysis.java @@ -1,15 +1,21 @@ package qilin.pta.toolkits.zipper.flowgraph; -import qilin.core.pag.AllocNode; +import qilin.core.PTA; +import qilin.core.pag.*; +import qilin.pta.toolkits.common.ToolUtil; import qilin.pta.toolkits.zipper.Global; -import qilin.pta.toolkits.zipper.analysis.ObjectAllocationGraph; import qilin.pta.toolkits.zipper.analysis.PotentialContextElement; -import qilin.pta.toolkits.zipper.pta.PointsToAnalysis; import qilin.util.ANSIColor; import qilin.util.graph.ConcurrentDirectedGraphImpl; import qilin.util.graph.Reachability; +import soot.RefType; import soot.SootMethod; import soot.Type; +import soot.Value; +import soot.jimple.AssignStmt; +import soot.jimple.InstanceInvokeExpr; +import soot.jimple.InvokeExpr; +import soot.jimple.Stmt; import java.util.*; import java.util.stream.Collectors; @@ -17,26 +23,22 @@ import static qilin.util.ANSIColor.color; public class FlowAnalysis { - private final PointsToAnalysis pta; - private final ObjectAllocationGraph oag; + private final PTA pta; private final PotentialContextElement pce; private final ObjectFlowGraph objectFlowGraph; private Type currentType; - private Set inVars; + private Set inVars; private Set outNodes; private Set visitedNodes; private Map> wuEdges; - // private DirectedGraphImpl pollutionFlowGraph; private ConcurrentDirectedGraphImpl pollutionFlowGraph; private Reachability reachability; - public FlowAnalysis(PointsToAnalysis pta, - ObjectAllocationGraph oag, + public FlowAnalysis(PTA pta, PotentialContextElement pce, ObjectFlowGraph ofg) { this.pta = pta; - this.oag = oag; this.pce = pce; this.objectFlowGraph = ofg; } @@ -44,27 +46,24 @@ public FlowAnalysis(PointsToAnalysis pta, public void initialize(Type type, Set inms, Set outms) { currentType = type; inVars = inms.stream() - .map(pta::getParameters) + .map(m -> ToolUtil.getParameters(pta.getPag(), m)) .flatMap(Collection::stream) .collect(Collectors.toSet()); outNodes = outms.stream() - .map(pta::getRetVars) + .map(m -> ToolUtil.getRetVars(pta.getPag(), m)) .flatMap(Collection::stream) - .map(objectFlowGraph::nodeOf) .filter(Objects::nonNull) .collect(Collectors.toSet()); visitedNodes = new HashSet<>(); wuEdges = new HashMap<>(); -// pollutionFlowGraph = new DirectedGraphImpl<>(); pollutionFlowGraph = new ConcurrentDirectedGraphImpl<>(); reachability = new Reachability<>(pollutionFlowGraph); } public void analyze(SootMethod startMethod) { - for (qilin.core.pag.VarNode param : pta.getParameters(startMethod)) { - Node node = objectFlowGraph.nodeOf(param); - if (node != null) { - dfs(node); + for (VarNode param : ToolUtil.getParameters(pta.getPag(), startMethod)) { + if (param != null) { + dfs(param); } else { if (Global.isDebug()) { System.out.println(param + " is absent in the flow graph."); @@ -74,13 +73,12 @@ public void analyze(SootMethod startMethod) { if (Global.isDebug()) { Set outMethods = new HashSet<>(); - for (qilin.core.pag.VarNode param : pta.getParameters(startMethod)) { - Node node = objectFlowGraph.nodeOf(param); - if (node != null) { + for (VarNode param : ToolUtil.getParameters(pta.getPag(), startMethod)) { + if (param != null) { for (Node outNode : outNodes) { - if (reachability.reachableNodesFrom(node).contains(outNode)) { - VarNode outVarNode = (VarNode) outNode; - outMethods.add(pta.declaringMethodOf(outVarNode.getVar())); + if (reachability.reachableNodesFrom(param).contains(outNode)) { + LocalVarNode outVarNode = (LocalVarNode) outNode; + outMethods.add(outVarNode.getMethod()); } } } @@ -112,10 +110,6 @@ public int numberOfPFGEdges() { return nrEdges; } -// public DirectedGraphImpl getPFG() { -// return pollutionFlowGraph; -// } - public ConcurrentDirectedGraphImpl getPFG() { return pollutionFlowGraph; } @@ -144,22 +138,34 @@ private void dfs(Node node) { pollutionFlowGraph.addNode(node); // add unwrapped flow edges if (Global.isEnableUnwrappedFlow()) { - if (node instanceof VarNode varNode) { - qilin.core.pag.VarNode var = varNode.getVar(); + if (node instanceof VarNode var) { // Optimization: approximate unwrapped flows to make // Zipper and pointer analysis run faster - pta.returnToVariablesOf(var).forEach(toVar -> { - Node toNode = objectFlowGraph.nodeOf(toVar); - if (outNodes.contains(toNode)) { - for (qilin.core.pag.VarNode inVar : inVars) { - if (!Collections.disjoint(pta.pointsToSetOf(inVar), pta.pointsToSetOf(var))) { - Edge unwrappedEdge = new Edge(Kind.UNWRAPPED_FLOW, node, toNode); - addWUEdge(node, unwrappedEdge); - break; + pta.getCgb().getReceiverToSitesMap() + .getOrDefault(var, Collections.emptySet()) + .forEach(vcs -> { + Stmt callsiteStmt = (Stmt) vcs.getUnit(); + InvokeExpr invo = callsiteStmt.getInvokeExpr(); + if (!(invo instanceof InstanceInvokeExpr)) { + return; } - } - } - }); + if (callsiteStmt instanceof AssignStmt assignStmt) { + Value lv = assignStmt.getLeftOp(); + if (!(lv.getType() instanceof RefType)) { + return; + } + final VarNode to = (VarNode) pta.getPag().findValNode(lv); + if (outNodes.contains(to)) { + for (VarNode inVar : inVars) { + if (!Collections.disjoint(ToolUtil.pointsToSetOf(pta, inVar), ToolUtil.pointsToSetOf(pta, var))) { + Edge unwrappedEdge = new Edge(Kind.UNWRAPPED_FLOW, node, to); + addWUEdge(node, unwrappedEdge); + break; + } + } + } + } + }); } } List nextEdges = new ArrayList<>(); @@ -170,9 +176,8 @@ private void dfs(Node node) { } case INTERPROCEDURAL_ASSIGN, INSTANCE_LOAD, WRAPPED_FLOW -> { // next must be a variable - VarNode next = (VarNode) edge.getTarget(); - qilin.core.pag.VarNode var = next.getVar(); - SootMethod inMethod = pta.declaringMethodOf(var); + LocalVarNode next = (LocalVarNode) edge.getTarget(); + SootMethod inMethod = next.getMethod(); // Optimization: filter out some potential spurious flows due to // the imprecision of context-insensitive pre-analysis, which // helps improve the performance of Zipper and pointer analysis. @@ -181,26 +186,38 @@ private void dfs(Node node) { } } case INSTANCE_STORE -> { - InstanceFieldNode next = (InstanceFieldNode) edge.getTarget(); + ContextField next = (ContextField) edge.getTarget(); AllocNode base = next.getBase(); if (base.getType().equals(currentType)) { // add wrapped flow edges to this variable if (Global.isEnableWrappedFlow()) { - pta.methodsInvokedOn(currentType).stream() - .map(pta::getThis) - .map(objectFlowGraph::nodeOf) - .filter(Objects::nonNull) // filter this variable of native methods + methodsInvokedOn(currentType).stream() + .map(m -> ToolUtil.getThis(pta.getPag(), m)) // filter this variable of native methods .map(n -> new Edge(Kind.WRAPPED_FLOW, next, n)) .forEach(e -> addWUEdge(next, e)); } nextEdges.add(edge); - } else if (oag.allocateesOf(currentType).contains(base)) { + } else if (pce.allocateesOf(currentType).contains(base)) { // Optimization, similar as above. if (Global.isEnableWrappedFlow()) { - Node assigned = objectFlowGraph.nodeOf(pta.assignedVarOf(base)); - if (assigned != null) { - Edge e = new Edge(Kind.WRAPPED_FLOW, next, assigned); - addWUEdge(next, e); + Set r = new HashSet<>(); + AllocNode mBase = (AllocNode) pta.parameterize(base, pta.emptyContext()); + pta.getPag().allocLookup(mBase).forEach(v -> { + if (v instanceof ContextVarNode cvn) { + if (cvn.base() instanceof LocalVarNode lvn) { + if (!lvn.isThis()) { + r.add(lvn); + } + } + } + }); + Iterator it = r.iterator(); + if (it.hasNext()) { + Node assigned = r.iterator().next(); + if (assigned != null) { + Edge e = new Edge(Kind.WRAPPED_FLOW, next, assigned); + addWUEdge(next, e); + } } } nextEdges.add(edge); @@ -223,6 +240,13 @@ private void addWUEdge(Node sourceNode, Edge edge) { wuEdges.computeIfAbsent(sourceNode, k -> new HashSet<>()).add(edge); } + private Collection methodsInvokedOn(Type type) { + return pta.getPag().getAllocNodes() + .stream().filter(o -> o.getType().equals(type)) + .map(pce::methodsInvokedOn).flatMap(Collection::stream) + .collect(Collectors.toSet()); + } + /** * @param node * @return out edges of node from OFG, and wuEdges, if present diff --git a/qilin.pta/src/qilin/pta/toolkits/zipper/flowgraph/IObjectFlowGraph.java b/qilin.pta/src/qilin/pta/toolkits/zipper/flowgraph/IObjectFlowGraph.java index b9ac30c..753f7cd 100644 --- a/qilin.pta/src/qilin/pta/toolkits/zipper/flowgraph/IObjectFlowGraph.java +++ b/qilin.pta/src/qilin/pta/toolkits/zipper/flowgraph/IObjectFlowGraph.java @@ -1,5 +1,7 @@ package qilin.pta.toolkits.zipper.flowgraph; +import qilin.core.pag.Node; + import java.util.Set; public interface IObjectFlowGraph { diff --git a/qilin.pta/src/qilin/pta/toolkits/zipper/flowgraph/InstanceFieldNode.java b/qilin.pta/src/qilin/pta/toolkits/zipper/flowgraph/InstanceFieldNode.java deleted file mode 100644 index ac03604..0000000 --- a/qilin.pta/src/qilin/pta/toolkits/zipper/flowgraph/InstanceFieldNode.java +++ /dev/null @@ -1,45 +0,0 @@ -package qilin.pta.toolkits.zipper.flowgraph; - -import qilin.core.pag.AllocNode; -import qilin.core.pag.SparkField; - -import java.util.Objects; - -public class InstanceFieldNode extends Node { - private final AllocNode base; - private final SparkField field; - - public InstanceFieldNode(final AllocNode base, final SparkField field) { - this.base = base; - this.field = field; - } - - public AllocNode getBase() { - return this.base; - } - - public SparkField getField() { - return this.field; - } - - @Override - public int hashCode() { - return Objects.hash(this.base, this.field); - } - - @Override - public boolean equals(final Object other) { - if (this == other) { - return true; - } - if (!(other instanceof final InstanceFieldNode anoNode)) { - return false; - } - return this.base.equals(anoNode.base) && this.field.equals(anoNode.field); - } - - @Override - public String toString() { - return "InstanceFieldNode: [" + this.base + "] " + this.field; - } -} diff --git a/qilin.pta/src/qilin/pta/toolkits/zipper/flowgraph/Node.java b/qilin.pta/src/qilin/pta/toolkits/zipper/flowgraph/Node.java deleted file mode 100644 index 77be23b..0000000 --- a/qilin.pta/src/qilin/pta/toolkits/zipper/flowgraph/Node.java +++ /dev/null @@ -1,24 +0,0 @@ -package qilin.pta.toolkits.zipper.flowgraph; - -import java.util.Collections; -import java.util.HashSet; -import java.util.Set; - -public class Node { - private Set outEdges; - - public Node() { - this.outEdges = Collections.emptySet(); - } - - public void addOutEdge(final Edge e) { - if (this.outEdges.isEmpty()) { - this.outEdges = new HashSet<>(); - } - this.outEdges.add(e); - } - - public Set getOutEdges() { - return this.outEdges; - } -} diff --git a/qilin.pta/src/qilin/pta/toolkits/zipper/flowgraph/ObjectFlowGraph.java b/qilin.pta/src/qilin/pta/toolkits/zipper/flowgraph/ObjectFlowGraph.java index fd8e8f1..2be92fd 100644 --- a/qilin.pta/src/qilin/pta/toolkits/zipper/flowgraph/ObjectFlowGraph.java +++ b/qilin.pta/src/qilin/pta/toolkits/zipper/flowgraph/ObjectFlowGraph.java @@ -1,134 +1,124 @@ package qilin.pta.toolkits.zipper.flowgraph; -import com.google.common.collect.HashBasedTable; -import com.google.common.collect.Table; -import qilin.core.pag.AllocNode; -import qilin.core.pag.SparkField; -import qilin.pta.toolkits.zipper.pta.PointsToAnalysis; +import qilin.core.PTA; +import qilin.core.builder.MethodNodeFactory; +import qilin.core.pag.*; +import qilin.pta.toolkits.zipper.Global; +import soot.SootMethod; +import soot.Value; +import soot.jimple.*; -import java.util.HashMap; -import java.util.HashSet; -import java.util.Map; -import java.util.Set; -import java.util.stream.Collectors; +import java.util.*; public class ObjectFlowGraph implements IObjectFlowGraph { + private final PTA pta; + private Map> outEdges; - private Set nodes; - private Map var2node; - private Table field2node; - - public ObjectFlowGraph(PointsToAnalysis pta) { - init(pta); - } - - /** - * @param var - * @return the VarNode representing var - */ - public VarNode nodeOf(qilin.core.pag.VarNode var) { -// if (!var2node.containsKey(var)) { -// throw new RuntimeException(var + " does not exist in Flow Graph"); -// } - return var2node.get(var); - } - - public Set succsOf(Node node) { - return node.getOutEdges() - .stream() - .map(Edge::getTarget) - .collect(Collectors.toSet()); + public ObjectFlowGraph(PTA pta) { + this.pta = pta; + init(); } + @Override public Set outEdgesOf(Node node) { - return node.getOutEdges(); + return outEdges.getOrDefault(node, Collections.emptySet()); } + @Override public Set allNodes() { - return nodes; + return outEdges.keySet(); } - private void init(PointsToAnalysis pta) { - nodes = new HashSet<>(); - var2node = new HashMap<>(); + private boolean localVarBase(ValNode valNode) { + if (valNode instanceof ContextVarNode cvn) { + return cvn.base() instanceof LocalVarNode; + } else { + return valNode instanceof LocalVarNode; + } + } - // Add local assignment edges. - pta.localAssignIterator().forEachRemaining(pair -> { - qilin.core.pag.VarNode to = pair.getFirst(); - qilin.core.pag.VarNode from = pair.getSecond(); - VarNode toNode = getVarNode(to); - VarNode fromNode = getVarNode(from); - fromNode.addOutEdge(new Edge(Kind.LOCAL_ASSIGN, fromNode, toNode)); - }); + private LocalVarNode fetchLocalVar(ValNode valNode) { + if (valNode instanceof ContextVarNode cvn) { + if (cvn.base() instanceof LocalVarNode) { + return (LocalVarNode) cvn.base(); + } + } else if (valNode instanceof LocalVarNode) { + return (LocalVarNode) valNode; + } + return null; + } - // Add inter-procedural assignment edges. - pta.interProceduralAssignIterator().forEachRemaining(pair -> { - qilin.core.pag.VarNode to = pair.getFirst(); - qilin.core.pag.VarNode from = pair.getSecond(); - VarNode toNode = getVarNode(to); - VarNode fromNode = getVarNode(from); - fromNode.addOutEdge(new Edge(Kind.INTERPROCEDURAL_ASSIGN, fromNode, toNode)); - }); + private LocalVarNode fetchVar(ValNode valNode) { + if (valNode instanceof ContextVarNode cvn) { + VarNode base = cvn.base(); + if (base instanceof LocalVarNode lvn) { + return lvn; + } + } else if (valNode instanceof LocalVarNode lvn) { + return lvn; + } + return null; + } - // Add this-passing assignment edges - pta.thisAssignIterator().forEachRemaining(pair -> { - qilin.core.pag.VarNode thisVar = pair.getFirst(); - qilin.core.pag.VarNode baseVar = pair.getSecond(); - VarNode toNode = getVarNode(thisVar); - VarNode fromNode = getVarNode(baseVar); - fromNode.addOutEdge(new Edge(Kind.INTERPROCEDURAL_ASSIGN, fromNode, toNode)); - }); + public void addOutEdge(final Edge e) { + outEdges.computeIfAbsent(e.getSource(), k -> new HashSet<>()).add(e); + } - field2node = HashBasedTable.create(); - // Add instance load edges; - pta.instanceLoadIterator().forEachRemaining(triple -> { - qilin.core.pag.VarNode var = triple.getFirst(); - AllocNode base = triple.getSecond(); - SparkField field = triple.getThird(); - VarNode varNode = getVarNode(var); - InstanceFieldNode fieldNode = getInstanceFieldNode(base, field); - fieldNode.addOutEdge(new Edge(Kind.INSTANCE_LOAD, fieldNode, varNode)); + private void init() { + outEdges = new HashMap<>(); + + pta.getPag().getSimple().forEach((s, ts) -> { + if (localVarBase(s)) { + ts.forEach(t -> { + if (localVarBase(t)) { + LocalVarNode toNode = fetchVar(t); + LocalVarNode fromNode = fetchVar(s); + if (fetchLocalVar(s).isInterProcSource()) { + addOutEdge(new Edge(Kind.INTERPROCEDURAL_ASSIGN, fromNode, toNode)); + } else { + if (fetchLocalVar(t).isInterProcTarget()) { + if (!fetchLocalVar(t).isThis()) { + addOutEdge(new Edge(Kind.INTERPROCEDURAL_ASSIGN, fromNode, toNode)); + } + } else { + addOutEdge(new Edge(Kind.LOCAL_ASSIGN, fromNode, toNode)); + } + } + } else if (t instanceof ContextField ctxField) { + LocalVarNode varNode = fetchVar(s); + addOutEdge(new Edge(Kind.INSTANCE_STORE, varNode, ctxField)); + } + }); + } else if (s instanceof ContextField ctxField) { + ts.forEach(t -> { + assert localVarBase(t); + LocalVarNode varNode = fetchVar(t); + addOutEdge(new Edge(Kind.INSTANCE_LOAD, ctxField, varNode)); + }); + } }); - // Add instance store edges; - pta.instanceStoreIterator().forEachRemaining(triple -> { - AllocNode base = triple.getFirst(); - SparkField field = triple.getSecond(); - qilin.core.pag.VarNode var = triple.getThird(); - VarNode varNode = getVarNode(var); - InstanceFieldNode fieldNode = getInstanceFieldNode(base, field); - varNode.addOutEdge(new Edge(Kind.INSTANCE_STORE, varNode, fieldNode)); + pta.getCallGraph().forEach(e -> { + Stmt callsite = e.srcStmt(); + SootMethod caller = e.src(); + if (caller != null) { + SootMethod callee = e.tgt(); + if (!callee.isStatic()) { + MethodNodeFactory calleeNF = pta.getPag().getMethodPAG(callee).nodeFactory(); + LocalVarNode thisVar = (LocalVarNode) calleeNF.caseThis(); + InvokeExpr ie = callsite.getInvokeExpr(); + Value base = null; + if (ie instanceof InstanceInvokeExpr iie) { + base = iie.getBase(); + } + if (base != null) { + LocalVarNode fromNode = (LocalVarNode) pta.getPag().findValNode(base); + addOutEdge(new Edge(Kind.INTERPROCEDURAL_ASSIGN, fromNode, thisVar)); + } + } + } else if (Global.isDebug()) { + System.out.println("Null caller of: " + callsite); + } }); } - - /** - * @param var - * @return the VarNode representing var. - * If the node does not exist, it will be created. - */ - private VarNode getVarNode(qilin.core.pag.VarNode var) { - if (!var2node.containsKey(var)) { - VarNode node = new VarNode(var); - var2node.put(var, node); - nodes.add(node); - return node; - } - return var2node.get(var); - } - - /** - * @param base - * @param field - * @return the instance field representing base.field. - * If the node does not exist, it will be created. - */ - private InstanceFieldNode getInstanceFieldNode(AllocNode base, SparkField field) { - if (!field2node.contains(base, field)) { - InstanceFieldNode node = new InstanceFieldNode(base, field); - field2node.put(base, field, node); - nodes.add(node); - return node; - } - return field2node.get(base, field); - } } diff --git a/qilin.pta/src/qilin/pta/toolkits/zipper/flowgraph/VarNode.java b/qilin.pta/src/qilin/pta/toolkits/zipper/flowgraph/VarNode.java deleted file mode 100644 index 639b301..0000000 --- a/qilin.pta/src/qilin/pta/toolkits/zipper/flowgraph/VarNode.java +++ /dev/null @@ -1,34 +0,0 @@ -package qilin.pta.toolkits.zipper.flowgraph; - -public class VarNode extends Node { - private final qilin.core.pag.VarNode var; - - public VarNode(final qilin.core.pag.VarNode var) { - this.var = var; - } - - public qilin.core.pag.VarNode getVar() { - return this.var; - } - - @Override - public int hashCode() { - return this.var.hashCode(); - } - - @Override - public boolean equals(final Object other) { - if (this == other) { - return true; - } - if (!(other instanceof final VarNode anoNode)) { - return false; - } - return this.var.equals(anoNode.var); - } - - @Override - public String toString() { - return "VarNode: <" + this.var + ">"; - } -} diff --git a/qilin.pta/src/qilin/pta/toolkits/zipper/pta/WrapperedPointsToAnalysis.java b/qilin.pta/src/qilin/pta/toolkits/zipper/pta/WrapperedPointsToAnalysis.java deleted file mode 100644 index bfcf7a4..0000000 --- a/qilin.pta/src/qilin/pta/toolkits/zipper/pta/WrapperedPointsToAnalysis.java +++ /dev/null @@ -1,470 +0,0 @@ -package qilin.pta.toolkits.zipper.pta; - -import com.google.common.collect.Streams; -import qilin.core.PTA; -import qilin.core.PTAScene; -import qilin.core.builder.MethodNodeFactory; -import qilin.core.pag.*; -import qilin.core.sets.P2SetVisitor; -import qilin.core.sets.PointsToSetInternal; -import qilin.pta.toolkits.zipper.Global; -import qilin.util.Pair; -import qilin.util.Triple; -import soot.*; -import soot.jimple.AssignStmt; -import soot.jimple.InstanceInvokeExpr; -import soot.jimple.InvokeExpr; -import soot.jimple.Stmt; -import soot.util.queue.QueueReader; - -import java.util.*; - -public class WrapperedPointsToAnalysis implements PointsToAnalysis { - private final PTA prePTA; - private Set allObjs; - private Set reachableMethods; - private Map directSuperType; - private Map> typeObjects; - private Map> typeMethods; - private List> thisAssign; - private Map> fieldPointsto; - private Map> obj2invokedMethods; - private Map objAssignedTo; - private Map> caller2callees; - private Map> allocatedHeaps; - private Map var2declaringMethod; - private Map> declaringMethod2var; - private Map> receiver2return; - private int totalPTSSize; - - public WrapperedPointsToAnalysis(PTA prePTA) { - this.prePTA = prePTA; - this.init(); - } - - public Set allObjects() { - return this.allObjs; - } - - public Set pointsToSetOf(final VarNode var) { - Set ret = new HashSet<>(); - PointsToSetInternal pts = (PointsToSetInternal) prePTA.reachingObjects(var); - pts.mapToCIPointsToSet().forall(new P2SetVisitor() { - @Override - public void visit(Node n) { - ret.add((AllocNode) n); - } - }); - return ret; - } - - @Override - public int pointsToSetSizeOf(VarNode var) { - return pointsToSetOf(var).size(); - } - - @Override - public int totalPointsToSetSize() { - return totalPTSSize; - } - - @Override - public Set variablesDeclaredIn(SootMethod method) { - return declaringMethod2var.getOrDefault(method, Collections.emptySet()); - } - - public Set objectsAllocatedIn(final SootMethod method) { - return this.allocatedHeaps.getOrDefault(method, Collections.emptySet()); - } - - public Set calleesOf(final SootMethod method) { - return caller2callees.getOrDefault(method, Collections.emptySet()); - } - - @Override - public Set reachableMethods() { - return reachableMethods; - } - - public Set methodsInvokedOn(final AllocNode obj) { - return this.obj2invokedMethods.getOrDefault(obj, Collections.emptySet()); - } - - public Set returnToVariablesOf(final VarNode recv) { - return receiver2return.getOrDefault(recv, Collections.emptySet()); - } - - private boolean localVarBase(ValNode valNode) { - if (valNode instanceof ContextVarNode cvn) { - return cvn.base() instanceof LocalVarNode; - } else { - return valNode instanceof LocalVarNode; - } - } - - private LocalVarNode fetchLocalVar(ValNode valNode) { - if (valNode instanceof ContextVarNode cvn) { - if (cvn.base() instanceof LocalVarNode) { - return (LocalVarNode) cvn.base(); - } - } else if (valNode instanceof LocalVarNode) { - return (LocalVarNode) valNode; - } - return null; - } - - private VarNode fetchVar(ValNode valNode) { - if (valNode instanceof ContextVarNode cvn) { - return cvn.base(); - } else if (valNode instanceof VarNode) { - return (VarNode) valNode; - } - return null; - } - - public Iterator> localAssignIterator() { - return new Iterator<>() { - private final Iterator> assignIter = prePTA.getPag().getSimple().entrySet().stream() - .filter(e -> localVarBase(e.getKey()) && !fetchLocalVar(e.getKey()).isInterProcSource()) - .flatMap(e -> e.getValue().stream() - .filter(tgt -> localVarBase(tgt) && !fetchLocalVar(tgt).isInterProcTarget()) - .map(tgt -> new Pair<>(fetchVar(tgt), fetchVar(e.getKey()))) - ) - .iterator(); - - @Override - public boolean hasNext() { - return this.assignIter.hasNext(); - } - - @Override - public Pair next() { - if (this.assignIter.hasNext()) { - return this.assignIter.next(); - } - throw new NoSuchElementException(); - } - }; - } - - public Iterator> interProceduralAssignIterator() { - return new Iterator<>() { - private final Iterator> assignIter = Streams.concat( - prePTA.getPag().getSimple().entrySet().stream() - .filter(e -> localVarBase(e.getKey()) && fetchLocalVar(e.getKey()).isInterProcSource()) - .flatMap(e -> e.getValue().stream() - .map(tgt -> new Pair<>(fetchVar(tgt), fetchVar(e.getKey()))) - ) - , - prePTA.getPag().getSimpleInv().entrySet().stream() - .filter(e -> localVarBase(e.getKey()) && fetchLocalVar(e.getKey()).isInterProcTarget() && !fetchLocalVar(e.getKey()).isThis()) - .flatMap(e -> e.getValue().stream() - .map(src -> new Pair<>(fetchVar(e.getKey()), fetchVar(src))) - ) - ) - .iterator(); - - @Override - public boolean hasNext() { - return this.assignIter.hasNext(); - } - - @Override - public Pair next() { - if (this.assignIter.hasNext()) { - return this.assignIter.next(); - } - throw new NoSuchElementException(); - } - }; - } - - public Iterator> instanceLoadIterator() { - return new Iterator<>() { - - private final Iterator> loadIter = prePTA.getPag().getSimple().entrySet().stream() - .filter(e -> e.getKey() instanceof ContextField) - .flatMap(e -> e.getValue().stream() - .map(tgt -> new Triple<>(fetchVar(tgt), ((ContextField) e.getKey()).getBase(), ((ContextField) e.getKey()).getField())) - ) - .iterator(); - - @Override - public boolean hasNext() { - return this.loadIter.hasNext(); - } - - @Override - public Triple next() { - if (this.loadIter.hasNext()) { - final Triple load = this.loadIter.next(); - final VarNode to = load.getFirst(); - final AllocNode base = load.getSecond(); - final SparkField field = load.getThird(); - return new Triple<>(to, base, field); - } - throw new NoSuchElementException(); - } - }; - } - - public Iterator> instanceStoreIterator() { - return new Iterator<>() { - private final Iterator> storeIter = prePTA.getPag().getSimpleInv().entrySet().stream() - .filter(e -> e.getKey() instanceof ContextField) - .flatMap(e -> e.getValue().stream().map(src -> new Triple<>(((ContextField) e.getKey()).getBase(), ((ContextField) e.getKey()).getField(), fetchVar(src))) - ).iterator(); - - @Override - public boolean hasNext() { - return this.storeIter.hasNext(); - } - - @Override - public Triple next() { - if (this.storeIter.hasNext()) { - final Triple store = this.storeIter.next(); - final AllocNode base = store.getFirst(); - final SparkField field = store.getSecond(); - final VarNode from = store.getThird(); - return new Triple<>(base, field, from); - } - throw new NoSuchElementException(); - } - }; - } - - public Iterator> thisAssignIterator() { - return this.thisAssign.iterator(); - } - - public Type declaringTypeOf(final SootMethod method) { - return method.getDeclaringClass().getType(); - } - - public Type directSuperTypeOf(final Type type) { - return this.directSuperType.get(type); - } - - public Set objectsOfType(final Type type) { - return this.typeObjects.get(type); - } - - @Override - public Set getParameters(SootMethod m) { - MethodNodeFactory mthdNF = prePTA.getPag().getMethodPAG(m).nodeFactory(); - Set ret = new HashSet<>(); - for (int i = 0; i < m.getParameterCount(); ++i) { - if (m.getParameterType(i) instanceof RefType) { - VarNode param = mthdNF.caseParm(i); - ret.add(param); - } - } - return ret; - } - - @Override - public Set getRetVars(SootMethod m) { - MethodNodeFactory mthdNF = prePTA.getPag().getMethodPAG(m).nodeFactory(); - if (m.getReturnType() instanceof RefType) { - VarNode ret = mthdNF.caseRet(); - return Collections.singleton(ret); - } - return Collections.emptySet(); - } - - @Override - public VarNode getThis(SootMethod m) { - MethodNodeFactory mthdNF = prePTA.getPag().getMethodPAG(m).nodeFactory(); - return mthdNF.caseThis(); - } - - public Set methodsInvokedOn(final Type type) { - return this.typeMethods.get(type); - } - - private void init() { - this.receiver2return = new HashMap<>(); - - for (SootMethod sig : prePTA.getNakedReachableMethods()) { - prePTA.getPag().getMethodPAG(sig).getInvokeStmts().forEach(s -> { - if (!(s instanceof AssignStmt)) { - return; - } - InvokeExpr invo = ((Stmt) s).getInvokeExpr(); - if (!(invo instanceof InstanceInvokeExpr)) { - return; - } - final VarNode recv = (VarNode) prePTA.getPag().findValNode(((InstanceInvokeExpr) invo).getBase()); - Value toNode = ((AssignStmt) s).getLeftOp(); - if (!(toNode.getType() instanceof RefType)) { - return; - } - final VarNode to = (VarNode) prePTA.getPag().findValNode(toNode); - receiver2return.computeIfAbsent(recv, k -> new HashSet<>()).add(to); - }); - } - - totalPTSSize = 0; - this.buildPointsToSet(); - this.computeAllocatedObjects(); - this.buildCallees(); - this.buildMethodsInvokedOnObjects(); - this.buildVarDeclaringMethods(); - this.buildObjectAssignedVariables(); - this.buildFieldPointsToSet(); - this.buildDirectSuperType(); - this.typeObjects = new HashMap<>(); - this.typeMethods = new HashMap<>(); - - this.allObjects().forEach(obj -> { - final Type type = obj.getType(); - this.typeObjects.putIfAbsent(type, new HashSet<>()); - this.typeObjects.get(type).add(obj); - this.typeMethods.putIfAbsent(type, new HashSet<>()); - this.typeMethods.get(type).addAll(this.methodsInvokedOn(obj)); - }); - } - - private void buildPointsToSet() { - this.allObjs = new HashSet<>(); - for (ValNode var : prePTA.getPag().getValNodeNumberer()) { - if (var instanceof VarNode varNode) { - Collection pts = pointsToSetOf(varNode); - totalPTSSize += pts.size(); - this.allObjs.addAll(pts); - } - } - } - - public SootMethod containingMethodOf(final AllocNode obj) { - return obj.getMethod(); - } - - public SootMethod declaringMethodOf(final VarNode var) { - return this.var2declaringMethod.get(var); - } - - public VarNode assignedVarOf(final AllocNode obj) { - return this.objAssignedTo.get(obj); - } - - private void computeAllocatedObjects() { - this.allocatedHeaps = new HashMap<>(); - for (AllocNode alloc : prePTA.getPag().getAllocNodeNumberer()) { - if (alloc.getMethod() == null) {//TODO special objects? - continue; - } - SootMethod method = alloc.getMethod(); - allocatedHeaps.computeIfAbsent(method, k -> new HashSet<>()).add(alloc); - } - } - - private void buildCallees() { - this.reachableMethods = new HashSet<>(); - this.thisAssign = new LinkedList<>(); - this.caller2callees = new HashMap<>(); - final Map callIn = new HashMap<>(); - final Map callBase = new HashMap<>(); - - for (final MethodOrMethodContext momc : prePTA.getReachableMethods()) { - SootMethod sig = momc.method(); - prePTA.getPag().getMethodPAG(sig).getInvokeStmts().forEach(s -> { - callIn.put(s, sig); - }); - } - - prePTA.getCgb().getReceiverToSitesMap().forEach((recv, sites) -> { - sites.forEach(site -> { - callBase.put(site.getUnit(), recv.getVariable()); - }); - }); - - prePTA.getCallGraph().forEach(e -> { - Unit callsiteStr = e.srcUnit(); - SootMethod caller = callIn.get(callsiteStr); - if (caller != null) { - SootMethod callee = e.tgt(); - caller2callees.computeIfAbsent(caller, k -> new HashSet<>()).add(callee); - this.reachableMethods.add(caller); - this.reachableMethods.add(callee); - if (!callee.isStatic()) { - MethodNodeFactory calleeNF = prePTA.getPag().getMethodPAG(callee).nodeFactory(); - VarNode thisVar = calleeNF.caseThis(); - Object baseVarStr = callBase.get(callsiteStr); - if (baseVarStr != null) { - VarNode baseVar = (VarNode) prePTA.getPag().findValNode(baseVarStr); - this.thisAssign.add(new Pair<>(thisVar, baseVar)); - } - } - } else if (Global.isDebug()) { - System.out.println("Null caller of: " + callsiteStr); - } - }); - - } - - private void buildMethodsInvokedOnObjects() { - this.obj2invokedMethods = new HashMap<>(); - reachableMethods.stream().filter(m -> !m.isStatic()).forEach(instMtd -> { - MethodNodeFactory mthdNF = prePTA.getPag().getMethodPAG(instMtd).nodeFactory(); - VarNode thisVar = mthdNF.caseThis(); - this.pointsToSetOf(thisVar).forEach(obj -> { - obj2invokedMethods.computeIfAbsent(obj, k -> new HashSet<>()).add(instMtd); - }); - }); - } - - private void buildVarDeclaringMethods() { - this.var2declaringMethod = new HashMap<>(); - this.declaringMethod2var = new HashMap<>(); - for (ValNode valnode : prePTA.getPag().getValNodeNumberer()) { - if (!(valnode instanceof LocalVarNode lvn)) { - continue; - } - SootMethod inMethod = lvn.getMethod(); - var2declaringMethod.put(lvn, inMethod); - declaringMethod2var.computeIfAbsent(inMethod, k -> new HashSet<>()).add(lvn); - } - } - - private void buildObjectAssignedVariables() { - this.objAssignedTo = new HashMap<>(); - for (final MethodOrMethodContext momc : prePTA.getReachableMethods()) { - MethodPAG mpag = prePTA.getPag().getMethodPAG(momc.method()); - QueueReader reader = mpag.getInternalReader().clone(); - while (reader.hasNext()) { - Node src = reader.next(); - Node tgt = reader.next(); - if (src instanceof AllocNode) { - objAssignedTo.put((AllocNode) src, (VarNode) tgt); - } - } - } - } - - private void buildFieldPointsToSet() { - this.fieldPointsto = new HashMap<>(); - prePTA.getPag().getContextFields().forEach(contextField -> { - AllocNode base = contextField.getBase(); - contextField.getP2Set().mapToCIPointsToSet().forall(new P2SetVisitor() { - @Override - public void visit(Node n) { - fieldPointsto.computeIfAbsent(base, k -> new HashSet<>()).add((AllocNode) n); - } - }); - }); - } - - private void buildDirectSuperType() { - this.directSuperType = new HashMap<>(); - PTAScene.v().getClasses().forEach(c -> { - if (!c.hasSuperclass()) { - return; - } - Type type = c.getType(); - Type superType = c.getSuperclass().getType(); - this.directSuperType.put(type, superType); - }); - } -} diff --git a/qilin.pta/src/qilin/pta/tools/BasePTA.java b/qilin.pta/src/qilin/pta/tools/BasePTA.java index 1ca51e5..7b7b3fa 100644 --- a/qilin.pta/src/qilin/pta/tools/BasePTA.java +++ b/qilin.pta/src/qilin/pta/tools/BasePTA.java @@ -2,20 +2,21 @@ import qilin.CoreConfig; import qilin.core.CorePTA; -import qilin.core.PTAScene; import qilin.core.builder.CallGraphBuilder; import qilin.core.pag.PAG; import qilin.core.solver.Propagator; import qilin.core.solver.Solver; import qilin.stat.IEvaluator; import qilin.stat.PTAEvaluator; +import qilin.stat.SimplifiedEvaluator; import qilin.util.PTAUtils; -public class BasePTA extends CorePTA { +public abstract class BasePTA extends CorePTA { protected IEvaluator evaluator; public BasePTA() { - this.evaluator = new PTAEvaluator(this); +// this.evaluator = new PTAEvaluator(this); + this.evaluator = new SimplifiedEvaluator(this); } public IEvaluator evaluator() { @@ -43,7 +44,6 @@ public void run() { pureRun(); evaluator.end(); dumpStats(); - pag.dumpPagStructureSize(); System.out.println(evaluator()); } diff --git a/qilin.pta/src/qilin/pta/tools/BeanPTA.java b/qilin.pta/src/qilin/pta/tools/BeanPTA.java index 4893378..4f9b870 100644 --- a/qilin.pta/src/qilin/pta/tools/BeanPTA.java +++ b/qilin.pta/src/qilin/pta/tools/BeanPTA.java @@ -18,7 +18,6 @@ package qilin.pta.tools; -import qilin.core.CorePTA; import qilin.parm.ctxcons.CtxConstructor; import qilin.parm.heapabst.AllocSiteAbstractor; import qilin.parm.heapabst.HeuristicAbstractor; @@ -28,7 +27,7 @@ import qilin.parm.select.PipelineSelector; import qilin.pta.PTAConfig; import qilin.pta.StagedPTA; -import qilin.pta.toolkits.bean.main.Bean; +import qilin.pta.toolkits.bean.Bean; import qilin.util.Stopwatch; import java.util.HashMap; @@ -38,7 +37,6 @@ * refer to "Making k-Object-Sensitive Pointer Analysis More Precise with Still k-Limiting" (SAS'16) * */ public class BeanPTA extends StagedPTA { - private final CorePTA prePTA; // currently, we only support k = 2 and hk = 1; // [current heap, [allocator heap, [heap ctx, new ctx]]] only for B-2obj; Map>> beanNexCtxMap = new HashMap<>(); diff --git a/qilin.pta/src/qilin/pta/tools/DebloatedPTA.java b/qilin.pta/src/qilin/pta/tools/DebloatedPTA.java index 12ef527..90a84c9 100644 --- a/qilin.pta/src/qilin/pta/tools/DebloatedPTA.java +++ b/qilin.pta/src/qilin/pta/tools/DebloatedPTA.java @@ -18,11 +18,9 @@ package qilin.pta.tools; -import qilin.core.PTA; import qilin.core.pag.*; import qilin.core.sets.PointsToSet; import qilin.core.solver.Propagator; -import qilin.parm.heapabst.HeapAbstractor; import qilin.parm.select.CtxSelector; import qilin.parm.select.DebloatingSelector; import qilin.parm.select.PipelineSelector; @@ -40,9 +38,7 @@ * refer to "Context Debloating for Object-Sensitive Pointer Analysis" (ASE'21) * */ public class DebloatedPTA extends StagedPTA { - protected PTA prePTA; protected BasePTA basePTA; - // context dependent heaps protected Set ctxDepHeaps = new HashSet<>(); /* @@ -53,7 +49,11 @@ public DebloatedPTA(BasePTA basePTA) { this.basePTA = basePTA; CtxSelector debloatingSelector = new DebloatingSelector(ctxDepHeaps); basePTA.setContextSelector(new PipelineSelector(basePTA.ctxSelector(), debloatingSelector)); - this.prePTA = new Spark(); + if (basePTA instanceof StagedPTA stagedPTA) { + this.prePTA = stagedPTA.getPrePTA(); + } else { + this.prePTA = new Spark(); + } System.out.println("debloating ...."); } @@ -120,11 +120,6 @@ public PointsToSet reachingObjectsInternal(PointsToSet s, SparkField f) { return basePTA.reachingObjectsInternal(s, f); } - @Override - public HeapAbstractor heapAbstractor() { - return basePTA.heapAbstractor(); - } - @Override public PointsToSet reachingObjects(Local l) { return basePTA.reachingObjects(l); diff --git a/qilin.pta/src/qilin/pta/tools/MahjongPTA.java b/qilin.pta/src/qilin/pta/tools/MahjongPTA.java index 39f1cd2..965a33d 100644 --- a/qilin.pta/src/qilin/pta/tools/MahjongPTA.java +++ b/qilin.pta/src/qilin/pta/tools/MahjongPTA.java @@ -18,8 +18,6 @@ package qilin.pta.tools; -import qilin.core.PTA; -import qilin.core.pag.PAG; import qilin.parm.ctxcons.CtxConstructor; import qilin.parm.heapabst.MahjongAbstractor; import qilin.parm.select.CtxSelector; @@ -55,12 +53,9 @@ public MahjongPTA(int k, int hk, CtxConstructor ctxCons) { @Override protected void preAnalysis() { PTAConfig.v().getPtaConfig().mergeHeap = false; - PTA prePTA = new Spark(); prePTA.pureRun(); Mahjong.run(prePTA, heapModelMap); - PAG pag = prePTA.getPag(); -// Map valToAllocNode = prePTA.getPag().valToAllocNode; heapModelMap.forEach((origin, merged) -> { if (pag.findAllocNode(origin) == null || pag.findAllocNode(merged) == null) { return; diff --git a/qilin.pta/src/qilin/pta/tools/PartialCallSiteSensPTA.java b/qilin.pta/src/qilin/pta/tools/PartialCallSiteSensPTA.java index d2217b2..3844561 100644 --- a/qilin.pta/src/qilin/pta/tools/PartialCallSiteSensPTA.java +++ b/qilin.pta/src/qilin/pta/tools/PartialCallSiteSensPTA.java @@ -116,7 +116,7 @@ protected void extraStats() { if (method.isPhantom()) { return; } - MethodPAG srcmpag = prePTA.getPag().getMethodPAG(method); + MethodPAG srcmpag = pag.getMethodPAG(method); QueueReader reader = srcmpag.getInternalReader().clone(); while (reader.hasNext()) { Node from = reader.next(), to = reader.next(); diff --git a/qilin.pta/src/qilin/pta/tools/PartialObjSensPTA.java b/qilin.pta/src/qilin/pta/tools/PartialObjSensPTA.java index db15d66..70dfa67 100644 --- a/qilin.pta/src/qilin/pta/tools/PartialObjSensPTA.java +++ b/qilin.pta/src/qilin/pta/tools/PartialObjSensPTA.java @@ -41,7 +41,6 @@ public abstract class PartialObjSensPTA extends StagedPTA { protected Set csnodes = new HashSet<>(); protected Set csmethods = new HashSet<>(); - protected BasePTA prePTA; protected PAG prePAG; // just for stats @@ -118,7 +117,7 @@ protected void extraStats() { if (method.isPhantom()) { return; } - MethodPAG srcmpag = prePTA.getPag().getMethodPAG(method); + MethodPAG srcmpag = pag.getMethodPAG(method); QueueReader reader = srcmpag.getInternalReader().clone(); while (reader.hasNext()) { Node from = reader.next(), to = reader.next(); diff --git a/qilin.pta/src/qilin/pta/tools/SelectxPTA.java b/qilin.pta/src/qilin/pta/tools/SelectxPTA.java index c733dea..3640b61 100644 --- a/qilin.pta/src/qilin/pta/tools/SelectxPTA.java +++ b/qilin.pta/src/qilin/pta/tools/SelectxPTA.java @@ -1,47 +1,48 @@ -/* Qilin - a Java Pointer Analysis Framework - * Copyright (C) 2021-2030 Qilin developers - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Lesser General Public License as - * published by the Free Software Foundation, either version 3.0 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Lesser Public License for more details. - * - * You should have received a copy of the GNU General Lesser Public - * License along with this program. If not, see - * . - */ - -package qilin.pta.tools; - -import qilin.pta.toolkits.selectx.Selectx; -import qilin.util.Stopwatch; - -import java.util.Map; - -public class SelectxPTA extends PartialCallSiteSensPTA { - - public SelectxPTA(int ctxLen) { - super(ctxLen); - System.out.println("selectx ... "); - } - - //=========context selector============= - - @Override - protected Map calculatingNode2Length() { - System.out.print("Construct transPAG..."); - Stopwatch stopwatch = Stopwatch.newAndStart("transPAG construction"); - prePAG = prePTA.getPag(); - Selectx selectx = new Selectx(prePTA); - stopwatch.stop(); - System.out.println(stopwatch); - System.out.println("Propagate.."); - return selectx.process(); - } - -} +/* Qilin - a Java Pointer Analysis Framework + * Copyright (C) 2021-2030 Qilin developers + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3.0 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * . + */ + +package qilin.pta.tools; + +import qilin.pta.toolkits.selectx.Selectx; + +import java.util.Map; + +public class SelectxPTA extends PartialCallSiteSensPTA { + + public SelectxPTA(int ctxLen) { + super(ctxLen); + System.out.println("selectx ... "); + } + + //=========context selector============= + + @Override + protected Map calculatingNode2Length() { + System.out.print("Construct transPAG..."); + long time = System.currentTimeMillis(); + + prePAG = prePTA.getPag(); + Selectx selectx = new Selectx(prePTA); + + System.out.println((System.currentTimeMillis() - time) / 1000 + "s"); + + System.out.println("Propagate.."); + return selectx.process(); + } + +} diff --git a/qilin.pta/src/qilin/pta/tools/ZipperPTA.java b/qilin.pta/src/qilin/pta/tools/ZipperPTA.java index d2efef2..6db8277 100644 --- a/qilin.pta/src/qilin/pta/tools/ZipperPTA.java +++ b/qilin.pta/src/qilin/pta/tools/ZipperPTA.java @@ -42,7 +42,6 @@ * and "A Principled Approach to Selective Context Sensitivity for Pointer Analysis" (TOPLAS'20) * */ public class ZipperPTA extends StagedPTA { - private final BasePTA prePTA; private final Set PCMs = new HashSet<>(); /* @@ -88,7 +87,7 @@ protected void extraStats() { if (method.isPhantom()) { return; } - MethodPAG srcmpag = prePTA.getPag().getMethodPAG(method); + MethodPAG srcmpag = pag.getMethodPAG(method); QueueReader reader = srcmpag.getInternalReader().clone(); while (reader.hasNext()) { Node from = reader.next(), to = reader.next(); diff --git a/qilin.pta/test/qilin/test/util/AliasAssertion.java b/qilin.pta/test/qilin/test/util/AliasAssertion.java index 16c736b..4890cfa 100644 --- a/qilin.pta/test/qilin/test/util/AliasAssertion.java +++ b/qilin.pta/test/qilin/test/util/AliasAssertion.java @@ -19,6 +19,7 @@ package qilin.test.util; import qilin.core.PTA; +import qilin.core.PTAScene; import qilin.core.sets.PointsToSet; import qilin.core.sets.PointsToSetInternal; import qilin.pta.PTAConfig; @@ -111,12 +112,12 @@ protected boolean isMayAlias(PTA pta, Value va, Value vb) { if (DEBUG) { // PTAUtils.dumpPts(pta, true); // PTAUtils.dumpPAG(pta.getPag(), "pag.txt"); - System.out.println("va points to: " + PTAUtils.getNodeLabel(pta.getPag().findLocalVarNode(va)) + pta.getPag().findLocalVarNode(va)); + System.out.println("va points to: " + PTAUtils.getNodeLabel(PTAScene.v().getNodeFactory().findLocalVarNode(va)) + PTAScene.v().getNodeFactory().findLocalVarNode(va)); PTAUtils.printPts(pts1); } PointsToSetInternal pts2 = ((PointsToSetInternal) pta.reachingObjects((Local) vb)).mapToCIPointsToSet(); if (DEBUG) { - System.out.println("vb points to: " + PTAUtils.getNodeLabel(pta.getPag().findLocalVarNode(vb)) + pta.getPag().findLocalVarNode(vb)); + System.out.println("vb points to: " + PTAUtils.getNodeLabel(PTAScene.v().getNodeFactory().findLocalVarNode(vb)) + PTAScene.v().getNodeFactory().findLocalVarNode(vb)); PTAUtils.printPts(pts2); } return pts1.hasNonEmptyIntersection(pts2); diff --git a/qilin.util/src/qilin/util/DataFactory.java b/qilin.util/src/qilin/util/DataFactory.java new file mode 100644 index 0000000..0f98e02 --- /dev/null +++ b/qilin.util/src/qilin/util/DataFactory.java @@ -0,0 +1,32 @@ +package qilin.util; + +import java.util.*; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.CopyOnWriteArrayList; + +public class DataFactory { + public static List createList() { +// return new ArrayList<>(); + return new CopyOnWriteArrayList<>(); + } + + public static Set createSet() { +// return new HashSet<>(); + return ConcurrentHashMap.newKeySet(); + } + + public static Set createSet(int initCapacity) { +// return new HashSet<>(initCapacity); + return ConcurrentHashMap.newKeySet(initCapacity); + } + + public static Map createMap() { +// return new HashMap<>(); + return new ConcurrentHashMap<>(); + } + + public static Map createMap(int initCapacity) { +// return new HashMap<>(initCapacity); + return new ConcurrentHashMap<>(initCapacity); + } +} diff --git a/qilin.util/src/qilin/util/Stopwatch.java b/qilin.util/src/qilin/util/Stopwatch.java index 789fd6b..2e21ab9 100644 --- a/qilin.util/src/qilin/util/Stopwatch.java +++ b/qilin.util/src/qilin/util/Stopwatch.java @@ -25,7 +25,7 @@ public class Stopwatch { private boolean inCounting; public static Stopwatch newAndStart(final String name) { - Stopwatch stopwatch = new Stopwatch(name); + Stopwatch stopwatch = new Stopwatch(name); stopwatch.start(); return stopwatch; } diff --git a/qilin.util/src/qilin/util/Util.java b/qilin.util/src/qilin/util/Util.java index 4bcb422..bb94314 100644 --- a/qilin.util/src/qilin/util/Util.java +++ b/qilin.util/src/qilin/util/Util.java @@ -22,7 +22,6 @@ import org.slf4j.LoggerFactory; import java.io.*; -import java.util.HashSet; import java.util.Map; import java.util.Set; import java.util.regex.Pattern; @@ -43,7 +42,7 @@ public static byte[] toUtf8(String s) { } public static boolean addToMap(Map> map, K key, V value) { - return map.computeIfAbsent(key, k -> new HashSet<>()).add(value); + return map.computeIfAbsent(key, k -> DataFactory.createSet()).add(value); } public static boolean removeFromMap(Map> map, K key, V value) { diff --git a/qilin.util/src/qilin/util/graph/DirectedGraph.java b/qilin.util/src/qilin/util/graph/DirectedGraph.java index d077004..f2f26e4 100644 --- a/qilin.util/src/qilin/util/graph/DirectedGraph.java +++ b/qilin.util/src/qilin/util/graph/DirectedGraph.java @@ -19,6 +19,10 @@ package qilin.util.graph; import java.util.Collection; +import java.util.HashSet; +import java.util.Set; +import java.util.Stack; +import java.util.stream.Collectors; public interface DirectedGraph { Collection allNodes(); @@ -26,4 +30,26 @@ public interface DirectedGraph { Collection predsOf(final N p); Collection succsOf(final N p); + + /* no cache, very slow.*/ + default Collection computeReachableNodes(N source) { + Set reachableNodes = new HashSet<>(); + Stack stack = new Stack<>(); + stack.push(source); + while (!stack.isEmpty()) { + N node = stack.pop(); + if (reachableNodes.add(node)) { + stack.addAll(succsOf(node)); + } + } + return reachableNodes; + } + + default Collection computeRootNodes() { + return allNodes().stream().filter(node -> predsOf(node).size() == 0).collect(Collectors.toSet()); + } + + default Collection computeTailNodes() { + return allNodes().stream().filter(node -> succsOf(node).size() == 0).collect(Collectors.toSet()); + } }