View Javadoc
1   package mockit.asm.util;
2   
3   import static mockit.asm.jvmConstants.ConstantPoolTypes.CLASS;
4   import static mockit.asm.jvmConstants.ConstantPoolTypes.DOUBLE;
5   import static mockit.asm.jvmConstants.ConstantPoolTypes.DYNAMIC;
6   import static mockit.asm.jvmConstants.ConstantPoolTypes.FIELD_REF;
7   import static mockit.asm.jvmConstants.ConstantPoolTypes.FLOAT;
8   import static mockit.asm.jvmConstants.ConstantPoolTypes.IMETHOD_REF;
9   import static mockit.asm.jvmConstants.ConstantPoolTypes.INTEGER;
10  import static mockit.asm.jvmConstants.ConstantPoolTypes.INVOKE_DYNAMIC;
11  import static mockit.asm.jvmConstants.ConstantPoolTypes.LONG;
12  import static mockit.asm.jvmConstants.ConstantPoolTypes.METHOD_HANDLE;
13  import static mockit.asm.jvmConstants.ConstantPoolTypes.METHOD_REF;
14  import static mockit.asm.jvmConstants.ConstantPoolTypes.METHOD_TYPE;
15  import static mockit.asm.jvmConstants.ConstantPoolTypes.MODULE;
16  import static mockit.asm.jvmConstants.ConstantPoolTypes.NAME_TYPE;
17  import static mockit.asm.jvmConstants.ConstantPoolTypes.PACKAGE;
18  import static mockit.asm.jvmConstants.ConstantPoolTypes.STRING;
19  import static mockit.asm.jvmConstants.ConstantPoolTypes.UTF8;
20  
21  import edu.umd.cs.findbugs.annotations.NonNull;
22  import edu.umd.cs.findbugs.annotations.Nullable;
23  
24  import mockit.asm.constantPool.DynamicItem;
25  import mockit.asm.jvmConstants.ConstantPoolTypes;
26  import mockit.asm.types.JavaType;
27  import mockit.asm.types.MethodType;
28  import mockit.asm.types.ReferenceType;
29  
30  import org.checkerframework.checker.index.qual.NonNegative;
31  
32  public class BytecodeReader {
33      /**
34       * The class to be parsed. <em>The content of this array must not be modified.</em>
35       */
36      @NonNull
37      public final byte[] code;
38  
39      /**
40       * The start index of each constant pool item in {@link #code}, plus one. The one byte offset skips the constant
41       * pool item tag that indicates its type.
42       */
43      @NonNull
44      public final int[] items;
45  
46      /**
47       * The String objects corresponding to the CONSTANT_Utf8 items. This cache avoids multiple parsing of a given
48       * CONSTANT_Utf8 constant pool item, which GREATLY improves performances (by a factor 2 to 3). This caching strategy
49       * could be extended to all constant pool items, but its benefit would not be so great for these items (because they
50       * are much less expensive to parse than CONSTANT_Utf8 items).
51       */
52      @NonNull
53      private final String[] strings;
54  
55      /**
56       * The buffer used to read strings.
57       */
58      @NonNull
59      private final char[] buf;
60  
61      /**
62       * The next index at {@link #code} to be read.
63       */
64      @NonNegative
65      public int codeIndex;
66  
67      protected BytecodeReader(@NonNull byte[] code) {
68          this.code = code;
69          codeIndex = 8;
70  
71          int itemCount = readUnsignedShort();
72          items = new int[itemCount];
73          strings = new String[itemCount];
74  
75          int maxStringSize = readConstantPoolItems();
76          buf = new char[maxStringSize];
77      }
78  
79      @NonNegative
80      private int readConstantPoolItems() {
81          int maxStringSize = 0;
82  
83          for (int itemIndex = 1; itemIndex < items.length; itemIndex++) {
84              int itemType = readSignedByte();
85              items[itemIndex] = codeIndex;
86              int itemSize = getItemSize(itemType);
87  
88              if (itemType == LONG || itemType == DOUBLE) {
89                  itemIndex++;
90              } else if (itemType == UTF8 && itemSize > maxStringSize) {
91                  maxStringSize = itemSize;
92              }
93  
94              codeIndex += itemSize - 1;
95          }
96  
97          return maxStringSize;
98      }
99  
100     @NonNegative
101     private int getItemSize(int itemType) {
102         switch (itemType) {
103             case FIELD_REF:
104             case METHOD_REF:
105             case IMETHOD_REF:
106             case INTEGER:
107             case FLOAT:
108             case NAME_TYPE:
109             case DYNAMIC:
110             case INVOKE_DYNAMIC:
111                 return 5;
112             case LONG:
113             case DOUBLE:
114                 return 9;
115             case UTF8:
116                 return 3 + readUnsignedShort(codeIndex);
117             case METHOD_HANDLE:
118                 return 4;
119             case MODULE:
120             case PACKAGE:
121             case CLASS:
122             case STRING:
123             case METHOD_TYPE:
124                 return 3;
125             default:
126                 throw new IllegalArgumentException("Unknown item type, cannot determine size: " + itemType);
127         }
128     }
129 
130     protected BytecodeReader(@NonNull BytecodeReader another) {
131         code = another.code;
132         items = another.items;
133         strings = another.strings;
134         buf = another.buf;
135         codeIndex = another.codeIndex;
136     }
137 
138     /**
139      * Reads an unsigned <code>byte</code> value in {@link #code}, incrementing {@link #codeIndex} by 1.
140      *
141      * @return the int
142      */
143     @NonNegative
144     public final int readUnsignedByte() {
145         return code[codeIndex++] & 0xFF;
146     }
147 
148     /**
149      * Reads an unsigned byte value in {@link #code}.
150      *
151      * @param u1CodeIndex
152      *            the start index of the value to be read in {@link #code}
153      *
154      * @return the int
155      */
156     @NonNegative
157     protected final int readUnsignedByte(@NonNegative int u1CodeIndex) {
158         return code[u1CodeIndex] & 0xFF;
159     }
160 
161     /**
162      * Reads a signed <code>byte</code> value in {@link #code}, incrementing {@link #codeIndex} by 1.
163      *
164      * @return the int
165      */
166     public final int readSignedByte() {
167         return code[codeIndex++];
168     }
169 
170     protected final char readChar(@NonNegative int s4CodeIndex) {
171         return (char) readInt(s4CodeIndex);
172     }
173 
174     protected final boolean readBoolean(@NonNegative int s4CodeIndex) {
175         return readInt(s4CodeIndex) != 0;
176     }
177 
178     /**
179      * Reads an unsigned short value in {@link #code}, incrementing {@link #codeIndex} by 2.
180      *
181      * @return the int
182      */
183     @NonNegative
184     public final int readUnsignedShort() {
185         byte[] b = code;
186         int i = codeIndex;
187         int byte0 = (b[i] & 0xFF) << 8;
188         i++;
189         int byte1 = b[i] & 0xFF;
190         i++;
191         codeIndex = i;
192         return byte0 | byte1;
193     }
194 
195     /**
196      * Reads an unsigned short value in {@link #code}.
197      *
198      * @param u2CodeIndex
199      *            the start index of the value to be read in {@link #code}
200      *
201      * @return the int
202      */
203     @NonNegative
204     protected final int readUnsignedShort(@NonNegative int u2CodeIndex) {
205         byte[] b = code;
206         int byte0 = (b[u2CodeIndex] & 0xFF) << 8;
207         int byte1 = b[u2CodeIndex + 1] & 0xFF;
208         return byte0 | byte1;
209     }
210 
211     /**
212      * Reads a signed <code>short</code> value in {@link #code}, incrementing {@link #codeIndex} by 2.
213      *
214      * @return the short
215      */
216     protected final short readShort() {
217         // noinspection NumericCastThatLosesPrecision
218         return (short) readUnsignedShort();
219     }
220 
221     /**
222      * Reads a signed short value in {@link #code}.
223      *
224      * @param u2CodeIndex
225      *            the start index of the value to be read in {@link #code}
226      *
227      * @return the short
228      */
229     protected final short readShort(@NonNegative int u2CodeIndex) {
230         // noinspection NumericCastThatLosesPrecision
231         return (short) readUnsignedShort(u2CodeIndex);
232     }
233 
234     /**
235      * Reads a signed <code>int</code> value in {@link #code}, incrementing {@link #codeIndex} by 4.
236      *
237      * @return the int
238      */
239     public final int readInt() {
240         byte[] b = code;
241         int i = codeIndex;
242         int byte0 = (b[i] & 0xFF) << 24;
243         i++;
244         int byte1 = (b[i] & 0xFF) << 16;
245         i++;
246         int byte2 = (b[i] & 0xFF) << 8;
247         i++;
248         int byte3 = b[i] & 0xFF;
249         i++;
250         codeIndex = i;
251         return byte0 | byte1 | byte2 | byte3;
252     }
253 
254     /**
255      * Reads a signed int value in {@link #code}.
256      *
257      * @param s4CodeIndex
258      *            the start index of the value to be read in {@link #code}
259      *
260      * @return the int
261      */
262     protected final int readInt(@NonNegative int s4CodeIndex) {
263         byte[] b = code;
264         return (b[s4CodeIndex] & 0xFF) << 24 | (b[s4CodeIndex + 1] & 0xFF) << 16 | (b[s4CodeIndex + 2] & 0xFF) << 8
265                 | b[s4CodeIndex + 3] & 0xFF;
266     }
267 
268     /**
269      * Reads a signed long value in {@link #code}, incrementing {@link #codeIndex} by 8.
270      *
271      * @return the long
272      */
273     public final long readLong() {
274         long l1 = readInt();
275         long l0 = readInt() & 0xFFFFFFFFL;
276         return l1 << 32 | l0;
277     }
278 
279     /**
280      * Reads a signed long value in {@link #code}.
281      *
282      * @param s8CodeIndex
283      *            the start index of the value to be read in {@link #code}
284      *
285      * @return the long
286      */
287     protected final long readLong(@NonNegative int s8CodeIndex) {
288         long l1 = readInt(s8CodeIndex);
289         long l0 = readInt(s8CodeIndex + 4) & 0xFFFFFFFFL;
290         return l1 << 32 | l0;
291     }
292 
293     public final double readDouble() {
294         long bits = readLong();
295         return Double.longBitsToDouble(bits);
296     }
297 
298     protected final double readDouble(@NonNegative int s8CodeIndex) {
299         long bits = readLong(s8CodeIndex);
300         return Double.longBitsToDouble(bits);
301     }
302 
303     public final float readFloat() {
304         int bits = readInt();
305         return Float.intBitsToFloat(bits);
306     }
307 
308     protected final float readFloat(@NonNegative int s4CodeIndex) {
309         int bits = readInt(s4CodeIndex);
310         return Float.intBitsToFloat(bits);
311     }
312 
313     /**
314      * Reads an UTF8 string in {@link #code}.
315      *
316      * @param itemIndex
317      *            index in {@link #items} for the UTF8 string to be read
318      *
319      * @return the string
320      */
321     @NonNull
322     @SuppressWarnings("CharUsedInArithmeticContext")
323     private String readUTF(@NonNegative int itemIndex) {
324         int startIndex = items[itemIndex];
325         int utfLen = readUnsignedShort(startIndex);
326         startIndex += 2;
327         int endIndex = startIndex + utfLen;
328         int strLen = 0;
329         int st = 0;
330         @SuppressWarnings("QuestionableName")
331         char cc = 0;
332 
333         while (startIndex < endIndex) {
334             int c = code[startIndex];
335             startIndex++;
336 
337             if (st == 0) {
338                 c &= 0xFF;
339 
340                 if (c < 0x80) { // 0xxxxxxx
341                     buf[strLen] = (char) c;
342                     strLen++;
343                 } else if (c < 0xE0 && c > 0xBF) { // 110x xxxx 10xx xxxx
344                     cc = (char) (c & 0x1F);
345                     st = 1;
346                 } else { // 1110 xxxx 10xx xxxx 10xx xxxx
347                     cc = (char) (c & 0x0F);
348                     st = 2;
349                 }
350             } else if (st == 1) { // byte 2 of 2-byte char or byte 3 of 3-byte char
351                 buf[strLen] = (char) (cc << 6 | c & 0x3F);
352                 strLen++;
353                 st = 0;
354             } else { // byte 2 of 3-byte char
355                 cc = (char) (cc << 6 | c & 0x3F);
356                 st = 1;
357             }
358         }
359 
360         return new String(buf, 0, strLen);
361     }
362 
363     /**
364      * Reads an UTF8 string constant pool item in {@link #code}, incrementing {@link #codeIndex} by 2.
365      *
366      * @return the String corresponding to the UTF8 item, or <code>null</code> if {@link #codeIndex} points to an item
367      *         whose value is zero
368      */
369     @Nullable
370     protected final String readUTF8() {
371         int itemIndex = readUnsignedShort();
372 
373         if (itemIndex == 0) {
374             return null;
375         }
376 
377         return readString(itemIndex);
378     }
379 
380     /**
381      * Reads an UTF8 string constant pool item in {@link #code}.
382      *
383      * @param u2CodeIndex
384      *            the index of an unsigned short value in {@link #code}, whose value is the index of an UTF8 constant
385      *            pool item
386      *
387      * @return the String corresponding to the UTF8 item, or <code>null</code> if index is zero or points to an item
388      *         whose value is zero
389      */
390     @Nullable
391     protected final String readUTF8(@NonNegative int u2CodeIndex) {
392         if (u2CodeIndex == 0) {
393             return null;
394         }
395 
396         int itemIndex = readUnsignedShort(u2CodeIndex);
397 
398         if (itemIndex == 0) {
399             return null;
400         }
401 
402         return readString(itemIndex);
403     }
404 
405     /**
406      * Reads the index of an UTF8 item in {@link #code}, incrementing {@link #codeIndex} by 2.
407      *
408      * @return the UTF8 string found in {@link #strings} at that index
409      */
410     @NonNull
411     public final String readNonnullUTF8() {
412         int itemIndex = readUnsignedShort();
413         return readString(itemIndex);
414     }
415 
416     /**
417      * Reads the index of an UTF8 item in {@link #code}.
418      *
419      * @param u2CodeIndex
420      *            the u 2 code index
421      *
422      * @return the UTF8 string found in {@link #strings} at that index
423      */
424     @NonNull
425     public final String readNonnullUTF8(@NonNegative int u2CodeIndex) {
426         int itemIndex = readUnsignedShort(u2CodeIndex);
427         return readString(itemIndex);
428     }
429 
430     /**
431      * Reads a string in {@link #strings} at the given index.
432      *
433      * @param itemIndex
434      *            the item index
435      *
436      * @return the string
437      */
438     @NonNull
439     public final String readString(@NonNegative int itemIndex) {
440         String cachedString = strings[itemIndex];
441 
442         if (cachedString != null) {
443             return cachedString;
444         }
445 
446         String newString = readUTF(itemIndex);
447         strings[itemIndex] = newString;
448         return newString;
449     }
450 
451     /**
452      * Reads the index of a constant item in {@link #code}, incrementing {@link #codeIndex} by 2.
453      *
454      * @return the UTF8 string found in {@link #strings} at that index
455      */
456     @NonNull
457     public final Object readConstItem() {
458         int constIndex = readUnsignedShort();
459         return readConst(constIndex);
460     }
461 
462     @NonNull
463     protected final Object readConstItem(@NonNegative int u2CodeIndex) {
464         int itemIndex = readUnsignedShort(u2CodeIndex);
465         return readConst(itemIndex);
466     }
467 
468     /**
469      * Reads a numeric or string constant pool item in {@link #code}.
470      *
471      * @param itemIndex
472      *            the index of a constant pool item
473      *
474      * @return the {@link Integer}, {@link Float}, {@link Long}, {@link Double}, {@link String}, {@link JavaType} or
475      *         {@link MethodHandle} corresponding to the given constant pool item
476      */
477     @NonNull
478     protected final Object readConst(@NonNegative int itemIndex) {
479         int constCodeIndex = items[itemIndex];
480         byte itemType = code[constCodeIndex - 1];
481 
482         switch (itemType) {
483             case INTEGER:
484                 return readInt(constCodeIndex);
485             case FLOAT:
486                 return readFloat(constCodeIndex);
487             case LONG:
488                 return readLong(constCodeIndex);
489             case DOUBLE:
490                 return readDouble(constCodeIndex);
491             case STRING:
492                 return readNonnullUTF8(constCodeIndex);
493             case CLASS:
494                 String typeDesc = readNonnullUTF8(constCodeIndex);
495                 return ReferenceType.createFromInternalName(typeDesc);
496             case METHOD_TYPE:
497                 String methodDesc = readNonnullUTF8(constCodeIndex);
498                 return MethodType.create(methodDesc);
499             case DYNAMIC: {
500                 int bsmStartIndex = readUnsignedShort(constCodeIndex);
501                 int nameIndex = readItem(constCodeIndex + 2);
502                 String name = readNonnullUTF8(nameIndex);
503                 String desc = readNonnullUTF8(nameIndex + 2);
504                 DynamicItem dynamicItem = new DynamicItem(itemIndex);
505                 dynamicItem.set(ConstantPoolTypes.DYNAMIC, name, desc, bsmStartIndex);
506                 return dynamicItem;
507             }
508             case METHOD_HANDLE:
509                 return readMethodHandle(constCodeIndex);
510             default:
511                 throw new IllegalArgumentException("Unknown const item type code: " + itemType);
512         }
513     }
514 
515     @NonNull
516     public final MethodHandle readMethodHandle() {
517         int itemIndex = readUnsignedShort();
518         return readMethodHandle(items[itemIndex]);
519     }
520 
521     @NonNull
522     protected final MethodHandle readMethodHandleItem(@NonNegative int bsmCodeIndex) {
523         int itemIndex = readUnsignedShort(bsmCodeIndex);
524         return readMethodHandle(items[itemIndex]);
525     }
526 
527     @NonNull
528     private MethodHandle readMethodHandle(@NonNegative int bsmCodeIndex) {
529         int tag = readUnsignedByte(bsmCodeIndex);
530         if (tag < MethodHandle.Tag.TAG_GETFIELD || tag > MethodHandle.Tag.TAG_INVOKEINTERFACE) {
531             throw new IllegalArgumentException("Illegal method-handle tag: " + tag);
532         }
533 
534         int classIndex = readItem(bsmCodeIndex + 1);
535         String owner = readNonnullClass(classIndex);
536 
537         int nameIndex = readItem(classIndex + 2);
538         String name = readNonnullUTF8(nameIndex);
539         String desc = readNonnullUTF8(nameIndex + 2);
540 
541         return new MethodHandle(tag, owner, name, desc);
542     }
543 
544     /**
545      * Reads the class name from the constant pool, incrementing {@link #codeIndex} by 2.
546      *
547      * @return the string
548      */
549     @Nullable
550     protected final String readClass() {
551         int itemCodeIndex = readItem();
552         return readUTF8(itemCodeIndex);
553     }
554 
555     /**
556      * Reads a class descriptor in {@link #code}, incrementing {@link #codeIndex} by 2.
557      *
558      * @return the string
559      */
560     @NonNull
561     public final String readNonnullClass() {
562         int itemCodeIndex = readItem();
563         return readNonnullUTF8(itemCodeIndex);
564     }
565 
566     @NonNull
567     public final String readNonnullClass(@NonNegative int u2CodeIndex) {
568         int itemCodeIndex = readItem(u2CodeIndex);
569         return readNonnullUTF8(itemCodeIndex);
570     }
571 
572     /**
573      * Reads an item index in {@link #code}, incrementing {@link #codeIndex} by 2.
574      *
575      * @return the item at that index in {@link #items}
576      */
577     @NonNegative
578     public final int readItem() {
579         int itemIndex = readUnsignedShort();
580         return items[itemIndex];
581     }
582 
583     @NonNegative
584     public final int readItem(@NonNegative int u2CodeIndex) {
585         int itemIndex = readUnsignedShort(u2CodeIndex);
586         return items[itemIndex];
587     }
588 }