Commit 29127391 authored by Carlos Galindo's avatar Carlos Galindo
Browse files

Enums with fields and methods

parent 06cfc165
Loading
Loading
Loading
Loading
+13 −6
Original line number Diff line number Diff line
@@ -133,14 +133,21 @@ public class CallGraph extends DirectedPseudograph<CallGraph.Vertex, CallGraph.E
    /** 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>() {
            private final Deque<ClassOrInterfaceDeclaration> classStack = new LinkedList<>();
            private final Deque<TypeDeclaration<?>> typeStack = new LinkedList<>();
            private final Deque<CallableDeclaration<?>> declStack = new LinkedList<>();

            @Override
            public void visit(ClassOrInterfaceDeclaration n, Void arg) {
                classStack.push(n);
                typeStack.push(n);
                super.visit(n, arg);
                classStack.pop();
                typeStack.pop();
            }

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

            // ============ Method declarations ===========
@@ -189,7 +196,7 @@ public class CallGraph extends DirectedPseudograph<CallGraph.Vertex, CallGraph.E
                }
                Optional<Expression> scope = call.getScope();
                // Determine the type of the call's scope
                Set<ClassOrInterfaceDeclaration> dynamicTypes;
                Set<? extends TypeDeclaration<?>> dynamicTypes;
                if (scope.isEmpty()) {
                    // a) No scope: any class the method is in, or any outer class if the class is not static.
                    // Early exit: it is easier to find the methods that override the
@@ -199,7 +206,7 @@ public class CallGraph extends DirectedPseudograph<CallGraph.Vertex, CallGraph.E
                    return;
                } else if (scope.get().isThisExpr() && scope.get().asThisExpr().getTypeName().isEmpty()) {
                    // b) just 'this', the current class and any subclass
                    dynamicTypes = classGraph.subclassesOf(classStack.peek());
                    dynamicTypes = classGraph.subclassesOf(typeStack.peek());
                } else if (scope.get().isThisExpr()) {
                    // c) 'ClassName.this', the given class and any subclass
                    dynamicTypes = classGraph.subclassesOf(scope.get().asThisExpr().resolve().asClass());
@@ -229,7 +236,7 @@ public class CallGraph extends DirectedPseudograph<CallGraph.Vertex, CallGraph.E
            @Override
            public void visit(FieldDeclaration n, Void arg) {
                if (declStack.isEmpty() && !n.isStatic()) {
                    for (ConstructorDeclaration cd : classStack.peek().getConstructors()) {
                    for (ConstructorDeclaration cd : typeStack.peek().getConstructors()) {
                        declStack.push(cd);
                        super.visit(n, arg);
                        declStack.pop();
+45 −38
Original line number Diff line number Diff line
@@ -52,9 +52,8 @@ public class ClassGraph extends DirectedPseudograph<ClassGraph.Vertex<?>, ClassG

    /** Locates the vertex that represents a given class or interface declaration.
     *  If the vertex is not contained in the graph, {@code null} will be returned. */
    @SuppressWarnings("unchecked")
    protected Vertex<ClassOrInterfaceDeclaration> findClassVertex(ClassOrInterfaceDeclaration declaration) {
        return (Vertex<ClassOrInterfaceDeclaration>) classDeclarationMap.get(mapKey(declaration));
    protected Vertex<? extends TypeDeclaration<?>> findClassVertex(TypeDeclaration<?> declaration) {
        return classDeclarationMap.get(mapKey(declaration));
    }

    /** Whether this graph contains the given type as a vertex. */
@@ -64,7 +63,7 @@ public class ClassGraph extends DirectedPseudograph<ClassGraph.Vertex<?>, ClassG

    /** Set of method declarations that override the given argument. */
    public Set<MethodDeclaration> overriddenSetOf(MethodDeclaration method) {
        return subclassesStreamOf(findClassVertex(method.findAncestor(ClassOrInterfaceDeclaration.class).orElseThrow()))
        return subclassesStreamOf(findClassVertex(method.findAncestor(TypeDeclaration.class).orElseThrow()))
                .flatMap(vertex -> outgoingEdgesOf(vertex).stream()
                        .filter(ClassArc.Member.class::isInstance)
                        .map(ClassGraph.this::getEdgeTarget)
@@ -95,51 +94,52 @@ public class ClassGraph extends DirectedPseudograph<ClassGraph.Vertex<?>, ClassG
    }

    /** Returns all child classes of the given class, including itself. */
    public Set<ClassOrInterfaceDeclaration> subclassesOf(ClassOrInterfaceDeclaration clazz) {
    public Set<? extends TypeDeclaration<?>> subclassesOf(TypeDeclaration<?> clazz) {
        return subclassesOf(findClassVertex(clazz));
    }

    /** Returns all child classes of the given class, including itself. */
    public Set<ClassOrInterfaceDeclaration> subclassesOf(ResolvedClassDeclaration clazz) {
    public Set<? extends TypeDeclaration<?>> subclassesOf(ResolvedClassDeclaration clazz) {
        return subclassesOf(classDeclarationMap.get(mapKey(clazz)));
    }

    public Set<ClassOrInterfaceDeclaration> subclassesOf(ResolvedReferenceType type) {
    public Set<? extends TypeDeclaration<?>> subclassesOf(ResolvedReferenceType type) {
        return subclassesOf(classDeclarationMap.get(mapKey(type)));
    }

    /** @see #subclassesOf(ClassOrInterfaceDeclaration) */
    @SuppressWarnings("unchecked")
    protected Set<ClassOrInterfaceDeclaration> subclassesOf(Vertex<? extends TypeDeclaration<?>> v) {
    /** @see #subclassesOf(TypeDeclaration) */
    protected Set<? extends TypeDeclaration<?>> subclassesOf(Vertex<? extends TypeDeclaration<?>> v) {
        if (v.getDeclaration() instanceof EnumDeclaration)
            return Collections.emptySet();
        return subclassesStreamOf((Vertex<ClassOrInterfaceDeclaration>) v)
            return Set.of(v.getDeclaration());
        return subclassesStreamOf(v)
                .map(Vertex::getDeclaration)
                .map(ClassOrInterfaceDeclaration.class::cast)
                .collect(Collectors.toSet());
    }

    @SuppressWarnings("unchecked")
    protected Stream<Vertex<ClassOrInterfaceDeclaration>> subclassesStreamOf(Vertex<ClassOrInterfaceDeclaration> classVertex) {
    protected Stream<Vertex<? extends TypeDeclaration<?>>> subclassesStreamOf(Vertex<? extends TypeDeclaration<?>> classVertex) {
        return Stream.concat(Stream.of(classVertex), outgoingEdgesOf(classVertex).stream()
                .filter(ClassArc.Extends.class::isInstance)
                .map(this::getEdgeTarget)
                .map(v -> (Vertex<ClassOrInterfaceDeclaration>) v)
                .map(v -> (Vertex<? extends TypeDeclaration<?>>) v)
                .flatMap(this::subclassesStreamOf));
    }

    // TODO: this method ignores default method implementations in interfaces, as can be overridden.
    /** Looks up a method in the graph, going up the class inheritance tree to locate a
     *  matching method. If no match can be found, throws an {@link IllegalArgumentException}. */
    public MethodDeclaration findMethodByTypeAndSignature(ClassOrInterfaceDeclaration type, CallableDeclaration<?> declaration) {
    public MethodDeclaration findMethodByTypeAndSignature(TypeDeclaration<?> type, CallableDeclaration<?> declaration) {
        Vertex<CallableDeclaration<?>> v = methodDeclarationMap.get(mapKey(declaration, type));
        if (v != null && v.declaration.isMethodDeclaration())
            return v.declaration.asMethodDeclaration();
        Optional<ClassOrInterfaceDeclaration> parentType = parentOf(type);
        if (parentType.isEmpty())
            throw new IllegalArgumentException("Cannot find the given declaration: " + declaration);
        if (type.isClassOrInterfaceDeclaration()) {
            Optional<ClassOrInterfaceDeclaration> parentType = parentOf(type.asClassOrInterfaceDeclaration());
            if (parentType.isPresent())
                return findMethodByTypeAndSignature(parentType.get(), declaration);
        }
        throw new IllegalArgumentException("Cannot find the given declaration: " + declaration);
    }

    /** Find the parent class or interface of a given class. */
    public Optional<ClassOrInterfaceDeclaration> parentOf(ClassOrInterfaceDeclaration declaration) {
@@ -164,7 +164,7 @@ public class ClassGraph extends DirectedPseudograph<ClassGraph.Vertex<?>, ClassG
            else
                return Optional.empty();
        } else if (callableDeclaration.isConstructorDeclaration()) {
            return Optional.of(generateObjectTreeFor(ASTUtils.getClassNode(callableDeclaration)));
            return Optional.of(generateObjectTreeFor(callableDeclaration.findAncestor(TypeDeclaration.class).orElseThrow()));
        } else {
            throw new IllegalArgumentException("Invalid callable declaration type");
        }
@@ -179,7 +179,7 @@ public class ClassGraph extends DirectedPseudograph<ClassGraph.Vertex<?>, ClassG
        return Optional.empty();
    }

    public ObjectTree generateObjectTreeFor(ClassOrInterfaceDeclaration declaration) {
    public ObjectTree generateObjectTreeFor(TypeDeclaration<?> declaration) {
        return generateObjectTreeFor(classDeclarationMap.get(mapKey(declaration)));
    }

@@ -196,11 +196,11 @@ public class ClassGraph extends DirectedPseudograph<ClassGraph.Vertex<?>, ClassG
    protected ObjectTree generatePolyObjectTreeFor(Vertex<? extends TypeDeclaration<?>> classVertex, ObjectTree tree, String level, int depth) {
        if (depth >= StaticConfig.K_LIMIT)
            return tree;
        Set<ClassOrInterfaceDeclaration> types = subclassesOf(classVertex);
        Set<? extends TypeDeclaration<?>> types = subclassesOf(classVertex);
        if (types.isEmpty()) {
            generateObjectTreeFor(classVertex, tree, level, depth);
        } else {
            for (ClassOrInterfaceDeclaration type : types) {
            for (TypeDeclaration<?> type : types) {
                Vertex<? extends TypeDeclaration<?>> subclassVertex = classDeclarationMap.get(mapKey(type));
                if (!findAllFieldsOf(subclassVertex).isEmpty()) {
                    ObjectTree newType = tree.addType(ASTUtils.resolvedTypeDeclarationToResolvedType(type.resolve()), level);
@@ -355,41 +355,48 @@ public class ClassGraph extends DirectedPseudograph<ClassGraph.Vertex<?>, ClassG
     * 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<ClassOrInterfaceDeclaration> classStack = new LinkedList<>();
            private final Deque<TypeDeclaration<?>> typeStack = new LinkedList<>();

            @Override
            public void visit(ClassOrInterfaceDeclaration n, Void arg) {
                classStack.push(n);
                typeStack.push(n);
                var v = classDeclarationMap.get(mapKey(n));
                addClassEdges(v);
                super.visit(n, arg);
                classStack.pop();
                typeStack.pop();
            }

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

            @Override
            public void visit(FieldDeclaration n, Void arg) {
                ClassOrInterfaceDeclaration clazz = classStack.peek();
                assert clazz != null;
                var c = classDeclarationMap.get(mapKey(clazz));
                Vertex<FieldDeclaration> v = fieldDeclarationMap.get(mapKey(n, clazz));
                assert !typeStack.isEmpty();
                TypeDeclaration<?> type = typeStack.peek();
                var c = classDeclarationMap.get(mapKey(type));
                Vertex<FieldDeclaration> v = fieldDeclarationMap.get(mapKey(n, type));
                addEdge(c, v, new ClassArc.Member());
            }

            @Override
            public void visit(MethodDeclaration n, Void arg) {
                ClassOrInterfaceDeclaration clazz = classStack.peek();
                assert clazz != null;
                var c = classDeclarationMap.get(mapKey(clazz));
                Vertex<CallableDeclaration<?>> v = methodDeclarationMap.get(mapKey(n, clazz));
                assert !typeStack.isEmpty();
                TypeDeclaration<?> type = typeStack.peek();
                var c = classDeclarationMap.get(mapKey(type));
                Vertex<CallableDeclaration<?>> v = methodDeclarationMap.get(mapKey(n, type));
                addEdge(c, v, new ClassArc.Member());
            }

            @Override
            public void visit(ConstructorDeclaration n, Void arg) {
                ClassOrInterfaceDeclaration clazz = classStack.peek();
                assert clazz != null;
                var c = classDeclarationMap.get(mapKey(clazz));
                Vertex<CallableDeclaration<?>> v = methodDeclarationMap.get(mapKey(n, clazz));
                assert !typeStack.isEmpty();
                TypeDeclaration<?> type = typeStack.peek();
                var c = classDeclarationMap.get(mapKey(type));
                Vertex<CallableDeclaration<?>> v = methodDeclarationMap.get(mapKey(n, type));
                addEdge(c, v, new ClassArc.Member());
            }
        }, null);
+19 −2
Original line number Diff line number Diff line
@@ -5,6 +5,7 @@ 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.body.TypeDeclaration;
import com.github.javaparser.ast.expr.Expression;
import com.github.javaparser.ast.expr.ThisExpr;
import com.github.javaparser.ast.stmt.ExplicitConstructorInvocationStmt;
@@ -197,7 +198,7 @@ public class JSysCFG extends ESCFG {
            connectTo(n);
            // 2. Insert dynamic class code (only for super())
            if (!n.isThis())
                ASTUtils.getClassInit(ASTUtils.getClassNode(rootNode.getAstNode()), false)
                ASTUtils.getTypeInit(n.findAncestor(TypeDeclaration.class).orElseThrow(), false)
                        .forEach(node -> node.accept(this, arg));
            // 3. Handle exceptions
            super.visitCallForExceptions(n);
@@ -213,7 +214,7 @@ public class JSysCFG extends ESCFG {
        @Override
        public void visit(ConstructorDeclaration n, Void arg) {
            // Insert call to super() if it is implicit.
            if (!ASTUtils.constructorHasExplicitConstructorInvocation(n)){
            if (ASTUtils.shouldInsertExplicitConstructorInvocation(n)) {
                var superCall = new ExplicitConstructorInvocationStmt(null, null, false, null, new NodeList<>());
                methodInsertedInstructions.add(superCall);
                n.getBody().addStatement(0, superCall);
@@ -237,6 +238,22 @@ public class JSysCFG extends ESCFG {
            }
        }

        @Override
        protected void buildEnter(CallableDeclaration<?> callableDeclaration) {
            super.buildEnter(callableDeclaration);
            // enums have no super(), so the implicitly inserted instructions
            // must be placed after the root node
            if (callableDeclaration.isConstructorDeclaration()) {
                ConstructorDeclaration cd = callableDeclaration.asConstructorDeclaration();
                TypeDeclaration<?> type = cd.findAncestor(TypeDeclaration.class).orElseThrow();
                if (!ASTUtils.shouldInsertExplicitConstructorInvocation(cd) &&
                        type.isEnumDeclaration() &&
                        ASTUtils.shouldInsertDynamicInitInEnum(cd)) {
                    ASTUtils.getTypeInit(type, false).forEach(n -> n.accept(this, null));
                }
            }
        }

        /**
         * Sets the expression for all return statements contained in its argument.
         * @param node The AST to search for return statements.
+7 −0
Original line number Diff line number Diff line
@@ -59,6 +59,13 @@ public class JSysDG extends ESSDG {
                        newlyInsertedConstructors.add(n.addConstructor(Modifier.Keyword.PUBLIC));
                    return super.visit(n, arg);
                }

                @Override
                public Visitable visit(EnumDeclaration n, Object arg) {
                    if (n.getConstructors().isEmpty())
                        newlyInsertedConstructors.add(n.addConstructor());
                    return super.visit(n, arg);
                }
            }, null);
        }

+2 −2
Original line number Diff line number Diff line
@@ -2,7 +2,7 @@ package es.upv.mist.slicing.graphs.oo;

import com.github.javaparser.ast.Node;
import com.github.javaparser.ast.body.CallableDeclaration;
import com.github.javaparser.ast.body.ClassOrInterfaceDeclaration;
import com.github.javaparser.ast.body.TypeDeclaration;
import com.github.javaparser.ast.expr.CastExpr;
import com.github.javaparser.ast.expr.Expression;
import com.github.javaparser.ast.expr.MethodCallExpr;
@@ -151,7 +151,7 @@ public class DynamicTypeResolver {
        ResolvedClassDeclaration type = expression.calculateResolvedType().asReferenceType()
                .getTypeDeclaration().orElseThrow().asClass();
        return classGraph.subclassesOf(type).stream()
                .map(ClassOrInterfaceDeclaration::resolve)
                .map(TypeDeclaration::resolve)
                .map(ASTUtils::resolvedTypeDeclarationToResolvedType);
    }
}
Loading