Commit 14d6553a authored by Javier Costa's avatar Javier Costa
Browse files

WIP: SDGVisitor

parent 5e79e173
Loading
Loading
Loading
Loading
+2 −2
Original line number Diff line number Diff line
@@ -13,8 +13,8 @@ import java.util.Optional;

public class Main {

    public static final String PROGRAM = Utils.PROGRAMS_FOLDER + "pdg/Test.java";
    public static final String GRAPH = GraphLog.PDG;
    public static final String PROGRAM = Utils.PROGRAMS_FOLDER + "sdg/Example1.java";
    public static final String GRAPH = GraphLog.SDG;
    public static final String METHOD = "main";

    public static void main(String[] args) throws IOException {
+12 −0
Original line number Diff line number Diff line
@@ -4,6 +4,10 @@ import tfm.utils.Logger;

public class Example1 {

    public Example1() {

    }

    public static void main(String[] args) {
        int x = 1;
        int y = 2;
@@ -17,4 +21,12 @@ public class Example1 {
        int res = x + y;
        return res;
    }

    public int m1() {
        return 1;
    }

    public int m2() {
        return m1();
    }
}
+91 −0
Original line number Diff line number Diff line
package tfm.utils;

import com.github.javaparser.ast.CompilationUnit;
import com.github.javaparser.ast.Node;
import com.github.javaparser.ast.body.ClassOrInterfaceDeclaration;
import com.github.javaparser.ast.body.MethodDeclaration;

import java.util.Objects;
import java.util.Optional;

public class Context {

    private CompilationUnit currentCU;
    private ClassOrInterfaceDeclaration currentClass;
    private MethodDeclaration currentMethod;

    public Context() {

    }

    public Context(CompilationUnit cu) {
        this(cu, null, null);
    }

    public Context(CompilationUnit cu, ClassOrInterfaceDeclaration clazz) {
        this(cu, clazz, null);
    }

    public Context(CompilationUnit cu, ClassOrInterfaceDeclaration clazz, MethodDeclaration method) {
        this.currentCU = cu;
        this.currentClass = clazz;
        this.currentMethod = method;
    }

    public Optional<CompilationUnit> getCurrentCU() {
        return Optional.ofNullable(currentCU);
    }

    public Optional<ClassOrInterfaceDeclaration> getCurrentClass() {
        return Optional.ofNullable(currentClass);
    }

    public Optional<MethodDeclaration> getCurrentMethod() {
        return Optional.ofNullable(currentMethod);
    }

    public void setCurrentCU(CompilationUnit currentCU) {
        this.currentCU = currentCU;
    }

    public void setCurrentClass(ClassOrInterfaceDeclaration currentClass) {
        this.currentClass = currentClass;
    }

    public void setCurrentMethod(MethodDeclaration currentMethod) {
        this.currentMethod = currentMethod;
    }

    @Override
    public int hashCode() {
        return getCurrentCU().map(Node::hashCode).orElse(0) +
                getCurrentClass().map(Node::hashCode).orElse(0) +
                getCurrentMethod().map(Node::hashCode).orElse(0);
    }

    @Override
    public boolean equals(Object obj) {
        if (this == obj) {
            return true;
        }

        if (!(obj instanceof Context)) {
            return false;
        }

        Context other = (Context) obj;

        return Objects.equals(currentCU, other.currentCU) &&
                Objects.equals(currentClass, other.currentClass) &&
                Objects.equals(currentMethod, other.currentMethod);
    }

    @Override
    public String toString() {
        return String.format("Context{compilationUnit: %s, class: %s, method: %s}",
                getCurrentCU().map(Node::toString),
                getCurrentClass().map(Node::toString),
                getCurrentMethod().map(Node::toString)
        );
    }
}
+141 −0
Original line number Diff line number Diff line
package tfm.visitors;

import com.github.javaparser.ast.Node;
import com.github.javaparser.ast.body.ClassOrInterfaceDeclaration;
import com.github.javaparser.ast.body.MethodDeclaration;
import com.github.javaparser.ast.body.VariableDeclarator;
import com.github.javaparser.ast.expr.MethodCallExpr;
import com.github.javaparser.ast.expr.VariableDeclarationExpr;
import com.github.javaparser.ast.stmt.ExpressionStmt;
import com.github.javaparser.ast.type.Type;
import com.github.javaparser.ast.visitor.VoidVisitorAdapter;
import tfm.graphs.SDGGraph;
import tfm.nodes.GraphNode;
import tfm.utils.Context;

import java.lang.reflect.Method;
import java.util.List;
import java.util.Objects;
import java.util.Optional;
import java.util.stream.Collectors;

public class MethodCallVisitor extends VoidVisitorAdapter<Context> {

    SDGGraph sdgGraph;

    public MethodCallVisitor(SDGGraph sdgGraph) {
        this.sdgGraph = sdgGraph;
    }

    @Override
    public void visit(MethodCallExpr methodCallExpr, Context context) {

        Optional<MethodDeclaration> optionalCallingMethod = methodCallExpr.getScope().isPresent()
                ? shouldMakeCallWithScope(methodCallExpr, context)
                : shouldMakeCallWithNoScope(methodCallExpr, context);

        if (!optionalCallingMethod.isPresent()) {
            return;
        }

        // todo make call
    }

    private Optional<MethodDeclaration> shouldMakeCallWithScope(MethodCallExpr methodCallExpr, Context context) {
        assert methodCallExpr.getScope().isPresent();

        String scopeName = methodCallExpr.getScope().get().toString();

        if (!context.getCurrentClass().isPresent()) {
            return Optional.empty();
        }

        ClassOrInterfaceDeclaration currentClass = context.getCurrentClass().get();

        // Check if it's a static method call of current class
        if (!Objects.equals(scopeName, currentClass.getNameAsString())) {

            // Check if 'scopeName' is a variable
            List<GraphNode<?>> declarations = sdgGraph.findDeclarationsOfVariable(scopeName);

            if (declarations.isEmpty()) {
                // It is a static method call of another class. We do nothing
                return Optional.empty();
            }

            /*
                It's a variable since it has declarations. We now have to check if the class name
                is the same as the current class (the object is an instance of our class)
            */
            GraphNode<?> declarationNode = declarations.get(declarations.size() - 1);

            ExpressionStmt declarationExpr = (ExpressionStmt) declarationNode.getAstNode();
            VariableDeclarationExpr variableDeclarationExpr = declarationExpr.getExpression().asVariableDeclarationExpr();

            Optional<VariableDeclarator> optionalVariableDeclarator = variableDeclarationExpr.getVariables().stream()
                    .filter(variableDeclarator -> Objects.equals(variableDeclarator.getNameAsString(), scopeName))
                    .findFirst();

            if (!optionalVariableDeclarator.isPresent()) {
                // should not happen
                return Optional.empty();
            }

            Type variableType = optionalVariableDeclarator.get().getType();

            if (!variableType.isClassOrInterfaceType()) {
                // Not class type
                return Optional.empty();
            }

            if (!Objects.equals(variableType.asClassOrInterfaceType().getNameAsString(), currentClass.getNameAsString())) {
                // object is not instance of our class
                return Optional.empty();
            }

            // if we got here, the object is instance of our class
        }

        // It's a static method call to a method of the current class

        return findMethodInClass(methodCallExpr, currentClass);
    }

    private Optional<MethodDeclaration> shouldMakeCallWithNoScope(MethodCallExpr methodCallExpr, Context context) {
        assert !methodCallExpr.getScope().isPresent();

        /*
            May be a call to a method of the current class or a call to an imported static method.
            In the first case, we make the call. Otherwise, not.
        */

        if (!context.getCurrentClass().isPresent()) {
            return Optional.empty();
        }

        // We get the current class and search along their methods to find the one we're looking for...

        ClassOrInterfaceDeclaration currentClass = context.getCurrentClass().get();

        return findMethodInClass(methodCallExpr, currentClass);
    }

    private Optional<MethodDeclaration> findMethodInClass(MethodCallExpr methodCallExpr, ClassOrInterfaceDeclaration klass) {
        String[] typeParameters = methodCallExpr.getTypeArguments()
                .map(types -> types.stream()
                        .map(Node::toString)
                        .collect(Collectors.toList())
                        .toArray(new String[types.size()])
                ).orElse(new String[]{});

        List<MethodDeclaration> classMethods =
                klass.getMethodsBySignature(methodCallExpr.getNameAsString(), typeParameters);

        if (classMethods.isEmpty()) {
            return Optional.empty(); // The method called is not inside the current class
        }

        // The current method is inside the current class, so we make the call
        return Optional.of(classMethods.get(0));
    }
}
+68 −0
Original line number Diff line number Diff line
package tfm.visitors;

import com.github.javaparser.ast.CompilationUnit;
import com.github.javaparser.ast.body.ClassOrInterfaceDeclaration;
import com.github.javaparser.ast.body.MethodDeclaration;
import com.github.javaparser.ast.visitor.VoidVisitorAdapter;
import tfm.graphs.PDGGraph;
import tfm.graphs.SDGGraph;
import tfm.utils.Context;

import java.util.ArrayList;
import java.util.List;

public class NewSDGVisitor extends VoidVisitorAdapter<Context> {

    SDGGraph sdgGraph;
    List<PDGGraph> pdgGraphs;

    public NewSDGVisitor(SDGGraph sdgGraph) {
        this.sdgGraph = sdgGraph;
        this.pdgGraphs = new ArrayList<>();
    }

    @Override
    public void visit(MethodDeclaration methodDeclaration, Context context) {
        if (!methodDeclaration.getBody().isPresent()) {
            return;
        }

        context.setCurrentMethod(methodDeclaration);

        // 1. Build PDG

        PDGGraph pdgGraph = new PDGGraph();
        PDGCFGVisitor pdgcfgVisitor = new PDGCFGVisitor(pdgGraph);

        methodDeclaration.accept(pdgcfgVisitor, pdgGraph.getRootNode());


        // 2. Expand method call nodes (build input and output variable nodes)
        // 2.1 Visit called methods with this visitor


        // 3. Build summary arcs
    }

    @Override
    public void visit(ClassOrInterfaceDeclaration classOrInterfaceDeclaration, Context context) {
//        if (sdgGraph.getRootNode() != null) {
//            throw new IllegalStateException("¡Solo podemos procesar una clase por el momento!");
//        }

        if (classOrInterfaceDeclaration.isInterface()) {
            throw new IllegalArgumentException("¡Las interfaces no estan permitidas!");
        }

        context.setCurrentClass(classOrInterfaceDeclaration);

        classOrInterfaceDeclaration.accept(this, context);
    }

    @Override
    public void visit(CompilationUnit compilationUnit, Context context) {
        context.setCurrentCU(compilationUnit);

        super.visit(compilationUnit, context);
    }
}