/*******************************************************************************
* 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.cfg;
import java.util.Collection;
import java.util.Iterator;
import org.eclipse.cdt.codan.core.cxx.internal.model.cfg.ControlFlowGraphBuilder;
import org.eclipse.cdt.codan.core.model.IChecker;
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.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.core.test.CodanFastCxxAstTestCase;
import org.eclipse.cdt.codan.internal.core.cfg.AbstractBasicBlock;
import org.eclipse.cdt.codan.internal.core.cfg.ControlFlowGraph;
import org.eclipse.cdt.core.dom.ast.IASTDeclaration;
import org.eclipse.cdt.core.dom.ast.IASTFunctionDefinition;
import org.eclipse.cdt.core.dom.ast.IASTTranslationUnit;
import org.eclipse.cdt.core.dom.ast.c.CASTVisitor;
import org.eclipse.cdt.core.parser.ParserLanguage;
/**
* TODO: add description
*/
public class ControlFlowGraphTest extends CodanFastCxxAstTestCase {
ControlFlowGraph graph;
/**
* @param ast
*/
private void processAst(IASTTranslationUnit ast) {
CASTVisitor visitor = new CASTVisitor() {
{
shouldVisitDeclarations = true;
}
@Override
public int visit(IASTDeclaration decl) {
if (decl instanceof IASTFunctionDefinition) {
graph = new ControlFlowGraphBuilder().build((IASTFunctionDefinition) decl);
return PROCESS_ABORT;
}
return PROCESS_CONTINUE;
}
};
ast.accept(visitor);
}
private void checkCfg() {
checkCfg(true);
}
/**
*
*/
private void checkCfg(boolean decision) {
assertNotNull(graph);
assertNotNull(graph.getStartNode());
Collection<IBasicBlock> nodes = graph.getNodes();
for (Iterator<IBasicBlock> iterator = nodes.iterator(); iterator.hasNext();) {
IBasicBlock node = iterator.next();
checkNode(node, decision);
}
}
/**
* @param node
*/
private void checkNode(IBasicBlock node, boolean decision) {
IBasicBlock[] incomingNodes = node.getIncomingNodes();
nodes: for (int i = 0; i < incomingNodes.length; i++) {
IBasicBlock b = incomingNodes[i];
if (b == null) {
// check if dead node
Iterator<IBasicBlock> iterator = graph.getUnconnectedNodeIterator();
boolean dead = false;
for (; iterator.hasNext();) {
IBasicBlock d = iterator.next();
if (node == d) {
dead = true;
break;
}
}
if (!dead)
fail("Block " + node + " prev is null");
} else if (!contains(node, b.getOutgoingNodes()))
fail("Block " + node + " inconsitent prev/next " + b);
}
IBasicBlock[] outgoingNodes = node.getOutgoingNodes();
for (int i = 0; i < outgoingNodes.length; i++) {
IBasicBlock b = outgoingNodes[i];
if (b == null)
fail("Block " + node + " next is null");
if (!contains(node, b.getIncomingNodes()))
fail("Block " + node + " inconsitent next/prev " + b);
}
if (node instanceof IDecisionNode && decision) {
assertTrue("decision node outgoing size " + node.getOutgoingSize(), node.getOutgoingSize() > 1);
assertNotNull(((IDecisionNode) node).getMergeNode());
}
}
/**
* @param node
* @param outgoingIterator
* @return
*/
private boolean contains(IBasicBlock node, IBasicBlock[] blocks) {
for (int i = 0; i < blocks.length; i++) {
IBasicBlock b = blocks[i];
if (b.equals(node))
return true;
}
return false;
}
protected void buildAndCheck(String code) {
buildAndCheck(code, false);
}
protected void buildAndCheck_cpp(String code) {
buildAndCheck(code, true);
}
/**
* @param file
*/
protected void buildAndCheck(String code, boolean cpp) {
buildCfg(code, cpp);
checkCfg();
}
/**
* @param code
* @param cpp
*/
private void buildCfg(String code, boolean cpp) {
parse(code, cpp ? ParserLanguage.CPP : ParserLanguage.C, true);
processAst(tu);
}
/**
* @param des
* @return
*/
private String data(IBasicBlock des) {
return ((AbstractBasicBlock) des).toStringData();
}
/**
* Return first node after the branch
*
* @param des
* @return
*/
private IBasicBlock branchEnd(IDecisionNode des, String label) {
IBasicBlock[] outgoingNodes = des.getOutgoingNodes();
for (int i = 0; i < outgoingNodes.length; i++) {
IBasicBlock iBasicBlock = outgoingNodes[i];
IBranchNode bn = (IBranchNode) iBasicBlock;
if (label.equals(bn.getLabel()))
return bn.getOutgoing();
}
return null;
}
/**
* Return node where control jumps, following the chain until jump is hit
*
* @param a
* @return
*/
private IBasicBlock jumpEnd(IBasicBlock a) {
if (a instanceof IJumpNode)
return ((IJumpNode) a).getOutgoing();
if (a instanceof ISingleOutgoing)
return jumpEnd(((ISingleOutgoing) a).getOutgoing());
return null;
}
@Override
public boolean isCpp() {
return true;
}
// main() {
// int a;
// a=1;
// }
public void test_basic() {
buildAndCheck(getAboveComment());
}
// main() {
// int a=10;
// while (a--) {
// a=a-2;
// }
// }
public void test_while() {
buildAndCheck(getAboveComment());
IStartNode startNode = graph.getStartNode();
IPlainNode decl = (IPlainNode) startNode.getOutgoing();
IConnectorNode conn = (IConnectorNode) decl.getOutgoing();
IDecisionNode des = (IDecisionNode) conn.getOutgoing();
assertEquals("a--", data(des));
IPlainNode bThen = (IPlainNode) branchEnd(des, IBranchNode.THEN);
assertEquals("a=a-2;", data(bThen));
IBasicBlock bElse = branchEnd(des, IBranchNode.ELSE);
IBasicBlock m2 = jumpEnd(bThen);
IBasicBlock m1 = jumpEnd(bElse);
assertSame(conn, m2);
IExitNode ret = (IExitNode) ((IConnectorNode) m1).getOutgoing();
}
// main() {
// int a=10;
// if (a--) {
// a=a-2;
// }
// }
public void test_if() {
buildAndCheck(getAboveComment());
IStartNode startNode = graph.getStartNode();
IPlainNode decl = (IPlainNode) startNode.getOutgoing();
IDecisionNode des = (IDecisionNode) decl.getOutgoing();
assertEquals("a--", data(des));
IPlainNode bThen = (IPlainNode) branchEnd(des, IBranchNode.THEN);
assertEquals("a=a-2;", data(bThen));
IBasicBlock bElse = branchEnd(des, IBranchNode.ELSE);
IBasicBlock m2 = jumpEnd(bThen);
IBasicBlock m1 = jumpEnd(bElse);
assertSame(m1, m2);
}
// main() {
// int a=10;
// if (a--) {
// return;
// }
// }
public void test_if_ret() {
buildAndCheck(getAboveComment());
IStartNode startNode = graph.getStartNode();
IPlainNode decl = (IPlainNode) startNode.getOutgoing();
IDecisionNode des = (IDecisionNode) decl.getOutgoing();
assertEquals("a--", data(des));
IExitNode bThen = (IExitNode) branchEnd(des, IBranchNode.THEN);
IBasicBlock bElse = branchEnd(des, IBranchNode.ELSE);
IBasicBlock m1 = jumpEnd(bElse);
}
// main() {
// return;
// a++;
// }
public void test_dead() {
buildCfg(getAboveComment(), false);
IStartNode startNode = graph.getStartNode();
IExitNode ret = (IExitNode) startNode.getOutgoing();
assertEquals(1, graph.getUnconnectedNodeSize());
}
// main() {
// int a=10;
// if (a--) {
// return;
// a++;
// }
// }
public void test_if_dead() {
buildCfg(getAboveComment(), false);
IStartNode startNode = graph.getStartNode();
IPlainNode decl = (IPlainNode) startNode.getOutgoing();
IDecisionNode des = (IDecisionNode) decl.getOutgoing();
assertEquals("a--", data(des));
IExitNode bThen = (IExitNode) branchEnd(des, IBranchNode.THEN);
IBasicBlock bElse = branchEnd(des, IBranchNode.ELSE);
IBasicBlock m1 = jumpEnd(bElse);
assertEquals(1, graph.getUnconnectedNodeSize());
}
// foo() {
// int a=10, x=5;
// if (a--) {
// if (x<0)
// a++;
// }
// }
public void test_ifif() {
buildAndCheck(getAboveComment());
IStartNode startNode = graph.getStartNode();
IPlainNode decl = (IPlainNode) startNode.getOutgoing();
IDecisionNode des = (IDecisionNode) decl.getOutgoing();
assertEquals("a--", data(des));
IDecisionNode bThen = (IDecisionNode) branchEnd(des, IBranchNode.THEN);
assertEquals("x<0", data(bThen));
IBasicBlock bElse = branchEnd(des, IBranchNode.ELSE);
IBasicBlock m2 = jumpEnd(branchEnd(bThen, IBranchNode.THEN));
IBasicBlock m1 = jumpEnd(bElse);
IBasicBlock m3 = jumpEnd(m2);
assertSame(m1, m3);
}
// int foo() {
// throw 5;
// }
public void test_throw() {
buildAndCheck_cpp(getAboveComment());
IStartNode startNode = graph.getStartNode();
assertEquals(1, graph.getExitNodeSize());
Iterator<IExitNode> exitNodeIterator = graph.getExitNodeIterator();
IExitNode exit = exitNodeIterator.next();
assertEquals("throw 5;", data(exit));
}
// int foo() {
// exit(0);
// }
public void test_exit() {
buildAndCheck(getAboveComment());
IStartNode startNode = graph.getStartNode();
assertEquals(1, graph.getExitNodeSize());
Iterator<IExitNode> exitNodeIterator = graph.getExitNodeIterator();
IExitNode exit = exitNodeIterator.next();
assertEquals("exit(0);", data(exit));
}
/*
* (non-Javadoc)
*
* @see org.eclipse.cdt.codan.core.test.CodanFastCxxAstTestCase#getChecker()
*/
@Override
public IChecker getChecker() {
return null;
}
// int foo() {
// void * p;
// try {
// *p = 1;
// } catch (int e) {
// };
// }
public void test_try() {
buildAndCheck_cpp(getAboveComment());
IStartNode startNode = graph.getStartNode();
IPlainNode decl = (IPlainNode) startNode.getOutgoing();
IDecisionNode des = (IDecisionNode) decl.getOutgoing();
//assertEquals("", data(des));
IPlainNode bThen = (IPlainNode) branchEnd(des, IBranchNode.THEN);
assertEquals("*p = 1;", data(bThen));
IBasicBlock bElse = null;
IBasicBlock[] outgoingNodes = des.getOutgoingNodes();
for (int i = 1; i < outgoingNodes.length; i++) {
IBasicBlock iBasicBlock = outgoingNodes[i];
IBranchNode bn = (IBranchNode) iBasicBlock;
bElse = bn;
}
IBasicBlock m2 = jumpEnd(bThen);
IBasicBlock m1 = jumpEnd(bElse);
assertSame(m1, m2);
}
// foo() {
// switch (0) {
// case 1: ;
// }
// }
public void test_switch1() {
buildCfg(getAboveComment(), false);
checkCfg(false);
}
// foo() {
// switch (0) {
// case 1: break;
// }
// }
public void test_switchbreak() {
buildCfg(getAboveComment(), false);
checkCfg(false);
}
// foo() {
// switch (0) {
// a++;
// }
// }
public void test_switchdead() {
buildCfg(getAboveComment(), false);
checkCfg(false);
IStartNode startNode = graph.getStartNode();
assertEquals(1, graph.getUnconnectedNodeSize());
}
}