/* * 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