Skip to content

Commit 9fbe8e2

Browse files
committed
Merge branch 'summary_arcs' into develop
2 parents e504f19 + 03d7ab1 commit 9fbe8e2

File tree

4 files changed

+335
-3
lines changed

4 files changed

+335
-3
lines changed
Lines changed: 133 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,133 @@
1+
package tfm.graphs;
2+
3+
import com.github.javaparser.ast.CompilationUnit;
4+
import com.github.javaparser.ast.NodeList;
5+
import com.github.javaparser.ast.body.CallableDeclaration;
6+
import com.github.javaparser.ast.body.ConstructorDeclaration;
7+
import com.github.javaparser.ast.body.InitializerDeclaration;
8+
import com.github.javaparser.ast.body.MethodDeclaration;
9+
import com.github.javaparser.ast.expr.MethodCallExpr;
10+
import com.github.javaparser.ast.expr.ObjectCreationExpr;
11+
import com.github.javaparser.ast.stmt.ExplicitConstructorInvocationStmt;
12+
import com.github.javaparser.ast.visitor.VoidVisitorAdapter;
13+
import com.github.javaparser.resolution.Resolvable;
14+
import com.github.javaparser.resolution.declarations.ResolvedMethodLikeDeclaration;
15+
import org.jgrapht.graph.DefaultEdge;
16+
import org.jgrapht.graph.DirectedPseudograph;
17+
import org.jgrapht.io.DOTExporter;
18+
19+
import java.util.Deque;
20+
import java.util.LinkedList;
21+
22+
/**
23+
* A directed graph which displays the available method declarations as nodes and their
24+
* invocations as edges (caller to callee).
25+
* <br/>
26+
* Method declarations include both {@link ConstructorDeclaration constructors}
27+
* and {@link MethodDeclaration method declarations}.
28+
* In the future, {@link InitializerDeclaration static initializer blocks} and field initializers will be included.
29+
* <br/>
30+
* Method calls include only direct method calls, from {@link MethodCallExpr normal call},
31+
* to {@link ObjectCreationExpr object creation} and {@link ExplicitConstructorInvocationStmt
32+
* explicit constructor invokation} ({@code this()}, {@code super()}).
33+
*/
34+
public class CallGraph extends DirectedPseudograph<CallableDeclaration<?>, CallGraph.Edge<?>> implements Buildable<NodeList<CompilationUnit>> {
35+
private boolean built = false;
36+
37+
public CallGraph() {
38+
super(null, null, false);
39+
}
40+
41+
@Override
42+
public void build(NodeList<CompilationUnit> arg) {
43+
buildVertices(arg);
44+
buildEdges(arg);
45+
built = true;
46+
}
47+
48+
@Override
49+
public boolean isBuilt() {
50+
return built;
51+
}
52+
53+
protected void buildVertices(NodeList<CompilationUnit> arg) {
54+
arg.accept(new VoidVisitorAdapter<Void>() {
55+
@Override
56+
public void visit(MethodDeclaration n, Void arg) {
57+
addVertex(n);
58+
super.visit(n, arg);
59+
}
60+
61+
@Override
62+
public void visit(ConstructorDeclaration n, Void arg) {
63+
addVertex(n);
64+
super.visit(n, arg);
65+
}
66+
}, null);
67+
}
68+
69+
protected void buildEdges(NodeList<CompilationUnit> arg) {
70+
arg.accept(new VoidVisitorAdapter<Void>() {
71+
private final Deque<CallableDeclaration<?>> declStack = new LinkedList<>();
72+
73+
// ============ Method declarations ===========
74+
// There are some locations not considered, which may lead to an error in the stack.
75+
// 1. Method calls in non-static field initializations are assigned to all constructors of that class
76+
// 2. Method calls in static field initializations are assigned to the static block of that class
77+
78+
@Override
79+
public void visit(MethodDeclaration n, Void arg) {
80+
declStack.push(n);
81+
super.visit(n, arg);
82+
declStack.pop();
83+
}
84+
85+
@Override
86+
public void visit(ConstructorDeclaration n, Void arg) {
87+
declStack.push(n);
88+
super.visit(n, arg);
89+
declStack.pop();
90+
}
91+
92+
// =============== Method calls ===============
93+
@Override
94+
public void visit(MethodCallExpr n, Void arg) {
95+
n.resolve().toAst().ifPresent(decl -> addEdge(declStack.peek(), decl, new Edge<>(n)));
96+
super.visit(n, arg);
97+
}
98+
99+
@Override
100+
public void visit(ObjectCreationExpr n, Void arg) {
101+
n.resolve().toAst().ifPresent(decl -> addEdge(declStack.peek(), decl, new Edge<>(n)));
102+
super.visit(n, arg);
103+
}
104+
105+
@Override
106+
public void visit(ExplicitConstructorInvocationStmt n, Void arg) {
107+
n.resolve().toAst().ifPresent(decl -> addEdge(declStack.peek(), decl, new Edge<>(n)));
108+
super.visit(n, arg);
109+
}
110+
}, null);
111+
}
112+
113+
public DOTExporter<CallableDeclaration<?>, Edge<?>> getDOTExporter() {
114+
int[] id = new int[]{0};
115+
return new DOTExporter<>(
116+
decl -> id[0]++ + "",
117+
decl -> decl.getDeclarationAsString(false, false, false),
118+
e -> e.getCallExpr().toString()
119+
);
120+
}
121+
122+
public static class Edge<T extends Resolvable<? extends ResolvedMethodLikeDeclaration>> extends DefaultEdge {
123+
protected T callExpr;
124+
125+
public Edge(T callExpr) {
126+
this.callExpr = callExpr;
127+
}
128+
129+
public T getCallExpr() {
130+
return callExpr;
131+
}
132+
}
133+
}

