All Downloads are FREE. Search and download functionalities are using the official Maven repository.

mockit.external.asm.BytecodeReader Maven / Gradle / Ivy

Go to download

JMockit is a Java toolkit for automated developer testing. It contains APIs for the creation of the objects to be tested, for mocking dependencies, and for faking external APIs; JUnit (4 & 5) and TestNG test runners are supported. It also contains an advanced code coverage tool.

There is a newer version: 1.49
Show newest version
package mockit.external.asm;

import javax.annotation.*;

import static mockit.external.asm.Item.Type.*;

class BytecodeReader
{
   /**
    * The class to be parsed. The content of this array must not be modified.
    */
   @Nonnull final byte[] code;

   /**
    * The start index of each constant pool item in {@link #code}, plus one.
    * The one byte offset skips the constant pool item tag that indicates its type.
    */
   @Nonnull final int[] items;

   /**
    * The String objects corresponding to the CONSTANT_Utf8 items. This cache avoids multiple parsing of a given
    * CONSTANT_Utf8 constant pool item, which GREATLY improves performances (by a factor 2 to 3). This caching
    * strategy could be extended to all constant pool items, but its benefit would not be so great for these items
    * (because they are much less expensive to parse than CONSTANT_Utf8 items).
    */
   @Nonnull private final String[] strings;

   /**
    * The buffer used to read strings.
    */
   @Nonnull private final char[] buf;

   /**
    * The next index at {@link #code} to be read.
    */
   @Nonnegative int codeIndex;

   BytecodeReader(@Nonnull byte[] code) {
      this.code = code;
      codeIndex = 8;

      int itemCount = readUnsignedShort();
      items = new int[itemCount];
      strings = new String[itemCount];

      int maxStringSize = readConstantPoolItems();
      buf = new char[maxStringSize];
   }

   @Nonnegative
   private int readConstantPoolItems() {
      int maxStringSize = 0;

      for (int itemIndex = 1; itemIndex < items.length; itemIndex++) {
         int itemType = readSignedByte();
         items[itemIndex] = codeIndex;
         int itemSize = getItemSize(itemType);

         if (itemType == LONG || itemType == DOUBLE) {
            itemIndex++;
         }
         else if (itemType == UTF8 && itemSize > maxStringSize) {
            maxStringSize = itemSize;
         }

         codeIndex += itemSize - 1;
      }

      return maxStringSize;
   }

   @Nonnegative
   private int getItemSize(int itemType) {
      switch (itemType) {
         case FIELD: case METH: case IMETH: case INT: case FLOAT: case NAME_TYPE: case INDY: return 5;
         case LONG: case DOUBLE: return 9;
         case UTF8: return 3 + readUnsignedShort(codeIndex);
         case HANDLE: return 4;
         default: return 3; // CLASS|STR|MTYPE
      }
   }

   BytecodeReader(@Nonnull BytecodeReader another) {
      code = another.code;
      items = another.items;
      strings = another.strings;
      buf = another.buf;
      codeIndex = another.codeIndex;
   }

   /**
    * Reads an unsigned byte value in {@link #code}, incrementing {@link #codeIndex} by 1.
    */
   final int readUnsignedByte() {
      return code[codeIndex++] & 0xFF;
   }

   /**
    * Reads an unsigned byte value in {@link #code}.
    *
    * @param codeIndex the start index of the value to be read in {@link #code}.
    * @return the read value.
    */
   final int readUnsignedByte(@Nonnegative int codeIndex) {
      return code[codeIndex] & 0xFF;
   }

   /**
    * Reads a signed byte value in {@link #code}, incrementing {@link #codeIndex} by 1.
    */
   final int readSignedByte() {
      return code[codeIndex++];
   }

   final char readChar(@Nonnegative int codeIndex) {
      return (char) readInt(codeIndex);
   }

   final boolean readBoolean(@Nonnegative int codeIndex) {
      return readInt(codeIndex) != 0;
   }

   /**
    * Reads an unsigned short value in {@link #code}, incrementing {@link #codeIndex} by 2.
    */
   @Nonnegative
   final int readUnsignedShort() {
      byte[] b = code;
      int i = codeIndex;
      int byte0 = (b[i++] & 0xFF) << 8;
      int byte1 =  b[i++] & 0xFF;
      codeIndex = i;
      return byte0 | byte1;
   }

   /**
    * Reads an unsigned short value in {@link #code}.
    *
    * @param codeIndex the start index of the value to be read in {@link #code}.
    * @return the read value.
    */
   @Nonnegative
   final int readUnsignedShort(@Nonnegative int codeIndex) {
      byte[] b = code;
      return ((b[codeIndex] & 0xFF) << 8) | (b[codeIndex + 1] & 0xFF);
   }

   /**
    * Reads a signed short value in {@link #code}, incrementing {@link #codeIndex} by 2.
    */
   final short readShort() {
      return (short) readUnsignedShort();
   }

   /**
    * Reads a signed short value in {@link #code}.
    *
    * @param codeIndex the start index of the value to be read in {@link #code}.
    * @return the read value.
    */
   final short readShort(@Nonnegative int codeIndex) {
      return (short) readUnsignedShort(codeIndex);
   }

