From f8183adff8ce513500d3bb8afb52c85d386d00c6 Mon Sep 17 00:00:00 2001 From: Javier Costa Date: Mon, 13 Jan 2020 02:41:38 +0100 Subject: [PATCH 01/20] Replacing method calls --- .gitignore | 1 + .../java/tfm/visitors/sdg/NewSDGBuilder.java | 2 +- .../java/tfm/visitors/sdg/SDGBuilder.java | 1 + .../{ => methodcall}/MethodCallReplacer.java | 5 +- .../MethodCallReplacerVisitor.java | 86 ++++++++++++++++--- 5 files changed, 80 insertions(+), 15 deletions(-) rename src/main/java/tfm/visitors/sdg/{ => methodcall}/MethodCallReplacer.java (81%) rename src/main/java/tfm/visitors/sdg/{ => methodcall}/MethodCallReplacerVisitor.java (67%) diff --git a/.gitignore b/.gitignore index d947638..fc21651 100644 --- a/.gitignore +++ b/.gitignore @@ -2,3 +2,4 @@ .idea/ target/ out/ +.settings/ \ No newline at end of file diff --git a/src/main/java/tfm/visitors/sdg/NewSDGBuilder.java b/src/main/java/tfm/visitors/sdg/NewSDGBuilder.java index 8938632..fb6ad4d 100644 --- a/src/main/java/tfm/visitors/sdg/NewSDGBuilder.java +++ b/src/main/java/tfm/visitors/sdg/NewSDGBuilder.java @@ -4,11 +4,11 @@ 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.utils.Context; +import tfm.visitors.sdg.methodcall.MethodCallReplacer; public class NewSDGBuilder extends VoidVisitorAdapter { diff --git a/src/main/java/tfm/visitors/sdg/SDGBuilder.java b/src/main/java/tfm/visitors/sdg/SDGBuilder.java index 9a51186..9935269 100644 --- a/src/main/java/tfm/visitors/sdg/SDGBuilder.java +++ b/src/main/java/tfm/visitors/sdg/SDGBuilder.java @@ -26,6 +26,7 @@ import java.util.Optional; * Asumimos que procesamos 1 archivo con una o más clases donde el primer método de la primera clase es el main * */ +@Deprecated public class SDGBuilder extends VoidVisitorAdapter { SDGGraph sdgGraph; diff --git a/src/main/java/tfm/visitors/sdg/MethodCallReplacer.java b/src/main/java/tfm/visitors/sdg/methodcall/MethodCallReplacer.java similarity index 81% rename from src/main/java/tfm/visitors/sdg/MethodCallReplacer.java rename to src/main/java/tfm/visitors/sdg/methodcall/MethodCallReplacer.java index 01b853f..dbc73b7 100644 --- a/src/main/java/tfm/visitors/sdg/MethodCallReplacer.java +++ b/src/main/java/tfm/visitors/sdg/methodcall/MethodCallReplacer.java @@ -1,9 +1,6 @@ -package tfm.visitors.sdg; +package tfm.visitors.sdg.methodcall; -import com.github.javaparser.ast.body.MethodDeclaration; -import tfm.graphs.PDGGraph; import tfm.graphs.SDGGraph; -import tfm.utils.Context; public class MethodCallReplacer { diff --git a/src/main/java/tfm/visitors/sdg/MethodCallReplacerVisitor.java b/src/main/java/tfm/visitors/sdg/methodcall/MethodCallReplacerVisitor.java similarity index 67% rename from src/main/java/tfm/visitors/sdg/MethodCallReplacerVisitor.java rename to src/main/java/tfm/visitors/sdg/methodcall/MethodCallReplacerVisitor.java index eb32ef0..88b5df0 100644 --- a/src/main/java/tfm/visitors/sdg/MethodCallReplacerVisitor.java +++ b/src/main/java/tfm/visitors/sdg/methodcall/MethodCallReplacerVisitor.java @@ -1,11 +1,14 @@ -package tfm.visitors.sdg; +package tfm.visitors.sdg.methodcall; import com.github.javaparser.ast.Node; import com.github.javaparser.ast.body.ClassOrInterfaceDeclaration; import com.github.javaparser.ast.body.MethodDeclaration; import com.github.javaparser.ast.body.VariableDeclarator; +import com.github.javaparser.ast.expr.AssignExpr; import com.github.javaparser.ast.expr.MethodCallExpr; +import com.github.javaparser.ast.expr.StringLiteralExpr; import com.github.javaparser.ast.expr.VariableDeclarationExpr; +import com.github.javaparser.ast.nodeTypes.NodeWithSimpleName; import com.github.javaparser.ast.stmt.ExpressionStmt; import com.github.javaparser.ast.type.Type; import com.github.javaparser.ast.visitor.VoidVisitorAdapter; @@ -13,10 +16,10 @@ import tfm.graphs.PDGGraph; import tfm.nodes.GraphNode; import tfm.utils.Context; import tfm.utils.Logger; +import tfm.utils.Utils; +import tfm.variables.actions.VariableDeclaration; -import java.util.List; -import java.util.Objects; -import java.util.Optional; +import java.util.*; import java.util.stream.Collectors; public class MethodCallReplacerVisitor extends VoidVisitorAdapter { @@ -38,19 +41,82 @@ public class MethodCallReplacerVisitor extends VoidVisitorAdapter { return; } - // todo make call + /* TODO + + 1. Get parameters of the method declaration + + 2. Parse arguments of the method call + + 3. Build in and out assignment expressions + + 4. Add in and out variable nodes + + 5. Compute data dependency of new nodes + + */ + + MethodDeclaration callingMethod = optionalCallingMethod.get(); + + // 1 + List parameterNames = callingMethod.getParameters().stream() + .map(NodeWithSimpleName::getNameAsString) + .collect(Collectors.toList()); + + // todo: Add global variables + + // 2 + List argumentNames = methodCallExpr.getArguments().stream() + .map(Node::toString) // todo: argument can be a numeric, string, or method call expression. Must differentiate between them + .collect(Collectors.toList()); + + // todo: Add global variables + + // 3 + if (parameterNames.size() != argumentNames.size()) { + // todo: The last parameter is varargs: wrap the last n arguments in an array + } + + List assignExprs = Utils.emptyList(); + + // IN assignments + for (int i = 0; i < parameterNames.size(); i++) { + AssignExpr assignExpr = + new AssignExpr( + new StringLiteralExpr(parameterNames.get(i) + "_in"), + new StringLiteralExpr(argumentNames.get(i)), + AssignExpr.Operator.ASSIGN + ); + + assignExprs.add(assignExpr); + } + + // OUT assignments + // todo + + // 4 + int lastId = pdgGraph.getNodes().stream() + .map(GraphNode::getId) + .max(Integer::compareTo) + .orElse(-1); + + for (AssignExpr assignExpr : assignExprs) { + pdgGraph.addNode(new GraphNode(lastId + 1, assignExpr.toString(), assignExpr)); + } + + // 5 + // todo + // For IN, get last defined variable + // For OUT, get first used variable + Logger.log(String.format("Method '%s' called", optionalCallingMethod.get().getNameAsString())); } private Optional shouldMakeCallWithScope(MethodCallExpr methodCallExpr, Context context) { assert methodCallExpr.getScope().isPresent(); + assert context.getCurrentClass().isPresent(); String scopeName = methodCallExpr.getScope().get().toString(); - if (!context.getCurrentClass().isPresent()) { - return Optional.empty(); - } - ClassOrInterfaceDeclaration currentClass = context.getCurrentClass().get(); // Check if it's a static method call of current class @@ -60,7 +126,7 @@ public class MethodCallReplacerVisitor extends VoidVisitorAdapter { List> declarations = pdgGraph.findDeclarationsOfVariable(scopeName); if (declarations.isEmpty()) { - // It is a static method call of another class. We do nothing + // It is a static method call of another class TODO: check other classes in the same file (compilation unit) return Optional.empty(); } -- GitLab From 898edf196b69a9f84535510559b24ed8cdd841a7 Mon Sep 17 00:00:00 2001 From: jacosro Date: Sun, 16 Feb 2020 20:06:14 +0100 Subject: [PATCH 02/20] new changes --- src/main/java/tfm/arcs/Arc.java | 24 +- src/main/java/tfm/arcs/sdg/CallArc.java | 16 ++ .../java/tfm/arcs/sdg/ParameterInOutArc.java | 16 ++ src/main/java/tfm/exec/Main.java | 6 +- .../java/tfm/graphs/GraphWithRootNode.java | 53 +++++ src/main/java/tfm/graphs/SDGGraph.java | 91 +++---- src/main/java/tfm/graphs/pdg/PDG.java | 82 +++++++ .../tfm/graphs/pdg/PostdominatorTree.java | 223 ++++++++++++++++++ .../graphs/sdg/MethodDeclarationReplacer.java | 12 + src/main/java/tfm/graphs/sdg/SDGBuilder.java | 136 +++++++++++ src/main/java/tfm/nodes/IdHelper.java | 22 ++ .../java/tfm/nodes/InOutVariableNode.java | 25 ++ src/main/java/tfm/nodes/MethodCallNode.java | 42 +--- src/main/java/tfm/nodes/MethodRootNode.java | 17 ++ .../tfm/nodes/NodeWithInOutVariables.java | 50 ++++ .../java/tfm/visitors/sdg/NewSDGBuilder.java | 69 ------ .../sdg/methodcall/MethodCallReplacer.java | 16 +- 17 files changed, 718 insertions(+), 182 deletions(-) create mode 100644 src/main/java/tfm/arcs/sdg/CallArc.java create mode 100644 src/main/java/tfm/arcs/sdg/ParameterInOutArc.java create mode 100644 src/main/java/tfm/graphs/GraphWithRootNode.java create mode 100644 src/main/java/tfm/graphs/pdg/PDG.java create mode 100644 src/main/java/tfm/graphs/pdg/PostdominatorTree.java create mode 100644 src/main/java/tfm/graphs/sdg/MethodDeclarationReplacer.java create mode 100644 src/main/java/tfm/graphs/sdg/SDGBuilder.java create mode 100644 src/main/java/tfm/nodes/IdHelper.java create mode 100644 src/main/java/tfm/nodes/InOutVariableNode.java create mode 100644 src/main/java/tfm/nodes/MethodRootNode.java create mode 100644 src/main/java/tfm/nodes/NodeWithInOutVariables.java delete mode 100644 src/main/java/tfm/visitors/sdg/NewSDGBuilder.java diff --git a/src/main/java/tfm/arcs/Arc.java b/src/main/java/tfm/arcs/Arc.java index 5c13d31..a961fc0 100644 --- a/src/main/java/tfm/arcs/Arc.java +++ b/src/main/java/tfm/arcs/Arc.java @@ -1,6 +1,11 @@ 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.arcs.sdg.CallArc; import tfm.nodes.GraphNode; import java.util.Objects; @@ -37,8 +42,21 @@ public abstract class Arc extends edg.graphlib.Arrow getFromNode() { - return (GraphNode) super.getFrom(); + /** @see CallArc */ + public final boolean isCallArc() { + return this instanceof CallArc; + } + + public final CallArc asCallArc() { + if (isCallArc()) + return (CallArc) this; + throw new UnsupportedOperationException("Not a CallArc"); + } + + @Override + public String toString() { + return String.format("%s{%d -> %d}", getClass().getName(), + ((GraphNode) getSource()).getId(), ((GraphNode) getTarget()).getId()); } public GraphNode getToNode() { diff --git a/src/main/java/tfm/arcs/sdg/CallArc.java b/src/main/java/tfm/arcs/sdg/CallArc.java new file mode 100644 index 0000000..43c4138 --- /dev/null +++ b/src/main/java/tfm/arcs/sdg/CallArc.java @@ -0,0 +1,16 @@ +package tfm.arcs.sdg; + +import org.jgrapht.io.Attribute; +import org.jgrapht.io.DefaultAttribute; +import tfm.arcs.Arc; + +import java.util.Map; + +public class CallArc extends Arc { + @Override + public Map getDotAttributes() { + Map map = super.getDotAttributes(); + map.put("style", DefaultAttribute.createAttribute("dashed")); + return map; + } +} diff --git a/src/main/java/tfm/arcs/sdg/ParameterInOutArc.java b/src/main/java/tfm/arcs/sdg/ParameterInOutArc.java new file mode 100644 index 0000000..50673b7 --- /dev/null +++ b/src/main/java/tfm/arcs/sdg/ParameterInOutArc.java @@ -0,0 +1,16 @@ +package tfm.arcs.sdg; + +import org.jgrapht.io.Attribute; +import org.jgrapht.io.DefaultAttribute; +import tfm.arcs.Arc; + +import java.util.Map; + +public class ParameterInOutArc extends Arc { + @Override + public Map getDotAttributes() { + Map map = super.getDotAttributes(); + map.put("style", DefaultAttribute.createAttribute("dashed")); + return map; + } +} diff --git a/src/main/java/tfm/exec/Main.java b/src/main/java/tfm/exec/Main.java index 9532105..31f920b 100644 --- a/src/main/java/tfm/exec/Main.java +++ b/src/main/java/tfm/exec/Main.java @@ -2,6 +2,7 @@ package tfm.exec; import com.github.javaparser.JavaParser; import com.github.javaparser.ast.Node; +import com.github.javaparser.ast.NodeList; import com.github.javaparser.ast.body.MethodDeclaration; import tfm.utils.Logger; import tfm.utils.Utils; @@ -63,8 +64,9 @@ public class Main { graphLog = new PDGLog(); break; case GraphLog.SDG: - graphLog = new SDGLog(); - break; + SDG sdg = new SDG(); + sdg.build(new NodeList<>(method.findCompilationUnit().get())); + return sdg; default: Logger.log("Unkown graph type"); System.exit(1); diff --git a/src/main/java/tfm/graphs/GraphWithRootNode.java b/src/main/java/tfm/graphs/GraphWithRootNode.java new file mode 100644 index 0000000..3061e60 --- /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/SDGGraph.java b/src/main/java/tfm/graphs/SDGGraph.java index ec4692b..ab63448 100644 --- a/src/main/java/tfm/graphs/SDGGraph.java +++ b/src/main/java/tfm/graphs/SDGGraph.java @@ -2,21 +2,28 @@ package tfm.graphs; import com.github.javaparser.ast.Node; import com.github.javaparser.ast.body.MethodDeclaration; -import com.github.javaparser.ast.body.Parameter; -import com.github.javaparser.ast.stmt.EmptyStmt; -import tfm.nodes.GraphNode; +import tfm.arcs.pdg.ControlDependencyArc; +import tfm.arcs.pdg.DataDependencyArc; +import tfm.arcs.sdg.CallArc; +import tfm.arcs.sdg.ParameterInOutArc; +import tfm.graphs.Buildable; +import tfm.graphs.Graph; +import tfm.nodes.*; +import tfm.slicing.Slice; +import tfm.slicing.Sliceable; import tfm.slicing.SlicingCriterion; import tfm.utils.Context; import java.util.*; 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 contextToRootNodeId; - public SDGGraph() { - this.contextPDGGraphMap = new HashMap<>(); + public SDG() { + this.contextToRootNodeId = new HashMap<>(); } @Override @@ -28,9 +35,8 @@ public class SDGGraph extends Graph { } @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), new Context()); } @Override @@ -38,66 +44,29 @@ public class SDGGraph extends Graph { return this; } - public Map getContextPDGGraphMap() { - return contextPDGGraphMap; - } - public Set getContexts() { - return contextPDGGraphMap.keySet(); + return contextToRootNodeId.keySet(); } - public Set getMethods() { - return getContexts().stream() - .filter(context -> context.getCurrentMethod().isPresent()) - .map(context -> context.getCurrentMethod().get()) - .collect(Collectors.toSet()); + public Optional> getRootNode(Context context) { + Integer id = this.contextToRootNodeId.get(context); + + return id != null ? findNodeById(id) : Optional.empty(); } - public Collection getPDGs() { - return contextPDGGraphMap.values(); + public void addControlDependencyArc(GraphNode from, GraphNode to) { + this.addEdge(from, to, new ControlDependencyArc()); } - @Deprecated - public void addPDG(PDGGraph pdgGraph, MethodDeclaration methodDeclaration) { - if (this.rootVertex == null) { - this.setRootVertex(new GraphNode<>(getNextVertexId(), methodDeclaration.getNameAsString(), methodDeclaration)); - } - - for (Parameter parameter : methodDeclaration.getParameters()) { - GraphNode sdgNode = new GraphNode<>( - getNextVertexId(), - String.format("%s = %s_in", parameter.getNameAsString(), parameter.getNameAsString()), - new EmptyStmt() - ); - - addVertex(sdgNode); - } - - for (GraphNode node : pdgGraph.getNodes()) { - if (!this.verticies.contains(node)) { - GraphNode sdgNode = new GraphNode<>( - getNextVertexId(), - node.getData(), - node.getAstNode(), - node.getIncomingArcs(), - node.getOutgoingArcs(), - node.getDeclaredVariables(), - node.getDefinedVariables(), - node.getUsedVariables() - ); - - addVertex(sdgNode); - } - } + public void addDataDependencyArc(GraphNode from, GraphNode to, String variable) { + this.addEdge(from, to, new DataDependencyArc(variable)); } - public void addMethod(MethodDeclaration methodDeclaration, PDGGraph pdgGraph) { - GraphNode methodRootNode = new GraphNode<>( - getNextVertexId(), - "ENTER " + methodDeclaration.getDeclarationAsString(false, false, true), - methodDeclaration - ); + public void addCallArc(MethodCallNode from, MethodRootNode to) { + this.addEdge(from, to, new CallArc()); + } - super.addVertex(methodRootNode); + public void addParameterInOutArc(InOutVariableNode from, InOutVariableNode to) { + this.addEdge(from, to, new ParameterInOutArc()); } } 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 0000000..e4b35e6 --- /dev/null +++ b/src/main/java/tfm/graphs/pdg/PDG.java @@ -0,0 +1,82 @@ +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.nodes.MethodRootNode; +import tfm.nodes.NodeFactory; +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/PostdominatorTree.java b/src/main/java/tfm/graphs/pdg/PostdominatorTree.java new file mode 100644 index 0000000..81fce86 --- /dev/null +++ b/src/main/java/tfm/graphs/pdg/PostdominatorTree.java @@ -0,0 +1,223 @@ +package tfm.graphs.pdg; + +import com.github.javaparser.ast.Node; +import org.jgrapht.graph.DefaultEdge; +import org.jgrapht.traverse.DepthFirstIterator; +import tfm.arcs.Arc; +import tfm.graphs.Graph; +import tfm.graphs.GraphWithRootNode; +import tfm.graphs.cfg.CFG; +import tfm.nodes.GraphNode; + +import java.util.*; +import java.util.stream.Collectors; + +public class PostdominatorTree extends DirectedTree, DefaultEdge> { + private Map routeMap; +// private Map, Set> servedByMap; + + public PostdominatorTree(CFG cfg) { + super(null, DefaultEdge::new, false); + // Vertices + cfg.vertexSet().forEach(this::addVertex); + // Edges + Map, GraphNode> map = immediatePostdominatorTree(cfg); + for (Map.Entry, GraphNode> entry : map.entrySet()) + addEdge(entry.getValue(), entry.getKey()); + // Set root + for (GraphNode node : vertexSet()) { + if (inDegreeOf(node) == 0) { + if (root == null) + root = node; + else throw new IllegalStateException("Multiple roots found!"); + } + } + if (root == null) + throw new IllegalStateException("No root found!"); + // Build route map and cache + routeMap = constructRomanChariots(cfg); +// servedByMap = constructCache(); + } + + private GraphNode parentOf(GraphNode node) { + Set edges = incomingEdgesOf(node); + if (edges.size() > 1) + throw new IllegalStateException("Node has multiple parents!"); + if (edges.isEmpty()) + throw new IllegalStateException("Node has no parents! Don't call this method on the root node!"); + return getEdgeSource(edges.iterator().next()); + } + + private Set> childrenOf(GraphNode node) { + return outgoingEdgesOf(node).stream().map(this::getEdgeTarget).collect(Collectors.toSet()); + } + + /** The set of nodes controlled by the arc */ + protected Set> cd(Graph graph, Arc arc) { + GraphNode u = graph.getEdgeSource(arc); + GraphNode v = graph.getEdgeTarget(arc); + if (v.equals(parentOf(u))) + return Collections.emptySet(); + if (!routeMap.containsKey(arc)) + throw new IllegalArgumentException("Arc is not valid!"); + return cd(routeMap.get(arc)); + } + + /** The nodes traversed by the route */ + private Set> cd(Route r) { + GraphNode w = r.dest; + Set> cdSet = new HashSet<>(); + for (GraphNode v = r.src; !v.equals(w) && !v.equals(root); v = parentOf(v)) + cdSet.add(v); + return cdSet; + } + + /** The set of nodes that is controlled by the node. + * Equivalent to the routes that serve the node. */ + protected Set> conds(CFG cfg, GraphNode w) { + Set> res = new HashSet<>(); + for (Arc arc : cfg.outgoingEdgesOf(w)) + if (routeMap.containsKey(arc)) + res.addAll(cd(cfg, arc)); + return res; + } + + private List> inTopDownOrder() { + Iterator> it = new DepthFirstIterator<>(this, root); + List> list = new LinkedList<>(); + it.forEachRemaining(node -> { + if (!list.contains(node)) + list.add(node); + }); + return list; + } + + public Set> controlDependenciesOf(CFG cfg, GraphNode node) { + return conds(cfg, node); + } + + private Map constructRomanChariots(CFG cfg) { + Map routes = new HashMap<>(); + for (GraphNode p : inTopDownOrder()) { + for (GraphNode u : childrenOf(p)) { + for (Arc arc : cfg.outgoingEdgesOf(u)) { + GraphNode v = cfg.getEdgeTarget(arc); + if (!v.equals(p)) { + // Append a `cd` set to end of routes + routes.put(arc, new Route(v, p)); + } + } + } + } + return routes; + } + + private static class Route { + GraphNode src; + GraphNode dest; + + public Route(GraphNode src, GraphNode dest) { + this.src = src; + this.dest = dest; + } + + @Override + public String toString() { + return String.format("[%d, %d)", src.getId(), dest.getId()); + } + } + + private Map, Set> constructCache() { + Map, Set> map = new HashMap<>(); + for (Route r : routeMap.values()) { + for (GraphNode n : cd(r)) { + if (!map.containsKey(n)) + map.put(n, new HashSet<>()); + map.get(n).add(r); + } + } + return map; + } + + private static Map, GraphNode> immediatePostdominatorTree(CFG cfg) { + Optional> optExitNode = cfg.vertexSet().stream().filter(gn -> gn.getInstruction().equals("Exit")).findFirst(); + if (!optExitNode.isPresent()) + throw new IllegalStateException("CFG lacks exit node"); + GraphNode exitNode = optExitNode.get(); + Map, GraphNode> postdoms = new HashMap<>(); + List> postorderList = postorder(cfg); + Map, Integer> sortingMap = new HashMap<>(); + for (int i = 0; i < postorderList.size(); i++) + sortingMap.put(postorderList.get(i), i); + postorderList.remove(exitNode); + postdoms.put(exitNode, exitNode); + boolean changed = true; + while (changed) { + changed = false; + for (GraphNode node : postorderList) { + GraphNode newIpostdom = null; + for (Arc arc : cfg.outgoingEdgesOf(node)) { + GraphNode successor = cfg.getEdgeTarget(arc); + if (newIpostdom == null && postdoms.containsKey(successor)) + newIpostdom = successor; + if (newIpostdom != null && postdoms.containsKey(successor)) + newIpostdom = reverseIntersect(successor, newIpostdom, postdoms, sortingMap); + } + if (postdoms.get(node) != newIpostdom) { + postdoms.put(node, newIpostdom); + changed = true; + } + } + } + postdoms.remove(exitNode); + return postdoms; + } + + private static GraphNode reverseIntersect(final GraphNode b1, final GraphNode b2, + final Map, GraphNode> postdoms, + final Map, Integer> sortingMap) { + GraphNode finger1 = b1; + GraphNode finger2 = b2; + while (finger1 != finger2) { + while (compare(finger1, finger2, sortingMap) > 0) + finger1 = postdoms.get(finger1); + while (compare(finger2, finger1, sortingMap) > 0) + finger2 = postdoms.get(finger2); + } + return finger1; + } + + private static int compare(GraphNode b1, GraphNode b2, Map, Integer> sortingMap) { + return Integer.compare(sortingMap.get(b1), sortingMap.get(b2)); + } + + private static List> postorder(GraphWithRootNode graph) { + Optional> rootNode = graph.getRootNode(); + if (!rootNode.isPresent()) + throw new IllegalStateException("CFG lacks root node"); + return postorder(graph, rootNode.get()); + } + + private static List postorder(org.jgrapht.Graph graph, V startVertex) { + List dfsOrder = new LinkedList<>(); + Iterator it = new DepthFirstIterator<>(graph, startVertex); + it.forEachRemaining(dfsOrder::add); + + List postorder = new LinkedList<>(); + for (V v : dfsOrder) + if (!postorder.contains(v)) + postorder.add(v); + return postorder; + } + + @Override + public String toString() { + StringBuilder builder = new StringBuilder("PostdominatorTree"); + for (DefaultEdge edge : edgeSet()) + builder.append(getEdgeSource(edge).getId()) + .append(" -> ") + .append(getEdgeTarget(edge).getId()) + .append('\n'); + return builder.toString(); + } +} diff --git a/src/main/java/tfm/graphs/sdg/MethodDeclarationReplacer.java b/src/main/java/tfm/graphs/sdg/MethodDeclarationReplacer.java new file mode 100644 index 0000000..ca0b02b --- /dev/null +++ b/src/main/java/tfm/graphs/sdg/MethodDeclarationReplacer.java @@ -0,0 +1,12 @@ +package tfm.graphs.sdg; + +import com.github.javaparser.ast.body.MethodDeclaration; +import com.github.javaparser.ast.visitor.VoidVisitorAdapter; + +public class MethodDeclarationReplacer extends VoidVisitorAdapter { + + @Override + public void visit(MethodDeclaration methodDeclaration, Void arg) { + + } +} 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 0000000..7aabd36 --- /dev/null +++ b/src/main/java/tfm/graphs/sdg/SDGBuilder.java @@ -0,0 +1,136 @@ +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.body.Parameter; +import com.github.javaparser.ast.expr.*; +import com.github.javaparser.ast.stmt.ExpressionStmt; +import com.github.javaparser.ast.visitor.VoidVisitorAdapter; +import tfm.arcs.Arc; +import tfm.graphs.pdg.PDG; +import tfm.nodes.GraphNode; +import tfm.utils.Context; + +import java.util.Map; +import java.util.Optional; + +class SDGBuilder extends VoidVisitorAdapter { + + SDG sdg; + + public SDGBuilder(SDG sdg) { + this.sdg = sdg; + } + + @Override + public void visit(MethodDeclaration methodDeclaration, Context context) { + if (!methodDeclaration.getBody().isPresent()) { + return; + } + + context.setCurrentMethod(methodDeclaration); + + // Build PDG and add to SDGGraph + PDG pdg = new PDG(); + pdg.build(methodDeclaration); + + for (GraphNode node : pdg.vertexSet()) { + sdg.addNode(node, false); + } + + for (Arc arc : pdg.edgeSet()) { + GraphNode from = pdg.getEdgeSource(arc); + GraphNode to = pdg.getEdgeTarget(arc); + + if (arc.isControlDependencyArc()) { + sdg.addControlDependencyArc(from, to); + } else { + sdg.addDataDependencyArc(from, to, arc.getLabel()); + } + } + + Optional> optionalMethodDeclarationNode = pdg.getRootNode(); + + if (!optionalMethodDeclarationNode.isPresent()) { + return; // Should not happen + } + + GraphNode methodDeclarationNode = optionalMethodDeclarationNode.get(); + + for (Parameter parameter : methodDeclaration.getParameters()) { + // In node + AssignExpr inAssignExpr = new AssignExpr(); + ExpressionStmt inExprStmt = new ExpressionStmt(inAssignExpr); + + inAssignExpr.setTarget( + new VariableDeclarationExpr( + parameter.getType(), + parameter.getNameAsString() + ) + ); + + inAssignExpr.setOperator(AssignExpr.Operator.ASSIGN); + + inAssignExpr.setValue(new NameExpr(parameter.getNameAsString() + "_in")); + + GraphNode inNode = sdg.addNode(inAssignExpr.toString(), inExprStmt); + + sdg.addControlDependencyArc(methodDeclarationNode, inNode); + + // Out node + AssignExpr outAssignExpr = new AssignExpr(); + ExpressionStmt outExprStmt = new ExpressionStmt(outAssignExpr); + + outAssignExpr.setTarget( + new VariableDeclarationExpr( + parameter.getType(), + parameter.getNameAsString() + "_out" + ) + ); + + outAssignExpr.setOperator(AssignExpr.Operator.ASSIGN); + + outAssignExpr.setValue(new NameExpr(parameter.getNameAsString())); + + GraphNode outNode = sdg.addNode(outAssignExpr.toString(), outExprStmt); + + sdg.addControlDependencyArc(methodDeclarationNode, outNode); + } + } + + @Override + public void visit(ClassOrInterfaceDeclaration classOrInterfaceDeclaration, Context context) { +// 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!"); + } + + context.setCurrentClass(classOrInterfaceDeclaration); + + classOrInterfaceDeclaration.getMembers().accept(this, context); + + // Once every PDG is built, expand method declaration nodes of each one + // todo methodDeclaration replacer + + + // 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(sdg); + methodCallReplacer.replace(); + + + + // 3. Build summary arcs + } + + @Override + public void visit(CompilationUnit compilationUnit, Context context) { + context.setCurrentCU(compilationUnit); + + super.visit(compilationUnit, context); + } +} diff --git a/src/main/java/tfm/nodes/IdHelper.java b/src/main/java/tfm/nodes/IdHelper.java new file mode 100644 index 0000000..27cb204 --- /dev/null +++ b/src/main/java/tfm/nodes/IdHelper.java @@ -0,0 +1,22 @@ +package tfm.nodes; + +class IdHelper { + + private static final int START_ID = 0; + + private static final IdHelper INSTANCE = new IdHelper(); + + private long nextId; + + private IdHelper() { + nextId = START_ID; + } + + public static IdHelper getInstance() { + return INSTANCE; + } + + public synchronized long getNextId() { + return nextId++; + } +} diff --git a/src/main/java/tfm/nodes/InOutVariableNode.java b/src/main/java/tfm/nodes/InOutVariableNode.java new file mode 100644 index 0000000..a5bbcf8 --- /dev/null +++ b/src/main/java/tfm/nodes/InOutVariableNode.java @@ -0,0 +1,25 @@ +package tfm.nodes; + +import com.github.javaparser.ast.stmt.ExpressionStmt; +import org.jetbrains.annotations.NotNull; + +import java.util.Collection; + +public class InOutVariableNode extends GraphNode { + + public InOutVariableNode(int id, String instruction, @NotNull ExpressionStmt astNode) { + super(id, instruction, astNode); + } + + public InOutVariableNode(int id, String instruction, @NotNull ExpressionStmt astNode, Collection declaredVariables, Collection definedVariables, Collection usedVariables) { + super(id, instruction, astNode, declaredVariables, definedVariables, usedVariables); + } + + public String getDeclaredVariable() { + return getDeclaredVariables().iterator().next(); + } + + public String getUsedVariable() { + return getUsedVariables().iterator().next(); + } +} diff --git a/src/main/java/tfm/nodes/MethodCallNode.java b/src/main/java/tfm/nodes/MethodCallNode.java index b241c5c..252f488 100644 --- a/src/main/java/tfm/nodes/MethodCallNode.java +++ b/src/main/java/tfm/nodes/MethodCallNode.java @@ -5,51 +5,15 @@ 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; - -public class MethodCallNode extends GraphNode { - - private List inParameters; - private List outParameters; - - public > MethodCallNode(N1 node) { - super(node); - - this.inParameters = new ArrayList<>(); - this.outParameters = new ArrayList<>(); - } +public class MethodCallNode extends NodeWithInOutVariables { public MethodCallNode(int id, String representation, ExpressionStmt node) { super(id, representation, node); - - this.inParameters = new ArrayList<>(); - this.outParameters = new ArrayList<>(); - } - - public MethodCallNode(int id, String representation, @NonNull ExpressionStmt node, Collection> incomingArcs, Collection> outgoingArcs, Set declaredVariables, Set definedVariables, Set usedVariables) { - super(id, representation, node, incomingArcs, outgoingArcs, declaredVariables, definedVariables, usedVariables); - - this.inParameters = new ArrayList<>(); - this.outParameters = new ArrayList<>(); - } - - public List getOutParameters() { - return outParameters; - } - - public void setOutParameters(List outParameters) { - this.outParameters = outParameters; - } - - public List getInParameters() { - return inParameters; } - public void setInParameters(List inParameters) { - this.inParameters = inParameters; + public MethodCallNode(int id, String representation, @NonNull ExpressionStmt node, Set declaredVariables, Set definedVariables, Set usedVariables) { + super(id, representation, node, declaredVariables, definedVariables, usedVariables); } } diff --git a/src/main/java/tfm/nodes/MethodRootNode.java b/src/main/java/tfm/nodes/MethodRootNode.java new file mode 100644 index 0000000..4f7cc13 --- /dev/null +++ b/src/main/java/tfm/nodes/MethodRootNode.java @@ -0,0 +1,17 @@ +package tfm.nodes; + +import com.github.javaparser.ast.body.MethodDeclaration; +import org.checkerframework.checker.nullness.qual.NonNull; + +import java.util.Set; + +public class MethodRootNode extends NodeWithInOutVariables { + + public MethodRootNode(int id, String representation, MethodDeclaration node) { + super(id, representation, node); + } + + public MethodRootNode(int id, String representation, @NonNull MethodDeclaration node, Set declaredVariables, Set definedVariables, Set usedVariables) { + super(id, representation, node, declaredVariables, definedVariables, usedVariables); + } +} diff --git a/src/main/java/tfm/nodes/NodeWithInOutVariables.java b/src/main/java/tfm/nodes/NodeWithInOutVariables.java new file mode 100644 index 0000000..00739b0 --- /dev/null +++ b/src/main/java/tfm/nodes/NodeWithInOutVariables.java @@ -0,0 +1,50 @@ +package tfm.nodes; + +import com.github.javaparser.ast.Node; +import org.checkerframework.checker.nullness.qual.NonNull; + +import java.util.*; + +public class NodeWithInOutVariables extends GraphNode { + + protected Map inVariablesMap; + protected Map outVariablesMap; + + protected NodeWithInOutVariables(int id, String representation, N node) { + super(id, representation, node); + + this.inVariablesMap = new HashMap<>(); + this.outVariablesMap = new HashMap<>(); + } + + protected NodeWithInOutVariables(int id, String representation, @NonNull N node, Set declaredVariables, Set definedVariables, Set usedVariables) { + super(id, representation, node, declaredVariables, definedVariables, usedVariables); + + this.inVariablesMap = new HashMap<>(); + this.outVariablesMap = new HashMap<>(); + } + + public Set getInVariables() { + return inVariablesMap.keySet(); + } + + public Optional getInNode(String variable) { + return Optional.ofNullable(inVariablesMap.get(variable)); + } + + public Set getOutVariables() { + return outVariablesMap.keySet(); + } + + public Optional getOutNode(String variable) { + return Optional.ofNullable(outVariablesMap.get(variable)); + } + + public void addInVariable(String variable, InOutVariableNode node) { + this.inVariablesMap.put(variable, node); + } + + public void addOutVariable(String variable, InOutVariableNode node) { + this.outVariablesMap.put(variable, node); + } +} diff --git a/src/main/java/tfm/visitors/sdg/NewSDGBuilder.java b/src/main/java/tfm/visitors/sdg/NewSDGBuilder.java deleted file mode 100644 index fb6ad4d..0000000 --- a/src/main/java/tfm/visitors/sdg/NewSDGBuilder.java +++ /dev/null @@ -1,69 +0,0 @@ -package tfm.visitors.sdg; - -import com.github.javaparser.ast.CompilationUnit; -import com.github.javaparser.ast.body.ClassOrInterfaceDeclaration; -import com.github.javaparser.ast.body.MethodDeclaration; -import com.github.javaparser.ast.visitor.VoidVisitorAdapter; -import tfm.graphbuilding.Graphs; -import tfm.graphs.PDGGraph; -import tfm.graphs.SDGGraph; -import tfm.utils.Context; -import tfm.visitors.sdg.methodcall.MethodCallReplacer; - -public class NewSDGBuilder extends VoidVisitorAdapter { - - SDGGraph sdgGraph; - - public NewSDGBuilder(SDGGraph sdgGraph) { - this.sdgGraph = sdgGraph; - } - - @Override - public void visit(MethodDeclaration methodDeclaration, Context context) { - if (!methodDeclaration.getBody().isPresent()) { - return; - } - - context.setCurrentMethod(methodDeclaration); - - // Build PDG and add to SDGGraph - PDGGraph pdgGraph = Graphs.PDG.fromASTNode(methodDeclaration); - - sdgGraph.addMethod(methodDeclaration, pdgGraph); - } - - @Override - public void visit(ClassOrInterfaceDeclaration classOrInterfaceDeclaration, Context context) { -// 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!"); - } - - context.setCurrentClass(classOrInterfaceDeclaration); - - classOrInterfaceDeclaration.accept(this, context); - - // Once every PDG is built, expand method declaration nodes of each one - // todo methodDeclaration replacer - - - // 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.replace(); - - - - // 3. Build summary arcs - } - - @Override - public void visit(CompilationUnit compilationUnit, Context context) { - context.setCurrentCU(compilationUnit); - - super.visit(compilationUnit, context); - } -} diff --git a/src/main/java/tfm/visitors/sdg/methodcall/MethodCallReplacer.java b/src/main/java/tfm/visitors/sdg/methodcall/MethodCallReplacer.java index dbc73b7..2429607 100644 --- a/src/main/java/tfm/visitors/sdg/methodcall/MethodCallReplacer.java +++ b/src/main/java/tfm/visitors/sdg/methodcall/MethodCallReplacer.java @@ -11,13 +11,13 @@ public class MethodCallReplacer { } public void replace() { - this.sdgGraph.getContextPDGGraphMap() - .forEach((context, pdgGraph) -> { - if (!context.getCurrentMethod().isPresent()) { - return; // Should NOT happen - } - - context.getCurrentMethod().get().accept(new MethodCallReplacerVisitor(pdgGraph), context); - }); +// this.sdg.getContextMethodRootMap() +// .forEach((context, pdgGraph) -> { +// if (!context.getCurrentMethod().isPresent()) { +// return; // Should NOT happen +// } +// +// context.getCurrentMethod().get().accept(new MethodCallReplacerVisitor(pdgGraph), context); +// }); } } -- GitLab From aaea10e1872ce0564bbda411617b71e91756c1b9 Mon Sep 17 00:00:00 2001 From: jacosro Date: Sun, 16 Feb 2020 19:47:39 +0100 Subject: [PATCH 03/20] Cheery-Pick: First approx: Changed id type to long and delegated id handling to IdHelper singleton --- src/main/java/tfm/exec/PDGLog.java | 2 +- src/main/java/tfm/graphs/Graph.java | 153 ++---------------- .../java/tfm/graphs/GraphWithRootNode.java | 16 +- src/main/java/tfm/graphs/cfg/CFG.java | 2 +- src/main/java/tfm/graphs/pdg/PDGBuilder.java | 8 +- .../tfm/graphs/pdg/PostdominatorTree.java | 4 +- src/main/java/tfm/nodes/GraphNode.java | 10 +- src/main/java/tfm/nodes/NodeFactory.java | 8 +- src/main/java/tfm/slicing/Slice.java | 6 +- src/test/java/tfm/graphs/pdg/PDGTests.java | 4 +- 10 files changed, 41 insertions(+), 172 deletions(-) diff --git a/src/main/java/tfm/exec/PDGLog.java b/src/main/java/tfm/exec/PDGLog.java index 5ed0162..af36803 100644 --- a/src/main/java/tfm/exec/PDGLog.java +++ b/src/main/java/tfm/exec/PDGLog.java @@ -29,7 +29,7 @@ public class PDGLog extends GraphLog { Logger.log("Nodes with variable info"); Logger.log(graph.vertexSet().stream() - .sorted(Comparator.comparingInt(GraphNode::getId)) + .sorted(Comparator.comparingLong(GraphNode::getId)) .map(node -> String.format("GraphNode { id: %s, instruction: %s, declared: %s, defined: %s, used: %s }", node.getId(), diff --git a/src/main/java/tfm/graphs/Graph.java b/src/main/java/tfm/graphs/Graph.java index e20452c..898e3ae 100644 --- a/src/main/java/tfm/graphs/Graph.java +++ b/src/main/java/tfm/graphs/Graph.java @@ -9,7 +9,6 @@ import tfm.nodes.NodeFactory; import tfm.utils.ASTUtils; import java.util.*; -import java.util.function.Consumer; import java.util.stream.Collectors; /** @@ -17,59 +16,25 @@ import java.util.stream.Collectors; * */ public abstract class Graph extends DirectedPseudograph, Arc> { - protected static final int DEFAULT_VERTEX_START_ID = 0; - - private int nextVertexId; - - public Graph() { - this(DEFAULT_VERTEX_START_ID); - } - - protected Graph(int vertexStartId) { + protected Graph() { super(null, null, false); - this.nextVertexId = vertexStartId; - } - - private GraphNode addNode(GraphNode node) { - this.addVertex(node); - - return node; - } - - private GraphNode addNode(int id, String instruction, ASTNode node) { - GraphNode newNode = NodeFactory.graphNode(id, instruction, node); - - return this.addNode(newNode); - } - - 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() - ); + public void addNode(GraphNode node) { + this.addVertex(node); + } - this.addVertex(copy); + public GraphNode addNode(String instruction, ASTNode node) { + GraphNode newNode = NodeFactory.graphNode(instruction, node); - return copy; + this.addNode(newNode); + + return newNode; } @SuppressWarnings("unchecked") @@ -80,7 +45,7 @@ public abstract class Graph extends DirectedPseudograph, Arc> { .map(node -> (GraphNode) node); } - public Optional> findNodeById(int id) { + public Optional> findNodeById(long id) { return vertexSet().stream() .filter(node -> Objects.equals(node.getId(), id)) .findFirst(); @@ -93,10 +58,6 @@ public abstract class Graph extends DirectedPseudograph, Arc> { .collect(Collectors.joining(System.lineSeparator())); } - protected synchronized int getNextVertexId() { - return nextVertexId++; - } - public List> findDeclarationsOfVariable(String variable) { return vertexSet().stream() .filter(node -> node.getDeclaredVariables().contains(variable)) @@ -115,98 +76,4 @@ public abstract class Graph extends DirectedPseudograph, Arc> { null, Arc::getDotAttributes); } - - /** - * 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 - */ - 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(); - - this.addVertex(newNode); - - 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 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 index 3061e60..27553f7 100644 --- a/src/main/java/tfm/graphs/GraphWithRootNode.java +++ b/src/main/java/tfm/graphs/GraphWithRootNode.java @@ -10,12 +10,10 @@ 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); + protected GraphWithRootNode() { + super(); } /** @@ -31,7 +29,7 @@ public abstract class GraphWithRootNode extends Graph return false; } - GraphNode root = NodeFactory.graphNode(ROOT_NODE_ID, instruction, rootNodeAst); + GraphNode root = NodeFactory.graphNode(instruction, rootNodeAst); this.rootNode = root; this.addVertex(root); @@ -42,6 +40,14 @@ public abstract class GraphWithRootNode extends Graph return Optional.ofNullable(rootNode); } + public void setRootNode(GraphNode rootNode) { + if (!this.containsVertex(rootNode)) { + throw new IllegalArgumentException("Cannot set root node: " + rootNode + " is not contained in graph!"); + } + + this.rootNode = rootNode; + } + @Override public boolean removeVertex(GraphNode graphNode) { if (Objects.equals(graphNode, rootNode)) { diff --git a/src/main/java/tfm/graphs/cfg/CFG.java b/src/main/java/tfm/graphs/cfg/CFG.java index 41c9697..c078314 100644 --- a/src/main/java/tfm/graphs/cfg/CFG.java +++ b/src/main/java/tfm/graphs/cfg/CFG.java @@ -40,7 +40,7 @@ public class CFG extends GraphWithRootNode { return findLastDefinitionsFrom(new HashSet<>(), startNode.getId(), startNode, variable); } - private Set> findLastDefinitionsFrom(Set visited, int startNode, GraphNode currentNode, String variable) { + private Set> findLastDefinitionsFrom(Set visited, long startNode, GraphNode currentNode, String variable) { visited.add(currentNode.getId()); Set> res = new HashSet<>(); diff --git a/src/main/java/tfm/graphs/pdg/PDGBuilder.java b/src/main/java/tfm/graphs/pdg/PDGBuilder.java index 070b89c..62aad5a 100644 --- a/src/main/java/tfm/graphs/pdg/PDGBuilder.java +++ b/src/main/java/tfm/graphs/pdg/PDGBuilder.java @@ -38,10 +38,6 @@ public class PDGBuilder { 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 @@ -53,6 +49,10 @@ public class PDGBuilder { .filter(node -> !Objects.equals(node, cfg.getExitNode())) .forEach(node -> pdg.addVertex(node)); + assert this.cfg.getRootNode().isPresent(); + + pdg.setRootNode(cfg.getRootNode().get()); + // Build control dependency ControlDependencyBuilder controlDependencyBuilder = new ControlDependencyBuilder(pdg, cfg); controlDependencyBuilder.analyze(); diff --git a/src/main/java/tfm/graphs/pdg/PostdominatorTree.java b/src/main/java/tfm/graphs/pdg/PostdominatorTree.java index 81fce86..cd1adec 100644 --- a/src/main/java/tfm/graphs/pdg/PostdominatorTree.java +++ b/src/main/java/tfm/graphs/pdg/PostdominatorTree.java @@ -191,8 +191,8 @@ public class PostdominatorTree extends DirectedTree, DefaultEdge> { return Integer.compare(sortingMap.get(b1), sortingMap.get(b2)); } - private static List> postorder(GraphWithRootNode graph) { - Optional> rootNode = graph.getRootNode(); + private static List> postorder(GraphWithRootNode graph) { + Optional> rootNode = graph.getRootNode(); if (!rootNode.isPresent()) throw new IllegalStateException("CFG lacks root node"); return postorder(graph, rootNode.get()); diff --git a/src/main/java/tfm/nodes/GraphNode.java b/src/main/java/tfm/nodes/GraphNode.java index bd4e741..aa41f1a 100644 --- a/src/main/java/tfm/nodes/GraphNode.java +++ b/src/main/java/tfm/nodes/GraphNode.java @@ -27,7 +27,7 @@ import java.util.Set; */ public class GraphNode implements Comparable> { - private final int id; + private final long id; private final String instruction; private final N astNode; @@ -35,7 +35,7 @@ public class GraphNode implements Comparable> { private final Set definedVariables; private final Set usedVariables; - GraphNode(int id, String instruction, @NotNull N astNode) { + GraphNode(long id, String instruction, @NotNull N astNode) { this( id, instruction, @@ -49,7 +49,7 @@ public class GraphNode implements Comparable> { } GraphNode( - int id, + long id, String instruction, @NotNull N astNode, Collection declaredVariables, @@ -73,7 +73,7 @@ public class GraphNode implements Comparable> { .visit(node); } - public int getId() { + public long getId() { return id; } @@ -139,6 +139,6 @@ public class GraphNode implements Comparable> { @Override public int compareTo(@NotNull GraphNode o) { - return Integer.compare(id, o.id); + return Long.compare(id, o.id); } } diff --git a/src/main/java/tfm/nodes/NodeFactory.java b/src/main/java/tfm/nodes/NodeFactory.java index 7d4f96e..52ab649 100644 --- a/src/main/java/tfm/nodes/NodeFactory.java +++ b/src/main/java/tfm/nodes/NodeFactory.java @@ -11,7 +11,6 @@ public class NodeFactory { * Returns a computed GraphNode (i.e. a GraphNode with computed the * declared, defined and used variables in its AST node) * - * @param id the id of the node * @param instruction the instruction that represents * @param node the node of the AST that represents * @param declaredVariables the set of declared variables @@ -21,7 +20,6 @@ public class NodeFactory { * @return a new GraphNode */ public static GraphNode computedGraphNode( - int id, String instruction, ASTNode node, Collection declaredVariables, @@ -35,7 +33,7 @@ public class NodeFactory { Objects.requireNonNull(usedVariables, "Used variables collection cannot be null!"); return new GraphNode<>( - id, + IdHelper.getInstance().getNextId(), instruction, node, declaredVariables, @@ -47,14 +45,12 @@ public class NodeFactory { /** * Returns a GraphNode computing the declared, defined and used variables in its AST node * - * @param id the id of the node * @param instruction the instruction that represents * @param node the node of the AST that represents * @param the type of the AST node * @return a new GraphNode */ public static GraphNode graphNode( - int id, String instruction, ASTNode node ) { @@ -62,7 +58,7 @@ public class NodeFactory { Objects.requireNonNull(node, "AST Node cannot be null"); return new GraphNode<>( - id, + IdHelper.getInstance().getNextId(), instruction, node ); diff --git a/src/main/java/tfm/slicing/Slice.java b/src/main/java/tfm/slicing/Slice.java index 2a8b844..91563f3 100644 --- a/src/main/java/tfm/slicing/Slice.java +++ b/src/main/java/tfm/slicing/Slice.java @@ -10,7 +10,7 @@ import java.util.*; import java.util.stream.Collectors; public class Slice { - private final Map> map = new HashMap<>(); + private final Map> map = new HashMap<>(); private final Set nodes = new HashSet<>(); public Slice() {} @@ -42,8 +42,8 @@ public class Slice { public Node getAst() { List> methods = map.values().stream().filter(e -> e.getAstNode() instanceof MethodDeclaration).collect(Collectors.toList()); if (methods.size() == 1) { - Optional secondNode = map.keySet().stream() - .sorted(Integer::compareTo).skip(1).findFirst(); + Optional secondNode = map.keySet().stream() + .sorted(Long::compareTo).skip(1).findFirst(); assert secondNode.isPresent(); Node n = map.get(secondNode.get()).getAstNode(); assert !(n instanceof MethodDeclaration); diff --git a/src/test/java/tfm/graphs/pdg/PDGTests.java b/src/test/java/tfm/graphs/pdg/PDGTests.java index 607c173..7b0d777 100644 --- a/src/test/java/tfm/graphs/pdg/PDGTests.java +++ b/src/test/java/tfm/graphs/pdg/PDGTests.java @@ -133,7 +133,7 @@ public class PDGTests { List slicedMethods = new LinkedList<>(); assert pdgs.length > 0; for (GraphNode node : pdgs[0].vertexSet().stream() - .sorted(Comparator.comparingInt(GraphNode::getId)) + .sorted(Comparator.comparingLong(GraphNode::getId)) .collect(Collectors.toList())) { // Skip start of graph if (node.getAstNode() instanceof MethodDeclaration) @@ -176,7 +176,7 @@ public class PDGTests { public final void printSlices(PDG pdg, Slice... slices) { pdg.vertexSet().stream() - .sorted(Comparator.comparingInt(GraphNode::getId)) + .sorted(Comparator.comparingLong(GraphNode::getId)) .forEach(n -> Logger.format("%3d: %s %s", n.getId(), Arrays.stream(slices) -- GitLab From cbbd7a651d5a371906a75a249c666edd048f1192 Mon Sep 17 00:00:00 2001 From: jacosro Date: Mon, 17 Feb 2020 01:38:58 +0100 Subject: [PATCH 04/20] Expanded method declaration nodes + WIP: expand method call nodes --- src/main/java/tfm/exec/Main.java | 2 +- src/main/java/tfm/graphs/pdg/PDG.java | 2 - .../tfm/graphs/sdg/MethodCallReplacer.java | 27 +++++++--- .../graphs/sdg/MethodCallReplacerVisitor.java | 11 +++- .../graphs/sdg/MethodDeclarationReplacer.java | 12 ----- src/main/java/tfm/graphs/sdg/SDG.java | 18 +++++-- src/main/java/tfm/graphs/sdg/SDGBuilder.java | 11 ++-- .../java/tfm/nodes/InOutVariableNode.java | 25 ---------- src/main/java/tfm/nodes/MethodCallNode.java | 17 ------- src/main/java/tfm/nodes/MethodRootNode.java | 17 ------- .../tfm/nodes/NodeWithInOutVariables.java | 50 ------------------- src/main/java/tfm/utils/Context.java | 16 ++++-- src/main/java/tfm/utils/Logger.java | 12 ++++- 13 files changed, 71 insertions(+), 149 deletions(-) delete mode 100644 src/main/java/tfm/graphs/sdg/MethodDeclarationReplacer.java delete mode 100644 src/main/java/tfm/nodes/InOutVariableNode.java delete mode 100644 src/main/java/tfm/nodes/MethodCallNode.java delete mode 100644 src/main/java/tfm/nodes/MethodRootNode.java delete mode 100644 src/main/java/tfm/nodes/NodeWithInOutVariables.java diff --git a/src/main/java/tfm/exec/Main.java b/src/main/java/tfm/exec/Main.java index c5fac4a..7f9ab0c 100644 --- a/src/main/java/tfm/exec/Main.java +++ b/src/main/java/tfm/exec/Main.java @@ -18,7 +18,7 @@ import java.util.Optional; public class Main { - public static final String PROGRAM = Utils.PROGRAMS_FOLDER + "cfg/Eval_4.java"; + public static final String PROGRAM = Utils.PROGRAMS_FOLDER + "sdg/Example1.java"; public static final String GRAPH = GraphLog.SDG; public static final String METHOD = "main"; diff --git a/src/main/java/tfm/graphs/pdg/PDG.java b/src/main/java/tfm/graphs/pdg/PDG.java index e4b35e6..df79831 100644 --- a/src/main/java/tfm/graphs/pdg/PDG.java +++ b/src/main/java/tfm/graphs/pdg/PDG.java @@ -8,8 +8,6 @@ import tfm.graphs.GraphWithRootNode; import tfm.graphs.Sliceable; import tfm.graphs.cfg.CFG; import tfm.nodes.GraphNode; -import tfm.nodes.MethodRootNode; -import tfm.nodes.NodeFactory; import tfm.slicing.Slice; import tfm.slicing.SlicingCriterion; import tfm.utils.NodeNotFoundException; diff --git a/src/main/java/tfm/graphs/sdg/MethodCallReplacer.java b/src/main/java/tfm/graphs/sdg/MethodCallReplacer.java index 7677607..015a83c 100644 --- a/src/main/java/tfm/graphs/sdg/MethodCallReplacer.java +++ b/src/main/java/tfm/graphs/sdg/MethodCallReplacer.java @@ -1,5 +1,11 @@ package tfm.graphs.sdg; +import tfm.nodes.GraphNode; +import tfm.utils.Context; +import tfm.utils.Logger; + +import java.util.Optional; + class MethodCallReplacer { private SDG sdg; @@ -9,13 +15,18 @@ class MethodCallReplacer { } public void replace() { -// this.sdg.getContextPDGGraphMap() -// .forEach((context, pdgGraph) -> { -// if (!context.getCurrentMethod().isPresent()) { -// return; // Should NOT happen -// } -// -// context.getCurrentMethod().get().accept(new MethodCallReplacerVisitor(pdgGraph), context); -// }); + this.sdg.getContexts().stream() + .filter(context -> context.getCurrentMethod().isPresent()) + .forEach(context -> { + Logger.log("MethodCallReplacer", context); + + Optional> optionalRootNode = this.sdg.getRootNode(context); + + if (!optionalRootNode.isPresent()) { + return; // We don't have visited the code (e.g. the MethodDeclaration for a method call) + } + + optionalRootNode.get().getAstNode().accept(new MethodCallReplacerVisitor(), context); + }); } } diff --git a/src/main/java/tfm/graphs/sdg/MethodCallReplacerVisitor.java b/src/main/java/tfm/graphs/sdg/MethodCallReplacerVisitor.java index f48547e..fe49e7c 100644 --- a/src/main/java/tfm/graphs/sdg/MethodCallReplacerVisitor.java +++ b/src/main/java/tfm/graphs/sdg/MethodCallReplacerVisitor.java @@ -9,6 +9,9 @@ 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 com.github.javaparser.symbolsolver.model.resolution.TypeSolver; +import com.github.javaparser.symbolsolver.resolution.typesolvers.CombinedTypeSolver; +import com.github.javaparser.symbolsolver.resolution.typesolvers.JavaParserTypeSolver; import tfm.graphs.pdg.PDG; import tfm.nodes.GraphNode; import tfm.utils.Context; @@ -23,23 +26,29 @@ class MethodCallReplacerVisitor extends VoidVisitorAdapter { private PDG pdg; + public MethodCallReplacerVisitor() { + + } + public MethodCallReplacerVisitor(PDG pdg) { this.pdg = pdg; } @Override public void visit(MethodCallExpr methodCallExpr, Context context) { + Logger.log("MethodCallReplacerVisitor", context); Optional optionalCallingMethod = methodCallExpr.getScope().isPresent() ? shouldMakeCallWithScope(methodCallExpr, context) : shouldMakeCallWithNoScope(methodCallExpr, context); if (!optionalCallingMethod.isPresent()) { + Logger.log("Discarding: " + methodCallExpr); return; } // todo make call - Logger.log(String.format("Method '%s' called", optionalCallingMethod.get().getNameAsString())); + Logger.log(String.format("%s | Method '%s' called", methodCallExpr, optionalCallingMethod.get().getNameAsString())); } private Optional shouldMakeCallWithScope(MethodCallExpr methodCallExpr, Context context) { diff --git a/src/main/java/tfm/graphs/sdg/MethodDeclarationReplacer.java b/src/main/java/tfm/graphs/sdg/MethodDeclarationReplacer.java deleted file mode 100644 index ca0b02b..0000000 --- a/src/main/java/tfm/graphs/sdg/MethodDeclarationReplacer.java +++ /dev/null @@ -1,12 +0,0 @@ -package tfm.graphs.sdg; - -import com.github.javaparser.ast.body.MethodDeclaration; -import com.github.javaparser.ast.visitor.VoidVisitorAdapter; - -public class MethodDeclarationReplacer extends VoidVisitorAdapter { - - @Override - public void visit(MethodDeclaration methodDeclaration, Void arg) { - - } -} diff --git a/src/main/java/tfm/graphs/sdg/SDG.java b/src/main/java/tfm/graphs/sdg/SDG.java index 6496a9c..bfc9b6a 100644 --- a/src/main/java/tfm/graphs/sdg/SDG.java +++ b/src/main/java/tfm/graphs/sdg/SDG.java @@ -3,6 +3,7 @@ package tfm.graphs.sdg; import com.github.javaparser.ast.CompilationUnit; import com.github.javaparser.ast.NodeList; import com.github.javaparser.ast.body.MethodDeclaration; +import com.github.javaparser.ast.stmt.ExpressionStmt; import tfm.arcs.pdg.ControlDependencyArc; import tfm.arcs.pdg.DataDependencyArc; import tfm.arcs.sdg.CallArc; @@ -16,12 +17,11 @@ import tfm.slicing.SlicingCriterion; import tfm.utils.Context; import java.util.*; -import java.util.stream.Collectors; public class SDG extends Graph implements Sliceable, Buildable> { private boolean built = false; - private Map contextToRootNodeId; + private Map contextToRootNodeId; public SDG() { this.contextToRootNodeId = new HashMap<>(); @@ -47,11 +47,19 @@ public class SDG extends Graph implements Sliceable, Buildable> getRootNode(Context context) { - Integer id = this.contextToRootNodeId.get(context); + Long id = this.contextToRootNodeId.get(context); return id != null ? findNodeById(id) : Optional.empty(); } + public void addRootNode(Context context, long id) { + if (!findNodeById(id).isPresent()) { + throw new IllegalArgumentException("Cannot add root node to SDG: " + id + " is not in graph!"); + } + + this.contextToRootNodeId.put(new Context(context), id); + } + public void addControlDependencyArc(GraphNode from, GraphNode to) { this.addEdge(from, to, new ControlDependencyArc()); } @@ -60,11 +68,11 @@ public class SDG extends Graph implements Sliceable, Buildable from, GraphNode to) { this.addEdge(from, to, new CallArc()); } - public void addParameterInOutArc(InOutVariableNode from, InOutVariableNode to) { + public void addParameterInOutArc(GraphNode from, GraphNode to) { this.addEdge(from, to, new ParameterInOutArc()); } } diff --git a/src/main/java/tfm/graphs/sdg/SDGBuilder.java b/src/main/java/tfm/graphs/sdg/SDGBuilder.java index 7aabd36..06455ae 100644 --- a/src/main/java/tfm/graphs/sdg/SDGBuilder.java +++ b/src/main/java/tfm/graphs/sdg/SDGBuilder.java @@ -11,6 +11,7 @@ import tfm.arcs.Arc; import tfm.graphs.pdg.PDG; import tfm.nodes.GraphNode; import tfm.utils.Context; +import tfm.utils.Logger; import java.util.Map; import java.util.Optional; @@ -36,9 +37,13 @@ class SDGBuilder extends VoidVisitorAdapter { pdg.build(methodDeclaration); for (GraphNode node : pdg.vertexSet()) { - sdg.addNode(node, false); + sdg.addNode(node); } + assert pdg.getRootNode().isPresent(); + + sdg.addRootNode(context, pdg.getRootNode().get().getId()); + for (Arc arc : pdg.edgeSet()) { GraphNode from = pdg.getEdgeSource(arc); GraphNode to = pdg.getEdgeTarget(arc); @@ -113,10 +118,6 @@ class SDGBuilder extends VoidVisitorAdapter { classOrInterfaceDeclaration.getMembers().accept(this, context); - // Once every PDG is built, expand method declaration nodes of each one - // todo methodDeclaration replacer - - // 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(sdg); diff --git a/src/main/java/tfm/nodes/InOutVariableNode.java b/src/main/java/tfm/nodes/InOutVariableNode.java deleted file mode 100644 index a5bbcf8..0000000 --- a/src/main/java/tfm/nodes/InOutVariableNode.java +++ /dev/null @@ -1,25 +0,0 @@ -package tfm.nodes; - -import com.github.javaparser.ast.stmt.ExpressionStmt; -import org.jetbrains.annotations.NotNull; - -import java.util.Collection; - -public class InOutVariableNode extends GraphNode { - - public InOutVariableNode(int id, String instruction, @NotNull ExpressionStmt astNode) { - super(id, instruction, astNode); - } - - public InOutVariableNode(int id, String instruction, @NotNull ExpressionStmt astNode, Collection declaredVariables, Collection definedVariables, Collection usedVariables) { - super(id, instruction, astNode, declaredVariables, definedVariables, usedVariables); - } - - public String getDeclaredVariable() { - return getDeclaredVariables().iterator().next(); - } - - public String getUsedVariable() { - return getUsedVariables().iterator().next(); - } -} diff --git a/src/main/java/tfm/nodes/MethodCallNode.java b/src/main/java/tfm/nodes/MethodCallNode.java deleted file mode 100644 index 9496146..0000000 --- a/src/main/java/tfm/nodes/MethodCallNode.java +++ /dev/null @@ -1,17 +0,0 @@ -package tfm.nodes; - -import com.github.javaparser.ast.stmt.ExpressionStmt; -import org.checkerframework.checker.nullness.qual.NonNull; - -import java.util.Set; - -public class MethodCallNode extends NodeWithInOutVariables { - - public MethodCallNode(int id, String representation, ExpressionStmt node) { - super(id, representation, node); - } - - public MethodCallNode(int id, String representation, @NonNull ExpressionStmt node, Set declaredVariables, Set definedVariables, Set usedVariables) { - super(id, representation, node, declaredVariables, definedVariables, usedVariables); - } -} diff --git a/src/main/java/tfm/nodes/MethodRootNode.java b/src/main/java/tfm/nodes/MethodRootNode.java deleted file mode 100644 index 4f7cc13..0000000 --- a/src/main/java/tfm/nodes/MethodRootNode.java +++ /dev/null @@ -1,17 +0,0 @@ -package tfm.nodes; - -import com.github.javaparser.ast.body.MethodDeclaration; -import org.checkerframework.checker.nullness.qual.NonNull; - -import java.util.Set; - -public class MethodRootNode extends NodeWithInOutVariables { - - public MethodRootNode(int id, String representation, MethodDeclaration node) { - super(id, representation, node); - } - - public MethodRootNode(int id, String representation, @NonNull MethodDeclaration node, Set declaredVariables, Set definedVariables, Set usedVariables) { - super(id, representation, node, declaredVariables, definedVariables, usedVariables); - } -} diff --git a/src/main/java/tfm/nodes/NodeWithInOutVariables.java b/src/main/java/tfm/nodes/NodeWithInOutVariables.java deleted file mode 100644 index 00739b0..0000000 --- a/src/main/java/tfm/nodes/NodeWithInOutVariables.java +++ /dev/null @@ -1,50 +0,0 @@ -package tfm.nodes; - -import com.github.javaparser.ast.Node; -import org.checkerframework.checker.nullness.qual.NonNull; - -import java.util.*; - -public class NodeWithInOutVariables extends GraphNode { - - protected Map inVariablesMap; - protected Map outVariablesMap; - - protected NodeWithInOutVariables(int id, String representation, N node) { - super(id, representation, node); - - this.inVariablesMap = new HashMap<>(); - this.outVariablesMap = new HashMap<>(); - } - - protected NodeWithInOutVariables(int id, String representation, @NonNull N node, Set declaredVariables, Set definedVariables, Set usedVariables) { - super(id, representation, node, declaredVariables, definedVariables, usedVariables); - - this.inVariablesMap = new HashMap<>(); - this.outVariablesMap = new HashMap<>(); - } - - public Set getInVariables() { - return inVariablesMap.keySet(); - } - - public Optional getInNode(String variable) { - return Optional.ofNullable(inVariablesMap.get(variable)); - } - - public Set getOutVariables() { - return outVariablesMap.keySet(); - } - - public Optional getOutNode(String variable) { - return Optional.ofNullable(outVariablesMap.get(variable)); - } - - public void addInVariable(String variable, InOutVariableNode node) { - this.inVariablesMap.put(variable, node); - } - - public void addOutVariable(String variable, InOutVariableNode node) { - this.outVariablesMap.put(variable, node); - } -} diff --git a/src/main/java/tfm/utils/Context.java b/src/main/java/tfm/utils/Context.java index 85da3a4..97fdf79 100644 --- a/src/main/java/tfm/utils/Context.java +++ b/src/main/java/tfm/utils/Context.java @@ -4,11 +4,13 @@ import com.github.javaparser.ast.CompilationUnit; import com.github.javaparser.ast.Node; import com.github.javaparser.ast.body.ClassOrInterfaceDeclaration; import com.github.javaparser.ast.body.MethodDeclaration; +import com.github.javaparser.ast.nodeTypes.NodeWithName; +import com.github.javaparser.ast.nodeTypes.NodeWithSimpleName; import java.util.Objects; import java.util.Optional; -public class Context { +public class Context implements Cloneable { private CompilationUnit currentCU; private ClassOrInterfaceDeclaration currentClass; @@ -32,6 +34,12 @@ public class Context { this.currentMethod = method; } + public Context(Context context) { + this.currentCU = context.currentCU; + this.currentClass = context.currentClass; + this.currentMethod = context.currentMethod; + } + public Optional getCurrentCU() { return Optional.ofNullable(currentCU); } @@ -83,9 +91,9 @@ public class Context { @Override public String toString() { return String.format("Context{compilationUnit: %s, class: %s, method: %s}", - getCurrentCU().map(Node::toString), - getCurrentClass().map(Node::toString), - getCurrentMethod().map(Node::toString) + getCurrentCU().flatMap(cu -> cu.getPackageDeclaration().map(NodeWithName::getNameAsString)).orElse(null), + getCurrentClass().map(NodeWithSimpleName::getNameAsString).orElse(null), + getCurrentMethod().map(NodeWithSimpleName::getNameAsString).orElse(null) ); } } diff --git a/src/main/java/tfm/utils/Logger.java b/src/main/java/tfm/utils/Logger.java index 7654082..4e98874 100644 --- a/src/main/java/tfm/utils/Logger.java +++ b/src/main/java/tfm/utils/Logger.java @@ -9,11 +9,19 @@ public class Logger { } public static void log(Object object) { - log(Objects.toString(object)); + log(String.valueOf(object)); } public static void log(String message) { - System.out.println(message); + log("", message); + } + + public static void log(String context, Object object) { + log(context, String.valueOf(object)); + } + + public static void log(String context, String message) { + System.out.println(String.format("%s: %s", context, message)); } public static void format(String message, Object... args) { -- GitLab From 7101ed4f06d3aac4340a05ba5f500dc3dc121579 Mon Sep 17 00:00:00 2001 From: jacosro Date: Fri, 28 Feb 2020 01:35:15 +0100 Subject: [PATCH 05/20] Changed sdg data structure from pdgs to root nodes --- src/main/java/tfm/graphs/Graph.java | 6 -- src/main/java/tfm/graphs/pdg/PDG.java | 8 +++ .../tfm/graphs/sdg/MethodCallReplacer.java | 4 +- .../graphs/sdg/MethodCallReplacerVisitor.java | 20 ++++-- src/main/java/tfm/graphs/sdg/SDG.java | 67 ++++++++++++++++--- src/main/java/tfm/graphs/sdg/SDGBuilder.java | 26 +++---- src/main/java/tfm/utils/Context.java | 2 +- src/main/java/tfm/utils/Logger.java | 9 ++- 8 files changed, 102 insertions(+), 40 deletions(-) diff --git a/src/main/java/tfm/graphs/Graph.java b/src/main/java/tfm/graphs/Graph.java index 898e3ae..a18dd53 100644 --- a/src/main/java/tfm/graphs/Graph.java +++ b/src/main/java/tfm/graphs/Graph.java @@ -58,12 +58,6 @@ public abstract class Graph extends DirectedPseudograph, Arc> { .collect(Collectors.joining(System.lineSeparator())); } - public List> findDeclarationsOfVariable(String variable) { - return vertexSet().stream() - .filter(node -> node.getDeclaredVariables().contains(variable)) - .collect(Collectors.toList()); - } - public boolean isEmpty() { return this.vertexSet().isEmpty(); } diff --git a/src/main/java/tfm/graphs/pdg/PDG.java b/src/main/java/tfm/graphs/pdg/PDG.java index df79831..328281a 100644 --- a/src/main/java/tfm/graphs/pdg/PDG.java +++ b/src/main/java/tfm/graphs/pdg/PDG.java @@ -12,7 +12,9 @@ import tfm.slicing.Slice; import tfm.slicing.SlicingCriterion; import tfm.utils.NodeNotFoundException; +import java.util.List; import java.util.Optional; +import java.util.stream.Collectors; /** * The Program Dependence Graph represents the statements of a method in @@ -77,4 +79,10 @@ public class PDG extends GraphWithRootNode implements Sliceab public boolean isBuilt() { return built; } + + public List> findDeclarationsOfVariable(String variable) { + return vertexSet().stream() + .filter(node -> node.getDeclaredVariables().contains(variable)) + .collect(Collectors.toList()); + } } diff --git a/src/main/java/tfm/graphs/sdg/MethodCallReplacer.java b/src/main/java/tfm/graphs/sdg/MethodCallReplacer.java index 015a83c..b837a67 100644 --- a/src/main/java/tfm/graphs/sdg/MethodCallReplacer.java +++ b/src/main/java/tfm/graphs/sdg/MethodCallReplacer.java @@ -1,5 +1,7 @@ package tfm.graphs.sdg; +import com.github.javaparser.ast.body.MethodDeclaration; +import tfm.graphs.GraphWithRootNode; import tfm.nodes.GraphNode; import tfm.utils.Context; import tfm.utils.Logger; @@ -20,7 +22,7 @@ class MethodCallReplacer { .forEach(context -> { Logger.log("MethodCallReplacer", context); - Optional> optionalRootNode = this.sdg.getRootNode(context); + Optional> optionalRootNode = this.sdg.getRootNode(context); if (!optionalRootNode.isPresent()) { return; // We don't have visited the code (e.g. the MethodDeclaration for a method call) diff --git a/src/main/java/tfm/graphs/sdg/MethodCallReplacerVisitor.java b/src/main/java/tfm/graphs/sdg/MethodCallReplacerVisitor.java index fe49e7c..15b9753 100644 --- a/src/main/java/tfm/graphs/sdg/MethodCallReplacerVisitor.java +++ b/src/main/java/tfm/graphs/sdg/MethodCallReplacerVisitor.java @@ -24,14 +24,26 @@ import java.util.stream.Collectors; class MethodCallReplacerVisitor extends VoidVisitorAdapter { - private PDG pdg; + private SDG sdg; + private GraphNode methodCallNode; public MethodCallReplacerVisitor() { } - public MethodCallReplacerVisitor(PDG pdg) { - this.pdg = pdg; + public MethodCallReplacerVisitor(SDG sdg) { + this.sdg = sdg; + } + + @Override + public void visit(ExpressionStmt n, Context arg) { + Optional> optionalNode = sdg.findNodeByASTNode(n); + + assert optionalNode.isPresent(); + + methodCallNode = optionalNode.get(); + + super.visit(n, arg); } @Override @@ -66,7 +78,7 @@ class MethodCallReplacerVisitor extends VoidVisitorAdapter { if (!Objects.equals(scopeName, currentClass.getNameAsString())) { // Check if 'scopeName' is a variable - List> declarations = pdg.findDeclarationsOfVariable(scopeName); + List> declarations = sdg.findDeclarationsOfVariable(scopeName, methodCallNode); if (declarations.isEmpty()) { // It is a static method call of another class. We do nothing diff --git a/src/main/java/tfm/graphs/sdg/SDG.java b/src/main/java/tfm/graphs/sdg/SDG.java index bfc9b6a..1e16f36 100644 --- a/src/main/java/tfm/graphs/sdg/SDG.java +++ b/src/main/java/tfm/graphs/sdg/SDG.java @@ -4,12 +4,14 @@ import com.github.javaparser.ast.CompilationUnit; import com.github.javaparser.ast.NodeList; import com.github.javaparser.ast.body.MethodDeclaration; import com.github.javaparser.ast.stmt.ExpressionStmt; +import tfm.arcs.Arc; import tfm.arcs.pdg.ControlDependencyArc; import tfm.arcs.pdg.DataDependencyArc; import tfm.arcs.sdg.CallArc; import tfm.arcs.sdg.ParameterInOutArc; import tfm.graphs.Buildable; import tfm.graphs.Graph; +import tfm.graphs.pdg.PDG; import tfm.nodes.*; import tfm.slicing.Slice; import tfm.slicing.Sliceable; @@ -17,14 +19,16 @@ import tfm.slicing.SlicingCriterion; import tfm.utils.Context; import java.util.*; +import java.util.function.Function; +import java.util.stream.Collectors; public class SDG extends Graph implements Sliceable, Buildable> { private boolean built = false; - private Map contextToRootNodeId; + private Map contextToMethodRoot; public SDG() { - this.contextToRootNodeId = new HashMap<>(); + this.contextToMethodRoot = new HashMap<>(); } @Override @@ -43,21 +47,45 @@ public class SDG extends Graph implements Sliceable, Buildable getContexts() { - return contextToRootNodeId.keySet(); + return contextToMethodRoot.keySet(); } - public Optional> getRootNode(Context context) { - Long id = this.contextToRootNodeId.get(context); + @SuppressWarnings("unchecked") + public List> getMethodRoots() { + return contextToMethodRoot.values().stream() + .map(id -> findNodeById(id)) + .filter(Optional::isPresent) + .map(optional -> (GraphNode) optional.get()) + .collect(Collectors.toList()); + } + + @SuppressWarnings("unchecked") + public Optional> getRootNode(Context context) { + Long id = this.contextToMethodRoot.get(context); + + if (id == null) { + return Optional.empty(); + } - return id != null ? findNodeById(id) : Optional.empty(); + return findNodeById(id).map(node -> (GraphNode) node); } public void addRootNode(Context context, long id) { - if (!findNodeById(id).isPresent()) { - throw new IllegalArgumentException("Cannot add root node to SDG: " + id + " is not in graph!"); - } + if (!findNodeById(id).isPresent()) + throw new IllegalArgumentException("Root node with id " + id + " is not contained in graph!"); + + this.contextToMethodRoot.put(new Context(context), id); + } + + public void addRootNode(Context context, GraphNode node) { + addRootNode(context, node.getId()); + } - this.contextToRootNodeId.put(new Context(context), id); + public Optional getContext(long id) { + return contextToMethodRoot.entrySet().stream() + .filter(entry -> Objects.equals(entry.getValue(), id)) + .findFirst() + .map(Map.Entry::getKey); } public void addControlDependencyArc(GraphNode from, GraphNode to) { @@ -75,4 +103,23 @@ public class SDG extends Graph implements Sliceable, Buildable from, GraphNode to) { this.addEdge(from, to, new ParameterInOutArc()); } + + public List> findDeclarationsOfVariable(String variable, GraphNode root) { + List> res = new ArrayList<>(); + + // First, expand the node + for (Arc arc : incomingEdgesOf(root)) { + if (arc.isDataDependencyArc() || arc.isControlDependencyArc()) { + res.addAll(findDeclarationsOfVariable(variable, getEdgeSource(arc))); + } + } + + // Finally, the current node + // This way, the last element of the list is the most recent declaration + if (root.getDeclaredVariables().contains(variable)) { + res.add(root); + } + + return res; + } } diff --git a/src/main/java/tfm/graphs/sdg/SDGBuilder.java b/src/main/java/tfm/graphs/sdg/SDGBuilder.java index 06455ae..9476c0e 100644 --- a/src/main/java/tfm/graphs/sdg/SDGBuilder.java +++ b/src/main/java/tfm/graphs/sdg/SDGBuilder.java @@ -14,6 +14,7 @@ import tfm.utils.Context; import tfm.utils.Logger; import java.util.Map; +import java.util.Objects; import java.util.Optional; class SDGBuilder extends VoidVisitorAdapter { @@ -36,32 +37,27 @@ class SDGBuilder extends VoidVisitorAdapter { PDG pdg = new PDG(); pdg.build(methodDeclaration); + assert pdg.isBuilt(); + assert pdg.getRootNode().isPresent(); + + // Add all nodes from PDG to SDG for (GraphNode node : pdg.vertexSet()) { sdg.addNode(node); } - assert pdg.getRootNode().isPresent(); - - sdg.addRootNode(context, pdg.getRootNode().get().getId()); - + // Add all arcs from PDG to SDG for (Arc arc : pdg.edgeSet()) { - GraphNode from = pdg.getEdgeSource(arc); - GraphNode to = pdg.getEdgeTarget(arc); - if (arc.isControlDependencyArc()) { - sdg.addControlDependencyArc(from, to); + sdg.addControlDependencyArc(pdg.getEdgeSource(arc), pdg.getEdgeTarget(arc)); } else { - sdg.addDataDependencyArc(from, to, arc.getLabel()); + sdg.addDataDependencyArc(pdg.getEdgeSource(arc), pdg.getEdgeTarget(arc), arc.getLabel()); } } - Optional> optionalMethodDeclarationNode = pdg.getRootNode(); - - if (!optionalMethodDeclarationNode.isPresent()) { - return; // Should not happen - } + GraphNode methodDeclarationNode = pdg.getRootNode().get(); - GraphNode methodDeclarationNode = optionalMethodDeclarationNode.get(); + // Add root node from PDG + sdg.addRootNode(context, methodDeclarationNode.getId()); for (Parameter parameter : methodDeclaration.getParameters()) { // In node diff --git a/src/main/java/tfm/utils/Context.java b/src/main/java/tfm/utils/Context.java index 97fdf79..7d4e5eb 100644 --- a/src/main/java/tfm/utils/Context.java +++ b/src/main/java/tfm/utils/Context.java @@ -10,7 +10,7 @@ import com.github.javaparser.ast.nodeTypes.NodeWithSimpleName; import java.util.Objects; import java.util.Optional; -public class Context implements Cloneable { +public class Context { private CompilationUnit currentCU; private ClassOrInterfaceDeclaration currentClass; diff --git a/src/main/java/tfm/utils/Logger.java b/src/main/java/tfm/utils/Logger.java index 4e98874..e621e29 100644 --- a/src/main/java/tfm/utils/Logger.java +++ b/src/main/java/tfm/utils/Logger.java @@ -1,7 +1,5 @@ package tfm.utils; -import java.util.Objects; - public class Logger { public static void log() { @@ -21,7 +19,12 @@ public class Logger { } public static void log(String context, String message) { - System.out.println(String.format("%s: %s", context, message)); + System.out.println( + String.format("%s%s", + context.isEmpty() ? "" : String.format("[%s]: ", context), + message + ) + ); } public static void format(String message, Object... args) { -- GitLab From c126015129779ee7201ae1deadfdbb984181c8e9 Mon Sep 17 00:00:00 2001 From: jacosro Date: Sun, 1 Mar 2020 12:46:17 +0100 Subject: [PATCH 06/20] Before variable nodes in CFG --- src/main/java/tfm/graphs/sdg/MethodCallReplacer.java | 2 +- .../java/tfm/graphs/sdg/MethodCallReplacerVisitor.java | 8 -------- src/main/java/tfm/graphs/sdg/SDGBuilder.java | 5 ----- 3 files changed, 1 insertion(+), 14 deletions(-) diff --git a/src/main/java/tfm/graphs/sdg/MethodCallReplacer.java b/src/main/java/tfm/graphs/sdg/MethodCallReplacer.java index b837a67..ed9d4e6 100644 --- a/src/main/java/tfm/graphs/sdg/MethodCallReplacer.java +++ b/src/main/java/tfm/graphs/sdg/MethodCallReplacer.java @@ -28,7 +28,7 @@ class MethodCallReplacer { return; // We don't have visited the code (e.g. the MethodDeclaration for a method call) } - optionalRootNode.get().getAstNode().accept(new MethodCallReplacerVisitor(), context); + optionalRootNode.get().getAstNode().accept(new MethodCallReplacerVisitor(sdg), context); }); } } diff --git a/src/main/java/tfm/graphs/sdg/MethodCallReplacerVisitor.java b/src/main/java/tfm/graphs/sdg/MethodCallReplacerVisitor.java index 15b9753..1ff7999 100644 --- a/src/main/java/tfm/graphs/sdg/MethodCallReplacerVisitor.java +++ b/src/main/java/tfm/graphs/sdg/MethodCallReplacerVisitor.java @@ -9,10 +9,6 @@ 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 com.github.javaparser.symbolsolver.model.resolution.TypeSolver; -import com.github.javaparser.symbolsolver.resolution.typesolvers.CombinedTypeSolver; -import com.github.javaparser.symbolsolver.resolution.typesolvers.JavaParserTypeSolver; -import tfm.graphs.pdg.PDG; import tfm.nodes.GraphNode; import tfm.utils.Context; import tfm.utils.Logger; @@ -27,10 +23,6 @@ class MethodCallReplacerVisitor extends VoidVisitorAdapter { private SDG sdg; private GraphNode methodCallNode; - public MethodCallReplacerVisitor() { - - } - public MethodCallReplacerVisitor(SDG sdg) { this.sdg = sdg; } diff --git a/src/main/java/tfm/graphs/sdg/SDGBuilder.java b/src/main/java/tfm/graphs/sdg/SDGBuilder.java index 9476c0e..ba042d8 100644 --- a/src/main/java/tfm/graphs/sdg/SDGBuilder.java +++ b/src/main/java/tfm/graphs/sdg/SDGBuilder.java @@ -11,11 +11,6 @@ import tfm.arcs.Arc; import tfm.graphs.pdg.PDG; import tfm.nodes.GraphNode; import tfm.utils.Context; -import tfm.utils.Logger; - -import java.util.Map; -import java.util.Objects; -import java.util.Optional; class SDGBuilder extends VoidVisitorAdapter { -- GitLab From 0c4f566bac3ce0fa84b0168a6bf8786f32df5f26 Mon Sep 17 00:00:00 2001 From: jacosro Date: Sun, 1 Mar 2020 19:25:11 +0100 Subject: [PATCH 07/20] Added node type --- src/main/java/tfm/exec/Main.java | 4 +- src/main/java/tfm/graphs/Graph.java | 13 ++- .../java/tfm/graphs/GraphWithRootNode.java | 7 +- .../tfm/graphs/augmented/ACFGBuilder.java | 3 +- src/main/java/tfm/graphs/cfg/CFGBuilder.java | 96 ++++++++++++++++--- .../tfm/graphs/pdg/DataDependencyBuilder.java | 12 ++- src/main/java/tfm/graphs/pdg/PDGBuilder.java | 2 + src/main/java/tfm/graphs/sdg/SDGBuilder.java | 40 -------- .../tfm/nodes/AbstractTypeNodeFactory.java | 48 ++++++++++ src/main/java/tfm/nodes/GraphNode.java | 24 ++++- src/main/java/tfm/nodes/IdHelper.java | 4 +- src/main/java/tfm/nodes/NodeFactory.java | 66 ------------- .../factories/InVariableNodeFactory.java | 11 +++ .../nodes/factories/MethodNodeFactory.java | 11 +++ .../java/tfm/nodes/factories/NodeFactory.java | 44 +++++++++ .../factories/OutVariableNodeFactory.java | 11 +++ .../nodes/factories/StatementNodeFactory.java | 11 +++ src/main/java/tfm/nodes/type/NodeType.java | 8 ++ .../tfm/graphs/pdg/HandCraftedGraphs.java | 5 +- src/test/java/tfm/graphs/pdg/PDGTests.java | 7 +- 20 files changed, 284 insertions(+), 143 deletions(-) create mode 100644 src/main/java/tfm/nodes/AbstractTypeNodeFactory.java delete mode 100644 src/main/java/tfm/nodes/NodeFactory.java create mode 100644 src/main/java/tfm/nodes/factories/InVariableNodeFactory.java create mode 100644 src/main/java/tfm/nodes/factories/MethodNodeFactory.java create mode 100644 src/main/java/tfm/nodes/factories/NodeFactory.java create mode 100644 src/main/java/tfm/nodes/factories/OutVariableNodeFactory.java create mode 100644 src/main/java/tfm/nodes/factories/StatementNodeFactory.java create mode 100644 src/main/java/tfm/nodes/type/NodeType.java diff --git a/src/main/java/tfm/exec/Main.java b/src/main/java/tfm/exec/Main.java index 7f9ab0c..26a863e 100644 --- a/src/main/java/tfm/exec/Main.java +++ b/src/main/java/tfm/exec/Main.java @@ -19,8 +19,8 @@ import java.util.Optional; public class Main { public static final String PROGRAM = Utils.PROGRAMS_FOLDER + "sdg/Example1.java"; - public static final String GRAPH = GraphLog.SDG; - public static final String METHOD = "main"; + public static final String GRAPH = GraphLog.PDG; + public static final String METHOD = "sum"; public static void main(String[] args) throws IOException { JavaParser.getStaticConfiguration().setAttributeComments(false); diff --git a/src/main/java/tfm/graphs/Graph.java b/src/main/java/tfm/graphs/Graph.java index a18dd53..7c5fd04 100644 --- a/src/main/java/tfm/graphs/Graph.java +++ b/src/main/java/tfm/graphs/Graph.java @@ -1,11 +1,12 @@ package tfm.graphs; import com.github.javaparser.ast.Node; +import org.jetbrains.annotations.NotNull; import org.jgrapht.graph.DirectedPseudograph; import org.jgrapht.io.DOTExporter; import tfm.arcs.Arc; import tfm.nodes.GraphNode; -import tfm.nodes.NodeFactory; +import tfm.nodes.factories.NodeFactory; import tfm.utils.ASTUtils; import java.util.*; @@ -25,12 +26,16 @@ public abstract class Graph extends DirectedPseudograph, Arc> { * * @param node the node to add to the graph */ - public void addNode(GraphNode node) { + public void addNode(@NotNull GraphNode node) { this.addVertex(node); } - public GraphNode addNode(String instruction, ASTNode node) { - GraphNode newNode = NodeFactory.graphNode(instruction, node); + public GraphNode addNode(@NotNull String instruction, @NotNull ASTNode node) { + return this.addNode(instruction, node, GraphNode.DEFAULT_FACTORY); + } + + public GraphNode addNode(@NotNull String instruction, @NotNull ASTNode node, @NotNull NodeFactory nodeFactory) { + GraphNode newNode = nodeFactory.graphNode(instruction, node); this.addNode(newNode); diff --git a/src/main/java/tfm/graphs/GraphWithRootNode.java b/src/main/java/tfm/graphs/GraphWithRootNode.java index 27553f7..6243243 100644 --- a/src/main/java/tfm/graphs/GraphWithRootNode.java +++ b/src/main/java/tfm/graphs/GraphWithRootNode.java @@ -2,8 +2,9 @@ package tfm.graphs; import com.github.javaparser.ast.Node; import com.github.javaparser.ast.body.MethodDeclaration; +import org.jetbrains.annotations.NotNull; import tfm.nodes.GraphNode; -import tfm.nodes.NodeFactory; +import tfm.nodes.factories.NodeFactory; import java.util.Objects; import java.util.Optional; @@ -24,12 +25,12 @@ public abstract class GraphWithRootNode extends Graph * @param rootNodeAst the AST node * @return true if the root node is created, false otherwise */ - public boolean buildRootNode(String instruction, ASTRootNode rootNodeAst) { + public boolean buildRootNode(@NotNull String instruction, @NotNull ASTRootNode rootNodeAst, @NotNull NodeFactory nodeFactory) { if (rootNode != null) { return false; } - GraphNode root = NodeFactory.graphNode(instruction, rootNodeAst); + GraphNode root = nodeFactory.graphNode(instruction, rootNodeAst); this.rootNode = root; this.addVertex(root); diff --git a/src/main/java/tfm/graphs/augmented/ACFGBuilder.java b/src/main/java/tfm/graphs/augmented/ACFGBuilder.java index 242a335..1aafc91 100644 --- a/src/main/java/tfm/graphs/augmented/ACFGBuilder.java +++ b/src/main/java/tfm/graphs/augmented/ACFGBuilder.java @@ -8,6 +8,7 @@ import com.github.javaparser.ast.stmt.*; import com.github.javaparser.ast.visitor.VoidVisitor; import tfm.graphs.cfg.CFGBuilder; import tfm.nodes.GraphNode; +import tfm.nodes.factories.MethodNodeFactory; import tfm.utils.ASTUtils; import java.util.LinkedList; @@ -237,7 +238,7 @@ public class ACFGBuilder extends CFGBuilder { if (!methodDeclaration.getBody().isPresent()) throw new IllegalStateException("The method must have a body!"); - graph.buildRootNode("ENTER " + methodDeclaration.getNameAsString(), methodDeclaration); + graph.buildRootNode("ENTER " + methodDeclaration.getNameAsString(), methodDeclaration, new MethodNodeFactory()); hangingNodes.add(graph.getRootNode().get()); methodDeclaration.getBody().get().accept(this, arg); diff --git a/src/main/java/tfm/graphs/cfg/CFGBuilder.java b/src/main/java/tfm/graphs/cfg/CFGBuilder.java index f686d67..e14c721 100644 --- a/src/main/java/tfm/graphs/cfg/CFGBuilder.java +++ b/src/main/java/tfm/graphs/cfg/CFGBuilder.java @@ -2,13 +2,16 @@ 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.body.Parameter; +import com.github.javaparser.ast.body.VariableDeclarator; +import com.github.javaparser.ast.expr.*; import com.github.javaparser.ast.stmt.*; import com.github.javaparser.ast.visitor.VoidVisitor; import com.github.javaparser.ast.visitor.VoidVisitorAdapter; import tfm.nodes.GraphNode; +import tfm.nodes.factories.InVariableNodeFactory; +import tfm.nodes.factories.MethodNodeFactory; +import tfm.nodes.factories.OutVariableNodeFactory; import tfm.utils.ASTUtils; import java.util.*; @@ -30,22 +33,38 @@ import java.util.*; * */ public class CFGBuilder extends VoidVisitorAdapter { - /** Stores the CFG representing the method analyzed. */ + /** + * 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. */ + /** + * 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. */ + /** + * 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. */ + /** + * 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. */ + /** + * 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. */ + /** + * 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 */ + /** + * 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 */ + /** + * Stack of lists of hanging cases on switch statements + */ protected final Deque>> switchEntriesStack = new LinkedList<>(); protected CFGBuilder(CFG graph) { @@ -197,7 +216,9 @@ public class CFGBuilder extends VoidVisitorAdapter { hangingNodes.addAll(breakStack.pop()); } - /** Switch entry, considered part of the condition of the switch. */ + /** + * Switch entry, considered part of the condition of the switch. + */ @Override public void visit(SwitchEntryStmt entryStmt, Void arg) { // Case header (prev -> case EXPR) @@ -270,11 +291,58 @@ public class CFGBuilder extends VoidVisitorAdapter { if (!methodDeclaration.getBody().isPresent()) throw new IllegalStateException("The method must have a body!"); - graph.buildRootNode("ENTER " + methodDeclaration.getNameAsString(), methodDeclaration); + graph.buildRootNode("ENTER " + methodDeclaration.getNameAsString(), methodDeclaration, new MethodNodeFactory()); + + // Compute variable in and out expressions (necessary to compute data dependence in SDG) + List inVariableExpressions = new ArrayList<>(); + List outVariableExpressions = new ArrayList<>(); + + for (Parameter parameter : methodDeclaration.getParameters()) { + // In expression + VariableDeclarationExpr inVariableDeclarationExpr = new VariableDeclarationExpr( + new VariableDeclarator( + parameter.getType(), + parameter.getNameAsString(), + new NameExpr(parameter.getNameAsString() + "_in") + ) + ); + + ExpressionStmt inExprStmt = new ExpressionStmt(inVariableDeclarationExpr); + + inVariableExpressions.add(inExprStmt); + + // Out expression + VariableDeclarationExpr outVariableDeclarationExpr = new VariableDeclarationExpr( + new VariableDeclarator( + parameter.getType(), + parameter.getNameAsString() + "_out", + new NameExpr(parameter.getNameAsString()) + ) + ); + + ExpressionStmt outExprStmt = new ExpressionStmt(outVariableDeclarationExpr); + + outVariableExpressions.add(outExprStmt); + } hangingNodes.add(graph.getRootNode().get()); + + // Add in variable nodes + for (ExpressionStmt expressionStmt : inVariableExpressions) { + GraphNode node = this.graph.addNode(expressionStmt.toString(), expressionStmt, new InVariableNodeFactory()); + connectTo(node); + } + methodDeclaration.getBody().get().accept(this, arg); + returnList.stream().filter(node -> !hangingNodes.contains(node)).forEach(hangingNodes::add); + + // Add out variable nodes + for (ExpressionStmt expressionStmt : outVariableExpressions) { + GraphNode node = this.graph.addNode(expressionStmt.toString(), expressionStmt, new OutVariableNodeFactory()); + connectTo(node); + } + GraphNode exitNode = connectTo(new EmptyStmt(), "Exit"); graph.setExitNode(exitNode); } diff --git a/src/main/java/tfm/graphs/pdg/DataDependencyBuilder.java b/src/main/java/tfm/graphs/pdg/DataDependencyBuilder.java index 42042b6..f8d11d5 100644 --- a/src/main/java/tfm/graphs/pdg/DataDependencyBuilder.java +++ b/src/main/java/tfm/graphs/pdg/DataDependencyBuilder.java @@ -6,6 +6,8 @@ import com.github.javaparser.ast.visitor.VoidVisitorAdapter; import tfm.graphs.cfg.CFG; import tfm.nodes.GraphNode; +import java.util.Optional; + class DataDependencyBuilder extends VoidVisitorAdapter { private CFG cfg; @@ -71,8 +73,16 @@ class DataDependencyBuilder extends VoidVisitorAdapter { switchEntryStmt.getStatements().accept(this, null); } + @Override + public void visit(ReturnStmt n, Void arg) { + buildDataDependency(n); + } + private void buildDataDependency(Node node) { - buildDataDependency(pdg.findNodeByASTNode(node).get()); + Optional> optionalGraphNode = pdg.findNodeByASTNode(node); + assert optionalGraphNode.isPresent(); + + buildDataDependency(optionalGraphNode.get()); } private void buildDataDependency(GraphNode node) { diff --git a/src/main/java/tfm/graphs/pdg/PDGBuilder.java b/src/main/java/tfm/graphs/pdg/PDGBuilder.java index 62aad5a..2b2f98e 100644 --- a/src/main/java/tfm/graphs/pdg/PDGBuilder.java +++ b/src/main/java/tfm/graphs/pdg/PDGBuilder.java @@ -60,5 +60,7 @@ public class PDGBuilder { // Build data dependency DataDependencyBuilder dataDependencyBuilder = new DataDependencyBuilder(pdg, cfg); methodBody.accept(dataDependencyBuilder, null); + + // Build data dependency of "out" variables } } diff --git a/src/main/java/tfm/graphs/sdg/SDGBuilder.java b/src/main/java/tfm/graphs/sdg/SDGBuilder.java index ba042d8..356dd05 100644 --- a/src/main/java/tfm/graphs/sdg/SDGBuilder.java +++ b/src/main/java/tfm/graphs/sdg/SDGBuilder.java @@ -53,46 +53,6 @@ class SDGBuilder extends VoidVisitorAdapter { // Add root node from PDG sdg.addRootNode(context, methodDeclarationNode.getId()); - - for (Parameter parameter : methodDeclaration.getParameters()) { - // In node - AssignExpr inAssignExpr = new AssignExpr(); - ExpressionStmt inExprStmt = new ExpressionStmt(inAssignExpr); - - inAssignExpr.setTarget( - new VariableDeclarationExpr( - parameter.getType(), - parameter.getNameAsString() - ) - ); - - inAssignExpr.setOperator(AssignExpr.Operator.ASSIGN); - - inAssignExpr.setValue(new NameExpr(parameter.getNameAsString() + "_in")); - - GraphNode inNode = sdg.addNode(inAssignExpr.toString(), inExprStmt); - - sdg.addControlDependencyArc(methodDeclarationNode, inNode); - - // Out node - AssignExpr outAssignExpr = new AssignExpr(); - ExpressionStmt outExprStmt = new ExpressionStmt(outAssignExpr); - - outAssignExpr.setTarget( - new VariableDeclarationExpr( - parameter.getType(), - parameter.getNameAsString() + "_out" - ) - ); - - outAssignExpr.setOperator(AssignExpr.Operator.ASSIGN); - - outAssignExpr.setValue(new NameExpr(parameter.getNameAsString())); - - GraphNode outNode = sdg.addNode(outAssignExpr.toString(), outExprStmt); - - sdg.addControlDependencyArc(methodDeclarationNode, outNode); - } } @Override diff --git a/src/main/java/tfm/nodes/AbstractTypeNodeFactory.java b/src/main/java/tfm/nodes/AbstractTypeNodeFactory.java new file mode 100644 index 0000000..3c73f2d --- /dev/null +++ b/src/main/java/tfm/nodes/AbstractTypeNodeFactory.java @@ -0,0 +1,48 @@ +package tfm.nodes; + +import com.github.javaparser.ast.Node; +import org.jetbrains.annotations.NotNull; +import tfm.nodes.factories.NodeFactory; +import tfm.nodes.type.NodeType; + +import java.util.Collection; +import java.util.Objects; + +public abstract class AbstractTypeNodeFactory implements NodeFactory { + + public GraphNode computedGraphNode( + @NotNull String instruction, + @NotNull ASTNode node, + @NotNull Collection declaredVariables, + @NotNull Collection definedVariables, + @NotNull Collection usedVariables + ) { + Objects.requireNonNull(instruction, "Instruction cannot be null!"); + Objects.requireNonNull(node, "AST Node cannot be null"); + Objects.requireNonNull(declaredVariables, "declared variables collection cannot be null!"); + Objects.requireNonNull(definedVariables, "defined variables collection cannot be null"); + Objects.requireNonNull(usedVariables, "Used variables collection cannot be null!"); + + return new GraphNode<>( + IdHelper.getInstance().getNextId(), + getSpecificType(), + instruction, + node, + declaredVariables, + definedVariables, + usedVariables + ); + } + + public GraphNode graphNode( + @NotNull String instruction, + @NotNull ASTNode node + ) { + Objects.requireNonNull(instruction, "Instruction cannot be null!"); + Objects.requireNonNull(node, "AST Node cannot be null"); + + return new GraphNode<>(IdHelper.getInstance().getNextId(), getSpecificType(), instruction, node); + } + + protected abstract NodeType getSpecificType(); +} diff --git a/src/main/java/tfm/nodes/GraphNode.java b/src/main/java/tfm/nodes/GraphNode.java index aa41f1a..a254bf7 100644 --- a/src/main/java/tfm/nodes/GraphNode.java +++ b/src/main/java/tfm/nodes/GraphNode.java @@ -1,12 +1,13 @@ package tfm.nodes; import com.github.javaparser.ast.Node; -import com.github.javaparser.ast.stmt.Statement; import org.jetbrains.annotations.NotNull; import tfm.graphs.cfg.CFG; import tfm.graphs.pdg.PDG; import tfm.graphs.sdg.SDG; -import tfm.utils.ASTUtils; +import tfm.nodes.factories.NodeFactory; +import tfm.nodes.factories.StatementNodeFactory; +import tfm.nodes.type.NodeType; import tfm.utils.Utils; import tfm.variables.VariableExtractor; @@ -27,6 +28,10 @@ import java.util.Set; */ public class GraphNode implements Comparable> { + public static final NodeFactory DEFAULT_FACTORY = new StatementNodeFactory(); + + private NodeType nodeType; + private final long id; private final String instruction; private final N astNode; @@ -35,9 +40,10 @@ public class GraphNode implements Comparable> { private final Set definedVariables; private final Set usedVariables; - GraphNode(long id, String instruction, @NotNull N astNode) { + GraphNode(long id, NodeType type, String instruction, @NotNull N astNode) { this( id, + type, instruction, astNode, Utils.emptySet(), @@ -50,6 +56,7 @@ public class GraphNode implements Comparable> { GraphNode( long id, + NodeType type, String instruction, @NotNull N astNode, Collection declaredVariables, @@ -57,6 +64,7 @@ public class GraphNode implements Comparable> { Collection usedVariables ) { this.id = id; + this.nodeType = type; this.instruction = instruction; this.astNode = astNode; @@ -78,8 +86,9 @@ public class GraphNode implements Comparable> { } public String toString() { - return String.format("GraphNode{id: %s, instruction: '%s', astNodeType: %s}", + return String.format("GraphNode{id: %s, type: %s, instruction: '%s', astNodeType: %s}", getId(), + getNodeType(), getInstruction(), getAstNode().getClass().getSimpleName() ); @@ -112,13 +121,14 @@ public class GraphNode implements Comparable> { GraphNode other = (GraphNode) o; return Objects.equals(getId(), other.getId()) + && Objects.equals(getNodeType(), other.getNodeType()) && Objects.equals(getInstruction(), other.getInstruction()) && Objects.equals(astNode, other.astNode); } @Override public int hashCode() { - return Objects.hash(getId(), getInstruction(), getAstNode()); + return Objects.hash(getId(), getNodeType(), getInstruction(), getAstNode()); } public Set getDeclaredVariables() { @@ -141,4 +151,8 @@ public class GraphNode implements Comparable> { public int compareTo(@NotNull GraphNode o) { return Long.compare(id, o.id); } + + public NodeType getNodeType() { + return nodeType; + } } diff --git a/src/main/java/tfm/nodes/IdHelper.java b/src/main/java/tfm/nodes/IdHelper.java index 27cb204..94e267b 100644 --- a/src/main/java/tfm/nodes/IdHelper.java +++ b/src/main/java/tfm/nodes/IdHelper.java @@ -12,11 +12,11 @@ class IdHelper { nextId = START_ID; } - public static IdHelper getInstance() { + static IdHelper getInstance() { return INSTANCE; } - public synchronized long getNextId() { + synchronized long getNextId() { return nextId++; } } diff --git a/src/main/java/tfm/nodes/NodeFactory.java b/src/main/java/tfm/nodes/NodeFactory.java deleted file mode 100644 index 52ab649..0000000 --- a/src/main/java/tfm/nodes/NodeFactory.java +++ /dev/null @@ -1,66 +0,0 @@ -package tfm.nodes; - -import com.github.javaparser.ast.Node; - -import java.util.Collection; -import java.util.Objects; - -public class NodeFactory { - - /** - * Returns a computed GraphNode (i.e. a GraphNode with computed the - * declared, defined and used variables in its AST node) - * - * @param instruction the instruction that represents - * @param node the node of the AST that represents - * @param declaredVariables the set of declared variables - * @param definedVariables the set of defined variables - * @param usedVariables the set of used variables - * @param the type of the AST node - * @return a new GraphNode - */ - public static GraphNode computedGraphNode( - String instruction, - ASTNode node, - Collection declaredVariables, - Collection definedVariables, - Collection usedVariables - ) { - Objects.requireNonNull(instruction, "Instruction cannot be null!"); - Objects.requireNonNull(node, "AST Node cannot be null"); - Objects.requireNonNull(declaredVariables, "declared variables collection cannot be null!"); - Objects.requireNonNull(definedVariables, "defined variables collection cannot be null"); - Objects.requireNonNull(usedVariables, "Used variables collection cannot be null!"); - - return new GraphNode<>( - IdHelper.getInstance().getNextId(), - instruction, - node, - declaredVariables, - definedVariables, - usedVariables - ); - } - - /** - * Returns a GraphNode computing the declared, defined and used variables in its AST node - * - * @param instruction the instruction that represents - * @param node the node of the AST that represents - * @param the type of the AST node - * @return a new GraphNode - */ - public static GraphNode graphNode( - String instruction, - ASTNode node - ) { - Objects.requireNonNull(instruction, "Instruction cannot be null!"); - Objects.requireNonNull(node, "AST Node cannot be null"); - - return new GraphNode<>( - IdHelper.getInstance().getNextId(), - instruction, - node - ); - } -} diff --git a/src/main/java/tfm/nodes/factories/InVariableNodeFactory.java b/src/main/java/tfm/nodes/factories/InVariableNodeFactory.java new file mode 100644 index 0000000..751b6bc --- /dev/null +++ b/src/main/java/tfm/nodes/factories/InVariableNodeFactory.java @@ -0,0 +1,11 @@ +package tfm.nodes.factories; + +import tfm.nodes.AbstractTypeNodeFactory; +import tfm.nodes.type.NodeType; + +public class InVariableNodeFactory extends AbstractTypeNodeFactory { + @Override + protected NodeType getSpecificType() { + return NodeType.VARIABLE_IN; + } +} diff --git a/src/main/java/tfm/nodes/factories/MethodNodeFactory.java b/src/main/java/tfm/nodes/factories/MethodNodeFactory.java new file mode 100644 index 0000000..318aeef --- /dev/null +++ b/src/main/java/tfm/nodes/factories/MethodNodeFactory.java @@ -0,0 +1,11 @@ +package tfm.nodes.factories; + +import tfm.nodes.AbstractTypeNodeFactory; +import tfm.nodes.type.NodeType; + +public class MethodNodeFactory extends AbstractTypeNodeFactory { + @Override + protected NodeType getSpecificType() { + return NodeType.METHOD; + } +} diff --git a/src/main/java/tfm/nodes/factories/NodeFactory.java b/src/main/java/tfm/nodes/factories/NodeFactory.java new file mode 100644 index 0000000..5682d7f --- /dev/null +++ b/src/main/java/tfm/nodes/factories/NodeFactory.java @@ -0,0 +1,44 @@ +package tfm.nodes.factories; + +import com.github.javaparser.ast.Node; +import org.jetbrains.annotations.NotNull; +import tfm.nodes.GraphNode; + +import java.util.Collection; + +public interface NodeFactory { + + /** + * Returns a computed GraphNode (i.e. a GraphNode with computed the + * declared, defined and used variables in its AST node) + * + * @param instruction the instruction that represents + * @param node the node of the AST that represents + * @param declaredVariables the set of declared variables + * @param definedVariables the set of defined variables + * @param usedVariables the set of used variables + * @param the type of the AST node + * @return a new GraphNode + */ + GraphNode computedGraphNode( + @NotNull String instruction, + @NotNull ASTNode node, + @NotNull Collection declaredVariables, + @NotNull Collection definedVariables, + @NotNull Collection usedVariables + ); + + + /** + * Returns a GraphNode computing the declared, defined and used variables in its AST node + * + * @param instruction the instruction that represents + * @param node the node of the AST that represents + * @param the type of the AST node + * @return a new GraphNode + */ + public GraphNode graphNode( + @NotNull String instruction, + @NotNull ASTNode node + ); +} diff --git a/src/main/java/tfm/nodes/factories/OutVariableNodeFactory.java b/src/main/java/tfm/nodes/factories/OutVariableNodeFactory.java new file mode 100644 index 0000000..8beba20 --- /dev/null +++ b/src/main/java/tfm/nodes/factories/OutVariableNodeFactory.java @@ -0,0 +1,11 @@ +package tfm.nodes.factories; + +import tfm.nodes.AbstractTypeNodeFactory; +import tfm.nodes.type.NodeType; + +public class OutVariableNodeFactory extends AbstractTypeNodeFactory { + @Override + protected NodeType getSpecificType() { + return NodeType.VARIABLE_OUT; + } +} diff --git a/src/main/java/tfm/nodes/factories/StatementNodeFactory.java b/src/main/java/tfm/nodes/factories/StatementNodeFactory.java new file mode 100644 index 0000000..3ac02e2 --- /dev/null +++ b/src/main/java/tfm/nodes/factories/StatementNodeFactory.java @@ -0,0 +1,11 @@ +package tfm.nodes.factories; + +import tfm.nodes.AbstractTypeNodeFactory; +import tfm.nodes.type.NodeType; + +public class StatementNodeFactory extends AbstractTypeNodeFactory { + @Override + protected NodeType getSpecificType() { + return NodeType.STATEMENT; + } +} diff --git a/src/main/java/tfm/nodes/type/NodeType.java b/src/main/java/tfm/nodes/type/NodeType.java new file mode 100644 index 0000000..90647d0 --- /dev/null +++ b/src/main/java/tfm/nodes/type/NodeType.java @@ -0,0 +1,8 @@ +package tfm.nodes.type; + +public enum NodeType { + STATEMENT, + METHOD, + VARIABLE_IN, + VARIABLE_OUT +} diff --git a/src/test/java/tfm/graphs/pdg/HandCraftedGraphs.java b/src/test/java/tfm/graphs/pdg/HandCraftedGraphs.java index 698bcc5..a1e187f 100644 --- a/src/test/java/tfm/graphs/pdg/HandCraftedGraphs.java +++ b/src/test/java/tfm/graphs/pdg/HandCraftedGraphs.java @@ -12,12 +12,13 @@ import tfm.graphs.augmented.ACFG; import tfm.graphs.augmented.APDG; import tfm.graphs.augmented.PPDG; import tfm.nodes.GraphNode; +import tfm.nodes.factories.MethodNodeFactory; public class HandCraftedGraphs { public static APDG problem1WithGotos() { // Generate the control flow of a graph ACFG cfg = new ACFG(); - cfg.buildRootNode("ENTER Problem1", new MethodDeclaration(new NodeList<>(), new VoidType(), "Problem1")); + cfg.buildRootNode("ENTER Problem1", new MethodDeclaration(new NodeList<>(), new VoidType(), "Problem1"), new MethodNodeFactory()); GraphNode wx = cfg.addNode("while (X)", new WhileStmt()); GraphNode ify = cfg.addNode("L: if (Y)", new IfStmt()); GraphNode ifz = cfg.addNode("if (Z)", new IfStmt()); @@ -56,7 +57,7 @@ public class HandCraftedGraphs { public static APDG problem1ContinueWithGotos() { // Generate the control flow of a graph ACFG cfg = new ACFG(); - cfg.buildRootNode("ENTER Problem1", new MethodDeclaration(new NodeList<>(), new VoidType(), "Problem1")); + cfg.buildRootNode("ENTER Problem1", new MethodDeclaration(new NodeList<>(), new VoidType(), "Problem1"), new MethodNodeFactory()); GraphNode wx = cfg.addNode("while (X)", new WhileStmt()); GraphNode ify = cfg.addNode("L: if (Y)", new IfStmt()); GraphNode ifz = cfg.addNode("if (Z)", new IfStmt()); diff --git a/src/test/java/tfm/graphs/pdg/PDGTests.java b/src/test/java/tfm/graphs/pdg/PDGTests.java index 7b0d777..6f53730 100644 --- a/src/test/java/tfm/graphs/pdg/PDGTests.java +++ b/src/test/java/tfm/graphs/pdg/PDGTests.java @@ -16,6 +16,7 @@ import tfm.graphs.augmented.APDG; import tfm.graphs.augmented.PPDG; import tfm.graphs.cfg.CFG; import tfm.nodes.GraphNode; +import tfm.nodes.factories.MethodNodeFactory; import tfm.slicing.GraphNodeCriterion; import tfm.slicing.Slice; import tfm.slicing.SlicingCriterion; @@ -80,7 +81,7 @@ public class PDGTests { CFG cfg = new CFG(); cfg.build(root); PDG pdg = new PDG(cfg); - pdg.buildRootNode("ENTER " + methodName, root); + pdg.buildRootNode("ENTER " + methodName, root, new MethodNodeFactory()); ctrlDepBuilder = new ControlDependencyBuilder(pdg, cfg); ctrlDepBuilder.analyze(); @@ -88,13 +89,13 @@ public class PDGTests { ACFG acfg = new ACFG(); acfg.build(root); APDG apdg = new APDG(acfg); - apdg.buildRootNode("ENTER " + methodName, root); + apdg.buildRootNode("ENTER " + methodName, root, new MethodNodeFactory()); ctrlDepBuilder = new ControlDependencyBuilder(apdg, acfg); ctrlDepBuilder.analyze(); // Create PPDG PPDG ppdg = new PPDG(acfg); - ppdg.buildRootNode("ENTER " + methodName, root); + ppdg.buildRootNode("ENTER " + methodName, root, new MethodNodeFactory()); ctrlDepBuilder = new ControlDependencyBuilder(ppdg, acfg); ctrlDepBuilder.analyze(); -- GitLab From cc05488cef454bdd6be9ae04611af83ec55ed142 Mon Sep 17 00:00:00 2001 From: jacosro Date: Mon, 2 Mar 2020 01:26:15 +0100 Subject: [PATCH 08/20] Add data dependency for out variables --- src/main/java/tfm/graphs/pdg/PDGBuilder.java | 29 ++++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/src/main/java/tfm/graphs/pdg/PDGBuilder.java b/src/main/java/tfm/graphs/pdg/PDGBuilder.java index 2b2f98e..3573f6b 100644 --- a/src/main/java/tfm/graphs/pdg/PDGBuilder.java +++ b/src/main/java/tfm/graphs/pdg/PDGBuilder.java @@ -1,9 +1,14 @@ package tfm.graphs.pdg; import com.github.javaparser.ast.body.MethodDeclaration; +import com.github.javaparser.ast.body.VariableDeclarator; +import com.github.javaparser.ast.expr.Expression; +import com.github.javaparser.ast.expr.VariableDeclarationExpr; import com.github.javaparser.ast.stmt.BlockStmt; +import com.github.javaparser.ast.stmt.ExpressionStmt; import tfm.graphs.cfg.CFG; import tfm.nodes.GraphNode; +import tfm.nodes.type.NodeType; import java.util.Objects; @@ -62,5 +67,29 @@ public class PDGBuilder { methodBody.accept(dataDependencyBuilder, null); // Build data dependency of "out" variables + pdg.vertexSet().stream() + .filter(node -> node.getNodeType() == NodeType.VARIABLE_OUT) + .forEach(node -> { + assert node.getAstNode() instanceof ExpressionStmt; + + Expression expression = ((ExpressionStmt) node.getAstNode()).getExpression(); + + assert expression.isVariableDeclarationExpr(); + + VariableDeclarationExpr variableDeclarationExpr = expression.asVariableDeclarationExpr(); + + // There should be only 1 variableDeclarator + assert variableDeclarationExpr.getVariables().size() == 1; + + VariableDeclarator variableDeclarator = variableDeclarationExpr.getVariables().get(0); + + assert variableDeclarator.getInitializer().isPresent(); + assert variableDeclarator.getInitializer().get().isNameExpr(); + + String variable = variableDeclarator.getInitializer().get().asNameExpr().getNameAsString(); + + cfg.findLastDefinitionsFrom(node, variable) + .forEach(variableDefinitionNode -> pdg.addDataDependencyArc(variableDefinitionNode, node, variable)); + }); } } -- GitLab From 14b6ef1467a7024cf2060c8c85a0e48e75a2c24d Mon Sep 17 00:00:00 2001 From: jacosro Date: Tue, 3 Mar 2020 00:34:57 +0100 Subject: [PATCH 09/20] WIP: MethodCallReplacerVisitor --- .../graphs/sdg/MethodCallReplacerVisitor.java | 176 ++++++++++++++++-- 1 file changed, 162 insertions(+), 14 deletions(-) diff --git a/src/main/java/tfm/graphs/sdg/MethodCallReplacerVisitor.java b/src/main/java/tfm/graphs/sdg/MethodCallReplacerVisitor.java index 1ff7999..6d8543b 100644 --- a/src/main/java/tfm/graphs/sdg/MethodCallReplacerVisitor.java +++ b/src/main/java/tfm/graphs/sdg/MethodCallReplacerVisitor.java @@ -1,15 +1,23 @@ package tfm.graphs.sdg; import com.github.javaparser.ast.Node; +import com.github.javaparser.ast.NodeList; import com.github.javaparser.ast.body.ClassOrInterfaceDeclaration; import com.github.javaparser.ast.body.MethodDeclaration; +import com.github.javaparser.ast.body.Parameter; import com.github.javaparser.ast.body.VariableDeclarator; +import com.github.javaparser.ast.expr.Expression; import com.github.javaparser.ast.expr.MethodCallExpr; +import com.github.javaparser.ast.expr.NameExpr; import com.github.javaparser.ast.expr.VariableDeclarationExpr; import com.github.javaparser.ast.stmt.ExpressionStmt; +import com.github.javaparser.ast.stmt.ReturnStmt; +import com.github.javaparser.ast.stmt.Statement; import com.github.javaparser.ast.type.Type; import com.github.javaparser.ast.visitor.VoidVisitorAdapter; import tfm.nodes.GraphNode; +import tfm.nodes.factories.InVariableNodeFactory; +import tfm.nodes.factories.OutVariableNodeFactory; import tfm.utils.Context; import tfm.utils.Logger; @@ -21,12 +29,25 @@ import java.util.stream.Collectors; class MethodCallReplacerVisitor extends VoidVisitorAdapter { private SDG sdg; - private GraphNode methodCallNode; + private GraphNode methodCallNode; public MethodCallReplacerVisitor(SDG sdg) { this.sdg = sdg; } + private void searchAndSetMethodCallNode(Node node) { + Optional> optionalNode = sdg.findNodeByASTNode(node); + assert optionalNode.isPresent(); + methodCallNode = optionalNode.get(); + + } + + @Override + public void visit(ReturnStmt n, Context arg) { + searchAndSetMethodCallNode(n); + super.visit(n, arg); + } + @Override public void visit(ExpressionStmt n, Context arg) { Optional> optionalNode = sdg.findNodeByASTNode(n); @@ -51,8 +72,48 @@ class MethodCallReplacerVisitor extends VoidVisitorAdapter { return; } + MethodDeclaration methodCalled = optionalCallingMethod.get(); + + Optional> optionalCalledMethodNode = sdg.findNodeByASTNode(methodCalled); + + assert optionalCalledMethodNode.isPresent(); + + GraphNode calledMethodNode = optionalCalledMethodNode.get(); + + sdg.addCallArc(methodCallNode, calledMethodNode); + + for (Parameter parameter : calledMethodNode.getAstNode().getParameters()) { + // In expression + VariableDeclarationExpr inVariableDeclarationExpr = new VariableDeclarationExpr( + new VariableDeclarator( + parameter.getType(), + parameter.getNameAsString() + "_in", + new NameExpr(parameter.getNameAsString()) + ) + ); + + ExpressionStmt inExprStmt = new ExpressionStmt(inVariableDeclarationExpr); + + GraphNode argumentInNode = sdg.addNode(inExprStmt.toString(), inExprStmt, new InVariableNodeFactory()); + + // Out expression + VariableDeclarationExpr outVariableDeclarationExpr = new VariableDeclarationExpr( + new VariableDeclarator( + parameter.getType(), + parameter.getNameAsString(), + new NameExpr(parameter.getNameAsString() + "_out") + ) + ); + + ExpressionStmt outExprStmt = new ExpressionStmt(outVariableDeclarationExpr); + + GraphNode argumentOutNode = sdg.addNode(outExprStmt.toString(), outExprStmt, new OutVariableNodeFactory()); + + + } + // todo make call - Logger.log(String.format("%s | Method '%s' called", methodCallExpr, optionalCallingMethod.get().getNameAsString())); + Logger.log("MethodCallReplacerVisitor", String.format("%s | Method '%s' called", methodCallExpr, methodCalled.getNameAsString())); } private Optional shouldMakeCallWithScope(MethodCallExpr methodCallExpr, Context context) { @@ -135,21 +196,108 @@ class MethodCallReplacerVisitor extends VoidVisitorAdapter { } private Optional findMethodInClass(MethodCallExpr methodCallExpr, ClassOrInterfaceDeclaration klass) { - String[] typeParameters = methodCallExpr.getTypeArguments() - .map(types -> types.stream() - .map(Node::toString) - .collect(Collectors.toList()) - .toArray(new String[types.size()]) - ).orElse(new String[]{}); - - List classMethods = - klass.getMethodsBySignature(methodCallExpr.getNameAsString(), typeParameters); + int argumentsCount = methodCallExpr.getArguments().size(); + + // Get methods with equal name and parameter count + List classMethods = klass.getMethodsByName(methodCallExpr.getNameAsString()).stream() + // Filter methods with equal or less (varargs) number of parameters + .filter(methodDeclaration -> { + NodeList parameters = methodDeclaration.getParameters(); + + if (parameters.size() == argumentsCount) { + return true; + } + + if (parameters.isEmpty()) { + return false; + } + + // There are more arguments than parameters. May be OK if last parameter is varargs + if (parameters.size() < argumentsCount) { + return parameters.get(parameters.size() - 1).isVarArgs(); + } + + // There are less arguments than parameters. May be OK if the last parameter is varargs + // and it's omited + return parameters.size() - 1 == argumentsCount + && parameters.get(parameters.size() - 1).isVarArgs(); + }) + .collect(Collectors.toList()); if (classMethods.isEmpty()) { - return Optional.empty(); // The method called is not inside the current class + // No methods in class with that name and parameter count + return Optional.empty(); + } + + if (classMethods.size() == 1) { + // We found the method! + return Optional.of(classMethods.get(0)); } - // The current method is inside the current class, so we make the call - return Optional.of(classMethods.get(0)); + /* + * Tricky one! We have to match argument and parameter types, so we have to: + * - Differentiate arguments expressions: + * - Easy: In case of CastExpr, get the type + * - Easy: In case of ObjectCreationExpr, get the type + * - Medium: In case of NameExpr, find the declaration and its type + * - Medium: In case of LiteralExpr, get the type + * - Hard: In case of MethodCallExpr, find MethodDeclaration and its type + * - If there is a varargs parameter, check every argument corresponding to it has the same type + * + * Example: + * At this point these three methods are considered as called: + * private void foo(int a, int b) {} + * private void foo(String a, String b) {} + * private void foo(String a, int... bs) {} + * + * We have to match types to get the correct one + * + * */ + + return classMethods.stream().filter(methodDeclaration -> { + boolean match = true; + + for (int i = 0; i < methodDeclaration.getParameters().size(); i++) { + if (!match) { + break; + } + + if (argumentsCount < i) { + return argumentsCount == i - 1 + && methodDeclaration.getParameter(i).isVarArgs(); + } + + // TODO - Convert into a visitor + + Expression argumentExpression = methodCallExpr.getArgument(i); + Parameter parameter = methodDeclaration.getParameter(i); + + if (argumentExpression.isCastExpr()) { + match = Objects.equals(argumentExpression.asCastExpr().getType(), parameter.getType()); + } else if (argumentExpression.isObjectCreationExpr()) { + match = Objects.equals(argumentExpression.asObjectCreationExpr().getType(), parameter.getType()); + } else if (argumentExpression.isNameExpr()) { + String variableName = argumentExpression.asNameExpr().getNameAsString(); + + List> declarationsOfVariable = sdg.findDeclarationsOfVariable(argumentExpression.asNameExpr().getNameAsString(), methodCallNode); + + assert !declarationsOfVariable.isEmpty(); + + GraphNode declarationNode = declarationsOfVariable.get(declarationsOfVariable.size() - 1); + + ExpressionStmt expressionStmt = (ExpressionStmt) declarationNode.getAstNode(); + + assert expressionStmt.getExpression().isVariableDeclarationExpr(); + + match = expressionStmt.getExpression().asVariableDeclarationExpr().getVariables().stream() + .filter(variableDeclarator -> Objects.equals(variableDeclarator.getName().asString(), variableName)) + .findFirst() + .map(variableDeclarator -> Objects.equals(variableDeclarator.getType(), parameter.getType())) + .orElse(false); + } // TODO: More checks + } + + return match; + }).findFirst(); } } -- GitLab From 645b5659b7562d046b01c1230494ff962a85cb42 Mon Sep 17 00:00:00 2001 From: jacosro Date: Wed, 4 Mar 2020 01:43:41 +0100 Subject: [PATCH 10/20] Added label to Arc --- src/main/java/tfm/arcs/Arc.java | 25 +++++--- .../java/tfm/arcs/pdg/DataDependencyArc.java | 20 +----- src/main/java/tfm/graphs/cfg/CFG.java | 1 + .../graphs/sdg/MethodCallReplacerVisitor.java | 61 ++++++++++++++++--- src/main/java/tfm/graphs/sdg/SDG.java | 2 +- 5 files changed, 72 insertions(+), 37 deletions(-) diff --git a/src/main/java/tfm/arcs/Arc.java b/src/main/java/tfm/arcs/Arc.java index 7a97b16..5af9bde 100644 --- a/src/main/java/tfm/arcs/Arc.java +++ b/src/main/java/tfm/arcs/Arc.java @@ -13,6 +13,16 @@ import java.util.Map; import java.util.Objects; public abstract class Arc extends DefaultEdge { + + private String label; + + protected Arc() { + } + + protected Arc(String label) { + this.label = label; + } + /** @see tfm.arcs.cfg.ControlFlowArc */ public final boolean isControlFlowArc() { return this instanceof ControlFlowArc; @@ -69,10 +79,6 @@ public abstract class Arc extends DefaultEdge { ((GraphNode) getSource()).getId(), ((GraphNode) getTarget()).getId()); } - public String getLabel() { - return ""; - } - public Map getDotAttributes() { return new HashMap<>(); } @@ -85,12 +91,17 @@ public abstract class Arc extends DefaultEdge { return false; if (!o.getClass().equals(this.getClass())) return false; - return Objects.equals(getSource(), ((Arc) o).getSource()) && - Objects.equals(getTarget(), ((Arc) o).getTarget()); + return Objects.equals(getSource(), ((Arc) o).getSource()) + && Objects.equals(getTarget(), ((Arc) o).getTarget()) + && Objects.equals(getLabel(), ((Arc) o).getLabel()); } @Override public int hashCode() { - return Objects.hash(getClass(), getSource(), getTarget()); + return Objects.hash(getClass(), getLabel(), getSource(), getTarget()); + } + + public String getLabel() { + return label; } } diff --git a/src/main/java/tfm/arcs/pdg/DataDependencyArc.java b/src/main/java/tfm/arcs/pdg/DataDependencyArc.java index 22a7bd5..649f84c 100644 --- a/src/main/java/tfm/arcs/pdg/DataDependencyArc.java +++ b/src/main/java/tfm/arcs/pdg/DataDependencyArc.java @@ -7,7 +7,6 @@ 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}, @@ -17,16 +16,9 @@ import java.util.Objects; * 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 String getLabel() { - return variable; + super(variable); } @Override @@ -36,15 +28,5 @@ public class DataDependencyArc extends Arc { map.put("color", DefaultAttribute.createAttribute("red")); return map; } - - @Override - public boolean equals(Object o) { - return super.equals(o) && Objects.equals(variable, ((DataDependencyArc) o).variable); - } - - @Override - public int hashCode() { - return Objects.hash(variable, super.hashCode()); - } } diff --git a/src/main/java/tfm/graphs/cfg/CFG.java b/src/main/java/tfm/graphs/cfg/CFG.java index c078314..97268d8 100644 --- a/src/main/java/tfm/graphs/cfg/CFG.java +++ b/src/main/java/tfm/graphs/cfg/CFG.java @@ -3,6 +3,7 @@ package tfm.graphs.cfg; import com.github.javaparser.ast.body.MethodDeclaration; import tfm.arcs.Arc; import tfm.arcs.cfg.ControlFlowArc; +import tfm.graphs.Graph; import tfm.graphs.GraphWithRootNode; import tfm.nodes.GraphNode; import tfm.utils.NodeNotFoundException; diff --git a/src/main/java/tfm/graphs/sdg/MethodCallReplacerVisitor.java b/src/main/java/tfm/graphs/sdg/MethodCallReplacerVisitor.java index 6d8543b..2aca612 100644 --- a/src/main/java/tfm/graphs/sdg/MethodCallReplacerVisitor.java +++ b/src/main/java/tfm/graphs/sdg/MethodCallReplacerVisitor.java @@ -10,9 +10,7 @@ import com.github.javaparser.ast.expr.Expression; import com.github.javaparser.ast.expr.MethodCallExpr; import com.github.javaparser.ast.expr.NameExpr; import com.github.javaparser.ast.expr.VariableDeclarationExpr; -import com.github.javaparser.ast.stmt.ExpressionStmt; -import com.github.javaparser.ast.stmt.ReturnStmt; -import com.github.javaparser.ast.stmt.Statement; +import com.github.javaparser.ast.stmt.*; import com.github.javaparser.ast.type.Type; import com.github.javaparser.ast.visitor.VoidVisitorAdapter; import tfm.nodes.GraphNode; @@ -43,24 +41,62 @@ class MethodCallReplacerVisitor extends VoidVisitorAdapter { } @Override - public void visit(ReturnStmt n, Context arg) { + public void visit(DoStmt n, Context arg) { searchAndSetMethodCallNode(n); super.visit(n, arg); } @Override - public void visit(ExpressionStmt n, Context arg) { - Optional> optionalNode = sdg.findNodeByASTNode(n); + public void visit(ForEachStmt n, Context arg) { + searchAndSetMethodCallNode(n); + super.visit(n, arg); + } - assert optionalNode.isPresent(); + @Override + public void visit(ForStmt n, Context arg) { + searchAndSetMethodCallNode(n); + super.visit(n, arg); + } - methodCallNode = optionalNode.get(); + @Override + public void visit(IfStmt n, Context arg) { + searchAndSetMethodCallNode(n); + super.visit(n, arg); + } + + @Override + public void visit(SwitchStmt n, Context arg) { + searchAndSetMethodCallNode(n); + super.visit(n, arg); + } + + @Override + public void visit(WhileStmt n, Context arg) { + searchAndSetMethodCallNode(n); + super.visit(n, arg); + } + @Override + public void visit(ReturnStmt n, Context arg) { + searchAndSetMethodCallNode(n); + super.visit(n, arg); + } + + @Override + public void visit(ExpressionStmt n, Context arg) { + searchAndSetMethodCallNode(n); super.visit(n, arg); } @Override public void visit(MethodCallExpr methodCallExpr, Context context) { + List arguments = methodCallExpr.getArguments(); + +// // Parse first method call expressions as arguments +// arguments.stream() +// .filter(Expression::isMethodCallExpr) +// .forEach(expression -> expression.accept(this, context)); + Logger.log("MethodCallReplacerVisitor", context); Optional optionalCallingMethod = methodCallExpr.getScope().isPresent() @@ -82,13 +118,18 @@ class MethodCallReplacerVisitor extends VoidVisitorAdapter { sdg.addCallArc(methodCallNode, calledMethodNode); - for (Parameter parameter : calledMethodNode.getAstNode().getParameters()) { + NodeList parameters = calledMethodNode.getAstNode().getParameters(); + + for (int i = 0; i < parameters.size(); i++) { + Parameter parameter = parameters.get(i); + Expression argument = arguments.get(i); + // In expression VariableDeclarationExpr inVariableDeclarationExpr = new VariableDeclarationExpr( new VariableDeclarator( parameter.getType(), parameter.getNameAsString() + "_in", - new NameExpr(parameter.getNameAsString()) + new NameExpr(argument.toString()) ) ); diff --git a/src/main/java/tfm/graphs/sdg/SDG.java b/src/main/java/tfm/graphs/sdg/SDG.java index 1e16f36..69389a3 100644 --- a/src/main/java/tfm/graphs/sdg/SDG.java +++ b/src/main/java/tfm/graphs/sdg/SDG.java @@ -96,7 +96,7 @@ public class SDG extends Graph implements Sliceable, Buildable from, GraphNode to) { + public void addCallArc(GraphNode from, GraphNode to) { this.addEdge(from, to, new CallArc()); } -- GitLab From e738cfd5408ce744df6c65021afa0b49dd908504 Mon Sep 17 00:00:00 2001 From: jacosro Date: Sun, 19 Apr 2020 20:54:03 +0200 Subject: [PATCH 11/20] Refactored NodeFactory --- .gitignore | 1 + src/main/java/tfm/graphs/Graph.java | 2 +- src/main/java/tfm/graphs/GraphWithRootNode.java | 2 +- src/main/java/tfm/graphs/augmented/ACFGBuilder.java | 5 +++-- src/main/java/tfm/graphs/cfg/CFGBuilder.java | 12 ++++++------ .../tfm/graphs/sdg/MethodCallReplacerVisitor.java | 8 ++++---- src/main/java/tfm/nodes/GraphNode.java | 4 +--- .../java/tfm/nodes/{factories => }/NodeFactory.java | 2 +- ...ractTypeNodeFactory.java => TypeNodeFactory.java} | 12 ++++++++++-- .../tfm/nodes/factories/InVariableNodeFactory.java | 11 ----------- .../java/tfm/nodes/factories/MethodNodeFactory.java | 11 ----------- .../tfm/nodes/factories/OutVariableNodeFactory.java | 11 ----------- .../tfm/nodes/factories/StatementNodeFactory.java | 11 ----------- src/test/java/tfm/graphs/pdg/HandCraftedGraphs.java | 7 ++++--- src/test/java/tfm/graphs/pdg/PDGTests.java | 9 +++++---- 15 files changed, 37 insertions(+), 71 deletions(-) rename src/main/java/tfm/nodes/{factories => }/NodeFactory.java (98%) rename src/main/java/tfm/nodes/{AbstractTypeNodeFactory.java => TypeNodeFactory.java} (85%) delete mode 100644 src/main/java/tfm/nodes/factories/InVariableNodeFactory.java delete mode 100644 src/main/java/tfm/nodes/factories/MethodNodeFactory.java delete mode 100644 src/main/java/tfm/nodes/factories/OutVariableNodeFactory.java delete mode 100644 src/main/java/tfm/nodes/factories/StatementNodeFactory.java diff --git a/.gitignore b/.gitignore index 8e69241..d627c53 100644 --- a/.gitignore +++ b/.gitignore @@ -3,3 +3,4 @@ target/ out/ .settings +.attach* diff --git a/src/main/java/tfm/graphs/Graph.java b/src/main/java/tfm/graphs/Graph.java index 7c5fd04..9326553 100644 --- a/src/main/java/tfm/graphs/Graph.java +++ b/src/main/java/tfm/graphs/Graph.java @@ -6,7 +6,7 @@ import org.jgrapht.graph.DirectedPseudograph; import org.jgrapht.io.DOTExporter; import tfm.arcs.Arc; import tfm.nodes.GraphNode; -import tfm.nodes.factories.NodeFactory; +import tfm.nodes.NodeFactory; import tfm.utils.ASTUtils; import java.util.*; diff --git a/src/main/java/tfm/graphs/GraphWithRootNode.java b/src/main/java/tfm/graphs/GraphWithRootNode.java index 6243243..d7a3442 100644 --- a/src/main/java/tfm/graphs/GraphWithRootNode.java +++ b/src/main/java/tfm/graphs/GraphWithRootNode.java @@ -4,7 +4,7 @@ import com.github.javaparser.ast.Node; import com.github.javaparser.ast.body.MethodDeclaration; import org.jetbrains.annotations.NotNull; import tfm.nodes.GraphNode; -import tfm.nodes.factories.NodeFactory; +import tfm.nodes.NodeFactory; import java.util.Objects; import java.util.Optional; diff --git a/src/main/java/tfm/graphs/augmented/ACFGBuilder.java b/src/main/java/tfm/graphs/augmented/ACFGBuilder.java index 1aafc91..3972f67 100644 --- a/src/main/java/tfm/graphs/augmented/ACFGBuilder.java +++ b/src/main/java/tfm/graphs/augmented/ACFGBuilder.java @@ -8,7 +8,8 @@ import com.github.javaparser.ast.stmt.*; import com.github.javaparser.ast.visitor.VoidVisitor; import tfm.graphs.cfg.CFGBuilder; import tfm.nodes.GraphNode; -import tfm.nodes.factories.MethodNodeFactory; +import tfm.nodes.TypeNodeFactory; +import tfm.nodes.type.NodeType; import tfm.utils.ASTUtils; import java.util.LinkedList; @@ -238,7 +239,7 @@ public class ACFGBuilder extends CFGBuilder { if (!methodDeclaration.getBody().isPresent()) throw new IllegalStateException("The method must have a body!"); - graph.buildRootNode("ENTER " + methodDeclaration.getNameAsString(), methodDeclaration, new MethodNodeFactory()); + graph.buildRootNode("ENTER " + methodDeclaration.getNameAsString(), methodDeclaration, TypeNodeFactory.fromType(NodeType.METHOD)); hangingNodes.add(graph.getRootNode().get()); methodDeclaration.getBody().get().accept(this, arg); diff --git a/src/main/java/tfm/graphs/cfg/CFGBuilder.java b/src/main/java/tfm/graphs/cfg/CFGBuilder.java index e14c721..624bb7b 100644 --- a/src/main/java/tfm/graphs/cfg/CFGBuilder.java +++ b/src/main/java/tfm/graphs/cfg/CFGBuilder.java @@ -9,9 +9,9 @@ import com.github.javaparser.ast.stmt.*; import com.github.javaparser.ast.visitor.VoidVisitor; import com.github.javaparser.ast.visitor.VoidVisitorAdapter; import tfm.nodes.GraphNode; -import tfm.nodes.factories.InVariableNodeFactory; -import tfm.nodes.factories.MethodNodeFactory; -import tfm.nodes.factories.OutVariableNodeFactory; +import tfm.nodes.NodeFactory; +import tfm.nodes.TypeNodeFactory; +import tfm.nodes.type.NodeType; import tfm.utils.ASTUtils; import java.util.*; @@ -291,7 +291,7 @@ public class CFGBuilder extends VoidVisitorAdapter { if (!methodDeclaration.getBody().isPresent()) throw new IllegalStateException("The method must have a body!"); - graph.buildRootNode("ENTER " + methodDeclaration.getNameAsString(), methodDeclaration, new MethodNodeFactory()); + graph.buildRootNode("ENTER " + methodDeclaration.getNameAsString(), methodDeclaration, TypeNodeFactory.fromType(NodeType.METHOD)); // Compute variable in and out expressions (necessary to compute data dependence in SDG) List inVariableExpressions = new ArrayList<>(); @@ -329,7 +329,7 @@ public class CFGBuilder extends VoidVisitorAdapter { // Add in variable nodes for (ExpressionStmt expressionStmt : inVariableExpressions) { - GraphNode node = this.graph.addNode(expressionStmt.toString(), expressionStmt, new InVariableNodeFactory()); + GraphNode node = this.graph.addNode(expressionStmt.toString(), expressionStmt, TypeNodeFactory.fromType(NodeType.VARIABLE_IN)); connectTo(node); } @@ -339,7 +339,7 @@ public class CFGBuilder extends VoidVisitorAdapter { // Add out variable nodes for (ExpressionStmt expressionStmt : outVariableExpressions) { - GraphNode node = this.graph.addNode(expressionStmt.toString(), expressionStmt, new OutVariableNodeFactory()); + GraphNode node = this.graph.addNode(expressionStmt.toString(), expressionStmt, TypeNodeFactory.fromType(NodeType.VARIABLE_OUT)); connectTo(node); } diff --git a/src/main/java/tfm/graphs/sdg/MethodCallReplacerVisitor.java b/src/main/java/tfm/graphs/sdg/MethodCallReplacerVisitor.java index 2aca612..0c07d11 100644 --- a/src/main/java/tfm/graphs/sdg/MethodCallReplacerVisitor.java +++ b/src/main/java/tfm/graphs/sdg/MethodCallReplacerVisitor.java @@ -14,8 +14,8 @@ import com.github.javaparser.ast.stmt.*; import com.github.javaparser.ast.type.Type; import com.github.javaparser.ast.visitor.VoidVisitorAdapter; import tfm.nodes.GraphNode; -import tfm.nodes.factories.InVariableNodeFactory; -import tfm.nodes.factories.OutVariableNodeFactory; +import tfm.nodes.TypeNodeFactory; +import tfm.nodes.type.NodeType; import tfm.utils.Context; import tfm.utils.Logger; @@ -135,7 +135,7 @@ class MethodCallReplacerVisitor extends VoidVisitorAdapter { ExpressionStmt inExprStmt = new ExpressionStmt(inVariableDeclarationExpr); - GraphNode argumentInNode = sdg.addNode(inExprStmt.toString(), inExprStmt, new InVariableNodeFactory()); + GraphNode argumentInNode = sdg.addNode(inExprStmt.toString(), inExprStmt, TypeNodeFactory.fromType(NodeType.VARIABLE_IN)); // Out expression VariableDeclarationExpr outVariableDeclarationExpr = new VariableDeclarationExpr( @@ -148,7 +148,7 @@ class MethodCallReplacerVisitor extends VoidVisitorAdapter { ExpressionStmt outExprStmt = new ExpressionStmt(outVariableDeclarationExpr); - GraphNode argumentOutNode = sdg.addNode(outExprStmt.toString(), outExprStmt, new OutVariableNodeFactory()); + GraphNode argumentOutNode = sdg.addNode(outExprStmt.toString(), outExprStmt, TypeNodeFactory.fromType(NodeType.VARIABLE_OUT)); } diff --git a/src/main/java/tfm/nodes/GraphNode.java b/src/main/java/tfm/nodes/GraphNode.java index a254bf7..faabb2e 100644 --- a/src/main/java/tfm/nodes/GraphNode.java +++ b/src/main/java/tfm/nodes/GraphNode.java @@ -5,8 +5,6 @@ import org.jetbrains.annotations.NotNull; import tfm.graphs.cfg.CFG; import tfm.graphs.pdg.PDG; import tfm.graphs.sdg.SDG; -import tfm.nodes.factories.NodeFactory; -import tfm.nodes.factories.StatementNodeFactory; import tfm.nodes.type.NodeType; import tfm.utils.Utils; import tfm.variables.VariableExtractor; @@ -28,7 +26,7 @@ import java.util.Set; */ public class GraphNode implements Comparable> { - public static final NodeFactory DEFAULT_FACTORY = new StatementNodeFactory(); + public static final NodeFactory DEFAULT_FACTORY = TypeNodeFactory.fromType(NodeType.STATEMENT); private NodeType nodeType; diff --git a/src/main/java/tfm/nodes/factories/NodeFactory.java b/src/main/java/tfm/nodes/NodeFactory.java similarity index 98% rename from src/main/java/tfm/nodes/factories/NodeFactory.java rename to src/main/java/tfm/nodes/NodeFactory.java index 5682d7f..b54b2ef 100644 --- a/src/main/java/tfm/nodes/factories/NodeFactory.java +++ b/src/main/java/tfm/nodes/NodeFactory.java @@ -1,4 +1,4 @@ -package tfm.nodes.factories; +package tfm.nodes; import com.github.javaparser.ast.Node; import org.jetbrains.annotations.NotNull; diff --git a/src/main/java/tfm/nodes/AbstractTypeNodeFactory.java b/src/main/java/tfm/nodes/TypeNodeFactory.java similarity index 85% rename from src/main/java/tfm/nodes/AbstractTypeNodeFactory.java rename to src/main/java/tfm/nodes/TypeNodeFactory.java index 3c73f2d..56a2bb9 100644 --- a/src/main/java/tfm/nodes/AbstractTypeNodeFactory.java +++ b/src/main/java/tfm/nodes/TypeNodeFactory.java @@ -2,13 +2,21 @@ package tfm.nodes; import com.github.javaparser.ast.Node; import org.jetbrains.annotations.NotNull; -import tfm.nodes.factories.NodeFactory; import tfm.nodes.type.NodeType; import java.util.Collection; import java.util.Objects; -public abstract class AbstractTypeNodeFactory implements NodeFactory { +public abstract class TypeNodeFactory implements NodeFactory { + + public static TypeNodeFactory fromType(NodeType type) { + return new TypeNodeFactory() { + @Override + protected NodeType getSpecificType() { + return type; + } + }; + } public GraphNode computedGraphNode( @NotNull String instruction, diff --git a/src/main/java/tfm/nodes/factories/InVariableNodeFactory.java b/src/main/java/tfm/nodes/factories/InVariableNodeFactory.java deleted file mode 100644 index 751b6bc..0000000 --- a/src/main/java/tfm/nodes/factories/InVariableNodeFactory.java +++ /dev/null @@ -1,11 +0,0 @@ -package tfm.nodes.factories; - -import tfm.nodes.AbstractTypeNodeFactory; -import tfm.nodes.type.NodeType; - -public class InVariableNodeFactory extends AbstractTypeNodeFactory { - @Override - protected NodeType getSpecificType() { - return NodeType.VARIABLE_IN; - } -} diff --git a/src/main/java/tfm/nodes/factories/MethodNodeFactory.java b/src/main/java/tfm/nodes/factories/MethodNodeFactory.java deleted file mode 100644 index 318aeef..0000000 --- a/src/main/java/tfm/nodes/factories/MethodNodeFactory.java +++ /dev/null @@ -1,11 +0,0 @@ -package tfm.nodes.factories; - -import tfm.nodes.AbstractTypeNodeFactory; -import tfm.nodes.type.NodeType; - -public class MethodNodeFactory extends AbstractTypeNodeFactory { - @Override - protected NodeType getSpecificType() { - return NodeType.METHOD; - } -} diff --git a/src/main/java/tfm/nodes/factories/OutVariableNodeFactory.java b/src/main/java/tfm/nodes/factories/OutVariableNodeFactory.java deleted file mode 100644 index 8beba20..0000000 --- a/src/main/java/tfm/nodes/factories/OutVariableNodeFactory.java +++ /dev/null @@ -1,11 +0,0 @@ -package tfm.nodes.factories; - -import tfm.nodes.AbstractTypeNodeFactory; -import tfm.nodes.type.NodeType; - -public class OutVariableNodeFactory extends AbstractTypeNodeFactory { - @Override - protected NodeType getSpecificType() { - return NodeType.VARIABLE_OUT; - } -} diff --git a/src/main/java/tfm/nodes/factories/StatementNodeFactory.java b/src/main/java/tfm/nodes/factories/StatementNodeFactory.java deleted file mode 100644 index 3ac02e2..0000000 --- a/src/main/java/tfm/nodes/factories/StatementNodeFactory.java +++ /dev/null @@ -1,11 +0,0 @@ -package tfm.nodes.factories; - -import tfm.nodes.AbstractTypeNodeFactory; -import tfm.nodes.type.NodeType; - -public class StatementNodeFactory extends AbstractTypeNodeFactory { - @Override - protected NodeType getSpecificType() { - return NodeType.STATEMENT; - } -} diff --git a/src/test/java/tfm/graphs/pdg/HandCraftedGraphs.java b/src/test/java/tfm/graphs/pdg/HandCraftedGraphs.java index a1e187f..d07ab93 100644 --- a/src/test/java/tfm/graphs/pdg/HandCraftedGraphs.java +++ b/src/test/java/tfm/graphs/pdg/HandCraftedGraphs.java @@ -12,13 +12,14 @@ import tfm.graphs.augmented.ACFG; import tfm.graphs.augmented.APDG; import tfm.graphs.augmented.PPDG; import tfm.nodes.GraphNode; -import tfm.nodes.factories.MethodNodeFactory; +import tfm.nodes.TypeNodeFactory; +import tfm.nodes.type.NodeType; public class HandCraftedGraphs { public static APDG problem1WithGotos() { // Generate the control flow of a graph ACFG cfg = new ACFG(); - cfg.buildRootNode("ENTER Problem1", new MethodDeclaration(new NodeList<>(), new VoidType(), "Problem1"), new MethodNodeFactory()); + cfg.buildRootNode("ENTER Problem1", new MethodDeclaration(new NodeList<>(), new VoidType(), "Problem1"), TypeNodeFactory.fromType(NodeType.METHOD)); GraphNode wx = cfg.addNode("while (X)", new WhileStmt()); GraphNode ify = cfg.addNode("L: if (Y)", new IfStmt()); GraphNode ifz = cfg.addNode("if (Z)", new IfStmt()); @@ -57,7 +58,7 @@ public class HandCraftedGraphs { public static APDG problem1ContinueWithGotos() { // Generate the control flow of a graph ACFG cfg = new ACFG(); - cfg.buildRootNode("ENTER Problem1", new MethodDeclaration(new NodeList<>(), new VoidType(), "Problem1"), new MethodNodeFactory()); + cfg.buildRootNode("ENTER Problem1", new MethodDeclaration(new NodeList<>(), new VoidType(), "Problem1"), TypeNodeFactory.fromType(NodeType.METHOD)); GraphNode wx = cfg.addNode("while (X)", new WhileStmt()); GraphNode ify = cfg.addNode("L: if (Y)", new IfStmt()); GraphNode ifz = cfg.addNode("if (Z)", new IfStmt()); diff --git a/src/test/java/tfm/graphs/pdg/PDGTests.java b/src/test/java/tfm/graphs/pdg/PDGTests.java index 6f53730..2f6e547 100644 --- a/src/test/java/tfm/graphs/pdg/PDGTests.java +++ b/src/test/java/tfm/graphs/pdg/PDGTests.java @@ -16,7 +16,8 @@ import tfm.graphs.augmented.APDG; import tfm.graphs.augmented.PPDG; import tfm.graphs.cfg.CFG; import tfm.nodes.GraphNode; -import tfm.nodes.factories.MethodNodeFactory; +import tfm.nodes.TypeNodeFactory; +import tfm.nodes.type.NodeType; import tfm.slicing.GraphNodeCriterion; import tfm.slicing.Slice; import tfm.slicing.SlicingCriterion; @@ -81,7 +82,7 @@ public class PDGTests { CFG cfg = new CFG(); cfg.build(root); PDG pdg = new PDG(cfg); - pdg.buildRootNode("ENTER " + methodName, root, new MethodNodeFactory()); + pdg.buildRootNode("ENTER " + methodName, root, TypeNodeFactory.fromType(NodeType.METHOD)); ctrlDepBuilder = new ControlDependencyBuilder(pdg, cfg); ctrlDepBuilder.analyze(); @@ -89,13 +90,13 @@ public class PDGTests { ACFG acfg = new ACFG(); acfg.build(root); APDG apdg = new APDG(acfg); - apdg.buildRootNode("ENTER " + methodName, root, new MethodNodeFactory()); + apdg.buildRootNode("ENTER " + methodName, root, TypeNodeFactory.fromType(NodeType.METHOD)); ctrlDepBuilder = new ControlDependencyBuilder(apdg, acfg); ctrlDepBuilder.analyze(); // Create PPDG PPDG ppdg = new PPDG(acfg); - ppdg.buildRootNode("ENTER " + methodName, root, new MethodNodeFactory()); + ppdg.buildRootNode("ENTER " + methodName, root, TypeNodeFactory.fromType(NodeType.METHOD)); ctrlDepBuilder = new ControlDependencyBuilder(ppdg, acfg); ctrlDepBuilder.analyze(); -- GitLab From b70f8c105407d89b0aa18e2cda1df2893e8eb9a4 Mon Sep 17 00:00:00 2001 From: jacosro Date: Sat, 2 May 2020 01:39:15 +0200 Subject: [PATCH 12/20] Implemented resolving method calls with JavaParser --- src/main/java/tfm/exec/MethodResolver.java | 105 ++++++++++++++++++ .../graphs/sdg/MethodCallReplacerVisitor.java | 50 +++++++-- src/main/java/tfm/nodes/GraphNode.java | 2 +- 3 files changed, 145 insertions(+), 12 deletions(-) create mode 100644 src/main/java/tfm/exec/MethodResolver.java diff --git a/src/main/java/tfm/exec/MethodResolver.java b/src/main/java/tfm/exec/MethodResolver.java new file mode 100644 index 0000000..e2d28d6 --- /dev/null +++ b/src/main/java/tfm/exec/MethodResolver.java @@ -0,0 +1,105 @@ +package tfm.exec; + +import com.github.javaparser.JavaParser; +import com.github.javaparser.ast.CompilationUnit; +import com.github.javaparser.ast.NodeList; +import com.github.javaparser.ast.body.AnnotationDeclaration; +import com.github.javaparser.ast.body.MethodDeclaration; +import com.github.javaparser.ast.expr.MethodCallExpr; +import com.github.javaparser.ast.visitor.VoidVisitorAdapter; +import com.github.javaparser.resolution.UnsolvedSymbolException; +import com.github.javaparser.resolution.declarations.ResolvedMethodDeclaration; +import com.github.javaparser.symbolsolver.javaparsermodel.JavaParserFacade; +import com.github.javaparser.symbolsolver.model.resolution.SymbolReference; +import com.github.javaparser.symbolsolver.model.resolution.TypeSolver; +import com.github.javaparser.symbolsolver.resolution.typesolvers.CombinedTypeSolver; +import com.github.javaparser.symbolsolver.resolution.typesolvers.JavaParserTypeSolver; +import com.github.javaparser.symbolsolver.resolution.typesolvers.ReflectionTypeSolver; +import tfm.graphs.sdg.SDG; +import tfm.nodes.GraphNode; +import tfm.utils.Context; +import tfm.utils.Logger; + +import java.io.File; +import java.io.FileNotFoundException; +import java.util.Arrays; +import java.util.Optional; + +public class MethodResolver { + + private static class Args { + String file; + String method; + } + + public static void main(String[] inputArgs) throws FileNotFoundException { + Args args = parseArgs(inputArgs); + + CompilationUnit cu = JavaParser.parse(new File(args.file)); + + SDG sdg = new SDG(); + sdg.build(new NodeList<>(cu)); + + VoidVisitorAdapter visitor = new VoidVisitorAdapter() { + @Override + public void visit(MethodCallExpr n, Void arg) { + TypeSolver solver = new JavaParserTypeSolver(args.file.substring(0, args.file.lastIndexOf('/'))); + + Logger.log("-- Trying to solve method " + n.getNameAsString() + " --"); + + Optional optionalResolvedMethod; + + try { + optionalResolvedMethod = getMethodCallWithJavaParserSymbolSolver(n, solver, new ReflectionTypeSolver()); + } catch (UnsolvedSymbolException e) { + optionalResolvedMethod = Optional.empty(); + } + + if (!optionalResolvedMethod.isPresent()) { + Logger.format("Not found: %s", n); + return; + } + + Logger.format("Found: %s", n.getNameAsString()); + Logger.log(optionalResolvedMethod.get().getSignature().asString()); + + Logger.log("-- Trying to match with a node from SDG --"); + Optional> methodDeclarationNode = optionalResolvedMethod.flatMap(sdg::findNodeByASTNode); + + if (!methodDeclarationNode.isPresent()) { + Logger.log("Failed to find node in SDG"); + return; + } + + Logger.format("SDG node: %s", methodDeclarationNode.get()); + + } + }; + + cu.accept(visitor, null); + } + + private static Args parseArgs(String[] args) { + Args res = new Args(); + + Logger.log(Arrays.asList(args)); + + try { + res.file = args[0]; + // res.method = args[2]; + } catch (Exception e) { + Logger.log("Incorrect syntax: java MethodResolver.class "); + System.exit(1); + } + + return res; + } + + private static Optional getMethodCallWithJavaParserSymbolSolver(MethodCallExpr methodCallExpr, TypeSolver... solvers) { + CombinedTypeSolver combinedTypeSolver = new CombinedTypeSolver(solvers); + + SymbolReference solver = JavaParserFacade.get(combinedTypeSolver).solve(methodCallExpr); + + return solver.isSolved() ? solver.getCorrespondingDeclaration().toAst() : Optional.empty(); + } +} diff --git a/src/main/java/tfm/graphs/sdg/MethodCallReplacerVisitor.java b/src/main/java/tfm/graphs/sdg/MethodCallReplacerVisitor.java index 0c07d11..8beb9fe 100644 --- a/src/main/java/tfm/graphs/sdg/MethodCallReplacerVisitor.java +++ b/src/main/java/tfm/graphs/sdg/MethodCallReplacerVisitor.java @@ -1,5 +1,6 @@ package tfm.graphs.sdg; +import com.github.javaparser.ParserConfiguration; import com.github.javaparser.ast.Node; import com.github.javaparser.ast.NodeList; import com.github.javaparser.ast.body.ClassOrInterfaceDeclaration; @@ -13,12 +14,25 @@ import com.github.javaparser.ast.expr.VariableDeclarationExpr; import com.github.javaparser.ast.stmt.*; import com.github.javaparser.ast.type.Type; import com.github.javaparser.ast.visitor.VoidVisitorAdapter; +import com.github.javaparser.resolution.UnsolvedSymbolException; +import com.github.javaparser.resolution.declarations.ResolvedMethodDeclaration; +import com.github.javaparser.resolution.declarations.ResolvedReferenceTypeDeclaration; +import com.github.javaparser.symbolsolver.JavaSymbolSolver; +import com.github.javaparser.symbolsolver.SourceFileInfoExtractor; +import com.github.javaparser.symbolsolver.javaparsermodel.JavaParserFacade; +import com.github.javaparser.symbolsolver.model.resolution.SymbolReference; +import com.github.javaparser.symbolsolver.model.resolution.TypeSolver; +import com.github.javaparser.symbolsolver.resolution.SymbolSolver; +import com.github.javaparser.symbolsolver.resolution.typesolvers.CombinedTypeSolver; +import com.github.javaparser.symbolsolver.resolution.typesolvers.JavaParserTypeSolver; +import com.github.javaparser.symbolsolver.resolution.typesolvers.ReflectionTypeSolver; import tfm.nodes.GraphNode; import tfm.nodes.TypeNodeFactory; import tfm.nodes.type.NodeType; import tfm.utils.Context; import tfm.utils.Logger; +import java.nio.file.Path; import java.util.List; import java.util.Objects; import java.util.Optional; @@ -99,22 +113,16 @@ class MethodCallReplacerVisitor extends VoidVisitorAdapter { Logger.log("MethodCallReplacerVisitor", context); - Optional optionalCallingMethod = methodCallExpr.getScope().isPresent() - ? shouldMakeCallWithScope(methodCallExpr, context) - : shouldMakeCallWithNoScope(methodCallExpr, context); + Optional> optionalNethodDeclarationNode = getMethodDeclarationNodeWithJavaParser(methodCallExpr); - if (!optionalCallingMethod.isPresent()) { - Logger.log("Discarding: " + methodCallExpr); + if (!optionalNethodDeclarationNode.isPresent()) { + Logger.format("Not found: '%s'. Discarding"); return; } - MethodDeclaration methodCalled = optionalCallingMethod.get(); + GraphNode calledMethodNode = optionalNethodDeclarationNode.get(); - Optional> optionalCalledMethodNode = sdg.findNodeByASTNode(methodCalled); - - assert optionalCalledMethodNode.isPresent(); - - GraphNode calledMethodNode = optionalCalledMethodNode.get(); + MethodDeclaration methodCalled = calledMethodNode.getAstNode(); sdg.addCallArc(methodCallNode, calledMethodNode); @@ -157,6 +165,26 @@ class MethodCallReplacerVisitor extends VoidVisitorAdapter { Logger.log("MethodCallReplacerVisitor", String.format("%s | Method '%s' called", methodCallExpr, methodCalled.getNameAsString())); } + private Optional> getMethodDeclarationNodeWithJavaParser(MethodCallExpr methodCallExpr) { + TypeSolver typeSolver = new ReflectionTypeSolver(); + + try { + SymbolReference solver = JavaParserFacade.get(typeSolver).solve(methodCallExpr); + + return solver.isSolved() + ? solver.getCorrespondingDeclaration().toAst() + .flatMap(methodDeclaration -> sdg.findNodeByASTNode(methodDeclaration)) + : Optional.empty(); + } catch (UnsolvedSymbolException e) { + return Optional.empty(); + } + } + + /** + * Handles method calls with scope. Examples: + * - System.out.println() -> println() is a method call with scope System.out + * - new A().getB() -> getB() is a method call with scope new A() + */ private Optional shouldMakeCallWithScope(MethodCallExpr methodCallExpr, Context context) { assert methodCallExpr.getScope().isPresent(); diff --git a/src/main/java/tfm/nodes/GraphNode.java b/src/main/java/tfm/nodes/GraphNode.java index faabb2e..0a7295b 100644 --- a/src/main/java/tfm/nodes/GraphNode.java +++ b/src/main/java/tfm/nodes/GraphNode.java @@ -28,7 +28,7 @@ public class GraphNode implements Comparable> { public static final NodeFactory DEFAULT_FACTORY = TypeNodeFactory.fromType(NodeType.STATEMENT); - private NodeType nodeType; + private final NodeType nodeType; private final long id; private final String instruction; -- GitLab From 51fc74bb9cd2ffdac2aa0a512a62bfe020b04429 Mon Sep 17 00:00:00 2001 From: jacosro Date: Sat, 2 May 2020 22:22:15 +0200 Subject: [PATCH 13/20] WIP method call nodes --- .../tfm/graphs/sdg/MethodCallReplacer.java | 18 +- .../graphs/sdg/MethodCallReplacerVisitor.java | 192 +----------------- src/main/java/tfm/graphs/sdg/SDG.java | 45 +--- src/main/java/tfm/graphs/sdg/SDGBuilder.java | 6 +- 4 files changed, 19 insertions(+), 242 deletions(-) diff --git a/src/main/java/tfm/graphs/sdg/MethodCallReplacer.java b/src/main/java/tfm/graphs/sdg/MethodCallReplacer.java index ed9d4e6..e8290fd 100644 --- a/src/main/java/tfm/graphs/sdg/MethodCallReplacer.java +++ b/src/main/java/tfm/graphs/sdg/MethodCallReplacer.java @@ -16,19 +16,9 @@ class MethodCallReplacer { this.sdg = sdg; } - public void replace() { - this.sdg.getContexts().stream() - .filter(context -> context.getCurrentMethod().isPresent()) - .forEach(context -> { - Logger.log("MethodCallReplacer", context); - - Optional> optionalRootNode = this.sdg.getRootNode(context); - - if (!optionalRootNode.isPresent()) { - return; // We don't have visited the code (e.g. the MethodDeclaration for a method call) - } - - optionalRootNode.get().getAstNode().accept(new MethodCallReplacerVisitor(sdg), context); - }); + public void replace(Context context) { + for (MethodDeclaration methodDeclaration : this.sdg.getMethodDeclarations()) { + methodDeclaration.accept(new MethodCallReplacerVisitor(sdg), context); + } } } diff --git a/src/main/java/tfm/graphs/sdg/MethodCallReplacerVisitor.java b/src/main/java/tfm/graphs/sdg/MethodCallReplacerVisitor.java index 8beb9fe..5133020 100644 --- a/src/main/java/tfm/graphs/sdg/MethodCallReplacerVisitor.java +++ b/src/main/java/tfm/graphs/sdg/MethodCallReplacerVisitor.java @@ -145,6 +145,8 @@ class MethodCallReplacerVisitor extends VoidVisitorAdapter { GraphNode argumentInNode = sdg.addNode(inExprStmt.toString(), inExprStmt, TypeNodeFactory.fromType(NodeType.VARIABLE_IN)); + sdg.addControlDependencyArc(methodCallNode, argumentInNode); + // Out expression VariableDeclarationExpr outVariableDeclarationExpr = new VariableDeclarationExpr( new VariableDeclarator( @@ -179,194 +181,4 @@ class MethodCallReplacerVisitor extends VoidVisitorAdapter { return Optional.empty(); } } - - /** - * Handles method calls with scope. Examples: - * - System.out.println() -> println() is a method call with scope System.out - * - new A().getB() -> getB() is a method call with scope new A() - */ - private Optional shouldMakeCallWithScope(MethodCallExpr methodCallExpr, Context context) { - assert methodCallExpr.getScope().isPresent(); - - String scopeName = methodCallExpr.getScope().get().toString(); - - if (!context.getCurrentClass().isPresent()) { - return Optional.empty(); - } - - ClassOrInterfaceDeclaration currentClass = context.getCurrentClass().get(); - - // Check if it's a static method call of current class - if (!Objects.equals(scopeName, currentClass.getNameAsString())) { - - // Check if 'scopeName' is a variable - List> declarations = sdg.findDeclarationsOfVariable(scopeName, methodCallNode); - - if (declarations.isEmpty()) { - // It is a static method call of another class. We do nothing - return Optional.empty(); - } - - /* - 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 Optional.empty(); - } - - Type variableType = optionalVariableDeclarator.get().getType(); - - if (!variableType.isClassOrInterfaceType()) { - // Not class type - return Optional.empty(); - } - - if (!Objects.equals(variableType.asClassOrInterfaceType().getNameAsString(), currentClass.getNameAsString())) { - // object is not instance of our class - return Optional.empty(); - } - - // if we got here, the object is instance of our class - } - - // It's a static method call to a method of the current class - - return findMethodInClass(methodCallExpr, currentClass); - } - - private Optional shouldMakeCallWithNoScope(MethodCallExpr methodCallExpr, Context context) { - assert !methodCallExpr.getScope().isPresent(); - - /* - May be a call to a method of the current class or a call to an imported static method. - In the first case, we make the call. Otherwise, not. - */ - - if (!context.getCurrentClass().isPresent()) { - return Optional.empty(); - } - - // We get the current class and search along their methods to find the one we're looking for... - - ClassOrInterfaceDeclaration currentClass = context.getCurrentClass().get(); - - return findMethodInClass(methodCallExpr, currentClass); - } - - private Optional findMethodInClass(MethodCallExpr methodCallExpr, ClassOrInterfaceDeclaration klass) { - int argumentsCount = methodCallExpr.getArguments().size(); - - // Get methods with equal name and parameter count - List classMethods = klass.getMethodsByName(methodCallExpr.getNameAsString()).stream() - // Filter methods with equal or less (varargs) number of parameters - .filter(methodDeclaration -> { - NodeList parameters = methodDeclaration.getParameters(); - - if (parameters.size() == argumentsCount) { - return true; - } - - if (parameters.isEmpty()) { - return false; - } - - // There are more arguments than parameters. May be OK if last parameter is varargs - if (parameters.size() < argumentsCount) { - return parameters.get(parameters.size() - 1).isVarArgs(); - } - - // There are less arguments than parameters. May be OK if the last parameter is varargs - // and it's omited - return parameters.size() - 1 == argumentsCount - && parameters.get(parameters.size() - 1).isVarArgs(); - }) - .collect(Collectors.toList()); - - if (classMethods.isEmpty()) { - // No methods in class with that name and parameter count - return Optional.empty(); - } - - if (classMethods.size() == 1) { - // We found the method! - return Optional.of(classMethods.get(0)); - } - - /* - * Tricky one! We have to match argument and parameter types, so we have to: - * - Differentiate arguments expressions: - * - Easy: In case of CastExpr, get the type - * - Easy: In case of ObjectCreationExpr, get the type - * - Medium: In case of NameExpr, find the declaration and its type - * - Medium: In case of LiteralExpr, get the type - * - Hard: In case of MethodCallExpr, find MethodDeclaration and its type - * - If there is a varargs parameter, check every argument corresponding to it has the same type - * - * Example: - * At this point these three methods are considered as called: - * private void foo(int a, int b) {} - * private void foo(String a, String b) {} - * private void foo(String a, int... bs) {} - * - * We have to match types to get the correct one - * - * */ - - return classMethods.stream().filter(methodDeclaration -> { - boolean match = true; - - for (int i = 0; i < methodDeclaration.getParameters().size(); i++) { - if (!match) { - break; - } - - if (argumentsCount < i) { - return argumentsCount == i - 1 - && methodDeclaration.getParameter(i).isVarArgs(); - } - - // TODO - Convert into a visitor - - Expression argumentExpression = methodCallExpr.getArgument(i); - Parameter parameter = methodDeclaration.getParameter(i); - - if (argumentExpression.isCastExpr()) { - match = Objects.equals(argumentExpression.asCastExpr().getType(), parameter.getType()); - } else if (argumentExpression.isObjectCreationExpr()) { - match = Objects.equals(argumentExpression.asObjectCreationExpr().getType(), parameter.getType()); - } else if (argumentExpression.isNameExpr()) { - String variableName = argumentExpression.asNameExpr().getNameAsString(); - - List> declarationsOfVariable = sdg.findDeclarationsOfVariable(argumentExpression.asNameExpr().getNameAsString(), methodCallNode); - - assert !declarationsOfVariable.isEmpty(); - - GraphNode declarationNode = declarationsOfVariable.get(declarationsOfVariable.size() - 1); - - ExpressionStmt expressionStmt = (ExpressionStmt) declarationNode.getAstNode(); - - assert expressionStmt.getExpression().isVariableDeclarationExpr(); - - match = expressionStmt.getExpression().asVariableDeclarationExpr().getVariables().stream() - .filter(variableDeclarator -> Objects.equals(variableDeclarator.getName().asString(), variableName)) - .findFirst() - .map(variableDeclarator -> Objects.equals(variableDeclarator.getType(), parameter.getType())) - .orElse(false); - } // TODO: More checks - } - - return match; - }).findFirst(); - } } diff --git a/src/main/java/tfm/graphs/sdg/SDG.java b/src/main/java/tfm/graphs/sdg/SDG.java index 69389a3..ff46006 100644 --- a/src/main/java/tfm/graphs/sdg/SDG.java +++ b/src/main/java/tfm/graphs/sdg/SDG.java @@ -11,6 +11,7 @@ import tfm.arcs.sdg.CallArc; import tfm.arcs.sdg.ParameterInOutArc; import tfm.graphs.Buildable; import tfm.graphs.Graph; +import tfm.graphs.cfg.CFG; import tfm.graphs.pdg.PDG; import tfm.nodes.*; import tfm.slicing.Slice; @@ -25,10 +26,10 @@ import java.util.stream.Collectors; public class SDG extends Graph implements Sliceable, Buildable> { private boolean built = false; - private Map contextToMethodRoot; + private Map methodCFGMap; public SDG() { - this.contextToMethodRoot = new HashMap<>(); + this.methodCFGMap = new HashMap<>(); } @Override @@ -46,46 +47,20 @@ public class SDG extends Graph implements Sliceable, Buildable getContexts() { - return contextToMethodRoot.keySet(); + public Set getMethodDeclarations() { + return this.methodCFGMap.keySet(); } - @SuppressWarnings("unchecked") - public List> getMethodRoots() { - return contextToMethodRoot.values().stream() - .map(id -> findNodeById(id)) - .filter(Optional::isPresent) - .map(optional -> (GraphNode) optional.get()) - .collect(Collectors.toList()); + public void setMethodCFG(MethodDeclaration methodDeclaration, CFG cfg) { + this.methodCFGMap.put(methodDeclaration, cfg); } - @SuppressWarnings("unchecked") - public Optional> getRootNode(Context context) { - Long id = this.contextToMethodRoot.get(context); - - if (id == null) { + public Optional getMethodCFG(MethodDeclaration methodDeclaration) { + if (!this.methodCFGMap.containsKey(methodDeclaration)) { return Optional.empty(); } - return findNodeById(id).map(node -> (GraphNode) node); - } - - public void addRootNode(Context context, long id) { - if (!findNodeById(id).isPresent()) - throw new IllegalArgumentException("Root node with id " + id + " is not contained in graph!"); - - this.contextToMethodRoot.put(new Context(context), id); - } - - public void addRootNode(Context context, GraphNode node) { - addRootNode(context, node.getId()); - } - - public Optional getContext(long id) { - return contextToMethodRoot.entrySet().stream() - .filter(entry -> Objects.equals(entry.getValue(), id)) - .findFirst() - .map(Map.Entry::getKey); + return Optional.of(this.methodCFGMap.get(methodDeclaration)); } public void addControlDependencyArc(GraphNode from, GraphNode to) { diff --git a/src/main/java/tfm/graphs/sdg/SDGBuilder.java b/src/main/java/tfm/graphs/sdg/SDGBuilder.java index 356dd05..48701ea 100644 --- a/src/main/java/tfm/graphs/sdg/SDGBuilder.java +++ b/src/main/java/tfm/graphs/sdg/SDGBuilder.java @@ -51,8 +51,8 @@ class SDGBuilder extends VoidVisitorAdapter { GraphNode methodDeclarationNode = pdg.getRootNode().get(); - // Add root node from PDG - sdg.addRootNode(context, methodDeclarationNode.getId()); + // Add CFG + sdg.setMethodCFG(methodDeclaration, pdg.getCfg()); } @Override @@ -72,7 +72,7 @@ class SDGBuilder 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(sdg); - methodCallReplacer.replace(); + methodCallReplacer.replace(context); -- GitLab From d1b305899b2f8b6cf7c74b41dc0f94d7ae71fe34 Mon Sep 17 00:00:00 2001 From: jacosro Date: Sun, 3 May 2020 23:06:06 +0200 Subject: [PATCH 14/20] WIP: method in and out call nodes --- .../graphs/sdg/MethodCallReplacerVisitor.java | 37 +++++++++++++------ src/main/java/tfm/graphs/sdg/SDG.java | 2 + 2 files changed, 28 insertions(+), 11 deletions(-) diff --git a/src/main/java/tfm/graphs/sdg/MethodCallReplacerVisitor.java b/src/main/java/tfm/graphs/sdg/MethodCallReplacerVisitor.java index 5133020..d36a6fd 100644 --- a/src/main/java/tfm/graphs/sdg/MethodCallReplacerVisitor.java +++ b/src/main/java/tfm/graphs/sdg/MethodCallReplacerVisitor.java @@ -1,16 +1,14 @@ package tfm.graphs.sdg; import com.github.javaparser.ParserConfiguration; +import com.github.javaparser.ast.ArrayCreationLevel; import com.github.javaparser.ast.Node; import com.github.javaparser.ast.NodeList; import com.github.javaparser.ast.body.ClassOrInterfaceDeclaration; import com.github.javaparser.ast.body.MethodDeclaration; import com.github.javaparser.ast.body.Parameter; import com.github.javaparser.ast.body.VariableDeclarator; -import com.github.javaparser.ast.expr.Expression; -import com.github.javaparser.ast.expr.MethodCallExpr; -import com.github.javaparser.ast.expr.NameExpr; -import com.github.javaparser.ast.expr.VariableDeclarationExpr; +import com.github.javaparser.ast.expr.*; import com.github.javaparser.ast.stmt.*; import com.github.javaparser.ast.type.Type; import com.github.javaparser.ast.visitor.VoidVisitorAdapter; @@ -26,6 +24,7 @@ import com.github.javaparser.symbolsolver.resolution.SymbolSolver; import com.github.javaparser.symbolsolver.resolution.typesolvers.CombinedTypeSolver; import com.github.javaparser.symbolsolver.resolution.typesolvers.JavaParserTypeSolver; import com.github.javaparser.symbolsolver.resolution.typesolvers.ReflectionTypeSolver; +import tfm.graphs.cfg.CFG; import tfm.nodes.GraphNode; import tfm.nodes.TypeNodeFactory; import tfm.nodes.type.NodeType; @@ -104,7 +103,7 @@ class MethodCallReplacerVisitor extends VoidVisitorAdapter { @Override public void visit(MethodCallExpr methodCallExpr, Context context) { - List arguments = methodCallExpr.getArguments(); + NodeList arguments = methodCallExpr.getArguments(); // // Parse first method call expressions as arguments // arguments.stream() @@ -120,17 +119,33 @@ class MethodCallReplacerVisitor extends VoidVisitorAdapter { return; } - GraphNode calledMethodNode = optionalNethodDeclarationNode.get(); + GraphNode methodDeclarationNode = optionalNethodDeclarationNode.get(); + MethodDeclaration methodDeclaration = methodDeclarationNode.getAstNode(); - MethodDeclaration methodCalled = calledMethodNode.getAstNode(); + Optional optionalCFG = sdg.getMethodCFG(methodDeclaration); + assert optionalCFG.isPresent(); + CFG methodCFG = optionalCFG.get(); - sdg.addCallArc(methodCallNode, calledMethodNode); + sdg.addCallArc(methodCallNode, methodDeclarationNode); - NodeList parameters = calledMethodNode.getAstNode().getParameters(); + NodeList parameters = methodDeclarationNode.getAstNode().getParameters(); for (int i = 0; i < parameters.size(); i++) { Parameter parameter = parameters.get(i); - Expression argument = arguments.get(i); + Expression argument; + + // TODO: Check if parameter is varargs... + if (!parameter.isVarArgs()) { + argument = arguments.get(i); + } else { + NodeList varArgs = new NodeList<>(arguments.subList(i, arguments.size())); + + argument = new ArrayCreationExpr( + parameter.getType(), + new NodeList<>(new ArrayCreationLevel(varArgs.size())), + new ArrayInitializerExpr(varArgs) + ); + } // In expression VariableDeclarationExpr inVariableDeclarationExpr = new VariableDeclarationExpr( @@ -164,7 +179,7 @@ class MethodCallReplacerVisitor extends VoidVisitorAdapter { } // todo make call - Logger.log("MethodCallReplacerVisitor", String.format("%s | Method '%s' called", methodCallExpr, methodCalled.getNameAsString())); + Logger.log("MethodCallReplacerVisitor", String.format("%s | Method '%s' called", methodCallExpr, methodDeclaration.getNameAsString())); } private Optional> getMethodDeclarationNodeWithJavaParser(MethodCallExpr methodCallExpr) { diff --git a/src/main/java/tfm/graphs/sdg/SDG.java b/src/main/java/tfm/graphs/sdg/SDG.java index ff46006..3603fc8 100644 --- a/src/main/java/tfm/graphs/sdg/SDG.java +++ b/src/main/java/tfm/graphs/sdg/SDG.java @@ -1,6 +1,7 @@ package tfm.graphs.sdg; import com.github.javaparser.ast.CompilationUnit; +import com.github.javaparser.ast.Node; import com.github.javaparser.ast.NodeList; import com.github.javaparser.ast.body.MethodDeclaration; import com.github.javaparser.ast.stmt.ExpressionStmt; @@ -21,6 +22,7 @@ import tfm.utils.Context; import java.util.*; import java.util.function.Function; +import java.util.function.Predicate; import java.util.stream.Collectors; public class SDG extends Graph implements Sliceable, Buildable> { -- GitLab From 34a967f78bfd8259e13eb8d3bfe0c67b3d72a4e1 Mon Sep 17 00:00:00 2001 From: jacosro Date: Sat, 9 May 2020 20:01:20 +0200 Subject: [PATCH 15/20] Done: IN nodes. WIP: OUT nodes --- .../graphs/sdg/MethodCallReplacerVisitor.java | 53 ++++++++++++++++++- src/test/res/programs/sdg/Example1.java | 6 +++ 2 files changed, 57 insertions(+), 2 deletions(-) diff --git a/src/main/java/tfm/graphs/sdg/MethodCallReplacerVisitor.java b/src/main/java/tfm/graphs/sdg/MethodCallReplacerVisitor.java index d36a6fd..fe1be2d 100644 --- a/src/main/java/tfm/graphs/sdg/MethodCallReplacerVisitor.java +++ b/src/main/java/tfm/graphs/sdg/MethodCallReplacerVisitor.java @@ -24,6 +24,8 @@ import com.github.javaparser.symbolsolver.resolution.SymbolSolver; import com.github.javaparser.symbolsolver.resolution.typesolvers.CombinedTypeSolver; import com.github.javaparser.symbolsolver.resolution.typesolvers.JavaParserTypeSolver; import com.github.javaparser.symbolsolver.resolution.typesolvers.ReflectionTypeSolver; +import tfm.arcs.Arc; +import tfm.arcs.pdg.DataDependencyArc; import tfm.graphs.cfg.CFG; import tfm.nodes.GraphNode; import tfm.nodes.TypeNodeFactory; @@ -115,7 +117,7 @@ class MethodCallReplacerVisitor extends VoidVisitorAdapter { Optional> optionalNethodDeclarationNode = getMethodDeclarationNodeWithJavaParser(methodCallExpr); if (!optionalNethodDeclarationNode.isPresent()) { - Logger.format("Not found: '%s'. Discarding"); + Logger.format("Not found: '%s'. Discarding", methodCallExpr); return; } @@ -134,7 +136,6 @@ class MethodCallReplacerVisitor extends VoidVisitorAdapter { Parameter parameter = parameters.get(i); Expression argument; - // TODO: Check if parameter is varargs... if (!parameter.isVarArgs()) { argument = arguments.get(i); } else { @@ -162,6 +163,33 @@ class MethodCallReplacerVisitor extends VoidVisitorAdapter { sdg.addControlDependencyArc(methodCallNode, argumentInNode); + // Handle data dependency: Remove arc from method call node and add it to IN node + + List inDataDependencies = sdg.incomingEdgesOf(methodCallNode).stream() + .filter(arc -> arc.isDataDependencyArc() && Objects.equals(arc.getLabel(), argument.toString())) + .map(Arc::asDataDependencyArc) + .collect(Collectors.toList()); + + for (DataDependencyArc arc : inDataDependencies) { + GraphNode dataDependencySource = sdg.getEdgeSource(arc); + sdg.removeEdge(arc); + sdg.addDataDependencyArc(dataDependencySource, argumentInNode, argument.toString()); + } + + // Now, find the corresponding method declaration's in node and link argument node with it + + Optional> optionalParameterInNode = sdg.outgoingEdgesOf(methodDeclarationNode).stream() + .map(arc -> (GraphNode) sdg.getEdgeTarget(arc)) + .filter(node -> node.getNodeType() == NodeType.VARIABLE_IN && node.getInstruction().contains(parameter.getNameAsString() + "_in")) + .findFirst(); + + if (optionalParameterInNode.isPresent()) { + sdg.addParameterInOutArc(argumentInNode, optionalParameterInNode.get()); + } else { + Logger.log("MethodCallReplacerVisitor", "WARNING: IN declaration node for argument " + argument + " not found."); + Logger.log("MethodCallReplacerVisitor", String.format("Context: %s, Method: %s, Call: %s", context.getCurrentMethod().get().getNameAsString(), methodDeclaration.getSignature().asString(), methodCallExpr)); + } + // Out expression VariableDeclarationExpr outVariableDeclarationExpr = new VariableDeclarationExpr( new VariableDeclarator( @@ -175,7 +203,21 @@ class MethodCallReplacerVisitor extends VoidVisitorAdapter { GraphNode argumentOutNode = sdg.addNode(outExprStmt.toString(), outExprStmt, TypeNodeFactory.fromType(NodeType.VARIABLE_OUT)); + sdg.addControlDependencyArc(methodCallNode, argumentOutNode); + // Now, find the corresponding method declaration's out node and link argument node with it + + Optional> optionalParameterOutNode = sdg.outgoingEdgesOf(methodDeclarationNode).stream() + .map(arc -> (GraphNode) sdg.getEdgeTarget(arc)) + .filter(node -> node.getNodeType() == NodeType.VARIABLE_OUT && node.getInstruction().contains(parameter.getNameAsString() + "_out")) + .findFirst(); + + if (optionalParameterOutNode.isPresent()) { + sdg.addParameterInOutArc(optionalParameterOutNode.get(), argumentOutNode); + } else { + Logger.log("MethodCallReplacerVisitor", "WARNING: OUT declaration node for argument " + argument + " not found."); + Logger.log("MethodCallReplacerVisitor", String.format("Context: %s, Method: %s, Call: %s", context.getCurrentMethod().get().getNameAsString(), methodDeclaration.getSignature().asString(), methodCallExpr)); + } } // todo make call @@ -196,4 +238,11 @@ class MethodCallReplacerVisitor extends VoidVisitorAdapter { return Optional.empty(); } } + + @Override + public void visit(MethodDeclaration n, Context arg) { + arg.setCurrentMethod(n); + + super.visit(n, arg); + } } diff --git a/src/test/res/programs/sdg/Example1.java b/src/test/res/programs/sdg/Example1.java index 400f8fd..34196aa 100644 --- a/src/test/res/programs/sdg/Example1.java +++ b/src/test/res/programs/sdg/Example1.java @@ -4,16 +4,20 @@ import tfm.utils.Logger; public class Example1 { + /* public Example1() { } + */ + public static void main(String[] args) { int x = 1; int y = 2; int f = sum(x, y); + Logger.log(x); Logger.log(f); } @@ -22,6 +26,7 @@ public class Example1 { return res; } + /* public int m1() { return 1; } @@ -29,4 +34,5 @@ public class Example1 { public int m2() { return m1(); } + */ } -- GitLab From 7cb05e5db1573b06dbe089a631b94b7057370359 Mon Sep 17 00:00:00 2001 From: jacosro Date: Sun, 10 May 2020 22:57:14 +0200 Subject: [PATCH 16/20] Added diary --- doc/diary/diary.md | 34 +++++++++++++ .../tfm/utils/MethodDeclarationSolver.java | 50 +++++++++++++++++++ 2 files changed, 84 insertions(+) create mode 100644 doc/diary/diary.md create mode 100644 src/main/java/tfm/utils/MethodDeclarationSolver.java diff --git a/doc/diary/diary.md b/doc/diary/diary.md new file mode 100644 index 0000000..e5d3391 --- /dev/null +++ b/doc/diary/diary.md @@ -0,0 +1,34 @@ +# TFM Diary + +## 10/5/20 + +### Implemented + +`out` nodes of method calls. + +### What is left + +#### Distinguish when an out node should exist + +An `out` node shouldn't always exist. Primitive types cannot be modified in any way inside a method, while object types can. + +An argument to a method call may be any kind of expression. In general, any literal expression, such as `1`, `null`, `""`, `true`, etc. will not have an `out` parameter. So we have to take care of the rest. + +An `out` node should exist if there is a possibility to trace the value. And it only exists that possibility when the value comes from a **variable** + +So, for the list of expressions that are not literal: +- `ArrayAccess (array[0])`: only if `array` is a variable +- `ArrayCreationExpr`: NO +- `ArrayInitializerExpr`: NO +- `BinaryExpr`: NO, it returns a value +- `CastExpr ((Cast) obj)`: only if `obj` is a variable +- `ClassExpr (obj.class)`: NO +- `ConditionalExpr (1?a:b)`: we'll have to check `a` and `b` expressions +- `FieldAccessExpr (obj.field)`: only if `obj` is a **variable** +- `InstanceOfExpr`: NO +- `MethodCallExpr (foo.bar(x))`: NO +- `NameExpr`: YES +- `ObjectCreationExpr`: NO +- `SuperExpr`: NO +- `ThisExpr`: NO +- `UnaryExpr (a++)`: we'll have to check if `a` is a variable \ No newline at end of file diff --git a/src/main/java/tfm/utils/MethodDeclarationSolver.java b/src/main/java/tfm/utils/MethodDeclarationSolver.java new file mode 100644 index 0000000..8b33aa8 --- /dev/null +++ b/src/main/java/tfm/utils/MethodDeclarationSolver.java @@ -0,0 +1,50 @@ +package tfm.utils; + +import com.github.javaparser.ast.Node; +import com.github.javaparser.ast.body.MethodDeclaration; +import com.github.javaparser.ast.expr.MethodCallExpr; +import com.github.javaparser.resolution.UnsolvedSymbolException; +import com.github.javaparser.resolution.declarations.ResolvedMethodDeclaration; +import com.github.javaparser.symbolsolver.javaparsermodel.JavaParserFacade; +import com.github.javaparser.symbolsolver.model.resolution.SymbolReference; +import com.github.javaparser.symbolsolver.model.resolution.TypeSolver; +import com.github.javaparser.symbolsolver.resolution.typesolvers.CombinedTypeSolver; +import com.github.javaparser.symbolsolver.resolution.typesolvers.ReflectionTypeSolver; +import tfm.nodes.GraphNode; + +import java.util.ArrayList; +import java.util.List; +import java.util.Optional; + +public class SymbolSolverWrapper { + + private static final SymbolSolverWrapper instance = new SymbolSolverWrapper(); + private static final List typeSolvers = new ArrayList<>(); + + private SymbolSolverWrapper() { + + } + + public static void addTypeSolver(TypeSolver typeSolver) { + typeSolvers.add(typeSolver); + } + + public static SymbolSolverWrapper getInstance() { + return instance; + } + + private Optional findNodeFrom(MethodCallExpr methodCallExpr) { + CombinedTypeSolver combinedTypeSolver = new CombinedTypeSolver(); + + try { + SymbolReference solver = JavaParserFacade.get(typeSolver).solve(methodCallExpr); + + return solver.isSolved() + ? solver.getCorrespondingDeclaration().toAst() + .flatMap(methodDeclaration -> sdg.findNodeByASTNode(methodDeclaration)) + : Optional.empty(); + } catch (UnsolvedSymbolException e) { + return Optional.empty(); + } + } +} -- GitLab From 1f9911ffafc383a52d7dc737bd09c7129feb6bdb Mon Sep 17 00:00:00 2001 From: jacosro Date: Sun, 10 May 2020 22:57:21 +0200 Subject: [PATCH 17/20] Implemented out nodes --- src/main/java/tfm/exec/Main.java | 6 ++- .../graphs/sdg/MethodCallReplacerVisitor.java | 43 +++++++++++++------ .../tfm/utils/MethodDeclarationSolver.java | 32 +++++++------- .../java/tfm/variables/VariableVisitor.java | 11 ++++- src/test/res/programs/sdg/Example1.java | 13 +++++- 5 files changed, 70 insertions(+), 35 deletions(-) diff --git a/src/main/java/tfm/exec/Main.java b/src/main/java/tfm/exec/Main.java index 26a863e..6d7a54e 100644 --- a/src/main/java/tfm/exec/Main.java +++ b/src/main/java/tfm/exec/Main.java @@ -4,6 +4,10 @@ import com.github.javaparser.JavaParser; import com.github.javaparser.ast.Node; import com.github.javaparser.ast.NodeList; import com.github.javaparser.ast.body.MethodDeclaration; +import com.github.javaparser.ast.type.Type; +import com.github.javaparser.ast.visitor.GenericVisitor; +import com.github.javaparser.ast.visitor.VoidVisitor; +import com.github.javaparser.resolution.types.ResolvedType; import tfm.graphs.cfg.CFG; import tfm.graphs.Graph; import tfm.graphs.pdg.PDG; @@ -20,7 +24,7 @@ public class Main { public static final String PROGRAM = Utils.PROGRAMS_FOLDER + "sdg/Example1.java"; public static final String GRAPH = GraphLog.PDG; - public static final String METHOD = "sum"; + public static final String METHOD = "main"; public static void main(String[] args) throws IOException { JavaParser.getStaticConfiguration().setAttributeComments(false); diff --git a/src/main/java/tfm/graphs/sdg/MethodCallReplacerVisitor.java b/src/main/java/tfm/graphs/sdg/MethodCallReplacerVisitor.java index fe1be2d..b43db0b 100644 --- a/src/main/java/tfm/graphs/sdg/MethodCallReplacerVisitor.java +++ b/src/main/java/tfm/graphs/sdg/MethodCallReplacerVisitor.java @@ -32,6 +32,7 @@ import tfm.nodes.TypeNodeFactory; import tfm.nodes.type.NodeType; import tfm.utils.Context; import tfm.utils.Logger; +import tfm.utils.MethodDeclarationSolver; import java.nio.file.Path; import java.util.List; @@ -114,7 +115,10 @@ class MethodCallReplacerVisitor extends VoidVisitorAdapter { Logger.log("MethodCallReplacerVisitor", context); - Optional> optionalNethodDeclarationNode = getMethodDeclarationNodeWithJavaParser(methodCallExpr); + Optional> optionalNethodDeclarationNode = + MethodDeclarationSolver.getInstance() + .findDeclarationFrom(methodCallExpr) + .flatMap(methodDeclaration -> sdg.findNodeByASTNode(methodDeclaration)); if (!optionalNethodDeclarationNode.isPresent()) { Logger.format("Not found: '%s'. Discarding", methodCallExpr); @@ -191,6 +195,15 @@ class MethodCallReplacerVisitor extends VoidVisitorAdapter { } // Out expression + + if (!argument.isNameExpr() && !sdg.findDeclarationsOfVariable(argument.toString(), methodCallNode).isEmpty()) { + /* + If the argument is not a variable (is a name expression and it is declared in the scope), + then there is no OUT node + */ + continue; + } + VariableDeclarationExpr outVariableDeclarationExpr = new VariableDeclarationExpr( new VariableDeclarator( parameter.getType(), @@ -205,13 +218,26 @@ class MethodCallReplacerVisitor extends VoidVisitorAdapter { sdg.addControlDependencyArc(methodCallNode, argumentOutNode); - // Now, find the corresponding method declaration's out node and link argument node with it + // Now, find the corresponding method call's out node and link argument node with it Optional> optionalParameterOutNode = sdg.outgoingEdgesOf(methodDeclarationNode).stream() .map(arc -> (GraphNode) sdg.getEdgeTarget(arc)) .filter(node -> node.getNodeType() == NodeType.VARIABLE_OUT && node.getInstruction().contains(parameter.getNameAsString() + "_out")) .findFirst(); + // Handle data dependency: remove arc from method call node and add it to OUT node + + List outDataDependencies = sdg.outgoingEdgesOf(methodCallNode).stream() + .filter(arc -> arc.isDataDependencyArc() && Objects.equals(arc.getLabel(), argument.toString())) + .map(Arc::asDataDependencyArc) + .collect(Collectors.toList()); + + for (DataDependencyArc arc : outDataDependencies) { + GraphNode dataDependencyTarget = sdg.getEdgeTarget(arc); + sdg.removeEdge(arc); + sdg.addDataDependencyArc(argumentOutNode, dataDependencyTarget, argument.toString()); + } + if (optionalParameterOutNode.isPresent()) { sdg.addParameterInOutArc(optionalParameterOutNode.get(), argumentOutNode); } else { @@ -224,19 +250,8 @@ class MethodCallReplacerVisitor extends VoidVisitorAdapter { Logger.log("MethodCallReplacerVisitor", String.format("%s | Method '%s' called", methodCallExpr, methodDeclaration.getNameAsString())); } - private Optional> getMethodDeclarationNodeWithJavaParser(MethodCallExpr methodCallExpr) { - TypeSolver typeSolver = new ReflectionTypeSolver(); + private void argumentAsNameExpr(GraphNode methodCallNode) { - try { - SymbolReference solver = JavaParserFacade.get(typeSolver).solve(methodCallExpr); - - return solver.isSolved() - ? solver.getCorrespondingDeclaration().toAst() - .flatMap(methodDeclaration -> sdg.findNodeByASTNode(methodDeclaration)) - : Optional.empty(); - } catch (UnsolvedSymbolException e) { - return Optional.empty(); - } } @Override diff --git a/src/main/java/tfm/utils/MethodDeclarationSolver.java b/src/main/java/tfm/utils/MethodDeclarationSolver.java index 8b33aa8..b6e7eba 100644 --- a/src/main/java/tfm/utils/MethodDeclarationSolver.java +++ b/src/main/java/tfm/utils/MethodDeclarationSolver.java @@ -1,6 +1,5 @@ package tfm.utils; -import com.github.javaparser.ast.Node; import com.github.javaparser.ast.body.MethodDeclaration; import com.github.javaparser.ast.expr.MethodCallExpr; import com.github.javaparser.resolution.UnsolvedSymbolException; @@ -9,39 +8,38 @@ import com.github.javaparser.symbolsolver.javaparsermodel.JavaParserFacade; import com.github.javaparser.symbolsolver.model.resolution.SymbolReference; import com.github.javaparser.symbolsolver.model.resolution.TypeSolver; import com.github.javaparser.symbolsolver.resolution.typesolvers.CombinedTypeSolver; -import com.github.javaparser.symbolsolver.resolution.typesolvers.ReflectionTypeSolver; -import tfm.nodes.GraphNode; -import java.util.ArrayList; -import java.util.List; -import java.util.Optional; +import java.util.*; -public class SymbolSolverWrapper { +public class MethodDeclarationSolver { - private static final SymbolSolverWrapper instance = new SymbolSolverWrapper(); - private static final List typeSolvers = new ArrayList<>(); + private static final MethodDeclarationSolver instance = new MethodDeclarationSolver(); + private static final List usedTypeSolvers = new ArrayList<>(); - private SymbolSolverWrapper() { + private MethodDeclarationSolver() { } - public static void addTypeSolver(TypeSolver typeSolver) { - typeSolvers.add(typeSolver); + public static void addTypeSolvers(TypeSolver... typeSolvers) { + usedTypeSolvers.addAll(Arrays.asList(typeSolvers)); } - public static SymbolSolverWrapper getInstance() { + public static MethodDeclarationSolver getInstance() { return instance; } - private Optional findNodeFrom(MethodCallExpr methodCallExpr) { - CombinedTypeSolver combinedTypeSolver = new CombinedTypeSolver(); + public Optional findDeclarationFrom(MethodCallExpr methodCallExpr) { + return this.findDeclarationFrom(methodCallExpr, usedTypeSolvers); + } + + public Optional findDeclarationFrom(MethodCallExpr methodCallExpr, Collection customTypeSolvers) { + CombinedTypeSolver combinedTypeSolver = new CombinedTypeSolver(customTypeSolvers.toArray(new TypeSolver[0])); try { - SymbolReference solver = JavaParserFacade.get(typeSolver).solve(methodCallExpr); + SymbolReference solver = JavaParserFacade.get(combinedTypeSolver).solve(methodCallExpr); return solver.isSolved() ? solver.getCorrespondingDeclaration().toAst() - .flatMap(methodDeclaration -> sdg.findNodeByASTNode(methodDeclaration)) : Optional.empty(); } catch (UnsolvedSymbolException e) { return Optional.empty(); diff --git a/src/main/java/tfm/variables/VariableVisitor.java b/src/main/java/tfm/variables/VariableVisitor.java index 1e4d91c..fd2f639 100644 --- a/src/main/java/tfm/variables/VariableVisitor.java +++ b/src/main/java/tfm/variables/VariableVisitor.java @@ -7,6 +7,8 @@ import com.github.javaparser.ast.visitor.VoidVisitorAdapter; import org.checkerframework.checker.nullness.qual.NonNull; import tfm.variables.actions.VariableAction; +import java.awt.*; + abstract class VariableVisitor extends VoidVisitorAdapter { @Override @@ -101,7 +103,14 @@ abstract class VariableVisitor extends VoidVisitorAdapter expression.accept(this, action.or(VariableAction.Actions.USE))); - n.getArguments().forEach(expression -> expression.accept(this, action.or(VariableAction.Actions.USE))); + n.getArguments().forEach(expression -> { + expression.accept(this, action.or(VariableAction.Actions.USE)); + + + if (expression.isNameExpr() || expression.isFieldAccessExpr()) { + expression.accept(this, action.or(VariableAction.Actions.DEFINITION)); + } + }); } @Override diff --git a/src/test/res/programs/sdg/Example1.java b/src/test/res/programs/sdg/Example1.java index 34196aa..39fdea4 100644 --- a/src/test/res/programs/sdg/Example1.java +++ b/src/test/res/programs/sdg/Example1.java @@ -11,16 +11,25 @@ public class Example1 { */ + int num; + public static void main(String[] args) { int x = 1; int y = 2; - int f = sum(x, y); + Example1 example1 = new Example1(); + Example1 example2 = new Example1(); + + int f = sum(example1.getNum(), example2.num); - Logger.log(x); + Logger.log(example1.num); Logger.log(f); } + public int getNum() { + return num; + } + private static int sum(int x, int y) { int res = x + y; return res; -- GitLab From ab67fdac292f0cc696348551ed926de567229930 Mon Sep 17 00:00:00 2001 From: jacosro Date: Tue, 12 May 2020 00:22:43 +0200 Subject: [PATCH 18/20] WIP: out nodes --- doc/diary/diary.md | 20 +++++- .../graphs/sdg/MethodCallReplacerVisitor.java | 71 +++++++++---------- .../graphs/sdg/OutNodeVariableVisitor.java | 62 ++++++++++++++++ src/main/java/tfm/nodes/type/NodeType.java | 1 + src/test/res/programs/sdg/Example2.java | 44 ++++++++++++ 5 files changed, 161 insertions(+), 37 deletions(-) create mode 100644 src/main/java/tfm/graphs/sdg/OutNodeVariableVisitor.java create mode 100644 src/test/res/programs/sdg/Example2.java diff --git a/doc/diary/diary.md b/doc/diary/diary.md index e5d3391..e59dc0c 100644 --- a/doc/diary/diary.md +++ b/doc/diary/diary.md @@ -31,4 +31,22 @@ So, for the list of expressions that are not literal: - `ObjectCreationExpr`: NO - `SuperExpr`: NO - `ThisExpr`: NO -- `UnaryExpr (a++)`: we'll have to check if `a` is a variable \ No newline at end of file +- `UnaryExpr (a++)`: we'll have to check the operator (only `++`, `--` return the variable), and if `a` is a variable + +## 11/5/20 + +### Implemented + +- Added a CALL node between the node which contains the statement with the `MethodCallExpr` and the `MethodDeclaration`. This node is linked with `in` and `out` nodes. This is because if a statement has more than 1 `MethodCallExpr`, it may have repeated `in` and `out` nodes with the same variables +- Changed `out` node expression from `VariableDeclarationExpr` to `AssignExpr`, where the name of the variable is the argument expression + +### WIP + +- `OutNodeVariableVisitor`, a visitor that returns the expressions that should be candidate for being `out` nodes. This visitor overrides all `*Expr` methods who may contain a `NameExpr` + - `ConditionalExpr`: Here, we have `ThenExpr` and `ElseExpr`. As we are performing a static analysis, both should be considered for `out` node in case they represented a variable (This is why `OutNodeVariableVisitor` uses a `List`, and doesn't just return a `String` representing the variable) + +### Issues + +- `Out` node is generated for `MethodCallExpr` as an argument + +- When a `MethodCallExpr` is an argument of another, we have to link its output to the `in` node of the containing one \ No newline at end of file diff --git a/src/main/java/tfm/graphs/sdg/MethodCallReplacerVisitor.java b/src/main/java/tfm/graphs/sdg/MethodCallReplacerVisitor.java index b43db0b..1cbe11e 100644 --- a/src/main/java/tfm/graphs/sdg/MethodCallReplacerVisitor.java +++ b/src/main/java/tfm/graphs/sdg/MethodCallReplacerVisitor.java @@ -1,29 +1,14 @@ package tfm.graphs.sdg; -import com.github.javaparser.ParserConfiguration; import com.github.javaparser.ast.ArrayCreationLevel; import com.github.javaparser.ast.Node; import com.github.javaparser.ast.NodeList; -import com.github.javaparser.ast.body.ClassOrInterfaceDeclaration; import com.github.javaparser.ast.body.MethodDeclaration; import com.github.javaparser.ast.body.Parameter; import com.github.javaparser.ast.body.VariableDeclarator; import com.github.javaparser.ast.expr.*; import com.github.javaparser.ast.stmt.*; -import com.github.javaparser.ast.type.Type; import com.github.javaparser.ast.visitor.VoidVisitorAdapter; -import com.github.javaparser.resolution.UnsolvedSymbolException; -import com.github.javaparser.resolution.declarations.ResolvedMethodDeclaration; -import com.github.javaparser.resolution.declarations.ResolvedReferenceTypeDeclaration; -import com.github.javaparser.symbolsolver.JavaSymbolSolver; -import com.github.javaparser.symbolsolver.SourceFileInfoExtractor; -import com.github.javaparser.symbolsolver.javaparsermodel.JavaParserFacade; -import com.github.javaparser.symbolsolver.model.resolution.SymbolReference; -import com.github.javaparser.symbolsolver.model.resolution.TypeSolver; -import com.github.javaparser.symbolsolver.resolution.SymbolSolver; -import com.github.javaparser.symbolsolver.resolution.typesolvers.CombinedTypeSolver; -import com.github.javaparser.symbolsolver.resolution.typesolvers.JavaParserTypeSolver; -import com.github.javaparser.symbolsolver.resolution.typesolvers.ReflectionTypeSolver; import tfm.arcs.Arc; import tfm.arcs.pdg.DataDependencyArc; import tfm.graphs.cfg.CFG; @@ -34,16 +19,13 @@ import tfm.utils.Context; import tfm.utils.Logger; import tfm.utils.MethodDeclarationSolver; -import java.nio.file.Path; -import java.util.List; -import java.util.Objects; -import java.util.Optional; +import java.util.*; import java.util.stream.Collectors; class MethodCallReplacerVisitor extends VoidVisitorAdapter { private SDG sdg; - private GraphNode methodCallNode; + private GraphNode originalMethodCallNode; public MethodCallReplacerVisitor(SDG sdg) { this.sdg = sdg; @@ -52,7 +34,7 @@ class MethodCallReplacerVisitor extends VoidVisitorAdapter { private void searchAndSetMethodCallNode(Node node) { Optional> optionalNode = sdg.findNodeByASTNode(node); assert optionalNode.isPresent(); - methodCallNode = optionalNode.get(); + originalMethodCallNode = optionalNode.get(); } @@ -108,10 +90,10 @@ class MethodCallReplacerVisitor extends VoidVisitorAdapter { public void visit(MethodCallExpr methodCallExpr, Context context) { NodeList arguments = methodCallExpr.getArguments(); -// // Parse first method call expressions as arguments -// arguments.stream() -// .filter(Expression::isMethodCallExpr) -// .forEach(expression -> expression.accept(this, context)); + // Parse first method call expressions as arguments + arguments.stream() + .filter(Expression::isMethodCallExpr) + .forEach(expression -> expression.accept(this, context)); Logger.log("MethodCallReplacerVisitor", context); @@ -132,6 +114,9 @@ class MethodCallReplacerVisitor extends VoidVisitorAdapter { assert optionalCFG.isPresent(); CFG methodCFG = optionalCFG.get(); + GraphNode methodCallNode = sdg.addNode("CALL " + methodCallExpr.toString(), methodCallExpr, TypeNodeFactory.fromType(NodeType.METHOD_CALL)); + + sdg.addControlDependencyArc(originalMethodCallNode, methodCallNode); sdg.addCallArc(methodCallNode, methodDeclarationNode); NodeList parameters = methodDeclarationNode.getAstNode().getParameters(); @@ -169,7 +154,7 @@ class MethodCallReplacerVisitor extends VoidVisitorAdapter { // Handle data dependency: Remove arc from method call node and add it to IN node - List inDataDependencies = sdg.incomingEdgesOf(methodCallNode).stream() + List inDataDependencies = sdg.incomingEdgesOf(originalMethodCallNode).stream() .filter(arc -> arc.isDataDependencyArc() && Objects.equals(arc.getLabel(), argument.toString())) .map(Arc::asDataDependencyArc) .collect(Collectors.toList()); @@ -196,23 +181,37 @@ class MethodCallReplacerVisitor extends VoidVisitorAdapter { // Out expression - if (!argument.isNameExpr() && !sdg.findDeclarationsOfVariable(argument.toString(), methodCallNode).isEmpty()) { + OutNodeVariableVisitor shouldHaveOutNodeVisitor = new OutNodeVariableVisitor(); + Set variablesForOutNode = new HashSet<>(); + argument.accept(shouldHaveOutNodeVisitor, variablesForOutNode); + + // Here, variablesForOutNode may have 1 variable or more depending on the expression + + if (variablesForOutNode.isEmpty()) { /* - If the argument is not a variable (is a name expression and it is declared in the scope), + If the argument is not a variable or it is not declared in the scope, then there is no OUT node */ + Logger.log("MethodCallReplacerVisitor", String.format("Expression '%s' should not have out node", argument.toString())); + continue; + } else if (variablesForOutNode.size() == 1) { + String variable = variablesForOutNode.iterator().next(); + + if (sdg.findDeclarationsOfVariable(variable, originalMethodCallNode).isEmpty()) { + Logger.log("MethodCallReplacerVisitor", String.format("Expression '%s' should not have out node", argument.toString())); + continue; + } + } else { continue; } - VariableDeclarationExpr outVariableDeclarationExpr = new VariableDeclarationExpr( - new VariableDeclarator( - parameter.getType(), - parameter.getNameAsString(), - new NameExpr(parameter.getNameAsString() + "_out") - ) + AssignExpr outVariableAssignExpr = new AssignExpr( + argument, + new NameExpr(parameter.getNameAsString() + "_out"), + AssignExpr.Operator.ASSIGN ); - ExpressionStmt outExprStmt = new ExpressionStmt(outVariableDeclarationExpr); + ExpressionStmt outExprStmt = new ExpressionStmt(outVariableAssignExpr); GraphNode argumentOutNode = sdg.addNode(outExprStmt.toString(), outExprStmt, TypeNodeFactory.fromType(NodeType.VARIABLE_OUT)); @@ -227,7 +226,7 @@ class MethodCallReplacerVisitor extends VoidVisitorAdapter { // Handle data dependency: remove arc from method call node and add it to OUT node - List outDataDependencies = sdg.outgoingEdgesOf(methodCallNode).stream() + List outDataDependencies = sdg.outgoingEdgesOf(originalMethodCallNode).stream() .filter(arc -> arc.isDataDependencyArc() && Objects.equals(arc.getLabel(), argument.toString())) .map(Arc::asDataDependencyArc) .collect(Collectors.toList()); diff --git a/src/main/java/tfm/graphs/sdg/OutNodeVariableVisitor.java b/src/main/java/tfm/graphs/sdg/OutNodeVariableVisitor.java new file mode 100644 index 0000000..b4fe6bc --- /dev/null +++ b/src/main/java/tfm/graphs/sdg/OutNodeVariableVisitor.java @@ -0,0 +1,62 @@ +package tfm.graphs.sdg; + +import com.github.javaparser.ast.expr.*; +import com.github.javaparser.ast.stmt.ExpressionStmt; +import com.github.javaparser.ast.visitor.VoidVisitorAdapter; +import tfm.utils.Logger; + +import java.util.Set; + +public class OutNodeVariableVisitor extends VoidVisitorAdapter> { + + @Override + public void visit(ArrayAccessExpr n, Set variables) { + n.getName().accept(this, variables); + } + + @Override + public void visit(CastExpr n, Set variables) { + n.getExpression().accept(this, variables); + } + + @Override + public void visit(ConditionalExpr n, Set variables) { + n.getThenExpr().accept(this, variables); + n.getElseExpr().accept(this, variables); + } + + @Override + public void visit(EnclosedExpr n, Set variables) { + n.getInner().accept(this, variables); + } + + @Override + public void visit(ExpressionStmt n, Set variables) { + n.getExpression().accept(this, variables); + } + + @Override + public void visit(FieldAccessExpr n, Set variables) { + Logger.log("ShouldHaveOutNodeVisitor", "Exploring " + n); + n.getScope().accept(this, variables); + } + + @Override + public void visit(NameExpr n, Set variables) { + Logger.log("ShouldHaveOutNodeVisitor", n + " is a variable!!"); + variables.add(n.getNameAsString()); + } + + @Override + public void visit(UnaryExpr n, Set variables) { + switch (n.getOperator()) { + case PLUS: + case MINUS: + case BITWISE_COMPLEMENT: + case LOGICAL_COMPLEMENT: + ; + } + + n.getExpression().accept(this, variables); + } +} diff --git a/src/main/java/tfm/nodes/type/NodeType.java b/src/main/java/tfm/nodes/type/NodeType.java index 90647d0..425d7b0 100644 --- a/src/main/java/tfm/nodes/type/NodeType.java +++ b/src/main/java/tfm/nodes/type/NodeType.java @@ -3,6 +3,7 @@ package tfm.nodes.type; public enum NodeType { STATEMENT, METHOD, + METHOD_CALL, VARIABLE_IN, VARIABLE_OUT } diff --git a/src/test/res/programs/sdg/Example2.java b/src/test/res/programs/sdg/Example2.java new file mode 100644 index 0000000..d92a978 --- /dev/null +++ b/src/test/res/programs/sdg/Example2.java @@ -0,0 +1,44 @@ +package tfm.programs.sdg; + +class InnerObject { + int x; + + public InnerObject() { + + } + + public InnerObject(int x) { + this.x = x; + } +} + +class CustomObject { + InnerObject inner; + + public CustomObject() { + + } + + public CustomObject(InnerObject inner) { + this.inner = inner; + } +} + + +public class Example2 { + + + public static void main(String[] args) { + CustomObject customObject = new CustomObject(new InnerObject(1)); + modifyCustomObject(customObject); + modifyInnerObject(customObject.inner); + } + + public static void modifyCustomObject(CustomObject customObject) { + customObject.inner = new InnerObject(2); + } + + public static void modifyInnerObject(InnerObject innerObject) { + innerObject.x = 5; + } +} \ No newline at end of file -- GitLab From 128fde288a802c6e1f1ec0d52b1ac28b28c7cbed Mon Sep 17 00:00:00 2001 From: jacosro Date: Sun, 17 May 2020 01:19:19 +0200 Subject: [PATCH 19/20] Implemented method call nodes for variables as arguments --- .../graphs/sdg/MethodCallReplacerVisitor.java | 13 ++++++--- src/main/java/tfm/graphs/sdg/SDG.java | 29 ++++++++++++------- src/test/res/programs/sdg/Example1.java | 12 ++++---- 3 files changed, 33 insertions(+), 21 deletions(-) diff --git a/src/main/java/tfm/graphs/sdg/MethodCallReplacerVisitor.java b/src/main/java/tfm/graphs/sdg/MethodCallReplacerVisitor.java index 1cbe11e..b843439 100644 --- a/src/main/java/tfm/graphs/sdg/MethodCallReplacerVisitor.java +++ b/src/main/java/tfm/graphs/sdg/MethodCallReplacerVisitor.java @@ -91,9 +91,9 @@ class MethodCallReplacerVisitor extends VoidVisitorAdapter { NodeList arguments = methodCallExpr.getArguments(); // Parse first method call expressions as arguments - arguments.stream() - .filter(Expression::isMethodCallExpr) - .forEach(expression -> expression.accept(this, context)); +// arguments.stream() +// .filter(Expression::isMethodCallExpr) +// .forEach(expression -> expression.accept(this, context)); Logger.log("MethodCallReplacerVisitor", context); @@ -187,6 +187,7 @@ class MethodCallReplacerVisitor extends VoidVisitorAdapter { // Here, variablesForOutNode may have 1 variable or more depending on the expression + Logger.log("MethodCallReplacerVisitor", String.format("Variables for out node: %s", variablesForOutNode)); if (variablesForOutNode.isEmpty()) { /* If the argument is not a variable or it is not declared in the scope, @@ -197,7 +198,11 @@ class MethodCallReplacerVisitor extends VoidVisitorAdapter { } else if (variablesForOutNode.size() == 1) { String variable = variablesForOutNode.iterator().next(); - if (sdg.findDeclarationsOfVariable(variable, originalMethodCallNode).isEmpty()) { + List> declarations = sdg.findDeclarationsOfVariable(variable, originalMethodCallNode); + + Logger.log("MethodCallReplacerVisitor", String.format("Declarations of variable: '%s': %s", variable, declarations)); + + if (declarations.isEmpty()) { Logger.log("MethodCallReplacerVisitor", String.format("Expression '%s' should not have out node", argument.toString())); continue; } diff --git a/src/main/java/tfm/graphs/sdg/SDG.java b/src/main/java/tfm/graphs/sdg/SDG.java index 3603fc8..3c3dd89 100644 --- a/src/main/java/tfm/graphs/sdg/SDG.java +++ b/src/main/java/tfm/graphs/sdg/SDG.java @@ -6,6 +6,7 @@ import com.github.javaparser.ast.NodeList; import com.github.javaparser.ast.body.MethodDeclaration; import com.github.javaparser.ast.stmt.ExpressionStmt; import tfm.arcs.Arc; +import tfm.arcs.cfg.ControlFlowArc; import tfm.arcs.pdg.ControlDependencyArc; import tfm.arcs.pdg.DataDependencyArc; import tfm.arcs.sdg.CallArc; @@ -19,6 +20,7 @@ import tfm.slicing.Slice; import tfm.slicing.Sliceable; import tfm.slicing.SlicingCriterion; import tfm.utils.Context; +import tfm.utils.Utils; import java.util.*; import java.util.function.Function; @@ -82,19 +84,24 @@ public class SDG extends Graph implements Sliceable, Buildable> findDeclarationsOfVariable(String variable, GraphNode root) { - List> res = new ArrayList<>(); + return this.methodCFGMap.values().stream() + .filter(cfg -> cfg.containsVertex(root)) + .findFirst() + .map(cfg -> doFindDeclarationsOfVariable(variable, root, cfg, Utils.emptyList())) + .orElse(Utils.emptyList()); + } - // First, expand the node - for (Arc arc : incomingEdgesOf(root)) { - if (arc.isDataDependencyArc() || arc.isControlDependencyArc()) { - res.addAll(findDeclarationsOfVariable(variable, getEdgeSource(arc))); - } - } + private List> doFindDeclarationsOfVariable(String variable, GraphNode root, CFG cfg, List> res) { + Set controlDependencies = cfg.incomingEdgesOf(root); - // Finally, the current node - // This way, the last element of the list is the most recent declaration - if (root.getDeclaredVariables().contains(variable)) { - res.add(root); + for (Arc arc : controlDependencies) { + GraphNode source = cfg.getEdgeSource(arc); + + if (source.getDeclaredVariables().contains(variable)) { + res.add(root); + } else { + res.addAll(doFindDeclarationsOfVariable(variable, source, cfg, res)); + } } return res; diff --git a/src/test/res/programs/sdg/Example1.java b/src/test/res/programs/sdg/Example1.java index 39fdea4..44fe6ee 100644 --- a/src/test/res/programs/sdg/Example1.java +++ b/src/test/res/programs/sdg/Example1.java @@ -14,16 +14,16 @@ public class Example1 { int num; public static void main(String[] args) { - int x = 1; - int y = 2; + int n1 = 1; + int n2 = 2; - Example1 example1 = new Example1(); - Example1 example2 = new Example1(); +// Example1 example1 = new Example1(); +// Example1 example2 = new Example1(); - int f = sum(example1.getNum(), example2.num); + int f = sum(sum(n1, n2), n2); - Logger.log(example1.num); Logger.log(f); + Logger.log(z); } public int getNum() { -- GitLab From 21428b4871ebf07ea9e43f03ba599d1a9ae8af43 Mon Sep 17 00:00:00 2001 From: jacosro Date: Sun, 17 May 2020 01:45:56 +0200 Subject: [PATCH 20/20] Added return call node --- src/main/java/tfm/arcs/sdg/ReturnArc.java | 16 ++++++++++++++++ .../graphs/sdg/MethodCallReplacerVisitor.java | 13 ++++++++++++- src/main/java/tfm/graphs/sdg/SDG.java | 7 +++++++ src/main/java/tfm/nodes/type/NodeType.java | 3 ++- 4 files changed, 37 insertions(+), 2 deletions(-) create mode 100644 src/main/java/tfm/arcs/sdg/ReturnArc.java diff --git a/src/main/java/tfm/arcs/sdg/ReturnArc.java b/src/main/java/tfm/arcs/sdg/ReturnArc.java new file mode 100644 index 0000000..ea3a55a --- /dev/null +++ b/src/main/java/tfm/arcs/sdg/ReturnArc.java @@ -0,0 +1,16 @@ +package tfm.arcs.sdg; + +import org.jgrapht.io.Attribute; +import org.jgrapht.io.DefaultAttribute; +import tfm.arcs.Arc; + +import java.util.Map; + +public class ReturnArc extends Arc { + @Override + public Map getDotAttributes() { + Map map = super.getDotAttributes(); + map.put("style", DefaultAttribute.createAttribute("dashed")); + return map; + } +} diff --git a/src/main/java/tfm/graphs/sdg/MethodCallReplacerVisitor.java b/src/main/java/tfm/graphs/sdg/MethodCallReplacerVisitor.java index b843439..ace0075 100644 --- a/src/main/java/tfm/graphs/sdg/MethodCallReplacerVisitor.java +++ b/src/main/java/tfm/graphs/sdg/MethodCallReplacerVisitor.java @@ -250,7 +250,18 @@ class MethodCallReplacerVisitor extends VoidVisitorAdapter { } } - // todo make call + // 'Return' node + GraphNode returnNode = sdg.addNode("return", new EmptyStmt(), TypeNodeFactory.fromType(NodeType.METHOD_CALL_RETURN)); + sdg.addControlDependencyArc(methodCallNode, returnNode); + + sdg.addReturnArc(returnNode, originalMethodCallNode); + + methodCFG.vertexSet().stream() + .filter(node -> node.getAstNode() instanceof ReturnStmt) + .map(node -> (GraphNode) node) + .forEach(node -> sdg.addReturnArc(node, returnNode)); + + Logger.log("MethodCallReplacerVisitor", String.format("%s | Method '%s' called", methodCallExpr, methodDeclaration.getNameAsString())); } diff --git a/src/main/java/tfm/graphs/sdg/SDG.java b/src/main/java/tfm/graphs/sdg/SDG.java index 3c3dd89..c17afc2 100644 --- a/src/main/java/tfm/graphs/sdg/SDG.java +++ b/src/main/java/tfm/graphs/sdg/SDG.java @@ -4,13 +4,16 @@ import com.github.javaparser.ast.CompilationUnit; import com.github.javaparser.ast.Node; import com.github.javaparser.ast.NodeList; import com.github.javaparser.ast.body.MethodDeclaration; +import com.github.javaparser.ast.stmt.EmptyStmt; import com.github.javaparser.ast.stmt.ExpressionStmt; +import com.github.javaparser.ast.stmt.ReturnStmt; import tfm.arcs.Arc; import tfm.arcs.cfg.ControlFlowArc; import tfm.arcs.pdg.ControlDependencyArc; import tfm.arcs.pdg.DataDependencyArc; import tfm.arcs.sdg.CallArc; import tfm.arcs.sdg.ParameterInOutArc; +import tfm.arcs.sdg.ReturnArc; import tfm.graphs.Buildable; import tfm.graphs.Graph; import tfm.graphs.cfg.CFG; @@ -83,6 +86,10 @@ public class SDG extends Graph implements Sliceable, Buildable from, GraphNode to) { + this.addEdge(from, to, new ReturnArc()); + } + public List> findDeclarationsOfVariable(String variable, GraphNode root) { return this.methodCFGMap.values().stream() .filter(cfg -> cfg.containsVertex(root)) diff --git a/src/main/java/tfm/nodes/type/NodeType.java b/src/main/java/tfm/nodes/type/NodeType.java index 425d7b0..2cb3fba 100644 --- a/src/main/java/tfm/nodes/type/NodeType.java +++ b/src/main/java/tfm/nodes/type/NodeType.java @@ -5,5 +5,6 @@ public enum NodeType { METHOD, METHOD_CALL, VARIABLE_IN, - VARIABLE_OUT + VARIABLE_OUT, + METHOD_CALL_RETURN } -- GitLab