diff --git a/.gitignore b/.gitignore index d947638b907cb3c9a1e887d86071337d19dfaf8a..77901fe0104c73e4efa7d26cd2a376b950baabd9 100644 --- a/.gitignore +++ b/.gitignore @@ -2,3 +2,4 @@ .idea/ target/ out/ +.settings \ No newline at end of file diff --git a/lib/graphlib.jar b/lib/graphlib.jar deleted file mode 100644 index cf0e9844307d1dff58335a2e6f3500c532f7d58d..0000000000000000000000000000000000000000 Binary files a/lib/graphlib.jar and /dev/null differ diff --git a/pom.xml b/pom.xml index 296359213f1fac45fc9721e94940f196b7fe0c0c..53b3a44c9465fe535cdba60083353e07c194728a 100644 --- a/pom.xml +++ b/pom.xml @@ -21,7 +21,6 @@ - com.github.javaparser javaparser-symbol-solver-core @@ -41,5 +40,17 @@ 1.3.0 + + org.jgrapht + jgrapht-io + 1.3.0 + + + + org.junit.jupiter + junit-jupiter + 5.5.2 + test + \ No newline at end of file diff --git a/readme.md b/readme.md index 5ef10f60754416053c5d780b1ec52590789b430d..357cd4e0835b30c81e5bf09981eec872f2e84540 100644 --- a/readme.md +++ b/readme.md @@ -34,9 +34,9 @@ Find `Slice` class (`tfm/slicing`), set the program path and execute. The sliced ## Structure -Graphs are built using a library called `graphlib`, located in `lib/graphlib.jar`. This library is old and has some issues I had to fix... +Graphs are built using a library called `JGraphT`. -The main class is the `Graph` class, which extends from `graphlib`'s `Graph` class. This class includes some behaviour fixes, and some general interest methods (like `toString`, `toGraphvizRepresentation`, etc.) +The main class is the `Graph` class, which extends from `JGraphT`'s `DefaultDirectedGraph` class. This class includes some general interest methods (like `toString`, etc.) Every graph has a set of nodes and arrows. `GraphNode` and `Arc` classes are used to represent them respectively. @@ -104,7 +104,7 @@ Forget about the `tfm/scopes` folder, it was an idea I had to discard and it has ### General -- Switch to a (much) better graph library like [JGraphT](https://jgrapht.org/). It also supports graph visualization +- Switch to a (much) better graph library like [JGraphT](https://jgrapht.org/). It also supports graph visualization (done). - Performance review - Make a test suite (test graph building, slicing, etc.) - Add support to more Java language features (lambdas, etc.) @@ -114,28 +114,44 @@ Forget about the `tfm/scopes` folder, it was an idea I had to discard and it has ### Build a CFG from a program ```java -public CFGGraph buildCFG(File programFile) { - JavaParser.getStaticConfiguration().setAttributeComments(false); // Always disable comments, just in case - - Node astRoot = JavaParser.parse(programFile); - - return Graphs.CFG.fromASTNode(astRoot); // Creates a new graph representing the program +public class Example { + public CFG buildCFG(File programFile) { + // Always disable attribution of comments, just in case + JavaParser.getStaticConfiguration().setAttributeComments(false); + + Node astRoot = JavaParser.parse(programFile); + Optional optMethod = astRoot.findFirst(MethodDeclaration.class); + if (!optMethod.isPresent) + throw new RuntimeException("No method could be found"); + + // Creates a new graph representing the program + CFG cfg = new CFG(); + cfg.build(optMethod.get()); + return cfg; + } } ``` ### Get a slice of the PDG of a program ```java -public PDGGraph getSlice(File program, SlicingCriterion slicingCriterion) { - JavaParser.getStaticConfiguration().setAttributeComments(false); // Always disable comments, just in case - - Node astRoot = JavaParser.parse(programFile); - - PDGGraph pdgGraph = Graphs.PDG.fromASTNode(astRoot); - - return pdgGraph.slice(slicingCriterion); +public class Example { + public PDGGraph getSlice(File program, SlicingCriterion slicingCriterion) { + // Always disable attribution of comments, just in case + JavaParser.getStaticConfiguration().setAttributeComments(false); + + Node astRoot = JavaParser.parse(programFile); + Optional optMethod = astRoot.findFirst(MethodDeclaration.class); + if (!optMethod.isPresent) + throw new RuntimeException("No method could be found"); + + // Creates a new graph representing the program + PDG pdg = new PDG(); + pdg.build(optMethod.get()); + // Slice PDG + return pdg.slice(slicingCriterion); + } } - ``` ## Workflow @@ -143,7 +159,7 @@ public PDGGraph getSlice(File program, SlicingCriterion slicingCriterion) { - Branches: - `master` (only for stable versions) - `develop` (main branch) - - `` + - `-name` 1. Discover a new feature/fix 2. Open an issue describing it and assign it diff --git a/src/main/java/tfm/arcs/Arc.java b/src/main/java/tfm/arcs/Arc.java index 5c13d3195c683795b099ee0c71ef090f2def7ace..143c900bff50fe3ad595d65285a5e9b454ccb8f8 100644 --- a/src/main/java/tfm/arcs/Arc.java +++ b/src/main/java/tfm/arcs/Arc.java @@ -1,72 +1,79 @@ package tfm.arcs; -import tfm.arcs.data.ArcData; +import org.jgrapht.graph.DefaultEdge; +import org.jgrapht.io.Attribute; +import tfm.arcs.cfg.ControlFlowArc; +import tfm.arcs.pdg.ControlDependencyArc; +import tfm.arcs.pdg.DataDependencyArc; import tfm.nodes.GraphNode; +import java.util.HashMap; +import java.util.Map; import java.util.Objects; -public abstract class Arc extends edg.graphlib.Arrow { +public abstract class Arc extends DefaultEdge { + public Arc() { - @SuppressWarnings("unchecked") - public Arc(GraphNode from, GraphNode to) { - super((edg.graphlib.Vertex) from, (edg.graphlib.Vertex) to); } - public abstract boolean isControlFlowArrow(); + public final boolean isControlFlowArc() { + return this instanceof ControlFlowArc; + } - public abstract boolean isControlDependencyArrow(); + public final ControlFlowArc asControlFlowArc() { + if (isControlFlowArc()) + return (ControlFlowArc) this; + throw new UnsupportedOperationException("Not a ControlFlowArc"); + } - public abstract boolean isDataDependencyArrow(); + public final boolean isControlDependencyArc() { + return this instanceof ControlDependencyArc; + } - @Override - public String toString() { - return String.format("Arc{data: %s, %s -> %s}", - getData(), - getFrom(), - getTo() - ); + public final ControlDependencyArc asControlDependencyArc() { + if (isControlDependencyArc()) + return (ControlDependencyArc) this; + throw new UnsupportedOperationException("Not a ControlDependencyArc"); } - public String toGraphvizRepresentation() { - GraphNode from = (GraphNode) getFrom(); - GraphNode to = (GraphNode) getTo(); + public final boolean isDataDependencyArc() { + return this instanceof DataDependencyArc; + } - return String.format("%s -> %s", - from.getId(), - to.getId() - ); + public final DataDependencyArc asDataDependencyArc() { + if (isDataDependencyArc()) + return (DataDependencyArc) this; + throw new UnsupportedOperationException("Not a DataDependencyArc"); } - public GraphNode getFromNode() { - return (GraphNode) super.getFrom(); + @Override + public String toString() { + return String.format("%s{%d -> %d}", getClass().getName(), + ((GraphNode) getSource()).getId(), ((GraphNode) getTarget()).getId()); } - public GraphNode getToNode() { - return (GraphNode) super.getTo(); + public String getLabel() { + return ""; } - @Override - public int hashCode() { - return Objects.hashCode(getData()) + getFrom().hashCode() + getTo().hashCode(); + public Map getDotAttributes() { + return new HashMap<>(); } @Override public boolean equals(Object o) { if (this == o) return true; - - if (!(o instanceof Arc)) + if (o == null) return false; + if (!o.getClass().equals(this.getClass())) + return false; + return Objects.equals(getSource(), ((Arc) o).getSource()) && + Objects.equals(getTarget(), ((Arc) o).getTarget()); + } - Arc arc = (Arc) o; - - GraphNode from = (GraphNode) arc.getFrom(); - GraphNode from2 = (GraphNode) getFrom(); - GraphNode to = (GraphNode) getTo(); - GraphNode to2 = (GraphNode) arc.getTo(); - - return Objects.equals(arc.getData(), getData()) && - Objects.equals(from.getId(), from2.getId()) && - Objects.equals(to.getId(), to2.getId()); + @Override + public int hashCode() { + return Objects.hash(getClass(), getSource(), getTarget()); } } diff --git a/src/main/java/tfm/arcs/cfg/ControlFlowArc.java b/src/main/java/tfm/arcs/cfg/ControlFlowArc.java index f51233cfc1ad6f95d811b1fb3190f999ecd95724..5abdda3f513886e271dff195af4677b682beb99f 100644 --- a/src/main/java/tfm/arcs/cfg/ControlFlowArc.java +++ b/src/main/java/tfm/arcs/cfg/ControlFlowArc.java @@ -1,36 +1,8 @@ package tfm.arcs.cfg; import tfm.arcs.Arc; -import tfm.arcs.data.VoidArcData; -import tfm.nodes.GraphNode; -public class ControlFlowArc extends Arc { - - public ControlFlowArc(GraphNode from, GraphNode to) { - super(from, to); - } - - @Override - public boolean isControlFlowArrow() { - return true; - } - - @Override - public boolean isControlDependencyArrow() { - return false; +public class ControlFlowArc extends Arc { + public ControlFlowArc() { } - - @Override - public boolean isDataDependencyArrow() { - return false; - } - - @Override - public String toString() { - return String.format("ControlFlowArc{%s -> %s}", - getFromNode().getId(), - getToNode().getId() - ); - } - } diff --git a/src/main/java/tfm/arcs/data/ArcData.java b/src/main/java/tfm/arcs/data/ArcData.java deleted file mode 100644 index a20b654f32f84d4001f8670146e64a33ad7eb82a..0000000000000000000000000000000000000000 --- a/src/main/java/tfm/arcs/data/ArcData.java +++ /dev/null @@ -1,8 +0,0 @@ -package tfm.arcs.data; - -public abstract class ArcData { - - public abstract boolean isVoid(); - - public abstract boolean isVariable(); -} diff --git a/src/main/java/tfm/arcs/data/VariableArcData.java b/src/main/java/tfm/arcs/data/VariableArcData.java deleted file mode 100644 index 0ce2e49edb6b108f47270114d13caac8484908f5..0000000000000000000000000000000000000000 --- a/src/main/java/tfm/arcs/data/VariableArcData.java +++ /dev/null @@ -1,37 +0,0 @@ -package tfm.arcs.data; - -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collection; -import java.util.List; - -public class VariableArcData extends ArcData { - private List variables; - - public VariableArcData(String... variables) { - this(Arrays.asList(variables)); - } - - public VariableArcData(Collection variables) { - this.variables = new ArrayList<>(variables); - } - - public List getVariables() { - return variables; - } - - @Override - public boolean isVoid() { - return false; - } - - @Override - public boolean isVariable() { - return true; - } - - @Override - public String toString() { - return variables.toString(); - } -} diff --git a/src/main/java/tfm/arcs/data/VoidArcData.java b/src/main/java/tfm/arcs/data/VoidArcData.java deleted file mode 100644 index de5d824192d9bf4a9797fabf66b2796b9f6a7ba6..0000000000000000000000000000000000000000 --- a/src/main/java/tfm/arcs/data/VoidArcData.java +++ /dev/null @@ -1,18 +0,0 @@ -package tfm.arcs.data; - -public class VoidArcData extends ArcData { - @Override - public boolean isVoid() { - return true; - } - - @Override - public boolean isVariable() { - return false; - } - - @Override - public String toString() { - return "void"; - } -} diff --git a/src/main/java/tfm/arcs/pdg/ControlDependencyArc.java b/src/main/java/tfm/arcs/pdg/ControlDependencyArc.java index 6d7c79bb40b562485c9e4a97353937689cbe6df1..914401e3c55ed3612cd757342b017088e0024d83 100644 --- a/src/main/java/tfm/arcs/pdg/ControlDependencyArc.java +++ b/src/main/java/tfm/arcs/pdg/ControlDependencyArc.java @@ -1,35 +1,8 @@ package tfm.arcs.pdg; import tfm.arcs.Arc; -import tfm.arcs.data.ArcData; -import tfm.nodes.GraphNode; -public class ControlDependencyArc extends Arc { - - public ControlDependencyArc(GraphNode from, GraphNode to) { - super(from, to); - } - - @Override - public boolean isControlFlowArrow() { - return false; - } - - @Override - public boolean isControlDependencyArrow() { - return true; - } - - @Override - public boolean isDataDependencyArrow() { - return false; - } - - @Override - public String toString() { - return String.format("ControlDependencyArc{%s -> %s}", - ((GraphNode) getFrom()).getId(), - ((GraphNode) getTo()).getId() - ); +public class ControlDependencyArc extends Arc { + public ControlDependencyArc() { } } diff --git a/src/main/java/tfm/arcs/pdg/DataDependencyArc.java b/src/main/java/tfm/arcs/pdg/DataDependencyArc.java index 11e8ac030217f4905feb05b3609d39ed8841fe1d..9806f85847b6de54dc562192e57daa831d6c5e05 100644 --- a/src/main/java/tfm/arcs/pdg/DataDependencyArc.java +++ b/src/main/java/tfm/arcs/pdg/DataDependencyArc.java @@ -1,54 +1,30 @@ package tfm.arcs.pdg; +import org.jgrapht.io.Attribute; +import org.jgrapht.io.DefaultAttribute; import tfm.arcs.Arc; -import tfm.arcs.data.VariableArcData; -import tfm.nodes.GraphNode; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.List; +import java.util.Map; -public class DataDependencyArc extends Arc { +public class DataDependencyArc extends Arc { + private final String variable; - public DataDependencyArc(GraphNode from, GraphNode to, String variable, String... variables) { - super(from, to); - - List variablesList = new ArrayList<>(variables.length + 1); - - variablesList.add(variable); - variablesList.addAll(Arrays.asList(variables)); - - VariableArcData variableArcData = new VariableArcData(variablesList); - - setData(variableArcData); - } - - @Override - public boolean isControlFlowArrow() { - return false; - } - - @Override - public boolean isControlDependencyArrow() { - return false; - } - - @Override - public boolean isDataDependencyArrow() { - return true; + public DataDependencyArc(String variable) { + super(); + this.variable = variable; } @Override - public String toString() { - return String.format("DataDependencyArc{%s, %s -> %s}", - getData(), - getFromNode().getId(), - getToNode().getId()); + public String getLabel() { + return variable; } @Override - public String toGraphvizRepresentation() { - return String.format("%s [style=dashed, color=red, label=\"%s\"];", super.toGraphvizRepresentation(), getData().toString()); + public Map getDotAttributes() { + Map map = super.getDotAttributes(); + map.put("style", DefaultAttribute.createAttribute("dashed")); + map.put("color", DefaultAttribute.createAttribute("red")); + return map; } } diff --git a/src/main/java/tfm/exec/CFGLog.java b/src/main/java/tfm/exec/CFGLog.java index e04a483c0f2649c6a2854ed04f02130706e62a7a..153728ac6904b45ef18a3a1c66d8423fa7424024 100644 --- a/src/main/java/tfm/exec/CFGLog.java +++ b/src/main/java/tfm/exec/CFGLog.java @@ -1,22 +1,22 @@ package tfm.exec; import com.github.javaparser.ast.Node; -import tfm.graphs.CFGGraph; +import tfm.graphs.CFG; import tfm.visitors.cfg.CFGBuilder; -public class CFGLog extends GraphLog { +public class CFGLog extends GraphLog { public CFGLog() { super(); } - public CFGLog(CFGGraph graph) { + public CFGLog(CFG graph) { super(graph); } @Override public void visit(Node node) { - this.graph = new CFGGraph(); + this.graph = new CFG(); node.accept(new CFGBuilder(graph), null); } } diff --git a/src/main/java/tfm/exec/GraphLog.java b/src/main/java/tfm/exec/GraphLog.java index a3d38faef6a4800ec543f5d5b7769d65df18df0d..43f39ad966c51ee0700c71d8b9225d423f3b7372 100644 --- a/src/main/java/tfm/exec/GraphLog.java +++ b/src/main/java/tfm/exec/GraphLog.java @@ -55,8 +55,11 @@ public abstract class GraphLog { "* GRAPHVIZ *\n" + "****************************" ); - Logger.log(graph.toGraphvizRepresentation()); - Logger.log(); + try (StringWriter stringWriter = new StringWriter()) { + graph.getDOTExporter().exportGraph(graph, stringWriter); + stringWriter.append('\n'); + Logger.log(stringWriter.toString()); + } } public void generateImages() throws IOException { @@ -72,18 +75,21 @@ public abstract class GraphLog { this.format = format; generated = true; File tmpDot = File.createTempFile("graph-source-", ".dot"); - tmpDot.deleteOnExit(); + + // Graph -> DOT -> file try (Writer w = new FileWriter(tmpDot)) { - w.write(graph.toGraphvizRepresentation()); + graph.getDOTExporter().exportGraph(graph, w); } + // Execute dot ProcessBuilder pb = new ProcessBuilder("dot", tmpDot.getAbsolutePath(), "-T" + format.getExt(), "-o", getImageFile().getAbsolutePath()); try { int result = pb.start().waitFor(); - if (result != 0) { - Logger.log("Image generation failed"); - } + if (result == 0) + tmpDot.deleteOnExit(); + else + Logger.log("Image generation failed, try running \"" + pb.toString() + "\" on your terminal."); } catch (InterruptedException e) { Logger.log("Image generation failed\n" + e.getMessage()); } diff --git a/src/main/java/tfm/exec/Main.java b/src/main/java/tfm/exec/Main.java index 9532105800151a0c67f1c4322b80bb0711468341..d604251a8879e21867f51d98f966b5f36ae878d7 100644 --- a/src/main/java/tfm/exec/Main.java +++ b/src/main/java/tfm/exec/Main.java @@ -13,7 +13,7 @@ import java.util.Optional; public class Main { - public static final String PROGRAM = Utils.PROGRAMS_FOLDER + "sdg/Example1.java"; + public static final String PROGRAM = Utils.PROGRAMS_FOLDER + "cfg/Eval_4.java"; public static final String GRAPH = GraphLog.SDG; public static final String METHOD = "main"; diff --git a/src/main/java/tfm/exec/PDGLog.java b/src/main/java/tfm/exec/PDGLog.java index a712abe2f2386ee9c6785f4bc02bd210b832f2ae..991681d74e9c288d67c400f5b4c7d2359fb555a1 100644 --- a/src/main/java/tfm/exec/PDGLog.java +++ b/src/main/java/tfm/exec/PDGLog.java @@ -1,6 +1,7 @@ package tfm.exec; -import tfm.graphs.PDGGraph; +import com.github.javaparser.ast.Node; +import tfm.graphs.PDG; import tfm.nodes.GraphNode; import tfm.utils.Logger; import tfm.visitors.pdg.PDGBuilder; @@ -9,7 +10,7 @@ import java.io.IOException; import java.util.Comparator; import java.util.stream.Collectors; -public class PDGLog extends GraphLog { +public class PDGLog extends GraphLog { private CFGLog cfgLog; @@ -17,22 +18,22 @@ public class PDGLog extends GraphLog { this(null); } - public PDGLog(PDGGraph pdgGraph) { - super(pdgGraph); + public PDGLog(PDG pdg) { + super(pdg); - if (graph != null && graph.getCfgGraph() != null) - cfgLog = new CFGLog(graph.getCfgGraph()); + if (graph != null && graph.getCfg() != null) + cfgLog = new CFGLog(graph.getCfg()); else cfgLog = null; } @Override - public void visit(com.github.javaparser.ast.Node node) { - this.graph = new PDGGraph(); + public void visit(Node node) { + this.graph = new PDG(); - node.accept(new PDGBuilder(graph), this.graph.getRootNode()); + node.accept(new PDGBuilder(graph), null); if (cfgLog == null) { - cfgLog = new CFGLog(graph.getCfgGraph()); + cfgLog = new CFGLog(graph.getCfg()); } } @@ -41,7 +42,7 @@ public class PDGLog extends GraphLog { super.log(); Logger.log("Nodes with variable info"); - Logger.log(graph.getNodes().stream() + Logger.log(graph.vertexSet().stream() .sorted(Comparator.comparingInt(GraphNode::getId)) .map(node -> String.format("GraphNode { id: %s, declared: %s, defined: %s, used: %s }", diff --git a/src/main/java/tfm/exec/SDGLog.java b/src/main/java/tfm/exec/SDGLog.java index b88de5468eabb9f24dc36c4af1265714eae365a5..702e7c91b061754f6607ea7430d27cc8eb1fa32b 100644 --- a/src/main/java/tfm/exec/SDGLog.java +++ b/src/main/java/tfm/exec/SDGLog.java @@ -1,16 +1,14 @@ package tfm.exec; import com.github.javaparser.ast.Node; -import tfm.graphs.SDGGraph; +import tfm.graphs.SDG; import tfm.visitors.sdg.SDGBuilder; -import java.io.IOException; - -public class SDGLog extends GraphLog { +public class SDGLog extends GraphLog { @Override public void visit(Node node) { - this.graph = new SDGGraph(); + this.graph = new SDG(); SDGBuilder sdgBuilder = new SDGBuilder(this.graph); node.accept(sdgBuilder, null); } diff --git a/src/main/java/tfm/graphbuilding/GraphOptions.java b/src/main/java/tfm/graphbuilding/GraphOptions.java index 2884f49981f1d6ae9df2c8c7acb2ca617547a0ad..31e7fe754ca26faf578c38baf666196b550935d1 100644 --- a/src/main/java/tfm/graphbuilding/GraphOptions.java +++ b/src/main/java/tfm/graphbuilding/GraphOptions.java @@ -1,10 +1,10 @@ package tfm.graphbuilding; import com.github.javaparser.ast.Node; -import tfm.graphs.CFGGraph; +import tfm.graphs.CFG; import tfm.graphs.Graph; -import tfm.graphs.PDGGraph; -import tfm.graphs.SDGGraph; +import tfm.graphs.PDG; +import tfm.graphs.SDG; import tfm.visitors.cfg.CFGBuilder; import tfm.visitors.pdg.PDGBuilder; import tfm.visitors.sdg.SDGBuilder; @@ -23,41 +23,41 @@ public abstract class GraphOptions { protected abstract void buildGraphWithSpecificVisitor(G emptyGraph, Node node); } -class CFGOptions extends GraphOptions { +class CFGOptions extends GraphOptions { @Override - public CFGGraph empty() { - return new CFGGraph(); + public CFG empty() { + return new CFG(); } @Override - protected void buildGraphWithSpecificVisitor(CFGGraph emptyGraph, Node node) { + protected void buildGraphWithSpecificVisitor(CFG emptyGraph, Node node) { node.accept(new CFGBuilder(emptyGraph), null); } } -class PDGOptions extends GraphOptions { +class PDGOptions extends GraphOptions { @Override - public PDGGraph empty() { - return new PDGGraph(); + public PDG empty() { + return new PDG(); } @Override - protected void buildGraphWithSpecificVisitor(PDGGraph emptyGraph, Node node) { - node.accept(new PDGBuilder(emptyGraph), emptyGraph.getRootNode()); + protected void buildGraphWithSpecificVisitor(PDG emptyGraph, Node node) { + node.accept(new PDGBuilder(emptyGraph), null); } } -class SDGOptions extends GraphOptions { +class SDGOptions extends GraphOptions { @Override - public SDGGraph empty() { - return new SDGGraph(); + public SDG empty() { + return new SDG(); } @Override - protected void buildGraphWithSpecificVisitor(SDGGraph emptyGraph, Node node) { + protected void buildGraphWithSpecificVisitor(SDG emptyGraph, Node node) { node.accept(new SDGBuilder(emptyGraph), null); } } \ No newline at end of file diff --git a/src/main/java/tfm/graphbuilding/Graphs.java b/src/main/java/tfm/graphbuilding/Graphs.java index 235124ca06360392507b42ea2b01df5c844c2612..548bfcdeec1ef9873db218a8fca751cf0367bb37 100644 --- a/src/main/java/tfm/graphbuilding/Graphs.java +++ b/src/main/java/tfm/graphbuilding/Graphs.java @@ -1,13 +1,13 @@ package tfm.graphbuilding; -import tfm.graphs.CFGGraph; -import tfm.graphs.PDGGraph; -import tfm.graphs.SDGGraph; +import tfm.graphs.CFG; +import tfm.graphs.PDG; +import tfm.graphs.SDG; public class Graphs { - public static final GraphOptions CFG = new CFGOptions(); - public static final GraphOptions PDG = new PDGOptions(); - public static final GraphOptions SDG = new SDGOptions(); + public static final GraphOptions CFG = new CFGOptions(); + public static final GraphOptions PDG = new PDGOptions(); + public static final GraphOptions SDG = new SDGOptions(); } \ No newline at end of file diff --git a/src/main/java/tfm/graphs/CFG.java b/src/main/java/tfm/graphs/CFG.java new file mode 100644 index 0000000000000000000000000000000000000000..36779212eb7ec03c3aab6fd8d9735f8868db6203 --- /dev/null +++ b/src/main/java/tfm/graphs/CFG.java @@ -0,0 +1,51 @@ +package tfm.graphs; + +import com.github.javaparser.ast.body.MethodDeclaration; +import tfm.arcs.Arc; +import tfm.arcs.cfg.ControlFlowArc; +import tfm.nodes.GraphNode; +import tfm.utils.NodeNotFoundException; + +import java.util.HashSet; +import java.util.Objects; +import java.util.Set; + +public class CFG extends GraphWithRootNode { + + public CFG() { + super(); + } + + public void addControlFlowEdge(GraphNode from, GraphNode to) { + super.addEdge(from, to, new ControlFlowArc()); + } + + public Set> findLastDefinitionsFrom(GraphNode startNode, String variable) { + if (!this.containsVertex(startNode)) + throw new NodeNotFoundException(startNode, this); + return findLastDefinitionsFrom(new HashSet<>(), startNode, startNode, variable); + } + + private Set> findLastDefinitionsFrom(Set visited, GraphNode startNode, GraphNode currentNode, String variable) { + visited.add(currentNode.getId()); + + Set> res = new HashSet<>(); + + for (Arc arc : incomingEdgesOf(currentNode)) { + ControlFlowArc controlFlowArc = arc.asControlFlowArc(); + + GraphNode from = this.getEdgeSource(controlFlowArc); + + if (!Objects.equals(startNode, from) && visited.contains(from.getId())) { + continue; + } + + if (from.getDefinedVariables().contains(variable)) { + res.add(from); + } else { + res.addAll(findLastDefinitionsFrom(visited, startNode, from, variable)); + } + } + return res; + } +} diff --git a/src/main/java/tfm/graphs/CFGGraph.java b/src/main/java/tfm/graphs/CFGGraph.java deleted file mode 100644 index 8faf9e31b4e356a9e32abbd94c67aa4fcb3a1466..0000000000000000000000000000000000000000 --- a/src/main/java/tfm/graphs/CFGGraph.java +++ /dev/null @@ -1,114 +0,0 @@ -package tfm.graphs; - -import com.github.javaparser.ast.Node; -import com.github.javaparser.ast.stmt.EmptyStmt; -import edg.graphlib.Arrow; -import tfm.arcs.Arc; -import tfm.arcs.cfg.ControlFlowArc; -import tfm.nodes.GraphNode; -import tfm.slicing.SlicingCriterion; -import tfm.utils.NodeNotFoundException; - -import java.util.Comparator; -import java.util.HashSet; -import java.util.Objects; -import java.util.Set; -import java.util.stream.Collectors; - -public class CFGGraph extends Graph { - - public CFGGraph() { - super(); - setRootVertex(new GraphNode<>(getNextVertexId(), getRootNodeData(), new EmptyStmt())); - } - - @Override - public GraphNode addNode(String instruction, ASTNode node) { - GraphNode vertex = new GraphNode<>(getNextVertexId(), instruction, node); - this.addVertex(vertex); - - return vertex; - } - - - protected String getRootNodeData() { - return "Start"; - } - - @SuppressWarnings("unchecked") - public void addControlFlowEdge(GraphNode from, GraphNode to) { - super.addEdge((Arrow) new ControlFlowArc(from, to)); - } - - @Override - public String toGraphvizRepresentation() { - String lineSep = System.lineSeparator(); - - String nodes = getNodes().stream() - .sorted(Comparator.comparingInt(GraphNode::getId)) - .map(GraphNode::toGraphvizRepresentation) - .collect(Collectors.joining(lineSep)); - - String arrows = - getArrows().stream() - .sorted(Comparator.comparingInt(arrow -> ((GraphNode) arrow.getFrom()).getId())) - .map(arrow -> ((Arc) arrow).toGraphvizRepresentation()) - .collect(Collectors.joining(lineSep)); - - return "digraph g{" + lineSep + - nodes + lineSep + - arrows + lineSep + - "}"; - } - - @Override - public Graph slice(SlicingCriterion slicingCriterion) { - return this; - } - - public Set> findLastDefinitionsFrom(GraphNode startNode, String variable) { -// Logger.log("======================================================="); -// Logger.log("Starting from " + startNode); -// Logger.log("Looking for variable " + variable); -// Logger.log(cfgGraph.toString()); - - if (!this.contains(startNode)) { - throw new NodeNotFoundException(startNode, this); - } - - return findLastDefinitionsFrom(new HashSet<>(), startNode, startNode, variable); - } - - private Set> findLastDefinitionsFrom(Set visited, GraphNode startNode, GraphNode currentNode, String variable) { - visited.add(currentNode.getId()); - -// Logger.log("On " + currentNode); - - Set> res = new HashSet<>(); - - for (Arc arc : currentNode.getIncomingArcs()) { - ControlFlowArc controlFlowArc = (ControlFlowArc) arc; - - GraphNode from = controlFlowArc.getFromNode(); - -// Logger.log("Arrow from node: " + from); - - if (!Objects.equals(startNode, from) && visited.contains(from.getId())) { -// Logger.log("It's already visited. Continuing..."); - continue; - } - - if (from.getDefinedVariables().contains(variable)) { -// Logger.log("Contains defined variable: " + variable); - res.add(from); - } else { -// Logger.log("Doesn't contain the variable, searching inside it"); - res.addAll(findLastDefinitionsFrom(visited, startNode, from, variable)); - } - } - -// Logger.format("Done with node %s", currentNode.getId()); - - return res; - } -} diff --git a/src/main/java/tfm/graphs/Graph.java b/src/main/java/tfm/graphs/Graph.java index 5ab503de87504e25013066d0da1e94836706ded5..ce739be90844293a7e80d941fb39d46afa030347 100644 --- a/src/main/java/tfm/graphs/Graph.java +++ b/src/main/java/tfm/graphs/Graph.java @@ -1,159 +1,212 @@ package tfm.graphs; import com.github.javaparser.ast.Node; -import edg.graphlib.Arrow; -import edg.graphlib.Vertex; +import org.jgrapht.graph.DefaultDirectedGraph; +import org.jgrapht.io.DOTExporter; import tfm.arcs.Arc; -import tfm.arcs.data.ArcData; import tfm.nodes.GraphNode; -import tfm.slicing.SlicingCriterion; +import tfm.nodes.NodeFactory; import java.util.*; +import java.util.function.Consumer; import java.util.stream.Collectors; /** - * A graphlib Graph without cost and data in arcs + * * */ -public abstract class Graph extends edg.graphlib.Graph { - - private int nextVertexId = 0; - -// public final static class NodeId { -// private static int nextVertexId = 0; -// -// private int id; -// -// private NodeId(int id) { -// this.id = id; -// } -// -// static synchronized NodeId getVertexId() { -// return new NodeId(nextVertexId++); -// } -// -// public int getId() { -// return id; -// } -// -// @Override -// public String toString() { -// return String.valueOf(id); -// } -// } +public abstract class Graph extends DefaultDirectedGraph, Arc> { + + protected static final int DEFAULT_VERTEX_START_ID = 0; + + private int nextVertexId; public Graph() { - super(); + this(DEFAULT_VERTEX_START_ID); } - /** - Library fix: if a node had an edge to itself resulted in 2 outgoing nodes instead of 1 outgoing and 1 incoming - */ - @Override - public boolean addEdge(Arrow arrow) { - Vertex from = arrow.getFrom(); - Vertex to = arrow.getTo(); - int cost = arrow.getCost(); - ArcData data = arrow.getData(); - - if (!verticies.contains(from)) - throw new IllegalArgumentException(String.format("from (%s) is not in graph", from)); - if (!verticies.contains(to)) - throw new IllegalArgumentException(String.format("to (%s) is not in graph", to)); - - List> es2 = from.findEdges(to); - - for (Arrow e2 : es2) { - if (e2 != null && cost == e2.getCost() && - ((data == null && e2.getData() == null) || - (data != null && data.equals(e2.getData())))) - return false; - } + protected Graph(int vertexStartId) { + super(null, null, false); + this.nextVertexId = vertexStartId; + } - // FIX - if (Objects.equals(from, to)) { - from.getOutgoingArrows().add(arrow); - from.getIncomingArrows().add(arrow); - } else { - from.addEdge(arrow); - to.addEdge(arrow); - } - edges.add(arrow); - return true; + private GraphNode addNode(GraphNode node) { + this.addVertex(node); + + return node; } - @SuppressWarnings("unchecked") - public GraphNode getRootNode() { - return (GraphNode) super.getRootVertex(); + private GraphNode addNode(int id, String instruction, ASTNode node) { + GraphNode newNode = NodeFactory.graphNode(id, instruction, node); + + return this.addNode(newNode); } - public abstract GraphNode addNode(String instruction, ASTNode node); + public GraphNode addNode(String instruction, ASTNode node) { + return this.addNode(getNextVertexId(), instruction, node); + } + + /** + * Adds the given node to the graph. + * + * One must be careful with this method, as the given node will have + * an id corresponding to the graph in which it was created, and may not fit + * in the current graph. + * + * @param node the node to add to the graph + * @param copyId whether to copy the node id or generate a new one + * @return the node instance added to the graph + */ + public GraphNode addNode(GraphNode node, boolean copyId) { + GraphNode copy = NodeFactory.computedGraphNode( + copyId ? node.getId() : getNextVertexId(), + node.getInstruction(), + node.getAstNode(), + node.getDeclaredVariables(), + node.getDefinedVariables(), + node.getUsedVariables() + ); + + this.addVertex(copy); + + return copy; + } @SuppressWarnings("unchecked") public Optional> findNodeByASTNode(ASTNode astNode) { - return getNodes().stream() + return vertexSet().stream() .filter(node -> Objects.equals(node.getAstNode(), astNode)) .findFirst() .map(node -> (GraphNode) node); } public Optional> findNodeById(int id) { - return getNodes().stream() + return vertexSet().stream() .filter(node -> Objects.equals(node.getId(), id)) .findFirst(); } - @SuppressWarnings("unchecked") - public Set> getNodes() { - return getVerticies().stream() - .map(vertex -> (GraphNode) vertex) - .collect(Collectors.toSet()); - } - - public Set> getArcs() { - return getArrows().stream() - .map(arrow -> (Arc) arrow) - .collect(Collectors.toSet()); - } - + @Override public String toString() { - return getNodes().stream() + return vertexSet().stream() .sorted(Comparator.comparingInt(GraphNode::getId)) .map(GraphNode::toString) .collect(Collectors.joining(System.lineSeparator())); } - public abstract String toGraphvizRepresentation(); - protected synchronized int getNextVertexId() { return nextVertexId++; } - public boolean contains(GraphNode graphNode) { - return getNodes().stream() - .anyMatch(node -> Objects.equals(node, graphNode)); + public List> findDeclarationsOfVariable(String variable) { + return vertexSet().stream() + .filter(node -> node.getDeclaredVariables().contains(variable)) + .collect(Collectors.toList()); } - public abstract Graph slice(SlicingCriterion slicingCriterion); + public boolean isEmpty() { + return this.vertexSet().isEmpty(); + } + + public DOTExporter, Arc> getDOTExporter() { + return new DOTExporter<>( + graphNode -> String.valueOf(graphNode.getId()), + GraphNode::getInstruction, + Arc::getLabel, + null, + Arc::getDotAttributes); + } /** - * Deprecated for incorrect behaviour. Use removeNode instead + * Modifies a current node in the graph by the changes done in the MutableGraphNode instance + * inside the function passed as parameter + * + * @param id the id of the node to be modified + * @param modifyFn a consumer which takes a MutableGraphNode as parameter */ - @Override - @Deprecated - public boolean removeVertex(Vertex vertex) { - throw new UnsupportedOperationException("Deprecated method. Use removeNode instead"); - } + public void modifyNode(int id, Consumer> modifyFn) { + this.findNodeById(id).ifPresent(node -> { + Set incomingArcs = new HashSet<>(incomingEdgesOf(node)); + Set outgoingArcs = new HashSet<>(outgoingEdgesOf(node)); + + this.removeVertex(node); + + MutableGraphNode modifiedNode = new MutableGraphNode<>((GraphNode) node); + + modifyFn.accept(modifiedNode); + + GraphNode newNode = modifiedNode.toGraphNode(); - public void removeNode(GraphNode node) { - verticies.remove(node); + this.addVertex(newNode); - edges.removeAll(node.getOutgoingArcs()); - edges.removeAll(node.getIncomingArcs()); + for (Arc incomingArc : incomingArcs) { + GraphNode from = getEdgeSource(incomingArc); + this.addEdge(from, newNode, incomingArc); + } + + for (Arc outgoingArc : outgoingArcs) { + GraphNode to = getEdgeTarget(outgoingArc); + this.addEdge(newNode, to, outgoingArc); + } + }); } - public List> findDeclarationsOfVariable(String variable) { - return getNodes().stream() - .filter(node -> node.getDeclaredVariables().contains(variable)) - .collect(Collectors.toList()); + public static class MutableGraphNode { + private int id; + private String instruction; + private ASTNode astNode; + private Set declaredVariables; + private Set definedVariables; + private Set usedVariables; + + private boolean mustCompute; + + MutableGraphNode(GraphNode node) { + this.id = node.getId(); + this.instruction = node.getInstruction(); + this.astNode = node.getAstNode(); + this.declaredVariables = node.getDeclaredVariables(); + this.definedVariables = node.getDefinedVariables(); + this.usedVariables = node.getUsedVariables(); + } + + GraphNode toGraphNode() { + return mustCompute + ? NodeFactory.graphNode(id, instruction, astNode) + : NodeFactory.computedGraphNode( + id, + instruction, + astNode, + declaredVariables, + definedVariables, + usedVariables + ); + } + + public int getId() { + return id; + } + + public void setId(int id) { + this.id = id; + } + + public String getInstruction() { + return instruction; + } + + public void setInstruction(String instruction) { + this.instruction = instruction; + } + + public ASTNode getAstNode() { + return astNode; + } + + public void setAstNode(ASTNode astNode) { + this.astNode = astNode; + + // If the AST node changes, we need to compute all variables for it + mustCompute = true; + } } } diff --git a/src/main/java/tfm/graphs/GraphWithRootNode.java b/src/main/java/tfm/graphs/GraphWithRootNode.java new file mode 100644 index 0000000000000000000000000000000000000000..76d01e6ff992d5ce6a67a0bf17873369e3bc236c --- /dev/null +++ b/src/main/java/tfm/graphs/GraphWithRootNode.java @@ -0,0 +1,52 @@ +package tfm.graphs; + +import com.github.javaparser.ast.Node; +import tfm.nodes.GraphNode; +import tfm.nodes.NodeFactory; + +import java.util.Objects; +import java.util.Optional; + +public abstract class GraphWithRootNode extends Graph { + + protected final int ROOT_NODE_ID = 0; + + protected GraphNode rootNode; + + public GraphWithRootNode() { + super(1); + } + + /** + * Builds the root node with the given instruction and AST node. + * If the root node already exists, just returns false + * + * @param instruction the instruction string + * @param rootNodeAst the AST node + * @return true if the root node is created, false otherwise + */ + public boolean buildRootNode(String instruction, ASTRootNode rootNodeAst) { + if (rootNode != null) { + return false; + } + + GraphNode root = NodeFactory.graphNode(ROOT_NODE_ID, instruction, rootNodeAst); + this.rootNode = root; + this.addVertex(root); + + return true; + } + + public Optional> getRootNode() { + return Optional.ofNullable(rootNode); + } + + @Override + public boolean removeVertex(GraphNode graphNode) { + if (Objects.equals(graphNode, rootNode)) { + return false; + } + + return super.removeVertex(graphNode); + } +} diff --git a/src/main/java/tfm/graphs/JGraph.java b/src/main/java/tfm/graphs/JGraph.java deleted file mode 100644 index 5fe88f8e2a4e69a6d478b9714dad39cdb9ed0c99..0000000000000000000000000000000000000000 --- a/src/main/java/tfm/graphs/JGraph.java +++ /dev/null @@ -1,12 +0,0 @@ -package tfm.graphs; - -import org.jgrapht.graph.DefaultDirectedGraph; -import tfm.arcs.Arc; -import tfm.nodes.JNode; - -public class JGraph extends DefaultDirectedGraph, Arc> { - - public JGraph() { - super(null, null, false); - } -} diff --git a/src/main/java/tfm/graphs/PDG.java b/src/main/java/tfm/graphs/PDG.java new file mode 100644 index 0000000000000000000000000000000000000000..98fad8e52bde77353d41ca39fb91aee1d8c63d11 --- /dev/null +++ b/src/main/java/tfm/graphs/PDG.java @@ -0,0 +1,93 @@ +package tfm.graphs; + +import com.github.javaparser.ast.Node; +import com.github.javaparser.ast.body.MethodDeclaration; +import tfm.arcs.Arc; +import tfm.arcs.pdg.ControlDependencyArc; +import tfm.arcs.pdg.DataDependencyArc; +import tfm.nodes.GraphNode; +import tfm.slicing.SlicingCriterion; +import tfm.utils.ASTUtils; +import tfm.utils.Logger; +import tfm.utils.NodeNotFoundException; +import tfm.visitors.pdg.PDGBuilder; + +import java.util.HashSet; +import java.util.Optional; +import java.util.Set; + +public class PDG extends GraphWithRootNode implements Sliceable { + + private CFG cfg; + + public PDG() { + super(); + } + + public PDG(CFG cfg) { + super(); + this.cfg = cfg; + } + + public void addControlDependencyArc(GraphNode from, GraphNode to) { + this.addEdge(from, to, new ControlDependencyArc()); + } + + public void addDataDependencyArc(GraphNode from, GraphNode to, String variable) { + this.addEdge(from, to, new DataDependencyArc(variable)); + } + + public void setCfg(CFG cfg) { + this.cfg = cfg; + } + + @Override + public PDG slice(SlicingCriterion slicingCriterion) { + Optional> optionalGraphNode = slicingCriterion.findNode(this); + + if (!optionalGraphNode.isPresent()) { + throw new NodeNotFoundException(slicingCriterion); + } + + GraphNode node = optionalGraphNode.get(); + + // Simply get slice nodes from GraphNode + Set sliceNodes = getSliceNodes(new HashSet<>(), node); + + PDG sliceGraph = new PDG(); + + Node astCopy = ASTUtils.cloneAST(node.getAstNode()); + + astCopy.accept(new PDGBuilder(sliceGraph), null); + + for (GraphNode sliceNode : sliceGraph.vertexSet()) { + if (!sliceNodes.contains(sliceNode.getId())) { + Logger.log("Removing node " + sliceNode.getId()); + sliceNode.getAstNode().removeForced(); + sliceGraph.removeVertex(sliceNode); + } + } + + return sliceGraph; + } + + private Set getSliceNodes(Set visited, GraphNode root) { + visited.add(root.getId()); + + for (Arc arc : incomingEdgesOf(root)) { + GraphNode from = this.getEdgeSource(arc); + + if (visited.contains(from.getId())) { + continue; + } + + getSliceNodes(visited, from); + } + + return visited; + } + + public CFG getCfg() { + return cfg; + } +} diff --git a/src/main/java/tfm/graphs/PDGGraph.java b/src/main/java/tfm/graphs/PDGGraph.java deleted file mode 100644 index 7cd03b498650efc04341f3d455effaf56aaaa4b5..0000000000000000000000000000000000000000 --- a/src/main/java/tfm/graphs/PDGGraph.java +++ /dev/null @@ -1,251 +0,0 @@ -package tfm.graphs; - -import com.github.javaparser.ast.Node; -import com.github.javaparser.ast.body.MethodDeclaration; -import com.github.javaparser.ast.stmt.EmptyStmt; -import edg.graphlib.Arrow; -import org.jetbrains.annotations.NotNull; -import tfm.arcs.Arc; -import tfm.arcs.pdg.ControlDependencyArc; -import tfm.arcs.pdg.DataDependencyArc; -import tfm.nodes.GraphNode; -import tfm.slicing.SlicingCriterion; -import tfm.utils.ASTUtils; -import tfm.utils.Logger; -import tfm.utils.NodeNotFoundException; -import tfm.visitors.pdg.PDGBuilder; - -import java.util.Comparator; -import java.util.HashSet; -import java.util.Optional; -import java.util.Set; -import java.util.stream.Collectors; - -public class PDGGraph extends Graph { - - private CFGGraph cfgGraph; - - public PDGGraph() { - setRootVertex(new GraphNode<>(getNextVertexId(), getRootNodeData(), new MethodDeclaration())); - } - - public PDGGraph(CFGGraph cfgGraph) { - this(); - this.cfgGraph = cfgGraph; - } - - protected String getRootNodeData() { - return "Entry"; - } - - public GraphNode addNode(GraphNode node) { - GraphNode vertex = new GraphNode<>(node); - super.addVertex(vertex); - - return vertex; - } - - @Override - public GraphNode addNode(String instruction, ASTNode node) { - return addNode(getNextVertexId(), instruction, node); - } - - public GraphNode addNode(int id, String instruction, ASTNode node) { - GraphNode vertex = new GraphNode<>(id, instruction, node); - super.addVertex(vertex); - - return vertex; - } - - @SuppressWarnings("unchecked") - private void addArc(Arc arc) { - super.addEdge(arc); - } - - public void addControlDependencyArc(GraphNode from, GraphNode to) { - ControlDependencyArc controlDependencyArc = new ControlDependencyArc(from, to); - - this.addArc(controlDependencyArc); - } - - public void addDataDependencyArc(GraphNode from, GraphNode to, String variable) { - DataDependencyArc dataDataDependencyArc = new DataDependencyArc(from, to, variable); - - this.addArc(dataDataDependencyArc); - } - - public Set getNodesAtLevel(int level) { - return getVerticies().stream() - .map(vertex -> (GraphNode) vertex) - .filter(node -> getLevelOf(node) == level) - .collect(Collectors.toSet()); - } - - public int getLevels() { - return getVerticies().stream() - .map(vertex -> (GraphNode) vertex) - .max(Comparator.comparingInt(this::getLevelOf)) - .map(node -> getLevelOf(node) + 1) - .orElse(0); - } - - public int getLevelOf(int nodeId) { - return findNodeById(nodeId) - .map(this::getLevelOf) - .orElseThrow(() -> new NodeNotFoundException("Node with id " + nodeId + " not found in PDG graph")); - } - - public int getLevelOf(@NotNull GraphNode node) { - Optional optionalControlDependencyArc = node.getIncomingArcs().stream() - .filter(Arc::isControlDependencyArrow) - .findFirst() - .map(arc -> (ControlDependencyArc) arc); - - if (!optionalControlDependencyArc.isPresent()) { - return 0; - } - - GraphNode parent = optionalControlDependencyArc.get().getFromNode(); - - return 1 + getLevelOf(parent); - } - - public void setCfgGraph(CFGGraph cfgGraph) { - this.cfgGraph = cfgGraph; - } - - @Override - public String toGraphvizRepresentation() { - String lineSep = System.lineSeparator(); - - String nodesDeclaration = getNodes().stream() - .sorted(Comparator.comparingInt(GraphNode::getId)) - .map(GraphNode::toGraphvizRepresentation) - .collect(Collectors.joining(lineSep)); - - StringBuilder rankedNodes = new StringBuilder(); - - // No level 0 is needed (only one node) - for (int i = 0; i < getLevels(); i++) { - Set levelNodes = getNodesAtLevel(i); - - if (levelNodes.size() <= 1) { - continue; - } - - // rank same - rankedNodes.append("{ rank = same; ") - .append(levelNodes.stream() - .map(node -> String.valueOf(node.getId())) - .collect(Collectors.joining(";"))) - .append(" }") - .append(lineSep); - - // invisible arrows for ordering - rankedNodes.append(levelNodes.stream() - .sorted(Comparator.comparingInt(GraphNode::getId)) - .map(node -> String.valueOf(node.getId())) - .collect(Collectors.joining(" -> "))) - .append("[style = invis];") - .append(lineSep); - } - - String arrows = - getArcs().stream() - .sorted(Comparator.comparingInt(arrow -> ((GraphNode) arrow.getFrom()).getId())) - .map(Arc::toGraphvizRepresentation) - .collect(Collectors.joining(lineSep)); - - - return "digraph g{" + lineSep + - "splines=true;" + lineSep + - nodesDeclaration + lineSep + - arrows + lineSep + - rankedNodes.toString() + - "}"; - } - - @Override - public PDGGraph slice(SlicingCriterion slicingCriterion) { - Optional> optionalGraphNode = slicingCriterion.findNode(this); - - if (!optionalGraphNode.isPresent()) { - throw new NodeNotFoundException(slicingCriterion); - } - - GraphNode node = optionalGraphNode.get(); - -// // DEPRECATED - Find CFGNode and find last definition of variable -// CFGNode cfgNode = this.cfgGraph.findNodeByASTNode(node.getAstNode()) -// .orElseThrow(() -> new NodeNotFoundException("CFGNode not found")); -// -// Set> definitionNodes = Utils.findLastDefinitionsFrom(cfgNode, slicingCriterion.getVariable()); -// -// Logger.format("Slicing node: %s", node); -// -// // Get slice nodes from definition nodes -// Set sliceNodes = definitionNodes.stream() -// .flatMap(definitionNode -> getSliceNodes(new HashSet<>(), this.findNodeByASTNode(definitionNode.getAstNode()).get()).stream()) -// .collect(Collectors.toSet()); -// -// sliceNodes.add(node.getId()); - - // Simply get slice nodes from GraphNode - Set sliceNodes = getSliceNodes(new HashSet<>(), node); - - PDGGraph sliceGraph = new PDGGraph(); - - Node astCopy = ASTUtils.cloneAST(node.getAstNode()); - - astCopy.accept(new PDGBuilder(sliceGraph), sliceGraph.getRootNode()); - - for (GraphNode sliceNode : sliceGraph.getNodes()) { - if (!sliceNodes.contains(sliceNode.getId())) { - Logger.log("Removing node " + sliceNode.getId()); - sliceNode.getAstNode().removeForced(); - sliceGraph.removeNode(sliceNode); - } - } - -// for (Arc arc : getArcs()) { -// Optional fromOptional = sliceGraph.findNodeById(arc.getFromNode().getId()); -// Optional toOptional = sliceGraph.findNodeById(arc.getToNode().getId()); -// -// if (fromOptional.isPresent() && toOptional.isPresent()) { -// GraphNode from = fromOptional.get(); -// GraphNode to = toOptional.get(); -// -// if (arc.isControlDependencyArrow()) { -// sliceGraph.addControlDependencyArc(from, to); -// } else { -// DataDependencyArc dataDependencyArc = (DataDependencyArc) arc; -// sliceGraph.addDataDependencyArc(from, to, dataDependencyArc.getData().getVariables().get(0)); -// } -// } -// } - - return sliceGraph; - } - - private Set getSliceNodes(Set visited, GraphNode root) { - visited.add(root.getId()); - - for (Arrow arrow : root.getIncomingArcs()) { - Arc arc = (Arc) arrow; - - GraphNode from = (GraphNode) arc.getFromNode(); - - if (visited.contains(from.getId())) { - continue; - } - - getSliceNodes(visited, from); - } - - return visited; - } - - public CFGGraph getCfgGraph() { - return cfgGraph; - } -} diff --git a/src/main/java/tfm/graphs/SDGGraph.java b/src/main/java/tfm/graphs/SDG.java similarity index 53% rename from src/main/java/tfm/graphs/SDGGraph.java rename to src/main/java/tfm/graphs/SDG.java index ec4692bfd16c598ede2d0c624c44e3a771feb733..6ba940e5b95675a804330c7fd91f983ad83b0527 100644 --- a/src/main/java/tfm/graphs/SDGGraph.java +++ b/src/main/java/tfm/graphs/SDG.java @@ -1,44 +1,33 @@ package tfm.graphs; -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.stmt.EmptyStmt; import tfm.nodes.GraphNode; +import tfm.nodes.NodeFactory; import tfm.slicing.SlicingCriterion; import tfm.utils.Context; -import java.util.*; +import java.util.Collection; +import java.util.HashMap; +import java.util.Map; +import java.util.Set; import java.util.stream.Collectors; -public class SDGGraph extends Graph { +public class SDG extends Graph implements Sliceable { - private Map contextPDGGraphMap; + private Map contextPDGGraphMap; - public SDGGraph() { + public SDG() { this.contextPDGGraphMap = new HashMap<>(); } @Override - public GraphNode addNode(String instruction, ASTNode node) { - GraphNode sdgNode = new GraphNode<>(getNextVertexId(), instruction, node); - super.addVertex(sdgNode); - - return sdgNode; + public SDG slice(SlicingCriterion slicingCriterion) { + throw new IllegalStateException("Not implemented (yet)"); } - @Override - public String toGraphvizRepresentation() { - return contextPDGGraphMap.values().stream() - .map(PDGGraph::toGraphvizRepresentation).collect(Collectors.joining("\n")); - } - - @Override - public Graph slice(SlicingCriterion slicingCriterion) { - return this; - } - - public Map getContextPDGGraphMap() { + public Map getContextPDGGraphMap() { return contextPDGGraphMap; } @@ -53,18 +42,14 @@ public class SDGGraph extends Graph { .collect(Collectors.toSet()); } - public Collection getPDGs() { + public Collection getPDGs() { return contextPDGGraphMap.values(); } @Deprecated - public void addPDG(PDGGraph pdgGraph, MethodDeclaration methodDeclaration) { - if (this.rootVertex == null) { - this.setRootVertex(new GraphNode<>(getNextVertexId(), methodDeclaration.getNameAsString(), methodDeclaration)); - } - + public void addPDG(PDG pdg, MethodDeclaration methodDeclaration) { for (Parameter parameter : methodDeclaration.getParameters()) { - GraphNode sdgNode = new GraphNode<>( + GraphNode sdgNode = NodeFactory.graphNode( getNextVertexId(), String.format("%s = %s_in", parameter.getNameAsString(), parameter.getNameAsString()), new EmptyStmt() @@ -73,14 +58,12 @@ public class SDGGraph extends Graph { addVertex(sdgNode); } - for (GraphNode node : pdgGraph.getNodes()) { - if (!this.verticies.contains(node)) { - GraphNode sdgNode = new GraphNode<>( + for (GraphNode node : pdg.vertexSet()) { + if (!this.containsVertex(node)) { + GraphNode sdgNode = NodeFactory.computedGraphNode( getNextVertexId(), - node.getData(), + node.getInstruction(), node.getAstNode(), - node.getIncomingArcs(), - node.getOutgoingArcs(), node.getDeclaredVariables(), node.getDefinedVariables(), node.getUsedVariables() @@ -91,8 +74,8 @@ public class SDGGraph extends Graph { } } - public void addMethod(MethodDeclaration methodDeclaration, PDGGraph pdgGraph) { - GraphNode methodRootNode = new GraphNode<>( + public void addMethod(MethodDeclaration methodDeclaration, PDG pdg) { + GraphNode methodRootNode = NodeFactory.graphNode( getNextVertexId(), "ENTER " + methodDeclaration.getDeclarationAsString(false, false, true), methodDeclaration diff --git a/src/main/java/tfm/graphs/Sliceable.java b/src/main/java/tfm/graphs/Sliceable.java new file mode 100644 index 0000000000000000000000000000000000000000..3edf64a944f069ab7ac72f34c2d07cfecdd6f61f --- /dev/null +++ b/src/main/java/tfm/graphs/Sliceable.java @@ -0,0 +1,7 @@ +package tfm.graphs; + +import tfm.slicing.SlicingCriterion; + +public interface Sliceable { + G slice(SlicingCriterion sc); +} diff --git a/src/main/java/tfm/nodes/CFGNode.java b/src/main/java/tfm/nodes/CFGNode.java deleted file mode 100644 index 75e4b1cdfe69b22c71fe7b20be8973287cda3fe3..0000000000000000000000000000000000000000 --- a/src/main/java/tfm/nodes/CFGNode.java +++ /dev/null @@ -1,26 +0,0 @@ -package tfm.nodes; - -import com.github.javaparser.ast.Node; - -import java.util.stream.Collectors; - -@Deprecated -public class CFGNode extends GraphNode { - - public > CFGNode(N1 node) { - super(node); - } - - public CFGNode(int nextVertexId, String rootNodeData, N node) { - super(nextVertexId, rootNodeData, node); - } - - @Override - public String toString() { - return String.format("CFGNode{id: %s, in: %s, out: %s", - getId(), - getIncomingArcs().stream().map(arc -> arc.getFromNode().getId()).collect(Collectors.toList()), - getOutgoingArcs().stream().map(arc -> arc.getToNode().getId()).collect(Collectors.toList()) - ); - } -} diff --git a/src/main/java/tfm/nodes/GraphNode.java b/src/main/java/tfm/nodes/GraphNode.java index 133b4cfbea085e8a68c0ee7e359af8d1e4e2f1b7..c0113599bc3221cc38bf5f8324a0f867349fdde6 100644 --- a/src/main/java/tfm/nodes/GraphNode.java +++ b/src/main/java/tfm/nodes/GraphNode.java @@ -2,119 +2,86 @@ package tfm.nodes; import com.github.javaparser.ast.Node; import com.github.javaparser.ast.stmt.Statement; -import edg.graphlib.Arrow; -import edg.graphlib.Vertex; -import org.checkerframework.checker.nullness.qual.NonNull; import org.jetbrains.annotations.NotNull; -import tfm.arcs.Arc; -import tfm.arcs.data.ArcData; import tfm.utils.Utils; import tfm.variables.VariableExtractor; -import java.util.*; -import java.util.stream.Collectors; +import java.util.Collection; +import java.util.HashSet; +import java.util.Objects; +import java.util.Set; -public class GraphNode extends Vertex { +/** + * Immutable class that represents an AST node inside a CFG, PDG or SDG. + * + * @param the AST node that represents + */ +public class GraphNode { - private int id; + private final int id; + private final String instruction; + private final N astNode; - protected N astNode; + private final Set declaredVariables; + private final Set definedVariables; + private final Set usedVariables; - protected Set declaredVariables; - protected Set definedVariables; - protected Set usedVariables; - - public > GraphNode(N1 node) { - this( - node.getId(), - node.getData(), - node.getAstNode(), - node.getIncomingArcs(), - node.getOutgoingArcs(), - node.getDeclaredVariables(), - node.getDefinedVariables(), - node.getUsedVariables() - ); - } - - public GraphNode(int id, String representation, @NotNull N astNode) { + GraphNode(int id, String instruction, @NotNull N astNode) { this( id, - representation, + instruction, astNode, - Utils.emptyList(), - Utils.emptyList(), Utils.emptySet(), Utils.emptySet(), Utils.emptySet() ); + + if (astNode instanceof Statement) { + extractVariables((Statement) astNode); + } } - public GraphNode( + GraphNode( int id, - String representation, - @NonNull N astNode, - Collection> incomingArcs, - Collection> outgoingArcs, - Set declaredVariables, - Set definedVariables, - Set usedVariables + String instruction, + @NotNull N astNode, + Collection declaredVariables, + Collection definedVariables, + Collection usedVariables ) { - super(null, representation); - this.id = id; - + this.instruction = instruction; this.astNode = astNode; - this.declaredVariables = declaredVariables; - this.definedVariables = definedVariables; - this.usedVariables = usedVariables; - - this.setIncomingArcs(incomingArcs); - this.setOutgoingArcs(outgoingArcs); - - if (astNode instanceof Statement) { - extractVariables((Statement) astNode); - } + this.declaredVariables = new HashSet<>(declaredVariables); + this.definedVariables = new HashSet<>(definedVariables); + this.usedVariables = new HashSet<>(usedVariables); } - private void extractVariables(@NonNull Statement statement) { + private void extractVariables(@NotNull Statement statement) { new VariableExtractor() - .setOnVariableDeclarationListener(variable -> this.declaredVariables.add(variable)) - .setOnVariableDefinitionListener(variable -> this.definedVariables.add(variable)) - .setOnVariableUseListener(variable -> this.usedVariables.add(variable)) + .setOnVariableDeclarationListener(this.declaredVariables::add) + .setOnVariableDefinitionListener(this.definedVariables::add) + .setOnVariableUseListener(this.usedVariables::add) .visit(statement); } - public void setId(int id) { - this.id = id; - } - public int getId() { return id; } public String toString() { - return String.format("GraphNode{id: %s, data: '%s', in: %s, out: %s}", + return String.format("GraphNode{id: %s, instruction: '%s', astNodeType: %s}", getId(), - getData(), - getIncomingArcs().stream().map(arc -> arc.getFromNode().getId()).collect(Collectors.toList()), - getOutgoingArcs().stream().map(arc -> arc.getToNode().getId()).collect(Collectors.toList())); + getInstruction(), + getAstNode().getClass().getSimpleName() + ); } public N getAstNode() { return astNode; } - public void setAstNode(N node) { - this.astNode = node; - } - - public Optional getFileLineNumber() { - return astNode.getBegin() - .map(begin -> begin.line); - } - public void addDeclaredVariable(String variable) { declaredVariables.add(variable); } @@ -135,19 +102,16 @@ public class GraphNode extends Vertex { if (!(o instanceof GraphNode)) return false; - GraphNode other = (GraphNode) o; + GraphNode other = (GraphNode) o; - return Objects.equals(getData(), other.getData()) + return Objects.equals(getId(), other.getId()) + && Objects.equals(getInstruction(), other.getInstruction()) && Objects.equals(astNode, other.astNode); -// && Objects.equals(getIncomingArrows(), other.getIncomingArrows()) -// && Objects.equals(getOutgoingArrows(), other.getOutgoingArrows()) -// && Objects.equals(getName(), other.getName()) ID IS ALWAYS UNIQUE, SO IT WILL NEVER BE THE SAME } - public String toGraphvizRepresentation() { - String text = getData().replace("\\", "\\\\") - .replace("\"", "\\\""); - return String.format("%s[label=\"%s: %s\"];", getId(), getId(), text); + @Override + public int hashCode() { + return Objects.hash(getId(), getInstruction(), getAstNode()); } public Set getDeclaredVariables() { @@ -162,47 +126,7 @@ public class GraphNode extends Vertex { return usedVariables; } - public List> getIncomingArcs() { - return super.getIncomingArrows().stream() - .map(arrow -> (Arc) arrow) - .collect(Collectors.toList()); - } - - public List> getOutgoingArcs() { - return super.getOutgoingArrows().stream() - .map(arrow -> (Arc) arrow) - .collect(Collectors.toList()); - } - - public , C extends Collection> void setIncomingArcs(C arcs) { - for (A arc : arcs) { - this.addIncomingEdge(arc.getFrom(), arc.getCost()); - } - } - - public , C extends Collection> void setOutgoingArcs(C arcs) { - for (A arc : arcs) { - this.addOutgoingEdge(arc.getTo(), arc.getCost()); - } - } - - /** - * Deprecated. Use getIncomingArcs instead - * @throws UnsupportedOperationException - */ - @Deprecated - @Override - public List> getIncomingArrows() { - return super.getIncomingArrows(); - } - - /** - * Deprecated. Use getOutgoingArcs instead - * @throws UnsupportedOperationException - */ - @Deprecated - @Override - public List> getOutgoingArrows() { - return super.getOutgoingArrows(); + public String getInstruction() { + return instruction; } } diff --git a/src/main/java/tfm/nodes/JNode.java b/src/main/java/tfm/nodes/JNode.java deleted file mode 100644 index 534f43315b09c02a8857e14cd7b30962f58c8b76..0000000000000000000000000000000000000000 --- a/src/main/java/tfm/nodes/JNode.java +++ /dev/null @@ -1,135 +0,0 @@ -package tfm.nodes; - -import com.github.javaparser.ast.Node; -import org.checkerframework.checker.nullness.qual.NonNull; -import tfm.utils.Utils; -import tfm.variables.VariableExtractor; - -import java.util.Objects; -import java.util.Optional; -import java.util.Set; - -public class JNode { - - public int getId() { - return id; - } - - public void setId(int id) { - this.id = id; - } - - public String getRepresentation() { - return representation; - } - - public void setRepresentation(String representation) { - this.representation = representation; - } - - public ASTNode getAstNode() { - return astNode; - } - - public void setAstNode(ASTNode astNode) { - this.astNode = astNode; - } - - public Set getDeclaredVariables() { - return declaredVariables; - } - - public Set getDefinedVariables() { - return definedVariables; - } - - public Set getUsedVariables() { - return usedVariables; - } - - private int id; - private String representation; - - protected ASTNode astNode; - protected Set declaredVariables; - protected Set definedVariables; - protected Set usedVariables; - - - public JNode(@NonNull JNode node) { - this(node.id, node.representation, node.astNode, node.declaredVariables, node.definedVariables, node.usedVariables); - } - - public JNode(int id, @NonNull String representation, @NonNull ASTNode astNode) { - this(id, representation, astNode, Utils.emptySet(), Utils.emptySet(), Utils.emptySet()); - - extractVariables(astNode); - } - - public JNode(int id, @NonNull String representation, @NonNull ASTNode astNode, Set declaredVariables, Set definedVariables, Set usedVariables) { - this.id = id; - this.representation = representation; - this.astNode = astNode; - this.declaredVariables = declaredVariables; - this.definedVariables = definedVariables; - this.usedVariables = usedVariables; - } - - private void extractVariables(ASTNode astNode) { - new VariableExtractor() - .setOnVariableDeclarationListener(variable -> this.declaredVariables.add(variable)) - .setOnVariableDefinitionListener(variable -> this.definedVariables.add(variable)) - .setOnVariableUseListener(variable -> this.usedVariables.add(variable)) - .visit(astNode); - } - - public void addDeclaredVariable(String variable) { - declaredVariables.add(variable); - } - - public void addDefinedVariable(String variable) { - definedVariables.add(variable); - } - - public void addUsedVariable(String variable) { - usedVariables.add(variable); - } - - public Optional getFileLineNumber() { - return astNode.getBegin() - .map(begin -> begin.line); - } - - @Override - public int hashCode() { - return id + astNode.hashCode(); - } - - @Override - public boolean equals(Object obj) { - if (obj == this) { - return true; - } - - if (!(obj instanceof JNode)) { - return false; - } - - JNode jNode = (JNode) obj; - - return jNode.id == id && Objects.equals(jNode.astNode, astNode); - } - - @Override - public String toString() { - return String.format("JNode{id: %s, repr: %s, astNodeClass: %s}", - id, - representation, - astNode.getClass().getName() - ); - } - - public String toGraphvizRepresentation() { - return String.format("%s[label=\"%s: %s\"];", getId(), getId(), getRepresentation()); - } -} diff --git a/src/main/java/tfm/nodes/MethodCallNode.java b/src/main/java/tfm/nodes/MethodCallNode.java index b241c5c580e514fdd996f43cff17cf960e16db85..95f0479710c03c4a68fcec061424d82b8c9e6f57 100644 --- a/src/main/java/tfm/nodes/MethodCallNode.java +++ b/src/main/java/tfm/nodes/MethodCallNode.java @@ -1,12 +1,9 @@ package tfm.nodes; import com.github.javaparser.ast.stmt.ExpressionStmt; -import edg.graphlib.Arrow; import org.checkerframework.checker.nullness.qual.NonNull; -import tfm.arcs.data.ArcData; import java.util.ArrayList; -import java.util.Collection; import java.util.List; import java.util.Set; @@ -17,7 +14,7 @@ public class MethodCallNode extends GraphNode { private List outParameters; public > MethodCallNode(N1 node) { - super(node); + super(node.getId(), node.getInstruction(), node.getAstNode()); this.inParameters = new ArrayList<>(); this.outParameters = new ArrayList<>(); @@ -30,8 +27,8 @@ public class MethodCallNode extends GraphNode { this.outParameters = new ArrayList<>(); } - public MethodCallNode(int id, String representation, @NonNull ExpressionStmt node, Collection> incomingArcs, Collection> outgoingArcs, Set declaredVariables, Set definedVariables, Set usedVariables) { - super(id, representation, node, incomingArcs, outgoingArcs, declaredVariables, definedVariables, usedVariables); + public MethodCallNode(int id, String representation, @NonNull ExpressionStmt node, Set declaredVariables, Set definedVariables, Set usedVariables) { + super(id, representation, node, declaredVariables, definedVariables, usedVariables); this.inParameters = new ArrayList<>(); this.outParameters = new ArrayList<>(); diff --git a/src/main/java/tfm/nodes/NodeFactory.java b/src/main/java/tfm/nodes/NodeFactory.java new file mode 100644 index 0000000000000000000000000000000000000000..7d4f96e5d1bbcb7c813bdf4e992521cc12584592 --- /dev/null +++ b/src/main/java/tfm/nodes/NodeFactory.java @@ -0,0 +1,70 @@ +package tfm.nodes; + +import com.github.javaparser.ast.Node; + +import java.util.Collection; +import java.util.Objects; + +public class NodeFactory { + + /** + * Returns a computed GraphNode (i.e. a GraphNode with computed the + * declared, defined and used variables in its AST node) + * + * @param id the id of the node + * @param instruction the instruction that represents + * @param node the node of the AST that represents + * @param declaredVariables the set of declared variables + * @param definedVariables the set of defined variables + * @param usedVariables the set of used variables + * @param the type of the AST node + * @return a new GraphNode + */ + public static GraphNode computedGraphNode( + int id, + String instruction, + ASTNode node, + Collection declaredVariables, + Collection definedVariables, + Collection usedVariables + ) { + Objects.requireNonNull(instruction, "Instruction cannot be null!"); + Objects.requireNonNull(node, "AST Node cannot be null"); + Objects.requireNonNull(declaredVariables, "declared variables collection cannot be null!"); + Objects.requireNonNull(definedVariables, "defined variables collection cannot be null"); + Objects.requireNonNull(usedVariables, "Used variables collection cannot be null!"); + + return new GraphNode<>( + id, + instruction, + node, + declaredVariables, + definedVariables, + usedVariables + ); + } + + /** + * Returns a GraphNode computing the declared, defined and used variables in its AST node + * + * @param id the id of the node + * @param instruction the instruction that represents + * @param node the node of the AST that represents + * @param the type of the AST node + * @return a new GraphNode + */ + public static GraphNode graphNode( + int id, + String instruction, + ASTNode node + ) { + Objects.requireNonNull(instruction, "Instruction cannot be null!"); + Objects.requireNonNull(node, "AST Node cannot be null"); + + return new GraphNode<>( + id, + instruction, + node + ); + } +} diff --git a/src/main/java/tfm/nodes/PDGNode.java b/src/main/java/tfm/nodes/PDGNode.java deleted file mode 100644 index fe8c5be37a7a278ce00478172a2e970106260d98..0000000000000000000000000000000000000000 --- a/src/main/java/tfm/nodes/PDGNode.java +++ /dev/null @@ -1,92 +0,0 @@ -package tfm.nodes; - -import com.github.javaparser.ast.Node; -import edg.graphlib.Arrow; -import org.checkerframework.checker.nullness.qual.NonNull; -import tfm.arcs.Arc; -import tfm.arcs.data.ArcData; -import tfm.arcs.pdg.ControlDependencyArc; -import tfm.utils.Logger; - -import java.util.ArrayList; -import java.util.Collection; -import java.util.List; -import java.util.Set; -import java.util.stream.Collectors; - -@Deprecated -public class PDGNode extends GraphNode { - - public PDGNode(int id, String data, N node) { - super(id, data, node); - } - - public PDGNode(int id, String representation, @NonNull N node, Collection> incomingArcs, Collection> outgoingArcs, Set declaredVariables, Set definedVariables, Set usedVariables) { - super(id, representation, node, incomingArcs, outgoingArcs, declaredVariables, definedVariables, usedVariables); - } - - public > PDGNode(N1 node) { - super(node); - } - - public String toString() { - List dataFrom = new ArrayList<>(); - List dataTo = new ArrayList<>(); - List controlFrom = new ArrayList<>(); - List controlTo = new ArrayList<>(); - - getIncomingArcs().forEach(arrow -> { - Logger.log(arrow); - Arc arc = (Arc) arrow; - GraphNode from = (GraphNode) arc.getFrom(); - - if (arc.isDataDependencyArrow()) { - dataFrom.add(from.getId()); - } else if (arc.isControlDependencyArrow()) { - controlFrom.add(from.getId()); - } - - }); - - getOutgoingArcs().forEach(arrow -> { - Arc arc = (Arc) arrow; - GraphNode to = (GraphNode) arc.getTo(); - - if (arc.isDataDependencyArrow()) { - dataTo.add(to.getId()); - } else if (arc.isControlDependencyArrow()) { - controlTo.add(to.getId()); - } - - }); - - return String.format("PDGNode{id: %s, data: %s, dataFrom: %s, dataTo: %s, controlFrom: %s, controlTo: %s}", - getId(), - getData(), - dataFrom, - dataTo, - controlFrom, - controlTo - ); - } - - public List getControlDependencies() { - return getIncomingArcs().stream() - .filter(arrow -> ((Arc) arrow).isControlDependencyArrow()) - .map(arc -> (ControlDependencyArc) arc) - .collect(Collectors.toList()); - } - - public int getLevel() { - return getLevel(this); - } - - private int getLevel(PDGNode node) { - List dependencies = node.getControlDependencies(); - - if (dependencies.isEmpty()) - return 0; - - return 1 + getLevel((PDGNode) dependencies.get(0).getFrom()); - } -} diff --git a/src/main/java/tfm/nodes/SDGNode.java b/src/main/java/tfm/nodes/SDGNode.java deleted file mode 100644 index 79a4120c68af66b1b3a17eeea681e0f08616a654..0000000000000000000000000000000000000000 --- a/src/main/java/tfm/nodes/SDGNode.java +++ /dev/null @@ -1,28 +0,0 @@ -package tfm.nodes; - -import com.github.javaparser.ast.Node; -import edg.graphlib.Arrow; -import org.checkerframework.checker.nullness.qual.NonNull; -import tfm.arcs.data.ArcData; - -import java.util.Collection; -import java.util.Set; - -@Deprecated -public class SDGNode extends GraphNode { - - public > SDGNode(N1 node) { - super(node); - } - - public SDGNode(int id, String representation, N node) { - super(id, representation, node); - } - public SDGNode(int id, String representation, @NonNull N node, Collection> incomingArcs, Collection> outgoingArcs, Set declaredVariables, Set definedVariables, Set usedVariables) { - super(id, representation, node, incomingArcs, outgoingArcs, declaredVariables, definedVariables, usedVariables); - } - - public String toString() { - return String.format("SDGNode{id: %s, data: %s, "); - } -} diff --git a/src/main/java/tfm/readme.md b/src/main/java/tfm/readme.md deleted file mode 100644 index 5ef10f60754416053c5d780b1ec52590789b430d..0000000000000000000000000000000000000000 --- a/src/main/java/tfm/readme.md +++ /dev/null @@ -1,153 +0,0 @@ -# TFM - -- [TFM](#tfm) - - [Introduction](#introduction) - - [Quick start](#quick-start) - - [Build a graph](#build-a-graph) - - [Slice a program](#slice-a-program) - - [Structure](#structure) - - [Summary](#summary) - - [Current state](#current-state) - - [Graphs](#graphs) - - [Statements covered](#statements-covered) - - [To do list](#to-do-list) - - [SDG](#sdg) - - [General](#general) - - [Code samples](#code-samples) - - [Build a CFG from a program](#build-a-cfg-from-a-program) - - [Get a slice of the PDG of a program](#get-a-slice-of-the-pdg-of-a-program) - - [Workflow](#workflow) - -## Introduction - -The main goal of this work is to develop a Java slicer. This is done by building a System Dependence Graph of the program being sliced - -## Quick start - -### Build a graph - -Find `Main` class (`tfm/exec`), modify static fields of the class (the program being analyzed, the graph to build, etc.) and execute it. You will find the output in `tfm/out` as a png image - -### Slice a program - -Find `Slice` class (`tfm/slicing`), set the program path and execute. The sliced program will be in `tfm/out` - -## Structure - -Graphs are built using a library called `graphlib`, located in `lib/graphlib.jar`. This library is old and has some issues I had to fix... - -The main class is the `Graph` class, which extends from `graphlib`'s `Graph` class. This class includes some behaviour fixes, and some general interest methods (like `toString`, `toGraphvizRepresentation`, etc.) - -Every graph has a set of nodes and arrows. `GraphNode` and `Arc` classes are used to represent them respectively. - -A set of visitors is implemented for many things, such as graph building, data dependence building, etc... (available in `tfm/visitors`) - -A bunch of programs are written in `tfm/programs`, you can write more there. - -Some naive testing is implemented in the `tfm/validation` folder. Currently, a PDG can be compared with a program to check their equality. - -Some util methods are available in `tfm/utils` (such as AST utils, logger, etc.) - -Forget about the `tfm/scopes` folder, it was an idea I had to discard and it has to be deleted. - -### Summary - -- Graphs (`tfm/graphs`) - - CFGGraph - - PDGGraph - - SDGGraph - -- Nodes (`tfm/nodes`) - - ~~CFGNode, PDGNode, SDGNode~~ (_Deprecated_) - - GraphNode - - MethodCallNode (_idk if this is necessary, maybe it can be deleted_) - -- Arcs (`tfm/arcs`) - - ControlFlowArc - - DataDependencyArc - - ControlDependencyArc - -- Visitors (`tfm/visitors`) - - CFGBuilder - - ~~PDGVisitor~~ (_Deprecated, it was an intent to build a PDG with no CFG needed_) - - PDGBuilder - - ControlDependencyBuilder - - DataDependencyBuilder - - SDGBuilder (_Probably deprecated_) - - NewSDGBuilder -**Work in progress**- - - MethodCallReplacerVisitor (_Replaces method call nodes with in and out variable nodes_) -**Work in progress**- - -## Current state - -### Graphs - -- CFG: Done! -- PDG: Done! -- SDG: PDGs are built for each method - -### Statements covered - -- Expressions (ExpressionStmt) -- If (IfStmt) -- While, DoWhile (WhileStmt, DoStmt) -- For, Foreach (ForStmt, ForeachStmt) -- Switch (SwitchStmt, SwitchEntryStmt) -- Break (BreakStmt) -- Continue (ContinueStmt) - -## To do list - -### SDG - -- Replace method call nodes with in and out variables nodes and build arrows for them -- Build summary arrows - -### General - -- Switch to a (much) better graph library like [JGraphT](https://jgrapht.org/). It also supports graph visualization -- Performance review -- Make a test suite (test graph building, slicing, etc.) -- Add support to more Java language features (lambdas, etc.) - -## Code samples - -### Build a CFG from a program - -```java -public CFGGraph buildCFG(File programFile) { - JavaParser.getStaticConfiguration().setAttributeComments(false); // Always disable comments, just in case - - Node astRoot = JavaParser.parse(programFile); - - return Graphs.CFG.fromASTNode(astRoot); // Creates a new graph representing the program -} -``` - -### Get a slice of the PDG of a program - -```java -public PDGGraph getSlice(File program, SlicingCriterion slicingCriterion) { - JavaParser.getStaticConfiguration().setAttributeComments(false); // Always disable comments, just in case - - Node astRoot = JavaParser.parse(programFile); - - PDGGraph pdgGraph = Graphs.PDG.fromASTNode(astRoot); - - return pdgGraph.slice(slicingCriterion); -} - -``` - -## Workflow - -- Branches: - - `master` (only for stable versions) - - `develop` (main branch) - - `` - -1. Discover a new feature/fix -2. Open an issue describing it and assign it -4. Create a new branch from `develop` with the same name as the issue number (e.g. for issue #12 the new branch is called `12`) -5. Write the solution to the issue -6. Once resolved, open a pull request from the issue branch to `develop` branch -7. Finally, when pull request is merged, remove branch \ No newline at end of file diff --git a/src/main/java/tfm/scopes/IfElseScope.java b/src/main/java/tfm/scopes/IfElseScope.java deleted file mode 100644 index 765b7f08f91a0cc842e5dca41b5b84b544dd22b0..0000000000000000000000000000000000000000 --- a/src/main/java/tfm/scopes/IfElseScope.java +++ /dev/null @@ -1,64 +0,0 @@ -package tfm.scopes; - -import tfm.nodes.GraphNode; -import tfm.variables.actions.VariableDefinition; - -import java.util.List; -import java.util.stream.Collectors; - -public class IfElseScope extends ScopeHolder { - - private VariableScope expressionScope; - private ScopeHolder thenScope; - private ScopeHolder elseScope; - - public IfElseScope(N node) { - super(node); - - this.expressionScope = new VariableScope<>(node); - this.thenScope = new ScopeHolder<>(node); - this.elseScope = new ScopeHolder<>(node); - - addSubscope(expressionScope); // expression - addSubscope(thenScope); // then - addSubscope(elseScope); // else - } - - public VariableScope getExpressionScope() { - return expressionScope; - } - - public ScopeHolder getThenScope() { - return thenScope; - } - - public ScopeHolder getElseScope() { - return elseScope; - } - - @Override - public void addVariableUse(String variable, N node) { - getExpressionScope().addVariableUse(variable, node); - } - - @Override - public List> getFirstDefinitions(String variable) { - return getSubscopes().stream() - .flatMap(scope -> scope.getFirstDefinitions(variable).stream()) - .collect(Collectors.toList()); - } - - @Override - public List> getLastDefinitions(String variable) { - return getSubscopes().stream() - .flatMap(scope -> scope.getLastDefinitions(variable).stream()) - .collect(Collectors.toList()); - } - - @Override - public List> getLastDefinitionsBeforeNode(String variable, N node) { - return getSubscopes().stream() - .flatMap(scope -> scope.getLastDefinitionsBeforeNode(variable, node).stream()) - .collect(Collectors.toList()); - } -} diff --git a/src/main/java/tfm/scopes/Scope.java b/src/main/java/tfm/scopes/Scope.java deleted file mode 100644 index 9d56e9088391497c39083c2b0f6a024d59be05cb..0000000000000000000000000000000000000000 --- a/src/main/java/tfm/scopes/Scope.java +++ /dev/null @@ -1,46 +0,0 @@ -package tfm.scopes; - -import tfm.nodes.GraphNode; -import tfm.variables.actions.VariableDeclaration; -import tfm.variables.actions.VariableDefinition; -import tfm.variables.actions.VariableUse; - -import java.util.List; -import java.util.Set; - -public abstract class Scope { - - protected N root; - - protected Scope(N root) { - this.root = root; - } - - public N getRoot() { - return root; - } - - public abstract void addVariableDeclaration(String variable, N context); - public abstract void addVariableDefinition(String variable, N context); - public abstract void addVariableUse(String variable, N context); - - public abstract boolean isVariableDeclared(String variable); - public abstract boolean isVariableDefined(String variable); - public abstract boolean isVariableUsed(String variable); - - public abstract List> getVariableDeclarations(String variable); - public abstract List> getVariableDefinitions(String variable); - public abstract List> getVariableUses(String variable); - - public abstract Set getDeclaredVariables(); - public abstract Set getDefinedVariables(); - public abstract Set getUsedVariables(); - - - public abstract List> getVariableUsesBeforeNode(String variable, N node); - - public abstract List> getFirstDefinitions(String variable); - public abstract List> getLastDefinitions(String variable); - public abstract List> getLastDefinitionsBeforeNode(String variable, N node); -} - diff --git a/src/main/java/tfm/scopes/ScopeHolder.java b/src/main/java/tfm/scopes/ScopeHolder.java deleted file mode 100644 index a5a47807223d97f294af72b7ee6b1cd2fdada51f..0000000000000000000000000000000000000000 --- a/src/main/java/tfm/scopes/ScopeHolder.java +++ /dev/null @@ -1,183 +0,0 @@ -package tfm.scopes; - -import tfm.nodes.GraphNode; -import tfm.variables.actions.VariableDeclaration; -import tfm.variables.actions.VariableDefinition; -import tfm.variables.actions.VariableUse; - -import java.util.*; -import java.util.stream.Collectors; - -public class ScopeHolder extends Scope { - - private Queue> subscopes; - - public ScopeHolder(N root) { - super(root); - - subscopes = Collections.asLifoQueue(new ArrayDeque<>()); - } - - private Optional> getLastScope() { - if (subscopes.isEmpty()) - return Optional.empty(); - - return Optional.of(subscopes.peek()); - } - - @Override - public void addVariableDeclaration(String variable, N context) { - Optional> optionalScope = getLastScope(); - - boolean newScope = !optionalScope.isPresent(); - - Scope scope = optionalScope.orElse(new VariableScope<>(context)); - - scope.addVariableDeclaration(variable, context); - - if (newScope) { - addSubscope(scope); - } - } - - @Override - public void addVariableDefinition(String variable, N context) { - Optional> optionalScope = getLastScope(); - - boolean newScope = !optionalScope.isPresent(); - - Scope scope = optionalScope.orElse(new VariableScope<>(context)); - - scope.addVariableDefinition(variable, context); - - if (newScope) { - addSubscope(scope); - } - } - - @Override - public void addVariableUse(String variable, N context) { - Optional> optionalScope = getLastScope(); - - boolean newScope = !optionalScope.isPresent(); - - Scope scope = optionalScope.orElse(new VariableScope<>(context)); - - scope.addVariableUse(variable, context); - - if (newScope) { - addSubscope(scope); - } - } - - public Queue> getSubscopes() { - return subscopes; - } - - public void addSubscope(Scope subscope) { - subscopes.add(subscope); - } - - @Override - public boolean isVariableDeclared(String variable) { - return subscopes.stream().anyMatch(subscope -> subscope.isVariableDeclared(variable)); - } - - @Override - public boolean isVariableDefined(String variable) { - return subscopes.stream().anyMatch(subscope -> subscope.isVariableDefined(variable)); - } - - @Override - public boolean isVariableUsed(String variable) { - return subscopes.stream().anyMatch(subscope -> subscope.isVariableUsed(variable)); - } - - @Override - public List> getVariableDeclarations(String variable) { - return subscopes.stream() - .flatMap(scope -> scope.getVariableDeclarations(variable).stream()) - .collect(Collectors.toList()); - } - - @Override - public List> getVariableDefinitions(String variable) { - return subscopes.stream() - .flatMap(scope -> scope.getVariableDefinitions(variable).stream()) - .collect(Collectors.toList()); - } - - @Override - public List> getVariableUses(String variable) { - return subscopes.stream() - .flatMap(scope -> scope.getVariableUses(variable).stream()) - .collect(Collectors.toList()); - } - - @Override - public Set getDeclaredVariables() { - return subscopes.stream() - .flatMap(scope -> scope.getDeclaredVariables().stream()) - .collect(Collectors.toSet()); - } - - @Override - public Set getDefinedVariables() { - return subscopes.stream() - .flatMap(scope -> scope.getDefinedVariables().stream()) - .collect(Collectors.toSet()); - } - - @Override - public Set getUsedVariables() { - return subscopes.stream() - .flatMap(scope -> scope.getUsedVariables().stream()) - .collect(Collectors.toSet()); - } - - @Override - public List> getVariableUsesBeforeNode(String variable, N node) { - return subscopes.stream() - .filter(_scope -> _scope.isVariableUsed(variable) && _scope.root.getId() <= node.getId()) - .flatMap(scope -> scope.getVariableUsesBeforeNode(variable, node).stream()) - .collect(Collectors.toList()); - } - - @Override - public List> getFirstDefinitions(String variable) { - Optional> scope = subscopes.stream() - .filter(_scope -> _scope.isVariableDefined(variable)) - .reduce((first, second) -> second); - - if (!scope.isPresent()) - return new ArrayList<>(0); - - return scope.get().getFirstDefinitions(variable); - } - - @Override - public List> getLastDefinitions(String variable) { - Optional> scope = subscopes.stream() - .filter(_scope -> _scope.isVariableDefined(variable)) - .findFirst(); - - if (!scope.isPresent()) - return new ArrayList<>(0); - - return scope.get().getLastDefinitions(variable); - } - - @Override - public List> getLastDefinitionsBeforeNode(String variable, N node) { - Optional> scope = subscopes.stream() - .filter(_scope -> _scope.isVariableDefined(variable) && _scope.root.getId() <= node.getId()) - .findFirst(); - - if (!scope.isPresent()) - return new ArrayList<>(0); - - return scope.get().getLastDefinitions(variable); - } - - -} diff --git a/src/main/java/tfm/scopes/VariableScope.java b/src/main/java/tfm/scopes/VariableScope.java deleted file mode 100644 index d8af5dfe11b456addeaafaae3a0422e66ab5ca7d..0000000000000000000000000000000000000000 --- a/src/main/java/tfm/scopes/VariableScope.java +++ /dev/null @@ -1,156 +0,0 @@ -package tfm.scopes; - -import tfm.nodes.GraphNode; -import tfm.variables.actions.VariableAction; -import tfm.variables.actions.VariableDeclaration; -import tfm.variables.actions.VariableDefinition; -import tfm.variables.actions.VariableUse; - -import java.util.*; - -public class VariableScope extends Scope { - - private Map>> variableDeclarations; - private Map>> variableDefinitions; - private Map>> variableUses; - - public VariableScope(N root) { - super(root); - - variableDeclarations = new HashMap<>(); - variableDefinitions = new HashMap<>(); - variableUses = new HashMap<>(); - } - - @Override - public boolean isVariableDeclared(String variable) { - return variableDeclarations.containsKey(variable); - } - - @Override - public boolean isVariableDefined(String variable) { - return variableDefinitions.containsKey(variable); - } - - @Override - public boolean isVariableUsed(String variable) { - return variableUses.containsKey(variable); - } - - @Override - public List> getVariableDeclarations(String variable) { - return new ArrayList<>(variableDeclarations.getOrDefault(variable, new ArrayList<>())); - } - - @Override - public List> getVariableDefinitions(String variable) { - return new ArrayList<>(variableDefinitions.getOrDefault(variable, new ArrayList<>())); - } - - @Override - public List> getVariableUses(String variable) { - return new ArrayList<>(variableUses.getOrDefault(variable, new ArrayList<>())); - } - - @Override - public Set getDeclaredVariables() { - return new HashSet<>(variableDeclarations.keySet()); - } - - @Override - public Set getDefinedVariables() { - return new HashSet<>(variableDefinitions.keySet()); - } - - @Override - public Set getUsedVariables() { - return new HashSet<>(variableUses.keySet()); - } - - @Override - public List> getVariableUsesBeforeNode(String variable, N node) { - List> res = getVariableUses(variable); - - if (res.isEmpty()) - return res; - - return res.stream() - .filter(variableUse -> variableUse.getNode().getId() <= node.getId()) - .max(Comparator.comparingInt(variableUse -> variableUse.getNode().getId())) - .map(variableUse -> new ArrayList<>(Collections.singletonList(variableUse))) - .orElseGet(ArrayList::new); - } - - @Override - public List> getFirstDefinitions(String variable) { - List> res = getVariableDefinitions(variable); - - if (res.isEmpty()) - return res; - - return res.subList(0, 1); - } - - @Override - public List> getLastDefinitions(String variable) { - List> res = getVariableDefinitions(variable); - - if (res.isEmpty()) - return res; - - return res.subList(res.size() - 1, res.size()); - } - - @Override - public List> getLastDefinitionsBeforeNode(String variable, N node) { - List> res = getVariableDefinitions(variable); - - if (res.isEmpty()) - return res; - - return res.stream() - .filter(variableDefinition -> variableDefinition.getNode().getId() <= node.getId()) - .max(Comparator.comparingInt(variableDefinition -> variableDefinition.getNode().getId())) - .map(variableDefinition -> new ArrayList<>(Collections.singletonList(variableDefinition))) - .orElseGet(ArrayList::new); - } - - @Override - public void addVariableDeclaration(String variable, N context) { - appendValue(variableDeclarations, variable, new VariableDeclaration<>(variable, context)); - } - - @Override - public void addVariableDefinition(String variable, N context) { - appendValue(variableDefinitions, variable, new VariableDefinition<>(variable, context)); - } - - @Override - public void addVariableUse(String variable, N context) { - appendValue(variableUses, variable, new VariableUse<>(variable, context)); - } - - private > void appendValue(Map> map, String variable, E action) { - List value = map.getOrDefault(variable, new ArrayList<>()); - - boolean exists = !value.isEmpty(); - - value.add(action); - - if (!exists) { - map.put(variable, value); - } - } - - private > void appendValues(Map> map, String variable, List actions) { - List value = map.getOrDefault(variable, new ArrayList<>()); - - boolean exists = !value.isEmpty(); - - value.addAll(actions); - - if (!exists) { - map.put(variable, value); - } - } -} diff --git a/src/main/java/tfm/slicing/LineNumberCriterion.java b/src/main/java/tfm/slicing/LineNumberCriterion.java index 2b0c2462e88a4d2be74343384b5067546e3a56f9..355398c354ff29a718d9bba5a86a184309aebfc4 100644 --- a/src/main/java/tfm/slicing/LineNumberCriterion.java +++ b/src/main/java/tfm/slicing/LineNumberCriterion.java @@ -1,9 +1,9 @@ package tfm.slicing; import com.github.javaparser.ast.Node; -import tfm.graphs.CFGGraph; -import tfm.graphs.PDGGraph; -import tfm.graphs.SDGGraph; +import tfm.graphs.CFG; +import tfm.graphs.PDG; +import tfm.graphs.SDG; import tfm.nodes.GraphNode; import tfm.utils.Logger; @@ -20,14 +20,14 @@ public class LineNumberCriterion extends SlicingCriterion { } @Override - public Optional> findNode(CFGGraph graph) { + public Optional> findNode(CFG graph) { return Optional.empty(); } @Override - public Optional> findNode(PDGGraph graph) { + public Optional> findNode(PDG graph) { // find node by line number - return graph.getNodes().stream().filter(node -> { + return graph.vertexSet().stream().filter(node -> { Node astNode = node.getAstNode(); if (!astNode.getBegin().isPresent() || !astNode.getEnd().isPresent()) @@ -43,7 +43,7 @@ public class LineNumberCriterion extends SlicingCriterion { } @Override - public Optional> findNode(SDGGraph graph) { + public Optional> findNode(SDG graph) { return Optional.empty(); } diff --git a/src/main/java/tfm/slicing/Slice.java b/src/main/java/tfm/slicing/Slice.java index dca9284bf4c8539eac8f54f6e235f3cdd6b833f9..034c6ec3cc1b12cdbe0ede0e573ac27871fee309 100644 --- a/src/main/java/tfm/slicing/Slice.java +++ b/src/main/java/tfm/slicing/Slice.java @@ -3,7 +3,7 @@ package tfm.slicing; import com.github.javaparser.JavaParser; import com.github.javaparser.ast.CompilationUnit; import tfm.exec.PDGLog; -import tfm.graphs.PDGGraph; +import tfm.graphs.PDG; import tfm.utils.Logger; import tfm.utils.Utils; import tfm.validation.PDGValidator; @@ -20,15 +20,15 @@ public class Slice { public static void main(String[] args) throws IOException { CompilationUnit compilationUnit = JavaParser.parse(new File(PROGRAM_FOLDER + PROGRAM_NAME + ".java")); - PDGGraph pdgGraph = new PDGGraph(); + PDG pdg = new PDG(); - compilationUnit.accept(new PDGBuilder(pdgGraph), pdgGraph.getRootNode()); + compilationUnit.accept(new PDGBuilder(pdg), null); Logger.log("=================="); Logger.log("= Starting slice ="); Logger.log("=================="); - PDGGraph sliced = pdgGraph.slice(new LineNumberCriterion(18, "x")); + PDG sliced = pdg.slice(new LineNumberCriterion(18, "x")); PDGLog pdgLog = new PDGLog(sliced); pdgLog.log(); diff --git a/src/main/java/tfm/slicing/SlicingCriterion.java b/src/main/java/tfm/slicing/SlicingCriterion.java index 2574071e7c1caaf7dfd213cf666713a5b3d3edf1..b01c35704f56f30e23b4ffaa6daef3406070142a 100644 --- a/src/main/java/tfm/slicing/SlicingCriterion.java +++ b/src/main/java/tfm/slicing/SlicingCriterion.java @@ -1,8 +1,8 @@ package tfm.slicing; -import tfm.graphs.CFGGraph; -import tfm.graphs.PDGGraph; -import tfm.graphs.SDGGraph; +import tfm.graphs.CFG; +import tfm.graphs.PDG; +import tfm.graphs.SDG; import tfm.nodes.GraphNode; import java.util.Optional; @@ -19,9 +19,9 @@ public abstract class SlicingCriterion { return variable; } - public abstract Optional> findNode(CFGGraph graph); - public abstract Optional> findNode(PDGGraph graph); - public abstract Optional> findNode(SDGGraph graph); + public abstract Optional> findNode(CFG graph); + public abstract Optional> findNode(PDG graph); + public abstract Optional> findNode(SDG graph); @Override public String toString() { diff --git a/src/main/java/tfm/utils/ASTUtils.java b/src/main/java/tfm/utils/ASTUtils.java index a8e70316198da35e4f89fa30ea6e897e35fb07a6..0ec3b1b3bd9d38266e9fcd32fed1d67bb5e50afd 100644 --- a/src/main/java/tfm/utils/ASTUtils.java +++ b/src/main/java/tfm/utils/ASTUtils.java @@ -1,6 +1,5 @@ package tfm.utils; -import com.github.javaparser.Position; import com.github.javaparser.ast.Node; import com.github.javaparser.ast.NodeList; import com.github.javaparser.ast.stmt.BlockStmt; diff --git a/src/main/java/tfm/validation/PDGValidator.java b/src/main/java/tfm/validation/PDGValidator.java index 48cb5d81df203242cd45a598ce31b1f04d3d329f..43e0241f31d5931fb4e37c8b530816b3c5db933b 100644 --- a/src/main/java/tfm/validation/PDGValidator.java +++ b/src/main/java/tfm/validation/PDGValidator.java @@ -7,13 +7,10 @@ import com.github.javaparser.ast.NodeList; import com.github.javaparser.ast.body.ClassOrInterfaceDeclaration; import com.github.javaparser.ast.body.MethodDeclaration; import com.github.javaparser.ast.body.Parameter; -import com.github.javaparser.ast.stmt.BlockStmt; -import com.github.javaparser.ast.stmt.Statement; import com.github.javaparser.ast.type.ArrayType; import com.github.javaparser.ast.type.VoidType; import com.github.javaparser.ast.visitor.VoidVisitorAdapter; -import tfm.graphs.PDGGraph; -import tfm.nodes.GraphNode; +import tfm.graphs.PDG; import tfm.utils.Logger; import tfm.utils.Utils; import tfm.visitors.pdg.PDGBuilder; @@ -21,7 +18,6 @@ import tfm.visitors.pdg.PDGBuilder; import java.io.File; import java.io.FileNotFoundException; import java.io.PrintWriter; -import java.util.Comparator; import java.util.Objects; import java.util.Optional; @@ -63,38 +59,41 @@ public class PDGValidator { } public static boolean generateAndCheck(MethodDeclaration methodDeclaration) { - PDGGraph graph = new PDGGraph(); + PDG graph = new PDG(); - methodDeclaration.accept(new PDGBuilder(graph), graph.getRootNode()); + methodDeclaration.accept(new PDGBuilder(graph), null); return check(methodDeclaration, graph); } - public static boolean check(MethodDeclaration methodDeclaration, PDGGraph graph) { + public static boolean check(MethodDeclaration methodDeclaration, PDG graph) { MethodDeclaration generatedMethod = generateMethod(methodDeclaration, graph); return ProgramComparator.areEqual(methodDeclaration, generatedMethod); } - public static MethodDeclaration generateMethod(MethodDeclaration info, PDGGraph graph) { - MethodDeclaration methodDeclaration = new MethodDeclaration(); - - methodDeclaration.setName(info.getNameAsString()); - methodDeclaration.setModifiers(info.getModifiers()); - methodDeclaration.setType(info.getType()); - methodDeclaration.setParameters(info.getParameters()); - - BlockStmt methodBody = new BlockStmt(); - methodDeclaration.setBody(methodBody); - - graph.getNodesAtLevel(1).stream() - .sorted(Comparator.comparingInt(GraphNode::getId)) - .forEach(node -> methodBody.addStatement((Statement) node.getAstNode())); - - return methodDeclaration; + @Deprecated + public static MethodDeclaration generateMethod(MethodDeclaration info, PDG graph) { + // TODO: this does not work properly, replace or remove + throw new IllegalStateException("Deprecated method"); +// MethodDeclaration methodDeclaration = new MethodDeclaration(); +// +// methodDeclaration.setName(info.getNameAsString()); +// methodDeclaration.setModifiers(info.getModifiers()); +// methodDeclaration.setType(info.getType()); +// methodDeclaration.setParameters(info.getParameters()); +// +// BlockStmt methodBody = new BlockStmt(); +// methodDeclaration.setBody(methodBody); +// +// graph.getNodesAtLevel(1).stream() +// .sorted(Comparator.comparingInt(GraphNode::getId)) +// .forEach(node -> methodBody.addStatement((Statement) node.getAstNode())); +// +// return methodDeclaration; } - public static void printPDGProgram(String fileName, PDGGraph graph) throws FileNotFoundException { + public static void printPDGProgram(String fileName, PDG graph) throws FileNotFoundException { CompilationUnit generatedProgram = new CompilationUnit(); ClassOrInterfaceDeclaration clazz = generatedProgram.addClass(fileName).setPublic(true); diff --git a/src/main/java/tfm/visitors/cfg/CFGBuilder.java b/src/main/java/tfm/visitors/cfg/CFGBuilder.java index 633aad830fb53252afc64c2c2e8f5f2ab3a67c5a..b3ea34c82335f5a8efe5d890573089dd51f57cbf 100644 --- a/src/main/java/tfm/visitors/cfg/CFGBuilder.java +++ b/src/main/java/tfm/visitors/cfg/CFGBuilder.java @@ -5,7 +5,7 @@ import com.github.javaparser.ast.expr.BooleanLiteralExpr; import com.github.javaparser.ast.expr.Expression; import com.github.javaparser.ast.stmt.*; import com.github.javaparser.ast.visitor.VoidVisitorAdapter; -import tfm.graphs.CFGGraph; +import tfm.graphs.CFG; import tfm.nodes.GraphNode; import tfm.utils.ASTUtils; @@ -13,45 +13,28 @@ import java.util.*; public class CFGBuilder extends VoidVisitorAdapter { - private CFGGraph graph; + private CFG graph; - private Queue lastParentNodes; - private List bodyBreaks; + private Queue> lastParentNodes; + private List> bodyBreaks; - public CFGBuilder(CFGGraph graph) { + public CFGBuilder(CFG graph) { this.graph = graph; - this.lastParentNodes = Collections.asLifoQueue( - new ArrayDeque<>( - Collections.singletonList(graph.getRootNode()) - ) - ); + this.lastParentNodes = Collections.asLifoQueue(new ArrayDeque<>()); this.bodyBreaks = new ArrayList<>(); } @Override public void visit(ExpressionStmt expressionStmt, Void arg) { - String expression = expressionStmt.toString().replace("\"", "\\\""); - - GraphNode nextNode = addNodeAndArcs(expression, expressionStmt); + GraphNode nextNode = addNodeAndArcs(expressionStmt.toString(), expressionStmt); lastParentNodes.add(nextNode); } -// @Override -// public void visit(VariableDeclarationExpr variableDeclarationExpr, Void arg) { -// GraphNode nextNode = addNodeAndArcs(variableDeclarationExpr.toString()); -// -// lastParentNodes.add(nextNode); -// -// Logger.log(variableDeclarationExpr); -// -// super.visit(variableDeclarationExpr, arg); -// } - @Override public void visit(IfStmt ifStmt, Void arg) { - GraphNode ifCondition = addNodeAndArcs( + GraphNode ifCondition = addNodeAndArcs( String.format("if (%s)", ifStmt.getCondition().toString()), ifStmt ); @@ -61,7 +44,7 @@ public class CFGBuilder extends VoidVisitorAdapter { // Visit "then" ifStmt.getThenStmt().accept(this, arg); - Queue lastThenNodes = new ArrayDeque<>(lastParentNodes); + Queue> lastThenNodes = new ArrayDeque<>(lastParentNodes); if (ifStmt.hasElseBranch()) { lastParentNodes.clear(); @@ -77,7 +60,7 @@ public class CFGBuilder extends VoidVisitorAdapter { @Override public void visit(WhileStmt whileStmt, Void arg) { - GraphNode whileCondition = addNodeAndArcs( + GraphNode whileCondition = addNodeAndArcs( String.format("while (%s)", whileStmt.getCondition().toString()), whileStmt ); @@ -101,7 +84,7 @@ public class CFGBuilder extends VoidVisitorAdapter { body.accept(this, arg); - GraphNode doWhileNode = addNodeAndArcs( + GraphNode doWhileNode = addNodeAndArcs( String.format("while (%s)", doStmt.getCondition()), doStmt ); @@ -120,19 +103,10 @@ public class CFGBuilder extends VoidVisitorAdapter { @Override public void visit(ForStmt forStmt, Void arg) { -// String inizialization = forStmt.getInitialization().stream() -// .map(GraphNode::toString) -// .collect(Collectors.joining(",")); -// -// String update = forStmt.getUpdate().stream() -// .map(GraphNode::toString) -// .collect(Collectors.joining(",")); - Expression comparison = forStmt.getCompare().orElse(new BooleanLiteralExpr(true)); -// forStmt.getInitialization().forEach(expression -> new ExpressionStmt(expression).accept(this, null)); - GraphNode forNode = addNodeAndArcs( + GraphNode forNode = addNodeAndArcs( String.format("for (;%s;)", comparison), forStmt ); @@ -156,7 +130,7 @@ public class CFGBuilder extends VoidVisitorAdapter { @Override public void visit(ForEachStmt forEachStmt, Void arg) { - GraphNode foreachNode = addNodeAndArcs( + GraphNode foreachNode = addNodeAndArcs( String.format("for (%s : %s)", forEachStmt.getVariable(), forEachStmt.getIterable()), forEachStmt ); @@ -176,23 +150,23 @@ public class CFGBuilder extends VoidVisitorAdapter { @Override public void visit(SwitchStmt switchStmt, Void arg) { - GraphNode switchNode = addNodeAndArcs( + GraphNode switchNode = addNodeAndArcs( String.format("switch (%s)", switchStmt.getSelector()), switchStmt ); lastParentNodes.add(switchNode); - List allEntryBreaks = new ArrayList<>(); + List> allEntryBreaks = new ArrayList<>(); - List lastEntryStatementsWithNoBreak = new ArrayList<>(); + List> lastEntryStatementsWithNoBreak = new ArrayList<>(); switchStmt.getEntries().forEach(switchEntryStmt -> { String label = switchEntryStmt.getLabel() .map(expression -> "case " + expression) .orElse("default"); - GraphNode switchEntryNode = addNodeAndArcs(label, switchEntryStmt); + GraphNode switchEntryNode = addNodeAndArcs(label, switchEntryStmt); lastParentNodes.add(switchEntryNode); lastParentNodes.addAll(lastEntryStatementsWithNoBreak); @@ -226,14 +200,14 @@ public class CFGBuilder extends VoidVisitorAdapter { public void visit(ContinueStmt continueStmt, Void arg) { Statement continuableStatement = ASTUtils.findFirstAncestorStatementFrom(continueStmt, ASTUtils::isLoop); - GraphNode continuableNode = graph.findNodeByASTNode(continuableStatement).get(); + GraphNode continuableNode = graph.findNodeByASTNode(continuableStatement).get(); lastParentNodes.forEach(parentNode -> graph.addControlFlowEdge(parentNode, continuableNode)); } @Override public void visit(ReturnStmt returnStmt, Void arg) { - GraphNode node = addNodeAndArcs( + GraphNode node = addNodeAndArcs( returnStmt.toString(), returnStmt ); @@ -243,19 +217,25 @@ public class CFGBuilder extends VoidVisitorAdapter { @Override public void visit(MethodDeclaration methodDeclaration, Void arg) { - if (!lastParentNodes.isEmpty() && Objects.equals(lastParentNodes.peek().getData(), "Stop")) { + if (!lastParentNodes.isEmpty() && Objects.equals(lastParentNodes.peek().getInstruction(), "Stop")) { throw new IllegalStateException("CFG is only allowed for one method, not multiple!"); } + this.graph.buildRootNode("Start", methodDeclaration); + + assert this.graph.getRootNode().isPresent(); + + lastParentNodes.add(this.graph.getRootNode().get()); + super.visit(methodDeclaration, arg); lastParentNodes.add(addNodeAndArcs("Stop", new EmptyStmt())); } - private GraphNode addNodeAndArcs(String nodeData, Statement statement) { - GraphNode node = graph.addNode(nodeData, statement); + private GraphNode addNodeAndArcs(String nodeData, Statement statement) { + GraphNode node = graph.addNode(nodeData, statement); - GraphNode parent = lastParentNodes.poll(); // ALWAYS exists a parent + GraphNode parent = lastParentNodes.poll(); // ALWAYS exists a parent graph.addControlFlowEdge(parent, node); while (!lastParentNodes.isEmpty()) { diff --git a/src/main/java/tfm/visitors/pdg/ControlDependencyBuilder.java b/src/main/java/tfm/visitors/pdg/ControlDependencyBuilder.java index 7ceab6685c786ed72b16a599d94af7d1def0faab..75c1b1e9c7053070e4b8d7621f7ee401e68640c4 100644 --- a/src/main/java/tfm/visitors/pdg/ControlDependencyBuilder.java +++ b/src/main/java/tfm/visitors/pdg/ControlDependencyBuilder.java @@ -2,20 +2,20 @@ package tfm.visitors.pdg; import com.github.javaparser.ast.stmt.*; import com.github.javaparser.ast.visitor.VoidVisitorAdapter; -import tfm.graphs.CFGGraph; -import tfm.graphs.PDGGraph; +import tfm.graphs.CFG; +import tfm.graphs.PDG; import tfm.nodes.GraphNode; import java.util.stream.Collectors; -public class ControlDependencyBuilder extends VoidVisitorAdapter { +public class ControlDependencyBuilder extends VoidVisitorAdapter> { - private CFGGraph cfgGraph; - private PDGGraph pdgGraph; + private CFG cfg; + private PDG pdg; - public ControlDependencyBuilder(PDGGraph pdgGraph, CFGGraph cfgGraph) { - this.pdgGraph = pdgGraph; - this.cfgGraph = cfgGraph; + public ControlDependencyBuilder(PDG pdg, CFG cfg) { + this.pdg = pdg; + this.cfg = cfg; } @Override @@ -25,7 +25,7 @@ public class ControlDependencyBuilder extends VoidVisitorAdapter { @Override public void visit(IfStmt ifStmt, GraphNode parent) { - GraphNode node = addNodeAndControlDependency(ifStmt, parent); + GraphNode node = addNodeAndControlDependency(ifStmt, parent); ifStmt.getThenStmt().accept(this, node); @@ -34,7 +34,7 @@ public class ControlDependencyBuilder extends VoidVisitorAdapter { @Override public void visit(WhileStmt whileStmt, GraphNode parent) { - GraphNode node = addNodeAndControlDependency(whileStmt, parent); + GraphNode node = addNodeAndControlDependency(whileStmt, parent); whileStmt.getBody().accept(this, node); } @@ -54,42 +54,41 @@ public class ControlDependencyBuilder extends VoidVisitorAdapter { .orElse("true"); - GraphNode forNode = pdgGraph.addNode( + GraphNode forNode = pdg.addNode( String.format("for (%s;%s;%s)", initialization, compare, update), forStmt ); - pdgGraph.addControlDependencyArc(parent, forNode); + pdg.addControlDependencyArc(parent, forNode); forStmt.getBody().accept(this, forNode); } @Override public void visit(ForEachStmt forEachStmt, GraphNode parent) { - GraphNode node = addNodeAndControlDependency(forEachStmt, parent); + GraphNode node = addNodeAndControlDependency(forEachStmt, parent); forEachStmt.getBody().accept(this, node); } @Override public void visit(SwitchStmt switchStmt, GraphNode parent) { - GraphNode node = addNodeAndControlDependency(switchStmt, parent); + GraphNode node = addNodeAndControlDependency(switchStmt, parent); switchStmt.getEntries().accept(this, node); } @Override public void visit(SwitchEntryStmt switchEntryStmt, GraphNode parent) { - GraphNode node = addNodeAndControlDependency(switchEntryStmt, parent); + GraphNode node = addNodeAndControlDependency(switchEntryStmt, parent); switchEntryStmt.getStatements().accept(this, node); } - private GraphNode addNodeAndControlDependency(Statement statement, GraphNode parent) { - GraphNode cfgNode = cfgGraph.findNodeByASTNode(statement).get(); - - GraphNode node = pdgGraph.addNode(cfgNode.getData(), cfgNode.getAstNode()); - pdgGraph.addControlDependencyArc(parent, node); + private GraphNode addNodeAndControlDependency(Statement statement, GraphNode parent) { + GraphNode cfgNode = cfg.findNodeByASTNode(statement).get(); + GraphNode node = pdg.addNode(cfgNode.getInstruction(), cfgNode.getAstNode()); + pdg.addControlDependencyArc(parent, node); return node; } diff --git a/src/main/java/tfm/visitors/pdg/DataDependencyBuilder.java b/src/main/java/tfm/visitors/pdg/DataDependencyBuilder.java index d7bd5f14746e4e0da30818d10c364be4abbf6088..786abca5667466f9229cea6d0b1fe0ce5a92133e 100644 --- a/src/main/java/tfm/visitors/pdg/DataDependencyBuilder.java +++ b/src/main/java/tfm/visitors/pdg/DataDependencyBuilder.java @@ -2,8 +2,8 @@ package tfm.visitors.pdg; import com.github.javaparser.ast.stmt.*; import com.github.javaparser.ast.visitor.VoidVisitorAdapter; -import tfm.graphs.CFGGraph; -import tfm.graphs.PDGGraph; +import tfm.graphs.CFG; +import tfm.graphs.PDG; import tfm.nodes.GraphNode; import tfm.variables.VariableExtractor; @@ -12,12 +12,12 @@ import java.util.Set; public class DataDependencyBuilder extends VoidVisitorAdapter { - private CFGGraph cfgGraph; - private PDGGraph pdgGraph; + private CFG cfg; + private PDG pdg; - public DataDependencyBuilder(PDGGraph pdgGraph, CFGGraph cfgGraph) { - this.pdgGraph = pdgGraph; - this.cfgGraph = cfgGraph; + public DataDependencyBuilder(PDG pdg, CFG cfg) { + this.pdg = pdg; + this.cfg = cfg; } @Override @@ -43,7 +43,7 @@ public class DataDependencyBuilder extends VoidVisitorAdapter { @Override public void visit(ForStmt forStmt, Void ignored) { - GraphNode forNode = pdgGraph.findNodeByASTNode(forStmt).get(); + GraphNode forNode = pdg.findNodeByASTNode(forStmt).get(); forStmt.getInitialization().stream() .map(ExpressionStmt::new) @@ -80,7 +80,7 @@ public class DataDependencyBuilder extends VoidVisitorAdapter { } private void buildDataDependency(Statement statement) { - buildDataDependency(pdgGraph.findNodeByASTNode(statement).get()); + buildDataDependency(pdg.findNodeByASTNode(statement).get()); } private void buildDataDependency(GraphNode node) { @@ -88,7 +88,7 @@ public class DataDependencyBuilder extends VoidVisitorAdapter { .setOnVariableUseListener(variable -> { node.addUsedVariable(variable); - Optional> nodeOptional = cfgGraph.findNodeByASTNode(node.getAstNode()); + Optional> nodeOptional = cfg.findNodeByASTNode(node.getAstNode()); if (!nodeOptional.isPresent()) { return; @@ -96,11 +96,11 @@ public class DataDependencyBuilder extends VoidVisitorAdapter { GraphNode cfgNode = nodeOptional.get(); - Set> lastDefinitions = cfgGraph.findLastDefinitionsFrom(cfgNode, variable); + Set> lastDefinitions = cfg.findLastDefinitionsFrom(cfgNode, variable); for (GraphNode definitionNode : lastDefinitions) { - pdgGraph.findNodeByASTNode(definitionNode.getAstNode()) - .ifPresent(pdgNode -> pdgGraph.addDataDependencyArc(pdgNode, node, variable)); + pdg.findNodeByASTNode(definitionNode.getAstNode()) + .ifPresent(pdgNode -> pdg.addDataDependencyArc(pdgNode, node, variable)); } }) .setOnVariableDefinitionListener(node::addDefinedVariable) @@ -114,13 +114,13 @@ public class DataDependencyBuilder extends VoidVisitorAdapter { .setOnVariableUseListener(variable -> { forNode.addUsedVariable(variable); - Optional> nodeOptional = cfgGraph.findNodeByASTNode(statement); + Optional> nodeOptional = cfg.findNodeByASTNode(statement); if (!nodeOptional.isPresent()) { return; } - pdgGraph.addDataDependencyArc(forNode, forNode, variable); + pdg.addDataDependencyArc(forNode, forNode, variable); }) .setOnVariableDefinitionListener(forNode::addDefinedVariable) .setOnVariableDeclarationListener(forNode::addDeclaredVariable) diff --git a/src/main/java/tfm/visitors/pdg/PDGBuilder.java b/src/main/java/tfm/visitors/pdg/PDGBuilder.java index 37aff8c5e66232bcb110f3f9bc557e8c823f041e..104294ab162d45ee3159413b1ee41fb21f39d63c 100644 --- a/src/main/java/tfm/visitors/pdg/PDGBuilder.java +++ b/src/main/java/tfm/visitors/pdg/PDGBuilder.java @@ -1,53 +1,47 @@ package tfm.visitors.pdg; -import com.github.javaparser.ast.Node; import com.github.javaparser.ast.body.MethodDeclaration; import com.github.javaparser.ast.stmt.BlockStmt; import com.github.javaparser.ast.visitor.VoidVisitorAdapter; -import tfm.graphs.CFGGraph; -import tfm.graphs.PDGGraph; -import tfm.nodes.GraphNode; +import tfm.graphs.CFG; +import tfm.graphs.PDG; import tfm.visitors.cfg.CFGBuilder; -public class PDGBuilder extends VoidVisitorAdapter> { +public class PDGBuilder extends VoidVisitorAdapter { - private PDGGraph pdgGraph; - private CFGGraph cfgGraph; + private PDG pdg; + private CFG cfg; - public PDGBuilder(PDGGraph pdgGraph) { - this(pdgGraph, new CFGGraph() { - @Override - protected String getRootNodeData() { - return "Start"; - } - }); + public PDGBuilder(PDG pdg) { + this(pdg, new CFG()); } - public PDGBuilder(PDGGraph pdgGraph, CFGGraph cfgGraph) { - this.pdgGraph = pdgGraph; - this.cfgGraph = cfgGraph; + public PDGBuilder(PDG pdg, CFG cfg) { + this.pdg = pdg; + this.cfg = cfg; - this.pdgGraph.setCfgGraph(cfgGraph); + this.pdg.setCfg(cfg); } - public void visit(MethodDeclaration methodDeclaration, GraphNode parent) { + public void visit(MethodDeclaration methodDeclaration, Void empty) { if (!methodDeclaration.getBody().isPresent()) return; - // Assign the method declaration to the root node of the PDG graph - this.pdgGraph.getRootNode().setAstNode(methodDeclaration); + this.pdg.buildRootNode("ENTER " + methodDeclaration.getNameAsString(), methodDeclaration); - BlockStmt methodBody = methodDeclaration.getBody().get(); + assert this.pdg.getRootNode().isPresent(); // build CFG - methodBody.accept(new CFGBuilder(cfgGraph), null); + methodDeclaration.accept(new CFGBuilder(cfg), null); + + BlockStmt methodBody = methodDeclaration.getBody().get(); // Build control dependency - ControlDependencyBuilder controlDependencyBuilder = new ControlDependencyBuilder(pdgGraph, cfgGraph); - methodBody.accept(controlDependencyBuilder, parent); + ControlDependencyBuilder controlDependencyBuilder = new ControlDependencyBuilder(pdg, cfg); + methodBody.accept(controlDependencyBuilder, pdg.getRootNode().get()); // Build data dependency - DataDependencyBuilder dataDependencyBuilder = new DataDependencyBuilder(pdgGraph, cfgGraph); + DataDependencyBuilder dataDependencyBuilder = new DataDependencyBuilder(pdg, cfg); methodBody.accept(dataDependencyBuilder, null); } } diff --git a/src/main/java/tfm/visitors/pdg/PDGVisitor.java b/src/main/java/tfm/visitors/pdg/PDGVisitor.java deleted file mode 100644 index 6599e2e6517299625b9e592f087d738bb2f6bfb6..0000000000000000000000000000000000000000 --- a/src/main/java/tfm/visitors/pdg/PDGVisitor.java +++ /dev/null @@ -1,253 +0,0 @@ -package tfm.visitors.pdg; - -import com.github.javaparser.ast.visitor.VoidVisitorAdapter; -import tfm.nodes.GraphNode; -import tfm.scopes.ScopeHolder; - -@Deprecated -public class PDGVisitor extends VoidVisitorAdapter>> { - -// private VariableSet variableSet; - -// protected PDGGraph graph; -// protected ScopeHolder globalScope; -// -// public PDGVisitor(PDGGraph graph, ScopeHolder scopeHolder) { -// this.graph = graph; -// this.globalScope = scopeHolder; -// } -// -// @Override -// public void visit(ExpressionStmt n, ScopeHolder scope) { -// Expression expression = n.getExpression(); -// -// PDGNode expressionNode = graph.addNode(expression.toString(), n); -// -// graph.addControlDependencyArc(scope.getRoot(), expressionNode); -// -// VariableScope expressionScope = new VariableScope<>(expressionNode); -// -// new VariableExtractor() -// .setOnVariableDeclarationListener(variable -> -// expressionScope.addVariableDeclaration(variable, expressionNode) -// ).setOnVariableDefinitionListener(variable -> -// expressionScope.addVariableDefinition(variable, expressionNode) -// ).setOnVariableUseListener(variable -> { -// expressionScope.addVariableUse(variable, expressionNode); -// -// Scope searchScope = scope.isVariableDefined(variable) ? scope : globalScope; -// -// searchScope.getLastDefinitions(variable) -// .forEach(variableDefinition -> graph.addDataDependencyArc( -// variableDefinition.getNode(), -// expressionNode, -// variable -// )); -// }) -// .visit(expression); -// -// scope.addSubscope(expressionScope); -// } -// -// @Override -// public void visit(IfStmt ifStmt, ScopeHolder scope) { -// PDGNode ifNode = graph.addNode( -// String.format("if (%s)", ifStmt.getCondition().toString()), -// ifStmt -// ); -// -// graph.addControlDependencyArc(scope.getRoot(), ifNode); -// -// ScopeHolder ifScope = ifStmt.hasElseBranch() ? new IfElseScope<>(ifNode) : new ScopeHolder<>(ifNode); -// -// new VariableExtractor() -// .setOnVariableUseListener(variable -> { -// ifScope.addVariableUse(variable, ifNode); -// -// Scope searchScope = scope.isVariableDefined(variable) ? scope : globalScope; -// -// searchScope.getLastDefinitions(variable) -// .forEach(variableDefinition -> -// graph.addDataDependencyArc( -// variableDefinition.getNode(), -// ifNode, -// variable -// ) -// ); -// }) -// .visit(ifStmt.getCondition()); -// -// if (!ifStmt.hasElseBranch()) { -// ifStmt.getThenStmt().accept(this, ifScope); -// } else { -// @SuppressWarnings("unchecked") -// IfElseScope ifElseScope = (IfElseScope) ifScope; -// -// ifStmt.getThenStmt().accept(this, ifElseScope.getThenScope()); -// ifStmt.getElseStmt().get().accept(this, ifElseScope.getElseScope()); -// } -// -// scope.addSubscope(ifScope); -// } -// -// @Override -// public void visit(WhileStmt whileStmt, ScopeHolder scope) { -// // assert whileStmt.getBegin().isPresent(); -// -// PDGNode whileNode = graph.addNode( -// String.format("while (%s)", whileStmt.getCondition().toString()), -// whileStmt -// ); -// -// graph.addControlDependencyArc(scope.getRoot(), whileNode); -// -// ScopeHolder whileScope = new ScopeHolder<>(whileNode); -// -// new VariableExtractor() -// .setOnVariableUseListener(variable -> { -// whileScope.addVariableUse(variable, whileNode); -// -// Scope searchScope = scope.isVariableDefined(variable) ? scope : globalScope; -// -// searchScope.getLastDefinitions(variable) -// .forEach(variableDefinition -> graph.addDataDependencyArc( -// variableDefinition.getNode(), -// whileNode, -// variable -// )); -// }) -// .visit(whileStmt.getCondition()); -// -// whileStmt.getBody().accept(this, whileScope); -// -// buildLoopDataDependencies(whileScope); -// -// scope.addSubscope(whileScope); -// } -// -// private void buildLoopDataDependencies(ScopeHolder scope) { -// scope.getDefinedVariables() -// .forEach(variable -> { -// List> firstDef = scope.getFirstDefinitions(variable); -// List> lastDef = scope.getLastDefinitions(variable); -// -// Set usesFromLastDef = new HashSet<>(); -// -// firstDef.forEach(variableDefinition -> { -// scope.getVariableUsesBeforeNode(variable, variableDefinition.getNode()) -// .forEach(use -> { -// if (!usesFromLastDef.contains(use.getNode().getId())) { -// lastDef.forEach(def -> graph.addDataDependencyArc( -// def.getNode(), -// use.getNode(), -// variable) -// ); -// -// usesFromLastDef.add(use.getNode().getId()); -// } -// }); -// }); -// }); -// } - -// @Override -// public void visit(ForStmt forStmt, PDGNode node) { -// // Add initialization nodes -// forStmt.getInitialization().stream() -// .map(expression -> graph.addNode(expression.toString())) -// .forEach(pdgVertex -> graph.addControlDependencyArc(parent, pdgVertex)); -// -// // Add condition node -// Expression condition = forStmt.getCompare().orElse(new BooleanLiteralExpr(true)); -// PDGNode conditionNode = graph.addNode(condition.toString()); -// -// graph.addControlDependencyArc(parent, conditionNode); -// -// // Visit for -// super.visit(forStmt, conditionNode); -// -// // Add update vertex -// forStmt.getUpdate().stream() -// .map(expression -> graph.addNode(expression.toString())) -// .forEach(pdgVertex -> graph.addControlDependencyArc(conditionNode, pdgVertex)); -// } - -// @Override -// public void visit(ForEachStmt forEachStmt, ScopeHolder scope) { -// // Initializer -// VariableDeclarationExpr iterator = new VariableDeclarationExpr( -// new VariableDeclarator( -// JavaParser.parseClassOrInterfaceType("Iterator"), -// "iterator", -// new ConditionalExpr( -// new MethodCallExpr( -// new MethodCallExpr( -// forEachStmt.getIterable(), -// "getClass" -// ), -// "isArray" -// ), -// new MethodCallExpr( -// new NameExpr("Arrays"), -// "asList", -// new NodeList<>( -// forEachStmt.getIterable() -// ) -// ), -// new CastExpr( -// JavaParser.parseClassOrInterfaceType("Iterable"), -// new CastExpr( -// JavaParser.parseClassOrInterfaceType("Object"), -// forEachStmt.getIterable() -// ) -// ) -// ) -// ) -// ); -// -// // Compare -// MethodCallExpr iteratorHasNext = new MethodCallExpr( -// new NameExpr("iterator"), -// "hasNext" -// ); -// -// // Body -// Type variableType = forEachStmt.getVariable().getCommonType(); -// String variableName = forEachStmt.getVariable().getVariables().get(0).getNameAsString(); -// -// BlockStmt foreachBody = Utils.blockWrapper(forEachStmt.getBody()); -// foreachBody.getStatements().addFirst( -// new ExpressionStmt( -// new VariableDeclarationExpr( -// new VariableDeclarator( -// variableType, -// variableName, -// new CastExpr( -// variableType, -// new MethodCallExpr( -// new NameExpr("iterator"), -// "next" -// ) -// ) -// ) -// ) -// ) -// ); -// -// new ForStmt(new NodeList<>(iterator), iteratorHasNext, new NodeList<>(), foreachBody) -// .accept(this, parent); - - -// } - -// @Override -// public void visit(SwitchStmt switchStmt, PDGNode node) { -// PDGNode switchNode = graph.addNode(switchStmt.toString()); -// -// graph.addControlDependencyArc(parent, switchNode); -// -// switchStmt.getSelector().accept(this, parent); -// switchStmt.getEntries() -// .forEach(switchEntryStmt -> switchEntryStmt.accept(this, switchNode)); -// } -} diff --git a/src/main/java/tfm/visitors/sdg/MethodCallReplacer.java b/src/main/java/tfm/visitors/sdg/MethodCallReplacer.java index 01b853f0479b902a56ee8346609b962eafeeeff1..4613134e2bedd8ab37853b59dd9097f3177f7cfe 100644 --- a/src/main/java/tfm/visitors/sdg/MethodCallReplacer.java +++ b/src/main/java/tfm/visitors/sdg/MethodCallReplacer.java @@ -1,20 +1,17 @@ package tfm.visitors.sdg; -import com.github.javaparser.ast.body.MethodDeclaration; -import tfm.graphs.PDGGraph; -import tfm.graphs.SDGGraph; -import tfm.utils.Context; +import tfm.graphs.SDG; public class MethodCallReplacer { - private SDGGraph sdgGraph; + private SDG sdg; - public MethodCallReplacer(SDGGraph sdgGraph) { - this.sdgGraph = sdgGraph; + public MethodCallReplacer(SDG sdg) { + this.sdg = sdg; } public void replace() { - this.sdgGraph.getContextPDGGraphMap() + this.sdg.getContextPDGGraphMap() .forEach((context, pdgGraph) -> { if (!context.getCurrentMethod().isPresent()) { return; // Should NOT happen diff --git a/src/main/java/tfm/visitors/sdg/MethodCallReplacerVisitor.java b/src/main/java/tfm/visitors/sdg/MethodCallReplacerVisitor.java index eb32ef08806e8c210a84619728e485c12f2cbefe..1eec815843a66ac4f4618a7714415a822190cd7c 100644 --- a/src/main/java/tfm/visitors/sdg/MethodCallReplacerVisitor.java +++ b/src/main/java/tfm/visitors/sdg/MethodCallReplacerVisitor.java @@ -9,7 +9,7 @@ import com.github.javaparser.ast.expr.VariableDeclarationExpr; import com.github.javaparser.ast.stmt.ExpressionStmt; import com.github.javaparser.ast.type.Type; import com.github.javaparser.ast.visitor.VoidVisitorAdapter; -import tfm.graphs.PDGGraph; +import tfm.graphs.PDG; import tfm.nodes.GraphNode; import tfm.utils.Context; import tfm.utils.Logger; @@ -21,10 +21,10 @@ import java.util.stream.Collectors; public class MethodCallReplacerVisitor extends VoidVisitorAdapter { - private PDGGraph pdgGraph; + private PDG pdg; - public MethodCallReplacerVisitor(PDGGraph pdgGraph) { - this.pdgGraph = pdgGraph; + public MethodCallReplacerVisitor(PDG pdg) { + this.pdg = pdg; } @Override @@ -57,7 +57,7 @@ public class MethodCallReplacerVisitor extends VoidVisitorAdapter { if (!Objects.equals(scopeName, currentClass.getNameAsString())) { // Check if 'scopeName' is a variable - List> declarations = pdgGraph.findDeclarationsOfVariable(scopeName); + List> declarations = pdg.findDeclarationsOfVariable(scopeName); if (declarations.isEmpty()) { // It is a static method call of another class. We do nothing diff --git a/src/main/java/tfm/visitors/sdg/NewSDGBuilder.java b/src/main/java/tfm/visitors/sdg/NewSDGBuilder.java index 8938632f772391768719d97097c7e9c81dbca4dd..bf510e0af703d2777943d39b0494529296785e72 100644 --- a/src/main/java/tfm/visitors/sdg/NewSDGBuilder.java +++ b/src/main/java/tfm/visitors/sdg/NewSDGBuilder.java @@ -4,18 +4,17 @@ import com.github.javaparser.ast.CompilationUnit; import com.github.javaparser.ast.body.ClassOrInterfaceDeclaration; import com.github.javaparser.ast.body.MethodDeclaration; import com.github.javaparser.ast.visitor.VoidVisitorAdapter; -import javassist.expr.MethodCall; import tfm.graphbuilding.Graphs; -import tfm.graphs.PDGGraph; -import tfm.graphs.SDGGraph; +import tfm.graphs.PDG; +import tfm.graphs.SDG; import tfm.utils.Context; public class NewSDGBuilder extends VoidVisitorAdapter { - SDGGraph sdgGraph; + SDG sdg; - public NewSDGBuilder(SDGGraph sdgGraph) { - this.sdgGraph = sdgGraph; + public NewSDGBuilder(SDG sdg) { + this.sdg = sdg; } @Override @@ -27,9 +26,9 @@ public class NewSDGBuilder extends VoidVisitorAdapter { context.setCurrentMethod(methodDeclaration); // Build PDG and add to SDGGraph - PDGGraph pdgGraph = Graphs.PDG.fromASTNode(methodDeclaration); + PDG pdg = Graphs.PDG.fromASTNode(methodDeclaration); - sdgGraph.addMethod(methodDeclaration, pdgGraph); + sdg.addMethod(methodDeclaration, pdg); } @Override @@ -52,7 +51,7 @@ public class NewSDGBuilder extends VoidVisitorAdapter { // Once every PDG is built, expand method call nodes of each one // and link them to the corresponding method declaration node - MethodCallReplacer methodCallReplacer = new MethodCallReplacer(sdgGraph); + MethodCallReplacer methodCallReplacer = new MethodCallReplacer(sdg); methodCallReplacer.replace(); diff --git a/src/main/java/tfm/visitors/sdg/SDGBuilder.java b/src/main/java/tfm/visitors/sdg/SDGBuilder.java index 9a5118652e06cc3ec0695d63a6f73d922c3571ce..dabf2fd3693babc2443bccb7ce613922fae4e485 100644 --- a/src/main/java/tfm/visitors/sdg/SDGBuilder.java +++ b/src/main/java/tfm/visitors/sdg/SDGBuilder.java @@ -11,8 +11,8 @@ import com.github.javaparser.ast.stmt.ExpressionStmt; import com.github.javaparser.ast.stmt.Statement; import com.github.javaparser.ast.type.Type; import com.github.javaparser.ast.visitor.VoidVisitorAdapter; -import tfm.graphs.PDGGraph; -import tfm.graphs.SDGGraph; +import tfm.graphs.PDG; +import tfm.graphs.SDG; import tfm.nodes.GraphNode; import tfm.visitors.pdg.PDGBuilder; @@ -28,15 +28,15 @@ import java.util.Optional; */ public class SDGBuilder extends VoidVisitorAdapter { - SDGGraph sdgGraph; - List pdgGraphs; + SDG sdg; + List pdgs; private ClassOrInterfaceDeclaration currentClass; private CompilationUnit currentCompilationUnit; - public SDGBuilder(SDGGraph sdgGraph) { - this.sdgGraph = sdgGraph; - this.pdgGraphs = new ArrayList<>(); + public SDGBuilder(SDG sdg) { + this.sdg = sdg; + this.pdgs = new ArrayList<>(); } @Override @@ -45,23 +45,17 @@ public class SDGBuilder extends VoidVisitorAdapter { return; - if (sdgGraph.isEmpty()) { - sdgGraph.setRootVertex( - new GraphNode<>( - 0, - "ENTER " + methodDeclaration.getNameAsString(), - methodDeclaration - ) - ); + if (sdg.isEmpty()) { + sdg.addNode("ENTER " + methodDeclaration.getNameAsString(), methodDeclaration); } else { // sdgGraph.addMethod(methodDeclaration); } - PDGGraph pdgGraph = new PDGGraph(); + PDG pdg = new PDG(); - PDGBuilder PDGBuilder = new PDGBuilder(pdgGraph) { + PDGBuilder PDGBuilder = new PDGBuilder(pdg) { @Override - public void visit(MethodCallExpr methodCallExpr, GraphNode parent) { + public void visit(MethodCallExpr methodCallExpr, Void empty) { if (methodCallExpr.getScope().isPresent()) { String scopeName = methodCallExpr.getScope().get().toString(); @@ -71,7 +65,7 @@ public class SDGBuilder extends VoidVisitorAdapter { if (!Objects.equals(scopeName, currentClassName)) { // Check if 'scopeName' is a variable - List> declarations = sdgGraph.findDeclarationsOfVariable(scopeName); + List> declarations = sdg.findDeclarationsOfVariable(scopeName); if (declarations.isEmpty()) { // It is a static method call of another class. We don't do anything @@ -117,12 +111,12 @@ public class SDGBuilder extends VoidVisitorAdapter { } }; - PDGBuilder.visit(methodDeclaration, pdgGraph.getRootNode()); + PDGBuilder.visit(methodDeclaration, null); - sdgGraph.addNode(methodDeclaration.getNameAsString(), methodDeclaration); + sdg.addNode(methodDeclaration.getNameAsString(), methodDeclaration); - pdgGraph.getNodes().stream().skip(1).forEach(pdgNode -> { + pdg.vertexSet().stream().skip(1).forEach(pdgNode -> { Statement statement = (Statement) pdgNode.getAstNode(); if (statement.isExpressionStmt()) { @@ -140,11 +134,11 @@ public class SDGBuilder extends VoidVisitorAdapter { - sdgGraph.addPDG(pdgGraph, methodDeclaration); + sdg.addPDG(pdg, methodDeclaration); methodDeclaration.accept(this, ignored); - pdgGraphs.add(pdgGraph); + pdgs.add(pdg); } @Override