Skip to content

Commit 5378767

Browse files
authored
Improve SideEffectAnalysis precision using context-sensitive information and more efficient algorithms (#196)
1 parent 6e58580 commit 5378767

File tree

8 files changed

+446
-102
lines changed

8 files changed

+446
-102
lines changed
Lines changed: 162 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,162 @@
1+
/*
2+
* Tai-e: A Static Analysis Framework for Java
3+
*
4+
* Copyright (C) 2022 Tian Tan <[email protected]>
5+
* Copyright (C) 2022 Yue Li <[email protected]>
6+
*
7+
* This file is part of Tai-e.
8+
*
9+
* Tai-e is free software: you can redistribute it and/or modify
10+
* it under the terms of the GNU Lesser General Public License
11+
* as published by the Free Software Foundation, either version 3
12+
* of the License, or (at your option) any later version.
13+
*
14+
* Tai-e is distributed in the hope that it will be useful,but WITHOUT
15+
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
16+
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General
17+
* Public License for more details.
18+
*
19+
* You should have received a copy of the GNU Lesser General Public
20+
* License along with Tai-e. If not, see <https://www.gnu.org/licenses/>.
21+
*/
22+
23+
package pascal.taie.analysis.sideeffect;
24+
25+
import pascal.taie.analysis.graph.callgraph.CallGraph;
26+
import pascal.taie.analysis.graph.callgraph.Edge;
27+
import pascal.taie.ir.stmt.Stmt;
28+
import pascal.taie.util.collection.Maps;
29+
30+
import java.util.Map;
31+
import java.util.Set;
32+
import java.util.stream.Collectors;
33+
import java.util.stream.Stream;
34+
35+
/**
36+
* A cached view of a given callgraph.
37+
* Query operations on the returned callgraph "read through" to the underlying callgraph,
38+
* and the results are cached.
39+
*
40+
* @implNote This class is not thread-safe
41+
*/
42+
class CachedCallGraph<CallSite, Method> implements CallGraph<CallSite, Method> {
43+
44+
private final CallGraph<CallSite, Method> delegate;
45+
46+
public CachedCallGraph(CallGraph<CallSite, Method> delegate) {
47+
this.delegate = delegate;
48+
}
49+
50+
private final Map<Method, Set<Method>> predecessorCache = Maps.newMap();
51+
52+
private final Map<Method, Set<Method>> successorCache = Maps.newMap();
53+
54+
private final Map<Method, Set<CallSite>> callerCache = Maps.newMap();
55+
56+
private final Map<CallSite, Set<Method>> calleeCache = Maps.newMap();
57+
58+
private final Map<Method, Set<CallSite>> callSitesInCache = Maps.newMap();
59+
60+
private final Map<CallSite, Set<Edge<CallSite, Method>>> edgesOutOfCache = Maps.newMap();
61+
62+
private final Map<Method, Set<Edge<CallSite, Method>>> edgesInToCache = Maps.newMap();
63+
64+
private Set<Edge<CallSite, Method>> edgesCache = null;
65+
66+
@Override
67+
public Set<Method> getPredsOf(Method node) {
68+
return predecessorCache.computeIfAbsent(node, delegate::getPredsOf);
69+
}
70+
71+
@Override
72+
public Set<Method> getSuccsOf(Method node) {
73+
return successorCache.computeIfAbsent(node, delegate::getSuccsOf);
74+
}
75+
76+
@Override
77+
public Set<Method> getNodes() {
78+
return delegate.getNodes();
79+
}
80+
81+
@Override
82+
public boolean isRelevant(Stmt stmt) {
83+
throw new UnsupportedOperationException();
84+
}
85+
86+
@Override
87+
public Set<Method> getResult(Stmt stmt) {
88+
throw new UnsupportedOperationException();
89+
}
90+
91+
@Override
92+
public Set<CallSite> getCallersOf(Method callee) {
93+
return callerCache.computeIfAbsent(callee, delegate::getCallersOf);
94+
}
95+
96+
@Override
97+
public Set<Method> getCalleesOf(CallSite callSite) {
98+
return calleeCache.computeIfAbsent(callSite, delegate::getCalleesOf);
99+
}
100+
101+
@Override
102+
public Set<Method> getCalleesOfM(Method caller) {
103+
return getSuccsOf(caller);
104+
}
105+
106+
@Override
107+
public Method getContainerOf(CallSite callSite) {
108+
return delegate.getContainerOf(callSite);
109+
}
110+
111+
@Override
112+
public Set<CallSite> getCallSitesIn(Method method) {
113+
return callSitesInCache.computeIfAbsent(method, delegate::getCallSitesIn);
114+
}
115+
116+
@Override
117+
public Stream<Edge<CallSite, Method>> edgesOutOf(CallSite callSite) {
118+
return edgesOutOfCache.computeIfAbsent(callSite,
119+
cs -> delegate.edgesOutOf(cs).collect(Collectors.toUnmodifiableSet()))
120+
.stream();
121+
}
122+
123+
@Override
124+
public Stream<Edge<CallSite, Method>> edgesInTo(Method method) {
125+
return edgesInToCache.computeIfAbsent(method,
126+
m -> delegate.edgesInTo(m).collect(Collectors.toUnmodifiableSet()))
127+
.stream();
128+
}
129+
130+
@Override
131+
public Stream<Edge<CallSite, Method>> edges() {
132+
if (edgesCache == null) {
133+
edgesCache = delegate.edges().collect(Collectors.toUnmodifiableSet());
134+
}
135+
return edgesCache.stream();
136+
}
137+
138+
@Override
139+
public int getNumberOfEdges() {
140+
return delegate.getNumberOfEdges();
141+
}
142+
143+
@Override
144+
public Stream<Method> entryMethods() {
145+
return delegate.entryMethods();
146+
}
147+
148+
@Override
149+
public Stream<Method> reachableMethods() {
150+
return delegate.reachableMethods();
151+
}
152+
153+
@Override
154+
public int getNumberOfMethods() {
155+
return delegate.getNumberOfMethods();
156+
}
157+
158+
@Override
159+
public boolean contains(Method method) {
160+
return delegate.contains(method);
161+
}
162+
}

src/main/java/pascal/taie/analysis/sideeffect/SideEffect.java

Lines changed: 11 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -24,36 +24,38 @@
2424

2525
import pascal.taie.analysis.StmtResult;
2626
import pascal.taie.analysis.graph.callgraph.CallGraph;
27+
import pascal.taie.analysis.pta.core.cs.context.Context;
2728
import pascal.taie.analysis.pta.core.heap.Obj;
2829
import pascal.taie.ir.stmt.Invoke;
2930
import pascal.taie.ir.stmt.Stmt;
3031
import pascal.taie.ir.stmt.StoreArray;
3132
import pascal.taie.ir.stmt.StoreField;
3233
import pascal.taie.language.classes.JMethod;
34+
import pascal.taie.util.collection.TwoKeyMap;
3335

3436
import java.util.Map;
3537
import java.util.Set;
3638
import java.util.stream.Collectors;
3739

3840
/**
39-
* Represents result of side-effect analysis.
41+
* Represents result of side effect analysis.
4042
*/
4143
public class SideEffect implements StmtResult<Set<Obj>> {
4244

4345
/**
4446
* Maps from a method to all objects directly or indirectly modified by it.
4547
*/
46-
private final Map<JMethod, Set<Obj>> methodMods;
48+
private final TwoKeyMap<JMethod, Context, Set<Obj>> methodMods;
4749

4850
/**
4951
* Maps from a stmt to the objects directly modified by it.
5052
*/
51-
private final Map<Stmt, Set<Obj>> stmtDirectMods;
53+
private final TwoKeyMap<Stmt, Context, Set<Obj>> stmtDirectMods;
5254

5355
private final CallGraph<Invoke, JMethod> callGraph;
5456

55-
SideEffect(Map<JMethod, Set<Obj>> methodMods,
56-
Map<Stmt, Set<Obj>> stmtDirectMods,
57+
SideEffect(TwoKeyMap<JMethod, Context, Set<Obj>> methodMods,
58+
TwoKeyMap<Stmt, Context, Set<Obj>> stmtDirectMods,
5759
CallGraph<Invoke, JMethod> callGraph) {
5860
this.methodMods = methodMods;
5961
this.stmtDirectMods = stmtDirectMods;
@@ -64,7 +66,8 @@ public class SideEffect implements StmtResult<Set<Obj>> {
6466
* @return set of objects that may be modified by given method.
6567
*/
6668
public Set<Obj> getModifiedObjects(JMethod method) {
67-
return methodMods.getOrDefault(method, Set.of());
69+
return methodMods.getOrDefault(method, Map.of()).values().stream()
70+
.flatMap(Set::stream).collect(Collectors.toUnmodifiableSet());
6871
}
6972

7073
/**
@@ -80,7 +83,8 @@ public Set<Obj> getModifiedObjects(Stmt stmt) {
8083
.flatMap(Set::stream)
8184
.collect(Collectors.toUnmodifiableSet());
8285
}
83-
return stmtDirectMods.getOrDefault(stmt, Set.of());
86+
return stmtDirectMods.getOrDefault(stmt, Map.of()).values().stream()
87+
.flatMap(Set::stream).collect(Collectors.toUnmodifiableSet());
8488
}
8589

8690
/**

src/main/java/pascal/taie/analysis/sideeffect/SideEffectAnalysis.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,6 @@ public SideEffectAnalysis(AnalysisConfig config) {
4646
@Override
4747
public SideEffect analyze() {
4848
PointerAnalysisResult pta = World.get().getResult(PointerAnalysis.ID);
49-
return new TopologicalSolver(onlyApp).solve(pta);
49+
return new TopologicalSolver(onlyApp, pta).solve();
5050
}
5151
}

0 commit comments

Comments
 (0)