sdg-core/src/main/java/tfm/graphs/sdg/SDG.java

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
import com.github.javaparser.ast.body.MethodDeclaration;
66
import com.github.javaparser.ast.expr.NameExpr;
77
import com.github.javaparser.ast.stmt.ExpressionStmt;
8+
import tfm.arcs.Arc;
89
import tfm.arcs.pdg.ControlDependencyArc;
910
import tfm.arcs.pdg.DataDependencyArc;
1011
import tfm.arcs.sdg.CallArc;
@@ -13,6 +14,7 @@
1314
import tfm.graphs.Buildable;
1415
import tfm.graphs.Graph;
1516
import tfm.graphs.cfg.CFG;
17+
import tfm.graphs.sdg.sumarcs.AnalysisSummaryArcsBuilder;
1618
import tfm.graphs.sdg.sumarcs.NaiveSummaryArcsBuilder;
1719
import tfm.nodes.GraphNode;
1820
import tfm.nodes.VariableAction;
@@ -21,7 +23,10 @@
2123
import tfm.slicing.Sliceable;
2224
import tfm.slicing.SlicingCriterion;
2325
import tfm.utils.Context;
26+
import tfm.utils.Logger;
2427

28+
import java.io.StringWriter;
29+
import java.io.Writer;
2530
import java.util.*;
2631
import java.util.stream.Collectors;
2732

@@ -48,8 +53,9 @@ public void build(NodeList<CompilationUnit> nodeList) {
4853
nodeList.accept(createBuilder(), new Context());
4954
Set<GraphNode<?>> vertices = Set.copyOf(vertexSet());
5055
vertices.forEach(n -> new MethodCallReplacerVisitor(this).startVisit(n));
51-
new NaiveSummaryArcsBuilder(this).visit();
5256
compilationUnits = nodeList;
57+
// new NaiveSummaryArcsBuilder(this).visit();
58+
new AnalysisSummaryArcsBuilder(this).visit();
5359
built = true;
5460
}
5561

