Search in sources :

Example 6 with DagNode

use of com.randomnoun.build.javaToGraphviz.dag.DagNode in project java-to-graphviz by randomnoun.

the class ControlFlowEdger method addMethodDeclarationEdges.

// draw lines from each statement to each other, with an artifical node at the end
// that all the thrown exceptions throw to, and that all the return statements return to.
// 
// methods don't have any edges leaving them, but we add one anyway so that we can layout
// methods inside anonymous classes better in graphviz. ( this edge will be transparent )
private List<ExitEdge> addMethodDeclarationEdges(Dag dag, DagNode method, LexicalScope scope) {
    MethodDeclaration md = (MethodDeclaration) method.astNode;
    // method.label = "method " + md.getName();
    method.gvAttributes.put("methodName", md.getName().toString());
    // last child is the block
    DagNode methodBlock = method.children.get(method.children.size() - 1);
    addEdge(method, methodBlock);
    LexicalScope lexicalScope = new LexicalScope();
    List<ExitEdge> ee = addBlockEdges(dag, methodBlock, lexicalScope);
    // add a node which all the return edges return to
    // this is an artificial node so maybe only construct it based on some gv declaration earlier on ?
    // (whereas all the other nodes are about as concrete as anything else in IT)
    // CompilationUnit cu = methodBlock.astNode.getParent();
    CompilationUnit cu = ASTResolving.findParentCompilationUnit(methodBlock.astNode);
    int endOfMethodLine = cu.getLineNumber(methodBlock.astNode.getStartPosition() + methodBlock.astNode.getLength());
    DagNode returnNode = new DagNode();
    returnNode.keepNode = method.keepNodeMatcher.matches("methodDeclarationEnd");
    // label this 'end' if it's a void method ?
    returnNode.type = "methodDeclarationEnd";
    returnNode.lineNumber = endOfMethodLine;
    // rn.name = dag.getUniqueName("m_" + endOfMethodLine);
    returnNode.classes.add("method");
    returnNode.classes.add("end");
    // rn.label = "return";
    returnNode.astNode = null;
    // keep the return node in the method grouping
    method.children.add(returnNode);
    DagSubgraph sg = dag.dagNodeToSubgraph.get(method);
    dag.addNode(sg, returnNode);
    for (ExitEdge e : lexicalScope.returnEdges) {
        e.n2 = returnNode;
        addEdge(e);
    }
    // and everything that was thrown connects to this node as well
    for (ExitEdge e : lexicalScope.throwEdges) {
        e.n2 = returnNode;
        addEdge(e);
    }
    // and everything flowing out of the first block connects to this node as well
    for (ExitEdge e : ee) {
        e.n2 = returnNode;
        addEdge(e);
    }
    // there's no exit edges out of a method
    // return Collections.emptyList();
    // maybe there is now so we can draw an edge out of anonymous classes
    ExitEdge e = new ExitEdge();
    e.n1 = returnNode;
    return Collections.singletonList(e);
}
Also used : CompilationUnit(org.eclipse.jdt.core.dom.CompilationUnit) DagNode(com.randomnoun.build.javaToGraphviz.dag.DagNode) MethodDeclaration(org.eclipse.jdt.core.dom.MethodDeclaration) ExitEdge(com.randomnoun.build.javaToGraphviz.dag.ExitEdge) DagSubgraph(com.randomnoun.build.javaToGraphviz.dag.DagSubgraph)

Example 7 with DagNode

use of com.randomnoun.build.javaToGraphviz.dag.DagNode in project java-to-graphviz by randomnoun.

the class ControlFlowEdger method addInfixExpressionEdges.

