ClassVisitor

ClassVisitor对一个java类的访问是有一定顺序的,其具体顺序如下:

visit [visitSource] [visitModule] [visitNestHost]visitOuterClass (visitNestMember|visitInnerClass|visitField| visitMethod) visitEnd; 说明:visit,visitEnd必须调用一次,[]表示最多调用一次; ()*表示()里面的访问可以按照排列顺序调用多次;

visit

访问类的头部

  1. public void visit(final int version,final int access, final String name, final String signature, final String superName, final String[] interfaces){
  2. if (cv != null) {
  3. cv.visit(version, access, name, signature, superName, interfaces);
  4. }};
  5. 其中version指的是类的版本;
  6. acess指的是类的修饰符;
  7. name类的名称;
  8. signature类的签名,如果类不是泛型或者没有继承泛型类,那么signature为空;
  9. superName类的父类名称;

visitSource

访问类的源码,就是. java 文件, 一般情况用不上;

  1. public void visitSource(final String source, final String debug) {
  2. if (cv != null) {
  3. cv.visitSource(source, debug);
  4. }
  5. }

visitModule

暂时不清楚用来干嘛的,用的比较少;

visitNestHost

访问类的 nest host;

nest 指的一个共享私有成员变量的包名相同的 class 集合,nest 中有一个 host(主类) 和多个 members(成员类),jdk11 为了提供更大,更广泛的嵌套类型,并且为了补足访问控制检测不足,引进了两个新的 class 文件属性,nest host 和 nest member,nest host 中包含了一个 nest members 列表,用来确定其他静态 nest members;nest member 中包含了一个 nest host 属性用来确定它的 nesthost;

visitOuterClass

访问类的外部类,一般用于 nest-class;

visitAnnotation

访问类的注解;

  1. public AnnotationVisitor visitAnnotation(final String descriptor, final boolean visible) {
  2. if (cv != null) {
  3. return cv.visitAnnotation(descriptor, visible);
  4. }
  5. return null;
  6. }
  7. 其中:
  8. descriptor:表示类注解类的描述;
  9. visible表示该注解是否运行时可见;
  10. return AnnotationVisitor:表示该注解类的Visitor,可以用来访问注解值;

visitTypeAnnotation

访问类的签名类型 (某个泛型) 的注解;

  1. public AnnotationVisitor visitTypeAnnotation(
  2. final int typeRef, final TypePath typePath, final String descriptor, final boolean visible) {
  3. if (api < Opcodes.ASM5) {
  4. throw new UnsupportedOperationException("This feature requires ASM5");
  5. }
  6. if (cv != null) {
  7. return cv.visitTypeAnnotation(typeRef, typePath, descriptor, visible);
  8. }
  9. return null;
  10. }
  11. 其中:
  12. typeRef:指的是类型引用,在这里只能是TypeReference.(CLASS_TYPE_PARAMETER |CLASS_TYPE_PARAMETER_BOUND|CLASS_EXTENDS );
  13. typePath:被注解的类型参数,wildcard bound,array element type,包含typeRefstatic inner type;
  14. descriptor: 注解类的描述;
  15. visible:该注解类型运行时是否可见;

visitAttribute

访问类的非标准属性;

public void visitAttribute(final Attribute attribute) {
    if (cv != null) {
      cv.visitAttribute(attribute);
    }
  }

visitNestMember

访问嵌套类的 nest member,只有 host class 被 visited 时才能调用该方法

visitInnerClass

访问一个内部类的信息;

visitField

访问一个类的域信息,如果需要修改或者新增一个域,可以通过重写此方法;

  public FieldVisitor visitField( final int access, final String name, final String descriptor,final String signature,
      final Object value) {
    if (cv != null) {
      return cv.visitField(access, name, descriptor, signature, value);
    }
    return null;
  }
其中
access:表示该域的访问方式,public,private或者static,final等等;
name:指的是域的名称;
descriptro:域的描述,一般指的是该field的参数类型;
signature:指的是域的签名,一般是泛型域才会有签名;
value:指的该域的初始值
reture FiedVisitor:表示将返回一个可以访问该域注解和属性的访问对象,如果不感兴趣的话,可以设置为空;

visitMethod

访问类的方法,如果需要修改类方法信息,则可以重写此方法;

  public MethodVisitor visitMethod( final int access,final String name,final String descriptor,final String signature, final String[] exceptions) {
    if (cv != null) {
      return cv.visitMethod(access, name, descriptor, signature, exceptions);
    }
    return null;
  }
