├── .gitignore ├── .idea ├── .gitignore ├── libraries │ ├── asm_9_2.xml │ └── dexmaker_2_28_1.xml ├── misc.xml ├── modules.xml └── vcs.xml ├── DMifierPlugin.iml ├── DMifierPlugin.zip ├── LICENSE ├── README.md ├── libs ├── asm-9.2.jar ├── asm-commons-9.2.jar ├── asm-util-9.2.jar ├── dalvik-dx-9.0.0_r3.jar └── dexmaker-2.28.1.jar ├── previews ├── 1.png ├── 2.png └── 3.png ├── resources └── META-INF │ └── plugin.xml └── src └── com └── wuyr └── dmifier ├── actions └── ViewCodeAction.kt ├── core └── DMifier.kt └── utils └── ReflectUtil.kt /.gitignore: -------------------------------------------------------------------------------- 1 | out/ 2 | -------------------------------------------------------------------------------- /.idea/.gitignore: -------------------------------------------------------------------------------- 1 | # Default ignored files 2 | /shelf/ 3 | /workspace.xml 4 | -------------------------------------------------------------------------------- /.idea/libraries/asm_9_2.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /.idea/libraries/dexmaker_2_28_1.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /.idea/misc.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /.idea/modules.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /.idea/vcs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /DMifierPlugin.iml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /DMifierPlugin.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wuyr/DMifier/db8f7155f40c18f09e901d529b6ade29f7ec6e2c/DMifierPlugin.zip -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "[]" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright [yyyy] [name of copyright owner] 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## 一款能将Java源码转成[dexmaker](https://github.com/linkedin/dexmaker)代码的IDEA插件,方便开发和测试 2 | 3 |
4 | 5 | ### 插件起源: 6 | 去年年底在wanandroid每日一问:[如何构造一个 hide interface 的实现类?](https://www.wanandroid.com/wenda/show/20867)问题中,***残页***同学的回答说,可以通过stub + compileOnly的方式用动态代理实现访问权限是package的接口。 7 | 8 | 我当时就想:用stub+动态代理还是有点麻烦,不是有个叫dexmaker的库可以在运行时动态生成dex吗?不知道能不能直接实现一个hidden的interface? 9 | 10 | 但我此前又没用过dexmaker,不知道代码要怎么写,觉得挺复杂的。 于是网上搜了一下生成dexmaker代码的工具,发现不但没有,就连dexmaker的相关文章也寥寥无几。 11 | 12 | 看了官方文档生成Fibonacci class的例子之后,忽然想到:这些代码都是有固定的规则和逻辑,那应该能做成一个自动生成的插件啊(实在不想自己动手写dexmaker代码)!之前不是有个IDEA插件可以生成ASM代码嘛? 13 | 14 | 于是反编译了那个*ASM Bytecode Viewer*插件,发现它的核心部分是直接使用ASM提供的jar包来实现的!原来ASM就有提供生成ASM代码的api!dexmaker为什么你就不提供一下这个东西啊!哈哈哈 15 | 16 | 接着又看了一下这个ASM的jar包,发现核心生成类***ASMifier***(这也是本项目的名字来源,它叫***ASMifier***,我就叫***D***(ex)***M***(aker)***ifier***吧,哈哈)继承了Printer,Printer结合ClassReader可以把class文件的指令都读取出来…… 17 | 18 |
19 | 20 | ### 实现原理: 21 | 参照了ASMifier的做法,也弄了一个继承自Printer的类,并在重写的`visitMethod`、`visitInsn`等方法里按照dexmaker的规则去输出dexmaker代码…… 22 | 23 |
24 | 25 | ### 使用方式: 26 | 插件安装后,在编辑器中右键菜单下方可看到 “View DexMaker Code” 选项: 27 | 28 | ![preview](https://github.com/wuyr/DMifier/raw/main/previews/1.png) 29 | 30 | 或在主菜单 *View* 底部点击: 31 | 32 | ![preview](https://github.com/wuyr/DMifier/raw/main/previews/2.png) 33 | 34 | 成功转换后会以Dialog的形式把结果弹出,直接复制到自己的代码里即可: 35 | 36 | ![preview](https://github.com/wuyr/DMifier/raw/main/previews/3.png) 37 | 38 |
39 | 40 | ### 安装: 41 | **在线安装:** 42 | 43 | 插件还在不断完善中,暂时未上传到JetBrain marketplace上。 44 | 45 | **本地安装:** 46 | 47 | 到 [releases](https://github.com/wuyr/DMifier/releases) 里下载最新的 *DMifierPlugin.zip* 后拖把它拖进IDE中并重启。 48 | 49 |
50 | 51 | ### 开发进度: 52 | **未实现的指令:** 53 | 54 | *NOP*、 55 | 56 | *POP2*、 57 | 58 | *DUP_X1*、 59 | 60 | *DUP_X2*、 61 | 62 | *DUP2*、 63 | 64 | *DUP2_X1*、 65 | 66 | *DUP2_X2*、 67 | 68 | *SWAP*、 69 | 70 | *JSR*、 71 | 72 | *MULTIANEWARRAY*(创建多维数组)、 73 | 74 | *MONITORENTER*、*MONITOREXIT*(synchronized语句)、 75 | 76 | (try catch语句,dexmaker没有提供moveException方法) 77 | 78 |
79 | 80 | **不支持的指令:** 81 | 82 | *INVOKEDYNAMIC*(Dalvik 字节码没有对应的指令)、 83 | 84 | *TABLESWITCH*、*LOOKUPSWITCH*(dexmaker没有提供相关api) 85 | 86 |
87 | 88 | **坑:** 89 | 90 | 对了,这里还有个dexmaker的坑,就是if语句 `if(a >= b)`,当a=0时,在生成dex之后,a和b的顺序会反过来,也就是变成了 `if(b >= 0)`。 提了issue一周没人回答,才发现dexmaker在去年2月份就停止了更新!坑啊!!! 91 | 92 |
93 | 94 | **后续计划:** 95 | - 优化转换结果显示方式; 96 | 97 | - 加入直接查看生成的dex文件的smali以及转成class后的代码; 98 | 99 |
100 | 101 | ### 更新日志: 102 | 103 | - **0.1** 提供基本转换功能。 104 | -------------------------------------------------------------------------------- /libs/asm-9.2.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wuyr/DMifier/db8f7155f40c18f09e901d529b6ade29f7ec6e2c/libs/asm-9.2.jar -------------------------------------------------------------------------------- /libs/asm-commons-9.2.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wuyr/DMifier/db8f7155f40c18f09e901d529b6ade29f7ec6e2c/libs/asm-commons-9.2.jar -------------------------------------------------------------------------------- /libs/asm-util-9.2.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wuyr/DMifier/db8f7155f40c18f09e901d529b6ade29f7ec6e2c/libs/asm-util-9.2.jar -------------------------------------------------------------------------------- /libs/dalvik-dx-9.0.0_r3.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wuyr/DMifier/db8f7155f40c18f09e901d529b6ade29f7ec6e2c/libs/dalvik-dx-9.0.0_r3.jar -------------------------------------------------------------------------------- /libs/dexmaker-2.28.1.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wuyr/DMifier/db8f7155f40c18f09e901d529b6ade29f7ec6e2c/libs/dexmaker-2.28.1.jar -------------------------------------------------------------------------------- /previews/1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wuyr/DMifier/db8f7155f40c18f09e901d529b6ade29f7ec6e2c/previews/1.png -------------------------------------------------------------------------------- /previews/2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wuyr/DMifier/db8f7155f40c18f09e901d529b6ade29f7ec6e2c/previews/2.png -------------------------------------------------------------------------------- /previews/3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wuyr/DMifier/db8f7155f40c18f09e901d529b6ade29f7ec6e2c/previews/3.png -------------------------------------------------------------------------------- /resources/META-INF/plugin.xml: -------------------------------------------------------------------------------- 1 | 2 | com.wuyr.dmifier 3 | DMifier 4 | 0.1 5 | 陈小缘 6 | 7 | 适用于Android项目的IDEA插件,用来自动生成DexMaker代码,类似『ASM Bytecode Viewer』

