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 - -