Commit af710942 authored by Sergio Pérez's avatar Sergio Pérez
Browse files

Class Graph with new arcs, members and inheritance relationship

parent 3bf8f3bf
Loading
Loading
Loading
Loading
Loading
+23 −0
Original line number Diff line number Diff line
package es.upv.mist.slicing.arcs.clg;

import es.upv.mist.slicing.arcs.Arc;
import es.upv.mist.slicing.graphs.ClassGraph;
import org.jgrapht.nio.Attribute;
import org.jgrapht.nio.DefaultAttribute;


import java.util.Map;


/**
 * An edge of the {@link ClassGraph}. Represents the inheritance relationship in Java.
 * It goes from the base class to the derived class
 */

public class ExtendsArc extends Arc {
    public Map<String, Attribute> getDotAttributes() {
        Map<String, Attribute> map = super.getDotAttributes();
        map.put("style", DefaultAttribute.createAttribute("dashed"));
        return map;
    }
}
+21 −0
Original line number Diff line number Diff line
package es.upv.mist.slicing.arcs.clg;

import es.upv.mist.slicing.arcs.Arc;
import es.upv.mist.slicing.graphs.ClassGraph;
import org.jgrapht.nio.Attribute;
import org.jgrapht.nio.DefaultAttribute;

import java.util.Map;

/**
 * An edge of the {@link ClassGraph}. Represents the implements relationship in Java.
 * It goes from the interface to the class that implements it.
 */

public class ImplementsArc extends Arc {
    public Map<String, Attribute> getDotAttributes() {
        Map<String, Attribute> map = super.getDotAttributes();
        map.put("style", DefaultAttribute.createAttribute("dashed"));
        return map;
    }
}
+21 −0
Original line number Diff line number Diff line
package es.upv.mist.slicing.arcs.clg;

import es.upv.mist.slicing.arcs.Arc;
import es.upv.mist.slicing.graphs.ClassGraph;
import org.jgrapht.nio.Attribute;
import org.jgrapht.nio.DefaultAttribute;

import java.util.Map;

/**
 * An edge of the {@link ClassGraph}. It represents the membership of a class node.
 * It links the class node and its inner data members/function definitions.
 */

public class MemberArc extends Arc {
    public Map<String, Attribute> getDotAttributes() {
        Map<String, Attribute> map = super.getDotAttributes();
        map.put("style", DefaultAttribute.createAttribute("dashed"));
        return map;
    }
}
+84 −103
Original line number Diff line number Diff line
@@ -2,19 +2,13 @@ package es.upv.mist.slicing.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.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.body.*;
import com.github.javaparser.ast.visitor.VoidVisitorAdapter;
import com.github.javaparser.resolution.Resolvable;
import com.github.javaparser.resolution.declarations.ResolvedMethodLikeDeclaration;
import es.upv.mist.slicing.arcs.clg.ExtendsArc;
import es.upv.mist.slicing.arcs.clg.ImplementsArc;
import es.upv.mist.slicing.arcs.clg.MemberArc;
import es.upv.mist.slicing.graphs.cfg.CFG;
import es.upv.mist.slicing.nodes.GraphNode;
import es.upv.mist.slicing.utils.ASTUtils;
import es.upv.mist.slicing.utils.NodeNotFoundException;
import es.upv.mist.slicing.utils.Utils;
import org.jgrapht.graph.DefaultEdge;
import org.jgrapht.graph.DirectedPseudograph;
@@ -22,27 +16,19 @@ import org.jgrapht.nio.dot.DOTExporter;

import java.util.*;

public class ClassGraph extends DirectedPseudograph<CallGraph.Vertex, CallGraph.Edge<?>> implements Buildable<NodeList<CompilationUnit>> {
    private final Map<CallableDeclaration<?>, CFG> cfgMap;
    private final Map<CallableDeclaration<?>, CallGraph.Vertex> vertexDeclarationMap = new IdentityHashMap<>();
public class ClassGraph extends DirectedPseudograph<ClassGraph.Vertex, DefaultEdge> implements Buildable<NodeList<CompilationUnit>> {

    /** 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
     * objects and there is no relationship to match them */
    private final Map<String, ClassGraph.Vertex> vertexDeclarationMap = new HashMap<>();

    private boolean built = false;