private List<ExitEdge> addInfixExpressionEdges(Dag dag, DagNode infixNode, LexicalScope scope) {
    InfixExpression ie = (InfixExpression) infixNode.astNode;
    DagNode leftDag = getDagChild(infixNode.children, ie.getLeftOperand(), null);
    DagNode rightDag = getDagChild(infixNode.children, ie.getRightOperand(), null);
    List<DagNode> extendedDags = getDagChildren(infixNode.children, ie.extendedOperands(), null);
    // Operator is a class, not an enum (!)
    Operator op = ie.getOperator();
    infixNode.gvAttributes.put("operatorToken", op.toString());
    // @TODO camelcase
    infixNode.gvAttributes.put("operatorName", Text.getLastComponent(op.getClass().getName()));
    // a + b      becomes a -> b -> +
    // a + b + c  should becomes a -> b -> + -> c -> + , which has two + nodes, even though there's only one in the AST. because you know. eclipse.
    // move infixExpression node after the a and b nodes
    Rejigger rejigger = hoistNode(dag, infixNode, leftDag);
    List<ExitEdge> prevNodes = null;
    if (op == Operator.CONDITIONAL_AND || op == Operator.CONDITIONAL_OR) {
        // InfixExpressions also include shortcut || which doesn't evaluate the second parameter if the first is true
        // so it should be a something a bit more complicated, control-flow wise
        // a
        // true? -N->  b
        // :Y         :
        // v          v
        infixNode.classes.add("infixConditional");
        prevNodes = addExpressionEdges(dag, leftDag, scope);
        prevNodes = rejigger.unhoistNode(dag, prevNodes);
        // graphviz diagram is a bit mong unless we swap the false and true edge orders. maybe.
        ExitEdge trueEdge = prevNodes.get(0);
        trueEdge.classes.add("infixConditional");
        trueEdge.classes.add(op == Operator.CONDITIONAL_OR ? "true" : "false");
        DagEdge falseEdge = addEdge(infixNode, rightDag);
        // well this is the non-shortcut branch, but hey
        falseEdge.classes.add("infixConditional");
        falseEdge.classes.add(op == Operator.CONDITIONAL_OR ? "false" : "true");
        List<ExitEdge> lastPrevNodes = addExpressionEdges(dag, rightDag, scope);
        for (int i = 0; i < extendedDags.size(); i++) {
            // actually probably need to add a new node here
            DagNode n = extendedDags.get(i);
            DagNode extInfixNode = new DagNode();
            extInfixNode.keepNode = infixNode.keepNodeMatcher.matches("infixExpressionCondition");
            // even though it isn't
            extInfixNode.type = "infixExpressionCondition";
            // even though it isn't
            extInfixNode.lineNumber = n.lineNumber;
            extInfixNode.classes.add("infixExpression");
            extInfixNode.classes.add("infixConditional");
            extInfixNode.astNode = null;
            extInfixNode.gvAttributes.put("operatorToken", op.toString());
            // @TODO camelcase
            extInfixNode.gvAttributes.put("operatorName", Text.getLastComponent(op.getClass().getName()));
            DagSubgraph sg = dag.dagNodeToSubgraph.get(infixNode);
            dag.addNode(sg, extInfixNode);
            // needs to be a child of ieNode as well so it's moved to subgraphs when that node moves
            infixNode.children.add(extInfixNode);
            for (ExitEdge e : lastPrevNodes) {
                e.n2 = extInfixNode;
                dag.edges.add(e);
            }
            trueEdge = new ExitEdge();
            trueEdge.n1 = extInfixNode;
            trueEdge.classes.add("infixConditional");
            trueEdge.classes.add(op == Operator.CONDITIONAL_OR ? "true" : "false");
            prevNodes.add(0, trueEdge);
            falseEdge = addEdge(extInfixNode, n);
            // well this is the non-shortcut branch, but hey
            falseEdge.classes.add("infixConditional");
            falseEdge.classes.add(op == Operator.CONDITIONAL_OR ? "false" : "true");
            lastPrevNodes = addExpressionEdges(dag, n, scope);
        }
        prevNodes.addAll(lastPrevNodes);
    } else {
        // non-shortcut e.g. +, just evaluate in order
        for (DagNode a : infixNode.children) {
            if (prevNodes != null) {
                for (ExitEdge e : prevNodes) {
                    e.n2 = a;
                    addEdge(e);
                }
            }
            prevNodes = addExpressionEdges(dag, a, scope);
        }
        prevNodes = rejigger.unhoistNode(dag, prevNodes);
    }
    return prevNodes;
}
Also used : Operator(org.eclipse.jdt.core.dom.InfixExpression.Operator) DagNode(com.randomnoun.build.javaToGraphviz.dag.DagNode) ExitEdge(com.randomnoun.build.javaToGraphviz.dag.ExitEdge) InfixExpression(org.eclipse.jdt.core.dom.InfixExpression) DagSubgraph(com.randomnoun.build.javaToGraphviz.dag.DagSubgraph) DagEdge(com.randomnoun.build.javaToGraphviz.dag.DagEdge)

