Commit 595506aa authored by Javier Costa's avatar Javier Costa
Browse files

WIP: summary arcs builder

parent feb672c4
Loading
Loading
Loading
Loading
+133 −0
Original line number Diff line number Diff line
package tfm.graphs;

import com.github.javaparser.ast.CompilationUnit;
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.InitializerDeclaration;
import com.github.javaparser.ast.body.MethodDeclaration;
import com.github.javaparser.ast.expr.MethodCallExpr;
import com.github.javaparser.ast.expr.ObjectCreationExpr;
import com.github.javaparser.ast.stmt.ExplicitConstructorInvocationStmt;
import com.github.javaparser.ast.visitor.VoidVisitorAdapter;
import com.github.javaparser.resolution.Resolvable;
import com.github.javaparser.resolution.declarations.ResolvedMethodLikeDeclaration;
import org.jgrapht.graph.DefaultEdge;
import org.jgrapht.graph.DirectedPseudograph;
import org.jgrapht.io.DOTExporter;

import java.util.Deque;
import java.util.LinkedList;

/**
 * A directed graph which displays the available method declarations as nodes and their
 * invocations as edges (caller to callee).
 * <br/>
 * Method declarations include both {@link ConstructorDeclaration constructors}
 * and {@link MethodDeclaration method declarations}.
 * In the future, {@link InitializerDeclaration static initializer blocks} and field initializers will be included.
 * <br/>
 * Method calls include only direct method calls, from {@link MethodCallExpr normal call},
 * 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>> {
    private boolean built = false;

    public CallGraph() {
        super(null, null, false);
    }

    @Override
    public void build(NodeList<CompilationUnit> arg) {
        buildVertices(arg);
        buildEdges(arg);
        built = true;
    }

    @Override
    public boolean isBuilt() {
        return built;
    }

    protected void buildVertices(NodeList<CompilationUnit> arg) {
        arg.accept(new VoidVisitorAdapter<Void>() {
            @Override
            public void visit(MethodDeclaration n, Void arg) {
                addVertex(n);
                super.visit(n, arg);
            }

            @Override
            public void visit(ConstructorDeclaration n, Void arg) {
                addVertex(n);
                super.visit(n, arg);
            }
        }, null);
    }

    protected void buildEdges(NodeList<CompilationUnit> arg) {
        arg.accept(new VoidVisitorAdapter<Void>() {
            private final Deque<CallableDeclaration<?>> declStack = new LinkedList<>();

            // ============ Method declarations ===========
            // There are some locations not considered, which may lead to an error in the stack.
            // 1. Method calls in non-static field initializations are assigned to all constructors of that class
            // 2. Method calls in static field initializations are assigned to the static block of that class

            @Override
            public void visit(MethodDeclaration n, Void arg) {
                declStack.push(n);
                super.visit(n, arg);
                declStack.pop();
            }

            @Override
            public void visit(ConstructorDeclaration n, Void arg) {
                declStack.push(n);
                super.visit(n, arg);
                declStack.pop();
            }

            // =============== Method calls ===============
            @Override
            public void visit(MethodCallExpr n, Void arg) {
                n.resolve().toAst().ifPresent(decl -> addEdge(declStack.peek(), decl, new Edge<>(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)));
                super.visit(n, arg);
            }

            @Override
            public void visit(ExplicitConstructorInvocationStmt n, Void arg) {
                n.resolve().toAst().ifPresent(decl -> addEdge(declStack.peek(), decl, new Edge<>(n)));
                super.visit(n, arg);
            }
        }, null);
    }

    public DOTExporter<CallableDeclaration<?>, Edge<?>> getDOTExporter() {
        int[] id = new int[]{0};
        return new DOTExporter<>(
                decl -> id[0]++ + "",
                decl -> decl.getDeclarationAsString(false, false, false),
                e -> e.getCallExpr().toString()
        );
    }

    public static class Edge<T extends Resolvable<? extends ResolvedMethodLikeDeclaration>> extends DefaultEdge {
        protected T callExpr;

        public Edge(T callExpr) {
            this.callExpr = callExpr;
        }

        public T getCallExpr() {
            return callExpr;
        }
    }
}
+130 −0
Original line number Diff line number Diff line
package tfm.graphs.sdg.sumarcs;

import com.github.javaparser.ast.body.CallableDeclaration;
import com.github.javaparser.ast.body.MethodDeclaration;
import com.github.javaparser.ast.expr.MethodCallExpr;
import tfm.arcs.Arc;
import tfm.arcs.sdg.CallArc;
import tfm.graphs.CallGraph;
import tfm.graphs.sdg.SDG;
import tfm.nodes.ActualIONode;
import tfm.nodes.FormalIONode;
import tfm.nodes.GraphNode;
import tfm.nodes.type.NodeType;
import tfm.utils.Utils;

import java.util.*;
import java.util.stream.Collectors;

public abstract class AnalysisSummaryArcsBuilder extends SummaryArcsBuilder {

    private static class SummaryArcPair {
        FormalIONode in;
        FormalIONode out;
    }

    private final SDG sdg;
    private final CallGraph callGraph;

    protected Map<CallableDeclaration<?>, Set<SummaryArcPair>> vertexDataMap = new HashMap<>();
    protected boolean built = false;

    public AnalysisSummaryArcsBuilder(SDG sdg) {
        super(sdg);

        CallGraph callGraph = new CallGraph();
        callGraph.build(sdg.getCompilationUnits());

        this.sdg = sdg;
        this.callGraph = callGraph;
    }

    public AnalysisSummaryArcsBuilder(SDG sdg, CallGraph callGraph) {
        super(sdg);

        this.sdg = sdg;
        this.callGraph = callGraph;
    }

    public Set<SummaryArcPair> getResult(MethodDeclaration vertex) {
        return vertexDataMap.get(vertex);
    }

    // Al acabar, los summary edges estan colocados
    @Override
    public void visit() {
        assert !built;
        List<CallableDeclaration<?>> workList = new LinkedList<>(callGraph.vertexSet());
        callGraph.vertexSet().forEach(v -> vertexDataMap.put(v, computeSummaryArcs(v)));
        while (!workList.isEmpty()) {
            List<CallableDeclaration<?>> newWorkList = new LinkedList<>();
            for (CallableDeclaration<?> vertex : workList) {
                updateVertex(vertex);
                Set<SummaryArcPair> newValue = computeSummaryArcs(vertex); // now with new arcs!!!
                if (!Objects.equals(vertexDataMap.get(vertex), newValue)) {
                    vertexDataMap.put(vertex, newValue);
                    newWorkList.addAll(callGraph.incomingEdgesOf(vertex).stream()
                            .map(callGraph::getEdgeSource).collect(Collectors.toSet()));
                }
            }
            workList = newWorkList;
        }
        vertexDataMap = Collections.unmodifiableMap(vertexDataMap);
        built = true;
    }

    protected void updateVertex(CallableDeclaration<?> declaration) {
        if (!declaration.isMethodDeclaration()) {
            return; // Parse only method declarations
        }

        Optional<GraphNode<MethodDeclaration>> optionalMethodDeclarationNode = sdg.findNodeByASTNode(declaration.asMethodDeclaration());

        if (optionalMethodDeclarationNode.isEmpty()) {
            return;
        }

        GraphNode<MethodDeclaration> methodDeclarationNode = optionalMethodDeclarationNode.get();

        // Copiar los summaries que hay en 'vertexDataMap' a todas las llamadas a metodo contenidas en este vertices (ver outgoingEdges)

        // Get call arcs from this declaration
        Set<CallArc> methodCallExprNodes = sdg.incomingEdgesOf(methodDeclarationNode).stream()
                .filter(Arc::isCallArc)
                .map(Arc::asCallArc)
                .collect(Collectors.toSet());

        for (CallArc callArc : methodCallExprNodes) {
            GraphNode<?> methodCallNode = sdg.getEdgeSource(callArc);

            for (SummaryArcPair summaryArcPair : vertexDataMap.getOrDefault(declaration, Utils.emptySet())) {
                FormalIONode inFormalNode = summaryArcPair.in;
                FormalIONode outFormalNode = summaryArcPair.out;

                Optional<ActualIONode> optionalIn = sdg.outgoingEdgesOf(methodCallNode).stream()
                        .filter(arc -> (sdg.getEdgeTarget(arc)).getNodeType() == NodeType.ACTUAL_IN)
                        .map(arc -> (ActualIONode) sdg.getEdgeTarget(arc))
                        .filter(actualIONode -> actualIONode.matchesFormalIO(inFormalNode))
                        .findFirst();

                Optional<ActualIONode> optionalOut = sdg.outgoingEdgesOf(methodCallNode).stream()
                        .filter(arc -> (sdg.getEdgeTarget(arc)).getNodeType() == NodeType.ACTUAL_OUT)
                        .map(arc -> (ActualIONode) sdg.getEdgeTarget(arc))
                        .filter(actualIONode -> actualIONode.matchesFormalIO(outFormalNode))
                        .findFirst();

                if (optionalIn.isEmpty() || optionalOut.isEmpty()) {
                    continue;
                }

                sdg.addSummaryArc(optionalIn.get(), optionalOut.get());
            }
        }
    }

    protected Set<SummaryArcPair> computeSummaryArcs(CallableDeclaration<?> declaration) {
        // Para cada formal-out, hacer slice intraprocedural. Anotar los formal-in incluidos. IMPORTANTE, SUMMARY SON INTRAPROC.
        // TODO
        return null;
    }
}