package ai.cfg;
import java.util.Arrays;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Set;
import java.util.Stack;
import org.eclipse.jdt.core.dom.ASTNode;
import org.eclipse.jdt.core.dom.ASTVisitor;
import org.eclipse.jdt.core.dom.AssertStatement;
import org.eclipse.jdt.core.dom.Block;
import org.eclipse.jdt.core.dom.BreakStatement;
import org.eclipse.jdt.core.dom.CatchClause;
import org.eclipse.jdt.core.dom.ConstructorInvocation;
import org.eclipse.jdt.core.dom.ContinueStatement;
import org.eclipse.jdt.core.dom.DoStatement;
import org.eclipse.jdt.core.dom.EmptyStatement;
import org.eclipse.jdt.core.dom.EnhancedForStatement;
import org.eclipse.jdt.core.dom.Expression;
import org.eclipse.jdt.core.dom.ExpressionStatement;
import org.eclipse.jdt.core.dom.ForStatement;
import org.eclipse.jdt.core.dom.IfStatement;
import org.eclipse.jdt.core.dom.Initializer;
import org.eclipse.jdt.core.dom.LabeledStatement;
import org.eclipse.jdt.core.dom.MethodDeclaration;
import org.eclipse.jdt.core.dom.ReturnStatement;
import org.eclipse.jdt.core.dom.SimpleName;
import org.eclipse.jdt.core.dom.Statement;
import org.eclipse.jdt.core.dom.SuperConstructorInvocation;
import org.eclipse.jdt.core.dom.SwitchCase;
import org.eclipse.jdt.core.dom.SwitchStatement;
import org.eclipse.jdt.core.dom.SynchronizedStatement;
import org.eclipse.jdt.core.dom.ThrowStatement;
import org.eclipse.jdt.core.dom.TryStatement;
import org.eclipse.jdt.core.dom.TypeDeclarationStatement;
import org.eclipse.jdt.core.dom.VariableDeclarationExpression;
import org.eclipse.jdt.core.dom.VariableDeclarationFragment;
import org.eclipse.jdt.core.dom.VariableDeclarationStatement;
import org.eclipse.jdt.core.dom.WhileStatement;
import ai.common.Pair;
public class MethodCFGBuilder extends ASTVisitor {
public static class MethodCFGBuilderException extends RuntimeException {
private static final long serialVersionUID = 8706002880560036534L;
public MethodCFGBuilderException(String format, Object... args) {
super(String.format(format, args));
}
}
class ProcessingContext {
private final CFGVertice previousVertice;
private final CFGVertice nextVertice;
private Pair<CFGVertice, BlockContext> returnDetails;
public ProcessingContext(CFGVertice previous, CFGVertice next, Pair<CFGVertice, BlockContext> returnDetails){
previousVertice = previous;
nextVertice = next;
this.returnDetails = returnDetails;
}
public ProcessingContext setPrevious(CFGVertice newPrevious) {
return new ProcessingContext(newPrevious, nextVertice, returnDetails);
}
public ProcessingContext setNext(CFGVertice newNextVertice) {
return new ProcessingContext(previousVertice, newNextVertice, returnDetails);
}
}
class BreakContext {
private final String label;
private final CFGVertice after;
private final BlockContext blockContext;
public BreakContext(SimpleName label, CFGVertice after, BlockContext blockContext) {
this.label = (label == null) ? null : label.getIdentifier();
this.after = after;
this.blockContext = blockContext;
}
}
class ContinueContext {
private final CFGVertice loopIteration;
private final String label;
private final BlockContext blockContext;
public ContinueContext(CFGVertice loopIteration, ASTNode loopNode, BlockContext blockContext){
this.loopIteration = loopIteration;
if (loopNode.getParent() instanceof LabeledStatement) {
LabeledStatement labeled = (LabeledStatement) loopNode.getParent();
this.label = labeled.getLabel().getIdentifier();
} else {
this.label = null;
}
this.blockContext = blockContext;
}
}
class BlockContext {
private final List<SimpleName> variables;
private final ASTNode node;
public BlockContext(ASTNode blockNode){
this.variables = new LinkedList<SimpleName>();
this.node = blockNode;
}
public void add(SimpleName name) {
this.variables.add(name);
}
}
private SimpleName getParentLabel(ASTNode node) {
if (node.getParent().getNodeType() == ASTNode.LABELED_STATEMENT)
return ((LabeledStatement)node.getParent()).getLabel();
else
return null;
}
private final InterestingCodeFragment codeFragment;
private final MethodControlFlowGraph graph;
private ProcessingContext context;
private Stack<ContinueContext> loops = new Stack<ContinueContext>();
private Stack<BreakContext> breakContextStack = new Stack<BreakContext>();
private Stack<BlockContext> blockStack = new Stack<BlockContext>();
private MethodCFGBuilder(InterestingCodeFragment codeFragment) {
this.codeFragment = codeFragment;
this.graph = new MethodControlFlowGraph(codeFragment);
blockStack.add(new BlockContext(codeFragment.getNode()));
this.context = new ProcessingContext(graph.getStartVertice(), graph.getEndVertice(),
Pair.create(graph.getEndVertice(), blockStack.peek()));
}
private void acceptChild(ASTNode node, ProcessingContext context) { // mimic ASTNode.acceptChild
if (node == null){
return;
};
ProcessingContext storedContext = this.context;
this.context = context;
try {
node.accept(this);
} finally {
this.context = storedContext;
}
}
private interface ChildProcessor {
void processChild(ASTNode node, ProcessingContext context);
}
private void processChildren(List<?> nodes, ProcessingContext context, ChildProcessor processor) {
if (nodes.size() == 0) {
graph.createEmptyEdge(context.previousVertice, context.nextVertice);
} else {
Object lastItem = nodes.get(nodes.size()-1);
CFGVertice before = context.previousVertice;
for(Object obj: nodes){
ASTNode child = (ASTNode) obj;
CFGVertice after;
if (obj==lastItem) {
after = context.nextVertice;
} else {
after = graph.addNewVertice(child);
}
before.addDescription(" block before:"+child.toString().split("\n")[0]);
processor.processChild(child, new ProcessingContext(before, after, context.returnDetails));
before = after;
}
}
}
private void acceptChildren(List<?> nodes, ProcessingContext context, final boolean newVariablesAsAssignments,
final BlockContext bc) {
processChildren(nodes, context, new ChildProcessor() {
@Override
public void processChild(ASTNode node, ProcessingContext context) {
graph.addMapping(node, context.previousVertice, context.nextVertice);
if (newVariablesAsAssignments && node.getNodeType() == ASTNode.VARIABLE_DECLARATION_STATEMENT){
VariableDeclarationStatement vds = (VariableDeclarationStatement) node;
processVariableDeclarationFragments(vds.fragments(), context, true, bc);
} else
acceptChild(node, context);
}
});
}
public boolean visit(MethodDeclaration node) {
// don't go inside other methods
return node == this.codeFragment.getNode();
}
public boolean visit(Initializer node) {
// don't go inside other methods
return node == this.codeFragment.getNode();
}
//possible statements are:
// * {@link TypeDeclarationStatement},
private static final Set<Integer> HANDLED_NOTE_TYPES = new HashSet<Integer>(Arrays.asList(
ASTNode.METHOD_DECLARATION,
//various statements
ASTNode.ASSERT_STATEMENT,
ASTNode.BLOCK,
ASTNode.BREAK_STATEMENT,
ASTNode.CONSTRUCTOR_INVOCATION,
ASTNode.CONTINUE_STATEMENT,
ASTNode.DO_STATEMENT,
ASTNode.EMPTY_STATEMENT,
ASTNode.ENHANCED_FOR_STATEMENT,
ASTNode.EXPRESSION_STATEMENT,
ASTNode.FOR_STATEMENT,
ASTNode.IF_STATEMENT,
ASTNode.LABELED_STATEMENT,
ASTNode.RETURN_STATEMENT,
ASTNode.SUPER_CONSTRUCTOR_INVOCATION,
ASTNode.SWITCH_STATEMENT,
ASTNode.SWITCH_CASE,
ASTNode.SYNCHRONIZED_STATEMENT,
ASTNode.THROW_STATEMENT,
ASTNode.TRY_STATEMENT,
ASTNode.TYPE_DECLARATION_STATEMENT,
ASTNode.VARIABLE_DECLARATION_STATEMENT,
ASTNode.WHILE_STATEMENT
));
@Override
public boolean preVisit2(ASTNode node) {
if (!HANDLED_NOTE_TYPES.contains(node.getNodeType()))
throw new MethodCFGBuilderException("Unhandled node: '%s'", node.getClass());
return true;
}
@Override
public boolean visit(ExpressionStatement node) {
graph.createExpressionStatementEdge(context.previousVertice, context.nextVertice, node.getExpression());
return false;
}
@Override
public boolean visit(EmptyStatement node) {
graph.createEmptyEdge(this.context.previousVertice, this.context.nextVertice);
return false;
}
@Override
public boolean visit(LabeledStatement node){
BreakContext bc = new BreakContext(node.getLabel(), context.nextVertice, blockStack.peek());
breakContextStack.push(bc);
acceptChild(node.getBody(), context);
if (breakContextStack.pop() != bc)
throw new MethodCFGBuilderException("Different break context!!");
return false;
}
private List<SimpleName> getAllVariables(BlockContext bc) {
List<SimpleName> result = new LinkedList<SimpleName>();
for(int i = blockStack.size()-1; i >=0 ; i--) {
BlockContext stackContext = blockStack.get(i);
if (stackContext == bc)
return result;
//FIXME: order of variables??
result.addAll(stackContext.variables);
}
throw new MethodCFGBuilderException("Cannot find variables!!");
}
private Pair<CFGVertice, List<SimpleName>> findBreakTarget(SimpleName labelNode) {
if (labelNode == null) {
if (breakContextStack.size() == 0)
throw new MethodCFGBuilderException("No break context found!!");
BreakContext bc = breakContextStack.peek();
return Pair.create(bc.after, getAllVariables(bc.blockContext));
}
String label = labelNode.getIdentifier();
for(int i=breakContextStack.size() - 1; i >= 0; i--) {
BreakContext bc = breakContextStack.get(i);
if (label.equals(bc.label))
return Pair.create(bc.after, getAllVariables(bc.blockContext));
}
throw new MethodCFGBuilderException("Unable to find break target");
}
private Pair<CFGVertice, List<SimpleName>> findLoopContext(SimpleName labelNode) {
if (labelNode == null) {
if (loops.size() == 0)
throw new MethodCFGBuilderException("No loop context found!!");
ContinueContext cc = loops.peek();
return Pair.create(cc.loopIteration, getAllVariables(cc.blockContext));
}
String label = labelNode.getIdentifier();
for (int i=loops.size()-1; i>=0; i--) {
ContinueContext possibleResult = loops.get(i);
if (label.equals(possibleResult.label))
return Pair.create(possibleResult.loopIteration, getAllVariables(possibleResult.blockContext));
}
throw new MethodCFGBuilderException("Unhandled case");
}
@Override
public boolean visit(BreakStatement node) {
//create edge outside the loop
Pair<CFGVertice, List<SimpleName>> target = findBreakTarget(node.getLabel());
graph.createEmptyEdge(context.previousVertice, target.left, target.right);
return false;
}
@Override
public boolean visit(ContinueStatement node) {
Pair<CFGVertice, List<SimpleName>> loop = findLoopContext(node.getLabel());
graph.createEmptyEdge(context.previousVertice, loop.left, loop.right);
return false;
}
private int whileCounter = 0;
@Override
public boolean visit(WhileStatement node) {
ProcessingContext context = this.context;
int id = whileCounter++;
this.context.previousVertice.addDescription("LoopWhile_"+id);
CFGVertice beforeLoopBody = graph.addNewVertice(node.getBody(), "WhileBody_"+id);
ContinueContext loopContext = new ContinueContext(context.previousVertice, node, blockStack.peek());
loops.push(loopContext);
BreakContext bc = null;
if (getParentLabel(node) == null) {
bc = new BreakContext(null, context.nextVertice, blockStack.peek());
breakContextStack.push(bc);
}
acceptChild(node.getBody(), new ProcessingContext(beforeLoopBody, context.previousVertice, context.returnDetails));
if (loops.pop() != loopContext)
throw new MethodCFGBuilderException("Different loop context!!");
if (bc != null && breakContextStack.pop() != bc)
throw new MethodCFGBuilderException("Different break context!!");
graph.addLoopInvariangMapping(node, context.previousVertice);
graph.createConditionalEdge(context.previousVertice, beforeLoopBody, context.nextVertice, node.getExpression());
return false;
}
private int forCounter = 0;
private void processVariableDeclarationFragments(List<?> fragments, ProcessingContext context,
final boolean newVariablesAsAssignemnts, final BlockContext blockContext) {
processChildren(fragments, context, new ChildProcessor() {
@Override
public void processChild(ASTNode fragment, ProcessingContext context) {
VariableDeclarationFragment vdf = (VariableDeclarationFragment) fragment;
if (!newVariablesAsAssignemnts)
blockContext.add(vdf.getName());
graph.createVariableDeclarationEdge(context.previousVertice, context.nextVertice,
vdf.getName(), vdf.getInitializer(), newVariablesAsAssignemnts);
}
});
}
private void processForExpressions(List<?> initializers, ProcessingContext context, final BlockContext bc) {
processChildren(initializers, context, new ChildProcessor() {
@Override
public void processChild(ASTNode node, ProcessingContext context) {
Expression initializer = (Expression) node;
if (initializer.getNodeType() == ASTNode.VARIABLE_DECLARATION_EXPRESSION)
processVariableDeclarationFragments(((VariableDeclarationExpression)initializer).fragments(), context, false,
bc);
else
graph.createExpressionStatementEdge(context.previousVertice, context.nextVertice, initializer);
}
});
}
@Override
public boolean visit(ForStatement node) {
ProcessingContext context = this.context;
int idx = forCounter++;
this.context.previousVertice.addDescription("ForInitialisers_"+idx);
//initialisers
BlockContext currentBlock = blockStack.peek();
BlockContext forBlockContext = pushNewBlockContext(node);
CFGVertice loopVertice = graph.addNewVertice(node, "LoopFor_" + idx);
graph.addLoopInvariangMapping(node, loopVertice);
processForExpressions(node.initializers(), new ProcessingContext(context.previousVertice, loopVertice, context.returnDetails), forBlockContext);
//loop body
CFGVertice beforeLoopBody = graph.addNewVertice(node.getBody(), "ForBody_" + idx);
CFGVertice afterLoopBody = graph.addNewVertice(null, "ForEnd_" + idx);
ContinueContext loopContext = new ContinueContext(afterLoopBody, node, forBlockContext);
loops.push(loopContext);
BreakContext bc = null;
if (getParentLabel(node) == null) {
bc = new BreakContext(null, context.nextVertice, currentBlock);
breakContextStack.push(bc);
}
acceptChild(node.getBody(), new ProcessingContext(beforeLoopBody, afterLoopBody, context.returnDetails));
if (loops.pop() != loopContext)
throw new MethodCFGBuilderException("Different loop context!!");
if (bc != null && breakContextStack.pop() != bc)
throw new MethodCFGBuilderException("Different break context!!");
//updaters
processForExpressions(node.updaters(), new ProcessingContext(afterLoopBody, loopVertice, context.returnDetails), forBlockContext);
if (forBlockContext != blockStack.pop())
throw new MethodCFGBuilderException("Different block context!!");
CFGVertice nextVertice = context.nextVertice;
if (forBlockContext.variables.size() > 0) {
CFGVertice removeInitializers = graph.addNewVertice(node, "Remove initializers");
graph.createEmptyEdge(removeInitializers, nextVertice, forBlockContext.variables);
nextVertice = removeInitializers;
}
graph.createConditionalEdge(loopVertice, beforeLoopBody, nextVertice, node.getExpression());
return false;
}
@Override
public boolean visit(VariableDeclarationStatement node) {
processVariableDeclarationFragments(node.fragments(), context, false, blockStack.peek());
return false;
}
int blockCounter = 0;
private void processBlock(Block node, ProcessingContext context, BlockContext bc) {
CFGVertice removeVars = graph.addNewVertice(null, "Remove block variables:"+blockCounter++);
acceptChildren(node.statements(), context.setNext(removeVars), false, bc);
graph.createEmptyEdge(removeVars, context.nextVertice, bc.variables);
}
@Override
public boolean visit(Block node) {
graph.addMapping(node, context.previousVertice, context.nextVertice);
BlockContext bc = pushNewBlockContext(node);
processBlock(node, this.context, bc);
if (blockStack.pop() != bc)
throw new MethodCFGBuilderException("Different block context!!");
return false;
}
private int ifCounter = 0;
private BlockContext pushNewBlockContext(ASTNode node) {
BlockContext bc = new BlockContext(node);
blockStack.push(bc);
return bc;
}
@Override
public boolean visit(IfStatement node) {
ProcessingContext context = this.context;
int id = ifCounter++;
this.context.previousVertice.addDescription("If_"+id);
//then
ASTNode thenNode = node.getThenStatement();
assert thenNode != null;
CFGVertice positiveStart;
positiveStart = graph.addNewVertice(thenNode, "Then_" + id);
acceptChild(thenNode, context.setPrevious(positiveStart));
//else
ASTNode elseNode = node.getElseStatement();
CFGVertice negativeStart = null;
if (elseNode != null) {
negativeStart = graph.addNewVertice(elseNode, "Else_" + id);
acceptChild(elseNode, context.setPrevious(negativeStart));
} else
negativeStart = context.nextVertice;
graph.createConditionalEdge(context.previousVertice, positiveStart, negativeStart, node.getExpression());
return false;
}
@Override
public boolean visit(ReturnStatement node) {
List<SimpleName> removeVars = getAllVariables(context.returnDetails.right);
graph.createEmptyEdge(context.previousVertice, context.returnDetails.left, removeVars);
return false;
}
@Override
public boolean visit(TryStatement node) {
BlockContext bc = blockStack.peek();
if (node.resources() != null && node.resources().size() > 0)
throw new MethodCFGBuilderException("Not handled case, non empt resources: '%s'", node.resources());
ProcessingContext context = this.context;
//first handle finally and prepare context for the rest
if (node.getFinally() != null) {
CFGVertice finallyStart = graph.addNewVertice(node.getFinally(), "Finally START");
graph.createExceptionEdge(context.previousVertice, finallyStart, null);
acceptChild(node.getFinally(), context.setPrevious(finallyStart));
context = new ProcessingContext(context.previousVertice, finallyStart, Pair.create(finallyStart, bc));
}
//exceptions next
if (node.catchClauses() != null && node.catchClauses().size() > 0) {
for(Object catchClauseObj: node.catchClauses()) {
CatchClause catchClause = (CatchClause) catchClauseObj;
CFGVertice catchStart = graph.addNewVertice(catchClause, "Catch " + catchClause.getException());
graph.createExceptionEdge(context.previousVertice, catchStart, catchClause.getException());
BlockContext cbc = pushNewBlockContext(node);
SimpleName excVariable = catchClause.getException().getName();
if (excVariable!=null)
cbc.add(excVariable);
processBlock(catchClause.getBody(), context.setPrevious(catchStart), cbc);
if (blockStack.pop() != cbc)
throw new MethodCFGBuilderException("Different block context!!");
}
}
acceptChild(node.getBody(), context);
return false;
}
@Override
public boolean visit(ConstructorInvocation node){
graph.createConstructorInvocationEdge(context.previousVertice, context.nextVertice, node);
return false;
}
@Override
public boolean visit(DoStatement node){
ProcessingContext context = this.context;
BlockContext blockContext = blockStack.peek();
int id = whileCounter++;
this.context.previousVertice.addDescription("LoopDoWhile_"+id);
CFGVertice loopCondition = graph.addNewVertice(node, "ConditionDoWhile_"+id);
ContinueContext loopContext = new ContinueContext(context.previousVertice, node, blockContext);
loops.push(loopContext);
BreakContext bc = null;
if (getParentLabel(node) == null) {
bc = new BreakContext(null, context.nextVertice, blockContext);
breakContextStack.push(bc);
}
acceptChild(node.getBody(), context.setNext(loopCondition));
if (loops.pop() != loopContext)
throw new MethodCFGBuilderException("Different loop context!!");
if (bc != null && breakContextStack.pop() != bc)
throw new MethodCFGBuilderException("Different break context!!!");
graph.addLoopInvariangMapping(node, loopCondition);
graph.createConditionalEdge(loopCondition, context.previousVertice, context.nextVertice, node.getExpression());
return false;
}
@Override
public boolean visit(SuperConstructorInvocation node){
graph.createSuperConstructorInvocationEdge(context.previousVertice, context.nextVertice, node);
return false;
}
private List<SimpleName> findBlockVariables(List<?> statements) {
List<SimpleName> result = new LinkedList<SimpleName>();
for(Object obj: statements) {
ASTNode node = (ASTNode) obj;
if (node.getNodeType() == ASTNode.VARIABLE_DECLARATION_STATEMENT) {
VariableDeclarationStatement vds = (VariableDeclarationStatement) node;
for(Object fragmentObj: vds.fragments())
result.add(((VariableDeclarationFragment)fragmentObj).getName());
}
}
return result;
}
private void addNewVariableDefinitions(List<SimpleName> variables, ProcessingContext context, final BlockContext bc) {
processChildren(variables, context, new ChildProcessor() {
@Override
public void processChild(ASTNode node, ProcessingContext context) {
SimpleName name = (SimpleName) node;
graph.createVariableDeclarationEdge(context.previousVertice, context.nextVertice, name, null, false);
bc.add(name);
}
});
}
@Override
public boolean visit(SwitchStatement node) {
ProcessingContext context = this.context;
context.previousVertice.addDescription("Switch");
BlockContext currentBlock = blockStack.peek();
//switch is a block
BlockContext switchBlock = pushNewBlockContext(node);
BreakContext bc = null;
if (getParentLabel(node) == null) {
bc = new BreakContext(null, context.nextVertice, currentBlock);
breakContextStack.push(bc);
}
CFGVertice previousVertice = context.previousVertice;
CFGVertice defaultTarget = null;
List<Statement> lastBlock = null;
List<Pair<CFGVertice, Expression>> cases = new LinkedList<Pair<CFGVertice,Expression>>();
List<SimpleName> blockVariables = findBlockVariables(node.statements());
if (blockVariables.size() > 0) {
CFGVertice afterVariables = graph.addNewVertice(null, "After switch variables");
addNewVariableDefinitions(blockVariables, context.setNext(afterVariables), switchBlock);
CFGVertice cleanVariables = graph.addNewVertice(null, "Remove switch block variables");
graph.createEmptyEdge(cleanVariables, context.nextVertice, switchBlock.variables);
context = new ProcessingContext(afterVariables, cleanVariables, context.returnDetails);
}
for(Object obj: node.statements()) {
Statement stmt = (Statement) obj;
if (stmt.getNodeType() == ASTNode.SWITCH_CASE) {
CFGVertice caseVertice = graph.addNewVertice(stmt, "Switch case: " + stmt);
SwitchCase aCase = (SwitchCase) stmt;
if (aCase.isDefault()) {
if (defaultTarget !=null)
throw new MethodCFGBuilderException("Two defaults??");
defaultTarget = caseVertice;
} else
cases.add(Pair.create(caseVertice, aCase.getExpression()));
if (lastBlock !=null)
acceptChildren(lastBlock, new ProcessingContext(previousVertice, caseVertice, context.returnDetails), true, switchBlock);
lastBlock = new LinkedList<Statement>();
previousVertice = caseVertice;
} else {
lastBlock.add(stmt);
}
}
if (lastBlock !=null)
acceptChildren(lastBlock, context.setPrevious(previousVertice), true, switchBlock);
if (bc != null && breakContextStack.pop() != bc)
throw new MethodCFGBuilderException("Different loop context!!");
//finally we create the case edge
if (defaultTarget == null) defaultTarget = context.nextVertice;
graph.createSwitchEdge(context.previousVertice, defaultTarget, node.getExpression(), cases);
if (switchBlock != blockStack.pop())
throw new MethodCFGBuilderException("Different block context!!");
return false;
}
@Override
public boolean visit(AssertStatement node) {
graph.createEmptyEdge(context.previousVertice, context.nextVertice);
return false; //NOTE: we assume assertions are not executed!!
}
@Override
public boolean visit(SynchronizedStatement node) {
ProcessingContext context = this.context;
context.previousVertice.addDescription("Synchronized expression");
CFGVertice blockStart = graph.addNewVertice(node, "Synchronized block");
graph.createExpressionStatementEdge(context.previousVertice, blockStart, node.getExpression());
acceptChild(node.getBody(), context.setPrevious(blockStart));
return false;
}
@Override
public boolean visit(TypeDeclarationStatement node) {
graph.createEmptyEdge(context.previousVertice, context.nextVertice);
return false;
}
@Override
public boolean visit(ThrowStatement node) {
//FIXME: we do not handle exceptions at all
//current implementation looses the path -does not add connection (previous -> next)
//since we handle exceptions in finallyOrCatch edges
return false;
}
@Override
public boolean visit(EnhancedForStatement node) {
ProcessingContext context = this.context;
int idx = forCounter++;
context.previousVertice.addDescription("BeforeEnhancedFor_"+idx);
BlockContext currentBlock = blockStack.peek();
//initializer expression
CFGVertice loopVertice = graph.addNewVertice(node, "LoopEnhancedFor_"+idx);
graph.createExpressionStatementEdge(context.previousVertice, loopVertice, node.getExpression());
CFGVertice loopVariable = graph.addNewVertice(node, "EnhancedForVariable_"+idx);
//dummy conditions
graph.addLoopInvariangMapping(node, loopVertice);
graph.createConditionalEdge(loopVertice, loopVariable, context.nextVertice, null); // no need to remove
BlockContext forBlockContext = pushNewBlockContext(node);
forBlockContext.add(node.getParameter().getName());
CFGVertice removeInitializers = graph.addNewVertice(node, "Remove initializers");
graph.createEmptyEdge(removeInitializers, loopVertice, forBlockContext.variables);
//dummy conditions
CFGVertice beforeLoopBody = graph.addNewVertice(node, "EnhancedForBody_"+idx);
graph.createVariableDeclarationEdge(loopVariable, beforeLoopBody, node.getParameter().getName(), null, false);
ContinueContext loopContext = new ContinueContext(removeInitializers, node, forBlockContext);
loops.push(loopContext);
BreakContext bc = null;
if (getParentLabel(node) == null) {
bc = new BreakContext(null, context.nextVertice, currentBlock);
breakContextStack.push(bc);
}
acceptChild(node.getBody(), new ProcessingContext(beforeLoopBody, removeInitializers, context.returnDetails));
if (loops.pop() != loopContext)
throw new MethodCFGBuilderException("Different loop context!!");
if (bc != null && breakContextStack.pop() != bc)
throw new MethodCFGBuilderException("Different break context!!!");
if (forBlockContext != blockStack.pop())
throw new MethodCFGBuilderException("Different block context!!!");
return false;
}
/**
* If method doesn't have body (abstract?) null is returned
*
* @param method
* @return
*/
public static MethodControlFlowGraph buildCFG(InterestingCodeFragment cf) {
//It may be an abstract method or interface method??
if (cf.getBody() == null)
return null;
MethodCFGBuilder builder = new MethodCFGBuilder(cf);
cf.getBody().accept(builder);
return builder.graph;
}
}