/*******************************************************************************
* Copyright (c) 2009, 2010 Alena Laskavaia
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Alena Laskavaia - initial API and implementation
*******************************************************************************/
package org.eclipse.cdt.codan.core.cxx.internal.model.cfg;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.Iterator;
import org.eclipse.cdt.codan.core.model.cfg.IBasicBlock;
import org.eclipse.cdt.codan.core.model.cfg.IBranchNode;
import org.eclipse.cdt.codan.core.model.cfg.ICfgData;
import org.eclipse.cdt.codan.core.model.cfg.IConnectorNode;
import org.eclipse.cdt.codan.core.model.cfg.IDecisionNode;
import org.eclipse.cdt.codan.core.model.cfg.IExitNode;
import org.eclipse.cdt.codan.core.model.cfg.IJumpNode;
import org.eclipse.cdt.codan.core.model.cfg.IPlainNode;
import org.eclipse.cdt.codan.core.model.cfg.ISingleOutgoing;
import org.eclipse.cdt.codan.core.model.cfg.IStartNode;
import org.eclipse.cdt.codan.internal.core.cfg.AbstractBasicBlock;
import org.eclipse.cdt.codan.internal.core.cfg.DecisionNode;
import org.eclipse.cdt.codan.internal.core.cfg.JumpNode;
import org.eclipse.cdt.core.dom.ast.IASTBreakStatement;
import org.eclipse.cdt.core.dom.ast.IASTCaseStatement;
import org.eclipse.cdt.core.dom.ast.IASTCompoundStatement;
import org.eclipse.cdt.core.dom.ast.IASTContinueStatement;
import org.eclipse.cdt.core.dom.ast.IASTDeclarationStatement;
import org.eclipse.cdt.core.dom.ast.IASTDefaultStatement;
import org.eclipse.cdt.core.dom.ast.IASTDoStatement;
import org.eclipse.cdt.core.dom.ast.IASTExpression;
import org.eclipse.cdt.core.dom.ast.IASTExpressionStatement;
import org.eclipse.cdt.core.dom.ast.IASTForStatement;
import org.eclipse.cdt.core.dom.ast.IASTFunctionCallExpression;
import org.eclipse.cdt.core.dom.ast.IASTFunctionDefinition;
import org.eclipse.cdt.core.dom.ast.IASTGotoStatement;
import org.eclipse.cdt.core.dom.ast.IASTIfStatement;
import org.eclipse.cdt.core.dom.ast.IASTLabelStatement;
import org.eclipse.cdt.core.dom.ast.IASTNode;
import org.eclipse.cdt.core.dom.ast.IASTNullStatement;
import org.eclipse.cdt.core.dom.ast.IASTProblemStatement;
import org.eclipse.cdt.core.dom.ast.IASTReturnStatement;
import org.eclipse.cdt.core.dom.ast.IASTStatement;
import org.eclipse.cdt.core.dom.ast.IASTSwitchStatement;
import org.eclipse.cdt.core.dom.ast.IASTUnaryExpression;
import org.eclipse.cdt.core.dom.ast.IASTWhileStatement;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTCatchHandler;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTTryBlockStatement;
/**
* This class creates C control flow graph
*/
public class ControlFlowGraphBuilder {
CxxStartNode start;
Collection<IExitNode> exits;
Collection<IBasicBlock> dead;
CxxExitNode returnExit;
CxxNodeFactory factory = new CxxNodeFactory();
IConnectorNode outerBreak;
IConnectorNode outerContinue;
HashMap<String, IBasicBlock> labels = new HashMap<String, IBasicBlock>(0);
/**
* @param def
* @return
*/
public CxxControlFlowGraph build(IASTFunctionDefinition def) {
IASTStatement body = def.getBody();
start = new CxxStartNode();
exits = new ArrayList<IExitNode>();
dead = new ArrayList<IBasicBlock>();
IBasicBlock last = createSubGraph(start, body);
if (!(last instanceof IExitNode) && !deadConnector(last)) {
returnExit = factory.createExitNode(null);
returnExit.setStartNode(start);
addOutgoing(last, returnExit);
exits.add(returnExit);
if (dead.size() > 0) {
for (Iterator<IBasicBlock> iterator = dead.iterator(); iterator.hasNext();) {
IBasicBlock ds = iterator.next();
IBasicBlock dl = findLast(ds);
if (dl != null && dl.getOutgoingSize() == 0 && dl != returnExit) {
((AbstractBasicBlock) dl).addOutgoing(returnExit);
}
}
}
}
CxxControlFlowGraph graph = new CxxControlFlowGraph(start, exits);
graph.setUnconnectedNodes(dead);
return graph;
}
/**
* @param last
* @return
*/
private boolean deadConnector(IBasicBlock conn) {
if (conn instanceof IJumpNode || conn instanceof IConnectorNode) {
if (conn.getIncomingSize() == 0) {
return true;
}
if (conn instanceof IJumpNode) {
IJumpNode jm = (IJumpNode) conn;
if (jm.isBackwardArc())
return false;
}
IBasicBlock[] conns = conn.getIncomingNodes();
for (int i = 0; i < conns.length; i++) {
IBasicBlock bb = conns[i];
if (!deadConnector(bb))
return false;
}
return true;
}
return false;
}
public IBasicBlock findLast(IBasicBlock node) {
if (node instanceof IJumpNode)
return null;
if (node.getOutgoingSize() == 0)
return node;
if (node instanceof ISingleOutgoing) {
return findLast(((ISingleOutgoing) node).getOutgoing());
} else if (node instanceof IDecisionNode) {
return findLast(((IDecisionNode) node).getMergeNode().getOutgoing());
}
return node;
}
/**
* @param start2
* @param body
*/
private IBasicBlock createSubGraph(IBasicBlock prev, IASTNode body) {
if (body instanceof IASTCompoundStatement) {
IASTCompoundStatement comp = (IASTCompoundStatement) body;
IASTNode[] children = comp.getChildren();
for (int i = 0; i < children.length; i++) {
IASTNode node = children[i];
IBasicBlock last = createSubGraph(prev, node);
prev = last;
}
} else if (body instanceof IASTExpressionStatement || body instanceof IASTDeclarationStatement || body instanceof IASTNullStatement) {
if (isThrowStatement(body) || isExitStatement(body)) {
CxxExitNode node = createExitNode(prev, body);
return node;
}
CxxPlainNode node = factory.createPlainNode(body);
addOutgoing(prev, node);
return node;
} else if (body instanceof IASTIfStatement) {
return createIf(prev, (IASTIfStatement) body);
} else if (body instanceof IASTWhileStatement) {
return createWhile(prev, (IASTWhileStatement) body);
} else if (body instanceof IASTForStatement) {
return createFor(prev, (IASTForStatement) body);
} else if (body instanceof IASTDoStatement) {
return createDoWhile(prev, (IASTDoStatement) body);
} else if (body instanceof IASTReturnStatement) {
CxxExitNode node = createExitNode(prev, body);
return node;
} else if (body instanceof IASTBreakStatement) {
if (outerBreak != null)
return addJump(prev, outerBreak);
return prev;
} else if (body instanceof IASTContinueStatement) {
if (outerContinue != null)
return addJump(prev, outerContinue);
return prev;
} else if (body instanceof IASTSwitchStatement) {
return createSwitch(prev, (IASTSwitchStatement) body);
} else if (body instanceof IASTLabelStatement) {
IASTLabelStatement ast = (IASTLabelStatement) body;
String labelName = ast.getName().toString();
IBranchNode labNode = (IBranchNode) labels.get(labelName);
IConnectorNode conn;
if (labNode != null) {
conn = (IConnectorNode) labNode.getOutgoing();
addOutgoing(prev, labNode);
} else {
// labeled statement contains of connector for jumps, branch for
// label
// and nested statement
conn = createLabelNodes(prev, labelName);
}
return createSubGraph(conn, ast.getNestedStatement());
} else if (body instanceof IASTGotoStatement) {
IASTGotoStatement ast = (IASTGotoStatement) body;
String labelName = ast.getName().toString();
IConnectorNode conn;
IBranchNode labNode = (IBranchNode) labels.get(labelName);
if (labNode != null) {
conn = (IConnectorNode) labNode.getOutgoing();
} else {
conn = createLabelNodes(null, labelName);
}
IJumpNode gotoNode = factory.createJumpNode();
((JumpNode) gotoNode).setJump(conn, labNode != null);
addOutgoing(prev, gotoNode);
return gotoNode;
} else if (body instanceof IASTProblemStatement) {
// System.err.println("problem");
CxxPlainNode node = factory.createPlainNode(body);
addOutgoing(prev, node);
return node;
} else if (body == null) {
// skip - sometimes body is empty such as no else
} else if (body instanceof ICPPASTTryBlockStatement) {
return createTry(prev, (ICPPASTTryBlockStatement) body);
} else {
System.err.println("unknown statement for cfg: " + body); //$NON-NLS-1$
}
return prev;
}
/**
* @param prev
* @param body
* @return
*/
private IBasicBlock createTry(IBasicBlock prev, ICPPASTTryBlockStatement body) {
DecisionNode ifNode = factory.createDecisionNode(body);
addOutgoing(prev, ifNode);
IConnectorNode mergeNode = factory.createConnectorNode();
ifNode.setMergeNode(mergeNode);
IBranchNode thenNode = factory.createBranchNode(IBranchNode.THEN);
addOutgoing(ifNode, thenNode);
IBasicBlock then = createSubGraph(thenNode, body.getTryBody());
addJump(then, mergeNode);
ICPPASTCatchHandler[] catchHandlers = body.getCatchHandlers();
for (int i = 0; i < catchHandlers.length; i++) {
ICPPASTCatchHandler handler = catchHandlers[i];
IBranchNode handlerNode = factory.createBranchNode(handler.getDeclaration());
addOutgoing(ifNode, handlerNode);
IBasicBlock els = createSubGraph(handlerNode, handler.getCatchBody());
addJump(els, mergeNode);
}
return mergeNode;
}
/**
* @param body
* @return
*/
private boolean isThrowStatement(IASTNode body) {
if (!(body instanceof IASTExpressionStatement))
return false;
IASTExpression expression = ((IASTExpressionStatement) body).getExpression();
if (!(expression instanceof IASTUnaryExpression))
return false;
return ((IASTUnaryExpression) expression).getOperator() == IASTUnaryExpression.op_throw;
}
private boolean isExitStatement(IASTNode body) {
if (!(body instanceof IASTExpressionStatement))
return false;
IASTExpression expression = ((IASTExpressionStatement) body).getExpression();
if (!(expression instanceof IASTFunctionCallExpression))
return false;
IASTExpression functionNameExpression = ((IASTFunctionCallExpression) expression).getFunctionNameExpression();
return functionNameExpression.getRawSignature().equals("exit"); //$NON-NLS-1$
}
/**
* @param prev
* @param body
* @return
*/
protected CxxExitNode createExitNode(IBasicBlock prev, IASTNode body) {
CxxExitNode node = factory.createExitNode(body);
node.setStartNode(start);
addOutgoing(prev, node);
exits.add(node);
return node;
}
/**
* @param prev
* @param labelName
* @return
*/
protected IConnectorNode createLabelNodes(IBasicBlock prev, String labelName) {
IBranchNode branch = factory.createBranchNode(labelName);
if (prev != null)
addOutgoing(prev, branch);
labels.put(labelName, branch);
IConnectorNode conn = factory.createConnectorNode();
addOutgoing(branch, conn);
return conn;
}
/**
* @param prev
* @param body
* @return
*/
protected IBasicBlock createIf(IBasicBlock prev, IASTIfStatement body) {
DecisionNode ifNode = factory.createDecisionNode(body.getConditionExpression());
addOutgoing(prev, ifNode);
IConnectorNode mergeNode = factory.createConnectorNode();
ifNode.setMergeNode(mergeNode);
IBranchNode thenNode = factory.createBranchNode(IBranchNode.THEN);
addOutgoing(ifNode, thenNode);
IBasicBlock then = createSubGraph(thenNode, body.getThenClause());
addJump(then, mergeNode);
IBranchNode elseNode = factory.createBranchNode(IBranchNode.ELSE);
addOutgoing(ifNode, elseNode);
IBasicBlock els = createSubGraph(elseNode, body.getElseClause());
addJump(els, mergeNode);
return mergeNode;
}
/**
* @param prev
* @param body
* @return
*/
private IBasicBlock createSwitch(IBasicBlock prev, IASTSwitchStatement body) {
DecisionNode node = factory.createDecisionNode(body.getControllerExpression());
addOutgoing(prev, node);
IConnectorNode conn = factory.createConnectorNode();
node.setMergeNode(conn);
createSwitchBody(node, conn, body.getBody());
return conn;
}
/**
* @param switchNode
* @param mergeNode
* @param def
* @param body
*/
private void createSwitchBody(DecisionNode switchNode, IConnectorNode mergeNode, IASTStatement body) {
if (!(body instanceof IASTCompoundStatement))
return; // bad
IASTCompoundStatement comp = (IASTCompoundStatement) body;
IASTNode[] children = comp.getChildren();
IBasicBlock prev = switchNode;
for (int i = 0; i < children.length; i++) {
IASTNode elem = children[i];
if (elem instanceof IASTCaseStatement || elem instanceof IASTDefaultStatement) {
IBranchNode lbl = null;
if (elem instanceof IASTCaseStatement) {
IASTCaseStatement caseSt = (IASTCaseStatement) elem;
lbl = factory.createBranchNode(caseSt);
} else if (elem instanceof IASTDefaultStatement) {
lbl = factory.createBranchNode(IBranchNode.DEFAULT);
}
if (!(prev instanceof IExitNode) && prev != switchNode) {
IConnectorNode here = factory.createConnectorNode();
addJump(prev, here);
addOutgoing(lbl, here);
prev = here;
} else {
prev = lbl;
}
addOutgoing(switchNode, lbl);
continue;
}
if (elem instanceof IASTBreakStatement) {
prev = addJump(prev, mergeNode);
continue;
}
IBasicBlock last = createSubGraph(prev, elem);
prev = last;
}
addJump(prev, mergeNode);
}
/**
* @param prev
* @param forNode
* @return
*/
private IBasicBlock createFor(IBasicBlock prev, IASTForStatement forNode) {
// add initializer
IPlainNode init = factory.createPlainNode(forNode.getInitializerStatement());
addOutgoing(prev, init);
prev = init;
// add continue connector
IConnectorNode beforeCheck = factory.createConnectorNode();
addOutgoing(prev, beforeCheck);
// decision node
CxxDecisionNode decision = factory.createDecisionNode(forNode.getConditionExpression());
addOutgoing(beforeCheck, decision);
// add break connector
IConnectorNode nBreak = factory.createConnectorNode();
decision.setMergeNode(nBreak);
// create body and jump to continue node
IBranchNode loopStart = factory.createBranchNode(IBranchNode.THEN);
addOutgoing(decision, loopStart);
// set break/continue
IConnectorNode nContinue = factory.createConnectorNode();
IConnectorNode savedContinue = outerContinue;
IConnectorNode savedBreak = outerBreak;
outerContinue = nContinue;
outerBreak = nBreak;
IBasicBlock endBody = createSubGraph(loopStart, forNode.getBody());
outerContinue = savedContinue;
outerBreak = savedBreak;
// inc
IPlainNode inc = factory.createPlainNode(forNode.getIterationExpression());
addOutgoing(endBody, nContinue);
addOutgoing(nContinue, inc);
// connect with backward link
addJump(inc, beforeCheck, true);
// add "else" branch
IBranchNode loopEnd = factory.createBranchNode(IBranchNode.ELSE);
addOutgoing(decision, loopEnd);
addJump(loopEnd, nBreak);
return nBreak;
}
/**
* @param prev
* @param body
* @return
*/
protected IBasicBlock createWhile(IBasicBlock prev, IASTWhileStatement body) {
// add continue connector
IConnectorNode nContinue = factory.createConnectorNode();
addOutgoing(prev, nContinue);
// decision node
CxxDecisionNode decision = factory.createDecisionNode(body.getCondition());
addOutgoing(nContinue, decision);
// add break connector
IConnectorNode nBreak = factory.createConnectorNode();
decision.setMergeNode(nBreak);
// create body and jump to continue node
IBranchNode loopStart = factory.createBranchNode(IBranchNode.THEN);
addOutgoing(decision, loopStart);
// set break/continue
IConnectorNode savedContinue = outerContinue;
IConnectorNode savedBreak = outerBreak;
outerContinue = nContinue;
outerBreak = nBreak;
IBasicBlock endBody = createSubGraph(loopStart, body.getBody());
// restore
outerContinue = savedContinue;
outerBreak = savedBreak;
// backward jump
addJump(endBody, nContinue, true);
// connect with else branch
IBranchNode loopEnd = factory.createBranchNode(IBranchNode.ELSE);
addOutgoing(decision, loopEnd);
addJump(loopEnd, nBreak);
return nBreak;
}
protected IBasicBlock createDoWhile(IBasicBlock prev, IASTDoStatement body) {
// create body and jump to continue node
IConnectorNode loopStart = factory.createConnectorNode();
addOutgoing(prev, loopStart);
// continue/break
IConnectorNode nContinue = factory.createConnectorNode();
IConnectorNode nBreak = factory.createConnectorNode();
IConnectorNode savedContinue = outerContinue;
IConnectorNode savedBreak = outerBreak;
outerContinue = nContinue;
outerBreak = nBreak;
IBasicBlock endBody = createSubGraph(loopStart, body.getBody());
// restore
outerContinue = savedContinue;
outerBreak = savedBreak;
// add continue connector
addOutgoing(endBody, nContinue);
// decision node
CxxDecisionNode decision = factory.createDecisionNode(body.getCondition());
addOutgoing(nContinue, decision);
// then branch
IBranchNode thenNode = factory.createBranchNode(IBranchNode.THEN);
addOutgoing(decision, thenNode);
IJumpNode jumpToStart = factory.createJumpNode();
addOutgoing(thenNode, jumpToStart);
((JumpNode) jumpToStart).setBackward(true);
// connect with backward link
addOutgoing(jumpToStart, loopStart);
// connect with else branch
IBranchNode loopEnd = factory.createBranchNode(IBranchNode.ELSE);
addOutgoing(decision, loopEnd);
// add break connector
decision.setMergeNode(nBreak);
addJump(loopEnd, nBreak);
return nBreak;
}
private IJumpNode addJump(IBasicBlock prev, IConnectorNode conn) {
return addJump(prev, conn, false);
}
private IJumpNode addJump(IBasicBlock prev, IConnectorNode conn, boolean backward) {
if (prev instanceof IJumpNode)
return (IJumpNode) prev;
if (prev instanceof IExitNode)
return null;
IJumpNode jump = factory.createJumpNode();
addOutgoing(prev, jump);
addOutgoing(jump, conn);
((JumpNode) jump).setBackward(backward);
return jump;
}
/**
* @param prev
* @param node
*/
private void addOutgoing(IBasicBlock prev, IBasicBlock node) {
if (prev instanceof IExitNode || prev == null) {
dead.add(node);
return;
} else if (prev instanceof ICfgData) {
if (prev instanceof IDecisionNode && !(node instanceof IBranchNode)) {
dead.add(node);
return;
}
((AbstractBasicBlock) prev).addOutgoing(node);
}
if (!(node instanceof IStartNode))
((AbstractBasicBlock) node).addIncoming(prev);
}
}