/* * Bytecode Analysis Framework * Copyright (C) 2003-2007 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.Iterator; import java.util.NoSuchElementException; import javax.annotation.CheckForNull; import org.apache.bcel.Constants; import org.apache.bcel.generic.CodeExceptionGen; import org.apache.bcel.generic.InstructionHandle; import edu.umd.cs.findbugs.SystemProperties; import edu.umd.cs.findbugs.graph.AbstractVertex; /** * Simple basic block abstraction for BCEL. * * @author David Hovemeyer * @see CFG */ public class BasicBlock extends AbstractVertex<Edge, BasicBlock> implements Debug { /* * ---------------------------------------------------------------------- * Static data * ---------------------------------------------------------------------- */ /** * Set of instruction opcodes that have an implicit null check. */ private static final BitSet nullCheckInstructionSet = new BitSet(); static { nullCheckInstructionSet.set(Constants.GETFIELD); nullCheckInstructionSet.set(Constants.PUTFIELD); nullCheckInstructionSet.set(Constants.INVOKESPECIAL); nullCheckInstructionSet.set(Constants.INVOKEVIRTUAL); nullCheckInstructionSet.set(Constants.INVOKEINTERFACE); nullCheckInstructionSet.set(Constants.AALOAD); nullCheckInstructionSet.set(Constants.AASTORE); nullCheckInstructionSet.set(Constants.BALOAD); nullCheckInstructionSet.set(Constants.BASTORE); nullCheckInstructionSet.set(Constants.CALOAD); nullCheckInstructionSet.set(Constants.CASTORE); nullCheckInstructionSet.set(Constants.DALOAD); nullCheckInstructionSet.set(Constants.DASTORE); nullCheckInstructionSet.set(Constants.FALOAD); nullCheckInstructionSet.set(Constants.FASTORE); nullCheckInstructionSet.set(Constants.IALOAD); nullCheckInstructionSet.set(Constants.IASTORE); nullCheckInstructionSet.set(Constants.LALOAD); nullCheckInstructionSet.set(Constants.LASTORE); nullCheckInstructionSet.set(Constants.SALOAD); nullCheckInstructionSet.set(Constants.SASTORE); nullCheckInstructionSet.set(Constants.MONITORENTER); nullCheckInstructionSet.set(Constants.ARRAYLENGTH); // nullCheckInstructionSet.set(Constants.MONITOREXIT); nullCheckInstructionSet.set(Constants.ATHROW); // Any others? } /* * ---------------------------------------------------------------------- * Fields * ---------------------------------------------------------------------- */ private InstructionHandle firstInstruction; private InstructionHandle lastInstruction; private InstructionHandle exceptionThrower; // instruction for which this // block is the ETB private CodeExceptionGen exceptionGen; // set if this block is the entry // point of an exception handler private boolean inJSRSubroutine; private int numNonExceptionSuccessors; /* * ---------------------------------------------------------------------- * Public methods * ---------------------------------------------------------------------- */ /** * Constructor. */ public BasicBlock() { this.firstInstruction = null; this.lastInstruction = null; this.exceptionThrower = null; this.exceptionGen = null; this.inJSRSubroutine = false; this.numNonExceptionSuccessors = -1; } public boolean isInJSRSubroutine() { return inJSRSubroutine; } void setInJSRSubroutine(boolean inJSRSubroutine) { this.inJSRSubroutine = inJSRSubroutine; } /** * Get the basic block's integer label. * * @deprecated call getLabel() instead * @return the BasicBlock's integer label */ @Deprecated public int getId() { return getLabel(); } @Override public String toString() { return "block " + String.valueOf(getLabel()); } /** * Set the instruction for which this block is the ETB. * * @param exceptionThrower * the instruction */ public void setExceptionThrower(InstructionHandle exceptionThrower) { this.exceptionThrower = exceptionThrower; } /** * Return whether or not this block is an exception thrower. */ public boolean isExceptionThrower() { return exceptionThrower != null; } /** * Get the instruction for which this block is an exception thrower. * * @return the instruction, or null if this block is not an exception * thrower */ public InstructionHandle getExceptionThrower() { return exceptionThrower; } /** * Return whether or not this block is a null check. */ public boolean isNullCheck() { // Null check blocks must be exception throwers, // and are always empty. (The only kind of non-empty // exception throwing block is one terminated by an ATHROW). if (!isExceptionThrower() || getFirstInstruction() != null) return false; short opcode = exceptionThrower.getInstruction().getOpcode(); return nullCheckInstructionSet.get(opcode); } /** * Get the first instruction in the basic block. */ public InstructionHandle getFirstInstruction() { return firstInstruction; } /** * Get the last instruction in the basic block. */ public InstructionHandle getLastInstruction() { return lastInstruction; } /** * Get the successor of given instruction within the basic block. * * @param handle * the instruction * @return the instruction's successor, or null if the instruction is the * last in the basic block */ public @CheckForNull InstructionHandle getSuccessorOf(InstructionHandle handle) { if (VERIFY_INTEGRITY) { if (!containsInstruction(handle)) throw new IllegalStateException(); } return handle == lastInstruction ? null : handle.getNext(); } /** * Get the predecessor of given instruction within the basic block. * * @param handle * the instruction * @return the instruction's predecessor, or null if the instruction is the * first in the basic block */ public InstructionHandle getPredecessorOf(InstructionHandle handle) { if (VERIFY_INTEGRITY) { if (!containsInstruction(handle)) throw new IllegalStateException(); } return handle == firstInstruction ? null : handle.getPrev(); } /** * Add an InstructionHandle to the basic block. * * @param handle * the InstructionHandle */ public void addInstruction(InstructionHandle handle) { if (firstInstruction == null) { firstInstruction = lastInstruction = handle; } else { if (VERIFY_INTEGRITY && handle != lastInstruction.getNext()) throw new IllegalStateException("Adding non-consecutive instruction"); lastInstruction = handle; } } /** * A forward Iterator over the instructions of a basic block. The * duplicate() method can be used to make an exact copy of this iterator. * Calling next() on the duplicate will not affect the original, and vice * versa. */ public class InstructionIterator implements Iterator<InstructionHandle> { private InstructionHandle next, last; public InstructionIterator(InstructionHandle first, InstructionHandle last) { this.next = first; this.last = last; } public boolean hasNext() { return next != null; } public InstructionHandle next() { if (!hasNext()) throw new NoSuchElementException(); InstructionHandle result = next; next = (result == last) ? null : next.getNext(); return result; } public void remove() { throw new UnsupportedOperationException(); } public InstructionIterator duplicate() { return new InstructionIterator(next, last); } @Override public boolean equals(Object o) { if (!(o instanceof InstructionIterator)) return false; InstructionIterator other = (InstructionIterator) o; return this.next == other.next && this.last == other.last; } @Override public int hashCode() { int code = getBasicBlock().hashCode() * 227; if (next != null) code += next.getPosition() + 1; return code; } private BasicBlock getBasicBlock() { return BasicBlock.this; } @Override public String toString() { StringBuilder buf = new StringBuilder(); buf.append("[basicBlock="); buf.append(getBasicBlock().getLabel()); if (next != null) { buf.append(", next=" + next); } else if (getBasicBlock().isExceptionThrower()) { buf.append(", check for" + getBasicBlock().getExceptionThrower()); } else { buf.append(", end"); } buf.append(']'); return buf.toString(); } } /** * Get an Iterator over the instructions in the basic block. */ public InstructionIterator instructionIterator() { return new InstructionIterator(firstInstruction, lastInstruction); } /** * A reverse Iterator over the instructions in a basic block. */ private static class InstructionReverseIterator implements Iterator<InstructionHandle> { private InstructionHandle next, first; public InstructionReverseIterator(InstructionHandle last, InstructionHandle first) { this.next = last; this.first = first; } public boolean hasNext() { return next != null; } public InstructionHandle next() throws NoSuchElementException { if (!hasNext()) throw new NoSuchElementException(); InstructionHandle result = next; next = (result == first) ? null : next.getPrev(); return result; } public void remove() { throw new UnsupportedOperationException(); } } /** * Get an Iterator over the instructions in the basic block in reverse * order. This is useful for backwards dataflow analyses. */ public Iterator<InstructionHandle> instructionReverseIterator() { return new InstructionReverseIterator(lastInstruction, firstInstruction); } /** * Return true if there are no Instructions in this basic block. */ public boolean isEmpty() { return firstInstruction == null; } public int pos() { if (isEmpty()) return getExceptionThrower().getPosition(); return firstInstruction.getPosition(); } /** * Is this block an exception handler? */ public boolean isExceptionHandler() { return exceptionGen != null; } /** * Get CodeExceptionGen object; returns null if this basic block is not the * entry point of an exception handler. * * @return the CodeExceptionGen object, or null */ public CodeExceptionGen getExceptionGen() { return exceptionGen; } /** * Set the CodeExceptionGen object. Marks this basic block as the entry * point of an exception handler. * * @param exceptionGen * the CodeExceptionGen object for the block */ public void setExceptionGen(CodeExceptionGen exceptionGen) { this.exceptionGen = exceptionGen; } /** * Return whether or not the basic block contains the given instruction. * * @param handle * the instruction * @return true if the block contains the instruction, false otherwise */ public boolean containsInstruction(InstructionHandle handle) { Iterator<InstructionHandle> i = instructionIterator(); while (i.hasNext()) { if (i.next() == handle) return true; } return false; } /** * Return whether or not the basic block contains the instruction with the * given bytecode offset. * * @param offset * the bytecode offset * @return true if the block contains an instruction with the given offset, * false if it does not */ public boolean containsInstructionWithOffset(int offset) { Iterator<InstructionHandle> i = instructionIterator(); while (i.hasNext()) { if (i.next().getPosition() == offset) return true; } return false; } /** * @return Returns the numNonExceptionSuccessors. */ int getNumNonExceptionSuccessors() { return numNonExceptionSuccessors; } /** * @param numNonExceptionSuccessors * The numNonExceptionSuccessors to set. */ void setNumNonExceptionSuccessors(int numNonExceptionSuccessors) { this.numNonExceptionSuccessors = numNonExceptionSuccessors; } } // vim:ts=4