9 |
10 | ]]>
11 | 12 |
  • 0.1 功能完善中
  • 14 |
    15 | ]]> 16 |
    17 | 18 | 19 | 20 | 21 | 23 | com.intellij.modules.platform 24 | 25 | 26 | 27 | 28 | 29 | 30 | 34 | 35 | 36 | 37 | 38 | 39 |
    -------------------------------------------------------------------------------- /src/com/wuyr/dmifier/actions/ViewCodeAction.kt: -------------------------------------------------------------------------------- 1 | package com.wuyr.dmifier.actions 2 | 3 | import com.intellij.openapi.actionSystem.AnAction 4 | import com.intellij.openapi.actionSystem.AnActionEvent 5 | import com.intellij.openapi.actionSystem.LangDataKeys 6 | import com.intellij.openapi.actionSystem.PlatformDataKeys 7 | import com.intellij.openapi.module.Module 8 | import com.intellij.openapi.project.Project 9 | import com.intellij.openapi.roots.* 10 | import com.intellij.openapi.ui.Messages 11 | import com.intellij.openapi.vfs.VirtualFile 12 | import com.intellij.openapi.vfs.VirtualFileManager 13 | import com.intellij.psi.PsiManager 14 | import com.intellij.task.ProjectTaskManager 15 | import com.intellij.util.io.URLUtil 16 | import com.wuyr.dmifier.core.DMifier 17 | import com.wuyr.dmifier.utils.invoke 18 | import org.objectweb.asm.ClassReader 19 | import org.objectweb.asm.util.TraceClassVisitor 20 | import java.io.File 21 | import java.io.FileInputStream 22 | import java.io.PrintWriter 23 | import java.io.StringWriter 24 | import java.nio.file.Paths 25 | 26 | class ViewCodeAction : AnAction() { 27 | 28 | override fun update(e: AnActionEvent) { 29 | e.presentation.isEnabled = e.project?.let { project -> 30 | e.getData(PlatformDataKeys.VIRTUAL_FILE)?.let { file -> 31 | PsiManager.getInstance(project).findFile(file)?.fileType?.name == "JAVA" 32 | } 33 | } ?: false 34 | } 35 | 36 | override fun actionPerformed(e: AnActionEvent) { 37 | e.project?.compileAndShow(e.getData(LangDataKeys.PSI_FILE)?.virtualFile ?: return) 38 | } 39 | 40 | private fun Project.compileAndShow(target: VirtualFile) { 41 | ProjectTaskManager.getInstance(this).compile(target).onSuccess { result -> 42 | if (!result.hasErrors()) { 43 | val file = PsiManager.getInstance(this).findFile(target) ?: return@onSuccess 44 | val packageName = file::class.invoke(file, "getPackageName")!! 45 | this.findClassFromOutputDirectories(target, packageName)?.let { outputFile -> 46 | Messages.showMessageDialog(outputFile.convertToDexMakerCode(), "DexMaker", null) 47 | } 48 | } 49 | } 50 | } 51 | 52 | private fun VirtualFile.convertToDexMakerCode() = StringWriter(2048).use { sw -> 53 | runCatching { 54 | ClassReader(FileInputStream(path)).accept(TraceClassVisitor(null, DMifier(), PrintWriter(sw)), 0) 55 | }.getOrElse { 56 | it.printStackTrace(PrintWriter(sw)) 57 | } 58 | sw.toString() 59 | } 60 | 61 | private fun Project.findClassFromOutputDirectories(target: VirtualFile, packageName: String): VirtualFile? { 62 | ProjectRootManager.getInstance(this).fileIndex.getModuleForFile(target)?.let { module -> 63 | module.getOutputDirectories(module.getModuleScope(false).contains(target)).forEach { outputDir -> 64 | outputDir?.let { dir -> 65 | VirtualFileManager.getInstance().getFileSystem(URLUtil.FILE_PROTOCOL).refreshAndFindFileByPath( 66 | Paths.get(dir, packageName, "${target.nameWithoutExtension}.class").toString() 67 | )?.also { outputFile -> 68 | outputFile.refresh(false, false) 69 | return outputFile 70 | } 71 | } 72 | } 73 | } 74 | return null 75 | } 76 | 77 | private fun Module.getOutputDirectories(isRelease: Boolean) = ArrayList().also { result -> 78 | CompilerModuleExtension.getInstance(this)?.let { moduleExtension -> 79 | CompilerProjectExtension.getInstance(project)?.let { projectExtension -> 80 | (if (isRelease) moduleExtension.compilerOutputPath else moduleExtension.compilerOutputPathForTests) 81 | ?.let { result.add(it.path) } ?: projectExtension.compilerOutput?.let { result.add(it.path) } 82 | OrderEnumerationHandler.EP_NAME.extensions.forEach { factory -> 83 | if (factory.isApplicable(this)) { 84 | ArrayList().also { urls -> 85 | @Suppress("OverrideOnly") 86 | factory.createHandler(this).addCustomModuleRoots( 87 | OrderRootType.CLASSES, ModuleRootManager.getInstance(this), urls, isRelease, !isRelease 88 | ) 89 | }.forEach { url -> 90 | result.add(VirtualFileManager.extractPath(url).replace('/', File.separatorChar)) 91 | } 92 | } 93 | } 94 | } 95 | } 96 | } 97 | } -------------------------------------------------------------------------------- /src/com/wuyr/dmifier/core/DMifier.kt: -------------------------------------------------------------------------------- 1 | package com.wuyr.dmifier.core 2 | 3 | import org.objectweb.asm.* 4 | import org.objectweb.asm.util.Printer 5 | import java.lang.reflect.Modifier 6 | import java.util.* 7 | 8 | class DMifier : Printer(589824) { 9 | 10 | private fun StringBuilder.newLine() = append("\n") 11 | 12 | private var superTypeId = "" 13 | 14 | override fun visit( 15 | version: Int, access: Int, name: String?, signature: String?, superName: String, interfaces: Array 16 | ) { 17 | appendCodeBlockAndAdd { 18 | append("TypeId classId = ") 19 | append(name?.typeId) 20 | append(";") 21 | newLine() 22 | append("String fileName = \"") 23 | val simpleName = name?.run { 24 | val lastSlashIndex = lastIndexOf('/') 25 | if (lastSlashIndex == -1) name 26 | else substring(lastSlashIndex + 1).replace("[-()]".toRegex(), "_") 27 | } ?: "DexMakerClass" 28 | append(simpleName) 29 | append(".generated\";") 30 | newLine() 31 | append("DexMaker dexMaker = new DexMaker();") 32 | newLine() 33 | append("TypeId[] interfacesTypes = new TypeId[") 34 | append(interfaces.size) 35 | append("];") 36 | newLine() 37 | interfaces.forEachIndexed { index, content -> 38 | append("interfacesTypes[") 39 | append(index) 40 | append("] = ") 41 | append(content.typeId) 42 | append(";") 43 | newLine() 44 | } 45 | append("dexMaker.declare(classId, fileName, ") 46 | append((access or ACCESS_CLASS).accessFlag) 47 | append(", ") 48 | superTypeId = superName.typeId 49 | append(superTypeId) 50 | append(", interfacesTypes);") 51 | newLine() 52 | } 53 | } 54 | 55 | override fun visit(name: String?, value: Any?) { 56 | appendCodeBlockAndAdd { 57 | append("TypeId classId = ") 58 | append(name?.typeId) 59 | append(";") 60 | newLine() 61 | append("String fileName = \"") 62 | val simpleName = name?.run { 63 | val lastSlashIndex = lastIndexOf('/') 64 | if (lastSlashIndex == -1) name 65 | else substring(lastSlashIndex + 1).replace("[-()]".toRegex(), "_") 66 | } ?: "DexMakerClass" 67 | append(simpleName) 68 | append(".generated\";") 69 | newLine() 70 | append("DexMaker dexMaker = new DexMaker();") 71 | newLine() 72 | append("dexMaker.declare(classId, fileName, ") 73 | append("Modifier.PUBLIC") 74 | append(");") 75 | newLine() 76 | } 77 | } 78 | 79 | private val Int.accessFlag: String 80 | get() = StringBuilder().also { sb -> 81 | if (this == 0) { 82 | sb.append("0") 83 | } else { 84 | if ((this and Modifier.PUBLIC) != 0) sb.append("Modifier.PUBLIC | ") 85 | if ((this and Modifier.PRIVATE) != 0) sb.append("Modifier.PRIVATE | ") 86 | if ((this and Modifier.PROTECTED) != 0) sb.append("Modifier.PROTECTED | ") 87 | if ((this and Modifier.STATIC) != 0) sb.append("Modifier.STATIC | ") 88 | if ((this and Modifier.FINAL) != 0) sb.append("Modifier.FINAL | ") 89 | if ((this and Modifier.SYNCHRONIZED) != 0 && this and ACCESS_CLASS == 0) sb.append("Modifier.SYNCHRONIZED | ") 90 | if ((this and Modifier.VOLATILE) != 0) sb.append("Modifier.VOLATILE | ") 91 | if ((this and Modifier.TRANSIENT) != 0 && this and ACCESS_FIELD != 0) sb.append("Modifier.TRANSIENT | ") 92 | if ((this and Modifier.NATIVE) != 0 && this and (ACCESS_CLASS or ACCESS_FIELD) == 0) sb.append("Modifier.NATIVE | ") 93 | if ((this and Modifier.INTERFACE) != 0) sb.append("Modifier.INTERFACE | ") 94 | if ((this and Modifier.ABSTRACT) != 0) sb.append("Modifier.ABSTRACT | ") 95 | if ((this and Modifier.STRICT) != 0) sb.append("Modifier.STRICT | ") 96 | if (sb.isEmpty()) { 97 | sb.append("0") 98 | } else { 99 | sb.deleteCharAt(sb.lastIndex) 100 | sb.deleteCharAt(sb.lastIndex) 101 | sb.deleteCharAt(sb.lastIndex) 102 | } 103 | } 104 | }.toString() 105 | 106 | override fun visitSource(source: String?, debug: String?) { 107 | 108 | } 109 | 110 | override fun visitOuterClass(owner: String?, name: String?, descriptor: String?) { 111 | 112 | } 113 | 114 | override fun visitClassAnnotation(descriptor: String?, visible: Boolean): Printer { 115 | 116 | return this 117 | } 118 | 119 | override fun visitClassAttribute(attribute: Attribute?) { 120 | 121 | } 122 | 123 | override fun visitInnerClass(name: String?, outerName: String?, innerName: String?, access: Int) { 124 | 125 | } 126 | 127 | override fun visitField(access: Int, name: String, descriptor: String, signature: String?, value: Any?): Printer { 128 | appendCodeBlockAndAdd { 129 | append("dexMaker.declare(classId.getField(") 130 | append(descriptor.typeId) 131 | append(", \"") 132 | append(name) 133 | append("\"), ") 134 | append((access or ACCESS_FIELD).accessFlag) 135 | append(", ") 136 | append(value) 137 | append(");") 138 | } 139 | return this 140 | } 141 | 142 | private var currentMethodParameters = emptyList() 143 | private var currentMethodReturnType = "" 144 | private var isStaticMethod = false 145 | 146 | override fun visitMethod( 147 | access: Int, name: String, descriptor: String, signature: String?, exceptions: Array? 148 | ): Printer { 149 | appendCodeBlockAndAdd { 150 | newLine() 151 | append("{") 152 | newLine() 153 | val parameters = descriptor.getParameterTypes() 154 | isStaticMethod = (access and Modifier.STATIC) != 0 155 | currentMethodParameters = parameters 156 | currentMethodReturnType = descriptor.getReturnType().typeId 157 | localCount = currentMethodParameters.size + if (isStaticMethod) 0 else 1 158 | append("MethodId methodId = classId.") 159 | when (name) { 160 | "" -> { 161 | append("getConstructor(") 162 | if (parameters.isNotEmpty()) { 163 | parameters.forEach { 164 | append(it.typeId) 165 | append(", ") 166 | } 167 | deleteCharAt(lastIndex) 168 | deleteCharAt(lastIndex) 169 | } 170 | append(");") 171 | } 172 | "" -> { 173 | append("getStaticInitializer();") 174 | } 175 | else -> { 176 | append("getMethod(") 177 | append(currentMethodReturnType) 178 | append(", \"") 179 | append(name) 180 | if (parameters.isEmpty()) { 181 | append("\"") 182 | } else { 183 | append("\", ") 184 | parameters.forEach { 185 | append(it.typeId) 186 | append(", ") 187 | } 188 | deleteCharAt(lastIndex) 189 | deleteCharAt(lastIndex) 190 | } 191 | append(");") 192 | } 193 | } 194 | newLine() 195 | append("Code methodCodeBlock = dexMaker.declare(methodId, ") 196 | append(access.accessFlag) 197 | append(");") 198 | newLine() 199 | } 200 | return this 201 | } 202 | 203 | private fun String.getReturnType() = substring(lastIndexOf(')') + 1) 204 | 205 | private fun String.getParameterTypes() = substring(1, lastIndexOf(')')).run { 206 | ArrayList().apply { 207 | split(";").forEach { type -> 208 | if (type.isNotEmpty()) { 209 | if (type.contains('[') && type.indexOf('[') != type.lastIndexOf('[')) { 210 | type.split("[").forEach { 211 | if (it.isNotEmpty()) { 212 | addParameter("[$it") 213 | } 214 | } 215 | } else { 216 | addParameter(type) 217 | } 218 | } 219 | } 220 | removeIf { it.isEmpty() } 221 | } 222 | } 223 | 224 | private fun ArrayList.addParameter(type: String) { 225 | if (type.startsWith('L') || type.startsWith("[L")) { 226 | add(type) 227 | } else { 228 | val index = type.indexOf(if (type.contains('[')) '[' else 'L') 229 | if (index == 0 && type.length >= 2) { 230 | add(type.substring(0, 2)) 231 | add(type.substring(2, type.length)) 232 | } else if (index > -1) { 233 | type.substring(0, index).forEach { c -> add(c.toString()) } 234 | add(type.substring(index, type.length)) 235 | } else { 236 | type.forEach { c -> add(c.toString()) } 237 | } 238 | } 239 | } 240 | 241 | private val String.typeId: String 242 | get() = when (this) { 243 | "Z" -> "TypeId.BOOLEAN" 244 | "C" -> "TypeId.CHAR" 245 | "F" -> "TypeId.FLOAT" 246 | "D" -> "TypeId.DOUBLE" 247 | "B" -> "TypeId.BYTE" 248 | "S" -> "TypeId.SHORT" 249 | "I" -> "TypeId.INT" 250 | "J" -> "TypeId.LONG" 251 | "V" -> "TypeId.VOID" 252 | else -> StringBuilder().also { sb -> 253 | sb.append("TypeId.get(\"") 254 | if ((!startsWith('L')) && !startsWith('[')) sb.append('L') 255 | sb.append(this) 256 | if (!endsWith(';') && length != 2) sb.append(';') 257 | sb.append("\")") 258 | }.toString() 259 | } 260 | 261 | private fun String.getMethodId(name: String, returnType: String, parameterTypes: List) = appendCodeBlock { 262 | append(typeId) 263 | append(".getMethod(") 264 | append(returnType.typeId) 265 | append(", \"") 266 | append(name) 267 | if (parameterTypes.isEmpty()) { 268 | append("\")") 269 | } else { 270 | append("\", ") 271 | append(parameterTypes.joinToString { it.typeId }) 272 | append(")") 273 | } 274 | } 275 | 276 | private fun String.getFieldId(type: String, name: String) = appendCodeBlock { 277 | append(typeId) 278 | append(".getField(") 279 | append(type.typeId) 280 | append(", \"") 281 | append(name) 282 | append("\")") 283 | } 284 | 285 | override fun visitClassEnd() { 286 | 287 | } 288 | 289 | override fun visitEnum(name: String?, descriptor: String?, value: String?) { 290 | 291 | } 292 | 293 | override fun visitAnnotation(name: String?, descriptor: String?): Printer { 294 | 295 | return this 296 | } 297 | 298 | override fun visitArray(name: String?): Printer { 299 | 300 | return this 301 | } 302 | 303 | override fun visitAnnotationEnd() { 304 | 305 | } 306 | 307 | override fun visitFieldAnnotation(descriptor: String?, visible: Boolean): Printer { 308 | 309 | return this 310 | } 311 | 312 | override fun visitFieldAttribute(attribute: Attribute?) { 313 | 314 | } 315 | 316 | override fun visitFieldEnd() { 317 | 318 | } 319 | 320 | override fun visitAnnotationDefault(): Printer { 321 | 322 | return this 323 | } 324 | 325 | override fun visitMethodAnnotation(descriptor: String?, visible: Boolean): Printer { 326 | return this 327 | } 328 | 329 | override fun visitParameterAnnotation(parameter: Int, descriptor: String?, visible: Boolean): Printer { 330 | return this 331 | } 332 | 333 | override fun visitMethodAttribute(attribute: Attribute?) { 334 | 335 | } 336 | 337 | override fun visitCode() { 338 | 339 | } 340 | 341 | override fun visitFrame(type: Int, numLocal: Int, local: Array?, numStack: Int, stack: Array?) { 342 | 343 | } 344 | 345 | private var operationStringBuilder = StringBuilder() 346 | private var declarationStringBuilder = StringBuilder() 347 | 348 | private fun appendDeclarationCodeBlock(block: StringBuilder.() -> Unit) { 349 | declarationStringBuilder.apply { 350 | block() 351 | newLine() 352 | } 353 | } 354 | 355 | private fun appendOperationCodeBlock(appendNewLine: Boolean = true, block: StringBuilder.() -> Unit) { 356 | operationStringBuilder.apply { 357 | block() 358 | if (appendNewLine) { 359 | newLine() 360 | } 361 | } 362 | } 363 | 364 | private fun flushTempCodeBlock() { 365 | declarationStringBuilder.newLine() 366 | text.add(declarationStringBuilder.toString()) 367 | text.add(operationStringBuilder.toString()) 368 | declarationStringBuilder.setLength(0) 369 | operationStringBuilder.setLength(0) 370 | } 371 | 372 | // localName : typeId 373 | private val stack = LinkedList>() 374 | 375 | private var localCount = 0 376 | private var tempLocalCount = 0 377 | private val localNames = HashMap>() 378 | 379 | private fun newLocalName(typeId: String, store: Boolean = false, conflictLocal: String? = null) = 380 | if (store && conflictLocal != null) { 381 | var currentConflictLocal = conflictLocal!! 382 | do { 383 | currentConflictLocal = "${currentConflictLocal}_append" 384 | } while (localNames.containsKey(currentConflictLocal)) 385 | localNames[currentConflictLocal] = currentConflictLocal to typeId 386 | currentConflictLocal 387 | } else (if (store) "local${localCount++}" else "tempLocal${tempLocalCount++}").also { name -> 388 | if (store) { 389 | if (typeId == "TypeId.LONG" || typeId == "TypeId.DOUBLE") { 390 | localCount++ 391 | } 392 | localNames[name] = name to typeId 393 | } 394 | } 395 | 396 | private fun getLocalName(index: Int): Pair? { 397 | var localName = "local$index" 398 | while (localNames.containsKey("${localName}_append")) { 399 | localName = "${localName}_append" 400 | } 401 | return localNames[localName] 402 | } 403 | 404 | private fun cast(targetType: String) { 405 | val typeId = targetType.typeId 406 | val output = newLocalName(typeId) 407 | appendDeclarationCodeBlock { 408 | append("Local ") 409 | append(output) 410 | append(" = methodCodeBlock.newLocal(") 411 | append(typeId) 412 | append(");") 413 | } 414 | appendOperationCodeBlock { 415 | append("methodCodeBlock.cast(") 416 | append(output) 417 | append(", ") 418 | append(stack.pop().first) 419 | append(");") 420 | } 421 | stack.push(output to typeId) 422 | } 423 | 424 | private fun loadConstant(type: String, value: Any?) { 425 | val typeId = type.typeId 426 | val output = newLocalName(typeId) 427 | appendDeclarationCodeBlock { 428 | append("Local ") 429 | append(output) 430 | append(" = methodCodeBlock.newLocal(") 431 | append(typeId) 432 | append(");") 433 | } 434 | appendOperationCodeBlock { 435 | append("methodCodeBlock.loadConstant(") 436 | append(output) 437 | append(", ") 438 | append(value) 439 | append(");") 440 | } 441 | stack.push(output to typeId) 442 | } 443 | 444 | override fun visitInsn(opcode: Int) { 445 | when (opcode) { 446 | Opcodes.ACONST_NULL -> loadConstant("java/lang/Object", null) 447 | Opcodes.ICONST_M1 -> loadConstant("I", -1) 448 | Opcodes.ICONST_0 -> loadConstant("I", 0) 449 | Opcodes.ICONST_1 -> loadConstant("I", 1) 450 | Opcodes.ICONST_2 -> loadConstant("I", 2) 451 | Opcodes.ICONST_3 -> loadConstant("I", 3) 452 | Opcodes.ICONST_4 -> loadConstant("I", 4) 453 | Opcodes.ICONST_5 -> loadConstant("I", 5) 454 | Opcodes.LCONST_0 -> loadConstant("J", 0) 455 | Opcodes.LCONST_1 -> loadConstant("J", 1) 456 | Opcodes.FCONST_0 -> loadConstant("F", 0) 457 | Opcodes.FCONST_1 -> loadConstant("F", 1) 458 | Opcodes.FCONST_2 -> loadConstant("F", 2) 459 | Opcodes.DCONST_0 -> loadConstant("D", 0) 460 | Opcodes.DCONST_1 -> loadConstant("D", 1) 461 | 462 | Opcodes.IALOAD, Opcodes.LALOAD, Opcodes.FALOAD, Opcodes.DALOAD, 463 | Opcodes.AALOAD, Opcodes.BALOAD, Opcodes.CALOAD, Opcodes.SALOAD -> { 464 | val typeId = stack[1].second.replaceFirst("[", "") 465 | val output = newLocalName(typeId) 466 | appendDeclarationCodeBlock { 467 | append("Local ") 468 | append(output) 469 | append(" = methodCodeBlock.newLocal(") 470 | append(typeId) 471 | append(");") 472 | } 473 | appendOperationCodeBlock { 474 | append("methodCodeBlock.aget(") 475 | append(output) 476 | append(", ") 477 | append(stack[1].first) 478 | append(", ") 479 | append(stack[0].first) 480 | append(");") 481 | stack.pop() 482 | stack.pop() 483 | } 484 | stack.push(output to typeId) 485 | } 486 | 487 | Opcodes.IASTORE, Opcodes.LASTORE, Opcodes.FASTORE, Opcodes.DASTORE, 488 | Opcodes.AASTORE, Opcodes.BASTORE, Opcodes.CASTORE, Opcodes.SASTORE -> { 489 | appendOperationCodeBlock { 490 | append("methodCodeBlock.aput(") 491 | append(stack[2].first) 492 | append(", ") 493 | append(stack[1].first) 494 | append(", ") 495 | append(stack[0].first) 496 | append(");") 497 | stack.pop() 498 | stack.pop() 499 | stack.pop() 500 | } 501 | } 502 | 503 | Opcodes.ARRAYLENGTH -> { 504 | val typeId = "TypeId.INT" 505 | val output = newLocalName(typeId) 506 | appendDeclarationCodeBlock { 507 | append("Local ") 508 | append(output) 509 | append(" = methodCodeBlock.newLocal(") 510 | append(typeId) 511 | append(");") 512 | } 513 | appendOperationCodeBlock { 514 | append("methodCodeBlock.arrayLength(") 515 | append(output) 516 | append(", ") 517 | append(stack.pop().first) 518 | append(");") 519 | } 520 | stack.push(output to typeId) 521 | } 522 | 523 | Opcodes.L2I, Opcodes.F2I, Opcodes.D2I -> cast("I") 524 | Opcodes.I2L, Opcodes.F2L, Opcodes.D2L -> cast("J") 525 | Opcodes.I2F, Opcodes.L2F, Opcodes.D2F -> cast("F") 526 | Opcodes.I2D, Opcodes.L2D, Opcodes.F2D -> cast("D") 527 | Opcodes.I2B -> cast("B") 528 | Opcodes.I2C -> cast("C") 529 | Opcodes.I2S -> cast("S") 530 | 531 | Opcodes.IADD -> binaryOp("I", "BinaryOp.ADD") 532 | Opcodes.LADD -> binaryOp("J", "BinaryOp.ADD") 533 | Opcodes.FADD -> binaryOp("F", "BinaryOp.ADD") 534 | Opcodes.DADD -> binaryOp("D", "BinaryOp.ADD") 535 | 536 | Opcodes.ISUB -> binaryOp("I", "BinaryOp.SUBTRACT") 537 | Opcodes.LSUB -> binaryOp("J", "BinaryOp.SUBTRACT") 538 | Opcodes.FSUB -> binaryOp("F", "BinaryOp.SUBTRACT") 539 | Opcodes.DSUB -> binaryOp("D", "BinaryOp.SUBTRACT") 540 | 541 | Opcodes.IMUL -> binaryOp("I", "BinaryOp.MULTIPLY") 542 | Opcodes.LMUL -> binaryOp("J", "BinaryOp.MULTIPLY") 543 | Opcodes.FMUL -> binaryOp("F", "BinaryOp.MULTIPLY") 544 | Opcodes.DMUL -> binaryOp("D", "BinaryOp.MULTIPLY") 545 | 546 | Opcodes.IDIV -> binaryOp("I", "BinaryOp.DIVIDE") 547 | Opcodes.LDIV -> binaryOp("J", "BinaryOp.DIVIDE") 548 | Opcodes.FDIV -> binaryOp("F", "BinaryOp.DIVIDE") 549 | Opcodes.DDIV -> binaryOp("D", "BinaryOp.DIVIDE") 550 | 551 | Opcodes.IREM -> binaryOp("I", "BinaryOp.REMAINDER") 552 | Opcodes.LREM -> binaryOp("J", "BinaryOp.REMAINDER") 553 | Opcodes.FREM -> binaryOp("F", "BinaryOp.REMAINDER") 554 | Opcodes.DREM -> binaryOp("D", "BinaryOp.REMAINDER") 555 | 556 | Opcodes.INEG -> binaryOp("I", "BinaryOp.NEGATE") 557 | Opcodes.LNEG -> binaryOp("J", "BinaryOp.NEGATE") 558 | Opcodes.FNEG -> binaryOp("F", "BinaryOp.NEGATE") 559 | Opcodes.DNEG -> binaryOp("D", "BinaryOp.NEGATE") 560 | 561 | Opcodes.ISHL -> binaryOp("I", "BinaryOp.SHIFT_LEFT") 562 | Opcodes.LSHL -> binaryOp("J", "BinaryOp.SHIFT_LEFT") 563 | 564 | Opcodes.ISHR -> binaryOp("I", "BinaryOp.SHIFT_RIGHT") 565 | Opcodes.LSHR -> binaryOp("J", "BinaryOp.SHIFT_RIGHT") 566 | 567 | Opcodes.IUSHR -> binaryOp("I", "BinaryOp.UNSIGNED_SHIFT_RIGHT") 568 | Opcodes.LUSHR -> binaryOp("J", "BinaryOp.UNSIGNED_SHIFT_RIGHT") 569 | 570 | Opcodes.IAND -> binaryOp("I", "BinaryOp.AND") 571 | Opcodes.LAND -> binaryOp("J", "BinaryOp.AND") 572 | 573 | Opcodes.IOR -> binaryOp("I", "BinaryOp.OR") 574 | Opcodes.LOR -> binaryOp("J", "BinaryOp.OR") 575 | 576 | Opcodes.IXOR -> binaryOp("I", "BinaryOp.XOR") 577 | Opcodes.LXOR -> binaryOp("J", "BinaryOp.XOR") 578 | 579 | Opcodes.LCMP -> { 580 | val typeId = "TypeId.INT" 581 | val output = newLocalName(typeId) 582 | appendDeclarationCodeBlock { 583 | append("Local ") 584 | append(output) 585 | append(" = methodCodeBlock.newLocal(TypeId.INT);") 586 | } 587 | appendOperationCodeBlock { 588 | append("methodCodeBlock.compareLongs(") 589 | append(output) 590 | append(", ") 591 | append(stack[1].first) 592 | append(", ") 593 | append(stack[0].first) 594 | append(");") 595 | stack.pop() 596 | stack.pop() 597 | } 598 | stack.push(output to typeId) 599 | } 600 | 601 | Opcodes.FCMPL, Opcodes.FCMPG, Opcodes.DCMPL, Opcodes.DCMPG -> { 602 | val typeId = "TypeId.INT" 603 | val output = newLocalName(typeId) 604 | appendDeclarationCodeBlock { 605 | append("Local ") 606 | append(output) 607 | append(" = methodCodeBlock.newLocal(TypeId.INT);") 608 | } 609 | appendOperationCodeBlock { 610 | append("methodCodeBlock.compareFloatingPoint(") 611 | append(output) 612 | append(", ") 613 | append(stack[1].first) 614 | append(", ") 615 | append(stack[0].first) 616 | append(", ") 617 | append(if (opcode == Opcodes.FCMPG || opcode == Opcodes.DCMPG) 1 else -1) 618 | append(");") 619 | stack.pop() 620 | stack.pop() 621 | } 622 | stack.push(output to typeId) 623 | } 624 | 625 | Opcodes.IRETURN, Opcodes.LRETURN, Opcodes.FRETURN, Opcodes.DRETURN, Opcodes.ARETURN -> { 626 | appendOperationCodeBlock { 627 | if (stack.peek().second != currentMethodReturnType) { 628 | stack.push(store(currentMethodReturnType) to currentMethodReturnType) 629 | } 630 | append("methodCodeBlock.returnValue(") 631 | append(stack.pop().first) 632 | append(");") 633 | stack.clear() 634 | } 635 | } 636 | 637 | Opcodes.RETURN -> { 638 | appendOperationCodeBlock { 639 | append("methodCodeBlock.returnVoid();") 640 | stack.clear() 641 | } 642 | } 643 | Opcodes.POP -> { 644 | stack.pop() 645 | } 646 | Opcodes.DUP -> { 647 | if (!pendingNewInstance) { 648 | stack.push(stack.peek()) 649 | } 650 | } 651 | Opcodes.MONITORENTER -> { 652 | appendOperationCodeBlock { 653 | append("methodCodeBlock.monitorEnter(") 654 | append(stack.pop().first) 655 | append(");") 656 | } 657 | } 658 | Opcodes.MONITOREXIT -> { 659 | appendOperationCodeBlock { 660 | append("methodCodeBlock.monitorExit(") 661 | append(stack.pop().first) 662 | append(");") 663 | } 664 | } 665 | Opcodes.ATHROW -> { 666 | appendOperationCodeBlock { 667 | append("methodCodeBlock.throwValue(") 668 | append(stack.pop().first) 669 | append(");") 670 | } 671 | } 672 | } 673 | 674 | } 675 | 676 | private fun binaryOp(type: String, op: String) { 677 | val typeId = type.typeId 678 | val output = newLocalName(typeId) 679 | appendDeclarationCodeBlock { 680 | append("Local ") 681 | append(output) 682 | append(" = methodCodeBlock.newLocal(") 683 | append(typeId) 684 | append(");") 685 | } 686 | appendOperationCodeBlock { 687 | append("methodCodeBlock.op(") 688 | append(op) 689 | append(", ") 690 | append(output) 691 | append(", ") 692 | append(stack[1].first) 693 | append(", ") 694 | append(stack[0].first) 695 | append(");") 696 | stack.pop() 697 | stack.pop() 698 | } 699 | stack.push(output to typeId) 700 | } 701 | 702 | override fun visitIntInsn(opcode: Int, operand: Int) { 703 | when (opcode) { 704 | Opcodes.BIPUSH -> loadConstant("B", operand) 705 | Opcodes.SIPUSH -> loadConstant("S", operand) 706 | Opcodes.NEWARRAY -> visitTypeInsn( 707 | Opcodes.ANEWARRAY, when (operand) { 708 | 4 -> "Z" 709 | 5 -> "C" 710 | 6 -> "F" 711 | 7 -> "D" 712 | 8 -> "B" 713 | 9 -> "S" 714 | 10 -> "I" 715 | 11 -> "J" 716 | else -> return 717 | } 718 | ) 719 | 720 | } 721 | } 722 | 723 | override fun visitVarInsn(opcode: Int, index: Int) { 724 | when (opcode) { 725 | Opcodes.ILOAD, Opcodes.LLOAD, Opcodes.FLOAD, Opcodes.DLOAD, Opcodes.ALOAD -> { 726 | val originLocalCount = currentMethodParameters.size + if (isStaticMethod) 0 else 1 727 | if (index < originLocalCount) { 728 | if (index == 0 && !isStaticMethod) { 729 | stack.push("methodCodeBlock.getThis(classId)" to "classId") 730 | } else { 731 | val realIndex = if (isStaticMethod) index else index - 1 732 | val typeId = currentMethodParameters[realIndex].typeId 733 | stack.push("methodCodeBlock.getParameter($realIndex, $typeId)" to typeId) 734 | } 735 | } else { 736 | stack.push(getLocalName(index)) 737 | } 738 | } 739 | Opcodes.ISTORE, Opcodes.LSTORE, Opcodes.FSTORE, Opcodes.DSTORE, Opcodes.ASTORE -> { 740 | val currentTypeId = stack.peek().second 741 | getLocalName(index)?.let { (targetLocal, targetTypeId) -> 742 | if (currentTypeId == targetTypeId) { 743 | store(currentTypeId, targetLocal) 744 | } else { 745 | store(currentTypeId, newLocalName(currentTypeId, true, targetLocal).also { 746 | appendDeclarationCodeBlock { 747 | append("Local ") 748 | append(it) 749 | append(" = methodCodeBlock.newLocal(") 750 | append(currentTypeId) 751 | append(");") 752 | } 753 | }) 754 | } 755 | } ?: store(currentTypeId) 756 | } 757 | } 758 | 759 | } 760 | 761 | private fun store(typeId: String, target: String? = null): String { 762 | val output = target ?: run { 763 | newLocalName(typeId, true).also { 764 | appendDeclarationCodeBlock { 765 | append("Local ") 766 | append(it) 767 | append(" = methodCodeBlock.newLocal(") 768 | append(typeId) 769 | append(");") 770 | } 771 | } 772 | } 773 | appendOperationCodeBlock { 774 | append("methodCodeBlock.move(") 775 | append(output) 776 | append(", ") 777 | append(stack.pop().first) 778 | append(");") 779 | } 780 | return output 781 | } 782 | 783 | private var pendingNewInstance = false 784 | 785 | override fun visitTypeInsn(opcode: Int, type: String) { 786 | when (opcode) { 787 | Opcodes.NEW -> { 788 | pendingNewInstance = true 789 | } 790 | Opcodes.ANEWARRAY -> { 791 | val ownerTypeId = (if (type.length == 1) "[$type" else "[L$type").typeId 792 | val output = newLocalName(ownerTypeId) 793 | appendDeclarationCodeBlock { 794 | append("Local ") 795 | append(output) 796 | append(" = methodCodeBlock.newLocal(") 797 | append(ownerTypeId) 798 | append(");") 799 | } 800 | appendOperationCodeBlock { 801 | append("methodCodeBlock.newArray(") 802 | append(output) 803 | append(", ") 804 | append(stack.pop().first) 805 | append(");") 806 | } 807 | stack.push(output to ownerTypeId) 808 | } 809 | Opcodes.CHECKCAST -> { 810 | cast(type) 811 | } 812 | Opcodes.INSTANCEOF -> { 813 | val typeId = type.typeId 814 | val output = newLocalName(typeId) 815 | appendDeclarationCodeBlock { 816 | append("Local ") 817 | append(output) 818 | append(" = methodCodeBlock.newLocal(") 819 | append(typeId) 820 | append(");") 821 | } 822 | appendOperationCodeBlock { 823 | append("methodCodeBlock.instanceOfType(") 824 | append(output) 825 | append(", ") 826 | append(stack.pop().first) 827 | append(", ") 828 | append(typeId) 829 | append(");") 830 | } 831 | stack.push(output to typeId) 832 | } 833 | } 834 | } 835 | 836 | override fun visitFieldInsn(opcode: Int, owner: String, name: String, descriptor: String) { 837 | when (opcode) { 838 | Opcodes.GETSTATIC -> { 839 | val typeId = descriptor.typeId 840 | val output = newLocalName(typeId) 841 | appendDeclarationCodeBlock { 842 | append("Local ") 843 | append(output) 844 | append(" = methodCodeBlock.newLocal(") 845 | append(typeId) 846 | append(");") 847 | } 848 | appendOperationCodeBlock { 849 | append("methodCodeBlock.sget(") 850 | append(owner.getFieldId(descriptor, name)) 851 | append(", ") 852 | append(output) 853 | append(");") 854 | } 855 | stack.push(output to typeId) 856 | } 857 | Opcodes.PUTSTATIC -> { 858 | appendOperationCodeBlock { 859 | append("methodCodeBlock.sput(") 860 | append(owner.getFieldId(descriptor, name)) 861 | append(", ") 862 | append(stack.peek().first) 863 | append(");") 864 | if (popAfterPut) { 865 | popAfterPut = false 866 | stack.pop() 867 | } 868 | } 869 | } 870 | Opcodes.GETFIELD -> { 871 | val typeId = descriptor.typeId 872 | val output = newLocalName(typeId) 873 | appendDeclarationCodeBlock { 874 | append("Local ") 875 | append(output) 876 | append(" = methodCodeBlock.newLocal(") 877 | append(typeId) 878 | append(");") 879 | } 880 | appendOperationCodeBlock { 881 | append("methodCodeBlock.iget(") 882 | append(owner.getFieldId(descriptor, name)) 883 | append(", ") 884 | append(output) 885 | append(", ") 886 | append(stack.pop().first) 887 | append(");") 888 | } 889 | stack.push(output to typeId) 890 | } 891 | Opcodes.PUTFIELD -> { 892 | appendOperationCodeBlock { 893 | append("methodCodeBlock.iput(") 894 | append(owner.getFieldId(descriptor, name)) 895 | append(", ") 896 | append(stack[1].first) 897 | append(", ") 898 | append(stack[0].first) 899 | append(");") 900 | stack.pop() 901 | if (popAfterPut) { 902 | popAfterPut = false 903 | stack.pop() 904 | } 905 | } 906 | } 907 | } 908 | } 909 | 910 | override fun visitInvokeDynamicInsn( 911 | name: String?, descriptor: String?, bootstrapMethodHandle: Handle?, vararg bootstrapMethodArguments: Any? 912 | ) { 913 | //FIXME: 不支持INVOKE DYNAMIC 914 | } 915 | 916 | override fun visitJumpInsn(opcode: Int, label: Label) { 917 | when (opcode) { 918 | Opcodes.IFEQ, Opcodes.IFNE, Opcodes.IFLT, Opcodes.IFGE, Opcodes.IFGT, Opcodes.IFLE -> { 919 | appendOperationCodeBlock { 920 | append("methodCodeBlock.compareZ(") 921 | append( 922 | when (opcode) { 923 | Opcodes.IFEQ -> "Comparison.EQ" 924 | Opcodes.IFNE -> "Comparison.NE" 925 | Opcodes.IFLT -> "Comparison.LT" 926 | Opcodes.IFGE -> "Comparison.GE" 927 | Opcodes.IFGT -> "Comparison.GT" 928 | Opcodes.IFLE -> "Comparison.LE" 929 | else -> "" 930 | } 931 | ) 932 | append(", ") 933 | append(label) 934 | append(", ") 935 | append(stack.pop().first) 936 | append(");") 937 | } 938 | } 939 | Opcodes.IF_ICMPEQ, Opcodes.IF_ICMPNE, Opcodes.IF_ICMPLT, Opcodes.IF_ICMPGE, 940 | Opcodes.IF_ICMPGT, Opcodes.IF_ICMPLE, Opcodes.IF_ACMPEQ, Opcodes.IF_ACMPNE -> { 941 | appendOperationCodeBlock { 942 | append("methodCodeBlock.compare(") 943 | append( 944 | when (opcode) { 945 | Opcodes.IF_ICMPEQ, Opcodes.IF_ACMPEQ -> "Comparison.EQ" 946 | Opcodes.IF_ICMPNE, Opcodes.IF_ACMPNE -> "Comparison.NE" 947 | Opcodes.IF_ICMPLT -> "Comparison.LT" 948 | Opcodes.IF_ICMPGE -> "Comparison.GE" 949 | Opcodes.IF_ICMPGT -> "Comparison.GT" 950 | Opcodes.IF_ICMPLE -> "Comparison.LE" 951 | else -> "" 952 | } 953 | ) 954 | append(", ") 955 | append(label) 956 | append(", ") 957 | append(stack[1].first) 958 | append(", ") 959 | append(stack[0].first) 960 | append(");") 961 | stack.pop() 962 | stack.pop() 963 | } 964 | } 965 | Opcodes.IFNULL -> { 966 | visitInsn(Opcodes.ACONST_NULL) 967 | visitJumpInsn(Opcodes.IF_ACMPEQ, label) 968 | } 969 | Opcodes.IFNONNULL -> { 970 | visitInsn(Opcodes.ACONST_NULL) 971 | visitJumpInsn(Opcodes.IF_ACMPNE, label) 972 | } 973 | Opcodes.GOTO -> { 974 | appendOperationCodeBlock { 975 | append("methodCodeBlock.jump(") 976 | append(label) 977 | append(");") 978 | } 979 | } 980 | } 981 | } 982 | 983 | private val labelMapping = HashSet