diff --git a/.gitignore b/.gitignore index 77901fe0104c73e4efa7d26cd2a376b950baabd9..d627c53c44b41750991e76b0b5c0a6300840fa36 100644 --- a/.gitignore +++ b/.gitignore @@ -2,4 +2,5 @@ .idea/ target/ out/ -.settings \ No newline at end of file +.settings +.attach* diff --git a/doc/diary/diary.md b/doc/diary/diary.md new file mode 100644 index 0000000000000000000000000000000000000000..e59dc0c8557f5c0ba21dd8914a1018aaf38f9164 --- /dev/null +++ b/doc/diary/diary.md @@ -0,0 +1,52 @@ +# 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 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/arcs/Arc.java b/src/main/java/tfm/arcs/Arc.java index 6dc746ef2353a913415c5faab82fc96c3a167511..5af9bde90e8b31acc3a6c0f285ef7bf3bbf4461b 100644 --- a/src/main/java/tfm/arcs/Arc.java +++ b/src/main/java/tfm/arcs/Arc.java @@ -5,6 +5,7 @@ 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.HashMap; @@ -12,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; @@ -51,16 +62,23 @@ public abstract class Arc extends DefaultEdge { throw new UnsupportedOperationException("Not a DataDependencyArc"); } + /** @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 String getLabel() { - return ""; - } - public Map getDotAttributes() { return new HashMap<>(); } @@ -73,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 22a7bd58804bcc2e6c0e11b76cd0d7aed94369ce..649f84c789dcafcb8636386d68d3252167d00933 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/arcs/sdg/CallArc.java b/src/main/java/tfm/arcs/sdg/CallArc.java new file mode 100644 index 0000000000000000000000000000000000000000..43c41382a6f644d6eae191c281ab5cdacc920856 --- /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 0000000000000000000000000000000000000000..50673b700ca84ca8f7e556f209334b5376298f70 --- /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/arcs/sdg/ReturnArc.java b/src/main/java/tfm/arcs/sdg/ReturnArc.java new file mode 100644 index 0000000000000000000000000000000000000000..ea3a55aea343f82d0445164b71d4174301fa2a35 --- /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/exec/Main.java b/src/main/java/tfm/exec/Main.java index 51b9e8ef71372561603322be8c7a66c0f4f23c62..6d7a54e41a450a85e931b32efde677cbb82f27c4 100644 --- a/src/main/java/tfm/exec/Main.java +++ b/src/main/java/tfm/exec/Main.java @@ -2,7 +2,12 @@ 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 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; @@ -17,8 +22,8 @@ import java.util.Optional; public class Main { - public static final String PROGRAM = Utils.PROGRAMS_FOLDER + "cfg/Eval_4.java"; - public static final String GRAPH = GraphLog.SDG; + public static final String PROGRAM = Utils.PROGRAMS_FOLDER + "sdg/Example1.java"; + public static final String GRAPH = GraphLog.PDG; public static final String METHOD = "main"; public static void main(String[] args) throws IOException { @@ -65,8 +70,9 @@ public class Main { pdg.build(method); return pdg; case GraphLog.SDG: - Logger.log("Not yet considered!"); - return new SDG(); + 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/exec/MethodResolver.java b/src/main/java/tfm/exec/MethodResolver.java new file mode 100644 index 0000000000000000000000000000000000000000..e2d28d6ccb7267ca43d700e6b042a3d7ce0cb09b --- /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/exec/PDGLog.java b/src/main/java/tfm/exec/PDGLog.java index 5ed01629705f85219e6f87e47e6ac12b549ea236..af36803c54f5cf8db6137d44411f37f351ef6278 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 e3a1473671d6bd15da9e944e1ab141bd3df9d50f..9326553a2022f1fad16a9a1c53d8dc0209e8feff 100644 --- a/src/main/java/tfm/graphs/Graph.java +++ b/src/main/java/tfm/graphs/Graph.java @@ -1,6 +1,7 @@ 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; @@ -9,7 +10,6 @@ import tfm.nodes.NodeFactory; import tfm.utils.ASTUtils; import java.util.*; -import java.util.function.Consumer; import java.util.stream.Collectors; /** @@ -17,59 +17,29 @@ 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(@NotNull GraphNode node) { + this.addVertex(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.addVertex(copy); + this.addNode(newNode); - return copy; + return newNode; } @SuppressWarnings("unchecked") @@ -80,7 +50,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(); @@ -88,22 +58,11 @@ public abstract class Graph extends DirectedPseudograph, Arc> { @Override public String toString() { - return vertexSet().stream() - .sorted(Comparator.comparingInt(GraphNode::getId)) + return vertexSet().stream().sorted() .map(GraphNode::toString) .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)) - .collect(Collectors.toList()); - } - public boolean isEmpty() { return this.vertexSet().isEmpty(); } @@ -116,98 +75,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 104b1601bf9f6b1761bafecc886cede230628527..d7a3442dd9d4ebbc23e0a787c63ef8d2af4a60b3 100644 --- a/src/main/java/tfm/graphs/GraphWithRootNode.java +++ b/src/main/java/tfm/graphs/GraphWithRootNode.java @@ -2,6 +2,7 @@ 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; @@ -10,12 +11,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(); } /** @@ -26,22 +25,30 @@ 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(ROOT_NODE_ID, instruction, rootNodeAst); + GraphNode root = nodeFactory.graphNode(instruction, rootNodeAst); this.rootNode = root; this.addVertex(root); return true; } - public Optional> getRootNode() { + public Optional> getRootNode() { 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/augmented/ACFGBuilder.java b/src/main/java/tfm/graphs/augmented/ACFGBuilder.java index d250d0909795fc76259e9eae06741489d5df99a6..3972f67bd20328d812df7efb07e069c52a2ab8f2 100644 --- a/src/main/java/tfm/graphs/augmented/ACFGBuilder.java +++ b/src/main/java/tfm/graphs/augmented/ACFGBuilder.java @@ -8,6 +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.TypeNodeFactory; +import tfm.nodes.type.NodeType; import tfm.utils.ASTUtils; import java.util.LinkedList; @@ -237,12 +239,13 @@ 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, TypeNodeFactory.fromType(NodeType.METHOD)); hangingNodes.add(graph.getRootNode().get()); methodDeclaration.getBody().get().accept(this, arg); returnList.stream().filter(node -> !hangingNodes.contains(node)).forEach(hangingNodes::add); nonExecHangingNodes.add(graph.getRootNode().get()); - connectTo(new EmptyStmt(), "Exit"); + GraphNode exitNode = connectTo(new EmptyStmt(), "Exit"); + ((ACFG) graph).setExitNode(exitNode); } } diff --git a/src/main/java/tfm/graphs/cfg/CFG.java b/src/main/java/tfm/graphs/cfg/CFG.java index 296428d245cdb6c66691d7201c043c982bd8388d..97268d8e8ea6f6de89e8f1622b8bc4b20e737e3c 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; @@ -20,6 +21,7 @@ import java.util.Set; */ public class CFG extends GraphWithRootNode { private boolean built = false; + protected GraphNode exitNode; public CFG() { super(); @@ -39,7 +41,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<>(); @@ -62,9 +64,28 @@ public class CFG extends GraphWithRootNode { return res; } + @Override + public boolean removeVertex(GraphNode graphNode) { + if (Objects.equals(graphNode, exitNode)) + return false; + return super.removeVertex(graphNode); + } + + public GraphNode getExitNode() { + return exitNode; + } + + protected void setExitNode(GraphNode exitNode) { + if (this.exitNode != null) + throw new IllegalStateException("Exit node already set!"); + this.exitNode = exitNode; + } + @Override public void build(MethodDeclaration method) { method.accept(newCFGBuilder(), null); + if (exitNode == null) + throw new IllegalStateException("Exit node missing!"); built = true; } diff --git a/src/main/java/tfm/graphs/cfg/CFGBuilder.java b/src/main/java/tfm/graphs/cfg/CFGBuilder.java index 56f099620d683c16c000eb279b707e1c24bd2d46..624bb7b0900fc26d0aa422e3ab2448e3cd900d05 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.NodeFactory; +import tfm.nodes.TypeNodeFactory; +import tfm.nodes.type.NodeType; 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,59 @@ 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, TypeNodeFactory.fromType(NodeType.METHOD)); + + // 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, TypeNodeFactory.fromType(NodeType.VARIABLE_IN)); + connectTo(node); + } + methodDeclaration.getBody().get().accept(this, arg); + returnList.stream().filter(node -> !hangingNodes.contains(node)).forEach(hangingNodes::add); - connectTo(new EmptyStmt(), "Exit"); + + // Add out variable nodes + for (ExpressionStmt expressionStmt : outVariableExpressions) { + GraphNode node = this.graph.addNode(expressionStmt.toString(), expressionStmt, TypeNodeFactory.fromType(NodeType.VARIABLE_OUT)); + connectTo(node); + } + + GraphNode exitNode = connectTo(new EmptyStmt(), "Exit"); + graph.setExitNode(exitNode); } } diff --git a/src/main/java/tfm/graphs/pdg/ControlDependencyBuilder.java b/src/main/java/tfm/graphs/pdg/ControlDependencyBuilder.java index 29fbe201c9fbcee7a1735c8c58db16de57f04fe9..4e2993342c00a2fc02d6ebac591e790d49277c7f 100644 --- a/src/main/java/tfm/graphs/pdg/ControlDependencyBuilder.java +++ b/src/main/java/tfm/graphs/pdg/ControlDependencyBuilder.java @@ -4,12 +4,8 @@ import tfm.arcs.Arc; import tfm.graphs.augmented.PPDG; import tfm.graphs.cfg.CFG; import tfm.nodes.GraphNode; -import tfm.nodes.NodeFactory; -import java.util.HashMap; -import java.util.HashSet; -import java.util.Map; -import java.util.Set; +import java.util.*; import java.util.stream.Collectors; /** @@ -38,23 +34,23 @@ class ControlDependencyBuilder { } public void analyze() { - Map, GraphNode> nodeMap = new HashMap<>(); assert cfg.getRootNode().isPresent(); assert pdg.getRootNode().isPresent(); - nodeMap.put(cfg.getRootNode().get(), pdg.getRootNode().get()); + Set> roots = new HashSet<>(cfg.vertexSet()); roots.remove(cfg.getRootNode().get()); + Set> cfgNodes = new HashSet<>(cfg.vertexSet()); - cfgNodes.removeIf(node -> node.getInstruction().equals("Exit")); + cfgNodes.remove(cfg.getExitNode()); for (GraphNode node : cfgNodes) - registerNode(node, nodeMap); + registerNode(node); for (GraphNode src : cfgNodes) { for (GraphNode dest : cfgNodes) { if (src == dest) continue; if (hasControlDependence(src, dest)) { - pdg.addControlDependencyArc(nodeMap.get(src), nodeMap.get(dest)); + pdg.addControlDependencyArc(src, dest); roots.remove(dest); } } @@ -62,15 +58,11 @@ class ControlDependencyBuilder { // In the original definition, nodes were dependent by default on the Enter/Start node for (GraphNode node : roots) if (!node.getInstruction().equals("Exit")) - pdg.addControlDependencyArc(pdg.getRootNode().get(), nodeMap.get(node)); + pdg.addControlDependencyArc(pdg.getRootNode().get(), node); } - public void registerNode(GraphNode node, Map, GraphNode> nodeMap) { - if (nodeMap.containsKey(node) || node.getInstruction().equals("Exit")) - return; - GraphNode clone = NodeFactory.graphNode(node.getId(), node.getInstruction(), node.getAstNode()); - nodeMap.put(node, clone); - pdg.addVertex(clone); + public void registerNode(GraphNode node) { + pdg.addVertex(node); } public boolean hasControlDependence(GraphNode a, GraphNode b) { diff --git a/src/main/java/tfm/graphs/pdg/DataDependencyBuilder.java b/src/main/java/tfm/graphs/pdg/DataDependencyBuilder.java index 42042b666e3ac578cd1d4401322e46108e702d50..f8d11d57ddb7401ade9c5bcdd0cb06e3047f8db3 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/PDG.java b/src/main/java/tfm/graphs/pdg/PDG.java index df79831ebd6abbbe7f49b44e1409f10f78c59b12..328281af6ebb27a13f3189cff6a9c96fe56dca2a 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/pdg/PDGBuilder.java b/src/main/java/tfm/graphs/pdg/PDGBuilder.java index 363e3a7ee255458419338abce3a832fcd1ec194b..3573f6b39a07db0f0e8bb12466c19a62f33530ff 100644 --- a/src/main/java/tfm/graphs/pdg/PDGBuilder.java +++ b/src/main/java/tfm/graphs/pdg/PDGBuilder.java @@ -1,8 +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; @@ -37,10 +43,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 @@ -52,6 +54,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(); @@ -59,5 +65,31 @@ public class PDGBuilder { // Build data dependency DataDependencyBuilder dataDependencyBuilder = new DataDependencyBuilder(pdg, cfg); 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)); + }); } } diff --git a/src/main/java/tfm/graphs/sdg/MethodCallReplacer.java b/src/main/java/tfm/graphs/sdg/MethodCallReplacer.java index 1dc5bb06ab6d369cf70868d60e3df218d6c87998..e8290fdec0024402f6e1f0ae4687a7f3c700f099 100644 --- a/src/main/java/tfm/graphs/sdg/MethodCallReplacer.java +++ b/src/main/java/tfm/graphs/sdg/MethodCallReplacer.java @@ -1,5 +1,13 @@ 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; + +import java.util.Optional; + class MethodCallReplacer { private SDG sdg; @@ -8,14 +16,9 @@ class MethodCallReplacer { this.sdg = sdg; } - 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); - }); + 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 f48547e63f91b698c3519cc1db7c697470056f40..ace0075eeac90cada1547361892d4792f8719612 100644 --- a/src/main/java/tfm/graphs/sdg/MethodCallReplacerVisitor.java +++ b/src/main/java/tfm/graphs/sdg/MethodCallReplacerVisitor.java @@ -1,142 +1,278 @@ package tfm.graphs.sdg; +import com.github.javaparser.ast.ArrayCreationLevel; import com.github.javaparser.ast.Node; -import com.github.javaparser.ast.body.ClassOrInterfaceDeclaration; +import com.github.javaparser.ast.NodeList; import com.github.javaparser.ast.body.MethodDeclaration; +import com.github.javaparser.ast.body.Parameter; import com.github.javaparser.ast.body.VariableDeclarator; -import com.github.javaparser.ast.expr.MethodCallExpr; -import com.github.javaparser.ast.expr.VariableDeclarationExpr; -import com.github.javaparser.ast.stmt.ExpressionStmt; -import com.github.javaparser.ast.type.Type; +import com.github.javaparser.ast.expr.*; +import com.github.javaparser.ast.stmt.*; import com.github.javaparser.ast.visitor.VoidVisitorAdapter; -import tfm.graphs.pdg.PDG; +import tfm.arcs.Arc; +import tfm.arcs.pdg.DataDependencyArc; +import tfm.graphs.cfg.CFG; import tfm.nodes.GraphNode; +import tfm.nodes.TypeNodeFactory; +import tfm.nodes.type.NodeType; import tfm.utils.Context; import tfm.utils.Logger; +import tfm.utils.MethodDeclarationSolver; -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 PDG pdg; + private SDG sdg; + private GraphNode originalMethodCallNode; - public MethodCallReplacerVisitor(PDG pdg) { - this.pdg = pdg; + public MethodCallReplacerVisitor(SDG sdg) { + this.sdg = sdg; + } + + private void searchAndSetMethodCallNode(Node node) { + Optional> optionalNode = sdg.findNodeByASTNode(node); + assert optionalNode.isPresent(); + originalMethodCallNode = optionalNode.get(); + + } + + @Override + public void visit(DoStmt n, Context arg) { + searchAndSetMethodCallNode(n); + super.visit(n, arg); + } + + @Override + public void visit(ForEachStmt n, Context arg) { + searchAndSetMethodCallNode(n); + super.visit(n, arg); + } + + @Override + public void visit(ForStmt n, Context arg) { + searchAndSetMethodCallNode(n); + super.visit(n, arg); + } + + @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) { + NodeList 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() - ? shouldMakeCallWithScope(methodCallExpr, context) - : shouldMakeCallWithNoScope(methodCallExpr, context); + Optional> optionalNethodDeclarationNode = + MethodDeclarationSolver.getInstance() + .findDeclarationFrom(methodCallExpr) + .flatMap(methodDeclaration -> sdg.findNodeByASTNode(methodDeclaration)); - if (!optionalCallingMethod.isPresent()) { + if (!optionalNethodDeclarationNode.isPresent()) { + Logger.format("Not found: '%s'. Discarding", methodCallExpr); return; } - // todo make call - Logger.log(String.format("Method '%s' called", optionalCallingMethod.get().getNameAsString())); - } + GraphNode methodDeclarationNode = optionalNethodDeclarationNode.get(); + MethodDeclaration methodDeclaration = methodDeclarationNode.getAstNode(); - private Optional shouldMakeCallWithScope(MethodCallExpr methodCallExpr, Context context) { - assert methodCallExpr.getScope().isPresent(); + Optional optionalCFG = sdg.getMethodCFG(methodDeclaration); + assert optionalCFG.isPresent(); + CFG methodCFG = optionalCFG.get(); - String scopeName = methodCallExpr.getScope().get().toString(); + GraphNode methodCallNode = sdg.addNode("CALL " + methodCallExpr.toString(), methodCallExpr, TypeNodeFactory.fromType(NodeType.METHOD_CALL)); - if (!context.getCurrentClass().isPresent()) { - return Optional.empty(); - } + sdg.addControlDependencyArc(originalMethodCallNode, methodCallNode); + sdg.addCallArc(methodCallNode, methodDeclarationNode); - ClassOrInterfaceDeclaration currentClass = context.getCurrentClass().get(); + NodeList parameters = methodDeclarationNode.getAstNode().getParameters(); - // Check if it's a static method call of current class - if (!Objects.equals(scopeName, currentClass.getNameAsString())) { + for (int i = 0; i < parameters.size(); i++) { + Parameter parameter = parameters.get(i); + Expression argument; - // Check if 'scopeName' is a variable - List> declarations = pdg.findDeclarationsOfVariable(scopeName); + if (!parameter.isVarArgs()) { + argument = arguments.get(i); + } else { + NodeList varArgs = new NodeList<>(arguments.subList(i, arguments.size())); - if (declarations.isEmpty()) { - // It is a static method call of another class. We do nothing - return Optional.empty(); + argument = new ArrayCreationExpr( + parameter.getType(), + new NodeList<>(new ArrayCreationLevel(varArgs.size())), + new ArrayInitializerExpr(varArgs) + ); } - /* - 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); + // In expression + VariableDeclarationExpr inVariableDeclarationExpr = new VariableDeclarationExpr( + new VariableDeclarator( + parameter.getType(), + parameter.getNameAsString() + "_in", + new NameExpr(argument.toString()) + ) + ); - ExpressionStmt declarationExpr = (ExpressionStmt) declarationNode.getAstNode(); - VariableDeclarationExpr variableDeclarationExpr = declarationExpr.getExpression().asVariableDeclarationExpr(); + ExpressionStmt inExprStmt = new ExpressionStmt(inVariableDeclarationExpr); - Optional optionalVariableDeclarator = variableDeclarationExpr.getVariables().stream() - .filter(variableDeclarator -> Objects.equals(variableDeclarator.getNameAsString(), scopeName)) - .findFirst(); + GraphNode argumentInNode = sdg.addNode(inExprStmt.toString(), inExprStmt, TypeNodeFactory.fromType(NodeType.VARIABLE_IN)); + + sdg.addControlDependencyArc(methodCallNode, argumentInNode); - if (!optionalVariableDeclarator.isPresent()) { - // should not happen - return Optional.empty(); + // Handle data dependency: Remove arc from method call node and add it to IN node + + List inDataDependencies = sdg.incomingEdgesOf(originalMethodCallNode).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()); } - Type variableType = optionalVariableDeclarator.get().getType(); + // 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 (!variableType.isClassOrInterfaceType()) { - // Not class type - return Optional.empty(); + 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)); } - if (!Objects.equals(variableType.asClassOrInterfaceType().getNameAsString(), currentClass.getNameAsString())) { - // object is not instance of our class - return Optional.empty(); + // Out expression + + OutNodeVariableVisitor shouldHaveOutNodeVisitor = new OutNodeVariableVisitor(); + Set variablesForOutNode = new HashSet<>(); + argument.accept(shouldHaveOutNodeVisitor, variablesForOutNode); + + // 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, + 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(); + + 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; + } + } else { + continue; } - // if we got here, the object is instance of our class - } + AssignExpr outVariableAssignExpr = new AssignExpr( + argument, + new NameExpr(parameter.getNameAsString() + "_out"), + AssignExpr.Operator.ASSIGN + ); - // It's a static method call to a method of the current class + ExpressionStmt outExprStmt = new ExpressionStmt(outVariableAssignExpr); - return findMethodInClass(methodCallExpr, currentClass); - } + GraphNode argumentOutNode = sdg.addNode(outExprStmt.toString(), outExprStmt, TypeNodeFactory.fromType(NodeType.VARIABLE_OUT)); + + sdg.addControlDependencyArc(methodCallNode, argumentOutNode); + + // 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 - private Optional shouldMakeCallWithNoScope(MethodCallExpr methodCallExpr, Context context) { - assert !methodCallExpr.getScope().isPresent(); + List outDataDependencies = sdg.outgoingEdgesOf(originalMethodCallNode).stream() + .filter(arc -> arc.isDataDependencyArc() && Objects.equals(arc.getLabel(), argument.toString())) + .map(Arc::asDataDependencyArc) + .collect(Collectors.toList()); - /* - 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. - */ + for (DataDependencyArc arc : outDataDependencies) { + GraphNode dataDependencyTarget = sdg.getEdgeTarget(arc); + sdg.removeEdge(arc); + sdg.addDataDependencyArc(argumentOutNode, dataDependencyTarget, argument.toString()); + } - if (!context.getCurrentClass().isPresent()) { - return Optional.empty(); + 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)); + } } - // We get the current class and search along their methods to find the one we're looking for... + // '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)); - ClassOrInterfaceDeclaration currentClass = context.getCurrentClass().get(); - return findMethodInClass(methodCallExpr, currentClass); + Logger.log("MethodCallReplacerVisitor", String.format("%s | Method '%s' called", methodCallExpr, methodDeclaration.getNameAsString())); } - 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[]{}); + private void argumentAsNameExpr(GraphNode methodCallNode) { - List classMethods = - klass.getMethodsBySignature(methodCallExpr.getNameAsString(), typeParameters); + } - if (classMethods.isEmpty()) { - return Optional.empty(); // The method called is not inside the current class - } + @Override + public void visit(MethodDeclaration n, Context arg) { + arg.setCurrentMethod(n); - // The current method is inside the current class, so we make the call - return Optional.of(classMethods.get(0)); + super.visit(n, arg); } } diff --git a/src/main/java/tfm/graphs/sdg/NewSDGBuilder.java b/src/main/java/tfm/graphs/sdg/NewSDGBuilder.java deleted file mode 100644 index b170a566e691441f60e17ce7356190d3b9fae9d7..0000000000000000000000000000000000000000 --- a/src/main/java/tfm/graphs/sdg/NewSDGBuilder.java +++ /dev/null @@ -1,67 +0,0 @@ -package tfm.graphs.sdg; - -import com.github.javaparser.ast.CompilationUnit; -import com.github.javaparser.ast.body.ClassOrInterfaceDeclaration; -import com.github.javaparser.ast.body.MethodDeclaration; -import com.github.javaparser.ast.visitor.VoidVisitorAdapter; -import tfm.graphs.pdg.PDG; -import tfm.utils.Context; - -class NewSDGBuilder extends VoidVisitorAdapter { - - SDG sdg; - - public NewSDGBuilder(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); - - sdg.addMethod(methodDeclaration, pdg); - } - - @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(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/graphs/sdg/OutNodeVariableVisitor.java b/src/main/java/tfm/graphs/sdg/OutNodeVariableVisitor.java new file mode 100644 index 0000000000000000000000000000000000000000..b4fe6bc612ab40080042fade30b2ccda484fd9e6 --- /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/graphs/sdg/SDG.java b/src/main/java/tfm/graphs/sdg/SDG.java index 30f7d36b1f0910e66ad3493caa9292bd2111ee74..c17afc2e0c816521c988e4c1064bbd6612f73239 100644 --- a/src/main/java/tfm/graphs/sdg/SDG.java +++ b/src/main/java/tfm/graphs/sdg/SDG.java @@ -1,32 +1,42 @@ 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.body.Parameter; 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; import tfm.graphs.pdg.PDG; -import tfm.nodes.GraphNode; -import tfm.nodes.NodeFactory; +import tfm.nodes.*; import tfm.slicing.Slice; import tfm.slicing.Sliceable; import tfm.slicing.SlicingCriterion; import tfm.utils.Context; +import tfm.utils.Utils; -import java.util.Collection; -import java.util.HashMap; -import java.util.Map; -import java.util.Set; +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> { private boolean built = false; - private Map contextPDGGraphMap; + + private Map methodCFGMap; public SDG() { - this.contextPDGGraphMap = new HashMap<>(); + this.methodCFGMap = new HashMap<>(); } @Override @@ -36,7 +46,7 @@ public class SDG extends Graph implements Sliceable, Buildable nodeList) { - nodeList.accept(new SDGBuilder(this), null); + nodeList.accept(new SDGBuilder(this), new Context()); } @Override @@ -44,60 +54,63 @@ public class SDG extends Graph implements Sliceable, Buildable getContextPDGGraphMap() { - return contextPDGGraphMap; + public Set getMethodDeclarations() { + return this.methodCFGMap.keySet(); } - public Set getContexts() { - return contextPDGGraphMap.keySet(); + public void setMethodCFG(MethodDeclaration methodDeclaration, CFG cfg) { + this.methodCFGMap.put(methodDeclaration, cfg); } - public Set getMethods() { - return getContexts().stream() - .filter(context -> context.getCurrentMethod().isPresent()) - .map(context -> context.getCurrentMethod().get()) - .collect(Collectors.toSet()); + public Optional getMethodCFG(MethodDeclaration methodDeclaration) { + if (!this.methodCFGMap.containsKey(methodDeclaration)) { + return Optional.empty(); + } + + return Optional.of(this.methodCFGMap.get(methodDeclaration)); } - public Collection getPDGs() { - return contextPDGGraphMap.values(); + public void addControlDependencyArc(GraphNode from, GraphNode to) { + this.addEdge(from, to, new ControlDependencyArc()); } - @Deprecated - public void addPDG(PDG pdg, MethodDeclaration methodDeclaration) { - for (Parameter parameter : methodDeclaration.getParameters()) { - GraphNode sdgNode = NodeFactory.graphNode( - getNextVertexId(), - String.format("%s = %s_in", parameter.getNameAsString(), parameter.getNameAsString()), - new EmptyStmt() - ); + public void addDataDependencyArc(GraphNode from, GraphNode to, String variable) { + this.addEdge(from, to, new DataDependencyArc(variable)); + } - addVertex(sdgNode); - } + public void addCallArc(GraphNode from, GraphNode to) { + this.addEdge(from, to, new CallArc()); + } - for (GraphNode node : pdg.vertexSet()) { - if (!this.containsVertex(node)) { - GraphNode sdgNode = NodeFactory.computedGraphNode( - getNextVertexId(), - node.getInstruction(), - node.getAstNode(), - node.getDeclaredVariables(), - node.getDefinedVariables(), - node.getUsedVariables() - ); - - addVertex(sdgNode); - } - } + public void addParameterInOutArc(GraphNode from, GraphNode to) { + this.addEdge(from, to, new ParameterInOutArc()); } - public void addMethod(MethodDeclaration methodDeclaration, PDG pdg) { - GraphNode methodRootNode = NodeFactory.graphNode( - getNextVertexId(), - "ENTER " + methodDeclaration.getDeclarationAsString(false, false, true), - methodDeclaration - ); + public void addReturnArc(GraphNode 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)) + .findFirst() + .map(cfg -> doFindDeclarationsOfVariable(variable, root, cfg, Utils.emptyList())) + .orElse(Utils.emptyList()); + } + + private List> doFindDeclarationsOfVariable(String variable, GraphNode root, CFG cfg, List> res) { + Set controlDependencies = cfg.incomingEdgesOf(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)); + } + } - super.addVertex(methodRootNode); + return res; } } diff --git a/src/main/java/tfm/graphs/sdg/SDGBuilder.java b/src/main/java/tfm/graphs/sdg/SDGBuilder.java index 6b1fddfa549226940ab74cd88d5d3d72c7b1a9c2..48701eacbd3781932475421d0d30f7a427e50f29 100644 --- a/src/main/java/tfm/graphs/sdg/SDGBuilder.java +++ b/src/main/java/tfm/graphs/sdg/SDGBuilder.java @@ -3,138 +3,60 @@ package tfm.graphs.sdg; import com.github.javaparser.ast.CompilationUnit; import com.github.javaparser.ast.body.ClassOrInterfaceDeclaration; import com.github.javaparser.ast.body.MethodDeclaration; -import com.github.javaparser.ast.expr.Expression; -import com.github.javaparser.ast.expr.MethodCallExpr; -import com.github.javaparser.ast.stmt.Statement; +import com.github.javaparser.ast.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.ArrayList; -import java.util.List; - -/** - * 31/8/19 - * Asumimos que procesamos 1 archivo con una o más clases donde el primer método de la primera clase es el main - * - */ -class SDGBuilder extends VoidVisitorAdapter { +class SDGBuilder extends VoidVisitorAdapter { SDG sdg; - List pdgs; - - private ClassOrInterfaceDeclaration currentClass; - private CompilationUnit currentCompilationUnit; - protected SDGBuilder(SDG sdg) { + public SDGBuilder(SDG sdg) { this.sdg = sdg; - this.pdgs = new ArrayList<>(); } @Override - public void visit(MethodDeclaration methodDeclaration, Void ignored) { - if (!methodDeclaration.getBody().isPresent()) + public void visit(MethodDeclaration methodDeclaration, Context context) { + if (!methodDeclaration.getBody().isPresent()) { return; - - - if (sdg.isEmpty()) { - sdg.addNode("ENTER " + methodDeclaration.getNameAsString(), methodDeclaration); - } else { -// sdgGraph.addMethod(methodDeclaration); } - PDG pdg = new PDG(); - - // TODO: this should happen in the PDG's creation, not here. -// PDGBuilder PDGBuilder = new PDGBuilder(pdg) { -// @Override -// public void visit(MethodCallExpr methodCallExpr, Void empty) { -// if (methodCallExpr.getScope().isPresent()) { -// String scopeName = methodCallExpr.getScope().get().toString(); -// -// String currentClassName = currentClass.getNameAsString(); -// -// // Check if it's a static method call of current class -// if (!Objects.equals(scopeName, currentClassName)) { -// -// // Check if 'scopeName' is a variable -// List> declarations = sdg.findDeclarationsOfVariable(scopeName); -// -// if (declarations.isEmpty()) { -// // It is a static method call of another class. We don't do anything -// return; -// } else { -// /* -// It's a variable since it has declarations. We now have to check if the class name -// is the same as the current class (the object is an instance of our class) -// */ -// GraphNode declarationNode = declarations.get(declarations.size() - 1); -// -// ExpressionStmt declarationExpr = (ExpressionStmt) declarationNode.getAstNode(); -// VariableDeclarationExpr variableDeclarationExpr = declarationExpr.getExpression().asVariableDeclarationExpr(); -// -// Optional optionalVariableDeclarator = variableDeclarationExpr.getVariables().stream() -// .filter(variableDeclarator -> Objects.equals(variableDeclarator.getNameAsString(), scopeName)) -// .findFirst(); -// -// if (!optionalVariableDeclarator.isPresent()) { -// // should not happen -// return; -// } -// -// Type variableType = optionalVariableDeclarator.get().getType(); -// -// if (!variableType.isClassOrInterfaceType()) { -// // Not class type -// return; -// } -// -// if (!Objects.equals(variableType.asClassOrInterfaceType().getNameAsString(), currentClassName)) { -// // object is not instance of our class -// return; -// } -// -// // if we got here, the object is instance of our class, so we make the call -// } -// } -// -// // It's a static method call to a method of the current class -// -// } -// } -// }; - -// PDGBuilder.createFrom(methodDeclaration); - - - sdg.addNode(methodDeclaration.getNameAsString(), methodDeclaration); - - pdg.vertexSet().stream().skip(1).forEach(pdgNode -> { - Statement statement = (Statement) pdgNode.getAstNode(); - - if (statement.isExpressionStmt()) { - Expression expression = statement.asExpressionStmt().getExpression(); - - expression.findFirst(MethodCallExpr.class).ifPresent(methodCallExpr -> { - - }); - } else { - - } - }); - + context.setCurrentMethod(methodDeclaration); + // Build PDG and add to SDGGraph + 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); + } - sdg.addPDG(pdg, methodDeclaration); + // Add all arcs from PDG to SDG + for (Arc arc : pdg.edgeSet()) { + if (arc.isControlDependencyArc()) { + sdg.addControlDependencyArc(pdg.getEdgeSource(arc), pdg.getEdgeTarget(arc)); + } else { + sdg.addDataDependencyArc(pdg.getEdgeSource(arc), pdg.getEdgeTarget(arc), arc.getLabel()); + } + } - methodDeclaration.accept(this, ignored); + GraphNode methodDeclarationNode = pdg.getRootNode().get(); - pdgs.add(pdg); + // Add CFG + sdg.setMethodCFG(methodDeclaration, pdg.getCfg()); } @Override - public void visit(ClassOrInterfaceDeclaration classOrInterfaceDeclaration, Void ignored) { + public void visit(ClassOrInterfaceDeclaration classOrInterfaceDeclaration, Context context) { // if (sdgGraph.getRootNode() != null) { // throw new IllegalStateException("¡Solo podemos procesar una clase por el momento!"); // } @@ -143,15 +65,24 @@ class SDGBuilder extends VoidVisitorAdapter { throw new IllegalArgumentException("¡Las interfaces no estan permitidas!"); } - currentClass = classOrInterfaceDeclaration; + context.setCurrentClass(classOrInterfaceDeclaration); + + classOrInterfaceDeclaration.getMembers().accept(this, context); + + // 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(context); + + - classOrInterfaceDeclaration.accept(this, ignored); + // 3. Build summary arcs } @Override - public void visit(CompilationUnit compilationUnit, Void ignored) { - currentCompilationUnit = compilationUnit; + public void visit(CompilationUnit compilationUnit, Context context) { + context.setCurrentCU(compilationUnit); - super.visit(compilationUnit, ignored); + super.visit(compilationUnit, context); } } diff --git a/src/main/java/tfm/nodes/GraphNode.java b/src/main/java/tfm/nodes/GraphNode.java index cb642f16caa94fd9a52bfe3add8302e1c6eae68f..0a7295bd42abea92c9facccc115f1f403dd6f364 100644 --- a/src/main/java/tfm/nodes/GraphNode.java +++ b/src/main/java/tfm/nodes/GraphNode.java @@ -1,12 +1,11 @@ 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.type.NodeType; import tfm.utils.Utils; import tfm.variables.VariableExtractor; @@ -25,9 +24,13 @@ import java.util.Set; * It is immutable. * @param The type of the AST represented by this node. */ -public class GraphNode { +public class GraphNode implements Comparable> { - private final int id; + public static final NodeFactory DEFAULT_FACTORY = TypeNodeFactory.fromType(NodeType.STATEMENT); + + private final NodeType nodeType; + + private final long id; private final String instruction; private final N astNode; @@ -35,9 +38,10 @@ public class GraphNode { private final Set definedVariables; private final Set usedVariables; - GraphNode(int id, String instruction, @NotNull N astNode) { + GraphNode(long id, NodeType type, String instruction, @NotNull N astNode) { this( id, + type, instruction, astNode, Utils.emptySet(), @@ -49,7 +53,8 @@ public class GraphNode { } GraphNode( - int id, + long id, + NodeType type, String instruction, @NotNull N astNode, Collection declaredVariables, @@ -57,6 +62,7 @@ public class GraphNode { Collection usedVariables ) { this.id = id; + this.nodeType = type; this.instruction = instruction; this.astNode = astNode; @@ -73,13 +79,14 @@ public class GraphNode { .visit(node); } - public int getId() { + public long getId() { return id; } 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 +119,14 @@ public class GraphNode { 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() { @@ -136,4 +144,13 @@ public class GraphNode { public String getInstruction() { return instruction; } + + @Override + 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 new file mode 100644 index 0000000000000000000000000000000000000000..94e267b7e224fb7346d96cb3cc6bb00523b3ccc9 --- /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; + } + + static IdHelper getInstance() { + return INSTANCE; + } + + synchronized long getNextId() { + return nextId++; + } +} diff --git a/src/main/java/tfm/nodes/MethodCallNode.java b/src/main/java/tfm/nodes/MethodCallNode.java deleted file mode 100644 index 95f0479710c03c4a68fcec061424d82b8c9e6f57..0000000000000000000000000000000000000000 --- a/src/main/java/tfm/nodes/MethodCallNode.java +++ /dev/null @@ -1,52 +0,0 @@ -package tfm.nodes; - -import com.github.javaparser.ast.stmt.ExpressionStmt; -import org.checkerframework.checker.nullness.qual.NonNull; - -import java.util.ArrayList; -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.getId(), node.getInstruction(), node.getAstNode()); - - this.inParameters = new ArrayList<>(); - this.outParameters = new ArrayList<>(); - } - - 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, Set declaredVariables, Set definedVariables, Set usedVariables) { - super(id, representation, node, 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; - } -} diff --git a/src/main/java/tfm/nodes/NodeFactory.java b/src/main/java/tfm/nodes/NodeFactory.java index 7d4f96e5d1bbcb7c813bdf4e992521cc12584592..b54b2efb8a07d6978b34ca83a4c279cbff078f5a 100644 --- a/src/main/java/tfm/nodes/NodeFactory.java +++ b/src/main/java/tfm/nodes/NodeFactory.java @@ -1,17 +1,17 @@ package tfm.nodes; import com.github.javaparser.ast.Node; +import org.jetbrains.annotations.NotNull; +import tfm.nodes.GraphNode; import java.util.Collection; -import java.util.Objects; -public class NodeFactory { +public interface 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 @@ -20,51 +20,25 @@ public class NodeFactory { * @param the type of the AST node * @return a new GraphNode */ - public static GraphNode computedGraphNode( - int id, - String instruction, - ASTNode node, - Collection declaredVariables, - Collection definedVariables, - Collection usedVariables - ) { - Objects.requireNonNull(instruction, "Instruction cannot be null!"); - Objects.requireNonNull(node, "AST Node cannot be null"); - Objects.requireNonNull(declaredVariables, "declared variables collection cannot be null!"); - Objects.requireNonNull(definedVariables, "defined variables collection cannot be null"); - Objects.requireNonNull(usedVariables, "Used variables collection cannot be null!"); + GraphNode computedGraphNode( + @NotNull String instruction, + @NotNull ASTNode node, + @NotNull Collection declaredVariables, + @NotNull Collection definedVariables, + @NotNull Collection usedVariables + ); - return new GraphNode<>( - id, - instruction, - node, - declaredVariables, - definedVariables, - usedVariables - ); - } /** * Returns a GraphNode computing the declared, defined and used variables in its AST node * - * @param id the id of the node * @param instruction the instruction that represents * @param node the node of the AST that represents * @param the type of the AST node * @return a new GraphNode */ - public static GraphNode graphNode( - int id, - String instruction, - ASTNode node - ) { - Objects.requireNonNull(instruction, "Instruction cannot be null!"); - Objects.requireNonNull(node, "AST Node cannot be null"); - - return new GraphNode<>( - id, - instruction, - node - ); - } + public GraphNode graphNode( + @NotNull String instruction, + @NotNull ASTNode node + ); } diff --git a/src/main/java/tfm/nodes/TypeNodeFactory.java b/src/main/java/tfm/nodes/TypeNodeFactory.java new file mode 100644 index 0000000000000000000000000000000000000000..56a2bb98bf212084b779517bdeee4a09e71e0c5e --- /dev/null +++ b/src/main/java/tfm/nodes/TypeNodeFactory.java @@ -0,0 +1,56 @@ +package tfm.nodes; + +import com.github.javaparser.ast.Node; +import org.jetbrains.annotations.NotNull; +import tfm.nodes.type.NodeType; + +import java.util.Collection; +import java.util.Objects; + +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, + @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/type/NodeType.java b/src/main/java/tfm/nodes/type/NodeType.java new file mode 100644 index 0000000000000000000000000000000000000000..2cb3fba400a1839efaa476e1160a28e21db1c9fa --- /dev/null +++ b/src/main/java/tfm/nodes/type/NodeType.java @@ -0,0 +1,10 @@ +package tfm.nodes.type; + +public enum NodeType { + STATEMENT, + METHOD, + METHOD_CALL, + VARIABLE_IN, + VARIABLE_OUT, + METHOD_CALL_RETURN +} diff --git a/src/main/java/tfm/slicing/Slice.java b/src/main/java/tfm/slicing/Slice.java index 2a8b8443517c26e5e330e4a22089e0aaf2c9b38d..91563f30a02fb85421dc3da02d4d957e85ad9adb 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/main/java/tfm/utils/Context.java b/src/main/java/tfm/utils/Context.java index 85da3a4579ffcafb5848de6bef2691898d7d2d88..7d4e5eb3350c7984c32252cfa6d62d303395015c 100644 --- a/src/main/java/tfm/utils/Context.java +++ b/src/main/java/tfm/utils/Context.java @@ -4,6 +4,8 @@ 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; @@ -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 76540824accac6f58b7afc4075be6cc1d70d3b19..e621e298aec8cd67fbc07e5934fd33457e673af7 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() { @@ -9,11 +7,24 @@ 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.isEmpty() ? "" : String.format("[%s]: ", context), + message + ) + ); } public static void format(String message, Object... args) { diff --git a/src/main/java/tfm/utils/MethodDeclarationSolver.java b/src/main/java/tfm/utils/MethodDeclarationSolver.java new file mode 100644 index 0000000000000000000000000000000000000000..b6e7ebab3061ada67e5115dab52bc4bae6afad1a --- /dev/null +++ b/src/main/java/tfm/utils/MethodDeclarationSolver.java @@ -0,0 +1,48 @@ +package tfm.utils; + +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 java.util.*; + +public class MethodDeclarationSolver { + + private static final MethodDeclarationSolver instance = new MethodDeclarationSolver(); + private static final List usedTypeSolvers = new ArrayList<>(); + + private MethodDeclarationSolver() { + + } + + public static void addTypeSolvers(TypeSolver... typeSolvers) { + usedTypeSolvers.addAll(Arrays.asList(typeSolvers)); + } + + public static MethodDeclarationSolver getInstance() { + return instance; + } + + 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(combinedTypeSolver).solve(methodCallExpr); + + return solver.isSolved() + ? solver.getCorrespondingDeclaration().toAst() + : 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 1e4d91c06c234e381e49de3785af29f34542bcfe..fd2f639071f991b04a2a8c9c35427e09f7ee84cb 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/java/tfm/graphs/pdg/HandCraftedGraphs.java b/src/test/java/tfm/graphs/pdg/HandCraftedGraphs.java index 698bcc543c9e88d7f673415092513e2b2f3e55ba..d07ab93f5541fddb83b2d5c33eb4b961c5a38a89 100644 --- a/src/test/java/tfm/graphs/pdg/HandCraftedGraphs.java +++ b/src/test/java/tfm/graphs/pdg/HandCraftedGraphs.java @@ -12,12 +12,14 @@ import tfm.graphs.augmented.ACFG; import tfm.graphs.augmented.APDG; import tfm.graphs.augmented.PPDG; import tfm.nodes.GraphNode; +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")); + 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()); @@ -56,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")); + 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 607c17365c0fa98248c9e2423210685f427c3f5a..2f6e54797d6a4793bba0cb39f91d7c6803d475bf 100644 --- a/src/test/java/tfm/graphs/pdg/PDGTests.java +++ b/src/test/java/tfm/graphs/pdg/PDGTests.java @@ -16,6 +16,8 @@ import tfm.graphs.augmented.APDG; import tfm.graphs.augmented.PPDG; import tfm.graphs.cfg.CFG; import tfm.nodes.GraphNode; +import tfm.nodes.TypeNodeFactory; +import tfm.nodes.type.NodeType; import tfm.slicing.GraphNodeCriterion; import tfm.slicing.Slice; import tfm.slicing.SlicingCriterion; @@ -80,7 +82,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, TypeNodeFactory.fromType(NodeType.METHOD)); ctrlDepBuilder = new ControlDependencyBuilder(pdg, cfg); ctrlDepBuilder.analyze(); @@ -88,13 +90,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, TypeNodeFactory.fromType(NodeType.METHOD)); ctrlDepBuilder = new ControlDependencyBuilder(apdg, acfg); ctrlDepBuilder.analyze(); // Create PPDG PPDG ppdg = new PPDG(acfg); - ppdg.buildRootNode("ENTER " + methodName, root); + ppdg.buildRootNode("ENTER " + methodName, root, TypeNodeFactory.fromType(NodeType.METHOD)); ctrlDepBuilder = new ControlDependencyBuilder(ppdg, acfg); ctrlDepBuilder.analyze(); @@ -133,7 +135,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 +178,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) diff --git a/src/test/res/programs/sdg/Example1.java b/src/test/res/programs/sdg/Example1.java index 400f8fd79cab0c6b36e218074bc8582b7dbeaa85..44fe6ee60f257481a63cdcb2ed93520dd23f6aa7 100644 --- a/src/test/res/programs/sdg/Example1.java +++ b/src/test/res/programs/sdg/Example1.java @@ -4,17 +4,30 @@ import tfm.utils.Logger; public class Example1 { + /* public 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(); - int f = sum(x, y); + int f = sum(sum(n1, n2), n2); Logger.log(f); + Logger.log(z); + } + + public int getNum() { + return num; } private static int sum(int x, int y) { @@ -22,6 +35,7 @@ public class Example1 { return res; } + /* public int m1() { return 1; } @@ -29,4 +43,5 @@ public class Example1 { public int m2() { return m1(); } + */ } diff --git a/src/test/res/programs/sdg/Example2.java b/src/test/res/programs/sdg/Example2.java new file mode 100644 index 0000000000000000000000000000000000000000..d92a97885f420ac85d50aa35f509822f505294c4 --- /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