From 17c92bef6fd80c7a303757a194d8299fe1a67871 Mon Sep 17 00:00:00 2001 From: Carlos Galindo Date: Mon, 1 Mar 2021 17:51:35 +0100 Subject: [PATCH 01/60] InterproceduralActionFinders structure altered, can merge trees * IAF now use a map of VariableAction instead of a set of StoredAction. * They contain a map with stored actions. * In their state, they accumulate the tree of repeated actions. --- .../es/upv/mist/slicing/graphs/CallGraph.java | 2 +- .../sdg/InterproceduralActionFinder.java | 90 +++++++++---------- .../sdg/InterproceduralDefinitionFinder.java | 34 ++++--- .../sdg/InterproceduralUsageFinder.java | 39 +++++--- .../mist/slicing/nodes/VariableAction.java | 81 +++++++++++++---- 5 files changed, 157 insertions(+), 89 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 3d1a880..d15c98e 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 @@ -275,7 +275,7 @@ public class CallGraph extends DirectedPseudograph The action to be searched for */ -public abstract class InterproceduralActionFinder extends BackwardDataFlowAnalysis, Set>> { +public abstract class InterproceduralActionFinder extends BackwardDataFlowAnalysis, Map> { protected final Map, CFG> cfgMap; + /** A map from vertex and action to its corresponding stored action, to avoid generating duplicate nodes. */ + protected final Map> actionStoredMap = new HashMap<>(); public InterproceduralActionFinder(CallGraph callGraph, Map, CFG> cfgMap) { super(callGraph); @@ -49,18 +51,26 @@ public abstract class InterproceduralActionFinder exte graph.vertexSet().forEach(this::saveDeclaration); } + /** Obtains the StoredAction object with information on which actions have been stored. */ + protected StoredAction getStored(CallGraph.Vertex vertex, A action) { + return actionStoredMap.get(vertex).get(action); + } + /** Save the current set of actions associated to the given declaration. It will avoid saving * duplicates by default, so this method may be called multiple times safely. */ protected void saveDeclaration(CallGraph.Vertex vertex) { - Set> storedActions = vertexDataMap.get(vertex); - + var actions = vertexDataMap.get(vertex); + // Update stored action map + actionStoredMap.computeIfAbsent(vertex, v -> new HashMap<>()); + for (A a : actions.keySet()) + actionStoredMap.get(vertex).computeIfAbsent(a, __ -> new StoredAction()); // FORMAL: per declaration (1) - for (StoredAction sa : storedActions) - sa.storeFormal(a -> sandBoxedHandler(vertex.getDeclaration(), a, this::handleFormalAction)); + for (A a : actions.keySet()) + getStored(vertex, a).storeFormal(() -> sandBoxedHandler(vertex, a, this::handleFormalAction)); // ACTUAL: per call (n) for (CallGraph.Edge edge : graph.incomingEdgesOf(vertex)) - storedActions.stream().sorted(new ParameterFieldSorter(edge)) - .forEach(sa -> sa.storeActual(edge, (e, a) -> sandBoxedHandler(e, a, this::handleActualAction))); + actions.keySet().stream().sorted(new ParameterFieldSorter(edge)).forEach(a -> + getStored(vertex, a).storeActual(edge, e -> sandBoxedHandler(e, a, this::handleActualAction))); } /** A sandbox to avoid resolution errors when a variable is included that is a class name @@ -74,7 +84,7 @@ public abstract class InterproceduralActionFinder exte } /** Generate the formal node(s) related to this action and declaration. */ - protected abstract void handleFormalAction(CallableDeclaration declaration, A action); + protected abstract void handleFormalAction(CallGraph.Vertex vertex, A action); /** Generate the actual node(s) related to this action and call. */ protected abstract void handleActualAction(CallGraph.Edge edge, A action); @@ -147,19 +157,19 @@ public abstract class InterproceduralActionFinder exte // =========================================================== @Override - protected Set> compute(CallGraph.Vertex vertex, Set predecessors) { + protected Map compute(CallGraph.Vertex vertex, Set predecessors) { saveDeclaration(vertex); - Set> newValue = new HashSet<>(vertexDataMap.get(vertex)); - newValue.addAll(initialValue(vertex)); + Map newValue = new HashMap<>(vertexDataMap.get(vertex)); + newValue.putAll(initialValue(vertex)); return newValue; } @Override - protected Set> initialValue(CallGraph.Vertex vertex) { + protected Map initialValue(CallGraph.Vertex vertex) { CFG cfg = cfgMap.get(vertex.getDeclaration()); if (cfg == null) - return Collections.emptySet(); - Stream stream = cfg.vertexSet().stream() + return Collections.emptyMap(); + Stream actionStream = cfg.vertexSet().stream() // Ignore root node, it is literally the entrypoint for interprocedural actions. .filter(n -> n != cfg.getRootNode()) .flatMap(n -> n.getVariableActions().stream()) @@ -167,9 +177,16 @@ public abstract class InterproceduralActionFinder exte .filter(Predicate.not(VariableAction::isSynthetic)) // We skip over non-root variables (for each 'x.a' action we'll find 'x' later) .filter(VariableAction::isRootAction); - return mapAndFilterActionStream(stream, cfg) - .map(StoredAction::new) - .collect(Collectors.toSet()); + Stream filteredStream = mapAndFilterActionStream(actionStream, cfg); + Map map = new HashMap<>(); + for (Iterator it = filteredStream.iterator(); it.hasNext(); ) { + A a = it.next(); + if (map.containsKey(a)) + map.get(a).addAll(a.getObjectTree()); + else + map.put(a, (ObjectTree) a.getObjectTree().clone()); + } + return map; } /** Given a stream of VariableAction objects, map it to the finders' type and @@ -183,19 +200,19 @@ public abstract class InterproceduralActionFinder exte /** A comparator to sort parameters and fields in the generation of actual nodes. It will sort * {@link StoredAction}s in the following order: fields, then parameters by descending index number. * The actual nodes will be generated in that order and inserted in reverse order in the graph node. */ - private class ParameterFieldSorter implements Comparator> { + private class ParameterFieldSorter implements Comparator { protected final CallGraph.Edge edge; public ParameterFieldSorter(CallGraph.Edge edge) { this.edge = edge; } @Override - public int compare(StoredAction o1, StoredAction o2) { + public int compare(A o1, A o2) { ResolvedValueDeclaration r1 = null; ResolvedValueDeclaration r2 = null; try { - r1 = o1.getAction().getResolvedValueDeclaration(); - r2 = o2.getAction().getResolvedValueDeclaration(); + r1 = o1.getResolvedValueDeclaration(); + r2 = o2.getResolvedValueDeclaration(); if (r1.isParameter() && r2.isParameter()) return -Integer.compare(ASTUtils.getMatchingParameterIndex(graph.getEdgeTarget(edge).getDeclaration(), r1.asParameter()), ASTUtils.getMatchingParameterIndex(graph.getEdgeTarget(edge).getDeclaration(), r2.asParameter())); @@ -219,47 +236,30 @@ public abstract class InterproceduralActionFinder exte /** A wrapper around a variable action, which keeps track of whether formal and actual nodes * have been saved to the graph or not. */ - protected static class StoredAction { - protected final A action; + protected static class StoredAction { /** Whether the action has been saved as actual node for each call. */ private final Map, Boolean> actualStoredMap = new HashMap<>(); /** Whether the action has been saved as formal node. */ protected boolean formalStored = false; - private StoredAction(A action) { - this.action = action; - } - - public A getAction() { - return action; - } + private StoredAction() {} /** If this action has not yet been saved as formal node, use the argument to do so, then mark it as stored. */ - private void storeFormal(Consumer save) { + private void storeFormal(Runnable save) { if (!formalStored) { - save.accept(action); + save.run(); formalStored = true; } } /** If this action has not yet been saved as actual node for the given edge, * use the consumer to do so, then mark it as stored. */ - private void storeActual(CallGraph.Edge edge, BiConsumer, A> save) { + private void storeActual(CallGraph.Edge edge, Consumer> save) { if (!actualStoredMap.getOrDefault(edge, false)) { - save.accept(edge, action); + save.accept(edge); actualStoredMap.put(edge, true); } } - - @Override - public int hashCode() { - return Objects.hash(action); - } - - @Override - public boolean equals(Object obj) { - return obj instanceof StoredAction && Objects.equals(action, ((StoredAction) obj).action); - } } } 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 b9ca771..8840ba4 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 @@ -9,6 +9,9 @@ import es.upv.mist.slicing.graphs.CallGraph; import es.upv.mist.slicing.graphs.cfg.CFG; import es.upv.mist.slicing.nodes.GraphNode; 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.ObjectTree; import es.upv.mist.slicing.nodes.io.ActualIONode; import es.upv.mist.slicing.nodes.io.FormalIONode; @@ -16,28 +19,31 @@ import java.util.*; import java.util.stream.Stream; /** An interprocedural definition finder, which adds the associated actions to formal and actual nodes in the CFGs. */ -public class InterproceduralDefinitionFinder extends InterproceduralActionFinder { +public class InterproceduralDefinitionFinder extends InterproceduralActionFinder { public InterproceduralDefinitionFinder(CallGraph callGraph, Map, CFG> cfgMap) { super(callGraph, cfgMap); } @Override - protected void handleFormalAction(CallableDeclaration declaration, VariableAction.Definition def) { - CFG cfg = cfgMap.get(declaration); + protected void handleFormalAction(CallGraph.Vertex vertex, Definition def) { + CFG cfg = cfgMap.get(vertex.getDeclaration()); ResolvedValueDeclaration resolved = def.getResolvedValueDeclaration(); + ObjectTree objTree = vertexDataMap.get(vertex).get(def); if (!resolved.isParameter() || !resolved.getType().isPrimitive()) { - FormalIONode formalOut = FormalIONode.createFormalOut(declaration, resolved); - cfg.getExitNode().addMovableVariable(new VariableAction.Movable(def.toUsage(cfg.getExitNode()), formalOut)); + FormalIONode formalOut = FormalIONode.createFormalOut(vertex.getDeclaration(), resolved); + Movable movable = new Movable(def.toUsage(cfg.getExitNode(), objTree), formalOut); + cfg.getExitNode().addMovableVariable(movable); } - FormalIONode formalIn = FormalIONode.createFormalInDecl(declaration, resolved); - cfg.getRootNode().addMovableVariable(new VariableAction.Movable(def.toDeclaration(cfg.getRootNode()), formalIn)); + FormalIONode formalIn = FormalIONode.createFormalInDecl(vertex.getDeclaration(), resolved); + cfg.getRootNode().addMovableVariable(new Movable(def.toDeclaration(cfg.getRootNode(), objTree), formalIn)); } @Override - protected void handleActualAction(CallGraph.Edge edge, VariableAction.Definition def) { - List movables = new LinkedList<>(); + protected void handleActualAction(CallGraph.Edge edge, Definition def) { + List movables = new LinkedList<>(); GraphNode graphNode = edge.getGraphNode(); ResolvedValueDeclaration resolved = def.getResolvedValueDeclaration(); + ObjectTree objTree = vertexDataMap.get(graph.getEdgeTarget(edge)).get(def); if (resolved.isParameter()) { Expression arg = extractArgument(resolved.asParameter(), edge, false); if (arg == null) @@ -47,9 +53,9 @@ public class InterproceduralDefinitionFinder extends InterproceduralActionFinder Set exprSet = new HashSet<>(); arg.accept(new OutNodeVariableVisitor(), exprSet); for (NameExpr nameExpr : exprSet) - movables.add(new VariableAction.Movable(new VariableAction.Definition(nameExpr, nameExpr.toString(), graphNode), actualOut)); + movables.add(new Movable(new Definition(nameExpr, nameExpr.toString(), graphNode, objTree), actualOut)); } else { - movables.add(new VariableAction.Movable(def.toDefinition(graphNode), actualOut)); + movables.add(new Movable(def.toDefinition(graphNode, objTree), actualOut)); } } else if (resolved.isField()) { // Known limitation: static fields @@ -59,8 +65,8 @@ public class InterproceduralDefinitionFinder extends InterproceduralActionFinder return; String aliasedName = obtainAliasedFieldName(def, edge); ActualIONode actualOut = ActualIONode.createActualOut(edge.getCall(), resolved, null); - var movableDef = new VariableAction.Definition(obtainScope(edge.getCall()), aliasedName, graphNode, null); - movables.add(new VariableAction.Movable(movableDef, actualOut)); + var movableDef = new Definition(obtainScope(edge.getCall()), aliasedName, graphNode, objTree); + movables.add(new Movable(movableDef, actualOut)); } else { throw new IllegalStateException("Definition must be either from a parameter or a field!"); } @@ -68,7 +74,7 @@ public class InterproceduralDefinitionFinder extends InterproceduralActionFinder } @Override - protected Stream mapAndFilterActionStream(Stream stream, CFG cfg) { + protected Stream mapAndFilterActionStream(Stream stream, CFG cfg) { return stream.filter(VariableAction::isDefinition) .map(VariableAction::asDefinition) .filter(def -> cfg.findDeclarationFor(def).isEmpty()); 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 979200f..401d10a 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 @@ -8,6 +8,7 @@ import es.upv.mist.slicing.graphs.CallGraph; import es.upv.mist.slicing.graphs.cfg.CFG; import es.upv.mist.slicing.nodes.GraphNode; import es.upv.mist.slicing.nodes.VariableAction; +import es.upv.mist.slicing.nodes.VariableAction.*; import es.upv.mist.slicing.nodes.VariableVisitor; import es.upv.mist.slicing.nodes.io.ActualIONode; import es.upv.mist.slicing.nodes.io.FormalIONode; @@ -19,32 +20,44 @@ import java.util.function.Predicate; import java.util.stream.Stream; /** An interprocedural usage finder, which adds the associated actions to formal and actual nodes in the CFGs. */ -public class InterproceduralUsageFinder extends InterproceduralActionFinder { +public class InterproceduralUsageFinder extends InterproceduralActionFinder { public InterproceduralUsageFinder(CallGraph callGraph, Map, CFG> cfgMap) { super(callGraph, cfgMap); } @Override - protected void handleFormalAction(CallableDeclaration declaration, VariableAction.Usage use) { - CFG cfg = cfgMap.get(declaration); + protected void handleFormalAction(CallGraph.Vertex vertex, Usage use) { + CFG cfg = cfgMap.get(vertex.getDeclaration()); ResolvedValueDeclaration resolved = use.getResolvedValueDeclaration(); - FormalIONode formalIn = FormalIONode.createFormalIn(declaration, resolved); - cfg.getRootNode().addMovableVariable(new VariableAction.Movable(use.toDefinition(cfg.getRootNode()), formalIn)); + FormalIONode formalIn = FormalIONode.createFormalIn(vertex.getDeclaration(), resolved); + ObjectTree objTree = vertexDataMap.get(vertex).get(use); + Movable movable = new Movable(use.toDefinition(cfg.getRootNode(), objTree), formalIn); + cfg.getRootNode().addMovableVariable(movable); } @Override - protected void handleActualAction(CallGraph.Edge edge, VariableAction.Usage use) { - List movables = new LinkedList<>(); + protected void handleActualAction(CallGraph.Edge edge, Usage use) { + List movables = new LinkedList<>(); GraphNode graphNode = edge.getGraphNode(); ResolvedValueDeclaration resolved = use.getResolvedValueDeclaration(); + ObjectTree objTree = vertexDataMap.get(graph.getEdgeTarget(edge)).get(use); if (resolved.isParameter()) { Expression argument = extractArgument(resolved.asParameter(), edge, true); ActualIONode actualIn = ActualIONode.createActualIn(edge.getCall(), resolved, argument); argument.accept(new VariableVisitor( - (n, exp, name) -> movables.add(new VariableAction.Movable(new VariableAction.Declaration(exp, name, graphNode), actualIn)), - (n, exp, name, expression) -> movables.add(new VariableAction.Movable(new VariableAction.Definition(exp, name, graphNode, expression), actualIn)), - (n, exp, name) -> movables.add(new VariableAction.Movable(new VariableAction.Usage(exp, name, graphNode), actualIn)) + (n, exp, name) -> movables.add(new Movable(new Declaration(exp, name, graphNode), actualIn)), + (n, exp, name, expression) -> movables.add(new Movable(new Definition(exp, name, graphNode, expression), actualIn)), + (n, exp, name) -> movables.add(new Movable(new Usage(exp, name, graphNode), actualIn)) ), VariableVisitor.Action.USE); + // a) es un objeto: movables==1 ('a', 'this') + // b) es una combinacion otras cosas ('a[1]', 'a.call()', construccion de string) + // void f(A a) { log(a.x); } <-- f(theA); // se copia el arbol de log(a) a f(theA) :D + // void f(A a) { log(a.x); } <-- f(as[1]); // no se debe copiar el arbol a as + // void f(A a) { log(a.x); } <-- f(call()); // el arbol va de log a call (por su retorno) + // TODO: this check is not specific enough + // Only copy the tree to the movables if there is only 1 movable: it is an object. + if (movables.size() == 1) + movables.get(0).getObjectTree().addAll(objTree); } else if (resolved.isField()) { // Known limitation: static fields // An object creation expression input an existing object via actual-in because it creates it. @@ -52,8 +65,8 @@ public class InterproceduralUsageFinder extends InterproceduralActionFinder mapAndFilterActionStream(Stream stream, CFG cfg) { + protected Stream mapAndFilterActionStream(Stream stream, CFG cfg) { return stream.filter(VariableAction::isUsage) .map(VariableAction::asUsage) .filter(Predicate.not(cfg::isCompletelyDefined)); diff --git a/sdg-core/src/main/java/es/upv/mist/slicing/nodes/VariableAction.java b/sdg-core/src/main/java/es/upv/mist/slicing/nodes/VariableAction.java index 556b12f..9f704e9 100644 --- a/sdg-core/src/main/java/es/upv/mist/slicing/nodes/VariableAction.java +++ b/sdg-core/src/main/java/es/upv/mist/slicing/nodes/VariableAction.java @@ -28,29 +28,32 @@ public abstract class VariableAction { protected final Expression variable; protected final String realName; protected final GraphNode graphNode; - protected final SimpleDirectedGraph objectTree = new SimpleDirectedGraph<>(null, DefaultEdge::new, false); + protected final ObjectTree objectTree; protected boolean optional = false; protected ResolvedValueDeclaration resolvedVariableCache; public VariableAction(Expression variable, String realName, GraphNode graphNode) { + this(variable, realName, graphNode, new ObjectTree()); + } + + public VariableAction(Expression variable, String realName, GraphNode graphNode, ObjectTree objectTree) { assert realName != null && !realName.isEmpty(); + assert objectTree != null; this.variable = variable; this.realName = realName; this.graphNode = graphNode; - this.objectTree.addVertex(realName); + this.objectTree = objectTree; + } + + public ObjectTree getObjectTree() { + return objectTree; } /** Add a field of this object, such that the same action performed on the object * is applied to this field too. Fields of fields may be specified separated by dots. */ public void addObjectField(String fieldName) { - String parent = null; - for (String element : fieldName.split("\\.")) { - objectTree.addVertex(element); - if (parent != null) - objectTree.addEdge(parent, element); - parent = element; - } + objectTree.addField(fieldName); } public VariableAction getRootAction() { @@ -205,18 +208,18 @@ public abstract class VariableAction { } /** Creates a new usage action with the same variable and the given node. */ - public final Usage toUsage(GraphNode graphNode) { - return new Usage(variable, realName, graphNode); + public final Usage toUsage(GraphNode graphNode, ObjectTree objectTree) { + return new Usage(variable, realName, graphNode, objectTree); } /** Creates a new definition action with the same variable and the given node. */ - public final Definition toDefinition(GraphNode graphNode) { - return new Definition(variable, realName, graphNode); + public final Definition toDefinition(GraphNode graphNode, ObjectTree objectTree) { + return new Definition(variable, realName, graphNode, objectTree); } /** Creates a new declaration action with the same variable and the given node. */ - public final Declaration toDeclaration(GraphNode graphNode) { - return new Declaration(variable, realName, graphNode); + public final Declaration toDeclaration(GraphNode graphNode, ObjectTree objectTree) { + return new Declaration(variable, realName, graphNode, objectTree); } @Override @@ -276,6 +279,10 @@ public abstract class VariableAction { super(variable, realName, graphNode); } + public Usage(Expression variable, String realName, GraphNode graphNode, ObjectTree objectTree) { + super(variable, realName, graphNode, objectTree); + } + @Override public String toString() { return "USE" + super.toString(); @@ -288,7 +295,7 @@ public abstract class VariableAction { protected final Expression expression; public Definition(Expression variable, String realName, GraphNode graphNode) { - this(variable, realName, graphNode, null); + this(variable, realName, graphNode, (Expression) null); } public Definition(Expression variable, String realName, GraphNode graphNode, Expression expression) { @@ -296,6 +303,15 @@ public abstract class VariableAction { this.expression = expression; } + public Definition(Expression variable, String realName, GraphNode graphNode, ObjectTree objectTree) { + this(variable, realName, graphNode, null, objectTree); + } + + public Definition(Expression variable, String realName, GraphNode graphNode, Expression expression, ObjectTree objectTree) { + super(variable, realName, graphNode, objectTree); + this.expression = expression; + } + /** @see #expression */ public Expression getExpression() { return expression; @@ -313,6 +329,10 @@ public abstract class VariableAction { super(variable, realName, graphNode); } + public Declaration(Expression variable, String realName, GraphNode graphNode, ObjectTree objectTree) { + super(variable, realName, graphNode, objectTree); + } + @Override public String toString() { return "DEC" + super.toString(); @@ -339,6 +359,11 @@ public abstract class VariableAction { this.inner = inner; } + @Override + public void addObjectField(String fieldName) { + throw new UnsupportedOperationException("Movable actions don't support the object tree"); + } + /** The final location of this action. This node may not yet be present * in the graph, if {@link #move(Graph)} has not been invoked. */ public SyntheticNode getRealNode() { @@ -432,4 +457,28 @@ public abstract class VariableAction { return Objects.hash(super.hashCode(), realNode, inner); } } + + public static class ObjectTree extends SimpleDirectedGraph { + private static final String ROOT = "-root-"; + + public ObjectTree() { + super(null, DefaultEdge::new, false); + addVertex(ROOT); + } + + public void addField(String fieldName) { + String parent = ROOT; + String[] splitField = fieldName.split("\\."); + for (int i = 1; i < splitField.length; i++) { + addVertex(splitField[i]); + addEdge(parent, splitField[i]); + parent = splitField[i]; + } + } + + public void addAll(ObjectTree tree) { + tree.vertexSet().forEach(this::addVertex); + tree.edgeSet().forEach(e -> addEdge(tree.getEdgeSource(e), tree.getEdgeTarget(e))); + } + } } -- GitLab From e4127a91ed15c93d87a3a6281d68af2856d28409 Mon Sep 17 00:00:00 2001 From: Carlos Galindo Date: Tue, 2 Mar 2021 13:17:46 +0100 Subject: [PATCH 02/60] Added MemberNodes to represent an object's tree in the PDG. * JSysPDG: inserts IO and Member nodes before generating data dependencies. * ObjectTree is now a real tree. * ObjectTree has string and node iterables. * VariableAction can be cloned with #createCopy(), the graphNode is set to null. * VariableAction.Movable can't have object tree, getter obtains inner's. --- .../mist/slicing/graphs/jsysdg/JSysPDG.java | 68 ++++++ .../sdg/InterproceduralActionFinder.java | 34 +-- .../sdg/InterproceduralDefinitionFinder.java | 13 +- .../sdg/InterproceduralUsageFinder.java | 13 +- .../mist/slicing/nodes/VariableAction.java | 213 ++++++++++++++++-- .../mist/slicing/nodes/VariableVisitor.java | 2 + .../upv/mist/slicing/nodes/oo/MemberNode.java | 28 +++ .../es/upv/mist/slicing/utils/ASTUtils.java | 4 + 8 files changed, 323 insertions(+), 52 deletions(-) create mode 100644 sdg-core/src/main/java/es/upv/mist/slicing/nodes/oo/MemberNode.java diff --git a/sdg-core/src/main/java/es/upv/mist/slicing/graphs/jsysdg/JSysPDG.java b/sdg-core/src/main/java/es/upv/mist/slicing/graphs/jsysdg/JSysPDG.java index 487ed1d..d085229 100644 --- a/sdg-core/src/main/java/es/upv/mist/slicing/graphs/jsysdg/JSysPDG.java +++ b/sdg-core/src/main/java/es/upv/mist/slicing/graphs/jsysdg/JSysPDG.java @@ -1,6 +1,15 @@ package es.upv.mist.slicing.graphs.jsysdg; import es.upv.mist.slicing.graphs.exceptionsensitive.ESPDG; +import es.upv.mist.slicing.graphs.pdg.PDG; +import es.upv.mist.slicing.nodes.GraphNode; +import es.upv.mist.slicing.nodes.VariableAction; +import es.upv.mist.slicing.nodes.io.CallNode; +import es.upv.mist.slicing.nodes.io.IONode; +import es.upv.mist.slicing.nodes.oo.MemberNode; + +import java.util.Deque; +import java.util.LinkedList; public class JSysPDG extends ESPDG { public JSysPDG() { @@ -10,4 +19,63 @@ public class JSysPDG extends ESPDG { public JSysPDG(JSysCFG cfg) { super(cfg); } + + @Override + protected PDG.Builder createBuilder() { + return new Builder(); + } + + protected class Builder extends ESPDG.Builder { + + /** Computes all the data dependencies between {@link VariableAction variable actions} of this graph. */ + @Override + protected void buildDataDependency() { + addSyntheticNodesToPDG(); + super.buildDataDependency(); + } + + protected void addSyntheticNodesToPDG() { + for (GraphNode node : cfg.vertexSet()) { + Deque callNodeStack = new LinkedList<>(); + for (VariableAction va : node.getVariableActions()) { + if (va instanceof VariableAction.CallMarker) { + // Compute the call node, if entering the marker. Additionally, it places the node + // in the graph and makes it control-dependent on its container. + if (!((VariableAction.CallMarker) va).isEnter()) { + callNodeStack.pop(); + } else { + CallNode callNode = CallNode.create(((VariableAction.CallMarker) va).getCall()); + if (node.isImplicitInstruction()) + callNode.markAsImplicit(); + addVertex(callNode); + addControlDependencyArc(node, callNode); + callNodeStack.push(callNode); + } + continue; + } + GraphNode parentNode; // node that represents the root of the object tree + if (va instanceof VariableAction.Movable) { + GraphNode realNode = ((VariableAction.Movable) va).getRealNode(); + addVertex(realNode); + connectRealNode(node, callNodeStack.peek(), realNode); + parentNode = realNode; + } else if (va.getObjectTree().isEmpty() || node instanceof IONode) { + parentNode = node; + } else { + parentNode = new MemberNode(va.toString(), null); + addVertex(parentNode); + addControlDependencyArc(node, parentNode); + } + // Extract the member nodes contained within the object tree + for (MemberNode memberNode : va.getObjectTree().nodeIterable()) { + MemberNode memberParent = memberNode.getParent(); + assert memberParent == null || containsVertex(memberParent); + addVertex(memberNode); + addControlDependencyArc(memberParent == null ? parentNode : memberParent, memberNode); + } + } + assert callNodeStack.isEmpty(); + } + } + } } diff --git a/sdg-core/src/main/java/es/upv/mist/slicing/graphs/sdg/InterproceduralActionFinder.java b/sdg-core/src/main/java/es/upv/mist/slicing/graphs/sdg/InterproceduralActionFinder.java index 1eeab36..df1caf6 100644 --- a/sdg-core/src/main/java/es/upv/mist/slicing/graphs/sdg/InterproceduralActionFinder.java +++ b/sdg-core/src/main/java/es/upv/mist/slicing/graphs/sdg/InterproceduralActionFinder.java @@ -14,7 +14,6 @@ import es.upv.mist.slicing.graphs.BackwardDataFlowAnalysis; import es.upv.mist.slicing.graphs.CallGraph; import es.upv.mist.slicing.graphs.cfg.CFG; import es.upv.mist.slicing.nodes.VariableAction; -import es.upv.mist.slicing.nodes.VariableAction.ObjectTree; import es.upv.mist.slicing.utils.ASTUtils; import es.upv.mist.slicing.utils.Logger; @@ -31,7 +30,7 @@ import java.util.stream.Stream; * declarations define, use or declare which variables, interprocedurally. * @param The action to be searched for */ -public abstract class InterproceduralActionFinder extends BackwardDataFlowAnalysis, Map> { +public abstract class InterproceduralActionFinder extends BackwardDataFlowAnalysis, Set> { protected final Map, CFG> cfgMap; /** A map from vertex and action to its corresponding stored action, to avoid generating duplicate nodes. */ protected final Map> actionStoredMap = new HashMap<>(); @@ -62,14 +61,14 @@ public abstract class InterproceduralActionFinder exte var actions = vertexDataMap.get(vertex); // Update stored action map actionStoredMap.computeIfAbsent(vertex, v -> new HashMap<>()); - for (A a : actions.keySet()) + for (A a : actions) actionStoredMap.get(vertex).computeIfAbsent(a, __ -> new StoredAction()); // FORMAL: per declaration (1) - for (A a : actions.keySet()) + for (A a : actions) getStored(vertex, a).storeFormal(() -> sandBoxedHandler(vertex, a, this::handleFormalAction)); // ACTUAL: per call (n) for (CallGraph.Edge edge : graph.incomingEdgesOf(vertex)) - actions.keySet().stream().sorted(new ParameterFieldSorter(edge)).forEach(a -> + actions.stream().sorted(new ParameterFieldSorter(edge)).forEach(a -> getStored(vertex, a).storeActual(edge, e -> sandBoxedHandler(e, a, this::handleActualAction))); } @@ -157,18 +156,18 @@ public abstract class InterproceduralActionFinder exte // =========================================================== @Override - protected Map compute(CallGraph.Vertex vertex, Set predecessors) { + protected Set compute(CallGraph.Vertex vertex, Set predecessors) { saveDeclaration(vertex); - Map newValue = new HashMap<>(vertexDataMap.get(vertex)); - newValue.putAll(initialValue(vertex)); + Set newValue = new HashSet<>(vertexDataMap.get(vertex)); + newValue.addAll(initialValue(vertex)); return newValue; } @Override - protected Map initialValue(CallGraph.Vertex vertex) { + protected Set initialValue(CallGraph.Vertex vertex) { CFG cfg = cfgMap.get(vertex.getDeclaration()); if (cfg == null) - return Collections.emptyMap(); + return Collections.emptySet(); Stream actionStream = cfg.vertexSet().stream() // Ignore root node, it is literally the entrypoint for interprocedural actions. .filter(n -> n != cfg.getRootNode()) @@ -178,15 +177,18 @@ public abstract class InterproceduralActionFinder exte // We skip over non-root variables (for each 'x.a' action we'll find 'x' later) .filter(VariableAction::isRootAction); Stream filteredStream = mapAndFilterActionStream(actionStream, cfg); - Map map = new HashMap<>(); + Set set = new HashSet<>(); for (Iterator it = filteredStream.iterator(); it.hasNext(); ) { A a = it.next(); - if (map.containsKey(a)) - map.get(a).addAll(a.getObjectTree()); - else - map.put(a, (ObjectTree) a.getObjectTree().clone()); + if (set.contains(a)) { + for (A aFromSet : set) + if (aFromSet.hashCode() == a.hashCode() && Objects.equals(aFromSet, a)) + aFromSet.getObjectTree().addAll(a.getObjectTree()); + } else { + set.add(a.createCopy()); + } } - return map; + return set; } /** Given a stream of VariableAction objects, map it to the finders' type and 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 8840ba4..cfce641 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 @@ -11,7 +11,6 @@ import es.upv.mist.slicing.nodes.GraphNode; 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.ObjectTree; import es.upv.mist.slicing.nodes.io.ActualIONode; import es.upv.mist.slicing.nodes.io.FormalIONode; @@ -28,14 +27,13 @@ public class InterproceduralDefinitionFinder extends InterproceduralActionFinder protected void handleFormalAction(CallGraph.Vertex vertex, Definition def) { CFG cfg = cfgMap.get(vertex.getDeclaration()); ResolvedValueDeclaration resolved = def.getResolvedValueDeclaration(); - ObjectTree objTree = vertexDataMap.get(vertex).get(def); if (!resolved.isParameter() || !resolved.getType().isPrimitive()) { FormalIONode formalOut = FormalIONode.createFormalOut(vertex.getDeclaration(), resolved); - Movable movable = new Movable(def.toUsage(cfg.getExitNode(), objTree), formalOut); + Movable movable = new Movable(def.toUsage(cfg.getExitNode()), formalOut); cfg.getExitNode().addMovableVariable(movable); } FormalIONode formalIn = FormalIONode.createFormalInDecl(vertex.getDeclaration(), resolved); - cfg.getRootNode().addMovableVariable(new Movable(def.toDeclaration(cfg.getRootNode(), objTree), formalIn)); + cfg.getRootNode().addMovableVariable(new Movable(def.toDeclaration(cfg.getRootNode()), formalIn)); } @Override @@ -43,7 +41,6 @@ public class InterproceduralDefinitionFinder extends InterproceduralActionFinder List movables = new LinkedList<>(); GraphNode graphNode = edge.getGraphNode(); ResolvedValueDeclaration resolved = def.getResolvedValueDeclaration(); - ObjectTree objTree = vertexDataMap.get(graph.getEdgeTarget(edge)).get(def); if (resolved.isParameter()) { Expression arg = extractArgument(resolved.asParameter(), edge, false); if (arg == null) @@ -53,9 +50,9 @@ public class InterproceduralDefinitionFinder extends InterproceduralActionFinder Set exprSet = new HashSet<>(); arg.accept(new OutNodeVariableVisitor(), exprSet); for (NameExpr nameExpr : exprSet) - movables.add(new Movable(new Definition(nameExpr, nameExpr.toString(), graphNode, objTree), actualOut)); + movables.add(new Movable(new Definition(nameExpr, nameExpr.toString(), graphNode, def.getObjectTree()), actualOut)); } else { - movables.add(new Movable(def.toDefinition(graphNode, objTree), actualOut)); + movables.add(new Movable(def.toDefinition(graphNode), actualOut)); } } else if (resolved.isField()) { // Known limitation: static fields @@ -65,7 +62,7 @@ public class InterproceduralDefinitionFinder extends InterproceduralActionFinder return; String aliasedName = obtainAliasedFieldName(def, edge); ActualIONode actualOut = ActualIONode.createActualOut(edge.getCall(), resolved, null); - var movableDef = new Definition(obtainScope(edge.getCall()), aliasedName, graphNode, objTree); + var movableDef = new Definition(obtainScope(edge.getCall()), aliasedName, graphNode, def.getObjectTree()); movables.add(new Movable(movableDef, actualOut)); } else { throw new IllegalStateException("Definition must be either from a parameter or a field!"); 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 401d10a..051b8e2 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 @@ -8,7 +8,10 @@ import es.upv.mist.slicing.graphs.CallGraph; import es.upv.mist.slicing.graphs.cfg.CFG; import es.upv.mist.slicing.nodes.GraphNode; import es.upv.mist.slicing.nodes.VariableAction; -import es.upv.mist.slicing.nodes.VariableAction.*; +import es.upv.mist.slicing.nodes.VariableAction.Declaration; +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.VariableVisitor; import es.upv.mist.slicing.nodes.io.ActualIONode; import es.upv.mist.slicing.nodes.io.FormalIONode; @@ -30,8 +33,7 @@ public class InterproceduralUsageFinder extends InterproceduralActionFinder movables = new LinkedList<>(); GraphNode graphNode = edge.getGraphNode(); ResolvedValueDeclaration resolved = use.getResolvedValueDeclaration(); - ObjectTree objTree = vertexDataMap.get(graph.getEdgeTarget(edge)).get(use); if (resolved.isParameter()) { Expression argument = extractArgument(resolved.asParameter(), edge, true); ActualIONode actualIn = ActualIONode.createActualIn(edge.getCall(), resolved, argument); @@ -57,7 +58,7 @@ public class InterproceduralUsageFinder extends InterproceduralActionFinder graphNode, ObjectTree objectTree) { - return new Usage(variable, realName, graphNode, objectTree); + public final Usage toUsage(GraphNode graphNode) { + return new Usage(variable, realName, graphNode, (ObjectTree) getObjectTree().clone()); } /** Creates a new definition action with the same variable and the given node. */ - public final Definition toDefinition(GraphNode graphNode, ObjectTree objectTree) { - return new Definition(variable, realName, graphNode, objectTree); + public final Definition toDefinition(GraphNode graphNode) { + return new Definition(variable, realName, graphNode, (ObjectTree) getObjectTree().clone()); } /** Creates a new declaration action with the same variable and the given node. */ - public final Declaration toDeclaration(GraphNode graphNode, ObjectTree objectTree) { - return new Declaration(variable, realName, graphNode, objectTree); + public final Declaration toDeclaration(GraphNode graphNode) { + return new Declaration(variable, realName, graphNode, (ObjectTree) getObjectTree().clone()); + } + + @SuppressWarnings("unchecked") + public final A createCopy() { + if (this instanceof Usage) + return (A) toUsage(null); + if (this instanceof Definition) + return (A) toDefinition(null); + if (this instanceof Declaration) + return (A) toDeclaration(null); + if (this instanceof Movable) { + Movable m = (Movable) this; + return (A) new Movable(m.inner.createCopy(), null); + } + throw new IllegalStateException("This kind of variable action can't be copied"); } @Override @@ -359,6 +373,10 @@ public abstract class VariableAction { this.inner = inner; } + public ObjectTree getObjectTree() { + return inner.getObjectTree(); + } + @Override public void addObjectField(String fieldName) { throw new UnsupportedOperationException("Movable actions don't support the object tree"); @@ -458,27 +476,178 @@ public abstract class VariableAction { } } - public static class ObjectTree extends SimpleDirectedGraph { - private static final String ROOT = "-root-"; + public static class ObjectTree implements Cloneable { + private static final Pattern FIELD_SPLIT = Pattern.compile("^(?(([_0-9A-Za-z]+\\.)*this)|([_0-9A-Za-z]+))(\\.(?.+))?$"); + + private final String memberName; + private final MemberNode memberNode; + private final Map childrenMap = new HashMap<>(); public ObjectTree() { - super(null, DefaultEdge::new, false); - addVertex(ROOT); + memberName = null; + memberNode = null; + } + + private ObjectTree(String memberName, ObjectTree parent) { + this.memberName = memberName; + this.memberNode = new MemberNode(memberName, parent.memberNode); } public void addField(String fieldName) { - String parent = ROOT; - String[] splitField = fieldName.split("\\."); - for (int i = 1; i < splitField.length; i++) { - addVertex(splitField[i]); - addEdge(parent, splitField[i]); - parent = splitField[i]; + String members = removeRoot(fieldName); + addNonRootField(members); + } + + private void addNonRootField(String members) { + if (members.contains(".")) { + int firstDot = members.indexOf('.'); + String first = members.substring(0, firstDot); + String rest = members.substring(firstDot + 1); + childrenMap.computeIfAbsent(first, f -> new ObjectTree(f, this)); + childrenMap.get(first).addNonRootField(rest); + } else { + childrenMap.computeIfAbsent(members, f -> new ObjectTree(f, this)); } } public void addAll(ObjectTree tree) { - tree.vertexSet().forEach(this::addVertex); - tree.edgeSet().forEach(e -> addEdge(tree.getEdgeSource(e), tree.getEdgeTarget(e))); + for (Map.Entry entry : tree.childrenMap.entrySet()) + if (childrenMap.containsKey(entry.getKey())) + childrenMap.get(entry.getKey()).addAll(entry.getValue()); + else + childrenMap.put(entry.getKey(), entry.getValue().clone(this)); + } + + public boolean hasMember(String member) { + String field = removeRoot(member); + return hasNonRootMember(field); + } + + private boolean hasNonRootMember(String members) { + if (members.contains(".")) { + int firstDot = members.indexOf('.'); + String first = members.substring(0, firstDot); + String rest = members.substring(firstDot + 1); + return childrenMap.containsKey(first) && childrenMap.get(first).hasNonRootMember(rest); + } else { + return childrenMap.containsKey(members); + } + } + + public MemberNode getNodeFor(String member) { + String field = removeRoot(member); + return getNodeForNonRoot(field); + } + + private MemberNode getNodeForNonRoot(String members) { + if (members.contains(".")) { + int firstDot = members.indexOf('.'); + String first = members.substring(0, firstDot); + String rest = members.substring(firstDot + 1); + assert childrenMap.containsKey(first); + return childrenMap.get(first).getNodeForNonRoot(rest); + } else { + assert childrenMap.containsKey(members); + return childrenMap.get(members).memberNode; + } + } + + public boolean isEmpty() { + return childrenMap.isEmpty(); + } + + public Iterable nameIterable() { + return () -> new Iterator<>() { + final Iterator it = treeIterator(); + + @Override + public boolean hasNext() { + return it.hasNext(); + } + + @Override + public String next() { + return it.next().memberName; + } + }; + } + + public Iterable nodeIterable() { + return () -> new Iterator<>() { + final Iterator it = treeIterator(); + + @Override + public boolean hasNext() { + return it.hasNext(); + } + + @Override + public MemberNode next() { + return it.next().memberNode; + } + }; + } + + private Iterator treeIterator() { + return new Iterator<>() { + final Set remaining = new HashSet<>(childrenMap.values()); + Iterator childIterator = null; + + @Override + public boolean hasNext() { + if (childIterator == null || !childIterator.hasNext()) + return !remaining.isEmpty(); + else + return true; + } + + @Override + public ObjectTree next() { + if (childIterator == null || !childIterator.hasNext()) { + ObjectTree tree = Utils.setPop(remaining); + childIterator = tree.treeIterator(); + return tree; + } else { + return childIterator.next(); + } + } + }; + } + + @Override + public Object clone() { + ObjectTree clone = new ObjectTree(); + for (Map.Entry entry : childrenMap.entrySet()) + clone.childrenMap.put(entry.getKey(), entry.getValue().clone(clone)); + return clone; + } + + private ObjectTree clone(ObjectTree parent) { + ObjectTree clone = new ObjectTree(memberName, parent); + for (Map.Entry entry : childrenMap.entrySet()) + clone.childrenMap.put(entry.getKey(), entry.getValue().clone(clone)); + return clone; + } + + protected String removeRoot(String fieldWithRoot) { + Matcher matcher = FIELD_SPLIT.matcher(fieldWithRoot); + if (matcher.matches() && matcher.group("fields") != null) + return matcher.group("fields"); + throw new IllegalArgumentException("Field should be of the form ., .this., where may not contain dots."); + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + ObjectTree tree = (ObjectTree) o; + return Objects.equals(memberName, tree.memberName) && + childrenMap.values().equals(tree.childrenMap.values()); + } + + @Override + public int hashCode() { + return Objects.hash(memberName, childrenMap); } } } 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 276207c..8415bd2 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 @@ -362,6 +362,7 @@ public class VariableVisitor extends GraphNodeContentVisitor { + protected final MemberNode parent; + + public MemberNode(String instruction, MemberNode parent) { + super(instruction, null, new LinkedList<>()); + this.parent = parent; + } + + public MemberNode getParent() { + return parent; + } + + @Override + public String toString() { + return String.format("%s{id: %s, label: '%s'}", + getClass().getSimpleName(), + getId(), + getLabel() + ); + } +} 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 88b1717..041ead4 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 @@ -42,10 +42,14 @@ public class ASTUtils { } 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); } public static boolean equalsWithRangeInCU(Node n1, Node n2) { + if (n1 == null || n2 == null) + return n1 == n2; return n1.findCompilationUnit().equals(n2.findCompilationUnit()) && equalsWithRange(n1, n2); } -- GitLab From e7d95501cb97828431f3d736ccd66655c0ab5e2f Mon Sep 17 00:00:00 2001 From: Carlos Galindo Date: Mon, 8 Mar 2021 12:48:25 +0100 Subject: [PATCH 03/60] Mega commit: object trees and their connections. * VariableAction stores pending connections between trees. * Update JavaParser from 3.17.0 to 3.19.0 * Object trees in assignments and return statements are linked to the appropriate expressions. * Return trees are linked interprocedurally. * ReturnStmt variable actions are now all generated by VariableVisitor. * CallNode.Return are now generated by VariableVisitor instead of the SDG after the interprocedural finders. * Actual-in nodes are now generated based on the expressions used, and then linked to the actual-in generated by the InterproceduralUsageFinder. * We now accept calls as scope or argument of another call. * ClassGraph is now a singleton. ClassGraph#newInstance() generates a new one. * ClassGraph can generate a complete ObjectTree for a given type. * MemberNode's parent can now be any node, and can be set after creation. * New slicing algorithm for the JSysDG. * Added interprocedural object-flow arcs. * Added ObjectFlow and Flow dependency arcs. * Introduced test to guarantee that interprocedural arcs are either input xor output. * Updated tests --- sdg-core/pom.xml | 4 +- .../java/es/upv/mist/slicing/arcs/Arc.java | 4 + .../slicing/arcs/pdg/FlowDependencyArc.java | 24 ++ .../arcs/pdg/ObjectFlowDependencyArc.java | 26 ++ .../slicing/arcs/sdg/ParameterInOutArc.java | 30 +- .../es/upv/mist/slicing/graphs/CallGraph.java | 17 +- .../upv/mist/slicing/graphs/ClassGraph.java | 90 ++++- .../graphs/ExpressionObjectTreeFinder.java | 170 +++++++++ .../mist/slicing/graphs/augmented/ACFG.java | 5 + .../slicing/graphs/augmented/ACFGBuilder.java | 4 - .../es/upv/mist/slicing/graphs/cfg/CFG.java | 11 +- .../mist/slicing/graphs/cfg/CFGBuilder.java | 6 +- .../graphs/exceptionsensitive/ESCFG.java | 10 +- .../mist/slicing/graphs/jsysdg/JSysCFG.java | 111 +++++- .../graphs/jsysdg/JSysCallConnector.java | 49 +++ .../mist/slicing/graphs/jsysdg/JSysDG.java | 14 +- .../mist/slicing/graphs/jsysdg/JSysPDG.java | 83 ++++- .../slicing/graphs/sdg/CallConnector.java | 4 +- .../sdg/InterproceduralDefinitionFinder.java | 9 +- .../sdg/InterproceduralUsageFinder.java | 72 ++-- .../es/upv/mist/slicing/graphs/sdg/SDG.java | 49 +-- .../es/upv/mist/slicing/nodes/GraphNode.java | 76 +++- .../mist/slicing/nodes/VariableAction.java | 240 ++++++++++-- .../mist/slicing/nodes/VariableVisitor.java | 346 ++++++++++++------ .../mist/slicing/nodes/io/ActualIONode.java | 10 +- .../es/upv/mist/slicing/nodes/io/IONode.java | 4 + .../upv/mist/slicing/nodes/oo/MemberNode.java | 11 +- .../ExceptionSensitiveSlicingAlgorithm.java | 8 +- .../slicing/JSysDGSlicingAlgorithm.java | 27 ++ .../es/upv/mist/slicing/slicing/Slice.java | 2 +- .../es/upv/mist/slicing/utils/ASTUtils.java | 38 +- .../java/es/upv/mist/slicing/SlicerTest.java | 2 +- .../arcs/sdg/InterproceduralArcTest.java | 31 ++ .../test/res/regression/carlos/Problem1.java | 2 + .../carlos/Problem1.java.sdg.sliced | 7 +- .../carlos/Problem3.java.sdg.sliced | 2 - .../regression/carlos/Problem3nonStatic.java | 20 + .../test/res/regression/carlos/TestJosep.java | 4 +- .../carlos/TestJosep.java.sdg.sliced | 1 + .../dinsa-tests/Josep2.java.sdg.sliced | 2 +- .../dinsa-tests/Josep3.java.sdg.sliced | 1 + .../dinsa-tests/Josep4.java.sdg.sliced | 1 + 42 files changed, 1340 insertions(+), 287 deletions(-) create mode 100644 sdg-core/src/main/java/es/upv/mist/slicing/arcs/pdg/FlowDependencyArc.java create mode 100644 sdg-core/src/main/java/es/upv/mist/slicing/arcs/pdg/ObjectFlowDependencyArc.java create mode 100644 sdg-core/src/main/java/es/upv/mist/slicing/graphs/ExpressionObjectTreeFinder.java create mode 100644 sdg-core/src/main/java/es/upv/mist/slicing/graphs/jsysdg/JSysCallConnector.java create mode 100644 sdg-core/src/main/java/es/upv/mist/slicing/slicing/JSysDGSlicingAlgorithm.java create mode 100644 sdg-core/src/test/java/es/upv/mist/slicing/arcs/sdg/InterproceduralArcTest.java create mode 100644 sdg-core/src/test/res/regression/carlos/Problem3nonStatic.java diff --git a/sdg-core/pom.xml b/sdg-core/pom.xml index efc9e87..e027b85 100644 --- a/sdg-core/pom.xml +++ b/sdg-core/pom.xml @@ -34,12 +34,12 @@ com.github.javaparser javaparser-core - 3.17.0 + 3.19.0 com.github.javaparser javaparser-symbol-solver-core - 3.17.0 + 3.19.0 org.jgrapht diff --git a/sdg-core/src/main/java/es/upv/mist/slicing/arcs/Arc.java b/sdg-core/src/main/java/es/upv/mist/slicing/arcs/Arc.java index 1a46b08..12f7cc8 100644 --- a/sdg-core/src/main/java/es/upv/mist/slicing/arcs/Arc.java +++ b/sdg-core/src/main/java/es/upv/mist/slicing/arcs/Arc.java @@ -89,6 +89,10 @@ public abstract class Arc extends DefaultEdge { throw new UnsupportedOperationException("Not a DataDependencyArc"); } + public boolean isObjectFlow() { + return false; + } + // =========================== SDG =========================== /** Whether or not this is an interprocedural arc that connects a call site to its declaration. */ diff --git a/sdg-core/src/main/java/es/upv/mist/slicing/arcs/pdg/FlowDependencyArc.java b/sdg-core/src/main/java/es/upv/mist/slicing/arcs/pdg/FlowDependencyArc.java new file mode 100644 index 0000000..dbd9213 --- /dev/null +++ b/sdg-core/src/main/java/es/upv/mist/slicing/arcs/pdg/FlowDependencyArc.java @@ -0,0 +1,24 @@ +package es.upv.mist.slicing.arcs.pdg; + +import es.upv.mist.slicing.arcs.Arc; +import org.jgrapht.nio.Attribute; +import org.jgrapht.nio.DefaultAttribute; + +import java.util.Map; + +public class FlowDependencyArc extends Arc { + public FlowDependencyArc() { + super(); + } + + public FlowDependencyArc(String variable) { + super(variable); + } + + @Override + public Map getDotAttributes() { + Map map = super.getDotAttributes(); + map.put("color", DefaultAttribute.createAttribute("red")); + return map; + } +} diff --git a/sdg-core/src/main/java/es/upv/mist/slicing/arcs/pdg/ObjectFlowDependencyArc.java b/sdg-core/src/main/java/es/upv/mist/slicing/arcs/pdg/ObjectFlowDependencyArc.java new file mode 100644 index 0000000..5214519 --- /dev/null +++ b/sdg-core/src/main/java/es/upv/mist/slicing/arcs/pdg/ObjectFlowDependencyArc.java @@ -0,0 +1,26 @@ +package es.upv.mist.slicing.arcs.pdg; + +import es.upv.mist.slicing.arcs.Arc; +import org.jgrapht.nio.Attribute; +import org.jgrapht.nio.DefaultAttribute; + +import java.util.Map; + +public class ObjectFlowDependencyArc extends Arc { + public ObjectFlowDependencyArc() { + super(); + } + + @Override + public boolean isObjectFlow() { + return true; + } + + @Override + public Map getDotAttributes() { + Map map = super.getDotAttributes(); + map.put("color", DefaultAttribute.createAttribute("red")); + map.put("style", DefaultAttribute.createAttribute("dashed")); + return map; + } +} diff --git a/sdg-core/src/main/java/es/upv/mist/slicing/arcs/sdg/ParameterInOutArc.java b/sdg-core/src/main/java/es/upv/mist/slicing/arcs/sdg/ParameterInOutArc.java index ccaba8b..5022c50 100644 --- a/sdg-core/src/main/java/es/upv/mist/slicing/arcs/sdg/ParameterInOutArc.java +++ b/sdg-core/src/main/java/es/upv/mist/slicing/arcs/sdg/ParameterInOutArc.java @@ -5,6 +5,7 @@ import es.upv.mist.slicing.nodes.io.ActualIONode; import es.upv.mist.slicing.nodes.io.CallNode; import es.upv.mist.slicing.nodes.io.FormalIONode; import es.upv.mist.slicing.nodes.io.OutputNode; +import es.upv.mist.slicing.nodes.oo.MemberNode; import org.jgrapht.nio.Attribute; import org.jgrapht.nio.DefaultAttribute; @@ -22,18 +23,39 @@ public class ParameterInOutArc extends InterproceduralArc { @Override public boolean isInterproceduralInputArc() { - GraphNode source = (GraphNode) getSource(); - GraphNode target = (GraphNode) getTarget(); + GraphNode source = getNodeCommon(getSource()); + GraphNode target = getNodeCommon(getTarget()); return source instanceof ActualIONode && ((ActualIONode) source).isInput() && target instanceof FormalIONode && ((FormalIONode) target).isInput(); } @Override public boolean isInterproceduralOutputArc() { - GraphNode source = (GraphNode) getSource(); - GraphNode target = (GraphNode) getTarget(); + GraphNode source = getNodeCommon(getSource()); + GraphNode target = getNodeCommon(getTarget()); return (source instanceof FormalIONode && ((FormalIONode) source).isOutput() && target instanceof ActualIONode && ((ActualIONode) target).isOutput()) || (source instanceof OutputNode && target instanceof CallNode.Return); } + + protected GraphNode getNodeCommon(Object node) { + GraphNode graphNode = (GraphNode) node; + while (graphNode instanceof MemberNode) + graphNode = ((MemberNode) graphNode).getParent(); + return graphNode; + } + + public static class ObjectFlow extends ParameterInOutArc { + @Override + public boolean isObjectFlow() { + return true; + } + + @Override + public Map getDotAttributes() { + Map map = super.getDotAttributes(); + map.put("style", DefaultAttribute.createAttribute("dotted")); + return map; + } + } } 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 d15c98e..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 @@ -53,7 +53,7 @@ public class CallGraph extends DirectedPseudograph> getCallTargets(Resolvable call) { return edgeSet().stream() - .filter(e -> e.getCall() == call) + .filter(e -> ASTUtils.equalsInDeclaration((Node) e.getCall(), (Node) call)) .map(this::getEdgeTarget) .map(Vertex::getDeclaration) .map(decl -> (CallableDeclaration) decl); @@ -168,22 +168,19 @@ public class CallGraph extends DirectedPseudograph createPolyEdges(decl, n)); - if (ASTUtils.shouldVisitArgumentsForMethodCalls(n)) - super.visit(n, arg); + super.visit(n, arg); } @Override public void visit(ObjectCreationExpr n, Void arg) { n.resolve().toAst().ifPresent(decl -> createNormalEdge(decl, n)); - if (ASTUtils.shouldVisitArgumentsForMethodCalls(n)) - super.visit(n, arg); + super.visit(n, arg); } @Override public void visit(ExplicitConstructorInvocationStmt n, Void arg) { n.resolve().toAst().ifPresent(decl -> createNormalEdge(decl, n)); - if (ASTUtils.shouldVisitArgumentsForMethodCalls(n)) - super.visit(n, arg); + super.visit(n, arg); } protected void createPolyEdges(MethodDeclaration decl, MethodCallExpr call) { @@ -241,9 +238,9 @@ public class CallGraph extends DirectedPseudograph, Edge> getDOTExporter() { - DOTExporter, Edge> dot = new DOTExporter<>(); - dot.setVertexAttributeProvider(decl -> Utils.dotLabel(decl.getDeclarationAsString(false, false, false))); + public DOTExporter> getDOTExporter() { + DOTExporter> dot = new DOTExporter<>(); + dot.setVertexAttributeProvider(vertex -> Utils.dotLabel(vertex.declaration.getDeclarationAsString(false, false, false))); dot.setEdgeAttributeProvider(edge -> Utils.dotLabel(edge.getCall().toString())); return dot; } diff --git a/sdg-core/src/main/java/es/upv/mist/slicing/graphs/ClassGraph.java b/sdg-core/src/main/java/es/upv/mist/slicing/graphs/ClassGraph.java index bc5bcc9..03d2526 100644 --- a/sdg-core/src/main/java/es/upv/mist/slicing/graphs/ClassGraph.java +++ b/sdg-core/src/main/java/es/upv/mist/slicing/graphs/ClassGraph.java @@ -4,10 +4,13 @@ import com.github.javaparser.ast.CompilationUnit; import com.github.javaparser.ast.NodeList; import com.github.javaparser.ast.body.*; import com.github.javaparser.ast.visitor.VoidVisitorAdapter; +import com.github.javaparser.resolution.UnsolvedSymbolException; import com.github.javaparser.resolution.declarations.ResolvedClassDeclaration; import com.github.javaparser.resolution.types.ResolvedReferenceType; import es.upv.mist.slicing.arcs.Arc; +import es.upv.mist.slicing.nodes.VariableAction.ObjectTree; import es.upv.mist.slicing.utils.ASTUtils; +import es.upv.mist.slicing.utils.Logger; import es.upv.mist.slicing.utils.Utils; import org.jgrapht.graph.DirectedPseudograph; import org.jgrapht.nio.dot.DOTExporter; @@ -17,6 +20,18 @@ import java.util.stream.Collectors; import java.util.stream.Stream; public class ClassGraph extends DirectedPseudograph implements Buildable> { + private static ClassGraph instance = null; + + public static ClassGraph getNewInstance() { + instance = null; + return getInstance(); + } + + public static ClassGraph getInstance() { + if (instance == null) + instance = new ClassGraph(); + return instance; + } /** The key of the vertex map needs to be a String because extendedTypes represent extended classes * as ClassOrInterfaceType objects while class declarations define classes as ClassOrInterfaceDeclaration @@ -25,7 +40,7 @@ public class ClassGraph extends DirectedPseudograph generateObjectTreeForReturnOf(CallableDeclaration callableDeclaration) { + if (callableDeclaration.isMethodDeclaration()) { + MethodDeclaration method = callableDeclaration.asMethodDeclaration(); + if (method.getType().isClassOrInterfaceType()) + try { + return Optional.of(generateObjectTreeFor(method.getType().asClassOrInterfaceType().resolve())); + } catch (UnsolvedSymbolException e) { + return Optional.empty(); + } + else + return Optional.empty(); + } else if (callableDeclaration.isConstructorDeclaration()) { + return Optional.of(generateObjectTreeFor(ASTUtils.getClassNode(callableDeclaration))); + } else { + throw new IllegalArgumentException("Invalid callable declaration type"); + } + } + + public ObjectTree generateObjectTreeFor(ClassOrInterfaceDeclaration declaration) { + return generateObjectTreeFor(vertexDeclarationMap.get(mapKey(declaration))); + } + + public ObjectTree generateObjectTreeFor(ResolvedReferenceType type) { + Vertex classVertex = vertexDeclarationMap.get(mapKey(type)); + if (classVertex == null) { + Logger.log("ResolvedReferenceType could not be found in class graph: " + type.describe()); + return new ObjectTree(); + } + return generateObjectTreeFor(classVertex); + } + + protected ObjectTree generateObjectTreeFor(Vertex classVertex) { + return generateObjectTreeFor(classVertex, new ObjectTree(), "-root-"); + } + + protected ObjectTree generateObjectTreeFor(Vertex classVertex, ObjectTree tree, String level) { + Map classFields = findAllFieldsOf(classVertex); + for (Map.Entry entry : classFields.entrySet()) { + tree.addField(level + '.' + entry.getKey()); + if (entry.getValue() != null) + generateObjectTreeFor(entry.getValue(), tree, level + '.' + entry.getKey()); + } + return tree; + } + + protected Map findAllFieldsOf(Vertex classVertex) { + assert classVertex.declaration instanceof ClassOrInterfaceDeclaration; + assert !classVertex.declaration.asClassOrInterfaceDeclaration().isInterface(); + ClassOrInterfaceDeclaration clazz = classVertex.getDeclaration().asClassOrInterfaceDeclaration(); + Map fieldMap = new HashMap<>(); + while (clazz != null) { + for (FieldDeclaration field : clazz.getFields()) { + for (VariableDeclarator var : field.getVariables()) { + if (fieldMap.containsKey(var.getNameAsString())) + continue; + Vertex v = null; + if (var.getType().isClassOrInterfaceType()) { + try { + v = vertexDeclarationMap.get(mapKey(var.getType().asClassOrInterfaceType().resolve())); + } catch (UnsolvedSymbolException ignored) { + } + } + fieldMap.put(var.getNameAsString(), v); + } + } + Optional parent = parentOf(clazz); + if (parent.isEmpty()) + break; + clazz = parent.get(); + } + return fieldMap; + } + @Override public void build(NodeList arg) { if (isBuilt()) 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 new file mode 100644 index 0000000..352cb40 --- /dev/null +++ b/sdg-core/src/main/java/es/upv/mist/slicing/graphs/ExpressionObjectTreeFinder.java @@ -0,0 +1,170 @@ +package es.upv.mist.slicing.graphs; + +import com.github.javaparser.ast.body.VariableDeclarator; +import com.github.javaparser.ast.expr.*; +import com.github.javaparser.ast.visitor.VoidVisitorAdapter; +import com.github.javaparser.resolution.Resolvable; +import com.github.javaparser.resolution.declarations.ResolvedMethodLikeDeclaration; +import com.github.javaparser.resolution.types.ResolvedReferenceType; +import com.github.javaparser.utils.Pair; +import es.upv.mist.slicing.nodes.GraphNode; +import es.upv.mist.slicing.nodes.VariableAction; +import es.upv.mist.slicing.nodes.VariableAction.ObjectTree; +import es.upv.mist.slicing.utils.ASTUtils; + +import java.util.LinkedList; +import java.util.List; + +import static es.upv.mist.slicing.graphs.cfg.CFGBuilder.VARIABLE_NAME_OUTPUT; + +public class ExpressionObjectTreeFinder { + + protected final GraphNode graphNode; + + public ExpressionObjectTreeFinder(GraphNode graphNode) { + this.graphNode = graphNode; + } + + public void handleVariableDeclarator(VariableDeclarator variableDeclarator) { + assert variableDeclarator.getInitializer().isPresent(); + VariableAction targetAction = locateVAVariableDeclarator(variableDeclarator.getNameAsString()); + ResolvedReferenceType type = variableDeclarator.getType().resolve().asReferenceType(); + var fields = ClassGraph.getInstance().generateObjectTreeFor(type); + targetAction.getObjectTree().addAll(fields); + locateExpressionResultTrees(variableDeclarator.getInitializer().get()) + .forEach(pair -> markTransference(pair, targetAction, "")); + } + + protected VariableAction locateVAVariableDeclarator(String varName) { + boolean foundDecl = false; + VariableAction lastDef = null; + for (VariableAction a : graphNode.getVariableActions()) { + if (a.isDeclaration()) { + if (a.getVariable().equals(varName)) + foundDecl = true; + else if (foundDecl) + return lastDef; + } else if (a.isDefinition() && a.getVariable().equals(varName)) { + lastDef = a; + } + } + assert lastDef != null: "Could not find DEF for variable declaration of " + varName; + return lastDef; + } + + public void handleAssignExpr(AssignExpr assignExpr, VariableAction assignTarget, String targetMember) { + ResolvedReferenceType type = assignExpr.getTarget().calculateResolvedType().asReferenceType(); + var fields = ClassGraph.getInstance().generateObjectTreeFor(type); + assignTarget.getObjectTree().addAll(fields); + locateExpressionResultTrees(assignExpr.getValue()) + .forEach(pair -> markTransference(pair, assignTarget, targetMember)); + } + + public void locateAndMarkTransferenceToRoot(Expression expr, int index) { + List list = graphNode.getVariableActions(); + if (index < 0) + locateAndMarkTransferenceToRoot(expr, list.get(list.size() + index)); + else + locateAndMarkTransferenceToRoot(expr, list.get(index)); + } + + public void locateAndMarkTransferenceToRoot(Expression expression, VariableAction targetAction) { + locateExpressionResultTrees(expression) + .forEach(pair -> markTransference(pair, targetAction, "")); + } + + protected List> locateExpressionResultTrees(Expression expression) { + List> list = new LinkedList<>(); + expression.accept(new VoidVisitorAdapter() { + @Override + public void visit(ArrayAccessExpr n, String arg) { + throw new UnsupportedOperationException("Array accesses are not supported as argument for return, call scope or argument. Please, pre-process your graph or use the EDG."); + } + + @Override + public void visit(AssignExpr n, String arg) { + n.getValue().accept(this, arg); + } + + @Override + public void visit(ConditionalExpr n, String arg) { + n.getThenExpr().accept(this, arg); + n.getElseExpr().accept(this, arg); + } + + @Override + public void visit(NameExpr n, String arg) { + if (n.resolve().isType()) + return; + for (VariableAction action : graphNode.getVariableActions()) { + if (action.isUsage() && + action.getVariable().equals(n.getNameAsString()) && + n.equals(action.getVariableExpression())) { + list.add(new Pair<>(action, arg)); + return; + } + } + throw new IllegalStateException("Cannot find USE action for var " + n); + } + + @Override + public void visit(ThisExpr n, String arg) { + for (VariableAction action : graphNode.getVariableActions()) { + if (action.isUsage()) { + if (action.getVariableExpression() == null) { + if (action.getVariable().matches("^.*this$")) { + list.add(new Pair<>(action, arg)); + return; + } + } else { + if (n.equals(action.getVariableExpression())) { + list.add(new Pair<>(action, arg)); + return; + } + } + } + } + throw new IllegalStateException("Could not find USE(this)"); + } + + @Override + public void visit(FieldAccessExpr n, String arg) { + if (!arg.isEmpty()) + arg = "." + arg; + n.getScope().accept(this, n.getNameAsString() + arg); + } + + @Override + public void visit(ObjectCreationExpr n, String arg) { + visitCall(n, arg); + } + + @Override + public void visit(MethodCallExpr n, String arg) { + visitCall(n, arg); + } + + protected void visitCall(Expression call, String arg) { + if (ASTUtils.shouldVisitArgumentsForMethodCalls((Resolvable) call)) + return; + for (VariableAction variableAction : graphNode.getVariableActions()) { + if (variableAction.isUsage() && + variableAction.getVariable().equals(VARIABLE_NAME_OUTPUT) && + call.equals(variableAction.getVariableExpression())) { + list.add(new Pair<>(variableAction, arg)); + return; + } + } + throw new IllegalStateException("Could not find USE(-output-) corresponding to call " + call); + } + }, ""); + return list; + } + + protected void markTransference(Pair sourcePair, VariableAction targetAction, String targetMember) { + VariableAction sourceAction = sourcePair.a; + String sourceMember = sourcePair.b; + ObjectTree.copyTree(sourceAction.getObjectTree(), targetAction.getObjectTree(), sourceMember, targetMember); + sourceAction.setPDGTreeConnectionTo(targetAction, sourceMember, targetMember); + } +} diff --git a/sdg-core/src/main/java/es/upv/mist/slicing/graphs/augmented/ACFG.java b/sdg-core/src/main/java/es/upv/mist/slicing/graphs/augmented/ACFG.java index 82144f4..b2a8c47 100644 --- a/sdg-core/src/main/java/es/upv/mist/slicing/graphs/augmented/ACFG.java +++ b/sdg-core/src/main/java/es/upv/mist/slicing/graphs/augmented/ACFG.java @@ -34,4 +34,9 @@ public class ACFG extends CFG { public boolean isPseudoPredicate(GraphNode node) { return outgoingEdgesOf(node).stream().filter(Arc::isNonExecutableControlFlowArc).count() == 1; } + + @Override + public boolean isPredicate(GraphNode graphNode) { + return super.isPredicate(graphNode) && !isPseudoPredicate(graphNode); + } } diff --git a/sdg-core/src/main/java/es/upv/mist/slicing/graphs/augmented/ACFGBuilder.java b/sdg-core/src/main/java/es/upv/mist/slicing/graphs/augmented/ACFGBuilder.java index d0aa7cf..61cbf66 100644 --- a/sdg-core/src/main/java/es/upv/mist/slicing/graphs/augmented/ACFGBuilder.java +++ b/sdg-core/src/main/java/es/upv/mist/slicing/graphs/augmented/ACFGBuilder.java @@ -131,10 +131,6 @@ public class ACFGBuilder extends CFGBuilder { @Override public void visit(ReturnStmt returnStmt, Void arg) { GraphNode node = connectTo(returnStmt); - returnStmt.getExpression().ifPresent(n -> { - n.accept(this, arg); - node.addDefinedVariable(null, VARIABLE_NAME_OUTPUT, n); - }); returnList.add(node); clearHanging(); nonExecHangingNodes.add(node); // NEW vs CFG diff --git a/sdg-core/src/main/java/es/upv/mist/slicing/graphs/cfg/CFG.java b/sdg-core/src/main/java/es/upv/mist/slicing/graphs/cfg/CFG.java index 32187e1..6a419b0 100644 --- a/sdg-core/src/main/java/es/upv/mist/slicing/graphs/cfg/CFG.java +++ b/sdg-core/src/main/java/es/upv/mist/slicing/graphs/cfg/CFG.java @@ -54,6 +54,10 @@ public class CFG extends GraphWithRootNode> { addEdge(from, to, arc); } + public boolean isPredicate(GraphNode graphNode) { + return outgoingEdgesOf(graphNode).size() > 1; + } + /** Obtain the definitions that may have reached the given variable action. */ public List findLastDefinitionsFrom(VariableAction variable) { return findLastVarActionsFrom(variable, VariableAction::isDefinition); @@ -93,12 +97,13 @@ public class CFG extends GraphWithRootNode> { stream = stream.takeWhile(va -> va != var); List list = stream.filter(var::matches).filter(filter).collect(Collectors.toList()); if (!list.isEmpty()) { - for (int i = list.size() - 1; i >= 0; i--) { + boolean found = false; + for (int i = list.size() - 1; i >= 0 && !found; i--) { result.add(list.get(i)); if (!list.get(i).isOptional()) - break; + found = true; } - if (!list.get(0).isOptional()) + if (found) return true; } diff --git a/sdg-core/src/main/java/es/upv/mist/slicing/graphs/cfg/CFGBuilder.java b/sdg-core/src/main/java/es/upv/mist/slicing/graphs/cfg/CFGBuilder.java index 0f8278e..a20e4d2 100644 --- a/sdg-core/src/main/java/es/upv/mist/slicing/graphs/cfg/CFGBuilder.java +++ b/sdg-core/src/main/java/es/upv/mist/slicing/graphs/cfg/CFGBuilder.java @@ -328,10 +328,6 @@ public class CFGBuilder extends VoidVisitorAdapter { @Override public void visit(ReturnStmt returnStmt, Void arg) { GraphNode node = connectTo(returnStmt); - returnStmt.getExpression().ifPresent(n -> { - n.accept(this, arg); - node.addDefinedVariable(null, VARIABLE_NAME_OUTPUT, n); - }); returnList.add(node); clearHanging(); } @@ -379,7 +375,7 @@ public class CFGBuilder extends VoidVisitorAdapter { protected void addMethodOutput(CallableDeclaration callableDeclaration, GraphNode exit) { if (!(callableDeclaration instanceof MethodDeclaration) || !((MethodDeclaration) callableDeclaration).getType().isVoidType()) { VariableAction usage = new VariableAction.Usage(null, VARIABLE_NAME_OUTPUT, exit); - exit.addMovableVariable(new VariableAction.Movable(usage, OutputNode.create(callableDeclaration))); + exit.addVariableAction(new VariableAction.Movable(usage, OutputNode.create(callableDeclaration))); } } } diff --git a/sdg-core/src/main/java/es/upv/mist/slicing/graphs/exceptionsensitive/ESCFG.java b/sdg-core/src/main/java/es/upv/mist/slicing/graphs/exceptionsensitive/ESCFG.java index cbde73c..11fe7aa 100644 --- a/sdg-core/src/main/java/es/upv/mist/slicing/graphs/exceptionsensitive/ESCFG.java +++ b/sdg-core/src/main/java/es/upv/mist/slicing/graphs/exceptionsensitive/ESCFG.java @@ -30,7 +30,7 @@ import java.util.*; * and multiple calls with exceptions per CFG node are not considered. */ public class ESCFG extends ACFG { - protected static final String ACTIVE_EXCEPTION_VARIABLE = "-activeException-"; + public static final String ACTIVE_EXCEPTION_VARIABLE = "-activeException-"; @Override protected CFGBuilder newCFGBuilder() { @@ -127,7 +127,7 @@ public class ESCFG extends ACFG { // Exception exit Collection exceptionExits = processExceptionSources(callableDeclaration); for (ExceptionExitNode node : exceptionExits) { - node.addUsedVariable(null, ACTIVE_EXCEPTION_VARIABLE); + node.addVAUseActiveException(); hangingNodes.add(node); lastNodes.addAll(hangingNodes); clearHanging(); @@ -240,7 +240,7 @@ public class ESCFG extends ACFG { stmtStack.push(n); GraphNode stmt = connectTo(n); n.getExpression().accept(this, arg); - stmt.addDefinedVariable(null, ACTIVE_EXCEPTION_VARIABLE, n.getExpression()); + stmt.addVADefineActiveException(n.getExpression()); populateExceptionSourceMap(new ExceptionSource(stmt, n.getExpression().calculateResolvedType())); clearHanging(); nonExecHangingNodes.add(stmt); @@ -286,7 +286,7 @@ public class ESCFG extends ACFG { for (ResolvedType type : resolved.getSpecifiedExceptions()) { hangingNodes.add(stmtNode); ExceptionReturnNode exceptionReturn = addExceptionReturnNode(call, type); - exceptionReturn.addDefinedVariable(null, ACTIVE_EXCEPTION_VARIABLE, null); // TODO: improve initializer + exceptionReturn.addVADefineActiveException(null); // TODO: improve initializer populateExceptionSourceMap(new ExceptionSource(exceptionReturn, type)); returnNodes.add(exceptionReturn); connectTo(exceptionReturn); @@ -313,7 +313,7 @@ public class ESCFG extends ACFG { for (ExceptionSource src : sources) (src.isActive() ? hangingNodes : nonExecHangingNodes).add(src.source); GraphNode node = connectTo(n, "catch (" + n.getParameter().toString() + ")"); - node.addUsedVariable(null, ACTIVE_EXCEPTION_VARIABLE); + node.addVAUseActiveException(); exceptionSourceMap.clear(); // 2. Set up as exception source ExceptionSource catchES = ExceptionSource.merge(node, sources); diff --git a/sdg-core/src/main/java/es/upv/mist/slicing/graphs/jsysdg/JSysCFG.java b/sdg-core/src/main/java/es/upv/mist/slicing/graphs/jsysdg/JSysCFG.java index b184a5f..20a9a05 100644 --- a/sdg-core/src/main/java/es/upv/mist/slicing/graphs/jsysdg/JSysCFG.java +++ b/sdg-core/src/main/java/es/upv/mist/slicing/graphs/jsysdg/JSysCFG.java @@ -5,18 +5,28 @@ import com.github.javaparser.ast.NodeList; import com.github.javaparser.ast.body.CallableDeclaration; import com.github.javaparser.ast.body.ConstructorDeclaration; import com.github.javaparser.ast.body.FieldDeclaration; +import com.github.javaparser.ast.expr.Expression; import com.github.javaparser.ast.expr.ThisExpr; import com.github.javaparser.ast.stmt.ExplicitConstructorInvocationStmt; import com.github.javaparser.ast.stmt.ReturnStmt; +import es.upv.mist.slicing.arcs.Arc; +import es.upv.mist.slicing.graphs.ClassGraph; +import es.upv.mist.slicing.graphs.ExpressionObjectTreeFinder; import es.upv.mist.slicing.graphs.cfg.CFGBuilder; import es.upv.mist.slicing.graphs.exceptionsensitive.ESCFG; import es.upv.mist.slicing.nodes.GraphNode; +import es.upv.mist.slicing.nodes.VariableAction; import es.upv.mist.slicing.nodes.io.MethodExitNode; import es.upv.mist.slicing.utils.ASTUtils; +import es.upv.mist.slicing.utils.NodeNotFoundException; +import java.util.HashSet; import java.util.LinkedList; import java.util.List; import java.util.Set; +import java.util.function.Predicate; +import java.util.stream.Collectors; +import java.util.stream.Stream; /** * An SDG that is tailored for Java, including a class graph, inheritance, @@ -28,9 +38,10 @@ public class JSysCFG extends ESCFG { throw new UnsupportedOperationException("Use build(CallableDeclaration, ClassGraph, Set)"); } - public void build(CallableDeclaration declaration, Set implicitConstructors) { + public void build(CallableDeclaration declaration, Set implicitConstructors, ClassGraph classGraph) { Builder builder = (Builder) newCFGBuilder(); builder.implicitDeclaration = implicitConstructors.contains(declaration); + builder.classGraph = classGraph; declaration.accept(builder, null); // Verify that it has been built exitNode = vertexSet().stream().filter(MethodExitNode.class::isInstance).findFirst() @@ -43,7 +54,70 @@ public class JSysCFG extends ESCFG { return new Builder(this); } + /** Given a usage of an object member, find the last definitions of that member. + * This method returns a list of variable actions, where the caller can find the member. */ + public List findLastDefinitionOfObjectMember(VariableAction usage, String member) { + return findLastVarActionsFrom(usage, def -> def.isDefinition() && def.getObjectTree().hasMember(member)); + } + + /** Given a usage of a primitive variable, find the last def actions that affect it. */ + public List findLastDefinitionOfPrimitive(VariableAction usage) { + return findLastVarActionsFrom(usage, VariableAction::isDefinition); + } + + /** Given the usage of a root object variable, find the last root definitions that affect it. */ + public List findLastDefinitionOfObjectRoot(VariableAction usage) { + return findLastVarActionsFrom(usage, VariableAction::isDefinition); + } + + /** Given a definition of a given member, locate all definitions of the same object until a definition + * containing the given member is found (not including that last one). If the member is found in the + * given definition, it will return a list with only the given definition. */ + public List findNextObjectDefinitionsFor(VariableAction definition, String member) { + if (!this.containsVertex(definition.getGraphNode())) + throw new NodeNotFoundException(definition.getGraphNode(), this); // TODO: al crear los root/resumen, las movable no se ponen en el movable + if (definition.getObjectTree().hasMember(member)) + return List.of(definition); + List list = new LinkedList<>(); + findNextVarActionsFor(new HashSet<>(), list, definition.getGraphNode(), definition, VariableAction::isDefinition, member); + return list; + } + + protected boolean findNextVarActionsFor(Set> visited, List result, + GraphNode currentNode, VariableAction var, + Predicate filter, String memberName) { + // Base case + if (visited.contains(currentNode)) + return true; + visited.add(currentNode); + + Stream stream = currentNode.getVariableActions().stream(); + if (var.getGraphNode().equals(currentNode)) + stream = stream.dropWhile(va -> va != var); + List list = stream.filter(var::matches).filter(filter).collect(Collectors.toList()); + if (!list.isEmpty()) { + boolean found = false; + for (VariableAction variableAction : list) { + if (!variableAction.isOptional() && variableAction.getObjectTree().hasMember(memberName)) { + found = true; + break; + } + result.add(variableAction); + } + if (found) + return true; + } + + // Not found: traverse backwards! + boolean allBranches = !outgoingEdgesOf(currentNode).isEmpty(); + for (Arc arc : outgoingEdgesOf(currentNode)) + if (arc.isExecutableControlFlowArc()) + allBranches &= findNextVarActionsFor(visited, result, getEdgeTarget(arc), var, filter, memberName); + return allBranches; + } + public class Builder extends ESCFG.Builder { + protected ClassGraph classGraph; /** List of implicit instructions inserted explicitly in this CFG. * They should be included in the graph as ImplicitNodes. */ protected List methodInsertedInstructions = new LinkedList<>(); @@ -106,5 +180,40 @@ public class JSysCFG extends ESCFG { .forEach(GraphNode::markAsImplicit); } } + + @Override + protected void addMethodOutput(CallableDeclaration callableDeclaration, GraphNode exit) { + super.addMethodOutput(callableDeclaration, exit); + for (VariableAction action : exit.getVariableActions()) { + if (action.getVariable().equals(VARIABLE_NAME_OUTPUT)) { + expandOutputVariable(callableDeclaration, action); + break; + } + } + } + + protected void expandOutputVariable(CallableDeclaration callableDeclaration, VariableAction useOutput) { + // Generate the full tree for the method's returned type (static) + var fields = classGraph.generateObjectTreeForReturnOf(callableDeclaration); + if (fields.isEmpty()) + return; + // Insert tree into the OutputNode + useOutput.getObjectTree().addAll(fields.get()); + // Insert tree into GraphNode nodes, the last action is always DEF(-output-) + vertexSet().stream() + .filter(gn -> gn.getAstNode() instanceof ReturnStmt) + .map(GraphNode::getVariableActions) + .map(list -> list.get(list.size() - 1)) + .map(VariableAction::getObjectTree) + .forEach(tree -> tree.addAll(fields.get())); + // Generate the assignment trees and prepare for linking + vertexSet().stream() + .filter(gn -> gn.getAstNode() instanceof ReturnStmt) + .forEach(gn -> { + Expression expr = ((ReturnStmt) gn.getAstNode()).getExpression().orElseThrow(); + ExpressionObjectTreeFinder finder = new ExpressionObjectTreeFinder(gn); + finder.locateAndMarkTransferenceToRoot(expr, -1); + }); + } } } diff --git a/sdg-core/src/main/java/es/upv/mist/slicing/graphs/jsysdg/JSysCallConnector.java b/sdg-core/src/main/java/es/upv/mist/slicing/graphs/jsysdg/JSysCallConnector.java new file mode 100644 index 0000000..4762449 --- /dev/null +++ b/sdg-core/src/main/java/es/upv/mist/slicing/graphs/jsysdg/JSysCallConnector.java @@ -0,0 +1,49 @@ +package es.upv.mist.slicing.graphs.jsysdg; + +import com.github.javaparser.ast.body.CallableDeclaration; +import es.upv.mist.slicing.graphs.CallGraph; +import es.upv.mist.slicing.graphs.cfg.CFGBuilder; +import es.upv.mist.slicing.graphs.exceptionsensitive.ExceptionSensitiveCallConnector; +import es.upv.mist.slicing.nodes.GraphNode; +import es.upv.mist.slicing.nodes.VariableAction; +import es.upv.mist.slicing.nodes.io.OutputNode; +import es.upv.mist.slicing.utils.ASTUtils; + +import java.util.List; +import java.util.function.Consumer; + +public class JSysCallConnector extends ExceptionSensitiveCallConnector { + public JSysCallConnector(JSysDG sdg) { + super(sdg); + } + + @Override + public void connectAllCalls(CallGraph callGraph) { + super.connectAllCalls(callGraph); + } + + @Override + protected void connectOutput(GraphNode> methodDeclaration, GraphNode callReturnNode) { + Consumer> action; + if (ASTUtils.declarationReturnIsObject(methodDeclaration.getAstNode())) + action = node -> connectObjectOutput(node, callReturnNode); + else + action = node -> sdg.addParameterInOutArc(node, callReturnNode); + sdg.outgoingEdgesOf(methodDeclaration).stream() + .map(sdg::getEdgeTarget) + .filter(OutputNode.class::isInstance) + .forEach(action); + } + + protected void connectObjectOutput(GraphNode methodOutputNode, GraphNode callReturnNode) { + List outputList = methodOutputNode.getVariableActions(); + assert outputList.size() == 1; + assert outputList.get(0).isUsage() && outputList.get(0).getVariable().equals(CFGBuilder.VARIABLE_NAME_OUTPUT); + List returnList = callReturnNode.getVariableActions(); + assert returnList.size() == 1; + assert returnList.get(0).isDefinition() && returnList.get(0).getVariable().equals(CFGBuilder.VARIABLE_NAME_OUTPUT); + VariableAction source = outputList.get(0); + VariableAction target = returnList.get(0); + source.applySDGTreeConnection((JSysDG) sdg, target); + } +} diff --git a/sdg-core/src/main/java/es/upv/mist/slicing/graphs/jsysdg/JSysDG.java b/sdg-core/src/main/java/es/upv/mist/slicing/graphs/jsysdg/JSysDG.java index 809c0d6..41f49d0 100644 --- a/sdg-core/src/main/java/es/upv/mist/slicing/graphs/jsysdg/JSysDG.java +++ b/sdg-core/src/main/java/es/upv/mist/slicing/graphs/jsysdg/JSysDG.java @@ -8,14 +8,21 @@ import com.github.javaparser.ast.body.ClassOrInterfaceDeclaration; import com.github.javaparser.ast.body.ConstructorDeclaration; import com.github.javaparser.ast.visitor.ModifierVisitor; import com.github.javaparser.ast.visitor.Visitable; +import es.upv.mist.slicing.graphs.ClassGraph; import es.upv.mist.slicing.graphs.augmented.PSDG; import es.upv.mist.slicing.graphs.cfg.CFG; import es.upv.mist.slicing.graphs.exceptionsensitive.ESSDG; import es.upv.mist.slicing.graphs.exceptionsensitive.ExceptionSensitiveCallConnector; import es.upv.mist.slicing.graphs.pdg.PDG; +import es.upv.mist.slicing.slicing.JSysDGSlicingAlgorithm; +import es.upv.mist.slicing.slicing.SlicingAlgorithm; import es.upv.mist.slicing.utils.NodeHashSet; public class JSysDG extends ESSDG { + @Override + protected SlicingAlgorithm createSlicingAlgorithm() { + return new JSysDGSlicingAlgorithm(this); + } @Override protected JSysDG.Builder createBuilder() { @@ -48,7 +55,7 @@ public class JSysDG extends ESSDG { @Override protected void buildCFG(CallableDeclaration declaration, CFG cfg) { - ((JSysCFG) cfg).build(declaration, newlyInsertedConstructors); + ((JSysCFG) cfg).build(declaration, newlyInsertedConstructors, ClassGraph.getInstance()); } @Override @@ -61,5 +68,10 @@ public class JSysDG extends ESSDG { assert cfg instanceof JSysCFG; return new JSysPDG((JSysCFG) cfg); } + + @Override + protected void connectCalls() { + new JSysCallConnector(JSysDG.this).connectAllCalls(callGraph); + } } } diff --git a/sdg-core/src/main/java/es/upv/mist/slicing/graphs/jsysdg/JSysPDG.java b/sdg-core/src/main/java/es/upv/mist/slicing/graphs/jsysdg/JSysPDG.java index d085229..b304486 100644 --- a/sdg-core/src/main/java/es/upv/mist/slicing/graphs/jsysdg/JSysPDG.java +++ b/sdg-core/src/main/java/es/upv/mist/slicing/graphs/jsysdg/JSysPDG.java @@ -1,5 +1,7 @@ package es.upv.mist.slicing.graphs.jsysdg; +import es.upv.mist.slicing.arcs.pdg.FlowDependencyArc; +import es.upv.mist.slicing.arcs.pdg.ObjectFlowDependencyArc; import es.upv.mist.slicing.graphs.exceptionsensitive.ESPDG; import es.upv.mist.slicing.graphs.pdg.PDG; import es.upv.mist.slicing.nodes.GraphNode; @@ -25,15 +27,77 @@ public class JSysPDG extends ESPDG { return new Builder(); } + // definicion de raiz --object-flow--> uso de raiz + protected void addObjectFlowDependencyArc(VariableAction definition, VariableAction usage) { + addEdge(graphNodeOf(definition), graphNodeOf(usage), new ObjectFlowDependencyArc()); + } + + // definicion de miembro --object-flow--> definicion de raiz + protected void addObjectFlowDependencyArc(VariableAction nextDefinitionRoot, String memberDefined, VariableAction definition) { + MemberNode defMember = definition.getObjectTree().getNodeFor(memberDefined); + addEdge(defMember, graphNodeOf(nextDefinitionRoot), new ObjectFlowDependencyArc()); + } + + // def --flow--> use || dec --flow--> def + protected void addFlowDependencyArc(VariableAction source, VariableAction target) { + addEdge(graphNodeOf(source), graphNodeOf(target), new FlowDependencyArc(source.getVariable())); + } + + // definicion de miembro --flow--> uso de miembro + protected void addFlowDependencyArc(VariableAction definition, VariableAction usage, String objMember) { + GraphNode defMember = definition.getObjectTree().getNodeFor(objMember); + GraphNode useMember = usage.getObjectTree().getNodeFor(objMember); + addEdge(defMember, useMember, new FlowDependencyArc(objMember)); + } + + protected void addValueDependencyArc(VariableAction usage, String member, GraphNode statement) { + addEdge(usage.getObjectTree().getNodeFor(member), statement, new FlowDependencyArc(member)); + } + + protected GraphNode graphNodeOf(VariableAction action) { + if (action instanceof VariableAction.Movable) + return ((VariableAction.Movable) action).getRealNode(); + if (action.getObjectTree().getMemberNode() != null) + return action.getObjectTree().getMemberNode(); + return action.getGraphNode(); + } + + @Override + public void addDataDependencyArc(VariableAction src, VariableAction tgt) { + throw new UnsupportedOperationException("Use flow or object-flow dependency"); + } + protected class Builder extends ESPDG.Builder { /** Computes all the data dependencies between {@link VariableAction variable actions} of this graph. */ @Override protected void buildDataDependency() { addSyntheticNodesToPDG(); - super.buildDataDependency(); + JSysCFG jSysCFG = (JSysCFG) cfg; + for (GraphNode node : vertexSet()) + for (VariableAction varAct : node.getVariableActions()) + if (varAct.isUsage()) { + if (varAct.isPrimitive()) + jSysCFG.findLastDefinitionOfPrimitive(varAct).forEach(def -> addFlowDependencyArc(def, varAct)); + else { + jSysCFG.findLastDefinitionOfObjectRoot(varAct).forEach(def -> addObjectFlowDependencyArc(def, varAct)); + for (String member : varAct.getObjectTree().nameIterable()) { + jSysCFG.findLastDefinitionOfObjectMember(varAct, member).forEach(def -> addFlowDependencyArc(def, varAct, member)); + addValueDependencyArc(varAct, member, node); + } + } + } else if (varAct.isDefinition()) { + if (!varAct.isSynthetic()) + jSysCFG.findDeclarationFor(varAct).ifPresent(dec -> addFlowDependencyArc(dec, varAct)); + if (!varAct.isPrimitive()) + for (String member : varAct.getObjectTree().nameIterable()) + jSysCFG.findNextObjectDefinitionsFor(varAct, member).forEach(def -> addObjectFlowDependencyArc(varAct, member, def)); + } } + @Override + protected void expandCalls() {} + protected void addSyntheticNodesToPDG() { for (GraphNode node : cfg.vertexSet()) { Deque callNodeStack = new LinkedList<>(); @@ -54,7 +118,7 @@ public class JSysPDG extends ESPDG { continue; } GraphNode parentNode; // node that represents the root of the object tree - if (va instanceof VariableAction.Movable) { + if (va instanceof VariableAction.Movable) { // TODO: there are Movables with multiple VA per movable!!! GraphNode realNode = ((VariableAction.Movable) va).getRealNode(); addVertex(realNode); connectRealNode(node, callNodeStack.peek(), realNode); @@ -62,20 +126,27 @@ public class JSysPDG extends ESPDG { } else if (va.getObjectTree().isEmpty() || node instanceof IONode) { parentNode = node; } else { - parentNode = new MemberNode(va.toString(), null); + MemberNode memberNode = new MemberNode(va.toString(), null); + va.getObjectTree().setMemberNode(memberNode); + parentNode = memberNode; addVertex(parentNode); addControlDependencyArc(node, parentNode); } // Extract the member nodes contained within the object tree for (MemberNode memberNode : va.getObjectTree().nodeIterable()) { - MemberNode memberParent = memberNode.getParent(); - assert memberParent == null || containsVertex(memberParent); + if (memberNode.getParent() == null) + memberNode.setParent(parentNode); + assert containsVertex(memberNode.getParent()); addVertex(memberNode); - addControlDependencyArc(memberParent == null ? parentNode : memberParent, memberNode); + addControlDependencyArc(memberNode.getParent(), memberNode); } } assert callNodeStack.isEmpty(); } + // Create the pre-established connections + cfg.vertexSet().stream() + .flatMap(node -> node.getVariableActions().stream()) + .forEach(va -> va.applyPDGTreeConnections(JSysPDG.this)); } } } diff --git a/sdg-core/src/main/java/es/upv/mist/slicing/graphs/sdg/CallConnector.java b/sdg-core/src/main/java/es/upv/mist/slicing/graphs/sdg/CallConnector.java index 971f373..fbcc297 100644 --- a/sdg-core/src/main/java/es/upv/mist/slicing/graphs/sdg/CallConnector.java +++ b/sdg-core/src/main/java/es/upv/mist/slicing/graphs/sdg/CallConnector.java @@ -94,10 +94,10 @@ public class CallConnector { } /** Connects a method call return node to its method output counterpart. Arc in reverse direction. */ - protected void connectOutput(GraphNode> methodDeclaration, GraphNode methodOutputNode) { + protected void connectOutput(GraphNode> methodDeclaration, GraphNode callReturnNode) { sdg.outgoingEdgesOf(methodDeclaration).stream() .map(sdg::getEdgeTarget) .filter(OutputNode.class::isInstance) - .forEach(n -> sdg.addParameterInOutArc(n, methodOutputNode)); + .forEach(n -> sdg.addParameterInOutArc(n, callReturnNode)); } } 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 cfce641..319f5f1 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 @@ -11,6 +11,7 @@ import es.upv.mist.slicing.nodes.GraphNode; 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.ObjectTree; import es.upv.mist.slicing.nodes.io.ActualIONode; import es.upv.mist.slicing.nodes.io.FormalIONode; @@ -30,10 +31,10 @@ public class InterproceduralDefinitionFinder extends InterproceduralActionFinder if (!resolved.isParameter() || !resolved.getType().isPrimitive()) { FormalIONode formalOut = FormalIONode.createFormalOut(vertex.getDeclaration(), resolved); Movable movable = new Movable(def.toUsage(cfg.getExitNode()), formalOut); - cfg.getExitNode().addMovableVariable(movable); + cfg.getExitNode().addVariableAction(movable); } FormalIONode formalIn = FormalIONode.createFormalInDecl(vertex.getDeclaration(), resolved); - cfg.getRootNode().addMovableVariable(new Movable(def.toDeclaration(cfg.getRootNode()), formalIn)); + cfg.getRootNode().addVariableAction(new Movable(def.toDeclaration(cfg.getRootNode()), formalIn)); } @Override @@ -50,7 +51,7 @@ public class InterproceduralDefinitionFinder extends InterproceduralActionFinder Set exprSet = new HashSet<>(); arg.accept(new OutNodeVariableVisitor(), exprSet); for (NameExpr nameExpr : exprSet) - movables.add(new Movable(new Definition(nameExpr, nameExpr.toString(), graphNode, def.getObjectTree()), actualOut)); + movables.add(new Movable(new Definition(nameExpr, nameExpr.toString(), graphNode, (ObjectTree) def.getObjectTree().clone()), actualOut)); } else { movables.add(new Movable(def.toDefinition(graphNode), actualOut)); } @@ -62,7 +63,7 @@ public class InterproceduralDefinitionFinder extends InterproceduralActionFinder return; String aliasedName = obtainAliasedFieldName(def, edge); ActualIONode actualOut = ActualIONode.createActualOut(edge.getCall(), resolved, null); - var movableDef = new Definition(obtainScope(edge.getCall()), aliasedName, graphNode, def.getObjectTree()); + var movableDef = new Definition(obtainScope(edge.getCall()), aliasedName, graphNode, (ObjectTree) def.getObjectTree().clone()); movables.add(new Movable(movableDef, actualOut)); } else { throw new IllegalStateException("Definition must be either from a parameter or a field!"); 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 051b8e2..f99b482 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,24 +1,26 @@ 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.declarations.ResolvedValueDeclaration; 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.VariableAction; -import es.upv.mist.slicing.nodes.VariableAction.Declaration; import es.upv.mist.slicing.nodes.VariableAction.Definition; import es.upv.mist.slicing.nodes.VariableAction.Movable; +import es.upv.mist.slicing.nodes.VariableAction.ObjectTree; import es.upv.mist.slicing.nodes.VariableAction.Usage; -import es.upv.mist.slicing.nodes.VariableVisitor; import es.upv.mist.slicing.nodes.io.ActualIONode; import es.upv.mist.slicing.nodes.io.FormalIONode; +import es.upv.mist.slicing.utils.ASTUtils; -import java.util.LinkedList; -import java.util.List; import java.util.Map; +import java.util.Objects; import java.util.function.Predicate; import java.util.stream.Stream; @@ -34,44 +36,52 @@ public class InterproceduralUsageFinder extends InterproceduralActionFinder edge, Usage use) { - List movables = new LinkedList<>(); GraphNode graphNode = edge.getGraphNode(); ResolvedValueDeclaration resolved = use.getResolvedValueDeclaration(); if (resolved.isParameter()) { - Expression argument = extractArgument(resolved.asParameter(), edge, true); - ActualIONode actualIn = ActualIONode.createActualIn(edge.getCall(), resolved, argument); - argument.accept(new VariableVisitor( - (n, exp, name) -> movables.add(new Movable(new Declaration(exp, name, graphNode), actualIn)), - (n, exp, name, expression) -> movables.add(new Movable(new Definition(exp, name, graphNode, expression), actualIn)), - (n, exp, name) -> movables.add(new Movable(new Usage(exp, name, graphNode), actualIn)) - ), VariableVisitor.Action.USE); - // a) es un objeto: movables==1 ('a', 'this') - // b) es una combinacion otras cosas ('a[1]', 'a.call()', construccion de string) - // void f(A a) { log(a.x); } <-- f(theA); // se copia el arbol de log(a) a f(theA) :D - // void f(A a) { log(a.x); } <-- f(as[1]); // no se debe copiar el arbol a as - // void f(A a) { log(a.x); } <-- f(call()); // el arbol va de log a call (por su retorno) - // TODO: this check is not specific enough - // Only copy the tree to the movables if there is only 1 movable: it is an object. - if (movables.size() == 1) - movables.get(0).getObjectTree().addAll(use.getObjectTree()); + ActualIONode actualIn = locateActualInNode(edge, resolved.getName()); + Definition def = new Definition(null, "-arg-in-", graphNode, (ObjectTree) use.getObjectTree().clone()); + Movable movDef = new Movable(def, actualIn); + actualIn.addVariableAction(movDef); + graphNode.addVariableActionAfterLastMatchingRealNode(movDef, actualIn); + ExpressionObjectTreeFinder finder = new ExpressionObjectTreeFinder(graphNode); + finder.locateAndMarkTransferenceToRoot(actualIn.getArgument(), def); } else if (resolved.isField()) { - // Known limitation: static fields - // An object creation expression input an existing object via actual-in because it creates it. - if (edge.getCall() instanceof ObjectCreationExpr) - return; - String aliasedName = obtainAliasedFieldName(use, edge); - ActualIONode actualIn = ActualIONode.createActualIn(edge.getCall(), resolved, null); - var movableUse = new Usage(obtainScope(edge.getCall()), aliasedName, graphNode, use.getObjectTree()); - movables.add(new Movable(movableUse, actualIn)); + boolean isStatic = resolved.isType() || (resolved.getType() != null && resolved.asField().isStatic()); + if (isStatic) { + // Known limitation: static fields + } else { + // An object creation expression input an existing object via actual-in because it creates it. + if (edge.getCall() instanceof ObjectCreationExpr) + return; + ActualIONode actualIn = locateActualInNode(edge, resolved.getName()); + Definition def = new Definition(null, "-scope-in-", graphNode, (ObjectTree) use.getObjectTree().clone()); + Movable movDef = new Movable(def, actualIn); + Expression scope = Objects.requireNonNullElseGet(actualIn.getArgument(), ThisExpr::new); + actualIn.addVariableAction(movDef); + graphNode.addVariableActionAfterLastMatchingRealNode(movDef, actualIn); + ExpressionObjectTreeFinder finder = new ExpressionObjectTreeFinder(graphNode); + finder.locateAndMarkTransferenceToRoot(scope, def); + } } else { throw new IllegalStateException("Definition must be either from a parameter or a field!"); } - graphNode.addActionsForCall(movables, edge.getCall(), true); + } + + protected ActualIONode locateActualInNode(CallGraph.Edge edge, String name) { + 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")); } @Override diff --git a/sdg-core/src/main/java/es/upv/mist/slicing/graphs/sdg/SDG.java b/sdg-core/src/main/java/es/upv/mist/slicing/graphs/sdg/SDG.java index a5767b2..8b1f488 100644 --- a/sdg-core/src/main/java/es/upv/mist/slicing/graphs/sdg/SDG.java +++ b/sdg-core/src/main/java/es/upv/mist/slicing/graphs/sdg/SDG.java @@ -5,7 +5,6 @@ import com.github.javaparser.ast.NodeList; import com.github.javaparser.ast.body.CallableDeclaration; import com.github.javaparser.ast.body.ConstructorDeclaration; import com.github.javaparser.ast.body.MethodDeclaration; -import com.github.javaparser.ast.stmt.ExplicitConstructorInvocationStmt; import com.github.javaparser.ast.visitor.VoidVisitorAdapter; import es.upv.mist.slicing.arcs.pdg.ControlDependencyArc; import es.upv.mist.slicing.arcs.pdg.DataDependencyArc; @@ -21,19 +20,14 @@ import es.upv.mist.slicing.graphs.cfg.CFG; import es.upv.mist.slicing.graphs.pdg.PDG; import es.upv.mist.slicing.nodes.GraphNode; import es.upv.mist.slicing.nodes.SyntheticNode; -import es.upv.mist.slicing.nodes.VariableAction; import es.upv.mist.slicing.nodes.io.ActualIONode; -import es.upv.mist.slicing.nodes.io.CallNode; import es.upv.mist.slicing.slicing.*; import es.upv.mist.slicing.utils.ASTUtils; import java.util.Collection; -import java.util.List; import java.util.Map; import java.util.Optional; -import static es.upv.mist.slicing.graphs.cfg.CFGBuilder.VARIABLE_NAME_OUTPUT; - /** * The System Dependence Graph represents the statements of a program in * a graph, connecting statements according to their {@link ControlDependencyArc control}, @@ -92,6 +86,16 @@ public class SDG extends Graph implements Sliceable, Buildable node) { + if (node instanceof SyntheticNode) + return false; + for (CFG cfg : cfgMap.values()) + if (cfg.containsVertex(node)) + return cfg.isPredicate(node); + throw new IllegalArgumentException("Node " + node.getId() + "'s associated CFG cannot be found!"); + } + public void addCallArc(GraphNode from, GraphNode> to) { this.addEdge(from, to, new CallArc()); } @@ -108,13 +112,12 @@ public class SDG extends Graph implements Sliceable, Buildable nodeList) { // See creation strategy at http://kaz2.dsic.upv.es:3000/Fzg46cQvT1GzHQG9hFnP1g#Using-data-flow-in-the-SDG // This ordering cannot be altered, as each step requires elements from the previous one. - classGraph = createClassGraph(nodeList); // 0 + createClassGraph(nodeList); // 0 buildCFGs(nodeList); // 1 callGraph = createCallGraph(nodeList); // 2 dataFlowAnalysis(); // 3 @@ -149,43 +152,21 @@ public class SDG extends Graph implements Sliceable, Buildable nodeList) { - CallGraph callGraph = new CallGraph(cfgMap, classGraph); + CallGraph callGraph = new CallGraph(cfgMap, ClassGraph.getInstance()); callGraph.build(nodeList); return callGraph; } /** Create class graph from the list of compilation units. */ - protected ClassGraph createClassGraph(NodeList nodeList){ - ClassGraph classGraph = new ClassGraph(); - classGraph.build(nodeList); - return classGraph; + protected void createClassGraph(NodeList nodeList){ + ClassGraph.getNewInstance().build(nodeList); } - /** Perform interprocedural analyses to determine the actual, formal and call return nodes. */ + /** Perform interprocedural analyses to determine the actual and formal nodes. */ protected void dataFlowAnalysis() { new InterproceduralDefinitionFinder(callGraph, cfgMap).save(); // 3.1 new InterproceduralUsageFinder(callGraph, cfgMap).save(); // 3.2 - insertCallOutput(); // 3.3 - } - - /** Insert {@link CallNode.Return call return} nodes onto all appropriate calls. */ - protected void insertCallOutput() { - for (CallGraph.Edge edge : callGraph.edgeSet()) { - if (ASTUtils.resolvableIsVoid(edge.getCall())) - continue; - // We handle super()/this() in VariableVisitor - if (edge.getCall() instanceof ExplicitConstructorInvocationStmt) - continue; - GraphNode graphNode = edge.getGraphNode(); - // A node defines -output- - var def = new VariableAction.Definition(null, VARIABLE_NAME_OUTPUT, graphNode); - var defMov = new VariableAction.Movable(def, CallNode.Return.create(edge.getCall())); - graphNode.addActionsForCall(List.of(defMov), edge.getCall(), false); - // The container of the call uses -output- - var use = new VariableAction.Usage(null, VARIABLE_NAME_OUTPUT, graphNode); - graphNode.addActionsAfterCall(edge.getCall(), use); - } } /** Build a PDG per declaration, based on the CFGs built previously and enhanced by data analyses. */ diff --git a/sdg-core/src/main/java/es/upv/mist/slicing/nodes/GraphNode.java b/sdg-core/src/main/java/es/upv/mist/slicing/nodes/GraphNode.java index 58c936b..ffdb983 100644 --- a/sdg-core/src/main/java/es/upv/mist/slicing/nodes/GraphNode.java +++ b/sdg-core/src/main/java/es/upv/mist/slicing/nodes/GraphNode.java @@ -9,10 +9,9 @@ import es.upv.mist.slicing.graphs.pdg.PDG; import es.upv.mist.slicing.graphs.sdg.SDG; import es.upv.mist.slicing.utils.ASTUtils; -import java.util.Collections; -import java.util.LinkedList; -import java.util.List; -import java.util.Objects; +import java.util.*; + +import static es.upv.mist.slicing.graphs.exceptionsensitive.ESCFG.ACTIVE_EXCEPTION_VARIABLE; /** * Represents a node in the various graphs ({@link CFG CFG}, {@link PDG PDG} and {@link SDG SDG}), @@ -32,6 +31,9 @@ public class GraphNode implements Comparable> { protected final List variableActions; /** The method calls contained */ protected final List> methodCalls = new LinkedList<>(); + /** Nodes that are generated as a result of the instruction represented by this GraphNode and that may + * be included in Movable actions. */ + protected final Set> syntheticNodesInMovables = new HashSet<>(); /** @see #isImplicitInstruction() */ protected boolean isImplicit = false; @@ -149,20 +151,63 @@ public class GraphNode implements Comparable> { throw new IllegalArgumentException("Could not find markers for " + call.resolve().getSignature() + " in " + this); } - /** Create and append a declaration of a variable to the list of actions of this node. */ - public void addDeclaredVariable(Expression variable, String realName) { - variableActions.add(new VariableAction.Declaration(variable, realName, this)); + public void addSyntheticNode(SyntheticNode node) { + syntheticNodesInMovables.add(node); + } + + public Collection> getSyntheticNodesInMovables() { + return Collections.unmodifiableSet(syntheticNodesInMovables); + } + + public void addVariableAction(VariableAction action) { + if (action instanceof VariableAction.Movable) + syntheticNodesInMovables.add(((VariableAction.Movable) action).getRealNode()); + variableActions.add(action); } - /** Create and append a definition of a variable to the list of actions of this node. */ - public void addDefinedVariable(Expression variable, String realName, Expression expression) { - VariableAction.Definition def = new VariableAction.Definition(variable, realName, this, expression); + @SuppressWarnings("unchecked") + public void addVariableActionAfterLastMatchingRealNode(VariableAction.Movable action, SyntheticNode realNode) { + boolean found = false; + for (int i = 0; i < variableActions.size(); i++) { + VariableAction a = variableActions.get(i); + if (a instanceof VariableAction.Movable + && ((VariableAction.Movable) a).getRealNode() == realNode) { + found = true; + } else if (found) { + // The previous one matched, this one does not. Add before this one. + variableActions.add(i, action); + return; + } + } + // If the last one matched, add to the end + if (found) + variableActions.add(action); + else { + assert syntheticNodesInMovables.contains(realNode); + addActionsForCall(List.of(action), (Resolvable) realNode.getAstNode(), true); + } + } + + public VariableAction.CallMarker locateCallForVariableAction(VariableAction action) { + VariableAction.CallMarker marker = null; + for (VariableAction a : variableActions) { + if (a instanceof VariableAction.CallMarker && ((VariableAction.CallMarker) a).isEnter()) + marker = (VariableAction.CallMarker) a; + if (action == a) { + assert marker != null; + return marker; + } + } + throw new IllegalArgumentException("Action not found within node"); + } + + public void addVADefineActiveException(Expression expression) { + VariableAction.Definition def = new VariableAction.Definition(null, ACTIVE_EXCEPTION_VARIABLE, this, expression); variableActions.add(def); } - /** Create and append a usage of a variable to the list of actions of this node. */ - public void addUsedVariable(Expression variable, String realName) { - VariableAction.Usage use = new VariableAction.Usage(variable, realName, this); + public void addVAUseActiveException() { + VariableAction.Usage use = new VariableAction.Usage(null, ACTIVE_EXCEPTION_VARIABLE, this); variableActions.add(use); } @@ -172,11 +217,6 @@ public class GraphNode implements Comparable> { variableActions.add(new VariableAction.CallMarker(call, this, enter)); } - /** Create and append a movable variable action to the list of actions of this node. */ - public void addMovableVariable(VariableAction.Movable movable) { - variableActions.add(movable); - } - // ============================================================ // ======================= Overridden ======================= // ============================================================ diff --git a/sdg-core/src/main/java/es/upv/mist/slicing/nodes/VariableAction.java b/sdg-core/src/main/java/es/upv/mist/slicing/nodes/VariableAction.java index 2845a5f..70647db 100644 --- a/sdg-core/src/main/java/es/upv/mist/slicing/nodes/VariableAction.java +++ b/sdg-core/src/main/java/es/upv/mist/slicing/nodes/VariableAction.java @@ -2,23 +2,36 @@ package es.upv.mist.slicing.nodes; import com.github.javaparser.ast.expr.Expression; import com.github.javaparser.ast.expr.FieldAccessExpr; +import com.github.javaparser.ast.stmt.ReturnStmt; import com.github.javaparser.resolution.Resolvable; +import com.github.javaparser.resolution.UnsolvedSymbolException; import com.github.javaparser.resolution.declarations.ResolvedMethodLikeDeclaration; import com.github.javaparser.resolution.declarations.ResolvedValueDeclaration; import com.github.javaparser.resolution.types.ResolvedType; import es.upv.mist.slicing.arcs.Arc; import es.upv.mist.slicing.arcs.pdg.DataDependencyArc; +import es.upv.mist.slicing.arcs.pdg.FlowDependencyArc; +import es.upv.mist.slicing.arcs.pdg.ObjectFlowDependencyArc; +import es.upv.mist.slicing.arcs.sdg.ParameterInOutArc; import es.upv.mist.slicing.graphs.Graph; +import es.upv.mist.slicing.graphs.jsysdg.JSysDG; +import es.upv.mist.slicing.graphs.jsysdg.JSysPDG; import es.upv.mist.slicing.graphs.pdg.PDG; +import es.upv.mist.slicing.nodes.io.CallNode; +import es.upv.mist.slicing.nodes.io.OutputNode; import es.upv.mist.slicing.nodes.oo.MemberNode; +import es.upv.mist.slicing.utils.ASTUtils; import es.upv.mist.slicing.utils.Utils; import java.lang.reflect.InvocationTargetException; import java.util.*; +import java.util.function.Supplier; import java.util.regex.Matcher; import java.util.regex.Pattern; import java.util.stream.Collectors; +import static es.upv.mist.slicing.graphs.cfg.CFGBuilder.VARIABLE_NAME_OUTPUT; + /** An action upon a variable (e.g. usage, definition, declaration) */ public abstract class VariableAction { protected static final String VARIABLE_PATTERN = "([a-zA-Z][a-zA-Z0-9_]*|_[a-zA-Z0-9_]+)"; @@ -32,6 +45,13 @@ public abstract class VariableAction { protected boolean optional = false; protected ResolvedValueDeclaration resolvedVariableCache; + /// Variables that control the automatic connection of ObjectTrees between VariableAction + /** A list of pairs representing connections to be made between trees in the PDG. + * The variable action that contains the tree we must connect to in the PDG. + * The string, or member where the tree connection must start (in PDG). E.g.: our tree is "a.b.c" and this variable is "a", + * the members "a.b" and "a.b.c" will be connected to "b" and "b.c" in treeConnectionTarget's tree.. */ + protected final List pdgTreeConnections = new LinkedList<>(); + public VariableAction(Expression variable, String realName, GraphNode graphNode) { this(variable, realName, graphNode, new ObjectTree()); } @@ -52,7 +72,20 @@ public abstract class VariableAction { /** Add a field of this object, such that the same action performed on the object * is applied to this field too. Fields of fields may be specified separated by dots. */ public void addObjectField(String fieldName) { - objectTree.addField(fieldName); + getObjectTree().addField(fieldName); + } + + public void setPDGTreeConnectionTo(VariableAction targetAction, String sourcePrefixWithoutRoot, String targetPrefixWithoutRoot) { + pdgTreeConnections.add(new ObjectTreeConnection(this, targetAction, sourcePrefixWithoutRoot, targetPrefixWithoutRoot)); + } + + public void applyPDGTreeConnections(JSysPDG pdg) { + pdgTreeConnections.forEach(c -> c.applyPDG(pdg)); + } + + public void applySDGTreeConnection(JSysDG sdg, VariableAction targetAction) { + ObjectTreeConnection connection = new ObjectTreeConnection(this, targetAction, "", ""); + connection.applySDG(sdg); } public VariableAction getRootAction() { @@ -60,7 +93,7 @@ public abstract class VariableAction { assert variable == null || variable.isNameExpr() || variable.isFieldAccessExpr() || variable.isThisExpr(); if (this instanceof Movable) { Movable movable = (Movable) this; - return new Movable(movable.inner.getRootAction(), (SyntheticNode) graphNode); + return new Movable(movable.inner.getRootAction(), movable.getRealNode()); } Expression nVar; String nRealName = getRootVariable(); @@ -117,6 +150,18 @@ public abstract class VariableAction { return !getVariable().matches(FIELD_PATTERN); } + public boolean isPrimitive() { // if (otherwise) handleAsCallReturn + if (realName.equals(VARIABLE_NAME_OUTPUT)) { + if (graphNode.getAstNode() instanceof ReturnStmt) { + return !ASTUtils.declarationReturnIsObject(ASTUtils.getDeclarationNode(graphNode.getAstNode())); + } else { + return ASTUtils.resolvableIsPrimitive(graphNode.locateCallForVariableAction(this).getCall()); + } + } + ResolvedValueDeclaration resolved = getResolvedValueDeclaration(); // if (graphNode.getAstNode() instanceof ReturnStmt) handleAsTypeOfCurrentMethod + return resolved.getType() != null && resolved.getType().isPrimitive(); + } + public String getVariable() { return realName; } @@ -141,9 +186,33 @@ public abstract class VariableAction { public ResolvedValueDeclaration getResolvedValueDeclaration() { if (resolvedVariableCache == null) { if (variable instanceof Resolvable) { - var resolved = ((Resolvable) variable).resolve(); - if (resolved instanceof ResolvedValueDeclaration) - resolvedVariableCache = (ResolvedValueDeclaration) resolved; + try { + var resolved = ((Resolvable) variable).resolve(); + if (resolved instanceof ResolvedValueDeclaration) + resolvedVariableCache = (ResolvedValueDeclaration) resolved; + } catch (UnsolvedSymbolException ignored) { + resolvedVariableCache = new ResolvedValueDeclaration() { + @Override + public ResolvedType getType() { + return null; + } + + @Override + public String getName() { + return realName; + } + + @Override + public boolean isType() { + return true; + } + + @Override + public boolean isField() { + return true; + } + }; + } } if (resolvedVariableCache == null) resolvedVariableCache = new ResolvedValueDeclaration() { @@ -377,17 +446,24 @@ public abstract class VariableAction { return inner.getObjectTree(); } - @Override - public void addObjectField(String fieldName) { - throw new UnsupportedOperationException("Movable actions don't support the object tree"); - } - /** The final location of this action. This node may not yet be present * in the graph, if {@link #move(Graph)} has not been invoked. */ public SyntheticNode getRealNode() { return realNode; } + @Override + public boolean isPrimitive() { + if (realName.equals(VARIABLE_NAME_OUTPUT)) { + if (realNode instanceof CallNode.Return) { + return ASTUtils.resolvableIsPrimitive((Resolvable) realNode.getAstNode()); + } else if (realNode instanceof OutputNode) { + return !ASTUtils.declarationReturnIsObject(ASTUtils.getDeclarationNode(realNode.getAstNode())); + } + } + return super.isPrimitive(); + } + /** Move the action from its node to its real node. The real node is added to the graph, * the action is deleted from its original node's list, a copy is created with the real * target and any {@link DataDependencyArc} is relocated to match this change. */ @@ -396,11 +472,11 @@ public abstract class VariableAction { VariableAction newAction; try { if (inner instanceof Definition && inner.asDefinition().getExpression() != null) - newAction = inner.getClass().getConstructor(Expression.class, String.class, GraphNode.class, Expression.class) - .newInstance(variable, realName, realNode, inner.asDefinition().expression); + newAction = inner.getClass().getConstructor(Expression.class, String.class, GraphNode.class, Expression.class, ObjectTree.class) + .newInstance(variable, realName, realNode, inner.asDefinition().expression, getObjectTree()); else - newAction = inner.getClass().getConstructor(Expression.class, String.class, GraphNode.class) - .newInstance(variable, realName, realNode); + newAction = inner.getClass().getConstructor(Expression.class, String.class, GraphNode.class, ObjectTree.class) + .newInstance(variable, realName, realNode, getObjectTree()); } catch (InstantiationException | IllegalAccessException | InvocationTargetException | NoSuchMethodException e) { throw new UnsupportedOperationException("The VariableAction constructor has changed!", e); } @@ -477,22 +553,33 @@ public abstract class VariableAction { } public static class ObjectTree implements Cloneable { - private static final Pattern FIELD_SPLIT = Pattern.compile("^(?(([_0-9A-Za-z]+\\.)*this)|([_0-9A-Za-z]+))(\\.(?.+))?$"); + private static final String ROOT_NAME = "-root-"; + private static final Pattern FIELD_SPLIT = Pattern.compile("^(?(([_0-9A-Za-z]+\\.)*this)|([_0-9A-Za-z]+)|(-root-))(\\.(?.+))?$"); - private final String memberName; - private final MemberNode memberNode; private final Map childrenMap = new HashMap<>(); + private MemberNode memberNode; + public ObjectTree() { - memberName = null; memberNode = null; } private ObjectTree(String memberName, ObjectTree parent) { - this.memberName = memberName; this.memberNode = new MemberNode(memberName, parent.memberNode); } + protected String getMemberName() { + return memberNode == null ? ROOT_NAME : memberNode.getLabel(); + } + + public MemberNode getMemberNode() { + return memberNode; + } + + public void setMemberNode(MemberNode memberNode) { + this.memberNode = memberNode; + } + public void addField(String fieldName) { String members = removeRoot(fieldName); addNonRootField(members); @@ -518,6 +605,37 @@ public abstract class VariableAction { childrenMap.put(entry.getKey(), entry.getValue().clone(this)); } + /** + * Copies a subtree from source into another subtree in target. + * @param source The source of the nodes. + * @param target The tree where nodes will be added + * @param sourcePrefix The prefix to be consumed before copying nodes. Without root. + * @param targetPrefix The prefix to be consumed before copying nodes. Without root. + */ + public static void copyTree(ObjectTree source, ObjectTree target, String sourcePrefix, String targetPrefix) { + ObjectTree a = source.findObjectTreeOfMember(sourcePrefix); + ObjectTree b = target.findObjectTreeOfMember(targetPrefix); + a.addAll(b); + } + + private ObjectTree findObjectTreeOfMember(String member) { + ObjectTree result = this; + while (!member.isEmpty()) { + int firstDot = member.indexOf('.'); + String first, rest; + if (firstDot != -1) { + first = member.substring(0, firstDot); + rest = member.substring(firstDot + 1); + } else { + first = member; + rest = ""; + } + result = result.childrenMap.get(first); + member = rest; + } + return result; + } + public boolean hasMember(String member) { String field = removeRoot(member); return hasNonRootMember(field); @@ -567,7 +685,19 @@ public abstract class VariableAction { @Override public String next() { - return it.next().memberName; + ObjectTree element = it.next(); + StringBuilder builder = new StringBuilder(); + MemberNode node = element.memberNode; + if (node == null) + return ROOT_NAME; + else + builder.append(node.getLabel()); + while (node.getParent() != null && node.getParent() instanceof MemberNode && node.getParent().getLabel().matches("^(USE|DEF|DEC)\\{")) { + node = (MemberNode) node.getParent(); + builder.insert(0, '.'); + builder.insert(0, node.getLabel()); + } + return builder.insert(0, "-root-.").toString(); } }; } @@ -614,6 +744,11 @@ public abstract class VariableAction { }; } + private Iterable treeIterable() { + return this::treeIterator; + } + + @SuppressWarnings("MethodDoesntCallSuperMethod") @Override public Object clone() { ObjectTree clone = new ObjectTree(); @@ -623,31 +758,88 @@ public abstract class VariableAction { } private ObjectTree clone(ObjectTree parent) { - ObjectTree clone = new ObjectTree(memberName, parent); + ObjectTree clone = new ObjectTree(getMemberName(), parent); for (Map.Entry entry : childrenMap.entrySet()) clone.childrenMap.put(entry.getKey(), entry.getValue().clone(clone)); return clone; } - protected String removeRoot(String fieldWithRoot) { + public static String removeRoot(String fieldWithRoot) { Matcher matcher = FIELD_SPLIT.matcher(fieldWithRoot); if (matcher.matches() && matcher.group("fields") != null) return matcher.group("fields"); throw new IllegalArgumentException("Field should be of the form ., .this., where may not contain dots."); } + public static String removeFields(String fieldWithRoot) { + Matcher matcher = FIELD_SPLIT.matcher(fieldWithRoot); + if (matcher.matches() && matcher.group("root") != null) + return matcher.group("root"); + throw new IllegalArgumentException("Field should be of the form ., .this., where may not contain dots."); + } + @Override public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; ObjectTree tree = (ObjectTree) o; - return Objects.equals(memberName, tree.memberName) && + return Objects.equals(getMemberName(), tree.getMemberName()) && childrenMap.values().equals(tree.childrenMap.values()); } @Override public int hashCode() { - return Objects.hash(memberName, childrenMap); + return Objects.hash(getMemberName(), childrenMap); } } + + static class ObjectTreeConnection { + protected final VariableAction sourceAction; + protected final VariableAction targetAction; + protected final String sourceMember; + protected final String targetMember; + + protected boolean applied = false; + + public ObjectTreeConnection(VariableAction sourceAction, VariableAction targetAction, String sourceMember, String targetMember) { + this.sourceAction = sourceAction; + this.targetAction = targetAction; + this.sourceMember = sourceMember; + this.targetMember = targetMember; + } + + public void applySDG(JSysDG graph) { + if (!applied) { + connectTrees(graph, ParameterInOutArc::new, ParameterInOutArc.ObjectFlow::new); + applied = true; + } + } + + public void applyPDG(JSysPDG graph) { + if (!applied) { + connectTrees(graph, FlowDependencyArc::new, ObjectFlowDependencyArc::new); + applied = true; + } + } + + protected void connectTrees(Graph graph, Supplier flowSupplier, Supplier objFlowSupplier) { + ObjectTree source = sourceAction.getObjectTree().findObjectTreeOfMember(sourceMember); + ObjectTree target = targetAction.getObjectTree().findObjectTreeOfMember(targetMember); + assert sourceMember.isEmpty() || source.getMemberName() != null; + assert targetMember.isEmpty() || target.getMemberName() != null; + GraphNode rootSrc = source.getMemberNode() != null ? source.getMemberNode() : sourceAction.getGraphNode(); + GraphNode rootTgt = target.getMemberNode() != null ? target.getMemberNode() : targetAction.getGraphNode(); + graph.addEdge(rootSrc, rootTgt, objFlowSupplier.get()); + for (ObjectTree tree : target.treeIterable()) { + MemberNode src = source.getNodeForNonRoot(tree.getMemberName()); + MemberNode tgt = tree.getMemberNode(); + if (tree.childrenMap.isEmpty()) + graph.addEdge(src, tgt, flowSupplier.get()); + else + graph.addEdge(src, tgt, objFlowSupplier.get()); + } + } + + + } } 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 8415bd2..f4fdeaf 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 @@ -1,19 +1,26 @@ package es.upv.mist.slicing.nodes; import com.github.javaparser.ast.Node; +import com.github.javaparser.ast.body.CallableDeclaration; import com.github.javaparser.ast.body.FieldDeclaration; 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.CatchClause; -import com.github.javaparser.ast.stmt.ExplicitConstructorInvocationStmt; -import com.github.javaparser.ast.stmt.ForEachStmt; +import com.github.javaparser.ast.nodeTypes.NodeWithArguments; +import com.github.javaparser.ast.stmt.*; +import com.github.javaparser.ast.visitor.VoidVisitorAdapter; import com.github.javaparser.resolution.Resolvable; import com.github.javaparser.resolution.UnsolvedSymbolException; +import com.github.javaparser.resolution.declarations.AssociableToAST; import com.github.javaparser.resolution.declarations.ResolvedMethodLikeDeclaration; import com.github.javaparser.resolution.declarations.ResolvedTypeDeclaration; import com.github.javaparser.resolution.declarations.ResolvedValueDeclaration; +import com.github.javaparser.symbolsolver.javaparsermodel.declarations.JavaParserMethodDeclaration; +import es.upv.mist.slicing.graphs.ClassGraph; +import es.upv.mist.slicing.graphs.ExpressionObjectTreeFinder; import es.upv.mist.slicing.graphs.GraphNodeContentVisitor; +import es.upv.mist.slicing.nodes.VariableAction.ObjectTree; +import es.upv.mist.slicing.nodes.io.ActualIONode; import es.upv.mist.slicing.nodes.io.CallNode; import es.upv.mist.slicing.utils.ASTUtils; import es.upv.mist.slicing.utils.Logger; @@ -21,9 +28,10 @@ import es.upv.mist.slicing.utils.Logger; import java.util.Deque; import java.util.LinkedList; import java.util.List; -import java.util.Objects; +import java.util.Optional; import static es.upv.mist.slicing.graphs.cfg.CFGBuilder.VARIABLE_NAME_OUTPUT; +import static es.upv.mist.slicing.nodes.VariableVisitor.Action.*; /** A graph node visitor that extracts the actions performed in a given GraphNode. An initial action mode can * be set, to consider variables found a declaration, definition or usage (default). @@ -50,39 +58,21 @@ public class VariableVisitor extends GraphNodeContentVisitor definitionStack = new LinkedList<>(); - - /** A variable visitor that will add each action to the list of actions of the graph node. - * The entry-point for this graph MUST be {@link #startVisit(GraphNode)} or {@link #startVisit(GraphNode, Action)} */ - public VariableVisitor() { - this(GraphNode::addDeclaredVariable, GraphNode::addDefinedVariable, GraphNode::addUsedVariable); - } - - /** A variable visitor that will perform the given actions when a variable is found. A node can accept this visitor, - * but calls will be ignored if the entry-point is not {@link #startVisit(GraphNode)} or {@link #startVisit(GraphNode, Action)}. - * The arguments are the actions to be performed when an action is found in the corresponding node. */ - public VariableVisitor(DeclarationConsumer declConsumer, DefinitionConsumer defConsumer, UsageConsumer useConsumer) { - this.declConsumer = Objects.requireNonNullElse(declConsumer, DeclarationConsumer.defaultConsumer()); - this.defConsumer = Objects.requireNonNullElse(defConsumer, DefinitionConsumer.defaultConsumer()); - this.useConsumer = Objects.requireNonNullElse(useConsumer, UsageConsumer.defaultConsumer()); - } + /** If this stack is non-empty, every action created must be of type Movable, and its real node must be + * the top of this stack. Used for actual-in nodes. */ + protected final Deque> realNodeStack = new LinkedList<>(); public void visitAsDefinition(Node node, Expression value, Action action) { definitionStack.push(value); - node.accept(this, action.or(Action.DEFINITION)); + node.accept(this, action.or(DEFINITION)); definitionStack.pop(); } @Override public void startVisit(GraphNode node) { - startVisit(node, Action.USE); + startVisit(node, USE); groupActionsByRoot(node); } @@ -96,6 +86,11 @@ public class VariableVisitor extends GraphNodeContentVisitor realNameWithoutRootList = new LinkedList<>(); + n.getTarget().accept(new VoidVisitorAdapter() { + @Override + public void visit(NameExpr nameExpr, Void arg) { + String realName = getRealName(nameExpr); + definitionStack.push(n.getValue()); + if (!realName.contains(".")) { + acceptAction(nameExpr, realName, DEFINITION); + realNameWithoutRootList.add(""); + } else { + String root = ObjectTree.removeFields(realName); + acceptAction(root, DEFINITION); + List list = graphNode.getVariableActions(); + VariableAction def = list.get(graphNode.getVariableActions().size() - 1); + def.getObjectTree().addField(realName); + realNameWithoutRootList.add(realName); + } + definitionStack.pop(); + } + + @Override + public void visit(FieldAccessExpr fieldAccessExpr, Void arg) { + Expression scope = fieldAccessExpr.getScope(); + boolean traverse = true; + while (traverse) { + if (scope.isFieldAccessExpr()) + scope = scope.asFieldAccessExpr().getScope(); + else if (scope.isEnclosedExpr()) + scope = scope.asEnclosedExpr().getInner(); + else if (scope.isCastExpr()) + scope = scope.asCastExpr().getExpression(); + else + traverse = false; + } + if (!scope.isNameExpr() && !scope.isThisExpr()) + throw new IllegalStateException("only valid assignments are this[.]+ =, and [.]+"); + String realName = getRealName(fieldAccessExpr); + String root = ObjectTree.removeFields(realName); + definitionStack.push(n.getValue()); + acceptAction(root, DEFINITION); + definitionStack.pop(); + List list = graphNode.getVariableActions(); + VariableAction def = list.get(graphNode.getVariableActions().size() - 1); + def.getObjectTree().addField(realName); + realNameWithoutRootList.add(ObjectTree.removeRoot(realName)); + } + + @Override + public void visit(ArrayAccessExpr n, Void arg) { + throw new UnsupportedOperationException("Arrays are not yet supported as target of assignment."); + } + }, null); + assert realNameWithoutRootList.size() == 1; + List list = graphNode.getVariableActions(); + ExpressionObjectTreeFinder finder = new ExpressionObjectTreeFinder(graphNode); + finder.handleAssignExpr(n, list.get(graphNode.getVariableActions().size() - 1), realNameWithoutRootList.get(0)); + } } @Override @@ -198,14 +284,14 @@ public class VariableVisitor extends GraphNodeContentVisitor { init.accept(this, action); - defConsumer.acceptDefinition(graphNode, null, realName, init); + acceptActionNullDefinition(v.getNameAsString()); }); + v.accept(this, action); } } @Override public void visit(FieldDeclaration n, Action action) { for (VariableDeclarator v : n.getVariables()) { - String realName; - realName = getRealNameForFieldDeclaration(v); - declConsumer.acceptDeclaration(graphNode, null, realName); + String realName = getRealNameForFieldDeclaration(v); + acceptAction(realName, DECLARATION); Expression init = v.getInitializer().orElseGet(() -> ASTUtils.initializerForField(n)); init.accept(this, action); - defConsumer.acceptDefinition(graphNode, null, realName, init); + definitionStack.push(init); + acceptAction(realName, DEFINITION); + definitionStack.pop(); + v.accept(this, action); } } + @Override + public void visit(VariableDeclarator n, Action arg) { + if (n.getType().isClassOrInterfaceType() && n.getInitializer().isPresent()) + new ExpressionObjectTreeFinder(graphNode).handleVariableDeclarator(n); + } + @Override public void visit(CatchClause n, Action arg) { - n.getParameter().accept(this, arg.or(Action.DECLARATION)); + n.getParameter().accept(this, arg.or(DECLARATION)); n.getBody().accept(this, arg); } @Override public void visit(Parameter n, Action arg) { - declConsumer.acceptDeclaration(graphNode, null, n.getNameAsString()); - defConsumer.acceptDefinition(graphNode, null, n.getNameAsString(), null); // TODO: improve initializer + acceptAction(n.getNameAsString(), DECLARATION); + acceptActionNullDefinition(n.getNameAsString()); } // ======================================================================= // ================================ CALLS ================================ // ======================================================================= - // If the call will be linked (there is an AST node), skip parameters, add markers and add to node // TODO: non-linked calls should mark both parameters and scope as DEF+USE (problem: 'System.out.println(z)') @Override @@ -264,20 +356,11 @@ public class VariableVisitor extends GraphNodeContentVisitor call, Action arg) { + protected boolean visitCall(Resolvable call, Action action) { + // If we don't have the AST for the call, we should visit the rest of the call. if (ASTUtils.shouldVisitArgumentsForMethodCalls(call, graphNode)) return true; + CallableDeclaration decl = ASTUtils.getResolvedAST(call.resolve()).orElseThrow(); + // Start graphNode.addCallMarker(call, true); - if (call instanceof ExplicitConstructorInvocationStmt) { - declareThis((ExplicitConstructorInvocationStmt) call); + // Scope + if (call instanceof ExplicitConstructorInvocationStmt) + acceptAction(getFQClassName((ExplicitConstructorInvocationStmt) call) + ".this", DECLARATION); + if (call instanceof MethodCallExpr && !((JavaParserMethodDeclaration) call.resolve()).isStatic()) { + ActualIONode scopeIn = ActualIONode.createActualIn(call, getFQClassName(decl) + ".this", ((MethodCallExpr) call).getScope().orElse(null)); + graphNode.addSyntheticNode(scopeIn); + realNodeStack.push(scopeIn); + ASTUtils.getResolvableScope(call).ifPresentOrElse( + scope -> scope.accept(this, action), + () -> acceptAction(getFQClassName(decl) + ".this", USE)); + realNodeStack.pop(); } - ASTUtils.getResolvableScope(call).ifPresent(s -> s.accept(this, arg)); + // Args + NodeWithArguments callWithArgs = (NodeWithArguments) call; + for (int i = 0; i < callWithArgs.getArguments().size(); i++) { + Expression argument = callWithArgs.getArguments().get(i); + ActualIONode actualIn = ActualIONode.createActualIn(call, decl.getParameter(i).getNameAsString(), argument); + graphNode.addSyntheticNode(actualIn); + realNodeStack.push(actualIn); + argument.accept(this, action); + realNodeStack.pop(); + } + // Return + insertOutputDefAndUse(call); + // End graphNode.addCallMarker(call, false); - return false; + return false; // We have manually visited each element, don't call super.visit(...) + } + + protected void insertOutputDefAndUse(Resolvable call) { + if (ASTUtils.resolvableIsVoid(call)) + return; + Expression callExpr = call instanceof Expression ? (Expression) call : null; + // A node defines -output- + var fields = getFieldsForReturn(call); + VariableAction.Definition def; + if (fields.isPresent()) + def = new VariableAction.Definition(callExpr, VARIABLE_NAME_OUTPUT, graphNode, (ObjectTree) fields.get().clone()); + else + def = new VariableAction.Definition(callExpr, VARIABLE_NAME_OUTPUT, graphNode); + var defMov = new VariableAction.Movable(def, CallNode.Return.create(call)); + graphNode.addVariableAction(defMov); + // The container of the call uses -output-, unless the call is wrapped in an ExpressionStmt + Optional parentNode = ((Node) call).getParentNode(); + if (parentNode.isEmpty() || !(parentNode.get() instanceof ExpressionStmt)) { + VariableAction.Usage use; + if (fields.isPresent()) + use = new VariableAction.Usage(callExpr, VARIABLE_NAME_OUTPUT, graphNode, (ObjectTree) fields.get().clone()); + else + use = new VariableAction.Usage(callExpr, VARIABLE_NAME_OUTPUT, graphNode); + graphNode.addVariableAction(use); + } } - /** Adds a declaration for the variable 'this'. */ - protected void declareThis(ExplicitConstructorInvocationStmt call) { - String variableName = getFQClassName(call) + ".this"; - declConsumer.acceptDeclaration(graphNode, null, variableName); + protected Optional getFieldsForReturn(Resolvable call) { + ResolvedMethodLikeDeclaration resolved = call.resolve(); + if (resolved instanceof AssociableToAST) { + Optional n = ((AssociableToAST) resolved).toAst(); + if (n.isPresent() && n.get() instanceof CallableDeclaration) + return ClassGraph.getInstance().generateObjectTreeForReturnOf((CallableDeclaration) n.get()); + } + return Optional.empty(); } /** Obtains the fully qualified class name of the class that contains an AST node. */ @@ -325,9 +461,24 @@ public class VariableVisitor extends GraphNodeContentVisitor graphNode, Expression variable, String realName); - - static DeclarationConsumer defaultConsumer() { - return (a, b, c) -> {}; - } - } - - @FunctionalInterface - public interface DefinitionConsumer { - void acceptDefinition(GraphNode graphNode, Expression variable, String realName, Expression valueAssigned); - - static DefinitionConsumer defaultConsumer() { - return (a, b, c, d) -> {}; - } - } - - @FunctionalInterface - public interface UsageConsumer { - void acceptUsage(GraphNode graphNode, Expression variable, String realName); - - static UsageConsumer defaultConsumer() { - return (a, b, c) -> {}; - } - } } diff --git a/sdg-core/src/main/java/es/upv/mist/slicing/nodes/io/ActualIONode.java b/sdg-core/src/main/java/es/upv/mist/slicing/nodes/io/ActualIONode.java index e074556..fb5c6a6 100644 --- a/sdg-core/src/main/java/es/upv/mist/slicing/nodes/io/ActualIONode.java +++ b/sdg-core/src/main/java/es/upv/mist/slicing/nodes/io/ActualIONode.java @@ -16,7 +16,11 @@ public class ActualIONode extends IONode { protected final Expression argument; protected ActualIONode(Resolvable astNode, ResolvedValueDeclaration variable, Expression argument, boolean isInput) { - super(createLabel(isInput, variable.getName(), argument), (Node) astNode, variable.getName(), isInput); + this(astNode, variable.getName(), argument, isInput); + } + + protected ActualIONode(Resolvable astNode, String variable, Expression argument, boolean isInput) { + super(createLabel(isInput, variable, argument), (Node) astNode, variable, isInput); this.argument = argument; } @@ -67,6 +71,10 @@ public class ActualIONode extends IONode { return new ActualIONode(methodCallExpr, resolvedDeclaration, argument, true); } + public static ActualIONode createActualIn(Resolvable methodCallExpr, String variable, Expression argument) { + return new ActualIONode(methodCallExpr, variable, argument, true); + } + public static ActualIONode createActualOut(Resolvable methodCallExpr, ResolvedValueDeclaration resolvedDeclaration, Expression argument) { return new ActualIONode(methodCallExpr, resolvedDeclaration, argument, false); } diff --git a/sdg-core/src/main/java/es/upv/mist/slicing/nodes/io/IONode.java b/sdg-core/src/main/java/es/upv/mist/slicing/nodes/io/IONode.java index d6ee384..f4f1a85 100644 --- a/sdg-core/src/main/java/es/upv/mist/slicing/nodes/io/IONode.java +++ b/sdg-core/src/main/java/es/upv/mist/slicing/nodes/io/IONode.java @@ -17,6 +17,10 @@ public abstract class IONode extends SyntheticNode { this.isInput = isInput; } + public String getVariableName() { + return variableName; + } + public boolean isInput() { return isInput; } diff --git a/sdg-core/src/main/java/es/upv/mist/slicing/nodes/oo/MemberNode.java b/sdg-core/src/main/java/es/upv/mist/slicing/nodes/oo/MemberNode.java index fb6eed9..17044d9 100644 --- a/sdg-core/src/main/java/es/upv/mist/slicing/nodes/oo/MemberNode.java +++ b/sdg-core/src/main/java/es/upv/mist/slicing/nodes/oo/MemberNode.java @@ -1,22 +1,27 @@ package es.upv.mist.slicing.nodes.oo; import com.github.javaparser.ast.Node; +import es.upv.mist.slicing.nodes.GraphNode; import es.upv.mist.slicing.nodes.SyntheticNode; import java.util.LinkedList; public class MemberNode extends SyntheticNode { - protected final MemberNode parent; + protected GraphNode parent; - public MemberNode(String instruction, MemberNode parent) { + public MemberNode(String instruction, GraphNode parent) { super(instruction, null, new LinkedList<>()); this.parent = parent; } - public MemberNode getParent() { + public GraphNode getParent() { return parent; } + public void setParent(GraphNode parent) { + this.parent = parent; + } + @Override public String toString() { return String.format("%s{id: %s, label: '%s'}", diff --git a/sdg-core/src/main/java/es/upv/mist/slicing/slicing/ExceptionSensitiveSlicingAlgorithm.java b/sdg-core/src/main/java/es/upv/mist/slicing/slicing/ExceptionSensitiveSlicingAlgorithm.java index 0aa3a3e..88f2dcd 100644 --- a/sdg-core/src/main/java/es/upv/mist/slicing/slicing/ExceptionSensitiveSlicingAlgorithm.java +++ b/sdg-core/src/main/java/es/upv/mist/slicing/slicing/ExceptionSensitiveSlicingAlgorithm.java @@ -51,8 +51,8 @@ public class ExceptionSensitiveSlicingAlgorithm implements SlicingAlgorithm { this.slicingCriterion = slicingCriterion; Slice slice = new Slice(); slice.add(slicingCriterion); - pass(slice, SDG_PASS_1.or(this::ppdgIgnore).or(this::essdgIgnore)); - pass(slice, SDG_PASS_2.or(this::ppdgIgnore).or(this::essdgIgnore)); + pass(slice, SDG_PASS_1.or(this::commonIgnoreConditions)); + pass(slice, SDG_PASS_2.or(this::commonIgnoreConditions)); return slice; } @@ -65,6 +65,10 @@ public class ExceptionSensitiveSlicingAlgorithm implements SlicingAlgorithm { return slice; } + protected boolean commonIgnoreConditions(Arc arc) { + return ppdgIgnore(arc) || essdgIgnore(arc); + } + /** * Perform a round of traversal, until no new nodes can be added to the slice. Then, apply rule 5. * @param slice A slice object that will serve as initial work-list and where nodes will be added. diff --git a/sdg-core/src/main/java/es/upv/mist/slicing/slicing/JSysDGSlicingAlgorithm.java b/sdg-core/src/main/java/es/upv/mist/slicing/slicing/JSysDGSlicingAlgorithm.java new file mode 100644 index 0000000..a08d94c --- /dev/null +++ b/sdg-core/src/main/java/es/upv/mist/slicing/slicing/JSysDGSlicingAlgorithm.java @@ -0,0 +1,27 @@ +package es.upv.mist.slicing.slicing; + +import com.github.javaparser.ast.stmt.CatchClause; +import es.upv.mist.slicing.arcs.Arc; +import es.upv.mist.slicing.graphs.jsysdg.JSysDG; +import es.upv.mist.slicing.nodes.GraphNode; +import es.upv.mist.slicing.nodes.exceptionsensitive.ExceptionExitNode; + +public class JSysDGSlicingAlgorithm extends ExceptionSensitiveSlicingAlgorithm { + public JSysDGSlicingAlgorithm(JSysDG graph) { + super(graph); + } + + @Override + protected boolean commonIgnoreConditions(Arc arc) { + return objectFlowIgnore(arc) || super.commonIgnoreConditions(arc); + } + + protected boolean objectFlowIgnore(Arc arc) { + GraphNode target = graph.getEdgeTarget(arc); + return arc.isObjectFlow() && // 1. The arc is object flow + slicingCriterion != target && // 2. The target is not the slicing criterion + reachedStream(target).noneMatch(Arc::isObjectFlow) && // 3. The target hasn't been reached by object flow arcs + !graph.isPredicate(target) && // 4. The target is not a predicate + !(target.getAstNode() instanceof CatchClause) && !(target instanceof ExceptionExitNode); // Some extra conditions for exceptions + } +} diff --git a/sdg-core/src/main/java/es/upv/mist/slicing/slicing/Slice.java b/sdg-core/src/main/java/es/upv/mist/slicing/slicing/Slice.java index 22ef03f..2638e45 100644 --- a/sdg-core/src/main/java/es/upv/mist/slicing/slicing/Slice.java +++ b/sdg-core/src/main/java/es/upv/mist/slicing/slicing/Slice.java @@ -60,7 +60,7 @@ public class Slice { // Add each node to the corresponding bucket of the map // Nodes may not belong to a compilation unit (fictional nodes), and they are skipped for the slice. for (GraphNode graphNode : map.values()) { - if (graphNode.isImplicitInstruction()) + if (graphNode.isImplicitInstruction() || graphNode.getAstNode() == null) continue; Optional cu = graphNode.getAstNode().findCompilationUnit(); if (cu.isEmpty()) continue; 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 041ead4..232392b 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 @@ -3,10 +3,7 @@ package es.upv.mist.slicing.utils; import com.github.javaparser.ast.Node; import com.github.javaparser.ast.body.*; import com.github.javaparser.ast.expr.*; -import com.github.javaparser.ast.stmt.BlockStmt; -import com.github.javaparser.ast.stmt.ExplicitConstructorInvocationStmt; -import com.github.javaparser.ast.stmt.SwitchEntry; -import com.github.javaparser.ast.stmt.SwitchStmt; +import com.github.javaparser.ast.stmt.*; import com.github.javaparser.ast.type.PrimitiveType; import com.github.javaparser.ast.type.Type; import com.github.javaparser.resolution.Resolvable; @@ -41,6 +38,14 @@ public class ASTUtils { return null; } + public static boolean equalsInDeclaration(Node n1, Node n2) { + if (n1 == n2) + return true; + CallableDeclaration d1 = getDeclarationNode(n1); + CallableDeclaration d2 = getDeclarationNode(n2); + return n1.equals(n2) && equalsWithRange(d1, d2); + } + public static boolean equalsWithRange(Node n1, Node n2) { if (n1 == null || n2 == null) return n1 == n2; @@ -63,6 +68,23 @@ public class ASTUtils { throw new IllegalArgumentException("Call didn't resolve to either method or constructor!"); } + public static boolean resolvableIsPrimitive(Resolvable call) { + var resolved = call.resolve(); + if (resolved instanceof ResolvedMethodDeclaration) + return ((ResolvedMethodDeclaration) resolved).getReturnType().isPrimitive(); + if (resolved instanceof ResolvedConstructorDeclaration) + return false; + throw new IllegalArgumentException("Call didn't resolve to either method or constructor!"); + } + + public static boolean declarationReturnIsObject(CallableDeclaration declaration) { + if (declaration.isMethodDeclaration()) + return declaration.asMethodDeclaration().getType().isClassOrInterfaceType(); + if (declaration.isConstructorDeclaration()) + return true; + throw new IllegalArgumentException("Declaration wan't method or constructor"); + } + public static int getMatchingParameterIndex(CallableDeclaration declaration, ResolvedParameterDeclaration param) { var parameters = declaration.getParameters(); for (int i = 0; i < parameters.size(); i++) @@ -189,6 +211,14 @@ public class ASTUtils { return (ClassOrInterfaceDeclaration) upperNode; } + public static CallableDeclaration getDeclarationNode(Node n) { + assert n instanceof Statement || n instanceof Expression || n instanceof CallableDeclaration; + Node upperNode = n; + while (!(upperNode instanceof CallableDeclaration)) + upperNode = upperNode.getParentNode().orElseThrow(); + return (CallableDeclaration) upperNode; + } + /** Generates the default initializer, given a field. In Java, reference types * default to null, booleans to false and all other primitives to 0. */ public static Expression initializerForField(FieldDeclaration field) { diff --git a/sdg-core/src/test/java/es/upv/mist/slicing/SlicerTest.java b/sdg-core/src/test/java/es/upv/mist/slicing/SlicerTest.java index f111440..83dadb2 100644 --- a/sdg-core/src/test/java/es/upv/mist/slicing/SlicerTest.java +++ b/sdg-core/src/test/java/es/upv/mist/slicing/SlicerTest.java @@ -59,7 +59,7 @@ public class SlicerTest { return Optional.of(Arguments.of(javaFile, slice, criterion.get())); } - @ParameterizedTest(name = "[{index}] {0}") + @ParameterizedTest(name = "{0}") @MethodSource("findAllFiles") public void slicerRegressionTest(File source, File target, SlicingCriterion sc) throws FileNotFoundException { if (!target.exists()) diff --git a/sdg-core/src/test/java/es/upv/mist/slicing/arcs/sdg/InterproceduralArcTest.java b/sdg-core/src/test/java/es/upv/mist/slicing/arcs/sdg/InterproceduralArcTest.java new file mode 100644 index 0000000..24842d8 --- /dev/null +++ b/sdg-core/src/test/java/es/upv/mist/slicing/arcs/sdg/InterproceduralArcTest.java @@ -0,0 +1,31 @@ +package es.upv.mist.slicing.arcs.sdg; + +import com.github.javaparser.StaticJavaParser; +import com.github.javaparser.ast.NodeList; +import es.upv.mist.slicing.graphs.jsysdg.JSysDG; +import es.upv.mist.slicing.graphs.sdg.SDG; +import es.upv.mist.slicing.slicing.SlicingCriterion; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.MethodSource; + +import java.io.File; +import java.io.FileNotFoundException; + +public class InterproceduralArcTest { + @ParameterizedTest(name = "{0}") + @MethodSource("es.upv.mist.slicing.SlicerTest#findAllFiles") + public void interproceduralArcsMustBeInputXorOutputTest(File source, File target, SlicingCriterion sc) throws FileNotFoundException { + if (!target.exists()) + return; + SDG sdg; + sdg = new JSysDG(); + sdg.build(new NodeList<>(StaticJavaParser.parse(source))); + sdg.edgeSet().stream() + .filter(InterproceduralArc.class::isInstance) + .map(InterproceduralArc.class::cast) + .forEach(arc -> { + assert arc.isInterproceduralInputArc() || arc.isInterproceduralOutputArc(); + assert !arc.isInterproceduralInputArc() || !arc.isInterproceduralOutputArc(); + }); + } +} diff --git a/sdg-core/src/test/res/regression/carlos/Problem1.java b/sdg-core/src/test/res/regression/carlos/Problem1.java index 925a4e8..045b39a 100644 --- a/sdg-core/src/test/res/regression/carlos/Problem1.java +++ b/sdg-core/src/test/res/regression/carlos/Problem1.java @@ -1,4 +1,6 @@ public class Problem1 { + static boolean X = true, Y = true, Z = true; + public static void main(String[] args) { while (X) { if (Y) { diff --git a/sdg-core/src/test/res/regression/carlos/Problem1.java.sdg.sliced b/sdg-core/src/test/res/regression/carlos/Problem1.java.sdg.sliced index 624bb93..24dd516 100644 --- a/sdg-core/src/test/res/regression/carlos/Problem1.java.sdg.sliced +++ b/sdg-core/src/test/res/regression/carlos/Problem1.java.sdg.sliced @@ -1,6 +1,11 @@ public class Problem1 { public static void main(String[] args) { - System.out.println("D"); + while (X) { + if (Y) { + break; + } + System.out.println("C"); + } } } diff --git a/sdg-core/src/test/res/regression/carlos/Problem3.java.sdg.sliced b/sdg-core/src/test/res/regression/carlos/Problem3.java.sdg.sliced index f181758..4e3ade0 100644 --- a/sdg-core/src/test/res/regression/carlos/Problem3.java.sdg.sliced +++ b/sdg-core/src/test/res/regression/carlos/Problem3.java.sdg.sliced @@ -1,12 +1,10 @@ public class Problem3 { public static void main() throws Exception { - x = 0; try { f(); } catch (Exception e) { } - x = 1; f(); } diff --git a/sdg-core/src/test/res/regression/carlos/Problem3nonStatic.java b/sdg-core/src/test/res/regression/carlos/Problem3nonStatic.java new file mode 100644 index 0000000..38c35ee --- /dev/null +++ b/sdg-core/src/test/res/regression/carlos/Problem3nonStatic.java @@ -0,0 +1,20 @@ +class Problem3 { + int x; + + public void main() throws Exception { + x = 0; + try { + f(); + } catch (Exception e) { + System.out.println("error"); + } + x = 1; + f(); + } + + public void f() throws Exception { + if (x % 2 == 0) + throw new Exception("error!"); + System.out.println("x = " + x); + } +} diff --git a/sdg-core/src/test/res/regression/carlos/TestJosep.java b/sdg-core/src/test/res/regression/carlos/TestJosep.java index 518eba5..63eae2a 100644 --- a/sdg-core/src/test/res/regression/carlos/TestJosep.java +++ b/sdg-core/src/test/res/regression/carlos/TestJosep.java @@ -1,5 +1,5 @@ public class TestJosep { - int x = 0; + static int x = 0; public static void main(int y) { int z = 0; while (y > z) { @@ -39,5 +39,5 @@ public class TestJosep { log(y); } - public void log(Object o) {} + public static void log(Object o) {} } diff --git a/sdg-core/src/test/res/regression/carlos/TestJosep.java.sdg.sliced b/sdg-core/src/test/res/regression/carlos/TestJosep.java.sdg.sliced index da134e6..2393031 100644 --- a/sdg-core/src/test/res/regression/carlos/TestJosep.java.sdg.sliced +++ b/sdg-core/src/test/res/regression/carlos/TestJosep.java.sdg.sliced @@ -1,6 +1,7 @@ public class TestJosep { public static void main(int y) { + int z = 0; log(z); } } 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 0740208..bf9ef9c 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 @@ -21,7 +21,7 @@ public class Josep2 { class Numeros { - int noHaceFalta = 1; + int haceFalta = 1; int random() { return haceFalta; diff --git a/sdg-core/src/test/res/regression/dinsa-tests/Josep3.java.sdg.sliced b/sdg-core/src/test/res/regression/dinsa-tests/Josep3.java.sdg.sliced index 739b3a4..d1643c8 100644 --- a/sdg-core/src/test/res/regression/dinsa-tests/Josep3.java.sdg.sliced +++ b/sdg-core/src/test/res/regression/dinsa-tests/Josep3.java.sdg.sliced @@ -3,6 +3,7 @@ class A { int x = 0; A(int x) { + this.x = x; } public static void main(int[] args) { diff --git a/sdg-core/src/test/res/regression/dinsa-tests/Josep4.java.sdg.sliced b/sdg-core/src/test/res/regression/dinsa-tests/Josep4.java.sdg.sliced index 6551a9e..071079b 100644 --- a/sdg-core/src/test/res/regression/dinsa-tests/Josep4.java.sdg.sliced +++ b/sdg-core/src/test/res/regression/dinsa-tests/Josep4.java.sdg.sliced @@ -3,6 +3,7 @@ class A { int x = 0; A(int x) { + this.x = x; } int getx() { -- GitLab From 499da150a590e568c4d00fd00da82237adcb867b Mon Sep 17 00:00:00 2001 From: Carlos Galindo Date: Mon, 8 Mar 2021 16:36:41 +0100 Subject: [PATCH 04/60] reorganized arc colors, added TotalDefinitionDep.Arc --- .../arcs/pdg/TotalDefinitionDependenceArc.java | 16 ++++++++++++++++ .../es/upv/mist/slicing/arcs/sdg/CallArc.java | 12 ------------ .../slicing/arcs/sdg/InterproceduralArc.java | 5 ++--- .../mist/slicing/arcs/sdg/ParameterInOutArc.java | 7 ------- .../es/upv/mist/slicing/arcs/sdg/SummaryArc.java | 1 + 5 files changed, 19 insertions(+), 22 deletions(-) create mode 100644 sdg-core/src/main/java/es/upv/mist/slicing/arcs/pdg/TotalDefinitionDependenceArc.java diff --git a/sdg-core/src/main/java/es/upv/mist/slicing/arcs/pdg/TotalDefinitionDependenceArc.java b/sdg-core/src/main/java/es/upv/mist/slicing/arcs/pdg/TotalDefinitionDependenceArc.java new file mode 100644 index 0000000..74d40d8 --- /dev/null +++ b/sdg-core/src/main/java/es/upv/mist/slicing/arcs/pdg/TotalDefinitionDependenceArc.java @@ -0,0 +1,16 @@ +package es.upv.mist.slicing.arcs.pdg; + +import es.upv.mist.slicing.arcs.Arc; +import org.jgrapht.nio.Attribute; +import org.jgrapht.nio.DefaultAttribute; + +import java.util.Map; + +public class TotalDefinitionDependenceArc extends Arc { + @Override + public Map getDotAttributes() { + Map map = super.getDotAttributes(); + map.put("color", DefaultAttribute.createAttribute("pink")); + return map; + } +} diff --git a/sdg-core/src/main/java/es/upv/mist/slicing/arcs/sdg/CallArc.java b/sdg-core/src/main/java/es/upv/mist/slicing/arcs/sdg/CallArc.java index 9f96b97..4a43afe 100644 --- a/sdg-core/src/main/java/es/upv/mist/slicing/arcs/sdg/CallArc.java +++ b/sdg-core/src/main/java/es/upv/mist/slicing/arcs/sdg/CallArc.java @@ -1,22 +1,10 @@ package es.upv.mist.slicing.arcs.sdg; -import org.jgrapht.nio.Attribute; -import org.jgrapht.nio.DefaultAttribute; - -import java.util.Map; - /** * An interprocedural arc that connects a call site with its * corresponding declaration. It is considered an interprocedural input. */ public class CallArc extends InterproceduralArc { - @Override - public Map getDotAttributes() { - Map map = super.getDotAttributes(); - map.put("style", DefaultAttribute.createAttribute("dashed")); - return map; - } - @Override public boolean isInterproceduralInputArc() { return true; diff --git a/sdg-core/src/main/java/es/upv/mist/slicing/arcs/sdg/InterproceduralArc.java b/sdg-core/src/main/java/es/upv/mist/slicing/arcs/sdg/InterproceduralArc.java index 549b97e..58efcb7 100644 --- a/sdg-core/src/main/java/es/upv/mist/slicing/arcs/sdg/InterproceduralArc.java +++ b/sdg-core/src/main/java/es/upv/mist/slicing/arcs/sdg/InterproceduralArc.java @@ -17,12 +17,11 @@ public abstract class InterproceduralArc extends Arc { public Map getDotAttributes() { var map = super.getDotAttributes(); map.put("penwidth", DefaultAttribute.createAttribute("3")); + map.put("style", DefaultAttribute.createAttribute("dashed")); if (isInterproceduralInputArc()) - map.put("color", DefaultAttribute.createAttribute("orange")); + map.put("color", DefaultAttribute.createAttribute("darkgreen")); else if (isInterproceduralOutputArc()) map.put("color", DefaultAttribute.createAttribute("blue")); - else - map.put("color", DefaultAttribute.createAttribute("green")); return map; } diff --git a/sdg-core/src/main/java/es/upv/mist/slicing/arcs/sdg/ParameterInOutArc.java b/sdg-core/src/main/java/es/upv/mist/slicing/arcs/sdg/ParameterInOutArc.java index 5022c50..17573e2 100644 --- a/sdg-core/src/main/java/es/upv/mist/slicing/arcs/sdg/ParameterInOutArc.java +++ b/sdg-core/src/main/java/es/upv/mist/slicing/arcs/sdg/ParameterInOutArc.java @@ -14,13 +14,6 @@ import java.util.Map; /** An interprocedural arc connecting {@link ActualIONode actual} and {@link FormalIONode formal} * nodes. The source and target must match: both must either be inputs or outputs. This arc may be an input or output. */ public class ParameterInOutArc extends InterproceduralArc { - @Override - public Map getDotAttributes() { - Map map = super.getDotAttributes(); - map.put("style", DefaultAttribute.createAttribute("dashed")); - return map; - } - @Override public boolean isInterproceduralInputArc() { GraphNode source = getNodeCommon(getSource()); diff --git a/sdg-core/src/main/java/es/upv/mist/slicing/arcs/sdg/SummaryArc.java b/sdg-core/src/main/java/es/upv/mist/slicing/arcs/sdg/SummaryArc.java index a423c25..e4fa633 100644 --- a/sdg-core/src/main/java/es/upv/mist/slicing/arcs/sdg/SummaryArc.java +++ b/sdg-core/src/main/java/es/upv/mist/slicing/arcs/sdg/SummaryArc.java @@ -14,6 +14,7 @@ public class SummaryArc extends Arc { public Map getDotAttributes() { Map map = super.getDotAttributes(); map.put("style", DefaultAttribute.createAttribute("bold")); + map.put("color", DefaultAttribute.createAttribute("#a01210")); // rojo oscuro return map; } } -- GitLab From 6ce682afe9f426533f91c0494ba720cb3dc53ed3 Mon Sep 17 00:00:00 2001 From: Carlos Galindo Date: Mon, 8 Mar 2021 16:39:50 +0100 Subject: [PATCH 05/60] Move active exception DEF from CFG builder to VariableVisitor, and connect to expression. --- .../java/es/upv/mist/slicing/graphs/ClassGraph.java | 10 +++------- .../slicing/graphs/exceptionsensitive/ESCFG.java | 1 - .../es/upv/mist/slicing/nodes/VariableVisitor.java | 12 ++++++++++++ 3 files changed, 15 insertions(+), 8 deletions(-) diff --git a/sdg-core/src/main/java/es/upv/mist/slicing/graphs/ClassGraph.java b/sdg-core/src/main/java/es/upv/mist/slicing/graphs/ClassGraph.java index 03d2526..d077356 100644 --- a/sdg-core/src/main/java/es/upv/mist/slicing/graphs/ClassGraph.java +++ b/sdg-core/src/main/java/es/upv/mist/slicing/graphs/ClassGraph.java @@ -10,7 +10,6 @@ import com.github.javaparser.resolution.types.ResolvedReferenceType; import es.upv.mist.slicing.arcs.Arc; import es.upv.mist.slicing.nodes.VariableAction.ObjectTree; import es.upv.mist.slicing.utils.ASTUtils; -import es.upv.mist.slicing.utils.Logger; import es.upv.mist.slicing.utils.Utils; import org.jgrapht.graph.DirectedPseudograph; import org.jgrapht.nio.dot.DOTExporter; @@ -151,15 +150,12 @@ public class ClassGraph extends DirectedPseudograph stmt = connectTo(n); n.getExpression().accept(this, arg); - stmt.addVADefineActiveException(n.getExpression()); populateExceptionSourceMap(new ExceptionSource(stmt, n.getExpression().calculateResolvedType())); clearHanging(); nonExecHangingNodes.add(stmt); 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 f4fdeaf..fa4eabe 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 @@ -31,6 +31,7 @@ import java.util.List; import java.util.Optional; import static es.upv.mist.slicing.graphs.cfg.CFGBuilder.VARIABLE_NAME_OUTPUT; +import static es.upv.mist.slicing.graphs.exceptionsensitive.ESCFG.ACTIVE_EXCEPTION_VARIABLE; import static es.upv.mist.slicing.nodes.VariableVisitor.Action.*; /** A graph node visitor that extracts the actions performed in a given GraphNode. An initial action mode can @@ -170,6 +171,17 @@ public class VariableVisitor extends GraphNodeContentVisitor Date: Mon, 8 Mar 2021 20:05:11 +0100 Subject: [PATCH 06/60] Generate total definition dependencies. --- .../mist/slicing/graphs/jsysdg/JSysCFG.java | 4 ++ .../mist/slicing/graphs/jsysdg/JSysPDG.java | 39 +++++++++++++++---- .../mist/slicing/nodes/VariableAction.java | 21 ++++++++-- .../mist/slicing/nodes/VariableVisitor.java | 28 +++++++++---- 4 files changed, 74 insertions(+), 18 deletions(-) diff --git a/sdg-core/src/main/java/es/upv/mist/slicing/graphs/jsysdg/JSysCFG.java b/sdg-core/src/main/java/es/upv/mist/slicing/graphs/jsysdg/JSysCFG.java index 20a9a05..41b28d8 100644 --- a/sdg-core/src/main/java/es/upv/mist/slicing/graphs/jsysdg/JSysCFG.java +++ b/sdg-core/src/main/java/es/upv/mist/slicing/graphs/jsysdg/JSysCFG.java @@ -70,6 +70,10 @@ public class JSysCFG extends ESCFG { return findLastVarActionsFrom(usage, VariableAction::isDefinition); } + public List findLastTotalDefinitionOf(VariableAction action, String member) { + return findLastVarActionsFrom(action, def -> def.isDefinition() && def.asDefinition().isTotallyDefinedMember(member)); + } + /** Given a definition of a given member, locate all definitions of the same object until a definition * containing the given member is found (not including that last one). If the member is found in the * given definition, it will return a list with only the given definition. */ diff --git a/sdg-core/src/main/java/es/upv/mist/slicing/graphs/jsysdg/JSysPDG.java b/sdg-core/src/main/java/es/upv/mist/slicing/graphs/jsysdg/JSysPDG.java index b304486..9871ef3 100644 --- a/sdg-core/src/main/java/es/upv/mist/slicing/graphs/jsysdg/JSysPDG.java +++ b/sdg-core/src/main/java/es/upv/mist/slicing/graphs/jsysdg/JSysPDG.java @@ -2,6 +2,7 @@ package es.upv.mist.slicing.graphs.jsysdg; import es.upv.mist.slicing.arcs.pdg.FlowDependencyArc; import es.upv.mist.slicing.arcs.pdg.ObjectFlowDependencyArc; +import es.upv.mist.slicing.arcs.pdg.TotalDefinitionDependenceArc; import es.upv.mist.slicing.graphs.exceptionsensitive.ESPDG; import es.upv.mist.slicing.graphs.pdg.PDG; import es.upv.mist.slicing.nodes.GraphNode; @@ -54,6 +55,12 @@ public class JSysPDG extends ESPDG { addEdge(usage.getObjectTree().getNodeFor(member), statement, new FlowDependencyArc(member)); } + protected void addTotalDefinitionDependencyArc(VariableAction totalDefinition, VariableAction target, String member) { + addEdge(totalDefinition.getObjectTree().getNodeFor(member), + target.getObjectTree().getNodeFor(member), + new TotalDefinitionDependenceArc()); + } + protected GraphNode graphNodeOf(VariableAction action) { if (action instanceof VariableAction.Movable) return ((VariableAction.Movable) action).getRealNode(); @@ -74,8 +81,18 @@ public class JSysPDG extends ESPDG { protected void buildDataDependency() { addSyntheticNodesToPDG(); JSysCFG jSysCFG = (JSysCFG) cfg; - for (GraphNode node : vertexSet()) - for (VariableAction varAct : node.getVariableActions()) + for (GraphNode node : vertexSet()) { + for (VariableAction varAct : node.getVariableActions()) { + // Total definition dependence + if (varAct.isUsage() || varAct.isDefinition()) { + // root + jSysCFG.findLastTotalDefinitionOf(varAct, "-root-").forEach(totalDef -> addTotalDefinitionDependencyArc(totalDef, varAct, "-root-")); + // members + for (String member : varAct.getObjectTree().nameIterable()) { + jSysCFG.findLastTotalDefinitionOf(varAct, member).forEach(totalDef -> addTotalDefinitionDependencyArc(totalDef, varAct, member)); + } + } + // Object flow, flow and declaration-definition dependencies if (varAct.isUsage()) { if (varAct.isPrimitive()) jSysCFG.findLastDefinitionOfPrimitive(varAct).forEach(def -> addFlowDependencyArc(def, varAct)); @@ -93,6 +110,8 @@ public class JSysPDG extends ESPDG { for (String member : varAct.getObjectTree().nameIterable()) jSysCFG.findNextObjectDefinitionsFor(varAct, member).forEach(def -> addObjectFlowDependencyArc(varAct, member, def)); } + } + } } @Override @@ -133,12 +152,10 @@ public class JSysPDG extends ESPDG { addControlDependencyArc(node, parentNode); } // Extract the member nodes contained within the object tree + if (va.getObjectTree().getMemberNode() != null) + insertMemberNode(va.getObjectTree().getMemberNode(), parentNode); for (MemberNode memberNode : va.getObjectTree().nodeIterable()) { - if (memberNode.getParent() == null) - memberNode.setParent(parentNode); - assert containsVertex(memberNode.getParent()); - addVertex(memberNode); - addControlDependencyArc(memberNode.getParent(), memberNode); + insertMemberNode(memberNode, parentNode); } } assert callNodeStack.isEmpty(); @@ -148,5 +165,13 @@ public class JSysPDG extends ESPDG { .flatMap(node -> node.getVariableActions().stream()) .forEach(va -> va.applyPDGTreeConnections(JSysPDG.this)); } + + protected void insertMemberNode(MemberNode memberNode, GraphNode parentNode) { + if (memberNode.getParent() == null) + memberNode.setParent(parentNode); + assert containsVertex(memberNode.getParent()); + addVertex(memberNode); + addControlDependencyArc(memberNode.getParent(), memberNode); + } } } diff --git a/sdg-core/src/main/java/es/upv/mist/slicing/nodes/VariableAction.java b/sdg-core/src/main/java/es/upv/mist/slicing/nodes/VariableAction.java index 70647db..8764e8d 100644 --- a/sdg-core/src/main/java/es/upv/mist/slicing/nodes/VariableAction.java +++ b/sdg-core/src/main/java/es/upv/mist/slicing/nodes/VariableAction.java @@ -376,6 +376,8 @@ public abstract class VariableAction { public static class Definition extends VariableAction { /** The value to which the variable has been defined. */ protected final Expression expression; + /** The members of the object tree that are total definitions. */ + protected String totallyDefinedMember; public Definition(Expression variable, String realName, GraphNode graphNode) { this(variable, realName, graphNode, (Expression) null); @@ -395,6 +397,17 @@ public abstract class VariableAction { this.expression = expression; } + public void setTotallyDefinedMember(String totallyDefinedMember) { + this.totallyDefinedMember = Objects.requireNonNull(totallyDefinedMember); + } + + public boolean isTotallyDefinedMember(String member) { + if (totallyDefinedMember == null) + return false; + return totallyDefinedMember.equals(member) || totallyDefinedMember.startsWith(member) + || ObjectTree.removeRoot(totallyDefinedMember).startsWith(ObjectTree.removeRoot(member)); + } + /** @see #expression */ public Expression getExpression() { return expression; @@ -658,7 +671,9 @@ public abstract class VariableAction { } private MemberNode getNodeForNonRoot(String members) { - if (members.contains(".")) { + if (members.isEmpty()) { + return memberNode; + } else if (members.contains(".")) { int firstDot = members.indexOf('.'); String first = members.substring(0, firstDot); String rest = members.substring(firstDot + 1); @@ -766,8 +781,8 @@ public abstract class VariableAction { public static String removeRoot(String fieldWithRoot) { Matcher matcher = FIELD_SPLIT.matcher(fieldWithRoot); - if (matcher.matches() && matcher.group("fields") != null) - return matcher.group("fields"); + if (matcher.matches()) + return matcher.group("fields") != null ? matcher.group("fields") : ""; throw new IllegalArgumentException("Field should be of the form ., .this., where may not contain dots."); } 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 fa4eabe..c43e3c2 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 @@ -178,7 +178,7 @@ public class VariableVisitor extends GraphNodeContentVisitor list = graphNode.getVariableActions(); - VariableAction def = list.get(graphNode.getVariableActions().size() - 1); + VariableAction.Definition def = getLastDefinition(); def.getObjectTree().addField(realName); + def.setTotallyDefinedMember(realName); realNameWithoutRootList.add(realName); } definitionStack.pop(); @@ -269,8 +271,8 @@ public class VariableVisitor extends GraphNodeContentVisitor list = graphNode.getVariableActions(); - VariableAction def = list.get(graphNode.getVariableActions().size() - 1); + VariableAction.Definition def = getLastDefinition(); + def.setTotallyDefinedMember(realName); def.getObjectTree().addField(realName); realNameWithoutRootList.add(ObjectTree.removeRoot(realName)); } @@ -281,9 +283,8 @@ public class VariableVisitor extends GraphNodeContentVisitor list = graphNode.getVariableActions(); ExpressionObjectTreeFinder finder = new ExpressionObjectTreeFinder(graphNode); - finder.handleAssignExpr(n, list.get(graphNode.getVariableActions().size() - 1), realNameWithoutRootList.get(0)); + finder.handleAssignExpr(n, getLastDefinition(), realNameWithoutRootList.get(0)); } } @@ -314,7 +315,11 @@ public class VariableVisitor extends GraphNodeContentVisitor { init.accept(this, action); - acceptActionNullDefinition(v.getNameAsString()); + definitionStack.push(init); + acceptAction(v.getNameAsString(), DEFINITION); + definitionStack.pop(); + if (v.getType().isClassOrInterfaceType()) + getLastDefinition().setTotallyDefinedMember(v.getNameAsString()); }); v.accept(this, action); } @@ -330,6 +335,8 @@ public class VariableVisitor extends GraphNodeContentVisitor list = graphNode.getVariableActions(); + return ((VariableAction.Definition) list.get(list.size() - 1)); + } } -- GitLab From 96cdbae999cfa98fec37533c0eec5e66a0c0985e Mon Sep 17 00:00:00 2001 From: Carlos Galindo Date: Mon, 8 Mar 2021 20:05:33 +0100 Subject: [PATCH 07/60] Better printing of graphs. --- .../src/main/java/es/upv/mist/slicing/cli/SlicedSDGLog.java | 4 ++++ .../src/main/java/es/upv/mist/slicing/nodes/GraphNode.java | 4 ++-- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/sdg-cli/src/main/java/es/upv/mist/slicing/cli/SlicedSDGLog.java b/sdg-cli/src/main/java/es/upv/mist/slicing/cli/SlicedSDGLog.java index b838d8b..a8af575 100644 --- a/sdg-cli/src/main/java/es/upv/mist/slicing/cli/SlicedSDGLog.java +++ b/sdg-cli/src/main/java/es/upv/mist/slicing/cli/SlicedSDGLog.java @@ -40,10 +40,14 @@ public class SlicedSDGLog extends SDGLog { Map map = new HashMap<>(); if (slice.contains(node) && node.equals(sc)) map.put("style", DefaultAttribute.createAttribute("filled,bold")); + else if (slice.contains(node) && node.isImplicitInstruction()) + map.put("style", DefaultAttribute.createAttribute("filled,dashed")); else if (slice.contains(node)) map.put("style", DefaultAttribute.createAttribute("filled")); else if (node.equals(sc)) map.put("style", DefaultAttribute.createAttribute("bold")); + else if (node.isImplicitInstruction()) + map.put("style", DefaultAttribute.createAttribute("dashed")); map.put("label", DefaultAttribute.createAttribute(node.getLongLabel())); return map; } diff --git a/sdg-core/src/main/java/es/upv/mist/slicing/nodes/GraphNode.java b/sdg-core/src/main/java/es/upv/mist/slicing/nodes/GraphNode.java index ffdb983..d5c5690 100644 --- a/sdg-core/src/main/java/es/upv/mist/slicing/nodes/GraphNode.java +++ b/sdg-core/src/main/java/es/upv/mist/slicing/nodes/GraphNode.java @@ -89,9 +89,9 @@ public class GraphNode implements Comparable> { /** The node's long-form label, including its id and information on variables. */ public String getLongLabel() { - String label = getId() + ": " + getLabel(); + String label = getId() + ": " + getLabel().replace("\\", "\\\\"); if (!getVariableActions().isEmpty()) - label += "\n" + getVariableActions().stream().map(Object::toString).reduce((a, b) -> a + "," + b).orElse("--"); + label += "\\n" + getVariableActions().stream().map(Object::toString).reduce((a, b) -> a + "," + b).orElse("--"); return label; } -- GitLab From f25f0e3c84885bfcbb18085660e546bd4a53c297 Mon Sep 17 00:00:00 2001 From: Carlos Galindo Date: Mon, 8 Mar 2021 20:05:44 +0100 Subject: [PATCH 08/60] Typos --- sdg-core/src/main/java/es/upv/mist/slicing/utils/ASTUtils.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 232392b..8aa6c59 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 @@ -82,7 +82,7 @@ public class ASTUtils { return declaration.asMethodDeclaration().getType().isClassOrInterfaceType(); if (declaration.isConstructorDeclaration()) return true; - throw new IllegalArgumentException("Declaration wan't method or constructor"); + throw new IllegalArgumentException("Declaration wasn't method or constructor"); } public static int getMatchingParameterIndex(CallableDeclaration declaration, ResolvedParameterDeclaration param) { -- GitLab From 98cf7c78d1c8bbc8d8a56fdd2ce61eaaaa029be0 Mon Sep 17 00:00:00 2001 From: Carlos Galindo Date: Tue, 9 Mar 2021 09:55:32 +0100 Subject: [PATCH 09/60] Moved ObjectTree and ObjectTreeConnection to their own files. --- .../upv/mist/slicing/graphs/ClassGraph.java | 2 +- .../graphs/ExpressionObjectTreeFinder.java | 2 +- .../sdg/InterproceduralDefinitionFinder.java | 2 +- .../sdg/InterproceduralUsageFinder.java | 2 +- .../es/upv/mist/slicing/nodes/ObjectTree.java | 256 +++++++++++++++ .../slicing/nodes/ObjectTreeConnection.java | 62 ++++ .../mist/slicing/nodes/VariableAction.java | 303 +----------------- .../mist/slicing/nodes/VariableVisitor.java | 1 - 8 files changed, 326 insertions(+), 304 deletions(-) create mode 100644 sdg-core/src/main/java/es/upv/mist/slicing/nodes/ObjectTree.java create mode 100644 sdg-core/src/main/java/es/upv/mist/slicing/nodes/ObjectTreeConnection.java diff --git a/sdg-core/src/main/java/es/upv/mist/slicing/graphs/ClassGraph.java b/sdg-core/src/main/java/es/upv/mist/slicing/graphs/ClassGraph.java index d077356..3245bf4 100644 --- a/sdg-core/src/main/java/es/upv/mist/slicing/graphs/ClassGraph.java +++ b/sdg-core/src/main/java/es/upv/mist/slicing/graphs/ClassGraph.java @@ -8,7 +8,7 @@ import com.github.javaparser.resolution.UnsolvedSymbolException; import com.github.javaparser.resolution.declarations.ResolvedClassDeclaration; import com.github.javaparser.resolution.types.ResolvedReferenceType; import es.upv.mist.slicing.arcs.Arc; -import es.upv.mist.slicing.nodes.VariableAction.ObjectTree; +import es.upv.mist.slicing.nodes.ObjectTree; import es.upv.mist.slicing.utils.ASTUtils; import es.upv.mist.slicing.utils.Utils; import org.jgrapht.graph.DirectedPseudograph; 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 352cb40..4de4ad8 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 @@ -8,8 +8,8 @@ import com.github.javaparser.resolution.declarations.ResolvedMethodLikeDeclarati import com.github.javaparser.resolution.types.ResolvedReferenceType; import com.github.javaparser.utils.Pair; 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.ObjectTree; import es.upv.mist.slicing.utils.ASTUtils; import java.util.LinkedList; 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 319f5f1..2a345f3 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 @@ -8,10 +8,10 @@ import com.github.javaparser.resolution.declarations.ResolvedValueDeclaration; import es.upv.mist.slicing.graphs.CallGraph; 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.ObjectTree; import es.upv.mist.slicing.nodes.io.ActualIONode; import es.upv.mist.slicing.nodes.io.FormalIONode; 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 f99b482..842ee49 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 @@ -10,10 +10,10 @@ 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.ObjectTree; import es.upv.mist.slicing.nodes.VariableAction.Usage; import es.upv.mist.slicing.nodes.io.ActualIONode; import es.upv.mist.slicing.nodes.io.FormalIONode; 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 new file mode 100644 index 0000000..364dcb6 --- /dev/null +++ b/sdg-core/src/main/java/es/upv/mist/slicing/nodes/ObjectTree.java @@ -0,0 +1,256 @@ +package es.upv.mist.slicing.nodes; + +import es.upv.mist.slicing.nodes.oo.MemberNode; +import es.upv.mist.slicing.utils.Utils; + +import java.util.*; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +public class ObjectTree implements Cloneable { + private static final String ROOT_NAME = "-root-"; + private static final Pattern FIELD_SPLIT = Pattern.compile("^(?(([_0-9A-Za-z]+\\.)*this)|([_0-9A-Za-z]+)|(-root-))(\\.(?.+))?$"); + + private final Map childrenMap = new HashMap<>(); + + private MemberNode memberNode; + + public ObjectTree() { + memberNode = null; + } + + private ObjectTree(String memberName, ObjectTree parent) { + this.memberNode = new MemberNode(memberName, parent.memberNode); + } + + protected String getMemberName() { + return memberNode == null ? ROOT_NAME : memberNode.getLabel(); + } + + public MemberNode getMemberNode() { + return memberNode; + } + + public void setMemberNode(MemberNode memberNode) { + this.memberNode = memberNode; + } + + public boolean hasChildren() { + return !childrenMap.isEmpty(); + } + + public void addField(String fieldName) { + String members = removeRoot(fieldName); + addNonRootField(members); + } + + private void addNonRootField(String members) { + if (members.contains(".")) { + int firstDot = members.indexOf('.'); + String first = members.substring(0, firstDot); + String rest = members.substring(firstDot + 1); + childrenMap.computeIfAbsent(first, f -> new ObjectTree(f, this)); + childrenMap.get(first).addNonRootField(rest); + } else { + childrenMap.computeIfAbsent(members, f -> new ObjectTree(f, this)); + } + } + + public void addAll(ObjectTree tree) { + for (Map.Entry entry : tree.childrenMap.entrySet()) + if (childrenMap.containsKey(entry.getKey())) + childrenMap.get(entry.getKey()).addAll(entry.getValue()); + else + childrenMap.put(entry.getKey(), entry.getValue().clone(this)); + } + + /** + * Copies a subtree from source into another subtree in target. + * + * @param source The source of the nodes. + * @param target The tree where nodes will be added + * @param sourcePrefix The prefix to be consumed before copying nodes. Without root. + * @param targetPrefix The prefix to be consumed before copying nodes. Without root. + */ + public static void copyTree(ObjectTree source, ObjectTree target, String sourcePrefix, String targetPrefix) { + ObjectTree a = source.findObjectTreeOfMember(sourcePrefix); + ObjectTree b = target.findObjectTreeOfMember(targetPrefix); + a.addAll(b); + } + + ObjectTree findObjectTreeOfMember(String member) { + ObjectTree result = this; + while (!member.isEmpty()) { + int firstDot = member.indexOf('.'); + String first, rest; + if (firstDot != -1) { + first = member.substring(0, firstDot); + rest = member.substring(firstDot + 1); + } else { + first = member; + rest = ""; + } + result = result.childrenMap.get(first); + member = rest; + } + return result; + } + + public boolean hasMember(String member) { + String field = removeRoot(member); + return hasNonRootMember(field); + } + + private boolean hasNonRootMember(String members) { + if (members.contains(".")) { + int firstDot = members.indexOf('.'); + String first = members.substring(0, firstDot); + String rest = members.substring(firstDot + 1); + return childrenMap.containsKey(first) && childrenMap.get(first).hasNonRootMember(rest); + } else { + return childrenMap.containsKey(members); + } + } + + public MemberNode getNodeFor(String member) { + String field = removeRoot(member); + return getNodeForNonRoot(field); + } + + MemberNode getNodeForNonRoot(String members) { + if (members.isEmpty()) { + return memberNode; + } else if (members.contains(".")) { + int firstDot = members.indexOf('.'); + String first = members.substring(0, firstDot); + String rest = members.substring(firstDot + 1); + assert childrenMap.containsKey(first); + return childrenMap.get(first).getNodeForNonRoot(rest); + } else { + assert childrenMap.containsKey(members); + return childrenMap.get(members).memberNode; + } + } + + public boolean isEmpty() { + return childrenMap.isEmpty(); + } + + public Iterable nameIterable() { + return () -> new Iterator<>() { + final Iterator it = treeIterator(); + + @Override + public boolean hasNext() { + return it.hasNext(); + } + + @Override + public String next() { + ObjectTree element = it.next(); + StringBuilder builder = new StringBuilder(); + MemberNode node = element.memberNode; + if (node == null) + return ROOT_NAME; + else + builder.append(node.getLabel()); + while (node.getParent() != null && node.getParent() instanceof MemberNode && node.getParent().getLabel().matches("^(USE|DEF|DEC)\\{")) { + node = (MemberNode) node.getParent(); + builder.insert(0, '.'); + builder.insert(0, node.getLabel()); + } + return builder.insert(0, "-root-.").toString(); + } + }; + } + + public Iterable nodeIterable() { + return () -> new Iterator<>() { + final Iterator it = treeIterator(); + + @Override + public boolean hasNext() { + return it.hasNext(); + } + + @Override + public MemberNode next() { + return it.next().memberNode; + } + }; + } + + private Iterator treeIterator() { + return new Iterator<>() { + final Set remaining = new HashSet<>(childrenMap.values()); + Iterator childIterator = null; + + @Override + public boolean hasNext() { + if (childIterator == null || !childIterator.hasNext()) + return !remaining.isEmpty(); + else + return true; + } + + @Override + public ObjectTree next() { + if (childIterator == null || !childIterator.hasNext()) { + ObjectTree tree = Utils.setPop(remaining); + childIterator = tree.treeIterator(); + return tree; + } else { + return childIterator.next(); + } + } + }; + } + + Iterable treeIterable() { + return this::treeIterator; + } + + @SuppressWarnings("MethodDoesntCallSuperMethod") + @Override + public Object clone() { + ObjectTree clone = new ObjectTree(); + for (Map.Entry entry : childrenMap.entrySet()) + clone.childrenMap.put(entry.getKey(), entry.getValue().clone(clone)); + return clone; + } + + private ObjectTree clone(ObjectTree parent) { + ObjectTree clone = new ObjectTree(getMemberName(), parent); + for (Map.Entry entry : childrenMap.entrySet()) + clone.childrenMap.put(entry.getKey(), entry.getValue().clone(clone)); + return clone; + } + + public static String removeRoot(String fieldWithRoot) { + Matcher matcher = FIELD_SPLIT.matcher(fieldWithRoot); + if (matcher.matches()) + return matcher.group("fields") != null ? matcher.group("fields") : ""; + throw new IllegalArgumentException("Field should be of the form ., .this., where may not contain dots."); + } + + public static String removeFields(String fieldWithRoot) { + Matcher matcher = FIELD_SPLIT.matcher(fieldWithRoot); + if (matcher.matches() && matcher.group("root") != null) + return matcher.group("root"); + throw new IllegalArgumentException("Field should be of the form ., .this., where may not contain dots."); + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + ObjectTree tree = (ObjectTree) o; + return Objects.equals(getMemberName(), tree.getMemberName()) && + childrenMap.values().equals(tree.childrenMap.values()); + } + + @Override + public int hashCode() { + return Objects.hash(getMemberName(), childrenMap); + } +} diff --git a/sdg-core/src/main/java/es/upv/mist/slicing/nodes/ObjectTreeConnection.java b/sdg-core/src/main/java/es/upv/mist/slicing/nodes/ObjectTreeConnection.java new file mode 100644 index 0000000..d815a5c --- /dev/null +++ b/sdg-core/src/main/java/es/upv/mist/slicing/nodes/ObjectTreeConnection.java @@ -0,0 +1,62 @@ +package es.upv.mist.slicing.nodes; + +import es.upv.mist.slicing.arcs.Arc; +import es.upv.mist.slicing.arcs.pdg.FlowDependencyArc; +import es.upv.mist.slicing.arcs.pdg.ObjectFlowDependencyArc; +import es.upv.mist.slicing.arcs.sdg.ParameterInOutArc; +import es.upv.mist.slicing.graphs.Graph; +import es.upv.mist.slicing.graphs.jsysdg.JSysDG; +import es.upv.mist.slicing.graphs.jsysdg.JSysPDG; +import es.upv.mist.slicing.nodes.oo.MemberNode; + +import java.util.function.Supplier; + +class ObjectTreeConnection { + protected final VariableAction sourceAction; + protected final VariableAction targetAction; + protected final String sourceMember; + protected final String targetMember; + + protected boolean applied = false; + + public ObjectTreeConnection(VariableAction sourceAction, VariableAction targetAction, String sourceMember, String targetMember) { + this.sourceAction = sourceAction; + this.targetAction = targetAction; + this.sourceMember = sourceMember; + this.targetMember = targetMember; + } + + public void applySDG(JSysDG graph) { + if (!applied) { + connectTrees(graph, ParameterInOutArc::new, ParameterInOutArc.ObjectFlow::new); + applied = true; + } + } + + public void applyPDG(JSysPDG graph) { + if (!applied) { + connectTrees(graph, FlowDependencyArc::new, ObjectFlowDependencyArc::new); + applied = true; + } + } + + protected void connectTrees(Graph graph, Supplier flowSupplier, Supplier objFlowSupplier) { + ObjectTree source = sourceAction.getObjectTree().findObjectTreeOfMember(sourceMember); + ObjectTree target = targetAction.getObjectTree().findObjectTreeOfMember(targetMember); + assert sourceMember.isEmpty() || source.getMemberName() != null; + assert targetMember.isEmpty() || target.getMemberName() != null; + GraphNode rootSrc = source.getMemberNode() != null ? source.getMemberNode() : sourceAction.getGraphNode(); + GraphNode rootTgt = target.getMemberNode() != null ? target.getMemberNode() : targetAction.getGraphNode(); + graph.addEdge(rootSrc, rootTgt, objFlowSupplier.get()); + for (ObjectTree tree : target.treeIterable()) { + MemberNode src = source.getNodeForNonRoot(tree.getMemberName()); + MemberNode tgt = tree.getMemberNode(); + if (tree.hasChildren()) + graph.addEdge(src, tgt, objFlowSupplier.get()); + else + graph.addEdge(src, tgt, flowSupplier.get()); + } + } + + +} diff --git a/sdg-core/src/main/java/es/upv/mist/slicing/nodes/VariableAction.java b/sdg-core/src/main/java/es/upv/mist/slicing/nodes/VariableAction.java index 8764e8d..d651d57 100644 --- a/sdg-core/src/main/java/es/upv/mist/slicing/nodes/VariableAction.java +++ b/sdg-core/src/main/java/es/upv/mist/slicing/nodes/VariableAction.java @@ -10,22 +10,19 @@ import com.github.javaparser.resolution.declarations.ResolvedValueDeclaration; import com.github.javaparser.resolution.types.ResolvedType; import es.upv.mist.slicing.arcs.Arc; import es.upv.mist.slicing.arcs.pdg.DataDependencyArc; -import es.upv.mist.slicing.arcs.pdg.FlowDependencyArc; -import es.upv.mist.slicing.arcs.pdg.ObjectFlowDependencyArc; -import es.upv.mist.slicing.arcs.sdg.ParameterInOutArc; import es.upv.mist.slicing.graphs.Graph; import es.upv.mist.slicing.graphs.jsysdg.JSysDG; import es.upv.mist.slicing.graphs.jsysdg.JSysPDG; import es.upv.mist.slicing.graphs.pdg.PDG; import es.upv.mist.slicing.nodes.io.CallNode; import es.upv.mist.slicing.nodes.io.OutputNode; -import es.upv.mist.slicing.nodes.oo.MemberNode; import es.upv.mist.slicing.utils.ASTUtils; -import es.upv.mist.slicing.utils.Utils; import java.lang.reflect.InvocationTargetException; -import java.util.*; -import java.util.function.Supplier; +import java.util.LinkedList; +import java.util.List; +import java.util.Objects; +import java.util.Set; import java.util.regex.Matcher; import java.util.regex.Pattern; import java.util.stream.Collectors; @@ -565,296 +562,4 @@ public abstract class VariableAction { } } - public static class ObjectTree implements Cloneable { - private static final String ROOT_NAME = "-root-"; - private static final Pattern FIELD_SPLIT = Pattern.compile("^(?(([_0-9A-Za-z]+\\.)*this)|([_0-9A-Za-z]+)|(-root-))(\\.(?.+))?$"); - - private final Map childrenMap = new HashMap<>(); - - private MemberNode memberNode; - - public ObjectTree() { - memberNode = null; - } - - private ObjectTree(String memberName, ObjectTree parent) { - this.memberNode = new MemberNode(memberName, parent.memberNode); - } - - protected String getMemberName() { - return memberNode == null ? ROOT_NAME : memberNode.getLabel(); - } - - public MemberNode getMemberNode() { - return memberNode; - } - - public void setMemberNode(MemberNode memberNode) { - this.memberNode = memberNode; - } - - public void addField(String fieldName) { - String members = removeRoot(fieldName); - addNonRootField(members); - } - - private void addNonRootField(String members) { - if (members.contains(".")) { - int firstDot = members.indexOf('.'); - String first = members.substring(0, firstDot); - String rest = members.substring(firstDot + 1); - childrenMap.computeIfAbsent(first, f -> new ObjectTree(f, this)); - childrenMap.get(first).addNonRootField(rest); - } else { - childrenMap.computeIfAbsent(members, f -> new ObjectTree(f, this)); - } - } - - public void addAll(ObjectTree tree) { - for (Map.Entry entry : tree.childrenMap.entrySet()) - if (childrenMap.containsKey(entry.getKey())) - childrenMap.get(entry.getKey()).addAll(entry.getValue()); - else - childrenMap.put(entry.getKey(), entry.getValue().clone(this)); - } - - /** - * Copies a subtree from source into another subtree in target. - * @param source The source of the nodes. - * @param target The tree where nodes will be added - * @param sourcePrefix The prefix to be consumed before copying nodes. Without root. - * @param targetPrefix The prefix to be consumed before copying nodes. Without root. - */ - public static void copyTree(ObjectTree source, ObjectTree target, String sourcePrefix, String targetPrefix) { - ObjectTree a = source.findObjectTreeOfMember(sourcePrefix); - ObjectTree b = target.findObjectTreeOfMember(targetPrefix); - a.addAll(b); - } - - private ObjectTree findObjectTreeOfMember(String member) { - ObjectTree result = this; - while (!member.isEmpty()) { - int firstDot = member.indexOf('.'); - String first, rest; - if (firstDot != -1) { - first = member.substring(0, firstDot); - rest = member.substring(firstDot + 1); - } else { - first = member; - rest = ""; - } - result = result.childrenMap.get(first); - member = rest; - } - return result; - } - - public boolean hasMember(String member) { - String field = removeRoot(member); - return hasNonRootMember(field); - } - - private boolean hasNonRootMember(String members) { - if (members.contains(".")) { - int firstDot = members.indexOf('.'); - String first = members.substring(0, firstDot); - String rest = members.substring(firstDot + 1); - return childrenMap.containsKey(first) && childrenMap.get(first).hasNonRootMember(rest); - } else { - return childrenMap.containsKey(members); - } - } - - public MemberNode getNodeFor(String member) { - String field = removeRoot(member); - return getNodeForNonRoot(field); - } - - private MemberNode getNodeForNonRoot(String members) { - if (members.isEmpty()) { - return memberNode; - } else if (members.contains(".")) { - int firstDot = members.indexOf('.'); - String first = members.substring(0, firstDot); - String rest = members.substring(firstDot + 1); - assert childrenMap.containsKey(first); - return childrenMap.get(first).getNodeForNonRoot(rest); - } else { - assert childrenMap.containsKey(members); - return childrenMap.get(members).memberNode; - } - } - - public boolean isEmpty() { - return childrenMap.isEmpty(); - } - - public Iterable nameIterable() { - return () -> new Iterator<>() { - final Iterator it = treeIterator(); - - @Override - public boolean hasNext() { - return it.hasNext(); - } - - @Override - public String next() { - ObjectTree element = it.next(); - StringBuilder builder = new StringBuilder(); - MemberNode node = element.memberNode; - if (node == null) - return ROOT_NAME; - else - builder.append(node.getLabel()); - while (node.getParent() != null && node.getParent() instanceof MemberNode && node.getParent().getLabel().matches("^(USE|DEF|DEC)\\{")) { - node = (MemberNode) node.getParent(); - builder.insert(0, '.'); - builder.insert(0, node.getLabel()); - } - return builder.insert(0, "-root-.").toString(); - } - }; - } - - public Iterable nodeIterable() { - return () -> new Iterator<>() { - final Iterator it = treeIterator(); - - @Override - public boolean hasNext() { - return it.hasNext(); - } - - @Override - public MemberNode next() { - return it.next().memberNode; - } - }; - } - - private Iterator treeIterator() { - return new Iterator<>() { - final Set remaining = new HashSet<>(childrenMap.values()); - Iterator childIterator = null; - - @Override - public boolean hasNext() { - if (childIterator == null || !childIterator.hasNext()) - return !remaining.isEmpty(); - else - return true; - } - - @Override - public ObjectTree next() { - if (childIterator == null || !childIterator.hasNext()) { - ObjectTree tree = Utils.setPop(remaining); - childIterator = tree.treeIterator(); - return tree; - } else { - return childIterator.next(); - } - } - }; - } - - private Iterable treeIterable() { - return this::treeIterator; - } - - @SuppressWarnings("MethodDoesntCallSuperMethod") - @Override - public Object clone() { - ObjectTree clone = new ObjectTree(); - for (Map.Entry entry : childrenMap.entrySet()) - clone.childrenMap.put(entry.getKey(), entry.getValue().clone(clone)); - return clone; - } - - private ObjectTree clone(ObjectTree parent) { - ObjectTree clone = new ObjectTree(getMemberName(), parent); - for (Map.Entry entry : childrenMap.entrySet()) - clone.childrenMap.put(entry.getKey(), entry.getValue().clone(clone)); - return clone; - } - - public static String removeRoot(String fieldWithRoot) { - Matcher matcher = FIELD_SPLIT.matcher(fieldWithRoot); - if (matcher.matches()) - return matcher.group("fields") != null ? matcher.group("fields") : ""; - throw new IllegalArgumentException("Field should be of the form ., .this., where may not contain dots."); - } - - public static String removeFields(String fieldWithRoot) { - Matcher matcher = FIELD_SPLIT.matcher(fieldWithRoot); - if (matcher.matches() && matcher.group("root") != null) - return matcher.group("root"); - throw new IllegalArgumentException("Field should be of the form ., .this., where may not contain dots."); - } - - @Override - public boolean equals(Object o) { - if (this == o) return true; - if (o == null || getClass() != o.getClass()) return false; - ObjectTree tree = (ObjectTree) o; - return Objects.equals(getMemberName(), tree.getMemberName()) && - childrenMap.values().equals(tree.childrenMap.values()); - } - - @Override - public int hashCode() { - return Objects.hash(getMemberName(), childrenMap); - } - } - - static class ObjectTreeConnection { - protected final VariableAction sourceAction; - protected final VariableAction targetAction; - protected final String sourceMember; - protected final String targetMember; - - protected boolean applied = false; - - public ObjectTreeConnection(VariableAction sourceAction, VariableAction targetAction, String sourceMember, String targetMember) { - this.sourceAction = sourceAction; - this.targetAction = targetAction; - this.sourceMember = sourceMember; - this.targetMember = targetMember; - } - - public void applySDG(JSysDG graph) { - if (!applied) { - connectTrees(graph, ParameterInOutArc::new, ParameterInOutArc.ObjectFlow::new); - applied = true; - } - } - - public void applyPDG(JSysPDG graph) { - if (!applied) { - connectTrees(graph, FlowDependencyArc::new, ObjectFlowDependencyArc::new); - applied = true; - } - } - - protected void connectTrees(Graph graph, Supplier flowSupplier, Supplier objFlowSupplier) { - ObjectTree source = sourceAction.getObjectTree().findObjectTreeOfMember(sourceMember); - ObjectTree target = targetAction.getObjectTree().findObjectTreeOfMember(targetMember); - assert sourceMember.isEmpty() || source.getMemberName() != null; - assert targetMember.isEmpty() || target.getMemberName() != null; - GraphNode rootSrc = source.getMemberNode() != null ? source.getMemberNode() : sourceAction.getGraphNode(); - GraphNode rootTgt = target.getMemberNode() != null ? target.getMemberNode() : targetAction.getGraphNode(); - graph.addEdge(rootSrc, rootTgt, objFlowSupplier.get()); - for (ObjectTree tree : target.treeIterable()) { - MemberNode src = source.getNodeForNonRoot(tree.getMemberName()); - MemberNode tgt = tree.getMemberNode(); - if (tree.childrenMap.isEmpty()) - graph.addEdge(src, tgt, flowSupplier.get()); - else - graph.addEdge(src, tgt, objFlowSupplier.get()); - } - } - - - } } 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 c43e3c2..77b865a 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 @@ -19,7 +19,6 @@ import com.github.javaparser.symbolsolver.javaparsermodel.declarations.JavaParse import es.upv.mist.slicing.graphs.ClassGraph; import es.upv.mist.slicing.graphs.ExpressionObjectTreeFinder; import es.upv.mist.slicing.graphs.GraphNodeContentVisitor; -import es.upv.mist.slicing.nodes.VariableAction.ObjectTree; import es.upv.mist.slicing.nodes.io.ActualIONode; import es.upv.mist.slicing.nodes.io.CallNode; import es.upv.mist.slicing.utils.ASTUtils; -- GitLab From 7860e367a1ba87620bdc2062cab55f088fe4c379 Mon Sep 17 00:00:00 2001 From: Carlos Galindo Date: Tue, 9 Mar 2021 12:36:20 +0100 Subject: [PATCH 10/60] Improve OutNodeVariableVisitor --- .../graphs/sdg/OutNodeVariableVisitor.java | 59 ++++++++++++++----- 1 file changed, 43 insertions(+), 16 deletions(-) diff --git a/sdg-core/src/main/java/es/upv/mist/slicing/graphs/sdg/OutNodeVariableVisitor.java b/sdg-core/src/main/java/es/upv/mist/slicing/graphs/sdg/OutNodeVariableVisitor.java index aaabe19..019b8a6 100644 --- a/sdg-core/src/main/java/es/upv/mist/slicing/graphs/sdg/OutNodeVariableVisitor.java +++ b/sdg-core/src/main/java/es/upv/mist/slicing/graphs/sdg/OutNodeVariableVisitor.java @@ -8,52 +8,79 @@ import java.util.Set; /** Visitor that obtains a set of variables that may have been redefined in * an expression passed as parameter to a call. */ -public class OutNodeVariableVisitor extends VoidVisitorAdapter> { +public class OutNodeVariableVisitor extends VoidVisitorAdapter> { @Override - public void visit(ArrayAccessExpr n, Set variables) { + public void visit(ArrayAccessExpr n, Set variables) { n.getName().accept(this, variables); } @Override - public void visit(CastExpr n, Set variables) { + public void visit(CastExpr n, Set variables) { n.getExpression().accept(this, variables); } @Override - public void visit(ConditionalExpr n, Set variables) { + 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) { + public void visit(EnclosedExpr n, Set variables) { n.getInner().accept(this, variables); } @Override - public void visit(ExpressionStmt n, Set variables) { + public void visit(ExpressionStmt n, Set variables) { n.getExpression().accept(this, variables); } @Override - public void visit(FieldAccessExpr n, Set variables) { + public void visit(FieldAccessExpr n, Set variables) { n.getScope().accept(this, variables); } @Override - public void visit(NameExpr n, Set variables) { + public void visit(ThisExpr n, Set variables) { variables.add(n); } @Override - public void visit(UnaryExpr n, Set variables) { - switch (n.getOperator()) { - case POSTFIX_DECREMENT: - case POSTFIX_INCREMENT: - case PREFIX_DECREMENT: - case PREFIX_INCREMENT: - n.getExpression().accept(this, variables); - } + public void visit(NameExpr n, Set variables) { + variables.add(n); } + + // Expressions that stop the visit: no object can be outputted, modified inside the call and returned so that + // we may access its value. + + @Override + public void visit(UnaryExpr n, Set variables) {} + + @Override + public void visit(ArrayCreationExpr n, Set arg) {} + + @Override + public void visit(ArrayInitializerExpr n, Set arg) {} + + @Override + public void visit(BinaryExpr n, Set arg) {} + + @Override + public void visit(ClassExpr n, Set arg) {} + + @Override + public void visit(InstanceOfExpr n, Set arg) {} + + @Override + public void visit(MethodCallExpr n, Set arg) {} + + @Override + public void visit(ObjectCreationExpr n, Set arg) {} + + @Override + public void visit(LambdaExpr n, Set arg) {} + + @Override + public void visit(MethodReferenceExpr n, Set arg) {} } -- GitLab From c41073661cd96f55ca1e4ba6ae23c3105367cfcc Mon Sep 17 00:00:00 2001 From: Carlos Galindo Date: Wed, 10 Mar 2021 10:50:35 +0100 Subject: [PATCH 11/60] Don't reuse the same CFG to compute PPDG exclusive edges. --- .../es/upv/mist/slicing/graphs/augmented/PPDG.java | 2 +- .../es/upv/mist/slicing/graphs/jsysdg/JSysPDG.java | 10 +++++++++- 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/sdg-core/src/main/java/es/upv/mist/slicing/graphs/augmented/PPDG.java b/sdg-core/src/main/java/es/upv/mist/slicing/graphs/augmented/PPDG.java index 853d85d..07d3a49 100644 --- a/sdg-core/src/main/java/es/upv/mist/slicing/graphs/augmented/PPDG.java +++ b/sdg-core/src/main/java/es/upv/mist/slicing/graphs/augmented/PPDG.java @@ -46,7 +46,7 @@ public class PPDG extends APDG { /** Finds the CD arcs that are only present in the PPDG and marks them as such. */ protected void markPPDGExclusiveEdges(CallableDeclaration declaration) { - APDG apdg = new APDG((ACFG) cfg); + APDG apdg = new APDG(); apdg.build(declaration); Set apdgArcs = apdg.edgeSet().stream() .filter(Arc::isUnconditionalControlDependencyArc) diff --git a/sdg-core/src/main/java/es/upv/mist/slicing/graphs/jsysdg/JSysPDG.java b/sdg-core/src/main/java/es/upv/mist/slicing/graphs/jsysdg/JSysPDG.java index 9871ef3..1e47a76 100644 --- a/sdg-core/src/main/java/es/upv/mist/slicing/graphs/jsysdg/JSysPDG.java +++ b/sdg-core/src/main/java/es/upv/mist/slicing/graphs/jsysdg/JSysPDG.java @@ -115,7 +115,15 @@ public class JSysPDG extends ESPDG { } @Override - protected void expandCalls() {} + protected void expandCalls() { + for (GraphNode graphNode : vertexSet()) { + for (VariableAction action : List.copyOf(graphNode.getVariableActions())) { + if (action instanceof VariableAction.Movable) { + ((VariableAction.Movable) action).moveOnly(); + } + } + } + } protected void addSyntheticNodesToPDG() { for (GraphNode node : cfg.vertexSet()) { -- GitLab From da46f315f8e1c492e75a134ea941d5537e647ce8 Mon Sep 17 00:00:00 2001 From: Carlos Galindo Date: Wed, 10 Mar 2021 10:59:55 +0100 Subject: [PATCH 12/60] Redesigned VariableAction and ObjectTree * VariableAction has 0..1 root OTs. * VariableAction no longer has expressions, but has its kind of variable and name (in the future possibly type). * Simplified checks and functions as a result --- .../slicing/arcs/pdg/DataDependencyArc.java | 2 +- .../upv/mist/slicing/graphs/ClassGraph.java | 2 +- .../graphs/ExpressionObjectTreeFinder.java | 40 +- .../mist/slicing/graphs/cfg/CFGBuilder.java | 2 +- .../mist/slicing/graphs/jsysdg/JSysCFG.java | 8 +- .../graphs/jsysdg/JSysCallConnector.java | 4 +- .../mist/slicing/graphs/jsysdg/JSysPDG.java | 62 +-- .../graphs/oo/DynamicTypeResolver.java | 8 +- .../sdg/InterproceduralActionFinder.java | 47 +- .../sdg/InterproceduralDefinitionFinder.java | 66 +-- .../sdg/InterproceduralUsageFinder.java | 20 +- .../es/upv/mist/slicing/nodes/GraphNode.java | 4 +- .../es/upv/mist/slicing/nodes/ObjectTree.java | 17 +- .../mist/slicing/nodes/VariableAction.java | 433 +++++++++--------- .../mist/slicing/nodes/VariableVisitor.java | 79 ++-- .../mist/slicing/nodes/io/ActualIONode.java | 17 +- .../mist/slicing/nodes/io/FormalIONode.java | 12 +- .../es/upv/mist/slicing/utils/ASTUtils.java | 20 +- 18 files changed, 408 insertions(+), 435 deletions(-) diff --git a/sdg-core/src/main/java/es/upv/mist/slicing/arcs/pdg/DataDependencyArc.java b/sdg-core/src/main/java/es/upv/mist/slicing/arcs/pdg/DataDependencyArc.java index acc2ae2..ed8650c 100644 --- a/sdg-core/src/main/java/es/upv/mist/slicing/arcs/pdg/DataDependencyArc.java +++ b/sdg-core/src/main/java/es/upv/mist/slicing/arcs/pdg/DataDependencyArc.java @@ -31,7 +31,7 @@ public class DataDependencyArc extends Arc { protected final VariableAction targetVar; public DataDependencyArc(VariableAction sourceVar, VariableAction targetVar) { - super(sourceVar.getVariable()); + super(sourceVar.getName()); if (VALID_VA_COMBOS.stream().noneMatch(p -> p.test(sourceVar, targetVar))) throw new IllegalArgumentException("Illegal combination of actions: " + sourceVar + ", " + targetVar); this.sourceVar = sourceVar; diff --git a/sdg-core/src/main/java/es/upv/mist/slicing/graphs/ClassGraph.java b/sdg-core/src/main/java/es/upv/mist/slicing/graphs/ClassGraph.java index 3245bf4..76a3d4e 100644 --- a/sdg-core/src/main/java/es/upv/mist/slicing/graphs/ClassGraph.java +++ b/sdg-core/src/main/java/es/upv/mist/slicing/graphs/ClassGraph.java @@ -156,7 +156,7 @@ public class ClassGraph extends DirectedPseudograph(action, arg)); return; } @@ -110,18 +109,9 @@ public class ExpressionObjectTreeFinder { @Override public void visit(ThisExpr n, String arg) { for (VariableAction action : graphNode.getVariableActions()) { - if (action.isUsage()) { - if (action.getVariableExpression() == null) { - if (action.getVariable().matches("^.*this$")) { - list.add(new Pair<>(action, arg)); - return; - } - } else { - if (n.equals(action.getVariableExpression())) { - list.add(new Pair<>(action, arg)); - return; - } - } + if (action.isUsage() && action.getName().matches("^.*this$")) { + list.add(new Pair<>(action, arg)); + return; } } throw new IllegalStateException("Could not find USE(this)"); @@ -147,12 +137,18 @@ public class ExpressionObjectTreeFinder { protected void visitCall(Expression call, String arg) { if (ASTUtils.shouldVisitArgumentsForMethodCalls((Resolvable) call)) return; + VariableAction lastUseOut = null; for (VariableAction variableAction : graphNode.getVariableActions()) { - if (variableAction.isUsage() && - variableAction.getVariable().equals(VARIABLE_NAME_OUTPUT) && - call.equals(variableAction.getVariableExpression())) { - list.add(new Pair<>(variableAction, arg)); - return; + if (variableAction instanceof VariableAction.CallMarker) { + VariableAction.CallMarker marker = (VariableAction.CallMarker) variableAction; + if (ASTUtils.equalsWithRange((Node) marker.getCall(), call) && !marker.isEnter()) { + assert lastUseOut != null; + list.add(new Pair<>(lastUseOut, arg)); + return; + } + } + if (variableAction.isUsage() && variableAction.getName().equals(VARIABLE_NAME_OUTPUT)) { + lastUseOut = variableAction; } } throw new IllegalStateException("Could not find USE(-output-) corresponding to call " + call); diff --git a/sdg-core/src/main/java/es/upv/mist/slicing/graphs/cfg/CFGBuilder.java b/sdg-core/src/main/java/es/upv/mist/slicing/graphs/cfg/CFGBuilder.java index a20e4d2..1386f8f 100644 --- a/sdg-core/src/main/java/es/upv/mist/slicing/graphs/cfg/CFGBuilder.java +++ b/sdg-core/src/main/java/es/upv/mist/slicing/graphs/cfg/CFGBuilder.java @@ -374,7 +374,7 @@ public class CFGBuilder extends VoidVisitorAdapter { * @see #VARIABLE_NAME_OUTPUT */ protected void addMethodOutput(CallableDeclaration callableDeclaration, GraphNode exit) { if (!(callableDeclaration instanceof MethodDeclaration) || !((MethodDeclaration) callableDeclaration).getType().isVoidType()) { - VariableAction usage = new VariableAction.Usage(null, VARIABLE_NAME_OUTPUT, exit); + VariableAction usage = new VariableAction.Usage(VariableAction.DeclarationType.SYNTHETIC, VARIABLE_NAME_OUTPUT, exit); exit.addVariableAction(new VariableAction.Movable(usage, OutputNode.create(callableDeclaration))); } } diff --git a/sdg-core/src/main/java/es/upv/mist/slicing/graphs/jsysdg/JSysCFG.java b/sdg-core/src/main/java/es/upv/mist/slicing/graphs/jsysdg/JSysCFG.java index 41b28d8..302cb49 100644 --- a/sdg-core/src/main/java/es/upv/mist/slicing/graphs/jsysdg/JSysCFG.java +++ b/sdg-core/src/main/java/es/upv/mist/slicing/graphs/jsysdg/JSysCFG.java @@ -57,7 +57,7 @@ public class JSysCFG extends ESCFG { /** Given a usage of an object member, find the last definitions of that member. * This method returns a list of variable actions, where the caller can find the member. */ public List findLastDefinitionOfObjectMember(VariableAction usage, String member) { - return findLastVarActionsFrom(usage, def -> def.isDefinition() && def.getObjectTree().hasMember(member)); + return findLastVarActionsFrom(usage, def -> def.isDefinition() && def.hasTreeMember(member)); } /** Given a usage of a primitive variable, find the last def actions that affect it. */ @@ -80,7 +80,7 @@ public class JSysCFG extends ESCFG { public List findNextObjectDefinitionsFor(VariableAction definition, String member) { if (!this.containsVertex(definition.getGraphNode())) throw new NodeNotFoundException(definition.getGraphNode(), this); // TODO: al crear los root/resumen, las movable no se ponen en el movable - if (definition.getObjectTree().hasMember(member)) + if (definition.hasTreeMember(member)) return List.of(definition); List list = new LinkedList<>(); findNextVarActionsFor(new HashSet<>(), list, definition.getGraphNode(), definition, VariableAction::isDefinition, member); @@ -102,7 +102,7 @@ public class JSysCFG extends ESCFG { if (!list.isEmpty()) { boolean found = false; for (VariableAction variableAction : list) { - if (!variableAction.isOptional() && variableAction.getObjectTree().hasMember(memberName)) { + if (!variableAction.isOptional() && variableAction.hasTreeMember(memberName)) { found = true; break; } @@ -189,7 +189,7 @@ public class JSysCFG extends ESCFG { protected void addMethodOutput(CallableDeclaration callableDeclaration, GraphNode exit) { super.addMethodOutput(callableDeclaration, exit); for (VariableAction action : exit.getVariableActions()) { - if (action.getVariable().equals(VARIABLE_NAME_OUTPUT)) { + if (action.getName().equals(VARIABLE_NAME_OUTPUT)) { expandOutputVariable(callableDeclaration, action); break; } diff --git a/sdg-core/src/main/java/es/upv/mist/slicing/graphs/jsysdg/JSysCallConnector.java b/sdg-core/src/main/java/es/upv/mist/slicing/graphs/jsysdg/JSysCallConnector.java index 4762449..255574b 100644 --- a/sdg-core/src/main/java/es/upv/mist/slicing/graphs/jsysdg/JSysCallConnector.java +++ b/sdg-core/src/main/java/es/upv/mist/slicing/graphs/jsysdg/JSysCallConnector.java @@ -38,10 +38,10 @@ public class JSysCallConnector extends ExceptionSensitiveCallConnector { protected void connectObjectOutput(GraphNode methodOutputNode, GraphNode callReturnNode) { List outputList = methodOutputNode.getVariableActions(); assert outputList.size() == 1; - assert outputList.get(0).isUsage() && outputList.get(0).getVariable().equals(CFGBuilder.VARIABLE_NAME_OUTPUT); + assert outputList.get(0).isUsage() && outputList.get(0).getName().equals(CFGBuilder.VARIABLE_NAME_OUTPUT); List returnList = callReturnNode.getVariableActions(); assert returnList.size() == 1; - assert returnList.get(0).isDefinition() && returnList.get(0).getVariable().equals(CFGBuilder.VARIABLE_NAME_OUTPUT); + assert returnList.get(0).isDefinition() && returnList.get(0).getName().equals(CFGBuilder.VARIABLE_NAME_OUTPUT); VariableAction source = outputList.get(0); VariableAction target = returnList.get(0); source.applySDGTreeConnection((JSysDG) sdg, target); diff --git a/sdg-core/src/main/java/es/upv/mist/slicing/graphs/jsysdg/JSysPDG.java b/sdg-core/src/main/java/es/upv/mist/slicing/graphs/jsysdg/JSysPDG.java index 1e47a76..97ad616 100644 --- a/sdg-core/src/main/java/es/upv/mist/slicing/graphs/jsysdg/JSysPDG.java +++ b/sdg-core/src/main/java/es/upv/mist/slicing/graphs/jsysdg/JSysPDG.java @@ -6,13 +6,14 @@ import es.upv.mist.slicing.arcs.pdg.TotalDefinitionDependenceArc; import es.upv.mist.slicing.graphs.exceptionsensitive.ESPDG; import es.upv.mist.slicing.graphs.pdg.PDG; 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.io.CallNode; -import es.upv.mist.slicing.nodes.io.IONode; import es.upv.mist.slicing.nodes.oo.MemberNode; import java.util.Deque; import java.util.LinkedList; +import java.util.List; public class JSysPDG extends ESPDG { public JSysPDG() { @@ -41,7 +42,7 @@ public class JSysPDG extends ESPDG { // def --flow--> use || dec --flow--> def protected void addFlowDependencyArc(VariableAction source, VariableAction target) { - addEdge(graphNodeOf(source), graphNodeOf(target), new FlowDependencyArc(source.getVariable())); + addEdge(graphNodeOf(source), graphNodeOf(target), new FlowDependencyArc(source.getName())); } // definicion de miembro --flow--> uso de miembro @@ -56,15 +57,18 @@ public class JSysPDG extends ESPDG { } protected void addTotalDefinitionDependencyArc(VariableAction totalDefinition, VariableAction target, String member) { - addEdge(totalDefinition.getObjectTree().getNodeFor(member), - target.getObjectTree().getNodeFor(member), - new TotalDefinitionDependenceArc()); + if (member.equals(ObjectTree.ROOT_NAME)) + addEdge(graphNodeOf(totalDefinition), graphNodeOf(target), new TotalDefinitionDependenceArc()); + else + addEdge(totalDefinition.getObjectTree().getNodeFor(member), + target.getObjectTree().getNodeFor(member), + new TotalDefinitionDependenceArc()); } protected GraphNode graphNodeOf(VariableAction action) { if (action instanceof VariableAction.Movable) return ((VariableAction.Movable) action).getRealNode(); - if (action.getObjectTree().getMemberNode() != null) + if (action.hasObjectTree()) return action.getObjectTree().getMemberNode(); return action.getGraphNode(); } @@ -80,6 +84,11 @@ public class JSysPDG extends ESPDG { @Override protected void buildDataDependency() { addSyntheticNodesToPDG(); + applyTreeConnections(); + buildJSysDataDependency(); + } + + protected void buildJSysDataDependency() { JSysCFG jSysCFG = (JSysCFG) cfg; for (GraphNode node : vertexSet()) { for (VariableAction varAct : node.getVariableActions()) { @@ -88,9 +97,9 @@ public class JSysPDG extends ESPDG { // root jSysCFG.findLastTotalDefinitionOf(varAct, "-root-").forEach(totalDef -> addTotalDefinitionDependencyArc(totalDef, varAct, "-root-")); // members - for (String member : varAct.getObjectTree().nameIterable()) { - jSysCFG.findLastTotalDefinitionOf(varAct, member).forEach(totalDef -> addTotalDefinitionDependencyArc(totalDef, varAct, member)); - } + if (varAct.hasObjectTree()) + for (String member : varAct.getObjectTree().nameIterable()) + jSysCFG.findLastTotalDefinitionOf(varAct, member).forEach(totalDef -> addTotalDefinitionDependencyArc(totalDef, varAct, member)); } // Object flow, flow and declaration-definition dependencies if (varAct.isUsage()) { @@ -98,15 +107,17 @@ public class JSysPDG extends ESPDG { jSysCFG.findLastDefinitionOfPrimitive(varAct).forEach(def -> addFlowDependencyArc(def, varAct)); else { jSysCFG.findLastDefinitionOfObjectRoot(varAct).forEach(def -> addObjectFlowDependencyArc(def, varAct)); - for (String member : varAct.getObjectTree().nameIterable()) { - jSysCFG.findLastDefinitionOfObjectMember(varAct, member).forEach(def -> addFlowDependencyArc(def, varAct, member)); - addValueDependencyArc(varAct, member, node); + if (varAct.hasObjectTree()) { + for (String member : varAct.getObjectTree().nameIterable()) { + jSysCFG.findLastDefinitionOfObjectMember(varAct, member).forEach(def -> addFlowDependencyArc(def, varAct, member)); + addValueDependencyArc(varAct, member, node); + } } } } else if (varAct.isDefinition()) { if (!varAct.isSynthetic()) jSysCFG.findDeclarationFor(varAct).ifPresent(dec -> addFlowDependencyArc(dec, varAct)); - if (!varAct.isPrimitive()) + if (!varAct.isPrimitive() && varAct.hasObjectTree()) for (String member : varAct.getObjectTree().nameIterable()) jSysCFG.findNextObjectDefinitionsFor(varAct, member).forEach(def -> addObjectFlowDependencyArc(varAct, member, def)); } @@ -146,29 +157,26 @@ public class JSysPDG extends ESPDG { } GraphNode parentNode; // node that represents the root of the object tree if (va instanceof VariableAction.Movable) { // TODO: there are Movables with multiple VA per movable!!! - GraphNode realNode = ((VariableAction.Movable) va).getRealNode(); - addVertex(realNode); - connectRealNode(node, callNodeStack.peek(), realNode); - parentNode = realNode; - } else if (va.getObjectTree().isEmpty() || node instanceof IONode) { - parentNode = node; + VariableAction.Movable movable = (VariableAction.Movable) va; + addVertex(movable.getRealNode()); + connectRealNode(node, callNodeStack.peek(), movable.getRealNode()); + parentNode = movable.getRealNode(); } else { - MemberNode memberNode = new MemberNode(va.toString(), null); - va.getObjectTree().setMemberNode(memberNode); - parentNode = memberNode; - addVertex(parentNode); - addControlDependencyArc(node, parentNode); + parentNode = node; } + if (!va.hasObjectTree()) + continue; // Extract the member nodes contained within the object tree - if (va.getObjectTree().getMemberNode() != null) - insertMemberNode(va.getObjectTree().getMemberNode(), parentNode); + insertMemberNode(va.getObjectTree().getMemberNode(), parentNode); for (MemberNode memberNode : va.getObjectTree().nodeIterable()) { insertMemberNode(memberNode, parentNode); } } assert callNodeStack.isEmpty(); } - // Create the pre-established connections + } + + protected void applyTreeConnections() { cfg.vertexSet().stream() .flatMap(node -> node.getVariableActions().stream()) .forEach(va -> va.applyPDGTreeConnections(JSysPDG.this)); diff --git a/sdg-core/src/main/java/es/upv/mist/slicing/graphs/oo/DynamicTypeResolver.java b/sdg-core/src/main/java/es/upv/mist/slicing/graphs/oo/DynamicTypeResolver.java index f6d5612..7796068 100644 --- a/sdg-core/src/main/java/es/upv/mist/slicing/graphs/oo/DynamicTypeResolver.java +++ b/sdg-core/src/main/java/es/upv/mist/slicing/graphs/oo/DynamicTypeResolver.java @@ -75,12 +75,8 @@ public class DynamicTypeResolver { /** Searches for the corresponding VariableAction object, then calls {@link #resolveVariableAction(VariableAction)}. */ protected Stream resolveVariable(Expression expression, GraphNode graphNode) { - Optional va = graphNode.getVariableActions().stream() - .filter(action -> action.hasVariableExpression() && ASTUtils.equalsWithRange(action.getVariableExpression(), expression)) - .findFirst(); - if (va.isEmpty()) - return anyTypeOf(expression); - return resolveVariableAction(va.get()); + // TODO: implement a search like ExpressionObjectTreeFinder. + return anyTypeOf(expression); } /** diff --git a/sdg-core/src/main/java/es/upv/mist/slicing/graphs/sdg/InterproceduralActionFinder.java b/sdg-core/src/main/java/es/upv/mist/slicing/graphs/sdg/InterproceduralActionFinder.java index df1caf6..a2fc064 100644 --- a/sdg-core/src/main/java/es/upv/mist/slicing/graphs/sdg/InterproceduralActionFinder.java +++ b/sdg-core/src/main/java/es/upv/mist/slicing/graphs/sdg/InterproceduralActionFinder.java @@ -8,8 +8,6 @@ import com.github.javaparser.ast.stmt.ExplicitConstructorInvocationStmt; import com.github.javaparser.resolution.Resolvable; import com.github.javaparser.resolution.UnsolvedSymbolException; import com.github.javaparser.resolution.declarations.ResolvedMethodLikeDeclaration; -import com.github.javaparser.resolution.declarations.ResolvedParameterDeclaration; -import com.github.javaparser.resolution.declarations.ResolvedValueDeclaration; import es.upv.mist.slicing.graphs.BackwardDataFlowAnalysis; import es.upv.mist.slicing.graphs.CallGraph; import es.upv.mist.slicing.graphs.cfg.CFG; @@ -23,8 +21,6 @@ import java.util.function.Consumer; import java.util.function.Predicate; import java.util.stream.Stream; -// TODO: this approach of generating actual nodes may skip an argument; this is only a problem if there is a definition -// TODO: update placement of actual and formal outputs for ESSDG (see if the definition/usage reaches all/any exits). /** * A backward data flow analysis on the call graph and a map of CFGs, to find which callable * declarations define, use or declare which variables, interprocedurally. @@ -78,7 +74,7 @@ public abstract class InterproceduralActionFinder exte try { handler.accept(location, action); } catch (UnsolvedSymbolException e) { - Logger.log("Skipping a symbol, cannot be resolved: " + action.getVariable()); + Logger.log("Skipping a symbol, cannot be resolved: " + action.getName()); } } @@ -108,11 +104,11 @@ public abstract class InterproceduralActionFinder exte /** Obtains the expression passed as argument for the given action at the given call. If {@code input} * is false, primitive parameters will be skipped, as their value cannot be redefined.*/ - protected Expression extractArgument(ResolvedParameterDeclaration p, CallGraph.Edge edge, boolean input) { + protected Expression extractArgument(VariableAction action, CallGraph.Edge edge, boolean input) { CallableDeclaration callTarget = graph.getEdgeTarget(edge).getDeclaration(); - if (!input && p.getType().isPrimitive()) + if (!input && action.isPrimitive()) return null; // primitives do not have actual-out! - int paramIndex = ASTUtils.getMatchingParameterIndex(callTarget, p); + int paramIndex = ASTUtils.getMatchingParameterIndex(callTarget, action.getName()); return ASTUtils.getResolvableArgs(edge.getCall()).get(paramIndex); } @@ -136,7 +132,7 @@ public abstract class InterproceduralActionFinder exte * the scope of the method. */ protected static String obtainAliasedFieldName(VariableAction action, CallGraph.Edge edge, String scope) { if (scope.isEmpty()) { - return action.getVariable(); + return action.getName(); } else { String newPrefix = scope; newPrefix = newPrefix.replaceAll("((\\.)super|^super)(\\.)?", "$2this$3"); @@ -144,7 +140,7 @@ public abstract class InterproceduralActionFinder exte String fqName = ASTUtils.getClassNode(edge.getGraphNode().getAstNode()).getFullyQualifiedName().orElseThrow(); newPrefix = fqName + ".this"; } - String withPrefix = action.getVariable(); + String withPrefix = action.getName(); String withoutPrefix = withPrefix.replaceFirst("^((.*\\.)?this\\.?)", ""); String result = newPrefix + withoutPrefix; return result.replaceFirst("this(\\.this)+", "this"); @@ -210,28 +206,15 @@ public abstract class InterproceduralActionFinder exte @Override public int compare(A o1, A o2) { - ResolvedValueDeclaration r1 = null; - ResolvedValueDeclaration r2 = null; - try { - r1 = o1.getResolvedValueDeclaration(); - r2 = o2.getResolvedValueDeclaration(); - if (r1.isParameter() && r2.isParameter()) - return -Integer.compare(ASTUtils.getMatchingParameterIndex(graph.getEdgeTarget(edge).getDeclaration(), r1.asParameter()), - ASTUtils.getMatchingParameterIndex(graph.getEdgeTarget(edge).getDeclaration(), r2.asParameter())); - else if (r1.isField() && r2.isField()) - return 0; - else if (r1.isParameter() && r2.isField()) - return -1; - else if (r1.isField() && r2.isParameter()) - return 1; - } catch (UnsolvedSymbolException e) { - if (r1 == null) - return 1; - else if (r2 == null) - return -1; - else - return 0; - } + if (o1.isParameter() && o2.isParameter()) + return -Integer.compare(ASTUtils.getMatchingParameterIndex(graph.getEdgeTarget(edge).getDeclaration(), o1.getName()), + ASTUtils.getMatchingParameterIndex(graph.getEdgeTarget(edge).getDeclaration(), o2.getName())); + else if (o1.isField() && o2.isField()) + return 0; + else if (o1.isParameter() && o2.isField()) + return -1; + else if (o1.isField() && o2.isParameter()) + return 1; throw new IllegalArgumentException("One or more arguments is not a field or parameter"); } } 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 2a345f3..d5125c8 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 @@ -1,19 +1,20 @@ 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.NameExpr; import com.github.javaparser.ast.expr.ObjectCreationExpr; -import com.github.javaparser.resolution.declarations.ResolvedValueDeclaration; import es.upv.mist.slicing.graphs.CallGraph; 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.DeclarationType; import es.upv.mist.slicing.nodes.VariableAction.Definition; import es.upv.mist.slicing.nodes.VariableAction.Movable; import es.upv.mist.slicing.nodes.io.ActualIONode; import es.upv.mist.slicing.nodes.io.FormalIONode; +import es.upv.mist.slicing.utils.ASTUtils; import java.util.*; import java.util.stream.Stream; @@ -27,13 +28,12 @@ public class InterproceduralDefinitionFinder extends InterproceduralActionFinder @Override protected void handleFormalAction(CallGraph.Vertex vertex, Definition def) { CFG cfg = cfgMap.get(vertex.getDeclaration()); - ResolvedValueDeclaration resolved = def.getResolvedValueDeclaration(); - if (!resolved.isParameter() || !resolved.getType().isPrimitive()) { - FormalIONode formalOut = FormalIONode.createFormalOut(vertex.getDeclaration(), resolved); + if (!def.isParameter() || !def.isPrimitive()) { + FormalIONode formalOut = FormalIONode.createFormalOut(vertex.getDeclaration(), def.getName()); Movable movable = new Movable(def.toUsage(cfg.getExitNode()), formalOut); cfg.getExitNode().addVariableAction(movable); } - FormalIONode formalIn = FormalIONode.createFormalInDecl(vertex.getDeclaration(), resolved); + FormalIONode formalIn = FormalIONode.createFormalInDecl(vertex.getDeclaration(), def.getName()); cfg.getRootNode().addVariableAction(new Movable(def.toDeclaration(cfg.getRootNode()), formalIn)); } @@ -41,36 +41,50 @@ public class InterproceduralDefinitionFinder extends InterproceduralActionFinder protected void handleActualAction(CallGraph.Edge edge, Definition def) { List movables = new LinkedList<>(); GraphNode graphNode = edge.getGraphNode(); - ResolvedValueDeclaration resolved = def.getResolvedValueDeclaration(); - if (resolved.isParameter()) { - Expression arg = extractArgument(resolved.asParameter(), edge, false); + if (def.isParameter()) { + Expression arg = extractArgument(def, edge, false); if (arg == null) return; - ActualIONode actualOut = ActualIONode.createActualOut(edge.getCall(), resolved, arg); - if (resolved.isParameter()) { - Set exprSet = new HashSet<>(); - arg.accept(new OutNodeVariableVisitor(), exprSet); - for (NameExpr nameExpr : exprSet) - movables.add(new Movable(new Definition(nameExpr, nameExpr.toString(), graphNode, (ObjectTree) def.getObjectTree().clone()), actualOut)); + ActualIONode actualOut = ActualIONode.createActualOut(edge.getCall(), def.getName(), arg); + extractOutputVariablesAsMovables(arg, movables, graphNode, actualOut, def); + } else if (def.isField()) { + if (def.isStatic()) { + // Known limitation: static fields } else { - movables.add(new Movable(def.toDefinition(graphNode), actualOut)); + /* NEW */ + // An object creation expression doesn't alter an existing object via actual-out + // it is returned and assigned via -output-. + if (edge.getCall() instanceof ObjectCreationExpr) + return; + + ActualIONode actualOut = ActualIONode.createActualOut(edge.getCall(), def.getName(), null); + Optional scope = ASTUtils.getResolvableScope(edge.getCall()); + if (scope.isPresent()) { + extractOutputVariablesAsMovables(scope.get(), movables, graphNode, actualOut, def); + } else { + String fqClassName = ASTUtils.getClassNode((Node) edge.getCall()).getFullyQualifiedName().orElseThrow(); + var movableDef = new Definition(DeclarationType.FIELD, fqClassName + ".this", graphNode, (ObjectTree) def.getObjectTree().clone()); + movables.add(new Movable(movableDef, actualOut)); + } } - } else if (resolved.isField()) { - // Known limitation: static fields - // An object creation expression doesn't alter an existing object via actual-out - // it is returned and assigned via -output-. - if (edge.getCall() instanceof ObjectCreationExpr) - return; - String aliasedName = obtainAliasedFieldName(def, edge); - ActualIONode actualOut = ActualIONode.createActualOut(edge.getCall(), resolved, null); - var movableDef = new Definition(obtainScope(edge.getCall()), aliasedName, graphNode, (ObjectTree) def.getObjectTree().clone()); - movables.add(new Movable(movableDef, actualOut)); } else { throw new IllegalStateException("Definition must be either from a parameter or a field!"); } graphNode.addActionsForCall(movables, edge.getCall(), false); } + protected void extractOutputVariablesAsMovables(Expression e, List movables, GraphNode graphNode, ActualIONode actualOut, VariableAction def) { + Set defExpressions = new HashSet<>(); + e.accept(new OutNodeVariableVisitor(), defExpressions); + for (Expression expression : defExpressions) { + 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)); + } + } + @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 842ee49..9685263 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 @@ -5,7 +5,6 @@ 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.declarations.ResolvedValueDeclaration; import es.upv.mist.slicing.graphs.CallGraph; import es.upv.mist.slicing.graphs.ExpressionObjectTreeFinder; import es.upv.mist.slicing.graphs.cfg.CFG; @@ -33,8 +32,7 @@ public class InterproceduralUsageFinder extends InterproceduralActionFinder edge, Usage use) { GraphNode graphNode = edge.getGraphNode(); - ResolvedValueDeclaration resolved = use.getResolvedValueDeclaration(); - if (resolved.isParameter()) { - ActualIONode actualIn = locateActualInNode(edge, resolved.getName()); - Definition def = new Definition(null, "-arg-in-", graphNode, (ObjectTree) use.getObjectTree().clone()); + if (use.isParameter()) { + ActualIONode actualIn = locateActualInNode(edge, use.getName()); + Definition def = new Definition(VariableAction.DeclarationType.SYNTHETIC, "-arg-in-", graphNode, (ObjectTree) use.getObjectTree().clone()); Movable movDef = new Movable(def, actualIn); actualIn.addVariableAction(movDef); graphNode.addVariableActionAfterLastMatchingRealNode(movDef, actualIn); ExpressionObjectTreeFinder finder = new ExpressionObjectTreeFinder(graphNode); finder.locateAndMarkTransferenceToRoot(actualIn.getArgument(), def); - } else if (resolved.isField()) { - boolean isStatic = resolved.isType() || (resolved.getType() != null && resolved.asField().isStatic()); - if (isStatic) { + } else if (use.isField()) { + if (use.isStatic()) { // Known limitation: static fields } else { // An object creation expression input an existing object via actual-in because it creates it. if (edge.getCall() instanceof ObjectCreationExpr) return; - ActualIONode actualIn = locateActualInNode(edge, resolved.getName()); - Definition def = new Definition(null, "-scope-in-", graphNode, (ObjectTree) use.getObjectTree().clone()); + ActualIONode actualIn = locateActualInNode(edge, use.getName()); + Definition def = new Definition(VariableAction.DeclarationType.SYNTHETIC, "-scope-in-", graphNode, (ObjectTree) use.getObjectTree().clone()); Movable movDef = new Movable(def, actualIn); Expression scope = Objects.requireNonNullElseGet(actualIn.getArgument(), ThisExpr::new); actualIn.addVariableAction(movDef); diff --git a/sdg-core/src/main/java/es/upv/mist/slicing/nodes/GraphNode.java b/sdg-core/src/main/java/es/upv/mist/slicing/nodes/GraphNode.java index d5c5690..5738942 100644 --- a/sdg-core/src/main/java/es/upv/mist/slicing/nodes/GraphNode.java +++ b/sdg-core/src/main/java/es/upv/mist/slicing/nodes/GraphNode.java @@ -202,12 +202,12 @@ public class GraphNode implements Comparable> { } public void addVADefineActiveException(Expression expression) { - VariableAction.Definition def = new VariableAction.Definition(null, ACTIVE_EXCEPTION_VARIABLE, this, expression); + VariableAction.Definition def = new VariableAction.Definition(VariableAction.DeclarationType.SYNTHETIC, ACTIVE_EXCEPTION_VARIABLE, this, expression); variableActions.add(def); } public void addVAUseActiveException() { - VariableAction.Usage use = new VariableAction.Usage(null, ACTIVE_EXCEPTION_VARIABLE, this); + VariableAction.Usage use = new VariableAction.Usage(VariableAction.DeclarationType.SYNTHETIC, ACTIVE_EXCEPTION_VARIABLE, this); variableActions.add(use); } 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 364dcb6..4cade75 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 @@ -8,7 +8,8 @@ import java.util.regex.Matcher; import java.util.regex.Pattern; public class ObjectTree implements Cloneable { - private static final String ROOT_NAME = "-root-"; + public static final String ROOT_NAME = "-root-"; + private static final Pattern FIELD_SPLIT = Pattern.compile("^(?(([_0-9A-Za-z]+\\.)*this)|([_0-9A-Za-z]+)|(-root-))(\\.(?.+))?$"); private final Map childrenMap = new HashMap<>(); @@ -16,7 +17,11 @@ public class ObjectTree implements Cloneable { private MemberNode memberNode; public ObjectTree() { - memberNode = null; + this(ROOT_NAME); + } + + public ObjectTree(String memberName) { + memberNode = new MemberNode(memberName, null); } private ObjectTree(String memberName, ObjectTree parent) { @@ -32,7 +37,13 @@ public class ObjectTree implements Cloneable { } public void setMemberNode(MemberNode memberNode) { + GraphNode oldParent = null; + if (this.memberNode != null) + oldParent = this.memberNode.getParent(); this.memberNode = memberNode; + if (oldParent != null) + this.memberNode.setParent(oldParent); + childrenMap.values().forEach(ot -> ot.memberNode.setParent(memberNode)); } public boolean hasChildren() { @@ -213,7 +224,7 @@ public class ObjectTree implements Cloneable { @SuppressWarnings("MethodDoesntCallSuperMethod") @Override public Object clone() { - ObjectTree clone = new ObjectTree(); + ObjectTree clone = new ObjectTree(memberNode.getLabel()); for (Map.Entry entry : childrenMap.entrySet()) clone.childrenMap.put(entry.getKey(), entry.getValue().clone(clone)); return clone; diff --git a/sdg-core/src/main/java/es/upv/mist/slicing/nodes/VariableAction.java b/sdg-core/src/main/java/es/upv/mist/slicing/nodes/VariableAction.java index d651d57..62be4b0 100644 --- a/sdg-core/src/main/java/es/upv/mist/slicing/nodes/VariableAction.java +++ b/sdg-core/src/main/java/es/upv/mist/slicing/nodes/VariableAction.java @@ -1,46 +1,73 @@ package es.upv.mist.slicing.nodes; -import com.github.javaparser.ast.expr.Expression; -import com.github.javaparser.ast.expr.FieldAccessExpr; -import com.github.javaparser.ast.stmt.ReturnStmt; +import com.github.javaparser.ast.expr.*; import com.github.javaparser.resolution.Resolvable; import com.github.javaparser.resolution.UnsolvedSymbolException; import com.github.javaparser.resolution.declarations.ResolvedMethodLikeDeclaration; import com.github.javaparser.resolution.declarations.ResolvedValueDeclaration; -import com.github.javaparser.resolution.types.ResolvedType; +import com.github.javaparser.symbolsolver.javaparsermodel.declarations.JavaParserSymbolDeclaration; import es.upv.mist.slicing.arcs.Arc; import es.upv.mist.slicing.arcs.pdg.DataDependencyArc; import es.upv.mist.slicing.graphs.Graph; import es.upv.mist.slicing.graphs.jsysdg.JSysDG; import es.upv.mist.slicing.graphs.jsysdg.JSysPDG; import es.upv.mist.slicing.graphs.pdg.PDG; -import es.upv.mist.slicing.nodes.io.CallNode; -import es.upv.mist.slicing.nodes.io.OutputNode; -import es.upv.mist.slicing.utils.ASTUtils; -import java.lang.reflect.InvocationTargetException; import java.util.LinkedList; import java.util.List; import java.util.Objects; import java.util.Set; -import java.util.regex.Matcher; -import java.util.regex.Pattern; import java.util.stream.Collectors; -import static es.upv.mist.slicing.graphs.cfg.CFGBuilder.VARIABLE_NAME_OUTPUT; +import static es.upv.mist.slicing.nodes.VariableAction.DeclarationType.*; /** An action upon a variable (e.g. usage, definition, declaration) */ public abstract class VariableAction { - protected static final String VARIABLE_PATTERN = "([a-zA-Z][a-zA-Z0-9_]*|_[a-zA-Z0-9_]+)"; - protected static final String FIELD_PATTERN = "^" + VARIABLE_PATTERN + "(\\." + VARIABLE_PATTERN + ")*" + "$"; + public enum DeclarationType { + FIELD, + STATIC_FIELD, + PARAMETER, + LOCAL_VARIABLE, + SYNTHETIC; + + public static DeclarationType valueOf(ResolvedValueDeclaration resolved) { + if (resolved.isType()) + return STATIC_FIELD; + if (resolved.isField() && resolved.asField().isStatic()) + return STATIC_FIELD; + if (resolved.isField()) + return FIELD; + if (resolved.isParameter()) + return PARAMETER; + if (resolved.isVariable()) + return LOCAL_VARIABLE; + if (resolved instanceof JavaParserSymbolDeclaration) + return LOCAL_VARIABLE; + throw new IllegalArgumentException("Invalid resolved value declaration"); + } + + public static DeclarationType valueOf(Expression expression) { + if (expression instanceof ThisExpr || expression instanceof SuperExpr) + return FIELD; + else if (expression instanceof NameExpr) + try { + return valueOf(expression.asNameExpr().resolve()); + } catch (UnsolvedSymbolException e) { + return STATIC_FIELD; + } + else if (expression instanceof FieldAccessExpr) + return valueOf(expression.asFieldAccessExpr().getScope()); + else + throw new IllegalStateException("Invalid expression type"); + } + } - protected final Expression variable; - protected final String realName; - protected final GraphNode graphNode; - protected final ObjectTree objectTree; + protected final String name; + protected final DeclarationType declarationType; + protected GraphNode graphNode; + protected ObjectTree objectTree; protected boolean optional = false; - protected ResolvedValueDeclaration resolvedVariableCache; /// Variables that control the automatic connection of ObjectTrees between VariableAction /** A list of pairs representing connections to be made between trees in the PDG. @@ -49,205 +76,145 @@ public abstract class VariableAction { * the members "a.b" and "a.b.c" will be connected to "b" and "b.c" in treeConnectionTarget's tree.. */ protected final List pdgTreeConnections = new LinkedList<>(); - public VariableAction(Expression variable, String realName, GraphNode graphNode) { - this(variable, realName, graphNode, new ObjectTree()); + private VariableAction(DeclarationType declarationType, String name, GraphNode graphNode) { + this(declarationType, name, graphNode, null); } - public VariableAction(Expression variable, String realName, GraphNode graphNode, ObjectTree objectTree) { - assert realName != null && !realName.isEmpty(); - assert objectTree != null; - this.variable = variable; - this.realName = realName; + private VariableAction(DeclarationType declarationType, String name, GraphNode graphNode, ObjectTree objectTree) { + assert name != null && !name.isEmpty(); + this.declarationType = declarationType; + this.name = name; this.graphNode = graphNode; this.objectTree = objectTree; } - public ObjectTree getObjectTree() { - return objectTree; + // ====================================================== + // ================= BASIC GETTERS/SETTERS ============== + // ====================================================== + + public boolean isParameter() { + return PARAMETER == declarationType; } - /** Add a field of this object, such that the same action performed on the object - * is applied to this field too. Fields of fields may be specified separated by dots. */ - public void addObjectField(String fieldName) { - getObjectTree().addField(fieldName); + public boolean isField() { + return declarationType == FIELD || declarationType == STATIC_FIELD; } - public void setPDGTreeConnectionTo(VariableAction targetAction, String sourcePrefixWithoutRoot, String targetPrefixWithoutRoot) { - pdgTreeConnections.add(new ObjectTreeConnection(this, targetAction, sourcePrefixWithoutRoot, targetPrefixWithoutRoot)); + public boolean isStatic() { + return declarationType == STATIC_FIELD; } - public void applyPDGTreeConnections(JSysPDG pdg) { - pdgTreeConnections.forEach(c -> c.applyPDG(pdg)); + public boolean isLocalVariable() { + return declarationType == LOCAL_VARIABLE; } - public void applySDGTreeConnection(JSysDG sdg, VariableAction targetAction) { - ObjectTreeConnection connection = new ObjectTreeConnection(this, targetAction, "", ""); - connection.applySDG(sdg); + public ObjectTree getObjectTree() { + if (!hasObjectTree()) + setObjectTree(new ObjectTree()); + return objectTree; } - public VariableAction getRootAction() { - assert !isRootAction(); - assert variable == null || variable.isNameExpr() || variable.isFieldAccessExpr() || variable.isThisExpr(); - if (this instanceof Movable) { - Movable movable = (Movable) this; - return new Movable(movable.inner.getRootAction(), movable.getRealNode()); - } - Expression nVar; - String nRealName = getRootVariable(); - GraphNode nNode = graphNode; - Expression nExpr = isDefinition() ? asDefinition().expression : null; - if (variable == null || !(variable instanceof FieldAccessExpr)) { - // This appears only when generated from a field: just set the variable to null - assert realName.contains(".this."); - nVar = null; - } else { // We are in a FieldAccessExpr - nVar = variable; - while (nVar.isFieldAccessExpr()) - nVar = variable.asFieldAccessExpr().getScope(); - } - if (this instanceof Usage) - return new Usage(nVar, nRealName, nNode); - if (this instanceof Definition) - return new Definition(nVar, nRealName, nNode, nExpr); - if (this instanceof Declaration) - throw new UnsupportedOperationException("Can't create a root node for a declaration!"); - throw new IllegalStateException("Invalid action type"); + protected void setObjectTree(ObjectTree objectTree) { + this.objectTree = objectTree; } - public String getRootVariable() { - Pattern rootVariable = Pattern.compile("^(?(([_0-9A-Za-z]+\\.)*this)|([_0-9A-Za-z]+)).*$"); - Matcher matcher = rootVariable.matcher(realName); - if (matcher.matches()) { - if (matcher.group("root") != null) - return matcher.group("root"); // [type.this] or [this] - else - throw new IllegalStateException("Invalid real name: " + realName); - } else { - return null; - } + public String getName() { + return name; } - public boolean isRootAction() { - return isSynthetic() || Objects.equals(getRootVariable(), realName); + /** Whether this action is always performed when its parent node is executed or not. */ + public boolean isOptional() { + return optional; } - public static boolean typeMatches(VariableAction a, VariableAction b) { - return (a.isDeclaration() && b.isDeclaration()) || - (a.isDefinition() && b.isDefinition()) || - (a.isUsage() && b.isUsage()); + public void setOptional(boolean optional) { + this.optional = optional; } - public static boolean rootMatches(VariableAction a, VariableAction b) { - return a.getRootVariable().equals(b.getRootVariable()); + /** The node that performs this action, in which this object is contained. */ + public GraphNode getGraphNode() { + return graphNode; } /** Whether this action is performed upon an invented variable, * introduced by this library (e.g. the active exception or the returned value). */ public boolean isSynthetic() { - return !getVariable().matches(FIELD_PATTERN); + return declarationType == SYNTHETIC; } - public boolean isPrimitive() { // if (otherwise) handleAsCallReturn - if (realName.equals(VARIABLE_NAME_OUTPUT)) { - if (graphNode.getAstNode() instanceof ReturnStmt) { - return !ASTUtils.declarationReturnIsObject(ASTUtils.getDeclarationNode(graphNode.getAstNode())); - } else { - return ASTUtils.resolvableIsPrimitive(graphNode.locateCallForVariableAction(this).getCall()); - } - } - ResolvedValueDeclaration resolved = getResolvedValueDeclaration(); // if (graphNode.getAstNode() instanceof ReturnStmt) handleAsTypeOfCurrentMethod - return resolved.getType() != null && resolved.getType().isPrimitive(); + /** Whether the argument is performed upon the same variable as this action. */ + public boolean matches(VariableAction action) { + return name.equals(action.name); } - public String getVariable() { - return realName; + public boolean isPrimitive() { + return isRootAction() && !hasObjectTree(); } - public boolean hasVariableExpression() { - return variable != null; + // ====================================================== + // =================== OBJECT TREE ====================== + // ====================================================== + + public boolean hasTreeMember(String member) { + if (!hasObjectTree()) + return false; + return getObjectTree().hasMember(member); } - public Expression getVariableExpression() { - return variable; + public boolean hasObjectTree() { + return objectTree != null; } - /** - * Returns the resolved value declaration. When the action being performed - * is done so on a ThisExpr, the resulting declaration has the following properties: - *
    - *
  • Can return type and name
  • - *
  • Is not a parameter, it's a field.
  • - *
  • All other methods are left to their default implementations.
  • - *
- */ - public ResolvedValueDeclaration getResolvedValueDeclaration() { - if (resolvedVariableCache == null) { - if (variable instanceof Resolvable) { - try { - var resolved = ((Resolvable) variable).resolve(); - if (resolved instanceof ResolvedValueDeclaration) - resolvedVariableCache = (ResolvedValueDeclaration) resolved; - } catch (UnsolvedSymbolException ignored) { - resolvedVariableCache = new ResolvedValueDeclaration() { - @Override - public ResolvedType getType() { - return null; - } - - @Override - public String getName() { - return realName; - } - - @Override - public boolean isType() { - return true; - } - - @Override - public boolean isField() { - return true; - } - }; - } - } - if (resolvedVariableCache == null) - resolvedVariableCache = new ResolvedValueDeclaration() { - @Override - public ResolvedType getType() { - return null; - } + public boolean isObjectTreeEmpty() { + if (!hasObjectTree()) + return true; + return getObjectTree().isEmpty(); + } - @Override - public String getName() { - return realName; - } + public void setPDGTreeConnectionTo(VariableAction targetAction, String sourcePrefixWithoutRoot, String targetPrefixWithoutRoot) { + pdgTreeConnections.add(new ObjectTreeConnection(this, targetAction, sourcePrefixWithoutRoot, targetPrefixWithoutRoot)); + } - @Override - public boolean isField() { - return true; - } - }; - } - return resolvedVariableCache; + public void applyPDGTreeConnections(JSysPDG pdg) { + pdgTreeConnections.forEach(c -> c.applyPDG(pdg)); } - // TODO: detected optional actions - /** Whether this action is always performed when its parent node is executed or not. */ - public boolean isOptional() { - return optional; + public void applySDGTreeConnection(JSysDG sdg, VariableAction targetAction) { + ObjectTreeConnection connection = new ObjectTreeConnection(this, targetAction, "", ""); + connection.applySDG(sdg); } - /** The node that performs this action, in which this object is contained. */ - public GraphNode getGraphNode() { - return graphNode; + // ====================================================== + // =================== ROOT ACTIONS ===================== + // ====================================================== + + public VariableAction getRootAction() { + assert !isRootAction(); + if (this instanceof Movable) { + Movable movable = (Movable) this; + return new Movable(movable.inner.getRootAction(), movable.getRealNode()); + } + if (this instanceof Usage) + return new Usage(declarationType, ObjectTree.removeFields(name), graphNode); + if (this instanceof Definition) + return new Definition(declarationType, ObjectTree.removeFields(name), graphNode, asDefinition().expression); + if (this instanceof Declaration) + throw new UnsupportedOperationException("Can't create a root node for a declaration!"); + throw new IllegalStateException("Invalid action type"); } - /** Whether the argument is performed upon the same variable as this action. */ - public boolean matches(VariableAction action) { - return Objects.equals(action.realName, realName); + public boolean isRootAction() { + return isSynthetic() || Objects.equals(ObjectTree.removeFields(name), name); } + public boolean rootMatches(VariableAction b) { + return ObjectTree.removeFields(name).equals(ObjectTree.removeFields(b.name)); + } + + // ====================================================== + // ============== SUBTYPES AND CLONING ================== + // ====================================================== + public boolean isUsage() { return this instanceof Usage; } @@ -274,52 +241,67 @@ public abstract class VariableAction { /** Creates a new usage action with the same variable and the given node. */ public final Usage toUsage(GraphNode graphNode) { - return new Usage(variable, realName, graphNode, (ObjectTree) getObjectTree().clone()); + ObjectTree tree = hasObjectTree() ? (ObjectTree) getObjectTree().clone() : null; + return new Usage(declarationType, name, graphNode, tree); } /** Creates a new definition action with the same variable and the given node. */ public final Definition toDefinition(GraphNode graphNode) { - return new Definition(variable, realName, graphNode, (ObjectTree) getObjectTree().clone()); + ObjectTree tree = hasObjectTree() ? (ObjectTree) getObjectTree().clone() : null; + return new Definition(declarationType, name, graphNode, tree); } /** Creates a new declaration action with the same variable and the given node. */ public final Declaration toDeclaration(GraphNode graphNode) { - return new Declaration(variable, realName, graphNode, (ObjectTree) getObjectTree().clone()); + ObjectTree tree = hasObjectTree() ? (ObjectTree) getObjectTree().clone() : null; + return new Declaration(declarationType, name, graphNode, tree); } - @SuppressWarnings("unchecked") public final
A createCopy() { + return createCopy(null); + } + + @SuppressWarnings("unchecked") + public final A createCopy(GraphNode graphNode) { if (this instanceof Usage) - return (A) toUsage(null); + return (A) toUsage(graphNode); if (this instanceof Definition) - return (A) toDefinition(null); + return (A) toDefinition(graphNode); if (this instanceof Declaration) - return (A) toDeclaration(null); + return (A) toDeclaration(graphNode); if (this instanceof Movable) { + assert graphNode == null || graphNode instanceof SyntheticNode; Movable m = (Movable) this; - return (A) new Movable(m.inner.createCopy(), null); + return (A) new Movable(m.inner.createCopy(), (SyntheticNode) graphNode); } throw new IllegalStateException("This kind of variable action can't be copied"); } + // ====================================================== + // =============== OVERRIDDEN METHODS =================== + // ====================================================== + @Override public boolean equals(Object obj) { return obj instanceof VariableAction && obj.getClass().equals(getClass()) && - Objects.equals(variable, ((VariableAction) obj).variable) && - realName.equals(((VariableAction) obj).realName); + name.equals(((VariableAction) obj).name); } @Override public int hashCode() { - return Objects.hash(getClass(), variable, realName); + return Objects.hash(getClass(), name); } @Override public String toString() { - return "{" + realName + "}"; + return "{" + name + "}"; } + // ====================================================== + // ==================== SUBCLASSES ====================== + // ====================================================== + /** An invented action used to locate the relative position of the start and end of a call inside a list of actions. */ public static class CallMarker extends VariableAction { protected final Resolvable call; @@ -331,6 +313,11 @@ public abstract class VariableAction { this.enter = enter; } + @Override + public boolean isRootAction() { + return true; + } + /** The call this marker represents. */ public Resolvable getCall() { return call; @@ -355,12 +342,12 @@ public abstract class VariableAction { /** A usage of a variable. */ public static class Usage extends VariableAction { - public Usage(Expression variable, String realName, GraphNode graphNode) { - super(variable, realName, graphNode); + public Usage(DeclarationType declarationType, String name, GraphNode graphNode) { + super(Objects.requireNonNull(declarationType), name, graphNode); } - public Usage(Expression variable, String realName, GraphNode graphNode, ObjectTree objectTree) { - super(variable, realName, graphNode, objectTree); + public Usage(DeclarationType declarationType, String name, GraphNode graphNode, ObjectTree objectTree) { + super(Objects.requireNonNull(declarationType), name, graphNode, objectTree); } @Override @@ -376,21 +363,21 @@ public abstract class VariableAction { /** The members of the object tree that are total definitions. */ protected String totallyDefinedMember; - public Definition(Expression variable, String realName, GraphNode graphNode) { - this(variable, realName, graphNode, (Expression) null); + public Definition(DeclarationType declarationType, String name, GraphNode graphNode) { + this(declarationType, name, graphNode, (Expression) null); } - public Definition(Expression variable, String realName, GraphNode graphNode, Expression expression) { - super(variable, realName, graphNode); + public Definition(DeclarationType declarationType, String name, GraphNode graphNode, Expression expression) { + super(Objects.requireNonNull(declarationType), name, graphNode); this.expression = expression; } - public Definition(Expression variable, String realName, GraphNode graphNode, ObjectTree objectTree) { - this(variable, realName, graphNode, null, objectTree); + public Definition(DeclarationType declarationType, String name, GraphNode graphNode, ObjectTree objectTree) { + this(declarationType, name, graphNode, null, objectTree); } - public Definition(Expression variable, String realName, GraphNode graphNode, Expression expression, ObjectTree objectTree) { - super(variable, realName, graphNode, objectTree); + public Definition(DeclarationType declarationType, String name, GraphNode graphNode, Expression expression, ObjectTree objectTree) { + super(Objects.requireNonNull(declarationType), name, graphNode, objectTree); this.expression = expression; } @@ -418,12 +405,12 @@ public abstract class VariableAction { /** A declaration of a variable. */ public static class Declaration extends VariableAction { - public Declaration(Expression variable, String realName, GraphNode graphNode) { - super(variable, realName, graphNode); + public Declaration(DeclarationType declarationType, String name, GraphNode graphNode) { + super(Objects.requireNonNull(declarationType), name, graphNode); } - public Declaration(Expression variable, String realName, GraphNode graphNode, ObjectTree objectTree) { - super(variable, realName, graphNode, objectTree); + public Declaration(DeclarationType declarationType, String name, GraphNode graphNode, ObjectTree objectTree) { + super(Objects.requireNonNull(declarationType), name, graphNode, objectTree); } @Override @@ -445,64 +432,59 @@ public abstract class VariableAction { * to generate dependencies and a {@link PDG PDG} node that * is the final location of this action. */ public Movable(VariableAction inner, SyntheticNode pdgNode) { - super(inner.variable, inner.realName, inner.graphNode); + super(inner.declarationType, inner.name, inner.graphNode); if (inner instanceof Movable) throw new IllegalArgumentException("'inner' must be an unmovable action"); this.realNode = pdgNode; this.inner = inner; } + @Override public ObjectTree getObjectTree() { + if (!inner.hasObjectTree()) + inner.setObjectTree(new ObjectTree()); return inner.getObjectTree(); } + @Override + protected void setObjectTree(ObjectTree objectTree) { + inner.objectTree = objectTree; + } + + @Override + public boolean hasObjectTree() { + return inner.objectTree != null; + } + /** The final location of this action. This node may not yet be present * in the graph, if {@link #move(Graph)} has not been invoked. */ public SyntheticNode getRealNode() { return realNode; } - @Override - public boolean isPrimitive() { - if (realName.equals(VARIABLE_NAME_OUTPUT)) { - if (realNode instanceof CallNode.Return) { - return ASTUtils.resolvableIsPrimitive((Resolvable) realNode.getAstNode()); - } else if (realNode instanceof OutputNode) { - return !ASTUtils.declarationReturnIsObject(ASTUtils.getDeclarationNode(realNode.getAstNode())); - } - } - return super.isPrimitive(); - } - /** Move the action from its node to its real node. The real node is added to the graph, * the action is deleted from its original node's list, a copy is created with the real * target and any {@link DataDependencyArc} is relocated to match this change. */ public VariableAction move(Graph graph) { - // Create unwrapped action (the graphNode field must be changed). - VariableAction newAction; - try { - if (inner instanceof Definition && inner.asDefinition().getExpression() != null) - newAction = inner.getClass().getConstructor(Expression.class, String.class, GraphNode.class, Expression.class, ObjectTree.class) - .newInstance(variable, realName, realNode, inner.asDefinition().expression, getObjectTree()); - else - newAction = inner.getClass().getConstructor(Expression.class, String.class, GraphNode.class, ObjectTree.class) - .newInstance(variable, realName, realNode, getObjectTree()); - } catch (InstantiationException | IllegalAccessException | InvocationTargetException | NoSuchMethodException e) { - throw new UnsupportedOperationException("The VariableAction constructor has changed!", e); - } // Add node graph.addVertex(realNode); // Move to node - graphNode.variableActions.remove(this); - realNode.variableActions.add(newAction); + moveOnly(); // Move data dependencies Set.copyOf(graph.edgesOf(graphNode).stream() .filter(Arc::isDataDependencyArc) .map(Arc::asDataDependencyArc) .filter(arc -> arc.getSourceVar() == this || arc.getTargetVar() == this) .collect(Collectors.toSet())) // copying to avoid modifying while iterating - .forEach(arc -> moveDataDependencyArc(arc, graph, newAction)); - return newAction; + .forEach(arc -> moveDataDependencyArc(arc, graph, inner)); + return inner; + } + + /** Relocate the inner VA from its current node to its real node. */ + public void moveOnly() { + graphNode.variableActions.remove(this); + realNode.variableActions.add(inner); + inner.graphNode = realNode; } /** Relocates a data dependency arc, by creating a new one with matching information and deleting the old one. */ @@ -561,5 +543,4 @@ public abstract class VariableAction { return Objects.hash(super.hashCode(), realNode, inner); } } - } 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 77b865a..f2bb855 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 @@ -19,6 +19,7 @@ import com.github.javaparser.symbolsolver.javaparsermodel.declarations.JavaParse import es.upv.mist.slicing.graphs.ClassGraph; import es.upv.mist.slicing.graphs.ExpressionObjectTreeFinder; import es.upv.mist.slicing.graphs.GraphNodeContentVisitor; +import es.upv.mist.slicing.nodes.VariableAction.DeclarationType; import es.upv.mist.slicing.nodes.io.ActualIONode; import es.upv.mist.slicing.nodes.io.CallNode; import es.upv.mist.slicing.utils.ASTUtils; @@ -31,6 +32,7 @@ import java.util.Optional; import static es.upv.mist.slicing.graphs.cfg.CFGBuilder.VARIABLE_NAME_OUTPUT; import static es.upv.mist.slicing.graphs.exceptionsensitive.ESCFG.ACTIVE_EXCEPTION_VARIABLE; +import static es.upv.mist.slicing.nodes.VariableAction.DeclarationType.*; import static es.upv.mist.slicing.nodes.VariableVisitor.Action.*; /** A graph node visitor that extracts the actions performed in a given GraphNode. An initial action mode can @@ -115,33 +117,33 @@ public class VariableVisitor extends GraphNodeContentVisitor { init.accept(this, action); definitionStack.push(init); - acceptAction(v.getNameAsString(), DEFINITION); + acceptAction(LOCAL_VARIABLE, v.getNameAsString(), DEFINITION); definitionStack.pop(); if (v.getType().isClassOrInterfaceType()) getLastDefinition().setTotallyDefinedMember(v.getNameAsString()); @@ -328,11 +330,11 @@ public class VariableVisitor extends GraphNodeContentVisitor ASTUtils.initializerForField(n)); init.accept(this, action); definitionStack.push(init); - acceptAction(realName, DEFINITION); + acceptAction(FIELD, realName, DEFINITION); definitionStack.pop(); if (v.getType().isClassOrInterfaceType()) getLastDefinition().setTotallyDefinedMember(realName); @@ -354,8 +356,8 @@ public class VariableVisitor extends GraphNodeContentVisitor scope.accept(this, action), - () -> acceptAction(getFQClassName(decl) + ".this", USE)); + () -> acceptAction(FIELD, getFQClassName(decl) + ".this", USE)); realNodeStack.pop(); } // Args @@ -427,14 +429,13 @@ public class VariableVisitor extends GraphNodeContentVisitor call) { if (ASTUtils.resolvableIsVoid(call)) return; - Expression callExpr = call instanceof Expression ? (Expression) call : null; // A node defines -output- var fields = getFieldsForReturn(call); VariableAction.Definition def; if (fields.isPresent()) - def = new VariableAction.Definition(callExpr, VARIABLE_NAME_OUTPUT, graphNode, (ObjectTree) fields.get().clone()); + def = new VariableAction.Definition(SYNTHETIC, VARIABLE_NAME_OUTPUT, graphNode, (ObjectTree) fields.get().clone()); else - def = new VariableAction.Definition(callExpr, VARIABLE_NAME_OUTPUT, graphNode); + def = new VariableAction.Definition(SYNTHETIC, VARIABLE_NAME_OUTPUT, graphNode); var defMov = new VariableAction.Movable(def, CallNode.Return.create(call)); graphNode.addVariableAction(defMov); // The container of the call uses -output-, unless the call is wrapped in an ExpressionStmt @@ -442,9 +443,9 @@ public class VariableVisitor extends GraphNodeContentVisitor list = graphNode.getVariableActions(); return ((VariableAction.Definition) list.get(list.size() - 1)); diff --git a/sdg-core/src/main/java/es/upv/mist/slicing/nodes/io/ActualIONode.java b/sdg-core/src/main/java/es/upv/mist/slicing/nodes/io/ActualIONode.java index fb5c6a6..aa1ee4b 100644 --- a/sdg-core/src/main/java/es/upv/mist/slicing/nodes/io/ActualIONode.java +++ b/sdg-core/src/main/java/es/upv/mist/slicing/nodes/io/ActualIONode.java @@ -7,7 +7,6 @@ import com.github.javaparser.resolution.Resolvable; import com.github.javaparser.resolution.declarations.ResolvedConstructorDeclaration; import com.github.javaparser.resolution.declarations.ResolvedMethodDeclaration; import com.github.javaparser.resolution.declarations.ResolvedMethodLikeDeclaration; -import com.github.javaparser.resolution.declarations.ResolvedValueDeclaration; import java.util.Objects; @@ -15,10 +14,6 @@ import java.util.Objects; public class ActualIONode extends IONode { protected final Expression argument; - protected ActualIONode(Resolvable astNode, ResolvedValueDeclaration variable, Expression argument, boolean isInput) { - this(astNode, variable.getName(), argument, isInput); - } - protected ActualIONode(Resolvable astNode, String variable, Expression argument, boolean isInput) { super(createLabel(isInput, variable, argument), (Node) astNode, variable, isInput); this.argument = argument; @@ -67,15 +62,11 @@ public class ActualIONode extends IONode { return String.format("%s = %s_out", arg, paramName); } - public static ActualIONode createActualIn(Resolvable methodCallExpr, ResolvedValueDeclaration resolvedDeclaration, Expression argument) { - return new ActualIONode(methodCallExpr, resolvedDeclaration, argument, true); - } - - public static ActualIONode createActualIn(Resolvable methodCallExpr, String variable, Expression argument) { - return new ActualIONode(methodCallExpr, variable, argument, true); + public static ActualIONode createActualIn(Resolvable methodCallExpr, String name, Expression argument) { + return new ActualIONode(methodCallExpr, name, argument, true); } - public static ActualIONode createActualOut(Resolvable methodCallExpr, ResolvedValueDeclaration resolvedDeclaration, Expression argument) { - return new ActualIONode(methodCallExpr, resolvedDeclaration, argument, false); + public static ActualIONode createActualOut(Resolvable methodCallExpr, String name, Expression argument) { + return new ActualIONode(methodCallExpr, name, argument, false); } } diff --git a/sdg-core/src/main/java/es/upv/mist/slicing/nodes/io/FormalIONode.java b/sdg-core/src/main/java/es/upv/mist/slicing/nodes/io/FormalIONode.java index 4b3864e..a639d15 100644 --- a/sdg-core/src/main/java/es/upv/mist/slicing/nodes/io/FormalIONode.java +++ b/sdg-core/src/main/java/es/upv/mist/slicing/nodes/io/FormalIONode.java @@ -1,7 +1,6 @@ package es.upv.mist.slicing.nodes.io; import com.github.javaparser.ast.body.CallableDeclaration; -import com.github.javaparser.resolution.declarations.ResolvedValueDeclaration; /** A formal-in or formal-out node, displaying interprocedural data dependencies. */ public class FormalIONode extends IONode> { @@ -20,16 +19,15 @@ public class FormalIONode extends IONode> { return String.format("%s_out = %1$s", varName); } - public static FormalIONode createFormalIn(CallableDeclaration declaration, ResolvedValueDeclaration resolvedValue) { - return new FormalIONode(declaration, resolvedValue.getName(), true); + public static FormalIONode createFormalIn(CallableDeclaration declaration, String name) { + return new FormalIONode(declaration, name, true); } - public static FormalIONode createFormalOut(CallableDeclaration declaration, ResolvedValueDeclaration resolvedValue) { - return new FormalIONode(declaration, resolvedValue.getName(), false); + public static FormalIONode createFormalOut(CallableDeclaration declaration, String name) { + return new FormalIONode(declaration, name, false); } - public static FormalIONode createFormalInDecl(CallableDeclaration declaration, ResolvedValueDeclaration resolvedValue) { - String name = resolvedValue.getName(); + public static FormalIONode createFormalInDecl(CallableDeclaration declaration, String name) { return new FormalIONode(name, declaration, name, true); } } 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 8aa6c59..8d8aea6 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 @@ -7,7 +7,10 @@ import com.github.javaparser.ast.stmt.*; import com.github.javaparser.ast.type.PrimitiveType; import com.github.javaparser.ast.type.Type; import com.github.javaparser.resolution.Resolvable; -import com.github.javaparser.resolution.declarations.*; +import com.github.javaparser.resolution.declarations.ResolvedConstructorDeclaration; +import com.github.javaparser.resolution.declarations.ResolvedMethodDeclaration; +import com.github.javaparser.resolution.declarations.ResolvedMethodLikeDeclaration; +import com.github.javaparser.resolution.declarations.ResolvedReferenceTypeDeclaration; import com.github.javaparser.resolution.types.ResolvedType; import com.github.javaparser.symbolsolver.model.typesystem.ReferenceTypeImpl; import es.upv.mist.slicing.nodes.GraphNode; @@ -85,25 +88,14 @@ public class ASTUtils { throw new IllegalArgumentException("Declaration wasn't method or constructor"); } - public static int getMatchingParameterIndex(CallableDeclaration declaration, ResolvedParameterDeclaration param) { + public static int getMatchingParameterIndex(CallableDeclaration declaration, String paramName) { var parameters = declaration.getParameters(); for (int i = 0; i < parameters.size(); i++) - if (resolvedParameterEquals(param, parameters.get(i).resolve())) + if (parameters.get(i).getNameAsString().equals(paramName)) return i; throw new IllegalArgumentException("Expression resolved to a parameter, but could not be found!"); } - public static int getMatchingParameterIndex(ResolvedMethodLikeDeclaration declaration, ResolvedParameterDeclaration param) { - for (int i = 0; i < declaration.getNumberOfParams(); i++) - if (resolvedParameterEquals(declaration.getParam(i), param)) - return i; - throw new IllegalArgumentException("Expression resolved to a parameter, but could not be found!"); - } - - protected static boolean resolvedParameterEquals(ResolvedParameterDeclaration p1, ResolvedParameterDeclaration p2) { - return p2.getType().equals(p1.getType()) && p2.getName().equals(p1.getName()); - } - public static List getResolvableArgs(Resolvable call) { if (call instanceof MethodCallExpr) return ((MethodCallExpr) call).getArguments(); -- GitLab From 4940eccca977a5ab5377c2db35962da831fa3e5b Mon Sep 17 00:00:00 2001 From: Carlos Galindo Date: Wed, 10 Mar 2021 11:10:56 +0100 Subject: [PATCH 13/60] Deactivate the polymorphic call graph for now. --- .../src/main/java/es/upv/mist/slicing/graphs/CallGraph.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 f37ab25..d01113b 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 createPolyEdges(decl, n)); + n.resolve().toAst().ifPresent(decl -> createNormalEdge(decl, n)); super.visit(n, arg); } -- GitLab From 9813598b0bab1814759954c38d276fcd9fd9444c Mon Sep 17 00:00:00 2001 From: Carlos Galindo Date: Wed, 10 Mar 2021 11:51:20 +0100 Subject: [PATCH 14/60] Remove class prefix from this --- .../sdg/InterproceduralActionFinder.java | 4 --- .../sdg/InterproceduralDefinitionFinder.java | 4 +-- .../mist/slicing/nodes/VariableVisitor.java | 34 +++++-------------- 3 files changed, 10 insertions(+), 32 deletions(-) diff --git a/sdg-core/src/main/java/es/upv/mist/slicing/graphs/sdg/InterproceduralActionFinder.java b/sdg-core/src/main/java/es/upv/mist/slicing/graphs/sdg/InterproceduralActionFinder.java index a2fc064..5860593 100644 --- a/sdg-core/src/main/java/es/upv/mist/slicing/graphs/sdg/InterproceduralActionFinder.java +++ b/sdg-core/src/main/java/es/upv/mist/slicing/graphs/sdg/InterproceduralActionFinder.java @@ -136,10 +136,6 @@ public abstract class InterproceduralActionFinder exte } else { String newPrefix = scope; newPrefix = newPrefix.replaceAll("((\\.)super|^super)(\\.)?", "$2this$3"); - if (newPrefix.equals("this")) { - String fqName = ASTUtils.getClassNode(edge.getGraphNode().getAstNode()).getFullyQualifiedName().orElseThrow(); - newPrefix = fqName + ".this"; - } String withPrefix = action.getName(); String withoutPrefix = withPrefix.replaceFirst("^((.*\\.)?this\\.?)", ""); String result = newPrefix + withoutPrefix; 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 d5125c8..1113627 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 @@ -1,6 +1,5 @@ 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; @@ -62,8 +61,7 @@ public class InterproceduralDefinitionFinder extends InterproceduralActionFinder if (scope.isPresent()) { extractOutputVariablesAsMovables(scope.get(), movables, graphNode, actualOut, def); } else { - String fqClassName = ASTUtils.getClassNode((Node) edge.getCall()).getFullyQualifiedName().orElseThrow(); - var movableDef = new Definition(DeclarationType.FIELD, fqClassName + ".this", graphNode, (ObjectTree) def.getObjectTree().clone()); + var movableDef = new Definition(DeclarationType.FIELD, "this", graphNode, (ObjectTree) def.getObjectTree().clone()); movables.add(new Movable(movableDef, actualOut)); } } 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 f2bb855..b075bdc 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 @@ -13,7 +13,6 @@ import com.github.javaparser.resolution.Resolvable; import com.github.javaparser.resolution.UnsolvedSymbolException; import com.github.javaparser.resolution.declarations.AssociableToAST; import com.github.javaparser.resolution.declarations.ResolvedMethodLikeDeclaration; -import com.github.javaparser.resolution.declarations.ResolvedTypeDeclaration; import com.github.javaparser.resolution.declarations.ResolvedValueDeclaration; import com.github.javaparser.symbolsolver.javaparsermodel.declarations.JavaParserMethodDeclaration; import es.upv.mist.slicing.graphs.ClassGraph; @@ -374,13 +373,12 @@ public class VariableVisitor extends GraphNodeContentVisitor scope.accept(this, action), - () -> acceptAction(FIELD, getFQClassName(decl) + ".this", USE)); + () -> acceptAction(FIELD, "this", USE)); realNodeStack.pop(); } // Args @@ -460,15 +458,9 @@ public class VariableVisitor extends GraphNodeContentVisitor Date: Wed, 10 Mar 2021 11:59:23 +0100 Subject: [PATCH 15/60] Better names for root MemberNodes --- .../main/java/es/upv/mist/slicing/nodes/VariableAction.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/sdg-core/src/main/java/es/upv/mist/slicing/nodes/VariableAction.java b/sdg-core/src/main/java/es/upv/mist/slicing/nodes/VariableAction.java index 62be4b0..96620f9 100644 --- a/sdg-core/src/main/java/es/upv/mist/slicing/nodes/VariableAction.java +++ b/sdg-core/src/main/java/es/upv/mist/slicing/nodes/VariableAction.java @@ -110,7 +110,7 @@ public abstract class VariableAction { public ObjectTree getObjectTree() { if (!hasObjectTree()) - setObjectTree(new ObjectTree()); + setObjectTree(new ObjectTree(getName())); return objectTree; } @@ -442,7 +442,7 @@ public abstract class VariableAction { @Override public ObjectTree getObjectTree() { if (!inner.hasObjectTree()) - inner.setObjectTree(new ObjectTree()); + inner.setObjectTree(new ObjectTree(getName())); return inner.getObjectTree(); } -- GitLab From ffc7d398dcbcb12c20c98695d13562b53bd2ceb3 Mon Sep 17 00:00:00 2001 From: Carlos Galindo Date: Wed, 10 Mar 2021 12:48:24 +0100 Subject: [PATCH 16/60] Link ObjectTreeConnections with value dependencies when the target is primitive/has no tree. --- .../upv/mist/slicing/graphs/ClassGraph.java | 10 ++ .../graphs/ExpressionObjectTreeFinder.java | 18 +-- .../mist/slicing/graphs/jsysdg/JSysCFG.java | 25 ++-- .../mist/slicing/graphs/jsysdg/JSysPDG.java | 7 +- .../es/upv/mist/slicing/nodes/ObjectTree.java | 4 + .../slicing/nodes/ObjectTreeConnection.java | 45 ++++--- .../mist/slicing/nodes/VariableVisitor.java | 114 +++++++++--------- 7 files changed, 122 insertions(+), 101 deletions(-) diff --git a/sdg-core/src/main/java/es/upv/mist/slicing/graphs/ClassGraph.java b/sdg-core/src/main/java/es/upv/mist/slicing/graphs/ClassGraph.java index 76a3d4e..fb4ce67 100644 --- a/sdg-core/src/main/java/es/upv/mist/slicing/graphs/ClassGraph.java +++ b/sdg-core/src/main/java/es/upv/mist/slicing/graphs/ClassGraph.java @@ -7,6 +7,7 @@ import com.github.javaparser.ast.visitor.VoidVisitorAdapter; import com.github.javaparser.resolution.UnsolvedSymbolException; import com.github.javaparser.resolution.declarations.ResolvedClassDeclaration; import com.github.javaparser.resolution.types.ResolvedReferenceType; +import com.github.javaparser.resolution.types.ResolvedType; import es.upv.mist.slicing.arcs.Arc; import es.upv.mist.slicing.nodes.ObjectTree; import es.upv.mist.slicing.utils.ASTUtils; @@ -145,6 +146,15 @@ public class ClassGraph extends DirectedPseudograph generateObjectTreeForType(ResolvedType type) { + if (type.isReferenceType()) { + Vertex v = vertexDeclarationMap.get(mapKey(type.asReferenceType())); + if (v != null) + return Optional.of(generateObjectTreeFor(v)); + } + return Optional.empty(); + } + public ObjectTree generateObjectTreeFor(ClassOrInterfaceDeclaration declaration) { return generateObjectTreeFor(vertexDeclarationMap.get(mapKey(declaration))); } 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 29f41fe..d92a8ed 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 @@ -6,7 +6,6 @@ import com.github.javaparser.ast.expr.*; import com.github.javaparser.ast.visitor.VoidVisitorAdapter; import com.github.javaparser.resolution.Resolvable; import com.github.javaparser.resolution.declarations.ResolvedMethodLikeDeclaration; -import com.github.javaparser.resolution.types.ResolvedReferenceType; import com.github.javaparser.utils.Pair; import es.upv.mist.slicing.nodes.GraphNode; import es.upv.mist.slicing.nodes.ObjectTree; @@ -29,9 +28,8 @@ public class ExpressionObjectTreeFinder { public void handleVariableDeclarator(VariableDeclarator variableDeclarator) { assert variableDeclarator.getInitializer().isPresent(); VariableAction targetAction = locateVAVariableDeclarator(variableDeclarator.getNameAsString()); - ResolvedReferenceType type = variableDeclarator.getType().resolve().asReferenceType(); - var fields = ClassGraph.getInstance().generateObjectTreeFor(type); - targetAction.getObjectTree().addAll(fields); + ClassGraph.getInstance().generateObjectTreeForType(variableDeclarator.getType().resolve()) + .ifPresent(objectTree -> targetAction.getObjectTree().addAll(objectTree)); locateExpressionResultTrees(variableDeclarator.getInitializer().get()) .forEach(pair -> markTransference(pair, targetAction, "")); } @@ -54,9 +52,8 @@ public class ExpressionObjectTreeFinder { } public void handleAssignExpr(AssignExpr assignExpr, VariableAction assignTarget, String targetMember) { - ResolvedReferenceType type = assignExpr.getTarget().calculateResolvedType().asReferenceType(); - var fields = ClassGraph.getInstance().generateObjectTreeFor(type); - assignTarget.getObjectTree().addAll(fields); + ClassGraph.getInstance().generateObjectTreeForType(assignExpr.getTarget().calculateResolvedType()) + .ifPresent(fields -> assignTarget.getObjectTree().addAll(fields)); locateExpressionResultTrees(assignExpr.getValue()) .forEach(pair -> markTransference(pair, assignTarget, targetMember)); } @@ -97,6 +94,10 @@ public class ExpressionObjectTreeFinder { public void visit(NameExpr n, String arg) { if (n.resolve().isType()) return; + if (n.resolve().isField()) { + new FieldAccessExpr(new ThisExpr(), n.getNameAsString()).accept(this, arg); + return; + } for (VariableAction action : graphNode.getVariableActions()) { if (action.isUsage() && action.getName().equals(n.getNameAsString())) { list.add(new Pair<>(action, arg)); @@ -160,7 +161,8 @@ public class ExpressionObjectTreeFinder { protected void markTransference(Pair sourcePair, VariableAction targetAction, String targetMember) { VariableAction sourceAction = sourcePair.a; String sourceMember = sourcePair.b; - ObjectTree.copyTree(sourceAction.getObjectTree(), targetAction.getObjectTree(), sourceMember, targetMember); + if (sourceAction.hasObjectTree() && !sourceAction.getObjectTree().isLeaf(sourceMember)) + ObjectTree.copyTree(sourceAction.getObjectTree(), targetAction.getObjectTree(), sourceMember, targetMember); sourceAction.setPDGTreeConnectionTo(targetAction, sourceMember, targetMember); } } diff --git a/sdg-core/src/main/java/es/upv/mist/slicing/graphs/jsysdg/JSysCFG.java b/sdg-core/src/main/java/es/upv/mist/slicing/graphs/jsysdg/JSysCFG.java index 302cb49..ccb18be 100644 --- a/sdg-core/src/main/java/es/upv/mist/slicing/graphs/jsysdg/JSysCFG.java +++ b/sdg-core/src/main/java/es/upv/mist/slicing/graphs/jsysdg/JSysCFG.java @@ -199,24 +199,23 @@ public class JSysCFG extends ESCFG { protected void expandOutputVariable(CallableDeclaration callableDeclaration, VariableAction useOutput) { // Generate the full tree for the method's returned type (static) var fields = classGraph.generateObjectTreeForReturnOf(callableDeclaration); - if (fields.isEmpty()) - return; - // Insert tree into the OutputNode - useOutput.getObjectTree().addAll(fields.get()); - // Insert tree into GraphNode nodes, the last action is always DEF(-output-) - vertexSet().stream() - .filter(gn -> gn.getAstNode() instanceof ReturnStmt) - .map(GraphNode::getVariableActions) - .map(list -> list.get(list.size() - 1)) - .map(VariableAction::getObjectTree) - .forEach(tree -> tree.addAll(fields.get())); + if (fields.isPresent()) { + // Insert tree into the OutputNode + useOutput.getObjectTree().addAll(fields.get()); + // Insert tree into GraphNode nodes, the last action is always DEF(-output-) + vertexSet().stream() + .filter(gn -> gn.getAstNode() instanceof ReturnStmt) + .map(GraphNode::getVariableActions) + .map(list -> list.get(list.size() - 1)) + .map(VariableAction::getObjectTree) + .forEach(tree -> tree.addAll(fields.get())); + } // Generate the assignment trees and prepare for linking vertexSet().stream() .filter(gn -> gn.getAstNode() instanceof ReturnStmt) .forEach(gn -> { Expression expr = ((ReturnStmt) gn.getAstNode()).getExpression().orElseThrow(); - ExpressionObjectTreeFinder finder = new ExpressionObjectTreeFinder(gn); - finder.locateAndMarkTransferenceToRoot(expr, -1); + new ExpressionObjectTreeFinder(gn).locateAndMarkTransferenceToRoot(expr, -1); }); } } diff --git a/sdg-core/src/main/java/es/upv/mist/slicing/graphs/jsysdg/JSysPDG.java b/sdg-core/src/main/java/es/upv/mist/slicing/graphs/jsysdg/JSysPDG.java index 97ad616..ba0a3ec 100644 --- a/sdg-core/src/main/java/es/upv/mist/slicing/graphs/jsysdg/JSysPDG.java +++ b/sdg-core/src/main/java/es/upv/mist/slicing/graphs/jsysdg/JSysPDG.java @@ -107,12 +107,9 @@ public class JSysPDG extends ESPDG { jSysCFG.findLastDefinitionOfPrimitive(varAct).forEach(def -> addFlowDependencyArc(def, varAct)); else { jSysCFG.findLastDefinitionOfObjectRoot(varAct).forEach(def -> addObjectFlowDependencyArc(def, varAct)); - if (varAct.hasObjectTree()) { - for (String member : varAct.getObjectTree().nameIterable()) { + if (varAct.hasObjectTree()) + for (String member : varAct.getObjectTree().nameIterable()) jSysCFG.findLastDefinitionOfObjectMember(varAct, member).forEach(def -> addFlowDependencyArc(def, varAct, member)); - addValueDependencyArc(varAct, member, node); - } - } } } else if (varAct.isDefinition()) { if (!varAct.isSynthetic()) 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 4cade75..e6c2a99 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 @@ -75,6 +75,10 @@ public class ObjectTree implements Cloneable { childrenMap.put(entry.getKey(), entry.getValue().clone(this)); } + public boolean isLeaf(String memberWithoutRoot) { + return findObjectTreeOfMember(memberWithoutRoot).childrenMap.isEmpty(); + } + /** * Copies a subtree from source into another subtree in target. * diff --git a/sdg-core/src/main/java/es/upv/mist/slicing/nodes/ObjectTreeConnection.java b/sdg-core/src/main/java/es/upv/mist/slicing/nodes/ObjectTreeConnection.java index d815a5c..86c6c1b 100644 --- a/sdg-core/src/main/java/es/upv/mist/slicing/nodes/ObjectTreeConnection.java +++ b/sdg-core/src/main/java/es/upv/mist/slicing/nodes/ObjectTreeConnection.java @@ -41,22 +41,35 @@ class ObjectTreeConnection { } protected void connectTrees(Graph graph, Supplier flowSupplier, Supplier objFlowSupplier) { - ObjectTree source = sourceAction.getObjectTree().findObjectTreeOfMember(sourceMember); - ObjectTree target = targetAction.getObjectTree().findObjectTreeOfMember(targetMember); - assert sourceMember.isEmpty() || source.getMemberName() != null; - assert targetMember.isEmpty() || target.getMemberName() != null; - GraphNode rootSrc = source.getMemberNode() != null ? source.getMemberNode() : sourceAction.getGraphNode(); - GraphNode rootTgt = target.getMemberNode() != null ? target.getMemberNode() : targetAction.getGraphNode(); - graph.addEdge(rootSrc, rootTgt, objFlowSupplier.get()); - for (ObjectTree tree : target.treeIterable()) { - MemberNode src = source.getNodeForNonRoot(tree.getMemberName()); - MemberNode tgt = tree.getMemberNode(); - if (tree.hasChildren()) - graph.addEdge(src, tgt, objFlowSupplier.get()); - else - graph.addEdge(src, tgt, flowSupplier.get()); + ObjectTree source = null, target = null; + GraphNode rootSrc, rootTgt; + assert sourceMember.isEmpty() || sourceAction.hasObjectTree(); + assert targetMember.isEmpty() || targetAction.hasObjectTree(); + if (sourceAction.hasObjectTree()) { + source = sourceAction.getObjectTree().findObjectTreeOfMember(sourceMember); + rootSrc = source.getMemberNode(); + } else { + rootSrc = sourceAction.getGraphNode(); + } + if (targetAction.hasObjectTree()) { + target = targetAction.getObjectTree().findObjectTreeOfMember(targetMember); + rootTgt = target.getMemberNode(); + } else { + rootTgt = targetAction.getGraphNode(); + } + if (source == null || target == null) { + if (!rootSrc.equals(rootTgt)) + graph.addEdge(rootSrc, rootTgt, new FlowDependencyArc()); // VALUE DEPENDENCE + } else { + graph.addEdge(rootSrc, rootTgt, objFlowSupplier.get()); + for (ObjectTree tree : target.treeIterable()) { + MemberNode src = source.getNodeForNonRoot(tree.getMemberName()); + MemberNode tgt = tree.getMemberNode(); + if (tree.hasChildren()) + graph.addEdge(src, tgt, objFlowSupplier.get()); + else + graph.addEdge(src, tgt, flowSupplier.get()); + } } } - - } 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 b075bdc..3e1d4c1 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 @@ -222,70 +222,66 @@ public class VariableVisitor extends GraphNodeContentVisitor realNameWithoutRootList = new LinkedList<>(); + n.getTarget().accept(new VoidVisitorAdapter() { + @Override + public void visit(NameExpr nameExpr, Void arg) { + String realName = getRealName(nameExpr); + definitionStack.push(n.getValue()); + if (!realName.contains(".")) { + acceptAction(nameExpr, realName, DEFINITION); VariableAction.Definition def = getLastDefinition(); def.setTotallyDefinedMember(realName); + realNameWithoutRootList.add(""); + } else { + String root = ObjectTree.removeFields(realName); + acceptAction(nameExpr, root, DEFINITION); + VariableAction.Definition def = getLastDefinition(); def.getObjectTree().addField(realName); - realNameWithoutRootList.add(ObjectTree.removeRoot(realName)); + def.setTotallyDefinedMember(realName); + realNameWithoutRootList.add(realName); } + definitionStack.pop(); + } - @Override - public void visit(ArrayAccessExpr n, Void arg) { - throw new UnsupportedOperationException("Arrays are not yet supported as target of assignment."); + @Override + public void visit(FieldAccessExpr fieldAccessExpr, Void arg) { + Expression scope = fieldAccessExpr.getScope(); + boolean traverse = true; + while (traverse) { + if (scope.isFieldAccessExpr()) + scope = scope.asFieldAccessExpr().getScope(); + else if (scope.isEnclosedExpr()) + scope = scope.asEnclosedExpr().getInner(); + else if (scope.isCastExpr()) + scope = scope.asCastExpr().getExpression(); + else + traverse = false; } - }, null); - assert realNameWithoutRootList.size() == 1; - ExpressionObjectTreeFinder finder = new ExpressionObjectTreeFinder(graphNode); - finder.handleAssignExpr(n, getLastDefinition(), realNameWithoutRootList.get(0)); - } + if (!scope.isNameExpr() && !scope.isThisExpr()) + throw new IllegalStateException("only valid assignments are this[.]+ =, and [.]+"); + String realName = getRealName(fieldAccessExpr); + String root = ObjectTree.removeFields(realName); + definitionStack.push(n.getValue()); + acceptAction(fieldAccessExpr, root, DEFINITION); + definitionStack.pop(); + VariableAction.Definition def = getLastDefinition(); + def.setTotallyDefinedMember(realName); + def.getObjectTree().addField(realName); + realNameWithoutRootList.add(ObjectTree.removeRoot(realName)); + } + + @Override + public void visit(ArrayAccessExpr n, Void arg) { + throw new UnsupportedOperationException("Arrays are not yet supported as target of assignment."); + } + }, null); + assert realNameWithoutRootList.size() == 1; + ExpressionObjectTreeFinder finder = new ExpressionObjectTreeFinder(graphNode); + finder.handleAssignExpr(n, getLastDefinition(), realNameWithoutRootList.get(0)); } @Override @@ -320,8 +316,8 @@ public class VariableVisitor extends GraphNodeContentVisitor Date: Wed, 10 Mar 2021 13:25:54 +0100 Subject: [PATCH 17/60] Fix condition in tree connections --- .../es/upv/mist/slicing/graphs/ExpressionObjectTreeFinder.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) 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 d92a8ed..19cf29c 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 @@ -161,7 +161,8 @@ public class ExpressionObjectTreeFinder { protected void markTransference(Pair sourcePair, VariableAction targetAction, String targetMember) { VariableAction sourceAction = sourcePair.a; String sourceMember = sourcePair.b; - if (sourceAction.hasObjectTree() && !sourceAction.getObjectTree().isLeaf(sourceMember)) + if (targetAction.hasObjectTree() && + (!sourceAction.hasObjectTree() || !sourceAction.getObjectTree().isLeaf(sourceMember))) ObjectTree.copyTree(sourceAction.getObjectTree(), targetAction.getObjectTree(), sourceMember, targetMember); sourceAction.setPDGTreeConnectionTo(targetAction, sourceMember, targetMember); } -- GitLab From bbc7464ec0be455382d0b85a6136d357c5f311d3 Mon Sep 17 00:00:00 2001 From: Carlos Galindo Date: Wed, 10 Mar 2021 13:42:03 +0100 Subject: [PATCH 18/60] Generate interprocedural tree links between actual/formal-in --- .../graphs/jsysdg/JSysCallConnector.java | 29 +++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/sdg-core/src/main/java/es/upv/mist/slicing/graphs/jsysdg/JSysCallConnector.java b/sdg-core/src/main/java/es/upv/mist/slicing/graphs/jsysdg/JSysCallConnector.java index 255574b..9f7571d 100644 --- a/sdg-core/src/main/java/es/upv/mist/slicing/graphs/jsysdg/JSysCallConnector.java +++ b/sdg-core/src/main/java/es/upv/mist/slicing/graphs/jsysdg/JSysCallConnector.java @@ -6,6 +6,8 @@ import es.upv.mist.slicing.graphs.cfg.CFGBuilder; import es.upv.mist.slicing.graphs.exceptionsensitive.ExceptionSensitiveCallConnector; import es.upv.mist.slicing.nodes.GraphNode; import es.upv.mist.slicing.nodes.VariableAction; +import es.upv.mist.slicing.nodes.io.ActualIONode; +import es.upv.mist.slicing.nodes.io.FormalIONode; import es.upv.mist.slicing.nodes.io.OutputNode; import es.upv.mist.slicing.utils.ASTUtils; @@ -22,6 +24,33 @@ public class JSysCallConnector extends ExceptionSensitiveCallConnector { super.connectAllCalls(callGraph); } + @Override + protected void connectActualIn(GraphNode> declaration, ActualIONode actualIn) { + sdg.outgoingEdgesOf(declaration).stream() + .map(sdg::getEdgeTarget) + .filter(FormalIONode.class::isInstance) + .map(FormalIONode.class::cast) + .filter(actualIn::matchesFormalIO) + .forEach(formalIn -> { + boolean primitive = !formalIn.getVariableName().equals("this") + && declaration.getAstNode().getParameterByName(formalIn.getVariableName()) + .orElseThrow().getType().isPrimitiveType(); + if (primitive) + sdg.addParameterInOutArc(actualIn, formalIn); + else + connectObjectActualIn(actualIn, formalIn); + }); + } + + protected void connectObjectActualIn(GraphNode actualIn, GraphNode formalIn) { + List actualList = actualIn.getVariableActions(); + List formalList = formalIn.getVariableActions(); + assert formalList.size() == 1; + VariableAction actualVar = actualList.get(actualList.size() - 1); + VariableAction formalVar = formalList.get(0); + actualVar.applySDGTreeConnection((JSysDG) sdg, formalVar); + } + @Override protected void connectOutput(GraphNode> methodDeclaration, GraphNode callReturnNode) { Consumer> action; -- GitLab From 235869487a12452184f8aa1121a61a0de73c2b5b Mon Sep 17 00:00:00 2001 From: Carlos Galindo Date: Wed, 10 Mar 2021 16:38:42 +0100 Subject: [PATCH 19/60] SummaryArcs are now generated between every child node of actual-outs and actual-ins. --- .../mist/slicing/graphs/jsysdg/JSysDG.java | 11 ++ .../graphs/jsysdg/SummaryArcAnalyzer.java | 133 ++++++++++++++++++ 2 files changed, 144 insertions(+) create mode 100644 sdg-core/src/main/java/es/upv/mist/slicing/graphs/jsysdg/SummaryArcAnalyzer.java diff --git a/sdg-core/src/main/java/es/upv/mist/slicing/graphs/jsysdg/JSysDG.java b/sdg-core/src/main/java/es/upv/mist/slicing/graphs/jsysdg/JSysDG.java index 41f49d0..41277d5 100644 --- a/sdg-core/src/main/java/es/upv/mist/slicing/graphs/jsysdg/JSysDG.java +++ b/sdg-core/src/main/java/es/upv/mist/slicing/graphs/jsysdg/JSysDG.java @@ -8,12 +8,14 @@ import com.github.javaparser.ast.body.ClassOrInterfaceDeclaration; import com.github.javaparser.ast.body.ConstructorDeclaration; import com.github.javaparser.ast.visitor.ModifierVisitor; import com.github.javaparser.ast.visitor.Visitable; +import es.upv.mist.slicing.arcs.sdg.SummaryArc; import es.upv.mist.slicing.graphs.ClassGraph; import es.upv.mist.slicing.graphs.augmented.PSDG; import es.upv.mist.slicing.graphs.cfg.CFG; import es.upv.mist.slicing.graphs.exceptionsensitive.ESSDG; import es.upv.mist.slicing.graphs.exceptionsensitive.ExceptionSensitiveCallConnector; import es.upv.mist.slicing.graphs.pdg.PDG; +import es.upv.mist.slicing.nodes.SyntheticNode; import es.upv.mist.slicing.slicing.JSysDGSlicingAlgorithm; import es.upv.mist.slicing.slicing.SlicingAlgorithm; import es.upv.mist.slicing.utils.NodeHashSet; @@ -29,6 +31,10 @@ public class JSysDG extends ESSDG { return new JSysDG.Builder(); } + public void addSummaryArc(SyntheticNode from, SyntheticNode to) { + addEdge(from, to, new SummaryArc()); + } + /** Populates an ESSDG, using ESPDG and ESCFG as default graphs. * @see PSDG.Builder * @see ExceptionSensitiveCallConnector */ @@ -73,5 +79,10 @@ public class JSysDG extends ESSDG { protected void connectCalls() { new JSysCallConnector(JSysDG.this).connectAllCalls(callGraph); } + + @Override + protected void createSummaryArcs() { + new SummaryArcAnalyzer(JSysDG.this, callGraph).analyze();; + } } } diff --git a/sdg-core/src/main/java/es/upv/mist/slicing/graphs/jsysdg/SummaryArcAnalyzer.java b/sdg-core/src/main/java/es/upv/mist/slicing/graphs/jsysdg/SummaryArcAnalyzer.java new file mode 100644 index 0000000..9932bbf --- /dev/null +++ b/sdg-core/src/main/java/es/upv/mist/slicing/graphs/jsysdg/SummaryArcAnalyzer.java @@ -0,0 +1,133 @@ +package es.upv.mist.slicing.graphs.jsysdg; + +import com.github.javaparser.ast.body.CallableDeclaration; +import es.upv.mist.slicing.arcs.Arc; +import es.upv.mist.slicing.graphs.BackwardDataFlowAnalysis; +import es.upv.mist.slicing.graphs.CallGraph; +import es.upv.mist.slicing.nodes.GraphNode; +import es.upv.mist.slicing.nodes.SyntheticNode; +import es.upv.mist.slicing.nodes.VariableAction; +import es.upv.mist.slicing.nodes.exceptionsensitive.ExitNode; +import es.upv.mist.slicing.nodes.io.FormalIONode; +import es.upv.mist.slicing.nodes.io.OutputNode; +import es.upv.mist.slicing.nodes.oo.MemberNode; + +import java.util.*; +import java.util.stream.Stream; + +public class SummaryArcAnalyzer extends BackwardDataFlowAnalysis, Map, Set>>> { + protected final JSysDG sdg; + + public SummaryArcAnalyzer(JSysDG sdg, CallGraph graph) { + super(graph); + this.sdg = sdg; + } + + @Override + protected Map, Set>> compute(CallGraph.Vertex vertex, Set predecessors) { + saveDeclaration(vertex); + return initialValue(vertex); + } + + @Override + protected Map, Set>> initialValue(CallGraph.Vertex vertex) { + Map, Set>> value; + if (vertexDataMap.containsKey(vertex)) { + value = vertexDataMap.get(vertex); + } else { + value = new HashMap<>(); + for (var formalOut : getFormalOutNodes(vertex.getDeclaration())) + value.put(formalOut, new HashSet<>()); + } + value.replaceAll((key, oldValue) -> computeFormalIn(key)); + return value; + } + + /** Generate all summary arcs for a given call. Arc generation should be idempotent: + * if this method is called repeatedly it should not create duplicate summary arcs. */ + protected void saveDeclaration(CallGraph.Vertex vertex) { + var result = vertexDataMap.get(vertex); + for (CallGraph.Edge edge : graph.incomingEdgesOf(vertex)) { + for (var entry : result.entrySet()) { + var actualOutOpt = findOutputNode(edge, entry.getKey()); + if (actualOutOpt.isEmpty()) + continue; + for (var formalIn : entry.getValue()) { + var actualInOpt = findActualIn(edge, formalIn); + if (actualInOpt.isEmpty()) + continue; + if (!sdg.containsEdge(actualInOpt.get(), actualOutOpt.get())) + sdg.addSummaryArc(actualInOpt.get(), actualOutOpt.get()); + } + } + } + } + + protected Set> getFormalOutNodes(CallableDeclaration declaration) { + Set> set = new HashSet<>(); + Stream.concat( + Stream.concat( + sdg.vertexSet().stream() // formal-out nodes + .filter(FormalIONode.class::isInstance) + .map(FormalIONode.class::cast) + .filter(FormalIONode::isOutput), + sdg.vertexSet().stream() // output nodes (the value returned) + .filter(OutputNode.class::isInstance) + .map(OutputNode.class::cast)), + sdg.vertexSet().stream() // normal/exception exit nodes (for exception handling) + .filter(ExitNode.class::isInstance) + .map(ExitNode.class::cast)) + // Only nodes that match the current declaration + .filter(node -> node.getAstNode() == declaration) + .forEach(set::add); + for (var node : Set.copyOf(set)) { + if (node.getVariableActions().isEmpty()) + continue; + assert node.getVariableActions().size() == 1; + VariableAction action = node.getVariableActions().get(0); + if (action.hasObjectTree()) { + set.add(action.getObjectTree().getMemberNode()); + for (MemberNode memberNode : action.getObjectTree().nodeIterable()) + set.add(memberNode); + } + } + return set; + } + + protected Set> computeFormalIn(SyntheticNode formalOut) { + Set> result = new HashSet<>(); + for (GraphNode graphNode : sdg.createSlicingAlgorithm().traverseProcedure(formalOut).getGraphNodes()) + if (isFormalIn(graphNode) && graphNode instanceof SyntheticNode) + result.add((SyntheticNode) graphNode); + return result; + } + + protected Optional> findActualIn(CallGraph.Edge edge, SyntheticNode formalIn) { + return sdg.incomingEdgesOf(formalIn).stream() + .filter(Arc::isInterproceduralInputArc) + .map(sdg::getEdgeSource) + .filter(actualIn -> goToParent(actualIn).getAstNode() == edge.getCall()) + .map(node -> (SyntheticNode) node) + .findAny(); + } + + protected Optional> findOutputNode(CallGraph.Edge edge, SyntheticNode formalOut) { + return sdg.outgoingEdgesOf(formalOut).stream() + .filter(Arc::isInterproceduralOutputArc) + .map(sdg::getEdgeTarget) + .filter(actualOut -> goToParent(actualOut).getAstNode() == edge.getCall()) + .map(node -> (SyntheticNode) node) + .findAny(); + } + + private boolean isFormalIn(GraphNode graphNode) { + GraphNode parent = goToParent(graphNode); + return parent instanceof FormalIONode && ((FormalIONode) parent).isInput(); + } + + private GraphNode goToParent(GraphNode memberNode) { + if (memberNode instanceof MemberNode) + return goToParent(((MemberNode) memberNode).getParent()); + return memberNode; + } +} -- GitLab From 2ef0b2d04261ad2a7c0cfdd35f2cbdce7124d7fa Mon Sep 17 00:00:00 2001 From: Carlos Galindo Date: Wed, 10 Mar 2021 16:47:10 +0100 Subject: [PATCH 20/60] Fixed bug with totally defined detection --- .../main/java/es/upv/mist/slicing/nodes/VariableAction.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/sdg-core/src/main/java/es/upv/mist/slicing/nodes/VariableAction.java b/sdg-core/src/main/java/es/upv/mist/slicing/nodes/VariableAction.java index 96620f9..e13ca52 100644 --- a/sdg-core/src/main/java/es/upv/mist/slicing/nodes/VariableAction.java +++ b/sdg-core/src/main/java/es/upv/mist/slicing/nodes/VariableAction.java @@ -388,8 +388,8 @@ public abstract class VariableAction { public boolean isTotallyDefinedMember(String member) { if (totallyDefinedMember == null) return false; - return totallyDefinedMember.equals(member) || totallyDefinedMember.startsWith(member) - || ObjectTree.removeRoot(totallyDefinedMember).startsWith(ObjectTree.removeRoot(member)); + return totallyDefinedMember.equals(member) || member.startsWith(totallyDefinedMember) + || ObjectTree.removeRoot(member).startsWith(ObjectTree.removeRoot(totallyDefinedMember)); } /** @see #expression */ -- GitLab From 0551948b4f47d82d9f4e276b441db51671b10145 Mon Sep 17 00:00:00 2001 From: Carlos Galindo Date: Wed, 10 Mar 2021 17:09:40 +0100 Subject: [PATCH 21/60] fixes for returned value of explicit constructor invocations * TotalDefinition now can be connected to field declaration or total definition. --- .../slicing/graphs/ExpressionObjectTreeFinder.java | 2 +- .../es/upv/mist/slicing/graphs/jsysdg/JSysCFG.java | 4 +++- .../java/es/upv/mist/slicing/nodes/ObjectTree.java | 2 +- .../es/upv/mist/slicing/nodes/VariableVisitor.java | 11 +++++++++++ 4 files changed, 16 insertions(+), 3 deletions(-) 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 19cf29c..0a2a353 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 @@ -163,7 +163,7 @@ public class ExpressionObjectTreeFinder { String sourceMember = sourcePair.b; if (targetAction.hasObjectTree() && (!sourceAction.hasObjectTree() || !sourceAction.getObjectTree().isLeaf(sourceMember))) - ObjectTree.copyTree(sourceAction.getObjectTree(), targetAction.getObjectTree(), sourceMember, targetMember); + ObjectTree.copyTargetTreeToSource(sourceAction.getObjectTree(), targetAction.getObjectTree(), sourceMember, targetMember); sourceAction.setPDGTreeConnectionTo(targetAction, sourceMember, targetMember); } } diff --git a/sdg-core/src/main/java/es/upv/mist/slicing/graphs/jsysdg/JSysCFG.java b/sdg-core/src/main/java/es/upv/mist/slicing/graphs/jsysdg/JSysCFG.java index ccb18be..fde7fcc 100644 --- a/sdg-core/src/main/java/es/upv/mist/slicing/graphs/jsysdg/JSysCFG.java +++ b/sdg-core/src/main/java/es/upv/mist/slicing/graphs/jsysdg/JSysCFG.java @@ -71,7 +71,9 @@ public class JSysCFG extends ESCFG { } public List findLastTotalDefinitionOf(VariableAction action, String member) { - return findLastVarActionsFrom(action, def -> def.isDefinition() && def.asDefinition().isTotallyDefinedMember(member)); + return findLastVarActionsFrom(action, def -> + (def.isDeclaration() && def.hasTreeMember(member)) + || (def.isDefinition() && def.asDefinition().isTotallyDefinedMember(member))); } /** Given a definition of a given member, locate all definitions of the same object until a definition 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 e6c2a99..bce69a7 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 @@ -87,7 +87,7 @@ public class ObjectTree implements Cloneable { * @param sourcePrefix The prefix to be consumed before copying nodes. Without root. * @param targetPrefix The prefix to be consumed before copying nodes. Without root. */ - public static void copyTree(ObjectTree source, ObjectTree target, String sourcePrefix, String targetPrefix) { + public static void copyTargetTreeToSource(ObjectTree source, ObjectTree target, String sourcePrefix, String targetPrefix) { ObjectTree a = source.findObjectTreeOfMember(sourcePrefix); ObjectTree b = target.findObjectTreeOfMember(targetPrefix); a.addAll(b); 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 3e1d4c1..1e16782 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 @@ -375,6 +375,17 @@ public class VariableVisitor extends GraphNodeContentVisitor vaList = graphNode.getVariableActions(); + if (vaList.size() >= 5) { // call-super, DEC(this), USE(-output-), ret-super, DEF(this) + VariableAction useOutput = vaList.get(vaList.size() - 3); + VariableAction defThis = vaList.get(vaList.size() - 1); + assert useOutput.isUsage() && useOutput.getName().equals(VARIABLE_NAME_OUTPUT); + assert defThis.isDefinition() && defThis.getName().equals("this"); + defThis.asDefinition().setTotallyDefinedMember("this"); + ObjectTree.copyTargetTreeToSource(defThis.getObjectTree(), useOutput.getObjectTree(), "", ""); + useOutput.setPDGTreeConnectionTo(defThis, "", ""); + } } @Override -- GitLab From 2c8293d5bf4e2d92c9f51fa97da3e7f2999c30a5 Mon Sep 17 00:00:00 2001 From: Carlos Galindo Date: Wed, 10 Mar 2021 17:14:06 +0100 Subject: [PATCH 22/60] fix! summaries are now generated between actual/formal-in of object type --- .../es/upv/mist/slicing/graphs/jsysdg/JSysCallConnector.java | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/sdg-core/src/main/java/es/upv/mist/slicing/graphs/jsysdg/JSysCallConnector.java b/sdg-core/src/main/java/es/upv/mist/slicing/graphs/jsysdg/JSysCallConnector.java index 9f7571d..085a9f7 100644 --- a/sdg-core/src/main/java/es/upv/mist/slicing/graphs/jsysdg/JSysCallConnector.java +++ b/sdg-core/src/main/java/es/upv/mist/slicing/graphs/jsysdg/JSysCallConnector.java @@ -35,9 +35,8 @@ public class JSysCallConnector extends ExceptionSensitiveCallConnector { boolean primitive = !formalIn.getVariableName().equals("this") && declaration.getAstNode().getParameterByName(formalIn.getVariableName()) .orElseThrow().getType().isPrimitiveType(); - if (primitive) - sdg.addParameterInOutArc(actualIn, formalIn); - else + sdg.addParameterInOutArc(actualIn, formalIn); + if (!primitive) connectObjectActualIn(actualIn, formalIn); }); } -- GitLab From ab3035529987ee240c8e76cf5b7504ff25d3fd32 Mon Sep 17 00:00:00 2001 From: Carlos Galindo Date: Wed, 10 Mar 2021 17:24:05 +0100 Subject: [PATCH 23/60] fix! remove multiple instances of scope-in and arg-in from actual-in nodes. --- .../upv/mist/slicing/graphs/sdg/InterproceduralUsageFinder.java | 2 -- .../main/java/es/upv/mist/slicing/nodes/VariableVisitor.java | 1 - 2 files changed, 3 deletions(-) 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 9685263..b6d3570 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 @@ -44,7 +44,6 @@ public class InterproceduralUsageFinder extends InterproceduralActionFinder Date: Wed, 10 Mar 2021 17:46:19 +0100 Subject: [PATCH 24/60] Group actions before applying the ExpressionObjectTreeFinder and make grouping actions by root idempotent. --- .../java/es/upv/mist/slicing/nodes/VariableVisitor.java | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) 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 5ce45f8..4020a20 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 @@ -265,7 +265,10 @@ public class VariableVisitor extends GraphNodeContentVisitor Date: Wed, 10 Mar 2021 18:01:08 +0100 Subject: [PATCH 25/60] Add value dependency between the active exception definition and its throw/exception return statement. --- .../es/upv/mist/slicing/graphs/jsysdg/JSysPDG.java | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/sdg-core/src/main/java/es/upv/mist/slicing/graphs/jsysdg/JSysPDG.java b/sdg-core/src/main/java/es/upv/mist/slicing/graphs/jsysdg/JSysPDG.java index ba0a3ec..b5302f1 100644 --- a/sdg-core/src/main/java/es/upv/mist/slicing/graphs/jsysdg/JSysPDG.java +++ b/sdg-core/src/main/java/es/upv/mist/slicing/graphs/jsysdg/JSysPDG.java @@ -3,6 +3,7 @@ package es.upv.mist.slicing.graphs.jsysdg; import es.upv.mist.slicing.arcs.pdg.FlowDependencyArc; import es.upv.mist.slicing.arcs.pdg.ObjectFlowDependencyArc; import es.upv.mist.slicing.arcs.pdg.TotalDefinitionDependenceArc; +import es.upv.mist.slicing.graphs.exceptionsensitive.ESCFG; import es.upv.mist.slicing.graphs.exceptionsensitive.ESPDG; import es.upv.mist.slicing.graphs.pdg.PDG; import es.upv.mist.slicing.nodes.GraphNode; @@ -86,6 +87,7 @@ public class JSysPDG extends ESPDG { addSyntheticNodesToPDG(); applyTreeConnections(); buildJSysDataDependency(); + valueDependencyForThrowStatements(); } protected void buildJSysDataDependency() { @@ -186,5 +188,15 @@ public class JSysPDG extends ESPDG { addVertex(memberNode); addControlDependencyArc(memberNode.getParent(), memberNode); } + + protected void valueDependencyForThrowStatements() { + for (GraphNode node : vertexSet()) { + for (VariableAction action : node.getVariableActions()) { + if (action.isDefinition() && action.getName().equals(ESCFG.ACTIVE_EXCEPTION_VARIABLE)) { + addValueDependencyArc(action, "-root-", node); + } + } + } + } } } -- GitLab From 048fcceef970f87f028b2d63a241a56ed5c6c797 Mon Sep 17 00:00:00 2001 From: Carlos Galindo Date: Wed, 10 Mar 2021 18:26:46 +0100 Subject: [PATCH 26/60] Add value dependencies in tree connections (=, return, throw, etc). --- .../upv/mist/slicing/nodes/ObjectTreeConnection.java | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/sdg-core/src/main/java/es/upv/mist/slicing/nodes/ObjectTreeConnection.java b/sdg-core/src/main/java/es/upv/mist/slicing/nodes/ObjectTreeConnection.java index 86c6c1b..788eb53 100644 --- a/sdg-core/src/main/java/es/upv/mist/slicing/nodes/ObjectTreeConnection.java +++ b/sdg-core/src/main/java/es/upv/mist/slicing/nodes/ObjectTreeConnection.java @@ -12,6 +12,7 @@ import es.upv.mist.slicing.nodes.oo.MemberNode; import java.util.function.Supplier; class ObjectTreeConnection { + protected final VariableAction sourceAction; protected final VariableAction targetAction; protected final String sourceMember; @@ -41,6 +42,7 @@ class ObjectTreeConnection { } protected void connectTrees(Graph graph, Supplier flowSupplier, Supplier objFlowSupplier) { + Supplier valueSupplier = flowSupplier; ObjectTree source = null, target = null; GraphNode rootSrc, rootTgt; assert sourceMember.isEmpty() || sourceAction.hasObjectTree(); @@ -59,15 +61,17 @@ class ObjectTreeConnection { } if (source == null || target == null) { if (!rootSrc.equals(rootTgt)) - graph.addEdge(rootSrc, rootTgt, new FlowDependencyArc()); // VALUE DEPENDENCE + graph.addEdge(rootSrc, rootTgt, valueSupplier.get()); } else { graph.addEdge(rootSrc, rootTgt, objFlowSupplier.get()); + graph.addEdge(rootSrc, rootTgt, valueSupplier.get()); for (ObjectTree tree : target.treeIterable()) { MemberNode src = source.getNodeForNonRoot(tree.getMemberName()); MemberNode tgt = tree.getMemberNode(); - if (tree.hasChildren()) + if (tree.hasChildren()) { graph.addEdge(src, tgt, objFlowSupplier.get()); - else + graph.addEdge(src, tgt, valueSupplier.get()); + } else graph.addEdge(src, tgt, flowSupplier.get()); } } -- GitLab From e66f48188e346ef276740c9870e630d20fd942b8 Mon Sep 17 00:00:00 2001 From: Carlos Galindo Date: Wed, 10 Mar 2021 18:26:59 +0100 Subject: [PATCH 27/60] fix! search for root graph node reordered. --- .../main/java/es/upv/mist/slicing/graphs/jsysdg/JSysPDG.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/sdg-core/src/main/java/es/upv/mist/slicing/graphs/jsysdg/JSysPDG.java b/sdg-core/src/main/java/es/upv/mist/slicing/graphs/jsysdg/JSysPDG.java index b5302f1..5b8979c 100644 --- a/sdg-core/src/main/java/es/upv/mist/slicing/graphs/jsysdg/JSysPDG.java +++ b/sdg-core/src/main/java/es/upv/mist/slicing/graphs/jsysdg/JSysPDG.java @@ -67,10 +67,10 @@ public class JSysPDG extends ESPDG { } protected GraphNode graphNodeOf(VariableAction action) { - if (action instanceof VariableAction.Movable) - return ((VariableAction.Movable) action).getRealNode(); if (action.hasObjectTree()) return action.getObjectTree().getMemberNode(); + if (action instanceof VariableAction.Movable) + return ((VariableAction.Movable) action).getRealNode(); return action.getGraphNode(); } -- GitLab From a1e33e3b315b5ea6e6c3b5ac17141c21776e945f Mon Sep 17 00:00:00 2001 From: Carlos Galindo Date: Wed, 10 Mar 2021 18:40:22 +0100 Subject: [PATCH 28/60] Mark -output- as totally defined every time it is defined. --- .../java/es/upv/mist/slicing/graphs/jsysdg/JSysCFG.java | 3 +-- .../upv/mist/slicing/graphs/jsysdg/JSysCallConnector.java | 3 +-- .../src/main/java/es/upv/mist/slicing/nodes/GraphNode.java | 6 ++++++ .../java/es/upv/mist/slicing/nodes/VariableVisitor.java | 5 ++++- 4 files changed, 12 insertions(+), 5 deletions(-) diff --git a/sdg-core/src/main/java/es/upv/mist/slicing/graphs/jsysdg/JSysCFG.java b/sdg-core/src/main/java/es/upv/mist/slicing/graphs/jsysdg/JSysCFG.java index fde7fcc..b584c4f 100644 --- a/sdg-core/src/main/java/es/upv/mist/slicing/graphs/jsysdg/JSysCFG.java +++ b/sdg-core/src/main/java/es/upv/mist/slicing/graphs/jsysdg/JSysCFG.java @@ -207,8 +207,7 @@ public class JSysCFG extends ESCFG { // Insert tree into GraphNode nodes, the last action is always DEF(-output-) vertexSet().stream() .filter(gn -> gn.getAstNode() instanceof ReturnStmt) - .map(GraphNode::getVariableActions) - .map(list -> list.get(list.size() - 1)) + .map(GraphNode::getLastVariableAction) .map(VariableAction::getObjectTree) .forEach(tree -> tree.addAll(fields.get())); } diff --git a/sdg-core/src/main/java/es/upv/mist/slicing/graphs/jsysdg/JSysCallConnector.java b/sdg-core/src/main/java/es/upv/mist/slicing/graphs/jsysdg/JSysCallConnector.java index 085a9f7..462ac38 100644 --- a/sdg-core/src/main/java/es/upv/mist/slicing/graphs/jsysdg/JSysCallConnector.java +++ b/sdg-core/src/main/java/es/upv/mist/slicing/graphs/jsysdg/JSysCallConnector.java @@ -42,10 +42,9 @@ public class JSysCallConnector extends ExceptionSensitiveCallConnector { } protected void connectObjectActualIn(GraphNode actualIn, GraphNode formalIn) { - List actualList = actualIn.getVariableActions(); List formalList = formalIn.getVariableActions(); assert formalList.size() == 1; - VariableAction actualVar = actualList.get(actualList.size() - 1); + VariableAction actualVar = actualIn.getLastVariableAction(); VariableAction formalVar = formalList.get(0); actualVar.applySDGTreeConnection((JSysDG) sdg, formalVar); } diff --git a/sdg-core/src/main/java/es/upv/mist/slicing/nodes/GraphNode.java b/sdg-core/src/main/java/es/upv/mist/slicing/nodes/GraphNode.java index 5738942..c77b250 100644 --- a/sdg-core/src/main/java/es/upv/mist/slicing/nodes/GraphNode.java +++ b/sdg-core/src/main/java/es/upv/mist/slicing/nodes/GraphNode.java @@ -81,6 +81,12 @@ public class GraphNode implements Comparable> { return Collections.unmodifiableList(variableActions); } + public VariableAction getLastVariableAction() { + if (variableActions.isEmpty()) + throw new IllegalStateException("There are no variable actions in this node"); + return variableActions.get(variableActions.size() - 1); + } + /** The node's label. It represents the portion of the node that * is covered by this node, in the case of block statements. */ public String getLabel() { 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 4020a20..4454b72 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 @@ -167,6 +167,8 @@ public class VariableVisitor extends GraphNodeContentVisitor vaList = graphNode.getVariableActions(); if (vaList.size() >= 5) { // call-super, DEC(this), USE(-output-), ret-super, DEF(this) VariableAction useOutput = vaList.get(vaList.size() - 3); - VariableAction defThis = vaList.get(vaList.size() - 1); + VariableAction defThis = graphNode.getLastVariableAction(); assert useOutput.isUsage() && useOutput.getName().equals(VARIABLE_NAME_OUTPUT); assert defThis.isDefinition() && defThis.getName().equals("this"); defThis.asDefinition().setTotallyDefinedMember("this"); @@ -444,6 +446,7 @@ public class VariableVisitor extends GraphNodeContentVisitor Date: Wed, 10 Mar 2021 18:40:45 +0100 Subject: [PATCH 29/60] fix! value dependency arc for active exception only when it is separated from its parent node. --- .../es/upv/mist/slicing/graphs/jsysdg/JSysPDG.java | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/sdg-core/src/main/java/es/upv/mist/slicing/graphs/jsysdg/JSysPDG.java b/sdg-core/src/main/java/es/upv/mist/slicing/graphs/jsysdg/JSysPDG.java index 5b8979c..e7e1175 100644 --- a/sdg-core/src/main/java/es/upv/mist/slicing/graphs/jsysdg/JSysPDG.java +++ b/sdg-core/src/main/java/es/upv/mist/slicing/graphs/jsysdg/JSysPDG.java @@ -190,13 +190,12 @@ public class JSysPDG extends ESPDG { } protected void valueDependencyForThrowStatements() { - for (GraphNode node : vertexSet()) { - for (VariableAction action : node.getVariableActions()) { - if (action.isDefinition() && action.getName().equals(ESCFG.ACTIVE_EXCEPTION_VARIABLE)) { + for (GraphNode node : vertexSet()) + for (VariableAction action : node.getVariableActions()) + if (action.isDefinition() + && action.hasObjectTree() + && action.getName().equals(ESCFG.ACTIVE_EXCEPTION_VARIABLE)) addValueDependencyArc(action, "-root-", node); - } - } - } } } } -- GitLab From 2bb7bac6b6100ebd10f829d693fa358397c4fe7b Mon Sep 17 00:00:00 2001 From: Carlos Galindo Date: Wed, 10 Mar 2021 18:43:01 +0100 Subject: [PATCH 30/60] fix! exclude static fields from ExpressionObjectTreeFinder check --- .../upv/mist/slicing/graphs/ExpressionObjectTreeFinder.java | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) 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 0a2a353..d46392c 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 @@ -6,6 +6,7 @@ import com.github.javaparser.ast.expr.*; import com.github.javaparser.ast.visitor.VoidVisitorAdapter; import com.github.javaparser.resolution.Resolvable; import com.github.javaparser.resolution.declarations.ResolvedMethodLikeDeclaration; +import com.github.javaparser.resolution.declarations.ResolvedValueDeclaration; import com.github.javaparser.utils.Pair; import es.upv.mist.slicing.nodes.GraphNode; import es.upv.mist.slicing.nodes.ObjectTree; @@ -92,9 +93,10 @@ public class ExpressionObjectTreeFinder { @Override public void visit(NameExpr n, String arg) { - if (n.resolve().isType()) + ResolvedValueDeclaration resolved = n.resolve(); + if (resolved.isType()) return; - if (n.resolve().isField()) { + if (resolved.isField() && !resolved.asField().isStatic()) { new FieldAccessExpr(new ThisExpr(), n.getNameAsString()).accept(this, arg); return; } -- GitLab From fef704d0b56dd0bab320de80e7a37c85d365a895 Mon Sep 17 00:00:00 2001 From: Carlos Galindo Date: Wed, 10 Mar 2021 19:24:04 +0100 Subject: [PATCH 31/60] Add Structural arcs to avoid stopping the traversal when a pseudo-predicate (i.e. return) has objects connected to it. --- .../mist/slicing/arcs/pdg/StructuralArc.java | 16 +++++++++++++++ .../mist/slicing/graphs/jsysdg/JSysPDG.java | 20 +++++++++++++++++-- .../slicing/graphs/sdg/CallConnector.java | 3 --- 3 files changed, 34 insertions(+), 5 deletions(-) create mode 100644 sdg-core/src/main/java/es/upv/mist/slicing/arcs/pdg/StructuralArc.java diff --git a/sdg-core/src/main/java/es/upv/mist/slicing/arcs/pdg/StructuralArc.java b/sdg-core/src/main/java/es/upv/mist/slicing/arcs/pdg/StructuralArc.java new file mode 100644 index 0000000..85b91c2 --- /dev/null +++ b/sdg-core/src/main/java/es/upv/mist/slicing/arcs/pdg/StructuralArc.java @@ -0,0 +1,16 @@ +package es.upv.mist.slicing.arcs.pdg; + +import es.upv.mist.slicing.arcs.Arc; +import org.jgrapht.nio.Attribute; +import org.jgrapht.nio.DefaultAttribute; + +import java.util.Map; + +public class StructuralArc extends Arc { + @Override + public Map getDotAttributes() { + Map map = super.getDotAttributes(); + map.put("style", DefaultAttribute.createAttribute("dashed")); + return map; + } +} diff --git a/sdg-core/src/main/java/es/upv/mist/slicing/graphs/jsysdg/JSysPDG.java b/sdg-core/src/main/java/es/upv/mist/slicing/graphs/jsysdg/JSysPDG.java index e7e1175..f649c80 100644 --- a/sdg-core/src/main/java/es/upv/mist/slicing/graphs/jsysdg/JSysPDG.java +++ b/sdg-core/src/main/java/es/upv/mist/slicing/graphs/jsysdg/JSysPDG.java @@ -2,6 +2,7 @@ package es.upv.mist.slicing.graphs.jsysdg; import es.upv.mist.slicing.arcs.pdg.FlowDependencyArc; import es.upv.mist.slicing.arcs.pdg.ObjectFlowDependencyArc; +import es.upv.mist.slicing.arcs.pdg.StructuralArc; import es.upv.mist.slicing.arcs.pdg.TotalDefinitionDependenceArc; import es.upv.mist.slicing.graphs.exceptionsensitive.ESCFG; import es.upv.mist.slicing.graphs.exceptionsensitive.ESPDG; @@ -9,6 +10,7 @@ import es.upv.mist.slicing.graphs.pdg.PDG; 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.io.ActualIONode; import es.upv.mist.slicing.nodes.io.CallNode; import es.upv.mist.slicing.nodes.oo.MemberNode; @@ -30,6 +32,10 @@ public class JSysPDG extends ESPDG { return new Builder(); } + protected void addStructuralArc(GraphNode source, GraphNode target) { + addEdge(source, target, new StructuralArc()); + } + // definicion de raiz --object-flow--> uso de raiz protected void addObjectFlowDependencyArc(VariableAction definition, VariableAction usage) { addEdge(graphNodeOf(definition), graphNodeOf(usage), new ObjectFlowDependencyArc()); @@ -149,7 +155,7 @@ public class JSysPDG extends ESPDG { if (node.isImplicitInstruction()) callNode.markAsImplicit(); addVertex(callNode); - addControlDependencyArc(node, callNode); + addStructuralArc(node, callNode); callNodeStack.push(callNode); } continue; @@ -175,6 +181,16 @@ public class JSysPDG extends ESPDG { } } + @Override + protected void connectRealNode(GraphNode graphNode, CallNode callNode, GraphNode realNode) { + if (realNode instanceof ActualIONode || realNode instanceof CallNode.Return) { + assert callNode != null; + addStructuralArc(callNode, realNode); + } else { + addStructuralArc(graphNode == cfg.getExitNode() ? rootNode : graphNode, realNode); + } + } + protected void applyTreeConnections() { cfg.vertexSet().stream() .flatMap(node -> node.getVariableActions().stream()) @@ -186,7 +202,7 @@ public class JSysPDG extends ESPDG { memberNode.setParent(parentNode); assert containsVertex(memberNode.getParent()); addVertex(memberNode); - addControlDependencyArc(memberNode.getParent(), memberNode); + addStructuralArc(memberNode.getParent(), memberNode); } protected void valueDependencyForThrowStatements() { diff --git a/sdg-core/src/main/java/es/upv/mist/slicing/graphs/sdg/CallConnector.java b/sdg-core/src/main/java/es/upv/mist/slicing/graphs/sdg/CallConnector.java index fbcc297..ca0a304 100644 --- a/sdg-core/src/main/java/es/upv/mist/slicing/graphs/sdg/CallConnector.java +++ b/sdg-core/src/main/java/es/upv/mist/slicing/graphs/sdg/CallConnector.java @@ -3,7 +3,6 @@ package es.upv.mist.slicing.graphs.sdg; import com.github.javaparser.ast.body.CallableDeclaration; import com.github.javaparser.resolution.Resolvable; import com.github.javaparser.resolution.declarations.ResolvedMethodLikeDeclaration; -import es.upv.mist.slicing.arcs.Arc; import es.upv.mist.slicing.arcs.sdg.CallArc; import es.upv.mist.slicing.arcs.sdg.ParameterInOutArc; import es.upv.mist.slicing.graphs.CallGraph; @@ -54,7 +53,6 @@ public class CallConnector { // Locate and connect all ACTUAL nodes sdg.outgoingEdgesOf(callNode).stream() - .filter(Arc::isControlDependencyArc) .map(sdg::getEdgeTarget) .filter(ActualIONode.class::isInstance) .map(ActualIONode.class::cast) @@ -67,7 +65,6 @@ public class CallConnector { // Locate and connect the -output- node sdg.outgoingEdgesOf(callNode).stream() - .filter(Arc::isControlDependencyArc) .map(sdg::getEdgeTarget) .filter(CallNode.Return.class::isInstance) .forEach(n -> connectOutput(declarationNode, n)); -- GitLab From 1c08eb01d9f9f9c8d270e9ddd5da097449e13e0d Mon Sep 17 00:00:00 2001 From: Carlos Galindo Date: Wed, 10 Mar 2021 20:05:56 +0100 Subject: [PATCH 32/60] Add declaration arcs between field declarations and its definitions. --- .../mist/slicing/graphs/jsysdg/JSysCFG.java | 28 ++++++++++++++++++- .../mist/slicing/graphs/jsysdg/JSysPDG.java | 11 ++++++++ 2 files changed, 38 insertions(+), 1 deletion(-) diff --git a/sdg-core/src/main/java/es/upv/mist/slicing/graphs/jsysdg/JSysCFG.java b/sdg-core/src/main/java/es/upv/mist/slicing/graphs/jsysdg/JSysCFG.java index b584c4f..ef7d6ab 100644 --- a/sdg-core/src/main/java/es/upv/mist/slicing/graphs/jsysdg/JSysCFG.java +++ b/sdg-core/src/main/java/es/upv/mist/slicing/graphs/jsysdg/JSysCFG.java @@ -70,6 +70,32 @@ public class JSysCFG extends ESCFG { return findLastVarActionsFrom(usage, VariableAction::isDefinition); } + public List findAllFutureObjectDefinitionsFor(VariableAction action) { + List list = new LinkedList<>(); + Predicate filter = a -> a.isDefinition() && a.getName().equals("this") && a.hasTreeMember(action.getName()); + findAllFutureVarActionsFor(new HashSet<>(), list, action.getGraphNode(), action, filter); + return list; + } + + protected void findAllFutureVarActionsFor(Set> visited, List result, + GraphNode currentNode, VariableAction var, + Predicate filter) { + // Base case + if (visited.contains(currentNode)) + return; + visited.add(currentNode); + + Stream stream = currentNode.getVariableActions().stream(); + if (var.getGraphNode().equals(currentNode)) + stream = stream.dropWhile(va -> va != var); + stream.filter(filter).forEach(result::add); + + // always traverse forwards! + for (Arc arc : outgoingEdgesOf(currentNode)) + if (arc.isExecutableControlFlowArc()) + findAllFutureVarActionsFor(visited, result, getEdgeTarget(arc), var, filter); + } + public List findLastTotalDefinitionOf(VariableAction action, String member) { return findLastVarActionsFrom(action, def -> (def.isDeclaration() && def.hasTreeMember(member)) @@ -114,7 +140,7 @@ public class JSysCFG extends ESCFG { return true; } - // Not found: traverse backwards! + // Not found: traverse forwards! boolean allBranches = !outgoingEdgesOf(currentNode).isEmpty(); for (Arc arc : outgoingEdgesOf(currentNode)) if (arc.isExecutableControlFlowArc()) diff --git a/sdg-core/src/main/java/es/upv/mist/slicing/graphs/jsysdg/JSysPDG.java b/sdg-core/src/main/java/es/upv/mist/slicing/graphs/jsysdg/JSysPDG.java index f649c80..175dffd 100644 --- a/sdg-core/src/main/java/es/upv/mist/slicing/graphs/jsysdg/JSysPDG.java +++ b/sdg-core/src/main/java/es/upv/mist/slicing/graphs/jsysdg/JSysPDG.java @@ -52,6 +52,11 @@ public class JSysPDG extends ESPDG { addEdge(graphNodeOf(source), graphNodeOf(target), new FlowDependencyArc(source.getName())); } + protected void addDeclarationFlowDependencyArc(VariableAction declaration, VariableAction definition) { + MemberNode defMember = definition.getObjectTree().getNodeFor(declaration.getName()); + addEdge(graphNodeOf(declaration), defMember, new FlowDependencyArc()); + } + // definicion de miembro --flow--> uso de miembro protected void addFlowDependencyArc(VariableAction definition, VariableAction usage, String objMember) { GraphNode defMember = definition.getObjectTree().getNodeFor(objMember); @@ -120,11 +125,17 @@ public class JSysPDG extends ESPDG { jSysCFG.findLastDefinitionOfObjectMember(varAct, member).forEach(def -> addFlowDependencyArc(def, varAct, member)); } } else if (varAct.isDefinition()) { + // Flow declaration --> definition if (!varAct.isSynthetic()) jSysCFG.findDeclarationFor(varAct).ifPresent(dec -> addFlowDependencyArc(dec, varAct)); + // Object flow definition --> definition if (!varAct.isPrimitive() && varAct.hasObjectTree()) for (String member : varAct.getObjectTree().nameIterable()) jSysCFG.findNextObjectDefinitionsFor(varAct, member).forEach(def -> addObjectFlowDependencyArc(varAct, member, def)); + } else if (varAct.isDeclaration()) { + if (varAct.getName().startsWith("this.")) + jSysCFG.findAllFutureObjectDefinitionsFor(varAct) + .forEach(def -> addDeclarationFlowDependencyArc(varAct, def)); } } } -- GitLab From 175dba1163e51eac0624fd9a2f3b91a33108a6f6 Mon Sep 17 00:00:00 2001 From: Carlos Galindo Date: Thu, 11 Mar 2021 10:38:53 +0100 Subject: [PATCH 33/60] fix! tweaks to totally defined members and assignment discovery --- .../java/es/upv/mist/slicing/nodes/VariableAction.java | 8 ++++++-- .../java/es/upv/mist/slicing/nodes/VariableVisitor.java | 2 +- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/sdg-core/src/main/java/es/upv/mist/slicing/nodes/VariableAction.java b/sdg-core/src/main/java/es/upv/mist/slicing/nodes/VariableAction.java index e13ca52..951f78b 100644 --- a/sdg-core/src/main/java/es/upv/mist/slicing/nodes/VariableAction.java +++ b/sdg-core/src/main/java/es/upv/mist/slicing/nodes/VariableAction.java @@ -388,8 +388,12 @@ public abstract class VariableAction { public boolean isTotallyDefinedMember(String member) { if (totallyDefinedMember == null) return false; - return totallyDefinedMember.equals(member) || member.startsWith(totallyDefinedMember) - || ObjectTree.removeRoot(member).startsWith(ObjectTree.removeRoot(totallyDefinedMember)); + if (totallyDefinedMember.equals(member)) + return true; + if (member.startsWith(totallyDefinedMember) + || ObjectTree.removeRoot(member).startsWith(ObjectTree.removeRoot(totallyDefinedMember))) + return hasTreeMember(member); + return false; } /** @see #expression */ 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 4454b72..aa90f93 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 @@ -243,7 +243,7 @@ public class VariableVisitor extends GraphNodeContentVisitor Date: Thu, 11 Mar 2021 10:39:27 +0100 Subject: [PATCH 34/60] update test cases and remove uncompilable code --- .../test/res/regression/carlos/TestJosep.java | 18 +++++++++--------- .../carlos/TestJosep.java.sdg.sliced | 1 - .../dinsa-tests/Josep2.java.sdg.sliced | 5 +---- 3 files changed, 10 insertions(+), 14 deletions(-) diff --git a/sdg-core/src/test/res/regression/carlos/TestJosep.java b/sdg-core/src/test/res/regression/carlos/TestJosep.java index 63eae2a..1f661e1 100644 --- a/sdg-core/src/test/res/regression/carlos/TestJosep.java +++ b/sdg-core/src/test/res/regression/carlos/TestJosep.java @@ -29,15 +29,15 @@ public class TestJosep { log(z); } - public void objetos() { - Object o = new Object(); - o.x = 10; - Object y = new Object(); - y.z = 210; - o = y; - log(o); - log(y); - } +// public void objetos() { +// Object o = new Object(); +// o.x = 10; +// Object y = new Object(); +// y.z = 210; +// o = y; +// log(o); +// log(y); +// } public static void log(Object o) {} } diff --git a/sdg-core/src/test/res/regression/carlos/TestJosep.java.sdg.sliced b/sdg-core/src/test/res/regression/carlos/TestJosep.java.sdg.sliced index 2393031..da134e6 100644 --- a/sdg-core/src/test/res/regression/carlos/TestJosep.java.sdg.sliced +++ b/sdg-core/src/test/res/regression/carlos/TestJosep.java.sdg.sliced @@ -1,7 +1,6 @@ public class TestJosep { public static void main(int y) { - int z = 0; log(z); } } 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 bf9ef9c..859cc9d 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 @@ -31,9 +31,6 @@ class Numeros { class GrandesNumeros extends Numeros { GrandesNumeros(double x) { - } - - int random() { - return haceFalta; + haceFalta = 0; } } -- GitLab From edd157c6fc9f0ef2d94b8754ffcf21130d53bc82 Mon Sep 17 00:00:00 2001 From: Carlos Galindo Date: Thu, 11 Mar 2021 11:03:30 +0100 Subject: [PATCH 35/60] fix! total definition dependence should not be generated for synthetic variables --- .../main/java/es/upv/mist/slicing/graphs/jsysdg/JSysPDG.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sdg-core/src/main/java/es/upv/mist/slicing/graphs/jsysdg/JSysPDG.java b/sdg-core/src/main/java/es/upv/mist/slicing/graphs/jsysdg/JSysPDG.java index 175dffd..e70833b 100644 --- a/sdg-core/src/main/java/es/upv/mist/slicing/graphs/jsysdg/JSysPDG.java +++ b/sdg-core/src/main/java/es/upv/mist/slicing/graphs/jsysdg/JSysPDG.java @@ -106,7 +106,7 @@ public class JSysPDG extends ESPDG { for (GraphNode node : vertexSet()) { for (VariableAction varAct : node.getVariableActions()) { // Total definition dependence - if (varAct.isUsage() || varAct.isDefinition()) { + if ((varAct.isUsage() || varAct.isDefinition()) && !varAct.isSynthetic()) { // root jSysCFG.findLastTotalDefinitionOf(varAct, "-root-").forEach(totalDef -> addTotalDefinitionDependencyArc(totalDef, varAct, "-root-")); // members -- GitLab From c19c0a411a6bbe79998ff22ac584cc483cfe6765 Mon Sep 17 00:00:00 2001 From: Carlos Galindo Date: Thu, 11 Mar 2021 12:46:58 +0100 Subject: [PATCH 36/60] Add interprocedural object arcs between formal/actual-out. Redesigned JSysCallConnector, to rely more on CallConnector. --- .../graphs/jsysdg/JSysCallConnector.java | 58 +++++++++---------- .../slicing/graphs/sdg/CallConnector.java | 22 +++++-- 2 files changed, 45 insertions(+), 35 deletions(-) diff --git a/sdg-core/src/main/java/es/upv/mist/slicing/graphs/jsysdg/JSysCallConnector.java b/sdg-core/src/main/java/es/upv/mist/slicing/graphs/jsysdg/JSysCallConnector.java index 462ac38..b16bea1 100644 --- a/sdg-core/src/main/java/es/upv/mist/slicing/graphs/jsysdg/JSysCallConnector.java +++ b/sdg-core/src/main/java/es/upv/mist/slicing/graphs/jsysdg/JSysCallConnector.java @@ -1,18 +1,17 @@ package es.upv.mist.slicing.graphs.jsysdg; -import com.github.javaparser.ast.body.CallableDeclaration; import es.upv.mist.slicing.graphs.CallGraph; import es.upv.mist.slicing.graphs.cfg.CFGBuilder; import es.upv.mist.slicing.graphs.exceptionsensitive.ExceptionSensitiveCallConnector; import es.upv.mist.slicing.nodes.GraphNode; import es.upv.mist.slicing.nodes.VariableAction; import es.upv.mist.slicing.nodes.io.ActualIONode; +import es.upv.mist.slicing.nodes.io.CallNode; import es.upv.mist.slicing.nodes.io.FormalIONode; import es.upv.mist.slicing.nodes.io.OutputNode; import es.upv.mist.slicing.utils.ASTUtils; import java.util.List; -import java.util.function.Consumer; public class JSysCallConnector extends ExceptionSensitiveCallConnector { public JSysCallConnector(JSysDG sdg) { @@ -25,41 +24,38 @@ public class JSysCallConnector extends ExceptionSensitiveCallConnector { } @Override - protected void connectActualIn(GraphNode> declaration, ActualIONode actualIn) { - sdg.outgoingEdgesOf(declaration).stream() - .map(sdg::getEdgeTarget) - .filter(FormalIONode.class::isInstance) - .map(FormalIONode.class::cast) - .filter(actualIn::matchesFormalIO) - .forEach(formalIn -> { - boolean primitive = !formalIn.getVariableName().equals("this") - && declaration.getAstNode().getParameterByName(formalIn.getVariableName()) - .orElseThrow().getType().isPrimitiveType(); - sdg.addParameterInOutArc(actualIn, formalIn); - if (!primitive) - connectObjectActualIn(actualIn, formalIn); - }); + protected void createActualInConnection(ActualIONode actualIn, FormalIONode formalIn) { + super.createActualInConnection(actualIn, formalIn); + if (formalIsObject(formalIn)) + connectObjectInterprocedurally(actualIn, formalIn); } - protected void connectObjectActualIn(GraphNode actualIn, GraphNode formalIn) { - List formalList = formalIn.getVariableActions(); - assert formalList.size() == 1; - VariableAction actualVar = actualIn.getLastVariableAction(); - VariableAction formalVar = formalList.get(0); - actualVar.applySDGTreeConnection((JSysDG) sdg, formalVar); + @Override + protected void createActualOutConnection(FormalIONode formalOut, ActualIONode actualOut) { + super.createActualOutConnection(formalOut, actualOut); + if (formalIsObject(formalOut)) + connectObjectInterprocedurally(formalOut, actualOut); + } + + protected boolean formalIsObject(FormalIONode formalNode) { + return formalNode.getVariableName().equals("this") + || !formalNode.getAstNode().getParameterByName(formalNode.getVariableName()) + .orElseThrow().getType().isPrimitiveType(); + } + + protected void connectObjectInterprocedurally(GraphNode source, GraphNode target) { + assert !target.getVariableActions().isEmpty(); + assert !source.getVariableActions().isEmpty(); + for (VariableAction targetVar : target.getVariableActions()) + source.getLastVariableAction().applySDGTreeConnection((JSysDG) sdg, targetVar); } @Override - protected void connectOutput(GraphNode> methodDeclaration, GraphNode callReturnNode) { - Consumer> action; - if (ASTUtils.declarationReturnIsObject(methodDeclaration.getAstNode())) - action = node -> connectObjectOutput(node, callReturnNode); + protected void createOutputReturnConnection(OutputNode outputNode, CallNode.Return callReturnNode) { + if (ASTUtils.declarationReturnIsObject(outputNode.getAstNode())) + connectObjectOutput(outputNode, callReturnNode); else - action = node -> sdg.addParameterInOutArc(node, callReturnNode); - sdg.outgoingEdgesOf(methodDeclaration).stream() - .map(sdg::getEdgeTarget) - .filter(OutputNode.class::isInstance) - .forEach(action); + super.createOutputReturnConnection(outputNode, callReturnNode); } protected void connectObjectOutput(GraphNode methodOutputNode, GraphNode callReturnNode) { diff --git a/sdg-core/src/main/java/es/upv/mist/slicing/graphs/sdg/CallConnector.java b/sdg-core/src/main/java/es/upv/mist/slicing/graphs/sdg/CallConnector.java index ca0a304..0a92bdb 100644 --- a/sdg-core/src/main/java/es/upv/mist/slicing/graphs/sdg/CallConnector.java +++ b/sdg-core/src/main/java/es/upv/mist/slicing/graphs/sdg/CallConnector.java @@ -67,6 +67,7 @@ public class CallConnector { sdg.outgoingEdgesOf(callNode).stream() .map(sdg::getEdgeTarget) .filter(CallNode.Return.class::isInstance) + .map(CallNode.Return.class::cast) .forEach(n -> connectOutput(declarationNode, n)); } @@ -77,7 +78,11 @@ public class CallConnector { .filter(FormalIONode.class::isInstance) .map(FormalIONode.class::cast) .filter(actualIn::matchesFormalIO) - .forEach(formalIn -> sdg.addParameterInOutArc(actualIn, formalIn)); + .forEach(formalIn -> createActualInConnection(actualIn, formalIn)); + } + + protected void createActualInConnection(ActualIONode actualIn, FormalIONode formalIn) { + sdg.addParameterInOutArc(actualIn, formalIn); } /** Connects an actual-out node to its formal-out counterpart. Arc in reverse direction. */ @@ -87,14 +92,23 @@ public class CallConnector { .filter(FormalIONode.class::isInstance) .map(FormalIONode.class::cast) .filter(actualOut::matchesFormalIO) - .forEach(formalOut -> sdg.addParameterInOutArc(formalOut, actualOut)); + .forEach(formalOut -> createActualOutConnection(formalOut, actualOut)); + } + + protected void createActualOutConnection(FormalIONode formalOut, ActualIONode actualOut) { + sdg.addParameterInOutArc(formalOut, actualOut); } /** Connects a method call return node to its method output counterpart. Arc in reverse direction. */ - protected void connectOutput(GraphNode> methodDeclaration, GraphNode callReturnNode) { + protected void connectOutput(GraphNode> methodDeclaration, CallNode.Return callReturnNode) { sdg.outgoingEdgesOf(methodDeclaration).stream() .map(sdg::getEdgeTarget) .filter(OutputNode.class::isInstance) - .forEach(n -> sdg.addParameterInOutArc(n, callReturnNode)); + .map(OutputNode.class::cast) + .forEach(n -> createOutputReturnConnection(n, callReturnNode)); + } + + protected void createOutputReturnConnection(OutputNode outputNode, CallNode.Return callReturnNode) { + sdg.addParameterInOutArc(outputNode, callReturnNode); } } -- GitLab From 26ceed484da191745b60feff51b9248d2fabceae Mon Sep 17 00:00:00 2001 From: Carlos Galindo Date: Thu, 11 Mar 2021 13:12:25 +0100 Subject: [PATCH 37/60] Always insert 'return this;' at the end of constructors. --- .../java/es/upv/mist/slicing/graphs/jsysdg/JSysCFG.java | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/sdg-core/src/main/java/es/upv/mist/slicing/graphs/jsysdg/JSysCFG.java b/sdg-core/src/main/java/es/upv/mist/slicing/graphs/jsysdg/JSysCFG.java index ef7d6ab..be8cc09 100644 --- a/sdg-core/src/main/java/es/upv/mist/slicing/graphs/jsysdg/JSysCFG.java +++ b/sdg-core/src/main/java/es/upv/mist/slicing/graphs/jsysdg/JSysCFG.java @@ -196,12 +196,13 @@ public class JSysCFG extends ESCFG { // Insert call to super() if it is implicit. if (!ASTUtils.constructorHasExplicitConstructorInvocation(n)){ var superCall = new ExplicitConstructorInvocationStmt(null, null, false, null, new NodeList<>()); - var returnThis = new ReturnStmt(new ThisExpr()); methodInsertedInstructions.add(superCall); - methodInsertedInstructions.add(returnThis); n.getBody().addStatement(0, superCall); - n.getBody().addStatement(returnThis); } + // insert return this; + var returnThis = new ReturnStmt(new ThisExpr()); + methodInsertedInstructions.add(returnThis); + n.getBody().addStatement(returnThis); // Perform the same task as previous graphs. super.visit(n, arg); // Convert enter/exit nodes to implicit if appropriate -- GitLab From ff2dcf4e9028d753792a39b130e2f3568b1c5029 Mon Sep 17 00:00:00 2001 From: Carlos Galindo Date: Thu, 11 Mar 2021 13:25:42 +0100 Subject: [PATCH 38/60] Temporarily modify existing return statements in constructors to return 'this'. --- .../mist/slicing/graphs/jsysdg/JSysCFG.java | 21 +++++++++++++++++-- 1 file changed, 19 insertions(+), 2 deletions(-) diff --git a/sdg-core/src/main/java/es/upv/mist/slicing/graphs/jsysdg/JSysCFG.java b/sdg-core/src/main/java/es/upv/mist/slicing/graphs/jsysdg/JSysCFG.java index be8cc09..9458a66 100644 --- a/sdg-core/src/main/java/es/upv/mist/slicing/graphs/jsysdg/JSysCFG.java +++ b/sdg-core/src/main/java/es/upv/mist/slicing/graphs/jsysdg/JSysCFG.java @@ -9,6 +9,8 @@ import com.github.javaparser.ast.expr.Expression; import com.github.javaparser.ast.expr.ThisExpr; import com.github.javaparser.ast.stmt.ExplicitConstructorInvocationStmt; import com.github.javaparser.ast.stmt.ReturnStmt; +import com.github.javaparser.ast.visitor.ModifierVisitor; +import com.github.javaparser.ast.visitor.Visitable; import es.upv.mist.slicing.arcs.Arc; import es.upv.mist.slicing.graphs.ClassGraph; import es.upv.mist.slicing.graphs.ExpressionObjectTreeFinder; @@ -25,6 +27,7 @@ import java.util.LinkedList; import java.util.List; import java.util.Set; import java.util.function.Predicate; +import java.util.function.Supplier; import java.util.stream.Collectors; import java.util.stream.Stream; @@ -199,12 +202,16 @@ public class JSysCFG extends ESCFG { methodInsertedInstructions.add(superCall); n.getBody().addStatement(0, superCall); } - // insert return this; - var returnThis = new ReturnStmt(new ThisExpr()); + // insert return this; at the end of the constructor + var returnThis = new ReturnStmt(); methodInsertedInstructions.add(returnThis); n.getBody().addStatement(returnThis); + // modify every return statement so that it returns 'this' + modifyAllReturnExpr(n, ThisExpr::new); // Perform the same task as previous graphs. super.visit(n, arg); + // restore return statements + modifyAllReturnExpr(n, () -> null); // Convert enter/exit nodes to implicit if appropriate if (implicitDeclaration) { getRootNode().markAsImplicit(); @@ -214,6 +221,16 @@ public class JSysCFG extends ESCFG { } } + protected void modifyAllReturnExpr(Node node, Supplier expressionSupplier) { + node.accept(new ModifierVisitor() { + @Override + public Visitable visit(ReturnStmt n, Void arg) { + n.setExpression(expressionSupplier.get()); + return n; + } + }, null); + } + @Override protected void addMethodOutput(CallableDeclaration callableDeclaration, GraphNode exit) { super.addMethodOutput(callableDeclaration, exit); -- GitLab From 55cb8d0d882ac8cb3947525c10d084a57150a0ea Mon Sep 17 00:00:00 2001 From: Carlos Galindo Date: Thu, 11 Mar 2021 13:52:43 +0100 Subject: [PATCH 39/60] Improved accuracy of isTotallyDefinedMember(String) --- .../main/java/es/upv/mist/slicing/nodes/VariableAction.java | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/sdg-core/src/main/java/es/upv/mist/slicing/nodes/VariableAction.java b/sdg-core/src/main/java/es/upv/mist/slicing/nodes/VariableAction.java index 951f78b..1ca946e 100644 --- a/sdg-core/src/main/java/es/upv/mist/slicing/nodes/VariableAction.java +++ b/sdg-core/src/main/java/es/upv/mist/slicing/nodes/VariableAction.java @@ -156,6 +156,8 @@ public abstract class VariableAction { // ====================================================== public boolean hasTreeMember(String member) { + if (member.isEmpty()) + return hasObjectTree(); if (!hasObjectTree()) return false; return getObjectTree().hasMember(member); @@ -392,7 +394,7 @@ public abstract class VariableAction { return true; if (member.startsWith(totallyDefinedMember) || ObjectTree.removeRoot(member).startsWith(ObjectTree.removeRoot(totallyDefinedMember))) - return hasTreeMember(member); + return ObjectTree.removeRoot(member).isEmpty() || hasTreeMember(member); return false; } -- GitLab From 1fe56b0373ad6021f837e619d32685d45ad6fbcc Mon Sep 17 00:00:00 2001 From: Carlos Galindo Date: Thu, 11 Mar 2021 13:55:24 +0100 Subject: [PATCH 40/60] Total definition dependence now allowed for usages of synthetic variables. --- .../main/java/es/upv/mist/slicing/graphs/jsysdg/JSysPDG.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sdg-core/src/main/java/es/upv/mist/slicing/graphs/jsysdg/JSysPDG.java b/sdg-core/src/main/java/es/upv/mist/slicing/graphs/jsysdg/JSysPDG.java index e70833b..3be25f3 100644 --- a/sdg-core/src/main/java/es/upv/mist/slicing/graphs/jsysdg/JSysPDG.java +++ b/sdg-core/src/main/java/es/upv/mist/slicing/graphs/jsysdg/JSysPDG.java @@ -106,7 +106,7 @@ public class JSysPDG extends ESPDG { for (GraphNode node : vertexSet()) { for (VariableAction varAct : node.getVariableActions()) { // Total definition dependence - if ((varAct.isUsage() || varAct.isDefinition()) && !varAct.isSynthetic()) { + if (varAct.isUsage() || (varAct.isDefinition() && !varAct.isSynthetic())) { // root jSysCFG.findLastTotalDefinitionOf(varAct, "-root-").forEach(totalDef -> addTotalDefinitionDependencyArc(totalDef, varAct, "-root-")); // members -- GitLab From 5be4499711ae1bc205fe0acc619526adef120cd4 Mon Sep 17 00:00:00 2001 From: Carlos Galindo Date: Thu, 11 Mar 2021 14:02:51 +0100 Subject: [PATCH 41/60] Total definition dependence should be skipped for primitive actions --- .../main/java/es/upv/mist/slicing/graphs/jsysdg/JSysPDG.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sdg-core/src/main/java/es/upv/mist/slicing/graphs/jsysdg/JSysPDG.java b/sdg-core/src/main/java/es/upv/mist/slicing/graphs/jsysdg/JSysPDG.java index 3be25f3..3ab15ca 100644 --- a/sdg-core/src/main/java/es/upv/mist/slicing/graphs/jsysdg/JSysPDG.java +++ b/sdg-core/src/main/java/es/upv/mist/slicing/graphs/jsysdg/JSysPDG.java @@ -106,7 +106,7 @@ public class JSysPDG extends ESPDG { for (GraphNode node : vertexSet()) { for (VariableAction varAct : node.getVariableActions()) { // Total definition dependence - if (varAct.isUsage() || (varAct.isDefinition() && !varAct.isSynthetic())) { + if (!varAct.isPrimitive() && (varAct.isUsage() || (varAct.isDefinition() && !varAct.isSynthetic()))) { // root jSysCFG.findLastTotalDefinitionOf(varAct, "-root-").forEach(totalDef -> addTotalDefinitionDependencyArc(totalDef, varAct, "-root-")); // members -- GitLab From a185d54e47ddc6f1c1ef4a0486a9f78739714b35 Mon Sep 17 00:00:00 2001 From: Carlos Galindo Date: Thu, 11 Mar 2021 14:03:10 +0100 Subject: [PATCH 42/60] Update tests --- .../test/res/regression/dinsa-tests/Josep6.java.sdg.sliced | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/sdg-core/src/test/res/regression/dinsa-tests/Josep6.java.sdg.sliced b/sdg-core/src/test/res/regression/dinsa-tests/Josep6.java.sdg.sliced index 90e514e..3440484 100644 --- a/sdg-core/src/test/res/regression/dinsa-tests/Josep6.java.sdg.sliced +++ b/sdg-core/src/test/res/regression/dinsa-tests/Josep6.java.sdg.sliced @@ -1,9 +1,6 @@ class A { - int xA = 0; - A(int newx) { - xA = newx; } public static void main(String[] args) { @@ -16,6 +13,8 @@ class A { class B extends A { + int xB = 5; + B(double valor) { super((int) valor); } -- GitLab From 582047e09804963c5f7ea9ce8a1c6e0c715614d8 Mon Sep 17 00:00:00 2001 From: Carlos Galindo Date: Thu, 11 Mar 2021 15:24:11 +0100 Subject: [PATCH 43/60] Removed expired TODO items --- .../es/upv/mist/slicing/graphs/exceptionsensitive/ESCFG.java | 2 +- .../main/java/es/upv/mist/slicing/graphs/jsysdg/JSysCFG.java | 2 +- .../main/java/es/upv/mist/slicing/graphs/jsysdg/JSysPDG.java | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/sdg-core/src/main/java/es/upv/mist/slicing/graphs/exceptionsensitive/ESCFG.java b/sdg-core/src/main/java/es/upv/mist/slicing/graphs/exceptionsensitive/ESCFG.java index 4d2193a..be16aa3 100644 --- a/sdg-core/src/main/java/es/upv/mist/slicing/graphs/exceptionsensitive/ESCFG.java +++ b/sdg-core/src/main/java/es/upv/mist/slicing/graphs/exceptionsensitive/ESCFG.java @@ -285,7 +285,7 @@ public class ESCFG extends ACFG { for (ResolvedType type : resolved.getSpecifiedExceptions()) { hangingNodes.add(stmtNode); ExceptionReturnNode exceptionReturn = addExceptionReturnNode(call, type); - exceptionReturn.addVADefineActiveException(null); // TODO: improve initializer + exceptionReturn.addVADefineActiveException(null); populateExceptionSourceMap(new ExceptionSource(exceptionReturn, type)); returnNodes.add(exceptionReturn); connectTo(exceptionReturn); diff --git a/sdg-core/src/main/java/es/upv/mist/slicing/graphs/jsysdg/JSysCFG.java b/sdg-core/src/main/java/es/upv/mist/slicing/graphs/jsysdg/JSysCFG.java index 9458a66..265fc91 100644 --- a/sdg-core/src/main/java/es/upv/mist/slicing/graphs/jsysdg/JSysCFG.java +++ b/sdg-core/src/main/java/es/upv/mist/slicing/graphs/jsysdg/JSysCFG.java @@ -110,7 +110,7 @@ public class JSysCFG extends ESCFG { * given definition, it will return a list with only the given definition. */ public List findNextObjectDefinitionsFor(VariableAction definition, String member) { if (!this.containsVertex(definition.getGraphNode())) - throw new NodeNotFoundException(definition.getGraphNode(), this); // TODO: al crear los root/resumen, las movable no se ponen en el movable + throw new NodeNotFoundException(definition.getGraphNode(), this); if (definition.hasTreeMember(member)) return List.of(definition); List list = new LinkedList<>(); diff --git a/sdg-core/src/main/java/es/upv/mist/slicing/graphs/jsysdg/JSysPDG.java b/sdg-core/src/main/java/es/upv/mist/slicing/graphs/jsysdg/JSysPDG.java index 3ab15ca..aae37ce 100644 --- a/sdg-core/src/main/java/es/upv/mist/slicing/graphs/jsysdg/JSysPDG.java +++ b/sdg-core/src/main/java/es/upv/mist/slicing/graphs/jsysdg/JSysPDG.java @@ -172,7 +172,7 @@ public class JSysPDG extends ESPDG { continue; } GraphNode parentNode; // node that represents the root of the object tree - if (va instanceof VariableAction.Movable) { // TODO: there are Movables with multiple VA per movable!!! + if (va instanceof VariableAction.Movable) { VariableAction.Movable movable = (VariableAction.Movable) va; addVertex(movable.getRealNode()); connectRealNode(node, callNodeStack.peek(), movable.getRealNode()); -- GitLab From 14b25ce747a0b2d7acec3677e3636ae2d13e6760 Mon Sep 17 00:00:00 2001 From: Carlos Galindo Date: Thu, 11 Mar 2021 15:32:16 +0100 Subject: [PATCH 44/60] Do not generate trees in actual-in nodes that represent integer parameters. --- .../graphs/sdg/InterproceduralUsageFinder.java | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) 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 b6d3570..78fac15 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 @@ -41,12 +41,14 @@ public class InterproceduralUsageFinder extends InterproceduralActionFinder edge, Usage use) { GraphNode graphNode = edge.getGraphNode(); if (use.isParameter()) { - ActualIONode actualIn = locateActualInNode(edge, use.getName()); - Definition def = new Definition(VariableAction.DeclarationType.SYNTHETIC, "-arg-in-", graphNode, (ObjectTree) use.getObjectTree().clone()); - Movable movDef = new Movable(def, actualIn); - graphNode.addVariableActionAfterLastMatchingRealNode(movDef, actualIn); - ExpressionObjectTreeFinder finder = new ExpressionObjectTreeFinder(graphNode); - finder.locateAndMarkTransferenceToRoot(actualIn.getArgument(), def); + if (!use.isPrimitive()) { + ActualIONode actualIn = locateActualInNode(edge, use.getName()); + Definition def = new Definition(VariableAction.DeclarationType.SYNTHETIC, "-arg-in-", graphNode, (ObjectTree) use.getObjectTree().clone()); + Movable movDef = new Movable(def, actualIn); + graphNode.addVariableActionAfterLastMatchingRealNode(movDef, actualIn); + ExpressionObjectTreeFinder finder = new ExpressionObjectTreeFinder(graphNode); + finder.locateAndMarkTransferenceToRoot(actualIn.getArgument(), def); + } } else if (use.isField()) { if (use.isStatic()) { // Known limitation: static fields -- GitLab From 03651a82b86abc0ce6b85c7a6197d151c3557eca Mon Sep 17 00:00:00 2001 From: Carlos Galindo Date: Thu, 11 Mar 2021 16:55:54 +0100 Subject: [PATCH 45/60] When searching for interprocedural actions, don't generate trees for variables that don't have them. --- .../mist/slicing/graphs/sdg/InterproceduralActionFinder.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sdg-core/src/main/java/es/upv/mist/slicing/graphs/sdg/InterproceduralActionFinder.java b/sdg-core/src/main/java/es/upv/mist/slicing/graphs/sdg/InterproceduralActionFinder.java index 5860593..d0c1daa 100644 --- a/sdg-core/src/main/java/es/upv/mist/slicing/graphs/sdg/InterproceduralActionFinder.java +++ b/sdg-core/src/main/java/es/upv/mist/slicing/graphs/sdg/InterproceduralActionFinder.java @@ -172,7 +172,7 @@ public abstract class InterproceduralActionFinder exte Set set = new HashSet<>(); for (Iterator it = filteredStream.iterator(); it.hasNext(); ) { A a = it.next(); - if (set.contains(a)) { + if (set.contains(a) && a.hasObjectTree()) { for (A aFromSet : set) if (aFromSet.hashCode() == a.hashCode() && Objects.equals(aFromSet, a)) aFromSet.getObjectTree().addAll(a.getObjectTree()); -- GitLab From cf735a3f2f3d5849d8a69d8eefa58a1c51777031 Mon Sep 17 00:00:00 2001 From: Carlos Galindo Date: Thu, 11 Mar 2021 17:00:41 +0100 Subject: [PATCH 46/60] Update tests (worse due to polymorphism, better when detecting fields) --- .../test/res/regression/review-07-2020/P5.java.sdg.sliced | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) 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 0740208..859cc9d 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 @@ -21,7 +21,7 @@ public class Josep2 { class Numeros { - int noHaceFalta = 1; + int haceFalta = 1; int random() { return haceFalta; @@ -31,9 +31,6 @@ class Numeros { class GrandesNumeros extends Numeros { GrandesNumeros(double x) { - } - - int random() { - return haceFalta; + haceFalta = 0; } } -- GitLab From ee4949815a63dd94bd3724bb6030a5ea49e6b560 Mon Sep 17 00:00:00 2001 From: Carlos Galindo Date: Thu, 11 Mar 2021 17:08:08 +0100 Subject: [PATCH 47/60] Simple array handling, equivalent to old handling as primitives. --- .../graphs/ExpressionObjectTreeFinder.java | 7 ++++- .../slicing/nodes/ObjectTreeConnection.java | 4 +-- .../mist/slicing/nodes/ValueConnection.java | 28 +++++++++++++++++++ .../mist/slicing/nodes/VariableAction.java | 12 ++++++-- .../mist/slicing/nodes/VariableVisitor.java | 28 ++++++++++++++++--- 5 files changed, 70 insertions(+), 9 deletions(-) create mode 100644 sdg-core/src/main/java/es/upv/mist/slicing/nodes/ValueConnection.java 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 d46392c..4a29add 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 @@ -59,6 +59,11 @@ public class ExpressionObjectTreeFinder { .forEach(pair -> markTransference(pair, assignTarget, targetMember)); } + public void handleArrayAssignExpr(AssignExpr assignExpr) { + locateExpressionResultTrees(assignExpr.getValue()) + .forEach(pair -> pair.a.setPDGValueConnection(pair.b)); + } + public void locateAndMarkTransferenceToRoot(Expression expr, int index) { List list = graphNode.getVariableActions(); if (index < 0) @@ -77,7 +82,7 @@ public class ExpressionObjectTreeFinder { expression.accept(new VoidVisitorAdapter() { @Override public void visit(ArrayAccessExpr n, String arg) { - throw new UnsupportedOperationException("Array accesses are not supported as argument for return, call scope or argument. Please, pre-process your graph or use the EDG."); + n.getName().accept(this, arg); } @Override diff --git a/sdg-core/src/main/java/es/upv/mist/slicing/nodes/ObjectTreeConnection.java b/sdg-core/src/main/java/es/upv/mist/slicing/nodes/ObjectTreeConnection.java index 788eb53..43b1836 100644 --- a/sdg-core/src/main/java/es/upv/mist/slicing/nodes/ObjectTreeConnection.java +++ b/sdg-core/src/main/java/es/upv/mist/slicing/nodes/ObjectTreeConnection.java @@ -11,7 +11,7 @@ import es.upv.mist.slicing.nodes.oo.MemberNode; import java.util.function.Supplier; -class ObjectTreeConnection { +class ObjectTreeConnection implements VariableAction.PDGConnection { protected final VariableAction sourceAction; protected final VariableAction targetAction; @@ -34,7 +34,7 @@ class ObjectTreeConnection { } } - public void applyPDG(JSysPDG graph) { + public void apply(JSysPDG graph) { if (!applied) { connectTrees(graph, FlowDependencyArc::new, ObjectFlowDependencyArc::new); applied = true; diff --git a/sdg-core/src/main/java/es/upv/mist/slicing/nodes/ValueConnection.java b/sdg-core/src/main/java/es/upv/mist/slicing/nodes/ValueConnection.java new file mode 100644 index 0000000..171f95c --- /dev/null +++ b/sdg-core/src/main/java/es/upv/mist/slicing/nodes/ValueConnection.java @@ -0,0 +1,28 @@ +package es.upv.mist.slicing.nodes; + +import es.upv.mist.slicing.arcs.pdg.FlowDependencyArc; +import es.upv.mist.slicing.graphs.jsysdg.JSysPDG; + +public class ValueConnection implements VariableAction.PDGConnection { + protected final VariableAction action; + protected final String member; + + public ValueConnection(VariableAction action, String member) { + this.action = action; + if (member.isEmpty()) + this.member = "-root-"; + else + this.member = "-root-." + member; + } + + @Override + public void apply(JSysPDG graph) { + GraphNode statementNode; + if (action instanceof VariableAction.Movable) + statementNode = ((VariableAction.Movable) action).getRealNode(); + else + statementNode = action.getGraphNode(); + if (action.hasTreeMember(member)) + graph.addEdge(action.getObjectTree().getNodeFor(member), statementNode, new FlowDependencyArc()); + } +} diff --git a/sdg-core/src/main/java/es/upv/mist/slicing/nodes/VariableAction.java b/sdg-core/src/main/java/es/upv/mist/slicing/nodes/VariableAction.java index 1ca946e..1ff490e 100644 --- a/sdg-core/src/main/java/es/upv/mist/slicing/nodes/VariableAction.java +++ b/sdg-core/src/main/java/es/upv/mist/slicing/nodes/VariableAction.java @@ -74,7 +74,7 @@ public abstract class VariableAction { * The variable action that contains the tree we must connect to in the PDG. * The string, or member where the tree connection must start (in PDG). E.g.: our tree is "a.b.c" and this variable is "a", * the members "a.b" and "a.b.c" will be connected to "b" and "b.c" in treeConnectionTarget's tree.. */ - protected final List pdgTreeConnections = new LinkedList<>(); + protected final List pdgTreeConnections = new LinkedList<>(); private VariableAction(DeclarationType declarationType, String name, GraphNode graphNode) { this(declarationType, name, graphNode, null); @@ -177,8 +177,12 @@ public abstract class VariableAction { pdgTreeConnections.add(new ObjectTreeConnection(this, targetAction, sourcePrefixWithoutRoot, targetPrefixWithoutRoot)); } + public void setPDGValueConnection(String member) { + pdgTreeConnections.add(new ValueConnection(this, member)); + } + public void applyPDGTreeConnections(JSysPDG pdg) { - pdgTreeConnections.forEach(c -> c.applyPDG(pdg)); + pdgTreeConnections.forEach(c -> c.apply(pdg)); } public void applySDGTreeConnection(JSysDG sdg, VariableAction targetAction) { @@ -549,4 +553,8 @@ public abstract class VariableAction { return Objects.hash(super.hashCode(), realNode, inner); } } + + public interface PDGConnection { + void apply(JSysPDG graph); + } } 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 aa90f93..10a5986 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 @@ -160,6 +160,18 @@ public class VariableVisitor extends GraphNodeContentVisitor realNameWithoutRootList = new LinkedList<>(); + List foundArray = new LinkedList<>(); n.getTarget().accept(new VoidVisitorAdapter() { @Override public void visit(NameExpr nameExpr, Void arg) { @@ -280,13 +293,18 @@ public class VariableVisitor extends GraphNodeContentVisitor Date: Thu, 11 Mar 2021 17:31:48 +0100 Subject: [PATCH 48/60] fix! condition to copy trees in ExpressionObjectTreeFinder --- .../es/upv/mist/slicing/graphs/ExpressionObjectTreeFinder.java | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) 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 4a29add..f88a829 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 @@ -168,8 +168,7 @@ public class ExpressionObjectTreeFinder { protected void markTransference(Pair sourcePair, VariableAction targetAction, String targetMember) { VariableAction sourceAction = sourcePair.a; String sourceMember = sourcePair.b; - if (targetAction.hasObjectTree() && - (!sourceAction.hasObjectTree() || !sourceAction.getObjectTree().isLeaf(sourceMember))) + if (targetAction.hasObjectTree()) ObjectTree.copyTargetTreeToSource(sourceAction.getObjectTree(), targetAction.getObjectTree(), sourceMember, targetMember); sourceAction.setPDGTreeConnectionTo(targetAction, sourceMember, targetMember); } -- GitLab From 70d8a3ae89bb8c91be555db81d818de19c6e2f9d Mon Sep 17 00:00:00 2001 From: Carlos Galindo Date: Thu, 11 Mar 2021 17:32:15 +0100 Subject: [PATCH 49/60] update tests (regression) this test requires improved variable targeting --- .../regression/review-07-2020/P1.java.sdg.sliced | 16 ---------------- 1 file changed, 16 deletions(-) diff --git a/sdg-core/src/test/res/regression/review-07-2020/P1.java.sdg.sliced b/sdg-core/src/test/res/regression/review-07-2020/P1.java.sdg.sliced index 2785ae1..fdacf0a 100644 --- a/sdg-core/src/test/res/regression/review-07-2020/P1.java.sdg.sliced +++ b/sdg-core/src/test/res/regression/review-07-2020/P1.java.sdg.sliced @@ -3,22 +3,6 @@ class FiguresGroup { public Figure greatestFigure() { if (numF == 0) return null; - Figure f = figuresList[0]; - double a = figuresList[0].area(); - for (int i = 1; i < numF; i++) { - double b = figuresList[i].area(); - if (a < b) { - f = figuresList[i]; - a = b; - } - } return f; } } - -class Figure { - - int area() { - return 5; - } -} -- GitLab From 00f16679b7712119a683f70680429602f1ffcbf7 Mon Sep 17 00:00:00 2001 From: Carlos Galindo Date: Thu, 11 Mar 2021 17:33:05 +0100 Subject: [PATCH 50/60] simplify output action generation in variable visitor --- .../mist/slicing/nodes/VariableVisitor.java | 18 +++++------------- 1 file changed, 5 insertions(+), 13 deletions(-) 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 10a5986..37b90fc 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 @@ -461,24 +461,16 @@ public class VariableVisitor extends GraphNodeContentVisitor (ObjectTree) tree.clone()).orElse(null)); def.setTotallyDefinedMember("-root-"); var defMov = new VariableAction.Movable(def, CallNode.Return.create(call)); graphNode.addVariableAction(defMov); // The container of the call uses -output-, unless the call is wrapped in an ExpressionStmt Optional parentNode = ((Node) call).getParentNode(); - if (parentNode.isEmpty() || !(parentNode.get() instanceof ExpressionStmt)) { - VariableAction.Usage use; - if (fields.isPresent()) - use = new VariableAction.Usage(SYNTHETIC, VARIABLE_NAME_OUTPUT, graphNode, (ObjectTree) fields.get().clone()); - else - use = new VariableAction.Usage(SYNTHETIC, VARIABLE_NAME_OUTPUT, graphNode); - graphNode.addVariableAction(use); - } + if (parentNode.isEmpty() || !(parentNode.get() instanceof ExpressionStmt)) + graphNode.addVariableAction(new VariableAction.Usage(SYNTHETIC, VARIABLE_NAME_OUTPUT, graphNode, + fields.map(tree -> (ObjectTree) tree.clone()).orElse(null))); } protected Optional getFieldsForReturn(Resolvable call) { -- GitLab From 079ecc495c8be65602f61575c5af482ae829983b Mon Sep 17 00:00:00 2001 From: Carlos Galindo Date: Thu, 11 Mar 2021 18:09:10 +0100 Subject: [PATCH 51/60] SlicingCriterion can select several nodes from the graph instead of a single one. --- .../es/upv/mist/slicing/cli/PHPSlice.java | 4 +- .../es/upv/mist/slicing/cli/SlicedSDGLog.java | 14 ++++-- .../es/upv/mist/slicing/graphs/sdg/SDG.java | 14 +++--- .../slicing/ClassicSlicingAlgorithm.java | 4 +- .../ExceptionSensitiveSlicingAlgorithm.java | 10 ++--- .../slicing/slicing/GraphNodeCriterion.java | 33 -------------- .../slicing/JSysDGSlicingAlgorithm.java | 2 +- .../slicing/slicing/LineNumberCriterion.java | 45 ++++++------------- .../slicing/NodeIdSlicingCriterion.java | 33 -------------- .../PseudoPredicateSlicingAlgorithm.java | 10 +++-- .../slicing/slicing/SlicingAlgorithm.java | 4 +- .../slicing/slicing/SlicingCriterion.java | 35 +++++---------- 12 files changed, 61 insertions(+), 147 deletions(-) delete mode 100644 sdg-core/src/main/java/es/upv/mist/slicing/slicing/GraphNodeCriterion.java delete mode 100644 sdg-core/src/main/java/es/upv/mist/slicing/slicing/NodeIdSlicingCriterion.java diff --git a/sdg-cli/src/main/java/es/upv/mist/slicing/cli/PHPSlice.java b/sdg-cli/src/main/java/es/upv/mist/slicing/cli/PHPSlice.java index 6a4b7f4..1c5bd54 100644 --- a/sdg-cli/src/main/java/es/upv/mist/slicing/cli/PHPSlice.java +++ b/sdg-cli/src/main/java/es/upv/mist/slicing/cli/PHPSlice.java @@ -10,7 +10,6 @@ import es.upv.mist.slicing.graphs.cfg.CFG; import es.upv.mist.slicing.graphs.exceptionsensitive.ESSDG; import es.upv.mist.slicing.graphs.sdg.SDG; import es.upv.mist.slicing.slicing.FileLineSlicingCriterion; -import es.upv.mist.slicing.slicing.NodeIdSlicingCriterion; import es.upv.mist.slicing.slicing.Slice; import es.upv.mist.slicing.slicing.SlicingCriterion; import es.upv.mist.slicing.utils.StaticTypeSolver; @@ -20,6 +19,7 @@ import java.io.File; import java.io.FileNotFoundException; import java.io.IOException; import java.io.PrintWriter; +import java.util.Set; public class PHPSlice { protected static final Options OPTIONS = new Options(); @@ -108,7 +108,7 @@ public class PHPSlice { } sdg.build(units); - SlicingCriterion sc = new NodeIdSlicingCriterion(0, ""); + SlicingCriterion sc = graph -> Set.of(graph.findNodeById(0).orElseThrow()); Slice slice = new Slice(); if (scId != 0) { // Slice the SDG diff --git a/sdg-cli/src/main/java/es/upv/mist/slicing/cli/SlicedSDGLog.java b/sdg-cli/src/main/java/es/upv/mist/slicing/cli/SlicedSDGLog.java index a8af575..09f5562 100644 --- a/sdg-cli/src/main/java/es/upv/mist/slicing/cli/SlicedSDGLog.java +++ b/sdg-cli/src/main/java/es/upv/mist/slicing/cli/SlicedSDGLog.java @@ -11,11 +11,13 @@ import org.jgrapht.nio.dot.DOTExporter; import java.util.HashMap; import java.util.Map; +import java.util.NoSuchElementException; +import java.util.Set; /** Utility to export a sliced SDG in dot and show the slices and slicing criterion. */ public class SlicedSDGLog extends SDGLog { protected final Slice slice; - protected final GraphNode sc; + protected final Set> sc; public SlicedSDGLog(SDG graph, Slice slice) { this(graph, slice, null); @@ -24,7 +26,11 @@ public class SlicedSDGLog extends SDGLog { public SlicedSDGLog(SDG graph, Slice slice, SlicingCriterion sc) { super(graph); this.slice = slice; - this.sc = sc == null ? null : sc.findNode(graph).orElse(null); + Set> set = null; + try { + set = sc.findNode(graph); + } catch (NullPointerException | NoSuchElementException ignored) {} + this.sc = set; } @Override @@ -38,13 +44,13 @@ public class SlicedSDGLog extends SDGLog { protected Map vertexAttributes(GraphNode node) { Map map = new HashMap<>(); - if (slice.contains(node) && node.equals(sc)) + if (slice.contains(node) && sc.contains(node)) map.put("style", DefaultAttribute.createAttribute("filled,bold")); else if (slice.contains(node) && node.isImplicitInstruction()) map.put("style", DefaultAttribute.createAttribute("filled,dashed")); else if (slice.contains(node)) map.put("style", DefaultAttribute.createAttribute("filled")); - else if (node.equals(sc)) + else if (sc.contains(node)) map.put("style", DefaultAttribute.createAttribute("bold")); else if (node.isImplicitInstruction()) map.put("style", DefaultAttribute.createAttribute("dashed")); diff --git a/sdg-core/src/main/java/es/upv/mist/slicing/graphs/sdg/SDG.java b/sdg-core/src/main/java/es/upv/mist/slicing/graphs/sdg/SDG.java index 8b1f488..008169d 100644 --- a/sdg-core/src/main/java/es/upv/mist/slicing/graphs/sdg/SDG.java +++ b/sdg-core/src/main/java/es/upv/mist/slicing/graphs/sdg/SDG.java @@ -26,7 +26,8 @@ import es.upv.mist.slicing.utils.ASTUtils; import java.util.Collection; import java.util.Map; -import java.util.Optional; +import java.util.NoSuchElementException; +import java.util.Set; /** * The System Dependence Graph represents the statements of a program in @@ -53,10 +54,13 @@ public class SDG extends Graph implements Sliceable, Buildable> optSlicingNode = slicingCriterion.findNode(this); - if (optSlicingNode.isEmpty()) - throw new IllegalArgumentException("Could not locate the slicing criterion in the SDG"); - return createSlicingAlgorithm().traverse(optSlicingNode.get()); + Set> slicingCriterionNodes; + try { + slicingCriterionNodes = slicingCriterion.findNode(this); + } catch (NoSuchElementException e) { + throw new IllegalArgumentException("Could not locate the slicing criterion " + slicingCriterion); + } + return createSlicingAlgorithm().traverse(slicingCriterionNodes); } protected SlicingAlgorithm createSlicingAlgorithm() { diff --git a/sdg-core/src/main/java/es/upv/mist/slicing/slicing/ClassicSlicingAlgorithm.java b/sdg-core/src/main/java/es/upv/mist/slicing/slicing/ClassicSlicingAlgorithm.java index 7d4ed15..a8af80a 100644 --- a/sdg-core/src/main/java/es/upv/mist/slicing/slicing/ClassicSlicingAlgorithm.java +++ b/sdg-core/src/main/java/es/upv/mist/slicing/slicing/ClassicSlicingAlgorithm.java @@ -29,9 +29,9 @@ public class ClassicSlicingAlgorithm implements SlicingAlgorithm { } @Override - public Slice traverse(GraphNode slicingCriterion) { + public Slice traverse(Set> slicingCriterion) { Slice slice = new Slice(); - slice.add(slicingCriterion); + slice.addAll(slicingCriterion); pass(slice, this::ignorePass1); pass(slice, this::ignorePass2); return slice; diff --git a/sdg-core/src/main/java/es/upv/mist/slicing/slicing/ExceptionSensitiveSlicingAlgorithm.java b/sdg-core/src/main/java/es/upv/mist/slicing/slicing/ExceptionSensitiveSlicingAlgorithm.java index 88f2dcd..c8d382e 100644 --- a/sdg-core/src/main/java/es/upv/mist/slicing/slicing/ExceptionSensitiveSlicingAlgorithm.java +++ b/sdg-core/src/main/java/es/upv/mist/slicing/slicing/ExceptionSensitiveSlicingAlgorithm.java @@ -35,7 +35,7 @@ public class ExceptionSensitiveSlicingAlgorithm implements SlicingAlgorithm { protected static final Predicate SDG_PASS_2 = Arc::isInterproceduralInputArc; protected final ESSDG graph; - protected GraphNode slicingCriterion; + protected Set> slicingCriterion; /** Set of the arcs that have been traversed in the slicing process. */ protected final Set traversedArcSet = new HashSet<>(); @@ -47,10 +47,10 @@ public class ExceptionSensitiveSlicingAlgorithm implements SlicingAlgorithm { } @Override - public Slice traverse(GraphNode slicingCriterion) { + public Slice traverse(Set> slicingCriterion) { this.slicingCriterion = slicingCriterion; Slice slice = new Slice(); - slice.add(slicingCriterion); + slice.addAll(slicingCriterion); pass(slice, SDG_PASS_1.or(this::commonIgnoreConditions)); pass(slice, SDG_PASS_2.or(this::commonIgnoreConditions)); return slice; @@ -58,7 +58,7 @@ public class ExceptionSensitiveSlicingAlgorithm implements SlicingAlgorithm { @Override public Slice traverseProcedure(GraphNode slicingCriterion) { - this.slicingCriterion = slicingCriterion; + this.slicingCriterion = Set.of(slicingCriterion); Slice slice = new Slice(); slice.add(slicingCriterion); pass(slice, INTRAPROCEDURAL); @@ -121,7 +121,7 @@ public class ExceptionSensitiveSlicingAlgorithm implements SlicingAlgorithm { return arc.isUnconditionalControlDependencyArc() && graph.isPseudoPredicate(target) && reachedStream(target).allMatch(Arc::isUnconditionalControlDependencyArc) && - !target.equals(slicingCriterion); + !slicingCriterion.contains(target); } /** Applies rule 4 of the algorithm. */ diff --git a/sdg-core/src/main/java/es/upv/mist/slicing/slicing/GraphNodeCriterion.java b/sdg-core/src/main/java/es/upv/mist/slicing/slicing/GraphNodeCriterion.java deleted file mode 100644 index 1d9c8e2..0000000 --- a/sdg-core/src/main/java/es/upv/mist/slicing/slicing/GraphNodeCriterion.java +++ /dev/null @@ -1,33 +0,0 @@ -package es.upv.mist.slicing.slicing; - -import es.upv.mist.slicing.graphs.cfg.CFG; -import es.upv.mist.slicing.graphs.pdg.PDG; -import es.upv.mist.slicing.graphs.sdg.SDG; -import es.upv.mist.slicing.nodes.GraphNode; - -import java.util.Optional; - -/** A criterion that locates nodes by {@link GraphNode}. */ -public class GraphNodeCriterion extends SlicingCriterion { - private final GraphNode node; - - public GraphNodeCriterion(GraphNode node, String variable) { - super(variable); - this.node = node; - } - - @Override - public Optional> findNode(CFG graph) { - return graph.findNodeById(node.getId()); - } - - @Override - public Optional> findNode(PDG graph) { - return graph.findNodeById(node.getId()); - } - - @Override - public Optional> findNode(SDG graph) { - return graph.findNodeById(node.getId()); - } -} diff --git a/sdg-core/src/main/java/es/upv/mist/slicing/slicing/JSysDGSlicingAlgorithm.java b/sdg-core/src/main/java/es/upv/mist/slicing/slicing/JSysDGSlicingAlgorithm.java index a08d94c..8bc4ef3 100644 --- a/sdg-core/src/main/java/es/upv/mist/slicing/slicing/JSysDGSlicingAlgorithm.java +++ b/sdg-core/src/main/java/es/upv/mist/slicing/slicing/JSysDGSlicingAlgorithm.java @@ -19,7 +19,7 @@ public class JSysDGSlicingAlgorithm extends ExceptionSensitiveSlicingAlgorithm { protected boolean objectFlowIgnore(Arc arc) { GraphNode target = graph.getEdgeTarget(arc); return arc.isObjectFlow() && // 1. The arc is object flow - slicingCriterion != target && // 2. The target is not the slicing criterion + !slicingCriterion.contains(target) && // 2. The target is not the slicing criterion reachedStream(target).noneMatch(Arc::isObjectFlow) && // 3. The target hasn't been reached by object flow arcs !graph.isPredicate(target) && // 4. The target is not a predicate !(target.getAstNode() instanceof CatchClause) && !(target instanceof ExceptionExitNode); // Some extra conditions for exceptions diff --git a/sdg-core/src/main/java/es/upv/mist/slicing/slicing/LineNumberCriterion.java b/sdg-core/src/main/java/es/upv/mist/slicing/slicing/LineNumberCriterion.java index 84fd948..4753051 100644 --- a/sdg-core/src/main/java/es/upv/mist/slicing/slicing/LineNumberCriterion.java +++ b/sdg-core/src/main/java/es/upv/mist/slicing/slicing/LineNumberCriterion.java @@ -4,55 +4,36 @@ import com.github.javaparser.Position; import com.github.javaparser.ast.CompilationUnit; import com.github.javaparser.ast.Node; import com.github.javaparser.ast.NodeList; -import com.github.javaparser.ast.stmt.Statement; -import es.upv.mist.slicing.graphs.cfg.CFG; -import es.upv.mist.slicing.graphs.pdg.PDG; import es.upv.mist.slicing.graphs.sdg.SDG; import es.upv.mist.slicing.nodes.GraphNode; -import es.upv.mist.slicing.utils.Logger; +import java.util.NoSuchElementException; import java.util.Optional; +import java.util.Set; +import java.util.stream.Collectors; /** A criterion that locates nodes by line. It may only be used in single-declaration graphs. */ -public class LineNumberCriterion extends SlicingCriterion { +public class LineNumberCriterion implements SlicingCriterion { protected static final Position DEFAULT_POSITION = new Position(0, 0); protected final int lineNumber; + protected final String variable; public LineNumberCriterion(int lineNumber, String variable) { - super(variable); + this.variable = variable; this.lineNumber = lineNumber; } @Override - public Optional> findNode(CFG graph) { - return Optional.empty(); - } - - @Override - public Optional> findNode(PDG graph) { - // find node by line number - return graph.vertexSet().stream().filter(node -> { - Node astNode = node.getAstNode(); - - if (astNode.getBegin().isEmpty() || astNode.getEnd().isEmpty()) - return false; - - int begin = astNode.getBegin().get().line; - int end = astNode.getEnd().get().line; - - Logger.format("begin %s end %s", begin, end); - - return lineNumber == begin || lineNumber == end; - }).findFirst(); - } - - @Override - public Optional> findNode(SDG graph) { + public Set> findNode(SDG graph) { Optional optCu = findCompilationUnit(graph.getCompilationUnits()); if (optCu.isEmpty()) - return Optional.empty(); - return optCu.get().findFirst(Statement.class, this::matchesLine).flatMap(graph::findNodeByASTNode); + throw new NoSuchElementException(); + return optCu.get().findAll(Node.class, this::matchesLine).stream() + .map(graph::findNodeByASTNode) + .filter(Optional::isPresent) + .map(Optional::get) + .collect(Collectors.toSet()); } /** Locates the compilation unit that corresponds to this criterion's file. */ diff --git a/sdg-core/src/main/java/es/upv/mist/slicing/slicing/NodeIdSlicingCriterion.java b/sdg-core/src/main/java/es/upv/mist/slicing/slicing/NodeIdSlicingCriterion.java deleted file mode 100644 index 58c6fdc..0000000 --- a/sdg-core/src/main/java/es/upv/mist/slicing/slicing/NodeIdSlicingCriterion.java +++ /dev/null @@ -1,33 +0,0 @@ -package es.upv.mist.slicing.slicing; - -import es.upv.mist.slicing.graphs.cfg.CFG; -import es.upv.mist.slicing.graphs.pdg.PDG; -import es.upv.mist.slicing.graphs.sdg.SDG; -import es.upv.mist.slicing.nodes.GraphNode; - -import java.util.Optional; - -/** A criterion that locates nodes by numerical id. */ -public class NodeIdSlicingCriterion extends SlicingCriterion { - protected final long id; - - public NodeIdSlicingCriterion(long id, String variable) { - super(variable); - this.id = id; - } - - @Override - public Optional> findNode(CFG graph) { - return graph.findNodeById(id); - } - - @Override - public Optional> findNode(PDG graph) { - return graph.findNodeById(id); - } - - @Override - public Optional> findNode(SDG graph) { - return graph.findNodeById(id); - } -} diff --git a/sdg-core/src/main/java/es/upv/mist/slicing/slicing/PseudoPredicateSlicingAlgorithm.java b/sdg-core/src/main/java/es/upv/mist/slicing/slicing/PseudoPredicateSlicingAlgorithm.java index eb7292b..2046786 100644 --- a/sdg-core/src/main/java/es/upv/mist/slicing/slicing/PseudoPredicateSlicingAlgorithm.java +++ b/sdg-core/src/main/java/es/upv/mist/slicing/slicing/PseudoPredicateSlicingAlgorithm.java @@ -4,8 +4,10 @@ import es.upv.mist.slicing.arcs.Arc; import es.upv.mist.slicing.graphs.augmented.PSDG; import es.upv.mist.slicing.nodes.GraphNode; +import java.util.Set; + public class PseudoPredicateSlicingAlgorithm extends ClassicSlicingAlgorithm { - protected GraphNode slicingCriterion; + protected Set> slicingCriterion; public PseudoPredicateSlicingAlgorithm(PSDG graph) { super(graph); @@ -13,12 +15,12 @@ public class PseudoPredicateSlicingAlgorithm extends ClassicSlicingAlgorithm { @Override public Slice traverseProcedure(GraphNode slicingCriterion) { - this.slicingCriterion = slicingCriterion; + this.slicingCriterion = Set.of(slicingCriterion); return super.traverseProcedure(slicingCriterion); } @Override - public Slice traverse(GraphNode slicingCriterion) { + public Slice traverse(Set> slicingCriterion) { this.slicingCriterion = slicingCriterion; return super.traverse(slicingCriterion); } @@ -42,6 +44,6 @@ public class PseudoPredicateSlicingAlgorithm extends ClassicSlicingAlgorithm { GraphNode target = graph.getEdgeTarget(arc); return ((PSDG) graph).isPseudoPredicate(target) && arc.isControlDependencyArc() - && target != slicingCriterion; + && !slicingCriterion.contains(target); } } diff --git a/sdg-core/src/main/java/es/upv/mist/slicing/slicing/SlicingAlgorithm.java b/sdg-core/src/main/java/es/upv/mist/slicing/slicing/SlicingAlgorithm.java index 4e271c2..f52a3e6 100644 --- a/sdg-core/src/main/java/es/upv/mist/slicing/slicing/SlicingAlgorithm.java +++ b/sdg-core/src/main/java/es/upv/mist/slicing/slicing/SlicingAlgorithm.java @@ -2,9 +2,11 @@ package es.upv.mist.slicing.slicing; import es.upv.mist.slicing.nodes.GraphNode; +import java.util.Set; + public interface SlicingAlgorithm { /** Obtain the nodes reached by this algorithm in a classic 2-pass interprocedural slice. */ - Slice traverse(GraphNode slicingCriterion); + Slice traverse(Set> slicingCriterion); /** Obtain the nodes reached by this algorithm intraprocedurally (i.e. without traversing interprocedural arcs. */ Slice traverseProcedure(GraphNode slicingCriterion); } diff --git a/sdg-core/src/main/java/es/upv/mist/slicing/slicing/SlicingCriterion.java b/sdg-core/src/main/java/es/upv/mist/slicing/slicing/SlicingCriterion.java index c1e2bfc..b17c5b3 100644 --- a/sdg-core/src/main/java/es/upv/mist/slicing/slicing/SlicingCriterion.java +++ b/sdg-core/src/main/java/es/upv/mist/slicing/slicing/SlicingCriterion.java @@ -1,34 +1,19 @@ package es.upv.mist.slicing.slicing; -import es.upv.mist.slicing.graphs.cfg.CFG; -import es.upv.mist.slicing.graphs.pdg.PDG; import es.upv.mist.slicing.graphs.sdg.SDG; import es.upv.mist.slicing.nodes.GraphNode; -import java.util.Optional; +import java.util.NoSuchElementException; +import java.util.Set; /** A slicing criterion, or the point of interest in slicing. The selected variable(s) * (if any) must produce the same sequence of values in the original program as in the sliced one. */ -public abstract class SlicingCriterion { - protected final String variable; - - public SlicingCriterion(String variable) { - this.variable = variable; - } - - public String getVariable() { - return variable; - } - - /** Locate the slicing criterion in a control flow graph. */ - public abstract Optional> findNode(CFG graph); - /** Locate the slicing criterion in a program dependence graph. */ - public abstract Optional> findNode(PDG graph); - /** Locate the slicing criterion in a system dependence graph. */ - public abstract Optional> findNode(SDG graph); - - @Override - public String toString() { - return "(" + variable + ")"; - } +public interface SlicingCriterion { + /** + * Locates the nodes that represent the slicing criterion in the SDG. + * @return A set with at least one element, all of which directly represent + * the selected criterion in the SDG. + * @throws NoSuchElementException When the slicing criterion cannot be located. + */ + Set> findNode(SDG sdg); } -- GitLab From 396b28c663bafecfc4f70dd9dd735daaa0588924 Mon Sep 17 00:00:00 2001 From: Carlos Galindo Date: Thu, 11 Mar 2021 18:50:56 +0100 Subject: [PATCH 52/60] Slicing criterion now selects all nodes that represent the selected statement If a variable is provided, only nodes that represent that variable (or its MemberNode, if object) will be selected. Related tests updated. --- README.md | 2 +- .../java/es/upv/mist/slicing/cli/Slicer.java | 96 ++++++------------- .../slicing/FileLineSlicingCriterion.java | 6 +- .../slicing/slicing/LineNumberCriterion.java | 50 +++++++++- .../carlos/TestJosep.java.sdg.sliced | 1 + .../review-07-2020/P1.java.sdg.sliced | 16 ++++ 6 files changed, 96 insertions(+), 75 deletions(-) diff --git a/README.md b/README.md index 96e58dc..08caa28 100644 --- a/README.md +++ b/README.md @@ -20,7 +20,7 @@ A fat jar containing all the project's dependencies can be then located at `./sd ### Slice a Java program -The slicing criterion can be specified with the flag `-c {file}#{line}:{var}[!{occurrence}`, where the file, line and variable can be specified. If the variable appears multiple times in the given line, an occurrence can be set (append `:2` to select the second occurrence). +The slicing criterion can be specified with the flag `-c {file}#{line}:{var}`, where the file, line and variable can be specified. If the variable appears multiple times in the given line, all of them will be selected. If we wish to slice following program with respect to variable `sum` in line 11, diff --git a/sdg-cli/src/main/java/es/upv/mist/slicing/cli/Slicer.java b/sdg-cli/src/main/java/es/upv/mist/slicing/cli/Slicer.java index 6035f2d..984e0b9 100644 --- a/sdg-cli/src/main/java/es/upv/mist/slicing/cli/Slicer.java +++ b/sdg-cli/src/main/java/es/upv/mist/slicing/cli/Slicer.java @@ -7,9 +7,9 @@ import com.github.javaparser.ast.comments.BlockComment; import com.github.javaparser.ast.nodeTypes.NodeWithName; import com.github.javaparser.symbolsolver.resolution.typesolvers.JavaParserTypeSolver; import es.upv.mist.slicing.graphs.augmented.ASDG; -import es.upv.mist.slicing.graphs.jsysdg.JSysDG; import es.upv.mist.slicing.graphs.augmented.PSDG; import es.upv.mist.slicing.graphs.exceptionsensitive.ESSDG; +import es.upv.mist.slicing.graphs.jsysdg.JSysDG; import es.upv.mist.slicing.graphs.sdg.SDG; import es.upv.mist.slicing.slicing.FileLineSlicingCriterion; import es.upv.mist.slicing.slicing.Slice; @@ -21,7 +21,9 @@ import org.apache.commons.cli.*; import java.io.File; import java.io.FileNotFoundException; import java.io.PrintWriter; -import java.util.*; +import java.util.Collections; +import java.util.HashSet; +import java.util.Set; import java.util.regex.Matcher; import java.util.regex.Pattern; import java.util.stream.Stream; @@ -37,9 +39,8 @@ public class Slicer { static { String fileRe = "(?[^#]+\\.java)"; String lineRe = "(?[1-9]\\d*)"; - String varsRe = "(?[a-zA-Z_]\\w*(?:,[a-zA-Z_]\\w*)*)"; - String numsRe = "(?[1-9]\\d*(?:,[1-9]\\d*)*)"; - SC_PATTERN = Pattern.compile(fileRe + "#" + lineRe + "(?::" + varsRe + "(?:!" + numsRe + ")?)?"); + String varsRe = "(?[a-zA-Z_]\\w*(?:,[a-zA-Z_]\\w*)*)"; + SC_PATTERN = Pattern.compile(fileRe + "#" + lineRe + "(?::" + varsRe + ")?"); } static { @@ -50,30 +51,21 @@ public class Slicer { .build()); OPTIONS.addOption(Option .builder("l").longOpt("line") - .hasArg().argName("lineNumber").type(Number.class) + .hasArg().argName("line-number").type(Number.class) .desc("The line that contains the statement of the slicing criterion.") .build()); OPTIONS.addOption(Option .builder("v").longOpt("var") - .hasArgs().argName("variableName").valueSeparator(',') + .hasArgs().argName("variable-name") .desc("The name of the variable of the slicing criterion. Not setting this option is" + - " equivalent to selecting an empty set; setting multiple variables is allowed," + - " separated by commas") - .build()); - OPTIONS.addOption(Option - .builder("n").longOpt("number") - .hasArgs().argName("occurrenceNumber").valueSeparator(',') - .desc("The occurrence number of the variable(s) selected. If this argument is not set, it will" + - " default to the first appearance of each variable. If the occurrence number must be set" + - " for every variable in the same order.") + " equivalent to selecting all the code in the given line number.") .build()); OPTIONS.addOption(Option .builder("c").longOpt("criterion") - .hasArg().argName("file#line[:var[!occurrence]]") - .desc("The slicing criterion, in the format \"file#line:var\". Optionally, the occurrence can be" + - " appended as \"!occurrence\". This option may be replaced by \"-f\", \"-l\", \"-v\" and \"-n\"." + - " If both are specified, the others are discarded. It functions in a similar way: the variable" + - " and occurrence may be skipped or declared multiple times.") + .hasArg().argName("file#line[:var]") + .desc("The slicing criterion, in the format \"file#line:var\". The variable is optional."+ + " This option may be replaced by \"-f\", \"-l\" and \"-v\"." + + " If this argument is set, it will override the individual ones.") .build()); OPTIONS.addOption(Option .builder("i").longOpt("include") @@ -84,13 +76,13 @@ public class Slicer { .build()); OPTIONS.addOption(Option .builder("o").longOpt("output") - .hasArg().argName("outputDir") + .hasArg().argName("output-dir") .desc("The directory where the sliced source code should be placed. By default, it is placed at " + DEFAULT_OUTPUT_DIR) .build()); OPTIONS.addOption(Option .builder("t").longOpt("type") - .hasArg().argName("graph_type") + .hasArg().argName("graph-type") .desc("The type of graph to be built. Available options are SDG, ASDG, PSDG, ESSDG.") .build()); OPTIONS.addOption(Option @@ -103,8 +95,7 @@ public class Slicer { private File outputDir = DEFAULT_OUTPUT_DIR; private File scFile; private int scLine; - private final List scVars = new ArrayList<>(); - private final List scVarOccurrences = new ArrayList<>(); + private String scVar; private final CommandLine cliOpts; public Slicer(String... cliArgs) throws ParseException { @@ -117,23 +108,14 @@ public class Slicer { throw new ParseException("Invalid format for slicing criterion, see --help for more details"); setScFile(matcher.group("file")); setScLine(Integer.parseInt(matcher.group("line"))); - String vars = matcher.group("vars"); - String nums = matcher.group("nums"); - if (vars != null) { - if (nums != null) - setScVars(vars.split(","), nums.split(",")); - else - setScVars(vars.split(",")); - } + String var = matcher.group("var"); + if (var != null) + setScVar(var); } else if (cliOpts.hasOption('f') && cliOpts.hasOption('l')) { setScFile(cliOpts.getOptionValue('f')); setScLine(((Number) cliOpts.getParsedOptionValue("l")).intValue()); - if (cliOpts.hasOption('v')) { - if (cliOpts.hasOption('n')) - setScVars(cliOpts.getOptionValues('v'), cliOpts.getOptionValues('n')); - else - setScVars(cliOpts.getOptionValues('v')); - } + if (cliOpts.hasOption('v')) + setScVar(cliOpts.getOptionValue('v')); } else { throw new ParseException("Slicing criterion not specified: either use \"-c\" or \"-f\" and \"-l\"."); } @@ -164,26 +146,8 @@ public class Slicer { scLine = line; } - private void setScVars(String[] scVars) throws ParseException { - String[] array = new String[scVars.length]; - Arrays.fill(array, "1"); - setScVars(scVars, array); - } - - private void setScVars(String[] scVars, String[] scOccurrences) throws ParseException { - if (scVars.length != scOccurrences.length) - throw new ParseException("If the number of occurrence is specified, it must be specified once per variable."); - try { - for (int i = 0; i < scVars.length; i++) { - this.scVars.add(scVars[i]); - int n = Integer.parseUnsignedInt(scOccurrences[i]); - if (n <= 0) - throw new ParseException("The number of occurrence must be larger than 0."); - this.scVarOccurrences.add(n); - } - } catch (NumberFormatException e) { - throw new ParseException(e.getMessage()); - } + private void setScVar(String scVar) { + this.scVar = scVar; } public Set getDirIncludeSet() { @@ -202,12 +166,8 @@ public class Slicer { return scLine; } - public List getScVars() { - return Collections.unmodifiableList(scVars); - } - - public List getScVarOccurrences() { - return Collections.unmodifiableList(scVarOccurrences); + public String getScVar() { + return scVar; } public void slice() throws ParseException { @@ -245,7 +205,7 @@ public class Slicer { sdg.build(new NodeList<>(units)); // Slice the SDG - SlicingCriterion sc = new FileLineSlicingCriterion(scFile, scLine); + SlicingCriterion sc = new FileLineSlicingCriterion(scFile, scLine, scVar); Slice slice = sdg.slice(sc); // Convert the slice to code and output the result to `outputDir` @@ -285,8 +245,8 @@ public class Slicer { protected String getDisclaimer(CompilationUnit.Storage s) { return String.format("\n\tThis file was automatically generated as part of a slice with criterion" + - "\n\tfile: %s, line: %d, variable(s): %s\n\tOriginal file: %s\n", - scFile, scLine, String.join(", ", scVars), s.getPath()); + "\n\tfile: %s, line: %d, variable: %s\n\tOriginal file: %s\n", + scFile, scLine, scVar, s.getPath()); } protected void printHelp() { diff --git a/sdg-core/src/main/java/es/upv/mist/slicing/slicing/FileLineSlicingCriterion.java b/sdg-core/src/main/java/es/upv/mist/slicing/slicing/FileLineSlicingCriterion.java index 8a1afdb..dd7f398 100644 --- a/sdg-core/src/main/java/es/upv/mist/slicing/slicing/FileLineSlicingCriterion.java +++ b/sdg-core/src/main/java/es/upv/mist/slicing/slicing/FileLineSlicingCriterion.java @@ -11,7 +11,11 @@ public class FileLineSlicingCriterion extends LineNumberCriterion { protected final File file; public FileLineSlicingCriterion(File file, int lineNumber) { - super(lineNumber, null); + this(file, lineNumber, null); + } + + public FileLineSlicingCriterion(File file, int lineNumber, String variable) { + super(lineNumber, variable); this.file = file; } diff --git a/sdg-core/src/main/java/es/upv/mist/slicing/slicing/LineNumberCriterion.java b/sdg-core/src/main/java/es/upv/mist/slicing/slicing/LineNumberCriterion.java index 4753051..de7bd6f 100644 --- a/sdg-core/src/main/java/es/upv/mist/slicing/slicing/LineNumberCriterion.java +++ b/sdg-core/src/main/java/es/upv/mist/slicing/slicing/LineNumberCriterion.java @@ -4,20 +4,21 @@ import com.github.javaparser.Position; import com.github.javaparser.ast.CompilationUnit; import com.github.javaparser.ast.Node; import com.github.javaparser.ast.NodeList; +import es.upv.mist.slicing.arcs.pdg.StructuralArc; import es.upv.mist.slicing.graphs.sdg.SDG; import es.upv.mist.slicing.nodes.GraphNode; +import es.upv.mist.slicing.nodes.ObjectTree; -import java.util.NoSuchElementException; -import java.util.Optional; -import java.util.Set; +import java.util.*; import java.util.stream.Collectors; +import java.util.stream.Stream; /** A criterion that locates nodes by line. It may only be used in single-declaration graphs. */ public class LineNumberCriterion implements SlicingCriterion { protected static final Position DEFAULT_POSITION = new Position(0, 0); protected final int lineNumber; - protected final String variable; + protected String variable; public LineNumberCriterion(int lineNumber, String variable) { this.variable = variable; @@ -29,11 +30,19 @@ public class LineNumberCriterion implements SlicingCriterion { Optional optCu = findCompilationUnit(graph.getCompilationUnits()); if (optCu.isEmpty()) throw new NoSuchElementException(); - return optCu.get().findAll(Node.class, this::matchesLine).stream() + + Set> set = optCu.get().findAll(Node.class, this::matchesLine).stream() .map(graph::findNodeByASTNode) .filter(Optional::isPresent) .map(Optional::get) + .flatMap(node -> locateVariableNodes(node, graph)) .collect(Collectors.toSet()); + if (set.isEmpty() && !variable.startsWith("this")) { + variable = "this." + variable; + return findNode(graph); + } else { + return set; + } } /** Locates the compilation unit that corresponds to this criterion's file. */ @@ -46,6 +55,37 @@ public class LineNumberCriterion implements SlicingCriterion { return node.getBegin().orElse(DEFAULT_POSITION).line == lineNumber; } + protected Stream> locateVariableNodes(GraphNode graphNode, SDG graph) { + if (variable == null) + return locateAllNodes(graphNode, graph); + return locateAllNodes(graphNode, graph) + .map(GraphNode::getVariableActions).flatMap(List::stream) + .map(variableAction -> { + if (variableAction.getName().equals(variable)) { + if (variableAction.hasObjectTree()) + return variableAction.getObjectTree().getMemberNode(); + else + return variableAction.getGraphNode(); + } else if (variable.contains(".") && variableAction.getName().equals(ObjectTree.removeFields(variable))) { + if (variableAction.hasTreeMember(variable)) + return variableAction.getObjectTree().getNodeFor(variable); + else + return null; + } else { + return null; + } + }) + .filter(Objects::nonNull); + } + + protected Stream> locateAllNodes(GraphNode graphNode, SDG graph) { + Stream> result = graph.outgoingEdgesOf(graphNode).stream() + .filter(StructuralArc.class::isInstance) + .map(graph::getEdgeTarget) + .flatMap(node -> locateAllNodes(node, graph)); + return Stream.concat(Stream.of(graphNode), result); + } + @Override public String toString() { return String.format("(%s, %s)", lineNumber, variable); diff --git a/sdg-core/src/test/res/regression/carlos/TestJosep.java.sdg.sliced b/sdg-core/src/test/res/regression/carlos/TestJosep.java.sdg.sliced index da134e6..2393031 100644 --- a/sdg-core/src/test/res/regression/carlos/TestJosep.java.sdg.sliced +++ b/sdg-core/src/test/res/regression/carlos/TestJosep.java.sdg.sliced @@ -1,6 +1,7 @@ public class TestJosep { public static void main(int y) { + int z = 0; log(z); } } diff --git a/sdg-core/src/test/res/regression/review-07-2020/P1.java.sdg.sliced b/sdg-core/src/test/res/regression/review-07-2020/P1.java.sdg.sliced index fdacf0a..2785ae1 100644 --- a/sdg-core/src/test/res/regression/review-07-2020/P1.java.sdg.sliced +++ b/sdg-core/src/test/res/regression/review-07-2020/P1.java.sdg.sliced @@ -3,6 +3,22 @@ class FiguresGroup { public Figure greatestFigure() { if (numF == 0) return null; + Figure f = figuresList[0]; + double a = figuresList[0].area(); + for (int i = 1; i < numF; i++) { + double b = figuresList[i].area(); + if (a < b) { + f = figuresList[i]; + a = b; + } + } return f; } } + +class Figure { + + int area() { + return 5; + } +} -- GitLab From 7a0844e57b6fe90b4726e082edc255164aea9a0d Mon Sep 17 00:00:00 2001 From: Carlos Galindo Date: Thu, 11 Mar 2021 19:19:08 +0100 Subject: [PATCH 53/60] Add tree linking for primitive type declarations. --- .../mist/slicing/graphs/ExpressionObjectTreeFinder.java | 7 +++++-- .../java/es/upv/mist/slicing/nodes/VariableVisitor.java | 2 +- 2 files changed, 6 insertions(+), 3 deletions(-) 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 f88a829..fc09bbb 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 @@ -168,8 +168,11 @@ public class ExpressionObjectTreeFinder { protected void markTransference(Pair sourcePair, VariableAction targetAction, String targetMember) { VariableAction sourceAction = sourcePair.a; String sourceMember = sourcePair.b; - if (targetAction.hasObjectTree()) + if (targetAction.hasObjectTree()) { ObjectTree.copyTargetTreeToSource(sourceAction.getObjectTree(), targetAction.getObjectTree(), sourceMember, targetMember); - sourceAction.setPDGTreeConnectionTo(targetAction, sourceMember, targetMember); + sourceAction.setPDGTreeConnectionTo(targetAction, sourceMember, targetMember); + } else { + sourceAction.setPDGValueConnection(sourceMember); + } } } 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 37b90fc..6c00e9e 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 @@ -362,7 +362,7 @@ public class VariableVisitor extends GraphNodeContentVisitor Date: Thu, 11 Mar 2021 19:20:08 +0100 Subject: [PATCH 54/60] New tests with return statements in the constructor. --- .../programs/sdg/ConstructorWithReturn.java | 17 +++++++++++++++++ .../ConstructorWithReturn.java.sdg.criterion | 1 + .../sdg/ConstructorWithReturn.java.sdg.sliced | 17 +++++++++++++++++ 3 files changed, 35 insertions(+) create mode 100644 sdg-core/src/test/res/regression/programs/sdg/ConstructorWithReturn.java create mode 100644 sdg-core/src/test/res/regression/programs/sdg/ConstructorWithReturn.java.sdg.criterion create mode 100644 sdg-core/src/test/res/regression/programs/sdg/ConstructorWithReturn.java.sdg.sliced diff --git a/sdg-core/src/test/res/regression/programs/sdg/ConstructorWithReturn.java b/sdg-core/src/test/res/regression/programs/sdg/ConstructorWithReturn.java new file mode 100644 index 0000000..e5a72ed --- /dev/null +++ b/sdg-core/src/test/res/regression/programs/sdg/ConstructorWithReturn.java @@ -0,0 +1,17 @@ +class Test { + int a; + + Test(int a, boolean b, int c) { + if (b) { + this.a = a * c / (a + c); + return; + } + this.a = 10; + } + + public static void main(String[] args) { + Test t = new Test(15, true, 10); + int a = t.a; + System.out.println(a); + } +} \ No newline at end of file diff --git a/sdg-core/src/test/res/regression/programs/sdg/ConstructorWithReturn.java.sdg.criterion b/sdg-core/src/test/res/regression/programs/sdg/ConstructorWithReturn.java.sdg.criterion new file mode 100644 index 0000000..3f10ffe --- /dev/null +++ b/sdg-core/src/test/res/regression/programs/sdg/ConstructorWithReturn.java.sdg.criterion @@ -0,0 +1 @@ +15 \ No newline at end of file diff --git a/sdg-core/src/test/res/regression/programs/sdg/ConstructorWithReturn.java.sdg.sliced b/sdg-core/src/test/res/regression/programs/sdg/ConstructorWithReturn.java.sdg.sliced new file mode 100644 index 0000000..e5a72ed --- /dev/null +++ b/sdg-core/src/test/res/regression/programs/sdg/ConstructorWithReturn.java.sdg.sliced @@ -0,0 +1,17 @@ +class Test { + int a; + + Test(int a, boolean b, int c) { + if (b) { + this.a = a * c / (a + c); + return; + } + this.a = 10; + } + + public static void main(String[] args) { + Test t = new Test(15, true, 10); + int a = t.a; + System.out.println(a); + } +} \ No newline at end of file -- GitLab From acc873182832081277bab5a8a6b815f9caa42321 Mon Sep 17 00:00:00 2001 From: Carlos Galindo Date: Thu, 11 Mar 2021 19:41:06 +0100 Subject: [PATCH 55/60] Checks and changes to lower the amount of primitive variables that develop an object tree. --- .../graphs/ExpressionObjectTreeFinder.java | 27 +++++++++++++++++++ .../sdg/InterproceduralActionFinder.java | 9 ++++--- .../sdg/InterproceduralDefinitionFinder.java | 2 ++ .../sdg/InterproceduralUsageFinder.java | 1 + 4 files changed, 35 insertions(+), 4 deletions(-) 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 fc09bbb..f09ef77 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 @@ -161,6 +161,33 @@ public class ExpressionObjectTreeFinder { } throw new IllegalStateException("Could not find USE(-output-) corresponding to call " + call); } + + @Override + public void visit(ArrayCreationExpr n, String arg) {} + + @Override + public void visit(ArrayInitializerExpr n, String arg) {} + + @Override + public void visit(BinaryExpr n, String arg) {} + + @Override + public void visit(ClassExpr n, String arg) {} + + @Override + public void visit(InstanceOfExpr n, String arg) {} + + @Override + public void visit(UnaryExpr n, String arg) {} + + @Override + public void visit(LambdaExpr n, String arg) {} + + @Override + public void visit(MethodReferenceExpr n, String arg) {} + + @Override + public void visit(PatternExpr n, String arg) {} }, ""); return list; } diff --git a/sdg-core/src/main/java/es/upv/mist/slicing/graphs/sdg/InterproceduralActionFinder.java b/sdg-core/src/main/java/es/upv/mist/slicing/graphs/sdg/InterproceduralActionFinder.java index d0c1daa..48dd13d 100644 --- a/sdg-core/src/main/java/es/upv/mist/slicing/graphs/sdg/InterproceduralActionFinder.java +++ b/sdg-core/src/main/java/es/upv/mist/slicing/graphs/sdg/InterproceduralActionFinder.java @@ -172,10 +172,11 @@ public abstract class InterproceduralActionFinder exte Set set = new HashSet<>(); for (Iterator it = filteredStream.iterator(); it.hasNext(); ) { A a = it.next(); - if (set.contains(a) && a.hasObjectTree()) { - for (A aFromSet : set) - if (aFromSet.hashCode() == a.hashCode() && Objects.equals(aFromSet, a)) - aFromSet.getObjectTree().addAll(a.getObjectTree()); + if (set.contains(a)) { + if (a.hasObjectTree()) + for (A aFromSet : set) + if (aFromSet.hashCode() == a.hashCode() && Objects.equals(aFromSet, a)) + aFromSet.getObjectTree().addAll(a.getObjectTree()); } else { set.add(a.createCopy()); } 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 1113627..0c97501 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 @@ -61,6 +61,7 @@ public class InterproceduralDefinitionFinder extends InterproceduralActionFinder 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)); } @@ -75,6 +76,7 @@ public class InterproceduralDefinitionFinder extends InterproceduralActionFinder Set defExpressions = new HashSet<>(); 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) 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 78fac15..ee9efd3 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 @@ -42,6 +42,7 @@ public class InterproceduralUsageFinder extends InterproceduralActionFinder graphNode = edge.getGraphNode(); if (use.isParameter()) { if (!use.isPrimitive()) { + assert use.hasObjectTree(); ActualIONode actualIn = locateActualInNode(edge, use.getName()); Definition def = new Definition(VariableAction.DeclarationType.SYNTHETIC, "-arg-in-", graphNode, (ObjectTree) use.getObjectTree().clone()); Movable movDef = new Movable(def, actualIn); -- GitLab From 799323f3123fee3889ab70e300d9c350da21413b Mon Sep 17 00:00:00 2001 From: Carlos Galindo Date: Thu, 11 Mar 2021 19:55:19 +0100 Subject: [PATCH 56/60] fix! handle field declarations properly --- .../graphs/ExpressionObjectTreeFinder.java | 16 +++++++++------- .../upv/mist/slicing/nodes/VariableVisitor.java | 5 ++++- 2 files changed, 13 insertions(+), 8 deletions(-) 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 f09ef77..ec64533 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 @@ -26,29 +26,31 @@ public class ExpressionObjectTreeFinder { this.graphNode = graphNode; } - public void handleVariableDeclarator(VariableDeclarator variableDeclarator) { + public void handleVariableDeclarator(VariableDeclarator variableDeclarator, String realName) { assert variableDeclarator.getInitializer().isPresent(); - VariableAction targetAction = locateVAVariableDeclarator(variableDeclarator.getNameAsString()); + VariableAction targetAction = locateVAVariableDeclarator(realName); ClassGraph.getInstance().generateObjectTreeForType(variableDeclarator.getType().resolve()) .ifPresent(objectTree -> targetAction.getObjectTree().addAll(objectTree)); locateExpressionResultTrees(variableDeclarator.getInitializer().get()) .forEach(pair -> markTransference(pair, targetAction, "")); } - protected VariableAction locateVAVariableDeclarator(String varName) { + protected VariableAction locateVAVariableDeclarator(String realName) { + String root = realName.contains(".") ? ObjectTree.removeFields(realName) : realName; boolean foundDecl = false; VariableAction lastDef = null; for (VariableAction a : graphNode.getVariableActions()) { if (a.isDeclaration()) { - if (a.getName().equals(varName)) + if (a.getName().equals(realName)) foundDecl = true; else if (foundDecl) return lastDef; - } else if (a.isDefinition() && a.getName().equals(varName)) { - lastDef = a; + } else if (a.isDefinition() && a.getName().equals(root)) { + if (root.equals(realName) || a.hasTreeMember(realName)) + lastDef = a; } } - assert lastDef != null: "Could not find DEF for variable declaration of " + varName; + assert lastDef != null: "Could not find DEF for variable declaration of " + realName; return lastDef; } 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 6c00e9e..63e8634 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 @@ -364,7 +364,10 @@ public class VariableVisitor extends GraphNodeContentVisitor Date: Thu, 11 Mar 2021 20:00:03 +0100 Subject: [PATCH 57/60] artificially inserted instructions are now stored in a NodeHashSet to improve equality checks. --- .../main/java/es/upv/mist/slicing/graphs/jsysdg/JSysCFG.java | 3 ++- .../programs/sdg/ConstructorWithReturn.java.sdg.sliced | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/sdg-core/src/main/java/es/upv/mist/slicing/graphs/jsysdg/JSysCFG.java b/sdg-core/src/main/java/es/upv/mist/slicing/graphs/jsysdg/JSysCFG.java index 265fc91..2a88a84 100644 --- a/sdg-core/src/main/java/es/upv/mist/slicing/graphs/jsysdg/JSysCFG.java +++ b/sdg-core/src/main/java/es/upv/mist/slicing/graphs/jsysdg/JSysCFG.java @@ -20,6 +20,7 @@ import es.upv.mist.slicing.nodes.GraphNode; import es.upv.mist.slicing.nodes.VariableAction; import es.upv.mist.slicing.nodes.io.MethodExitNode; import es.upv.mist.slicing.utils.ASTUtils; +import es.upv.mist.slicing.utils.NodeHashSet; import es.upv.mist.slicing.utils.NodeNotFoundException; import java.util.HashSet; @@ -155,7 +156,7 @@ public class JSysCFG extends ESCFG { protected ClassGraph classGraph; /** List of implicit instructions inserted explicitly in this CFG. * They should be included in the graph as ImplicitNodes. */ - protected List methodInsertedInstructions = new LinkedList<>(); + protected NodeHashSet methodInsertedInstructions = new NodeHashSet<>(); /** Whether we are building a CFG for an implicit method or not. */ protected boolean implicitDeclaration = false; diff --git a/sdg-core/src/test/res/regression/programs/sdg/ConstructorWithReturn.java.sdg.sliced b/sdg-core/src/test/res/regression/programs/sdg/ConstructorWithReturn.java.sdg.sliced index e5a72ed..19fa511 100644 --- a/sdg-core/src/test/res/regression/programs/sdg/ConstructorWithReturn.java.sdg.sliced +++ b/sdg-core/src/test/res/regression/programs/sdg/ConstructorWithReturn.java.sdg.sliced @@ -1,4 +1,5 @@ class Test { + int a; Test(int a, boolean b, int c) { @@ -14,4 +15,4 @@ class Test { int a = t.a; System.out.println(a); } -} \ No newline at end of file +} -- GitLab From 330651043e267e0832f030173e8869a86983acad Mon Sep 17 00:00:00 2001 From: Carlos Galindo Date: Fri, 12 Mar 2021 09:50:48 +0100 Subject: [PATCH 58/60] Add test case for a constructor with return statements at the end of every CFG path. --- .../sdg/ConstructorWithAllReturns.java | 20 +++++++++++++++++++ ...nstructorWithAllReturns.java.sdg.criterion | 1 + .../ConstructorWithAllReturns.java.sdg.sliced | 20 +++++++++++++++++++ 3 files changed, 41 insertions(+) create mode 100644 sdg-core/src/test/res/regression/programs/sdg/ConstructorWithAllReturns.java create mode 100644 sdg-core/src/test/res/regression/programs/sdg/ConstructorWithAllReturns.java.sdg.criterion create mode 100644 sdg-core/src/test/res/regression/programs/sdg/ConstructorWithAllReturns.java.sdg.sliced diff --git a/sdg-core/src/test/res/regression/programs/sdg/ConstructorWithAllReturns.java b/sdg-core/src/test/res/regression/programs/sdg/ConstructorWithAllReturns.java new file mode 100644 index 0000000..47ac262 --- /dev/null +++ b/sdg-core/src/test/res/regression/programs/sdg/ConstructorWithAllReturns.java @@ -0,0 +1,20 @@ +class Test { + + int a; + + Test(int a, boolean b, int c) { + if (b) { + this.a = a * c / (a + c); + return; + } else { + this.a = 10; + return; + } + } + + public static void main(String[] args) { + Test t = new Test(15, true, 10); + int a = t.a; + System.out.println(a); + } +} \ No newline at end of file diff --git a/sdg-core/src/test/res/regression/programs/sdg/ConstructorWithAllReturns.java.sdg.criterion b/sdg-core/src/test/res/regression/programs/sdg/ConstructorWithAllReturns.java.sdg.criterion new file mode 100644 index 0000000..25bf17f --- /dev/null +++ b/sdg-core/src/test/res/regression/programs/sdg/ConstructorWithAllReturns.java.sdg.criterion @@ -0,0 +1 @@ +18 \ No newline at end of file diff --git a/sdg-core/src/test/res/regression/programs/sdg/ConstructorWithAllReturns.java.sdg.sliced b/sdg-core/src/test/res/regression/programs/sdg/ConstructorWithAllReturns.java.sdg.sliced new file mode 100644 index 0000000..47ac262 --- /dev/null +++ b/sdg-core/src/test/res/regression/programs/sdg/ConstructorWithAllReturns.java.sdg.sliced @@ -0,0 +1,20 @@ +class Test { + + int a; + + Test(int a, boolean b, int c) { + if (b) { + this.a = a * c / (a + c); + return; + } else { + this.a = 10; + return; + } + } + + public static void main(String[] args) { + Test t = new Test(15, true, 10); + int a = t.a; + System.out.println(a); + } +} \ No newline at end of file -- GitLab From ee10b5bda2121bb7c3d952d311cabb9898b10162 Mon Sep 17 00:00:00 2001 From: Carlos Galindo Date: Fri, 12 Mar 2021 23:10:59 +0100 Subject: [PATCH 59/60] Small fixes found from coverage analysis. * Unified both summary arc analyzers * Deleted unused code. * Converted checks into assertions * Removed some bad patterns, including a possible bug in BinaryExpr handling. --- .../es/upv/mist/slicing/cli/PHPSlice.java | 2 +- .../graphs/ExpressionObjectTreeFinder.java | 5 +- .../es/upv/mist/slicing/graphs/Graph.java | 5 - .../slicing/graphs/augmented/ACFGBuilder.java | 16 +-- .../mist/slicing/graphs/cfg/CFGBuilder.java | 10 ++ .../graphs/exceptionsensitive/ESCFG.java | 15 +-- .../mist/slicing/graphs/jsysdg/JSysDG.java | 8 +- .../mist/slicing/graphs/jsysdg/JSysPDG.java | 90 ++++++++++------- .../graphs/jsysdg/SummaryArcAnalyzer.java | 77 +++------------ .../sdg/AbstractSummaryArcAnalyzer.java | 99 +++++++++++++++++++ .../slicing/graphs/sdg/CallConnector.java | 6 -- .../sdg/InterproceduralActionFinder.java | 66 ++----------- .../sdg/InterproceduralDefinitionFinder.java | 15 +-- .../sdg/InterproceduralUsageFinder.java | 3 +- .../graphs/sdg/OutNodeVariableVisitor.java | 6 -- .../es/upv/mist/slicing/graphs/sdg/SDG.java | 22 ++--- .../graphs/sdg/SummaryArcAnalyzer.java | 84 ++-------------- .../es/upv/mist/slicing/nodes/GraphNode.java | 28 ------ .../es/upv/mist/slicing/nodes/ObjectTree.java | 25 +---- .../mist/slicing/nodes/ValueConnection.java | 7 +- .../mist/slicing/nodes/VariableAction.java | 6 -- .../mist/slicing/nodes/VariableVisitor.java | 17 ++-- .../mist/slicing/nodes/io/ActualIONode.java | 13 +-- .../upv/mist/slicing/nodes/type/NodeType.java | 53 ---------- .../es/upv/mist/slicing/slicing/Slice.java | 5 - .../slicing/slicing/SlicePruneVisitor.java | 11 +-- .../es/upv/mist/slicing/utils/ASTUtils.java | 21 +--- .../es/upv/mist/slicing/utils/Logger.java | 7 +- .../java/es/upv/mist/slicing/utils/Utils.java | 13 ++- 29 files changed, 251 insertions(+), 484 deletions(-) create mode 100644 sdg-core/src/main/java/es/upv/mist/slicing/graphs/sdg/AbstractSummaryArcAnalyzer.java delete mode 100644 sdg-core/src/main/java/es/upv/mist/slicing/nodes/type/NodeType.java diff --git a/sdg-cli/src/main/java/es/upv/mist/slicing/cli/PHPSlice.java b/sdg-cli/src/main/java/es/upv/mist/slicing/cli/PHPSlice.java index 1c5bd54..4b71825 100644 --- a/sdg-cli/src/main/java/es/upv/mist/slicing/cli/PHPSlice.java +++ b/sdg-cli/src/main/java/es/upv/mist/slicing/cli/PHPSlice.java @@ -108,7 +108,7 @@ public class PHPSlice { } sdg.build(units); - SlicingCriterion sc = graph -> Set.of(graph.findNodeById(0).orElseThrow()); + SlicingCriterion sc = graph -> Set.of(graph.findNodeBy(n -> n.getId() == (long) 0).orElseThrow()); Slice slice = new Slice(); if (scId != 0) { // Slice the SDG 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 ec64533..07ff6cb 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 @@ -68,10 +68,7 @@ public class ExpressionObjectTreeFinder { public void locateAndMarkTransferenceToRoot(Expression expr, int index) { List list = graphNode.getVariableActions(); - if (index < 0) - locateAndMarkTransferenceToRoot(expr, list.get(list.size() + index)); - else - locateAndMarkTransferenceToRoot(expr, list.get(index)); + locateAndMarkTransferenceToRoot(expr, list.get(index < 0 ? list.size() + index : index)); } public void locateAndMarkTransferenceToRoot(Expression expression, VariableAction targetAction) { diff --git a/sdg-core/src/main/java/es/upv/mist/slicing/graphs/Graph.java b/sdg-core/src/main/java/es/upv/mist/slicing/graphs/Graph.java index 0498f17..f834025 100644 --- a/sdg-core/src/main/java/es/upv/mist/slicing/graphs/Graph.java +++ b/sdg-core/src/main/java/es/upv/mist/slicing/graphs/Graph.java @@ -46,11 +46,6 @@ public abstract class Graph extends DirectedPseudograph, Arc> { throw new IllegalStateException("There may only be one real node representing each AST node in the graph!"); } - /** Search for a node in the graph given its id. */ - public Optional> findNodeById(long id) { - return findNodeBy(n -> n.getId() == id); - } - /** Search for a node in the graph given a predicate it must pass. * If multiple nodes match the predicate, the first one found is returned. */ public Optional> findNodeBy(Predicate> p) { diff --git a/sdg-core/src/main/java/es/upv/mist/slicing/graphs/augmented/ACFGBuilder.java b/sdg-core/src/main/java/es/upv/mist/slicing/graphs/augmented/ACFGBuilder.java index 61cbf66..bd819ea 100644 --- a/sdg-core/src/main/java/es/upv/mist/slicing/graphs/augmented/ACFGBuilder.java +++ b/sdg-core/src/main/java/es/upv/mist/slicing/graphs/augmented/ACFGBuilder.java @@ -4,7 +4,6 @@ import com.github.javaparser.ast.body.CallableDeclaration; import com.github.javaparser.ast.stmt.*; import es.upv.mist.slicing.graphs.cfg.CFGBuilder; import es.upv.mist.slicing.nodes.GraphNode; -import es.upv.mist.slicing.nodes.io.MethodExitNode; import es.upv.mist.slicing.utils.ASTUtils; import java.util.Deque; @@ -141,17 +140,8 @@ public class ACFGBuilder extends CFGBuilder { // ====================================================================== @Override - protected void visitCallableDeclaration(CallableDeclaration callableDeclaration, Void arg) { - graph.buildRootNode(callableDeclaration); - hangingNodes.add(graph.getRootNode()); - - ASTUtils.getCallableBody(callableDeclaration).accept(this, arg); - returnList.stream().filter(node -> !hangingNodes.contains(node)).forEach(hangingNodes::add); - nonExecHangingNodes.add(graph.getRootNode()); // NEW vs CFG - - MethodExitNode exit = new MethodExitNode(callableDeclaration); - graph.addVertex(exit); - addMethodOutput(callableDeclaration, exit); - connectTo(exit); + protected void buildExit(CallableDeclaration callableDeclaration) { + nonExecHangingNodes.add(graph.getRootNode()); + super.buildExit(callableDeclaration); } } diff --git a/sdg-core/src/main/java/es/upv/mist/slicing/graphs/cfg/CFGBuilder.java b/sdg-core/src/main/java/es/upv/mist/slicing/graphs/cfg/CFGBuilder.java index 1386f8f..494ed55 100644 --- a/sdg-core/src/main/java/es/upv/mist/slicing/graphs/cfg/CFGBuilder.java +++ b/sdg-core/src/main/java/es/upv/mist/slicing/graphs/cfg/CFGBuilder.java @@ -358,12 +358,22 @@ public class CFGBuilder extends VoidVisitorAdapter { * to the exit node. */ protected void visitCallableDeclaration(CallableDeclaration callableDeclaration, Void arg) { + buildEnter(callableDeclaration); + visitCallableDeclarationBody(callableDeclaration, arg); + buildExit(callableDeclaration); + } + + protected void buildEnter(CallableDeclaration callableDeclaration) { graph.buildRootNode(callableDeclaration); hangingNodes.add(graph.getRootNode()); + } + protected void visitCallableDeclarationBody(CallableDeclaration callableDeclaration, Void arg) { ASTUtils.getCallableBody(callableDeclaration).accept(this, arg); returnList.stream().filter(node -> !hangingNodes.contains(node)).forEach(hangingNodes::add); + } + protected void buildExit(CallableDeclaration callableDeclaration) { MethodExitNode exit = new MethodExitNode(callableDeclaration); graph.addVertex(exit); addMethodOutput(callableDeclaration, exit); diff --git a/sdg-core/src/main/java/es/upv/mist/slicing/graphs/exceptionsensitive/ESCFG.java b/sdg-core/src/main/java/es/upv/mist/slicing/graphs/exceptionsensitive/ESCFG.java index be16aa3..75fd42b 100644 --- a/sdg-core/src/main/java/es/upv/mist/slicing/graphs/exceptionsensitive/ESCFG.java +++ b/sdg-core/src/main/java/es/upv/mist/slicing/graphs/exceptionsensitive/ESCFG.java @@ -17,7 +17,6 @@ import es.upv.mist.slicing.graphs.cfg.CFGBuilder; import es.upv.mist.slicing.nodes.GraphNode; import es.upv.mist.slicing.nodes.exceptionsensitive.*; import es.upv.mist.slicing.nodes.io.MethodExitNode; -import es.upv.mist.slicing.utils.ASTUtils; import es.upv.mist.slicing.utils.Logger; import java.util.*; @@ -109,13 +108,7 @@ public class ESCFG extends ACFG { } @Override - protected void visitCallableDeclaration(CallableDeclaration callableDeclaration, Void arg) { - buildRootNode(callableDeclaration); - hangingNodes.add(getRootNode()); - - ASTUtils.getCallableBody(callableDeclaration).accept(this, arg); - returnList.stream().filter(node -> !hangingNodes.contains(node)).forEach(hangingNodes::add); - // NEW vs ACFG + protected void buildExit(CallableDeclaration callableDeclaration) { if (!exceptionSourceMap.isEmpty()) { // Normal exit NormalExitNode normalExit = new NormalExitNode(callableDeclaration); @@ -138,12 +131,12 @@ public class ESCFG extends ACFG { MethodExitNode exit = new MethodExitNode(callableDeclaration); addVertex(exit); - if (exceptionSourceMap.isEmpty()) // NEW vs ACFG + if (exceptionSourceMap.isEmpty()) addMethodOutput(callableDeclaration, exit); - nonExecHangingNodes.addAll(exitNonExecHangingNodes); // NEW vs ACFG + nonExecHangingNodes.addAll(exitNonExecHangingNodes); connectTo(exit); - processPendingNormalResultNodes(); // NEW vs ACFG + processPendingNormalResultNodes(); } /** Converts the remaining exception sources into a collection of exception exit nodes. diff --git a/sdg-core/src/main/java/es/upv/mist/slicing/graphs/jsysdg/JSysDG.java b/sdg-core/src/main/java/es/upv/mist/slicing/graphs/jsysdg/JSysDG.java index 41277d5..6956213 100644 --- a/sdg-core/src/main/java/es/upv/mist/slicing/graphs/jsysdg/JSysDG.java +++ b/sdg-core/src/main/java/es/upv/mist/slicing/graphs/jsysdg/JSysDG.java @@ -8,14 +8,12 @@ import com.github.javaparser.ast.body.ClassOrInterfaceDeclaration; import com.github.javaparser.ast.body.ConstructorDeclaration; import com.github.javaparser.ast.visitor.ModifierVisitor; import com.github.javaparser.ast.visitor.Visitable; -import es.upv.mist.slicing.arcs.sdg.SummaryArc; import es.upv.mist.slicing.graphs.ClassGraph; import es.upv.mist.slicing.graphs.augmented.PSDG; import es.upv.mist.slicing.graphs.cfg.CFG; import es.upv.mist.slicing.graphs.exceptionsensitive.ESSDG; import es.upv.mist.slicing.graphs.exceptionsensitive.ExceptionSensitiveCallConnector; import es.upv.mist.slicing.graphs.pdg.PDG; -import es.upv.mist.slicing.nodes.SyntheticNode; import es.upv.mist.slicing.slicing.JSysDGSlicingAlgorithm; import es.upv.mist.slicing.slicing.SlicingAlgorithm; import es.upv.mist.slicing.utils.NodeHashSet; @@ -31,10 +29,6 @@ public class JSysDG extends ESSDG { return new JSysDG.Builder(); } - public void addSummaryArc(SyntheticNode from, SyntheticNode to) { - addEdge(from, to, new SummaryArc()); - } - /** Populates an ESSDG, using ESPDG and ESCFG as default graphs. * @see PSDG.Builder * @see ExceptionSensitiveCallConnector */ @@ -82,7 +76,7 @@ public class JSysDG extends ESSDG { @Override protected void createSummaryArcs() { - new SummaryArcAnalyzer(JSysDG.this, callGraph).analyze();; + new SummaryArcAnalyzer(JSysDG.this, callGraph).analyze(); } } } diff --git a/sdg-core/src/main/java/es/upv/mist/slicing/graphs/jsysdg/JSysPDG.java b/sdg-core/src/main/java/es/upv/mist/slicing/graphs/jsysdg/JSysPDG.java index aae37ce..4275c38 100644 --- a/sdg-core/src/main/java/es/upv/mist/slicing/graphs/jsysdg/JSysPDG.java +++ b/sdg-core/src/main/java/es/upv/mist/slicing/graphs/jsysdg/JSysPDG.java @@ -8,7 +8,6 @@ import es.upv.mist.slicing.graphs.exceptionsensitive.ESCFG; import es.upv.mist.slicing.graphs.exceptionsensitive.ESPDG; import es.upv.mist.slicing.graphs.pdg.PDG; 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.io.ActualIONode; import es.upv.mist.slicing.nodes.io.CallNode; @@ -18,6 +17,8 @@ import java.util.Deque; import java.util.LinkedList; import java.util.List; +import static es.upv.mist.slicing.nodes.ObjectTree.ROOT_NAME; + public class JSysPDG extends ESPDG { public JSysPDG() { this(new JSysCFG()); @@ -69,7 +70,7 @@ public class JSysPDG extends ESPDG { } protected void addTotalDefinitionDependencyArc(VariableAction totalDefinition, VariableAction target, String member) { - if (member.equals(ObjectTree.ROOT_NAME)) + if (member.equals(ROOT_NAME)) addEdge(graphNodeOf(totalDefinition), graphNodeOf(target), new TotalDefinitionDependenceArc()); else addEdge(totalDefinition.getObjectTree().getNodeFor(member), @@ -106,41 +107,62 @@ public class JSysPDG extends ESPDG { for (GraphNode node : vertexSet()) { for (VariableAction varAct : node.getVariableActions()) { // Total definition dependence - if (!varAct.isPrimitive() && (varAct.isUsage() || (varAct.isDefinition() && !varAct.isSynthetic()))) { - // root - jSysCFG.findLastTotalDefinitionOf(varAct, "-root-").forEach(totalDef -> addTotalDefinitionDependencyArc(totalDef, varAct, "-root-")); - // members - if (varAct.hasObjectTree()) - for (String member : varAct.getObjectTree().nameIterable()) - jSysCFG.findLastTotalDefinitionOf(varAct, member).forEach(totalDef -> addTotalDefinitionDependencyArc(totalDef, varAct, member)); - } - // Object flow, flow and declaration-definition dependencies - if (varAct.isUsage()) { - if (varAct.isPrimitive()) - jSysCFG.findLastDefinitionOfPrimitive(varAct).forEach(def -> addFlowDependencyArc(def, varAct)); - else { - jSysCFG.findLastDefinitionOfObjectRoot(varAct).forEach(def -> addObjectFlowDependencyArc(def, varAct)); - if (varAct.hasObjectTree()) - for (String member : varAct.getObjectTree().nameIterable()) - jSysCFG.findLastDefinitionOfObjectMember(varAct, member).forEach(def -> addFlowDependencyArc(def, varAct, member)); - } - } else if (varAct.isDefinition()) { - // Flow declaration --> definition - if (!varAct.isSynthetic()) - jSysCFG.findDeclarationFor(varAct).ifPresent(dec -> addFlowDependencyArc(dec, varAct)); - // Object flow definition --> definition - if (!varAct.isPrimitive() && varAct.hasObjectTree()) - for (String member : varAct.getObjectTree().nameIterable()) - jSysCFG.findNextObjectDefinitionsFor(varAct, member).forEach(def -> addObjectFlowDependencyArc(varAct, member, def)); - } else if (varAct.isDeclaration()) { - if (varAct.getName().startsWith("this.")) - jSysCFG.findAllFutureObjectDefinitionsFor(varAct) - .forEach(def -> addDeclarationFlowDependencyArc(varAct, def)); - } + buildTotalDefinitionDependence(jSysCFG, varAct); + if (varAct.isUsage()) + buildUsageDependencies(jSysCFG, varAct); + else if (varAct.isDefinition()) + buildDefinitionDependencies(jSysCFG, varAct); + else if (varAct.isDeclaration()) + buildDeclarationDependencies(jSysCFG, varAct); } } } + /** Generate total definition dependence. Only generated for non-primitive usages and non-primitive, + * non-synthetic definitions. Connects each member to its previous total definition. */ + private void buildTotalDefinitionDependence(JSysCFG jSysCFG, VariableAction varAct) { + if (!varAct.isPrimitive() && (varAct.isUsage() || (varAct.isDefinition() && !varAct.isSynthetic()))) { + jSysCFG.findLastTotalDefinitionOf(varAct, ROOT_NAME).forEach(totalDef -> addTotalDefinitionDependencyArc(totalDef, varAct, ROOT_NAME)); + if (!varAct.hasObjectTree()) + return; + for (String member : varAct.getObjectTree().nameIterable()) + jSysCFG.findLastTotalDefinitionOf(varAct, member).forEach(totalDef -> addTotalDefinitionDependencyArc(totalDef, varAct, member)); + } + } + + /** Generate dependencies to usages, including flow dependency for primitives, + * object flow for object roots and flow for object members. */ + private void buildUsageDependencies(JSysCFG jSysCFG, VariableAction varAct) { + if (varAct.isPrimitive()) { + jSysCFG.findLastDefinitionOfPrimitive(varAct).forEach(def -> addFlowDependencyArc(def, varAct)); + } else { + jSysCFG.findLastDefinitionOfObjectRoot(varAct).forEach(def -> addObjectFlowDependencyArc(def, varAct)); + if (!varAct.hasObjectTree()) + return; + for (String member : varAct.getObjectTree().nameIterable()) + jSysCFG.findLastDefinitionOfObjectMember(varAct, member).forEach(def -> addFlowDependencyArc(def, varAct, member)); + } + } + + /** Generates dec --> def flow and def --> def object flow dependencies. */ + private void buildDefinitionDependencies(JSysCFG jSysCFG, VariableAction varAct) { + // Flow declaration --> definition + if (!varAct.isSynthetic()) + jSysCFG.findDeclarationFor(varAct).ifPresent(dec -> addFlowDependencyArc(dec, varAct)); + // Object flow definition --> definition + if (varAct.isPrimitive() || !varAct.hasObjectTree()) + return; + for (String member : varAct.getObjectTree().nameIterable()) + jSysCFG.findNextObjectDefinitionsFor(varAct, member).forEach(def -> addObjectFlowDependencyArc(varAct, member, def)); + } + + /** Generates dec --> def declaration dependencies for objects (constructors only). */ + private void buildDeclarationDependencies(JSysCFG jSysCFG, VariableAction varAct) { + if (!varAct.getName().startsWith("this.")) + return; + jSysCFG.findAllFutureObjectDefinitionsFor(varAct).forEach(def -> addDeclarationFlowDependencyArc(varAct, def)); + } + @Override protected void expandCalls() { for (GraphNode graphNode : vertexSet()) { @@ -222,7 +244,7 @@ public class JSysPDG extends ESPDG { if (action.isDefinition() && action.hasObjectTree() && action.getName().equals(ESCFG.ACTIVE_EXCEPTION_VARIABLE)) - addValueDependencyArc(action, "-root-", node); + addValueDependencyArc(action, ROOT_NAME, node); } } } diff --git a/sdg-core/src/main/java/es/upv/mist/slicing/graphs/jsysdg/SummaryArcAnalyzer.java b/sdg-core/src/main/java/es/upv/mist/slicing/graphs/jsysdg/SummaryArcAnalyzer.java index 9932bbf..f9b2778 100644 --- a/sdg-core/src/main/java/es/upv/mist/slicing/graphs/jsysdg/SummaryArcAnalyzer.java +++ b/sdg-core/src/main/java/es/upv/mist/slicing/graphs/jsysdg/SummaryArcAnalyzer.java @@ -2,84 +2,26 @@ package es.upv.mist.slicing.graphs.jsysdg; import com.github.javaparser.ast.body.CallableDeclaration; import es.upv.mist.slicing.arcs.Arc; -import es.upv.mist.slicing.graphs.BackwardDataFlowAnalysis; import es.upv.mist.slicing.graphs.CallGraph; +import es.upv.mist.slicing.graphs.sdg.AbstractSummaryArcAnalyzer; import es.upv.mist.slicing.nodes.GraphNode; import es.upv.mist.slicing.nodes.SyntheticNode; import es.upv.mist.slicing.nodes.VariableAction; -import es.upv.mist.slicing.nodes.exceptionsensitive.ExitNode; import es.upv.mist.slicing.nodes.io.FormalIONode; -import es.upv.mist.slicing.nodes.io.OutputNode; import es.upv.mist.slicing.nodes.oo.MemberNode; -import java.util.*; -import java.util.stream.Stream; - -public class SummaryArcAnalyzer extends BackwardDataFlowAnalysis, Map, Set>>> { - protected final JSysDG sdg; +import java.util.HashSet; +import java.util.Optional; +import java.util.Set; +public class SummaryArcAnalyzer extends AbstractSummaryArcAnalyzer, SyntheticNode, SyntheticNode> { public SummaryArcAnalyzer(JSysDG sdg, CallGraph graph) { - super(graph); - this.sdg = sdg; - } - - @Override - protected Map, Set>> compute(CallGraph.Vertex vertex, Set predecessors) { - saveDeclaration(vertex); - return initialValue(vertex); + super(sdg, graph); } @Override - protected Map, Set>> initialValue(CallGraph.Vertex vertex) { - Map, Set>> value; - if (vertexDataMap.containsKey(vertex)) { - value = vertexDataMap.get(vertex); - } else { - value = new HashMap<>(); - for (var formalOut : getFormalOutNodes(vertex.getDeclaration())) - value.put(formalOut, new HashSet<>()); - } - value.replaceAll((key, oldValue) -> computeFormalIn(key)); - return value; - } - - /** Generate all summary arcs for a given call. Arc generation should be idempotent: - * if this method is called repeatedly it should not create duplicate summary arcs. */ - protected void saveDeclaration(CallGraph.Vertex vertex) { - var result = vertexDataMap.get(vertex); - for (CallGraph.Edge edge : graph.incomingEdgesOf(vertex)) { - for (var entry : result.entrySet()) { - var actualOutOpt = findOutputNode(edge, entry.getKey()); - if (actualOutOpt.isEmpty()) - continue; - for (var formalIn : entry.getValue()) { - var actualInOpt = findActualIn(edge, formalIn); - if (actualInOpt.isEmpty()) - continue; - if (!sdg.containsEdge(actualInOpt.get(), actualOutOpt.get())) - sdg.addSummaryArc(actualInOpt.get(), actualOutOpt.get()); - } - } - } - } - protected Set> getFormalOutNodes(CallableDeclaration declaration) { - Set> set = new HashSet<>(); - Stream.concat( - Stream.concat( - sdg.vertexSet().stream() // formal-out nodes - .filter(FormalIONode.class::isInstance) - .map(FormalIONode.class::cast) - .filter(FormalIONode::isOutput), - sdg.vertexSet().stream() // output nodes (the value returned) - .filter(OutputNode.class::isInstance) - .map(OutputNode.class::cast)), - sdg.vertexSet().stream() // normal/exception exit nodes (for exception handling) - .filter(ExitNode.class::isInstance) - .map(ExitNode.class::cast)) - // Only nodes that match the current declaration - .filter(node -> node.getAstNode() == declaration) - .forEach(set::add); + Set> set = super.getFormalOutNodes(declaration); for (var node : Set.copyOf(set)) { if (node.getVariableActions().isEmpty()) continue; @@ -94,14 +36,16 @@ public class SummaryArcAnalyzer extends BackwardDataFlowAnalysis> computeFormalIn(SyntheticNode formalOut) { Set> result = new HashSet<>(); - for (GraphNode graphNode : sdg.createSlicingAlgorithm().traverseProcedure(formalOut).getGraphNodes()) + for (GraphNode graphNode : ((JSysDG) sdg).createSlicingAlgorithm().traverseProcedure(formalOut).getGraphNodes()) if (isFormalIn(graphNode) && graphNode instanceof SyntheticNode) result.add((SyntheticNode) graphNode); return result; } + @Override protected Optional> findActualIn(CallGraph.Edge edge, SyntheticNode formalIn) { return sdg.incomingEdgesOf(formalIn).stream() .filter(Arc::isInterproceduralInputArc) @@ -111,6 +55,7 @@ public class SummaryArcAnalyzer extends BackwardDataFlowAnalysis> findOutputNode(CallGraph.Edge edge, SyntheticNode formalOut) { return sdg.outgoingEdgesOf(formalOut).stream() .filter(Arc::isInterproceduralOutputArc) diff --git a/sdg-core/src/main/java/es/upv/mist/slicing/graphs/sdg/AbstractSummaryArcAnalyzer.java b/sdg-core/src/main/java/es/upv/mist/slicing/graphs/sdg/AbstractSummaryArcAnalyzer.java new file mode 100644 index 0000000..6d393d4 --- /dev/null +++ b/sdg-core/src/main/java/es/upv/mist/slicing/graphs/sdg/AbstractSummaryArcAnalyzer.java @@ -0,0 +1,99 @@ +package es.upv.mist.slicing.graphs.sdg; + +import com.github.javaparser.ast.body.CallableDeclaration; +import es.upv.mist.slicing.graphs.BackwardDataFlowAnalysis; +import es.upv.mist.slicing.graphs.CallGraph; +import es.upv.mist.slicing.nodes.SyntheticNode; +import es.upv.mist.slicing.nodes.exceptionsensitive.ExitNode; +import es.upv.mist.slicing.nodes.io.FormalIONode; +import es.upv.mist.slicing.nodes.io.OutputNode; + +import java.util.*; +import java.util.stream.Stream; + +public abstract class AbstractSummaryArcAnalyzer, FormalOut extends SyntheticNode, FormalIn extends SyntheticNode> + extends BackwardDataFlowAnalysis, Map>> { + protected final SDG sdg; + + protected AbstractSummaryArcAnalyzer(SDG sdg, CallGraph graph) { + super(graph); + this.sdg = sdg; + } + + @Override + protected Map> compute(CallGraph.Vertex vertex, Set predecessors) { + saveDeclaration(vertex); + return initialValue(vertex); + } + + @Override + protected Map> initialValue(CallGraph.Vertex vertex) { + Map> value; + if (vertexDataMap.containsKey(vertex)) { + value = vertexDataMap.get(vertex); + } else { + value = new HashMap<>(); + for (var formalOut : getFormalOutNodes(vertex.getDeclaration())) + value.put(formalOut, new HashSet<>()); + } + value.replaceAll((key, oldValue) -> computeFormalIn(key)); + return value; + } + + /** Obtain all nodes that represent the output of a method declaration. These include formal-out, + * return nodes and normal/exception exit nodes (for exception handling). */ + protected Set getFormalOutNodes(CallableDeclaration declaration) { + Set set = new HashSet<>(); + Stream.concat( + Stream.concat( + sdg.vertexSet().stream() // formal-out nodes + .filter(FormalIONode.class::isInstance) + .map(FormalIONode.class::cast) + .filter(FormalIONode::isOutput), + sdg.vertexSet().stream() // output nodes (the value returned) + .filter(OutputNode.class::isInstance) + .map(OutputNode.class::cast)), + sdg.vertexSet().stream() // normal/exception exit nodes (for exception handling) + .filter(ExitNode.class::isInstance) + .map(ExitNode.class::cast)) + // Only nodes that match the current declaration + .filter(node -> node.getAstNode() == declaration) + .forEach(e -> set.add((FormalOut) e)); + return set; + } + + /** Given an output or formal-out node, locate the formal-in nodes it depends on. + * This search should be performed intra-procedurally, the parent class will take + * care of the rest of cases by adding summary arcs computed for other declarations. */ + protected abstract Set computeFormalIn(FormalOut formalOut); + + /** Generate all summary arcs for a given call. Arc generation should be idempotent: + * if this method is called repeatedly it should not create duplicate summary arcs. */ + protected void saveDeclaration(CallGraph.Vertex vertex) { + var result = vertexDataMap.get(vertex); + for (CallGraph.Edge edge : graph.incomingEdgesOf(vertex)) { + for (var entry : result.entrySet()) { + var actualOutOpt = findOutputNode(edge, entry.getKey()); + if (actualOutOpt.isEmpty()) + continue; + for (var formalIn : entry.getValue()) { + var actualInOpt = findActualIn(edge, formalIn); + if (actualInOpt.isEmpty()) + continue; + if (!sdg.containsEdge(actualInOpt.get(), actualOutOpt.get())) + sdg.addSummaryArc(actualInOpt.get(), actualOutOpt.get()); + } + } + } + } + + /** Find the actual-in that represents the given formal-in in the given call. + * There may not be one. In that case, the dependency between formal-in/out should + * not result in a summary arc. */ + protected abstract Optional findActualIn(CallGraph.Edge edge, FormalIn formalIn); + + /** Find the actual-out, return or exception/normal return node that represents the given + * formal-out, output or exception/normal exit node in the given call. There may not be one. + * In that case, the dependency between formal-in/out should not result in a summary arc. */ + protected abstract Optional> findOutputNode(CallGraph.Edge edge, FormalOut formalOut); +} diff --git a/sdg-core/src/main/java/es/upv/mist/slicing/graphs/sdg/CallConnector.java b/sdg-core/src/main/java/es/upv/mist/slicing/graphs/sdg/CallConnector.java index 0a92bdb..26d3c86 100644 --- a/sdg-core/src/main/java/es/upv/mist/slicing/graphs/sdg/CallConnector.java +++ b/sdg-core/src/main/java/es/upv/mist/slicing/graphs/sdg/CallConnector.java @@ -11,7 +11,6 @@ import es.upv.mist.slicing.nodes.io.ActualIONode; import es.upv.mist.slicing.nodes.io.CallNode; import es.upv.mist.slicing.nodes.io.FormalIONode; import es.upv.mist.slicing.nodes.io.OutputNode; -import es.upv.mist.slicing.utils.Logger; /** Adds interprocedural arcs between the 'PDG components' of an SDG. * Arcs generated include {@link ParameterInOutArc parameter input/output} and @@ -37,11 +36,6 @@ public class CallConnector { var callExpr = (Resolvable) callNode.getAstNode(); callGraph.getCallTargets(callExpr) .map(sdg::findNodeByASTNode) - .filter(opt -> { - if (opt.isEmpty()) - Logger.format("Method declaration not found: '%s'. Discarding", callExpr); - return opt.isPresent(); - }) .map(opt -> opt.orElseThrow(IllegalArgumentException::new)) .forEach(node -> connectCall(callNode, node)); } diff --git a/sdg-core/src/main/java/es/upv/mist/slicing/graphs/sdg/InterproceduralActionFinder.java b/sdg-core/src/main/java/es/upv/mist/slicing/graphs/sdg/InterproceduralActionFinder.java index 48dd13d..f6b0482 100644 --- a/sdg-core/src/main/java/es/upv/mist/slicing/graphs/sdg/InterproceduralActionFinder.java +++ b/sdg-core/src/main/java/es/upv/mist/slicing/graphs/sdg/InterproceduralActionFinder.java @@ -2,18 +2,14 @@ package es.upv.mist.slicing.graphs.sdg; import com.github.javaparser.ast.body.CallableDeclaration; import com.github.javaparser.ast.expr.Expression; -import com.github.javaparser.ast.expr.MethodCallExpr; -import com.github.javaparser.ast.expr.ThisExpr; -import com.github.javaparser.ast.stmt.ExplicitConstructorInvocationStmt; -import com.github.javaparser.resolution.Resolvable; import com.github.javaparser.resolution.UnsolvedSymbolException; -import com.github.javaparser.resolution.declarations.ResolvedMethodLikeDeclaration; import es.upv.mist.slicing.graphs.BackwardDataFlowAnalysis; import es.upv.mist.slicing.graphs.CallGraph; import es.upv.mist.slicing.graphs.cfg.CFG; import es.upv.mist.slicing.nodes.VariableAction; import es.upv.mist.slicing.utils.ASTUtils; import es.upv.mist.slicing.utils.Logger; +import es.upv.mist.slicing.utils.Utils; import java.util.*; import java.util.function.BiConsumer; @@ -31,7 +27,7 @@ public abstract class InterproceduralActionFinder exte /** A map from vertex and action to its corresponding stored action, to avoid generating duplicate nodes. */ protected final Map> actionStoredMap = new HashMap<>(); - public InterproceduralActionFinder(CallGraph callGraph, Map, CFG> cfgMap) { + protected InterproceduralActionFinder(CallGraph callGraph, Map, CFG> cfgMap) { super(callGraph); this.cfgMap = cfgMap; } @@ -88,59 +84,14 @@ public abstract class InterproceduralActionFinder exte // ============== AUXILIARY METHODS FOR CHILDREN ============= // =========================================================== - /** Given a call, obtains the scope. If none is present it may return null. - * ExpressionConstructorInvocations result in a this expression, as they - * may be seen as dynamic method calls that can modify 'this'. */ - protected static Expression obtainScope(Resolvable call) { - if (call instanceof MethodCallExpr) { - var methodCall = (MethodCallExpr) call; - return methodCall.getScope().orElse(null); - } else if (call instanceof ExplicitConstructorInvocationStmt) { - return new ThisExpr(); - } else { - throw new IllegalArgumentException("The given call is not of a valid type"); - } - } - /** Obtains the expression passed as argument for the given action at the given call. If {@code input} * is false, primitive parameters will be skipped, as their value cannot be redefined.*/ - protected Expression extractArgument(VariableAction action, CallGraph.Edge edge, boolean input) { + protected Optional extractArgument(VariableAction action, CallGraph.Edge edge, boolean input) { CallableDeclaration callTarget = graph.getEdgeTarget(edge).getDeclaration(); if (!input && action.isPrimitive()) - return null; // primitives do not have actual-out! + return Optional.empty(); // primitives do not have actual-out! int paramIndex = ASTUtils.getMatchingParameterIndex(callTarget, action.getName()); - return ASTUtils.getResolvableArgs(edge.getCall()).get(paramIndex); - } - - /** Generate the name that should be given to an object in a caller method, given an action - * in the callee method. This is used to transform a reference to 'this' into the scope - * of a method. */ - protected static String obtainAliasedFieldName(VariableAction action, CallGraph.Edge edge) { - if (edge.getCall() instanceof MethodCallExpr) { - Optional optScope = ((MethodCallExpr) edge.getCall()).getScope(); - return obtainAliasedFieldName(action, edge, optScope.isPresent() ? optScope.get().toString() : ""); - } else if (edge.getCall() instanceof ExplicitConstructorInvocationStmt) { - // The only possibility is 'this' or its fields, so we return empty scope and 'type.this.' is generated - return obtainAliasedFieldName(action, edge, ""); - } else { - throw new IllegalArgumentException("The given call is not of a valid type"); - } - } - - /** To be used by {@link #obtainAliasedFieldName(VariableAction, CallGraph.Edge)} exclusively.
- * Given a scope, name inside a method and call, translates the name of a variable, such that 'this' becomes - * the scope of the method. */ - protected static String obtainAliasedFieldName(VariableAction action, CallGraph.Edge edge, String scope) { - if (scope.isEmpty()) { - return action.getName(); - } else { - String newPrefix = scope; - newPrefix = newPrefix.replaceAll("((\\.)super|^super)(\\.)?", "$2this$3"); - String withPrefix = action.getName(); - String withoutPrefix = withPrefix.replaceFirst("^((.*\\.)?this\\.?)", ""); - String result = newPrefix + withoutPrefix; - return result.replaceFirst("this(\\.this)+", "this"); - } + return Optional.of(ASTUtils.getResolvableArgs(edge.getCall()).get(paramIndex)); } // =========================================================== @@ -158,8 +109,7 @@ public abstract class InterproceduralActionFinder
exte @Override protected Set initialValue(CallGraph.Vertex vertex) { CFG cfg = cfgMap.get(vertex.getDeclaration()); - if (cfg == null) - return Collections.emptySet(); + assert cfg != null; Stream actionStream = cfg.vertexSet().stream() // Ignore root node, it is literally the entrypoint for interprocedural actions. .filter(n -> n != cfg.getRootNode()) @@ -174,9 +124,7 @@ public abstract class InterproceduralActionFinder exte A a = it.next(); if (set.contains(a)) { if (a.hasObjectTree()) - for (A aFromSet : set) - if (aFromSet.hashCode() == a.hashCode() && Objects.equals(aFromSet, a)) - aFromSet.getObjectTree().addAll(a.getObjectTree()); + Utils.setGet(set, a).getObjectTree().addAll(a.getObjectTree()); } else { set.add(a.createCopy()); } 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 0c97501..2a248d5 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 @@ -41,21 +41,16 @@ public class InterproceduralDefinitionFinder extends InterproceduralActionFinder List movables = new LinkedList<>(); GraphNode graphNode = edge.getGraphNode(); if (def.isParameter()) { - Expression arg = extractArgument(def, edge, false); - if (arg == null) + Optional arg = extractArgument(def, edge, false); + if (arg.isEmpty()) return; - ActualIONode actualOut = ActualIONode.createActualOut(edge.getCall(), def.getName(), arg); - extractOutputVariablesAsMovables(arg, movables, graphNode, actualOut, def); + ActualIONode actualOut = 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 { - /* NEW */ - // An object creation expression doesn't alter an existing object via actual-out - // it is returned and assigned via -output-. - if (edge.getCall() instanceof ObjectCreationExpr) - return; - + assert !(edge.getCall() instanceof ObjectCreationExpr); ActualIONode actualOut = ActualIONode.createActualOut(edge.getCall(), def.getName(), null); Optional scope = ASTUtils.getResolvableScope(edge.getCall()); if (scope.isPresent()) { 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 ee9efd3..ea84b28 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 @@ -55,8 +55,7 @@ public class InterproceduralUsageFinder extends InterproceduralActionFinder> 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) { n.getScope().accept(this, variables); diff --git a/sdg-core/src/main/java/es/upv/mist/slicing/graphs/sdg/SDG.java b/sdg-core/src/main/java/es/upv/mist/slicing/graphs/sdg/SDG.java index 008169d..02236c2 100644 --- a/sdg-core/src/main/java/es/upv/mist/slicing/graphs/sdg/SDG.java +++ b/sdg-core/src/main/java/es/upv/mist/slicing/graphs/sdg/SDG.java @@ -20,7 +20,6 @@ import es.upv.mist.slicing.graphs.cfg.CFG; import es.upv.mist.slicing.graphs.pdg.PDG; import es.upv.mist.slicing.nodes.GraphNode; import es.upv.mist.slicing.nodes.SyntheticNode; -import es.upv.mist.slicing.nodes.io.ActualIONode; import es.upv.mist.slicing.slicing.*; import es.upv.mist.slicing.utils.ASTUtils; @@ -108,7 +107,7 @@ public class SDG extends Graph implements Sliceable, Buildable to) { + public void addSummaryArc(SyntheticNode from, SyntheticNode to) { this.addEdge(from, to, new SummaryArc()); } @@ -121,13 +120,13 @@ public class SDG extends Graph implements Sliceable, Buildable nodeList) { // See creation strategy at http://kaz2.dsic.upv.es:3000/Fzg46cQvT1GzHQG9hFnP1g#Using-data-flow-in-the-SDG // This ordering cannot be altered, as each step requires elements from the previous one. - createClassGraph(nodeList); // 0 - buildCFGs(nodeList); // 1 - callGraph = createCallGraph(nodeList); // 2 - dataFlowAnalysis(); // 3 - buildAndCopyPDGs(); // 4 - connectCalls(); // 5 - createSummaryArcs(); // 6 + createClassGraph(nodeList); // 0 + buildCFGs(nodeList); // 1 + createCallGraph(nodeList); // 2 + dataFlowAnalysis(); // 3 + buildAndCopyPDGs(); // 4 + connectCalls(); // 5 + createSummaryArcs(); // 6 } /** Build a CFG per declaration found in the list of compilation units. */ @@ -155,10 +154,9 @@ public class SDG extends Graph implements Sliceable, Buildable nodeList) { - CallGraph callGraph = new CallGraph(cfgMap, ClassGraph.getInstance()); + protected void createCallGraph(NodeList nodeList) { + callGraph = new CallGraph(cfgMap, ClassGraph.getInstance()); callGraph.build(nodeList); - return callGraph; } /** Create class graph from the list of compilation units. */ diff --git a/sdg-core/src/main/java/es/upv/mist/slicing/graphs/sdg/SummaryArcAnalyzer.java b/sdg-core/src/main/java/es/upv/mist/slicing/graphs/sdg/SummaryArcAnalyzer.java index c1ab83b..eaf7090 100644 --- a/sdg-core/src/main/java/es/upv/mist/slicing/graphs/sdg/SummaryArcAnalyzer.java +++ b/sdg-core/src/main/java/es/upv/mist/slicing/graphs/sdg/SummaryArcAnalyzer.java @@ -1,7 +1,6 @@ package es.upv.mist.slicing.graphs.sdg; import com.github.javaparser.ast.body.CallableDeclaration; -import es.upv.mist.slicing.graphs.BackwardDataFlowAnalysis; import es.upv.mist.slicing.graphs.CallGraph; import es.upv.mist.slicing.nodes.SyntheticNode; import es.upv.mist.slicing.nodes.exceptionsensitive.ExitNode; @@ -11,63 +10,16 @@ import es.upv.mist.slicing.nodes.io.CallNode; import es.upv.mist.slicing.nodes.io.FormalIONode; import es.upv.mist.slicing.nodes.io.OutputNode; -import java.util.*; +import java.util.Optional; +import java.util.Set; import java.util.stream.Collectors; -import java.util.stream.Stream; - -public class SummaryArcAnalyzer extends BackwardDataFlowAnalysis, Map>, Set>> { - protected final SDG sdg; +public class SummaryArcAnalyzer extends AbstractSummaryArcAnalyzer>, FormalIONode> { public SummaryArcAnalyzer(SDG sdg, CallGraph graph) { - super(graph); - this.sdg = sdg; - } - - @Override - protected Map>, Set> compute(CallGraph.Vertex vertex, Set predecessors) { - saveDeclaration(vertex); - return initialValue(vertex); + super(sdg, graph); } @Override - protected Map>, Set> initialValue(CallGraph.Vertex vertex) { - Map>, Set> value; - if (vertexDataMap.containsKey(vertex)) { - value = vertexDataMap.get(vertex); - } else { - value = new HashMap<>(); - for (var formalOut : getFormalOutNodes(vertex.getDeclaration())) - value.put(formalOut, new HashSet<>()); - } - value.replaceAll((key, oldValue) -> computeFormalIn(key)); - return value; - } - - /** Obtain all nodes that represent the output of a method declaration. These include formal-out, - * return nodes and normal/exception exit nodes (for exception handling). */ - protected Set>> getFormalOutNodes(CallableDeclaration declaration) { - Set>> set = new HashSet<>(); - Stream.concat( - Stream.concat( - sdg.vertexSet().stream() // formal-out nodes - .filter(FormalIONode.class::isInstance) - .map(FormalIONode.class::cast) - .filter(FormalIONode::isOutput), - sdg.vertexSet().stream() // output nodes (the value returned) - .filter(OutputNode.class::isInstance) - .map(OutputNode.class::cast)), - sdg.vertexSet().stream() // normal/exception exit nodes (for exception handling) - .filter(ExitNode.class::isInstance) - .map(ExitNode.class::cast)) - // Only nodes that match the current declaration - .filter(node -> node.getAstNode() == declaration) - .forEach(set::add); - return set; - } - - /** Given an output or formal-out node, locate the formal-in nodes it depends on. - * This search should be performed intra-procedurally, the parent class will take - * care of the rest of cases by adding summary arcs computed for other declarations. */ protected Set computeFormalIn(SyntheticNode> formalOut) { return sdg.createSlicingAlgorithm().traverseProcedure(formalOut).getGraphNodes().stream() .filter(FormalIONode.class::isInstance) @@ -76,29 +28,7 @@ public class SummaryArcAnalyzer extends BackwardDataFlowAnalysis edge : graph.incomingEdgesOf(vertex)) { - for (var entry : result.entrySet()) { - var actualOutOpt = findOutputNode(edge, entry.getKey()); - if (actualOutOpt.isEmpty()) - continue; - for (var formalIn : entry.getValue()) { - var actualInOpt = findActualIn(edge, formalIn); - if (actualInOpt.isEmpty()) - continue; - if (!sdg.containsEdge(actualInOpt.get(), actualOutOpt.get())) - sdg.addSummaryArc(actualInOpt.get(), actualOutOpt.get()); - } - } - } - } - - /** Find the actual-in that represents the given formal-in in the given call. - * There may not be one. In that case, the dependency between formal-in/out should - * not result in a summary arc. */ + @Override protected Optional findActualIn(CallGraph.Edge edge, FormalIONode formalIn) { return sdg.vertexSet().stream() .filter(ActualIONode.class::isInstance) @@ -108,9 +38,7 @@ public class SummaryArcAnalyzer extends BackwardDataFlowAnalysis> findOutputNode(CallGraph.Edge edge, SyntheticNode> formalOut) { if (formalOut instanceof FormalIONode) return findActualOut(edge, (FormalIONode) formalOut); diff --git a/sdg-core/src/main/java/es/upv/mist/slicing/nodes/GraphNode.java b/sdg-core/src/main/java/es/upv/mist/slicing/nodes/GraphNode.java index c77b250..eebac12 100644 --- a/sdg-core/src/main/java/es/upv/mist/slicing/nodes/GraphNode.java +++ b/sdg-core/src/main/java/es/upv/mist/slicing/nodes/GraphNode.java @@ -142,21 +142,6 @@ public class GraphNode implements Comparable> { throw new IllegalArgumentException("Could not find markers for " + call.resolve().getSignature() + " in " + this); } - /** Append the given actions to after the actions of the given call. */ - public void addActionsAfterCall(Resolvable call, VariableAction... actions) { - for (int i = 0; i < variableActions.size(); i++) { - VariableAction var = variableActions.get(i); - if (var instanceof VariableAction.CallMarker) { - VariableAction.CallMarker marker = (VariableAction.CallMarker) var; - if (marker.getCall().equals(call) && !marker.isEnter()) { - variableActions.addAll(i + 1, List.of(actions)); - return; - } - } - } - throw new IllegalArgumentException("Could not find markers for " + call.resolve().getSignature() + " in " + this); - } - public void addSyntheticNode(SyntheticNode node) { syntheticNodesInMovables.add(node); } @@ -194,19 +179,6 @@ public class GraphNode implements Comparable> { } } - public VariableAction.CallMarker locateCallForVariableAction(VariableAction action) { - VariableAction.CallMarker marker = null; - for (VariableAction a : variableActions) { - if (a instanceof VariableAction.CallMarker && ((VariableAction.CallMarker) a).isEnter()) - marker = (VariableAction.CallMarker) a; - if (action == a) { - assert marker != null; - return marker; - } - } - throw new IllegalArgumentException("Action not found within node"); - } - public void addVADefineActiveException(Expression expression) { VariableAction.Definition def = new VariableAction.Definition(VariableAction.DeclarationType.SYNTHETIC, ACTIVE_EXCEPTION_VARIABLE, this, expression); variableActions.add(def); 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 bce69a7..a1478ee 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 @@ -13,8 +13,7 @@ public class ObjectTree implements Cloneable { private static final Pattern FIELD_SPLIT = Pattern.compile("^(?(([_0-9A-Za-z]+\\.)*this)|([_0-9A-Za-z]+)|(-root-))(\\.(?.+))?$"); private final Map childrenMap = new HashMap<>(); - - private MemberNode memberNode; + private final MemberNode memberNode; public ObjectTree() { this(ROOT_NAME); @@ -36,16 +35,6 @@ public class ObjectTree implements Cloneable { return memberNode; } - public void setMemberNode(MemberNode memberNode) { - GraphNode oldParent = null; - if (this.memberNode != null) - oldParent = this.memberNode.getParent(); - this.memberNode = memberNode; - if (oldParent != null) - this.memberNode.setParent(oldParent); - childrenMap.values().forEach(ot -> ot.memberNode.setParent(memberNode)); - } - public boolean hasChildren() { return !childrenMap.isEmpty(); } @@ -75,10 +64,6 @@ public class ObjectTree implements Cloneable { childrenMap.put(entry.getKey(), entry.getValue().clone(this)); } - public boolean isLeaf(String memberWithoutRoot) { - return findObjectTreeOfMember(memberWithoutRoot).childrenMap.isEmpty(); - } - /** * Copies a subtree from source into another subtree in target. * @@ -147,10 +132,6 @@ public class ObjectTree implements Cloneable { } } - public boolean isEmpty() { - return childrenMap.isEmpty(); - } - public Iterable nameIterable() { return () -> new Iterator<>() { final Iterator it = treeIterator(); @@ -169,12 +150,12 @@ public class ObjectTree implements Cloneable { return ROOT_NAME; else builder.append(node.getLabel()); - while (node.getParent() != null && node.getParent() instanceof MemberNode && node.getParent().getLabel().matches("^(USE|DEF|DEC)\\{")) { + while (node.getParent() instanceof MemberNode && node.getParent().getLabel().matches("^(USE|DEF|DEC)\\{")) { node = (MemberNode) node.getParent(); builder.insert(0, '.'); builder.insert(0, node.getLabel()); } - return builder.insert(0, "-root-.").toString(); + return builder.insert(0, ROOT_NAME + ".").toString(); } }; } diff --git a/sdg-core/src/main/java/es/upv/mist/slicing/nodes/ValueConnection.java b/sdg-core/src/main/java/es/upv/mist/slicing/nodes/ValueConnection.java index 171f95c..fd829f5 100644 --- a/sdg-core/src/main/java/es/upv/mist/slicing/nodes/ValueConnection.java +++ b/sdg-core/src/main/java/es/upv/mist/slicing/nodes/ValueConnection.java @@ -3,16 +3,15 @@ package es.upv.mist.slicing.nodes; import es.upv.mist.slicing.arcs.pdg.FlowDependencyArc; import es.upv.mist.slicing.graphs.jsysdg.JSysPDG; +import static es.upv.mist.slicing.nodes.ObjectTree.ROOT_NAME; + public class ValueConnection implements VariableAction.PDGConnection { protected final VariableAction action; protected final String member; public ValueConnection(VariableAction action, String member) { this.action = action; - if (member.isEmpty()) - this.member = "-root-"; - else - this.member = "-root-." + member; + this.member = member.isEmpty() ? ROOT_NAME : ROOT_NAME + "." + member; } @Override diff --git a/sdg-core/src/main/java/es/upv/mist/slicing/nodes/VariableAction.java b/sdg-core/src/main/java/es/upv/mist/slicing/nodes/VariableAction.java index 1ff490e..875e707 100644 --- a/sdg-core/src/main/java/es/upv/mist/slicing/nodes/VariableAction.java +++ b/sdg-core/src/main/java/es/upv/mist/slicing/nodes/VariableAction.java @@ -167,12 +167,6 @@ public abstract class VariableAction { return objectTree != null; } - public boolean isObjectTreeEmpty() { - if (!hasObjectTree()) - return true; - return getObjectTree().isEmpty(); - } - public void setPDGTreeConnectionTo(VariableAction targetAction, String sourcePrefixWithoutRoot, String targetPrefixWithoutRoot) { pdgTreeConnections.add(new ObjectTreeConnection(this, targetAction, sourcePrefixWithoutRoot, targetPrefixWithoutRoot)); } 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 63e8634..8afd7e5 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 @@ -31,6 +31,7 @@ import java.util.Optional; import static es.upv.mist.slicing.graphs.cfg.CFGBuilder.VARIABLE_NAME_OUTPUT; import static es.upv.mist.slicing.graphs.exceptionsensitive.ESCFG.ACTIVE_EXCEPTION_VARIABLE; +import static es.upv.mist.slicing.nodes.ObjectTree.ROOT_NAME; import static es.upv.mist.slicing.nodes.VariableAction.DeclarationType.*; import static es.upv.mist.slicing.nodes.VariableVisitor.Action.*; @@ -180,7 +181,7 @@ public class VariableVisitor extends GraphNodeContentVisitor l.accept(this, arg)); } @@ -247,8 +250,7 @@ public class VariableVisitor extends GraphNodeContentVisitor (ObjectTree) tree.clone()).orElse(null)); - def.setTotallyDefinedMember("-root-"); + def.setTotallyDefinedMember(ROOT_NAME); var defMov = new VariableAction.Movable(def, CallNode.Return.create(call)); graphNode.addVariableAction(defMov); // The container of the call uses -output-, unless the call is wrapped in an ExpressionStmt @@ -571,7 +573,6 @@ public class VariableVisitor extends GraphNodeContentVisitor list = graphNode.getVariableActions(); - return ((VariableAction.Definition) list.get(list.size() - 1)); + return graphNode.getLastVariableAction().asDefinition(); } } diff --git a/sdg-core/src/main/java/es/upv/mist/slicing/nodes/io/ActualIONode.java b/sdg-core/src/main/java/es/upv/mist/slicing/nodes/io/ActualIONode.java index aa1ee4b..fc30e15 100644 --- a/sdg-core/src/main/java/es/upv/mist/slicing/nodes/io/ActualIONode.java +++ b/sdg-core/src/main/java/es/upv/mist/slicing/nodes/io/ActualIONode.java @@ -4,9 +4,8 @@ import com.github.javaparser.ast.Node; import com.github.javaparser.ast.body.BodyDeclaration; import com.github.javaparser.ast.expr.Expression; import com.github.javaparser.resolution.Resolvable; -import com.github.javaparser.resolution.declarations.ResolvedConstructorDeclaration; -import com.github.javaparser.resolution.declarations.ResolvedMethodDeclaration; import com.github.javaparser.resolution.declarations.ResolvedMethodLikeDeclaration; +import es.upv.mist.slicing.utils.ASTUtils; import java.util.Objects; @@ -34,14 +33,10 @@ public class ActualIONode extends IONode { && Objects.equals(o.getAstNode(), resolvedASTNode()); } + @SuppressWarnings("unchecked") protected BodyDeclaration resolvedASTNode() { - @SuppressWarnings("unchecked") - ResolvedMethodLikeDeclaration declaration = ((Resolvable) astNode).resolve(); - if (declaration instanceof ResolvedConstructorDeclaration) - return ((ResolvedConstructorDeclaration) declaration).toAst().orElse(null); - else if (declaration instanceof ResolvedMethodDeclaration) - return ((ResolvedMethodDeclaration) declaration).toAst().orElse(null); - throw new IllegalStateException("AST node of invalid type"); + return ASTUtils.getResolvedAST(((Resolvable) astNode).resolve()) + .orElse(null); } @Override diff --git a/sdg-core/src/main/java/es/upv/mist/slicing/nodes/type/NodeType.java b/sdg-core/src/main/java/es/upv/mist/slicing/nodes/type/NodeType.java deleted file mode 100644 index a393177..0000000 --- a/sdg-core/src/main/java/es/upv/mist/slicing/nodes/type/NodeType.java +++ /dev/null @@ -1,53 +0,0 @@ -package es.upv.mist.slicing.nodes.type; - -public enum NodeType { - /** An instruction in the program. Statements, predicates and - * pseudo-predicates are included in this {@link NodeType}. */ - STATEMENT, - /** The Enter node of a method. */ - METHOD_ENTER, - /** The Exit node of a method. */ - METHOD_EXIT, - /** The collector for natural exits of a method. */ - METHOD_NORMAL_EXIT, - /** The collector for exceptional exits of a given type of a method. */ - METHOD_EXCEPTION_EXIT, - /** A method call, that is contained in a {@link #STATEMENT} node. */ - METHOD_CALL, - /** An argument or globally accessible variable that - * has been used in a method call. */ - ACTUAL_IN, - /** An argument or globally accessible variable that - * has been modified in a method call. */ - ACTUAL_OUT, - /** An argument or globally accessible variable that - * has been used in a method declaration. */ - FORMAL_IN, - /** An argument or globally accessible variable that - * has been modified in a method declaration. */ - FORMAL_OUT, - /** The representation of all normal exits from a method. */ - METHOD_CALL_NORMAL_RETURN, - /** The representation of all exceptional exits from a method. It may be split by type. */ - METHOD_CALL_EXCEPTION_RETURN, - /** A node representing the return value of a non-void method call. */ - METHOD_CALL_RETURN, - /** A node representing the return value of a non-void method declaration. */ - METHOD_OUTPUT; - - public boolean is(NodeType type) { - if (this == type) { - return true; - } - - if (this == METHOD_CALL_RETURN) { - return type == ACTUAL_OUT; - } - - if (this == METHOD_OUTPUT) { - return type == FORMAL_OUT; - } - - return false; - } -} diff --git a/sdg-core/src/main/java/es/upv/mist/slicing/slicing/Slice.java b/sdg-core/src/main/java/es/upv/mist/slicing/slicing/Slice.java index 2638e45..1f271da 100644 --- a/sdg-core/src/main/java/es/upv/mist/slicing/slicing/Slice.java +++ b/sdg-core/src/main/java/es/upv/mist/slicing/slicing/Slice.java @@ -33,11 +33,6 @@ public class Slice { return map.containsKey(node.getId()); } - /** Whether the slice contains the given AST node. */ - public boolean contains(Node node) { - return map.values().stream().anyMatch(gn -> gn.getAstNode() == node); - } - @Override public int hashCode() { return map.hashCode(); diff --git a/sdg-core/src/main/java/es/upv/mist/slicing/slicing/SlicePruneVisitor.java b/sdg-core/src/main/java/es/upv/mist/slicing/slicing/SlicePruneVisitor.java index f5569b0..2d96a34 100644 --- a/sdg-core/src/main/java/es/upv/mist/slicing/slicing/SlicePruneVisitor.java +++ b/sdg-core/src/main/java/es/upv/mist/slicing/slicing/SlicePruneVisitor.java @@ -19,13 +19,10 @@ import java.util.stream.Collectors; public class SlicePruneVisitor extends ModifierVisitor> { // ========== Utility methods ========== - /** Place a valid placeholder in this node's body, if any. */ - protected void fillBody(Node n) { - if (!(n instanceof NodeWithBody)) - return; - NodeWithBody nb = ((NodeWithBody) n); - if (nb.getBody() == null) - nb.setBody(new EmptyStmt()); + /** Place a valid placeholder in this node's body, if there is none. */ + protected void fillBody(NodeWithBody n) { + if (n.getBody() == null) + n.setBody(new EmptyStmt()); } // ========== File visitors ========== 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 8d8aea6..3ab147b 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 @@ -1,6 +1,7 @@ package es.upv.mist.slicing.utils; import com.github.javaparser.ast.Node; +import com.github.javaparser.ast.NodeList; import com.github.javaparser.ast.body.*; import com.github.javaparser.ast.expr.*; import com.github.javaparser.ast.stmt.*; @@ -23,13 +24,6 @@ public class ASTUtils { throw new UnsupportedOperationException("This is a static, utility class"); } - public static boolean isContained(Node upper, Node contained) { - Optional parent = contained.getParentNode(); - if (parent.isEmpty()) - return false; - return equalsWithRangeInCU(upper, parent.get()) || isContained(upper, parent.get()); - } - public static boolean switchHasDefaultCase(SwitchStmt stmt) { return switchGetDefaultCase(stmt) != null; } @@ -71,15 +65,6 @@ public class ASTUtils { throw new IllegalArgumentException("Call didn't resolve to either method or constructor!"); } - public static boolean resolvableIsPrimitive(Resolvable call) { - var resolved = call.resolve(); - if (resolved instanceof ResolvedMethodDeclaration) - return ((ResolvedMethodDeclaration) resolved).getReturnType().isPrimitive(); - if (resolved instanceof ResolvedConstructorDeclaration) - return false; - throw new IllegalArgumentException("Call didn't resolve to either method or constructor!"); - } - public static boolean declarationReturnIsObject(CallableDeclaration declaration) { if (declaration.isMethodDeclaration()) return declaration.asMethodDeclaration().getType().isClassOrInterfaceType(); @@ -141,8 +126,8 @@ public class ASTUtils { } public static boolean constructorHasExplicitConstructorInvocation(ConstructorDeclaration declaration) { - return !getCallableBody(declaration).getStatements().isEmpty() && - getCallableBody(declaration).getStatements().getFirst().get() instanceof ExplicitConstructorInvocationStmt; + final NodeList statements = declaration.getBody().getStatements(); + return !statements.isEmpty() && statements.get(0) instanceof ExplicitConstructorInvocationStmt; } /** diff --git a/sdg-core/src/main/java/es/upv/mist/slicing/utils/Logger.java b/sdg-core/src/main/java/es/upv/mist/slicing/utils/Logger.java index 20dd030..6c60d5d 100644 --- a/sdg-core/src/main/java/es/upv/mist/slicing/utils/Logger.java +++ b/sdg-core/src/main/java/es/upv/mist/slicing/utils/Logger.java @@ -38,12 +38,7 @@ public class Logger { } public static void log(String context, String message) { - printStreams.forEach(out -> out.println( - String.format("%s%s", - context.isEmpty() ? "" : String.format("[%s]: ", context), - message - ) - )); + printStreams.forEach(out -> out.println((context.isEmpty() ? "" : "[" + context + "]: ") + message)); } public static void format(String message, Object... args) { diff --git a/sdg-core/src/main/java/es/upv/mist/slicing/utils/Utils.java b/sdg-core/src/main/java/es/upv/mist/slicing/utils/Utils.java index d5f2dc8..57d6e8f 100644 --- a/sdg-core/src/main/java/es/upv/mist/slicing/utils/Utils.java +++ b/sdg-core/src/main/java/es/upv/mist/slicing/utils/Utils.java @@ -1,12 +1,10 @@ package es.upv.mist.slicing.utils; +import es.upv.mist.slicing.nodes.VariableAction; import org.jgrapht.nio.Attribute; import org.jgrapht.nio.DefaultAttribute; -import java.util.HashMap; -import java.util.Iterator; -import java.util.Map; -import java.util.Set; +import java.util.*; /** General utilities. */ public class Utils { @@ -29,4 +27,11 @@ public class Utils { map.put("label", DefaultAttribute.createAttribute(label)); return map; } + + public static A setGet(Set set, A action) { + for (A aFromSet : set) + if (aFromSet.hashCode() == action.hashCode() && Objects.equals(aFromSet, action)) + return aFromSet; + throw new NoSuchElementException("Could not locate " + action + " in set."); + } } -- GitLab From a82a331c00d72c412a8394b0eae5ef4c6a425f7a Mon Sep 17 00:00:00 2001 From: Carlos Galindo Date: Wed, 17 Mar 2021 20:41:20 +0100 Subject: [PATCH 60/60] Documentation and minor changes * Some assertions changed to Illegal{State,Argument}Exception. --- .../slicing/arcs/pdg/FlowDependencyArc.java | 1 + .../arcs/pdg/ObjectFlowDependencyArc.java | 2 + .../mist/slicing/arcs/pdg/StructuralArc.java | 3 + .../pdg/TotalDefinitionDependenceArc.java | 3 + .../slicing/arcs/sdg/ParameterInOutArc.java | 3 + .../upv/mist/slicing/graphs/ClassGraph.java | 1 + .../graphs/ExpressionObjectTreeFinder.java | 61 ++++++++++++++++++- .../mist/slicing/graphs/augmented/ACFG.java | 2 + .../es/upv/mist/slicing/graphs/cfg/CFG.java | 1 + .../mist/slicing/graphs/cfg/CFGBuilder.java | 3 + .../graphs/exceptionsensitive/ESCFG.java | 1 + .../mist/slicing/graphs/jsysdg/JSysCFG.java | 21 +++++++ .../graphs/jsysdg/JSysCallConnector.java | 16 +++++ .../mist/slicing/graphs/jsysdg/JSysPDG.java | 6 +- .../graphs/jsysdg/SummaryArcAnalyzer.java | 4 ++ .../sdg/AbstractSummaryArcAnalyzer.java | 6 ++ .../sdg/InterproceduralDefinitionFinder.java | 3 + .../sdg/InterproceduralUsageFinder.java | 1 + .../graphs/sdg/SummaryArcAnalyzer.java | 3 + .../es/upv/mist/slicing/nodes/GraphNode.java | 19 ++++-- .../es/upv/mist/slicing/nodes/ObjectTree.java | 59 ++++++++++++++++++ .../slicing/nodes/ObjectTreeConnection.java | 4 ++ .../mist/slicing/nodes/ValueConnection.java | 2 + .../mist/slicing/nodes/VariableAction.java | 13 +++- .../upv/mist/slicing/nodes/oo/MemberNode.java | 3 + .../java/es/upv/mist/slicing/utils/Utils.java | 12 ++-- 26 files changed, 239 insertions(+), 14 deletions(-) diff --git a/sdg-core/src/main/java/es/upv/mist/slicing/arcs/pdg/FlowDependencyArc.java b/sdg-core/src/main/java/es/upv/mist/slicing/arcs/pdg/FlowDependencyArc.java index dbd9213..27a1174 100644 --- a/sdg-core/src/main/java/es/upv/mist/slicing/arcs/pdg/FlowDependencyArc.java +++ b/sdg-core/src/main/java/es/upv/mist/slicing/arcs/pdg/FlowDependencyArc.java @@ -6,6 +6,7 @@ import org.jgrapht.nio.DefaultAttribute; import java.util.Map; +/** Represents a data dependency in an object-oriented SDG or PDG. */ public class FlowDependencyArc extends Arc { public FlowDependencyArc() { super(); diff --git a/sdg-core/src/main/java/es/upv/mist/slicing/arcs/pdg/ObjectFlowDependencyArc.java b/sdg-core/src/main/java/es/upv/mist/slicing/arcs/pdg/ObjectFlowDependencyArc.java index 5214519..b2dd036 100644 --- a/sdg-core/src/main/java/es/upv/mist/slicing/arcs/pdg/ObjectFlowDependencyArc.java +++ b/sdg-core/src/main/java/es/upv/mist/slicing/arcs/pdg/ObjectFlowDependencyArc.java @@ -6,6 +6,8 @@ import org.jgrapht.nio.DefaultAttribute; import java.util.Map; +/** Represents a data dependency between objects or between a field + * and its parent in an object oriented SDG or PDG. */ public class ObjectFlowDependencyArc extends Arc { public ObjectFlowDependencyArc() { super(); diff --git a/sdg-core/src/main/java/es/upv/mist/slicing/arcs/pdg/StructuralArc.java b/sdg-core/src/main/java/es/upv/mist/slicing/arcs/pdg/StructuralArc.java index 85b91c2..904ef1a 100644 --- a/sdg-core/src/main/java/es/upv/mist/slicing/arcs/pdg/StructuralArc.java +++ b/sdg-core/src/main/java/es/upv/mist/slicing/arcs/pdg/StructuralArc.java @@ -6,6 +6,9 @@ import org.jgrapht.nio.DefaultAttribute; import java.util.Map; +/** Replaces control dependencies that were used to generate a tree-like + * structure in the PDG and SDG. Some examples include the connection + * of actual/formal-in/out and object trees. They should always be traversed. */ public class StructuralArc extends Arc { @Override public Map getDotAttributes() { diff --git a/sdg-core/src/main/java/es/upv/mist/slicing/arcs/pdg/TotalDefinitionDependenceArc.java b/sdg-core/src/main/java/es/upv/mist/slicing/arcs/pdg/TotalDefinitionDependenceArc.java index 74d40d8..4d19a6d 100644 --- a/sdg-core/src/main/java/es/upv/mist/slicing/arcs/pdg/TotalDefinitionDependenceArc.java +++ b/sdg-core/src/main/java/es/upv/mist/slicing/arcs/pdg/TotalDefinitionDependenceArc.java @@ -6,6 +6,9 @@ import org.jgrapht.nio.DefaultAttribute; import java.util.Map; +/** Represents a dependence where the source completely defines + * the target. This is used when the type of an object or just + * its existence is required, but not any specific field. */ public class TotalDefinitionDependenceArc extends Arc { @Override public Map getDotAttributes() { diff --git a/sdg-core/src/main/java/es/upv/mist/slicing/arcs/sdg/ParameterInOutArc.java b/sdg-core/src/main/java/es/upv/mist/slicing/arcs/sdg/ParameterInOutArc.java index 17573e2..23f8001 100644 --- a/sdg-core/src/main/java/es/upv/mist/slicing/arcs/sdg/ParameterInOutArc.java +++ b/sdg-core/src/main/java/es/upv/mist/slicing/arcs/sdg/ParameterInOutArc.java @@ -38,6 +38,9 @@ public class ParameterInOutArc extends InterproceduralArc { return graphNode; } + /** Represents an input/output arc with an additional traversal restriction. + * It merges the functions of an interprocedural input/output arc and an + * object-flow arc. */ public static class ObjectFlow extends ParameterInOutArc { @Override public boolean isObjectFlow() { diff --git a/sdg-core/src/main/java/es/upv/mist/slicing/graphs/ClassGraph.java b/sdg-core/src/main/java/es/upv/mist/slicing/graphs/ClassGraph.java index fb4ce67..726e0b4 100644 --- a/sdg-core/src/main/java/es/upv/mist/slicing/graphs/ClassGraph.java +++ b/sdg-core/src/main/java/es/upv/mist/slicing/graphs/ClassGraph.java @@ -22,6 +22,7 @@ import java.util.stream.Stream; public class ClassGraph extends DirectedPseudograph implements Buildable> { private static ClassGraph instance = null; + /** Generates and returns a new class graph. This destroys the reference to the previous instance. */ public static ClassGraph getNewInstance() { instance = null; return getInstance(); 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 07ff6cb..2d4fb0f 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 @@ -18,16 +18,49 @@ import java.util.List; import static es.upv.mist.slicing.graphs.cfg.CFGBuilder.VARIABLE_NAME_OUTPUT; +/** + * A helper class that locates and connects all object trees that can be the output of an expression. + *
+ * Examples: + *
    + *
  • {@code a} results on that variable being linked.
  • + *
  • {@code a ? b : c} links both {@code b} and {@code c}.
  • + *
  • {@code a.b} links the subtree of {@code b}.
  • + *
  • {@code f()} links the output of the call.
  • + *
+ * These patterns can be mixed in any order. + *
+ *

Practical usage

+ * This class can be used to link assignments, declarations and other statements, + * to link, for example, the expression in {@code return a ? b : c} to the -output- + * variable action. This linkage must be performed in the same GraphNode, so the previous + * return would have a DEF(-output-), and that action is the target of the links setup + * by this class. + *

Assignments, declarations

+ * Generate a new object and use the method {@link #handleAssignExpr(AssignExpr, VariableAction, String) handleAssignExpr()}, + * {@link #handleVariableDeclarator(VariableDeclarator, String) handleVariableDeclarator()} or {@link #handleArrayAssignExpr(AssignExpr) handleArrayAssignExpr()}. + *

Other statements

+ * Generate a new object and use either {@link #locateAndMarkTransferenceToRoot(Expression, VariableAction)}, to link to + * the given variable action or {@link #locateAndMarkTransferenceToRoot(Expression, int)} to extract the variable action + * from the graph node used in the constructor. This is used for return and throw statements, and actual-in/out nodes. + */ public class ExpressionObjectTreeFinder { + /** The node that contains both the expression to be scanned, the source variable action and + * the desired variable action target. */ protected final GraphNode graphNode; + /** Creates a new ExpressionObjectTreeFinder for the given GraphNode. */ public ExpressionObjectTreeFinder(GraphNode graphNode) { this.graphNode = graphNode; } + /** Prepares the connection between the right-hand side of a variable declarator and the variable. + * The variable declarator must have an initializer, and the realName indicates the absolute (fields + * prefixed by 'this.') name of the variable being declared. */ public void handleVariableDeclarator(VariableDeclarator variableDeclarator, String realName) { - assert variableDeclarator.getInitializer().isPresent(); + if (variableDeclarator.getInitializer().isEmpty()) + throw new IllegalArgumentException("The variableDeclarator must have an initializer!"); VariableAction targetAction = locateVAVariableDeclarator(realName); ClassGraph.getInstance().generateObjectTreeForType(variableDeclarator.getType().resolve()) .ifPresent(objectTree -> targetAction.getObjectTree().addAll(objectTree)); @@ -35,6 +68,8 @@ public class ExpressionObjectTreeFinder { .forEach(pair -> markTransference(pair, targetAction, "")); } + /** Finds the variable action that corresponds to the definition of a variable + * in a VariableDeclarator with initializer. */ protected VariableAction locateVAVariableDeclarator(String realName) { String root = realName.contains(".") ? ObjectTree.removeFields(realName) : realName; boolean foundDecl = false; @@ -50,10 +85,15 @@ public class ExpressionObjectTreeFinder { lastDef = a; } } - assert lastDef != null: "Could not find DEF for variable declaration of " + realName; + if (lastDef == null) + throw new IllegalStateException("Could not find DEF for variable declaration of " + realName); return lastDef; } + /** Prepares the connection between the right-hand side of an assignment to the left-hand side. + * The caller must provide the variable action that represents the definition of the variable action. + * If the LHS of this assignment is an array access expression, the method + * {@link #handleArrayAssignExpr(AssignExpr)} should be used. */ public void handleAssignExpr(AssignExpr assignExpr, VariableAction assignTarget, String targetMember) { ClassGraph.getInstance().generateObjectTreeForType(assignExpr.getTarget().calculateResolvedType()) .ifPresent(fields -> assignTarget.getObjectTree().addAll(fields)); @@ -61,21 +101,36 @@ public class ExpressionObjectTreeFinder { .forEach(pair -> markTransference(pair, assignTarget, targetMember)); } + /** Prepares the connection between the right-hand side of the assignment and the GraphNode + * that represents the assignment, as currently array access expressions are treated as primitives. */ public void handleArrayAssignExpr(AssignExpr assignExpr) { locateExpressionResultTrees(assignExpr.getValue()) .forEach(pair -> pair.a.setPDGValueConnection(pair.b)); } + /** + * Finds the variable action corresponding to the given index in the GraphNode used in the constructor + * and then connects each valid tree found in the given expression to the former variable action. + * Negative indices may be used, and will access variable actions from the end of the GraphNode's list, + * starting at -1 for the tail of the list. + */ public void locateAndMarkTransferenceToRoot(Expression expr, int index) { List list = graphNode.getVariableActions(); locateAndMarkTransferenceToRoot(expr, list.get(index < 0 ? list.size() + index : index)); } + /** Connects each valid tree found in the given expression to the given variable action. */ public void locateAndMarkTransferenceToRoot(Expression expression, VariableAction targetAction) { locateExpressionResultTrees(expression) .forEach(pair -> markTransference(pair, targetAction, "")); } + /** + * Finds object trees that correspond to the output of the given expression. + * @param expression An expression that outputs an object. + * @return A list of pairs, containing the variable actions and the member of such + * actions where the link must be placed. + */ protected List> locateExpressionResultTrees(Expression expression) { List> list = new LinkedList<>(); expression.accept(new VoidVisitorAdapter() { @@ -191,6 +246,8 @@ public class ExpressionObjectTreeFinder { return list; } + /** Prepares a tree connection, to be applied when the PDG is built. This class is used in the construction + * of the CFG, and the object trees are not yet placed, so the arcs cannot be generated yet. */ protected void markTransference(Pair sourcePair, VariableAction targetAction, String targetMember) { VariableAction sourceAction = sourcePair.a; String sourceMember = sourcePair.b; diff --git a/sdg-core/src/main/java/es/upv/mist/slicing/graphs/augmented/ACFG.java b/sdg-core/src/main/java/es/upv/mist/slicing/graphs/augmented/ACFG.java index b2a8c47..8ebbb6e 100644 --- a/sdg-core/src/main/java/es/upv/mist/slicing/graphs/augmented/ACFG.java +++ b/sdg-core/src/main/java/es/upv/mist/slicing/graphs/augmented/ACFG.java @@ -35,6 +35,8 @@ public class ACFG extends CFG { return outgoingEdgesOf(node).stream().filter(Arc::isNonExecutableControlFlowArc).count() == 1; } + /** Whether the given node is a predicate or not. A node is a predicate if + * it has more than one outgoing edge and it is not a pseudo-predicate. */ @Override public boolean isPredicate(GraphNode graphNode) { return super.isPredicate(graphNode) && !isPseudoPredicate(graphNode); diff --git a/sdg-core/src/main/java/es/upv/mist/slicing/graphs/cfg/CFG.java b/sdg-core/src/main/java/es/upv/mist/slicing/graphs/cfg/CFG.java index 6a419b0..347fbea 100644 --- a/sdg-core/src/main/java/es/upv/mist/slicing/graphs/cfg/CFG.java +++ b/sdg-core/src/main/java/es/upv/mist/slicing/graphs/cfg/CFG.java @@ -54,6 +54,7 @@ public class CFG extends GraphWithRootNode> { addEdge(from, to, arc); } + /** Whether the given node is a predicate or not. A node is a predicate if it has more than one outgoing edge. */ public boolean isPredicate(GraphNode graphNode) { return outgoingEdgesOf(graphNode).size() > 1; } diff --git a/sdg-core/src/main/java/es/upv/mist/slicing/graphs/cfg/CFGBuilder.java b/sdg-core/src/main/java/es/upv/mist/slicing/graphs/cfg/CFGBuilder.java index 494ed55..f34565a 100644 --- a/sdg-core/src/main/java/es/upv/mist/slicing/graphs/cfg/CFGBuilder.java +++ b/sdg-core/src/main/java/es/upv/mist/slicing/graphs/cfg/CFGBuilder.java @@ -363,16 +363,19 @@ public class CFGBuilder extends VoidVisitorAdapter { buildExit(callableDeclaration); } + /** Generate the ENTER node and add it to the list of hanging nodes. */ protected void buildEnter(CallableDeclaration callableDeclaration) { graph.buildRootNode(callableDeclaration); hangingNodes.add(graph.getRootNode()); } + /** Visit the body and add the return nodes to the hanging nodes list. */ protected void visitCallableDeclarationBody(CallableDeclaration callableDeclaration, Void arg) { ASTUtils.getCallableBody(callableDeclaration).accept(this, arg); returnList.stream().filter(node -> !hangingNodes.contains(node)).forEach(hangingNodes::add); } + /** Generate the method EXIT node. */ protected void buildExit(CallableDeclaration callableDeclaration) { MethodExitNode exit = new MethodExitNode(callableDeclaration); graph.addVertex(exit); diff --git a/sdg-core/src/main/java/es/upv/mist/slicing/graphs/exceptionsensitive/ESCFG.java b/sdg-core/src/main/java/es/upv/mist/slicing/graphs/exceptionsensitive/ESCFG.java index 75fd42b..c9e038a 100644 --- a/sdg-core/src/main/java/es/upv/mist/slicing/graphs/exceptionsensitive/ESCFG.java +++ b/sdg-core/src/main/java/es/upv/mist/slicing/graphs/exceptionsensitive/ESCFG.java @@ -29,6 +29,7 @@ import java.util.*; * and multiple calls with exceptions per CFG node are not considered. */ public class ESCFG extends ACFG { + /** The name for the currently active exception variable. */ public static final String ACTIVE_EXCEPTION_VARIABLE = "-activeException-"; @Override diff --git a/sdg-core/src/main/java/es/upv/mist/slicing/graphs/jsysdg/JSysCFG.java b/sdg-core/src/main/java/es/upv/mist/slicing/graphs/jsysdg/JSysCFG.java index 2a88a84..c7f4a36 100644 --- a/sdg-core/src/main/java/es/upv/mist/slicing/graphs/jsysdg/JSysCFG.java +++ b/sdg-core/src/main/java/es/upv/mist/slicing/graphs/jsysdg/JSysCFG.java @@ -74,6 +74,7 @@ public class JSysCFG extends ESCFG { return findLastVarActionsFrom(usage, VariableAction::isDefinition); } + /** Given a field declaration, locate all definitions that affect the given member. */ public List findAllFutureObjectDefinitionsFor(VariableAction action) { List list = new LinkedList<>(); Predicate filter = a -> a.isDefinition() && a.getName().equals("this") && a.hasTreeMember(action.getName()); @@ -81,6 +82,9 @@ public class JSysCFG extends ESCFG { return list; } + /** Locate variable actions that match the given filter, starting in {@code currentNode}, at variable action + * {@code var} and searching forwards through the control-flow graph. The resulting variable actions are + * placed in the given argument. This search does not stop when an action in that control-flow branch is found. */ protected void findAllFutureVarActionsFor(Set> visited, List result, GraphNode currentNode, VariableAction var, Predicate filter) { @@ -100,6 +104,8 @@ public class JSysCFG extends ESCFG { findAllFutureVarActionsFor(visited, result, getEdgeTarget(arc), var, filter); } + /** Given an action that defines a member, locates the previous total definition that gave + * it value. */ public List findLastTotalDefinitionOf(VariableAction action, String member) { return findLastVarActionsFrom(action, def -> (def.isDeclaration() && def.hasTreeMember(member)) @@ -119,6 +125,9 @@ public class JSysCFG extends ESCFG { return list; } + /** Locate variable actions that match the given filter and variable name, starting in {@code currentNode}, + * at variable action {@code var} and searching backwards. The resulting variable actions are placed in + * the given argument. This search stops after finding a matching action in each branch. */ protected boolean findNextVarActionsFor(Set> visited, List result, GraphNode currentNode, VariableAction var, Predicate filter, String memberName) { @@ -222,6 +231,11 @@ public class JSysCFG extends ESCFG { } } + /** + * Sets the expression for all return statements contained in its argument. + * @param node The AST to search for return statements. + * @param expressionSupplier The expression to be set. + */ protected void modifyAllReturnExpr(Node node, Supplier expressionSupplier) { node.accept(new ModifierVisitor() { @Override @@ -243,6 +257,13 @@ public class JSysCFG extends ESCFG { } } + /** + * Generates the object tree for the output of a declaration, and copies that same tree + * to each of its return statements. It also sets the connection between them, to be applied + * later. + * @param callableDeclaration The root of the declarations' AST. + * @param useOutput The variable at the method's exit that uses -output-. + */ protected void expandOutputVariable(CallableDeclaration callableDeclaration, VariableAction useOutput) { // Generate the full tree for the method's returned type (static) var fields = classGraph.generateObjectTreeForReturnOf(callableDeclaration); diff --git a/sdg-core/src/main/java/es/upv/mist/slicing/graphs/jsysdg/JSysCallConnector.java b/sdg-core/src/main/java/es/upv/mist/slicing/graphs/jsysdg/JSysCallConnector.java index b16bea1..05b5f15 100644 --- a/sdg-core/src/main/java/es/upv/mist/slicing/graphs/jsysdg/JSysCallConnector.java +++ b/sdg-core/src/main/java/es/upv/mist/slicing/graphs/jsysdg/JSysCallConnector.java @@ -13,6 +13,18 @@ import es.upv.mist.slicing.utils.ASTUtils; import java.util.List; +/** + * Generates interprocedural arcs between call sites and declarations. + * In the JSysDG, these include: + *
    + *
  • Actual-in to formal-in.
  • + *
  • Formal-out to actual-out.
  • + *
  • Output to call return (equivalent to the previous element but for the method's output).
  • + *
  • Exception exit to exception return.
  • + *
  • Normal exit to normal return.
  • + *
+ * For each node that features an object tree, that tree is connected in the same manner. + */ public class JSysCallConnector extends ExceptionSensitiveCallConnector { public JSysCallConnector(JSysDG sdg) { super(sdg); @@ -37,12 +49,15 @@ public class JSysCallConnector extends ExceptionSensitiveCallConnector { connectObjectInterprocedurally(formalOut, actualOut); } + /** Whether the given formal node represents an object with an object tree. */ protected boolean formalIsObject(FormalIONode formalNode) { return formalNode.getVariableName().equals("this") || !formalNode.getAstNode().getParameterByName(formalNode.getVariableName()) .orElseThrow().getType().isPrimitiveType(); } + /** Connects the object tree from the last variable action in the source node to + * each object tree in the target node. */ protected void connectObjectInterprocedurally(GraphNode source, GraphNode target) { assert !target.getVariableActions().isEmpty(); assert !source.getVariableActions().isEmpty(); @@ -58,6 +73,7 @@ public class JSysCallConnector extends ExceptionSensitiveCallConnector { super.createOutputReturnConnection(outputNode, callReturnNode); } + /** Generates the tree connection between the output and return nodes (definition to call). */ protected void connectObjectOutput(GraphNode methodOutputNode, GraphNode callReturnNode) { List outputList = methodOutputNode.getVariableActions(); assert outputList.size() == 1; diff --git a/sdg-core/src/main/java/es/upv/mist/slicing/graphs/jsysdg/JSysPDG.java b/sdg-core/src/main/java/es/upv/mist/slicing/graphs/jsysdg/JSysPDG.java index 4275c38..51a5f1d 100644 --- a/sdg-core/src/main/java/es/upv/mist/slicing/graphs/jsysdg/JSysPDG.java +++ b/sdg-core/src/main/java/es/upv/mist/slicing/graphs/jsysdg/JSysPDG.java @@ -93,7 +93,6 @@ public class JSysPDG extends ESPDG { protected class Builder extends ESPDG.Builder { - /** Computes all the data dependencies between {@link VariableAction variable actions} of this graph. */ @Override protected void buildDataDependency() { addSyntheticNodesToPDG(); @@ -102,6 +101,7 @@ public class JSysPDG extends ESPDG { valueDependencyForThrowStatements(); } + /** Compute flow, object flow and total definition dependence. */ protected void buildJSysDataDependency() { JSysCFG jSysCFG = (JSysCFG) cfg; for (GraphNode node : vertexSet()) { @@ -174,6 +174,7 @@ public class JSysPDG extends ESPDG { } } + /** Add movables to the PDG, and all MemberNodes contained in object trees. */ protected void addSyntheticNodesToPDG() { for (GraphNode node : cfg.vertexSet()) { Deque callNodeStack = new LinkedList<>(); @@ -224,12 +225,14 @@ public class JSysPDG extends ESPDG { } } + /** Apply the pre-assigned connections between object trees. */ protected void applyTreeConnections() { cfg.vertexSet().stream() .flatMap(node -> node.getVariableActions().stream()) .forEach(va -> va.applyPDGTreeConnections(JSysPDG.this)); } + /** Inserts a member node from an object tree onto the PDG. */ protected void insertMemberNode(MemberNode memberNode, GraphNode parentNode) { if (memberNode.getParent() == null) memberNode.setParent(parentNode); @@ -238,6 +241,7 @@ public class JSysPDG extends ESPDG { addStructuralArc(memberNode.getParent(), memberNode); } + /** Connects the tree that represents the active exception to its parent graph node. */ protected void valueDependencyForThrowStatements() { for (GraphNode node : vertexSet()) for (VariableAction action : node.getVariableActions()) diff --git a/sdg-core/src/main/java/es/upv/mist/slicing/graphs/jsysdg/SummaryArcAnalyzer.java b/sdg-core/src/main/java/es/upv/mist/slicing/graphs/jsysdg/SummaryArcAnalyzer.java index f9b2778..faa8674 100644 --- a/sdg-core/src/main/java/es/upv/mist/slicing/graphs/jsysdg/SummaryArcAnalyzer.java +++ b/sdg-core/src/main/java/es/upv/mist/slicing/graphs/jsysdg/SummaryArcAnalyzer.java @@ -14,6 +14,10 @@ import java.util.HashSet; import java.util.Optional; import java.util.Set; +/** + * Generates and places on the graph all summary arcs between actual-in and actual-out, return and exception/normal + * return nodes. Additionally, it generates them between the object trees of each of the aforementioned nodes. + */ public class SummaryArcAnalyzer extends AbstractSummaryArcAnalyzer, SyntheticNode, SyntheticNode> { public SummaryArcAnalyzer(JSysDG sdg, CallGraph graph) { super(sdg, graph); diff --git a/sdg-core/src/main/java/es/upv/mist/slicing/graphs/sdg/AbstractSummaryArcAnalyzer.java b/sdg-core/src/main/java/es/upv/mist/slicing/graphs/sdg/AbstractSummaryArcAnalyzer.java index 6d393d4..ca5a407 100644 --- a/sdg-core/src/main/java/es/upv/mist/slicing/graphs/sdg/AbstractSummaryArcAnalyzer.java +++ b/sdg-core/src/main/java/es/upv/mist/slicing/graphs/sdg/AbstractSummaryArcAnalyzer.java @@ -11,6 +11,12 @@ import es.upv.mist.slicing.nodes.io.OutputNode; import java.util.*; import java.util.stream.Stream; +/** + * Base class for generating and placing in an SDG the summary arcs. + * @param The type of node for actual-in nodes. + * @param The type of node for formal-out nodes. + * @param The type of node for formal-in nodes. + */ public abstract class AbstractSummaryArcAnalyzer, FormalOut extends SyntheticNode, FormalIn extends SyntheticNode> extends BackwardDataFlowAnalysis, Map>> { protected final SDG sdg; 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 2a248d5..bbfd848 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 @@ -67,6 +67,9 @@ public class InterproceduralDefinitionFinder extends InterproceduralActionFinder graphNode.addActionsForCall(movables, edge.getCall(), false); } + /** For each variable of an expression that may be passed through it (i.e., that if passed as argument of a function, could + * a modification to that reference affect any part of the variables passed as input?). It then generates the necessary + * definitions and links between trees in order to define each of them as a function of the given actual out. */ protected void extractOutputVariablesAsMovables(Expression e, List movables, GraphNode graphNode, ActualIONode actualOut, VariableAction def) { Set defExpressions = new HashSet<>(); e.accept(new OutNodeVariableVisitor(), defExpressions); 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 ea84b28..e429cc0 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 @@ -69,6 +69,7 @@ public class InterproceduralUsageFinder extends InterproceduralActionFinder edge, String name) { return edge.getGraphNode().getSyntheticNodesInMovables().stream() .filter(ActualIONode.class::isInstance) diff --git a/sdg-core/src/main/java/es/upv/mist/slicing/graphs/sdg/SummaryArcAnalyzer.java b/sdg-core/src/main/java/es/upv/mist/slicing/graphs/sdg/SummaryArcAnalyzer.java index eaf7090..e380702 100644 --- a/sdg-core/src/main/java/es/upv/mist/slicing/graphs/sdg/SummaryArcAnalyzer.java +++ b/sdg-core/src/main/java/es/upv/mist/slicing/graphs/sdg/SummaryArcAnalyzer.java @@ -14,6 +14,9 @@ import java.util.Optional; import java.util.Set; import java.util.stream.Collectors; +/** + * Generates the summary arcs between actual-in and actual-out, return and exception/exit return nodes. + */ public class SummaryArcAnalyzer extends AbstractSummaryArcAnalyzer>, FormalIONode> { public SummaryArcAnalyzer(SDG sdg, CallGraph graph) { super(sdg, graph); diff --git a/sdg-core/src/main/java/es/upv/mist/slicing/nodes/GraphNode.java b/sdg-core/src/main/java/es/upv/mist/slicing/nodes/GraphNode.java index eebac12..19f5438 100644 --- a/sdg-core/src/main/java/es/upv/mist/slicing/nodes/GraphNode.java +++ b/sdg-core/src/main/java/es/upv/mist/slicing/nodes/GraphNode.java @@ -81,6 +81,8 @@ public class GraphNode implements Comparable> { return Collections.unmodifiableList(variableActions); } + /** Returns the last variable action in this node. + * @throws IllegalStateException If there are no variable actions. */ public VariableAction getLastVariableAction() { if (variableActions.isEmpty()) throw new IllegalStateException("There are no variable actions in this node"); @@ -142,20 +144,29 @@ public class GraphNode implements Comparable> { throw new IllegalArgumentException("Could not find markers for " + call.resolve().getSignature() + " in " + this); } + /** Register a node that is contained in this node until the CFG + * is converted into the PDG. */ public void addSyntheticNode(SyntheticNode node) { syntheticNodesInMovables.add(node); } + /** @see #syntheticNodesInMovables */ public Collection> getSyntheticNodesInMovables() { return Collections.unmodifiableSet(syntheticNodesInMovables); } + /** Append a variable action to the list of variable actions. When the action + * is movable, its real node is registered in {@link #syntheticNodesInMovables}. */ public void addVariableAction(VariableAction action) { if (action instanceof VariableAction.Movable) syntheticNodesInMovables.add(((VariableAction.Movable) action).getRealNode()); variableActions.add(action); } + /** + * Searches for the last variable action that matches the given real node, and appends + * the given action immediately after. + */ @SuppressWarnings("unchecked") public void addVariableActionAfterLastMatchingRealNode(VariableAction.Movable action, SyntheticNode realNode) { boolean found = false; @@ -179,14 +190,14 @@ public class GraphNode implements Comparable> { } } + /** Adds the variable action DEF(-active-exception-) to the end of this method. */ public void addVADefineActiveException(Expression expression) { - VariableAction.Definition def = new VariableAction.Definition(VariableAction.DeclarationType.SYNTHETIC, ACTIVE_EXCEPTION_VARIABLE, this, expression); - variableActions.add(def); + variableActions.add(new VariableAction.Definition(VariableAction.DeclarationType.SYNTHETIC, ACTIVE_EXCEPTION_VARIABLE, this, expression)); } + /** Adds the variable action USE(-active-exception-) to the end of this method. */ public void addVAUseActiveException() { - VariableAction.Usage use = new VariableAction.Usage(VariableAction.DeclarationType.SYNTHETIC, ACTIVE_EXCEPTION_VARIABLE, this); - variableActions.add(use); + variableActions.add(new VariableAction.Usage(VariableAction.DeclarationType.SYNTHETIC, ACTIVE_EXCEPTION_VARIABLE, this)); } /** Create and append a call marker to the list of actions of this node. */ 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 a1478ee..b3ca2a7 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 @@ -7,26 +7,44 @@ import java.util.*; import java.util.regex.Matcher; import java.util.regex.Pattern; +/** + * 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. + * If the variable is undefined when the tree is created, the root of this + * tree will be named "-root-". The real name of the root can be found in + * its associated VariableAction.
+ * + * Object trees may not be reused, and must be cloned via {@link #clone()}. + * Otherwise, the MemberNodes representing the tree will be the same in the graph. + */ public class ObjectTree implements Cloneable { + /** The default name of a tree's root. */ 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-))(\\.(?.+))?$"); + /** Direct children of this tree node, mapped by field name. */ private final Map childrenMap = new HashMap<>(); + /** The MemberNode that represents this tree node in the PDG and SDG. */ private final MemberNode memberNode; + /** Create a root of a new object tree with the default name. */ public ObjectTree() { this(ROOT_NAME); } + /** Create a root of a new object tree with the given name. */ public ObjectTree(String memberName) { memberNode = new MemberNode(memberName, null); } + /** Create a child tree node for the given field, whose node is linked to the given parent. */ private ObjectTree(String memberName, ObjectTree parent) { this.memberNode = new MemberNode(memberName, parent.memberNode); } + /** The name of the variable or field represented by this tree. It doesn't include ancestors. */ protected String getMemberName() { return memberNode == null ? ROOT_NAME : memberNode.getLabel(); } @@ -35,15 +53,26 @@ public class ObjectTree implements Cloneable { return memberNode; } + /** Whether this object tree has fields. */ public boolean hasChildren() { return !childrenMap.isEmpty(); } + /** + * Insert a field with the given name. This method should only be called on a root object tree. + * This method may be used to add multiple levels simultaneously, calling this method with + * the argument {@code "a.b.c"} on a new root tree, it will create the tree "b" inside the root + * and "c" inside "b". + * @param fieldName The field to be added, should include the root variable name. For example, + * to add the field "x" to a variable "a", this argument should be "a.x". + */ public void addField(String fieldName) { String members = removeRoot(fieldName); addNonRootField(members); } + /** Similar to {@link #addField(String)}, but may be called at any level + * and the argument must not contain the root variable. */ private void addNonRootField(String members) { if (members.contains(".")) { int firstDot = members.indexOf('.'); @@ -56,6 +85,8 @@ public class ObjectTree implements Cloneable { } } + /** Copies the structure of another object tree into this object tree. + * All elements inserted in the current tree are a copy of the argument's children and members. */ public void addAll(ObjectTree tree) { for (Map.Entry entry : tree.childrenMap.entrySet()) if (childrenMap.containsKey(entry.getKey())) @@ -78,6 +109,10 @@ public class ObjectTree implements Cloneable { a.addAll(b); } + /** + * Locate an object tree that represents a field of this object. + * @param member The field, without a root prefix. + */ ObjectTree findObjectTreeOfMember(String member) { ObjectTree result = this; while (!member.isEmpty()) { @@ -96,11 +131,15 @@ public class ObjectTree implements Cloneable { return result; } + /** Whether this object tree contains the given member. The argument should contain the root variable name. */ public boolean hasMember(String member) { String field = removeRoot(member); return hasNonRootMember(field); } + /** Similar to hasMember, but valid at any level of the tree and the argument should not contain + * the root variable's name. + * @see #hasMember(String) */ private boolean hasNonRootMember(String members) { if (members.contains(".")) { int firstDot = members.indexOf('.'); @@ -112,11 +151,14 @@ public class ObjectTree implements Cloneable { } } + /** Obtain the member node that corresponds to the given field name (with root). */ public MemberNode getNodeFor(String member) { String field = removeRoot(member); return getNodeForNonRoot(field); } + /** Similar to getNodeFor, but valid at any level of the tree, and the argument must be the field only. + * @see #getNodeFor(String) */ MemberNode getNodeForNonRoot(String members) { if (members.isEmpty()) { return memberNode; @@ -132,6 +174,8 @@ public class ObjectTree implements Cloneable { } } + /** @return An iterable through the names (with full prefixes) of all members of this tree, + * excluding the root. */ public Iterable nameIterable() { return () -> new Iterator<>() { final Iterator it = treeIterator(); @@ -160,6 +204,7 @@ public class ObjectTree implements Cloneable { }; } + /** @return An iterable through the nodes of all members of this tree, excluding the root. */ public Iterable nodeIterable() { return () -> new Iterator<>() { final Iterator it = treeIterator(); @@ -176,6 +221,7 @@ public class ObjectTree implements Cloneable { }; } + /** @return An iterator through all the trees of this structure, excluding the root. */ private Iterator treeIterator() { return new Iterator<>() { final Set remaining = new HashSet<>(childrenMap.values()); @@ -202,6 +248,7 @@ public class ObjectTree implements Cloneable { }; } + /** @see #treeIterator() */ Iterable treeIterable() { return this::treeIterator; } @@ -222,6 +269,12 @@ public class ObjectTree implements Cloneable { return clone; } + /** + * Utility method to remove the root variable from a string. The root element or root of + * the object tree should be either "-root-", a valid variable name or an optionally type-prefixed + * this (A.this, package.A.this or this). + * @throws IllegalArgumentException When there is no root to remove. + */ public static String removeRoot(String fieldWithRoot) { Matcher matcher = FIELD_SPLIT.matcher(fieldWithRoot); if (matcher.matches()) @@ -229,6 +282,12 @@ public class ObjectTree implements Cloneable { throw new IllegalArgumentException("Field should be of the form ., .this., where may not contain dots."); } + /** + * Utility method to remove the fields a string, retaining just the root. The root element or root of + * the object tree should be either "-root-", a valid variable name or an optionally type-prefixed + * this (A.this, package.A.this or this). + * @throws IllegalArgumentException When there are no fields to remove. + */ public static String removeFields(String fieldWithRoot) { Matcher matcher = FIELD_SPLIT.matcher(fieldWithRoot); if (matcher.matches() && matcher.group("root") != null) diff --git a/sdg-core/src/main/java/es/upv/mist/slicing/nodes/ObjectTreeConnection.java b/sdg-core/src/main/java/es/upv/mist/slicing/nodes/ObjectTreeConnection.java index 43b1836..035c1a9 100644 --- a/sdg-core/src/main/java/es/upv/mist/slicing/nodes/ObjectTreeConnection.java +++ b/sdg-core/src/main/java/es/upv/mist/slicing/nodes/ObjectTreeConnection.java @@ -11,6 +11,8 @@ import es.upv.mist.slicing.nodes.oo.MemberNode; import java.util.function.Supplier; +/** A connection between two object trees. This object can specify the connection between two different + * levels of object trees, for example to represent the assignment {@code a.b = c.d}. */ class ObjectTreeConnection implements VariableAction.PDGConnection { protected final VariableAction sourceAction; @@ -27,6 +29,7 @@ class ObjectTreeConnection implements VariableAction.PDGConnection { this.targetMember = targetMember; } + /** Apply the connection represented by this object on an SDG. This means that all arcs will be interprocedural. */ public void applySDG(JSysDG graph) { if (!applied) { connectTrees(graph, ParameterInOutArc::new, ParameterInOutArc.ObjectFlow::new); @@ -34,6 +37,7 @@ class ObjectTreeConnection implements VariableAction.PDGConnection { } } + @Override public void apply(JSysPDG graph) { if (!applied) { connectTrees(graph, FlowDependencyArc::new, ObjectFlowDependencyArc::new); diff --git a/sdg-core/src/main/java/es/upv/mist/slicing/nodes/ValueConnection.java b/sdg-core/src/main/java/es/upv/mist/slicing/nodes/ValueConnection.java index fd829f5..f3648fd 100644 --- a/sdg-core/src/main/java/es/upv/mist/slicing/nodes/ValueConnection.java +++ b/sdg-core/src/main/java/es/upv/mist/slicing/nodes/ValueConnection.java @@ -5,6 +5,8 @@ import es.upv.mist.slicing.graphs.jsysdg.JSysPDG; import static es.upv.mist.slicing.nodes.ObjectTree.ROOT_NAME; +/** A connection that represents a value dependence, from one element of an object tree to the + * main GraphNode that represents the instruction. */ public class ValueConnection implements VariableAction.PDGConnection { protected final VariableAction action; protected final String member; diff --git a/sdg-core/src/main/java/es/upv/mist/slicing/nodes/VariableAction.java b/sdg-core/src/main/java/es/upv/mist/slicing/nodes/VariableAction.java index 875e707..9a0fabb 100644 --- a/sdg-core/src/main/java/es/upv/mist/slicing/nodes/VariableAction.java +++ b/sdg-core/src/main/java/es/upv/mist/slicing/nodes/VariableAction.java @@ -23,6 +23,7 @@ import static es.upv.mist.slicing.nodes.VariableAction.DeclarationType.*; /** An action upon a variable (e.g. usage, definition, declaration) */ public abstract class VariableAction { + /** The kinds of declaration that an action can act upon. */ public enum DeclarationType { FIELD, STATIC_FIELD, @@ -69,7 +70,6 @@ public abstract class VariableAction { protected ObjectTree objectTree; protected boolean optional = false; - /// Variables that control the automatic connection of ObjectTrees between VariableAction /** A list of pairs representing connections to be made between trees in the PDG. * The variable action that contains the tree we must connect to in the PDG. * The string, or member where the tree connection must start (in PDG). E.g.: our tree is "a.b.c" and this variable is "a", @@ -108,6 +108,11 @@ public abstract class VariableAction { return declarationType == LOCAL_VARIABLE; } + /** + * Warning! This method implicitly creates an object tree if there is none. + * To avoid modifying the variable action, check with {@link #hasObjectTree()} + * before calling this method. + */ public ObjectTree getObjectTree() { if (!hasObjectTree()) setObjectTree(new ObjectTree(getName())); @@ -548,7 +553,13 @@ public abstract class VariableAction { } } + /** + * A connection that is setup in the CFG creation stage, but + * cannot be applied until the PDG creation stage. + */ public interface PDGConnection { + /** Apply the connection in the given PDG. + * This action can be performed multiple times, but the connection will only be made once. */ void apply(JSysPDG graph); } } diff --git a/sdg-core/src/main/java/es/upv/mist/slicing/nodes/oo/MemberNode.java b/sdg-core/src/main/java/es/upv/mist/slicing/nodes/oo/MemberNode.java index 17044d9..dc2cd0b 100644 --- a/sdg-core/src/main/java/es/upv/mist/slicing/nodes/oo/MemberNode.java +++ b/sdg-core/src/main/java/es/upv/mist/slicing/nodes/oo/MemberNode.java @@ -6,6 +6,9 @@ import es.upv.mist.slicing.nodes.SyntheticNode; import java.util.LinkedList; +/** A synthetic node that represents an object or field that is within a + * VariableAction. They are placed in the graph when the PDG is built, + * and allow for a more granular representation and slicing of objects. */ public class MemberNode extends SyntheticNode { protected GraphNode parent; diff --git a/sdg-core/src/main/java/es/upv/mist/slicing/utils/Utils.java b/sdg-core/src/main/java/es/upv/mist/slicing/utils/Utils.java index 57d6e8f..7b3b337 100644 --- a/sdg-core/src/main/java/es/upv/mist/slicing/utils/Utils.java +++ b/sdg-core/src/main/java/es/upv/mist/slicing/utils/Utils.java @@ -1,6 +1,5 @@ package es.upv.mist.slicing.utils; -import es.upv.mist.slicing.nodes.VariableAction; import org.jgrapht.nio.Attribute; import org.jgrapht.nio.DefaultAttribute; @@ -28,10 +27,11 @@ public class Utils { return map; } - public static
A setGet(Set set, A action) { - for (A aFromSet : set) - if (aFromSet.hashCode() == action.hashCode() && Objects.equals(aFromSet, action)) - return aFromSet; - throw new NoSuchElementException("Could not locate " + action + " in set."); + /** Find the matching element in the set and return it. */ + public static E setGet(Set set, E object) { + for (E element : set) + if (element.hashCode() == object.hashCode() && Objects.equals(element, object)) + return element; + throw new NoSuchElementException("Could not locate " + object + " in set."); } } -- GitLab