001: /*
002: * Bytecode Analysis Framework
003: * Copyright (C) 2004, University of Maryland
004: *
005: * This library is free software; you can redistribute it and/or
006: * modify it under the terms of the GNU Lesser General Public
007: * License as published by the Free Software Foundation; either
008: * version 2.1 of the License, or (at your option) any later version.
009: *
010: * This library is distributed in the hope that it will be useful,
011: * but WITHOUT ANY WARRANTY; without even the implied warranty of
012: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
013: * Lesser General Public License for more details.
014: *
015: * You should have received a copy of the GNU Lesser General Public
016: * License along with this library; if not, write to the Free Software
017: * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
018: */
019:
020: package edu.umd.cs.findbugs.ba;
021:
022: import java.util.BitSet;
023:
024: /**
025: * Dataflow value representing the current nesting of
026: * catch and finally blocks. We assume that any catch block
027: * with a non-empty catch type is a user catch block,
028: * and any catch block with an empty catch type (i.e., catch all
029: * exceptions) is a finally block. This assumption isn't quite
030: * accurate, but it seems to be a reasonable first approximation.
031: *
032: * <p>If valid (isValid() returns true),
033: * a BlockType value is a stack of elements, which are either CATCH
034: * or FINALLY values. Call getDepth() to get the current
035: * nesting depth. Call get(int <i>n</i>) to get the
036: * <i>n</i>th stack item. Call getTopValue() to get the current
037: * top of the stack.</p>
038: *
039: * <p>If invalid (isValid() returns false),
040: * a BlockType value is either <i>top</i> or <i>bottom</i>.
041: * These are the special values at the top and bottom of
042: * the dataflow lattice.</p>
043: *
044: * <p>The dataflow lattice is effectively finite-height because
045: * real Java methods are guaranteed to have a finite
046: * catch and finally block nesting level.</p>
047: *
048: * @see BlockTypeAnalysis
049: * @author David Hovemeyer
050: */
051: public class BlockType extends BitSet {
052: /**
053: *
054: */
055: private static final long serialVersionUID = 1L;
056: public static final boolean CATCH = false;
057: public static final boolean FINALLY = true;
058:
059: private boolean isValid;
060: private boolean isTop;
061: private int depth;
062:
063: /**
064: * Constructor.
065: * Should only be called by BlockTypeAnalysis.
066: */
067: BlockType() {
068: }
069:
070: @Override
071: public int hashCode() {
072: final int prime = 31;
073: int result = super .hashCode();
074: result = prime * result + depth;
075: result = prime * result + (isTop ? 1231 : 1237);
076: result = prime * result + (isValid ? 1231 : 1237);
077: return result;
078: }
079:
080: @Override
081: public boolean equals(Object obj) {
082: if (this == obj)
083: return true;
084: if (!super .equals(obj))
085: return false;
086: if (!(obj instanceof BlockType))
087: return false;
088: final BlockType other = (BlockType) obj;
089: if (depth != other.depth)
090: return false;
091: if (isTop != other.isTop)
092: return false;
093: if (isValid != other.isValid)
094: return false;
095: return true;
096: }
097:
098: /**
099: * Return whether or not this value is valid,
100: * meaning it contains a valid representation of the
101: * nesting of catch and finally blocks.
102: */
103: public boolean isValid() {
104: return isValid;
105: }
106:
107: /**
108: * Get the current nesting depth.
109: * The value must be valid.
110: */
111: public int getDepth() {
112: if (!isValid)
113: throw new IllegalStateException();
114: return depth;
115: }
116:
117: /**
118: * Get the top value on the catch and finally block nesting stack.
119: */
120: public boolean getTopValue() {
121: if (depth == 0)
122: throw new IllegalStateException();
123: return get(depth - 1);
124: }
125:
126: /**
127: * Return whether or not this value represents "normal" control-flow.
128: * Normal control flow are all blocks outside any catch or finally block.
129: */
130: public boolean isNormal() {
131: if (!isValid)
132: throw new IllegalStateException();
133: return getDepth() == 0;
134: }
135:
136: /**
137: * Make this value represent "normal" control flow.
138: */
139: public void setNormal() {
140: this .isValid = true;
141: this .depth = 0;
142: }
143:
144: /**
145: * Return whether or not this is the special "top" dataflow value.
146: */
147: public boolean isTop() {
148: return !isValid && isTop;
149: }
150:
151: /**
152: * Make this the special "top" dataflow value.
153: */
154: public void setTop() {
155: this .isValid = false;
156: this .isTop = true;
157: }
158:
159: /**
160: * Return whether or not this is the special "bottom" dataflow value.
161: */
162: public boolean isBottom() {
163: return !isValid && !isTop;
164: }
165:
166: /**
167: * Make this the special "bottom" dataflow value.
168: */
169: public void setBottom() {
170: this .isValid = false;
171: this .isTop = false;
172: }
173:
174: /**
175: * Make this object an exact duplicate of given object.
176: *
177: * @param other the other BlockType object
178: */
179: public void copyFrom(BlockType other) {
180: this .isValid = other.isValid;
181: this .isTop = other.isTop;
182: if (isValid) {
183: this .depth = other.depth;
184: this .clear();
185: this .or(other);
186: }
187: }
188:
189: /**
190: * Return whether or not this object is identical to the one given.
191: *
192: * @param other the other BlockType object
193: * @return true if this object is identical to the one given,
194: * false otherwise
195: */
196: public boolean sameAs(BlockType other) {
197: if (!this .isValid) {
198: return !other.isValid && (this .isTop == other.isTop);
199: } else {
200: if (!other.isValid)
201: return false;
202: else {
203: // Both facts are valid
204: if (this .depth != other.depth)
205: return false;
206:
207: // Compare bits
208: for (int i = 0; i < this .depth; ++i) {
209: if (this .get(i) != other.get(i))
210: return false;
211: }
212:
213: return true;
214: }
215: }
216: }
217:
218: /**
219: * Merge other dataflow value into this value.
220: *
221: * @param other the other BlockType value
222: */
223: public void mergeWith(BlockType other) {
224: if (this .isTop() || other.isBottom()) {
225: copyFrom(other);
226: } else if (isValid()) {
227: // To merge, we take the common prefix
228: int pfxLen = Math.min(this .depth, other.depth);
229: int commonLen;
230: for (commonLen = 0; commonLen < pfxLen; ++commonLen) {
231: if (this .get(commonLen) != other.get(commonLen))
232: break;
233: }
234: this .depth = commonLen;
235: }
236: }
237:
238: /**
239: * Enter a catch block.
240: */
241: public void pushCatch() {
242: push(CATCH);
243: }
244:
245: /**
246: * Enter a finally block.
247: */
248: public void pushFinally() {
249: push(FINALLY);
250: }
251:
252: @Override
253: public String toString() {
254: if (isTop())
255: return "<top>";
256: else if (isBottom())
257: return "<bottom>";
258: else {
259: StringBuffer buf = new StringBuffer();
260: buf.append("N");
261: for (int i = 0; i < depth; ++i) {
262: buf.append(get(i) == CATCH ? "C" : "F");
263: }
264: return buf.toString();
265: }
266: }
267:
268: private void push(boolean value) {
269: set(depth, value);
270: ++depth;
271: }
272: }
273:
274: // vim:ts=4
|