2. CheckMethodAdapter
同ClassVisitor一样,MethodVisitor本身也不会检查调用顺序是否适当,所以对于Method的操作,ASM提供了CheckMethodAdapter用于检测方法调用顺序是否在正确。使用方式是将MethodVisitor对象委托给CheckMethodAdapter。
示例代码:
https://github.com/dengshiwei/asm-module/blob/master/asm_example/src/main/java/com/andoter/asm_example/part3/CheckMethodDemo.kt
funmain{
valclassReader = ClassReader( "com.andoter.asm_example.part3.MyMethodAdapter")
valclassWriter = ClassWriter(classReader, ClassWriter.COMPUTE_MAXS)
valclassVisitor = object: ClassVisitor(Opcodes.ASM7, classWriter) {
overridefunvisitMethod(
access: Int,
name: String?,
deor: String?,
signature: String?,
exceptions: Array< outString>?
) : MethodVisitor {
varmethodVisitor = cv.visitMethod(access, name, deor, signature, exceptions)
// 将 MethodVisitor 委托给 CheckMethodAdapter
methodVisitor = CheckMethodAdapter(methodVisitor)
returnMyMethodAdapter(methodVisitor)
}
}
classReader.accept(classVisitor, ClassReader.SKIP_DEBUG)
}
3. LocalVariablesSorter
该工具类将局部变量按照他们出现的顺序进行重新编号,同时可以很方便的使用newLocal方法创建一个新的局部变量。
比如对于一个有两个参数的方法,插入一个新的局部变量时,新的局部变量索引就是 3。所以这个方法对于在方法中插入局部变量很有用。
示例代码:
https://github.com/dengshiwei/asm-module/blob/master/asm_example/src/main/java/com/andoter/asm_example/part3/AddTimerAdapter4.kt
classAddTimerMethodAdapter4(
privatevarowner: String,
access: Int,
deor: String?,
methodVisitor: MethodVisitor?
) : LocalVariablesSorter(Opcodes.ASM7, access, deor, methodVisitor ){
privatevartime: Int= -1
overridefunvisitCode{
super.visitCode
mv.visitMethodInsn(Opcodes.INVOKESTATIC, "java/lang/System", "currentTimeMillis", "J", false)
time = newLocal(Type.LONG_TYPE) // 新建一个局部变量
mv.visitVarInsn(Opcodes.LSTORE, time)
}
overridefunvisitInsn(opcode: Int){
if(opcode >= Opcodes.IRETURN && opcode <= Opcodes.RETURN || Opcodes.ATHROW == opcode) {
mv.visitMethodInsn(Opcodes.INVOKESTATIC, "java/lang/System", "currentTimeMillis", "J", false)
mv.visitVarInsn(Opcodes.LLOAD, time)
mv.visitInsn(Opcodes.LSUB)
mv.visitFieldInsn(Opcodes.GETSTATIC, owner, "timer", "J")
mv.visitInsn(Opcodes.LADD)
mv.visitFieldInsn(Opcodes.PUTSTATIC, owner, "timer", "J")
}
super.visitInsn(opcode)
}
overridefunvisitMaxs(maxStack: Int, maxLocals: Int){
super.visitMaxs(maxStack + 4, maxLocals)
}
}
4. AdaviceAdapter
该类是一个抽象类,可以方便在方法的开头或者任意的结束位置进行代码的插入。
主要的好处是可以检测到构造函数,这个适配器大多数的代码都是为了检测构造函数。该类属于LocalVariablesSorter的子类,所以也同样可以直接通过newLocal方法创建新的局部变量。
示例代码:
https://github.com/dengshiwei/asm-module/blob/master/asm_example/src/main/java/com/andoter/asm_example/part3/AddTimerAdapter6.kt
classAddTimerMethodAdapter6(
privatevarowner: String,
methodVisitor: MethodVisitor?,
access: Int,
name: String?,
deor: String?
) : AdviceAdapter(Opcodes.ASM7, methodVisitor, access, name, deor) {
overridefunonMethodEnter{
mv.visitFieldInsn(GETSTATIC, owner, "timer", "J")
mv.visitMethodInsn(
INVOKESTATIC, "java/lang/System",
"currentTimeMillis", "J", false
)
mv.visitInsn(LSUB)
mv.visitFieldInsn(PUTSTATIC, owner, "timer", "J")
}
overridefunonMethodExit(opcode: Int){
mv.visitFieldInsn(GETSTATIC, owner, "timer", "J")
mv.visitMethodInsn(
INVOKESTATIC, "java/lang/System",
"currentTimeMillis", "J", false
)
mv.visitInsn(LADD)
mv.visitFieldInsn(PUTSTATIC, owner, "timer", "J")
}
overridefunvisitMaxs(maxStack: Int, maxLocals: Int){
mv.visitMaxs(maxStack + 4, maxLocals)
}
}
2.3 FieldVisitor
用于Field字段的详细信息访问,比如字段上的注解。
访问时按照以下顺序执行:( visitAnnotation | visitTypeAnnotation | visitAttribute )* visitEnd。
方法说明:
visitAnnotation(String deor, boolean visible) :访问字段上的注解
visitAttribute(Attribute attribute) :访问字段上的属性
visitEnd :访问结束
visitTypeAnnotation :访问字段上 Type 类型的注解
FieldVisitor接口演示示例代码:
classFiledVisitorPrinter(fieldVisitor: FieldVisitor?) : FieldVisitor(Opcodes.ASM7, fieldVisitor) {
overridefunvisitEnd{
super.visitEnd
ADLog.info( "visitEnd")
}
overridefunvisitAnnotation(deor: String?, visible: Boolean): AnnotationVisitor {
ADLog.info( "visitAnnotation, des = $deor, visiable = $visible")
returnsuper.visitAnnotation(deor, visible)
}
overridefunvisitTypeAnnotation(
typeRef: Int,
typePath: TypePath?,
deor: String?,
visible: Boolean
) : AnnotationVisitor {
returnsuper.visitTypeAnnotation(typeRef, typePath, deor, visible)
}
overridefunvisitAttribute(attribute: Attribute?){
super.visitAttribute(attribute)
}
}
使用FieldVisitor新增一个字段。
示例代码:
https://github.com/dengshiwei/asm-module/blob/master/asm_example/src/main/java/com/andoter/asm_example/part2/AddFieldDemo.kt
classAddFieldAdapter( privatevarversion: Int, classVisitor: ClassVisitor) : ClassVisitor(version, classVisitor){
varisExists = false
lateinitvarfiledName: String
privatevarfiledAccessFlag: Int= Opcodes.ACC_PUBLIC
lateinitvarfieldDeion: String
lateinitvarclassVisitor: ClassVisitor
constructor(version: Int, classVisitor: ClassVisitor, filedName:String, filedAccess: Int, fieldDeion: String) : this(version, classVisitor){
this.classVisitor = classVisitor
this.filedAccessFlag = filedAccess
this.fieldDeion = fieldDeion
this.filedName = filedName
}
overridefunvisit(version: Int, access: Int, name: String?, signature: String?, superName: String?, interfaces: Array< outString>?){
ADLog.info( "visit, version = $version, access = ${AccessCodeUtils.accCode2String(access)}, name = ${name}, signature = $signature")
super.visit(version, access, name, signature, superName, interfaces)
}
overridefunvisitField(access: Int, name: String?, deor: String?, signature: String?, value: Any?): FieldVisitor? {
ADLog.info( "visitField: access= ${AccessCodeUtils.accCode2String(access)},name= $name,deor= $deor,"+
"signature= $signature,value= $value")
if(name == "name") {
println( "存在字段 name,value = ")
}
if( this.filedName == name) {
this.isExists = true
}
returnsuper.visitField(access, name, deor, signature, value)
}
overridefunvisitEnd{
super.visitEnd
if(!isExists) {
valfiledVisitor = cv.visitField(filedAccessFlag, filedName, fieldDeion, null, null)
filedVisitor?.visitEnd
}
ADLog.info( "visitEnd")
}
}
在上面的示例代码中,在visitField中检测字段是否存在,如果字段存在,则在visitEnd中不进行插入,如果不存在,则插入对应的字段。
1. CheckFieldAdapter
同ClassVisitor一样,FieldVisitor本身也不会检查调用顺序是否适当,所以对于Field的操作,ASM提供了CheckFieldAdapter用于检测调用顺序是否在正确。
使用方式是将FieldVisitor对象委托给CheckFieldAdapter。
funmain{
valclassReader = ClassReader( "com.andoter.asm_example.field.CheckFieldInsn")
valclassWriter = ClassWriter(classReader, ClassWriter.COMPUTE_MAXS)
classReader.accept( object: ClassVisitor(Opcodes.ASM7, classWriter){
varisExist = false
overridefunvisitField(
access: Int,
name: String?,
deor: String?,
signature: String?,
value: Any?
) : FieldVisitor? {
if(name == "TAG") {
isExist = true
}
returnsuper.visitField(access, name, deor, signature, value)
}
overridefunvisitEnd{
super.visitEnd
if(!isExist) {
valfieldVisitor = cv.visitField(Opcodes.ACC_PUBLIC, "TAG", "Ljava/lang/String;", null, "CheckField")
valcheckFieldAdapter = CheckFieldAdapter(fieldVisitor)
checkFieldAdapter.visitAnnotation( "Lcom/andoter/Interface;", true)
checkFieldAdapter.visitEnd
}
}
}, ClassReader.SKIP_DEBUG)
ClassOutputUtil.byte2File( "asm_example/files/CheckFieldInsn.class", classWriter.toByteArray)
}
在示例代码中,我们在visitEnd中新增一个字段。
2. TraceFieldVisitor
功能作用同TraceClassVisitor的使用。使用方式是将FieldVisitor对象委托给TraceFieldVisitor。参照上面的示例代码,我们稍作调整。
overridefunvisitEnd{
super.visitEnd
if(!isExist) {
valfieldVisitor = cv.visitField(Opcodes.ACC_PUBLIC, "TAG", "Ljava/lang/String;", null, "CheckField")
valtraceFieldVisitor = TraceFieldVisitor(fieldVisitor, Textifier)
traceFieldVisitor.visitAnnotation( "Lcom/andoter/Interface;", true)
traceFieldVisitor.visitEnd
}
}
2.4 AnnotationVisitor
用于访问注解。
调用顺序如下:( visit | visitEnum | visitAnnotation | visitArray )* visitEnd
方法说明:
visit(String name, Object value) :访问注解的名称和值
visitEnum(String name, String deor, String value) :访问注解的 Enum 值
visitAnnotation(String name, String deor) :访问嵌套注解
visitArray(String name) :访问注解的 Array 值
visitEnd :访问结束
示例代码:
https://github.com/dengshiwei/asm-module/blob/master/asm_example/src/main/java/com/andoter/asm_example/part4/AnnotationPrinter.kt
@Deprecated(message = "deprecated")
classAnnotationPrinter{
}
funmain{
valclassReader = ClassReader( "com.andoter.asm_example.part4.AnnotationPrinter")
valclassWriter = ClassWriter(classReader, ClassWriter.COMPUTE_MAXS)
classReader.accept( object: ClassVisitor(Opcodes.ASM7, classWriter) {
overridefunvisitAnnotation(deor: String?, visible: Boolean): AnnotationVisitor {
returnAnnotationPrinterVisitor(cv.visitAnnotation(deor, visible))
}
}, ClassReader.SKIP_DEBUG)
}
classAnnotationPrinterVisitor(annotationVisitor: AnnotationVisitor) : AnnotationVisitor(Opcodes.ASM7, annotationVisitor) {
overridefunvisitEnd{
super.visitEnd
ADLog.info( "visitEnd")
}
overridefunvisitAnnotation(name: String?, deor: String?): AnnotationVisitor {
ADLog.info( "visitAnnotation, name = $name, deor = $deor")
returnsuper.visitAnnotation(name, deor)
}
overridefunvisitEnum(name: String?, deor: String?, value: String?){
ADLog.info( "visitEnum, name = $name, deor = $deor, value = $value")
super.visitEnum(name, deor, value)
}
overridefunvisit(name: String?, value: Any?){
super.visit(name, value)
ADLog.info( "visit, name = $name, value = $value")
}
overridefunvisitArray(name: String?): AnnotationVisitor {
ADLog.info( "visitArray, name = $name")
returnsuper.visitArray(name)
}
}
同样对于Annotation,在ASM中提供了对应的CheckAnnotationAdapter和TraceAnnotationVisitor用于检测辅助我们检测生成的注解,用法和ClassVisitor对应的基本相同,这里提供下CheckAnnotationAdapter的示例代码和TraceAnnotationVisitor的示例代码作为参照。
https://github.com/dengshiwei/asm-module/blob/master/asm_example/src/main/java/com/andoter/asm_example/part4/CheckAnnotationAdapterDemo.kt
https://github.com/dengshiwei/asm-module/blob/master/asm_example/src/main/java/com/andoter/asm_example/part4/TraceAnnotationVisitorDemo.kt
2.5 SignatureVisitor
用于泛型的类型签名处理。
该类用于处理Method、Class和Type的签名处理。
类签名的访问顺序: ( visitFormalTypeParameter visitClassBound? visitInterfaceBound* )* (visitSuperclass visitInterface* )
方法签名的访问顺序: ( visitFormalTypeParameter visitClassBound? visitInterfaceBound* )* (visitParameterType* visitReturnType visitExceptionType* )
Type 签名的访问顺序: visitBaseType | visitTypeVariable | visitArrayType | ( visitClassType visitTypeArgument* ( visitInnerClassType visitTypeArgument* )* visitEnd ) )
目前针对这块使用的比较少,示例代码可以参照ASM 指导手册中的示例。
https://github.com/dengshiwei/asm-module/blob/master/asm_example/src/main/java/com/andoter/asm_example/part4/SignatureGeneric.kt
3
树 API
在树API中,类用一个对象树表示,比如一个类用ClassNode对象表示,一个方法用MethodNode对象表示。它通过将每个节点都通过一个Node对象的表示方式,所以在树API包下涉及到很多类。
另外一点需要我们注意,在树API中的很多类都是继承核心API的类进行实现的,比如ClassNode是继承ClassVisitor,MethodNode是继承MethodVisitor实现。所以通过树API可以转换为等价的事件序列。
这其中包含很多XXXInsnNode的指令操作节点,它们都是AbstractInsnNode的子类。同时在树API中使用InsnList对象表示一个指令的集合,一个AbstractInsnNode指令对象只能出现在一个InsnList当中。
3.1 ClassNode
用于生成和表示一个类对象,继承ClassVisitor进行实现。
从它的基本结构中可以看出,这个类的字段都分别对应着一个类的结构。比如name表示名称,signature表示类型签名,fields表示类的组成字段,methods表示类的组成方法。
我们使用ClassNode生成一个类,示例代码如下:
https://github.com/dengshiwei/asm-module/blob/master/asm_example/src/main/java/com/andoter/asm_example/part6/CreateClass.kt
/*
使用树 API 生成类的过程,就是创建一个 ClassNode 对象,然后初始化它的字段。还是以 2.2.3 中的例子说明:
package pkg;
public interface Comparable extends Measurable {
int LESS = -1;
int EQUAL = 0;
int GREATER = 1;
int compareTo(Object o);
}
使用树 API 生成类时,需要大约多花费 30% 的时间,占用的内存也比较多。但是可以按照任意顺序生成元素,一些情况下可能比较方便。
*/
funmain{
valclassNode = ClassNode
classNode.version = Opcodes.V1_5
classNode.access = Opcodes.ACC_PUBLIC + Opcodes.ACC_INTERFACE + Opcodes.ACC_ABSTRACT
classNode.name = "pkg/Comparable"
classNode.superName = "java/lang/Object"
classNode.interfaces.add( "pkg/Measurable")
classNode.fields.add(
FieldNode(
Opcodes.ACC_PUBLIC + Opcodes.ACC_FINAL + Opcodes.ACC_STATIC,
"LESS",
"I",
null,
-1
)
)
classNode.fields.add(
FieldNode(
Opcodes.ACC_PUBLIC + Opcodes.ACC_FINAL + Opcodes.ACC_STATIC,
"EQUAL",
"I",
null,
0
)
)
classNode.fields.add(
FieldNode(
Opcodes.ACC_PUBLIC + Opcodes.ACC_FINAL + Opcodes.ACC_STATIC,
"GREATER",
"I",
null,
1
)
)
classNode.methods.add(
MethodNode(
Opcodes.ACC_PUBLIC + Opcodes.ACC_ABSTRACT,
"compareTo",
"(Ljava/lang/Object;)I",
null,
null
)
)
}
使用树API生成类时,需要大约多花费 30% 的时间,占用的内存也比较多。但是可以按照任意顺序生成元素,一些情况下可能比较方便。
3.2 FieldNode
用于生成和表示类的字段,继承FieldVisitor进行实现。
示例代码可以参照ClassNode中展示的代码。
3.3 ClassVisitor 与 ClassNode 交互
在 3.1 小节中,我们介绍了如何创建一个ClassNode对象,下面我们介绍下如何从一个ClassReader所读的字节数组中转换一个ClassNode对象。
1. ClassWriter 与 ClassNode
valclassNode = ClassNode
valclassWriter = ClassWriter(ClassWriter.COMPUTE_MAXS)
classNode.accept(classWriter)
首先创建一个ClassNode对象,然后使用accept方法将ClassWriter传入进去即可。
2. ClassReader 与 ClassNode
valclassNode = ClassNode
valclassReader = ClassReader( "")
classReader.accept(classNode, FLAG)
首先创建一个ClassNode对象,然后使用ClassReader的accept方法将ClassNode传入进去即可。因为本身ClassNode对象也是一个ClassVisitor对象。
3.4 MethodNode
用于生成和表示类的方法。
我们重点介绍InsnList instructions字段。InsnList是一个由指令组成的双向链表,它们的链接存储在AbstractInsnNode对象中。
AbstractInsnNode类是表示字节代码指令的类的超类。它的子类是Xxx InsnNode类,对应于MethodVisitor接口的visitXxx Insn方法,例如,VarInsnNode类对应于visitVarInsn方法。所以对于一个AbstractInsnNode对象来说,具有以下特征:
一个 AbstractInsnNode 对象在一个指令列表中最多出现一次
一个 AbstractInsnNode 对象不能同时属于多个指令列表
如果一个 AbstractInsnNode 属于某个指令列表,要将它添加到另一列个指令表,必须先将其从原指令列表中删除
将一个列表中的所有元素都添加到另一个指令列表中,将会清空第一个列表。
标记与帧,还有行号,尽管它们并不是指令,但也都用AbstractInsnNode类的子类表示,即LabelNode、FrameNode和LineNumberNode类。
通过MethodNode生成一个方法,在下面的示例中,生成checkAndSetF方法。示例代码如下:
https://github.com/dengshiwei/asm-module/blob/master/asm_example/src/main/java/com/andoter/asm_example/part7/MakeMethod.kt
classMakeMethod{
privatevarf : Int= 0
funcheckAndSetF(f: Int){
if(f >= 0) {
this.f = f
} else{
throwIllegalArgumentException
}
}
}
/*
参照 3.1.5 小节中的代码:
public void checkAndSetF(int f) {
if (f >= 0) {
this.f = f;
} else {
throw new IllegalArgumentException;
}
}
这个新 setter 方法的字节代码如下:
ILOAD 1
IFLT label
ALOAD 0
ILOAD 1
PUTFIELD pkg/Bean f I
GOTO end
label:
NEW java/lang/IllegalArgumentException
DUP
INVOKESPECIAL java/lang/IllegalArgumentException V
ATHROW
end:
RETURN
*/
funmain{
valmn = MethodNode
valinsnList = mn.instructions
insnList.add(VarInsnNode(Opcodes.ILOAD, 1))
vallabel = LabelNode
insnList.add(JumpInsnNode(Opcodes.IFLE, label))
insnList.add(VarInsnNode(Opcodes.ALOAD, 0))
insnList.add(VarInsnNode(Opcodes.ILOAD, 1))
insnList.add(FieldInsnNode(Opcodes.PUTFIELD, "pkg/Bean", "f", "I"))
valendLabel = LabelNode
insnList.add(JumpInsnNode(Opcodes.GOTO, endLabel))
insnList.add(endLabel)
insnList.add(FrameNode(Opcodes.F_SAME, 0, null, 0, null))
insnList.add(TypeInsnNode(Opcodes.NEW, "java/lang/IllegalArgumentException"))
insnList.add(InsnNode(Opcodes.DUP))
insnList.add(MethodInsnNode(Opcodes.INVOKESPECIAL, "java/lang/IllegalArgumentException", "", "V"))
insnList.add(InsnNode(Opcodes.ATHROW))
insnList.add(endLabel)
insnList.add(FrameNode(Opcodes.F_SAME, 0, null, 0, null))
insnList.add(InsnNode(Opcodes.RETURN))
mn.maxLocals = 2
mn.maxStack = 2
}
使用MethodNode还可以完成很多功能,比如转换方法、移除字段的自我赋值等。
https://github.com/dengshiwei/asm-module/blob/master/asm_example/src/main/java/com/andoter/asm_example/part7/RemoveGetFieldPutFieldTransformer.kt
4
方法分析
在ASM中提供了基于树API的方法分析模块,用于对方法进行分析。方法分析大致可以分为两种类型:
数据流分析:指对于一个方法的每条指令,计算其执行帧的状态;
控制流分析:指计算一个方法的控制流图,并对这个图进行分析。控制流图的节点为指令,如果指令 j 可以紧跟在 i 之后执行,则图的有向边将连接这两条指令 i→j 。
同时对于数据流分析又可以分为正向分析和反向分析。
正向分析:是指对于每条指令,根据执行帧在执行此条指令之前的状态,计算执行帧在这一指令执行后的状态;
反向分析:是指对于每条指令,根据执行帧在执行此指令之后的状态,计算执行帧在这一指令执行前的状态。
基本类图
Analyzer :分析主类;
BasicInterpreter :基本的数据流分析器,主要是作为一个空实现,用于构建 Analyzer 对象;
BasicVerifier :基本数据流校验器, BasicVerifier 是 BasicInterpreter 的子类,用于实现对字节码指令是否正确的校验;
SimpleVerifier :简单数据流校验器, SimpleVerifier 是 BasicVerifier 的子类,它使用更多的集合来模拟字节代码指令的执行,所以它可以检测出更多的错误;
Interpreter 类是抽象类,它利用在 BasicValue 类中定义的 7 个值集来模拟字节代码指令的效果:
UNINITIALIZED_VALUE 指“所有可能值”
INT_VALUE 指“所有 int 、 short 、 byte 、 boolean 或 char 值”
FLOAT_VALUE 指“所有 float 值”
LONG_VALUE 指“所有 long 值”
DOUBLE_VALUE 指“所有 double 值”
REFERENCE_VALUE 指“所有对象和数组值”
RETURNADDRESS_VALUE 用于子协程
基本使用
ClassReader classReader = newClassReader(bytecode);
ClassNode classNode = newClassNode;
classReader.accept(classNode, ClassReader.SKIP_DEBUG);
for(MethodNode method : classNode.methods) {
if(method.instructions.size > 0) {
Analyzer analyzer = newAnalyzer( newBasicInterpreter); // 参数可以是 BasicVerifier、SimpleVerifier
analyzer.analyze(classNode.name, method);
Frame[] frames = analyzer.getFrames;
// Elements of the frames array now contains info for each instruction
// from the analyzed method. BasicInterpreter creates BasicValue, that
// is using simplified type system that distinguishes the UNINITIALZED,
// INT, FLOAT, LONG, DOUBLE, REFERENCE and RETURNADDRESS types.
...
}
}
在分析之后,无论什么样的Interpreter实现,由Analyzer.getFrames方法返回的计算帧,对于不可到达的指令都是null。这一特性可用于非常轻松地实现一个RemoveDeadCodeAdapter类用于删除无用代码,示例代码如下:
https://github.com/dengshiwei/asm-module/blob/master/asm_example/src/main/java/com/andoter/asm_example/part8/RemoveDeadCode.kt
classRemoveDeadCodeAdapter(
varowner: String?, varaccess: Int, varname: String?,
vardesc: String?, varmethodVisitor: MethodVisitor
) : MethodVisitor(Opcodes.ASM7, MethodNode(access, name, desc, null, null)) {
overridefunvisitEnd{
valmethodNode = mv asMethodNode
valanalyzer = Analyzer(BasicInterpreter)
try{
analyzer.analyze(owner, methodNode)
valframes = analyzer.frames
valinsns = methodNode.instructions.toArray
for(i ininsns.indices) {
if(frames[i] == null&& insns[i] ! isLabelNode) {
methodNode.instructions.remove(insns[i])
}
}
} catch(ignore: AnalyzerException) {
}
methodNode.accept(methodVisitor)
}
}
funmain{
valclassReader = ClassReader( "com.andoter.asm_example.part8.RemoveDeadCode")
valclassWriter = ClassWriter(classReader, ClassWriter.COMPUTE_MAXS)
classReader.accept( object: ClassVisitor(Opcodes.ASM7, classWriter) {
privatevarname: String? = ""
overridefunvisit(
version: Int,
access: Int,
name: String?,
signature: String?,
superName: String?,
interfaces: Array< outString>?
) {
this.name = name
super.visit(version, access, name, signature, superName, interfaces)
}
overridefunvisitMethod(
access: Int,
name: String?,
deor: String?,
signature: String?,
exceptions: Array< outString>?
) : MethodVisitor? {
ADLog.info( "visitMethod, name = $name, desc = $deor")
valmethodVisitor = super.visitMethod(access, name, deor, signature, exceptions)
if(methodVisitor != null) {
returnRemoveDeadCodeAdapter( this.name, access, name, deor, methodVisitor)
}
returnmethodVisitor
}
}, ClassReader.SKIP_DEBUG)
ClassOutputUtil.byte2File( "asm_example/files/RemoveDeadCode.class", classWriter.toByteArray)
}
5
总结
本文只是对ASM的基础功能有一个简单介绍,用于快速入门,更多详细的指导可以参照asm4-guide进行系统的学习。最后推荐两个使用ASM应用案例开源库供大家学习。
https://asm.ow2.io/asm4-guide.pdf
神策数据埋点行业的实践: sa-sdk-android-plugin2 https://github.com/sensorsdata/sa-sdk-android-plugin2
字节跳动抖音 Android 团队 ByteX 项目: github.com/bytedance/B… https://github.com/bytedance/ByteXASM 官方指导手册:https://asm.ow2.io/asm4-guide.pdf