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 extends String> 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:
+ *
+ * - Create a new {@link ACFG}.
+ * - Create a new {@link ACFGBuilder}, passing the graph as argument.
+ * - 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)}
+ * - 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.
+ *
+ */
+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:
+ *
+ * - Create a new {@link CFG}.
+ * - Create a new {@link CFGBuilder}, passing the graph as argument.
+ * - 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)}
+ * - 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.
+ *
+ */
+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 extends GraphNode>> nodeOptional = cfgGraph.findNodeByASTNode(node.getAstNode());
+ Optional extends GraphNode>> 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 extends GraphNode>> nodeOptional = cfgGraph.findNodeByASTNode(statement);
+ Optional extends GraphNode>> 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:
+ *
+ * - Create an empty {@link CFG}.
+ * - Create an empty {@link PDG} (optionally passing the {@link CFG} as argument).
+ * - Create a new {@link PDGBuilder}, passing both graphs as arguments.
+ * - 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)}
+ * - 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.
+ *
+ */
+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 extends Arrow> incomingArcs,
- Collection extends Arrow> 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 extends Arrow> incomingArcs, Collection extends Arrow> 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