Example 8 with DagNode

use of com.randomnoun.build.javaToGraphviz.dag.DagNode in project java-to-graphviz by randomnoun.

the class ControlFlowEdger method addTryEdges.

// draw branches into and out of try statements
// try/catch/finally's are weird things to draw; what I've done is:
// * try resources link to try body
// * try body links to finally body
// * any throw edge in the try body links to all catch bodies
// ( ideally it'd just link to the correct catch body, but that requires knowing the class hierarchies, which we don't know yet )
// * return edges from the try body lead into the finally
// * exit edges are from the finally body, or from the try & catch bodies if there's no finally
// in the css, it helps if you put subgraphs with borders around the try, catch and finally bodies.
private List<ExitEdge> addTryEdges(Dag dag, DagNode tryNode, LexicalScope scope) {
    // draw the edges
    TryStatement ts = (TryStatement) tryNode.astNode;
    List<DagNode> resourceDags = getDagChildren(tryNode.children, ts.resources(), "tryResource");
    DagNode bodyDag = getDagChild(tryNode.children, ts.getBody(), "tryBody");
    // tryCatch ?
    List<DagNode> catchClauseDags = getDagChildren(tryNode.children, ts.catchClauses(), "catch");
    // tryFinally ?
    DagNode finallyDag = getDagChild(tryNode.children, ts.getFinally(), "finally");
    boolean hasResource = false;
    if (bodyDag == null) {
        throw new IllegalStateException("try with no body");
    }
    LexicalScope throwScope = scope.newThrowScope();
    ExitEdge ee = new ExitEdge();
    ee.n1 = tryNode;
    List<ExitEdge> prevNodes = Collections.singletonList(ee);
    for (DagNode rd : resourceDags) {
        if (prevNodes != null) {
            for (ExitEdge e : prevNodes) {
                e.n2 = rd;
                addEdge(e);
            }
        }
        // if exceptions occur here they pass to the exception handler defined here
        prevNodes = addExpressionEdges(dag, rd, throwScope);
        hasResource = true;
    }
    for (ExitEdge e : prevNodes) {
        e.n2 = bodyDag;
        addEdge(e);
    }
    // addEdge(tryNode,  bodyDag);
    List<ExitEdge> tryPrevNodes = new ArrayList<>(addEdges(dag, bodyDag, throwScope));
    for (DagNode ccDag : catchClauseDags) {
        for (ExitEdge e : throwScope.throwEdges) {
            // don't know which catch without a type hierarchy so create an edge for each one
            DagEdge de = new DagEdge();
            de.n1 = e.n1;
            de.n2 = ccDag;
            de.classes.add("throw");
            addEdge(de);
        }
        // original scope for exceptions thrown in exception handlers
        // @TODO not sure about 'return's here though. feel they should still go through the finally.
        List<ExitEdge> ccPrevNodes = addEdges(dag, ccDag, scope);
        tryPrevNodes.addAll(ccPrevNodes);
    }
    if (finallyDag != null) {
        // if hasResource == true, we could create an artificial node at the top of the finally clause
        // that closes the Closable resources
        // returns within the try{} finally{} go through the finally
        boolean returnIntoFinally = false, otherIntoFinally = false;
        ;
        for (ExitEdge e : throwScope.returnEdges) {
            e.n2 = finallyDag;
            addEdge(e);
            returnIntoFinally = true;
        }
        for (ExitEdge e : tryPrevNodes) {
            e.n2 = finallyDag;
            addEdge(e);
            otherIntoFinally = true;
        }
        tryPrevNodes = addEdges(dag, finallyDag, scope);
        // then this finally can return as well
        if (returnIntoFinally) {
            // (as we do for methods)
            for (ExitEdge e : tryPrevNodes) {
                ee = new ExitEdge();
                ee.n1 = e.n1;
                ee.classes.add("return");
                scope.returnEdges.add(ee);
            }
            if (!otherIntoFinally) {
                // everything returns
                tryPrevNodes = Collections.emptyList();
            }
        }
    } else {
        // no finally block, returns are handled as before
        scope.returnEdges.addAll(throwScope.returnEdges);
    }
    return tryPrevNodes;
}
Also used : DagNode(com.randomnoun.build.javaToGraphviz.dag.DagNode) TryStatement(org.eclipse.jdt.core.dom.TryStatement) ExitEdge(com.randomnoun.build.javaToGraphviz.dag.ExitEdge) ArrayList(java.util.ArrayList) DagEdge(com.randomnoun.build.javaToGraphviz.dag.DagEdge)

