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