Skip to content
CFGVisitor.java 8.61 KiB
Newer Older
Javier Costa's avatar
Javier Costa committed
package tfm.visitors;
Javier Costa's avatar
Javier Costa committed

Javier Costa's avatar
Javier Costa committed
import com.github.javaparser.ast.Node;
Javier Costa's avatar
Javier Costa committed
import com.github.javaparser.ast.NodeList;
Javier Costa's avatar
Javier Costa committed
import com.github.javaparser.ast.body.MethodDeclaration;
Javier Costa's avatar
Javier Costa committed
import com.github.javaparser.ast.expr.*;
Javier Costa's avatar
Javier Costa committed
import com.github.javaparser.ast.stmt.*;
import com.github.javaparser.ast.visitor.VoidVisitorAdapter;
Javier Costa's avatar
Javier Costa committed
import tfm.graphs.CFGGraph;
Javier Costa's avatar
Javier Costa committed
import tfm.nodes.CFGNode;
Javier Costa's avatar
Javier Costa committed
import tfm.utils.Utils;
Javier Costa's avatar
Javier Costa committed

import java.util.*;
Javier Costa's avatar
Javier Costa committed
import java.util.stream.Collectors;
Javier Costa's avatar
Javier Costa committed

public class CFGVisitor extends VoidVisitorAdapter<Void> {

    private CFGGraph graph;

Javier Costa's avatar
Javier Costa committed
    private Queue<CFGNode> lastParentNodes;
Javier Costa's avatar
Javier Costa committed
    private List<CFGNode> bodyBreaks;
Javier Costa's avatar
Javier Costa committed

    public CFGVisitor(CFGGraph graph) {
        this.graph = graph;
        this.lastParentNodes = Collections.asLifoQueue(
                new ArrayDeque<>(
Javier Costa's avatar
Javier Costa committed
                        Collections.singletonList(graph.getRootNode())
Javier Costa's avatar
Javier Costa committed
                )
        );
Javier Costa's avatar
Javier Costa committed

        this.bodyBreaks = new ArrayList<>();
Javier Costa's avatar
Javier Costa committed
    }

    @Override
    public void visit(ExpressionStmt expressionStmt, Void arg) {
Javier Costa's avatar
Javier Costa committed
        String expression = expressionStmt.toString().replace("\"", "\\\"");
Javier Costa's avatar
Javier Costa committed

Javier Costa's avatar
Javier Costa committed
        CFGNode nextNode = addNodeAndArcs(expression, expressionStmt);
Javier Costa's avatar
Javier Costa committed

Javier Costa's avatar
Javier Costa committed
        lastParentNodes.add(nextNode);
Javier Costa's avatar
Javier Costa committed
    }

//    @Override
//    public void visit(VariableDeclarationExpr variableDeclarationExpr, Void arg) {
Javier Costa's avatar
Javier Costa committed
//        CFGNode<String> nextNode = addNodeAndArcs(variableDeclarationExpr.toString());
Javier Costa's avatar
Javier Costa committed
//
//        lastParentNodes.add(nextNode);
//
//        Logger.log(variableDeclarationExpr);
Javier Costa's avatar
Javier Costa committed
//
//        super.visit(variableDeclarationExpr, arg);
//    }

    @Override
    public void visit(IfStmt ifStmt, Void arg) {
Javier Costa's avatar
Javier Costa committed
        CFGNode ifCondition = addNodeAndArcs(
Javier Costa's avatar
Javier Costa committed
                String.format("if (%s)", ifStmt.getCondition().toString()),
Javier Costa's avatar
Javier Costa committed
                ifStmt
Javier Costa's avatar
Javier Costa committed
        );

        lastParentNodes.add(ifCondition);

        // Visit "then"
Javier Costa's avatar
Javier Costa committed
        ifStmt.getThenStmt().accept(this, arg);
Javier Costa's avatar
Javier Costa committed

Javier Costa's avatar
Javier Costa committed
        Queue<CFGNode> lastThenNodes = new ArrayDeque<>(lastParentNodes);
Javier Costa's avatar
Javier Costa committed

        if (ifStmt.hasElseBranch()) {
            lastParentNodes.clear();
            lastParentNodes.add(ifCondition); // Set if nodes as root

Javier Costa's avatar
Javier Costa committed
            ifStmt.getElseStmt().get().accept(this, arg);
Javier Costa's avatar
Javier Costa committed

            lastParentNodes.addAll(lastThenNodes);
        } else {
            lastParentNodes.add(ifCondition);
        }
    }

