[
  {
    "path": ".gitignore",
    "content": "# Compiled class file\n*.class\n\n# Log file\n*.log\n\n# BlueJ files\n*.ctxt\n\n# Mobile Tools for Java (J2ME)\n.mtj.tmp/\n\n# Package Files #\n*.jar\n*.war\n*.nar\n*.ear\n*.zip\n*.tar.gz\n*.rar\n\n# virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml\nhs_err_pid*\n\n# IDEA\n\n.idea/\n*.iml\n*.kotlin_module\ntarget/\nlib/\nbuild/\ndist/\n.*\n*.data\n!.gitignore\n"
  },
  {
    "path": "LICENSE",
    "content": "MIT License\n\nCopyright (c) 2020 lsieun\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n"
  },
  {
    "path": "README.md",
    "content": "# Learn Java ASM\n\n[![Gitee](https://img.shields.io/badge/Gitee-white.svg?style=flat-square&logo=gitee&logoColor=C71D23)](https://gitee.com/lsieun/learn-java-asm)\n[![Github](https://img.shields.io/badge/GitHub-white.svg?style=flat-square&logo=github&logoColor=181717)](https://github.com/lsieun/learn-java-asm)\n![GitHub](https://img.shields.io/github/license/lsieun/learn-java-asm)\n![GitHub stars](https://img.shields.io/github/stars/lsieun/learn-java-asm.svg)\n![GitHub forks](https://img.shields.io/github/forks/lsieun/learn-java-asm.svg)\n![GitHub issues](https://img.shields.io/github/issues-raw/lsieun/learn-java-asm?label=issues)\n![GitHub last commit](https://img.shields.io/github/last-commit/lsieun/learn-java-asm.svg)\n\n:maple_leaf: Java [ASM](https://asm.ow2.io/) is an open-source java library for manipulating bytecode.\n\n本项目旨在系统地介绍如何学习Java ASM的知识，主要涉及Core API、OPCODE和Tree API等内容。至于学习的预期目标就是，用一个形象的说法来讲，让字节码在你的手中“跳舞”：看看你的左手，一个完整的ClassFile拆解成不同粒度的字节码内容；看看你的右手，不同粒度的字节码内容又重新组织成一个ClassFile结构。\n\n```text\n           _______                                 ,        _        _        \n          (,     /'                              /'/      /' `\\     ' )     _)\n               /'                              /' /     /'   ._)    //  _/~/' \n             /'____ .     ,   ____          ,/'  /     (____      /'/_/~ /'   \n   _       /'/'    )|    /  /'    )        /`--,/           )   /' /~  /'     \n /' `    /'/'    /' |  /' /'    /'       /'    /          /'  /'     /'       \n(_____,/' (___,/(___|/(__(___,/(__   (,/'     (_,(_____,/'(,/'      (_,       \n```\n\n---\n\n如果我们学会了Java ASM之后，可能还是需要一个具体的应用场景来进行使用，\n这个场景就是由 [Java Agent](https://docs.oracle.com/en/java/javase/25/docs/api/java.instrument/java/lang/instrument/Instrumentation.html) 开启的。\n\n那么，Java ASM和Java Agent这两者之间是什么关系呢？\nJava ASM是一个操作字节码的工具（tool），而Java Agent提供了修改字节码的机会（opportunity）。\n想像这样一个场景：\n有一个JVM正在运行，突然Java Agent在JVM上打开一扇大门，Java ASM通过大门冲进JVM里面，就要开始修改字节码了。\n\n```text\n.class --- Java ASM --- Java Agent --- JVM\n```\n\n再打个比方，Java ASM就是“一匹千里马”，而Java Agent就是“伯乐”。\n如果遇不到“伯乐”，可能“千里马”的才能就埋没了；正因为有了“伯乐”，“千里马”就有了施展才能的机会。\n\n```text\n世有伯乐，然后有千里马。\n千里马常有，而伯乐不常有。\n故虽有名马，祗辱于奴隶人之手，骈死于槽枥之间，不以千里称也。\n```\n\n---\n\n## 1. 如何使用\n\n### 1.1 代码下载\n\n[![Gitee](https://img.shields.io/badge/Gitee-white.svg?style=flat-square&logo=gitee&logoColor=C71D23)](https://gitee.com/lsieun/learn-java-asm)\n[![Github](https://img.shields.io/badge/GitHub-white.svg?style=flat-square&logo=github&logoColor=181717)](https://github.com/lsieun/learn-java-asm)\n\n从[Gitee](https://gitee.com/lsieun/learn-java-asm) 仓库下载代码，使用如下命令：\n\n```text\ngit clone https://gitee.com/lsieun/learn-java-asm\n```\n\n从[GitHub](https://github.com/lsieun/learn-java-asm) 仓库下载代码，使用如下命令：\n\n```text\ngit clone https://github.com/lsieun/learn-java-asm\n```\n\n### 1.2 开发环境\n\n[![Licence](https://img.shields.io/github/license/lsieun/learn-java-asm?style=social)](./LICENSE)\n![Git](https://img.shields.io/badge/Git-white.svg?style=flat-square&logo=git&logoColor=F05032)\n![Java](https://img.shields.io/badge/-Java-white.svg?style=flat-square&logo=java&logoColor=007396)\n![Apache Maven](https://img.shields.io/badge/Maven-white.svg?style=flat-square&logo=Apache%20Maven&logoColor=C71A36)\n![IntelliJ IDEA](https://img.shields.io/badge/IntelliJ_IDEA-white.svg?style=flat-square&logo=intellij-idea&logoColor=000000)\n\n- [Git](https://git-scm.com/)\n- [Java 8](https://www.oracle.com/java/technologies/javase/javase8-archive-downloads.html)\n- [Apache Maven](https://maven.apache.org/)\n- [IntelliJ IDEA](https://www.jetbrains.com/idea/download/other.html) (Ultimate or Community Edition)\n\n在`learn-java-asm`项目当中，使用的ASM版本为`9.0`。如果想使用最新![Maven Central](https://img.shields.io/maven-central/v/org.ow2.asm/asm.svg?color=25a162&label=ASM) 版本，可以修改`pom.xml`文件中的`asm.version`属性：\n\n```text\n<asm.version>9.0</asm.version>\n```\n\n### 1.3. 运行代码\n\n在`learn-java-asm`项目当中，包含`main`方法的类主要位于`run`包（`src/main/java/run`）。\n\n## 2. 课程资料\n\n[![51cto](https://img.shields.io/website/https/edu.51cto.com.svg?label=51cto)](https://edu.51cto.com/lecturer/9210464.html)\n[![Bilibili](https://img.shields.io/website/https/bilibili.com.svg?label=bilibili&style=flat-square&logo=bilibili&logoColor=00A1D6)](https://space.bilibili.com/1321054247)\n[![lsieun.github.io](https://img.shields.io/website/https/lsieun.github.io.svg?label=lsieun.github.io)](https://lsieun.github.io)\n\n- 《Java ASM系列一：Core API》\n  - 文章地址： [lsieun.github.io](https://lsieun.github.io/java/asm/java-asm-season-01.html) | [51CTO](https://blog.51cto.com/lsieun/2924583)\n  - 视频地址： [51CTO](https://edu.51cto.com/course/28517.html) | [Bilibili](https://space.bilibili.com/1321054247/channel/seriesdetail?sid=381716)\n- 《Java ASM系列二：OPCODE》\n  - 文章地址：[lsieun.github.io](https://lsieun.github.io/java/asm/java-asm-season-02.html) | [51CTO](https://blog.51cto.com/lsieun/3273965)\n  - 视频地址：[51CTO](https://edu.51cto.com/course/28870.html) | [Bilibili](https://space.bilibili.com/1321054247/channel/seriesdetail?sid=381716)\n- 《Java ASM系列三：Tree API》\n  - 文章地址：[lsieun.github.io](https://lsieun.github.io/java/asm/java-asm-season-03.html) | [51CTO](https://blog.51cto.com/lsieun/4034588)\n  - 视频地址：[51CTO](https://edu.51cto.com/course/29459.html) | [Bilibili](https://space.bilibili.com/1321054247/channel/seriesdetail?sid=381716)\n\n### 2.1. ASM的组成部分\n\n从组成结构上来说，Java ASM有Core API和Tree API两部分组成。\n\n```text\n                                   ┌─── asm.jar\n                                   │\n            ┌─── Core API ─────────┼─── asm-util.jar\n            │                      │\n            │                      └─── asm-commons.jar\nJava ASM ───┤\n            │\n            │                      ┌─── asm-tree.jar\n            └─── Tree API ─────────┤\n                                   └─── asm-analysis.jar\n```\n\n从依赖关系角度上说，Java ASM当中的各个`.jar`之间的依赖关系如下：\n\n```text\n┌────────────────────────────┬─────────────────────────────┐\n│                    ┌───────┴────────┐                    │\n│     util           │    analysis    │         commons    │\n│             ┌──────┴────────────────┴──────┐             │\n│             │             tree             │             │\n├─────────────┴──────────────────────────────┴─────────────┤\n│                           core                           │\n└──────────────────────────────────────────────────────────┘\n```\n\n### 2.2. ASM能够做什么\n\n从应用的角度来说，Java ASM可以进行Class Generation、Class Transformation和Class Analysis三个类型的操作。\n\n```text\n                                   ┌─── find potential bugs\n                                   │\n            ┌─── analysis ─────────┼─── detect unused code\n            │                      │\n            │                      └─── reverse engineer code\n            │\nJava ASM ───┼─── generation\n            │\n            │                      ┌─── optimize programs\n            │                      │\n            └─── transformation ───┼─── obfuscate programs\n                                   │\n                                   └─── insert performance monitoring code\n```\n\n### 2.3. top, null和void\n\n在下表当中，top、null和void三者相对应的转换值：\n\n```text\n┌─────────────┬────────────────────────────┬────────────────────────────────┐\n│   .class    │          ASM Type          │      ASM Value in Frame        │\n├─────────────┼────────────────────────────┼────────────────────────────────┤\n│     top     │            null            │ BasicValue.UNINITIALIZED_VALUE │\n├─────────────┼────────────────────────────┼────────────────────────────────┤\n│ aconst_null │ BasicInterpreter.NULL_TYPE │   BasicValue.REFERENCE_VALUE   │\n├─────────────┼────────────────────────────┼────────────────────────────────┤\n│    void     │       Type.VOID_TYPE       │              null              │\n└─────────────┴────────────────────────────┴────────────────────────────────┘\n```\n\n## 3. 注意事项\n\n### 3.1. 添加typo字典\n\n在编写代码的过程中，会遇到一些Typo提示，原因是`insn`等内容不是合法的单词。\n\n解决方法：借助于IntelliJ IDEA的[Spellchecking](https://www.jetbrains.com/help/idea/spellchecking.html) 的功能。\n\n操作步骤：\n\n- 第一步，在`Settings/Preferences`当中，找到`Editor | Natural Languages | Spelling`位置。\n- 第二步，在右侧的Custom dictionaries位置，添加**custom dictionary**，在`learn-java-asm`项目根目录下，有一个`accepted-words.dic`文件，添加该文件即可。\n\n配置完成之后，需要**重新启动IntelliJ IDEA**才能生效。\n\n### 3.2. 查看笔记\n\n在编写代码的过程中，为了方便理解代码，我添加了一些笔记，格式如下：\n\n```text\nNOTE: 希望这是一条有用的笔记\n```\n\n但是，在默认情况下，它并不会高亮显示，因此不容易被察觉到。\n\n解决方法：借助于IntelliJ IDEA的[TODO comments](https://www.jetbrains.com/help/idea/using-todo.html) 功能。\n\n操作步骤：\n\n- 第一步，在`Settings/Preferences`当中，找到`Editor | TODO`位置。\n- 第二步，在右侧的Patterns位置，添加以下内容：\n\n```text\n\\bnote\\b.*\n```\n\n配置完成之后，需要**重新启动IntelliJ IDEA**才能生效。\n\n### 3.3. 关闭调试信息\n\n在默认情况下，运行任何类，都会输出调试信息。在调试信息中，会带有`[DEBUG]`标识。\n\n如果想关闭调试信息，可以修改`lsieun.cst.Const`类的`DEBUG`字段值为`false`（默认值为`true`）：\n\n```java\npublic class Const {\n    public static final boolean DEBUG = false;\n}\n```\n\n然后，执行`mvn clean compile`对类进行重新编译：\n\n```text\nmvn clean compile\n```\n\n等待编译完成之后，再次运行程序。\n\n## 4. 交流反馈\n\n- 如果您有好的想法，可以提issues\n- 如果您想贡献代码，可以进行fork\n- 如果您有其它问题，可以添加QQ群（参考联系方式）\n\n## 5. 联系方式\n\n[![wechat](https://img.shields.io/badge/-lsieun-white.svg?style=flat-square&logo=wechat&logoColor=07C160)](https://lsieun.github.io/assets/images/contact/we-chat.jpg)\n[![Tencent QQ](https://img.shields.io/badge/515882294-white.svg?style=flat-square&logo=tencentqq&logoColor=EB1923)](https://lsieun.github.io/assets/images/contact/qq.png)\n[![QQ Group](https://img.shields.io/badge/584642776-white.svg?style=flat-square&logo=tencentqq&logoColor=1DA1F2&label=QQ%20Group)](https://lsieun.github.io/assets/images/contact/qq-group.jpg)\n[![Java字节码交流QQ群](https://pub.idqqimg.com/wpa/images/group.png)](https://jq.qq.com/?_wv=1027&k=yOBiOaJV)\n\n## 6. License\n\nThis project is licensed under the MIT License.\nSee the [LICENSE](./LICENSE) file for the full license text.\n"
  },
  {
    "path": "accepted-words.dic",
    "content": "bilibili\ngitee\ninsn\nlsieun\n"
  },
  {
    "path": "pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <modelVersion>4.0.0</modelVersion>\n\n    <groupId>lsieun</groupId>\n    <artifactId>learn-java-asm</artifactId>\n    <version>1.0-SNAPSHOT</version>\n\n    <properties>\n        <!-- Resource -->\n        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>\n        <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>\n\n        <!-- JDK -->\n        <java.version>1.8</java.version>\n        <maven.compiler.source>${java.version}</maven.compiler.source>\n        <maven.compiler.target>${java.version}</maven.compiler.target>\n        <asm.version>9.9.1</asm.version>\n    </properties>\n\n    <dependencies>\n        <dependency>\n            <groupId>org.ow2.asm</groupId>\n            <artifactId>asm</artifactId>\n            <version>${asm.version}</version>\n        </dependency>\n        <dependency>\n            <groupId>org.ow2.asm</groupId>\n            <artifactId>asm-commons</artifactId>\n            <version>${asm.version}</version>\n        </dependency>\n        <dependency>\n            <groupId>org.ow2.asm</groupId>\n            <artifactId>asm-util</artifactId>\n            <version>${asm.version}</version>\n        </dependency>\n        <dependency>\n            <groupId>org.ow2.asm</groupId>\n            <artifactId>asm-tree</artifactId>\n            <version>${asm.version}</version>\n        </dependency>\n        <dependency>\n            <groupId>org.ow2.asm</groupId>\n            <artifactId>asm-analysis</artifactId>\n            <version>${asm.version}</version>\n        </dependency>\n    </dependencies>\n\n    <build>\n        <plugins>\n            <!-- Java Compiler -->\n            <plugin>\n                <groupId>org.apache.maven.plugins</groupId>\n                <artifactId>maven-compiler-plugin</artifactId>\n                <version>3.13.0</version>\n                <configuration>\n                    <source>${java.version}</source>\n                    <target>${java.version}</target>\n                    <fork>true</fork>\n                    <compilerArgs>\n                        <arg>-g</arg>\n                        <arg>-parameters</arg>\n                    </compilerArgs>\n                </configuration>\n            </plugin>\n\n            <!-- 生成 source code 的 JAR 包 -->\n            <plugin>\n                <groupId>org.apache.maven.plugins</groupId>\n                <artifactId>maven-source-plugin</artifactId>\n                <version>3.3.1</version>\n                <executions>\n                    <execution>\n                        <id>default-source-jar</id>\n                        <phase>package</phase>\n                        <goals>\n                            <goal>jar</goal>\n                        </goals>\n                    </execution>\n                </executions>\n            </plugin>\n        </plugins>\n    </build>\n\n</project>"
  },
  {
    "path": "src/main/java/lsieun/annotation/todo/ToDo.java",
    "content": "package lsieun.annotation.todo;\n\nimport java.lang.annotation.Retention;\nimport java.lang.annotation.RetentionPolicy;\nimport java.lang.annotation.Target;\n\nimport static java.lang.annotation.ElementType.*;\n\n@Retention(RetentionPolicy.SOURCE)\n@Target({\n        TYPE,\n        FIELD,\n        CONSTRUCTOR,\n        METHOD,\n        PARAMETER,\n        LOCAL_VARIABLE,\n        ANNOTATION_TYPE,\n        PACKAGE,\n        TYPE_PARAMETER,\n        TYPE_USE,\n//        MODULE,\n//        RECORD_COMPONENT,\n})\npublic @interface ToDo {\n    String[] value();\n}"
  },
  {
    "path": "src/main/java/lsieun/asm/analysis/ControlFlowAnalyzer.java",
    "content": "package lsieun.asm.analysis;\n\nimport org.objectweb.asm.Opcodes;\nimport org.objectweb.asm.tree.*;\nimport org.objectweb.asm.tree.analysis.AnalyzerException;\n\nimport java.util.ArrayList;\nimport java.util.List;\n\n/**\n * 将 {@link org.objectweb.asm.tree.analysis.Analyzer}类的control flow analysis功能抽取出来.\n *\n * NOTE: 当前类抛弃了所有泛型相关的信息(V)，抛弃了Frame、抛弃了Interpreter、抛弃了Value。\n *       但是，它仍然能够实现control flow analysis的功能。\n *\n * @see org.objectweb.asm.tree.analysis.Analyzer\n */\npublic class ControlFlowAnalyzer implements Opcodes {\n\n    private InsnList insnList;\n    private int insnListSize;\n    private List<TryCatchBlockNode>[] handlers;\n\n    /**\n     * 记录需要处理的instructions.\n     *\n     * NOTE: 这三个字段为一组，应该一起处理，最好是放到同一个方法里来处理。\n     * 因此，我就加了三个新方法。\n     * {@link #initInstructionsToProcess()}、{@link #addInstructionsToProcess(int)}和\n     * {@link #removeInstructionsToProcess()}\n     *\n     * @see #initInstructionsToProcess()\n     * @see #addInstructionsToProcess(int)\n     * @see #removeInstructionsToProcess()\n     */\n    private boolean[] inInstructionsToProcess;\n    private int[] instructionsToProcess;\n    private int numInstructionsToProcess;\n\n    public ControlFlowAnalyzer() {\n    }\n\n    public List<TryCatchBlockNode> getHandlers(final int insnIndex) {\n        return handlers[insnIndex];\n    }\n\n\n    @SuppressWarnings(\"unchecked\")\n    // NOTE: analyze方法的返回值类型变成了void类型。\n    public void analyze(final String owner, final MethodNode method) throws AnalyzerException {\n        if ((method.access & (ACC_ABSTRACT | ACC_NATIVE)) != 0) {\n            return;\n        }\n        insnList = method.instructions;\n        insnListSize = insnList.size();\n        handlers = (List<TryCatchBlockNode>[]) new List<?>[insnListSize];\n\n        initInstructionsToProcess();\n\n        // For each exception handler, and each instruction within its range, record in 'handlers' the\n        // fact that execution can flow from this instruction to the exception handler.\n        for (int i = 0; i < method.tryCatchBlocks.size(); ++i) {\n            TryCatchBlockNode tryCatchBlock = method.tryCatchBlocks.get(i);\n            int startIndex = insnList.indexOf(tryCatchBlock.start);\n            int endIndex = insnList.indexOf(tryCatchBlock.end);\n            for (int j = startIndex; j < endIndex; ++j) {\n                List<TryCatchBlockNode> insnHandlers = handlers[j];\n                if (insnHandlers == null) {\n                    insnHandlers = new ArrayList<>();\n                    handlers[j] = insnHandlers;\n                }\n                insnHandlers.add(tryCatchBlock);\n            }\n        }\n\n\n        // Initializes the data structures for the control flow analysis.\n        // NOTE: 调用addInstructionsToProcess方法，传入参数0，启动整个循环过程。\n        addInstructionsToProcess(0);\n        init(owner, method);\n\n        // Control flow analysis.\n        while (numInstructionsToProcess > 0) {\n            // Get and remove one instruction from the list of instructions to process.\n            int insnIndex = removeInstructionsToProcess();\n\n            // Simulate the execution of this instruction.\n            AbstractInsnNode insnNode = method.instructions.get(insnIndex);\n            int insnOpcode = insnNode.getOpcode();\n            int insnType = insnNode.getType();\n\n            if (insnType == AbstractInsnNode.LABEL\n                    || insnType == AbstractInsnNode.LINE\n                    || insnType == AbstractInsnNode.FRAME) {\n                newControlFlowEdge(insnIndex, insnIndex + 1);\n            }\n            else {\n                if (insnNode instanceof JumpInsnNode) {\n                    JumpInsnNode jumpInsn = (JumpInsnNode) insnNode;\n                    if (insnOpcode != GOTO && insnOpcode != JSR) {\n                        newControlFlowEdge(insnIndex, insnIndex + 1);\n                    }\n                    int jumpInsnIndex = insnList.indexOf(jumpInsn.label);\n                    newControlFlowEdge(insnIndex, jumpInsnIndex);\n                }\n                else if (insnNode instanceof LookupSwitchInsnNode) {\n                    LookupSwitchInsnNode lookupSwitchInsn = (LookupSwitchInsnNode) insnNode;\n                    int targetInsnIndex = insnList.indexOf(lookupSwitchInsn.dflt);\n                    newControlFlowEdge(insnIndex, targetInsnIndex);\n                    for (int i = 0; i < lookupSwitchInsn.labels.size(); ++i) {\n                        LabelNode label = lookupSwitchInsn.labels.get(i);\n                        targetInsnIndex = insnList.indexOf(label);\n                        newControlFlowEdge(insnIndex, targetInsnIndex);\n                    }\n                }\n                else if (insnNode instanceof TableSwitchInsnNode) {\n                    TableSwitchInsnNode tableSwitchInsn = (TableSwitchInsnNode) insnNode;\n                    int targetInsnIndex = insnList.indexOf(tableSwitchInsn.dflt);\n                    newControlFlowEdge(insnIndex, targetInsnIndex);\n                    for (int i = 0; i < tableSwitchInsn.labels.size(); ++i) {\n                        LabelNode label = tableSwitchInsn.labels.get(i);\n                        targetInsnIndex = insnList.indexOf(label);\n                        newControlFlowEdge(insnIndex, targetInsnIndex);\n                    }\n                }\n                else if (insnOpcode != ATHROW && (insnOpcode < IRETURN || insnOpcode > RETURN)) {\n                    newControlFlowEdge(insnIndex, insnIndex + 1);\n                }\n            }\n\n            List<TryCatchBlockNode> insnHandlers = handlers[insnIndex];\n            if (insnHandlers != null) {\n                for (TryCatchBlockNode tryCatchBlock : insnHandlers) {\n                    newControlFlowExceptionEdge(insnIndex, tryCatchBlock);\n                }\n            }\n\n        }\n    }\n\n\n    protected void init(final String owner, final MethodNode method) throws AnalyzerException {\n        // Nothing to do.\n    }\n\n    // NOTE: 这是一个新添加的方法\n    private void initInstructionsToProcess() {\n        inInstructionsToProcess = new boolean[insnListSize];\n        instructionsToProcess = new int[insnListSize];\n        numInstructionsToProcess = 0;\n    }\n\n    // NOTE: 这是一个新添加的方法\n    private int removeInstructionsToProcess() {\n        int insnIndex = this.instructionsToProcess[--numInstructionsToProcess];\n        inInstructionsToProcess[insnIndex] = false;\n        return insnIndex;\n    }\n\n    // NOTE: 这是一个新添加的方法\n    private void addInstructionsToProcess(final int insnIndex) {\n        if (!inInstructionsToProcess[insnIndex]) {\n            inInstructionsToProcess[insnIndex] = true;\n            instructionsToProcess[numInstructionsToProcess++] = insnIndex;\n        }\n    }\n\n    protected void newControlFlowEdge(final int insnIndex, final int successorIndex) {\n        // Nothing to do.\n        addInstructionsToProcess(successorIndex);\n    }\n\n    protected void newControlFlowExceptionEdge(final int insnIndex, final TryCatchBlockNode tryCatchBlock) {\n        newControlFlowExceptionEdge(insnIndex, insnList.indexOf(tryCatchBlock.handler));\n    }\n\n    protected void newControlFlowExceptionEdge(final int insnIndex, final int successorIndex) {\n        // Nothing to do.\n        addInstructionsToProcess(successorIndex);\n    }\n}\n"
  },
  {
    "path": "src/main/java/lsieun/asm/analysis/ControlFlowAnalyzer2.java",
    "content": "package lsieun.asm.analysis;\n\nimport lsieun.asm.analysis.graph.InsnBlock;\nimport org.objectweb.asm.tree.AbstractInsnNode;\nimport org.objectweb.asm.tree.MethodNode;\nimport org.objectweb.asm.tree.analysis.AnalyzerException;\n\nimport java.util.List;\n\npublic class ControlFlowAnalyzer2 extends ControlFlowAnalyzer{\n    private AbstractInsnNode[] nodeArray;\n    public InsnBlock[] blocks;\n\n    public ControlFlowAnalyzer2() {\n\n    }\n\n    @Override\n    public void analyze(String owner, MethodNode method) throws AnalyzerException {\n        nodeArray = method.instructions.toArray();\n        int length = nodeArray.length;\n        blocks = new InsnBlock[length];\n        InsnText insnText = new InsnText();\n        for (int i = 0; i < length; i++) {\n            blocks[i] = getBlock(i);\n            AbstractInsnNode node = nodeArray[i];\n            List<String> lines = insnText.toLines(node);\n            blocks[i].addLines(lines);\n        }\n\n        super.analyze(owner, method);\n    }\n\n    @Override\n    protected void newControlFlowEdge(int insnIndex, int successorIndex) {\n        // 首先，处理自己的代码逻辑\n        AbstractInsnNode insnNode = nodeArray[insnIndex];\n        int insnOpcode = insnNode.getOpcode();\n        int insnType = insnNode.getType();\n\n        if (insnType == AbstractInsnNode.JUMP_INSN) {\n            if ((insnIndex + 1) == successorIndex) {\n                addNext(insnIndex, successorIndex);\n            }\n            else {\n                addJump(insnIndex, successorIndex);\n            }\n        }\n        else if (insnOpcode == LOOKUPSWITCH) {\n            addJump(insnIndex, successorIndex);\n        }\n        else if (insnOpcode == TABLESWITCH) {\n            addJump(insnIndex, successorIndex);\n        }\n        else if (insnOpcode == RET) {\n            addJump(insnIndex, successorIndex);\n        }\n        else if (insnOpcode == ATHROW || (insnOpcode >= IRETURN && insnOpcode <= RETURN)) {\n            assert false : \"should not be here\";\n            removeNextAndJump(insnIndex);\n        }\n        else {\n            addNext(insnIndex, successorIndex);\n        }\n\n        // 其次，调用父类的方法实现\n        super.newControlFlowEdge(insnIndex, successorIndex);\n    }\n\n    private void addNext(int fromIndex, int toIndex) {\n        InsnBlock currentBlock = getBlock(fromIndex);\n        InsnBlock nextBlock = getBlock(toIndex);\n        currentBlock.addNext(nextBlock);\n    }\n\n    private void addJump(int fromIndex, int toIndex) {\n        InsnBlock currentBlock = getBlock(fromIndex);\n        InsnBlock nextBlock = getBlock(toIndex);\n        currentBlock.addJump(nextBlock);\n    }\n\n    private void removeNextAndJump(int insnIndex) {\n        InsnBlock currentBlock = getBlock(insnIndex);\n        currentBlock.nextBlockList.clear();\n        currentBlock.jumpBlockList.clear();\n    }\n\n    private InsnBlock getBlock(int insnIndex) {\n        InsnBlock block = blocks[insnIndex];\n        if (block == null){\n            block = new InsnBlock();\n            blocks[insnIndex] = block;\n        }\n        return block;\n    }\n\n    public InsnBlock[] getBlocks() {\n        return blocks;\n    }\n}\n"
  },
  {
    "path": "src/main/java/lsieun/asm/analysis/ControlFlowEdgeAnalyzer.java",
    "content": "package lsieun.asm.analysis;\n\nimport lsieun.asm.analysis.graph.InsnBlock;\nimport org.objectweb.asm.tree.AbstractInsnNode;\nimport org.objectweb.asm.tree.MethodNode;\nimport org.objectweb.asm.tree.analysis.*;\n\nimport java.util.List;\n\npublic class ControlFlowEdgeAnalyzer<V extends Value> extends Analyzer<V> {\n    private AbstractInsnNode[] nodeArray;\n    public InsnBlock[] blocks;\n\n    public ControlFlowEdgeAnalyzer(Interpreter<V> interpreter) {\n        super(interpreter);\n    }\n\n    @Override\n    public Frame<V>[] analyze(String owner, MethodNode method) throws AnalyzerException {\n        nodeArray = method.instructions.toArray();\n        int length = nodeArray.length;\n        blocks = new InsnBlock[length];\n        InsnText insnText = new InsnText();\n        for (int i = 0; i < length; i++) {\n            blocks[i] = getBlock(i);\n            AbstractInsnNode node = nodeArray[i];\n            List<String> lines = insnText.toLines(node);\n            blocks[i].addLines(lines);\n        }\n\n        return super.analyze(owner, method);\n    }\n\n    @Override\n    protected void newControlFlowEdge(int insnIndex, int successorIndex) {\n        // 首先，处理自己的代码逻辑\n        AbstractInsnNode insnNode = nodeArray[insnIndex];\n        int insnOpcode = insnNode.getOpcode();\n        int insnType = insnNode.getType();\n\n        if (insnType == AbstractInsnNode.JUMP_INSN) {\n            if ((insnIndex + 1) == successorIndex) {\n                addNext(insnIndex, successorIndex);\n            }\n            else {\n                addJump(insnIndex, successorIndex);\n            }\n        }\n        else if (insnOpcode == LOOKUPSWITCH) {\n            addJump(insnIndex, successorIndex);\n        }\n        else if (insnOpcode == TABLESWITCH) {\n            addJump(insnIndex, successorIndex);\n        }\n        else if (insnOpcode == RET) {\n            addJump(insnIndex, successorIndex);\n        }\n        else if (insnOpcode == ATHROW || (insnOpcode >= IRETURN && insnOpcode <= RETURN)) {\n            assert false : \"should not be here\";\n            removeNextAndJump(insnIndex);\n        }\n        else {\n            addNext(insnIndex, successorIndex);\n        }\n\n        // 其次，调用父类的方法实现\n        super.newControlFlowEdge(insnIndex, successorIndex);\n    }\n\n    private void addNext(int fromIndex, int toIndex) {\n        InsnBlock currentBlock = getBlock(fromIndex);\n        InsnBlock nextBlock = getBlock(toIndex);\n        currentBlock.addNext(nextBlock);\n    }\n\n    private void addJump(int fromIndex, int toIndex) {\n        InsnBlock currentBlock = getBlock(fromIndex);\n        InsnBlock nextBlock = getBlock(toIndex);\n        currentBlock.addJump(nextBlock);\n    }\n\n    private void removeNextAndJump(int insnIndex) {\n        InsnBlock currentBlock = getBlock(insnIndex);\n        currentBlock.nextBlockList.clear();\n        currentBlock.jumpBlockList.clear();\n    }\n\n    private InsnBlock getBlock(int insnIndex) {\n        InsnBlock block = blocks[insnIndex];\n        if (block == null){\n            block = new InsnBlock();\n            blocks[insnIndex] = block;\n        }\n        return block;\n    }\n\n    public InsnBlock[] getBlocks() {\n        return blocks;\n    }\n\n}\n"
  },
  {
    "path": "src/main/java/lsieun/asm/analysis/ControlFlowEdgeAnalyzer2.java",
    "content": "package lsieun.asm.analysis;\n\nimport lsieun.asm.analysis.graph.InsnBlock;\nimport org.objectweb.asm.tree.analysis.Interpreter;\nimport org.objectweb.asm.tree.analysis.Value;\n\nimport java.util.ArrayList;\nimport java.util.HashSet;\nimport java.util.List;\nimport java.util.Set;\n\npublic class ControlFlowEdgeAnalyzer2<V extends Value> extends ControlFlowEdgeAnalyzer<V> {\n\n    public ControlFlowEdgeAnalyzer2(Interpreter<V> interpreter) {\n        super(interpreter);\n    }\n\n    @Override\n    public InsnBlock[] getBlocks() {\n        //（1）调用父类实现\n        InsnBlock[] blocks = super.getBlocks();\n\n        //（2）如果结果为空，则提前返回\n        if (blocks == null || blocks.length < 1) {\n            return blocks;\n        }\n\n        //（3）记录“分隔点”\n        Set<InsnBlock> newBlockSet = new HashSet<>();\n        int length = blocks.length;\n        for (int i = 0; i < length; i++) {\n            InsnBlock currentBlock = blocks[i];\n            List<InsnBlock> nextBlockList = currentBlock.nextBlockList;\n            List<InsnBlock> jumpBlockList = currentBlock.jumpBlockList;\n\n            boolean hasNext = false;\n            boolean hasJump = false;\n\n            if (nextBlockList.size() > 0) {\n                hasNext = true;\n            }\n\n            if (jumpBlockList.size() > 0) {\n                hasJump = true;\n            }\n\n            if (!hasNext && (i + 1) < length) {\n                newBlockSet.add(blocks[i + 1]);\n            }\n\n            if (hasJump) {\n                newBlockSet.addAll(jumpBlockList);\n\n                if (hasNext) {\n                    newBlockSet.add(blocks[i + 1]);\n                }\n            }\n        }\n\n        //（4）利用“分隔点”，合并成不同的分组\n        List<InsnBlock> resultList = new ArrayList<>();\n        for (int i = 0; i < length; i++) {\n            InsnBlock currentBlock = blocks[i];\n\n            if (i == 0) {\n                resultList.add(currentBlock);\n            }\n            else if (newBlockSet.contains(currentBlock)) {\n                resultList.add(currentBlock);\n            }\n            else {\n                int size = resultList.size();\n                InsnBlock lastBlock = resultList.get(size - 1);\n                lastBlock.lines.addAll(currentBlock.lines);\n                lastBlock.jumpBlockList.clear();\n                lastBlock.jumpBlockList.addAll(currentBlock.jumpBlockList);\n                lastBlock.nextBlockList.clear();\n                lastBlock.nextBlockList.addAll(currentBlock.nextBlockList);\n            }\n        }\n\n        return resultList.toArray(new InsnBlock[0]);\n    }\n}\n"
  },
  {
    "path": "src/main/java/lsieun/asm/analysis/ControlFlowGraphAnalyzer.java",
    "content": "package lsieun.asm.analysis;\n\nimport lsieun.asm.analysis.graph.InsnBlock;\nimport org.objectweb.asm.tree.*;\n\nimport java.util.*;\n\nimport static org.objectweb.asm.Opcodes.*;\n\npublic class ControlFlowGraphAnalyzer {\n    private final Set<LabelNode> jumpLabelSet = new HashSet<>();\n    private final Map<LabelNode, Set<InsnBlock>> jumpLabelMap = new HashMap<>();\n    private final Map<LabelNode, InsnBlock> labelInBlockMap = new HashMap<>();\n    private final InsnText insnText = new InsnText();\n\n    private InsnBlock currentBlock;\n    private final List<AbstractInsnNode> nodeList = new ArrayList<>();\n    private final List<InsnBlock> blockList = new ArrayList<>();\n\n    public ControlFlowGraphAnalyzer() {\n        this.currentBlock = new InsnBlock();\n    }\n\n    public void analyze(MethodNode mn) {\n        findJumpLabel(mn);\n        instruction2Block(mn);\n        linkBlocks();\n    }\n\n\n    private void findJumpLabel(MethodNode mn) {\n        InsnList instructions = mn.instructions;\n\n        for (AbstractInsnNode node : instructions) {\n            if (node instanceof JumpInsnNode) {\n                // 当前block与跳转目标的关系\n                JumpInsnNode currentNode = (JumpInsnNode) node;\n                jumpLabelSet.add(currentNode.label);\n            }\n            else if (node instanceof TableSwitchInsnNode) {\n                // 当前block与跳转目标的关系\n                TableSwitchInsnNode currentNode = (TableSwitchInsnNode) node;\n                jumpLabelSet.add(currentNode.dflt);\n                jumpLabelSet.addAll(currentNode.labels);\n            }\n            else if (node instanceof LookupSwitchInsnNode) {\n                // 当前block与跳转目标的关系\n                LookupSwitchInsnNode currentNode = (LookupSwitchInsnNode) node;\n                jumpLabelSet.add(currentNode.dflt);\n                jumpLabelSet.addAll(currentNode.labels);\n            }\n        }\n\n        List<TryCatchBlockNode> tryCatchBlocks = mn.tryCatchBlocks;\n        for (TryCatchBlockNode node : tryCatchBlocks) {\n            jumpLabelSet.add(node.handler);\n        }\n    }\n\n    private void instruction2Block(MethodNode mn) {\n        InsnList instructions = mn.instructions;\n\n        for (AbstractInsnNode node : instructions) {\n            int opcode = node.getOpcode();\n\n            if (node instanceof JumpInsnNode) {\n                nodeList.add(node);\n\n                // 当前block收集数据完成\n                completeBlock();\n\n                // 当前block与跳转目标的关系\n                JumpInsnNode currentNode = (JumpInsnNode) node;\n                addJumpFromBlockToLabel(currentNode.label);\n\n                // 当前block与下一个block的关系\n                InsnBlock nextBlock = new InsnBlock();\n                if ((opcode >= IFEQ && opcode <= IF_ACMPNE) || (opcode >= IFNULL && opcode <= IFNONNULL)) {\n                    currentBlock.nextBlockList.add(nextBlock);\n                }\n\n                // 下一个block成为当前block\n                currentBlock = nextBlock;\n            }\n            else if (node instanceof TableSwitchInsnNode) {\n                nodeList.add(node);\n\n                // 当前block收集数据完成\n                completeBlock();\n\n                // 当前block与跳转目标的关系\n                TableSwitchInsnNode currentNode = (TableSwitchInsnNode) node;\n                int min = currentNode.min;\n                int max = currentNode.max;\n                for (int i = min; i <= max; i++) {\n                    addJumpFromBlockToLabel(currentNode.labels.get(i - min));\n                }\n                addJumpFromBlockToLabel(currentNode.dflt);\n\n                // 下一个block成为当前block\n                InsnBlock nextBlock = new InsnBlock();\n                currentBlock = nextBlock;\n            }\n            else if (node instanceof LookupSwitchInsnNode) {\n                nodeList.add(node);\n\n                // 当前block收集数据完成\n                completeBlock();\n\n                // 当前block与跳转目标的关系\n                LookupSwitchInsnNode currentNode = (LookupSwitchInsnNode) node;\n                List<LabelNode> labels = currentNode.labels;\n                for (LabelNode labelNode : labels) {\n                    addJumpFromBlockToLabel(labelNode);\n                }\n                addJumpFromBlockToLabel(currentNode.dflt);\n\n                // 下一个block成为当前block\n                InsnBlock nextBlock = new InsnBlock();\n                currentBlock = nextBlock;\n            }\n            else if (node instanceof LabelNode) {\n                LabelNode currentNode = (LabelNode) node;\n\n                if (jumpLabelSet.contains(currentNode)) {\n                    if (nodeList.size() > 0) {\n                        // 当前block收集数据完成\n                        completeBlock();\n\n                        // 下一个block成为当前block\n                        InsnBlock nextBlock = new InsnBlock();\n                        currentBlock.nextBlockList.add(nextBlock);\n                        currentBlock = nextBlock;\n                    }\n\n                }\n\n                nodeList.add(node);\n                labelInBlockMap.put(currentNode, currentBlock);\n            }\n            else if (node instanceof InsnNode) {\n                nodeList.add(node);\n\n                // 当前block收集数据完成\n                if ((opcode >= IRETURN && opcode <= RETURN) || (opcode == ATHROW)) {\n                    completeBlock();\n\n                    InsnBlock nextBlock = new InsnBlock();\n                    currentBlock = nextBlock;\n                }\n\n            }\n            else {\n                nodeList.add(node);\n            }\n        }\n\n        if (nodeList.size() > 0) {\n            // 当前block收集数据完成\n            completeBlock();\n        }\n    }\n\n    private void linkBlocks() {\n        for (Map.Entry<LabelNode, Set<InsnBlock>> item : jumpLabelMap.entrySet()) {\n            LabelNode key = item.getKey();\n            Set<InsnBlock> set = item.getValue();\n\n            InsnBlock targetBlock = labelInBlockMap.get(key);\n            for (InsnBlock block : set) {\n                block.jumpBlockList.add(targetBlock);\n            }\n        }\n    }\n\n\n    private void addJumpFromBlockToLabel(LabelNode labelNode) {\n        Set<InsnBlock> list = jumpLabelMap.get(labelNode);\n        if (list != null) {\n            list.add(currentBlock);\n\n        }\n        else {\n            list = new HashSet<>();\n            list.add(currentBlock);\n            jumpLabelMap.put(labelNode, list);\n        }\n    }\n\n    private void completeBlock() {\n        for (AbstractInsnNode node : nodeList) {\n            List<String> lines = insnText.toLines(node);\n            currentBlock.addLines(lines);\n        }\n\n        nodeList.clear();\n        blockList.add(currentBlock);\n    }\n\n    public InsnBlock[] getBlocks() {\n        return blockList.toArray(new InsnBlock[0]);\n    }\n\n}\n"
  },
  {
    "path": "src/main/java/lsieun/asm/analysis/DataFlowAnalyzer.java",
    "content": "package lsieun.asm.analysis;\n\nimport org.objectweb.asm.Opcodes;\nimport org.objectweb.asm.Type;\nimport org.objectweb.asm.tree.*;\nimport org.objectweb.asm.tree.analysis.AnalyzerException;\nimport org.objectweb.asm.tree.analysis.Frame;\nimport org.objectweb.asm.tree.analysis.Interpreter;\nimport org.objectweb.asm.tree.analysis.Value;\n\nimport java.util.ArrayList;\nimport java.util.List;\n\npublic class DataFlowAnalyzer<V extends Value> implements Opcodes {\n\n    private final Interpreter<V> interpreter;\n\n    // 数据输入：指令集和异常处理\n    private InsnList insnList;\n    private int insnListSize;\n    private List<TryCatchBlockNode>[] handlers;\n\n    // 中间状态：记录需要哪一个指令需要处理\n    private boolean[] inInstructionsToProcess;\n    private int[] instructionsToProcess;\n    private int numInstructionsToProcess;\n\n    // 数据输出：最终的返回结果\n    private Frame<V>[] frames;\n\n    public DataFlowAnalyzer(final Interpreter<V> interpreter) {\n        this.interpreter = interpreter;\n    }\n\n    public List<TryCatchBlockNode> getHandlers(final int insnIndex) {\n        return handlers[insnIndex];\n    }\n\n    public Frame<V>[] getFrames() {\n        return frames;\n    }\n\n\n    @SuppressWarnings(\"unchecked\")\n    public Frame<V>[] analyze(final String owner, final MethodNode method) throws AnalyzerException {\n        if ((method.access & (ACC_ABSTRACT | ACC_NATIVE)) != 0) {\n            frames = (Frame<V>[]) new Frame<?>[0];\n            return frames;\n        }\n        insnList = method.instructions;\n        insnListSize = insnList.size();\n        handlers = (List<TryCatchBlockNode>[]) new List<?>[insnListSize];\n        frames = (Frame<V>[]) new Frame<?>[insnListSize];\n        inInstructionsToProcess = new boolean[insnListSize];\n        instructionsToProcess = new int[insnListSize];\n        numInstructionsToProcess = 0;\n\n        // For each exception handler, and each instruction within its range, record in 'handlers' the\n        // fact that execution can flow from this instruction to the exception handler.\n        for (int i = 0; i < method.tryCatchBlocks.size(); ++i) {\n            TryCatchBlockNode tryCatchBlock = method.tryCatchBlocks.get(i);\n            int startIndex = insnList.indexOf(tryCatchBlock.start);\n            int endIndex = insnList.indexOf(tryCatchBlock.end);\n            for (int j = startIndex; j < endIndex; ++j) {\n                List<TryCatchBlockNode> insnHandlers = handlers[j];\n                if (insnHandlers == null) {\n                    insnHandlers = new ArrayList<>();\n                    handlers[j] = insnHandlers;\n                }\n                insnHandlers.add(tryCatchBlock);\n            }\n        }\n\n        // Initializes the data structures for the control flow analysis.\n        Frame<V> currentFrame = computeInitialFrame(owner, method);\n        merge(0, currentFrame);\n        init(owner, method);\n\n        // Control flow analysis.\n        while (numInstructionsToProcess > 0) {\n            // Get and remove one instruction from the list of instructions to process.\n            int insnIndex = instructionsToProcess[--numInstructionsToProcess];\n            Frame<V> oldFrame = frames[insnIndex];\n            inInstructionsToProcess[insnIndex] = false;\n\n            // Simulate the execution of this instruction.\n            AbstractInsnNode insnNode = null;\n            try {\n                insnNode = method.instructions.get(insnIndex);\n                int insnOpcode = insnNode.getOpcode();\n                int insnType = insnNode.getType();\n\n                if (insnType == AbstractInsnNode.LABEL\n                        || insnType == AbstractInsnNode.LINE\n                        || insnType == AbstractInsnNode.FRAME) {\n                    merge(insnIndex + 1, oldFrame);\n                    newControlFlowEdge(insnIndex, insnIndex + 1);\n                }\n                else {\n                    currentFrame.init(oldFrame).execute(insnNode, interpreter);\n\n                    if (insnNode instanceof JumpInsnNode) {\n                        JumpInsnNode jumpInsn = (JumpInsnNode) insnNode;\n                        if (insnOpcode != GOTO && insnOpcode != JSR) {\n                            currentFrame.initJumpTarget(insnOpcode, /* target = */ null);\n                            merge(insnIndex + 1, currentFrame);\n                            newControlFlowEdge(insnIndex, insnIndex + 1);\n                        }\n                        int jumpInsnIndex = insnList.indexOf(jumpInsn.label);\n                        currentFrame.initJumpTarget(insnOpcode, jumpInsn.label);\n                        merge(jumpInsnIndex, currentFrame);\n                        newControlFlowEdge(insnIndex, jumpInsnIndex);\n                    }\n                    else if (insnNode instanceof LookupSwitchInsnNode) {\n                        LookupSwitchInsnNode lookupSwitchInsn = (LookupSwitchInsnNode) insnNode;\n                        int targetInsnIndex = insnList.indexOf(lookupSwitchInsn.dflt);\n                        currentFrame.initJumpTarget(insnOpcode, lookupSwitchInsn.dflt);\n                        merge(targetInsnIndex, currentFrame);\n                        newControlFlowEdge(insnIndex, targetInsnIndex);\n                        for (int i = 0; i < lookupSwitchInsn.labels.size(); ++i) {\n                            LabelNode label = lookupSwitchInsn.labels.get(i);\n                            targetInsnIndex = insnList.indexOf(label);\n                            currentFrame.initJumpTarget(insnOpcode, label);\n                            merge(targetInsnIndex, currentFrame);\n                            newControlFlowEdge(insnIndex, targetInsnIndex);\n                        }\n                    }\n                    else if (insnNode instanceof TableSwitchInsnNode) {\n                        TableSwitchInsnNode tableSwitchInsn = (TableSwitchInsnNode) insnNode;\n                        int targetInsnIndex = insnList.indexOf(tableSwitchInsn.dflt);\n                        currentFrame.initJumpTarget(insnOpcode, tableSwitchInsn.dflt);\n                        merge(targetInsnIndex, currentFrame);\n                        newControlFlowEdge(insnIndex, targetInsnIndex);\n                        for (int i = 0; i < tableSwitchInsn.labels.size(); ++i) {\n                            LabelNode label = tableSwitchInsn.labels.get(i);\n                            currentFrame.initJumpTarget(insnOpcode, label);\n                            targetInsnIndex = insnList.indexOf(label);\n                            merge(targetInsnIndex, currentFrame);\n                            newControlFlowEdge(insnIndex, targetInsnIndex);\n                        }\n                    }\n                    else if (insnOpcode != ATHROW && (insnOpcode < IRETURN || insnOpcode > RETURN)) {\n                        merge(insnIndex + 1, currentFrame);\n                        newControlFlowEdge(insnIndex, insnIndex + 1);\n                    }\n                }\n\n                List<TryCatchBlockNode> insnHandlers = handlers[insnIndex];\n                if (insnHandlers != null) {\n                    for (TryCatchBlockNode tryCatchBlock : insnHandlers) {\n                        Type catchType;\n                        if (tryCatchBlock.type == null) {\n                            catchType = Type.getObjectType(\"java/lang/Throwable\");\n                        }\n                        else {\n                            catchType = Type.getObjectType(tryCatchBlock.type);\n                        }\n                        if (newControlFlowExceptionEdge(insnIndex, tryCatchBlock)) {\n                            Frame<V> handler = newFrame(oldFrame);\n                            handler.clearStack();\n                            handler.push(interpreter.newExceptionValue(tryCatchBlock, handler, catchType));\n                            merge(insnList.indexOf(tryCatchBlock.handler), handler);\n                        }\n                    }\n                }\n            }\n            catch (AnalyzerException e) {\n                throw new AnalyzerException(e.node, \"Error at instruction \" + insnIndex + \": \" + e.getMessage(), e);\n            }\n            catch (RuntimeException e) {\n                // DontCheck(IllegalCatch): can't be fixed, for backward compatibility.\n                throw new AnalyzerException(insnNode, \"Error at instruction \" + insnIndex + \": \" + e.getMessage(), e);\n            }\n        }\n\n        return frames;\n    }\n\n    private Frame<V> computeInitialFrame(final String owner, final MethodNode method) {\n        Frame<V> frame = newFrame(method.maxLocals, method.maxStack);\n        int currentLocal = 0;\n        boolean isInstanceMethod = (method.access & ACC_STATIC) == 0;\n        if (isInstanceMethod) {\n            Type ownerType = Type.getObjectType(owner);\n            frame.setLocal(currentLocal, interpreter.newParameterValue(isInstanceMethod, currentLocal, ownerType));\n            currentLocal++;\n        }\n        Type[] argumentTypes = Type.getArgumentTypes(method.desc);\n        for (Type argumentType : argumentTypes) {\n            frame.setLocal(\n                    currentLocal,\n                    interpreter.newParameterValue(isInstanceMethod, currentLocal, argumentType));\n            currentLocal++;\n            if (argumentType.getSize() == 2) {\n                frame.setLocal(currentLocal, interpreter.newEmptyValue(currentLocal));\n                currentLocal++;\n            }\n        }\n        while (currentLocal < method.maxLocals) {\n            frame.setLocal(currentLocal, interpreter.newEmptyValue(currentLocal));\n            currentLocal++;\n        }\n        frame.setReturn(interpreter.newReturnTypeValue(Type.getReturnType(method.desc)));\n        return frame;\n    }\n\n\n    protected void init(final String owner, final MethodNode method) throws AnalyzerException {\n        // Nothing to do.\n    }\n\n    protected Frame<V> newFrame(final int numLocals, final int numStack) {\n        return new Frame<>(numLocals, numStack);\n    }\n\n    protected Frame<V> newFrame(final Frame<? extends V> frame) {\n        return new Frame<>(frame);\n    }\n\n    protected void newControlFlowEdge(final int insnIndex, final int successorIndex) {\n        // Nothing to do.\n    }\n\n    protected boolean newControlFlowExceptionEdge(final int insnIndex, final TryCatchBlockNode tryCatchBlock) {\n        return newControlFlowExceptionEdge(insnIndex, insnList.indexOf(tryCatchBlock.handler));\n    }\n\n    protected boolean newControlFlowExceptionEdge(final int insnIndex, final int successorIndex) {\n        return true;\n    }\n\n    // -----------------------------------------------------------------------------------------------\n\n    private void merge(final int insnIndex, final Frame<V> frame)\n            throws AnalyzerException {\n        boolean changed;\n        Frame<V> oldFrame = frames[insnIndex];\n        if (oldFrame == null) {\n            frames[insnIndex] = newFrame(frame);\n            changed = true;\n        }\n        else {\n            changed = oldFrame.merge(frame, interpreter);\n        }\n\n        if (changed && !inInstructionsToProcess[insnIndex]) {\n            inInstructionsToProcess[insnIndex] = true;\n            instructionsToProcess[numInstructionsToProcess++] = insnIndex;\n        }\n    }\n\n}\n"
  },
  {
    "path": "src/main/java/lsieun/asm/analysis/InsnText.java",
    "content": "package lsieun.asm.analysis;\n\nimport lsieun.utils.OpcodeConst;\nimport org.objectweb.asm.Type;\nimport org.objectweb.asm.tree.*;\n\nimport java.util.ArrayList;\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Map;\n\nimport static org.objectweb.asm.Opcodes.BIPUSH;\nimport static org.objectweb.asm.Opcodes.SIPUSH;\n\npublic class InsnText {\n    private static final String INSTRUCTION_FORMAT = \"%s %s\";\n\n    private final Map<LabelNode, String> labelNames = new HashMap<>();\n\n    public List<String> toLines(AbstractInsnNode[] array) {\n        List<String> resultList = new ArrayList<>();\n        if (array == null || array.length < 1) {\n            return resultList;\n        }\n\n        for (AbstractInsnNode node : array) {\n            List<String> lines = toLines(node);\n            resultList.addAll(lines);\n        }\n        return resultList;\n    }\n\n    public List<String> toLines(AbstractInsnNode node) {\n        List<String> resultList = new ArrayList<>();\n        if (node instanceof TableSwitchInsnNode) {\n            TableSwitchInsnNode currentNode = (TableSwitchInsnNode) node;\n            List<String> list = table_switch_node_to_str_list(currentNode);\n            resultList.addAll(list);\n        }\n        else if (node instanceof LookupSwitchInsnNode) {\n            LookupSwitchInsnNode currentNode = (LookupSwitchInsnNode) node;\n            List<String> list = lookup_switch_node_to_str_list(currentNode);\n            resultList.addAll(list);\n        }\n        else {\n            String item = node_to_str(node);\n            resultList.add(item);\n        }\n\n        return resultList;\n    }\n\n    public String node_to_str(AbstractInsnNode currentNode) {\n        if (currentNode instanceof InsnNode) {\n            return getOpcodeName(currentNode);\n        }\n        else if (currentNode instanceof IntInsnNode) {\n            int opcode = currentNode.getOpcode();\n            String opcodeName = getOpcodeName(currentNode);\n            IntInsnNode node = (IntInsnNode) currentNode;\n            int operand = node.operand;\n            if (opcode == BIPUSH || opcode == SIPUSH) {\n                return String.format(INSTRUCTION_FORMAT, opcodeName, operand);\n            }\n            else {\n                final String firstArg;\n                switch (operand) {\n                    case 4: {\n                        firstArg = \"4 (boolean)\";\n                        break;\n                    }\n                    case 5: {\n                        firstArg = \"5 (char)\";\n                        break;\n                    }\n                    case 6: {\n                        firstArg = \"6 (float)\";\n                        break;\n                    }\n                    case 7: {\n                        firstArg = \"7 (double)\";\n                        break;\n                    }\n                    case 8: {\n                        firstArg = \"8 (byte)\";\n                        break;\n                    }\n                    case 9: {\n                        firstArg = \"9 (short)\";\n                        break;\n                    }\n                    case 10: {\n                        firstArg = \"10 (int)\";\n                        break;\n                    }\n                    case 11: {\n                        firstArg = \"11 (long)\";\n                        break;\n                    }\n                    default:\n                        throw new RuntimeException(\"atype is not supported: \" + operand);\n                }\n                return String.format(INSTRUCTION_FORMAT, opcodeName, firstArg);\n            }\n        }\n        else if (currentNode instanceof VarInsnNode) {\n            String opcodeName = getOpcodeName(currentNode);\n            VarInsnNode node = (VarInsnNode) currentNode;\n            int var = node.var;\n            if (var >= 0 && var <= 3) {\n                return String.format(\"%s_%d\", opcodeName, var);\n            }\n            else {\n                return String.format(INSTRUCTION_FORMAT, opcodeName, var);\n            }\n        }\n        else if (currentNode instanceof TypeInsnNode) {\n            String opcodeName = getOpcodeName(currentNode);\n            TypeInsnNode node = (TypeInsnNode) currentNode;\n            String type = getSimpleName(node.desc);\n            return String.format(INSTRUCTION_FORMAT, opcodeName, type);\n        }\n        else if (currentNode instanceof FieldInsnNode) {\n            String opcodeName = getOpcodeName(currentNode);\n            FieldInsnNode node = (FieldInsnNode) currentNode;\n            String type = getSimpleName(node.owner);\n            return String.format(\"%s %s.%s\", opcodeName, type, node.name);\n        }\n        else if (currentNode instanceof MethodInsnNode) {\n            String opcodeName = getOpcodeName(currentNode);\n            MethodInsnNode node = (MethodInsnNode) currentNode;\n            String type = getSimpleName(node.owner);\n            return String.format(\"%s %s.%s\", opcodeName, type, node.name);\n        }\n        else if (currentNode instanceof InvokeDynamicInsnNode) {\n            String opcodeName = getOpcodeName(currentNode);\n            InvokeDynamicInsnNode node = (InvokeDynamicInsnNode) currentNode;\n            Type methodType = Type.getMethodType(node.desc);\n            Type returnType = methodType.getReturnType();\n            String type = getSimpleName(returnType.getInternalName());\n            return String.format(\"%s %s.%s\", opcodeName, type, node.name);\n        }\n        else if (currentNode instanceof JumpInsnNode) {\n            String opcodeName = getOpcodeName(currentNode);\n            JumpInsnNode node = (JumpInsnNode) currentNode;\n            String labelName = getLabelName(node.label);\n            return String.format(INSTRUCTION_FORMAT, opcodeName, labelName);\n        }\n        else if (currentNode instanceof LabelNode) {\n            LabelNode node = (LabelNode) currentNode;\n            return getLabelName(node);\n        }\n        else if (currentNode instanceof LdcInsnNode) {\n            String opcodeName = getOpcodeName(currentNode);\n            LdcInsnNode node = (LdcInsnNode) currentNode;\n            Object cst = node.cst;\n            if (cst instanceof Integer) {\n                return String.format(\"%s %s(int)\", opcodeName, cst);\n            }\n            else if (cst instanceof Float) {\n                return String.format(\"%s %s(float)\", opcodeName, cst);\n            }\n            else if (cst instanceof Long) {\n                return String.format(\"%s %s(long)\", opcodeName, cst);\n            }\n            else if (cst instanceof Double) {\n                return String.format(\"%s %s(double)\", opcodeName, cst);\n            }\n            else if (cst instanceof String) {\n                return String.format(\"%s \\\"%s\\\"\", opcodeName, cst);\n            }\n            else if (cst instanceof Class<?>) {\n                return String.format(\"%s %s(class)\", opcodeName, cst);\n            }\n            else {\n                return String.format(\"%s %s\", opcodeName, cst);\n            }\n        }\n        else if (currentNode instanceof IincInsnNode) {\n            String opcodeName = getOpcodeName(currentNode);\n            IincInsnNode node = (IincInsnNode) currentNode;\n            return String.format(\"%s %d %d\", opcodeName, node.var, node.incr);\n        }\n        else if (currentNode instanceof MultiANewArrayInsnNode) {\n            String opcodeName = getOpcodeName(currentNode);\n            MultiANewArrayInsnNode node = (MultiANewArrayInsnNode) currentNode;\n            String type = getSimpleName(node.desc);\n            return String.format(INSTRUCTION_FORMAT, opcodeName, type);\n        }\n        else if (currentNode instanceof FrameNode) {\n            return \"FrameNode\";\n        }\n        else if (currentNode instanceof LineNumberNode) {\n            return \"LineNumberNode\";\n        }\n        else {\n            System.out.println(currentNode.getClass());\n        }\n        return currentNode.toString();\n    }\n\n    public List<String> table_switch_node_to_str_list(TableSwitchInsnNode currentNode) {\n        String opcodeName = getOpcodeName(currentNode);\n        int min = currentNode.min;\n        int max = currentNode.max;\n\n        List<String> list = new ArrayList<>();\n        list.add(String.format(\"%s {\", opcodeName));\n        for (int i = min; i <= max; i++) {\n            LabelNode labelNode = currentNode.labels.get(i - min);\n            String labelName = getLabelName(labelNode);\n            list.add(String.format(\"    %d: %s\", i, labelName));\n        }\n        list.add(String.format(\"    default: %s\", getLabelName(currentNode.dflt)));\n        list.add(\"}\");\n        return list;\n    }\n\n    public List<String> lookup_switch_node_to_str_list(LookupSwitchInsnNode currentNode) {\n        String opcodeName = getOpcodeName(currentNode);\n        List<Integer> keys = currentNode.keys;\n        int size = keys.size();\n\n        List<String> list = new ArrayList<>();\n        list.add(String.format(\"%s {\", opcodeName));\n        for (int i = 0; i < size; i++) {\n            int caseValue = keys.get(i);\n            LabelNode labelNode = currentNode.labels.get(i);\n            String labelName = getLabelName(labelNode);\n            list.add(String.format(\"    %d: %s\", caseValue, labelName));\n        }\n        list.add(String.format(\"    default: %s\", getLabelName(currentNode.dflt)));\n        list.add(\"}\");\n        return list;\n    }\n\n    private String getLabelName(LabelNode labelNode) {\n        String labelName = labelNames.get(labelNode);\n        if (labelName == null) {\n            labelName = \"L\" + labelNames.size();\n            labelNames.put(labelNode, labelName);\n        }\n        return labelName;\n    }\n\n    private static String getOpcodeName(AbstractInsnNode currentNode) {\n        int opcode = currentNode.getOpcode();\n        return OpcodeConst.getOpcodeName(opcode);\n    }\n\n    private static String getSimpleName(String descriptor) {\n        int squareIndex = descriptor.lastIndexOf(\"[\");\n        String prefix = descriptor.substring(0, squareIndex + 1);\n\n        String simpleName = descriptor.substring(squareIndex + 1);\n        if (simpleName.startsWith(\"L\") && simpleName.endsWith(\";\")) {\n            simpleName = simpleName.substring(1, simpleName.length() - 1);\n        }\n\n        int slashIndex = simpleName.lastIndexOf(\"/\");\n        simpleName = simpleName.substring(slashIndex + 1);\n\n        return prefix + simpleName;\n    }\n}\n"
  },
  {
    "path": "src/main/java/lsieun/asm/analysis/MockAnalyzer.java",
    "content": "package lsieun.asm.analysis;\n\nimport org.objectweb.asm.Opcodes;\nimport org.objectweb.asm.Type;\nimport org.objectweb.asm.tree.*;\nimport org.objectweb.asm.tree.analysis.AnalyzerException;\nimport org.objectweb.asm.tree.analysis.Frame;\nimport org.objectweb.asm.tree.analysis.Interpreter;\nimport org.objectweb.asm.tree.analysis.Value;\n\npublic class MockAnalyzer<V extends Value> implements Opcodes {\n    private final Interpreter<V> interpreter;\n\n    public MockAnalyzer(Interpreter<V> interpreter) {\n        this.interpreter = interpreter;\n    }\n\n    @SuppressWarnings(\"unchecked\")\n    public Frame<V>[] analyze(String owner, MethodNode method) throws AnalyzerException {\n        // 第一步，如果是abstract或native方法，则直接返回。\n        if ((method.access & (ACC_ABSTRACT | ACC_NATIVE)) != 0) {\n            return (Frame<V>[]) new Frame<?>[0];\n        }\n\n        // 第二步，定义局部变量\n        // （1）数据输入：获取指令集\n        InsnList insnList = method.instructions;\n        int size = insnList.size();\n\n        // （2）中间状态：记录需要哪一个指令需要处理\n        boolean[] instructionsToProcess = new boolean[size];\n\n        // （3）数据输出：最终的返回结果\n        Frame<V>[] frames = (Frame<V>[]) new Frame<?>[size];\n\n        // 第三步，开始计算\n        // （1）开始计算：根据方法的参数，计算方法的初始Frame\n        Frame<V> currentFrame = computeInitialFrame(owner, method);\n        merge(frames, 0, currentFrame, instructionsToProcess);\n\n        // （2）开始计算：根据方法的每一条指令，计算相应的Frame\n        while (getCount(instructionsToProcess) > 0) {\n            // 获取需要处理的指令索引（insnIndex）和旧的Frame（oldFrame）\n            int insnIndex = getFirst(instructionsToProcess);\n            Frame<V> oldFrame = frames[insnIndex];\n            instructionsToProcess[insnIndex] = false;\n\n            // 模拟每一条指令的执行\n            try {\n                AbstractInsnNode insnNode = method.instructions.get(insnIndex);\n                int insnOpcode = insnNode.getOpcode();\n                int insnType = insnNode.getType();\n\n                // 这三者并不是真正的指令，分别表示Label、LineNumberTable和Frame\n                if (insnType == AbstractInsnNode.LABEL\n                        || insnType == AbstractInsnNode.LINE\n                        || insnType == AbstractInsnNode.FRAME) {\n                    merge(frames, insnIndex + 1, oldFrame, instructionsToProcess);\n                }\n                else {\n                    // 这里是真正的指令\n                    currentFrame.init(oldFrame).execute(insnNode, interpreter);\n\n                    if (insnNode instanceof JumpInsnNode) {\n                        JumpInsnNode jumpInsn = (JumpInsnNode) insnNode;\n                        // if之后的语句\n                        if (insnOpcode != GOTO) {\n                            merge(frames, insnIndex + 1, currentFrame, instructionsToProcess);\n                        }\n\n                        // if和goto跳转之后的位置\n                        int jumpInsnIndex = insnList.indexOf(jumpInsn.label);\n                        merge(frames, jumpInsnIndex, currentFrame, instructionsToProcess);\n                    }\n                    else if (insnNode instanceof LookupSwitchInsnNode) {\n                        LookupSwitchInsnNode lookupSwitchInsn = (LookupSwitchInsnNode) insnNode;\n\n                        // lookupswitch的default情况\n                        int targetInsnIndex = insnList.indexOf(lookupSwitchInsn.dflt);\n                        merge(frames, targetInsnIndex, currentFrame, instructionsToProcess);\n\n                        // lookupswitch的各种case情况\n                        for (int i = 0; i < lookupSwitchInsn.labels.size(); ++i) {\n                            LabelNode label = lookupSwitchInsn.labels.get(i);\n                            targetInsnIndex = insnList.indexOf(label);\n                            merge(frames, targetInsnIndex, currentFrame, instructionsToProcess);\n                        }\n                    }\n                    else if (insnNode instanceof TableSwitchInsnNode) {\n                        TableSwitchInsnNode tableSwitchInsn = (TableSwitchInsnNode) insnNode;\n\n                        // tableswitch的default情况\n                        int targetInsnIndex = insnList.indexOf(tableSwitchInsn.dflt);\n                        merge(frames, targetInsnIndex, currentFrame, instructionsToProcess);\n\n                        // tableswitch的各种case情况\n                        for (int i = 0; i < tableSwitchInsn.labels.size(); ++i) {\n                            LabelNode label = tableSwitchInsn.labels.get(i);\n                            targetInsnIndex = insnList.indexOf(label);\n                            merge(frames, targetInsnIndex, currentFrame, instructionsToProcess);\n                        }\n                    }\n                    else if (insnOpcode != ATHROW && (insnOpcode < IRETURN || insnOpcode > RETURN)) {\n                        merge(frames, insnIndex + 1, currentFrame, instructionsToProcess);\n                    }\n                }\n            }\n            catch (AnalyzerException e) {\n                throw new AnalyzerException(e.node, \"Error at instruction \" + insnIndex + \": \" + e.getMessage(), e);\n            }\n        }\n\n        return frames;\n    }\n\n    private int getCount(boolean[] array) {\n        int count = 0;\n        for (boolean flag : array) {\n            if (flag) {\n                count++;\n            }\n        }\n        return count;\n    }\n\n    private int getFirst(boolean[] array) {\n        int length = array.length;\n        for (int i = 0; i < length; i++) {\n            boolean flag = array[i];\n            if (flag) {\n                return i;\n            }\n        }\n        return -1;\n    }\n\n    private Frame<V> computeInitialFrame(final String owner, final MethodNode method) {\n        Frame<V> frame = new Frame<>(method.maxLocals, method.maxStack);\n        int currentLocal = 0;\n\n        // 第一步，判断是否需要存储this变量\n        boolean isInstanceMethod = (method.access & ACC_STATIC) == 0;\n        if (isInstanceMethod) {\n            Type ownerType = Type.getObjectType(owner);\n            V value = interpreter.newParameterValue(isInstanceMethod, currentLocal, ownerType);\n            frame.setLocal(currentLocal, value);\n            currentLocal++;\n        }\n\n        // 第二步，将方法的参数存入到local variable内\n        Type[] argumentTypes = Type.getArgumentTypes(method.desc);\n        for (Type argumentType : argumentTypes) {\n            V value = interpreter.newParameterValue(isInstanceMethod, currentLocal, argumentType);\n            frame.setLocal(currentLocal, value);\n            currentLocal++;\n            if (argumentType.getSize() == 2) {\n                frame.setLocal(currentLocal, interpreter.newEmptyValue(currentLocal));\n                currentLocal++;\n            }\n        }\n\n        // 第三步，将local variable的剩余位置填补上空值\n        while (currentLocal < method.maxLocals) {\n            frame.setLocal(currentLocal, interpreter.newEmptyValue(currentLocal));\n            currentLocal++;\n        }\n\n        // 第四步，设置返回值类型\n        frame.setReturn(interpreter.newReturnTypeValue(Type.getReturnType(method.desc)));\n        return frame;\n    }\n\n    /**\n     * Merge old frame with new frame.\n     *\n     * @param frames 所有的frame信息。\n     * @param insnIndex 当前指令的索引。\n     * @param newFrame 新的frame\n     * @param instructionsToProcess 记录哪一条指令需要处理\n     * @throws AnalyzerException 分析错误，抛出此异常\n     */\n    private void merge(Frame<V>[] frames, int insnIndex, Frame<V> newFrame, boolean[] instructionsToProcess) throws AnalyzerException {\n        boolean changed;\n        Frame<V> oldFrame = frames[insnIndex];\n        if (oldFrame == null) {\n            frames[insnIndex] = new Frame<>(newFrame);\n            changed = true;\n        }\n        else {\n            changed = oldFrame.merge(newFrame, interpreter);\n        }\n\n        if (changed && !instructionsToProcess[insnIndex]) {\n            instructionsToProcess[insnIndex] = true;\n        }\n    }\n}\n"
  },
  {
    "path": "src/main/java/lsieun/asm/analysis/RemoveDeadCodeNode.java",
    "content": "package lsieun.asm.analysis;\n\nimport lsieun.asm.tree.transformer.MethodTransformer;\nimport org.objectweb.asm.ClassVisitor;\nimport org.objectweb.asm.tree.AbstractInsnNode;\nimport org.objectweb.asm.tree.ClassNode;\nimport org.objectweb.asm.tree.LabelNode;\nimport org.objectweb.asm.tree.MethodNode;\nimport org.objectweb.asm.tree.analysis.*;\n\npublic class RemoveDeadCodeNode extends ClassNode {\n    public RemoveDeadCodeNode(int api, ClassVisitor cv) {\n        super(api);\n        this.cv = cv;\n    }\n\n    @Override\n    public void visitEnd() {\n        // 首先，处理自己的代码逻辑\n        MethodTransformer mt = new MethodRemoveDeadCodeTransformer(name, null);\n        for (MethodNode mn : methods) {\n            mt.transform(mn);\n        }\n\n        // 其次，调用父类的方法实现\n        super.visitEnd();\n\n        // 最后，向后续ClassVisitor传递\n        if (cv != null) {\n            accept(cv);\n        }\n    }\n\n    private static class MethodRemoveDeadCodeTransformer extends MethodTransformer {\n        private final String owner;\n\n        public MethodRemoveDeadCodeTransformer(String owner, MethodTransformer mt) {\n            super(mt);\n            this.owner = owner;\n        }\n\n        @Override\n        public void transform(MethodNode mn) {\n            // 首先，处理自己的代码逻辑\n            Analyzer<BasicValue> analyzer = new Analyzer<>(new BasicInterpreter());\n            try {\n                analyzer.analyze(owner, mn);\n                Frame<BasicValue>[] frames = analyzer.getFrames();\n                AbstractInsnNode[] insnNodes = mn.instructions.toArray();\n                for (int i = 0; i < frames.length; i++) {\n                    if (frames[i] == null && !(insnNodes[i] instanceof LabelNode)) {\n                        mn.instructions.remove(insnNodes[i]);\n                    }\n                }\n            }\n            catch (AnalyzerException ex) {\n                ex.printStackTrace();\n            }\n\n            // 其次，调用父类的方法实现\n            super.transform(mn);\n        }\n    }\n}\n"
  },
  {
    "path": "src/main/java/lsieun/asm/analysis/RemoveDeadCodeVisitor.java",
    "content": "package lsieun.asm.analysis;\n\nimport org.objectweb.asm.ClassVisitor;\nimport org.objectweb.asm.MethodVisitor;\nimport org.objectweb.asm.tree.AbstractInsnNode;\nimport org.objectweb.asm.tree.LabelNode;\nimport org.objectweb.asm.tree.MethodNode;\nimport org.objectweb.asm.tree.analysis.*;\n\npublic class RemoveDeadCodeVisitor extends ClassVisitor {\n    private String owner;\n\n    public RemoveDeadCodeVisitor(int api, ClassVisitor cv) {\n        super(api, cv);\n    }\n\n    @Override\n    public void visit(int version, int access, String name, String signature, String superName, String[] interfaces) {\n        super.visit(version, access, name, signature, superName, interfaces);\n        this.owner = name;\n    }\n\n    @Override\n    public MethodVisitor visitMethod(int access, String name, String descriptor, String signature, String[] exceptions) {\n        MethodVisitor mv = super.visitMethod(access, name, descriptor, signature, exceptions);\n        if (mv != null) {\n            mv = new MethodRemoveDeadCodeAdapter(api, owner, access, name, descriptor, mv);\n        }\n        return mv;\n    }\n\n    private static class MethodRemoveDeadCodeAdapter extends MethodVisitor {\n        private final String owner;\n        private final MethodVisitor next;\n\n        public MethodRemoveDeadCodeAdapter(int api, String owner, int access, String name, String desc, MethodVisitor next) {\n            super(api, new MethodNode(access, name, desc, null, null));\n            this.owner = owner;\n            this.next = next;\n        }\n\n        @Override\n        public void visitEnd() {\n            // 首先，处理自己的代码逻辑\n            MethodNode mn = (MethodNode) mv;\n            Analyzer<BasicValue> analyzer = new Analyzer<>(new BasicInterpreter());\n            try {\n                analyzer.analyze(owner, mn);\n                Frame<BasicValue>[] frames = analyzer.getFrames();\n                AbstractInsnNode[] insnNodes = mn.instructions.toArray();\n                for (int i = 0; i < frames.length; i++) {\n                    if (frames[i] == null && !(insnNodes[i] instanceof LabelNode)) {\n                        mn.instructions.remove(insnNodes[i]);\n                    }\n                }\n            }\n            catch (AnalyzerException ex) {\n                ex.printStackTrace();\n            }\n\n            // 其次，调用父类的方法实现\n            super.visitEnd();\n\n            // 最后，向后续MethodVisitor传递\n            if (next != null) {\n                mn.accept(next);\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "src/main/java/lsieun/asm/analysis/RemoveUnusedCastNode.java",
    "content": "package lsieun.asm.analysis;\n\nimport lsieun.asm.tree.transformer.MethodTransformer;\nimport org.objectweb.asm.ClassVisitor;\nimport org.objectweb.asm.Type;\nimport org.objectweb.asm.tree.*;\nimport org.objectweb.asm.tree.analysis.*;\n\nimport static org.objectweb.asm.Opcodes.CHECKCAST;\n\npublic class RemoveUnusedCastNode extends ClassNode {\n    public RemoveUnusedCastNode(int api, ClassVisitor cv) {\n        super(api);\n        this.cv = cv;\n    }\n\n    @Override\n    public void visitEnd() {\n        // 首先，处理自己的代码逻辑\n        MethodTransformer mt = new MethodRemoveUnusedCastTransformer(name, null);\n        for (MethodNode mn : methods) {\n            if (\"<init>\".equals(mn.name) || \"<clinit>\".equals(mn.name)) {\n                continue;\n            }\n            InsnList insns = mn.instructions;\n            if (insns.size() == 0) {\n                continue;\n            }\n            mt.transform(mn);\n        }\n\n        // 其次，调用父类的方法实现\n        super.visitEnd();\n\n        // 最后，向后续ClassVisitor传递\n        if (cv != null) {\n            accept(cv);\n        }\n    }\n\n    private static class MethodRemoveUnusedCastTransformer extends MethodTransformer {\n        private final String owner;\n\n        public MethodRemoveUnusedCastTransformer(String owner, MethodTransformer mt) {\n            super(mt);\n            this.owner = owner;\n        }\n\n        @Override\n        public void transform(MethodNode mn) {\n            // 首先，处理自己的代码逻辑\n            Analyzer<BasicValue> analyzer = new Analyzer<>(new SimpleVerifier());\n            try {\n                Frame<BasicValue>[] frames = analyzer.analyze(owner, mn);\n                AbstractInsnNode[] insnNodes = mn.instructions.toArray();\n                for (int i = 0; i < insnNodes.length; i++) {\n                    AbstractInsnNode insn = insnNodes[i];\n                    if (insn.getOpcode() == CHECKCAST) {\n                        Frame<BasicValue> f = frames[i];\n                        if (f != null && f.getStackSize() > 0) {\n                            BasicValue operand = f.getStack(f.getStackSize() - 1);\n                            Class<?> to = getClass(((TypeInsnNode) insn).desc);\n                            Class<?> from = getClass(operand.getType());\n                            if (to.isAssignableFrom(from)) {\n                                mn.instructions.remove(insn);\n                            }\n                        }\n                    }\n                }\n            }\n            catch (AnalyzerException ex) {\n                ex.printStackTrace();\n            }\n\n            // 其次，调用父类的方法实现\n            super.transform(mn);\n        }\n\n        private static Class<?> getClass(String desc) {\n            try {\n                return Class.forName(desc.replace('/', '.'));\n            }\n            catch (ClassNotFoundException ex) {\n                throw new RuntimeException(ex.toString());\n            }\n        }\n\n        private static Class<?> getClass(Type t) {\n            if (t.getSort() == Type.OBJECT) {\n                return getClass(t.getInternalName());\n            }\n            return getClass(t.getDescriptor());\n        }\n    }\n}\n"
  },
  {
    "path": "src/main/java/lsieun/asm/analysis/RemoveUnusedCastVisitor.java",
    "content": "package lsieun.asm.analysis;\n\nimport org.objectweb.asm.ClassVisitor;\nimport org.objectweb.asm.MethodVisitor;\nimport org.objectweb.asm.commons.AnalyzerAdapter;\n\nimport static org.objectweb.asm.Opcodes.*;\n\npublic class RemoveUnusedCastVisitor extends ClassVisitor {\n    private String owner;\n\n    public RemoveUnusedCastVisitor(int api, ClassVisitor classVisitor) {\n        super(api, classVisitor);\n    }\n\n    @Override\n    public void visit(int version, int access, String name, String signature, String superName, String[] interfaces) {\n        super.visit(version, access, name, signature, superName, interfaces);\n        this.owner = name;\n    }\n\n    @Override\n    public MethodVisitor visitMethod(int access, String name, String descriptor, String signature, String[] exceptions) {\n        MethodVisitor mv = super.visitMethod(access, name, descriptor, signature, exceptions);\n        if (mv != null && !name.equals(\"<init>\")) {\n            boolean isAbstractMethod = (access & ACC_ABSTRACT) != 0;\n            boolean isNativeMethod = (access & ACC_NATIVE) != 0;\n            if (!isAbstractMethod && !isNativeMethod) {\n                RemoveUnusedCastAdapter adapter = new RemoveUnusedCastAdapter(api, mv);\n                adapter.aa = new AnalyzerAdapter(owner, access, name, descriptor, adapter);\n                return adapter.aa;\n            }\n        }\n\n        return mv;\n    }\n\n    private static class RemoveUnusedCastAdapter extends MethodVisitor {\n        public AnalyzerAdapter aa;\n\n        public RemoveUnusedCastAdapter(int api, MethodVisitor methodVisitor) {\n            super(api, methodVisitor);\n        }\n\n        @Override\n        public void visitTypeInsn(int opcode, String type) {\n            // 首先，处理自己的代码逻辑\n            if (opcode == CHECKCAST) {\n                Class<?> to = getClass(type);\n                if (aa.stack != null && aa.stack.size() > 0) {\n                    Object operand = aa.stack.get(aa.stack.size() - 1);\n                    if (operand instanceof String) {\n                        Class<?> from = getClass((String) operand);\n                        if (to.isAssignableFrom(from)) {\n                            return;\n                        }\n                    }\n                }\n            }\n\n            // 其次，调用父类的方法实现\n            super.visitTypeInsn(opcode, type);\n        }\n\n        private static Class<?> getClass(String desc) {\n            try {\n                return Class.forName(desc.replace('/', '.'));\n            }\n            catch (ClassNotFoundException ex) {\n                throw new RuntimeException(ex.toString());\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "src/main/java/lsieun/asm/analysis/cc/CyclomaticComplexity.java",
    "content": "package lsieun.asm.analysis.cc;\n\nimport org.objectweb.asm.tree.MethodNode;\nimport org.objectweb.asm.tree.analysis.*;\n\npublic class CyclomaticComplexity {\n    public static int getCyclomaticComplexity(String owner, MethodNode mn) throws AnalyzerException {\n        // 第一步，获取Frame信息\n        Analyzer<BasicValue> analyzer = new CyclomaticComplexityAnalyzer<>(new BasicInterpreter());\n        Frame<BasicValue>[] frames = analyzer.analyze(owner, mn);\n\n        // 第二步，计算复杂度\n        int edges = 0;\n        int nodes = 0;\n        for (Frame<BasicValue> frame : frames) {\n            if (frame != null) {\n                edges += ((CyclomaticComplexityFrame<BasicValue>) frame).successors.size();\n                nodes += 1;\n            }\n        }\n        return edges - nodes + 2;\n    }\n}\n"
  },
  {
    "path": "src/main/java/lsieun/asm/analysis/cc/CyclomaticComplexityAnalyzer.java",
    "content": "package lsieun.asm.analysis.cc;\n\nimport org.objectweb.asm.tree.analysis.*;\n\npublic class CyclomaticComplexityAnalyzer<V extends Value> extends Analyzer<V> {\n    public CyclomaticComplexityAnalyzer(Interpreter<V> interpreter) {\n        super(interpreter);\n    }\n\n    @Override\n    protected Frame<V> newFrame(int numLocals, int numStack) {\n        return new CyclomaticComplexityFrame<>(numLocals, numStack);\n    }\n\n    @Override\n    protected Frame<V> newFrame(Frame<? extends V> frame) {\n        return new CyclomaticComplexityFrame<>(frame);\n    }\n\n    @Override\n    protected void newControlFlowEdge(int insnIndex, int successorIndex) {\n        CyclomaticComplexityFrame<V> frame = (CyclomaticComplexityFrame<V>) getFrames()[insnIndex];\n        frame.successors.add((CyclomaticComplexityFrame<V>) getFrames()[successorIndex]);\n    }\n}\n"
  },
  {
    "path": "src/main/java/lsieun/asm/analysis/cc/CyclomaticComplexityFrame.java",
    "content": "package lsieun.asm.analysis.cc;\n\nimport org.objectweb.asm.tree.analysis.Frame;\nimport org.objectweb.asm.tree.analysis.Value;\n\nimport java.util.HashSet;\nimport java.util.Set;\n\npublic class CyclomaticComplexityFrame<V extends Value> extends Frame<V> {\n    public Set<CyclomaticComplexityFrame<V>> successors = new HashSet<>();\n\n    public CyclomaticComplexityFrame(int numLocals, int numStack) {\n        super(numLocals, numStack);\n    }\n\n    public CyclomaticComplexityFrame(Frame<? extends V> frame) {\n        super(frame);\n    }\n}\n"
  },
  {
    "path": "src/main/java/lsieun/asm/analysis/cfg/ControlFlowBuilder.java",
    "content": "package lsieun.asm.analysis.cfg;\n\nimport org.objectweb.asm.tree.MethodNode;\nimport org.objectweb.asm.tree.analysis.AnalyzerException;\n\nimport static org.objectweb.asm.Opcodes.ACC_ABSTRACT;\nimport static org.objectweb.asm.Opcodes.ACC_NATIVE;\n\npublic final class ControlFlowBuilder {\n    public static ControlFlowGraph buildCFG(String className, MethodNode methodNode) throws AnalyzerException {\n        if ((methodNode.access & (ACC_ABSTRACT | ACC_NATIVE)) != 0) {\n            throw new RuntimeException(\"method body is empty\");\n        }\n\n        int size = methodNode.instructions.size();\n        SimpleEdgeCreator edgeCreator = new SimpleEdgeCreator(size);\n        FramelessAnalyzer myAnalyzer = new FramelessAnalyzer(edgeCreator);\n        myAnalyzer.analyze(methodNode);\n\n\n        int[][] transitions = new int[edgeCreator.transitions.length][];\n        for (int i = 0; i < transitions.length; i++) {\n            transitions[i] = edgeCreator.transitions[i].toNativeArray();\n        }\n        int[][] errorTransitions = new int[edgeCreator.errorTransitions.length][];\n        for (int i = 0; i < errorTransitions.length; i++) {\n            errorTransitions[i] = edgeCreator.errorTransitions[i].toNativeArray();\n        }\n\n        return new ControlFlowGraph(transitions, errorTransitions);\n    }\n}\n"
  },
  {
    "path": "src/main/java/lsieun/asm/analysis/cfg/ControlFlowGraph.java",
    "content": "package lsieun.asm.analysis.cfg;\n\nimport java.util.Arrays;\nimport java.util.Formatter;\n\npublic final class ControlFlowGraph {\n    public final int[][] transitions;\n    public final int[][] errorTransitions;\n\n    public ControlFlowGraph(int[][] transitions, int[][] errorTransitions) {\n        this.transitions = transitions;\n        this.errorTransitions = errorTransitions;\n    }\n\n    @Override\n    public String toString() {\n        StringBuilder sb = new StringBuilder();\n        Formatter fm = new Formatter(sb);\n        fm.format(\"ControlFlowGraph%n\");\n\n        fm.format(\"Normal Transitions%n\");\n        int normalLength = transitions.length;\n        for (int i = 0; i < normalLength; i++) {\n            int[] array = transitions[i];\n            fm.format(\"%03d    %s%n\", i, Arrays.toString(array));\n        }\n\n        fm.format(\"Error Transitions%n\");\n        int errorLength = transitions.length;\n        for (int i = 0; i < errorLength; i++) {\n            int[] array = errorTransitions[i];\n            fm.format(\"%03d    %s%n\", i, Arrays.toString(array));\n        }\n        return sb.toString();\n    }\n}\n"
  },
  {
    "path": "src/main/java/lsieun/asm/analysis/cfg/Edge.java",
    "content": "package lsieun.asm.analysis.cfg;\n\npublic final class Edge {\n    public final int from;\n    public final int to;\n\n    public Edge(int from, int to) {\n        this.from = from;\n        this.to = to;\n    }\n\n    @Override\n    public boolean equals(Object obj) {\n        if (this == obj) return true;\n        if (!(obj instanceof Edge)) {\n            return false;\n        }\n        Edge edge = (Edge) obj;\n        return (from == edge.from) && (to == edge.to);\n    }\n\n    @Override\n    public int hashCode() {\n        return 31 * from + to;\n    }\n}"
  },
  {
    "path": "src/main/java/lsieun/asm/analysis/cfg/EdgeCreator.java",
    "content": "package lsieun.asm.analysis.cfg;\n\npublic interface EdgeCreator {\n    void newControlFlowEdge(int insn, int successor);\n\n    void newControlFlowExceptionEdge(int insn, int successor, boolean npe);\n}"
  },
  {
    "path": "src/main/java/lsieun/asm/analysis/cfg/FramelessAnalyzer.java",
    "content": "package lsieun.asm.analysis.cfg;\n\nimport org.objectweb.asm.tree.*;\nimport org.objectweb.asm.tree.analysis.AnalyzerException;\n\nimport java.util.ArrayList;\nimport java.util.HashSet;\nimport java.util.List;\nimport java.util.Set;\n\nimport static org.objectweb.asm.Opcodes.*;\n\n/**\n * Specialized version of {@link org.objectweb.asm.tree.analysis.Analyzer}.\n * Calculation of fix-point of frames is removed, since frames are not needed to build control flow graph.\n * So, the main point here is handling of try-catch-finally blocks.\n */\npublic class FramelessAnalyzer {\n    private static final Set<String> NPE_HANDLERS = new HashSet<>();\n\n    static {\n        NPE_HANDLERS.add(\"java/lang/Throwable\");\n        NPE_HANDLERS.add(\"java/lang/Exception\");\n        NPE_HANDLERS.add(\"java/lang/RuntimeException\");\n        NPE_HANDLERS.add(\"java/lang/NullPointerException\");\n    }\n\n    protected boolean[] wasQueued;\n    protected boolean[] queued;\n    protected int[] queue;\n    protected int top;\n    protected final EdgeCreator myEdgeCreator;\n\n    public FramelessAnalyzer(EdgeCreator creator) {\n        myEdgeCreator = creator;\n    }\n\n    public void analyze(MethodNode m) throws AnalyzerException {\n        int n = m.instructions.size();\n        if ((m.access & (ACC_ABSTRACT | ACC_NATIVE)) != 0 || n == 0) {\n            return;\n        }\n        InsnList insns = m.instructions;\n        List<TryCatchBlockNode>[] handlers = newListArray(n);\n        queued = new boolean[n];\n        wasQueued = new boolean[n];\n        queue = new int[n];\n        top = 0;\n\n        // computes exception handlers for each instruction\n        for (TryCatchBlockNode tcb : m.tryCatchBlocks) {\n            int begin = insns.indexOf(tcb.start);\n            int end = insns.indexOf(tcb.end);\n            for (int j = begin; j < end; ++j) {\n                List<TryCatchBlockNode> insnHandlers = handlers[j];\n                if (insnHandlers == null) {\n                    insnHandlers = new ArrayList<>();\n                    handlers[j] = insnHandlers;\n                }\n                insnHandlers.add(tcb);\n            }\n        }\n\n        merge(0);\n        // control flow analysis\n        while (top > 0) {\n            int insn = queue[--top];\n\n            queued[insn] = false;\n\n            AbstractInsnNode insnNode = null;\n            try {\n                insnNode = m.instructions.get(insn);\n                int insnOpcode = insnNode.getOpcode();\n                int insnType = insnNode.getType();\n\n                if (insnType == AbstractInsnNode.LABEL || insnType == AbstractInsnNode.LINE || insnType == AbstractInsnNode.FRAME) {\n                    merge(insn + 1);\n                    myEdgeCreator.newControlFlowEdge(insn, insn + 1);\n                }\n                else {\n\n                    if (insnNode instanceof JumpInsnNode) {\n                        JumpInsnNode j = (JumpInsnNode) insnNode;\n                        if (insnOpcode != GOTO && insnOpcode != JSR) {\n                            merge(insn + 1);\n                            myEdgeCreator.newControlFlowEdge(insn, insn + 1);\n                        }\n                        int jump = insns.indexOf(j.label);\n                        if (insnOpcode == JSR) {\n                            throw new AnalyzerException(insnNode, \"ret is not supported\");\n                        }\n                        else {\n                            merge(jump);\n                        }\n                        myEdgeCreator.newControlFlowEdge(insn, jump);\n                    }\n                    else if (insnNode instanceof LookupSwitchInsnNode) {\n                        LookupSwitchInsnNode lsi = (LookupSwitchInsnNode) insnNode;\n                        int jump = insns.indexOf(lsi.dflt);\n                        merge(jump);\n                        myEdgeCreator.newControlFlowEdge(insn, jump);\n                        for (int j = 0; j < lsi.labels.size(); ++j) {\n                            LabelNode label = lsi.labels.get(j);\n                            jump = insns.indexOf(label);\n                            merge(jump);\n                            myEdgeCreator.newControlFlowEdge(insn, jump);\n                        }\n                    }\n                    else if (insnNode instanceof TableSwitchInsnNode) {\n                        TableSwitchInsnNode tsi = (TableSwitchInsnNode) insnNode;\n                        int jump = insns.indexOf(tsi.dflt);\n                        merge(jump);\n                        myEdgeCreator.newControlFlowEdge(insn, jump);\n                        for (int j = 0; j < tsi.labels.size(); ++j) {\n                            LabelNode label = tsi.labels.get(j);\n                            jump = insns.indexOf(label);\n                            merge(jump);\n                            myEdgeCreator.newControlFlowEdge(insn, jump);\n                        }\n                    }\n                    else if (insnOpcode == RET) {\n                        throw new AnalyzerException(insnNode, \"ret is not supported\");\n                    }\n                    else if (insnOpcode != ATHROW && (insnOpcode < IRETURN || insnOpcode > RETURN)) {\n                        merge(insn + 1);\n                        myEdgeCreator.newControlFlowEdge(insn, insn + 1);\n                    }\n                }\n\n                List<TryCatchBlockNode> insnHandlers = handlers[insn];\n                if (insnHandlers != null) {\n                    for (TryCatchBlockNode tcb : insnHandlers) {\n                        myEdgeCreator.newControlFlowExceptionEdge(insn, insns.indexOf(tcb.handler), NPE_HANDLERS.contains(tcb.type));\n                        merge(insns.indexOf(tcb.handler));\n                    }\n                }\n            }\n            catch (AnalyzerException e) {\n                throw new AnalyzerException(e.node, \"Error at instruction \" + insn + \": \" + e.getMessage(), e);\n            }\n            catch (Exception e) {\n                throw new AnalyzerException(insnNode, \"Error at instruction \" + insn + \": \" + e.getMessage(), e);\n            }\n        }\n    }\n\n    // -------------------------------------------------------------------------\n\n    protected void merge(int insn) {\n        boolean changes = false;\n\n        if (!wasQueued[insn]) {\n            wasQueued[insn] = true;\n            changes = true;\n        }\n\n        if (changes && !queued[insn]) {\n            queued[insn] = true;\n            queue[top++] = insn;\n        }\n    }\n\n    public static <V> List<V>[] newListArray(int size) {\n        @SuppressWarnings(\"unchecked\")\n        List<V>[] a = (List<V>[]) new List[size];\n        return a;\n    }\n}"
  },
  {
    "path": "src/main/java/lsieun/asm/analysis/cfg/SimpleEdgeCreator.java",
    "content": "package lsieun.asm.analysis.cfg;\n\nimport lsieun.trove.TIntArrayList;\n\npublic class SimpleEdgeCreator implements EdgeCreator {\n    public final TIntArrayList[] transitions;\n    public final TIntArrayList[] errorTransitions;\n\n    public SimpleEdgeCreator(int size) {\n        this.transitions = new TIntArrayList[size];\n        this.errorTransitions = new TIntArrayList[size];\n        for (int i = 0; i < transitions.length; i++) {\n            this.transitions[i] = new TIntArrayList();\n            this.errorTransitions[i] = new TIntArrayList();\n        }\n    }\n\n    @Override\n    public void newControlFlowEdge(int insn, int successor) {\n        if (!transitions[insn].contains(successor)) {\n            transitions[insn].add(successor);\n        }\n    }\n\n    @Override\n    public void newControlFlowExceptionEdge(int insn, int successor, boolean npe) {\n        if (!errorTransitions[insn].contains(successor)) {\n            errorTransitions[insn].add(successor);\n        }\n    }\n}\n"
  },
  {
    "path": "src/main/java/lsieun/asm/analysis/diagnosis/DeadCodeDiagnosis.java",
    "content": "package lsieun.asm.analysis.diagnosis;\n\nimport lsieun.asm.analysis.ControlFlowAnalyzer;\nimport lsieun.trove.TIntArrayList;\nimport org.objectweb.asm.tree.InsnList;\nimport org.objectweb.asm.tree.MethodNode;\nimport org.objectweb.asm.tree.analysis.AnalyzerException;\n\npublic class DeadCodeDiagnosis {\n    public static int[] diagnose(String className, MethodNode mn) throws AnalyzerException {\n        InsnList instructions = mn.instructions;\n        int size = instructions.size();\n\n        boolean[] flags = new boolean[size];\n        ControlFlowAnalyzer analyzer = new ControlFlowAnalyzer() {\n            @Override\n            protected void newControlFlowEdge(int insnIndex, int successorIndex) {\n                // 首先，处理自己的代码逻辑\n                flags[insnIndex] = true;\n                flags[successorIndex] = true;\n\n                // 其次，调用父类的实现\n                super.newControlFlowEdge(insnIndex, successorIndex);\n            }\n        };\n        analyzer.analyze(className, mn);\n\n\n        TIntArrayList intArrayList = new TIntArrayList();\n        for (int i = 0; i < size; i++) {\n            boolean flag = flags[i];\n            if (!flag) {\n                intArrayList.add(i);\n            }\n        }\n\n        return intArrayList.toNativeArray();\n    }\n}\n"
  },
  {
    "path": "src/main/java/lsieun/asm/analysis/diagnosis/NullDereferenceDiagnosis.java",
    "content": "package lsieun.asm.analysis.diagnosis;\n\nimport lsieun.asm.analysis.nullability.NullDeferenceInterpreter;\nimport lsieun.trove.TIntArrayList;\nimport org.objectweb.asm.Type;\nimport org.objectweb.asm.tree.AbstractInsnNode;\nimport org.objectweb.asm.tree.InsnList;\nimport org.objectweb.asm.tree.MethodInsnNode;\nimport org.objectweb.asm.tree.MethodNode;\nimport org.objectweb.asm.tree.analysis.*;\n\nimport java.util.Arrays;\n\nimport static org.objectweb.asm.Opcodes.*;\n\npublic class NullDereferenceDiagnosis {\n    public static int[] diagnose(String owner, MethodNode mn) throws AnalyzerException {\n        // 第一步，获取Frame信息\n        Analyzer<BasicValue> analyzer = new Analyzer<>(new NullDeferenceInterpreter(ASM9));\n        Frame<BasicValue>[] frames = analyzer.analyze(owner, mn);\n\n        // 第二步，判断是否为null或maybe-null，收集数据\n        TIntArrayList intArrayList = new TIntArrayList();\n        InsnList instructions = mn.instructions;\n        int size = instructions.size();\n        for (int i = 0; i < size; i++) {\n            AbstractInsnNode insn = instructions.get(i);\n            if (frames[i] != null) {\n                Value value = getTarget(insn, frames[i]);\n                if (value == NullDeferenceInterpreter.NULL_VALUE || value == NullDeferenceInterpreter.MAYBE_NULL_VALUE) {\n                    intArrayList.add(i);\n                }\n            }\n        }\n\n        // 第三步，将结果转换成int[]形式\n        int[] array = intArrayList.toNativeArray();\n        Arrays.sort(array);\n        return array;\n    }\n\n    private static BasicValue getTarget(AbstractInsnNode insn, Frame<BasicValue> frame) {\n        int opcode = insn.getOpcode();\n        switch (opcode) {\n            case GETFIELD:\n            case ARRAYLENGTH:\n            case MONITORENTER:\n            case MONITOREXIT:\n                return getStackValue(frame, 0);\n            case PUTFIELD:\n                return getStackValue(frame, 1);\n            case INVOKEVIRTUAL:\n            case INVOKESPECIAL:\n            case INVOKEINTERFACE:\n                String desc = ((MethodInsnNode) insn).desc;\n                return getStackValue(frame, Type.getArgumentTypes(desc).length);\n\n        }\n        return null;\n    }\n\n    private static BasicValue getStackValue(Frame<BasicValue> frame, int index) {\n        int top = frame.getStackSize() - 1;\n        return index <= top ? frame.getStack(top - index) : null;\n    }\n}\n"
  },
  {
    "path": "src/main/java/lsieun/asm/analysis/diagnosis/NullabilityDiagnosis.java",
    "content": "package lsieun.asm.analysis.diagnosis;\n\nimport lsieun.asm.analysis.nullability.Nullability;\nimport lsieun.asm.analysis.nullability.NullabilityAnalyzer;\nimport lsieun.asm.analysis.nullability.NullabilityInterpreter;\nimport lsieun.asm.analysis.nullability.NullabilityValue;\nimport lsieun.trove.TIntArrayList;\nimport org.objectweb.asm.Opcodes;\nimport org.objectweb.asm.Type;\nimport org.objectweb.asm.tree.AbstractInsnNode;\nimport org.objectweb.asm.tree.InsnList;\nimport org.objectweb.asm.tree.MethodInsnNode;\nimport org.objectweb.asm.tree.MethodNode;\nimport org.objectweb.asm.tree.analysis.Analyzer;\nimport org.objectweb.asm.tree.analysis.AnalyzerException;\nimport org.objectweb.asm.tree.analysis.Frame;\n\nimport java.util.Arrays;\n\nimport static org.objectweb.asm.Opcodes.*;\n\npublic class NullabilityDiagnosis {\n    public static int[] diagnose(String className, MethodNode mn) throws AnalyzerException {\n        // 第一步，获取Frame信息\n        Analyzer<NullabilityValue> analyzer = new NullabilityAnalyzer(new NullabilityInterpreter(Opcodes.ASM9));\n        Frame<NullabilityValue>[] frames = analyzer.analyze(className, mn);\n\n        // 第二步，判断是否为null或maybe-null，收集数据\n        TIntArrayList intArrayList = new TIntArrayList();\n        InsnList instructions = mn.instructions;\n        int size = instructions.size();\n        for (int i = 0; i < size; i++) {\n            AbstractInsnNode insn = instructions.get(i);\n            if (frames[i] != null) {\n                NullabilityValue value = getTarget(insn, frames[i]);\n                if (value == null) continue;\n                if (value.getState() == Nullability.NULL || value.getState() == Nullability.NULLABLE) {\n                    intArrayList.add(i);\n                }\n            }\n        }\n\n        // 第三步，将结果转换成int[]形式\n        int[] array = intArrayList.toNativeArray();\n        Arrays.sort(array);\n        return array;\n    }\n\n    private static NullabilityValue getTarget(AbstractInsnNode insn, Frame<NullabilityValue> frame) {\n        int opcode = insn.getOpcode();\n        switch (opcode) {\n            case GETFIELD:\n            case ARRAYLENGTH:\n            case MONITORENTER:\n            case MONITOREXIT:\n                return getStackValue(frame, 0);\n            case PUTFIELD:\n                return getStackValue(frame, 1);\n            case INVOKEVIRTUAL:\n            case INVOKESPECIAL:\n            case INVOKEINTERFACE:\n                String desc = ((MethodInsnNode) insn).desc;\n                return getStackValue(frame, Type.getArgumentTypes(desc).length);\n\n        }\n        return null;\n    }\n\n    private static NullabilityValue getStackValue(Frame<NullabilityValue> frame, int index) {\n        int top = frame.getStackSize() - 1;\n        return index <= top ? frame.getStack(top - index) : null;\n    }\n}\n"
  },
  {
    "path": "src/main/java/lsieun/asm/analysis/diagnosis/RedundantVariableDiagnosis.java",
    "content": "package lsieun.asm.analysis.diagnosis;\n\nimport lsieun.trove.TIntArrayList;\nimport org.objectweb.asm.Opcodes;\nimport org.objectweb.asm.tree.AbstractInsnNode;\nimport org.objectweb.asm.tree.InsnList;\nimport org.objectweb.asm.tree.MethodNode;\nimport org.objectweb.asm.tree.VarInsnNode;\nimport org.objectweb.asm.tree.analysis.*;\n\nimport java.util.Arrays;\n\npublic class RedundantVariableDiagnosis {\n    public static int[] diagnose(String className, MethodNode mn) throws AnalyzerException {\n        // 第一步，准备工作。使用SimpleVerifier进行分析，得到frames信息\n        Analyzer<BasicValue> analyzer = new Analyzer<>(new SimpleVerifier());\n        Frame<BasicValue>[] frames = analyzer.analyze(className, mn);\n\n        // 第二步，利用frames信息，查看local variable当中哪些slot数据出现了冗余\n        TIntArrayList localIndexList = new TIntArrayList();\n        for (Frame<BasicValue> f : frames) {\n            int locals = f.getLocals();\n            for (int i = 0; i < locals; i++) {\n                BasicValue val1 = f.getLocal(i);\n                if (val1 == BasicValue.UNINITIALIZED_VALUE) {\n                    continue;\n                }\n                for (int j = i + 1; j < locals; j++) {\n                    BasicValue val2 = f.getLocal(j);\n                    if (val2 == BasicValue.UNINITIALIZED_VALUE) {\n                        continue;\n                    }\n                    if (val1 == val2) {\n                        if (!localIndexList.contains(j)) {\n                            localIndexList.add(j);\n                        }\n                    }\n                }\n            }\n        }\n\n        // 第三步，将slot的索引值（local index）转换成instruction的索引值（insn index）\n        TIntArrayList insnIndexList = new TIntArrayList();\n        InsnList instructions = mn.instructions;\n        int size = instructions.size();\n        for (int i = 0; i < size; i++) {\n            AbstractInsnNode node = instructions.get(i);\n            int opcode = node.getOpcode();\n            if (opcode >= Opcodes.ISTORE && opcode <= Opcodes.ASTORE) {\n                VarInsnNode varInsnNode = (VarInsnNode) node;\n                if (localIndexList.contains(varInsnNode.var)) {\n                    if (!insnIndexList.contains(i)) {\n                        insnIndexList.add(i);\n                    }\n                }\n            }\n        }\n\n        // 第四步，将insnIndexList转换成int[]形式\n        int[] array = insnIndexList.toNativeArray();\n        Arrays.sort(array);\n        return array;\n    }\n}\n"
  },
  {
    "path": "src/main/java/lsieun/asm/analysis/diagnosis/RelatedInstructionDiagnosis.java",
    "content": "package lsieun.asm.analysis.diagnosis;\n\nimport lsieun.asm.analysis.transition.DestinationInterpreter;\nimport lsieun.trove.TIntArrayList;\nimport org.objectweb.asm.Opcodes;\nimport org.objectweb.asm.tree.AbstractInsnNode;\nimport org.objectweb.asm.tree.InsnList;\nimport org.objectweb.asm.tree.MethodNode;\nimport org.objectweb.asm.tree.VarInsnNode;\nimport org.objectweb.asm.tree.analysis.*;\n\nimport java.util.ArrayList;\nimport java.util.Arrays;\nimport java.util.List;\n\npublic class RelatedInstructionDiagnosis {\n    public static int[] diagnose(String className, MethodNode mn, int insnIndex) throws AnalyzerException {\n        // 第一步，判断insnIndex范围是否合理\n        InsnList instructions = mn.instructions;\n        int size = instructions.size();\n        if (insnIndex < 0 || insnIndex >= size) {\n            String message = String.format(\"the 'insnIndex' argument should in range [0, %d]\", size - 1);\n            throw new IllegalArgumentException(message);\n        }\n\n        // 第二步，获取两个Frame\n        Frame<SourceValue>[] sourceFrames = getSourceFrames(className, mn);\n        Frame<SourceValue>[] destinationFrames = getDestinationFrames(className, mn);\n\n        // 第三步，循环处理，所有结果记录到这个intArrayList变量中\n        TIntArrayList intArrayList = new TIntArrayList();\n        // 循环tmpInsnList\n        List<AbstractInsnNode> tmpInsnList = new ArrayList<>();\n        AbstractInsnNode insnNode = instructions.get(insnIndex);\n        tmpInsnList.add(insnNode);\n        for (int i = 0; i < tmpInsnList.size(); i++) {\n            AbstractInsnNode currentNode = tmpInsnList.get(i);\n            int opcode = currentNode.getOpcode();\n\n            int index = instructions.indexOf(currentNode);\n            intArrayList.add(index);\n\n            // 第一种情况，处理load相关的opcode情况\n            Frame<SourceValue> srcFrame = sourceFrames[index];\n            if (opcode >= Opcodes.ILOAD && opcode <= Opcodes.ALOAD) {\n                VarInsnNode varInsnNode = (VarInsnNode) currentNode;\n                int localIndex = varInsnNode.var;\n                SourceValue value = srcFrame.getLocal(localIndex);\n                for (AbstractInsnNode insn : value.insns) {\n                    if (!tmpInsnList.contains(insn)) {\n                        tmpInsnList.add(insn);\n                    }\n                }\n            }\n\n            // 第二种情况，从dstFrame到srcFrame查找\n            Frame<SourceValue> dstFrame = destinationFrames[index];\n            int stackSize = dstFrame.getStackSize();\n            for (int j = 0; j < stackSize; j++) {\n                SourceValue value = dstFrame.getStack(j);\n                if (value.insns.contains(currentNode)) {\n                    for (AbstractInsnNode insn : srcFrame.getStack(j).insns) {\n                        if (!tmpInsnList.contains(insn)) {\n                            tmpInsnList.add(insn);\n                        }\n                    }\n                }\n            }\n        }\n\n        // 第四步，将intArrayList变量转换成int[]，并进行排序\n        int[] array = intArrayList.toNativeArray();\n        Arrays.sort(array);\n        return array;\n    }\n\n\n    private static Frame<SourceValue>[] getSourceFrames(String className, MethodNode mn) throws AnalyzerException {\n        Analyzer<SourceValue> analyzer = new Analyzer<>(new SourceInterpreter());\n        return analyzer.analyze(className, mn);\n    }\n\n    private static Frame<SourceValue>[] getDestinationFrames(String className, MethodNode mn) throws AnalyzerException {\n        Analyzer<SourceValue> analyzer = new Analyzer<>(new DestinationInterpreter());\n        return analyzer.analyze(className, mn);\n    }\n}\n"
  },
  {
    "path": "src/main/java/lsieun/asm/analysis/diagnosis/ReverseEngineerMethodArgumentsDiagnosis.java",
    "content": "package lsieun.asm.analysis.diagnosis;\n\nimport org.objectweb.asm.Type;\nimport org.objectweb.asm.tree.*;\nimport org.objectweb.asm.tree.analysis.*;\n\nimport java.util.ArrayList;\nimport java.util.List;\n\npublic class ReverseEngineerMethodArgumentsDiagnosis {\n    private static final String UNKNOWN_VARIABLE_NAME = \"unknown\";\n\n    public static void diagnose(String className, MethodNode mn) throws AnalyzerException {\n        // 第一步，获取Frame信息\n        Analyzer<SourceValue> analyzer = new Analyzer<>(new SourceInterpreter());\n        Frame<SourceValue>[] frames = analyzer.analyze(className, mn);\n\n        // 第二步，获取LocalVariableTable信息\n        List<LocalVariableNode> localVariables = mn.localVariables;\n        if (localVariables == null || localVariables.size() < 1) {\n            System.out.println(\"LocalVariableTable is Empty\");\n            return;\n        }\n\n        // 第三步，获取instructions，并找到与invoke相关的指令\n        InsnList instructions = mn.instructions;\n        int[] methodInsnArray = findMethodInvokes(instructions);\n\n        // 第四步，对invoke相关的指令进行反编译\n        for (int methodInsn : methodInsnArray) {\n            // (1) 获取方法的参数\n            MethodInsnNode methodInsnNode = (MethodInsnNode) instructions.get(methodInsn);\n            Type methodType = Type.getMethodType(methodInsnNode.desc);\n            Type[] argumentTypes = methodType.getArgumentTypes();\n            int argNum = argumentTypes.length;\n\n            // (2) 从Frame当中获取指令，并将指令转换LocalVariableTable当中的变量名\n            Frame<SourceValue> f = frames[methodInsn];\n            int stackSize = f.getStackSize();\n            List<String> argList = new ArrayList<>();\n            for (int i = 0; i < argNum; i++) {\n                int stackIndex = stackSize - argNum + i;\n                SourceValue stackValue = f.getStack(stackIndex);\n                AbstractInsnNode insn = stackValue.insns.iterator().next();\n                String argName = getMethodVariableName(insn, localVariables);\n                argList.add(argName);\n            }\n\n            // (3) 将反编译的结果打印出来\n            String line = String.format(\"%s.%s(%s)\", methodInsnNode.owner, methodInsnNode.name, argList);\n            System.out.println(line);\n        }\n    }\n\n    public static String getMethodVariableName(AbstractInsnNode insn, List<LocalVariableNode> localVariables) {\n        if (insn instanceof VarInsnNode) {\n            VarInsnNode varInsnNode = (VarInsnNode) insn;\n            int localIndex = varInsnNode.var;\n\n            for (LocalVariableNode node : localVariables) {\n                if (node.index == localIndex) {\n                    return node.name;\n                }\n            }\n\n            return String.format(\"locals[%d]\", localIndex);\n        }\n        return UNKNOWN_VARIABLE_NAME;\n    }\n\n    public static int[] findMethodInvokes(InsnList instructions) {\n        int size = instructions.size();\n        boolean[] methodArray = new boolean[size];\n        for (int i = 0; i < size; i++) {\n            AbstractInsnNode node = instructions.get(i);\n            if (node instanceof MethodInsnNode) {\n                methodArray[i] = true;\n            }\n        }\n\n        int count = 0;\n        for (boolean flag : methodArray) {\n            if (flag) {\n                count++;\n            }\n        }\n\n        int[] array = new int[count];\n        int j = 0;\n        for (int i = 0; i < size; i++) {\n            boolean flag = methodArray[i];\n            if (flag) {\n                array[j] = i;\n                j++;\n            }\n        }\n        return array;\n    }\n}\n"
  },
  {
    "path": "src/main/java/lsieun/asm/analysis/graph/InsnBlock.java",
    "content": "package lsieun.asm.analysis.graph;\n\nimport java.util.ArrayList;\nimport java.util.List;\n\npublic class InsnBlock {\n    // 文字信息\n    public final List<String> lines = new ArrayList<>();\n\n    // 关联关系\n    public final List<InsnBlock> nextBlockList = new ArrayList<>();\n    public final List<InsnBlock> jumpBlockList = new ArrayList<>();\n\n    public void addLines(List<String> list) {\n        lines.addAll(list);\n    }\n\n    public void addNext(InsnBlock item) {\n        if (!nextBlockList.contains(item)) {\n            nextBlockList.add(item);\n        }\n    }\n\n    public void addJump(InsnBlock item) {\n        if (!jumpBlockList.contains(item)) {\n            jumpBlockList.add(item);\n        }\n    }\n\n}\n"
  },
  {
    "path": "src/main/java/lsieun/asm/analysis/graph/TextBox.java",
    "content": "package lsieun.asm.analysis.graph;\n\npublic class TextBox {\n    public final int row;\n    public final int col;\n    public final int width;\n    public final int height;\n\n    public TextBox(int row, int col, int width, int height) {\n        this.row = row;\n        this.col = col;\n        this.width = width;\n        this.height = height;\n    }\n\n    public static TextBox valueOf(int row, int col, int width, int height) {\n        return new TextBox(row, col, width, height);\n    }\n}\n"
  },
  {
    "path": "src/main/java/lsieun/asm/analysis/graph/TextGraph.java",
    "content": "package lsieun.asm.analysis.graph;\n\nimport lsieun.drawing.canvas.Box;\nimport lsieun.drawing.canvas.Canvas;\nimport lsieun.drawing.canvas.TextDirection;\nimport lsieun.drawing.theme.line.ContinuousLine;\nimport lsieun.drawing.theme.text.PlainText;\n\nimport java.util.ArrayList;\nimport java.util.List;\n\npublic class TextGraph {\n    private static final int ROW_SPACE = 1;\n    private static final int COL_SPACE = 3;\n\n    private final InsnBlock[] blockArray;\n    private final TextBox[] boxArray;\n    private final int boxNum;\n    private final int maxInstructionLength;\n    private final Canvas canvas = new Canvas();\n\n    public TextGraph(InsnBlock[] blockArray) {\n        this.blockArray = blockArray;\n        this.boxNum = blockArray.length;\n        this.boxArray = new TextBox[boxNum];\n        this.maxInstructionLength = findMaxStringLength(blockArray);\n    }\n\n    @SuppressWarnings(\"UnnecessaryLocalVariable\")\n    public void draw(int startRow, int startCol) {\n        int row = startRow;\n        int col = startCol;\n        int length = boxArray.length;\n        for (int i = 0; i < length; i++) {\n            InsnBlock block = blockArray[i];\n\n            int width = getOdd(maxInstructionLength + 2);\n            int height = block.lines.size();\n\n            TextBox box = TextBox.valueOf(row, col, width, height);\n            boxArray[i] = box;\n            drawBoxAndText(box, block.lines);\n\n            row += height + ROW_SPACE + 2;\n        }\n\n        drawLinks();\n\n        System.out.println(canvas);\n    }\n\n    private void drawBoxAndText(TextBox box, List<String> lines) {\n        int row = box.row;\n        int col = box.col;\n        int width = box.width;\n        int height = box.height;\n\n        canvas.moveTo(row, col);\n        canvas.drawRectangle(width, height);\n        canvas.draw(row + 1, col + 2, PlainText.valueOf(lines));\n    }\n\n    private void drawLinks() {\n        for (int i = 0; i < boxNum; i++) {\n            InsnBlock currentBlock = blockArray[i];\n            TextBox currentBox = boxArray[i];\n\n            // draw next\n            List<TextBox> nextBoxes = findBoxes(currentBlock.nextBlockList);\n            for (TextBox nextBox : nextBoxes) {\n                int rowStart = currentBox.row + currentBox.height + 1;\n                int rowStop = nextBox.row;\n                int col = currentBox.col + currentBox.width / 2;\n\n                canvas.moveTo(rowStart, col);\n                canvas.drawPixel(Box.DOWN_AND_HORIZONTAL);\n                canvas.moveTo(rowStop, col);\n                canvas.drawPixel(Box.UP_AND_HORIZONTAL);\n                canvas.moveTo(rowStart + 1, col).drawVerticalLine(rowStop - rowStart - 1);\n            }\n\n            // draw jump\n            List<TextBox> jumpBoxes = findBoxes(currentBlock.jumpBlockList);\n            for (TextBox nextBox : jumpBoxes) {\n                int rowStart = currentBox.row + currentBox.height;\n                int rowStop = nextBox.row + 1;\n                int colStart = currentBox.col + currentBox.width + 1;\n                int colStop = currentBox.col + currentBox.width + 1 + (i + 1) * COL_SPACE;\n\n                canvas.moveTo(rowStart, colStart);\n                canvas.drawPixel(Box.VERTICAL_AND_RIGHT);\n                canvas.moveTo(rowStop, colStart);\n                canvas.drawPixel(Box.VERTICAL_AND_RIGHT);\n\n                if (rowStart < rowStop) {\n                    ContinuousLine line = new ContinuousLine();\n                    line.setDirection(TextDirection.RIGHT);\n                    line.drawLine(colStop - colStart)\n                            .turn(TextDirection.DOWN).drawLine(rowStop - rowStart - 1)\n                            .turn(TextDirection.LEFT).drawLine(colStop - colStart);\n                    canvas.draw(rowStart, colStart + 1, line);\n\n                }\n                else {\n                    ContinuousLine line = new ContinuousLine();\n                    line.setDirection(TextDirection.RIGHT);\n                    line.drawLine(colStop - colStart)\n                            .turn(TextDirection.UP).drawLine(rowStart - rowStop - 1)\n                            .turn(TextDirection.LEFT).drawLine(colStop - colStart);\n                    canvas.draw(rowStart, colStart + 1, line);\n                }\n            }\n        }\n    }\n\n    private List<TextBox> findBoxes(List<InsnBlock> blockList) {\n        List<TextBox> boxList = new ArrayList<>();\n\n        for (int i = 0; i < boxNum; i++) {\n            InsnBlock block = blockArray[i];\n            if (blockList.contains(block)) {\n                boxList.add(boxArray[i]);\n            }\n        }\n\n        return boxList;\n    }\n\n    private int getOdd(int num) {\n        int remainder = num % 2;\n        if (remainder == 0) {\n            return num + 1;\n        }\n        return num;\n    }\n\n    private int findMaxStringLength(InsnBlock[] blockArray) {\n        int maxLength = 0;\n        for (InsnBlock block : blockArray) {\n            int length = findMaxStringLength(block.lines);\n            if (length > maxLength) {\n                maxLength = length;\n            }\n        }\n        return maxLength;\n    }\n\n    private int findMaxStringLength(List<String> lines) {\n        int maxLength = 0;\n        for (String item : lines) {\n            if (item == null) continue;\n            int length = item.length();\n            if (length > maxLength) {\n                maxLength = length;\n            }\n        }\n        return maxLength;\n    }\n}\n"
  },
  {
    "path": "src/main/java/lsieun/asm/analysis/nullability/NullDeferenceInterpreter.java",
    "content": "package lsieun.asm.analysis.nullability;\n\nimport org.objectweb.asm.Type;\nimport org.objectweb.asm.tree.AbstractInsnNode;\nimport org.objectweb.asm.tree.analysis.AnalyzerException;\nimport org.objectweb.asm.tree.analysis.BasicInterpreter;\nimport org.objectweb.asm.tree.analysis.BasicValue;\nimport org.objectweb.asm.tree.analysis.Value;\n\npublic class NullDeferenceInterpreter extends BasicInterpreter {\n    public final static BasicValue NULL_VALUE = new BasicValue(NULL_TYPE);\n    public final static BasicValue MAYBE_NULL_VALUE = new BasicValue(Type.getObjectType(\"may-be-null\"));\n\n    public NullDeferenceInterpreter(int api) {\n        super(api);\n    }\n\n    @Override\n    public BasicValue newOperation(AbstractInsnNode insn) throws AnalyzerException {\n        if (insn.getOpcode() == ACONST_NULL) {\n            return NULL_VALUE;\n        }\n        return super.newOperation(insn);\n    }\n\n    @Override\n    public BasicValue merge(BasicValue value1, BasicValue value2) {\n        if (isRef(value1) && isRef(value2) && value1 != value2) {\n            return MAYBE_NULL_VALUE;\n        }\n        return super.merge(value1, value2);\n    }\n\n    private boolean isRef(Value value) {\n        return value == BasicValue.REFERENCE_VALUE || value == NULL_VALUE || value == MAYBE_NULL_VALUE;\n    }\n}\n"
  },
  {
    "path": "src/main/java/lsieun/asm/analysis/nullability/Nullability.java",
    "content": "package lsieun.asm.analysis.nullability;\n\npublic enum Nullability {\n    UNKNOWN(0),\n    NOT_NULL(1),\n    NULL(1),\n    NULLABLE(2);\n\n    public final int priority;\n\n    Nullability(int priority) {\n        this.priority = priority;\n    }\n\n    public static Nullability merge(Nullability value1, Nullability value2) {\n        // 第一种情况，两者相等，则直接返回一个\n        if (value1 == value2) {\n            return value1;\n        }\n\n        // 第二种情况，两者不相等，比较优先级大小，谁大返回谁\n        int priority1 = value1.priority;\n        int priority2 = value2.priority;\n        if (priority1 > priority2) {\n            return value1;\n        }\n        else if (priority1 < priority2) {\n            return value2;\n        }\n\n        // 第三种情况，两者不相等，但优先级相等，则一个是NOT_NULL，另一个是NULL\n        return NULLABLE;\n    }\n}\n"
  },
  {
    "path": "src/main/java/lsieun/asm/analysis/nullability/NullabilityAnalyzer.java",
    "content": "package lsieun.asm.analysis.nullability;\n\nimport org.objectweb.asm.tree.analysis.Analyzer;\nimport org.objectweb.asm.tree.analysis.Frame;\nimport org.objectweb.asm.tree.analysis.Interpreter;\n\npublic class NullabilityAnalyzer extends Analyzer<NullabilityValue> {\n    public NullabilityAnalyzer(Interpreter<NullabilityValue> interpreter) {\n        super(interpreter);\n    }\n\n    @Override\n    protected Frame<NullabilityValue> newFrame(Frame<? extends NullabilityValue> frame) {\n        return new NullabilityFrame((NullabilityFrame) frame);\n    }\n\n    @Override\n    protected Frame<NullabilityValue> newFrame(int numLocals, int numStack) {\n        return new NullabilityFrame(numLocals, numStack);\n    }\n}\n"
  },
  {
    "path": "src/main/java/lsieun/asm/analysis/nullability/NullabilityFrame.java",
    "content": "package lsieun.asm.analysis.nullability;\n\nimport org.objectweb.asm.Opcodes;\nimport org.objectweb.asm.tree.LabelNode;\nimport org.objectweb.asm.tree.analysis.Frame;\n\npublic class NullabilityFrame extends Frame<NullabilityValue> {\n    public NullabilityFrame(int numLocals, int numStack) {\n        super(numLocals, numStack);\n    }\n\n    public NullabilityFrame(NullabilityFrame frame) {\n        super(frame);\n    }\n\n    @Override\n    public void initJumpTarget(int opcode, LabelNode target) {\n        // 首先，处理自己的代码逻辑\n        int stackIndex = getStackSize();\n        NullabilityValue oldValue = getStack(stackIndex);\n        switch (opcode) {\n            case Opcodes.IFNULL: {\n                if (target == null) {\n                    updateFrame(oldValue, Nullability.NOT_NULL);\n                }\n                else {\n                    updateFrame(oldValue, Nullability.NULL);\n                }\n                break;\n            }\n            case Opcodes.IFNONNULL: {\n                if (target == null) {\n                    updateFrame(oldValue, Nullability.NULL);\n                }\n                else {\n                    updateFrame(oldValue, Nullability.NOT_NULL);\n                }\n                break;\n            }\n        }\n\n        // 其次，调用父类的方法实现\n        super.initJumpTarget(opcode, target);\n    }\n\n    private void updateFrame(NullabilityValue oldValue, Nullability newState) {\n        NullabilityValue newValue = new NullabilityValue(oldValue.getType(), newState);\n        int numLocals = getLocals();\n        for (int i = 0; i < numLocals; i++) {\n            NullabilityValue currentValue = getLocal(i);\n            if (oldValue == currentValue) {\n                setLocal(i, newValue);\n            }\n        }\n\n        int numStack = getMaxStackSize();\n        for (int i = 0; i < numStack; i++) {\n            NullabilityValue currentValue = getStack(i);\n            if (oldValue == currentValue) {\n                setStack(i, newValue);\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "src/main/java/lsieun/asm/analysis/nullability/NullabilityInterpreter.java",
    "content": "package lsieun.asm.analysis.nullability;\n\nimport org.objectweb.asm.ConstantDynamic;\nimport org.objectweb.asm.Handle;\nimport org.objectweb.asm.Opcodes;\nimport org.objectweb.asm.Type;\nimport org.objectweb.asm.tree.*;\nimport org.objectweb.asm.tree.analysis.AnalyzerException;\nimport org.objectweb.asm.tree.analysis.Interpreter;\n\nimport java.util.List;\n\npublic class NullabilityInterpreter extends Interpreter<NullabilityValue> implements Opcodes {\n    public static final Type NULL_TYPE = Type.getObjectType(\"null\");\n\n    public static final NullabilityValue UNINITIALIZED_VALUE = new NullabilityValue(null);\n    public static final NullabilityValue RETURN_ADDRESS_VALUE = new NullabilityValue(Type.VOID_TYPE);\n\n    private final ClassLoader loader = getClass().getClassLoader();\n\n    public NullabilityInterpreter(int api) {\n        super(api);\n    }\n\n    @Override\n    public NullabilityValue newValue(Type type) {\n        if (type == null) {\n            return UNINITIALIZED_VALUE;\n        }\n\n        int sort = type.getSort();\n        if (sort == Type.VOID) {\n            return null;\n        }\n        return new NullabilityValue(type);\n    }\n\n    @Override\n    public NullabilityValue newOperation(AbstractInsnNode insn) throws AnalyzerException {\n        switch (insn.getOpcode()) {\n            case ACONST_NULL:\n                return new NullabilityValue(NULL_TYPE, Nullability.NULL);\n            case ICONST_M1:\n            case ICONST_0:\n            case ICONST_1:\n            case ICONST_2:\n            case ICONST_3:\n            case ICONST_4:\n            case ICONST_5:\n            case BIPUSH:\n            case SIPUSH:\n                return newValue(Type.INT_TYPE);\n            case LCONST_0:\n            case LCONST_1:\n                return newValue(Type.LONG_TYPE);\n            case FCONST_0:\n            case FCONST_1:\n            case FCONST_2:\n                return newValue(Type.FLOAT_TYPE);\n            case DCONST_0:\n            case DCONST_1:\n                return newValue(Type.DOUBLE_TYPE);\n            case LDC:\n                Object value = ((LdcInsnNode) insn).cst;\n                if (value instanceof Integer) {\n                    return newValue(Type.INT_TYPE);\n                }\n                else if (value instanceof Float) {\n                    return newValue(Type.FLOAT_TYPE);\n                }\n                else if (value instanceof Long) {\n                    return newValue(Type.LONG_TYPE);\n                }\n                else if (value instanceof Double) {\n                    return newValue(Type.DOUBLE_TYPE);\n                }\n                else if (value instanceof String) {\n                    return new NullabilityValue(Type.getObjectType(\"java/lang/String\"), Nullability.NOT_NULL);\n                }\n                else if (value instanceof Type) {\n                    int sort = ((Type) value).getSort();\n                    if (sort == Type.OBJECT || sort == Type.ARRAY) {\n                        return newValue(Type.getObjectType(\"java/lang/Class\"));\n                    }\n                    else if (sort == Type.METHOD) {\n                        return newValue(Type.getObjectType(\"java/lang/invoke/MethodType\"));\n                    }\n                    else {\n                        throw new AnalyzerException(insn, \"Illegal LDC value \" + value);\n                    }\n                }\n                else if (value instanceof Handle) {\n                    return newValue(Type.getObjectType(\"java/lang/invoke/MethodHandle\"));\n                }\n                else if (value instanceof ConstantDynamic) {\n                    return newValue(Type.getType(((ConstantDynamic) value).getDescriptor()));\n                }\n                else {\n                    throw new AnalyzerException(insn, \"Illegal LDC value \" + value);\n                }\n            case JSR:\n                return RETURN_ADDRESS_VALUE;\n            case GETSTATIC:\n                return newValue(Type.getType(((FieldInsnNode) insn).desc));\n            case NEW:\n                return newValue(Type.getObjectType(((TypeInsnNode) insn).desc));\n            default:\n                throw new AssertionError();\n        }\n    }\n\n    @Override\n    public NullabilityValue copyOperation(AbstractInsnNode insn, NullabilityValue value) {\n        return value;\n    }\n\n    @Override\n    public NullabilityValue unaryOperation(AbstractInsnNode insn, NullabilityValue value) throws AnalyzerException {\n        switch (insn.getOpcode()) {\n            case INEG:\n            case IINC:\n            case L2I:\n            case F2I:\n            case D2I:\n            case I2B:\n            case I2C:\n            case I2S:\n            case ARRAYLENGTH:\n            case INSTANCEOF:\n                return newValue(Type.INT_TYPE);\n            case FNEG:\n            case I2F:\n            case L2F:\n            case D2F:\n                return newValue(Type.FLOAT_TYPE);\n            case LNEG:\n            case I2L:\n            case F2L:\n            case D2L:\n                return newValue(Type.LONG_TYPE);\n            case DNEG:\n            case I2D:\n            case L2D:\n            case F2D:\n                return newValue(Type.DOUBLE_TYPE);\n            case GETFIELD:\n                return newValue(Type.getType(((FieldInsnNode) insn).desc));\n            case NEWARRAY:\n                switch (((IntInsnNode) insn).operand) {\n                    case T_BOOLEAN:\n                        return newValue(Type.getType(\"[Z\"));\n                    case T_CHAR:\n                        return newValue(Type.getType(\"[C\"));\n                    case T_BYTE:\n                        return newValue(Type.getType(\"[B\"));\n                    case T_SHORT:\n                        return newValue(Type.getType(\"[S\"));\n                    case T_INT:\n                        return newValue(Type.getType(\"[I\"));\n                    case T_FLOAT:\n                        return newValue(Type.getType(\"[F\"));\n                    case T_DOUBLE:\n                        return newValue(Type.getType(\"[D\"));\n                    case T_LONG:\n                        return newValue(Type.getType(\"[J\"));\n                    default:\n                        break;\n                }\n                throw new AnalyzerException(insn, \"Invalid array type\");\n            case ANEWARRAY:\n                return newValue(Type.getType(\"[\" + Type.getObjectType(((TypeInsnNode) insn).desc)));\n            case CHECKCAST:\n                return value;\n            case IFEQ:\n            case IFNE:\n            case IFLT:\n            case IFGE:\n            case IFGT:\n            case IFLE:\n            case TABLESWITCH:\n            case LOOKUPSWITCH:\n            case IRETURN:\n            case LRETURN:\n            case FRETURN:\n            case DRETURN:\n            case ARETURN:\n            case PUTSTATIC:\n            case ATHROW:\n            case MONITORENTER:\n            case MONITOREXIT:\n            case IFNULL:\n            case IFNONNULL:\n                return null;\n            default:\n                throw new AssertionError();\n        }\n    }\n\n    @Override\n    public NullabilityValue binaryOperation(AbstractInsnNode insn,\n                                            NullabilityValue value1,\n                                            NullabilityValue value2) {\n        switch (insn.getOpcode()) {\n            case IALOAD:\n            case BALOAD:\n            case CALOAD:\n            case SALOAD:\n            case IADD:\n            case ISUB:\n            case IMUL:\n            case IDIV:\n            case IREM:\n            case ISHL:\n            case ISHR:\n            case IUSHR:\n            case IAND:\n            case IOR:\n            case IXOR:\n            case LCMP:\n            case FCMPL:\n            case FCMPG:\n            case DCMPL:\n            case DCMPG:\n                return newValue(Type.INT_TYPE);\n            case FALOAD:\n            case FADD:\n            case FSUB:\n            case FMUL:\n            case FDIV:\n            case FREM:\n                return newValue(Type.FLOAT_TYPE);\n            case LALOAD:\n            case LADD:\n            case LSUB:\n            case LMUL:\n            case LDIV:\n            case LREM:\n            case LSHL:\n            case LSHR:\n            case LUSHR:\n            case LAND:\n            case LOR:\n            case LXOR:\n                return newValue(Type.LONG_TYPE);\n            case DALOAD:\n            case DADD:\n            case DSUB:\n            case DMUL:\n            case DDIV:\n            case DREM:\n                return newValue(Type.LONG_TYPE);\n            case AALOAD:\n                return getElementValue(value1);\n            case IF_ICMPEQ:\n            case IF_ICMPNE:\n            case IF_ICMPLT:\n            case IF_ICMPGE:\n            case IF_ICMPGT:\n            case IF_ICMPLE:\n            case IF_ACMPEQ:\n            case IF_ACMPNE:\n            case PUTFIELD:\n                return null;\n            default:\n                throw new AssertionError();\n        }\n    }\n\n    @Override\n    public NullabilityValue ternaryOperation(AbstractInsnNode insn,\n                                             NullabilityValue value1,\n                                             NullabilityValue value2,\n                                             NullabilityValue value3) {\n        return null;\n    }\n\n    @Override\n    public NullabilityValue naryOperation(AbstractInsnNode insn,\n                                          List<? extends NullabilityValue> values) {\n        int opcode = insn.getOpcode();\n        if (opcode == MULTIANEWARRAY) {\n            return newValue(Type.getType(((MultiANewArrayInsnNode) insn).desc));\n        }\n        else if (opcode == INVOKEDYNAMIC) {\n            return newValue(Type.getReturnType(((InvokeDynamicInsnNode) insn).desc));\n        }\n        else {\n            return newValue(Type.getReturnType(((MethodInsnNode) insn).desc));\n        }\n    }\n\n    @Override\n    public void returnOperation(AbstractInsnNode insn,\n                                NullabilityValue value,\n                                NullabilityValue expected) {\n        // Nothing to do.\n    }\n\n    @Override\n    public NullabilityValue merge(NullabilityValue value1, NullabilityValue value2) {\n        // 合并两者的状态\n        Nullability mergedState = Nullability.merge(value1.getState(), value2.getState());\n\n\n        // 第一种情况，两个value的类型相同且状态（state）相同\n        if (value1.equals(value2)) {\n            return value1;\n        }\n\n        // 第二种情况，两个value的类型相同，但状态（state）不同，需要合并它们的状态（state）\n        Type type1 = value1.getType();\n        Type type2 = value2.getType();\n        if (type1 != null && type1.equals(type2)) {\n            Type type = value1.getType();\n            return new NullabilityValue(type, mergedState);\n        }\n\n        // 第三种情况，两个value的类型不相同的，而且要合并它们的状态（state）\n        if (type1 != null\n                && (type1.getSort() == Type.OBJECT || type1.getSort() == Type.ARRAY)\n                && type2 != null\n                && (type2.getSort() == Type.OBJECT || type2.getSort() == Type.ARRAY)) {\n            if (type1.equals(NULL_TYPE)) {\n                return new NullabilityValue(type2, mergedState);\n            }\n            if (type2.equals(NULL_TYPE)) {\n                return new NullabilityValue(type1, mergedState);\n            }\n            if (isAssignableFrom(type1, type2)) {\n                return new NullabilityValue(type1, mergedState);\n            }\n            if (isAssignableFrom(type2, type1)) {\n                return new NullabilityValue(type2, mergedState);\n            }\n            int numDimensions = 0;\n            if (type1.getSort() == Type.ARRAY\n                    && type2.getSort() == Type.ARRAY\n                    && type1.getDimensions() == type2.getDimensions()\n                    && type1.getElementType().getSort() == Type.OBJECT\n                    && type2.getElementType().getSort() == Type.OBJECT) {\n                numDimensions = type1.getDimensions();\n                type1 = type1.getElementType();\n                type2 = type2.getElementType();\n            }\n\n\n            while (true) {\n                if (type1 == null || isInterface(type1)) {\n                    NullabilityValue arrayValue = newArrayValue(Type.getObjectType(\"java/lang/Object\"), numDimensions);\n                    return new NullabilityValue(arrayValue.getType(), mergedState);\n                }\n                type1 = getSuperClass(type1);\n                if (isAssignableFrom(type1, type2)) {\n                    NullabilityValue arrayValue = newArrayValue(type1, numDimensions);\n                    return new NullabilityValue(arrayValue.getType(), mergedState);\n                }\n            }\n        }\n        return UNINITIALIZED_VALUE;\n    }\n\n    protected boolean isInterface(final Type type) {\n        return getClass(type).isInterface();\n    }\n\n    protected Type getSuperClass(final Type type) {\n        Class<?> superClass = getClass(type).getSuperclass();\n        return superClass == null ? null : Type.getType(superClass);\n    }\n\n    private NullabilityValue newArrayValue(final Type type, final int dimensions) {\n        if (dimensions == 0) {\n            return newValue(type);\n        }\n        else {\n            StringBuilder descriptor = new StringBuilder();\n            for (int i = 0; i < dimensions; ++i) {\n                descriptor.append('[');\n            }\n            descriptor.append(type.getDescriptor());\n            return newValue(Type.getType(descriptor.toString()));\n        }\n    }\n\n    protected NullabilityValue getElementValue(final NullabilityValue objectArrayValue) {\n        Type arrayType = objectArrayValue.getType();\n        if (arrayType != null) {\n            if (arrayType.getSort() == Type.ARRAY) {\n                return newValue(Type.getType(arrayType.getDescriptor().substring(1)));\n            }\n            else if (arrayType.equals(NULL_TYPE)) {\n                return objectArrayValue;\n            }\n        }\n        throw new AssertionError();\n    }\n\n    protected boolean isSubTypeOf(final NullabilityValue value, final NullabilityValue expected) {\n        Type expectedType = expected.getType();\n        Type type = value.getType();\n        switch (expectedType.getSort()) {\n            case Type.INT:\n            case Type.FLOAT:\n            case Type.LONG:\n            case Type.DOUBLE:\n                return type.equals(expectedType);\n            case Type.ARRAY:\n            case Type.OBJECT:\n                if (type.equals(NULL_TYPE)) {\n                    return true;\n                }\n                else if (type.getSort() == Type.OBJECT || type.getSort() == Type.ARRAY) {\n                    if (isAssignableFrom(expectedType, type)) {\n                        return true;\n                    }\n                    else if (getClass(expectedType).isInterface()) {\n                        // The merge of class or interface types can only yield class types (because it is not\n                        // possible in general to find an unambiguous common super interface, due to multiple\n                        // inheritance). Because of this limitation, we need to relax the subtyping check here\n                        // if 'value' is an interface.\n                        return Object.class.isAssignableFrom(getClass(type));\n                    }\n                    else {\n                        return false;\n                    }\n                }\n                else {\n                    return false;\n                }\n            default:\n                throw new AssertionError();\n        }\n    }\n\n    protected boolean isAssignableFrom(final Type type1, final Type type2) {\n        if (type1.equals(type2)) {\n            return true;\n        }\n        return getClass(type1).isAssignableFrom(getClass(type2));\n    }\n\n    protected Class<?> getClass(final Type type) {\n        try {\n            if (type.getSort() == Type.ARRAY) {\n                return Class.forName(type.getDescriptor().replace('/', '.'), false, loader);\n            }\n            return Class.forName(type.getClassName(), false, loader);\n        }\n        catch (ClassNotFoundException e) {\n            throw new TypeNotPresentException(e.toString(), e);\n        }\n    }\n}\n"
  },
  {
    "path": "src/main/java/lsieun/asm/analysis/nullability/NullabilityValue.java",
    "content": "package lsieun.asm.analysis.nullability;\n\nimport org.objectweb.asm.Type;\nimport org.objectweb.asm.tree.analysis.Value;\n\npublic class NullabilityValue implements Value {\n    private final Type type;\n    private Nullability state;\n\n    public NullabilityValue(Type type) {\n        this(type, Nullability.UNKNOWN);\n    }\n\n    public NullabilityValue(Type type, Nullability state) {\n        this.type = type;\n        this.state = state;\n    }\n\n    public Type getType() {\n        return type;\n    }\n\n    public void setState(Nullability state) {\n        this.state = state;\n    }\n\n    public Nullability getState() {\n        return state;\n    }\n\n    @Override\n    public int getSize() {\n        return type == Type.LONG_TYPE || type == Type.DOUBLE_TYPE ? 2 : 1;\n    }\n\n    public boolean isReference() {\n        return type != null && (type.getSort() == Type.OBJECT || type.getSort() == Type.ARRAY);\n    }\n\n    @Override\n    public boolean equals(final Object value) {\n        if (value == this) {\n            return true;\n        }\n        else if (value instanceof NullabilityValue) {\n            NullabilityValue another = (NullabilityValue) value;\n            if (type == null) {\n                return ((NullabilityValue) value).type == null;\n            }\n            else {\n                return type.equals(((NullabilityValue) value).type) && state == another.state;\n            }\n        }\n        else {\n            return false;\n        }\n    }\n\n    @Override\n    public int hashCode() {\n        return type == null ? 0 : type.hashCode();\n    }\n}\n"
  },
  {
    "path": "src/main/java/lsieun/asm/analysis/state/StateInterpreter.java",
    "content": "package lsieun.asm.analysis.state;\n\nimport org.objectweb.asm.Opcodes;\nimport org.objectweb.asm.Type;\nimport org.objectweb.asm.tree.AbstractInsnNode;\nimport org.objectweb.asm.tree.analysis.AnalyzerException;\nimport org.objectweb.asm.tree.analysis.Interpreter;\n\nimport java.util.List;\n\npublic class StateInterpreter extends Interpreter<StateValue> implements Opcodes {\n    public StateInterpreter() {\n        super(ASM9);\n        if (getClass() != StateInterpreter.class) {\n            throw new IllegalStateException();\n        }\n    }\n\n    public StateInterpreter(int api) {\n        super(api);\n    }\n\n    @Override\n    public StateValue newValue(Type type) {\n        return null;\n    }\n\n    @Override\n    public StateValue newOperation(AbstractInsnNode insn) throws AnalyzerException {\n        return null;\n    }\n\n    @Override\n    public StateValue copyOperation(AbstractInsnNode insn, StateValue value) throws AnalyzerException {\n        return null;\n    }\n\n    @Override\n    public StateValue unaryOperation(AbstractInsnNode insn, StateValue value) throws AnalyzerException {\n        return null;\n    }\n\n    @Override\n    public StateValue binaryOperation(AbstractInsnNode insn, StateValue value1, StateValue value2) throws AnalyzerException {\n        return null;\n    }\n\n    @Override\n    public StateValue ternaryOperation(AbstractInsnNode insn, StateValue value1, StateValue value2, StateValue value3) throws AnalyzerException {\n        return null;\n    }\n\n    @Override\n    public StateValue naryOperation(AbstractInsnNode insn, List<? extends StateValue> values) throws AnalyzerException {\n        return null;\n    }\n\n    @Override\n    public void returnOperation(AbstractInsnNode insn, StateValue value, StateValue expected) throws AnalyzerException {\n\n    }\n\n    @Override\n    public StateValue merge(StateValue value1, StateValue value2) {\n        return null;\n    }\n}\n"
  },
  {
    "path": "src/main/java/lsieun/asm/analysis/state/StateType.java",
    "content": "package lsieun.asm.analysis.state;\n\npublic enum StateType {\n    DEFAULT, // default\n    TOP,\n    INT,\n    FLOAT,\n    LONG,\n    DOUBLE,\n    NULL,\n\n    CP, // constant pool\n    HEAP; // 从堆内存上分配的对象\n}\n"
  },
  {
    "path": "src/main/java/lsieun/asm/analysis/state/StateValue.java",
    "content": "package lsieun.asm.analysis.state;\n\nimport org.objectweb.asm.tree.analysis.Value;\n\npublic class StateValue implements Value {\n    public final int size;\n\n    public StateValue(int size) {\n        this.size = size;\n    }\n\n    @Override\n    public int getSize() {\n        return size;\n    }\n}\n"
  },
  {
    "path": "src/main/java/lsieun/asm/analysis/transition/DestinationInterpreter.java",
    "content": "package lsieun.asm.analysis.transition;\n\nimport org.objectweb.asm.Opcodes;\nimport org.objectweb.asm.Type;\nimport org.objectweb.asm.tree.*;\nimport org.objectweb.asm.tree.analysis.AnalyzerException;\nimport org.objectweb.asm.tree.analysis.Interpreter;\nimport org.objectweb.asm.tree.analysis.SourceValue;\n\nimport java.util.HashSet;\nimport java.util.List;\n\npublic class DestinationInterpreter extends Interpreter<SourceValue> implements Opcodes {\n    public DestinationInterpreter() {\n        super(ASM9);\n        if (getClass() != DestinationInterpreter.class) {\n            throw new IllegalStateException();\n        }\n    }\n\n    protected DestinationInterpreter(final int api) {\n        super(api);\n    }\n\n    @Override\n    public SourceValue newValue(Type type) {\n        if (type == Type.VOID_TYPE) {\n            return null;\n        }\n        return new SourceValue(type == null ? 1 : type.getSize(), new HashSet<>());\n    }\n\n    @Override\n    public SourceValue newOperation(AbstractInsnNode insn) {\n        int size;\n        switch (insn.getOpcode()) {\n            case LCONST_0:\n            case LCONST_1:\n            case DCONST_0:\n            case DCONST_1:\n                size = 2;\n                break;\n            case LDC:\n                Object value = ((LdcInsnNode) insn).cst;\n                size = value instanceof Long || value instanceof Double ? 2 : 1;\n                break;\n            case GETSTATIC:\n                size = Type.getType(((FieldInsnNode) insn).desc).getSize();\n                break;\n            default:\n                size = 1;\n                break;\n        }\n        return new SourceValue(size, new HashSet<>());\n    }\n\n    @Override\n    public SourceValue copyOperation(AbstractInsnNode insn, SourceValue value) throws AnalyzerException {\n        int opcode = insn.getOpcode();\n        if (opcode >= ISTORE && opcode <= ASTORE) {\n            value.insns.add(insn);\n        }\n\n        return new SourceValue(value.getSize(), new HashSet<>());\n    }\n\n    @Override\n    public SourceValue unaryOperation(AbstractInsnNode insn, SourceValue value) throws AnalyzerException {\n        value.insns.add(insn);\n\n        int size;\n        switch (insn.getOpcode()) {\n            case LNEG:\n            case DNEG:\n            case I2L:\n            case I2D:\n            case L2D:\n            case F2L:\n            case F2D:\n            case D2L:\n                size = 2;\n                break;\n            case GETFIELD:\n                size = Type.getType(((FieldInsnNode) insn).desc).getSize();\n                break;\n            default:\n                size = 1;\n                break;\n        }\n        return new SourceValue(size, new HashSet<>());\n    }\n\n    @Override\n    public SourceValue binaryOperation(AbstractInsnNode insn, SourceValue value1, SourceValue value2) throws AnalyzerException {\n        value1.insns.add(insn);\n        value2.insns.add(insn);\n\n        int size;\n        switch (insn.getOpcode()) {\n            case LALOAD:\n            case DALOAD:\n            case LADD:\n            case DADD:\n            case LSUB:\n            case DSUB:\n            case LMUL:\n            case DMUL:\n            case LDIV:\n            case DDIV:\n            case LREM:\n            case DREM:\n            case LSHL:\n            case LSHR:\n            case LUSHR:\n            case LAND:\n            case LOR:\n            case LXOR:\n                size = 2;\n                break;\n            default:\n                size = 1;\n                break;\n        }\n        return new SourceValue(size, new HashSet<>());\n    }\n\n    @Override\n    public SourceValue ternaryOperation(AbstractInsnNode insn, SourceValue value1, SourceValue value2, SourceValue value3) throws AnalyzerException {\n        value1.insns.add(insn);\n        value2.insns.add(insn);\n        value3.insns.add(insn);\n\n        return new SourceValue(1, new HashSet<>());\n    }\n\n    @Override\n    public SourceValue naryOperation(AbstractInsnNode insn, List<? extends SourceValue> values) throws AnalyzerException {\n        if (values != null) {\n            for (SourceValue v : values) {\n                v.insns.add(insn);\n            }\n        }\n\n        int size;\n        int opcode = insn.getOpcode();\n        if (opcode == MULTIANEWARRAY) {\n            size = 1;\n        }\n        else if (opcode == INVOKEDYNAMIC) {\n            size = Type.getReturnType(((InvokeDynamicInsnNode) insn).desc).getSize();\n        }\n        else {\n            size = Type.getReturnType(((MethodInsnNode) insn).desc).getSize();\n        }\n        return new SourceValue(size, new HashSet<>());\n    }\n\n    @Override\n    public void returnOperation(AbstractInsnNode insn, SourceValue value, SourceValue expected) throws AnalyzerException {\n        // Nothing to do.\n    }\n\n    @Override\n    public SourceValue merge(final SourceValue value1, final SourceValue value2) {\n        return new SourceValue(Math.min(value1.size, value2.size), new HashSet<>());\n    }\n}\n"
  },
  {
    "path": "src/main/java/lsieun/asm/commons/ClassRemapperExample01.java",
    "content": "package lsieun.asm.commons;\n\nimport lsieun.utils.FileUtils;\nimport org.objectweb.asm.ClassReader;\nimport org.objectweb.asm.ClassVisitor;\nimport org.objectweb.asm.ClassWriter;\nimport org.objectweb.asm.commons.ClassRemapper;\nimport org.objectweb.asm.commons.Remapper;\nimport org.objectweb.asm.commons.SimpleRemapper;\n\npublic class ClassRemapperExample01 {\n    public static void main(String[] args) {\n        String origin_name = \"sample/HelloWorld\";\n        String target_name = \"sample/GoodChild\";\n        String origin_filepath = getFilePath(origin_name);\n        byte[] bytes1 = FileUtils.readBytes(origin_filepath);\n\n        //（1）构建ClassReader\n        ClassReader cr = new ClassReader(bytes1);\n\n        //（2）构建ClassWriter\n        ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_FRAMES);\n\n        //（3）串连ClassVisitor\n        Remapper remapper = new SimpleRemapper(origin_name, target_name);\n        ClassVisitor cv = new ClassRemapper(cw, remapper);\n\n        //（4）两者进行结合\n        int parsingOptions = ClassReader.SKIP_DEBUG | ClassReader.SKIP_FRAMES;\n        cr.accept(cv, parsingOptions);\n\n        //（5）重新生成Class\n        byte[] bytes2 = cw.toByteArray();\n\n        String target_filepath = getFilePath(target_name);\n        FileUtils.writeBytes(target_filepath, bytes2);\n    }\n\n    public static String getFilePath(String internalName) {\n        String relative_path = String.format(\"%s.class\", internalName);\n        return FileUtils.getFilePath(relative_path);\n    }\n}\n"
  },
  {
    "path": "src/main/java/lsieun/asm/commons/ClassRemapperExample02.java",
    "content": "package lsieun.asm.commons;\n\nimport lsieun.utils.FileUtils;\nimport org.objectweb.asm.ClassReader;\nimport org.objectweb.asm.ClassVisitor;\nimport org.objectweb.asm.ClassWriter;\nimport org.objectweb.asm.commons.ClassRemapper;\nimport org.objectweb.asm.commons.Remapper;\nimport org.objectweb.asm.commons.SimpleRemapper;\n\nimport java.util.HashMap;\nimport java.util.Map;\n\npublic class ClassRemapperExample02 {\n    public static void main(String[] args) {\n        String origin_name = \"sample/HelloWorld\";\n        String target_name = \"sample/GoodChild\";\n        String origin_filepath = getFilePath(origin_name);\n        byte[] bytes1 = FileUtils.readBytes(origin_filepath);\n\n        //（1）构建ClassReader\n        ClassReader cr = new ClassReader(bytes1);\n\n        //（2）构建ClassWriter\n        ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_FRAMES);\n\n        //（3）串连ClassVisitor\n        Map<String, String> mapping = new HashMap<>();\n        mapping.put(origin_name, target_name);\n        mapping.put(origin_name + \".intValue\", \"a\");\n        mapping.put(origin_name + \".test()V\", \"b\");\n        Remapper mapper = new SimpleRemapper(mapping);\n        ClassVisitor cv = new ClassRemapper(cw, mapper);\n\n        //（4）两者进行结合\n        int parsingOptions = ClassReader.SKIP_DEBUG | ClassReader.SKIP_FRAMES;\n        cr.accept(cv, parsingOptions);\n\n        //（5）重新生成Class\n        byte[] bytes2 = cw.toByteArray();\n\n        String target_filepath = getFilePath(target_name);\n        FileUtils.writeBytes(target_filepath, bytes2);\n    }\n\n    public static String getFilePath(String internalName) {\n        String relative_path = String.format(\"%s.class\", internalName);\n        return FileUtils.getFilePath(relative_path);\n    }\n}\n"
  },
  {
    "path": "src/main/java/lsieun/asm/commons/ClassRemapperExample03.java",
    "content": "package lsieun.asm.commons;\n\nimport lsieun.utils.FileUtils;\nimport org.objectweb.asm.ClassReader;\nimport org.objectweb.asm.ClassVisitor;\nimport org.objectweb.asm.ClassWriter;\nimport org.objectweb.asm.commons.ClassRemapper;\nimport org.objectweb.asm.commons.Remapper;\nimport org.objectweb.asm.commons.SimpleRemapper;\n\nimport java.util.HashMap;\nimport java.util.Map;\n\npublic class ClassRemapperExample03 {\n    public static void main(String[] args) {\n        Map<String, String> mapping = new HashMap<>();\n        mapping.put(\"sample/HelloWorld\", \"sample/AAA\");\n        mapping.put(\"sample/GoodChild\", \"sample/BBB\");\n        mapping.put(\"sample/HelloWorld.test()V\", \"a\");\n        mapping.put(\"sample/GoodChild.study()V\", \"b\");\n        obfuscate(\"sample/HelloWorld\", \"sample/AAA\", mapping);\n        obfuscate(\"sample/GoodChild\", \"sample/BBB\", mapping);\n    }\n\n    public static void obfuscate(String origin_name, String target_name, Map<String, String> mapping) {\n        String origin_filepath = getFilePath(origin_name);\n        byte[] bytes1 = FileUtils.readBytes(origin_filepath);\n\n        //（1）构建ClassReader\n        ClassReader cr = new ClassReader(bytes1);\n\n        //（2）构建ClassWriter\n        ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_FRAMES);\n\n        //（3）串连ClassVisitor\n        Remapper mapper = new SimpleRemapper(mapping);\n        ClassVisitor cv = new ClassRemapper(cw, mapper);\n\n        //（4）两者进行结合\n        int parsingOptions = ClassReader.SKIP_DEBUG | ClassReader.SKIP_FRAMES;\n        cr.accept(cv, parsingOptions);\n\n        //（5）重新生成Class\n        byte[] bytes2 = cw.toByteArray();\n\n        String target_filepath = getFilePath(target_name);\n        FileUtils.writeBytes(target_filepath, bytes2);\n    }\n\n    public static String getFilePath(String internalName) {\n        String relative_path = String.format(\"%s.class\", internalName);\n        return FileUtils.getFilePath(relative_path);\n    }\n}\n"
  },
  {
    "path": "src/main/java/lsieun/asm/commons/GeneratorAdapterExample01.java",
    "content": "package lsieun.asm.commons;\n\nimport lsieun.utils.FileUtils;\nimport org.objectweb.asm.ClassWriter;\nimport org.objectweb.asm.Type;\nimport org.objectweb.asm.commons.GeneratorAdapter;\nimport org.objectweb.asm.commons.Method;\nimport org.objectweb.asm.util.TraceClassVisitor;\n\nimport java.io.PrintStream;\nimport java.io.PrintWriter;\n\nimport static org.objectweb.asm.Opcodes.*;\n\npublic class GeneratorAdapterExample01 {\n    public static void main(String[] args) throws Exception {\n        String relative_path = \"sample/HelloWorld.class\";\n        String filepath = FileUtils.getFilePath(relative_path);\n\n        // (1) 生成byte[]内容\n        byte[] bytes = dump();\n\n        // (2) 保存byte[]到文件\n        FileUtils.writeBytes(filepath, bytes);\n    }\n\n    public static byte[] dump() throws Exception {\n        ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_FRAMES);\n        PrintWriter printWriter = new PrintWriter(System.out);\n        TraceClassVisitor cv = new TraceClassVisitor(cw, printWriter);\n\n        cv.visit(V1_8, ACC_PUBLIC + ACC_SUPER, \"sample/HelloWorld\", null, \"java/lang/Object\", null);\n\n        {\n            Method m1 = Method.getMethod(\"void <init> ()\");\n            GeneratorAdapter mg = new GeneratorAdapter(ACC_PUBLIC, m1, null, null, cv);\n            mg.loadThis();\n            mg.invokeConstructor(Type.getType(Object.class), m1);\n            mg.returnValue();\n            mg.endMethod();\n        }\n\n        {\n            Method m2 = Method.getMethod(\"void main (String[])\");\n            GeneratorAdapter mg = new GeneratorAdapter(ACC_PUBLIC + ACC_STATIC, m2, null, null, cv);\n            mg.getStatic(Type.getType(System.class), \"out\", Type.getType(PrintStream.class));\n            mg.push(\"Hello world!\");\n            mg.invokeVirtual(Type.getType(PrintStream.class), Method.getMethod(\"void println (String)\"));\n            mg.returnValue();\n            mg.endMethod();\n        }\n\n        cv.visitEnd();\n\n        return cw.toByteArray();\n    }\n}\n"
  },
  {
    "path": "src/main/java/lsieun/asm/commons/InstructionAdapterExample01.java",
    "content": "package lsieun.asm.commons;\n\nimport lsieun.utils.FileUtils;\nimport org.objectweb.asm.ClassWriter;\nimport org.objectweb.asm.MethodVisitor;\nimport org.objectweb.asm.Type;\nimport org.objectweb.asm.commons.InstructionAdapter;\n\nimport static org.objectweb.asm.Opcodes.*;\n\npublic class InstructionAdapterExample01 {\n    public static void main(String[] args) throws Exception {\n        String relative_path = \"sample/HelloWorld.class\";\n        String filepath = FileUtils.getFilePath(relative_path);\n\n        // (1) 生成byte[]内容\n        byte[] bytes = dump();\n\n        // (2) 保存byte[]到文件\n        FileUtils.writeBytes(filepath, bytes);\n    }\n\n    public static byte[] dump() throws Exception {\n        // (1) 创建ClassWriter对象\n        ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_FRAMES);\n\n        // (2) 调用visitXxx()方法\n        cw.visit(V1_8, ACC_PUBLIC + ACC_SUPER, \"sample/HelloWorld\",\n                null, \"java/lang/Object\", null);\n\n        {\n            MethodVisitor mv1 = cw.visitMethod(ACC_PUBLIC, \"<init>\", \"()V\", null, null);\n            InstructionAdapter ia = new InstructionAdapter(mv1);\n            ia.visitCode();\n            ia.load(0, InstructionAdapter.OBJECT_TYPE);\n            ia.invokespecial(\"java/lang/Object\", \"<init>\", \"()V\", false);\n            ia.areturn(Type.VOID_TYPE);\n            ia.visitMaxs(1, 1);\n            ia.visitEnd();\n        }\n\n        {\n            MethodVisitor mv2 = cw.visitMethod(ACC_PUBLIC, \"test\", \"()V\", null, null);\n            InstructionAdapter ia = new InstructionAdapter(mv2);\n            ia.visitCode();\n            ia.getstatic(\"java/lang/System\", \"out\", \"Ljava/io/PrintStream;\");\n            ia.aconst(\"Hello World\");\n            ia.invokevirtual(\"java/io/PrintStream\", \"println\", \"(Ljava/lang/String;)V\", false);\n            ia.areturn(Type.VOID_TYPE);\n            ia.visitMaxs(2, 1);\n            ia.visitEnd();\n        }\n\n        cw.visitEnd();\n\n        // (3) 调用toByteArray()方法\n        return cw.toByteArray();\n    }\n}\n"
  },
  {
    "path": "src/main/java/lsieun/asm/commons/MethodStackMapFrame02Visitor.java",
    "content": "package lsieun.asm.commons;\n\nimport lsieun.classfile.ClassFile;\nimport lsieun.classfile.InsnRaw;\nimport org.objectweb.asm.*;\nimport org.objectweb.asm.commons.AnalyzerAdapter;\n\nimport java.util.List;\n\npublic class MethodStackMapFrame02Visitor extends ClassVisitor {\n    private String owner;\n    private final ClassFile classFile;\n    private int methodIndex = 0;\n\n    public MethodStackMapFrame02Visitor(int api, ClassVisitor classVisitor, byte[] bytes) {\n        super(api, classVisitor);\n        this.classFile = new ClassFile(bytes);\n    }\n\n    @Override\n    public void visit(int version, int access, String name, String signature, String superName, String[] interfaces) {\n        super.visit(version, access, name, signature, superName, interfaces);\n        this.owner = name;\n    }\n\n    @Override\n    public MethodVisitor visitMethod(int access, String name, String descriptor, String signature, String[] exceptions) {\n        MethodVisitor mv = super.visitMethod(access, name, descriptor, signature, exceptions);\n        byte[] code_bytes = classFile.getCode(methodIndex++);\n        InsnRaw insnRaw = new InsnRaw(code_bytes);\n        List<String> list = insnRaw.getList();\n        mv = new MethodStackMapFrame02Adapter(api, owner, access, name, descriptor, mv, list);\n        return mv;\n    }\n\n    private static class MethodStackMapFrame02Adapter extends AnalyzerAdapter {\n        private static final String FORMAT = \"%-30s // %s\";\n        private static final String START = \"{\";\n        private static final String STOP = \"}\";\n        private static final String EMPTY = \"{}\";\n        private static final String SEPARATOR = \"|\";\n\n        private final int methodAccess;\n        private final String methodName;\n        private final String methodDesc;\n        private final List<String> insnList;\n        private int insnIndex = 0;\n\n        public MethodStackMapFrame02Adapter(int api, String owner, int access, String name, String descriptor, MethodVisitor methodVisitor, List<String> insnList) {\n            super(api, owner, access, name, descriptor, methodVisitor);\n            this.methodAccess = access;\n            this.methodName = name;\n            this.methodDesc = descriptor;\n            this.insnList = insnList;\n        }\n\n        @Override\n        public void visitCode() {\n            super.visitCode();\n\n            System.out.println(methodName + \":\" + methodDesc);\n            String frame = getStackFrame();\n            String line = String.format(FORMAT, \"\", frame);\n            System.out.println(line);\n        }\n\n        @Override\n        public void visitFrame(int type, int numLocal, Object[] local, int numStack, Object[] stack) {\n            super.visitFrame(type, numLocal, local, numStack, stack);\n\n            String frame = getStackFrame();\n            String line = String.format(FORMAT, \"\", frame);\n            System.out.println(line);\n        }\n\n        @Override\n        public void visitInsn(int opcode) {\n            super.visitInsn(opcode);\n            printStackMapFrame();\n        }\n\n        @Override\n        public void visitIntInsn(int opcode, int operand) {\n            super.visitIntInsn(opcode, operand);\n            printStackMapFrame();\n        }\n\n        @Override\n        public void visitVarInsn(int opcode, int var) {\n            super.visitVarInsn(opcode, var);\n            printStackMapFrame();\n        }\n\n        @Override\n        public void visitTypeInsn(int opcode, String type) {\n            super.visitTypeInsn(opcode, type);\n            printStackMapFrame();\n        }\n\n        @Override\n        public void visitFieldInsn(int opcode, String owner, String name, String descriptor) {\n            super.visitFieldInsn(opcode, owner, name, descriptor);\n            printStackMapFrame();\n        }\n\n        @Override\n        public void visitMethodInsn(int opcode, String owner, String name, String descriptor, boolean isInterface) {\n            super.visitMethodInsn(opcode, owner, name, descriptor, isInterface);\n            printStackMapFrame();\n        }\n\n        @Override\n        public void visitInvokeDynamicInsn(String name, String descriptor, Handle bootstrapMethodHandle, Object... bootstrapMethodArguments) {\n            super.visitInvokeDynamicInsn(name, descriptor, bootstrapMethodHandle, bootstrapMethodArguments);\n            printStackMapFrame();\n        }\n\n        @Override\n        public void visitJumpInsn(int opcode, Label label) {\n            super.visitJumpInsn(opcode, label);\n            printStackMapFrame();\n        }\n\n        @Override\n        public void visitLdcInsn(Object value) {\n            super.visitLdcInsn(value);\n            printStackMapFrame();\n        }\n\n        @Override\n        public void visitIincInsn(int var, int increment) {\n            super.visitIincInsn(var, increment);\n            printStackMapFrame();\n        }\n\n        @Override\n        public void visitTableSwitchInsn(int min, int max, Label dflt, Label... labels) {\n            super.visitTableSwitchInsn(min, max, dflt, labels);\n\n            int insnSize = insnList.size();\n            String insn;\n            if (insnIndex < insnSize) {\n                insn = insnList.get(insnIndex);\n                insnIndex++;\n            }\n            else {\n                insn = \"\";\n            }\n\n            String[] array = insn.split(System.lineSeparator(), 2);\n            String frame = getStackFrame();\n            String line = String.format(FORMAT, array[0], frame);\n            System.out.println(line);\n            System.out.println(array[1]);\n        }\n\n        @Override\n        public void visitLookupSwitchInsn(Label dflt, int[] keys, Label[] labels) {\n            super.visitLookupSwitchInsn(dflt, keys, labels);\n\n            int insnSize = insnList.size();\n            String insn;\n            if (insnIndex < insnSize) {\n                insn = insnList.get(insnIndex);\n                insnIndex++;\n            }\n            else {\n                insn = \"\";\n            }\n\n            String[] array = insn.split(System.lineSeparator(), 2);\n            String frame = getStackFrame();\n            String line = String.format(FORMAT, array[0], frame);\n            System.out.println(line);\n            System.out.println(array[1]);\n        }\n\n        @Override\n        public void visitMultiANewArrayInsn(String descriptor, int numDimensions) {\n            super.visitMultiANewArrayInsn(descriptor, numDimensions);\n            printStackMapFrame();\n        }\n\n        @Override\n        public void visitEnd() {\n            super.visitEnd();\n\n            System.out.println();\n            System.out.println();\n        }\n\n        private void printStackMapFrame() {\n            int insnSize = insnList.size();\n            String insn;\n            if (insnIndex < insnSize) {\n                insn = insnList.get(insnIndex);\n                insnIndex++;\n            }\n            else {\n                insn = \"\";\n            }\n\n            if (insn.contains(\" wide \")) {\n                System.out.println(insn);\n                insn = insnList.get(insnIndex);\n                insnIndex++;\n            }\n\n            String frame = getStackFrame();\n            String line = String.format(FORMAT, insn, frame);\n            System.out.println(line);\n        }\n\n        private String getStackFrame() {\n            if (locals != null && locals.size() > 0) {\n                boolean isStaticMethod = (methodAccess & Opcodes.ACC_STATIC) != 0;\n                if (!isStaticMethod) {\n                    Object obj = locals.get(0);\n                    if (obj != Opcodes.UNINITIALIZED_THIS) {\n                        locals.set(0, \"this\");\n                    }\n                }\n            }\n            String locals_str = locals == null ? EMPTY : list2Str(locals);\n            String stack_str = stack == null ? EMPTY : list2Str(stack);\n            return String.format(\"%s %s %s\", locals_str, SEPARATOR, stack_str);\n        }\n\n        private String list2Str(List<Object> list) {\n            if (list == null || list.size() == 0) return EMPTY;\n            int size = list.size();\n            String[] array = new String[size];\n            for (int i = 0; i < size; i++) {\n                Object item = list.get(i);\n                array[i] = item2Str(item);\n            }\n\n            return array2Str(array);\n        }\n\n        private String array2Str(String[] array) {\n            if (array == null || array.length == 0) return EMPTY;\n            int length = array.length;\n\n            StringBuilder sb = new StringBuilder();\n            sb.append(START);\n            for (int i = 0; i < length - 1; i++) {\n                sb.append(array[i]).append(\", \");\n            }\n            sb.append(array[length - 1]);\n            sb.append(STOP);\n            return sb.toString();\n        }\n\n        private String item2Str(Object obj) {\n            if (obj == Opcodes.TOP) {\n                return \"top\";\n            }\n            else if (obj == Opcodes.INTEGER) {\n                return \"int\";\n            }\n            else if (obj == Opcodes.FLOAT) {\n                return \"float\";\n            }\n            else if (obj == Opcodes.DOUBLE) {\n                return \"double\";\n            }\n            else if (obj == Opcodes.LONG) {\n                return \"long\";\n            }\n            else if (obj == Opcodes.NULL) {\n                return \"null\";\n            }\n            else if (obj == Opcodes.UNINITIALIZED_THIS) {\n                return \"uninitialized_this\";\n            }\n            else if (obj instanceof Label) {\n                Object value = uninitializedTypes.get(obj);\n                return \"uninitialized_\" + getSimpleClassName(value);\n            }\n            else {\n                return getSimpleClassName(obj);\n            }\n        }\n\n        private String getSimpleClassName(Object obj) {\n            if (obj == null) return \"null\";\n            String descriptor = obj.toString();\n\n            int squareIndex = descriptor.lastIndexOf(\"[\");\n            String prefix = descriptor.substring(0, squareIndex + 1);\n\n            String simpleName = descriptor.substring(squareIndex + 1);\n            if (simpleName.startsWith(\"L\") && simpleName.endsWith(\";\")) {\n                simpleName = simpleName.substring(1, simpleName.length() - 1);\n            }\n\n            int slashIndex = simpleName.lastIndexOf(\"/\");\n            simpleName = simpleName.substring(slashIndex + 1);\n\n            return prefix + simpleName;\n        }\n    }\n}\n"
  },
  {
    "path": "src/main/java/lsieun/asm/commons/MethodStackMapFrameVisitor.java",
    "content": "package lsieun.asm.commons;\n\nimport org.objectweb.asm.*;\nimport org.objectweb.asm.commons.AnalyzerAdapter;\n\nimport java.util.Arrays;\nimport java.util.List;\n\npublic class MethodStackMapFrameVisitor extends ClassVisitor {\n    private String owner;\n\n    public MethodStackMapFrameVisitor(int api, ClassVisitor classVisitor) {\n        super(api, classVisitor);\n    }\n\n    @Override\n    public void visit(int version, int access, String name, String signature, String superName, String[] interfaces) {\n        super.visit(version, access, name, signature, superName, interfaces);\n        this.owner = name;\n    }\n\n    @Override\n    public MethodVisitor visitMethod(int access, String name, String descriptor, String signature, String[] exceptions) {\n        MethodVisitor mv = super.visitMethod(access, name, descriptor, signature, exceptions);\n        return new MethodStackMapFrameAdapter(api, owner, access, name, descriptor, mv);\n    }\n\n    private static class MethodStackMapFrameAdapter extends AnalyzerAdapter {\n        private final String methodName;\n        private final String methodDesc;\n\n        public MethodStackMapFrameAdapter(int api, String owner, int access, String name, String descriptor, MethodVisitor methodVisitor) {\n            super(api, owner, access, name, descriptor, methodVisitor);\n            this.methodName = name;\n            this.methodDesc = descriptor;\n        }\n\n        @Override\n        public void visitCode() {\n            super.visitCode();\n            System.out.println();\n            System.out.println(methodName + methodDesc);\n            printStackMapFrame();\n        }\n\n        @Override\n        public void visitInsn(int opcode) {\n            super.visitInsn(opcode);\n            printStackMapFrame();\n        }\n\n        @Override\n        public void visitIntInsn(int opcode, int operand) {\n            super.visitIntInsn(opcode, operand);\n            printStackMapFrame();\n        }\n\n        @Override\n        public void visitVarInsn(int opcode, int var) {\n            super.visitVarInsn(opcode, var);\n            printStackMapFrame();\n        }\n\n        @Override\n        public void visitTypeInsn(int opcode, String type) {\n            super.visitTypeInsn(opcode, type);\n            printStackMapFrame();\n        }\n\n        @Override\n        public void visitFieldInsn(int opcode, String owner, String name, String descriptor) {\n            super.visitFieldInsn(opcode, owner, name, descriptor);\n            printStackMapFrame();\n        }\n\n        @Override\n        public void visitMethodInsn(int opcode, String owner, String name, String descriptor, boolean isInterface) {\n            super.visitMethodInsn(opcode, owner, name, descriptor, isInterface);\n            printStackMapFrame();\n        }\n\n        @Override\n        public void visitInvokeDynamicInsn(String name, String descriptor, Handle bootstrapMethodHandle, Object... bootstrapMethodArguments) {\n            super.visitInvokeDynamicInsn(name, descriptor, bootstrapMethodHandle, bootstrapMethodArguments);\n            printStackMapFrame();\n        }\n\n        @Override\n        public void visitJumpInsn(int opcode, Label label) {\n            super.visitJumpInsn(opcode, label);\n            printStackMapFrame();\n        }\n\n        @Override\n        public void visitLdcInsn(Object value) {\n            super.visitLdcInsn(value);\n            printStackMapFrame();\n        }\n\n        @Override\n        public void visitIincInsn(int var, int increment) {\n            super.visitIincInsn(var, increment);\n            printStackMapFrame();\n        }\n\n        @Override\n        public void visitTableSwitchInsn(int min, int max, Label dflt, Label... labels) {\n            super.visitTableSwitchInsn(min, max, dflt, labels);\n            printStackMapFrame();\n        }\n\n        @Override\n        public void visitLookupSwitchInsn(Label dflt, int[] keys, Label[] labels) {\n            super.visitLookupSwitchInsn(dflt, keys, labels);\n            printStackMapFrame();\n        }\n\n        @Override\n        public void visitMultiANewArrayInsn(String descriptor, int numDimensions) {\n            super.visitMultiANewArrayInsn(descriptor, numDimensions);\n            printStackMapFrame();\n        }\n\n        private void printStackMapFrame() {\n            String locals_str = locals == null ? \"[]\" : list2Str(locals);\n            String stack_str = stack == null ? \"[]\" : list2Str(stack);\n            String line = String.format(\"%s %s\", locals_str, stack_str);\n            System.out.println(line);\n        }\n\n        private String list2Str(List<Object> list) {\n            if (list == null || list.size() == 0) return \"[]\";\n            int size = list.size();\n            String[] array = new String[size];\n            for (int i = 0; i < size; i++) {\n                Object item = list.get(i);\n                array[i] = item2Str(item);\n            }\n            return Arrays.toString(array);\n        }\n\n        private String item2Str(Object obj) {\n            if (obj == Opcodes.TOP) {\n                return \"top\";\n            }\n            else if (obj == Opcodes.INTEGER) {\n                return \"int\";\n            }\n            else if (obj == Opcodes.FLOAT) {\n                return \"float\";\n            }\n            else if (obj == Opcodes.DOUBLE) {\n                return \"double\";\n            }\n            else if (obj == Opcodes.LONG) {\n                return \"long\";\n            }\n            else if (obj == Opcodes.NULL) {\n                return \"null\";\n            }\n            else if (obj == Opcodes.UNINITIALIZED_THIS) {\n                return \"uninitialized_this\";\n            }\n            else if (obj instanceof Label) {\n                Object value = uninitializedTypes.get(obj);\n                if (value == null) {\n                    return obj.toString();\n                }\n                else {\n                    return \"uninitialized_\" + value;\n                }\n            }\n            else {\n                return obj.toString();\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "src/main/java/lsieun/asm/commons/SerialVersionUIDAdderExample01.java",
    "content": "package lsieun.asm.commons;\n\nimport lsieun.utils.FileUtils;\nimport org.objectweb.asm.ClassReader;\nimport org.objectweb.asm.ClassVisitor;\nimport org.objectweb.asm.ClassWriter;\nimport org.objectweb.asm.Opcodes;\nimport org.objectweb.asm.commons.SerialVersionUIDAdder;\n\npublic class SerialVersionUIDAdderExample01 {\n    public static void main(String[] args) {\n        String relative_path = \"sample/HelloWorld.class\";\n        String filepath = FileUtils.getFilePath(relative_path);\n        byte[] bytes1 = FileUtils.readBytes(filepath);\n\n        //（1）构建ClassReader\n        ClassReader cr = new ClassReader(bytes1);\n\n        //（2）构建ClassWriter\n        ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_FRAMES);\n\n        //（3）串连ClassVisitor\n        int api = Opcodes.ASM9;\n        ClassVisitor cv = new SerialVersionUIDAdder(cw);\n\n        //（4）结合ClassReader和ClassVisitor\n        int parsingOptions = ClassReader.SKIP_DEBUG | ClassReader.SKIP_FRAMES;\n        cr.accept(cv, parsingOptions);\n\n        //（5）生成byte[]\n        byte[] bytes2 = cw.toByteArray();\n\n        FileUtils.writeBytes(filepath, bytes2);\n    }\n}\n"
  },
  {
    "path": "src/main/java/lsieun/asm/commons/StaticInitMergerExample01.java",
    "content": "package lsieun.asm.commons;\n\nimport lsieun.asm.template.ClassAddInterfaceVisitor;\nimport lsieun.asm.template.ClassMergeVisitor;\nimport lsieun.utils.FileUtils;\nimport org.objectweb.asm.ClassReader;\nimport org.objectweb.asm.ClassVisitor;\nimport org.objectweb.asm.ClassWriter;\nimport org.objectweb.asm.Opcodes;\nimport org.objectweb.asm.commons.*;\nimport org.objectweb.asm.tree.ClassNode;\n\nimport java.util.List;\n\npublic class StaticInitMergerExample01 {\n    private static final int API_VERSION = Opcodes.ASM9;\n\n    public static void main(String[] args) {\n        // 第一步，读取两个类文件\n        String first_class = \"sample/HelloWorld\";\n        String second_class = \"sample/GoodChild\";\n\n        String first_class_filepath = getFilePath(first_class);\n        byte[] bytes1 = FileUtils.readBytes(first_class_filepath);\n\n        String second_class_filepath = getFilePath(second_class);\n        byte[] bytes2 = FileUtils.readBytes(second_class_filepath);\n\n        // 第二步，将sample/GoodChild类重命名为sample/HelloWorld\n        byte[] bytes3 = renameClass(second_class, first_class, bytes2);\n\n        // 第三步，合并两个类\n        byte[] bytes4 = mergeClass(bytes1, bytes3);\n\n        // 第四步，处理重复的class initialization method\n        byte[] bytes5 = removeDuplicateStaticInitMethod(bytes4);\n        FileUtils.writeBytes(first_class_filepath, bytes5);\n    }\n\n    public static String getFilePath(String internalName) {\n        String relative_path = String.format(\"%s.class\", internalName);\n        return FileUtils.getFilePath(relative_path);\n    }\n\n    public static byte[] renameClass(String origin_name, String target_name, byte[] bytes) {\n        //（1）构建ClassReader\n        ClassReader cr = new ClassReader(bytes);\n\n        //（2）构建ClassWriter\n        ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_FRAMES);\n\n        //（3）串连ClassVisitor\n        Remapper remapper = new SimpleRemapper(origin_name, target_name);\n        ClassVisitor cv = new ClassRemapper(cw, remapper);\n\n        //（4）两者进行结合\n        int parsingOptions = ClassReader.SKIP_DEBUG | ClassReader.SKIP_FRAMES;\n        cr.accept(cv, parsingOptions);\n\n        //（5）重新生成Class\n        return cw.toByteArray();\n    }\n\n    public static byte[] mergeClass(byte[] bytes1, byte[] bytes2) {\n        //（1）构建ClassReader\n        ClassReader cr = new ClassReader(bytes1);\n\n        //（2）构建ClassWriter\n        ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_FRAMES);\n\n        //（3）串连ClassVisitor\n        ClassNode cn = getClassNode(bytes2);\n        List<String> interface_list = cn.interfaces;\n        int size = interface_list.size();\n        String[] interfaces = new String[size];\n        for (int i = 0; i < size; i++) {\n            String item = interface_list.get(i);\n            interfaces[i] = item;\n        }\n        ClassMergeVisitor cmv = new ClassMergeVisitor(API_VERSION, cw, cn);\n        ClassAddInterfaceVisitor cv = new ClassAddInterfaceVisitor(API_VERSION, cmv, interfaces);\n\n        //（4）两者进行结合\n        int parsingOptions = ClassReader.SKIP_DEBUG | ClassReader.SKIP_FRAMES;\n        cr.accept(cv, parsingOptions);\n\n        //（5）重新生成Class\n        return cw.toByteArray();\n    }\n\n    public static ClassNode getClassNode(byte[] bytes) {\n        ClassReader cr = new ClassReader(bytes);\n        ClassNode cn = new ClassNode();\n        int parsingOptions = ClassReader.SKIP_DEBUG | ClassReader.SKIP_FRAMES;\n        cr.accept(cn, parsingOptions);\n        return cn;\n    }\n\n    public static byte[] removeDuplicateStaticInitMethod(byte[] bytes) {\n        //（1）构建ClassReader\n        ClassReader cr = new ClassReader(bytes);\n\n        //（2）构建ClassWriter\n        ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_FRAMES);\n\n        //（3）串连ClassVisitor\n        ClassVisitor cv = new StaticInitMerger(\"class_init$\", cw);\n\n        //（4）结合ClassReader和ClassVisitor\n        int parsingOptions = ClassReader.SKIP_DEBUG | ClassReader.SKIP_FRAMES;\n        cr.accept(cv, parsingOptions);\n\n        //（5）生成byte[]\n        return cw.toByteArray();\n    }\n}\n"
  },
  {
    "path": "src/main/java/lsieun/asm/core/ChangeURLVisitor.java",
    "content": "package lsieun.asm.core;\n\nimport lsieun.annotation.todo.ToDo;\nimport org.objectweb.asm.ClassVisitor;\nimport org.objectweb.asm.Label;\nimport org.objectweb.asm.MethodVisitor;\nimport org.objectweb.asm.Opcodes;\nimport org.objectweb.asm.commons.AdviceAdapter;\n\n@ToDo(\"Remove\")\npublic class ChangeURLVisitor extends ClassVisitor {\n    public ChangeURLVisitor(ClassVisitor classVisitor) {\n        super(Opcodes.ASM9, classVisitor);\n    }\n\n    @Override\n    public MethodVisitor visitMethod(int access, String name, String descriptor, String signature, String[] exceptions) {\n        MethodVisitor mv = super.visitMethod(access, name, descriptor, signature, exceptions);\n        if (mv != null && \"<init>\".equals(name) && \"(Ljava/net/URL;Ljava/lang/String;Ljava/net/URLStreamHandler;)V\".equals(descriptor)) {\n            mv = new ChangeURLAdapter(mv, access, name, descriptor);\n        }\n        return mv;\n    }\n\n    public class ChangeURLAdapter extends AdviceAdapter {\n        protected ChangeURLAdapter(MethodVisitor methodVisitor, int access, String name, String descriptor) {\n            super(Opcodes.ASM9, methodVisitor, access, name, descriptor);\n        }\n\n        @Override\n        protected void onMethodEnter() {\n            super.visitVarInsn(ALOAD, 2);\n            Label elseLabel = new Label();\n            super.visitJumpInsn(IFNULL, elseLabel);\n            super.visitVarInsn(ALOAD, 2);\n            super.visitLdcInsn(\"/lservice/rpc/validateKey.action\");\n            super.visitMethodInsn(INVOKEVIRTUAL, \"java/lang/String\", \"contains\", \"(Ljava/lang/CharSequence;)Z\", false);\n            super.visitJumpInsn(IFEQ, elseLabel);\n            super.visitTypeInsn(NEW, \"java/net/MalformedURLException\");\n            super.visitInsn(DUP);\n            super.visitMethodInsn(INVOKESPECIAL, \"java/net/MalformedURLException\", \"<init>\", \"()V\", false);\n            super.visitInsn(ATHROW);\n            super.visitLabel(elseLabel);\n        }\n    }\n}\n"
  },
  {
    "path": "src/main/java/lsieun/asm/core/ClassChangeVersionVisitor.java",
    "content": "package lsieun.asm.core;\n\nimport org.objectweb.asm.ClassVisitor;\nimport org.objectweb.asm.Opcodes;\n\npublic class ClassChangeVersionVisitor extends ClassVisitor {\n    public ClassChangeVersionVisitor(int api, ClassVisitor classVisitor) {\n        super(api, classVisitor);\n    }\n\n    @Override\n    public void visit(int version, int access, String name, String signature, String superName, String[] interfaces) {\n        super.visit(Opcodes.V1_7, access, name, signature, superName, interfaces);\n    }\n}\n"
  },
  {
    "path": "src/main/java/lsieun/asm/core/ClassCloneVisitor.java",
    "content": "package lsieun.asm.core;\n\nimport org.objectweb.asm.ClassVisitor;\n\npublic class ClassCloneVisitor extends ClassVisitor {\n    public ClassCloneVisitor(int api, ClassVisitor cw) {\n        super(api, cw);\n    }\n\n    @Override\n    public void visit(int version, int access, String name, String signature, String superName, String[] interfaces) {\n        super.visit(version, access, name, signature, superName, new String[]{\"java/lang/Cloneable\"});\n    }\n}\n"
  },
  {
    "path": "src/main/java/lsieun/asm/core/ClassDecompileVisitor.java",
    "content": "package lsieun.asm.core;\n\nimport org.objectweb.asm.*;\n\npublic class ClassDecompileVisitor extends ClassVisitor {\n\n    public ClassDecompileVisitor() {\n        super(Opcodes.ASM9);\n    }\n\n    @Override\n    public void visit(int version, int access, String name, String signature, String superName, String[] interfaces) {\n        System.out.println(name + \" extends \" + superName + \" {\");\n    }\n\n    @Override\n    public FieldVisitor visitField(int access, String name, String descriptor, String signature, Object value) {\n        System.out.println(\"    \" + descriptor + \" \" + name);\n        return null;\n    }\n\n    @Override\n    public MethodVisitor visitMethod(int access, String name, String descriptor, String signature, String[] exceptions) {\n        System.out.println(\"    \" + name + \" \" + descriptor);\n        return null;\n    }\n\n    @Override\n    public void visitEnd() {\n        System.out.println(\"}\");\n    }\n}\n"
  },
  {
    "path": "src/main/java/lsieun/asm/core/ClassListMemberVisitor.java",
    "content": "package lsieun.asm.core;\n\nimport org.objectweb.asm.ClassVisitor;\nimport org.objectweb.asm.FieldVisitor;\nimport org.objectweb.asm.MethodVisitor;\n\nimport java.util.Arrays;\nimport java.util.Formatter;\n\npublic class ClassListMemberVisitor extends ClassVisitor {\n    private final Formatter fm = new Formatter();\n\n    public ClassListMemberVisitor(int api) {\n        super(api);\n    }\n\n    public ClassListMemberVisitor(int api, ClassVisitor classVisitor) {\n        super(api, classVisitor);\n    }\n\n    @Override\n    public void visit(int version, int access, String name, String signature, String superName, String[] interfaces) {\n        fm.format(\"%s extends %s implements %s {%n\", name, superName, Arrays.toString(interfaces));\n    }\n\n    @Override\n    public FieldVisitor visitField(int access, String name, String descriptor, String signature, Object value) {\n        fm.format(\"    %s: %s%n\", name, descriptor);\n        return null;\n    }\n\n    @Override\n    public MethodVisitor visitMethod(int access, String name, String descriptor, String signature, String[] exceptions) {\n        fm.format(\"    %s: %s%n\", name, descriptor);\n        return null;\n    }\n\n    @Override\n    public void visitEnd() {\n        fm.format(\"}\");\n    }\n\n    public String getText() {\n        return fm.toString();\n    }\n}\n"
  },
  {
    "path": "src/main/java/lsieun/asm/core/ClassRemoveAttributeVisitor.java",
    "content": "package lsieun.asm.core;\n\nimport org.objectweb.asm.ClassVisitor;\n\npublic class ClassRemoveAttributeVisitor extends ClassVisitor {\n    public ClassRemoveAttributeVisitor(int api, ClassVisitor cv) {\n        super(api, cv);\n    }\n\n    @Override\n    public void visitSource(String source, String debug) {\n        // do nothing\n    }\n\n    @Override\n    public void visitOuterClass(String owner, String name, String descriptor) {\n        // do nothing\n    }\n\n    @Override\n    public void visitInnerClass(String name, String outerName, String innerName, int access) {\n        // do nothing\n    }\n}\n"
  },
  {
    "path": "src/main/java/lsieun/asm/core/MethodAroundVisitor.java",
    "content": "package lsieun.asm.core;\n\nimport org.objectweb.asm.ClassVisitor;\nimport org.objectweb.asm.MethodVisitor;\nimport org.objectweb.asm.Opcodes;\n\nimport static org.objectweb.asm.Opcodes.*;\n\npublic class MethodAroundVisitor extends ClassVisitor {\n    public MethodAroundVisitor(int api, ClassVisitor classVisitor) {\n        super(api, classVisitor);\n    }\n\n    @Override\n    public MethodVisitor visitMethod(int access, String name, String descriptor, String signature, String[] exceptions) {\n        MethodVisitor mv = super.visitMethod(access, name, descriptor, signature, exceptions);\n        if (mv != null && !\"<init>\".equals(name)) {\n            boolean isAbstractMethod = (access & Opcodes.ACC_ABSTRACT) == Opcodes.ACC_ABSTRACT;\n            boolean isNativeMethod = (access & Opcodes.ACC_NATIVE) == Opcodes.ACC_NATIVE;\n            if (!isAbstractMethod && !isNativeMethod) {\n                mv = new MethodAroundAdapter(api, mv);\n            }\n        }\n        return mv;\n    }\n\n    private static class MethodAroundAdapter extends MethodVisitor {\n        public MethodAroundAdapter(int api, MethodVisitor methodVisitor) {\n            super(api, methodVisitor);\n        }\n\n        @Override\n        public void visitCode() {\n            // 首先，处理自己的代码逻辑\n            super.visitFieldInsn(Opcodes.GETSTATIC, \"java/lang/System\", \"out\", \"Ljava/io/PrintStream;\");\n            super.visitLdcInsn(\"Method Enter...\");\n            super.visitMethodInsn(Opcodes.INVOKEVIRTUAL, \"java/io/PrintStream\", \"println\", \"(Ljava/lang/String;)V\", false);\n\n            // 其次，调用父类的方法实现\n            super.visitCode();\n        }\n\n        @Override\n        public void visitInsn(int opcode) {\n            // 首先，处理自己的代码逻辑\n            if (opcode == ATHROW || (opcode >= IRETURN && opcode <= RETURN)) {\n                super.visitFieldInsn(Opcodes.GETSTATIC, \"java/lang/System\", \"out\", \"Ljava/io/PrintStream;\");\n                super.visitLdcInsn(\"Method Exit...\");\n                super.visitMethodInsn(Opcodes.INVOKEVIRTUAL, \"java/io/PrintStream\", \"println\", \"(Ljava/lang/String;)V\", false);\n            }\n\n            // 其次，调用父类的方法实现\n            super.visitInsn(opcode);\n        }\n    }\n}\n"
  },
  {
    "path": "src/main/java/lsieun/asm/core/MethodAroundVisitor2.java",
    "content": "package lsieun.asm.core;\n\nimport org.objectweb.asm.ClassVisitor;\nimport org.objectweb.asm.MethodVisitor;\nimport org.objectweb.asm.Opcodes;\n\nimport static org.objectweb.asm.Opcodes.*;\n\npublic class MethodAroundVisitor2 extends ClassVisitor {\n    public MethodAroundVisitor2(int api, ClassVisitor classVisitor) {\n        super(api, classVisitor);\n    }\n\n    @Override\n    public MethodVisitor visitMethod(int access, String name, String descriptor, String signature, String[] exceptions) {\n        MethodVisitor mv = super.visitMethod(access, name, descriptor, signature, exceptions);\n        if (mv != null && !\"<init>\".equals(name)) {\n            boolean isAbstractMethod = (access & Opcodes.ACC_ABSTRACT) == Opcodes.ACC_ABSTRACT;\n            boolean isNativeMethod = (access & Opcodes.ACC_NATIVE) == Opcodes.ACC_NATIVE;\n            if (!isAbstractMethod && !isNativeMethod) {\n                mv = new MethodAroundAdapter(api, mv);\n            }\n        }\n        return mv;\n    }\n\n    private static class MethodAroundAdapter extends MethodVisitor {\n        public MethodAroundAdapter(int api, MethodVisitor methodVisitor) {\n            super(api, methodVisitor);\n        }\n\n        @Override\n        public void visitCode() {\n            // 首先，处理自己的代码逻辑\n            super.visitFieldInsn(Opcodes.GETSTATIC, \"java/lang/System\", \"out\", \"Ljava/io/PrintStream;\");\n            super.visitLdcInsn(\"Method Enter222\");\n            super.visitMethodInsn(Opcodes.INVOKEVIRTUAL, \"java/io/PrintStream\", \"println\", \"(Ljava/lang/String;)V\", false);\n\n            // 其次，调用父类的方法实现\n            super.visitCode();\n        }\n\n        @Override\n        public void visitInsn(int opcode) {\n            // 首先，处理自己的代码逻辑\n            if (opcode == ATHROW || (opcode >= IRETURN && opcode <= RETURN)) {\n                super.visitFieldInsn(Opcodes.GETSTATIC, \"java/lang/System\", \"out\", \"Ljava/io/PrintStream;\");\n                super.visitLdcInsn(\"Method Exit222\");\n                super.visitMethodInsn(Opcodes.INVOKEVIRTUAL, \"java/io/PrintStream\", \"println\", \"(Ljava/lang/String;)V\", false);\n            }\n\n            // 其次，调用父类的方法实现\n            super.visitInsn(opcode);\n        }\n    }\n}\n"
  },
  {
    "path": "src/main/java/lsieun/asm/core/MethodEmptyBodyVisitor.java",
    "content": "package lsieun.asm.core;\n\nimport org.objectweb.asm.ClassVisitor;\nimport org.objectweb.asm.MethodVisitor;\nimport org.objectweb.asm.Opcodes;\nimport org.objectweb.asm.Type;\n\nimport static org.objectweb.asm.Opcodes.*;\n\npublic class MethodEmptyBodyVisitor extends ClassVisitor {\n    private String owner;\n    private final String methodName;\n    private final String methodDesc;\n\n    public MethodEmptyBodyVisitor(int api, ClassVisitor classVisitor, String methodName, String methodDesc) {\n        super(api, classVisitor);\n        this.methodName = methodName;\n        this.methodDesc = methodDesc;\n    }\n\n    @Override\n    public void visit(int version, int access, String name, String signature, String superName, String[] interfaces) {\n        super.visit(version, access, name, signature, superName, interfaces);\n        this.owner = name;\n    }\n\n    @Override\n    public MethodVisitor visitMethod(int access, String name, String descriptor, String signature, String[] exceptions) {\n        MethodVisitor mv = super.visitMethod(access, name, descriptor, signature, exceptions);\n        if (mv != null && methodName.equals(name) && methodDesc.equals(descriptor)) {\n            boolean isAbstractMethod = (access & ACC_ABSTRACT) != 0;\n            boolean isNativeMethod = (access & ACC_NATIVE) != 0;\n            if (!isAbstractMethod && !isNativeMethod) {\n                generateNewBody(mv, owner, access, name, descriptor);\n                return null;\n            }\n        }\n        return mv;\n    }\n\n    protected void generateNewBody(MethodVisitor mv, String owner, int methodAccess, String methodName, String methodDesc) {\n        // (1) method argument types and return type\n        Type t = Type.getType(methodDesc);\n        Type[] argumentTypes = t.getArgumentTypes();\n        Type returnType = t.getReturnType();\n\n\n        // (2) compute the size of local variable and operand stack\n        boolean isStaticMethod = ((methodAccess & Opcodes.ACC_STATIC) != 0);\n        int localSize = isStaticMethod ? 0 : 1;\n        for (Type argType : argumentTypes) {\n            localSize += argType.getSize();\n        }\n        int stackSize = returnType.getSize();\n\n\n        // (3) method body\n        mv.visitCode();\n        if (returnType.getSort() == Type.VOID) {\n            mv.visitInsn(RETURN);\n        }\n        else if (returnType.getSort() >= Type.BOOLEAN && returnType.getSort() <= Type.INT) {\n            mv.visitInsn(ICONST_1);\n            mv.visitInsn(IRETURN);\n        }\n        else if (returnType.getSort() == Type.LONG) {\n            mv.visitInsn(LCONST_0);\n            mv.visitInsn(LRETURN);\n        }\n        else if (returnType.getSort() == Type.FLOAT) {\n            mv.visitInsn(FCONST_0);\n            mv.visitInsn(FRETURN);\n        }\n        else if (returnType.getSort() == Type.DOUBLE) {\n            mv.visitInsn(DCONST_0);\n            mv.visitInsn(DRETURN);\n        }\n        else {\n            mv.visitInsn(ACONST_NULL);\n            mv.visitInsn(ARETURN);\n        }\n        mv.visitMaxs(stackSize, localSize);\n        mv.visitEnd();\n    }\n}\n"
  },
  {
    "path": "src/main/java/lsieun/asm/core/MethodEnterVisitor.java",
    "content": "package lsieun.asm.core;\n\nimport org.objectweb.asm.ClassVisitor;\nimport org.objectweb.asm.Label;\nimport org.objectweb.asm.MethodVisitor;\nimport org.objectweb.asm.Opcodes;\n\nimport static org.objectweb.asm.Opcodes.*;\n\npublic class MethodEnterVisitor extends ClassVisitor {\n    public MethodEnterVisitor(int api, ClassVisitor classVisitor) {\n        super(api, classVisitor);\n    }\n\n    @Override\n    public MethodVisitor visitMethod(int access, String name, String descriptor, String signature, String[] exceptions) {\n        MethodVisitor mv = super.visitMethod(access, name, descriptor, signature, exceptions);\n        if (mv != null && !\"<init>\".equals(name)) {\n            mv = new MethodEnterAdapter(api, mv);\n        }\n        return mv;\n    }\n\n    private static class MethodEnterAdapter extends MethodVisitor {\n        public MethodEnterAdapter(int api, MethodVisitor methodVisitor) {\n            super(api, methodVisitor);\n        }\n\n        @Override\n        public void visitCode() {\n            // 首先，处理自己的代码逻辑\n            super.visitFieldInsn(Opcodes.GETSTATIC, \"java/lang/System\", \"out\", \"Ljava/io/PrintStream;\");\n            super.visitLdcInsn(\"Method Enter...\");\n            super.visitMethodInsn(Opcodes.INVOKEVIRTUAL, \"java/io/PrintStream\", \"println\", \"(Ljava/lang/String;)V\", false);\n\n            // 其次，调用父类的方法实现\n            super.visitCode();\n        }\n    }\n}\n"
  },
  {
    "path": "src/main/java/lsieun/asm/core/MethodExitVisitor.java",
    "content": "package lsieun.asm.core;\n\nimport org.objectweb.asm.ClassVisitor;\nimport org.objectweb.asm.MethodVisitor;\nimport org.objectweb.asm.Opcodes;\n\npublic class MethodExitVisitor extends ClassVisitor {\n    public MethodExitVisitor(int api, ClassVisitor classVisitor) {\n        super(api, classVisitor);\n    }\n\n    @Override\n    public MethodVisitor visitMethod(int access, String name, String descriptor, String signature, String[] exceptions) {\n        MethodVisitor mv = super.visitMethod(access, name, descriptor, signature, exceptions);\n        if (mv != null && !\"<init>\".equals(name)) {\n            mv = new MethodExitAdapter(api, mv);\n        }\n        return mv;\n    }\n\n    private static class MethodExitAdapter extends MethodVisitor {\n        public MethodExitAdapter(int api, MethodVisitor methodVisitor) {\n            super(api, methodVisitor);\n        }\n\n        @Override\n        public void visitInsn(int opcode) {\n            // 首先，处理自己的代码逻辑\n            if (opcode == Opcodes.ATHROW || (opcode >= Opcodes.IRETURN && opcode <= Opcodes.RETURN)) {\n                super.visitFieldInsn(Opcodes.GETSTATIC, \"java/lang/System\", \"out\", \"Ljava/io/PrintStream;\");\n                super.visitLdcInsn(\"Method Exit...\");\n                super.visitMethodInsn(Opcodes.INVOKEVIRTUAL, \"java/io/PrintStream\", \"println\", \"(Ljava/lang/String;)V\", false);\n            }\n\n            // 其次，调用父类的方法实现\n            super.visitInsn(opcode);\n        }\n    }\n}\n"
  },
  {
    "path": "src/main/java/lsieun/asm/core/MethodFindInvokeVisitor.java",
    "content": "package lsieun.asm.core;\n\nimport org.objectweb.asm.ClassVisitor;\nimport org.objectweb.asm.MethodVisitor;\nimport org.objectweb.asm.util.Printer;\n\nimport java.util.ArrayList;\nimport java.util.List;\n\npublic class MethodFindInvokeVisitor extends ClassVisitor {\n    private final String methodName;\n    private final String methodDesc;\n\n    public MethodFindInvokeVisitor(int api, ClassVisitor classVisitor, String methodName, String methodDesc) {\n        super(api, classVisitor);\n        this.methodName = methodName;\n        this.methodDesc = methodDesc;\n    }\n\n    @Override\n    public MethodVisitor visitMethod(int access, String name, String descriptor, String signature, String[] exceptions) {\n        if (methodName.equals(name) && methodDesc.equals(descriptor)) {\n            return new MethodFindInvokeAdapter(api, null);\n        }\n        return null;\n    }\n\n    private static class MethodFindInvokeAdapter extends MethodVisitor {\n        private final List<String> list = new ArrayList<>();\n\n        public MethodFindInvokeAdapter(int api, MethodVisitor methodVisitor) {\n            super(api, methodVisitor);\n        }\n\n        @Override\n        public void visitMethodInsn(int opcode, String owner, String name, String descriptor, boolean isInterface) {\n            // 首先，处理自己的代码逻辑\n            String info = String.format(\"%s %s.%s%s\", Printer.OPCODES[opcode], owner, name, descriptor);\n            if (!list.contains(info)) {\n                list.add(info);\n            }\n\n            // 其次，调用父类的方法实现\n            super.visitMethodInsn(opcode, owner, name, descriptor, isInterface);\n        }\n\n        @Override\n        public void visitEnd() {\n            // 首先，处理自己的代码逻辑\n            for (String item : list) {\n                System.out.println(item);\n            }\n\n            // 其次，调用父类的方法实现\n            super.visitEnd();\n        }\n    }\n}\n"
  },
  {
    "path": "src/main/java/lsieun/asm/core/MethodFindRefVisitor.java",
    "content": "package lsieun.asm.core;\n\nimport org.objectweb.asm.ClassVisitor;\nimport org.objectweb.asm.MethodVisitor;\n\nimport java.util.ArrayList;\nimport java.util.List;\n\nimport static org.objectweb.asm.Opcodes.ACC_ABSTRACT;\nimport static org.objectweb.asm.Opcodes.ACC_NATIVE;\n\npublic class MethodFindRefVisitor extends ClassVisitor {\n    private final String methodOwner;\n    private final String methodName;\n    private final String methodDesc;\n\n    private String owner;\n    private final List<String> resultList = new ArrayList<>();\n\n    public MethodFindRefVisitor(int api, ClassVisitor classVisitor, String methodOwner, String methodName, String methodDesc) {\n        super(api, classVisitor);\n        this.methodOwner = methodOwner;\n        this.methodName = methodName;\n        this.methodDesc = methodDesc;\n    }\n\n    @Override\n    public void visit(int version, int access, String name, String signature, String superName, String[] interfaces) {\n        super.visit(version, access, name, signature, superName, interfaces);\n        this.owner = name;\n    }\n\n    @Override\n    public MethodVisitor visitMethod(int access, String name, String descriptor, String signature, String[] exceptions) {\n        boolean isAbstractMethod = (access & ACC_ABSTRACT) != 0;\n        boolean isNativeMethod = (access & ACC_NATIVE) != 0;\n        if (!isAbstractMethod && !isNativeMethod) {\n            return new MethodFindRefAdaptor(api, null, owner, name, descriptor);\n        }\n        return null;\n    }\n\n    @Override\n    public void visitEnd() {\n        // 首先，处理自己的代码逻辑\n        for (String item : resultList) {\n            System.out.println(item);\n        }\n\n        // 其次，调用父类的方法实现\n        super.visitEnd();\n    }\n\n    private class MethodFindRefAdaptor extends MethodVisitor {\n        private final String currentMethodOwner;\n        private final String currentMethodName;\n        private final String currentMethodDesc;\n\n        public MethodFindRefAdaptor(int api, MethodVisitor methodVisitor, String currentMethodOwner, String currentMethodName, String currentMethodDesc) {\n            super(api, methodVisitor);\n            this.currentMethodOwner = currentMethodOwner;\n            this.currentMethodName = currentMethodName;\n            this.currentMethodDesc = currentMethodDesc;\n        }\n\n        @Override\n        public void visitMethodInsn(int opcode, String owner, String name, String descriptor, boolean isInterface) {\n            // 首先，处理自己的代码逻辑\n            if (methodOwner.equals(owner) && methodName.equals(name) && methodDesc.equals(descriptor)) {\n                String info = String.format(\"%s.%s%s\", currentMethodOwner, currentMethodName, currentMethodDesc);\n                if (!resultList.contains(info)) {\n                    resultList.add(info);\n                }\n            }\n\n            // 其次，调用父类的方法实现\n            super.visitMethodInsn(opcode, owner, name, descriptor, isInterface);\n        }\n    }\n}\n"
  },
  {
    "path": "src/main/java/lsieun/asm/core/MethodParameterVisitor.java",
    "content": "package lsieun.asm.core;\n\nimport org.objectweb.asm.ClassVisitor;\nimport org.objectweb.asm.MethodVisitor;\nimport org.objectweb.asm.Opcodes;\nimport org.objectweb.asm.Type;\n\nimport static org.objectweb.asm.Opcodes.*;\n\npublic class MethodParameterVisitor extends ClassVisitor {\n    public MethodParameterVisitor(int api, ClassVisitor classVisitor) {\n        super(api, classVisitor);\n    }\n\n    @Override\n    public MethodVisitor visitMethod(int access, String name, String descriptor, String signature, String[] exceptions) {\n        MethodVisitor mv = super.visitMethod(access, name, descriptor, signature, exceptions);\n        if (mv != null && !name.equals(\"<init>\")) {\n            boolean isAbstractMethod = (access & ACC_ABSTRACT) != 0;\n            boolean isNativeMethod = (access & ACC_NATIVE) != 0;\n            if (!isAbstractMethod && !isNativeMethod) {\n                mv = new MethodParameterAdapter(api, mv, access, name, descriptor);\n            }\n        }\n        return mv;\n    }\n\n    private static class MethodParameterAdapter extends MethodVisitor {\n        private final int methodAccess;\n        private final String methodName;\n        private final String methodDesc;\n\n        public MethodParameterAdapter(int api, MethodVisitor mv, int methodAccess, String methodName, String methodDesc) {\n            super(api, mv);\n            this.methodAccess = methodAccess;\n            this.methodName = methodName;\n            this.methodDesc = methodDesc;\n        }\n\n        @Override\n        public void visitCode() {\n            // 首先，处理自己的代码逻辑\n            boolean isStatic = ((methodAccess & ACC_STATIC) != 0);\n            int slotIndex = isStatic ? 0 : 1;\n\n            printMessage(\"Method Enter: \" + methodName + methodDesc);\n\n            Type methodType = Type.getMethodType(methodDesc);\n            Type[] argumentTypes = methodType.getArgumentTypes();\n            for (Type t : argumentTypes) {\n                int sort = t.getSort();\n                int size = t.getSize();\n                String descriptor = t.getDescriptor();\n\n                int opcode = t.getOpcode(ILOAD);\n                super.visitVarInsn(opcode, slotIndex);\n\n                if (sort == Type.BOOLEAN) {\n                    printBoolean();\n                }\n                else if (sort == Type.CHAR) {\n                    printChar();\n                }\n                else if (sort == Type.BYTE || sort == Type.SHORT || sort == Type.INT) {\n                    printInt();\n                }\n                else if (sort == Type.FLOAT) {\n                    printFloat();\n                }\n                else if (sort == Type.LONG) {\n                    printLong();\n                }\n                else if (sort == Type.DOUBLE) {\n                    printDouble();\n                }\n                else if (sort == Type.OBJECT && \"Ljava/lang/String;\".equals(descriptor)) {\n                    printString();\n                }\n                else if (sort == Type.OBJECT) {\n                    printObject();\n                }\n                else {\n                    printMessage(\"No Support\");\n                    if (size == 1) {\n                        super.visitInsn(Opcodes.POP);\n                    }\n                    else {\n                        super.visitInsn(Opcodes.POP2);\n                    }\n                }\n                slotIndex += size;\n            }\n\n            // 其次，调用父类的方法实现\n            super.visitCode();\n        }\n\n        @Override\n        public void visitInsn(int opcode) {\n            // 首先，处理自己的代码逻辑\n            if ((opcode >= IRETURN && opcode <= RETURN) || opcode == ATHROW) {\n                printMessage(\"Method Exit:\");\n                if (opcode == IRETURN) {\n                    super.visitInsn(DUP);\n                    Type methodType = Type.getMethodType(methodDesc);\n                    Type returnType = methodType.getReturnType();\n                    if (returnType == Type.BOOLEAN_TYPE) {\n                        printBoolean();\n                    }\n                    else if (returnType == Type.CHAR_TYPE) {\n                        printChar();\n                    }\n                    else {\n                        printInt();\n                    }\n                }\n                else if (opcode == FRETURN) {\n                    super.visitInsn(DUP);\n                    printFloat();\n                }\n                else if (opcode == LRETURN) {\n                    super.visitInsn(DUP2);\n                    printLong();\n                }\n                else if (opcode == DRETURN) {\n                    super.visitInsn(DUP2);\n                    printDouble();\n                }\n                else if (opcode == ARETURN) {\n                    super.visitInsn(DUP);\n                    printObject();\n                }\n                else if (opcode == RETURN) {\n                    printMessage(\"    return void\");\n                }\n                else {\n                    printMessage(\"    abnormal return\");\n                }\n            }\n\n            // 其次，调用父类的方法实现\n            super.visitInsn(opcode);\n        }\n\n        private void printBoolean() {\n            super.visitFieldInsn(GETSTATIC, \"java/lang/System\", \"out\", \"Ljava/io/PrintStream;\");\n            super.visitInsn(SWAP);\n            super.visitMethodInsn(INVOKEVIRTUAL, \"java/io/PrintStream\", \"println\", \"(Z)V\", false);\n        }\n\n        private void printChar() {\n            super.visitFieldInsn(GETSTATIC, \"java/lang/System\", \"out\", \"Ljava/io/PrintStream;\");\n            super.visitInsn(SWAP);\n            super.visitMethodInsn(INVOKEVIRTUAL, \"java/io/PrintStream\", \"println\", \"(C)V\", false);\n        }\n\n        private void printInt() {\n            super.visitFieldInsn(GETSTATIC, \"java/lang/System\", \"out\", \"Ljava/io/PrintStream;\");\n            super.visitInsn(SWAP);\n            super.visitMethodInsn(INVOKEVIRTUAL, \"java/io/PrintStream\", \"println\", \"(I)V\", false);\n        }\n\n        private void printFloat() {\n            super.visitFieldInsn(GETSTATIC, \"java/lang/System\", \"out\", \"Ljava/io/PrintStream;\");\n            super.visitInsn(SWAP);\n            super.visitMethodInsn(INVOKEVIRTUAL, \"java/io/PrintStream\", \"println\", \"(F)V\", false);\n        }\n\n        private void printLong() {\n            super.visitFieldInsn(GETSTATIC, \"java/lang/System\", \"out\", \"Ljava/io/PrintStream;\");\n            super.visitInsn(DUP_X2);\n            super.visitInsn(POP);\n            super.visitMethodInsn(INVOKEVIRTUAL, \"java/io/PrintStream\", \"println\", \"(J)V\", false);\n        }\n\n        private void printDouble() {\n            super.visitFieldInsn(GETSTATIC, \"java/lang/System\", \"out\", \"Ljava/io/PrintStream;\");\n            super.visitInsn(DUP_X2);\n            super.visitInsn(POP);\n            super.visitMethodInsn(INVOKEVIRTUAL, \"java/io/PrintStream\", \"println\", \"(D)V\", false);\n        }\n\n        private void printString() {\n            super.visitFieldInsn(GETSTATIC, \"java/lang/System\", \"out\", \"Ljava/io/PrintStream;\");\n            super.visitInsn(SWAP);\n            super.visitMethodInsn(INVOKEVIRTUAL, \"java/io/PrintStream\", \"println\", \"(Ljava/lang/String;)V\", false);\n        }\n\n        private void printObject() {\n            super.visitFieldInsn(GETSTATIC, \"java/lang/System\", \"out\", \"Ljava/io/PrintStream;\");\n            super.visitInsn(SWAP);\n            super.visitMethodInsn(INVOKEVIRTUAL, \"java/io/PrintStream\", \"println\", \"(Ljava/lang/Object;)V\", false);\n        }\n\n        private void printMessage(String str) {\n            super.visitFieldInsn(GETSTATIC, \"java/lang/System\", \"out\", \"Ljava/io/PrintStream;\");\n            super.visitLdcInsn(str);\n            super.visitMethodInsn(INVOKEVIRTUAL, \"java/io/PrintStream\", \"println\", \"(Ljava/lang/String;)V\", false);\n        }\n    }\n}\n"
  },
  {
    "path": "src/main/java/lsieun/asm/core/MethodParameterVisitor2.java",
    "content": "package lsieun.asm.core;\n\nimport org.objectweb.asm.ClassVisitor;\nimport org.objectweb.asm.MethodVisitor;\nimport org.objectweb.asm.Type;\n\nimport static org.objectweb.asm.Opcodes.*;\n\npublic class MethodParameterVisitor2 extends ClassVisitor {\n    public MethodParameterVisitor2(int api, ClassVisitor classVisitor) {\n        super(api, classVisitor);\n    }\n\n    @Override\n    public MethodVisitor visitMethod(int access, String name, String descriptor, String signature, String[] exceptions) {\n        MethodVisitor mv = super.visitMethod(access, name, descriptor, signature, exceptions);\n        if (mv != null && !name.equals(\"<init>\")) {\n            boolean isAbstractMethod = (access & ACC_ABSTRACT) != 0;\n            boolean isNativeMethod = (access & ACC_NATIVE) != 0;\n            if (!isAbstractMethod && !isNativeMethod) {\n                mv = new MethodParameterAdapter2(api, mv, access, name, descriptor);\n            }\n        }\n        return mv;\n    }\n\n    private static class MethodParameterAdapter2 extends MethodVisitor {\n        private final int methodAccess;\n        private final String methodName;\n        private final String methodDesc;\n\n        public MethodParameterAdapter2(int api, MethodVisitor mv, int methodAccess, String methodName, String methodDesc) {\n            super(api, mv);\n            this.methodAccess = methodAccess;\n            this.methodName = methodName;\n            this.methodDesc = methodDesc;\n        }\n\n        @Override\n        public void visitCode() {\n            // 首先，处理自己的代码逻辑\n            boolean isStatic = ((methodAccess & ACC_STATIC) != 0);\n            int slotIndex = isStatic ? 0 : 1;\n\n            printMessage(\"Method Enter: \" + methodName + methodDesc);\n\n            Type methodType = Type.getMethodType(methodDesc);\n            Type[] argumentTypes = methodType.getArgumentTypes();\n            for (Type t : argumentTypes) {\n                int sort = t.getSort();\n                int size = t.getSize();\n                String descriptor = t.getDescriptor();\n                int opcode = t.getOpcode(ILOAD);\n                super.visitVarInsn(opcode, slotIndex);\n                if (sort >= Type.BOOLEAN && sort <= Type.DOUBLE) {\n                    String methodDesc = String.format(\"(%s)V\", descriptor);\n                    printValueOnStack(methodDesc);\n                }\n                else {\n                    printValueOnStack(\"(Ljava/lang/Object;)V\");\n                }\n\n                slotIndex += size;\n            }\n\n            // 其次，调用父类的方法实现\n            super.visitCode();\n        }\n\n        @Override\n        public void visitInsn(int opcode) {\n            // 首先，处理自己的代码逻辑\n            if ((opcode >= IRETURN && opcode <= RETURN) || opcode == ATHROW) {\n                printMessage(\"Method Exit: \" + methodName + methodDesc);\n                if (opcode >= IRETURN && opcode <= DRETURN) {\n                    Type methodType = Type.getMethodType(methodDesc);\n                    Type returnType = methodType.getReturnType();\n                    int size = returnType.getSize();\n                    String descriptor = returnType.getDescriptor();\n\n                    if (size == 1) {\n                        super.visitInsn(DUP);\n                    }\n                    else {\n                        super.visitInsn(DUP2);\n                    }\n                    String methodDesc = String.format(\"(%s)V\", descriptor);\n                    printValueOnStack(methodDesc);\n                }\n                else if (opcode == ARETURN) {\n                    super.visitInsn(DUP);\n                    printValueOnStack(\"(Ljava/lang/Object;)V\");\n                }\n                else if (opcode == RETURN) {\n                    printMessage(\"    return void\");\n                }\n                else {\n                    printMessage(\"    abnormal return\");\n                }\n            }\n\n            // 其次，调用父类的方法实现\n            super.visitInsn(opcode);\n        }\n\n        private void printMessage(String str) {\n            super.visitLdcInsn(str);\n            super.visitMethodInsn(INVOKESTATIC, \"sample/ParameterUtils\", \"printText\", \"(Ljava/lang/String;)V\", false);\n        }\n\n        private void printValueOnStack(String descriptor) {\n            super.visitMethodInsn(INVOKESTATIC, \"sample/ParameterUtils\", \"printValueOnStack\", descriptor, false);\n        }\n    }\n}\n"
  },
  {
    "path": "src/main/java/lsieun/asm/core/MethodPatternAdapter.java",
    "content": "package lsieun.asm.core;\n\nimport org.objectweb.asm.Handle;\nimport org.objectweb.asm.Label;\nimport org.objectweb.asm.MethodVisitor;\n\npublic abstract class MethodPatternAdapter extends MethodVisitor {\n    protected final static int SEEN_NOTHING = 0;\n    protected int state;\n\n    public MethodPatternAdapter(int api, MethodVisitor methodVisitor) {\n        super(api, methodVisitor);\n    }\n\n    @Override\n    public void visitInsn(int opcode) {\n        visitInsn();\n        super.visitInsn(opcode);\n    }\n\n    @Override\n    public void visitIntInsn(int opcode, int operand) {\n        visitInsn();\n        super.visitIntInsn(opcode, operand);\n    }\n\n    @Override\n    public void visitVarInsn(int opcode, int var) {\n        visitInsn();\n        super.visitVarInsn(opcode, var);\n    }\n\n    @Override\n    public void visitTypeInsn(int opcode, String type) {\n        visitInsn();\n        super.visitTypeInsn(opcode, type);\n    }\n\n    @Override\n    public void visitFieldInsn(int opcode, String owner, String name, String descriptor) {\n        visitInsn();\n        super.visitFieldInsn(opcode, owner, name, descriptor);\n    }\n\n    @Override\n    public void visitMethodInsn(int opcode, String owner, String name, String descriptor) {\n        visitInsn();\n        super.visitMethodInsn(opcode, owner, name, descriptor);\n    }\n\n    @Override\n    public void visitMethodInsn(int opcode, String owner, String name, String descriptor, boolean isInterface) {\n        visitInsn();\n        super.visitMethodInsn(opcode, owner, name, descriptor, isInterface);\n    }\n\n    @Override\n    public void visitInvokeDynamicInsn(String name, String descriptor, Handle bootstrapMethodHandle, Object... bootstrapMethodArguments) {\n        visitInsn();\n        super.visitInvokeDynamicInsn(name, descriptor, bootstrapMethodHandle, bootstrapMethodArguments);\n    }\n\n    @Override\n    public void visitJumpInsn(int opcode, Label label) {\n        visitInsn();\n        super.visitJumpInsn(opcode, label);\n    }\n\n    @Override\n    public void visitLdcInsn(Object value) {\n        visitInsn();\n        super.visitLdcInsn(value);\n    }\n\n    @Override\n    public void visitIincInsn(int var, int increment) {\n        visitInsn();\n        super.visitIincInsn(var, increment);\n    }\n\n    @Override\n    public void visitTableSwitchInsn(int min, int max, Label dflt, Label... labels) {\n        visitInsn();\n        super.visitTableSwitchInsn(min, max, dflt, labels);\n    }\n\n    @Override\n    public void visitLookupSwitchInsn(Label dflt, int[] keys, Label[] labels) {\n        visitInsn();\n        super.visitLookupSwitchInsn(dflt, keys, labels);\n    }\n\n    @Override\n    public void visitMultiANewArrayInsn(String descriptor, int numDimensions) {\n        visitInsn();\n        super.visitMultiANewArrayInsn(descriptor, numDimensions);\n    }\n\n    @Override\n    public void visitTryCatchBlock(Label start, Label end, Label handler, String type) {\n        visitInsn();\n        super.visitTryCatchBlock(start, end, handler, type);\n    }\n\n    @Override\n    public void visitLabel(Label label) {\n        visitInsn();\n        super.visitLabel(label);\n    }\n\n    @Override\n    public void visitFrame(int type, int numLocal, Object[] local, int numStack, Object[] stack) {\n        visitInsn();\n        super.visitFrame(type, numLocal, local, numStack, stack);\n    }\n\n    @Override\n    public void visitMaxs(int maxStack, int maxLocals) {\n        visitInsn();\n        super.visitMaxs(maxStack, maxLocals);\n    }\n\n    protected abstract void visitInsn();\n}\n"
  },
  {
    "path": "src/main/java/lsieun/asm/core/MethodRemoveAddZeroVisitor.java",
    "content": "package lsieun.asm.core;\n\nimport org.objectweb.asm.ClassVisitor;\nimport org.objectweb.asm.MethodVisitor;\n\nimport static org.objectweb.asm.Opcodes.*;\n\npublic class MethodRemoveAddZeroVisitor extends ClassVisitor {\n    public MethodRemoveAddZeroVisitor(int api, ClassVisitor classVisitor) {\n        super(api, classVisitor);\n    }\n\n    @Override\n    public MethodVisitor visitMethod(int access, String name, String descriptor, String signature, String[] exceptions) {\n        MethodVisitor mv = cv.visitMethod(access, name, descriptor, signature, exceptions);\n        if (mv != null && !\"<init>\".equals(name) && !\"<clinit>\".equals(name)) {\n            boolean isAbstractMethod = (access & ACC_ABSTRACT) != 0;\n            boolean isNativeMethod = (access & ACC_NATIVE) != 0;\n            if (!isAbstractMethod && !isNativeMethod) {\n                mv = new MethodRemoveAddZeroAdapter(api, mv);\n            }\n        }\n        return mv;\n    }\n\n    private static class MethodRemoveAddZeroAdapter extends MethodPatternAdapter {\n        private static final int SEEN_ICONST_0 = 1;\n\n        public MethodRemoveAddZeroAdapter(int api, MethodVisitor methodVisitor) {\n            super(api, methodVisitor);\n        }\n\n        @Override\n        public void visitInsn(int opcode) {\n            // 第一，对于感兴趣的状态进行处理\n            switch (state) {\n                case SEEN_NOTHING:\n                    if (opcode == ICONST_0) {\n                        state = SEEN_ICONST_0;\n                        return;\n                    }\n                    break;\n                case SEEN_ICONST_0:\n                    if (opcode == IADD) {\n                        state = SEEN_NOTHING;\n                        return;\n                    }\n                    else if (opcode == ICONST_0) {\n                        mv.visitInsn(ICONST_0);\n                        return;\n                    }\n                    break;\n            }\n\n            // 第二，对于不感兴趣的状态，交给父类进行处理\n            super.visitInsn(opcode);\n        }\n\n        @Override\n        protected void visitInsn() {\n            if (state == SEEN_ICONST_0) {\n                mv.visitInsn(ICONST_0);\n            }\n            state = SEEN_NOTHING;\n        }\n    }\n}\n"
  },
  {
    "path": "src/main/java/lsieun/asm/core/MethodRemoveGetFieldPutFieldVisitor.java",
    "content": "package lsieun.asm.core;\n\nimport org.objectweb.asm.ClassVisitor;\nimport org.objectweb.asm.MethodVisitor;\n\nimport static org.objectweb.asm.Opcodes.*;\n\npublic class MethodRemoveGetFieldPutFieldVisitor extends ClassVisitor {\n    public MethodRemoveGetFieldPutFieldVisitor(int api, ClassVisitor classVisitor) {\n        super(api, classVisitor);\n    }\n\n    @Override\n    public MethodVisitor visitMethod(int access, String name, String descriptor, String signature, String[] exceptions) {\n        MethodVisitor mv = cv.visitMethod(access, name, descriptor, signature, exceptions);\n        if (mv != null && !\"<init>\".equals(name) && !\"<clinit>\".equals(name)) {\n            boolean isAbstractMethod = (access & ACC_ABSTRACT) != 0;\n            boolean isNativeMethod = (access & ACC_NATIVE) != 0;\n            if (!isAbstractMethod && !isNativeMethod) {\n                mv = new MethodRemoveGetFieldPutFieldAdapter(api, mv);\n            }\n        }\n        return mv;\n    }\n\n    private class MethodRemoveGetFieldPutFieldAdapter extends MethodPatternAdapter {\n        private final static int SEEN_ALOAD_0 = 1;\n        private final static int SEEN_ALOAD_0_ALOAD_0 = 2;\n        private final static int SEEN_ALOAD_0_ALOAD_0_GETFIELD = 3;\n\n        private String fieldOwner;\n        private String fieldName;\n        private String fieldDesc;\n\n        public MethodRemoveGetFieldPutFieldAdapter(int api, MethodVisitor methodVisitor) {\n            super(api, methodVisitor);\n        }\n\n        @Override\n        public void visitVarInsn(int opcode, int var) {\n            // 第一，对于感兴趣的状态进行处理\n            switch (state) {\n                case SEEN_NOTHING:\n                    if (opcode == ALOAD && var == 0) {\n                        state = SEEN_ALOAD_0;\n                        return;\n                    }\n                    break;\n                case SEEN_ALOAD_0:\n                    if (opcode == ALOAD && var == 0) {\n                        state = SEEN_ALOAD_0_ALOAD_0;\n                        return;\n                    }\n                    break;\n                case SEEN_ALOAD_0_ALOAD_0:\n                    if (opcode == ALOAD && var == 0) {\n                        mv.visitVarInsn(opcode, var);\n                        return;\n                    }\n                    break;\n            }\n\n            // 第二，对于不感兴趣的状态，交给父类进行处理\n            super.visitVarInsn(opcode, var);\n        }\n\n        @Override\n        public void visitFieldInsn(int opcode, String owner, String name, String descriptor) {\n            // 第一，对于感兴趣的状态进行处理\n            switch (state) {\n                case SEEN_ALOAD_0_ALOAD_0:\n                    if (opcode == GETFIELD) {\n                        state = SEEN_ALOAD_0_ALOAD_0_GETFIELD;\n                        fieldOwner = owner;\n                        fieldName = name;\n                        fieldDesc = descriptor;\n                        return;\n                    }\n                    break;\n                case SEEN_ALOAD_0_ALOAD_0_GETFIELD:\n                    if (opcode == PUTFIELD && name.equals(fieldName)) {\n                        state = SEEN_NOTHING;\n                        return;\n                    }\n                    break;\n            }\n\n            // 第二，对于不感兴趣的状态，交给父类进行处理\n            super.visitFieldInsn(opcode, owner, name, descriptor);\n        }\n\n        @Override\n        protected void visitInsn() {\n            switch (state) {\n                case SEEN_ALOAD_0:\n                    mv.visitVarInsn(ALOAD, 0);\n                    break;\n                case SEEN_ALOAD_0_ALOAD_0:\n                    mv.visitVarInsn(ALOAD, 0);\n                    mv.visitVarInsn(ALOAD, 0);\n                    break;\n                case SEEN_ALOAD_0_ALOAD_0_GETFIELD:\n                    mv.visitVarInsn(ALOAD, 0);\n                    mv.visitVarInsn(ALOAD, 0);\n                    mv.visitFieldInsn(GETFIELD, fieldOwner, fieldName, fieldDesc);\n                    break;\n            }\n            state = SEEN_NOTHING;\n        }\n    }\n}\n"
  },
  {
    "path": "src/main/java/lsieun/asm/core/MethodRemoveNopVisitor.java",
    "content": "package lsieun.asm.core;\n\nimport org.objectweb.asm.ClassVisitor;\nimport org.objectweb.asm.MethodVisitor;\n\nimport static org.objectweb.asm.Opcodes.*;\n\npublic class MethodRemoveNopVisitor extends ClassVisitor {\n    public MethodRemoveNopVisitor(int api, ClassVisitor classVisitor) {\n        super(api, classVisitor);\n    }\n\n    @Override\n    public MethodVisitor visitMethod(int access, String name, String descriptor, String signature, String[] exceptions) {\n        MethodVisitor mv = super.visitMethod(access, name, descriptor, signature, exceptions);\n        if (mv != null && !\"<init>\".equals(name) && !\"<clinit>\".equals(name)) {\n            boolean isAbstractMethod = (access & ACC_ABSTRACT) != 0;\n            boolean isNativeMethod = (access & ACC_NATIVE) != 0;\n            if (!isAbstractMethod && !isNativeMethod) {\n                mv = new MethodRemoveNopAdapter(api, mv);\n            }\n\n        }\n        return mv;\n    }\n\n    private static class MethodRemoveNopAdapter extends MethodVisitor {\n        public MethodRemoveNopAdapter(int api, MethodVisitor methodVisitor) {\n            super(api, methodVisitor);\n        }\n\n        @Override\n        public void visitInsn(int opcode) {\n            if (opcode != NOP) {\n                super.visitInsn(opcode);\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "src/main/java/lsieun/asm/core/MethodRemovePrintVisitor.java",
    "content": "package lsieun.asm.core;\n\nimport org.objectweb.asm.ClassVisitor;\nimport org.objectweb.asm.MethodVisitor;\n\nimport static org.objectweb.asm.Opcodes.*;\n\npublic class MethodRemovePrintVisitor extends ClassVisitor {\n    public MethodRemovePrintVisitor(int api, ClassVisitor classVisitor) {\n        super(api, classVisitor);\n    }\n\n    @Override\n    public MethodVisitor visitMethod(int access, String name, String descriptor, String signature, String[] exceptions) {\n        MethodVisitor mv = cv.visitMethod(access, name, descriptor, signature, exceptions);\n        if (mv != null && !\"<init>\".equals(name) && !\"<clinit>\".equals(name)) {\n            boolean isAbstractMethod = (access & ACC_ABSTRACT) != 0;\n            boolean isNativeMethod = (access & ACC_NATIVE) != 0;\n            if (!isAbstractMethod && !isNativeMethod) {\n                mv = new MethodRemovePrintAdaptor(api, mv);\n            }\n        }\n        return mv;\n    }\n\n    private class MethodRemovePrintAdaptor extends MethodPatternAdapter {\n        private static final int SEEN_GETSTATIC = 1;\n        private static final int SEEN_GETSTATIC_LDC = 2;\n\n        private String message;\n\n        public MethodRemovePrintAdaptor(int api, MethodVisitor methodVisitor) {\n            super(api, methodVisitor);\n        }\n\n        @Override\n        public void visitFieldInsn(int opcode, String owner, String name, String descriptor) {\n            // 第一，对于感兴趣的状态进行处理\n            boolean flag = (opcode == GETSTATIC && owner.equals(\"java/lang/System\") && name.equals(\"out\") && descriptor.equals(\"Ljava/io/PrintStream;\"));\n            switch (state) {\n                case SEEN_NOTHING:\n                    if (flag) {\n                        state = SEEN_GETSTATIC;\n                        return;\n                    }\n                    break;\n                case SEEN_GETSTATIC:\n                    if (flag) {\n                        mv.visitFieldInsn(GETSTATIC, \"java/lang/System\", \"out\", \"Ljava/io/PrintStream;\");\n                        return;\n                    }\n            }\n\n            // 第二，对于不感兴趣的状态，交给父类进行处理\n            super.visitFieldInsn(opcode, owner, name, descriptor);\n        }\n\n        @Override\n        public void visitLdcInsn(Object value) {\n            // 第一，对于感兴趣的状态进行处理\n            switch (state) {\n                case SEEN_GETSTATIC:\n                    if (value instanceof String) {\n                        state = SEEN_GETSTATIC_LDC;\n                        message = (String) value;\n                        return;\n                    }\n                    break;\n            }\n\n            // 第二，对于不感兴趣的状态，交给父类进行处理\n            super.visitLdcInsn(value);\n        }\n\n        @Override\n        public void visitMethodInsn(int opcode, String owner, String name, String descriptor, boolean isInterface) {\n            // 第一，对于感兴趣的状态进行处理\n            switch (state) {\n                case SEEN_GETSTATIC_LDC:\n                    if (opcode == INVOKEVIRTUAL && owner.equals(\"java/io/PrintStream\") &&\n                            name.equals(\"println\") && descriptor.equals(\"(Ljava/lang/String;)V\")) {\n                        state = SEEN_NOTHING;\n                        return;\n                    }\n                    break;\n            }\n\n            // 第二，对于不感兴趣的状态，交给父类进行处理\n            super.visitMethodInsn(opcode, owner, name, descriptor, isInterface);\n        }\n\n        @Override\n        protected void visitInsn() {\n            switch (state) {\n                case SEEN_GETSTATIC:\n                    mv.visitFieldInsn(GETSTATIC, \"java/lang/System\", \"out\", \"Ljava/io/PrintStream;\");\n                    break;\n                case SEEN_GETSTATIC_LDC:\n                    mv.visitFieldInsn(GETSTATIC, \"java/lang/System\", \"out\", \"Ljava/io/PrintStream;\");\n                    mv.visitLdcInsn(message);\n                    break;\n            }\n\n            state = SEEN_NOTHING;\n        }\n    }\n}\n"
  },
  {
    "path": "src/main/java/lsieun/asm/core/MethodReplaceInvokeVisitor.java",
    "content": "package lsieun.asm.core;\n\nimport org.objectweb.asm.ClassVisitor;\nimport org.objectweb.asm.MethodVisitor;\n\nimport static org.objectweb.asm.Opcodes.ACC_ABSTRACT;\nimport static org.objectweb.asm.Opcodes.ACC_NATIVE;\n\npublic class MethodReplaceInvokeVisitor extends ClassVisitor {\n    private final String oldOwner;\n    private final String oldMethodName;\n    private final String oldMethodDesc;\n\n    private final int newOpcode;\n    private final String newOwner;\n    private final String newMethodName;\n    private final String newMethodDesc;\n\n    public MethodReplaceInvokeVisitor(int api, ClassVisitor classVisitor,\n                                      String oldOwner, String oldMethodName, String oldMethodDesc,\n                                      int newOpcode, String newOwner, String newMethodName, String newMethodDesc) {\n        super(api, classVisitor);\n        this.oldOwner = oldOwner;\n        this.oldMethodName = oldMethodName;\n        this.oldMethodDesc = oldMethodDesc;\n\n        this.newOpcode = newOpcode;\n        this.newOwner = newOwner;\n        this.newMethodName = newMethodName;\n        this.newMethodDesc = newMethodDesc;\n    }\n\n    @Override\n    public MethodVisitor visitMethod(int access, String name, String descriptor, String signature, String[] exceptions) {\n        MethodVisitor mv = super.visitMethod(access, name, descriptor, signature, exceptions);\n        if (mv != null && !\"<init>\".equals(name) && !\"<clinit>\".equals(name)) {\n            boolean isAbstractMethod = (access & ACC_ABSTRACT) != 0;\n            boolean isNativeMethod = (access & ACC_NATIVE) != 0;\n            if (!isAbstractMethod && !isNativeMethod) {\n                mv = new MethodReplaceInvokeAdapter(api, mv);\n            }\n        }\n        return mv;\n    }\n\n    private class MethodReplaceInvokeAdapter extends MethodVisitor {\n        public MethodReplaceInvokeAdapter(int api, MethodVisitor methodVisitor) {\n            super(api, methodVisitor);\n        }\n\n        @Override\n        public void visitMethodInsn(int opcode, String owner, String name, String descriptor, boolean isInterface) {\n            if (oldOwner.equals(owner) && oldMethodName.equals(name) && oldMethodDesc.equals(descriptor)) {\n                // 注意，最后一个参数是false，会不会太武断呢？\n                super.visitMethodInsn(newOpcode, newOwner, newMethodName, newMethodDesc, false);\n            }\n            else {\n                super.visitMethodInsn(opcode, owner, name, descriptor, isInterface);\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "src/main/java/lsieun/asm/core/MethodTimerVisitor.java",
    "content": "package lsieun.asm.core;\n\nimport org.objectweb.asm.ClassVisitor;\nimport org.objectweb.asm.FieldVisitor;\nimport org.objectweb.asm.MethodVisitor;\n\nimport static org.objectweb.asm.Opcodes.*;\n\npublic class MethodTimerVisitor extends ClassVisitor {\n    private String owner;\n    private boolean isInterface;\n\n    public MethodTimerVisitor(int api, ClassVisitor classVisitor) {\n        super(api, classVisitor);\n    }\n\n    @Override\n    public void visit(int version, int access, String name, String signature, String superName, String[] interfaces) {\n        super.visit(version, access, name, signature, superName, interfaces);\n        owner = name;\n        isInterface = (access & ACC_INTERFACE) != 0;\n    }\n\n    @Override\n    public MethodVisitor visitMethod(int access, String name, String descriptor, String signature, String[] exceptions) {\n        MethodVisitor mv = super.visitMethod(access, name, descriptor, signature, exceptions);\n        if (!isInterface && mv != null && !\"<init>\".equals(name) && !\"<clinit>\".equals(name)) {\n            boolean isAbstractMethod = (access & ACC_ABSTRACT) != 0;\n            boolean isNativeMethod = (access & ACC_NATIVE) != 0;\n            if (!isAbstractMethod && !isNativeMethod) {\n                mv = new MethodTimerAdapter(api, mv, owner);\n            }\n        }\n        return mv;\n    }\n\n    @Override\n    public void visitEnd() {\n        if (!isInterface) {\n            FieldVisitor fv = super.visitField(ACC_PUBLIC | ACC_STATIC, \"timer\", \"J\", null, null);\n            if (fv != null) {\n                fv.visitEnd();\n            }\n        }\n        super.visitEnd();\n    }\n\n    private static class MethodTimerAdapter extends MethodVisitor {\n        private final String owner;\n\n        public MethodTimerAdapter(int api, MethodVisitor mv, String owner) {\n            super(api, mv);\n            this.owner = owner;\n        }\n\n        @Override\n        public void visitCode() {\n            // 首先，处理自己的代码逻辑\n            super.visitFieldInsn(GETSTATIC, owner, \"timer\", \"J\");\n            super.visitMethodInsn(INVOKESTATIC, \"java/lang/System\", \"currentTimeMillis\", \"()J\", false);\n            super.visitInsn(LSUB);\n            super.visitFieldInsn(PUTSTATIC, owner, \"timer\", \"J\");\n\n            // 其次，调用父类的方法实现\n            super.visitCode();\n        }\n\n        @Override\n        public void visitInsn(int opcode) {\n            // 首先，处理自己的代码逻辑\n            if ((opcode >= IRETURN && opcode <= RETURN) || opcode == ATHROW) {\n                super.visitFieldInsn(GETSTATIC, owner, \"timer\", \"J\");\n                super.visitMethodInsn(INVOKESTATIC, \"java/lang/System\", \"currentTimeMillis\", \"()J\", false);\n                super.visitInsn(LADD);\n                super.visitFieldInsn(PUTSTATIC, owner, \"timer\", \"J\");\n            }\n\n            // 其次，调用父类的方法实现\n            super.visitInsn(opcode);\n        }\n    }\n}"
  },
  {
    "path": "src/main/java/lsieun/asm/core/MethodTimerVisitor2.java",
    "content": "package lsieun.asm.core;\n\nimport org.objectweb.asm.ClassVisitor;\nimport org.objectweb.asm.FieldVisitor;\nimport org.objectweb.asm.MethodVisitor;\n\nimport static org.objectweb.asm.Opcodes.*;\n\npublic class MethodTimerVisitor2 extends ClassVisitor {\n    private String owner;\n    private boolean isInterface;\n\n    public MethodTimerVisitor2(int api, ClassVisitor classVisitor) {\n        super(api, classVisitor);\n    }\n\n    @Override\n    public void visit(int version, int access, String name, String signature, String superName, String[] interfaces) {\n        super.visit(version, access, name, signature, superName, interfaces);\n        owner = name;\n        isInterface = (access & ACC_INTERFACE) != 0;\n    }\n\n    @Override\n    public MethodVisitor visitMethod(int access, String name, String descriptor, String signature, String[] exceptions) {\n        MethodVisitor mv = super.visitMethod(access, name, descriptor, signature, exceptions);\n        if (!isInterface && mv != null && !\"<init>\".equals(name) && !\"<clinit>\".equals(name)) {\n            boolean isAbstractMethod = (access & ACC_ABSTRACT) != 0;\n            boolean isNativeMethod = (access & ACC_NATIVE) != 0;\n            if (!isAbstractMethod && !isNativeMethod) {\n                // 每遇到一个合适的方法，就添加一个相应的字段\n                FieldVisitor fv = super.visitField(ACC_PUBLIC | ACC_STATIC, getFieldName(name), \"J\", null, null);\n                if (fv != null) {\n                    fv.visitEnd();\n                }\n\n                mv = new MethodTimerAdapter2(api, mv, owner, name);\n            }\n\n        }\n        return mv;\n    }\n\n\n    private String getFieldName(String methodName) {\n        return \"timer_\" + methodName;\n    }\n\n    private class MethodTimerAdapter2 extends MethodVisitor {\n        private final String owner;\n        private final String methodName;\n\n        public MethodTimerAdapter2(int api, MethodVisitor mv, String owner, String methodName) {\n            super(api, mv);\n            this.owner = owner;\n            this.methodName = methodName;\n        }\n\n        @Override\n        public void visitCode() {\n            // 首先，处理自己的代码逻辑\n            super.visitFieldInsn(GETSTATIC, owner, getFieldName(methodName), \"J\"); // 注意，字段名字要对应\n            super.visitMethodInsn(INVOKESTATIC, \"java/lang/System\", \"currentTimeMillis\", \"()J\", false);\n            super.visitInsn(LSUB);\n            super.visitFieldInsn(PUTSTATIC, owner, getFieldName(methodName), \"J\"); // 注意，字段名字要对应\n\n            // 其次，调用父类的方法实现\n            super.visitCode();\n        }\n\n        @Override\n        public void visitInsn(int opcode) {\n            // 首先，处理自己的代码逻辑\n            if ((opcode >= IRETURN && opcode <= RETURN) || opcode == ATHROW) {\n                super.visitFieldInsn(GETSTATIC, owner, getFieldName(methodName), \"J\"); // 注意，字段名字要对应\n                super.visitMethodInsn(INVOKESTATIC, \"java/lang/System\", \"currentTimeMillis\", \"()J\", false);\n                super.visitInsn(LADD);\n                super.visitFieldInsn(PUTSTATIC, owner, getFieldName(methodName), \"J\"); // 注意，字段名字要对应\n            }\n\n            // 其次，调用父类的方法实现\n            super.visitInsn(opcode);\n        }\n    }\n}\n"
  },
  {
    "path": "src/main/java/lsieun/asm/core/MethodTimerVisitor3.java",
    "content": "package lsieun.asm.core;\n\nimport org.objectweb.asm.ClassVisitor;\nimport org.objectweb.asm.MethodVisitor;\nimport org.objectweb.asm.Type;\nimport org.objectweb.asm.commons.LocalVariablesSorter;\n\nimport static org.objectweb.asm.Opcodes.*;\n\npublic class MethodTimerVisitor3 extends ClassVisitor {\n    public MethodTimerVisitor3(int api, ClassVisitor cv) {\n        super(api, cv);\n    }\n\n    @Override\n    public MethodVisitor visitMethod(int access, String name, String descriptor, String signature, String[] exceptions) {\n        MethodVisitor mv = super.visitMethod(access, name, descriptor, signature, exceptions);\n\n        if (mv != null && !\"<init>\".equals(name) && !\"<clinit>\".equals(name)) {\n            boolean isAbstractMethod = (access & ACC_ABSTRACT) != 0;\n            boolean isNativeMethod = (access & ACC_NATIVE) != 0;\n            if (!isAbstractMethod && !isNativeMethod) {\n                mv = new MethodTimerAdapter3(api, access, name, descriptor, mv);\n            }\n        }\n        return mv;\n    }\n\n    private static class MethodTimerAdapter3 extends LocalVariablesSorter {\n        private final String methodName;\n        private final String methodDesc;\n        private int slotIndex;\n\n        public MethodTimerAdapter3(int api, int access, String name, String descriptor, MethodVisitor methodVisitor) {\n            super(api, access, descriptor, methodVisitor);\n            this.methodName = name;\n            this.methodDesc = descriptor;\n        }\n\n        @Override\n        public void visitCode() {\n            // 首先，实现自己的逻辑\n            slotIndex = newLocal(Type.LONG_TYPE);\n            mv.visitMethodInsn(INVOKESTATIC, \"java/lang/System\", \"currentTimeMillis\", \"()J\", false);\n            mv.visitVarInsn(LSTORE, slotIndex);\n\n            // 其次，调用父类的实现\n            super.visitCode();\n        }\n\n        @Override\n        public void visitInsn(int opcode) {\n            // 首先，实现自己的逻辑\n            if ((opcode >= IRETURN && opcode <= RETURN) || opcode == ATHROW) {\n                mv.visitMethodInsn(INVOKESTATIC, \"java/lang/System\", \"currentTimeMillis\", \"()J\", false);\n                mv.visitVarInsn(LLOAD, slotIndex);\n                mv.visitInsn(LSUB);\n                mv.visitVarInsn(LSTORE, slotIndex);\n                mv.visitFieldInsn(GETSTATIC, \"java/lang/System\", \"out\", \"Ljava/io/PrintStream;\");\n                mv.visitTypeInsn(NEW, \"java/lang/StringBuilder\");\n                mv.visitInsn(DUP);\n                mv.visitMethodInsn(INVOKESPECIAL, \"java/lang/StringBuilder\", \"<init>\", \"()V\", false);\n                mv.visitLdcInsn(methodName + methodDesc + \" method execute: \");\n                mv.visitMethodInsn(INVOKEVIRTUAL, \"java/lang/StringBuilder\", \"append\", \"(Ljava/lang/String;)Ljava/lang/StringBuilder;\", false);\n                mv.visitVarInsn(LLOAD, slotIndex);\n                mv.visitMethodInsn(INVOKEVIRTUAL, \"java/lang/StringBuilder\", \"append\", \"(J)Ljava/lang/StringBuilder;\", false);\n                mv.visitMethodInsn(INVOKEVIRTUAL, \"java/lang/StringBuilder\", \"toString\", \"()Ljava/lang/String;\", false);\n                mv.visitMethodInsn(INVOKEVIRTUAL, \"java/io/PrintStream\", \"println\", \"(Ljava/lang/String;)V\", false);\n            }\n\n            // 其次，调用父类的实现\n            super.visitInsn(opcode);\n        }\n    }\n}"
  },
  {
    "path": "src/main/java/lsieun/asm/core/MethodTimerVisitor4.java",
    "content": "package lsieun.asm.core;\n\nimport org.objectweb.asm.ClassVisitor;\nimport org.objectweb.asm.MethodVisitor;\nimport org.objectweb.asm.Type;\nimport org.objectweb.asm.commons.AdviceAdapter;\n\nimport static org.objectweb.asm.Opcodes.*;\n\npublic class MethodTimerVisitor4 extends ClassVisitor {\n    public MethodTimerVisitor4(int api, ClassVisitor classVisitor) {\n        super(api, classVisitor);\n    }\n\n    @Override\n    public MethodVisitor visitMethod(int access, String name, String descriptor, String signature, String[] exceptions) {\n        MethodVisitor mv = super.visitMethod(access, name, descriptor, signature, exceptions);\n        if (mv != null) {\n            boolean isAbstractMethod = (access & ACC_ABSTRACT) != 0;\n            boolean isNativeMethod = (access & ACC_NATIVE) != 0;\n            if (!isAbstractMethod && !isNativeMethod) {\n                mv = new MethodTimerAdapter4(api, mv, access, name, descriptor);\n            }\n        }\n        return mv;\n    }\n\n    private static class MethodTimerAdapter4 extends AdviceAdapter {\n        private int slotIndex;\n\n        public MethodTimerAdapter4(int api, MethodVisitor mv, int access, String name, String descriptor) {\n            super(api, mv, access, name, descriptor);\n        }\n\n        @Override\n        protected void onMethodEnter() {\n            slotIndex = newLocal(Type.LONG_TYPE);\n            mv.visitMethodInsn(INVOKESTATIC, \"java/lang/System\", \"currentTimeMillis\", \"()J\", false);\n            mv.visitVarInsn(LSTORE, slotIndex);\n        }\n\n        @Override\n        protected void onMethodExit(int opcode) {\n            if ((opcode >= IRETURN && opcode <= RETURN) || opcode == ATHROW) {\n                mv.visitMethodInsn(INVOKESTATIC, \"java/lang/System\", \"currentTimeMillis\", \"()J\", false);\n                mv.visitVarInsn(LLOAD, slotIndex);\n                mv.visitInsn(LSUB);\n                mv.visitVarInsn(LSTORE, slotIndex);\n                mv.visitFieldInsn(GETSTATIC, \"java/lang/System\", \"out\", \"Ljava/io/PrintStream;\");\n                mv.visitTypeInsn(NEW, \"java/lang/StringBuilder\");\n                mv.visitInsn(DUP);\n                mv.visitMethodInsn(INVOKESPECIAL, \"java/lang/StringBuilder\", \"<init>\", \"()V\", false);\n                mv.visitLdcInsn(getName() + methodDesc + \" method execute: \");\n                mv.visitMethodInsn(INVOKEVIRTUAL, \"java/lang/StringBuilder\", \"append\", \"(Ljava/lang/String;)Ljava/lang/StringBuilder;\", false);\n                mv.visitVarInsn(LLOAD, slotIndex);\n                mv.visitMethodInsn(INVOKEVIRTUAL, \"java/lang/StringBuilder\", \"append\", \"(J)Ljava/lang/StringBuilder;\", false);\n                mv.visitMethodInsn(INVOKEVIRTUAL, \"java/lang/StringBuilder\", \"toString\", \"()Ljava/lang/String;\", false);\n                mv.visitMethodInsn(INVOKEVIRTUAL, \"java/io/PrintStream\", \"println\", \"(Ljava/lang/String;)V\", false);\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "src/main/java/lsieun/asm/core/SuperPackageAttribute.java",
    "content": "package lsieun.asm.core;\n\nimport org.objectweb.asm.*;\n\npublic class SuperPackageAttribute extends Attribute {\n    public String name;\n\n    public SuperPackageAttribute() {\n        super(\"Superpackage\");\n    }\n\n    public SuperPackageAttribute(String name) {\n        this();\n        this.name = name;\n    }\n\n    @Override\n    protected Attribute read(ClassReader classReader, int offset, int length, char[] charBuffer, int codeAttributeOffset, Label[] labels) {\n        String name = classReader.readUTF8(offset, charBuffer);\n        return new SuperPackageAttribute(name);\n    }\n\n    @Override\n    protected ByteVector write(ClassWriter classWriter, byte[] code, int codeLength, int maxStack, int maxLocals) {\n        int index = classWriter.newUTF8(name);\n        return new ByteVector().putShort(index);\n    }\n\n    @Override\n    public String toString() {\n        return String.format(\"%s {name='%s'}\", type, name);\n    }\n}\n"
  },
  {
    "path": "src/main/java/lsieun/asm/core/counter/ClassCounterVisitor.java",
    "content": "package lsieun.asm.core.counter;\n\nimport org.objectweb.asm.ClassVisitor;\nimport org.objectweb.asm.FieldVisitor;\nimport org.objectweb.asm.MethodVisitor;\n\nimport static org.objectweb.asm.Opcodes.*;\n\npublic class ClassCounterVisitor extends ClassVisitor {\n    private String owner;\n    private boolean isInterface;\n\n    public ClassCounterVisitor(int api, ClassVisitor cv) {\n        super(api, cv);\n    }\n\n    @Override\n    public void visit(int version, int access, String name, String signature, String superName, String[] interfaces) {\n        super.visit(version, access, name, signature, superName, interfaces);\n        owner = name;\n        isInterface = (access & ACC_INTERFACE) != 0;\n    }\n\n    @Override\n    public MethodVisitor visitMethod(int access, String name, String descriptor, String signature, String[] exceptions) {\n        MethodVisitor mv = super.visitMethod(access, name, descriptor, signature, exceptions);\n        if (!isInterface && mv != null && !name.equals(\"<init>\")) {\n            String fieldName = name + \"_count\";\n            mv = new MethodCounterAdapter(api, mv, owner, fieldName);\n\n            FieldVisitor fv = super.visitField(ACC_PUBLIC | ACC_STATIC, fieldName, \"I\", null, null);\n            if (fv != null) {\n                fv.visitEnd();\n            }\n        }\n        return mv;\n    }\n}\n"
  },
  {
    "path": "src/main/java/lsieun/asm/core/counter/MethodCounterAdapter.java",
    "content": "package lsieun.asm.core.counter;\n\nimport org.objectweb.asm.MethodVisitor;\n\nimport static org.objectweb.asm.Opcodes.*;\n\npublic class MethodCounterAdapter extends MethodVisitor {\n    private final String owner;\n    private final String fieldName;\n\n    public MethodCounterAdapter(int api, MethodVisitor mv, String owner, String fieldName) {\n        super(api, mv);\n        this.owner = owner;\n        this.fieldName = fieldName;\n    }\n\n    @Override\n    public void visitCode() {\n        super.visitFieldInsn(GETSTATIC, owner, fieldName, \"I\");\n        super.visitInsn(ICONST_1);\n        super.visitInsn(IADD);\n        super.visitFieldInsn(PUTSTATIC, owner, fieldName, \"I\");\n        super.visitCode();\n    }\n}\n"
  },
  {
    "path": "src/main/java/lsieun/asm/core/empty/EmptyAnnotationVisitor.java",
    "content": "package lsieun.asm.core.empty;\n\nimport org.objectweb.asm.AnnotationVisitor;\n\npublic class EmptyAnnotationVisitor extends AnnotationVisitor {\n    public EmptyAnnotationVisitor(int api, AnnotationVisitor annotationVisitor) {\n        super(api, annotationVisitor);\n    }\n}\n"
  },
  {
    "path": "src/main/java/lsieun/asm/core/empty/EmptyClassVisitor.java",
    "content": "package lsieun.asm.core.empty;\n\nimport org.objectweb.asm.*;\n\npublic class EmptyClassVisitor extends ClassVisitor {\n    public EmptyClassVisitor(int api, ClassVisitor classVisitor) {\n        super(api, classVisitor);\n    }\n\n    @Override\n    public ModuleVisitor visitModule(String name, int access, String version) {\n        ModuleVisitor mv = super.visitModule(name, access, version);\n        return new EmptyModuleVisitor(api, mv);\n    }\n\n    @Override\n    public AnnotationVisitor visitAnnotation(String descriptor, boolean visible) {\n        AnnotationVisitor av = super.visitAnnotation(descriptor, visible);\n        return new EmptyAnnotationVisitor(api, av);\n    }\n\n    @Override\n    public AnnotationVisitor visitTypeAnnotation(int typeRef, TypePath typePath, String descriptor, boolean visible) {\n        AnnotationVisitor av = super.visitTypeAnnotation(typeRef, typePath, descriptor, visible);\n        return new EmptyAnnotationVisitor(api, av);\n    }\n\n    @Override\n    public RecordComponentVisitor visitRecordComponent(String name, String descriptor, String signature) {\n        RecordComponentVisitor rcv = super.visitRecordComponent(name, descriptor, signature);\n        return new EmptyRecordComponentVisitor(api, rcv);\n    }\n\n    @Override\n    public FieldVisitor visitField(int access, String name, String descriptor, String signature, Object value) {\n        FieldVisitor fv = super.visitField(access, name, descriptor, signature, value);\n        return new EmptyFieldVisitor(api, fv);\n    }\n\n    @Override\n    public MethodVisitor visitMethod(int access, String name, String descriptor, String signature, String[] exceptions) {\n        MethodVisitor mv = super.visitMethod(access, name, descriptor, signature, exceptions);\n        return new EmptyMethodVisitor(api, mv);\n    }\n\n}\n"
  },
  {
    "path": "src/main/java/lsieun/asm/core/empty/EmptyFieldVisitor.java",
    "content": "package lsieun.asm.core.empty;\n\nimport org.objectweb.asm.FieldVisitor;\n\npublic class EmptyFieldVisitor extends FieldVisitor {\n    public EmptyFieldVisitor(int api, FieldVisitor fieldVisitor) {\n        super(api, fieldVisitor);\n    }\n}\n"
  },
  {
    "path": "src/main/java/lsieun/asm/core/empty/EmptyMethodVisitor.java",
    "content": "package lsieun.asm.core.empty;\n\nimport org.objectweb.asm.MethodVisitor;\n\npublic class EmptyMethodVisitor extends MethodVisitor {\n    public EmptyMethodVisitor(int api, MethodVisitor methodVisitor) {\n        super(api, methodVisitor);\n    }\n}\n"
  },
  {
    "path": "src/main/java/lsieun/asm/core/empty/EmptyModuleVisitor.java",
    "content": "package lsieun.asm.core.empty;\n\nimport org.objectweb.asm.ModuleVisitor;\n\npublic class EmptyModuleVisitor extends ModuleVisitor {\n    public EmptyModuleVisitor(int api, ModuleVisitor moduleVisitor) {\n        super(api, moduleVisitor);\n    }\n}\n"
  },
  {
    "path": "src/main/java/lsieun/asm/core/empty/EmptyRecordComponentVisitor.java",
    "content": "package lsieun.asm.core.empty;\n\nimport org.objectweb.asm.RecordComponentVisitor;\n\npublic class EmptyRecordComponentVisitor extends RecordComponentVisitor {\n    public EmptyRecordComponentVisitor(int api, RecordComponentVisitor recordComponentVisitor) {\n        super(api, recordComponentVisitor);\n    }\n}\n"
  },
  {
    "path": "src/main/java/lsieun/asm/core/info/InfoClassVisitor.java",
    "content": "package lsieun.asm.core.info;\n\nimport org.objectweb.asm.*;\n\nimport java.util.ArrayList;\nimport java.util.Arrays;\nimport java.util.List;\n\npublic class InfoClassVisitor extends ClassVisitor {\n    public InfoClassVisitor(int api, ClassVisitor classVisitor) {\n        super(api, classVisitor);\n    }\n\n    @Override\n    public void visit(int version, int access, String name, String signature, String superName, String[] interfaces) {\n        String line = String.format(\"ClassVisitor.visit(%d, %s, %s, %s, %s, %s);\", version, getAccess(access), name, signature, superName, Arrays.toString(interfaces));\n        System.out.println(line);\n        super.visit(version, access, name, signature, superName, interfaces);\n    }\n\n    @Override\n    public void visitAttribute(Attribute attribute) {\n        String line = String.format(\"ClassVisitor.visitAttribute(%s);\", attribute);\n        System.out.println(line);\n\n        super.visitAttribute(attribute);\n    }\n\n    @Override\n    public FieldVisitor visitField(int access, String name, String descriptor, String signature, Object value) {\n        String line = String.format(\"ClassVisitor.visitField(%s, %s, %s, %s, %s);\", getAccess(access), name, descriptor, signature, value);\n        System.out.println(line);\n\n        FieldVisitor fv = super.visitField(access, name, descriptor, signature, value);\n        return new InfoFieldVisitor(api, fv);\n    }\n\n    @Override\n    public MethodVisitor visitMethod(int access, String name, String descriptor, String signature, String[] exceptions) {\n        String line = String.format(\"ClassVisitor.visitMethod(%s, %s, %s, %s, %s);\", getAccess(access), name, descriptor, signature, Arrays.toString(exceptions));\n        System.out.println(line);\n\n        MethodVisitor mv = super.visitMethod(access, name, descriptor, signature, exceptions);\n        return new InfoMethodVisitor(api, mv);\n    }\n\n    @Override\n    public void visitEnd() {\n        String line = String.format(\"ClassVisitor.visitEnd();\");\n        System.out.println(line);\n        super.visitEnd();\n    }\n\n    private String getAccess(int access) {\n        List<String> list = new ArrayList<>();\n        if ((access & Opcodes.ACC_PUBLIC) != 0) {\n            list.add(\"ACC_PUBLIC\");\n        }\n        else if ((access & Opcodes.ACC_PROTECTED) != 0) {\n            list.add(\"ACC_PROTECTED\");\n        }\n        else if ((access & Opcodes.ACC_PRIVATE) != 0) {\n            list.add(\"ACC_PRIVATE\");\n        }\n\n        if ((access & Opcodes.ACC_STATIC) != 0) {\n            list.add(\"ACC_STATIC\");\n        }\n        if ((access & Opcodes.ACC_FINAL) != 0) {\n            list.add(\"ACC_FINAL\");\n        }\n        if ((access & Opcodes.ACC_NATIVE) != 0) {\n            list.add(\"ACC_NATIVE\");\n        }\n        if ((access & Opcodes.ACC_INTERFACE) != 0) {\n            list.add(\"ACC_INTERFACE\");\n        }\n        if ((access & Opcodes.ACC_ABSTRACT) != 0) {\n            list.add(\"ACC_ABSTRACT\");\n        }\n        if ((access & Opcodes.ACC_SYNTHETIC) != 0) {\n            list.add(\"ACC_SYNTHETIC\");\n        }\n        return list.toString();\n    }\n}\n"
  },
  {
    "path": "src/main/java/lsieun/asm/core/info/InfoFieldVisitor.java",
    "content": "package lsieun.asm.core.info;\n\nimport org.objectweb.asm.FieldVisitor;\n\npublic class InfoFieldVisitor extends FieldVisitor {\n    public InfoFieldVisitor(int api, FieldVisitor fieldVisitor) {\n        super(api, fieldVisitor);\n    }\n\n    @Override\n    public void visitEnd() {\n        String line = String.format(\"    FieldVisitor.visitEnd();\");\n        System.out.println(line);\n        super.visitEnd();\n    }\n}\n"
  },
  {
    "path": "src/main/java/lsieun/asm/core/info/InfoMethodVisitor.java",
    "content": "package lsieun.asm.core.info;\n\nimport org.objectweb.asm.Label;\nimport org.objectweb.asm.MethodVisitor;\nimport org.objectweb.asm.util.Printer;\n\npublic class InfoMethodVisitor extends MethodVisitor {\n    public InfoMethodVisitor(int api, MethodVisitor methodVisitor) {\n        super(api, methodVisitor);\n    }\n\n    @Override\n    public void visitCode() {\n        String line = String.format(\"    MethodVisitor.visitCode();\");\n        System.out.println(line);\n        super.visitCode();\n    }\n\n    @Override\n    public void visitInsn(int opcode) {\n        String line = String.format(\"    MethodVisitor.visitInsn(%s);\", Printer.OPCODES[opcode]);\n        System.out.println(line);\n        super.visitInsn(opcode);\n    }\n\n    @Override\n    public void visitIntInsn(int opcode, int operand) {\n        String line = String.format(\"    MethodVisitor.visitIntInsn(%s, %s);\", Printer.OPCODES[opcode], operand);\n        System.out.println(line);\n        super.visitIntInsn(opcode, operand);\n    }\n\n    @Override\n    public void visitVarInsn(int opcode, int var) {\n        String line = String.format(\"    MethodVisitor.visitVarInsn(%s, %s);\", Printer.OPCODES[opcode], var);\n        System.out.println(line);\n        super.visitVarInsn(opcode, var);\n    }\n\n    @Override\n    public void visitTypeInsn(int opcode, String type) {\n        String line = String.format(\"    MethodVisitor.visitTypeInsn(%s, %s);\", Printer.OPCODES[opcode], type);\n        System.out.println(line);\n        super.visitTypeInsn(opcode, type);\n    }\n\n    @Override\n    public void visitFieldInsn(int opcode, String owner, String name, String descriptor) {\n        String line = String.format(\"    MethodVisitor.visitFieldInsn(%s, %s, %s, %s);\", Printer.OPCODES[opcode], owner, name, descriptor);\n        System.out.println(line);\n        super.visitFieldInsn(opcode, owner, name, descriptor);\n    }\n\n    @Override\n    public void visitMethodInsn(int opcode, String owner, String name, String descriptor, boolean isInterface) {\n        String line = String.format(\"    MethodVisitor.visitMethodInsn(%s, %s, %s, %s, %s);\", Printer.OPCODES[opcode], owner, name, descriptor, isInterface);\n        System.out.println(line);\n        super.visitMethodInsn(opcode, owner, name, descriptor, isInterface);\n    }\n\n    @Override\n    public void visitJumpInsn(int opcode, Label label) {\n        String line = String.format(\"    MethodVisitor.visitJumpInsn(%s, %s);\", Printer.OPCODES[opcode], label);\n        System.out.println(line);\n        super.visitJumpInsn(opcode, label);\n    }\n\n    @Override\n    public void visitLabel(Label label) {\n        String line = String.format(\"    MethodVisitor.visitLabel(%s);\", label);\n        System.out.println(line);\n        super.visitLabel(label);\n    }\n\n    @Override\n    public void visitLdcInsn(Object value) {\n        String line = String.format(\"    MethodVisitor.visitLdcInsn(%s);\", value);\n        System.out.println(line);\n        super.visitLdcInsn(value);\n    }\n\n    @Override\n    public void visitIincInsn(int var, int increment) {\n        String line = String.format(\"    MethodVisitor.visitIincInsn(%s, %s);\", var, increment);\n        System.out.println(line);\n        super.visitIincInsn(var, increment);\n    }\n\n    @Override\n    public void visitMaxs(int maxStack, int maxLocals) {\n        String line = String.format(\"    MethodVisitor.visitMaxs(%s, %s);\", maxStack, maxLocals);\n        System.out.println(line);\n        super.visitMaxs(maxStack, maxLocals);\n    }\n\n    @Override\n    public void visitEnd() {\n        String line = String.format(\"    MethodVisitor.visitEnd();\");\n        System.out.println(line);\n        super.visitEnd();\n    }\n}\n"
  },
  {
    "path": "src/main/java/lsieun/asm/core/timer/ClassTimerVisitor.java",
    "content": "package lsieun.asm.core.timer;\n\nimport org.objectweb.asm.ClassVisitor;\nimport org.objectweb.asm.FieldVisitor;\nimport org.objectweb.asm.MethodVisitor;\n\nimport static org.objectweb.asm.Opcodes.*;\n\npublic class ClassTimerVisitor extends ClassVisitor {\n    private String owner;\n    private boolean isInterface;\n\n    public ClassTimerVisitor(int api, ClassVisitor cv) {\n        super(api, cv);\n    }\n\n    @Override\n    public void visit(int version, int access, String name, String signature, String superName, String[] interfaces) {\n        super.visit(version, access, name, signature, superName, interfaces);\n        owner = name;\n        isInterface = (access & ACC_INTERFACE) != 0;\n    }\n\n    @Override\n    public MethodVisitor visitMethod(int access, String name, String descriptor, String signature, String[] exceptions) {\n        MethodVisitor mv = super.visitMethod(access, name, descriptor, signature, exceptions);\n        if (!isInterface && mv != null && !name.equals(\"<init>\")) {\n            mv = new MethodTimerAdapter(api, mv, owner);\n        }\n        return mv;\n    }\n\n    @Override\n    public void visitEnd() {\n        if (!isInterface) {\n            FieldVisitor fv = super.visitField(ACC_PUBLIC | ACC_STATIC, \"timer\", \"J\", null, null);\n            if (fv != null) {\n                fv.visitEnd();\n            }\n        }\n        super.visitEnd();\n    }\n}\n"
  },
  {
    "path": "src/main/java/lsieun/asm/core/timer/MethodTimerAdapter.java",
    "content": "package lsieun.asm.core.timer;\n\nimport org.objectweb.asm.MethodVisitor;\n\nimport static org.objectweb.asm.Opcodes.*;\n\npublic class MethodTimerAdapter extends MethodVisitor {\n    private final String owner;\n\n    public MethodTimerAdapter(int api, MethodVisitor mv, String owner) {\n        super(api, mv);\n        this.owner = owner;\n    }\n\n    @Override\n    public void visitCode() {\n        super.visitCode();\n        super.visitFieldInsn(GETSTATIC, owner, \"timer\", \"J\");\n        super.visitMethodInsn(INVOKESTATIC, \"java/lang/System\", \"currentTimeMillis\", \"()J\", false);\n        super.visitInsn(LSUB);\n        super.visitFieldInsn(PUTSTATIC, owner, \"timer\", \"J\");\n    }\n\n    @Override\n    public void visitInsn(int opcode) {\n        if ((opcode >= IRETURN && opcode <= RETURN) || opcode == ATHROW) {\n            super.visitFieldInsn(GETSTATIC, owner, \"timer\", \"J\");\n            super.visitMethodInsn(INVOKESTATIC, \"java/lang/System\", \"currentTimeMillis\", \"()J\", false);\n            super.visitInsn(LADD);\n            super.visitFieldInsn(PUTSTATIC, owner, \"timer\", \"J\");\n        }\n        super.visitInsn(opcode);\n    }\n}\n"
  },
  {
    "path": "src/main/java/lsieun/asm/template/CheckMethodAnnotationVisitor.java",
    "content": "package lsieun.asm.template;\n\nimport org.objectweb.asm.AnnotationVisitor;\nimport org.objectweb.asm.ClassVisitor;\nimport org.objectweb.asm.MethodVisitor;\n\nimport java.util.ArrayList;\nimport java.util.List;\n\npublic class CheckMethodAnnotationVisitor extends ClassVisitor {\n    // 需要处理的方法放到这里\n    public List<String> result = new ArrayList<>();\n\n    public CheckMethodAnnotationVisitor(int api, ClassVisitor classVisitor) {\n        super(api, classVisitor);\n    }\n\n    @Override\n    public MethodVisitor visitMethod(int access, String name, String descriptor, String signature, String[] exceptions) {\n        MethodVisitor mv = super.visitMethod(access, name, descriptor, signature, exceptions);\n        mv = new CheckMethodAnnotationAdapter(api, mv, name, descriptor);\n        return mv;\n    }\n\n    private class CheckMethodAnnotationAdapter extends MethodVisitor {\n        private final String methodName;\n        private final String methodDesc;\n\n        public CheckMethodAnnotationAdapter(int api, MethodVisitor methodVisitor, String methodName, String methodDesc) {\n            super(api, methodVisitor);\n            this.methodName = methodName;\n            this.methodDesc = methodDesc;\n        }\n\n        @Override\n        public AnnotationVisitor visitAnnotation(String descriptor, boolean visible) {\n            // 在这里进行判断：是否需要对方法进行处理\n            if (descriptor.equals(\"Lsample/MyTag;\")) {\n                String item = methodName + \":\" + methodDesc;\n                result.add(item);\n            }\n            return super.visitAnnotation(descriptor, visible);\n        }\n    }\n}\n"
  },
  {
    "path": "src/main/java/lsieun/asm/template/ClassAddAnnotationVisitor.java",
    "content": "package lsieun.asm.template;\n\nimport org.objectweb.asm.*;\n\npublic class ClassAddAnnotationVisitor extends ClassVisitor {\n    private final String annotationDesc;\n    private boolean isAnnotationPresent;\n\n    public ClassAddAnnotationVisitor(int api, ClassVisitor classVisitor, String annotationDesc) {\n        super(api, classVisitor);\n        this.annotationDesc = annotationDesc;\n    }\n\n    @Override\n    public AnnotationVisitor visitAnnotation(String descriptor, boolean visible) {\n        if (visible && descriptor.equals(annotationDesc)) {\n            isAnnotationPresent = true;\n        }\n        return super.visitAnnotation(descriptor, visible);\n    }\n\n    @Override\n    public void visitNestMember(String nestMember) {\n        addAnnotation();\n        super.visitNestMember(nestMember);\n    }\n\n    @Override\n    public void visitInnerClass(String name, String outerName, String innerName, int access) {\n        addAnnotation();\n        super.visitInnerClass(name, outerName, innerName, access);\n    }\n\n    @Override\n    public RecordComponentVisitor visitRecordComponent(String name, String descriptor, String signature) {\n        addAnnotation();\n        return super.visitRecordComponent(name, descriptor, signature);\n    }\n\n    @Override\n    public FieldVisitor visitField(int access, String name, String descriptor, String signature, Object value) {\n        addAnnotation();\n        return super.visitField(access, name, descriptor, signature, value);\n    }\n\n    @Override\n    public MethodVisitor visitMethod(int access, String name, String descriptor, String signature, String[] exceptions) {\n        addAnnotation();\n        return super.visitMethod(access, name, descriptor, signature, exceptions);\n    }\n\n    @Override\n    public void visitEnd() {\n        addAnnotation();\n        super.visitEnd();\n    }\n\n    private void addAnnotation() {\n        if (!isAnnotationPresent) {\n            AnnotationVisitor av = super.visitAnnotation(annotationDesc, true);\n            if (av != null) {\n                av.visitEnd();\n            }\n            isAnnotationPresent = true;\n        }\n    }\n}\n"
  },
  {
    "path": "src/main/java/lsieun/asm/template/ClassAddCustomAttributeVisitor.java",
    "content": "package lsieun.asm.template;\n\nimport lsieun.utils.ByteUtils;\nimport org.objectweb.asm.*;\n\npublic class ClassAddCustomAttributeVisitor extends ClassVisitor {\n    private final String attrName;\n    private final String attrContent;\n    private boolean isAttrPresent;\n\n    public ClassAddCustomAttributeVisitor(int api, ClassVisitor classVisitor, String attrName, String attrContent) {\n        super(api, classVisitor);\n        this.attrName = attrName;\n        this.attrContent = attrContent;\n        this.isAttrPresent = false;\n    }\n\n    @Override\n    public void visitAttribute(Attribute attribute) {\n        if (attribute.type.equals(attrName)) {\n            isAttrPresent = true;\n        }\n        super.visitAttribute(attribute);\n    }\n\n    @Override\n    public void visitNestMember(String nestMember) {\n        addAttribute();\n        super.visitNestMember(nestMember);\n    }\n\n    @Override\n    public void visitInnerClass(String name, String outerName, String innerName, int access) {\n        addAttribute();\n        super.visitInnerClass(name, outerName, innerName, access);\n    }\n\n    @Override\n    public RecordComponentVisitor visitRecordComponent(String name, String descriptor, String signature) {\n        addAttribute();\n        return super.visitRecordComponent(name, descriptor, signature);\n    }\n\n    @Override\n    public FieldVisitor visitField(int access, String name, String descriptor, String signature, Object value) {\n        addAttribute();\n        return super.visitField(access, name, descriptor, signature, value);\n    }\n\n    @Override\n    public MethodVisitor visitMethod(int access, String name, String descriptor, String signature, String[] exceptions) {\n        addAttribute();\n        return super.visitMethod(access, name, descriptor, signature, exceptions);\n    }\n\n    @Override\n    public void visitEnd() {\n        addAttribute();\n        super.visitEnd();\n    }\n\n    private void addAttribute() {\n        if (!isAttrPresent) {\n            int hashCode = attrContent.hashCode();\n            byte[] info = ByteUtils.intToByteArray(hashCode);\n            Attribute attr = new CustomAttribute(attrName, info);\n            super.visitAttribute(attr);\n            isAttrPresent = true;\n        }\n    }\n}\n"
  },
  {
    "path": "src/main/java/lsieun/asm/template/ClassAddFieldVisitor.java",
    "content": "package lsieun.asm.template;\n\nimport org.objectweb.asm.ClassVisitor;\nimport org.objectweb.asm.FieldVisitor;\n\npublic class ClassAddFieldVisitor extends ClassVisitor {\n    private final int fieldAccess;\n    private final String fieldName;\n    private final String fieldDesc;\n    private boolean isFieldPresent;\n\n    public ClassAddFieldVisitor(int api, ClassVisitor classVisitor, int fieldAccess, String fieldName, String fieldDesc) {\n        super(api, classVisitor);\n        this.fieldAccess = fieldAccess;\n        this.fieldName = fieldName;\n        this.fieldDesc = fieldDesc;\n    }\n\n    @Override\n    public FieldVisitor visitField(int access, String name, String descriptor, String signature, Object value) {\n        if (name.equals(fieldName)) {\n            isFieldPresent = true;\n        }\n        return super.visitField(access, name, descriptor, signature, value);\n    }\n\n    @Override\n    public void visitEnd() {\n        if (!isFieldPresent) {\n            FieldVisitor fv = super.visitField(fieldAccess, fieldName, fieldDesc, null, null);\n            if (fv != null) {\n                fv.visitEnd();\n            }\n        }\n        super.visitEnd();\n    }\n}\n"
  },
  {
    "path": "src/main/java/lsieun/asm/template/ClassAddInterfaceVisitor.java",
    "content": "package lsieun.asm.template;\n\nimport org.objectweb.asm.ClassVisitor;\n\nimport java.util.Arrays;\nimport java.util.HashSet;\nimport java.util.Set;\n\npublic class ClassAddInterfaceVisitor extends ClassVisitor {\n    private final String[] newInterfaces;\n\n    public ClassAddInterfaceVisitor(int api, ClassVisitor cv, String[] newInterfaces) {\n        super(api, cv);\n        this.newInterfaces = newInterfaces;\n    }\n\n    @Override\n    public void visit(int version, int access, String name, String signature, String superName, String[] interfaces) {\n        Set<String> set = new HashSet<>(); // 注意，这里使用Set是为了避免出现重复接口\n        if (interfaces != null) {\n            set.addAll(Arrays.asList(interfaces));\n        }\n        if (newInterfaces != null) {\n            set.addAll(Arrays.asList(newInterfaces));\n        }\n        super.visit(version, access, name, signature, superName, set.toArray(new String[0]));\n    }\n}\n"
  },
  {
    "path": "src/main/java/lsieun/asm/template/ClassAddMethodVisitor.java",
    "content": "package lsieun.asm.template;\n\nimport org.objectweb.asm.ClassVisitor;\nimport org.objectweb.asm.MethodVisitor;\n\npublic abstract class ClassAddMethodVisitor extends ClassVisitor {\n    private final int methodAccess;\n    private final String methodName;\n    private final String methodDesc;\n    private final String methodSignature;\n    private final String[] methodExceptions;\n    private boolean isMethodPresent;\n\n    public ClassAddMethodVisitor(int api, ClassVisitor cv, int methodAccess, String methodName, String methodDesc,\n                                 String signature, String[] exceptions) {\n        super(api, cv);\n        this.methodAccess = methodAccess;\n        this.methodName = methodName;\n        this.methodDesc = methodDesc;\n        this.methodSignature = signature;\n        this.methodExceptions = exceptions;\n        this.isMethodPresent = false;\n    }\n\n    @Override\n    public MethodVisitor visitMethod(int access, String name, String descriptor, String signature, String[] exceptions) {\n        if (name.equals(methodName) && descriptor.equals(methodDesc)) {\n            isMethodPresent = true;\n        }\n        return super.visitMethod(access, name, descriptor, signature, exceptions);\n    }\n\n    @Override\n    public void visitEnd() {\n        if (!isMethodPresent) {\n            MethodVisitor mv = super.visitMethod(methodAccess, methodName, methodDesc, methodSignature, methodExceptions);\n            if (mv != null) {\n                // create method body\n                generateMethodBody(mv);\n            }\n        }\n\n        super.visitEnd();\n    }\n\n    protected abstract void generateMethodBody(MethodVisitor mv);\n}\n"
  },
  {
    "path": "src/main/java/lsieun/asm/template/ClassGetAttributeContentVisitor.java",
    "content": "package lsieun.asm.template;\n\nimport org.objectweb.asm.ClassVisitor;\nimport org.objectweb.asm.FieldVisitor;\nimport org.objectweb.asm.MethodVisitor;\n\npublic class ClassGetAttributeContentVisitor extends ClassVisitor {\n    private final StringBuilder attr = new StringBuilder();\n\n    public ClassGetAttributeContentVisitor(int api, ClassVisitor classVisitor) {\n        super(api, classVisitor);\n    }\n\n    @Override\n    public void visit(int version, int access, String name, String signature, String superName, String[] interfaces) {\n        attr.append(name);\n        super.visit(version, access, name, signature, superName, interfaces);\n    }\n\n    @Override\n    public FieldVisitor visitField(int access, String name, String descriptor, String signature, Object value) {\n        attr.append(name);\n        return super.visitField(access, name, descriptor, signature, value);\n    }\n\n    @Override\n    public MethodVisitor visitMethod(int access, String name, String descriptor, String signature, String[] exceptions) {\n        attr.append(name);\n        return super.visitMethod(access, name, descriptor, signature, exceptions);\n    }\n\n    public String getAttributeContent() {\n        return attr.toString();\n    }\n}\n"
  },
  {
    "path": "src/main/java/lsieun/asm/template/ClassMergeVisitor.java",
    "content": "package lsieun.asm.template;\n\nimport org.objectweb.asm.ClassVisitor;\nimport org.objectweb.asm.tree.ClassNode;\nimport org.objectweb.asm.tree.FieldNode;\nimport org.objectweb.asm.tree.MethodNode;\n\nimport java.util.List;\n\npublic class ClassMergeVisitor extends ClassVisitor {\n    private final ClassNode anotherClass;\n\n    public ClassMergeVisitor(int api, ClassVisitor classVisitor, ClassNode anotherClass) {\n        super(api, classVisitor);\n        this.anotherClass = anotherClass;\n    }\n\n    @Override\n    public void visitEnd() {\n        List<FieldNode> fields = anotherClass.fields;\n        for (FieldNode fn : fields) {\n            fn.accept(this);\n        }\n\n        List<MethodNode> methods = anotherClass.methods;\n        for (MethodNode mn : methods) {\n            String methodName = mn.name;\n            if (\"<init>\".equals(methodName)) {\n                continue;\n            }\n            mn.accept(this);\n        }\n        super.visitEnd();\n    }\n}\n"
  },
  {
    "path": "src/main/java/lsieun/asm/template/ClassMergeVisitorRun.java",
    "content": "package lsieun.asm.template;\n\nimport lsieun.utils.FileUtils;\nimport lsieun.utils.ReadUtils;\nimport org.objectweb.asm.ClassReader;\nimport org.objectweb.asm.ClassVisitor;\nimport org.objectweb.asm.ClassWriter;\nimport org.objectweb.asm.Opcodes;\nimport org.objectweb.asm.tree.ClassNode;\n\nimport java.util.List;\n\npublic class ClassMergeVisitorRun {\n    public static void main(String[] args) {\n        String first_class = \"sample/HelloWorld\";\n        String second_class = \"sample/GoodChild\";\n        String filepath = getFilePath(first_class);\n        byte[] bytes1 = ReadUtils.readByPath(filepath);\n\n        //（1）构建ClassReader\n        ClassReader cr = new ClassReader(bytes1);\n\n        //（2）构建ClassWriter\n        ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_FRAMES);\n\n        //（3）串连ClassVisitor\n        byte[] second_class_bytes = rename(second_class, first_class);\n        ClassNode cn = getClassNode(second_class_bytes);\n        List<String> interface_list = cn.interfaces;\n        int size = interface_list.size();\n        String[] interfaces = new String[size];\n        for (int i = 0; i < size; i++) {\n            String item = interface_list.get(i);\n            interfaces[i] = item;\n        }\n        ClassVisitor cv = new ClassMergeVisitor(Opcodes.ASM9, cw, cn);\n        cv = new ClassAddInterfaceVisitor(Opcodes.ASM9, cv, interfaces);\n\n        //（4）两者进行结合\n        cr.accept(cv, 0);\n\n        //（5）重新生成Class\n        byte[] bytes2 = cw.toByteArray();\n\n        FileUtils.writeBytes(filepath, bytes2);\n    }\n\n    public static ClassNode getClassNode(byte[] bytes) {\n        ClassReader cr = new ClassReader(bytes);\n        ClassNode cn = new ClassNode();\n        cr.accept(cn, 0);\n        return cn;\n    }\n\n    public static byte[] rename(String origin_name, String target_name) {\n        String origin_filepath = getFilePath(origin_name);\n        byte[] bytes1 = ReadUtils.readByPath(origin_filepath);\n\n        //（1）构建ClassReader\n        ClassReader cr = new ClassReader(bytes1);\n\n        //（2）构建ClassWriter\n        ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_FRAMES);\n\n        //（3）串连ClassVisitor\n        ClassVisitor cv = new ClassRenameAdapter(Opcodes.ASM9, cw, origin_name, target_name);\n\n        //（4）两者进行结合\n        cr.accept(cv, 0);\n\n        //（5）重新生成Class\n        return cw.toByteArray();\n    }\n\n    public static String getFilePath(String internalName) {\n        String relative_path = String.format(\"%s.class\", internalName);\n        return FileUtils.getFilePath(relative_path);\n    }\n}\n"
  },
  {
    "path": "src/main/java/lsieun/asm/template/ClassPrintAnnotationVisitor.java",
    "content": "package lsieun.asm.template;\n\nimport org.objectweb.asm.AnnotationVisitor;\nimport org.objectweb.asm.ClassVisitor;\nimport org.objectweb.asm.MethodVisitor;\n\n// 第一个类\npublic class ClassPrintAnnotationVisitor extends ClassVisitor {\n    private String owner;\n\n    public ClassPrintAnnotationVisitor(int api, ClassVisitor classVisitor) {\n        super(api, classVisitor);\n    }\n\n    @Override\n    public void visit(int version, int access, String name, String signature, String superName, String[] interfaces) {\n        super.visit(version, access, name, signature, superName, interfaces);\n        this.owner = name;\n    }\n\n    @Override\n    public AnnotationVisitor visitAnnotation(String descriptor, boolean visible) {\n        // (1) 调用父类的实现\n        AnnotationVisitor av = super.visitAnnotation(descriptor, visible);\n\n        // (2) 添加自己的代码逻辑\n        String info = String.format(\"Class: %s - %s\", owner, descriptor);\n        System.out.println(info);\n        return new AnnotationPrinter(api, av);\n    }\n\n    @Override\n    public MethodVisitor visitMethod(int access, String name, String descriptor, String signature, String[] exceptions) {\n        MethodVisitor mv = super.visitMethod(access, name, descriptor, signature, exceptions);\n        mv = new MethodPrintAnnotationAdapter(api, mv, owner, name, descriptor);\n        return mv;\n    }\n\n    // 第二个类\n    private static class MethodPrintAnnotationAdapter extends MethodVisitor {\n        private final String owner;\n        private final String methodName;\n        private final String methodDesc;\n\n\n        public MethodPrintAnnotationAdapter(int api, MethodVisitor methodVisitor, String owner, String methodName, String methodDesc) {\n            super(api, methodVisitor);\n            this.owner = owner;\n            this.methodName = methodName;\n            this.methodDesc = methodDesc;\n        }\n\n        @Override\n        public AnnotationVisitor visitAnnotation(String descriptor, boolean visible) {\n            // (1) 调用父类的实现\n            AnnotationVisitor av = super.visitAnnotation(descriptor, visible);\n            \n            // (2) 添加自己的代码逻辑\n            String info = String.format(\"Method: %s.%s:%s - %s\", owner, methodName, methodDesc, descriptor);\n            System.out.println(info);\n            return new AnnotationPrinter(api, av);\n        }\n    }\n\n    // 第三个类\n    private static class AnnotationPrinter extends AnnotationVisitor {\n        public AnnotationPrinter(int api, AnnotationVisitor annotationVisitor) {\n            super(api, annotationVisitor);\n        }\n\n        @Override\n        public void visit(String name, Object value) {\n            // (1) 添加自己的代码逻辑\n            String info = String.format(\"    %s: %s\", name, value);\n            System.out.println(info);\n\n            // (2) 调用父类的实现\n            super.visit(name, value);\n        }\n\n        @Override\n        public void visitEnum(String name, String descriptor, String value) {\n            // (1) 添加自己的代码逻辑\n            String info = String.format(\"    %s: %s %s\", name, descriptor, value);\n            System.out.println(info);\n\n            // (2) 调用父类的实现\n            super.visitEnum(name, descriptor, value);\n        }\n\n        @Override\n        public AnnotationVisitor visitAnnotation(String name, String descriptor) {\n            // (1) 添加自己的代码逻辑\n            String info = String.format(\"    %s: %s\", name, descriptor);\n            System.out.println(info);\n\n            // (2) 调用父类的实现\n            return super.visitAnnotation(name, descriptor);\n        }\n\n        @Override\n        public AnnotationVisitor visitArray(String name) {\n            // (1) 添加自己的代码逻辑\n            String info = String.format(\"    %s\", name);\n            System.out.println(info);\n\n            // (2) 调用父类的实现\n            return super.visitArray(name);\n        }\n    }\n}\n"
  },
  {
    "path": "src/main/java/lsieun/asm/template/ClassPrintParameterVisitor.java",
    "content": "package lsieun.asm.template;\n\nimport org.objectweb.asm.ClassVisitor;\nimport org.objectweb.asm.MethodVisitor;\nimport org.objectweb.asm.Type;\nimport org.objectweb.asm.commons.AdviceAdapter;\n\nimport static org.objectweb.asm.Opcodes.*;\n\npublic class ClassPrintParameterVisitor extends ClassVisitor {\n    public ClassPrintParameterVisitor(int api, ClassVisitor classVisitor) {\n        super(api, classVisitor);\n    }\n\n    @Override\n    public MethodVisitor visitMethod(int access, String name, String descriptor, String signature, String[] exceptions) {\n        MethodVisitor mv = super.visitMethod(access, name, descriptor, signature, exceptions);\n        if (mv != null) {\n            boolean isAbstractMethod = (access & ACC_ABSTRACT) != 0;\n            boolean isNativeMethod = (access & ACC_NATIVE) != 0;\n            if (!isAbstractMethod && !isNativeMethod) {\n                mv = new MethodPrintParameterAdapter(api, mv, access, name, descriptor);\n            }\n        }\n        return mv;\n    }\n\n    public static class MethodPrintParameterAdapter extends AdviceAdapter {\n        public MethodPrintParameterAdapter(int api, MethodVisitor mv, int access, String name, String descriptor) {\n            super(api, mv, access, name, descriptor);\n        }\n\n        @Override\n        protected void onMethodEnter() {\n            printMessage(\"Method Enter: \" + getName() + methodDesc);\n\n            Type[] argumentTypes = getArgumentTypes();\n            for (int i = 0; i < argumentTypes.length; i++) {\n                Type t = argumentTypes[i];\n                loadArg(i);\n                box(t);\n                printValueOnStack(\"(Ljava/lang/Object;)V\");\n            }\n        }\n\n        @Override\n        protected void onMethodExit(int opcode) {\n            printMessage(\"Method Exit: \" + getName() + methodDesc);\n\n            if (opcode == ATHROW) {\n                super.visitLdcInsn(\"abnormal return\");\n            }\n            else if (opcode == RETURN) {\n                super.visitLdcInsn(\"return void\");\n            }\n            else if (opcode == ARETURN) {\n                dup();\n            }\n            else {\n                if (opcode == LRETURN || opcode == DRETURN) {\n                    dup2();\n                }\n                else {\n                    dup();\n                }\n                box(getReturnType());\n            }\n            printValueOnStack(\"(Ljava/lang/Object;)V\");\n        }\n\n        private void printMessage(String str) {\n            super.visitLdcInsn(str);\n            super.visitMethodInsn(INVOKESTATIC, \"sample/ParameterUtils\", \"printText\", \"(Ljava/lang/String;)V\", false);\n        }\n\n        private void printValueOnStack(String descriptor) {\n            super.visitMethodInsn(INVOKESTATIC, \"sample/ParameterUtils\", \"printValueOnStack\", descriptor, false);\n        }\n    }\n}\n"
  },
  {
    "path": "src/main/java/lsieun/asm/template/ClassRemoveFieldVisitor.java",
    "content": "package lsieun.asm.template;\n\nimport org.objectweb.asm.ClassVisitor;\nimport org.objectweb.asm.FieldVisitor;\n\npublic class ClassRemoveFieldVisitor extends ClassVisitor {\n    private final String fieldName;\n    private final String fieldDesc;\n\n    public ClassRemoveFieldVisitor(int api, ClassVisitor cv, String fieldName, String fieldDesc) {\n        super(api, cv);\n        this.fieldName = fieldName;\n        this.fieldDesc = fieldDesc;\n    }\n\n    @Override\n    public FieldVisitor visitField(int access, String name, String descriptor, String signature, Object value) {\n        if (name.equals(fieldName) && descriptor.equals(fieldDesc)) {\n            return null;\n        }\n        else {\n            return super.visitField(access, name, descriptor, signature, value);\n        }\n    }\n}\n"
  },
  {
    "path": "src/main/java/lsieun/asm/template/ClassRemoveMethodVisitor.java",
    "content": "package lsieun.asm.template;\n\nimport org.objectweb.asm.ClassVisitor;\nimport org.objectweb.asm.MethodVisitor;\n\npublic class ClassRemoveMethodVisitor extends ClassVisitor {\n    private final String methodName;\n    private final String methodDesc;\n\n    public ClassRemoveMethodVisitor(int api, ClassVisitor cv, String methodName, String methodDesc) {\n        super(api, cv);\n        this.methodName = methodName;\n        this.methodDesc = methodDesc;\n    }\n\n    @Override\n    public MethodVisitor visitMethod(int access, String name, String descriptor, String signature, String[] exceptions) {\n        if (name.equals(methodName) && descriptor.equals(methodDesc)) {\n            return null;\n        }\n        else {\n            return super.visitMethod(access, name, descriptor, signature, exceptions);\n        }\n    }\n}\n"
  },
  {
    "path": "src/main/java/lsieun/asm/template/ClassRenameAdapter.java",
    "content": "package lsieun.asm.template;\n\nimport org.objectweb.asm.ClassVisitor;\nimport org.objectweb.asm.FieldVisitor;\nimport org.objectweb.asm.MethodVisitor;\n\npublic class ClassRenameAdapter extends ClassVisitor {\n    private final String oldOwner;\n    private final String newOwner;\n\n    public ClassRenameAdapter(int api, ClassVisitor classVisitor, String oldOwner, String newOwner) {\n        super(api, classVisitor);\n        this.oldOwner = oldOwner;\n        this.newOwner = newOwner;\n    }\n\n    @Override\n    public void visit(int version, int access, String name, String signature, String superName, String[] interfaces) {\n        if (name.equals(oldOwner)) {\n            super.visit(version, access, newOwner, signature, superName, interfaces);\n        }\n        else {\n            super.visit(version, access, name, signature, superName, interfaces);\n        }\n    }\n\n    @Override\n    public FieldVisitor visitField(int access, String name, String descriptor, String signature, Object value) {\n        String oldDesc = getDescriptor(oldOwner);\n        if (descriptor.contains(oldDesc)) {\n            String newDesc = getDescriptor(newOwner);\n            String desc = descriptor.replaceAll(oldDesc, newDesc);\n            return super.visitField(access, name, desc, signature, value);\n        }\n        return super.visitField(access, name, descriptor, signature, value);\n    }\n\n    @Override\n    public MethodVisitor visitMethod(int access, String name, String descriptor, String signature, String[] exceptions) {\n        String desc = descriptor;\n        String oldDesc = getDescriptor(oldOwner);\n        if (descriptor.contains(oldDesc)) {\n            String newDesc = getDescriptor(newOwner);\n            desc = descriptor.replaceAll(oldDesc, newDesc);\n        }\n        MethodVisitor mv = super.visitMethod(access, name, desc, signature, exceptions);\n        if (mv != null) {\n            mv = new RefRenameAdapter(api, mv, oldOwner, newOwner);\n        }\n        return mv;\n    }\n\n    public String getDescriptor(String internalName) {\n        return String.format(\"L%s;\", internalName);\n    }\n}\n"
  },
  {
    "path": "src/main/java/lsieun/asm/template/ClassRenameAdapterRun.java",
    "content": "package lsieun.asm.template;\n\nimport lsieun.utils.FileUtils;\nimport lsieun.utils.ReadUtils;\nimport org.objectweb.asm.ClassReader;\nimport org.objectweb.asm.ClassVisitor;\nimport org.objectweb.asm.ClassWriter;\nimport org.objectweb.asm.Opcodes;\n\npublic class ClassRenameAdapterRun {\n    public static void main(String[] args) {\n        String origin_name = \"sample/HelloWorld\";\n        String target_name = \"sample/GoodChild\";\n        String origin_filepath = getFilePath(origin_name);\n        byte[] bytes1 = ReadUtils.readByPath(origin_filepath);\n\n        //（1）构建ClassReader\n        ClassReader cr = new ClassReader(bytes1);\n\n        //（2）构建ClassWriter\n        ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_FRAMES);\n\n        //（3）串连ClassVisitor\n        ClassVisitor cv = new ClassRenameAdapter(Opcodes.ASM9, cw, origin_name, target_name);\n\n        //（4）两者进行结合\n        cr.accept(cv, 0);\n\n        //（5）重新生成Class\n        byte[] bytes2 = cw.toByteArray();\n\n        String target_filepath = getFilePath(target_name);\n        FileUtils.writeBytes(target_filepath, bytes2);\n    }\n\n    public static String getFilePath(String internalName) {\n        String relative_path = String.format(\"%s.class\", internalName);\n        return FileUtils.getFilePath(relative_path);\n    }\n}\n"
  },
  {
    "path": "src/main/java/lsieun/asm/template/ClassReplaceMethodBodyVisitor.java",
    "content": "package lsieun.asm.template;\n\nimport org.objectweb.asm.ClassVisitor;\nimport org.objectweb.asm.MethodVisitor;\n\npublic abstract class ClassReplaceMethodBodyVisitor extends ClassVisitor {\n    protected String owner;\n    protected final String methodName;\n    protected final String methodDesc;\n    private final boolean keepOriginalMethod;\n\n    public ClassReplaceMethodBodyVisitor(int api, ClassVisitor cv, String methodName, String methodDesc) {\n        this(api, cv, methodName, methodDesc, true);\n    }\n\n    public ClassReplaceMethodBodyVisitor(int api, ClassVisitor cv, String methodName, String methodDesc, boolean keepOriginalMethod) {\n        super(api, cv);\n        this.methodName = methodName;\n        this.methodDesc = methodDesc;\n        this.keepOriginalMethod = keepOriginalMethod;\n    }\n\n    @Override\n    public void visit(int version, int access, String name, String signature, String superName, String[] interfaces) {\n        super.visit(version, access, name, signature, superName, interfaces);\n        this.owner = name;\n    }\n\n    @Override\n    public MethodVisitor visitMethod(int access, String name, String descriptor, String signature, String[] exceptions) {\n        if (name.equals(methodName) && descriptor.equals(methodDesc)) {\n            // 生成新方法（从抽象逻辑上来说，这是第二步；从代码角度来说，先执行）\n            generateNewMethod(access, name, descriptor, signature, exceptions);\n\n            if (keepOriginalMethod) {\n                // 修改原来方法的名字（从抽象逻辑上来说，这是第一步；从代码角度来说，后执行）\n                String newName = getNewName(name);\n                return super.visitMethod(access, newName, descriptor, signature, exceptions);\n            }\n            else {\n                // 删除原来的方法\n                return null;\n            }\n\n        }\n        return super.visitMethod(access, name, descriptor, signature, exceptions);\n    }\n\n    protected String getNewName(String name) {\n        return String.format(\"orig$%s\", name);\n    }\n\n    private void generateNewMethod(int access, String name, String descriptor, String signature, String[] exceptions) {\n        MethodVisitor mv = super.visitMethod(access, name, descriptor, signature, exceptions);\n        if (mv != null) {\n            generateMethodBody(mv);\n        }\n    }\n\n    protected abstract void generateMethodBody(MethodVisitor mv);\n}\n"
  },
  {
    "path": "src/main/java/lsieun/asm/template/CustomAttribute.java",
    "content": "package lsieun.asm.template;\n\nimport lsieun.utils.ByteUtils;\nimport lsieun.utils.HexFormat;\nimport lsieun.utils.HexUtils;\nimport org.objectweb.asm.*;\n\npublic class CustomAttribute extends Attribute {\n    private static final byte[] CODE_BLOB_BYTE_ARRAY = new byte[]{\n            (byte) 0xC0, (byte) 0xDE, (byte) 0xB1, 0x0B\n    };\n    private static final int CODE_BLOB_INT_VALUE = 0xC0DEB10B;\n\n    private final byte[] info;\n\n    public CustomAttribute(String type, byte[] info) {\n        super(type);\n        this.info = info;\n    }\n\n    @Override\n    protected Attribute read(ClassReader classReader, int offset, int length, char[] charBuffer, int codeAttributeOffset, Label[] labels) {\n        int magic = classReader.readInt(offset);\n        if (magic != CODE_BLOB_INT_VALUE) {\n            throw new RuntimeException(\"magic is not right! expected: \" + CODE_BLOB_INT_VALUE + \", actual: \" + magic);\n        }\n        int value = classReader.readInt(offset + 4);\n        byte[] info = ByteUtils.intToByteArray(value);\n        return new CustomAttribute(CustomAttribute.class.getSimpleName(), info);\n    }\n\n    @Override\n    protected ByteVector write(ClassWriter classWriter, byte[] code, int codeLength, int maxStack, int maxLocals) {\n        ByteVector byteVector = new ByteVector();\n        byteVector.putByteArray(CODE_BLOB_BYTE_ARRAY, 0, CODE_BLOB_BYTE_ARRAY.length);\n        byteVector.putByteArray(info, 0, info.length);\n        return byteVector;\n    }\n\n    @Override\n    public String toString() {\n        return String.format(\"%s {name='%s'}\", type, HexUtils.format(info, HexFormat.FORMAT_FF_FF));\n    }\n}\n"
  },
  {
    "path": "src/main/java/lsieun/asm/template/FieldAccessAdapter.java",
    "content": "package lsieun.asm.template;\n\nimport org.objectweb.asm.ClassVisitor;\nimport org.objectweb.asm.MethodVisitor;\n\nimport java.util.List;\n\npublic class FieldAccessAdapter extends ClassVisitor {\n    private final List<Info> list;\n\n    public FieldAccessAdapter(int api, ClassVisitor cv, List<Info> list) {\n        super(api, cv);\n        this.list = list;\n    }\n\n    @Override\n    public MethodVisitor visitMethod(int access, String name, String descriptor, String signature, String[] exceptions) {\n        MethodVisitor mv = super.visitMethod(access, name, descriptor, signature, exceptions);\n        if (mv != null) {\n            mv = new FieldAccessConverter(api, mv, list);\n        }\n        return mv;\n    }\n}\n"
  },
  {
    "path": "src/main/java/lsieun/asm/template/FieldAccessAdapterRun.java",
    "content": "package lsieun.asm.template;\n\nimport lsieun.utils.FileUtils;\nimport lsieun.utils.ReadUtils;\nimport org.objectweb.asm.ClassReader;\nimport org.objectweb.asm.ClassVisitor;\nimport org.objectweb.asm.ClassWriter;\nimport org.objectweb.asm.Opcodes;\n\nimport java.util.ArrayList;\nimport java.util.List;\n\npublic class FieldAccessAdapterRun {\n    public static void main(String[] args) {\n        String relative_path = \"sample/HelloWorld.class\";\n        String filepath = FileUtils.getFilePath(relative_path);\n        byte[] bytes1 = ReadUtils.readByPath(filepath);\n\n        //（1）构建ClassReader\n        ClassReader cr = new ClassReader(bytes1);\n\n        //（2）构建ClassWriter\n        ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_FRAMES);\n\n        //（3）串连ClassVisitor\n        Info info1 = new Info(Opcodes.GETFIELD, \"sample/HelloWorld\", \"intValue\", \"I\",\n                Opcodes.INVOKEVIRTUAL, \"sample/HelloWorld\", \"getHour\", \"()I\");\n        Info info2 = new Info(Opcodes.GETSTATIC, \"sample/HelloWorld\", \"staticValue\", \"I\",\n                Opcodes.INVOKESTATIC, \"sample/GoodChild\", \"getAge\", \"()I\");\n        List<Info> list = new ArrayList<>();\n        list.add(info1);\n        list.add(info2);\n        ClassVisitor cv = new FieldAccessAdapter(Opcodes.ASM9, cw, list);\n\n        //（4）两者进行结合\n        cr.accept(cv, ClassReader.SKIP_FRAMES);\n\n        //（5）重新生成Class\n        byte[] bytes2 = cw.toByteArray();\n\n        FileUtils.writeBytes(filepath, bytes2);\n    }\n}\n"
  },
  {
    "path": "src/main/java/lsieun/asm/template/FieldAccessConverter.java",
    "content": "package lsieun.asm.template;\n\nimport org.objectweb.asm.MethodVisitor;\n\nimport java.util.ArrayList;\nimport java.util.List;\n\npublic class FieldAccessConverter extends MethodVisitor {\n    private final List<Info> list;\n\n    public FieldAccessConverter(int api, MethodVisitor mv, List<Info> list) {\n        super(api, mv);\n        if (list == null) {\n            this.list = new ArrayList<>();\n        }\n        else {\n            this.list = list;\n        }\n    }\n\n    @Override\n    public void visitFieldInsn(int opcode, String owner, String name, String descriptor) {\n        Info info = matchingInfo(opcode, owner, name, descriptor);\n        if (info != null) {\n            super.visitMethodInsn(info.targetOpcode, info.targetOwner, info.targetName, info.targetDesc, false);\n            return;\n        }\n        super.visitFieldInsn(opcode, owner, name, descriptor);\n    }\n\n    private Info matchingInfo(int opcode, String owner, String name, String descriptor) {\n        for (Info info : list) {\n            if (opcode == info.srcOpcode &&\n                    owner.equals(info.srcOwner) &&\n                    name.equals(info.srcName) &&\n                    descriptor.equals(info.srcDesc)) {\n                return info;\n            }\n        }\n        return null;\n    }\n}\n"
  },
  {
    "path": "src/main/java/lsieun/asm/template/Info.java",
    "content": "package lsieun.asm.template;\n\npublic class Info {\n    public final int srcOpcode;\n    public final String srcOwner;\n    public final String srcName;\n    public final String srcDesc;\n\n    public final int targetOpcode;\n    public final String targetOwner;\n    public final String targetName;\n    public final String targetDesc;\n\n    public Info(\n            int srcOpcode, String srcOwner, String srcName, String srcDesc,\n            int targetOpcode, String targetOwner, String targetName, String targetDesc) {\n        this.srcOpcode = srcOpcode;\n        this.srcOwner = srcOwner;\n        this.srcName = srcName;\n        this.srcDesc = srcDesc;\n        this.targetOpcode = targetOpcode;\n        this.targetOwner = targetOwner;\n        this.targetName = targetName;\n        this.targetDesc = targetDesc;\n    }\n}\n"
  },
  {
    "path": "src/main/java/lsieun/asm/template/MethodCallAdapter.java",
    "content": "package lsieun.asm.template;\n\nimport org.objectweb.asm.ClassVisitor;\nimport org.objectweb.asm.MethodVisitor;\n\nimport java.util.List;\n\npublic class MethodCallAdapter extends ClassVisitor {\n    private final List<Info> list;\n\n    public MethodCallAdapter(int api, ClassVisitor cv, List<Info> list) {\n        super(api, cv);\n        this.list = list;\n    }\n\n    @Override\n    public MethodVisitor visitMethod(int access, String name, String descriptor, String signature, String[] exceptions) {\n        MethodVisitor mv = super.visitMethod(access, name, descriptor, signature, exceptions);\n        if (mv != null) {\n            mv = new MethodCallConverter(api, mv, list);\n        }\n        return mv;\n    }\n}\n"
  },
  {
    "path": "src/main/java/lsieun/asm/template/MethodCallAdapterRun.java",
    "content": "package lsieun.asm.template;\n\nimport lsieun.utils.FileUtils;\nimport lsieun.utils.ReadUtils;\nimport org.objectweb.asm.ClassReader;\nimport org.objectweb.asm.ClassVisitor;\nimport org.objectweb.asm.ClassWriter;\nimport org.objectweb.asm.Opcodes;\n\nimport java.util.ArrayList;\nimport java.util.List;\n\npublic class MethodCallAdapterRun {\n    public static void main(String[] args) {\n        String relative_path = \"sample/HelloWorld.class\";\n        String filepath = FileUtils.getFilePath(relative_path);\n        byte[] bytes1 = ReadUtils.readByPath(filepath);\n\n        //（1）构建ClassReader\n        ClassReader cr = new ClassReader(bytes1);\n\n        //（2）构建ClassWriter\n        ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_FRAMES);\n\n        //（3）串连ClassVisitor\n        Info info1 = new Info(Opcodes.INVOKEVIRTUAL, \"sample/HelloWorld\", \"add\", \"(II)I\",\n                Opcodes.INVOKEVIRTUAL, \"sample/HelloWorld\", \"sub\", \"(II)I\");\n        List<Info> list = new ArrayList<>();\n        list.add(info1);\n        ClassVisitor cv = new MethodCallAdapter(Opcodes.ASM9, cw, list);\n\n        //（4）两者进行结合\n        cr.accept(cv, ClassReader.SKIP_FRAMES);\n\n        //（5）重新生成Class\n        byte[] bytes2 = cw.toByteArray();\n\n        FileUtils.writeBytes(filepath, bytes2);\n    }\n}\n"
  },
  {
    "path": "src/main/java/lsieun/asm/template/MethodCallConverter.java",
    "content": "package lsieun.asm.template;\n\nimport org.objectweb.asm.MethodVisitor;\n\nimport java.util.ArrayList;\nimport java.util.List;\n\npublic class MethodCallConverter extends MethodVisitor {\n    private final List<Info> list;\n\n    public MethodCallConverter(int api, MethodVisitor mv, List<Info> list) {\n        super(api, mv);\n        if (list == null) {\n            this.list = new ArrayList<>();\n        }\n        else {\n            this.list = list;\n        }\n    }\n\n    @Override\n    public void visitMethodInsn(int opcode, String owner, String name, String descriptor, boolean isInterface) {\n        Info info = matchingInfo(opcode, owner, name, descriptor);\n        if (info != null) {\n            super.visitMethodInsn(info.targetOpcode, info.targetOwner, info.targetName, info.targetDesc, false);\n            return;\n        }\n        super.visitMethodInsn(opcode, owner, name, descriptor, isInterface);\n    }\n\n    private Info matchingInfo(int opcode, String owner, String name, String descriptor) {\n        for (Info info : list) {\n            if (opcode == info.srcOpcode &&\n                    owner.equals(info.srcOwner) &&\n                    name.equals(info.srcName) &&\n                    descriptor.equals(info.srcDesc)) {\n                return info;\n            }\n        }\n        return null;\n    }\n}\n"
  },
  {
    "path": "src/main/java/lsieun/asm/template/MethodEnteringAdapter.java",
    "content": "package lsieun.asm.template;\n\nimport org.objectweb.asm.MethodVisitor;\nimport org.objectweb.asm.commons.AdviceAdapter;\n\npublic class MethodEnteringAdapter extends AdviceAdapter {\n    private final String methodName;\n\n    public MethodEnteringAdapter(int api, MethodVisitor mv, int access, String name, String descriptor) {\n        super(api, mv, access, name, descriptor);\n        this.methodName = name;\n    }\n\n    @Override\n    protected void onMethodEnter() {\n        super.visitFieldInsn(GETSTATIC, \"java/lang/System\", \"out\", \"Ljava/io/PrintStream;\");\n        super.visitLdcInsn(\"Entering \" + methodName + \" Method\");\n        super.visitMethodInsn(INVOKEVIRTUAL, \"java/io/PrintStream\", \"println\", \"(Ljava/lang/String;)V\", false);\n    }\n}\n"
  },
  {
    "path": "src/main/java/lsieun/asm/template/MethodEnteringVisitor.java",
    "content": "package lsieun.asm.template;\n\nimport org.objectweb.asm.ClassVisitor;\nimport org.objectweb.asm.MethodVisitor;\n\npublic class MethodEnteringVisitor extends ClassVisitor {\n    public MethodEnteringVisitor(int api, ClassVisitor cv) {\n        super(api, cv);\n    }\n\n    @Override\n    public MethodVisitor visitMethod(int access, String name, String descriptor, String signature, String[] exceptions) {\n        MethodVisitor mv = super.visitMethod(access, name, descriptor, signature, exceptions);\n        if (mv != null) {\n            mv = new MethodEnteringAdapter(api, mv, access, name, descriptor);\n        }\n        return mv;\n    }\n}\n"
  },
  {
    "path": "src/main/java/lsieun/asm/template/MethodEnteringVisitorRun.java",
    "content": "package lsieun.asm.template;\n\nimport lsieun.utils.FileUtils;\nimport lsieun.utils.ReadUtils;\nimport org.objectweb.asm.ClassReader;\nimport org.objectweb.asm.ClassVisitor;\nimport org.objectweb.asm.ClassWriter;\nimport org.objectweb.asm.Opcodes;\n\npublic class MethodEnteringVisitorRun {\n    public static void main(String[] args) {\n        String relative_path = \"sample/HelloWorld.class\";\n        String filepath = FileUtils.getFilePath(relative_path);\n        byte[] bytes1 = ReadUtils.readByPath(filepath);\n\n        //（1）构建ClassReader\n        ClassReader cr = new ClassReader(bytes1);\n\n        //（2）构建ClassWriter\n        ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_FRAMES);\n\n        //（3）串连ClassVisitor\n        ClassVisitor cv = new MethodEnteringVisitor(Opcodes.ASM9, cw);\n\n        //（4）两者进行结合\n        cr.accept(cv, 0);\n\n        //（5）重新生成Class\n        byte[] bytes2 = cw.toByteArray();\n\n        FileUtils.writeBytes(filepath, bytes2);\n    }\n}\n"
  },
  {
    "path": "src/main/java/lsieun/asm/template/MethodExitingAdapter.java",
    "content": "package lsieun.asm.template;\n\nimport org.objectweb.asm.ClassVisitor;\nimport org.objectweb.asm.MethodVisitor;\n\npublic class MethodExitingAdapter extends ClassVisitor {\n    public MethodExitingAdapter(int api, ClassVisitor cv) {\n        super(api, cv);\n    }\n\n    @Override\n    public MethodVisitor visitMethod(int access, String name, String descriptor, String signature, String[] exceptions) {\n        MethodVisitor mv = super.visitMethod(access, name, descriptor, signature, exceptions);\n        if (mv != null) {\n            mv = new MethodExitingConverter(api, mv, access, name, descriptor);\n        }\n        return mv;\n    }\n}\n"
  },
  {
    "path": "src/main/java/lsieun/asm/template/MethodExitingAdapterRun.java",
    "content": "package lsieun.asm.template;\n\nimport lsieun.utils.FileUtils;\nimport lsieun.utils.ReadUtils;\nimport org.objectweb.asm.ClassReader;\nimport org.objectweb.asm.ClassVisitor;\nimport org.objectweb.asm.ClassWriter;\nimport org.objectweb.asm.Opcodes;\n\npublic class MethodExitingAdapterRun {\n    public static void main(String[] args) {\n        String relative_path = \"sample/HelloWorld.class\";\n        String filepath = FileUtils.getFilePath(relative_path);\n        byte[] bytes1 = ReadUtils.readByPath(filepath);\n\n        //（1）构建ClassReader\n        ClassReader cr = new ClassReader(bytes1);\n\n        //（2）构建ClassWriter\n        ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_FRAMES);\n\n        //（3）串连ClassVisitor\n        ClassVisitor cv = new MethodExitingAdapter(Opcodes.ASM9, cw);\n\n        //（4）两者进行结合\n        cr.accept(cv, ClassReader.SKIP_FRAMES);\n\n        //（5）重新生成Class\n        byte[] bytes2 = cw.toByteArray();\n\n        FileUtils.writeBytes(filepath, bytes2);\n    }\n}\n"
  },
  {
    "path": "src/main/java/lsieun/asm/template/MethodExitingConverter.java",
    "content": "package lsieun.asm.template;\n\nimport org.objectweb.asm.MethodVisitor;\nimport org.objectweb.asm.Opcodes;\nimport org.objectweb.asm.commons.AdviceAdapter;\n\npublic class MethodExitingConverter extends AdviceAdapter {\n    private final String methodName;\n\n    protected MethodExitingConverter(int api, MethodVisitor mv, int access, String name, String descriptor) {\n        super(api, mv, access, name, descriptor);\n        this.methodName = name;\n    }\n\n    @Override\n    protected void onMethodExit(int opcode) {\n        super.visitFieldInsn(GETSTATIC, \"java/lang/System\", \"out\", \"Ljava/io/PrintStream;\");\n        if (opcode == Opcodes.ATHROW) {\n            super.visitLdcInsn(\"Exiting on exception \" + methodName + \" Method\");\n        }\n        else {\n            super.visitLdcInsn(\"Exiting \" + methodName + \" Method\");\n        }\n\n        super.visitMethodInsn(INVOKEVIRTUAL, \"java/io/PrintStream\", \"println\", \"(Ljava/lang/String;)V\", false);\n    }\n}\n"
  },
  {
    "path": "src/main/java/lsieun/asm/template/MethodFinallyAdapter.java",
    "content": "package lsieun.asm.template;\n\nimport org.objectweb.asm.ClassVisitor;\nimport org.objectweb.asm.MethodVisitor;\n\npublic class MethodFinallyAdapter extends ClassVisitor {\n    public MethodFinallyAdapter(int api, ClassVisitor cv) {\n        super(api, cv);\n    }\n\n    @Override\n    public MethodVisitor visitMethod(int access, String name, String descriptor, String signature, String[] exceptions) {\n        MethodVisitor mv = super.visitMethod(access, name, descriptor, signature, exceptions);\n        if (mv != null && \"main\".equals(name)) {\n            mv = new MethodFinallyConverter(api, mv, access, name, descriptor);\n        }\n        return mv;\n    }\n}\n"
  },
  {
    "path": "src/main/java/lsieun/asm/template/MethodFinallyAdapterRun.java",
    "content": "package lsieun.asm.template;\n\nimport lsieun.utils.FileUtils;\nimport lsieun.utils.ReadUtils;\nimport org.objectweb.asm.ClassReader;\nimport org.objectweb.asm.ClassVisitor;\nimport org.objectweb.asm.ClassWriter;\nimport org.objectweb.asm.Opcodes;\n\npublic class MethodFinallyAdapterRun {\n    public static void main(String[] args) {\n        String relative_path = \"sample/HelloWorld.class\";\n        String filepath = FileUtils.getFilePath(relative_path);\n        byte[] bytes1 = ReadUtils.readByPath(filepath);\n\n        //（1）构建ClassReader\n        ClassReader cr = new ClassReader(bytes1);\n\n        //（2）构建ClassWriter\n        ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_FRAMES);\n\n        //（3）串连ClassVisitor\n        ClassVisitor cv = new MethodFinallyAdapter(Opcodes.ASM9, cw);\n\n        //（4）两者进行结合\n        cr.accept(cv, ClassReader.SKIP_FRAMES);\n\n        //（5）重新生成Class\n        byte[] bytes2 = cw.toByteArray();\n\n        FileUtils.writeBytes(filepath, bytes2);\n    }\n}\n"
  },
  {
    "path": "src/main/java/lsieun/asm/template/MethodFinallyConverter.java",
    "content": "package lsieun.asm.template;\n\nimport org.objectweb.asm.Label;\nimport org.objectweb.asm.MethodVisitor;\nimport org.objectweb.asm.Opcodes;\nimport org.objectweb.asm.commons.AdviceAdapter;\n\npublic class MethodFinallyConverter extends AdviceAdapter {\n    private final String methodName;\n    private final Label tryLabel = new Label();\n    private final Label finallyLabel = new Label();\n\n    protected MethodFinallyConverter(int api, MethodVisitor mv, int access, String name, String descriptor) {\n        super(api, mv, access, name, descriptor);\n        this.methodName = name;\n    }\n\n    @Override\n    public void visitCode() {\n        super.visitCode();\n        super.visitLabel(tryLabel);\n    }\n\n    @Override\n    public void visitMaxs(int maxStack, int maxLocals) {\n\n        super.visitLabel(finallyLabel);\n        super.visitTryCatchBlock(tryLabel, finallyLabel, finallyLabel, null);\n\n        onFinally();\n        super.visitInsn(Opcodes.ATHROW);\n\n        super.visitMaxs(maxStack, maxLocals);\n    }\n\n    @Override\n    protected void onMethodExit(int opcode) {\n        if (opcode != Opcodes.ATHROW) {\n            onFinally();\n        }\n    }\n\n    private void onFinally() {\n        super.visitFieldInsn(GETSTATIC, \"java/lang/System\", \"err\", \"Ljava/io/PrintStream;\");\n        super.visitLdcInsn(\"Exiting \" + methodName + \" Method\");\n        super.visitMethodInsn(INVOKEVIRTUAL, \"java/io/PrintStream\", \"println\", \"(Ljava/lang/String;)V\", false);\n    }\n}\n"
  },
  {
    "path": "src/main/java/lsieun/asm/template/MethodPrintInstructionAdapter.java",
    "content": "package lsieun.asm.template;\n\nimport lsieun.utils.OpcodeConst;\nimport org.objectweb.asm.Handle;\nimport org.objectweb.asm.Label;\nimport org.objectweb.asm.MethodVisitor;\nimport org.objectweb.asm.Type;\n\nimport static org.objectweb.asm.Opcodes.*;\n\npublic class MethodPrintInstructionAdapter extends MethodVisitor {\n    private static final String OPCODE_INSTRUCTION_FORMAT = \"%-15s %-6s\";\n    public static final Type OBJECT_TYPE = Type.getType(\"Ljava/lang/Object;\");\n\n    private final String methodName;\n    private final String methodDesc;\n\n    public MethodPrintInstructionAdapter(int api, MethodVisitor methodVisitor, String methodName, String methodDesc) {\n        super(api, methodVisitor);\n        this.methodName = methodName;\n        this.methodDesc = methodDesc;\n    }\n\n    @Override\n    public void visitCode() {\n        String line = String.format(\"Method: %s:%s\", methodName, methodDesc);\n        printMessage(line);\n\n        super.visitCode();\n    }\n\n    // ILOAD, LLOAD, FLOAD, DLOAD, ALOAD,\n    // ISTORE, LSTORE, FSTORE, DSTORE, ASTORE\n    // or RET.\n    @Override\n    public void visitVarInsn(int opcode, int index) {\n        String instruction = String.format(OPCODE_INSTRUCTION_FORMAT, OpcodeConst.getOpcodeName(opcode), index);\n        printMessage(instruction);\n\n        if (opcode == ILOAD) {\n            super.visitVarInsn(opcode, index);\n            dupAndPrintValueOnStack(Type.INT_TYPE);\n        }\n        else if (opcode == LLOAD) {\n            super.visitVarInsn(opcode, index);\n            dupAndPrintValueOnStack(Type.LONG_TYPE);\n        }\n        else if (opcode == FLOAD) {\n            super.visitVarInsn(opcode, index);\n            dupAndPrintValueOnStack(Type.FLOAT_TYPE);\n        }\n        else if (opcode == DLOAD) {\n            super.visitVarInsn(opcode, index);\n            dupAndPrintValueOnStack(Type.DOUBLE_TYPE);\n        }\n        else if (opcode == ALOAD) {\n            super.visitVarInsn(opcode, index);\n            dupAndPrintValueOnStack(OBJECT_TYPE);\n        }\n        else if (opcode == ISTORE) {\n            dupAndPrintValueOnStack(Type.INT_TYPE);\n            super.visitVarInsn(opcode, index);\n        }\n        else if (opcode == LSTORE) {\n            dupAndPrintValueOnStack(Type.LONG_TYPE);\n            super.visitVarInsn(opcode, index);\n        }\n        else if (opcode == FSTORE) {\n            dupAndPrintValueOnStack(Type.FLOAT_TYPE);\n            super.visitVarInsn(opcode, index);\n        }\n        else if (opcode == DSTORE) {\n            dupAndPrintValueOnStack(Type.DOUBLE_TYPE);\n            super.visitVarInsn(opcode, index);\n        }\n        else if (opcode == ASTORE) {\n            dupAndPrintValueOnStack(OBJECT_TYPE);\n            super.visitVarInsn(opcode, index);\n        }\n        else {\n            super.visitVarInsn(opcode, index);\n            super.visitLdcInsn(\"not supported\");\n            printValueOnStack(OBJECT_TYPE);\n        }\n    }\n\n    @Override\n    public void visitInsn(int opcode) {\n        String instruction = String.format(OPCODE_INSTRUCTION_FORMAT, OpcodeConst.getOpcodeName(opcode), \"\");\n        printMessage(instruction);\n\n        super.visitInsn(opcode);\n    }\n\n    @Override\n    public void visitIincInsn(int var, int increment) {\n        String opcode_arg = String.format(\"%s %s\", var, increment);\n        String instruction = String.format(OPCODE_INSTRUCTION_FORMAT, \"iinc\", opcode_arg);\n        printMessage(instruction);\n\n        super.visitIincInsn(var, increment);\n    }\n\n    @Override\n    public void visitIntInsn(int opcode, int operand) {\n        String opcode_arg = String.valueOf(operand);\n        String instruction = String.format(OPCODE_INSTRUCTION_FORMAT, OpcodeConst.getOpcodeName(opcode), opcode_arg);\n        printMessage(instruction);\n\n        super.visitIntInsn(opcode, operand);\n    }\n\n    @Override\n    public void visitJumpInsn(int opcode, Label label) {\n        String instruction = String.format(OPCODE_INSTRUCTION_FORMAT, OpcodeConst.getOpcodeName(opcode), \"\");\n        printMessage(instruction);\n\n        switch (opcode) {\n            case IFEQ:\n            case IFNE:\n            case IFLT:\n            case IFGE:\n            case IFGT:\n            case IFLE:\n                dupAndPrintValueOnStack(Type.INT_TYPE);\n                break;\n            case IF_ICMPEQ:\n            case IF_ICMPNE:\n            case IF_ICMPLT:\n            case IF_ICMPGE:\n            case IF_ICMPGT:\n            case IF_ICMPLE:\n                super.visitInsn(DUP2);\n                super.visitInsn(POP);\n                printValueOnStack(Type.INT_TYPE);\n                dupAndPrintValueOnStack(Type.INT_TYPE);\n                break;\n            case IF_ACMPEQ:\n            case IF_ACMPNE:\n                super.visitInsn(DUP2);\n                super.visitInsn(POP);\n                printValueOnStack(OBJECT_TYPE);\n                dupAndPrintValueOnStack(OBJECT_TYPE);\n                break;\n            case IFNULL:\n            case IFNONNULL:\n                dupAndPrintValueOnStack(OBJECT_TYPE);\n                break;\n            default:\n                break;\n        }\n\n        super.visitJumpInsn(opcode, label);\n    }\n\n    @Override\n    public void visitLdcInsn(Object value) {\n        String instruction = String.format(OPCODE_INSTRUCTION_FORMAT, \"LDC\", value);\n        printMessage(instruction);\n\n        super.visitLdcInsn(value);\n    }\n\n    @Override\n    public void visitTableSwitchInsn(int min, int max, Label dflt, Label... labels) {\n        String instruction = String.format(OPCODE_INSTRUCTION_FORMAT, \"tableswitch\", \"\");\n        printMessage(instruction);\n\n        super.visitTableSwitchInsn(min, max, dflt, labels);\n    }\n\n    @Override\n    public void visitLookupSwitchInsn(Label dflt, int[] keys, Label[] labels) {\n        String instruction = String.format(OPCODE_INSTRUCTION_FORMAT, \"lookupswitch\", \"\");\n        printMessage(instruction);\n\n        super.visitLookupSwitchInsn(dflt, keys, labels);\n    }\n\n    @Override\n    public void visitMultiANewArrayInsn(String descriptor, int numDimensions) {\n        String opcode_arg = descriptor + \":\" + numDimensions;\n        String instruction = String.format(OPCODE_INSTRUCTION_FORMAT, \"multianewarray\", opcode_arg);\n        printMessage(instruction);\n\n        super.visitMultiANewArrayInsn(descriptor, numDimensions);\n    }\n\n    @Override\n    public void visitFieldInsn(int opcode, String owner, String name, String descriptor) {\n        String opcode_arg = String.format(\"%s.%s:%s\", owner, name, descriptor);\n        String instruction = String.format(OPCODE_INSTRUCTION_FORMAT, OpcodeConst.getOpcodeName(opcode), opcode_arg);\n        printMessage(instruction);\n\n        super.visitFieldInsn(opcode, owner, name, descriptor);\n    }\n\n    @Override\n    public void visitMethodInsn(int opcode, String owner, String name, String descriptor, boolean isInterface) {\n        String opcode_arg = String.format(\"%s.%s:%s\", owner, name, descriptor);\n        String instruction = String.format(OPCODE_INSTRUCTION_FORMAT, OpcodeConst.getOpcodeName(opcode), opcode_arg);\n        printMessage(instruction);\n\n        super.visitMethodInsn(opcode, owner, name, descriptor, isInterface);\n    }\n\n    @Override\n    public void visitInvokeDynamicInsn(String name, String descriptor, Handle bootstrapMethodHandle, Object... bootstrapMethodArguments) {\n        String opcode_arg = String.format(\"%s:%s\", name, descriptor);\n        String instruction = String.format(OPCODE_INSTRUCTION_FORMAT, \"invokedynamic\", opcode_arg);\n        printMessage(instruction);\n\n        super.visitInvokeDynamicInsn(name, descriptor, bootstrapMethodHandle, bootstrapMethodArguments);\n    }\n\n    @Override\n    public void visitTypeInsn(int opcode, String type) {\n        String instruction = String.format(OPCODE_INSTRUCTION_FORMAT, OpcodeConst.getOpcodeName(opcode), type);\n        printMessage(instruction);\n\n        super.visitTypeInsn(opcode, type);\n    }\n\n    private void printMessage(String message) {\n        super.visitFieldInsn(GETSTATIC, \"java/lang/System\", \"out\", \"Ljava/io/PrintStream;\");\n        super.visitLdcInsn(message);\n        super.visitMethodInsn(INVOKEVIRTUAL, \"java/io/PrintStream\", \"println\", \"(Ljava/lang/String;)V\", false);\n    }\n\n    private void dup(Type t) {\n        int size = t.getSize();\n        if (size == 1) {\n            super.visitInsn(DUP);\n        }\n        else {\n            super.visitInsn(DUP2);\n        }\n    }\n\n    private void printValueOnStack(Type t) {\n        int size = t.getSize();\n        String descriptor = String.format(\"(%s)V\", t.getDescriptor());\n\n        super.visitFieldInsn(GETSTATIC, \"java/lang/System\", \"out\", \"Ljava/io/PrintStream;\");\n        if (size == 1) {\n            super.visitInsn(SWAP);\n        }\n        else {\n            super.visitInsn(DUP_X2);\n            super.visitInsn(POP);\n        }\n        super.visitMethodInsn(INVOKEVIRTUAL, \"java/io/PrintStream\", \"println\", descriptor, false);\n    }\n\n    private void dupAndPrintValueOnStack(Type t) {\n        dup(t);\n        printValueOnStack(t);\n    }\n}\n"
  },
  {
    "path": "src/main/java/lsieun/asm/template/MethodPrintInstructionVisitor.java",
    "content": "package lsieun.asm.template;\n\nimport org.objectweb.asm.ClassVisitor;\nimport org.objectweb.asm.MethodVisitor;\n\nimport static org.objectweb.asm.Opcodes.ACC_ABSTRACT;\nimport static org.objectweb.asm.Opcodes.ACC_NATIVE;\n\npublic class MethodPrintInstructionVisitor extends ClassVisitor {\n    public MethodPrintInstructionVisitor(int api, ClassVisitor classVisitor) {\n        super(api, classVisitor);\n    }\n\n    @Override\n    public MethodVisitor visitMethod(int access, String name, String descriptor, String signature, String[] exceptions) {\n        MethodVisitor mv = super.visitMethod(access, name, descriptor, signature, exceptions);\n        if (mv != null && !\"<init>\".equals(name) && !\"<clinit>\".equals(name)) {\n            boolean isAbstractMethod = (access & ACC_ABSTRACT) != 0;\n            boolean isNativeMethod = (access & ACC_NATIVE) != 0;\n            if (!isAbstractMethod && !isNativeMethod) {\n                mv = new MethodPrintInstructionAdapter(api, mv, name, descriptor);\n            }\n        }\n        return mv;\n    }\n}\n"
  },
  {
    "path": "src/main/java/lsieun/asm/template/MethodWithSameTryCatchLogicVisitor.java",
    "content": "package lsieun.asm.template;\n\nimport org.objectweb.asm.ClassVisitor;\nimport org.objectweb.asm.Label;\nimport org.objectweb.asm.MethodVisitor;\n\nimport java.util.ArrayList;\nimport java.util.List;\n\nimport static org.objectweb.asm.Opcodes.*;\n\npublic class MethodWithSameTryCatchLogicVisitor extends ClassVisitor {\n    public MethodWithSameTryCatchLogicVisitor(int api, ClassVisitor classVisitor) {\n        super(api, classVisitor);\n    }\n\n    @Override\n    public MethodVisitor visitMethod(int access, String name, String descriptor, String signature, String[] exceptions) {\n        MethodVisitor mv = super.visitMethod(access, name, descriptor, signature, exceptions);\n        if (mv != null) {\n            boolean isAbstractMethod = (access & ACC_ABSTRACT) != 0;\n            boolean isNativeMethod = (access & ACC_NATIVE) != 0;\n            if (!isAbstractMethod && !isNativeMethod) {\n                mv = new MethodWithSameTryCatchLogicAdapter(api, mv);\n            }\n        }\n        return mv;\n    }\n\n\n    private static class MethodWithSameTryCatchLogicAdapter extends MethodVisitor {\n        private final List<Label> handlerList = new ArrayList<>();\n\n        public MethodWithSameTryCatchLogicAdapter(int api, MethodVisitor methodVisitor) {\n            super(api, methodVisitor);\n        }\n\n        @Override\n        public void visitTryCatchBlock(Label start, Label end, Label handler, String type) {\n            // 首先，处理自己的代码逻辑\n            if (!handlerList.contains(handler)) {\n                handlerList.add(handler);\n            }\n\n            // 其次，调用父类的方法实现\n            super.visitTryCatchBlock(start, end, handler, type);\n        }\n\n        @Override\n        public void visitLabel(Label label) {\n            // 首先，调用父类的方法实现\n            super.visitLabel(label);\n\n            // 其次，处理自己的代码逻辑\n            // 需要注意：不要将operand stack上的异常给弄丢了。\n            if (handlerList.contains(label)) {\n                // 在这里，我们复制一份来使用\n                super.visitInsn(DUP);\n                super.visitFieldInsn(GETSTATIC, \"java/lang/System\", \"out\", \"Ljava/io/PrintStream;\");\n                super.visitMethodInsn(INVOKEVIRTUAL, \"java/lang/Exception\", \"printStackTrace\", \"(Ljava/io/PrintStream;)V\", false);\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "src/main/java/lsieun/asm/template/MethodWithWholeTryCatchVisitor.java",
    "content": "package lsieun.asm.template;\n\nimport org.objectweb.asm.*;\n\nimport static org.objectweb.asm.Opcodes.*;\n\npublic class MethodWithWholeTryCatchVisitor extends ClassVisitor {\n    private final String methodName;\n    private final String methodDesc;\n\n    public MethodWithWholeTryCatchVisitor(int api, ClassVisitor classVisitor, String methodName, String methodDesc) {\n        super(api, classVisitor);\n        this.methodName = methodName;\n        this.methodDesc = methodDesc;\n    }\n\n    @Override\n    public MethodVisitor visitMethod(int access, String name, String descriptor, String signature, String[] exceptions) {\n        MethodVisitor mv = super.visitMethod(access, name, descriptor, signature, exceptions);\n        if (mv != null && !\"<init>\".equals(name) && methodName.equals(name) && methodDesc.equals(descriptor)) {\n            boolean isAbstractMethod = (access & ACC_ABSTRACT) != 0;\n            boolean isNativeMethod = (access & ACC_NATIVE) != 0;\n            if (!isAbstractMethod && !isNativeMethod) {\n                mv = new MethodWithWholeTryCatchAdapter(api, mv, access, descriptor);\n            }\n        }\n        return mv;\n    }\n\n    private static class MethodWithWholeTryCatchAdapter extends MethodVisitor {\n        private final int methodAccess;\n        private final String methodDesc;\n\n        private final Label startLabel = new Label();\n        private final Label endLabel = new Label();\n        private final Label handlerLabel = new Label();\n\n        public MethodWithWholeTryCatchAdapter(int api, MethodVisitor methodVisitor, int methodAccess, String methodDesc) {\n            super(api, methodVisitor);\n            this.methodAccess = methodAccess;\n            this.methodDesc = methodDesc;\n        }\n\n        public void visitCode() {\n            // 首先，处理自己的代码逻辑\n            // (1) startLabel\n            super.visitLabel(startLabel);\n\n            // 其次，调用父类的方法实现\n            super.visitCode();\n        }\n\n        @Override\n        public void visitMaxs(int maxStack, int maxLocals) {\n            // 首先，处理自己的代码逻辑\n            // (2) endLabel\n            super.visitLabel(endLabel);\n\n            // (3) handlerLabel\n            super.visitLabel(handlerLabel);\n            int localIndex = getLocalIndex();\n            super.visitVarInsn(ASTORE, localIndex);\n            super.visitVarInsn(ALOAD, localIndex);\n            super.visitFieldInsn(GETSTATIC, \"java/lang/System\", \"out\", \"Ljava/io/PrintStream;\");\n            super.visitMethodInsn(INVOKEVIRTUAL, \"java/lang/Exception\", \"printStackTrace\", \"(Ljava/io/PrintStream;)V\", false);\n            super.visitVarInsn(ALOAD, localIndex);\n            super.visitInsn(Opcodes.ATHROW);\n\n            // (4) visitTryCatchBlock\n            super.visitTryCatchBlock(startLabel, endLabel, handlerLabel, \"java/lang/Exception\");\n\n            // 其次，调用父类的方法实现\n            super.visitMaxs(maxStack, maxLocals);\n        }\n\n        private int getLocalIndex() {\n            Type t = Type.getType(methodDesc);\n            Type[] argumentTypes = t.getArgumentTypes();\n\n            boolean isStaticMethod = ((methodAccess & ACC_STATIC) != 0);\n            int localIndex = isStaticMethod ? 0 : 1;\n            for (Type argType : argumentTypes) {\n                localIndex += argType.getSize();\n            }\n            return localIndex;\n        }\n    }\n}\n"
  },
  {
    "path": "src/main/java/lsieun/asm/template/RefRenameAdapter.java",
    "content": "package lsieun.asm.template;\n\nimport org.objectweb.asm.MethodVisitor;\nimport org.objectweb.asm.Type;\n\npublic class RefRenameAdapter extends MethodVisitor {\n    private final String oldOwner;\n    private final String newOwner;\n\n    public RefRenameAdapter(int api, MethodVisitor mv, String oldOwner, String newOwner) {\n        super(api, mv);\n        this.oldOwner = oldOwner;\n        this.newOwner = newOwner;\n    }\n\n    @Override\n    public void visitLdcInsn(Object value) {\n        if (value instanceof Type) {\n            Type t = Type.getType(getDescriptor(oldOwner));\n            if (value.equals(t)) {\n                super.visitLdcInsn(Type.getType(getDescriptor(newOwner)));\n                return;\n            }\n        }\n        super.visitLdcInsn(value);\n    }\n\n    public String getDescriptor(String internalName) {\n        return String.format(\"L%s;\", internalName);\n    }\n\n    @Override\n    public void visitFieldInsn(int opcode, String owner, String name, String descriptor) {\n        if (owner.equals(this.oldOwner)) {\n            super.visitFieldInsn(opcode, newOwner, name, descriptor);\n        }\n        else {\n            super.visitFieldInsn(opcode, owner, name, descriptor);\n        }\n    }\n\n    @Override\n    public void visitMethodInsn(int opcode, String owner, String name, String descriptor, boolean isInterface) {\n        if (owner.equals(this.oldOwner)) {\n            super.visitMethodInsn(opcode, newOwner, name, descriptor, isInterface);\n        }\n        else {\n            super.visitMethodInsn(opcode, owner, name, descriptor, isInterface);\n        }\n    }\n}\n"
  },
  {
    "path": "src/main/java/lsieun/asm/template/RemoveSyntheticVisitor.java",
    "content": "package lsieun.asm.template;\n\nimport org.objectweb.asm.ClassVisitor;\nimport org.objectweb.asm.FieldVisitor;\nimport org.objectweb.asm.MethodVisitor;\nimport org.objectweb.asm.Opcodes;\n\npublic class RemoveSyntheticVisitor extends ClassVisitor {\n    public RemoveSyntheticVisitor(int api, ClassVisitor classVisitor) {\n        super(api, classVisitor);\n    }\n\n    @Override\n    public void visit(int version, int access, String name, String signature, String superName, String[] interfaces) {\n        super.visit(version, access & (~Opcodes.ACC_SYNTHETIC), name, signature, superName, interfaces);\n    }\n\n    @Override\n    public FieldVisitor visitField(int access, String name, String descriptor, String signature, Object value) {\n        return super.visitField(access & (~Opcodes.ACC_SYNTHETIC), name, descriptor, signature, value);\n    }\n\n    @Override\n    public MethodVisitor visitMethod(int access, String name, String descriptor, String signature, String[] exceptions) {\n        return super.visitMethod(access & ~(Opcodes.ACC_SYNTHETIC | Opcodes.ACC_BRIDGE), name, descriptor, signature, exceptions);\n    }\n}\n"
  },
  {
    "path": "src/main/java/lsieun/asm/tree/ChangeMemberNameBasedOnAnnotationNode.java",
    "content": "package lsieun.asm.tree;\n\nimport org.objectweb.asm.ClassVisitor;\nimport org.objectweb.asm.tree.AnnotationNode;\nimport org.objectweb.asm.tree.ClassNode;\nimport org.objectweb.asm.tree.MethodNode;\n\nimport java.util.List;\n\npublic class ChangeMemberNameBasedOnAnnotationNode extends ClassNode {\n    private final String annotationDescriptor;\n\n    public ChangeMemberNameBasedOnAnnotationNode(int api, ClassVisitor cv, String annotationDescriptor) {\n        super(api);\n        this.annotationDescriptor = annotationDescriptor;\n        this.cv = cv;\n    }\n\n    @Override\n    public void visitEnd() {\n        // 首先，处理自己的代码逻辑\n        for (MethodNode mn : methods) {\n            boolean flag = exists(annotationDescriptor, mn.visibleAnnotations);\n            if (!flag) flag = exists(annotationDescriptor, mn.invisibleAnnotations);\n\n            if (flag) {\n                mn.name = \"changed_\" + mn.name;\n            }\n        }\n\n        // 其次，调用父类的方法实现（根据实际情况，选择保留，或删除）\n        super.visitEnd();\n\n        // 最后，向后续ClassVisitor传递\n        if (cv != null) {\n            accept(cv);\n        }\n    }\n\n    private boolean exists(String name, List<AnnotationNode> annotations) {\n        if (annotations == null) return false;\n        for (AnnotationNode an : annotations) {\n            if (an.desc.equals(name)) {\n                return true;\n            }\n        }\n        return false;\n    }\n}\n"
  },
  {
    "path": "src/main/java/lsieun/asm/tree/ChangeThisNode.java",
    "content": "package lsieun.asm.tree;\n\nimport org.objectweb.asm.ClassVisitor;\nimport org.objectweb.asm.MethodVisitor;\nimport org.objectweb.asm.Opcodes;\nimport org.objectweb.asm.Type;\nimport org.objectweb.asm.tree.ClassNode;\nimport org.objectweb.asm.tree.InsnList;\nimport org.objectweb.asm.tree.MethodNode;\n\nimport static org.objectweb.asm.Opcodes.*;\n\npublic class ChangeThisNode extends ClassNode {\n    public ChangeThisNode(int api, ClassVisitor cv) {\n        super(api);\n        this.cv = cv;\n    }\n\n    @Override\n    public void visitEnd() {\n        // 首先，处理自己的代码逻辑\n        int size = methods.size();\n        for (int i = 0; i < size; i++) {\n            MethodNode mn = methods.get(i);\n            if (\"<init>\".equals(mn.name) || \"<clinit>\".equals(mn.name)) {\n                continue;\n            }\n            InsnList instructions = mn.instructions;\n            if (instructions.size() == 0) {\n                continue;\n            }\n\n            int maxLocals = mn.maxLocals;\n\n            int api = Opcodes.ASM9;\n            MethodNode newMethodNode = new MethodNode(api, mn.access, mn.name, mn.desc, mn.signature, mn.exceptions.toArray(new String[0]));\n            MethodVisitor mv = new ChangeThisAdapter(api, newMethodNode, mn.access, mn.name, mn.desc, maxLocals);\n            mn.accept(mv);\n            methods.set(i, newMethodNode);\n        }\n\n        // 其次，调用父类的方法实现（根据实际情况，选择保留，或删除）\n        super.visitEnd();\n\n        // 最后，向后续ClassVisitor传递\n        if (cv != null) {\n            accept(cv);\n        }\n    }\n\n    private static class ChangeThisAdapter extends MethodVisitor {\n        private final int methodAccess;\n        private final String methodName;\n        private final String methodDesc;\n        private final int maxLocals;\n\n        public ChangeThisAdapter(int api, MethodVisitor mv, int access, String methodName, String descriptor, int maxLocals) {\n            super(api, mv);\n            this.methodAccess = access;\n            this.methodName = methodName;\n            this.methodDesc = descriptor;\n            this.maxLocals = maxLocals;\n        }\n\n        @Override\n        public void visitCode() {\n            // 首先，处理自己的代码逻辑\n            boolean isStatic = (methodAccess & ACC_STATIC) != 0;\n\n            // 第一步，考虑要不要复制this：\n            // - 如果是static方法，就不复制了；\n            // - 如果是non-static方法，就进行复制\n            if (!isStatic) {\n                //进入这里，说明是non-static方法，对this进行复制\n                super.visitVarInsn(ALOAD, 0);\n                super.visitVarInsn(ASTORE, getBackUpIndex(0));\n            }\n\n            // 第二步，考虑方法接收的参数\n            // 根据方法描述符（methodDesc）获取各个参数的类型\n            Type methodType = Type.getMethodType(methodDesc);\n            Type[] argumentTypes = methodType.getArgumentTypes();\n\n            // 对各个参数类型进行循环\n            int localIndex = isStatic ? 0 : 1;\n            for (Type t : argumentTypes) {\n                // 将参数加载到栈上\n                int load_opcode = t.getOpcode(ILOAD);\n                super.visitVarInsn(load_opcode, localIndex);\n\n                // 放到结尾目标位置\n                int store_opcode = t.getOpcode(ISTORE);\n                super.visitVarInsn(store_opcode, getBackUpIndex(localIndex));\n\n                // 更新索引的位置\n                localIndex += t.getSize();\n            }\n\n            // 其次，调用父类的方法实现\n            super.visitCode();\n        }\n\n        @Override\n        public void visitInsn(int opcode) {\n            // 首先，处理自己的代码逻辑\n            if (opcode == ATHROW || (opcode >= IRETURN && opcode <= RETURN)) {\n                // 首先，处理自己的代码逻辑\n                boolean isStatic = (methodAccess & ACC_STATIC) != 0;\n\n                if (!isStatic) {\n                    super.visitVarInsn(ALOAD, getBackUpIndex(0));\n                    super.visitInsn(POP);\n                }\n\n                Type methodType = Type.getMethodType(methodDesc);\n                Type[] argumentTypes = methodType.getArgumentTypes();\n                int localIndex = isStatic ? 0 : 1;\n                for (Type t : argumentTypes) {\n                    int load_opcode = t.getOpcode(ILOAD);\n                    super.visitVarInsn(load_opcode, getBackUpIndex(localIndex));\n                    int size = t.getSize();\n                    if (size == 1) {\n                        super.visitInsn(POP);\n                    }\n                    else {\n                        super.visitInsn(POP2);\n                    }\n                    localIndex += t.getSize();\n                }\n\n                super.visitFieldInsn(GETSTATIC, \"java/lang/System\", \"out\", \"Ljava/io/PrintStream;\");\n                super.visitLdcInsn(\"%s %s:%s\");\n                super.visitInsn(ICONST_3);\n                super.visitTypeInsn(ANEWARRAY, \"java/lang/Object\");\n                super.visitInsn(DUP);\n                super.visitInsn(ICONST_0);\n                super.visitVarInsn(ALOAD, getBackUpIndex(0));\n                super.visitMethodInsn(INVOKEVIRTUAL, \"java/lang/Object\", \"getClass\", \"()Ljava/lang/Class;\", false);\n                super.visitMethodInsn(INVOKEVIRTUAL, \"java/lang/Class\", \"getName\", \"()Ljava/lang/String;\", false);\n                super.visitInsn(AASTORE);\n                super.visitInsn(DUP);\n                super.visitInsn(ICONST_1);\n                super.visitLdcInsn(methodName);\n                super.visitInsn(AASTORE);\n                super.visitInsn(DUP);\n                super.visitInsn(ICONST_2);\n                super.visitLdcInsn(methodDesc);\n                super.visitInsn(AASTORE);\n                super.visitMethodInsn(INVOKESTATIC, \"java/lang/String\", \"format\", \"(Ljava/lang/String;[Ljava/lang/Object;)Ljava/lang/String;\", false);\n                super.visitMethodInsn(INVOKEVIRTUAL, \"java/io/PrintStream\", \"println\", \"(Ljava/lang/String;)V\", false);\n            }\n\n            // 其次，调用父类的方法实现\n            super.visitInsn(opcode);\n        }\n\n        private int getBackUpIndex(int localIndex) {\n            return maxLocals + localIndex;\n        }\n    }\n}\n"
  },
  {
    "path": "src/main/java/lsieun/asm/tree/ClassAddCustomAttributeNode.java",
    "content": "package lsieun.asm.tree;\n\nimport lsieun.asm.template.CustomAttribute;\nimport lsieun.utils.ByteUtils;\nimport org.objectweb.asm.Attribute;\nimport org.objectweb.asm.ClassVisitor;\nimport org.objectweb.asm.tree.ClassNode;\nimport org.objectweb.asm.tree.FieldNode;\nimport org.objectweb.asm.tree.MethodNode;\n\nimport java.util.ArrayList;\n\npublic class ClassAddCustomAttributeNode extends ClassNode {\n    private final String attrName;\n\n    public ClassAddCustomAttributeNode(int api, ClassVisitor cv, String attrName) {\n        super(api);\n        this.cv = cv;\n        this.attrName = attrName;\n    }\n\n    @Override\n    public void visitEnd() {\n        // 首先，处理自己的代码逻辑\n        StringBuilder sb = new StringBuilder();\n        sb.append(name);\n        for (FieldNode fn : fields) {\n            sb.append(fn.name);\n        }\n        for (MethodNode mn : methods) {\n            sb.append(mn.name);\n        }\n        int hashCode = sb.toString().hashCode();\n        byte[] info = ByteUtils.intToByteArray(hashCode);\n        Attribute customAttribute = new CustomAttribute(attrName, info);\n        if (attrs == null) {\n            attrs = new ArrayList<>();\n        }\n        attrs.add(customAttribute);\n\n        // 其次，调用父类的方法实现（根据实际情况，选择保留，或删除）\n        super.visitEnd();\n\n        // 最后，向后续ClassVisitor传递\n        if (cv != null) {\n            accept(cv);\n        }\n    }\n}\n"
  },
  {
    "path": "src/main/java/lsieun/asm/tree/ClassAddFieldNode.java",
    "content": "package lsieun.asm.tree;\n\nimport lsieun.asm.tree.transformer.ClassTransformer;\nimport org.objectweb.asm.ClassVisitor;\nimport org.objectweb.asm.tree.ClassNode;\nimport org.objectweb.asm.tree.FieldNode;\n\npublic class ClassAddFieldNode extends ClassNode {\n    private final int fieldAccess;\n    private final String fieldName;\n    private final String fieldDesc;\n\n    public ClassAddFieldNode(int api, ClassVisitor cv,\n                             int fieldAccess, String fieldName, String fieldDesc) {\n        super(api);\n        this.cv = cv;\n        this.fieldAccess = fieldAccess;\n        this.fieldName = fieldName;\n        this.fieldDesc = fieldDesc;\n    }\n\n    @Override\n    public void visitEnd() {\n        // 首先，处理自己的代码逻辑\n        ClassTransformer ct = new ClassAddFieldTransformer(null, fieldAccess, fieldName, fieldDesc);\n        ct.transform(this);\n\n        // 其次，调用父类的方法实现（根据实际情况，选择保留，或删除）\n        super.visitEnd();\n\n        // 最后，向后续ClassVisitor传递\n        if (cv != null) {\n            accept(cv);\n        }\n    }\n\n    private static class ClassAddFieldTransformer extends ClassTransformer {\n        private final int fieldAccess;\n        private final String fieldName;\n        private final String fieldDesc;\n\n        public ClassAddFieldTransformer(ClassTransformer ct, int fieldAccess, String fieldName, String fieldDesc) {\n            super(ct);\n            this.fieldAccess = fieldAccess;\n            this.fieldName = fieldName;\n            this.fieldDesc = fieldDesc;\n        }\n\n        @Override\n        public void transform(ClassNode cn) {\n            // 首先，处理自己的代码逻辑\n            boolean isPresent = false;\n            for (FieldNode fn : cn.fields) {\n                if (fieldName.equals(fn.name)) {\n                    isPresent = true;\n                    break;\n                }\n            }\n            if (!isPresent) {\n                cn.fields.add(new FieldNode(fieldAccess, fieldName, fieldDesc, null, null));\n            }\n\n            // 其次，调用父类的方法实现\n            super.transform(cn);\n        }\n    }\n}\n"
  },
  {
    "path": "src/main/java/lsieun/asm/tree/ClassAddMethodNode.java",
    "content": "package lsieun.asm.tree;\n\nimport lsieun.asm.tree.transformer.ClassTransformer;\nimport org.objectweb.asm.ClassVisitor;\nimport org.objectweb.asm.tree.ClassNode;\nimport org.objectweb.asm.tree.MethodNode;\n\nimport java.util.function.Consumer;\n\npublic class ClassAddMethodNode extends ClassNode {\n    private final int methodAccess;\n    private final String methodName;\n    private final String methodDesc;\n    private final Consumer<MethodNode> methodBody;\n\n    public ClassAddMethodNode(int api, ClassVisitor cv,\n                              int methodAccess, String methodName, String methodDesc,\n                              Consumer<MethodNode> methodBody) {\n        super(api);\n        this.cv = cv;\n        this.methodAccess = methodAccess;\n        this.methodName = methodName;\n        this.methodDesc = methodDesc;\n        this.methodBody = methodBody;\n    }\n\n    @Override\n    public void visitEnd() {\n        // 首先，处理自己的代码逻辑\n        ClassTransformer ct = new ClassAddMethodTransformer(null, methodAccess, methodName, methodDesc, methodBody);\n        ct.transform(this);\n\n        // 其次，调用父类的方法实现（根据实际情况，选择保留，或删除）\n        super.visitEnd();\n\n        // 最后，向后续ClassVisitor传递\n        if (cv != null) {\n            accept(cv);\n        }\n    }\n\n    private static class ClassAddMethodTransformer extends ClassTransformer {\n        private final int methodAccess;\n        private final String methodName;\n        private final String methodDesc;\n        private final Consumer<MethodNode> methodBody;\n\n        public ClassAddMethodTransformer(ClassTransformer ct,\n                                         int methodAccess, String methodName, String methodDesc,\n                                         Consumer<MethodNode> methodBody) {\n            super(ct);\n            this.methodAccess = methodAccess;\n            this.methodName = methodName;\n            this.methodDesc = methodDesc;\n            this.methodBody = methodBody;\n        }\n\n        @Override\n        public void transform(ClassNode cn) {\n            // 首先，处理自己的代码逻辑\n            boolean isPresent = false;\n            for (MethodNode mn : cn.methods) {\n                if (methodName.equals(mn.name) && methodDesc.equals(mn.desc)) {\n                    isPresent = true;\n                    break;\n                }\n            }\n            if (!isPresent) {\n                MethodNode mn = new MethodNode(methodAccess, methodName, methodDesc, null, null);\n                cn.methods.add(mn);\n\n                if (methodBody != null) {\n                    methodBody.accept(mn);\n                }\n            }\n\n            // 其次，调用父类的方法实现\n            super.transform(cn);\n        }\n    }\n}\n"
  },
  {
    "path": "src/main/java/lsieun/asm/tree/ClassAddTimerNode.java",
    "content": "package lsieun.asm.tree;\n\nimport lsieun.asm.tree.transformer.ClassTransformer;\nimport org.objectweb.asm.ClassVisitor;\nimport org.objectweb.asm.tree.*;\n\nimport static org.objectweb.asm.Opcodes.*;\n\npublic class ClassAddTimerNode extends ClassNode {\n    public ClassAddTimerNode(int api, ClassVisitor cv) {\n        super(api);\n        this.cv = cv;\n    }\n\n    @Override\n    public void visitEnd() {\n        // 首先，处理自己的代码逻辑\n        ClassTransformer ct = new ClassAddTimerTransformer(null);\n        ct.transform(this);\n\n        // 其次，调用父类的方法实现（根据实际情况，选择保留，或删除）\n        super.visitEnd();\n\n        // 最后，向后续ClassVisitor传递\n        if (cv != null) {\n            accept(cv);\n        }\n    }\n\n    private static class ClassAddTimerTransformer extends ClassTransformer {\n        public ClassAddTimerTransformer(ClassTransformer ct) {\n            super(ct);\n        }\n\n        @Override\n        public void transform(ClassNode cn) {\n            for (MethodNode mn : cn.methods) {\n                if (\"<init>\".equals(mn.name) || \"<clinit>\".equals(mn.name)) {\n                    continue;\n                }\n                InsnList instructions = mn.instructions;\n                if (instructions.size() == 0) {\n                    continue;\n                }\n                for (AbstractInsnNode item : instructions) {\n                    int opcode = item.getOpcode();\n                    // 在方法退出之前，加上当前时间戳\n                    if ((opcode >= IRETURN && opcode <= RETURN) || (opcode == ATHROW)) {\n                        InsnList il = new InsnList();\n                        il.add(new FieldInsnNode(GETSTATIC, cn.name, \"timer\", \"J\"));\n                        il.add(new MethodInsnNode(INVOKESTATIC, \"java/lang/System\", \"currentTimeMillis\", \"()J\"));\n                        il.add(new InsnNode(LADD));\n                        il.add(new FieldInsnNode(PUTSTATIC, cn.name, \"timer\", \"J\"));\n                        instructions.insertBefore(item, il);\n                    }\n                }\n\n                // 在方法刚进入之后，减去当前时间戳\n                InsnList il = new InsnList();\n                il.add(new FieldInsnNode(GETSTATIC, cn.name, \"timer\", \"J\"));\n                il.add(new MethodInsnNode(INVOKESTATIC, \"java/lang/System\", \"currentTimeMillis\", \"()J\"));\n                il.add(new InsnNode(LSUB));\n                il.add(new FieldInsnNode(PUTSTATIC, cn.name, \"timer\", \"J\"));\n                instructions.insert(il);\n\n                // local variables的大小，保持不变\n                // mn.maxLocals = mn.maxLocals;\n                // operand stack的大小，增加4个位置\n                mn.maxStack += 4;\n            }\n\n            int acc = ACC_PUBLIC | ACC_STATIC;\n            cn.fields.add(new FieldNode(acc, \"timer\", \"J\", null, null));\n            super.transform(cn);\n        }\n    }\n}\n"
  },
  {
    "path": "src/main/java/lsieun/asm/tree/ClassRemoveFieldNode.java",
    "content": "package lsieun.asm.tree;\n\nimport lsieun.asm.tree.transformer.ClassTransformer;\nimport org.objectweb.asm.ClassVisitor;\nimport org.objectweb.asm.tree.ClassNode;\n\npublic class ClassRemoveFieldNode extends ClassNode {\n    private final String fieldName;\n    private final String fieldDesc;\n\n    public ClassRemoveFieldNode(int api, ClassVisitor cv, String fieldName, String fieldDesc) {\n        super(api);\n        this.cv = cv;\n        this.fieldName = fieldName;\n        this.fieldDesc = fieldDesc;\n    }\n\n    @Override\n    public void visitEnd() {\n        // 首先，处理自己的代码逻辑\n        ClassTransformer ct = new ClassRemoveFieldTransformer(null, fieldName, fieldDesc);\n        ct.transform(this);\n\n        // 其次，调用父类的方法实现（根据实际情况，选择保留，或删除）\n        super.visitEnd();\n\n        // 最后，向后续ClassVisitor传递\n        if (cv != null) {\n            accept(cv);\n        }\n    }\n\n    private static class ClassRemoveFieldTransformer extends ClassTransformer {\n        private final String fieldName;\n        private final String fieldDesc;\n\n        public ClassRemoveFieldTransformer(ClassTransformer ct, String fieldName, String fieldDesc) {\n            super(ct);\n            this.fieldName = fieldName;\n            this.fieldDesc = fieldDesc;\n        }\n\n        @Override\n        public void transform(ClassNode cn) {\n            // 首先，处理自己的代码逻辑\n            cn.fields.removeIf(fn -> fieldName.equals(fn.name) && fieldDesc.equals(fn.desc));\n\n            // 其次，调用父类的方法实现\n            super.transform(cn);\n        }\n    }\n}\n"
  },
  {
    "path": "src/main/java/lsieun/asm/tree/ClassRemoveMethodNode.java",
    "content": "package lsieun.asm.tree;\n\nimport lsieun.asm.tree.transformer.ClassTransformer;\nimport org.objectweb.asm.ClassVisitor;\nimport org.objectweb.asm.tree.ClassNode;\n\npublic class ClassRemoveMethodNode extends ClassNode {\n    private final String methodName;\n    private final String methodDesc;\n\n    public ClassRemoveMethodNode(int api, ClassVisitor cv, String methodName, String methodDesc) {\n        super(api);\n        this.cv = cv;\n        this.methodName = methodName;\n        this.methodDesc = methodDesc;\n    }\n\n    @Override\n    public void visitEnd() {\n        // 首先，处理自己的代码逻辑\n        ClassTransformer ct = new ClassRemoveMethodTransformer(null, methodName, methodDesc);\n        ct.transform(this);\n\n        // 其次，调用父类的方法实现（根据实际情况，选择保留，或删除）\n        super.visitEnd();\n\n        // 最后，向后续ClassVisitor传递\n        if (cv != null) {\n            accept(cv);\n        }\n    }\n\n    private static class ClassRemoveMethodTransformer extends ClassTransformer {\n        private final String methodName;\n        private final String methodDesc;\n\n        public ClassRemoveMethodTransformer(ClassTransformer ct, String methodName, String methodDesc) {\n            super(ct);\n            this.methodName = methodName;\n            this.methodDesc = methodDesc;\n        }\n\n        @Override\n        public void transform(ClassNode cn) {\n            // 首先，处理自己的代码逻辑\n            cn.methods.removeIf(mn -> methodName.equals(mn.name) && methodDesc.equals(mn.desc));\n\n            // 其次，调用父类的方法实现\n            super.transform(cn);\n        }\n    }\n}\n"
  },
  {
    "path": "src/main/java/lsieun/asm/tree/MixCore2TreeVisitor.java",
    "content": "package lsieun.asm.tree;\n\nimport org.objectweb.asm.ClassVisitor;\nimport org.objectweb.asm.MethodVisitor;\nimport org.objectweb.asm.tree.*;\n\nimport static org.objectweb.asm.Opcodes.*;\n\npublic class MixCore2TreeVisitor extends ClassVisitor {\n    public MixCore2TreeVisitor(int api, ClassVisitor classVisitor) {\n        super(api, classVisitor);\n    }\n\n    @Override\n    public MethodVisitor visitMethod(int access, String name, String descriptor, String signature, String[] exceptions) {\n        MethodVisitor mv = super.visitMethod(access, name, descriptor, signature, exceptions);\n        if (mv != null && !\"<init>\".equals(name) && !\"<clinit>\".equals(name)) {\n            boolean isAbstractMethod = (access & ACC_ABSTRACT) != 0;\n            boolean isNativeMethod = (access & ACC_NATIVE) != 0;\n            if (!isAbstractMethod && !isNativeMethod) {\n                mv = new MethodEnterNode(api, access, name, descriptor, signature, exceptions, mv);\n            }\n        }\n        return mv;\n    }\n\n    private static class MethodEnterNode extends MethodNode {\n        public MethodEnterNode(int api, int access, String name, String descriptor,\n                               String signature, String[] exceptions,\n                               MethodVisitor mv) {\n            super(api, access, name, descriptor, signature, exceptions);\n            this.mv = mv;\n        }\n\n        @Override\n        public void visitEnd() {\n            // 首先，处理自己的代码逻辑\n            InsnList il = new InsnList();\n            il.add(new FieldInsnNode(GETSTATIC, \"java/lang/System\", \"out\", \"Ljava/io/PrintStream;\"));\n            il.add(new LdcInsnNode(\"Method Enter\"));\n            il.add(new MethodInsnNode(INVOKEVIRTUAL, \"java/io/PrintStream\", \"println\", \"(Ljava/lang/String;)V\", false));\n            instructions.insert(il);\n\n            // 其次，调用父类的方法实现（根据实际情况，选择保留，或删除）\n            super.visitEnd();\n\n            // 最后，向后续MethodVisitor传递\n            if (mv != null) {\n                accept(mv);\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "src/main/java/lsieun/asm/tree/MixTree2CoreNode.java",
    "content": "package lsieun.asm.tree;\n\nimport lsieun.asm.template.MethodEnteringAdapter;\nimport org.objectweb.asm.ClassVisitor;\nimport org.objectweb.asm.MethodVisitor;\nimport org.objectweb.asm.Opcodes;\nimport org.objectweb.asm.tree.ClassNode;\nimport org.objectweb.asm.tree.InsnList;\nimport org.objectweb.asm.tree.MethodNode;\n\npublic class MixTree2CoreNode extends ClassNode {\n    public MixTree2CoreNode(int api, ClassVisitor cv) {\n        super(api);\n        this.cv = cv;\n    }\n\n    @Override\n    public void visitEnd() {\n        // 首先，处理自己的代码逻辑\n        int size = methods.size();\n        for (int i = 0; i < size; i++) {\n            MethodNode mn = methods.get(i);\n            if (\"<init>\".equals(mn.name) || \"<clinit>\".equals(mn.name)) {\n                continue;\n            }\n            InsnList instructions = mn.instructions;\n            if (instructions.size() == 0) {\n                continue;\n            }\n\n            int api = Opcodes.ASM9;\n            MethodNode newMethodNode = new MethodNode(api, mn.access, mn.name, mn.desc, mn.signature, mn.exceptions.toArray(new String[0]));\n            MethodVisitor mv = new MethodEnteringAdapter(api, newMethodNode, mn.access, mn.name, mn.desc);\n            mn.accept(mv);\n            methods.set(i, newMethodNode);\n        }\n\n        // 其次，调用父类的方法实现（根据实际情况，选择保留，或删除）\n        super.visitEnd();\n\n        // 最后，向后续ClassVisitor传递\n        if (cv != null) {\n            accept(cv);\n        }\n    }\n}\n"
  },
  {
    "path": "src/main/java/lsieun/asm/tree/OptimizeJumpNode.java",
    "content": "package lsieun.asm.tree;\n\nimport lsieun.asm.tree.transformer.MethodTransformer;\nimport org.objectweb.asm.ClassVisitor;\nimport org.objectweb.asm.tree.*;\n\nimport static org.objectweb.asm.Opcodes.*;\n\npublic class OptimizeJumpNode extends ClassNode {\n    public OptimizeJumpNode(int api, ClassVisitor cv) {\n        super(api);\n        this.cv = cv;\n    }\n\n    @Override\n    public void visitEnd() {\n        // 首先，处理自己的代码逻辑\n        MethodTransformer mt = new MethodOptimizeJumpTransformer(null);\n        for (MethodNode mn : methods) {\n            if (\"<init>\".equals(mn.name) || \"<clinit>\".equals(mn.name)) {\n                continue;\n            }\n            InsnList instructions = mn.instructions;\n            if (instructions.size() == 0) {\n                continue;\n            }\n            mt.transform(mn);\n        }\n\n        // 其次，调用父类的方法实现（根据实际情况，选择保留，或删除）\n        super.visitEnd();\n\n        // 最后，向后续ClassVisitor传递\n        if (cv != null) {\n            accept(cv);\n        }\n    }\n\n    private static class MethodOptimizeJumpTransformer extends MethodTransformer {\n        public MethodOptimizeJumpTransformer(MethodTransformer mt) {\n            super(mt);\n        }\n\n        @Override\n        public void transform(MethodNode mn) {\n            // 首先，处理自己的代码逻辑\n            InsnList instructions = mn.instructions;\n            for (AbstractInsnNode insnNode : instructions) {\n                if (insnNode instanceof JumpInsnNode) {\n                    JumpInsnNode jumpInsnNode = (JumpInsnNode) insnNode;\n                    LabelNode label = jumpInsnNode.label;\n                    AbstractInsnNode target;\n                    while (true) {\n                        target = label;\n                        while (target != null && target.getOpcode() < 0) {\n                            target = target.getNext();\n                        }\n\n                        if (target != null && target.getOpcode() == GOTO) {\n                            label = ((JumpInsnNode) target).label;\n                        }\n                        else {\n                            break;\n                        }\n                    }\n\n                    // update target\n                    jumpInsnNode.label = label;\n                    // if possible, replace jump with target instruction\n                    if (insnNode.getOpcode() == GOTO && target != null) {\n                        int opcode = target.getOpcode();\n                        if ((opcode >= IRETURN && opcode <= RETURN) || opcode == ATHROW) {\n                            instructions.set(insnNode, target.clone(null));\n                        }\n                    }\n                }\n            }\n\n            // 其次，调用父类的方法实现\n            super.transform(mn);\n        }\n    }\n}\n"
  },
  {
    "path": "src/main/java/lsieun/asm/tree/RemoveGetFieldPutFieldNode.java",
    "content": "package lsieun.asm.tree;\n\nimport lsieun.asm.tree.transformer.MethodTransformer;\nimport org.objectweb.asm.ClassVisitor;\nimport org.objectweb.asm.tree.*;\n\nimport java.util.ListIterator;\n\nimport static org.objectweb.asm.Opcodes.*;\n\npublic class RemoveGetFieldPutFieldNode extends ClassNode {\n    public RemoveGetFieldPutFieldNode(int api, ClassVisitor cv) {\n        super(api);\n        this.cv = cv;\n    }\n\n    @Override\n    public void visitEnd() {\n        // 首先，处理自己的代码逻辑\n        MethodTransformer mt = new MethodRemoveGetFieldPutFieldTransformer(null);\n        for (MethodNode mn : methods) {\n            if (\"<init>\".equals(mn.name) || \"<clinit>\".equals(mn.name)) {\n                continue;\n            }\n            InsnList instructions = mn.instructions;\n            if (instructions.size() == 0) {\n                continue;\n            }\n            mt.transform(mn);\n        }\n\n        // 其次，调用父类的方法实现（根据实际情况，选择保留，或删除）\n        super.visitEnd();\n\n        // 最后，向后续ClassVisitor传递\n        if (cv != null) {\n            accept(cv);\n        }\n    }\n\n    private static class MethodRemoveGetFieldPutFieldTransformer extends MethodTransformer {\n        public MethodRemoveGetFieldPutFieldTransformer(MethodTransformer mt) {\n            super(mt);\n        }\n\n        @Override\n        public void transform(MethodNode mn) {\n            // 首先，处理自己的代码逻辑\n            InsnList instructions = mn.instructions;\n            ListIterator<AbstractInsnNode> it = instructions.iterator();\n            while (it.hasNext()) {\n                AbstractInsnNode node1 = it.next();\n                if (isALOAD0(node1)) {\n                    AbstractInsnNode node2 = getNext(node1);\n                    if (node2 != null && isALOAD0(node2)) {\n                        AbstractInsnNode node3 = getNext(node2);\n                        if (node3 != null && node3.getOpcode() == GETFIELD) {\n                            AbstractInsnNode node4 = getNext(node3);\n                            if (node4 != null && node4.getOpcode() == PUTFIELD) {\n                                if (sameField(node3, node4)) {\n                                    while (it.next() != node4) {\n                                    }\n                                    instructions.remove(node1);\n                                    instructions.remove(node2);\n                                    instructions.remove(node3);\n                                    instructions.remove(node4);\n                                }\n                            }\n                        }\n                    }\n                }\n            }\n\n            // 其次，调用父类的方法实现\n            super.transform(mn);\n        }\n\n        private static AbstractInsnNode getNext(AbstractInsnNode insn) {\n            do {\n                insn = insn.getNext();\n                if (insn != null && !(insn instanceof LineNumberNode)) {\n                    break;\n                }\n            } while (insn != null);\n            return insn;\n        }\n\n        private static boolean isALOAD0(AbstractInsnNode insnNode) {\n            return insnNode.getOpcode() == ALOAD && ((VarInsnNode) insnNode).var == 0;\n        }\n\n        private static boolean sameField(AbstractInsnNode oneInsnNode, AbstractInsnNode anotherInsnNode) {\n            if (!(oneInsnNode instanceof FieldInsnNode)) return false;\n            if (!(anotherInsnNode instanceof FieldInsnNode)) return false;\n            FieldInsnNode fieldInsnNode1 = (FieldInsnNode) oneInsnNode;\n            FieldInsnNode fieldInsnNode2 = (FieldInsnNode) anotherInsnNode;\n            String name1 = fieldInsnNode1.name;\n            String name2 = fieldInsnNode2.name;\n            return name1.equals(name2);\n        }\n    }\n}\n"
  },
  {
    "path": "src/main/java/lsieun/asm/tree/my/MyClassNode.java",
    "content": "package lsieun.asm.tree.my;\n\nimport org.objectweb.asm.ClassVisitor;\nimport org.objectweb.asm.tree.ClassNode;\n\npublic class MyClassNode extends ClassNode {\n    public MyClassNode(int api, ClassVisitor cv) {\n        super(api);\n        this.cv = cv;\n    }\n\n    @Override\n    public void visitEnd() {\n        // 首先，处理自己的代码逻辑\n        // put your transformation code here\n\n        // 其次，调用父类的方法实现（根据实际情况，选择保留，或删除）\n        super.visitEnd();\n\n        // 最后，向后续ClassVisitor传递\n        if (cv != null) {\n            accept(cv);\n        }\n    }\n}\n"
  },
  {
    "path": "src/main/java/lsieun/asm/tree/my/MyClassVisitor.java",
    "content": "package lsieun.asm.tree.my;\n\nimport org.objectweb.asm.ClassVisitor;\nimport org.objectweb.asm.tree.ClassNode;\n\npublic class MyClassVisitor extends ClassVisitor {\n    private final ClassVisitor next;\n\n    public MyClassVisitor(int api, ClassVisitor classVisitor) {\n        super(api, new ClassNode());\n        this.next = classVisitor;\n    }\n\n    @Override\n    public void visitEnd() {\n        // 首先，处理自己的代码逻辑\n        ClassNode cn = (ClassNode) cv;\n        // put your transformation code here\n\n        // 其次，调用父类的方法实现（根据实际情况，选择保留，或删除）\n        super.visitEnd();\n\n        // 最后，向后续ClassVisitor传递\n        if (next != null) {\n            cn.accept(next);\n        }\n    }\n}\n"
  },
  {
    "path": "src/main/java/lsieun/asm/tree/my/MyMethodAdapter.java",
    "content": "package lsieun.asm.tree.my;\n\nimport org.objectweb.asm.MethodVisitor;\nimport org.objectweb.asm.tree.MethodNode;\n\npublic class MyMethodAdapter extends MethodVisitor {\n    private final MethodVisitor next;\n\n    public MyMethodAdapter(int api, int access, String name, String desc,\n                           String signature, String[] exceptions, MethodVisitor mv) {\n        super(api, new MethodNode(access, name, desc, signature, exceptions));\n        this.next = mv;\n    }\n\n    @Override\n    public void visitEnd() {\n        // 首先，处理自己的代码逻辑\n        MethodNode mn = (MethodNode) mv;\n        // put your transformation code here\n\n        // 其次，调用父类的方法实现（根据实际情况，选择保留，或删除）\n        super.visitEnd();\n\n        // 最后，向后续MethodVisitor传递\n        if (next != null) {\n            mn.accept(next);\n        }\n    }\n}\n"
  },
  {
    "path": "src/main/java/lsieun/asm/tree/my/MyMethodNode.java",
    "content": "package lsieun.asm.tree.my;\n\nimport org.objectweb.asm.MethodVisitor;\nimport org.objectweb.asm.tree.MethodNode;\n\npublic class MyMethodNode extends MethodNode {\n    public MyMethodNode(int access, String name, String descriptor,\n                        String signature, String[] exceptions,\n                        MethodVisitor mv) {\n        super(access, name, descriptor, signature, exceptions);\n        this.mv = mv;\n    }\n\n    @Override\n    public void visitEnd() {\n        // 首先，处理自己的代码逻辑\n        // put your transformation code here\n\n        // 其次，调用父类的方法实现（根据实际情况，选择保留，或删除）\n        super.visitEnd();\n\n        // 最后，向后续MethodVisitor传递\n        if (mv != null) {\n            accept(mv);\n        }\n    }\n}\n"
  },
  {
    "path": "src/main/java/lsieun/asm/tree/transformer/ClassTransformer.java",
    "content": "package lsieun.asm.tree.transformer;\n\nimport org.objectweb.asm.tree.ClassNode;\n\npublic abstract class ClassTransformer {\n    protected ClassTransformer ct;\n\n    public ClassTransformer(ClassTransformer ct) {\n        this.ct = ct;\n    }\n\n    public void transform(ClassNode cn) {\n        if (ct != null) {\n            ct.transform(cn);\n        }\n    }\n}\n"
  },
  {
    "path": "src/main/java/lsieun/asm/tree/transformer/MethodTransformer.java",
    "content": "package lsieun.asm.tree.transformer;\n\nimport org.objectweb.asm.tree.MethodNode;\n\npublic abstract class MethodTransformer {\n    protected MethodTransformer mt;\n\n    public MethodTransformer(MethodTransformer mt) {\n        this.mt = mt;\n    }\n\n    public void transform(MethodNode mn) {\n        if (mt != null) {\n            mt.transform(mn);\n        }\n    }\n}\n"
  },
  {
    "path": "src/main/java/lsieun/asm/util/CheckClassAdapterExample01Generate.java",
    "content": "package lsieun.asm.util;\n\nimport lsieun.utils.FileUtils;\nimport org.objectweb.asm.ClassVisitor;\nimport org.objectweb.asm.ClassWriter;\nimport org.objectweb.asm.util.CheckClassAdapter;\n\npublic class CheckClassAdapterExample01Generate {\n    public static void main(String[] args) throws Exception {\n        String relative_path = \"sample/HelloWorld.class\";\n        String filepath = FileUtils.getFilePath(relative_path);\n\n        // (1) 生成byte[]内容\n        byte[] bytes = dump();\n\n        // (2) 保存byte[]到文件\n        FileUtils.writeBytes(filepath, bytes);\n    }\n\n    public static byte[] dump() throws Exception {\n        // (1) 创建ClassWriter对象\n        ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_FRAMES);\n        ClassVisitor cv = new CheckClassAdapter(cw);\n\n        // (2) 调用visitXxx()方法\n        CodeUtils.generate(cv);\n\n        // (3) 调用toByteArray()方法\n        return cw.toByteArray();\n    }\n}\n"
  },
  {
    "path": "src/main/java/lsieun/asm/util/CheckClassAdapterExample02Generate.java",
    "content": "package lsieun.asm.util;\n\nimport lsieun.utils.FileUtils;\nimport org.objectweb.asm.ClassReader;\nimport org.objectweb.asm.ClassWriter;\nimport org.objectweb.asm.util.CheckClassAdapter;\n\nimport java.io.PrintWriter;\n\npublic class CheckClassAdapterExample02Generate {\n    public static void main(String[] args) throws Exception {\n        String relative_path = \"sample/HelloWorld.class\";\n        String filepath = FileUtils.getFilePath(relative_path);\n\n        // (1) 生成byte[]内容\n        byte[] bytes = dump();\n\n        // (2) 保存byte[]到文件\n        FileUtils.writeBytes(filepath, bytes);\n\n        // (3) 检查\n        PrintWriter printWriter = new PrintWriter(System.out);\n        CheckClassAdapter.verify(new ClassReader(bytes), false, printWriter);\n    }\n\n    public static byte[] dump() throws Exception {\n        // (1) 创建ClassWriter对象\n        ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_FRAMES);\n\n        // (2) 调用visitXxx()方法\n        CodeUtils.generate(cw);\n\n        // (3) 调用toByteArray()方法\n        return cw.toByteArray();\n    }\n}\n"
  },
  {
    "path": "src/main/java/lsieun/asm/util/CodeUtils.java",
    "content": "package lsieun.asm.util;\n\nimport org.objectweb.asm.ClassVisitor;\nimport org.objectweb.asm.FieldVisitor;\nimport org.objectweb.asm.MethodVisitor;\n\nimport static org.objectweb.asm.Opcodes.*;\n\npublic class CodeUtils {\n    public static void generate(ClassVisitor cv) {\n        // (2) 调用visitXxx()方法\n        cv.visit(V1_8, ACC_PUBLIC + ACC_SUPER, \"sample/HelloWorld\",\n                null, \"java/lang/Object\", null);\n\n        {\n            FieldVisitor fv = cv.visitField(ACC_PRIVATE, \"intValue\", \"I\", null, null);\n            fv.visitEnd();\n        }\n\n        {\n            MethodVisitor mv1 = cv.visitMethod(ACC_PUBLIC, \"<init>\", \"()V\", null, null);\n            mv1.visitCode();\n            mv1.visitVarInsn(ALOAD, 0);\n            mv1.visitMethodInsn(INVOKESPECIAL, \"java/lang/Object\", \"<init>\", \"()V\", false);\n            mv1.visitInsn(RETURN);\n            mv1.visitMaxs(1, 1);\n            mv1.visitEnd();\n        }\n\n        {\n            MethodVisitor mv2 = cv.visitMethod(ACC_PUBLIC, \"test\", \"()V\", null, null);\n            mv2.visitCode();\n            mv2.visitFieldInsn(GETSTATIC, \"java/lang/System\", \"out\", \"Ljava/io/PrintStream;\");\n            mv2.visitLdcInsn(\"Hello World\");\n            mv2.visitMethodInsn(INVOKEVIRTUAL, \"java/io/PrintStream\", \"println\", \"(Ljava/lang/String;)V\", false);\n            mv2.visitInsn(RETURN);\n            mv2.visitMaxs(2, 1);\n            mv2.visitEnd();\n        }\n        cv.visitEnd();\n    }\n}\n"
  },
  {
    "path": "src/main/java/lsieun/asm/util/TraceClassVisitorExample01Generate.java",
    "content": "package lsieun.asm.util;\n\nimport lsieun.utils.FileUtils;\nimport org.objectweb.asm.ClassWriter;\nimport org.objectweb.asm.FieldVisitor;\nimport org.objectweb.asm.MethodVisitor;\nimport org.objectweb.asm.util.TraceClassVisitor;\n\nimport java.io.PrintWriter;\n\nimport static org.objectweb.asm.Opcodes.*;\n\npublic class TraceClassVisitorExample01Generate {\n    public static void main(String[] args) throws Exception {\n        String relative_path = \"sample/HelloWorld.class\";\n        String filepath = FileUtils.getFilePath(relative_path);\n\n        // (1) 生成byte[]内容\n        byte[] bytes = dump();\n\n        // (2) 保存byte[]到文件\n        FileUtils.writeBytes(filepath, bytes);\n    }\n\n    public static byte[] dump() throws Exception {\n        // (1) 创建ClassWriter对象\n        ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_FRAMES);\n        PrintWriter printWriter = new PrintWriter(System.out);\n        TraceClassVisitor cv = new TraceClassVisitor(cw, printWriter);\n\n        // (2) 调用visitXxx()方法\n        cv.visit(V1_8, ACC_PUBLIC + ACC_SUPER, \"sample/HelloWorld\", null, \"java/lang/Object\", null);\n\n        {\n            FieldVisitor fv1 = cv.visitField(ACC_PRIVATE, \"intValue\", \"I\", null, null);\n            fv1.visitEnd();\n        }\n\n        {\n            FieldVisitor fv2 = cv.visitField(ACC_PRIVATE, \"strValue\", \"Ljava/lang/String;\", null, null);\n            fv2.visitEnd();\n        }\n\n        {\n            MethodVisitor mv1 = cv.visitMethod(ACC_PUBLIC, \"<init>\", \"()V\", null, null);\n            mv1.visitCode();\n            mv1.visitVarInsn(ALOAD, 0);\n            mv1.visitMethodInsn(INVOKESPECIAL, \"java/lang/Object\", \"<init>\", \"()V\", false);\n            mv1.visitInsn(RETURN);\n            mv1.visitMaxs(0, 0);\n            mv1.visitEnd();\n        }\n\n        {\n            MethodVisitor mv2 = cv.visitMethod(ACC_PUBLIC, \"test\", \"()V\", null, null);\n            mv2.visitCode();\n            mv2.visitFieldInsn(GETSTATIC, \"java/lang/System\", \"out\", \"Ljava/io/PrintStream;\");\n            mv2.visitLdcInsn(\"Hello World\");\n            mv2.visitMethodInsn(INVOKEVIRTUAL, \"java/io/PrintStream\", \"println\", \"(Ljava/lang/String;)V\", false);\n            mv2.visitInsn(RETURN);\n            mv2.visitMaxs(2, 1);\n            mv2.visitEnd();\n        }\n\n        cv.visitEnd();\n\n        // (3) 调用toByteArray()方法\n        return cw.toByteArray();\n    }\n}"
  },
  {
    "path": "src/main/java/lsieun/asm/util/TraceClassVisitorExample02Transform.java",
    "content": "package lsieun.asm.util;\n\nimport lsieun.asm.core.MethodTimerVisitor;\nimport lsieun.utils.FileUtils;\nimport org.objectweb.asm.ClassReader;\nimport org.objectweb.asm.ClassVisitor;\nimport org.objectweb.asm.ClassWriter;\nimport org.objectweb.asm.Opcodes;\nimport org.objectweb.asm.util.TraceClassVisitor;\n\nimport java.io.PrintWriter;\n\npublic class TraceClassVisitorExample02Transform {\n    public static void main(String[] args) {\n        String relative_path = \"sample/HelloWorld.class\";\n        String filepath = FileUtils.getFilePath(relative_path);\n        byte[] bytes1 = FileUtils.readBytes(filepath);\n\n        //（1）构建ClassReader\n        ClassReader cr = new ClassReader(bytes1);\n\n        //（2）构建ClassWriter\n        ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_FRAMES);\n\n        //（3）串连ClassVisitor\n        int api = Opcodes.ASM9;\n        PrintWriter printWriter = new PrintWriter(System.out);\n        TraceClassVisitor tcv = new TraceClassVisitor(cw, printWriter);\n        ClassVisitor cv = new MethodTimerVisitor(api, tcv);\n\n        //（4）结合ClassReader和ClassVisitor\n        int parsingOptions = ClassReader.SKIP_DEBUG | ClassReader.SKIP_FRAMES;\n        cr.accept(cv, parsingOptions);\n\n        //（5）生成byte[]\n        byte[] bytes2 = cw.toByteArray();\n\n        FileUtils.writeBytes(filepath, bytes2);\n    }\n}"
  },
  {
    "path": "src/main/java/lsieun/asm/util/TraceClassVisitorExample03.java",
    "content": "package lsieun.asm.util;\n\nimport static org.objectweb.asm.Opcodes.*;\n\nimport java.io.PrintWriter;\n\nimport org.objectweb.asm.util.TraceClassVisitor;\n\n@SuppressWarnings(\"Duplicates\")\npublic class TraceClassVisitorExample03 {\n    public static void main(String[] args) {\n        PrintWriter printWriter = new PrintWriter(System.out);\n        TraceClassVisitor cv = new TraceClassVisitor(null, printWriter);\n        cv.visit(V1_7, ACC_PUBLIC+ACC_ABSTRACT+ACC_INTERFACE, \"sample/HelloWorld\",\n                null, \"java/lang/Object\", new String[]{\"java/io/Serializable\"});\n        cv.visitField(ACC_PUBLIC+ACC_FINAL+ACC_STATIC, \"LESS\", \"I\",\n                null, new Integer(-1)).visitEnd();\n        cv.visitField(ACC_PUBLIC+ACC_FINAL+ACC_STATIC, \"EQUAL\", \"I\",\n                null, new Integer(0)).visitEnd();\n        cv.visitField(ACC_PUBLIC+ACC_FINAL+ACC_STATIC, \"GREATER\", \"I\",\n                null, new Integer(1)).visitEnd();\n        cv.visitMethod(ACC_PUBLIC+ACC_ABSTRACT, \"compareTo\", \"(Ljava/lang/Object;)I\",\n                null, null).visitEnd();\n        cv.visitEnd();\n    }\n}\n"
  },
  {
    "path": "src/main/java/lsieun/asm/util/TreePrinter.java",
    "content": "package lsieun.asm.util;\n\nimport org.objectweb.asm.*;\nimport org.objectweb.asm.util.Printer;\n\nimport java.util.Collections;\nimport java.util.Formatter;\nimport java.util.HashMap;\nimport java.util.Map;\n\npublic class TreePrinter extends Printer {\n    private static final int ACCESS_CLASS = 0x40000;\n    private static final int ACCESS_FIELD = 0x80000;\n    private static final int ACCESS_INNER = 0x100000;\n    private static final int ACCESS_MODULE = 0x200000;\n\n    private static final String COMMA = \"\\\", \\\"\";\n    private static final String NEW_OBJECT_ARRAY = \", new Object[] {\";\n    private static final String END_ARRAY = \" }\";\n\n    private static final Map<Integer, String> CLASS_VERSIONS;\n\n    static {\n        HashMap<Integer, String> classVersions = new HashMap<>();\n        classVersions.put(Opcodes.V1_1, \"V1_1\");\n        classVersions.put(Opcodes.V1_2, \"V1_2\");\n        classVersions.put(Opcodes.V1_3, \"V1_3\");\n        classVersions.put(Opcodes.V1_4, \"V1_4\");\n        classVersions.put(Opcodes.V1_5, \"V1_5\");\n        classVersions.put(Opcodes.V1_6, \"V1_6\");\n        classVersions.put(Opcodes.V1_7, \"V1_7\");\n        classVersions.put(Opcodes.V1_8, \"V1_8\");\n        classVersions.put(Opcodes.V9, \"V9\");\n        classVersions.put(Opcodes.V10, \"V10\");\n        classVersions.put(Opcodes.V11, \"V11\");\n        classVersions.put(Opcodes.V12, \"V12\");\n        classVersions.put(Opcodes.V13, \"V13\");\n        classVersions.put(Opcodes.V14, \"V14\");\n        classVersions.put(Opcodes.V15, \"V15\");\n        classVersions.put(Opcodes.V16, \"V16\");\n        CLASS_VERSIONS = Collections.unmodifiableMap(classVersions);\n    }\n\n    protected final String name;\n\n    protected Map<Label, String> labelNames;\n\n    public TreePrinter() {\n        this(Opcodes.ASM9, \"classNode\");\n    }\n\n    public TreePrinter(int api, String visitorVariableName) {\n        super(api);\n        this.name = visitorVariableName;\n    }\n\n    @Override\n    public void visit(int version, int access, String name, String signature, String superName, String[] interfaces) {\n        String simpleName;\n        if (name == null) {\n            simpleName = \"module-info\";\n        }\n        else {\n            int lastSlashIndex = name.lastIndexOf('/');\n            if (lastSlashIndex == -1) {\n                simpleName = name;\n            }\n            else {\n                text.add(\"package asm.\" + name.substring(0, lastSlashIndex).replace('/', '.') + \";\\n\");\n                simpleName = name.substring(lastSlashIndex + 1).replaceAll(\"[-\\\\(\\\\)]\", \"_\");\n            }\n        }\n\n        stringBuilder.setLength(0);\n        Formatter fm = new Formatter(stringBuilder);\n        String versionString = CLASS_VERSIONS.get(version);\n        fm.format(\"import org.objectweb.asm.*;%n\");\n        fm.format(\"import org.objectweb.asm.tree.*;%n%n\");\n        fm.format(\"public class %sDump implements Opcodes {%n%n\", simpleName);\n        fm.format(\"public static byte[] dump () throws Exception {%n\");\n        fm.format(\"    ClassNode cn = new ClassNode();%n\");\n        fm.format(\"    %s = %s;%n\", \"cn.version\", versionString);\n        fm.format(\"    %s = %s;%n\", \"cn.access\", toAccessFlags(access | ACCESS_CLASS));\n        fm.format(\"    %s = %s;%n\", \"cn.name\", toConstant(name));\n        fm.format(\"    %s = %s;%n\", \"cn.signature\", toConstant(signature));\n        fm.format(\"    %s = %s;%n\", \"cn.superName\", toConstant(superName));\n        if (interfaces != null) {\n            for (String item : interfaces) {\n                fm.format(\"    cn.interfaces.add(%s);%n\", toConstant(item));\n            }\n        }\n        fm.format(\"%n\");\n\n        text.add(stringBuilder.toString());\n    }\n\n    @Override\n    public void visitSource(String source, String debug) {\n        // do nothing\n    }\n\n    @Override\n    public void visitOuterClass(String owner, String name, String descriptor) {\n        // do nothing\n    }\n\n    @Override\n    public TreePrinter visitClassAnnotation(String descriptor, boolean visible) {\n        return null;\n    }\n\n    @Override\n    public void visitClassAttribute(Attribute attribute) {\n        // do nothing\n    }\n\n    @Override\n    public void visitInnerClass(String name, String outerName, String innerName, int access) {\n        // do nothing\n    }\n\n    @Override\n    public TreePrinter visitField(int access, String name, String descriptor, String signature, Object value) {\n        text.add(\"    {\" + System.lineSeparator());\n\n        String accessFlags = toAccessFlags(access | ACCESS_FIELD);\n\n        stringBuilder.setLength(0);\n        Formatter fm = new Formatter(stringBuilder);\n        fm.format(\"        FieldNode fieldNode = new FieldNode(%s, %s, %s, %s, %s);%n\", accessFlags, toConstant(name), toConstant(descriptor), toConstant(signature), toConstant(value));\n        fm.format(\"        cn.fields.add(fieldNode);%n\");\n        text.add(stringBuilder.toString());\n\n        TreePrinter printer = createVariable(\"fieldNode\");\n        text.add(printer.getText());\n        text.add(\"    }\" + System.lineSeparator() + System.lineSeparator());\n        return printer;\n    }\n\n    @Override\n    public TreePrinter visitMethod(int access, String name, String descriptor, String signature, String[] exceptions) {\n        text.add(\"    {\" + System.lineSeparator());\n\n        String exceptionString = \"null\";\n        if (exceptions != null && exceptions.length > 0) {\n            exceptionString = \"new String[] {\";\n            for (int i = 0; i < exceptions.length; ++i) {\n                exceptionString += (i == 0 ? \" \" : \", \");\n                exceptionString += toConstant(exceptions[i]);\n            }\n            exceptionString += \" }\";\n        }\n        String accessFlags = toAccessFlags(access);\n        stringBuilder.setLength(0);\n        Formatter fm = new Formatter(stringBuilder);\n        fm.format(\"        MethodNode methodNode = new MethodNode(%s, %s, %s, %s, %s);%n\", accessFlags, toConstant(name), toConstant(descriptor), toConstant(signature), exceptionString);\n        fm.format(\"        cn.methods.add(methodNode);%n\");\n\n        boolean isAbstractMethod = (access & Opcodes.ACC_ABSTRACT) != 0;\n        if (!isAbstractMethod) {\n            fm.format(\"%n\");\n            fm.format(\"        InsnList il = methodNode.instructions;%n\");\n        }\n        text.add(stringBuilder.toString());\n\n        TreePrinter printer = createVariable(\"methodNode\");\n        text.add(printer.getText());\n\n        text.add(\"    }\" + System.lineSeparator() + System.lineSeparator());\n        return printer;\n    }\n\n    @Override\n    public void visitClassEnd() {\n        // do nothing\n        stringBuilder.setLength(0);\n        Formatter fm = new Formatter(stringBuilder);\n        fm.format(\"    ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_FRAMES);%n\");\n        fm.format(\"    cn.accept(cw);%n\");\n        fm.format(\"    return cw.toByteArray();%n\");\n\n        text.add(stringBuilder.toString());\n\n        text.add(\"}\" + System.lineSeparator());\n        text.add(\"}\");\n    }\n\n    @Override\n    public void visit(String name, Object value) {\n        // do nothing\n    }\n\n    @Override\n    public void visitEnum(String name, String descriptor, String value) {\n        // do nothing\n    }\n\n    @Override\n    public TreePrinter visitAnnotation(String name, String descriptor) {\n        return null;\n    }\n\n    @Override\n    public TreePrinter visitArray(String name) {\n        return null;\n    }\n\n    @Override\n    public void visitAnnotationEnd() {\n        // do nothing\n    }\n\n    @Override\n    public TreePrinter visitFieldAnnotation(String descriptor, boolean visible) {\n        return null;\n    }\n\n    @Override\n    public void visitFieldAttribute(Attribute attribute) {\n        // do nothing\n    }\n\n    @Override\n    public void visitFieldEnd() {\n        // do nothing\n    }\n\n    @Override\n    public TreePrinter visitAnnotationDefault() {\n        return null;\n    }\n\n    @Override\n    public TreePrinter visitMethodAnnotation(String descriptor, boolean visible) {\n        return null;\n    }\n\n    @Override\n    public TreePrinter visitParameterAnnotation(int parameter, String descriptor, boolean visible) {\n        return null;\n    }\n\n    @Override\n    public void visitMethodAttribute(Attribute attribute) {\n        // do nothing\n    }\n\n    @Override\n    public void visitCode() {\n        // do nothing\n    }\n\n    @Override\n    public void visitFrame(int type, int numLocal, Object[] local, int numStack, Object[] stack) {\n        // do nothing\n    }\n\n    @Override\n    public void visitInsn(int opcode) {\n        stringBuilder.setLength(0);\n        Formatter fm = new Formatter(stringBuilder);\n        fm.format(\"        il.add(new InsnNode(%s));%n\", OPCODES[opcode]);\n        text.add(stringBuilder.toString());\n    }\n\n    @Override\n    public void visitIntInsn(int opcode, int operand) {\n        stringBuilder.setLength(0);\n        Formatter fm = new Formatter(stringBuilder);\n        fm.format(\"        il.add(new IntInsnNode(%s, %s));%n\", OPCODES[opcode], opcode == Opcodes.NEWARRAY ? TYPES[operand] : Integer.toString(operand));\n        text.add(stringBuilder.toString());\n    }\n\n    @Override\n    public void visitVarInsn(int opcode, int var) {\n        stringBuilder.setLength(0);\n        Formatter fm = new Formatter(stringBuilder);\n        fm.format(\"        il.add(new VarInsnNode(%s, %s));%n\", OPCODES[opcode], var);\n        text.add(stringBuilder.toString());\n    }\n\n    @Override\n    public void visitTypeInsn(int opcode, String type) {\n        stringBuilder.setLength(0);\n        Formatter fm = new Formatter(stringBuilder);\n        fm.format(\"        il.add(new TypeInsnNode(%s, %s));%n\", OPCODES[opcode], toConstant(type));\n        text.add(stringBuilder.toString());\n    }\n\n    @Override\n    public void visitFieldInsn(int opcode, String owner, String name, String descriptor) {\n        stringBuilder.setLength(0);\n        Formatter fm = new Formatter(stringBuilder);\n        fm.format(\"        il.add(new FieldInsnNode(%s, %s, %s, %s));%n\", OPCODES[opcode], toConstant(owner), toConstant(name), toConstant(descriptor));\n        text.add(stringBuilder.toString());\n    }\n\n    @Override\n    public void visitMethodInsn(int opcode, String owner, String name, String descriptor, boolean isInterface) {\n        stringBuilder.setLength(0);\n        Formatter fm = new Formatter(stringBuilder);\n        fm.format(\"        il.add(new MethodInsnNode(%s, %s, %s, %s, %s));%n\", OPCODES[opcode], toConstant(owner), toConstant(name), toConstant(descriptor), isInterface ? \"true\" : \"false\");\n        text.add(stringBuilder.toString());\n    }\n\n    @Override\n    public void visitInvokeDynamicInsn(String name, String descriptor, Handle bootstrapMethodHandle, Object... bootstrapMethodArguments) {\n        String bootstrapMethodArgumentsString = \"new Object[]{\";\n\n        for (int i = 0; i < bootstrapMethodArguments.length; ++i) {\n            bootstrapMethodArgumentsString += toConstant(bootstrapMethodArguments[i]);\n            if (i != bootstrapMethodArguments.length - 1) {\n                bootstrapMethodArgumentsString += \", \";\n            }\n        }\n        bootstrapMethodArgumentsString += \"}\";\n\n        stringBuilder.setLength(0);\n        Formatter fm = new Formatter(stringBuilder);\n        fm.format(\"        il.add(new InvokeDynamicInsnNode(%s, %s, %s, %s));%n\", toConstant(name), toConstant(descriptor), toConstant(bootstrapMethodHandle), bootstrapMethodArgumentsString);\n        text.add(stringBuilder.toString());\n    }\n\n    @Override\n    public void visitJumpInsn(int opcode, Label label) {\n        String declareLabel = declareLabelNode(label);\n        String labelName = toLabelName(label);\n        stringBuilder.setLength(0);\n        Formatter fm = new Formatter(stringBuilder);\n        if (declareLabel != null && !\"\".equals(declareLabel)) {\n            fm.format(\"        %s%n\", declareLabel);\n        }\n\n        fm.format(\"        il.add(new JumpInsnNode(%s, %s));%n\", OPCODES[opcode], labelName);\n        text.add(stringBuilder.toString());\n    }\n\n    @Override\n    public void visitLabel(Label label) {\n        String labelName = toLabelName(label);\n        stringBuilder.setLength(0);\n        Formatter fm = new Formatter(stringBuilder);\n        fm.format(\"%n\");\n        fm.format(\"        il.add(%s);%n\", labelName);\n        text.add(stringBuilder.toString());\n    }\n\n    @Override\n    public void visitLdcInsn(Object value) {\n        stringBuilder.setLength(0);\n        Formatter fm = new Formatter(stringBuilder);\n        fm.format(\"        il.add(new LdcInsnNode(%s));%n\", toConstant(value));\n        text.add(stringBuilder.toString());\n    }\n\n    @Override\n    public void visitIincInsn(int var, int increment) {\n        stringBuilder.setLength(0);\n        Formatter fm = new Formatter(stringBuilder);\n        fm.format(\"        il.add(new IincInsnNode(%s, %s));%n\", var, increment);\n        text.add(stringBuilder.toString());\n    }\n\n    @Override\n    public void visitTableSwitchInsn(int min, int max, Label dflt, Label... labels) {\n        stringBuilder.setLength(0);\n        Formatter fm = new Formatter(stringBuilder);\n\n        for (Label label : labels) {\n            String declareLabel = declareLabelNode(label);\n            fm.format(\"        %s%n\", declareLabel);\n        }\n        String defautLabel = declareLabelNode(dflt);\n\n        StringBuilder labelsString = new StringBuilder(\"new LabelNode[] {\");\n        for (int i = 0; i < labels.length; ++i) {\n            labelsString.append(i == 0 ? \" \" : \", \");\n            labelsString.append(toLabelName(labels[i]));\n        }\n        labelsString.append(END_ARRAY);\n\n        fm.format(\"        %s%n\", defautLabel);\n        fm.format(\"        il.add(new TableSwitchInsnNode(%s, %s, %s, %s));%n\", min, max, toLabelName(dflt), labelsString);\n        text.add(stringBuilder.toString());\n    }\n\n    @Override\n    public void visitLookupSwitchInsn(Label dflt, int[] keys, Label[] labels) {\n        stringBuilder.setLength(0);\n        Formatter fm = new Formatter(stringBuilder);\n\n        StringBuilder keyString = new StringBuilder(\"new int[] {\");\n        for (int i = 0; i < keys.length; ++i) {\n            keyString.append(i == 0 ? \" \" : \", \");\n            keyString.append(keys[i]);\n        }\n        keyString.append(\" }\");\n\n        for (Label label : labels) {\n            String declareLabel = declareLabelNode(label);\n            fm.format(\"        %s%n\", declareLabel);\n        }\n        String defautLabel = declareLabelNode(dflt);\n\n        StringBuilder labelsString = new StringBuilder(\"new LabelNode[] {\");\n        for (int i = 0; i < labels.length; ++i) {\n            labelsString.append(i == 0 ? \" \" : \", \");\n            labelsString.append(toLabelName(labels[i]));\n        }\n        labelsString.append(END_ARRAY);\n\n        fm.format(\"        %s%n\", defautLabel);\n        fm.format(\"        il.add(new LookupSwitchInsnNode(%s, %s, %s));%n\", toLabelName(dflt), keyString, labelsString);\n        text.add(stringBuilder.toString());\n    }\n\n    @Override\n    public void visitMultiANewArrayInsn(String descriptor, int numDimensions) {\n        stringBuilder.setLength(0);\n        Formatter fm = new Formatter(stringBuilder);\n        fm.format(\"        il.add(new MultiANewArrayInsnNode(%s, %s));%n\", toConstant(descriptor), numDimensions);\n        text.add(stringBuilder.toString());\n    }\n\n    @Override\n    public void visitTryCatchBlock(Label start, Label end, Label handler, String type) {\n        stringBuilder.setLength(0);\n        Formatter fm = new Formatter(stringBuilder);\n        String startLabelNodeString = declareLabelNode(start);\n        String endLabelNodeString = declareLabelNode(end);\n        String handlerLabelNodeString = declareLabelNode(handler);\n        if (startLabelNodeString != null && !\"\".equals(startLabelNodeString)) {\n            fm.format(\"        %s%n\", startLabelNodeString);\n        }\n        if (endLabelNodeString != null && !\"\".equals(endLabelNodeString)) {\n            fm.format(\"        %s%n\", endLabelNodeString);\n        }\n        if (handlerLabelNodeString != null && !\"\".equals(handlerLabelNodeString)) {\n            fm.format(\"        %s%n\", handlerLabelNodeString);\n        }\n\n        fm.format(\"        methodNode.tryCatchBlocks.add(new TryCatchBlockNode(%s, %s, %s, %s));%n\", toLabelName(start), toLabelName(end), toLabelName(handler), toConstant(type));\n        text.add(stringBuilder.toString());\n    }\n\n    @Override\n    public void visitLocalVariable(String name, String descriptor, String signature, Label start, Label end, int index) {\n        // do nothing\n    }\n\n    @Override\n    public void visitLineNumber(int line, Label start) {\n        // do nothing\n    }\n\n    @Override\n    public void visitMaxs(int maxStack, int maxLocals) {\n        stringBuilder.setLength(0);\n        Formatter fm = new Formatter(stringBuilder);\n        fm.format(\"        %n\");\n        fm.format(\"        methodNode.maxStack = %s;%n\", maxStack);\n        fm.format(\"        methodNode.maxLocals = %s;%n\", maxLocals);\n        text.add(stringBuilder.toString());\n    }\n\n    @Override\n    public void visitMethodEnd() {\n        // do nothing\n    }\n\n    // -----------------------------------------------------------------------------------------------\n    // Utility methods\n    // -----------------------------------------------------------------------------------------------\n    protected TreePrinter createVariable(String visitorVariableName) {\n        return new TreePrinter(api, visitorVariableName);\n    }\n\n    private String toAccessFlags(final int accessFlags) {\n        StringBuilder sb = new StringBuilder();\n        boolean isEmpty = true;\n        if ((accessFlags & Opcodes.ACC_PUBLIC) != 0) {\n            sb.append(\"ACC_PUBLIC\");\n            isEmpty = false;\n        }\n        if ((accessFlags & Opcodes.ACC_PRIVATE) != 0) {\n            sb.append(\"ACC_PRIVATE\");\n            isEmpty = false;\n        }\n        if ((accessFlags & Opcodes.ACC_PROTECTED) != 0) {\n            sb.append(\"ACC_PROTECTED\");\n            isEmpty = false;\n        }\n        if ((accessFlags & Opcodes.ACC_FINAL) != 0) {\n            if (!isEmpty) {\n                sb.append(\" | \");\n            }\n            if ((accessFlags & ACCESS_MODULE) == 0) {\n                sb.append(\"ACC_FINAL\");\n            }\n            else {\n                sb.append(\"ACC_TRANSITIVE\");\n            }\n            isEmpty = false;\n        }\n        if ((accessFlags & Opcodes.ACC_STATIC) != 0) {\n            if (!isEmpty) {\n                sb.append(\" | \");\n            }\n            sb.append(\"ACC_STATIC\");\n            isEmpty = false;\n        }\n        if ((accessFlags & (Opcodes.ACC_SYNCHRONIZED | Opcodes.ACC_SUPER | Opcodes.ACC_TRANSITIVE))\n                != 0) {\n            if (!isEmpty) {\n                sb.append(\" | \");\n            }\n            if ((accessFlags & ACCESS_CLASS) == 0) {\n                if ((accessFlags & ACCESS_MODULE) == 0) {\n                    sb.append(\"ACC_SYNCHRONIZED\");\n                }\n                else {\n                    sb.append(\"ACC_TRANSITIVE\");\n                }\n            }\n            else {\n                sb.append(\"ACC_SUPER\");\n            }\n            isEmpty = false;\n        }\n        if ((accessFlags & (Opcodes.ACC_VOLATILE | Opcodes.ACC_BRIDGE | Opcodes.ACC_STATIC_PHASE))\n                != 0) {\n            if (!isEmpty) {\n                sb.append(\" | \");\n            }\n            if ((accessFlags & ACCESS_FIELD) == 0) {\n                if ((accessFlags & ACCESS_MODULE) == 0) {\n                    sb.append(\"ACC_BRIDGE\");\n                }\n                else {\n                    sb.append(\"ACC_STATIC_PHASE\");\n                }\n            }\n            else {\n                sb.append(\"ACC_VOLATILE\");\n            }\n            isEmpty = false;\n        }\n        if ((accessFlags & Opcodes.ACC_VARARGS) != 0\n                && (accessFlags & (ACCESS_CLASS | ACCESS_FIELD)) == 0) {\n            if (!isEmpty) {\n                sb.append(\" | \");\n            }\n            sb.append(\"ACC_VARARGS\");\n            isEmpty = false;\n        }\n        if ((accessFlags & Opcodes.ACC_TRANSIENT) != 0 && (accessFlags & ACCESS_FIELD) != 0) {\n            if (!isEmpty) {\n                sb.append(\" | \");\n            }\n            sb.append(\"ACC_TRANSIENT\");\n            isEmpty = false;\n        }\n        if ((accessFlags & Opcodes.ACC_NATIVE) != 0\n                && (accessFlags & (ACCESS_CLASS | ACCESS_FIELD)) == 0) {\n            if (!isEmpty) {\n                sb.append(\" | \");\n            }\n            sb.append(\"ACC_NATIVE\");\n            isEmpty = false;\n        }\n        if ((accessFlags & Opcodes.ACC_ENUM) != 0\n                && (accessFlags & (ACCESS_CLASS | ACCESS_FIELD | ACCESS_INNER)) != 0) {\n            if (!isEmpty) {\n                sb.append(\" | \");\n            }\n            sb.append(\"ACC_ENUM\");\n            isEmpty = false;\n        }\n        if ((accessFlags & Opcodes.ACC_ANNOTATION) != 0\n                && (accessFlags & (ACCESS_CLASS | ACCESS_INNER)) != 0) {\n            if (!isEmpty) {\n                sb.append(\" | \");\n            }\n            sb.append(\"ACC_ANNOTATION\");\n            isEmpty = false;\n        }\n        if ((accessFlags & Opcodes.ACC_ABSTRACT) != 0) {\n            if (!isEmpty) {\n                sb.append(\" | \");\n            }\n            sb.append(\"ACC_ABSTRACT\");\n            isEmpty = false;\n        }\n        if ((accessFlags & Opcodes.ACC_INTERFACE) != 0) {\n            if (!isEmpty) {\n                sb.append(\" | \");\n            }\n            sb.append(\"ACC_INTERFACE\");\n            isEmpty = false;\n        }\n        if ((accessFlags & Opcodes.ACC_STRICT) != 0) {\n            if (!isEmpty) {\n                sb.append(\" | \");\n            }\n            sb.append(\"ACC_STRICT\");\n            isEmpty = false;\n        }\n        if ((accessFlags & Opcodes.ACC_SYNTHETIC) != 0) {\n            if (!isEmpty) {\n                sb.append(\" | \");\n            }\n            sb.append(\"ACC_SYNTHETIC\");\n            isEmpty = false;\n        }\n        if ((accessFlags & Opcodes.ACC_DEPRECATED) != 0) {\n            if (!isEmpty) {\n                sb.append(\" | \");\n            }\n            sb.append(\"ACC_DEPRECATED\");\n            isEmpty = false;\n        }\n        if ((accessFlags & Opcodes.ACC_RECORD) != 0) {\n            if (!isEmpty) {\n                sb.append(\" | \");\n            }\n            sb.append(\"ACC_RECORD\");\n            isEmpty = false;\n        }\n        if ((accessFlags & (Opcodes.ACC_MANDATED | Opcodes.ACC_MODULE)) != 0) {\n            if (!isEmpty) {\n                sb.append(\" | \");\n            }\n            if ((accessFlags & ACCESS_CLASS) == 0) {\n                sb.append(\"ACC_MANDATED\");\n            }\n            else {\n                sb.append(\"ACC_MODULE\");\n            }\n            isEmpty = false;\n        }\n        if (isEmpty) {\n            sb.append('0');\n        }\n        return sb.toString();\n    }\n\n    protected String toConstant(final Object value) {\n        StringBuilder sb = new StringBuilder();\n        if (value == null) {\n            sb.append(\"null\");\n        }\n        else if (value instanceof String) {\n            appendString(sb, (String) value);\n        }\n        else if (value instanceof Type) {\n            sb.append(\"Type.getType(\\\"\");\n            sb.append(((Type) value).getDescriptor());\n            sb.append(\"\\\")\");\n        }\n        else if (value instanceof Handle) {\n            sb.append(\"new Handle(\");\n            Handle handle = (Handle) value;\n            sb.append(\"Opcodes.\").append(HANDLE_TAG[handle.getTag()]).append(\", \\\"\");\n            sb.append(handle.getOwner()).append(COMMA);\n            sb.append(handle.getName()).append(COMMA);\n            sb.append(handle.getDesc()).append(\"\\\", \");\n            sb.append(handle.isInterface()).append(\")\");\n        }\n        else if (value instanceof ConstantDynamic) {\n            sb.append(\"new ConstantDynamic(\\\"\");\n            ConstantDynamic constantDynamic = (ConstantDynamic) value;\n            sb.append(constantDynamic.getName()).append(COMMA);\n            sb.append(constantDynamic.getDescriptor()).append(\"\\\", \");\n            sb.append(toConstant(constantDynamic.getBootstrapMethod()));\n\n            sb.append(NEW_OBJECT_ARRAY);\n            int bootstrapMethodArgumentCount = constantDynamic.getBootstrapMethodArgumentCount();\n            for (int i = 0; i < bootstrapMethodArgumentCount; ++i) {\n                sb.append(toConstant(constantDynamic.getBootstrapMethodArgument(i)));\n                if (i != bootstrapMethodArgumentCount - 1) {\n                    sb.append(\", \");\n                }\n            }\n            sb.append(\"})\");\n        }\n        else if (value instanceof Byte) {\n            sb.append(\"new Byte((byte)\").append(value).append(')');\n        }\n        else if (value instanceof Boolean) {\n            sb.append(((Boolean) value).booleanValue() ? \"Boolean.TRUE\" : \"Boolean.FALSE\");\n        }\n        else if (value instanceof Short) {\n            sb.append(\"new Short((short)\").append(value).append(')');\n        }\n        else if (value instanceof Character) {\n            sb\n                    .append(\"new Character((char)\")\n                    .append((int) ((Character) value).charValue())\n                    .append(')');\n        }\n        else if (value instanceof Integer) {\n            sb.append(\"new Integer(\").append(value).append(')');\n        }\n        else if (value instanceof Float) {\n            sb.append(\"new Float(\\\"\").append(value).append(\"\\\")\");\n        }\n        else if (value instanceof Long) {\n            sb.append(\"new Long(\").append(value).append(\"L)\");\n        }\n        else if (value instanceof Double) {\n            sb.append(\"new Double(\\\"\").append(value).append(\"\\\")\");\n        }\n        else if (value instanceof byte[]) {\n            byte[] byteArray = (byte[]) value;\n            sb.append(\"new byte[] {\");\n            for (int i = 0; i < byteArray.length; i++) {\n                sb.append(i == 0 ? \"\" : \",\").append(byteArray[i]);\n            }\n            sb.append('}');\n        }\n        else if (value instanceof boolean[]) {\n            boolean[] booleanArray = (boolean[]) value;\n            sb.append(\"new boolean[] {\");\n            for (int i = 0; i < booleanArray.length; i++) {\n                sb.append(i == 0 ? \"\" : \",\").append(booleanArray[i]);\n            }\n            sb.append('}');\n        }\n        else if (value instanceof short[]) {\n            short[] shortArray = (short[]) value;\n            sb.append(\"new short[] {\");\n            for (int i = 0; i < shortArray.length; i++) {\n                sb.append(i == 0 ? \"\" : \",\").append(\"(short)\").append(shortArray[i]);\n            }\n            sb.append('}');\n        }\n        else if (value instanceof char[]) {\n            char[] charArray = (char[]) value;\n            sb.append(\"new char[] {\");\n            for (int i = 0; i < charArray.length; i++) {\n                sb.append(i == 0 ? \"\" : \",\").append(\"(char)\").append((int) charArray[i]);\n            }\n            sb.append('}');\n        }\n        else if (value instanceof int[]) {\n            int[] intArray = (int[]) value;\n            sb.append(\"new int[] {\");\n            for (int i = 0; i < intArray.length; i++) {\n                sb.append(i == 0 ? \"\" : \",\").append(intArray[i]);\n            }\n            sb.append('}');\n        }\n        else if (value instanceof long[]) {\n            long[] longArray = (long[]) value;\n            sb.append(\"new long[] {\");\n            for (int i = 0; i < longArray.length; i++) {\n                sb.append(i == 0 ? \"\" : \",\").append(longArray[i]).append('L');\n            }\n            sb.append('}');\n        }\n        else if (value instanceof float[]) {\n            float[] floatArray = (float[]) value;\n            sb.append(\"new float[] {\");\n            for (int i = 0; i < floatArray.length; i++) {\n                sb.append(i == 0 ? \"\" : \",\").append(floatArray[i]).append('f');\n            }\n            sb.append('}');\n        }\n        else if (value instanceof double[]) {\n            double[] doubleArray = (double[]) value;\n            sb.append(\"new double[] {\");\n            for (int i = 0; i < doubleArray.length; i++) {\n                sb.append(i == 0 ? \"\" : \",\").append(doubleArray[i]).append('d');\n            }\n            sb.append('}');\n        }\n        return sb.toString();\n    }\n\n    protected String declareLabelNode(final Label label) {\n        if (labelNames == null) {\n            labelNames = new HashMap<>();\n        }\n\n        String labelName = labelNames.get(label);\n        if (labelName == null) {\n            labelName = \"labelNode\" + labelNames.size();\n            labelNames.put(label, labelName);\n            StringBuilder sb = new StringBuilder();\n            sb.append(\"LabelNode \").append(labelName).append(\" = new LabelNode();\");\n            return sb.toString();\n        }\n        return \"\";\n    }\n\n    protected String toLabelName(final Label label) {\n        return labelNames.get(label);\n    }\n}\n"
  },
  {
    "path": "src/main/java/lsieun/classfile/CPInfo.java",
    "content": "package lsieun.classfile;\n\npublic class CPInfo {\n    static final int CONSTANT_UTF8_TAG = 1;\n\n    static final int CONSTANT_INTEGER_TAG = 3;\n    static final int CONSTANT_FLOAT_TAG = 4;\n    static final int CONSTANT_LONG_TAG = 5;\n    static final int CONSTANT_DOUBLE_TAG = 6;\n    static final int CONSTANT_CLASS_TAG = 7;\n    static final int CONSTANT_STRING_TAG = 8;\n    static final int CONSTANT_FIELDREF_TAG = 9;\n    static final int CONSTANT_METHODREF_TAG = 10;\n    static final int CONSTANT_INTERFACE_METHODREF_TAG = 11;\n    static final int CONSTANT_NAME_AND_TYPE_TAG = 12;\n\n\n    static final int CONSTANT_METHOD_HANDLE_TAG = 15;\n    static final int CONSTANT_METHOD_TYPE_TAG = 16;\n    static final int CONSTANT_DYNAMIC_TAG = 17;\n    static final int CONSTANT_INVOKE_DYNAMIC_TAG = 18;\n    static final int CONSTANT_MODULE_TAG = 19;\n    static final int CONSTANT_PACKAGE_TAG = 20;\n}\n"
  },
  {
    "path": "src/main/java/lsieun/classfile/ClassFile.java",
    "content": "package lsieun.classfile;\n\nimport java.util.Arrays;\n\npublic class ClassFile {\n    private final byte[] classFileBuffer;\n\n    private final int constantPoolCount;\n    private final int[] cpInfoOffsets;\n\n    private final int header;\n\n    private final int interfaces_count_offset;\n    public final int interfacesCount;\n\n    private final int fields_count_offset;\n    public final int fieldCount;\n    private final int[] fieldInfoOffsets;\n\n    private final int methods_count_offset;\n    public final int methodCount;\n    private final int[] methodInfoOffsets;\n\n    private final int attributes_count_offset;\n    public final int attributeCount;\n    private final int[] attributeInfoOffsets;\n\n    public ClassFile(byte[] classFileBuffer) {\n        this(classFileBuffer, 0, classFileBuffer.length);\n    }\n\n    public ClassFile(byte[] classFileBuffer, int classFileOffset, int classFileLength) {\n        this.classFileBuffer = classFileBuffer;\n        this.constantPoolCount = readUnsignedShort(classFileOffset + 8);\n        this.cpInfoOffsets = new int[constantPoolCount];\n\n        int currentCpInfoIndex = 1;\n        int currentCpInfoOffset = classFileOffset + 10;\n\n        while (currentCpInfoIndex < constantPoolCount) {\n            this.cpInfoOffsets[currentCpInfoIndex++] = currentCpInfoOffset + 1;\n            int cpInfoSize;\n            switch (classFileBuffer[currentCpInfoOffset]) {\n                case CPInfo.CONSTANT_FIELDREF_TAG:\n                case CPInfo.CONSTANT_METHODREF_TAG:\n                case CPInfo.CONSTANT_INTERFACE_METHODREF_TAG:\n                case CPInfo.CONSTANT_INTEGER_TAG:\n                case CPInfo.CONSTANT_FLOAT_TAG:\n                case CPInfo.CONSTANT_NAME_AND_TYPE_TAG:\n                    cpInfoSize = 5;\n                    break;\n                case CPInfo.CONSTANT_DYNAMIC_TAG:\n                    cpInfoSize = 5;\n                    break;\n                case CPInfo.CONSTANT_INVOKE_DYNAMIC_TAG:\n                    cpInfoSize = 5;\n                    break;\n                case CPInfo.CONSTANT_LONG_TAG:\n                case CPInfo.CONSTANT_DOUBLE_TAG:\n                    cpInfoSize = 9;\n                    currentCpInfoIndex++;\n                    break;\n                case CPInfo.CONSTANT_UTF8_TAG:\n                    cpInfoSize = 3 + readUnsignedShort(currentCpInfoOffset + 1);\n                    break;\n                case CPInfo.CONSTANT_METHOD_HANDLE_TAG:\n                    cpInfoSize = 4;\n                    break;\n                case CPInfo.CONSTANT_CLASS_TAG:\n                case CPInfo.CONSTANT_STRING_TAG:\n                case CPInfo.CONSTANT_METHOD_TYPE_TAG:\n                case CPInfo.CONSTANT_PACKAGE_TAG:\n                case CPInfo.CONSTANT_MODULE_TAG:\n                    cpInfoSize = 3;\n                    break;\n                default:\n                    throw new IllegalArgumentException();\n            }\n            currentCpInfoOffset += cpInfoSize;\n        }\n\n        // The Classfile's access_flags field is just after the last constant pool entry.\n        this.header = currentCpInfoOffset;\n\n        // interfaces_count is after the access_flags, this_class and super_class fields (2 bytes each).\n        int currentOffset = header + 6;\n        this.interfaces_count_offset = currentOffset;\n        this.interfacesCount = readUnsignedShort(currentOffset);\n\n        currentOffset += 2 + interfacesCount * 2;\n        this.fields_count_offset = currentOffset;\n        this.fieldCount = readUnsignedShort(currentOffset);\n        this.fieldInfoOffsets = new int[fieldCount];\n\n        currentOffset += 2;\n        for (int i = 0; i < this.fieldCount; i++) {\n            this.fieldInfoOffsets[i] = currentOffset;\n\n            int count = readUnsignedShort(currentOffset + 6);\n\n            currentOffset += 8;\n            for (int j = 0; j < count; j++) {\n                int size = getAttributeSize(currentOffset);\n                currentOffset += size;\n            }\n        }\n\n        this.methods_count_offset = currentOffset;\n        this.methodCount = readUnsignedShort(currentOffset);\n        this.methodInfoOffsets = new int[methodCount];\n\n        currentOffset += 2;\n        for (int i = 0; i < this.methodCount; i++) {\n            this.methodInfoOffsets[i] = currentOffset;\n\n            int count = readUnsignedShort(currentOffset + 6);\n            currentOffset += 8;\n            for (int j = 0; j < count; j++) {\n                int size = getAttributeSize(currentOffset);\n                currentOffset += size;\n            }\n        }\n\n        this.attributes_count_offset = currentOffset;\n        this.attributeCount = readUnsignedShort(currentOffset);\n        this.attributeInfoOffsets = new int[attributeCount];\n\n        currentOffset += 2;\n        for (int i = 0; i < this.attributeCount; i++) {\n            this.attributeInfoOffsets[i] = currentOffset;\n            int size = getAttributeSize(currentOffset);\n            currentOffset += size;\n        }\n    }\n\n    public byte[] getCode(int methodIndex) {\n        int currentOffset = methodInfoOffsets[methodIndex];\n        int count = readUnsignedShort(currentOffset + 6);\n\n        currentOffset += 8;\n        for (int i = 0; i < count; i++) {\n            int attribute_name_index = readUnsignedShort(currentOffset);\n            String attrName = readUTF8(attribute_name_index);\n            if (\"Code\".equals(attrName)) {\n                break;\n            }\n\n            int size = getAttributeSize(currentOffset);\n            currentOffset += size;\n        }\n\n        currentOffset += 10;\n        int code_length = readInt(currentOffset);\n\n        currentOffset += 4;\n        return Arrays.copyOfRange(classFileBuffer, currentOffset, currentOffset + code_length);\n    }\n\n    private int getAttributeSize(int attribute_info_offset) {\n        int size = 6;\n        int attribute_length = readInt(attribute_info_offset + 2);\n        size += attribute_length;\n        return size;\n    }\n\n    public int readByte(final int offset) {\n        return classFileBuffer[offset] & 0xFF;\n    }\n\n    public int readUnsignedShort(final int offset) {\n        byte[] classBuffer = classFileBuffer;\n        return ((classBuffer[offset] & 0xFF) << 8) | (classBuffer[offset + 1] & 0xFF);\n    }\n\n    public short readShort(final int offset) {\n        byte[] classBuffer = classFileBuffer;\n        return (short) (((classBuffer[offset] & 0xFF) << 8) | (classBuffer[offset + 1] & 0xFF));\n    }\n\n    public int readInt(final int offset) {\n        byte[] classBuffer = classFileBuffer;\n        return ((classBuffer[offset] & 0xFF) << 24)\n                | ((classBuffer[offset + 1] & 0xFF) << 16)\n                | ((classBuffer[offset + 2] & 0xFF) << 8)\n                | (classBuffer[offset + 3] & 0xFF);\n    }\n\n    public long readLong(final int offset) {\n        long l1 = readInt(offset);\n        long l0 = readInt(offset + 4) & 0xFFFFFFFFL;\n        return (l1 << 32) | l0;\n    }\n\n    private String readUTF8(int cp_info_index) {\n        int utfOffset = cpInfoOffsets[cp_info_index];\n        int utfLength = readUnsignedShort(utfOffset);\n        return readUTF8(utfOffset + 2, utfLength);\n    }\n\n    private String readUTF8(final int utfOffset, final int utfLength) {\n        char[] charBuffer = new char[utfLength];\n        int currentOffset = utfOffset;\n        int endOffset = currentOffset + utfLength;\n        int strLength = 0;\n        byte[] classBuffer = classFileBuffer;\n        while (currentOffset < endOffset) {\n            int currentByte = classBuffer[currentOffset++];\n            if ((currentByte & 0x80) == 0) {\n                charBuffer[strLength++] = (char) (currentByte & 0x7F);\n            }\n            else if ((currentByte & 0xE0) == 0xC0) {\n                charBuffer[strLength++] =\n                        (char) (((currentByte & 0x1F) << 6) + (classBuffer[currentOffset++] & 0x3F));\n            }\n            else {\n                charBuffer[strLength++] =\n                        (char)\n                                (((currentByte & 0xF) << 12)\n                                        + ((classBuffer[currentOffset++] & 0x3F) << 6)\n                                        + (classBuffer[currentOffset++] & 0x3F));\n            }\n        }\n        return new String(charBuffer, 0, strLength);\n    }\n}\n"
  },
  {
    "path": "src/main/java/lsieun/classfile/InsnRaw.java",
    "content": "package lsieun.classfile;\n\nimport java.util.ArrayList;\nimport java.util.Formatter;\nimport java.util.List;\n\npublic class InsnRaw {\n    private static final String NO_ARG_FORMAT = \"%04d: %-20s\";\n    private static final String ONE_ARG_FORMAT = \"%04d: %-15s %-4s\";\n    private static final String TWO_ARG_FORMAT = \"%04d: %-10s %-4s %-4s\";\n    private static final String CP_INS_FORMAT = \"%04d: %-15s #%-3s\";\n    private static final String NEW_ARRAY_FORMAT = \"%04d: %-10s %-9s\";\n    private static final String SWITCH_FORMAT = \"%04d: %-20s%n\";\n    private static final String SWITCH_START_FORMAT = \"      {%n\";\n    private static final String CASE_FORMAT = \"      %9s: %d%n\";\n    private static final String SWITCH_STOP_FORMAT = \"      }\";\n\n    private final byte[] code_bytes;\n\n    public InsnRaw(byte[] code_bytes) {\n        this.code_bytes = code_bytes;\n    }\n\n    @SuppressWarnings(\"Duplicates\")\n    public List<String> getList() {\n        List<String> list = new ArrayList<>();\n\n        int length = code_bytes.length;\n\n        boolean wide = false;\n        int currentOffSet = 0;\n\n        while (currentOffSet < length) {\n            int opcode = readUnsignedByte(currentOffSet);\n\n            final String item;\n            final int size;\n            switch (opcode) {\n                case 0: // nop\n                {\n                    item = toNoArgIns(currentOffSet, \"nop\");\n                    size = 1;\n                    break;\n                }\n                case 1: // aconst_null\n                {\n                    item = toNoArgIns(currentOffSet, \"aconst_null\");\n                    size = 1;\n                    break;\n                }\n                case 2: // iconst_m1\n                {\n                    item = toNoArgIns(currentOffSet, \"iconst_m1\");\n                    size = 1;\n                    break;\n                }\n                case 3: // iconst_0\n                {\n                    item = toNoArgIns(currentOffSet, \"iconst_0\");\n                    size = 1;\n                    break;\n                }\n                case 4: // iconst_1\n                {\n                    item = toNoArgIns(currentOffSet, \"iconst_1\");\n                    size = 1;\n                    break;\n                }\n                case 5: // iconst_2\n                {\n                    item = toNoArgIns(currentOffSet, \"iconst_2\");\n                    size = 1;\n                    break;\n                }\n                case 6: // iconst_3\n                {\n                    item = toNoArgIns(currentOffSet, \"iconst_3\");\n                    size = 1;\n                    break;\n                }\n                case 7: // iconst_4\n                {\n                    item = toNoArgIns(currentOffSet, \"iconst_4\");\n                    size = 1;\n                    break;\n                }\n                case 8: // iconst_5\n                {\n                    item = toNoArgIns(currentOffSet, \"iconst_5\");\n                    size = 1;\n                    break;\n                }\n                case 9: // lconst_0\n                {\n                    item = toNoArgIns(currentOffSet, \"lconst_0\");\n                    size = 1;\n                    break;\n                }\n                case 10: // lconst_1\n                {\n                    item = toNoArgIns(currentOffSet, \"lconst_1\");\n                    size = 1;\n                    break;\n                }\n                case 11: // fconst_0\n                {\n                    item = toNoArgIns(currentOffSet, \"fconst_0\");\n                    size = 1;\n                    break;\n                }\n                case 12: // fconst_1\n                {\n                    item = toNoArgIns(currentOffSet, \"fconst_1\");\n                    size = 1;\n                    break;\n                }\n                case 13: // fconst_2\n                {\n                    item = toNoArgIns(currentOffSet, \"fconst_2\");\n                    size = 1;\n                    break;\n                }\n                case 14: // dconst_0\n                {\n                    item = toNoArgIns(currentOffSet, \"dconst_0\");\n                    size = 1;\n                    break;\n                }\n                case 15: // dconst_1\n                {\n                    item = toNoArgIns(currentOffSet, \"dconst_1\");\n                    size = 1;\n                    break;\n                }\n                case 16: // bipush\n                {\n                    int value = readByte(currentOffSet + 1);\n                    String firstArg = String.valueOf(value);\n                    item = toOneArgIns(currentOffSet, \"bipush\", firstArg);\n                    size = 2;\n                    break;\n                }\n                case 17: // sipush\n                {\n                    int value = readShort(currentOffSet + 1);\n                    String firstArg = String.valueOf(value);\n                    item = toOneArgIns(currentOffSet, \"sipush\", firstArg);\n                    size = 3;\n                    break;\n                }\n                case 18: // ldc\n                {\n                    int cpIndex = readUnsignedByte(currentOffSet + 1);\n                    item = visitCPIns(currentOffSet, \"ldc\", cpIndex);\n                    size = 2;\n                    break;\n                }\n                case 19: // ldc_w\n                {\n                    int cpIndex = readUnsignedShort(currentOffSet + 1);\n                    item = visitCPIns(currentOffSet, \"ldc_w\", cpIndex);\n                    size = 3;\n                    break;\n                }\n                case 20: // ldc2_w\n                {\n                    int cpIndex = readUnsignedShort(currentOffSet + 1);\n                    item = visitCPIns(currentOffSet, \"ldc2_w\", cpIndex);\n                    size = 3;\n                    break;\n                }\n                case 21: // iload\n                {\n                    final int localIndex;\n                    if (wide) {\n                        localIndex = readUnsignedShort(currentOffSet + 1);\n                        wide = false;\n                        size = 3;\n                    }else {\n                        localIndex = readUnsignedByte(currentOffSet + 1);\n                        size = 2;\n                    }\n                    String firstArg = String.valueOf(localIndex);\n                    item = toOneArgIns(currentOffSet, \"iload\", firstArg);\n                    break;\n                }\n                case 22: // lload\n                {\n                    final int localIndex;\n                    if (wide) {\n                        localIndex = readUnsignedShort(currentOffSet + 1);\n                        wide = false;\n                        size = 3;\n                    }else {\n                        localIndex = readUnsignedByte(currentOffSet + 1);\n                        size = 2;\n                    }\n                    String firstArg = String.valueOf(localIndex);\n                    item = toOneArgIns(currentOffSet, \"lload\", firstArg);\n                    break;\n                }\n                case 23: // fload\n                {\n                    final int localIndex;\n                    if (wide) {\n                        localIndex = readUnsignedShort(currentOffSet + 1);\n                        wide = false;\n                        size = 3;\n                    }else {\n                        localIndex = readUnsignedByte(currentOffSet + 1);\n                        size = 2;\n                    }\n                    String firstArg = String.valueOf(localIndex);\n                    item = toOneArgIns(currentOffSet, \"fload\", firstArg);\n                    break;\n                }\n                case 24: // dload\n                {\n                    final int localIndex;\n                    if (wide) {\n                        localIndex = readUnsignedShort(currentOffSet + 1);\n                        wide = false;\n                        size = 3;\n                    }else {\n                        localIndex = readUnsignedByte(currentOffSet + 1);\n                        size = 2;\n                    }\n                    String firstArg = String.valueOf(localIndex);\n                    item = toOneArgIns(currentOffSet, \"dload\", firstArg);\n                    break;\n                }\n                case 25: // aload\n                {\n                    final int localIndex;\n                    if (wide) {\n                        localIndex = readUnsignedShort(currentOffSet + 1);\n                        wide = false;\n                        size = 3;\n                    }else {\n                        localIndex = readUnsignedByte(currentOffSet + 1);\n                        size = 2;\n                    }\n                    String firstArg = String.valueOf(localIndex);\n                    item = toOneArgIns(currentOffSet, \"aload\", firstArg);\n                    break;\n                }\n                case 26: // iload_0\n                {\n                    item = toNoArgIns(currentOffSet, \"iload_0\");\n                    size = 1;\n                    break;\n                }\n                case 27: // iload_1\n                {\n                    item = toNoArgIns(currentOffSet, \"iload_1\");\n                    size = 1;\n                    break;\n                }\n                case 28: // iload_2\n                {\n                    item = toNoArgIns(currentOffSet, \"iload_2\");\n                    size = 1;\n                    break;\n                }\n                case 29: // iload_3\n                {\n                    item = toNoArgIns(currentOffSet, \"iload_3\");\n                    size = 1;\n                    break;\n                }\n                case 30: // lload_0\n                {\n                    item = toNoArgIns(currentOffSet, \"lload_0\");\n                    size = 1;\n                    break;\n                }\n                case 31: // lload_1\n                {\n                    item = toNoArgIns(currentOffSet, \"lload_1\");\n                    size = 1;\n                    break;\n                }\n                case 32: // lload_2\n                {\n                    item = toNoArgIns(currentOffSet, \"lload_2\");\n                    size = 1;\n                    break;\n                }\n                case 33: // lload_3\n                {\n                    item = toNoArgIns(currentOffSet, \"lload_3\");\n                    size = 1;\n                    break;\n                }\n                case 34: // fload_0\n                {\n                    item = toNoArgIns(currentOffSet, \"fload_0\");\n                    size = 1;\n                    break;\n                }\n                case 35: // fload_1\n                {\n                    item = toNoArgIns(currentOffSet, \"fload_1\");\n                    size = 1;\n                    break;\n                }\n                case 36: // fload_2\n                {\n                    item = toNoArgIns(currentOffSet, \"fload_2\");\n                    size = 1;\n                    break;\n                }\n                case 37: // fload_3\n                {\n                    item = toNoArgIns(currentOffSet, \"fload_3\");\n                    size = 1;\n                    break;\n                }\n                case 38: // dload_0\n                {\n                    item = toNoArgIns(currentOffSet, \"dload_0\");\n                    size = 1;\n                    break;\n                }\n                case 39: // dload_1\n                {\n                    item = toNoArgIns(currentOffSet, \"dload_1\");\n                    size = 1;\n                    break;\n                }\n                case 40: // dload_2\n                {\n                    item = toNoArgIns(currentOffSet, \"dload_2\");\n                    size = 1;\n                    break;\n                }\n                case 41: // dload_3\n                {\n                    item = toNoArgIns(currentOffSet, \"dload_3\");\n                    size = 1;\n                    break;\n                }\n                case 42: // aload_0\n                {\n                    item = toNoArgIns(currentOffSet, \"aload_0\");\n                    size = 1;\n                    break;\n                }\n                case 43: // aload_1\n                {\n                    item = toNoArgIns(currentOffSet, \"aload_1\");\n                    size = 1;\n                    break;\n                }\n                case 44: // aload_2\n                {\n                    item = toNoArgIns(currentOffSet, \"aload_2\");\n                    size = 1;\n                    break;\n                }\n                case 45: // aload_3\n                {\n                    item = toNoArgIns(currentOffSet, \"aload_3\");\n                    size = 1;\n                    break;\n                }\n                case 46: // iaload\n                {\n                    item = toNoArgIns(currentOffSet, \"iaload\");\n                    size = 1;\n                    break;\n                }\n                case 47: // laload\n                {\n                    item = toNoArgIns(currentOffSet, \"laload\");\n                    size = 1;\n                    break;\n                }\n                case 48: // faload\n                {\n                    item = toNoArgIns(currentOffSet, \"faload\");\n                    size = 1;\n                    break;\n                }\n                case 49: // daload\n                {\n                    item = toNoArgIns(currentOffSet, \"daload\");\n                    size = 1;\n                    break;\n                }\n                case 50: // aaload\n                {\n                    item = toNoArgIns(currentOffSet, \"aaload\");\n                    size = 1;\n                    break;\n                }\n                case 51: // baload\n                {\n                    item = toNoArgIns(currentOffSet, \"baload\");\n                    size = 1;\n                    break;\n                }\n                case 52: // caload\n                {\n                    item = toNoArgIns(currentOffSet, \"caload\");\n                    size = 1;\n                    break;\n                }\n                case 53: // saload\n                {\n                    item = toNoArgIns(currentOffSet, \"saload\");\n                    size = 1;\n                    break;\n                }\n                case 54: // istore\n                {\n                    final int localIndex;\n                    if (wide) {\n                        localIndex = readUnsignedShort(currentOffSet + 1);\n                        wide = false;\n                        size = 3;\n                    }else {\n                        localIndex = readUnsignedByte(currentOffSet + 1);\n                        size = 2;\n                    }\n                    String firstArg = String.valueOf(localIndex);\n                    item = toOneArgIns(currentOffSet, \"istore\", firstArg);\n                    break;\n                }\n                case 55: // lstore\n                {\n                    final int localIndex;\n                    if (wide) {\n                        localIndex = readUnsignedShort(currentOffSet + 1);\n                        wide = false;\n                        size = 3;\n                    }else {\n                        localIndex = readUnsignedByte(currentOffSet + 1);\n                        size = 2;\n                    }\n                    String firstArg = String.valueOf(localIndex);\n                    item = toOneArgIns(currentOffSet, \"lstore\", firstArg);\n                    break;\n                }\n                case 56: // fstore\n                {\n                    final int localIndex;\n                    if (wide) {\n                        localIndex = readUnsignedShort(currentOffSet + 1);\n                        wide = false;\n                        size = 3;\n                    }else {\n                        localIndex = readUnsignedByte(currentOffSet + 1);\n                        size = 2;\n                    }\n                    String firstArg = String.valueOf(localIndex);\n                    item = toOneArgIns(currentOffSet, \"fstore\", firstArg);\n                    break;\n                }\n                case 57: // dstore\n                {\n                    final int localIndex;\n                    if (wide) {\n                        localIndex = readUnsignedShort(currentOffSet + 1);\n                        wide = false;\n                        size = 3;\n                    }else {\n                        localIndex = readUnsignedByte(currentOffSet + 1);\n                        size = 2;\n                    }\n                    String firstArg = String.valueOf(localIndex);\n                    item = toOneArgIns(currentOffSet, \"dstore\", firstArg);\n                    break;\n                }\n                case 58: // astore\n                {\n                    final int localIndex;\n                    if (wide) {\n                        localIndex = readUnsignedShort(currentOffSet + 1);\n                        wide = false;\n                        size = 3;\n                    }else {\n                        localIndex = readUnsignedByte(currentOffSet + 1);\n                        size = 2;\n                    }\n                    String firstArg = String.valueOf(localIndex);\n                    item = toOneArgIns(currentOffSet, \"astore\", firstArg);\n                    break;\n                }\n                case 59: // istore_0\n                {\n                    item = toNoArgIns(currentOffSet, \"istore_0\");\n                    size = 1;\n                    break;\n                }\n                case 60: // istore_1\n                {\n                    item = toNoArgIns(currentOffSet, \"istore_1\");\n                    size = 1;\n                    break;\n                }\n                case 61: // istore_2\n                {\n                    item = toNoArgIns(currentOffSet, \"istore_2\");\n                    size = 1;\n                    break;\n                }\n                case 62: // istore_3\n                {\n                    item = toNoArgIns(currentOffSet, \"istore_3\");\n                    size = 1;\n                    break;\n                }\n                case 63: // lstore_0\n                {\n                    item = toNoArgIns(currentOffSet, \"lstore_0\");\n                    size = 1;\n                    break;\n                }\n                case 64: // lstore_1\n                {\n                    item = toNoArgIns(currentOffSet, \"lstore_1\");\n                    size = 1;\n                    break;\n                }\n                case 65: // lstore_2\n                {\n                    item = toNoArgIns(currentOffSet, \"lstore_2\");\n                    size = 1;\n                    break;\n                }\n                case 66: // lstore_3\n                {\n                    item = toNoArgIns(currentOffSet, \"lstore_3\");\n                    size = 1;\n                    break;\n                }\n                case 67: // fstore_0\n                {\n                    item = toNoArgIns(currentOffSet, \"fstore_0\");\n                    size = 1;\n                    break;\n                }\n                case 68: // fstore_1\n                {\n                    item = toNoArgIns(currentOffSet, \"fstore_1\");\n                    size = 1;\n                    break;\n                }\n                case 69: // fstore_2\n                {\n                    item = toNoArgIns(currentOffSet, \"fstore_2\");\n                    size = 1;\n                    break;\n                }\n                case 70: // fstore_3\n                {\n                    item = toNoArgIns(currentOffSet, \"fstore_3\");\n                    size = 1;\n                    break;\n                }\n                case 71: // dstore_0\n                {\n                    item = toNoArgIns(currentOffSet, \"dstore_0\");\n                    size = 1;\n                    break;\n                }\n                case 72: // dstore_1\n                {\n                    item = toNoArgIns(currentOffSet, \"dstore_1\");\n                    size = 1;\n                    break;\n                }\n                case 73: // dstore_2\n                {\n                    item = toNoArgIns(currentOffSet, \"dstore_2\");\n                    size = 1;\n                    break;\n                }\n                case 74: // dstore_3\n                {\n                    item = toNoArgIns(currentOffSet, \"dstore_3\");\n                    size = 1;\n                    break;\n                }\n                case 75: // astore_0\n                {\n                    item = toNoArgIns(currentOffSet, \"astore_0\");\n                    size = 1;\n                    break;\n                }\n                case 76: // astore_1\n                {\n                    item = toNoArgIns(currentOffSet, \"astore_1\");\n                    size = 1;\n                    break;\n                }\n                case 77: // astore_2\n                {\n                    item = toNoArgIns(currentOffSet, \"astore_2\");\n                    size = 1;\n                    break;\n                }\n                case 78: // astore_3\n                {\n                    item = toNoArgIns(currentOffSet, \"astore_3\");\n                    size = 1;\n                    break;\n                }\n                case 79: // iastore\n                {\n                    item = toNoArgIns(currentOffSet, \"iastore\");\n                    size = 1;\n                    break;\n                }\n                case 80: // lastore\n                {\n                    item = toNoArgIns(currentOffSet, \"lastore\");\n                    size = 1;\n                    break;\n                }\n                case 81: // fastore\n                {\n                    item = toNoArgIns(currentOffSet, \"fastore\");\n                    size = 1;\n                    break;\n                }\n                case 82: // dastore\n                {\n                    item = toNoArgIns(currentOffSet, \"dastore\");\n                    size = 1;\n                    break;\n                }\n                case 83: // aastore\n                {\n                    item = toNoArgIns(currentOffSet, \"aastore\");\n                    size = 1;\n                    break;\n                }\n                case 84: // bastore\n                {\n                    item = toNoArgIns(currentOffSet, \"bastore\");\n                    size = 1;\n                    break;\n                }\n                case 85: // castore\n                {\n                    item = toNoArgIns(currentOffSet, \"castore\");\n                    size = 1;\n                    break;\n                }\n                case 86: // sastore\n                {\n                    item = toNoArgIns(currentOffSet, \"sastore\");\n                    size = 1;\n                    break;\n                }\n                case 87: // pop\n                {\n                    item = toNoArgIns(currentOffSet, \"pop\");\n                    size = 1;\n                    break;\n                }\n                case 88: // pop2\n                {\n                    item = toNoArgIns(currentOffSet, \"pop2\");\n                    size = 1;\n                    break;\n                }\n                case 89: // dup\n                {\n                    item = toNoArgIns(currentOffSet, \"dup\");\n                    size = 1;\n                    break;\n                }\n                case 90: // dup_x1\n                {\n                    item = toNoArgIns(currentOffSet, \"dup_x1\");\n                    size = 1;\n                    break;\n                }\n                case 91: // dup_x2\n                {\n                    item = toNoArgIns(currentOffSet, \"dup_x2\");\n                    size = 1;\n                    break;\n                }\n                case 92: // dup2\n                {\n                    item = toNoArgIns(currentOffSet, \"dup2\");\n                    size = 1;\n                    break;\n                }\n                case 93: // dup2_x1\n                {\n                    item = toNoArgIns(currentOffSet, \"dup2_x1\");\n                    size = 1;\n                    break;\n                }\n                case 94: // dup2_x2\n                {\n                    item = toNoArgIns(currentOffSet, \"dup2_x2\");\n                    size = 1;\n                    break;\n                }\n                case 95: // swap\n                {\n                    item = toNoArgIns(currentOffSet, \"swap\");\n                    size = 1;\n                    break;\n                }\n                case 96: // iadd\n                {\n                    item = toNoArgIns(currentOffSet, \"iadd\");\n                    size = 1;\n                    break;\n                }\n                case 97: // ladd\n                {\n                    item = toNoArgIns(currentOffSet, \"ladd\");\n                    size = 1;\n                    break;\n                }\n                case 98: // fadd\n                {\n                    item = toNoArgIns(currentOffSet, \"fadd\");\n                    size = 1;\n                    break;\n                }\n                case 99: // dadd\n                {\n                    item = toNoArgIns(currentOffSet, \"dadd\");\n                    size = 1;\n                    break;\n                }\n                case 100: // isub\n                {\n                    item = toNoArgIns(currentOffSet, \"isub\");\n                    size = 1;\n                    break;\n                }\n                case 101: // lsub\n                {\n                    item = toNoArgIns(currentOffSet, \"lsub\");\n                    size = 1;\n                    break;\n                }\n                case 102: // fsub\n                {\n                    item = toNoArgIns(currentOffSet, \"fsub\");\n                    size = 1;\n                    break;\n                }\n                case 103: // dsub\n                {\n                    item = toNoArgIns(currentOffSet, \"dsub\");\n                    size = 1;\n                    break;\n                }\n                case 104: // imul\n                {\n                    item = toNoArgIns(currentOffSet, \"imul\");\n                    size = 1;\n                    break;\n                }\n                case 105: // lmul\n                {\n                    item = toNoArgIns(currentOffSet, \"lmul\");\n                    size = 1;\n                    break;\n                }\n                case 106: // fmul\n                {\n                    item = toNoArgIns(currentOffSet, \"fmul\");\n                    size = 1;\n                    break;\n                }\n                case 107: // dmul\n                {\n                    item = toNoArgIns(currentOffSet, \"dmul\");\n                    size = 1;\n                    break;\n                }\n                case 108: // idiv\n                {\n                    item = toNoArgIns(currentOffSet, \"idiv\");\n                    size = 1;\n                    break;\n                }\n                case 109: // ldiv\n                {\n                    item = toNoArgIns(currentOffSet, \"ldiv\");\n                    size = 1;\n                    break;\n                }\n                case 110: // fdiv\n                {\n                    item = toNoArgIns(currentOffSet, \"fdiv\");\n                    size = 1;\n                    break;\n                }\n                case 111: // ddiv\n                {\n                    item = toNoArgIns(currentOffSet, \"ddiv\");\n                    size = 1;\n                    break;\n                }\n                case 112: // irem\n                {\n                    item = toNoArgIns(currentOffSet, \"irem\");\n                    size = 1;\n                    break;\n                }\n                case 113: // lrem\n                {\n                    item = toNoArgIns(currentOffSet, \"lrem\");\n                    size = 1;\n                    break;\n                }\n                case 114: // frem\n                {\n                    item = toNoArgIns(currentOffSet, \"frem\");\n                    size = 1;\n                    break;\n                }\n                case 115: // drem\n                {\n                    item = toNoArgIns(currentOffSet, \"drem\");\n                    size = 1;\n                    break;\n                }\n                case 116: // ineg\n                {\n                    item = toNoArgIns(currentOffSet, \"ineg\");\n                    size = 1;\n                    break;\n                }\n\n                case 117: // lneg\n                {\n                    item = toNoArgIns(currentOffSet, \"lneg\");\n                    size = 1;\n                    break;\n                }\n\n                case 118: // fneg\n                {\n                    item = toNoArgIns(currentOffSet, \"fneg\");\n                    size = 1;\n                    break;\n                }\n\n                case 119: // dneg\n                {\n                    item = toNoArgIns(currentOffSet, \"dneg\");\n                    size = 1;\n                    break;\n                }\n\n                case 120: // ishl\n                {\n                    item = toNoArgIns(currentOffSet, \"ishl\");\n                    size = 1;\n                    break;\n                }\n\n                case 121: // lshl\n                {\n                    item = toNoArgIns(currentOffSet, \"lshl\");\n                    size = 1;\n                    break;\n                }\n\n                case 122: // ishr\n                {\n                    item = toNoArgIns(currentOffSet, \"ishr\");\n                    size = 1;\n                    break;\n                }\n\n                case 123: // lshr\n                {\n                    item = toNoArgIns(currentOffSet, \"lshr\");\n                    size = 1;\n                    break;\n                }\n\n                case 124: // iushr\n                {\n                    item = toNoArgIns(currentOffSet, \"iushr\");\n                    size = 1;\n                    break;\n                }\n\n                case 125: // lushr\n                {\n                    item = toNoArgIns(currentOffSet, \"lushr\");\n                    size = 1;\n                    break;\n                }\n\n                case 126: // iand\n                {\n                    item = toNoArgIns(currentOffSet, \"iand\");\n                    size = 1;\n                    break;\n                }\n\n                case 127: // land\n                {\n                    item = toNoArgIns(currentOffSet, \"land\");\n                    size = 1;\n                    break;\n                }\n\n                case 128: // ior\n                {\n                    item = toNoArgIns(currentOffSet, \"ior\");\n                    size = 1;\n                    break;\n                }\n\n                case 129: // lor\n                {\n                    item = toNoArgIns(currentOffSet, \"lor\");\n                    size = 1;\n                    break;\n                }\n\n                case 130: // ixor\n                {\n                    item = toNoArgIns(currentOffSet, \"ixor\");\n                    size = 1;\n                    break;\n                }\n\n                case 131: // lxor\n                {\n                    item = toNoArgIns(currentOffSet, \"lxor\");\n                    size = 1;\n                    break;\n                }\n                case 132: // iinc\n                {\n                    int index;\n                    int val;\n                    if (wide) {\n                        index = readUnsignedShort(currentOffSet + 1);\n                        val = readShort(currentOffSet + 3);\n                        size = 5;\n                    }\n                    else {\n                        index = readUnsignedByte(currentOffSet + 1);\n                        val = readByte(currentOffSet + 2);\n                        size = 3;\n                    }\n                    item = toTwoArgIns(currentOffSet, \"iinc\", String.valueOf(index), String.valueOf(val));\n                    break;\n                }\n\n                case 133: // i2l\n                {\n                    item = toNoArgIns(currentOffSet, \"i2l\");\n                    size = 1;\n                    break;\n                }\n\n                case 134: // i2f\n                {\n                    item = toNoArgIns(currentOffSet, \"i2f\");\n                    size = 1;\n                    break;\n                }\n\n                case 135: // i2d\n                {\n                    item = toNoArgIns(currentOffSet, \"i2d\");\n                    size = 1;\n                    break;\n                }\n\n                case 136: // l2i\n                {\n                    item = toNoArgIns(currentOffSet, \"l2i\");\n                    size = 1;\n                    break;\n                }\n\n                case 137: // l2f\n                {\n                    item = toNoArgIns(currentOffSet, \"l2f\");\n                    size = 1;\n                    break;\n                }\n\n                case 138: // l2d\n                {\n                    item = toNoArgIns(currentOffSet, \"l2d\");\n                    size = 1;\n                    break;\n                }\n\n                case 139: // f2i\n                {\n                    item = toNoArgIns(currentOffSet, \"f2i\");\n                    size = 1;\n                    break;\n                }\n\n                case 140: // f2l\n                {\n                    item = toNoArgIns(currentOffSet, \"f2l\");\n                    size = 1;\n                    break;\n                }\n\n                case 141: // f2d\n                {\n                    item = toNoArgIns(currentOffSet, \"f2d\");\n                    size = 1;\n                    break;\n                }\n\n                case 142: // d2i\n                {\n                    item = toNoArgIns(currentOffSet, \"d2i\");\n                    size = 1;\n                    break;\n                }\n\n                case 143: // d2l\n                {\n                    item = toNoArgIns(currentOffSet, \"d2l\");\n                    size = 1;\n                    break;\n                }\n\n                case 144: // d2f\n                {\n                    item = toNoArgIns(currentOffSet, \"d2f\");\n                    size = 1;\n                    break;\n                }\n\n                case 145: // i2b\n                {\n                    item = toNoArgIns(currentOffSet, \"i2b\");\n                    size = 1;\n                    break;\n                }\n\n                case 146: // i2c\n                {\n                    item = toNoArgIns(currentOffSet, \"i2c\");\n                    size = 1;\n                    break;\n                }\n\n                case 147: // i2s\n                {\n                    item = toNoArgIns(currentOffSet, \"i2s\");\n                    size = 1;\n                    break;\n                }\n                case 148: // lcmp\n                {\n                    item = toNoArgIns(currentOffSet, \"lcmp\");\n                    size = 1;\n                    break;\n                }\n\n                case 149: // fcmpl\n                {\n                    item = toNoArgIns(currentOffSet, \"fcmpl\");\n                    size = 1;\n                    break;\n                }\n\n                case 150: // fcmpg\n                {\n                    item = toNoArgIns(currentOffSet, \"fcmpg\");\n                    size = 1;\n                    break;\n                }\n\n                case 151: // dcmpl\n                {\n                    item = toNoArgIns(currentOffSet, \"dcmpl\");\n                    size = 1;\n                    break;\n                }\n\n                case 152: // dcmpg\n                {\n                    item = toNoArgIns(currentOffSet, \"dcmpg\");\n                    size = 1;\n                    break;\n                }\n                case 153: // ifeq\n                {\n                    int value = readShort(currentOffSet + 1);\n                    String firstArg = String.valueOf(value);\n                    item = toOneArgIns(currentOffSet, \"ifeq\", firstArg);\n                    size = 3;\n                    break;\n                }\n\n                case 154: // ifne\n                {\n                    int value = readShort(currentOffSet + 1);\n                    String firstArg = String.valueOf(value);\n                    item = toOneArgIns(currentOffSet, \"ifne\", firstArg);\n                    size = 3;\n                    break;\n                }\n\n                case 155: // iflt\n                {\n                    int value = readShort(currentOffSet + 1);\n                    String firstArg = String.valueOf(value);\n                    item = toOneArgIns(currentOffSet, \"iflt\", firstArg);\n                    size = 3;\n                    break;\n                }\n\n                case 156: // ifge\n                {\n                    int value = readShort(currentOffSet + 1);\n                    String firstArg = String.valueOf(value);\n                    item = toOneArgIns(currentOffSet, \"ifge\", firstArg);\n                    size = 3;\n                    break;\n                }\n\n                case 157: // ifgt\n                {\n                    int value = readShort(currentOffSet + 1);\n                    String firstArg = String.valueOf(value);\n                    item = toOneArgIns(currentOffSet, \"ifgt\", firstArg);\n                    size = 3;\n                    break;\n                }\n\n                case 158: // ifle\n                {\n                    int value = readShort(currentOffSet + 1);\n                    String firstArg = String.valueOf(value);\n                    item = toOneArgIns(currentOffSet, \"ifle\", firstArg);\n                    size = 3;\n                    break;\n                }\n\n                case 159: // if_icmpeq\n                {\n                    int value = readShort(currentOffSet + 1);\n                    String firstArg = String.valueOf(value);\n                    item = toOneArgIns(currentOffSet, \"if_icmpeq\", firstArg);\n                    size = 3;\n                    break;\n                }\n\n                case 160: // if_icmpne\n                {\n                    int value = readShort(currentOffSet + 1);\n                    String firstArg = String.valueOf(value);\n                    item = toOneArgIns(currentOffSet, \"if_icmpne\", firstArg);\n                    size = 3;\n                    break;\n                }\n\n                case 161: // if_icmplt\n                {\n                    int value = readShort(currentOffSet + 1);\n                    String firstArg = String.valueOf(value);\n                    item = toOneArgIns(currentOffSet, \"if_icmplt\", firstArg);\n                    size = 3;\n                    break;\n                }\n\n                case 162: // if_icmpge\n                {\n                    int value = readShort(currentOffSet + 1);\n                    String firstArg = String.valueOf(value);\n                    item = toOneArgIns(currentOffSet, \"if_icmpge\", firstArg);\n                    size = 3;\n                    break;\n                }\n\n                case 163: // if_icmpgt\n                {\n                    int value = readShort(currentOffSet + 1);\n                    String firstArg = String.valueOf(value);\n                    item = toOneArgIns(currentOffSet, \"if_icmpgt\", firstArg);\n                    size = 3;\n                    break;\n                }\n\n                case 164: // if_icmple\n                {\n                    int value = readShort(currentOffSet + 1);\n                    String firstArg = String.valueOf(value);\n                    item = toOneArgIns(currentOffSet, \"if_icmple\", firstArg);\n                    size = 3;\n                    break;\n                }\n\n                case 165: // if_acmpeq\n                {\n                    int value = readShort(currentOffSet + 1);\n                    String firstArg = String.valueOf(value);\n                    item = toOneArgIns(currentOffSet, \"if_acmpeq\", firstArg);\n                    size = 3;\n                    break;\n                }\n\n                case 166: // if_acmpne\n                {\n                    int value = readShort(currentOffSet + 1);\n                    String firstArg = String.valueOf(value);\n                    item = toOneArgIns(currentOffSet, \"if_acmpne\", firstArg);\n                    size = 3;\n                    break;\n                }\n\n                case 167: // goto\n                {\n                    int value = readShort(currentOffSet + 1);\n                    String firstArg = String.valueOf(value);\n                    item = toOneArgIns(currentOffSet, \"goto\", firstArg);\n                    size = 3;\n                    break;\n                }\n\n                case 168: // jsr\n                {\n                    int address = readShort(currentOffSet + 1);\n                    String firstArg = String.valueOf(address);\n                    item = toOneArgIns(currentOffSet, \"jsr\", firstArg);\n                    size = 3;\n                    break;\n                }\n                case 169: // ret\n                {\n                    int index;\n                    if (wide) {\n                        index = readUnsignedShort(currentOffSet + 1);\n                        size = 3;\n                        wide = false;\n                    }\n                    else {\n                        index = readUnsignedByte(currentOffSet + 1);\n                        size = 2;\n                    }\n                    String firstArg = String.valueOf(index);\n                    item = toOneArgIns(currentOffSet, \"ret\", firstArg);\n\n                    break;\n                }\n                case 170: // tableswitch\n                {\n                    int pad = 3 - currentOffSet % 4;\n                    int defaultOffset = readInt(currentOffSet + pad + 1);\n                    int low = readInt(currentOffSet + pad + 1 + 4);\n                    int high = readInt(currentOffSet + pad + 1 + 8);\n\n                    StringBuilder sb = new StringBuilder();\n                    Formatter fm = new Formatter(sb);\n                    fm.format(SWITCH_FORMAT, currentOffSet, \"tableswitch\");\n                    fm.format(SWITCH_START_FORMAT);\n                    int caseCount = high - low + 1;\n                    for (int i = 0; i < caseCount; i++) {\n                        String caseValue = String.valueOf(low + i);\n                        int offset = readInt(currentOffSet + pad + 1 + 12 + i * 4);\n                        fm.format(CASE_FORMAT, caseValue, offset);\n                    }\n                    fm.format(CASE_FORMAT, \"default\", defaultOffset);\n                    fm.format(SWITCH_STOP_FORMAT);\n\n                    item = sb.toString();\n                    size = 1 /*opcode*/ + pad + 12/*default-low-high*/ + (high - low + 1) * 4;\n                    break;\n                }\n                case 171: // lookupswitch\n                {\n                    int pad = 3 - currentOffSet % 4;\n                    int defaultOffset = readInt(currentOffSet + pad + 1);\n                    int npairs = readInt(currentOffSet + pad + 1 + 4);\n\n                    StringBuilder sb = new StringBuilder();\n                    Formatter fm = new Formatter(sb);\n                    fm.format(SWITCH_FORMAT, currentOffSet, \"lookupswitch\");\n                    fm.format(SWITCH_START_FORMAT);\n                    for (int i = 0; i < npairs; i++) {\n                        int caseConst = readInt(currentOffSet + pad + 1 + 8 + i * 8);\n                        String caseValue = String.valueOf(caseConst);\n                        int offset = readInt(currentOffSet + pad + 1 + 8 + i * 8 + 4);\n                        fm.format(CASE_FORMAT, caseValue, offset);\n                    }\n                    fm.format(CASE_FORMAT, \"default\", defaultOffset);\n                    fm.format(SWITCH_STOP_FORMAT);\n\n                    item = sb.toString();\n                    size = 1 /*opcode*/ + pad + 8/*default-npairs*/ + (npairs) * 8;\n                    break;\n                }\n                case 172: // ireturn\n                {\n                    item = toNoArgIns(currentOffSet, \"ireturn\");\n                    size = 1;\n                    break;\n                }\n                case 173: // lreturn\n                {\n                    item = toNoArgIns(currentOffSet, \"lreturn\");\n                    size = 1;\n                    break;\n                }\n                case 174: // freturn\n                {\n                    item = toNoArgIns(currentOffSet, \"freturn\");\n                    size = 1;\n                    break;\n                }\n                case 175: // dreturn\n                {\n                    item = toNoArgIns(currentOffSet, \"dreturn\");\n                    size = 1;\n                    break;\n                }\n                case 176: // areturn\n                {\n                    item = toNoArgIns(currentOffSet, \"areturn\");\n                    size = 1;\n                    break;\n                }\n                case 177: // return\n                {\n                    item = toNoArgIns(currentOffSet, \"return\");\n                    size = 1;\n                    break;\n                }\n                case 178: // getstatic\n                {\n                    int cpIndex = readUnsignedShort(currentOffSet + 1);\n                    item = visitCPIns(currentOffSet, \"getstatic\", cpIndex);\n                    size = 3;\n                    break;\n                }\n                case 179: // putstatic\n                {\n                    int cpIndex = readUnsignedShort(currentOffSet + 1);\n                    item = visitCPIns(currentOffSet, \"putstatic\", cpIndex);\n                    size = 3;\n                    break;\n                }\n                case 180: // getfield\n                {\n                    int cpIndex = readUnsignedShort(currentOffSet + 1);\n                    item = visitCPIns(currentOffSet, \"getfield\", cpIndex);\n                    size = 3;\n                    break;\n                }\n                case 181: // putfield\n                {\n                    int cpIndex = readUnsignedShort(currentOffSet + 1);\n                    item = visitCPIns(currentOffSet, \"putfield\", cpIndex);\n                    size = 3;\n                    break;\n                }\n                case 182: // invokevirtual\n                {\n                    int cpIndex = readUnsignedShort(currentOffSet + 1);\n                    item = visitCPIns(currentOffSet, \"invokevirtual\", cpIndex);\n                    size = 3;\n                    break;\n                }\n                case 183: // invokespecial\n                {\n                    int cpIndex = readUnsignedShort(currentOffSet + 1);\n                    item = visitCPIns(currentOffSet, \"invokespecial\", cpIndex);\n                    size = 3;\n                    break;\n                }\n                case 184: // invokestatic\n                {\n                    int cpIndex = readUnsignedShort(currentOffSet + 1);\n                    item = visitCPIns(currentOffSet, \"invokestatic\", cpIndex);\n                    size = 3;\n                    break;\n                }\n                case 185: // invokeinterface\n                {\n                    int cpIndex = readUnsignedShort(currentOffSet + 1);\n                    int count = readUnsignedByte(currentOffSet + 3);\n                    String firstArg = String.format(\"#%d  %d\", cpIndex, count);\n                    item = toOneArgIns(currentOffSet, \"invokeinterface\", firstArg);\n                    size = 5;\n                    break;\n                }\n                case 186: // invokedynamic\n                {\n                    int cpIndex = readUnsignedShort(currentOffSet + 1);\n                    String firstArg = String.format(\"#%d\", cpIndex);\n                    item = toOneArgIns(currentOffSet, \"invokedynamic\", firstArg);\n                    size = 5;\n                    break;\n                }\n                case 187: // new\n                {\n                    int cpIndex = readUnsignedShort(currentOffSet + 1);\n                    item = visitCPIns(currentOffSet, \"new\", cpIndex);\n                    size = 3;\n                    break;\n                }\n                case 188: // newarray\n                {\n                    int atype = readByte(currentOffSet + 1);\n                    final String firstArg;\n                    switch (atype) {\n                        case 4: {\n                            firstArg = \"4 (boolean)\";\n                            break;\n                        }\n                        case 5: {\n                            firstArg = \"5 (char)\";\n                            break;\n                        }\n                        case 6: {\n                            firstArg = \"6 (float)\";\n                            break;\n                        }\n                        case 7: {\n                            firstArg = \"7 (double)\";\n                            break;\n                        }\n                        case 8: {\n                            firstArg = \"8 (byte)\";\n                            break;\n                        }\n                        case 9: {\n                            firstArg = \"9 (short)\";\n                            break;\n                        }\n                        case 10: {\n                            firstArg = \"10 (int)\";\n                            break;\n                        }\n                        case 11: {\n                            firstArg = \"11 (long)\";\n                            break;\n                        }\n                        default:\n                            throw new RuntimeException(\"atype is not supported: \" + atype);\n                    }\n                    item = String.format(NEW_ARRAY_FORMAT, currentOffSet, \"newarray\", firstArg);\n                    size = 2;\n                    break;\n                }\n                case 189: // anewarray\n                {\n                    int cpIndex = readUnsignedShort(currentOffSet + 1);\n                    item = visitCPIns(currentOffSet, \"anewarray\", cpIndex);\n                    size = 3;\n                    break;\n                }\n                case 190: // arraylength\n                {\n                    item = toNoArgIns(currentOffSet, \"arraylength\");\n                    size = 1;\n                    break;\n                }\n                case 191: // athrow\n                {\n                    item = toNoArgIns(currentOffSet, \"athrow\");\n                    size = 1;\n                    break;\n                }\n                case 192: // checkcast\n                {\n                    int cpIndex = readUnsignedShort(currentOffSet + 1);\n                    item = visitCPIns(currentOffSet, \"checkcast\", cpIndex);\n                    size = 3;\n                    break;\n                }\n                case 193: // instanceof\n                {\n                    int cpIndex = readUnsignedShort(currentOffSet + 1);\n                    item = visitCPIns(currentOffSet, \"instanceof\", cpIndex);\n                    size = 3;\n                    break;\n                }\n                case 194: // monitorenter\n                {\n                    item = toNoArgIns(currentOffSet, \"monitorenter\");\n                    size = 1;\n                    break;\n                }\n                case 195: // monitorexit\n                {\n                    item = toNoArgIns(currentOffSet, \"monitorexit\");\n                    size = 1;\n                    break;\n                }\n                case 196: // wide\n                {\n                    wide = true;\n                    item = toNoArgIns(currentOffSet, \"wide\");\n                    size = 1;\n                    break;\n                }\n                case 197: // multianewarray\n                {\n                    int cpIndex = readUnsignedShort(currentOffSet + 1);\n                    int dimensions = readByte(currentOffSet + 3);\n\n                    String firstArg = String.format(\"#%d  %d\", cpIndex, dimensions);\n                    item = toOneArgIns(currentOffSet, \"multianewarray\", firstArg);\n                    size = 4;\n                    break;\n                }\n                case 198: // ifnull\n                {\n                    int offset = readUnsignedShort(currentOffSet + 1);\n                    String firstArg = String.valueOf(offset);\n                    item = toOneArgIns(currentOffSet, \"ifnull\", firstArg);\n                    size = 3;\n                    break;\n                }\n                case 199: // ifnonnull\n                {\n                    int offset = readUnsignedShort(currentOffSet + 1);\n                    String firstArg = String.valueOf(offset);\n                    item = toOneArgIns(currentOffSet, \"ifnonnull\", firstArg);\n                    size = 3;\n                    break;\n                }\n                case 200: // goto_w\n                {\n                    int offset = readInt(currentOffSet + 1);\n                    String firstArg = String.valueOf(offset);\n                    item = toOneArgIns(currentOffSet, \"goto_w\", firstArg);\n                    size = 5;\n                    break;\n                }\n                case 201: // jsr_w\n                {\n                    int offset = readInt(currentOffSet + 1);\n                    String firstArg = String.valueOf(offset);\n                    item = toOneArgIns(currentOffSet, \"jsr_w\", firstArg);\n                    size = 5;\n                    break;\n                }\n                default:\n                    throw new RuntimeException(\"illegal opcode: \" + opcode);\n            }\n            list.add(item);\n            currentOffSet += size;\n        }\n\n        return list;\n    }\n\n    private String toNoArgIns(int offset, String mnemonic_symbol) {\n        return String.format(NO_ARG_FORMAT, offset, mnemonic_symbol);\n    }\n\n    private String toOneArgIns(int currentOffSet, String mnemonic_symbol, String firstArg) {\n        return String.format(ONE_ARG_FORMAT, currentOffSet, mnemonic_symbol, firstArg);\n    }\n\n    private String toTwoArgIns(int currentOffSet, String mnemonic_symbol, String firstArg, String secondArg) {\n        return String.format(TWO_ARG_FORMAT, currentOffSet, mnemonic_symbol, firstArg, secondArg);\n    }\n\n    private String visitCPIns(int currentOffSet, String mnemonic_symbol, int cpIndex) {\n        return String.format(CP_INS_FORMAT, currentOffSet, mnemonic_symbol, cpIndex);\n    }\n\n    public int readByte(final int offset) {\n        return code_bytes[offset];\n    }\n\n    public int readUnsignedByte(final int offset) {\n        return code_bytes[offset] & 0xFF;\n    }\n\n    public short readShort(final int offset) {\n        byte[] classBuffer = code_bytes;\n        return (short) (((classBuffer[offset] & 0xFF) << 8) | (classBuffer[offset + 1] & 0xFF));\n    }\n\n    public int readUnsignedShort(final int offset) {\n        byte[] classBuffer = code_bytes;\n        return ((classBuffer[offset] & 0xFF) << 8) | (classBuffer[offset + 1] & 0xFF);\n    }\n\n    public int readInt(final int offset) {\n        byte[] classBuffer = code_bytes;\n        return ((classBuffer[offset] & 0xFF) << 24)\n                | ((classBuffer[offset + 1] & 0xFF) << 16)\n                | ((classBuffer[offset + 2] & 0xFF) << 8)\n                | (classBuffer[offset + 3] & 0xFF);\n    }\n\n    public long readLong(final int offset) {\n        long l1 = readInt(offset);\n        long l0 = readInt(offset + 4) & 0xFFFFFFFFL;\n        return (l1 << 32) | l0;\n    }\n}\n"
  },
  {
    "path": "src/main/java/lsieun/cst/Const.java",
    "content": "package lsieun.cst;\n\npublic class Const {\n    public static final boolean DEBUG = true;\n\n    public static final String DEBUG_FORMAT = \"[DEBUG] %s%n\";\n    public static final String DIVISION_LINE = \"================================================================\";\n    public static final String PREVIOUS_LINE = \"↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑\";\n    public static final String NEXT_LINE     = \"↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓\";\n}\n"
  },
  {
    "path": "src/main/java/lsieun/drawing/canvas/Box.java",
    "content": "package lsieun.drawing.canvas;\n\n/**\n * <ul>\n *     <li> LEFT: 0b1000</li>\n *     <li>   UP: 0b0100</li>\n *     <li>RIGHT: 0b0010</li>\n *     <li> DOWN: 0b0001</li>\n * </ul>\n */\npublic enum Box {\n    SPACE(0b0000, \" \"),\n    HORIZONTAL(0b1010, \"─\"),\n    VERTICAL(0b0101, \"│\"),\n    DOWN_AND_RIGHT(0b0011, \"┌\"),\n    DOWN_AND_LEFT(0b1001, \"┐\"),\n    UP_AND_RIGHT(0b0110, \"└\"),\n    UP_AND_LEFT(0b1100, \"┘\"),\n    VERTICAL_AND_RIGHT(0b0111, \"├\"),\n    VERTICAL_AND_LEFT(0b1101, \"┤\"),\n    DOWN_AND_HORIZONTAL(0b1011, \"┬\"),\n    UP_AND_HORIZONTAL(0b1110, \"┴\"),\n    VERTICAL_AND_HORIZONTAL(0b1111, \"┼\"),\n    ;\n\n    public final int flag;\n    public final String val;\n\n    Box(int flag, String val) {\n        this.flag = flag;\n        this.val = val;\n    }\n\n    public Box merge(Box another) {\n        int flag = this.flag | another.flag;\n        return fromFlag(flag);\n    }\n\n    public static Box merge(String val1, String val2) {\n        Box one = fromString(val1);\n        Box another = fromString(val2);\n        return one.merge(another);\n    }\n\n    public static Box fromString(String val) {\n        Box[] values = values();\n        for (Box item : values) {\n            if (item.val.equals(val)) {\n                return item;\n            }\n        }\n        throw new RuntimeException(\"Unexpected Value: \" + val);\n    }\n\n    public static Box fromFlag(int flag) {\n        Box[] values = values();\n        for (Box item : values) {\n            if (item.flag == flag) {\n                return item;\n            }\n        }\n        throw new RuntimeException(\"Unexpected flag: \" + flag);\n    }\n\n    public static boolean isValid(String val) {\n        Box[] values = values();\n        for (Box item : values) {\n            if (item.val.equals(val)) {\n                return true;\n            }\n        }\n        return false;\n    }\n}\n"
  },
  {
    "path": "src/main/java/lsieun/drawing/canvas/Canvas.java",
    "content": "package lsieun.drawing.canvas;\n\nimport lsieun.utils.StringUtils;\n\nimport java.util.ArrayList;\nimport java.util.Collections;\nimport java.util.Formatter;\nimport java.util.List;\n\npublic class Canvas {\n    private int row;\n    private int col;\n    private final List<TextPixel> pixelList = new ArrayList<>();\n\n    // region move\n    public Canvas moveTo(int row, int col) {\n        this.row = row;\n        this.col = col;\n        return this;\n    }\n\n    public Canvas up(int count) {\n        this.row -= count;\n        return this;\n    }\n\n    public Canvas down(int count) {\n        this.row += count;\n        return this;\n    }\n\n    public Canvas left(int count) {\n        this.col -= count;\n        return this;\n    }\n\n    public Canvas right(int count) {\n        this.col += count;\n        return this;\n    }\n\n    public void rectifyPosition() {\n        int minRow = 0;\n        int minCol = 0;\n        for (TextPixel pixel : pixelList) {\n            int row = pixel.row;\n            if (row < minRow) {\n                minRow = row;\n            }\n            int col = pixel.col;\n            if (col < minCol) {\n                minCol = col;\n            }\n        }\n\n        for (TextPixel pixel : pixelList) {\n            pixel.row -= minRow;\n            pixel.col -= minCol;\n        }\n    }\n\n    public void updatePosition(int startRow, int startCol) {\n        for (TextPixel pixel : pixelList) {\n            pixel.row += startRow;\n            pixel.col += startCol;\n        }\n    }\n    // endregion\n\n    // region Point\n    public void drawPixel(Box value) {\n        TextPixel pixel = findPixel(row, col);\n        if (pixel != null) {\n            if (Box.isValid(pixel.value)) {\n                pixel.value = Box.merge(pixel.value, value.val).val;\n            }\n            else {\n                pixel.value = value.val;\n            }\n        }\n        else {\n            pixel = TextPixel.valueOf(row, col, value.val);\n            pixelList.add(pixel);\n            Collections.sort(pixelList);\n        }\n    }\n\n    public void drawPixel(String value) {\n        String firstChar = value.substring(0, 1);\n        TextPixel pixel = findPixel(row, col);\n        if (pixel != null) {\n            pixel.value = firstChar;\n        }\n        else {\n            pixel = TextPixel.valueOf(row, col, firstChar);\n            pixelList.add(pixel);\n            Collections.sort(pixelList);\n        }\n    }\n\n    public Canvas switchDirection(TextDirection from, TextDirection to) {\n        if (from == TextDirection.UP && to == TextDirection.LEFT) {\n            drawPixel(Box.UP_AND_LEFT);\n            left(1);\n        }\n        else if (from == TextDirection.UP && to == TextDirection.RIGHT) {\n            drawPixel(Box.UP_AND_RIGHT);\n            right(1);\n        }\n        else if (from == TextDirection.RIGHT && to == TextDirection.UP) {\n            drawPixel(Box.UP_AND_RIGHT);\n            up(1);\n        }\n        else if (from == TextDirection.RIGHT && to == TextDirection.DOWN) {\n            drawPixel(Box.DOWN_AND_RIGHT);\n            down(1);\n        }\n        else if (from == TextDirection.DOWN && to == TextDirection.RIGHT) {\n            drawPixel(Box.DOWN_AND_RIGHT);\n            right(1);\n        }\n        else if (from == TextDirection.DOWN && to == TextDirection.LEFT) {\n            drawPixel(Box.DOWN_AND_LEFT);\n            left(1);\n        }\n        else if (from == TextDirection.LEFT && to == TextDirection.DOWN) {\n            drawPixel(Box.DOWN_AND_LEFT);\n            down(1);\n        }\n        else if (from == TextDirection.LEFT && to == TextDirection.UP) {\n            drawPixel(Box.UP_AND_LEFT);\n            up(1);\n        }\n        else {\n            assert false : \"impossible here\";\n        }\n\n        return this;\n    }\n    // endregion\n\n    // region Line\n    public Canvas drawHorizontalLine(int num) {\n        int step = num > 0 ? 1 : -1;\n        int count = Math.abs(num);\n        for (int i = 0; i < count; i++) {\n            drawPixel(Box.HORIZONTAL);\n            col += step;\n        }\n        return this;\n    }\n\n    public Canvas drawVerticalLine(int num) {\n        int step = num > 0 ? 1 : -1;\n        int count = Math.abs(num);\n        for (int i = 0; i < count; i++) {\n            drawPixel(Box.VERTICAL);\n            row += step;\n        }\n        return this;\n    }\n\n    public void drawText(String text) {\n        int length = text.length();\n        for (int i = 0; i < length; i++) {\n            String ch = text.substring(i, i + 1);\n            drawPixel(ch);\n            col++;\n        }\n    }\n    // endregion\n\n    // region Plane\n    public void drawRectangle(int width, int height) {\n        // left top\n        drawPixel(Box.DOWN_AND_RIGHT);\n        right(1);\n        for (int i = 0; i < width; i++) {\n            drawPixel(Box.HORIZONTAL);\n            right(1);\n        }\n\n        // right top\n        drawPixel(Box.DOWN_AND_LEFT);\n        down(1);\n        for (int i = 0; i < height; i++) {\n            drawPixel(Box.VERTICAL);\n            down(1);\n        }\n\n        // right bottom\n        drawPixel(Box.UP_AND_LEFT);\n        left(1);\n        for (int i = 0; i < width; i++) {\n            drawPixel(Box.HORIZONTAL);\n            left(1);\n        }\n\n        // right left\n        drawPixel(Box.UP_AND_RIGHT);\n        up(1);\n        for (int i = 0; i < height; i++) {\n            drawPixel(Box.VERTICAL);\n            up(1);\n        }\n    }\n\n    public void drawTable(int[] rowHeightArray, int[] colWidthArray) {\n        int startRow = row;\n        int startCol = col;\n\n        int rowCount = rowHeightArray.length;\n        int colCount = colWidthArray.length;\n\n        int totalWidth = colCount - 1;\n        for (int width : colWidthArray) {\n            totalWidth += width;\n        }\n\n        int totalHeight = rowCount - 1;\n        for (int height : rowHeightArray) {\n            totalHeight += height;\n        }\n\n        // outer border\n        drawRectangle(totalWidth, totalHeight);\n\n        // inner horizontal border\n        moveTo(startRow, startCol);\n        for (int i = 0; i < colCount - 1; i++) {\n            int width = colWidthArray[i];\n            right(width + 1);\n            drawPixel(Box.DOWN_AND_HORIZONTAL);\n            down(1);\n            drawVerticalLine(totalHeight);\n            drawPixel(Box.UP_AND_HORIZONTAL);\n            up(totalHeight + 1);\n        }\n\n        // inner vertical border\n        moveTo(startRow, startCol);\n        for (int i = 0; i < rowCount - 1; i++) {\n            int height = rowHeightArray[i];\n            down(height + 1);\n            drawPixel(Box.VERTICAL_AND_RIGHT);\n            right(1);\n            drawHorizontalLine(totalWidth);\n            drawPixel(Box.VERTICAL_AND_LEFT);\n            left(totalWidth + 1);\n        }\n\n    }\n    // endregion\n\n    public void printPixels() {\n        Collections.sort(pixelList);\n        StringBuilder sb = new StringBuilder();\n        Formatter fm = new Formatter(sb);\n        for (TextPixel pixel : pixelList) {\n            fm.format(\"[DEBUG] %s%n\", pixel);\n        }\n        System.out.println(sb);\n    }\n\n    public List<String> getLines() {\n        List<String> lines = new ArrayList<>();\n        Collections.sort(pixelList);\n\n        int maxRow = findMaxRow(pixelList);\n        for (int row = 0; row <= maxRow; row++) {\n            List<TextPixel> rowList = findRowItems(row);\n            int maxCol = findMaxCol(rowList);\n            int i = 0;\n            StringBuilder sb = new StringBuilder();\n            for (int col = 0; col <= maxCol; col++) {\n\n                TextPixel item = null;\n                if (i < rowList.size()) {\n                    item = rowList.get(i);\n                }\n\n                if (item != null && item.col == col) {\n                    sb.append(item.value);\n                    i++;\n                }\n                else {\n                    sb.append(Box.SPACE.val);\n                }\n            }\n            String line = sb.toString();\n            lines.add(line);\n        }\n        return lines;\n    }\n\n    @Override\n    public String toString() {\n        List<String> lines = getLines();\n        return StringUtils.list2str(lines);\n    }\n\n    public void draw(int row, int col, Drawable drawable) {\n        drawable.draw(this, row, col);\n    }\n\n    public void overlay(Canvas canvas) {\n        for (TextPixel pixel : canvas.pixelList) {\n            int targetRow = pixel.row;\n            int targetCol = pixel.col;\n            TextPixel targetPixel = findPixel(targetRow, targetCol);\n            if (targetPixel != null) {\n                if (Box.isValid(targetPixel.value) && Box.isValid(pixel.value)) {\n                    targetPixel.value = Box.merge(targetPixel.value, pixel.value).val;\n                }\n                else {\n                    targetPixel.value = pixel.value;\n                }\n            }\n            else {\n                targetPixel = TextPixel.valueOf(targetRow, targetCol, pixel.value);\n                pixelList.add(targetPixel);\n            }\n        }\n\n        Collections.sort(pixelList);\n    }\n\n    // region private methods\n    private TextPixel findPixel(int row, int col) {\n        for (TextPixel item : pixelList) {\n            if (item.row == row && item.col == col) {\n                return item;\n            }\n        }\n        return null;\n    }\n\n    private int findMaxRow(List<TextPixel> list) {\n        int maxRow = 0;\n        for (TextPixel item : list) {\n            if (item.row > maxRow) {\n                maxRow = item.row;\n            }\n        }\n        return maxRow;\n    }\n\n    private int findMaxCol(List<TextPixel> list) {\n        int maxCol = 0;\n        for (TextPixel item : list) {\n            if (item.col > maxCol) {\n                maxCol = item.col;\n            }\n        }\n        return maxCol;\n    }\n\n    private List<TextPixel> findRowItems(int row) {\n        List<TextPixel> list = new ArrayList<>();\n        for (TextPixel item : pixelList) {\n            if (item.col < 0) {\n                continue;\n            }\n            if (item.row == row) {\n                list.add(item);\n            }\n        }\n        return list;\n    }\n    // endregion\n}\n"
  },
  {
    "path": "src/main/java/lsieun/drawing/canvas/Drawable.java",
    "content": "package lsieun.drawing.canvas;\n\npublic interface Drawable {\n    void draw(Canvas canvas, int startRow, int startCol);\n}\n"
  },
  {
    "path": "src/main/java/lsieun/drawing/canvas/TextAlign.java",
    "content": "package lsieun.drawing.canvas;\n\npublic enum TextAlign {\n    LEFT,\n    CENTER,\n    RIGHT\n}\n"
  },
  {
    "path": "src/main/java/lsieun/drawing/canvas/TextDirection.java",
    "content": "package lsieun.drawing.canvas;\n\npublic enum TextDirection {\n    UP,\n    DOWN,\n    LEFT,\n    RIGHT\n}\n"
  },
  {
    "path": "src/main/java/lsieun/drawing/canvas/TextPixel.java",
    "content": "package lsieun.drawing.canvas;\n\npublic class TextPixel implements Comparable<TextPixel> {\n    public int row;\n    public int col;\n    public String value;\n\n    public TextPixel(int row, int col, String value) {\n        this.row = row;\n        this.col = col;\n        this.value = value;\n    }\n\n    @Override\n    public int compareTo(TextPixel item) {\n        if (this.row < item.row) {\n            return -1;\n        }\n        else if (this.row > item.row) {\n            return 1;\n        }\n        else if (this.col < item.col) {\n            return -1;\n        }\n        else if (this.col > item.col) {\n            return 1;\n        }\n        return 0;\n    }\n\n    @Override\n    public String toString() {\n        return String.format(\"TextPixel(%d, %d, %s)\", row, col, value);\n    }\n\n    public static TextPixel valueOf(int row, int col, String value) {\n        return new TextPixel(row, col, value);\n    }\n}\n"
  },
  {
    "path": "src/main/java/lsieun/drawing/theme/line/ContinuousLine.java",
    "content": "package lsieun.drawing.theme.line;\n\nimport lsieun.drawing.canvas.Box;\nimport lsieun.drawing.canvas.Canvas;\nimport lsieun.drawing.canvas.Drawable;\nimport lsieun.drawing.canvas.TextDirection;\n\npublic class ContinuousLine implements Drawable {\n    private final Canvas localCanvas = new Canvas();\n\n    private TextDirection direction = TextDirection.RIGHT;\n\n    public void setDirection(TextDirection direction) {\n        this.direction = direction;\n    }\n\n    public ContinuousLine turn(TextDirection direction) {\n        if (this.direction == TextDirection.UP && direction == TextDirection.LEFT) {\n            localCanvas.drawPixel(Box.DOWN_AND_LEFT);\n            localCanvas.left(1);\n        }\n        else if (this.direction == TextDirection.UP && direction == TextDirection.RIGHT) {\n            localCanvas.drawPixel(Box.DOWN_AND_RIGHT);\n            localCanvas.right(1);\n        }\n        else if (this.direction == TextDirection.RIGHT && direction == TextDirection.UP) {\n            localCanvas.drawPixel(Box.UP_AND_LEFT);\n            localCanvas.up(1);\n        }\n        else if (this.direction == TextDirection.RIGHT && direction == TextDirection.DOWN) {\n            localCanvas.drawPixel(Box.DOWN_AND_LEFT);\n            localCanvas.down(1);\n        }\n        else if (this.direction == TextDirection.DOWN && direction == TextDirection.RIGHT) {\n            localCanvas.drawPixel(Box.UP_AND_RIGHT);\n            localCanvas.right(1);\n        }\n        else if (this.direction == TextDirection.DOWN && direction == TextDirection.LEFT) {\n            localCanvas.drawPixel(Box.UP_AND_LEFT);\n            localCanvas.left(1);\n        }\n        else if (this.direction == TextDirection.LEFT && direction == TextDirection.DOWN) {\n            localCanvas.drawPixel(Box.DOWN_AND_RIGHT);\n            localCanvas.down(1);\n        }\n        else if (this.direction == TextDirection.LEFT && direction == TextDirection.UP) {\n            localCanvas.drawPixel(Box.UP_AND_RIGHT);\n            localCanvas.up(1);\n        }\n        else {\n            assert false : \"impossible here\";\n        }\n        this.direction = direction;\n        return this;\n    }\n\n    public ContinuousLine drawLine(int count) {\n        if (count < 0) {\n            throw new IllegalArgumentException(\"'count' should be greater than zero: \" + count);\n        }\n\n        switch (direction) {\n            case UP:\n                localCanvas.drawVerticalLine(-count);\n                break;\n            case RIGHT:\n                localCanvas.drawHorizontalLine(count);\n                break;\n            case DOWN:\n                localCanvas.drawVerticalLine(count);\n                break;\n            case LEFT:\n                localCanvas.drawHorizontalLine(-count);\n                break;\n            default:\n                assert false : \"impossible here\";\n        }\n        return this;\n    }\n\n    @Override\n    public void draw(Canvas canvas, int startRow, int startCol) {\n        localCanvas.updatePosition(startRow, startCol);\n        localCanvas.rectifyPosition();\n\n        canvas.overlay(localCanvas);\n    }\n}\n"
  },
  {
    "path": "src/main/java/lsieun/drawing/theme/shape/Rectangle.java",
    "content": "package lsieun.drawing.theme.shape;\n\nimport lsieun.drawing.canvas.Box;\nimport lsieun.drawing.canvas.Canvas;\nimport lsieun.drawing.canvas.Drawable;\n\npublic class Rectangle implements Drawable {\n    public final int width;\n    public final int height;\n\n    public Rectangle(int width, int height) {\n        this.width = width;\n        this.height = height;\n    }\n\n    @SuppressWarnings({\"UnnecessaryLocalVariable\", \"Duplicates\"})\n    @Override\n    public void draw(Canvas canvas, int startRow, int startCol) {\n        int left = startCol;\n        int right = left + width + 1;\n        int top = startRow;\n        int bottom = top + height + 1;\n\n        // four corners\n        canvas.moveTo(top, left);\n        canvas.drawPixel(Box.DOWN_AND_RIGHT);\n        canvas.moveTo(top, right);\n        canvas.drawPixel(Box.DOWN_AND_LEFT);\n        canvas.moveTo(bottom, left);\n        canvas.drawPixel(Box.UP_AND_RIGHT);\n        canvas.moveTo(bottom, right);\n        canvas.drawPixel(Box.UP_AND_LEFT);\n\n        // four borders\n        canvas.moveTo(top, left + 1);\n        canvas.drawHorizontalLine(width);\n        canvas.moveTo(bottom, left + 1);\n        canvas.drawHorizontalLine(width);\n        canvas.moveTo(top + 1, left);\n        canvas.drawVerticalLine(height);\n        canvas.moveTo(top + 1, right);\n        canvas.drawVerticalLine(height);\n    }\n}\n"
  },
  {
    "path": "src/main/java/lsieun/drawing/theme/shape/RectangleWithText.java",
    "content": "package lsieun.drawing.theme.shape;\n\nimport lsieun.drawing.canvas.Canvas;\nimport lsieun.drawing.canvas.Drawable;\nimport lsieun.drawing.canvas.TextAlign;\n\nimport java.util.List;\n\npublic class RectangleWithText extends Rectangle implements Drawable {\n    public final List<String> lines;\n    public final TextAlign align;\n\n\n    public RectangleWithText(int width, int height, List<String> lines, TextAlign align) {\n        super(width, height);\n        this.lines = lines;\n        this.align = align;\n    }\n\n    @SuppressWarnings({\"UnnecessaryLocalVariable\"})\n    @Override\n    public void draw(Canvas canvas, int startRow, int startCol) {\n        // draw border\n        super.draw(canvas, startRow, startCol);\n\n        int left = startCol;\n        int right = left + width + 1;\n        int top = startRow;\n\n        // text\n        if (lines == null) return;\n        int size = Math.min(lines.size(), height);\n        int length = width - 2;\n        if (length < 1) return;\n        for (int i = 0; i < size; i++) {\n            String item = lines.get(i);\n            if (item == null) continue;\n            if (item.length() > length) {\n                item = item.substring(0, length);\n            }\n            int row = top + i + 1;\n            switch (align) {\n                case LEFT: {\n                    canvas.moveTo(row, left + 2);\n                    canvas.drawText(item);\n                    break;\n                }\n                case CENTER: {\n                    canvas.moveTo(row, left + 2 + (length - item.length()) / 2);\n                    canvas.drawText(item);\n                    break;\n                }\n                case RIGHT: {\n                    canvas.moveTo(row, right - 1 - item.length());\n                    canvas.drawText(item);\n                    break;\n                }\n                default:\n                    throw new RuntimeException(\"unsupported align: \" + align);\n            }\n        }\n\n    }\n}\n"
  },
  {
    "path": "src/main/java/lsieun/drawing/theme/table/AbstractTable.java",
    "content": "package lsieun.drawing.theme.table;\n\nimport lsieun.drawing.canvas.Drawable;\n\npublic abstract class AbstractTable implements Drawable {\n    public final int cell_inner_padding;\n\n    public AbstractTable() {\n        this(1);\n    }\n\n    public AbstractTable(int cell_inner_padding) {\n        this.cell_inner_padding = cell_inner_padding;\n    }\n\n    protected abstract int getCellLength(int row, int col);\n\n    public int[] getColWidthArray(int rowCount, int colCount) {\n        int[] colWidthArray = new int[colCount];\n        for (int row = 0; row < rowCount; row++) {\n            for (int col = 0; col < colCount; col++) {\n                int length = getCellLength(row, col) + 2 * cell_inner_padding;\n                if (length > colWidthArray[col]) {\n                    colWidthArray[col] = length;\n                }\n            }\n        }\n        return colWidthArray;\n    }\n}\n"
  },
  {
    "path": "src/main/java/lsieun/drawing/theme/table/FixedWidthOneLineTable.java",
    "content": "package lsieun.drawing.theme.table;\n\nimport lsieun.drawing.canvas.Drawable;\nimport lsieun.drawing.canvas.TextAlign;\n\npublic class FixedWidthOneLineTable extends OneLineTable implements Drawable {\n    public final int fixedWidth;\n\n    public FixedWidthOneLineTable(String[][] matrix, TextAlign align, int fixedWidth) {\n        super(matrix, align);\n        this.fixedWidth = fixedWidth;\n    }\n\n    @Override\n    protected int getCellLength(int row, int col) {\n        return fixedWidth;\n    }\n}\n"
  },
  {
    "path": "src/main/java/lsieun/drawing/theme/table/OneLineTable.java",
    "content": "package lsieun.drawing.theme.table;\n\nimport lsieun.drawing.canvas.Canvas;\nimport lsieun.drawing.canvas.Drawable;\nimport lsieun.drawing.canvas.TextAlign;\n\npublic class OneLineTable extends AbstractTable implements Drawable {\n    public final String[][] matrix;\n    public final TextAlign align;\n\n    private final int row_padding;\n    private final int col_padding;\n\n    public OneLineTable(String[][] matrix, TextAlign align) {\n        this(matrix, align, 0, 1);\n    }\n\n    public OneLineTable(String[][] matrix, TextAlign align, int row_padding, int col_padding) {\n        this.matrix = matrix;\n        this.align = align;\n        this.row_padding = row_padding;\n        this.col_padding = col_padding;\n    }\n\n    @Override\n    protected int getCellLength(int row, int col) {\n        String item = matrix[row][col];\n        int length =  item == null ? 0 : item.length();\n        return length + 2 * col_padding;\n    }\n\n    @Override\n    public void draw(Canvas canvas, int startRow, int startCol) {\n        int rowCount = matrix.length;\n        int colCount = matrix[0].length;\n\n        int[] rowHeightArray = new int[rowCount];\n        int[] colWidthArray = getColWidthArray(rowCount, colCount);\n\n        for (int i = 0; i < rowCount; i++) {\n            rowHeightArray[i] = 1 + 2 * row_padding;\n        }\n\n        // draw border\n        canvas.moveTo(startRow, startCol);\n        canvas.drawTable(rowHeightArray, colWidthArray);\n\n        // draw text\n        int currentRow = startRow;\n        for (int i = 0; i < rowCount; i++) {\n            if (i > 0) {\n                currentRow += rowHeightArray[i - 1] + 1;\n            }\n\n            int currentCol = startCol;\n            for (int j = 0; j < colCount; j++) {\n                if (j > 0) {\n                    currentCol += colWidthArray[j - 1] + 1;\n                }\n\n                String item = matrix[i][j];\n                if (item == null) item = \"\";\n\n                int currentWidth = colWidthArray[j];\n\n                int row = currentRow + 1 + row_padding;\n                int left = currentCol;\n                int right = left + currentWidth + 1;\n\n                switch (align) {\n                    case LEFT: {\n                        canvas.moveTo(row, left + col_padding + 1);\n                        canvas.drawText(item);\n                        break;\n                    }\n                    case CENTER: {\n                        canvas.moveTo(row, left + (currentWidth - item.length()) / 2 + 1);\n                        canvas.drawText(item);\n                        break;\n                    }\n                    case RIGHT: {\n                        canvas.moveTo(row, right - col_padding - item.length());\n                        canvas.drawText(item);\n                        break;\n                    }\n                    default:\n                        assert false : \"impossible\";\n                }\n            }\n\n        }\n    }\n}\n"
  },
  {
    "path": "src/main/java/lsieun/drawing/theme/text/PlainText.java",
    "content": "package lsieun.drawing.theme.text;\n\nimport lsieun.drawing.canvas.Canvas;\nimport lsieun.drawing.canvas.Drawable;\n\nimport java.util.ArrayList;\nimport java.util.List;\n\npublic class PlainText implements Drawable {\n    public final List<String> lines = new ArrayList<>();\n\n    @Override\n    public void draw(Canvas canvas, int startRow, int startCol) {\n        int size = lines.size();\n        for (int i = 0; i < size; i++) {\n            String line = lines.get(i);\n            if (line == null) line = \"\";\n            canvas.moveTo(startRow + i, startCol)\n                    .drawText(line);\n        }\n    }\n\n    public static PlainText valueOf(String line) {\n        PlainText text = new PlainText();\n        text.lines.add(line);\n        return text;\n    }\n\n    public static PlainText valueOf(List<String> lines) {\n        PlainText text = new PlainText();\n        text.lines.addAll(lines);\n        return text;\n    }\n}\n"
  },
  {
    "path": "src/main/java/lsieun/trove/HashFunctions.java",
    "content": "package lsieun.trove;\n\npublic final class HashFunctions {\n    /**\n     * Returns a hashcode for the specified value.\n     *\n     * @return  a hash code value for the specified value. \n     */\n    public static int hash(double value) {\n        long bits = Double.doubleToLongBits(value);\n        return (int)(bits ^ (bits >>> 32));\n        //return (int) Double.doubleToLongBits(value*663608941.737);\n        //this avoids excessive hashCollisions in the case values are\n        //of the form (1.0, 2.0, 3.0, ...)\n    }\n\n    /**\n     * Returns a hashcode for the specified value.\n     *\n     * @return  a hash code value for the specified value. \n     */\n    public static int hash(float value) {\n        return Float.floatToIntBits(value*663608941.737f);\n        // this avoids excessive hashCollisions in the case values are\n        // of the form (1.0, 2.0, 3.0, ...)\n    }\n\n    /**\n     * Returns a hashcode for the specified value. The hashcode is computed as\n     * <blockquote><pre> \n     * 31^5*(d[0]*31^(n-1) + d[1]*31^(n-2) + ... + d[n-1])\n     * </pre></blockquote>\n     * using <code>int</code> arithmetic, where <code>d[i]</code> is\n     * the <i>i</i>th digit of the value, counting from the right,\n     * <code>n</code> is the number of decimal digits of the specified\n     * value, and <code>^</code> indicates exponentiation.  (The hash\n     * value of the value zero is zero.)\n     *\n     * @return  a hash code value for the specified value. \n     */\n    public static int hash(int value) {\n        //return value * 0x278DDE6D; // see cern.jet.random.engine.DRand\n\t\n        return value;\n\t\n        /*\n          value &= 0x7FFFFFFF; // make it >=0\n          int hashCode = 0;\n          do hashCode = 31*hashCode + value%10;\n          while ((value /= 10) > 0);\n\n          return 28629151*hashCode; // spread even further; h*31^5\n        */\n    }\n\n    /**\n     * Returns a hashcode for the specified value. \n     *\n     * @return  a hash code value for the specified value. \n     */\n    public static int hash(long value) {\n        return (int)(value ^ (value >> 32));\n\n        /*\n         * The hashcode is computed as\n         * <blockquote><pre> \n         * 31^5*(d[0]*31^(n-1) + d[1]*31^(n-2) + ... + d[n-1])\n         * </pre></blockquote>\n         * using <code>int</code> arithmetic, where <code>d[i]</code> is the \n         * <i>i</i>th digit of the value, counting from the right, <code>n</code> is the number of decimal digits of the specified value,\n         * and <code>^</code> indicates exponentiation.\n         * (The hash value of the value zero is zero.)\n \n         value &= 0x7FFFFFFFFFFFFFFFL; // make it >=0 (0x7FFFFFFFFFFFFFFFL==Long.MAX_VALUE)\n         int hashCode = 0;\n         do hashCode = 31*hashCode + (int) (value%10);\n         while ((value /= 10) > 0);\n\n         return 28629151*hashCode; // spread even further; h*31^5\n        */\n    }\n\n    /**\n     * Returns a hashcode for the specified object.\n     *\n     * @return  a hash code value for the specified object. \n     */\n    public static int hash(Object object) {\n        return object==null ? 0 : object.hashCode();\n    }\n}"
  },
  {
    "path": "src/main/java/lsieun/trove/PrimeFinder.java",
    "content": "package lsieun.trove;\n\nimport java.util.Arrays;\n\npublic final class PrimeFinder {\n\t/**\n\t * The largest prime this class can generate; currently equal to\n\t * <tt>Integer.MAX_VALUE</tt>.\n\t */\n\tpublic static final int largestPrime = Integer.MAX_VALUE; //yes, it is prime.\n\n\t/**\n\t * The prime number list.\n\t * Primes are chosen such that for any desired capacity >= 1000\n\t * the list includes a prime number <= desired capacity * 1.11.\n     *\n\t * Therefore, primes can be retrieved which are quite close to any\n\t * desired capacity, which in turn avoids wasting memory.\n     *\n\t * For example, the list includes\n\t * 1039,1117,1201,1277,1361,1439,1523,1597,1759,1907,2081.\n     *\n\t * So if you need a prime >= 1040, you will find a prime <=\n\t * 1040*1.11=1154.\n\t *\t\n\t * Primes are chosen such that they are optimized for a hashtable\n\t * growthfactor of 2.0;\n     *\n\t * If your hashtable has such a growthfactor then, after initially\n\t * \"rounding to a prime\" upon hashtable construction, it will\n\t * later expand to prime capacities such that there exist no\n\t * better primes.\n\t *\n\t * In total these are about 32*10=320 numbers -> 1 KB of static\n\t * memory needed.\n\t */\n\t\n\tprivate static final int[] primeCapacities = {\n\t\t3, 5, 7, 11, 17, 23, 31, 37, 43, 47, 67, 79, 89, 97, 137, 163, 179, 197, 277, 311, 331, 359, 379, 397, 433, 557,\n\t\t599, 631, 673, 719, 761, 797, 877, 953, 1039, 1117, 1201, 1277, 1361, 1439, 1523, 1597, 1759, 1907, 2081, 2237,\n\t\t2411, 2557, 2729, 2879, 3049, 3203, 3527, 3821, 4177, 4481, 4831, 5119, 5471, 5779, 6101, 6421, 7057, 7643, 8363,\n\t\t8963, 9677, 10243, 10949, 11579, 12203, 12853, 14143, 15287, 16729, 17929, 19373, 20507, 21911, 23159, 24407,\n\t\t25717, 28289, 30577, 33461, 35863, 38747, 41017, 43853, 46327, 48817, 51437, 56591, 61169, 66923, 71741, 77509,\n\t\t82037, 87719, 92657, 97649, 102877, 113189, 122347, 133853, 143483, 155027, 164089, 175447, 185323, 195311, 205759,\n\t\t226379, 244703, 267713, 286973, 310081, 328213, 350899, 370661, 390647, 411527, 452759, 489407, 535481, 573953,\n\t\t620171, 656429, 701819, 741337, 781301, 823117, 905551, 978821, 1070981, 1147921, 1240361, 1312867, 1403641,\n\t\t1482707, 1562611, 1646237, 1811107, 1957651, 2141977, 2295859, 2480729, 2625761, 2807303, 2965421, 3125257,\n\t\t3292489, 3622219, 3915341, 4283963, 4591721, 4961459, 5251529, 5614657, 5930887, 6250537, 6584983, 7244441,\n\t\t7830701, 8567929, 9183457, 9922933, 10503061, 11229331, 11861791, 12501169, 13169977, 14488931, 15661423,\n\t\t17135863, 18366923, 19845871, 21006137, 22458671, 23723597, 25002389, 26339969, 28977863, 31322867, 34271747,\n\t\t36733847, 39691759, 42012281, 44917381, 47447201, 50004791, 52679969, 57955739, 62645741, 68543509, 73467739,\n\t\t79383533, 84024581, 89834777, 94894427, 100009607, 105359939, 115911563, 125291483, 137087021, 146935499,\n\t\t158767069, 168049163, 179669557, 189788857, 200019221, 210719881, 231823147, 250582987, 274174111, 293871013,\n\t\t317534141, 336098327, 359339171, 379577741, 400038451, 421439783, 463646329, 501165979, 548348231, 587742049,\n\t\t635068283, 672196673, 718678369, 759155483, 800076929, 842879579, 927292699, 1002331963, 1096696463, 1175484103,\n\t\t1270136683, 1344393353, 1437356741, 1518310967, 1600153859, 1685759167, 1854585413, 2004663929, 2147483647\n\t};\n\n    /**\n     * Returns a prime number which is <code>&gt;= desiredCapacity</code>\n     * and very close to <code>desiredCapacity</code> (within 11% if\n     * <code>desiredCapacity &gt;= 1000</code>).\n     *\n     * @param desiredCapacity the capacity desired by the user.\n     * @return the capacity which should be used for a hashtable.\n     */\n    public static int nextPrime(int desiredCapacity) {\n        int i = Arrays.binarySearch(primeCapacities, desiredCapacity);\n        if (i<0) {\n            // desired capacity not found, choose next prime greater\n            // than desired capacity\n            i = -i -1; // remember the semantics of binarySearch...\n        }\n        return primeCapacities[i];\n    }\n}"
  },
  {
    "path": "src/main/java/lsieun/trove/THash.java",
    "content": "package lsieun.trove;\n\npublic abstract class THash implements Cloneable {\n    /** the current number of occupied slots in the hash. */\n    protected transient int _size;\n\n    /** the current number of free slots in the hash. */\n    protected transient int _free;\n\n    /**\n     * Number of entries marked REMOVED (by either TObjectHash or TPrimitiveHash)\n     */\n    protected transient int _deadkeys;\n\n    /**\n     * the load above which rehashing occurs.\n     */\n    protected static final float DEFAULT_LOAD_FACTOR = 0.8f;\n\n    /** the default initial capacity for the hash table.  This is one\n     * less than a prime value because one is added to it when\n     * searching for a prime capacity to account for the free slot\n     * required by open addressing. Thus, the real default capacity is\n     * 11.\n     */\n    protected static final int DEFAULT_INITIAL_CAPACITY = 4;\n    // fake capacity which means the map was just created; used for conserving memory\n    protected static final int JUST_CREATED_CAPACITY = -1;\n    protected static final Object[] EMPTY_OBJECT_ARRAY = new Object[0];\n\n    /** Determines how full the internal table can become before\n     * rehashing is required. This must be a value in the range: 0.0 <\n     * loadFactor < 1.0.  The default value is 0.5, which is about as\n     * large as you can get in open addressing without hurting\n     * performance.  Cf. Knuth, Volume 3., Chapter 6.\n     */\n    protected final float _loadFactor;\n\n    /**\n     * The maximum number of elements allowed without allocating more\n     * space.\n     */\n    protected int _maxSize;\n\n    /**\n     * Creates a new <code>THash</code> instance with the default\n     * capacity and load factor.\n     */\n    public THash() {\n        this(JUST_CREATED_CAPACITY, DEFAULT_LOAD_FACTOR);\n    }\n\n    /**\n     * Creates a new <code>THash</code> instance with a prime capacity\n     * at or near the specified capacity and with the default load\n     * factor.\n     *\n     * @param initialCapacity an <code>int</code> value\n     */\n    public THash(int initialCapacity) {\n        this(initialCapacity, DEFAULT_LOAD_FACTOR);\n    }\n\n    /**\n     * Creates a new <code>THash</code> instance with a prime capacity\n     * at or near the minimum needed to hold <tt>initialCapacity</tt>\n     * elements with load factor <tt>loadFactor</tt> without triggering\n     * a rehash.\n     *\n     * @param initialCapacity an <code>int</code> value\n     * @param loadFactor      a <code>float</code> value\n     */\n    public THash(int initialCapacity, float loadFactor) {\n        super();\n        _loadFactor = loadFactor;\n        setUp(initialCapacity == JUST_CREATED_CAPACITY ? JUST_CREATED_CAPACITY : (int) (initialCapacity / loadFactor) + 1);\n    }\n\n    @Override\n    public Object clone() {\n        try {\n            return super.clone();\n        } catch (CloneNotSupportedException cnse) {\n            return null; // it's supported\n        }\n    }\n\n    /**\n     * Tells whether this set is currently holding any elements.\n     *\n     * @return a <code>boolean</code> value\n     */\n    public boolean isEmpty() {\n        return 0 == _size;\n    }\n\n    /**\n     * Returns the number of distinct elements in this collection.\n     *\n     * @return an <code>int</code> value\n     */\n    public int size() {\n        return _size;\n    }\n\n    /**\n     * @return the current physical capacity of the hash table.\n     */\n    protected abstract int capacity();\n\n    /**\n     * Ensure that this hashtable has sufficient capacity to hold\n     * <tt>desiredCapacity<tt> <b>additional</b> elements without\n     * requiring a rehash.  This is a tuning method you can call\n     * before doing a large insert.\n     *\n     * @param desiredCapacity an <code>int</code> value\n     */\n    public void ensureCapacity(int desiredCapacity) {\n        if (desiredCapacity > (_maxSize - size())) {\n            rehash(PrimeFinder.nextPrime((int) ((desiredCapacity + size()) / _loadFactor) + 2));\n            computeMaxSize(capacity());\n        }\n    }\n\n    /**\n     * Compresses the hashtable to the minimum prime size (as defined\n     * by PrimeFinder) that will hold all of the elements currently in\n     * the table.  If you have done a lot of <tt>remove</tt>\n     * operations and plan to do a lot of queries or insertions or\n     * iteration, it is a good idea to invoke this method.  Doing so\n     * will accomplish two things:\n     *\n     * <ol>\n     * <li> You'll free memory allocated to the table but no\n     * longer needed because of the remove()s.</li>\n     *\n     * <li> You'll get better query/insert/iterator performance\n     * because there won't be any <tt>REMOVED</tt> slots to skip\n     * over when probing for indices in the table.</li>\n     * </ol>\n     */\n    public void compact() {\n        // need at least one free spot for open addressing\n        rehash(PrimeFinder.nextPrime((int) (size() / _loadFactor) + 2));\n        computeMaxSize(capacity());\n    }\n\n    /**\n     * This simply calls {@link #compact compact}.  It is included for\n     * symmetry with other collection classes.  Note that the name of this\n     * method is somewhat misleading (which is why we prefer\n     * <tt>compact</tt>) as the load factor may require capacity above\n     * and beyond the size of this collection.\n     *\n     * @see #compact\n     */\n    public final void trimToSize() {\n        compact();\n    }\n\n    /**\n     * Delete the record at <tt>index</tt>.  Reduces the size of the\n     * collection by one.\n     *\n     * @param index an <code>int</code> value\n     */\n    protected void removeAt(int index) {\n        _size--;\n        _deadkeys++;\n\n        compactIfNecessary();\n    }\n\n    private void compactIfNecessary() {\n        if (_deadkeys > _size && capacity() > 42) {\n            // Compact if more than 50% of all keys are dead. Also, don't trash small maps\n            compact();\n        }\n    }\n\n    public final void stopCompactingOnRemove() {\n        if (_deadkeys < 0) {\n            throw new IllegalStateException(\"Unpaired stop/startCompactingOnRemove\");\n        }\n\n        _deadkeys -= capacity();\n    }\n\n    public final void startCompactingOnRemove(boolean compact) {\n        if (_deadkeys > 0) {\n            throw new IllegalStateException(\"Unpaired stop/startCompactingOnRemove\");\n        }\n        _deadkeys += capacity();\n\n        if (compact) {\n            compactIfNecessary();\n        }\n    }\n\n    /**\n     * Empties the collection.\n     */\n    public void clear() {\n        _size = 0;\n        _free = capacity();\n        _deadkeys = 0;\n    }\n\n    /**\n     * initializes the hashtable to a prime capacity which is at least\n     * <tt>initialCapacity + 1</tt>.\n     *\n     * @param initialCapacity an <code>int</code> value\n     * @return the actual capacity chosen\n     */\n    protected int setUp(int initialCapacity) {\n        int capacity = initialCapacity == JUST_CREATED_CAPACITY ? 0 : PrimeFinder.nextPrime(initialCapacity);\n        computeMaxSize(capacity);\n        return capacity;\n    }\n\n    /**\n     * Rehashes the set.\n     *\n     * @param newCapacity an <code>int</code> value\n     */\n    protected abstract void rehash(int newCapacity);\n\n    /**\n     * Computes the values of maxSize. There will always be at least\n     * one free slot required.\n     *\n     * @param capacity an <code>int</code> value\n     */\n    private void computeMaxSize(int capacity) {\n        // need at least one free slot for open addressing\n        _maxSize = Math.max(0, Math.min(capacity - 1, (int) (capacity * _loadFactor)));\n        _free = capacity - _size; // reset the free element count\n        _deadkeys = 0;\n    }\n\n    /**\n     * After an insert, this hook is called to adjust the size/free\n     * values of the set and to perform rehashing if necessary.\n     */\n    protected final void postInsertHook(boolean usedFreeSlot) {\n        if (usedFreeSlot) {\n            _free--;\n        }\n        else {\n            _deadkeys--;\n        }\n\n        // rehash whenever we exhaust the available space in the table\n        if (++_size > _maxSize || _free == 0) {\n            rehash(PrimeFinder.nextPrime(calculateGrownCapacity()));\n            computeMaxSize(capacity());\n        }\n    }\n\n    protected int calculateGrownCapacity() {\n        return capacity() << 1;\n    }\n}// THash"
  },
  {
    "path": "src/main/java/lsieun/trove/TIntArrayList.java",
    "content": "package lsieun.trove;\n\nimport java.io.Serializable;\nimport java.util.Arrays;\nimport java.util.Objects;\nimport java.util.Random;\n\n/**\n * A resizable, array-backed list of int primitives.\n * <p>\n *\n * @author Eric D. Friedman\n */\npublic class TIntArrayList implements Serializable, Cloneable {\n\n    /**\n     * the data of the list\n     */\n    protected transient int[] _data; // null means the array list was just created\n\n    /**\n     * the index after the last entry in the list\n     */\n    protected transient int _pos;\n\n    /**\n     * the default capacity for new lists\n     */\n    protected static final int DEFAULT_CAPACITY = 4;\n\n    /**\n     * Creates a new <code>TIntArrayList</code> instance with the\n     * default capacity.\n     */\n    public TIntArrayList() {\n    }\n\n    /**\n     * Creates a new <code>TIntArrayList</code> instance with the\n     * specified capacity.\n     *\n     * @param capacity an <code>int</code> value\n     */\n    public TIntArrayList(int capacity) {\n        _data = new int[capacity];\n        _pos = 0;\n    }\n\n    /**\n     * Creates a new <code>TIntArrayList</code> instance whose\n     * capacity is the greater of the length of <tt>values</tt> and\n     * DEFAULT_CAPACITY and whose initial contents are the specified\n     * values.\n     *\n     * @param values an <code>int[]</code> value\n     */\n    public TIntArrayList(int[] values) {\n        this(Math.max(values.length, DEFAULT_CAPACITY));\n        add(values);\n    }\n\n    // sizing\n\n    /**\n     * Grow the internal array as needed to accommodate the specified\n     * number of elements.  The size of the array doubles on each\n     * resize unless <tt>capacity</tt> requires more than twice the\n     * current capacity.\n     *\n     * @param capacity an <code>int</code> value\n     */\n    public void ensureCapacity(int capacity) {\n        if (_data == null) {\n            _data = new int[Math.max(DEFAULT_CAPACITY, capacity)];\n        }\n        if (capacity > _data.length) {\n            int newCap = Math.max(_data.length << 1, capacity);\n            int[] tmp = new int[newCap];\n            System.arraycopy(_data, 0, tmp, 0, _data.length);\n            _data = tmp;\n        }\n    }\n\n    /**\n     * Returns the number of values in the list.\n     *\n     * @return the number of values in the list.\n     */\n    public int size() {\n        return _pos;\n    }\n\n    /**\n     * Tests whether this list contains any values.\n     *\n     * @return true if the list is empty.\n     */\n    public boolean isEmpty() {\n        return _pos == 0;\n    }\n\n    /**\n     * Sheds any excess capacity above and beyond the current size of\n     * the list.\n     */\n    public void trimToSize() {\n        if (_data != null && _data.length > size()) {\n            int[] tmp = new int[size()];\n            toNativeArray(tmp, 0, tmp.length);\n            _data = tmp;\n        }\n    }\n\n    // modifying\n\n    /**\n     * Adds <tt>val</tt> to the end of the list, growing as needed.\n     *\n     * @param val an <code>int</code> value\n     */\n    public void add(int val) {\n        ensureCapacity(_pos + 1);\n        _data[_pos++] = val;\n    }\n\n    /**\n     * Adds the values in the array <tt>vals</tt> to the end of the\n     * list, in order.\n     *\n     * @param vals an <code>int[]</code> value\n     */\n    public void add(int[] vals) {\n        add(vals, 0, vals.length);\n    }\n\n    /**\n     * Adds a subset of the values in the array <tt>vals</tt> to the\n     * end of the list, in order.\n     *\n     * @param vals   an <code>int[]</code> value\n     * @param offset the offset at which to start copying\n     * @param length the number of values to copy.\n     */\n    public void add(int[] vals, int offset, int length) {\n        ensureCapacity(_pos + length);\n        System.arraycopy(vals, offset, _data, _pos, length);\n        _pos += length;\n    }\n\n    /**\n     * Inserts <tt>value</tt> into the list at <tt>offset</tt>.  All\n     * values including and to the right of <tt>offset</tt> are shifted\n     * to the right.\n     *\n     * @param offset an <code>int</code> value\n     * @param value  an <code>int</code> value\n     */\n    public void insert(int offset, int value) {\n        if (offset == _pos) {\n            add(value);\n            return;\n        }\n        ensureCapacity(_pos + 1);\n        // shift right\n        System.arraycopy(_data, offset, _data, offset + 1, _pos - offset);\n        // insert\n        _data[offset] = value;\n        _pos++;\n    }\n\n    /**\n     * Inserts the array of <tt>values</tt> into the list at\n     * <tt>offset</tt>.  All values including and to the right of\n     * <tt>offset</tt> are shifted to the right.\n     *\n     * @param offset an <code>int</code> value\n     * @param values an <code>int[]</code> value\n     */\n    public void insert(int offset, int[] values) {\n        insert(offset, values, 0, values.length);\n    }\n\n    /**\n     * Inserts a slice of the array of <tt>values</tt> into the list\n     * at <tt>offset</tt>.  All values including and to the right of\n     * <tt>offset</tt> are shifted to the right.\n     *\n     * @param offset    an <code>int</code> value\n     * @param values    an <code>int[]</code> value\n     * @param valOffset the offset in the values array at which to\n     *                  start copying.\n     * @param len       the number of values to copy from the values array\n     */\n    public void insert(int offset, int[] values, int valOffset, int len) {\n        if (offset == _pos) {\n            add(values, valOffset, len);\n            return;\n        }\n\n        ensureCapacity(_pos + len);\n        // shift right\n        System.arraycopy(_data, offset, _data, offset + len, _pos - offset);\n        // insert\n        System.arraycopy(values, valOffset, _data, offset, len);\n        _pos += len;\n    }\n\n    /**\n     * Returns the value at the specified offset.\n     *\n     * @param offset an <code>int</code> value\n     * @return an <code>int</code> value\n     */\n    public int get(int offset) {\n        if (offset >= _pos) {\n            throw new ArrayIndexOutOfBoundsException(offset);\n        }\n        return _data[offset];\n    }\n\n    /**\n     * Returns the value at the specified offset without doing any\n     * bounds checking.\n     *\n     * @param offset an <code>int</code> value\n     * @return an <code>int</code> value\n     */\n    public int getQuick(int offset) {\n        return _data[offset];\n    }\n\n    /**\n     * Sets the value at the specified offset.\n     *\n     * @param offset an <code>int</code> value\n     * @param val    an <code>int</code> value\n     */\n    public void set(int offset, int val) {\n        if (offset < 0 || offset >= _pos) {\n            throw new ArrayIndexOutOfBoundsException(offset);\n        }\n        _data[offset] = val;\n    }\n\n    /**\n     * Sets the value at the specified offset and returns the\n     * previously stored value.\n     *\n     * @param offset an <code>int</code> value\n     * @param val    an <code>int</code> value\n     * @return the value previously stored at offset.\n     */\n    public int getSet(int offset, int val) {\n        if (offset < 0 || offset >= _pos) {\n            throw new ArrayIndexOutOfBoundsException(offset);\n        }\n        int old = _data[offset];\n        _data[offset] = val;\n        return old;\n    }\n\n    /**\n     * Replace the values in the list starting at <tt>offset</tt> with\n     * the contents of the <tt>values</tt> array.\n     *\n     * @param offset the first offset to replace\n     * @param values the source of the new values\n     */\n    public void set(int offset, int[] values) {\n        set(offset, values, 0, values.length);\n    }\n\n    /**\n     * Replace the values in the list starting at <tt>offset</tt> with\n     * <tt>length</tt> values from the <tt>values</tt> array, starting\n     * at valOffset.\n     *\n     * @param offset    the first offset to replace\n     * @param values    the source of the new values\n     * @param valOffset the first value to copy from the values array\n     * @param length    the number of values to copy\n     */\n    public void set(int offset, int[] values, int valOffset, int length) {\n        if (offset < 0 || offset + length > _pos) {\n            throw new ArrayIndexOutOfBoundsException(offset);\n        }\n        System.arraycopy(_data, offset, values, valOffset, length);\n    }\n\n    /**\n     * Sets the value at the specified offset without doing any bounds\n     * checking.\n     *\n     * @param offset an <code>int</code> value\n     * @param val    an <code>int</code> value\n     */\n    public void setQuick(int offset, int val) {\n        _data[offset] = val;\n    }\n\n    /**\n     * Flushes the internal state of the list, resetting the capacity\n     * to the default.\n     */\n    public void clear() {\n        _data = null;\n        _pos = 0;\n    }\n\n    /**\n     * Flushes the internal state of the list, setting the capacity of\n     * the empty list to <tt>capacity</tt>.\n     *\n     * @param capacity an <code>int</code> value\n     */\n    public void clear(int capacity) {\n        _data = new int[capacity];\n        _pos = 0;\n    }\n\n    /**\n     * Sets the size of the list to 0, but does not change its\n     * capacity.  This method can be used as an alternative to the\n     * {@link #clear clear} method if you want to recycle a list without\n     * allocating new backing arrays.\n     *\n     * @see #clear\n     */\n    public void reset() {\n        fill((int) 0);\n        _pos = 0;\n    }\n\n    /**\n     * Sets the size of the list to 0, but does not change its\n     * capacity.  This method can be used as an alternative to the\n     * {@link #clear clear} method if you want to recycle a list\n     * without allocating new backing arrays.  This method differs\n     * from {@link #reset reset} in that it does not clear the old\n     * values in the backing array.  Thus, it is possible for {@link\n     * #getQuick getQuick} to return stale data if this method is used\n     * and the caller is careless about bounds checking.\n     *\n     * @see #reset\n     * @see #clear\n     * @see #getQuick\n     */\n    public void resetQuick() {\n        _pos = 0;\n    }\n\n    /**\n     * Removes the value at <tt>offset</tt> from the list.\n     *\n     * @param offset an <code>int</code> value\n     * @return the value previously stored at offset.\n     */\n    public int remove(int offset) {\n        int old = get(offset);\n        remove(offset, 1);\n        return old;\n    }\n\n    /**\n     * Removes <tt>length</tt> values from the list, starting at\n     * <tt>offset</tt>\n     *\n     * @param offset an <code>int</code> value\n     * @param length an <code>int</code> value\n     */\n    public void remove(int offset, int length) {\n        if (offset < 0 || offset >= _pos) {\n            throw new ArrayIndexOutOfBoundsException(offset);\n        }\n\n        if (offset == 0) {\n            // data at the front\n            System.arraycopy(_data, length, _data, 0, _pos - length);\n        }\n        else if (_pos - length == offset) {\n            // no copy to make, decrementing pos \"deletes\" values at\n            // the end\n        }\n        else {\n            // data in the middle\n            System.arraycopy(_data, offset + length,\n                    _data, offset, _pos - (offset + length));\n        }\n        _pos -= length;\n        // no need to clear old values beyond _pos, because this is a\n        // primitive collection and 0 takes as much room as any other\n        // value\n    }\n\n    /**\n     * Transform each value in the list using the specified function.\n     *\n     * @param function a <code>TIntFunction</code> value\n     */\n    public void transformValues(TIntFunction function) {\n        for (int i = _pos; i-- > 0; ) {\n            _data[i] = function.execute(_data[i]);\n        }\n    }\n\n    /**\n     * Reverse the order of the elements in the list.\n     */\n    public void reverse() {\n        reverse(0, _pos);\n    }\n\n    /**\n     * Reverse the order of the elements in the range of the list.\n     *\n     * @param from the inclusive index at which to start reversing\n     * @param to   the exclusive index at which to stop reversing\n     */\n    public void reverse(int from, int to) {\n        if (from == to) {\n            return;             // nothing to do\n        }\n        if (from > to) {\n            throw new IllegalArgumentException(\"from cannot be greater than to\");\n        }\n        for (int i = from, j = to - 1; i < j; i++, j--) {\n            swap(i, j);\n        }\n    }\n\n    /**\n     * Shuffle the elements of the list using the specified random\n     * number generator.\n     *\n     * @param rand a <code>Random</code> value\n     */\n    public void shuffle(Random rand) {\n        for (int i = _pos; i-- > 1; ) {\n            swap(i, rand.nextInt(i));\n        }\n    }\n\n    /**\n     * Swap the values at offsets <tt>i</tt> and <tt>j</tt>.\n     *\n     * @param i an offset into the data array\n     * @param j an offset into the data array\n     */\n    private void swap(int i, int j) {\n        int tmp = _data[i];\n        _data[i] = _data[j];\n        _data[j] = tmp;\n    }\n\n    // copying\n\n    /**\n     * Returns a clone of this list.  Since this is a primitive\n     * collection, this will be a deep clone.\n     *\n     * @return a deep clone of the list.\n     */\n    @Override\n    public Object clone() {\n        TIntArrayList clone = null;\n        try {\n            clone = (TIntArrayList) super.clone();\n            clone._data = _data == null ? null : _data.clone();\n        }\n        catch (CloneNotSupportedException e) {\n            // it's supported\n        } // end of try-catch\n        return clone;\n    }\n\n    /**\n     * Copies the contents of the list into a native array.\n     *\n     * @return an <code>int[]</code> value\n     */\n    public int[] toNativeArray() {\n        return toNativeArray(0, _pos);\n    }\n\n    /**\n     * Copies a slice of the list into a native array.\n     *\n     * @param offset the offset at which to start copying\n     * @param len    the number of values to copy.\n     * @return an <code>int[]</code> value\n     */\n    public int[] toNativeArray(int offset, int len) {\n        int[] rv = new int[len];\n        toNativeArray(rv, offset, len);\n        return rv;\n    }\n\n    /**\n     * Copies a slice of the list into a native array.\n     *\n     * @param dest   the array to copy into.\n     * @param offset the offset of the first value to copy\n     * @param len    the number of values to copy.\n     */\n    public void toNativeArray(int[] dest, int offset, int len) {\n        if (len == 0) {\n            return;             // nothing to copy\n        }\n        if (offset < 0 || offset >= _pos) {\n            throw new ArrayIndexOutOfBoundsException(offset);\n        }\n        System.arraycopy(_data, offset, dest, 0, len);\n    }\n\n    // comparing\n\n    /**\n     * Compares this list to another list, value by value.\n     *\n     * @param other the object to compare against\n     * @return true if other is a TIntArrayList and has exactly the\n     * same values.\n     */\n    @Override\n    public boolean equals(Object other) {\n        if (other == this) {\n            return true;\n        }\n        else if (other instanceof TIntArrayList) {\n            TIntArrayList that = (TIntArrayList) other;\n            if (that.size() != size()) {\n                return false;\n            }\n            else {\n                for (int i = _pos; i-- > 0; ) {\n                    if (_data[i] != that._data[i]) {\n                        return false;\n                    }\n                }\n                return true;\n            }\n        }\n        else {\n            return false;\n        }\n    }\n\n    @Override\n    public int hashCode() {\n        int h = 0;\n        for (int i = _pos; i-- > 0; ) {\n            h += Objects.hashCode(_data[i]);\n        }\n        return h;\n    }\n\n    // procedures\n\n    /**\n     * Applies the procedure to each value in the list in ascending\n     * (front to back) order.\n     *\n     * @param procedure a <code>TIntProcedure</code> value\n     * @return true if the procedure did not terminate prematurely.\n     */\n    public boolean forEach(TIntProcedure procedure) {\n        for (int i = 0; i < _pos; i++) {\n            if (!procedure.execute(_data[i])) {\n                return false;\n            }\n        }\n        return true;\n    }\n\n    /**\n     * Applies the procedure to each value in the list in descending\n     * (back to front) order.\n     *\n     * @param procedure a <code>TIntProcedure</code> value\n     * @return true if the procedure did not terminate prematurely.\n     */\n    public boolean forEachDescending(TIntProcedure procedure) {\n        for (int i = _pos; i-- > 0; ) {\n            if (!procedure.execute(_data[i])) {\n                return false;\n            }\n        }\n        return true;\n    }\n\n    // sorting\n\n    /**\n     * Sort the values in the list (ascending) using the Sun quicksort\n     * implementation.\n     *\n     * @see java.util.Arrays#sort\n     */\n    public void sort() {\n        if (!isEmpty()) Arrays.sort(_data, 0, _pos);\n    }\n\n    /**\n     * Sort a slice of the list (ascending) using the Sun quicksort\n     * implementation.\n     *\n     * @param fromIndex the index at which to start sorting (inclusive)\n     * @param toIndex   the index at which to stop sorting (exclusive)\n     * @see java.util.Arrays#sort\n     */\n    public void sort(int fromIndex, int toIndex) {\n        if (!isEmpty()) Arrays.sort(_data, fromIndex, toIndex);\n    }\n\n    // filling\n\n    /**\n     * Fills every slot in the list with the specified value.\n     *\n     * @param val the value to use when filling\n     */\n    public void fill(int val) {\n        if (!isEmpty()) Arrays.fill(_data, 0, _pos, val);\n    }\n\n    /**\n     * Fills a range in the list with the specified value.\n     *\n     * @param fromIndex the offset at which to start filling (inclusive)\n     * @param toIndex   the offset at which to stop filling (exclusive)\n     * @param val       the value to use when filling\n     */\n    public void fill(int fromIndex, int toIndex, int val) {\n        if (toIndex > _pos) {\n            ensureCapacity(toIndex);\n            _pos = toIndex;\n        }\n        if (!isEmpty()) Arrays.fill(_data, fromIndex, toIndex, val);\n    }\n\n    // searching\n\n    /**\n     * Performs a binary search for <tt>value</tt> in the entire list.\n     * Note that you <b>must</b> {@link #sort sort} the list before\n     * doing a search.\n     *\n     * @param value the value to search for\n     * @return the absolute offset in the list of the value, or its\n     * negative insertion point into the sorted list.\n     */\n    public int binarySearch(int value) {\n        return binarySearch(value, 0, _pos);\n    }\n\n    /**\n     * Performs a binary search for <tt>value</tt> in the specified\n     * range.  Note that you <b>must</b> {@link #sort sort} the list\n     * or the range before doing a search.\n     *\n     * @param value     the value to search for\n     * @param fromIndex the lower boundary of the range (inclusive)\n     * @param toIndex   the upper boundary of the range (exclusive)\n     * @return the absolute offset in the list of the value, or its\n     * negative insertion point into the sorted list.\n     */\n    public int binarySearch(int value, int fromIndex, int toIndex) {\n        if (fromIndex < 0) {\n            throw new ArrayIndexOutOfBoundsException(fromIndex);\n        }\n        if (toIndex > _pos) {\n            throw new ArrayIndexOutOfBoundsException(toIndex);\n        }\n\n        int low = fromIndex;\n        int high = toIndex - 1;\n\n        while (low <= high) {\n            int mid = (low + high) >> 1;\n            int midVal = _data[mid];\n\n            if (midVal < value) {\n                low = mid + 1;\n            }\n            else if (midVal > value) {\n                high = mid - 1;\n            }\n            else {\n                return mid; // value found\n            }\n        }\n        return -(low + 1);  // value not found.\n    }\n\n    /**\n     * Searches the list front to back for the index of\n     * <tt>value</tt>.\n     *\n     * @param value an <code>int</code> value\n     * @return the first offset of the value, or -1 if it is not in\n     * the list.\n     * @see #binarySearch for faster searches on sorted lists\n     */\n    public int indexOf(int value) {\n        return indexOf(0, value);\n    }\n\n    /**\n     * Searches the list front to back for the index of\n     * <tt>value</tt>, starting at <tt>offset</tt>.\n     *\n     * @param offset the offset at which to start the linear search\n     *               (inclusive)\n     * @param value  an <code>int</code> value\n     * @return the first offset of the value, or -1 if it is not in\n     * the list.\n     * @see #binarySearch for faster searches on sorted lists\n     */\n    public int indexOf(int offset, int value) {\n        for (int i = offset; i < _pos; i++) {\n            if (_data[i] == value) {\n                return i;\n            }\n        }\n        return -1;\n    }\n\n    /**\n     * Searches the list back to front for the last index of\n     * <tt>value</tt>.\n     *\n     * @param value an <code>int</code> value\n     * @return the last offset of the value, or -1 if it is not in\n     * the list.\n     * @see #binarySearch for faster searches on sorted lists\n     */\n    public int lastIndexOf(int value) {\n        return lastIndexOf(_pos, value);\n    }\n\n    /**\n     * Searches the list back to front for the last index of\n     * <tt>value</tt>, starting at <tt>offset</tt>.\n     *\n     * @param offset the offset at which to start the linear search\n     *               (exclusive)\n     * @param value  an <code>int</code> value\n     * @return the last offset of the value, or -1 if it is not in\n     * the list.\n     * @see #binarySearch for faster searches on sorted lists\n     */\n    public int lastIndexOf(int offset, int value) {\n        for (int i = offset; i-- > 0; ) {\n            if (_data[i] == value) {\n                return i;\n            }\n        }\n        return -1;\n    }\n\n    /**\n     * Searches the list for <tt>value</tt>\n     *\n     * @param value an <code>int</code> value\n     * @return true if value is in the list.\n     */\n    public boolean contains(int value) {\n        return lastIndexOf(value) >= 0;\n    }\n\n    /**\n     * Searches the list for values satisfying <tt>condition</tt> in\n     * the manner of the *nix <tt>grep</tt> utility.\n     *\n     * @param condition a condition to apply to each element in the list\n     * @return a list of values which match the condition.\n     */\n    public TIntArrayList grep(TIntProcedure condition) {\n        TIntArrayList list = new TIntArrayList();\n        for (int i = 0; i < _pos; i++) {\n            if (condition.execute(_data[i])) {\n                list.add(_data[i]);\n            }\n        }\n        return list;\n    }\n\n    /**\n     * Searches the list for values which do <b>not</b> satisfy\n     * <tt>condition</tt>.  This is akin to *nix <code>grep -v</code>.\n     *\n     * @param condition a condition to apply to each element in the list\n     * @return a list of values which do not match the condition.\n     */\n    public TIntArrayList inverseGrep(TIntProcedure condition) {\n        TIntArrayList list = new TIntArrayList();\n        for (int i = 0; i < _pos; i++) {\n            if (!condition.execute(_data[i])) {\n                list.add(_data[i]);\n            }\n        }\n        return list;\n    }\n\n    /**\n     * Finds the maximum value in the list.\n     *\n     * @return the largest value in the list.\n     * @throws IllegalStateException if the list is empty\n     */\n    public int max() {\n        if (size() == 0) {\n            throw new IllegalStateException(\"cannot find maximum of an empty list\");\n        }\n        int max = _data[_pos - 1];\n        for (int i = _pos - 1; i-- > 0; ) {\n            max = (int) Math.max(max, _data[_pos]);\n        }\n        return max;\n    }\n\n    /**\n     * Finds the minimum value in the list.\n     *\n     * @return the smallest value in the list.\n     * @throws IllegalStateException if the list is empty\n     */\n    public int min() {\n        if (size() == 0) {\n            throw new IllegalStateException(\"cannot find minimum of an empty list\");\n        }\n        int min = _data[_pos - 1];\n        for (int i = _pos - 1; i-- > 0; ) {\n            min = (int) Math.min(min, _data[_pos]);\n        }\n        return min;\n    }\n\n    // stringification\n\n    /**\n     * Returns a String representation of the list, front to back.\n     *\n     * @return a <code>String</code> value\n     */\n    @Override\n    public String toString() {\n        final StringBuffer buf = new StringBuffer(\"{\");\n        forEach(val -> {\n            buf.append(val);\n            buf.append(\", \");\n            return true;\n        });\n        buf.append(\"}\");\n        return buf.toString();\n    }\n} // TIntArrayList"
  },
  {
    "path": "src/main/java/lsieun/trove/TIntFunction.java",
    "content": "package lsieun.trove;\n\n/**\n * Interface for functions that accept and return one int primitive.\n *\n * Created: Mon Nov  5 22:19:36 2001\n *\n * @author Eric D. Friedman\n */\n\npublic interface TIntFunction {\n    /**\n     * Execute this function with <tt>value</tt>\n     *\n     * @param value an <code>int</code> input\n     * @return an <code>int</code> result\n     */\n    int execute(int value);\n}// TIntFunction"
  },
  {
    "path": "src/main/java/lsieun/trove/TIntHash.java",
    "content": "package lsieun.trove;\n\npublic abstract class TIntHash extends TPrimitiveHash\n    implements TIntHashingStrategy {\n\n    /** the set of ints */\n    protected transient int[] _set;\n\n    /** strategy used to hash values in this collection */\n    protected final TIntHashingStrategy _hashingStrategy;\n\n    /**\n     * Creates a new <code>TIntHash</code> instance with the default\n     * capacity and load factor.\n     */\n    public TIntHash() {\n        _hashingStrategy = this;\n    }\n\n    /**\n     * Creates a new <code>TIntHash</code> instance whose capacity\n     * is the next highest prime above <tt>initialCapacity + 1</tt>\n     * unless that value is already prime.\n     *\n     * @param initialCapacity an <code>int</code> value\n     */\n    public TIntHash(int initialCapacity) {\n        super(initialCapacity);\n        _hashingStrategy = this;\n    }\n\n    /**\n     * Creates a new <code>TIntHash</code> instance with a prime\n     * value at or near the specified capacity and load factor.\n     *\n     * @param initialCapacity used to find a prime capacity for the table.\n     * @param loadFactor used to calculate the threshold over which\n     * rehashing takes place.\n     */\n    public TIntHash(int initialCapacity, float loadFactor) {\n        super(initialCapacity, loadFactor);\n        _hashingStrategy = this;\n    }\n\n    /**\n     * Creates a new <code>TIntHash</code> instance with the default\n     * capacity and load factor.\n     * @param strategy used to compute hash codes and to compare keys.\n     */\n    public TIntHash(TIntHashingStrategy strategy) {\n        _hashingStrategy = strategy;\n    }\n\n    /**\n     * Creates a new <code>TIntHash</code> instance whose capacity\n     * is the next highest prime above <tt>initialCapacity + 1</tt>\n     * unless that value is already prime.\n     *\n     * @param initialCapacity an <code>int</code> value\n     * @param strategy used to compute hash codes and to compare keys.\n     */\n    public TIntHash(int initialCapacity, TIntHashingStrategy strategy) {\n        super(initialCapacity);\n        _hashingStrategy = strategy;\n    }\n\n    /**\n     * Creates a new <code>TIntHash</code> instance with a prime\n     * value at or near the specified capacity and load factor.\n     *\n     * @param initialCapacity used to find a prime capacity for the table.\n     * @param loadFactor used to calculate the threshold over which\n     * rehashing takes place.\n     * @param strategy used to compute hash codes and to compare keys.\n     */\n    public TIntHash(int initialCapacity, float loadFactor, TIntHashingStrategy strategy) {\n        super(initialCapacity, loadFactor);\n        _hashingStrategy = strategy;\n    }\n\n    /**\n     * @return a deep clone of this collection\n     */\n    @Override\n    public Object clone() {\n        TIntHash h = (TIntHash)super.clone();\n        h._set = _set == null ? null : _set.clone();\n        return h;\n    }\n\n    /**\n     * initializes the hash table to a prime capacity which is at least\n     * <tt>initialCapacity + 1</tt>.\n     *\n     * @param initialCapacity an <code>int</code> value\n     * @return the actual capacity chosen\n     */\n    @Override\n    protected int setUp(int initialCapacity) {\n        int capacity = super.setUp(initialCapacity);\n        _set = initialCapacity == JUST_CREATED_CAPACITY ? null : new int[capacity];\n        return capacity;\n    }\n\n    /**\n     * Searches the set for <tt>val</tt>\n     *\n     * @param val an <code>int</code> value\n     * @return a <code>boolean</code> value\n     */\n    public boolean contains(int val) {\n        return index(val) >= 0;\n    }\n\n    /**\n     * Executes <tt>procedure</tt> for each element in the set.\n     *\n     * @param procedure a <code>TObjectProcedure</code> value\n     * @return false if the loop over the set terminated because\n     * the procedure returned false for some value.\n     */\n    public boolean forEach(TIntProcedure procedure) {\n        byte[] states = _states;\n        int[] set = _set;\n        if (states != null) {\n            for (int i = states.length; i-- > 0;) {\n                if (states[i] == FULL && ! procedure.execute(set[i])) {\n                    return false;\n                }\n            }\n        }\n        return true;\n    }\n\n    /**\n     * Releases the element currently stored at <tt>index</tt>.\n     *\n     * @param index an <code>int</code> value\n     */\n    @Override\n    protected void removeAt(int index) {\n        _set[index] = (int)0;\n        super.removeAt(index);\n    }\n\n    /**\n     * Locates the index of <tt>val</tt>.\n     *\n     * @param val an <code>int</code> value\n     * @return the index of <tt>val</tt> or -1 if it isn't in the set.\n     */\n    protected int index(int val) {\n        byte[] states = _states;\n        if (states == null) return -1;\n        int[] set = _set;\n        int length = states.length;\n        int hash = _hashingStrategy.computeHashCode(val) & 0x7fffffff;\n        int index = hash % length;\n\n        if (states[index] != FREE &&\n            (states[index] == REMOVED || set[index] != val)) {\n            // see Knuth, p. 529\n            int probe = 1 + (hash % (length - 2));\n\n            do {\n                index -= probe;\n                if (index < 0) {\n                    index += length;\n                }\n            } while (states[index] != FREE &&\n                     (states[index] == REMOVED || set[index] != val));\n        }\n\n        return states[index] == FREE ? -1 : index;\n    }\n\n    /**\n     * Locates the index at which <tt>val</tt> can be inserted.  if\n     * there is already a value equal()ing <tt>val</tt> in the set,\n     * returns that value as a negative integer.\n     *\n     * @param val an <code>int</code> value\n     * @return an <code>int</code> value\n     */\n    protected int insertionIndex(int val) {\n        if (_set == null) {\n            setUp((int) (DEFAULT_INITIAL_CAPACITY / DEFAULT_LOAD_FACTOR + 1));\n        }\n        byte[] states = _states;\n        int[] set = _set;\n        int length = states.length;\n        int hash = _hashingStrategy.computeHashCode(val) & 0x7fffffff;\n        int index = hash % length;\n\n        if (states[index] == FREE) {\n            return index;       // empty, all done\n        } else if (states[index] == FULL && set[index] == val) {\n            return -index -1;   // already stored\n        } else {                // already FULL or REMOVED, must probe\n            // compute the double hash\n            int probe = 1 + (hash % (length - 2));\n            // starting at the natural offset, probe until we find an\n            // offset that isn't full.\n            do {\n                index -= probe;\n                if (index < 0) {\n                    index += length;\n                }\n            } while (states[index] == FULL && set[index] != val);\n\n            // if the index we found was removed: continue probing until we\n            // locate a free location or an element which equal()s the\n            // one we have.\n            if (states[index] == REMOVED) {\n                int firstRemoved = index;\n                while (states[index] != FREE &&\n                       (states[index] == REMOVED || set[index] != val)) {\n                    index -= probe;\n                    if (index < 0) {\n                        index += length;\n                    }\n                }\n                return states[index] == FULL ? -index -1 : firstRemoved;\n            }\n            // if it's full, the key is already stored\n            return states[index] == FULL ? -index -1 : index;\n        }\n    }\n\n    /**\n     * Default implementation of TIntHashingStrategy:\n     * delegates hashing to HashFunctions.hash(int).\n     *\n     * @param val the value to hash\n     * @return the hash code.\n     */\n    public final int computeHashCode(int val) {\n        return HashFunctions.hash(val);\n    }\n} // TIntHash"
  },
  {
    "path": "src/main/java/lsieun/trove/TIntHashingStrategy.java",
    "content": "package lsieun.trove;\n\nimport java.io.Serializable;\n\n/**\n * Interface to support pluggable hashing strategies in maps and sets.\n * Implementors can use this interface to make the trove hashing\n * algorithms use an optimal strategy when computing hashcodes.\n *\n * Created: Sun Nov  4 08:56:06 2001\n *\n * @author Eric D. Friedman\n */\npublic interface TIntHashingStrategy extends Serializable {\n    /**\n     * Computes a hash code for the specified int.  Implementors\n     * can use the int's own value or a custom scheme designed to\n     * minimize collisions for a known set of input.\n     *\n     * @param val int for which the hashcode is to be computed\n     * @return the hashCode\n     */\n    int computeHashCode(int val);\n} // TIntHashingStrategy"
  },
  {
    "path": "src/main/java/lsieun/trove/TIntIntHashMap.java",
    "content": "package lsieun.trove;\n\npublic class TIntIntHashMap extends TIntHash {\n\n    /**\n     * the values of the map\n     */\n    protected transient int[] _values;\n\n    /**\n     * Creates a new <code>TIntIntHashMap</code> instance with the default\n     * capacity and load factor.\n     */\n    public TIntIntHashMap() {\n    }\n\n    /**\n     * Creates a new <code>TIntIntHashMap</code> instance with a prime\n     * capacity equal to or greater than <tt>initialCapacity</tt> and\n     * with the default load factor.\n     *\n     * @param initialCapacity an <code>int</code> value\n     */\n    public TIntIntHashMap(int initialCapacity) {\n        super(initialCapacity);\n    }\n\n    /**\n     * Creates a new <code>TIntIntHashMap</code> instance with a prime\n     * capacity equal to or greater than <tt>initialCapacity</tt> and\n     * with the specified load factor.\n     *\n     * @param initialCapacity an <code>int</code> value\n     * @param loadFactor      a <code>float</code> value\n     */\n    public TIntIntHashMap(int initialCapacity, float loadFactor) {\n        super(initialCapacity, loadFactor);\n    }\n\n    /**\n     * Creates a new <code>TIntIntHashMap</code> instance with the default\n     * capacity and load factor.\n     *\n     * @param strategy used to compute hash codes and to compare keys.\n     */\n    public TIntIntHashMap(TIntHashingStrategy strategy) {\n        super(strategy);\n    }\n\n    /**\n     * Creates a new <code>TIntIntHashMap</code> instance whose capacity\n     * is the next highest prime above <tt>initialCapacity + 1</tt>\n     * unless that value is already prime.\n     *\n     * @param initialCapacity an <code>int</code> value\n     * @param strategy        used to compute hash codes and to compare keys.\n     */\n    public TIntIntHashMap(int initialCapacity, TIntHashingStrategy strategy) {\n        super(initialCapacity, strategy);\n    }\n\n    /**\n     * Creates a new <code>TIntIntHashMap</code> instance with a prime\n     * value at or near the specified capacity and load factor.\n     *\n     * @param initialCapacity used to find a prime capacity for the table.\n     * @param loadFactor      used to calculate the threshold over which\n     *                        rehashing takes place.\n     * @param strategy        used to compute hash codes and to compare keys.\n     */\n    public TIntIntHashMap(int initialCapacity, float loadFactor, TIntHashingStrategy strategy) {\n        super(initialCapacity, loadFactor, strategy);\n    }\n\n    /**\n     * @return a deep clone of this collection\n     */\n    @Override\n    public Object clone() {\n        TIntIntHashMap m = (TIntIntHashMap) super.clone();\n        m._values = _values == null ? null : _values.clone();\n        return m;\n    }\n\n    /**\n     * @return a TIntIntIterator with access to this map's keys and values\n     */\n    public TIntIntIterator iterator() {\n        return new TIntIntIterator(this);\n    }\n\n    /**\n     * initializes the hashtable to a prime capacity which is at least\n     * <tt>initialCapacity + 1</tt>.\n     *\n     * @param initialCapacity an <code>int</code> value\n     * @return the actual capacity chosen\n     */\n    @Override\n    protected int setUp(int initialCapacity) {\n        int capacity = super.setUp(initialCapacity);\n        _values = initialCapacity == JUST_CREATED_CAPACITY ? null : new int[capacity];\n        return capacity;\n    }\n\n    /**\n     * Inserts a key/value pair into the map.\n     *\n     * @param key   an <code>int</code> value\n     * @param value an <code>int</code> value\n     * @return the previous value associated with <tt>key</tt>,\n     * or null if none was found.\n     */\n    public int put(int key, int value) {\n        int previous = 0;\n        int index = insertionIndex(key);\n        boolean isNewMapping = true;\n        if (index < 0) {\n            index = -index - 1;\n            previous = _values[index];\n            isNewMapping = false;\n        }\n        byte previousState = _states[index];\n        _set[index] = key;\n        _states[index] = FULL;\n        _values[index] = value;\n        if (isNewMapping) {\n            postInsertHook(previousState == FREE);\n        }\n\n        return previous;\n    }\n\n    /**\n     * rehashes the map to the new capacity.\n     *\n     * @param newCapacity an <code>int</code> value\n     */\n    @Override\n    protected void rehash(int newCapacity) {\n        int oldCapacity = capacity();\n        int[] oldKeys = _set;\n        int[] oldVals = _values;\n        byte[] oldStates = _states;\n\n        _set = new int[newCapacity];\n        _values = new int[newCapacity];\n        _states = new byte[newCapacity];\n\n        for (int i = oldCapacity; i-- > 0; ) {\n            if (oldStates[i] == FULL) {\n                int o = oldKeys[i];\n                int index = insertionIndex(o);\n                _set[index] = o;\n                _values[index] = oldVals[i];\n                _states[index] = FULL;\n            }\n        }\n    }\n\n    /**\n     * retrieves the value for <tt>key</tt>\n     *\n     * @param key an <code>int</code> value\n     * @return the value of <tt>key</tt> or null if no such mapping exists.\n     */\n    public int get(int key) {\n        int index = index(key);\n        return index < 0 ? 0 : _values[index];\n    }\n\n    /**\n     * Empties the map.\n     */\n    @Override\n    public void clear() {\n        super.clear();\n        int[] keys = _set;\n        int[] vals = _values;\n        if (vals == null) return;\n        byte[] states = _states;\n\n        for (int i = keys.length; i-- > 0; ) {\n            keys[i] = 0;\n            vals[i] = 0;\n            states[i] = FREE;\n        }\n    }\n\n    /**\n     * Deletes a key/value pair from the map.\n     *\n     * @param key an <code>int</code> value\n     * @return an <code>int</code> value\n     */\n    public int remove(int key) {\n        int prev = 0;\n        int index = index(key);\n        if (index >= 0) {\n            prev = _values[index];\n            removeAt(index);    // clear key,state; adjust size\n        }\n        return prev;\n    }\n\n    /**\n     * Compares this map with another map for equality of their stored\n     * entries.\n     *\n     * @param other an <code>Object</code> value\n     * @return a <code>boolean</code> value\n     */\n    @Override\n    public boolean equals(Object other) {\n        if (!(other instanceof TIntIntHashMap)) {\n            return false;\n        }\n        TIntIntHashMap that = (TIntIntHashMap) other;\n        if (that.size() != size()) {\n            return false;\n        }\n        return forEachEntry(new EqProcedure(that));\n    }\n\n    @Override\n    public int hashCode() {\n        HashProcedure p = new HashProcedure();\n        forEachEntry(p);\n        return p.getHashCode();\n    }\n\n    private final class HashProcedure implements TIntIntProcedure {\n        private int h;\n\n        HashProcedure() {\n        }\n\n        public int getHashCode() {\n            return h;\n        }\n\n        public final boolean execute(int key, int value) {\n            h += _hashingStrategy.computeHashCode(key) ^ HashFunctions.hash(value);\n            return true;\n        }\n    }\n\n    private static final class EqProcedure implements TIntIntProcedure {\n        private final TIntIntHashMap _otherMap;\n\n        EqProcedure(TIntIntHashMap otherMap) {\n            _otherMap = otherMap;\n        }\n\n        public final boolean execute(int key, int value) {\n            int index = _otherMap.index(key);\n            return index >= 0 && eq(value, _otherMap.get(key));\n        }\n\n        /**\n         * Compare two ints for equality.\n         */\n        private static boolean eq(int v1, int v2) {\n            return v1 == v2;\n        }\n\n    }\n\n    /**\n     * removes the mapping at <tt>index</tt> from the map.\n     *\n     * @param index an <code>int</code> value\n     */\n    @Override\n    protected void removeAt(int index) {\n        _values[index] = 0;\n        super.removeAt(index);  // clear key, state; adjust size\n    }\n\n    /**\n     * Returns the values of the map.\n     *\n     * @return a <code>Collection</code> value\n     */\n    public int[] getValues() {\n        int[] vals = new int[size()];\n        int[] v = _values;\n        byte[] states = _states;\n\n        if (states != null) {\n            for (int i = states.length, j = 0; i-- > 0; ) {\n                if (states[i] == FULL) {\n                    vals[j++] = v[i];\n                }\n            }\n        }\n        return vals;\n    }\n\n    /**\n     * returns the keys of the map.\n     *\n     * @return a <code>Set</code> value\n     */\n    public int[] keys() {\n        int[] keys = new int[size()];\n        int[] k = _set;\n        byte[] states = _states;\n\n        if (states != null) {\n            for (int i = states.length, j = 0; i-- > 0; ) {\n                if (states[i] == FULL) {\n                    keys[j++] = k[i];\n                }\n            }\n        }\n        return keys;\n    }\n\n    /**\n     * checks for the presence of <tt>val</tt> in the values of the map.\n     *\n     * @param val an <code>int</code> value\n     * @return a <code>boolean</code> value\n     */\n    public boolean containsValue(int val) {\n        byte[] states = _states;\n        int[] vals = _values;\n        if (states != null) {\n            for (int i = states.length; i-- > 0; ) {\n                if (states[i] == FULL && val == vals[i]) {\n                    return true;\n                }\n            }\n        }\n        return false;\n    }\n\n\n    /**\n     * checks for the present of <tt>key</tt> in the keys of the map.\n     *\n     * @param key an <code>int</code> value\n     * @return a <code>boolean</code> value\n     */\n    public boolean containsKey(int key) {\n        return contains(key);\n    }\n\n    /**\n     * Executes <tt>procedure</tt> for each key in the map.\n     *\n     * @param procedure a <code>TIntProcedure</code> value\n     * @return false if the loop over the keys terminated because\n     * the procedure returned false for some key.\n     */\n    public boolean forEachKey(TIntProcedure procedure) {\n        return forEach(procedure);\n    }\n\n    /**\n     * Executes <tt>procedure</tt> for each value in the map.\n     *\n     * @param procedure a <code>TIntProcedure</code> value\n     * @return false if the loop over the values terminated because\n     * the procedure returned false for some value.\n     */\n    public boolean forEachValue(TIntProcedure procedure) {\n        byte[] states = _states;\n        int[] values = _values;\n        if (states != null) {\n            for (int i = states.length; i-- > 0; ) {\n                if (states[i] == FULL && !procedure.execute(values[i])) {\n                    return false;\n                }\n            }\n        }\n        return true;\n    }\n\n    /**\n     * Executes <tt>procedure</tt> for each key/value entry in the\n     * map.\n     *\n     * @param procedure a <code>TIntIntProcedure</code> value\n     * @return false if the loop over the entries terminated because\n     * the procedure returned false for some entry.\n     */\n    public boolean forEachEntry(TIntIntProcedure procedure) {\n        byte[] states = _states;\n        int[] keys = _set;\n        int[] values = _values;\n        if (states != null) {\n            for (int i = states.length; i-- > 0; ) {\n                if (states[i] == FULL && !procedure.execute(keys[i], values[i])) {\n                    return false;\n                }\n            }\n        }\n        return true;\n    }\n\n    /**\n     * Retains only those entries in the map for which the procedure\n     * returns a true value.\n     *\n     * @param procedure determines which entries to keep\n     * @return true if the map was modified.\n     */\n    public boolean retainEntries(TIntIntProcedure procedure) {\n        boolean modified = false;\n        byte[] states = _states;\n        int[] keys = _set;\n        int[] values = _values;\n        if (states != null) {\n            for (int i = states.length; i-- > 0; ) {\n                if (states[i] == FULL && !procedure.execute(keys[i], values[i])) {\n                    removeAt(i);\n                    modified = true;\n                }\n            }\n        }\n        return modified;\n    }\n\n    /**\n     * Transform the values in this map using <tt>function</tt>.\n     *\n     * @param function a <code>TIntFunction</code> value\n     */\n    public void transformValues(TIntFunction function) {\n        byte[] states = _states;\n        int[] values = _values;\n        if (states != null) {\n            for (int i = states.length; i-- > 0; ) {\n                if (states[i] == FULL) {\n                    values[i] = function.execute(values[i]);\n                }\n            }\n        }\n    }\n\n    /**\n     * Increments the primitive value mapped to key by 1\n     *\n     * @param key the key of the value to increment\n     * @return true if a mapping was found and modified.\n     */\n    public boolean increment(int key) {\n        return adjustValue(key, (int) 1);\n    }\n\n    /**\n     * Adjusts the primitive value mapped to key.\n     *\n     * @param key    the key of the value to increment\n     * @param amount the amount to adjust the value by.\n     * @return true if a mapping was found and modified.\n     */\n    public boolean adjustValue(int key, int amount) {\n        int index = index(key);\n        if (index < 0) {\n            return false;\n        }\n        else {\n            _values[index] += amount;\n            return true;\n        }\n    }\n\n    @Override\n    public String toString() {\n        final StringBuilder sb = new StringBuilder();\n        forEachEntry(new TIntIntProcedure() {\n            public boolean execute(int key, int value) {\n                if (sb.length() != 0) {\n                    sb.append(',').append(' ');\n                }\n                sb.append(key);\n                sb.append('=');\n                sb.append(value);\n                return true;\n            }\n        });\n        sb.append('}');\n        sb.insert(0, '{');\n        return sb.toString();\n    }\n\n} // TIntIntHashMap"
  },
  {
    "path": "src/main/java/lsieun/trove/TIntIntIterator.java",
    "content": "package lsieun.trove;\n\npublic class TIntIntIterator extends TPrimitiveIterator {\n    /**\n     * the collection being iterated over\n     */\n    private final TIntIntHashMap _map;\n\n    /**\n     * Creates an iterator over the specified map\n     */\n    public TIntIntIterator(TIntIntHashMap map) {\n        super(map);\n        _map = map;\n    }\n\n    /**\n     * Moves the iterator forward to the next entry in the underlying map.\n     *\n     * @throws java.util.NoSuchElementException\n     *          if the iterator is already exhausted\n     */\n    public void advance() {\n        moveToNextIndex();\n    }\n\n    /**\n     * Provides access to the key of the mapping at the iterator's position.\n     * Note that you must <tt>advance()</tt> the iterator at least once\n     * before invoking this method.\n     *\n     * @return the key of the entry at the iterator's current position.\n     */\n    public int key() {\n        return _map._set[_index];\n    }\n\n    /**\n     * Provides access to the value of the mapping at the iterator's position.\n     * Note that you must <tt>advance()</tt> the iterator at least once\n     * before invoking this method.\n     *\n     * @return the value of the entry at the iterator's current position.\n     */\n    public int value() {\n        return _map._values[_index];\n    }\n\n    /**\n     * Replace the value of the mapping at the iterator's position with the\n     * specified value. Note that you must <tt>advance()</tt> the iterator at\n     * least once before invoking this method.\n     *\n     * @param val the value to set in the current entry\n     * @return the old value of the entry.\n     */\n    public int setValue(int val) {\n        int old = value();\n        _map._values[_index] = val;\n        return old;\n    }\n}// TIntIntIterator"
  },
  {
    "path": "src/main/java/lsieun/trove/TIntIntProcedure.java",
    "content": "package lsieun.trove;\n\npublic interface TIntIntProcedure {\n\n    /**\n     * Executes this procedure. A false return value indicates that\n     * the application executing this procedure should not invoke this\n     * procedure again.\n     *\n     * @param a an <code>int</code> value\n     * @param b an <code>int</code> value\n     * @return true if additional invocations of the procedure are\n     * allowed.\n     */\n    boolean execute(int a, int b);\n}// TIntIntProcedure"
  },
  {
    "path": "src/main/java/lsieun/trove/TIntProcedure.java",
    "content": "package lsieun.trove;\n\npublic interface TIntProcedure {\n    /**\n     * Executes this procedure. A false return value indicates that\n     * the application executing this procedure should not invoke this\n     * procedure again.\n     *\n     * @param value a value of type <code>int</code>\n     * @return true if additional invocations of the procedure are\n     * allowed.\n     */\n    boolean execute(int value);\n}// TIntProcedure"
  },
  {
    "path": "src/main/java/lsieun/trove/TIterator.java",
    "content": "package lsieun.trove;\n\nimport java.util.ConcurrentModificationException;\nimport java.util.NoSuchElementException;\n\n/**\n * Abstract iterator class for THash implementations.  This class provides some\n * of the common iterator operations (hasNext(), remove()) and allows subclasses\n * to define the mechanism(s) for advancing the iterator and returning data.\n *\n * @author Eric D. Friedman\n * @version $Id: TIterator.java,v 1.6 2004/09/24 09:11:15 cdr Exp $\n */\nabstract class TIterator {\n    /** the data structure this iterator traverses */\n    protected final THash _hash;\n    /** the number of elements this iterator believes are in the\n     * data structure it accesses. */\n    protected int _expectedSize;\n    /** the index used for iteration. */\n    protected int _index;\n\n    /**\n     *  Create an instance of TIterator over the specified THash.\n     */\n    public TIterator(THash hash) {\n        _hash = hash;\n        _expectedSize = _hash.size();\n        _index = _hash.capacity();\n    }\n\n    /**\n     * Returns true if the iterator can be advanced past its current\n     * location.\n     *\n     * @return a <code>boolean</code> value\n     */\n    public boolean hasNext() {\n        return nextIndex() >= 0;\n    }\n\n    /**\n     * Removes the last entry returned by the iterator.\n     * Invoking this method more than once for a single entry\n     * will leave the underlying data structure in a confused\n     * state.\n     */\n    public void remove() {\n        if (_expectedSize != _hash.size()) {\n            throw new ConcurrentModificationException();\n        }\n\n        _hash.stopCompactingOnRemove();\n        try {\n            _hash.removeAt(_index);\n        } finally {\n            _hash.startCompactingOnRemove(false);\n        }\n\n        _expectedSize--;\n    }\n\n    /**\n     * Sets the internal <tt>index</tt> so that the `next' object\n     * can be returned.\n     */\n    protected final void moveToNextIndex() {\n        // doing the assignment && < 0 in one line shaves\n        // 3 opcodes...\n        if ((_index = nextIndex()) < 0) {\n            throw new NoSuchElementException();\n        }\n    }\n\n    /**\n     * Returns the index of the next value in the data structure\n     * or a negative value if the iterator is exhausted.\n     *\n     * @return an <code>int</code> value\n     */\n    protected abstract int nextIndex();\n} // TIterator"
  },
  {
    "path": "src/main/java/lsieun/trove/TPrimitiveHash.java",
    "content": "package lsieun.trove;\n\npublic abstract class TPrimitiveHash extends THash {\n    /** flags indicating whether each position in the hash is\n     * FREE, FULL, or REMOVED */\n    protected transient byte[] _states;\n\n    /* constants used for state flags */\n\n    /** flag indicating that a slot in the hashtable is available */\n    protected static final byte FREE = 0;\n\n    /** flag indicating that a slot in the hashtable is occupied */\n    protected static final byte FULL = 1;\n\n    /** flag indicating that the value of a slot in the hashtable \n     * was deleted */\n    protected static final byte REMOVED = 2;\n\n    /**\n     * Creates a new <code>THash</code> instance with the default\n     * capacity and load factor.\n     */\n    public TPrimitiveHash() {\n        super();\n    }\n\n    /**\n     * Creates a new <code>TPrimitiveHash</code> instance with a prime\n     * capacity at or near the specified capacity and with the default\n     * load factor.\n     *\n     * @param initialCapacity an <code>int</code> value\n     */\n    public TPrimitiveHash(int initialCapacity) {\n        this(initialCapacity, DEFAULT_LOAD_FACTOR);\n    }\n\n    /**\n     * Creates a new <code>TPrimitiveHash</code> instance with a prime\n     * capacity at or near the minimum needed to hold\n     * <tt>initialCapacity<tt> elements with load factor\n     * <tt>loadFactor</tt> without triggering a rehash.\n     *\n     * @param initialCapacity an <code>int</code> value\n     * @param loadFactor a <code>float</code> value\n     */\n    public TPrimitiveHash(int initialCapacity, float loadFactor) {\n        super(initialCapacity, loadFactor);\n    }\n\n    @Override\n    public Object clone() {\n        TPrimitiveHash h = (TPrimitiveHash)super.clone();\n        h._states = _states == null ? null : _states.clone();\n        return h;\n    }\n\n    /**\n     * Returns the capacity of the hash table.  This is the true\n     * physical capacity, without adjusting for the load factor.\n     *\n     * @return the physical capacity of the hash table.\n     */\n    @Override\n    protected int capacity() {\n        return _states == null ? 0 : _states.length;\n    }\n\n    /**\n     * Delete the record at <tt>index</tt>.\n     *\n     * @param index an <code>int</code> value\n     */\n    @Override\n    protected void removeAt(int index) {\n        _states[index] = REMOVED;\n        super.removeAt(index);\n    }\n\n    /**\n     * initializes the hashtable to a prime capacity which is at least\n     * <tt>initialCapacity + 1</tt>.  \n     *\n     * @param initialCapacity an <code>int</code> value\n     * @return the actual capacity chosen\n     */\n    @Override\n    protected int setUp(int initialCapacity) {\n        int capacity = super.setUp(initialCapacity);\n        _states = initialCapacity == JUST_CREATED_CAPACITY ? null : new byte[capacity];\n        return capacity;\n    }\n} // TPrimitiveHash"
  },
  {
    "path": "src/main/java/lsieun/trove/TPrimitiveIterator.java",
    "content": "package lsieun.trove;\n\nimport java.util.ConcurrentModificationException;\n\n/**\n * Implements all iterator functions for the hashed object set.\n * Subclasses may override objectAtIndex to vary the object\n * returned by calls to next() (e.g. for values, and Map.Entry\n * objects).\n *\n * <p> Note that iteration is fastest if you forego the calls to\n * <tt>hasNext</tt> in favor of checking the size of the structure\n * yourself and then call next() that many times:\n *\n * <pre>\n * Iterator i = collection.iterator();\n * for (int size = collection.size(); size-- > 0;) {\n *   Object o = i.next();\n * }\n * </pre>\n *\n * <p>You may, of course, use the hasNext(), next() idiom too if\n * you aren't in a performance critical spot.</p>\n *\n */\nabstract class TPrimitiveIterator extends TIterator {\n    /** the collection on which this iterator operates. */\n    protected final TPrimitiveHash _hash;\n\n    /**\n     * Creates a TPrimitiveIterator for the specified collection.\n     */\n    public TPrimitiveIterator(TPrimitiveHash hash) {\n\tsuper(hash);\n        _hash = hash;\n    }\n    \n    /**\n     * Returns the index of the next value in the data structure\n     * or a negative value if the iterator is exhausted.\n     *\n     * @return an <code>int</code> value\n     * @exception ConcurrentModificationException if the underlying collection's\n     * size has been modified since the iterator was created.\n     */\n    @Override\n    protected final int nextIndex() {\n        if (_expectedSize != _hash.size()) {\n            throw new ConcurrentModificationException();\n        }\n\n        byte[] states = _hash._states;\n        int i = _index;\n        while (i-- > 0 && (states[i] != TPrimitiveHash.FULL)) ;\n        return i;\n    }\n\n} // TPrimitiveIterator"
  },
  {
    "path": "src/main/java/lsieun/trove/package-info.java",
    "content": "/**\n * 这个包下的代码是从trove4j复制过来的.\n * <p>\n * 当前项目的主要关注点就是Java ASM，因此在pom.xml文件中我想只引用ASM相关的jar包，尽量避免引用别的jar包。\n * </p>\n * <p>\n *     这里的代码原本来自于这里：\n * </p>\n * <ul>\n *     <li>groupId: org.jetbrains.intellij.deps</li>\n *     <li>artifactId: trove4j</li>\n *     <li>version: 1.0.20200330</li>\n * </ul>\n */\npackage lsieun.trove;"
  },
  {
    "path": "src/main/java/lsieun/utils/ASMUtilsCore.java",
    "content": "package lsieun.utils;\n\nimport org.objectweb.asm.*;\n\npublic class ASMUtilsCore {\n    public int read(ClassReader cr, int off, char[] buf,\n                    int codeOff, Label[] labels) {\n        int methodOff = getMethodOffset(cr, codeOff, buf);\n        int acc = cr.readUnsignedShort(methodOff); // method access\n        String name = cr.readUTF8(methodOff + 2, buf); // method name\n        String desc = cr.readUTF8(methodOff + 4, buf); // method desc\n        return -1;\n    }\n\n    public static int getMethodOffset(ClassReader cr, int codeOff, char[] buf) {\n        int off = cr.header + 6;\n        int interfacesCount = cr.readUnsignedShort(off);\n        off += 2 + interfacesCount * 2;\n\n        int fieldsCount = cr.readUnsignedShort(off);\n        off += 2;\n        for (; fieldsCount > 0; --fieldsCount) {\n            int attrCount = cr.readUnsignedShort(off + 6);\n            off += 8;  // fields\n            for (; attrCount > 0; --attrCount) {\n                off += 6 + cr.readInt(off + 2);\n            }\n        }\n\n        int methodsCount = cr.readUnsignedShort(off);\n        off += 2;\n        for (; methodsCount > 0; --methodsCount) {\n            int methodOff = off;\n            int attrCount = cr.readUnsignedShort(off + 6);\n            off += 8;  // methods\n            for (; attrCount > 0; --attrCount) {\n                String attrName = cr.readUTF8(off, buf);\n                off += 6;\n                if (attrName.equals(\"Code\")) {\n                    if (codeOff == off) {\n                        return methodOff;\n                    }\n                }\n                off += cr.readInt(off - 4);\n            }\n        }\n        return -1;\n    }\n\n    // org.objectweb.asm.commons.GeneratorAdapter.swap(org.objectweb.asm.Type, org.objectweb.asm.Type)\n    public static void swap(MethodVisitor mv, Type stackTop, Type belowTop) {\n        if (stackTop.getSize() == 1) {\n            if (belowTop.getSize() == 1) {\n                // Top = 1, below = 1\n                mv.visitInsn(Opcodes.SWAP);\n            }\n            else {\n                // Top = 1, below = 2\n                mv.visitInsn(Opcodes.DUP_X2);\n                mv.visitInsn(Opcodes.POP);\n            }\n        }\n        else {\n            if (belowTop.getSize() == 1) {\n                // Top = 2, below = 1\n                mv.visitInsn(Opcodes.DUP2_X1);\n            }\n            else {\n                // Top = 2, below = 2\n                mv.visitInsn(Opcodes.DUP2_X2);\n            }\n            mv.visitInsn(Opcodes.POP2);\n        }\n    }\n}\n"
  },
  {
    "path": "src/main/java/lsieun/utils/ASMUtilsTree.java",
    "content": "package lsieun.utils;\n\nimport org.objectweb.asm.tree.*;\nimport org.objectweb.asm.tree.analysis.*;\n\npublic class ASMUtilsTree {\n    private static final int NOT_EXIST = -1;\n\n    public static String getVariableName(String className, MethodNode mn, int line, String methodName, int param) throws AnalyzerException {\n        Analyzer<SourceValue> a = new Analyzer<>(new SourceInterpreter());\n        Frame<SourceValue>[] frames = a.analyze(className, mn);\n        InsnList instructions = mn.instructions;\n        LabelNode label = findLineLabel(instructions, line);\n        int methodInsnIndex = findMethodCall(instructions, label, methodName);\n        SourceValue stack = frames[methodInsnIndex].getStack(param);\n        AbstractInsnNode insn = stack.insns.iterator().next();\n        if (insn instanceof VarInsnNode) {\n            VarInsnNode varInsnNode = (VarInsnNode) insn;\n            int var = varInsnNode.var;\n            LocalVariableNode localVariableNode = mn.localVariables.get(var);\n            return localVariableNode.name;\n        }\n        return null;\n    }\n\n    public static LabelNode findLineLabel(InsnList instructions, int line) {\n        for (AbstractInsnNode node : instructions) {\n            if (node instanceof LineNumberNode) {\n                LineNumberNode lineNumberNode = (LineNumberNode) node;\n                if (lineNumberNode.line == line) {\n                    return lineNumberNode.start;\n                }\n            }\n        }\n        return null;\n    }\n\n    public static int findMethodCall(InsnList instructions, LabelNode label, String name) {\n        if (!instructions.contains(label)) {\n            return NOT_EXIST;\n        }\n        int index = instructions.indexOf(label);\n        index++;\n        int size = instructions.size();\n        while (index < size) {\n            AbstractInsnNode node = instructions.get(index);\n            if (node instanceof MethodInsnNode) {\n                MethodInsnNode methodInsnNode = (MethodInsnNode) node;\n                if (methodInsnNode.name.equals(name)) {\n                    return index;\n                }\n            }\n            index++;\n        }\n\n        return NOT_EXIST;\n    }\n}\n"
  },
  {
    "path": "src/main/java/lsieun/utils/BoxDrawingUtils.java",
    "content": "package lsieun.utils;\n\nimport lsieun.asm.analysis.InsnText;\nimport lsieun.drawing.canvas.Box;\nimport lsieun.drawing.canvas.Canvas;\nimport lsieun.drawing.canvas.Drawable;\nimport lsieun.drawing.canvas.TextAlign;\nimport lsieun.drawing.theme.table.OneLineTable;\nimport org.objectweb.asm.tree.AbstractInsnNode;\nimport org.objectweb.asm.tree.InsnList;\n\nimport java.util.List;\n\npublic class BoxDrawingUtils {\n    public static final String EMPTY = \"\";\n\n    public static void printInstructionLinks(InsnList instructions, int[] array) {\n        if (array == null || array.length < 1) {\n            return;\n        }\n\n        InsnText insnText = new InsnText();\n        int n = 5;\n\n        Canvas canvas = new Canvas();\n        int currentRow = 0;\n\n        int size = instructions.size();\n\n        int length = array.length;\n        int min = array[0];\n        int max = array[length - 1];\n        for (int i = 0; i < size; i++) {\n            AbstractInsnNode node = instructions.get(i);\n\n            canvas.moveTo(currentRow, 0);\n            if (i < min || i > max) {\n                canvas.drawPixel(Box.SPACE);\n            }\n            else if ((min == max)) {\n                // NOTE: 如果min和max相等，那么 i == min == max。\n                //       由于第1个条件的判断，此时min < i < max，再加上min == max，所以i == min == max。\n                canvas.drawPixel(Box.VERTICAL_AND_RIGHT);\n                canvas.right(1);\n                canvas.drawHorizontalLine(n - 1);\n            }\n            else if (i == min) {\n                canvas.drawPixel(Box.DOWN_AND_RIGHT);\n                canvas.right(1);\n                canvas.drawHorizontalLine(n - 1);\n            }\n            else if (i == max) {\n                canvas.drawPixel(Box.UP_AND_RIGHT);\n                canvas.right(1);\n                canvas.drawHorizontalLine(n - 1);\n            }\n            else if (contains(array, i)) {\n                canvas.drawPixel(Box.VERTICAL_AND_RIGHT);\n                canvas.right(1);\n                canvas.drawHorizontalLine(n - 1);\n            }\n            else {\n                canvas.drawPixel(Box.VERTICAL);\n            }\n\n            List<String> lines = insnText.toLines(node);\n            String secondPart;\n            {\n                secondPart = lines.get(0);\n                String format1 = \"%03d: %s\";\n                String message = String.format(format1, i, secondPart);\n\n                canvas.moveTo(currentRow, 0);\n                canvas.right(5);\n                canvas.drawText(message);\n            }\n\n            if (lines.size() > 1) {\n                Box ch;\n                String format2 = \"%4s %s\";\n                if (i >= min && i <= max) {\n                    ch = Box.VERTICAL;\n                }\n                else {\n                    ch = Box.SPACE;\n                }\n\n                for (int j = 1; j < lines.size(); j++) {\n                    secondPart = lines.get(j);\n                    String message = String.format(format2, EMPTY, secondPart);\n                    currentRow++;\n                    canvas.moveTo(currentRow, 0);\n                    canvas.drawPixel(ch);\n                    canvas.right(5);\n                    canvas.drawText(message);\n                }\n            }\n\n            currentRow++;\n        }\n\n        System.out.println(canvas);\n    }\n\n    private static boolean contains(int[] array, int val) {\n        for (int item : array) {\n            if (item == val) {\n                return true;\n            }\n        }\n        return false;\n    }\n\n    public static void printTable(String[][] matrix) {\n        Drawable table = new OneLineTable(matrix, TextAlign.CENTER);\n        Canvas canvas = new Canvas();\n        canvas.draw(0, 0, table);\n        System.out.println(canvas);\n    }\n\n}\n"
  },
  {
    "path": "src/main/java/lsieun/utils/ByteArrayClassLoader.java",
    "content": "package lsieun.utils;\n\nimport org.objectweb.asm.ClassReader;\nimport org.objectweb.asm.Type;\n\npublic class ByteArrayClassLoader extends ClassLoader {\n    public final Class<?> defineClass(byte[] bytes) {\n        ClassReader cr = new ClassReader(bytes);\n        String internalName = cr.getClassName();\n        String className = Type.getObjectType(internalName).getClassName();\n        return defineClass(className, bytes);\n    }\n\n    public Class<?> defineClass(String name, byte[] bytes) {\n        return super.defineClass(name, bytes, 0, bytes.length);\n    }\n}\n"
  },
  {
    "path": "src/main/java/lsieun/utils/ByteUtils.java",
    "content": "package lsieun.utils;\n\nimport java.io.ByteArrayOutputStream;\nimport java.io.IOException;\nimport java.nio.ByteBuffer;\n\npublic class ByteUtils {\n    public static byte[] intToByteArray(int value) {\n        ByteBuffer buffer = ByteBuffer.allocate(Integer.BYTES);\n        buffer.putInt(value);\n        return buffer.array();\n    }\n\n    public static byte[] merge(byte[]... bytesArray) {\n        if (bytesArray == null || bytesArray.length < 1) return null;\n\n        ByteArrayOutputStream bao = new ByteArrayOutputStream();\n        for (byte[] bytes : bytesArray) {\n            if (bytes != null && bytes.length > 0) {\n                try {\n                    bao.write(bytes);\n                }\n                catch (IOException e) {\n                    e.printStackTrace();\n                }\n            }\n        }\n\n        return bao.toByteArray();\n    }\n\n    public static byte[] concatenate(byte[] bytes1, byte[] bytes2) {\n        int len1 = bytes1.length;\n        int len2 = bytes2.length;\n\n        byte[] result_bytes = new byte[len1 + len2];\n\n        System.arraycopy(bytes1, 0, result_bytes, 0, len1);\n        System.arraycopy(bytes2, 0, result_bytes, len1, len2);\n\n        return result_bytes;\n    }\n\n    public static byte[] concatenate(byte[] bytes1, byte[] bytes2, byte[] bytes3) {\n        int len1 = bytes1.length;\n        int len2 = bytes2.length;\n        int len3 = bytes3.length;\n\n        byte[] result_bytes = new byte[len1 + len2 + len3];\n\n        System.arraycopy(bytes1, 0, result_bytes, 0, len1);\n        System.arraycopy(bytes2, 0, result_bytes, len1, len2);\n        System.arraycopy(bytes3, 0, result_bytes, len1 + len2, len3);\n\n        return result_bytes;\n    }\n}\n"
  },
  {
    "path": "src/main/java/lsieun/utils/DescriptorUtils.java",
    "content": "package lsieun.utils;\n\npublic class DescriptorUtils {\n    public static String simplify(String descriptor) {\n        int squareIndex = descriptor.lastIndexOf(\"[\");\n        String prefix = descriptor.substring(0, squareIndex + 1);\n\n        String simpleName = descriptor.substring(squareIndex + 1);\n        if (simpleName.startsWith(\"L\") && simpleName.endsWith(\";\")) {\n            simpleName = simpleName.substring(1, simpleName.length() - 1);\n        }\n\n        int slashIndex = simpleName.lastIndexOf(\"/\");\n        simpleName = simpleName.substring(slashIndex + 1);\n\n        return prefix + simpleName;\n    }\n}\n"
  },
  {
    "path": "src/main/java/lsieun/utils/FileUtils.java",
    "content": "package lsieun.utils;\n\nimport lsieun.cst.Const;\n\nimport java.io.*;\nimport java.nio.charset.StandardCharsets;\nimport java.util.ArrayList;\nimport java.util.List;\n\npublic class FileUtils {\n    public static String getFilePath(String relativePath) {\n        String dir = FileUtils.class.getResource(\"/\").getPath();\n        return dir + relativePath;\n    }\n\n    public static String getFilePath(Class<?> clazz, String className) {\n        String path = clazz.getResource(\"/\").getPath();\n        return String.format(\"%s%s.class\", path, className.replace('.', File.separatorChar));\n    }\n\n    public static byte[] readBytes(String filepath) {\n        File file = new File(filepath);\n        if (!file.exists()) {\n            throw new IllegalArgumentException(\"File Not Exist: \" + filepath);\n        }\n\n        InputStream in = null;\n\n        try {\n            in = new FileInputStream(file);\n            in = new BufferedInputStream(in);\n\n            ByteArrayOutputStream bao = new ByteArrayOutputStream();\n            IOUtils.copy(in, bao);\n\n            return bao.toByteArray();\n        }\n        catch (IOException e) {\n            e.printStackTrace();\n        }\n        finally {\n            IOUtils.closeQuietly(in);\n        }\n\n        throw new RuntimeException(\"Can not read file: \" + filepath);\n    }\n\n    public static void writeBytes(String filepath, byte[] bytes) {\n        File file = new File(filepath);\n        File dirFile = file.getParentFile();\n        mkdirs(dirFile);\n\n        try (OutputStream out = new FileOutputStream(filepath);\n             BufferedOutputStream buff = new BufferedOutputStream(out)) {\n            buff.write(bytes);\n            buff.flush();\n        }\n        catch (IOException e) {\n            e.printStackTrace();\n        }\n\n        if (Const.DEBUG) System.out.println(\"file://\" + filepath);\n    }\n\n    public static List<String> readLines(String filepath) {\n        return readLines(filepath, \"UTF8\");\n    }\n\n    public static List<String> readLines(String filepath, String charsetName) {\n        File file = new File(filepath);\n        if (!file.exists()) {\n            throw new IllegalArgumentException(\"File Not Exist: \" + filepath);\n        }\n\n        InputStream in = null;\n        Reader reader = null;\n        BufferedReader bufferReader = null;\n\n        try {\n            in = new FileInputStream(file);\n            reader = new InputStreamReader(in, charsetName);\n            bufferReader = new BufferedReader(reader);\n\n            List<String> list = new ArrayList<>();\n            String line;\n            while ((line = bufferReader.readLine()) != null) {\n                list.add(line);\n            }\n            return list;\n        }\n        catch (IOException e) {\n            e.printStackTrace();\n        }\n        finally {\n            IOUtils.closeQuietly(bufferReader);\n            IOUtils.closeQuietly(reader);\n            IOUtils.closeQuietly(in);\n        }\n\n        assert !Const.DEBUG : \"bytes is null\";\n\n        return null;\n    }\n\n    public static void writeLines(String filepath, List<String> lines) {\n        if (lines == null || lines.size() < 1) return;\n\n        File file = new File(filepath);\n        File dirFile = file.getParentFile();\n        mkdirs(dirFile);\n\n        OutputStream out = null;\n        Writer writer = null;\n        BufferedWriter bufferedWriter = null;\n\n        try {\n            out = new FileOutputStream(file);\n            writer = new OutputStreamWriter(out, StandardCharsets.UTF_8);\n            bufferedWriter = new BufferedWriter(writer);\n\n            for (String line : lines) {\n                bufferedWriter.write(line);\n                bufferedWriter.newLine();\n            }\n        }\n        catch (IOException ex) {\n            ex.printStackTrace();\n        }\n        finally {\n            IOUtils.closeQuietly(bufferedWriter);\n            IOUtils.closeQuietly(writer);\n            IOUtils.closeQuietly(out);\n        }\n    }\n\n    public static void mkdirs(File dirFile) {\n        boolean file_exists = dirFile.exists();\n\n        if (file_exists && dirFile.isDirectory()) {\n            return;\n        }\n\n        if (file_exists && dirFile.isFile()) {\n            throw new RuntimeException(\"Not A Directory: \" + dirFile);\n        }\n\n        if (!file_exists) {\n            boolean flag = dirFile.mkdirs();\n            assert !Const.DEBUG || flag : \"Create Directory Failed: \" + dirFile.getAbsolutePath();\n        }\n    }\n\n    public static void clear(File file) {\n        if (!file.exists()) {\n            return;\n        }\n\n        if (file.isDirectory()) {\n            File[] files = file.listFiles();\n            if (files != null && files.length > 0) {\n                for (File f : files) {\n                    delete(f);\n                }\n            }\n        }\n        else {\n            delete(file);\n        }\n    }\n\n    public static void delete(File file) {\n        if (!file.exists()) {\n            return;\n        }\n\n        if (file.isFile()) {\n            boolean flag = file.delete();\n            assert !Const.DEBUG || flag : \"[Warning] delete file failed: \" + file.getAbsolutePath();\n        }\n\n        if (file.isDirectory()) {\n            File[] files = file.listFiles();\n            if (files != null && files.length > 0) {\n                for (File f : files) {\n                    delete(f);\n                }\n            }\n\n            boolean flag = file.delete();\n            assert !Const.DEBUG || flag : \"[Warning] delete file failed: \" + file.getAbsolutePath();\n        }\n    }\n\n    public static byte[] readStream(final InputStream in, final boolean close) {\n        if (in == null) {\n            throw new IllegalArgumentException(\"inputStream is null!!!\");\n        }\n\n        try {\n            ByteArrayOutputStream out = new ByteArrayOutputStream();\n            IOUtils.copy(in, out);\n            return out.toByteArray();\n        }\n        catch (IOException e) {\n            e.printStackTrace();\n        }\n        finally {\n            if (close) {\n                IOUtils.closeQuietly(in);\n            }\n        }\n        return null;\n    }\n\n    public static InputStream getInputStream(String className) {\n        return ClassLoader.getSystemResourceAsStream(className.replace('.', '/') + \".class\");\n    }\n}\n"
  },
  {
    "path": "src/main/java/lsieun/utils/FrameUtils.java",
    "content": "package lsieun.utils;\n\nimport lsieun.asm.analysis.InsnText;\nimport lsieun.cst.Const;\nimport lsieun.drawing.canvas.Canvas;\nimport lsieun.drawing.canvas.TextAlign;\nimport lsieun.drawing.theme.shape.Rectangle;\nimport lsieun.drawing.theme.table.FixedWidthOneLineTable;\nimport org.objectweb.asm.tree.AbstractInsnNode;\nimport org.objectweb.asm.tree.InsnList;\nimport org.objectweb.asm.tree.MethodNode;\nimport org.objectweb.asm.tree.analysis.*;\n\nimport java.util.ArrayList;\nimport java.util.List;\nimport java.util.function.Function;\n\npublic class FrameUtils {\n    private static final String START = \"{\";\n    private static final String STOP = \"}\";\n    private static final String EMPTY = \"{}\";\n    private static final String SEPARATOR = \"|\";\n\n    public static <V extends Value, T> void printFrames(String owner, MethodNode mn, Analyzer<V> analyzer, Function<V, T> func) throws AnalyzerException {\n        System.out.println(mn.name + \":\" + mn.desc);\n\n        //（1）获取Instruction信息\n        InsnList instructions = mn.instructions;\n        int size = instructions.size();\n        InsnText insnText = new InsnText();\n\n        //（2）获取Frame信息\n        Frame<V>[] frames = analyzer.analyze(owner, mn);\n\n        //（3）结合Instruction信息和Frame信息\n        // NOTE: 右对齐，使用“%36s”；左对齐，使用“%-36s”。\n        String format = \"%03d:    %-36s    %s\";\n        for (int index = 0; index < size; index++) {\n            AbstractInsnNode node = instructions.get(index);\n            List<String> nodeLines = insnText.toLines(node);\n\n            Frame<V> f = frames[index];\n            String frameLine = FrameUtils.getFrameLine(f, func);\n\n            String firstLine = String.format(format, index, nodeLines.get(0), frameLine);\n            System.out.println(firstLine);\n            for (int i = 1; i < nodeLines.size(); i++) {\n                String item = nodeLines.get(i);\n                String line = String.format(\"%4s    %-36s\", \"\", item);\n                System.out.println(line);\n            }\n        }\n\n        System.out.println(Const.DIVISION_LINE);\n        System.out.println();\n    }\n\n    public static <V extends Value, T> String getFrameLine(Frame<V> f, Function<V, T> func) {\n        if (f == null) {\n            return toLine(null, null);\n        }\n\n        List<Object> localList = new ArrayList<>();\n        for (int i = 0; i < f.getLocals(); ++i) {\n            V localValue = f.getLocal(i);\n            if (func == null) {\n                localList.add(localValue);\n            } else {\n                T item = func.apply(localValue);\n                localList.add(item);\n            }\n        }\n\n        List<Object> stackList = new ArrayList<>();\n        for (int j = 0; j < f.getStackSize(); ++j) {\n            V stackValue = f.getStack(j);\n            if (func == null) {\n                stackList.add(stackValue);\n            } else {\n                T item = func.apply(stackValue);\n                stackList.add(item);\n            }\n        }\n\n        return toLine(localList, stackList);\n    }\n\n    public static <T> String toLine(List<T> localList, List<T> stackList) {\n        String locals_str = toLine(localList);\n        String stack_str = toLine(stackList);\n        return String.format(\"%s %s %s\", locals_str, SEPARATOR, stack_str);\n    }\n\n    private static <T> String toLine(List<T> list) {\n        if (list == null || list.size() == 0) return EMPTY;\n        int size = list.size();\n\n        StringBuilder sb = new StringBuilder();\n        sb.append(START);\n        for (int i = 0; i < size - 1; i++) {\n            T item = list.get(i);\n            sb.append(item).append(\", \");\n        }\n        sb.append(list.get(size - 1));\n        sb.append(STOP);\n        return sb.toString();\n    }\n\n    public static <V extends Value, T> void printGraph(String owner, MethodNode mn, Analyzer<V> analyzer, Function<V, T> func) throws AnalyzerException {\n        System.out.println(mn.name + \":\" + mn.desc);\n\n        //（1）获取Instruction信息\n        int maxLocals = mn.maxLocals;\n        int maxStack = mn.maxStack;\n        InsnList instructions = mn.instructions;\n        int size = instructions.size();\n        InsnText insnText = new InsnText();\n\n        //（2）获取Frame信息\n        Frame<V>[] frames = analyzer.analyze(owner, mn);\n\n        //（3）结合Instruction信息和Frame信息\n        // NOTE: 右对齐，使用“%36s”；左对齐，使用“%-36s”。\n        String format = \"%03d:    %-36s    %s\";\n        for (int index = 0; index < size; index++) {\n            AbstractInsnNode node = instructions.get(index);\n            List<String> nodeLines = insnText.toLines(node);\n\n            Frame<V> f = frames[index];\n\n            printOneFrame(maxLocals, maxStack, f, func);\n\n            String frameLine = FrameUtils.getFrameLine(f, func);\n\n            String firstLine = String.format(format, index, nodeLines.get(0), frameLine);\n            System.out.println(firstLine);\n            for (int i = 1; i < nodeLines.size(); i++) {\n                String item = nodeLines.get(i);\n                String line = String.format(\"%4s    %-36s\", \"\", item);\n                System.out.println(line);\n            }\n        }\n\n        System.out.println(Const.DIVISION_LINE);\n        System.out.println();\n    }\n\n    public static <V extends Value, T> void printOneFrame(int maxLocals, int maxStack, Frame<V> f, Function<V, T> func) {\n        Canvas canvas = new Canvas();\n\n        // padding\n        int padding = 5;\n        String local_variable_label = \"local variable\";\n        String operand_stack_label = \"operand stack\";\n        int local_variable_col = 40;\n\n        // frame rectangle\n        int frame_width1 = local_variable_col + maxLocals * 7;\n        int frame_width2 = local_variable_col + local_variable_label.length() + padding;\n        int frame_width = Math.max(frame_width1, frame_width2);\n        int frame_height = maxStack * 2 + 4;\n        Rectangle rectangle = new Rectangle(frame_width, frame_height);\n        canvas.draw(0, 0, rectangle);\n\n        // operand stack label\n        canvas.moveTo(2, padding);\n        canvas.drawText(operand_stack_label);\n\n        // operand stack value\n        int stackSize = f.getStackSize();\n        String[][] matrix = new String[maxStack][1];\n        int index = matrix.length - 1;\n        for (int i = 0; i < stackSize; i++) {\n            V stackValue = f.getStack(i);\n            String str = func.apply(stackValue).toString();\n            matrix[index][0] = str;\n            int valueSize = stackValue.getSize();\n            if (valueSize == 2) {\n                matrix[index - 1][0] = \"top\";\n            }\n            index -= valueSize;\n        }\n        FixedWidthOneLineTable stack_table = new FixedWidthOneLineTable(matrix, TextAlign.CENTER, 25);\n        canvas.draw(3, 5, stack_table);\n\n        // local variable label\n        canvas.moveTo(maxStack * 2, local_variable_col);\n        canvas.drawText(local_variable_label);\n\n        // local variable block\n        String[][] local_variable_matrix = new String[1][maxLocals];\n        for (int i = 0; i < maxLocals; i++) {\n            local_variable_matrix[0][i] = \"\" + i;\n        }\n        FixedWidthOneLineTable local_table = new FixedWidthOneLineTable(local_variable_matrix, TextAlign.CENTER, 3);\n        canvas.draw(maxStack * 2 + 1, local_variable_col, local_table);\n\n        // local variable value\n        canvas.moveTo(1, frame_width + padding);\n        canvas.drawText(\"locals: \" + f.getLocals());\n        canvas.moveTo(2, frame_width + padding);\n        canvas.drawText(\"stacks: \" + stackSize);\n        int localIndex = 0;\n        for (int i = 0; i < maxLocals; i++) {\n            V localValue = f.getLocal(i);\n            String str = func.apply(localValue).toString();\n            String line = String.format(\"%d: %s\", i, str);\n            canvas.moveTo(4 + i, frame_width + padding);\n            canvas.drawText(line);\n        }\n\n        System.out.println(canvas);\n    }\n}\n"
  },
  {
    "path": "src/main/java/lsieun/utils/HexFormat.java",
    "content": "package lsieun.utils;\n\npublic enum HexFormat {\n    FORMAT_FF_FF(\"\", 0),\n    FORMAT_FF_FF_8(\"\", 8),\n    FORMAT_FF_FF_16(\"\", 16),\n    FORMAT_FF_FF_32(\"\", 32),\n\n    FORMAT_FF_SPACE_FF(\" \", 0),\n    FORMAT_FF_SPACE_FF_8(\" \", 8),\n    FORMAT_FF_SPACE_FF_16(\" \", 16),\n    FORMAT_FF_SPACE_FF_32(\" \", 32),\n\n    FORMAT_FF_COLON_FF(\":\", 0),\n    FORMAT_FF_COLON_FF_8(\":\", 8),\n    FORMAT_FF_COLON_FF_16(\":\", 16),\n    FORMAT_FF_COLON_FF_32(\":\", 32),\n    ;\n\n    public final String separator;\n    public final int columns;\n\n    HexFormat(String separator, int columns) {\n        this.separator = separator;\n        this.columns = columns;\n    }\n}\n"
  },
  {
    "path": "src/main/java/lsieun/utils/HexUtils.java",
    "content": "package lsieun.utils;\n\nimport java.util.Formatter;\n\npublic class HexUtils {\n\n    public static String format(byte[] bytes, HexFormat format) {\n        String separator = format.separator;\n        int bytes_column = format.columns;\n        return format(bytes, separator, bytes_column);\n    }\n\n    public static String format(byte[] bytes, String separator, int bytes_column) {\n        if (bytes == null || bytes.length < 1) return \"\";\n\n        StringBuilder sb = new StringBuilder();\n        Formatter fm = new Formatter(sb);\n\n        int length = bytes.length;\n        for (int i = 0; i < length - 1; i++) {\n            int val = bytes[i] & 0xFF;\n            fm.format(\"%02X\", val);\n            if (bytes_column > 0 && (i + 1) % bytes_column == 0) {\n                fm.format(\"%n\");\n            } else {\n                fm.format(\"%s\", separator);\n            }\n        }\n        {\n            int val = bytes[length - 1] & 0xFF;\n            fm.format(\"%02X\", val);\n        }\n\n        return sb.toString();\n    }\n\n    public static byte[] parse(String str, HexFormat format) {\n        char[] chars = format.separator.toCharArray();\n        return parse(str, chars);\n    }\n\n    public static byte[] parse(String str, char[] chars) {\n        int length = str.length();\n\n        StringBuilder sb = new StringBuilder();\n        for (int i = 0; i < length; i++) {\n            char ch = str.charAt(i);\n            if (is_in(ch, chars)) {\n                continue;\n            }\n            sb.append(ch);\n        }\n        String hex_str = sb.toString();\n        return parse(hex_str);\n    }\n\n    public static boolean is_in(char ch, char[] chars) {\n        for (char item : chars) {\n            if (item == ch) {\n                return true;\n            }\n        }\n        return false;\n    }\n\n    public static byte[] parse(String hex_str) {\n        int length = hex_str.length();\n        int count = length / 2;\n\n        byte[] bytes = new byte[count];\n        for (int i = 0; i < count; i++) {\n            String item = hex_str.substring(2 * i, 2 * i + 2);\n            int val = Integer.parseInt(item, 16);\n            bytes[i] = (byte) val;\n        }\n        return bytes;\n    }\n\n    public static String toHex(byte[] bytes) {\n        return format(bytes, HexFormat.FORMAT_FF_FF);\n    }\n}\n"
  },
  {
    "path": "src/main/java/lsieun/utils/IOUtils.java",
    "content": "package lsieun.utils;\n\nimport java.io.Closeable;\nimport java.io.IOException;\nimport java.io.InputStream;\nimport java.io.OutputStream;\nimport java.io.Reader;\nimport java.io.Writer;\n\npublic class IOUtils {\n    public static final int EOF = -1;\n\n    private static final int DEFAULT_BUFFER_SIZE = 1024 * 4;\n\n    public static int copy(final InputStream input, final OutputStream output) throws IOException {\n        final long count = copyLarge(input, output);\n        if (count > Integer.MAX_VALUE) {\n            return -1;\n        }\n        return (int) count;\n    }\n\n    public static long copyLarge(final InputStream input, final OutputStream output)\n            throws IOException {\n        return copy(input, output, DEFAULT_BUFFER_SIZE);\n    }\n\n    public static long copy(final InputStream input, final OutputStream output, final int bufferSize)\n            throws IOException {\n        return copyLarge(input, output, new byte[bufferSize]);\n    }\n\n    public static long copyLarge(final InputStream input, final OutputStream output, final byte[] buffer)\n            throws IOException {\n        long count = 0;\n        int n;\n        while (EOF != (n = input.read(buffer))) {\n            output.write(buffer, 0, n);\n            count += n;\n        }\n        return count;\n    }\n\n    public static long copyLarge(final Reader input, final Writer output) throws IOException {\n        return copyLarge(input, output, new char[DEFAULT_BUFFER_SIZE]);\n    }\n\n    public static long copyLarge(final Reader input, final Writer output, final char[] buffer) throws IOException {\n        long count = 0;\n        int n;\n        while (EOF != (n = input.read(buffer))) {\n            output.write(buffer, 0, n);\n            count += n;\n        }\n        return count;\n    }\n\n    public static void closeQuietly(final Closeable closeable) {\n        try {\n            if (closeable != null) {\n                closeable.close();\n            }\n        } catch (final IOException ioe) {\n            // ignore\n        }\n    }\n}\n"
  },
  {
    "path": "src/main/java/lsieun/utils/JarUtils.java",
    "content": "package lsieun.utils;\n\nimport java.io.BufferedInputStream;\nimport java.io.ByteArrayOutputStream;\nimport java.io.IOException;\nimport java.io.InputStream;\nimport java.util.*;\nimport java.util.jar.JarEntry;\nimport java.util.jar.JarFile;\n\npublic class JarUtils {\n    public static final int BUFFER_SIZE = 16 * 1024;\n\n    public static List<String> getAllEntries(String filePath) {\n        List<String> list = new ArrayList<>();\n        try {\n            JarFile jarFile = new JarFile(filePath);\n            Enumeration<JarEntry> entries = jarFile.entries();\n            while (entries.hasMoreElements()) {\n                JarEntry entry = entries.nextElement();\n                list.add(entry.getName());\n            }\n            jarFile.close();\n        } catch (IOException e) {\n            e.printStackTrace();\n        }\n        return list;\n    }\n\n    public static List<String> getClassEntries(String filepath) {\n        List<String> list = getAllEntries(filepath);\n        int size = list.size();\n        for (int i = size - 1; i >= 0; i--) {\n            String jarItem = list.get(i);\n            if (jarItem != null && jarItem.endsWith(\".class\")) {\n                continue;\n            }\n            list.remove(i);\n        }\n        return list;\n    }\n\n    public static byte[] readClass(String jarPath, String entryName) {\n        try (\n                JarFile jarFile = new JarFile(jarPath);\n                InputStream in = jarFile.getInputStream(jarFile.getJarEntry(entryName))\n        ) {\n            ByteArrayOutputStream bao = new ByteArrayOutputStream();\n            IOUtils.copy(in, bao);\n\n            return bao.toByteArray();\n        } catch (IOException e) {\n            e.printStackTrace();\n        }\n        return null;\n    }\n\n    public static Map<String, ByteArrayOutputStream> getAllEntryMap(String jarPath, List<String> entryList) {\n        if (jarPath == null || \"\".equals(jarPath)) return null;\n        if (entryList == null || entryList.size() < 1) return null;\n\n        Map<String, ByteArrayOutputStream> map = new HashMap<>();\n        JarFile jarFile = null;\n        try {\n            jarFile = new JarFile(jarPath);\n            for (String entryName : entryList) {\n                JarEntry entry = jarFile.getJarEntry(entryName);\n                InputStream in = jarFile.getInputStream(entry);\n\n                in = new BufferedInputStream(in);\n\n                ByteArrayOutputStream out = new ByteArrayOutputStream();\n\n                byte[] buf = new byte[BUFFER_SIZE];\n                int len;\n                while ((len = in.read(buf)) != -1) {\n                    out.write(buf, 0, len);\n                }\n\n                in.close();\n\n                map.put(entryName, out);\n            }\n        } catch (IOException e) {\n            e.printStackTrace();\n        } finally {\n            if (jarFile != null) {\n                try {\n                    jarFile.close();\n                } catch (IOException e) {\n                    e.printStackTrace();\n                }\n            }\n        }\n        return map;\n    }\n}\n"
  },
  {
    "path": "src/main/java/lsieun/utils/OpcodeConst.java",
    "content": "package lsieun.utils;\n\npublic class OpcodeConst {\n    /** Illegal opcode. */\n    public static final short  UNDEFINED      = -1;\n\n    /** Illegal opcode. */\n    public static final short  UNPREDICTABLE  = -2;\n\n    /** Illegal opcode. */\n    public static final short  RESERVED       = -3;\n\n    /** Mnemonic for an illegal opcode. */\n    public static final String ILLEGAL_OPCODE = \"<illegal opcode>\";\n\n    /**\n     * Names of opcodes.  Indexed by opcode.  OPCODE_NAMES[ALOAD] = \"aload\".\n     */\n    private static final String[] OPCODE_NAMES = {\n            // region opcode names\n            \"nop\", \"aconst_null\", \"iconst_m1\", \"iconst_0\", \"iconst_1\",\n            \"iconst_2\", \"iconst_3\", \"iconst_4\", \"iconst_5\", \"lconst_0\",\n            \"lconst_1\", \"fconst_0\", \"fconst_1\", \"fconst_2\", \"dconst_0\",\n            \"dconst_1\", \"bipush\", \"sipush\", \"ldc\", \"ldc_w\", \"ldc2_w\", \"iload\",\n            \"lload\", \"fload\", \"dload\", \"aload\", \"iload_0\", \"iload_1\", \"iload_2\",\n            \"iload_3\", \"lload_0\", \"lload_1\", \"lload_2\", \"lload_3\", \"fload_0\",\n            \"fload_1\", \"fload_2\", \"fload_3\", \"dload_0\", \"dload_1\", \"dload_2\",\n            \"dload_3\", \"aload_0\", \"aload_1\", \"aload_2\", \"aload_3\", \"iaload\",\n            \"laload\", \"faload\", \"daload\", \"aaload\", \"baload\", \"caload\", \"saload\",\n            \"istore\", \"lstore\", \"fstore\", \"dstore\", \"astore\", \"istore_0\",\n            \"istore_1\", \"istore_2\", \"istore_3\", \"lstore_0\", \"lstore_1\",\n            \"lstore_2\", \"lstore_3\", \"fstore_0\", \"fstore_1\", \"fstore_2\",\n            \"fstore_3\", \"dstore_0\", \"dstore_1\", \"dstore_2\", \"dstore_3\",\n            \"astore_0\", \"astore_1\", \"astore_2\", \"astore_3\", \"iastore\", \"lastore\",\n            \"fastore\", \"dastore\", \"aastore\", \"bastore\", \"castore\", \"sastore\",\n            \"pop\", \"pop2\", \"dup\", \"dup_x1\", \"dup_x2\", \"dup2\", \"dup2_x1\",\n            \"dup2_x2\", \"swap\", \"iadd\", \"ladd\", \"fadd\", \"dadd\", \"isub\", \"lsub\",\n            \"fsub\", \"dsub\", \"imul\", \"lmul\", \"fmul\", \"dmul\", \"idiv\", \"ldiv\",\n            \"fdiv\", \"ddiv\", \"irem\", \"lrem\", \"frem\", \"drem\", \"ineg\", \"lneg\",\n            \"fneg\", \"dneg\", \"ishl\", \"lshl\", \"ishr\", \"lshr\", \"iushr\", \"lushr\",\n            \"iand\", \"land\", \"ior\", \"lor\", \"ixor\", \"lxor\", \"iinc\", \"i2l\", \"i2f\",\n            \"i2d\", \"l2i\", \"l2f\", \"l2d\", \"f2i\", \"f2l\", \"f2d\", \"d2i\", \"d2l\", \"d2f\",\n            \"i2b\", \"i2c\", \"i2s\", \"lcmp\", \"fcmpl\", \"fcmpg\",\n            \"dcmpl\", \"dcmpg\", \"ifeq\", \"ifne\", \"iflt\", \"ifge\", \"ifgt\", \"ifle\",\n            \"if_icmpeq\", \"if_icmpne\", \"if_icmplt\", \"if_icmpge\", \"if_icmpgt\",\n            \"if_icmple\", \"if_acmpeq\", \"if_acmpne\", \"goto\", \"jsr\", \"ret\",\n            \"tableswitch\", \"lookupswitch\", \"ireturn\", \"lreturn\", \"freturn\",\n            \"dreturn\", \"areturn\", \"return\", \"getstatic\", \"putstatic\", \"getfield\",\n            \"putfield\", \"invokevirtual\", \"invokespecial\", \"invokestatic\",\n            \"invokeinterface\", \"invokedynamic\", \"new\", \"newarray\", \"anewarray\",\n            \"arraylength\", \"athrow\", \"checkcast\", \"instanceof\", \"monitorenter\",\n            \"monitorexit\", \"wide\", \"multianewarray\", \"ifnull\", \"ifnonnull\",\n            \"goto_w\", \"jsr_w\", \"breakpoint\", ILLEGAL_OPCODE, ILLEGAL_OPCODE,\n            ILLEGAL_OPCODE, ILLEGAL_OPCODE, ILLEGAL_OPCODE, ILLEGAL_OPCODE,\n            ILLEGAL_OPCODE, ILLEGAL_OPCODE, ILLEGAL_OPCODE, ILLEGAL_OPCODE,\n            ILLEGAL_OPCODE, ILLEGAL_OPCODE, ILLEGAL_OPCODE, ILLEGAL_OPCODE,\n            ILLEGAL_OPCODE, ILLEGAL_OPCODE, ILLEGAL_OPCODE, ILLEGAL_OPCODE,\n            ILLEGAL_OPCODE, ILLEGAL_OPCODE, ILLEGAL_OPCODE, ILLEGAL_OPCODE,\n            ILLEGAL_OPCODE, ILLEGAL_OPCODE, ILLEGAL_OPCODE, ILLEGAL_OPCODE,\n            ILLEGAL_OPCODE, ILLEGAL_OPCODE, ILLEGAL_OPCODE, ILLEGAL_OPCODE,\n            ILLEGAL_OPCODE, ILLEGAL_OPCODE, ILLEGAL_OPCODE, ILLEGAL_OPCODE,\n            ILLEGAL_OPCODE, ILLEGAL_OPCODE, ILLEGAL_OPCODE, ILLEGAL_OPCODE,\n            ILLEGAL_OPCODE, ILLEGAL_OPCODE, ILLEGAL_OPCODE, ILLEGAL_OPCODE,\n            ILLEGAL_OPCODE, ILLEGAL_OPCODE, ILLEGAL_OPCODE, ILLEGAL_OPCODE,\n            ILLEGAL_OPCODE, ILLEGAL_OPCODE, ILLEGAL_OPCODE, ILLEGAL_OPCODE,\n            ILLEGAL_OPCODE, \"impdep1\", \"impdep2\"\n            // endregion\n    };\n\n    public static final int OPCODE_NAMES_LENGTH = OPCODE_NAMES.length;\n\n    public static String getOpcodeName(final int index) {\n        return OPCODE_NAMES[index];\n    }\n\n    private static final short[] NO_OF_OPERANDS = {\n            // region Number of byte code operands\n            0/*nop*/, 0/*aconst_null*/, 0/*iconst_m1*/, 0/*iconst_0*/,\n            0/*iconst_1*/, 0/*iconst_2*/, 0/*iconst_3*/, 0/*iconst_4*/,\n            0/*iconst_5*/, 0/*lconst_0*/, 0/*lconst_1*/, 0/*fconst_0*/,\n            0/*fconst_1*/, 0/*fconst_2*/, 0/*dconst_0*/, 0/*dconst_1*/,\n            1/*bipush*/, 2/*sipush*/, 1/*ldc*/, 2/*ldc_w*/, 2/*ldc2_w*/,\n            1/*iload*/, 1/*lload*/, 1/*fload*/, 1/*dload*/, 1/*aload*/,\n            0/*iload_0*/, 0/*iload_1*/, 0/*iload_2*/, 0/*iload_3*/,\n            0/*lload_0*/, 0/*lload_1*/, 0/*lload_2*/, 0/*lload_3*/,\n            0/*fload_0*/, 0/*fload_1*/, 0/*fload_2*/, 0/*fload_3*/,\n            0/*dload_0*/, 0/*dload_1*/, 0/*dload_2*/, 0/*dload_3*/,\n            0/*aload_0*/, 0/*aload_1*/, 0/*aload_2*/, 0/*aload_3*/,\n            0/*iaload*/, 0/*laload*/, 0/*faload*/, 0/*daload*/,\n            0/*aaload*/, 0/*baload*/, 0/*caload*/, 0/*saload*/,\n            1/*istore*/, 1/*lstore*/, 1/*fstore*/, 1/*dstore*/,\n            1/*astore*/, 0/*istore_0*/, 0/*istore_1*/, 0/*istore_2*/,\n            0/*istore_3*/, 0/*lstore_0*/, 0/*lstore_1*/, 0/*lstore_2*/,\n            0/*lstore_3*/, 0/*fstore_0*/, 0/*fstore_1*/, 0/*fstore_2*/,\n            0/*fstore_3*/, 0/*dstore_0*/, 0/*dstore_1*/, 0/*dstore_2*/,\n            0/*dstore_3*/, 0/*astore_0*/, 0/*astore_1*/, 0/*astore_2*/,\n            0/*astore_3*/, 0/*iastore*/, 0/*lastore*/, 0/*fastore*/,\n            0/*dastore*/, 0/*aastore*/, 0/*bastore*/, 0/*castore*/,\n            0/*sastore*/, 0/*pop*/, 0/*pop2*/, 0/*dup*/, 0/*dup_x1*/,\n            0/*dup_x2*/, 0/*dup2*/, 0/*dup2_x1*/, 0/*dup2_x2*/, 0/*swap*/,\n            0/*iadd*/, 0/*ladd*/, 0/*fadd*/, 0/*dadd*/, 0/*isub*/,\n            0/*lsub*/, 0/*fsub*/, 0/*dsub*/, 0/*imul*/, 0/*lmul*/,\n            0/*fmul*/, 0/*dmul*/, 0/*idiv*/, 0/*ldiv*/, 0/*fdiv*/,\n            0/*ddiv*/, 0/*irem*/, 0/*lrem*/, 0/*frem*/, 0/*drem*/,\n            0/*ineg*/, 0/*lneg*/, 0/*fneg*/, 0/*dneg*/, 0/*ishl*/,\n            0/*lshl*/, 0/*ishr*/, 0/*lshr*/, 0/*iushr*/, 0/*lushr*/,\n            0/*iand*/, 0/*land*/, 0/*ior*/, 0/*lor*/, 0/*ixor*/, 0/*lxor*/,\n            2/*iinc*/, 0/*i2l*/, 0/*i2f*/, 0/*i2d*/, 0/*l2i*/, 0/*l2f*/,\n            0/*l2d*/, 0/*f2i*/, 0/*f2l*/, 0/*f2d*/, 0/*d2i*/, 0/*d2l*/,\n            0/*d2f*/, 0/*i2b*/, 0/*i2c*/, 0/*i2s*/, 0/*lcmp*/, 0/*fcmpl*/,\n            0/*fcmpg*/, 0/*dcmpl*/, 0/*dcmpg*/, 2/*ifeq*/, 2/*ifne*/,\n            2/*iflt*/, 2/*ifge*/, 2/*ifgt*/, 2/*ifle*/, 2/*if_icmpeq*/,\n            2/*if_icmpne*/, 2/*if_icmplt*/, 2/*if_icmpge*/, 2/*if_icmpgt*/,\n            2/*if_icmple*/, 2/*if_acmpeq*/, 2/*if_acmpne*/, 2/*goto*/,\n            2/*jsr*/, 1/*ret*/, UNPREDICTABLE/*tableswitch*/, UNPREDICTABLE/*lookupswitch*/,\n            0/*ireturn*/, 0/*lreturn*/, 0/*freturn*/,\n            0/*dreturn*/, 0/*areturn*/, 0/*return*/,\n            2/*getstatic*/, 2/*putstatic*/, 2/*getfield*/,\n            2/*putfield*/, 2/*invokevirtual*/, 2/*invokespecial*/, 2/*invokestatic*/,\n            4/*invokeinterface*/, 4/*invokedynamic*/, 2/*new*/,\n            1/*newarray*/, 2/*anewarray*/,\n            0/*arraylength*/, 0/*athrow*/, 2/*checkcast*/,\n            2/*instanceof*/, 0/*monitorenter*/,\n            0/*monitorexit*/, UNPREDICTABLE/*wide*/, 3/*multianewarray*/,\n            2/*ifnull*/, 2/*ifnonnull*/, 4/*goto_w*/,\n            4/*jsr_w*/, 0/*breakpoint*/, UNDEFINED,\n            UNDEFINED, UNDEFINED, UNDEFINED, UNDEFINED, UNDEFINED, UNDEFINED,\n            UNDEFINED, UNDEFINED, UNDEFINED, UNDEFINED, UNDEFINED, UNDEFINED,\n            UNDEFINED, UNDEFINED, UNDEFINED, UNDEFINED, UNDEFINED, UNDEFINED,\n            UNDEFINED, UNDEFINED, UNDEFINED, UNDEFINED, UNDEFINED, UNDEFINED,\n            UNDEFINED, UNDEFINED, UNDEFINED, UNDEFINED, UNDEFINED, UNDEFINED,\n            UNDEFINED, UNDEFINED, UNDEFINED, UNDEFINED, UNDEFINED, UNDEFINED,\n            UNDEFINED, UNDEFINED, UNDEFINED, UNDEFINED, UNDEFINED, UNDEFINED,\n            UNDEFINED, UNDEFINED, UNDEFINED, UNDEFINED, UNDEFINED, UNDEFINED,\n            UNDEFINED, UNDEFINED, RESERVED/*impdep1*/, RESERVED/*impdep2*/\n            // endregion\n    };\n\n    /**\n     *\n     * @param index\n     * @return Number of byte code operands\n     */\n    public static short getNoOfOperands(final int index) {\n        return NO_OF_OPERANDS[index];\n    }\n\n}\n"
  },
  {
    "path": "src/main/java/lsieun/utils/ReadUtils.java",
    "content": "package lsieun.utils;\n\nimport lsieun.cst.Const;\n\nimport java.io.InputStream;\n\npublic class ReadUtils {\n    /**\n     * @param filepath 例如，/home/user/Workspace/tmp/HelloWorld.class\n     * @return file bytes\n     */\n    public static byte[] readByPath(String filepath) {\n        System.out.println(\"Class File Path: file://\" + filepath);\n        return FileUtils.readBytes(filepath);\n    }\n\n    /**\n     * @param class_name 例如， lsieun.classfile.MagicNumber\n     * @return class file bytes\n     */\n    public static byte[] readByProject(String class_name) {\n        String filepath = FileUtils.getFilePath(ReadUtils.class, class_name);\n        System.out.println(\"Class File Path: file://\" + filepath);\n\n        return FileUtils.readBytes(filepath);\n    }\n\n    /**\n     * @param clazz 例如 Object.class\n     * @return class file bytes\n     */\n    public static byte[] readByClassLoader(Class<?> clazz) {\n        String class_name = clazz.getName();\n        if (Const.DEBUG) System.out.println(\"Load Class: \" + class_name);\n        InputStream in = FileUtils.getInputStream(class_name);\n        return FileUtils.readStream(in, true);\n    }\n\n    /**\n     * @param jar_path   例如，/usr/local/jdk8/jre/lib/rt.jar\n     * @param entry_name 例如，java/lang/Object.class\n     * @return jar file bytes\n     */\n    public static byte[] readByJar(String jar_path, String entry_name) {\n        String filepath = \"jar:file:\" + jar_path + \"!/\" + entry_name;\n        System.out.println(\"Class File Path: \" + filepath);\n\n        return JarUtils.readClass(jar_path, entry_name);\n    }\n}\n"
  },
  {
    "path": "src/main/java/lsieun/utils/StringUtils.java",
    "content": "package lsieun.utils;\n\nimport java.util.Formatter;\nimport java.util.List;\n\npublic class StringUtils {\n    public static byte[] array2Bytes(String str) {\n        String[] array = str.replace(\"[\", \"\").replace(\"]\", \"\").split(\",\");\n        int length = array.length;\n        byte[] bytes = new byte[length];\n        for (int i = 0; i < length; i++) {\n            int val = Integer.parseInt(array[i].trim());\n            bytes[i] = (byte) val;\n        }\n        return bytes;\n    }\n\n    public static String list2str(List<String> list) {\n        StringBuilder sb = new StringBuilder();\n        Formatter fm = new Formatter(sb);\n        for (String item : list) {\n            fm.format(\"%s%n\", item);\n        }\n        return sb.toString();\n    }\n}\n"
  },
  {
    "path": "src/main/java/lsieun/utils/ValueUtils.java",
    "content": "package lsieun.utils;\n\nimport lsieun.asm.analysis.nullability.Nullability;\nimport lsieun.asm.analysis.nullability.NullabilityInterpreter;\nimport lsieun.asm.analysis.nullability.NullabilityValue;\nimport org.objectweb.asm.Type;\nimport org.objectweb.asm.tree.AbstractInsnNode;\nimport org.objectweb.asm.tree.MethodNode;\nimport org.objectweb.asm.tree.analysis.BasicValue;\nimport org.objectweb.asm.tree.analysis.SourceValue;\n\nimport java.util.Arrays;\n\npublic class ValueUtils {\n    public static String fromBasicValue2String(BasicValue basicValue) {\n        String descriptor = basicValue.toString();\n        return DescriptorUtils.simplify(descriptor);\n    }\n\n    public static String fromSourceValue2String(MethodNode mn, SourceValue sourceValue) {\n        int size = sourceValue.insns.size();\n        int[] array = new int[size];\n        int i = 0;\n        for (AbstractInsnNode node : sourceValue.insns) {\n            array[i] = mn.instructions.indexOf(node);\n            i++;\n        }\n        Arrays.sort(array);\n        return Arrays.toString(array);\n    }\n\n    public static String fromSourceValue2Index(MethodNode mn, SourceValue sourceValue) {\n        int size = sourceValue.insns.size();\n        int[] array = new int[size];\n        int i = 0;\n        for (AbstractInsnNode node : sourceValue.insns) {\n            array[i] = mn.instructions.indexOf(node);\n            i++;\n        }\n        Arrays.sort(array);\n        return Arrays.toString(array);\n    }\n\n    public static String fromNullabilityValue2String(NullabilityValue value) {\n        String firstPart;\n\n        Type type = value.getType();\n        if (value == NullabilityInterpreter.UNINITIALIZED_VALUE) {\n            return \".\";\n        }\n        else if (value == NullabilityInterpreter.RETURN_ADDRESS_VALUE) {\n            return \"address\";\n        }\n        else if (type.getSort() == Type.INT) {\n            firstPart = \"int\";\n        }\n        else if (type.getSort() == Type.FLOAT) {\n            firstPart = \"float\";\n        }\n        else if (type.getSort() == Type.LONG) {\n            firstPart = \"long\";\n        }\n        else if (type.getSort() == Type.DOUBLE) {\n            firstPart = \"double\";\n        }\n        else if (value.isReference()) {\n            firstPart = type.getClassName();\n            int index = firstPart.lastIndexOf(\".\");\n            if (index != -1) {\n                firstPart = firstPart.substring(index + 1);\n            }\n        }\n        else {\n            firstPart = String.format(\"illegal value: %s\", type);\n        }\n\n        String secondPart;\n        Nullability state = value.getState();\n        switch (state) {\n            case UNKNOWN:\n                secondPart = \"\";\n                break;\n            case NOT_NULL:\n                secondPart = \":NOT-NULL\";\n                break;\n            case NULL:\n                secondPart = \":NULL\";\n                break;\n            case NULLABLE:\n                secondPart = \":NULLABLE\";\n                break;\n            default:\n                secondPart = \":IMPOSSIBLE\";\n        }\n        return firstPart + secondPart;\n    }\n}\n"
  },
  {
    "path": "src/main/java/run/ASMPrint.java",
    "content": "package run;\n\nimport org.objectweb.asm.ClassReader;\nimport org.objectweb.asm.util.ASMifier;\nimport org.objectweb.asm.util.Printer;\nimport org.objectweb.asm.util.Textifier;\nimport org.objectweb.asm.util.TraceClassVisitor;\n\nimport java.io.IOException;\nimport java.io.PrintWriter;\n\n/**\n * Print asm code or text.\n * 这里的代码是参考自{@link org.objectweb.asm.util.Printer}的main方法。\n * 推荐使用{@link PrintASMCodeCore}或 {@link PrintASMTextClass}类。\n *\n * @see PrintASMCodeCore\n * @see PrintASMCodeTree\n * @see PrintASMTextClass\n * @see PrintASMTextLambda\n * @see Printer\n */\npublic class ASMPrint {\n    public static void main(String[] args) throws IOException {\n        // (1) 设置参数\n        String className = \"sample.HelloWorld\";\n        int parsingOptions = ClassReader.SKIP_FRAMES | ClassReader.SKIP_DEBUG;\n        boolean asmCode = true;\n\n        // (2) 打印结果\n        Printer printer = asmCode ? new ASMifier() : new Textifier();\n        PrintWriter printWriter = new PrintWriter(System.out, true);\n        TraceClassVisitor traceClassVisitor = new TraceClassVisitor(null, printer, printWriter);\n        new ClassReader(className).accept(traceClassVisitor, parsingOptions);\n    }\n}\n"
  },
  {
    "path": "src/main/java/run/BoxDrawingRun.java",
    "content": "package run;\n\nimport lsieun.utils.BoxDrawingUtils;\n\npublic class BoxDrawingRun {\n    private static final String[][] Interpreter_Value_Matrix = {\n            {\"0\", \"Interpreter\", \"Value\", \"Range\"},\n            {\"1\", \"BasicInterpreter\", \"BasicValue\", \"7\"},\n            {\"2\", \"BasicVerifier\", \"BasicValue\", \"7\"},\n            {\"3\", \"SimpleVerifier\", \"BasicValue\", \"N\"},\n            {\"4\", \"SourceInterpreter\", \"SourceValue\", \"N\"},\n    };\n\n    private static final String[][] Analyzer_Frame_Interpreter_Value_Matrix = {\n            {\"Fixed\", \"Analyzer\"},\n            {\"\", \"Frame\"},\n            {\"Variable\", \"Interpreter\"},\n            {\"\", \"Value\"},\n    };\n\n    private static final String[][] Top_Null_Void = {\n            {\"\", \"ASM Type\", \"Frame Value\"},\n            {\"top\", \"null\", \"BasicValue.UNINITIALIZED_VALUE\"},\n            {\"aconst_null\", \"BasicInterpreter.NULL_TYPE\", \"BasicValue.REFERENCE_VALUE\"},\n            {\"void\", \"Type.VOID_TYPE\", \"null\"},\n    };\n\n    public static void main(String[] args) {\n        BoxDrawingUtils.printTable(Top_Null_Void);\n    }\n}\n"
  },
  {
    "path": "src/main/java/run/BytecodeRun.java",
    "content": "package run;\n\nimport lsieun.classfile.InsnRaw;\nimport lsieun.cst.Const;\nimport lsieun.utils.HexUtils;\n\nimport java.util.List;\n\npublic class BytecodeRun {\n    public static void main(String[] args) {\n        // (1) 准备输入参数\n        String input = \"    0x0000000: 121a 121c b600 2099 0022 b200 26b2 0026\\n\" +\n                \"    0x0000010: b200 2b04 bc0a 5903 044f b600 2fb6 0033\\n\" +\n                \"    0x0000020: c000 35b6 0039 a700 22b2 003e b600 42b2\\n\" +\n                \"    0x0000030: 0026 b200 2b04 bc0a 5903 044f b600 2fb6\\n\" +\n                \"    0x0000040: 0033 c000 35b6 0047 b0\";\n\n        // (2) 转换为十六进制字符串\n        StringBuilder sb = new StringBuilder();\n        String[] lines = input.split(\"\\n\");\n        for (String item : lines) {\n            System.out.println(item.trim());\n            String[] array = item.split(\":\");\n            sb.append(array[1]);\n        }\n        String bytecodeHexStr = sb.toString().replaceAll(\"\\\\s\", \"\");\n        System.out.println(Const.DIVISION_LINE);\n\n        // (3) 打印instruction信息\n        byte[] bytes = HexUtils.parse(bytecodeHexStr);\n        InsnRaw insnRaw = new InsnRaw(bytes);\n        List<String> instructions = insnRaw.getList();\n        for (String item : instructions) {\n            System.out.println(item);\n        }\n    }\n}\n"
  },
  {
    "path": "src/main/java/run/ControlFlowGraphRun.java",
    "content": "package run;\n\nimport lsieun.asm.analysis.*;\nimport lsieun.asm.analysis.cc.CyclomaticComplexity;\nimport lsieun.asm.analysis.graph.InsnBlock;\nimport lsieun.asm.analysis.graph.TextGraph;\nimport lsieun.utils.FileUtils;\nimport org.objectweb.asm.ClassReader;\nimport org.objectweb.asm.tree.ClassNode;\nimport org.objectweb.asm.tree.MethodNode;\nimport org.objectweb.asm.tree.analysis.AnalyzerException;\nimport org.objectweb.asm.tree.analysis.BasicInterpreter;\nimport org.objectweb.asm.tree.analysis.BasicValue;\n\nimport java.util.List;\n\npublic class ControlFlowGraphRun {\n    public static void main(String[] args) throws Exception {\n        String relative_path = \"sample/HelloWorld.class\";\n        String filepath = FileUtils.getFilePath(relative_path);\n        byte[] bytes = FileUtils.readBytes(filepath);\n\n        //（1）构建ClassReader\n        ClassReader cr = new ClassReader(bytes);\n\n        //（2）生成ClassNode\n        ClassNode cn = new ClassNode();\n\n        int parsingOptions = ClassReader.SKIP_DEBUG | ClassReader.SKIP_FRAMES;\n        cr.accept(cn, parsingOptions);\n\n        //（3）查找方法\n        String methodName = \"test\";\n        MethodNode targetNode = null;\n        for (MethodNode mn : cn.methods) {\n            if (mn.name.equals(methodName)) {\n                targetNode = mn;\n                break;\n            }\n        }\n        if (targetNode == null) {\n            System.out.println(\"Can not find method: \" + methodName);\n            return;\n        }\n\n        //（4）进行图形化显示\n        System.out.println(\"Origin:\");\n        display(cn.name, targetNode, ControlFlowGraphType.NONE);\n        System.out.println(\"Control Flow Graph:\");\n        display(cn.name, targetNode, ControlFlowGraphType.STANDARD);\n\n        //（5）打印复杂度\n        int complexity = CyclomaticComplexity.getCyclomaticComplexity(cn.name, targetNode);\n        String line = String.format(\"%s:%s complexity: %d\", targetNode.name, targetNode.desc, complexity);\n        System.out.println(line);\n    }\n\n    private static void display(String owner, MethodNode mn, ControlFlowGraphType option) throws AnalyzerException {\n        //（1）准备数据\n        InsnBlock[] blocks;\n        switch (option) {\n            case NONE: {\n                InsnText insnText = new InsnText();\n                List<String> lines = insnText.toLines(mn.instructions.toArray());\n\n                InsnBlock block = new InsnBlock();\n                block.addLines(lines);\n\n                blocks = new InsnBlock[1];\n                blocks[0] = block;\n                break;\n            }\n            case ONE_INSN_ONE_BOX: {\n                ControlFlowEdgeAnalyzer<BasicValue> analyzer = new ControlFlowEdgeAnalyzer<>(new BasicInterpreter());\n                analyzer.analyze(owner, mn);\n                blocks = analyzer.getBlocks();\n                break;\n            }\n            case MULTI_INSN_ONE_BOX:\n            case STANDARD: {\n                ControlFlowEdgeAnalyzer<BasicValue> analyzer = new ControlFlowEdgeAnalyzer2<>(new BasicInterpreter());\n                analyzer.analyze(owner, mn);\n                blocks = analyzer.getBlocks();\n                break;\n            }\n            case EXPERIMENT_ONE_INSN_ONE_BOX: {\n                ControlFlowAnalyzer2 analyzer = new ControlFlowAnalyzer2();\n                analyzer.analyze(owner, mn);\n                blocks = analyzer.getBlocks();\n                break;\n            }\n            case EXPERIMENT_MULTI_INSN_ONE_BOX: {\n                ControlFlowGraphAnalyzer analyzer = new ControlFlowGraphAnalyzer();\n                analyzer.analyze(mn);\n                blocks = analyzer.getBlocks();\n                break;\n            }\n            default: {\n                throw new RuntimeException(\"unknown type: \" + option);\n            }\n        }\n\n        //（2）图形显示\n        TextGraph textGraph = new TextGraph(blocks);\n        textGraph.draw(0, 0);\n    }\n}\n"
  },
  {
    "path": "src/main/java/run/ControlFlowGraphType.java",
    "content": "package run;\n\npublic enum ControlFlowGraphType {\n    NONE,\n    ONE_INSN_ONE_BOX,\n    MULTI_INSN_ONE_BOX,\n    STANDARD,\n    EXPERIMENT_ONE_INSN_ONE_BOX,\n    EXPERIMENT_MULTI_INSN_ONE_BOX;\n}\n"
  },
  {
    "path": "src/main/java/run/HelloWorldAnalysisCore.java",
    "content": "package run;\n\nimport lsieun.asm.core.*;\nimport lsieun.utils.FileUtils;\nimport org.objectweb.asm.ClassReader;\nimport org.objectweb.asm.ClassVisitor;\nimport org.objectweb.asm.Opcodes;\n\npublic class HelloWorldAnalysisCore {\n    public static void main(String[] args) {\n        String relative_path = \"sample/HelloWorld.class\";\n        String filepath = FileUtils.getFilePath(relative_path);\n        byte[] bytes = FileUtils.readBytes(filepath);\n\n        //（1）构建ClassReader\n        ClassReader cr = new ClassReader(bytes);\n\n        //（2）分析ClassVisitor\n        int api = Opcodes.ASM9;\n        ClassVisitor cv = new MethodFindRefVisitor(api, null, \"sample/HelloWorld\", \"test\", \"(III)V\");\n\n        //（3）结合ClassReader和ClassVisitor\n        int parsingOptions = ClassReader.SKIP_DEBUG | ClassReader.SKIP_FRAMES;\n        cr.accept(cv, parsingOptions);\n    }\n}\n"
  },
  {
    "path": "src/main/java/run/HelloWorldAnalysisTree.java",
    "content": "package run;\n\nimport lsieun.asm.analysis.cc.CyclomaticComplexity;\nimport lsieun.utils.FileUtils;\nimport org.objectweb.asm.ClassReader;\nimport org.objectweb.asm.Opcodes;\nimport org.objectweb.asm.tree.ClassNode;\nimport org.objectweb.asm.tree.MethodNode;\n\nimport java.util.List;\n\npublic class HelloWorldAnalysisTree {\n    public static void main(String[] args) throws Exception {\n        String relative_path = \"sample/HelloWorld.class\";\n        String filepath = FileUtils.getFilePath(relative_path);\n        byte[] bytes = FileUtils.readBytes(filepath);\n\n        //（1）构建ClassReader\n        ClassReader cr = new ClassReader(bytes);\n\n        //（2）生成ClassNode\n        int api = Opcodes.ASM9;\n        ClassNode cn = new ClassNode(api);\n\n        int parsingOptions = ClassReader.SKIP_DEBUG | ClassReader.SKIP_FRAMES;\n        cr.accept(cn, parsingOptions);\n\n        //（3）进行分析\n        String className = cn.name;\n        List<MethodNode> methods = cn.methods;\n        MethodNode mn = methods.get(1);\n        int complexity = CyclomaticComplexity.getCyclomaticComplexity(className, mn);\n        String line = String.format(\"%s:%s%n    complexity: %d\", mn.name, mn.desc, complexity);\n        System.out.println(line);\n    }\n}\n"
  },
  {
    "path": "src/main/java/run/HelloWorldFrameCore.java",
    "content": "package run;\n\nimport lsieun.asm.commons.MethodStackMapFrameVisitor;\nimport lsieun.utils.FileUtils;\nimport org.objectweb.asm.ClassReader;\nimport org.objectweb.asm.ClassVisitor;\nimport org.objectweb.asm.Opcodes;\n\npublic class HelloWorldFrameCore {\n    public static void main(String[] args) {\n        String relative_path = \"sample/HelloWorld.class\";\n        String filepath = FileUtils.getFilePath(relative_path);\n        byte[] bytes = FileUtils.readBytes(filepath);\n\n        //（1）构建ClassReader\n        ClassReader cr = new ClassReader(bytes);\n\n        //（2）构建ClassVisitor\n        int api = Opcodes.ASM9;\n        ClassVisitor cv = new MethodStackMapFrameVisitor(api, null);\n\n        //（3）结合ClassReader和ClassVisitor\n        int parsingOptions = ClassReader.EXPAND_FRAMES; // 注意，这里使用了EXPAND_FRAMES\n        cr.accept(cv, parsingOptions);\n    }\n}\n"
  },
  {
    "path": "src/main/java/run/HelloWorldFrameCore02.java",
    "content": "package run;\n\nimport lsieun.asm.commons.MethodStackMapFrame02Visitor;\nimport lsieun.utils.FileUtils;\nimport org.objectweb.asm.ClassReader;\nimport org.objectweb.asm.ClassVisitor;\nimport org.objectweb.asm.Opcodes;\n\npublic class HelloWorldFrameCore02 {\n    public static void main(String[] args) {\n        String relative_path = \"sample/HelloWorld.class\";\n        String filepath = FileUtils.getFilePath(relative_path);\n        byte[] bytes = FileUtils.readBytes(filepath);\n\n        //（1）构建ClassReader\n        ClassReader cr = new ClassReader(bytes);\n\n        //（2）构建ClassVisitor\n        int api = Opcodes.ASM9;\n        ClassVisitor cv = new MethodStackMapFrame02Visitor(api, null, bytes);\n\n        //（3）结合ClassReader和ClassVisitor\n        int parsingOptions = ClassReader.EXPAND_FRAMES; // 注意，这里使用了EXPAND_FRAMES\n        cr.accept(cv, parsingOptions);\n    }\n}\n"
  },
  {
    "path": "src/main/java/run/HelloWorldFrameTree.java",
    "content": "package run;\n\nimport lsieun.asm.analysis.InsnText;\nimport lsieun.asm.analysis.nullability.*;\nimport lsieun.asm.analysis.transition.DestinationInterpreter;\nimport lsieun.utils.FileUtils;\nimport lsieun.utils.FrameUtils;\nimport lsieun.utils.ValueUtils;\nimport org.objectweb.asm.ClassReader;\nimport org.objectweb.asm.Opcodes;\nimport org.objectweb.asm.tree.AbstractInsnNode;\nimport org.objectweb.asm.tree.ClassNode;\nimport org.objectweb.asm.tree.MethodNode;\nimport org.objectweb.asm.tree.analysis.*;\n\nimport java.util.List;\nimport java.util.function.Function;\n\npublic class HelloWorldFrameTree {\n    public static void main(String[] args) throws Exception {\n        String relative_path = \"sample/HelloWorld.class\";\n        String filepath = FileUtils.getFilePath(relative_path);\n        byte[] bytes = FileUtils.readBytes(filepath);\n\n        // (1)构建ClassReader\n        ClassReader cr = new ClassReader(bytes);\n\n        // (2) 构建ClassNode\n        ClassNode cn = new ClassNode();\n        cr.accept(cn, ClassReader.SKIP_FRAMES | ClassReader.SKIP_DEBUG);\n\n        // (3) 查看方法Instruction和Frame\n        String owner = cn.name;\n        List<MethodNode> methods = cn.methods;\n        for (MethodNode mn : methods) {\n            print(owner, mn, 0);\n        }\n    }\n\n    private static void print(String owner, MethodNode mn, int option) throws AnalyzerException {\n        InsnText insnText = new InsnText();\n\n        switch (option) {\n            case 0: {\n                Analyzer<BasicValue> analyzer = new Analyzer<>(new SimpleVerifier());\n                FrameUtils.printGraph(owner, mn, analyzer, ValueUtils::fromBasicValue2String);\n                break;\n            }\n            case 1:\n                print(owner, mn, new BasicInterpreter(), null);\n                break;\n            case 2:\n                print(owner, mn, new BasicVerifier(), null);\n                break;\n            case 3:\n                print(owner, mn, new SimpleVerifier(), null);\n                break;\n            case 4:\n                print(owner, mn, new SimpleVerifier(), item -> ValueUtils.fromBasicValue2String(item) + \"@\" + System.identityHashCode(item));\n                break;\n            case 5:\n                print(owner, mn, new SimpleVerifier(), ValueUtils::fromBasicValue2String);\n                break;\n            case 6:\n                print(owner, mn, new SourceInterpreter(), null);\n                break;\n            case 7:\n                print(owner, mn, new SourceInterpreter(), sourceValue -> insnText.toLines(sourceValue.insns.toArray(new AbstractInsnNode[0])));\n                break;\n            case 8:\n                print(owner, mn, new SourceInterpreter(), sourceValue ->\n                        ValueUtils.fromSourceValue2Index(mn, sourceValue)\n                );\n                break;\n            case 9:\n                print(owner, mn, new DestinationInterpreter(), sourceValue -> insnText.toLines(sourceValue.insns.toArray(new AbstractInsnNode[0])));\n                break;\n            case 10:\n                print(owner, mn, new DestinationInterpreter(), sourceValue ->\n                        ValueUtils.fromSourceValue2Index(mn, sourceValue)\n                );\n                break;\n            case 11:\n                print(owner, mn, new NullDeferenceInterpreter(Opcodes.ASM9), value -> {\n                    if (value.isReference()) {\n                        if (value == NullDeferenceInterpreter.NULL_VALUE) {\n                            return \"null\";\n                        }\n                        else if (value == NullDeferenceInterpreter.MAYBE_NULL_VALUE) {\n                            return \"may-be-null\";\n                        }\n                    }\n\n                    return value.toString();\n                });\n                break;\n            case 12: {\n                Analyzer<NullabilityValue> analyzer = new NullabilityAnalyzer(new NullabilityInterpreter(Opcodes.ASM9));\n                FrameUtils.printFrames(owner, mn, analyzer, ValueUtils::fromNullabilityValue2String);\n                break;\n            }\n            default:\n                throw new IllegalArgumentException(\"option is not valid: \" + option);\n        }\n    }\n\n    // NOTE: print方法重点是修改第3个和第4个参数\n    public static <V extends Value, T> void print(String owner, MethodNode mn, Interpreter<V> interpreter, Function<V, T> func) throws AnalyzerException {\n        Analyzer<V> analyzer = new Analyzer<>(interpreter);\n        FrameUtils.printFrames(owner, mn, analyzer, func);\n    }\n}\n"
  },
  {
    "path": "src/main/java/run/HelloWorldGenerateCore.java",
    "content": "package run;\n\nimport lsieun.utils.FileUtils;\nimport org.objectweb.asm.*;\n\nimport static org.objectweb.asm.Opcodes.*;\n\npublic class HelloWorldGenerateCore {\n    public static void main(String[] args) throws Exception {\n        String relative_path = \"sample/HelloWorld.class\";\n        String filepath = FileUtils.getFilePath(relative_path);\n\n        // (1) 生成byte[]内容\n        byte[] bytes = dump();\n\n        // (2) 保存byte[]到文件\n        FileUtils.writeBytes(filepath, bytes);\n    }\n\n    public static byte[] dump() throws Exception {\n        // (1) 创建ClassWriter对象\n        ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_FRAMES);\n\n        // (2) 调用visitXxx()方法\n        cw.visit(V1_8, ACC_PUBLIC + ACC_SUPER, \"sample/HelloWorld\",\n                null, \"java/lang/Object\", null);\n\n        {\n            MethodVisitor mv1 = cw.visitMethod(ACC_PUBLIC, \"<init>\", \"()V\", null, null);\n            mv1.visitCode();\n            mv1.visitVarInsn(ALOAD, 0);\n            mv1.visitMethodInsn(INVOKESPECIAL, \"java/lang/Object\", \"<init>\", \"()V\", false);\n            mv1.visitInsn(RETURN);\n            mv1.visitMaxs(1, 1);\n            mv1.visitEnd();\n        }\n\n        {\n            MethodVisitor mv2 = cw.visitMethod(ACC_PUBLIC, \"test\", \"()V\", null, null);\n            mv2.visitCode();\n            mv2.visitInsn(NOP);\n            mv2.visitFieldInsn(GETSTATIC, \"java/lang/System\", \"out\", \"Ljava/io/PrintStream;\");\n            mv2.visitInsn(NOP);\n            mv2.visitLdcInsn(\"Hello World\");\n            mv2.visitInsn(NOP);\n            mv2.visitMethodInsn(INVOKEVIRTUAL, \"java/io/PrintStream\", \"println\", \"(Ljava/lang/String;)V\", false);\n            mv2.visitInsn(NOP);\n            mv2.visitInsn(RETURN);\n            mv2.visitMaxs(2, 1);\n            mv2.visitEnd();\n        }\n        cw.visitEnd();\n\n        // (3) 调用toByteArray()方法\n        return cw.toByteArray();\n    }\n}"
  },
  {
    "path": "src/main/java/run/HelloWorldGenerateTree.java",
    "content": "package run;\n\nimport lsieun.utils.FileUtils;\nimport org.objectweb.asm.ClassWriter;\nimport org.objectweb.asm.tree.*;\n\nimport static org.objectweb.asm.Opcodes.*;\n\npublic class HelloWorldGenerateTree {\n    public static void main(String[] args) throws Exception {\n        String relative_path = \"sample/HelloWorld.class\";\n        String filepath = FileUtils.getFilePath(relative_path);\n\n        // (1) 生成byte[]内容\n        byte[] bytes = dump();\n\n        // (2) 保存byte[]到文件\n        FileUtils.writeBytes(filepath, bytes);\n    }\n\n    public static byte[] dump() throws Exception {\n        // (1) 使用ClassNode类收集数据\n        ClassNode cn = new ClassNode();\n        cn.version = V1_8;\n        cn.access = ACC_PUBLIC | ACC_SUPER;\n        cn.name = \"sample/HelloWorld\";\n        cn.signature = null;\n        cn.superName = \"java/lang/Object\";\n\n        {\n            MethodNode mn1 = new MethodNode(ACC_PUBLIC, \"<init>\", \"()V\", null, null);\n            cn.methods.add(mn1);\n\n            InsnList il = mn1.instructions;\n            il.add(new VarInsnNode(ALOAD, 0));\n            il.add(new MethodInsnNode(INVOKESPECIAL, \"java/lang/Object\", \"<init>\", \"()V\", false));\n            il.add(new InsnNode(RETURN));\n\n            mn1.maxStack = 1;\n            mn1.maxLocals = 1;\n        }\n\n        {\n            MethodNode mn2 = new MethodNode(ACC_PUBLIC, \"test\", \"()V\", null, null);\n            cn.methods.add(mn2);\n\n            InsnList il = mn2.instructions;\n            il.add(new FieldInsnNode(GETSTATIC, \"java/lang/System\", \"out\", \"Ljava/io/PrintStream;\"));\n            il.add(new LdcInsnNode(\"Hello World\"));\n            il.add(new MethodInsnNode(INVOKEVIRTUAL, \"java/io/PrintStream\", \"println\", \"(Ljava/lang/String;)V\", false));\n            il.add(new InsnNode(RETURN));\n\n            mn2.maxStack = 2;\n            mn2.maxLocals = 1;\n        }\n\n        // (2) 使用ClassWriter类生成字节码\n        ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_FRAMES);\n        cn.accept(cw);\n        return cw.toByteArray();\n    }\n}\n"
  },
  {
    "path": "src/main/java/run/HelloWorldRun.java",
    "content": "package run;\n\npublic class HelloWorldRun {\n    public static void main(String[] args) throws Exception {\n        Class<?> clazz = Class.forName(\"sample.HelloWorld\");\n        Object instance = clazz.newInstance();\n        System.out.println(instance);\n    }\n}\n"
  },
  {
    "path": "src/main/java/run/HelloWorldTransformCore.java",
    "content": "package run;\n\nimport lsieun.asm.core.*;\nimport lsieun.asm.template.*;\nimport lsieun.utils.FileUtils;\nimport org.objectweb.asm.*;\n\npublic class HelloWorldTransformCore {\n    public static void main(String[] args) {\n        String relative_path = \"sample/HelloWorld.class\";\n        String filepath = FileUtils.getFilePath(relative_path);\n        byte[] bytes1 = FileUtils.readBytes(filepath);\n\n        //（1）构建ClassReader\n        ClassReader cr = new ClassReader(bytes1);\n\n        //（2）构建ClassWriter\n        ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_FRAMES);\n\n        //（3）串连ClassVisitor\n        int api = Opcodes.ASM9;\n        ClassVisitor cv = new ClassChangeVersionVisitor(api, cw);\n\n        //（4）结合ClassReader和ClassVisitor\n        int parsingOptions = ClassReader.SKIP_DEBUG | ClassReader.SKIP_FRAMES;\n        cr.accept(cv, parsingOptions);\n\n        //（5）生成byte[]\n        byte[] bytes2 = cw.toByteArray();\n\n        FileUtils.writeBytes(filepath, bytes2);\n    }\n}\n"
  },
  {
    "path": "src/main/java/run/HelloWorldTransformTree.java",
    "content": "package run;\n\nimport lsieun.asm.analysis.*;\nimport lsieun.asm.tree.*;\nimport lsieun.utils.FileUtils;\nimport org.objectweb.asm.ClassReader;\nimport org.objectweb.asm.ClassWriter;\nimport org.objectweb.asm.Opcodes;\nimport org.objectweb.asm.tree.*;\n\npublic class HelloWorldTransformTree {\n    public static void main(String[] args) {\n        String relative_path = \"sample/HelloWorld.class\";\n        String filepath = FileUtils.getFilePath(relative_path);\n        byte[] bytes1 = FileUtils.readBytes(filepath);\n\n        // (1)构建ClassReader\n        ClassReader cr = new ClassReader(bytes1);\n\n        // (2)构建ClassWriter\n        ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_FRAMES);\n\n        // (3)串连ClassNode\n        int api = Opcodes.ASM9;\n        ClassNode cn = new RemoveUnusedCastNode(api, cw);\n\n        //（4）结合ClassReader和ClassNode\n        int parsingOptions = ClassReader.SKIP_DEBUG | ClassReader.SKIP_FRAMES;\n        cr.accept(cn, parsingOptions);\n\n        // (5) 生成byte[]\n        byte[] bytes2 = cw.toByteArray();\n\n        FileUtils.writeBytes(filepath, bytes2);\n    }\n}\n"
  },
  {
    "path": "src/main/java/run/MockAnalyzerRun.java",
    "content": "package run;\n\nimport lsieun.asm.analysis.MockAnalyzer;\nimport lsieun.cst.Const;\nimport lsieun.utils.FileUtils;\nimport org.objectweb.asm.ClassReader;\nimport org.objectweb.asm.Opcodes;\nimport org.objectweb.asm.tree.ClassNode;\nimport org.objectweb.asm.tree.MethodNode;\nimport org.objectweb.asm.tree.analysis.*;\n\nimport java.util.List;\n\npublic class MockAnalyzerRun {\n    public static void main(String[] args) throws Exception {\n        String relative_path = \"sample/HelloWorld.class\";\n        String filepath = FileUtils.getFilePath(relative_path);\n        byte[] bytes = FileUtils.readBytes(filepath);\n\n        //（1）构建ClassReader\n        ClassReader cr = new ClassReader(bytes);\n\n        //（2）分析ClassVisitor\n        int api = Opcodes.ASM9;\n        ClassNode cn = new ClassNode();\n        int parsingOptions = ClassReader.SKIP_DEBUG | ClassReader.SKIP_FRAMES;\n        cr.accept(cn, parsingOptions);\n\n        //（3）分析ClassVisitor\n        MockAnalyzer<BasicValue> analyzer = new MockAnalyzer<>(new SimpleVerifier());\n\n        List<MethodNode> methods = cn.methods;\n        for (MethodNode mn : methods) {\n            System.out.println(\"Method Name: \" + mn.name + \":\" + mn.desc);\n            Frame<BasicValue>[] frames = analyzer.analyze(cn.name, mn);\n            for (Frame<?> f : frames) {\n                System.out.println(f);\n            }\n            System.out.println(Const.DIVISION_LINE);\n        }\n    }\n}\n"
  },
  {
    "path": "src/main/java/run/PrintASMCodeCore.java",
    "content": "package run;\n\nimport org.objectweb.asm.ClassReader;\nimport org.objectweb.asm.util.ASMifier;\nimport org.objectweb.asm.util.Printer;\nimport org.objectweb.asm.util.TraceClassVisitor;\n\nimport java.io.IOException;\nimport java.io.PrintWriter;\n\n/**\n * Print asm core code.\n *\n * @see ASMPrint\n * @see PrintASMCodeTree\n * @see PrintASMTextClass\n * @see PrintASMTextLambda\n */\npublic class PrintASMCodeCore {\n    public static void main(String[] args) throws IOException {\n        // (1) 设置参数\n        String className = \"sample.HelloWorld\";\n        int parsingOptions = ClassReader.SKIP_FRAMES | ClassReader.SKIP_DEBUG;\n\n        // (2) 打印结果\n        Printer printer = new ASMifier();\n        PrintWriter printWriter = new PrintWriter(System.out, true);\n        TraceClassVisitor traceClassVisitor = new TraceClassVisitor(null, printer, printWriter);\n        new ClassReader(className).accept(traceClassVisitor, parsingOptions);\n    }\n}\n"
  },
  {
    "path": "src/main/java/run/PrintASMCodeTree.java",
    "content": "package run;\n\nimport lsieun.asm.util.TreePrinter;\nimport org.objectweb.asm.ClassReader;\nimport org.objectweb.asm.util.Printer;\nimport org.objectweb.asm.util.TraceClassVisitor;\n\nimport java.io.IOException;\nimport java.io.PrintWriter;\n\n/**\n * Print asm tree code.\n *\n * @see ASMPrint\n * @see PrintASMCodeCore\n * @see PrintASMTextClass\n * @see PrintASMTextLambda\n */\npublic class PrintASMCodeTree {\n    public static void main(String[] args) throws IOException {\n        // (1) 设置参数\n        String className = \"sample.HelloWorld\";\n        int parsingOptions = ClassReader.SKIP_FRAMES | ClassReader.SKIP_DEBUG;\n\n        // (2) 打印结果\n        Printer printer = new TreePrinter();\n        PrintWriter printWriter = new PrintWriter(System.out, true);\n        TraceClassVisitor traceClassVisitor = new TraceClassVisitor(null, printer, printWriter);\n        new ClassReader(className).accept(traceClassVisitor, parsingOptions);\n    }\n}\n"
  },
  {
    "path": "src/main/java/run/PrintASMTextClass.java",
    "content": "package run;\n\nimport org.objectweb.asm.ClassReader;\nimport org.objectweb.asm.util.Printer;\nimport org.objectweb.asm.util.Textifier;\nimport org.objectweb.asm.util.TraceClassVisitor;\n\nimport java.io.IOException;\nimport java.io.PrintWriter;\n\n/**\n * @see ASMPrint\n * @see PrintASMCodeCore\n * @see PrintASMCodeTree\n * @see PrintASMTextLambda\n */\npublic class PrintASMTextClass {\n    public static void main(String[] args) throws IOException {\n        // (1) 设置参数\n        String className = \"sample.HelloWorld\";\n        int parsingOptions = ClassReader.SKIP_FRAMES | ClassReader.SKIP_DEBUG;\n\n        // (2) 打印结果\n        Printer printer = new Textifier();\n        PrintWriter printWriter = new PrintWriter(System.out, true);\n        TraceClassVisitor traceClassVisitor = new TraceClassVisitor(null, printer, printWriter);\n        new ClassReader(className).accept(traceClassVisitor, parsingOptions);\n    }\n}\n"
  },
  {
    "path": "src/main/java/run/PrintASMTextLambda.java",
    "content": "package run;\n\nimport lsieun.utils.StringUtils;\nimport org.objectweb.asm.ClassReader;\nimport org.objectweb.asm.util.Printer;\nimport org.objectweb.asm.util.Textifier;\nimport org.objectweb.asm.util.TraceClassVisitor;\n\nimport java.io.IOException;\nimport java.io.PrintWriter;\n\n/**\n * Print Lambda anonymous inner class.\n *\n * @see ASMPrint\n * @see PrintASMCodeCore\n * @see PrintASMCodeTree\n * @see PrintASMTextClass\n */\npublic class PrintASMTextLambda {\n    public static void main(String[] args) throws IOException {\n        // (1) 设置参数\n        String str = \"[-54, -2, -70, -66, ...]\";\n        byte[] bytes = StringUtils.array2Bytes(str);\n        int parsingOptions = ClassReader.SKIP_FRAMES | ClassReader.SKIP_DEBUG;\n\n        // (2) 打印结果\n        Printer printer = new Textifier();\n        PrintWriter printWriter = new PrintWriter(System.out, true);\n        TraceClassVisitor traceClassVisitor = new TraceClassVisitor(null, printer, printWriter);\n        new ClassReader(bytes).accept(traceClassVisitor, parsingOptions);\n    }\n}\n"
  },
  {
    "path": "src/main/java/run/PrintOpcodeTable.java",
    "content": "package run;\n\nimport lsieun.utils.OpcodeConst;\n\n/**\n * Print opcode between {@link #start} and {@link #stop}.\n */\npublic class PrintOpcodeTable {\n    private static int start = 0;\n    private static int stop = 256;\n\n    public static void main(String[] args) throws Exception {\n        printOpcode();\n    }\n\n    public static void printOpcode() {\n        if (start < 0) {\n            start = 0;\n        }\n        if (stop > 256) {\n            stop = 256;\n        }\n        int num = stop - start;\n        int column = 4;\n        int row = num / column;\n        int remainder = num % column;\n        if (remainder != 0) {\n            row++;\n        }\n        System.out.println(\"| opcode | mnemonic symbol | opcode | mnemonic symbol | opcode | mnemonic symbol | opcode | mnemonic symbol |\");\n        System.out.println(\"|--------|-----------------|--------|-----------------|--------|-----------------|--------|-----------------|\");\n        for (int i = 0; i < row; i++) {\n            int val1 = start + i;\n            int val2 = val1 + row;\n            int val3 = val2 + row;\n            int val4 = val3 + row;\n            String line = String.format(\"| %-6d | %-15s | %-6d | %-15s | %-6d | %-15s | %-6d | %-15s |\",\n                    val1, getOpcodeName(val1),\n                    val2, getOpcodeName(val2),\n                    val3, getOpcodeName(val3),\n                    val4, getOpcodeName(val4)\n            );\n            System.out.println(line);\n        }\n    }\n\n    public static String getOpcodeName(int i) {\n        if (i < 0 || i >= OpcodeConst.OPCODE_NAMES_LENGTH) {\n            return \"\";\n        }\n        if (i < start || i >= stop) {\n            return \"\";\n        }\n        String opcodeName = OpcodeConst.getOpcodeName(i);\n        if (OpcodeConst.ILLEGAL_OPCODE.equals(opcodeName)) {\n            opcodeName = \"\";\n        }\n        return opcodeName;\n    }\n}\n"
  },
  {
    "path": "src/main/java/run/jar/CompareTwoJarFile.java",
    "content": "package run.jar;\n\nimport lsieun.utils.FileUtils;\nimport lsieun.utils.JarUtils;\nimport org.objectweb.asm.ClassReader;\nimport org.objectweb.asm.ClassVisitor;\nimport org.objectweb.asm.util.Printer;\nimport org.objectweb.asm.util.Textifier;\nimport org.objectweb.asm.util.TraceClassVisitor;\n\nimport java.io.ByteArrayOutputStream;\nimport java.io.PrintWriter;\nimport java.util.*;\n\npublic class CompareTwoJarFile {\n    // TODO: 应该先比较两个文件内容是否相等，使用 Files.mismatch，或使用 byte[] 比对\n    public static void main(String[] args) {\n        String jarPath1 = \"D:/git-repo/learn-java-asm/first.jar\";\n        String jarPath2 = \"D:/git-repo/learn-java-asm/second.jar\";\n\n        List<String> list1 = JarUtils.getAllEntries(jarPath1);\n        List<String> list2 = JarUtils.getAllEntries(jarPath2);\n        Set<String> set = new HashSet<>();\n        set.addAll(list1);\n        set.addAll(list2);\n        String message = String.format(\"total: %s, list1=%s, list2=%s\", set.size(), list1.size(), list2.size());\n        System.out.println(message);\n\n        Map<String, ByteArrayOutputStream> map1 = JarUtils.getAllEntryMap(jarPath1, list1);\n        Map<String, ByteArrayOutputStream> map2 = JarUtils.getAllEntryMap(jarPath2, list2);\n\n        for (String item : set) {\n            // 第一种情况，路径，不处理\n            if (item.endsWith(\"/\")) continue;\n\n            byte[] bytes1 = null;\n            byte[] bytes2 = null;\n\n            ByteArrayOutputStream bao1 = map1.get(item);\n            ByteArrayOutputStream bao2 = map2.get(item);\n\n            if (bao1 != null) {\n                bytes1 = bao1.toByteArray();\n            }\n            if (bao2 != null) {\n                bytes2 = bao2.toByteArray();\n            }\n\n            // 第二种情况，相等，不处理\n            boolean equals = Arrays.equals(bytes1, bytes2);\n            if (equals) continue;\n\n            // 第三种情况，不相等，进行输出\n            if (!item.endsWith(\".class\")) {\n                generateOtherFile(bytes1, item, \"a\");\n                generateOtherFile(bytes2, item, \"b\");\n            }\n            else {\n                generateClassText(bytes1, \"a\");\n                generateClassText(bytes2, \"b\");\n            }\n        }\n    }\n\n    public static void generateOtherFile(byte[] bytes, String item, String middle) {\n        if (bytes == null || bytes.length == 0) return;\n\n        int lastIndex = item.lastIndexOf(\".\");\n\n        String prefix;\n        String suffix;\n        if (lastIndex != -1) {\n            prefix = item.substring(0, lastIndex);\n            suffix = item.substring(lastIndex);\n        }\n        else {\n            prefix = item;\n            suffix = \"\";\n        }\n\n        String newItem = prefix + middle + suffix;\n        String filepath = FileUtils.getFilePath(newItem);\n        FileUtils.writeBytes(filepath, bytes);\n    }\n\n    public static void generateClassText(byte[] bytes, String suffix) {\n        if (bytes == null || bytes.length == 0) return;\n\n        // 第一步，创建ClassReader\n        ClassReader cr = new ClassReader(bytes);\n        String className = cr.getClassName();\n        String filepath = FileUtils.getFilePath(className + \"-\" + suffix + \".txt\");\n\n        // 第二步，创建ClassVisitor\n        ByteArrayOutputStream bao = new ByteArrayOutputStream();\n        Printer printer = new Textifier();\n        PrintWriter printWriter = new PrintWriter(bao, true);\n        ClassVisitor cv = new TraceClassVisitor(null, printer, printWriter);\n\n        // 第三步，连接ClassReader和ClassVisitor\n        int parsingOptions = ClassReader.SKIP_DEBUG | ClassReader.SKIP_FRAMES;\n        cr.accept(cv, parsingOptions);\n\n        // 第四步，输出到文件\n        FileUtils.writeBytes(filepath, bao.toByteArray());\n    }\n}\n"
  },
  {
    "path": "src/main/java/run/jar/FindJarClassItem.java",
    "content": "package run.jar;\n\nimport lsieun.utils.JarUtils;\nimport org.objectweb.asm.ClassReader;\nimport org.objectweb.asm.tree.ClassNode;\nimport org.objectweb.asm.tree.MethodNode;\n\nimport java.io.ByteArrayOutputStream;\nimport java.util.List;\nimport java.util.Map;\n\npublic class FindJarClassItem {\n    public static void main(String[] args) {\n        String jarPath = \"D:/git-repo/learn-java-asm/someJar.jar\";\n        List<String> list = JarUtils.getClassEntries(jarPath);\n        Map<String, ByteArrayOutputStream> map = JarUtils.getAllEntryMap(jarPath, list);\n        for (Map.Entry<String, ByteArrayOutputStream> entry : map.entrySet()) {\n            String item = entry.getKey();\n            ByteArrayOutputStream bao = entry.getValue();\n            byte[] bytes = bao.toByteArray();\n\n            ClassReader cr = new ClassReader(bytes);\n            ClassNode cn = new ClassNode();\n            cr.accept(cn, ClassReader.SKIP_FRAMES | ClassReader.SKIP_DEBUG);\n\n            List<MethodNode> methods = cn.methods;\n            for (MethodNode mn : methods) {\n                String methodName = mn.name;\n                String methodDesc = mn.desc;\n                if (methodDesc.endsWith(\"Ljava/security/PublicKey;\")) {\n                    System.out.println(item + \".\" + methodName + \":\" + methodDesc);\n                }\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "src/main/java/sample/HelloWorld.java",
    "content": "package sample;\n\nimport java.util.function.Consumer;\n\npublic class HelloWorld {\n    public static void main(String[] args) {\n        Consumer<String> c = System.out::println;\n        c.accept(\"Hello World\");\n    }\n}"
  },
  {
    "path": "src/main/java/sample/ParameterUtils.java",
    "content": "package sample;\n\nimport java.io.PrintStream;\nimport java.text.SimpleDateFormat;\nimport java.util.Arrays;\nimport java.util.Date;\n\npublic class ParameterUtils {\n    private static final ThreadLocal<SimpleDateFormat> formatter = ThreadLocal.withInitial(\n            () -> new SimpleDateFormat(\"yyyy-MM-dd HH:mm:ss\")\n    );\n\n    public static void printValueOnStack(boolean value) {\n        System.out.println(\"    \" + value);\n    }\n\n    public static void printValueOnStack(byte value) {\n        System.out.println(\"    \" + value);\n    }\n\n    public static void printValueOnStack(char value) {\n        System.out.println(\"    \" + value);\n    }\n\n    public static void printValueOnStack(short value) {\n        System.out.println(\"    \" + value);\n    }\n\n    public static void printValueOnStack(int value) {\n        System.out.println(\"    \" + value);\n    }\n\n    public static void printValueOnStack(float value) {\n        System.out.println(\"    \" + value);\n    }\n\n    public static void printValueOnStack(long value) {\n        System.out.println(\"    \" + value);\n    }\n\n    public static void printValueOnStack(double value) {\n        System.out.println(\"    \" + value);\n    }\n\n    public static void printValueOnStack(Object value) {\n        if (value == null) {\n            System.out.println(\"    \" + value);\n        } else if (value instanceof String) {\n            System.out.println(\"    \" + value);\n        } else if (value instanceof Date) {\n            System.out.println(\"    \" + formatter.get().format(value));\n        } else if (value instanceof char[]) {\n            System.out.println(\"    \" + Arrays.toString((char[]) value));\n        } else {\n            System.out.println(\"    \" + value.getClass() + \": \" + value.toString());\n        }\n    }\n\n    public static void printText(String str) {\n        System.out.println(str);\n    }\n\n    public static void output(PrintStream printStream, int val) {\n        printStream.println(\"ParameterUtils: \" + val);\n    }\n}\n"
  }
]