Search in sources :

Example 1 with DagEdge

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

the class ControlFlowEdger method addEnhancedForEdges.

// draw branches into and out of extended for body
private List<ExitEdge> addEnhancedForEdges(Dag dag, DagNode forNode, LexicalScope scope) {
    // draw the edges
    EnhancedForStatement fs = (EnhancedForStatement) forNode.astNode;
    DagNode initialiserDag = getDagChild(forNode.children, fs.getParameter(), "initialiser");
    DagNode exprDag = getDagChild(forNode.children, fs.getExpression(), "expression");
    DagNode repeatingBlock = getDagChild(forNode.children, fs.getBody(), null);
    // move forStatement node after the initialisation & expression nodes
    Rejigger rejigger = hoistNode(dag, forNode, initialiserDag);
    // expression is null for method calls within the same object
    List<ExitEdge> prevNodes = addEdges(dag, initialiserDag, scope);
    DagEdge firstExpressionEdge = null;
    if (prevNodes != null) {
        for (ExitEdge e : prevNodes) {
            e.n2 = exprDag;
            addEdge(e);
            firstExpressionEdge = e;
        }
    }
    prevNodes = addExpressionEdges(dag, exprDag, scope);
    // this is the node we loop back to
    DagNode firstExpressionNode = firstExpressionEdge == null ? rejigger.inEdge.n2 : firstExpressionEdge.n2;
    prevNodes = rejigger.unhoistNode(dag, prevNodes);
    DagEdge forTrue = prevNodes.get(0);
    forTrue.classes.add("enhancedFor");
    forTrue.classes.add("true");
    forTrue.n2 = repeatingBlock;
    addEdge(forTrue);
    LexicalScope newScope = scope.newBreakContinueScope(forNode, firstExpressionNode);
    prevNodes = addEdges(dag, repeatingBlock, newScope);
    for (ExitEdge e : prevNodes) {
        // exprDag
        DagEdge backEdge = dag.addBackEdge(e.n1, firstExpressionNode, null);
        backEdge.classes.add("enhancedFor");
    }
    // the entire for
    prevNodes = new ArrayList<>();
    // prevNodes.addAll(repeatingBlockPrevNodes);  // could add hidden edges here to force the 'after loop' nodes to appear under the for
    // forward edges for any breaks inside the for scoped to this for
    prevNodes.addAll(newScope.breakEdges);
    ExitEdge forFalse = new ExitEdge();
    forFalse.n1 = forNode;
    forFalse.classes.add("enhancedFor");
    forFalse.classes.add("false");
    prevNodes.add(forFalse);
    return prevNodes;
}
Also used : DagNode(com.randomnoun.build.javaToGraphviz.dag.DagNode) ExitEdge(com.randomnoun.build.javaToGraphviz.dag.ExitEdge) EnhancedForStatement(org.eclipse.jdt.core.dom.EnhancedForStatement) DagEdge(com.randomnoun.build.javaToGraphviz.dag.DagEdge)

Example 2 with DagEdge

use of com.randomnoun.build.javaToGraphviz.dag.DagEdge 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 3 with DagEdge

use of com.randomnoun.build.javaToGraphviz.dag.DagEdge 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 4 with DagEdge

use of com.randomnoun.build.javaToGraphviz.dag.DagEdge 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)

Example 5 with DagEdge

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

the class ControlFlowEdger method addClassInstanceCreationEdges.

