>());
243 | }
244 | contexts.get(method).add(context);
245 |
246 | // Push this context on the top of the analysis stack.
247 | analysisStack.add(context);
248 |
249 | }
250 |
251 | /**
252 | * Processes a call statement.
253 | *
254 | *
255 | * Retrieves a value context for the callee if one exists with the given
256 | * entry value, or else creates a new one and adds the transition to
257 | * the context transition table.
258 | *
259 | *
260 | *
261 | * If the callee context has already been analysed, returns the resulting
262 | * exit value. For newly created contexts the result would be null,
263 | * as they are obviously not analysed even once.
264 | *
265 | *
266 | *
267 | * Note that this method is not directly called by {@link #doAnalysis() doAnalysis}, but
268 | * is instead called by {@link #flowFunction(Context, Object, Object) flowFunction} when a method
269 | * call statement is encountered. The reason for this design decision is
270 | * that client analyses may want their own setup and tear down sequences
271 | * before a call is made (similar to edge flow functions at the call and
272 | * return site). Also, analyses may want to choose which method call to
273 | * process at an invoke statement in the case of virtual calls (e.g. a
274 | * points-to analysis may build the call-graph on-the-fly).
275 | *
276 | *
277 | * Therefore, it is the responsibility of the client analysis to detect
278 | * an invoke expression when implementing {@link #flowFunction(Context, Object, Object) flowFunction},
279 | * and suitably invoke {@link #processCall(Context, Object, Object, Object) processCall} with
280 | * the input data flow value which may be different from the IN/OUT value of the node due to
281 | * handling of arguments, etc. Similarly, the result of {@link #processCall(Context, Object, Object, Object) processCall}
282 | * may be modified by the client to handle return values, etc. before returning from {@link #flowFunction(Context, Object, Object) flowFunction}.
283 | * Ideally, {@link #flowFunction(Context, Object, Object) flowFunction} should return
284 | * null if and only if {@link #processCall(Context, Object, Object, Object) processCall}
285 | * returns null.
286 | *
287 | * @param callerContext the analysis context at the call-site
288 | * @param callNode the calling statement
289 | * @param method the method being called
290 | * @param entryValue the data flow value at the entry of the called method.
291 | * @return the data flow value at the exit of the called method,
292 | * if available, or null if unavailable.
293 | */
294 | protected A processCall(Context callerContext, N callNode, M method, A entryValue) {
295 | CallSite callSite = new CallSite(callerContext, callNode);
296 |
297 | // Check if the called method has a context associated with this entry flow:
298 | Context calleeContext = getContext(method, entryValue);
299 | // If not, then set 'calleeContext' to a new context with the given entry flow.
300 | if (calleeContext == null) {
301 | calleeContext = new Context(method, programRepresentation().getControlFlowGraph(method), false);
302 | initContext(calleeContext, entryValue);
303 | if (verbose) {
304 | System.out.println("[NEW] X" + callerContext + " -> X" + calleeContext + " " + method + " ");
305 | }
306 | }
307 |
308 | // Store the transition from the calling context and site to the called context.
309 | contextTransitions.addTransition(callSite, calleeContext);
310 |
311 | // Check if 'caleeContext' has been analysed (surely not if it is just newly made):
312 | if (calleeContext.isAnalysed()) {
313 | if (verbose) {
314 | System.out.println("[HIT] X" + callerContext + " -> X" + calleeContext + " " + method + " ");
315 | }
316 | // If yes, then return the 'exitFlow' of the 'calleeContext'.
317 | return calleeContext.getExitValue();
318 | } else {
319 | // If not, then return 'null'.
320 | return null;
321 | }
322 | }
323 |
324 | protected abstract A flowFunction(Context context, N unit, A in);
325 |
326 | }
327 |
--------------------------------------------------------------------------------
/src/main/java/vasco/ProgramRepresentation.java:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright (C) 2013 Rohan Padhye
3 | *
4 | * This library is free software: you can redistribute it and/or modify
5 | * it under the terms of the GNU Lesser General Public License as
6 | * published by the Free Software Foundation, either version 2.1 of the
7 | * License, or (at your option) any later version.
8 | *
9 | * This library is distributed in the hope that it will be useful,
10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 | * GNU Lesser General Public License for more details.
13 | *
14 | * You should have received a copy of the GNU Lesser General Public License
15 | * along with this program. If not, see .
16 | *
17 | */
18 | package vasco;
19 |
20 | import java.util.List;
21 |
22 | import soot.toolkits.graph.DirectedGraph;
23 |
24 | /**
25 | * A wrapper for the API used by the underlying
26 | * intermediate representation over which inter-procedural analysis is to be
27 | * performed.
28 | *
29 | * In particular, the program representation should specify program entry
30 | * points, build control flow graphs for a given method and resolve virtual
31 | * method calls.
32 | *
33 | * @author Rohan Padhye
34 | *
35 | * @param the type of a method
36 | * @param the type of a node in the CFG
37 | */
38 | public interface ProgramRepresentation {
39 |
40 | /**
41 | * Returns a list of program entry points (methods). The entry points
42 | * may be static or non-static.
43 | *
44 | * Client analyses implementing an {@link InterProceduralAnalysis}
45 | * must implement the
46 | * {@link InterProceduralAnalysis#boundaryValue(Object) boundaryValue} method
47 | * for each entry point specified by the program representation.
48 | *
49 | * @return a list of program entry points (methods)
50 | */
51 | public List getEntryPoints();
52 |
53 | /**
54 | * Returns an intra-procedural control-flow-graph for a given procedure (method).
55 | *
56 | * The returned CFG may include exceptional control transfer in addition
57 | * to conditional and unconditional jumps, but does not include inter-procedural
58 | * call/return edges. Nodes containing method calls are treated like nodes
59 | * containing ordinary imperative instructions.
60 | *
61 | * @param method the method whose CFG to return
62 | * @return an intra-procedural control-flow-graph for a given procedure (method)
63 | */
64 | public DirectedGraph getControlFlowGraph(M method);
65 |
66 | /**
67 | * Returns whether a given node contains a method call.
68 | * @param node a node in the control-flow graph
69 | * @return whether a given node contains a method call.
70 | */
71 | public boolean isCall(N node);
72 |
73 | public boolean isPhantomMethod(M method);
74 | /**
75 | * Returns a list of target methods for call in the given node.
76 | *
77 | *
78 | * For static methods and special invocations (such as constructors), there
79 | * will be only one target, and hence a singleton list will be returned.
80 | *
81 | *
82 | *
83 | * For virtual calls, there may be multiple targets which are resolved using
84 | * an available call graph.
85 | *
86 | *
87 | *
88 | * If even a single target does not have an analysable method body (e.g. native
89 | * methods in Java), then null
is returned, to indicate that the
90 | * targets cannot be properly resolved.
91 | * TODO: Native method flow functions?
92 | *
93 | *
94 | * @param callerMethod the method in which the call statement originates
95 | * @param callNode the node containing the call statement
96 | * @return a list of methods which are the target of this call, if their bodies
97 | * are available, or else null
in the case of native targets
98 | */
99 | public List resolveTargets(M callerMethod, N callNode);
100 |
101 | }
102 |
--------------------------------------------------------------------------------
/src/main/java/vasco/callgraph/CallGraphTransformer.java:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright (C) 2013 Rohan Padhye
3 | *
4 | * This library is free software: you can redistribute it and/or modify
5 | * it under the terms of the GNU Lesser General Public License as
6 | * published by the Free Software Foundation, either version 2.1 of the
7 | * License, or (at your option) any later version.
8 | *
9 | * This library is distributed in the hope that it will be useful,
10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 | * GNU Lesser General Public License for more details.
13 | *
14 | * You should have received a copy of the GNU Lesser General Public License
15 | * along with this program. If not, see .
16 | *
17 | */
18 | package vasco.callgraph;
19 |
20 | import java.util.ArrayList;
21 | import java.util.Collection;
22 | import java.util.HashMap;
23 | import java.util.Iterator;
24 | import java.util.Map;
25 | import java.util.Set;
26 |
27 | import soot.Kind;
28 | import soot.Scene;
29 | import soot.SceneTransformer;
30 | import soot.SootMethod;
31 | import soot.Unit;
32 | import soot.jimple.InvokeExpr;
33 | import soot.jimple.Stmt;
34 | import soot.jimple.toolkits.callgraph.CallGraph;
35 | import soot.jimple.toolkits.callgraph.ContextSensitiveCallGraph;
36 | import soot.jimple.toolkits.callgraph.ContextSensitiveEdge;
37 | import soot.jimple.toolkits.callgraph.Edge;
38 | import vasco.CallSite;
39 | import vasco.Context;
40 | import vasco.ContextTransitionTable;
41 |
42 | /**
43 | * A Soot {@link SceneTransformer} for performing {@link PointsToAnalysis}.
44 | *
45 | * @author Rohan Padhye
46 | */
47 | public class CallGraphTransformer extends SceneTransformer {
48 |
49 | private PointsToAnalysis pointsToAnalysis;
50 |
51 | /**
52 | * {@inheritDoc}
53 | */
54 | @SuppressWarnings("deprecation")
55 | @Override
56 | protected void internalTransform(String arg0, @SuppressWarnings("rawtypes") Map arg1) {
57 | // Perform the points-to analysis
58 | pointsToAnalysis = new PointsToAnalysis();
59 | pointsToAnalysis.doAnalysis();
60 |
61 | // Use the context transition table generated by the analysis to construct soot call graphs
62 | final ContextTransitionTable ctt = pointsToAnalysis.getContextTransitionTable();
63 |
64 |
65 | // Initialize collections (for creating the soot context-sensitive call graph)
66 | final Set allMethods = pointsToAnalysis.getMethods();
67 | final Map, Collection> csEdgesIntoContext = new HashMap, Collection>();
68 | final Map, Collection> csEdgesOutOfContext = new HashMap, Collection>();
69 | final Map, Collection> csEdgesOutOfCallSite = new HashMap, Collection>();
70 | final Collection csEdges = new ArrayList();
71 |
72 | // Initialize the context-insensitive call graph
73 | CallGraph callGraph = new CallGraph();
74 |
75 | // Create soot-style edges for every context transition
76 | for (Map.Entry, Map>> e : ctt.getTransitions().entrySet()) {
77 | CallSite cs = e.getKey();
78 | final Context sourceContext = cs.getCallingContext();
79 | final SootMethod sourceMethod = sourceContext.getMethod();
80 | final Stmt stmt = (Stmt) cs.getCallNode();
81 | final Map> targets = e.getValue();
82 | for (final SootMethod targetMethod : targets.keySet()) {
83 | final Context targetContext = targets.get(targetMethod);
84 |
85 | Kind k;
86 | if ("".equals(targetMethod.getName())) {
87 | k = Kind.CLINIT;
88 | } else if (stmt.containsInvokeExpr()) {
89 | k = Edge.ieToKind(stmt.getInvokeExpr());
90 | } else {
91 | k = Kind.INVALID;
92 | }
93 |
94 | // The context-insensitive edge
95 | Edge cgEdge = new Edge(sourceMethod, stmt, targetMethod, k);
96 |
97 | // Add it to the context-insensitive call-graph
98 | callGraph.addEdge(cgEdge);
99 |
100 | // The context-sensitive edge
101 | ContextSensitiveEdge csEdge = new ContextSensitiveEdge() {
102 |
103 | @Override
104 | public Kind kind() {
105 | if ("".equals(targetMethod.getName())) {
106 | return Kind.CLINIT;
107 | } else if (stmt.containsInvokeExpr()) {
108 | return Edge.ieToKind(stmt.getInvokeExpr());
109 | } else {
110 | return Kind.INVALID;
111 | }
112 | }
113 |
114 | @Override
115 | public SootMethod src() {
116 | return sourceMethod;
117 | }
118 |
119 | @Override
120 | public soot.Context srcCtxt() {
121 | return sourceContext;
122 | }
123 |
124 | @Override
125 | public Stmt srcStmt() {
126 | return (Stmt) stmt;
127 | }
128 |
129 | @Override
130 | public Unit srcUnit() {
131 | return stmt;
132 | }
133 |
134 | @Override
135 | public SootMethod tgt() {
136 | return targetMethod;
137 | }
138 |
139 | @Override
140 | public soot.Context tgtCtxt() {
141 | return targetContext;
142 | }
143 |
144 | };
145 |
146 | // Add this in all the collections
147 | csEdges.add(csEdge);
148 |
149 | if (!csEdgesOutOfContext.containsKey(sourceContext))
150 | csEdgesOutOfContext.put(sourceContext, new ArrayList());
151 | csEdgesOutOfContext.get(sourceContext).add(csEdge);
152 |
153 | if (!csEdgesOutOfCallSite.containsKey(cs))
154 | csEdgesOutOfCallSite.put(cs, new ArrayList());
155 | csEdgesOutOfCallSite.get(cs).add(csEdge);
156 |
157 | if (!csEdgesIntoContext.containsKey(targetContext))
158 | csEdgesIntoContext.put(targetContext, new ArrayList());
159 | csEdgesIntoContext.get(targetContext).add(csEdge);
160 |
161 |
162 |
163 | }
164 |
165 | }
166 |
167 | // Set the scene's context-insensitive call-graph to what we just created
168 | Scene.v().setCallGraph(callGraph);
169 |
170 | // Set the scene's context-sensitive call graph to one that we construct on-the-fly using the above collections
171 | Scene.v().setContextSensitiveCallGraph(new ContextSensitiveCallGraph() {
172 |
173 | @SuppressWarnings("unchecked")
174 | private Context vContext(soot.Context sContext) {
175 | return (Context) sContext;
176 | }
177 |
178 | private CallSite vCallSite(soot.Context sContext, Unit unit) {
179 | return new CallSite(vContext(sContext), unit);
180 | }
181 |
182 | @Override
183 | public Iterator> edgesOutOf(soot.Context sContext, SootMethod m, Unit stmt) {
184 | return csEdgesOutOfCallSite.get((vCallSite(sContext, stmt))).iterator();
185 | }
186 |
187 | @Override
188 | public Iterator> edgesOutOf(soot.Context sContext, SootMethod m) {
189 | return csEdgesOutOfContext.get(vContext(sContext)).iterator();
190 | }
191 |
192 | @Override
193 | public Iterator> edgesInto(soot.Context sContext, SootMethod m) {
194 | return csEdgesIntoContext.get(vContext(sContext)).iterator();
195 | }
196 |
197 | @Override
198 | public Iterator> edgeSources() {
199 | return allMethods.iterator();
200 | }
201 |
202 | @Override
203 | public Iterator> allEdges() {
204 | return csEdges.iterator();
205 | }
206 | });
207 |
208 | }
209 |
210 | /**
211 | * Returns a reference to the {@link PointsToAnalysis} object.
212 | * @return a reference to the {@link PointsToAnalysis} object
213 | */
214 | public PointsToAnalysis getPointsToAnalysis() {
215 | return pointsToAnalysis;
216 | }
217 |
218 |
219 |
220 | }
221 |
--------------------------------------------------------------------------------
/src/main/java/vasco/callgraph/PointsToAnalysis.java:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright (C) 2013 Rohan Padhye
3 | *
4 | * This library is free software: you can redistribute it and/or modify
5 | * it under the terms of the GNU Lesser General Public License as
6 | * published by the Free Software Foundation, either version 2.1 of the
7 | * License, or (at your option) any later version.
8 | *
9 | * This library is distributed in the hope that it will be useful,
10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 | * GNU Lesser General Public License for more details.
13 | *
14 | * You should have received a copy of the GNU Lesser General Public License
15 | * along with this program. If not, see .
16 | *
17 | */
18 | package vasco.callgraph;
19 |
20 |
21 | import java.util.Collections;
22 | import java.util.HashSet;
23 | import java.util.Set;
24 |
25 | import soot.Local;
26 | import soot.RefLikeType;
27 | import soot.RefType;
28 | import soot.Scene;
29 | import soot.SootClass;
30 | import soot.SootField;
31 | import soot.SootMethod;
32 | import soot.Unit;
33 | import soot.Value;
34 | import soot.jimple.AnyNewExpr;
35 | import soot.jimple.ArrayRef;
36 | import soot.jimple.AssignStmt;
37 | import soot.jimple.CastExpr;
38 | import soot.jimple.CaughtExceptionRef;
39 | import soot.jimple.Constant;
40 | import soot.jimple.DefinitionStmt;
41 | import soot.jimple.IdentityRef;
42 | import soot.jimple.InstanceFieldRef;
43 | import soot.jimple.InstanceInvokeExpr;
44 | import soot.jimple.IntConstant;
45 | import soot.jimple.InterfaceInvokeExpr;
46 | import soot.jimple.InvokeExpr;
47 | import soot.jimple.InvokeStmt;
48 | import soot.jimple.NewArrayExpr;
49 | import soot.jimple.ReturnStmt;
50 | import soot.jimple.SpecialInvokeExpr;
51 | import soot.jimple.StaticFieldRef;
52 | import soot.jimple.StaticInvokeExpr;
53 | import soot.jimple.Stmt;
54 | import soot.jimple.VirtualInvokeExpr;
55 | import soot.jimple.internal.JNewArrayExpr;
56 | import vasco.CallSite;
57 | import vasco.Context;
58 | import vasco.OldForwardInterProceduralAnalysis;
59 | import vasco.ProgramRepresentation;
60 | import vasco.soot.DefaultJimpleRepresentation;
61 |
62 | /**
63 | * An inter-procedural analysis for constructing a context-sensitive call graph
64 | * on-the-fly.
65 | *
66 | * This analysis uses the value-based context-sensitive inter-procedural framework
67 | * and was developed as an example instantiation of the framework.
68 | *
69 | * The analysis uses {@link PointsToGraph} objects as data flow values, which
70 | * in turn abstracts heap locations using allocation sites.
71 | *
72 | *
Warning! The current implementation of this class uses
73 | * the old API (see {@link OldForwardInterProceduralAnalysis}) without separate
74 | * call/return flow functions. The developer is currently in the process of migrating
75 | * this implementation to the new API (see {@link vasco.ForwardInterProceduralAnalysis ForwardInterProceduralAnalysis}).
76 | *
77 | * @author Rohan Padhye
78 | */
79 | public class PointsToAnalysis extends OldForwardInterProceduralAnalysis {
80 |
81 | private static final SootMethod DUMMY_METHOD = new SootMethod("DUMMY_METHOD", Collections.EMPTY_LIST, Scene.v().getObjectType());
82 |
83 | /**
84 | * A shared points-to graph that maintains information about objects
85 | * reachable from static fields (modelled as fields of a dummy global variable).
86 | *
87 | * For static load/store statements, we union this points-to graph with the
88 | * points-to graph in the flow function, perform the operation, and then
89 | * separate stuff out again.
90 | */
91 | private PointsToGraph staticHeap;
92 |
93 | /**
94 | * A set of classes whose static initialisers have been processed.
95 | */
96 | private Set clinitCalled;
97 |
98 | /**
99 | * Constructs a new points-to analysis as a forward-flow inter-procedural
100 | * analysis.
101 | */
102 | public PointsToAnalysis() {
103 | super();
104 |
105 | // Play around with these flags
106 | this.freeResultsOnTheFly = true;
107 | this.verbose = true;
108 |
109 | // No classes statically initialised yet
110 | this.clinitCalled = new HashSet();
111 |
112 | // Create a static points-to graph with a single "global" root object
113 | this.staticHeap = topValue();
114 | this.staticHeap.assignNew(PointsToGraph.GLOBAL_LOCAL, PointsToGraph.GLOBAL_SITE);
115 | }
116 |
117 | /**
118 | * Returns a points-to graph with the locals of main initialised to
119 | * null, except the command-line arguments which are
120 | * initialised to an array of strings.
121 | */
122 | @Override
123 | public PointsToGraph boundaryValue(SootMethod entryPoint) {
124 | // For now we only support entry to the main method
125 | assert(entryPoint == Scene.v().getMainMethod());
126 |
127 | // Ok, start setting up entry value
128 | PointsToGraph entryValue = new PointsToGraph();
129 |
130 | // Locals of main... (only reference types)
131 | SootMethod mainMethod = Scene.v().getMainMethod();
132 | for (Local local : mainMethod.getActiveBody().getLocals()) {
133 | if (local.getType() instanceof RefLikeType) {
134 | entryValue.assign(local, null);
135 | }
136 | }
137 |
138 | // Command-line arguments to main...
139 | Local argsLocal = mainMethod.getActiveBody().getParameterLocal(0);
140 | NewArrayExpr argsExpr = new JNewArrayExpr(Scene.v().getRefType("java.lang.String"), IntConstant.v(0));
141 | entryValue.assignNew(argsLocal, argsExpr);
142 | entryValue.setFieldConstant(argsLocal, PointsToGraph.ARRAY_FIELD, PointsToGraph.STRING_CONST);
143 |
144 |
145 | return entryValue;
146 | }
147 |
148 | /**
149 | * Returns a copy of the given points-to graph.
150 | */
151 | @Override
152 | public PointsToGraph copy(PointsToGraph graph) {
153 | return new PointsToGraph(graph);
154 | }
155 |
156 | /**
157 | * Performs operations on points-to graphs depending on the statement inside
158 | * a CFG node.
159 | */
160 | @Override
161 | protected PointsToGraph flowFunction(Context context, Unit unit, PointsToGraph in)
162 | {
163 |
164 | // First set OUT to copy of IN (this is default for most statements).
165 | PointsToGraph out = new PointsToGraph(in);
166 |
167 | // This analysis is written assuming that units are statements (and not,
168 | // for example, basic blocks)
169 | assert (unit instanceof Stmt);
170 | Stmt stmt = (Stmt) unit;
171 |
172 | // What kind of statement?
173 | if (stmt instanceof DefinitionStmt) {
174 | // Assignment of LHS to an RHS
175 | Value lhsOp = ((DefinitionStmt) stmt).getLeftOp();
176 | Value rhsOp = ((DefinitionStmt) stmt).getRightOp();
177 |
178 | // Invoke static initialisers if static members accessed
179 | // for the first time
180 | StaticFieldRef staticReference = null;
181 | if (lhsOp instanceof StaticFieldRef) {
182 | staticReference = ((StaticFieldRef) lhsOp);
183 | } else if (rhsOp instanceof StaticFieldRef) {
184 | staticReference = ((StaticFieldRef) rhsOp);
185 | }
186 | if (staticReference != null) {
187 | SootClass declaringClass = staticReference.getField().getDeclaringClass();
188 | if (clinitCalled.contains(declaringClass) == false) {
189 | clinitCalled.add(declaringClass);
190 | // Don't initialise library classes
191 | if (declaringClass.isLibraryClass()) {
192 | // Set all static fields to null
193 | for (SootField field : declaringClass.getFields()) {
194 | // Only for static reference fields
195 | if (field.isStatic() && field.getType() instanceof RefLikeType) {
196 | staticHeap.setFieldSummary(PointsToGraph.GLOBAL_LOCAL, field);
197 | }
198 | }
199 | } else {
200 | // We have to initialise this class...
201 | if (declaringClass.declaresMethodByName("")) {
202 | // Get the static initialisation method
203 | SootMethod clinit = declaringClass.getMethodByName("");
204 | // At its entry use a blank value (with STICKY to avoid TOP termination)
205 | PointsToGraph clinitEntryValue = topValue();
206 | clinitEntryValue.assign(PointsToGraph.STICKY_LOCAL, null);
207 | // Make the call!
208 | this.processCall(context, stmt, clinit,clinitEntryValue);
209 | // Do not process this statement now, wait for clinit to return
210 | // and this statement as a "return site"
211 | return null;
212 | }
213 | // If no defined for this class, then continue as normal :-)
214 | }
215 | }
216 | }
217 |
218 |
219 | // Handle statement depending on type
220 | if (lhsOp.getType() instanceof RefLikeType) {
221 | // Both LHS and RHS are RefLikeType
222 | if (lhsOp instanceof InstanceFieldRef || lhsOp instanceof ArrayRef) {
223 | // SETFIELD
224 | Local lhs = (Local)(lhsOp instanceof InstanceFieldRef ?
225 | ((InstanceFieldRef) lhsOp).getBase() :
226 | ((ArrayRef) lhsOp).getBase());
227 | SootField field = lhsOp instanceof InstanceFieldRef ?
228 | ((InstanceFieldRef) lhsOp).getField() : PointsToGraph.ARRAY_FIELD;
229 |
230 | // RHS can be a local or constant (string, class, null)
231 | if (rhsOp instanceof Local) {
232 | Local rhs = (Local) rhsOp;
233 | out.setField(lhs, field, rhs);
234 | } else if (rhsOp instanceof Constant) {
235 | Constant rhs = (Constant) rhsOp;
236 | out.setFieldConstant(lhs, field, rhs);
237 | } else {
238 | throw new RuntimeException(rhsOp.toString());
239 | }
240 | } else if (rhsOp instanceof InstanceFieldRef || rhsOp instanceof ArrayRef) {
241 | // GETFIELD
242 | Local rhs = (Local)(rhsOp instanceof InstanceFieldRef ?
243 | ((InstanceFieldRef) rhsOp).getBase() :
244 | ((ArrayRef) rhsOp).getBase());
245 | SootField field = rhsOp instanceof InstanceFieldRef ?
246 | ((InstanceFieldRef) rhsOp).getField() : PointsToGraph.ARRAY_FIELD;
247 |
248 | // LHS has to be local
249 | if (lhsOp instanceof Local) {
250 | Local lhs = (Local) lhsOp;
251 | out.getField(lhs, rhs, field);
252 | } else {
253 | throw new RuntimeException(lhsOp.toString());
254 | }
255 | } else if (rhsOp instanceof AnyNewExpr) {
256 | // NEW, NEWARRAY or NEWMULTIARRAY
257 | AnyNewExpr anyNewExpr = (AnyNewExpr) rhsOp;
258 | if (lhsOp instanceof Local) {
259 | Local lhs = (Local) lhsOp;
260 | out.assignNew(lhs, anyNewExpr);
261 | } else {
262 | throw new RuntimeException(lhsOp.toString());
263 | }
264 | } else if (rhsOp instanceof InvokeExpr) {
265 | // STATICINVOKE, SPECIALINVOKE, VIRTUALINVOKE or INTERFACEINVOKE
266 | InvokeExpr expr = (InvokeExpr) rhsOp;
267 | // Handle method invocation!
268 | out = handleInvoke(context, stmt, expr, in);
269 | } else if (lhsOp instanceof StaticFieldRef) {
270 | // Get parameters
271 | SootField staticField = ((StaticFieldRef) lhsOp).getField();
272 | // Temporarily union locals and globals
273 | PointsToGraph tmp = topValue();
274 | tmp.union(out, staticHeap);
275 | // Store RHS into static field
276 | if (rhsOp instanceof Local) {
277 | Local rhsLocal = (Local) rhsOp;
278 | tmp.setField(PointsToGraph.GLOBAL_LOCAL, staticField, rhsLocal);
279 | } else if (rhsOp instanceof Constant) {
280 | Constant rhsConstant = (Constant) rhsOp;
281 | tmp.setFieldConstant(PointsToGraph.GLOBAL_LOCAL, staticField, rhsConstant);
282 | } else {
283 | throw new RuntimeException(rhsOp.toString());
284 | }
285 | // Now get rid of all locals, params, etc.
286 | Set locals = new HashSet(tmp.roots.keySet());
287 | for (Local local : locals) {
288 | // Everything except the GLOBAL must go!
289 | if (local != PointsToGraph.GLOBAL_LOCAL) {
290 | tmp.kill(local);
291 | }
292 | }
293 | // Global information is updated!
294 | staticHeap = tmp;
295 |
296 | } else if (rhsOp instanceof StaticFieldRef) {
297 | // Get parameters
298 | Local lhsLocal = (Local) lhsOp;
299 | SootField staticField = ((StaticFieldRef) rhsOp).getField();
300 | // Temporarily union locals and globals
301 | PointsToGraph tmp = topValue();
302 | tmp.union(out, staticHeap);
303 | // Load static field into LHS local
304 | tmp.getField(lhsLocal, PointsToGraph.GLOBAL_LOCAL, staticField);
305 | // Now get rid of globals that we do not care about
306 | tmp.kill(PointsToGraph.GLOBAL_LOCAL);
307 | // Local information is updated!
308 | out = tmp;
309 |
310 | } else if (rhsOp instanceof CaughtExceptionRef) {
311 | Local lhs = (Local) lhsOp;
312 | out.assignSummary(lhs);
313 | } else if (rhsOp instanceof IdentityRef) {
314 | // Ignore identities
315 | } else if (lhsOp instanceof Local) {
316 | // Assignment
317 | Local lhs = (Local) lhsOp;
318 | // RHS op is a local, constant or class cast
319 | if (rhsOp instanceof Local) {
320 | Local rhs = (Local) rhsOp;
321 | out.assign(lhs, rhs);
322 | } else if (rhsOp instanceof Constant) {
323 | Constant rhs = (Constant) rhsOp;
324 | out.assignConstant(lhs, rhs);
325 | } else if (rhsOp instanceof CastExpr) {
326 | Value op = ((CastExpr) rhsOp).getOp();
327 | if (op instanceof Local) {
328 | Local rhs = (Local) op;
329 | out.assign(lhs, rhs);
330 | } else if (op instanceof Constant) {
331 | Constant rhs = (Constant) op;
332 | out.assignConstant(lhs, rhs);
333 | } else {
334 | throw new RuntimeException(op.toString());
335 | }
336 | } else {
337 | throw new RuntimeException(rhsOp.toString());
338 | }
339 | } else {
340 | throw new RuntimeException(unit.toString());
341 | }
342 | } else if (rhsOp instanceof InvokeExpr) {
343 | // For non-reference types, only method invocations are important
344 | InvokeExpr expr = (InvokeExpr) rhsOp;
345 | // Handle method invocation!
346 | out = handleInvoke(context, stmt, expr, in);
347 | }
348 |
349 | } else if (stmt instanceof InvokeStmt) {
350 | // INVOKE without a return
351 | InvokeExpr expr = stmt.getInvokeExpr();
352 | // Handle method invocation!
353 | out = handleInvoke(context, stmt, expr, in);
354 | } else if (stmt instanceof ReturnStmt) {
355 | // Returning a value (not return-void as those are of type ReturnVoidStmt)
356 | Value op = ((ReturnStmt) stmt).getOp();
357 | Local lhs = PointsToGraph.RETURN_LOCAL;
358 | // We only care about reference-type returns
359 | if (op.getType() instanceof RefLikeType) {
360 | // We can return a local or a constant
361 | if (op instanceof Local) {
362 | Local rhs = (Local) op;
363 | out.assign(lhs, rhs);
364 | } else if (op instanceof Constant) {
365 | Constant rhs = (Constant) op;
366 | out.assignConstant(lhs, rhs);
367 | } else {
368 | throw new RuntimeException(op.toString());
369 | }
370 | }
371 | }
372 |
373 | return out;
374 |
375 | }
376 |
377 |
378 | /**
379 | * Computes the targets of an invoke expression using a given points-to graph.
380 | *
381 | * For static invocations, there is only target. For instance method
382 | * invocations, the targets depend on the type of receiver objects pointed-to
383 | * by the instance variable whose method is being invoked.
384 | *
385 | * If the instance variable points to a summary node, then the returned
386 | * value is null signifying a default call-site.
387 | */
388 | private Set getTargets(SootMethod callerMethod, Stmt callStmt, InvokeExpr ie, PointsToGraph ptg) {
389 | Set targets = new HashSet();
390 | SootMethod invokedMethod = ie.getMethod();
391 | String subsignature = invokedMethod.getSubSignature();
392 |
393 | // Static and special invocations refer to the target method directly
394 | if (ie instanceof StaticInvokeExpr || ie instanceof SpecialInvokeExpr) {
395 | targets.add(invokedMethod);
396 | return targets;
397 | } else {
398 | assert (ie instanceof InterfaceInvokeExpr || ie instanceof VirtualInvokeExpr);
399 | // Get the receiver
400 | Local receiver = (Local) ((InstanceInvokeExpr) ie).getBase();
401 | // Get what objects the receiver points-to
402 | Set heapNodes = ptg.getTargets(receiver);
403 | if (heapNodes != null) {
404 | // For each object, find the invoked method for the declared type
405 | for (AnyNewExpr heapNode : heapNodes) {
406 | if (heapNode == PointsToGraph.SUMMARY_NODE) {
407 | // If even one pointee is a summary node, then this is a default site
408 | return null;
409 | } else if (heapNode instanceof NewArrayExpr) {
410 | // Probably getClass() or something like that on an array
411 | return null;
412 | }
413 | // Find the top-most class that declares a method with the given
414 | // signature and add it to the resulting targets
415 | SootClass sootClass = ((RefType) heapNode.getType()).getSootClass();
416 | do {
417 | if (sootClass.declaresMethod(subsignature)) {
418 | targets.add(sootClass.getMethod(subsignature));
419 | break;
420 | } else if (sootClass.hasSuperclass()) {
421 | sootClass = sootClass.getSuperclass();
422 | } else {
423 | sootClass = null;
424 | }
425 | } while (sootClass != null);
426 | }
427 | }
428 | if (targets.isEmpty()) {
429 | // System.err.println("Warning! Null call at: " + callStmt+ " in " + callerMethod);
430 | }
431 | return targets;
432 | }
433 | }
434 |
435 |
436 | private Set getDummyTarget() {
437 | Set targets = new HashSet();
438 | targets.add(DUMMY_METHOD);
439 | return targets;
440 | }
441 |
442 | /**
443 | * Handles a call site by resolving the targets of the method invocation.
444 | *
445 | * The resultant flow is the union of the exit flows of all the analysed
446 | * callees. If the method returns a reference-like value, this is also taken
447 | * into account.
448 | */
449 | protected PointsToGraph handleInvoke(Context callerContext, Stmt callStmt,
450 | InvokeExpr ie, PointsToGraph in) {
451 | // Get the caller method
452 | SootMethod callerMethod = callerContext.getMethod();
453 | // Initialise the final result as TOP first
454 | PointsToGraph resultFlow = topValue();
455 |
456 | // If this statement is an assignment to an object, then set LHS for
457 | // RETURN values.
458 | Local lhs = null;
459 | Value lhsOp = null;
460 | if (callStmt instanceof AssignStmt) {
461 | lhsOp = ((AssignStmt) callStmt).getLeftOp();
462 | if (lhsOp.getType() instanceof RefLikeType) {
463 | lhs = (Local) lhsOp;
464 | }
465 | }
466 |
467 | // Find target methods for this call site (invoke expression) using the points-to data
468 | Set targets = getTargets(callerMethod, callStmt, ie, in);
469 |
470 | // If "targets" is null, that means the invoking instance was SUMMARY
471 | // So we use the DUMMY METHOD (which is a method with no body)
472 | if (targets == null) {
473 | targets = getDummyTarget();
474 | this.contextTransitions.addTransition(new CallSite(callerContext, callStmt), null);
475 | if (verbose) {
476 | System.out.println("[DEF] X" + callerContext + " -> DEFAULT " + ie.getMethod());
477 | }
478 | }
479 |
480 | // Make calls for all target methods
481 | for (SootMethod calledMethod : targets) {
482 |
483 | // The call-edge is obtained by assign parameters and THIS, and killing caller's locals
484 | PointsToGraph callEdge = copy(in);
485 | if (calledMethod.hasActiveBody()) {
486 | // We need to maintain a set of locals not to kill (in case the call is recursive)
487 | Set doNotKill = new HashSet();
488 |
489 | // Initialise sticky parameter
490 | callEdge.assign(PointsToGraph.STICKY_LOCAL, null);
491 | doNotKill.add(PointsToGraph.STICKY_LOCAL);
492 |
493 | // Assign this...
494 | if (ie instanceof InstanceInvokeExpr) {
495 | Local receiver = (Local)((InstanceInvokeExpr) ie).getBase();
496 | Local thisLocal = calledMethod.getActiveBody().getThisLocal();
497 | callEdge.assign(thisLocal, receiver);
498 | doNotKill.add(thisLocal);
499 | // Sticky it!
500 | callEdge.assignSticky(PointsToGraph.STICKY_LOCAL, thisLocal);
501 | }
502 |
503 | // Assign parameters...
504 | for (int i = 0; i < calledMethod.getParameterCount(); i++) {
505 | // Only for reference-like parameters
506 | if (calledMethod.getParameterType(i) instanceof RefLikeType) {
507 | Local parameter = calledMethod.getActiveBody().getParameterLocal(i);
508 | Value argValue = ie.getArg(i);
509 | // The argument can be a constant or local, so handle accordingly
510 | if (argValue instanceof Local) {
511 | Local argLocal = (Local) argValue;
512 | callEdge.assign(parameter, argLocal);
513 | doNotKill.add(parameter);
514 | // Sticky it!
515 | callEdge.assignSticky(PointsToGraph.STICKY_LOCAL, argLocal);
516 | } else if (argValue instanceof Constant) {
517 | Constant argConstant = (Constant) argValue;
518 | callEdge.assignConstant(parameter, argConstant);
519 | doNotKill.add(parameter);
520 | // No need to sticky constants as caller does not store them anyway
521 | } else {
522 | throw new RuntimeException(argValue.toString());
523 | }
524 | }
525 | }
526 |
527 | // Kill caller data...
528 | for (Local callerLocal : callerMethod.getActiveBody().getLocals()) {
529 | if (doNotKill.contains(callerLocal) == false)
530 | callEdge.kill(callerLocal);
531 | }
532 |
533 | // There should be no "return local", but we kill it anyway (just in case)
534 | callEdge.kill(PointsToGraph.RETURN_LOCAL);
535 |
536 | // Create callee locals..
537 | for (Local calleeLocal : calledMethod.getActiveBody().getLocals()) {
538 | if (calleeLocal.getType() instanceof RefLikeType
539 | && doNotKill.contains(calleeLocal) == false) {
540 | callEdge.assign(calleeLocal, null);
541 | }
542 | }
543 | }
544 |
545 | // The intra-procedural edge is the IN value minus the objects from the call edge
546 | PointsToGraph intraEdge = copy(in);
547 | if (lhs != null) {
548 | // Oh, and remove the LHS targets too
549 | intraEdge.assign(lhs, null);
550 | }
551 | //intraEdge.subtractHeap(callEdge);
552 |
553 | // Value at the start of the called procedure is
554 | // whatever went through the call edge
555 | PointsToGraph entryFlow = callEdge;
556 |
557 |
558 |
559 | // Make the call to this method!! (in case of body-less methods, no change)
560 | PointsToGraph exitFlow = calledMethod.hasActiveBody() ?
561 | processCall(callerContext, callStmt, calledMethod, entryFlow) : entryFlow;
562 |
563 | // If the called context was analysed, exitFlow will be set, else it
564 | // will be null.
565 | if (exitFlow != null) {
566 |
567 | // Propagate stuff from called procedure's exit to the caller's return-site
568 | PointsToGraph returnEdge = copy(exitFlow);
569 |
570 | // Two ways to handle this:
571 | if (calledMethod.hasActiveBody()) {
572 | // Kill all the called method's locals. That's right.
573 | for (Local calleeLocal : calledMethod.getActiveBody().getLocals()) {
574 | returnEdge.kill(calleeLocal);
575 | }
576 | // Remove the stickies (so not to interfere with stickies in the intra-edge)
577 | // but do not collect unreachable nodes
578 | returnEdge.killWithoutGC(PointsToGraph.STICKY_LOCAL);
579 |
580 | }
581 |
582 | // Let's unite the intra-edge with the return edge
583 | PointsToGraph callOut = topValue();
584 | callOut.union(intraEdge, returnEdge);
585 |
586 |
587 | // Now we are only left with the return value, if any
588 | if (calledMethod.hasActiveBody()) {
589 | if (lhs != null) {
590 | callOut.assign(lhs, PointsToGraph.RETURN_LOCAL);
591 | }
592 | // Kill the @return variable whether there was an LHS or not
593 | callOut.kill(PointsToGraph.RETURN_LOCAL);
594 | } else {
595 | // Handle returned objects for native methods
596 | if (lhs != null) {
597 | // If a string is returned, optimise
598 | if (lhs.getType().equals(PointsToGraph.STRING_CONST.getType())) {
599 | callOut.assignConstant(lhs, PointsToGraph.STRING_CONST);
600 | } else if (lhs.getType().equals(PointsToGraph.CLASS_CONST.getType())) {
601 | callOut.assignConstant(lhs, PointsToGraph.CLASS_CONST);
602 | } else {
603 | // Have to assume the worst!
604 | //System.err.println("Warning! Summary node returned at " +
605 | // callStmt + " in " + callerMethod);
606 | callOut.assignSummary(lhs);
607 | }
608 | }
609 | // Also assume that all parameters are modified
610 | for (int i = 0; i < calledMethod.getParameterCount(); i++) {
611 | // Only for reference-like parameters
612 | if (calledMethod.getParameterType(i) instanceof RefLikeType) {
613 | Value argValue = ie.getArg(i);
614 | // Summarize if the argument is local (i.e. not a constant)
615 | if (argValue instanceof Local) {
616 | Local argLocal = (Local) argValue;
617 | callOut.summarizeTargetFields(argLocal);
618 | }
619 | }
620 | }
621 | }
622 |
623 | // As we may have multiple virtual calls, merge the value at OUT
624 | // of this target's call-site with an accumulator (resultFlow)
625 | resultFlow = meet(resultFlow, callOut);
626 | }
627 | }
628 |
629 | // If at least one call succeeded, result flow is not TOP
630 | if (resultFlow.equals(topValue())) {
631 | return null;
632 | } else {
633 | return resultFlow;
634 | }
635 | }
636 |
637 |
638 |
639 |
640 |
641 | /**
642 | * Returns the union of two points-to graphs.
643 | */
644 | @Override
645 | public PointsToGraph meet(PointsToGraph op1, PointsToGraph op2) {
646 | PointsToGraph result = new PointsToGraph();
647 | result.union(op1, op2);
648 | return result;
649 | }
650 |
651 | /**
652 | * The default data flow value (lattice top) is the empty points-to graph.
653 | */
654 | @Override
655 | public PointsToGraph topValue() {
656 | return new PointsToGraph();
657 | }
658 |
659 | @Override
660 | public ProgramRepresentation programRepresentation() {
661 | return DefaultJimpleRepresentation.v();
662 | }
663 | }
664 |
--------------------------------------------------------------------------------
/src/main/java/vasco/soot/ContextSensitiveJimpleRepresentation.java:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright (C) 2013 Rohan Padhye
3 | *
4 | * This library is free software: you can redistribute it and/or modify
5 | * it under the terms of the GNU Lesser General Public License as
6 | * published by the Free Software Foundation, either version 2.1 of the
7 | * License, or (at your option) any later version.
8 | *
9 | * This library is distributed in the hope that it will be useful,
10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 | * GNU Lesser General Public License for more details.
13 | *
14 | * You should have received a copy of the GNU Lesser General Public License
15 | * along with this program. If not, see .
16 | *
17 | */
18 | package vasco.soot;
19 |
20 | import java.util.Collections;
21 | import java.util.HashMap;
22 | import java.util.Iterator;
23 | import java.util.LinkedList;
24 | import java.util.List;
25 | import java.util.Map;
26 |
27 | import soot.MethodContext;
28 | import soot.MethodOrMethodContext;
29 | import soot.Scene;
30 | import soot.SootMethod;
31 | import soot.Unit;
32 | import soot.jimple.toolkits.callgraph.ContextSensitiveEdge;
33 | import soot.toolkits.graph.DirectedGraph;
34 | import soot.toolkits.graph.ExceptionalUnitGraph;
35 | import vasco.ProgramRepresentation;
36 |
37 | /**
38 | * A program representation for Soot using the Jimple IR with a context-sensitive
39 | * call graph. This representation uses control-flow graphs of individual units including exceptional
40 | * control flow, and resolves virtual calls using the call graph returned by
41 | * {@link soot.Scene#getContextSensitiveCallGraph() Scene#getContextSensitiveCallGraph}.
42 | *
43 | * Note: This class follows the Singleton pattern. The singleton
44 | * object is available through {@link #v()}.
45 | *
46 | * @author Rohan Padhye
47 | *
48 | */
49 | public class ContextSensitiveJimpleRepresentation implements ProgramRepresentation {
50 |
51 | // Cache for control flow graphs
52 | private Map> cfgCache;
53 |
54 | // Private constructor, see #v() to retrieve singleton object
55 | private ContextSensitiveJimpleRepresentation() {
56 | cfgCache = new HashMap>();
57 | }
58 |
59 | /**
60 | * Returns a singleton list containing the main
method.
61 | * @see Scene#getMainMethod()
62 | */
63 | @Override
64 | public List getEntryPoints() {
65 | return Collections.singletonList(MethodContext.v(Scene.v().getMainMethod(), null));
66 | }
67 |
68 | /**
69 | * Returns an {@link ExceptionalUnitGraph} for a given method.
70 | */
71 | @Override
72 | public DirectedGraph getControlFlowGraph(MethodOrMethodContext momc) {
73 | if (cfgCache.containsKey(momc.method()) == false) {
74 | cfgCache.put(momc.method(), new ExceptionalUnitGraph(momc.method().getActiveBody()));
75 | }
76 | return cfgCache.get(momc.method());
77 | }
78 |
79 | /**
80 | * Returns true
iff the Jimple statement contains an
81 | * invoke expression.
82 | */
83 | @Override
84 | public boolean isCall(Unit node) {
85 | return ((soot.jimple.Stmt) node).containsInvokeExpr();
86 | }
87 |
88 | /**
89 | * Resolves virtual calls using the Soot's context-sensitive call graph and returns
90 | * a list of method-contexts which are the targets of explicit edges.
91 | */
92 | @Override
93 | public List resolveTargets(MethodOrMethodContext momc, Unit node) {
94 | List targets = new LinkedList();
95 | @SuppressWarnings("rawtypes")
96 | Iterator it = Scene.v().getContextSensitiveCallGraph().edgesOutOf(momc.context(), momc.method(), node);
97 | while(it.hasNext()) {
98 | ContextSensitiveEdge edge = (ContextSensitiveEdge) it.next();
99 | if (edge.kind().isExplicit()) {
100 | targets.add(MethodContext.v(edge.tgt(), edge.tgtCtxt()));
101 | }
102 | }
103 | return targets;
104 | }
105 |
106 | // The singleton object
107 | private static ContextSensitiveJimpleRepresentation singleton = new ContextSensitiveJimpleRepresentation();
108 |
109 | /**
110 | * Returns a reference to the singleton object of this class.
111 | */
112 | public static ContextSensitiveJimpleRepresentation v() { return singleton; }
113 |
114 | @Override
115 | public boolean isPhantomMethod(MethodOrMethodContext method) {
116 | // TODO Auto-generated method stub
117 | return false;
118 | }
119 |
120 | }
121 |
--------------------------------------------------------------------------------
/src/main/java/vasco/soot/DefaultJimpleRepresentation.java:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright (C) 2013 Rohan Padhye
3 | *
4 | * This library is free software: you can redistribute it and/or modify
5 | * it under the terms of the GNU Lesser General Public License as
6 | * published by the Free Software Foundation, either version 2.1 of the
7 | * License, or (at your option) any later version.
8 | *
9 | * This library is distributed in the hope that it will be useful,
10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 | * GNU Lesser General Public License for more details.
13 | *
14 | * You should have received a copy of the GNU Lesser General Public License
15 | * along with this program. If not, see .
16 | *
17 | */
18 | package vasco.soot;
19 |
20 | import java.util.Collections;
21 | import java.util.HashMap;
22 | import java.util.Iterator;
23 | import java.util.LinkedList;
24 | import java.util.List;
25 | import java.util.Map;
26 |
27 | import soot.Scene;
28 | import soot.SootMethod;
29 | import soot.Unit;
30 | import soot.jimple.toolkits.callgraph.Edge;
31 | import soot.toolkits.graph.DirectedGraph;
32 | import soot.toolkits.graph.ExceptionalUnitGraph;
33 | import vasco.ProgramRepresentation;
34 |
35 | /**
36 | * A default program representation for Soot using the Jimple IR. This
37 | * representation uses control-flow graphs of individual units including exceptional
38 | * control flow, and resolves virtual calls using the default context-insensitive
39 | * call graph.
40 | *
41 | * Note: This class follows the Singleton pattern. The singleton
42 | * object is available through {@link #v()}.
43 | *
44 | * @author Rohan Padhye
45 | *
46 | */
47 | public class DefaultJimpleRepresentation implements ProgramRepresentation {
48 |
49 | // Cache for control flow graphs
50 | private Map> cfgCache;
51 |
52 | // Private constructor, see #v() to retrieve singleton object
53 | private DefaultJimpleRepresentation() {
54 | cfgCache = new HashMap>();
55 | }
56 |
57 | /**
58 | * Returns a singleton list containing the main
method.
59 | * @see Scene#getMainMethod()
60 | */
61 | @Override
62 | public List getEntryPoints() {
63 | return Collections.singletonList(Scene.v().getMainMethod());
64 | }
65 |
66 | /**
67 | * Returns an {@link ExceptionalUnitGraph} for a given method.
68 | */
69 | @Override
70 | public DirectedGraph getControlFlowGraph(SootMethod method) {
71 | if (cfgCache.containsKey(method) == false) {
72 | cfgCache.put(method, new ExceptionalUnitGraph(method.getActiveBody()));
73 | }
74 | return cfgCache.get(method);
75 | }
76 |
77 | /**
78 | * Returns true
iff the Jimple statement contains an
79 | * invoke expression.
80 | */
81 | @Override
82 | public boolean isCall(Unit node) {
83 | return ((soot.jimple.Stmt) node).containsInvokeExpr();
84 | }
85 |
86 | /**
87 | * Resolves virtual calls using the default call graph and returns
88 | * a list of methods which are the targets of explicit edges.
89 | * TODO: Should we consider thread/clinit edges?
90 | */
91 | @Override
92 | public List resolveTargets(SootMethod method, Unit node) {
93 | List targets = new LinkedList();
94 | Iterator it = Scene.v().getCallGraph().edgesOutOf(node);
95 | while(it.hasNext()) {
96 | Edge edge = it.next();
97 | if (edge.isExplicit()) {
98 | targets.add(edge.tgt());
99 | }
100 | }
101 | return targets;
102 | }
103 |
104 | // The singleton object
105 | private static DefaultJimpleRepresentation singleton = new DefaultJimpleRepresentation();
106 |
107 | /**
108 | * Returns a reference to the singleton object of this class.
109 | */
110 | public static DefaultJimpleRepresentation v() { return singleton; }
111 |
112 | @Override
113 | public boolean isPhantomMethod(SootMethod method) {
114 | return method.isPhantom() || !method.hasActiveBody();
115 | }
116 |
117 | }
118 |
--------------------------------------------------------------------------------
/src/main/java/vasco/soot/examples/CopyConstantAnalysis.java:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright (C) 2013 Rohan Padhye
3 | *
4 | * This library is free software: you can redistribute it and/or modify
5 | * it under the terms of the GNU Lesser General Public License as
6 | * published by the Free Software Foundation, either version 2.1 of the
7 | * License, or (at your option) any later version.
8 | *
9 | * This library is distributed in the hope that it will be useful,
10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 | * GNU Lesser General Public License for more details.
13 | *
14 | * You should have received a copy of the GNU Lesser General Public License
15 | * along with this program. If not, see .
16 | *
17 | */
18 | package vasco.soot.examples;
19 |
20 | import java.util.HashMap;
21 | import java.util.Map;
22 |
23 | import soot.Local;
24 | import soot.SootMethod;
25 | import soot.Unit;
26 | import soot.Value;
27 | import soot.jimple.AssignStmt;
28 | import soot.jimple.CastExpr;
29 | import soot.jimple.Constant;
30 | import soot.jimple.InstanceInvokeExpr;
31 | import soot.jimple.InvokeExpr;
32 | import soot.jimple.ReturnStmt;
33 | import soot.jimple.Stmt;
34 | import soot.jimple.internal.JimpleLocal;
35 | import vasco.Context;
36 | import vasco.ForwardInterProceduralAnalysis;
37 | import vasco.ProgramRepresentation;
38 | import vasco.soot.DefaultJimpleRepresentation;
39 |
40 | /**
41 | * An inter-procedural copy constant propagation analysis.
42 | *
43 | * This analysis uses a mapping of {@link Local}s to {@link Constant}s as
44 | * data flow values. The flow functions consider assignments of constants
45 | * to locals (immediate operands) as well as assignments of locals to locals
46 | * where the operand has a constant value. This type of analysis is commonly referred
47 | * to as copy constant propagation.
48 | *
49 | *
50 | * @author Rohan Padhye
51 | *
52 | */
53 | public class CopyConstantAnalysis extends ForwardInterProceduralAnalysis> {
54 |
55 | // An artificial local representing returned value of a procedure (used because a method can have multiple return statements).
56 | private static final Local RETURN_LOCAL = new JimpleLocal("@return", null);
57 |
58 | // Simply constructs a forward flow inter-procedural analysis with the VERBOSE option set.
59 | public CopyConstantAnalysis() {
60 | super();
61 | verbose = true;
62 | }
63 |
64 | // Private utility method to assign the constant value of the RHS (if any) from the input map to the LHS in the output map.
65 | private void assign(Local lhs, Value rhs, Map input, Map output) {
66 | // First remove casts, if any.
67 | if (rhs instanceof CastExpr) {
68 | rhs = ((CastExpr) rhs).getOp();
69 | }
70 | // Then check if the RHS operand is a constant or local
71 | if (rhs instanceof Constant) {
72 | // If RHS is a constant, it is a direct gen
73 | output.put(lhs, (Constant) rhs);
74 | } else if (rhs instanceof Local) {
75 | // Copy constant-status of RHS to LHS (indirect gen), if exists
76 | if(input.containsKey(rhs)) {
77 | output.put(lhs, input.get(rhs));
78 | }
79 | } else {
80 | // RHS is some compound expression, then LHS is non-constant (only kill)
81 | output.put(lhs, null);
82 | }
83 | }
84 |
85 | @Override
86 | public Map normalFlowFunction(Context> context, Unit unit,
87 | Map inValue) {
88 | // Initialize result to input
89 | Map outValue = copy(inValue);
90 | // Only statements assigning locals matter
91 | if (unit instanceof AssignStmt) {
92 | // Get operands
93 | Value lhsOp = ((AssignStmt) unit).getLeftOp();
94 | Value rhsOp = ((AssignStmt) unit).getRightOp();
95 | if (lhsOp instanceof Local) {
96 | assign((Local) lhsOp, rhsOp, inValue, outValue);
97 | }
98 | } else if (unit instanceof ReturnStmt) {
99 | // Get operand
100 | Value rhsOp = ((ReturnStmt) unit).getOp();
101 | assign(RETURN_LOCAL, rhsOp, inValue, outValue);
102 | }
103 | // Return the data flow value at the OUT of the statement
104 | return outValue;
105 | }
106 |
107 | @Override
108 | public Map callEntryFlowFunction(Context> context, SootMethod calledMethod, Unit unit, Map inValue) {
109 | // Initialise result to empty map
110 | Map entryValue = topValue();
111 | // Map arguments to parameters
112 | InvokeExpr ie = ((Stmt) unit).getInvokeExpr();
113 | for (int i = 0; i < ie.getArgCount(); i++) {
114 | Value arg = ie.getArg(i);
115 | Local param = calledMethod.getActiveBody().getParameterLocal(i);
116 | assign(param, arg, inValue, entryValue);
117 | }
118 | // And instance of the this local
119 | if (ie instanceof InstanceInvokeExpr) {
120 | Value instance = ((InstanceInvokeExpr) ie).getBase();
121 | Local thisLocal = calledMethod.getActiveBody().getThisLocal();
122 | assign(thisLocal, instance, inValue, entryValue);
123 | }
124 | // Return the entry value at the called method
125 | return entryValue;
126 | }
127 |
128 | @Override
129 | public Map callExitFlowFunction(Context> context, SootMethod calledMethod, Unit unit, Map exitValue) {
130 | // Initialise result to an empty value
131 | Map afterCallValue = topValue();
132 | // Only propagate constants for return values
133 | if (unit instanceof AssignStmt) {
134 | Value lhsOp = ((AssignStmt) unit).getLeftOp();
135 | assign((Local) lhsOp, RETURN_LOCAL, exitValue, afterCallValue);
136 | }
137 | // Return the map with the returned value's constant
138 | return afterCallValue;
139 | }
140 |
141 | @Override
142 | public Map callLocalFlowFunction(Context> context, Unit unit, Map inValue) {
143 | // Initialise result to the input
144 | Map afterCallValue = copy(inValue);
145 | // Remove information for return value (as it's value will flow from the call)
146 | if (unit instanceof AssignStmt) {
147 | Value lhsOp = ((AssignStmt) unit).getLeftOp();
148 | afterCallValue.remove(lhsOp);
149 | }
150 | // Rest of the map remains the same
151 | return afterCallValue;
152 |
153 | }
154 |
155 | @Override
156 | public Map boundaryValue(SootMethod method) {
157 | return topValue();
158 | }
159 |
160 | @Override
161 | public Map copy(Map src) {
162 | return new HashMap(src);
163 | }
164 |
165 |
166 |
167 | @Override
168 | public Map meet(Map op1, Map op2) {
169 | Map result;
170 | // First add everything in the first operand
171 | result = new HashMap(op1);
172 | // Then add everything in the second operand, bottoming out the common keys with different values
173 | for (Local x : op2.keySet()) {
174 | if (op1.containsKey(x)) {
175 | // Check the values in both operands
176 | Constant c1 = op1.get(x);
177 | Constant c2 = op2.get(x);
178 | if (c1 != null && c1.equals(c2) == false) {
179 | // Set to non-constant
180 | result.put(x, null);
181 | }
182 | } else {
183 | // Only in second operand, so add as-is
184 | result.put(x, op2.get(x));
185 | }
186 | }
187 | return result;
188 | }
189 |
190 | /**
191 | * Returns an empty map.
192 | */
193 | @Override
194 | public Map topValue() {
195 | return new HashMap();
196 | }
197 |
198 | /**
199 | * Returns a default jimple representation.
200 | * @see DefaultJimpleRepresentation
201 | */
202 | @Override
203 | public ProgramRepresentation programRepresentation() {
204 | return DefaultJimpleRepresentation.v();
205 | }
206 |
207 | }
208 |
--------------------------------------------------------------------------------
/src/main/java/vasco/soot/examples/SignAnalysis.java:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright (C) 2013 Rohan Padhye
3 | *
4 | * This library is free software: you can redistribute it and/or modify
5 | * it under the terms of the GNU Lesser General Public License as
6 | * published by the Free Software Foundation, either version 2.1 of the
7 | * License, or (at your option) any later version.
8 | *
9 | * This library is distributed in the hope that it will be useful,
10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 | * GNU Lesser General Public License for more details.
13 | *
14 | * You should have received a copy of the GNU Lesser General Public License
15 | * along with this program. If not, see .
16 | *
17 | */
18 | package vasco.soot.examples;
19 |
20 | import static vasco.soot.examples.SignAnalysis.Sign.BOTTOM;
21 | import static vasco.soot.examples.SignAnalysis.Sign.NEGATIVE;
22 | import static vasco.soot.examples.SignAnalysis.Sign.POSITIVE;
23 | import static vasco.soot.examples.SignAnalysis.Sign.ZERO;
24 |
25 | import java.util.HashMap;
26 | import java.util.Map;
27 |
28 | import soot.IntType;
29 | import soot.Local;
30 | import soot.SootMethod;
31 | import soot.Unit;
32 | import soot.Value;
33 | import soot.jimple.AddExpr;
34 | import soot.jimple.AssignStmt;
35 | import soot.jimple.BinopExpr;
36 | import soot.jimple.CastExpr;
37 | import soot.jimple.InstanceInvokeExpr;
38 | import soot.jimple.IntConstant;
39 | import soot.jimple.InvokeExpr;
40 | import soot.jimple.MulExpr;
41 | import soot.jimple.NumericConstant;
42 | import soot.jimple.ReturnStmt;
43 | import soot.jimple.Stmt;
44 | import soot.jimple.UnopExpr;
45 | import soot.jimple.internal.AbstractNegExpr;
46 | import soot.jimple.internal.JimpleLocal;
47 | import vasco.Context;
48 | import vasco.ForwardInterProceduralAnalysis;
49 | import vasco.ProgramRepresentation;
50 | import vasco.soot.DefaultJimpleRepresentation;
51 |
52 | /**
53 | * An inter-procedural simplified sign analysis.
54 | *
55 | * This analysis maps numeric variables to a sign (negative, positive or
56 | * zero), if it is statically determined to be singular, or else BOTTOM.
57 | *
58 | *
Flow functions are non-distributive for statements involving sums or
59 | * products of two variables.
60 | *
61 | * This is an example implementation only and hence only support analysis
62 | * of integer-valued local variables, and only handles addition, multiplication
63 | * and unary negation of such variables or values.
64 | *
65 | * @author Rohan Padhye
66 | *
67 | */
68 | public class SignAnalysis extends ForwardInterProceduralAnalysis> {
69 |
70 |
71 | // An artificial local representing returned value of a procedure (used because a method can have multiple return statements).
72 | private static final Local RETURN_LOCAL = new JimpleLocal("@return", IntType.v());
73 |
74 | // Simply constructs a forward flow inter-procedural analysis with the VERBOSE option set.
75 | public SignAnalysis() {
76 | super();
77 | verbose = true;
78 | }
79 |
80 | // Returns the sign of a constant or local if it is determinable, or else BOTTOM if the sign cannot be determined. */
81 | private Sign signOf(Value value, Map dfv) {
82 | if (value instanceof Local) {
83 | // if the value is a local variable, then look-up the data-flow map
84 | Local local = (Local) value;
85 | if (dfv.containsKey(local)) {
86 | return dfv.get(local);
87 | } else {
88 | return Sign.TOP;
89 | }
90 | } else if (value instanceof NumericConstant) {
91 | // If the value is a constant, we can get a definite sign
92 | NumericConstant num = (NumericConstant) value;
93 | NumericConstant zero = IntConstant.v(0);
94 | NumericConstant one = IntConstant.v(1);
95 | if (num.lessThan(zero).equals(one)) {
96 | return NEGATIVE;
97 | } else if (num.greaterThan(zero).equals(one)) {
98 | return POSITIVE;
99 | } else {
100 | return ZERO;
101 | }
102 | } else if (value instanceof BinopExpr) {
103 | BinopExpr expr = (BinopExpr) value;
104 | Value op1 = expr.getOp1();
105 | Value op2 = expr.getOp2();
106 | Sign sign1 = signOf(op1, dfv);
107 | Sign sign2 = signOf(op2, dfv);
108 | if (value instanceof AddExpr) {
109 | // Handle arithmetic plus
110 | return sign1.plus(sign2);
111 | } else if (value instanceof MulExpr) {
112 | // Handle arithmetic multiplication
113 | return sign1.mult(sign2);
114 | } else {
115 | // We do not handle other types of binary expressions
116 | return BOTTOM;
117 | }
118 | } else if (value instanceof UnopExpr) {
119 | if (value instanceof AbstractNegExpr) {
120 | // Handle unary minus
121 | Value op = ((AbstractNegExpr) value).getOp();
122 | Sign sign = signOf(op, dfv);
123 | return sign.negate();
124 | } else {
125 | // We do not handle other types of binary expressions
126 | return BOTTOM;
127 | }
128 | } else {
129 | // We do not handle other types of compound expressions
130 | return BOTTOM;
131 | }
132 | }
133 |
134 | // Private utility method to assign the constant value of the RHS (if any) from the input map to the LHS in the output map.
135 | private void assign(Local lhs, Value rhs, Map input, Map output) {
136 | // We only care about numeric locals
137 | if (lhs.getType() instanceof IntType) {
138 | // First remove casts, if any.
139 | if (rhs instanceof CastExpr) {
140 | rhs = ((CastExpr) rhs).getOp();
141 | }
142 | // Determine the sign of the RHS and add it to the map
143 | Sign sign = signOf(rhs, input);
144 | output.put(lhs, sign);
145 | }
146 | }
147 |
148 | @Override
149 | public Map normalFlowFunction(
150 | Context> context, Unit unit,
151 | Map inValue) {
152 | // Initialize result to input
153 | Map outValue = copy(inValue);
154 | // Only statements assigning locals matter
155 | if (unit instanceof AssignStmt) {
156 | // Get operands
157 | Value lhsOp = ((AssignStmt) unit).getLeftOp();
158 | Value rhsOp = ((AssignStmt) unit).getRightOp();
159 | if (lhsOp instanceof Local) {
160 | assign((Local) lhsOp, rhsOp, inValue, outValue);
161 | }
162 | } else if (unit instanceof ReturnStmt) {
163 | // Get operand
164 | Value rhsOp = ((ReturnStmt) unit).getOp();
165 | assign(RETURN_LOCAL, rhsOp, inValue, outValue);
166 | }
167 | // Return the data flow value at the OUT of the statement
168 | return outValue;
169 | }
170 |
171 | @Override
172 | public Map callEntryFlowFunction(
173 | Context> context, SootMethod calledMethod, Unit unit,
174 | Map inValue) {
175 | // Initialise result to empty map
176 | Map entryValue = topValue();
177 | // Map arguments to parameters
178 | InvokeExpr ie = ((Stmt) unit).getInvokeExpr();
179 | for (int i = 0; i < ie.getArgCount(); i++) {
180 | Value arg = ie.getArg(i);
181 | Local param = calledMethod.getActiveBody().getParameterLocal(i);
182 | assign(param, arg, inValue, entryValue);
183 | }
184 | // And instance of the this local
185 | if (ie instanceof InstanceInvokeExpr) {
186 | Value instance = ((InstanceInvokeExpr) ie).getBase();
187 | Local thisLocal = calledMethod.getActiveBody().getThisLocal();
188 | assign(thisLocal, instance, inValue, entryValue);
189 | }
190 | // Return the entry value at the called method
191 | return entryValue;
192 | }
193 |
194 | @Override
195 | public Map callExitFlowFunction(Context> context, SootMethod calledMethod, Unit unit, Map exitValue) {
196 | // Initialise result to an empty value
197 | Map afterCallValue = topValue();
198 | // Only propagate signs for return values
199 | if (unit instanceof AssignStmt) {
200 | Value lhsOp = ((AssignStmt) unit).getLeftOp();
201 | assign((Local) lhsOp, RETURN_LOCAL, exitValue, afterCallValue);
202 | }
203 | // Return the map with the returned value's sign
204 | return afterCallValue;
205 | }
206 |
207 | @Override
208 | public Map callLocalFlowFunction(Context> context, Unit unit, Map inValue) {
209 | // Initialise result to the input
210 | Map afterCallValue = copy(inValue);
211 | // Remove information for return value (as it's value will flow from the call)
212 | if (unit instanceof AssignStmt) {
213 | Value lhsOp = ((AssignStmt) unit).getLeftOp();
214 | afterCallValue.remove(lhsOp);
215 | }
216 | // Rest of the map remains the same
217 | return afterCallValue;
218 |
219 | }
220 |
221 | @Override
222 | public Map boundaryValue(SootMethod method) {
223 | return topValue();
224 | }
225 |
226 | @Override
227 | public Map copy(Map src) {
228 | return new HashMap(src);
229 | }
230 |
231 |
232 |
233 | @Override
234 | public Map meet(Map op1, Map op2) {
235 | Map result;
236 | // First add everything in the first operand
237 | result = new HashMap(op1);
238 | // Then add everything in the second operand, bottoming out the common keys with different values
239 | for (Local x : op2.keySet()) {
240 | if (op1.containsKey(x)) {
241 | // Check the values in both operands
242 | Sign sign1 = op1.get(x);
243 | Sign sign2 = op2.get(x);
244 | result.put(x, sign1.meet(sign2));
245 | } else {
246 | // Only in second operand, so add as-is
247 | result.put(x, op2.get(x));
248 | }
249 | }
250 | return result;
251 | }
252 |
253 | /**
254 | * Returns an empty map.
255 | */
256 | @Override
257 | public Map topValue() {
258 | return new HashMap();
259 | }
260 |
261 | /**
262 | * Returns a default jimple representation.
263 | * @see DefaultJimpleRepresentation
264 | */
265 | @Override
266 | public ProgramRepresentation programRepresentation() {
267 | return DefaultJimpleRepresentation.v();
268 | }
269 |
270 | /** A data-flow value representation of a sign. */
271 | public static interface Sign {
272 |
273 | /** Returns a sign which is the lattice MEET of this sign with the argument. */
274 | public Sign meet(Sign other);
275 |
276 | /** Returns a sign which is the result of adding a number with this sign with a number having the sign of the argument. */
277 | public Sign plus(Sign other);
278 |
279 | /** Returns a sign which is the result of multiplying a number with this sign with a number having the sign of the argument. */
280 | public Sign mult(Sign other);
281 |
282 | /** Returns a sign which is the result of negating a number with this sign. */
283 | public Sign negate();
284 |
285 | /** An unknown sign representing the BOTTOM value of the lattice. */
286 | public static final Sign BOTTOM = new Sign() {
287 |
288 | @Override
289 | public Sign meet(Sign other) {
290 | return BOTTOM;
291 | }
292 |
293 | @Override
294 | public Sign plus(Sign other) {
295 | return BOTTOM;
296 | }
297 |
298 | @Override
299 | public Sign mult(Sign other) {
300 | return BOTTOM;
301 | }
302 |
303 | @Override
304 | public Sign negate() {
305 | return BOTTOM;
306 | }
307 |
308 | @Override
309 | public String toString() {
310 | return "_|_";
311 | }
312 | };
313 |
314 | /** The sign of an undefined variable, representing the TOP value of the lattice. */
315 | public static final Sign TOP = new Sign() {
316 |
317 | @Override
318 | public Sign meet(Sign other) {
319 | return other;
320 | }
321 |
322 | @Override
323 | public Sign plus(Sign other) {
324 | return other;
325 | }
326 |
327 | @Override
328 | public Sign mult(Sign other) {
329 | return other;
330 | }
331 |
332 | @Override
333 | public Sign negate() {
334 | return TOP;
335 | }
336 |
337 | @Override
338 | public String toString() {
339 | return "T";
340 | }
341 | };
342 |
343 | /** The sign of the number 0. */
344 | public static final Sign ZERO = new Sign() {
345 |
346 | @Override
347 | public Sign meet(Sign other) {
348 | if (other == TOP || other == ZERO) {
349 | return ZERO;
350 | } else {
351 | return BOTTOM;
352 | }
353 | }
354 |
355 | @Override
356 | public Sign plus(Sign other) {
357 | if (other == TOP || other == ZERO) {
358 | return ZERO;
359 | } else if (other == POSITIVE) {
360 | return POSITIVE;
361 | } else if (other == NEGATIVE) {
362 | return NEGATIVE;
363 | } else {
364 | return BOTTOM;
365 | }
366 | }
367 |
368 | @Override
369 | public Sign mult(Sign other) {
370 | if (other == TOP || other == ZERO || other == POSITIVE || other == NEGATIVE) {
371 | return ZERO;
372 | } else {
373 | return BOTTOM;
374 | }
375 | }
376 |
377 | @Override
378 | public Sign negate() {
379 | return ZERO;
380 | }
381 |
382 | @Override
383 | public String toString() {
384 | return "0";
385 | }
386 | };
387 |
388 | /** The sign of positive numbers. */
389 | public static final Sign POSITIVE = new Sign() {
390 |
391 | @Override
392 | public Sign meet(Sign other) {
393 | if (other == TOP || other == POSITIVE) {
394 | return POSITIVE;
395 | } else {
396 | return BOTTOM;
397 | }
398 | }
399 |
400 | @Override
401 | public Sign plus(Sign other) {
402 | if (other == TOP || other == POSITIVE || other == ZERO) {
403 | return POSITIVE;
404 | } else if (other == NEGATIVE) {
405 | return BOTTOM;
406 | } else {
407 | return BOTTOM;
408 | }
409 | }
410 |
411 | @Override
412 | public Sign mult(Sign other) {
413 | if (other == TOP || other == POSITIVE) {
414 | return POSITIVE;
415 | } else if (other == ZERO) {
416 | return ZERO;
417 | } else if (other == NEGATIVE) {
418 | return NEGATIVE;
419 | } else {
420 | return BOTTOM;
421 | }
422 | }
423 |
424 | @Override
425 | public Sign negate() {
426 | return NEGATIVE;
427 | }
428 |
429 | @Override
430 | public String toString() {
431 | return "+";
432 | }
433 | };
434 |
435 | /** The sign of negative numbers. */
436 | public static final Sign NEGATIVE = new Sign() {
437 | @Override
438 | public Sign meet(Sign other) {
439 | if (other == TOP || other == NEGATIVE) {
440 | return NEGATIVE;
441 | } else {
442 | return BOTTOM;
443 | }
444 | }
445 | @Override
446 | public Sign plus(Sign other) {
447 | if (other == TOP || other == NEGATIVE || other == ZERO) {
448 | return NEGATIVE;
449 | } else if (other == POSITIVE) {
450 | return BOTTOM;
451 | } else {
452 | return BOTTOM;
453 | }
454 | }
455 | @Override
456 | public Sign mult(Sign other) {
457 | if (other == TOP || other == NEGATIVE) {
458 | return POSITIVE;
459 | } else if (other == ZERO) {
460 | return ZERO;
461 | } else if (other == POSITIVE) {
462 | return NEGATIVE;
463 | } else {
464 | return BOTTOM;
465 | }
466 | }
467 | @Override
468 | public Sign negate() {
469 | return POSITIVE;
470 | }
471 | @Override
472 | public String toString() {
473 | return "-";
474 | }
475 | };
476 | }
477 |
478 | }
479 |
480 |
481 |
482 |
483 |
--------------------------------------------------------------------------------
/src/test/java/vasco/callgraph/CallGraphTest.java:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright (C) 2013 Rohan Padhye
3 | *
4 | * This library is free software: you can redistribute it and/or modify
5 | * it under the terms of the GNU Lesser General Public License as
6 | * published by the Free Software Foundation, either version 2.1 of the
7 | * License, or (at your option) any later version.
8 | *
9 | * This library is distributed in the hope that it will be useful,
10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 | * GNU Lesser General Public License for more details.
13 | *
14 | * You should have received a copy of the GNU Lesser General Public License
15 | * along with this program. If not, see .
16 | *
17 | */
18 | package vasco.callgraph;
19 |
20 |
21 | import java.io.File;
22 | import java.io.FileNotFoundException;
23 | import java.io.FileOutputStream;
24 | import java.io.IOException;
25 | import java.io.PrintWriter;
26 | import java.util.HashSet;
27 | import java.util.Iterator;
28 | import java.util.LinkedList;
29 | import java.util.List;
30 | import java.util.Map;
31 | import java.util.Set;
32 | import java.util.Stack;
33 |
34 | import org.junit.Test;
35 | import soot.PackManager;
36 | import soot.Scene;
37 | import soot.SootMethod;
38 | import soot.Transform;
39 | import soot.Unit;
40 | import soot.jimple.toolkits.callgraph.Edge;
41 | import vasco.CallSite;
42 | import vasco.Context;
43 | import vasco.ContextTransitionTable;
44 |
45 | /**
46 | * A main class for testing call graph construction using a Flow and Context
47 | * Sensitive Points-to Analysis (FCPA).
48 | *
49 | * Usage: