Commit 14b6ef14 authored by jacosro's avatar jacosro
Browse files

WIP: MethodCallReplacerVisitor

parent cc05488c
Loading
Loading
Loading
Loading
+162 −14
Original line number Diff line number Diff line
package tfm.graphs.sdg;

import com.github.javaparser.ast.Node;
import com.github.javaparser.ast.NodeList;
import com.github.javaparser.ast.body.ClassOrInterfaceDeclaration;
import com.github.javaparser.ast.body.MethodDeclaration;
import com.github.javaparser.ast.body.Parameter;
import com.github.javaparser.ast.body.VariableDeclarator;
import com.github.javaparser.ast.expr.Expression;
import com.github.javaparser.ast.expr.MethodCallExpr;
import com.github.javaparser.ast.expr.NameExpr;
import com.github.javaparser.ast.expr.VariableDeclarationExpr;
import com.github.javaparser.ast.stmt.ExpressionStmt;
import com.github.javaparser.ast.stmt.ReturnStmt;
import com.github.javaparser.ast.stmt.Statement;
import com.github.javaparser.ast.type.Type;
import com.github.javaparser.ast.visitor.VoidVisitorAdapter;
import tfm.nodes.GraphNode;
import tfm.nodes.factories.InVariableNodeFactory;
import tfm.nodes.factories.OutVariableNodeFactory;
import tfm.utils.Context;
import tfm.utils.Logger;

@@ -21,12 +29,25 @@ import java.util.stream.Collectors;
class MethodCallReplacerVisitor extends VoidVisitorAdapter<Context> {

    private SDG sdg;
    private GraphNode<ExpressionStmt> methodCallNode;
    private GraphNode<?> methodCallNode;

    public MethodCallReplacerVisitor(SDG sdg) {
        this.sdg = sdg;
    }

    private void searchAndSetMethodCallNode(Node node) {
        Optional<GraphNode<Node>> optionalNode = sdg.findNodeByASTNode(node);
        assert optionalNode.isPresent();
        methodCallNode = optionalNode.get();

    }

    @Override
    public void visit(ReturnStmt n, Context arg) {
        searchAndSetMethodCallNode(n);
        super.visit(n, arg);
    }

    @Override
    public void visit(ExpressionStmt n, Context arg) {
        Optional<GraphNode<ExpressionStmt>> optionalNode = sdg.findNodeByASTNode(n);
@@ -51,8 +72,48 @@ class MethodCallReplacerVisitor extends VoidVisitorAdapter<Context> {
            return;
        }

        MethodDeclaration methodCalled = optionalCallingMethod.get();

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

        assert optionalCalledMethodNode.isPresent();

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

        sdg.addCallArc(methodCallNode, calledMethodNode);

        for (Parameter parameter : calledMethodNode.getAstNode().getParameters()) {
            // In expression
            VariableDeclarationExpr inVariableDeclarationExpr = new VariableDeclarationExpr(
                    new VariableDeclarator(
                            parameter.getType(),
                            parameter.getNameAsString() + "_in",
                            new NameExpr(parameter.getNameAsString())
                    )
            );

            ExpressionStmt inExprStmt = new ExpressionStmt(inVariableDeclarationExpr);

            GraphNode<ExpressionStmt> argumentInNode = sdg.addNode(inExprStmt.toString(), inExprStmt, new InVariableNodeFactory());

            // Out expression
            VariableDeclarationExpr outVariableDeclarationExpr = new VariableDeclarationExpr(
                    new VariableDeclarator(
                            parameter.getType(),
                            parameter.getNameAsString(),
                            new NameExpr(parameter.getNameAsString() + "_out")
                    )
            );

            ExpressionStmt outExprStmt = new ExpressionStmt(outVariableDeclarationExpr);

            GraphNode<ExpressionStmt> argumentOutNode = sdg.addNode(outExprStmt.toString(), outExprStmt, new OutVariableNodeFactory());


        }

        // todo make call
        Logger.log(String.format("%s | Method '%s' called", methodCallExpr, optionalCallingMethod.get().getNameAsString()));
        Logger.log("MethodCallReplacerVisitor", String.format("%s | Method '%s' called", methodCallExpr, methodCalled.getNameAsString()));
    }