private List<ExitEdge> addClassInstanceCreationEdges(Dag dag, DagNode node, LexicalScope scope) {
    ClassInstanceCreation cic = (ClassInstanceCreation) node.astNode;
    // maybe we evaluate the index first ? not sure. reckon it's probably the array ref
    DagNode expressionDag = getDagChild(node.children, cic.getExpression(), null);
    List<DagNode> argumentDags = getDagChildren(node.children, cic.arguments(), null);
    DagNode anonClassDag = getDagChild(node.children, cic.getAnonymousClassDeclaration(), "anonymousClass");
    // AnonymousClassDeclaration
    node.gvAttributes.put("type", cic.getType().toString());
    // DagNode indexDag = getDagChild(node.children, fa.getIndex(), null);
    // DagNode fieldDag = getDagChild(node.children, fa.getName(), null); // will put the name on the FA node
    List<ExitEdge> prevNodes = null;
    if (expressionDag != null || argumentDags.size() > 0) {
        // move methodInvocation node after the expression & argument nodes
        Rejigger rejigger = hoistNode(dag, node, expressionDag != null ? expressionDag : argumentDags.get(0));
        // expression is null for method calls within the same object
        if (expressionDag != null) {
            prevNodes = addExpressionEdges(dag, expressionDag, scope);
        }
        for (DagNode a : argumentDags) {
            if (prevNodes != null) {
                for (ExitEdge e : prevNodes) {
                    e.n2 = a;
                    e.classes.add("invocationArgument");
                    addEdge(e);
                }
            }
            prevNodes = addExpressionEdges(dag, a, scope);
        }
        prevNodes = rejigger.unhoistNode(dag, prevNodes);
    } else {
        ExitEdge e = new ExitEdge();
        e.n1 = node;
        prevNodes = Collections.singletonList(e);
    }
    // jam the anonymous class in here as if it was a lambda, but with methods like a class
    if (anonClassDag != null) {
        for (ExitEdge e : prevNodes) {
            e.n2 = anonClassDag;
            addEdge(e);
        }
        AnonymousClassDeclaration acdNode = (AnonymousClassDeclaration) anonClassDag.astNode;
        List<DagNode> bodyDeclarationDags = getDagChildren(anonClassDag.children, acdNode.bodyDeclarations(), null);
        LexicalScope lexicalScope = scope.newTypeScope();
        // add edges for everything in the class
        // @TODO check fields don't appear though. unless they initalise things, then maybe. Or maybe that goes into the constructors.
        // or not.
        // yeesh what about the static initializers. what about them indeed.
        List<ExitEdge> ees = new ArrayList<>();
        for (DagNode n : bodyDeclarationDags) {
            // add a transparent edge to each thing defined in this class so that the 'AnonymousClassDeclaration' node appears above them
            DagEdge e = addEdge(anonClassDag, n);
            e.classes.add("anonymousClassDeclarationBegin");
            ees.addAll(addEdges(dag, n, lexicalScope));
        }
        // add an artificial node so we can create an edge out of this thing
        CompilationUnit cu = ASTResolving.findParentCompilationUnit(acdNode);
        int endOfAnonClassLine = cu.getLineNumber(acdNode.getStartPosition() + acdNode.getLength());
        DagNode returnNode = new DagNode();
        returnNode.keepNode = anonClassDag.keepNodeMatcher.matches("anonymousClassDeclarationEnd");
        returnNode.type = "anonymousClassDeclarationEnd";
        returnNode.lineNumber = endOfAnonClassLine;
        // rn.name = dag.getUniqueName("m_" + endOfMethodLine);
        returnNode.classes.add("anonymousClassDeclaration");
        returnNode.classes.add("end");
        // rn.label = "return";
        returnNode.astNode = null;
        // include the artificial return inside the lambda grouping
        anonClassDag.children.add(returnNode);
        DagSubgraph sg = dag.dagNodeToSubgraph.get(anonClassDag);
        dag.addNode(sg, returnNode);
        for (ExitEdge ee : ees) {
            // add a transparent edge from each thing defined in this class to the artifical node
            // so that it appears underneath them
            ee.n2 = returnNode;
            ee.classes.add("anonymousClassDeclarationEnd");
            addEdge(ee);
        }
        // there's no exit edges out of an anonymous class
        // this gets truncated to the subgraph boundary in some css
        ExitEdge e = new ExitEdge();
        e.n1 = returnNode;
        prevNodes = Collections.singletonList(e);
    }
    return prevNodes;
}
Also used : ClassInstanceCreation(org.eclipse.jdt.core.dom.ClassInstanceCreation) CompilationUnit(org.eclipse.jdt.core.dom.CompilationUnit) AnonymousClassDeclaration(org.eclipse.jdt.core.dom.AnonymousClassDeclaration) ArrayList(java.util.ArrayList) DagSubgraph(com.randomnoun.build.javaToGraphviz.dag.DagSubgraph) DagNode(com.randomnoun.build.javaToGraphviz.dag.DagNode) ExitEdge(com.randomnoun.build.javaToGraphviz.dag.ExitEdge) DagEdge(com.randomnoun.build.javaToGraphviz.dag.DagEdge)

Aggregations

DagEdge (com.randomnoun.build.javaToGraphviz.dag.DagEdge)23 DagNode (com.randomnoun.build.javaToGraphviz.dag.DagNode)20 ExitEdge (com.randomnoun.build.javaToGraphviz.dag.ExitEdge)13 ArrayList (java.util.ArrayList)8 DagSubgraph (com.randomnoun.build.javaToGraphviz.dag.DagSubgraph)6 DagElement (com.randomnoun.build.javaToGraphviz.dom.DagElement)3 CompilationUnit (org.eclipse.jdt.core.dom.CompilationUnit)2 ConditionalExpression (org.eclipse.jdt.core.dom.ConditionalExpression)2 EnhancedForStatement (org.eclipse.jdt.core.dom.EnhancedForStatement)2 InfixExpression (org.eclipse.jdt.core.dom.InfixExpression)2 AstEdger (com.randomnoun.build.javaToGraphviz.astToDag.AstEdger)1 ControlFlowEdger (com.randomnoun.build.javaToGraphviz.astToDag.ControlFlowEdger)1 DagNodeFilter (com.randomnoun.build.javaToGraphviz.astToDag.DagNodeFilter)1 DagStyleApplier (com.randomnoun.build.javaToGraphviz.astToDag.DagStyleApplier)1 LexicalScope (com.randomnoun.build.javaToGraphviz.astToDag.LexicalScope)1 EntryEdge (com.randomnoun.build.javaToGraphviz.dag.EntryEdge)1 ExceptionErrorHandler (com.randomnoun.build.javaToGraphviz.dom.StylesheetApplier.ExceptionErrorHandler)1 CSSOMParser (com.steadystate.css.parser.CSSOMParser)1 IOException (java.io.IOException)1 PrintWriter (java.io.PrintWriter)1