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

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

package mockit.asm.util;

import javax.annotation.*;

import mockit.asm.types.*;
import static mockit.asm.jvmConstants.ConstantPoolTypes.*;

public class BytecodeReader
{
   /**
    * The class to be parsed. The content of this array must not be modified.
    */
   @Nonnull public 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 public 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 public int codeIndex;

   protected 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 CONDY: 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
      }
   }

   protected 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.
    */
   public final int readUnsignedByte() {
      return code[codeIndex++] & 0xFF;
   }

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

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

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

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

   /**
    * Reads an unsigned short value in {@link #code}, incrementing {@link #codeIndex} by 2.
    */
   @Nonnegative
   public 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 u2CodeIndex the start index of the value to be read in {@link #code}.
    * @return the read value.
    */
   @Nonnegative
   protected final int readUnsignedShort(@Nonnegative int u2CodeIndex) {
      byte[] b = code;
      return ((b[u2CodeIndex] & 0xFF) << 8) | (b[u2CodeIndex + 1] & 0xFF);
   }

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

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

   /**
    * Reads a signed int value in {@link #code}, incrementing {@link #codeIndex} by 4.
    */
   public 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 s4CodeIndex the start index of the value to be read in {@link #code}.
    * @return the read value.
    */
   protected final int readInt(@Nonnegative int s4CodeIndex) {
      byte[] b = code;
      return
         ((b[s4CodeIndex] & 0xFF) << 24) | ((b[s4CodeIndex + 1] & 0xFF) << 16) |
         ((b[s4CodeIndex + 2] & 0xFF) << 8) | (b[s4CodeIndex + 3] & 0xFF);
   }

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

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

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

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

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

   protected final float readFloat(@Nonnegative int s4CodeIndex) {
      int bits = readInt(s4CodeIndex);
      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 @SuppressWarnings("CharUsedInArithmeticContext")
   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;
      @SuppressWarnings("QuestionableName") char cc = 0;

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

         if (st == 0) {
            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 UTF8 item, or null if {@link #codeIndex} points to an item whose value is zero.
    */
   @Nullable
   protected 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 u2CodeIndex the 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 UTF8 item, or null if index is zero or points to an item whose value is zero.
    */
   @Nullable
   protected final String readUTF8(@Nonnegative int u2CodeIndex) {
      if (u2CodeIndex == 0) {
         return null;
      }

      int itemIndex = readUnsignedShort(u2CodeIndex);

      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
   public 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
   public final String readNonnullUTF8(@Nonnegative int u2CodeIndex) {
      int itemIndex = readUnsignedShort(u2CodeIndex);
      return readString(itemIndex);
   }

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

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

      String newString = readUTF(itemIndex);
      strings[itemIndex] = newString;
      return newString;
   }

   /**
    * 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
   public final Object readConstItem() {
      int constIndex = readUnsignedShort();
      Object cst = readConst(constIndex);
      return cst;
   }

   @Nonnull
   protected final Object readConstItem(@Nonnegative int u2CodeIndex) {
      int itemIndex = readUnsignedShort(u2CodeIndex);
      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
   protected final Object readConst(@Nonnegative int itemIndex) {
      int constCodeIndex = items[itemIndex];
      byte itemType = code[constCodeIndex - 1];

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

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

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

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

      int classIndex = readItem(bsmCodeIndex + 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
   protected final String readClass() {
      int itemCodeIndex = readItem();
      String classDesc = readUTF8(itemCodeIndex);
      return classDesc;
   }

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

   @Nonnull
   public final String readNonnullClass(@Nonnegative int u2CodeIndex) {
      int itemCodeIndex = readItem(u2CodeIndex);
      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
   public final int readItem() {
      int itemIndex = readUnsignedShort();
      return items[itemIndex];
   }

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




© 2015 - 2024 Weber Informatics LLC | Privacy Policy