diff --git a/iacfg/pom.xml b/iacfg/pom.xml
index e4ef82dc6454abed74ad8a3b58a8b8410c203568..aa13e6c54ef70ec2aee225c95a7757ce796c3046 100644
--- a/iacfg/pom.xml
+++ b/iacfg/pom.xml
@@ -16,6 +16,7 @@
21
UTF-8
+
commons-cli
@@ -26,12 +27,12 @@
es.upv.mist.slicing
sdg-core
1.3.0
- compile
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
deleted file mode 100644
index bc4b19c5658a7951e47e495a64cd98d358a89399..0000000000000000000000000000000000000000
--- a/iacfg/src/main/java/es/upv/mist/slicing/I_ACFG.java
+++ /dev/null
@@ -1,300 +0,0 @@
-package es.upv.mist.slicing;
-
-import com.github.javaparser.StaticJavaParser;
-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.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;
-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;
-import es.upv.mist.slicing.graphs.augmented.ACFG;
-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.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;
-
-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 {
- 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 {
- NodeList units = new NodeList<>();
- 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 {
- units.add(StaticJavaParser.parse(file));
- } catch (FileNotFoundException e) {
- System.out.println("No se encontrĂ³ el archivo");
- }
-
- createClassGraph(units);
- buildACFGs(units);
- createCallGraph(units);
- dataFlowAnalysis();
- 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...");
- }
-
- protected void buildACFGs(NodeList nodeList) {
- 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);
- super.visit(n, arg);
- }
-
- @Override
- public void visit(ConstructorDeclaration 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);
- super.visit(n, arg);
- }
- }, null);
- }
-
- /** Create class graph from the list of compilation units. */
- protected void createClassGraph(NodeList nodeList){
- ClassGraph.getNewInstance().build(nodeList);
- }
-
- /** Create call graph from the list of compilation units. */
- protected void createCallGraph(NodeList nodeList) {
- callGraph = new CallGraph(acfgMap, 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
- }
-
- /** 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 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);
- }
- }
- firstMovable = false;
- lastMovableNode = realNode;
- }
- }
- assert callNodeStack.isEmpty();
- }
- }
-
-
- 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);
- }
- });
- }
- }
- }
-
- 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;
- }
-
- private boolean isEnter(GraphNode> graphNode) {
- return this.incomingEdgesOf(graphNode).isEmpty();
- }
-
- 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
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
new file mode 100644
index 0000000000000000000000000000000000000000..7d21a8703082c3acc01119d9f9d8ad39e54b8a8c
--- /dev/null
+++ b/iacfg/src/main/java/es/upv/mist/slicing/graphs/icfg/ICFG.java
@@ -0,0 +1,316 @@
+package es.upv.mist.slicing.graphs.icfg;
+
+import com.github.javaparser.StaticJavaParser;
+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.ClassOrInterfaceDeclaration;
+import com.github.javaparser.ast.body.ConstructorDeclaration;
+import com.github.javaparser.ast.body.MethodDeclaration;
+import com.github.javaparser.ast.visitor.VoidVisitorAdapter;
+import es.upv.mist.slicing.arcs.Arc;
+import es.upv.mist.slicing.arcs.cfg.ControlFlowArc;
+import es.upv.mist.slicing.graphs.CallGraph;
+import es.upv.mist.slicing.graphs.ClassGraph;
+import es.upv.mist.slicing.graphs.Graph;
+import es.upv.mist.slicing.graphs.augmented.ACFG;
+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.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 java.io.File;
+import java.io.FileNotFoundException;
+import java.util.*;
+
+import static es.upv.mist.slicing.util.SingletonCollector.toSingleton;
+
+/**
+ * An interprocedural CFG, whose component CFGs are built as ACFGs.
+ */
+public class ICFG extends Graph {
+
+ protected static final Map, CFG> cfgMap = ASTUtils.newIdentityHashMap();
+ protected CallGraph callGraph;
+ protected boolean built = false;
+
+ public void build(File file) {
+ if (built) return;
+ NodeList units = new NodeList<>();
+
+ StaticJavaParser.getConfiguration().setAttributeComments(false);
+ StaticTypeSolver.addTypeSolverJRE();
+
+ try {
+ units.add(StaticJavaParser.parse(file));
+ } catch (FileNotFoundException e) {
+ System.out.println("No se encontrĂ³ el archivo");
+ }
+
+ createClassGraph(units);
+ buildCFGs(units);
+ createCallGraph(units);
+ dataFlowAnalysis();
+ copyCFGs();
+ expandCalls();
+ joinCFGs();
+ built = true;
+ }
+
+ protected void buildCFGs(NodeList nodeList) {
+ nodeList.accept(new VoidVisitorAdapter() {
+ @Override
+ public void visit(MethodDeclaration n, Void arg) {
+ 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)
+ return; // Allow abstract methods
+ ACFG acfg = new ACFG();
+ acfg.build(n);
+ cfgMap.put(n, acfg);
+ }
+ }, null);
+ }
+
+ /** Create class graph from the list of compilation units. */
+ protected void createClassGraph(NodeList nodeList){
+ ClassGraph.getNewInstance().build(nodeList);
+ }
+
+ /** Create call graph from the list of compilation units. */
+ protected void createCallGraph(NodeList nodeList) {
+ 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, 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 copyCFGs() {
+ for (CFG cfg : cfgMap.values()) {
+ cfg.vertexSet().forEach(this::addVertex);
+ cfg.edgeSet().forEach(arc -> addEdge(cfg.getEdgeSource(arc), cfg.getEdgeTarget(arc), arc));
+ }
+ }
+
+ /**
+ * 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())) {
+ 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) {
+ 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())) {
+ input = false;
+ 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))
+ insertCallerNode(res, CallNode.Return.create(callMarker.getCall()), callMarker);
+ else {
+ addEdge(res.getLast(), movable.getRealNode(), new ControlFlowArc.NonExecutable());
+ res.add(movable.getRealNode());
+ continue;
+ }
+ }
+ // Connect node to previous nodes and add to resulting list
+ connectAppend(res, movable.getRealNode());
+ }
+ }
+ 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 insertCallerNode(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 joinCFGs() {
+ 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 = cfgMap.get(callGraph.getEdgeTarget(call).getDeclaration()).getRootNode();
+ GraphNode> exitNode = cfgMap.get(callGraph.getEdgeTarget(call).getDeclaration()).getExitNode();
+ // Connections
+ addControlFlowArc(callNode, enterNode);
+ addControlFlowArc(exitNode, returnNode);
+ }
+ }
+
+ protected static boolean isActualIn(GraphNode> node) {
+ return node instanceof ActualIONode && ((ActualIONode) node).isInput();
+ }
+
+ private static boolean isExit(GraphNode> graphNode) {
+ return graphNode instanceof MethodExitNode;
+ }
+
+ private boolean isEnter(GraphNode> graphNode) {
+ return this.incomingEdgesOf(graphNode).isEmpty();
+ }
+
+ public void addControlFlowArc(GraphNode> from, GraphNode> to) {
+ this.addEdge(from, to, new ControlFlowArc());
+ }
+}
\ No newline at end of file
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 0000000000000000000000000000000000000000..4186944a642829c625efeaeb7e4db8f359a1cb75
--- /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);
+ }
+ );
+ }
+}
diff --git a/iacfg/src/test/java/es/upv/mist/slicing/graphs/icfg/ICFGTest.java b/iacfg/src/test/java/es/upv/mist/slicing/graphs/icfg/ICFGTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..5dee0b0b79a50c85c971991dfbb14ff3b5bd6bdc
--- /dev/null
+++ b/iacfg/src/test/java/es/upv/mist/slicing/graphs/icfg/ICFGTest.java
@@ -0,0 +1,27 @@
+package es.upv.mist.slicing.graphs.icfg;
+
+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 ICFGTest {
+ public static void main(String[] args) throws IOException {
+ File file = new File(Thread.currentThread().getContextClassLoader().getResource("Test.java").getPath());
+
+ ICFG icfg = new ICFG();
+ 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 4823a3b740b30eec2b07eb7b24dffc88d532d49c..ff52b0664df190ad6fd88587b195c4bc5540d038 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;
diff --git a/pom.xml b/pom.xml
index 77bee6c44f334f7ae9a788dc7038bb14e9198995..e4ccbc4f6ab491a1355567e480ceb77b3589be8f 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 68114d8fb86410f6c067a49fe71db33eb6a7fd0f..397f69574f94c6c5c8a8a4ba5ac2a32d5b97c6f5 100644
--- a/sdg-core/pom.xml
+++ b/sdg-core/pom.xml
@@ -12,11 +12,6 @@
1.3.0
-
- 11
- 11
-
-