ASM架构

ASM是围绕事件生产者(class parser)、事件消费者(class writer)和各种预定义的事件过滤器的架构。架构图如下所示:
Pasted image 20221116144652

导入ASM库

1
2
3
4
5
6
dependencies {   
//ASM相关依赖
implementation 'org.ow2.asm:asm:9.4'
implementation 'org.ow2.asm:asm-commons:9.4'
implementation 'org.ow2.asm:asm-util:9.4'
}

Class结构

简述

类源码与已编译类之间是有许多不同之处的:

  • 已编译类只能描述一个类,当源码包含几个类,例如一个源码包含一个类,其中还有一个内部类。它们会被编译成两个类文件:一个主类,一个内部类。主类文件包含它的内部类的引用,并且定义在方法内部的内部类包含对其闭包函数的引用。
  • 已编译类不包含注释。
  • 已编译类不包含packageimport模块,因此所有的类型名字都必须是全量的。
    类结构的描述:
    Pasted image 20221116151116

Class内部描述

类型描述

Java类型 类型描述
boolean Z
char C
byte B
short S
int I
float F
long J
double D
Object Ljava/lang/Object;
int[] [I
Object[][] [[Ljava/lang/Object;

方法描述

方法源码 方法描述
void m(int i, float f) (IF)V
int m(Object o) (Ljava/lang/Object;)I
int[] m(int i, String s) (ILjava/lang/String;)[I
Object m(int[] i) ([I)Ljava/lang/Object;

ClassVisitor

ClassVisitor类结构

1
2
3
4
5
6
7
8
9
10
11
12
13
public abstract class ClassVisitor { 
public ClassVisitor(int api);
public ClassVisitor(int api, ClassVisitor cv);
public void visit(int version, int access, String name, String signature, String superName, String[] interfaces);
public void visitSource(String source, String debug);
public void visitOuterClass(String owner, String name, String desc);
AnnotationVisitor visitAnnotation(String desc, boolean visible);
public void visitAttribute(Attribute attr);
public void visitInnerClass(String name, String outerName, String innerName, int access);
public FieldVisitor visitField(int access, String name, String desc, String signature, Object value);
public MethodVisitor visitMethod(int access, String name, String desc, String signature, String[] exceptions);
void visitEnd();
}

ClassVisitor类方法的调用必须随着下面的顺序:

1
visit visitSource? visitOuterClass? ( visitAnnotation | visitAttribute )* (visitInnerClass | visitField | visitMethod )* visitEnd

FieldVisitor类结构

1
2
3
4
5
6
7
public abstract class FieldVisitor { 
public FieldVisitor(int api);
public FieldVisitor(int api, FieldVisitor fv);
public AnnotationVisitor visitAnnotation(String desc, boolean visible);
public void visitAttribute(Attribute attr);
public void visitEnd();
}

解析类

ClassReader能够解析存在的class,接下来一个例子是一个ClassPrinter用来打印访问类的信息。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
class ClassPrinter: ClassVisitor(Opcodes.ASM5) {  
override fun visit(
version: Int,
access: Int,
name: String?,
signature: String?,
superName: String?,
interfaces: Array<out String>?
) {
println("$name extends $superName {")
}

override fun visitField(
access: Int,
name: String?,
descriptor: String?,
signature: String?,
value: Any?
): FieldVisitor? {
println(" $descriptor $name")
return null;
}

override fun visitMethod(
access: Int,
name: String?,
descriptor: String?,
signature: String?,
exceptions: Array<out String>?
): MethodVisitor? {
println(" $name $descriptor")
return null
}

override fun visitEnd() {
println("}")
}
}

然后将ClassReaderClassPrinter结合起来。

1
2
3
val cp = ClassPrinter();  
val cr = ClassReader("java.lang.Runnable")
cr.accept(cp, 0)

整个事件从accept()方法产生,然后依次调用ClassPrinter中的方法,并打印相关信息。

生成类

通过ClassWriter可以生成类,看以下的接口,我们将通过ClassWriter生成这样一个类:

1
2
3
4
5
6
public interface Comparable extends Object { 
int LESS = -1;
int EQUAL = 0;
int GREATER = 1;
int compareTo(Object o);
}

生成这样一个接口只需要调用ClassVisitor六个方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
val cw = ClassWriter(0)
cw.visit(
V1_8,
ACC_PUBLIC + ACC_ABSTRACT + ACC_INTERFACE,
"com/bravestsnail/router/asm/Comparable",
null, "java.lang.Object", arrayOf()
)
cw.visitField(ACC_PUBLIC + ACC_FINAL + ACC_STATIC, "LESS", "I", null, -1).visitEnd()
cw.visitField(ACC_PUBLIC + ACC_FINAL + ACC_STATIC, "EQUAL", "I", null, 0).visitEnd()
cw.visitField(ACC_PUBLIC + ACC_FINAL + ACC_STATIC, "GREATER", "I", null, 1).visitEnd()
cw.visitMethod(ACC_PUBLIC + ACC_ABSTRACT, "compareTo", "(Ljava/lang/object;)I", null, null).visitEnd()
cw.visitEnd()
val b = cw.toByteArray()

第二行代码代表的意义:
定义一个com/bravestsnail/router/asm/Comparable,其java版本1.8,修饰符为public+abstract+interface代表是一个接口,其没有泛型,父类为java.lang.Object, 接口列表为空。
接下来三行定义了三个静态常量,调用visitEnd()是代表立即结束FieldVisitor的访问。
接下来一行定义了compareTo抽象方法。

转换类

看以下转换类的简单示例:

1
2
3
4
5
6
byte[] b1 = ...; ClassWriter cw = new ClassWriter(0); 
// cv forwards all events to cw
ClassVisitor cv = new ClassVisitor(ASM4, cw) { };
ClassReader cr = new ClassReader(b1);
cr.accept(cv, 0);
byte[] b2 = cw.toByteArray(); // b2 represents the same class as b1

Pasted image 20221116202306
上面是转换链,由Reader产生事件,Adapter对这些进行转换,最终转换的结果在Writer重建。
接下来看一个转换java版本的Adapter:

1
2
3
4
5
6
7
8
public class ChangeVersionAdapter extends ClassVisitor { 
public ChangeVersionAdapter(ClassVisitor cv) { super(ASM4, cv); }

@Override public void visit(int version, int access, String name, String
signature, String superName, String[] interfaces) {
cv.visit(V1_5, access, name, signature, superName, interfaces);
}
}

上面的类在visit()方法中对该事件进行了转发,并且修改java版本为1.5。
看看整个过程的时序图:
Pasted image 20221116203123

优化

ClassReaderClassWriter互相持有对方的引用,将会执行一项优化,直接复制ClassReader中不需要转换的字节数组数据到ClassWriter

移除类成员

1
2
3
4
5
6
7
8
9
10
11
12
13
14
class RemoveDebugAdapter(cv: ClassVisitor?) : ClassVisitor(ASM4, cv) {  
fun visitSource(source: String?, debug: String?) {}
fun visitOuterClass(owner: String?, name: String?, desc: String?) {}
fun visitInnerClass(
name: String?, outerName: String?,
innerName: String?, access: Int
) {}
fun visitMethod(
access: Int, name: String,
desc: String, signature: String?, exceptions: Array<String?>?
): MethodVisitor? {
return null
}
}

移除类成员可以通过不转发相应事件或者返回null。

添加类成员

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
class AddFieldAdapter(  
cv: ClassVisitor?, private val fAcc: Int, private val fName: String,
private val fDesc: String
) : ClassVisitor(ASM4, cv) {
private var isFieldPresent = false
fun visitField(
access: Int, name: String, desc: String?,
signature: String?, value: Any?
): FieldVisitor {
if (name == fName) {
isFieldPresent = true
}
return cv.visitField(access, name, desc, signature, value)
}

fun visitEnd() {
if (!isFieldPresent) {
val fv: FieldVisitor = cv.visitField(fAcc, fName, fDesc, null, null)
if (fv != null) {
fv.visitEnd()
}
}
cv.visitEnd()
}
}

类分析工具

TraceClassVisitor

追踪打印类结构

CheckClassAdapter

检查生成的类

ASMifier

将class文件转换成ASM代码

Method结构