Loading src/main/java/tfm/exec/Config.java +9 −0 Original line number Diff line number Diff line Loading @@ -9,8 +9,17 @@ package tfm.exec; * <li>ACFG</li>: the <it>augmented</it> CFG; adds non-executable edges to represent unconditional * jumps such as {@code break}, {@code return}, {@code switch} and much more. * </ul> * The <b>Program Dependence Graph</b> has variations in a similar fashion. * <ul> * <li>PDG</li>: based on the CFG, it computes control and data dependence to connect the nodes. * <li>APDG</li>: similar to the PDG, but based on the ACFG. The non-executable edges are ignored * when computing data dependencies. * </ul> */ public class Config { public static final int CFG = 0, ACFG = 1; public static final int PDG = 0, APDG = 1; public static int CFG_TYPE = CFG; public static int PDG_TYPE = PDG; } src/main/java/tfm/graphs/PDGGraph.java +38 −60 Original line number Diff line number Diff line Loading @@ -2,10 +2,10 @@ 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.data.ArcData; import tfm.arcs.pdg.ControlDependencyArc; import tfm.arcs.pdg.DataDependencyArc; import tfm.nodes.GraphNode; Loading @@ -21,7 +21,15 @@ import java.util.Optional; import java.util.Set; import java.util.stream.Collectors; /** * The <b>Program Dependence Graph</b> 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 tfm.visitors.pdg.PDGBuilder PDGBuilder}. * @see tfm.exec.Config Config (for the available variations of the PDG) */ public class PDGGraph extends Graph { public static boolean isRanked = false, isSorted = false; private CFGGraph cfgGraph; Loading @@ -38,7 +46,7 @@ public class PDGGraph extends Graph { return "Entry"; } public GraphNode addNode(GraphNode<?> node) { public GraphNode<?> addNode(GraphNode<?> node) { GraphNode<?> vertex = new GraphNode<>(node); super.addVertex(vertex); Loading @@ -58,32 +66,32 @@ public class PDGGraph extends Graph { } @SuppressWarnings("unchecked") private void addArc(Arc arc) { super.addEdge(arc); private void addArc(Arc<? extends ArcData> arc) { super.addEdge((Arrow<String, ArcData>) arc); } public void addControlDependencyArc(GraphNode from, GraphNode to) { 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) { public void addDataDependencyArc(GraphNode<?> from, GraphNode<?> to, String variable) { DataDependencyArc dataDataDependencyArc = new DataDependencyArc(from, to, variable); this.addArc(dataDataDependencyArc); } public Set<GraphNode> getNodesAtLevel(int level) { public Set<GraphNode<?>> getNodesAtLevel(int level) { return getVerticies().stream() .map(vertex -> (GraphNode) vertex) .map(vertex -> (GraphNode<?>) vertex) .filter(node -> getLevelOf(node) == level) .collect(Collectors.toSet()); } public int getLevels() { return getVerticies().stream() .map(vertex -> (GraphNode) vertex) .map(vertex -> (GraphNode<?>) vertex) .max(Comparator.comparingInt(this::getLevelOf)) .map(node -> getLevelOf(node) + 1) .orElse(0); Loading @@ -98,6 +106,7 @@ public class PDGGraph extends Graph { public int getLevelOf(@NotNull GraphNode<?> node) { Optional<ControlDependencyArc> optionalControlDependencyArc = node.getIncomingArcs().stream() .filter(Arc::isControlDependencyArrow) .filter(a -> a.getFromNode().getId() < node.getId()) .findFirst() .map(arc -> (ControlDependencyArc) arc); Loading Loading @@ -127,21 +136,24 @@ public class PDGGraph extends Graph { // No level 0 is needed (only one node) for (int i = 0; i < getLevels(); i++) { Set<GraphNode> levelNodes = getNodesAtLevel(i); Set<GraphNode<?>> levelNodes = getNodesAtLevel(i); if (levelNodes.size() <= 1) { continue; } // rank same if (isRanked) { rankedNodes.append("{ rank = same; ") .append(levelNodes.stream() .map(node -> String.valueOf(node.getId())) .collect(Collectors.joining(";"))) .append(" }") .append(lineSep); } // invisible arrows for ordering if (isSorted) { rankedNodes.append(levelNodes.stream() .sorted(Comparator.comparingInt(GraphNode::getId)) .map(node -> String.valueOf(node.getId())) Loading @@ -149,10 +161,11 @@ public class PDGGraph extends Graph { .append("[style = invis];") .append(lineSep); } } String arrows = getArcs().stream() .sorted(Comparator.comparingInt(arrow -> ((GraphNode) arrow.getFrom()).getId())) .sorted(Comparator.comparingInt(arrow -> ((GraphNode<?>) arrow.getFrom()).getId())) .map(Arc::toGraphvizRepresentation) .collect(Collectors.joining(lineSep)); Loading @@ -173,22 +186,7 @@ public class PDGGraph extends Graph { 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<CFGNode<?>> definitionNodes = Utils.findLastDefinitionsFrom(cfgNode, slicingCriterion.getVariable()); // // Logger.format("Slicing node: %s", node); // // // Get slice nodes from definition nodes // Set<Integer> sliceNodes = definitionNodes.stream() // .flatMap(definitionNode -> getSliceNodes(new HashSet<>(), this.findNodeByASTNode(definitionNode.getAstNode()).get()).stream()) // .collect(Collectors.toSet()); // // sliceNodes.add(node.getId()); GraphNode<?> node = optionalGraphNode.get(); // Simply get slice nodes from GraphNode Set<Integer> sliceNodes = getSliceNodes(new HashSet<>(), node); Loading @@ -199,7 +197,7 @@ public class PDGGraph extends Graph { astCopy.accept(new PDGBuilder(sliceGraph), sliceGraph.getRootNode()); for (GraphNode sliceNode : sliceGraph.getNodes()) { for (GraphNode<?> sliceNode : sliceGraph.getNodes()) { if (!sliceNodes.contains(sliceNode.getId())) { Logger.log("Removing node " + sliceNode.getId()); sliceNode.getAstNode().removeForced(); Loading @@ -207,37 +205,17 @@ public class PDGGraph extends Graph { } } // for (Arc arc : getArcs()) { // Optional<GraphNode> fromOptional = sliceGraph.findNodeById(arc.getFromNode().getId()); // Optional<GraphNode> 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<Integer> getSliceNodes(Set<Integer> visited, GraphNode<?> root) { visited.add(root.getId()); for (Arrow arrow : root.getIncomingArcs()) { Arc arc = (Arc) arrow; GraphNode<?> from = (GraphNode) arc.getFromNode(); for (Arc<ArcData> arc : root.getIncomingArcs()) { GraphNode<?> from = arc.getFromNode(); if (visited.contains(from.getId())) { if (visited.contains(from.getId())) continue; } getSliceNodes(visited, from); } Loading src/main/java/tfm/nodes/GraphNode.java +11 −2 Original line number Diff line number Diff line Loading @@ -14,6 +14,14 @@ import tfm.variables.VariableExtractor; import java.util.*; import java.util.stream.Collectors; /** * Represents a node in the various graphs ({@link tfm.graphs.CFGGraph CFG}, * {@link tfm.graphs.PDGGraph PDG} and {@link tfm.graphs.SDGGraph 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. * @param <N> The type of the AST represented by this node. */ public class GraphNode<N extends Node> extends Vertex<String, ArcData> { private int id; Loading Loading @@ -135,9 +143,10 @@ public class GraphNode<N extends Node> extends Vertex<String, ArcData> { if (!(o instanceof GraphNode)) return false; GraphNode other = (GraphNode) o; GraphNode<?> other = (GraphNode<?>) o; return Objects.equals(getData(), other.getData()) return this.getId() == other.getId() && Objects.equals(getData(), other.getData()) && Objects.equals(astNode, other.astNode); // && Objects.equals(getIncomingArrows(), other.getIncomingArrows()) // && Objects.equals(getOutgoingArrows(), other.getOutgoingArrows()) Loading src/main/java/tfm/visitors/pdg/ControlDependencyBuilder.java +88 −78 Original line number Diff line number Diff line package tfm.visitors.pdg; import com.github.javaparser.ast.stmt.*; import com.github.javaparser.ast.visitor.VoidVisitorAdapter; import tfm.arcs.Arc; import tfm.arcs.data.ArcData; import tfm.graphs.CFGGraph; import tfm.graphs.PDGGraph; import tfm.nodes.GraphNode; import java.util.stream.Collectors; public class ControlDependencyBuilder extends VoidVisitorAdapter<GraphNode> { private CFGGraph cfgGraph; private PDGGraph pdgGraph; public ControlDependencyBuilder(PDGGraph pdgGraph, CFGGraph cfgGraph) { this.pdgGraph = pdgGraph; this.cfgGraph = cfgGraph; import java.util.*; /** * A simple but slow finder of control dependencies. * <br/> * 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: * <br/> * A node <i>b</i> is control dependent on another node <i>a</i> if and only if <i>b</i> postdominates * one but not all of the successors of <i>a</i>. * <br/> * A node <i>b</i> postdominates another node <i>a</i> if and only if <i>b</i> appears in every path * from <i>a</i> to the "Exit" node. * <br/> * There exist better, cheaper approaches that have linear complexity w.r.t. the number of edges in the CFG. * <b>Usage:</b> pass an empty {@link PDGGraph} and a filled {@link CFGGraph} and then run {@link #analyze()}. * This builder should only be used once, and then discarded. */ public class ControlDependencyBuilder { private final PDGGraph pdg; private final CFGGraph cfg; public ControlDependencyBuilder(PDGGraph pdg, CFGGraph cfg) { this.pdg = pdg; this.cfg = cfg; } @Override public void visit(ExpressionStmt expressionStmt, GraphNode parent) { addNodeAndControlDependency(expressionStmt, parent); public void analyze() { Map<GraphNode<?>, GraphNode<?>> nodeMap = new HashMap<>(); nodeMap.put(cfg.getRootNode(), pdg.getRootNode()); Set<GraphNode<?>> roots = new HashSet<>(cfg.getNodes()); roots.remove(cfg.getRootNode()); Set<GraphNode<?>> cfgNodes = new HashSet<>(cfg.getNodes()); cfgNodes.removeIf(node -> node.getData().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)); if (pdg.contains(src)) roots.remove(dest); } @Override public void visit(IfStmt ifStmt, GraphNode parent) { GraphNode node = addNodeAndControlDependency(ifStmt, parent); ifStmt.getThenStmt().accept(this, node); ifStmt.getElseStmt().ifPresent(statement -> statement.accept(this, node)); } @Override public void visit(WhileStmt whileStmt, GraphNode parent) { GraphNode node = addNodeAndControlDependency(whileStmt, parent); whileStmt.getBody().accept(this, node); } @Override public void visit(ForStmt forStmt, GraphNode parent) { String initialization = forStmt.getInitialization().stream() .map(com.github.javaparser.ast.Node::toString) .collect(Collectors.joining(",")); String update = forStmt.getUpdate().stream() .map(com.github.javaparser.ast.Node::toString) .collect(Collectors.joining(",")); String compare = forStmt.getCompare() .map(com.github.javaparser.ast.Node::toString) .orElse("true"); GraphNode forNode = pdgGraph.addNode( String.format("for (%s;%s;%s)", initialization, compare, update), forStmt ); pdgGraph.addControlDependencyArc(parent, forNode); forStmt.getBody().accept(this, forNode); // In the original definition, nodes were dependent by default on the Enter/Start node for (GraphNode<?> node : roots) if (!node.getData().equals("Exit")) pdg.addControlDependencyArc(pdg.getRootNode(), nodeMap.get(node)); } @Override public void visit(ForEachStmt forEachStmt, GraphNode parent) { GraphNode node = addNodeAndControlDependency(forEachStmt, parent); forEachStmt.getBody().accept(this, node); public void registerNode(GraphNode<?> node, Map<GraphNode<?>, GraphNode<?>> nodeMap) { if (nodeMap.containsKey(node) || node.getData().equals("Exit")) return; GraphNode<?> clone = new GraphNode<>(node.getId(), node.getData(), node.getAstNode()); nodeMap.put(node, clone); pdg.addNode(clone); } @Override public void visit(SwitchStmt switchStmt, GraphNode parent) { GraphNode node = addNodeAndControlDependency(switchStmt, parent); switchStmt.getEntries().accept(this, node); public static boolean hasControlDependence(GraphNode<?> a, GraphNode<?> b) { int yes = 0; List<Arc<ArcData>> list = a.getOutgoingArcs(); // Nodes with less than 1 outgoing arc cannot control another node. if (list.size() < 2) return false; for (Arc<ArcData> arc : list) { GraphNode<?> successor = arc.getToNode(); if (postdominates(successor, b)) yes++; } @Override public void visit(SwitchEntryStmt switchEntryStmt, GraphNode parent) { GraphNode node = addNodeAndControlDependency(switchEntryStmt, parent); switchEntryStmt.getStatements().accept(this, node); int no = list.size() - yes; return yes > 0 && no > 0; } private GraphNode addNodeAndControlDependency(Statement statement, GraphNode parent) { GraphNode<?> cfgNode = cfgGraph.findNodeByASTNode(statement).get(); GraphNode node = pdgGraph.addNode(cfgNode.getData(), cfgNode.getAstNode()); pdgGraph.addControlDependencyArc(parent, node); public static boolean postdominates(GraphNode<?> a, GraphNode<?> b) { return postdominates(a, b, new HashSet<>()); } return node; private static boolean postdominates(GraphNode<?> a, GraphNode<?> b, Set<GraphNode<?>> visited) { // Stop w/ success if a == b or a has already been visited if (a.equals(b) || visited.contains(a)) return true; List<Arc<ArcData>> outgoing = a.getOutgoingArcs(); // Stop w/ failure if there are no edges to traverse from a if (outgoing.size() == 0) return false; // Find all possible paths starting from a, if ALL find b, then true, else false visited.add(a); for (Arc<ArcData> out : outgoing) { if (!postdominates(out.getToNode(), b, visited)) return false; } return true; } } src/main/java/tfm/visitors/pdg/PDGBuilder.java +4 −4 Original line number Diff line number Diff line Loading @@ -14,15 +14,15 @@ import tfm.visitors.cfg.CFGBuilder; * <br/> * <b>Usage:</b> * <ol> * <li>Create and fill a {@link CFGGraph} for the desired method.</li> * <li>Create an empty {@link CFGGraph}.</li> * <li>Create an empty {@link PDGGraph} (optionally passing the {@link CFGGraph} as argument).</li> * <li>Create a new {@link PDGBuilder}, passing both graphs as arguments.</li> * <li>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)}</li> * <li>Once the previous step is finished, the complete PDG is saved in * the object created in the second step. <emph>The builder should be discarded * and not reused.</emph></li> * the object created in the second step. The builder should be discarded * and not reused.</li> * </ol> */ public class PDGBuilder extends VoidVisitorAdapter<GraphNode<?>> { Loading Loading @@ -60,7 +60,7 @@ public class PDGBuilder extends VoidVisitorAdapter<GraphNode<?>> { // Build control dependency ControlDependencyBuilder controlDependencyBuilder = new ControlDependencyBuilder(pdgGraph, cfgGraph); methodBody.accept(controlDependencyBuilder, parent); controlDependencyBuilder.analyze(); // Build data dependency DataDependencyBuilder dataDependencyBuilder = new DataDependencyBuilder(pdgGraph, cfgGraph); Loading Loading
src/main/java/tfm/exec/Config.java +9 −0 Original line number Diff line number Diff line Loading @@ -9,8 +9,17 @@ package tfm.exec; * <li>ACFG</li>: the <it>augmented</it> CFG; adds non-executable edges to represent unconditional * jumps such as {@code break}, {@code return}, {@code switch} and much more. * </ul> * The <b>Program Dependence Graph</b> has variations in a similar fashion. * <ul> * <li>PDG</li>: based on the CFG, it computes control and data dependence to connect the nodes. * <li>APDG</li>: similar to the PDG, but based on the ACFG. The non-executable edges are ignored * when computing data dependencies. * </ul> */ public class Config { public static final int CFG = 0, ACFG = 1; public static final int PDG = 0, APDG = 1; public static int CFG_TYPE = CFG; public static int PDG_TYPE = PDG; }
src/main/java/tfm/graphs/PDGGraph.java +38 −60 Original line number Diff line number Diff line Loading @@ -2,10 +2,10 @@ 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.data.ArcData; import tfm.arcs.pdg.ControlDependencyArc; import tfm.arcs.pdg.DataDependencyArc; import tfm.nodes.GraphNode; Loading @@ -21,7 +21,15 @@ import java.util.Optional; import java.util.Set; import java.util.stream.Collectors; /** * The <b>Program Dependence Graph</b> 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 tfm.visitors.pdg.PDGBuilder PDGBuilder}. * @see tfm.exec.Config Config (for the available variations of the PDG) */ public class PDGGraph extends Graph { public static boolean isRanked = false, isSorted = false; private CFGGraph cfgGraph; Loading @@ -38,7 +46,7 @@ public class PDGGraph extends Graph { return "Entry"; } public GraphNode addNode(GraphNode<?> node) { public GraphNode<?> addNode(GraphNode<?> node) { GraphNode<?> vertex = new GraphNode<>(node); super.addVertex(vertex); Loading @@ -58,32 +66,32 @@ public class PDGGraph extends Graph { } @SuppressWarnings("unchecked") private void addArc(Arc arc) { super.addEdge(arc); private void addArc(Arc<? extends ArcData> arc) { super.addEdge((Arrow<String, ArcData>) arc); } public void addControlDependencyArc(GraphNode from, GraphNode to) { 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) { public void addDataDependencyArc(GraphNode<?> from, GraphNode<?> to, String variable) { DataDependencyArc dataDataDependencyArc = new DataDependencyArc(from, to, variable); this.addArc(dataDataDependencyArc); } public Set<GraphNode> getNodesAtLevel(int level) { public Set<GraphNode<?>> getNodesAtLevel(int level) { return getVerticies().stream() .map(vertex -> (GraphNode) vertex) .map(vertex -> (GraphNode<?>) vertex) .filter(node -> getLevelOf(node) == level) .collect(Collectors.toSet()); } public int getLevels() { return getVerticies().stream() .map(vertex -> (GraphNode) vertex) .map(vertex -> (GraphNode<?>) vertex) .max(Comparator.comparingInt(this::getLevelOf)) .map(node -> getLevelOf(node) + 1) .orElse(0); Loading @@ -98,6 +106,7 @@ public class PDGGraph extends Graph { public int getLevelOf(@NotNull GraphNode<?> node) { Optional<ControlDependencyArc> optionalControlDependencyArc = node.getIncomingArcs().stream() .filter(Arc::isControlDependencyArrow) .filter(a -> a.getFromNode().getId() < node.getId()) .findFirst() .map(arc -> (ControlDependencyArc) arc); Loading Loading @@ -127,21 +136,24 @@ public class PDGGraph extends Graph { // No level 0 is needed (only one node) for (int i = 0; i < getLevels(); i++) { Set<GraphNode> levelNodes = getNodesAtLevel(i); Set<GraphNode<?>> levelNodes = getNodesAtLevel(i); if (levelNodes.size() <= 1) { continue; } // rank same if (isRanked) { rankedNodes.append("{ rank = same; ") .append(levelNodes.stream() .map(node -> String.valueOf(node.getId())) .collect(Collectors.joining(";"))) .append(" }") .append(lineSep); } // invisible arrows for ordering if (isSorted) { rankedNodes.append(levelNodes.stream() .sorted(Comparator.comparingInt(GraphNode::getId)) .map(node -> String.valueOf(node.getId())) Loading @@ -149,10 +161,11 @@ public class PDGGraph extends Graph { .append("[style = invis];") .append(lineSep); } } String arrows = getArcs().stream() .sorted(Comparator.comparingInt(arrow -> ((GraphNode) arrow.getFrom()).getId())) .sorted(Comparator.comparingInt(arrow -> ((GraphNode<?>) arrow.getFrom()).getId())) .map(Arc::toGraphvizRepresentation) .collect(Collectors.joining(lineSep)); Loading @@ -173,22 +186,7 @@ public class PDGGraph extends Graph { 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<CFGNode<?>> definitionNodes = Utils.findLastDefinitionsFrom(cfgNode, slicingCriterion.getVariable()); // // Logger.format("Slicing node: %s", node); // // // Get slice nodes from definition nodes // Set<Integer> sliceNodes = definitionNodes.stream() // .flatMap(definitionNode -> getSliceNodes(new HashSet<>(), this.findNodeByASTNode(definitionNode.getAstNode()).get()).stream()) // .collect(Collectors.toSet()); // // sliceNodes.add(node.getId()); GraphNode<?> node = optionalGraphNode.get(); // Simply get slice nodes from GraphNode Set<Integer> sliceNodes = getSliceNodes(new HashSet<>(), node); Loading @@ -199,7 +197,7 @@ public class PDGGraph extends Graph { astCopy.accept(new PDGBuilder(sliceGraph), sliceGraph.getRootNode()); for (GraphNode sliceNode : sliceGraph.getNodes()) { for (GraphNode<?> sliceNode : sliceGraph.getNodes()) { if (!sliceNodes.contains(sliceNode.getId())) { Logger.log("Removing node " + sliceNode.getId()); sliceNode.getAstNode().removeForced(); Loading @@ -207,37 +205,17 @@ public class PDGGraph extends Graph { } } // for (Arc arc : getArcs()) { // Optional<GraphNode> fromOptional = sliceGraph.findNodeById(arc.getFromNode().getId()); // Optional<GraphNode> 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<Integer> getSliceNodes(Set<Integer> visited, GraphNode<?> root) { visited.add(root.getId()); for (Arrow arrow : root.getIncomingArcs()) { Arc arc = (Arc) arrow; GraphNode<?> from = (GraphNode) arc.getFromNode(); for (Arc<ArcData> arc : root.getIncomingArcs()) { GraphNode<?> from = arc.getFromNode(); if (visited.contains(from.getId())) { if (visited.contains(from.getId())) continue; } getSliceNodes(visited, from); } Loading
src/main/java/tfm/nodes/GraphNode.java +11 −2 Original line number Diff line number Diff line Loading @@ -14,6 +14,14 @@ import tfm.variables.VariableExtractor; import java.util.*; import java.util.stream.Collectors; /** * Represents a node in the various graphs ({@link tfm.graphs.CFGGraph CFG}, * {@link tfm.graphs.PDGGraph PDG} and {@link tfm.graphs.SDGGraph 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. * @param <N> The type of the AST represented by this node. */ public class GraphNode<N extends Node> extends Vertex<String, ArcData> { private int id; Loading Loading @@ -135,9 +143,10 @@ public class GraphNode<N extends Node> extends Vertex<String, ArcData> { if (!(o instanceof GraphNode)) return false; GraphNode other = (GraphNode) o; GraphNode<?> other = (GraphNode<?>) o; return Objects.equals(getData(), other.getData()) return this.getId() == other.getId() && Objects.equals(getData(), other.getData()) && Objects.equals(astNode, other.astNode); // && Objects.equals(getIncomingArrows(), other.getIncomingArrows()) // && Objects.equals(getOutgoingArrows(), other.getOutgoingArrows()) Loading
src/main/java/tfm/visitors/pdg/ControlDependencyBuilder.java +88 −78 Original line number Diff line number Diff line package tfm.visitors.pdg; import com.github.javaparser.ast.stmt.*; import com.github.javaparser.ast.visitor.VoidVisitorAdapter; import tfm.arcs.Arc; import tfm.arcs.data.ArcData; import tfm.graphs.CFGGraph; import tfm.graphs.PDGGraph; import tfm.nodes.GraphNode; import java.util.stream.Collectors; public class ControlDependencyBuilder extends VoidVisitorAdapter<GraphNode> { private CFGGraph cfgGraph; private PDGGraph pdgGraph; public ControlDependencyBuilder(PDGGraph pdgGraph, CFGGraph cfgGraph) { this.pdgGraph = pdgGraph; this.cfgGraph = cfgGraph; import java.util.*; /** * A simple but slow finder of control dependencies. * <br/> * 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: * <br/> * A node <i>b</i> is control dependent on another node <i>a</i> if and only if <i>b</i> postdominates * one but not all of the successors of <i>a</i>. * <br/> * A node <i>b</i> postdominates another node <i>a</i> if and only if <i>b</i> appears in every path * from <i>a</i> to the "Exit" node. * <br/> * There exist better, cheaper approaches that have linear complexity w.r.t. the number of edges in the CFG. * <b>Usage:</b> pass an empty {@link PDGGraph} and a filled {@link CFGGraph} and then run {@link #analyze()}. * This builder should only be used once, and then discarded. */ public class ControlDependencyBuilder { private final PDGGraph pdg; private final CFGGraph cfg; public ControlDependencyBuilder(PDGGraph pdg, CFGGraph cfg) { this.pdg = pdg; this.cfg = cfg; } @Override public void visit(ExpressionStmt expressionStmt, GraphNode parent) { addNodeAndControlDependency(expressionStmt, parent); public void analyze() { Map<GraphNode<?>, GraphNode<?>> nodeMap = new HashMap<>(); nodeMap.put(cfg.getRootNode(), pdg.getRootNode()); Set<GraphNode<?>> roots = new HashSet<>(cfg.getNodes()); roots.remove(cfg.getRootNode()); Set<GraphNode<?>> cfgNodes = new HashSet<>(cfg.getNodes()); cfgNodes.removeIf(node -> node.getData().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)); if (pdg.contains(src)) roots.remove(dest); } @Override public void visit(IfStmt ifStmt, GraphNode parent) { GraphNode node = addNodeAndControlDependency(ifStmt, parent); ifStmt.getThenStmt().accept(this, node); ifStmt.getElseStmt().ifPresent(statement -> statement.accept(this, node)); } @Override public void visit(WhileStmt whileStmt, GraphNode parent) { GraphNode node = addNodeAndControlDependency(whileStmt, parent); whileStmt.getBody().accept(this, node); } @Override public void visit(ForStmt forStmt, GraphNode parent) { String initialization = forStmt.getInitialization().stream() .map(com.github.javaparser.ast.Node::toString) .collect(Collectors.joining(",")); String update = forStmt.getUpdate().stream() .map(com.github.javaparser.ast.Node::toString) .collect(Collectors.joining(",")); String compare = forStmt.getCompare() .map(com.github.javaparser.ast.Node::toString) .orElse("true"); GraphNode forNode = pdgGraph.addNode( String.format("for (%s;%s;%s)", initialization, compare, update), forStmt ); pdgGraph.addControlDependencyArc(parent, forNode); forStmt.getBody().accept(this, forNode); // In the original definition, nodes were dependent by default on the Enter/Start node for (GraphNode<?> node : roots) if (!node.getData().equals("Exit")) pdg.addControlDependencyArc(pdg.getRootNode(), nodeMap.get(node)); } @Override public void visit(ForEachStmt forEachStmt, GraphNode parent) { GraphNode node = addNodeAndControlDependency(forEachStmt, parent); forEachStmt.getBody().accept(this, node); public void registerNode(GraphNode<?> node, Map<GraphNode<?>, GraphNode<?>> nodeMap) { if (nodeMap.containsKey(node) || node.getData().equals("Exit")) return; GraphNode<?> clone = new GraphNode<>(node.getId(), node.getData(), node.getAstNode()); nodeMap.put(node, clone); pdg.addNode(clone); } @Override public void visit(SwitchStmt switchStmt, GraphNode parent) { GraphNode node = addNodeAndControlDependency(switchStmt, parent); switchStmt.getEntries().accept(this, node); public static boolean hasControlDependence(GraphNode<?> a, GraphNode<?> b) { int yes = 0; List<Arc<ArcData>> list = a.getOutgoingArcs(); // Nodes with less than 1 outgoing arc cannot control another node. if (list.size() < 2) return false; for (Arc<ArcData> arc : list) { GraphNode<?> successor = arc.getToNode(); if (postdominates(successor, b)) yes++; } @Override public void visit(SwitchEntryStmt switchEntryStmt, GraphNode parent) { GraphNode node = addNodeAndControlDependency(switchEntryStmt, parent); switchEntryStmt.getStatements().accept(this, node); int no = list.size() - yes; return yes > 0 && no > 0; } private GraphNode addNodeAndControlDependency(Statement statement, GraphNode parent) { GraphNode<?> cfgNode = cfgGraph.findNodeByASTNode(statement).get(); GraphNode node = pdgGraph.addNode(cfgNode.getData(), cfgNode.getAstNode()); pdgGraph.addControlDependencyArc(parent, node); public static boolean postdominates(GraphNode<?> a, GraphNode<?> b) { return postdominates(a, b, new HashSet<>()); } return node; private static boolean postdominates(GraphNode<?> a, GraphNode<?> b, Set<GraphNode<?>> visited) { // Stop w/ success if a == b or a has already been visited if (a.equals(b) || visited.contains(a)) return true; List<Arc<ArcData>> outgoing = a.getOutgoingArcs(); // Stop w/ failure if there are no edges to traverse from a if (outgoing.size() == 0) return false; // Find all possible paths starting from a, if ALL find b, then true, else false visited.add(a); for (Arc<ArcData> out : outgoing) { if (!postdominates(out.getToNode(), b, visited)) return false; } return true; } }
src/main/java/tfm/visitors/pdg/PDGBuilder.java +4 −4 Original line number Diff line number Diff line Loading @@ -14,15 +14,15 @@ import tfm.visitors.cfg.CFGBuilder; * <br/> * <b>Usage:</b> * <ol> * <li>Create and fill a {@link CFGGraph} for the desired method.</li> * <li>Create an empty {@link CFGGraph}.</li> * <li>Create an empty {@link PDGGraph} (optionally passing the {@link CFGGraph} as argument).</li> * <li>Create a new {@link PDGBuilder}, passing both graphs as arguments.</li> * <li>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)}</li> * <li>Once the previous step is finished, the complete PDG is saved in * the object created in the second step. <emph>The builder should be discarded * and not reused.</emph></li> * the object created in the second step. The builder should be discarded * and not reused.</li> * </ol> */ public class PDGBuilder extends VoidVisitorAdapter<GraphNode<?>> { Loading Loading @@ -60,7 +60,7 @@ public class PDGBuilder extends VoidVisitorAdapter<GraphNode<?>> { // Build control dependency ControlDependencyBuilder controlDependencyBuilder = new ControlDependencyBuilder(pdgGraph, cfgGraph); methodBody.accept(controlDependencyBuilder, parent); controlDependencyBuilder.analyze(); // Build data dependency DataDependencyBuilder dataDependencyBuilder = new DataDependencyBuilder(pdgGraph, cfgGraph); Loading