   /**
    * Reads a signed int value in {@link #code}, incrementing {@link #codeIndex} by 4.
    */
   final int readInt() {
      byte[] b = code;
      int i = codeIndex;
      int byte0 = (b[i++] & 0xFF) << 24;
      int byte1 = (b[i++] & 0xFF) << 16;
      int byte2 = (b[i++] & 0xFF) << 8;
      int byte3 =  b[i++] & 0xFF;
      codeIndex = i;
      return byte0 | byte1 | byte2 | byte3;
   }

   /**
    * Reads a signed int value in {@link #code}.
    *
    * @param codeIndex the start index of the value to be read in {@link #code}.
    * @return the read value.
    */
   final int readInt(@Nonnegative int codeIndex) {
      byte[] b = code;
      return
         ((b[codeIndex] & 0xFF) << 24) | ((b[codeIndex + 1] & 0xFF) << 16) |
         ((b[codeIndex + 2] & 0xFF) << 8) | (b[codeIndex + 3] & 0xFF);
   }

   /**
    * Reads a signed long value in {@link #code}, incrementing {@link #codeIndex} by 8.
    */
   final long readLong() {
      long l1 = readInt();
      long l0 = readInt() & 0xFFFFFFFFL;
      return (l1 << 32) | l0;
   }

   /**
    * Reads a signed long value in {@link #code}.
    *
    * @param codeIndex the start index of the value to be read in {@link #code}.
    * @return the read value.
    */
   final long readLong(@Nonnegative int codeIndex) {
      long l1 = readInt(codeIndex);
      long l0 = readInt(codeIndex + 4) & 0xFFFFFFFFL;
      return (l1 << 32) | l0;
   }

   final double readDouble() {
      long bits = readLong();
      return Double.longBitsToDouble(bits);
   }

   final double readDouble(@Nonnegative int codeIndex) {
      long bits = readLong(codeIndex);
      return Double.longBitsToDouble(bits);
   }

   final float readFloat() {
      int bits = readInt();
      return Float.intBitsToFloat(bits);
   }

   final float readFloat(@Nonnegative int codeIndex) {
      int bits = readInt(codeIndex);
      return Float.intBitsToFloat(bits);
   }

   /**
    * Reads an UTF8 string in {@link #code}.
    *
    * @param itemIndex index in {@link #items} for the UTF8 string to be read.
    * @return the String corresponding to the specified UTF8 string.
    */
   @Nonnull
   private String readUTF(@Nonnegative int itemIndex) {
      int startIndex = items[itemIndex];
      int utfLen = readUnsignedShort(startIndex);
      startIndex += 2;
      int endIndex = startIndex + utfLen;
      int strLen = 0;
      int st = 0;
      char cc = 0;

      while (startIndex < endIndex) {
         int c = code[startIndex++];

         if (st == 0) {
            c = c & 0xFF;

            if (c < 0x80) { // 0xxxxxxx
               buf[strLen++] = (char) c;
            }
            else if (c < 0xE0 && c > 0xBF) { // 110x xxxx 10xx xxxx
               cc = (char) (c & 0x1F);
               st = 1;
            }
            else { // 1110 xxxx 10xx xxxx 10xx xxxx
               cc = (char) (c & 0x0F);
               st = 2;
            }
         }
         else if (st == 1) { // byte 2 of 2-byte char or byte 3 of 3-byte char
            buf[strLen++] = (char) ((cc << 6) | (c & 0x3F));
            st = 0;
         }
         else { // byte 2 of 3-byte char
            cc = (char) ((cc << 6) | (c & 0x3F));
            st = 1;
         }
      }

      return new String(buf, 0, strLen);
   }

   /**
    * Reads an UTF8 string constant pool item in {@link #code}, incrementing {@link #codeIndex} by 2.
    *
    * @return the String corresponding to the specified UTF8 item, or null if {@link #codeIndex} points to an
    * item whose value is zero.
    */
   @Nullable
   final String readUTF8() {
      int itemIndex = readUnsignedShort();

      if (itemIndex == 0) {
         return null;
      }

      return readString(itemIndex);
   }

   /**
    * Reads an UTF8 string constant pool item in {@link #code}.
    *
    * @param codeIndex the start index of an unsigned short value in {@link #code}, whose value is the index of an UTF8
    *                  constant pool item.
    * @return the String corresponding to the specified UTF8 item, or null if index is zero or points to an
    * item whose value is zero.
    */
   @Nullable
   final String readUTF8(@Nonnegative int codeIndex) {
      if (codeIndex == 0) {
         return null;
      }

      int itemIndex = readUnsignedShort(codeIndex);

      if (itemIndex == 0) {
         return null;
      }

      return readString(itemIndex);
   }

   /**
    * Reads the index of an UTF8 item in {@link #code}, incrementing {@link #codeIndex} by 2.
    *
    * @return the UTF8 string found in {@link #strings} at that index
    */
   @Nonnull
   final String readNonnullUTF8() {
      int itemIndex = readUnsignedShort();
      return readString(itemIndex);
   }

