ASM 是一款广为使用的字节码操作库,很多 Gradle Transform 会用它对编译后的代码进行插桩和修改操作,源码仓库在 GitLab

访问者模式(Visitor pattern)

Visitor pattern represent an operation to be performed on the elements of an object structure. Visitor lets you define a new operation without changing the classes of the elements on which it operates.

访问者模式 是一种将数据结构与数据操作分离的设计模式,它封装一些作用于某种数据结构中的各元素的操作,可以在不改变数据结构的前提下定义作用于这些元素的新的操作,属于行为型模式

访问者模式被称为最复杂的设计模式,并且使用频率不高,设计模式的作者也评价为:大多情况下,你不需要使用访问者模式,但是一旦需要使用它时,那就真的需要使用了,其基本思想是:

访问者模式的核心是解耦数据结构与数据操作,使得对元素的操作具备优秀的扩展性,可以通过扩展不同的数据操作类型(访问者)实现对相同元素集的不同的操作

structure_of_class.png

ASM 概览

整个 ASM 库的核心概念是访问者模式

字节码(.class 文件)是一种固定格式的文件,它的数据结构如下图,对应的解析逻辑定义在 ClassReader,然后提供了 ClassVisitorMethodVisitorFieldVisitorAnnotationVisitor 等各种 Visitor 来访问

ClassVisitor

ClassVisitor 提供了以下的 visitor 方法去访问对应的数据结构,下面以 java.lang.String 为例

public abstract class ClassVisitor {
  public void visit(
      int version,           // 52
      int access,            // 49
      String name,           // java/lang/String
      String signature,      // Ljava/lang/Object;Ljava/io/Serializable;Ljava/lang/Comparable<Ljava/lang/String;>;Ljava/lang/CharSequence;
      String superName,      // java/lang/Object
      String[] interfaces    // java/io/Serializable,java/lang/Comparable,java/lang/CharSequence
  )

  // access:18, name:value, descriptor:[C, signature:null, value:null
  // access:26, name:serialVersionUID, descriptor:J, signature:null, value:-6849794470754667710
  // access:25, name:CASE_INSENSITIVE_ORDER, descriptor:Ljava/util/Comparator;, signature:Ljava/util/Comparator<Ljava/lang/String;>;, value:null
  // ...
  public FieldVisitor visitField(int access, String name, String descriptor, String signature, Object value)

  // access:1, name:<init>, descriptor:(Ljava/lang/String;)V, signature:null, exceptions:null
  // access:1, name:<init>, descriptor:([BIILjava/lang/String;)V, signature:null, exceptions:java/io/UnsupportedEncodingException
  // access:1, name:getBytes, descriptor:(Ljava/lang/String;)[B, signature:null, exceptions:java/io/UnsupportedEncodingException
  // access:9, name:valueOf, descriptor:(Ljava/lang/Object;)Ljava/lang/String;, signature:null, exceptions:null
  // ...
  public MethodVisitor visitMethod(int access, String name, String descriptor, String signature, String[] exceptions)

  // name:java/lang/String$CaseInsensitiveComparator, outerName:java/lang/String, innerName:CaseInsensitiveComparator, access:10
  public void visitInnerClass(String name, String outerName, String innerName, int access)

  public void visitSource(String source, String debug)
  public ModuleVisitor visitModule(String name, int access, String version)
  public void visitNestHost(String nestHost)
  public void visitOuterClass(String owner, String name, String descriptor)
  public AnnotationVisitor visitAnnotation(String descriptor, boolean visible)
  public AnnotationVisitor visitTypeAnnotation(int typeRef, TypePath typePath, String descriptor, boolean visible)
  public void visitAttribute(Attribute attribute)
  public void visitNestMember(String nestMember)
  public void visitPermittedSubclass(String permittedSubclass)
  public RecordComponentVisitor visitRecordComponent(String name, String descriptor, String signature)
  public void visitEnd()
}

描述符(descriptor)的规则和例子如下图:

type_descriptors.png

sample_of_method_descriptors.png

interface Opcodes {

  // Java ClassFile versions (the minor version is stored in the 16 most significant bits, and the
  // major version in the 16 least significant bits).

  int V1_1 = 3 << 16 | 45;
  int V1_2 = 0 << 16 | 46;
  int V1_3 = 0 << 16 | 47;
  int V1_4 = 0 << 16 | 48;
  int V1_5 = 0 << 16 | 49;
  int V1_6 = 0 << 16 | 50;
  int V1_7 = 0 << 16 | 51;
  int V1_8 = 0 << 16 | 52;
  int V9 = 0 << 16 | 53;
  int V10 = 0 << 16 | 54;
  int V11 = 0 << 16 | 55;
  int V12 = 0 << 16 | 56;
  int V13 = 0 << 16 | 57;
  int V14 = 0 << 16 | 58;
  int V15 = 0 << 16 | 59;
  int V16 = 0 << 16 | 60;
  int V17 = 0 << 16 | 61;
  int V18 = 0 << 16 | 62;
  int V19 = 0 << 16 | 63;

  /**
   * Version flag indicating that the class is using 'preview' features.
   *
   * <p>{@code version & V_PREVIEW == V_PREVIEW} tests if a version is flagged with {@code
   * V_PREVIEW}.
   */
  int V_PREVIEW = 0xFFFF0000;
}

version 是 class 文件格式的版本号,有如下版本号的定义:

interface Opcodes {
  // Access flags values, defined in
  // - <https://docs.oracle.com/javase/specs/jvms/se9/html/jvms-4.html#jvms-4.1-200-E.1>
  // - <https://docs.oracle.com/javase/specs/jvms/se9/html/jvms-4.html#jvms-4.5-200-A.1>
  // - <https://docs.oracle.com/javase/specs/jvms/se9/html/jvms-4.html#jvms-4.6-200-A.1>
  // - <https://docs.oracle.com/javase/specs/jvms/se9/html/jvms-4.html#jvms-4.7.25>

  int ACC_PUBLIC = 0x0001; // class, field, method
  int ACC_PRIVATE = 0x0002; // class, field, method
  int ACC_PROTECTED = 0x0004; // class, field, method
  int ACC_STATIC = 0x0008; // field, method
  int ACC_FINAL = 0x0010; // class, field, method, parameter
  int ACC_SUPER = 0x0020; // class
  int ACC_SYNCHRONIZED = 0x0020; // method
  int ACC_OPEN = 0x0020; // module
  int ACC_TRANSITIVE = 0x0020; // module requires
  int ACC_VOLATILE = 0x0040; // field
  int ACC_BRIDGE = 0x0040; // method
  int ACC_STATIC_PHASE = 0x0040; // module requires
  int ACC_VARARGS = 0x0080; // method
  int ACC_TRANSIENT = 0x0080; // field
  int ACC_NATIVE = 0x0100; // method
  int ACC_INTERFACE = 0x0200; // class
  int ACC_ABSTRACT = 0x0400; // class, method
  int ACC_STRICT = 0x0800; // method
  int ACC_SYNTHETIC = 0x1000; // class, field, method, parameter, module *
  int ACC_ANNOTATION = 0x2000; // class
  int ACC_ENUM = 0x4000; // class(?) field inner
  int ACC_MANDATED = 0x8000; // field, method, parameter, module, module *
  int ACC_MODULE = 0x8000; // class
}

access 是修饰符控制位的组合,有以下控制位:

MethodVisitor