    @Override
    public void visit(WhileStmt whileStmt, Void arg) {
Javier Costa's avatar
Javier Costa committed
        CFGNode whileCondition = addNodeAndArcs(
Javier Costa's avatar
Javier Costa committed
                String.format("while (%s)", whileStmt.getCondition().toString()),
Javier Costa's avatar
Javier Costa committed
                whileStmt
Javier Costa's avatar
Javier Costa committed
        );

        lastParentNodes.add(whileCondition);

Javier Costa's avatar
Javier Costa committed
        whileStmt.getBody().accept(this, arg);
Javier Costa's avatar
Javier Costa committed

        while (!lastParentNodes.isEmpty()) {
            graph.addControlFlowEdge(lastParentNodes.poll(), whileCondition);
        }

        lastParentNodes.add(whileCondition);
Javier Costa's avatar
Javier Costa committed
        lastParentNodes.addAll(bodyBreaks);
        bodyBreaks.clear();
Javier Costa's avatar
Javier Costa committed
    }

Javier Costa's avatar
Javier Costa committed
    @Override
    public void visit(DoStmt doStmt, Void arg) {
        BlockStmt body = Utils.blockWrapper(doStmt.getBody());

        body.accept(this, arg);

        CFGNode doWhileNode = addNodeAndArcs(
                String.format("while (%s)", doStmt.getCondition()),
                doStmt
        );

        if (!body.isEmpty()) {
            Statement firstBodyStatement = body.getStatement(0);

            graph.findNodeByStatement(firstBodyStatement)
                    .ifPresent(node -> graph.addControlFlowEdge(doWhileNode, node));
        }

        lastParentNodes.add(doWhileNode);
Javier Costa's avatar
Javier Costa committed
        lastParentNodes.addAll(bodyBreaks);
        bodyBreaks.clear();
Javier Costa's avatar
Javier Costa committed
    @Override
    public void visit(ForStmt forStmt, Void arg) {
Javier Costa's avatar
Javier Costa committed
        String inizialization = forStmt.getInitialization().stream()
                .map(Node::toString)
                .collect(Collectors.joining(","));
Javier Costa's avatar
Javier Costa committed

Javier Costa's avatar
Javier Costa committed
        String update = forStmt.getUpdate().stream()
                .map(Node::toString)
                .collect(Collectors.joining(","));
Javier Costa's avatar
Javier Costa committed

Javier Costa's avatar
Javier Costa committed
        Expression comparison = forStmt.getCompare().orElse(new BooleanLiteralExpr(true));
//
//        forStmt.getInitialization().forEach(expression -> new ExpressionStmt(expression).accept(this, null));
Javier Costa's avatar
Javier Costa committed

Javier Costa's avatar
Javier Costa committed
        CFGNode forNode = addNodeAndArcs(
Javier Costa's avatar
Javier Costa committed
                String.format("for (%s;%s;%s)", inizialization, comparison, update),
Javier Costa's avatar
Javier Costa committed
                forStmt
        );

        lastParentNodes.add(forNode);

        BlockStmt body = Utils.blockWrapper(forStmt.getBody());
Javier Costa's avatar
Javier Costa committed

Javier Costa's avatar
Javier Costa committed
//        forStmt.getUpdate().forEach(body::addStatement);
Javier Costa's avatar
Javier Costa committed

Javier Costa's avatar
Javier Costa committed
        body.accept(this, arg);

        while (!lastParentNodes.isEmpty()) {
            graph.addControlFlowEdge(lastParentNodes.poll(), forNode);
        }
Javier Costa's avatar
Javier Costa committed

Javier Costa's avatar
Javier Costa committed
        lastParentNodes.add(forNode);
Javier Costa's avatar
Javier Costa committed
        lastParentNodes.addAll(bodyBreaks);
        bodyBreaks.clear();
Javier Costa's avatar
Javier Costa committed
    }

Javier Costa's avatar
Javier Costa committed
    @Override
    public void visit(ForEachStmt forEachStmt, Void arg) {
Javier Costa's avatar
Javier Costa committed
        CFGNode foreachNode = addNodeAndArcs(
                String.format("for (%s : %s)", forEachStmt.getVariable(), forEachStmt.getIterable()),
                forEachStmt
Javier Costa's avatar
Javier Costa committed
        );

Javier Costa's avatar
Javier Costa committed
        lastParentNodes.add(foreachNode);

        forEachStmt.getBody().accept(this, arg);

        while (!lastParentNodes.isEmpty()) {
            graph.addControlFlowEdge(lastParentNodes.poll(), foreachNode);
        }

        lastParentNodes.add(foreachNode);
Javier Costa's avatar
Javier Costa committed
        lastParentNodes.addAll(bodyBreaks);
        bodyBreaks.clear();
Javier Costa's avatar
Javier Costa committed
    }

    @Override
    public void visit(SwitchStmt switchStmt, Void arg) {
        CFGNode switchNode = addNodeAndArcs(
                String.format("switch (%s)", switchStmt.getSelector()),
                switchStmt
        );

        lastParentNodes.add(switchNode);

Javier Costa's avatar
Javier Costa committed
//        List<CFGNode> lastEntryParents = new ArrayList<>();
//
//        switchStmt.getEntries().forEach(entry -> {
//            Optional<BreakStmt> entryBreak = entry.findFirst(BreakStmt.class, breakStmt -> {
//                Optional<Node> parent = breakStmt.getParentNode();
//
//                return parent.isPresent() && parent.get()   .equals(entry);
//            });
//
//            new BlockStmt(entry.getStatements()).accept(this, arg);
//
//            if (entryBreak.isPresent()) {
//                while (!lastParentNodes.isEmpty()) {
//                    lastEntryParents.add(lastParentNodes.poll());
//                }
//            }
//
//            lastParentNodes.add(switchNode);
//        });
//
//        lastParentNodes.clear();
//        lastParentNodes.addAll(lastEntryParents);
Javier Costa's avatar
Javier Costa committed

Javier Costa's avatar
Javier Costa committed
        List<CFGNode> allEntryBreaks = new ArrayList<>();
Javier Costa's avatar
Javier Costa committed

Javier Costa's avatar
Javier Costa committed
        List<CFGNode> lastEntryStatementsWithNoBreak = new ArrayList<>();

Javier Costa's avatar
Javier Costa committed
        switchStmt.getEntries().forEach(switchEntryStmt -> {
Javier Costa's avatar
Javier Costa committed
            String label = switchEntryStmt.getLabel()
                    .map(expression -> "case " + expression)
                    .orElse("default");

            CFGNode switchEntryNode = addNodeAndArcs(label, switchEntryStmt);

            lastParentNodes.add(switchEntryNode);
            lastParentNodes.addAll(lastEntryStatementsWithNoBreak);
            lastEntryStatementsWithNoBreak.clear();

Javier Costa's avatar
Javier Costa committed
            switchEntryStmt.getStatements().accept(this, null);
Javier Costa's avatar
Javier Costa committed

Javier Costa's avatar
Javier Costa committed
            if (!bodyBreaks.isEmpty()) { // means it has break
Javier Costa's avatar
Javier Costa committed
                allEntryBreaks.addAll(bodyBreaks); // save breaks of entry
Javier Costa's avatar
Javier Costa committed

Javier Costa's avatar
Javier Costa committed
                lastParentNodes.clear();
Javier Costa's avatar
Javier Costa committed
                lastParentNodes.add(switchEntryNode); // Set switch as the only parent
Javier Costa's avatar
Javier Costa committed

Javier Costa's avatar
Javier Costa committed
                bodyBreaks.clear(); // Clear breaks
Javier Costa's avatar
Javier Costa committed
            } else {
                lastEntryStatementsWithNoBreak.addAll(lastParentNodes);
                lastParentNodes.clear();
                lastParentNodes.add(switchEntryNode);
Javier Costa's avatar
Javier Costa committed
            }
Javier Costa's avatar
Javier Costa committed
        });

Javier Costa's avatar
Javier Costa committed
        lastParentNodes.addAll(allEntryBreaks);
Javier Costa's avatar
Javier Costa committed
    }

    @Override
Javier Costa's avatar
Javier Costa committed
    public void visit(BreakStmt breakStmt, Void arg) {
Javier Costa's avatar
Javier Costa committed
        bodyBreaks.addAll(lastParentNodes);
Javier Costa's avatar
Javier Costa committed
    }

    @Override
    public void visit(ContinueStmt continueStmt, Void arg) {
Javier Costa's avatar
Javier Costa committed
        Statement continuableStatement = Utils.findFirstAncestorStatementFrom(continueStmt, Utils::isLoop);
Javier Costa's avatar
Javier Costa committed

Javier Costa's avatar
Javier Costa committed
        CFGNode continuableNode = graph.findNodeByStatement(continuableStatement).get();

        lastParentNodes.forEach(parentNode -> graph.addControlFlowEdge(parentNode, continuableNode));
Javier Costa's avatar
Javier Costa committed
    @Override
    public void visit(MethodDeclaration methodDeclaration, Void arg) {
        super.visit(methodDeclaration, arg);

Javier Costa's avatar
Javier Costa committed
        addNodeAndArcs("Stop", new EmptyStmt());
Javier Costa's avatar
Javier Costa committed
    }

Javier Costa's avatar
Javier Costa committed
    private CFGNode addNodeAndArcs(String nodeData, Statement statement) {
        CFGNode node = graph.addNode(nodeData, statement);
Javier Costa's avatar
Javier Costa committed

Javier Costa's avatar
Javier Costa committed
        CFGNode parent = lastParentNodes.poll(); // ALWAYS exists a parent
Javier Costa's avatar
Javier Costa committed
        graph.addControlFlowEdge(parent, node);

        while (!lastParentNodes.isEmpty()) {
            parent = lastParentNodes.poll();
            graph.addControlFlowEdge(parent, node);
        }

        return node;
    }
Javier Costa's avatar
Javier Costa committed


Javier Costa's avatar
Javier Costa committed
}