Loading sdg-cli/pom.xml +1 −1 Original line number Diff line number Diff line Loading @@ -27,7 +27,7 @@ <dependency> <groupId>tfm</groupId> <artifactId>sdg-core</artifactId> <version>1.2.0</version> <version>1.2.1</version> <scope>compile</scope> </dependency> </dependencies> Loading sdg-core/pom.xml +1 −1 Original line number Diff line number Diff line Loading @@ -6,7 +6,7 @@ <groupId>tfm</groupId> <artifactId>sdg-core</artifactId> <version>1.2.0</version> <version>1.2.1</version> <properties> <maven.compiler.source>11</maven.compiler.source> Loading sdg-core/src/main/java/tfm/graphs/BackwardDataFlowAnalysis.java +3 −4 Original line number Diff line number Diff line Loading @@ -15,9 +15,9 @@ import java.util.stream.Collectors; public abstract class BackwardDataFlowAnalysis<V, E, D> { /** The graph on which this algorithm iterates. */ protected final AbstractGraph<V, E> graph; /** A mapping of the latest value computed per node. */ protected Map<V, D> vertexDataMap = new HashMap<>(); protected final Map<V, D> vertexDataMap = new HashMap<>(); protected boolean built = false; public BackwardDataFlowAnalysis(AbstractGraph<V, E> graph) { Loading @@ -34,7 +34,7 @@ public abstract class BackwardDataFlowAnalysis<V, E, D> { List<V> newWorkList = new LinkedList<>(); for (V vertex : workList) { Set<V> mayAffectVertex = graph.outgoingEdgesOf(vertex).stream() .map(graph::getEdgeTarget).collect(Collectors.toSet()); .map(graph::getEdgeTarget).collect(Collectors.toCollection(() -> Collections.newSetFromMap(new IdentityHashMap<>()))); D newValue = compute(vertex, mayAffectVertex); if (!Objects.equals(vertexDataMap.get(vertex), newValue)) { vertexDataMap.put(vertex, newValue); Loading @@ -43,7 +43,6 @@ public abstract class BackwardDataFlowAnalysis<V, E, D> { } workList = newWorkList; } vertexDataMap = Collections.unmodifiableMap(vertexDataMap); built = true; } Loading sdg-core/src/main/java/tfm/graphs/CallGraph.java +53 −9 Original line number Diff line number Diff line Loading @@ -17,11 +17,10 @@ import org.jgrapht.graph.DirectedPseudograph; import org.jgrapht.io.DOTExporter; import tfm.graphs.cfg.CFG; import tfm.nodes.GraphNode; import tfm.utils.ASTUtils; import tfm.utils.NodeNotFoundException; import java.util.Deque; import java.util.LinkedList; import java.util.Map; import java.util.*; /** * A directed graph which displays the available method declarations as nodes and their Loading @@ -35,8 +34,9 @@ import java.util.Map; * to {@link ObjectCreationExpr object creation} and {@link ExplicitConstructorInvocationStmt * explicit constructor invokation} ({@code this()}, {@code super()}). */ public class CallGraph extends DirectedPseudograph<CallableDeclaration<?>, CallGraph.Edge<?>> implements Buildable<NodeList<CompilationUnit>> { public class CallGraph extends DirectedPseudograph<CallGraph.Vertex, CallGraph.Edge<?>> implements Buildable<NodeList<CompilationUnit>> { private final Map<CallableDeclaration<?>, CFG> cfgMap; private final Map<CallableDeclaration<?>, Vertex> vertexDeclarationMap = new IdentityHashMap<>(); private boolean built = false; Loading @@ -50,6 +50,8 @@ public class CallGraph extends DirectedPseudograph<CallableDeclaration<?>, CallG return edgeSet().stream() .filter(e -> e.getCall() == call) .map(this::getEdgeTarget) .map(Vertex::getDeclaration) .map(CallableDeclaration.class::cast) .findFirst() .orElse(null); } Loading @@ -73,18 +75,29 @@ public class CallGraph extends DirectedPseudograph<CallableDeclaration<?>, CallG arg.accept(new VoidVisitorAdapter<Void>() { @Override public void visit(MethodDeclaration n, Void arg) { addVertex(n); addDeclaration(n); super.visit(n, arg); } @Override public void visit(ConstructorDeclaration n, Void arg) { addVertex(n); addDeclaration(n); super.visit(n, arg); } }, null); } protected void addDeclaration(CallableDeclaration<?> n) { Vertex v = new Vertex(n); vertexDeclarationMap.put(n, v); addVertex(v); } protected boolean addEdge(CallableDeclaration<?> source, CallableDeclaration<?> target, Resolvable<? extends ResolvedMethodLikeDeclaration> call) { Edge<?> edge = new Edge<>(call, findGraphNode(call, source)); return addEdge(vertexDeclarationMap.get(source), vertexDeclarationMap.get(target), edge); } /** Find the calls to methods and constructors (edges) in the given list of compilation units. */ protected void buildEdges(NodeList<CompilationUnit> arg) { arg.accept(new VoidVisitorAdapter<Void>() { Loading Loading @@ -112,19 +125,19 @@ public class CallGraph extends DirectedPseudograph<CallableDeclaration<?>, CallG // =============== Method calls =============== @Override public void visit(MethodCallExpr n, Void arg) { n.resolve().toAst().ifPresent(decl -> addEdge(declStack.peek(), decl, new Edge<>(n, findGraphNode(n, declStack.peek())))); n.resolve().toAst().ifPresent(decl -> addEdge(declStack.peek(), decl, n)); super.visit(n, arg); } @Override public void visit(ObjectCreationExpr n, Void arg) { n.resolve().toAst().ifPresent(decl -> addEdge(declStack.peek(), decl, new Edge<>(n, findGraphNode(n, declStack.peek())))); n.resolve().toAst().ifPresent(decl -> addEdge(declStack.peek(), decl, n)); super.visit(n, arg); } @Override public void visit(ExplicitConstructorInvocationStmt n, Void arg) { n.resolve().toAst().ifPresent(decl -> addEdge(declStack.peek(), decl, new Edge<>(n, findGraphNode(n, declStack.peek())))); n.resolve().toAst().ifPresent(decl -> addEdge(declStack.peek(), decl, n)); super.visit(n, arg); } }, null); Loading @@ -148,6 +161,37 @@ public class CallGraph extends DirectedPseudograph<CallableDeclaration<?>, CallG ); } /** A vertex containing the declaration it represents. It only exists because * JGraphT relies heavily on equals comparison, which may not be correct in declarations. */ public static class Vertex { protected final CallableDeclaration<?> declaration; public Vertex(CallableDeclaration<?> declaration) { assert declaration instanceof ConstructorDeclaration || declaration instanceof MethodDeclaration; this.declaration = declaration; } /** The declaration represented by this node. */ public CallableDeclaration<?> getDeclaration() { return declaration; } @Override public int hashCode() { return Objects.hash(declaration, declaration.getRange()); } @Override public boolean equals(Object obj) { return obj instanceof Vertex && ASTUtils.equalsWithRangeInCU(((Vertex) obj).declaration, declaration); } @Override public String toString() { return super.toString(); } } /** An edge containing the call it represents, and the graph node that contains it. */ public static class Edge<T extends Resolvable<? extends ResolvedMethodLikeDeclaration>> extends DefaultEdge { protected final T call; Loading sdg-core/src/main/java/tfm/graphs/sdg/InterproceduralActionFinder.java +13 −14 Original line number Diff line number Diff line Loading @@ -23,13 +23,12 @@ import java.util.function.Consumer; * declarations define, use or declare which variables, interprocedurally. * @param <A> The action to be searched for */ public abstract class InterproceduralActionFinder<A extends VariableAction> extends BackwardDataFlowAnalysis<CallableDeclaration<?>, CallGraph.Edge<?>, Set<InterproceduralActionFinder.StoredAction<A>>> { public abstract class InterproceduralActionFinder<A extends VariableAction> extends BackwardDataFlowAnalysis<CallGraph.Vertex, CallGraph.Edge<?>, Set<InterproceduralActionFinder.StoredAction<A>>> { protected final Map<CallableDeclaration<?>, CFG> cfgMap; public InterproceduralActionFinder(CallGraph callGraph, Map<CallableDeclaration<?>, CFG> cfgMap) { super(callGraph); this.cfgMap = cfgMap; this.vertexDataMap = new IdentityHashMap<>(); // CallableDeclarations can't be reliably be compared with equals. } // =========================================================== Loading @@ -39,19 +38,19 @@ public abstract class InterproceduralActionFinder<A extends VariableAction> exte /** Entry-point to the class. Performs the analysis and then saves the results to the CFG nodes. */ public void save() { if (!built) analyze(); cfgMap.keySet().forEach(this::saveDeclaration); graph.vertexSet().forEach(this::saveDeclaration); } /** 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(CallableDeclaration<?> declaration) { Set<StoredAction<A>> storedActions = vertexDataMap.get(declaration); protected void saveDeclaration(CallGraph.Vertex vertex) { Set<StoredAction<A>> storedActions = vertexDataMap.get(vertex); // FORMAL: per declaration (1) for (StoredAction<A> sa : storedActions) sa.storeFormal(a -> sandBoxedHandler(declaration, a, this::handleFormalAction)); sa.storeFormal(a -> sandBoxedHandler(vertex.getDeclaration(), a, this::handleFormalAction)); // ACTUAL: per call (n) for (CallGraph.Edge<?> edge : graph.incomingEdgesOf(declaration)) 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))); } Loading @@ -76,7 +75,7 @@ public abstract class InterproceduralActionFinder<A extends VariableAction> exte * is false, primitive parameters will be skipped, as their value cannot be redefined.*/ protected Expression extractArgument(VariableAction action, CallGraph.Edge<?> edge, boolean input) { ResolvedValueDeclaration resolved = action.getNameExpr().resolve(); CallableDeclaration<?> callTarget = graph.getEdgeTarget(edge); CallableDeclaration<?> callTarget = graph.getEdgeTarget(edge).getDeclaration(); if (resolved.isParameter()) { ResolvedParameterDeclaration p = resolved.asParameter(); if (!input && p.getType().isPrimitive()) Loading @@ -91,10 +90,10 @@ public abstract class InterproceduralActionFinder<A extends VariableAction> exte } @Override protected Set<StoredAction<A>> compute(CallableDeclaration<?> declaration, Set<CallableDeclaration<?>> calledDeclarations) { saveDeclaration(declaration); Set<StoredAction<A>> newValue = new HashSet<>(vertexDataMap.get(declaration)); newValue.addAll(initialValue(declaration)); protected Set<StoredAction<A>> compute(CallGraph.Vertex vertex, Set<CallGraph.Vertex> predecessors) { saveDeclaration(vertex); Set<StoredAction<A>> newValue = new HashSet<>(vertexDataMap.get(vertex)); newValue.addAll(initialValue(vertex)); return newValue; } Loading @@ -120,8 +119,8 @@ public abstract class InterproceduralActionFinder<A extends VariableAction> exte r1 = o1.getAction().getNameExpr().resolve(); r2 = o2.getAction().getNameExpr().resolve(); if (r1.isParameter() && r2.isParameter()) return -Integer.compare(ASTUtils.getMatchingParameterIndex(graph.getEdgeTarget(edge), r1.asParameter()), ASTUtils.getMatchingParameterIndex(graph.getEdgeTarget(edge), r2.asParameter())); 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()) Loading Loading
sdg-cli/pom.xml +1 −1 Original line number Diff line number Diff line Loading @@ -27,7 +27,7 @@ <dependency> <groupId>tfm</groupId> <artifactId>sdg-core</artifactId> <version>1.2.0</version> <version>1.2.1</version> <scope>compile</scope> </dependency> </dependencies> Loading
sdg-core/pom.xml +1 −1 Original line number Diff line number Diff line Loading @@ -6,7 +6,7 @@ <groupId>tfm</groupId> <artifactId>sdg-core</artifactId> <version>1.2.0</version> <version>1.2.1</version> <properties> <maven.compiler.source>11</maven.compiler.source> Loading
sdg-core/src/main/java/tfm/graphs/BackwardDataFlowAnalysis.java +3 −4 Original line number Diff line number Diff line Loading @@ -15,9 +15,9 @@ import java.util.stream.Collectors; public abstract class BackwardDataFlowAnalysis<V, E, D> { /** The graph on which this algorithm iterates. */ protected final AbstractGraph<V, E> graph; /** A mapping of the latest value computed per node. */ protected Map<V, D> vertexDataMap = new HashMap<>(); protected final Map<V, D> vertexDataMap = new HashMap<>(); protected boolean built = false; public BackwardDataFlowAnalysis(AbstractGraph<V, E> graph) { Loading @@ -34,7 +34,7 @@ public abstract class BackwardDataFlowAnalysis<V, E, D> { List<V> newWorkList = new LinkedList<>(); for (V vertex : workList) { Set<V> mayAffectVertex = graph.outgoingEdgesOf(vertex).stream() .map(graph::getEdgeTarget).collect(Collectors.toSet()); .map(graph::getEdgeTarget).collect(Collectors.toCollection(() -> Collections.newSetFromMap(new IdentityHashMap<>()))); D newValue = compute(vertex, mayAffectVertex); if (!Objects.equals(vertexDataMap.get(vertex), newValue)) { vertexDataMap.put(vertex, newValue); Loading @@ -43,7 +43,6 @@ public abstract class BackwardDataFlowAnalysis<V, E, D> { } workList = newWorkList; } vertexDataMap = Collections.unmodifiableMap(vertexDataMap); built = true; } Loading
sdg-core/src/main/java/tfm/graphs/CallGraph.java +53 −9 Original line number Diff line number Diff line Loading @@ -17,11 +17,10 @@ import org.jgrapht.graph.DirectedPseudograph; import org.jgrapht.io.DOTExporter; import tfm.graphs.cfg.CFG; import tfm.nodes.GraphNode; import tfm.utils.ASTUtils; import tfm.utils.NodeNotFoundException; import java.util.Deque; import java.util.LinkedList; import java.util.Map; import java.util.*; /** * A directed graph which displays the available method declarations as nodes and their Loading @@ -35,8 +34,9 @@ import java.util.Map; * to {@link ObjectCreationExpr object creation} and {@link ExplicitConstructorInvocationStmt * explicit constructor invokation} ({@code this()}, {@code super()}). */ public class CallGraph extends DirectedPseudograph<CallableDeclaration<?>, CallGraph.Edge<?>> implements Buildable<NodeList<CompilationUnit>> { public class CallGraph extends DirectedPseudograph<CallGraph.Vertex, CallGraph.Edge<?>> implements Buildable<NodeList<CompilationUnit>> { private final Map<CallableDeclaration<?>, CFG> cfgMap; private final Map<CallableDeclaration<?>, Vertex> vertexDeclarationMap = new IdentityHashMap<>(); private boolean built = false; Loading @@ -50,6 +50,8 @@ public class CallGraph extends DirectedPseudograph<CallableDeclaration<?>, CallG return edgeSet().stream() .filter(e -> e.getCall() == call) .map(this::getEdgeTarget) .map(Vertex::getDeclaration) .map(CallableDeclaration.class::cast) .findFirst() .orElse(null); } Loading @@ -73,18 +75,29 @@ public class CallGraph extends DirectedPseudograph<CallableDeclaration<?>, CallG arg.accept(new VoidVisitorAdapter<Void>() { @Override public void visit(MethodDeclaration n, Void arg) { addVertex(n); addDeclaration(n); super.visit(n, arg); } @Override public void visit(ConstructorDeclaration n, Void arg) { addVertex(n); addDeclaration(n); super.visit(n, arg); } }, null); } protected void addDeclaration(CallableDeclaration<?> n) { Vertex v = new Vertex(n); vertexDeclarationMap.put(n, v); addVertex(v); } protected boolean addEdge(CallableDeclaration<?> source, CallableDeclaration<?> target, Resolvable<? extends ResolvedMethodLikeDeclaration> call) { Edge<?> edge = new Edge<>(call, findGraphNode(call, source)); return addEdge(vertexDeclarationMap.get(source), vertexDeclarationMap.get(target), edge); } /** Find the calls to methods and constructors (edges) in the given list of compilation units. */ protected void buildEdges(NodeList<CompilationUnit> arg) { arg.accept(new VoidVisitorAdapter<Void>() { Loading Loading @@ -112,19 +125,19 @@ public class CallGraph extends DirectedPseudograph<CallableDeclaration<?>, CallG // =============== Method calls =============== @Override public void visit(MethodCallExpr n, Void arg) { n.resolve().toAst().ifPresent(decl -> addEdge(declStack.peek(), decl, new Edge<>(n, findGraphNode(n, declStack.peek())))); n.resolve().toAst().ifPresent(decl -> addEdge(declStack.peek(), decl, n)); super.visit(n, arg); } @Override public void visit(ObjectCreationExpr n, Void arg) { n.resolve().toAst().ifPresent(decl -> addEdge(declStack.peek(), decl, new Edge<>(n, findGraphNode(n, declStack.peek())))); n.resolve().toAst().ifPresent(decl -> addEdge(declStack.peek(), decl, n)); super.visit(n, arg); } @Override public void visit(ExplicitConstructorInvocationStmt n, Void arg) { n.resolve().toAst().ifPresent(decl -> addEdge(declStack.peek(), decl, new Edge<>(n, findGraphNode(n, declStack.peek())))); n.resolve().toAst().ifPresent(decl -> addEdge(declStack.peek(), decl, n)); super.visit(n, arg); } }, null); Loading @@ -148,6 +161,37 @@ public class CallGraph extends DirectedPseudograph<CallableDeclaration<?>, CallG ); } /** A vertex containing the declaration it represents. It only exists because * JGraphT relies heavily on equals comparison, which may not be correct in declarations. */ public static class Vertex { protected final CallableDeclaration<?> declaration; public Vertex(CallableDeclaration<?> declaration) { assert declaration instanceof ConstructorDeclaration || declaration instanceof MethodDeclaration; this.declaration = declaration; } /** The declaration represented by this node. */ public CallableDeclaration<?> getDeclaration() { return declaration; } @Override public int hashCode() { return Objects.hash(declaration, declaration.getRange()); } @Override public boolean equals(Object obj) { return obj instanceof Vertex && ASTUtils.equalsWithRangeInCU(((Vertex) obj).declaration, declaration); } @Override public String toString() { return super.toString(); } } /** An edge containing the call it represents, and the graph node that contains it. */ public static class Edge<T extends Resolvable<? extends ResolvedMethodLikeDeclaration>> extends DefaultEdge { protected final T call; Loading
sdg-core/src/main/java/tfm/graphs/sdg/InterproceduralActionFinder.java +13 −14 Original line number Diff line number Diff line Loading @@ -23,13 +23,12 @@ import java.util.function.Consumer; * declarations define, use or declare which variables, interprocedurally. * @param <A> The action to be searched for */ public abstract class InterproceduralActionFinder<A extends VariableAction> extends BackwardDataFlowAnalysis<CallableDeclaration<?>, CallGraph.Edge<?>, Set<InterproceduralActionFinder.StoredAction<A>>> { public abstract class InterproceduralActionFinder<A extends VariableAction> extends BackwardDataFlowAnalysis<CallGraph.Vertex, CallGraph.Edge<?>, Set<InterproceduralActionFinder.StoredAction<A>>> { protected final Map<CallableDeclaration<?>, CFG> cfgMap; public InterproceduralActionFinder(CallGraph callGraph, Map<CallableDeclaration<?>, CFG> cfgMap) { super(callGraph); this.cfgMap = cfgMap; this.vertexDataMap = new IdentityHashMap<>(); // CallableDeclarations can't be reliably be compared with equals. } // =========================================================== Loading @@ -39,19 +38,19 @@ public abstract class InterproceduralActionFinder<A extends VariableAction> exte /** Entry-point to the class. Performs the analysis and then saves the results to the CFG nodes. */ public void save() { if (!built) analyze(); cfgMap.keySet().forEach(this::saveDeclaration); graph.vertexSet().forEach(this::saveDeclaration); } /** 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(CallableDeclaration<?> declaration) { Set<StoredAction<A>> storedActions = vertexDataMap.get(declaration); protected void saveDeclaration(CallGraph.Vertex vertex) { Set<StoredAction<A>> storedActions = vertexDataMap.get(vertex); // FORMAL: per declaration (1) for (StoredAction<A> sa : storedActions) sa.storeFormal(a -> sandBoxedHandler(declaration, a, this::handleFormalAction)); sa.storeFormal(a -> sandBoxedHandler(vertex.getDeclaration(), a, this::handleFormalAction)); // ACTUAL: per call (n) for (CallGraph.Edge<?> edge : graph.incomingEdgesOf(declaration)) 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))); } Loading @@ -76,7 +75,7 @@ public abstract class InterproceduralActionFinder<A extends VariableAction> exte * is false, primitive parameters will be skipped, as their value cannot be redefined.*/ protected Expression extractArgument(VariableAction action, CallGraph.Edge<?> edge, boolean input) { ResolvedValueDeclaration resolved = action.getNameExpr().resolve(); CallableDeclaration<?> callTarget = graph.getEdgeTarget(edge); CallableDeclaration<?> callTarget = graph.getEdgeTarget(edge).getDeclaration(); if (resolved.isParameter()) { ResolvedParameterDeclaration p = resolved.asParameter(); if (!input && p.getType().isPrimitive()) Loading @@ -91,10 +90,10 @@ public abstract class InterproceduralActionFinder<A extends VariableAction> exte } @Override protected Set<StoredAction<A>> compute(CallableDeclaration<?> declaration, Set<CallableDeclaration<?>> calledDeclarations) { saveDeclaration(declaration); Set<StoredAction<A>> newValue = new HashSet<>(vertexDataMap.get(declaration)); newValue.addAll(initialValue(declaration)); protected Set<StoredAction<A>> compute(CallGraph.Vertex vertex, Set<CallGraph.Vertex> predecessors) { saveDeclaration(vertex); Set<StoredAction<A>> newValue = new HashSet<>(vertexDataMap.get(vertex)); newValue.addAll(initialValue(vertex)); return newValue; } Loading @@ -120,8 +119,8 @@ public abstract class InterproceduralActionFinder<A extends VariableAction> exte r1 = o1.getAction().getNameExpr().resolve(); r2 = o2.getAction().getNameExpr().resolve(); if (r1.isParameter() && r2.isParameter()) return -Integer.compare(ASTUtils.getMatchingParameterIndex(graph.getEdgeTarget(edge), r1.asParameter()), ASTUtils.getMatchingParameterIndex(graph.getEdgeTarget(edge), r2.asParameter())); 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()) Loading