Lines changed: 193 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,193 @@
1+
package tfm.graphs.sdg.sumarcs;
2+
3+
import com.github.javaparser.ast.body.CallableDeclaration;
4+
import com.github.javaparser.ast.body.MethodDeclaration;
5+
import tfm.arcs.Arc;
6+
import tfm.arcs.sdg.CallArc;
7+
import tfm.graphs.CallGraph;
8+
import tfm.graphs.sdg.SDG;
9+
import tfm.nodes.ActualIONode;
10+
import tfm.nodes.FormalIONode;
11+
import tfm.nodes.GraphNode;
12+
import tfm.nodes.type.NodeType;
13+
import tfm.utils.Utils;
14+
15+
import java.util.*;
16+
import java.util.stream.Collectors;
17+
18+
/**
19+
* Performs a fixed point analysis over the call graph of a given SDG
20+
*/
21+
public class AnalysisSummaryArcsBuilder extends SummaryArcsBuilder {
22+
23+
private static class SummaryArcPair {
24+
FormalIONode in;
25+
GraphNode<?> out; // out node is either FormalIONode or METHOD_OUTPUT
26+
27+
SummaryArcPair(FormalIONode in, GraphNode<?> out) {
28+
this.in = in; this.out = out;
29+
}
30+
}
31+
32+
private final SDG sdg;
33+
private final CallGraph callGraph;
34+
35+
protected Map<CallableDeclaration<?>, Set<SummaryArcPair>> vertexDataMap = new HashMap<>();
36+
protected boolean built = false;
37+
38+
public AnalysisSummaryArcsBuilder(SDG sdg) {
39+
super(sdg);
40+
41+
CallGraph callGraph = new CallGraph();
42+
callGraph.build(sdg.getCompilationUnits());
43+
44+
this.sdg = sdg;
45+
this.callGraph = callGraph;
46+
}
47+
48+
public AnalysisSummaryArcsBuilder(SDG sdg, CallGraph callGraph) {
49+
super(sdg);
50+
51+
this.sdg = sdg;
52+
this.callGraph = callGraph;
53+
}
54+
55+
public Set<SummaryArcPair> getResult(MethodDeclaration vertex) {
56+
return vertexDataMap.get(vertex);
57+
}
58+
59+
@Override
60+
public void visit() {
61+
assert !built;
62+
List<CallableDeclaration<?>> workList = new LinkedList<>(callGraph.vertexSet());
63+
callGraph.vertexSet().forEach(v -> vertexDataMap.put(v, computeSummaryArcs(v)));
64+
while (!workList.isEmpty()) {
65+
List<CallableDeclaration<?>> newWorkList = new LinkedList<>();
66+
for (CallableDeclaration<?> vertex : workList) {
67+
updateVertex(vertex);
68+
Set<SummaryArcPair> newValue = computeSummaryArcs(vertex); // now with new arcs!!!
69+
if (!Objects.equals(vertexDataMap.get(vertex), newValue)) {
70+
vertexDataMap.put(vertex, newValue);
71+
newWorkList.addAll(callGraph.incomingEdgesOf(vertex).stream()
72+
.map(callGraph::getEdgeSource).collect(Collectors.toSet()));
73+
}
74+
}
75+
workList = newWorkList;
76+
}
77+
vertexDataMap = Collections.unmodifiableMap(vertexDataMap);
78+
built = true;
79+
}
80+
81+
protected void updateVertex(CallableDeclaration<?> declaration) {
82+
if (!declaration.isMethodDeclaration()) {
83+
return; // Parse only method declarations
84+
}
85+
86+
Optional<GraphNode<MethodDeclaration>> optionalMethodDeclarationNode = sdg.findNodeByASTNode(declaration.asMethodDeclaration());
87+
88+
if (optionalMethodDeclarationNode.isEmpty()) {
89+
return;
90+
}
91+
92+
GraphNode<MethodDeclaration> methodDeclarationNode = optionalMethodDeclarationNode.get();
93+
94+
// Get call arcs from this declaration
95+
Set<CallArc> methodCallExprNodes = sdg.incomingEdgesOf(methodDeclarationNode).stream()
96+
.filter(Arc::isCallArc)
97+
.map(Arc::asCallArc)
98+
.collect(Collectors.toSet());
99+
100+
for (CallArc callArc : methodCallExprNodes) {
101+
GraphNode<?> methodCallNode = sdg.getEdgeSource(callArc);
102+
103+
for (SummaryArcPair summaryArcPair : vertexDataMap.getOrDefault(declaration, Utils.emptySet())) {
104+
FormalIONode inFormalNode = summaryArcPair.in;
105+
GraphNode<?> outFormalNode = summaryArcPair.out;
106+
107+
Optional<ActualIONode> optionalIn = sdg.outgoingEdgesOf(methodCallNode).stream()
108+
.filter(arc -> (sdg.getEdgeTarget(arc)).getNodeType().is(NodeType.ACTUAL_IN))
109+
.map(arc -> (ActualIONode) sdg.getEdgeTarget(arc))
110+
.filter(actualIONode -> actualIONode.matchesFormalIO(inFormalNode))
111+
.findFirst();
112+
113+
Optional<? extends GraphNode<?>> optionalOut = sdg.outgoingEdgesOf(methodCallNode).stream()
114+
.map(sdg::getEdgeTarget)
115+
.filter(node -> node.getNodeType().is(NodeType.ACTUAL_OUT))
116+
.filter(actualNode -> {
117+
if (actualNode instanceof ActualIONode) {
118+
return outFormalNode instanceof FormalIONode
119+
&& ((ActualIONode) actualNode).matchesFormalIO((FormalIONode) outFormalNode);
120+
}
121+
// otherwise, actualNode must be METHOD_CALL_RETURN
122+
if (actualNode.getNodeType() != NodeType.METHOD_CALL_RETURN) {
123+
return false;
124+
}
125+
126+
return outFormalNode.getNodeType() == NodeType.METHOD_OUTPUT;
127+
})
128+
.findFirst();
129+
130+
if (optionalIn.isEmpty() || optionalOut.isEmpty()) {
131+
continue;
132+
}
133+
134+
sdg.addSummaryArc(optionalIn.get(), optionalOut.get());
135+
}
136+
}
137+
}
138+
139+
protected Set<SummaryArcPair> computeSummaryArcs(CallableDeclaration<?> declaration) {
140+
Optional<GraphNode<MethodDeclaration>> optionalMethodDeclarationNode = sdg.findNodeByASTNode(declaration.asMethodDeclaration());
141+
142+
if (optionalMethodDeclarationNode.isEmpty()) {
143+
return Utils.emptySet();
144+
}
145+
146+
GraphNode<MethodDeclaration> methodDeclarationNode = optionalMethodDeclarationNode.get();
147+
148+
// Get formal out nodes from declaration
149+
Set<GraphNode<?>> formalOutNodes = sdg.outgoingEdgesOf(methodDeclarationNode).stream()
150+
.filter(Arc::isControlDependencyArc)
151+
.map(sdg::getEdgeTarget)
152+
.filter(node -> node.getNodeType().is(NodeType.FORMAL_OUT))
153+
.collect(Collectors.toSet());
154+
155+
Set<SummaryArcPair> res = new HashSet<>();
156+
157+
for (GraphNode<?> formalOutNode : formalOutNodes) {
158+
for (FormalIONode formalInNode : findReachableFormalInNodes(formalOutNode)) {
159+
res.add(new SummaryArcPair(formalInNode, formalOutNode));
160+
}
161+
}
162+
163+
return res;
164+
}
165+
166+
private Set<FormalIONode> findReachableFormalInNodes(GraphNode<?> formalOutNode) {
167+
return this.doFindReachableFormalInNodes(formalOutNode, Utils.emptySet());
168+
}
169+
170+
private Set<FormalIONode> doFindReachableFormalInNodes(GraphNode<?> root, Set<Long> visited) {
171+
visited.add(root.getId());
172+
173+
Set<FormalIONode> res = Utils.emptySet();
174+
175+
if (root.getNodeType().is(NodeType.FORMAL_IN)) {
176+
res.add((FormalIONode) root);
177+
} else {
178+
for (Arc arc : sdg.incomingEdgesOf(root)) {
179+
GraphNode<?> nextNode = sdg.getEdgeSource(arc);
180+
181+
if (visited.contains(nextNode.getId())) {
182+
continue;
183+
}
184+
185+
if (arc.isDataDependencyArc() || arc.isControlDependencyArc() || arc.isSummaryArc()) {
186+
res.addAll(this.doFindReachableFormalInNodes(nextNode, visited));
187+
}
188+
}
189+
}
190+
191+
return res;
192+
}
193+
}

sdg-core/src/test/res/programs/sdg/Example1.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,9 @@ public class Example1 {
55
public static void main(String[] args) {
66
int n1 = 1;
77
int n2 = 2;
8-
8+
99
int f = sum(n1, n2);
10-
10+
1111
System.out.println(f);
1212
}
1313

0 commit comments

Comments
 (0)