ClassVisitor
ClassVisitor对一个java类的访问是有一定顺序的,其具体顺序如下:
visit [visitSource] [visitModule] [visitNestHost]visitOuterClass (visitNestMember|visitInnerClass|visitField| visitMethod) visitEnd; 说明:visit,visitEnd必须调用一次,[]表示最多调用一次; ()*表示()里面的访问可以按照排列顺序调用多次;
visit
访问类的头部
public void visit(final int version,final int access, final String name, final String signature, final String superName, final String[] interfaces){
if (cv != null) {
cv.visit(version, access, name, signature, superName, interfaces);
}};
其中version指的是类的版本;
acess指的是类的修饰符;
name类的名称;
signature类的签名,如果类不是泛型或者没有继承泛型类,那么signature为空;
superName类的父类名称;
visitSource
访问类的源码,就是. java 文件, 一般情况用不上;
public void visitSource(final String source, final String debug) {
if (cv != null) {
cv.visitSource(source, debug);
}
}
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
访问类的注解;
public AnnotationVisitor visitAnnotation(final String descriptor, final boolean visible) {
if (cv != null) {
return cv.visitAnnotation(descriptor, visible);
}
return null;
}
其中:
descriptor:表示类注解类的描述;
visible表示该注解是否运行时可见;
return AnnotationVisitor:表示该注解类的Visitor,可以用来访问注解值;
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 (cv != null) {
return cv.visitTypeAnnotation(typeRef, typePath, descriptor, visible);
}
return null;
}
其中:
typeRef:指的是类型引用,在这里只能是TypeReference.(CLASS_TYPE_PARAMETER |CLASS_TYPE_PARAMETER_BOUND|CLASS_EXTENDS );
typePath:被注解的类型参数,wildcard bound,array element type,包含typeRef的static inner type;
descriptor: 注解类的描述;
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