    private Optional<MethodDeclaration> shouldMakeCallWithScope(MethodCallExpr methodCallExpr, Context context) {
@@ -135,21 +196,108 @@ class MethodCallReplacerVisitor extends VoidVisitorAdapter<Context> {
    }

    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[]{});
        int argumentsCount = methodCallExpr.getArguments().size();

        // Get methods with equal name and parameter count
        List<MethodDeclaration> classMethods = klass.getMethodsByName(methodCallExpr.getNameAsString()).stream()
                // Filter methods with equal or less (varargs) number of parameters
                .filter(methodDeclaration -> {
                    NodeList<Parameter> parameters = methodDeclaration.getParameters();

        List<MethodDeclaration> classMethods =
                klass.getMethodsBySignature(methodCallExpr.getNameAsString(), typeParameters);
                    if (parameters.size() == argumentsCount) {
                        return true;
                    }

                    if (parameters.isEmpty()) {
                        return false;
                    }

                    // There are more arguments than parameters. May be OK if last parameter is varargs
                    if (parameters.size() < argumentsCount) {
                        return parameters.get(parameters.size() - 1).isVarArgs();
                    }

                    // There are less arguments than parameters. May be OK if the last parameter is varargs
                    // and it's omited
                    return parameters.size() - 1 == argumentsCount
                            && parameters.get(parameters.size() - 1).isVarArgs();
                })
                .collect(Collectors.toList());

        if (classMethods.isEmpty()) {
            return Optional.empty(); // The method called is not inside the current class
            // No methods in class with that name and parameter count
            return Optional.empty();
        }

        // The current method is inside the current class, so we make the call
        if (classMethods.size() == 1) {
            // We found the method!
            return Optional.of(classMethods.get(0));
        }

        /*
         * Tricky one! We have to match argument and parameter types, so we have to:
         *   - Differentiate arguments expressions:
         *       - Easy: In case of CastExpr, get the type
         *       - Easy: In case of ObjectCreationExpr, get the type
         *       - Medium: In case of NameExpr, find the declaration and its type
         *       - Medium: In case of LiteralExpr, get the type
         *       - Hard: In case of MethodCallExpr, find MethodDeclaration and its type
         *   - If there is a varargs parameter, check every argument corresponding to it has the same type
         *
         * Example:
         *   At this point these three methods are considered as called:
         *    private void foo(int a, int b) {}
         *    private void foo(String a, String b) {}
         *    private void foo(String a, int... bs) {}
         *
         * We have to match types to get the correct one
         *
         * */

        return classMethods.stream().filter(methodDeclaration -> {
            boolean match = true;

            for (int i = 0; i < methodDeclaration.getParameters().size(); i++) {
                if (!match) {
                    break;
                }

                if (argumentsCount < i) {
                    return argumentsCount == i - 1
                            && methodDeclaration.getParameter(i).isVarArgs();
                }

                // TODO - Convert into a visitor

                Expression argumentExpression = methodCallExpr.getArgument(i);
                Parameter parameter = methodDeclaration.getParameter(i);

                if (argumentExpression.isCastExpr()) {
                    match = Objects.equals(argumentExpression.asCastExpr().getType(), parameter.getType());
                } else if (argumentExpression.isObjectCreationExpr()) {
                    match = Objects.equals(argumentExpression.asObjectCreationExpr().getType(), parameter.getType());
                } else if (argumentExpression.isNameExpr()) {
                    String variableName = argumentExpression.asNameExpr().getNameAsString();

                    List<GraphNode<?>> declarationsOfVariable = sdg.findDeclarationsOfVariable(argumentExpression.asNameExpr().getNameAsString(), methodCallNode);

                    assert !declarationsOfVariable.isEmpty();

                    GraphNode<?> declarationNode = declarationsOfVariable.get(declarationsOfVariable.size() - 1);

                    ExpressionStmt expressionStmt = (ExpressionStmt) declarationNode.getAstNode();

                    assert expressionStmt.getExpression().isVariableDeclarationExpr();

                    match = expressionStmt.getExpression().asVariableDeclarationExpr().getVariables().stream()
                            .filter(variableDeclarator -> Objects.equals(variableDeclarator.getName().asString(), variableName))
                            .findFirst()
                            .map(variableDeclarator -> Objects.equals(variableDeclarator.getType(), parameter.getType()))
                            .orElse(false);
                } // TODO: More checks
            }

            return match;
        }).findFirst();
    }
}