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);
}
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;
}
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;
}
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;
}
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;
}
Aggregations