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/.gitmodules b/.gitmodules new file mode 100644 index 0000000000000000000000000000000000000000..46394f31083b0e1b8c7e764a168cf31eca891e60 --- /dev/null +++ b/.gitmodules @@ -0,0 +1,3 @@ +[submodule "src/test/res/java-slicing-benchmarks"] + path = src/test/res/java-slicing-benchmarks + url = kaz:repos/java-slicing-benchmarks.git 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..44ab23ebfbcf34e8a6194830a1a9285f0ec4a27d 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 + RELEASE + 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..6dc746ef2353a913415c5faab82fc96c3a167511 100644 --- a/src/main/java/tfm/arcs/Arc.java +++ b/src/main/java/tfm/arcs/Arc.java @@ -1,72 +1,84 @@ 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 { - - @SuppressWarnings("unchecked") - public Arc(GraphNode from, GraphNode to) { - super((edg.graphlib.Vertex) from, (edg.graphlib.Vertex) to); +public abstract class Arc extends DefaultEdge { + /** @see tfm.arcs.cfg.ControlFlowArc */ + public final boolean isControlFlowArc() { + return this instanceof ControlFlowArc; } - public abstract boolean isControlFlowArrow(); + public final ControlFlowArc asControlFlowArc() { + if (isControlFlowArc()) + return (ControlFlowArc) this; + throw new UnsupportedOperationException("Not a ControlFlowArc"); + } - public abstract boolean isControlDependencyArrow(); + /** @see tfm.arcs.cfg.ControlFlowArc.NonExecutable */ + public final boolean isExecutableControlFlowArc() { + return this instanceof ControlFlowArc && + !(this instanceof ControlFlowArc.NonExecutable); + } - public abstract boolean isDataDependencyArrow(); + /** @see tfm.arcs.pdg.ControlDependencyArc */ + 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(); + /** @see tfm.arcs.pdg.DataDependencyArc */ + 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..4a5f23f385692648c4fe10fb444c23f9146246d2 100644 --- a/src/main/java/tfm/arcs/cfg/ControlFlowArc.java +++ b/src/main/java/tfm/arcs/cfg/ControlFlowArc.java @@ -1,36 +1,33 @@ package tfm.arcs.cfg; +import org.jgrapht.io.Attribute; +import org.jgrapht.io.DefaultAttribute; 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; +import tfm.graphs.augmented.ACFG; +import tfm.graphs.cfg.CFG; + +import java.util.Map; + +/** + * An edge of the {@link CFG}, representing the direct + * flow of control. It connects two instructions if, when the source + * is executed, one of the possible next instructions is the destination. + */ +public class ControlFlowArc extends Arc { + /** + * Represents a non-executable control flow arc, used within the {@link ACFG ACFG}. + * Initially it had the following meaning: connecting a statement with + * the following one as if the source was a {@code nop} command (no operation). + *
+ * It is used to improve control dependence, and it should be skipped when + * computing data dependence and other analyses. + */ + public static final class NonExecutable extends ControlFlowArc { + @Override + public Map getDotAttributes() { + Map map = super.getDotAttributes(); + map.put("style", DefaultAttribute.createAttribute("dashed")); + return map; + } } - - @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..969d9f8dddc552d39704ecad8dcc330a6be6b336 100644 --- a/src/main/java/tfm/arcs/pdg/ControlDependencyArc.java +++ b/src/main/java/tfm/arcs/pdg/ControlDependencyArc.java @@ -1,35 +1,14 @@ 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() - ); - } +import tfm.graphs.pdg.PDG; +import tfm.graphs.sdg.SDG; + +/** + * An arc used in the {@link PDG} and {@link SDG} + * used to represent control dependence between two nodes. The traditional definition of + * control dependence is: a node {@code a} is control dependent on node + * {@code b} if and only if {@code b} alters the number of times {@code a} is executed. + */ +public class ControlDependencyArc extends Arc { } diff --git a/src/main/java/tfm/arcs/pdg/DataDependencyArc.java b/src/main/java/tfm/arcs/pdg/DataDependencyArc.java index 11e8ac030217f4905feb05b3609d39ed8841fe1d..22a7bd58804bcc2e6c0e11b76cd0d7aed94369ce 100644 --- a/src/main/java/tfm/arcs/pdg/DataDependencyArc.java +++ b/src/main/java/tfm/arcs/pdg/DataDependencyArc.java @@ -1,54 +1,50 @@ 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; - -public class DataDependencyArc extends Arc { - - 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; +import tfm.graphs.pdg.PDG; +import tfm.graphs.sdg.SDG; + +import java.util.Map; +import java.util.Objects; + +/** + * An arc used in the {@link PDG} and {@link SDG}, + * representing the declaration of some data linked to its usage (of that value). + * There is data dependency between two nodes if and only if (1) the source may + * declare a variable, (2) the destination may use it, and (3) there is a + * path between the nodes where the variable is not redefined. + */ +public class DataDependencyArc extends Arc { + private final String variable; + + public DataDependencyArc(String variable) { + super(); + this.variable = variable; } @Override - public boolean isControlDependencyArrow() { - return false; + public String getLabel() { + return variable; } @Override - public boolean isDataDependencyArrow() { - return true; + public Map getDotAttributes() { + Map map = super.getDotAttributes(); + map.put("style", DefaultAttribute.createAttribute("dashed")); + map.put("color", DefaultAttribute.createAttribute("red")); + return map; } @Override - public String toString() { - return String.format("DataDependencyArc{%s, %s -> %s}", - getData(), - getFromNode().getId(), - getToNode().getId()); + public boolean equals(Object o) { + return super.equals(o) && Objects.equals(variable, ((DataDependencyArc) o).variable); } @Override - public String toGraphvizRepresentation() { - return String.format("%s [style=dashed, color=red, label=\"%s\"];", super.toGraphvizRepresentation(), getData().toString()); + public int hashCode() { + return Objects.hash(variable, super.hashCode()); } } diff --git a/src/main/java/tfm/exec/CFGLog.java b/src/main/java/tfm/exec/CFGLog.java index e04a483c0f2649c6a2854ed04f02130706e62a7a..396953513ab30e0b159c91827699ac9bcda04345 100644 --- a/src/main/java/tfm/exec/CFGLog.java +++ b/src/main/java/tfm/exec/CFGLog.java @@ -1,22 +1,13 @@ package tfm.exec; -import com.github.javaparser.ast.Node; -import tfm.graphs.CFGGraph; -import tfm.visitors.cfg.CFGBuilder; - -public class CFGLog extends GraphLog { +import tfm.graphs.cfg.CFG; +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(); - 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..0fc1fe2851c2101872fe19290919456190c286ea 100644 --- a/src/main/java/tfm/exec/GraphLog.java +++ b/src/main/java/tfm/exec/GraphLog.java @@ -1,6 +1,5 @@ package tfm.exec; -import com.github.javaparser.ast.Node; import tfm.graphs.Graph; import tfm.utils.FileUtil; import tfm.utils.Logger; @@ -40,9 +39,6 @@ public abstract class GraphLog { this.graph = graph; } - public abstract void visit(Node node); - - public void log() throws IOException { Logger.log( "****************************\n" + @@ -55,8 +51,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 { @@ -68,22 +67,25 @@ public abstract class GraphLog { } public void generateImages(String imageName, Format format) throws IOException { - this.imageName = imageName; + this.imageName = imageName + "-" + graph.getClass().getName(); 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..51b9e8ef71372561603322be8c7a66c0f4f23c62 100644 --- a/src/main/java/tfm/exec/Main.java +++ b/src/main/java/tfm/exec/Main.java @@ -3,6 +3,10 @@ package tfm.exec; import com.github.javaparser.JavaParser; import com.github.javaparser.ast.Node; import com.github.javaparser.ast.body.MethodDeclaration; +import tfm.graphs.cfg.CFG; +import tfm.graphs.Graph; +import tfm.graphs.pdg.PDG; +import tfm.graphs.sdg.SDG; import tfm.utils.Logger; import tfm.utils.Utils; @@ -13,7 +17,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"; @@ -37,14 +41,12 @@ public class Main { } // GraphLog - GraphLog graphLog = getGraphLog(args.length == 1 ? args[0] : GRAPH); - long t0 = System.nanoTime(); - graphLog.visit(root); + Graph graph = getBuiltGraph(args.length == 1 ? args[0] : GRAPH, (MethodDeclaration) root); long tf = System.nanoTime(); - long tt = tf - t0; + GraphLog graphLog = getGraphLog(graph); graphLog.log(); graphLog.openVisualRepresentation(); @@ -52,24 +54,35 @@ public class Main { Logger.format("Graph generated in %.2f ms", tt / 10e6); } - private static GraphLog getGraphLog(String graph) throws IOException { - GraphLog graphLog = null; - + private static Graph getBuiltGraph(String graph, MethodDeclaration method) { switch (graph) { case GraphLog.CFG: - graphLog = new CFGLog(); - break; + CFG cfg = new CFG(); + cfg.build(method); + return cfg; case GraphLog.PDG: - graphLog = new PDGLog(); - break; + PDG pdg = new PDG(); + pdg.build(method); + return pdg; case GraphLog.SDG: - graphLog = new SDGLog(); - break; + Logger.log("Not yet considered!"); + return new SDG(); default: Logger.log("Unkown graph type"); System.exit(1); + return null; } + } - return graphLog; + private static GraphLog getGraphLog(Graph graph) { + if (graph instanceof CFG) + return new CFGLog((CFG) graph); + else if (graph instanceof PDG) + return new PDGLog((PDG) graph); + else if (graph instanceof SDG) + return new SDGLog((SDG) graph); + Logger.log("Unknown graph type"); + System.exit(1); + return null; } } diff --git a/src/main/java/tfm/exec/PDGLog.java b/src/main/java/tfm/exec/PDGLog.java index a712abe2f2386ee9c6785f4bc02bd210b832f2ae..6634a78aa903e725e1bc61807d1af36eee3da4d9 100644 --- a/src/main/java/tfm/exec/PDGLog.java +++ b/src/main/java/tfm/exec/PDGLog.java @@ -1,47 +1,34 @@ package tfm.exec; -import tfm.graphs.PDGGraph; +import tfm.graphs.pdg.PDG; import tfm.nodes.GraphNode; import tfm.utils.Logger; -import tfm.visitors.pdg.PDGBuilder; 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; public PDGLog() { 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(); - - node.accept(new PDGBuilder(graph), this.graph.getRootNode()); - - if (cfgLog == null) { - cfgLog = new CFGLog(graph.getCfgGraph()); - } - } - @Override public void log() throws IOException { 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 }", @@ -55,9 +42,9 @@ public class PDGLog extends GraphLog { @Override public void generateImages(String imageName, Format format) throws IOException { - super.generateImages(imageName + "-pdg", format); + super.generateImages(imageName, format); if (cfgLog != null) - cfgLog.generateImages(imageName + "-cfg", format); + cfgLog.generateImages(imageName, format); } @Override diff --git a/src/main/java/tfm/exec/SDGLog.java b/src/main/java/tfm/exec/SDGLog.java index b88de5468eabb9f24dc36c4af1265714eae365a5..cdeae275973139a049d4ddd741318af161a46e24 100644 --- a/src/main/java/tfm/exec/SDGLog.java +++ b/src/main/java/tfm/exec/SDGLog.java @@ -1,17 +1,13 @@ package tfm.exec; -import com.github.javaparser.ast.Node; -import tfm.graphs.SDGGraph; -import tfm.visitors.sdg.SDGBuilder; +import tfm.graphs.sdg.SDG; -import java.io.IOException; - -public class SDGLog extends GraphLog { +public class SDGLog extends GraphLog { + public SDGLog() { + super(); + } - @Override - public void visit(Node node) { - this.graph = new SDGGraph(); - SDGBuilder sdgBuilder = new SDGBuilder(this.graph); - node.accept(sdgBuilder, null); + public SDGLog(SDG graph) { + super(graph); } } diff --git a/src/main/java/tfm/graphbuilding/GraphOptions.java b/src/main/java/tfm/graphbuilding/GraphOptions.java deleted file mode 100644 index 2884f49981f1d6ae9df2c8c7acb2ca617547a0ad..0000000000000000000000000000000000000000 --- a/src/main/java/tfm/graphbuilding/GraphOptions.java +++ /dev/null @@ -1,63 +0,0 @@ -package tfm.graphbuilding; - -import com.github.javaparser.ast.Node; -import tfm.graphs.CFGGraph; -import tfm.graphs.Graph; -import tfm.graphs.PDGGraph; -import tfm.graphs.SDGGraph; -import tfm.visitors.cfg.CFGBuilder; -import tfm.visitors.pdg.PDGBuilder; -import tfm.visitors.sdg.SDGBuilder; - -public abstract class GraphOptions { - public abstract G empty(); - - public G fromASTNode(Node node) { - G emptyGraph = empty(); - - buildGraphWithSpecificVisitor(emptyGraph, node); - - return emptyGraph; - } - - protected abstract void buildGraphWithSpecificVisitor(G emptyGraph, Node node); -} - -class CFGOptions extends GraphOptions { - - @Override - public CFGGraph empty() { - return new CFGGraph(); - } - - @Override - protected void buildGraphWithSpecificVisitor(CFGGraph emptyGraph, Node node) { - node.accept(new CFGBuilder(emptyGraph), null); - } -} - -class PDGOptions extends GraphOptions { - - @Override - public PDGGraph empty() { - return new PDGGraph(); - } - - @Override - protected void buildGraphWithSpecificVisitor(PDGGraph emptyGraph, Node node) { - node.accept(new PDGBuilder(emptyGraph), emptyGraph.getRootNode()); - } -} - -class SDGOptions extends GraphOptions { - - @Override - public SDGGraph empty() { - return new SDGGraph(); - } - - @Override - protected void buildGraphWithSpecificVisitor(SDGGraph 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 deleted file mode 100644 index 235124ca06360392507b42ea2b01df5c844c2612..0000000000000000000000000000000000000000 --- a/src/main/java/tfm/graphbuilding/Graphs.java +++ /dev/null @@ -1,13 +0,0 @@ -package tfm.graphbuilding; - -import tfm.graphs.CFGGraph; -import tfm.graphs.PDGGraph; -import tfm.graphs.SDGGraph; - -public class Graphs { - - 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/Buildable.java b/src/main/java/tfm/graphs/Buildable.java new file mode 100644 index 0000000000000000000000000000000000000000..1eac7aacffeb0e96bc3b0d87424a0a440e7ffdd5 --- /dev/null +++ b/src/main/java/tfm/graphs/Buildable.java @@ -0,0 +1,6 @@ +package tfm.graphs; + +public interface Buildable { + void build(A arg); + boolean isBuilt(); +} 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..104b1601bf9f6b1761bafecc886cede230628527 --- /dev/null +++ b/src/main/java/tfm/graphs/GraphWithRootNode.java @@ -0,0 +1,53 @@ +package tfm.graphs; + +import com.github.javaparser.ast.Node; +import com.github.javaparser.ast.body.MethodDeclaration; +import tfm.nodes.GraphNode; +import tfm.nodes.NodeFactory; + +import java.util.Objects; +import java.util.Optional; + +public abstract class GraphWithRootNode extends Graph implements Buildable { + + 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/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/Sliceable.java b/src/main/java/tfm/graphs/Sliceable.java new file mode 100644 index 0000000000000000000000000000000000000000..504d947121508a4f8e03bd3c0b642f0f0d35550f --- /dev/null +++ b/src/main/java/tfm/graphs/Sliceable.java @@ -0,0 +1,8 @@ +package tfm.graphs; + +import tfm.slicing.Slice; +import tfm.slicing.SlicingCriterion; + +public interface Sliceable { + Slice slice(SlicingCriterion sc); +} diff --git a/src/main/java/tfm/graphs/augmented/ACFG.java b/src/main/java/tfm/graphs/augmented/ACFG.java new file mode 100644 index 0000000000000000000000000000000000000000..b05e44e043bee72cbad1a46c8b7170fc6aea065e --- /dev/null +++ b/src/main/java/tfm/graphs/augmented/ACFG.java @@ -0,0 +1,17 @@ +package tfm.graphs.augmented; + +import tfm.arcs.cfg.ControlFlowArc; +import tfm.graphs.cfg.CFG; +import tfm.graphs.cfg.CFGBuilder; +import tfm.nodes.GraphNode; + +public class ACFG extends CFG { + public void addNonExecutableControlFlowEdge(GraphNode from, GraphNode to) { + addControlFlowEdge(from, to, new ControlFlowArc.NonExecutable()); + } + + @Override + protected CFGBuilder newCFGBuilder() { + return new ACFGBuilder(this); + } +} diff --git a/src/main/java/tfm/graphs/augmented/ACFGBuilder.java b/src/main/java/tfm/graphs/augmented/ACFGBuilder.java new file mode 100644 index 0000000000000000000000000000000000000000..d250d0909795fc76259e9eae06741489d5df99a6 --- /dev/null +++ b/src/main/java/tfm/graphs/augmented/ACFGBuilder.java @@ -0,0 +1,248 @@ +package tfm.graphs.augmented; + +import com.github.javaparser.ast.Node; +import com.github.javaparser.ast.body.MethodDeclaration; +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.VoidVisitor; +import tfm.graphs.cfg.CFGBuilder; +import tfm.nodes.GraphNode; +import tfm.utils.ASTUtils; + +import java.util.LinkedList; +import java.util.List; + +/** + * Populates a {@link ACFG}, given one and an AST root node. + * For now it only accepts {@link MethodDeclaration} as roots, as it disallows + * multiple methods. + *
+ * Usage: + *
    + *
  1. Create a new {@link ACFG}.
  2. + *
  3. Create a new {@link ACFGBuilder}, passing the graph as argument.
  4. + *
  5. Accept the builder as a visitor of the {@link MethodDeclaration} you + * want to analyse using {@link Node#accept(VoidVisitor, Object)}: {@code methodDecl.accept(builder, null)}
  6. + *
  7. Once the previous step is finished, the complete CFG is saved in + * the object created in the first step. The builder should be discarded + * and not reused.
  8. + *
+ */ +public class ACFGBuilder extends CFGBuilder { + /** Same as {@link ACFGBuilder#hangingNodes}, but to be connected as non-executable edges. */ + private final List> nonExecHangingNodes = new LinkedList<>(); + + protected ACFGBuilder(ACFG graph) { + super(graph); + } + + @Override + protected void connectTo(GraphNode node) { + for (GraphNode src : nonExecHangingNodes) + ((ACFG) graph).addNonExecutableControlFlowEdge(src, node); + super.connectTo(node); + } + + @Override + protected void clearHanging() { + super.clearHanging(); + nonExecHangingNodes.clear(); + } + + @Override + public void visit(IfStmt ifStmt, Void arg) { + // *if* -> {then else} -> after + GraphNode cond = connectTo(ifStmt, String.format("if (%s)", ifStmt.getCondition())); + + // if -> {*then* else} -> after + ifStmt.getThenStmt().accept(this, arg); + List> hangingThenNodes = new LinkedList<>(hangingNodes); + List> hangingThenFalseNodes = new LinkedList<>(nonExecHangingNodes); + + if (ifStmt.getElseStmt().isPresent()) { + // if -> {then *else*} -> after + clearHanging(); + hangingNodes.add(cond); + ifStmt.getElseStmt().get().accept(this, arg); + hangingNodes.addAll(hangingThenNodes); + nonExecHangingNodes.addAll(hangingThenFalseNodes); + } else { + // if -> {then **} -> after + hangingNodes.add(cond); + } + // if -> {then else} -> *after* + } + + @Override + public void visit(WhileStmt whileStmt, Void arg) { + GraphNode cond = connectTo(whileStmt, String.format("while (%s)", whileStmt.getCondition())); + breakStack.push(new LinkedList<>()); + continueStack.push(new LinkedList<>()); + + whileStmt.getBody().accept(this, arg); + + hangingNodes.addAll(continueStack.pop()); + hangLabelledContinueStmts(whileStmt.getParentNode().orElse(null)); + // Loop contains anything + if (hangingNodes.size() != 1 || hangingNodes.get(0) != cond) + connectTo(cond); + else if (!nonExecHangingNodes.isEmpty()) + connectTo(cond); + hangingNodes.addAll(breakStack.pop()); + } + + @Override + public void visit(DoStmt doStmt, Void arg) { + breakStack.push(new LinkedList<>()); + continueStack.push(new LinkedList<>()); + + GraphNode cond = connectTo(doStmt, String.format("while (%s)", doStmt.getCondition())); + + doStmt.getBody().accept(this, arg); + + hangingNodes.addAll(continueStack.pop()); + hangLabelledContinueStmts(doStmt.getParentNode().orElse(null)); + // Loop contains anything + if (hangingNodes.size() != 1 || hangingNodes.get(0) != cond) + connectTo(cond); + else if (!nonExecHangingNodes.isEmpty()) + connectTo(cond); + hangingNodes.addAll(breakStack.pop()); + } + + @Override + public void visit(ForStmt forStmt, Void arg) { + breakStack.push(new LinkedList<>()); + continueStack.push(new LinkedList<>()); + + // Initialization + forStmt.getInitialization().forEach(this::connectTo); + + // Condition + Expression condition = forStmt.getCompare().orElse(new BooleanLiteralExpr(true)); + GraphNode cond = connectTo(forStmt, String.format("for (;%s;)", condition)); + + // Body and update expressions + forStmt.getBody().accept(this, arg); + forStmt.getUpdate().forEach(this::connectTo); + + // Condition if body contained anything + hangingNodes.addAll(continueStack.pop()); + hangLabelledContinueStmts(forStmt.getParentNode().orElse(null)); + if ((hangingNodes.size()) != 1 || hangingNodes.get(0) != cond) + connectTo(cond); + else if (!nonExecHangingNodes.isEmpty()) + connectTo(cond); + hangingNodes.addAll(breakStack.pop()); + } + + @Override + public void visit(ForEachStmt forEachStmt, Void arg) { + breakStack.push(new LinkedList<>()); + continueStack.push(new LinkedList<>()); + + GraphNode cond = connectTo(forEachStmt, + String.format("for (%s : %s)", forEachStmt.getVariable(), forEachStmt.getIterable())); + + forEachStmt.getBody().accept(this, arg); + + hangingNodes.addAll(continueStack.pop()); + hangLabelledContinueStmts(forEachStmt.getParentNode().orElse(null)); + if (hangingNodes.size() != 1 || hangingNodes.get(0) != cond) + connectTo(cond); + else if (!nonExecHangingNodes.isEmpty()) + connectTo(cond); + hangingNodes.addAll(breakStack.pop()); + } + + @Override + public void visit(SwitchStmt switchStmt, Void arg) { + // Link previous statement to the switch's selector + switchEntriesStack.push(new LinkedList<>()); + breakStack.push(new LinkedList<>()); + GraphNode cond = connectTo(switchStmt, String.format("switch (%s)", switchStmt.getSelector())); + // expr --> each case (fallthrough by default, so case --> case too) + for (SwitchEntryStmt entry : switchStmt.getEntries()) { + entry.accept(this, arg); // expr && prev case --> case --> next case + hangingNodes.add(cond); // expr --> next case + } + // The next statement will be linked to: + // 1. All break statements that broke from the switch (done with break section) + // 2. If the switch doesn't have a default statement, the switch's selector (already present) + // 3. If the last entry doesn't break, to the last statement (present already) + // If the last case is a default case, remove the selector node from the list of nodes (see 2) + if (ASTUtils.switchHasDefaultCase(switchStmt)) + hangingNodes.remove(cond); + List> entries = switchEntriesStack.pop(); + GraphNode def = null; + for (GraphNode entry : entries) { + if (!entry.getAstNode().getLabel().isPresent()) { + def = entry; + break; + } + } + if (def != null) { + List> aux = new LinkedList<>(hangingNodes); + List> aux2 = new LinkedList<>(nonExecHangingNodes); + clearHanging(); + entries.remove(def); + nonExecHangingNodes.addAll(entries); + connectTo(def); + clearHanging(); + hangingNodes.addAll(aux); + nonExecHangingNodes.add(def); + nonExecHangingNodes.addAll(aux2); + } else { + nonExecHangingNodes.addAll(entries); + } + // End block and break section + hangingNodes.addAll(breakStack.pop()); + } + + @Override + public void visit(BreakStmt breakStmt, Void arg) { + GraphNode node = connectTo(breakStmt); + if (breakStmt.getLabel().isPresent()) + breakMap.get(breakStmt.getLabel().get()).add(node); + else + breakStack.peek().add(node); + clearHanging(); + nonExecHangingNodes.add(node); + } + + @Override + public void visit(ContinueStmt continueStmt, Void arg) { + GraphNode node = connectTo(continueStmt); + if (continueStmt.getLabel().isPresent()) + continueMap.get(continueStmt.getLabel().get()).add(node); + else + continueStack.peek().add(node); + clearHanging(); + nonExecHangingNodes.add(node); + } + + @Override + public void visit(ReturnStmt returnStmt, Void arg) { + GraphNode node = connectTo(returnStmt); + returnList.add(node); + clearHanging(); + nonExecHangingNodes.add(node); + } + + @Override + public void visit(MethodDeclaration methodDeclaration, Void arg) { + if (graph.getRootNode().isPresent()) + throw new IllegalStateException("CFG is only allowed for one method, not multiple!"); + if (!methodDeclaration.getBody().isPresent()) + throw new IllegalStateException("The method must have a body!"); + + graph.buildRootNode("ENTER " + methodDeclaration.getNameAsString(), methodDeclaration); + + hangingNodes.add(graph.getRootNode().get()); + methodDeclaration.getBody().get().accept(this, arg); + returnList.stream().filter(node -> !hangingNodes.contains(node)).forEach(hangingNodes::add); + nonExecHangingNodes.add(graph.getRootNode().get()); + connectTo(new EmptyStmt(), "Exit"); + } +} diff --git a/src/main/java/tfm/graphs/augmented/APDG.java b/src/main/java/tfm/graphs/augmented/APDG.java new file mode 100644 index 0000000000000000000000000000000000000000..98074189526673f569f85c8ec7ab3389a887a3d6 --- /dev/null +++ b/src/main/java/tfm/graphs/augmented/APDG.java @@ -0,0 +1,13 @@ +package tfm.graphs.augmented; + +import tfm.graphs.pdg.PDG; + +public class APDG extends PDG { + public APDG() { + this(new ACFG()); + } + + public APDG(ACFG acfg) { + super(acfg); + } +} diff --git a/src/main/java/tfm/graphs/augmented/PPDG.java b/src/main/java/tfm/graphs/augmented/PPDG.java new file mode 100644 index 0000000000000000000000000000000000000000..c864533e2a48e947e1fb45cf8c432284b26264a1 --- /dev/null +++ b/src/main/java/tfm/graphs/augmented/PPDG.java @@ -0,0 +1,41 @@ +package tfm.graphs.augmented; + +import tfm.arcs.Arc; +import tfm.nodes.GraphNode; +import tfm.slicing.Slice; +import tfm.utils.ASTUtils; + +public class PPDG extends APDG { + public PPDG() { + this(new ACFG()); + } + + public PPDG(ACFG acfg) { + super(acfg); + } + + @Override + protected void getSliceNodes(Slice slice, GraphNode node) { + slice.add(node); + + for (Arc arc : incomingEdgesOf(node)) { + GraphNode from = getEdgeSource(arc); + if (slice.contains(from)) + continue; + getSliceNodesPPDG(slice, from); + } + } + + protected void getSliceNodesPPDG(Slice slice, GraphNode node) { + slice.add(node); + if (ASTUtils.isPseudoPredicate(node.getAstNode())) + return; + + for (Arc arc : incomingEdgesOf(node)) { + GraphNode from = getEdgeSource(arc); + if (slice.contains(from)) + continue; + getSliceNodesPPDG(slice, from); + } + } +} diff --git a/src/main/java/tfm/graphs/cfg/CFG.java b/src/main/java/tfm/graphs/cfg/CFG.java new file mode 100644 index 0000000000000000000000000000000000000000..5d15f19af0c31e7617a2bea744e066c48c41a614 --- /dev/null +++ b/src/main/java/tfm/graphs/cfg/CFG.java @@ -0,0 +1,79 @@ +package tfm.graphs.cfg; + +import com.github.javaparser.ast.body.MethodDeclaration; +import tfm.arcs.Arc; +import tfm.arcs.cfg.ControlFlowArc; +import tfm.graphs.GraphWithRootNode; +import tfm.nodes.GraphNode; +import tfm.utils.NodeNotFoundException; + +import java.util.HashSet; +import java.util.Objects; +import java.util.Set; + +/** + * The Control Flow Graph represents the statements of a method in + * a graph, displaying the connections between each statement and the ones that + * may follow it. You can build one manually or use the {@link CFGBuilder CFGBuilder}. + * The variations of the CFG are implemented as child classes. + * @see ControlFlowArc + */ +public class CFG extends GraphWithRootNode { + private boolean built = false; + + public CFG() { + super(); + } + + public void addControlFlowEdge(GraphNode from, GraphNode to) { + addControlFlowEdge(from, to, new ControlFlowArc()); + } + + protected void addControlFlowEdge(GraphNode from, GraphNode to, ControlFlowArc arc) { + super.addEdge(from, to, arc); + } + + 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)) { + if (!arc.isExecutableControlFlowArc()) + continue; + GraphNode from = getEdgeSource(arc); + + 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; + } + + @Override + public void build(MethodDeclaration method) { + method.accept(newCFGBuilder(), null); + built = true; + } + + @Override + public boolean isBuilt() { + return built; + } + + protected CFGBuilder newCFGBuilder() { + return new CFGBuilder(this); + } +} diff --git a/src/main/java/tfm/graphs/cfg/CFGBuilder.java b/src/main/java/tfm/graphs/cfg/CFGBuilder.java new file mode 100644 index 0000000000000000000000000000000000000000..56f099620d683c16c000eb279b707e1c24bd2d46 --- /dev/null +++ b/src/main/java/tfm/graphs/cfg/CFGBuilder.java @@ -0,0 +1,280 @@ +package tfm.graphs.cfg; + +import com.github.javaparser.ast.Node; +import com.github.javaparser.ast.body.MethodDeclaration; +import com.github.javaparser.ast.expr.BooleanLiteralExpr; +import com.github.javaparser.ast.expr.Expression; +import com.github.javaparser.ast.expr.SimpleName; +import com.github.javaparser.ast.stmt.*; +import com.github.javaparser.ast.visitor.VoidVisitor; +import com.github.javaparser.ast.visitor.VoidVisitorAdapter; +import tfm.nodes.GraphNode; +import tfm.utils.ASTUtils; + +import java.util.*; + +/** + * Populates a {@link CFG}, given one and an AST root node. + * For now it only accepts {@link MethodDeclaration} as roots, as it disallows + * multiple methods. + *
+ * Usage: + *
    + *
  1. Create a new {@link CFG}.
  2. + *
  3. Create a new {@link CFGBuilder}, passing the graph as argument.
  4. + *
  5. Accept the builder as a visitor of the {@link MethodDeclaration} you + * want to analyse using {@link Node#accept(VoidVisitor, Object)}: {@code methodDecl.accept(builder, null)}
  6. + *
  7. Once the previous step is finished, the complete CFG is saved in + * the object created in the first step. The builder should be discarded + * and not reused.
  8. + *
+ */ +public class CFGBuilder extends VoidVisitorAdapter { + /** Stores the CFG representing the method analyzed. */ + protected final CFG graph; + /** Nodes that haven't yet been connected to another one. + * The next node will be the destination, they are the source. */ + protected final List> hangingNodes = new LinkedList<>(); + /** Stack of break statements collected in various (nestable) breakable blocks. */ + protected final Deque>> breakStack = new LinkedList<>(); + /** Stack of continue statements collected in various (nestable) continuable blocks. */ + protected final Deque>> continueStack = new LinkedList<>(); + /** Lists of labelled break statements, mapped according to their label. */ + protected final Map>> breakMap = new HashMap<>(); + /** Lists of labelled continue statements, mapped according to their label. */ + protected final Map>> continueMap = new HashMap<>(); + /** Return statements that should be connected to the final node, if it is created at the end of the */ + protected final List> returnList = new LinkedList<>(); + /** Stack of lists of hanging cases on switch statements */ + protected final Deque>> switchEntriesStack = new LinkedList<>(); + + protected CFGBuilder(CFG graph) { + this.graph = graph; + } + + protected GraphNode connectTo(T n) { + return connectTo(n, n.toString()); + } + + protected GraphNode connectTo(T n, String text) { + GraphNode dest = graph.addNode(text, n); + connectTo(dest); + return dest; + } + + protected void connectTo(GraphNode node) { + for (GraphNode src : hangingNodes) + graph.addControlFlowEdge(src, node); + clearHanging(); + hangingNodes.add(node); + } + + protected void clearHanging() { + hangingNodes.clear(); + } + + @Override + public void visit(ExpressionStmt expressionStmt, Void arg) { + connectTo(expressionStmt); + } + + @Override + public void visit(IfStmt ifStmt, Void arg) { + // *if* -> {then else} -> after + GraphNode cond = connectTo(ifStmt, String.format("if (%s)", ifStmt.getCondition())); + + // if -> {*then* else} -> after + ifStmt.getThenStmt().accept(this, arg); + List> hangingThenNodes = new LinkedList<>(hangingNodes); + + if (ifStmt.getElseStmt().isPresent()) { + // if -> {then *else*} -> after + clearHanging(); + hangingNodes.add(cond); + ifStmt.getElseStmt().get().accept(this, arg); + hangingNodes.addAll(hangingThenNodes); + } else { + // if -> {then **} -> after + hangingNodes.add(cond); + } + // if -> {then else} -> *after* + } + + @Override + public void visit(LabeledStmt n, Void arg) { + breakMap.put(n.getLabel(), new LinkedList<>()); + continueMap.put(n.getLabel(), new LinkedList<>()); + super.visit(n, arg); + hangingNodes.addAll(breakMap.remove(n.getLabel())); + // Remove the label from the continue map; the list should have been emptied + // in the corresponding loop. + if (!continueMap.remove(n.getLabel()).isEmpty()) + throw new IllegalStateException("Labeled loop has not cleared its list of continue statements!"); + } + + protected void hangLabelledContinueStmts(Node loopParent) { + if (loopParent instanceof LabeledStmt) { + SimpleName label = ((LabeledStmt) loopParent).getLabel(); + if (continueMap.containsKey(label)) { + List> list = continueMap.get(label); + hangingNodes.addAll(list); + list.clear(); + } + } + } + + @Override + public void visit(WhileStmt whileStmt, Void arg) { + GraphNode cond = connectTo(whileStmt, String.format("while (%s)", whileStmt.getCondition())); + breakStack.push(new LinkedList<>()); + continueStack.push(new LinkedList<>()); + + whileStmt.getBody().accept(this, arg); + + hangingNodes.addAll(continueStack.pop()); + hangLabelledContinueStmts(whileStmt.getParentNode().orElse(null)); + // Loop contains anything + if (hangingNodes.size() != 1 || hangingNodes.get(0) != cond) + connectTo(cond); + hangingNodes.addAll(breakStack.pop()); + } + + @Override + public void visit(DoStmt doStmt, Void arg) { + breakStack.push(new LinkedList<>()); + continueStack.push(new LinkedList<>()); + + GraphNode cond = connectTo(doStmt, String.format("while (%s)", doStmt.getCondition())); + + doStmt.getBody().accept(this, arg); + + hangingNodes.addAll(continueStack.pop()); + hangLabelledContinueStmts(doStmt.getParentNode().orElse(null)); + // Loop contains anything + if (hangingNodes.size() != 1 || hangingNodes.get(0) != cond) + connectTo(cond); + hangingNodes.addAll(breakStack.pop()); + } + + @Override + public void visit(ForStmt forStmt, Void arg) { + breakStack.push(new LinkedList<>()); + continueStack.push(new LinkedList<>()); + + // Initialization + forStmt.getInitialization().forEach(this::connectTo); + + // Condition + Expression condition = forStmt.getCompare().orElse(new BooleanLiteralExpr(true)); + GraphNode cond = connectTo(forStmt, String.format("for (;%s;)", condition)); + + // Body and update expressions + forStmt.getBody().accept(this, arg); + forStmt.getUpdate().forEach(this::connectTo); + + // Condition if body contained anything + hangingNodes.addAll(continueStack.pop()); + hangLabelledContinueStmts(forStmt.getParentNode().orElse(null)); + if ((hangingNodes.size()) != 1 || hangingNodes.get(0) != cond) + connectTo(cond); + hangingNodes.addAll(breakStack.pop()); + } + + @Override + public void visit(ForEachStmt forEachStmt, Void arg) { + breakStack.push(new LinkedList<>()); + continueStack.push(new LinkedList<>()); + + GraphNode cond = connectTo(forEachStmt, + String.format("for (%s : %s)", forEachStmt.getVariable(), forEachStmt.getIterable())); + + forEachStmt.getBody().accept(this, arg); + + hangingNodes.addAll(continueStack.pop()); + hangLabelledContinueStmts(forEachStmt.getParentNode().orElse(null)); + if (hangingNodes.size() != 1 || hangingNodes.get(0) != cond) + connectTo(cond); + hangingNodes.addAll(breakStack.pop()); + } + + /** Switch entry, considered part of the condition of the switch. */ + @Override + public void visit(SwitchEntryStmt entryStmt, Void arg) { + // Case header (prev -> case EXPR) + GraphNode node; + if (entryStmt.getLabel().isPresent()) { + node = connectTo(entryStmt, "case " + entryStmt.getLabel().get()); + } else { + node = connectTo(entryStmt, "default"); + } + switchEntriesStack.peek().add(node); + // Case body (case EXPR --> body) + entryStmt.getStatements().accept(this, arg); + // body --> next + } + + @Override + public void visit(SwitchStmt switchStmt, Void arg) { + // Link previous statement to the switch's selector + switchEntriesStack.push(new LinkedList<>()); + breakStack.push(new LinkedList<>()); + GraphNode cond = connectTo(switchStmt, String.format("switch (%s)", switchStmt.getSelector())); + // expr --> each case (fallthrough by default, so case --> case too) + for (SwitchEntryStmt entry : switchStmt.getEntries()) { + entry.accept(this, arg); // expr && prev case --> case --> next case + hangingNodes.add(cond); // expr --> next case + } + // The next statement will be linked to: + // 1. All break statements that broke from the switch (done with break section) + // 2. If the switch doesn't have a default statement, the switch's selector (already present) + // 3. If the last entry doesn't break, to the last statement (present already) + // If the last case is a default case, remove the selector node from the list of nodes (see 2) + if (ASTUtils.switchHasDefaultCase(switchStmt)) + hangingNodes.remove(cond); + List> entries = switchEntriesStack.pop(); + // End block and break section + hangingNodes.addAll(breakStack.pop()); + } + + @Override + public void visit(BreakStmt breakStmt, Void arg) { + GraphNode node = connectTo(breakStmt); + if (breakStmt.getLabel().isPresent()) + breakMap.get(breakStmt.getLabel().get()).add(node); + else + breakStack.peek().add(node); + clearHanging(); + } + + @Override + public void visit(ContinueStmt continueStmt, Void arg) { + GraphNode node = connectTo(continueStmt); + if (continueStmt.getLabel().isPresent()) + continueMap.get(continueStmt.getLabel().get()).add(node); + else + continueStack.peek().add(node); + clearHanging(); + } + + @Override + public void visit(ReturnStmt returnStmt, Void arg) { + GraphNode node = connectTo(returnStmt); + returnList.add(node); + clearHanging(); + } + + @Override + public void visit(MethodDeclaration methodDeclaration, Void arg) { + if (graph.getRootNode().isPresent()) + throw new IllegalStateException("CFG is only allowed for one method, not multiple!"); + if (!methodDeclaration.getBody().isPresent()) + throw new IllegalStateException("The method must have a body!"); + + graph.buildRootNode("ENTER " + methodDeclaration.getNameAsString(), methodDeclaration); + + hangingNodes.add(graph.getRootNode().get()); + methodDeclaration.getBody().get().accept(this, arg); + returnList.stream().filter(node -> !hangingNodes.contains(node)).forEach(hangingNodes::add); + connectTo(new EmptyStmt(), "Exit"); + } +} diff --git a/src/main/java/tfm/graphs/pdg/ControlDependencyBuilder.java b/src/main/java/tfm/graphs/pdg/ControlDependencyBuilder.java new file mode 100644 index 0000000000000000000000000000000000000000..29fbe201c9fbcee7a1735c8c58db16de57f04fe9 --- /dev/null +++ b/src/main/java/tfm/graphs/pdg/ControlDependencyBuilder.java @@ -0,0 +1,114 @@ +package tfm.graphs.pdg; + +import tfm.arcs.Arc; +import tfm.graphs.augmented.PPDG; +import tfm.graphs.cfg.CFG; +import tfm.nodes.GraphNode; +import tfm.nodes.NodeFactory; + +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; +import java.util.stream.Collectors; + +/** + * A simple but slow finder of control dependencies. + *
+ * It has a polynomial complexity (between cubed and n**4) with respect to the number of nodes in the CFG. + * It uses the following definition of control dependence: + *
+ * A node b is control dependent on another node a if and only if b postdominates + * one but not all of the successors of a. + *
+ * A node b postdominates another node a if and only if b appears in every path + * from a to the "Exit" node. + *
+ * There exist better, cheaper approaches that have linear complexity w.r.t. the number of edges in the CFG. + * Usage: pass an empty {@link PDG} and a filled {@link CFG} and then run {@link #analyze()}. + * This builder should only be used once, and then discarded. + */ +class ControlDependencyBuilder { + private final PDG pdg; + private final CFG cfg; + + public ControlDependencyBuilder(PDG pdg, CFG cfg) { + this.pdg = pdg; + this.cfg = cfg; + } + + public void analyze() { + Map, GraphNode> nodeMap = new HashMap<>(); + assert cfg.getRootNode().isPresent(); + assert pdg.getRootNode().isPresent(); + nodeMap.put(cfg.getRootNode().get(), pdg.getRootNode().get()); + Set> roots = new HashSet<>(cfg.vertexSet()); + roots.remove(cfg.getRootNode().get()); + Set> cfgNodes = new HashSet<>(cfg.vertexSet()); + cfgNodes.removeIf(node -> node.getInstruction().equals("Exit")); + + for (GraphNode node : cfgNodes) + registerNode(node, nodeMap); + + for (GraphNode src : cfgNodes) { + for (GraphNode dest : cfgNodes) { + if (src == dest) continue; + if (hasControlDependence(src, dest)) { + pdg.addControlDependencyArc(nodeMap.get(src), nodeMap.get(dest)); + roots.remove(dest); + } + } + } + // In the original definition, nodes were dependent by default on the Enter/Start node + for (GraphNode node : roots) + if (!node.getInstruction().equals("Exit")) + pdg.addControlDependencyArc(pdg.getRootNode().get(), nodeMap.get(node)); + } + + public void registerNode(GraphNode node, Map, GraphNode> nodeMap) { + if (nodeMap.containsKey(node) || node.getInstruction().equals("Exit")) + return; + GraphNode clone = NodeFactory.graphNode(node.getId(), node.getInstruction(), node.getAstNode()); + nodeMap.put(node, clone); + pdg.addVertex(clone); + } + + public boolean hasControlDependence(GraphNode a, GraphNode b) { + int yes = 0; + Set list = cfg.outgoingEdgesOf(a); + // Nodes with less than 1 outgoing arc cannot control another node. + if (cfg.outDegreeOf(a) < 2) + return false; + for (Arc arc : cfg.outgoingEdgesOf(a)) { + GraphNode successor = cfg.getEdgeTarget(arc); + if (postdominates(successor, b)) + yes++; + } + int no = list.size() - yes; + return yes > 0 && no > 0; + } + + public boolean postdominates(GraphNode a, GraphNode b) { + return postdominates(a, b, new HashSet<>()); + } + + private boolean postdominates(GraphNode a, GraphNode b, Set> visited) { + // Stop w/ success if a == b or a has already been visited + if (a.equals(b) || visited.contains(a)) + return true; + Set outgoing = cfg.outgoingEdgesOf(a); + // Limit the traversal if it is a PPDG + if (pdg instanceof PPDG) + outgoing = outgoing.stream().filter(Arc::isExecutableControlFlowArc).collect(Collectors.toSet()); + // Stop w/ failure if there are no edges to traverse from a + if (outgoing.isEmpty()) + return false; + // Find all possible paths starting from a, if ALL find b, then true, else false + visited.add(a); + for (Arc out : outgoing) { + if (!postdominates(cfg.getEdgeTarget(out), b, visited)) + return false; + } + return true; + } +} diff --git a/src/main/java/tfm/visitors/pdg/DataDependencyBuilder.java b/src/main/java/tfm/graphs/pdg/DataDependencyBuilder.java similarity index 77% rename from src/main/java/tfm/visitors/pdg/DataDependencyBuilder.java rename to src/main/java/tfm/graphs/pdg/DataDependencyBuilder.java index d7bd5f14746e4e0da30818d10c364be4abbf6088..7972730e5750a1dce273148890304d8fe995552c 100644 --- a/src/main/java/tfm/visitors/pdg/DataDependencyBuilder.java +++ b/src/main/java/tfm/graphs/pdg/DataDependencyBuilder.java @@ -1,23 +1,23 @@ -package tfm.visitors.pdg; +package tfm.graphs.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.CFG; +import tfm.graphs.pdg.PDG; import tfm.nodes.GraphNode; import tfm.variables.VariableExtractor; import java.util.Optional; import java.util.Set; -public class DataDependencyBuilder extends VoidVisitorAdapter { +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/graphs/pdg/PDG.java b/src/main/java/tfm/graphs/pdg/PDG.java new file mode 100644 index 0000000000000000000000000000000000000000..df79831ebd6abbbe7f49b44e1409f10f78c59b12 --- /dev/null +++ b/src/main/java/tfm/graphs/pdg/PDG.java @@ -0,0 +1,80 @@ +package tfm.graphs.pdg; + +import com.github.javaparser.ast.body.MethodDeclaration; +import tfm.arcs.Arc; +import tfm.arcs.pdg.ControlDependencyArc; +import tfm.arcs.pdg.DataDependencyArc; +import tfm.graphs.GraphWithRootNode; +import tfm.graphs.Sliceable; +import tfm.graphs.cfg.CFG; +import tfm.nodes.GraphNode; +import tfm.slicing.Slice; +import tfm.slicing.SlicingCriterion; +import tfm.utils.NodeNotFoundException; + +import java.util.Optional; + +/** + * The Program Dependence Graph represents the statements of a method in + * a graph, connecting statements according to their {@link ControlDependencyArc control} + * and {@link DataDependencyArc data} relationships. You can build one manually or use + * the {@link PDGBuilder PDGBuilder}. + * The variations of the PDG are represented as child types. + */ +public class PDG extends GraphWithRootNode implements Sliceable { + private boolean built = false; + private CFG cfg; + + public PDG() { + this(new CFG()); + } + + 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)); + } + + @Override + public Slice slice(SlicingCriterion slicingCriterion) { + Optional> node = slicingCriterion.findNode(this); + if (!node.isPresent()) + throw new NodeNotFoundException(slicingCriterion); + Slice slice = new Slice(); + getSliceNodes(slice, node.get()); + return slice; + } + + protected void getSliceNodes(Slice slice, GraphNode node) { + slice.add(node); + + for (Arc arc : incomingEdgesOf(node)) { + GraphNode from = getEdgeSource(arc); + if (slice.contains(from)) + continue; + getSliceNodes(slice, from); + } + } + + public CFG getCfg() { + return cfg; + } + + @Override + public void build(MethodDeclaration method) { + new PDGBuilder(this).createFrom(method); + built = true; + } + + @Override + public boolean isBuilt() { + return built; + } +} diff --git a/src/main/java/tfm/graphs/pdg/PDGBuilder.java b/src/main/java/tfm/graphs/pdg/PDGBuilder.java new file mode 100644 index 0000000000000000000000000000000000000000..d699bc59fcef2e5d5bb16d3b96b09d07513e0766 --- /dev/null +++ b/src/main/java/tfm/graphs/pdg/PDGBuilder.java @@ -0,0 +1,56 @@ +package tfm.graphs.pdg; + +import com.github.javaparser.ast.body.MethodDeclaration; +import com.github.javaparser.ast.stmt.BlockStmt; +import tfm.graphs.cfg.CFG; + +/** + * Populates a {@link PDG}, given a complete {@link CFG}, an empty {@link PDG} and an AST root node. + * For now it only accepts {@link MethodDeclaration} as root, as it can only receive a single CFG. + *
+ * Usage: + *
    + *
  1. Create an empty {@link CFG}.
  2. + *
  3. Create an empty {@link PDG} (optionally passing the {@link CFG} as argument).
  4. + *
  5. Create a new {@link PDGBuilder}, passing both graphs as arguments.
  6. + *
  7. Accept the builder as a visitor of the {@link MethodDeclaration} you want to analyse using + * {@link com.github.javaparser.ast.Node#accept(com.github.javaparser.ast.visitor.VoidVisitor, Object) Node#accept(VoidVisitor, Object)}: + * {@code methodDecl.accept(builder, null)}
  8. + *
  9. Once the previous step is finished, the complete PDG is saved in + * the object created in the second step. The builder should be discarded + * and not reused.
  10. + *
+ */ +public class PDGBuilder { + private PDG pdg; + private CFG cfg; + + protected PDGBuilder(PDG pdg) { + assert pdg.getCfg() != null; + this.pdg = pdg; + this.cfg = pdg.getCfg(); + } + + public void createFrom(MethodDeclaration methodDeclaration) { + if (!methodDeclaration.getBody().isPresent()) + throw new IllegalStateException("Method needs to have a body"); + + this.pdg.buildRootNode("ENTER " + methodDeclaration.getNameAsString(), methodDeclaration); + + assert this.pdg.getRootNode().isPresent(); + + BlockStmt methodBody = methodDeclaration.getBody().get(); + + // build CFG + if (!cfg.isBuilt()) + cfg.build(methodDeclaration); + + // Build control dependency + ControlDependencyBuilder controlDependencyBuilder = new ControlDependencyBuilder(pdg, cfg); + controlDependencyBuilder.analyze(); + + // Build data dependency + DataDependencyBuilder dataDependencyBuilder = new DataDependencyBuilder(pdg, cfg); + methodBody.accept(dataDependencyBuilder, null); + } +} diff --git a/src/main/java/tfm/visitors/sdg/MethodCallReplacer.java b/src/main/java/tfm/graphs/sdg/MethodCallReplacer.java similarity index 50% rename from src/main/java/tfm/visitors/sdg/MethodCallReplacer.java rename to src/main/java/tfm/graphs/sdg/MethodCallReplacer.java index 01b853f0479b902a56ee8346609b962eafeeeff1..1dc5bb06ab6d369cf70868d60e3df218d6c87998 100644 --- a/src/main/java/tfm/visitors/sdg/MethodCallReplacer.java +++ b/src/main/java/tfm/graphs/sdg/MethodCallReplacer.java @@ -1,20 +1,15 @@ -package tfm.visitors.sdg; +package tfm.graphs.sdg; -import com.github.javaparser.ast.body.MethodDeclaration; -import tfm.graphs.PDGGraph; -import tfm.graphs.SDGGraph; -import tfm.utils.Context; +class MethodCallReplacer { -public class MethodCallReplacer { + private SDG sdg; - private SDGGraph sdgGraph; - - 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/graphs/sdg/MethodCallReplacerVisitor.java similarity index 93% rename from src/main/java/tfm/visitors/sdg/MethodCallReplacerVisitor.java rename to src/main/java/tfm/graphs/sdg/MethodCallReplacerVisitor.java index eb32ef08806e8c210a84619728e485c12f2cbefe..f48547e63f91b698c3519cc1db7c697470056f40 100644 --- a/src/main/java/tfm/visitors/sdg/MethodCallReplacerVisitor.java +++ b/src/main/java/tfm/graphs/sdg/MethodCallReplacerVisitor.java @@ -1,4 +1,4 @@ -package tfm.visitors.sdg; +package tfm.graphs.sdg; import com.github.javaparser.ast.Node; import com.github.javaparser.ast.body.ClassOrInterfaceDeclaration; @@ -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.PDG; import tfm.nodes.GraphNode; import tfm.utils.Context; import tfm.utils.Logger; @@ -19,12 +19,12 @@ import java.util.Objects; import java.util.Optional; import java.util.stream.Collectors; -public class MethodCallReplacerVisitor extends VoidVisitorAdapter { +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/graphs/sdg/NewSDGBuilder.java similarity index 78% rename from src/main/java/tfm/visitors/sdg/NewSDGBuilder.java rename to src/main/java/tfm/graphs/sdg/NewSDGBuilder.java index 8938632f772391768719d97097c7e9c81dbca4dd..b170a566e691441f60e17ce7356190d3b9fae9d7 100644 --- a/src/main/java/tfm/visitors/sdg/NewSDGBuilder.java +++ b/src/main/java/tfm/graphs/sdg/NewSDGBuilder.java @@ -1,21 +1,18 @@ -package tfm.visitors.sdg; +package tfm.graphs.sdg; 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.PDG; import tfm.utils.Context; -public class NewSDGBuilder extends VoidVisitorAdapter { +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 +24,10 @@ public class NewSDGBuilder extends VoidVisitorAdapter { context.setCurrentMethod(methodDeclaration); // Build PDG and add to SDGGraph - PDGGraph pdgGraph = Graphs.PDG.fromASTNode(methodDeclaration); + PDG pdg = new PDG(); + pdg.build(methodDeclaration); - sdgGraph.addMethod(methodDeclaration, pdgGraph); + sdg.addMethod(methodDeclaration, pdg); } @Override @@ -52,7 +50,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/graphs/SDGGraph.java b/src/main/java/tfm/graphs/sdg/SDG.java similarity index 54% rename from src/main/java/tfm/graphs/SDGGraph.java rename to src/main/java/tfm/graphs/sdg/SDG.java index ec4692bfd16c598ede2d0c624c44e3a771feb733..30f7d36b1f0910e66ad3493caa9292bd2111ee74 100644 --- a/src/main/java/tfm/graphs/SDGGraph.java +++ b/src/main/java/tfm/graphs/sdg/SDG.java @@ -1,44 +1,50 @@ -package tfm.graphs; +package tfm.graphs.sdg; -import com.github.javaparser.ast.Node; +import com.github.javaparser.ast.CompilationUnit; +import com.github.javaparser.ast.NodeList; import com.github.javaparser.ast.body.MethodDeclaration; import com.github.javaparser.ast.body.Parameter; import com.github.javaparser.ast.stmt.EmptyStmt; +import tfm.graphs.Buildable; +import tfm.graphs.Graph; +import tfm.graphs.pdg.PDG; import tfm.nodes.GraphNode; +import tfm.nodes.NodeFactory; +import tfm.slicing.Slice; +import tfm.slicing.Sliceable; 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, Buildable> { + private boolean built = false; + 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 Slice slice(SlicingCriterion slicingCriterion) { + throw new RuntimeException("Slicing not implemented for the SDG"); } @Override - public String toGraphvizRepresentation() { - return contextPDGGraphMap.values().stream() - .map(PDGGraph::toGraphvizRepresentation).collect(Collectors.joining("\n")); + public void build(NodeList nodeList) { + nodeList.accept(new SDGBuilder(this), null); } @Override - public Graph slice(SlicingCriterion slicingCriterion) { - return this; + public boolean isBuilt() { + return built; } - public Map getContextPDGGraphMap() { + public Map getContextPDGGraphMap() { return contextPDGGraphMap; } @@ -53,18 +59,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 +75,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 +91,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/sdg/SDGBuilder.java b/src/main/java/tfm/graphs/sdg/SDGBuilder.java new file mode 100644 index 0000000000000000000000000000000000000000..6b1fddfa549226940ab74cd88d5d3d72c7b1a9c2 --- /dev/null +++ b/src/main/java/tfm/graphs/sdg/SDGBuilder.java @@ -0,0 +1,157 @@ +package tfm.graphs.sdg; + +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.expr.Expression; +import com.github.javaparser.ast.expr.MethodCallExpr; +import com.github.javaparser.ast.stmt.Statement; +import com.github.javaparser.ast.visitor.VoidVisitorAdapter; +import tfm.graphs.pdg.PDG; + +import java.util.ArrayList; +import java.util.List; + +/** + * 31/8/19 + * Asumimos que procesamos 1 archivo con una o más clases donde el primer método de la primera clase es el main + * + */ +class SDGBuilder extends VoidVisitorAdapter { + + SDG sdg; + List pdgs; + + private ClassOrInterfaceDeclaration currentClass; + private CompilationUnit currentCompilationUnit; + + protected SDGBuilder(SDG sdg) { + this.sdg = sdg; + this.pdgs = new ArrayList<>(); + } + + @Override + public void visit(MethodDeclaration methodDeclaration, Void ignored) { + if (!methodDeclaration.getBody().isPresent()) + return; + + + if (sdg.isEmpty()) { + sdg.addNode("ENTER " + methodDeclaration.getNameAsString(), methodDeclaration); + } else { +// sdgGraph.addMethod(methodDeclaration); + } + + PDG pdg = new PDG(); + + // TODO: this should happen in the PDG's creation, not here. +// PDGBuilder PDGBuilder = new PDGBuilder(pdg) { +// @Override +// public void visit(MethodCallExpr methodCallExpr, Void empty) { +// if (methodCallExpr.getScope().isPresent()) { +// String scopeName = methodCallExpr.getScope().get().toString(); +// +// String currentClassName = currentClass.getNameAsString(); +// +// // Check if it's a static method call of current class +// if (!Objects.equals(scopeName, currentClassName)) { +// +// // Check if 'scopeName' is a variable +// List> declarations = sdg.findDeclarationsOfVariable(scopeName); +// +// if (declarations.isEmpty()) { +// // It is a static method call of another class. We don't do anything +// return; +// } else { +// /* +// It's a variable since it has declarations. We now have to check if the class name +// is the same as the current class (the object is an instance of our class) +// */ +// GraphNode declarationNode = declarations.get(declarations.size() - 1); +// +// ExpressionStmt declarationExpr = (ExpressionStmt) declarationNode.getAstNode(); +// VariableDeclarationExpr variableDeclarationExpr = declarationExpr.getExpression().asVariableDeclarationExpr(); +// +// Optional optionalVariableDeclarator = variableDeclarationExpr.getVariables().stream() +// .filter(variableDeclarator -> Objects.equals(variableDeclarator.getNameAsString(), scopeName)) +// .findFirst(); +// +// if (!optionalVariableDeclarator.isPresent()) { +// // should not happen +// return; +// } +// +// Type variableType = optionalVariableDeclarator.get().getType(); +// +// if (!variableType.isClassOrInterfaceType()) { +// // Not class type +// return; +// } +// +// if (!Objects.equals(variableType.asClassOrInterfaceType().getNameAsString(), currentClassName)) { +// // object is not instance of our class +// return; +// } +// +// // if we got here, the object is instance of our class, so we make the call +// } +// } +// +// // It's a static method call to a method of the current class +// +// } +// } +// }; + +// PDGBuilder.createFrom(methodDeclaration); + + + sdg.addNode(methodDeclaration.getNameAsString(), methodDeclaration); + + pdg.vertexSet().stream().skip(1).forEach(pdgNode -> { + Statement statement = (Statement) pdgNode.getAstNode(); + + if (statement.isExpressionStmt()) { + Expression expression = statement.asExpressionStmt().getExpression(); + + expression.findFirst(MethodCallExpr.class).ifPresent(methodCallExpr -> { + + }); + } else { + + } + }); + + + + + + sdg.addPDG(pdg, methodDeclaration); + + methodDeclaration.accept(this, ignored); + + pdgs.add(pdg); + } + + @Override + public void visit(ClassOrInterfaceDeclaration classOrInterfaceDeclaration, Void ignored) { +// if (sdgGraph.getRootNode() != null) { +// throw new IllegalStateException("¡Solo podemos procesar una clase por el momento!"); +// } + + if (classOrInterfaceDeclaration.isInterface()) { + throw new IllegalArgumentException("¡Las interfaces no estan permitidas!"); + } + + currentClass = classOrInterfaceDeclaration; + + classOrInterfaceDeclaration.accept(this, ignored); + } + + @Override + public void visit(CompilationUnit compilationUnit, Void ignored) { + currentCompilationUnit = compilationUnit; + + super.visit(compilationUnit, ignored); + } +} 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..43c764638550fbda1f5a1d4da24db27cc93b83b6 100644 --- a/src/main/java/tfm/nodes/GraphNode.java +++ b/src/main/java/tfm/nodes/GraphNode.java @@ -2,119 +2,94 @@ 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.graphs.cfg.CFG; +import tfm.graphs.pdg.PDG; +import tfm.graphs.sdg.SDG; import tfm.utils.Utils; import tfm.variables.VariableExtractor; -import java.util.*; -import java.util.stream.Collectors; - -public class GraphNode extends Vertex { - - private int id; - - protected N astNode; - - 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) { +import java.util.Collection; +import java.util.HashSet; +import java.util.Objects; +import java.util.Set; + +/** + * Represents a node in the various graphs ({@link CFG CFG}, + * {@link PDG PDG} and {@link SDG SDG}), + * including its AST representation and the connections it has to other nodes + * in the same graph. It can hold a string of characters that will be used + * to represent it. + *
+ * It is immutable. + * @param The type of the AST represented by this node. + */ +public class GraphNode { + + private final int id; + private final String instruction; + private final N astNode; + + private final Set declaredVariables; + private final Set definedVariables; + private final Set usedVariables; + + 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 +110,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 +134,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/GraphNodeCriterion.java b/src/main/java/tfm/slicing/GraphNodeCriterion.java new file mode 100644 index 0000000000000000000000000000000000000000..927435cdaaca2a0f3e8337626080ca4acb403e12 --- /dev/null +++ b/src/main/java/tfm/slicing/GraphNodeCriterion.java @@ -0,0 +1,32 @@ +package tfm.slicing; + +import tfm.graphs.cfg.CFG; +import tfm.graphs.pdg.PDG; +import tfm.graphs.sdg.SDG; +import tfm.nodes.GraphNode; + +import java.util.Optional; + +public class GraphNodeCriterion extends SlicingCriterion { + private final GraphNode node; + + public GraphNodeCriterion(GraphNode node, String variable) { + super(variable); + this.node = node; + } + + @Override + public Optional> findNode(CFG graph) { + return graph.findNodeById(node.getId()); + } + + @Override + public Optional> findNode(PDG graph) { + return graph.findNodeById(node.getId()); + } + + @Override + public Optional> findNode(SDG graph) { + return graph.findNodeById(node.getId()); + } +} diff --git a/src/main/java/tfm/slicing/LineNumberCriterion.java b/src/main/java/tfm/slicing/LineNumberCriterion.java index 2b0c2462e88a4d2be74343384b5067546e3a56f9..3eb8ead496dc6c3bde450244e5f66f1a7b6807d4 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.CFG; +import tfm.graphs.pdg.PDG; +import tfm.graphs.sdg.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..2a8b8443517c26e5e330e4a22089e0aaf2c9b38d 100644 --- a/src/main/java/tfm/slicing/Slice.java +++ b/src/main/java/tfm/slicing/Slice.java @@ -1,40 +1,65 @@ package tfm.slicing; -import com.github.javaparser.JavaParser; -import com.github.javaparser.ast.CompilationUnit; -import tfm.exec.PDGLog; -import tfm.graphs.PDGGraph; -import tfm.utils.Logger; -import tfm.utils.Utils; -import tfm.validation.PDGValidator; -import tfm.visitors.pdg.PDGBuilder; - -import java.io.File; -import java.io.IOException; +import com.github.javaparser.ast.Node; +import com.github.javaparser.ast.body.MethodDeclaration; +import com.github.javaparser.ast.visitor.CloneVisitor; +import com.github.javaparser.ast.visitor.Visitable; +import tfm.nodes.GraphNode; + +import java.util.*; +import java.util.stream.Collectors; public class Slice { + private final Map> map = new HashMap<>(); + private final Set nodes = new HashSet<>(); - public static final String PROGRAM_FOLDER = Utils.PROGRAMS_FOLDER + "pdg/"; - public static final String PROGRAM_NAME = "Example2"; + public Slice() {} - public static void main(String[] args) throws IOException { - CompilationUnit compilationUnit = JavaParser.parse(new File(PROGRAM_FOLDER + PROGRAM_NAME + ".java")); + public void add(GraphNode node) { + assert !map.containsKey(node.getId()); + map.put(node.getId(), node); + nodes.add(node.getAstNode()); + } - PDGGraph pdgGraph = new PDGGraph(); + public boolean contains(GraphNode node) { + return map.containsKey(node.getId()); + } - compilationUnit.accept(new PDGBuilder(pdgGraph), pdgGraph.getRootNode()); + public boolean contains(Node node) { + return nodes.contains(node); + } - Logger.log("=================="); - Logger.log("= Starting slice ="); - Logger.log("=================="); + @Override + public int hashCode() { + return map.hashCode(); + } - PDGGraph sliced = pdgGraph.slice(new LineNumberCriterion(18, "x")); + @Override + public boolean equals(Object obj) { + return obj instanceof Slice && map.equals(((Slice) obj).map); + } - PDGLog pdgLog = new PDGLog(sliced); - pdgLog.log(); - pdgLog.generateImages(PROGRAM_NAME + "-sliced"); - pdgLog.openVisualRepresentation(); + public Node getAst() { + List> methods = map.values().stream().filter(e -> e.getAstNode() instanceof MethodDeclaration).collect(Collectors.toList()); + if (methods.size() == 1) { + Optional secondNode = map.keySet().stream() + .sorted(Integer::compareTo).skip(1).findFirst(); + assert secondNode.isPresent(); + Node n = map.get(secondNode.get()).getAstNode(); + assert !(n instanceof MethodDeclaration); + while (!(n instanceof MethodDeclaration) && n.getParentNode().isPresent()) + n = n.getParentNode().get(); + assert n instanceof MethodDeclaration; + return getMethodAst(n); + } else if (methods.size() > 1) + throw new RuntimeException("Not implemented"); + throw new RuntimeException("No method found in the slice"); + } - PDGValidator.printPDGProgram("Slice" + PROGRAM_NAME, sliced); + private MethodDeclaration getMethodAst(Node node) { + Visitable clone = node.accept(new CloneVisitor(), null); + assert clone instanceof MethodDeclaration; + clone.accept(new SliceAstVisitor(), this); + return ((MethodDeclaration) clone); } } diff --git a/src/main/java/tfm/slicing/SliceAstVisitor.java b/src/main/java/tfm/slicing/SliceAstVisitor.java new file mode 100644 index 0000000000000000000000000000000000000000..63f187c59d408a94a75f465a7be139a76996c8eb --- /dev/null +++ b/src/main/java/tfm/slicing/SliceAstVisitor.java @@ -0,0 +1,118 @@ +package tfm.slicing; + +import com.github.javaparser.ast.Node; +import com.github.javaparser.ast.NodeList; +import com.github.javaparser.ast.expr.BooleanLiteralExpr; +import com.github.javaparser.ast.nodeTypes.NodeWithBody; +import com.github.javaparser.ast.stmt.*; +import com.github.javaparser.ast.visitor.ModifierVisitor; +import com.github.javaparser.ast.visitor.Visitable; + +import java.util.stream.Collectors; + +public class SliceAstVisitor extends ModifierVisitor { + @Override + public Visitable visit(BreakStmt n, Slice arg) { + return arg.contains(n) ? n : null; + } + + @Override + public Visitable visit(ContinueStmt n, Slice arg) { + return arg.contains(n) ? n : null; + } + + @Override + public Visitable visit(DoStmt n, Slice arg) { + boolean keep = arg.contains(n); + super.visit(n, arg); + fillBody(n); + return keep ? n : null; + } + + @Override + public Visitable visit(ForEachStmt n, Slice arg) { + boolean keep = arg.contains(n); + super.visit(n, arg); + fillBody(n); + return keep ? n : null; + } + + @Override + public Visitable visit(ForStmt n, Slice arg) { + boolean keep = arg.contains(n); + super.visit(n, arg); + n.setInitialization(new NodeList<>(n.getInitialization().stream() + .filter(arg::contains).collect(Collectors.toList()))); + n.setUpdate(new NodeList<>(n.getUpdate().stream() + .filter(arg::contains).collect(Collectors.toList()))); + fillBody(n); + if (keep) + return n; + if (n.getInitialization().isEmpty() && n.getUpdate().isEmpty()) + return null; + return new ForStmt(n.getInitialization(), new BooleanLiteralExpr(false), + n.getUpdate(), n.getBody()); + } + + @Override + public Visitable visit(WhileStmt n, Slice arg) { + boolean keep = arg.contains(n); + super.visit(n, arg); + fillBody(n); + return keep ? n : null; + } + + @Override + public Visitable visit(IfStmt n, Slice arg) { + boolean keep = arg.contains(n); + super.visit(n, arg); + if (n.getThenStmt() == null) + n.setThenStmt(new EmptyStmt()); + return keep ? n : null; + } + + @Override + public Visitable visit(LabeledStmt n, Slice arg) { + super.visit(n, arg); + return n.getStatement() != null ? n : null; + } + + @Override + public Visitable visit(ReturnStmt n, Slice arg) { + return arg.contains(n) ? n : null; + } + + @Override + public Visitable visit(ThrowStmt n, Slice arg) { + return arg.contains(n) ? n : null; + } + + @Override + public Visitable visit(SwitchEntryStmt n, Slice arg) { + boolean keep = arg.contains(n); + super.visit(n, arg); + if (!n.getStatements().isEmpty()) + return n; + return keep ? n : null; + } + + @Override + public Visitable visit(SwitchStmt n, Slice arg) { + boolean keep = arg.contains(n); + super.visit(n, arg); + return keep ? n : null; + } + + @Override + public Visitable visit(ExpressionStmt n, Slice arg) { + return arg.contains(n) ? n : null; + } + + private void fillBody(Node n) { + if (!(n instanceof NodeWithBody)) + return; + NodeWithBody nb = ((NodeWithBody) n); + if (nb.getBody() == null) + nb.setBody(new EmptyStmt()); + } +} diff --git a/src/main/java/tfm/slicing/Sliceable.java b/src/main/java/tfm/slicing/Sliceable.java new file mode 100644 index 0000000000000000000000000000000000000000..c13101e2af3d1b94e66ff69c142e9031e3d70655 --- /dev/null +++ b/src/main/java/tfm/slicing/Sliceable.java @@ -0,0 +1,5 @@ +package tfm.slicing; + +public interface Sliceable { + Slice slice(SlicingCriterion sc); +} diff --git a/src/main/java/tfm/slicing/SlicingCriterion.java b/src/main/java/tfm/slicing/SlicingCriterion.java index 2574071e7c1caaf7dfd213cf666713a5b3d3edf1..d42d7bef31804b1c4767cd4a7e61f00751ad0d69 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.CFG; +import tfm.graphs.pdg.PDG; +import tfm.graphs.sdg.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..e0c9320e855f9c28c5dbec06c1ece05cb9c3bd27 100644 --- a/src/main/java/tfm/utils/ASTUtils.java +++ b/src/main/java/tfm/utils/ASTUtils.java @@ -1,11 +1,8 @@ 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; -import com.github.javaparser.ast.stmt.EmptyStmt; -import com.github.javaparser.ast.stmt.Statement; +import com.github.javaparser.ast.stmt.*; import java.util.Objects; import java.util.Optional; @@ -59,4 +56,25 @@ public class ASTUtils { return Objects.equals(parent, upper) || isContained(upper, parent); } + + public static boolean switchHasDefaultCase(SwitchStmt stmt) { + return switchGetDefaultCase(stmt) != null; + } + + public static SwitchEntryStmt switchGetDefaultCase(SwitchStmt stmt) { + for (SwitchEntryStmt entry : stmt.getEntries()) + if (!entry.getLabel().isPresent()) + return entry; + return null; + } + + public static boolean isPseudoPredicate(Node node) { + return node instanceof BreakStmt + || node instanceof ContinueStmt + || node instanceof ReturnStmt + || node instanceof ThrowStmt + || node instanceof SwitchEntryStmt + || node instanceof TryStmt + || node instanceof CatchClause; + } } diff --git a/src/main/java/tfm/utils/Utils.java b/src/main/java/tfm/utils/Utils.java index 968b994e52995a3b9d341f42c0532caf1543f409..de41fe31a8bccf9885c7660ed61af3b844ba17ef 100644 --- a/src/main/java/tfm/utils/Utils.java +++ b/src/main/java/tfm/utils/Utils.java @@ -7,7 +7,7 @@ import java.util.Set; public class Utils { - public static final String PROGRAMS_FOLDER = "src/main/java/tfm/programs/"; + public static final String PROGRAMS_FOLDER = "src/test/res/programs/"; public static List emptyList() { return new ArrayList<>(0); diff --git a/src/main/java/tfm/validation/PDGValidator.java b/src/main/java/tfm/validation/PDGValidator.java deleted file mode 100644 index 48cb5d81df203242cd45a598ce31b1f04d3d329f..0000000000000000000000000000000000000000 --- a/src/main/java/tfm/validation/PDGValidator.java +++ /dev/null @@ -1,123 +0,0 @@ -package tfm.validation; - -import com.github.javaparser.JavaParser; -import com.github.javaparser.ast.CompilationUnit; -import com.github.javaparser.ast.Modifier; -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.utils.Logger; -import tfm.utils.Utils; -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; - -public class PDGValidator { - - private static final String PROGRAM_FOLDER = Utils.PROGRAMS_FOLDER + "pdg"; - private static final String PROGRAM_NAME = "Example2"; - private static final String METHOD_NAME = "main"; - - public static void main(String[] args) throws FileNotFoundException { - JavaParser.getStaticConfiguration().setAttributeComments(false); - - CompilationUnit originalProgram = JavaParser.parse(new File(String.format("%s/%s.java", PROGRAM_FOLDER, PROGRAM_NAME))); - - if (METHOD_NAME.isEmpty()) { - originalProgram.accept(new VoidVisitorAdapter() { - @Override - public void visit(MethodDeclaration n, Void arg) { - Logger.format("On method: %s. Generating and comparing...", n.getNameAsString()); - boolean check = generateAndCheck(n); - - Logger.format("Result: %s", check ? "equal" : "not equal"); - } - }, null); - } else { - Optional optionalTarget = originalProgram.findFirst(MethodDeclaration.class, - methodDeclaration -> Objects.equals(methodDeclaration.getNameAsString(), METHOD_NAME)); - - if (!optionalTarget.isPresent()) { - throw new RuntimeException(String.format("Method '%s' not found", METHOD_NAME)); - } - - Logger.format("On method: %s. Generating and comparing...", METHOD_NAME); - - boolean check = generateAndCheck(optionalTarget.get()); - - Logger.format("Result: %s", check ? "equal" : "not equal"); - } - } - - public static boolean generateAndCheck(MethodDeclaration methodDeclaration) { - PDGGraph graph = new PDGGraph(); - - methodDeclaration.accept(new PDGBuilder(graph), graph.getRootNode()); - - return check(methodDeclaration, graph); - } - - public static boolean check(MethodDeclaration methodDeclaration, PDGGraph 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; - } - - public static void printPDGProgram(String fileName, PDGGraph graph) throws FileNotFoundException { - CompilationUnit generatedProgram = new CompilationUnit(); - ClassOrInterfaceDeclaration clazz = generatedProgram.addClass(fileName).setPublic(true); - - MethodDeclaration info = new MethodDeclaration(); - - info.setModifiers(Modifier.Keyword.PUBLIC, Modifier.Keyword.STATIC); - info.setType(new VoidType()); - info.setName("main"); - - Parameter parameter = new Parameter( - new ArrayType(JavaParser.parseClassOrInterfaceType("String")), - "args" - ); - info.setParameters(new NodeList<>(parameter)); - - MethodDeclaration generated = generateMethod(info, graph); - - clazz.addMember(generated); - - PrintWriter printWriter = new PrintWriter(new File(String.format("out/%s.java", fileName))); - - printWriter.print(clazz.toString()); - - printWriter.close(); - } -} diff --git a/src/main/java/tfm/validation/ProgramComparator.java b/src/main/java/tfm/validation/ProgramComparator.java deleted file mode 100644 index 2f6c26317010ac8e636101061c8fad6b701cf7f7..0000000000000000000000000000000000000000 --- a/src/main/java/tfm/validation/ProgramComparator.java +++ /dev/null @@ -1,24 +0,0 @@ -package tfm.validation; - -import com.github.javaparser.JavaParser; -import com.github.javaparser.ast.CompilationUnit; -import com.github.javaparser.ast.Node; - -import java.io.File; -import java.io.FileNotFoundException; - -public class ProgramComparator { - - public static boolean areEqual(File one, File two) throws FileNotFoundException { - JavaParser.getStaticConfiguration().setAttributeComments(false); - - CompilationUnit cu1 = JavaParser.parse(one); - CompilationUnit cu2 = JavaParser.parse(two); - - return areEqual(cu1, cu2); - } - - public static boolean areEqual(Node one, Node two) { - return one.equals(two); - } -} diff --git a/src/main/java/tfm/visitors/cfg/CFGBuilder.java b/src/main/java/tfm/visitors/cfg/CFGBuilder.java deleted file mode 100644 index 633aad830fb53252afc64c2c2e8f5f2ab3a67c5a..0000000000000000000000000000000000000000 --- a/src/main/java/tfm/visitors/cfg/CFGBuilder.java +++ /dev/null @@ -1,270 +0,0 @@ -package tfm.visitors.cfg; - -import com.github.javaparser.ast.body.MethodDeclaration; -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.nodes.GraphNode; -import tfm.utils.ASTUtils; - -import java.util.*; - -public class CFGBuilder extends VoidVisitorAdapter { - - private CFGGraph graph; - - private Queue lastParentNodes; - private List bodyBreaks; - - public CFGBuilder(CFGGraph graph) { - this.graph = graph; - this.lastParentNodes = Collections.asLifoQueue( - new ArrayDeque<>( - Collections.singletonList(graph.getRootNode()) - ) - ); - - this.bodyBreaks = new ArrayList<>(); - } - - @Override - public void visit(ExpressionStmt expressionStmt, Void arg) { - String expression = expressionStmt.toString().replace("\"", "\\\""); - - GraphNode nextNode = addNodeAndArcs(expression, 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( - String.format("if (%s)", ifStmt.getCondition().toString()), - ifStmt - ); - - lastParentNodes.add(ifCondition); - - // Visit "then" - ifStmt.getThenStmt().accept(this, arg); - - Queue lastThenNodes = new ArrayDeque<>(lastParentNodes); - - if (ifStmt.hasElseBranch()) { - lastParentNodes.clear(); - lastParentNodes.add(ifCondition); // Set if nodes as root - - ifStmt.getElseStmt().get().accept(this, arg); - - lastParentNodes.addAll(lastThenNodes); - } else { - lastParentNodes.add(ifCondition); - } - } - - @Override - public void visit(WhileStmt whileStmt, Void arg) { - GraphNode whileCondition = addNodeAndArcs( - String.format("while (%s)", whileStmt.getCondition().toString()), - whileStmt - ); - - lastParentNodes.add(whileCondition); - - whileStmt.getBody().accept(this, arg); - - while (!lastParentNodes.isEmpty()) { - graph.addControlFlowEdge(lastParentNodes.poll(), whileCondition); - } - - lastParentNodes.add(whileCondition); - lastParentNodes.addAll(bodyBreaks); - bodyBreaks.clear(); - } - - @Override - public void visit(DoStmt doStmt, Void arg) { - BlockStmt body = ASTUtils.blockWrapper(doStmt.getBody()); - - body.accept(this, arg); - - GraphNode doWhileNode = addNodeAndArcs( - String.format("while (%s)", doStmt.getCondition()), - doStmt - ); - - if (!body.isEmpty()) { - Statement firstBodyStatement = body.getStatement(0); - - graph.findNodeByASTNode(firstBodyStatement) - .ifPresent(node -> graph.addControlFlowEdge(doWhileNode, node)); - } - - lastParentNodes.add(doWhileNode); - lastParentNodes.addAll(bodyBreaks); - bodyBreaks.clear(); - } - - @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( - String.format("for (;%s;)", comparison), - forStmt - ); - - lastParentNodes.add(forNode); - - BlockStmt body = ASTUtils.blockWrapper(forStmt.getBody()).clone(); - - forStmt.getUpdate().forEach(body::addStatement); - - body.accept(this, arg); - - while (!lastParentNodes.isEmpty()) { - graph.addControlFlowEdge(lastParentNodes.poll(), forNode); - } - - lastParentNodes.add(forNode); - lastParentNodes.addAll(bodyBreaks); - bodyBreaks.clear(); - } - - @Override - public void visit(ForEachStmt forEachStmt, Void arg) { - GraphNode foreachNode = addNodeAndArcs( - String.format("for (%s : %s)", forEachStmt.getVariable(), forEachStmt.getIterable()), - forEachStmt - ); - - lastParentNodes.add(foreachNode); - - forEachStmt.getBody().accept(this, arg); - - while (!lastParentNodes.isEmpty()) { - graph.addControlFlowEdge(lastParentNodes.poll(), foreachNode); - } - - lastParentNodes.add(foreachNode); - lastParentNodes.addAll(bodyBreaks); - bodyBreaks.clear(); - } - - @Override - public void visit(SwitchStmt switchStmt, Void arg) { - GraphNode switchNode = addNodeAndArcs( - String.format("switch (%s)", switchStmt.getSelector()), - switchStmt - ); - - lastParentNodes.add(switchNode); - - List allEntryBreaks = 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); - - lastParentNodes.add(switchEntryNode); - lastParentNodes.addAll(lastEntryStatementsWithNoBreak); - lastEntryStatementsWithNoBreak.clear(); - - switchEntryStmt.getStatements().accept(this, null); - - if (!bodyBreaks.isEmpty()) { // means it has break - allEntryBreaks.addAll(bodyBreaks); // save breaks of entry - - lastParentNodes.clear(); - lastParentNodes.add(switchEntryNode); // Set switch as the only parent - - bodyBreaks.clear(); // Clear breaks - } else { - lastEntryStatementsWithNoBreak.addAll(lastParentNodes); - lastParentNodes.clear(); - lastParentNodes.add(switchEntryNode); - } - }); - - lastParentNodes.addAll(allEntryBreaks); - } - - @Override - public void visit(BreakStmt breakStmt, Void arg) { - bodyBreaks.addAll(lastParentNodes); - } - - @Override - public void visit(ContinueStmt continueStmt, Void arg) { - Statement continuableStatement = ASTUtils.findFirstAncestorStatementFrom(continueStmt, ASTUtils::isLoop); - - GraphNode continuableNode = graph.findNodeByASTNode(continuableStatement).get(); - - lastParentNodes.forEach(parentNode -> graph.addControlFlowEdge(parentNode, continuableNode)); - } - - @Override - public void visit(ReturnStmt returnStmt, Void arg) { - GraphNode node = addNodeAndArcs( - returnStmt.toString(), - returnStmt - ); - - lastParentNodes.add(node); - } - - @Override - public void visit(MethodDeclaration methodDeclaration, Void arg) { - if (!lastParentNodes.isEmpty() && Objects.equals(lastParentNodes.peek().getData(), "Stop")) { - throw new IllegalStateException("CFG is only allowed for one method, not multiple!"); - } - - super.visit(methodDeclaration, arg); - - lastParentNodes.add(addNodeAndArcs("Stop", new EmptyStmt())); - } - - private GraphNode addNodeAndArcs(String nodeData, Statement statement) { - GraphNode node = graph.addNode(nodeData, statement); - - GraphNode parent = lastParentNodes.poll(); // ALWAYS exists a parent - graph.addControlFlowEdge(parent, node); - - while (!lastParentNodes.isEmpty()) { - parent = lastParentNodes.poll(); - graph.addControlFlowEdge(parent, node); - } - - return node; - } - - -} diff --git a/src/main/java/tfm/visitors/pdg/ControlDependencyBuilder.java b/src/main/java/tfm/visitors/pdg/ControlDependencyBuilder.java deleted file mode 100644 index 7ceab6685c786ed72b16a599d94af7d1def0faab..0000000000000000000000000000000000000000 --- a/src/main/java/tfm/visitors/pdg/ControlDependencyBuilder.java +++ /dev/null @@ -1,96 +0,0 @@ -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.nodes.GraphNode; - -import java.util.stream.Collectors; - -public class ControlDependencyBuilder extends VoidVisitorAdapter { - - private CFGGraph cfgGraph; - private PDGGraph pdgGraph; - - public ControlDependencyBuilder(PDGGraph pdgGraph, CFGGraph cfgGraph) { - this.pdgGraph = pdgGraph; - this.cfgGraph = cfgGraph; - } - - @Override - public void visit(ExpressionStmt expressionStmt, GraphNode parent) { - addNodeAndControlDependency(expressionStmt, parent); - } - - @Override - public void visit(IfStmt ifStmt, GraphNode parent) { - GraphNode node = addNodeAndControlDependency(ifStmt, parent); - - ifStmt.getThenStmt().accept(this, node); - - ifStmt.getElseStmt().ifPresent(statement -> statement.accept(this, node)); - } - - @Override - public void visit(WhileStmt whileStmt, GraphNode parent) { - GraphNode node = addNodeAndControlDependency(whileStmt, parent); - - whileStmt.getBody().accept(this, node); - } - - @Override - public void visit(ForStmt forStmt, GraphNode parent) { - String initialization = forStmt.getInitialization().stream() - .map(com.github.javaparser.ast.Node::toString) - .collect(Collectors.joining(",")); - - String update = forStmt.getUpdate().stream() - .map(com.github.javaparser.ast.Node::toString) - .collect(Collectors.joining(",")); - - String compare = forStmt.getCompare() - .map(com.github.javaparser.ast.Node::toString) - .orElse("true"); - - - GraphNode forNode = pdgGraph.addNode( - String.format("for (%s;%s;%s)", initialization, compare, update), - forStmt - ); - - pdgGraph.addControlDependencyArc(parent, forNode); - - forStmt.getBody().accept(this, forNode); - } - - @Override - public void visit(ForEachStmt forEachStmt, GraphNode parent) { - GraphNode node = addNodeAndControlDependency(forEachStmt, parent); - - forEachStmt.getBody().accept(this, node); - } - - @Override - public void visit(SwitchStmt switchStmt, GraphNode parent) { - GraphNode node = addNodeAndControlDependency(switchStmt, parent); - - switchStmt.getEntries().accept(this, node); - } - - @Override - public void visit(SwitchEntryStmt switchEntryStmt, GraphNode 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); - - return node; - } -} diff --git a/src/main/java/tfm/visitors/pdg/PDGBuilder.java b/src/main/java/tfm/visitors/pdg/PDGBuilder.java deleted file mode 100644 index 37aff8c5e66232bcb110f3f9bc557e8c823f041e..0000000000000000000000000000000000000000 --- a/src/main/java/tfm/visitors/pdg/PDGBuilder.java +++ /dev/null @@ -1,53 +0,0 @@ -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.visitors.cfg.CFGBuilder; - -public class PDGBuilder extends VoidVisitorAdapter> { - - private PDGGraph pdgGraph; - private CFGGraph cfgGraph; - - public PDGBuilder(PDGGraph pdgGraph) { - this(pdgGraph, new CFGGraph() { - @Override - protected String getRootNodeData() { - return "Start"; - } - }); - } - - public PDGBuilder(PDGGraph pdgGraph, CFGGraph cfgGraph) { - this.pdgGraph = pdgGraph; - this.cfgGraph = cfgGraph; - - this.pdgGraph.setCfgGraph(cfgGraph); - } - - public void visit(MethodDeclaration methodDeclaration, GraphNode parent) { - if (!methodDeclaration.getBody().isPresent()) - return; - - // Assign the method declaration to the root node of the PDG graph - this.pdgGraph.getRootNode().setAstNode(methodDeclaration); - - BlockStmt methodBody = methodDeclaration.getBody().get(); - - // build CFG - methodBody.accept(new CFGBuilder(cfgGraph), null); - - // Build control dependency - ControlDependencyBuilder controlDependencyBuilder = new ControlDependencyBuilder(pdgGraph, cfgGraph); - methodBody.accept(controlDependencyBuilder, parent); - - // Build data dependency - DataDependencyBuilder dataDependencyBuilder = new DataDependencyBuilder(pdgGraph, cfgGraph); - 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/MethodDeclarationReplacer.java b/src/main/java/tfm/visitors/sdg/MethodDeclarationReplacer.java deleted file mode 100644 index 30542f1658f57ada0dbd53ada88abe7f44363710..0000000000000000000000000000000000000000 --- a/src/main/java/tfm/visitors/sdg/MethodDeclarationReplacer.java +++ /dev/null @@ -1,4 +0,0 @@ -package tfm.visitors.sdg; - -public class MethodDeclarationReplacer { -} diff --git a/src/main/java/tfm/visitors/sdg/SDGBuilder.java b/src/main/java/tfm/visitors/sdg/SDGBuilder.java deleted file mode 100644 index 9a5118652e06cc3ec0695d63a6f73d922c3571ce..0000000000000000000000000000000000000000 --- a/src/main/java/tfm/visitors/sdg/SDGBuilder.java +++ /dev/null @@ -1,171 +0,0 @@ -package tfm.visitors.sdg; - -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.body.VariableDeclarator; -import com.github.javaparser.ast.expr.Expression; -import com.github.javaparser.ast.expr.MethodCallExpr; -import com.github.javaparser.ast.expr.VariableDeclarationExpr; -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.nodes.GraphNode; -import tfm.visitors.pdg.PDGBuilder; - -import java.util.ArrayList; -import java.util.List; -import java.util.Objects; -import java.util.Optional; - -/** - * 31/8/19 - * Asumimos que procesamos 1 archivo con una o más clases donde el primer método de la primera clase es el main - * - */ -public class SDGBuilder extends VoidVisitorAdapter { - - SDGGraph sdgGraph; - List pdgGraphs; - - private ClassOrInterfaceDeclaration currentClass; - private CompilationUnit currentCompilationUnit; - - public SDGBuilder(SDGGraph sdgGraph) { - this.sdgGraph = sdgGraph; - this.pdgGraphs = new ArrayList<>(); - } - - @Override - public void visit(MethodDeclaration methodDeclaration, Void ignored) { - if (!methodDeclaration.getBody().isPresent()) - return; - - - if (sdgGraph.isEmpty()) { - sdgGraph.setRootVertex( - new GraphNode<>( - 0, - "ENTER " + methodDeclaration.getNameAsString(), - methodDeclaration - ) - ); - } else { -// sdgGraph.addMethod(methodDeclaration); - } - - PDGGraph pdgGraph = new PDGGraph(); - - PDGBuilder PDGBuilder = new PDGBuilder(pdgGraph) { - @Override - public void visit(MethodCallExpr methodCallExpr, GraphNode parent) { - if (methodCallExpr.getScope().isPresent()) { - String scopeName = methodCallExpr.getScope().get().toString(); - - String currentClassName = currentClass.getNameAsString(); - - // Check if it's a static method call of current class - if (!Objects.equals(scopeName, currentClassName)) { - - // Check if 'scopeName' is a variable - List> declarations = sdgGraph.findDeclarationsOfVariable(scopeName); - - if (declarations.isEmpty()) { - // It is a static method call of another class. We don't do anything - return; - } else { - /* - It's a variable since it has declarations. We now have to check if the class name - is the same as the current class (the object is an instance of our class) - */ - GraphNode declarationNode = declarations.get(declarations.size() - 1); - - ExpressionStmt declarationExpr = (ExpressionStmt) declarationNode.getAstNode(); - VariableDeclarationExpr variableDeclarationExpr = declarationExpr.getExpression().asVariableDeclarationExpr(); - - Optional optionalVariableDeclarator = variableDeclarationExpr.getVariables().stream() - .filter(variableDeclarator -> Objects.equals(variableDeclarator.getNameAsString(), scopeName)) - .findFirst(); - - if (!optionalVariableDeclarator.isPresent()) { - // should not happen - return; - } - - Type variableType = optionalVariableDeclarator.get().getType(); - - if (!variableType.isClassOrInterfaceType()) { - // Not class type - return; - } - - if (!Objects.equals(variableType.asClassOrInterfaceType().getNameAsString(), currentClassName)) { - // object is not instance of our class - return; - } - - // if we got here, the object is instance of our class, so we make the call - } - } - - // It's a static method call to a method of the current class - - } - } - }; - - PDGBuilder.visit(methodDeclaration, pdgGraph.getRootNode()); - - - sdgGraph.addNode(methodDeclaration.getNameAsString(), methodDeclaration); - - pdgGraph.getNodes().stream().skip(1).forEach(pdgNode -> { - Statement statement = (Statement) pdgNode.getAstNode(); - - if (statement.isExpressionStmt()) { - Expression expression = statement.asExpressionStmt().getExpression(); - - expression.findFirst(MethodCallExpr.class).ifPresent(methodCallExpr -> { - - }); - } else { - - } - }); - - - - - - sdgGraph.addPDG(pdgGraph, methodDeclaration); - - methodDeclaration.accept(this, ignored); - - pdgGraphs.add(pdgGraph); - } - - @Override - public void visit(ClassOrInterfaceDeclaration classOrInterfaceDeclaration, Void ignored) { -// if (sdgGraph.getRootNode() != null) { -// throw new IllegalStateException("¡Solo podemos procesar una clase por el momento!"); -// } - - if (classOrInterfaceDeclaration.isInterface()) { - throw new IllegalArgumentException("¡Las interfaces no estan permitidas!"); - } - - currentClass = classOrInterfaceDeclaration; - - classOrInterfaceDeclaration.accept(this, ignored); - } - - @Override - public void visit(CompilationUnit compilationUnit, Void ignored) { - currentCompilationUnit = compilationUnit; - - super.visit(compilationUnit, ignored); - } -} diff --git a/src/test/java/tfm/graphs/pdg/HandCraftedGraphs.java b/src/test/java/tfm/graphs/pdg/HandCraftedGraphs.java new file mode 100644 index 0000000000000000000000000000000000000000..698bcc543c9e88d7f673415092513e2b2f3e55ba --- /dev/null +++ b/src/test/java/tfm/graphs/pdg/HandCraftedGraphs.java @@ -0,0 +1,97 @@ +package tfm.graphs.pdg; + +import com.github.javaparser.ast.NodeList; +import com.github.javaparser.ast.body.MethodDeclaration; +import com.github.javaparser.ast.expr.MethodCallExpr; +import com.github.javaparser.ast.stmt.ContinueStmt; +import com.github.javaparser.ast.stmt.EmptyStmt; +import com.github.javaparser.ast.stmt.IfStmt; +import com.github.javaparser.ast.stmt.WhileStmt; +import com.github.javaparser.ast.type.VoidType; +import tfm.graphs.augmented.ACFG; +import tfm.graphs.augmented.APDG; +import tfm.graphs.augmented.PPDG; +import tfm.nodes.GraphNode; + +public class HandCraftedGraphs { + public static APDG problem1WithGotos() { + // Generate the control flow of a graph + ACFG cfg = new ACFG(); + cfg.buildRootNode("ENTER Problem1", new MethodDeclaration(new NodeList<>(), new VoidType(), "Problem1")); + GraphNode wx = cfg.addNode("while (X)", new WhileStmt()); + GraphNode ify = cfg.addNode("L: if (Y)", new IfStmt()); + GraphNode ifz = cfg.addNode("if (Z)", new IfStmt()); + GraphNode a = cfg.addNode("A();", new MethodCallExpr("A")); + GraphNode b = cfg.addNode("B();", new MethodCallExpr("B")); + GraphNode c = cfg.addNode("C();", new MethodCallExpr("C")); + GraphNode d = cfg.addNode("D();", new MethodCallExpr("D")); + GraphNode g1 = cfg.addNode("goto L;", new ContinueStmt("L")); + GraphNode g2 = cfg.addNode("goto L;", new ContinueStmt("L")); + + GraphNode end = cfg.addNode("Exit", new EmptyStmt()); + + cfg.addControlFlowEdge(cfg.getRootNode().get(), wx); + cfg.addControlFlowEdge(wx, ify); + cfg.addControlFlowEdge(wx, d); + cfg.addControlFlowEdge(ify, ifz); + cfg.addControlFlowEdge(ify, c); + cfg.addControlFlowEdge(ifz, a); + cfg.addControlFlowEdge(ifz, b); + cfg.addControlFlowEdge(a, g1); + cfg.addControlFlowEdge(b, g2); + cfg.addControlFlowEdge(c, wx); + cfg.addControlFlowEdge(d, end); + cfg.addNonExecutableControlFlowEdge(g1, b); + cfg.addControlFlowEdge(g1, ify); + cfg.addNonExecutableControlFlowEdge(g2, c); + cfg.addControlFlowEdge(g2, ify); + cfg.addNonExecutableControlFlowEdge(cfg.getRootNode().get(), end); + + PPDG pdg = new PPDG(cfg); + ControlDependencyBuilder gen = new ControlDependencyBuilder(pdg, cfg); + gen.analyze(); + return pdg; + } + + public static APDG problem1ContinueWithGotos() { + // Generate the control flow of a graph + ACFG cfg = new ACFG(); + cfg.buildRootNode("ENTER Problem1", new MethodDeclaration(new NodeList<>(), new VoidType(), "Problem1")); + GraphNode wx = cfg.addNode("while (X)", new WhileStmt()); + GraphNode ify = cfg.addNode("L: if (Y)", new IfStmt()); + GraphNode ifz = cfg.addNode("if (Z)", new IfStmt()); + GraphNode a = cfg.addNode("A();", new MethodCallExpr("A")); + GraphNode b = cfg.addNode("B();", new MethodCallExpr("B")); + GraphNode c = cfg.addNode("C();", new MethodCallExpr("C")); + GraphNode d = cfg.addNode("D();", new MethodCallExpr("D")); + GraphNode g1 = cfg.addNode("goto L1;", new ContinueStmt("L")); + GraphNode g2 = cfg.addNode("goto L2;", new ContinueStmt("L")); + GraphNode g3 = cfg.addNode("goto L3;", new ContinueStmt("L")); + + GraphNode end = cfg.addNode("Exit", new EmptyStmt()); + + cfg.addControlFlowEdge(cfg.getRootNode().get(), wx); + cfg.addControlFlowEdge(wx, ify); + cfg.addControlFlowEdge(wx, d); + cfg.addControlFlowEdge(ify, ifz); + cfg.addControlFlowEdge(ify, c); + cfg.addControlFlowEdge(ifz, a); + cfg.addControlFlowEdge(ifz, b); + cfg.addControlFlowEdge(a, g1); + cfg.addControlFlowEdge(b, g3); + cfg.addControlFlowEdge(c, wx); + cfg.addControlFlowEdge(d, end); + cfg.addNonExecutableControlFlowEdge(g1, b); + cfg.addControlFlowEdge(g1, ify); + cfg.addNonExecutableControlFlowEdge(g2, c); + cfg.addControlFlowEdge(g2, ify); + cfg.addNonExecutableControlFlowEdge(g3, g2); + cfg.addControlFlowEdge(g3, ify); + cfg.addNonExecutableControlFlowEdge(cfg.getRootNode().get(), end); + + PPDG pdg = new PPDG(cfg); + ControlDependencyBuilder gen = new ControlDependencyBuilder(pdg, cfg); + gen.analyze(); + return pdg; + } +} diff --git a/src/test/java/tfm/graphs/pdg/PDGTests.java b/src/test/java/tfm/graphs/pdg/PDGTests.java new file mode 100644 index 0000000000000000000000000000000000000000..607c17365c0fa98248c9e2423210685f427c3f5a --- /dev/null +++ b/src/test/java/tfm/graphs/pdg/PDGTests.java @@ -0,0 +1,187 @@ +package tfm.graphs.pdg; + +import com.github.javaparser.JavaParser; +import com.github.javaparser.ast.Modifier; +import com.github.javaparser.ast.Node; +import com.github.javaparser.ast.body.ClassOrInterfaceDeclaration; +import com.github.javaparser.ast.body.MethodDeclaration; +import com.github.javaparser.ast.stmt.ThrowStmt; +import com.github.javaparser.ast.stmt.TryStmt; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.MethodSource; +import tfm.exec.GraphLog; +import tfm.exec.PDGLog; +import tfm.graphs.augmented.ACFG; +import tfm.graphs.augmented.APDG; +import tfm.graphs.augmented.PPDG; +import tfm.graphs.cfg.CFG; +import tfm.nodes.GraphNode; +import tfm.slicing.GraphNodeCriterion; +import tfm.slicing.Slice; +import tfm.slicing.SlicingCriterion; +import tfm.utils.Logger; + +import java.io.File; +import java.io.IOException; +import java.io.PrintWriter; +import java.util.Arrays; +import java.util.Comparator; +import java.util.LinkedList; +import java.util.List; +import java.util.stream.Collectors; + +public class PDGTests { + static { + JavaParser.getStaticConfiguration().setAttributeComments(false); + } + + private boolean error = false; + + @ParameterizedTest(name = "[{index}] {0} ({1})") + @MethodSource("tfm.utils.FileFinder#findAllMethodDeclarations") + public void ppdgTest(File file, String methodName, MethodDeclaration root) throws IOException { + runPdg(file, methodName, root, new PPDG()); + } + + @ParameterizedTest(name = "[{index}] {0} ({1})") + @MethodSource("tfm.utils.FileFinder#findAllMethodDeclarations") + public void apdgTest(File file, String methodName, MethodDeclaration root) throws IOException { + runPdg(file, methodName, root, new APDG()); + } + + @ParameterizedTest(name = "[{index}] {0} ({1})") + @MethodSource("tfm.utils.FileFinder#findAllMethodDeclarations") + public void pdgTest(File file, String methodName, MethodDeclaration root) throws IOException { + runPdg(file, methodName, root, new PDG()); + } + + private void runPdg(File file, String methodName, MethodDeclaration root, PDG pdg) throws IOException { + pdg.build(root); + GraphLog graphLog = new PDGLog(pdg); + graphLog.log(); + try { + graphLog.generateImages(file.getPath() + "-" + methodName); + } catch (Exception e) { + System.err.println("Could not generate PNG"); + System.err.println(e.getMessage()); + } + } + + @ParameterizedTest(name = "[{index}] {0} ({1})") + @MethodSource("tfm.utils.FileFinder#findAllMethodDeclarations") + public void pdgCompare(File file, String methodName, MethodDeclaration root) { + ControlDependencyBuilder ctrlDepBuilder; + + if (containsUnsupportedStatements(root)) { + System.err.println("Contains unsupported instructions"); + } + + // Create PDG + CFG cfg = new CFG(); + cfg.build(root); + PDG pdg = new PDG(cfg); + pdg.buildRootNode("ENTER " + methodName, root); + ctrlDepBuilder = new ControlDependencyBuilder(pdg, cfg); + ctrlDepBuilder.analyze(); + + // Create APDG + ACFG acfg = new ACFG(); + acfg.build(root); + APDG apdg = new APDG(acfg); + apdg.buildRootNode("ENTER " + methodName, root); + ctrlDepBuilder = new ControlDependencyBuilder(apdg, acfg); + ctrlDepBuilder.analyze(); + + // Create PPDG + PPDG ppdg = new PPDG(acfg); + ppdg.buildRootNode("ENTER " + methodName, root); + ctrlDepBuilder = new ControlDependencyBuilder(ppdg, acfg); + ctrlDepBuilder.analyze(); + + // Print graphs (commented to decrease the test's time) + String filePathNoExt = file.getPath().substring(0, file.getPath().lastIndexOf('.')); +// String name = filePathNoExt + "/" + methodName; +// new PDGLog(pdg).generateImages(name); +// new PDGLog(apdg).generateImages(name); +// new PDGLog(ppdg).generateImages(name); + + // Compare + List slicedMethods = compareGraphs(pdg, apdg, ppdg); + + // Write sliced methods to a java file. + ClassOrInterfaceDeclaration clazz = new ClassOrInterfaceDeclaration(); + slicedMethods.forEach(clazz::addMember); + clazz.setName(methodName); + clazz.setModifier(Modifier.Keyword.PUBLIC, true); + try (PrintWriter pw = new PrintWriter(new File("./out/" + filePathNoExt + "/" + methodName + ".java"))) { + pw.println(clazz); + } catch (Exception e) { + Logger.log("Error! Could not write classes to file"); + } + + assert !error; + } + + public static boolean containsUnsupportedStatements(Node node) { + return node.findFirst(TryStmt.class).isPresent() + || node.findFirst(ThrowStmt.class).isPresent(); + } + + + /** Slices both graphs on every possible node and compares the result */ + public List compareGraphs(PDG... pdgs) { + List slicedMethods = new LinkedList<>(); + assert pdgs.length > 0; + for (GraphNode node : pdgs[0].vertexSet().stream() + .sorted(Comparator.comparingInt(GraphNode::getId)) + .collect(Collectors.toList())) { + // Skip start of graph + if (node.getAstNode() instanceof MethodDeclaration) + continue; + + // Perform slices + SlicingCriterion sc = new GraphNodeCriterion(node, "x"); + Slice[] slices = Arrays.stream(pdgs).map(p -> p.slice(sc)).toArray(Slice[]::new); + + // Compare slices + boolean ok = true; + Slice referenceSlice = slices[0]; + for (Slice slice : slices) { + ok = referenceSlice.equals(slice); + error |= !ok; + if (!ok) break; + } + + // Display slice + Logger.log("Slicing on " + node.getId()); + if (!ok) + Logger.log("FAILED!"); + printSlices(pdgs[0], slices); + + // Save slices as MethodDeclaration + int i = 0; + for (Slice s : slices) { + i++; + try { + MethodDeclaration m = ((MethodDeclaration) s.getAst()); + m.setName(m.getName() + "_slice" + node.getId() + "_pdg" + i); + slicedMethods.add(m); + } catch (RuntimeException e) { + Logger.log("Error: " + e.getMessage()); + } + } + } + return slicedMethods; + } + + public final void printSlices(PDG pdg, Slice... slices) { + pdg.vertexSet().stream() + .sorted(Comparator.comparingInt(GraphNode::getId)) + .forEach(n -> Logger.format("%3d: %s %s", + n.getId(), + Arrays.stream(slices) + .map(s -> s.contains(n) ? "x" : " ") + .reduce((a, b) -> a + " " + b).orElse("--error--"), + n.getInstruction())); + } +} diff --git a/src/test/java/tfm/utils/FileFinder.java b/src/test/java/tfm/utils/FileFinder.java new file mode 100644 index 0000000000000000000000000000000000000000..b123405ac368787c937d44492057d8a79d93f305 --- /dev/null +++ b/src/test/java/tfm/utils/FileFinder.java @@ -0,0 +1,40 @@ +package tfm.utils; + +import com.github.javaparser.JavaParser; +import com.github.javaparser.ast.body.MethodDeclaration; +import org.junit.jupiter.params.provider.Arguments; + +import java.io.File; +import java.io.FileNotFoundException; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.List; + +public class FileFinder { + private static final String TEST_FILES = "./src/test/res/"; + private static final String DOT_JAVA = ".java"; + + public static Collection findFiles(File directory, String suffix) throws FileNotFoundException { + Collection res = new ArrayList<>(); + File[] files = directory.listFiles(); + if (files == null) return Collections.emptyList(); + for (File f : files) { + if (f.getName().endsWith(suffix)) + for (MethodDeclaration m : methodsOf(f)) + res.add(Arguments.of(f, m.getNameAsString(), m)); + if (f.isDirectory()) + res.addAll(findFiles(f, suffix)); + } + return res; + } + + public static Arguments[] findAllMethodDeclarations() throws FileNotFoundException { + Collection args = findFiles(new File(TEST_FILES), DOT_JAVA); + return args.toArray(new Arguments[0]); + } + + private static List methodsOf(File file) throws FileNotFoundException { + return JavaParser.parse(file).findAll(MethodDeclaration.class); + } +} diff --git a/src/test/res/carlos/Problem1.java b/src/test/res/carlos/Problem1.java new file mode 100644 index 0000000000000000000000000000000000000000..72608748b93232fdf904f1643918938abf3b3022 --- /dev/null +++ b/src/test/res/carlos/Problem1.java @@ -0,0 +1,16 @@ +public class Problem1 { + public static void main(String[] args) { + while (X) { + if (Y) { + if (Z) { + A(); + break; + } + B(); + break; + } + C(); + } + D(); + } +} \ No newline at end of file diff --git a/src/test/res/carlos/Problem2.java b/src/test/res/carlos/Problem2.java new file mode 100644 index 0000000000000000000000000000000000000000..89533f5fb4bbb2b30af74a0f54e62f4acf4548d1 --- /dev/null +++ b/src/test/res/carlos/Problem2.java @@ -0,0 +1,11 @@ +public class Problem2 { + public static void main(String[] args) { + int a = 0; + while (a >= 0) { + if (a > 10) + break; + a++; + } + System.out.println(a); + } +} \ No newline at end of file diff --git a/src/test/res/carlos/Problem3.java b/src/test/res/carlos/Problem3.java new file mode 100644 index 0000000000000000000000000000000000000000..955ea04a12bf692e1456260d002809e60e834222 --- /dev/null +++ b/src/test/res/carlos/Problem3.java @@ -0,0 +1,20 @@ +public class Problem3 { + public static int x; + + public static void main() throws Exception { + x = 0; + try { + f(); + } catch (Exception e) { + log("error"); + } + x = 1; + f(); + } + + public static void f() { + if (x % 2 == 0) + throw new Exception("error!"); + log("x = " + x); + } +} \ No newline at end of file diff --git a/src/test/res/carlos/Test1.java b/src/test/res/carlos/Test1.java new file mode 100644 index 0000000000000000000000000000000000000000..da111afefb516c05940b4f4eeae78352927f33e0 --- /dev/null +++ b/src/test/res/carlos/Test1.java @@ -0,0 +1,8 @@ +public class Test1 { + public static void main(String[] args) { + for (int a = 0; a < 10; a++) { + System.out.println(a); + } + System.out.println(true); + } +} diff --git a/src/test/res/java-slicing-benchmarks b/src/test/res/java-slicing-benchmarks new file mode 160000 index 0000000000000000000000000000000000000000..cc21305fd85556362c596aeed4155f1effaf181d --- /dev/null +++ b/src/test/res/java-slicing-benchmarks @@ -0,0 +1 @@ +Subproject commit cc21305fd85556362c596aeed4155f1effaf181d diff --git a/src/test/res/ltd-samples/BasicBreak.java b/src/test/res/ltd-samples/BasicBreak.java new file mode 100644 index 0000000000000000000000000000000000000000..17204ab036dea291e59878580eec0f5b246b0cab --- /dev/null +++ b/src/test/res/ltd-samples/BasicBreak.java @@ -0,0 +1,17 @@ +package mytest; + +public class BasicBreak { + public static void main(String[] args) { + int x = 0; + bucle: + while (true) { + x++; + for (int y = 0; y < 10; y++) { + if (y == x) break; + if (y * 2 == x) break bucle; + } + if (x > 10) break; + x++; + } + } +} diff --git a/src/test/res/ltd-samples/BasicContinue.java b/src/test/res/ltd-samples/BasicContinue.java new file mode 100644 index 0000000000000000000000000000000000000000..a55a66b50d1746e98cb927b8f0f1bd84930c1e05 --- /dev/null +++ b/src/test/res/ltd-samples/BasicContinue.java @@ -0,0 +1,17 @@ +package mytest; + +public class BasicContinue { + public static void main(String[] args) { + int x = 0; + bucle: + while (x < 20) { + x++; + for (int y = 0; y < 10; y++) { + if (y == x) continue; + if (y * 2 == x) continue bucle; + } + if (x > 10) continue; + x++; + } + } +} diff --git a/src/test/res/ltd-samples/BasicForeach.java b/src/test/res/ltd-samples/BasicForeach.java new file mode 100644 index 0000000000000000000000000000000000000000..08dd728f82dba17d164585949b387ed82bf0b9f9 --- /dev/null +++ b/src/test/res/ltd-samples/BasicForeach.java @@ -0,0 +1,9 @@ +public class BasicForeach { + public static void main(String[] args){ + int[] numbers = + {1,2,3,4,5,6,7,8,9,10}; + for (int item : numbers) { + System.out.println("Count is: " + item); + } + } +} \ No newline at end of file diff --git a/src/test/res/ltd-samples/BasicIf.java b/src/test/res/ltd-samples/BasicIf.java new file mode 100644 index 0000000000000000000000000000000000000000..5ceb9ec1d933388e9fb72911306f19c6bc12e159 --- /dev/null +++ b/src/test/res/ltd-samples/BasicIf.java @@ -0,0 +1,11 @@ +public class BasicIf { + public static void main(String[] args) { + boolean isMoving = false; + int currentSpeed = 10; + if (isMoving) { + currentSpeed--; + } else { + System.err.println("The bicycle has already stopped!"); + } + } +} \ No newline at end of file diff --git a/src/test/res/ltd-samples/BasicIfElse.java b/src/test/res/ltd-samples/BasicIfElse.java new file mode 100644 index 0000000000000000000000000000000000000000..c8099fde48da6ee30b68d21583616634977e4af9 --- /dev/null +++ b/src/test/res/ltd-samples/BasicIfElse.java @@ -0,0 +1,19 @@ +public class BasicIfElse { + public static void main(String[] args) { + int testscore = 76; + char grade; + + if (testscore >= 90) { + grade = 'A'; + } else if (testscore >= 80) { + grade = 'B'; + } else if (testscore >= 70) { + grade = 'C'; + } else if (testscore >= 60) { + grade = 'D'; + } else { + grade = 'F'; + } + System.out.println("Grade = " + grade); + } +} diff --git a/src/test/res/ltd-samples/BasicSwitch.java b/src/test/res/ltd-samples/BasicSwitch.java new file mode 100644 index 0000000000000000000000000000000000000000..1ea12d6a321c418a7f274e9556f9880754109878 --- /dev/null +++ b/src/test/res/ltd-samples/BasicSwitch.java @@ -0,0 +1,28 @@ +package mytest; + +public class BasicSwitch { + public static void main(String[] args) { + int x = Integer.valueOf(args[0]); + int y = -1; + switch (x) { + case 1: + y = 10; + break; + case 2: + y = 20; + break; + case 3: + y = 30; + break; + case 4: + case 5: + y = 100; + break; + case 6: + System.err.println("Error"); + case 10: + y = 0; + } + System.out.println(y); + } +} diff --git a/src/test/res/ltd-samples/BasicSwitchDefault.java b/src/test/res/ltd-samples/BasicSwitchDefault.java new file mode 100644 index 0000000000000000000000000000000000000000..70262e3801c9586b5a0f55ba41224ef554f82bab --- /dev/null +++ b/src/test/res/ltd-samples/BasicSwitchDefault.java @@ -0,0 +1,23 @@ +package mytest; + +public class BasicSwitchDefault { + public static void main(String[] args) { + int x = Integer.valueOf(args[0]); + int y; + switch (x % 3) { + case 0: + y = 10; + break; + case 1: + y = 20; + break; + case 2: + y = 30; + break; + default: + y = -1; + break; + } + System.out.println(y); + } +} diff --git a/src/test/res/ltd-samples/BasicSwitchNoBreak.java b/src/test/res/ltd-samples/BasicSwitchNoBreak.java new file mode 100644 index 0000000000000000000000000000000000000000..ab94ff621454858e2890cc5f2d12e5bc63b1fb2f --- /dev/null +++ b/src/test/res/ltd-samples/BasicSwitchNoBreak.java @@ -0,0 +1,16 @@ +package mytest; + +public class BasicSwitchNoBreak { + public static void main(String[] args) { + String res = ""; + switch (args[0]) { + case "a": + res = "abc"; + case "b": + res = "bac"; + case "c": + res = "cab"; + } + System.out.println(res); + } +} diff --git a/src/test/res/ltd-samples/Bucles_1.java b/src/test/res/ltd-samples/Bucles_1.java new file mode 100755 index 0000000000000000000000000000000000000000..664ab83beaa1f83ad2f67aa94226b93fa9386031 --- /dev/null +++ b/src/test/res/ltd-samples/Bucles_1.java @@ -0,0 +1,14 @@ +package ejemplos; + +public class Bucles_1 { + + public static void main(String[] args) { + // BUCLE WHILE (sin anidamiento) + int x = 1; + while (x <= 10) { + System.out.print(x); + x++; + } + System.out.println(); + } +} diff --git a/src/test/res/ltd-samples/Bucles_2.java b/src/test/res/ltd-samples/Bucles_2.java new file mode 100755 index 0000000000000000000000000000000000000000..3d388e2cb8138f2fd8657929f9618c46cab53e3d --- /dev/null +++ b/src/test/res/ltd-samples/Bucles_2.java @@ -0,0 +1,21 @@ +package ejemplos; + +public class Bucles_2 { + + public static void main(String[] args) { + // BUCLE WHILE anidado a otro WHILE + System.out.println("Empieza bucle WHILE anidado a otro WHILE:"); + int x = 1; + char y = 'a'; + while (x <= 10) { + System.out.print(" " + x); + y = 'a'; + while (y <= 'c') { + System.out.print(" " + y); + y++; + } + x++; + } + System.out.println(); + } +} diff --git a/src/test/res/ltd-samples/Bucles_3.java b/src/test/res/ltd-samples/Bucles_3.java new file mode 100755 index 0000000000000000000000000000000000000000..96e4d867adbc35428aaf70d8d09c6e53db08c4ec --- /dev/null +++ b/src/test/res/ltd-samples/Bucles_3.java @@ -0,0 +1,35 @@ +package ejemplos; + +public class Bucles_3 { + + public static void main(String[] args) { + int x; + + // BUCLE FOR (sin anidamiento) + System.out.println("Empieza bucle FOR:"); + for (x = 1; x <= 10; x++) { + System.out.print(" " + x); + } + System.out.println(); + + // BUCLE WHILE (sin anidamiento) + System.out.println("Empieza bucle WHILE:"); + x = 1; + while (x <= 10) { + System.out.print(" " + x); + x++; + } + System.out.println(); + + // BUCLE DO WHILE (sin anidamiento) + System.out.println("Empieza bucle DO WHILE:"); + x = 1; + do { + System.out.print(" " + x); + x++; + } + while (x <= 10); + System.out.println(); + + } +} diff --git a/src/test/res/ltd-samples/Bucles_4.java b/src/test/res/ltd-samples/Bucles_4.java new file mode 100755 index 0000000000000000000000000000000000000000..9f3c130027fdc0ad5a86c46c3c53f18b95f9c957 --- /dev/null +++ b/src/test/res/ltd-samples/Bucles_4.java @@ -0,0 +1,37 @@ +package ejemplos; + +public class Bucles_4 { + + public static void main(String[] args) { + int x = 1; + + //Bucle 1: Contador + while (x < 10) { + System.out.println(x); + x++; + } + + //Bucle 2: Sumatorio + int suma = 0; + int y = 1; + while (y < 10) { + suma += y; + y++; + } + System.out.println(suma); + + //Bucle 3: Sumatorio + int sumatorio = 0; + int min = 10; + int max = 100; + for (int num = min; num <= max; num++) { + sumatorio += num; + } + System.out.println(sumatorio); + + int count = 0; + while (count < 10) + count++; + System.out.println(count); + } +} \ No newline at end of file diff --git a/src/test/res/ltd-samples/Bucles_5.java b/src/test/res/ltd-samples/Bucles_5.java new file mode 100755 index 0000000000000000000000000000000000000000..e54d85e85ac0f5b87a8812dd2b2344eb5870cfd6 --- /dev/null +++ b/src/test/res/ltd-samples/Bucles_5.java @@ -0,0 +1,47 @@ +package ejemplos; + +public class Bucles_5 { + + public static void main(String[] args) { + int x = 0; + char y = '0'; + + // BUCLE FOR anidado a otro FOR + System.out.println("Empieza bucle FOR anidado a otro FOR:"); + for (x = 1; x <= 10; x++) { + System.out.print(" " + x); + for (y = 'a'; y <= 'c'; y++) { + System.out.print(" " + y); + } + } + System.out.println(); + + // BUCLE WHILE anidado a otro WHILE + System.out.println("Empieza bucle WHILE anidado a otro WHILE:"); + x = 1; + while (x <= 10) { + System.out.print(" " + x); + y = 'a'; + while (y <= 'c') { + System.out.print(" " + y); + y++; + } + x++; + } + System.out.println(); + + // BUCLE FOR anidado a bucle DO WHILE + System.out.println("Empieza bucle FOR anidado a bucle DO WHILE:"); + x = 1; + do { + System.out.print(" " + x); + for (y = 'a'; y <= 'c'; y++) { + System.out.print(" " + y); + } + x++; + } + while (x <= 10); + System.out.println(); + + } +} diff --git a/src/test/res/ltd-samples/Bucles_6.java b/src/test/res/ltd-samples/Bucles_6.java new file mode 100644 index 0000000000000000000000000000000000000000..9ba83b5d73b2427733d41cb751ba7d8b09abe012 --- /dev/null +++ b/src/test/res/ltd-samples/Bucles_6.java @@ -0,0 +1,23 @@ +package ejemplos; + +public class Bucles_6 { + + public static void main(String[] args) { + // BUCLE WHILE (sin anidamiento) + System.out.println("Empieza bucle WHILE:"); + int x = 1; + while (x <= 10) { + System.out.print(" " + x); + x++; + while (x <= 10) { + System.out.print(" " + x); + x++; + } + } + while (x <= 10) { + System.out.print(" " + x); + x++; + } + System.out.println(); + } +} diff --git a/src/test/res/ltd-samples/Bucles_Josep.java b/src/test/res/ltd-samples/Bucles_Josep.java new file mode 100644 index 0000000000000000000000000000000000000000..d391cee0f84b4e29321e07976579ff2093bdb749 --- /dev/null +++ b/src/test/res/ltd-samples/Bucles_Josep.java @@ -0,0 +1,39 @@ +package ejemplos; + +public class Bucles_Josep { + + public static void main(String[] args) { + + int x=0; + int y=0, z=0; + + z=x+y; + + if (z>0) + { + for(int a=1;a==1;) + { + a++; + if (a == 2) + break; + a++; + } + } + + while(z==0) + { + if (z==0) + { + z++; + } + else + { + z--; + } + } + + + + + } +} diff --git a/src/test/res/ltd-samples/ReturnTest.java b/src/test/res/ltd-samples/ReturnTest.java new file mode 100644 index 0000000000000000000000000000000000000000..4b3f7e83739f29120ea4756b7c28ec82921b3edc --- /dev/null +++ b/src/test/res/ltd-samples/ReturnTest.java @@ -0,0 +1,12 @@ +public class ReturnTest { + public static void main(String[] args) { + int i = Integer.valueOf(args[0]); + if (i == 0) { + System.out.println("true"); + return; + } else { + System.out.println("false"); + return; + } + } +} \ No newline at end of file diff --git a/src/test/res/ltd-samples/Test_1.java b/src/test/res/ltd-samples/Test_1.java new file mode 100644 index 0000000000000000000000000000000000000000..7264be03be6f0c676d8e2dafe964ed881806cf6d --- /dev/null +++ b/src/test/res/ltd-samples/Test_1.java @@ -0,0 +1,12 @@ +package ejemplos; + +public class Test_1 { + + public static void main(String[] args) { + System.out.println("HOLA mundo"); + int x = 1; + x = 2; + x = 3; + x = 4; + } +} diff --git a/src/test/res/ltd-samples/Test_2.java b/src/test/res/ltd-samples/Test_2.java new file mode 100644 index 0000000000000000000000000000000000000000..3ce1e38aeb82078b4a9fd68afdeb571c0334c45c --- /dev/null +++ b/src/test/res/ltd-samples/Test_2.java @@ -0,0 +1,13 @@ +package ejemplos; + +public class Test_2 { + + public static void main(String[] args) { + int x = 1; + x++; + ++x; + int y = 0; + x = x + y; + System.out.println(x); + } +} diff --git a/src/test/res/ltd-samples/Test_3.java b/src/test/res/ltd-samples/Test_3.java new file mode 100644 index 0000000000000000000000000000000000000000..e08b7f7c40010b5dbfba7b9128e979478e142caa --- /dev/null +++ b/src/test/res/ltd-samples/Test_3.java @@ -0,0 +1,13 @@ +package ejemplos; + +public class Test_3 { + + public static void main(String[] args) { + int x = 1; + + if (x == 1) + x = 2; + x = 3; + x = 4; + } +} diff --git a/src/test/res/ltd-samples/Test_4.java b/src/test/res/ltd-samples/Test_4.java new file mode 100644 index 0000000000000000000000000000000000000000..43e923caaf3949b89744cacda6291fcd2890112d --- /dev/null +++ b/src/test/res/ltd-samples/Test_4.java @@ -0,0 +1,18 @@ +package ejemplos; + +public class Test_4 { + + public static void main(String[] args) { + int x = 1; + + if (x == 1) { + x = 2; + if (x >= 1) { + x = 3; + x = 4; + } + } + x = 5; + x = 6; + } +} diff --git a/src/test/res/ltd-samples/Test_5.java b/src/test/res/ltd-samples/Test_5.java new file mode 100644 index 0000000000000000000000000000000000000000..4dc96e4a7cac7deda71ea251890761cd0efba879 --- /dev/null +++ b/src/test/res/ltd-samples/Test_5.java @@ -0,0 +1,19 @@ +package ejemplos; + +public class Test_5 { + + public static void main(String[] args) { + int x = 1; + + if (x == 1) { + x = 2; + if (x >= 1) { + x = 3; + x = 4; + } + x = 5; + } + x = 6; + x = 7; + } +} diff --git a/src/test/res/ltd-samples/Test_6.java b/src/test/res/ltd-samples/Test_6.java new file mode 100644 index 0000000000000000000000000000000000000000..91d0e1928207224d2e7f8c57b8d0cebfb7b40b5d --- /dev/null +++ b/src/test/res/ltd-samples/Test_6.java @@ -0,0 +1,17 @@ +package ejemplos; + +public class Test_6 { + + public static void main(String[] args) { + int x = 1; + + if (x == 1) { + x = 2; + x = 3; + } else { + x = 4; + x = 5; + } + x = 6; + } +} diff --git a/src/test/res/ltd-samples/Test_7.java b/src/test/res/ltd-samples/Test_7.java new file mode 100644 index 0000000000000000000000000000000000000000..60b7f5f33b76a01215a2060af41661e246939f2c --- /dev/null +++ b/src/test/res/ltd-samples/Test_7.java @@ -0,0 +1,18 @@ +package ejemplos; + + +public class Test_7 { + + public static void main(String[] args) { + int x = 1; + + if (x == 1) { + x = 2; + } else x = 3; + x = 4; + if (x == 2) { + x = 5; + } else if (x == 3) x = 6; + x = 7; + } +} diff --git a/src/test/res/ltd-samples/Test_8.java b/src/test/res/ltd-samples/Test_8.java new file mode 100644 index 0000000000000000000000000000000000000000..92c240f11186ae25acbe70834e9d52798227f196 --- /dev/null +++ b/src/test/res/ltd-samples/Test_8.java @@ -0,0 +1,19 @@ +package ejemplos; + +public class Test_8 { + + public static void main(String[] args) { + int x = 1; + + if (x == 1) { + x = 2; + } + x = 5; + x = 6; + if (x == 2) { + x = 7; + } + if (x == 3) x = 8; + x = 9; + } +} diff --git a/src/test/res/ltd-samples/Test_9.java b/src/test/res/ltd-samples/Test_9.java new file mode 100644 index 0000000000000000000000000000000000000000..95a3c53f8e8656c36286ef2cc405707f9e6378de --- /dev/null +++ b/src/test/res/ltd-samples/Test_9.java @@ -0,0 +1,35 @@ +package ejemplos; + +public class Test_9 { + + + public static void main(String[] args) { + // ANIDAMIENTO de IF y WHILE + + // ANIDAMIENTO de IF y WHILE 2 + + int x = 0; + if (x > 1) { + x = 1; + while (x > 2) { + x = 2; + while (x > 3) { + x = 3; + if (x > 4) { + x = 4; + if (x > 5) { + x = 5; + } + x--; + } + x--; + } + x--; + } + x--; + } + + x--; + + } +} diff --git a/src/test/res/papers/Example1_Horwitz_PPDG.java b/src/test/res/papers/Example1_Horwitz_PPDG.java new file mode 100644 index 0000000000000000000000000000000000000000..da381ff8c1846f60e344f4294a9f3c2edcbef40b --- /dev/null +++ b/src/test/res/papers/Example1_Horwitz_PPDG.java @@ -0,0 +1,8 @@ +public class Test { + public static void main(String[] args) { + switch (X) { + case e1: + + } + } +} \ No newline at end of file diff --git a/src/main/java/tfm/programs/WhileLoop.java b/src/test/res/programs/WhileLoop.java similarity index 97% rename from src/main/java/tfm/programs/WhileLoop.java rename to src/test/res/programs/WhileLoop.java index 81b2f483753a57850e404226dd35d91e63edebc8..da61f460ad74ce1e65f1656b32a5c292ee2d4b3f 100644 --- a/src/main/java/tfm/programs/WhileLoop.java +++ b/src/test/res/programs/WhileLoop.java @@ -2,6 +2,8 @@ package tfm.programs; public class WhileLoop { + void main() {} + void while1() { int x = 1; int y = 2; diff --git a/src/test/res/programs/cfg/CFG_Test1.java b/src/test/res/programs/cfg/CFG_Test1.java new file mode 100644 index 0000000000000000000000000000000000000000..01539fd31a489ab51e6b8971fd5581e9f4b189d6 --- /dev/null +++ b/src/test/res/programs/cfg/CFG_Test1.java @@ -0,0 +1,5 @@ +public class CFG_Test1 { + public static void main(String[] args) { + + } +} \ No newline at end of file diff --git a/src/test/res/programs/cfg/CFG_Test2.java b/src/test/res/programs/cfg/CFG_Test2.java new file mode 100644 index 0000000000000000000000000000000000000000..b885e305057a9f7e621266a5a1315e01f0e62e95 --- /dev/null +++ b/src/test/res/programs/cfg/CFG_Test2.java @@ -0,0 +1,8 @@ +public class CFG_Test2 { + public static void main(String[] args) { + int a = 1; + int b = 2; + int c = 3; + int d = 4; + } +} \ No newline at end of file diff --git a/src/test/res/programs/cfg/CFG_Test3.java b/src/test/res/programs/cfg/CFG_Test3.java new file mode 100644 index 0000000000000000000000000000000000000000..e1fba8cb9969a9f1344ccdba1f8e5ce06f918c56 --- /dev/null +++ b/src/test/res/programs/cfg/CFG_Test3.java @@ -0,0 +1,8 @@ +public class CFG_Test3 { + public static void main(String[] args) { + int a = 1; + while (a < 10) + a++; + System.out.println(a); + } +} \ No newline at end of file diff --git a/src/test/res/programs/cfg/CFG_Test4.java b/src/test/res/programs/cfg/CFG_Test4.java new file mode 100644 index 0000000000000000000000000000000000000000..1580ba593145be4665de88cd71b15dca0ebb585e --- /dev/null +++ b/src/test/res/programs/cfg/CFG_Test4.java @@ -0,0 +1,11 @@ +public class CFG_Test4 { + public static void main(String[] args) { + int a = 1; + if (a % 2 == 0) { + a++; + } else { + a--; + } + System.out.println(a); + } +} \ No newline at end of file diff --git a/src/test/res/programs/cfg/CFG_Test5.java b/src/test/res/programs/cfg/CFG_Test5.java new file mode 100644 index 0000000000000000000000000000000000000000..816e4569916c487665a7d18928fbf54790afc495 --- /dev/null +++ b/src/test/res/programs/cfg/CFG_Test5.java @@ -0,0 +1,9 @@ +public class CFG_Test5 { + public static void main(String[] args) { + int a = 1; + if (a % 2 == 0) { + a++; + } + System.out.println(a); + } +} \ No newline at end of file diff --git a/src/main/java/tfm/programs/cfg/Eval_1.java b/src/test/res/programs/cfg/Eval_1.java similarity index 100% rename from src/main/java/tfm/programs/cfg/Eval_1.java rename to src/test/res/programs/cfg/Eval_1.java diff --git a/src/main/java/tfm/programs/cfg/Eval_2.java b/src/test/res/programs/cfg/Eval_2.java similarity index 100% rename from src/main/java/tfm/programs/cfg/Eval_2.java rename to src/test/res/programs/cfg/Eval_2.java diff --git a/src/main/java/tfm/programs/cfg/Eval_3.java b/src/test/res/programs/cfg/Eval_3.java similarity index 100% rename from src/main/java/tfm/programs/cfg/Eval_3.java rename to src/test/res/programs/cfg/Eval_3.java diff --git a/src/main/java/tfm/programs/cfg/Eval_4.java b/src/test/res/programs/cfg/Eval_4.java similarity index 100% rename from src/main/java/tfm/programs/cfg/Eval_4.java rename to src/test/res/programs/cfg/Eval_4.java diff --git a/src/main/java/tfm/programs/pdg/Example1.java b/src/test/res/programs/pdg/Example1.java similarity index 100% rename from src/main/java/tfm/programs/pdg/Example1.java rename to src/test/res/programs/pdg/Example1.java diff --git a/src/main/java/tfm/programs/pdg/Example2.java b/src/test/res/programs/pdg/Example2.java similarity index 100% rename from src/main/java/tfm/programs/pdg/Example2.java rename to src/test/res/programs/pdg/Example2.java diff --git a/src/main/java/tfm/programs/pdg/Example3.java b/src/test/res/programs/pdg/Example3.java similarity index 100% rename from src/main/java/tfm/programs/pdg/Example3.java rename to src/test/res/programs/pdg/Example3.java diff --git a/src/main/java/tfm/programs/pdg/Test.java b/src/test/res/programs/pdg/Test.java similarity index 100% rename from src/main/java/tfm/programs/pdg/Test.java rename to src/test/res/programs/pdg/Test.java diff --git a/src/main/java/tfm/programs/sdg/Example1.java b/src/test/res/programs/sdg/Example1.java similarity index 100% rename from src/main/java/tfm/programs/sdg/Example1.java rename to src/test/res/programs/sdg/Example1.java