Commit b70f8c10 authored by jacosro's avatar jacosro
Browse files

Implemented resolving method calls with JavaParser

parent e738cfd5
Loading
Loading
Loading
Loading
+105 −0
Original line number Diff line number Diff line
package tfm.exec;

import com.github.javaparser.JavaParser;
import com.github.javaparser.ast.CompilationUnit;
import com.github.javaparser.ast.NodeList;
import com.github.javaparser.ast.body.AnnotationDeclaration;
import com.github.javaparser.ast.body.MethodDeclaration;
import com.github.javaparser.ast.expr.MethodCallExpr;
import com.github.javaparser.ast.visitor.VoidVisitorAdapter;
import com.github.javaparser.resolution.UnsolvedSymbolException;
import com.github.javaparser.resolution.declarations.ResolvedMethodDeclaration;
import com.github.javaparser.symbolsolver.javaparsermodel.JavaParserFacade;
import com.github.javaparser.symbolsolver.model.resolution.SymbolReference;
import com.github.javaparser.symbolsolver.model.resolution.TypeSolver;
import com.github.javaparser.symbolsolver.resolution.typesolvers.CombinedTypeSolver;
import com.github.javaparser.symbolsolver.resolution.typesolvers.JavaParserTypeSolver;
import com.github.javaparser.symbolsolver.resolution.typesolvers.ReflectionTypeSolver;
import tfm.graphs.sdg.SDG;
import tfm.nodes.GraphNode;
import tfm.utils.Context;
import tfm.utils.Logger;

import java.io.File;
import java.io.FileNotFoundException;
import java.util.Arrays;
import java.util.Optional;

public class MethodResolver {

    private static class Args {
        String file;
        String method;
    }

    public static void main(String[] inputArgs) throws FileNotFoundException {
        Args args = parseArgs(inputArgs);

        CompilationUnit cu = JavaParser.parse(new File(args.file));

        SDG sdg = new SDG();
        sdg.build(new NodeList<>(cu));

        VoidVisitorAdapter<Void> visitor = new VoidVisitorAdapter<Void>() {
            @Override
            public void visit(MethodCallExpr n, Void arg) {
                TypeSolver solver = new JavaParserTypeSolver(args.file.substring(0, args.file.lastIndexOf('/')));

                Logger.log("-- Trying to solve method " + n.getNameAsString() + " --");

                Optional<MethodDeclaration> optionalResolvedMethod;

                try {
                    optionalResolvedMethod = getMethodCallWithJavaParserSymbolSolver(n, solver, new ReflectionTypeSolver());
                } catch (UnsolvedSymbolException e) {
                    optionalResolvedMethod = Optional.empty();
                }

                if (!optionalResolvedMethod.isPresent()) {
                    Logger.format("Not found: %s", n);
                    return;
                }

                Logger.format("Found: %s", n.getNameAsString());
                Logger.log(optionalResolvedMethod.get().getSignature().asString());

                Logger.log("-- Trying to match with a node from SDG --");
                Optional<GraphNode<MethodDeclaration>> methodDeclarationNode = optionalResolvedMethod.flatMap(sdg::findNodeByASTNode);

                if (!methodDeclarationNode.isPresent()) {
                    Logger.log("Failed to find node in SDG");
                    return;
                }

                Logger.format("SDG node: %s", methodDeclarationNode.get());

            }
        };

        cu.accept(visitor, null);
    }

    private static Args parseArgs(String[] args) {
        Args res = new Args();

        Logger.log(Arrays.asList(args));

        try {
            res.file = args[0];
            // res.method = args[2];
        } catch (Exception e) {
            Logger.log("Incorrect syntax: java MethodResolver.class <file> <methodName>");
            System.exit(1);
        }

        return res;
    }

    private static Optional<MethodDeclaration> getMethodCallWithJavaParserSymbolSolver(MethodCallExpr methodCallExpr, TypeSolver... solvers) {
        CombinedTypeSolver combinedTypeSolver = new CombinedTypeSolver(solvers);

        SymbolReference<ResolvedMethodDeclaration> solver = JavaParserFacade.get(combinedTypeSolver).solve(methodCallExpr);

        return solver.isSolved() ? solver.getCorrespondingDeclaration().toAst() : Optional.empty();
    }
}
+39 −11
Original line number Diff line number Diff line
package tfm.graphs.sdg;

