Verified Commit 06a1778d authored by Carlos Galindo's avatar Carlos Galindo
Browse files

CallGraph: wrap declarations in a vertex to guarantee .equals() behaviour.

parent eda610c9
Loading
Loading
Loading
Loading
+1 −1
Original line number Diff line number Diff line
@@ -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>
+1 −1
Original line number Diff line number Diff line
@@ -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>
+3 −4
Original line number Diff line number Diff line
@@ -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) {
@@ -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);
@@ -43,7 +43,6 @@ public abstract class BackwardDataFlowAnalysis<V, E, D> {
            }
            workList = newWorkList;
        }
        vertexDataMap = Collections.unmodifiableMap(vertexDataMap);
        built = true;
    }

+53 −9
Original line number Diff line number Diff line
@@ -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
@@ -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;

@@ -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);
    }
@@ -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>() {
@@ -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);
@@ -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;
+13 −14
Original line number Diff line number Diff line
@@ -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.
    }

    // ===========================================================
@@ -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)));
    }
@@ -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())
@@ -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;
    }

@@ -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