Example 9 with DagNode

use of com.randomnoun.build.javaToGraphviz.dag.DagNode in project java-to-graphviz by randomnoun.

the class ControlFlowEdger method addVariableDeclarationExpressionEdges.

// same as VariableDeclaration, except it's an expression ( e.g. for loop initialisers )
private List<ExitEdge> addVariableDeclarationExpressionEdges(Dag dag, DagNode node, LexicalScope scope) {
    VariableDeclarationExpression vde = (VariableDeclarationExpression) node.astNode;
    List<DagNode> fragments = getDagChildren(node.children, vde.fragments(), null);
    node.gvAttributes.put("type", vde.getType().toString());
    ExitEdge e = new ExitEdge();
    e.n1 = node;
    List<ExitEdge> prevNodes = Collections.singletonList(e);
    for (DagNode f : fragments) {
        for (ExitEdge pe : prevNodes) {
            pe.n2 = f;
            dag.edges.add(pe);
        }
        // name, dimension(s), expression
        VariableDeclarationFragment vdf = (VariableDeclarationFragment) f.astNode;
        DagNode expressionDag = getDagChild(f.children, vdf.getInitializer(), null);
        if (expressionDag != null) {
            Rejigger rejigger = hoistNode(dag, f, expressionDag);
            prevNodes = addExpressionEdges(dag, expressionDag, scope);
            prevNodes = rejigger.unhoistNode(dag, prevNodes);
        }
        f.gvAttributes.put("type", vde.getType().toString());
        f.gvAttributes.put("variableName", vdf.getName().toString());
    }
    return prevNodes;
}
Also used : DagNode(com.randomnoun.build.javaToGraphviz.dag.DagNode) ExitEdge(com.randomnoun.build.javaToGraphviz.dag.ExitEdge) VariableDeclarationFragment(org.eclipse.jdt.core.dom.VariableDeclarationFragment) VariableDeclarationExpression(org.eclipse.jdt.core.dom.VariableDeclarationExpression)

Example 10 with DagNode

use of com.randomnoun.build.javaToGraphviz.dag.DagNode in project java-to-graphviz by randomnoun.

the class ControlFlowEdger method addWhileEdges.

