ASM 字节码框架源码解析

ClassReader 解析

2019-02-06  本文已影响5人  Xcdf

1. ClassReader的字段

  public final byte[] b;

   包含了要解析的Class字节数组,该数组内容不能被修改。此字段适用于Attribute子类,通常不需要类访问者。需要注意的是:Class文件可以从该数组中的任何偏移量开始,即它不必从偏移地址0开始,使用getItem()方法和header属性来获取字节码在此数组中的偏移量。

  private final int[] cpInfoOffsets;

   在常量池中的第i个常量的偏移地址为cpInfoOffsets [i] -1,那么第i个常量的tag值为b[cpInfoOffsets [i] -1]。

  private final String[] constantUtf8Values;
  private final ConstantDynamic[] constantDynamicValues;

   010101010...01011100---> "abxsc" 。设定缓存的目的是:避免了对给定CONSTANT_Utf8,CONSTANT_Dynamic常量池项的多次解析。

  private final int[] bootstrapMethodOffsets;

   bootstrap_methods数组的每个元素的在b[]的起始偏移量。

  private final int maxStringLength;

   保守估计类的常量池中包含的字符串的最大长度。

  public final int header;

  ClassFile的access_flags字段在b[]中的字节偏移量。

  在构建ClassReader实例时,它首先保存字节码二进制数组为byte[] b

2. ClassReader 的构造器

   有4个重载的构造器,目的是初始化上述字段。
1.读取的二进制字节数组byte[] classFile。

  public ClassReader(final byte[] classFile) {
    this(classFile, 0, classFile.length);
  }

2.包含了字节码的数组classFileBuffer,字节码数组的由偏移量classFileOffset和字节码长度classFileLength决定。

  public ClassReader(byte[] classFileBuffer,int classFileOffset,  int classFileLength ) { 
    this(classFileBuffer, classFileOffset, true);
  }

3.使用InputStream或类加载器来获取类的二进制字节码。

  public ClassReader(final InputStream inputStream) throws IOException {
    this(readStream(inputStream, false));
  }
  public ClassReader(final String className) throws IOException {
    this(readStream(ClassLoader.getSystemResourceAsStream(
                        className.replace('.', '/') + ".class"), true));
  }

4.所有的构造器都会调用如下构造器:
  创建一个新的构造器,并且该构造器的不应该暴露为public。
classFileBuffer为包含字节码的数组;classFileOffset为字节码的第一个字节在b[]中的偏移量 ;checkClassVersion 是否检查Class文件的版本号。

ClassReader(
   final byte[] classFileBuffer, 
   final int classFileOffset,
   final boolean checkClassVersion) {
    b = classFileBuffer;
    // 检查类的主版本号(major version)
    if (checkClassVersion && readShort(classFileOffset + 6) > Opcodes.V12) {
      throw new IllegalArgumentException(
          "Unsupported class file major version " + readShort(classFileOffset + 6));
    }
    // 创建常量池数组
    int constantPoolCount = readUnsignedShort(classFileOffset + 8);// 常量池大小
    cpInfoOffsets = new int[constantPoolCount];// 创建一个常量池数组
    constantUtf8Values = new String[constantPoolCount];// 创建一个 字符串数组
    // 其他条目的偏移量取决于所有先前条目的总大小
    int currentCpInfoIndex = 1;
    int currentCpInfoOffset = classFileOffset + 10;
    int currentMaxStringLength = 0;
    boolean hasConstantDynamic = false;
    boolean hasConstantInvokeDynamic = false;

    while (currentCpInfoIndex < constantPoolCount) {
      cpInfoOffsets[currentCpInfoIndex++] = currentCpInfoOffset + 1;
      int cpInfoSize;
      // 不同类型的常量的所占的字节码不同
      switch (classFileBuffer[currentCpInfoOffset]) {
        //...
        case Symbol.CONSTANT_LONG_TAG:
          cpInfoSize = 9;
          currentCpInfoIndex++;
          break;
        case Symbol.CONSTANT_METHOD_HANDLE_TAG:
          cpInfoSize = 4;
          break;
        case Symbol.CONSTANT_STRING_TAG:
          cpInfoSize = 3;
          break;
        default:
          throw new IllegalArgumentException();
      }
      currentCpInfoOffset += cpInfoSize;
    }
    maxStringLength = currentMaxStringLength;
    // The Classfile's access_flags field is just after the last constant pool entry.
    header = currentCpInfoOffset;

    // Allocate the cache of ConstantDynamic values, if there is at least one.
    constantDynamicValues = hasConstantDynamic ? new ConstantDynamic[constantPoolCount] : null;

    // Read the BootstrapMethods attribute, if any (only get the offset of each method).
    bootstrapMethodOffsets =
        (hasConstantDynamic | hasConstantInvokeDynamic)
            ? readBootstrapMethodsAttribute(currentMaxStringLength)
            : null;
  }

