From 5dc18b6ee2f3206030500830b5804477aa6a660b Mon Sep 17 00:00:00 2001 From: Carlos Galindo Date: Wed, 5 May 2021 20:03:59 +0200 Subject: [PATCH] added polymorphic calls * Call targets are computed based on static types (same as variable action's dynamic types). * Usage and definition finders now deal with accumulating information in the same variable action ('-arg-in-' or '-scope-in-'). That variable action is created in the VariableVisitor, and the transference is prepared at the end of the finder, instead of with each handleActualAction. * ObjectTree: pattern now accepts any variable, including fake patterns like ('-root-' or '-arg-in-') * Tests updated --- .../es/upv/mist/slicing/graphs/CallGraph.java | 2 +- .../graphs/ExpressionObjectTreeFinder.java | 3 +- .../sdg/InterproceduralDefinitionFinder.java | 49 ++++++++-- .../sdg/InterproceduralUsageFinder.java | 91 ++++++++++++++----- .../es/upv/mist/slicing/nodes/ObjectTree.java | 5 +- .../mist/slicing/nodes/VariableVisitor.java | 8 ++ .../mist/slicing/nodes/io/ActualIONode.java | 12 +-- .../es/upv/mist/slicing/utils/ASTUtils.java | 14 +++ .../dinsa-tests/Josep2.java.sdg.sliced | 4 + .../review-07-2020/P5.java.sdg.sliced | 4 + 10 files changed, 143 insertions(+), 49 deletions(-) diff --git a/sdg-core/src/main/java/es/upv/mist/slicing/graphs/CallGraph.java b/sdg-core/src/main/java/es/upv/mist/slicing/graphs/CallGraph.java index d01113b..f37ab25 100644 --- a/sdg-core/src/main/java/es/upv/mist/slicing/graphs/CallGraph.java +++ b/sdg-core/src/main/java/es/upv/mist/slicing/graphs/CallGraph.java @@ -167,7 +167,7 @@ public class CallGraph extends DirectedPseudograph createNormalEdge(decl, n)); + n.resolve().toAst().ifPresent(decl -> createPolyEdges(decl, n)); super.visit(n, arg); } diff --git a/sdg-core/src/main/java/es/upv/mist/slicing/graphs/ExpressionObjectTreeFinder.java b/sdg-core/src/main/java/es/upv/mist/slicing/graphs/ExpressionObjectTreeFinder.java index 456f2b4..3fb8c66 100644 --- a/sdg-core/src/main/java/es/upv/mist/slicing/graphs/ExpressionObjectTreeFinder.java +++ b/sdg-core/src/main/java/es/upv/mist/slicing/graphs/ExpressionObjectTreeFinder.java @@ -1,6 +1,5 @@ package es.upv.mist.slicing.graphs; -import com.github.javaparser.ast.Node; import com.github.javaparser.ast.body.VariableDeclarator; import com.github.javaparser.ast.expr.*; import com.github.javaparser.ast.visitor.VoidVisitorAdapter; @@ -203,7 +202,7 @@ public class ExpressionObjectTreeFinder { for (VariableAction variableAction : graphNode.getVariableActions()) { if (variableAction instanceof VariableAction.CallMarker) { VariableAction.CallMarker marker = (VariableAction.CallMarker) variableAction; - if (ASTUtils.equalsWithRange((Node) marker.getCall(), (Node) call) && !marker.isEnter()) { + if (ASTUtils.equalsWithRange(marker.getCall(), call) && !marker.isEnter()) { assert lastUseOut != null; list.add(new Pair<>(lastUseOut, arg)); return; diff --git a/sdg-core/src/main/java/es/upv/mist/slicing/graphs/sdg/InterproceduralDefinitionFinder.java b/sdg-core/src/main/java/es/upv/mist/slicing/graphs/sdg/InterproceduralDefinitionFinder.java index bbfd848..afef587 100644 --- a/sdg-core/src/main/java/es/upv/mist/slicing/graphs/sdg/InterproceduralDefinitionFinder.java +++ b/sdg-core/src/main/java/es/upv/mist/slicing/graphs/sdg/InterproceduralDefinitionFinder.java @@ -44,21 +44,28 @@ public class InterproceduralDefinitionFinder extends InterproceduralActionFinder Optional arg = extractArgument(def, edge, false); if (arg.isEmpty()) return; - ActualIONode actualOut = ActualIONode.createActualOut(edge.getCall(), def.getName(), arg.get()); + ActualIONode actualOut = locateActualOutNode(edge, def.getName()) + .orElseGet(() -> ActualIONode.createActualOut(edge.getCall(), def.getName(), arg.get())); extractOutputVariablesAsMovables(arg.get(), movables, graphNode, actualOut, def); } else if (def.isField()) { if (def.isStatic()) { // Known limitation: static fields } else { assert !(edge.getCall() instanceof ObjectCreationExpr); - ActualIONode actualOut = ActualIONode.createActualOut(edge.getCall(), def.getName(), null); + ActualIONode actualOut = locateActualOutNode(edge, def.getName()) + .orElseGet(() -> ActualIONode.createActualOut(edge.getCall(), def.getName(), null)); Optional scope = ASTUtils.getResolvableScope(edge.getCall()); if (scope.isPresent()) { extractOutputVariablesAsMovables(scope.get(), movables, graphNode, actualOut, def); } else { assert def.hasObjectTree(); - var movableDef = new Definition(DeclarationType.FIELD, "this", graphNode, (ObjectTree) def.getObjectTree().clone()); - movables.add(new Movable(movableDef, actualOut)); + Optional optVA = locateDefinition(graphNode, "this"); + if (optVA.isPresent()) + optVA.get().getObjectTree().addAll(def.getObjectTree()); + else { + var movableDef = new Definition(DeclarationType.FIELD, "this", graphNode, (ObjectTree) def.getObjectTree().clone()); + movables.add(new Movable(movableDef, actualOut)); + } } } } else { @@ -75,14 +82,38 @@ public class InterproceduralDefinitionFinder extends InterproceduralActionFinder e.accept(new OutNodeVariableVisitor(), defExpressions); for (Expression expression : defExpressions) { assert def.hasObjectTree(); - DeclarationType type = DeclarationType.valueOf(expression); - Definition inner = new Definition(type, expression.toString(), graphNode, (ObjectTree) def.getObjectTree().clone()); - if (defExpressions.size() > 1) - inner.setOptional(true); - movables.add(new Movable(inner, actualOut)); + Optional optVa = locateDefinition(graphNode, expression.toString()); + if (optVa.isPresent()) { + optVa.get().getObjectTree().addAll(def.getObjectTree()); + } else { + DeclarationType type = DeclarationType.valueOf(expression); + Definition inner = new Definition(type, expression.toString(), graphNode, (ObjectTree) def.getObjectTree().clone()); + if (defExpressions.size() > 1) + inner.setOptional(true); + movables.add(new Movable(inner, actualOut)); + } } } + /** Find the actual out node in the given edge call that corresponds to the given variable name. */ + protected Optional locateActualOutNode(CallGraph.Edge edge, String name) { + return edge.getGraphNode().getSyntheticNodesInMovables().stream() + .filter(ActualIONode.class::isInstance) + .map(ActualIONode.class::cast) + .filter(ActualIONode::isOutput) + .filter(actual -> actual.getVariableName().equals(name)) + .filter(actual -> ASTUtils.equalsWithRange(actual.getAstNode(), edge.getCall())) + .findFirst(); + } + + /** Try to locate the definition for the given variable name in the given node. */ + protected Optional locateDefinition(GraphNode graphNode, String name) { + return graphNode.getVariableActions().stream() + .filter(va -> va.getName().equals(name)) + .filter(VariableAction::isDefinition) + .findAny(); + } + @Override protected Stream mapAndFilterActionStream(Stream stream, CFG cfg) { return stream.filter(VariableAction::isDefinition) diff --git a/sdg-core/src/main/java/es/upv/mist/slicing/graphs/sdg/InterproceduralUsageFinder.java b/sdg-core/src/main/java/es/upv/mist/slicing/graphs/sdg/InterproceduralUsageFinder.java index e429cc0..ad1e384 100644 --- a/sdg-core/src/main/java/es/upv/mist/slicing/graphs/sdg/InterproceduralUsageFinder.java +++ b/sdg-core/src/main/java/es/upv/mist/slicing/graphs/sdg/InterproceduralUsageFinder.java @@ -1,17 +1,16 @@ package es.upv.mist.slicing.graphs.sdg; -import com.github.javaparser.ast.Node; import com.github.javaparser.ast.body.CallableDeclaration; import com.github.javaparser.ast.expr.Expression; import com.github.javaparser.ast.expr.ObjectCreationExpr; import com.github.javaparser.ast.expr.ThisExpr; +import com.github.javaparser.resolution.Resolvable; +import com.github.javaparser.resolution.declarations.ResolvedMethodLikeDeclaration; import es.upv.mist.slicing.graphs.CallGraph; import es.upv.mist.slicing.graphs.ExpressionObjectTreeFinder; import es.upv.mist.slicing.graphs.cfg.CFG; import es.upv.mist.slicing.nodes.GraphNode; -import es.upv.mist.slicing.nodes.ObjectTree; import es.upv.mist.slicing.nodes.VariableAction; -import es.upv.mist.slicing.nodes.VariableAction.Definition; import es.upv.mist.slicing.nodes.VariableAction.Movable; import es.upv.mist.slicing.nodes.VariableAction.Usage; import es.upv.mist.slicing.nodes.io.ActualIONode; @@ -20,7 +19,9 @@ import es.upv.mist.slicing.utils.ASTUtils; import java.util.Map; import java.util.Objects; +import java.util.Set; import java.util.function.Predicate; +import java.util.stream.Collectors; import java.util.stream.Stream; /** An interprocedural usage finder, which adds the associated actions to formal and actual nodes in the CFGs. */ @@ -29,6 +30,32 @@ public class InterproceduralUsageFinder extends InterproceduralActionFinder edge : graph.edgeSet()) { + for (ActualIONode actualIn : locateActualInNode(edge)) { + for (VariableAction va : edge.getGraphNode().getVariableActions()) { + if (va instanceof Movable && ((Movable) va).getRealNode().equals(actualIn)) { + ExpressionObjectTreeFinder finder = new ExpressionObjectTreeFinder(edge.getGraphNode()); + if (va.getName().equals("-scope-in-")) { + Expression scope = Objects.requireNonNullElseGet(actualIn.getArgument(), ThisExpr::new); + finder.locateAndMarkTransferenceToRoot(scope, va); + } else if (va.getName().equals("-arg-in-")) { + finder.locateAndMarkTransferenceToRoot(actualIn.getArgument(), va); + } + } + } + } + } + } + @Override protected void handleFormalAction(CallGraph.Vertex vertex, Usage use) { CFG cfg = cfgMap.get(vertex.getDeclaration()); @@ -43,12 +70,9 @@ public class InterproceduralUsageFinder extends InterproceduralActionFinder edge, String name) { + /** Find all actual in nodes in the given call. */ + protected Set locateActualInNode(CallGraph.Edge edge) { return edge.getGraphNode().getSyntheticNodesInMovables().stream() .filter(ActualIONode.class::isInstance) .map(ActualIONode.class::cast) .filter(ActualIONode::isInput) - .filter(actual -> actual.getVariableName().equals(name)) - .filter(actual -> ASTUtils.equalsWithRange(actual.getAstNode(), (Node) edge.getCall())) - .findFirst() - .orElseThrow(() -> new IllegalStateException("can't locate actual-in node")); + .filter(actual -> ASTUtils.equalsWithRange(actual.getAstNode(), edge.getCall())) + .collect(Collectors.toSet()); + } + + /** Find the -arg-in- variable action that corresponds to the given node, call and index. */ + protected VariableAction locateArgIn(GraphNode graphNode, Resolvable call, int index) { + return locateActionIn(graphNode, call, index, "-arg-in-"); + } + + /** Find the -scope-in- variable action that corresponds to the given node and call. */ + protected VariableAction locateScopeIn(GraphNode graphNode, Resolvable call) { + return locateActionIn(graphNode, call, 0, "-scope-in-"); + } + + /** Find the nth variable action from the given node and call that matches the given name. 0 represents the first occurrence. */ + protected VariableAction locateActionIn(GraphNode graphNode, Resolvable call, int index, String actionName) { + boolean inCall = false; + for (VariableAction va : graphNode.getVariableActions()) { + if (va instanceof VariableAction.CallMarker && ASTUtils.equalsWithRange(((VariableAction.CallMarker) va).getCall(), call)) { + if (((VariableAction.CallMarker) va).isEnter()) + inCall = true; + else + break; // The call has ended, can't find the action now + } + if (inCall && va.isDefinition() && va.getName().equals(actionName)) { + if (index == 0) + return va; + else + index--; + } + } + throw new IllegalStateException("Could not locate " + actionName + " for call " + call + " in node " + graphNode); } @Override diff --git a/sdg-core/src/main/java/es/upv/mist/slicing/nodes/ObjectTree.java b/sdg-core/src/main/java/es/upv/mist/slicing/nodes/ObjectTree.java index 72d67b6..fee6599 100644 --- a/sdg-core/src/main/java/es/upv/mist/slicing/nodes/ObjectTree.java +++ b/sdg-core/src/main/java/es/upv/mist/slicing/nodes/ObjectTree.java @@ -11,9 +11,6 @@ import java.util.regex.Pattern; import java.util.stream.Collectors; import java.util.stream.Stream; -import static es.upv.mist.slicing.graphs.cfg.CFGBuilder.VARIABLE_NAME_OUTPUT; -import static es.upv.mist.slicing.graphs.exceptionsensitive.ESCFG.ACTIVE_EXCEPTION_VARIABLE; - /** * A tree data structure that mimics the tree found in an object's fields. * Each tree contains a MemberNode that represents its, including a name. @@ -29,7 +26,7 @@ public class ObjectTree implements Cloneable { public static final String ROOT_NAME = "-root-"; /** Regex pattern to split the root from the fields of a field access expression. */ - private static final Pattern FIELD_SPLIT = Pattern.compile("^(?(([_0-9A-Za-z]+\\.)*this)|([_0-9A-Za-z]+)|(" + ROOT_NAME + ")|(" + VARIABLE_NAME_OUTPUT + ")|(" + ACTIVE_EXCEPTION_VARIABLE + "))(\\.(?.+))?$"); + private static final Pattern FIELD_SPLIT = Pattern.compile("^(?(([_0-9A-Za-z]+\\.)*this)|(?(-?))([_0-9A-Za-z]+\\k)+)(\\.(?.+))?$"); /** Direct children of this tree node, mapped by field name. */ private final Map childrenMap = new HashMap<>(); diff --git a/sdg-core/src/main/java/es/upv/mist/slicing/nodes/VariableVisitor.java b/sdg-core/src/main/java/es/upv/mist/slicing/nodes/VariableVisitor.java index 66fd51a..370649b 100644 --- a/sdg-core/src/main/java/es/upv/mist/slicing/nodes/VariableVisitor.java +++ b/sdg-core/src/main/java/es/upv/mist/slicing/nodes/VariableVisitor.java @@ -475,6 +475,10 @@ public class VariableVisitor extends GraphNodeContentVisitor { // 2. Our variables must match (type + name) && Objects.equals(variableName, o.variableName) // 3. in matches in, out matches out - && isInput() == o.isInput() - // 4. The method call must resolve to the method declaration of the argument. - && Objects.equals(o.getAstNode(), resolvedASTNode()); - } - - @SuppressWarnings("unchecked") - protected BodyDeclaration resolvedASTNode() { - return ASTUtils.getResolvedAST(((Resolvable) astNode).resolve()) - .orElse(null); + && isInput() == o.isInput(); } @Override diff --git a/sdg-core/src/main/java/es/upv/mist/slicing/utils/ASTUtils.java b/sdg-core/src/main/java/es/upv/mist/slicing/utils/ASTUtils.java index f6ac191..b84f838 100644 --- a/sdg-core/src/main/java/es/upv/mist/slicing/utils/ASTUtils.java +++ b/sdg-core/src/main/java/es/upv/mist/slicing/utils/ASTUtils.java @@ -43,12 +43,26 @@ public class ASTUtils { return n1.equals(n2) && equalsWithRange(d1, d2); } + /** @see #equalsWithRange(Node, Node) */ + public static boolean equalsWithRange(Resolvable n1, Resolvable n2) { + return equalsWithRange((Node) n1, (Node) n2); + } + + /** @see #equalsWithRange(Node, Node) */ + public static boolean equalsWithRange(Node n1, Resolvable n2) { + return equalsWithRange(n1, (Node) n2); + } + + /** Compares two JavaParser nodes and their ranges (position in the file). If you need to compare between nodes + * from different files, you may want to use {@link #equalsWithRangeInCU(Node, Node)} */ public static boolean equalsWithRange(Node n1, Node n2) { if (n1 == null || n2 == null) return n1 == n2; return Objects.equals(n1.getRange(), n2.getRange()) && Objects.equals(n1, n2); } + /** Compares two JavaParser nodes, their ranges (position in the file) and compilation units (file they're in). + * If the nodes belong to the same CU or have no CU, you can use {@link #equalsWithRange(Node, Node)}*/ public static boolean equalsWithRangeInCU(Node n1, Node n2) { if (n1 == null || n2 == null) return n1 == n2; diff --git a/sdg-core/src/test/res/regression/dinsa-tests/Josep2.java.sdg.sliced b/sdg-core/src/test/res/regression/dinsa-tests/Josep2.java.sdg.sliced index 859cc9d..a445f17 100644 --- a/sdg-core/src/test/res/regression/dinsa-tests/Josep2.java.sdg.sliced +++ b/sdg-core/src/test/res/regression/dinsa-tests/Josep2.java.sdg.sliced @@ -33,4 +33,8 @@ class GrandesNumeros extends Numeros { GrandesNumeros(double x) { haceFalta = 0; } + + int random() { + return haceFalta; + } } diff --git a/sdg-core/src/test/res/regression/review-07-2020/P5.java.sdg.sliced b/sdg-core/src/test/res/regression/review-07-2020/P5.java.sdg.sliced index 859cc9d..a445f17 100644 --- a/sdg-core/src/test/res/regression/review-07-2020/P5.java.sdg.sliced +++ b/sdg-core/src/test/res/regression/review-07-2020/P5.java.sdg.sliced @@ -33,4 +33,8 @@ class GrandesNumeros extends Numeros { GrandesNumeros(double x) { haceFalta = 0; } + + int random() { + return haceFalta; + } } -- GitLab