Commit 2627a52b authored by Javier Costa's avatar Javier Costa
Browse files

MethodCallReplacer

parent 998bda29
Loading
Loading
Loading
Loading
+2 −2
Original line number Diff line number Diff line
@@ -82,8 +82,8 @@ public abstract class Graph extends edg.graphlib.Graph<String, ArcData> {
    }

    @SuppressWarnings("unchecked")
    public GraphNode<?> getRootNode() {
        return (GraphNode<?>) super.getRootVertex();
    public <ASTNode extends Node> GraphNode<ASTNode> getRootNode() {
        return (GraphNode<ASTNode>) super.getRootVertex();
    }

    public abstract <ASTNode extends Node> GraphNode<ASTNode> addNode(String instruction, ASTNode node);
+2 −1
Original line number Diff line number Diff line
package tfm.graphs;

import com.github.javaparser.ast.Node;
import com.github.javaparser.ast.body.MethodDeclaration;
import com.github.javaparser.ast.stmt.EmptyStmt;
import edg.graphlib.Arrow;
import org.jetbrains.annotations.NotNull;
@@ -25,7 +26,7 @@ public class PDGGraph extends Graph {
    private CFGGraph cfgGraph;

    public PDGGraph() {
        setRootVertex(new GraphNode<>(getNextVertexId(), getRootNodeData(), new EmptyStmt()));
        setRootVertex(new GraphNode<>(getNextVertexId(), getRootNodeData(), new MethodDeclaration()));
    }

    public PDGGraph(CFGGraph cfgGraph) {
+29 −30
Original line number Diff line number Diff line
@@ -4,23 +4,19 @@ import com.github.javaparser.ast.Node;
import com.github.javaparser.ast.body.MethodDeclaration;
import com.github.javaparser.ast.body.Parameter;
import com.github.javaparser.ast.stmt.EmptyStmt;
import edg.graphlib.Visitor;
import tfm.arcs.data.ArcData;
import tfm.nodes.GraphNode;
import tfm.nodes.PDGNode;
import tfm.slicing.SlicingCriterion;
import tfm.utils.Context;

import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import java.util.*;
import java.util.stream.Collectors;

public class SDGGraph extends Graph {

    private List<PDGGraph> pdgGraphList;
    private Map<Context, PDGGraph> contextPDGGraphMap;

    public SDGGraph() {
        this.pdgGraphList = new ArrayList<>();
        this.contextPDGGraphMap = new HashMap<>();
    }

    @Override
@@ -33,7 +29,8 @@ public class SDGGraph extends Graph {

    @Override
    public String toGraphvizRepresentation() {
        return pdgGraphList.stream().map(PDGGraph::toGraphvizRepresentation).collect(Collectors.joining("\n"));
        return contextPDGGraphMap.values().stream()
                .map(PDGGraph::toGraphvizRepresentation).collect(Collectors.joining("\n"));
    }

    @Override
@@ -41,6 +38,26 @@ public class SDGGraph extends Graph {
        return this;
    }

    public Map<Context, PDGGraph> getContextPDGGraphMap() {
        return contextPDGGraphMap;
    }

    public Set<Context> getContexts() {
        return contextPDGGraphMap.keySet();
    }

    public Set<MethodDeclaration> getMethods() {
        return getContexts().stream()
                .filter(context -> context.getCurrentMethod().isPresent())
                .map(context -> context.getCurrentMethod().get())
                .collect(Collectors.toSet());
    }

    public Collection<PDGGraph> getPDGs() {
        return contextPDGGraphMap.values();
    }

    @Deprecated
    public void addPDG(PDGGraph pdgGraph, MethodDeclaration methodDeclaration) {
        if (this.rootVertex == null) {
            this.setRootVertex(new GraphNode<>(getNextVertexId(), methodDeclaration.getNameAsString(), methodDeclaration));
@@ -74,31 +91,13 @@ public class SDGGraph extends Graph {
        }
    }

    public GraphNode<MethodDeclaration> addMethod(MethodDeclaration methodDeclaration, PDGGraph pdgGraph) {
        GraphNode<MethodDeclaration> node = new GraphNode<>(
    public void addMethod(MethodDeclaration methodDeclaration, PDGGraph pdgGraph) {
        GraphNode<MethodDeclaration> methodRootNode = new GraphNode<>(
                getNextVertexId(),
                "ENTER " + methodDeclaration.getDeclarationAsString(false, false, true),
                methodDeclaration
        );

        pdgGraph.depthFirstSearch(pdgGraph.getRootNode(), (Visitor<String, ArcData>) (g, v) -> {
            if (Objects.equals(g.getRootVertex(), v)) {
                return; // We don't care about root node (entry node)
            }

            PDGNode<?> pdgNode = (PDGNode) v;

            GraphNode<?> sdgNode = new GraphNode<>(
                    getNextVertexId(),
                    pdgNode.getData(),
                    pdgNode.getAstNode()
            );


        });

        super.addVertex(node);

        return node;
        super.addVertex(methodRootNode);
    }
}
+4 −0
Original line number Diff line number Diff line
package tfm.visitors.pdg;

import com.github.javaparser.ast.Node;
import com.github.javaparser.ast.body.MethodDeclaration;
import com.github.javaparser.ast.stmt.BlockStmt;
import com.github.javaparser.ast.visitor.VoidVisitorAdapter;
@@ -33,6 +34,9 @@ public class PDGBuilder extends VoidVisitorAdapter<GraphNode<?>> {
        if (!methodDeclaration.getBody().isPresent())
            return;

        // Assign the method declaration to the root node of the PDG graph
        this.pdgGraph.getRootNode().setAstNode(methodDeclaration);

        BlockStmt methodBody = methodDeclaration.getBody().get();

        // build CFG
+11 −126
Original line number Diff line number Diff line
package tfm.visitors.sdg;

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.PDGGraph;
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 MethodCallReplacer {

public class MethodCallReplacer extends VoidVisitorAdapter<Context> {

    SDGGraph sdgGraph;
    private SDGGraph sdgGraph;

    public MethodCallReplacer(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
    public void replace() {
        this.sdgGraph.getContextPDGGraphMap()
                .forEach((context, pdgGraph) -> {
                    if (!context.getCurrentMethod().isPresent()) {
                        return; // Should NOT happen
                    }

        // The current method is inside the current class, so we make the call
        return Optional.of(classMethods.get(0));
                    context.getCurrentMethod().get().accept(new MethodCallReplacerVisitor(pdgGraph), context);
                });
    }
}
Loading