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.type;
021:
022: import java.io.Serializable;
023: import java.util.BitSet;
024: import java.util.Iterator;
025: import java.util.NoSuchElementException;
026:
027: import org.apache.bcel.generic.ObjectType;
028: import org.apache.bcel.generic.ReferenceType;
029: import org.apache.bcel.generic.Type;
030:
031: import edu.umd.cs.findbugs.ba.AnalysisContext;
032: import edu.umd.cs.findbugs.ba.Hierarchy;
033: import edu.umd.cs.findbugs.ba.ch.Subtypes2;
034:
035: /**
036: * Class for keeping track of exceptions that can be
037: * thrown by an instruction. We distinguish <em>explicit</em>
038: * and <em>implicit</em> exceptions. Explicit exceptions
039: * are explicitly declared, thrown, or caught. Implicit exceptions
040: * are runtime faults (NPE, array out of bounds)
041: * not explicitly handled by the user code.
042: *
043: * @author David Hovemeyer
044: * @see TypeAnalysis
045: */
046: public class ExceptionSet implements Serializable {
047: private static final long serialVersionUID = 1;
048:
049: private ExceptionSetFactory factory;
050: private BitSet exceptionSet;
051: private BitSet explicitSet;
052: private int size;
053: private boolean universalHandler;
054: private Type commonSupertype;
055:
056: /**
057: * Object to iterate over the exception types in the set.
058: */
059: public class ThrownExceptionIterator implements
060: Iterator<ObjectType> {
061: private int last = -1, next = -1;
062:
063: ThrownExceptionIterator() {
064: findNext();
065: }
066:
067: public boolean hasNext() {
068: if (last == next)
069: findNext();
070: return next < factory.getNumTypes();
071: }
072:
073: public ObjectType next() {
074: if (!hasNext())
075: throw new NoSuchElementException();
076: ObjectType result = factory.getType(next);
077: last = next;
078: return result;
079: }
080:
081: public boolean isExplicit() {
082: return explicitSet.get(last);
083: }
084:
085: public void remove() {
086: exceptionSet.clear(last);
087: explicitSet.clear(last);
088: --size;
089: commonSupertype = null;
090: }
091:
092: private void findNext() {
093: ++next;
094: while (next < factory.getNumTypes()) {
095: if (exceptionSet.get(next))
096: break;
097: ++next;
098: }
099: }
100: }
101:
102: /**
103: * Constructor.
104: * Creates an empty set.
105: */
106: ExceptionSet(ExceptionSetFactory factory) {
107: this .factory = factory;
108: this .exceptionSet = new BitSet();
109: this .explicitSet = new BitSet();
110: this .size = 0;
111: this .universalHandler = false;
112: }
113:
114: /**
115: * Return an exact copy of this object.
116: */
117: public ExceptionSet duplicate() {
118: ExceptionSet dup = factory.createExceptionSet();
119: dup.exceptionSet.clear();
120: dup.exceptionSet.or(this .exceptionSet);
121: dup.explicitSet.clear();
122: dup.explicitSet.or(this .explicitSet);
123: dup.size = this .size;
124: dup.universalHandler = this .universalHandler;
125: dup.commonSupertype = this .commonSupertype;
126:
127: return dup;
128: }
129:
130: @Override
131: public int hashCode() {
132: return exceptionSet.hashCode() + explicitSet.hashCode();
133: }
134:
135: @Override
136: public boolean equals(Object o) {
137: if (o == null)
138: return false;
139: if (o.getClass() != this .getClass())
140: return false;
141:
142: ExceptionSet other = (ExceptionSet) o;
143: return exceptionSet.equals(other.exceptionSet)
144: && explicitSet.equals(other.explicitSet)
145: && universalHandler == other.universalHandler;
146: }
147:
148: /**
149: * Get the least (lowest in the lattice) common supertype
150: * of the exceptions in the set. Returns the special TOP
151: * type if the set is empty.
152: */
153: public Type getCommonSupertype() throws ClassNotFoundException {
154: if (commonSupertype != null)
155: return commonSupertype;
156:
157: if (isEmpty()) {
158: // This probably means that we're looking at an
159: // infeasible exception path.
160: return TypeFrame.getTopType();
161: }
162:
163: // Compute first common superclass
164: ThrownExceptionIterator i = iterator();
165: ReferenceType result = i.next();
166: while (i.hasNext()) {
167: if (Subtypes2.ENABLE_SUBTYPES2_FOR_COMMON_SUPERCLASS_QUERIES) {
168: result = AnalysisContext.currentAnalysisContext()
169: .getSubtypes2().getFirstCommonSuperclass(
170: result, i.next());
171: } else {
172: result = result.getFirstCommonSuperclass(i.next());
173: }
174: if (result == null) {
175: // This should only happen if the class hierarchy
176: // is incomplete. We'll just be conservative.
177: result = Type.THROWABLE;
178: break;
179: }
180: }
181:
182: // Cache and return the result
183: commonSupertype = result;
184: return result;
185: }
186:
187: /**
188: * Return an iterator over thrown exceptions.
189: */
190: public ThrownExceptionIterator iterator() {
191: return new ThrownExceptionIterator();
192: }
193:
194: /**
195: * Return whether or not the set is empty.
196: */
197: public boolean isEmpty() {
198: return size == 0;
199: }
200:
201: /**
202: * Checks to see if the exception set is a singleton set
203: * containing just the named exception
204: * @param exceptionName (in dotted format)
205: * @return true if it is
206: */
207: public boolean isSingleton(String exceptionName) {
208: if (size != 1)
209: return false;
210: ObjectType e = iterator().next();
211: return e.toString().equals(exceptionName);
212:
213: }
214:
215: /**
216: * Add an explicit exception.
217: *
218: * @param type type of the exception
219: */
220: public void addExplicit(ObjectType type) {
221: add(type, true);
222: }
223:
224: /**
225: * Add an implicit exception.
226: *
227: * @param type type of the exception
228: */
229: public void addImplicit(ObjectType type) {
230: add(type, false);
231: }
232:
233: /**
234: * Add an exception.
235: *
236: * @param type the exception type
237: * @param explicit true if the exception is explicitly declared
238: * or thrown, false if implicit
239: */
240: public void add(ObjectType type, boolean explicit) {
241: int index = factory.getIndexOfType(type);
242: if (!exceptionSet.get(index))
243: ++size;
244: exceptionSet.set(index);
245: if (explicit)
246: explicitSet.set(index);
247:
248: commonSupertype = null;
249: }
250:
251: /**
252: * Add all exceptions in the given set.
253: *
254: * @param other the set
255: */
256: public void addAll(ExceptionSet other) {
257: exceptionSet.or(other.exceptionSet);
258: explicitSet.or(other.explicitSet);
259: size = countBits(exceptionSet);
260:
261: commonSupertype = null;
262: }
263:
264: private int countBits(BitSet bitSet) {
265: int count = 0;
266: for (int i = 0; i < factory.getNumTypes(); ++i) {
267: if (bitSet.get(i))
268: ++count;
269: }
270: return count;
271: }
272:
273: /**
274: * Remove all exceptions from the set.
275: */
276: public void clear() {
277: exceptionSet.clear();
278: explicitSet.clear();
279: universalHandler = false;
280: commonSupertype = null;
281: }
282:
283: /**
284: * Return whether or not a universal exception handler
285: * was reached by the set.
286: */
287: public void sawUniversal() {
288: clear();
289: universalHandler = true;
290: }
291:
292: /**
293: * Mark the set as having reached a universal exception handler.
294: */
295: public boolean sawUniversalHandler() {
296: return universalHandler;
297: }
298:
299: /**
300: * Return whether or not the set contains any checked exceptions.
301: */
302: public boolean containsCheckedExceptions()
303: throws ClassNotFoundException {
304: for (ThrownExceptionIterator i = iterator(); i.hasNext();) {
305: ObjectType type = i.next();
306: if (!Hierarchy.isUncheckedException(type))
307: return true;
308: }
309: return false;
310: }
311:
312: /**
313: * Return whether or not the set contains any explicit exceptions.
314: */
315: public boolean containsExplicitExceptions() {
316: for (ThrownExceptionIterator i = iterator(); i.hasNext();) {
317: i.next();
318: if (i.isExplicit())
319: return true;
320: }
321: return false;
322: }
323:
324: @Override
325: public String toString() {
326: StringBuffer buf = new StringBuffer();
327: buf.append('{');
328: boolean first = true;
329: for (ThrownExceptionIterator i = iterator(); i.hasNext();) {
330: ObjectType type = i.next();
331: if (first)
332: first = false;
333: else
334: buf.append(',');
335: boolean implicit = !i.isExplicit();
336: if (implicit)
337: buf.append('[');
338: buf.append(type.toString());
339: if (implicit)
340: buf.append(']');
341: }
342: buf.append('}');
343: return buf.toString();
344: }
345:
346: public int size() {
347: return size;
348: }
349: }
350:
351: // vim:ts=4
|