/******************************************************************************* * Copyright (c) 2009, 2015 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.internal.core.cfg; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.Iterator; import java.util.LinkedHashSet; import java.util.List; 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.IControlFlowGraph; 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.ISingleOutgoing; import org.eclipse.cdt.codan.core.model.cfg.IStartNode; /** * Implementation of control flow graph */ public class ControlFlowGraph implements IControlFlowGraph { private List<IExitNode> exitNodes; private List<IBasicBlock> deadNodes = new ArrayList<IBasicBlock>(); private IStartNode start; public ControlFlowGraph(IStartNode start, Collection<IExitNode> exitNodes) { setExitNodes(exitNodes); this.start = start; } @Override public Iterator<IExitNode> getExitNodeIterator() { return exitNodes.iterator(); } @Override public int getExitNodeSize() { return exitNodes.size(); } public void setExitNodes(Collection<IExitNode> exitNodes) { if (this.exitNodes != null) throw new IllegalArgumentException("Cannot modify already exiting connector"); //$NON-NLS-1$ this.exitNodes = Collections.unmodifiableList(new ArrayList<IExitNode>(exitNodes)); } public void setUnconnectedNodes(Collection<IBasicBlock> nodes) { this.deadNodes = Collections.unmodifiableList(new ArrayList<IBasicBlock>(nodes)); } /* * (non-Javadoc) * * @seeorg.eclipse.cdt.codan.provisional.core.model.cfg.IControlFlowGraph# * getStartNode() */ @Override public IStartNode getStartNode() { return start; } void setStartNode(IStartNode start) { this.start = start; } public void print(IBasicBlock node) { System.out.println(node.getClass().getSimpleName() + ": " //$NON-NLS-1$ + ((AbstractBasicBlock) node).toStringData()); if (node instanceof IDecisionNode) { // todo IBasicBlock[] branches = ((IDecisionNode) node).getOutgoingNodes(); for (int i = 0; i < branches.length; i++) { IBasicBlock brNode = branches[i]; System.out.println("{"); //$NON-NLS-1$ print(brNode); System.out.println("}"); //$NON-NLS-1$ } print(((IDecisionNode) node).getMergeNode()); } else if (node instanceof ISingleOutgoing) { IBasicBlock next = ((ISingleOutgoing) node).getOutgoing(); if (!(next instanceof IConnectorNode && !(next instanceof IBranchNode))) print(next); } } /* * (non-Javadoc) * * @seeorg.eclipse.cdt.codan.provisional.core.model.cfg.IControlFlowGraph# * getUnconnectedNodeIterator() */ @Override public Iterator<IBasicBlock> getUnconnectedNodeIterator() { return deadNodes.iterator(); } /* * (non-Javadoc) * * @seeorg.eclipse.cdt.codan.provisional.core.model.cfg.IControlFlowGraph# * getUnconnectedNodeSize() */ @Override public int getUnconnectedNodeSize() { return deadNodes.size(); } /* * (non-Javadoc) * * @see org.eclipse.cdt.codan.core.model.cfg.IControlFlowGraph#getNodes () */ @Override public Collection<IBasicBlock> getNodes() { Collection<IBasicBlock> result = new LinkedHashSet<IBasicBlock>(); getNodes(getStartNode(), result); getDeadNodes(result); return result; } private void getNodes(IBasicBlock start, Collection<IBasicBlock> result) { if (start == null) return; // huh if (result.contains(start)) return; result.add(start); for (IBasicBlock bb : start.getOutgoingNodes()) { getNodes(bb, result); } if (start instanceof IConnectorNode) { // special case where connect can have some incoming branch nodes not in the graph for (IBasicBlock bb : start.getIncomingNodes()) { getNodes(bb, result); } } } public Collection<IBasicBlock> getDeadNodes() { Collection<IBasicBlock> result = new LinkedHashSet<IBasicBlock>(); getDeadNodes(result); return result; } private void getDeadNodes(Collection<IBasicBlock> result) { Collection<IBasicBlock> liveNodes = new LinkedHashSet<IBasicBlock>(); getNodes(getStartNode(), liveNodes); for (Iterator<IBasicBlock> iterator = deadNodes.iterator(); iterator.hasNext();) { IBasicBlock d = iterator.next(); getDeadNodes(d, result, liveNodes); } } public void getDeadNodes(IBasicBlock start, Collection<IBasicBlock> result, Collection<IBasicBlock> liveNodes) { if (start == null) return; // huh if (result.contains(start)) return; if (liveNodes.contains(start)) return; // a live node is by definition not dead result.add(start); for (IBasicBlock bb : start.getOutgoingNodes()) { getDeadNodes(bb, result, liveNodes); } if (start instanceof IConnectorNode) { // Sometimes, a dead connector node can have incoming nodes that are not otherwise reachable // from unconnected nodes (this happens for a branch node for a dead label). for (IBasicBlock bb : start.getIncomingNodes()) { getDeadNodes(bb, result, liveNodes); } } } }