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
[](https://gitee.com/lsieun/learn-java-asm)
[](https://github.com/lsieun/learn-java-asm)





: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 代码下载
[](https://gitee.com/lsieun/learn-java-asm)
[](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 开发环境
[](./LICENSE)




- [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`。如果想使用最新 版本,可以修改`pom.xml`文件中的`asm.version`属性:
```text
9.0
```
### 1.3. 运行代码
在`learn-java-asm`项目当中,包含`main`方法的类主要位于`run`包(`src/main/java/run`)。
## 2. 课程资料
[](https://edu.51cto.com/lecturer/9210464.html)
[](https://space.bilibili.com/1321054247)
[](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. 联系方式
[](https://lsieun.github.io/assets/images/contact/we-chat.jpg)
[](https://lsieun.github.io/assets/images/contact/qq.png)
[](https://lsieun.github.io/assets/images/contact/qq-group.jpg)
[](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 extends V> 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 extends V> 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 extends V> 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 extends NullabilityValue> 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 extends NullabilityValue> 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 extends StateValue> 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 extends SourceValue> 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