Loading sdg-core/src/main/java/tfm/graphs/CallGraph.java 0 → 100644 +133 −0 Original line number Diff line number Diff line package tfm.graphs; import com.github.javaparser.ast.CompilationUnit; import com.github.javaparser.ast.NodeList; import com.github.javaparser.ast.body.CallableDeclaration; import com.github.javaparser.ast.body.ConstructorDeclaration; import com.github.javaparser.ast.body.InitializerDeclaration; import com.github.javaparser.ast.body.MethodDeclaration; import com.github.javaparser.ast.expr.MethodCallExpr; import com.github.javaparser.ast.expr.ObjectCreationExpr; import com.github.javaparser.ast.stmt.ExplicitConstructorInvocationStmt; import com.github.javaparser.ast.visitor.VoidVisitorAdapter; import com.github.javaparser.resolution.Resolvable; import com.github.javaparser.resolution.declarations.ResolvedMethodLikeDeclaration; import org.jgrapht.graph.DefaultEdge; import org.jgrapht.graph.DirectedPseudograph; import org.jgrapht.io.DOTExporter; import java.util.Deque; import java.util.LinkedList; /** * A directed graph which displays the available method declarations as nodes and their * invocations as edges (caller to callee). * <br/> * Method declarations include both {@link ConstructorDeclaration constructors} * and {@link MethodDeclaration method declarations}. * In the future, {@link InitializerDeclaration static initializer blocks} and field initializers will be included. * <br/> * Method calls include only direct method calls, from {@link MethodCallExpr normal call}, * to {@link ObjectCreationExpr object creation} and {@link ExplicitConstructorInvocationStmt * explicit constructor invokation} ({@code this()}, {@code super()}). */ public class CallGraph extends DirectedPseudograph<CallableDeclaration<?>, CallGraph.Edge<?>> implements Buildable<NodeList<CompilationUnit>> { private boolean built = false; public CallGraph() { super(null, null, false); } @Override public void build(NodeList<CompilationUnit> arg) { buildVertices(arg); buildEdges(arg); built = true; } @Override public boolean isBuilt() { return built; } protected void buildVertices(NodeList<CompilationUnit> arg) { arg.accept(new VoidVisitorAdapter<Void>() { @Override public void visit(MethodDeclaration n, Void arg) { addVertex(n); super.visit(n, arg); } @Override public void visit(ConstructorDeclaration n, Void arg) { addVertex(n); super.visit(n, arg); } }, null); } protected void buildEdges(NodeList<CompilationUnit> arg) { arg.accept(new VoidVisitorAdapter<Void>() { private final Deque<CallableDeclaration<?>> declStack = new LinkedList<>(); // ============ Method declarations =========== // There are some locations not considered, which may lead to an error in the stack. // 1. Method calls in non-static field initializations are assigned to all constructors of that class // 2. Method calls in static field initializations are assigned to the static block of that class @Override public void visit(MethodDeclaration n, Void arg) { declStack.push(n); super.visit(n, arg); declStack.pop(); } @Override public void visit(ConstructorDeclaration n, Void arg) { declStack.push(n); super.visit(n, arg); declStack.pop(); } // =============== Method calls =============== @Override public void visit(MethodCallExpr n, Void arg) { n.resolve().toAst().ifPresent(decl -> addEdge(declStack.peek(), decl, new Edge<>(n))); super.visit(n, arg); } @Override public void visit(ObjectCreationExpr n, Void arg) { n.resolve().toAst().ifPresent(decl -> addEdge(declStack.peek(), decl, new Edge<>(n))); super.visit(n, arg); } @Override public void visit(ExplicitConstructorInvocationStmt n, Void arg) { n.resolve().toAst().ifPresent(decl -> addEdge(declStack.peek(), decl, new Edge<>(n))); super.visit(n, arg); } }, null); } public DOTExporter<CallableDeclaration<?>, Edge<?>> getDOTExporter() { int[] id = new int[]{0}; return new DOTExporter<>( decl -> id[0]++ + "", decl -> decl.getDeclarationAsString(false, false, false), e -> e.getCallExpr().toString() ); } public static class Edge<T extends Resolvable<? extends ResolvedMethodLikeDeclaration>> extends DefaultEdge { protected T callExpr; public Edge(T callExpr) { this.callExpr = callExpr; } public T getCallExpr() { return callExpr; } } } sdg-core/src/main/java/tfm/graphs/sdg/SDG.java +7 −1 Original line number Diff line number Diff line Loading @@ -5,6 +5,7 @@ import com.github.javaparser.ast.NodeList; import com.github.javaparser.ast.body.MethodDeclaration; import com.github.javaparser.ast.expr.NameExpr; import com.github.javaparser.ast.stmt.ExpressionStmt; import tfm.arcs.Arc; import tfm.arcs.pdg.ControlDependencyArc; import tfm.arcs.pdg.DataDependencyArc; import tfm.arcs.sdg.CallArc; Loading @@ -13,6 +14,7 @@ import tfm.arcs.sdg.SummaryArc; import tfm.graphs.Buildable; import tfm.graphs.Graph; import tfm.graphs.cfg.CFG; import tfm.graphs.sdg.sumarcs.AnalysisSummaryArcsBuilder; import tfm.graphs.sdg.sumarcs.NaiveSummaryArcsBuilder; import tfm.nodes.GraphNode; import tfm.nodes.VariableAction; Loading @@ -21,7 +23,10 @@ import tfm.slicing.Slice; import tfm.slicing.Sliceable; import tfm.slicing.SlicingCriterion; import tfm.utils.Context; import tfm.utils.Logger; import java.io.StringWriter; import java.io.Writer; import java.util.*; import java.util.stream.Collectors; Loading @@ -48,8 +53,9 @@ public class SDG extends Graph implements Sliceable, Buildable<NodeList<Compilat nodeList.accept(createBuilder(), new Context()); Set<GraphNode<?>> vertices = Set.copyOf(vertexSet()); vertices.forEach(n -> new MethodCallReplacerVisitor(this).startVisit(n)); new NaiveSummaryArcsBuilder(this).visit(); compilationUnits = nodeList; // new NaiveSummaryArcsBuilder(this).visit(); new AnalysisSummaryArcsBuilder(this).visit(); built = true; } Loading sdg-core/src/main/java/tfm/graphs/sdg/sumarcs/AnalysisSummaryArcsBuilder.java 0 → 100644 +193 −0 Original line number Diff line number Diff line package tfm.graphs.sdg.sumarcs; import com.github.javaparser.ast.body.CallableDeclaration; import com.github.javaparser.ast.body.MethodDeclaration; import tfm.arcs.Arc; import tfm.arcs.sdg.CallArc; import tfm.graphs.CallGraph; import tfm.graphs.sdg.SDG; import tfm.nodes.ActualIONode; import tfm.nodes.FormalIONode; import tfm.nodes.GraphNode; import tfm.nodes.type.NodeType; import tfm.utils.Utils; import java.util.*; import java.util.stream.Collectors; /** * Performs a fixed point analysis over the call graph of a given SDG */ public class AnalysisSummaryArcsBuilder extends SummaryArcsBuilder { private static class SummaryArcPair { FormalIONode in; GraphNode<?> out; // out node is either FormalIONode or METHOD_OUTPUT SummaryArcPair(FormalIONode in, GraphNode<?> out) { this.in = in; this.out = out; } } private final SDG sdg; private final CallGraph callGraph; protected Map<CallableDeclaration<?>, Set<SummaryArcPair>> vertexDataMap = new HashMap<>(); protected boolean built = false; public AnalysisSummaryArcsBuilder(SDG sdg) { super(sdg); CallGraph callGraph = new CallGraph(); callGraph.build(sdg.getCompilationUnits()); this.sdg = sdg; this.callGraph = callGraph; } public AnalysisSummaryArcsBuilder(SDG sdg, CallGraph callGraph) { super(sdg); this.sdg = sdg; this.callGraph = callGraph; } public Set<SummaryArcPair> getResult(MethodDeclaration vertex) { return vertexDataMap.get(vertex); } @Override public void visit() { assert !built; List<CallableDeclaration<?>> workList = new LinkedList<>(callGraph.vertexSet()); callGraph.vertexSet().forEach(v -> vertexDataMap.put(v, computeSummaryArcs(v))); while (!workList.isEmpty()) { List<CallableDeclaration<?>> newWorkList = new LinkedList<>(); for (CallableDeclaration<?> vertex : workList) { updateVertex(vertex); Set<SummaryArcPair> newValue = computeSummaryArcs(vertex); // now with new arcs!!! if (!Objects.equals(vertexDataMap.get(vertex), newValue)) { vertexDataMap.put(vertex, newValue); newWorkList.addAll(callGraph.incomingEdgesOf(vertex).stream() .map(callGraph::getEdgeSource).collect(Collectors.toSet())); } } workList = newWorkList; } vertexDataMap = Collections.unmodifiableMap(vertexDataMap); built = true; } protected void updateVertex(CallableDeclaration<?> declaration) { if (!declaration.isMethodDeclaration()) { return; // Parse only method declarations } Optional<GraphNode<MethodDeclaration>> optionalMethodDeclarationNode = sdg.findNodeByASTNode(declaration.asMethodDeclaration()); if (optionalMethodDeclarationNode.isEmpty()) { return; } GraphNode<MethodDeclaration> methodDeclarationNode = optionalMethodDeclarationNode.get(); // Get call arcs from this declaration Set<CallArc> methodCallExprNodes = sdg.incomingEdgesOf(methodDeclarationNode).stream() .filter(Arc::isCallArc) .map(Arc::asCallArc) .collect(Collectors.toSet()); for (CallArc callArc : methodCallExprNodes) { GraphNode<?> methodCallNode = sdg.getEdgeSource(callArc); for (SummaryArcPair summaryArcPair : vertexDataMap.getOrDefault(declaration, Utils.emptySet())) { FormalIONode inFormalNode = summaryArcPair.in; GraphNode<?> outFormalNode = summaryArcPair.out; Optional<ActualIONode> optionalIn = sdg.outgoingEdgesOf(methodCallNode).stream() .filter(arc -> (sdg.getEdgeTarget(arc)).getNodeType().is(NodeType.ACTUAL_IN)) .map(arc -> (ActualIONode) sdg.getEdgeTarget(arc)) .filter(actualIONode -> actualIONode.matchesFormalIO(inFormalNode)) .findFirst(); Optional<? extends GraphNode<?>> optionalOut = sdg.outgoingEdgesOf(methodCallNode).stream() .map(sdg::getEdgeTarget) .filter(node -> node.getNodeType().is(NodeType.ACTUAL_OUT)) .filter(actualNode -> { if (actualNode instanceof ActualIONode) { return outFormalNode instanceof FormalIONode && ((ActualIONode) actualNode).matchesFormalIO((FormalIONode) outFormalNode); } // otherwise, actualNode must be METHOD_CALL_RETURN if (actualNode.getNodeType() != NodeType.METHOD_CALL_RETURN) { return false; } return outFormalNode.getNodeType() == NodeType.METHOD_OUTPUT; }) .findFirst(); if (optionalIn.isEmpty() || optionalOut.isEmpty()) { continue; } sdg.addSummaryArc(optionalIn.get(), optionalOut.get()); } } } protected Set<SummaryArcPair> computeSummaryArcs(CallableDeclaration<?> declaration) { Optional<GraphNode<MethodDeclaration>> optionalMethodDeclarationNode = sdg.findNodeByASTNode(declaration.asMethodDeclaration()); if (optionalMethodDeclarationNode.isEmpty()) { return Utils.emptySet(); } GraphNode<MethodDeclaration> methodDeclarationNode = optionalMethodDeclarationNode.get(); // Get formal out nodes from declaration Set<GraphNode<?>> formalOutNodes = sdg.outgoingEdgesOf(methodDeclarationNode).stream() .filter(Arc::isControlDependencyArc) .map(sdg::getEdgeTarget) .filter(node -> node.getNodeType().is(NodeType.FORMAL_OUT)) .collect(Collectors.toSet()); Set<SummaryArcPair> res = new HashSet<>(); for (GraphNode<?> formalOutNode : formalOutNodes) { for (FormalIONode formalInNode : findReachableFormalInNodes(formalOutNode)) { res.add(new SummaryArcPair(formalInNode, formalOutNode)); } } return res; } private Set<FormalIONode> findReachableFormalInNodes(GraphNode<?> formalOutNode) { return this.doFindReachableFormalInNodes(formalOutNode, Utils.emptySet()); } private Set<FormalIONode> doFindReachableFormalInNodes(GraphNode<?> root, Set<Long> visited) { visited.add(root.getId()); Set<FormalIONode> res = Utils.emptySet(); if (root.getNodeType().is(NodeType.FORMAL_IN)) { res.add((FormalIONode) root); } else { for (Arc arc : sdg.incomingEdgesOf(root)) { GraphNode<?> nextNode = sdg.getEdgeSource(arc); if (visited.contains(nextNode.getId())) { continue; } if (arc.isDataDependencyArc() || arc.isControlDependencyArc() || arc.isSummaryArc()) { res.addAll(this.doFindReachableFormalInNodes(nextNode, visited)); } } } return res; } } sdg-core/src/test/res/programs/sdg/Example1.java +2 −2 File changed.Contains only whitespace changes. Show changes Loading
sdg-core/src/main/java/tfm/graphs/CallGraph.java 0 → 100644 +133 −0 Original line number Diff line number Diff line package tfm.graphs; import com.github.javaparser.ast.CompilationUnit; import com.github.javaparser.ast.NodeList; import com.github.javaparser.ast.body.CallableDeclaration; import com.github.javaparser.ast.body.ConstructorDeclaration; import com.github.javaparser.ast.body.InitializerDeclaration; import com.github.javaparser.ast.body.MethodDeclaration; import com.github.javaparser.ast.expr.MethodCallExpr; import com.github.javaparser.ast.expr.ObjectCreationExpr; import com.github.javaparser.ast.stmt.ExplicitConstructorInvocationStmt; import com.github.javaparser.ast.visitor.VoidVisitorAdapter; import com.github.javaparser.resolution.Resolvable; import com.github.javaparser.resolution.declarations.ResolvedMethodLikeDeclaration; import org.jgrapht.graph.DefaultEdge; import org.jgrapht.graph.DirectedPseudograph; import org.jgrapht.io.DOTExporter; import java.util.Deque; import java.util.LinkedList; /** * A directed graph which displays the available method declarations as nodes and their * invocations as edges (caller to callee). * <br/> * Method declarations include both {@link ConstructorDeclaration constructors} * and {@link MethodDeclaration method declarations}. * In the future, {@link InitializerDeclaration static initializer blocks} and field initializers will be included. * <br/> * Method calls include only direct method calls, from {@link MethodCallExpr normal call}, * to {@link ObjectCreationExpr object creation} and {@link ExplicitConstructorInvocationStmt * explicit constructor invokation} ({@code this()}, {@code super()}). */ public class CallGraph extends DirectedPseudograph<CallableDeclaration<?>, CallGraph.Edge<?>> implements Buildable<NodeList<CompilationUnit>> { private boolean built = false; public CallGraph() { super(null, null, false); } @Override public void build(NodeList<CompilationUnit> arg) { buildVertices(arg); buildEdges(arg); built = true; } @Override public boolean isBuilt() { return built; } protected void buildVertices(NodeList<CompilationUnit> arg) { arg.accept(new VoidVisitorAdapter<Void>() { @Override public void visit(MethodDeclaration n, Void arg) { addVertex(n); super.visit(n, arg); } @Override public void visit(ConstructorDeclaration n, Void arg) { addVertex(n); super.visit(n, arg); } }, null); } protected void buildEdges(NodeList<CompilationUnit> arg) { arg.accept(new VoidVisitorAdapter<Void>() { private final Deque<CallableDeclaration<?>> declStack = new LinkedList<>(); // ============ Method declarations =========== // There are some locations not considered, which may lead to an error in the stack. // 1. Method calls in non-static field initializations are assigned to all constructors of that class // 2. Method calls in static field initializations are assigned to the static block of that class @Override public void visit(MethodDeclaration n, Void arg) { declStack.push(n); super.visit(n, arg); declStack.pop(); } @Override public void visit(ConstructorDeclaration n, Void arg) { declStack.push(n); super.visit(n, arg); declStack.pop(); } // =============== Method calls =============== @Override public void visit(MethodCallExpr n, Void arg) { n.resolve().toAst().ifPresent(decl -> addEdge(declStack.peek(), decl, new Edge<>(n))); super.visit(n, arg); } @Override public void visit(ObjectCreationExpr n, Void arg) { n.resolve().toAst().ifPresent(decl -> addEdge(declStack.peek(), decl, new Edge<>(n))); super.visit(n, arg); } @Override public void visit(ExplicitConstructorInvocationStmt n, Void arg) { n.resolve().toAst().ifPresent(decl -> addEdge(declStack.peek(), decl, new Edge<>(n))); super.visit(n, arg); } }, null); } public DOTExporter<CallableDeclaration<?>, Edge<?>> getDOTExporter() { int[] id = new int[]{0}; return new DOTExporter<>( decl -> id[0]++ + "", decl -> decl.getDeclarationAsString(false, false, false), e -> e.getCallExpr().toString() ); } public static class Edge<T extends Resolvable<? extends ResolvedMethodLikeDeclaration>> extends DefaultEdge { protected T callExpr; public Edge(T callExpr) { this.callExpr = callExpr; } public T getCallExpr() { return callExpr; } } }
sdg-core/src/main/java/tfm/graphs/sdg/SDG.java +7 −1 Original line number Diff line number Diff line Loading @@ -5,6 +5,7 @@ import com.github.javaparser.ast.NodeList; import com.github.javaparser.ast.body.MethodDeclaration; import com.github.javaparser.ast.expr.NameExpr; import com.github.javaparser.ast.stmt.ExpressionStmt; import tfm.arcs.Arc; import tfm.arcs.pdg.ControlDependencyArc; import tfm.arcs.pdg.DataDependencyArc; import tfm.arcs.sdg.CallArc; Loading @@ -13,6 +14,7 @@ import tfm.arcs.sdg.SummaryArc; import tfm.graphs.Buildable; import tfm.graphs.Graph; import tfm.graphs.cfg.CFG; import tfm.graphs.sdg.sumarcs.AnalysisSummaryArcsBuilder; import tfm.graphs.sdg.sumarcs.NaiveSummaryArcsBuilder; import tfm.nodes.GraphNode; import tfm.nodes.VariableAction; Loading @@ -21,7 +23,10 @@ import tfm.slicing.Slice; import tfm.slicing.Sliceable; import tfm.slicing.SlicingCriterion; import tfm.utils.Context; import tfm.utils.Logger; import java.io.StringWriter; import java.io.Writer; import java.util.*; import java.util.stream.Collectors; Loading @@ -48,8 +53,9 @@ public class SDG extends Graph implements Sliceable, Buildable<NodeList<Compilat nodeList.accept(createBuilder(), new Context()); Set<GraphNode<?>> vertices = Set.copyOf(vertexSet()); vertices.forEach(n -> new MethodCallReplacerVisitor(this).startVisit(n)); new NaiveSummaryArcsBuilder(this).visit(); compilationUnits = nodeList; // new NaiveSummaryArcsBuilder(this).visit(); new AnalysisSummaryArcsBuilder(this).visit(); built = true; } Loading
sdg-core/src/main/java/tfm/graphs/sdg/sumarcs/AnalysisSummaryArcsBuilder.java 0 → 100644 +193 −0 Original line number Diff line number Diff line package tfm.graphs.sdg.sumarcs; import com.github.javaparser.ast.body.CallableDeclaration; import com.github.javaparser.ast.body.MethodDeclaration; import tfm.arcs.Arc; import tfm.arcs.sdg.CallArc; import tfm.graphs.CallGraph; import tfm.graphs.sdg.SDG; import tfm.nodes.ActualIONode; import tfm.nodes.FormalIONode; import tfm.nodes.GraphNode; import tfm.nodes.type.NodeType; import tfm.utils.Utils; import java.util.*; import java.util.stream.Collectors; /** * Performs a fixed point analysis over the call graph of a given SDG */ public class AnalysisSummaryArcsBuilder extends SummaryArcsBuilder { private static class SummaryArcPair { FormalIONode in; GraphNode<?> out; // out node is either FormalIONode or METHOD_OUTPUT SummaryArcPair(FormalIONode in, GraphNode<?> out) { this.in = in; this.out = out; } } private final SDG sdg; private final CallGraph callGraph; protected Map<CallableDeclaration<?>, Set<SummaryArcPair>> vertexDataMap = new HashMap<>(); protected boolean built = false; public AnalysisSummaryArcsBuilder(SDG sdg) { super(sdg); CallGraph callGraph = new CallGraph(); callGraph.build(sdg.getCompilationUnits()); this.sdg = sdg; this.callGraph = callGraph; } public AnalysisSummaryArcsBuilder(SDG sdg, CallGraph callGraph) { super(sdg); this.sdg = sdg; this.callGraph = callGraph; } public Set<SummaryArcPair> getResult(MethodDeclaration vertex) { return vertexDataMap.get(vertex); } @Override public void visit() { assert !built; List<CallableDeclaration<?>> workList = new LinkedList<>(callGraph.vertexSet()); callGraph.vertexSet().forEach(v -> vertexDataMap.put(v, computeSummaryArcs(v))); while (!workList.isEmpty()) { List<CallableDeclaration<?>> newWorkList = new LinkedList<>(); for (CallableDeclaration<?> vertex : workList) { updateVertex(vertex); Set<SummaryArcPair> newValue = computeSummaryArcs(vertex); // now with new arcs!!! if (!Objects.equals(vertexDataMap.get(vertex), newValue)) { vertexDataMap.put(vertex, newValue); newWorkList.addAll(callGraph.incomingEdgesOf(vertex).stream() .map(callGraph::getEdgeSource).collect(Collectors.toSet())); } } workList = newWorkList; } vertexDataMap = Collections.unmodifiableMap(vertexDataMap); built = true; } protected void updateVertex(CallableDeclaration<?> declaration) { if (!declaration.isMethodDeclaration()) { return; // Parse only method declarations } Optional<GraphNode<MethodDeclaration>> optionalMethodDeclarationNode = sdg.findNodeByASTNode(declaration.asMethodDeclaration()); if (optionalMethodDeclarationNode.isEmpty()) { return; } GraphNode<MethodDeclaration> methodDeclarationNode = optionalMethodDeclarationNode.get(); // Get call arcs from this declaration Set<CallArc> methodCallExprNodes = sdg.incomingEdgesOf(methodDeclarationNode).stream() .filter(Arc::isCallArc) .map(Arc::asCallArc) .collect(Collectors.toSet()); for (CallArc callArc : methodCallExprNodes) { GraphNode<?> methodCallNode = sdg.getEdgeSource(callArc); for (SummaryArcPair summaryArcPair : vertexDataMap.getOrDefault(declaration, Utils.emptySet())) { FormalIONode inFormalNode = summaryArcPair.in; GraphNode<?> outFormalNode = summaryArcPair.out; Optional<ActualIONode> optionalIn = sdg.outgoingEdgesOf(methodCallNode).stream() .filter(arc -> (sdg.getEdgeTarget(arc)).getNodeType().is(NodeType.ACTUAL_IN)) .map(arc -> (ActualIONode) sdg.getEdgeTarget(arc)) .filter(actualIONode -> actualIONode.matchesFormalIO(inFormalNode)) .findFirst(); Optional<? extends GraphNode<?>> optionalOut = sdg.outgoingEdgesOf(methodCallNode).stream() .map(sdg::getEdgeTarget) .filter(node -> node.getNodeType().is(NodeType.ACTUAL_OUT)) .filter(actualNode -> { if (actualNode instanceof ActualIONode) { return outFormalNode instanceof FormalIONode && ((ActualIONode) actualNode).matchesFormalIO((FormalIONode) outFormalNode); } // otherwise, actualNode must be METHOD_CALL_RETURN if (actualNode.getNodeType() != NodeType.METHOD_CALL_RETURN) { return false; } return outFormalNode.getNodeType() == NodeType.METHOD_OUTPUT; }) .findFirst(); if (optionalIn.isEmpty() || optionalOut.isEmpty()) { continue; } sdg.addSummaryArc(optionalIn.get(), optionalOut.get()); } } } protected Set<SummaryArcPair> computeSummaryArcs(CallableDeclaration<?> declaration) { Optional<GraphNode<MethodDeclaration>> optionalMethodDeclarationNode = sdg.findNodeByASTNode(declaration.asMethodDeclaration()); if (optionalMethodDeclarationNode.isEmpty()) { return Utils.emptySet(); } GraphNode<MethodDeclaration> methodDeclarationNode = optionalMethodDeclarationNode.get(); // Get formal out nodes from declaration Set<GraphNode<?>> formalOutNodes = sdg.outgoingEdgesOf(methodDeclarationNode).stream() .filter(Arc::isControlDependencyArc) .map(sdg::getEdgeTarget) .filter(node -> node.getNodeType().is(NodeType.FORMAL_OUT)) .collect(Collectors.toSet()); Set<SummaryArcPair> res = new HashSet<>(); for (GraphNode<?> formalOutNode : formalOutNodes) { for (FormalIONode formalInNode : findReachableFormalInNodes(formalOutNode)) { res.add(new SummaryArcPair(formalInNode, formalOutNode)); } } return res; } private Set<FormalIONode> findReachableFormalInNodes(GraphNode<?> formalOutNode) { return this.doFindReachableFormalInNodes(formalOutNode, Utils.emptySet()); } private Set<FormalIONode> doFindReachableFormalInNodes(GraphNode<?> root, Set<Long> visited) { visited.add(root.getId()); Set<FormalIONode> res = Utils.emptySet(); if (root.getNodeType().is(NodeType.FORMAL_IN)) { res.add((FormalIONode) root); } else { for (Arc arc : sdg.incomingEdgesOf(root)) { GraphNode<?> nextNode = sdg.getEdgeSource(arc); if (visited.contains(nextNode.getId())) { continue; } if (arc.isDataDependencyArc() || arc.isControlDependencyArc() || arc.isSummaryArc()) { res.addAll(this.doFindReachableFormalInNodes(nextNode, visited)); } } } return res; } }
sdg-core/src/test/res/programs/sdg/Example1.java +2 −2 File changed.Contains only whitespace changes. Show changes