import com.github.javaparser.ParserConfiguration;
import com.github.javaparser.ast.Node;
import com.github.javaparser.ast.NodeList;
import com.github.javaparser.ast.body.ClassOrInterfaceDeclaration;
@@ -13,12 +14,25 @@ import com.github.javaparser.ast.expr.VariableDeclarationExpr;
import com.github.javaparser.ast.stmt.*;
import com.github.javaparser.ast.type.Type;
import com.github.javaparser.ast.visitor.VoidVisitorAdapter;
import com.github.javaparser.resolution.UnsolvedSymbolException;
import com.github.javaparser.resolution.declarations.ResolvedMethodDeclaration;
import com.github.javaparser.resolution.declarations.ResolvedReferenceTypeDeclaration;
import com.github.javaparser.symbolsolver.JavaSymbolSolver;
import com.github.javaparser.symbolsolver.SourceFileInfoExtractor;
import com.github.javaparser.symbolsolver.javaparsermodel.JavaParserFacade;
import com.github.javaparser.symbolsolver.model.resolution.SymbolReference;
import com.github.javaparser.symbolsolver.model.resolution.TypeSolver;
import com.github.javaparser.symbolsolver.resolution.SymbolSolver;
import com.github.javaparser.symbolsolver.resolution.typesolvers.CombinedTypeSolver;
import com.github.javaparser.symbolsolver.resolution.typesolvers.JavaParserTypeSolver;
import com.github.javaparser.symbolsolver.resolution.typesolvers.ReflectionTypeSolver;
import tfm.nodes.GraphNode;
import tfm.nodes.TypeNodeFactory;
import tfm.nodes.type.NodeType;
import tfm.utils.Context;
import tfm.utils.Logger;

import java.nio.file.Path;
import java.util.List;
import java.util.Objects;
import java.util.Optional;
@@ -99,22 +113,16 @@ class MethodCallReplacerVisitor extends VoidVisitorAdapter<Context> {

        Logger.log("MethodCallReplacerVisitor", context);

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

        if (!optionalCallingMethod.isPresent()) {
            Logger.log("Discarding: " + methodCallExpr);
        if (!optionalNethodDeclarationNode.isPresent()) {
            Logger.format("Not found: '%s'. Discarding");
            return;
        }

        MethodDeclaration methodCalled = optionalCallingMethod.get();
        GraphNode<MethodDeclaration> calledMethodNode = optionalNethodDeclarationNode.get();

        Optional<GraphNode<MethodDeclaration>> optionalCalledMethodNode = sdg.findNodeByASTNode(methodCalled);

        assert optionalCalledMethodNode.isPresent();

        GraphNode<MethodDeclaration> calledMethodNode = optionalCalledMethodNode.get();
        MethodDeclaration methodCalled = calledMethodNode.getAstNode();

        sdg.addCallArc(methodCallNode, calledMethodNode);

@@ -157,6 +165,26 @@ class MethodCallReplacerVisitor extends VoidVisitorAdapter<Context> {
        Logger.log("MethodCallReplacerVisitor", String.format("%s | Method '%s' called", methodCallExpr, methodCalled.getNameAsString()));
    }

    private Optional<GraphNode<MethodDeclaration>> getMethodDeclarationNodeWithJavaParser(MethodCallExpr methodCallExpr) {
        TypeSolver typeSolver = new ReflectionTypeSolver();

        try {
            SymbolReference<ResolvedMethodDeclaration> solver = JavaParserFacade.get(typeSolver).solve(methodCallExpr);

            return solver.isSolved()
                    ? solver.getCorrespondingDeclaration().toAst()
                        .flatMap(methodDeclaration -> sdg.findNodeByASTNode(methodDeclaration))
                    : Optional.empty();
        } catch (UnsolvedSymbolException e) {
            return Optional.empty();
        }
    }

    /**
     * Handles method calls with scope. Examples:
     *  - System.out.println() -> println() is a method call with scope System.out
     *  - new A().getB() -> getB() is a method call with scope new A()
     */
    private Optional<MethodDeclaration> shouldMakeCallWithScope(MethodCallExpr methodCallExpr, Context context) {
        assert methodCallExpr.getScope().isPresent();

+1 −1
Original line number Diff line number Diff line
@@ -28,7 +28,7 @@ public class GraphNode<N extends Node> implements Comparable<GraphNode<?>> {

    public static final NodeFactory DEFAULT_FACTORY = TypeNodeFactory.fromType(NodeType.STATEMENT);

    private NodeType nodeType;
    private final NodeType nodeType;

    private final long id;
    private final String instruction;