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