diff --git a/src/main/java/tfm/graphs/augmented/ACFG.java b/src/main/java/tfm/graphs/augmented/ACFG.java index a8fbf15152e0ad136e44ec79c091fe89e6fd6148..b05e44e043bee72cbad1a46c8b7170fc6aea065e 100644 --- a/src/main/java/tfm/graphs/augmented/ACFG.java +++ b/src/main/java/tfm/graphs/augmented/ACFG.java @@ -10,11 +10,6 @@ public class ACFG extends CFG { addControlFlowEdge(from, to, new ControlFlowArc.NonExecutable()); } - @Override - protected void setExitNode(GraphNode exitNode) { - super.setExitNode(exitNode); - } - @Override protected CFGBuilder newCFGBuilder() { return new ACFGBuilder(this); diff --git a/src/main/java/tfm/graphs/augmented/ACFGBuilder.java b/src/main/java/tfm/graphs/augmented/ACFGBuilder.java index 3972f67bd20328d812df7efb07e069c52a2ab8f2..37bb23f6016a0eee41e287be7e57246fe042b00f 100644 --- a/src/main/java/tfm/graphs/augmented/ACFGBuilder.java +++ b/src/main/java/tfm/graphs/augmented/ACFGBuilder.java @@ -2,6 +2,7 @@ package tfm.graphs.augmented; import com.github.javaparser.ast.Node; import com.github.javaparser.ast.body.MethodDeclaration; +import com.github.javaparser.ast.body.Parameter; import com.github.javaparser.ast.expr.BooleanLiteralExpr; import com.github.javaparser.ast.expr.Expression; import com.github.javaparser.ast.stmt.*; @@ -239,13 +240,16 @@ public class ACFGBuilder extends CFGBuilder { if (!methodDeclaration.getBody().isPresent()) throw new IllegalStateException("The method must have a body!"); - graph.buildRootNode("ENTER " + methodDeclaration.getNameAsString(), methodDeclaration, TypeNodeFactory.fromType(NodeType.METHOD)); + graph.buildRootNode("ENTER " + methodDeclaration.getNameAsString(), methodDeclaration, TypeNodeFactory.fromType(NodeType.METHOD_ENTER)); hangingNodes.add(graph.getRootNode().get()); + for (Parameter param : methodDeclaration.getParameters()) + connectTo(addFormalInGraphNode(param)); methodDeclaration.getBody().get().accept(this, arg); returnList.stream().filter(node -> !hangingNodes.contains(node)).forEach(hangingNodes::add); + for (Parameter param : methodDeclaration.getParameters()) + connectTo(addFormalOutGraphNode(param)); nonExecHangingNodes.add(graph.getRootNode().get()); - GraphNode exitNode = connectTo(new EmptyStmt(), "Exit"); - ((ACFG) graph).setExitNode(exitNode); + connectTo(graph.addNode("Exit", new EmptyStmt(), TypeNodeFactory.fromType(NodeType.METHOD_EXIT))); } } diff --git a/src/main/java/tfm/graphs/cfg/CFG.java b/src/main/java/tfm/graphs/cfg/CFG.java index 97268d8e8ea6f6de89e8f1622b8bc4b20e737e3c..ddf50c34817674f88a1976793ca66e09a2266852 100644 --- a/src/main/java/tfm/graphs/cfg/CFG.java +++ b/src/main/java/tfm/graphs/cfg/CFG.java @@ -3,9 +3,9 @@ package tfm.graphs.cfg; import com.github.javaparser.ast.body.MethodDeclaration; import tfm.arcs.Arc; import tfm.arcs.cfg.ControlFlowArc; -import tfm.graphs.Graph; import tfm.graphs.GraphWithRootNode; import tfm.nodes.GraphNode; +import tfm.nodes.type.NodeType; import tfm.utils.NodeNotFoundException; import java.util.HashSet; @@ -21,7 +21,6 @@ import java.util.Set; */ public class CFG extends GraphWithRootNode { private boolean built = false; - protected GraphNode exitNode; public CFG() { super(); @@ -66,28 +65,20 @@ public class CFG extends GraphWithRootNode { @Override public boolean removeVertex(GraphNode graphNode) { - if (Objects.equals(graphNode, exitNode)) + // Cannot remove exit node + // Enter node's removal is checked in super#removeVertex(GraphNode) + if (graphNode.getNodeType() == NodeType.METHOD_EXIT) return false; return super.removeVertex(graphNode); } - public GraphNode getExitNode() { - return exitNode; - } - - protected void setExitNode(GraphNode exitNode) { - if (this.exitNode != null) - throw new IllegalStateException("Exit node already set!"); - this.exitNode = exitNode; - } - @Override public void build(MethodDeclaration method) { method.accept(newCFGBuilder(), null); - if (exitNode == null) - throw new IllegalStateException("Exit node missing!"); + if (vertexSet().stream().noneMatch(n -> n.getNodeType() == NodeType.METHOD_EXIT)) + throw new IllegalStateException("There is no exit node after building the graph"); built = true; - } + }/**/ @Override public boolean isBuilt() { diff --git a/src/main/java/tfm/graphs/cfg/CFGBuilder.java b/src/main/java/tfm/graphs/cfg/CFGBuilder.java index 624bb7b0900fc26d0aa422e3ab2448e3cd900d05..0038c506b865c8e682a3b7c7f1e0970395dcc440 100644 --- a/src/main/java/tfm/graphs/cfg/CFGBuilder.java +++ b/src/main/java/tfm/graphs/cfg/CFGBuilder.java @@ -9,13 +9,15 @@ import com.github.javaparser.ast.stmt.*; import com.github.javaparser.ast.visitor.VoidVisitor; import com.github.javaparser.ast.visitor.VoidVisitorAdapter; import tfm.nodes.GraphNode; -import tfm.nodes.NodeFactory; import tfm.nodes.TypeNodeFactory; import tfm.nodes.type.NodeType; import tfm.utils.ASTUtils; import java.util.*; +import static tfm.nodes.type.NodeType.FORMAL_IN; +import static tfm.nodes.type.NodeType.FORMAL_OUT; + /** * Populates a {@link CFG}, given one and an AST root node. * For now it only accepts {@link MethodDeclaration} as roots, as it disallows @@ -286,64 +288,46 @@ public class CFGBuilder extends VoidVisitorAdapter { @Override public void visit(MethodDeclaration methodDeclaration, Void arg) { + // Sanity checks if (graph.getRootNode().isPresent()) throw new IllegalStateException("CFG is only allowed for one method, not multiple!"); if (!methodDeclaration.getBody().isPresent()) - throw new IllegalStateException("The method must have a body!"); - - graph.buildRootNode("ENTER " + methodDeclaration.getNameAsString(), methodDeclaration, TypeNodeFactory.fromType(NodeType.METHOD)); - - // Compute variable in and out expressions (necessary to compute data dependence in SDG) - List inVariableExpressions = new ArrayList<>(); - List outVariableExpressions = new ArrayList<>(); - - for (Parameter parameter : methodDeclaration.getParameters()) { - // In expression - VariableDeclarationExpr inVariableDeclarationExpr = new VariableDeclarationExpr( - new VariableDeclarator( - parameter.getType(), - parameter.getNameAsString(), - new NameExpr(parameter.getNameAsString() + "_in") - ) - ); - - ExpressionStmt inExprStmt = new ExpressionStmt(inVariableDeclarationExpr); - - inVariableExpressions.add(inExprStmt); - - // Out expression - VariableDeclarationExpr outVariableDeclarationExpr = new VariableDeclarationExpr( - new VariableDeclarator( - parameter.getType(), - parameter.getNameAsString() + "_out", - new NameExpr(parameter.getNameAsString()) - ) - ); - - ExpressionStmt outExprStmt = new ExpressionStmt(outVariableDeclarationExpr); - - outVariableExpressions.add(outExprStmt); - } + throw new IllegalStateException("The method must have a body! Abstract methods have no CFG"); + // Create the root node + graph.buildRootNode( + "ENTER " + methodDeclaration.getNameAsString(), + methodDeclaration, + TypeNodeFactory.fromType(NodeType.METHOD_ENTER)); hangingNodes.add(graph.getRootNode().get()); - - // Add in variable nodes - for (ExpressionStmt expressionStmt : inVariableExpressions) { - GraphNode node = this.graph.addNode(expressionStmt.toString(), expressionStmt, TypeNodeFactory.fromType(NodeType.VARIABLE_IN)); - connectTo(node); - } - + // Create and connect formal-in nodes sequentially + for (Parameter param : methodDeclaration.getParameters()) + connectTo(addFormalInGraphNode(param)); + // Visit the body of the method methodDeclaration.getBody().get().accept(this, arg); - + // Append all return statements (without repetition) returnList.stream().filter(node -> !hangingNodes.contains(node)).forEach(hangingNodes::add); - // Add out variable nodes - for (ExpressionStmt expressionStmt : outVariableExpressions) { - GraphNode node = this.graph.addNode(expressionStmt.toString(), expressionStmt, TypeNodeFactory.fromType(NodeType.VARIABLE_OUT)); - connectTo(node); - } + // Create and connect formal-out nodes sequentially + for (Parameter param : methodDeclaration.getParameters()) + connectTo(addFormalOutGraphNode(param)); + // Create and connect the exit node + connectTo(graph.addNode("Exit", new EmptyStmt(), TypeNodeFactory.fromType(NodeType.METHOD_EXIT))); + } + + protected GraphNode addFormalInGraphNode(Parameter param) { + ExpressionStmt exprStmt = new ExpressionStmt(new VariableDeclarationExpr(new VariableDeclarator( + param.getType(), + param.getNameAsString(), + new NameExpr(param.getNameAsString() + "_in")))); + return graph.addNode(exprStmt.toString(), exprStmt, TypeNodeFactory.fromType(FORMAL_IN)); + } - GraphNode exitNode = connectTo(new EmptyStmt(), "Exit"); - graph.setExitNode(exitNode); + protected GraphNode addFormalOutGraphNode(Parameter param) { + ExpressionStmt exprStmt = new ExpressionStmt(new VariableDeclarationExpr(new VariableDeclarator( + param.getType(), + param.getNameAsString() + "_out", + new NameExpr(param.getNameAsString())))); + return graph.addNode(exprStmt.toString(), exprStmt, TypeNodeFactory.fromType(FORMAL_OUT)); } } diff --git a/src/main/java/tfm/graphs/pdg/ControlDependencyBuilder.java b/src/main/java/tfm/graphs/pdg/ControlDependencyBuilder.java index 4e2993342c00a2fc02d6ebac591e790d49277c7f..fd4cd4ceede4c3bed52ed32d4b7aedf78e2dcfd0 100644 --- a/src/main/java/tfm/graphs/pdg/ControlDependencyBuilder.java +++ b/src/main/java/tfm/graphs/pdg/ControlDependencyBuilder.java @@ -4,8 +4,10 @@ import tfm.arcs.Arc; import tfm.graphs.augmented.PPDG; import tfm.graphs.cfg.CFG; import tfm.nodes.GraphNode; +import tfm.nodes.type.NodeType; -import java.util.*; +import java.util.HashSet; +import java.util.Set; import java.util.stream.Collectors; /** @@ -41,7 +43,7 @@ class ControlDependencyBuilder { roots.remove(cfg.getRootNode().get()); Set> cfgNodes = new HashSet<>(cfg.vertexSet()); - cfgNodes.remove(cfg.getExitNode()); + cfgNodes.removeIf(node -> node.getNodeType() == NodeType.METHOD_EXIT); for (GraphNode node : cfgNodes) registerNode(node); diff --git a/src/main/java/tfm/graphs/pdg/PDGBuilder.java b/src/main/java/tfm/graphs/pdg/PDGBuilder.java index 3573f6b39a07db0f0e8bb12466c19a62f33530ff..321edd034970429b6807ebe1bc5c8a21f8903e1c 100644 --- a/src/main/java/tfm/graphs/pdg/PDGBuilder.java +++ b/src/main/java/tfm/graphs/pdg/PDGBuilder.java @@ -7,11 +7,8 @@ import com.github.javaparser.ast.expr.VariableDeclarationExpr; import com.github.javaparser.ast.stmt.BlockStmt; import com.github.javaparser.ast.stmt.ExpressionStmt; import tfm.graphs.cfg.CFG; -import tfm.nodes.GraphNode; import tfm.nodes.type.NodeType; -import java.util.Objects; - /** * Populates a {@link PDG}, given a complete {@link CFG}, an empty {@link PDG} and an AST root node. * For now it only accepts {@link MethodDeclaration} as root, as it can only receive a single CFG. @@ -51,7 +48,7 @@ public class PDGBuilder { // Copy nodes from CFG to PDG cfg.vertexSet().stream() - .filter(node -> !Objects.equals(node, cfg.getExitNode())) + .filter(node -> node.getNodeType() != NodeType.METHOD_EXIT) .forEach(node -> pdg.addVertex(node)); assert this.cfg.getRootNode().isPresent(); @@ -68,7 +65,7 @@ public class PDGBuilder { // Build data dependency of "out" variables pdg.vertexSet().stream() - .filter(node -> node.getNodeType() == NodeType.VARIABLE_OUT) + .filter(node -> node.getNodeType() == NodeType.FORMAL_OUT) .forEach(node -> { assert node.getAstNode() instanceof ExpressionStmt; diff --git a/src/main/java/tfm/graphs/sdg/MethodCallReplacerVisitor.java b/src/main/java/tfm/graphs/sdg/MethodCallReplacerVisitor.java index ace0075eeac90cada1547361892d4792f8719612..5331beb798e20fc8eb7eb65d9e3b78aff714f5fc 100644 --- a/src/main/java/tfm/graphs/sdg/MethodCallReplacerVisitor.java +++ b/src/main/java/tfm/graphs/sdg/MethodCallReplacerVisitor.java @@ -148,7 +148,7 @@ class MethodCallReplacerVisitor extends VoidVisitorAdapter { ExpressionStmt inExprStmt = new ExpressionStmt(inVariableDeclarationExpr); - GraphNode argumentInNode = sdg.addNode(inExprStmt.toString(), inExprStmt, TypeNodeFactory.fromType(NodeType.VARIABLE_IN)); + GraphNode argumentInNode = sdg.addNode(inExprStmt.toString(), inExprStmt, TypeNodeFactory.fromType(NodeType.ACTUAL_IN)); sdg.addControlDependencyArc(methodCallNode, argumentInNode); @@ -169,7 +169,8 @@ class MethodCallReplacerVisitor extends VoidVisitorAdapter { Optional> optionalParameterInNode = sdg.outgoingEdgesOf(methodDeclarationNode).stream() .map(arc -> (GraphNode) sdg.getEdgeTarget(arc)) - .filter(node -> node.getNodeType() == NodeType.VARIABLE_IN && node.getInstruction().contains(parameter.getNameAsString() + "_in")) + .filter(node -> node.getNodeType() == NodeType.FORMAL_IN) + .filter(node -> node.getInstruction().contains(parameter.getNameAsString() + "_in")) .findFirst(); if (optionalParameterInNode.isPresent()) { @@ -218,7 +219,7 @@ class MethodCallReplacerVisitor extends VoidVisitorAdapter { ExpressionStmt outExprStmt = new ExpressionStmt(outVariableAssignExpr); - GraphNode argumentOutNode = sdg.addNode(outExprStmt.toString(), outExprStmt, TypeNodeFactory.fromType(NodeType.VARIABLE_OUT)); + GraphNode argumentOutNode = sdg.addNode(outExprStmt.toString(), outExprStmt, TypeNodeFactory.fromType(NodeType.ACTUAL_OUT)); sdg.addControlDependencyArc(methodCallNode, argumentOutNode); @@ -226,7 +227,8 @@ class MethodCallReplacerVisitor extends VoidVisitorAdapter { Optional> optionalParameterOutNode = sdg.outgoingEdgesOf(methodDeclarationNode).stream() .map(arc -> (GraphNode) sdg.getEdgeTarget(arc)) - .filter(node -> node.getNodeType() == NodeType.VARIABLE_OUT && node.getInstruction().contains(parameter.getNameAsString() + "_out")) + .filter(node -> node.getNodeType() == NodeType.FORMAL_OUT) + .filter(node -> node.getInstruction().contains(parameter.getNameAsString() + "_out")) .findFirst(); // Handle data dependency: remove arc from method call node and add it to OUT node diff --git a/src/main/java/tfm/nodes/type/NodeType.java b/src/main/java/tfm/nodes/type/NodeType.java index 2cb3fba400a1839efaa476e1160a28e21db1c9fa..1851d62c70cdfac56c70381a6af46b789bbc247c 100644 --- a/src/main/java/tfm/nodes/type/NodeType.java +++ b/src/main/java/tfm/nodes/type/NodeType.java @@ -1,10 +1,27 @@ package tfm.nodes.type; public enum NodeType { + /** An instruction in the program. Statements, predicates and + * pseudo-predicates are included in this {@link NodeType}. */ STATEMENT, - METHOD, + /** The Enter node of a method. */ + METHOD_ENTER, + /** The Exit node of a method. */ + METHOD_EXIT, + /** A method call, that is contained in a {@link #STATEMENT} node. */ METHOD_CALL, - VARIABLE_IN, - VARIABLE_OUT, - METHOD_CALL_RETURN + /** An argument or globally accesible variable that + * has been used in a method call. */ + ACTUAL_IN, + /** An argument or globally accessible variable that + * has been modified in a method call. */ + ACTUAL_OUT, + /** An argument or globally accessible variable that + * has been used in a method declaration. */ + FORMAL_IN, + /** An argument or globally accessible variable that + * has been modified in a method declaration. */ + FORMAL_OUT, + /** A node representing the return value of a non-void method call. */ + METHOD_CALL_RETURN, } diff --git a/src/test/java/tfm/graphs/pdg/HandCraftedGraphs.java b/src/test/java/tfm/graphs/pdg/HandCraftedGraphs.java index d07ab93f5541fddb83b2d5c33eb4b961c5a38a89..1a6725352ab9eb16dfebb78c1537d86054539ac5 100644 --- a/src/test/java/tfm/graphs/pdg/HandCraftedGraphs.java +++ b/src/test/java/tfm/graphs/pdg/HandCraftedGraphs.java @@ -19,7 +19,7 @@ public class HandCraftedGraphs { public static APDG problem1WithGotos() { // Generate the control flow of a graph ACFG cfg = new ACFG(); - cfg.buildRootNode("ENTER Problem1", new MethodDeclaration(new NodeList<>(), new VoidType(), "Problem1"), TypeNodeFactory.fromType(NodeType.METHOD)); + cfg.buildRootNode("ENTER Problem1", new MethodDeclaration(new NodeList<>(), new VoidType(), "Problem1"), TypeNodeFactory.fromType(NodeType.METHOD_ENTER)); GraphNode wx = cfg.addNode("while (X)", new WhileStmt()); GraphNode ify = cfg.addNode("L: if (Y)", new IfStmt()); GraphNode ifz = cfg.addNode("if (Z)", new IfStmt()); @@ -58,7 +58,7 @@ public class HandCraftedGraphs { public static APDG problem1ContinueWithGotos() { // Generate the control flow of a graph ACFG cfg = new ACFG(); - cfg.buildRootNode("ENTER Problem1", new MethodDeclaration(new NodeList<>(), new VoidType(), "Problem1"), TypeNodeFactory.fromType(NodeType.METHOD)); + cfg.buildRootNode("ENTER Problem1", new MethodDeclaration(new NodeList<>(), new VoidType(), "Problem1"), TypeNodeFactory.fromType(NodeType.METHOD_ENTER)); GraphNode wx = cfg.addNode("while (X)", new WhileStmt()); GraphNode ify = cfg.addNode("L: if (Y)", new IfStmt()); GraphNode ifz = cfg.addNode("if (Z)", new IfStmt()); diff --git a/src/test/java/tfm/graphs/pdg/PDGTests.java b/src/test/java/tfm/graphs/pdg/PDGTests.java index 2f6e54797d6a4793bba0cb39f91d7c6803d475bf..3063df63c4869b0841dd6db0a6987beafc005146 100644 --- a/src/test/java/tfm/graphs/pdg/PDGTests.java +++ b/src/test/java/tfm/graphs/pdg/PDGTests.java @@ -82,7 +82,7 @@ public class PDGTests { CFG cfg = new CFG(); cfg.build(root); PDG pdg = new PDG(cfg); - pdg.buildRootNode("ENTER " + methodName, root, TypeNodeFactory.fromType(NodeType.METHOD)); + pdg.buildRootNode("ENTER " + methodName, root, TypeNodeFactory.fromType(NodeType.METHOD_ENTER)); ctrlDepBuilder = new ControlDependencyBuilder(pdg, cfg); ctrlDepBuilder.analyze(); @@ -90,13 +90,13 @@ public class PDGTests { ACFG acfg = new ACFG(); acfg.build(root); APDG apdg = new APDG(acfg); - apdg.buildRootNode("ENTER " + methodName, root, TypeNodeFactory.fromType(NodeType.METHOD)); + apdg.buildRootNode("ENTER " + methodName, root, TypeNodeFactory.fromType(NodeType.METHOD_ENTER)); ctrlDepBuilder = new ControlDependencyBuilder(apdg, acfg); ctrlDepBuilder.analyze(); // Create PPDG PPDG ppdg = new PPDG(acfg); - ppdg.buildRootNode("ENTER " + methodName, root, TypeNodeFactory.fromType(NodeType.METHOD)); + ppdg.buildRootNode("ENTER " + methodName, root, TypeNodeFactory.fromType(NodeType.METHOD_ENTER)); ctrlDepBuilder = new ControlDependencyBuilder(ppdg, acfg); ctrlDepBuilder.analyze();