    public ClassGraph(Map<CallableDeclaration<?>, CFG> cfgMap) {
    public ClassGraph() {
        super(null, null, false);
        this.cfgMap = cfgMap;
    }

    /** Resolve a call to its declaration, by using the call AST nodes stored on the edges. */
    public CallableDeclaration<?> getCallTarget(Resolvable<? extends ResolvedMethodLikeDeclaration> call) {
        return edgeSet().stream()
                .filter(e -> e.getCall() == call)
                .map(this::getEdgeTarget)
                .map(CallGraph.Vertex::getDeclaration)
                .map(CallableDeclaration.class::cast)
                .findFirst()
                .orElse(null);
    }

    @Override
    public void build(NodeList<CompilationUnit> arg) {
@@ -58,85 +44,109 @@ public class ClassGraph extends DirectedPseudograph<CallGraph.Vertex, CallGraph.
        return built;
    }

    /** Find the method and constructor declarations (vertices) in the given list of compilation units. */
    /** Find the class declarations, the field declaration, and method and constructor declarations (vertices)
     * in the given list of compilation units. */
    protected void buildVertices(NodeList<CompilationUnit> arg) {
        arg.accept(new VoidVisitorAdapter<Void>() {

//            QUESTIONS & LACKS:
//              1) Is it necessary to include something apart from class vertices?
//              2) Private classes inside other classes?
//              3) Static declaration blocks not considered

            @Override
            public void visit(MethodDeclaration n, Void arg) {
                addDeclaration(n);
            public void visit(ClassOrInterfaceDeclaration n, Void arg) {
                addClassDeclaration(n);
                super.visit(n, arg);
            }

            @Override
            public void visit(FieldDeclaration n, Void arg) {
                addFieldDeclaration(n);
            }

            @Override
            public void visit(MethodDeclaration n, Void arg) {
                addCallableDeclaration(n);
            }

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

    protected void addDeclaration(CallableDeclaration<?> n) {
        CallGraph.Vertex v = new CallGraph.Vertex(n);
        vertexDeclarationMap.put(n, v);
    /** Add a class declaration vertex to the class graph */
    protected void addClassDeclaration(ClassOrInterfaceDeclaration n) {
        ClassGraph.Vertex v = new ClassGraph.Vertex(n);
        // Required string to match ClassOrInterfaceType and ClassOrInterfaceDeclaration
        vertexDeclarationMap.put(n.getNameAsString(), v);
        addVertex(v);
    }

    /** Add a field declaration vertex to the class graph */
    protected void addFieldDeclaration(FieldDeclaration n){
        ClassGraph.Vertex v = new ClassGraph.Vertex(n);
        // Key value: hashCode + toString() to avoid multiple field declarations with the same syntax in different classes
        vertexDeclarationMap.put(n.hashCode() + n.toString(), v);
        addVertex(v);
    }

    protected boolean addEdge(CallableDeclaration<?> source, CallableDeclaration<?> target, Resolvable<? extends ResolvedMethodLikeDeclaration> call) {
        CallGraph.Edge<?> edge = new CallGraph.Edge<>(call, findGraphNode(call, source));
        return addEdge(vertexDeclarationMap.get(source), vertexDeclarationMap.get(target), edge);
    /** Add a method/constructor declaration vertex to the class graph */
    protected void addCallableDeclaration(CallableDeclaration<?> n){
        assert n instanceof ConstructorDeclaration || n instanceof MethodDeclaration;
        ClassGraph.Vertex v = new ClassGraph.Vertex(n);
        vertexDeclarationMap.put(n.hashCode() + n.getSignature().toString(), v);
        addVertex(v);
    }

    /** Find the calls to methods and constructors (edges) in the given list of compilation units. */
    /** Find the class declarations, field declarations, and method declarations and build the corresponding
     * member/extends/implements relationships in the given list of compilation units. */
    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
            private final Deque<ClassOrInterfaceDeclaration> classStack = new LinkedList<>();

            @Override
            public void visit(MethodDeclaration n, Void arg) {
                declStack.push(n);
            public void visit(ClassOrInterfaceDeclaration n, Void arg) {
                classStack.push(n);
                Vertex v = vertexDeclarationMap.get(n.getNameAsString());
                addClassEdges(v);
                super.visit(n, arg);
                declStack.pop();
                classStack.pop();
            }

            @Override
            public void visit(ConstructorDeclaration n, Void arg) {
                declStack.push(n);
                super.visit(n, arg);
                declStack.pop();
            public void visit(FieldDeclaration n, Void arg) {
                ClassOrInterfaceDeclaration clazz = classStack.peek();
                Vertex c = vertexDeclarationMap.get(clazz.getNameAsString());
                Vertex v = vertexDeclarationMap.get(n.hashCode() + n.toString());
                addEdge(c, v, new MemberArc());
            }

            // =============== Method calls ===============
            @Override
            public void visit(MethodCallExpr n, Void arg) {
                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, n));
                super.visit(n, arg);
            public void visit(MethodDeclaration n, Void arg) {
                ClassOrInterfaceDeclaration clazz = classStack.peek();
                Vertex c = vertexDeclarationMap.get(clazz.getNameAsString());
                Vertex v = vertexDeclarationMap.get(n.hashCode() + n.getSignature().toString());
                addEdge(c, v, new MemberArc());
            }

            @Override
            public void visit(ExplicitConstructorInvocationStmt n, Void arg) {
                n.resolve().toAst().ifPresent(decl -> addEdge(declStack.peek(), decl, n));
                super.visit(n, arg);
            public void visit(ConstructorDeclaration n, Void arg) {
                ClassOrInterfaceDeclaration clazz = classStack.peek();
                Vertex c = vertexDeclarationMap.get(clazz.getNameAsString());
                Vertex v = vertexDeclarationMap.get(n.hashCode() + n.getSignature().toString());
                addEdge(c, v, new MemberArc());
            }
        }, null);
    }

    /** Locates the node in the collection of CFGs that contains the given call. */
    protected GraphNode<?> findGraphNode(Resolvable<? extends ResolvedMethodLikeDeclaration> n, CallableDeclaration<?> declaration) {
        for (GraphNode<?> node : cfgMap.get(declaration).vertexSet())
            if (node.containsCall(n))
                return node;
        throw new NodeNotFoundException("call " + n + " could not be located!");
    protected void addClassEdges(Vertex v){
        assert v.declaration instanceof ClassOrInterfaceDeclaration;
        ClassOrInterfaceDeclaration dv = (ClassOrInterfaceDeclaration) v.declaration;
        dv.getExtendedTypes().forEach(p -> addEdge(vertexDeclarationMap.get(p.getNameAsString()), v, new ExtendsArc()));
        dv.getImplementedTypes().forEach(p -> addEdge(vertexDeclarationMap.get(p.getNameAsString()), v, new ImplementsArc()));
    }

    /** Creates a graph-appropriate DOT exporter. */
@@ -150,15 +160,16 @@ public class ClassGraph extends DirectedPseudograph<CallGraph.Vertex, CallGraph.
    /** 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;
        // First ancestor common class in the JavaParser hierarchy for
        // ClassOrInterfaceDeclaration, FieldDeclaration and CallableDeclaration
        protected final BodyDeclaration<?> declaration;

        public Vertex(CallableDeclaration<?> declaration) {
            assert declaration instanceof ConstructorDeclaration || declaration instanceof MethodDeclaration;
        public Vertex(BodyDeclaration<?> declaration) {
            this.declaration = declaration;
        }

        /** The declaration represented by this node. */
        public CallableDeclaration<?> getDeclaration() {
        public BodyDeclaration<?> getDeclaration() {
            return declaration;
        }

@@ -177,36 +188,6 @@ public class ClassGraph extends DirectedPseudograph<CallGraph.Vertex, CallGraph.
            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;
        protected final GraphNode<?> graphNode;

        public Edge(T call, GraphNode<?> graphNode) {
            assert call instanceof MethodCallExpr || call instanceof ObjectCreationExpr || call instanceof ExplicitConstructorInvocationStmt;
            this.call = call;
            this.graphNode = graphNode;
        }

        /** The call represented by this edge. Using the {@link CallGraph} it can be effortlessly resolved. */
        public T getCall() {
            return call;
        }

        /** The graph node that contains the call represented by this edge. */
        public GraphNode<?> getGraphNode() {
            return graphNode;
        }

        @Override
        public String toString() {
            return String.format("%s -%d-> %s",
                    ((CallableDeclaration<?>) getSource()).getDeclarationAsString(false, false, false),
                    graphNode.getId(),
                    ((CallableDeclaration<?>) getTarget()).getDeclarationAsString(false, false, false));
        }
    }
}

+10 −0
Original line number Diff line number Diff line
@@ -15,6 +15,7 @@ import es.upv.mist.slicing.arcs.sdg.ParameterInOutArc;
import es.upv.mist.slicing.arcs.sdg.SummaryArc;
import es.upv.mist.slicing.graphs.Buildable;
import es.upv.mist.slicing.graphs.CallGraph;
import es.upv.mist.slicing.graphs.ClassGraph;
import es.upv.mist.slicing.graphs.Graph;
import es.upv.mist.slicing.graphs.cfg.CFG;
import es.upv.mist.slicing.graphs.cfg.CFGBuilder;
@@ -107,6 +108,7 @@ public class SDG extends Graph implements Sliceable, Buildable<NodeList<Compilat
            // See creation strategy at http://kaz2.dsic.upv.es:3000/Fzg46cQvT1GzHQG9hFnP1g#Using-data-flow-in-the-SDG
            buildCFGs(nodeList);                             // 1
            CallGraph callGraph = createCallGraph(nodeList); // 2
            ClassGraph classGraph = createClassGraph(nodeList); // TODO: Update order and creation strategy
            dataFlowAnalysis(callGraph);                     // 3
            buildAndCopyPDGs();                              // 4
            connectCalls(callGraph);                         // 5
@@ -139,6 +141,14 @@ public class SDG extends Graph implements Sliceable, Buildable<NodeList<Compilat
            return callGraph;
        }

        /** Create class graph from the list of compilation units. */
        protected ClassGraph createClassGraph(NodeList<CompilationUnit> nodeList){
            ClassGraph classGraph = new ClassGraph();
            classGraph.build(nodeList);
            return classGraph;
        }


        /** Perform interprocedural analyses to determine the actual, formal and call return nodes. */
        protected void dataFlowAnalysis(CallGraph callGraph) {
            new InterproceduralDefinitionFinder(callGraph, cfgMap).save(); // 3.1