001: /*
002: * Bytecode Analysis Framework
003: * Copyright (C) 2003,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 edu.umd.cs.findbugs.SystemProperties;
023:
024: /**
025: * Scan the raw bytecodes of a method.
026: * This is useful in order to find out quickly whether or not
027: * a method uses particular instructions.
028: *
029: * @author David Hovemeyer
030: */
031: public class BytecodeScanner implements org.apache.bcel.Constants {
032: private static final boolean DEBUG = SystemProperties
033: .getBoolean("bs.debug");
034:
035: /**
036: * Callback interface to report scanned instructions.
037: */
038: public interface Callback {
039: /**
040: * Called to indicate that a particular bytecode has been scanned.
041: *
042: * @param opcode the opcode of the instruction
043: * @param index the bytecode offset of the instruction
044: */
045: public void handleInstruction(int opcode, int index);
046: }
047:
048: /**
049: * Convert the unsigned value of a byte into a short.
050: *
051: * @param value the byte
052: * @return the byte's unsigned value as a short
053: */
054: private static short unsignedValueOf(byte value) {
055: short result;
056: if ((value & 0x80) != 0) {
057: result = (short) (value & 0x7F);
058: result |= 0x80;
059: } else {
060: result = (short) value;
061: }
062: return result;
063: }
064:
065: /**
066: * Extract an int from bytes at the given offset in the array.
067: *
068: * @param arr the array
069: * @param offset the offset in the array
070: */
071: private static int extractInt(byte[] arr, int offset) {
072: return ((arr[offset] & 0xFF) << 24)
073: | ((arr[offset + 1] & 0xFF) << 16)
074: | ((arr[offset + 2] & 0xFF) << 8)
075: | (arr[offset + 3] & 0xFF);
076: }
077:
078: private static final int PAD[] = { 0, 3, 2, 1 };
079:
080: /**
081: * Scan the raw bytecodes of a method.
082: *
083: * @param instructionList the bytecodes
084: * @param callback the callback object
085: */
086: public void scan(byte[] instructionList, Callback callback) {
087:
088: boolean wide = false;
089:
090: for (int index = 0; index < instructionList.length;) {
091: short opcode = unsignedValueOf(instructionList[index]);
092: callback.handleInstruction(opcode, index);
093:
094: if (DEBUG)
095: System.out.println(index + ": " + OPCODE_NAMES[opcode]);
096:
097: switch (opcode) {
098:
099: // Single byte instructions.
100: case NOP:
101: case ACONST_NULL:
102: case ICONST_M1:
103: case ICONST_0:
104: case ICONST_1:
105: case ICONST_2:
106: case ICONST_3:
107: case ICONST_4:
108: case ICONST_5:
109: case LCONST_0:
110: case LCONST_1:
111: case FCONST_0:
112: case FCONST_1:
113: case FCONST_2:
114: case DCONST_0:
115: case DCONST_1:
116: case ILOAD_0:
117: case ILOAD_1:
118: case ILOAD_2:
119: case ILOAD_3:
120: case LLOAD_0:
121: case LLOAD_1:
122: case LLOAD_2:
123: case LLOAD_3:
124: case FLOAD_0:
125: case FLOAD_1:
126: case FLOAD_2:
127: case FLOAD_3:
128: case DLOAD_0:
129: case DLOAD_1:
130: case DLOAD_2:
131: case DLOAD_3:
132: case ALOAD_0:
133: case ALOAD_1:
134: case ALOAD_2:
135: case ALOAD_3:
136: case IALOAD:
137: case LALOAD:
138: case FALOAD:
139: case DALOAD:
140: case AALOAD:
141: case BALOAD:
142: case CALOAD:
143: case SALOAD:
144: case ISTORE_0:
145: case ISTORE_1:
146: case ISTORE_2:
147: case ISTORE_3:
148: case LSTORE_0:
149: case LSTORE_1:
150: case LSTORE_2:
151: case LSTORE_3:
152: case FSTORE_0:
153: case FSTORE_1:
154: case FSTORE_2:
155: case FSTORE_3:
156: case DSTORE_0:
157: case DSTORE_1:
158: case DSTORE_2:
159: case DSTORE_3:
160: case ASTORE_0:
161: case ASTORE_1:
162: case ASTORE_2:
163: case ASTORE_3:
164: case IASTORE:
165: case LASTORE:
166: case FASTORE:
167: case DASTORE:
168: case AASTORE:
169: case BASTORE:
170: case CASTORE:
171: case SASTORE:
172: case POP:
173: case POP2:
174: case DUP:
175: case DUP_X1:
176: case DUP_X2:
177: case DUP2:
178: case DUP2_X1:
179: case DUP2_X2:
180: case SWAP:
181: case IADD:
182: case LADD:
183: case FADD:
184: case DADD:
185: case ISUB:
186: case LSUB:
187: case FSUB:
188: case DSUB:
189: case IMUL:
190: case LMUL:
191: case FMUL:
192: case DMUL:
193: case IDIV:
194: case LDIV:
195: case FDIV:
196: case DDIV:
197: case IREM:
198: case LREM:
199: case FREM:
200: case DREM:
201: case INEG:
202: case LNEG:
203: case FNEG:
204: case DNEG:
205: case ISHL:
206: case LSHL:
207: case ISHR:
208: case LSHR:
209: case IUSHR:
210: case LUSHR:
211: case IAND:
212: case LAND:
213: case IOR:
214: case LOR:
215: case IXOR:
216: case LXOR:
217: case I2L:
218: case I2F:
219: case I2D:
220: case L2I:
221: case L2F:
222: case L2D:
223: case F2I:
224: case F2L:
225: case F2D:
226: case D2I:
227: case D2L:
228: case D2F:
229: case I2B:
230: case I2C:
231: case I2S:
232: case LCMP:
233: case FCMPL:
234: case FCMPG:
235: case DCMPL:
236: case DCMPG:
237: case IRETURN:
238: case LRETURN:
239: case FRETURN:
240: case DRETURN:
241: case ARETURN:
242: case RETURN:
243: case ARRAYLENGTH:
244: case ATHROW:
245: case MONITORENTER:
246: case MONITOREXIT:
247: ++index;
248: break;
249:
250: // Two byte instructions.
251: case BIPUSH:
252: case LDC:
253: case NEWARRAY:
254: index += 2;
255: break;
256:
257: // Instructions that can be used with the WIDE prefix.
258: case ILOAD:
259: case LLOAD:
260: case FLOAD:
261: case DLOAD:
262: case ALOAD:
263: case ISTORE:
264: case LSTORE:
265: case FSTORE:
266: case DSTORE:
267: case ASTORE:
268: case RET:
269: if (wide) {
270: // Skip opcode and two immediate bytes.
271: index += 3;
272: wide = false;
273: } else {
274: // Skip opcode and one immediate byte.
275: index += 2;
276: }
277: break;
278:
279: // IINC is a special case for WIDE handling
280: case IINC:
281: if (wide) {
282: // Skip opcode, two byte index, and two byte immediate value.
283: index += 5;
284: wide = false;
285: } else {
286: // Skip opcode, one byte index, and one byte immedate value.
287: index += 3;
288: }
289: break;
290:
291: // Three byte instructions.
292: case SIPUSH:
293: case LDC_W:
294: case LDC2_W:
295: case IFEQ:
296: case IFNE:
297: case IFLT:
298: case IFGE:
299: case IFGT:
300: case IFLE:
301: case IF_ICMPEQ:
302: case IF_ICMPNE:
303: case IF_ICMPLT:
304: case IF_ICMPGE:
305: case IF_ICMPGT:
306: case IF_ICMPLE:
307: case IF_ACMPEQ:
308: case IF_ACMPNE:
309: case GOTO:
310: case JSR:
311: case GETSTATIC:
312: case PUTSTATIC:
313: case GETFIELD:
314: case PUTFIELD:
315: case INVOKEVIRTUAL:
316: case INVOKESPECIAL:
317: case INVOKESTATIC:
318: case NEW:
319: case ANEWARRAY:
320: case CHECKCAST:
321: case INSTANCEOF:
322: case IFNULL:
323: case IFNONNULL:
324: index += 3;
325: break;
326:
327: // Four byte instructions.
328: case MULTIANEWARRAY:
329: index += 4;
330: break;
331:
332: // Five byte instructions.
333: case INVOKEINTERFACE:
334: case GOTO_W:
335: case JSR_W:
336: index += 5;
337: break;
338:
339: // TABLESWITCH - variable length.
340: case TABLESWITCH: {
341: // Skip padding.
342: int offset = index + 1; // skip the opcode
343: offset += PAD[offset & 3];
344: assert (offset & 3) == 0;
345:
346: // offset should now be posited at the default value
347:
348: // Extract min and max values.
349: int low = extractInt(instructionList, offset + 4);
350: int high = extractInt(instructionList, offset + 8);
351: int tableSize = (high - low) + 1;
352: if (DEBUG)
353: System.out.println("tableswitch: low=" + low
354: + ", high=" + high + ", tableSize="
355: + tableSize);
356:
357: // Skip to next instruction.
358: index = offset + 12 + (tableSize * 4);
359: }
360: break;
361:
362: // LOOKUPSWITCH - variable length.
363: case LOOKUPSWITCH: {
364: // Skip padding.
365: int offset = index + 1; // skip the opcode
366: offset += PAD[offset & 3];
367: assert (offset & 3) == 0;
368:
369: // offset should now be posited at the default value
370:
371: // Extract number of value/offset pairs.
372: int numPairs = extractInt(instructionList, offset + 4);
373: if (DEBUG)
374: System.out.println("lookupswitch: numPairs="
375: + numPairs);
376:
377: // Skip to next instruction.
378: index = offset + 8 + (numPairs * 8);
379: }
380: break;
381:
382: // Wide prefix.
383: case WIDE:
384: wide = true;
385: ++index;
386: break;
387:
388: default:
389: throw new IllegalArgumentException("Bad opcode "
390: + opcode + " at offset " + index);
391: }
392:
393: if (index < 0)
394: throw new IllegalStateException("index=" + index
395: + ", opcode=" + opcode);
396:
397: }
398: }
399: }
400:
401: // vim:ts=4
|