其中:
decsriptor:表示方法的参数类型和返回值类型;

visitEnd

访问类的尾部,只有当类访问结束时,才能调用该方法,同时必须调用该方法;

FieldVisitor

FieldVisitor 也是有一定访问顺序的,其访问顺序为:

(visitAnnotation | visitTypeAnnotation| visitAttribute) visitEnd ()中可以访问多次,而 visitEnd 在访问结束时必须访问一次

visitAnnotation

访问域的注解

public AnnotationVisitor visitAnnotation(final String descriptor, final boolean visible) {
    if (fv != null) {
      return fv.visitAnnotation(descriptor, visible);
    }
    return null;
  }

其中
descriptor表示注解类的描述,即注解类的描述:如“Ljava/lang/JavaBean”等;
visible表示该注解运行时是否可见;

visitTypeAnnotation

访问域的类型上的注解

 public AnnotationVisitor visitTypeAnnotation(
      final int typeRef, final TypePath typePath, final String descriptor, final boolean visible) {
    if (api < Opcodes.ASM5) {
      throw new UnsupportedOperationException("This feature requires ASM5");
    }
    if (fv != null) {
      return fv.visitTypeAnnotation(typeRef, typePath, descriptor, visible);
    }
    return null;
  }

visitAttribtue

访问域属性

visitEnd

域访问完成后必须调用该方法;

MethodVisitor

MethodVisitor 其方法访问顺序如下:

(visitParameter) [ visitAnnotationDefault ] ( visitAnnotation |visitAnnotableParameterCount | visitParameterAnnotation visitTypeAnnotation | visitAttribute ) [ visitCode ( visitFrame | visitInsn | visitLabel | visitInsnAnnotation | visitTryCatchBlock | visitTryCatchAnnotation | visitLocalVariable | visitLocalVariableAnnotation | visitLineNumber ) visitMaxs ] visitEnd ()表示可以调用任意次; []表示里面的方法最多调用一次

visitParameter

访问方法一个参数

 public void visitParameter(final String name, final int access) {
    if (api < Opcodes.ASM5) {
      throw new UnsupportedOperationException(REQUIRES_ASM5);
    }
    if (mv != null) {
      mv.visitParameter(name, access);
    }
  }
name表示参数名称;
access表示参数访问类型如final,synthetick,MANDATED;

visitAnnotationDefualt

访问注解接口方法的默认值

visitAnnotaion

访问方法的一个注解;

visitTypeAnnotation

访问方法签名上的一个类型的注解;

visitAnnotableParameterCount

访问注解参数数量,就是访问方法参数有注解参数个数;

visitParameterAnnotation

访问参数的注解,返回一个 AnnotationVisitor 可以访问该注解值;

visitAttribute

访问方法属性;

visitCode

开始访问方法代码,此处可以添加方法运行前拦截器;

visitFrame

访问方法局部变量的当前状态以及操作栈成员信息,方法栈必须是 expanded 格式或者 compressed 格式, 该方法必须在 visitInsn 方法前调用;

visitIntInsn

访问数值类型指令

public void visitIntInsn(final int opcode, final int operand) {
    if (mv != null) {
      mv.visitIntInsn(opcode, operand);
    }
  }
opcode表示操作码指令,在这里opcode可以是Opcodes.BIPUSH,Opcodes.SIPUSH,Opcodes.NEWARRAY中一个;
operand表示操作数,
如果opcode 为BIPUSH,那么operand value必须在Byte. minValue和Byte.maxValue之间;
如果opcode为SIPUSH,那么operand value必须在Short.minValue和Short.minValue之间;
如果opcode为NEWARRAY,那么operand value 可以取下面中一个:
Opcodes.T_BOOLEN,OPcodes.T_BYTE,OPCODES.T_CHAR,OPcodes.T_SHORT,OPcodes.T_INT,OPcodes.T_FLOAT,Opcodes.T_DOUBLE,Opcodes.T_LONG;

visitVarInsn

访问本地变量类型指令

 public void visitVarInsn(final int opcode, final int var) {
    if (mv != null) {
      mv.visitVarInsn(opcode, var);
    }
  }
var表示需要访问的变量;
opcode:操作码可以是LOAD,STORE,RET中一种;

visitTypeInsn

