ASM使用小抄

0x00

参考:ASM教程

0x01 Core Api

生成新的类

核心接口是ClassVisitor,其中方法调用的顺序为

visit
[visitSource][visitModule][visitNestHost][visitPermittedSubclass][visitOuterClass]
(
 visitAnnotation |
 visitTypeAnnotation |
 visitAttribute
)*
(
 visitNestMember |
 visitInnerClass |
 visitRecordComponent |
 visitField |
 visitMethod
)* 
visitEnd

ClassWrite继承于ClassVisitor,ClassWrite最重要的是在构造方法时提供的参数
在创建ClassWriter对象的时候,要指定一个flags参数,它可以选择的值有三个
第一个,可以选取的值是0。ASM不会自动计算max stacks和max locals,也不会自动计算stack map frames。
第二个,可以选取的值是ClassWriter.COMPUTE_MAXS。ASM会自动计算max stacks和max locals,但不会自动计算stack map frames。
第三个,可以选取的值是ClassWriter.COMPUTE_FRAMES(推荐使用)。ASM会自动计算max stacks和max locals,也会自动计算stack map frames。

ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_FRAMES);

生成接口

public interface HelloWorld {
}
// (1) 创建ClassWriter对象
ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_FRAMES);

// (2) 调用visitXxx()方法
cw.visit(
        V1_8,                                        // version
        ACC_PUBLIC + ACC_ABSTRACT + ACC_INTERFACE,   // access
        "sample/HelloWorld",                         // name
        null,                                        // signature
        "java/lang/Object",                          // superName
        null                                         // interfaces
);

cw.visitEnd();

// (3) 调用toByteArray()方法
return cw.toByteArray();

生成接口+字段+方法

public interface HelloWorld extends Cloneable {
    int LESS = -1;
    int EQUAL = 0;
    int GREATER = 1;
    int compareTo(Object o);
}
// (1) 创建ClassWriter对象
ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_FRAMES);

// (2) 调用visitXxx()方法
cw.visit(V1_8, ACC_PUBLIC + ACC_ABSTRACT + ACC_INTERFACE, "sample/HelloWorld",
        null, "java/lang/Object", new String[]{"java/lang/Cloneable"});

{
    FieldVisitor fv1 = cw.visitField(ACC_PUBLIC + ACC_FINAL + ACC_STATIC, "LESS", "I", null, -1);
    fv1.visitEnd();
}

{
    FieldVisitor fv2 = cw.visitField(ACC_PUBLIC + ACC_FINAL + ACC_STATIC, "EQUAL", "I", null, 0);
    fv2.visitEnd();
}

{
    FieldVisitor fv3 = cw.visitField(ACC_PUBLIC + ACC_FINAL + ACC_STATIC, "GREATER", "I", null, 1);
    fv3.visitEnd();
}

{
    MethodVisitor mv1 = cw.visitMethod(ACC_PUBLIC + ACC_ABSTRACT, "compareTo", "(Ljava/lang/Object;)I", null, null);
    mv1.visitEnd();
}


cw.visitEnd();

// (3) 调用toByteArray()方法
return cw.toByteArray();

生成类

public class HelloWorld {
}
// (1) 创建ClassWriter对象
ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_FRAMES);

// (2) 调用visitXxx()方法
cw.visit(V1_8, ACC_PUBLIC + ACC_SUPER, "sample/HelloWorld",
        null, "java/lang/Object", null);

// 创建默认的构造方法
MethodVisitor mv = cw.visitMethod(ACC_PUBLIC, "<init>", "()V", null, null);
mv.visitCode();
// 构造方法中调用超类的构造方法
mv.visitVarInsn(ALOAD, 0);
mv.visitMethodInsn(INVOKESPECIAL, "java/lang/Object", "<init>", "()V", false);
mv.visitInsn(RETURN);
mv.visitMaxs(1, 1);
mv.visitEnd();

cw.visitEnd();

// (3) 调用toByteArray()方法
return cw.toByteArray();

生成含有注解的字段

// 希望生成HelloWorld接口
public interface HelloWorld {
    @MyTag(name = "tomcat", age = 10)
    int intValue = 100;
}

public @interface MyTag {
    String name();
    int age();
}
// (1) 创建ClassWriter对象
ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_FRAMES);

// (2) 调用visitXxx()方法
cw.visit(V1_8, ACC_PUBLIC | ACC_ABSTRACT | ACC_INTERFACE, "sample/HelloWorld", null, "java/lang/Object", null);

{
    FieldVisitor fv1 = cw.visitField(ACC_PUBLIC | ACC_FINAL | ACC_STATIC, "intValue", "I", null, 100);

    {
        AnnotationVisitor anno = fv1.visitAnnotation("Lsample/MyTag;", false);
        anno.visit("name", "tomcat");
        anno.visit("age", 10);
        anno.visitEnd();
    }

    fv1.visitEnd();
}

cw.visitEnd();

// (3) 调用toByteArray()方法
return cw.toByteArray();

MethodVisitor是另一个重要的接口,其提供了众多的方法用于设置方法体中指令
其定义方法的调用顺序如下

(visitParameter)*
[visitAnnotationDefault]
(visitAnnotation | visitAnnotableParameterCount | visitParameterAnnotation | visitTypeAnnotation | visitAttribute)*
[
    visitCode
    (
        visitFrame |
        visitXxxInsn |
        visitLabel |
        visitInsnAnnotation |
        visitTryCatchBlock |
        visitTryCatchAnnotation |
        visitLocalVariable |
        visitLocalVariableAnnotation |
        visitLineNumber
    )*
    visitMaxs
]
visitEnd

我们可以把这些visitXxx()方法分成三组:

  • 第一组,在visitCode()方法之前的方法。这一组的方法,主要负责parameter、annotation和attributes等内容,这些内容并不是方法当中“必不可少”的一部分;在当前课程当中,我们暂时不去考虑这些内容,可以忽略这一组方法。
  • 第二组,在visitCode()方法和visitMaxs()方法之间的方法。这一组的方法,主要负责当前方法的“方法体”内的opcode内容。其中,visitCode()方法,标志着方法体的开始,而visitMaxs()方法,标志着方法体的结束。
  • 第三组,是visitEnd()方法。这个visitEnd()方法,是最后一个进行调用的方法。

对这些visitXxx()方法进行精简之后,内容如下:

[
    visitCode
    (
        visitFrame |
        visitXxxInsn |
        visitLabel |
        visitTryCatchBlock
    )*
    visitMaxs
]
visitEnd

这些方法的调用顺序,可以记忆如下:

  • 第一步,调用visitCode()方法,调用一次。
  • 第二步,调用visitXxxInsn()方法,可以调用多次。对这些方法的调用,就是在构建方法的“方法体”。
  • 第三步,调用visitMaxs()方法,调用一次。
  • 第四步,调用visitEnd()方法,调用一次。

遇到的坑,修改类成员变量

在这里插入图片描述

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值