/*
* Bytecode Analysis Framework
* Copyright (C) 2003-2008 University of Maryland
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
package edu.umd.cs.findbugs.ba;
import java.util.BitSet;
import java.util.Collection;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.NoSuchElementException;
import java.util.TreeSet;
import org.apache.bcel.generic.ATHROW;
import org.apache.bcel.generic.InstructionHandle;
import org.apache.bcel.generic.MethodGen;
import edu.umd.cs.findbugs.ba.Edge.Type;
import edu.umd.cs.findbugs.graph.AbstractGraph;
import edu.umd.cs.findbugs.util.NullIterator;
/**
* Simple control flow graph abstraction for BCEL.
*
* @see BasicBlock
* @see Edge
*/
public class CFG extends AbstractGraph<Edge, BasicBlock> implements Debug {
/*
* ----------------------------------------------------------------------
* CFG flags
* ----------------------------------------------------------------------
*/
/** Flag set if infeasible exception edges have been pruned from the CFG. */
public static final int PRUNED_INFEASIBLE_EXCEPTIONS = 1;
/**
* Flag set if normal return edges from calls to methods which
* unconditionally throw an exception have been removed.
*/
public static final int PRUNED_UNCONDITIONAL_THROWERS = 2;
/**
* Flag set if CFG has been "refined"; i.e., to the extent possible, all
* infeasible edges have been removed.
*/
public static final int REFINED = 4;
/**
* Flag set if CFG edges corresponding to failed assertions have been
* removed.
*/
public static final int PRUNED_FAILED_ASSERTION_EDGES = 8;
/** Flag set if CFG is busy (in the process of being refined. */
public static final int BUSY = 16;
public static final int FOUND_INEXACT_UNCONDITIONAL_THROWERS = 32;
/*
* ----------------------------------------------------------------------
* Helper classes
* ----------------------------------------------------------------------
*/
/**
* An Iterator over the Locations in the CFG. Because of JSR subroutines,
* the same instruction may actually be part of multiple basic blocks (with
* different facts true in each, due to calling context). Locations specify
* both the instruction and the basic block.
*/
private class LocationIterator implements Iterator<Location> {
private Iterator<BasicBlock> blockIter;
private BasicBlock curBlock;
private Iterator<InstructionHandle> instructionIter;
private Location next;
private LocationIterator() {
this.blockIter = blockIterator();
findNext();
}
public boolean hasNext() {
findNext();
return next != null;
}
public Location next() {
findNext();
if (next == null)
throw new NoSuchElementException();
Location result = next;
next = null;
return result;
}
public void remove() {
throw new UnsupportedOperationException();
}
private void findNext() {
while (next == null) {
// Make sure we have an instruction iterator
if (instructionIter == null) {
if (!blockIter.hasNext())
return; // At end
curBlock = blockIter.next();
instructionIter = curBlock.instructionIterator();
}
if (instructionIter.hasNext())
next = new Location(instructionIter.next(), curBlock);
else
instructionIter = null; // Go to next block
}
}
}
/*
* ----------------------------------------------------------------------
* Fields
* ----------------------------------------------------------------------
*/
private BasicBlock entry, exit;
private int flags;
private String methodName; // for informational purposes
private MethodGen methodGen;
private List<Edge> removedEdgeList;
/*
* ----------------------------------------------------------------------
* Public methods
* ----------------------------------------------------------------------
*/
/**
* Constructor. Creates empty control flow graph (with just entry and exit
* nodes).
*/
public CFG() {
}
/**
* @param methodName
* The methodName to set.
*/
public void setMethodName(String methodName) {
this.methodName = methodName;
}
public void setMethodGen(MethodGen methodGen) {
this.methodGen = methodGen;
}
public MethodGen getMethodGen() {
return methodGen;
}
/**
* @return Returns the methodName.
*/
public String getMethodName() {
return methodName;
}
public String getMethodSig() {
return methodGen.getSignature();
}
public void setFlags(int flags) {
this.flags = flags;
}
public void setFlag(int flags) {
this.flags |= flags;
}
public void clearFlag(int flags) {
this.flags &= ~flags;
}
public int getFlags() {
return flags;
}
public boolean isFlagSet(int flag) {
return (flags & flag) != 0;
}
/**
* Get the entry node.
*/
public BasicBlock getEntry() {
if (entry == null) {
entry = allocate();
}
return entry;
}
/**
* Get the exit node.
*/
public BasicBlock getExit() {
if (exit == null) {
exit = allocate();
}
return exit;
}
/**
* Add a unique edge to the graph. There must be no other edge already in
* the CFG with the same source and destination blocks.
*
* @param source
* the source basic block
* @param dest
* the destination basic block
* @param type
* the type of edge; see constants in EdgeTypes interface
* @return the newly created Edge
* @throws IllegalStateException
* if there is already an edge in the CFG with the same source
* and destination block
*/
public Edge createEdge(BasicBlock source, BasicBlock dest, @Edge.Type int type) {
Edge edge = createEdge(source, dest);
edge.setType(type);
return edge;
}
/**
* Look up an Edge by its id.
*
* @param id
* the id of the edge to look up
* @return the Edge, or null if no matching Edge was found
*/
public Edge lookupEdgeById(int id) {
Iterator<Edge> i = edgeIterator();
while (i.hasNext()) {
Edge edge = i.next();
if (edge.getId() == id)
return edge;
}
return null;
}
/**
* Look up a BasicBlock by its unique label.
*
* @param blockLabel
* the label of a BasicBlock
* @return the BasicBlock with the given label, or null if there is no such
* BasicBlock
*/
public BasicBlock lookupBlockByLabel(int blockLabel) {
for (Iterator<BasicBlock> i = blockIterator(); i.hasNext();) {
BasicBlock basicBlock = i.next();
if (basicBlock.getLabel() == blockLabel) {
return basicBlock;
}
}
return null;
}
/**
* Get an Iterator over the nodes (BasicBlocks) of the control flow graph.
*/
public Iterator<BasicBlock> blockIterator() {
return vertexIterator();
}
/*
* * Get an Iteratable over the nodes (BasicBlocks) of the control flow
* graph.
*/
public Iterable<BasicBlock> blocks() {
return vertices();
}
/**
* Get an Iterator over the Locations in the control flow graph.
*/
public Iterator<Location> locationIterator() {
return new LocationIterator();
}
/**
* Get an Iterator over the Locations in the control flow graph.
*/
public Iterable<Location> locations() {
return new Iterable<Location>() {
public Iterator<Location> iterator() {
return locationIterator();
}
};
}
/**
* Returns a collection of locations, ordered according to the compareTo
* ordering over locations. If you want to list all the locations in a CFG
* for debugging purposes, this is a good order to do so in.
*
* @return collection of locations
*/
public Collection<Location> orderedLocations() {
TreeSet<Location> tree = new TreeSet<Location>();
for (Iterator<Location> locs = locationIterator(); locs.hasNext();) {
Location loc = locs.next();
tree.add(loc);
}
return tree;
}
/**
* Get Collection of basic blocks whose IDs are specified by given BitSet.
*
* @param labelSet
* BitSet of block labels
* @return a Collection containing the blocks whose IDs are given
*/
public Collection<BasicBlock> getBlocks(BitSet labelSet) {
LinkedList<BasicBlock> result = new LinkedList<BasicBlock>();
for (Iterator<BasicBlock> i = blockIterator(); i.hasNext();) {
BasicBlock block = i.next();
if (labelSet.get(block.getLabel()))
result.add(block);
}
return result;
}
/**
* Get a Collection of basic blocks which contain the bytecode instruction
* with given offset.
*
* @param offset
* the bytecode offset of an instruction
* @return Collection of BasicBlock objects which contain the instruction
* with that offset
*/
public Collection<BasicBlock> getBlocksContainingInstructionWithOffset(int offset) {
LinkedList<BasicBlock> result = new LinkedList<BasicBlock>();
for (Iterator<BasicBlock> i = blockIterator(); i.hasNext();) {
BasicBlock block = i.next();
if (block.containsInstructionWithOffset(offset))
result.add(block);
}
return result;
}
/**
* Get a Collection of Locations which specify the instruction at given
* bytecode offset.
*
* @param offset
* the bytecode offset
* @return all Locations referring to the instruction at that offset
*/
public Collection<Location> getLocationsContainingInstructionWithOffset(int offset) {
LinkedList<Location> result = new LinkedList<Location>();
for (Iterator<Location> i = locationIterator(); i.hasNext();) {
Location location = i.next();
if (location.getHandle().getPosition() == offset) {
result.add(location);
}
}
return result;
}
/**
* Get the first predecessor reachable from given edge type.
*
* @param target
* the target block
* @param edgeType
* the edge type leading from the predecessor
* @return the predecessor, or null if there is no incoming edge with the
* specified edge type
*/
public BasicBlock getPredecessorWithEdgeType(BasicBlock target, @Type int edgeType) {
Edge edge = getIncomingEdgeWithType(target, edgeType);
return edge != null ? edge.getSource() : null;
}
/**
* Get the first successor reachable from given edge type.
*
* @param source
* the source block
* @param edgeType
* the edge type leading to the successor
* @return the successor, or null if there is no outgoing edge with the
* specified edge type
*/
public BasicBlock getSuccessorWithEdgeType(BasicBlock source, @Type int edgeType) {
Edge edge = getOutgoingEdgeWithType(source, edgeType);
return edge != null ? edge.getTarget() : null;
}
/**
* Get the Location where exception(s) thrown on given exception edge are
* thrown.
*
* @param exceptionEdge
* the exception Edge
* @return Location where exception(s) are thrown from
*/
public Location getExceptionThrowerLocation(Edge exceptionEdge) {
if (!exceptionEdge.isExceptionEdge())
throw new IllegalArgumentException();
InstructionHandle handle = exceptionEdge.getSource().getExceptionThrower();
if (handle == null)
throw new IllegalStateException();
BasicBlock basicBlock = (handle.getInstruction() instanceof ATHROW) ? exceptionEdge.getSource()
: getSuccessorWithEdgeType(exceptionEdge.getSource(), EdgeTypes.FALL_THROUGH_EDGE);
if (basicBlock == null) {
if (removedEdgeList != null) {
// The fall-through edge might have been removed during
// CFG pruning. Look for it in the removed edge list.
for (Edge removedEdge : removedEdgeList) {
if (removedEdge.getType() == EdgeTypes.FALL_THROUGH_EDGE
&& removedEdge.getSource() == exceptionEdge.getSource()) {
basicBlock = removedEdge.getTarget();
break;
}
}
}
}
if (basicBlock == null) {
throw new IllegalStateException("No basic block for thrower " + handle + " in " + this.methodGen.getClassName() + "."
+ this.methodName + " : " + this.methodGen.getSignature());
}
return new Location(handle, basicBlock);
}
/**
* Get an Iterator over Edges removed from this CFG.
*
* @return Iterator over Edges removed from this CFG
*/
public Iterator<Edge> removedEdgeIterator() {
return removedEdgeList != null ? removedEdgeList.iterator() : new NullIterator<Edge>();
}
/**
* Get the first incoming edge in basic block with given type.
*
* @param basicBlock
* the basic block
* @param edgeType
* the edge type
* @return the Edge, or null if there is no edge with that edge type
*/
public Edge getIncomingEdgeWithType(BasicBlock basicBlock, @Type int edgeType) {
return getEdgeWithType(incomingEdgeIterator(basicBlock), edgeType);
}
/**
* Get the first outgoing edge in basic block with given type.
*
* @param basicBlock
* the basic block
* @param edgeType
* the edge type
* @return the Edge, or null if there is no edge with that edge type
*/
public Edge getOutgoingEdgeWithType(BasicBlock basicBlock, @Type int edgeType) {
return getEdgeWithType(outgoingEdgeIterator(basicBlock), edgeType);
}
private Edge getEdgeWithType(Iterator<Edge> iter, @Type int edgeType) {
while (iter.hasNext()) {
Edge edge = iter.next();
if (edge.getType() == edgeType)
return edge;
}
return null;
}
/**
* Allocate a new BasicBlock. The block won't be connected to any node in
* the graph.
*/
public BasicBlock allocate() {
BasicBlock b = new BasicBlock();
addVertex(b);
return b;
}
/**
* Get number of basic blocks. This is just here for compatibility with the
* old CFG method names.
*/
public int getNumBasicBlocks() {
return getNumVertices();
}
/**
* Get the number of edge labels allocated. This is just here for
* compatibility with the old CFG method names.
*/
public int getMaxEdgeId() {
return getNumEdgeLabels();
}
public void checkIntegrity() {
// Ensure that basic blocks have only consecutive instructions
for (Iterator<BasicBlock> i = blockIterator(); i.hasNext();) {
BasicBlock basicBlock = i.next();
InstructionHandle prev = null;
for (Iterator<InstructionHandle> j = basicBlock.instructionIterator(); j.hasNext();) {
InstructionHandle handle = j.next();
if (prev != null && prev.getNext() != handle)
throw new IllegalStateException("Non-consecutive instructions in block " + basicBlock.getLabel() + ": prev="
+ prev + ", handle=" + handle);
prev = handle;
}
}
}
@Override
protected Edge allocateEdge(BasicBlock source, BasicBlock target) {
return new Edge(source, target);
}
/*
* (non-Javadoc)
*
* @see
* edu.umd.cs.findbugs.graph.AbstractGraph#removeEdge(edu.umd.cs.findbugs
* .graph.AbstractEdge)
*/
@Override
public void removeEdge(Edge edge) {
super.removeEdge(edge);
// Keep track of removed edges.
if (removedEdgeList == null) {
removedEdgeList = new LinkedList<Edge>();
}
removedEdgeList.add(edge);
}
/**
* Get number of non-exception control successors of given basic block.
*
* @param block
* a BasicBlock
* @return number of non-exception control successors of the basic block
*/
public int getNumNonExceptionSucessors(BasicBlock block) {
int numNonExceptionSuccessors = block.getNumNonExceptionSuccessors();
if (numNonExceptionSuccessors < 0) {
numNonExceptionSuccessors = 0;
for (Iterator<Edge> i = outgoingEdgeIterator(block); i.hasNext();) {
Edge edge = i.next();
if (!edge.isExceptionEdge()) {
numNonExceptionSuccessors++;
}
}
block.setNumNonExceptionSuccessors(numNonExceptionSuccessors);
}
return numNonExceptionSuccessors;
}
/**
* Get the Location representing the entry to the CFG. Note that this is a
* "fake" Location, and shouldn't be relied on to yield source line
* information.
*
* @return Location at entry to CFG
*/
public Location getLocationAtEntry() {
InstructionHandle handle = getEntry().getFirstInstruction();
assert handle != null;
return new Location(handle, getEntry());
}
public Location getPreviousLocation(Location loc) {
InstructionHandle handle = loc.getHandle();
BasicBlock basicBlock = loc.getBasicBlock();
if (basicBlock.getFirstInstruction().equals(handle)) {
BasicBlock prevBlock = basicBlock;
while (true) {
prevBlock = getPredecessorWithEdgeType(prevBlock, EdgeTypes.FALL_THROUGH_EDGE);
if (prevBlock == null)
return loc;
handle = prevBlock.getLastInstruction();
if (handle != null)
return new Location(handle, prevBlock);
}
} else {
handle = handle.getPrev();
return new Location(handle, basicBlock);
}
}
}
// vim:ts=4