访问类型指令,类型指令会把类的内部名称当成参数 Type

  public void visitTypeInsn(final int opcode, final String type) {
    if (mv != null) {
      mv.visitTypeInsn(opcode, type);
    }
  }
opcode:操作码为NEW ,ANEWARRAY,CHECKCAST,INSTANCEOF;
type:对象或者数组的内部名称,可以通过Type.getInternalName()获取;

visitFieldInsn

域操作指令,用来加载或者存储对象的 Field;

 public void visitFieldInsn(
      final int opcode, final String owner, final String name, final String descriptor) {
    if (mv != null) {
      mv.visitFieldInsn(opcode, owner, name, descriptor);
    }
  }

visitMethodInsn

访问方法操作指令

 public void visitMethodInsn(
      final int opcode,
      final String owner,
      final String name,
      final String descriptor,
      final boolean isInterface) {
    if (api < Opcodes.ASM5) {
      if (isInterface != (opcode == Opcodes.INVOKEINTERFACE)) {
        throw new IllegalArgumentException("INVOKESPECIAL/STATIC on interfaces requires ASM5");
      }
      visitMethodInsn(opcode, owner, name, descriptor);
      return;
    }
    if (mv != null) {
      mv.visitMethodInsn(opcode, owner, name, descriptor, isInterface);
    }
  }
opcode:为INVOKESPECIAL,INVOKESTATIC,INVOKEVIRTUAL,INVOKEINTERFACE;
owner:方法拥有者的名称;
name:方法名称;
descriptor:方法描述,参数和返回值;
isInterface;是否是接口;

visitDynamicInsn

访问动态类型指令;

visitJumpInsn

访问比较跳转指令;

 public void visitJumpInsn(final int opcode, final Label label) {
    if (mv != null) {
      mv.visitJumpInsn(opcode, label);
    }
  }
opcode: IFEQ,  IFNE, IFLT, IFGE, IFGT, IFLE, IF_ICMPEQ, IF_ICMPNE, IF_ICMPLT, IF_ICMPGE, IF_ICMPGT, IF_ICMPLE, IF_ACMPEQ, IF_ACMPNE, GOTO, JSR, IFNULL or IFNONNULL.
label:跳转目的label;

visitLabelInsn

访问 label,当会在调用该方法后访问该 label 标记一个指令;

visitLdcInsn

访问 ldc 指令,也就是访问常量池索引;

public void visitLdcInsn(final Object value) {
    if (api < Opcodes.ASM5
        && (value instanceof Handle
            || (value instanceof Type && ((Type) value).getSort() == Type.METHOD))) {
      throw new UnsupportedOperationException(REQUIRES_ASM5);
    }
    if (api != Opcodes.ASM7 && value instanceof ConstantDynamic) {
      throw new UnsupportedOperationException("This feature requires ASM7");
    }
    if (mv != null) {
      mv.visitLdcInsn(value);
    }
  }
value:必须是非空的Integer,Float,Double,Long,String,或者对象的Type,Array的Type,Method Sort的Type,或者Method Handle常量中的Handle,或者ConstantDynamic;

visitIincInsn

访问本地变量索引增加指令;

 public void visitIincInsn(final int var, final int increment) {
    if (mv != null) {
      mv.visitIincInsn(var, increment);
    }
  }
var:表示本地变量的索引
  • visitTableSwitchInsn: 访问跳转指令;
  • visitLookupSwitchInsn: 访问查询跳转指令;
  • visitMultiANewArrayInsn: 访问多维数组指令;
  • visitInsnAnnotation: 访问指令注解,必须在访问注解之后调用;
  • visitTryCatchBlock: 方法 try—catch 块;
  • visitTryCatchBAnnotation: 访问 try…catch 块上异常处理的类型注解,必须在调用 visitTryCatchBlock 之后调用;
  • visitLocalVariable: 访问本地变量描述;
 public void visitLocalVariable(
      final String name,
      final String descriptor,
      final String signature,
      final Label start,
      final Label end,
      final int index) {
    if (mv != null) {
      mv.visitLocalVariable(name, descriptor, signature, start, end, index);
    }
  }
name:本地变量名称;
desriptor:本地变量类型描述;
signature:本地变量类型签名;
start:关联该本地变量范围的第一个指令的位置;
end:关联该本地变量范围的最后一个指令的位置:
index:本地变量的索引;
  • visitLineNumber: 访问行号描述;
  • visitMaxs: 访问操作数栈最大值和本地变量表最大值;

AnnotationVisitor

