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