3.获取类、父类和接口的名称

  // 类的访问标识
  public int getAccess() {
    return readUnsignedShort(header);
  }
  // 类的名称
  public String getClassName() {
    return readClass(header + 2, new char[maxStringLength]);
  }
  // 获取父类名称
  public String getSuperName() {
    return readClass(header + 4, new char[maxStringLength]);
  }
  // 获取实现的接口名称
  public String[] getInterfaces() {
    int currentOffset = header + 6;
    int interfacesCount = readUnsignedShort(currentOffset);
    String[] interfaces = new String[interfacesCount];
    if (interfacesCount > 0) {
      char[] charBuffer = new char[maxStringLength];
      for (int i = 0; i < interfacesCount; ++i) {
        currentOffset += 2;
        interfaces[i] = readClass(currentOffset, charBuffer);
      }
    }
    return interfaces;
  }

4. 公有方法

  在调用ClassReader的accept方法时,它解析字节码中常量池之后的所有元素。

  // 使得给定的访问器访问JVM字节码结构, classVisitor是类的访问者 
  public void accept(final ClassVisitor classVisitor, final int parsingOptions) {
    accept(classVisitor, new Attribute[0], parsingOptions);
  }

  public void accept(
      final ClassVisitor classVisitor,
      final Attribute[] attributePrototypes,
      final int parsingOptions) {
    Context context = new Context();
    context.attributePrototypes = attributePrototypes;
    context.parsingOptions = parsingOptions;
    context.charBuffer = new char[maxStringLength];

    // Read the access_flags, this_class, super_class, interface_count and interfaces fields.
    char[] charBuffer = context.charBuffer;
    int currentOffset = header;
    int accessFlags = readUnsignedShort(currentOffset);
    String thisClass = readClass(currentOffset + 2, charBuffer);
    String superClass = readClass(currentOffset + 4, charBuffer);
    String[] interfaces = new String[readUnsignedShort(currentOffset + 6)];
    currentOffset += 8;
    for (int i = 0; i < interfaces.length; ++i) {
      interfaces[i] = readClass(currentOffset, charBuffer);
      currentOffset += 2;
    }
    // Visit .... 

    // Visit the class declaration. The minor_version and major_version fields start 6 bytes before
    // the first constant pool entry, which itself starts at cpInfoOffsets[1] - 1 (by definition).
    classVisitor.visit(
        readInt(cpInfoOffsets[1] - 7), accessFlags, thisClass, signature, superClass, interfaces);

    // Visit the InnerClasses attribute.
    if (innerClassesOffset != 0) {
      int numberOfClasses = readUnsignedShort(innerClassesOffset);
      int currentClassesOffset = innerClassesOffset + 2;
      while (numberOfClasses-- > 0) {
        classVisitor.visitInnerClass(
            readClass(currentClassesOffset, charBuffer),
            readClass(currentClassesOffset + 2, charBuffer),
            readUTF8(currentClassesOffset + 4, charBuffer),
            readUnsignedShort(currentClassesOffset + 6));
        currentClassesOffset += 8;
      }
    }

    // Visit the fields and methods.
    int fieldsCount = readUnsignedShort(currentOffset);
    currentOffset += 2;
    while (fieldsCount-- > 0) {
      currentOffset = readField(classVisitor, context, currentOffset);
    }
    int methodsCount = readUnsignedShort(currentOffset);
    currentOffset += 2;
    while (methodsCount-- > 0) {
      currentOffset = readMethod(classVisitor, context, currentOffset);
    }

    // Visit the end of the class.
    classVisitor.visitEnd();
  }

5.私有方法

读取代码块、属性和方法


读取代码块、属性和方法 读取注解 image.png image.png image.png
上一篇下一篇

猜你喜欢

热点阅读