/* * Bytecode Analysis Framework * Copyright (C) 2004, 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; /** * Dataflow value representing the current nesting of * catch and finally blocks. We assume that any catch block * with a non-empty catch type is a user catch block, * and any catch block with an empty catch type (i.e., catch all * exceptions) is a finally block. This assumption isn't quite * accurate, but it seems to be a reasonable first approximation. * * <p>If valid (isValid() returns true), * a BlockType value is a stack of elements, which are either CATCH * or FINALLY values. Call getDepth() to get the current * nesting depth. Call get(int <i>n</i>) to get the * <i>n</i>th stack item. Call getTopValue() to get the current * top of the stack.</p> * * <p>If invalid (isValid() returns false), * a BlockType value is either <i>top</i> or <i>bottom</i>. * These are the special values at the top and bottom of * the dataflow lattice.</p> * * <p>The dataflow lattice is effectively finite-height because * real Java methods are guaranteed to have a finite * catch and finally block nesting level.</p> * * @see BlockTypeAnalysis * @author David Hovemeyer */ public class BlockType extends BitSet { /** * */ private static final long serialVersionUID = 1L; public static final boolean CATCH = false; public static final boolean FINALLY = true; private boolean isValid; private boolean isTop; private int depth; /** * Constructor. * Should only be called by BlockTypeAnalysis. */ BlockType() { } @Override public int hashCode() { final int prime = 31; int result = super.hashCode(); result = prime * result + depth; result = prime * result + (isTop ? 1231 : 1237); result = prime * result + (isValid ? 1231 : 1237); return result; } @Override public boolean equals(Object obj) { if (this == obj) return true; if (!super.equals(obj)) return false; if (!(obj instanceof BlockType)) return false; final BlockType other = (BlockType) obj; if (depth != other.depth) return false; if (isTop != other.isTop) return false; if (isValid != other.isValid) return false; return true; } /** * Return whether or not this value is valid, * meaning it contains a valid representation of the * nesting of catch and finally blocks. */ public boolean isValid() { return isValid; } /** * Get the current nesting depth. * The value must be valid. */ public int getDepth() { if (!isValid) throw new IllegalStateException(); return depth; } /** * Get the top value on the catch and finally block nesting stack. */ public boolean getTopValue() { if (depth == 0) throw new IllegalStateException(); return get(depth - 1); } /** * Return whether or not this value represents "normal" control-flow. * Normal control flow are all blocks outside any catch or finally block. */ public boolean isNormal() { if (!isValid) throw new IllegalStateException(); return getDepth() == 0; } /** * Make this value represent "normal" control flow. */ public void setNormal() { this.isValid = true; this.depth = 0; } /** * Return whether or not this is the special "top" dataflow value. */ public boolean isTop() { return !isValid && isTop; } /** * Make this the special "top" dataflow value. */ public void setTop() { this.isValid = false; this.isTop = true; } /** * Return whether or not this is the special "bottom" dataflow value. */ public boolean isBottom() { return !isValid && !isTop; } /** * Make this the special "bottom" dataflow value. */ public void setBottom() { this.isValid = false; this.isTop = false; } /** * Make this object an exact duplicate of given object. * * @param other the other BlockType object */ public void copyFrom(BlockType other) { this.isValid = other.isValid; this.isTop = other.isTop; if (isValid) { this.depth = other.depth; this.clear(); this.or(other); } } /** * Return whether or not this object is identical to the one given. * * @param other the other BlockType object * @return true if this object is identical to the one given, * false otherwise */ public boolean sameAs(BlockType other) { if (!this.isValid) { return !other.isValid && (this.isTop == other.isTop); } else { if (!other.isValid) return false; else { // Both facts are valid if (this.depth != other.depth) return false; // Compare bits for (int i = 0; i < this.depth; ++i) { if (this.get(i) != other.get(i)) return false; } return true; } } } /** * Merge other dataflow value into this value. * * @param other the other BlockType value */ public void mergeWith(BlockType other) { if (this.isTop() || other.isBottom()) { copyFrom(other); } else if (isValid()) { // To merge, we take the common prefix int pfxLen = Math.min(this.depth, other.depth); int commonLen; for (commonLen = 0; commonLen < pfxLen; ++commonLen) { if (this.get(commonLen) != other.get(commonLen)) break; } this.depth = commonLen; } } /** * Enter a catch block. */ public void pushCatch() { push(CATCH); } /** * Enter a finally block. */ public void pushFinally() { push(FINALLY); } @Override public String toString() { if (isTop()) return "<top>"; else if (isBottom()) return "<bottom>"; else { StringBuilder buf = new StringBuilder(); buf.append("N"); for (int i = 0; i < depth; ++i) { buf.append(get(i) == CATCH ? "C" : "F"); } return buf.toString(); } } private void push(boolean value) { set(depth, value); ++depth; } } // vim:ts=4