Repository: lsieun/learn-java-asm Branch: main Commit: a8b6328a3207 Files: 231 Total size: 615.8 KB Directory structure: gitextract_4n4clgmd/ ├── .gitignore ├── LICENSE ├── README.md ├── accepted-words.dic ├── pom.xml └── src/ └── main/ └── java/ ├── lsieun/ │ ├── annotation/ │ │ └── todo/ │ │ └── ToDo.java │ ├── asm/ │ │ ├── analysis/ │ │ │ ├── ControlFlowAnalyzer.java │ │ │ ├── ControlFlowAnalyzer2.java │ │ │ ├── ControlFlowEdgeAnalyzer.java │ │ │ ├── ControlFlowEdgeAnalyzer2.java │ │ │ ├── ControlFlowGraphAnalyzer.java │ │ │ ├── DataFlowAnalyzer.java │ │ │ ├── InsnText.java │ │ │ ├── MockAnalyzer.java │ │ │ ├── RemoveDeadCodeNode.java │ │ │ ├── RemoveDeadCodeVisitor.java │ │ │ ├── RemoveUnusedCastNode.java │ │ │ ├── RemoveUnusedCastVisitor.java │ │ │ ├── cc/ │ │ │ │ ├── CyclomaticComplexity.java │ │ │ │ ├── CyclomaticComplexityAnalyzer.java │ │ │ │ └── CyclomaticComplexityFrame.java │ │ │ ├── cfg/ │ │ │ │ ├── ControlFlowBuilder.java │ │ │ │ ├── ControlFlowGraph.java │ │ │ │ ├── Edge.java │ │ │ │ ├── EdgeCreator.java │ │ │ │ ├── FramelessAnalyzer.java │ │ │ │ └── SimpleEdgeCreator.java │ │ │ ├── diagnosis/ │ │ │ │ ├── DeadCodeDiagnosis.java │ │ │ │ ├── NullDereferenceDiagnosis.java │ │ │ │ ├── NullabilityDiagnosis.java │ │ │ │ ├── RedundantVariableDiagnosis.java │ │ │ │ ├── RelatedInstructionDiagnosis.java │ │ │ │ └── ReverseEngineerMethodArgumentsDiagnosis.java │ │ │ ├── graph/ │ │ │ │ ├── InsnBlock.java │ │ │ │ ├── TextBox.java │ │ │ │ └── TextGraph.java │ │ │ ├── nullability/ │ │ │ │ ├── NullDeferenceInterpreter.java │ │ │ │ ├── Nullability.java │ │ │ │ ├── NullabilityAnalyzer.java │ │ │ │ ├── NullabilityFrame.java │ │ │ │ ├── NullabilityInterpreter.java │ │ │ │ └── NullabilityValue.java │ │ │ ├── state/ │ │ │ │ ├── StateInterpreter.java │ │ │ │ ├── StateType.java │ │ │ │ └── StateValue.java │ │ │ └── transition/ │ │ │ └── DestinationInterpreter.java │ │ ├── commons/ │ │ │ ├── ClassRemapperExample01.java │ │ │ ├── ClassRemapperExample02.java │ │ │ ├── ClassRemapperExample03.java │ │ │ ├── GeneratorAdapterExample01.java │ │ │ ├── InstructionAdapterExample01.java │ │ │ ├── MethodStackMapFrame02Visitor.java │ │ │ ├── MethodStackMapFrameVisitor.java │ │ │ ├── SerialVersionUIDAdderExample01.java │ │ │ └── StaticInitMergerExample01.java │ │ ├── core/ │ │ │ ├── ChangeURLVisitor.java │ │ │ ├── ClassChangeVersionVisitor.java │ │ │ ├── ClassCloneVisitor.java │ │ │ ├── ClassDecompileVisitor.java │ │ │ ├── ClassListMemberVisitor.java │ │ │ ├── ClassRemoveAttributeVisitor.java │ │ │ ├── MethodAroundVisitor.java │ │ │ ├── MethodAroundVisitor2.java │ │ │ ├── MethodEmptyBodyVisitor.java │ │ │ ├── MethodEnterVisitor.java │ │ │ ├── MethodExitVisitor.java │ │ │ ├── MethodFindInvokeVisitor.java │ │ │ ├── MethodFindRefVisitor.java │ │ │ ├── MethodParameterVisitor.java │ │ │ ├── MethodParameterVisitor2.java │ │ │ ├── MethodPatternAdapter.java │ │ │ ├── MethodRemoveAddZeroVisitor.java │ │ │ ├── MethodRemoveGetFieldPutFieldVisitor.java │ │ │ ├── MethodRemoveNopVisitor.java │ │ │ ├── MethodRemovePrintVisitor.java │ │ │ ├── MethodReplaceInvokeVisitor.java │ │ │ ├── MethodTimerVisitor.java │ │ │ ├── MethodTimerVisitor2.java │ │ │ ├── MethodTimerVisitor3.java │ │ │ ├── MethodTimerVisitor4.java │ │ │ ├── SuperPackageAttribute.java │ │ │ ├── counter/ │ │ │ │ ├── ClassCounterVisitor.java │ │ │ │ └── MethodCounterAdapter.java │ │ │ ├── empty/ │ │ │ │ ├── EmptyAnnotationVisitor.java │ │ │ │ ├── EmptyClassVisitor.java │ │ │ │ ├── EmptyFieldVisitor.java │ │ │ │ ├── EmptyMethodVisitor.java │ │ │ │ ├── EmptyModuleVisitor.java │ │ │ │ └── EmptyRecordComponentVisitor.java │ │ │ ├── info/ │ │ │ │ ├── InfoClassVisitor.java │ │ │ │ ├── InfoFieldVisitor.java │ │ │ │ └── InfoMethodVisitor.java │ │ │ └── timer/ │ │ │ ├── ClassTimerVisitor.java │ │ │ └── MethodTimerAdapter.java │ │ ├── template/ │ │ │ ├── CheckMethodAnnotationVisitor.java │ │ │ ├── ClassAddAnnotationVisitor.java │ │ │ ├── ClassAddCustomAttributeVisitor.java │ │ │ ├── ClassAddFieldVisitor.java │ │ │ ├── ClassAddInterfaceVisitor.java │ │ │ ├── ClassAddMethodVisitor.java │ │ │ ├── ClassGetAttributeContentVisitor.java │ │ │ ├── ClassMergeVisitor.java │ │ │ ├── ClassMergeVisitorRun.java │ │ │ ├── ClassPrintAnnotationVisitor.java │ │ │ ├── ClassPrintParameterVisitor.java │ │ │ ├── ClassRemoveFieldVisitor.java │ │ │ ├── ClassRemoveMethodVisitor.java │ │ │ ├── ClassRenameAdapter.java │ │ │ ├── ClassRenameAdapterRun.java │ │ │ ├── ClassReplaceMethodBodyVisitor.java │ │ │ ├── CustomAttribute.java │ │ │ ├── FieldAccessAdapter.java │ │ │ ├── FieldAccessAdapterRun.java │ │ │ ├── FieldAccessConverter.java │ │ │ ├── Info.java │ │ │ ├── MethodCallAdapter.java │ │ │ ├── MethodCallAdapterRun.java │ │ │ ├── MethodCallConverter.java │ │ │ ├── MethodEnteringAdapter.java │ │ │ ├── MethodEnteringVisitor.java │ │ │ ├── MethodEnteringVisitorRun.java │ │ │ ├── MethodExitingAdapter.java │ │ │ ├── MethodExitingAdapterRun.java │ │ │ ├── MethodExitingConverter.java │ │ │ ├── MethodFinallyAdapter.java │ │ │ ├── MethodFinallyAdapterRun.java │ │ │ ├── MethodFinallyConverter.java │ │ │ ├── MethodPrintInstructionAdapter.java │ │ │ ├── MethodPrintInstructionVisitor.java │ │ │ ├── MethodWithSameTryCatchLogicVisitor.java │ │ │ ├── MethodWithWholeTryCatchVisitor.java │ │ │ ├── RefRenameAdapter.java │ │ │ └── RemoveSyntheticVisitor.java │ │ ├── tree/ │ │ │ ├── ChangeMemberNameBasedOnAnnotationNode.java │ │ │ ├── ChangeThisNode.java │ │ │ ├── ClassAddCustomAttributeNode.java │ │ │ ├── ClassAddFieldNode.java │ │ │ ├── ClassAddMethodNode.java │ │ │ ├── ClassAddTimerNode.java │ │ │ ├── ClassRemoveFieldNode.java │ │ │ ├── ClassRemoveMethodNode.java │ │ │ ├── MixCore2TreeVisitor.java │ │ │ ├── MixTree2CoreNode.java │ │ │ ├── OptimizeJumpNode.java │ │ │ ├── RemoveGetFieldPutFieldNode.java │ │ │ ├── my/ │ │ │ │ ├── MyClassNode.java │ │ │ │ ├── MyClassVisitor.java │ │ │ │ ├── MyMethodAdapter.java │ │ │ │ └── MyMethodNode.java │ │ │ └── transformer/ │ │ │ ├── ClassTransformer.java │ │ │ └── MethodTransformer.java │ │ └── util/ │ │ ├── CheckClassAdapterExample01Generate.java │ │ ├── CheckClassAdapterExample02Generate.java │ │ ├── CodeUtils.java │ │ ├── TraceClassVisitorExample01Generate.java │ │ ├── TraceClassVisitorExample02Transform.java │ │ ├── TraceClassVisitorExample03.java │ │ └── TreePrinter.java │ ├── classfile/ │ │ ├── CPInfo.java │ │ ├── ClassFile.java │ │ └── InsnRaw.java │ ├── cst/ │ │ └── Const.java │ ├── drawing/ │ │ ├── canvas/ │ │ │ ├── Box.java │ │ │ ├── Canvas.java │ │ │ ├── Drawable.java │ │ │ ├── TextAlign.java │ │ │ ├── TextDirection.java │ │ │ └── TextPixel.java │ │ └── theme/ │ │ ├── line/ │ │ │ └── ContinuousLine.java │ │ ├── shape/ │ │ │ ├── Rectangle.java │ │ │ └── RectangleWithText.java │ │ ├── table/ │ │ │ ├── AbstractTable.java │ │ │ ├── FixedWidthOneLineTable.java │ │ │ └── OneLineTable.java │ │ └── text/ │ │ └── PlainText.java │ ├── trove/ │ │ ├── HashFunctions.java │ │ ├── PrimeFinder.java │ │ ├── THash.java │ │ ├── TIntArrayList.java │ │ ├── TIntFunction.java │ │ ├── TIntHash.java │ │ ├── TIntHashingStrategy.java │ │ ├── TIntIntHashMap.java │ │ ├── TIntIntIterator.java │ │ ├── TIntIntProcedure.java │ │ ├── TIntProcedure.java │ │ ├── TIterator.java │ │ ├── TPrimitiveHash.java │ │ ├── TPrimitiveIterator.java │ │ └── package-info.java │ └── utils/ │ ├── ASMUtilsCore.java │ ├── ASMUtilsTree.java │ ├── BoxDrawingUtils.java │ ├── ByteArrayClassLoader.java │ ├── ByteUtils.java │ ├── DescriptorUtils.java │ ├── FileUtils.java │ ├── FrameUtils.java │ ├── HexFormat.java │ ├── HexUtils.java │ ├── IOUtils.java │ ├── JarUtils.java │ ├── OpcodeConst.java │ ├── ReadUtils.java │ ├── StringUtils.java │ └── ValueUtils.java ├── run/ │ ├── ASMPrint.java │ ├── BoxDrawingRun.java │ ├── BytecodeRun.java │ ├── ControlFlowGraphRun.java │ ├── ControlFlowGraphType.java │ ├── HelloWorldAnalysisCore.java │ ├── HelloWorldAnalysisTree.java │ ├── HelloWorldFrameCore.java │ ├── HelloWorldFrameCore02.java │ ├── HelloWorldFrameTree.java │ ├── HelloWorldGenerateCore.java │ ├── HelloWorldGenerateTree.java │ ├── HelloWorldRun.java │ ├── HelloWorldTransformCore.java │ ├── HelloWorldTransformTree.java │ ├── MockAnalyzerRun.java │ ├── PrintASMCodeCore.java │ ├── PrintASMCodeTree.java │ ├── PrintASMTextClass.java │ ├── PrintASMTextLambda.java │ ├── PrintOpcodeTable.java │ └── jar/ │ ├── CompareTwoJarFile.java │ └── FindJarClassItem.java └── sample/ ├── HelloWorld.java └── ParameterUtils.java ================================================ FILE CONTENTS ================================================ ================================================ FILE: .gitignore ================================================ # Compiled class file *.class # Log file *.log # BlueJ files *.ctxt # Mobile Tools for Java (J2ME) .mtj.tmp/ # Package Files # *.jar *.war *.nar *.ear *.zip *.tar.gz *.rar # virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml hs_err_pid* # IDEA .idea/ *.iml *.kotlin_module target/ lib/ build/ dist/ .* *.data !.gitignore ================================================ FILE: LICENSE ================================================ MIT License Copyright (c) 2020 lsieun Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ================================================ FILE: README.md ================================================ # Learn Java ASM [![Gitee](https://img.shields.io/badge/Gitee-white.svg?style=flat-square&logo=gitee&logoColor=C71D23)](https://gitee.com/lsieun/learn-java-asm) [![Github](https://img.shields.io/badge/GitHub-white.svg?style=flat-square&logo=github&logoColor=181717)](https://github.com/lsieun/learn-java-asm) ![GitHub](https://img.shields.io/github/license/lsieun/learn-java-asm) ![GitHub stars](https://img.shields.io/github/stars/lsieun/learn-java-asm.svg) ![GitHub forks](https://img.shields.io/github/forks/lsieun/learn-java-asm.svg) ![GitHub issues](https://img.shields.io/github/issues-raw/lsieun/learn-java-asm?label=issues) ![GitHub last commit](https://img.shields.io/github/last-commit/lsieun/learn-java-asm.svg) :maple_leaf: Java [ASM](https://asm.ow2.io/) is an open-source java library for manipulating bytecode. 本项目旨在系统地介绍如何学习Java ASM的知识,主要涉及Core API、OPCODE和Tree API等内容。至于学习的预期目标就是,用一个形象的说法来讲,让字节码在你的手中“跳舞”:看看你的左手,一个完整的ClassFile拆解成不同粒度的字节码内容;看看你的右手,不同粒度的字节码内容又重新组织成一个ClassFile结构。 ```text _______ , _ _ (, /' /'/ /' `\ ' ) _) /' /' / /' ._) // _/~/' /'____ . , ____ ,/' / (____ /'/_/~ /' _ /'/' )| / /' ) /`--,/ ) /' /~ /' /' ` /'/' /' | /' /' /' /' / /' /' /' (_____,/' (___,/(___|/(__(___,/(__ (,/' (_,(_____,/'(,/' (_, ``` --- 如果我们学会了Java ASM之后,可能还是需要一个具体的应用场景来进行使用, 这个场景就是由 [Java Agent](https://docs.oracle.com/en/java/javase/25/docs/api/java.instrument/java/lang/instrument/Instrumentation.html) 开启的。 那么,Java ASM和Java Agent这两者之间是什么关系呢? Java ASM是一个操作字节码的工具(tool),而Java Agent提供了修改字节码的机会(opportunity)。 想像这样一个场景: 有一个JVM正在运行,突然Java Agent在JVM上打开一扇大门,Java ASM通过大门冲进JVM里面,就要开始修改字节码了。 ```text .class --- Java ASM --- Java Agent --- JVM ``` 再打个比方,Java ASM就是“一匹千里马”,而Java Agent就是“伯乐”。 如果遇不到“伯乐”,可能“千里马”的才能就埋没了;正因为有了“伯乐”,“千里马”就有了施展才能的机会。 ```text 世有伯乐,然后有千里马。 千里马常有,而伯乐不常有。 故虽有名马,祗辱于奴隶人之手,骈死于槽枥之间,不以千里称也。 ``` --- ## 1. 如何使用 ### 1.1 代码下载 [![Gitee](https://img.shields.io/badge/Gitee-white.svg?style=flat-square&logo=gitee&logoColor=C71D23)](https://gitee.com/lsieun/learn-java-asm) [![Github](https://img.shields.io/badge/GitHub-white.svg?style=flat-square&logo=github&logoColor=181717)](https://github.com/lsieun/learn-java-asm) 从[Gitee](https://gitee.com/lsieun/learn-java-asm) 仓库下载代码,使用如下命令: ```text git clone https://gitee.com/lsieun/learn-java-asm ``` 从[GitHub](https://github.com/lsieun/learn-java-asm) 仓库下载代码,使用如下命令: ```text git clone https://github.com/lsieun/learn-java-asm ``` ### 1.2 开发环境 [![Licence](https://img.shields.io/github/license/lsieun/learn-java-asm?style=social)](./LICENSE) ![Git](https://img.shields.io/badge/Git-white.svg?style=flat-square&logo=git&logoColor=F05032) ![Java](https://img.shields.io/badge/-Java-white.svg?style=flat-square&logo=java&logoColor=007396) ![Apache Maven](https://img.shields.io/badge/Maven-white.svg?style=flat-square&logo=Apache%20Maven&logoColor=C71A36) ![IntelliJ IDEA](https://img.shields.io/badge/IntelliJ_IDEA-white.svg?style=flat-square&logo=intellij-idea&logoColor=000000) - [Git](https://git-scm.com/) - [Java 8](https://www.oracle.com/java/technologies/javase/javase8-archive-downloads.html) - [Apache Maven](https://maven.apache.org/) - [IntelliJ IDEA](https://www.jetbrains.com/idea/download/other.html) (Ultimate or Community Edition) 在`learn-java-asm`项目当中,使用的ASM版本为`9.0`。如果想使用最新![Maven Central](https://img.shields.io/maven-central/v/org.ow2.asm/asm.svg?color=25a162&label=ASM) 版本,可以修改`pom.xml`文件中的`asm.version`属性: ```text 9.0 ``` ### 1.3. 运行代码 在`learn-java-asm`项目当中,包含`main`方法的类主要位于`run`包(`src/main/java/run`)。 ## 2. 课程资料 [![51cto](https://img.shields.io/website/https/edu.51cto.com.svg?label=51cto)](https://edu.51cto.com/lecturer/9210464.html) [![Bilibili](https://img.shields.io/website/https/bilibili.com.svg?label=bilibili&style=flat-square&logo=bilibili&logoColor=00A1D6)](https://space.bilibili.com/1321054247) [![lsieun.github.io](https://img.shields.io/website/https/lsieun.github.io.svg?label=lsieun.github.io)](https://lsieun.github.io) - 《Java ASM系列一:Core API》 - 文章地址: [lsieun.github.io](https://lsieun.github.io/java/asm/java-asm-season-01.html) | [51CTO](https://blog.51cto.com/lsieun/2924583) - 视频地址: [51CTO](https://edu.51cto.com/course/28517.html) | [Bilibili](https://space.bilibili.com/1321054247/channel/seriesdetail?sid=381716) - 《Java ASM系列二:OPCODE》 - 文章地址:[lsieun.github.io](https://lsieun.github.io/java/asm/java-asm-season-02.html) | [51CTO](https://blog.51cto.com/lsieun/3273965) - 视频地址:[51CTO](https://edu.51cto.com/course/28870.html) | [Bilibili](https://space.bilibili.com/1321054247/channel/seriesdetail?sid=381716) - 《Java ASM系列三:Tree API》 - 文章地址:[lsieun.github.io](https://lsieun.github.io/java/asm/java-asm-season-03.html) | [51CTO](https://blog.51cto.com/lsieun/4034588) - 视频地址:[51CTO](https://edu.51cto.com/course/29459.html) | [Bilibili](https://space.bilibili.com/1321054247/channel/seriesdetail?sid=381716) ### 2.1. ASM的组成部分 从组成结构上来说,Java ASM有Core API和Tree API两部分组成。 ```text ┌─── asm.jar │ ┌─── Core API ─────────┼─── asm-util.jar │ │ │ └─── asm-commons.jar Java ASM ───┤ │ │ ┌─── asm-tree.jar └─── Tree API ─────────┤ └─── asm-analysis.jar ``` 从依赖关系角度上说,Java ASM当中的各个`.jar`之间的依赖关系如下: ```text ┌────────────────────────────┬─────────────────────────────┐ │ ┌───────┴────────┐ │ │ util │ analysis │ commons │ │ ┌──────┴────────────────┴──────┐ │ │ │ tree │ │ ├─────────────┴──────────────────────────────┴─────────────┤ │ core │ └──────────────────────────────────────────────────────────┘ ``` ### 2.2. ASM能够做什么 从应用的角度来说,Java ASM可以进行Class Generation、Class Transformation和Class Analysis三个类型的操作。 ```text ┌─── find potential bugs │ ┌─── analysis ─────────┼─── detect unused code │ │ │ └─── reverse engineer code │ Java ASM ───┼─── generation │ │ ┌─── optimize programs │ │ └─── transformation ───┼─── obfuscate programs │ └─── insert performance monitoring code ``` ### 2.3. top, null和void 在下表当中,top、null和void三者相对应的转换值: ```text ┌─────────────┬────────────────────────────┬────────────────────────────────┐ │ .class │ ASM Type │ ASM Value in Frame │ ├─────────────┼────────────────────────────┼────────────────────────────────┤ │ top │ null │ BasicValue.UNINITIALIZED_VALUE │ ├─────────────┼────────────────────────────┼────────────────────────────────┤ │ aconst_null │ BasicInterpreter.NULL_TYPE │ BasicValue.REFERENCE_VALUE │ ├─────────────┼────────────────────────────┼────────────────────────────────┤ │ void │ Type.VOID_TYPE │ null │ └─────────────┴────────────────────────────┴────────────────────────────────┘ ``` ## 3. 注意事项 ### 3.1. 添加typo字典 在编写代码的过程中,会遇到一些Typo提示,原因是`insn`等内容不是合法的单词。 解决方法:借助于IntelliJ IDEA的[Spellchecking](https://www.jetbrains.com/help/idea/spellchecking.html) 的功能。 操作步骤: - 第一步,在`Settings/Preferences`当中,找到`Editor | Natural Languages | Spelling`位置。 - 第二步,在右侧的Custom dictionaries位置,添加**custom dictionary**,在`learn-java-asm`项目根目录下,有一个`accepted-words.dic`文件,添加该文件即可。 配置完成之后,需要**重新启动IntelliJ IDEA**才能生效。 ### 3.2. 查看笔记 在编写代码的过程中,为了方便理解代码,我添加了一些笔记,格式如下: ```text NOTE: 希望这是一条有用的笔记 ``` 但是,在默认情况下,它并不会高亮显示,因此不容易被察觉到。 解决方法:借助于IntelliJ IDEA的[TODO comments](https://www.jetbrains.com/help/idea/using-todo.html) 功能。 操作步骤: - 第一步,在`Settings/Preferences`当中,找到`Editor | TODO`位置。 - 第二步,在右侧的Patterns位置,添加以下内容: ```text \bnote\b.* ``` 配置完成之后,需要**重新启动IntelliJ IDEA**才能生效。 ### 3.3. 关闭调试信息 在默认情况下,运行任何类,都会输出调试信息。在调试信息中,会带有`[DEBUG]`标识。 如果想关闭调试信息,可以修改`lsieun.cst.Const`类的`DEBUG`字段值为`false`(默认值为`true`): ```java public class Const { public static final boolean DEBUG = false; } ``` 然后,执行`mvn clean compile`对类进行重新编译: ```text mvn clean compile ``` 等待编译完成之后,再次运行程序。 ## 4. 交流反馈 - 如果您有好的想法,可以提issues - 如果您想贡献代码,可以进行fork - 如果您有其它问题,可以添加QQ群(参考联系方式) ## 5. 联系方式 [![wechat](https://img.shields.io/badge/-lsieun-white.svg?style=flat-square&logo=wechat&logoColor=07C160)](https://lsieun.github.io/assets/images/contact/we-chat.jpg) [![Tencent QQ](https://img.shields.io/badge/515882294-white.svg?style=flat-square&logo=tencentqq&logoColor=EB1923)](https://lsieun.github.io/assets/images/contact/qq.png) [![QQ Group](https://img.shields.io/badge/584642776-white.svg?style=flat-square&logo=tencentqq&logoColor=1DA1F2&label=QQ%20Group)](https://lsieun.github.io/assets/images/contact/qq-group.jpg) [![Java字节码交流QQ群](https://pub.idqqimg.com/wpa/images/group.png)](https://jq.qq.com/?_wv=1027&k=yOBiOaJV) ## 6. License This project is licensed under the MIT License. See the [LICENSE](./LICENSE) file for the full license text. ================================================ FILE: accepted-words.dic ================================================ bilibili gitee insn lsieun ================================================ FILE: pom.xml ================================================ 4.0.0 lsieun learn-java-asm 1.0-SNAPSHOT UTF-8 UTF-8 1.8 ${java.version} ${java.version} 9.9.1 org.ow2.asm asm ${asm.version} org.ow2.asm asm-commons ${asm.version} org.ow2.asm asm-util ${asm.version} org.ow2.asm asm-tree ${asm.version} org.ow2.asm asm-analysis ${asm.version} org.apache.maven.plugins maven-compiler-plugin 3.13.0 ${java.version} ${java.version} true -g -parameters org.apache.maven.plugins maven-source-plugin 3.3.1 default-source-jar package jar ================================================ FILE: src/main/java/lsieun/annotation/todo/ToDo.java ================================================ package lsieun.annotation.todo; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; import static java.lang.annotation.ElementType.*; @Retention(RetentionPolicy.SOURCE) @Target({ TYPE, FIELD, CONSTRUCTOR, METHOD, PARAMETER, LOCAL_VARIABLE, ANNOTATION_TYPE, PACKAGE, TYPE_PARAMETER, TYPE_USE, // MODULE, // RECORD_COMPONENT, }) public @interface ToDo { String[] value(); } ================================================ FILE: src/main/java/lsieun/asm/analysis/ControlFlowAnalyzer.java ================================================ package lsieun.asm.analysis; import org.objectweb.asm.Opcodes; import org.objectweb.asm.tree.*; import org.objectweb.asm.tree.analysis.AnalyzerException; import java.util.ArrayList; import java.util.List; /** * 将 {@link org.objectweb.asm.tree.analysis.Analyzer}类的control flow analysis功能抽取出来. * * NOTE: 当前类抛弃了所有泛型相关的信息(V),抛弃了Frame、抛弃了Interpreter、抛弃了Value。 * 但是,它仍然能够实现control flow analysis的功能。 * * @see org.objectweb.asm.tree.analysis.Analyzer */ public class ControlFlowAnalyzer implements Opcodes { private InsnList insnList; private int insnListSize; private List[] handlers; /** * 记录需要处理的instructions. * * NOTE: 这三个字段为一组,应该一起处理,最好是放到同一个方法里来处理。 * 因此,我就加了三个新方法。 * {@link #initInstructionsToProcess()}、{@link #addInstructionsToProcess(int)}和 * {@link #removeInstructionsToProcess()} * * @see #initInstructionsToProcess() * @see #addInstructionsToProcess(int) * @see #removeInstructionsToProcess() */ private boolean[] inInstructionsToProcess; private int[] instructionsToProcess; private int numInstructionsToProcess; public ControlFlowAnalyzer() { } public List getHandlers(final int insnIndex) { return handlers[insnIndex]; } @SuppressWarnings("unchecked") // NOTE: analyze方法的返回值类型变成了void类型。 public void analyze(final String owner, final MethodNode method) throws AnalyzerException { if ((method.access & (ACC_ABSTRACT | ACC_NATIVE)) != 0) { return; } insnList = method.instructions; insnListSize = insnList.size(); handlers = (List[]) new List[insnListSize]; initInstructionsToProcess(); // For each exception handler, and each instruction within its range, record in 'handlers' the // fact that execution can flow from this instruction to the exception handler. for (int i = 0; i < method.tryCatchBlocks.size(); ++i) { TryCatchBlockNode tryCatchBlock = method.tryCatchBlocks.get(i); int startIndex = insnList.indexOf(tryCatchBlock.start); int endIndex = insnList.indexOf(tryCatchBlock.end); for (int j = startIndex; j < endIndex; ++j) { List insnHandlers = handlers[j]; if (insnHandlers == null) { insnHandlers = new ArrayList<>(); handlers[j] = insnHandlers; } insnHandlers.add(tryCatchBlock); } } // Initializes the data structures for the control flow analysis. // NOTE: 调用addInstructionsToProcess方法,传入参数0,启动整个循环过程。 addInstructionsToProcess(0); init(owner, method); // Control flow analysis. while (numInstructionsToProcess > 0) { // Get and remove one instruction from the list of instructions to process. int insnIndex = removeInstructionsToProcess(); // Simulate the execution of this instruction. AbstractInsnNode insnNode = method.instructions.get(insnIndex); int insnOpcode = insnNode.getOpcode(); int insnType = insnNode.getType(); if (insnType == AbstractInsnNode.LABEL || insnType == AbstractInsnNode.LINE || insnType == AbstractInsnNode.FRAME) { newControlFlowEdge(insnIndex, insnIndex + 1); } else { if (insnNode instanceof JumpInsnNode) { JumpInsnNode jumpInsn = (JumpInsnNode) insnNode; if (insnOpcode != GOTO && insnOpcode != JSR) { newControlFlowEdge(insnIndex, insnIndex + 1); } int jumpInsnIndex = insnList.indexOf(jumpInsn.label); newControlFlowEdge(insnIndex, jumpInsnIndex); } else if (insnNode instanceof LookupSwitchInsnNode) { LookupSwitchInsnNode lookupSwitchInsn = (LookupSwitchInsnNode) insnNode; int targetInsnIndex = insnList.indexOf(lookupSwitchInsn.dflt); newControlFlowEdge(insnIndex, targetInsnIndex); for (int i = 0; i < lookupSwitchInsn.labels.size(); ++i) { LabelNode label = lookupSwitchInsn.labels.get(i); targetInsnIndex = insnList.indexOf(label); newControlFlowEdge(insnIndex, targetInsnIndex); } } else if (insnNode instanceof TableSwitchInsnNode) { TableSwitchInsnNode tableSwitchInsn = (TableSwitchInsnNode) insnNode; int targetInsnIndex = insnList.indexOf(tableSwitchInsn.dflt); newControlFlowEdge(insnIndex, targetInsnIndex); for (int i = 0; i < tableSwitchInsn.labels.size(); ++i) { LabelNode label = tableSwitchInsn.labels.get(i); targetInsnIndex = insnList.indexOf(label); newControlFlowEdge(insnIndex, targetInsnIndex); } } else if (insnOpcode != ATHROW && (insnOpcode < IRETURN || insnOpcode > RETURN)) { newControlFlowEdge(insnIndex, insnIndex + 1); } } List insnHandlers = handlers[insnIndex]; if (insnHandlers != null) { for (TryCatchBlockNode tryCatchBlock : insnHandlers) { newControlFlowExceptionEdge(insnIndex, tryCatchBlock); } } } } protected void init(final String owner, final MethodNode method) throws AnalyzerException { // Nothing to do. } // NOTE: 这是一个新添加的方法 private void initInstructionsToProcess() { inInstructionsToProcess = new boolean[insnListSize]; instructionsToProcess = new int[insnListSize]; numInstructionsToProcess = 0; } // NOTE: 这是一个新添加的方法 private int removeInstructionsToProcess() { int insnIndex = this.instructionsToProcess[--numInstructionsToProcess]; inInstructionsToProcess[insnIndex] = false; return insnIndex; } // NOTE: 这是一个新添加的方法 private void addInstructionsToProcess(final int insnIndex) { if (!inInstructionsToProcess[insnIndex]) { inInstructionsToProcess[insnIndex] = true; instructionsToProcess[numInstructionsToProcess++] = insnIndex; } } protected void newControlFlowEdge(final int insnIndex, final int successorIndex) { // Nothing to do. addInstructionsToProcess(successorIndex); } protected void newControlFlowExceptionEdge(final int insnIndex, final TryCatchBlockNode tryCatchBlock) { newControlFlowExceptionEdge(insnIndex, insnList.indexOf(tryCatchBlock.handler)); } protected void newControlFlowExceptionEdge(final int insnIndex, final int successorIndex) { // Nothing to do. addInstructionsToProcess(successorIndex); } } ================================================ FILE: src/main/java/lsieun/asm/analysis/ControlFlowAnalyzer2.java ================================================ package lsieun.asm.analysis; import lsieun.asm.analysis.graph.InsnBlock; import org.objectweb.asm.tree.AbstractInsnNode; import org.objectweb.asm.tree.MethodNode; import org.objectweb.asm.tree.analysis.AnalyzerException; import java.util.List; public class ControlFlowAnalyzer2 extends ControlFlowAnalyzer{ private AbstractInsnNode[] nodeArray; public InsnBlock[] blocks; public ControlFlowAnalyzer2() { } @Override public void analyze(String owner, MethodNode method) throws AnalyzerException { nodeArray = method.instructions.toArray(); int length = nodeArray.length; blocks = new InsnBlock[length]; InsnText insnText = new InsnText(); for (int i = 0; i < length; i++) { blocks[i] = getBlock(i); AbstractInsnNode node = nodeArray[i]; List lines = insnText.toLines(node); blocks[i].addLines(lines); } super.analyze(owner, method); } @Override protected void newControlFlowEdge(int insnIndex, int successorIndex) { // 首先,处理自己的代码逻辑 AbstractInsnNode insnNode = nodeArray[insnIndex]; int insnOpcode = insnNode.getOpcode(); int insnType = insnNode.getType(); if (insnType == AbstractInsnNode.JUMP_INSN) { if ((insnIndex + 1) == successorIndex) { addNext(insnIndex, successorIndex); } else { addJump(insnIndex, successorIndex); } } else if (insnOpcode == LOOKUPSWITCH) { addJump(insnIndex, successorIndex); } else if (insnOpcode == TABLESWITCH) { addJump(insnIndex, successorIndex); } else if (insnOpcode == RET) { addJump(insnIndex, successorIndex); } else if (insnOpcode == ATHROW || (insnOpcode >= IRETURN && insnOpcode <= RETURN)) { assert false : "should not be here"; removeNextAndJump(insnIndex); } else { addNext(insnIndex, successorIndex); } // 其次,调用父类的方法实现 super.newControlFlowEdge(insnIndex, successorIndex); } private void addNext(int fromIndex, int toIndex) { InsnBlock currentBlock = getBlock(fromIndex); InsnBlock nextBlock = getBlock(toIndex); currentBlock.addNext(nextBlock); } private void addJump(int fromIndex, int toIndex) { InsnBlock currentBlock = getBlock(fromIndex); InsnBlock nextBlock = getBlock(toIndex); currentBlock.addJump(nextBlock); } private void removeNextAndJump(int insnIndex) { InsnBlock currentBlock = getBlock(insnIndex); currentBlock.nextBlockList.clear(); currentBlock.jumpBlockList.clear(); } private InsnBlock getBlock(int insnIndex) { InsnBlock block = blocks[insnIndex]; if (block == null){ block = new InsnBlock(); blocks[insnIndex] = block; } return block; } public InsnBlock[] getBlocks() { return blocks; } } ================================================ FILE: src/main/java/lsieun/asm/analysis/ControlFlowEdgeAnalyzer.java ================================================ package lsieun.asm.analysis; import lsieun.asm.analysis.graph.InsnBlock; import org.objectweb.asm.tree.AbstractInsnNode; import org.objectweb.asm.tree.MethodNode; import org.objectweb.asm.tree.analysis.*; import java.util.List; public class ControlFlowEdgeAnalyzer extends Analyzer { private AbstractInsnNode[] nodeArray; public InsnBlock[] blocks; public ControlFlowEdgeAnalyzer(Interpreter interpreter) { super(interpreter); } @Override public Frame[] analyze(String owner, MethodNode method) throws AnalyzerException { nodeArray = method.instructions.toArray(); int length = nodeArray.length; blocks = new InsnBlock[length]; InsnText insnText = new InsnText(); for (int i = 0; i < length; i++) { blocks[i] = getBlock(i); AbstractInsnNode node = nodeArray[i]; List lines = insnText.toLines(node); blocks[i].addLines(lines); } return super.analyze(owner, method); } @Override protected void newControlFlowEdge(int insnIndex, int successorIndex) { // 首先,处理自己的代码逻辑 AbstractInsnNode insnNode = nodeArray[insnIndex]; int insnOpcode = insnNode.getOpcode(); int insnType = insnNode.getType(); if (insnType == AbstractInsnNode.JUMP_INSN) { if ((insnIndex + 1) == successorIndex) { addNext(insnIndex, successorIndex); } else { addJump(insnIndex, successorIndex); } } else if (insnOpcode == LOOKUPSWITCH) { addJump(insnIndex, successorIndex); } else if (insnOpcode == TABLESWITCH) { addJump(insnIndex, successorIndex); } else if (insnOpcode == RET) { addJump(insnIndex, successorIndex); } else if (insnOpcode == ATHROW || (insnOpcode >= IRETURN && insnOpcode <= RETURN)) { assert false : "should not be here"; removeNextAndJump(insnIndex); } else { addNext(insnIndex, successorIndex); } // 其次,调用父类的方法实现 super.newControlFlowEdge(insnIndex, successorIndex); } private void addNext(int fromIndex, int toIndex) { InsnBlock currentBlock = getBlock(fromIndex); InsnBlock nextBlock = getBlock(toIndex); currentBlock.addNext(nextBlock); } private void addJump(int fromIndex, int toIndex) { InsnBlock currentBlock = getBlock(fromIndex); InsnBlock nextBlock = getBlock(toIndex); currentBlock.addJump(nextBlock); } private void removeNextAndJump(int insnIndex) { InsnBlock currentBlock = getBlock(insnIndex); currentBlock.nextBlockList.clear(); currentBlock.jumpBlockList.clear(); } private InsnBlock getBlock(int insnIndex) { InsnBlock block = blocks[insnIndex]; if (block == null){ block = new InsnBlock(); blocks[insnIndex] = block; } return block; } public InsnBlock[] getBlocks() { return blocks; } } ================================================ FILE: src/main/java/lsieun/asm/analysis/ControlFlowEdgeAnalyzer2.java ================================================ package lsieun.asm.analysis; import lsieun.asm.analysis.graph.InsnBlock; import org.objectweb.asm.tree.analysis.Interpreter; import org.objectweb.asm.tree.analysis.Value; import java.util.ArrayList; import java.util.HashSet; import java.util.List; import java.util.Set; public class ControlFlowEdgeAnalyzer2 extends ControlFlowEdgeAnalyzer { public ControlFlowEdgeAnalyzer2(Interpreter interpreter) { super(interpreter); } @Override public InsnBlock[] getBlocks() { //(1)调用父类实现 InsnBlock[] blocks = super.getBlocks(); //(2)如果结果为空,则提前返回 if (blocks == null || blocks.length < 1) { return blocks; } //(3)记录“分隔点” Set newBlockSet = new HashSet<>(); int length = blocks.length; for (int i = 0; i < length; i++) { InsnBlock currentBlock = blocks[i]; List nextBlockList = currentBlock.nextBlockList; List jumpBlockList = currentBlock.jumpBlockList; boolean hasNext = false; boolean hasJump = false; if (nextBlockList.size() > 0) { hasNext = true; } if (jumpBlockList.size() > 0) { hasJump = true; } if (!hasNext && (i + 1) < length) { newBlockSet.add(blocks[i + 1]); } if (hasJump) { newBlockSet.addAll(jumpBlockList); if (hasNext) { newBlockSet.add(blocks[i + 1]); } } } //(4)利用“分隔点”,合并成不同的分组 List resultList = new ArrayList<>(); for (int i = 0; i < length; i++) { InsnBlock currentBlock = blocks[i]; if (i == 0) { resultList.add(currentBlock); } else if (newBlockSet.contains(currentBlock)) { resultList.add(currentBlock); } else { int size = resultList.size(); InsnBlock lastBlock = resultList.get(size - 1); lastBlock.lines.addAll(currentBlock.lines); lastBlock.jumpBlockList.clear(); lastBlock.jumpBlockList.addAll(currentBlock.jumpBlockList); lastBlock.nextBlockList.clear(); lastBlock.nextBlockList.addAll(currentBlock.nextBlockList); } } return resultList.toArray(new InsnBlock[0]); } } ================================================ FILE: src/main/java/lsieun/asm/analysis/ControlFlowGraphAnalyzer.java ================================================ package lsieun.asm.analysis; import lsieun.asm.analysis.graph.InsnBlock; import org.objectweb.asm.tree.*; import java.util.*; import static org.objectweb.asm.Opcodes.*; public class ControlFlowGraphAnalyzer { private final Set jumpLabelSet = new HashSet<>(); private final Map> jumpLabelMap = new HashMap<>(); private final Map labelInBlockMap = new HashMap<>(); private final InsnText insnText = new InsnText(); private InsnBlock currentBlock; private final List nodeList = new ArrayList<>(); private final List blockList = new ArrayList<>(); public ControlFlowGraphAnalyzer() { this.currentBlock = new InsnBlock(); } public void analyze(MethodNode mn) { findJumpLabel(mn); instruction2Block(mn); linkBlocks(); } private void findJumpLabel(MethodNode mn) { InsnList instructions = mn.instructions; for (AbstractInsnNode node : instructions) { if (node instanceof JumpInsnNode) { // 当前block与跳转目标的关系 JumpInsnNode currentNode = (JumpInsnNode) node; jumpLabelSet.add(currentNode.label); } else if (node instanceof TableSwitchInsnNode) { // 当前block与跳转目标的关系 TableSwitchInsnNode currentNode = (TableSwitchInsnNode) node; jumpLabelSet.add(currentNode.dflt); jumpLabelSet.addAll(currentNode.labels); } else if (node instanceof LookupSwitchInsnNode) { // 当前block与跳转目标的关系 LookupSwitchInsnNode currentNode = (LookupSwitchInsnNode) node; jumpLabelSet.add(currentNode.dflt); jumpLabelSet.addAll(currentNode.labels); } } List tryCatchBlocks = mn.tryCatchBlocks; for (TryCatchBlockNode node : tryCatchBlocks) { jumpLabelSet.add(node.handler); } } private void instruction2Block(MethodNode mn) { InsnList instructions = mn.instructions; for (AbstractInsnNode node : instructions) { int opcode = node.getOpcode(); if (node instanceof JumpInsnNode) { nodeList.add(node); // 当前block收集数据完成 completeBlock(); // 当前block与跳转目标的关系 JumpInsnNode currentNode = (JumpInsnNode) node; addJumpFromBlockToLabel(currentNode.label); // 当前block与下一个block的关系 InsnBlock nextBlock = new InsnBlock(); if ((opcode >= IFEQ && opcode <= IF_ACMPNE) || (opcode >= IFNULL && opcode <= IFNONNULL)) { currentBlock.nextBlockList.add(nextBlock); } // 下一个block成为当前block currentBlock = nextBlock; } else if (node instanceof TableSwitchInsnNode) { nodeList.add(node); // 当前block收集数据完成 completeBlock(); // 当前block与跳转目标的关系 TableSwitchInsnNode currentNode = (TableSwitchInsnNode) node; int min = currentNode.min; int max = currentNode.max; for (int i = min; i <= max; i++) { addJumpFromBlockToLabel(currentNode.labels.get(i - min)); } addJumpFromBlockToLabel(currentNode.dflt); // 下一个block成为当前block InsnBlock nextBlock = new InsnBlock(); currentBlock = nextBlock; } else if (node instanceof LookupSwitchInsnNode) { nodeList.add(node); // 当前block收集数据完成 completeBlock(); // 当前block与跳转目标的关系 LookupSwitchInsnNode currentNode = (LookupSwitchInsnNode) node; List labels = currentNode.labels; for (LabelNode labelNode : labels) { addJumpFromBlockToLabel(labelNode); } addJumpFromBlockToLabel(currentNode.dflt); // 下一个block成为当前block InsnBlock nextBlock = new InsnBlock(); currentBlock = nextBlock; } else if (node instanceof LabelNode) { LabelNode currentNode = (LabelNode) node; if (jumpLabelSet.contains(currentNode)) { if (nodeList.size() > 0) { // 当前block收集数据完成 completeBlock(); // 下一个block成为当前block InsnBlock nextBlock = new InsnBlock(); currentBlock.nextBlockList.add(nextBlock); currentBlock = nextBlock; } } nodeList.add(node); labelInBlockMap.put(currentNode, currentBlock); } else if (node instanceof InsnNode) { nodeList.add(node); // 当前block收集数据完成 if ((opcode >= IRETURN && opcode <= RETURN) || (opcode == ATHROW)) { completeBlock(); InsnBlock nextBlock = new InsnBlock(); currentBlock = nextBlock; } } else { nodeList.add(node); } } if (nodeList.size() > 0) { // 当前block收集数据完成 completeBlock(); } } private void linkBlocks() { for (Map.Entry> item : jumpLabelMap.entrySet()) { LabelNode key = item.getKey(); Set set = item.getValue(); InsnBlock targetBlock = labelInBlockMap.get(key); for (InsnBlock block : set) { block.jumpBlockList.add(targetBlock); } } } private void addJumpFromBlockToLabel(LabelNode labelNode) { Set list = jumpLabelMap.get(labelNode); if (list != null) { list.add(currentBlock); } else { list = new HashSet<>(); list.add(currentBlock); jumpLabelMap.put(labelNode, list); } } private void completeBlock() { for (AbstractInsnNode node : nodeList) { List lines = insnText.toLines(node); currentBlock.addLines(lines); } nodeList.clear(); blockList.add(currentBlock); } public InsnBlock[] getBlocks() { return blockList.toArray(new InsnBlock[0]); } } ================================================ FILE: src/main/java/lsieun/asm/analysis/DataFlowAnalyzer.java ================================================ package lsieun.asm.analysis; import org.objectweb.asm.Opcodes; import org.objectweb.asm.Type; import org.objectweb.asm.tree.*; import org.objectweb.asm.tree.analysis.AnalyzerException; import org.objectweb.asm.tree.analysis.Frame; import org.objectweb.asm.tree.analysis.Interpreter; import org.objectweb.asm.tree.analysis.Value; import java.util.ArrayList; import java.util.List; public class DataFlowAnalyzer implements Opcodes { private final Interpreter interpreter; // 数据输入:指令集和异常处理 private InsnList insnList; private int insnListSize; private List[] handlers; // 中间状态:记录需要哪一个指令需要处理 private boolean[] inInstructionsToProcess; private int[] instructionsToProcess; private int numInstructionsToProcess; // 数据输出:最终的返回结果 private Frame[] frames; public DataFlowAnalyzer(final Interpreter interpreter) { this.interpreter = interpreter; } public List getHandlers(final int insnIndex) { return handlers[insnIndex]; } public Frame[] getFrames() { return frames; } @SuppressWarnings("unchecked") public Frame[] analyze(final String owner, final MethodNode method) throws AnalyzerException { if ((method.access & (ACC_ABSTRACT | ACC_NATIVE)) != 0) { frames = (Frame[]) new Frame[0]; return frames; } insnList = method.instructions; insnListSize = insnList.size(); handlers = (List[]) new List[insnListSize]; frames = (Frame[]) new Frame[insnListSize]; inInstructionsToProcess = new boolean[insnListSize]; instructionsToProcess = new int[insnListSize]; numInstructionsToProcess = 0; // For each exception handler, and each instruction within its range, record in 'handlers' the // fact that execution can flow from this instruction to the exception handler. for (int i = 0; i < method.tryCatchBlocks.size(); ++i) { TryCatchBlockNode tryCatchBlock = method.tryCatchBlocks.get(i); int startIndex = insnList.indexOf(tryCatchBlock.start); int endIndex = insnList.indexOf(tryCatchBlock.end); for (int j = startIndex; j < endIndex; ++j) { List insnHandlers = handlers[j]; if (insnHandlers == null) { insnHandlers = new ArrayList<>(); handlers[j] = insnHandlers; } insnHandlers.add(tryCatchBlock); } } // Initializes the data structures for the control flow analysis. Frame currentFrame = computeInitialFrame(owner, method); merge(0, currentFrame); init(owner, method); // Control flow analysis. while (numInstructionsToProcess > 0) { // Get and remove one instruction from the list of instructions to process. int insnIndex = instructionsToProcess[--numInstructionsToProcess]; Frame oldFrame = frames[insnIndex]; inInstructionsToProcess[insnIndex] = false; // Simulate the execution of this instruction. AbstractInsnNode insnNode = null; try { insnNode = method.instructions.get(insnIndex); int insnOpcode = insnNode.getOpcode(); int insnType = insnNode.getType(); if (insnType == AbstractInsnNode.LABEL || insnType == AbstractInsnNode.LINE || insnType == AbstractInsnNode.FRAME) { merge(insnIndex + 1, oldFrame); newControlFlowEdge(insnIndex, insnIndex + 1); } else { currentFrame.init(oldFrame).execute(insnNode, interpreter); if (insnNode instanceof JumpInsnNode) { JumpInsnNode jumpInsn = (JumpInsnNode) insnNode; if (insnOpcode != GOTO && insnOpcode != JSR) { currentFrame.initJumpTarget(insnOpcode, /* target = */ null); merge(insnIndex + 1, currentFrame); newControlFlowEdge(insnIndex, insnIndex + 1); } int jumpInsnIndex = insnList.indexOf(jumpInsn.label); currentFrame.initJumpTarget(insnOpcode, jumpInsn.label); merge(jumpInsnIndex, currentFrame); newControlFlowEdge(insnIndex, jumpInsnIndex); } else if (insnNode instanceof LookupSwitchInsnNode) { LookupSwitchInsnNode lookupSwitchInsn = (LookupSwitchInsnNode) insnNode; int targetInsnIndex = insnList.indexOf(lookupSwitchInsn.dflt); currentFrame.initJumpTarget(insnOpcode, lookupSwitchInsn.dflt); merge(targetInsnIndex, currentFrame); newControlFlowEdge(insnIndex, targetInsnIndex); for (int i = 0; i < lookupSwitchInsn.labels.size(); ++i) { LabelNode label = lookupSwitchInsn.labels.get(i); targetInsnIndex = insnList.indexOf(label); currentFrame.initJumpTarget(insnOpcode, label); merge(targetInsnIndex, currentFrame); newControlFlowEdge(insnIndex, targetInsnIndex); } } else if (insnNode instanceof TableSwitchInsnNode) { TableSwitchInsnNode tableSwitchInsn = (TableSwitchInsnNode) insnNode; int targetInsnIndex = insnList.indexOf(tableSwitchInsn.dflt); currentFrame.initJumpTarget(insnOpcode, tableSwitchInsn.dflt); merge(targetInsnIndex, currentFrame); newControlFlowEdge(insnIndex, targetInsnIndex); for (int i = 0; i < tableSwitchInsn.labels.size(); ++i) { LabelNode label = tableSwitchInsn.labels.get(i); currentFrame.initJumpTarget(insnOpcode, label); targetInsnIndex = insnList.indexOf(label); merge(targetInsnIndex, currentFrame); newControlFlowEdge(insnIndex, targetInsnIndex); } } else if (insnOpcode != ATHROW && (insnOpcode < IRETURN || insnOpcode > RETURN)) { merge(insnIndex + 1, currentFrame); newControlFlowEdge(insnIndex, insnIndex + 1); } } List insnHandlers = handlers[insnIndex]; if (insnHandlers != null) { for (TryCatchBlockNode tryCatchBlock : insnHandlers) { Type catchType; if (tryCatchBlock.type == null) { catchType = Type.getObjectType("java/lang/Throwable"); } else { catchType = Type.getObjectType(tryCatchBlock.type); } if (newControlFlowExceptionEdge(insnIndex, tryCatchBlock)) { Frame handler = newFrame(oldFrame); handler.clearStack(); handler.push(interpreter.newExceptionValue(tryCatchBlock, handler, catchType)); merge(insnList.indexOf(tryCatchBlock.handler), handler); } } } } catch (AnalyzerException e) { throw new AnalyzerException(e.node, "Error at instruction " + insnIndex + ": " + e.getMessage(), e); } catch (RuntimeException e) { // DontCheck(IllegalCatch): can't be fixed, for backward compatibility. throw new AnalyzerException(insnNode, "Error at instruction " + insnIndex + ": " + e.getMessage(), e); } } return frames; } private Frame computeInitialFrame(final String owner, final MethodNode method) { Frame frame = newFrame(method.maxLocals, method.maxStack); int currentLocal = 0; boolean isInstanceMethod = (method.access & ACC_STATIC) == 0; if (isInstanceMethod) { Type ownerType = Type.getObjectType(owner); frame.setLocal(currentLocal, interpreter.newParameterValue(isInstanceMethod, currentLocal, ownerType)); currentLocal++; } Type[] argumentTypes = Type.getArgumentTypes(method.desc); for (Type argumentType : argumentTypes) { frame.setLocal( currentLocal, interpreter.newParameterValue(isInstanceMethod, currentLocal, argumentType)); currentLocal++; if (argumentType.getSize() == 2) { frame.setLocal(currentLocal, interpreter.newEmptyValue(currentLocal)); currentLocal++; } } while (currentLocal < method.maxLocals) { frame.setLocal(currentLocal, interpreter.newEmptyValue(currentLocal)); currentLocal++; } frame.setReturn(interpreter.newReturnTypeValue(Type.getReturnType(method.desc))); return frame; } protected void init(final String owner, final MethodNode method) throws AnalyzerException { // Nothing to do. } protected Frame newFrame(final int numLocals, final int numStack) { return new Frame<>(numLocals, numStack); } protected Frame newFrame(final Frame frame) { return new Frame<>(frame); } protected void newControlFlowEdge(final int insnIndex, final int successorIndex) { // Nothing to do. } protected boolean newControlFlowExceptionEdge(final int insnIndex, final TryCatchBlockNode tryCatchBlock) { return newControlFlowExceptionEdge(insnIndex, insnList.indexOf(tryCatchBlock.handler)); } protected boolean newControlFlowExceptionEdge(final int insnIndex, final int successorIndex) { return true; } // ----------------------------------------------------------------------------------------------- private void merge(final int insnIndex, final Frame frame) throws AnalyzerException { boolean changed; Frame oldFrame = frames[insnIndex]; if (oldFrame == null) { frames[insnIndex] = newFrame(frame); changed = true; } else { changed = oldFrame.merge(frame, interpreter); } if (changed && !inInstructionsToProcess[insnIndex]) { inInstructionsToProcess[insnIndex] = true; instructionsToProcess[numInstructionsToProcess++] = insnIndex; } } } ================================================ FILE: src/main/java/lsieun/asm/analysis/InsnText.java ================================================ package lsieun.asm.analysis; import lsieun.utils.OpcodeConst; import org.objectweb.asm.Type; import org.objectweb.asm.tree.*; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import static org.objectweb.asm.Opcodes.BIPUSH; import static org.objectweb.asm.Opcodes.SIPUSH; public class InsnText { private static final String INSTRUCTION_FORMAT = "%s %s"; private final Map labelNames = new HashMap<>(); public List toLines(AbstractInsnNode[] array) { List resultList = new ArrayList<>(); if (array == null || array.length < 1) { return resultList; } for (AbstractInsnNode node : array) { List lines = toLines(node); resultList.addAll(lines); } return resultList; } public List toLines(AbstractInsnNode node) { List resultList = new ArrayList<>(); if (node instanceof TableSwitchInsnNode) { TableSwitchInsnNode currentNode = (TableSwitchInsnNode) node; List list = table_switch_node_to_str_list(currentNode); resultList.addAll(list); } else if (node instanceof LookupSwitchInsnNode) { LookupSwitchInsnNode currentNode = (LookupSwitchInsnNode) node; List list = lookup_switch_node_to_str_list(currentNode); resultList.addAll(list); } else { String item = node_to_str(node); resultList.add(item); } return resultList; } public String node_to_str(AbstractInsnNode currentNode) { if (currentNode instanceof InsnNode) { return getOpcodeName(currentNode); } else if (currentNode instanceof IntInsnNode) { int opcode = currentNode.getOpcode(); String opcodeName = getOpcodeName(currentNode); IntInsnNode node = (IntInsnNode) currentNode; int operand = node.operand; if (opcode == BIPUSH || opcode == SIPUSH) { return String.format(INSTRUCTION_FORMAT, opcodeName, operand); } else { final String firstArg; switch (operand) { case 4: { firstArg = "4 (boolean)"; break; } case 5: { firstArg = "5 (char)"; break; } case 6: { firstArg = "6 (float)"; break; } case 7: { firstArg = "7 (double)"; break; } case 8: { firstArg = "8 (byte)"; break; } case 9: { firstArg = "9 (short)"; break; } case 10: { firstArg = "10 (int)"; break; } case 11: { firstArg = "11 (long)"; break; } default: throw new RuntimeException("atype is not supported: " + operand); } return String.format(INSTRUCTION_FORMAT, opcodeName, firstArg); } } else if (currentNode instanceof VarInsnNode) { String opcodeName = getOpcodeName(currentNode); VarInsnNode node = (VarInsnNode) currentNode; int var = node.var; if (var >= 0 && var <= 3) { return String.format("%s_%d", opcodeName, var); } else { return String.format(INSTRUCTION_FORMAT, opcodeName, var); } } else if (currentNode instanceof TypeInsnNode) { String opcodeName = getOpcodeName(currentNode); TypeInsnNode node = (TypeInsnNode) currentNode; String type = getSimpleName(node.desc); return String.format(INSTRUCTION_FORMAT, opcodeName, type); } else if (currentNode instanceof FieldInsnNode) { String opcodeName = getOpcodeName(currentNode); FieldInsnNode node = (FieldInsnNode) currentNode; String type = getSimpleName(node.owner); return String.format("%s %s.%s", opcodeName, type, node.name); } else if (currentNode instanceof MethodInsnNode) { String opcodeName = getOpcodeName(currentNode); MethodInsnNode node = (MethodInsnNode) currentNode; String type = getSimpleName(node.owner); return String.format("%s %s.%s", opcodeName, type, node.name); } else if (currentNode instanceof InvokeDynamicInsnNode) { String opcodeName = getOpcodeName(currentNode); InvokeDynamicInsnNode node = (InvokeDynamicInsnNode) currentNode; Type methodType = Type.getMethodType(node.desc); Type returnType = methodType.getReturnType(); String type = getSimpleName(returnType.getInternalName()); return String.format("%s %s.%s", opcodeName, type, node.name); } else if (currentNode instanceof JumpInsnNode) { String opcodeName = getOpcodeName(currentNode); JumpInsnNode node = (JumpInsnNode) currentNode; String labelName = getLabelName(node.label); return String.format(INSTRUCTION_FORMAT, opcodeName, labelName); } else if (currentNode instanceof LabelNode) { LabelNode node = (LabelNode) currentNode; return getLabelName(node); } else if (currentNode instanceof LdcInsnNode) { String opcodeName = getOpcodeName(currentNode); LdcInsnNode node = (LdcInsnNode) currentNode; Object cst = node.cst; if (cst instanceof Integer) { return String.format("%s %s(int)", opcodeName, cst); } else if (cst instanceof Float) { return String.format("%s %s(float)", opcodeName, cst); } else if (cst instanceof Long) { return String.format("%s %s(long)", opcodeName, cst); } else if (cst instanceof Double) { return String.format("%s %s(double)", opcodeName, cst); } else if (cst instanceof String) { return String.format("%s \"%s\"", opcodeName, cst); } else if (cst instanceof Class) { return String.format("%s %s(class)", opcodeName, cst); } else { return String.format("%s %s", opcodeName, cst); } } else if (currentNode instanceof IincInsnNode) { String opcodeName = getOpcodeName(currentNode); IincInsnNode node = (IincInsnNode) currentNode; return String.format("%s %d %d", opcodeName, node.var, node.incr); } else if (currentNode instanceof MultiANewArrayInsnNode) { String opcodeName = getOpcodeName(currentNode); MultiANewArrayInsnNode node = (MultiANewArrayInsnNode) currentNode; String type = getSimpleName(node.desc); return String.format(INSTRUCTION_FORMAT, opcodeName, type); } else if (currentNode instanceof FrameNode) { return "FrameNode"; } else if (currentNode instanceof LineNumberNode) { return "LineNumberNode"; } else { System.out.println(currentNode.getClass()); } return currentNode.toString(); } public List table_switch_node_to_str_list(TableSwitchInsnNode currentNode) { String opcodeName = getOpcodeName(currentNode); int min = currentNode.min; int max = currentNode.max; List list = new ArrayList<>(); list.add(String.format("%s {", opcodeName)); for (int i = min; i <= max; i++) { LabelNode labelNode = currentNode.labels.get(i - min); String labelName = getLabelName(labelNode); list.add(String.format(" %d: %s", i, labelName)); } list.add(String.format(" default: %s", getLabelName(currentNode.dflt))); list.add("}"); return list; } public List lookup_switch_node_to_str_list(LookupSwitchInsnNode currentNode) { String opcodeName = getOpcodeName(currentNode); List keys = currentNode.keys; int size = keys.size(); List list = new ArrayList<>(); list.add(String.format("%s {", opcodeName)); for (int i = 0; i < size; i++) { int caseValue = keys.get(i); LabelNode labelNode = currentNode.labels.get(i); String labelName = getLabelName(labelNode); list.add(String.format(" %d: %s", caseValue, labelName)); } list.add(String.format(" default: %s", getLabelName(currentNode.dflt))); list.add("}"); return list; } private String getLabelName(LabelNode labelNode) { String labelName = labelNames.get(labelNode); if (labelName == null) { labelName = "L" + labelNames.size(); labelNames.put(labelNode, labelName); } return labelName; } private static String getOpcodeName(AbstractInsnNode currentNode) { int opcode = currentNode.getOpcode(); return OpcodeConst.getOpcodeName(opcode); } private static String getSimpleName(String descriptor) { int squareIndex = descriptor.lastIndexOf("["); String prefix = descriptor.substring(0, squareIndex + 1); String simpleName = descriptor.substring(squareIndex + 1); if (simpleName.startsWith("L") && simpleName.endsWith(";")) { simpleName = simpleName.substring(1, simpleName.length() - 1); } int slashIndex = simpleName.lastIndexOf("/"); simpleName = simpleName.substring(slashIndex + 1); return prefix + simpleName; } } ================================================ FILE: src/main/java/lsieun/asm/analysis/MockAnalyzer.java ================================================ package lsieun.asm.analysis; import org.objectweb.asm.Opcodes; import org.objectweb.asm.Type; import org.objectweb.asm.tree.*; import org.objectweb.asm.tree.analysis.AnalyzerException; import org.objectweb.asm.tree.analysis.Frame; import org.objectweb.asm.tree.analysis.Interpreter; import org.objectweb.asm.tree.analysis.Value; public class MockAnalyzer implements Opcodes { private final Interpreter interpreter; public MockAnalyzer(Interpreter interpreter) { this.interpreter = interpreter; } @SuppressWarnings("unchecked") public Frame[] analyze(String owner, MethodNode method) throws AnalyzerException { // 第一步,如果是abstract或native方法,则直接返回。 if ((method.access & (ACC_ABSTRACT | ACC_NATIVE)) != 0) { return (Frame[]) new Frame[0]; } // 第二步,定义局部变量 // (1)数据输入:获取指令集 InsnList insnList = method.instructions; int size = insnList.size(); // (2)中间状态:记录需要哪一个指令需要处理 boolean[] instructionsToProcess = new boolean[size]; // (3)数据输出:最终的返回结果 Frame[] frames = (Frame[]) new Frame[size]; // 第三步,开始计算 // (1)开始计算:根据方法的参数,计算方法的初始Frame Frame currentFrame = computeInitialFrame(owner, method); merge(frames, 0, currentFrame, instructionsToProcess); // (2)开始计算:根据方法的每一条指令,计算相应的Frame while (getCount(instructionsToProcess) > 0) { // 获取需要处理的指令索引(insnIndex)和旧的Frame(oldFrame) int insnIndex = getFirst(instructionsToProcess); Frame oldFrame = frames[insnIndex]; instructionsToProcess[insnIndex] = false; // 模拟每一条指令的执行 try { AbstractInsnNode insnNode = method.instructions.get(insnIndex); int insnOpcode = insnNode.getOpcode(); int insnType = insnNode.getType(); // 这三者并不是真正的指令,分别表示Label、LineNumberTable和Frame if (insnType == AbstractInsnNode.LABEL || insnType == AbstractInsnNode.LINE || insnType == AbstractInsnNode.FRAME) { merge(frames, insnIndex + 1, oldFrame, instructionsToProcess); } else { // 这里是真正的指令 currentFrame.init(oldFrame).execute(insnNode, interpreter); if (insnNode instanceof JumpInsnNode) { JumpInsnNode jumpInsn = (JumpInsnNode) insnNode; // if之后的语句 if (insnOpcode != GOTO) { merge(frames, insnIndex + 1, currentFrame, instructionsToProcess); } // if和goto跳转之后的位置 int jumpInsnIndex = insnList.indexOf(jumpInsn.label); merge(frames, jumpInsnIndex, currentFrame, instructionsToProcess); } else if (insnNode instanceof LookupSwitchInsnNode) { LookupSwitchInsnNode lookupSwitchInsn = (LookupSwitchInsnNode) insnNode; // lookupswitch的default情况 int targetInsnIndex = insnList.indexOf(lookupSwitchInsn.dflt); merge(frames, targetInsnIndex, currentFrame, instructionsToProcess); // lookupswitch的各种case情况 for (int i = 0; i < lookupSwitchInsn.labels.size(); ++i) { LabelNode label = lookupSwitchInsn.labels.get(i); targetInsnIndex = insnList.indexOf(label); merge(frames, targetInsnIndex, currentFrame, instructionsToProcess); } } else if (insnNode instanceof TableSwitchInsnNode) { TableSwitchInsnNode tableSwitchInsn = (TableSwitchInsnNode) insnNode; // tableswitch的default情况 int targetInsnIndex = insnList.indexOf(tableSwitchInsn.dflt); merge(frames, targetInsnIndex, currentFrame, instructionsToProcess); // tableswitch的各种case情况 for (int i = 0; i < tableSwitchInsn.labels.size(); ++i) { LabelNode label = tableSwitchInsn.labels.get(i); targetInsnIndex = insnList.indexOf(label); merge(frames, targetInsnIndex, currentFrame, instructionsToProcess); } } else if (insnOpcode != ATHROW && (insnOpcode < IRETURN || insnOpcode > RETURN)) { merge(frames, insnIndex + 1, currentFrame, instructionsToProcess); } } } catch (AnalyzerException e) { throw new AnalyzerException(e.node, "Error at instruction " + insnIndex + ": " + e.getMessage(), e); } } return frames; } private int getCount(boolean[] array) { int count = 0; for (boolean flag : array) { if (flag) { count++; } } return count; } private int getFirst(boolean[] array) { int length = array.length; for (int i = 0; i < length; i++) { boolean flag = array[i]; if (flag) { return i; } } return -1; } private Frame computeInitialFrame(final String owner, final MethodNode method) { Frame frame = new Frame<>(method.maxLocals, method.maxStack); int currentLocal = 0; // 第一步,判断是否需要存储this变量 boolean isInstanceMethod = (method.access & ACC_STATIC) == 0; if (isInstanceMethod) { Type ownerType = Type.getObjectType(owner); V value = interpreter.newParameterValue(isInstanceMethod, currentLocal, ownerType); frame.setLocal(currentLocal, value); currentLocal++; } // 第二步,将方法的参数存入到local variable内 Type[] argumentTypes = Type.getArgumentTypes(method.desc); for (Type argumentType : argumentTypes) { V value = interpreter.newParameterValue(isInstanceMethod, currentLocal, argumentType); frame.setLocal(currentLocal, value); currentLocal++; if (argumentType.getSize() == 2) { frame.setLocal(currentLocal, interpreter.newEmptyValue(currentLocal)); currentLocal++; } } // 第三步,将local variable的剩余位置填补上空值 while (currentLocal < method.maxLocals) { frame.setLocal(currentLocal, interpreter.newEmptyValue(currentLocal)); currentLocal++; } // 第四步,设置返回值类型 frame.setReturn(interpreter.newReturnTypeValue(Type.getReturnType(method.desc))); return frame; } /** * Merge old frame with new frame. * * @param frames 所有的frame信息。 * @param insnIndex 当前指令的索引。 * @param newFrame 新的frame * @param instructionsToProcess 记录哪一条指令需要处理 * @throws AnalyzerException 分析错误,抛出此异常 */ private void merge(Frame[] frames, int insnIndex, Frame newFrame, boolean[] instructionsToProcess) throws AnalyzerException { boolean changed; Frame oldFrame = frames[insnIndex]; if (oldFrame == null) { frames[insnIndex] = new Frame<>(newFrame); changed = true; } else { changed = oldFrame.merge(newFrame, interpreter); } if (changed && !instructionsToProcess[insnIndex]) { instructionsToProcess[insnIndex] = true; } } } ================================================ FILE: src/main/java/lsieun/asm/analysis/RemoveDeadCodeNode.java ================================================ package lsieun.asm.analysis; import lsieun.asm.tree.transformer.MethodTransformer; import org.objectweb.asm.ClassVisitor; import org.objectweb.asm.tree.AbstractInsnNode; import org.objectweb.asm.tree.ClassNode; import org.objectweb.asm.tree.LabelNode; import org.objectweb.asm.tree.MethodNode; import org.objectweb.asm.tree.analysis.*; public class RemoveDeadCodeNode extends ClassNode { public RemoveDeadCodeNode(int api, ClassVisitor cv) { super(api); this.cv = cv; } @Override public void visitEnd() { // 首先,处理自己的代码逻辑 MethodTransformer mt = new MethodRemoveDeadCodeTransformer(name, null); for (MethodNode mn : methods) { mt.transform(mn); } // 其次,调用父类的方法实现 super.visitEnd(); // 最后,向后续ClassVisitor传递 if (cv != null) { accept(cv); } } private static class MethodRemoveDeadCodeTransformer extends MethodTransformer { private final String owner; public MethodRemoveDeadCodeTransformer(String owner, MethodTransformer mt) { super(mt); this.owner = owner; } @Override public void transform(MethodNode mn) { // 首先,处理自己的代码逻辑 Analyzer analyzer = new Analyzer<>(new BasicInterpreter()); try { analyzer.analyze(owner, mn); Frame[] frames = analyzer.getFrames(); AbstractInsnNode[] insnNodes = mn.instructions.toArray(); for (int i = 0; i < frames.length; i++) { if (frames[i] == null && !(insnNodes[i] instanceof LabelNode)) { mn.instructions.remove(insnNodes[i]); } } } catch (AnalyzerException ex) { ex.printStackTrace(); } // 其次,调用父类的方法实现 super.transform(mn); } } } ================================================ FILE: src/main/java/lsieun/asm/analysis/RemoveDeadCodeVisitor.java ================================================ package lsieun.asm.analysis; import org.objectweb.asm.ClassVisitor; import org.objectweb.asm.MethodVisitor; import org.objectweb.asm.tree.AbstractInsnNode; import org.objectweb.asm.tree.LabelNode; import org.objectweb.asm.tree.MethodNode; import org.objectweb.asm.tree.analysis.*; public class RemoveDeadCodeVisitor extends ClassVisitor { private String owner; public RemoveDeadCodeVisitor(int api, ClassVisitor cv) { super(api, cv); } @Override public void visit(int version, int access, String name, String signature, String superName, String[] interfaces) { super.visit(version, access, name, signature, superName, interfaces); this.owner = name; } @Override public MethodVisitor visitMethod(int access, String name, String descriptor, String signature, String[] exceptions) { MethodVisitor mv = super.visitMethod(access, name, descriptor, signature, exceptions); if (mv != null) { mv = new MethodRemoveDeadCodeAdapter(api, owner, access, name, descriptor, mv); } return mv; } private static class MethodRemoveDeadCodeAdapter extends MethodVisitor { private final String owner; private final MethodVisitor next; public MethodRemoveDeadCodeAdapter(int api, String owner, int access, String name, String desc, MethodVisitor next) { super(api, new MethodNode(access, name, desc, null, null)); this.owner = owner; this.next = next; } @Override public void visitEnd() { // 首先,处理自己的代码逻辑 MethodNode mn = (MethodNode) mv; Analyzer analyzer = new Analyzer<>(new BasicInterpreter()); try { analyzer.analyze(owner, mn); Frame[] frames = analyzer.getFrames(); AbstractInsnNode[] insnNodes = mn.instructions.toArray(); for (int i = 0; i < frames.length; i++) { if (frames[i] == null && !(insnNodes[i] instanceof LabelNode)) { mn.instructions.remove(insnNodes[i]); } } } catch (AnalyzerException ex) { ex.printStackTrace(); } // 其次,调用父类的方法实现 super.visitEnd(); // 最后,向后续MethodVisitor传递 if (next != null) { mn.accept(next); } } } } ================================================ FILE: src/main/java/lsieun/asm/analysis/RemoveUnusedCastNode.java ================================================ package lsieun.asm.analysis; import lsieun.asm.tree.transformer.MethodTransformer; import org.objectweb.asm.ClassVisitor; import org.objectweb.asm.Type; import org.objectweb.asm.tree.*; import org.objectweb.asm.tree.analysis.*; import static org.objectweb.asm.Opcodes.CHECKCAST; public class RemoveUnusedCastNode extends ClassNode { public RemoveUnusedCastNode(int api, ClassVisitor cv) { super(api); this.cv = cv; } @Override public void visitEnd() { // 首先,处理自己的代码逻辑 MethodTransformer mt = new MethodRemoveUnusedCastTransformer(name, null); for (MethodNode mn : methods) { if ("".equals(mn.name) || "".equals(mn.name)) { continue; } InsnList insns = mn.instructions; if (insns.size() == 0) { continue; } mt.transform(mn); } // 其次,调用父类的方法实现 super.visitEnd(); // 最后,向后续ClassVisitor传递 if (cv != null) { accept(cv); } } private static class MethodRemoveUnusedCastTransformer extends MethodTransformer { private final String owner; public MethodRemoveUnusedCastTransformer(String owner, MethodTransformer mt) { super(mt); this.owner = owner; } @Override public void transform(MethodNode mn) { // 首先,处理自己的代码逻辑 Analyzer analyzer = new Analyzer<>(new SimpleVerifier()); try { Frame[] frames = analyzer.analyze(owner, mn); AbstractInsnNode[] insnNodes = mn.instructions.toArray(); for (int i = 0; i < insnNodes.length; i++) { AbstractInsnNode insn = insnNodes[i]; if (insn.getOpcode() == CHECKCAST) { Frame f = frames[i]; if (f != null && f.getStackSize() > 0) { BasicValue operand = f.getStack(f.getStackSize() - 1); Class to = getClass(((TypeInsnNode) insn).desc); Class from = getClass(operand.getType()); if (to.isAssignableFrom(from)) { mn.instructions.remove(insn); } } } } } catch (AnalyzerException ex) { ex.printStackTrace(); } // 其次,调用父类的方法实现 super.transform(mn); } private static Class getClass(String desc) { try { return Class.forName(desc.replace('/', '.')); } catch (ClassNotFoundException ex) { throw new RuntimeException(ex.toString()); } } private static Class getClass(Type t) { if (t.getSort() == Type.OBJECT) { return getClass(t.getInternalName()); } return getClass(t.getDescriptor()); } } } ================================================ FILE: src/main/java/lsieun/asm/analysis/RemoveUnusedCastVisitor.java ================================================ package lsieun.asm.analysis; import org.objectweb.asm.ClassVisitor; import org.objectweb.asm.MethodVisitor; import org.objectweb.asm.commons.AnalyzerAdapter; import static org.objectweb.asm.Opcodes.*; public class RemoveUnusedCastVisitor extends ClassVisitor { private String owner; public RemoveUnusedCastVisitor(int api, ClassVisitor classVisitor) { super(api, classVisitor); } @Override public void visit(int version, int access, String name, String signature, String superName, String[] interfaces) { super.visit(version, access, name, signature, superName, interfaces); this.owner = name; } @Override public MethodVisitor visitMethod(int access, String name, String descriptor, String signature, String[] exceptions) { MethodVisitor mv = super.visitMethod(access, name, descriptor, signature, exceptions); if (mv != null && !name.equals("")) { boolean isAbstractMethod = (access & ACC_ABSTRACT) != 0; boolean isNativeMethod = (access & ACC_NATIVE) != 0; if (!isAbstractMethod && !isNativeMethod) { RemoveUnusedCastAdapter adapter = new RemoveUnusedCastAdapter(api, mv); adapter.aa = new AnalyzerAdapter(owner, access, name, descriptor, adapter); return adapter.aa; } } return mv; } private static class RemoveUnusedCastAdapter extends MethodVisitor { public AnalyzerAdapter aa; public RemoveUnusedCastAdapter(int api, MethodVisitor methodVisitor) { super(api, methodVisitor); } @Override public void visitTypeInsn(int opcode, String type) { // 首先,处理自己的代码逻辑 if (opcode == CHECKCAST) { Class to = getClass(type); if (aa.stack != null && aa.stack.size() > 0) { Object operand = aa.stack.get(aa.stack.size() - 1); if (operand instanceof String) { Class from = getClass((String) operand); if (to.isAssignableFrom(from)) { return; } } } } // 其次,调用父类的方法实现 super.visitTypeInsn(opcode, type); } private static Class getClass(String desc) { try { return Class.forName(desc.replace('/', '.')); } catch (ClassNotFoundException ex) { throw new RuntimeException(ex.toString()); } } } } ================================================ FILE: src/main/java/lsieun/asm/analysis/cc/CyclomaticComplexity.java ================================================ package lsieun.asm.analysis.cc; import org.objectweb.asm.tree.MethodNode; import org.objectweb.asm.tree.analysis.*; public class CyclomaticComplexity { public static int getCyclomaticComplexity(String owner, MethodNode mn) throws AnalyzerException { // 第一步,获取Frame信息 Analyzer analyzer = new CyclomaticComplexityAnalyzer<>(new BasicInterpreter()); Frame[] frames = analyzer.analyze(owner, mn); // 第二步,计算复杂度 int edges = 0; int nodes = 0; for (Frame frame : frames) { if (frame != null) { edges += ((CyclomaticComplexityFrame) frame).successors.size(); nodes += 1; } } return edges - nodes + 2; } } ================================================ FILE: src/main/java/lsieun/asm/analysis/cc/CyclomaticComplexityAnalyzer.java ================================================ package lsieun.asm.analysis.cc; import org.objectweb.asm.tree.analysis.*; public class CyclomaticComplexityAnalyzer extends Analyzer { public CyclomaticComplexityAnalyzer(Interpreter interpreter) { super(interpreter); } @Override protected Frame newFrame(int numLocals, int numStack) { return new CyclomaticComplexityFrame<>(numLocals, numStack); } @Override protected Frame newFrame(Frame frame) { return new CyclomaticComplexityFrame<>(frame); } @Override protected void newControlFlowEdge(int insnIndex, int successorIndex) { CyclomaticComplexityFrame frame = (CyclomaticComplexityFrame) getFrames()[insnIndex]; frame.successors.add((CyclomaticComplexityFrame) getFrames()[successorIndex]); } } ================================================ FILE: src/main/java/lsieun/asm/analysis/cc/CyclomaticComplexityFrame.java ================================================ package lsieun.asm.analysis.cc; import org.objectweb.asm.tree.analysis.Frame; import org.objectweb.asm.tree.analysis.Value; import java.util.HashSet; import java.util.Set; public class CyclomaticComplexityFrame extends Frame { public Set> successors = new HashSet<>(); public CyclomaticComplexityFrame(int numLocals, int numStack) { super(numLocals, numStack); } public CyclomaticComplexityFrame(Frame frame) { super(frame); } } ================================================ FILE: src/main/java/lsieun/asm/analysis/cfg/ControlFlowBuilder.java ================================================ package lsieun.asm.analysis.cfg; import org.objectweb.asm.tree.MethodNode; import org.objectweb.asm.tree.analysis.AnalyzerException; import static org.objectweb.asm.Opcodes.ACC_ABSTRACT; import static org.objectweb.asm.Opcodes.ACC_NATIVE; public final class ControlFlowBuilder { public static ControlFlowGraph buildCFG(String className, MethodNode methodNode) throws AnalyzerException { if ((methodNode.access & (ACC_ABSTRACT | ACC_NATIVE)) != 0) { throw new RuntimeException("method body is empty"); } int size = methodNode.instructions.size(); SimpleEdgeCreator edgeCreator = new SimpleEdgeCreator(size); FramelessAnalyzer myAnalyzer = new FramelessAnalyzer(edgeCreator); myAnalyzer.analyze(methodNode); int[][] transitions = new int[edgeCreator.transitions.length][]; for (int i = 0; i < transitions.length; i++) { transitions[i] = edgeCreator.transitions[i].toNativeArray(); } int[][] errorTransitions = new int[edgeCreator.errorTransitions.length][]; for (int i = 0; i < errorTransitions.length; i++) { errorTransitions[i] = edgeCreator.errorTransitions[i].toNativeArray(); } return new ControlFlowGraph(transitions, errorTransitions); } } ================================================ FILE: src/main/java/lsieun/asm/analysis/cfg/ControlFlowGraph.java ================================================ package lsieun.asm.analysis.cfg; import java.util.Arrays; import java.util.Formatter; public final class ControlFlowGraph { public final int[][] transitions; public final int[][] errorTransitions; public ControlFlowGraph(int[][] transitions, int[][] errorTransitions) { this.transitions = transitions; this.errorTransitions = errorTransitions; } @Override public String toString() { StringBuilder sb = new StringBuilder(); Formatter fm = new Formatter(sb); fm.format("ControlFlowGraph%n"); fm.format("Normal Transitions%n"); int normalLength = transitions.length; for (int i = 0; i < normalLength; i++) { int[] array = transitions[i]; fm.format("%03d %s%n", i, Arrays.toString(array)); } fm.format("Error Transitions%n"); int errorLength = transitions.length; for (int i = 0; i < errorLength; i++) { int[] array = errorTransitions[i]; fm.format("%03d %s%n", i, Arrays.toString(array)); } return sb.toString(); } } ================================================ FILE: src/main/java/lsieun/asm/analysis/cfg/Edge.java ================================================ package lsieun.asm.analysis.cfg; public final class Edge { public final int from; public final int to; public Edge(int from, int to) { this.from = from; this.to = to; } @Override public boolean equals(Object obj) { if (this == obj) return true; if (!(obj instanceof Edge)) { return false; } Edge edge = (Edge) obj; return (from == edge.from) && (to == edge.to); } @Override public int hashCode() { return 31 * from + to; } } ================================================ FILE: src/main/java/lsieun/asm/analysis/cfg/EdgeCreator.java ================================================ package lsieun.asm.analysis.cfg; public interface EdgeCreator { void newControlFlowEdge(int insn, int successor); void newControlFlowExceptionEdge(int insn, int successor, boolean npe); } ================================================ FILE: src/main/java/lsieun/asm/analysis/cfg/FramelessAnalyzer.java ================================================ package lsieun.asm.analysis.cfg; import org.objectweb.asm.tree.*; import org.objectweb.asm.tree.analysis.AnalyzerException; import java.util.ArrayList; import java.util.HashSet; import java.util.List; import java.util.Set; import static org.objectweb.asm.Opcodes.*; /** * Specialized version of {@link org.objectweb.asm.tree.analysis.Analyzer}. * Calculation of fix-point of frames is removed, since frames are not needed to build control flow graph. * So, the main point here is handling of try-catch-finally blocks. */ public class FramelessAnalyzer { private static final Set NPE_HANDLERS = new HashSet<>(); static { NPE_HANDLERS.add("java/lang/Throwable"); NPE_HANDLERS.add("java/lang/Exception"); NPE_HANDLERS.add("java/lang/RuntimeException"); NPE_HANDLERS.add("java/lang/NullPointerException"); } protected boolean[] wasQueued; protected boolean[] queued; protected int[] queue; protected int top; protected final EdgeCreator myEdgeCreator; public FramelessAnalyzer(EdgeCreator creator) { myEdgeCreator = creator; } public void analyze(MethodNode m) throws AnalyzerException { int n = m.instructions.size(); if ((m.access & (ACC_ABSTRACT | ACC_NATIVE)) != 0 || n == 0) { return; } InsnList insns = m.instructions; List[] handlers = newListArray(n); queued = new boolean[n]; wasQueued = new boolean[n]; queue = new int[n]; top = 0; // computes exception handlers for each instruction for (TryCatchBlockNode tcb : m.tryCatchBlocks) { int begin = insns.indexOf(tcb.start); int end = insns.indexOf(tcb.end); for (int j = begin; j < end; ++j) { List insnHandlers = handlers[j]; if (insnHandlers == null) { insnHandlers = new ArrayList<>(); handlers[j] = insnHandlers; } insnHandlers.add(tcb); } } merge(0); // control flow analysis while (top > 0) { int insn = queue[--top]; queued[insn] = false; AbstractInsnNode insnNode = null; try { insnNode = m.instructions.get(insn); int insnOpcode = insnNode.getOpcode(); int insnType = insnNode.getType(); if (insnType == AbstractInsnNode.LABEL || insnType == AbstractInsnNode.LINE || insnType == AbstractInsnNode.FRAME) { merge(insn + 1); myEdgeCreator.newControlFlowEdge(insn, insn + 1); } else { if (insnNode instanceof JumpInsnNode) { JumpInsnNode j = (JumpInsnNode) insnNode; if (insnOpcode != GOTO && insnOpcode != JSR) { merge(insn + 1); myEdgeCreator.newControlFlowEdge(insn, insn + 1); } int jump = insns.indexOf(j.label); if (insnOpcode == JSR) { throw new AnalyzerException(insnNode, "ret is not supported"); } else { merge(jump); } myEdgeCreator.newControlFlowEdge(insn, jump); } else if (insnNode instanceof LookupSwitchInsnNode) { LookupSwitchInsnNode lsi = (LookupSwitchInsnNode) insnNode; int jump = insns.indexOf(lsi.dflt); merge(jump); myEdgeCreator.newControlFlowEdge(insn, jump); for (int j = 0; j < lsi.labels.size(); ++j) { LabelNode label = lsi.labels.get(j); jump = insns.indexOf(label); merge(jump); myEdgeCreator.newControlFlowEdge(insn, jump); } } else if (insnNode instanceof TableSwitchInsnNode) { TableSwitchInsnNode tsi = (TableSwitchInsnNode) insnNode; int jump = insns.indexOf(tsi.dflt); merge(jump); myEdgeCreator.newControlFlowEdge(insn, jump); for (int j = 0; j < tsi.labels.size(); ++j) { LabelNode label = tsi.labels.get(j); jump = insns.indexOf(label); merge(jump); myEdgeCreator.newControlFlowEdge(insn, jump); } } else if (insnOpcode == RET) { throw new AnalyzerException(insnNode, "ret is not supported"); } else if (insnOpcode != ATHROW && (insnOpcode < IRETURN || insnOpcode > RETURN)) { merge(insn + 1); myEdgeCreator.newControlFlowEdge(insn, insn + 1); } } List insnHandlers = handlers[insn]; if (insnHandlers != null) { for (TryCatchBlockNode tcb : insnHandlers) { myEdgeCreator.newControlFlowExceptionEdge(insn, insns.indexOf(tcb.handler), NPE_HANDLERS.contains(tcb.type)); merge(insns.indexOf(tcb.handler)); } } } catch (AnalyzerException e) { throw new AnalyzerException(e.node, "Error at instruction " + insn + ": " + e.getMessage(), e); } catch (Exception e) { throw new AnalyzerException(insnNode, "Error at instruction " + insn + ": " + e.getMessage(), e); } } } // ------------------------------------------------------------------------- protected void merge(int insn) { boolean changes = false; if (!wasQueued[insn]) { wasQueued[insn] = true; changes = true; } if (changes && !queued[insn]) { queued[insn] = true; queue[top++] = insn; } } public static List[] newListArray(int size) { @SuppressWarnings("unchecked") List[] a = (List[]) new List[size]; return a; } } ================================================ FILE: src/main/java/lsieun/asm/analysis/cfg/SimpleEdgeCreator.java ================================================ package lsieun.asm.analysis.cfg; import lsieun.trove.TIntArrayList; public class SimpleEdgeCreator implements EdgeCreator { public final TIntArrayList[] transitions; public final TIntArrayList[] errorTransitions; public SimpleEdgeCreator(int size) { this.transitions = new TIntArrayList[size]; this.errorTransitions = new TIntArrayList[size]; for (int i = 0; i < transitions.length; i++) { this.transitions[i] = new TIntArrayList(); this.errorTransitions[i] = new TIntArrayList(); } } @Override public void newControlFlowEdge(int insn, int successor) { if (!transitions[insn].contains(successor)) { transitions[insn].add(successor); } } @Override public void newControlFlowExceptionEdge(int insn, int successor, boolean npe) { if (!errorTransitions[insn].contains(successor)) { errorTransitions[insn].add(successor); } } } ================================================ FILE: src/main/java/lsieun/asm/analysis/diagnosis/DeadCodeDiagnosis.java ================================================ package lsieun.asm.analysis.diagnosis; import lsieun.asm.analysis.ControlFlowAnalyzer; import lsieun.trove.TIntArrayList; import org.objectweb.asm.tree.InsnList; import org.objectweb.asm.tree.MethodNode; import org.objectweb.asm.tree.analysis.AnalyzerException; public class DeadCodeDiagnosis { public static int[] diagnose(String className, MethodNode mn) throws AnalyzerException { InsnList instructions = mn.instructions; int size = instructions.size(); boolean[] flags = new boolean[size]; ControlFlowAnalyzer analyzer = new ControlFlowAnalyzer() { @Override protected void newControlFlowEdge(int insnIndex, int successorIndex) { // 首先,处理自己的代码逻辑 flags[insnIndex] = true; flags[successorIndex] = true; // 其次,调用父类的实现 super.newControlFlowEdge(insnIndex, successorIndex); } }; analyzer.analyze(className, mn); TIntArrayList intArrayList = new TIntArrayList(); for (int i = 0; i < size; i++) { boolean flag = flags[i]; if (!flag) { intArrayList.add(i); } } return intArrayList.toNativeArray(); } } ================================================ FILE: src/main/java/lsieun/asm/analysis/diagnosis/NullDereferenceDiagnosis.java ================================================ package lsieun.asm.analysis.diagnosis; import lsieun.asm.analysis.nullability.NullDeferenceInterpreter; import lsieun.trove.TIntArrayList; import org.objectweb.asm.Type; import org.objectweb.asm.tree.AbstractInsnNode; import org.objectweb.asm.tree.InsnList; import org.objectweb.asm.tree.MethodInsnNode; import org.objectweb.asm.tree.MethodNode; import org.objectweb.asm.tree.analysis.*; import java.util.Arrays; import static org.objectweb.asm.Opcodes.*; public class NullDereferenceDiagnosis { public static int[] diagnose(String owner, MethodNode mn) throws AnalyzerException { // 第一步,获取Frame信息 Analyzer analyzer = new Analyzer<>(new NullDeferenceInterpreter(ASM9)); Frame[] frames = analyzer.analyze(owner, mn); // 第二步,判断是否为null或maybe-null,收集数据 TIntArrayList intArrayList = new TIntArrayList(); InsnList instructions = mn.instructions; int size = instructions.size(); for (int i = 0; i < size; i++) { AbstractInsnNode insn = instructions.get(i); if (frames[i] != null) { Value value = getTarget(insn, frames[i]); if (value == NullDeferenceInterpreter.NULL_VALUE || value == NullDeferenceInterpreter.MAYBE_NULL_VALUE) { intArrayList.add(i); } } } // 第三步,将结果转换成int[]形式 int[] array = intArrayList.toNativeArray(); Arrays.sort(array); return array; } private static BasicValue getTarget(AbstractInsnNode insn, Frame frame) { int opcode = insn.getOpcode(); switch (opcode) { case GETFIELD: case ARRAYLENGTH: case MONITORENTER: case MONITOREXIT: return getStackValue(frame, 0); case PUTFIELD: return getStackValue(frame, 1); case INVOKEVIRTUAL: case INVOKESPECIAL: case INVOKEINTERFACE: String desc = ((MethodInsnNode) insn).desc; return getStackValue(frame, Type.getArgumentTypes(desc).length); } return null; } private static BasicValue getStackValue(Frame frame, int index) { int top = frame.getStackSize() - 1; return index <= top ? frame.getStack(top - index) : null; } } ================================================ FILE: src/main/java/lsieun/asm/analysis/diagnosis/NullabilityDiagnosis.java ================================================ package lsieun.asm.analysis.diagnosis; import lsieun.asm.analysis.nullability.Nullability; import lsieun.asm.analysis.nullability.NullabilityAnalyzer; import lsieun.asm.analysis.nullability.NullabilityInterpreter; import lsieun.asm.analysis.nullability.NullabilityValue; import lsieun.trove.TIntArrayList; import org.objectweb.asm.Opcodes; import org.objectweb.asm.Type; import org.objectweb.asm.tree.AbstractInsnNode; import org.objectweb.asm.tree.InsnList; import org.objectweb.asm.tree.MethodInsnNode; import org.objectweb.asm.tree.MethodNode; import org.objectweb.asm.tree.analysis.Analyzer; import org.objectweb.asm.tree.analysis.AnalyzerException; import org.objectweb.asm.tree.analysis.Frame; import java.util.Arrays; import static org.objectweb.asm.Opcodes.*; public class NullabilityDiagnosis { public static int[] diagnose(String className, MethodNode mn) throws AnalyzerException { // 第一步,获取Frame信息 Analyzer analyzer = new NullabilityAnalyzer(new NullabilityInterpreter(Opcodes.ASM9)); Frame[] frames = analyzer.analyze(className, mn); // 第二步,判断是否为null或maybe-null,收集数据 TIntArrayList intArrayList = new TIntArrayList(); InsnList instructions = mn.instructions; int size = instructions.size(); for (int i = 0; i < size; i++) { AbstractInsnNode insn = instructions.get(i); if (frames[i] != null) { NullabilityValue value = getTarget(insn, frames[i]); if (value == null) continue; if (value.getState() == Nullability.NULL || value.getState() == Nullability.NULLABLE) { intArrayList.add(i); } } } // 第三步,将结果转换成int[]形式 int[] array = intArrayList.toNativeArray(); Arrays.sort(array); return array; } private static NullabilityValue getTarget(AbstractInsnNode insn, Frame frame) { int opcode = insn.getOpcode(); switch (opcode) { case GETFIELD: case ARRAYLENGTH: case MONITORENTER: case MONITOREXIT: return getStackValue(frame, 0); case PUTFIELD: return getStackValue(frame, 1); case INVOKEVIRTUAL: case INVOKESPECIAL: case INVOKEINTERFACE: String desc = ((MethodInsnNode) insn).desc; return getStackValue(frame, Type.getArgumentTypes(desc).length); } return null; } private static NullabilityValue getStackValue(Frame frame, int index) { int top = frame.getStackSize() - 1; return index <= top ? frame.getStack(top - index) : null; } } ================================================ FILE: src/main/java/lsieun/asm/analysis/diagnosis/RedundantVariableDiagnosis.java ================================================ package lsieun.asm.analysis.diagnosis; import lsieun.trove.TIntArrayList; import org.objectweb.asm.Opcodes; import org.objectweb.asm.tree.AbstractInsnNode; import org.objectweb.asm.tree.InsnList; import org.objectweb.asm.tree.MethodNode; import org.objectweb.asm.tree.VarInsnNode; import org.objectweb.asm.tree.analysis.*; import java.util.Arrays; public class RedundantVariableDiagnosis { public static int[] diagnose(String className, MethodNode mn) throws AnalyzerException { // 第一步,准备工作。使用SimpleVerifier进行分析,得到frames信息 Analyzer analyzer = new Analyzer<>(new SimpleVerifier()); Frame[] frames = analyzer.analyze(className, mn); // 第二步,利用frames信息,查看local variable当中哪些slot数据出现了冗余 TIntArrayList localIndexList = new TIntArrayList(); for (Frame f : frames) { int locals = f.getLocals(); for (int i = 0; i < locals; i++) { BasicValue val1 = f.getLocal(i); if (val1 == BasicValue.UNINITIALIZED_VALUE) { continue; } for (int j = i + 1; j < locals; j++) { BasicValue val2 = f.getLocal(j); if (val2 == BasicValue.UNINITIALIZED_VALUE) { continue; } if (val1 == val2) { if (!localIndexList.contains(j)) { localIndexList.add(j); } } } } } // 第三步,将slot的索引值(local index)转换成instruction的索引值(insn index) TIntArrayList insnIndexList = new TIntArrayList(); InsnList instructions = mn.instructions; int size = instructions.size(); for (int i = 0; i < size; i++) { AbstractInsnNode node = instructions.get(i); int opcode = node.getOpcode(); if (opcode >= Opcodes.ISTORE && opcode <= Opcodes.ASTORE) { VarInsnNode varInsnNode = (VarInsnNode) node; if (localIndexList.contains(varInsnNode.var)) { if (!insnIndexList.contains(i)) { insnIndexList.add(i); } } } } // 第四步,将insnIndexList转换成int[]形式 int[] array = insnIndexList.toNativeArray(); Arrays.sort(array); return array; } } ================================================ FILE: src/main/java/lsieun/asm/analysis/diagnosis/RelatedInstructionDiagnosis.java ================================================ package lsieun.asm.analysis.diagnosis; import lsieun.asm.analysis.transition.DestinationInterpreter; import lsieun.trove.TIntArrayList; import org.objectweb.asm.Opcodes; import org.objectweb.asm.tree.AbstractInsnNode; import org.objectweb.asm.tree.InsnList; import org.objectweb.asm.tree.MethodNode; import org.objectweb.asm.tree.VarInsnNode; import org.objectweb.asm.tree.analysis.*; import java.util.ArrayList; import java.util.Arrays; import java.util.List; public class RelatedInstructionDiagnosis { public static int[] diagnose(String className, MethodNode mn, int insnIndex) throws AnalyzerException { // 第一步,判断insnIndex范围是否合理 InsnList instructions = mn.instructions; int size = instructions.size(); if (insnIndex < 0 || insnIndex >= size) { String message = String.format("the 'insnIndex' argument should in range [0, %d]", size - 1); throw new IllegalArgumentException(message); } // 第二步,获取两个Frame Frame[] sourceFrames = getSourceFrames(className, mn); Frame[] destinationFrames = getDestinationFrames(className, mn); // 第三步,循环处理,所有结果记录到这个intArrayList变量中 TIntArrayList intArrayList = new TIntArrayList(); // 循环tmpInsnList List tmpInsnList = new ArrayList<>(); AbstractInsnNode insnNode = instructions.get(insnIndex); tmpInsnList.add(insnNode); for (int i = 0; i < tmpInsnList.size(); i++) { AbstractInsnNode currentNode = tmpInsnList.get(i); int opcode = currentNode.getOpcode(); int index = instructions.indexOf(currentNode); intArrayList.add(index); // 第一种情况,处理load相关的opcode情况 Frame srcFrame = sourceFrames[index]; if (opcode >= Opcodes.ILOAD && opcode <= Opcodes.ALOAD) { VarInsnNode varInsnNode = (VarInsnNode) currentNode; int localIndex = varInsnNode.var; SourceValue value = srcFrame.getLocal(localIndex); for (AbstractInsnNode insn : value.insns) { if (!tmpInsnList.contains(insn)) { tmpInsnList.add(insn); } } } // 第二种情况,从dstFrame到srcFrame查找 Frame dstFrame = destinationFrames[index]; int stackSize = dstFrame.getStackSize(); for (int j = 0; j < stackSize; j++) { SourceValue value = dstFrame.getStack(j); if (value.insns.contains(currentNode)) { for (AbstractInsnNode insn : srcFrame.getStack(j).insns) { if (!tmpInsnList.contains(insn)) { tmpInsnList.add(insn); } } } } } // 第四步,将intArrayList变量转换成int[],并进行排序 int[] array = intArrayList.toNativeArray(); Arrays.sort(array); return array; } private static Frame[] getSourceFrames(String className, MethodNode mn) throws AnalyzerException { Analyzer analyzer = new Analyzer<>(new SourceInterpreter()); return analyzer.analyze(className, mn); } private static Frame[] getDestinationFrames(String className, MethodNode mn) throws AnalyzerException { Analyzer analyzer = new Analyzer<>(new DestinationInterpreter()); return analyzer.analyze(className, mn); } } ================================================ FILE: src/main/java/lsieun/asm/analysis/diagnosis/ReverseEngineerMethodArgumentsDiagnosis.java ================================================ package lsieun.asm.analysis.diagnosis; import org.objectweb.asm.Type; import org.objectweb.asm.tree.*; import org.objectweb.asm.tree.analysis.*; import java.util.ArrayList; import java.util.List; public class ReverseEngineerMethodArgumentsDiagnosis { private static final String UNKNOWN_VARIABLE_NAME = "unknown"; public static void diagnose(String className, MethodNode mn) throws AnalyzerException { // 第一步,获取Frame信息 Analyzer analyzer = new Analyzer<>(new SourceInterpreter()); Frame[] frames = analyzer.analyze(className, mn); // 第二步,获取LocalVariableTable信息 List localVariables = mn.localVariables; if (localVariables == null || localVariables.size() < 1) { System.out.println("LocalVariableTable is Empty"); return; } // 第三步,获取instructions,并找到与invoke相关的指令 InsnList instructions = mn.instructions; int[] methodInsnArray = findMethodInvokes(instructions); // 第四步,对invoke相关的指令进行反编译 for (int methodInsn : methodInsnArray) { // (1) 获取方法的参数 MethodInsnNode methodInsnNode = (MethodInsnNode) instructions.get(methodInsn); Type methodType = Type.getMethodType(methodInsnNode.desc); Type[] argumentTypes = methodType.getArgumentTypes(); int argNum = argumentTypes.length; // (2) 从Frame当中获取指令,并将指令转换LocalVariableTable当中的变量名 Frame f = frames[methodInsn]; int stackSize = f.getStackSize(); List argList = new ArrayList<>(); for (int i = 0; i < argNum; i++) { int stackIndex = stackSize - argNum + i; SourceValue stackValue = f.getStack(stackIndex); AbstractInsnNode insn = stackValue.insns.iterator().next(); String argName = getMethodVariableName(insn, localVariables); argList.add(argName); } // (3) 将反编译的结果打印出来 String line = String.format("%s.%s(%s)", methodInsnNode.owner, methodInsnNode.name, argList); System.out.println(line); } } public static String getMethodVariableName(AbstractInsnNode insn, List localVariables) { if (insn instanceof VarInsnNode) { VarInsnNode varInsnNode = (VarInsnNode) insn; int localIndex = varInsnNode.var; for (LocalVariableNode node : localVariables) { if (node.index == localIndex) { return node.name; } } return String.format("locals[%d]", localIndex); } return UNKNOWN_VARIABLE_NAME; } public static int[] findMethodInvokes(InsnList instructions) { int size = instructions.size(); boolean[] methodArray = new boolean[size]; for (int i = 0; i < size; i++) { AbstractInsnNode node = instructions.get(i); if (node instanceof MethodInsnNode) { methodArray[i] = true; } } int count = 0; for (boolean flag : methodArray) { if (flag) { count++; } } int[] array = new int[count]; int j = 0; for (int i = 0; i < size; i++) { boolean flag = methodArray[i]; if (flag) { array[j] = i; j++; } } return array; } } ================================================ FILE: src/main/java/lsieun/asm/analysis/graph/InsnBlock.java ================================================ package lsieun.asm.analysis.graph; import java.util.ArrayList; import java.util.List; public class InsnBlock { // 文字信息 public final List lines = new ArrayList<>(); // 关联关系 public final List nextBlockList = new ArrayList<>(); public final List jumpBlockList = new ArrayList<>(); public void addLines(List list) { lines.addAll(list); } public void addNext(InsnBlock item) { if (!nextBlockList.contains(item)) { nextBlockList.add(item); } } public void addJump(InsnBlock item) { if (!jumpBlockList.contains(item)) { jumpBlockList.add(item); } } } ================================================ FILE: src/main/java/lsieun/asm/analysis/graph/TextBox.java ================================================ package lsieun.asm.analysis.graph; public class TextBox { public final int row; public final int col; public final int width; public final int height; public TextBox(int row, int col, int width, int height) { this.row = row; this.col = col; this.width = width; this.height = height; } public static TextBox valueOf(int row, int col, int width, int height) { return new TextBox(row, col, width, height); } } ================================================ FILE: src/main/java/lsieun/asm/analysis/graph/TextGraph.java ================================================ package lsieun.asm.analysis.graph; import lsieun.drawing.canvas.Box; import lsieun.drawing.canvas.Canvas; import lsieun.drawing.canvas.TextDirection; import lsieun.drawing.theme.line.ContinuousLine; import lsieun.drawing.theme.text.PlainText; import java.util.ArrayList; import java.util.List; public class TextGraph { private static final int ROW_SPACE = 1; private static final int COL_SPACE = 3; private final InsnBlock[] blockArray; private final TextBox[] boxArray; private final int boxNum; private final int maxInstructionLength; private final Canvas canvas = new Canvas(); public TextGraph(InsnBlock[] blockArray) { this.blockArray = blockArray; this.boxNum = blockArray.length; this.boxArray = new TextBox[boxNum]; this.maxInstructionLength = findMaxStringLength(blockArray); } @SuppressWarnings("UnnecessaryLocalVariable") public void draw(int startRow, int startCol) { int row = startRow; int col = startCol; int length = boxArray.length; for (int i = 0; i < length; i++) { InsnBlock block = blockArray[i]; int width = getOdd(maxInstructionLength + 2); int height = block.lines.size(); TextBox box = TextBox.valueOf(row, col, width, height); boxArray[i] = box; drawBoxAndText(box, block.lines); row += height + ROW_SPACE + 2; } drawLinks(); System.out.println(canvas); } private void drawBoxAndText(TextBox box, List lines) { int row = box.row; int col = box.col; int width = box.width; int height = box.height; canvas.moveTo(row, col); canvas.drawRectangle(width, height); canvas.draw(row + 1, col + 2, PlainText.valueOf(lines)); } private void drawLinks() { for (int i = 0; i < boxNum; i++) { InsnBlock currentBlock = blockArray[i]; TextBox currentBox = boxArray[i]; // draw next List nextBoxes = findBoxes(currentBlock.nextBlockList); for (TextBox nextBox : nextBoxes) { int rowStart = currentBox.row + currentBox.height + 1; int rowStop = nextBox.row; int col = currentBox.col + currentBox.width / 2; canvas.moveTo(rowStart, col); canvas.drawPixel(Box.DOWN_AND_HORIZONTAL); canvas.moveTo(rowStop, col); canvas.drawPixel(Box.UP_AND_HORIZONTAL); canvas.moveTo(rowStart + 1, col).drawVerticalLine(rowStop - rowStart - 1); } // draw jump List jumpBoxes = findBoxes(currentBlock.jumpBlockList); for (TextBox nextBox : jumpBoxes) { int rowStart = currentBox.row + currentBox.height; int rowStop = nextBox.row + 1; int colStart = currentBox.col + currentBox.width + 1; int colStop = currentBox.col + currentBox.width + 1 + (i + 1) * COL_SPACE; canvas.moveTo(rowStart, colStart); canvas.drawPixel(Box.VERTICAL_AND_RIGHT); canvas.moveTo(rowStop, colStart); canvas.drawPixel(Box.VERTICAL_AND_RIGHT); if (rowStart < rowStop) { ContinuousLine line = new ContinuousLine(); line.setDirection(TextDirection.RIGHT); line.drawLine(colStop - colStart) .turn(TextDirection.DOWN).drawLine(rowStop - rowStart - 1) .turn(TextDirection.LEFT).drawLine(colStop - colStart); canvas.draw(rowStart, colStart + 1, line); } else { ContinuousLine line = new ContinuousLine(); line.setDirection(TextDirection.RIGHT); line.drawLine(colStop - colStart) .turn(TextDirection.UP).drawLine(rowStart - rowStop - 1) .turn(TextDirection.LEFT).drawLine(colStop - colStart); canvas.draw(rowStart, colStart + 1, line); } } } } private List findBoxes(List blockList) { List boxList = new ArrayList<>(); for (int i = 0; i < boxNum; i++) { InsnBlock block = blockArray[i]; if (blockList.contains(block)) { boxList.add(boxArray[i]); } } return boxList; } private int getOdd(int num) { int remainder = num % 2; if (remainder == 0) { return num + 1; } return num; } private int findMaxStringLength(InsnBlock[] blockArray) { int maxLength = 0; for (InsnBlock block : blockArray) { int length = findMaxStringLength(block.lines); if (length > maxLength) { maxLength = length; } } return maxLength; } private int findMaxStringLength(List lines) { int maxLength = 0; for (String item : lines) { if (item == null) continue; int length = item.length(); if (length > maxLength) { maxLength = length; } } return maxLength; } } ================================================ FILE: src/main/java/lsieun/asm/analysis/nullability/NullDeferenceInterpreter.java ================================================ package lsieun.asm.analysis.nullability; import org.objectweb.asm.Type; import org.objectweb.asm.tree.AbstractInsnNode; import org.objectweb.asm.tree.analysis.AnalyzerException; import org.objectweb.asm.tree.analysis.BasicInterpreter; import org.objectweb.asm.tree.analysis.BasicValue; import org.objectweb.asm.tree.analysis.Value; public class NullDeferenceInterpreter extends BasicInterpreter { public final static BasicValue NULL_VALUE = new BasicValue(NULL_TYPE); public final static BasicValue MAYBE_NULL_VALUE = new BasicValue(Type.getObjectType("may-be-null")); public NullDeferenceInterpreter(int api) { super(api); } @Override public BasicValue newOperation(AbstractInsnNode insn) throws AnalyzerException { if (insn.getOpcode() == ACONST_NULL) { return NULL_VALUE; } return super.newOperation(insn); } @Override public BasicValue merge(BasicValue value1, BasicValue value2) { if (isRef(value1) && isRef(value2) && value1 != value2) { return MAYBE_NULL_VALUE; } return super.merge(value1, value2); } private boolean isRef(Value value) { return value == BasicValue.REFERENCE_VALUE || value == NULL_VALUE || value == MAYBE_NULL_VALUE; } } ================================================ FILE: src/main/java/lsieun/asm/analysis/nullability/Nullability.java ================================================ package lsieun.asm.analysis.nullability; public enum Nullability { UNKNOWN(0), NOT_NULL(1), NULL(1), NULLABLE(2); public final int priority; Nullability(int priority) { this.priority = priority; } public static Nullability merge(Nullability value1, Nullability value2) { // 第一种情况,两者相等,则直接返回一个 if (value1 == value2) { return value1; } // 第二种情况,两者不相等,比较优先级大小,谁大返回谁 int priority1 = value1.priority; int priority2 = value2.priority; if (priority1 > priority2) { return value1; } else if (priority1 < priority2) { return value2; } // 第三种情况,两者不相等,但优先级相等,则一个是NOT_NULL,另一个是NULL return NULLABLE; } } ================================================ FILE: src/main/java/lsieun/asm/analysis/nullability/NullabilityAnalyzer.java ================================================ package lsieun.asm.analysis.nullability; import org.objectweb.asm.tree.analysis.Analyzer; import org.objectweb.asm.tree.analysis.Frame; import org.objectweb.asm.tree.analysis.Interpreter; public class NullabilityAnalyzer extends Analyzer { public NullabilityAnalyzer(Interpreter interpreter) { super(interpreter); } @Override protected Frame newFrame(Frame frame) { return new NullabilityFrame((NullabilityFrame) frame); } @Override protected Frame newFrame(int numLocals, int numStack) { return new NullabilityFrame(numLocals, numStack); } } ================================================ FILE: src/main/java/lsieun/asm/analysis/nullability/NullabilityFrame.java ================================================ package lsieun.asm.analysis.nullability; import org.objectweb.asm.Opcodes; import org.objectweb.asm.tree.LabelNode; import org.objectweb.asm.tree.analysis.Frame; public class NullabilityFrame extends Frame { public NullabilityFrame(int numLocals, int numStack) { super(numLocals, numStack); } public NullabilityFrame(NullabilityFrame frame) { super(frame); } @Override public void initJumpTarget(int opcode, LabelNode target) { // 首先,处理自己的代码逻辑 int stackIndex = getStackSize(); NullabilityValue oldValue = getStack(stackIndex); switch (opcode) { case Opcodes.IFNULL: { if (target == null) { updateFrame(oldValue, Nullability.NOT_NULL); } else { updateFrame(oldValue, Nullability.NULL); } break; } case Opcodes.IFNONNULL: { if (target == null) { updateFrame(oldValue, Nullability.NULL); } else { updateFrame(oldValue, Nullability.NOT_NULL); } break; } } // 其次,调用父类的方法实现 super.initJumpTarget(opcode, target); } private void updateFrame(NullabilityValue oldValue, Nullability newState) { NullabilityValue newValue = new NullabilityValue(oldValue.getType(), newState); int numLocals = getLocals(); for (int i = 0; i < numLocals; i++) { NullabilityValue currentValue = getLocal(i); if (oldValue == currentValue) { setLocal(i, newValue); } } int numStack = getMaxStackSize(); for (int i = 0; i < numStack; i++) { NullabilityValue currentValue = getStack(i); if (oldValue == currentValue) { setStack(i, newValue); } } } } ================================================ FILE: src/main/java/lsieun/asm/analysis/nullability/NullabilityInterpreter.java ================================================ package lsieun.asm.analysis.nullability; import org.objectweb.asm.ConstantDynamic; import org.objectweb.asm.Handle; import org.objectweb.asm.Opcodes; import org.objectweb.asm.Type; import org.objectweb.asm.tree.*; import org.objectweb.asm.tree.analysis.AnalyzerException; import org.objectweb.asm.tree.analysis.Interpreter; import java.util.List; public class NullabilityInterpreter extends Interpreter implements Opcodes { public static final Type NULL_TYPE = Type.getObjectType("null"); public static final NullabilityValue UNINITIALIZED_VALUE = new NullabilityValue(null); public static final NullabilityValue RETURN_ADDRESS_VALUE = new NullabilityValue(Type.VOID_TYPE); private final ClassLoader loader = getClass().getClassLoader(); public NullabilityInterpreter(int api) { super(api); } @Override public NullabilityValue newValue(Type type) { if (type == null) { return UNINITIALIZED_VALUE; } int sort = type.getSort(); if (sort == Type.VOID) { return null; } return new NullabilityValue(type); } @Override public NullabilityValue newOperation(AbstractInsnNode insn) throws AnalyzerException { switch (insn.getOpcode()) { case ACONST_NULL: return new NullabilityValue(NULL_TYPE, Nullability.NULL); case ICONST_M1: case ICONST_0: case ICONST_1: case ICONST_2: case ICONST_3: case ICONST_4: case ICONST_5: case BIPUSH: case SIPUSH: return newValue(Type.INT_TYPE); case LCONST_0: case LCONST_1: return newValue(Type.LONG_TYPE); case FCONST_0: case FCONST_1: case FCONST_2: return newValue(Type.FLOAT_TYPE); case DCONST_0: case DCONST_1: return newValue(Type.DOUBLE_TYPE); case LDC: Object value = ((LdcInsnNode) insn).cst; if (value instanceof Integer) { return newValue(Type.INT_TYPE); } else if (value instanceof Float) { return newValue(Type.FLOAT_TYPE); } else if (value instanceof Long) { return newValue(Type.LONG_TYPE); } else if (value instanceof Double) { return newValue(Type.DOUBLE_TYPE); } else if (value instanceof String) { return new NullabilityValue(Type.getObjectType("java/lang/String"), Nullability.NOT_NULL); } else if (value instanceof Type) { int sort = ((Type) value).getSort(); if (sort == Type.OBJECT || sort == Type.ARRAY) { return newValue(Type.getObjectType("java/lang/Class")); } else if (sort == Type.METHOD) { return newValue(Type.getObjectType("java/lang/invoke/MethodType")); } else { throw new AnalyzerException(insn, "Illegal LDC value " + value); } } else if (value instanceof Handle) { return newValue(Type.getObjectType("java/lang/invoke/MethodHandle")); } else if (value instanceof ConstantDynamic) { return newValue(Type.getType(((ConstantDynamic) value).getDescriptor())); } else { throw new AnalyzerException(insn, "Illegal LDC value " + value); } case JSR: return RETURN_ADDRESS_VALUE; case GETSTATIC: return newValue(Type.getType(((FieldInsnNode) insn).desc)); case NEW: return newValue(Type.getObjectType(((TypeInsnNode) insn).desc)); default: throw new AssertionError(); } } @Override public NullabilityValue copyOperation(AbstractInsnNode insn, NullabilityValue value) { return value; } @Override public NullabilityValue unaryOperation(AbstractInsnNode insn, NullabilityValue value) throws AnalyzerException { switch (insn.getOpcode()) { case INEG: case IINC: case L2I: case F2I: case D2I: case I2B: case I2C: case I2S: case ARRAYLENGTH: case INSTANCEOF: return newValue(Type.INT_TYPE); case FNEG: case I2F: case L2F: case D2F: return newValue(Type.FLOAT_TYPE); case LNEG: case I2L: case F2L: case D2L: return newValue(Type.LONG_TYPE); case DNEG: case I2D: case L2D: case F2D: return newValue(Type.DOUBLE_TYPE); case GETFIELD: return newValue(Type.getType(((FieldInsnNode) insn).desc)); case NEWARRAY: switch (((IntInsnNode) insn).operand) { case T_BOOLEAN: return newValue(Type.getType("[Z")); case T_CHAR: return newValue(Type.getType("[C")); case T_BYTE: return newValue(Type.getType("[B")); case T_SHORT: return newValue(Type.getType("[S")); case T_INT: return newValue(Type.getType("[I")); case T_FLOAT: return newValue(Type.getType("[F")); case T_DOUBLE: return newValue(Type.getType("[D")); case T_LONG: return newValue(Type.getType("[J")); default: break; } throw new AnalyzerException(insn, "Invalid array type"); case ANEWARRAY: return newValue(Type.getType("[" + Type.getObjectType(((TypeInsnNode) insn).desc))); case CHECKCAST: return value; case IFEQ: case IFNE: case IFLT: case IFGE: case IFGT: case IFLE: case TABLESWITCH: case LOOKUPSWITCH: case IRETURN: case LRETURN: case FRETURN: case DRETURN: case ARETURN: case PUTSTATIC: case ATHROW: case MONITORENTER: case MONITOREXIT: case IFNULL: case IFNONNULL: return null; default: throw new AssertionError(); } } @Override public NullabilityValue binaryOperation(AbstractInsnNode insn, NullabilityValue value1, NullabilityValue value2) { switch (insn.getOpcode()) { case IALOAD: case BALOAD: case CALOAD: case SALOAD: case IADD: case ISUB: case IMUL: case IDIV: case IREM: case ISHL: case ISHR: case IUSHR: case IAND: case IOR: case IXOR: case LCMP: case FCMPL: case FCMPG: case DCMPL: case DCMPG: return newValue(Type.INT_TYPE); case FALOAD: case FADD: case FSUB: case FMUL: case FDIV: case FREM: return newValue(Type.FLOAT_TYPE); case LALOAD: case LADD: case LSUB: case LMUL: case LDIV: case LREM: case LSHL: case LSHR: case LUSHR: case LAND: case LOR: case LXOR: return newValue(Type.LONG_TYPE); case DALOAD: case DADD: case DSUB: case DMUL: case DDIV: case DREM: return newValue(Type.LONG_TYPE); case AALOAD: return getElementValue(value1); case IF_ICMPEQ: case IF_ICMPNE: case IF_ICMPLT: case IF_ICMPGE: case IF_ICMPGT: case IF_ICMPLE: case IF_ACMPEQ: case IF_ACMPNE: case PUTFIELD: return null; default: throw new AssertionError(); } } @Override public NullabilityValue ternaryOperation(AbstractInsnNode insn, NullabilityValue value1, NullabilityValue value2, NullabilityValue value3) { return null; } @Override public NullabilityValue naryOperation(AbstractInsnNode insn, List values) { int opcode = insn.getOpcode(); if (opcode == MULTIANEWARRAY) { return newValue(Type.getType(((MultiANewArrayInsnNode) insn).desc)); } else if (opcode == INVOKEDYNAMIC) { return newValue(Type.getReturnType(((InvokeDynamicInsnNode) insn).desc)); } else { return newValue(Type.getReturnType(((MethodInsnNode) insn).desc)); } } @Override public void returnOperation(AbstractInsnNode insn, NullabilityValue value, NullabilityValue expected) { // Nothing to do. } @Override public NullabilityValue merge(NullabilityValue value1, NullabilityValue value2) { // 合并两者的状态 Nullability mergedState = Nullability.merge(value1.getState(), value2.getState()); // 第一种情况,两个value的类型相同且状态(state)相同 if (value1.equals(value2)) { return value1; } // 第二种情况,两个value的类型相同,但状态(state)不同,需要合并它们的状态(state) Type type1 = value1.getType(); Type type2 = value2.getType(); if (type1 != null && type1.equals(type2)) { Type type = value1.getType(); return new NullabilityValue(type, mergedState); } // 第三种情况,两个value的类型不相同的,而且要合并它们的状态(state) if (type1 != null && (type1.getSort() == Type.OBJECT || type1.getSort() == Type.ARRAY) && type2 != null && (type2.getSort() == Type.OBJECT || type2.getSort() == Type.ARRAY)) { if (type1.equals(NULL_TYPE)) { return new NullabilityValue(type2, mergedState); } if (type2.equals(NULL_TYPE)) { return new NullabilityValue(type1, mergedState); } if (isAssignableFrom(type1, type2)) { return new NullabilityValue(type1, mergedState); } if (isAssignableFrom(type2, type1)) { return new NullabilityValue(type2, mergedState); } int numDimensions = 0; if (type1.getSort() == Type.ARRAY && type2.getSort() == Type.ARRAY && type1.getDimensions() == type2.getDimensions() && type1.getElementType().getSort() == Type.OBJECT && type2.getElementType().getSort() == Type.OBJECT) { numDimensions = type1.getDimensions(); type1 = type1.getElementType(); type2 = type2.getElementType(); } while (true) { if (type1 == null || isInterface(type1)) { NullabilityValue arrayValue = newArrayValue(Type.getObjectType("java/lang/Object"), numDimensions); return new NullabilityValue(arrayValue.getType(), mergedState); } type1 = getSuperClass(type1); if (isAssignableFrom(type1, type2)) { NullabilityValue arrayValue = newArrayValue(type1, numDimensions); return new NullabilityValue(arrayValue.getType(), mergedState); } } } return UNINITIALIZED_VALUE; } protected boolean isInterface(final Type type) { return getClass(type).isInterface(); } protected Type getSuperClass(final Type type) { Class superClass = getClass(type).getSuperclass(); return superClass == null ? null : Type.getType(superClass); } private NullabilityValue newArrayValue(final Type type, final int dimensions) { if (dimensions == 0) { return newValue(type); } else { StringBuilder descriptor = new StringBuilder(); for (int i = 0; i < dimensions; ++i) { descriptor.append('['); } descriptor.append(type.getDescriptor()); return newValue(Type.getType(descriptor.toString())); } } protected NullabilityValue getElementValue(final NullabilityValue objectArrayValue) { Type arrayType = objectArrayValue.getType(); if (arrayType != null) { if (arrayType.getSort() == Type.ARRAY) { return newValue(Type.getType(arrayType.getDescriptor().substring(1))); } else if (arrayType.equals(NULL_TYPE)) { return objectArrayValue; } } throw new AssertionError(); } protected boolean isSubTypeOf(final NullabilityValue value, final NullabilityValue expected) { Type expectedType = expected.getType(); Type type = value.getType(); switch (expectedType.getSort()) { case Type.INT: case Type.FLOAT: case Type.LONG: case Type.DOUBLE: return type.equals(expectedType); case Type.ARRAY: case Type.OBJECT: if (type.equals(NULL_TYPE)) { return true; } else if (type.getSort() == Type.OBJECT || type.getSort() == Type.ARRAY) { if (isAssignableFrom(expectedType, type)) { return true; } else if (getClass(expectedType).isInterface()) { // The merge of class or interface types can only yield class types (because it is not // possible in general to find an unambiguous common super interface, due to multiple // inheritance). Because of this limitation, we need to relax the subtyping check here // if 'value' is an interface. return Object.class.isAssignableFrom(getClass(type)); } else { return false; } } else { return false; } default: throw new AssertionError(); } } protected boolean isAssignableFrom(final Type type1, final Type type2) { if (type1.equals(type2)) { return true; } return getClass(type1).isAssignableFrom(getClass(type2)); } protected Class getClass(final Type type) { try { if (type.getSort() == Type.ARRAY) { return Class.forName(type.getDescriptor().replace('/', '.'), false, loader); } return Class.forName(type.getClassName(), false, loader); } catch (ClassNotFoundException e) { throw new TypeNotPresentException(e.toString(), e); } } } ================================================ FILE: src/main/java/lsieun/asm/analysis/nullability/NullabilityValue.java ================================================ package lsieun.asm.analysis.nullability; import org.objectweb.asm.Type; import org.objectweb.asm.tree.analysis.Value; public class NullabilityValue implements Value { private final Type type; private Nullability state; public NullabilityValue(Type type) { this(type, Nullability.UNKNOWN); } public NullabilityValue(Type type, Nullability state) { this.type = type; this.state = state; } public Type getType() { return type; } public void setState(Nullability state) { this.state = state; } public Nullability getState() { return state; } @Override public int getSize() { return type == Type.LONG_TYPE || type == Type.DOUBLE_TYPE ? 2 : 1; } public boolean isReference() { return type != null && (type.getSort() == Type.OBJECT || type.getSort() == Type.ARRAY); } @Override public boolean equals(final Object value) { if (value == this) { return true; } else if (value instanceof NullabilityValue) { NullabilityValue another = (NullabilityValue) value; if (type == null) { return ((NullabilityValue) value).type == null; } else { return type.equals(((NullabilityValue) value).type) && state == another.state; } } else { return false; } } @Override public int hashCode() { return type == null ? 0 : type.hashCode(); } } ================================================ FILE: src/main/java/lsieun/asm/analysis/state/StateInterpreter.java ================================================ package lsieun.asm.analysis.state; import org.objectweb.asm.Opcodes; import org.objectweb.asm.Type; import org.objectweb.asm.tree.AbstractInsnNode; import org.objectweb.asm.tree.analysis.AnalyzerException; import org.objectweb.asm.tree.analysis.Interpreter; import java.util.List; public class StateInterpreter extends Interpreter implements Opcodes { public StateInterpreter() { super(ASM9); if (getClass() != StateInterpreter.class) { throw new IllegalStateException(); } } public StateInterpreter(int api) { super(api); } @Override public StateValue newValue(Type type) { return null; } @Override public StateValue newOperation(AbstractInsnNode insn) throws AnalyzerException { return null; } @Override public StateValue copyOperation(AbstractInsnNode insn, StateValue value) throws AnalyzerException { return null; } @Override public StateValue unaryOperation(AbstractInsnNode insn, StateValue value) throws AnalyzerException { return null; } @Override public StateValue binaryOperation(AbstractInsnNode insn, StateValue value1, StateValue value2) throws AnalyzerException { return null; } @Override public StateValue ternaryOperation(AbstractInsnNode insn, StateValue value1, StateValue value2, StateValue value3) throws AnalyzerException { return null; } @Override public StateValue naryOperation(AbstractInsnNode insn, List values) throws AnalyzerException { return null; } @Override public void returnOperation(AbstractInsnNode insn, StateValue value, StateValue expected) throws AnalyzerException { } @Override public StateValue merge(StateValue value1, StateValue value2) { return null; } } ================================================ FILE: src/main/java/lsieun/asm/analysis/state/StateType.java ================================================ package lsieun.asm.analysis.state; public enum StateType { DEFAULT, // default TOP, INT, FLOAT, LONG, DOUBLE, NULL, CP, // constant pool HEAP; // 从堆内存上分配的对象 } ================================================ FILE: src/main/java/lsieun/asm/analysis/state/StateValue.java ================================================ package lsieun.asm.analysis.state; import org.objectweb.asm.tree.analysis.Value; public class StateValue implements Value { public final int size; public StateValue(int size) { this.size = size; } @Override public int getSize() { return size; } } ================================================ FILE: src/main/java/lsieun/asm/analysis/transition/DestinationInterpreter.java ================================================ package lsieun.asm.analysis.transition; import org.objectweb.asm.Opcodes; import org.objectweb.asm.Type; import org.objectweb.asm.tree.*; import org.objectweb.asm.tree.analysis.AnalyzerException; import org.objectweb.asm.tree.analysis.Interpreter; import org.objectweb.asm.tree.analysis.SourceValue; import java.util.HashSet; import java.util.List; public class DestinationInterpreter extends Interpreter implements Opcodes { public DestinationInterpreter() { super(ASM9); if (getClass() != DestinationInterpreter.class) { throw new IllegalStateException(); } } protected DestinationInterpreter(final int api) { super(api); } @Override public SourceValue newValue(Type type) { if (type == Type.VOID_TYPE) { return null; } return new SourceValue(type == null ? 1 : type.getSize(), new HashSet<>()); } @Override public SourceValue newOperation(AbstractInsnNode insn) { int size; switch (insn.getOpcode()) { case LCONST_0: case LCONST_1: case DCONST_0: case DCONST_1: size = 2; break; case LDC: Object value = ((LdcInsnNode) insn).cst; size = value instanceof Long || value instanceof Double ? 2 : 1; break; case GETSTATIC: size = Type.getType(((FieldInsnNode) insn).desc).getSize(); break; default: size = 1; break; } return new SourceValue(size, new HashSet<>()); } @Override public SourceValue copyOperation(AbstractInsnNode insn, SourceValue value) throws AnalyzerException { int opcode = insn.getOpcode(); if (opcode >= ISTORE && opcode <= ASTORE) { value.insns.add(insn); } return new SourceValue(value.getSize(), new HashSet<>()); } @Override public SourceValue unaryOperation(AbstractInsnNode insn, SourceValue value) throws AnalyzerException { value.insns.add(insn); int size; switch (insn.getOpcode()) { case LNEG: case DNEG: case I2L: case I2D: case L2D: case F2L: case F2D: case D2L: size = 2; break; case GETFIELD: size = Type.getType(((FieldInsnNode) insn).desc).getSize(); break; default: size = 1; break; } return new SourceValue(size, new HashSet<>()); } @Override public SourceValue binaryOperation(AbstractInsnNode insn, SourceValue value1, SourceValue value2) throws AnalyzerException { value1.insns.add(insn); value2.insns.add(insn); int size; switch (insn.getOpcode()) { case LALOAD: case DALOAD: case LADD: case DADD: case LSUB: case DSUB: case LMUL: case DMUL: case LDIV: case DDIV: case LREM: case DREM: case LSHL: case LSHR: case LUSHR: case LAND: case LOR: case LXOR: size = 2; break; default: size = 1; break; } return new SourceValue(size, new HashSet<>()); } @Override public SourceValue ternaryOperation(AbstractInsnNode insn, SourceValue value1, SourceValue value2, SourceValue value3) throws AnalyzerException { value1.insns.add(insn); value2.insns.add(insn); value3.insns.add(insn); return new SourceValue(1, new HashSet<>()); } @Override public SourceValue naryOperation(AbstractInsnNode insn, List values) throws AnalyzerException { if (values != null) { for (SourceValue v : values) { v.insns.add(insn); } } int size; int opcode = insn.getOpcode(); if (opcode == MULTIANEWARRAY) { size = 1; } else if (opcode == INVOKEDYNAMIC) { size = Type.getReturnType(((InvokeDynamicInsnNode) insn).desc).getSize(); } else { size = Type.getReturnType(((MethodInsnNode) insn).desc).getSize(); } return new SourceValue(size, new HashSet<>()); } @Override public void returnOperation(AbstractInsnNode insn, SourceValue value, SourceValue expected) throws AnalyzerException { // Nothing to do. } @Override public SourceValue merge(final SourceValue value1, final SourceValue value2) { return new SourceValue(Math.min(value1.size, value2.size), new HashSet<>()); } } ================================================ FILE: src/main/java/lsieun/asm/commons/ClassRemapperExample01.java ================================================ package lsieun.asm.commons; import lsieun.utils.FileUtils; import org.objectweb.asm.ClassReader; import org.objectweb.asm.ClassVisitor; import org.objectweb.asm.ClassWriter; import org.objectweb.asm.commons.ClassRemapper; import org.objectweb.asm.commons.Remapper; import org.objectweb.asm.commons.SimpleRemapper; public class ClassRemapperExample01 { public static void main(String[] args) { String origin_name = "sample/HelloWorld"; String target_name = "sample/GoodChild"; String origin_filepath = getFilePath(origin_name); byte[] bytes1 = FileUtils.readBytes(origin_filepath); //(1)构建ClassReader ClassReader cr = new ClassReader(bytes1); //(2)构建ClassWriter ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_FRAMES); //(3)串连ClassVisitor Remapper remapper = new SimpleRemapper(origin_name, target_name); ClassVisitor cv = new ClassRemapper(cw, remapper); //(4)两者进行结合 int parsingOptions = ClassReader.SKIP_DEBUG | ClassReader.SKIP_FRAMES; cr.accept(cv, parsingOptions); //(5)重新生成Class byte[] bytes2 = cw.toByteArray(); String target_filepath = getFilePath(target_name); FileUtils.writeBytes(target_filepath, bytes2); } public static String getFilePath(String internalName) { String relative_path = String.format("%s.class", internalName); return FileUtils.getFilePath(relative_path); } } ================================================ FILE: src/main/java/lsieun/asm/commons/ClassRemapperExample02.java ================================================ package lsieun.asm.commons; import lsieun.utils.FileUtils; import org.objectweb.asm.ClassReader; import org.objectweb.asm.ClassVisitor; import org.objectweb.asm.ClassWriter; import org.objectweb.asm.commons.ClassRemapper; import org.objectweb.asm.commons.Remapper; import org.objectweb.asm.commons.SimpleRemapper; import java.util.HashMap; import java.util.Map; public class ClassRemapperExample02 { public static void main(String[] args) { String origin_name = "sample/HelloWorld"; String target_name = "sample/GoodChild"; String origin_filepath = getFilePath(origin_name); byte[] bytes1 = FileUtils.readBytes(origin_filepath); //(1)构建ClassReader ClassReader cr = new ClassReader(bytes1); //(2)构建ClassWriter ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_FRAMES); //(3)串连ClassVisitor Map mapping = new HashMap<>(); mapping.put(origin_name, target_name); mapping.put(origin_name + ".intValue", "a"); mapping.put(origin_name + ".test()V", "b"); Remapper mapper = new SimpleRemapper(mapping); ClassVisitor cv = new ClassRemapper(cw, mapper); //(4)两者进行结合 int parsingOptions = ClassReader.SKIP_DEBUG | ClassReader.SKIP_FRAMES; cr.accept(cv, parsingOptions); //(5)重新生成Class byte[] bytes2 = cw.toByteArray(); String target_filepath = getFilePath(target_name); FileUtils.writeBytes(target_filepath, bytes2); } public static String getFilePath(String internalName) { String relative_path = String.format("%s.class", internalName); return FileUtils.getFilePath(relative_path); } } ================================================ FILE: src/main/java/lsieun/asm/commons/ClassRemapperExample03.java ================================================ package lsieun.asm.commons; import lsieun.utils.FileUtils; import org.objectweb.asm.ClassReader; import org.objectweb.asm.ClassVisitor; import org.objectweb.asm.ClassWriter; import org.objectweb.asm.commons.ClassRemapper; import org.objectweb.asm.commons.Remapper; import org.objectweb.asm.commons.SimpleRemapper; import java.util.HashMap; import java.util.Map; public class ClassRemapperExample03 { public static void main(String[] args) { Map mapping = new HashMap<>(); mapping.put("sample/HelloWorld", "sample/AAA"); mapping.put("sample/GoodChild", "sample/BBB"); mapping.put("sample/HelloWorld.test()V", "a"); mapping.put("sample/GoodChild.study()V", "b"); obfuscate("sample/HelloWorld", "sample/AAA", mapping); obfuscate("sample/GoodChild", "sample/BBB", mapping); } public static void obfuscate(String origin_name, String target_name, Map mapping) { String origin_filepath = getFilePath(origin_name); byte[] bytes1 = FileUtils.readBytes(origin_filepath); //(1)构建ClassReader ClassReader cr = new ClassReader(bytes1); //(2)构建ClassWriter ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_FRAMES); //(3)串连ClassVisitor Remapper mapper = new SimpleRemapper(mapping); ClassVisitor cv = new ClassRemapper(cw, mapper); //(4)两者进行结合 int parsingOptions = ClassReader.SKIP_DEBUG | ClassReader.SKIP_FRAMES; cr.accept(cv, parsingOptions); //(5)重新生成Class byte[] bytes2 = cw.toByteArray(); String target_filepath = getFilePath(target_name); FileUtils.writeBytes(target_filepath, bytes2); } public static String getFilePath(String internalName) { String relative_path = String.format("%s.class", internalName); return FileUtils.getFilePath(relative_path); } } ================================================ FILE: src/main/java/lsieun/asm/commons/GeneratorAdapterExample01.java ================================================ package lsieun.asm.commons; import lsieun.utils.FileUtils; import org.objectweb.asm.ClassWriter; import org.objectweb.asm.Type; import org.objectweb.asm.commons.GeneratorAdapter; import org.objectweb.asm.commons.Method; import org.objectweb.asm.util.TraceClassVisitor; import java.io.PrintStream; import java.io.PrintWriter; import static org.objectweb.asm.Opcodes.*; public class GeneratorAdapterExample01 { public static void main(String[] args) throws Exception { String relative_path = "sample/HelloWorld.class"; String filepath = FileUtils.getFilePath(relative_path); // (1) 生成byte[]内容 byte[] bytes = dump(); // (2) 保存byte[]到文件 FileUtils.writeBytes(filepath, bytes); } public static byte[] dump() throws Exception { ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_FRAMES); PrintWriter printWriter = new PrintWriter(System.out); TraceClassVisitor cv = new TraceClassVisitor(cw, printWriter); cv.visit(V1_8, ACC_PUBLIC + ACC_SUPER, "sample/HelloWorld", null, "java/lang/Object", null); { Method m1 = Method.getMethod("void ()"); GeneratorAdapter mg = new GeneratorAdapter(ACC_PUBLIC, m1, null, null, cv); mg.loadThis(); mg.invokeConstructor(Type.getType(Object.class), m1); mg.returnValue(); mg.endMethod(); } { Method m2 = Method.getMethod("void main (String[])"); GeneratorAdapter mg = new GeneratorAdapter(ACC_PUBLIC + ACC_STATIC, m2, null, null, cv); mg.getStatic(Type.getType(System.class), "out", Type.getType(PrintStream.class)); mg.push("Hello world!"); mg.invokeVirtual(Type.getType(PrintStream.class), Method.getMethod("void println (String)")); mg.returnValue(); mg.endMethod(); } cv.visitEnd(); return cw.toByteArray(); } } ================================================ FILE: src/main/java/lsieun/asm/commons/InstructionAdapterExample01.java ================================================ package lsieun.asm.commons; import lsieun.utils.FileUtils; import org.objectweb.asm.ClassWriter; import org.objectweb.asm.MethodVisitor; import org.objectweb.asm.Type; import org.objectweb.asm.commons.InstructionAdapter; import static org.objectweb.asm.Opcodes.*; public class InstructionAdapterExample01 { public static void main(String[] args) throws Exception { String relative_path = "sample/HelloWorld.class"; String filepath = FileUtils.getFilePath(relative_path); // (1) 生成byte[]内容 byte[] bytes = dump(); // (2) 保存byte[]到文件 FileUtils.writeBytes(filepath, bytes); } public static byte[] dump() throws Exception { // (1) 创建ClassWriter对象 ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_FRAMES); // (2) 调用visitXxx()方法 cw.visit(V1_8, ACC_PUBLIC + ACC_SUPER, "sample/HelloWorld", null, "java/lang/Object", null); { MethodVisitor mv1 = cw.visitMethod(ACC_PUBLIC, "", "()V", null, null); InstructionAdapter ia = new InstructionAdapter(mv1); ia.visitCode(); ia.load(0, InstructionAdapter.OBJECT_TYPE); ia.invokespecial("java/lang/Object", "", "()V", false); ia.areturn(Type.VOID_TYPE); ia.visitMaxs(1, 1); ia.visitEnd(); } { MethodVisitor mv2 = cw.visitMethod(ACC_PUBLIC, "test", "()V", null, null); InstructionAdapter ia = new InstructionAdapter(mv2); ia.visitCode(); ia.getstatic("java/lang/System", "out", "Ljava/io/PrintStream;"); ia.aconst("Hello World"); ia.invokevirtual("java/io/PrintStream", "println", "(Ljava/lang/String;)V", false); ia.areturn(Type.VOID_TYPE); ia.visitMaxs(2, 1); ia.visitEnd(); } cw.visitEnd(); // (3) 调用toByteArray()方法 return cw.toByteArray(); } } ================================================ FILE: src/main/java/lsieun/asm/commons/MethodStackMapFrame02Visitor.java ================================================ package lsieun.asm.commons; import lsieun.classfile.ClassFile; import lsieun.classfile.InsnRaw; import org.objectweb.asm.*; import org.objectweb.asm.commons.AnalyzerAdapter; import java.util.List; public class MethodStackMapFrame02Visitor extends ClassVisitor { private String owner; private final ClassFile classFile; private int methodIndex = 0; public MethodStackMapFrame02Visitor(int api, ClassVisitor classVisitor, byte[] bytes) { super(api, classVisitor); this.classFile = new ClassFile(bytes); } @Override public void visit(int version, int access, String name, String signature, String superName, String[] interfaces) { super.visit(version, access, name, signature, superName, interfaces); this.owner = name; } @Override public MethodVisitor visitMethod(int access, String name, String descriptor, String signature, String[] exceptions) { MethodVisitor mv = super.visitMethod(access, name, descriptor, signature, exceptions); byte[] code_bytes = classFile.getCode(methodIndex++); InsnRaw insnRaw = new InsnRaw(code_bytes); List list = insnRaw.getList(); mv = new MethodStackMapFrame02Adapter(api, owner, access, name, descriptor, mv, list); return mv; } private static class MethodStackMapFrame02Adapter extends AnalyzerAdapter { private static final String FORMAT = "%-30s // %s"; private static final String START = "{"; private static final String STOP = "}"; private static final String EMPTY = "{}"; private static final String SEPARATOR = "|"; private final int methodAccess; private final String methodName; private final String methodDesc; private final List insnList; private int insnIndex = 0; public MethodStackMapFrame02Adapter(int api, String owner, int access, String name, String descriptor, MethodVisitor methodVisitor, List insnList) { super(api, owner, access, name, descriptor, methodVisitor); this.methodAccess = access; this.methodName = name; this.methodDesc = descriptor; this.insnList = insnList; } @Override public void visitCode() { super.visitCode(); System.out.println(methodName + ":" + methodDesc); String frame = getStackFrame(); String line = String.format(FORMAT, "", frame); System.out.println(line); } @Override public void visitFrame(int type, int numLocal, Object[] local, int numStack, Object[] stack) { super.visitFrame(type, numLocal, local, numStack, stack); String frame = getStackFrame(); String line = String.format(FORMAT, "", frame); System.out.println(line); } @Override public void visitInsn(int opcode) { super.visitInsn(opcode); printStackMapFrame(); } @Override public void visitIntInsn(int opcode, int operand) { super.visitIntInsn(opcode, operand); printStackMapFrame(); } @Override public void visitVarInsn(int opcode, int var) { super.visitVarInsn(opcode, var); printStackMapFrame(); } @Override public void visitTypeInsn(int opcode, String type) { super.visitTypeInsn(opcode, type); printStackMapFrame(); } @Override public void visitFieldInsn(int opcode, String owner, String name, String descriptor) { super.visitFieldInsn(opcode, owner, name, descriptor); printStackMapFrame(); } @Override public void visitMethodInsn(int opcode, String owner, String name, String descriptor, boolean isInterface) { super.visitMethodInsn(opcode, owner, name, descriptor, isInterface); printStackMapFrame(); } @Override public void visitInvokeDynamicInsn(String name, String descriptor, Handle bootstrapMethodHandle, Object... bootstrapMethodArguments) { super.visitInvokeDynamicInsn(name, descriptor, bootstrapMethodHandle, bootstrapMethodArguments); printStackMapFrame(); } @Override public void visitJumpInsn(int opcode, Label label) { super.visitJumpInsn(opcode, label); printStackMapFrame(); } @Override public void visitLdcInsn(Object value) { super.visitLdcInsn(value); printStackMapFrame(); } @Override public void visitIincInsn(int var, int increment) { super.visitIincInsn(var, increment); printStackMapFrame(); } @Override public void visitTableSwitchInsn(int min, int max, Label dflt, Label... labels) { super.visitTableSwitchInsn(min, max, dflt, labels); int insnSize = insnList.size(); String insn; if (insnIndex < insnSize) { insn = insnList.get(insnIndex); insnIndex++; } else { insn = ""; } String[] array = insn.split(System.lineSeparator(), 2); String frame = getStackFrame(); String line = String.format(FORMAT, array[0], frame); System.out.println(line); System.out.println(array[1]); } @Override public void visitLookupSwitchInsn(Label dflt, int[] keys, Label[] labels) { super.visitLookupSwitchInsn(dflt, keys, labels); int insnSize = insnList.size(); String insn; if (insnIndex < insnSize) { insn = insnList.get(insnIndex); insnIndex++; } else { insn = ""; } String[] array = insn.split(System.lineSeparator(), 2); String frame = getStackFrame(); String line = String.format(FORMAT, array[0], frame); System.out.println(line); System.out.println(array[1]); } @Override public void visitMultiANewArrayInsn(String descriptor, int numDimensions) { super.visitMultiANewArrayInsn(descriptor, numDimensions); printStackMapFrame(); } @Override public void visitEnd() { super.visitEnd(); System.out.println(); System.out.println(); } private void printStackMapFrame() { int insnSize = insnList.size(); String insn; if (insnIndex < insnSize) { insn = insnList.get(insnIndex); insnIndex++; } else { insn = ""; } if (insn.contains(" wide ")) { System.out.println(insn); insn = insnList.get(insnIndex); insnIndex++; } String frame = getStackFrame(); String line = String.format(FORMAT, insn, frame); System.out.println(line); } private String getStackFrame() { if (locals != null && locals.size() > 0) { boolean isStaticMethod = (methodAccess & Opcodes.ACC_STATIC) != 0; if (!isStaticMethod) { Object obj = locals.get(0); if (obj != Opcodes.UNINITIALIZED_THIS) { locals.set(0, "this"); } } } String locals_str = locals == null ? EMPTY : list2Str(locals); String stack_str = stack == null ? EMPTY : list2Str(stack); return String.format("%s %s %s", locals_str, SEPARATOR, stack_str); } private String list2Str(List list) { if (list == null || list.size() == 0) return EMPTY; int size = list.size(); String[] array = new String[size]; for (int i = 0; i < size; i++) { Object item = list.get(i); array[i] = item2Str(item); } return array2Str(array); } private String array2Str(String[] array) { if (array == null || array.length == 0) return EMPTY; int length = array.length; StringBuilder sb = new StringBuilder(); sb.append(START); for (int i = 0; i < length - 1; i++) { sb.append(array[i]).append(", "); } sb.append(array[length - 1]); sb.append(STOP); return sb.toString(); } private String item2Str(Object obj) { if (obj == Opcodes.TOP) { return "top"; } else if (obj == Opcodes.INTEGER) { return "int"; } else if (obj == Opcodes.FLOAT) { return "float"; } else if (obj == Opcodes.DOUBLE) { return "double"; } else if (obj == Opcodes.LONG) { return "long"; } else if (obj == Opcodes.NULL) { return "null"; } else if (obj == Opcodes.UNINITIALIZED_THIS) { return "uninitialized_this"; } else if (obj instanceof Label) { Object value = uninitializedTypes.get(obj); return "uninitialized_" + getSimpleClassName(value); } else { return getSimpleClassName(obj); } } private String getSimpleClassName(Object obj) { if (obj == null) return "null"; String descriptor = obj.toString(); int squareIndex = descriptor.lastIndexOf("["); String prefix = descriptor.substring(0, squareIndex + 1); String simpleName = descriptor.substring(squareIndex + 1); if (simpleName.startsWith("L") && simpleName.endsWith(";")) { simpleName = simpleName.substring(1, simpleName.length() - 1); } int slashIndex = simpleName.lastIndexOf("/"); simpleName = simpleName.substring(slashIndex + 1); return prefix + simpleName; } } } ================================================ FILE: src/main/java/lsieun/asm/commons/MethodStackMapFrameVisitor.java ================================================ package lsieun.asm.commons; import org.objectweb.asm.*; import org.objectweb.asm.commons.AnalyzerAdapter; import java.util.Arrays; import java.util.List; public class MethodStackMapFrameVisitor extends ClassVisitor { private String owner; public MethodStackMapFrameVisitor(int api, ClassVisitor classVisitor) { super(api, classVisitor); } @Override public void visit(int version, int access, String name, String signature, String superName, String[] interfaces) { super.visit(version, access, name, signature, superName, interfaces); this.owner = name; } @Override public MethodVisitor visitMethod(int access, String name, String descriptor, String signature, String[] exceptions) { MethodVisitor mv = super.visitMethod(access, name, descriptor, signature, exceptions); return new MethodStackMapFrameAdapter(api, owner, access, name, descriptor, mv); } private static class MethodStackMapFrameAdapter extends AnalyzerAdapter { private final String methodName; private final String methodDesc; public MethodStackMapFrameAdapter(int api, String owner, int access, String name, String descriptor, MethodVisitor methodVisitor) { super(api, owner, access, name, descriptor, methodVisitor); this.methodName = name; this.methodDesc = descriptor; } @Override public void visitCode() { super.visitCode(); System.out.println(); System.out.println(methodName + methodDesc); printStackMapFrame(); } @Override public void visitInsn(int opcode) { super.visitInsn(opcode); printStackMapFrame(); } @Override public void visitIntInsn(int opcode, int operand) { super.visitIntInsn(opcode, operand); printStackMapFrame(); } @Override public void visitVarInsn(int opcode, int var) { super.visitVarInsn(opcode, var); printStackMapFrame(); } @Override public void visitTypeInsn(int opcode, String type) { super.visitTypeInsn(opcode, type); printStackMapFrame(); } @Override public void visitFieldInsn(int opcode, String owner, String name, String descriptor) { super.visitFieldInsn(opcode, owner, name, descriptor); printStackMapFrame(); } @Override public void visitMethodInsn(int opcode, String owner, String name, String descriptor, boolean isInterface) { super.visitMethodInsn(opcode, owner, name, descriptor, isInterface); printStackMapFrame(); } @Override public void visitInvokeDynamicInsn(String name, String descriptor, Handle bootstrapMethodHandle, Object... bootstrapMethodArguments) { super.visitInvokeDynamicInsn(name, descriptor, bootstrapMethodHandle, bootstrapMethodArguments); printStackMapFrame(); } @Override public void visitJumpInsn(int opcode, Label label) { super.visitJumpInsn(opcode, label); printStackMapFrame(); } @Override public void visitLdcInsn(Object value) { super.visitLdcInsn(value); printStackMapFrame(); } @Override public void visitIincInsn(int var, int increment) { super.visitIincInsn(var, increment); printStackMapFrame(); } @Override public void visitTableSwitchInsn(int min, int max, Label dflt, Label... labels) { super.visitTableSwitchInsn(min, max, dflt, labels); printStackMapFrame(); } @Override public void visitLookupSwitchInsn(Label dflt, int[] keys, Label[] labels) { super.visitLookupSwitchInsn(dflt, keys, labels); printStackMapFrame(); } @Override public void visitMultiANewArrayInsn(String descriptor, int numDimensions) { super.visitMultiANewArrayInsn(descriptor, numDimensions); printStackMapFrame(); } private void printStackMapFrame() { String locals_str = locals == null ? "[]" : list2Str(locals); String stack_str = stack == null ? "[]" : list2Str(stack); String line = String.format("%s %s", locals_str, stack_str); System.out.println(line); } private String list2Str(List list) { if (list == null || list.size() == 0) return "[]"; int size = list.size(); String[] array = new String[size]; for (int i = 0; i < size; i++) { Object item = list.get(i); array[i] = item2Str(item); } return Arrays.toString(array); } private String item2Str(Object obj) { if (obj == Opcodes.TOP) { return "top"; } else if (obj == Opcodes.INTEGER) { return "int"; } else if (obj == Opcodes.FLOAT) { return "float"; } else if (obj == Opcodes.DOUBLE) { return "double"; } else if (obj == Opcodes.LONG) { return "long"; } else if (obj == Opcodes.NULL) { return "null"; } else if (obj == Opcodes.UNINITIALIZED_THIS) { return "uninitialized_this"; } else if (obj instanceof Label) { Object value = uninitializedTypes.get(obj); if (value == null) { return obj.toString(); } else { return "uninitialized_" + value; } } else { return obj.toString(); } } } } ================================================ FILE: src/main/java/lsieun/asm/commons/SerialVersionUIDAdderExample01.java ================================================ package lsieun.asm.commons; import lsieun.utils.FileUtils; import org.objectweb.asm.ClassReader; import org.objectweb.asm.ClassVisitor; import org.objectweb.asm.ClassWriter; import org.objectweb.asm.Opcodes; import org.objectweb.asm.commons.SerialVersionUIDAdder; public class SerialVersionUIDAdderExample01 { public static void main(String[] args) { String relative_path = "sample/HelloWorld.class"; String filepath = FileUtils.getFilePath(relative_path); byte[] bytes1 = FileUtils.readBytes(filepath); //(1)构建ClassReader ClassReader cr = new ClassReader(bytes1); //(2)构建ClassWriter ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_FRAMES); //(3)串连ClassVisitor int api = Opcodes.ASM9; ClassVisitor cv = new SerialVersionUIDAdder(cw); //(4)结合ClassReader和ClassVisitor int parsingOptions = ClassReader.SKIP_DEBUG | ClassReader.SKIP_FRAMES; cr.accept(cv, parsingOptions); //(5)生成byte[] byte[] bytes2 = cw.toByteArray(); FileUtils.writeBytes(filepath, bytes2); } } ================================================ FILE: src/main/java/lsieun/asm/commons/StaticInitMergerExample01.java ================================================ package lsieun.asm.commons; import lsieun.asm.template.ClassAddInterfaceVisitor; import lsieun.asm.template.ClassMergeVisitor; import lsieun.utils.FileUtils; import org.objectweb.asm.ClassReader; import org.objectweb.asm.ClassVisitor; import org.objectweb.asm.ClassWriter; import org.objectweb.asm.Opcodes; import org.objectweb.asm.commons.*; import org.objectweb.asm.tree.ClassNode; import java.util.List; public class StaticInitMergerExample01 { private static final int API_VERSION = Opcodes.ASM9; public static void main(String[] args) { // 第一步,读取两个类文件 String first_class = "sample/HelloWorld"; String second_class = "sample/GoodChild"; String first_class_filepath = getFilePath(first_class); byte[] bytes1 = FileUtils.readBytes(first_class_filepath); String second_class_filepath = getFilePath(second_class); byte[] bytes2 = FileUtils.readBytes(second_class_filepath); // 第二步,将sample/GoodChild类重命名为sample/HelloWorld byte[] bytes3 = renameClass(second_class, first_class, bytes2); // 第三步,合并两个类 byte[] bytes4 = mergeClass(bytes1, bytes3); // 第四步,处理重复的class initialization method byte[] bytes5 = removeDuplicateStaticInitMethod(bytes4); FileUtils.writeBytes(first_class_filepath, bytes5); } public static String getFilePath(String internalName) { String relative_path = String.format("%s.class", internalName); return FileUtils.getFilePath(relative_path); } public static byte[] renameClass(String origin_name, String target_name, byte[] bytes) { //(1)构建ClassReader ClassReader cr = new ClassReader(bytes); //(2)构建ClassWriter ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_FRAMES); //(3)串连ClassVisitor Remapper remapper = new SimpleRemapper(origin_name, target_name); ClassVisitor cv = new ClassRemapper(cw, remapper); //(4)两者进行结合 int parsingOptions = ClassReader.SKIP_DEBUG | ClassReader.SKIP_FRAMES; cr.accept(cv, parsingOptions); //(5)重新生成Class return cw.toByteArray(); } public static byte[] mergeClass(byte[] bytes1, byte[] bytes2) { //(1)构建ClassReader ClassReader cr = new ClassReader(bytes1); //(2)构建ClassWriter ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_FRAMES); //(3)串连ClassVisitor ClassNode cn = getClassNode(bytes2); List interface_list = cn.interfaces; int size = interface_list.size(); String[] interfaces = new String[size]; for (int i = 0; i < size; i++) { String item = interface_list.get(i); interfaces[i] = item; } ClassMergeVisitor cmv = new ClassMergeVisitor(API_VERSION, cw, cn); ClassAddInterfaceVisitor cv = new ClassAddInterfaceVisitor(API_VERSION, cmv, interfaces); //(4)两者进行结合 int parsingOptions = ClassReader.SKIP_DEBUG | ClassReader.SKIP_FRAMES; cr.accept(cv, parsingOptions); //(5)重新生成Class return cw.toByteArray(); } public static ClassNode getClassNode(byte[] bytes) { ClassReader cr = new ClassReader(bytes); ClassNode cn = new ClassNode(); int parsingOptions = ClassReader.SKIP_DEBUG | ClassReader.SKIP_FRAMES; cr.accept(cn, parsingOptions); return cn; } public static byte[] removeDuplicateStaticInitMethod(byte[] bytes) { //(1)构建ClassReader ClassReader cr = new ClassReader(bytes); //(2)构建ClassWriter ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_FRAMES); //(3)串连ClassVisitor ClassVisitor cv = new StaticInitMerger("class_init$", cw); //(4)结合ClassReader和ClassVisitor int parsingOptions = ClassReader.SKIP_DEBUG | ClassReader.SKIP_FRAMES; cr.accept(cv, parsingOptions); //(5)生成byte[] return cw.toByteArray(); } } ================================================ FILE: src/main/java/lsieun/asm/core/ChangeURLVisitor.java ================================================ package lsieun.asm.core; import lsieun.annotation.todo.ToDo; import org.objectweb.asm.ClassVisitor; import org.objectweb.asm.Label; import org.objectweb.asm.MethodVisitor; import org.objectweb.asm.Opcodes; import org.objectweb.asm.commons.AdviceAdapter; @ToDo("Remove") public class ChangeURLVisitor extends ClassVisitor { public ChangeURLVisitor(ClassVisitor classVisitor) { super(Opcodes.ASM9, classVisitor); } @Override public MethodVisitor visitMethod(int access, String name, String descriptor, String signature, String[] exceptions) { MethodVisitor mv = super.visitMethod(access, name, descriptor, signature, exceptions); if (mv != null && "".equals(name) && "(Ljava/net/URL;Ljava/lang/String;Ljava/net/URLStreamHandler;)V".equals(descriptor)) { mv = new ChangeURLAdapter(mv, access, name, descriptor); } return mv; } public class ChangeURLAdapter extends AdviceAdapter { protected ChangeURLAdapter(MethodVisitor methodVisitor, int access, String name, String descriptor) { super(Opcodes.ASM9, methodVisitor, access, name, descriptor); } @Override protected void onMethodEnter() { super.visitVarInsn(ALOAD, 2); Label elseLabel = new Label(); super.visitJumpInsn(IFNULL, elseLabel); super.visitVarInsn(ALOAD, 2); super.visitLdcInsn("/lservice/rpc/validateKey.action"); super.visitMethodInsn(INVOKEVIRTUAL, "java/lang/String", "contains", "(Ljava/lang/CharSequence;)Z", false); super.visitJumpInsn(IFEQ, elseLabel); super.visitTypeInsn(NEW, "java/net/MalformedURLException"); super.visitInsn(DUP); super.visitMethodInsn(INVOKESPECIAL, "java/net/MalformedURLException", "", "()V", false); super.visitInsn(ATHROW); super.visitLabel(elseLabel); } } } ================================================ FILE: src/main/java/lsieun/asm/core/ClassChangeVersionVisitor.java ================================================ package lsieun.asm.core; import org.objectweb.asm.ClassVisitor; import org.objectweb.asm.Opcodes; public class ClassChangeVersionVisitor extends ClassVisitor { public ClassChangeVersionVisitor(int api, ClassVisitor classVisitor) { super(api, classVisitor); } @Override public void visit(int version, int access, String name, String signature, String superName, String[] interfaces) { super.visit(Opcodes.V1_7, access, name, signature, superName, interfaces); } } ================================================ FILE: src/main/java/lsieun/asm/core/ClassCloneVisitor.java ================================================ package lsieun.asm.core; import org.objectweb.asm.ClassVisitor; public class ClassCloneVisitor extends ClassVisitor { public ClassCloneVisitor(int api, ClassVisitor cw) { super(api, cw); } @Override public void visit(int version, int access, String name, String signature, String superName, String[] interfaces) { super.visit(version, access, name, signature, superName, new String[]{"java/lang/Cloneable"}); } } ================================================ FILE: src/main/java/lsieun/asm/core/ClassDecompileVisitor.java ================================================ package lsieun.asm.core; import org.objectweb.asm.*; public class ClassDecompileVisitor extends ClassVisitor { public ClassDecompileVisitor() { super(Opcodes.ASM9); } @Override public void visit(int version, int access, String name, String signature, String superName, String[] interfaces) { System.out.println(name + " extends " + superName + " {"); } @Override public FieldVisitor visitField(int access, String name, String descriptor, String signature, Object value) { System.out.println(" " + descriptor + " " + name); return null; } @Override public MethodVisitor visitMethod(int access, String name, String descriptor, String signature, String[] exceptions) { System.out.println(" " + name + " " + descriptor); return null; } @Override public void visitEnd() { System.out.println("}"); } } ================================================ FILE: src/main/java/lsieun/asm/core/ClassListMemberVisitor.java ================================================ package lsieun.asm.core; import org.objectweb.asm.ClassVisitor; import org.objectweb.asm.FieldVisitor; import org.objectweb.asm.MethodVisitor; import java.util.Arrays; import java.util.Formatter; public class ClassListMemberVisitor extends ClassVisitor { private final Formatter fm = new Formatter(); public ClassListMemberVisitor(int api) { super(api); } public ClassListMemberVisitor(int api, ClassVisitor classVisitor) { super(api, classVisitor); } @Override public void visit(int version, int access, String name, String signature, String superName, String[] interfaces) { fm.format("%s extends %s implements %s {%n", name, superName, Arrays.toString(interfaces)); } @Override public FieldVisitor visitField(int access, String name, String descriptor, String signature, Object value) { fm.format(" %s: %s%n", name, descriptor); return null; } @Override public MethodVisitor visitMethod(int access, String name, String descriptor, String signature, String[] exceptions) { fm.format(" %s: %s%n", name, descriptor); return null; } @Override public void visitEnd() { fm.format("}"); } public String getText() { return fm.toString(); } } ================================================ FILE: src/main/java/lsieun/asm/core/ClassRemoveAttributeVisitor.java ================================================ package lsieun.asm.core; import org.objectweb.asm.ClassVisitor; public class ClassRemoveAttributeVisitor extends ClassVisitor { public ClassRemoveAttributeVisitor(int api, ClassVisitor cv) { super(api, cv); } @Override public void visitSource(String source, String debug) { // do nothing } @Override public void visitOuterClass(String owner, String name, String descriptor) { // do nothing } @Override public void visitInnerClass(String name, String outerName, String innerName, int access) { // do nothing } } ================================================ FILE: src/main/java/lsieun/asm/core/MethodAroundVisitor.java ================================================ package lsieun.asm.core; import org.objectweb.asm.ClassVisitor; import org.objectweb.asm.MethodVisitor; import org.objectweb.asm.Opcodes; import static org.objectweb.asm.Opcodes.*; public class MethodAroundVisitor extends ClassVisitor { public MethodAroundVisitor(int api, ClassVisitor classVisitor) { super(api, classVisitor); } @Override public MethodVisitor visitMethod(int access, String name, String descriptor, String signature, String[] exceptions) { MethodVisitor mv = super.visitMethod(access, name, descriptor, signature, exceptions); if (mv != null && !"".equals(name)) { boolean isAbstractMethod = (access & Opcodes.ACC_ABSTRACT) == Opcodes.ACC_ABSTRACT; boolean isNativeMethod = (access & Opcodes.ACC_NATIVE) == Opcodes.ACC_NATIVE; if (!isAbstractMethod && !isNativeMethod) { mv = new MethodAroundAdapter(api, mv); } } return mv; } private static class MethodAroundAdapter extends MethodVisitor { public MethodAroundAdapter(int api, MethodVisitor methodVisitor) { super(api, methodVisitor); } @Override public void visitCode() { // 首先,处理自己的代码逻辑 super.visitFieldInsn(Opcodes.GETSTATIC, "java/lang/System", "out", "Ljava/io/PrintStream;"); super.visitLdcInsn("Method Enter..."); super.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "java/io/PrintStream", "println", "(Ljava/lang/String;)V", false); // 其次,调用父类的方法实现 super.visitCode(); } @Override public void visitInsn(int opcode) { // 首先,处理自己的代码逻辑 if (opcode == ATHROW || (opcode >= IRETURN && opcode <= RETURN)) { super.visitFieldInsn(Opcodes.GETSTATIC, "java/lang/System", "out", "Ljava/io/PrintStream;"); super.visitLdcInsn("Method Exit..."); super.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "java/io/PrintStream", "println", "(Ljava/lang/String;)V", false); } // 其次,调用父类的方法实现 super.visitInsn(opcode); } } } ================================================ FILE: src/main/java/lsieun/asm/core/MethodAroundVisitor2.java ================================================ package lsieun.asm.core; import org.objectweb.asm.ClassVisitor; import org.objectweb.asm.MethodVisitor; import org.objectweb.asm.Opcodes; import static org.objectweb.asm.Opcodes.*; public class MethodAroundVisitor2 extends ClassVisitor { public MethodAroundVisitor2(int api, ClassVisitor classVisitor) { super(api, classVisitor); } @Override public MethodVisitor visitMethod(int access, String name, String descriptor, String signature, String[] exceptions) { MethodVisitor mv = super.visitMethod(access, name, descriptor, signature, exceptions); if (mv != null && !"".equals(name)) { boolean isAbstractMethod = (access & Opcodes.ACC_ABSTRACT) == Opcodes.ACC_ABSTRACT; boolean isNativeMethod = (access & Opcodes.ACC_NATIVE) == Opcodes.ACC_NATIVE; if (!isAbstractMethod && !isNativeMethod) { mv = new MethodAroundAdapter(api, mv); } } return mv; } private static class MethodAroundAdapter extends MethodVisitor { public MethodAroundAdapter(int api, MethodVisitor methodVisitor) { super(api, methodVisitor); } @Override public void visitCode() { // 首先,处理自己的代码逻辑 super.visitFieldInsn(Opcodes.GETSTATIC, "java/lang/System", "out", "Ljava/io/PrintStream;"); super.visitLdcInsn("Method Enter222"); super.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "java/io/PrintStream", "println", "(Ljava/lang/String;)V", false); // 其次,调用父类的方法实现 super.visitCode(); } @Override public void visitInsn(int opcode) { // 首先,处理自己的代码逻辑 if (opcode == ATHROW || (opcode >= IRETURN && opcode <= RETURN)) { super.visitFieldInsn(Opcodes.GETSTATIC, "java/lang/System", "out", "Ljava/io/PrintStream;"); super.visitLdcInsn("Method Exit222"); super.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "java/io/PrintStream", "println", "(Ljava/lang/String;)V", false); } // 其次,调用父类的方法实现 super.visitInsn(opcode); } } } ================================================ FILE: src/main/java/lsieun/asm/core/MethodEmptyBodyVisitor.java ================================================ package lsieun.asm.core; import org.objectweb.asm.ClassVisitor; import org.objectweb.asm.MethodVisitor; import org.objectweb.asm.Opcodes; import org.objectweb.asm.Type; import static org.objectweb.asm.Opcodes.*; public class MethodEmptyBodyVisitor extends ClassVisitor { private String owner; private final String methodName; private final String methodDesc; public MethodEmptyBodyVisitor(int api, ClassVisitor classVisitor, String methodName, String methodDesc) { super(api, classVisitor); this.methodName = methodName; this.methodDesc = methodDesc; } @Override public void visit(int version, int access, String name, String signature, String superName, String[] interfaces) { super.visit(version, access, name, signature, superName, interfaces); this.owner = name; } @Override public MethodVisitor visitMethod(int access, String name, String descriptor, String signature, String[] exceptions) { MethodVisitor mv = super.visitMethod(access, name, descriptor, signature, exceptions); if (mv != null && methodName.equals(name) && methodDesc.equals(descriptor)) { boolean isAbstractMethod = (access & ACC_ABSTRACT) != 0; boolean isNativeMethod = (access & ACC_NATIVE) != 0; if (!isAbstractMethod && !isNativeMethod) { generateNewBody(mv, owner, access, name, descriptor); return null; } } return mv; } protected void generateNewBody(MethodVisitor mv, String owner, int methodAccess, String methodName, String methodDesc) { // (1) method argument types and return type Type t = Type.getType(methodDesc); Type[] argumentTypes = t.getArgumentTypes(); Type returnType = t.getReturnType(); // (2) compute the size of local variable and operand stack boolean isStaticMethod = ((methodAccess & Opcodes.ACC_STATIC) != 0); int localSize = isStaticMethod ? 0 : 1; for (Type argType : argumentTypes) { localSize += argType.getSize(); } int stackSize = returnType.getSize(); // (3) method body mv.visitCode(); if (returnType.getSort() == Type.VOID) { mv.visitInsn(RETURN); } else if (returnType.getSort() >= Type.BOOLEAN && returnType.getSort() <= Type.INT) { mv.visitInsn(ICONST_1); mv.visitInsn(IRETURN); } else if (returnType.getSort() == Type.LONG) { mv.visitInsn(LCONST_0); mv.visitInsn(LRETURN); } else if (returnType.getSort() == Type.FLOAT) { mv.visitInsn(FCONST_0); mv.visitInsn(FRETURN); } else if (returnType.getSort() == Type.DOUBLE) { mv.visitInsn(DCONST_0); mv.visitInsn(DRETURN); } else { mv.visitInsn(ACONST_NULL); mv.visitInsn(ARETURN); } mv.visitMaxs(stackSize, localSize); mv.visitEnd(); } } ================================================ FILE: src/main/java/lsieun/asm/core/MethodEnterVisitor.java ================================================ package lsieun.asm.core; import org.objectweb.asm.ClassVisitor; import org.objectweb.asm.Label; import org.objectweb.asm.MethodVisitor; import org.objectweb.asm.Opcodes; import static org.objectweb.asm.Opcodes.*; public class MethodEnterVisitor extends ClassVisitor { public MethodEnterVisitor(int api, ClassVisitor classVisitor) { super(api, classVisitor); } @Override public MethodVisitor visitMethod(int access, String name, String descriptor, String signature, String[] exceptions) { MethodVisitor mv = super.visitMethod(access, name, descriptor, signature, exceptions); if (mv != null && !"".equals(name)) { mv = new MethodEnterAdapter(api, mv); } return mv; } private static class MethodEnterAdapter extends MethodVisitor { public MethodEnterAdapter(int api, MethodVisitor methodVisitor) { super(api, methodVisitor); } @Override public void visitCode() { // 首先,处理自己的代码逻辑 super.visitFieldInsn(Opcodes.GETSTATIC, "java/lang/System", "out", "Ljava/io/PrintStream;"); super.visitLdcInsn("Method Enter..."); super.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "java/io/PrintStream", "println", "(Ljava/lang/String;)V", false); // 其次,调用父类的方法实现 super.visitCode(); } } } ================================================ FILE: src/main/java/lsieun/asm/core/MethodExitVisitor.java ================================================ package lsieun.asm.core; import org.objectweb.asm.ClassVisitor; import org.objectweb.asm.MethodVisitor; import org.objectweb.asm.Opcodes; public class MethodExitVisitor extends ClassVisitor { public MethodExitVisitor(int api, ClassVisitor classVisitor) { super(api, classVisitor); } @Override public MethodVisitor visitMethod(int access, String name, String descriptor, String signature, String[] exceptions) { MethodVisitor mv = super.visitMethod(access, name, descriptor, signature, exceptions); if (mv != null && !"".equals(name)) { mv = new MethodExitAdapter(api, mv); } return mv; } private static class MethodExitAdapter extends MethodVisitor { public MethodExitAdapter(int api, MethodVisitor methodVisitor) { super(api, methodVisitor); } @Override public void visitInsn(int opcode) { // 首先,处理自己的代码逻辑 if (opcode == Opcodes.ATHROW || (opcode >= Opcodes.IRETURN && opcode <= Opcodes.RETURN)) { super.visitFieldInsn(Opcodes.GETSTATIC, "java/lang/System", "out", "Ljava/io/PrintStream;"); super.visitLdcInsn("Method Exit..."); super.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "java/io/PrintStream", "println", "(Ljava/lang/String;)V", false); } // 其次,调用父类的方法实现 super.visitInsn(opcode); } } } ================================================ FILE: src/main/java/lsieun/asm/core/MethodFindInvokeVisitor.java ================================================ package lsieun.asm.core; import org.objectweb.asm.ClassVisitor; import org.objectweb.asm.MethodVisitor; import org.objectweb.asm.util.Printer; import java.util.ArrayList; import java.util.List; public class MethodFindInvokeVisitor extends ClassVisitor { private final String methodName; private final String methodDesc; public MethodFindInvokeVisitor(int api, ClassVisitor classVisitor, String methodName, String methodDesc) { super(api, classVisitor); this.methodName = methodName; this.methodDesc = methodDesc; } @Override public MethodVisitor visitMethod(int access, String name, String descriptor, String signature, String[] exceptions) { if (methodName.equals(name) && methodDesc.equals(descriptor)) { return new MethodFindInvokeAdapter(api, null); } return null; } private static class MethodFindInvokeAdapter extends MethodVisitor { private final List list = new ArrayList<>(); public MethodFindInvokeAdapter(int api, MethodVisitor methodVisitor) { super(api, methodVisitor); } @Override public void visitMethodInsn(int opcode, String owner, String name, String descriptor, boolean isInterface) { // 首先,处理自己的代码逻辑 String info = String.format("%s %s.%s%s", Printer.OPCODES[opcode], owner, name, descriptor); if (!list.contains(info)) { list.add(info); } // 其次,调用父类的方法实现 super.visitMethodInsn(opcode, owner, name, descriptor, isInterface); } @Override public void visitEnd() { // 首先,处理自己的代码逻辑 for (String item : list) { System.out.println(item); } // 其次,调用父类的方法实现 super.visitEnd(); } } } ================================================ FILE: src/main/java/lsieun/asm/core/MethodFindRefVisitor.java ================================================ package lsieun.asm.core; import org.objectweb.asm.ClassVisitor; import org.objectweb.asm.MethodVisitor; import java.util.ArrayList; import java.util.List; import static org.objectweb.asm.Opcodes.ACC_ABSTRACT; import static org.objectweb.asm.Opcodes.ACC_NATIVE; public class MethodFindRefVisitor extends ClassVisitor { private final String methodOwner; private final String methodName; private final String methodDesc; private String owner; private final List resultList = new ArrayList<>(); public MethodFindRefVisitor(int api, ClassVisitor classVisitor, String methodOwner, String methodName, String methodDesc) { super(api, classVisitor); this.methodOwner = methodOwner; this.methodName = methodName; this.methodDesc = methodDesc; } @Override public void visit(int version, int access, String name, String signature, String superName, String[] interfaces) { super.visit(version, access, name, signature, superName, interfaces); this.owner = name; } @Override public MethodVisitor visitMethod(int access, String name, String descriptor, String signature, String[] exceptions) { boolean isAbstractMethod = (access & ACC_ABSTRACT) != 0; boolean isNativeMethod = (access & ACC_NATIVE) != 0; if (!isAbstractMethod && !isNativeMethod) { return new MethodFindRefAdaptor(api, null, owner, name, descriptor); } return null; } @Override public void visitEnd() { // 首先,处理自己的代码逻辑 for (String item : resultList) { System.out.println(item); } // 其次,调用父类的方法实现 super.visitEnd(); } private class MethodFindRefAdaptor extends MethodVisitor { private final String currentMethodOwner; private final String currentMethodName; private final String currentMethodDesc; public MethodFindRefAdaptor(int api, MethodVisitor methodVisitor, String currentMethodOwner, String currentMethodName, String currentMethodDesc) { super(api, methodVisitor); this.currentMethodOwner = currentMethodOwner; this.currentMethodName = currentMethodName; this.currentMethodDesc = currentMethodDesc; } @Override public void visitMethodInsn(int opcode, String owner, String name, String descriptor, boolean isInterface) { // 首先,处理自己的代码逻辑 if (methodOwner.equals(owner) && methodName.equals(name) && methodDesc.equals(descriptor)) { String info = String.format("%s.%s%s", currentMethodOwner, currentMethodName, currentMethodDesc); if (!resultList.contains(info)) { resultList.add(info); } } // 其次,调用父类的方法实现 super.visitMethodInsn(opcode, owner, name, descriptor, isInterface); } } } ================================================ FILE: src/main/java/lsieun/asm/core/MethodParameterVisitor.java ================================================ package lsieun.asm.core; import org.objectweb.asm.ClassVisitor; import org.objectweb.asm.MethodVisitor; import org.objectweb.asm.Opcodes; import org.objectweb.asm.Type; import static org.objectweb.asm.Opcodes.*; public class MethodParameterVisitor extends ClassVisitor { public MethodParameterVisitor(int api, ClassVisitor classVisitor) { super(api, classVisitor); } @Override public MethodVisitor visitMethod(int access, String name, String descriptor, String signature, String[] exceptions) { MethodVisitor mv = super.visitMethod(access, name, descriptor, signature, exceptions); if (mv != null && !name.equals("")) { boolean isAbstractMethod = (access & ACC_ABSTRACT) != 0; boolean isNativeMethod = (access & ACC_NATIVE) != 0; if (!isAbstractMethod && !isNativeMethod) { mv = new MethodParameterAdapter(api, mv, access, name, descriptor); } } return mv; } private static class MethodParameterAdapter extends MethodVisitor { private final int methodAccess; private final String methodName; private final String methodDesc; public MethodParameterAdapter(int api, MethodVisitor mv, int methodAccess, String methodName, String methodDesc) { super(api, mv); this.methodAccess = methodAccess; this.methodName = methodName; this.methodDesc = methodDesc; } @Override public void visitCode() { // 首先,处理自己的代码逻辑 boolean isStatic = ((methodAccess & ACC_STATIC) != 0); int slotIndex = isStatic ? 0 : 1; printMessage("Method Enter: " + methodName + methodDesc); Type methodType = Type.getMethodType(methodDesc); Type[] argumentTypes = methodType.getArgumentTypes(); for (Type t : argumentTypes) { int sort = t.getSort(); int size = t.getSize(); String descriptor = t.getDescriptor(); int opcode = t.getOpcode(ILOAD); super.visitVarInsn(opcode, slotIndex); if (sort == Type.BOOLEAN) { printBoolean(); } else if (sort == Type.CHAR) { printChar(); } else if (sort == Type.BYTE || sort == Type.SHORT || sort == Type.INT) { printInt(); } else if (sort == Type.FLOAT) { printFloat(); } else if (sort == Type.LONG) { printLong(); } else if (sort == Type.DOUBLE) { printDouble(); } else if (sort == Type.OBJECT && "Ljava/lang/String;".equals(descriptor)) { printString(); } else if (sort == Type.OBJECT) { printObject(); } else { printMessage("No Support"); if (size == 1) { super.visitInsn(Opcodes.POP); } else { super.visitInsn(Opcodes.POP2); } } slotIndex += size; } // 其次,调用父类的方法实现 super.visitCode(); } @Override public void visitInsn(int opcode) { // 首先,处理自己的代码逻辑 if ((opcode >= IRETURN && opcode <= RETURN) || opcode == ATHROW) { printMessage("Method Exit:"); if (opcode == IRETURN) { super.visitInsn(DUP); Type methodType = Type.getMethodType(methodDesc); Type returnType = methodType.getReturnType(); if (returnType == Type.BOOLEAN_TYPE) { printBoolean(); } else if (returnType == Type.CHAR_TYPE) { printChar(); } else { printInt(); } } else if (opcode == FRETURN) { super.visitInsn(DUP); printFloat(); } else if (opcode == LRETURN) { super.visitInsn(DUP2); printLong(); } else if (opcode == DRETURN) { super.visitInsn(DUP2); printDouble(); } else if (opcode == ARETURN) { super.visitInsn(DUP); printObject(); } else if (opcode == RETURN) { printMessage(" return void"); } else { printMessage(" abnormal return"); } } // 其次,调用父类的方法实现 super.visitInsn(opcode); } private void printBoolean() { super.visitFieldInsn(GETSTATIC, "java/lang/System", "out", "Ljava/io/PrintStream;"); super.visitInsn(SWAP); super.visitMethodInsn(INVOKEVIRTUAL, "java/io/PrintStream", "println", "(Z)V", false); } private void printChar() { super.visitFieldInsn(GETSTATIC, "java/lang/System", "out", "Ljava/io/PrintStream;"); super.visitInsn(SWAP); super.visitMethodInsn(INVOKEVIRTUAL, "java/io/PrintStream", "println", "(C)V", false); } private void printInt() { super.visitFieldInsn(GETSTATIC, "java/lang/System", "out", "Ljava/io/PrintStream;"); super.visitInsn(SWAP); super.visitMethodInsn(INVOKEVIRTUAL, "java/io/PrintStream", "println", "(I)V", false); } private void printFloat() { super.visitFieldInsn(GETSTATIC, "java/lang/System", "out", "Ljava/io/PrintStream;"); super.visitInsn(SWAP); super.visitMethodInsn(INVOKEVIRTUAL, "java/io/PrintStream", "println", "(F)V", false); } private void printLong() { super.visitFieldInsn(GETSTATIC, "java/lang/System", "out", "Ljava/io/PrintStream;"); super.visitInsn(DUP_X2); super.visitInsn(POP); super.visitMethodInsn(INVOKEVIRTUAL, "java/io/PrintStream", "println", "(J)V", false); } private void printDouble() { super.visitFieldInsn(GETSTATIC, "java/lang/System", "out", "Ljava/io/PrintStream;"); super.visitInsn(DUP_X2); super.visitInsn(POP); super.visitMethodInsn(INVOKEVIRTUAL, "java/io/PrintStream", "println", "(D)V", false); } private void printString() { super.visitFieldInsn(GETSTATIC, "java/lang/System", "out", "Ljava/io/PrintStream;"); super.visitInsn(SWAP); super.visitMethodInsn(INVOKEVIRTUAL, "java/io/PrintStream", "println", "(Ljava/lang/String;)V", false); } private void printObject() { super.visitFieldInsn(GETSTATIC, "java/lang/System", "out", "Ljava/io/PrintStream;"); super.visitInsn(SWAP); super.visitMethodInsn(INVOKEVIRTUAL, "java/io/PrintStream", "println", "(Ljava/lang/Object;)V", false); } private void printMessage(String str) { super.visitFieldInsn(GETSTATIC, "java/lang/System", "out", "Ljava/io/PrintStream;"); super.visitLdcInsn(str); super.visitMethodInsn(INVOKEVIRTUAL, "java/io/PrintStream", "println", "(Ljava/lang/String;)V", false); } } } ================================================ FILE: src/main/java/lsieun/asm/core/MethodParameterVisitor2.java ================================================ package lsieun.asm.core; import org.objectweb.asm.ClassVisitor; import org.objectweb.asm.MethodVisitor; import org.objectweb.asm.Type; import static org.objectweb.asm.Opcodes.*; public class MethodParameterVisitor2 extends ClassVisitor { public MethodParameterVisitor2(int api, ClassVisitor classVisitor) { super(api, classVisitor); } @Override public MethodVisitor visitMethod(int access, String name, String descriptor, String signature, String[] exceptions) { MethodVisitor mv = super.visitMethod(access, name, descriptor, signature, exceptions); if (mv != null && !name.equals("")) { boolean isAbstractMethod = (access & ACC_ABSTRACT) != 0; boolean isNativeMethod = (access & ACC_NATIVE) != 0; if (!isAbstractMethod && !isNativeMethod) { mv = new MethodParameterAdapter2(api, mv, access, name, descriptor); } } return mv; } private static class MethodParameterAdapter2 extends MethodVisitor { private final int methodAccess; private final String methodName; private final String methodDesc; public MethodParameterAdapter2(int api, MethodVisitor mv, int methodAccess, String methodName, String methodDesc) { super(api, mv); this.methodAccess = methodAccess; this.methodName = methodName; this.methodDesc = methodDesc; } @Override public void visitCode() { // 首先,处理自己的代码逻辑 boolean isStatic = ((methodAccess & ACC_STATIC) != 0); int slotIndex = isStatic ? 0 : 1; printMessage("Method Enter: " + methodName + methodDesc); Type methodType = Type.getMethodType(methodDesc); Type[] argumentTypes = methodType.getArgumentTypes(); for (Type t : argumentTypes) { int sort = t.getSort(); int size = t.getSize(); String descriptor = t.getDescriptor(); int opcode = t.getOpcode(ILOAD); super.visitVarInsn(opcode, slotIndex); if (sort >= Type.BOOLEAN && sort <= Type.DOUBLE) { String methodDesc = String.format("(%s)V", descriptor); printValueOnStack(methodDesc); } else { printValueOnStack("(Ljava/lang/Object;)V"); } slotIndex += size; } // 其次,调用父类的方法实现 super.visitCode(); } @Override public void visitInsn(int opcode) { // 首先,处理自己的代码逻辑 if ((opcode >= IRETURN && opcode <= RETURN) || opcode == ATHROW) { printMessage("Method Exit: " + methodName + methodDesc); if (opcode >= IRETURN && opcode <= DRETURN) { Type methodType = Type.getMethodType(methodDesc); Type returnType = methodType.getReturnType(); int size = returnType.getSize(); String descriptor = returnType.getDescriptor(); if (size == 1) { super.visitInsn(DUP); } else { super.visitInsn(DUP2); } String methodDesc = String.format("(%s)V", descriptor); printValueOnStack(methodDesc); } else if (opcode == ARETURN) { super.visitInsn(DUP); printValueOnStack("(Ljava/lang/Object;)V"); } else if (opcode == RETURN) { printMessage(" return void"); } else { printMessage(" abnormal return"); } } // 其次,调用父类的方法实现 super.visitInsn(opcode); } private void printMessage(String str) { super.visitLdcInsn(str); super.visitMethodInsn(INVOKESTATIC, "sample/ParameterUtils", "printText", "(Ljava/lang/String;)V", false); } private void printValueOnStack(String descriptor) { super.visitMethodInsn(INVOKESTATIC, "sample/ParameterUtils", "printValueOnStack", descriptor, false); } } } ================================================ FILE: src/main/java/lsieun/asm/core/MethodPatternAdapter.java ================================================ package lsieun.asm.core; import org.objectweb.asm.Handle; import org.objectweb.asm.Label; import org.objectweb.asm.MethodVisitor; public abstract class MethodPatternAdapter extends MethodVisitor { protected final static int SEEN_NOTHING = 0; protected int state; public MethodPatternAdapter(int api, MethodVisitor methodVisitor) { super(api, methodVisitor); } @Override public void visitInsn(int opcode) { visitInsn(); super.visitInsn(opcode); } @Override public void visitIntInsn(int opcode, int operand) { visitInsn(); super.visitIntInsn(opcode, operand); } @Override public void visitVarInsn(int opcode, int var) { visitInsn(); super.visitVarInsn(opcode, var); } @Override public void visitTypeInsn(int opcode, String type) { visitInsn(); super.visitTypeInsn(opcode, type); } @Override public void visitFieldInsn(int opcode, String owner, String name, String descriptor) { visitInsn(); super.visitFieldInsn(opcode, owner, name, descriptor); } @Override public void visitMethodInsn(int opcode, String owner, String name, String descriptor) { visitInsn(); super.visitMethodInsn(opcode, owner, name, descriptor); } @Override public void visitMethodInsn(int opcode, String owner, String name, String descriptor, boolean isInterface) { visitInsn(); super.visitMethodInsn(opcode, owner, name, descriptor, isInterface); } @Override public void visitInvokeDynamicInsn(String name, String descriptor, Handle bootstrapMethodHandle, Object... bootstrapMethodArguments) { visitInsn(); super.visitInvokeDynamicInsn(name, descriptor, bootstrapMethodHandle, bootstrapMethodArguments); } @Override public void visitJumpInsn(int opcode, Label label) { visitInsn(); super.visitJumpInsn(opcode, label); } @Override public void visitLdcInsn(Object value) { visitInsn(); super.visitLdcInsn(value); } @Override public void visitIincInsn(int var, int increment) { visitInsn(); super.visitIincInsn(var, increment); } @Override public void visitTableSwitchInsn(int min, int max, Label dflt, Label... labels) { visitInsn(); super.visitTableSwitchInsn(min, max, dflt, labels); } @Override public void visitLookupSwitchInsn(Label dflt, int[] keys, Label[] labels) { visitInsn(); super.visitLookupSwitchInsn(dflt, keys, labels); } @Override public void visitMultiANewArrayInsn(String descriptor, int numDimensions) { visitInsn(); super.visitMultiANewArrayInsn(descriptor, numDimensions); } @Override public void visitTryCatchBlock(Label start, Label end, Label handler, String type) { visitInsn(); super.visitTryCatchBlock(start, end, handler, type); } @Override public void visitLabel(Label label) { visitInsn(); super.visitLabel(label); } @Override public void visitFrame(int type, int numLocal, Object[] local, int numStack, Object[] stack) { visitInsn(); super.visitFrame(type, numLocal, local, numStack, stack); } @Override public void visitMaxs(int maxStack, int maxLocals) { visitInsn(); super.visitMaxs(maxStack, maxLocals); } protected abstract void visitInsn(); } ================================================ FILE: src/main/java/lsieun/asm/core/MethodRemoveAddZeroVisitor.java ================================================ package lsieun.asm.core; import org.objectweb.asm.ClassVisitor; import org.objectweb.asm.MethodVisitor; import static org.objectweb.asm.Opcodes.*; public class MethodRemoveAddZeroVisitor extends ClassVisitor { public MethodRemoveAddZeroVisitor(int api, ClassVisitor classVisitor) { super(api, classVisitor); } @Override public MethodVisitor visitMethod(int access, String name, String descriptor, String signature, String[] exceptions) { MethodVisitor mv = cv.visitMethod(access, name, descriptor, signature, exceptions); if (mv != null && !"".equals(name) && !"".equals(name)) { boolean isAbstractMethod = (access & ACC_ABSTRACT) != 0; boolean isNativeMethod = (access & ACC_NATIVE) != 0; if (!isAbstractMethod && !isNativeMethod) { mv = new MethodRemoveAddZeroAdapter(api, mv); } } return mv; } private static class MethodRemoveAddZeroAdapter extends MethodPatternAdapter { private static final int SEEN_ICONST_0 = 1; public MethodRemoveAddZeroAdapter(int api, MethodVisitor methodVisitor) { super(api, methodVisitor); } @Override public void visitInsn(int opcode) { // 第一,对于感兴趣的状态进行处理 switch (state) { case SEEN_NOTHING: if (opcode == ICONST_0) { state = SEEN_ICONST_0; return; } break; case SEEN_ICONST_0: if (opcode == IADD) { state = SEEN_NOTHING; return; } else if (opcode == ICONST_0) { mv.visitInsn(ICONST_0); return; } break; } // 第二,对于不感兴趣的状态,交给父类进行处理 super.visitInsn(opcode); } @Override protected void visitInsn() { if (state == SEEN_ICONST_0) { mv.visitInsn(ICONST_0); } state = SEEN_NOTHING; } } } ================================================ FILE: src/main/java/lsieun/asm/core/MethodRemoveGetFieldPutFieldVisitor.java ================================================ package lsieun.asm.core; import org.objectweb.asm.ClassVisitor; import org.objectweb.asm.MethodVisitor; import static org.objectweb.asm.Opcodes.*; public class MethodRemoveGetFieldPutFieldVisitor extends ClassVisitor { public MethodRemoveGetFieldPutFieldVisitor(int api, ClassVisitor classVisitor) { super(api, classVisitor); } @Override public MethodVisitor visitMethod(int access, String name, String descriptor, String signature, String[] exceptions) { MethodVisitor mv = cv.visitMethod(access, name, descriptor, signature, exceptions); if (mv != null && !"".equals(name) && !"".equals(name)) { boolean isAbstractMethod = (access & ACC_ABSTRACT) != 0; boolean isNativeMethod = (access & ACC_NATIVE) != 0; if (!isAbstractMethod && !isNativeMethod) { mv = new MethodRemoveGetFieldPutFieldAdapter(api, mv); } } return mv; } private class MethodRemoveGetFieldPutFieldAdapter extends MethodPatternAdapter { private final static int SEEN_ALOAD_0 = 1; private final static int SEEN_ALOAD_0_ALOAD_0 = 2; private final static int SEEN_ALOAD_0_ALOAD_0_GETFIELD = 3; private String fieldOwner; private String fieldName; private String fieldDesc; public MethodRemoveGetFieldPutFieldAdapter(int api, MethodVisitor methodVisitor) { super(api, methodVisitor); } @Override public void visitVarInsn(int opcode, int var) { // 第一,对于感兴趣的状态进行处理 switch (state) { case SEEN_NOTHING: if (opcode == ALOAD && var == 0) { state = SEEN_ALOAD_0; return; } break; case SEEN_ALOAD_0: if (opcode == ALOAD && var == 0) { state = SEEN_ALOAD_0_ALOAD_0; return; } break; case SEEN_ALOAD_0_ALOAD_0: if (opcode == ALOAD && var == 0) { mv.visitVarInsn(opcode, var); return; } break; } // 第二,对于不感兴趣的状态,交给父类进行处理 super.visitVarInsn(opcode, var); } @Override public void visitFieldInsn(int opcode, String owner, String name, String descriptor) { // 第一,对于感兴趣的状态进行处理 switch (state) { case SEEN_ALOAD_0_ALOAD_0: if (opcode == GETFIELD) { state = SEEN_ALOAD_0_ALOAD_0_GETFIELD; fieldOwner = owner; fieldName = name; fieldDesc = descriptor; return; } break; case SEEN_ALOAD_0_ALOAD_0_GETFIELD: if (opcode == PUTFIELD && name.equals(fieldName)) { state = SEEN_NOTHING; return; } break; } // 第二,对于不感兴趣的状态,交给父类进行处理 super.visitFieldInsn(opcode, owner, name, descriptor); } @Override protected void visitInsn() { switch (state) { case SEEN_ALOAD_0: mv.visitVarInsn(ALOAD, 0); break; case SEEN_ALOAD_0_ALOAD_0: mv.visitVarInsn(ALOAD, 0); mv.visitVarInsn(ALOAD, 0); break; case SEEN_ALOAD_0_ALOAD_0_GETFIELD: mv.visitVarInsn(ALOAD, 0); mv.visitVarInsn(ALOAD, 0); mv.visitFieldInsn(GETFIELD, fieldOwner, fieldName, fieldDesc); break; } state = SEEN_NOTHING; } } } ================================================ FILE: src/main/java/lsieun/asm/core/MethodRemoveNopVisitor.java ================================================ package lsieun.asm.core; import org.objectweb.asm.ClassVisitor; import org.objectweb.asm.MethodVisitor; import static org.objectweb.asm.Opcodes.*; public class MethodRemoveNopVisitor extends ClassVisitor { public MethodRemoveNopVisitor(int api, ClassVisitor classVisitor) { super(api, classVisitor); } @Override public MethodVisitor visitMethod(int access, String name, String descriptor, String signature, String[] exceptions) { MethodVisitor mv = super.visitMethod(access, name, descriptor, signature, exceptions); if (mv != null && !"".equals(name) && !"".equals(name)) { boolean isAbstractMethod = (access & ACC_ABSTRACT) != 0; boolean isNativeMethod = (access & ACC_NATIVE) != 0; if (!isAbstractMethod && !isNativeMethod) { mv = new MethodRemoveNopAdapter(api, mv); } } return mv; } private static class MethodRemoveNopAdapter extends MethodVisitor { public MethodRemoveNopAdapter(int api, MethodVisitor methodVisitor) { super(api, methodVisitor); } @Override public void visitInsn(int opcode) { if (opcode != NOP) { super.visitInsn(opcode); } } } } ================================================ FILE: src/main/java/lsieun/asm/core/MethodRemovePrintVisitor.java ================================================ package lsieun.asm.core; import org.objectweb.asm.ClassVisitor; import org.objectweb.asm.MethodVisitor; import static org.objectweb.asm.Opcodes.*; public class MethodRemovePrintVisitor extends ClassVisitor { public MethodRemovePrintVisitor(int api, ClassVisitor classVisitor) { super(api, classVisitor); } @Override public MethodVisitor visitMethod(int access, String name, String descriptor, String signature, String[] exceptions) { MethodVisitor mv = cv.visitMethod(access, name, descriptor, signature, exceptions); if (mv != null && !"".equals(name) && !"".equals(name)) { boolean isAbstractMethod = (access & ACC_ABSTRACT) != 0; boolean isNativeMethod = (access & ACC_NATIVE) != 0; if (!isAbstractMethod && !isNativeMethod) { mv = new MethodRemovePrintAdaptor(api, mv); } } return mv; } private class MethodRemovePrintAdaptor extends MethodPatternAdapter { private static final int SEEN_GETSTATIC = 1; private static final int SEEN_GETSTATIC_LDC = 2; private String message; public MethodRemovePrintAdaptor(int api, MethodVisitor methodVisitor) { super(api, methodVisitor); } @Override public void visitFieldInsn(int opcode, String owner, String name, String descriptor) { // 第一,对于感兴趣的状态进行处理 boolean flag = (opcode == GETSTATIC && owner.equals("java/lang/System") && name.equals("out") && descriptor.equals("Ljava/io/PrintStream;")); switch (state) { case SEEN_NOTHING: if (flag) { state = SEEN_GETSTATIC; return; } break; case SEEN_GETSTATIC: if (flag) { mv.visitFieldInsn(GETSTATIC, "java/lang/System", "out", "Ljava/io/PrintStream;"); return; } } // 第二,对于不感兴趣的状态,交给父类进行处理 super.visitFieldInsn(opcode, owner, name, descriptor); } @Override public void visitLdcInsn(Object value) { // 第一,对于感兴趣的状态进行处理 switch (state) { case SEEN_GETSTATIC: if (value instanceof String) { state = SEEN_GETSTATIC_LDC; message = (String) value; return; } break; } // 第二,对于不感兴趣的状态,交给父类进行处理 super.visitLdcInsn(value); } @Override public void visitMethodInsn(int opcode, String owner, String name, String descriptor, boolean isInterface) { // 第一,对于感兴趣的状态进行处理 switch (state) { case SEEN_GETSTATIC_LDC: if (opcode == INVOKEVIRTUAL && owner.equals("java/io/PrintStream") && name.equals("println") && descriptor.equals("(Ljava/lang/String;)V")) { state = SEEN_NOTHING; return; } break; } // 第二,对于不感兴趣的状态,交给父类进行处理 super.visitMethodInsn(opcode, owner, name, descriptor, isInterface); } @Override protected void visitInsn() { switch (state) { case SEEN_GETSTATIC: mv.visitFieldInsn(GETSTATIC, "java/lang/System", "out", "Ljava/io/PrintStream;"); break; case SEEN_GETSTATIC_LDC: mv.visitFieldInsn(GETSTATIC, "java/lang/System", "out", "Ljava/io/PrintStream;"); mv.visitLdcInsn(message); break; } state = SEEN_NOTHING; } } } ================================================ FILE: src/main/java/lsieun/asm/core/MethodReplaceInvokeVisitor.java ================================================ package lsieun.asm.core; import org.objectweb.asm.ClassVisitor; import org.objectweb.asm.MethodVisitor; import static org.objectweb.asm.Opcodes.ACC_ABSTRACT; import static org.objectweb.asm.Opcodes.ACC_NATIVE; public class MethodReplaceInvokeVisitor extends ClassVisitor { private final String oldOwner; private final String oldMethodName; private final String oldMethodDesc; private final int newOpcode; private final String newOwner; private final String newMethodName; private final String newMethodDesc; public MethodReplaceInvokeVisitor(int api, ClassVisitor classVisitor, String oldOwner, String oldMethodName, String oldMethodDesc, int newOpcode, String newOwner, String newMethodName, String newMethodDesc) { super(api, classVisitor); this.oldOwner = oldOwner; this.oldMethodName = oldMethodName; this.oldMethodDesc = oldMethodDesc; this.newOpcode = newOpcode; this.newOwner = newOwner; this.newMethodName = newMethodName; this.newMethodDesc = newMethodDesc; } @Override public MethodVisitor visitMethod(int access, String name, String descriptor, String signature, String[] exceptions) { MethodVisitor mv = super.visitMethod(access, name, descriptor, signature, exceptions); if (mv != null && !"".equals(name) && !"".equals(name)) { boolean isAbstractMethod = (access & ACC_ABSTRACT) != 0; boolean isNativeMethod = (access & ACC_NATIVE) != 0; if (!isAbstractMethod && !isNativeMethod) { mv = new MethodReplaceInvokeAdapter(api, mv); } } return mv; } private class MethodReplaceInvokeAdapter extends MethodVisitor { public MethodReplaceInvokeAdapter(int api, MethodVisitor methodVisitor) { super(api, methodVisitor); } @Override public void visitMethodInsn(int opcode, String owner, String name, String descriptor, boolean isInterface) { if (oldOwner.equals(owner) && oldMethodName.equals(name) && oldMethodDesc.equals(descriptor)) { // 注意,最后一个参数是false,会不会太武断呢? super.visitMethodInsn(newOpcode, newOwner, newMethodName, newMethodDesc, false); } else { super.visitMethodInsn(opcode, owner, name, descriptor, isInterface); } } } } ================================================ FILE: src/main/java/lsieun/asm/core/MethodTimerVisitor.java ================================================ package lsieun.asm.core; import org.objectweb.asm.ClassVisitor; import org.objectweb.asm.FieldVisitor; import org.objectweb.asm.MethodVisitor; import static org.objectweb.asm.Opcodes.*; public class MethodTimerVisitor extends ClassVisitor { private String owner; private boolean isInterface; public MethodTimerVisitor(int api, ClassVisitor classVisitor) { super(api, classVisitor); } @Override public void visit(int version, int access, String name, String signature, String superName, String[] interfaces) { super.visit(version, access, name, signature, superName, interfaces); owner = name; isInterface = (access & ACC_INTERFACE) != 0; } @Override public MethodVisitor visitMethod(int access, String name, String descriptor, String signature, String[] exceptions) { MethodVisitor mv = super.visitMethod(access, name, descriptor, signature, exceptions); if (!isInterface && mv != null && !"".equals(name) && !"".equals(name)) { boolean isAbstractMethod = (access & ACC_ABSTRACT) != 0; boolean isNativeMethod = (access & ACC_NATIVE) != 0; if (!isAbstractMethod && !isNativeMethod) { mv = new MethodTimerAdapter(api, mv, owner); } } return mv; } @Override public void visitEnd() { if (!isInterface) { FieldVisitor fv = super.visitField(ACC_PUBLIC | ACC_STATIC, "timer", "J", null, null); if (fv != null) { fv.visitEnd(); } } super.visitEnd(); } private static class MethodTimerAdapter extends MethodVisitor { private final String owner; public MethodTimerAdapter(int api, MethodVisitor mv, String owner) { super(api, mv); this.owner = owner; } @Override public void visitCode() { // 首先,处理自己的代码逻辑 super.visitFieldInsn(GETSTATIC, owner, "timer", "J"); super.visitMethodInsn(INVOKESTATIC, "java/lang/System", "currentTimeMillis", "()J", false); super.visitInsn(LSUB); super.visitFieldInsn(PUTSTATIC, owner, "timer", "J"); // 其次,调用父类的方法实现 super.visitCode(); } @Override public void visitInsn(int opcode) { // 首先,处理自己的代码逻辑 if ((opcode >= IRETURN && opcode <= RETURN) || opcode == ATHROW) { super.visitFieldInsn(GETSTATIC, owner, "timer", "J"); super.visitMethodInsn(INVOKESTATIC, "java/lang/System", "currentTimeMillis", "()J", false); super.visitInsn(LADD); super.visitFieldInsn(PUTSTATIC, owner, "timer", "J"); } // 其次,调用父类的方法实现 super.visitInsn(opcode); } } } ================================================ FILE: src/main/java/lsieun/asm/core/MethodTimerVisitor2.java ================================================ package lsieun.asm.core; import org.objectweb.asm.ClassVisitor; import org.objectweb.asm.FieldVisitor; import org.objectweb.asm.MethodVisitor; import static org.objectweb.asm.Opcodes.*; public class MethodTimerVisitor2 extends ClassVisitor { private String owner; private boolean isInterface; public MethodTimerVisitor2(int api, ClassVisitor classVisitor) { super(api, classVisitor); } @Override public void visit(int version, int access, String name, String signature, String superName, String[] interfaces) { super.visit(version, access, name, signature, superName, interfaces); owner = name; isInterface = (access & ACC_INTERFACE) != 0; } @Override public MethodVisitor visitMethod(int access, String name, String descriptor, String signature, String[] exceptions) { MethodVisitor mv = super.visitMethod(access, name, descriptor, signature, exceptions); if (!isInterface && mv != null && !"".equals(name) && !"".equals(name)) { boolean isAbstractMethod = (access & ACC_ABSTRACT) != 0; boolean isNativeMethod = (access & ACC_NATIVE) != 0; if (!isAbstractMethod && !isNativeMethod) { // 每遇到一个合适的方法,就添加一个相应的字段 FieldVisitor fv = super.visitField(ACC_PUBLIC | ACC_STATIC, getFieldName(name), "J", null, null); if (fv != null) { fv.visitEnd(); } mv = new MethodTimerAdapter2(api, mv, owner, name); } } return mv; } private String getFieldName(String methodName) { return "timer_" + methodName; } private class MethodTimerAdapter2 extends MethodVisitor { private final String owner; private final String methodName; public MethodTimerAdapter2(int api, MethodVisitor mv, String owner, String methodName) { super(api, mv); this.owner = owner; this.methodName = methodName; } @Override public void visitCode() { // 首先,处理自己的代码逻辑 super.visitFieldInsn(GETSTATIC, owner, getFieldName(methodName), "J"); // 注意,字段名字要对应 super.visitMethodInsn(INVOKESTATIC, "java/lang/System", "currentTimeMillis", "()J", false); super.visitInsn(LSUB); super.visitFieldInsn(PUTSTATIC, owner, getFieldName(methodName), "J"); // 注意,字段名字要对应 // 其次,调用父类的方法实现 super.visitCode(); } @Override public void visitInsn(int opcode) { // 首先,处理自己的代码逻辑 if ((opcode >= IRETURN && opcode <= RETURN) || opcode == ATHROW) { super.visitFieldInsn(GETSTATIC, owner, getFieldName(methodName), "J"); // 注意,字段名字要对应 super.visitMethodInsn(INVOKESTATIC, "java/lang/System", "currentTimeMillis", "()J", false); super.visitInsn(LADD); super.visitFieldInsn(PUTSTATIC, owner, getFieldName(methodName), "J"); // 注意,字段名字要对应 } // 其次,调用父类的方法实现 super.visitInsn(opcode); } } } ================================================ FILE: src/main/java/lsieun/asm/core/MethodTimerVisitor3.java ================================================ package lsieun.asm.core; import org.objectweb.asm.ClassVisitor; import org.objectweb.asm.MethodVisitor; import org.objectweb.asm.Type; import org.objectweb.asm.commons.LocalVariablesSorter; import static org.objectweb.asm.Opcodes.*; public class MethodTimerVisitor3 extends ClassVisitor { public MethodTimerVisitor3(int api, ClassVisitor cv) { super(api, cv); } @Override public MethodVisitor visitMethod(int access, String name, String descriptor, String signature, String[] exceptions) { MethodVisitor mv = super.visitMethod(access, name, descriptor, signature, exceptions); if (mv != null && !"".equals(name) && !"".equals(name)) { boolean isAbstractMethod = (access & ACC_ABSTRACT) != 0; boolean isNativeMethod = (access & ACC_NATIVE) != 0; if (!isAbstractMethod && !isNativeMethod) { mv = new MethodTimerAdapter3(api, access, name, descriptor, mv); } } return mv; } private static class MethodTimerAdapter3 extends LocalVariablesSorter { private final String methodName; private final String methodDesc; private int slotIndex; public MethodTimerAdapter3(int api, int access, String name, String descriptor, MethodVisitor methodVisitor) { super(api, access, descriptor, methodVisitor); this.methodName = name; this.methodDesc = descriptor; } @Override public void visitCode() { // 首先,实现自己的逻辑 slotIndex = newLocal(Type.LONG_TYPE); mv.visitMethodInsn(INVOKESTATIC, "java/lang/System", "currentTimeMillis", "()J", false); mv.visitVarInsn(LSTORE, slotIndex); // 其次,调用父类的实现 super.visitCode(); } @Override public void visitInsn(int opcode) { // 首先,实现自己的逻辑 if ((opcode >= IRETURN && opcode <= RETURN) || opcode == ATHROW) { mv.visitMethodInsn(INVOKESTATIC, "java/lang/System", "currentTimeMillis", "()J", false); mv.visitVarInsn(LLOAD, slotIndex); mv.visitInsn(LSUB); mv.visitVarInsn(LSTORE, slotIndex); mv.visitFieldInsn(GETSTATIC, "java/lang/System", "out", "Ljava/io/PrintStream;"); mv.visitTypeInsn(NEW, "java/lang/StringBuilder"); mv.visitInsn(DUP); mv.visitMethodInsn(INVOKESPECIAL, "java/lang/StringBuilder", "", "()V", false); mv.visitLdcInsn(methodName + methodDesc + " method execute: "); mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/StringBuilder", "append", "(Ljava/lang/String;)Ljava/lang/StringBuilder;", false); mv.visitVarInsn(LLOAD, slotIndex); mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/StringBuilder", "append", "(J)Ljava/lang/StringBuilder;", false); mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/StringBuilder", "toString", "()Ljava/lang/String;", false); mv.visitMethodInsn(INVOKEVIRTUAL, "java/io/PrintStream", "println", "(Ljava/lang/String;)V", false); } // 其次,调用父类的实现 super.visitInsn(opcode); } } } ================================================ FILE: src/main/java/lsieun/asm/core/MethodTimerVisitor4.java ================================================ package lsieun.asm.core; import org.objectweb.asm.ClassVisitor; import org.objectweb.asm.MethodVisitor; import org.objectweb.asm.Type; import org.objectweb.asm.commons.AdviceAdapter; import static org.objectweb.asm.Opcodes.*; public class MethodTimerVisitor4 extends ClassVisitor { public MethodTimerVisitor4(int api, ClassVisitor classVisitor) { super(api, classVisitor); } @Override public MethodVisitor visitMethod(int access, String name, String descriptor, String signature, String[] exceptions) { MethodVisitor mv = super.visitMethod(access, name, descriptor, signature, exceptions); if (mv != null) { boolean isAbstractMethod = (access & ACC_ABSTRACT) != 0; boolean isNativeMethod = (access & ACC_NATIVE) != 0; if (!isAbstractMethod && !isNativeMethod) { mv = new MethodTimerAdapter4(api, mv, access, name, descriptor); } } return mv; } private static class MethodTimerAdapter4 extends AdviceAdapter { private int slotIndex; public MethodTimerAdapter4(int api, MethodVisitor mv, int access, String name, String descriptor) { super(api, mv, access, name, descriptor); } @Override protected void onMethodEnter() { slotIndex = newLocal(Type.LONG_TYPE); mv.visitMethodInsn(INVOKESTATIC, "java/lang/System", "currentTimeMillis", "()J", false); mv.visitVarInsn(LSTORE, slotIndex); } @Override protected void onMethodExit(int opcode) { if ((opcode >= IRETURN && opcode <= RETURN) || opcode == ATHROW) { mv.visitMethodInsn(INVOKESTATIC, "java/lang/System", "currentTimeMillis", "()J", false); mv.visitVarInsn(LLOAD, slotIndex); mv.visitInsn(LSUB); mv.visitVarInsn(LSTORE, slotIndex); mv.visitFieldInsn(GETSTATIC, "java/lang/System", "out", "Ljava/io/PrintStream;"); mv.visitTypeInsn(NEW, "java/lang/StringBuilder"); mv.visitInsn(DUP); mv.visitMethodInsn(INVOKESPECIAL, "java/lang/StringBuilder", "", "()V", false); mv.visitLdcInsn(getName() + methodDesc + " method execute: "); mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/StringBuilder", "append", "(Ljava/lang/String;)Ljava/lang/StringBuilder;", false); mv.visitVarInsn(LLOAD, slotIndex); mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/StringBuilder", "append", "(J)Ljava/lang/StringBuilder;", false); mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/StringBuilder", "toString", "()Ljava/lang/String;", false); mv.visitMethodInsn(INVOKEVIRTUAL, "java/io/PrintStream", "println", "(Ljava/lang/String;)V", false); } } } } ================================================ FILE: src/main/java/lsieun/asm/core/SuperPackageAttribute.java ================================================ package lsieun.asm.core; import org.objectweb.asm.*; public class SuperPackageAttribute extends Attribute { public String name; public SuperPackageAttribute() { super("Superpackage"); } public SuperPackageAttribute(String name) { this(); this.name = name; } @Override protected Attribute read(ClassReader classReader, int offset, int length, char[] charBuffer, int codeAttributeOffset, Label[] labels) { String name = classReader.readUTF8(offset, charBuffer); return new SuperPackageAttribute(name); } @Override protected ByteVector write(ClassWriter classWriter, byte[] code, int codeLength, int maxStack, int maxLocals) { int index = classWriter.newUTF8(name); return new ByteVector().putShort(index); } @Override public String toString() { return String.format("%s {name='%s'}", type, name); } } ================================================ FILE: src/main/java/lsieun/asm/core/counter/ClassCounterVisitor.java ================================================ package lsieun.asm.core.counter; import org.objectweb.asm.ClassVisitor; import org.objectweb.asm.FieldVisitor; import org.objectweb.asm.MethodVisitor; import static org.objectweb.asm.Opcodes.*; public class ClassCounterVisitor extends ClassVisitor { private String owner; private boolean isInterface; public ClassCounterVisitor(int api, ClassVisitor cv) { super(api, cv); } @Override public void visit(int version, int access, String name, String signature, String superName, String[] interfaces) { super.visit(version, access, name, signature, superName, interfaces); owner = name; isInterface = (access & ACC_INTERFACE) != 0; } @Override public MethodVisitor visitMethod(int access, String name, String descriptor, String signature, String[] exceptions) { MethodVisitor mv = super.visitMethod(access, name, descriptor, signature, exceptions); if (!isInterface && mv != null && !name.equals("")) { String fieldName = name + "_count"; mv = new MethodCounterAdapter(api, mv, owner, fieldName); FieldVisitor fv = super.visitField(ACC_PUBLIC | ACC_STATIC, fieldName, "I", null, null); if (fv != null) { fv.visitEnd(); } } return mv; } } ================================================ FILE: src/main/java/lsieun/asm/core/counter/MethodCounterAdapter.java ================================================ package lsieun.asm.core.counter; import org.objectweb.asm.MethodVisitor; import static org.objectweb.asm.Opcodes.*; public class MethodCounterAdapter extends MethodVisitor { private final String owner; private final String fieldName; public MethodCounterAdapter(int api, MethodVisitor mv, String owner, String fieldName) { super(api, mv); this.owner = owner; this.fieldName = fieldName; } @Override public void visitCode() { super.visitFieldInsn(GETSTATIC, owner, fieldName, "I"); super.visitInsn(ICONST_1); super.visitInsn(IADD); super.visitFieldInsn(PUTSTATIC, owner, fieldName, "I"); super.visitCode(); } } ================================================ FILE: src/main/java/lsieun/asm/core/empty/EmptyAnnotationVisitor.java ================================================ package lsieun.asm.core.empty; import org.objectweb.asm.AnnotationVisitor; public class EmptyAnnotationVisitor extends AnnotationVisitor { public EmptyAnnotationVisitor(int api, AnnotationVisitor annotationVisitor) { super(api, annotationVisitor); } } ================================================ FILE: src/main/java/lsieun/asm/core/empty/EmptyClassVisitor.java ================================================ package lsieun.asm.core.empty; import org.objectweb.asm.*; public class EmptyClassVisitor extends ClassVisitor { public EmptyClassVisitor(int api, ClassVisitor classVisitor) { super(api, classVisitor); } @Override public ModuleVisitor visitModule(String name, int access, String version) { ModuleVisitor mv = super.visitModule(name, access, version); return new EmptyModuleVisitor(api, mv); } @Override public AnnotationVisitor visitAnnotation(String descriptor, boolean visible) { AnnotationVisitor av = super.visitAnnotation(descriptor, visible); return new EmptyAnnotationVisitor(api, av); } @Override public AnnotationVisitor visitTypeAnnotation(int typeRef, TypePath typePath, String descriptor, boolean visible) { AnnotationVisitor av = super.visitTypeAnnotation(typeRef, typePath, descriptor, visible); return new EmptyAnnotationVisitor(api, av); } @Override public RecordComponentVisitor visitRecordComponent(String name, String descriptor, String signature) { RecordComponentVisitor rcv = super.visitRecordComponent(name, descriptor, signature); return new EmptyRecordComponentVisitor(api, rcv); } @Override public FieldVisitor visitField(int access, String name, String descriptor, String signature, Object value) { FieldVisitor fv = super.visitField(access, name, descriptor, signature, value); return new EmptyFieldVisitor(api, fv); } @Override public MethodVisitor visitMethod(int access, String name, String descriptor, String signature, String[] exceptions) { MethodVisitor mv = super.visitMethod(access, name, descriptor, signature, exceptions); return new EmptyMethodVisitor(api, mv); } } ================================================ FILE: src/main/java/lsieun/asm/core/empty/EmptyFieldVisitor.java ================================================ package lsieun.asm.core.empty; import org.objectweb.asm.FieldVisitor; public class EmptyFieldVisitor extends FieldVisitor { public EmptyFieldVisitor(int api, FieldVisitor fieldVisitor) { super(api, fieldVisitor); } } ================================================ FILE: src/main/java/lsieun/asm/core/empty/EmptyMethodVisitor.java ================================================ package lsieun.asm.core.empty; import org.objectweb.asm.MethodVisitor; public class EmptyMethodVisitor extends MethodVisitor { public EmptyMethodVisitor(int api, MethodVisitor methodVisitor) { super(api, methodVisitor); } } ================================================ FILE: src/main/java/lsieun/asm/core/empty/EmptyModuleVisitor.java ================================================ package lsieun.asm.core.empty; import org.objectweb.asm.ModuleVisitor; public class EmptyModuleVisitor extends ModuleVisitor { public EmptyModuleVisitor(int api, ModuleVisitor moduleVisitor) { super(api, moduleVisitor); } } ================================================ FILE: src/main/java/lsieun/asm/core/empty/EmptyRecordComponentVisitor.java ================================================ package lsieun.asm.core.empty; import org.objectweb.asm.RecordComponentVisitor; public class EmptyRecordComponentVisitor extends RecordComponentVisitor { public EmptyRecordComponentVisitor(int api, RecordComponentVisitor recordComponentVisitor) { super(api, recordComponentVisitor); } } ================================================ FILE: src/main/java/lsieun/asm/core/info/InfoClassVisitor.java ================================================ package lsieun.asm.core.info; import org.objectweb.asm.*; import java.util.ArrayList; import java.util.Arrays; import java.util.List; public class InfoClassVisitor extends ClassVisitor { public InfoClassVisitor(int api, ClassVisitor classVisitor) { super(api, classVisitor); } @Override public void visit(int version, int access, String name, String signature, String superName, String[] interfaces) { String line = String.format("ClassVisitor.visit(%d, %s, %s, %s, %s, %s);", version, getAccess(access), name, signature, superName, Arrays.toString(interfaces)); System.out.println(line); super.visit(version, access, name, signature, superName, interfaces); } @Override public void visitAttribute(Attribute attribute) { String line = String.format("ClassVisitor.visitAttribute(%s);", attribute); System.out.println(line); super.visitAttribute(attribute); } @Override public FieldVisitor visitField(int access, String name, String descriptor, String signature, Object value) { String line = String.format("ClassVisitor.visitField(%s, %s, %s, %s, %s);", getAccess(access), name, descriptor, signature, value); System.out.println(line); FieldVisitor fv = super.visitField(access, name, descriptor, signature, value); return new InfoFieldVisitor(api, fv); } @Override public MethodVisitor visitMethod(int access, String name, String descriptor, String signature, String[] exceptions) { String line = String.format("ClassVisitor.visitMethod(%s, %s, %s, %s, %s);", getAccess(access), name, descriptor, signature, Arrays.toString(exceptions)); System.out.println(line); MethodVisitor mv = super.visitMethod(access, name, descriptor, signature, exceptions); return new InfoMethodVisitor(api, mv); } @Override public void visitEnd() { String line = String.format("ClassVisitor.visitEnd();"); System.out.println(line); super.visitEnd(); } private String getAccess(int access) { List list = new ArrayList<>(); if ((access & Opcodes.ACC_PUBLIC) != 0) { list.add("ACC_PUBLIC"); } else if ((access & Opcodes.ACC_PROTECTED) != 0) { list.add("ACC_PROTECTED"); } else if ((access & Opcodes.ACC_PRIVATE) != 0) { list.add("ACC_PRIVATE"); } if ((access & Opcodes.ACC_STATIC) != 0) { list.add("ACC_STATIC"); } if ((access & Opcodes.ACC_FINAL) != 0) { list.add("ACC_FINAL"); } if ((access & Opcodes.ACC_NATIVE) != 0) { list.add("ACC_NATIVE"); } if ((access & Opcodes.ACC_INTERFACE) != 0) { list.add("ACC_INTERFACE"); } if ((access & Opcodes.ACC_ABSTRACT) != 0) { list.add("ACC_ABSTRACT"); } if ((access & Opcodes.ACC_SYNTHETIC) != 0) { list.add("ACC_SYNTHETIC"); } return list.toString(); } } ================================================ FILE: src/main/java/lsieun/asm/core/info/InfoFieldVisitor.java ================================================ package lsieun.asm.core.info; import org.objectweb.asm.FieldVisitor; public class InfoFieldVisitor extends FieldVisitor { public InfoFieldVisitor(int api, FieldVisitor fieldVisitor) { super(api, fieldVisitor); } @Override public void visitEnd() { String line = String.format(" FieldVisitor.visitEnd();"); System.out.println(line); super.visitEnd(); } } ================================================ FILE: src/main/java/lsieun/asm/core/info/InfoMethodVisitor.java ================================================ package lsieun.asm.core.info; import org.objectweb.asm.Label; import org.objectweb.asm.MethodVisitor; import org.objectweb.asm.util.Printer; public class InfoMethodVisitor extends MethodVisitor { public InfoMethodVisitor(int api, MethodVisitor methodVisitor) { super(api, methodVisitor); } @Override public void visitCode() { String line = String.format(" MethodVisitor.visitCode();"); System.out.println(line); super.visitCode(); } @Override public void visitInsn(int opcode) { String line = String.format(" MethodVisitor.visitInsn(%s);", Printer.OPCODES[opcode]); System.out.println(line); super.visitInsn(opcode); } @Override public void visitIntInsn(int opcode, int operand) { String line = String.format(" MethodVisitor.visitIntInsn(%s, %s);", Printer.OPCODES[opcode], operand); System.out.println(line); super.visitIntInsn(opcode, operand); } @Override public void visitVarInsn(int opcode, int var) { String line = String.format(" MethodVisitor.visitVarInsn(%s, %s);", Printer.OPCODES[opcode], var); System.out.println(line); super.visitVarInsn(opcode, var); } @Override public void visitTypeInsn(int opcode, String type) { String line = String.format(" MethodVisitor.visitTypeInsn(%s, %s);", Printer.OPCODES[opcode], type); System.out.println(line); super.visitTypeInsn(opcode, type); } @Override public void visitFieldInsn(int opcode, String owner, String name, String descriptor) { String line = String.format(" MethodVisitor.visitFieldInsn(%s, %s, %s, %s);", Printer.OPCODES[opcode], owner, name, descriptor); System.out.println(line); super.visitFieldInsn(opcode, owner, name, descriptor); } @Override public void visitMethodInsn(int opcode, String owner, String name, String descriptor, boolean isInterface) { String line = String.format(" MethodVisitor.visitMethodInsn(%s, %s, %s, %s, %s);", Printer.OPCODES[opcode], owner, name, descriptor, isInterface); System.out.println(line); super.visitMethodInsn(opcode, owner, name, descriptor, isInterface); } @Override public void visitJumpInsn(int opcode, Label label) { String line = String.format(" MethodVisitor.visitJumpInsn(%s, %s);", Printer.OPCODES[opcode], label); System.out.println(line); super.visitJumpInsn(opcode, label); } @Override public void visitLabel(Label label) { String line = String.format(" MethodVisitor.visitLabel(%s);", label); System.out.println(line); super.visitLabel(label); } @Override public void visitLdcInsn(Object value) { String line = String.format(" MethodVisitor.visitLdcInsn(%s);", value); System.out.println(line); super.visitLdcInsn(value); } @Override public void visitIincInsn(int var, int increment) { String line = String.format(" MethodVisitor.visitIincInsn(%s, %s);", var, increment); System.out.println(line); super.visitIincInsn(var, increment); } @Override public void visitMaxs(int maxStack, int maxLocals) { String line = String.format(" MethodVisitor.visitMaxs(%s, %s);", maxStack, maxLocals); System.out.println(line); super.visitMaxs(maxStack, maxLocals); } @Override public void visitEnd() { String line = String.format(" MethodVisitor.visitEnd();"); System.out.println(line); super.visitEnd(); } } ================================================ FILE: src/main/java/lsieun/asm/core/timer/ClassTimerVisitor.java ================================================ package lsieun.asm.core.timer; import org.objectweb.asm.ClassVisitor; import org.objectweb.asm.FieldVisitor; import org.objectweb.asm.MethodVisitor; import static org.objectweb.asm.Opcodes.*; public class ClassTimerVisitor extends ClassVisitor { private String owner; private boolean isInterface; public ClassTimerVisitor(int api, ClassVisitor cv) { super(api, cv); } @Override public void visit(int version, int access, String name, String signature, String superName, String[] interfaces) { super.visit(version, access, name, signature, superName, interfaces); owner = name; isInterface = (access & ACC_INTERFACE) != 0; } @Override public MethodVisitor visitMethod(int access, String name, String descriptor, String signature, String[] exceptions) { MethodVisitor mv = super.visitMethod(access, name, descriptor, signature, exceptions); if (!isInterface && mv != null && !name.equals("")) { mv = new MethodTimerAdapter(api, mv, owner); } return mv; } @Override public void visitEnd() { if (!isInterface) { FieldVisitor fv = super.visitField(ACC_PUBLIC | ACC_STATIC, "timer", "J", null, null); if (fv != null) { fv.visitEnd(); } } super.visitEnd(); } } ================================================ FILE: src/main/java/lsieun/asm/core/timer/MethodTimerAdapter.java ================================================ package lsieun.asm.core.timer; import org.objectweb.asm.MethodVisitor; import static org.objectweb.asm.Opcodes.*; public class MethodTimerAdapter extends MethodVisitor { private final String owner; public MethodTimerAdapter(int api, MethodVisitor mv, String owner) { super(api, mv); this.owner = owner; } @Override public void visitCode() { super.visitCode(); super.visitFieldInsn(GETSTATIC, owner, "timer", "J"); super.visitMethodInsn(INVOKESTATIC, "java/lang/System", "currentTimeMillis", "()J", false); super.visitInsn(LSUB); super.visitFieldInsn(PUTSTATIC, owner, "timer", "J"); } @Override public void visitInsn(int opcode) { if ((opcode >= IRETURN && opcode <= RETURN) || opcode == ATHROW) { super.visitFieldInsn(GETSTATIC, owner, "timer", "J"); super.visitMethodInsn(INVOKESTATIC, "java/lang/System", "currentTimeMillis", "()J", false); super.visitInsn(LADD); super.visitFieldInsn(PUTSTATIC, owner, "timer", "J"); } super.visitInsn(opcode); } } ================================================ FILE: src/main/java/lsieun/asm/template/CheckMethodAnnotationVisitor.java ================================================ package lsieun.asm.template; import org.objectweb.asm.AnnotationVisitor; import org.objectweb.asm.ClassVisitor; import org.objectweb.asm.MethodVisitor; import java.util.ArrayList; import java.util.List; public class CheckMethodAnnotationVisitor extends ClassVisitor { // 需要处理的方法放到这里 public List result = new ArrayList<>(); public CheckMethodAnnotationVisitor(int api, ClassVisitor classVisitor) { super(api, classVisitor); } @Override public MethodVisitor visitMethod(int access, String name, String descriptor, String signature, String[] exceptions) { MethodVisitor mv = super.visitMethod(access, name, descriptor, signature, exceptions); mv = new CheckMethodAnnotationAdapter(api, mv, name, descriptor); return mv; } private class CheckMethodAnnotationAdapter extends MethodVisitor { private final String methodName; private final String methodDesc; public CheckMethodAnnotationAdapter(int api, MethodVisitor methodVisitor, String methodName, String methodDesc) { super(api, methodVisitor); this.methodName = methodName; this.methodDesc = methodDesc; } @Override public AnnotationVisitor visitAnnotation(String descriptor, boolean visible) { // 在这里进行判断:是否需要对方法进行处理 if (descriptor.equals("Lsample/MyTag;")) { String item = methodName + ":" + methodDesc; result.add(item); } return super.visitAnnotation(descriptor, visible); } } } ================================================ FILE: src/main/java/lsieun/asm/template/ClassAddAnnotationVisitor.java ================================================ package lsieun.asm.template; import org.objectweb.asm.*; public class ClassAddAnnotationVisitor extends ClassVisitor { private final String annotationDesc; private boolean isAnnotationPresent; public ClassAddAnnotationVisitor(int api, ClassVisitor classVisitor, String annotationDesc) { super(api, classVisitor); this.annotationDesc = annotationDesc; } @Override public AnnotationVisitor visitAnnotation(String descriptor, boolean visible) { if (visible && descriptor.equals(annotationDesc)) { isAnnotationPresent = true; } return super.visitAnnotation(descriptor, visible); } @Override public void visitNestMember(String nestMember) { addAnnotation(); super.visitNestMember(nestMember); } @Override public void visitInnerClass(String name, String outerName, String innerName, int access) { addAnnotation(); super.visitInnerClass(name, outerName, innerName, access); } @Override public RecordComponentVisitor visitRecordComponent(String name, String descriptor, String signature) { addAnnotation(); return super.visitRecordComponent(name, descriptor, signature); } @Override public FieldVisitor visitField(int access, String name, String descriptor, String signature, Object value) { addAnnotation(); return super.visitField(access, name, descriptor, signature, value); } @Override public MethodVisitor visitMethod(int access, String name, String descriptor, String signature, String[] exceptions) { addAnnotation(); return super.visitMethod(access, name, descriptor, signature, exceptions); } @Override public void visitEnd() { addAnnotation(); super.visitEnd(); } private void addAnnotation() { if (!isAnnotationPresent) { AnnotationVisitor av = super.visitAnnotation(annotationDesc, true); if (av != null) { av.visitEnd(); } isAnnotationPresent = true; } } } ================================================ FILE: src/main/java/lsieun/asm/template/ClassAddCustomAttributeVisitor.java ================================================ package lsieun.asm.template; import lsieun.utils.ByteUtils; import org.objectweb.asm.*; public class ClassAddCustomAttributeVisitor extends ClassVisitor { private final String attrName; private final String attrContent; private boolean isAttrPresent; public ClassAddCustomAttributeVisitor(int api, ClassVisitor classVisitor, String attrName, String attrContent) { super(api, classVisitor); this.attrName = attrName; this.attrContent = attrContent; this.isAttrPresent = false; } @Override public void visitAttribute(Attribute attribute) { if (attribute.type.equals(attrName)) { isAttrPresent = true; } super.visitAttribute(attribute); } @Override public void visitNestMember(String nestMember) { addAttribute(); super.visitNestMember(nestMember); } @Override public void visitInnerClass(String name, String outerName, String innerName, int access) { addAttribute(); super.visitInnerClass(name, outerName, innerName, access); } @Override public RecordComponentVisitor visitRecordComponent(String name, String descriptor, String signature) { addAttribute(); return super.visitRecordComponent(name, descriptor, signature); } @Override public FieldVisitor visitField(int access, String name, String descriptor, String signature, Object value) { addAttribute(); return super.visitField(access, name, descriptor, signature, value); } @Override public MethodVisitor visitMethod(int access, String name, String descriptor, String signature, String[] exceptions) { addAttribute(); return super.visitMethod(access, name, descriptor, signature, exceptions); } @Override public void visitEnd() { addAttribute(); super.visitEnd(); } private void addAttribute() { if (!isAttrPresent) { int hashCode = attrContent.hashCode(); byte[] info = ByteUtils.intToByteArray(hashCode); Attribute attr = new CustomAttribute(attrName, info); super.visitAttribute(attr); isAttrPresent = true; } } } ================================================ FILE: src/main/java/lsieun/asm/template/ClassAddFieldVisitor.java ================================================ package lsieun.asm.template; import org.objectweb.asm.ClassVisitor; import org.objectweb.asm.FieldVisitor; public class ClassAddFieldVisitor extends ClassVisitor { private final int fieldAccess; private final String fieldName; private final String fieldDesc; private boolean isFieldPresent; public ClassAddFieldVisitor(int api, ClassVisitor classVisitor, int fieldAccess, String fieldName, String fieldDesc) { super(api, classVisitor); this.fieldAccess = fieldAccess; this.fieldName = fieldName; this.fieldDesc = fieldDesc; } @Override public FieldVisitor visitField(int access, String name, String descriptor, String signature, Object value) { if (name.equals(fieldName)) { isFieldPresent = true; } return super.visitField(access, name, descriptor, signature, value); } @Override public void visitEnd() { if (!isFieldPresent) { FieldVisitor fv = super.visitField(fieldAccess, fieldName, fieldDesc, null, null); if (fv != null) { fv.visitEnd(); } } super.visitEnd(); } } ================================================ FILE: src/main/java/lsieun/asm/template/ClassAddInterfaceVisitor.java ================================================ package lsieun.asm.template; import org.objectweb.asm.ClassVisitor; import java.util.Arrays; import java.util.HashSet; import java.util.Set; public class ClassAddInterfaceVisitor extends ClassVisitor { private final String[] newInterfaces; public ClassAddInterfaceVisitor(int api, ClassVisitor cv, String[] newInterfaces) { super(api, cv); this.newInterfaces = newInterfaces; } @Override public void visit(int version, int access, String name, String signature, String superName, String[] interfaces) { Set set = new HashSet<>(); // 注意,这里使用Set是为了避免出现重复接口 if (interfaces != null) { set.addAll(Arrays.asList(interfaces)); } if (newInterfaces != null) { set.addAll(Arrays.asList(newInterfaces)); } super.visit(version, access, name, signature, superName, set.toArray(new String[0])); } } ================================================ FILE: src/main/java/lsieun/asm/template/ClassAddMethodVisitor.java ================================================ package lsieun.asm.template; import org.objectweb.asm.ClassVisitor; import org.objectweb.asm.MethodVisitor; public abstract class ClassAddMethodVisitor extends ClassVisitor { private final int methodAccess; private final String methodName; private final String methodDesc; private final String methodSignature; private final String[] methodExceptions; private boolean isMethodPresent; public ClassAddMethodVisitor(int api, ClassVisitor cv, int methodAccess, String methodName, String methodDesc, String signature, String[] exceptions) { super(api, cv); this.methodAccess = methodAccess; this.methodName = methodName; this.methodDesc = methodDesc; this.methodSignature = signature; this.methodExceptions = exceptions; this.isMethodPresent = false; } @Override public MethodVisitor visitMethod(int access, String name, String descriptor, String signature, String[] exceptions) { if (name.equals(methodName) && descriptor.equals(methodDesc)) { isMethodPresent = true; } return super.visitMethod(access, name, descriptor, signature, exceptions); } @Override public void visitEnd() { if (!isMethodPresent) { MethodVisitor mv = super.visitMethod(methodAccess, methodName, methodDesc, methodSignature, methodExceptions); if (mv != null) { // create method body generateMethodBody(mv); } } super.visitEnd(); } protected abstract void generateMethodBody(MethodVisitor mv); } ================================================ FILE: src/main/java/lsieun/asm/template/ClassGetAttributeContentVisitor.java ================================================ package lsieun.asm.template; import org.objectweb.asm.ClassVisitor; import org.objectweb.asm.FieldVisitor; import org.objectweb.asm.MethodVisitor; public class ClassGetAttributeContentVisitor extends ClassVisitor { private final StringBuilder attr = new StringBuilder(); public ClassGetAttributeContentVisitor(int api, ClassVisitor classVisitor) { super(api, classVisitor); } @Override public void visit(int version, int access, String name, String signature, String superName, String[] interfaces) { attr.append(name); super.visit(version, access, name, signature, superName, interfaces); } @Override public FieldVisitor visitField(int access, String name, String descriptor, String signature, Object value) { attr.append(name); return super.visitField(access, name, descriptor, signature, value); } @Override public MethodVisitor visitMethod(int access, String name, String descriptor, String signature, String[] exceptions) { attr.append(name); return super.visitMethod(access, name, descriptor, signature, exceptions); } public String getAttributeContent() { return attr.toString(); } } ================================================ FILE: src/main/java/lsieun/asm/template/ClassMergeVisitor.java ================================================ package lsieun.asm.template; import org.objectweb.asm.ClassVisitor; import org.objectweb.asm.tree.ClassNode; import org.objectweb.asm.tree.FieldNode; import org.objectweb.asm.tree.MethodNode; import java.util.List; public class ClassMergeVisitor extends ClassVisitor { private final ClassNode anotherClass; public ClassMergeVisitor(int api, ClassVisitor classVisitor, ClassNode anotherClass) { super(api, classVisitor); this.anotherClass = anotherClass; } @Override public void visitEnd() { List fields = anotherClass.fields; for (FieldNode fn : fields) { fn.accept(this); } List methods = anotherClass.methods; for (MethodNode mn : methods) { String methodName = mn.name; if ("".equals(methodName)) { continue; } mn.accept(this); } super.visitEnd(); } } ================================================ FILE: src/main/java/lsieun/asm/template/ClassMergeVisitorRun.java ================================================ package lsieun.asm.template; import lsieun.utils.FileUtils; import lsieun.utils.ReadUtils; import org.objectweb.asm.ClassReader; import org.objectweb.asm.ClassVisitor; import org.objectweb.asm.ClassWriter; import org.objectweb.asm.Opcodes; import org.objectweb.asm.tree.ClassNode; import java.util.List; public class ClassMergeVisitorRun { public static void main(String[] args) { String first_class = "sample/HelloWorld"; String second_class = "sample/GoodChild"; String filepath = getFilePath(first_class); byte[] bytes1 = ReadUtils.readByPath(filepath); //(1)构建ClassReader ClassReader cr = new ClassReader(bytes1); //(2)构建ClassWriter ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_FRAMES); //(3)串连ClassVisitor byte[] second_class_bytes = rename(second_class, first_class); ClassNode cn = getClassNode(second_class_bytes); List interface_list = cn.interfaces; int size = interface_list.size(); String[] interfaces = new String[size]; for (int i = 0; i < size; i++) { String item = interface_list.get(i); interfaces[i] = item; } ClassVisitor cv = new ClassMergeVisitor(Opcodes.ASM9, cw, cn); cv = new ClassAddInterfaceVisitor(Opcodes.ASM9, cv, interfaces); //(4)两者进行结合 cr.accept(cv, 0); //(5)重新生成Class byte[] bytes2 = cw.toByteArray(); FileUtils.writeBytes(filepath, bytes2); } public static ClassNode getClassNode(byte[] bytes) { ClassReader cr = new ClassReader(bytes); ClassNode cn = new ClassNode(); cr.accept(cn, 0); return cn; } public static byte[] rename(String origin_name, String target_name) { String origin_filepath = getFilePath(origin_name); byte[] bytes1 = ReadUtils.readByPath(origin_filepath); //(1)构建ClassReader ClassReader cr = new ClassReader(bytes1); //(2)构建ClassWriter ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_FRAMES); //(3)串连ClassVisitor ClassVisitor cv = new ClassRenameAdapter(Opcodes.ASM9, cw, origin_name, target_name); //(4)两者进行结合 cr.accept(cv, 0); //(5)重新生成Class return cw.toByteArray(); } public static String getFilePath(String internalName) { String relative_path = String.format("%s.class", internalName); return FileUtils.getFilePath(relative_path); } } ================================================ FILE: src/main/java/lsieun/asm/template/ClassPrintAnnotationVisitor.java ================================================ package lsieun.asm.template; import org.objectweb.asm.AnnotationVisitor; import org.objectweb.asm.ClassVisitor; import org.objectweb.asm.MethodVisitor; // 第一个类 public class ClassPrintAnnotationVisitor extends ClassVisitor { private String owner; public ClassPrintAnnotationVisitor(int api, ClassVisitor classVisitor) { super(api, classVisitor); } @Override public void visit(int version, int access, String name, String signature, String superName, String[] interfaces) { super.visit(version, access, name, signature, superName, interfaces); this.owner = name; } @Override public AnnotationVisitor visitAnnotation(String descriptor, boolean visible) { // (1) 调用父类的实现 AnnotationVisitor av = super.visitAnnotation(descriptor, visible); // (2) 添加自己的代码逻辑 String info = String.format("Class: %s - %s", owner, descriptor); System.out.println(info); return new AnnotationPrinter(api, av); } @Override public MethodVisitor visitMethod(int access, String name, String descriptor, String signature, String[] exceptions) { MethodVisitor mv = super.visitMethod(access, name, descriptor, signature, exceptions); mv = new MethodPrintAnnotationAdapter(api, mv, owner, name, descriptor); return mv; } // 第二个类 private static class MethodPrintAnnotationAdapter extends MethodVisitor { private final String owner; private final String methodName; private final String methodDesc; public MethodPrintAnnotationAdapter(int api, MethodVisitor methodVisitor, String owner, String methodName, String methodDesc) { super(api, methodVisitor); this.owner = owner; this.methodName = methodName; this.methodDesc = methodDesc; } @Override public AnnotationVisitor visitAnnotation(String descriptor, boolean visible) { // (1) 调用父类的实现 AnnotationVisitor av = super.visitAnnotation(descriptor, visible); // (2) 添加自己的代码逻辑 String info = String.format("Method: %s.%s:%s - %s", owner, methodName, methodDesc, descriptor); System.out.println(info); return new AnnotationPrinter(api, av); } } // 第三个类 private static class AnnotationPrinter extends AnnotationVisitor { public AnnotationPrinter(int api, AnnotationVisitor annotationVisitor) { super(api, annotationVisitor); } @Override public void visit(String name, Object value) { // (1) 添加自己的代码逻辑 String info = String.format(" %s: %s", name, value); System.out.println(info); // (2) 调用父类的实现 super.visit(name, value); } @Override public void visitEnum(String name, String descriptor, String value) { // (1) 添加自己的代码逻辑 String info = String.format(" %s: %s %s", name, descriptor, value); System.out.println(info); // (2) 调用父类的实现 super.visitEnum(name, descriptor, value); } @Override public AnnotationVisitor visitAnnotation(String name, String descriptor) { // (1) 添加自己的代码逻辑 String info = String.format(" %s: %s", name, descriptor); System.out.println(info); // (2) 调用父类的实现 return super.visitAnnotation(name, descriptor); } @Override public AnnotationVisitor visitArray(String name) { // (1) 添加自己的代码逻辑 String info = String.format(" %s", name); System.out.println(info); // (2) 调用父类的实现 return super.visitArray(name); } } } ================================================ FILE: src/main/java/lsieun/asm/template/ClassPrintParameterVisitor.java ================================================ package lsieun.asm.template; import org.objectweb.asm.ClassVisitor; import org.objectweb.asm.MethodVisitor; import org.objectweb.asm.Type; import org.objectweb.asm.commons.AdviceAdapter; import static org.objectweb.asm.Opcodes.*; public class ClassPrintParameterVisitor extends ClassVisitor { public ClassPrintParameterVisitor(int api, ClassVisitor classVisitor) { super(api, classVisitor); } @Override public MethodVisitor visitMethod(int access, String name, String descriptor, String signature, String[] exceptions) { MethodVisitor mv = super.visitMethod(access, name, descriptor, signature, exceptions); if (mv != null) { boolean isAbstractMethod = (access & ACC_ABSTRACT) != 0; boolean isNativeMethod = (access & ACC_NATIVE) != 0; if (!isAbstractMethod && !isNativeMethod) { mv = new MethodPrintParameterAdapter(api, mv, access, name, descriptor); } } return mv; } public static class MethodPrintParameterAdapter extends AdviceAdapter { public MethodPrintParameterAdapter(int api, MethodVisitor mv, int access, String name, String descriptor) { super(api, mv, access, name, descriptor); } @Override protected void onMethodEnter() { printMessage("Method Enter: " + getName() + methodDesc); Type[] argumentTypes = getArgumentTypes(); for (int i = 0; i < argumentTypes.length; i++) { Type t = argumentTypes[i]; loadArg(i); box(t); printValueOnStack("(Ljava/lang/Object;)V"); } } @Override protected void onMethodExit(int opcode) { printMessage("Method Exit: " + getName() + methodDesc); if (opcode == ATHROW) { super.visitLdcInsn("abnormal return"); } else if (opcode == RETURN) { super.visitLdcInsn("return void"); } else if (opcode == ARETURN) { dup(); } else { if (opcode == LRETURN || opcode == DRETURN) { dup2(); } else { dup(); } box(getReturnType()); } printValueOnStack("(Ljava/lang/Object;)V"); } private void printMessage(String str) { super.visitLdcInsn(str); super.visitMethodInsn(INVOKESTATIC, "sample/ParameterUtils", "printText", "(Ljava/lang/String;)V", false); } private void printValueOnStack(String descriptor) { super.visitMethodInsn(INVOKESTATIC, "sample/ParameterUtils", "printValueOnStack", descriptor, false); } } } ================================================ FILE: src/main/java/lsieun/asm/template/ClassRemoveFieldVisitor.java ================================================ package lsieun.asm.template; import org.objectweb.asm.ClassVisitor; import org.objectweb.asm.FieldVisitor; public class ClassRemoveFieldVisitor extends ClassVisitor { private final String fieldName; private final String fieldDesc; public ClassRemoveFieldVisitor(int api, ClassVisitor cv, String fieldName, String fieldDesc) { super(api, cv); this.fieldName = fieldName; this.fieldDesc = fieldDesc; } @Override public FieldVisitor visitField(int access, String name, String descriptor, String signature, Object value) { if (name.equals(fieldName) && descriptor.equals(fieldDesc)) { return null; } else { return super.visitField(access, name, descriptor, signature, value); } } } ================================================ FILE: src/main/java/lsieun/asm/template/ClassRemoveMethodVisitor.java ================================================ package lsieun.asm.template; import org.objectweb.asm.ClassVisitor; import org.objectweb.asm.MethodVisitor; public class ClassRemoveMethodVisitor extends ClassVisitor { private final String methodName; private final String methodDesc; public ClassRemoveMethodVisitor(int api, ClassVisitor cv, String methodName, String methodDesc) { super(api, cv); this.methodName = methodName; this.methodDesc = methodDesc; } @Override public MethodVisitor visitMethod(int access, String name, String descriptor, String signature, String[] exceptions) { if (name.equals(methodName) && descriptor.equals(methodDesc)) { return null; } else { return super.visitMethod(access, name, descriptor, signature, exceptions); } } } ================================================ FILE: src/main/java/lsieun/asm/template/ClassRenameAdapter.java ================================================ package lsieun.asm.template; import org.objectweb.asm.ClassVisitor; import org.objectweb.asm.FieldVisitor; import org.objectweb.asm.MethodVisitor; public class ClassRenameAdapter extends ClassVisitor { private final String oldOwner; private final String newOwner; public ClassRenameAdapter(int api, ClassVisitor classVisitor, String oldOwner, String newOwner) { super(api, classVisitor); this.oldOwner = oldOwner; this.newOwner = newOwner; } @Override public void visit(int version, int access, String name, String signature, String superName, String[] interfaces) { if (name.equals(oldOwner)) { super.visit(version, access, newOwner, signature, superName, interfaces); } else { super.visit(version, access, name, signature, superName, interfaces); } } @Override public FieldVisitor visitField(int access, String name, String descriptor, String signature, Object value) { String oldDesc = getDescriptor(oldOwner); if (descriptor.contains(oldDesc)) { String newDesc = getDescriptor(newOwner); String desc = descriptor.replaceAll(oldDesc, newDesc); return super.visitField(access, name, desc, signature, value); } return super.visitField(access, name, descriptor, signature, value); } @Override public MethodVisitor visitMethod(int access, String name, String descriptor, String signature, String[] exceptions) { String desc = descriptor; String oldDesc = getDescriptor(oldOwner); if (descriptor.contains(oldDesc)) { String newDesc = getDescriptor(newOwner); desc = descriptor.replaceAll(oldDesc, newDesc); } MethodVisitor mv = super.visitMethod(access, name, desc, signature, exceptions); if (mv != null) { mv = new RefRenameAdapter(api, mv, oldOwner, newOwner); } return mv; } public String getDescriptor(String internalName) { return String.format("L%s;", internalName); } } ================================================ FILE: src/main/java/lsieun/asm/template/ClassRenameAdapterRun.java ================================================ package lsieun.asm.template; import lsieun.utils.FileUtils; import lsieun.utils.ReadUtils; import org.objectweb.asm.ClassReader; import org.objectweb.asm.ClassVisitor; import org.objectweb.asm.ClassWriter; import org.objectweb.asm.Opcodes; public class ClassRenameAdapterRun { public static void main(String[] args) { String origin_name = "sample/HelloWorld"; String target_name = "sample/GoodChild"; String origin_filepath = getFilePath(origin_name); byte[] bytes1 = ReadUtils.readByPath(origin_filepath); //(1)构建ClassReader ClassReader cr = new ClassReader(bytes1); //(2)构建ClassWriter ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_FRAMES); //(3)串连ClassVisitor ClassVisitor cv = new ClassRenameAdapter(Opcodes.ASM9, cw, origin_name, target_name); //(4)两者进行结合 cr.accept(cv, 0); //(5)重新生成Class byte[] bytes2 = cw.toByteArray(); String target_filepath = getFilePath(target_name); FileUtils.writeBytes(target_filepath, bytes2); } public static String getFilePath(String internalName) { String relative_path = String.format("%s.class", internalName); return FileUtils.getFilePath(relative_path); } } ================================================ FILE: src/main/java/lsieun/asm/template/ClassReplaceMethodBodyVisitor.java ================================================ package lsieun.asm.template; import org.objectweb.asm.ClassVisitor; import org.objectweb.asm.MethodVisitor; public abstract class ClassReplaceMethodBodyVisitor extends ClassVisitor { protected String owner; protected final String methodName; protected final String methodDesc; private final boolean keepOriginalMethod; public ClassReplaceMethodBodyVisitor(int api, ClassVisitor cv, String methodName, String methodDesc) { this(api, cv, methodName, methodDesc, true); } public ClassReplaceMethodBodyVisitor(int api, ClassVisitor cv, String methodName, String methodDesc, boolean keepOriginalMethod) { super(api, cv); this.methodName = methodName; this.methodDesc = methodDesc; this.keepOriginalMethod = keepOriginalMethod; } @Override public void visit(int version, int access, String name, String signature, String superName, String[] interfaces) { super.visit(version, access, name, signature, superName, interfaces); this.owner = name; } @Override public MethodVisitor visitMethod(int access, String name, String descriptor, String signature, String[] exceptions) { if (name.equals(methodName) && descriptor.equals(methodDesc)) { // 生成新方法(从抽象逻辑上来说,这是第二步;从代码角度来说,先执行) generateNewMethod(access, name, descriptor, signature, exceptions); if (keepOriginalMethod) { // 修改原来方法的名字(从抽象逻辑上来说,这是第一步;从代码角度来说,后执行) String newName = getNewName(name); return super.visitMethod(access, newName, descriptor, signature, exceptions); } else { // 删除原来的方法 return null; } } return super.visitMethod(access, name, descriptor, signature, exceptions); } protected String getNewName(String name) { return String.format("orig$%s", name); } private void generateNewMethod(int access, String name, String descriptor, String signature, String[] exceptions) { MethodVisitor mv = super.visitMethod(access, name, descriptor, signature, exceptions); if (mv != null) { generateMethodBody(mv); } } protected abstract void generateMethodBody(MethodVisitor mv); } ================================================ FILE: src/main/java/lsieun/asm/template/CustomAttribute.java ================================================ package lsieun.asm.template; import lsieun.utils.ByteUtils; import lsieun.utils.HexFormat; import lsieun.utils.HexUtils; import org.objectweb.asm.*; public class CustomAttribute extends Attribute { private static final byte[] CODE_BLOB_BYTE_ARRAY = new byte[]{ (byte) 0xC0, (byte) 0xDE, (byte) 0xB1, 0x0B }; private static final int CODE_BLOB_INT_VALUE = 0xC0DEB10B; private final byte[] info; public CustomAttribute(String type, byte[] info) { super(type); this.info = info; } @Override protected Attribute read(ClassReader classReader, int offset, int length, char[] charBuffer, int codeAttributeOffset, Label[] labels) { int magic = classReader.readInt(offset); if (magic != CODE_BLOB_INT_VALUE) { throw new RuntimeException("magic is not right! expected: " + CODE_BLOB_INT_VALUE + ", actual: " + magic); } int value = classReader.readInt(offset + 4); byte[] info = ByteUtils.intToByteArray(value); return new CustomAttribute(CustomAttribute.class.getSimpleName(), info); } @Override protected ByteVector write(ClassWriter classWriter, byte[] code, int codeLength, int maxStack, int maxLocals) { ByteVector byteVector = new ByteVector(); byteVector.putByteArray(CODE_BLOB_BYTE_ARRAY, 0, CODE_BLOB_BYTE_ARRAY.length); byteVector.putByteArray(info, 0, info.length); return byteVector; } @Override public String toString() { return String.format("%s {name='%s'}", type, HexUtils.format(info, HexFormat.FORMAT_FF_FF)); } } ================================================ FILE: src/main/java/lsieun/asm/template/FieldAccessAdapter.java ================================================ package lsieun.asm.template; import org.objectweb.asm.ClassVisitor; import org.objectweb.asm.MethodVisitor; import java.util.List; public class FieldAccessAdapter extends ClassVisitor { private final List list; public FieldAccessAdapter(int api, ClassVisitor cv, List list) { super(api, cv); this.list = list; } @Override public MethodVisitor visitMethod(int access, String name, String descriptor, String signature, String[] exceptions) { MethodVisitor mv = super.visitMethod(access, name, descriptor, signature, exceptions); if (mv != null) { mv = new FieldAccessConverter(api, mv, list); } return mv; } } ================================================ FILE: src/main/java/lsieun/asm/template/FieldAccessAdapterRun.java ================================================ package lsieun.asm.template; import lsieun.utils.FileUtils; import lsieun.utils.ReadUtils; import org.objectweb.asm.ClassReader; import org.objectweb.asm.ClassVisitor; import org.objectweb.asm.ClassWriter; import org.objectweb.asm.Opcodes; import java.util.ArrayList; import java.util.List; public class FieldAccessAdapterRun { public static void main(String[] args) { String relative_path = "sample/HelloWorld.class"; String filepath = FileUtils.getFilePath(relative_path); byte[] bytes1 = ReadUtils.readByPath(filepath); //(1)构建ClassReader ClassReader cr = new ClassReader(bytes1); //(2)构建ClassWriter ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_FRAMES); //(3)串连ClassVisitor Info info1 = new Info(Opcodes.GETFIELD, "sample/HelloWorld", "intValue", "I", Opcodes.INVOKEVIRTUAL, "sample/HelloWorld", "getHour", "()I"); Info info2 = new Info(Opcodes.GETSTATIC, "sample/HelloWorld", "staticValue", "I", Opcodes.INVOKESTATIC, "sample/GoodChild", "getAge", "()I"); List list = new ArrayList<>(); list.add(info1); list.add(info2); ClassVisitor cv = new FieldAccessAdapter(Opcodes.ASM9, cw, list); //(4)两者进行结合 cr.accept(cv, ClassReader.SKIP_FRAMES); //(5)重新生成Class byte[] bytes2 = cw.toByteArray(); FileUtils.writeBytes(filepath, bytes2); } } ================================================ FILE: src/main/java/lsieun/asm/template/FieldAccessConverter.java ================================================ package lsieun.asm.template; import org.objectweb.asm.MethodVisitor; import java.util.ArrayList; import java.util.List; public class FieldAccessConverter extends MethodVisitor { private final List list; public FieldAccessConverter(int api, MethodVisitor mv, List list) { super(api, mv); if (list == null) { this.list = new ArrayList<>(); } else { this.list = list; } } @Override public void visitFieldInsn(int opcode, String owner, String name, String descriptor) { Info info = matchingInfo(opcode, owner, name, descriptor); if (info != null) { super.visitMethodInsn(info.targetOpcode, info.targetOwner, info.targetName, info.targetDesc, false); return; } super.visitFieldInsn(opcode, owner, name, descriptor); } private Info matchingInfo(int opcode, String owner, String name, String descriptor) { for (Info info : list) { if (opcode == info.srcOpcode && owner.equals(info.srcOwner) && name.equals(info.srcName) && descriptor.equals(info.srcDesc)) { return info; } } return null; } } ================================================ FILE: src/main/java/lsieun/asm/template/Info.java ================================================ package lsieun.asm.template; public class Info { public final int srcOpcode; public final String srcOwner; public final String srcName; public final String srcDesc; public final int targetOpcode; public final String targetOwner; public final String targetName; public final String targetDesc; public Info( int srcOpcode, String srcOwner, String srcName, String srcDesc, int targetOpcode, String targetOwner, String targetName, String targetDesc) { this.srcOpcode = srcOpcode; this.srcOwner = srcOwner; this.srcName = srcName; this.srcDesc = srcDesc; this.targetOpcode = targetOpcode; this.targetOwner = targetOwner; this.targetName = targetName; this.targetDesc = targetDesc; } } ================================================ FILE: src/main/java/lsieun/asm/template/MethodCallAdapter.java ================================================ package lsieun.asm.template; import org.objectweb.asm.ClassVisitor; import org.objectweb.asm.MethodVisitor; import java.util.List; public class MethodCallAdapter extends ClassVisitor { private final List list; public MethodCallAdapter(int api, ClassVisitor cv, List list) { super(api, cv); this.list = list; } @Override public MethodVisitor visitMethod(int access, String name, String descriptor, String signature, String[] exceptions) { MethodVisitor mv = super.visitMethod(access, name, descriptor, signature, exceptions); if (mv != null) { mv = new MethodCallConverter(api, mv, list); } return mv; } } ================================================ FILE: src/main/java/lsieun/asm/template/MethodCallAdapterRun.java ================================================ package lsieun.asm.template; import lsieun.utils.FileUtils; import lsieun.utils.ReadUtils; import org.objectweb.asm.ClassReader; import org.objectweb.asm.ClassVisitor; import org.objectweb.asm.ClassWriter; import org.objectweb.asm.Opcodes; import java.util.ArrayList; import java.util.List; public class MethodCallAdapterRun { public static void main(String[] args) { String relative_path = "sample/HelloWorld.class"; String filepath = FileUtils.getFilePath(relative_path); byte[] bytes1 = ReadUtils.readByPath(filepath); //(1)构建ClassReader ClassReader cr = new ClassReader(bytes1); //(2)构建ClassWriter ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_FRAMES); //(3)串连ClassVisitor Info info1 = new Info(Opcodes.INVOKEVIRTUAL, "sample/HelloWorld", "add", "(II)I", Opcodes.INVOKEVIRTUAL, "sample/HelloWorld", "sub", "(II)I"); List list = new ArrayList<>(); list.add(info1); ClassVisitor cv = new MethodCallAdapter(Opcodes.ASM9, cw, list); //(4)两者进行结合 cr.accept(cv, ClassReader.SKIP_FRAMES); //(5)重新生成Class byte[] bytes2 = cw.toByteArray(); FileUtils.writeBytes(filepath, bytes2); } } ================================================ FILE: src/main/java/lsieun/asm/template/MethodCallConverter.java ================================================ package lsieun.asm.template; import org.objectweb.asm.MethodVisitor; import java.util.ArrayList; import java.util.List; public class MethodCallConverter extends MethodVisitor { private final List list; public MethodCallConverter(int api, MethodVisitor mv, List list) { super(api, mv); if (list == null) { this.list = new ArrayList<>(); } else { this.list = list; } } @Override public void visitMethodInsn(int opcode, String owner, String name, String descriptor, boolean isInterface) { Info info = matchingInfo(opcode, owner, name, descriptor); if (info != null) { super.visitMethodInsn(info.targetOpcode, info.targetOwner, info.targetName, info.targetDesc, false); return; } super.visitMethodInsn(opcode, owner, name, descriptor, isInterface); } private Info matchingInfo(int opcode, String owner, String name, String descriptor) { for (Info info : list) { if (opcode == info.srcOpcode && owner.equals(info.srcOwner) && name.equals(info.srcName) && descriptor.equals(info.srcDesc)) { return info; } } return null; } } ================================================ FILE: src/main/java/lsieun/asm/template/MethodEnteringAdapter.java ================================================ package lsieun.asm.template; import org.objectweb.asm.MethodVisitor; import org.objectweb.asm.commons.AdviceAdapter; public class MethodEnteringAdapter extends AdviceAdapter { private final String methodName; public MethodEnteringAdapter(int api, MethodVisitor mv, int access, String name, String descriptor) { super(api, mv, access, name, descriptor); this.methodName = name; } @Override protected void onMethodEnter() { super.visitFieldInsn(GETSTATIC, "java/lang/System", "out", "Ljava/io/PrintStream;"); super.visitLdcInsn("Entering " + methodName + " Method"); super.visitMethodInsn(INVOKEVIRTUAL, "java/io/PrintStream", "println", "(Ljava/lang/String;)V", false); } } ================================================ FILE: src/main/java/lsieun/asm/template/MethodEnteringVisitor.java ================================================ package lsieun.asm.template; import org.objectweb.asm.ClassVisitor; import org.objectweb.asm.MethodVisitor; public class MethodEnteringVisitor extends ClassVisitor { public MethodEnteringVisitor(int api, ClassVisitor cv) { super(api, cv); } @Override public MethodVisitor visitMethod(int access, String name, String descriptor, String signature, String[] exceptions) { MethodVisitor mv = super.visitMethod(access, name, descriptor, signature, exceptions); if (mv != null) { mv = new MethodEnteringAdapter(api, mv, access, name, descriptor); } return mv; } } ================================================ FILE: src/main/java/lsieun/asm/template/MethodEnteringVisitorRun.java ================================================ package lsieun.asm.template; import lsieun.utils.FileUtils; import lsieun.utils.ReadUtils; import org.objectweb.asm.ClassReader; import org.objectweb.asm.ClassVisitor; import org.objectweb.asm.ClassWriter; import org.objectweb.asm.Opcodes; public class MethodEnteringVisitorRun { public static void main(String[] args) { String relative_path = "sample/HelloWorld.class"; String filepath = FileUtils.getFilePath(relative_path); byte[] bytes1 = ReadUtils.readByPath(filepath); //(1)构建ClassReader ClassReader cr = new ClassReader(bytes1); //(2)构建ClassWriter ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_FRAMES); //(3)串连ClassVisitor ClassVisitor cv = new MethodEnteringVisitor(Opcodes.ASM9, cw); //(4)两者进行结合 cr.accept(cv, 0); //(5)重新生成Class byte[] bytes2 = cw.toByteArray(); FileUtils.writeBytes(filepath, bytes2); } } ================================================ FILE: src/main/java/lsieun/asm/template/MethodExitingAdapter.java ================================================ package lsieun.asm.template; import org.objectweb.asm.ClassVisitor; import org.objectweb.asm.MethodVisitor; public class MethodExitingAdapter extends ClassVisitor { public MethodExitingAdapter(int api, ClassVisitor cv) { super(api, cv); } @Override public MethodVisitor visitMethod(int access, String name, String descriptor, String signature, String[] exceptions) { MethodVisitor mv = super.visitMethod(access, name, descriptor, signature, exceptions); if (mv != null) { mv = new MethodExitingConverter(api, mv, access, name, descriptor); } return mv; } } ================================================ FILE: src/main/java/lsieun/asm/template/MethodExitingAdapterRun.java ================================================ package lsieun.asm.template; import lsieun.utils.FileUtils; import lsieun.utils.ReadUtils; import org.objectweb.asm.ClassReader; import org.objectweb.asm.ClassVisitor; import org.objectweb.asm.ClassWriter; import org.objectweb.asm.Opcodes; public class MethodExitingAdapterRun { public static void main(String[] args) { String relative_path = "sample/HelloWorld.class"; String filepath = FileUtils.getFilePath(relative_path); byte[] bytes1 = ReadUtils.readByPath(filepath); //(1)构建ClassReader ClassReader cr = new ClassReader(bytes1); //(2)构建ClassWriter ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_FRAMES); //(3)串连ClassVisitor ClassVisitor cv = new MethodExitingAdapter(Opcodes.ASM9, cw); //(4)两者进行结合 cr.accept(cv, ClassReader.SKIP_FRAMES); //(5)重新生成Class byte[] bytes2 = cw.toByteArray(); FileUtils.writeBytes(filepath, bytes2); } } ================================================ FILE: src/main/java/lsieun/asm/template/MethodExitingConverter.java ================================================ package lsieun.asm.template; import org.objectweb.asm.MethodVisitor; import org.objectweb.asm.Opcodes; import org.objectweb.asm.commons.AdviceAdapter; public class MethodExitingConverter extends AdviceAdapter { private final String methodName; protected MethodExitingConverter(int api, MethodVisitor mv, int access, String name, String descriptor) { super(api, mv, access, name, descriptor); this.methodName = name; } @Override protected void onMethodExit(int opcode) { super.visitFieldInsn(GETSTATIC, "java/lang/System", "out", "Ljava/io/PrintStream;"); if (opcode == Opcodes.ATHROW) { super.visitLdcInsn("Exiting on exception " + methodName + " Method"); } else { super.visitLdcInsn("Exiting " + methodName + " Method"); } super.visitMethodInsn(INVOKEVIRTUAL, "java/io/PrintStream", "println", "(Ljava/lang/String;)V", false); } } ================================================ FILE: src/main/java/lsieun/asm/template/MethodFinallyAdapter.java ================================================ package lsieun.asm.template; import org.objectweb.asm.ClassVisitor; import org.objectweb.asm.MethodVisitor; public class MethodFinallyAdapter extends ClassVisitor { public MethodFinallyAdapter(int api, ClassVisitor cv) { super(api, cv); } @Override public MethodVisitor visitMethod(int access, String name, String descriptor, String signature, String[] exceptions) { MethodVisitor mv = super.visitMethod(access, name, descriptor, signature, exceptions); if (mv != null && "main".equals(name)) { mv = new MethodFinallyConverter(api, mv, access, name, descriptor); } return mv; } } ================================================ FILE: src/main/java/lsieun/asm/template/MethodFinallyAdapterRun.java ================================================ package lsieun.asm.template; import lsieun.utils.FileUtils; import lsieun.utils.ReadUtils; import org.objectweb.asm.ClassReader; import org.objectweb.asm.ClassVisitor; import org.objectweb.asm.ClassWriter; import org.objectweb.asm.Opcodes; public class MethodFinallyAdapterRun { public static void main(String[] args) { String relative_path = "sample/HelloWorld.class"; String filepath = FileUtils.getFilePath(relative_path); byte[] bytes1 = ReadUtils.readByPath(filepath); //(1)构建ClassReader ClassReader cr = new ClassReader(bytes1); //(2)构建ClassWriter ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_FRAMES); //(3)串连ClassVisitor ClassVisitor cv = new MethodFinallyAdapter(Opcodes.ASM9, cw); //(4)两者进行结合 cr.accept(cv, ClassReader.SKIP_FRAMES); //(5)重新生成Class byte[] bytes2 = cw.toByteArray(); FileUtils.writeBytes(filepath, bytes2); } } ================================================ FILE: src/main/java/lsieun/asm/template/MethodFinallyConverter.java ================================================ package lsieun.asm.template; import org.objectweb.asm.Label; import org.objectweb.asm.MethodVisitor; import org.objectweb.asm.Opcodes; import org.objectweb.asm.commons.AdviceAdapter; public class MethodFinallyConverter extends AdviceAdapter { private final String methodName; private final Label tryLabel = new Label(); private final Label finallyLabel = new Label(); protected MethodFinallyConverter(int api, MethodVisitor mv, int access, String name, String descriptor) { super(api, mv, access, name, descriptor); this.methodName = name; } @Override public void visitCode() { super.visitCode(); super.visitLabel(tryLabel); } @Override public void visitMaxs(int maxStack, int maxLocals) { super.visitLabel(finallyLabel); super.visitTryCatchBlock(tryLabel, finallyLabel, finallyLabel, null); onFinally(); super.visitInsn(Opcodes.ATHROW); super.visitMaxs(maxStack, maxLocals); } @Override protected void onMethodExit(int opcode) { if (opcode != Opcodes.ATHROW) { onFinally(); } } private void onFinally() { super.visitFieldInsn(GETSTATIC, "java/lang/System", "err", "Ljava/io/PrintStream;"); super.visitLdcInsn("Exiting " + methodName + " Method"); super.visitMethodInsn(INVOKEVIRTUAL, "java/io/PrintStream", "println", "(Ljava/lang/String;)V", false); } } ================================================ FILE: src/main/java/lsieun/asm/template/MethodPrintInstructionAdapter.java ================================================ package lsieun.asm.template; import lsieun.utils.OpcodeConst; import org.objectweb.asm.Handle; import org.objectweb.asm.Label; import org.objectweb.asm.MethodVisitor; import org.objectweb.asm.Type; import static org.objectweb.asm.Opcodes.*; public class MethodPrintInstructionAdapter extends MethodVisitor { private static final String OPCODE_INSTRUCTION_FORMAT = "%-15s %-6s"; public static final Type OBJECT_TYPE = Type.getType("Ljava/lang/Object;"); private final String methodName; private final String methodDesc; public MethodPrintInstructionAdapter(int api, MethodVisitor methodVisitor, String methodName, String methodDesc) { super(api, methodVisitor); this.methodName = methodName; this.methodDesc = methodDesc; } @Override public void visitCode() { String line = String.format("Method: %s:%s", methodName, methodDesc); printMessage(line); super.visitCode(); } // ILOAD, LLOAD, FLOAD, DLOAD, ALOAD, // ISTORE, LSTORE, FSTORE, DSTORE, ASTORE // or RET. @Override public void visitVarInsn(int opcode, int index) { String instruction = String.format(OPCODE_INSTRUCTION_FORMAT, OpcodeConst.getOpcodeName(opcode), index); printMessage(instruction); if (opcode == ILOAD) { super.visitVarInsn(opcode, index); dupAndPrintValueOnStack(Type.INT_TYPE); } else if (opcode == LLOAD) { super.visitVarInsn(opcode, index); dupAndPrintValueOnStack(Type.LONG_TYPE); } else if (opcode == FLOAD) { super.visitVarInsn(opcode, index); dupAndPrintValueOnStack(Type.FLOAT_TYPE); } else if (opcode == DLOAD) { super.visitVarInsn(opcode, index); dupAndPrintValueOnStack(Type.DOUBLE_TYPE); } else if (opcode == ALOAD) { super.visitVarInsn(opcode, index); dupAndPrintValueOnStack(OBJECT_TYPE); } else if (opcode == ISTORE) { dupAndPrintValueOnStack(Type.INT_TYPE); super.visitVarInsn(opcode, index); } else if (opcode == LSTORE) { dupAndPrintValueOnStack(Type.LONG_TYPE); super.visitVarInsn(opcode, index); } else if (opcode == FSTORE) { dupAndPrintValueOnStack(Type.FLOAT_TYPE); super.visitVarInsn(opcode, index); } else if (opcode == DSTORE) { dupAndPrintValueOnStack(Type.DOUBLE_TYPE); super.visitVarInsn(opcode, index); } else if (opcode == ASTORE) { dupAndPrintValueOnStack(OBJECT_TYPE); super.visitVarInsn(opcode, index); } else { super.visitVarInsn(opcode, index); super.visitLdcInsn("not supported"); printValueOnStack(OBJECT_TYPE); } } @Override public void visitInsn(int opcode) { String instruction = String.format(OPCODE_INSTRUCTION_FORMAT, OpcodeConst.getOpcodeName(opcode), ""); printMessage(instruction); super.visitInsn(opcode); } @Override public void visitIincInsn(int var, int increment) { String opcode_arg = String.format("%s %s", var, increment); String instruction = String.format(OPCODE_INSTRUCTION_FORMAT, "iinc", opcode_arg); printMessage(instruction); super.visitIincInsn(var, increment); } @Override public void visitIntInsn(int opcode, int operand) { String opcode_arg = String.valueOf(operand); String instruction = String.format(OPCODE_INSTRUCTION_FORMAT, OpcodeConst.getOpcodeName(opcode), opcode_arg); printMessage(instruction); super.visitIntInsn(opcode, operand); } @Override public void visitJumpInsn(int opcode, Label label) { String instruction = String.format(OPCODE_INSTRUCTION_FORMAT, OpcodeConst.getOpcodeName(opcode), ""); printMessage(instruction); switch (opcode) { case IFEQ: case IFNE: case IFLT: case IFGE: case IFGT: case IFLE: dupAndPrintValueOnStack(Type.INT_TYPE); break; case IF_ICMPEQ: case IF_ICMPNE: case IF_ICMPLT: case IF_ICMPGE: case IF_ICMPGT: case IF_ICMPLE: super.visitInsn(DUP2); super.visitInsn(POP); printValueOnStack(Type.INT_TYPE); dupAndPrintValueOnStack(Type.INT_TYPE); break; case IF_ACMPEQ: case IF_ACMPNE: super.visitInsn(DUP2); super.visitInsn(POP); printValueOnStack(OBJECT_TYPE); dupAndPrintValueOnStack(OBJECT_TYPE); break; case IFNULL: case IFNONNULL: dupAndPrintValueOnStack(OBJECT_TYPE); break; default: break; } super.visitJumpInsn(opcode, label); } @Override public void visitLdcInsn(Object value) { String instruction = String.format(OPCODE_INSTRUCTION_FORMAT, "LDC", value); printMessage(instruction); super.visitLdcInsn(value); } @Override public void visitTableSwitchInsn(int min, int max, Label dflt, Label... labels) { String instruction = String.format(OPCODE_INSTRUCTION_FORMAT, "tableswitch", ""); printMessage(instruction); super.visitTableSwitchInsn(min, max, dflt, labels); } @Override public void visitLookupSwitchInsn(Label dflt, int[] keys, Label[] labels) { String instruction = String.format(OPCODE_INSTRUCTION_FORMAT, "lookupswitch", ""); printMessage(instruction); super.visitLookupSwitchInsn(dflt, keys, labels); } @Override public void visitMultiANewArrayInsn(String descriptor, int numDimensions) { String opcode_arg = descriptor + ":" + numDimensions; String instruction = String.format(OPCODE_INSTRUCTION_FORMAT, "multianewarray", opcode_arg); printMessage(instruction); super.visitMultiANewArrayInsn(descriptor, numDimensions); } @Override public void visitFieldInsn(int opcode, String owner, String name, String descriptor) { String opcode_arg = String.format("%s.%s:%s", owner, name, descriptor); String instruction = String.format(OPCODE_INSTRUCTION_FORMAT, OpcodeConst.getOpcodeName(opcode), opcode_arg); printMessage(instruction); super.visitFieldInsn(opcode, owner, name, descriptor); } @Override public void visitMethodInsn(int opcode, String owner, String name, String descriptor, boolean isInterface) { String opcode_arg = String.format("%s.%s:%s", owner, name, descriptor); String instruction = String.format(OPCODE_INSTRUCTION_FORMAT, OpcodeConst.getOpcodeName(opcode), opcode_arg); printMessage(instruction); super.visitMethodInsn(opcode, owner, name, descriptor, isInterface); } @Override public void visitInvokeDynamicInsn(String name, String descriptor, Handle bootstrapMethodHandle, Object... bootstrapMethodArguments) { String opcode_arg = String.format("%s:%s", name, descriptor); String instruction = String.format(OPCODE_INSTRUCTION_FORMAT, "invokedynamic", opcode_arg); printMessage(instruction); super.visitInvokeDynamicInsn(name, descriptor, bootstrapMethodHandle, bootstrapMethodArguments); } @Override public void visitTypeInsn(int opcode, String type) { String instruction = String.format(OPCODE_INSTRUCTION_FORMAT, OpcodeConst.getOpcodeName(opcode), type); printMessage(instruction); super.visitTypeInsn(opcode, type); } private void printMessage(String message) { super.visitFieldInsn(GETSTATIC, "java/lang/System", "out", "Ljava/io/PrintStream;"); super.visitLdcInsn(message); super.visitMethodInsn(INVOKEVIRTUAL, "java/io/PrintStream", "println", "(Ljava/lang/String;)V", false); } private void dup(Type t) { int size = t.getSize(); if (size == 1) { super.visitInsn(DUP); } else { super.visitInsn(DUP2); } } private void printValueOnStack(Type t) { int size = t.getSize(); String descriptor = String.format("(%s)V", t.getDescriptor()); super.visitFieldInsn(GETSTATIC, "java/lang/System", "out", "Ljava/io/PrintStream;"); if (size == 1) { super.visitInsn(SWAP); } else { super.visitInsn(DUP_X2); super.visitInsn(POP); } super.visitMethodInsn(INVOKEVIRTUAL, "java/io/PrintStream", "println", descriptor, false); } private void dupAndPrintValueOnStack(Type t) { dup(t); printValueOnStack(t); } } ================================================ FILE: src/main/java/lsieun/asm/template/MethodPrintInstructionVisitor.java ================================================ package lsieun.asm.template; import org.objectweb.asm.ClassVisitor; import org.objectweb.asm.MethodVisitor; import static org.objectweb.asm.Opcodes.ACC_ABSTRACT; import static org.objectweb.asm.Opcodes.ACC_NATIVE; public class MethodPrintInstructionVisitor extends ClassVisitor { public MethodPrintInstructionVisitor(int api, ClassVisitor classVisitor) { super(api, classVisitor); } @Override public MethodVisitor visitMethod(int access, String name, String descriptor, String signature, String[] exceptions) { MethodVisitor mv = super.visitMethod(access, name, descriptor, signature, exceptions); if (mv != null && !"".equals(name) && !"".equals(name)) { boolean isAbstractMethod = (access & ACC_ABSTRACT) != 0; boolean isNativeMethod = (access & ACC_NATIVE) != 0; if (!isAbstractMethod && !isNativeMethod) { mv = new MethodPrintInstructionAdapter(api, mv, name, descriptor); } } return mv; } } ================================================ FILE: src/main/java/lsieun/asm/template/MethodWithSameTryCatchLogicVisitor.java ================================================ package lsieun.asm.template; import org.objectweb.asm.ClassVisitor; import org.objectweb.asm.Label; import org.objectweb.asm.MethodVisitor; import java.util.ArrayList; import java.util.List; import static org.objectweb.asm.Opcodes.*; public class MethodWithSameTryCatchLogicVisitor extends ClassVisitor { public MethodWithSameTryCatchLogicVisitor(int api, ClassVisitor classVisitor) { super(api, classVisitor); } @Override public MethodVisitor visitMethod(int access, String name, String descriptor, String signature, String[] exceptions) { MethodVisitor mv = super.visitMethod(access, name, descriptor, signature, exceptions); if (mv != null) { boolean isAbstractMethod = (access & ACC_ABSTRACT) != 0; boolean isNativeMethod = (access & ACC_NATIVE) != 0; if (!isAbstractMethod && !isNativeMethod) { mv = new MethodWithSameTryCatchLogicAdapter(api, mv); } } return mv; } private static class MethodWithSameTryCatchLogicAdapter extends MethodVisitor { private final List