AnnotationVisitor 是用来访问 Annotation 的,AnnotationVisitor 访问顺序如下:

(visit | visitEnum| visitAnnotation | code visitArray) visitEnd. ()可以访问多次,而 visitEnd 只能访问一次;

  • visit: 访问注解的基本值;
  • visitEnum: 访问注解的枚举类型值;
  • visitAnnotation: 访问嵌套注解类型,也就是一个注解可能被其他注解所注释;
  • visitArray: 访问注解的数组值;
  • visitEnd: 访问结束通知;

SignatureVisitor

SignatureVisitor 使用来访问签名的,其访问顺序如下:

类签名 ClassSignature:( visitFormalTypeParameter visitClassBound? visitInterfaceBound ) (visitSuperclass visitInterface ) 方法签名 MethodSignature:( visitFormalTypeParameter visitClassBound? visitInterfaceBound ) ( visitParameterType visitReturnType visitExceptionType ) 域签名 FieldSignature: visitBaseType | visitTypeVariable | visitArrayType | ( visitClassType visitTypeArgument (visitInnerClassType visitTypeArgument ) visitEnd ) )

  • visitFormalTypeParameter(final String name): 访问正规类型参数;
  • visitClassBound:访问最后一个被访问的正规类型参数的类界限;
  • visitInterfaceBound: 访问最后一个被访问的正规类型参数的接口界限;
  • visitSuperclass:访问该类型的超类;
  • visitInterface: 访问该类所实现的接口;
  • visitParameterType: 访问方法参数类型;
  • visitReturnType: 访问方法返回值类型;
  • visitExceptionType: 访问方法异常类型;
  • visitBaseType: 访问基本类型的签名;
  • visitTypeVariable: 访问类型变量的签名;
  • visitArrayType: 访问一个数组类型的签名;
  • visitClassType:开始访问类或者接口类型的签名;
  • visitInnerClassType: 访问内部类;
  • visitTypeArgument: 访问最后被访问类或者接口的无界限类型参数;
  • visitTypeArgument(final char wildcard): 访问最后被访问类或者内部类的类型参数;
  • visitEnd:访问结束通知

ClassNode

ASM中修改生成class主要依赖ClassNode类

public class ClassNode ... {
    public int version;
    public int access;
    public String name;
    public String signature;
    public String superName;
    public List<String> interfaces;
    public String sourceFile;
    public String sourceDebug;
    public String outerClass;
    public String outerMethod;
    public String outerMethodDesc;
    public List<AnnotationNode> visibleAnnotations;
    public List<AnnotationNode> invisibleAnnotations;
    public List<Attribute> attrs;
    public List<InnerClassNode> innerClasses;
    public List<FieldNode> fields;
    public List<MethodNode> methods;
}


MethodNode

public class MethodNode ... {
    public int access;
    public String name;
    public String desc;
    public String signature;
    public List<String> exceptions;
    public List<AnnotationNode> visibleAnnotations;
    public List<AnnotationNode> invisibleAnnotations;
    public List<Attribute> attrs;
    public Object annotationDefault;
    public List<AnnotationNode>[] visibleParameterAnnotations;
    public List<AnnotationNode>[] invisibleParameterAnnotations;
    public InsnList instructions;
    public List<TryCatchBlockNode> tryCatchBlocks;
    public List<LocalVariableNode> localVariables;
    public int maxStack;
    public int maxLocals;
}

这里面的成员基本和ClassNode类似,其中最重要的是instructions对象,它是InsnList类型,是一个关联 AbstractInsnNode 的双链表

InsnList对象可以看作是指令的链表。其主要有以下的特性:

  • 同一个AbstractInsnNode对象最多在链表中出现一次
  • 同一个AbstractInsNode不能同时属于多个InsnList对象
  • 因此,如果将一个AbstractInsNode添加到链表中,需要先从之前的链表中将其移除。

AbstractInsnNode

AbstractInsnNode代表了一条字节码指令,其格式如下:

public abstract class AbstractInsnNode {
    public int getOpcode();
    public int getType();
    public AbstractInsnNode getPrevious();
    public AbstractInsnNode getNext();
    public void accept(MethodVisitor cv);
    public AbstractInsnNode clone(Map labels);
}

其中XxxInsnNode子类和visitXxxInsn 函数相对应。labels和frames以及lineNumbers,尽管它们不属于指令,也使用AbstractInsnNode的子类来表示:LabelNode,FrameNode,LineNumberNode