   /**
    * Reads the index of an UTF8 item in {@link #code}.
    *
    * @return the UTF8 string found in {@link #strings} at that index
    */
   @Nonnull
   final String readNonnullUTF8(@Nonnegative int codeIndex) {
      int itemIndex = readUnsignedShort(codeIndex);
      return readString(itemIndex);
   }

   /**
    * Reads a string in {@link #strings} at the given index.
    */
   @Nonnull
   final String readString(@Nonnegative int itemIndex) {
      String string = strings[itemIndex];

      if (string != null) {
         return string;
      }

      string = readUTF(itemIndex);
      strings[itemIndex] = string;
      return string;
   }

   /**
    * Reads the index of a constant item in {@link #code}, incrementing {@link #codeIndex} by 2.
    *
    * @return the UTF8 string found in {@link #strings} at that index
    */
   @Nonnull
   final Object readConstItem() {
      int constIndex = readUnsignedShort();
      Object cst = readConst(constIndex);
      return cst;
   }

   @Nonnull
   final Object readConstItem(@Nonnegative int codeIndex) {
      int itemIndex = readUnsignedShort(codeIndex);
      return readConst(itemIndex);
   }

   /**
    * Reads a numeric or string constant pool item in {@link #code}.
    *
    * @param itemIndex the index of a constant pool item.
    * @return the {@link Integer}, {@link Float}, {@link Long}, {@link Double}, {@link String}, {@link JavaType} or
    * {@link MethodHandle} corresponding to the given constant pool item.
    */
   @Nonnull
   final Object readConst(@Nonnegative int itemIndex) {
      int codeIndex = items[itemIndex];
      byte itemType = code[codeIndex - 1];

      switch (itemType) {
         case INT:    return readInt(codeIndex);
         case FLOAT:  return readFloat(codeIndex);
         case LONG:   return readLong(codeIndex);
         case DOUBLE: return readDouble(codeIndex);
         case STR:    return readNonnullUTF8(codeIndex);
         case CLASS:
            String typeDesc = readNonnullUTF8(codeIndex);
            return ReferenceType.createFromInternalName(typeDesc);
         case MTYPE:
            String methodDesc = readNonnullUTF8(codeIndex);
            return MethodType.create(methodDesc);
      // case HANDLE_BASE + [1..9]:
         default:
            return readMethodHandle(codeIndex);
      }
   }

   @Nonnull
   final MethodHandle readMethodHandle() {
      int itemIndex = readUnsignedShort();
      int codeIndex = items[itemIndex];
      return readMethodHandle(codeIndex);
   }

   @Nonnull
   final MethodHandle readMethodHandleItem(@Nonnegative int codeIndex) {
      int itemIndex = readUnsignedShort(codeIndex);
      codeIndex = items[itemIndex];
      return readMethodHandle(codeIndex);
   }

   @Nonnull
   private MethodHandle readMethodHandle(@Nonnegative int codeIndex) {
      int tag = readUnsignedByte(codeIndex);

      int classIndex = readItem(codeIndex + 1);
      String owner = readNonnullClass(classIndex);

      int nameIndex = readItem(classIndex + 2);
      String name = readNonnullUTF8(nameIndex);
      String desc = readNonnullUTF8(nameIndex + 2);

      return new MethodHandle(tag, owner, name, desc);
   }

   /**
    * Reads the class name from the constant pool, incrementing {@link #codeIndex} by 2.
    */
   @Nullable
   final String readClass() {
      int itemCodeIndex = readItem();
      String classDesc = readUTF8(itemCodeIndex);
      return classDesc;
   }

   /**
    * Reads a class name constant pool item in {@link #code}.
    *
    * @param codeIndex the start index of an unsigned short value in {@link #code}, whose value is the index of a class
    *                  constant pool item.
    * @return the String corresponding to the specified class item.
    */
   @Nullable
   final String readClass(@Nonnegative int codeIndex) {
      // Computes the start index of the CONSTANT_Class item in code and reads the CONSTANT_Utf8 item designated by the
      // first two bytes of this CONSTANT_Class item.
      int itemCodeIndex = readItem(codeIndex);
      String classDesc = readUTF8(itemCodeIndex);
      return classDesc;
   }

   /**
    * Reads a class descriptor in {@link #code}, incrementing {@link #codeIndex} by 2.
    */
   @Nonnull
   final String readNonnullClass() {
      int itemCodeIndex = readItem();
      String classDesc = readNonnullUTF8(itemCodeIndex);
      return classDesc;
   }

   @Nonnull
   final String readNonnullClass(@Nonnegative int codeIndex) {
      int itemCodeIndex = readItem(codeIndex);
      String classDesc = readNonnullUTF8(itemCodeIndex);
      return classDesc;
   }

   /**
    * Reads an item index in {@link #code}, incrementing {@link #codeIndex} by 2.
    *
    * @return the item at that index in {@link #items}
    */
   @Nonnegative
   final int readItem() {
      int itemIndex = readUnsignedShort();
      return items[itemIndex];
   }

   @Nonnegative
   final int readItem(@Nonnegative int codeIndex) {
      int itemIndex = readUnsignedShort(codeIndex);
      return items[itemIndex];
   }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy