From 1f463363b22b4340166ab928eb31ca17e9bc4a0e Mon Sep 17 00:00:00 2001 From: Carlos Galindo Date: Wed, 26 Mar 2025 14:42:02 +0100 Subject: [PATCH 1/7] joinACFGs: overhaul - The previous logic did not handle methods with the same name across classes - Much quicker, using the callGraph. --- .../main/java/es/upv/mist/slicing/I_ACFG.java | 58 ++++++------------- .../mist/slicing/util/SingletonCollector.java | 18 ++++++ 2 files changed, 37 insertions(+), 39 deletions(-) create mode 100644 iacfg/src/main/java/es/upv/mist/slicing/util/SingletonCollector.java diff --git a/iacfg/src/main/java/es/upv/mist/slicing/I_ACFG.java b/iacfg/src/main/java/es/upv/mist/slicing/I_ACFG.java index bc4b19c5..b9edcb36 100644 --- a/iacfg/src/main/java/es/upv/mist/slicing/I_ACFG.java +++ b/iacfg/src/main/java/es/upv/mist/slicing/I_ACFG.java @@ -38,6 +38,8 @@ import java.util.logging.Level; import java.util.logging.Logger; import java.util.stream.Collectors; +import static es.upv.mist.slicing.util.SingletonCollector.toSingleton; + public class I_ACFG extends Graph { protected static final Map, CFG> acfgMap = ASTUtils.newIdentityHashMap(); @@ -237,48 +239,26 @@ public class I_ACFG extends Graph { protected void joinACFGs() { - List> enterNodes = new LinkedList<>(); - List> exitNodes = new LinkedList<>(); - - for (GraphNode graphNode : Set.copyOf(vertexSet())) { - if(isEnter(graphNode) && graphNode.getAstNode() instanceof MethodDeclaration) { - enterNodes.add(graphNode); - } - if(isExit(graphNode)) { - exitNodes.add(graphNode); - } - } - - enterNodes.sort(Comparator.comparingLong(GraphNode::getId)); - exitNodes.sort(Comparator.comparingLong(GraphNode::getId)); - - enterNodes.remove(0); - exitNodes.remove(0); - - for (GraphNode graphNode : Set.copyOf(vertexSet())) { - if(graphNode instanceof CallNode) { - CallNode callNode = (CallNode) graphNode; - enterNodes.forEach(enterNode -> { - if(isSameAstNode(enterNode, callNode)){ - addControlFlowArc(callNode, enterNode); - } - }); - } - if(graphNode instanceof CallNode.Return) { - SyntheticNode returnNode = (SyntheticNode) graphNode; - exitNodes.forEach(exitNode -> { - if(isSameAstNode(exitNode, returnNode)){ - addControlFlowArc(exitNode, returnNode); - } - }); - } + for (CallGraph.Edge call : callGraph.edgeSet()) { + // Nodes to be paired up + GraphNode callNode = vertexSet().stream() + .filter(n -> n instanceof CallNode) + .filter(n -> n.getAstNode().equals(call.getCall())) + .collect(toSingleton()); + GraphNode returnNode = vertexSet().stream() + .filter(n -> n instanceof CallNode.Return) + .filter(n -> n.getAstNode().equals(call.getCall())) + .collect(toSingleton()); + GraphNode enterNode = acfgMap.get(callGraph.getEdgeTarget(call).getDeclaration()).getRootNode(); + GraphNode exitNode = acfgMap.get(callGraph.getEdgeTarget(call).getDeclaration()).getExitNode(); + // Connections + addControlFlowArc(callNode, enterNode); + addControlFlowArc(exitNode, returnNode); + // TODO: move this arc creation to expandCalls + addEdge(callNode, returnNode, new ControlFlowArc.NonExecutable()); } } - private boolean isSameAstNode(GraphNode comparableNode, GraphNode graphNode ) { - return Objects.equals(((MethodDeclaration) comparableNode.getAstNode()).getName().getTokenRange().get().getBegin().getText(), ((MethodCallExpr) graphNode.getAstNode()).getName().getTokenRange().get().getBegin().getText()); - } - private static boolean isExit(GraphNode graphNode) { return graphNode instanceof MethodExitNode; } diff --git a/iacfg/src/main/java/es/upv/mist/slicing/util/SingletonCollector.java b/iacfg/src/main/java/es/upv/mist/slicing/util/SingletonCollector.java new file mode 100644 index 00000000..4186944a --- /dev/null +++ b/iacfg/src/main/java/es/upv/mist/slicing/util/SingletonCollector.java @@ -0,0 +1,18 @@ +package es.upv.mist.slicing.util; + +import java.util.stream.Collector; +import java.util.stream.Collectors; + +public class SingletonCollector { + public static Collector toSingleton() { + return Collectors.collectingAndThen( + Collectors.toList(), + list -> { + if (list.size() != 1) { + throw new IllegalStateException("Stream should contain exactly one element"); + } + return list.get(0); + } + ); + } +} -- GitLab From 78e16871fba9f901510f4b6b9fedda92f37103e2 Mon Sep 17 00:00:00 2001 From: Carlos Galindo Date: Wed, 26 Mar 2025 18:07:39 +0100 Subject: [PATCH 2/7] Increase JDK target to 21 --- iacfg/pom.xml | 2 +- pom.xml | 4 ++-- sdg-core/pom.xml | 5 ----- 3 files changed, 3 insertions(+), 8 deletions(-) diff --git a/iacfg/pom.xml b/iacfg/pom.xml index e4ef82dc..d7fda393 100644 --- a/iacfg/pom.xml +++ b/iacfg/pom.xml @@ -16,6 +16,7 @@ 21 UTF-8 + commons-cli @@ -26,7 +27,6 @@ es.upv.mist.slicing sdg-core 1.3.0 - compile es.upv.mist.slicing diff --git a/pom.xml b/pom.xml index 77bee6c4..e4ccbc4f 100644 --- a/pom.xml +++ b/pom.xml @@ -17,8 +17,8 @@ maven-compiler-plugin 3.8.1 - 11 - 11 + 21 + 21 diff --git a/sdg-core/pom.xml b/sdg-core/pom.xml index 68114d8f..397f6957 100644 --- a/sdg-core/pom.xml +++ b/sdg-core/pom.xml @@ -12,11 +12,6 @@ 1.3.0 - - 11 - 11 - - -- GitLab From e17fc82d0d0bf45d593c20c95b18dba450fb1416 Mon Sep 17 00:00:00 2001 From: Carlos Galindo Date: Wed, 26 Mar 2025 18:08:24 +0100 Subject: [PATCH 3/7] extractCalls: reorganized code completely --- .../main/java/es/upv/mist/slicing/I_ACFG.java | 283 +++++++++++------- 1 file changed, 167 insertions(+), 116 deletions(-) diff --git a/iacfg/src/main/java/es/upv/mist/slicing/I_ACFG.java b/iacfg/src/main/java/es/upv/mist/slicing/I_ACFG.java index b9edcb36..49a64807 100644 --- a/iacfg/src/main/java/es/upv/mist/slicing/I_ACFG.java +++ b/iacfg/src/main/java/es/upv/mist/slicing/I_ACFG.java @@ -7,7 +7,6 @@ import com.github.javaparser.ast.body.CallableDeclaration; import com.github.javaparser.ast.body.ClassOrInterfaceDeclaration; import com.github.javaparser.ast.body.ConstructorDeclaration; import com.github.javaparser.ast.body.MethodDeclaration; -import com.github.javaparser.ast.expr.MethodCallExpr; import com.github.javaparser.ast.visitor.VoidVisitorAdapter; import com.github.javaparser.utils.CodeGenerationUtils; import es.upv.mist.slicing.arcs.Arc; @@ -22,21 +21,17 @@ import es.upv.mist.slicing.graphs.cfg.CFG; import es.upv.mist.slicing.graphs.sdg.InterproceduralDefinitionFinder; import es.upv.mist.slicing.graphs.sdg.InterproceduralUsageFinder; import es.upv.mist.slicing.nodes.GraphNode; -import es.upv.mist.slicing.nodes.SyntheticNode; import es.upv.mist.slicing.nodes.VariableAction; +import es.upv.mist.slicing.nodes.io.ActualIONode; import es.upv.mist.slicing.nodes.io.CallNode; import es.upv.mist.slicing.nodes.io.MethodExitNode; import es.upv.mist.slicing.utils.ASTUtils; import es.upv.mist.slicing.utils.StaticTypeSolver; -import org.apache.commons.cli.ParseException; import java.io.File; import java.io.FileNotFoundException; import java.io.IOException; import java.util.*; -import java.util.logging.Level; -import java.util.logging.Logger; -import java.util.stream.Collectors; import static es.upv.mist.slicing.util.SingletonCollector.toSingleton; @@ -45,7 +40,7 @@ public class I_ACFG extends Graph { protected static final Map, CFG> acfgMap = ASTUtils.newIdentityHashMap(); protected CallGraph callGraph; - public static void main(String[] args) throws ParseException, IOException { + public static void main(String[] args) throws IOException { String ruta = "/src/main/java/es/upv/mist/slicing/tests/"; String fichero = "Test.java"; @@ -58,7 +53,6 @@ public class I_ACFG extends Graph { File file = new File(CodeGenerationUtils.mavenModuleRoot(I_ACFG.class)+ruta+fichero); StaticJavaParser.getConfiguration().setAttributeComments(false); - Logger.getLogger(Logger.GLOBAL_LOGGER_NAME).log(Level.INFO, "Configuring JavaParser"); StaticTypeSolver.addTypeSolverJRE(); try { @@ -74,7 +68,7 @@ public class I_ACFG extends Graph { copyACFGs(); expandCalls(); joinACFGs(); - new GraphLog<>(this){ + new GraphLog<>(this) { @Override protected DOTAttributes edgeAttributes(Arc arc) { DOTAttributes att = super.edgeAttributes(arc); @@ -90,18 +84,17 @@ public class I_ACFG extends Graph { nodeList.accept(new VoidVisitorAdapter() { @Override public void visit(MethodDeclaration n, Void arg) { - boolean isInInterface = n.findAncestor(ClassOrInterfaceDeclaration.class) - .map(ClassOrInterfaceDeclaration::isInterface).orElse(false); - if (n.isAbstract() || isInInterface) - return; // Allow abstract methods - ACFG acfg = new ACFG(); - acfg.build(n); - acfgMap.put(n, acfg); + visitCallable(n); super.visit(n, arg); } @Override public void visit(ConstructorDeclaration n, Void arg) { + visitCallable(n); + super.visit(n, arg); + } + + private void visitCallable(CallableDeclaration n) { boolean isInInterface = n.findAncestor(ClassOrInterfaceDeclaration.class) .map(ClassOrInterfaceDeclaration::isInterface).orElse(false); if (n.isAbstract() || isInInterface) @@ -109,7 +102,6 @@ public class I_ACFG extends Graph { ACFG acfg = new ACFG(); acfg.build(n); acfgMap.put(n, acfg); - super.visit(n, arg); } }, null); } @@ -139,104 +131,168 @@ public class I_ACFG extends Graph { } } + /** + * Expands movable variable actions in calls, enter and exit nodes to their + * own separate nodes, placing them in the order of execution, and maintaining + * the properties of each CFG (single sink, single source). + */ protected void expandCalls() { for (GraphNode graphNode : Set.copyOf(vertexSet())) { - GraphNode lastMovableNode = null; - Deque> firstNodeStack = new LinkedList<>(); - boolean firstMovable = true; - Deque callNodeStack = new LinkedList<>(); - VariableAction lastAction = graphNode.getVariableActions().isEmpty() ? null : graphNode.getLastVariableAction(); - VariableAction firstAction = graphNode.getVariableActions().isEmpty() ? null : graphNode.getVariableActions().get(0); - Set> movableNodes = new HashSet<>(); - for (VariableAction action : List.copyOf(graphNode.getVariableActions())) { - if (action instanceof VariableAction.CallMarker) { - VariableAction.CallMarker variableAction = (VariableAction.CallMarker) action; - // Compute the call node, if entering the marker. Additionally, it places the node - // in the graph and makes it control-dependent on its container. - if (!variableAction.isEnter()) { - CallNode callNode = callNodeStack.peek(); - if (!containsVertex(callNode)) { - addVertex(callNode); - if (lastMovableNode != null) - addControlFlowArc(lastMovableNode, callNode); - lastMovableNode = callNode; - } - callNodeStack.pop(); - } else { - CallNode callNode = CallNode.create(variableAction.getCall()); - if (graphNode.isImplicitInstruction()) - callNode.markAsImplicit(); - callNodeStack.push(callNode); - } - } else if (action instanceof VariableAction.Movable) { - if(!isEnter(graphNode) && !isExit(graphNode)) { - movableNodes.add(graphNode); - } - // Move the variable to its own node, add that node to the graph and connect it. - var movable = (VariableAction.Movable) action; - movable.move(this); - SyntheticNode realNode = movable.getRealNode(); - if (realNode instanceof CallNode.Return) { - CallNode callNode = callNodeStack.peek(); - addVertex(callNode); - addControlFlowArc(lastMovableNode, callNode); - if(movableNodes.contains(graphNode)) { - addControlFlowArc(realNode, graphNode); - movableNodes.remove(graphNode); - } - } else if(isExit(graphNode) && firstMovable) { - Set.copyOf(this.incomingEdgesOf(graphNode).stream() - .filter(Arc::isExecutableControlFlowArc) - .collect(Collectors.toSet())) - .forEach(arc -> { - GraphNode source = getEdgeSource(arc); - removeEdge(arc); - addEdge(source, realNode, arc); - }); - addControlFlowArc(realNode, graphNode); - } else { - if(firstAction.equals(action) && isEnter(graphNode)){ - firstNodeStack.add(movable.getRealNode()); - } - if(lastAction != null && lastAction.equals(action)){ - Set.copyOf(this.outgoingEdgesOf(graphNode).stream() - .filter(Arc::isExecutableControlFlowArc) - .collect(Collectors.toSet())) - .forEach(arc -> { - GraphNode target = getEdgeTarget(arc); - removeEdge(arc); - addEdge(realNode, target, arc); - }); - - // obtener primer nodo - if(!firstNodeStack.isEmpty()){ - addControlFlowArc(graphNode, firstNodeStack.pop()); - } - } - - if (movableNodes.contains(graphNode)) { - Set.copyOf(this.incomingEdgesOf(graphNode).stream() - .filter(Arc::isExecutableControlFlowArc) - .collect(Collectors.toSet())) - .forEach(arc -> { - GraphNode source = getEdgeSource(arc); - removeEdge(arc); - addEdge(source, realNode, arc); - }); - } - - if(!firstMovable) { - connectRealNode(graphNode, lastMovableNode, realNode); - } + if (isEnter(graphNode) || isExit(graphNode)) + expandEnterExitNode(graphNode); + else + expandCalls(graphNode); + } + } + + /** + * Extracts movable variable actions from a given Enter or Exit node. For the former, + * it places new nodes after it, moving the first instructions after formal-in nodes. + * For the latter, it places new nodes before it, moving the last instructions before + * formal-out nodes. + */ + protected void expandEnterExitNode(GraphNode node) { + LinkedList> list = new LinkedList<>(); + + // All actions should be movables, move each and connect them as a linked list + // movable1 --> movable2 ... --> movableN + for (VariableAction va : List.copyOf(node.getVariableActions())) { + if (va instanceof VariableAction.Movable movable) { + movable.move(this); + connectAppend(list, movable.getRealNode()); + } else { + throw new IllegalStateException("Enter node has non-movable actions"); + } + } + + if (!list.isEmpty()) + insertListInGraph(node, list, isEnter(node)); + } + + /** + * Inserts a list of nodes in the graph before or after the given node. This method + * requires that the insList of nodes already be linked together in the graph. + * This method is not affected by and does not modify non-executable control-flow edges.
+ * Graph before this method: a, b -> node -> x, y | ins[0] -> ... -> ins[N]
+ * Graph after this method (before the node): a, b -> ins[0] -> ... -> ins[N] -> node -> x, y
+ * Graph after this method (after the node): a, b -> node -> ins[0] -> ... -> ins[N] -> x, y + */ + protected void insertListInGraph(GraphNode node, LinkedList> insList, boolean after) { + if (after) { + for (Arc e : outgoingEdgesOf(node).stream() + .filter(Arc::isExecutableControlFlowArc) + .toList()) { + GraphNode target = getEdgeTarget(e); + removeEdge(e); + addEdge(insList.getLast(), target, e); + } + addControlFlowArc(node, insList.getFirst()); + } else { + for (Arc e : incomingEdgesOf(node).stream() + .filter(Arc::isExecutableControlFlowArc) + .toList()) { + GraphNode source = getEdgeSource(e); + removeEdge(e); + addEdge(source, insList.getFirst(), e); + } + addControlFlowArc(insList.getLast(), node); + } + } + + /** + * Expands the calls inside a single node, if any. The node that contains + * the calls is placed after all the calls happened, with the previous instruction + * connected to the first actual-in. The sequence of nodes in a single call is: + * previous instruction, actual-in*, call node, return node, actual-out*, graphNode, + * next instruction. * means that 0-n nodes may appear. + */ + protected void expandCalls(GraphNode graphNode) { + Iterator iterator = List.copyOf(graphNode.getVariableActions()).iterator(); + LinkedList> extracted = new LinkedList<>(); + while (iterator.hasNext()) { + VariableAction va = iterator.next(); + if (va instanceof VariableAction.Movable) + throw new IllegalStateException("Movable outside Enter/Exit or call"); + else if (va instanceof VariableAction.CallMarker marker && marker.isEnter()) { + extracted.addAll(extractMovables(iterator, marker)); + } + } + // Place extracted sequence before the node + if (!extracted.isEmpty()) + insertListInGraph(graphNode, extracted, false); + } + + /** + * Extracts movable nodes from a single call, recursively. + * @param iterator An iterator over a GraphNode's variable actions, which will be consumed + * until the closing CallMarker that matches callMarker is found. + * @param callMarker The call marker that enters the call to be analyzed. + * @return The list of nodes, inserted into the graph and connected to each other. + * Includes all nested calls. The first node in the list has no incoming edges. + * The last node in the list has no outgoing edges. + */ + protected LinkedList> extractMovables(Iterator iterator, VariableAction.CallMarker callMarker) { + LinkedList> res = new LinkedList<>(); + boolean input = true; + while (iterator.hasNext()) { + VariableAction va = iterator.next(); + if (va instanceof VariableAction.CallMarker newMarker) { + if (newMarker.isEnter()) { + // Recursively enter the next function and connect it. Consumes movables. + List> moved = extractMovables(iterator, newMarker); + connectAppend(res, moved); + } else if (newMarker.getCall().equals(callMarker.getCall())) { + return res; // Process ended. + } else { + throw new IllegalStateException("Invalid pairing of call markers"); + } + } else if (va instanceof VariableAction.Movable movable) { + movable.move(this); + // Check whether to insert call node (when we move from input to output) + if (input && !isActualIn(movable.getRealNode())) { + input = false; + insertNode(res, CallNode.create(callMarker.getCall()), callMarker); + // If it is a call to void method, add a blank "return" + if (!(movable.getRealNode() instanceof CallNode.Return)) + insertNode(res, CallNode.Return.create(callMarker.getCall()), callMarker); + else { + addEdge(res.getLast(), movable.getRealNode(), new ControlFlowArc.NonExecutable()); + res.add(movable.getRealNode()); + continue; } - firstMovable = false; - lastMovableNode = realNode; } + // Connect node to previous nodes and add to resulting list + connectAppend(res, movable.getRealNode()); } - assert callNodeStack.isEmpty(); } + throw new IllegalStateException("Closing call marker not found!"); } + /** Inserts a newly created CallNode or Return node into the graph and connects it to the sequence. */ + protected void insertNode(List> sequence, GraphNode node, VariableAction.CallMarker callMarker) { + addVertex(node); + if (callMarker.getGraphNode().isImplicitInstruction()) + node.markAsImplicit(); + if (node instanceof CallNode.Return) { + addEdge(sequence.getLast(), node, new ControlFlowArc.NonExecutable()); + sequence.add(node); + } else + connectAppend(sequence, node); + } + + /** Connects a node with the last element of the list (if not empty) and adds it to the list. */ + private void connectAppend(List> list, GraphNode node) { + if (!list.isEmpty()) + addControlFlowArc(list.getLast(), node); + list.add(node); + } + + /** Connects two lists of nodes in the graph (if the first is not empty) and concatenates them. */ + private void connectAppend(List> list, List> list2) { + if (!list.isEmpty()) + addControlFlowArc(list.getLast(), list2.getFirst()); + list.addAll(list2); + } protected void joinACFGs() { for (CallGraph.Edge call : callGraph.edgeSet()) { @@ -254,11 +310,13 @@ public class I_ACFG extends Graph { // Connections addControlFlowArc(callNode, enterNode); addControlFlowArc(exitNode, returnNode); - // TODO: move this arc creation to expandCalls - addEdge(callNode, returnNode, new ControlFlowArc.NonExecutable()); } } + protected static boolean isActualIn(GraphNode node) { + return node instanceof ActualIONode && ((ActualIONode) node).isInput(); + } + private static boolean isExit(GraphNode graphNode) { return graphNode instanceof MethodExitNode; } @@ -270,11 +328,4 @@ public class I_ACFG extends Graph { public void addControlFlowArc(GraphNode from, GraphNode to) { this.addEdge(from, to, new ControlFlowArc()); } - - /** Connects the real node to the proper parent, control-dependent-wise. */ - protected void connectRealNode(GraphNode graphNode, GraphNode lastNode, GraphNode realNode) { - if(lastNode != null && !lastNode.equals(realNode)) { - addControlFlowArc(lastNode, realNode); - } - } } \ No newline at end of file -- GitLab From 5e71bf05105d0be6c37911146f4995a0d30ab141 Mon Sep 17 00:00:00 2001 From: Carlos Galindo Date: Wed, 26 Mar 2025 18:16:19 +0100 Subject: [PATCH 4/7] Testing: extract testing code and resources from library. --- iacfg/pom.xml | 1 + .../main/java/es/upv/mist/slicing/I_ACFG.java | 28 +++---------------- .../mist/slicing/graphs/icfg/IACFGTest.java | 28 +++++++++++++++++++ .../tests => test/resources}/Test.java | 2 -- 4 files changed, 33 insertions(+), 26 deletions(-) create mode 100644 iacfg/src/test/java/es/upv/mist/slicing/graphs/icfg/IACFGTest.java rename iacfg/src/{main/java/es/upv/mist/slicing/tests => test/resources}/Test.java (93%) diff --git a/iacfg/pom.xml b/iacfg/pom.xml index d7fda393..aa13e6c5 100644 --- a/iacfg/pom.xml +++ b/iacfg/pom.xml @@ -32,6 +32,7 @@ es.upv.mist.slicing sdg-cli 1.3.0 + test
diff --git a/iacfg/src/main/java/es/upv/mist/slicing/I_ACFG.java b/iacfg/src/main/java/es/upv/mist/slicing/I_ACFG.java index 49a64807..a7763a59 100644 --- a/iacfg/src/main/java/es/upv/mist/slicing/I_ACFG.java +++ b/iacfg/src/main/java/es/upv/mist/slicing/I_ACFG.java @@ -8,11 +8,8 @@ import com.github.javaparser.ast.body.ClassOrInterfaceDeclaration; import com.github.javaparser.ast.body.ConstructorDeclaration; import com.github.javaparser.ast.body.MethodDeclaration; import com.github.javaparser.ast.visitor.VoidVisitorAdapter; -import com.github.javaparser.utils.CodeGenerationUtils; import es.upv.mist.slicing.arcs.Arc; import es.upv.mist.slicing.arcs.cfg.ControlFlowArc; -import es.upv.mist.slicing.cli.DOTAttributes; -import es.upv.mist.slicing.cli.GraphLog; import es.upv.mist.slicing.graphs.CallGraph; import es.upv.mist.slicing.graphs.ClassGraph; import es.upv.mist.slicing.graphs.Graph; @@ -30,7 +27,6 @@ import es.upv.mist.slicing.utils.StaticTypeSolver; import java.io.File; import java.io.FileNotFoundException; -import java.io.IOException; import java.util.*; import static es.upv.mist.slicing.util.SingletonCollector.toSingleton; @@ -39,18 +35,11 @@ public class I_ACFG extends Graph { protected static final Map, CFG> acfgMap = ASTUtils.newIdentityHashMap(); protected CallGraph callGraph; + protected boolean built = false; - public static void main(String[] args) throws IOException { - String ruta = "/src/main/java/es/upv/mist/slicing/tests/"; - String fichero = "Test.java"; - - I_ACFG iAcfg = new I_ACFG(); - iAcfg.generarACFG(ruta, fichero); - } - - public void generarACFG(String ruta, String fichero) throws IOException { + public void build(File file) { + if (built) return; NodeList units = new NodeList<>(); - File file = new File(CodeGenerationUtils.mavenModuleRoot(I_ACFG.class)+ruta+fichero); StaticJavaParser.getConfiguration().setAttributeComments(false); StaticTypeSolver.addTypeSolverJRE(); @@ -68,16 +57,7 @@ public class I_ACFG extends Graph { copyACFGs(); expandCalls(); joinACFGs(); - new GraphLog<>(this) { - @Override - protected DOTAttributes edgeAttributes(Arc arc) { - DOTAttributes att = super.edgeAttributes(arc); - if (arc.isNonExecutableControlFlowArc()) - att.add("style", "dashed"); - return att; - } - }.generateImages("migrafo"); - System.out.println("Grafo generado..."); + built = true; } protected void buildACFGs(NodeList nodeList) { diff --git a/iacfg/src/test/java/es/upv/mist/slicing/graphs/icfg/IACFGTest.java b/iacfg/src/test/java/es/upv/mist/slicing/graphs/icfg/IACFGTest.java new file mode 100644 index 00000000..73768481 --- /dev/null +++ b/iacfg/src/test/java/es/upv/mist/slicing/graphs/icfg/IACFGTest.java @@ -0,0 +1,28 @@ +package es.upv.mist.slicing.graphs.icfg; + +import es.upv.mist.slicing.I_ACFG; +import es.upv.mist.slicing.arcs.Arc; +import es.upv.mist.slicing.cli.DOTAttributes; +import es.upv.mist.slicing.cli.GraphLog; + +import java.io.File; +import java.io.IOException; + +public class IACFGTest { + public static void main(String[] args) throws IOException { + File file = new File(Thread.currentThread().getContextClassLoader().getResource("Test.java").getPath()); + + I_ACFG icfg = new I_ACFG(); + icfg.build(file); + new GraphLog<>(icfg) { + @Override + protected DOTAttributes edgeAttributes(Arc arc) { + DOTAttributes att = super.edgeAttributes(arc); + if (arc.isNonExecutableControlFlowArc()) + att.add("style", "dashed"); + return att; + } + }.generateImages("migrafo"); + System.out.println("Grafo generado..."); + } +} diff --git a/iacfg/src/main/java/es/upv/mist/slicing/tests/Test.java b/iacfg/src/test/resources/Test.java similarity index 93% rename from iacfg/src/main/java/es/upv/mist/slicing/tests/Test.java rename to iacfg/src/test/resources/Test.java index 4823a3b7..ff52b066 100644 --- a/iacfg/src/main/java/es/upv/mist/slicing/tests/Test.java +++ b/iacfg/src/test/resources/Test.java @@ -1,5 +1,3 @@ -package es.upv.mist.slicing.tests; - public class Test { public static int z = 0; -- GitLab From 36502e9b9c8a58fc08ec337790218e41ace195fd Mon Sep 17 00:00:00 2001 From: Carlos Galindo Date: Wed, 26 Mar 2025 18:28:47 +0100 Subject: [PATCH 5/7] Fix: do not add self-loops in movable multi-action actual nodes Previously, two movable actions that represented the same node had a self-loop because it connected to the last node (itself). --- iacfg/src/main/java/es/upv/mist/slicing/I_ACFG.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/iacfg/src/main/java/es/upv/mist/slicing/I_ACFG.java b/iacfg/src/main/java/es/upv/mist/slicing/I_ACFG.java index a7763a59..6eb3efcf 100644 --- a/iacfg/src/main/java/es/upv/mist/slicing/I_ACFG.java +++ b/iacfg/src/main/java/es/upv/mist/slicing/I_ACFG.java @@ -227,6 +227,8 @@ public class I_ACFG extends Graph { throw new IllegalStateException("Invalid pairing of call markers"); } } else if (va instanceof VariableAction.Movable movable) { + if (containsVertex(movable.getRealNode())) + continue; // Skip multi-action nodes movable.move(this); // Check whether to insert call node (when we move from input to output) if (input && !isActualIn(movable.getRealNode())) { -- GitLab From 517a902cc5eadf1314ce2f015330b54359902e00 Mon Sep 17 00:00:00 2001 From: Carlos Galindo Date: Wed, 26 Mar 2025 18:37:54 +0100 Subject: [PATCH 6/7] Rename IACFG to ICFG for simplicity --- .../{I_ACFG.java => graphs/icfg/ICFG.java} | 39 ++++++++++--------- .../icfg/{IACFGTest.java => ICFGTest.java} | 5 +-- 2 files changed, 23 insertions(+), 21 deletions(-) rename iacfg/src/main/java/es/upv/mist/slicing/{I_ACFG.java => graphs/icfg/ICFG.java} (91%) rename iacfg/src/test/java/es/upv/mist/slicing/graphs/icfg/{IACFGTest.java => ICFGTest.java} (89%) diff --git a/iacfg/src/main/java/es/upv/mist/slicing/I_ACFG.java b/iacfg/src/main/java/es/upv/mist/slicing/graphs/icfg/ICFG.java similarity index 91% rename from iacfg/src/main/java/es/upv/mist/slicing/I_ACFG.java rename to iacfg/src/main/java/es/upv/mist/slicing/graphs/icfg/ICFG.java index 6eb3efcf..aa69d1b9 100644 --- a/iacfg/src/main/java/es/upv/mist/slicing/I_ACFG.java +++ b/iacfg/src/main/java/es/upv/mist/slicing/graphs/icfg/ICFG.java @@ -1,4 +1,4 @@ -package es.upv.mist.slicing; +package es.upv.mist.slicing.graphs.icfg; import com.github.javaparser.StaticJavaParser; import com.github.javaparser.ast.CompilationUnit; @@ -31,9 +31,12 @@ import java.util.*; import static es.upv.mist.slicing.util.SingletonCollector.toSingleton; -public class I_ACFG extends Graph { +/** + * An interprocedural CFG, whose component CFGs are built as ACFGs. + */ +public class ICFG extends Graph { - protected static final Map, CFG> acfgMap = ASTUtils.newIdentityHashMap(); + protected static final Map, CFG> cfgMap = ASTUtils.newIdentityHashMap(); protected CallGraph callGraph; protected boolean built = false; @@ -51,16 +54,16 @@ public class I_ACFG extends Graph { } createClassGraph(units); - buildACFGs(units); + buildCFGs(units); createCallGraph(units); dataFlowAnalysis(); - copyACFGs(); + copyCFGs(); expandCalls(); - joinACFGs(); + joinCFGs(); built = true; } - protected void buildACFGs(NodeList nodeList) { + protected void buildCFGs(NodeList nodeList) { nodeList.accept(new VoidVisitorAdapter() { @Override public void visit(MethodDeclaration n, Void arg) { @@ -81,7 +84,7 @@ public class I_ACFG extends Graph { return; // Allow abstract methods ACFG acfg = new ACFG(); acfg.build(n); - acfgMap.put(n, acfg); + cfgMap.put(n, acfg); } }, null); } @@ -93,21 +96,21 @@ public class I_ACFG extends Graph { /** Create call graph from the list of compilation units. */ protected void createCallGraph(NodeList nodeList) { - callGraph = new CallGraph(acfgMap, ClassGraph.getInstance()); + callGraph = new CallGraph(cfgMap, ClassGraph.getInstance()); callGraph.build(nodeList); } /** Perform interprocedural analyses to determine the actual and formal nodes. */ protected void dataFlowAnalysis() { - new InterproceduralDefinitionFinder(callGraph, acfgMap).save(); // 3.1 - new InterproceduralUsageFinder(callGraph, acfgMap).save(); // 3.2 + new InterproceduralDefinitionFinder(callGraph, cfgMap).save(); // 3.1 + new InterproceduralUsageFinder(callGraph, cfgMap).save(); // 3.2 } /** Build a PDG per declaration, based on the CFGs built previously and enhanced by data analyses. */ - protected void copyACFGs() { - for (CFG acfg : acfgMap.values()) { - acfg.vertexSet().forEach(this::addVertex); - acfg.edgeSet().forEach(arc -> addEdge(acfg.getEdgeSource(arc), acfg.getEdgeTarget(arc), arc)); + protected void copyCFGs() { + for (CFG cfg : cfgMap.values()) { + cfg.vertexSet().forEach(this::addVertex); + cfg.edgeSet().forEach(arc -> addEdge(cfg.getEdgeSource(arc), cfg.getEdgeTarget(arc), arc)); } } @@ -276,7 +279,7 @@ public class I_ACFG extends Graph { list.addAll(list2); } - protected void joinACFGs() { + protected void joinCFGs() { for (CallGraph.Edge call : callGraph.edgeSet()) { // Nodes to be paired up GraphNode callNode = vertexSet().stream() @@ -287,8 +290,8 @@ public class I_ACFG extends Graph { .filter(n -> n instanceof CallNode.Return) .filter(n -> n.getAstNode().equals(call.getCall())) .collect(toSingleton()); - GraphNode enterNode = acfgMap.get(callGraph.getEdgeTarget(call).getDeclaration()).getRootNode(); - GraphNode exitNode = acfgMap.get(callGraph.getEdgeTarget(call).getDeclaration()).getExitNode(); + GraphNode enterNode = cfgMap.get(callGraph.getEdgeTarget(call).getDeclaration()).getRootNode(); + GraphNode exitNode = cfgMap.get(callGraph.getEdgeTarget(call).getDeclaration()).getExitNode(); // Connections addControlFlowArc(callNode, enterNode); addControlFlowArc(exitNode, returnNode); diff --git a/iacfg/src/test/java/es/upv/mist/slicing/graphs/icfg/IACFGTest.java b/iacfg/src/test/java/es/upv/mist/slicing/graphs/icfg/ICFGTest.java similarity index 89% rename from iacfg/src/test/java/es/upv/mist/slicing/graphs/icfg/IACFGTest.java rename to iacfg/src/test/java/es/upv/mist/slicing/graphs/icfg/ICFGTest.java index 73768481..5dee0b0b 100644 --- a/iacfg/src/test/java/es/upv/mist/slicing/graphs/icfg/IACFGTest.java +++ b/iacfg/src/test/java/es/upv/mist/slicing/graphs/icfg/ICFGTest.java @@ -1,6 +1,5 @@ package es.upv.mist.slicing.graphs.icfg; -import es.upv.mist.slicing.I_ACFG; import es.upv.mist.slicing.arcs.Arc; import es.upv.mist.slicing.cli.DOTAttributes; import es.upv.mist.slicing.cli.GraphLog; @@ -8,11 +7,11 @@ import es.upv.mist.slicing.cli.GraphLog; import java.io.File; import java.io.IOException; -public class IACFGTest { +public class ICFGTest { public static void main(String[] args) throws IOException { File file = new File(Thread.currentThread().getContextClassLoader().getResource("Test.java").getPath()); - I_ACFG icfg = new I_ACFG(); + ICFG icfg = new ICFG(); icfg.build(file); new GraphLog<>(icfg) { @Override -- GitLab From e807d207e51d38381afdaddc64632feb28720a92 Mon Sep 17 00:00:00 2001 From: Jonathan Andrade Date: Thu, 27 Mar 2025 03:05:21 -0500 Subject: [PATCH 7/7] Change insertNode to insertCallerNode just to be more descriptive --- .../src/main/java/es/upv/mist/slicing/graphs/icfg/ICFG.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/iacfg/src/main/java/es/upv/mist/slicing/graphs/icfg/ICFG.java b/iacfg/src/main/java/es/upv/mist/slicing/graphs/icfg/ICFG.java index aa69d1b9..7d21a870 100644 --- a/iacfg/src/main/java/es/upv/mist/slicing/graphs/icfg/ICFG.java +++ b/iacfg/src/main/java/es/upv/mist/slicing/graphs/icfg/ICFG.java @@ -236,10 +236,10 @@ public class ICFG extends Graph { // Check whether to insert call node (when we move from input to output) if (input && !isActualIn(movable.getRealNode())) { input = false; - insertNode(res, CallNode.create(callMarker.getCall()), callMarker); + insertCallerNode(res, CallNode.create(callMarker.getCall()), callMarker); // If it is a call to void method, add a blank "return" if (!(movable.getRealNode() instanceof CallNode.Return)) - insertNode(res, CallNode.Return.create(callMarker.getCall()), callMarker); + insertCallerNode(res, CallNode.Return.create(callMarker.getCall()), callMarker); else { addEdge(res.getLast(), movable.getRealNode(), new ControlFlowArc.NonExecutable()); res.add(movable.getRealNode()); @@ -254,7 +254,7 @@ public class ICFG extends Graph { } /** Inserts a newly created CallNode or Return node into the graph and connects it to the sequence. */ - protected void insertNode(List> sequence, GraphNode node, VariableAction.CallMarker callMarker) { + protected void insertCallerNode(List> sequence, GraphNode node, VariableAction.CallMarker callMarker) { addVertex(node); if (callMarker.getGraphNode().isImplicitInstruction()) node.markAsImplicit(); -- GitLab