private List<ExitEdge> addWhileEdges(Dag dag, DagNode whileNode, LexicalScope scope) {
    // draw the edges
    WhileStatement ws = (WhileStatement) whileNode.astNode;
    // org.eclipse.jdt.core.dom.Block in org.eclipse.jdt.core.dom.ForStatement
    // for (DagNode c : forNode.children) { logger.info(c.astNode.getClass().getName() + " in " + c.astNode.getParent().getClass().getName()); }
    DagNode exprDag = getDagChild(whileNode.children, ws.getExpression(), null);
    DagNode repeatingBlock = getDagChild(whileNode.children, ws.getBody(), null);
    // move forStatement node after the initialisation & expression nodes
    Rejigger rejigger = hoistNode(dag, whileNode, exprDag);
    List<ExitEdge> prevNodes = addExpressionEdges(dag, exprDag, scope);
    // this is the node we loop back to
    DagNode firstExpressionNode = rejigger.inEdge.n2;
    prevNodes = rejigger.unhoistNode(dag, prevNodes);
    // DagNode repeatingBlock = whileNode.children.get(0);
    DagEdge whileTrue = addEdge(whileNode, repeatingBlock);
    // whileTrue.label = "Y";
    whileTrue.classes.add("while");
    whileTrue.classes.add("true");
    // List<ExitEdge> whileBreakEdges = new ArrayList<>();
    LexicalScope newScope = scope.newBreakContinueScope(whileNode, firstExpressionNode);
    // new continue node
    List<ExitEdge> repeatingBlockPrevNodes = addEdges(dag, repeatingBlock, newScope);
    for (ExitEdge e : repeatingBlockPrevNodes) {
        DagEdge backEdge = dag.addBackEdge(e.n1, firstExpressionNode, null);
        backEdge.classes.add("while");
    }
    // the entire while
    prevNodes = new ArrayList<>();
    prevNodes.addAll(repeatingBlockPrevNodes);
    prevNodes.addAll(newScope.breakEdges);
    // add an edge from the top of the while as well, as it's possible the repeatingBlock may never execute
    ExitEdge e = new ExitEdge();
    e.n1 = whileNode;
    // e.label = "N";
    e.classes.add("while");
    e.classes.add("false");
    prevNodes.add(e);
    return prevNodes;
}
Also used : DagNode(com.randomnoun.build.javaToGraphviz.dag.DagNode) ExitEdge(com.randomnoun.build.javaToGraphviz.dag.ExitEdge) WhileStatement(org.eclipse.jdt.core.dom.WhileStatement) DagEdge(com.randomnoun.build.javaToGraphviz.dag.DagEdge)

Aggregations

DagNode (com.randomnoun.build.javaToGraphviz.dag.DagNode)55 ExitEdge (com.randomnoun.build.javaToGraphviz.dag.ExitEdge)33 DagEdge (com.randomnoun.build.javaToGraphviz.dag.DagEdge)20 DagSubgraph (com.randomnoun.build.javaToGraphviz.dag.DagSubgraph)11 ArrayList (java.util.ArrayList)8 DagElement (com.randomnoun.build.javaToGraphviz.dom.DagElement)6 GvComment (com.randomnoun.build.javaToGraphviz.comment.GvComment)3 ExceptionErrorHandler (com.randomnoun.build.javaToGraphviz.dom.StylesheetApplier.ExceptionErrorHandler)3 CSSOMParser (com.steadystate.css.parser.CSSOMParser)3 ASTNode (org.eclipse.jdt.core.dom.ASTNode)3 CompilationUnit (org.eclipse.jdt.core.dom.CompilationUnit)3 ConditionalExpression (org.eclipse.jdt.core.dom.ConditionalExpression)3 InfixExpression (org.eclipse.jdt.core.dom.InfixExpression)3 InstanceofExpression (org.eclipse.jdt.core.dom.InstanceofExpression)3 CommentText (com.randomnoun.build.javaToGraphviz.comment.CommentText)2 GvEndGraphComment (com.randomnoun.build.javaToGraphviz.comment.GvEndGraphComment)2 GvEndSubgraphComment (com.randomnoun.build.javaToGraphviz.comment.GvEndSubgraphComment)2 GvGraphComment (com.randomnoun.build.javaToGraphviz.comment.GvGraphComment)2 GvKeepNodeComment (com.randomnoun.build.javaToGraphviz.comment.GvKeepNodeComment)2 GvLiteralComment (com.randomnoun.build.javaToGraphviz.comment.GvLiteralComment)2