├── .gitignore ├── README.md ├── asm_example ├── .gitignore ├── build.gradle └── src │ └── main │ └── java │ └── com │ └── andoter │ └── asm_example │ ├── field │ ├── CheckFieldInsn.kt │ ├── FieldVisitorDemo.kt │ └── TraceFieldInsn.kt │ ├── part10 │ └── 树 API 向后兼容 │ ├── part2 │ ├── AddFieldAdapter.kt │ ├── AddFieldDemo.kt │ ├── ChangeVersionVisitor.kt │ ├── CheckClassAdapterDemo.kt │ ├── ClassPrintVisitor.kt │ ├── ClassReaderDemo.kt │ ├── ClassWriteDemo.kt │ ├── ConvertDemo.kt │ ├── MyModuleVisitor.kt │ ├── RemoveDebugAdapter.kt │ ├── RemoveDebugDemo.kt │ ├── TraceClassVisitorDemo.kt │ └── TypeDemo.kt │ ├── part3 │ ├── AddTimerAdapter.kt │ ├── AddTimerAdapter2.kt │ ├── AddTimerAdapter3.kt │ ├── AddTimerAdapter4.kt │ ├── AddTimerAdapter5.kt │ ├── AddTimerAdapter6.kt │ ├── CheckMethodDemo.kt │ ├── Example_3_1_3.kt │ ├── GenerateMethod.kt │ ├── MethodPrint.kt │ ├── RemoveAddZeroAdapter.kt │ ├── RemoveNopAdapter.kt │ ├── TraceMethodVisitorDemo.kt │ ├── TypeDemo.kt │ └── 字节码指令 │ ├── part4 │ ├── AddAnnotationAdapter.kt │ ├── AnnotationDemo.kt │ ├── AnnotationPrinter.kt │ ├── CheckAnnotationAdapterDemo.kt │ ├── DeprecatedDump.kt │ ├── SignatureGeneric.kt │ └── TraceAnnotationVisitorDemo.kt │ ├── part5 │ └── 向后兼容 │ ├── part6 │ ├── AddFieldDemo.kt │ ├── CreateClass.kt │ ├── PatternDemo.kt │ ├── RemoveMethodDemo.kt │ └── TreeAPI.kt │ ├── part7 │ ├── AddTimerTransformer.kt │ ├── MakeMethod.kt │ ├── MethodNodeAPI.kt │ ├── MyAdapter.kt │ ├── OptimizeJumpTransformer.kt │ ├── RemoveGetFieldPutFieldTransformer.kt │ └── RemoveGetFieldPutFieldTransformer2.kt │ ├── part8 │ ├── AnalyzerClass.kt │ ├── BasicVerifierAdapter.kt │ ├── CyclomaticComplexity.kt │ ├── IsNullInterpreter.kt │ ├── RemoveDeadCode.kt │ ├── RemoveUnusedCastAdapter.kt │ └── RemoveUnusedCastTransformer.kt │ ├── part9 │ ├── AnnotationNodeAdapter.kt │ └── SignatureVisitorAdapter.java │ └── utils │ ├── ADLog.kt │ ├── AccessCodeUtils.kt │ └── ClassOutputUtil.kt ├── build.gradle ├── doc ├── ASM6 开发者指南 │ ├── ASM 6 开发者指南.md │ ├── asm-package-dependencies.svg │ ├── asm-package-overview.svg │ └── control-flow-graph-example.svg ├── blog │ ├── AOP 利器 ASM 基础入门.md │ ├── AnalysisAPI.png │ ├── AnnotationVisitor.png │ ├── ClassNode.png │ ├── ClassVisitor.png │ ├── CoreAPI.png │ ├── FieldNode.png │ ├── FieldVisitor.png │ ├── MethodNode.png │ ├── MethodVisitor.png │ ├── SignatureVisitor.png │ ├── TreeAPI.png │ └── package_classes.png └── uml-file │ ├── AnalysisAPI.puml │ ├── CoreAPI.puml │ ├── SignatureVisitor.puml │ ├── TreeAPI.puml │ └── package_classes.puml ├── gradle.properties ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat └── settings.gradle /.gitignore: -------------------------------------------------------------------------------- 1 | *.iml 2 | .gradle 3 | /local.properties 4 | /.idea/caches 5 | /.idea/libraries 6 | /.idea/modules.xml 7 | /.idea/workspace.xml 8 | /.idea/navEditor.xml 9 | /.idea/assetWizardSettings.xml 10 | /.idea 11 | .DS_Store 12 | /build 13 | /captures 14 | .externalNativeBuild 15 | .cxx 16 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # asm-module 2 | 本工程中主要是针对 ASM 4 教程中的示例代码整合而来,将每个示例代码片段整合成可运行的 `Java` 运行程序,方便初学者学习使用。 3 | - 基础入门介绍文章[AOP 利器 ASM 基础入门](doc/blog/AOP%20利器%20ASM%20基础入门.md) 4 | - ASM 开发指南中文译文:[ASM 6 开发者指南](doc/ASM6%20开发者指南/ASM%206%20开发者指南.md) 5 | - 个人总结的额外案例:[asm-example](https://github.com/dengshiwei/asm-example) 6 | 7 | ## 章节目录 8 | 9 | ### [第 2 章](asm_example/src/main/java/com/andoter/asm_example/part2) 10 | #### 2.2 接口与组件 11 | - [2.2.1 ClassReader 读取](asm_example/src/main/java/com/andoter/asm_example/part2/ClassReaderDemo.kt) 12 | - [2.2.2 ClassVisitor 分析类](asm_example/src/main/java/com/andoter/asm_example/part2/ClassPrintVisitor.kt) 13 | - [2.2.3 ClassWriter 生成类](asm_example/src/main/java/com/andoter/asm_example/part2/ClassWriteDemo.kt) 14 | - [2.2.4 转换类](asm_example/src/main/java/com/andoter/asm_example/part2/ConvertDemo.kt) 15 | - [2.2.5 移除类成员](asm_example/src/main/java/com/andoter/asm_example/part2/RemoveDebugDemo.kt) 16 | - [2.2.6 添加类成员](asm_example/src/main/java/com/andoter/asm_example/part2/AddFieldDemo.kt) 17 | - 额外补充 18 | - [FieldVisitor](asm_example/src/main/java/com/andoter/asm_example/field/FieldVisitorDemo.kt) 19 | - [CheckFieldInsn](asm_example/src/main/java/com/andoter/asm_example/field/CheckFieldInsn.kt) 20 | - [TraceFieldInsn](asm_example/src/main/java/com/andoter/asm_example/field/TraceFieldInsn.kt) 21 | 22 | #### 2.3 工具 23 | - [2.3.1 Type](asm_example/src/main/java/com/andoter/asm_example/part2/TypeDemo.kt) 24 | - [2.3.2 TraceClassVisitor](asm_example/src/main/java/com/andoter/asm_example/part2/TraceClassVisitorDemo.kt) 25 | - [2.3.3 CheckClassAdapter](asm_example/src/main/java/com/andoter/asm_example/part2/CheckClassAdapterDemo.kt) 26 | 27 | ### [第 3 章](MethodVisitor) 28 | #### 3.1 结构 29 | - [3.1.3 字节码指令](asm_example/src/main/java/com/andoter/asm_example/part3/字节码指令) 30 | 31 | #### 3.2 接口与组件 32 | - [3.2.1 MethodVisitor](asm_example/src/main/java/com/andoter/asm_example/part3/MethodPrint.kt) 33 | - [3.2.2 生成方法](asm_example/src/main/java/com/andoter/asm_example/part3/GenerateMethod.kt) 34 | - [3.2.3 转换方法](asm_example/src/main/java/com/andoter/asm_example/part3/RemoveNopAdapter.kt) 35 | - [3.2.4 无状态转换](asm_example/src/main/java/com/andoter/asm_example/part3/AddTimerAdapter.kt) 36 | - [3.2.5 有状态转换](asm_example/src/main/java/com/andoter/asm_example/part3/RemoveAddZeroAdapter.kt) 37 | 38 | #### 3.3 工具 39 | - 3.3.1 基本工具 40 | - [1. Type](asm_example/src/main/java/com/andoter/asm_example/part3/TypeDemo.kt) 41 | - [2. TraceMethodVisitor](asm_example/src/main/java/com/andoter/asm_example/part3/TraceMethodVisitorDemo.kt) 42 | - 3.3.2 AnalyzerAdapter 43 | - [AddTimerMethodAdapter2](asm_example/src/main/java/com/andoter/asm_example/part3/AddTimerAdapter2.kt) 44 | - [AddTimerMethodAdapter3](asm_example/src/main/java/com/andoter/asm_example/part3/AddTimerAdapter3.kt) 45 | - 3.3.3 LocalVariablesSorter 46 | - [AddTimerMethodAdapter4](asm_example/src/main/java/com/andoter/asm_example/part3/AddTimerAdapter4.kt) 47 | - [AddTimerMethodAdapter5](asm_example/src/main/java/com/andoter/asm_example/part3/AddTimerAdapter5.kt) 48 | - [3.3.4. AdviceAdapter](asm_example/src/main/java/com/andoter/asm_example/part3/AddTimerAdapter6.kt) 49 | 50 | ### [第 4 章](asm_example/src/main/java/com/andoter/asm_example/part4) 51 | #### 4.1 泛型 52 | - [4.1.2 接口与组件 SignatureVisitor](asm_example/src/main/java/com/andoter/asm_example/part4/SignatureGeneric.kt) 53 | 54 | #### 4.2 注解 55 | - 4.2.2 接口与组件 56 | - [AnnotationVisitor 基础](asm_example/src/main/java/com/andoter/asm_example/part4/AnnotationPrinter.kt) 57 | - [删除注解 RemoveAnnotationAdapter](asm_example/src/main/java/com/andoter/asm_example/part4/AnnotationDemo.kt) 58 | - [添加注解 AddAnnotationAdapter](asm_example/src/main/java/com/andoter/asm_example/part4/AddAnnotationAdapter.kt) 59 | 60 | - 4.2.3 工具 61 | - [创建注解类 DeprecatedDump](asm_example/src/main/java/com/andoter/asm_example/part4/DeprecatedDump.kt) 62 | - [TraceAnnotationVisitor](asm_example/src/main/java/com/andoter/asm_example/part4/TraceAnnotationVisitorDemo.kt) 63 | - [CheckAnnotationAdapter](asm_example/src/main/java/com/andoter/asm_example/part4/CheckAnnotationAdapterDemo.kt) 64 | 65 | ### [第 5 章](asm_example/src/main/java/com/andoter/asm_example/part5) 66 | - [向后兼容/向下兼容](asm_example/src/main/java/com/andoter/asm_example/part5/向后兼容) 67 | 68 | ### [第 6 章](asm_example/src/main/java/com/andoter/asm_example/part6) 69 | - 6.1 接口组件介绍 70 | - [6.1.1 基本介绍](asm_example/src/main/java/com/andoter/asm_example/part6/TreeAPI.kt) 71 | - [6.1.2 生成类](asm_example/src/main/java/com/andoter/asm_example/part6/CreateClass.kt) 72 | - 6.1.3 添加删除类成员 73 | - [删除类成员](asm_example/src/main/java/com/andoter/asm_example/part6/RemoveMethodDemo.kt) 74 | - [添加类成员](asm_example/src/main/java/com/andoter/asm_example/part6/AddFieldDemo.kt) 75 | - 6.2 组件合成 76 | - [6.2.1 基本介绍](asm_example/src/main/java/com/andoter/asm_example/part6/TreeAPI.kt) 77 | - [6.2.2 模式](asm_example/src/main/java/com/andoter/asm_example/part6/PatternDemo.kt) 78 | 79 | ### [第 7 章](asm_example/src/main/java/com/andoter/asm_example/part7) 80 | - 7.1 接口与组件 81 | - [7.1.1 介绍](asm_example/src/main/java/com/andoter/asm_example/part7/MethodNodeAPI.kt) 82 | - [7.1.2 生成方法](asm_example/src/main/java/com/andoter/asm_example/part7/MakeMethod.kt) 83 | - 7.1.4 无状态转换和有状态转换 84 | - [AddTimerTransformer](asm_example/src/main/java/com/andoter/asm_example/part7/AddTimerTransformer.kt) 85 | - [RemoveGetFieldPutFieldTransformer](asm_example/src/main/java/com/andoter/asm_example/part7/RemoveGetFieldPutFieldTransformer.kt) 86 | - [RemoveGetFieldPutFieldTransformer2](asm_example/src/main/java/com/andoter/asm_example/part7/RemoveGetFieldPutFieldTransformer2.kt) 87 | - 7.1.5 [全局转换](asm_example/src/main/java/com/andoter/asm_example/part7/OptimizeJumpTransformer.kt) 88 | - 7.2 组件与合成 89 | - [7.2.2 模式](asm_example/src/main/java/com/andoter/asm_example/part7/MyAdapter.kt) 90 | 91 | ### [第 8 章](asm_example/src/main/java/com/andoter/asm_example/part8) 92 | - [8.1 介绍](asm_example/src/main/java/com/andoter/asm_example/part8/AnalyzerClass.kt) 93 | - 8.2 接口与组件 94 | - [8.2.1 基本数据流分析](asm_example/src/main/java/com/andoter/asm_example/part8/RemoveDeadCode.kt) 95 | - [8.2.2 基本数据流校验器](asm_example/src/main/java/com/andoter/asm_example/part8/BasicVerifierAdapter.kt) 96 | - [8.2.3 简单数据流校验器](asm_example/src/main/java/com/andoter/asm_example/part8/RemoveUnusedCastTransformer.kt) 97 | - [RemoveUnusedCastAdapter](asm_example/src/main/java/com/andoter/asm_example/part8/RemoveUnusedCastAdapter.kt) 98 | - [8.2.4 自定义数据流分析](asm_example/src/main/java/com/andoter/asm_example/part8/IsNullInterpreter.kt) 99 | - [8.2.5 控制流分析](asm_example/src/main/java/com/andoter/asm_example/part8/CyclomaticComplexity.kt) 100 | 101 | ### [第 9 章](asm_example/src/main/java/com/andoter/asm_example/part9) 102 | - [9.1 泛型](asm_example/src/main/java/com/andoter/asm_example/part9/SignatureVisitorAdapter.java) 103 | - [9.2 注解](asm_example/src/main/java/com/andoter/asm_example/part9/AnnotationNodeAdapter.kt) 104 | 105 | ### [第 10 章](asm_example/src/main/java/com/andoter/asm_example/part10) 106 | -------------------------------------------------------------------------------- /asm_example/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | /.idea -------------------------------------------------------------------------------- /asm_example/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'java-library' 2 | apply plugin: 'kotlin' 3 | 4 | dependencies { 5 | implementation fileTree(dir: 'libs', include: ['*.jar']) 6 | implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version" 7 | implementation 'org.ow2.asm:asm:7.0' 8 | implementation 'org.ow2.asm:asm-commons:7.0' 9 | implementation 'org.ow2.asm:asm-analysis:7.0' 10 | implementation 'org.ow2.asm:asm-util:7.0' 11 | implementation 'org.ow2.asm:asm-tree:7.0' 12 | } 13 | 14 | sourceCompatibility = "1.7" 15 | targetCompatibility = "1.7" -------------------------------------------------------------------------------- /asm_example/src/main/java/com/andoter/asm_example/field/CheckFieldInsn.kt: -------------------------------------------------------------------------------- 1 | package com.andoter.asm_example.field 2 | 3 | import com.andoter.asm_example.utils.ClassOutputUtil 4 | import org.objectweb.asm.* 5 | import org.objectweb.asm.util.CheckFieldAdapter 6 | 7 | class CheckFieldInsn { 8 | } 9 | 10 | fun main() { 11 | val classReader = ClassReader("com.andoter.asm_example.field.CheckFieldInsn") 12 | val classWriter = ClassWriter(classReader, ClassWriter.COMPUTE_MAXS) 13 | classReader.accept(object: ClassVisitor(Opcodes.ASM7, classWriter){ 14 | var isExist = false 15 | override fun visitField( 16 | access: Int, 17 | name: String?, 18 | descriptor: String?, 19 | signature: String?, 20 | value: Any? 21 | ): FieldVisitor? { 22 | if (name == "TAG") { 23 | isExist = true 24 | } 25 | 26 | return super.visitField(access, name, descriptor, signature, value) 27 | } 28 | 29 | override fun visitEnd() { 30 | super.visitEnd() 31 | if (!isExist) { 32 | val fieldVisitor = cv.visitField(Opcodes.ACC_PUBLIC, "TAG", "Ljava/lang/String;",null,"CheckField") 33 | val checkFieldAdapter = CheckFieldAdapter(fieldVisitor) 34 | checkFieldAdapter.visitAnnotation("Lcom/andoter/Interface;", true) 35 | checkFieldAdapter.visitEnd() 36 | } 37 | } 38 | }, ClassReader.SKIP_DEBUG) 39 | 40 | ClassOutputUtil.byte2File("asm_example/files/CheckFieldInsn.class", classWriter.toByteArray()) 41 | } -------------------------------------------------------------------------------- /asm_example/src/main/java/com/andoter/asm_example/field/FieldVisitorDemo.kt: -------------------------------------------------------------------------------- 1 | package com.andoter.asm_example.field 2 | 3 | import com.andoter.asm_example.utils.ADLog 4 | import org.objectweb.asm.* 5 | 6 | class FieldVisitorDemo { 7 | private val TAG = "FieldVisitor" 8 | } 9 | 10 | fun main() { 11 | val classReader = ClassReader("com.andoter.asm_example.field.FieldVisitorDemo") 12 | classReader.accept(object:ClassVisitor(Opcodes.ASM7){ 13 | override fun visitField( 14 | access: Int, 15 | name: String?, 16 | descriptor: String?, 17 | signature: String?, 18 | value: Any? 19 | ): FieldVisitor? { 20 | return FiledVisitorPrinter(super.visitField(access, name, descriptor, signature, value)) 21 | } 22 | }, ClassReader.SKIP_DEBUG) 23 | } 24 | 25 | class FiledVisitorPrinter(fieldVisitor: FieldVisitor?) : FieldVisitor(Opcodes.ASM7, fieldVisitor) { 26 | override fun visitEnd() { 27 | super.visitEnd() 28 | ADLog.info("visitEnd") 29 | } 30 | 31 | override fun visitAnnotation(descriptor: String?, visible: Boolean): AnnotationVisitor { 32 | ADLog.info("visitAnnotation, des = $descriptor, visiable = $visible") 33 | return super.visitAnnotation(descriptor, visible) 34 | } 35 | 36 | override fun visitTypeAnnotation( 37 | typeRef: Int, 38 | typePath: TypePath?, 39 | descriptor: String?, 40 | visible: Boolean 41 | ): AnnotationVisitor { 42 | return super.visitTypeAnnotation(typeRef, typePath, descriptor, visible) 43 | } 44 | 45 | override fun visitAttribute(attribute: Attribute?) { 46 | super.visitAttribute(attribute) 47 | } 48 | } 49 | 50 | -------------------------------------------------------------------------------- /asm_example/src/main/java/com/andoter/asm_example/field/TraceFieldInsn.kt: -------------------------------------------------------------------------------- 1 | package com.andoter.asm_example.field 2 | 3 | import com.andoter.asm_example.utils.ClassOutputUtil 4 | import org.objectweb.asm.* 5 | import org.objectweb.asm.util.CheckFieldAdapter 6 | import org.objectweb.asm.util.Textifier 7 | import org.objectweb.asm.util.TraceFieldVisitor 8 | 9 | class TraceFieldInsn { 10 | } 11 | 12 | fun main() { 13 | val classReader = ClassReader("com.andoter.asm_example.field.TraceFieldInsn") 14 | val classWriter = ClassWriter(classReader, ClassWriter.COMPUTE_MAXS) 15 | classReader.accept(object: ClassVisitor(Opcodes.ASM7, classWriter){ 16 | var isExist = false 17 | override fun visitField( 18 | access: Int, 19 | name: String?, 20 | descriptor: String?, 21 | signature: String?, 22 | value: Any? 23 | ): FieldVisitor? { 24 | if (name == "TAG") { 25 | isExist = true 26 | } 27 | 28 | return super.visitField(access, name, descriptor, signature, value) 29 | } 30 | 31 | override fun visitEnd() { 32 | super.visitEnd() 33 | if (!isExist) { 34 | val fieldVisitor = cv.visitField(Opcodes.ACC_PUBLIC, "TAG", "Ljava/lang/String;",null,"CheckField") 35 | val traceFieldVisitor = TraceFieldVisitor(fieldVisitor, Textifier()) 36 | traceFieldVisitor.visitAnnotation("Lcom/andoter/Interface;", true) 37 | traceFieldVisitor.visitEnd() 38 | } 39 | } 40 | }, ClassReader.SKIP_DEBUG) 41 | 42 | ClassOutputUtil.byte2File("asm_example/files/TraceFieldInsn.class", classWriter.toByteArray()) 43 | } -------------------------------------------------------------------------------- /asm_example/src/main/java/com/andoter/asm_example/part10/树 API 向后兼容: -------------------------------------------------------------------------------- 1 | ### 10.2 规则 2 | 首先,如果使用树 API 编写一个类生成器,那就不需要遵循什么规则(和核心 API 一样)。可以用任意构造器创建 ClassNode 和其他元素,可以使用这些类的任意方法。 3 | 另一方面,如果要用树 API 编写类分析器或类适配器,也就是说,如果使用 ClassNode 或其他直接或间接地通过 ClassReader.accept()填充的类似类,或者如果重写这些类中的 4 | 一个,则必须遵循下面给出的规则。 5 | 6 | #### 规则 1 7 | 要用 ASM 版本 X 的树 API 编写类分析器或适配器,则使用以这一确切版本为参数的构造器创建 ClassNode(而不是使用没有参数的默认构造器)。 8 | 9 | #### 规则 2 10 | 要用 ASM 版本 X 的树 API 编写一个类分析器或适配器,使用别人创建的 ClassNode,在以任何方式使用这个 ClassNode 之前,都要以这个确切版本号为参数,调用 11 | 它的 check()方法。 -------------------------------------------------------------------------------- /asm_example/src/main/java/com/andoter/asm_example/part2/AddFieldAdapter.kt: -------------------------------------------------------------------------------- 1 | package com.andoter.asm_example.part2 2 | 3 | import com.andoter.asm_example.utils.ADLog 4 | import com.andoter.asm_example.utils.AccessCodeUtils 5 | import org.objectweb.asm.ClassVisitor 6 | import org.objectweb.asm.FieldVisitor 7 | import org.objectweb.asm.Opcodes 8 | 9 | class AddFieldAdapter(private var version:Int, classVisitor: ClassVisitor) : ClassVisitor(version, classVisitor){ 10 | var isExists = false 11 | lateinit var filedName: String 12 | private var filedAccessFlag: Int = Opcodes.ACC_PUBLIC 13 | lateinit var fieldDescription: String 14 | lateinit var classVisitor: ClassVisitor 15 | constructor(version: Int, classVisitor: ClassVisitor, filedName:String, filedAccess:Int, fieldDescription: String) :this(version, classVisitor){ 16 | this.classVisitor = classVisitor 17 | this.filedAccessFlag = filedAccess 18 | this.fieldDescription = fieldDescription 19 | this.filedName = filedName 20 | } 21 | 22 | override fun visit(version: Int, access: Int, name: String?, signature: String?, superName: String?, interfaces: Array?) { 23 | ADLog.info("visit, version = $version, access = ${AccessCodeUtils.accCode2String(access)}, name = ${name}, signature = $signature") 24 | super.visit(version, access, name, signature, superName, interfaces) 25 | } 26 | 27 | override fun visitField(access: Int, name: String?, descriptor: String?, signature: String?, value: Any?): FieldVisitor? { 28 | ADLog.info("visitField: access=${AccessCodeUtils.accCode2String(access)},name=$name,descriptor=$descriptor," + 29 | "signature=$signature,value=$value") 30 | if (name == "name") { 31 | println("存在字段 name,value = ") 32 | } 33 | 34 | if (this.filedName == name) { 35 | this.isExists = true 36 | } 37 | return super.visitField(access, name, descriptor, signature, value) 38 | } 39 | 40 | override fun visitEnd() { 41 | super.visitEnd() 42 | if (!isExists) { 43 | val filedVisitor = cv.visitField(filedAccessFlag, filedName, fieldDescription, null, null) 44 | filedVisitor?.visitEnd() 45 | } 46 | ADLog.info("visitEnd") 47 | } 48 | } -------------------------------------------------------------------------------- /asm_example/src/main/java/com/andoter/asm_example/part2/AddFieldDemo.kt: -------------------------------------------------------------------------------- 1 | package com.andoter.asm_example.part2 2 | 3 | import com.andoter.asm_example.utils.ClassOutputUtil 4 | import org.objectweb.asm.ClassReader 5 | import org.objectweb.asm.ClassWriter 6 | import org.objectweb.asm.Opcodes 7 | 8 | class AddFieldDemo { 9 | private var name:String = "" 10 | 11 | 12 | fun getName():String{ 13 | return this.name 14 | } 15 | } 16 | 17 | fun main() { 18 | val classReader = ClassReader("com.andoter.asm_example.part2.AddFieldDemo") 19 | val classWriter = ClassWriter(classReader, ClassWriter.COMPUTE_MAXS) 20 | val classVisitor = AddFieldAdapter(Opcodes.ASM7, classWriter, "age",Opcodes.ACC_PUBLIC + Opcodes.ACC_STATIC, "Ljava/lang/String") 21 | classReader.accept(classVisitor, ClassReader.SKIP_CODE) 22 | 23 | println("===== 处理后的信息 ======") 24 | val printVisitor = ClassPrintVisitor(Opcodes.ASM7) 25 | val printReader = ClassReader(classWriter.toByteArray()) 26 | printReader.accept(printVisitor, ClassReader.SKIP_CODE) 27 | 28 | //输出文件查看 29 | ClassOutputUtil.byte2File("asm_example/files/AddFiled.class", classWriter.toByteArray()) 30 | } -------------------------------------------------------------------------------- /asm_example/src/main/java/com/andoter/asm_example/part2/ChangeVersionVisitor.kt: -------------------------------------------------------------------------------- 1 | package com.andoter.asm_example.part2 2 | 3 | import com.andoter.asm_example.utils.ADLog 4 | import com.andoter.asm_example.utils.AccessCodeUtils 5 | import org.objectweb.asm.ClassVisitor 6 | import org.objectweb.asm.ClassWriter 7 | import org.objectweb.asm.MethodVisitor 8 | 9 | 10 | class ChangeVersionVisitor(p0: Int) : ClassVisitor(p0) { 11 | private lateinit var classWriter: ClassWriter 12 | private var startTime:Long = 0 13 | constructor(version: Int, classWriter: ClassWriter):this(version) { 14 | this.classWriter = classWriter 15 | } 16 | 17 | override fun visitMethod(access: Int, name: String?, descriptor: String?, signature: String?, exceptions: Array?): MethodVisitor? { 18 | ADLog.info("[visitMethod],access = ${AccessCodeUtils.accCode2String(access)},"+ 19 | "name = ${name},descriptor=${descriptor},signature=${signature}" 20 | ) 21 | return classWriter.visitMethod(access, name, descriptor, signature, exceptions) 22 | } 23 | 24 | override fun visit(version: Int, access: Int, name: String?, signature: String?, superName: String?, interfaces: Array?) { 25 | startTime = System.nanoTime() 26 | ADLog.info("[visit],version = ${version},access=${AccessCodeUtils.accCode2String(access)},name=${name}") 27 | classWriter.visit(52, access, name, signature, superName, interfaces) 28 | } 29 | 30 | override fun visitEnd() { 31 | super.visitEnd() 32 | ADLog.info("[visitEnd], time = ${System.nanoTime() - startTime}") 33 | } 34 | } -------------------------------------------------------------------------------- /asm_example/src/main/java/com/andoter/asm_example/part2/CheckClassAdapterDemo.kt: -------------------------------------------------------------------------------- 1 | package com.andoter.asm_example.part2 2 | 3 | import com.andoter.asm_example.utils.ClassOutputUtil 4 | import org.objectweb.asm.ClassWriter 5 | import org.objectweb.asm.Opcodes 6 | import org.objectweb.asm.util.CheckClassAdapter 7 | 8 | /* 9 | ClassWriter 类并不会核实对其方法的调用顺序是否恰当,以及参数是否有效。因此,有 10 | 可能会生成一些被 Java 虚拟机验证器拒绝的无效类。为了尽可能提前检测出部分此类错误,可 11 | 以使用 CheckClassAdapter 类。和 TraceClassVisitor 类似,这个类也扩展了 12 | ClassVisitor 类,并将对其方法的所有调用都委托到另一个 ClassVisitor,比如一个 13 | TraceClassVisitor 或一个 ClassWriter。但是,这个类并不会打印所访问类的文本表示, 14 | 而是验证其对方法的调用顺序是否适当,参数是否有效,然后才会委托给下一个访问器。当发生 15 | 错误时,会抛出 IllegalStateException 或 IllegalArgumentException。 16 | */ 17 | class CheckClassDemo { 18 | 19 | } 20 | 21 | fun main() { 22 | val classWriter = ClassWriter(0) 23 | val checkClassAdapter = CheckClassAdapter(classWriter) 24 | checkClassAdapter.visit( 25 | Opcodes.V1_7, Opcodes.ACC_PUBLIC + Opcodes.ACC_ABSTRACT + Opcodes.ACC_INTERFACE, 26 | "pkg/Comparable",null, "java/lang/Object1", arrayOf("pkg/Mesureable")) 27 | checkClassAdapter.visitField(Opcodes.ACC_PUBLIC+ Opcodes.ACC_FINAL + Opcodes.ACC_STATIC, "LESS", "I", null, -1).visitEnd() 28 | checkClassAdapter.visitField(Opcodes.ACC_PUBLIC+ Opcodes.ACC_FINAL + Opcodes.ACC_STATIC, "EQUAL", "I", null, 0).visitEnd() 29 | checkClassAdapter.visitField(Opcodes.ACC_PUBLIC+ Opcodes.ACC_FINAL + Opcodes.ACC_STATIC, "GREATER", "I", null, 1).visitEnd() 30 | // 比如这里,我们将 Ljava/lang/Object 修改为 Ljava/lang/Object1,此时编译就会报错 31 | checkClassAdapter.visitMethod(Opcodes.ACC_PUBLIC + Opcodes.ACC_ABSTRACT, "compareTo", "(Ljava/lang/Object;)I" ,null, null).visitEnd() 32 | checkClassAdapter.visitEnd() 33 | val writeByte = classWriter.toByteArray() 34 | 35 | // 输出字节码,然后通过 javap 指令查看输出 36 | ClassOutputUtil.byte2File("asm_example/files/Comparable.class", writeByte) 37 | } -------------------------------------------------------------------------------- /asm_example/src/main/java/com/andoter/asm_example/part2/ClassPrintVisitor.kt: -------------------------------------------------------------------------------- 1 | package com.andoter.asm_example.part2 2 | 3 | import com.andoter.asm_example.utils.ADLog 4 | import com.andoter.asm_example.utils.AccessCodeUtils 5 | import org.objectweb.asm.* 6 | 7 | class ClassPrintVisitor(version: Int) : ClassVisitor(version) { 8 | 9 | private val apiVersion = api 10 | override fun visitMethod( 11 | access: Int, 12 | name: String?, 13 | descriptor: String?, 14 | signature: String?, 15 | exceptions: Array? 16 | ): MethodVisitor? { 17 | ADLog.info( 18 | "visitMethod:access=${AccessCodeUtils.accCode2String(access)}," + 19 | "name=${name}," + 20 | "descriptor=$descriptor," + 21 | "signature=$signature" 22 | ) 23 | return null 24 | } 25 | 26 | override fun visitModule(name: String?, access: Int, version: String?): ModuleVisitor? { 27 | ADLog.info("visitModule:name=$name,access=${AccessCodeUtils.accCode2String(access)},version=$version") 28 | return MyModuleVisitor(apiVersion) 29 | } 30 | 31 | override fun visitNestHost(nestHost: String?) { 32 | ADLog.info("visitNestHost:nestHost=$nestHost") 33 | super.visitNestHost(nestHost) 34 | } 35 | 36 | override fun visitInnerClass( 37 | name: String?, 38 | outerName: String?, 39 | innerName: String?, 40 | access: Int 41 | ) { 42 | ADLog.info( 43 | "visitInnerClass:name=$name,outerName=$outerName,innerName:$innerName,access=${AccessCodeUtils.accCode2String( 44 | access 45 | )}" 46 | ) 47 | super.visitInnerClass(name, outerName, innerName, access) 48 | } 49 | 50 | override fun visitSource(source: String?, debug: String?) { 51 | ADLog.info("visitSource:source=$source,debug=$debug") 52 | super.visitSource(source, debug) 53 | } 54 | 55 | override fun visitOuterClass(owner: String?, name: String?, descriptor: String?) { 56 | ADLog.info("visitOuterClass:owner:$owner,name=$name,descriptor=$descriptor") 57 | super.visitOuterClass(owner, name, descriptor) 58 | } 59 | 60 | override fun visit( 61 | version: Int, 62 | access: Int, 63 | name: String?, 64 | signature: String?, 65 | superName: String?, 66 | interfaces: Array? 67 | ) { 68 | ADLog.info( 69 | "visit:version=$version, access=${AccessCodeUtils.accCode2String(access)},name=$name,signature=$signature" + 70 | ",superName=$superName" 71 | ) 72 | super.visit(version, access, name, signature, superName, interfaces) 73 | } 74 | 75 | override fun visitNestMember(nestMember: String?) { 76 | ADLog.info("visitNestMember:nestMember=$nestMember") 77 | super.visitNestMember(nestMember) 78 | } 79 | 80 | override fun visitField( 81 | access: Int, 82 | name: String?, 83 | descriptor: String?, 84 | signature: String?, 85 | value: Any? 86 | ): FieldVisitor? { 87 | ADLog.info( 88 | "visitField:access=${AccessCodeUtils.accCode2String(access)},name=$name,descriptor=$descriptor," + 89 | "signature=$signature,value=$value" 90 | ) 91 | return super.visitField(access, name, descriptor, signature, value) 92 | } 93 | 94 | override fun visitEnd() { 95 | ADLog.info("visitEnd") 96 | super.visitEnd() 97 | } 98 | 99 | override fun visitAnnotation(descriptor: String?, visible: Boolean): AnnotationVisitor? { 100 | ADLog.info("visitAnnotation:descriptor=$descriptor,visible=$visible") 101 | return super.visitAnnotation(descriptor, visible) 102 | } 103 | 104 | override fun visitTypeAnnotation( 105 | typeRef: Int, 106 | typePath: TypePath?, 107 | descriptor: String?, 108 | visible: Boolean 109 | ): AnnotationVisitor? { 110 | ADLog.info("visitTypeAnnotation:typeRef=$typeRef,typePath=${typePath.toString()},descriptor=$descriptor,visible=$visible") 111 | return super.visitTypeAnnotation(typeRef, typePath, descriptor, visible) 112 | } 113 | 114 | override fun visitAttribute(attribute: Attribute?) { 115 | ADLog.info("visitAttribute") 116 | super.visitAttribute(attribute) 117 | } 118 | } -------------------------------------------------------------------------------- /asm_example/src/main/java/com/andoter/asm_example/part2/ClassReaderDemo.kt: -------------------------------------------------------------------------------- 1 | package com.andoter.asm_example.part2 2 | 3 | import org.objectweb.asm.ClassReader 4 | import org.objectweb.asm.Opcodes 5 | 6 | /** 7 | * ClassReader 类用于读取一个类文件的字节码,通过 accept 方法委托给 ClassVisitor 解析 8 | * 其中有在 accept 方法中有 4 个重要的 parsingOptions: 9 | * - ClassReader.SKIP_DEBUG:标识跳过调试内容,即 SourceFile(跳过源文件)、SourceDebugExtension(源码调试扩展)、LocalVariableTable(局部变量表)、 10 | * LocalVariableTypeTable(局部变量类型表)和 LineNumberTable(行号表属性), 11 | * 同时以下方法既不会被解析也不会被访问(ClassVisitor.visitSource,MethodVisitor.visitLocalVariable,MethodVisitor.visitLineNumber)。 12 | * 使用此标识后,类文件调试信息会被去除,请警记 13 | * - ClassReader.SKIP_CODE:跳过 Code attributes(代码属性)将不会被转换和访问,比如方法体代码不会进行解析和访问。 14 | * - ClassReader.SKIP_FRAMES:跳过 StackMap(栈图)和 StackMapTable(栈图表) 属性,即 MethodVisitor.visitFrame 方法不会被转换和访问。 15 | * 当使用 ClassWriter.COMPUTE_FRAMES 时,该标识会很有用,因为它避免了访问帧内容(这些内容会被忽略和重新计算,无需访问)。 16 | * - ClassReader.EXPAND_FRAMES:表示扩展栈帧图。默认栈图以它们原始格式(V1_6以下使用扩展格式,其他使用压缩格式)被访问。 17 | * 如果设置该标识,栈图则始终以扩展格式进行访问(此标识在 ClassReader 和 ClassWriter 中增加了解压/压缩步骤,会大幅度降低性能) 18 | * 19 | */ 20 | class ClassReaderDemo { 21 | } 22 | 23 | fun main() { 24 | val classReader = ClassReader("java.util.ArrayList") 25 | val classVisitor = ClassPrintVisitor(Opcodes.ASM7) 26 | classReader.accept(classVisitor, ClassReader.SKIP_DEBUG) 27 | } -------------------------------------------------------------------------------- /asm_example/src/main/java/com/andoter/asm_example/part2/ClassWriteDemo.kt: -------------------------------------------------------------------------------- 1 | package com.andoter.asm_example.part2 2 | 3 | import com.andoter.asm_example.utils.ClassOutputUtil 4 | import org.objectweb.asm.ClassWriter 5 | import org.objectweb.asm.Opcodes 6 | 7 | 8 | /** 9 | * 在后面的第三章中,我们会了解到帧栈的概念,为一个方法计算栈映射帧并不是非常容易: 10 | * 必须计算所有帧,找出与跳转目标相对应的帧,或者跳在无条件跳转之后的帧,最后压缩剩余帧。 11 | * 还好,在 ASM 中帮我们完成了这些工作,提供了几个选项: 12 | * - new ClassWriter(0):不会计算任何东西,必须由开发者手动进行计算帧、局部变量、操作数栈的大小 13 | * - new ClassWriter(ClassWriter.COMPUTE_MAXS):自动计算操作数栈和局部变量表大小,但是必须调用 visitMaxs(int) 方法,可以使用任何参数,它会被重新计算覆盖。 14 | * 但是该方式需要自行计算帧。 15 | * - new ClassWriter(ClassWriter.COMPUTE_FRAMES):计算全部,必须调用 visitMaxs(int),但是不必调用 visitFrame() 16 | * 17 | * COMPUTE_MAXS 选项使 ClassWriter 的速度降低 10%,而使用 COMPUTE_FRAMES 选项则使其降低一半。 18 | * 这必须与我们自行计算时所耗费的时间进行比较:在特定情况下,经常会存在一些比 ASM 所用算法更容易、更快速的计算方法,但ASM 使用的算法必须能够处理所有情况。 19 | */ 20 | class ClassWriteDemo { 21 | } 22 | 23 | /* 24 | package pkg; 25 | public interface Comparable extends Mesurable { 26 | int LESS = -1; 27 | int EQUAL = 0; 28 | int GREATER = 1; 29 | int compareTo(Object o); 30 | } 31 | */ 32 | 33 | fun main() { 34 | val classWriter = ClassWriter(0) 35 | classWriter.visit(Opcodes.V1_7, Opcodes.ACC_PUBLIC + Opcodes.ACC_ABSTRACT + Opcodes.ACC_INTERFACE, 36 | "pkg/Comparable",null, "java/lang/Object", arrayOf("pkg/Mesureable")) 37 | classWriter.visitField(Opcodes.ACC_PUBLIC+ Opcodes.ACC_FINAL + Opcodes.ACC_STATIC, "LESS", "I", null, -1).visitEnd() 38 | classWriter.visitField(Opcodes.ACC_PUBLIC+ Opcodes.ACC_FINAL + Opcodes.ACC_STATIC, "EQUAL", "I", null, 0).visitEnd() 39 | classWriter.visitField(Opcodes.ACC_PUBLIC+ Opcodes.ACC_FINAL + Opcodes.ACC_STATIC, "GREATER", "I", null, 1).visitEnd() 40 | classWriter.visitMethod(Opcodes.ACC_PUBLIC + Opcodes.ACC_ABSTRACT, "compareTo", "(Ljava/lang/Object;)I" ,null, null).visitEnd() 41 | classWriter.visitEnd() 42 | val writeByte = classWriter.toByteArray() 43 | 44 | // 输出字节码,然后通过 javap 指令查看输出 45 | ClassOutputUtil.byte2File("asm_example/files/Comparable.class", writeByte) 46 | } -------------------------------------------------------------------------------- /asm_example/src/main/java/com/andoter/asm_example/part2/ConvertDemo.kt: -------------------------------------------------------------------------------- 1 | package com.andoter.asm_example.part2 2 | 3 | 4 | import org.objectweb.asm.ClassReader 5 | import org.objectweb.asm.ClassWriter 6 | import org.objectweb.asm.Opcodes 7 | import kotlin.math.max 8 | 9 | class ConvertDemo(private val number: Int, private val age: Int) { 10 | 11 | fun addNumber(scale:Int) { 12 | val max = max(number, age) 13 | val result = max * scale 14 | } 15 | } 16 | 17 | fun main() { 18 | // 第一种方式中,如果在 ConvertVisitor 中仅仅在 visit 方法中使用 classwrite 修改了 version 版本,此时处理后的信息日志输出中只有 visitor。需要 19 | // 在 visitMethod 中也调用 classWrite 进行输出才行,而不是 调用 super 20 | val classWriter = ClassWriter(ClassWriter.COMPUTE_MAXS) 21 | val classReader = ClassReader("com.andoter.asm_example.part2.ConvertDemo") 22 | val classVisitor = ChangeVersionVisitor(Opcodes.ASM7, classWriter) 23 | classReader.accept(classVisitor, ClassReader.SKIP_CODE) 24 | 25 | println("===== 处理后的信息 ======") 26 | val printVisitor = ClassPrintVisitor(Opcodes.ASM7) 27 | val printReader = ClassReader(classWriter.toByteArray()) 28 | printReader.accept(printVisitor, ClassReader.SKIP_CODE) 29 | 30 | println("==== 2====") 31 | // 第二种方式,将一个 ClassReader 对象传递给 ClassWrite,会极大提高效率,详情看日志的 visitEnd 时间对比 32 | val classReader2 = ClassReader("com.andoter.asm_example.part2.ConvertDemo") 33 | val classWriter2 = ClassWriter(classReader2, ClassWriter.COMPUTE_MAXS) //将 classWrite 传入一个 classReader 参数 34 | val classVisitor2 = ChangeVersionVisitor(Opcodes.ASM7, classWriter2) 35 | classReader2.accept(classVisitor2, ClassReader.SKIP_CODE) 36 | 37 | println("===== 处理后的信息2 ======") 38 | val printVisitor2 = ClassPrintVisitor(Opcodes.ASM7) 39 | val printReader2 = ClassReader(classWriter.toByteArray()) 40 | printReader2.accept(printVisitor2, ClassReader.SKIP_CODE) 41 | } 42 | 43 | class MyLoader(p0: ClassLoader?) : ClassLoader(p0) { 44 | 45 | 46 | override fun findClass(p0: String?): Class<*> { 47 | return super.findClass(p0) 48 | } 49 | } -------------------------------------------------------------------------------- /asm_example/src/main/java/com/andoter/asm_example/part2/MyModuleVisitor.kt: -------------------------------------------------------------------------------- 1 | package com.andoter.asm_example.part2 2 | 3 | import com.andoter.asm_example.utils.ADLog 4 | import org.objectweb.asm.ModuleVisitor 5 | 6 | class MyModuleVisitor(api: Int) : ModuleVisitor(api) { 7 | override fun visitEnd() { 8 | super.visitEnd() 9 | ADLog.info("visitEnd") 10 | } 11 | 12 | override fun visitExport(packaze: String?, access: Int, vararg modules: String?) { 13 | super.visitExport(packaze, access, *modules) 14 | ADLog.info("visitExport") 15 | } 16 | 17 | override fun visitMainClass(mainClass: String?) { 18 | super.visitMainClass(mainClass) 19 | ADLog.info("visitMainClass") 20 | } 21 | 22 | override fun visitOpen(packaze: String?, access: Int, vararg modules: String?) { 23 | super.visitOpen(packaze, access, *modules) 24 | ADLog.info("visitOpen") 25 | } 26 | 27 | override fun visitPackage(packaze: String?) { 28 | super.visitPackage(packaze) 29 | ADLog.info("visitPackage") 30 | } 31 | 32 | override fun visitProvide(service: String?, vararg providers: String?) { 33 | super.visitProvide(service, *providers) 34 | ADLog.info("visitProvide") 35 | } 36 | 37 | override fun visitRequire(module: String?, access: Int, version: String?) { 38 | super.visitRequire(module, access, version) 39 | ADLog.info("visitRequire") 40 | } 41 | 42 | override fun visitUse(service: String?) { 43 | super.visitUse(service) 44 | ADLog.info("visitUse") 45 | } 46 | } -------------------------------------------------------------------------------- /asm_example/src/main/java/com/andoter/asm_example/part2/RemoveDebugAdapter.kt: -------------------------------------------------------------------------------- 1 | package com.andoter.asm_example.part2 2 | 3 | import com.andoter.asm_example.utils.ADLog 4 | import com.andoter.asm_example.utils.AccessCodeUtils 5 | import org.objectweb.asm.ClassVisitor 6 | import org.objectweb.asm.ClassWriter 7 | import org.objectweb.asm.FieldVisitor 8 | import org.objectweb.asm.MethodVisitor 9 | 10 | 11 | class RemoveDebugAdapter(p0: Int) : ClassVisitor(p0) { 12 | private lateinit var classWriter: ClassWriter 13 | constructor(version: Int, classWriter: ClassWriter) : this(version) { 14 | this.classWriter = classWriter 15 | } 16 | 17 | 18 | override fun visitMethod(access: Int, name: String?, descriptor: String?, signature: String?, exceptions: Array?): MethodVisitor? { 19 | ADLog.info("visitMethod:access=${AccessCodeUtils.accCode2String(access)}," + 20 | "name=${name}," + 21 | "descriptor=$descriptor," + 22 | "signature=$signature") 23 | if ("getDebugMode()Z" == name + descriptor) {// 移除 getDebugMode 方法 24 | return null 25 | } 26 | 27 | return classWriter.visitMethod(access, name, descriptor, signature, exceptions) 28 | } 29 | 30 | override fun visitSource(source: String?, debug: String?) { 31 | ADLog.info("visitSource: source = ${source}, debug = $debug") 32 | //classWriter.visitSource(source, debug) // 通过注释不进行转发,完成对 visitSource 的删除 33 | } 34 | 35 | override fun visit(version: Int, access: Int, name: String?, signature: String?, superName: String?, interfaces: Array?) { 36 | ADLog.info("visit: version=$version, access=${AccessCodeUtils.accCode2String(access)},name=$name,signature=$signature" + 37 | ",superName=$superName") 38 | classWriter.visit(version, access, name, signature, superName, interfaces) 39 | } 40 | 41 | override fun visitField(access: Int, name: String?, descriptor: String?, signature: String?, value: Any?): FieldVisitor? { 42 | ADLog.info("visitField: access=${AccessCodeUtils.accCode2String(access)},name=$name,descriptor=$descriptor," + 43 | "signature=$signature,value=$value") 44 | return classWriter.visitField(access, name, descriptor, signature, value) 45 | } 46 | 47 | override fun visitEnd() { 48 | ADLog.info("visitEnd") 49 | classWriter.visitEnd() 50 | } 51 | } -------------------------------------------------------------------------------- /asm_example/src/main/java/com/andoter/asm_example/part2/RemoveDebugDemo.kt: -------------------------------------------------------------------------------- 1 | package com.andoter.asm_example.part2 2 | 3 | import org.objectweb.asm.ClassReader 4 | import org.objectweb.asm.ClassWriter 5 | import org.objectweb.asm.Opcodes 6 | 7 | class RemoveDebugDemo(var debug:Boolean) { 8 | 9 | fun setDebugValue(debug: Boolean) { 10 | this.debug = debug 11 | } 12 | 13 | fun getDebugMode():Boolean { 14 | return this.debug 15 | } 16 | } 17 | 18 | fun main() { 19 | val classReader = ClassReader("com.andoter.asm_example.part2.RemoveDebugDemo") 20 | val classWriter = ClassWriter(classReader, ClassWriter.COMPUTE_MAXS) 21 | val classVisitor = RemoveDebugAdapter(Opcodes.ASM7, classWriter) 22 | classReader.accept(classVisitor, ClassReader.SKIP_CODE) 23 | 24 | println("==== 删除结果 ======") 25 | val printClassVisitor = ClassPrintVisitor(Opcodes.ASM7) 26 | val printReader = ClassReader(classWriter.toByteArray()) 27 | printReader.accept(printClassVisitor, ClassReader.SKIP_CODE) 28 | } -------------------------------------------------------------------------------- /asm_example/src/main/java/com/andoter/asm_example/part2/TraceClassVisitorDemo.kt: -------------------------------------------------------------------------------- 1 | package com.andoter.asm_example.part2 2 | 3 | import com.andoter.asm_example.utils.ClassOutputUtil 4 | import org.objectweb.asm.ClassWriter 5 | import org.objectweb.asm.Opcodes 6 | import org.objectweb.asm.util.TraceClassVisitor 7 | import java.io.PrintWriter 8 | 9 | /** 10 | * ClassWrite 输出的是字节数组,对于我们确认修改后的字节码是否符合预期用处并不大, 11 | * 在前面,我们是通过 File 文件流操作,将字节码输出到文件,然后利用 javap 指令来查看是否符合预期。 12 | * 在 ASM 中提供了 TraceClassVisitor 用来对我们的输出做检查,不必这么麻烦 13 | */ 14 | interface TraceClassVisitorDemo { 15 | var className: String 16 | var classVersion: Int 17 | 18 | fun getTraceInfo(): String 19 | } 20 | 21 | /** 22 | * 依照上面的 TraceClassVisitorDemo 为例 23 | */ 24 | fun main() { 25 | val classWriter = ClassWriter(0) 26 | /* 27 | 使用 TraceClassVisitor,同时使用 System.out 流将结果输出。 28 | 另外测试时还有一种写法,输出到文件。PrintWriter("asm_example/files/TraceClassVisitorDemo.class") 但是输出的文件通过 javap -v 指令查看会报错。 29 | 详情可参照:https://stackoverflow.com/questions/63443099/asm-traceclassvisitor-output-file-is-error 30 | */ 31 | val traceClassWriter = 32 | TraceClassVisitor(classWriter, PrintWriter(System.out)) 33 | traceClassWriter.visit( 34 | Opcodes.V1_7, 35 | Opcodes.ACC_PUBLIC + Opcodes.ACC_INTERFACE + Opcodes.ACC_ABSTRACT, 36 | "com.andoter.asm_example.part2/TraceClassVisitorDemo", 37 | null, 38 | "java/lang/Object", 39 | null 40 | ) 41 | traceClassWriter.visitSource("TraceClassVisitorDemo.class", null) 42 | traceClassWriter.visitField(Opcodes.ACC_PUBLIC+ Opcodes.ACC_FINAL + Opcodes.ACC_STATIC, "className", "Ljava/lang/String;", null, "").visitEnd() 43 | traceClassWriter.visitField(Opcodes.ACC_PUBLIC+ Opcodes.ACC_FINAL + Opcodes.ACC_STATIC, "classVersion", "I", null, 50).visitEnd() 44 | traceClassWriter.visitMethod( 45 | Opcodes.ACC_PUBLIC + Opcodes.ACC_ABSTRACT, 46 | "getTraceInfo", 47 | "()Ljava/lang/String;", 48 | null, 49 | null 50 | ).visitEnd() 51 | traceClassWriter.visitEnd() 52 | 53 | ClassOutputUtil.byte2File("asm_example/files/TraceClassVisitorDemo1.class", classWriter.toByteArray()) 54 | } -------------------------------------------------------------------------------- /asm_example/src/main/java/com/andoter/asm_example/part2/TypeDemo.kt: -------------------------------------------------------------------------------- 1 | package com.andoter.asm_example.part2 2 | 3 | import jdk.internal.org.objectweb.asm.Type 4 | 5 | class TypeDemo { 6 | fun testMethod(value: String):Int { 7 | return value.length 8 | } 9 | } 10 | 11 | /** 12 | * Type 类型是 ASM 提供的一个辅助类,用于对内部类型转换 13 | */ 14 | fun main() { 15 | // getInternalName 方法返回一个 Type 的内部名。例如, 16 | // Type.getType(String.class). getInternalName()给出 String 类 17 | val getInternalName = Type.getType(String::class.java).internalName 18 | val getInternalName2 = Type.getInternalName(String::class.java) 19 | println("$getInternalName,$getInternalName2") 20 | // getDescriptor 方法返回一个 Type 的述符。 21 | val getDescriptor = Type.getDescriptor(String::class.java) 22 | val getDescriptor2 = Type.getType(String::class.java).descriptor 23 | println("$getDescriptor,$getDescriptor2") 24 | 25 | // Type 获取方法的描述符,传入 Method 对象 26 | val getMethodDescriptor = Type.getMethodDescriptor(TypeDemo::class.java.getDeclaredMethod("testMethod", String::class.java)) 27 | println("getMethodDescriptor = $getMethodDescriptor") 28 | // Type 获取方法的描述符,传入方法的返回值类型和参数类型 29 | val getMethodDescriptor2 = Type.getMethodDescriptor(Type.INT_TYPE, Type.LONG_TYPE) 30 | println("getMethodDescriptor2 = $getMethodDescriptor2") 31 | 32 | val getArgumentType = Type.getArgumentTypes(TypeDemo::class.java.getDeclaredMethod("testMethod", String::class.java)) 33 | println("getArgumentType = ${getArgumentType[0]}") 34 | 35 | val getReturnType = Type.getReturnType(TypeDemo::class.java.getDeclaredMethod("testMethod", String::class.java)) 36 | println("getReturnType = $getReturnType") 37 | } -------------------------------------------------------------------------------- /asm_example/src/main/java/com/andoter/asm_example/part3/AddTimerAdapter.kt: -------------------------------------------------------------------------------- 1 | package com.andoter.asm_example.part3 2 | 3 | import com.andoter.asm_example.utils.ADLog 4 | import com.andoter.asm_example.utils.AccessCodeUtils 5 | import org.objectweb.asm.* 6 | import java.lang.reflect.Field 7 | 8 | class AddTimerAdapter { 9 | fun addTimer() { 10 | Thread.sleep(100) 11 | } 12 | } 13 | 14 | /* 15 | // 目标结构 16 | class AddTimerAdapter { 17 | var time:Long = 0 18 | fun addTimer() { 19 | time -= System.currentTimeMillis() 20 | Thread.sleep(200) 21 | time += System.currentTimeMillis() 22 | } 23 | } 24 | */ 25 | 26 | fun main() { 27 | val classReader = ClassReader("com.andoter.asm_example.part3.AddTimerAdapter") 28 | val classWriter = ClassWriter(classReader, 0) 29 | val classVisitor = AddTimerAdapterVisitor(Opcodes.ASM7, classWriter) 30 | 31 | classReader.accept(classVisitor, ClassReader.SKIP_DEBUG) 32 | // 通过自定义 Loader 加载修改后的 .class 文件 33 | val loader = MyLoader() 34 | val addTimerAdapterClazz = loader.defineClass("com.andoter.asm_example.part3.AddTimerAdapter", classWriter.toByteArray()) 35 | // 获取 addTimer 方法 36 | val method = addTimerAdapterClazz?.getDeclaredMethod("addTimer") 37 | // 获取 time 字段 38 | val field : Field? = addTimerAdapterClazz?.getDeclaredField("time") 39 | // 触发方法 40 | method?.invoke(addTimerAdapterClazz?.newInstance()) 41 | // 打印 time 耗时 42 | ADLog.error("time = ${field?.getLong(addTimerAdapterClazz?.newInstance())}") 43 | } 44 | 45 | class AddTimerAdapterVisitor(api: Int, classVisitor: ClassVisitor) : 46 | ClassVisitor(api, classVisitor) { 47 | private var owner: String? = "" 48 | private var isInterface: Boolean = false 49 | 50 | override fun visit( 51 | version: Int, 52 | access: Int, 53 | name: String?, 54 | signature: String?, 55 | superName: String?, 56 | interfaces: Array? 57 | ) { 58 | owner = name 59 | isInterface = access and Opcodes.ACC_INTERFACE != 0 60 | ADLog.info("visit, name = $name, isInterface = $isInterface") 61 | cv.visit(version, access, name, signature, superName, interfaces) 62 | } 63 | 64 | override fun visitMethod( 65 | access: Int, 66 | name: String?, 67 | descriptor: String?, 68 | signature: String?, 69 | exceptions: Array? 70 | ): MethodVisitor { 71 | ADLog.info("visitMethod, name = $name, descriptor = $descriptor") 72 | var methodVisitor = cv.visitMethod(access, name, descriptor, signature, exceptions) 73 | if (!isInterface && methodVisitor != null && name != "") { 74 | methodVisitor = AddTimerMethod(Opcodes.ASM7, methodVisitor, owner) 75 | } 76 | return methodVisitor 77 | } 78 | 79 | override fun visitEnd() { 80 | ADLog.info("visitEnd") 81 | if (!isInterface) { 82 | val fieldVisitor = 83 | cv.visitField(Opcodes.ACC_PUBLIC + Opcodes.ACC_STATIC, "time", "J", null, null) 84 | fieldVisitor?.visitEnd() 85 | } 86 | super.visitEnd() 87 | } 88 | } 89 | 90 | class AddTimerMethod(api: Int, methodVisitor: MethodVisitor) : MethodVisitor(api, methodVisitor) { 91 | private var owner: String? = "" 92 | 93 | constructor(api: Int, methodVisitor: MethodVisitor, owner: String?) : this(api, methodVisitor) { 94 | this.owner = owner 95 | } 96 | 97 | 98 | override fun visitCode() { 99 | ADLog.info("visitCode") 100 | mv.visitCode() 101 | mv.visitFieldInsn(Opcodes.GETSTATIC, owner, "time", "J") 102 | mv.visitMethodInsn( 103 | Opcodes.INVOKESTATIC, 104 | "java/lang/System", 105 | "currentTimeMillis", 106 | "()J" 107 | ) 108 | mv.visitInsn(Opcodes.LSUB) 109 | mv.visitFieldInsn(Opcodes.PUTSTATIC, owner, "time", "J") 110 | } 111 | 112 | override fun visitInsn(opcode: Int) { 113 | ADLog.info("visitInsn, opcode = ${AccessCodeUtils.opcode2String(opcode)}") 114 | if (opcode >= Opcodes.IRETURN && opcode <= Opcodes.RETURN || opcode == Opcodes.ATHROW) { 115 | mv.visitFieldInsn(Opcodes.GETSTATIC, owner, "time", "J") 116 | mv.visitMethodInsn(Opcodes.INVOKESTATIC, "java/lang/System", "currentTimeMillis", "()J") 117 | mv.visitInsn(Opcodes.LADD) 118 | mv.visitFieldInsn(Opcodes.PUTSTATIC, owner, "time", "J") 119 | } 120 | mv.visitInsn(opcode) 121 | } 122 | 123 | override fun visitMaxs(maxStack: Int, maxLocals: Int) { 124 | ADLog.info("visitMaxs") 125 | mv.visitMaxs(maxStack + 4, maxLocals) 126 | } 127 | } 128 | 129 | class MyLoader : ClassLoader() { 130 | fun defineClass(name: String?, b: ByteArray): Class<*>? { 131 | return defineClass(name, b, 0, b.size) 132 | } 133 | } -------------------------------------------------------------------------------- /asm_example/src/main/java/com/andoter/asm_example/part3/AddTimerAdapter2.kt: -------------------------------------------------------------------------------- 1 | package com.andoter.asm_example.part3 2 | 3 | import com.andoter.asm_example.utils.ADLog 4 | import org.objectweb.asm.* 5 | import org.objectweb.asm.commons.AnalyzerAdapter 6 | import java.lang.reflect.Field 7 | import kotlin.math.max 8 | 9 | class AddTimerAdapter2 { 10 | fun addTimer() { 11 | Thread.sleep(200) 12 | } 13 | } 14 | 15 | fun main() { 16 | val classReader = ClassReader("com.andoter.asm_example.part3.AddTimerAdapter2") 17 | val classWriter = ClassWriter(classReader, ClassWriter.COMPUTE_MAXS) 18 | classReader.accept(object : ClassVisitor(Opcodes.ASM7, classWriter) { 19 | var owner: String? = "" 20 | override fun visit( 21 | version: Int, 22 | access: Int, 23 | name: String?, 24 | signature: String?, 25 | superName: String?, 26 | interfaces: Array? 27 | ) { 28 | owner = name 29 | super.visit(version, access, name, signature, superName, interfaces) 30 | ADLog.info("owner = $owner") 31 | } 32 | 33 | override fun visitMethod( 34 | access: Int, 35 | name: String?, 36 | descriptor: String?, 37 | signature: String?, 38 | exceptions: Array? 39 | ): MethodVisitor { 40 | return AddTimerMethodAdapter2( 41 | Opcodes.ASM7, 42 | owner, 43 | access, 44 | name, 45 | descriptor, 46 | cv.visitMethod(access, name, descriptor, signature, exceptions) 47 | ) 48 | } 49 | 50 | override fun visitEnd() { 51 | ADLog.info("visitEnd") 52 | val fieldVisitor = 53 | cv.visitField(Opcodes.ACC_PUBLIC + Opcodes.ACC_STATIC, "time", "J", null, null) 54 | fieldVisitor?.visitEnd() 55 | super.visitEnd() 56 | } 57 | }, ClassReader.SKIP_DEBUG) 58 | 59 | // 通过自定义 Loader 加载修改后的 .class 文件 60 | val loader = MyLoader() 61 | val addTimerAdapterClazz = loader.defineClass( 62 | "com.andoter.asm_example.part3.AddTimerAdapter2", 63 | classWriter.toByteArray() 64 | ) 65 | // 获取 addTimer 方法 66 | val method = addTimerAdapterClazz?.getDeclaredMethod("addTimer") 67 | // 获取 time 字段 68 | val field: Field? = addTimerAdapterClazz?.getDeclaredField("time") 69 | // 触发方法 70 | method?.invoke(addTimerAdapterClazz?.newInstance()) 71 | // 打印 time 耗时 72 | ADLog.error("time = ${field?.getLong(addTimerAdapterClazz?.newInstance())}") 73 | } 74 | 75 | class AddTimerMethodAdapter2( 76 | api: Int, 77 | var owner: String?, 78 | access: Int, 79 | name: String?, 80 | descriptor: String?, 81 | methodVisitor: MethodVisitor? 82 | ) : AnalyzerAdapter(api, owner, access, name, descriptor, methodVisitor) { 83 | private var maxStack: Int = 0 84 | 85 | override fun visitCode() { 86 | super.visitCode() 87 | mv.visitFieldInsn(Opcodes.GETSTATIC, owner, "time", "J") 88 | mv.visitMethodInsn( 89 | Opcodes.INVOKESTATIC, 90 | "java/lang/System", 91 | "currentTimeMillis", 92 | "()J", 93 | false 94 | ) 95 | mv.visitInsn(Opcodes.LSUB) 96 | mv.visitFieldInsn(Opcodes.PUTSTATIC, owner, "time", "J") 97 | maxStack = 4 98 | } 99 | 100 | override fun visitInsn(opcode: Int) { 101 | if (opcode >= Opcodes.IRETURN && opcode <= Opcodes.RETURN || opcode == Opcodes.ATHROW) { 102 | mv.visitFieldInsn(Opcodes.GETSTATIC, owner, "time", "J") 103 | mv.visitMethodInsn(Opcodes.INVOKESTATIC, "java/lang/System", "currentTimeMillis", "()J") 104 | mv.visitInsn(Opcodes.LADD) 105 | mv.visitFieldInsn(Opcodes.PUTSTATIC, owner, "time", "J") 106 | maxStack = max(maxStack, stack.size + 4) 107 | } 108 | super.visitInsn(opcode) 109 | } 110 | 111 | override fun visitMaxs(maxStack: Int, maxLocals: Int) { 112 | super.visitMaxs(this.maxStack.coerceAtLeast(maxStack), maxLocals) 113 | } 114 | } -------------------------------------------------------------------------------- /asm_example/src/main/java/com/andoter/asm_example/part3/AddTimerAdapter3.kt: -------------------------------------------------------------------------------- 1 | package com.andoter.asm_example.part3 2 | 3 | import com.andoter.asm_example.utils.ADLog 4 | import org.objectweb.asm.* 5 | import org.objectweb.asm.commons.AnalyzerAdapter 6 | import java.lang.reflect.Field 7 | 8 | class AddTimerAdapter3 { 9 | fun addTimer() { 10 | Thread.sleep(200) 11 | } 12 | } 13 | 14 | fun main() { 15 | val classReader = ClassReader("com.andoter.asm_example.part3.AddTimerAdapter3") 16 | val classWriter = ClassWriter(classReader, ClassWriter.COMPUTE_MAXS) 17 | classReader.accept(object : ClassVisitor(Opcodes.ASM7, classWriter) { 18 | var owner: String? = "" 19 | override fun visit( 20 | version: Int, 21 | access: Int, 22 | name: String?, 23 | signature: String?, 24 | superName: String?, 25 | interfaces: Array? 26 | ) { 27 | owner = name 28 | super.visit(version, access, name, signature, superName, interfaces) 29 | ADLog.info("owner = $owner") 30 | } 31 | 32 | override fun visitMethod( 33 | access: Int, 34 | name: String?, 35 | descriptor: String?, 36 | signature: String?, 37 | exceptions: Array? 38 | ): MethodVisitor { 39 | return AddTimerMethodAdapter3( 40 | Opcodes.ASM7, 41 | owner, 42 | access, 43 | name, 44 | descriptor, 45 | cv.visitMethod(access, name, descriptor, signature, exceptions) 46 | ) 47 | } 48 | 49 | override fun visitEnd() { 50 | ADLog.info("visitEnd") 51 | val fieldVisitor = 52 | cv.visitField(Opcodes.ACC_PUBLIC + Opcodes.ACC_STATIC, "time", "J", null, null) 53 | fieldVisitor?.visitEnd() 54 | super.visitEnd() 55 | } 56 | }, ClassReader.SKIP_DEBUG) 57 | 58 | // 通过自定义 Loader 加载修改后的 .class 文件 59 | val loader = MyLoader() 60 | val addTimerAdapterClazz = loader.defineClass( 61 | "com.andoter.asm_example.part3.AddTimerAdapter3", 62 | classWriter.toByteArray() 63 | ) 64 | // 获取 addTimer 方法 65 | val method = addTimerAdapterClazz?.getDeclaredMethod("addTimer") 66 | // 获取 time 字段 67 | val field: Field? = addTimerAdapterClazz?.getDeclaredField("time") 68 | // 触发方法 69 | method?.invoke(addTimerAdapterClazz?.newInstance()) 70 | // 打印 time 耗时 71 | ADLog.error("time = ${field?.getLong(addTimerAdapterClazz?.newInstance())}") 72 | } 73 | 74 | class AddTimerMethodAdapter3( 75 | api: Int, 76 | var owner: String?, 77 | access: Int, 78 | name: String?, 79 | descriptor: String?, 80 | methodVisitor: MethodVisitor? 81 | ) : AnalyzerAdapter(api, owner, access, name, descriptor, methodVisitor) { 82 | 83 | override fun visitCode() { 84 | super.visitCode() 85 | super.visitFieldInsn(Opcodes.GETSTATIC, owner, "time", "J") 86 | super.visitMethodInsn( 87 | Opcodes.INVOKESTATIC, 88 | "java/lang/System", 89 | "currentTimeMillis", 90 | "()J", 91 | false 92 | ) 93 | super.visitInsn(Opcodes.LSUB) 94 | super.visitFieldInsn(Opcodes.PUTSTATIC, owner, "time", "J") 95 | } 96 | 97 | override fun visitInsn(opcode: Int) { 98 | if (opcode >= Opcodes.IRETURN && opcode <= Opcodes.RETURN || opcode == Opcodes.ATHROW) { 99 | super.visitFieldInsn(Opcodes.GETSTATIC, owner, "time", "J") 100 | super.visitMethodInsn(Opcodes.INVOKESTATIC, "java/lang/System", "currentTimeMillis", "()J") 101 | super.visitInsn(Opcodes.LADD) 102 | super.visitFieldInsn(Opcodes.PUTSTATIC, owner, "time", "J") 103 | } 104 | super.visitInsn(opcode) 105 | } 106 | 107 | override fun visitMaxs(maxStack: Int, maxLocals: Int) { 108 | super.visitMaxs(maxStack, maxLocals) 109 | } 110 | } -------------------------------------------------------------------------------- /asm_example/src/main/java/com/andoter/asm_example/part3/AddTimerAdapter4.kt: -------------------------------------------------------------------------------- 1 | package com.andoter.asm_example.part3 2 | 3 | import com.andoter.asm_example.utils.ADLog 4 | import org.objectweb.asm.* 5 | import org.objectweb.asm.commons.LocalVariablesSorter 6 | import java.lang.reflect.Field 7 | 8 | class AddTimerAdapter4 { 9 | fun addTimer() { 10 | Thread.sleep(200) 11 | } 12 | } 13 | 14 | /* 15 | 实现类似如下效果: 16 | public class AddTimerAdapter4 { 17 | public static long timer; 18 | public void addTimer() throws Exception { 19 | long t = System.currentTimeMillis(); 20 | Thread.sleep(200); 21 | timer += System.currentTimeMillis() - t; 22 | } 23 | } 24 | */ 25 | fun main() { 26 | val classReader = ClassReader("com.andoter.asm_example.part3.AddTimerAdapter4") 27 | val classWriter = ClassWriter(classReader, ClassWriter.COMPUTE_MAXS) 28 | classReader.accept(object : ClassVisitor(Opcodes.ASM7, classWriter) { 29 | var owner: String = "" 30 | override fun visit( 31 | version: Int, 32 | access: Int, 33 | name: String?, 34 | signature: String?, 35 | superName: String?, 36 | interfaces: Array? 37 | ) { 38 | owner = name!! 39 | super.visit(version, access, name, signature, superName, interfaces) 40 | ADLog.info("owner = $owner") 41 | } 42 | 43 | override fun visitMethod( 44 | access: Int, 45 | name: String?, 46 | descriptor: String?, 47 | signature: String?, 48 | exceptions: Array? 49 | ): MethodVisitor { 50 | return AddTimerMethodAdapter4( 51 | owner, 52 | access, 53 | descriptor, 54 | cv.visitMethod(access, name, descriptor, signature, exceptions) 55 | ) 56 | } 57 | 58 | override fun visitEnd() { 59 | ADLog.info("visitEnd") 60 | val fieldVisitor = 61 | cv.visitField(Opcodes.ACC_PUBLIC + Opcodes.ACC_STATIC, "timer", "J", null, null) 62 | fieldVisitor?.visitEnd() 63 | super.visitEnd() 64 | } 65 | }, ClassReader.SKIP_DEBUG) 66 | 67 | // 通过自定义 Loader 加载修改后的 .class 文件 68 | val loader = MyLoader() 69 | val addTimerAdapterClazz = loader.defineClass( 70 | "com.andoter.asm_example.part3.AddTimerAdapter4", 71 | classWriter.toByteArray() 72 | ) 73 | // 获取 addTimer 方法 74 | val method = addTimerAdapterClazz?.getDeclaredMethod("addTimer") 75 | // 获取 time 字段 76 | val field: Field? = addTimerAdapterClazz?.getDeclaredField("timer") 77 | // 触发方法 78 | method?.invoke(addTimerAdapterClazz?.newInstance()) 79 | // 打印 time 耗时 80 | ADLog.error("time = ${field?.getLong(addTimerAdapterClazz?.newInstance())}") 81 | } 82 | 83 | class AddTimerMethodAdapter4( 84 | private var owner: String, 85 | access: Int, 86 | descriptor: String?, 87 | methodVisitor: MethodVisitor? 88 | ) : LocalVariablesSorter(Opcodes.ASM7, access, descriptor, methodVisitor ){ 89 | private var time:Int = -1 90 | 91 | override fun visitCode() { 92 | super.visitCode() 93 | mv.visitMethodInsn(Opcodes.INVOKESTATIC, "java/lang/System", "currentTimeMillis", "()J", false) 94 | time = newLocal(Type.LONG_TYPE) 95 | mv.visitVarInsn(Opcodes.LSTORE, time) 96 | } 97 | 98 | override fun visitInsn(opcode: Int) { 99 | if (opcode >= Opcodes.IRETURN && opcode <= Opcodes.RETURN || Opcodes.ATHROW == opcode) { 100 | mv.visitMethodInsn(Opcodes.INVOKESTATIC, "java/lang/System", "currentTimeMillis", "()J", false) 101 | mv.visitVarInsn(Opcodes.LLOAD, time) 102 | mv.visitInsn(Opcodes.LSUB) 103 | mv.visitFieldInsn(Opcodes.GETSTATIC, owner, "timer", "J") 104 | mv.visitInsn(Opcodes.LADD) 105 | mv.visitFieldInsn(Opcodes.PUTSTATIC, owner, "timer", "J") 106 | } 107 | super.visitInsn(opcode) 108 | } 109 | 110 | override fun visitMaxs(maxStack: Int, maxLocals: Int) { 111 | super.visitMaxs(maxStack + 4, maxLocals) 112 | } 113 | } -------------------------------------------------------------------------------- /asm_example/src/main/java/com/andoter/asm_example/part3/AddTimerAdapter5.kt: -------------------------------------------------------------------------------- 1 | package com.andoter.asm_example.part3 2 | 3 | import com.andoter.asm_example.utils.ADLog 4 | import org.objectweb.asm.* 5 | import org.objectweb.asm.commons.AnalyzerAdapter 6 | import org.objectweb.asm.commons.LocalVariablesSorter 7 | import java.lang.reflect.Field 8 | import kotlin.math.max 9 | 10 | class AddTimerAdapter5 { 11 | fun addTimer() { 12 | Thread.sleep(200) 13 | } 14 | } 15 | 16 | /* 17 | 实现类似如下效果: 18 | public class AddTimerAdapter5 { 19 | public static long timer; 20 | public void addTimer() throws Exception { 21 | long t = System.currentTimeMillis(); 22 | Thread.sleep(200); 23 | timer += System.currentTimeMillis() - t; 24 | } 25 | } 26 | */ 27 | fun main() { 28 | val classReader = ClassReader("com.andoter.asm_example.part3.AddTimerAdapter5") 29 | val classWriter = ClassWriter(classReader, ClassWriter.COMPUTE_MAXS) 30 | classReader.accept(object : ClassVisitor(Opcodes.ASM7, classWriter) { 31 | private var isInterface: Boolean = false 32 | var owner: String = "" 33 | override fun visit( 34 | version: Int, 35 | access: Int, 36 | name: String?, 37 | signature: String?, 38 | superName: String?, 39 | interfaces: Array? 40 | ) { 41 | owner = name!! 42 | isInterface = access and Opcodes.ACC_INTERFACE != 0 43 | super.visit(version, access, name, signature, superName, interfaces) 44 | ADLog.info("owner = $owner") 45 | } 46 | 47 | override fun visitMethod( 48 | access: Int, 49 | name: String?, 50 | descriptor: String?, 51 | signature: String?, 52 | exceptions: Array? 53 | ): MethodVisitor { 54 | if (!isInterface && cv != null && name != "") { 55 | val methodAdapter = AddTimerMethodAdapter5( 56 | owner, 57 | cv.visitMethod(access, name, descriptor, signature, exceptions) 58 | ) 59 | methodAdapter.analyzerAdapter = AnalyzerAdapter(owner, access, name, descriptor, methodAdapter) 60 | methodAdapter.localVariablesSorter = LocalVariablesSorter(access, descriptor, methodAdapter) 61 | return methodAdapter.localVariablesSorter!! 62 | } 63 | return super.visitMethod(access, name, descriptor, signature, exceptions) 64 | } 65 | 66 | override fun visitEnd() { 67 | ADLog.info("visitEnd") 68 | val fieldVisitor = 69 | cv.visitField(Opcodes.ACC_PUBLIC + Opcodes.ACC_STATIC, "timer", "J", null, null) 70 | fieldVisitor?.visitEnd() 71 | super.visitEnd() 72 | } 73 | }, ClassReader.SKIP_DEBUG) 74 | 75 | // 通过自定义 Loader 加载修改后的 .class 文件 76 | val loader = MyLoader() 77 | val addTimerAdapterClazz = loader.defineClass( 78 | "com.andoter.asm_example.part3.AddTimerAdapter5", 79 | classWriter.toByteArray() 80 | ) 81 | // 获取 addTimer 方法 82 | val method = addTimerAdapterClazz?.getDeclaredMethod("addTimer") 83 | // 获取 time 字段 84 | val field: Field? = addTimerAdapterClazz?.getDeclaredField("timer") 85 | // 触发方法 86 | method?.invoke(addTimerAdapterClazz?.newInstance()) 87 | // 打印 time 耗时 88 | ADLog.error("time = ${field?.getLong(addTimerAdapterClazz?.newInstance())}") 89 | } 90 | 91 | class AddTimerMethodAdapter5( 92 | private var owner: String, 93 | methodVisitor: MethodVisitor? 94 | ) : MethodVisitor(Opcodes.ASM7, methodVisitor ){ 95 | private var time:Int = -1 96 | var localVariablesSorter: LocalVariablesSorter? = null 97 | var analyzerAdapter: AnalyzerAdapter? = null 98 | private var maxStack: Int = 0 99 | 100 | override fun visitCode() { 101 | super.visitCode() 102 | mv.visitMethodInsn(Opcodes.INVOKESTATIC, "java/lang/System", "currentTimeMillis", "()J", false) 103 | time = localVariablesSorter?.newLocal(Type.LONG_TYPE)!! 104 | mv.visitVarInsn(Opcodes.LSTORE, time) 105 | maxStack = 4 106 | } 107 | 108 | override fun visitInsn(opcode: Int) { 109 | if (opcode >= Opcodes.IRETURN && opcode <= Opcodes.RETURN || Opcodes.ATHROW == opcode) { 110 | mv.visitMethodInsn(Opcodes.INVOKESTATIC, "java/lang/System", "currentTimeMillis", "()J", false) 111 | mv.visitVarInsn(Opcodes.LLOAD, time) 112 | mv.visitInsn(Opcodes.LSUB) 113 | mv.visitFieldInsn(Opcodes.GETSTATIC, owner, "timer", "J") 114 | mv.visitInsn(Opcodes.LADD) 115 | mv.visitFieldInsn(Opcodes.PUTSTATIC, owner, "timer", "J") 116 | maxStack = max(analyzerAdapter!!.stack.size + 4, maxStack) 117 | } 118 | super.visitInsn(opcode) 119 | } 120 | 121 | override fun visitMaxs(maxStack: Int, maxLocals: Int) { 122 | mv.visitMaxs(max(this.maxStack, maxStack), maxLocals) 123 | } 124 | } -------------------------------------------------------------------------------- /asm_example/src/main/java/com/andoter/asm_example/part3/AddTimerAdapter6.kt: -------------------------------------------------------------------------------- 1 | package com.andoter.asm_example.part3 2 | 3 | import com.andoter.asm_example.utils.ADLog 4 | import org.objectweb.asm.* 5 | import org.objectweb.asm.commons.AdviceAdapter 6 | import java.lang.reflect.Field 7 | 8 | /* 9 | AdviceAdapter 是继承自 MethodVisitor 的一个抽象类,可用于在一个方法的开头以及任意 RETURN 或 ATHROW 指令之前插入代码。 10 | 它的优点是可以对构造器也是有效的,在构造器中插入到构造器调用之后。事实上,这个适配器的大多数代码都是用于检测对这个构造器的调用。 11 | 注意,AdviceAdapter 继承自 LocalVariablesSorter,所以也可以轻松完成对一个局部变量的操作。 12 | 13 | */ 14 | class AddTimerAdapter6 { 15 | fun addTimer() { 16 | Thread.sleep(200) 17 | } 18 | } 19 | 20 | /* 21 | 实现类似如下效果: 22 | public class AddTimerAdapter5 { 23 | public static long timer; 24 | public void addTimer() throws Exception { 25 | long t = System.currentTimeMillis(); 26 | Thread.sleep(200); 27 | timer += System.currentTimeMillis() - t; 28 | } 29 | } 30 | */ 31 | fun main() { 32 | val classReader = ClassReader("com.andoter.asm_example.part3.AddTimerAdapter6") 33 | val classWriter = ClassWriter(classReader, ClassWriter.COMPUTE_MAXS) 34 | classReader.accept(object : ClassVisitor(Opcodes.ASM7, classWriter) { 35 | private var isInterface: Boolean = false 36 | var owner: String = "" 37 | override fun visit( 38 | version: Int, 39 | access: Int, 40 | name: String?, 41 | signature: String?, 42 | superName: String?, 43 | interfaces: Array? 44 | ) { 45 | owner = name!! 46 | isInterface = access and Opcodes.ACC_INTERFACE != 0 47 | super.visit(version, access, name, signature, superName, interfaces) 48 | ADLog.info("owner = $owner") 49 | } 50 | 51 | override fun visitMethod( 52 | access: Int, 53 | name: String?, 54 | descriptor: String?, 55 | signature: String?, 56 | exceptions: Array? 57 | ): MethodVisitor { 58 | if (!isInterface && cv != null && name != "") { 59 | return AddTimerMethodAdapter6( 60 | owner, 61 | cv.visitMethod(access, name, descriptor, signature, exceptions), 62 | access, name, descriptor 63 | ) 64 | } 65 | return super.visitMethod(access, name, descriptor, signature, exceptions) 66 | } 67 | 68 | override fun visitEnd() { 69 | ADLog.info("visitEnd") 70 | val fieldVisitor = 71 | cv.visitField(Opcodes.ACC_PUBLIC + Opcodes.ACC_STATIC, "timer", "J", null, null) 72 | fieldVisitor?.visitEnd() 73 | super.visitEnd() 74 | } 75 | }, ClassReader.SKIP_DEBUG) 76 | 77 | // 通过自定义 Loader 加载修改后的 .class 文件 78 | val loader = MyLoader() 79 | val addTimerAdapterClazz = loader.defineClass( 80 | "com.andoter.asm_example.part3.AddTimerAdapter6", 81 | classWriter.toByteArray() 82 | ) 83 | // 获取 addTimer 方法 84 | val method = addTimerAdapterClazz?.getDeclaredMethod("addTimer") 85 | // 获取 time 字段 86 | val field: Field? = addTimerAdapterClazz?.getDeclaredField("timer") 87 | // 触发方法 88 | method?.invoke(addTimerAdapterClazz?.newInstance()) 89 | // 打印 time 耗时 90 | ADLog.error("time = ${field?.getLong(addTimerAdapterClazz?.newInstance())}") 91 | } 92 | 93 | class AddTimerMethodAdapter6( 94 | private var owner: String, 95 | methodVisitor: MethodVisitor?, 96 | access: Int, 97 | name: String?, 98 | descriptor: String? 99 | ) : AdviceAdapter(Opcodes.ASM7, methodVisitor, access, name, descriptor) { 100 | 101 | override fun onMethodEnter() { 102 | mv.visitFieldInsn(GETSTATIC, owner, "timer", "J") 103 | mv.visitMethodInsn( 104 | INVOKESTATIC, "java/lang/System", 105 | "currentTimeMillis", "()J", false 106 | ) 107 | mv.visitInsn(LSUB) 108 | mv.visitFieldInsn(PUTSTATIC, owner, "timer", "J") 109 | } 110 | 111 | override fun onMethodExit(opcode: Int) { 112 | mv.visitFieldInsn(GETSTATIC, owner, "timer", "J") 113 | mv.visitMethodInsn( 114 | INVOKESTATIC, "java/lang/System", 115 | "currentTimeMillis", "()J", false 116 | ) 117 | mv.visitInsn(LADD) 118 | mv.visitFieldInsn(PUTSTATIC, owner, "timer", "J") 119 | } 120 | 121 | override fun visitMaxs(maxStack: Int, maxLocals: Int) { 122 | mv.visitMaxs(maxStack + 4, maxLocals) 123 | } 124 | } -------------------------------------------------------------------------------- /asm_example/src/main/java/com/andoter/asm_example/part3/CheckMethodDemo.kt: -------------------------------------------------------------------------------- 1 | package com.andoter.asm_example.part3 2 | 3 | import org.objectweb.asm.* 4 | import org.objectweb.asm.util.CheckMethodAdapter 5 | 6 | /** 7 | * 在前面的章节中,我们了解到 CheckClassAdapter 用于检查 ClassVisitor 方法调用的顺序是否正确,参数是否有效。 8 | * 同样,对于 MethodVisitor 的方法调用,可以使用 CheckMethodVisitor 来检查一个方法。 9 | */ 10 | class CheckMethodDemo { 11 | 12 | fun checkMethod() { 13 | println("check method is ok") 14 | } 15 | } 16 | 17 | fun main() { 18 | val classReader = ClassReader("com.andoter.asm_example.part3.MyMethodAdapter") 19 | val classWriter = ClassWriter(classReader, ClassWriter.COMPUTE_MAXS) 20 | val classVisitor = object : ClassVisitor(Opcodes.ASM7, classWriter) { 21 | override fun visitMethod( 22 | access: Int, 23 | name: String?, 24 | descriptor: String?, 25 | signature: String?, 26 | exceptions: Array? 27 | ): MethodVisitor { 28 | var methodVisitor = cv.visitMethod(access, name, descriptor, signature, exceptions) 29 | methodVisitor = CheckMethodAdapter(methodVisitor) 30 | return MyMethodAdapter(methodVisitor) 31 | } 32 | } 33 | classReader.accept(classVisitor, ClassReader.SKIP_DEBUG) 34 | } -------------------------------------------------------------------------------- /asm_example/src/main/java/com/andoter/asm_example/part3/Example_3_1_3.kt: -------------------------------------------------------------------------------- 1 | package com.andoter.asm_example.part3 2 | 3 | data class Example_3_1_3(var name:String) 4 | 5 | /* 6 | 通过 Tools -> Kotlin -> Show kotlin Bytecode 查看上面的 data class 的字节码 7 | 8 | // ================com/andoter/asm_example/part3/Example_3_1_3.class ================= 9 | // class version 50.0 (50) 10 | // access flags 0x31 11 | public final class com/andoter/asm_example/part3/Example_3_1_3 { 12 | 13 | 14 | // access flags 0x2 15 | private Ljava/lang/String; name 16 | @Lorg/jetbrains/annotations/NotNull;() // invisible 17 | 18 | // access flags 0x11 19 | public final getName()Ljava/lang/String; 20 | @Lorg/jetbrains/annotations/NotNull;() // invisible 21 | L0 22 | LINENUMBER 3 L0 23 | ALOAD 0 24 | GETFIELD com/andoter/asm_example/part3/Example_3_1_3.name : Ljava/lang/String; 25 | ARETURN 26 | L1 27 | LOCALVARIABLE this Lcom/andoter/asm_example/part3/Example_3_1_3; L0 L1 0 28 | MAXSTACK = 1 29 | MAXLOCALS = 1 30 | 31 | // access flags 0x11 32 | public final setName(Ljava/lang/String;)V 33 | // annotable parameter count: 1 (visible) 34 | // annotable parameter count: 1 (invisible) 35 | @Lorg/jetbrains/annotations/NotNull;() // invisible, parameter 0 36 | L0 37 | ALOAD 1 38 | LDC "" 39 | INVOKESTATIC kotlin/jvm/internal/Intrinsics.checkParameterIsNotNull (Ljava/lang/Object;Ljava/lang/String;)V 40 | L1 41 | LINENUMBER 3 L1 42 | ALOAD 0 43 | ALOAD 1 44 | PUTFIELD com/andoter/asm_example/part3/Example_3_1_3.name : Ljava/lang/String; 45 | RETURN 46 | L2 47 | LOCALVARIABLE this Lcom/andoter/asm_example/part3/Example_3_1_3; L0 L2 0 48 | LOCALVARIABLE Ljava/lang/String; L0 L2 1 49 | MAXSTACK = 2 50 | MAXLOCALS = 2 51 | 52 | // access flags 0x1 53 | public (Ljava/lang/String;)V 54 | // annotable parameter count: 1 (visible) 55 | // annotable parameter count: 1 (invisible) 56 | @Lorg/jetbrains/annotations/NotNull;() // invisible, parameter 0 57 | L0 58 | ALOAD 1 59 | LDC "name" 60 | INVOKESTATIC kotlin/jvm/internal/Intrinsics.checkParameterIsNotNull (Ljava/lang/Object;Ljava/lang/String;)V 61 | L1 62 | LINENUMBER 3 L1 63 | ALOAD 0 64 | INVOKESPECIAL java/lang/Object. ()V 65 | ALOAD 0 66 | ALOAD 1 67 | PUTFIELD com/andoter/asm_example/part3/Example_3_1_3.name : Ljava/lang/String; 68 | RETURN 69 | L2 70 | LOCALVARIABLE this Lcom/andoter/asm_example/part3/Example_3_1_3; L0 L2 0 71 | LOCALVARIABLE name Ljava/lang/String; L0 L2 1 72 | MAXSTACK = 2 73 | MAXLOCALS = 2 74 | 75 | // access flags 0x11 76 | public final component1()Ljava/lang/String; 77 | @Lorg/jetbrains/annotations/NotNull;() // invisible 78 | L0 79 | ALOAD 0 80 | GETFIELD com/andoter/asm_example/part3/Example_3_1_3.name : Ljava/lang/String; 81 | ARETURN 82 | L1 83 | LOCALVARIABLE this Lcom/andoter/asm_example/part3/Example_3_1_3; L0 L1 0 84 | MAXSTACK = 1 85 | MAXLOCALS = 1 86 | 87 | // access flags 0x11 88 | public final copy(Ljava/lang/String;)Lcom/andoter/asm_example/part3/Example_3_1_3; 89 | @Lorg/jetbrains/annotations/NotNull;() // invisible 90 | // annotable parameter count: 1 (visible) 91 | // annotable parameter count: 1 (invisible) 92 | @Lorg/jetbrains/annotations/NotNull;() // invisible, parameter 0 93 | L0 94 | ALOAD 1 95 | LDC "name" 96 | INVOKESTATIC kotlin/jvm/internal/Intrinsics.checkParameterIsNotNull (Ljava/lang/Object;Ljava/lang/String;)V 97 | NEW com/andoter/asm_example/part3/Example_3_1_3 98 | DUP 99 | ALOAD 1 100 | INVOKESPECIAL com/andoter/asm_example/part3/Example_3_1_3. (Ljava/lang/String;)V 101 | ARETURN 102 | L1 103 | LOCALVARIABLE this Lcom/andoter/asm_example/part3/Example_3_1_3; L0 L1 0 104 | LOCALVARIABLE name Ljava/lang/String; L0 L1 1 105 | MAXSTACK = 3 106 | MAXLOCALS = 2 107 | 108 | // access flags 0x1009 109 | public static synthetic copy$default(Lcom/andoter/asm_example/part3/Example_3_1_3;Ljava/lang/String;ILjava/lang/Object;)Lcom/andoter/asm_example/part3/Example_3_1_3; 110 | ILOAD 2 111 | ICONST_1 112 | IAND 113 | IFEQ L0 114 | ALOAD 0 115 | GETFIELD com/andoter/asm_example/part3/Example_3_1_3.name : Ljava/lang/String; 116 | ASTORE 1 117 | L0 118 | ALOAD 0 119 | ALOAD 1 120 | INVOKEVIRTUAL com/andoter/asm_example/part3/Example_3_1_3.copy (Ljava/lang/String;)Lcom/andoter/asm_example/part3/Example_3_1_3; 121 | ARETURN 122 | MAXSTACK = 2 123 | MAXLOCALS = 4 124 | 125 | // access flags 0x1 126 | public toString()Ljava/lang/String; 127 | @Lorg/jetbrains/annotations/NotNull;() // invisible 128 | NEW java/lang/StringBuilder 129 | DUP 130 | INVOKESPECIAL java/lang/StringBuilder. ()V 131 | LDC "Example_3_1_3(name=" 132 | INVOKEVIRTUAL java/lang/StringBuilder.append (Ljava/lang/String;)Ljava/lang/StringBuilder; 133 | ALOAD 0 134 | GETFIELD com/andoter/asm_example/part3/Example_3_1_3.name : Ljava/lang/String; 135 | INVOKEVIRTUAL java/lang/StringBuilder.append (Ljava/lang/String;)Ljava/lang/StringBuilder; 136 | LDC ")" 137 | INVOKEVIRTUAL java/lang/StringBuilder.append (Ljava/lang/String;)Ljava/lang/StringBuilder; 138 | INVOKEVIRTUAL java/lang/StringBuilder.toString ()Ljava/lang/String; 139 | ARETURN 140 | MAXSTACK = 2 141 | MAXLOCALS = 1 142 | 143 | // access flags 0x1 144 | public hashCode()I 145 | ALOAD 0 146 | GETFIELD com/andoter/asm_example/part3/Example_3_1_3.name : Ljava/lang/String; 147 | DUP 148 | IFNULL L0 149 | INVOKEVIRTUAL java/lang/Object.hashCode ()I 150 | GOTO L1 151 | L0 152 | POP 153 | ICONST_0 154 | L1 155 | IRETURN 156 | MAXSTACK = 2 157 | MAXLOCALS = 1 158 | 159 | // access flags 0x1 160 | public equals(Ljava/lang/Object;)Z 161 | @Lorg/jetbrains/annotations/Nullable;() // invisible, parameter 0 162 | ALOAD 0 163 | ALOAD 1 164 | IF_ACMPEQ L0 165 | ALOAD 1 166 | INSTANCEOF com/andoter/asm_example/part3/Example_3_1_3 167 | IFEQ L1 168 | ALOAD 1 169 | CHECKCAST com/andoter/asm_example/part3/Example_3_1_3 170 | ASTORE 2 171 | ALOAD 0 172 | GETFIELD com/andoter/asm_example/part3/Example_3_1_3.name : Ljava/lang/String; 173 | ALOAD 2 174 | GETFIELD com/andoter/asm_example/part3/Example_3_1_3.name : Ljava/lang/String; 175 | INVOKESTATIC kotlin/jvm/internal/Intrinsics.areEqual (Ljava/lang/Object;Ljava/lang/Object;)Z 176 | IFEQ L1 177 | L0 178 | ICONST_1 179 | IRETURN 180 | L1 181 | ICONST_0 182 | IRETURN 183 | MAXSTACK = 2 184 | MAXLOCALS = 3 185 | 186 | @Lkotlin/Metadata;(mv={1, 1, 16}, bv={1, 0, 3}, k=1, d1={"\u0000\"\n\u0002\u0018\u0002\n\u0002\u0010\u0000\n\u0000\n\u0002\u0010\u000e\n\u0002\u0008\u0007\n\u0002\u0010\u000b\n\u0002\u0008\u0002\n\u0002\u0010\u0008\n\u0002\u0008\u0002\u0008\u0086\u0008\u0018\u00002\u00020\u0001B\r\u0012\u0006\u0010\u0002\u001a\u00020\u0003\u00a2\u0006\u0002\u0010\u0004J\u0009\u0010\u0008\u001a\u00020\u0003H\u00c6\u0003J\u0013\u0010\u0009\u001a\u00020\u00002\u0008\u0008\u0002\u0010\u0002\u001a\u00020\u0003H\u00c6\u0001J\u0013\u0010\n\u001a\u00020\u000b2\u0008\u0010\u000c\u001a\u0004\u0018\u00010\u0001H\u00d6\u0003J\u0009\u0010\r\u001a\u00020\u000eH\u00d6\u0001J\u0009\u0010\u000f\u001a\u00020\u0003H\u00d6\u0001R\u001a\u0010\u0002\u001a\u00020\u0003X\u0086\u000e\u00a2\u0006\u000e\n\u0000\u001a\u0004\u0008\u0005\u0010\u0006\"\u0004\u0008\u0007\u0010\u0004\u00a8\u0006\u0010"}, d2={"Lcom/andoter/asm_example/part3/Example_3_1_3;", "", "name", "", "(Ljava/lang/String;)V", "getName", "()Ljava/lang/String;", "setName", "component1", "copy", "equals", "", "other", "hashCode", "", "toString", "asm_example"}) 187 | // compiled from: Example_3_1_3.kt 188 | } 189 | 190 | 191 | // ================META-INF/asm_example.kotlin_module ================= 192 |  193 | 194 | 195 | 196 | 197 | */ -------------------------------------------------------------------------------- /asm_example/src/main/java/com/andoter/asm_example/part3/GenerateMethod.kt: -------------------------------------------------------------------------------- 1 | package com.andoter.asm_example.part3 2 | 3 | import com.andoter.asm_example.utils.ClassOutputUtil 4 | import org.objectweb.asm.ClassWriter 5 | import org.objectweb.asm.Opcodes 6 | import org.objectweb.asm.util.TraceClassVisitor 7 | import java.io.PrintWriter 8 | 9 | class GenerateMethod { 10 | } 11 | 12 | class Bean{ 13 | private var f:Int =1 14 | fun getF(): Int { 15 | return this.f 16 | } 17 | 18 | fun setF(value: Int) { 19 | this.f = value 20 | } 21 | } 22 | 23 | /* 24 | // access flags 0x11 25 | public final getF()I 26 | L0 27 | LINENUMBER 9 L0 28 | ALOAD 0 29 | GETFIELD com/andoter/asm_example/part3/Bean.f : I 30 | IRETURN 31 | L1 32 | LOCALVARIABLE this Lcom/andoter/asm_example/part3/Bean; L0 L1 0 33 | MAXSTACK = 1 34 | MAXLOCALS = 1 35 | */ 36 | fun main() { 37 | val classWriter = ClassWriter(0) 38 | val traceClassWriter = 39 | TraceClassVisitor(classWriter, PrintWriter(System.out)) 40 | traceClassWriter.visit( 41 | Opcodes.V1_7, 42 | Opcodes.ACC_PUBLIC + Opcodes.ACC_FINAL, 43 | "pkg/Bean", 44 | null, 45 | "java/lang/Object", 46 | null 47 | ) 48 | val methodVisitor = traceClassWriter.visitMethod(Opcodes.ACC_PUBLIC + Opcodes.ACC_FINAL, "getF", 49 | "()I", null, null) 50 | methodVisitor.visitCode() 51 | methodVisitor.visitVarInsn(Opcodes.ALOAD, 0) 52 | methodVisitor.visitFieldInsn(Opcodes.GETFIELD, "pkg/Bean", "f", "I") 53 | methodVisitor.visitInsn(Opcodes.IRETURN) 54 | methodVisitor.visitMaxs(1, 1)//定义执行栈帧的局部变量表和操作数栈的大小 55 | methodVisitor.visitEnd() 56 | traceClassWriter.visitEnd() 57 | /* 58 | * 输出的字节码文件: 59 | * package pkg; 60 | public final class Bean { 61 | public final int getF() { 62 | return this.f; 63 | } 64 | } 65 | */ 66 | ClassOutputUtil.byte2File("asm_example/files/Bean.class", classWriter.toByteArray()) 67 | } -------------------------------------------------------------------------------- /asm_example/src/main/java/com/andoter/asm_example/part3/MethodPrint.kt: -------------------------------------------------------------------------------- 1 | package com.andoter.asm_example.part3 2 | 3 | import com.andoter.asm_example.utils.ADLog 4 | import com.andoter.asm_example.utils.AccessCodeUtils 5 | import org.objectweb.asm.* 6 | import kotlin.math.max 7 | import org.objectweb.asm.ClassVisitor as ClassVisitor1 8 | 9 | 10 | class MethodDemo { 11 | var name = "method" 12 | fun printName(value:String) { 13 | val change = "change" 14 | val result = if(change.length > name.length) change else name 15 | name = "$result + ${max(5,6)}" 16 | println(name) 17 | } 18 | 19 | override fun toString(): String { 20 | return "name" 21 | } 22 | } 23 | 24 | 25 | fun main() { 26 | val classReader = ClassReader("com.andoter.asm_example.part3.MethodDemo") 27 | val classVisitor = object : ClassVisitor1(Opcodes.ASM7) { 28 | override fun visitMethod( 29 | access: Int, 30 | name: String?, 31 | descriptor: String?, 32 | signature: String?, 33 | exceptions: Array? 34 | ): MethodVisitor { 35 | ADLog.info("开始扫描方法:$name") 36 | val mv = super.visitMethod(access, name, descriptor, signature, exceptions) 37 | return MethodPrint(Opcodes.ASM7, mv) 38 | } 39 | } 40 | 41 | classReader.accept(classVisitor, ClassReader.SKIP_DEBUG) 42 | } 43 | 44 | /** 45 | * MethodVisit 首先会访问注释和属性信息,然后才是方法的字节码,这些访问顺序在 visitCode 和 visitMaxs 调用之间。所以 46 | * 这两个方法可以用于检测字节码在访问序列中的开始和结束。ASM 中提供了三个基于 MethodVisitor API 的组件,用于生成和转化方法。 47 | * 48 | * - ClassReader 类分析已编译方法的内容,其在 accept 方法的参数中传递了 ClassVisitor,ClassReader 类将针对这一 ClassVisitor 返回的 49 | * MethodVisitor 对象调用响应的方法。 50 | * - ClassWriter 的 visitMethod 方法返回 MethodVisitor 接口的一个实现,它直接以二进制形式生成已编译方法 51 | * - MethodVisitor类将它接收到的所有方法调用委托给另一个MethodVisitor方法。可以将它看作一个事件筛选器。 52 | * 53 | * MethodVisitor 回调方法有: 54 | * - visitParameter:访问方法一个参数 55 | * - visitAnnotationDefualt:访问注解接口方法的默认值 56 | * - visitAnnotaion:访问方法的一个注解 57 | * - visitTypeAnnotation:访问方法签名上的一个类型的注解 58 | * - visitAnnotableParameterCount:访问注解参数数量,就是访问方法参数有注解参数个数 59 | * - visitParameterAnnotation:访问参数的注解,返回一个 AnnotationVisitor 可以访问该注解值 60 | * - visitAttribute:访问方法的属性 61 | * - visitCode:开始访问方法代码,此处可以添加方法运行前拦截器 62 | * - visitFrame:访问方法局部变量的当前状态以及操作栈成员信息 63 | * - visitIntInsn:访问数值类型指令,当 int 取值-1~5采用 ICONST 指令,取值 -128~127 采用 BIPUSH 指令,取值 -32768~32767 采用 SIPUSH 指令,取值 -2147483648~2147483647 采用 ldc 指令。 64 | * - visitVarInsn:访问本地变量类型指令 65 | * - visitTypeInsn:访问类型指令,类型指令会把类的内部名称当成参数 Type 66 | * - visitFieldInsn:域操作指令,用来加载或者存储对象的 Field 67 | * - visitMethodInsn:访问方法操作指令 68 | * - visitDynamicInsn:访问动态类型指令 69 | * - visitJumpInsn:访问比较跳转指令 70 | * - visitLabelInsn:访问 label,当会在调用该方法后访问该label标记一个指令 71 | * - visitLdcInsn:访问 LDC 指令,也就是访问常量池索引 72 | * - visitLineNumber:访问行号描述 73 | * - visitMaxs:访问操作数栈最大值和本地变量表最大值 74 | * - visitLocalVariable:访问本地变量描述 75 | */ 76 | class MethodPrint(api: Int, methodVisitor: MethodVisitor?) : MethodVisitor(api, methodVisitor) { 77 | 78 | override fun visitMultiANewArrayInsn(descriptor: String?, numDimensions: Int) { 79 | super.visitMultiANewArrayInsn(descriptor, numDimensions) 80 | ADLog.info("visitMultiANewArrayInsn, descriptor = $descriptor, numDimensions = $numDimensions") 81 | } 82 | 83 | override fun visitFrame( 84 | type: Int, 85 | numLocal: Int, 86 | local: Array?, 87 | numStack: Int, 88 | stack: Array? 89 | ) { 90 | super.visitFrame(type, numLocal, local, numStack, stack) 91 | ADLog.info("visitFrame, type = $type, numLocal = $numLocal, local.size = $(local.size), numStack = $numStack") 92 | } 93 | 94 | override fun visitVarInsn(opcode: Int, `var`: Int) { 95 | super.visitVarInsn(opcode, `var`) 96 | ADLog.info("visitVarInsn, opcode = ${AccessCodeUtils.opcode2String(opcode)}, var = $`var`") 97 | } 98 | 99 | override fun visitTryCatchBlock(start: Label?, end: Label?, handler: Label?, type: String?) { 100 | super.visitTryCatchBlock(start, end, handler, type) 101 | ADLog.info("visitTryCatchBlock") 102 | } 103 | 104 | override fun visitLookupSwitchInsn(dflt: Label?, keys: IntArray?, labels: Array?) { 105 | super.visitLookupSwitchInsn(dflt, keys, labels) 106 | ADLog.info("visitLookupSwitchInsn") 107 | } 108 | 109 | override fun visitJumpInsn(opcode: Int, label: Label?) { 110 | super.visitJumpInsn(opcode, label) 111 | ADLog.info("visitJumpInsn, opcode = ${AccessCodeUtils.opcode2String(opcode)}") 112 | } 113 | 114 | override fun visitLdcInsn(value: Any?) { 115 | super.visitLdcInsn(value) 116 | ADLog.info("visitLdcInsn, value = $value") 117 | } 118 | 119 | override fun visitAnnotableParameterCount(parameterCount: Int, visible: Boolean) { 120 | super.visitAnnotableParameterCount(parameterCount, visible) 121 | } 122 | 123 | override fun visitIntInsn(opcode: Int, operand: Int) { 124 | super.visitIntInsn(opcode, operand) 125 | ADLog.info("visitIntInsn, opcode = ${AccessCodeUtils.opcode2String(opcode)}, operand = $operand") 126 | } 127 | 128 | override fun visitTypeInsn(opcode: Int, type: String?) { 129 | super.visitTypeInsn(opcode, type) 130 | ADLog.info("visitTypeInsn, opcode = ${AccessCodeUtils.opcode2String(opcode)}, type = $type") 131 | } 132 | 133 | override fun visitAnnotationDefault(): AnnotationVisitor? { 134 | ADLog.info("visitAnnotationDefault") 135 | return super.visitAnnotationDefault() 136 | } 137 | 138 | override fun visitAnnotation(descriptor: String?, visible: Boolean): AnnotationVisitor? { 139 | ADLog.info("visitAnnotation") 140 | return super.visitAnnotation(descriptor, visible) 141 | } 142 | 143 | override fun visitTypeAnnotation( 144 | typeRef: Int, 145 | typePath: TypePath?, 146 | descriptor: String?, 147 | visible: Boolean 148 | ): AnnotationVisitor? { 149 | ADLog.info("visitTypeAnnotation") 150 | return super.visitTypeAnnotation(typeRef, typePath, descriptor, visible) 151 | } 152 | 153 | override fun visitMaxs(maxStack: Int, maxLocals: Int) { 154 | super.visitMaxs(maxStack, maxLocals) 155 | ADLog.info("visitMaxs") 156 | } 157 | 158 | override fun visitInvokeDynamicInsn( 159 | name: String?, 160 | descriptor: String?, 161 | bootstrapMethodHandle: Handle?, 162 | vararg bootstrapMethodArguments: Any? 163 | ) { 164 | ADLog.info("visitInvokeDynamicInsn, name = $name, descriptor = $descriptor, bootstrapMethodHandle = ${bootstrapMethodHandle?.name}") 165 | super.visitInvokeDynamicInsn( 166 | name, 167 | descriptor, 168 | bootstrapMethodHandle, 169 | *bootstrapMethodArguments 170 | ) 171 | } 172 | 173 | override fun visitLabel(label: Label?) { 174 | super.visitLabel(label) 175 | ADLog.info("visitLabel") 176 | } 177 | 178 | override fun visitTryCatchAnnotation( 179 | typeRef: Int, 180 | typePath: TypePath?, 181 | descriptor: String?, 182 | visible: Boolean 183 | ): AnnotationVisitor? { 184 | return super.visitTryCatchAnnotation(typeRef, typePath, descriptor, visible) 185 | } 186 | 187 | override fun visitMethodInsn(opcode: Int, owner: String?, name: String?, descriptor: String?) { 188 | super.visitMethodInsn(opcode, owner, name, descriptor) 189 | ADLog.info("visitMethodInsn, opcode = ${AccessCodeUtils.opcode2String(opcode)}, owner = $owner, name = $name, descriptor = $descriptor") 190 | } 191 | 192 | override fun visitMethodInsn( 193 | opcode: Int, 194 | owner: String?, 195 | name: String?, 196 | descriptor: String?, 197 | isInterface: Boolean 198 | ) { 199 | super.visitMethodInsn(opcode, owner, name, descriptor, isInterface) 200 | ADLog.info("visitMethodInsn, opcode = ${AccessCodeUtils.opcode2String(opcode)}, owner = $owner, name = $name, descriptor = $descriptor") 201 | } 202 | 203 | override fun visitInsn(opcode: Int) { 204 | super.visitInsn(opcode) 205 | ADLog.info("visitInsn, opcode = ${AccessCodeUtils.opcode2String(opcode)}") 206 | } 207 | 208 | override fun visitInsnAnnotation( 209 | typeRef: Int, 210 | typePath: TypePath?, 211 | descriptor: String?, 212 | visible: Boolean 213 | ): AnnotationVisitor? { 214 | ADLog.info("visitInsnAnnotation") 215 | return super.visitInsnAnnotation(typeRef, typePath, descriptor, visible) 216 | } 217 | 218 | override fun visitParameterAnnotation( 219 | parameter: Int, 220 | descriptor: String?, 221 | visible: Boolean 222 | ): AnnotationVisitor? { 223 | ADLog.info("visitParameterAnnotation") 224 | return super.visitParameterAnnotation(parameter, descriptor, visible) 225 | } 226 | 227 | override fun visitIincInsn(`var`: Int, increment: Int) { 228 | super.visitIincInsn(`var`, increment) 229 | 230 | ADLog.info("visitIincInsn") 231 | } 232 | 233 | override fun visitLineNumber(line: Int, start: Label?) { 234 | super.visitLineNumber(line, start) 235 | ADLog.info("visitLineNumber") 236 | } 237 | 238 | override fun visitLocalVariableAnnotation( 239 | typeRef: Int, 240 | typePath: TypePath?, 241 | start: Array?, 242 | end: Array?, 243 | index: IntArray?, 244 | descriptor: String?, 245 | visible: Boolean 246 | ): AnnotationVisitor? { 247 | ADLog.info("visitLocalVariableAnnotation") 248 | return super.visitLocalVariableAnnotation( 249 | typeRef, 250 | typePath, 251 | start, 252 | end, 253 | index, 254 | descriptor, 255 | visible 256 | ) 257 | } 258 | 259 | override fun visitTableSwitchInsn(min: Int, max: Int, dflt: Label?, vararg labels: Label?) { 260 | super.visitTableSwitchInsn(min, max, dflt, *labels) 261 | ADLog.info("visitTableSwitchInsn") 262 | } 263 | 264 | override fun visitEnd() { 265 | super.visitEnd() 266 | ADLog.info("visitEnd") 267 | } 268 | 269 | override fun visitLocalVariable( 270 | name: String?, 271 | descriptor: String?, 272 | signature: String?, 273 | start: Label?, 274 | end: Label?, 275 | index: Int 276 | ) { 277 | super.visitLocalVariable(name, descriptor, signature, start, end, index) 278 | ADLog.info( 279 | "visitLocalVariable, name = $name, descriptor = $descriptor, " + 280 | "signature = $signature, start = $start + end = $end, index = $index" 281 | ) 282 | } 283 | 284 | override fun visitParameter(name: String?, access: Int) { 285 | super.visitParameter(name, access) 286 | ADLog.info("visitParameter, name = $name, access = ${AccessCodeUtils.accCode2String(access)}") 287 | } 288 | 289 | override fun visitAttribute(attribute: Attribute?) { 290 | super.visitAttribute(attribute) 291 | ADLog.info("visitAttribute") 292 | } 293 | 294 | override fun visitFieldInsn(opcode: Int, owner: String?, name: String?, descriptor: String?) { 295 | super.visitFieldInsn(opcode, owner, name, descriptor) 296 | ADLog.info("visitFieldInsn, opcode = ${AccessCodeUtils.opcode2String(opcode)}, owner = $owner, name = $name, descriptor = $descriptor") 297 | } 298 | 299 | override fun visitCode() { 300 | super.visitCode() 301 | ADLog.info("visitCode") 302 | } 303 | } -------------------------------------------------------------------------------- /asm_example/src/main/java/com/andoter/asm_example/part3/RemoveAddZeroAdapter.kt: -------------------------------------------------------------------------------- 1 | package com.andoter.asm_example.part3 2 | 3 | import com.andoter.asm_example.utils.ClassOutputUtil 4 | import org.objectweb.asm.* 5 | 6 | /* 7 | 在上一节中,我们对 AddTimer 的操作称之为无状态转换。 8 | 无状态转换即在局部的转换中,不会依赖当前指令访问之前的指令的转换。比如我们在方法的开头添加代码,每个方法都会添加。 9 | 10 | 比如我们要删除所有出现 ICONST_0 IADD 的序列,因为这个操作就是加入 0,没什么意义。所以当访问到 IADD 时,上一条指令 11 | 是 ICONST_0,则进行指令删除。这样就需要存储上个指令的,像这种转换称之为有状态转换。 12 | */ 13 | class RemoveAddZeroAdapter { 14 | fun addNumber(number:Int) { 15 | var result = number + 0 16 | println("result = $result") 17 | } 18 | } 19 | 20 | /* 21 | 针对 RemoteAddZeroAdapter 类的 addNumber 方法作为例子,转换后的字节码片段如下: 22 | ILOAD 1 23 | ICONST_0 24 | IADD 25 | ISTORE 2 26 | */ 27 | fun main() { 28 | val classReader = ClassReader("com.andoter.asm_example.part3.RemoveAddZeroAdapter") 29 | val classWriter = ClassWriter(classReader, ClassWriter.COMPUTE_MAXS) 30 | val classVisitor = object : ClassVisitor(Opcodes.ASM7, classWriter) { 31 | override fun visitMethod( 32 | access: Int, 33 | name: String?, 34 | descriptor: String?, 35 | signature: String?, 36 | exceptions: Array? 37 | ): MethodVisitor { 38 | val methodVisitor = cv.visitMethod(access, name, descriptor, signature, exceptions) 39 | return RemoteZeroAdapter(methodVisitor) 40 | } 41 | } 42 | classReader.accept(classVisitor, ClassReader.SKIP_DEBUG) 43 | /* 44 | 输出 .class 文件,然后通过 javap -v 查看 45 | stack=2, locals=5, args_size=2 46 | 0: iload_1 47 | 1: istore_2 48 | 2: new #8 // class java/lang/StringBuilder 49 | 5: dup 50 | 6: invokespecial #12 // Method java/lang/StringBuilder."":()V 51 | 9: ldc #14 // String result = 52 | 11: invokevirtual #18 53 | */ 54 | ClassOutputUtil.byte2File("asm_example/files/RemoteZeroAdapter.class", classWriter.toByteArray()) 55 | } 56 | 57 | abstract class PatternMethodVisitor(api: Int, methodVisitor: MethodVisitor?) : 58 | MethodVisitor(api, methodVisitor) { 59 | protected val SEEN_NOTHING= 0 60 | protected var state:Int = 0 61 | override fun visitInsn(opcode: Int) { 62 | visitInsn() 63 | super.visitInsn(opcode) 64 | } 65 | 66 | override fun visitIntInsn(opcode: Int, operand: Int) { 67 | visitInsn() 68 | super.visitIntInsn(opcode, operand) 69 | } 70 | 71 | abstract fun visitInsn() 72 | } 73 | 74 | class RemoteZeroAdapter(methodVisitor: MethodVisitor?) : PatternMethodVisitor(Opcodes.ASM7, methodVisitor = methodVisitor) { 75 | private val SEEN_ICONST_0 = 1 76 | 77 | override fun visitInsn(opcode: Int) { 78 | if (state == SEEN_ICONST_0) { 79 | if (opcode == Opcodes.IADD) { 80 | state = SEEN_NOTHING 81 | return 82 | } 83 | } 84 | 85 | visitInsn() 86 | 87 | if (opcode == Opcodes.ICONST_0) { 88 | state = SEEN_ICONST_0 89 | return 90 | } 91 | mv.visitInsn(opcode) 92 | } 93 | 94 | override fun visitInsn() { 95 | if (state == SEEN_ICONST_0) { 96 | mv.visitInsn(Opcodes.ICONST_0) 97 | } 98 | state = SEEN_NOTHING 99 | } 100 | } 101 | -------------------------------------------------------------------------------- /asm_example/src/main/java/com/andoter/asm_example/part3/RemoveNopAdapter.kt: -------------------------------------------------------------------------------- 1 | package com.andoter.asm_example.part3 2 | 3 | import com.andoter.asm_example.utils.ClassOutputUtil 4 | import org.objectweb.asm.* 5 | 6 | /** 7 | * 在前面的章节中,我们可以对 ClassVisitor 的方法进行拦截不转发,达到删除方法的目的。 8 | * 同样对于 MethodVisitor 也可以将收到的方法回调进行修改,比如改变参数、删除某次调用的指令、 9 | * 接收到的调用之间插入代码、增加新的指令等。 10 | */ 11 | class RemoveNopAdapter(api: Int, methodVisitor: MethodVisitor?) : 12 | MethodVisitor(api, methodVisitor) { 13 | 14 | override fun visitInsn(opcode: Int) { 15 | if (opcode != Opcodes.NOP) { 16 | mv.visitInsn(opcode) 17 | } 18 | } 19 | } 20 | 21 | class RemoveNopAdapterVisitor(api: Int, classVisitor: ClassVisitor?) : ClassVisitor(api, classVisitor) { 22 | override fun visitMethod( 23 | access: Int, 24 | name: String?, 25 | descriptor: String?, 26 | signature: String?, 27 | exceptions: Array? 28 | ): MethodVisitor? { 29 | if (name == "trimToSize") { 30 | return null 31 | } 32 | 33 | val methodVisitor = cv?.visitMethod(access, name, descriptor, signature, exceptions) 34 | if (methodVisitor != null) { 35 | return RemoveNopAdapter(Opcodes.ASM7, methodVisitor) 36 | } 37 | return super.visitMethod(access, name, descriptor, signature, exceptions) 38 | } 39 | } 40 | 41 | fun main() { 42 | val classReader = ClassReader("java.util.ArrayList") 43 | val classWriter = ClassWriter(ClassWriter.COMPUTE_MAXS) 44 | val remoteVisitor = RemoveNopAdapterVisitor(Opcodes.ASM7, classWriter) 45 | classReader.accept(remoteVisitor, ClassReader.SKIP_DEBUG) 46 | 47 | ClassOutputUtil.byte2File("asm_example/files/ArrayList.class", classWriter.toByteArray()) 48 | } -------------------------------------------------------------------------------- /asm_example/src/main/java/com/andoter/asm_example/part3/TraceMethodVisitorDemo.kt: -------------------------------------------------------------------------------- 1 | package com.andoter.asm_example.part3 2 | 3 | import org.objectweb.asm.* 4 | import org.objectweb.asm.util.Textifier 5 | import org.objectweb.asm.util.TraceMethodVisitor 6 | import java.util.* 7 | 8 | 9 | /* 10 | TraceMethodVisitor 用于跟踪某一方法链接调用处的内容,而不是跟踪类的所有类容。 11 | */ 12 | class TraceMethodVisitorDemo { 13 | fun visitorMethod() { 14 | println("访问方法的内容") 15 | println("当前事件 = ${Date().time}") 16 | } 17 | } 18 | 19 | fun main() { 20 | val classReader = ClassReader("com.andoter.asm_example.part3.TraceMethodVisitorDemo") 21 | val classWriter = ClassWriter(classReader, ClassWriter.COMPUTE_MAXS) 22 | val classVisitor = object : ClassVisitor(Opcodes.ASM7, classWriter) { 23 | override fun visitMethod( 24 | access: Int, 25 | name: String?, 26 | descriptor: String?, 27 | signature: String?, 28 | exceptions: Array? 29 | ): MethodVisitor { 30 | var methodVisitor = cv.visitMethod(access, name, descriptor, signature, exceptions) 31 | if (methodVisitor != null) { 32 | val printer = object : Textifier(Opcodes.ASM7) { 33 | override fun visitMethodEnd() { 34 | super.visitMethodEnd() 35 | println(this.stringBuilder.toString()) 36 | } 37 | } 38 | methodVisitor = MyMethodAdapter(TraceMethodVisitor(methodVisitor, printer)) 39 | return methodVisitor 40 | } 41 | return super.visitMethod(access, name, descriptor, signature, exceptions) 42 | } 43 | } 44 | classReader.accept(classVisitor, ClassReader.SKIP_DEBUG) 45 | } 46 | 47 | class MyMethodAdapter(methodVisitor: MethodVisitor) : MethodVisitor(Opcodes.ASM7, methodVisitor) { 48 | override fun visitCode() { 49 | mv.visitCode() 50 | } 51 | 52 | override fun visitInsn(opcode: Int) { 53 | mv.visitInsn(opcode) 54 | } 55 | 56 | override fun visitLdcInsn(value: Any?) { 57 | if (value is String) { 58 | value.replace("事件", "时间") 59 | } 60 | mv.visitLdcInsn(value) 61 | } 62 | 63 | override fun visitEnd() { 64 | mv.visitEnd() 65 | } 66 | } -------------------------------------------------------------------------------- /asm_example/src/main/java/com/andoter/asm_example/part3/TypeDemo.kt: -------------------------------------------------------------------------------- 1 | package com.andoter.asm_example.part3 2 | 3 | import org.objectweb.asm.Opcodes 4 | import org.objectweb.asm.Type 5 | 6 | /* 7 | 前面 2.3 中介绍的 Type 工具类同样可以用作方法 8 | */ 9 | class TypeDemo { 10 | fun test() { 11 | 12 | } 13 | } 14 | 15 | fun main() { 16 | 17 | val type = Type.FLOAT_TYPE.getOpcode(Opcodes.IMUL) 18 | println(type) 19 | Type.getArgumentTypes(TypeDemo::class.java.getDeclaredMethod("test", null)) 20 | } -------------------------------------------------------------------------------- /asm_example/src/main/java/com/andoter/asm_example/part3/字节码指令: -------------------------------------------------------------------------------- 1 | ### 3.1.1 Java 虚拟机 2 | 在 Java 虚拟机栈中,最小的执行单位是栈帧。通常一个栈帧由局部变量表、操作数栈、动态链接和方法返回地址信息。每个方法的执行到结束都对应着一个栈帧在虚拟机的入栈和出栈过程。 3 | 4 | #### 局部变量表 5 | 局部变量表是一组变量值的存储空间,用于存放方法参数和方法内部定义的局部变量。**在 .java 编译成 .class 文件时,已经确定了所需要分配的局部变量表和操作数栈的大小。** 6 | 7 | **变量槽** 8 | 9 | 局部变量表的容量单位是变量槽(Variable Slot)。每个变量槽最多可以存储 32 为长度的空间,所以对于 byte、char、boolean、short、int、float、reference 是占用一个变量槽, 10 | 对于 64 位长度的 long 和 double 占用连个变量槽。 11 | 使用局部变量表时,通过索引定位对应数据的位置,索引值的范围是从 0 开始至局部变量表最大的变量槽数量。 12 | 如果访问的是 32 位数据类型的变量,索引 N 就代表了使用第 N 个变量槽,如果访问的是 64 位数据类型的变量,则说明会同时使用第 N 和 N+1 两个变量槽。 13 | 对于两个相邻的共同存放一个 64 位数据的两个变量槽,虚拟机不允许采用任何方式单独访问其中的某一个,如果遇到进行这种操作的字节码,Java 虚拟机就会在类加载的校验阶段中抛出异常。 14 | 15 | 当一个方法被调用时,会使用局部变量表来完成参数值到参数变量列表的传递过程。如果执行的是对象实例的成员方法(不是 static 修饰的方法),那么局部变量表中第 0 位索引的变量槽默认就是该对象实例的引用(this),在方法中可以通过关键字 this 来访问到这个隐含的参数。 16 | 其余参数则按照参数表顺序排列,参数表分配完毕后,再根据方法体内部定义的局部变量顺序和作用域分配其余的变量槽。 17 | 为了尽可能节省栈帧所耗的内存空间,局部变量表中的变量槽是可以重用的,当方法体中定义的局部变量超出其作用域时,该局部变量对应的变量槽就可以交给其他变量来重用。 18 | 19 | **变量槽复用** 20 | 21 | 为了节省栈帧空间,局部变量表中的变量槽是可以复用的,当 PC 计数器的指令执行完毕时,这个变量槽就可以交给其它变量复用。 22 | 23 | #### 操作数栈 24 | 操作数栈和局部变量表一样,在编译时期就已经确定所需要分配的操作数栈的最大容量。 25 | 操作数栈的每一个位置可以是任意的 `Java` 数据类型,`32` 位数据类型所占的栈容量为 `1`,`64` 位数据类型占用的栈容量为 `2`。 26 | 27 | 当一个方法开始执行的时候,所对应的操作数栈是空的,在方法执行的过程中,会有各种字节码指令往操作数栈中写入和提取内容,也就是出栈 / 入栈操作,最终操作数栈的值也在发生改变。 28 | 29 | ### 3.1.2 字节码指令 30 | 字节码指令由一个标识该指令的操作码和固定数目的参数组成: 31 | - 操作码是一个无符号字节值——即字节代码名,由注记符号标识 32 | - 参数是静态值,确定了精确的指令行为,紧跟在操作码之后 33 | 字节码指令大致可以分为两类,一类指令用于局部变量和操作数栈之间传递值。另一类用于对操作数栈的值 34 | 进行弹出和计算,并压入栈中。 35 | 36 | 常见的局部变量操作指令有: 37 | - ILOAD:用于加载 boolean、int、byte、short 和 char 类型的局部变量到操作数栈 38 | - FLOAD:用于加载 float 类型局部变量到操作数栈 39 | - LLOAD:用于加载 lang 类型局部变量到操作数栈,需要加载两个槽 slot 40 | - DLOAD:用于加载 double 类型局部变量到操作数栈,需要加载两个槽 slot 41 | - ALOAD:用于加载非基础类型的局部变量到操作数栈,比如对象之类的 42 | 43 | 常见的操作数栈指令有: 44 | - ISTORE:从操作数栈弹出 boolean、int、byte、short 和 char 类型的局部变量,并将它存储在由其索引 i 指定的局部变量中 45 | - FSTORE:从操作数栈弹出 float 类型的局部变量,并将它存储在由其索引 i 指定的局部变量中 46 | - LSTORE:从操作数栈弹出 long 类型的局部变量,并将它存储在由其索引 i 指定的局部变量中 47 | - DSTORE:从操作数栈弹出 double 类型的局部变量,并将它存储在由其索引 i 指定的局部变量中 48 | - ASTORE:用于弹出非基础类型的局部变量,并将它存储在由其索引 i 指定的局部变量中 49 | 50 | 通过上面可以看到,每种对应的数据类型都对应不同的 `XLOAD` 或 `XSTORE`。这是为了保证不会执行 51 | 非法的转换。 52 | - 将一个值存储在局部变量表中,在以不同的类型加载它是非法操作,比如存入 ISTORE 类型,使用 FLOAD 加载 53 | - 如果向一个局部变量表中的位置存储一个值,而这个值不同于原来的存储类型,这种操作是合法的 54 | 55 | **以上两个特性,意味着:一个局部变量的类型,可能在方法执行期间发生变化。**比如你在方法的最后读取局部变量,可能实际类型 56 | 已经发生变化。 57 | 58 | [常见字节码指令介绍可以参照 Java 虚拟机指令](https://docs.oracle.com/javase/specs/jvms/se7/html/index.html) 59 | 60 | 61 | 62 | -------------------------------------------------------------------------------- /asm_example/src/main/java/com/andoter/asm_example/part4/AddAnnotationAdapter.kt: -------------------------------------------------------------------------------- 1 | package com.andoter.asm_example.part4 2 | 3 | import com.andoter.asm_example.part3.MyLoader 4 | import com.andoter.asm_example.utils.ADLog 5 | import com.andoter.asm_example.utils.ClassOutputUtil 6 | import org.objectweb.asm.* 7 | 8 | /** 9 | * 类的注解添加要复杂一些,因为存在一些限制条件:必须调用 ClassVisitor 类的方法 10 | */ 11 | class AddAnnotationAdapter { 12 | } 13 | 14 | @Target(AnnotationTarget.FUNCTION, AnnotationTarget.CLASS) 15 | @Retention(AnnotationRetention.RUNTIME) 16 | annotation class NewAPI 17 | 18 | fun main() { 19 | val classReader = ClassReader("com.andoter.asm_example.part4.AddAnnotationAdapter") 20 | val classWriter = ClassWriter(classReader, ClassWriter.COMPUTE_MAXS) 21 | val addAnnotationAdapter = AddAnnotationAdapterVisitor(classWriter, "Lcom/andoter/asm_example/part4/NewAPI;") 22 | classReader.accept(addAnnotationAdapter, ClassReader.SKIP_DEBUG) 23 | //输出文件查看 24 | ClassOutputUtil.byte2File("asm_example/files/AddAnnotation.class", classWriter.toByteArray()) 25 | val loadClass = MyLoader() 26 | val classAdapter = loadClass.defineClass("com.andoter.asm_example.part4.AddAnnotationAdapter",classWriter.toByteArray()) 27 | val method = classAdapter?.declaredMethods 28 | 29 | val annotations = classAdapter?.annotations 30 | 31 | val getAnnotation = classAdapter?.getDeclaredAnnotation(NewAPI::class.java) 32 | if (getAnnotation != null) { 33 | println("newApi") 34 | } else { 35 | println("NoAPI") 36 | } 37 | } 38 | 39 | 40 | class AddAnnotationAdapterVisitor(classVisitor: ClassVisitor, val annoDesc: String) : ClassVisitor(Opcodes.ASM7,classVisitor) { 41 | private var isAnnotationPresent = false 42 | override fun visitInnerClass( 43 | name: String?, 44 | outerName: String?, 45 | innerName: String?, 46 | access: Int 47 | ) { 48 | addAnnotation() 49 | cv.visitInnerClass(name, outerName, innerName, access) 50 | } 51 | 52 | override fun visit( 53 | version: Int, 54 | access: Int, 55 | name: String?, 56 | signature: String?, 57 | superName: String?, 58 | interfaces: Array? 59 | ) { 60 | cv.visit(version, access, name, signature, superName, interfaces) 61 | } 62 | 63 | override fun visitField( 64 | access: Int, 65 | name: String?, 66 | descriptor: String?, 67 | signature: String?, 68 | value: Any? 69 | ): FieldVisitor { 70 | addAnnotation() 71 | return cv.visitField(access, name, descriptor, signature, value) 72 | } 73 | 74 | override fun visitMethod( 75 | access: Int, 76 | name: String?, 77 | descriptor: String?, 78 | signature: String?, 79 | exceptions: Array? 80 | ): MethodVisitor { 81 | addAnnotation() 82 | return cv.visitMethod(access, name, descriptor, signature, exceptions) 83 | } 84 | 85 | override fun visitEnd() { 86 | addAnnotation() 87 | cv.visitEnd() 88 | } 89 | 90 | override fun visitAnnotation(descriptor: String?, visible: Boolean): AnnotationVisitor { 91 | ADLog.info("descriptor = $descriptor") 92 | if (visible and (annoDesc == descriptor)) { 93 | isAnnotationPresent = true 94 | } 95 | return super.visitAnnotation(descriptor, visible) 96 | } 97 | 98 | private fun addAnnotation() { 99 | if (!isAnnotationPresent) { 100 | ADLog.info("descriptor = $annoDesc") 101 | cv.visitAnnotation(annoDesc, true)?.visitEnd() 102 | isAnnotationPresent = true 103 | } 104 | } 105 | } -------------------------------------------------------------------------------- /asm_example/src/main/java/com/andoter/asm_example/part4/AnnotationDemo.kt: -------------------------------------------------------------------------------- 1 | package com.andoter.asm_example.part4 2 | 3 | import com.andoter.asm_example.utils.ADLog 4 | import com.andoter.asm_example.utils.ClassOutputUtil 5 | import org.objectweb.asm.* 6 | 7 | /** 8 | * 在 Java 中有很多注解供开发者使用,比如 @exception、@author 等。 9 | * 同时也可以使用元注解进行自定义注解的声明定义。 10 | * 在 ASM 中提供了 AnnotationVisitor 类用于注解的访问,可以访问注解的键值对。 11 | */ 12 | @Andoter(authorities = "andoter") 13 | class AnnotationDemo { 14 | 15 | override fun toString(): String { 16 | return super.toString() 17 | } 18 | 19 | @Andoter(authorities = "andoter") 20 | fun annotationTest() { 21 | 22 | } 23 | } 24 | 25 | @Target(AnnotationTarget.FUNCTION, AnnotationTarget.CLASS) 26 | @Retention(AnnotationRetention.RUNTIME) 27 | annotation class Andoter (val authorities: String) 28 | 29 | fun main() { 30 | val classReader = ClassReader("com.andoter.asm_example.part4.AnnotationDemo") 31 | val classWriter = ClassWriter(classReader, ClassWriter.COMPUTE_MAXS) 32 | val removeAnnotationAdapter = RemoveAnnotationAdapter(classWriter, "Lcom/andoter/asm_example/part4/Andoter") 33 | classReader.accept(removeAnnotationAdapter, ClassReader.SKIP_DEBUG) 34 | //输出文件查看 35 | ClassOutputUtil.byte2File("asm_example/files/RemoteAnnotation.class", classWriter.toByteArray()) 36 | } 37 | 38 | class RemoveAnnotationAdapter(classVisitor : ClassVisitor, var annotation: String) : ClassVisitor(Opcodes.ASM7, classVisitor) { 39 | 40 | override fun visitAnnotation(descriptor: String?, visible: Boolean): AnnotationVisitor? { 41 | ADLog.info("descriptor = $descriptor") 42 | if (descriptor == annotation) { 43 | return null 44 | } 45 | return cv.visitAnnotation(descriptor, visible) 46 | } 47 | } -------------------------------------------------------------------------------- /asm_example/src/main/java/com/andoter/asm_example/part4/AnnotationPrinter.kt: -------------------------------------------------------------------------------- 1 | package com.andoter.asm_example.part4 2 | 3 | import com.andoter.asm_example.utils.ADLog 4 | import org.objectweb.asm.* 5 | @Deprecated(message = "deprecated") 6 | class AnnotationPrinter { 7 | } 8 | 9 | fun main() { 10 | val classReader = ClassReader("com.andoter.asm_example.part4.AnnotationPrinter") 11 | val classWriter = ClassWriter(classReader, ClassWriter.COMPUTE_MAXS) 12 | classReader.accept(object : ClassVisitor(Opcodes.ASM7, classWriter) { 13 | override fun visitAnnotation(descriptor: String?, visible: Boolean): AnnotationVisitor { 14 | return AnnotationPrinterVisitor(cv.visitAnnotation(descriptor, visible)) 15 | } 16 | }, ClassReader.SKIP_DEBUG) 17 | } 18 | 19 | 20 | class AnnotationPrinterVisitor(annotationVisitor: AnnotationVisitor) : AnnotationVisitor(Opcodes.ASM7, annotationVisitor) { 21 | 22 | override fun visitEnd() { 23 | super.visitEnd() 24 | ADLog.info("visitEnd") 25 | } 26 | 27 | override fun visitAnnotation(name: String?, descriptor: String?): AnnotationVisitor { 28 | ADLog.info("visitAnnotation, name = $name, descriptor = $descriptor") 29 | return super.visitAnnotation(name, descriptor) 30 | } 31 | 32 | override fun visitEnum(name: String?, descriptor: String?, value: String?) { 33 | ADLog.info("visitEnum, name = $name, descriptor = $descriptor, value = $value") 34 | super.visitEnum(name, descriptor, value) 35 | } 36 | 37 | override fun visit(name: String?, value: Any?) { 38 | super.visit(name, value) 39 | ADLog.info("visit, name = $name, value = $value") 40 | } 41 | 42 | override fun visitArray(name: String?): AnnotationVisitor { 43 | ADLog.info("visitArray, name = $name") 44 | return super.visitArray(name) 45 | } 46 | } -------------------------------------------------------------------------------- /asm_example/src/main/java/com/andoter/asm_example/part4/CheckAnnotationAdapterDemo.kt: -------------------------------------------------------------------------------- 1 | package com.andoter.asm_example.part4 2 | 3 | import org.objectweb.asm.ClassWriter 4 | import org.objectweb.asm.Opcodes 5 | import org.objectweb.asm.util.CheckAnnotationAdapter 6 | 7 | class CheckAnnotationAdapterDemo { 8 | } 9 | 10 | fun main() { 11 | CheckAnnotation.dump() 12 | } 13 | 14 | /** 15 | * 通过代码示例创建一个注解类,一个无值,一个具有枚举值。同样对于注解也有:TraceAnnotationVisitor 和 CheckAnnotationAdapter 用于 16 | * 输出和检测是否合法正确 17 | */ 18 | object CheckAnnotation : Opcodes { 19 | fun dump(): ByteArray { 20 | val classWriter = ClassWriter(0) 21 | classWriter.visit(Opcodes.ASM7, Opcodes.ACC_PUBLIC + Opcodes.ACC_ANNOTATION + Opcodes.ACC_ABSTRACT + Opcodes.ACC_INTERFACE, 22 | "java/lang/Deprecated", null, "java/lang/Object", arrayOf( "java/lang/annotation/Annotation" ) 23 | ) 24 | var av = classWriter.visitAnnotation("Ljava/lang/annotation/Documented;", true) 25 | var checkAnnotationVisitor = CheckAnnotationAdapter(av) 26 | checkAnnotationVisitor.visitEnd() 27 | 28 | av = classWriter.visitAnnotation("Ljava/lang/annotation/Retention;", true) 29 | checkAnnotationVisitor = CheckAnnotationAdapter(av) 30 | checkAnnotationVisitor.visitEnum("value", "Ljava/lang/annotation/RetentionPolicy;", 31 | "RUNTIME") 32 | checkAnnotationVisitor.visitEnd() 33 | 34 | classWriter.visitEnd() 35 | return classWriter.toByteArray() 36 | } 37 | } -------------------------------------------------------------------------------- /asm_example/src/main/java/com/andoter/asm_example/part4/DeprecatedDump.kt: -------------------------------------------------------------------------------- 1 | package com.andoter.asm_example.part4 2 | 3 | import com.andoter.asm_example.utils.ClassOutputUtil 4 | import org.objectweb.asm.ClassWriter 5 | import org.objectweb.asm.Opcodes 6 | 7 | class DeprecatedDump { 8 | } 9 | 10 | fun main() { 11 | val deprecatedDump = DeprecatedDumpAdapter.dump() 12 | //输出文件查看 13 | ClassOutputUtil.byte2File("asm_example/files/DeprecatedDumpAdapter.class", deprecatedDump) 14 | /* 15 | 查看输出的文件: 16 | @Documented 17 | @Retention(RetentionPolicy.RUNTIME) 18 | public @interface Deprecated { 19 | } 20 | */ 21 | } 22 | 23 | /** 24 | * 通过代码示例创建一个注解类,一个无值,一个具有枚举值。同样对于注解也有:TraceAnnotationVisitor 和 CheckAnnotationAdapter 用于 25 | * 输出和检测是否合法正确 26 | */ 27 | object DeprecatedDumpAdapter : Opcodes { 28 | fun dump(): ByteArray { 29 | val classWriter = ClassWriter(0) 30 | classWriter.visit(Opcodes.ASM7, Opcodes.ACC_PUBLIC + Opcodes.ACC_ANNOTATION + Opcodes.ACC_ABSTRACT + Opcodes.ACC_INTERFACE, 31 | "java/lang/Deprecated", null, "java/lang/Object", arrayOf( "java/lang/annotation/Annotation" ) 32 | ) 33 | var av = classWriter.visitAnnotation("Ljava/lang/annotation/Documented;", true) 34 | av.visitEnd() 35 | 36 | av = classWriter.visitAnnotation("Ljava/lang/annotation/Retention;", true) 37 | av.visitEnum("value", "Ljava/lang/annotation/RetentionPolicy;", 38 | "RUNTIME") 39 | av.visitEnd() 40 | 41 | classWriter.visitEnd() 42 | return classWriter.toByteArray() 43 | } 44 | } -------------------------------------------------------------------------------- /asm_example/src/main/java/com/andoter/asm_example/part4/SignatureGeneric.kt: -------------------------------------------------------------------------------- 1 | package com.andoter.asm_example.part4 2 | 3 | import org.objectweb.asm.Opcodes 4 | import org.objectweb.asm.signature.SignatureReader 5 | import org.objectweb.asm.signature.SignatureVisitor 6 | import org.objectweb.asm.signature.SignatureWriter 7 | 8 | /* 9 | 在 ASM 的 API 中提供了 SignatureVisitor 类用于泛型的签名访问。 10 | 访问类型签名的顺序: 11 | visitBaseType | visitArrayType | visitTypeVariable | 12 | ( visitClassType visitTypeArgument* 13 | ( visitInnerClassType visitTypeArgument* )* visitEnd ) ) 14 | 15 | 访问方法签名的顺序: 16 | ( visitFormalTypeParameter visitClassBound? visitInterfaceBound* )* 17 | visitParameterType* visitReturnType visitExceptionType* 18 | 19 | 访问类签名的顺序: 20 | ( visitFormalTypeParameter visitClassBound? visitInterfaceBound* )* 21 | visitSuperClass visitInterface* 22 | 23 | 和 ClassVisitor 一样,ASM 基于 SignatureVisitor 提供了 SignatureReader 和 SignatureWriter 用于分析和生成签名。 24 | */ 25 | class SignatureGeneric { 26 | fun addNumber(number: T): T { 27 | println(number) 28 | return number 29 | } 30 | } 31 | 32 | fun main() { 33 | val s = "Ljava/util/HashMap.HashIterator;" 34 | val renaming = mutableMapOf() 35 | renaming["java/util/HashMap"] = "A" 36 | renaming["java/util/HashMap.HashIterator"] = "B" 37 | val signatureWrite = SignatureWriter() 38 | val renameSignatureAdapter = RenameSignatureAdapter(signatureWrite, renaming) 39 | val signatureReader = SignatureReader(s) 40 | signatureReader.accept(renameSignatureAdapter) 41 | println("result = $signatureWrite") 42 | } 43 | 44 | class RenameSignatureAdapter(api: Int) : SignatureVisitor(api) { 45 | private lateinit var signatureVisitor:SignatureVisitor 46 | private var renaming: Map?= null 47 | private var oldName : String? = null 48 | 49 | constructor( 50 | signatureVisitor: SignatureVisitor, 51 | renaming: Map 52 | ) : this(Opcodes.ASM7) { 53 | this.signatureVisitor = signatureVisitor 54 | this.renaming = renaming 55 | } 56 | 57 | override fun visitFormalTypeParameter(name: String?) { 58 | signatureVisitor.visitFormalTypeParameter(name) 59 | } 60 | 61 | override fun visitClassBound(): SignatureVisitor { 62 | signatureVisitor.visitClassBound() 63 | return this 64 | } 65 | 66 | override fun visitInterfaceBound(): SignatureVisitor { 67 | signatureVisitor.visitInterfaceBound() 68 | return this 69 | } 70 | 71 | override fun visitClassType(name: String?) { 72 | oldName = name 73 | val newName = renaming?.get(oldName) 74 | signatureVisitor.visitClassType(if (name == null) name else newName) 75 | } 76 | 77 | override fun visitInnerClassType(name: String?) { 78 | oldName = "$oldName.$name" 79 | val temName = oldName 80 | val newName = renaming?.get(temName) 81 | signatureVisitor.visitInnerClassType(if (name == null) name else newName) 82 | } 83 | 84 | override fun visitTypeArgument() { 85 | signatureVisitor.visitTypeArgument() 86 | } 87 | 88 | override fun visitTypeArgument(wildcard: Char): SignatureVisitor { 89 | return signatureVisitor.visitTypeArgument(wildcard) 90 | } 91 | 92 | override fun visitEnd() { 93 | signatureVisitor.visitEnd() 94 | } 95 | } -------------------------------------------------------------------------------- /asm_example/src/main/java/com/andoter/asm_example/part4/TraceAnnotationVisitorDemo.kt: -------------------------------------------------------------------------------- 1 | package com.andoter.asm_example.part4 2 | 3 | import org.objectweb.asm.ClassWriter 4 | import org.objectweb.asm.Opcodes 5 | import org.objectweb.asm.util.Textifier 6 | import org.objectweb.asm.util.TraceAnnotationVisitor 7 | 8 | class TraceAnnotationVisitorDemo { 9 | } 10 | 11 | fun main() { 12 | TraceAnnotationAdapter.dump() 13 | } 14 | 15 | /** 16 | * 通过代码示例创建一个注解类,一个无值,一个具有枚举值。同样对于注解也有:TraceAnnotationVisitor 和 CheckAnnotationAdapter 用于 17 | * 输出和检测是否合法正确 18 | */ 19 | object TraceAnnotationAdapter : Opcodes { 20 | fun dump(): ByteArray { 21 | val classWriter = ClassWriter(0) 22 | val printer = object : Textifier(Opcodes.ASM7) { 23 | override fun visitAnnotationEnd() { 24 | super.visitAnnotationEnd() 25 | println(this.stringBuilder.toString()) 26 | } 27 | } 28 | var traceAnnotationVisitor : TraceAnnotationVisitor 29 | classWriter.visit(Opcodes.ASM7, Opcodes.ACC_PUBLIC + Opcodes.ACC_ANNOTATION + Opcodes.ACC_ABSTRACT + Opcodes.ACC_INTERFACE, 30 | "java/lang/Deprecated", null, "java/lang/Object", arrayOf( "java/lang/annotation/Annotation" ) 31 | ) 32 | var av = classWriter.visitAnnotation("Ljava/lang/annotation/Documented;", true) 33 | traceAnnotationVisitor = TraceAnnotationVisitor(av, printer) 34 | traceAnnotationVisitor.visitEnd() 35 | 36 | av = classWriter.visitAnnotation("Ljava/lang/annotation/Retention;", true) 37 | traceAnnotationVisitor = TraceAnnotationVisitor(av, printer) 38 | traceAnnotationVisitor.visitEnum("value", "Ljava/lang/annotation/RetentionPolicy;", 39 | "RUNTIME") 40 | traceAnnotationVisitor.visitEnd() 41 | 42 | classWriter.visitEnd() 43 | return classWriter.toByteArray() 44 | } 45 | } -------------------------------------------------------------------------------- /asm_example/src/main/java/com/andoter/asm_example/part5/向后兼容: -------------------------------------------------------------------------------- 1 | ### 5.1 引言 2 | 随着 Java 不断的发展,在对类文件格式上也是不断补充新元素。比如 Java 9 引入的模块化(module)的概念,Java 类型的注释、Lambda 语法 等等。 3 | 到 ASM3.X 时,每次 Java 新增的变化都会导致 ASM API 向后兼容,非常不友好。到 ASM4.0 时,引入了一种新机制用于确保后面的版本到 4 | ASM4 都是后向兼容的。即为某个特定的 ASM 版本编写的类解析器,可在未来任何版本中使用。但是这些不仅仅靠 ASM 进行约束,还需要开发者遵守一些 5 | 简单的规则。 6 | > ASM 4.0 中引入的后向兼容机制要求将 ClassVisitor 、 FieldVisitor 、MethodVisitor 等由接口变为抽象类,具有一个以 ASM 版本为参数的构造器。 7 | 8 | 由此引入的后向兼容约定如下: 9 | - 如果本地集成使用 ASM 的版本为 x,那么本地编写代码时不能指定 ASM 版本大于 x,比如如果在 ClassReader.accept() 中指定了大于 x 的类作为输出,会失败报错 10 | - 如果本地集成使用 ASM 的版本为 x 编写的代码,对于未来任意大于 x 的 ASM 版本,该代码都可以不加修改的正常运行 11 | - 使用 ASM 的版本 x 编写的代码,当输入类的声明版本为 y 时,但只使用了 x 或以前版本的功能,则该代码可以在 y 版本或未来任何版本运行 12 | - 对于为 ASM X 编写的代码,如果使用了大于 x 版本号的功能,则改代码必然失败 13 | > 注意:最后三点与类生成器(ClassWriter) 无关 14 | 15 | ### 5.2 规则 16 | 规则 1: 17 | 要为 ASM X 编写一个 ClassVisitor 子类,就以这个版本号为参数,调用 ClassVisitor 构造器,在这个版本的 ClassVisitor 类中,绝对不要重写或调用弃用的方 18 | 法(或者将在之后版本引入的方法)。 19 | 20 | 规则 2: 21 | 不要使用访问器的继承,而要使用委托(即访问器链)。一种好的做法是让你的访问器类在默认情况为 final 的,以确保这一特性。 -------------------------------------------------------------------------------- /asm_example/src/main/java/com/andoter/asm_example/part6/AddFieldDemo.kt: -------------------------------------------------------------------------------- 1 | package com.andoter.asm_example.part6 2 | 3 | import com.andoter.asm_example.utils.ClassOutputUtil 4 | import org.objectweb.asm.ClassReader 5 | import org.objectweb.asm.ClassWriter 6 | import org.objectweb.asm.Opcodes 7 | import org.objectweb.asm.tree.ClassNode 8 | import org.objectweb.asm.tree.FieldNode 9 | 10 | /* 11 | 在使用树 API 进行类转换时,所花费的时间和内存都比使用核心 API 的多,但是使用树 API 进行转换时跟简单一些. 12 | 比如对于整个类添加数字签名,使用核心 API 需要将整个类访问完之后才能添加,这个时候就晚了,但是对于树 API 则没有这种限制. 13 | 事实上,对于这种转换,使用核心 API 需要先用 ClassReader 将类扫描一遍,然后在结合 ClassWriter 等转换链完成转换。 14 | 15 | 结论是:树 API 通常用于那些不能由核心 API 一次实现的转换。 16 | */ 17 | 18 | class AddFieldDemo { 19 | } 20 | 21 | fun main() { 22 | val classReader = ClassReader("com.andoter.asm_example.part6.AddFieldDemo") 23 | val classNode = ClassNode() 24 | val addFieldTransform = 25 | AddFieldTransform(Opcodes.ACC_PUBLIC + Opcodes.ACC_STATIC, "addField", "Ljava/lang/String;") 26 | classReader.accept(classNode, ClassReader.SKIP_DEBUG) 27 | addFieldTransform.transform(classNode) 28 | 29 | // 尝试输出进行查看 30 | val classWriter = ClassWriter(ClassWriter.COMPUTE_MAXS) 31 | classNode.accept(classWriter) 32 | //输出文件查看 33 | ClassOutputUtil.byte2File("asm_example/files/AddFieldDemo.class", classWriter.toByteArray()) 34 | } 35 | 36 | class AddFieldTransform(var filedAccess: Int, var fieldName: String, var fieldDesc: String) : 37 | ClassTransformer() { 38 | 39 | override fun transform(cn: ClassNode?) { 40 | var isPresent = false 41 | if (cn?.fields != null) { 42 | for (fieldNode in cn.fields) { 43 | if (fieldNode.name == fieldName) { 44 | isPresent = true 45 | break; 46 | } 47 | } 48 | if (!isPresent) { 49 | cn.fields.add(FieldNode(filedAccess, fieldName, fieldDesc, null, null)) 50 | } 51 | } 52 | super.transform(cn) 53 | } 54 | } -------------------------------------------------------------------------------- /asm_example/src/main/java/com/andoter/asm_example/part6/CreateClass.kt: -------------------------------------------------------------------------------- 1 | package com.andoter.asm_example.part6 2 | 3 | import org.objectweb.asm.Opcodes 4 | import org.objectweb.asm.tree.ClassNode 5 | import org.objectweb.asm.tree.FieldNode 6 | import org.objectweb.asm.tree.MethodNode 7 | 8 | class CreateClass { 9 | } 10 | 11 | /* 12 | 使用树 API 生成类的过程,就是创建一个 ClassNode 对象,然后初始化它的字段。还是以 2.2.3 中的例子说明: 13 | package pkg; 14 | public interface Comparable extends Measurable { 15 | int LESS = -1; 16 | int EQUAL = 0; 17 | int GREATER = 1; 18 | int compareTo(Object o); 19 | } 20 | 21 | 使用树 API 生成类时,需要大约多花费 30% 的时间,占用的内存也比较多。但是可以按照任意顺序生成元素,一些情况下可能比较方便。 22 | */ 23 | fun main() { 24 | val classNode = ClassNode() 25 | classNode.version = Opcodes.V1_5 26 | classNode.access = Opcodes.ACC_PUBLIC + Opcodes.ACC_INTERFACE + Opcodes.ACC_ABSTRACT 27 | classNode.name = "pkg/Comparable" 28 | classNode.superName = "java/lang/Object" 29 | classNode.interfaces.add("pkg/Measurable") 30 | classNode.fields.add( 31 | FieldNode( 32 | Opcodes.ACC_PUBLIC + Opcodes.ACC_FINAL + Opcodes.ACC_STATIC, 33 | "LESS", 34 | "I", 35 | null, 36 | -1 37 | ) 38 | ) 39 | classNode.fields.add( 40 | FieldNode( 41 | Opcodes.ACC_PUBLIC + Opcodes.ACC_FINAL + Opcodes.ACC_STATIC, 42 | "EQUAL", 43 | "I", 44 | null, 45 | 0 46 | ) 47 | ) 48 | classNode.fields.add( 49 | FieldNode( 50 | Opcodes.ACC_PUBLIC + Opcodes.ACC_FINAL + Opcodes.ACC_STATIC, 51 | "GREATER", 52 | "I", 53 | null, 54 | 1 55 | ) 56 | ) 57 | classNode.methods.add( 58 | MethodNode( 59 | Opcodes.ACC_PUBLIC + Opcodes.ACC_ABSTRACT, 60 | "compareTo", 61 | "(Ljava/lang/Object;)I", 62 | null, 63 | null 64 | ) 65 | ) 66 | } -------------------------------------------------------------------------------- /asm_example/src/main/java/com/andoter/asm_example/part6/PatternDemo.kt: -------------------------------------------------------------------------------- 1 | package com.andoter.asm_example.part6 2 | 3 | import org.objectweb.asm.ClassReader 4 | import org.objectweb.asm.ClassVisitor 5 | import org.objectweb.asm.ClassWriter 6 | import org.objectweb.asm.Opcodes 7 | import org.objectweb.asm.tree.ClassNode 8 | 9 | class PatternDemo { 10 | } 11 | 12 | fun main() { 13 | val classWriter = ClassWriter(0) 14 | val myClassAdapter = MyClassAdapter(classWriter) 15 | val classReader = ClassReader("com.andoter.asm_example.part6.PatternDemo") 16 | classReader.accept(myClassAdapter, ClassReader.SKIP_DEBUG) 17 | val bytes = classWriter.toByteArray() 18 | } 19 | 20 | class MyClassAdapter(var classVisitor: ClassVisitor) : ClassNode(Opcodes.ASM7) { 21 | 22 | override fun visitEnd() { 23 | // put your transformation code here 24 | accept(cv) 25 | } 26 | } -------------------------------------------------------------------------------- /asm_example/src/main/java/com/andoter/asm_example/part6/RemoveMethodDemo.kt: -------------------------------------------------------------------------------- 1 | package com.andoter.asm_example.part6 2 | 3 | import com.andoter.asm_example.utils.ADLog 4 | import com.andoter.asm_example.utils.ClassOutputUtil 5 | import org.objectweb.asm.ClassReader 6 | import org.objectweb.asm.ClassWriter 7 | import org.objectweb.asm.tree.ClassNode 8 | 9 | class RemoveMethodDemo { 10 | fun test() { 11 | 12 | } 13 | } 14 | 15 | fun main() { 16 | val removeMethod = RemoveMethodTransformer("test", "()V") 17 | val classReader = ClassReader("com.andoter.asm_example.part6.RemoveMethodDemo") 18 | val classNode = ClassNode() 19 | classReader.accept(classNode, ClassReader.SKIP_DEBUG) 20 | removeMethod.transform(classNode) 21 | 22 | // 尝试输出进行查看 23 | val classWriter = ClassWriter(ClassWriter.COMPUTE_MAXS) 24 | classNode.accept(classWriter) 25 | //输出文件查看 26 | ClassOutputUtil.byte2File("asm_example/files/RemoveMethodDemo.class", classWriter.toByteArray()) 27 | } 28 | 29 | open class ClassTransformer() { 30 | protected var ct: ClassTransformer? = null 31 | open fun transform(cn: ClassNode?) { 32 | if (ct != null) { 33 | ct!!.transform(cn) 34 | } 35 | } 36 | } 37 | 38 | class RemoveMethodTransformer( 39 | var methodName: String, 40 | var methodDesc: String 41 | ) : ClassTransformer() { 42 | 43 | override fun transform(cn: ClassNode?) { 44 | val methodNodes = cn?.methods 45 | if (methodNodes != null) { 46 | for (methodNode in methodNodes) { 47 | ADLog.info("node = " + methodNode.name + ", desc = " + methodNode.desc) 48 | if (methodNode.name == methodName && methodNode.desc == methodDesc) { 49 | methodNodes.remove(methodNode) 50 | } 51 | } 52 | } 53 | } 54 | } -------------------------------------------------------------------------------- /asm_example/src/main/java/com/andoter/asm_example/part6/TreeAPI.kt: -------------------------------------------------------------------------------- 1 | package com.andoter.asm_example.part6 2 | 3 | import org.objectweb.asm.ClassReader 4 | import org.objectweb.asm.ClassWriter 5 | import org.objectweb.asm.Opcodes 6 | import org.objectweb.asm.tree.ClassNode 7 | import org.objectweb.asm.tree.FieldNode 8 | import org.objectweb.asm.tree.MethodNode 9 | 10 | /* 11 | ClassNode 代表着一个类文件,是 ClassVisitor 的一个子类。内部包含了 name、access 等 class 文件的信息 12 | 13 | FiledNode 代表着一个字段 Field 信息,是 FieldVisitor 的子类 14 | public class FieldNode extends FieldVisitor { 15 | 16 | public int access; 17 | 18 | public String name; 19 | 20 | public String desc; 21 | 22 | public String signature; 23 | } 24 | 25 | MethodNode 代表着一个方法信息,是 MethodVisitor 的子类。 26 | */ 27 | class TreeAPI { 28 | } 29 | 30 | fun main() { 31 | // ClassNode 与 ClassReader 的结合转换 32 | val classNode = ClassNode() 33 | val classReader = ClassReader("") 34 | classReader.accept(classNode, ClassReader.SKIP_DEBUG) 35 | 36 | // ClassNode 与 ClassWriter 的结合 37 | val classNode1 = ClassNode() 38 | val classWriter = ClassWriter(ClassWriter.COMPUTE_MAXS) 39 | classNode1.accept(classWriter) 40 | 41 | val fieldNode = FieldNode(Opcodes.ACC_PUBLIC, "field", "Ljava/lang/String", null, "field") 42 | val methodNode = MethodNode() 43 | } -------------------------------------------------------------------------------- /asm_example/src/main/java/com/andoter/asm_example/part7/AddTimerTransformer.kt: -------------------------------------------------------------------------------- 1 | package com.andoter.asm_example.part7 2 | 3 | import com.andoter.asm_example.part3.MyLoader 4 | import com.andoter.asm_example.part6.ClassTransformer 5 | import com.andoter.asm_example.utils.ADLog 6 | import com.andoter.asm_example.utils.ClassOutputUtil 7 | import org.objectweb.asm.ClassReader 8 | import org.objectweb.asm.ClassWriter 9 | import org.objectweb.asm.Opcodes 10 | import org.objectweb.asm.tree.* 11 | import java.lang.reflect.Field 12 | 13 | class AddTimerMethodTree { 14 | fun testTimer() { 15 | Thread.sleep(1000) 16 | } 17 | } 18 | 19 | fun main() { 20 | val classNode = ClassNode() 21 | val classReader = ClassReader("com.andoter.asm_example.part7.AddTimerMethodTree") 22 | classReader.accept(classNode, 0) 23 | val addTimerTransformer = AddTimerTransformer() 24 | addTimerTransformer.transform(classNode) 25 | 26 | val classWriter = ClassWriter(ClassWriter.COMPUTE_MAXS) 27 | classNode.accept(classWriter) 28 | //输出文件查看 29 | ClassOutputUtil.byte2File("asm_example/files/AddTimerMethodTree.class", classWriter.toByteArray()) 30 | 31 | // 通过自定义 Loader 加载修改后的 .class 文件 32 | val loader = MyLoader() 33 | val addTimerAdapterClazz = loader.defineClass("com.andoter.asm_example.part7.AddTimerMethodTree", classWriter.toByteArray()) 34 | // 获取 addTimer 方法 35 | val method = addTimerAdapterClazz?.getDeclaredMethod("testTimer") 36 | // 获取 time 字段 37 | val field : Field? = addTimerAdapterClazz?.getDeclaredField("timer") 38 | // 触发方法 39 | method?.invoke(addTimerAdapterClazz.newInstance()) 40 | // 打印 time 耗时 41 | ADLog.error("timer = ${field?.getLong(addTimerAdapterClazz.newInstance())}") 42 | } 43 | 44 | 45 | class AddTimerTransformer : ClassTransformer() { 46 | 47 | override fun transform(cn: ClassNode?) { 48 | if (cn?.methods != null) { 49 | for (methodNode in cn.methods) { 50 | ADLog.info("name = ${methodNode.name}") 51 | if ("" == methodNode.name || "" == methodNode.name) { 52 | continue 53 | } 54 | val insns = methodNode.instructions 55 | if (insns.size() == 0) { 56 | continue 57 | } 58 | 59 | val iterator = insns.iterator() 60 | while (iterator.hasNext()) { 61 | val inNode = iterator.next() 62 | val opCode = inNode.opcode 63 | if (opCode >= Opcodes.IRETURN && opCode <= Opcodes.RETURN || opCode == Opcodes.ATHROW) { 64 | val insnList = InsnList() 65 | insnList.add(FieldInsnNode(Opcodes.GETSTATIC, cn.name, "timer", "J")) 66 | insnList.add( 67 | MethodInsnNode( 68 | Opcodes.INVOKESTATIC, 69 | "java/lang/System", 70 | "currentTimeMillis", 71 | "()J", 72 | false 73 | ) 74 | ) 75 | insnList.add(InsnNode(Opcodes.LADD)) 76 | insnList.add(FieldInsnNode(Opcodes.PUTSTATIC, cn.name, "timer", "J")) 77 | insns.insert(inNode.previous, insnList) // 在当前的指令前面插入代码 78 | } 79 | } 80 | 81 | val insnListAfter = InsnList() 82 | insnListAfter.add(FieldInsnNode(Opcodes.GETSTATIC, cn.name, "timer", "J")) 83 | insnListAfter.add( 84 | MethodInsnNode( 85 | Opcodes.INVOKESTATIC, 86 | "java/lang/System", 87 | "currentTimeMillis", 88 | "()J", 89 | false 90 | ) 91 | ) 92 | insnListAfter.add(InsnNode(Opcodes.LSUB)) 93 | insnListAfter.add(FieldInsnNode(Opcodes.PUTSTATIC, cn.name, "timer", "J")) 94 | insns.insert(insnListAfter) 95 | methodNode.maxStack += 4 96 | } 97 | val acc = Opcodes.ACC_PUBLIC + Opcodes.ACC_STATIC 98 | cn.fields.add(FieldNode(acc, "timer", "J", null, null)) 99 | } 100 | } 101 | } -------------------------------------------------------------------------------- /asm_example/src/main/java/com/andoter/asm_example/part7/MakeMethod.kt: -------------------------------------------------------------------------------- 1 | package com.andoter.asm_example.part7 2 | 3 | import org.objectweb.asm.Opcodes 4 | import org.objectweb.asm.tree.* 5 | 6 | class MakeMethod { 7 | private var f : Int = 0 8 | fun checkAndSetF(f: Int) { 9 | if (f >= 0) { 10 | this.f = f 11 | } else { 12 | throw IllegalArgumentException() 13 | } 14 | } 15 | } 16 | /* 17 | 参照 3.1.5 小节中的代码: 18 | public void checkAndSetF(int f) { 19 | if (f >= 0) { 20 | this.f = f; 21 | } else { 22 | throw new IllegalArgumentException(); 23 | } 24 | } 25 | 这个新 setter 方法的字节代码如下: 26 | ILOAD 1 27 | IFLT label 28 | ALOAD 0 29 | ILOAD 1 30 | PUTFIELD pkg/Bean f I 31 | GOTO end 32 | label: 33 | NEW java/lang/IllegalArgumentException 34 | DUP 35 | INVOKESPECIAL java/lang/IllegalArgumentException ()V 36 | ATHROW 37 | end: 38 | RETURN 39 | */ 40 | fun main() { 41 | val mn = MethodNode() 42 | val insnList = mn.instructions 43 | insnList.add(VarInsnNode(Opcodes.ILOAD, 1)) 44 | val label = LabelNode() 45 | insnList.add(JumpInsnNode(Opcodes.IFLE, label)) 46 | insnList.add(VarInsnNode(Opcodes.ALOAD, 0)) 47 | insnList.add(VarInsnNode(Opcodes.ILOAD, 1)) 48 | insnList.add(FieldInsnNode(Opcodes.PUTFIELD, "pkg/Bean", "f", "I")) 49 | val endLabel = LabelNode() 50 | insnList.add(JumpInsnNode(Opcodes.GOTO, endLabel)) 51 | insnList.add(endLabel) 52 | insnList.add(FrameNode(Opcodes.F_SAME, 0, null, 0, null)) 53 | insnList.add(TypeInsnNode(Opcodes.NEW, "java/lang/IllegalArgumentException")) 54 | insnList.add(InsnNode(Opcodes.DUP)) 55 | insnList.add(MethodInsnNode(Opcodes.INVOKESPECIAL, "java/lang/IllegalArgumentException", "","()V")) 56 | insnList.add(InsnNode(Opcodes.ATHROW)) 57 | insnList.add(endLabel) 58 | insnList.add(FrameNode(Opcodes.F_SAME, 0 ,null, 0, null)) 59 | insnList.add(InsnNode(Opcodes.RETURN)) 60 | mn.maxLocals = 2 61 | mn.maxStack = 2 62 | } -------------------------------------------------------------------------------- /asm_example/src/main/java/com/andoter/asm_example/part7/MethodNodeAPI.kt: -------------------------------------------------------------------------------- 1 | package com.andoter.asm_example.part7 2 | 3 | /* 4 | 在树 API 中通过 MethodNode 进行方法的生成和转换。 5 | public class MethodNode ... { 6 | public int access; 7 | public String name; 8 | public String desc; 9 | public String signature; 10 | public List exceptions; 11 | public List visibleAnnotations; 12 | public List invisibleAnnotations; 13 | public List attrs; 14 | public Object annotationDefault; 15 | public List[] visibleParameterAnnotations; 16 | public List[] invisibleParameterAnnotations; 17 | public InsnList instructions; 18 | public List tryCatchBlocks; 19 | public List localVariables; 20 | public int maxStack; 21 | public int maxLocals; 22 | } 23 | 24 | 这其中最重要的是从 instructions 字段后的几个。instructions 是一个指令列表,通过 InsnList 进行管理。 25 | InsnList 是一个由指令组成的双向链表,它们的链接存储在 AbstractInsnNode 对象本身中。AbstractInsnNode 类是表示字节代码指令的类的超类。 26 | 它的子类是 Xxx InsnNode 类,对应于 MethodVisitor 接口的 visitXxx Insn 方法,而且其构造方式完全相同。例如,VarInsnNode 类对应于 visitVarInsn 方法。 27 | 所以对于一个 AbstractInsnNode 对象来说,具有以下特征: 28 | - 一个 AbstractInsnNode 对象在一个指令列表中最多出现一次 29 | - 一个 AbstractInsnNode 对象不能同时属于多个指令列表 30 | - 一个结果是:如果一个 AbstractInsnNode 属于某个列表,要将它添加到另一列表,必须先将其从原列表中删除 31 | - 将一个列表中的所有元素都添加到另一个列表中,将会清空第一个列表。 32 | 33 | 标记与帧,还有行号,尽管它们并不是指令,但也都用 AbstractInsnNode 类的子类表示,即 LabelNode、FrameNode 和 LineNumberNode 类。 34 | */ 35 | class MethodNodeAPI { 36 | } -------------------------------------------------------------------------------- /asm_example/src/main/java/com/andoter/asm_example/part7/MyAdapter.kt: -------------------------------------------------------------------------------- 1 | package com.andoter.asm_example.part7 2 | 3 | import org.objectweb.asm.ClassReader 4 | import org.objectweb.asm.ClassVisitor 5 | import org.objectweb.asm.MethodVisitor 6 | import org.objectweb.asm.Opcodes 7 | import org.objectweb.asm.tree.MethodNode 8 | 9 | class MyAdapter { 10 | } 11 | 12 | // 基于继承模式 13 | class MyMethodAdapter( 14 | access: Int, name: String, desc: String, signature: String, exception: Array, 15 | methodVisitor: MethodVisitor 16 | ) : MethodNode(access, name, desc, signature, exception) { 17 | override fun visitEnd() { 18 | // 自定义转换代码 19 | accept(mv) 20 | } 21 | } 22 | 23 | 24 | class MyMethodAdapter2( 25 | access: Int, name: String, desc: String, signature: String, exception: Array, 26 | var methodVisitor: MethodVisitor 27 | ) : MethodVisitor(access, MethodNode(access, name, desc, signature, exception)) { 28 | 29 | 30 | 31 | override fun visitEnd() { 32 | // 自定义转换代码 33 | val mn = methodVisitor as MethodNode 34 | mn.accept(mv) 35 | } 36 | } 37 | 38 | fun main() { 39 | val classReader = ClassReader("java.lang.Runnable") 40 | classReader.accept(object : ClassVisitor(Opcodes.ASM7) { 41 | override fun visitMethod( 42 | access: Int, 43 | name: String, 44 | descriptor: String, 45 | signature: String, 46 | exceptions: Array 47 | ): MethodVisitor { 48 | return object: MethodNode(Opcodes.ASM7, access, name, descriptor, signature, exceptions){ 49 | override fun visitEnd() { 50 | super.visitEnd() 51 | } 52 | } 53 | } 54 | }, ClassReader.SKIP_DEBUG) 55 | } -------------------------------------------------------------------------------- /asm_example/src/main/java/com/andoter/asm_example/part7/OptimizeJumpTransformer.kt: -------------------------------------------------------------------------------- 1 | package com.andoter.asm_example.part7 2 | 3 | import com.andoter.asm_example.utils.ClassOutputUtil 4 | import org.objectweb.asm.ClassReader 5 | import org.objectweb.asm.ClassWriter 6 | import org.objectweb.asm.Opcodes 7 | import org.objectweb.asm.tree.AbstractInsnNode 8 | import org.objectweb.asm.tree.ClassNode 9 | import org.objectweb.asm.tree.JumpInsnNode 10 | import org.objectweb.asm.tree.MethodNode 11 | 12 | class Optimize { 13 | var number: Int = 0 14 | fun checkAndSet(value: Int) { 15 | if (value > 0) { 16 | this.number = value 17 | } else { 18 | return 19 | } 20 | } 21 | } 22 | 23 | 24 | fun main() { 25 | val classReader = ClassReader("com.andoter.asm_example.part7.Optimize") 26 | val classNode = ClassNode() 27 | classReader.accept(classNode, ClassReader.SKIP_DEBUG) 28 | val optimizeJumpTransformer = OptimizeJumpTransformer() 29 | for (methodNode in classNode.methods) { 30 | optimizeJumpTransformer.transform(methodNode) 31 | } 32 | 33 | val classWriter = ClassWriter(ClassWriter.COMPUTE_MAXS) 34 | classNode.accept(classWriter) 35 | ClassOutputUtil.byte2File("asm_example/files/Optimize.class", classWriter.toByteArray()) 36 | } 37 | 38 | class OptimizeJumpTransformer : MethodTransformer() { 39 | 40 | override fun transform(mn: MethodNode) { 41 | val insns = mn.instructions 42 | val iterator = insns.iterator() 43 | while (iterator.hasNext()) { 44 | val inNode = iterator.next() 45 | if (inNode is JumpInsnNode) { 46 | var label = inNode.label 47 | var target: AbstractInsnNode 48 | while (true) { 49 | target = label 50 | while (target != null && target.opcode < 0) { 51 | target = target.next 52 | } 53 | 54 | if (target != null && target.opcode == Opcodes.GOTO) { 55 | label = (target as JumpInsnNode).label 56 | } else { 57 | break; 58 | } 59 | (inNode as JumpInsnNode).label = label 60 | if (inNode.opcode == Opcodes.GOTO && target != null) { 61 | var op = target.opcode 62 | if (op >= Opcodes.IRETURN && op <= Opcodes.RETURN || op == Opcodes.ATHROW) { 63 | insns.set(inNode, target.clone(null)) 64 | } 65 | } 66 | } 67 | } 68 | } 69 | super.transform(mn) 70 | } 71 | } -------------------------------------------------------------------------------- /asm_example/src/main/java/com/andoter/asm_example/part7/RemoveGetFieldPutFieldTransformer.kt: -------------------------------------------------------------------------------- 1 | package com.andoter.asm_example.part7 2 | 3 | import com.andoter.asm_example.utils.ClassOutputUtil 4 | import org.objectweb.asm.ClassReader 5 | import org.objectweb.asm.ClassWriter 6 | import org.objectweb.asm.Opcodes 7 | import org.objectweb.asm.tree.* 8 | 9 | class BeanField { 10 | private var f:Int =1 11 | fun getF(): Int { 12 | return this.f 13 | } 14 | 15 | fun setF(value: Int) { 16 | this.f = f 17 | this.f = value 18 | } 19 | } 20 | 21 | fun main() { 22 | val classReader = ClassReader("com.andoter.asm_example.part7.BeanField") 23 | val classNode = ClassNode() 24 | classReader.accept(classNode, ClassReader.SKIP_DEBUG) 25 | val removeTransformer = RemoveGetFieldPutFieldTransformer() 26 | for (methodNode in classNode.methods) { 27 | removeTransformer.transform(methodNode) 28 | } 29 | 30 | val classWriter = ClassWriter(ClassWriter.COMPUTE_MAXS) 31 | classNode.accept(classWriter) 32 | /* 33 | 删除之前的字节码指令: 34 | public final void setF(int); 35 | descriptor: (I)V 36 | flags: ACC_PUBLIC, ACC_FINAL 37 | Code: 38 | stack=2, locals=2, args_size=2 39 | 0: aload_0 40 | 1: aload_0 41 | 2: getfield #27 // Field f:I 42 | 5: putfield #27 // Field f:I 43 | 8: aload_0 44 | 9: iload_1 45 | 10: putfield #27 // Field f:I 46 | 13: return 47 | 48 | 删除后的字节码指令: 49 | public final void setF(int); 50 | descriptor: (I)V 51 | flags: ACC_PUBLIC, ACC_FINAL 52 | Code: 53 | stack=2, locals=2, args_size=2 54 | 0: aload_0 55 | 1: iload_1 56 | 2: putfield #27 // Field f:I 57 | 5: return 58 | */ 59 | ClassOutputUtil.byte2File("asm_example/files/BeanField.class", classWriter.toByteArray()) 60 | } 61 | 62 | /* 63 | 我们可以看到,基于树 API 的代码数量要多余核心 API 的情况,但是这两种方式之间的主要区别是,使用树 API 时不需要状态机。 64 | 当有三个火多个连续的 ALOAD 0 指令时,不在成为问题。 65 | */ 66 | class RemoveGetFieldPutFieldTransformer : MethodTransformer() { 67 | override fun transform(mn: MethodNode) { 68 | val insnList = mn.instructions 69 | val iterator = insnList.iterator() 70 | while (iterator.hasNext()) { 71 | val insnNode = iterator.next() 72 | if (isALOAD(insnNode)) { 73 | val i2 = getNext(insnNode) 74 | if (i2 != null && isALOAD(i2)) { 75 | val i3 = getNext(i2) 76 | if (i3 != null && i3.opcode == Opcodes.GETFIELD) { 77 | val i4 = getNext(i3) 78 | if (i4 != null && i4.opcode == Opcodes.PUTFIELD) { 79 | if (sameField(i3, i4)) { 80 | while (iterator.next() != i4) { 81 | 82 | } 83 | insnList.remove(insnNode) 84 | insnList.remove(i2) 85 | insnList.remove(i3) 86 | insnList.remove(i4) 87 | } 88 | } 89 | } 90 | } 91 | } 92 | } 93 | } 94 | 95 | companion object { 96 | fun isALOAD(abstractInsnNode: AbstractInsnNode): Boolean { 97 | return abstractInsnNode.opcode == Opcodes.ALOAD && (abstractInsnNode as VarInsnNode).`var` == 0 98 | } 99 | 100 | fun sameField(iNode: AbstractInsnNode, jNode: AbstractInsnNode): Boolean { 101 | return (iNode as FieldInsnNode).name == (jNode as FieldInsnNode).name 102 | } 103 | 104 | private fun getNext(insnNode: AbstractInsnNode?): AbstractInsnNode? { 105 | var newNode = insnNode 106 | do { 107 | newNode = newNode?.next 108 | if (newNode != null && newNode !is LineNumberNode) { 109 | break 110 | } 111 | } while (newNode != null) 112 | return newNode 113 | } 114 | } 115 | } 116 | 117 | open class MethodTransformer { 118 | private var methodTransformer: MethodTransformer? = null 119 | 120 | open fun transform(mn: MethodNode) { 121 | methodTransformer?.transform(mn) 122 | } 123 | } 124 | -------------------------------------------------------------------------------- /asm_example/src/main/java/com/andoter/asm_example/part7/RemoveGetFieldPutFieldTransformer2.kt: -------------------------------------------------------------------------------- 1 | package com.andoter.asm_example.part7 2 | 3 | import com.andoter.asm_example.utils.ClassOutputUtil 4 | import org.objectweb.asm.ClassReader 5 | import org.objectweb.asm.ClassWriter 6 | import org.objectweb.asm.Opcodes 7 | import org.objectweb.asm.tree.* 8 | 9 | fun main() { 10 | val classReader = ClassReader("com.andoter.asm_example.part7.BeanField") 11 | val classNode = ClassNode() 12 | classReader.accept(classNode, ClassReader.SKIP_DEBUG) 13 | val removeTransformer = RemoveGetFieldPutFieldTransformer2() 14 | for (methodNode in classNode.methods) { 15 | removeTransformer.transform(methodNode) 16 | } 17 | 18 | val classWriter = ClassWriter(ClassWriter.COMPUTE_MAXS) 19 | classNode.accept(classWriter) 20 | /* 21 | 删除之前的字节码指令: 22 | public final void setF(int); 23 | descriptor: (I)V 24 | flags: ACC_PUBLIC, ACC_FINAL 25 | Code: 26 | stack=2, locals=2, args_size=2 27 | 0: aload_0 28 | 1: aload_0 29 | 2: getfield #27 // Field f:I 30 | 5: putfield #27 // Field f:I 31 | 8: aload_0 32 | 9: iload_1 33 | 10: putfield #27 // Field f:I 34 | 13: return 35 | 36 | 删除后的字节码指令: 37 | public final void setF(int); 38 | descriptor: (I)V 39 | flags: ACC_PUBLIC, ACC_FINAL 40 | Code: 41 | stack=2, locals=2, args_size=2 42 | 0: aload_0 43 | 1: iload_1 44 | 2: putfield #27 // Field f:I 45 | 5: return 46 | */ 47 | ClassOutputUtil.byte2File("asm_example/files/BeanField.class", classWriter.toByteArray()) 48 | } 49 | 50 | /* 51 | 通过对上面的 getNext() 方法进行调整,现在是对迭代列表进行操作。当序列识别确认完毕后,迭代器会恰好 52 | 在他后面的位置。所以不需要 while(i.next() != i4) 的循环遍历判断 53 | */ 54 | class RemoveGetFieldPutFieldTransformer2 : MethodTransformer() { 55 | override fun transform(mn: MethodNode) { 56 | val insnList = mn.instructions 57 | val iterator = insnList.iterator() 58 | while (iterator.hasNext()) { 59 | var insnNode = iterator.next() 60 | if (isALOAD(insnNode)) { 61 | var i2 = getNext(iterator) 62 | if (i2 != null && isALOAD(i2)) { 63 | var i3 = getNext(iterator) 64 | while (i3 != null && isALOAD(i3)) { 65 | insnNode = i2; 66 | i2 = i3; 67 | i3 = getNext(iterator); 68 | } 69 | if (i3 != null && i3.opcode == Opcodes.GETFIELD) { 70 | val i4 = getNext(iterator) 71 | if (i4 != null && i4.opcode == Opcodes.PUTFIELD) { 72 | if (sameField(i3, i4)) { 73 | insnList.remove(insnNode) 74 | insnList.remove(i2) 75 | insnList.remove(i3) 76 | insnList.remove(i4) 77 | } 78 | } 79 | } 80 | } 81 | } 82 | } 83 | } 84 | 85 | companion object { 86 | fun isALOAD(abstractInsnNode: AbstractInsnNode): Boolean { 87 | return abstractInsnNode.opcode == Opcodes.ALOAD && (abstractInsnNode as VarInsnNode).`var` == 0 88 | } 89 | 90 | fun sameField(iNode: AbstractInsnNode, jNode: AbstractInsnNode): Boolean { 91 | return (iNode as FieldInsnNode).name == (jNode as FieldInsnNode).name 92 | } 93 | 94 | private fun getNext(iterator: Iterator): AbstractInsnNode? { 95 | while (iterator.hasNext()) { 96 | val abstractInsnNode = iterator.next() 97 | if(abstractInsnNode !is LineNumberNode) { 98 | return abstractInsnNode 99 | } 100 | } 101 | return null 102 | } 103 | } 104 | } 105 | -------------------------------------------------------------------------------- /asm_example/src/main/java/com/andoter/asm_example/part8/AnalyzerClass.kt: -------------------------------------------------------------------------------- 1 | package com.andoter.asm_example.part8 2 | 3 | /* 4 | 本章介绍分析方法的 ASM 类型 API,它是基于树类型的 API。分析代码是一个很大的范畴,在本章中我们只介绍 ASM 中使用的算法. 5 | 想了解更多算法,可以找有关编译器的书籍进行了解. 6 | 代码分析的两种类型:数据流和控制流 7 | 数据流分析包括:对于一个方法的每条指令,计算其执行帧的状态. 8 | 控制流分析包括计算一个方法的控制流图,并对这个图进行分析。控制流图的节点为指令,如果指令 j 可以紧跟在 i 之后执行,则图的有向边将连接这两条指令 i→j。 9 | 10 | 数据流分析有两种类型: 11 | - 正向分析:是指对于每条指令,根据执行帧在执行此条指令之前的状态,计算执行帧在这一指令执行后的状态。 12 | - 反向分析:是指对于每条指令,根据执行帧在执行此指令之后的状态,计算执行帧在这一指令执行前的状态。 13 | 正向数据流分析的执行是对于一个方法的每个字节码指令,模拟其在执行帧上的执行,通常包括: 14 | - 从栈中弹出值,合并它们,将结果压入栈中 15 | 16 | 看起来是 Java 虚拟机要做的事情,实际上是对于目标可能出现的参数值,模拟一个方法执行的所有路径。而不是一组特定参数执行的单一路径。 17 | 18 | 控制流分析的基础是方法的控制流图。 19 | 20 | */ 21 | class AnalyzerClass { 22 | } -------------------------------------------------------------------------------- /asm_example/src/main/java/com/andoter/asm_example/part8/BasicVerifierAdapter.kt: -------------------------------------------------------------------------------- 1 | package com.andoter.asm_example.part8 2 | 3 | import com.andoter.asm_example.utils.ClassOutputUtil 4 | import org.objectweb.asm.* 5 | import org.objectweb.asm.tree.MethodNode 6 | import org.objectweb.asm.tree.analysis.Analyzer 7 | import org.objectweb.asm.tree.analysis.AnalyzerException 8 | import org.objectweb.asm.tree.analysis.BasicValue 9 | import org.objectweb.asm.tree.analysis.BasicVerifier 10 | 11 | /** 12 | * BasicVerifier 是 BasicInterpreter 的子类,用于实现对字节码指令是否正确的校验。 13 | * 例如,它会验证 IADD 指令的操作数为 INTEGER_VALUE 值(而 BasicInterpreter 只是返回结果,即 INTEGER_VALUE)。这个类可在开发类生成器或适配器时进行调试。 14 | */ 15 | class BasicVerifierAdapter : MethodVisitor { 16 | private var owner:String? = "" 17 | private var next:MethodVisitor 18 | constructor( 19 | owner: String?, 20 | access: Int, 21 | name: String?, 22 | desc: String?, 23 | methodVisitor: MethodVisitor 24 | ) : super(Opcodes.ASM7, MethodNode(access, name, desc, null, null)) { 25 | this.owner = owner 26 | this.next = methodVisitor 27 | } 28 | 29 | override fun visitEnd() { 30 | val methodNode = mv as MethodNode 31 | val analyzer = Analyzer(BasicVerifier()) 32 | try { 33 | analyzer.analyze(owner, methodNode) 34 | } catch (ignore: AnalyzerException) { 35 | throw RuntimeException(ignore.message); 36 | } 37 | methodNode.accept(next) 38 | } 39 | } 40 | 41 | fun main() { 42 | val classReader = ClassReader("org.objectweb.asm.tree.MethodNode") 43 | val classWriter = ClassWriter(classReader, ClassWriter.COMPUTE_MAXS) 44 | classReader.accept(object : ClassVisitor(Opcodes.ASM7, classWriter){ 45 | private var owner: String? = "" 46 | override fun visit( 47 | version: Int, 48 | access: Int, 49 | name: String?, 50 | signature: String?, 51 | superName: String?, 52 | interfaces: Array? 53 | ) { 54 | this.owner = name 55 | super.visit(version, access, name, signature, superName, interfaces) 56 | } 57 | 58 | override fun visitMethod( 59 | access: Int, 60 | name: String?, 61 | descriptor: String?, 62 | signature: String?, 63 | exceptions: Array? 64 | ): MethodVisitor { 65 | val methodVisitor = super.visitMethod(access, name, descriptor, signature, exceptions) 66 | if (methodVisitor != null) { 67 | return BasicVerifierAdapter(owner, access, name, descriptor, methodVisitor) 68 | } 69 | return methodVisitor 70 | } 71 | }, ClassReader.SKIP_DEBUG) 72 | 73 | ClassOutputUtil.byte2File("asm_example/files/MethodNode.class", classWriter.toByteArray()) 74 | } -------------------------------------------------------------------------------- /asm_example/src/main/java/com/andoter/asm_example/part8/CyclomaticComplexity.kt: -------------------------------------------------------------------------------- 1 | package com.andoter.asm_example.part8 2 | 3 | import org.objectweb.asm.tree.MethodNode 4 | import org.objectweb.asm.tree.analysis.* 5 | import java.util.* 6 | 7 | 8 | class CyclomaticComplexity { 9 | 10 | fun getCyclomaticComplexity(owner: String, mn: MethodNode) { 11 | val analyzer = object :Analyzer(BasicInterpreter()) { 12 | override fun newFrame(numLocals: Int, numStack: Int): Frame { 13 | return Node(numLocals, numStack) 14 | } 15 | 16 | override fun newControlFlowEdge(insnIndex: Int, successorIndex: Int) { 17 | val node = frames[insnIndex] as Node 18 | node.successors.plus(frames[successorIndex] as Node) 19 | super.newControlFlowEdge(insnIndex, successorIndex) 20 | } 21 | } 22 | } 23 | } 24 | 25 | class Node(numLocals: Int, numStack: Int) : Frame(numLocals, numStack) { 26 | var successors: Set> = mutableSetOf() 27 | } -------------------------------------------------------------------------------- /asm_example/src/main/java/com/andoter/asm_example/part8/IsNullInterpreter.kt: -------------------------------------------------------------------------------- 1 | package com.andoter.asm_example.part8 2 | 3 | import com.andoter.asm_example.utils.ADLog 4 | import org.objectweb.asm.ClassReader 5 | import org.objectweb.asm.Opcodes 6 | import org.objectweb.asm.Type 7 | import org.objectweb.asm.tree.AbstractInsnNode 8 | import org.objectweb.asm.tree.ClassNode 9 | import org.objectweb.asm.tree.MethodInsnNode 10 | import org.objectweb.asm.tree.MethodNode 11 | import org.objectweb.asm.tree.analysis.* 12 | import org.objectweb.asm.tree.analysis.BasicValue.REFERENCE_VALUE 13 | import java.util.* 14 | 15 | /* 16 | 检测调用对象是否可能为 null 17 | */ 18 | class IsNullInterpreter : BasicInterpreter(Opcodes.ASM7) { 19 | 20 | companion object { 21 | val NULL: BasicValue = BasicValue(null) 22 | val MAYBENULL: BasicValue = BasicValue(null) 23 | } 24 | 25 | override fun newOperation(insn: AbstractInsnNode?): BasicValue? { 26 | if (insn!!.opcode == Opcodes.ACONST_NULL) { 27 | return null 28 | } 29 | return super.newOperation(insn) 30 | } 31 | 32 | override fun merge(value1: BasicValue?, value2: BasicValue?): BasicValue { 33 | if (isRef(value1) && isRef(value2) && value1 != value2) { 34 | return MAYBENULL 35 | } 36 | return super.merge(value1, value2) 37 | } 38 | 39 | fun isRef(value: Value?): Boolean { 40 | return value == REFERENCE_VALUE || value == NULL || value == MAYBENULL 41 | } 42 | } 43 | 44 | /* 45 | findNullDereferences 方法用一个 IsNullInterpreter 分析给定方法节点。然后, 46 | 对于每条指令,检测其引用操作数(如果有的话)的可能值集是不是 NULL 集或 NONNULL 集。 47 | 若是,则这条指令可能导致一个 null 指针异常,将它添加到此类指令的列表中,该列表由这一 48 | 方法返回。 49 | getTarget 方法在帧 f 中返回与 insn 对象操作数相对应的 Value,如果 insn 没有对象 50 | 操作数,则返回 null。它的主要任务就是计算这个值相对于操作数栈顶端的偏移量,这一数量 51 | 取决于指令类型。 52 | */ 53 | class NullDereferenceAnalyzer { 54 | fun findNullDereferences(owner: String, mn: MethodNode): List { 55 | ADLog.info("owner = $owner, mn_name = ${mn.name}") 56 | val result = mutableListOf() 57 | val analyzer = Analyzer(IsNullInterpreter()) 58 | analyzer.analyze(owner, mn) 59 | val frames = analyzer.frames 60 | val insns = mn.instructions.toArray() 61 | for (i in insns.indices) { 62 | val insnNode = insns[i] 63 | if (frames[i] != null) { 64 | val value = getTarget(insnNode, f = frames[i]) 65 | if (value == null || value == IsNullInterpreter.MAYBENULL) { 66 | result.add(insnNode) 67 | } 68 | } 69 | } 70 | return result 71 | } 72 | 73 | private fun getTarget(insn:AbstractInsnNode,f:Frame):BasicValue? { 74 | when (insn.opcode) { 75 | Opcodes.GETFIELD,Opcodes.ARRAYLENGTH,Opcodes.MONITORENTER,Opcodes.MONITOREXIT -> return getStackValue(f, 0) 76 | Opcodes.PUTFIELD -> getStackValue(f, 1) 77 | Opcodes.INVOKEVIRTUAL,Opcodes.INVOKESPECIAL, Opcodes.INVOKEINTERFACE -> { 78 | val desc = (insn as MethodInsnNode).desc 79 | return getStackValue(f, Type.getArgumentTypes(desc).size) 80 | } 81 | } 82 | return null 83 | } 84 | 85 | private fun getStackValue(f: Frame, index:Int): BasicValue? { 86 | val top = f.stackSize - 1 87 | return if (index <= top) f.getStack(top - index) else null 88 | } 89 | } 90 | class NullTest { 91 | fun reset() { 92 | var o : String? = null 93 | val i = 20 94 | while (i < 10) { 95 | o = "number" 96 | } 97 | o.toString() 98 | } 99 | } 100 | 101 | fun main() { 102 | val classReader = ClassReader("com.andoter.asm_example.part8.NullTest") 103 | val classNode = ClassNode() 104 | classReader.accept(classNode, ClassReader.SKIP_DEBUG) 105 | val nullDereferenceAnalyzer = NullDereferenceAnalyzer() 106 | for (methodNode in classNode.methods) { 107 | val result = nullDereferenceAnalyzer.findNullDereferences(classNode.name, methodNode) 108 | for (abstractNode in result) { 109 | println("${abstractNode.opcode}") 110 | } 111 | } 112 | } -------------------------------------------------------------------------------- /asm_example/src/main/java/com/andoter/asm_example/part8/RemoveDeadCode.kt: -------------------------------------------------------------------------------- 1 | package com.andoter.asm_example.part8 2 | 3 | import com.andoter.asm_example.utils.ADLog 4 | import com.andoter.asm_example.utils.ClassOutputUtil 5 | import org.objectweb.asm.* 6 | import org.objectweb.asm.tree.LabelNode 7 | import org.objectweb.asm.tree.MethodNode 8 | import org.objectweb.asm.tree.analysis.Analyzer 9 | import org.objectweb.asm.tree.analysis.AnalyzerException 10 | import org.objectweb.asm.tree.analysis.BasicInterpreter 11 | import org.objectweb.asm.tree.analysis.BasicValue 12 | 13 | /* 14 | 在 ASM 的 API 中,org.objectweb.asm.tree.analysis 包中提供了用于分析代码的类,它是基于树 API 的一个进行正向数据流分析的框架。 15 | 为了能够以准确度不一的取值进行各种数据流分析,数据流分析算法分为两部分:一种是固 16 | 定的,由框架提供,另一种是变化的,由用户提供。更准确地说: 17 | - 整体数据流分析算法、将适当数量的值从栈中弹出和压回栈中的任务仅实现一次,用于 Analyzer 和 Frame 类中的所有内容。 18 | - 合并值的任何和计算值集并集的任务由用户定义的 Interpreter 和 Value 抽象类的子类提供。 19 | 尽管框架的主要目的是执行数据流分析,但 Analyzer 类也可构造所分析方法的控制流图。为此,可以重写这个类的newControlFlowEdge和newControlFlowExceptionEdge方法, 20 | 它们默认情况下不做任何事情。其结果可用于进行控制流分析。 21 | 22 | Interpreter 类是抽象类,它利用在 BasicValue 类中定义的 7 个值集来模拟字节代码指令的效果。 23 | - UNINITIALIZED_VALUE 指“所有可能值” 24 | - INT_VALUE 指“所有 int、short、byte、boolean 或 char 值” 25 | - FLOAT_VALUE 指“所有 float 值” 26 | - LONG_VALUE 指“所有 long 值” 27 | - DOUBLE_VALUE 指“所有 double 值” 28 | - REFERENCE_VALUE 指“所有对象和数组值” 29 | - RETURNADDRESS_VALUE 用于子例程 30 | 这个解释器本身作用不大,主要是作为一个空实现,用于构建 Analyzer 对象。 31 | */ 32 | class RemoveDeadCode { 33 | var number: Int = 0 34 | fun checkAndSet(value: Int) { 35 | if (value > 0) { 36 | this.number = value 37 | return 38 | } else { 39 | return 40 | } 41 | val sum = number + 10 42 | println() 43 | } 44 | } 45 | 46 | /* 47 | 在分析之后,无论什么样的 Interpreter 实现,由 Analyzer.getFrames 方法返回的计算帧,对于不可到达的指令都是 null。这一特性可用于 48 | 非常轻松地实现一个 RemoveDeadCodeAdapter 类用于删除无用代码。 49 | */ 50 | class RemoveDeadCodeAdapter( 51 | var owner: String?, var access: Int, var name: String?, 52 | var desc: String?, var methodVisitor: MethodVisitor 53 | ) : MethodVisitor(Opcodes.ASM7, MethodNode(access, name, desc, null, null)) { 54 | 55 | override fun visitEnd() { 56 | val methodNode = mv as MethodNode 57 | val analyzer = Analyzer(BasicInterpreter()) 58 | try { 59 | analyzer.analyze(owner, methodNode) 60 | val frames = analyzer.frames 61 | val insns = methodNode.instructions.toArray() 62 | for (i in insns.indices) { 63 | if (frames[i] == null && insns[i] !is LabelNode) { 64 | methodNode.instructions.remove(insns[i]) 65 | } 66 | } 67 | } catch (ignore: AnalyzerException) { 68 | 69 | } 70 | methodNode.accept(methodVisitor) 71 | } 72 | } 73 | 74 | fun main() { 75 | val classReader = ClassReader("com.andoter.asm_example.part8.RemoveDeadCode") 76 | val classWriter = ClassWriter(classReader, ClassWriter.COMPUTE_MAXS) 77 | classReader.accept(object : ClassVisitor(Opcodes.ASM7, classWriter) { 78 | private var name: String? = "" 79 | override fun visit( 80 | version: Int, 81 | access: Int, 82 | name: String?, 83 | signature: String?, 84 | superName: String?, 85 | interfaces: Array? 86 | ) { 87 | this.name = name 88 | super.visit(version, access, name, signature, superName, interfaces) 89 | } 90 | 91 | override fun visitMethod( 92 | access: Int, 93 | name: String?, 94 | descriptor: String?, 95 | signature: String?, 96 | exceptions: Array? 97 | ): MethodVisitor? { 98 | ADLog.info("visitMethod, name = $name, desc = $descriptor") 99 | val methodVisitor = super.visitMethod(access, name, descriptor, signature, exceptions) 100 | if (methodVisitor != null) { 101 | return RemoveDeadCodeAdapter(this.name, access, name, descriptor, methodVisitor) 102 | } 103 | return methodVisitor 104 | } 105 | }, ClassReader.SKIP_DEBUG) 106 | 107 | ClassOutputUtil.byte2File("asm_example/files/RemoveDeadCode.class", classWriter.toByteArray()) 108 | } -------------------------------------------------------------------------------- /asm_example/src/main/java/com/andoter/asm_example/part8/RemoveUnusedCastAdapter.kt: -------------------------------------------------------------------------------- 1 | package com.andoter.asm_example.part8 2 | 3 | import com.andoter.asm_example.utils.ADLog 4 | import com.andoter.asm_example.utils.ClassOutputUtil 5 | import org.objectweb.asm.* 6 | import org.objectweb.asm.commons.AnalyzerAdapter 7 | 8 | class RemoveUnusedCastAdapter( 9 | owner: String, 10 | access: Int, 11 | name: String?, 12 | desc: String?, 13 | methodVisitor: MethodVisitor 14 | ) : MethodVisitor(Opcodes.ASM7, methodVisitor) { 15 | 16 | private var analyzerAdapter: AnalyzerAdapter? = null 17 | 18 | init { 19 | analyzerAdapter = AnalyzerAdapter(owner, access, name, desc, methodVisitor) 20 | } 21 | 22 | override fun visitTypeInsn(opcode: Int, type: String?) { 23 | ADLog.info("opcode = $opcode, type = $type") 24 | if (opcode == Opcodes.CHECKCAST) { 25 | val to = type?.let { getClass(it) } 26 | if (analyzerAdapter?.stack != null && analyzerAdapter!!.stack.size > 0) { 27 | val operand = analyzerAdapter?.stack!![analyzerAdapter!!.stack.size - 1] 28 | if (operand is String) { 29 | val from = getClass(operand as String) 30 | if (to!!.isAssignableFrom(from)) { 31 | return 32 | } 33 | } 34 | } 35 | } 36 | mv.visitTypeInsn(opcode, type) 37 | } 38 | 39 | companion object { 40 | private fun getClass(desc: String): Class<*>? { 41 | return try { 42 | Class.forName(desc.replace("/", ".")) 43 | } catch (ex: ClassNotFoundException) { 44 | null 45 | } 46 | } 47 | } 48 | } 49 | 50 | fun main() { 51 | val classReader = ClassReader("com.andoter.asm_example.part8.Timer") 52 | val classWriter = ClassWriter(classReader, ClassWriter.COMPUTE_MAXS) 53 | classReader.accept(object : ClassVisitor(Opcodes.ASM7,classWriter){ 54 | private lateinit var owner: String 55 | override fun visit( 56 | version: Int, 57 | access: Int, 58 | name: String?, 59 | signature: String?, 60 | superName: String?, 61 | interfaces: Array? 62 | ) { 63 | this.owner = name!! 64 | super.visit(version, access, name, signature, superName, interfaces) 65 | } 66 | 67 | override fun visitMethod( 68 | access: Int, 69 | name: String?, 70 | descriptor: String?, 71 | signature: String?, 72 | exceptions: Array? 73 | ): MethodVisitor { 74 | val methodVisitor = super.visitMethod(access, name, descriptor, signature, exceptions) 75 | if (methodVisitor != null) { 76 | return RemoveUnusedCastAdapter(owner, access,name, descriptor, methodVisitor) 77 | } 78 | return methodVisitor 79 | } 80 | }, ClassReader.SKIP_DEBUG) 81 | 82 | ClassOutputUtil.byte2File("asm_example/files/Timer.class", classWriter.toByteArray()) 83 | } -------------------------------------------------------------------------------- /asm_example/src/main/java/com/andoter/asm_example/part8/RemoveUnusedCastTransformer.kt: -------------------------------------------------------------------------------- 1 | package com.andoter.asm_example.part8 2 | 3 | import com.andoter.asm_example.part7.MethodTransformer 4 | import com.andoter.asm_example.utils.ClassOutputUtil 5 | import org.objectweb.asm.ClassReader 6 | import org.objectweb.asm.ClassWriter 7 | import org.objectweb.asm.Opcodes 8 | import org.objectweb.asm.Type 9 | import org.objectweb.asm.tree.ClassNode 10 | import org.objectweb.asm.tree.MethodNode 11 | import org.objectweb.asm.tree.TypeInsnNode 12 | import org.objectweb.asm.tree.analysis.Analyzer 13 | import org.objectweb.asm.tree.analysis.AnalyzerException 14 | import org.objectweb.asm.tree.analysis.BasicValue 15 | import org.objectweb.asm.tree.analysis.SimpleVerifier 16 | 17 | /** 18 | * SimpleVerifier 是 BasicVerifier 的子类,它使用更多的集合来模拟字节代码指令的执行,所以它可以检测出更多的错误。 19 | * 这个类使用 Java 反射 API,以执行与类层次结构有关的验证和计算。然后,它将一个方法引用的类加载到 JVM 中。这一默认行为可以通过重写这个类的受保护方法来改变。 20 | * 和 BasicVerifier 一样,这个类也可以在开发类生成器或适配器时使用,以便更轻松地找出 Bug。 21 | * 22 | */ 23 | class RemoveUnusedCastTransformer(var owner: String) : MethodTransformer() { 24 | 25 | override fun transform(mn: MethodNode) { 26 | val analyzer = Analyzer(SimpleVerifier()) 27 | try { 28 | analyzer.analyze(this.owner, mn) 29 | val frames = analyzer.frames 30 | val insns = mn.instructions.toArray() 31 | for (i in insns.indices) { 32 | val insnNode = insns[i] 33 | if (insnNode.opcode == Opcodes.CHECKCAST) { 34 | val frame = frames[i] 35 | if (frame != null && frame.stackSize>0) { 36 | val operand = frame.getStack(frame.stackSize - 1) 37 | val to = getClass((insnNode as TypeInsnNode).desc) 38 | val from = getClass((operand as BasicValue).type) 39 | if (to!!.isAssignableFrom(from)) { 40 | mn.instructions.remove(insnNode) 41 | } 42 | } 43 | } 44 | } 45 | } catch (ex: AnalyzerException) { 46 | 47 | } 48 | super.transform(mn) 49 | } 50 | 51 | companion object { 52 | private fun getClass(desc: String) :Class<*>? { 53 | return try { 54 | Class.forName(desc.replace("/", ".")) 55 | } catch (ex : ClassNotFoundException) { 56 | null 57 | } 58 | } 59 | 60 | private fun getClass(t: Type): Class<*>? { 61 | return if (t.sort == Type.OBJECT) { 62 | getClass(t.internalName) 63 | } else getClass(t.descriptor) 64 | } 65 | } 66 | } 67 | 68 | fun main() { 69 | val classReader = ClassReader("com.andoter.asm_example.part8.Timer") 70 | val classWriter = ClassWriter(classReader, ClassWriter.COMPUTE_MAXS) 71 | val classNode = ClassNode() 72 | classReader.accept(classNode, ClassReader.SKIP_DEBUG) 73 | val removeUnusedCastTransformer = RemoveUnusedCastTransformer(classNode.name) 74 | for (methodNode in classNode.methods) { 75 | removeUnusedCastTransformer.transform(methodNode) 76 | } 77 | 78 | classNode.accept(classWriter) 79 | /* 80 | // 执行前 81 | public final java.lang.Runnable cast(); 82 | descriptor: ()Ljava/lang/Runnable; 83 | flags: ACC_PUBLIC, ACC_FINAL 84 | Code: 85 | stack=1, locals=1, args_size=1 86 | 0: aload_0 87 | 1: instanceof #6 // class java/lang/Runnable 88 | 4: ifeq 12 89 | 7: aload_0 90 | 8: checkcast #6 // class java/lang/Runnable 91 | 11: areturn 92 | 12: aconst_null 93 | 13: areturn 94 | StackMapTable: number_of_entries = 1 95 | frame_type = 12 /* same */ 96 | RuntimeInvisibleAnnotations: 97 | 0: #27() 98 | // 执行后 99 | public final java.lang.Runnable cast(); 100 | descriptor: ()Ljava/lang/Runnable; 101 | flags: ACC_PUBLIC, ACC_FINAL 102 | Code: 103 | stack=1, locals=1, args_size=1 104 | 0: aload_0 105 | 1: instanceof #6 // class java/lang/Runnable 106 | 4: ifeq 9 107 | 7: aload_0 108 | 8: areturn 109 | 9: aconst_null 110 | 10: areturn 111 | StackMapTable: number_of_entries = 1 112 | frame_type = 9 /* same */ 113 | RuntimeInvisibleAnnotations: 114 | 0: #27() 115 | */ 116 | ClassOutputUtil.byte2File("asm_example/files/Timer.class", classWriter.toByteArray()) 117 | } 118 | 119 | class Timer : Runnable { 120 | override fun run() { 121 | println("timer") 122 | } 123 | 124 | fun cast(): Runnable? { 125 | if (this is Runnable) { 126 | return this as Runnable 127 | } 128 | return null 129 | } 130 | } -------------------------------------------------------------------------------- /asm_example/src/main/java/com/andoter/asm_example/part9/AnnotationNodeAdapter.kt: -------------------------------------------------------------------------------- 1 | package com.andoter.asm_example.part9 2 | 3 | import com.andoter.asm_example.utils.ADLog 4 | import org.objectweb.asm.AnnotationVisitor 5 | import org.objectweb.asm.ClassReader 6 | import org.objectweb.asm.ClassVisitor 7 | import org.objectweb.asm.Opcodes 8 | import org.objectweb.asm.tree.AnnotationNode 9 | 10 | /** 11 | * 在树 API 中提供了对应的 AnnotationNode 类用于处理注解,该类继承 AnnotationVisitor 12 | */ 13 | 14 | @Test("Adapter") 15 | class AnnotationNodeAdapter { 16 | 17 | } 18 | 19 | annotation class Test(val value: String) 20 | 21 | fun main() { 22 | val classReader = ClassReader("com.andoter.asm_example.part9.AnnotationNodeAdapter") 23 | classReader.accept(object :ClassVisitor(Opcodes.ASM7){ 24 | override fun visitAnnotation(descriptor: String?, visible: Boolean): AnnotationVisitor { 25 | return object :AnnotationNode(Opcodes.ASM6, descriptor) { 26 | override fun visit(name: String?, value: Any?) { 27 | super.visit(name, value) 28 | ADLog.info("visit, name = $name, value = $value") 29 | } 30 | 31 | override fun visitEnd() { 32 | ADLog.info("visitEnd") 33 | super.visitEnd() 34 | } 35 | } 36 | } 37 | }, ClassReader.SKIP_DEBUG) 38 | 39 | } -------------------------------------------------------------------------------- /asm_example/src/main/java/com/andoter/asm_example/part9/SignatureVisitorAdapter.java: -------------------------------------------------------------------------------- 1 | package com.andoter.asm_example.part9; 2 | 3 | /** 4 | * 在树 API 中没有提供与 SignatureVisitor 对应的 SignatureNode 5 | */ 6 | class SignatureVisitorAdapter { 7 | } 8 | -------------------------------------------------------------------------------- /asm_example/src/main/java/com/andoter/asm_example/utils/ADLog.kt: -------------------------------------------------------------------------------- 1 | package com.andoter.asm_example.utils 2 | 3 | object ADLog { 4 | private const val TAG = "Andoter" 5 | var debug = false 6 | 7 | /** 8 | * 输出错误日志 9 | */ 10 | fun error(errorMessage: String) { 11 | try { 12 | println("${LogUI.ERROR.value}[$TAG]:$errorMessage${LogUI.END.value}") 13 | } catch (ex: Exception) { 14 | ex.printStackTrace() 15 | } 16 | } 17 | 18 | /** 19 | * 输出信息日志 20 | */ 21 | fun info(info: String) { 22 | try { 23 | println("${LogUI.INFO.value}[$TAG]:$info${LogUI.END.value}") 24 | } catch (ex: Exception) { 25 | ex.printStackTrace() 26 | } 27 | } 28 | 29 | /** 30 | * 具体规则 31 | * 字背景颜色范围: 40--49 字颜色: 30--39 32 | * 40: 黑 30: 黑 33 | * 41:红 31: 红 34 | * 42:绿 32: 绿 35 | * 43:黄 33: 黄 36 | * 44:蓝 34: 蓝 37 | * 45:紫 35: 紫 38 | * 46:深绿 36: 深绿 39 | * 47:白色 37: 白色 40 | * 41 | * 输出特效格式控制:[%d;%d;4m 分别为:前景色值、背景色值、加粗(1)斜体(3)下划线(4) 42 | * 033[0m 关闭所有属性 43 | * 033[1m 设置高亮度 44 | * 03[4m 下划线 45 | * 033[5m 闪烁 46 | * 033[7m 反显 47 | * 033[8m 消隐 48 | * 033[30m -- \033[37m 设置前景色 49 | * 033[40m -- \033[47m 设置背景色 50 | */ 51 | enum class LogUI(val value: String) { 52 | //color 53 | ERROR("\u001B[30;31m"), 54 | WARN("\u001B[30;33m"), 55 | INFO("\u001B[30;32m"), 56 | END("\u001B[0m") 57 | } 58 | } -------------------------------------------------------------------------------- /asm_example/src/main/java/com/andoter/asm_example/utils/AccessCodeUtils.kt: -------------------------------------------------------------------------------- 1 | package com.andoter.asm_example.utils 2 | 3 | import org.objectweb.asm.Opcodes 4 | 5 | /** 6 | * ACC_CODE 工具类 7 | */ 8 | object AccessCodeUtils { 9 | private const val ACCESS_PREFIX = "ACC_" 10 | private val mapAccess = mutableMapOf() 11 | private val mapOpcodes = mutableMapOf() 12 | /** 13 | * 修饰符 14 | */ 15 | fun accCode2String(code: Int): String { 16 | val accString = StringBuilder() 17 | if (mapAccess.isEmpty()) { 18 | accCodeMap() 19 | } 20 | 21 | mapAccess.forEach { (key, value) -> 22 | if ((code and key) > 0) { 23 | accString.append("$value ") 24 | } 25 | } 26 | return accString.toString() 27 | } 28 | 29 | /** 30 | * 转换其它操作 Opcode 31 | */ 32 | fun opcode2String(code: Int):String { 33 | if (mapOpcodes.isEmpty()) { 34 | accCodeMap() 35 | } 36 | 37 | for (op in mapOpcodes) { 38 | if (op.value == code) { 39 | return op.key 40 | } 41 | } 42 | return "" 43 | } 44 | 45 | /** 46 | * 生成 ACC_ 操作符集合 47 | */ 48 | private fun accCodeMap() { 49 | if (mapAccess.isEmpty()) { 50 | val fields = Opcodes::class.java.fields 51 | for (field in fields) { 52 | if (field.name.startsWith(ACCESS_PREFIX)) { 53 | mapAccess[field.getInt(null)] = field.name 54 | } else if (field.type == Int::class.java){ 55 | mapOpcodes[field.name] = field.getInt(null) 56 | } 57 | } 58 | } 59 | } 60 | } -------------------------------------------------------------------------------- /asm_example/src/main/java/com/andoter/asm_example/utils/ClassOutputUtil.kt: -------------------------------------------------------------------------------- 1 | package com.andoter.asm_example.utils 2 | 3 | import java.io.ByteArrayInputStream 4 | import java.io.File 5 | import java.io.FileOutputStream 6 | 7 | object ClassOutputUtil { 8 | 9 | fun byte2File(outputPath: String, sourceByte: ByteArray) { 10 | val file = File(outputPath) 11 | if (file.exists()) { 12 | file.delete() 13 | } 14 | 15 | val inputStream = ByteArrayInputStream(sourceByte) 16 | val outputStream = FileOutputStream(file) 17 | val buffer = ByteArray(1024) 18 | var len = 0 19 | while (inputStream.read(buffer).apply { len = this } != -1) { 20 | outputStream.write(buffer, 0, len) 21 | } 22 | outputStream.flush() 23 | outputStream.close() 24 | inputStream.close() 25 | } 26 | } -------------------------------------------------------------------------------- /build.gradle: -------------------------------------------------------------------------------- 1 | // Top-level build file where you can add configuration options common to all sub-projects/modules. 2 | buildscript { 3 | ext.kotlin_version = "1.3.72" 4 | repositories { 5 | google() 6 | jcenter() 7 | } 8 | dependencies { 9 | classpath "com.android.tools.build:gradle:4.0.0" 10 | classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" 11 | 12 | // NOTE: Do not place your application dependencies here; they belong 13 | // in the individual module build.gradle files 14 | } 15 | } 16 | 17 | allprojects { 18 | repositories { 19 | google() 20 | jcenter() 21 | } 22 | } 23 | 24 | task clean(type: Delete) { 25 | delete rootProject.buildDir 26 | } -------------------------------------------------------------------------------- /doc/ASM6 开发者指南/asm-package-dependencies.svg: -------------------------------------------------------------------------------- 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 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | org.objectweb.asm.util 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | org.objectweb.asm.tree.analysis 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | org.objectweb.asm.tree 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | org.objectweb.asm.xml 141 | 142 | 143 | 144 | 145 | 146 | 147 | 148 | 149 | org.objectweb.asm.commons 150 | 151 | 152 | 153 | 154 | 155 | 156 | 157 | 158 | org.objectweb.asm.signature 159 | 160 | 161 | 162 | 163 | 164 | 165 | 166 | 167 | org.objectweb.asm 168 | 169 | 170 | 171 | 172 | 173 | 174 | 175 | 176 | 177 | 178 | 179 | 180 | 181 | 182 | 183 | 184 | 185 | 186 | 187 | 188 | 189 | 190 | 191 | 192 | 193 | 194 | 195 | 196 | 197 | 198 | 199 | 200 | 201 | 202 | 203 | 204 | 205 | 206 | 207 | -------------------------------------------------------------------------------- /doc/blog/AnalysisAPI.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dengshiwei/asm-module/4ac9b11731db3bf688b849ffe6db41f1f156712f/doc/blog/AnalysisAPI.png -------------------------------------------------------------------------------- /doc/blog/AnnotationVisitor.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dengshiwei/asm-module/4ac9b11731db3bf688b849ffe6db41f1f156712f/doc/blog/AnnotationVisitor.png -------------------------------------------------------------------------------- /doc/blog/ClassNode.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dengshiwei/asm-module/4ac9b11731db3bf688b849ffe6db41f1f156712f/doc/blog/ClassNode.png -------------------------------------------------------------------------------- /doc/blog/ClassVisitor.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dengshiwei/asm-module/4ac9b11731db3bf688b849ffe6db41f1f156712f/doc/blog/ClassVisitor.png -------------------------------------------------------------------------------- /doc/blog/CoreAPI.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dengshiwei/asm-module/4ac9b11731db3bf688b849ffe6db41f1f156712f/doc/blog/CoreAPI.png -------------------------------------------------------------------------------- /doc/blog/FieldNode.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dengshiwei/asm-module/4ac9b11731db3bf688b849ffe6db41f1f156712f/doc/blog/FieldNode.png -------------------------------------------------------------------------------- /doc/blog/FieldVisitor.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dengshiwei/asm-module/4ac9b11731db3bf688b849ffe6db41f1f156712f/doc/blog/FieldVisitor.png -------------------------------------------------------------------------------- /doc/blog/MethodNode.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dengshiwei/asm-module/4ac9b11731db3bf688b849ffe6db41f1f156712f/doc/blog/MethodNode.png -------------------------------------------------------------------------------- /doc/blog/MethodVisitor.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dengshiwei/asm-module/4ac9b11731db3bf688b849ffe6db41f1f156712f/doc/blog/MethodVisitor.png -------------------------------------------------------------------------------- /doc/blog/SignatureVisitor.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dengshiwei/asm-module/4ac9b11731db3bf688b849ffe6db41f1f156712f/doc/blog/SignatureVisitor.png -------------------------------------------------------------------------------- /doc/blog/TreeAPI.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dengshiwei/asm-module/4ac9b11731db3bf688b849ffe6db41f1f156712f/doc/blog/TreeAPI.png -------------------------------------------------------------------------------- /doc/blog/package_classes.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dengshiwei/asm-module/4ac9b11731db3bf688b849ffe6db41f1f156712f/doc/blog/package_classes.png -------------------------------------------------------------------------------- /doc/uml-file/AnalysisAPI.puml: -------------------------------------------------------------------------------- 1 | @startuml 2 | 3 | package org.object.web.asm.tree.analysis { 4 | class Analyzer 5 | class BasicInterpreter 6 | class BasicValue 7 | class BasicVerifier 8 | class Frame 9 | class Interpreter 10 | class SimpleVerifier 11 | class SourceInterpreter 12 | class SourceValue 13 | interface Value 14 | 15 | Value <|-- BasicValue 16 | Interpreter <|-- BasicInterpreter 17 | BasicInterpreter <-- BasicVerifier 18 | BasicVerifier <-- SimpleVerifier 19 | } 20 | 21 | @enduml -------------------------------------------------------------------------------- /doc/uml-file/CoreAPI.puml: -------------------------------------------------------------------------------- 1 | @startuml 2 | 3 | abstract class ClassVisitor { 4 | visit(int version, int access, String name, String signature, String superName, String[] interfaces) 5 | visitAnnotation(String descriptor, boolean visible) 6 | visitAttribute(Attribute attribute) 7 | visitEnd() 8 | visitField(int access, String name, String descriptor, String signature, Object value) 9 | visitInnerClass(String name, String outerName, String innerName, int access) 10 | visitMethod(int access, String name, String descriptor, String signature, String[] exceptions) 11 | visitModule(String name, int access, String version) 12 | visitNestHost(String nestHost) 13 | visitNestMember(String nestMember) 14 | visitOuterClass(String owner, String name, String descriptor) 15 | visitPermittedSubclass(String permittedSubclass) 16 | visitRecordComponent(String name, String descriptor, String signature) 17 | visitSource(String source, String debug) 18 | visitTypeAnnotation(int typeRef, TypePath typePath, String descriptor, boolean visible) 19 | } 20 | 21 | ClassVisitor <-- ClassWriter 22 | ClassVisitor <-- CheckClassAdapter 23 | ClassVisitor <-- ClassNode 24 | ClassVisitor <-- ClassRemapper 25 | ClassVisitor <-- SerialVersionUIDAdder 26 | ClassVisitor <-- StaticInitMerger 27 | ClassVisitor <-- TraceClassVisitor 28 | 29 | abstract class AnnotationVisitor { 30 | visit(String name, Object value) 31 | visitAnnotation(String name, String descriptor) 32 | visitArray(String name) 33 | visitEnd() 34 | visitEnum(String name, String descriptor, String value) 35 | } 36 | 37 | AnnotationVisitor <-- AnnotationNode 38 | AnnotationVisitor <-- TraceAnnotationVisitor 39 | AnnotationVisitor <-- CheckAnnotationAdapter 40 | AnnotationVisitor <-- AnnotationRemapper 41 | 42 | 43 | class FiledVisitor { 44 | visitAnnotation(String descriptor, boolean visible) 45 | visitAttribute(Attribute attribute) 46 | visitEnd() 47 | visitTypeAnnotation(int typeRef, TypePath typePath, String descriptor, boolean visible) 48 | } 49 | 50 | FiledVisitor <-- CheckFieldAdapter 51 | FiledVisitor <-- FieldNode 52 | FiledVisitor <-- FieldRemapper 53 | FiledVisitor <-- TraceFieldVisitor 54 | 55 | class MethodVisitor { 56 | visitAnnotableParameterCount(int parameterCount, boolean visible) 57 | visitAnnotation(String descriptor, boolean visible) 58 | visitAnnotationDefault() 59 | visitAttribute(Attribute attribute) 60 | visitCode() 61 | visitEnd() 62 | visitFieldInsn(int opcode, String owner, String name, String descriptor) 63 | visitFrame(int type, int numLocal, Object[] local, int numStack, Object[] stack) 64 | visitIincInsn(int var, int increment) 65 | visitInsn(int opcode) 66 | visitInsnAnnotation(int typeRef, TypePath typePath, String descriptor, boolean visible) 67 | visitIntInsn(int opcode, int operand) 68 | visitInvokeDynamicInsn(String name, String descriptor, Handle bootstrapMethodHandle, Object... bootstrapMethodArguments) 69 | visitJumpInsn(int opcode, Label label) 70 | visitLabel(Label label) 71 | visitLdcInsn(Object value) 72 | visitLineNumber(int line, Label start) 73 | visitLocalVariable(String name, String descriptor, String signature, Label start, Label end, int index) 74 | visitLocalVariableAnnotation(int typeRef, TypePath typePath, Label[] start, Label[] end, int[] index, String descriptor, boolean visible) 75 | visitLookupSwitchInsn(Label dflt, int[] keys, Label[] labels) 76 | visitMaxs(int maxStack, int maxLocals) 77 | visitMethodInsn(int opcode, String owner, String name, String descriptor, boolean isInterface) 78 | visitMultiANewArrayInsn(String descriptor, int numDimensions) 79 | visitParameter(String name, int access) 80 | visitParameterAnnotation(int parameter, String descriptor, boolean visible) 81 | visitTableSwitchInsn(int min, int max, Label dflt, Label... labels) 82 | visitTryCatchAnnotation(int typeRef, TypePath typePath, String descriptor, boolean visible) 83 | visitTryCatchBlock(Label start, Label end, Label handler, String type) 84 | visitTypeAnnotation(int typeRef, TypePath typePath, String descriptor, boolean visible) 85 | visitTypeInsn(int opcode, java.lang.String type) 86 | visitVarInsn(int opcode, int var) 87 | } 88 | 89 | MethodVisitor <-- AnalyzerAdapter 90 | MethodVisitor <-- CheckMethodAdapter 91 | MethodVisitor <-- TraceMethodVisitor 92 | MethodVisitor <-- MethodNode 93 | MethodVisitor <-- MethodRemapper 94 | MethodVisitor <-- LocalVariablesSorter 95 | MethodVisitor <-- InstructionAdapter 96 | MethodVisitor <-- CodeSizeEvaluator 97 | 98 | class Reader { 99 | {static}int EXPAND_FRAMES 100 | {static}int SKIP_CODE 101 | {static}int SKIP_DEBUG 102 | {static}int SKIP_FRAMES 103 | } 104 | 105 | @enduml -------------------------------------------------------------------------------- /doc/uml-file/SignatureVisitor.puml: -------------------------------------------------------------------------------- 1 | @startuml 2 | 3 | class SignatureReader { 4 | + accept(SignatureVisitor signatureVistor) 5 | + acceptType(SignatureVisitor signatureVisitor) 6 | } 7 | 8 | Abstract class SignatureVisitor { 9 | + visitArrayType() 10 | + visitBaseType(char descriptor) 11 | + visitClassBound() 12 | + visitClassType(java.lang.String name) 13 | + visitExceptionType() 14 | + visitInnerClassType(java.lang.String name) 15 | + visitInterface() 16 | + visitInterfaceBound() 17 | + visitParameterType() 18 | + visitReturnType() 19 | + visitSuperclass() 20 | + visitTypeArgument() 21 | + visitEnd() 22 | } 23 | 24 | SignatureVisitor<-- CheckSignatureAdapter 25 | SignatureVisitor<-- SignatureRemapper 26 | SignatureVisitor<-- SignatureWriter 27 | SignatureVisitor<-- TraceSignatureVisitor 28 | 29 | @enduml -------------------------------------------------------------------------------- /doc/uml-file/TreeAPI.puml: -------------------------------------------------------------------------------- 1 | @startuml 2 | 3 | package org.object.web.asm.tree { 4 | class AbstractInsnNode 5 | class AnnotationNode 6 | class ClassNode 7 | class FieldNode 8 | class FieldInsnNode 9 | class FrameNode 10 | class IincInsnNode 11 | class InnerClassNode 12 | class InsnList 13 | class InsnNode 14 | class InvokeDynamicInsnNode 15 | class JumpInsnNode 16 | class LabelNode 17 | class LdcInsnNode 18 | class LineNumberNode 19 | class LocalVariableAnnotationNode 20 | class LocalVariableNode 21 | class LookupSwitchInsnNode 22 | class MethodInsnNode 23 | class MethodNode 24 | class ModuleExportNode 25 | class ModuleNode 26 | class ModuleOpenNode 27 | class ModuleProvideNode 28 | class ModuleRequireNode 29 | class MultiANewArrayInsnNode 30 | class RecordComponentNode 31 | class TableSwitchInsnNode 32 | class TryCatchBlockNode 33 | class TypeAnnotationNode 34 | class TypeInsnNode 35 | class VarInsnNode 36 | } 37 | 38 | class ClassNode extends ClassVisitor { 39 | public int version; 40 | public int access; 41 | public String name; 42 | public String signature; 43 | public String superName; 44 | public List interfaces; 45 | public String sourceFile; 46 | public String sourceDebug; 47 | public String outerClass; 48 | public String outerMethod; 49 | public String outerMethodDesc; 50 | public List visibleAnnotations; 51 | public List invisibleAnnotations; 52 | public List attrs; 53 | public List innerClasses; 54 | public List fields; 55 | public List methods; 56 | public ClassNode(); 57 | public ClassNode(final int api) 58 | } 59 | class FieldNode extends FieldVisitor { 60 | public int access; 61 | public String name; 62 | public String desc; 63 | public String signature; 64 | public Object value; 65 | public FieldNode(int access, String name, String desc, String signature, Object value); 66 | } 67 | class MethodNode extends MethodVisitor { 68 | public int access; 69 | public String name; 70 | public String desc; 71 | public String signature; 72 | public List exceptions; 73 | public List visibleAnnotations; 74 | public List invisibleAnnotations; 75 | public List attrs; 76 | public Object annotationDefault; 77 | public List[] visibleParameterAnnotations; 78 | public List[] invisibleParameterAnnotations; 79 | public InsnList instructions; 80 | public List tryCatchBlocks; 81 | public List localVariables; 82 | public int maxStack; 83 | public int maxLocals; 84 | } 85 | 86 | 87 | @enduml -------------------------------------------------------------------------------- /doc/uml-file/package_classes.puml: -------------------------------------------------------------------------------- 1 | @startuml 2 | 3 | package org.objectweb.asm { 4 | class AnnotationVisitor 5 | class Attribute 6 | class ByteVector 7 | class ClassReader 8 | class ClassVisitor 9 | class ClassWriter 10 | class FieldVisitor 11 | class Handle 12 | class Label 13 | class MethodVisitor 14 | class Type 15 | interface Opcodes 16 | } 17 | 18 | package org.objectweb.asm.commons { 19 | class AdviceAdapter 20 | class AnalyzerAdapter 21 | class CodeSizeEvaluator 22 | class GeneratorAdapter 23 | class InstrucationAdapter 24 | class JSRInlinerAdapter 25 | class LocalVariablesSorter 26 | class Method 27 | class Remapper 28 | class RemappingAnnotationAdapter 29 | class RemappingClassAdapter 30 | class RemappingFieldAdapter 31 | class RemappingMethodAdapter 32 | class RemappingSignatureAdapter 33 | class SerialVersionUIDAdder 34 | class SimpleRemapper 35 | class StaticInitMerger 36 | class TryCatchBlockSorter 37 | interface TableSwitchGenerator 38 | } 39 | 40 | package org.objectweb.asm.tree { 41 | class AbstractInsnNode 42 | class AnnotationNode 43 | class ClassNode 44 | class FieldInsnNode 45 | class FieldNode 46 | class FrameNode 47 | class IincInsnNode 48 | class InnerClassNode 49 | class InsnList 50 | class InsnNode 51 | class IntInsnNode 52 | class InvokeDynamicInsnNode 53 | class JumpInsnNode 54 | class LabelNode 55 | class LdcInsnNode 56 | class LineNumberNode 57 | class LocalVariableAnnotationNode 58 | class LocalVariableNode 59 | class LookupSwitchInsnNode 60 | class MethodInsnNode 61 | class MethodNode 62 | class ModuleExportNode 63 | class ModuleNode 64 | class ModuleOpenNode 65 | class ModuleProvideNode 66 | class ModuleRequireNode 67 | class MultiANewArrayInsnNode 68 | class ParameterNode 69 | class RecordComponentNode 70 | class TableSwitchInsnNode 71 | class TryCatchBlockNode 72 | class TypeAnnotationNode 73 | class TypeInsnNode 74 | class VarInsnNode 75 | } 76 | 77 | package org.objectweb.asm.tree.analysis { 78 | class Analyzer 79 | class BasicInterpreter 80 | class BasicValue 81 | class BasicVerifier 82 | class Frame 83 | class Interpreter 84 | class SimpleVerifier 85 | class SourceInterpreter 86 | class SourceValue 87 | } 88 | 89 | package org.objectweb.asm.signature { 90 | class SignatureReader 91 | class SignatureVisitor 92 | class SignatureWriter 93 | } 94 | 95 | package org.objectweb.asm.util { 96 | class ASMifier 97 | class CheckAnnotationAdapter 98 | class CheckClassAdapter 99 | class CheckFieldAdapter 100 | class CheckMethodAdapter 101 | class CheckModuleAdapter 102 | class CheckRecordComponentAdapter 103 | class CheckSignatureAdapter 104 | class Printer 105 | class Textifier 106 | class TraceAnnotationVisitor 107 | class TraceClassVisitor 108 | class TraceFieldVisitor 109 | class TraceMethodVisitor 110 | class TraceModuleVisitor 111 | class TraceRecordComponentVisitor 112 | class TraceSignatureVisitor 113 | } 114 | @enduml -------------------------------------------------------------------------------- /gradle.properties: -------------------------------------------------------------------------------- 1 | # Project-wide Gradle settings. 2 | # IDE (e.g. Android Studio) users: 3 | # Gradle settings configured through the IDE *will override* 4 | # any settings specified in this file. 5 | # For more details on how to configure your build environment visit 6 | # http://www.gradle.org/docs/current/userguide/build_environment.html 7 | # Specifies the JVM arguments used for the daemon process. 8 | # The setting is particularly useful for tweaking memory settings. 9 | org.gradle.jvmargs=-Xmx2048m 10 | # When configured, Gradle will run in incubating parallel mode. 11 | # This option should only be used with decoupled projects. More details, visit 12 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects 13 | # org.gradle.parallel=true 14 | # AndroidX package structure to make it clearer which packages are bundled with the 15 | # Android operating system, and which are packaged with your app"s APK 16 | # https://developer.android.com/topic/libraries/support-library/androidx-rn 17 | android.useAndroidX=true 18 | # Automatically convert third-party libraries to use AndroidX 19 | android.enableJetifier=true 20 | # Kotlin code style for this project: "official" or "obsolete": 21 | kotlin.code.style=official -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dengshiwei/asm-module/4ac9b11731db3bf688b849ffe6db41f1f156712f/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | #Sun Aug 16 20:43:08 CST 2020 2 | distributionBase=GRADLE_USER_HOME 3 | distributionPath=wrapper/dists 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists 6 | distributionUrl=https\://services.gradle.org/distributions/gradle-6.1.1-all.zip 7 | -------------------------------------------------------------------------------- /gradlew: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | 3 | ############################################################################## 4 | ## 5 | ## Gradle start up script for UN*X 6 | ## 7 | ############################################################################## 8 | 9 | # Attempt to set APP_HOME 10 | # Resolve links: $0 may be a link 11 | PRG="$0" 12 | # Need this for relative symlinks. 13 | while [ -h "$PRG" ] ; do 14 | ls=`ls -ld "$PRG"` 15 | link=`expr "$ls" : '.*-> \(.*\)$'` 16 | if expr "$link" : '/.*' > /dev/null; then 17 | PRG="$link" 18 | else 19 | PRG=`dirname "$PRG"`"/$link" 20 | fi 21 | done 22 | SAVED="`pwd`" 23 | cd "`dirname \"$PRG\"`/" >/dev/null 24 | APP_HOME="`pwd -P`" 25 | cd "$SAVED" >/dev/null 26 | 27 | APP_NAME="Gradle" 28 | APP_BASE_NAME=`basename "$0"` 29 | 30 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 31 | DEFAULT_JVM_OPTS="" 32 | 33 | # Use the maximum available, or set MAX_FD != -1 to use that value. 34 | MAX_FD="maximum" 35 | 36 | warn () { 37 | echo "$*" 38 | } 39 | 40 | die () { 41 | echo 42 | echo "$*" 43 | echo 44 | exit 1 45 | } 46 | 47 | # OS specific support (must be 'true' or 'false'). 48 | cygwin=false 49 | msys=false 50 | darwin=false 51 | nonstop=false 52 | case "`uname`" in 53 | CYGWIN* ) 54 | cygwin=true 55 | ;; 56 | Darwin* ) 57 | darwin=true 58 | ;; 59 | MINGW* ) 60 | msys=true 61 | ;; 62 | NONSTOP* ) 63 | nonstop=true 64 | ;; 65 | esac 66 | 67 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar 68 | 69 | # Determine the Java command to use to start the JVM. 70 | if [ -n "$JAVA_HOME" ] ; then 71 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 72 | # IBM's JDK on AIX uses strange locations for the executables 73 | JAVACMD="$JAVA_HOME/jre/sh/java" 74 | else 75 | JAVACMD="$JAVA_HOME/bin/java" 76 | fi 77 | if [ ! -x "$JAVACMD" ] ; then 78 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME 79 | 80 | Please set the JAVA_HOME variable in your environment to match the 81 | location of your Java installation." 82 | fi 83 | else 84 | JAVACMD="java" 85 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 86 | 87 | Please set the JAVA_HOME variable in your environment to match the 88 | location of your Java installation." 89 | fi 90 | 91 | # Increase the maximum file descriptors if we can. 92 | if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then 93 | MAX_FD_LIMIT=`ulimit -H -n` 94 | if [ $? -eq 0 ] ; then 95 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then 96 | MAX_FD="$MAX_FD_LIMIT" 97 | fi 98 | ulimit -n $MAX_FD 99 | if [ $? -ne 0 ] ; then 100 | warn "Could not set maximum file descriptor limit: $MAX_FD" 101 | fi 102 | else 103 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" 104 | fi 105 | fi 106 | 107 | # For Darwin, add options to specify how the application appears in the dock 108 | if $darwin; then 109 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" 110 | fi 111 | 112 | # For Cygwin, switch paths to Windows format before running java 113 | if $cygwin ; then 114 | APP_HOME=`cygpath --path --mixed "$APP_HOME"` 115 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` 116 | JAVACMD=`cygpath --unix "$JAVACMD"` 117 | 118 | # We build the pattern for arguments to be converted via cygpath 119 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` 120 | SEP="" 121 | for dir in $ROOTDIRSRAW ; do 122 | ROOTDIRS="$ROOTDIRS$SEP$dir" 123 | SEP="|" 124 | done 125 | OURCYGPATTERN="(^($ROOTDIRS))" 126 | # Add a user-defined pattern to the cygpath arguments 127 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then 128 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" 129 | fi 130 | # Now convert the arguments - kludge to limit ourselves to /bin/sh 131 | i=0 132 | for arg in "$@" ; do 133 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` 134 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option 135 | 136 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition 137 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` 138 | else 139 | eval `echo args$i`="\"$arg\"" 140 | fi 141 | i=$((i+1)) 142 | done 143 | case $i in 144 | (0) set -- ;; 145 | (1) set -- "$args0" ;; 146 | (2) set -- "$args0" "$args1" ;; 147 | (3) set -- "$args0" "$args1" "$args2" ;; 148 | (4) set -- "$args0" "$args1" "$args2" "$args3" ;; 149 | (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; 150 | (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; 151 | (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; 152 | (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; 153 | (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; 154 | esac 155 | fi 156 | 157 | # Escape application args 158 | save () { 159 | for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done 160 | echo " " 161 | } 162 | APP_ARGS=$(save "$@") 163 | 164 | # Collect all arguments for the java command, following the shell quoting and substitution rules 165 | eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" 166 | 167 | # by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong 168 | if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then 169 | cd "$(dirname "$0")" 170 | fi 171 | 172 | exec "$JAVACMD" "$@" 173 | -------------------------------------------------------------------------------- /gradlew.bat: -------------------------------------------------------------------------------- 1 | @if "%DEBUG%" == "" @echo off 2 | @rem ########################################################################## 3 | @rem 4 | @rem Gradle startup script for Windows 5 | @rem 6 | @rem ########################################################################## 7 | 8 | @rem Set local scope for the variables with windows NT shell 9 | if "%OS%"=="Windows_NT" setlocal 10 | 11 | set DIRNAME=%~dp0 12 | if "%DIRNAME%" == "" set DIRNAME=. 13 | set APP_BASE_NAME=%~n0 14 | set APP_HOME=%DIRNAME% 15 | 16 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 17 | set DEFAULT_JVM_OPTS= 18 | 19 | @rem Find java.exe 20 | if defined JAVA_HOME goto findJavaFromJavaHome 21 | 22 | set JAVA_EXE=java.exe 23 | %JAVA_EXE% -version >NUL 2>&1 24 | if "%ERRORLEVEL%" == "0" goto init 25 | 26 | echo. 27 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 28 | echo. 29 | echo Please set the JAVA_HOME variable in your environment to match the 30 | echo location of your Java installation. 31 | 32 | goto fail 33 | 34 | :findJavaFromJavaHome 35 | set JAVA_HOME=%JAVA_HOME:"=% 36 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 37 | 38 | if exist "%JAVA_EXE%" goto init 39 | 40 | echo. 41 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 42 | echo. 43 | echo Please set the JAVA_HOME variable in your environment to match the 44 | echo location of your Java installation. 45 | 46 | goto fail 47 | 48 | :init 49 | @rem Get command-line arguments, handling Windows variants 50 | 51 | if not "%OS%" == "Windows_NT" goto win9xME_args 52 | 53 | :win9xME_args 54 | @rem Slurp the command line arguments. 55 | set CMD_LINE_ARGS= 56 | set _SKIP=2 57 | 58 | :win9xME_args_slurp 59 | if "x%~1" == "x" goto execute 60 | 61 | set CMD_LINE_ARGS=%* 62 | 63 | :execute 64 | @rem Setup the command line 65 | 66 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 67 | 68 | @rem Execute Gradle 69 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% 70 | 71 | :end 72 | @rem End local scope for the variables with windows NT shell 73 | if "%ERRORLEVEL%"=="0" goto mainEnd 74 | 75 | :fail 76 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 77 | rem the _cmd.exe /c_ return code! 78 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 79 | exit /b 1 80 | 81 | :mainEnd 82 | if "%OS%"=="Windows_NT" endlocal 83 | 84 | :omega 85 | -------------------------------------------------------------------------------- /settings.gradle: -------------------------------------------------------------------------------- 1 | include ':asm_example' 2 | rootProject.name = "asm_module" --------------------------------------------------------------------------------