android asm gradle,Android AOP 利器 ,ASM 你值得拥有!

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字段的详细信息访问,比如字段上的注解。

cf8fa0a04fdfd367425c5c248dbb3d25.png

访问时按照以下顺序执行:( 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

用于访问注解。

fb955190c534f62004525b55675e748a.png

调用顺序如下:( 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

用于泛型的类型签名处理。

f652a5e1b36f5463594aabc93bcdbc0c.png

该类用于处理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可以转换为等价的事件序列。

0dc659471f35f93eee011dbb4e0df48e.png

这其中包含很多XXXInsnNode的指令操作节点,它们都是AbstractInsnNode的子类。同时在树API中使用InsnList对象表示一个指令的集合,一个AbstractInsnNode指令对象只能出现在一个InsnList当中。

3.1 ClassNode

用于生成和表示一个类对象,继承ClassVisitor进行实现。

9ce0e0ab7f76489aa8e130cc28b932c6.png

从它的基本结构中可以看出,这个类的字段都分别对应着一个类的结构。比如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进行实现。

76e86952ed964e4f331e9beb95012aac.png

示例代码可以参照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

用于生成和表示类的方法。

dff87a23cbab3a7e52f4401e30f15ea0.png

我们重点介绍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 。

同时对于数据流分析又可以分为正向分析和反向分析。

正向分析:是指对于每条指令,根据执行帧在执行此条指令之前的状态,计算执行帧在这一指令执行后的状态;

反向分析:是指对于每条指令,根据执行帧在执行此指令之后的状态,计算执行帧在这一指令执行前的状态。

基本类图

0c7117dcbbf5fc635d67c5f416823efd.png

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

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值