From 859d47956975a123a62072278cd334f5d9d520e4 Mon Sep 17 00:00:00 2001 From: Skylot <118523+skylot@users.noreply.github.com> Date: Sun, 22 Feb 2026 14:35:16 +0000 Subject: [PATCH] chore: remove 'jadx-script-kotlin' --- jadx-plugins/jadx-script-kotlin/README.md | 18 -- .../jadx-script-kotlin/build.gradle.kts | 87 ------ .../examples/deobf/deobf-resources.jadx.kts | 34 --- .../examples/deobf/deobf.jadx.kts | 33 --- .../examples/deobf/deobf2.jadx.kts | 43 --- .../examples/deobf/deobf_by_code.jadx.kts | 37 --- .../deobf/deobf_from_tostring.jadx.kts | 89 ------- .../deobf/deobf_method_param.jadx.kts | 34 --- .../examples/gui/bookmark.jadx.kts | 41 --- .../examples/gui/caret_mouse.jadx.kts | 19 -- .../examples/gui/custom_frida.jadx.kts | 120 --------- .../examples/gui/log_events.jadx.kts | 23 -- .../examples/gui/menu_entry.jadx.kts | 11 - .../examples/hello.jadx.kts | 29 -- .../examples/options.jadx.kts | 30 --- .../examples/replace.jadx.kts | 22 -- .../examples/replace_method_call.jadx.kts | 59 ---- .../examples/stages.jadx.kts | 64 ----- .../script/kotlin/JadxScriptKotlinPlugin.kt | 37 --- .../jadx/plugins/script/kotlin/ScriptCache.kt | 111 -------- .../jadx/plugins/script/kotlin/ScriptEval.kt | 212 --------------- .../plugins/script/kotlin/ScriptServices.kt | 82 ------ .../plugins/script/kotlin/gui/JInputScript.kt | 92 ------- .../script/kotlin/gui/JInputScripts.kt | 51 ---- .../kotlin/gui/JadxScriptInputCategory.kt | 44 --- .../script/kotlin/gui/JadxScriptOptionsUI.kt | 30 --- .../plugins/script/kotlin/gui/KtLintUtils.kt | 55 ---- .../script/kotlin/gui/ScriptCodeArea.kt | 76 ------ .../kotlin/gui/ScriptCompleteProvider.kt | 137 ---------- .../script/kotlin/gui/ScriptCompletionData.kt | 58 ---- .../kotlin/gui/ScriptCompletionRenderer.kt | 26 -- .../script/kotlin/gui/ScriptContentPanel.kt | 251 ------------------ .../script/kotlin/gui/ScriptErrorService.kt | 108 -------- .../kotlin/passes/JadxScriptAfterLoadPass.kt | 27 -- .../kotlin/runtime/JadxScriptTemplate.kt | 29 -- .../script/kotlin/runtime/ScriptRuntime.kt | 79 ------ .../plugins/script/kotlin/runtime/Utils.kt | 10 - .../script/kotlin/runtime/data/Debug.kt | 34 --- .../script/kotlin/runtime/data/Decompile.kt | 27 -- .../plugins/script/kotlin/runtime/data/Gui.kt | 65 ----- .../script/kotlin/runtime/data/Options.kt | 117 -------- .../script/kotlin/runtime/data/Rename.kt | 52 ---- .../script/kotlin/runtime/data/Replace.kt | 43 --- .../script/kotlin/runtime/data/Search.kt | 16 -- .../script/kotlin/runtime/data/Stages.kt | 76 ------ .../script/kotlin/runtime/data/Wrappers.kt | 77 ------ ...otlin.runtime.JadxScriptTemplate.classname | 0 .../services/jadx.api.plugins.JadxPlugin | 1 - .../src/test/kotlin/JadxScriptPluginTest.kt | 51 ---- .../src/test/kotlin/ScriptServicesTest.kt | 67 ----- .../src/test/resources/samples/hello.smali | 10 - .../test/resources/samples/simple.jadx.kts | 5 - .../test/resources/samples/test-deps.jadx.kts | 11 - .../src/test/resources/samples/test.jadx.kts | 20 -- settings.gradle.kts | 1 - 55 files changed, 2981 deletions(-) delete mode 100644 jadx-plugins/jadx-script-kotlin/README.md delete mode 100644 jadx-plugins/jadx-script-kotlin/build.gradle.kts delete mode 100644 jadx-plugins/jadx-script-kotlin/examples/deobf/deobf-resources.jadx.kts delete mode 100644 jadx-plugins/jadx-script-kotlin/examples/deobf/deobf.jadx.kts delete mode 100644 jadx-plugins/jadx-script-kotlin/examples/deobf/deobf2.jadx.kts delete mode 100644 jadx-plugins/jadx-script-kotlin/examples/deobf/deobf_by_code.jadx.kts delete mode 100644 jadx-plugins/jadx-script-kotlin/examples/deobf/deobf_from_tostring.jadx.kts delete mode 100644 jadx-plugins/jadx-script-kotlin/examples/deobf/deobf_method_param.jadx.kts delete mode 100644 jadx-plugins/jadx-script-kotlin/examples/gui/bookmark.jadx.kts delete mode 100644 jadx-plugins/jadx-script-kotlin/examples/gui/caret_mouse.jadx.kts delete mode 100644 jadx-plugins/jadx-script-kotlin/examples/gui/custom_frida.jadx.kts delete mode 100644 jadx-plugins/jadx-script-kotlin/examples/gui/log_events.jadx.kts delete mode 100644 jadx-plugins/jadx-script-kotlin/examples/gui/menu_entry.jadx.kts delete mode 100644 jadx-plugins/jadx-script-kotlin/examples/hello.jadx.kts delete mode 100644 jadx-plugins/jadx-script-kotlin/examples/options.jadx.kts delete mode 100644 jadx-plugins/jadx-script-kotlin/examples/replace.jadx.kts delete mode 100644 jadx-plugins/jadx-script-kotlin/examples/replace_method_call.jadx.kts delete mode 100644 jadx-plugins/jadx-script-kotlin/examples/stages.jadx.kts delete mode 100644 jadx-plugins/jadx-script-kotlin/src/main/kotlin/jadx/plugins/script/kotlin/JadxScriptKotlinPlugin.kt delete mode 100644 jadx-plugins/jadx-script-kotlin/src/main/kotlin/jadx/plugins/script/kotlin/ScriptCache.kt delete mode 100644 jadx-plugins/jadx-script-kotlin/src/main/kotlin/jadx/plugins/script/kotlin/ScriptEval.kt delete mode 100644 jadx-plugins/jadx-script-kotlin/src/main/kotlin/jadx/plugins/script/kotlin/ScriptServices.kt delete mode 100644 jadx-plugins/jadx-script-kotlin/src/main/kotlin/jadx/plugins/script/kotlin/gui/JInputScript.kt delete mode 100644 jadx-plugins/jadx-script-kotlin/src/main/kotlin/jadx/plugins/script/kotlin/gui/JInputScripts.kt delete mode 100644 jadx-plugins/jadx-script-kotlin/src/main/kotlin/jadx/plugins/script/kotlin/gui/JadxScriptInputCategory.kt delete mode 100644 jadx-plugins/jadx-script-kotlin/src/main/kotlin/jadx/plugins/script/kotlin/gui/JadxScriptOptionsUI.kt delete mode 100644 jadx-plugins/jadx-script-kotlin/src/main/kotlin/jadx/plugins/script/kotlin/gui/KtLintUtils.kt delete mode 100644 jadx-plugins/jadx-script-kotlin/src/main/kotlin/jadx/plugins/script/kotlin/gui/ScriptCodeArea.kt delete mode 100644 jadx-plugins/jadx-script-kotlin/src/main/kotlin/jadx/plugins/script/kotlin/gui/ScriptCompleteProvider.kt delete mode 100644 jadx-plugins/jadx-script-kotlin/src/main/kotlin/jadx/plugins/script/kotlin/gui/ScriptCompletionData.kt delete mode 100644 jadx-plugins/jadx-script-kotlin/src/main/kotlin/jadx/plugins/script/kotlin/gui/ScriptCompletionRenderer.kt delete mode 100644 jadx-plugins/jadx-script-kotlin/src/main/kotlin/jadx/plugins/script/kotlin/gui/ScriptContentPanel.kt delete mode 100644 jadx-plugins/jadx-script-kotlin/src/main/kotlin/jadx/plugins/script/kotlin/gui/ScriptErrorService.kt delete mode 100644 jadx-plugins/jadx-script-kotlin/src/main/kotlin/jadx/plugins/script/kotlin/passes/JadxScriptAfterLoadPass.kt delete mode 100644 jadx-plugins/jadx-script-kotlin/src/main/kotlin/jadx/plugins/script/kotlin/runtime/JadxScriptTemplate.kt delete mode 100644 jadx-plugins/jadx-script-kotlin/src/main/kotlin/jadx/plugins/script/kotlin/runtime/ScriptRuntime.kt delete mode 100644 jadx-plugins/jadx-script-kotlin/src/main/kotlin/jadx/plugins/script/kotlin/runtime/Utils.kt delete mode 100644 jadx-plugins/jadx-script-kotlin/src/main/kotlin/jadx/plugins/script/kotlin/runtime/data/Debug.kt delete mode 100644 jadx-plugins/jadx-script-kotlin/src/main/kotlin/jadx/plugins/script/kotlin/runtime/data/Decompile.kt delete mode 100644 jadx-plugins/jadx-script-kotlin/src/main/kotlin/jadx/plugins/script/kotlin/runtime/data/Gui.kt delete mode 100644 jadx-plugins/jadx-script-kotlin/src/main/kotlin/jadx/plugins/script/kotlin/runtime/data/Options.kt delete mode 100644 jadx-plugins/jadx-script-kotlin/src/main/kotlin/jadx/plugins/script/kotlin/runtime/data/Rename.kt delete mode 100644 jadx-plugins/jadx-script-kotlin/src/main/kotlin/jadx/plugins/script/kotlin/runtime/data/Replace.kt delete mode 100644 jadx-plugins/jadx-script-kotlin/src/main/kotlin/jadx/plugins/script/kotlin/runtime/data/Search.kt delete mode 100644 jadx-plugins/jadx-script-kotlin/src/main/kotlin/jadx/plugins/script/kotlin/runtime/data/Stages.kt delete mode 100644 jadx-plugins/jadx-script-kotlin/src/main/kotlin/jadx/plugins/script/kotlin/runtime/data/Wrappers.kt delete mode 100644 jadx-plugins/jadx-script-kotlin/src/main/resources/META-INF/kotlin/script/templates/jadx.plugins.script.kotlin.runtime.JadxScriptTemplate.classname delete mode 100644 jadx-plugins/jadx-script-kotlin/src/main/resources/META-INF/services/jadx.api.plugins.JadxPlugin delete mode 100644 jadx-plugins/jadx-script-kotlin/src/test/kotlin/JadxScriptPluginTest.kt delete mode 100644 jadx-plugins/jadx-script-kotlin/src/test/kotlin/ScriptServicesTest.kt delete mode 100644 jadx-plugins/jadx-script-kotlin/src/test/resources/samples/hello.smali delete mode 100644 jadx-plugins/jadx-script-kotlin/src/test/resources/samples/simple.jadx.kts delete mode 100644 jadx-plugins/jadx-script-kotlin/src/test/resources/samples/test-deps.jadx.kts delete mode 100644 jadx-plugins/jadx-script-kotlin/src/test/resources/samples/test.jadx.kts diff --git a/jadx-plugins/jadx-script-kotlin/README.md b/jadx-plugins/jadx-script-kotlin/README.md deleted file mode 100644 index 805144582..000000000 --- a/jadx-plugins/jadx-script-kotlin/README.md +++ /dev/null @@ -1,18 +0,0 @@ -## JADX scripting support - -### Examples - -Check script examples in [`examples/`](https://github.com/skylot/jadx/tree/master/jadx-plugins/jadx-script-kotlin/examples/)(start with [`hello`](https://github.com/skylot/jadx/blob/master/jadx-plugins/jadx-script-kotlin/examples/hello.jadx.kts)) - -### Script usage - -#### In jadx-cli - -Just add script file as input - -#### In jadx-gui - -1. Add script file to the project (using `Add files` or `New script` by right-click menu on `Inputs/Scripts`) -2. Script will appear in `Inputs/Scripts` section -3. After script change, you can run it using `Run` button in script editor toolbar or reload whole project (`Reload` button in toolbar or `F5`). - Also, you can enable `Live reload` option in `File` menu to reload project automatically on scripts change diff --git a/jadx-plugins/jadx-script-kotlin/build.gradle.kts b/jadx-plugins/jadx-script-kotlin/build.gradle.kts deleted file mode 100644 index 86b95ca1e..000000000 --- a/jadx-plugins/jadx-script-kotlin/build.gradle.kts +++ /dev/null @@ -1,87 +0,0 @@ -import org.jetbrains.kotlin.gradle.dsl.JvmTarget - -plugins { - id("java-library") - - kotlin("jvm") -} - -version = System.getenv("JADX_SCRIPT_KOTLIN_PLUGIN_VERSION") ?: "dev" - -dependencies { - compileOnly(project(":jadx-core")) - compileOnly(project(":jadx-commons:jadx-app-commons")) - - compileOnly(project(":jadx-gui")) - - implementation(kotlin("scripting-common")) - implementation(kotlin("scripting-jvm")) - implementation(kotlin("scripting-jvm-host")) - implementation(kotlin("scripting-ide-services")) - implementation(kotlin("scripting-compiler-embeddable")) - implementation(kotlin("compiler-embeddable")) - - implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.10.2") - - // allow to use maven dependencies in scripts - implementation(kotlin("scripting-dependencies")) - implementation(kotlin("scripting-dependencies-maven")) - - // autocomplete support in editor - compileOnly("com.fifesoft:autocomplete:3.3.2") - compileOnly("com.fifesoft:rsyntaxtextarea:3.6.0") - - // use KtLint for format and check jadx scripts - implementation("com.pinterest.ktlint:ktlint-rule-engine:1.8.0") - implementation("com.pinterest.ktlint:ktlint-ruleset-standard:1.8.0") - - compileOnly("io.github.oshai:kotlin-logging-jvm:7.0.13") - compileOnly("org.slf4j:slf4j-api:2.0.17") - - // register jadx script for IDE support (don't work now) - // kotlinScriptDef(project(":jadx-plugins:jadx-script-kotlin")) - - testImplementation(project(":jadx-core")) - testRuntimeOnly(project(":jadx-plugins:jadx-dex-input")) - testRuntimeOnly(project(":jadx-plugins:jadx-smali-input")) - - testImplementation("ch.qos.logback:logback-classic:1.5.22") - testImplementation("org.assertj:assertj-core:3.27.6") - - testImplementation("org.junit.jupiter:junit-jupiter:5.13.3") - testRuntimeOnly("org.junit.platform:junit-platform-launcher") -} - -repositories { - mavenLocal() - mavenCentral() - google() -} - -java { - sourceCompatibility = JavaVersion.VERSION_11 - targetCompatibility = JavaVersion.VERSION_11 -} - -kotlin { - compilerOptions { - jvmTarget.set(JvmTarget.JVM_11) - } -} - -tasks { - register("dist") { - group = "jadx-plugin" - dependsOn(jar) - - from(jar) - from(project.configurations.runtimeClasspath) - - archiveBaseName = project.name - destinationDirectory = layout.buildDirectory.dir("dist") - } - - withType(Test::class) { - useJUnitPlatform() - } -} diff --git a/jadx-plugins/jadx-script-kotlin/examples/deobf/deobf-resources.jadx.kts b/jadx-plugins/jadx-script-kotlin/examples/deobf/deobf-resources.jadx.kts deleted file mode 100644 index 6469f1091..000000000 --- a/jadx-plugins/jadx-script-kotlin/examples/deobf/deobf-resources.jadx.kts +++ /dev/null @@ -1,34 +0,0 @@ -import jadx.api.plugins.options.OptionFlag.PER_PROJECT - -/** - * Custom resources regexp deobfuscator - */ - -val jadx = getJadxInstance() - -val regexOpt = jadx.options.registerString( - name = "regex", - desc = "Apply resources rename for file names matches regex", - defaultValue = """[Oo0]+\.xml""", -).flags(PER_PROJECT) - -val regex = regexOpt.value.toRegex() -var n = 0 - -jadx.stages.prepare { - for (resFile in jadx.internalDecompiler.resources) { - val fullName = resFile.originalName - val name = fullName.substringAfterLast('/') - if (name matches regex) { - val path = fullName.substringBeforeLast('/') // TODO: path also may be obfuscated - val ext = name.substringAfterLast('.') - val newName = "$path/res-${n++}.$ext" - log.info { "renaming resource: '$fullName' to '$newName'" } - resFile.deobfName = newName - } - } -} - -jadx.afterLoad { - log.info { "Renames count: $n" } -} diff --git a/jadx-plugins/jadx-script-kotlin/examples/deobf/deobf.jadx.kts b/jadx-plugins/jadx-script-kotlin/examples/deobf/deobf.jadx.kts deleted file mode 100644 index e36c54176..000000000 --- a/jadx-plugins/jadx-script-kotlin/examples/deobf/deobf.jadx.kts +++ /dev/null @@ -1,33 +0,0 @@ -import jadx.api.plugins.options.OptionFlag.PER_PROJECT - -/** - * Custom regexp deobfuscator - */ - -val jadx = getJadxInstance() -jadx.args.isDeobfuscationOn = false -jadx.args.renameFlags = emptySet() - -val regexOpt = jadx.options.registerString( - name = "regex", - desc = "Apply rename for names matches regex", - defaultValue = "[Oo0]+", -).flags(PER_PROJECT) - -val regex = regexOpt.value.toRegex() -var n = 0 -jadx.rename.all { name, node -> - when { - name matches regex -> { - val newName = "${node.typeName()}${n++}" - log.info { "renaming ${node.typeName()} '$node' to '$newName'" } - newName - } - - else -> null - } -} - -jadx.afterLoad { - log.info { "Renames count: $n" } -} diff --git a/jadx-plugins/jadx-script-kotlin/examples/deobf/deobf2.jadx.kts b/jadx-plugins/jadx-script-kotlin/examples/deobf/deobf2.jadx.kts deleted file mode 100644 index a369506a1..000000000 --- a/jadx-plugins/jadx-script-kotlin/examples/deobf/deobf2.jadx.kts +++ /dev/null @@ -1,43 +0,0 @@ -/** - * Animal deobfuscator ^_^ - */ - -@file:DependsOn("com.github.javafaker:javafaker:1.0.2") - -import com.github.javafaker.Faker -import jadx.core.deobf.NameMapper -import java.util.Random - -val jadx = getJadxInstance() -jadx.args.isDeobfuscationOn = false -jadx.args.renameFlags = emptySet() - -val regex = """[Oo0]+""".toRegex() -val usedNames = mutableSetOf() -val faker = Faker(Random(1)) -var dups = 1 - -jadx.rename.all { name, node -> - when { - name matches regex -> { - val prefix = node.typeName().first() - val alias = faker.name().firstName().cap() + faker.animal().name().cap() - makeUnique(prefix, alias) - } - - else -> null - } -} - -fun makeUnique(prefix: Char, name: String): String { - while (true) { - val resName = prefix + NameMapper.removeInvalidCharsMiddle(name) - return if (usedNames.add(resName)) resName else "$resName${dups++}" - } -} - -jadx.afterLoad { - log.info { "Renames count: ${usedNames.size + dups}, names: ${usedNames.size}, dups: $dups" } -} - -fun String.cap() = this.replaceFirstChar(Char::uppercaseChar) diff --git a/jadx-plugins/jadx-script-kotlin/examples/deobf/deobf_by_code.jadx.kts b/jadx-plugins/jadx-script-kotlin/examples/deobf/deobf_by_code.jadx.kts deleted file mode 100644 index b2929d852..000000000 --- a/jadx-plugins/jadx-script-kotlin/examples/deobf/deobf_by_code.jadx.kts +++ /dev/null @@ -1,37 +0,0 @@ -/** - * Rename method if specified string is found - */ - -import jadx.api.plugins.input.insns.Opcode -import jadx.core.dex.nodes.MethodNode - -val renamesMap = mapOf( - "specificString" to "newMethodName", -) - -val jadx = getJadxInstance() - -var n = 0 -jadx.rename.all { _, node -> - var newName: String? = null - if (node is MethodNode) { - // use quick instructions scanner - node.codeReader?.visitInstructions { insn -> - if (insn.opcode == Opcode.CONST_STRING) { - insn.decode() - val constStr = insn.indexAsString - val renameStr = renamesMap[constStr] - if (renameStr != null) { - log.info { "Found '$constStr' in method $node, renaming to '$renameStr'" } - newName = renameStr - n++ - } - } - } - } - newName -} - -jadx.afterLoad { - log.info { "Script '$scriptName' renamed $n methods" } -} diff --git a/jadx-plugins/jadx-script-kotlin/examples/deobf/deobf_from_tostring.jadx.kts b/jadx-plugins/jadx-script-kotlin/examples/deobf/deobf_from_tostring.jadx.kts deleted file mode 100644 index 75992a8ef..000000000 --- a/jadx-plugins/jadx-script-kotlin/examples/deobf/deobf_from_tostring.jadx.kts +++ /dev/null @@ -1,89 +0,0 @@ -/** - * Rename class and fields using strings from toString() method - */ - -import jadx.core.deobf.NameMapper -import jadx.core.dex.attributes.AFlag -import jadx.core.dex.attributes.nodes.RenameReasonAttr -import jadx.core.dex.info.FieldInfo -import jadx.core.dex.instructions.ConstStringNode -import jadx.core.dex.instructions.IndexInsnNode -import jadx.core.dex.instructions.InsnType -import jadx.core.dex.instructions.args.InsnWrapArg -import jadx.core.dex.nodes.InsnNode -import jadx.core.dex.nodes.MethodNode -import jadx.plugins.script.kotlin.runtime.data.ScriptOrderedDecompilePass - -val jadx = getJadxInstance() - -// StringBuilder chain replaced by STR_CONCAT instruction in SimplifyVisitor -// Search for return with STR_CONCAT and process args -jadx.addPass(object : ScriptOrderedDecompilePass( - jadx, - "DeobfFromToString", - runAfter = listOf("SimplifyVisitor"), -) { - override fun visit(mth: MethodNode) { - if (mth.methodInfo.shortId == "toString()Ljava/lang/String;") { - val returnBlock = mth.exitBlock.predecessors.firstOrNull { it.contains(AFlag.RETURN) } - val lastInsn = returnBlock?.instructions?.lastOrNull() - if (lastInsn != null && lastInsn.type == InsnType.RETURN) { - val arg = lastInsn.getArg(0) - if (arg.isInsnWrap) { - val wrapInsn = (arg as InsnWrapArg).wrapInsn - if (wrapInsn.type == InsnType.STR_CONCAT) { - log.info { "Renaming using 'toString' in class: ${mth.parentClass}" } - processArgs(mth, wrapInsn) - } - } - } - } - } - - val clsSepRgx = Regex("[ ({:]") - - private fun processArgs(mth: MethodNode, wrapInsn: InsnNode): Boolean { - try { - var fldName: String? = null - for ((i, arg) in wrapInsn.arguments.withIndex()) { - val insn = arg.unwrap() ?: return false - if (i % 2 == 0) { - if (insn !is ConstStringNode) { - return false - } - var str = insn.string - if (i == 0) { - // class and first field name - val parts = str.split(clsSepRgx) - val clsName = parts[0] - if (NameMapper.isValidIdentifier(clsName)) { - mth.parentClass.run { - log.info { "rename class '$name' to '$clsName'" } - rename(clsName) - RenameReasonAttr.forNode(this).append("from toString()") - } - } - str = parts[1] - } - fldName = str.trim('\'', '=', ',', ' ', ':') - } else { - if (insn.type != InsnType.IGET) { - return false - } - val iget = insn as IndexInsnNode - val fldInfo = iget.index as FieldInfo - val fld = mth.parentClass.searchField(fldInfo) - if (fld != null && NameMapper.isValidIdentifier(fldName)) { - log.info { "rename field '${fld.name}' to '$fldName'" } - fld.rename(fldName) - RenameReasonAttr.forNode(fld).append("from toString()") - } - } - } - return true - } catch (e: Exception) { - log.error(e) { "Args process failed" } - return false - } - } -}) diff --git a/jadx-plugins/jadx-script-kotlin/examples/deobf/deobf_method_param.jadx.kts b/jadx-plugins/jadx-script-kotlin/examples/deobf/deobf_method_param.jadx.kts deleted file mode 100644 index 1aa0ad8b3..000000000 --- a/jadx-plugins/jadx-script-kotlin/examples/deobf/deobf_method_param.jadx.kts +++ /dev/null @@ -1,34 +0,0 @@ -/** - * Rename method parameters from value in attached annotation - */ - -import jadx.api.plugins.input.data.attributes.JadxAttrType -import jadx.core.deobf.NameMapper -import jadx.core.dex.nodes.MethodNode -import jadx.plugins.script.kotlin.runtime.data.ScriptDecompilePass - -val annCls = "Lretrofit2/http/Query;" -val annParam = "value" - -val jadx = getJadxInstance() - -// access to method parameters variables available only in decompile passes -jadx.addPass(object : ScriptDecompilePass(jadx, "RenameParams") { - override fun visit(mth: MethodNode) { - // parameter annotations stored in method attribute - mth.get(JadxAttrType.ANNOTATION_MTH_PARAMETERS)?.let { paramsAttr -> - for ((paramNum, annAttr) in paramsAttr.paramList.withIndex()) { - val name = annAttr?.get(annCls)?.values?.get(annParam)?.value as String? - if (NameMapper.isValidIdentifier(name)) { - mth.argRegs[paramNum].name = name - log.info { "Rename param $paramNum to $name in method $mth" } - } - } - } - } -}) - -jadx.afterLoad { - // force decompilation and run rename pass for all classes (optional) - jadx.decompile.allThreaded() -} diff --git a/jadx-plugins/jadx-script-kotlin/examples/gui/bookmark.jadx.kts b/jadx-plugins/jadx-script-kotlin/examples/gui/bookmark.jadx.kts deleted file mode 100644 index 76e2d7dfd..000000000 --- a/jadx-plugins/jadx-script-kotlin/examples/gui/bookmark.jadx.kts +++ /dev/null @@ -1,41 +0,0 @@ -import jadx.api.metadata.ICodeNodeRef -import jadx.core.dex.nodes.MethodNode - -val jadx = getJadxInstance() -var savedBookmark: ICodeNodeRef? = null - -jadx.gui.ifAvailable { - addPopupMenuAction( - "Set bookmark", - enabled = { true }, - keyBinding = "B", - action = ::setBookmark, - ) - - addMenuAction( - "Jump to bookmark", - action = ::jumpToBookmark, - ) -} - -fun setBookmark(node: ICodeNodeRef) { - val enclosing = jadx.gui.enclosingNodeUnderCaret ?: run { - log.info { "No enclosing node" } - return - } - // You can bookmark a field, method or a class - val target = if (enclosing is MethodNode) enclosing else node - - log.info { "Setting bookmark to: $target" } - savedBookmark = target -} - -fun jumpToBookmark() { - savedBookmark?.let { - if (!jadx.gui.open(it)) { - log.warn { "Failed to jump to bookmark: $it" } - } - } ?: run { - log.info { "No bookmark" } - } -} diff --git a/jadx-plugins/jadx-script-kotlin/examples/gui/caret_mouse.jadx.kts b/jadx-plugins/jadx-script-kotlin/examples/gui/caret_mouse.jadx.kts deleted file mode 100644 index ac4ef5828..000000000 --- a/jadx-plugins/jadx-script-kotlin/examples/gui/caret_mouse.jadx.kts +++ /dev/null @@ -1,19 +0,0 @@ -import jadx.api.metadata.ICodeNodeRef - -val jadx = getJadxInstance() - -jadx.gui.ifAvailable { - addPopupMenuAction( - "Print enclosing symbols under caret or mouse", - enabled = { true }, - keyBinding = "G", - action = ::runAction, - ) -} - -fun runAction(node: ICodeNodeRef) { - log.info { "Node under caret: ${jadx.gui.nodeUnderCaret}" } - log.info { "Enclosing node under caret: ${jadx.gui.enclosingNodeUnderCaret}" } - log.info { "Node under mouse: ${jadx.gui.nodeUnderMouse}" } - log.info { "Enclosing Node under mouse: ${jadx.gui.enclosingNodeUnderMouse}" } -} diff --git a/jadx-plugins/jadx-script-kotlin/examples/gui/custom_frida.jadx.kts b/jadx-plugins/jadx-script-kotlin/examples/gui/custom_frida.jadx.kts deleted file mode 100644 index 14aecb43a..000000000 --- a/jadx-plugins/jadx-script-kotlin/examples/gui/custom_frida.jadx.kts +++ /dev/null @@ -1,120 +0,0 @@ -@file:DependsOn("org.apache.commons:commons-text:1.10.0") - -import jadx.api.metadata.ICodeNodeRef -import jadx.core.codegen.TypeGen -import jadx.core.dex.instructions.args.ArgType -import jadx.core.dex.nodes.ClassNode -import jadx.core.dex.nodes.FieldNode -import jadx.core.dex.nodes.MethodNode -import jadx.core.utils.exceptions.JadxRuntimeException -import org.apache.commons.text.StringEscapeUtils - -val jadx = getJadxInstance() - -jadx.gui.ifAvailable { - addPopupMenuAction( - "Custom Frida snippet (g)", - enabled = ::isActionEnabled, - keyBinding = "G", - action = ::runAction, - ) -} - -fun isActionEnabled(node: ICodeNodeRef): Boolean { - return node is MethodNode || node is ClassNode || node is FieldNode -} - -fun runAction(node: ICodeNodeRef) { - try { - val fridaSnippet = generateFridaSnippet(node) - log.info { "Custom frida snippet:\n$fridaSnippet" } - jadx.gui.copyToClipboard(fridaSnippet) - } catch (e: Exception) { - log.error(e) { "Failed to generate Frida code snippet" } - } -} - -fun generateFridaSnippet(node: ICodeNodeRef): String { - return when (node) { - is MethodNode -> generateMethodSnippet(node) - is ClassNode -> generateClassSnippet(node) - is FieldNode -> generateFieldSnippet(node) - else -> throw JadxRuntimeException("Unsupported node type: " + node.javaClass) - } -} - -fun generateClassSnippet(cls: ClassNode): String { - return """let ${cls.name} = Java.use("${StringEscapeUtils.escapeEcmaScript(cls.rawName)}");""" -} - -fun generateMethodSnippet(mthNode: MethodNode): String { - val methodInfo = mthNode.methodInfo - val methodName = if (methodInfo.isConstructor) { - "\$init" - } else { - StringEscapeUtils.escapeEcmaScript(methodInfo.name) - } - val overload = if (isOverloaded(mthNode)) { - ".overload(${methodInfo.argumentsTypes.joinToString(transform = this::parseArgType)})" - } else { - "" - } - val shortClassName = mthNode.parentClass.name - val argNames = mthNode.collectArgNodes().map { a -> a.name } - val args = argNames.joinToString(separator = ", ") - val logArgs = if (argNames.isNotEmpty()) { - argNames.joinToString(separator = " + ', ' + ", prefix = " + ', ' + ") { p -> "'$p: ' + $p" } - } else { - "" - } - val clsSnippet = generateClassSnippet(mthNode.parentClass) - return if (methodInfo.isConstructor || methodInfo.returnType == ArgType.VOID) { - // no return value - """ - $clsSnippet - $shortClassName["$methodName"]$overload.implementation = function ($args) { - console.log('$shortClassName.$methodName is called'$logArgs); - this["$methodName"]($args); - }; - """.trimIndent() - } else { - """ - $clsSnippet - $shortClassName["$methodName"]$overload.implementation = function ($args) { - console.log('$shortClassName.$methodName is called'$logArgs); - let ret = this["$methodName"]($args); - console.log('$shortClassName.$methodName return: ' + ret); - return ret; - }; - """.trimIndent() - } -} - -fun generateFieldSnippet(fld: FieldNode): String { - var rawFieldName = StringEscapeUtils.escapeEcmaScript(fld.name) - for (methodNode in fld.parentClass.methods) { - if (methodNode.name == rawFieldName) { - rawFieldName = "_$rawFieldName" - break - } - } - return """ - ${generateClassSnippet(fld.parentClass)} - ${fld.name} = ${fld.parentClass.name}.$rawFieldName.value; - """.trimIndent() -} - -fun isOverloaded(methodNode: MethodNode): Boolean { - return methodNode.parentClass.methods.stream().anyMatch { m: MethodNode -> - m.name == methodNode.name && methodNode.methodInfo.shortId != m.methodInfo.shortId - } -} - -fun parseArgType(x: ArgType): String { - val typeStr = if (x.isArray) { - TypeGen.signature(x).replace("/", ".") - } else { - x.toString() - } - return "'$typeStr'" -} diff --git a/jadx-plugins/jadx-script-kotlin/examples/gui/log_events.jadx.kts b/jadx-plugins/jadx-script-kotlin/examples/gui/log_events.jadx.kts deleted file mode 100644 index b84c7b270..000000000 --- a/jadx-plugins/jadx-script-kotlin/examples/gui/log_events.jadx.kts +++ /dev/null @@ -1,23 +0,0 @@ -/** - * Log events - */ - -import jadx.api.plugins.events.JadxEvents - -val jadx = getJadxInstance() - -jadx.gui.ifAvailable { - // GUI only events - - jadx.events.addListener(JadxEvents.NODE_RENAMED_BY_USER) { rename -> - log.info { "Rename from '${rename.oldName}' to '${rename.newName}' for node ${rename.node}" } - } - - jadx.events.addListener(JadxEvents.RELOAD_PROJECT) { - log.info { "Project reloaded" } - } - - jadx.events.addListener(JadxEvents.RELOAD_SETTINGS_WINDOW) { - log.info { "Settings window reloaded" } - } -} diff --git a/jadx-plugins/jadx-script-kotlin/examples/gui/menu_entry.jadx.kts b/jadx-plugins/jadx-script-kotlin/examples/gui/menu_entry.jadx.kts deleted file mode 100644 index 19332c942..000000000 --- a/jadx-plugins/jadx-script-kotlin/examples/gui/menu_entry.jadx.kts +++ /dev/null @@ -1,11 +0,0 @@ -/** - * Add menu action (into 'Plugins' section) - */ - -val jadx = getJadxInstance() - -jadx.gui.ifAvailable { - addMenuAction("Decompile All") { - jadx.decompile.allThreaded() - } -} diff --git a/jadx-plugins/jadx-script-kotlin/examples/hello.jadx.kts b/jadx-plugins/jadx-script-kotlin/examples/hello.jadx.kts deleted file mode 100644 index b985c2280..000000000 --- a/jadx-plugins/jadx-script-kotlin/examples/hello.jadx.kts +++ /dev/null @@ -1,29 +0,0 @@ -// logger is preferred for output -log.info { "Hello from jadx script!" } - -// println will also work (will be redirected to logger) -println("println from script '$scriptName'") - -// get jadx decompiler script instance -val jadx = getJadxInstance() - -// adjust options if needed -jadx.args.isDeobfuscationOn = false - -// change names -jadx.rename.all { name -> - when (name) { - "HelloWorld" -> "HelloJadx" - else -> null - } -} - -// run some code after loading is finished -jadx.afterLoad { - log.info { "Loaded classes: ${jadx.classes.size}" } - // print first class code - jadx.classes.firstOrNull()?.let { cls -> - log.info { "Class: '${cls.name}'" } - log.info { cls.code } - } -} diff --git a/jadx-plugins/jadx-script-kotlin/examples/options.jadx.kts b/jadx-plugins/jadx-script-kotlin/examples/options.jadx.kts deleted file mode 100644 index fc9a64df8..000000000 --- a/jadx-plugins/jadx-script-kotlin/examples/options.jadx.kts +++ /dev/null @@ -1,30 +0,0 @@ -val jadx = getJadxInstance() - -val testOpt = jadx.options.registerString( - "test", - "Simple string option", - values = listOf("first", "second"), - defaultValue = "first", -) - -val numOpt = jadx.options.registerInt("number", "Number option").validate { it >= 0 } - -val boolOpt = jadx.options.registerYesNo("bool", "Boolean option") - -val allOptions = listOf(testOpt, numOpt, boolOpt) - -jadx.afterLoad { - printOptions() -} - -jadx.gui.ifAvailable { - addMenuAction("Print options") { - printOptions() - } -} - -fun printOptions() { - allOptions.forEach { opt -> - log.info { "Option: '${opt.name}', id: '${opt.id}', value: '${opt.value}'" } - } -} diff --git a/jadx-plugins/jadx-script-kotlin/examples/replace.jadx.kts b/jadx-plugins/jadx-script-kotlin/examples/replace.jadx.kts deleted file mode 100644 index 261bdc6f6..000000000 --- a/jadx-plugins/jadx-script-kotlin/examples/replace.jadx.kts +++ /dev/null @@ -1,22 +0,0 @@ -/** - * Instructions modification example. - * Replace first arg with const string. - */ - -import jadx.core.dex.instructions.ConstStringNode -import jadx.core.dex.instructions.InvokeNode -import jadx.core.dex.instructions.args.InsnArg - -val jadx = getJadxInstance() - -jadx.replace.insns { mth, insn -> - if (insn is InvokeNode) { - if (insn.callMth.shortId == "println(Ljava/lang/String;)V") { - val arg = insn.getArg(1) - val newArg = InsnArg.wrapInsnIntoArg(ConstStringNode("Jadx!")) - insn.setArg(1, newArg) - log.info { "Replace '$arg' with '$newArg' in $mth" } - } - } - null -} diff --git a/jadx-plugins/jadx-script-kotlin/examples/replace_method_call.jadx.kts b/jadx-plugins/jadx-script-kotlin/examples/replace_method_call.jadx.kts deleted file mode 100644 index 17f4c009b..000000000 --- a/jadx-plugins/jadx-script-kotlin/examples/replace_method_call.jadx.kts +++ /dev/null @@ -1,59 +0,0 @@ -/** - * Replace method call with calculated result. - * Useful for custom string deobfuscation. - * - * Example for sample from issue https://github.com/skylot/jadx/issues/1251 - */ - -import jadx.core.dex.instructions.ConstStringNode -import jadx.core.dex.instructions.InvokeNode -import jadx.core.dex.instructions.args.InsnArg -import jadx.core.dex.instructions.args.InsnWrapArg -import jadx.core.dex.instructions.args.RegisterArg - -val jadx = getJadxInstance() - -val mthSignature = "com.xshield.aa.iIiIiiiiII(Ljava/lang/String;)Ljava/lang/String;" - -jadx.replace.insns { mth, insn -> - if (insn is InvokeNode && insn.callMth.rawFullId == mthSignature) { - val str = getConstStr(insn.getArg(0)) - if (str != null) { - val resultStr = decode(str) - log.info { "Decode '$str' to '$resultStr' in $mth" } - return@insns ConstStringNode(resultStr) - } - } - null -} - -fun getConstStr(arg: InsnArg): String? { - val insn = when (arg) { - is InsnWrapArg -> arg.wrapInsn - is RegisterArg -> arg.assignInsn - else -> null - } - if (insn is ConstStringNode) { - return insn.string - } - return null -} - -/** - * Decompiled method, automatically converted to Kotlin by IntelliJ Idea - */ -fun decode(str: String): String { - val length = str.length - val cArr = CharArray(length) - var i = length - 1 - while (i >= 0) { - val i2 = i - 1 - cArr[i] = (str[i].code xor 'z'.code).toChar() - if (i2 < 0) { - break - } - i = i2 - 1 - cArr[i2] = (str[i2].code xor '\u000c'.code).toChar() - } - return String(cArr) -} diff --git a/jadx-plugins/jadx-script-kotlin/examples/stages.jadx.kts b/jadx-plugins/jadx-script-kotlin/examples/stages.jadx.kts deleted file mode 100644 index b12ef7ec6..000000000 --- a/jadx-plugins/jadx-script-kotlin/examples/stages.jadx.kts +++ /dev/null @@ -1,64 +0,0 @@ -// insert processing passes for different decompilation stages - -import jadx.core.dex.instructions.InsnType -import jadx.core.dex.nodes.IRegion -import java.lang.Integer.max - -val jadx = getJadxInstance() - -// print raw instructions -jadx.stages.rawInsns { mth, insns -> - log.info { "Instructions for method: $mth" } - for ((offset, insn) in insns.withIndex()) { - insn?.let { - log.info { " 0x${offset.hex()}: $insn" } - } - } -} - -// access method basic blocks -jadx.stages.mthBlocks { mth, blocks -> - // count invoke instructions - var invCount = 0 - for (block in blocks) { - for (insn in block.instructions) { - if (insn.type == InsnType.INVOKE) { - invCount++ - } - } - } - log.info { "Invokes count in method $mth = $invCount" } -} - -// access method regions -jadx.stages.mthRegions { mth, region -> - // recursively count max depth of nested regions - fun countRegionsDepth(region: IRegion): Int { - val subBlocks = region.subBlocks - if (subBlocks.isEmpty()) { - return 0 - } - var depth = 1 - for (block in subBlocks) { - if (block is IRegion) { - depth = max(depth, 1 + countRegionsDepth(block)) - } - } - return depth - } - - val depth = countRegionsDepth(region) - log.info { "Max region depth in method $mth = $depth" } - if (depth > 5) { - jadx.debug.printMethodRegions(mth, printInsns = true) - } -} - -jadx.afterLoad { - /* - Start full decompilation (optional): - 1. jadx-cli start decompilation automatically - 2. jadx-gui start decompilation only on class open or search, so you might need to force it - */ - // jadx.decompile.all() -} diff --git a/jadx-plugins/jadx-script-kotlin/src/main/kotlin/jadx/plugins/script/kotlin/JadxScriptKotlinPlugin.kt b/jadx-plugins/jadx-script-kotlin/src/main/kotlin/jadx/plugins/script/kotlin/JadxScriptKotlinPlugin.kt deleted file mode 100644 index eac9298eb..000000000 --- a/jadx-plugins/jadx-script-kotlin/src/main/kotlin/jadx/plugins/script/kotlin/JadxScriptKotlinPlugin.kt +++ /dev/null @@ -1,37 +0,0 @@ -package jadx.plugins.script.kotlin - -import jadx.api.plugins.JadxPlugin -import jadx.api.plugins.JadxPluginContext -import jadx.api.plugins.JadxPluginInfo -import jadx.api.plugins.JadxPluginInfoBuilder -import jadx.plugins.script.kotlin.gui.JadxScriptInputCategory -import jadx.plugins.script.kotlin.gui.JadxScriptOptionsUI -import jadx.plugins.script.kotlin.passes.JadxScriptAfterLoadPass -import jadx.plugins.script.kotlin.runtime.data.JadxScriptAllOptions - -class JadxScriptKotlinPlugin : JadxPlugin { - companion object { - const val PLUGIN_ID = "jadx-script-kotlin" - } - - override fun getPluginInfo(): JadxPluginInfo = JadxPluginInfoBuilder.pluginId(PLUGIN_ID) - .name("Jadx Script (Kotlin)") - .description("Scripting support for jadx using Kotlin script") - .homepage("https://github.com/jadx-decompiler/jadx-script-kotlin") - .requiredJadxVersion("1.5.4, r2596") - .provides("jadx-script") // conflict with bundled plugin from older jadx versions - .build() - - override fun init(context: JadxPluginContext) { - val scriptOptions = JadxScriptAllOptions() - context.registerOptions(scriptOptions) - val scripts = ScriptEval().process(context, scriptOptions) - if (scripts.isNotEmpty()) { - context.addPass(JadxScriptAfterLoadPass(scripts)) - context.guiContext?.let { guiContext -> - JadxScriptOptionsUI.setup(guiContext, scriptOptions) - JadxScriptInputCategory.register(context, guiContext) - } - } - } -} diff --git a/jadx-plugins/jadx-script-kotlin/src/main/kotlin/jadx/plugins/script/kotlin/ScriptCache.kt b/jadx-plugins/jadx-script-kotlin/src/main/kotlin/jadx/plugins/script/kotlin/ScriptCache.kt deleted file mode 100644 index 27502403a..000000000 --- a/jadx-plugins/jadx-script-kotlin/src/main/kotlin/jadx/plugins/script/kotlin/ScriptCache.kt +++ /dev/null @@ -1,111 +0,0 @@ -package jadx.plugins.script.kotlin - -import io.github.oshai.kotlinlogging.KotlinLogging -import jadx.api.plugins.JadxPluginContext -import jadx.core.utils.files.FileUtils -import java.nio.file.Path -import java.security.MessageDigest -import kotlin.io.path.exists -import kotlin.script.experimental.api.CompiledScript -import kotlin.script.experimental.api.ScriptCompilationConfiguration -import kotlin.script.experimental.api.SourceCode -import kotlin.script.experimental.jvm.CompiledJvmScriptsCache -import kotlin.script.experimental.jvm.impl.KJvmCompiledScript -import kotlin.script.experimental.jvmhost.loadScriptFromJar -import kotlin.script.experimental.jvmhost.saveToJar - -private val log = KotlinLogging.logger {} - -class ScriptCache { - private val enableCache = System.getProperty("JADX_SCRIPT_CACHE_ENABLE", "true").equals("true", ignoreCase = true) - - fun build(context: JadxPluginContext): CompiledJvmScriptsCache { - if (!enableCache) { - return CompiledJvmScriptsCache.NoCache - } - val cacheDir = getCacheDir(context) - log.debug { "script cache created in : $cacheDir" } - return JadxScriptsCache(cacheDir) - } - - /** - * Same as CompiledScriptJarsCache implementation, - * but remove all previous cache versions for the script with the same path and name. - * This should reduce old cache entries count - */ - class JadxScriptsCache(private val baseCacheDir: Path) : CompiledJvmScriptsCache { - override fun get( - script: SourceCode, - scriptCompilationConfiguration: ScriptCompilationConfiguration, - ): CompiledScript? { - val cacheDir = hashDir(baseCacheDir, script) - val file = hashFile(cacheDir, script, scriptCompilationConfiguration) - if (file.exists()) { - file.toFile().loadScriptFromJar().let { - log.debug { "loaded script from cache: $file" } - return it - } - } - log.debug { "script not found in cache: $file" } - FileUtils.deleteDirIfExists(cacheDir) - return null - } - - override fun store( - compiledScript: CompiledScript, - script: SourceCode, - scriptCompilationConfiguration: ScriptCompilationConfiguration, - ) { - val jvmScript = (compiledScript as? KJvmCompiledScript) - ?: throw IllegalArgumentException("Unsupported script type ${compiledScript::class.java.name}") - - val cacheDir = hashDir(baseCacheDir, script) - val file = hashFile(cacheDir, script, scriptCompilationConfiguration) - - FileUtils.deleteDirIfExists(cacheDir) - FileUtils.makeDirs(cacheDir) - jvmScript.saveToJar(file.toFile()) - log.debug { "script cached: $file" } - } - } - - private fun getCacheDir(context: JadxPluginContext): Path { - val cacheBaseDir = context.files().pluginCacheDir.resolve("compiled") - FileUtils.makeDirs(cacheBaseDir) - return cacheBaseDir - } - - companion object { - private fun hashDir(baseCacheDir: Path, script: SourceCode): Path { - if (script.name == null && script.locationId == null) { - return baseCacheDir.resolve("tmp") - } - val digest = MessageDigest.getInstance("MD5") - digest.add(script.name) - digest.add(script.locationId) - return baseCacheDir.resolve(digest.digest().toHexString()) - } - - private fun hashFile( - cacheDir: Path, - script: SourceCode, - scriptCompilationConfiguration: ScriptCompilationConfiguration, - ): Path { - val digest = MessageDigest.getInstance("MD5") - digest.add(script.text) - scriptCompilationConfiguration.notTransientData.entries - .sortedBy { it.key.name } - .forEach { - digest.add(it.key.name) - digest.add(it.value.toString()) - } - return cacheDir.resolve(digest.digest().toHexString() + ".jar") - } - - private fun MessageDigest.add(str: String?) { - str?.let { this.update(it.toByteArray()) } - } - - private fun ByteArray.toHexString(): String = joinToString("", transform = { "%02x".format(it) }) - } -} diff --git a/jadx-plugins/jadx-script-kotlin/src/main/kotlin/jadx/plugins/script/kotlin/ScriptEval.kt b/jadx-plugins/jadx-script-kotlin/src/main/kotlin/jadx/plugins/script/kotlin/ScriptEval.kt deleted file mode 100644 index 6f37da1a4..000000000 --- a/jadx-plugins/jadx-script-kotlin/src/main/kotlin/jadx/plugins/script/kotlin/ScriptEval.kt +++ /dev/null @@ -1,212 +0,0 @@ -package jadx.plugins.script.kotlin - -import io.github.oshai.kotlinlogging.KotlinLogging -import jadx.api.plugins.JadxPluginContext -import jadx.plugins.script.kotlin.runtime.JadxScriptData -import jadx.plugins.script.kotlin.runtime.JadxScriptTemplate -import jadx.plugins.script.kotlin.runtime.data.JadxScriptAllOptions -import kotlinx.coroutines.runBlocking -import org.jetbrains.kotlin.scripting.resolve.skipExtensionsResolutionForImplicitsExceptInnermost -import java.io.File -import kotlin.script.experimental.api.EvaluationResult -import kotlin.script.experimental.api.KotlinType -import kotlin.script.experimental.api.ResultValue -import kotlin.script.experimental.api.ResultWithDiagnostics -import kotlin.script.experimental.api.ScriptAcceptedLocation -import kotlin.script.experimental.api.ScriptCollectedData -import kotlin.script.experimental.api.ScriptCompilationConfiguration -import kotlin.script.experimental.api.ScriptConfigurationRefinementContext -import kotlin.script.experimental.api.ScriptDiagnostic.Severity -import kotlin.script.experimental.api.ScriptEvaluationConfiguration -import kotlin.script.experimental.api.acceptedLocations -import kotlin.script.experimental.api.asSuccess -import kotlin.script.experimental.api.collectedAnnotations -import kotlin.script.experimental.api.compilationConfiguration -import kotlin.script.experimental.api.compilerOptions -import kotlin.script.experimental.api.constructorArgs -import kotlin.script.experimental.api.defaultIdentifier -import kotlin.script.experimental.api.defaultImports -import kotlin.script.experimental.api.displayName -import kotlin.script.experimental.api.fileExtension -import kotlin.script.experimental.api.filePathPattern -import kotlin.script.experimental.api.hostConfiguration -import kotlin.script.experimental.api.ide -import kotlin.script.experimental.api.implicitReceivers -import kotlin.script.experimental.api.isStandalone -import kotlin.script.experimental.api.onSuccess -import kotlin.script.experimental.api.refineConfiguration -import kotlin.script.experimental.api.with -import kotlin.script.experimental.dependencies.CompoundDependenciesResolver -import kotlin.script.experimental.dependencies.DependsOn -import kotlin.script.experimental.dependencies.FileSystemDependenciesResolver -import kotlin.script.experimental.dependencies.Repository -import kotlin.script.experimental.dependencies.maven.MavenDependenciesResolver -import kotlin.script.experimental.dependencies.resolveFromScriptSourceAnnotations -import kotlin.script.experimental.host.ScriptingHostConfiguration -import kotlin.script.experimental.host.getScriptingClass -import kotlin.script.experimental.host.toScriptSource -import kotlin.script.experimental.host.with -import kotlin.script.experimental.jvm.JvmGetScriptingClass -import kotlin.script.experimental.jvm.baseClassLoader -import kotlin.script.experimental.jvm.compilationCache -import kotlin.script.experimental.jvm.dependenciesFromCurrentContext -import kotlin.script.experimental.jvm.jvm -import kotlin.script.experimental.jvm.updateClasspath -import kotlin.script.experimental.jvmhost.BasicJvmScriptingHost -import kotlin.system.measureTimeMillis -import kotlin.time.DurationUnit -import kotlin.time.toDuration - -private val log = KotlinLogging.logger {} - -object DefCompileConf : ScriptCompilationConfiguration(ScriptEval.buildDefaultCompileConf()) - -class ScriptEval { - companion object { - fun buildDefaultCompileConf(): ScriptCompilationConfiguration { - val scriptEval = ScriptEval() - val hostConf = scriptEval.buildHostConf(null) - return scriptEval.buildCompileConf(hostConf) - } - } - - fun process(context: JadxPluginContext, scriptOptions: JadxScriptAllOptions): List { - val jadx = context.decompiler - val scripts = jadx.args.inputFiles.filter { f -> f.name.endsWith(".jadx.kts") } - if (scripts.isEmpty()) { - return emptyList() - } - val scriptDataList = mutableListOf() - for (scriptFile in scripts) { - val scriptData = JadxScriptData(jadx, context, scriptOptions, scriptFile) - scriptDataList.add(scriptData) - eval(context, scriptData) - } - return scriptDataList - } - - private fun eval( - context: JadxPluginContext, - scriptData: JadxScriptData, - ) { - scriptData.log.debug { "Loading script: ${scriptData.scriptFile.absolutePath}" } - val hostConf = buildHostConf(context) - val compileConf = buildCompileConf(hostConf) - val evalConf = buildEvalConf(scriptData, compileConf) - val scriptingHost = BasicJvmScriptingHost(hostConf) - val execTime = measureTimeMillis { - val result = scriptingHost.eval(scriptData.scriptFile.toScriptSource(), compileConf, evalConf) - processEvalResult(result, scriptData) - } - scriptData.log.debug { "Script '${scriptData.scriptName}' executed in ${execTime.toDuration(DurationUnit.MILLISECONDS)}" } - } - - private fun processEvalResult(res: ResultWithDiagnostics, scriptData: JadxScriptData) { - val log = scriptData.log - for (r in res.reports) { - val msg = r.render(withSeverity = false) - when (r.severity) { - Severity.FATAL, Severity.ERROR -> log.error(r.exception) { "Script execution error: $msg" } - Severity.WARNING -> log.warn { "Script execution issue: $msg" } - Severity.INFO -> log.info { "Script report: $msg" } - Severity.DEBUG -> log.debug { "Script debug: $msg" } - } - } - when (res) { - is ResultWithDiagnostics.Success -> { - when (val retVal = res.value.returnValue) { - is ResultValue.Error -> log.error(retVal.error) { "Script execution error:" } - is ResultValue.Value -> log.info { "Script execution result: $retVal" } - is ResultValue.Unit -> {} - ResultValue.NotEvaluated -> {} - } - } - - is ResultWithDiagnostics.Failure -> { - scriptData.error = true - log.error { "Script execution failed: ${scriptData.scriptName}" } - } - } - } - - fun buildHostConf(context: JadxPluginContext?) = ScriptingHostConfiguration { - jvm { - getScriptingClass(JvmGetScriptingClass()) - baseClassLoader.put(JadxScriptTemplate::class.java.classLoader) - context?.let { - compilationCache(ScriptCache().build(context)) - } - } - } - - fun buildCompileConf(scriptingHostConf: ScriptingHostConfiguration) = ScriptCompilationConfiguration { - hostConfiguration.put(scriptingHostConf) - - displayName.put("Jadx script") - defaultIdentifier.put("JadxScript") - - fileExtension.put("jadx.kts") - filePathPattern.put(".*\\.jadx\\.kts") - - val receiversTypes = listOf(KotlinType(JadxScriptTemplate::class)) - implicitReceivers(receiversTypes) - skipExtensionsResolutionForImplicitsExceptInnermost(receiversTypes) - - jvm { - dependenciesFromCurrentContext( - wholeClasspath = true, - ) - } - - addBaseClass() - defaultImports(DependsOn::class, Repository::class) - - refineConfiguration { - onAnnotations(DependsOn::class, Repository::class, handler = ::configureMavenDepsOnAnnotations) - } - - ide { - acceptedLocations(ScriptAcceptedLocation.Everywhere) - } - - isStandalone(true) - - // forcing compiler to not use modules while building script classpath - // because shadow jar remove all modules-info.class (https://github.com/GradleUp/shadow/issues/710) - compilerOptions.append("-Xjdk-release=1.8") - } - - inline fun ScriptCompilationConfiguration.Builder.addBaseClass() { - val kClass = T::class - defaultImports.append(kClass.java.name) - hostConfiguration.update { - it.with { - this[jvm.baseClassLoader] = kClass.java.classLoader - } - } - } - - fun buildEvalConf(scriptData: JadxScriptData, compileConf: ScriptCompilationConfiguration): ScriptEvaluationConfiguration { - return ScriptEvaluationConfiguration { - hostConfiguration.put(compileConf[hostConfiguration]!!) - compilationConfiguration.put(compileConf) - constructorArgs(JadxScriptTemplate(scriptData)) - } - } - - private val resolver = CompoundDependenciesResolver(FileSystemDependenciesResolver(), MavenDependenciesResolver()) - - fun configureMavenDepsOnAnnotations(context: ScriptConfigurationRefinementContext): ResultWithDiagnostics { - val annotations = context.collectedData?.get(ScriptCollectedData.collectedAnnotations) - ?.takeIf { it.isNotEmpty() } - ?: return context.compilationConfiguration.asSuccess() - return runBlocking { - resolver.resolveFromScriptSourceAnnotations(annotations) - }.onSuccess { files: List -> - log.debug { "add script dependency: $files" } - context.compilationConfiguration.with { - updateClasspath(files) - }.asSuccess() - } - } -} diff --git a/jadx-plugins/jadx-script-kotlin/src/main/kotlin/jadx/plugins/script/kotlin/ScriptServices.kt b/jadx-plugins/jadx-script-kotlin/src/main/kotlin/jadx/plugins/script/kotlin/ScriptServices.kt deleted file mode 100644 index c70ce5f82..000000000 --- a/jadx-plugins/jadx-script-kotlin/src/main/kotlin/jadx/plugins/script/kotlin/ScriptServices.kt +++ /dev/null @@ -1,82 +0,0 @@ -package jadx.plugins.script.kotlin - -import jadx.api.plugins.JadxPluginContext -import kotlinx.coroutines.runBlocking -import org.jetbrains.kotlin.scripting.compiler.plugin.services.FirReplHistoryProviderImpl -import org.jetbrains.kotlin.scripting.compiler.plugin.services.firReplHistoryProvider -import org.jetbrains.kotlin.scripting.compiler.plugin.services.isReplSnippetSource -import org.jetbrains.kotlin.scripting.ide_services.compiler.KJvmReplCompilerWithIdeServices -import kotlin.script.experimental.api.ReplAnalyzerResult -import kotlin.script.experimental.api.ScriptCompilationConfiguration -import kotlin.script.experimental.api.ScriptDiagnostic -import kotlin.script.experimental.api.SourceCodeCompletionVariant -import kotlin.script.experimental.api.analysisDiagnostics -import kotlin.script.experimental.api.renderedResultType -import kotlin.script.experimental.api.repl -import kotlin.script.experimental.api.valueOrNull -import kotlin.script.experimental.host.toScriptSource -import kotlin.script.experimental.host.with -import kotlin.script.experimental.jvm.util.isError -import kotlin.script.experimental.jvm.util.toSourceCodePosition - -data class ScriptCompletionResult( - val completions: List, - val reports: MutableList, -) - -data class ScriptAnalyzeResult( - val success: Boolean, - val issues: List, - val renderType: String?, -) - -class ScriptServices(pluginContext: JadxPluginContext? = null) { - companion object { - const val AUTO_COMPLETE_INSERT_STR = "ABCDEF" // defined at KJvmReplCompleter.INSERTED_STRING - } - - private val compileConf: ScriptCompilationConfiguration - private val replCompiler: KJvmReplCompilerWithIdeServices - - init { - val scriptEval = ScriptEval() - val hostConf = scriptEval.buildHostConf(pluginContext) - hostConf.with { - repl { - firReplHistoryProvider(FirReplHistoryProviderImpl()) - isReplSnippetSource { sourceFile, _ -> - sourceFile?.name?.endsWith(".jadx.kts", ignoreCase = true) ?: false - } - } - } - compileConf = scriptEval.buildCompileConf(hostConf) - replCompiler = KJvmReplCompilerWithIdeServices(hostConf) - } - - fun complete(scriptName: String, code: String, cursor: Int): ScriptCompletionResult { - val snippet = code.toScriptSource(scriptName) - val result = runBlocking { - replCompiler.complete(snippet, cursor.toSourceCodePosition(snippet), compileConf) - } - return ScriptCompletionResult( - completions = result.valueOrNull()?.toList() ?: emptyList(), - reports = result.reports.toMutableList(), - ) - } - - fun analyze(scriptName: String, code: String): ScriptAnalyzeResult { - val sourceCode = code.toScriptSource(scriptName) - val result = runBlocking { - replCompiler.analyze(sourceCode, 0.toSourceCodePosition(sourceCode), compileConf) - } - val analyzerResult = result.valueOrNull() - val issues = mutableListOf() - analyzerResult?.get(ReplAnalyzerResult.analysisDiagnostics)?.let(issues::addAll) - issues.addAll(result.reports) - return ScriptAnalyzeResult( - success = !result.isError(), - issues = issues, - renderType = analyzerResult?.get(ReplAnalyzerResult.renderedResultType), - ) - } -} diff --git a/jadx-plugins/jadx-script-kotlin/src/main/kotlin/jadx/plugins/script/kotlin/gui/JInputScript.kt b/jadx-plugins/jadx-script-kotlin/src/main/kotlin/jadx/plugins/script/kotlin/gui/JInputScript.kt deleted file mode 100644 index 3d2785e98..000000000 --- a/jadx-plugins/jadx-script-kotlin/src/main/kotlin/jadx/plugins/script/kotlin/gui/JInputScript.kt +++ /dev/null @@ -1,92 +0,0 @@ -package jadx.plugins.script.kotlin.gui - -import io.github.oshai.kotlinlogging.KotlinLogging -import jadx.api.ICodeInfo -import jadx.api.impl.SimpleCodeInfo -import jadx.api.plugins.JadxPluginContext -import jadx.core.utils.exceptions.JadxRuntimeException -import jadx.core.utils.files.FileUtils -import jadx.gui.treemodel.JClass -import jadx.gui.treemodel.JEditableNode -import jadx.gui.ui.MainWindow -import jadx.gui.ui.panel.ContentPanel -import jadx.gui.ui.tab.TabbedPane -import jadx.gui.utils.NLS -import jadx.gui.utils.UiUtils -import jadx.gui.utils.ui.SimpleMenuItem -import org.fife.ui.rsyntaxtextarea.SyntaxConstants -import java.nio.file.Path -import javax.swing.Icon -import javax.swing.ImageIcon -import javax.swing.JPopupMenu - -private val log = KotlinLogging.logger {} - -class JInputScript( - val pluginContext: JadxPluginContext, - private val scriptPath: Path, -) : JEditableNode() { - companion object { - private val SCRIPT_ICON: ImageIcon = UiUtils.openSvgIcon("nodes/kotlin_script") - } - - private val name: String = scriptPath.fileName.toString().replace(".jadx.kts", "") - - override fun hasContent(): Boolean { - return true - } - - override fun getContentPanel(tabbedPane: TabbedPane): ContentPanel { - return ScriptContentPanel(pluginContext, tabbedPane, this) - } - - override fun getCodeInfo(): ICodeInfo { - try { - return SimpleCodeInfo(FileUtils.readFile(scriptPath)) - } catch (e: Exception) { - throw JadxRuntimeException("Failed to read script file: " + scriptPath.toAbsolutePath(), e) - } - } - - override fun save(newContent: String?) { - try { - FileUtils.writeFile(scriptPath, newContent) - log.debug { "Script saved: ${scriptPath.toAbsolutePath()}" } - } catch (e: Exception) { - throw JadxRuntimeException("Failed to write script file: " + scriptPath.toAbsolutePath(), e) - } - } - - override fun onTreePopupMenu(mainWindow: MainWindow): JPopupMenu { - val menu = JPopupMenu() - menu.add(SimpleMenuItem(NLS.str("popup.add_scripts")) { mainWindow.addFiles() }) - menu.add(SimpleMenuItem(NLS.str("popup.new_script")) { mainWindow.addNewScript() }) - menu.add(SimpleMenuItem(NLS.str("popup.remove")) { mainWindow.removeInput(scriptPath) }) - menu.add(SimpleMenuItem(NLS.str("popup.rename")) { mainWindow.renameInput(scriptPath) }) - return menu - } - - override fun getSyntaxName(): String { - return SyntaxConstants.SYNTAX_STYLE_KOTLIN - } - - override fun getJParent(): JClass? { - return null - } - - override fun getIcon(): Icon { - return SCRIPT_ICON - } - - override fun getName(): String { - return name - } - - override fun makeString(): String { - return name - } - - override fun getTooltip(): String { - return scriptPath.normalize().toAbsolutePath().toString() - } -} diff --git a/jadx-plugins/jadx-script-kotlin/src/main/kotlin/jadx/plugins/script/kotlin/gui/JInputScripts.kt b/jadx-plugins/jadx-script-kotlin/src/main/kotlin/jadx/plugins/script/kotlin/gui/JInputScripts.kt deleted file mode 100644 index 9b4ce2b8a..000000000 --- a/jadx-plugins/jadx-script-kotlin/src/main/kotlin/jadx/plugins/script/kotlin/gui/JInputScripts.kt +++ /dev/null @@ -1,51 +0,0 @@ -package jadx.plugins.script.kotlin.gui - -import jadx.api.plugins.JadxPluginContext -import jadx.gui.treemodel.JClass -import jadx.gui.treemodel.JNode -import jadx.gui.ui.MainWindow -import jadx.gui.utils.NLS -import jadx.gui.utils.UiUtils -import jadx.gui.utils.ui.SimpleMenuItem -import java.nio.file.Path -import javax.swing.Icon -import javax.swing.ImageIcon -import javax.swing.JPopupMenu - -class JInputScripts( - pluginContext: JadxPluginContext, - scripts: List, -) : JNode() { - companion object { - private val INPUT_SCRIPTS_ICON: ImageIcon = UiUtils.openSvgIcon("nodes/scriptsModel") - } - - init { - for (script in scripts) { - add(JInputScript(pluginContext, script)) - } - } - - override fun onTreePopupMenu(mainWindow: MainWindow): JPopupMenu { - val menu = JPopupMenu() - menu.add(SimpleMenuItem(NLS.str("popup.add_scripts")) { mainWindow.addFiles() }) - menu.add(SimpleMenuItem(NLS.str("popup.new_script")) { mainWindow.addNewScript() }) - return menu - } - - override fun getJParent(): JClass? { - return null - } - - override fun getIcon(): Icon { - return INPUT_SCRIPTS_ICON - } - - override fun getID(): String { - return "JInputScripts" - } - - override fun makeString(): String { - return NLS.str("tree.input_scripts") - } -} diff --git a/jadx-plugins/jadx-script-kotlin/src/main/kotlin/jadx/plugins/script/kotlin/gui/JadxScriptInputCategory.kt b/jadx-plugins/jadx-script-kotlin/src/main/kotlin/jadx/plugins/script/kotlin/gui/JadxScriptInputCategory.kt deleted file mode 100644 index cd98aa12d..000000000 --- a/jadx-plugins/jadx-script-kotlin/src/main/kotlin/jadx/plugins/script/kotlin/gui/JadxScriptInputCategory.kt +++ /dev/null @@ -1,44 +0,0 @@ -package jadx.plugins.script.kotlin.gui - -import jadx.api.plugins.JadxPluginContext -import jadx.api.plugins.gui.JadxGuiContext -import jadx.gui.plugins.context.GuiPluginContext -import jadx.gui.plugins.context.ITreeInputCategory -import jadx.gui.settings.data.ITabStatePersist -import jadx.gui.treemodel.JNode -import java.nio.file.Path - -object JadxScriptInputCategory { - fun register(pluginContext: JadxPluginContext, guiContext: JadxGuiContext) { - val internalContext = guiContext as GuiPluginContext - val inputCategory = InputScriptsBuilder(pluginContext) - internalContext.registerTreeInputCategory(inputCategory) - internalContext.registerTabStatePersistAdapter(InputScriptTabStatePersist(inputCategory)) - } -} - -class InputScriptsBuilder(private val pluginContext: JadxPluginContext) : ITreeInputCategory { - var scriptsRootNode: JInputScripts? = null - - override fun filesFilter(file: Path): Boolean { - return file.fileName.toString().endsWith(".jadx.kts", ignoreCase = true) - } - - override fun buildInputNode(files: List): JNode { - val scriptsNode = JInputScripts(pluginContext, files) - scriptsRootNode = scriptsNode - return scriptsNode - } -} - -class InputScriptTabStatePersist(private val scriptsBuilder: InputScriptsBuilder) : ITabStatePersist { - override fun getNodeClass() = JInputScript::class.java - - override fun save(node: JNode): String { - return node.name - } - - override fun load(nodeName: String): JNode? { - return scriptsBuilder.scriptsRootNode?.searchNode { it.name.equals(nodeName) } - } -} diff --git a/jadx-plugins/jadx-script-kotlin/src/main/kotlin/jadx/plugins/script/kotlin/gui/JadxScriptOptionsUI.kt b/jadx-plugins/jadx-script-kotlin/src/main/kotlin/jadx/plugins/script/kotlin/gui/JadxScriptOptionsUI.kt deleted file mode 100644 index 604756eb8..000000000 --- a/jadx-plugins/jadx-script-kotlin/src/main/kotlin/jadx/plugins/script/kotlin/gui/JadxScriptOptionsUI.kt +++ /dev/null @@ -1,30 +0,0 @@ -package jadx.plugins.script.kotlin.gui - -import jadx.api.plugins.gui.ISettingsGroup -import jadx.api.plugins.gui.JadxGuiContext -import jadx.plugins.script.kotlin.runtime.data.JadxScriptAllOptions -import javax.swing.JPanel - -object JadxScriptOptionsUI { - fun setup(guiContext: JadxGuiContext, scriptOptions: JadxScriptAllOptions) { - guiContext.settings().setCustomSettingsGroup(ScriptOptionsRootGroup(guiContext, scriptOptions)) - } -} - -private class ScriptOptionsRootGroup( - private val guiContext: JadxGuiContext, - private val scriptOptions: JadxScriptAllOptions, -) : ISettingsGroup { - - override fun getTitle() = "Scripts" - - override fun buildComponent() = JPanel() // empty panel for root node - - override fun getSubGroups(): List { - val settings = guiContext.settings() - return scriptOptions.descriptions - .groupBy { it.script } - .map { (script, options) -> settings.buildSettingsGroupForOptions(script, options) } - .toList() - } -} diff --git a/jadx-plugins/jadx-script-kotlin/src/main/kotlin/jadx/plugins/script/kotlin/gui/KtLintUtils.kt b/jadx-plugins/jadx-script-kotlin/src/main/kotlin/jadx/plugins/script/kotlin/gui/KtLintUtils.kt deleted file mode 100644 index b526ab38b..000000000 --- a/jadx-plugins/jadx-script-kotlin/src/main/kotlin/jadx/plugins/script/kotlin/gui/KtLintUtils.kt +++ /dev/null @@ -1,55 +0,0 @@ -package jadx.plugins.script.kotlin.gui - -import com.pinterest.ktlint.rule.engine.api.Code -import com.pinterest.ktlint.rule.engine.api.EditorConfigOverride -import com.pinterest.ktlint.rule.engine.api.KtLintRuleEngine -import com.pinterest.ktlint.rule.engine.core.api.AutocorrectDecision -import com.pinterest.ktlint.rule.engine.core.api.editorconfig.CODE_STYLE_PROPERTY -import com.pinterest.ktlint.rule.engine.core.api.editorconfig.CodeStyleValue -import com.pinterest.ktlint.rule.engine.core.api.editorconfig.INDENT_STYLE_PROPERTY -import com.pinterest.ktlint.ruleset.standard.StandardRuleSetProvider -import org.ec4j.core.model.PropertyType - -data class JadxLintError( - val line: Int, - val col: Int, - val ruleId: String, - val detail: String, -) - -object KtLintUtils { - private val ktLint by lazy { - KtLintRuleEngine( - ruleProviders = StandardRuleSetProvider().getRuleProviders(), - editorConfigOverride = EditorConfigOverride.from( - CODE_STYLE_PROPERTY to CodeStyleValue.intellij_idea, - INDENT_STYLE_PROPERTY to PropertyType.IndentStyleValue.tab, - ), - ) - } - - fun format(content: String): String { - val code = Code.fromSnippet(content, script = true) - return ktLint.format( - code, - rerunAfterAutocorrect = true, - defaultAutocorrect = true, - ) { AutocorrectDecision.ALLOW_AUTOCORRECT } - } - - fun lint(content: String): List { - val errors = mutableListOf() - val code = Code.fromSnippet(content, script = true) - ktLint.lint(code) { lintError -> - errors.add( - JadxLintError( - line = lintError.line, - col = lintError.col, - ruleId = lintError.ruleId.value, - detail = lintError.detail, - ), - ) - } - return errors - } -} diff --git a/jadx-plugins/jadx-script-kotlin/src/main/kotlin/jadx/plugins/script/kotlin/gui/ScriptCodeArea.kt b/jadx-plugins/jadx-script-kotlin/src/main/kotlin/jadx/plugins/script/kotlin/gui/ScriptCodeArea.kt deleted file mode 100644 index f657c2f7c..000000000 --- a/jadx-plugins/jadx-script-kotlin/src/main/kotlin/jadx/plugins/script/kotlin/gui/ScriptCodeArea.kt +++ /dev/null @@ -1,76 +0,0 @@ -package jadx.plugins.script.kotlin.gui - -import jadx.api.ICodeInfo -import jadx.gui.jobs.IBackgroundTask -import jadx.gui.jobs.LoadTask -import jadx.gui.settings.JadxSettings -import jadx.gui.ui.action.JadxAutoCompletion -import jadx.gui.ui.codearea.AbstractCodeArea -import jadx.gui.ui.panel.ContentPanel -import jadx.gui.utils.shortcut.ShortcutsController -import org.fife.ui.autocomplete.AutoCompletion - -class ScriptCodeArea(contentPanel: ContentPanel, val scriptNode: JInputScript) : - AbstractCodeArea(contentPanel, scriptNode) { - private val autoCompletion: AutoCompletion - private val shortcutsController: ShortcutsController - - init { - setSyntaxEditingStyle(scriptNode.syntaxName) - isCodeFoldingEnabled = true - closeCurlyBraces = true - - shortcutsController = contentPanel.mainWindow.shortcutsController - val settings = contentPanel.mainWindow.settings - autoCompletion = addAutoComplete(settings) - } - - private fun addAutoComplete(settings: JadxSettings): AutoCompletion { - val provider = ScriptCompleteProvider(this, scriptNode.pluginContext) - provider.setAutoActivationRules(false, ".") - val ac = JadxAutoCompletion(provider) - ac.setListCellRenderer(ScriptCompletionRenderer(settings)) - ac.isAutoActivationEnabled = true - ac.autoCompleteSingleChoices = true - ac.install(this) - shortcutsController.bindImmediate(ac) - return ac - } - - override fun getCodeInfo(): ICodeInfo { - return node.codeInfo - } - - override fun getLoadTask(): IBackgroundTask { - return LoadTask( - { node.codeInfo.getCodeStr() }, - { code -> - text = code - setCaretPosition(0) - setLoaded() - }, - ) - } - - override fun refresh() { - text = node.codeInfo.getCodeStr() - } - - fun updateCode(newCode: String?) { - val caretPos = caretPosition - text = newCode - setCaretPosition(caretPos) - scriptNode.isChanged = true - } - - fun save() { - scriptNode.save(getText()) - scriptNode.isChanged = false - } - - override fun dispose() { - shortcutsController.unbindActionsForComponent(this) - autoCompletion.uninstall() - super.dispose() - } -} diff --git a/jadx-plugins/jadx-script-kotlin/src/main/kotlin/jadx/plugins/script/kotlin/gui/ScriptCompleteProvider.kt b/jadx-plugins/jadx-script-kotlin/src/main/kotlin/jadx/plugins/script/kotlin/gui/ScriptCompleteProvider.kt deleted file mode 100644 index 8e885d889..000000000 --- a/jadx-plugins/jadx-script-kotlin/src/main/kotlin/jadx/plugins/script/kotlin/gui/ScriptCompleteProvider.kt +++ /dev/null @@ -1,137 +0,0 @@ -package jadx.plugins.script.kotlin.gui - -import io.github.oshai.kotlinlogging.KotlinLogging -import jadx.api.plugins.JadxPluginContext -import jadx.core.utils.exceptions.JadxRuntimeException -import jadx.gui.ui.codearea.AbstractCodeArea -import jadx.gui.utils.Icons -import jadx.plugins.script.kotlin.ScriptCompletionResult -import jadx.plugins.script.kotlin.ScriptServices -import jadx.plugins.script.kotlin.ScriptServices.Companion.AUTO_COMPLETE_INSERT_STR -import org.fife.ui.autocomplete.Completion -import org.fife.ui.autocomplete.CompletionProviderBase -import org.fife.ui.autocomplete.ParameterizedCompletion -import java.awt.Point -import javax.swing.Icon -import javax.swing.text.BadLocationException -import javax.swing.text.JTextComponent -import kotlin.script.experimental.api.ScriptDiagnostic -import kotlin.script.experimental.api.SourceCodeCompletionVariant - -private val log = KotlinLogging.logger {} - -private val ICONS_MAP = mapOf( - "class" to Icons.CLASS, - "method" to Icons.METHOD, - "field" to Icons.FIELD, - "property" to Icons.PROPERTY, - "parameter" to Icons.PARAMETER, - "package" to Icons.PACKAGE, -) - -class ScriptCompleteProvider( - private val codeArea: AbstractCodeArea, - private val pluginContext: JadxPluginContext, -) : CompletionProviderBase() { - - private val completions: List - get() { - try { - val code = codeArea.getText() - val caretPos = codeArea.caretPosition - val scriptServices = ScriptServices(pluginContext) - val scriptName = codeArea.getNode().getName() - val result = scriptServices.complete(scriptName, code, caretPos) - if (result.completions.isEmpty()) { - return listOf() - } - val replacePos = getReplacePos(caretPos, result) - if (!result.reports.isEmpty()) { - log.debug { "Script completion reports: ${result.reports}" } - } - log.debug { "Completions:\n${result.completions.joinToString(separator = "\n")}" } - return convertCompletions(result.completions, code, replacePos) - } catch (e: Exception) { - log.error(e) { "Code completion failed" } - return listOf() - } - } - - private fun convertCompletions( - completions: List, - code: String, - replacePos: Int, - ): List { - val count = completions.size - val list = ArrayList(count) - for (i in 0.. - if (report.severity == ScriptDiagnostic.Severity.ERROR) { - report.location?.let { location -> - location.start.line == line && report.message.endsWith(AUTO_COMPLETE_INSERT_STR) - } ?: false - } else { - false - } - } - if (completeReport == null) { - log.warn { "Failed to find completion report in: ${result.reports}" } - return caretPos - } - result.reports.remove(completeReport) - val col = caretPos - lineStart + 1 - return caretPos - (col - completeReport.location!!.start.col) - } - - override fun getAlreadyEnteredText(comp: JTextComponent?): String? { - try { - val pos = codeArea.caretPosition - return codeArea.getText(0, pos) - } catch (e: Exception) { - throw JadxRuntimeException("Failed to get text before caret", e) - } - } - - override fun getCompletionsAt(comp: JTextComponent, p: Point): List { - return this.completions - } - - override fun getCompletionsImpl(comp: JTextComponent): List { - return this.completions - } - - override fun getParameterizedCompletions(tc: JTextComponent): List? { - return null - } -} diff --git a/jadx-plugins/jadx-script-kotlin/src/main/kotlin/jadx/plugins/script/kotlin/gui/ScriptCompletionData.kt b/jadx-plugins/jadx-script-kotlin/src/main/kotlin/jadx/plugins/script/kotlin/gui/ScriptCompletionData.kt deleted file mode 100644 index 2e4398763..000000000 --- a/jadx-plugins/jadx-script-kotlin/src/main/kotlin/jadx/plugins/script/kotlin/gui/ScriptCompletionData.kt +++ /dev/null @@ -1,58 +0,0 @@ -package jadx.plugins.script.kotlin.gui - -import org.fife.ui.autocomplete.Completion -import org.fife.ui.autocomplete.CompletionProvider -import javax.swing.Icon -import javax.swing.text.JTextComponent - -class ScriptCompletionData( - private val provider: CompletionProvider, - private val relevance: Int, - private val input: String, - private val code: String, - private val replacePos: Int, - private val icon: Icon, - private val toolTip: String, - private val summary: String, -) : Completion { - - override fun getInputText(): String { - return input - } - - override fun getProvider(): CompletionProvider { - return provider - } - - override fun getAlreadyEntered(comp: JTextComponent?): String? { - return provider.getAlreadyEnteredText(comp) - } - - override fun getRelevance(): Int { - return relevance - } - - override fun getReplacementText(): String { - return code.substring(0, replacePos) + input - } - - override fun getIcon(): Icon { - return icon - } - - override fun getSummary(): String { - return summary - } - - override fun getToolTipText(): String { - return toolTip - } - - override fun compareTo(other: Completion): Int { - return relevance.compareTo(other.relevance) - } - - override fun toString(): String { - return input - } -} diff --git a/jadx-plugins/jadx-script-kotlin/src/main/kotlin/jadx/plugins/script/kotlin/gui/ScriptCompletionRenderer.kt b/jadx-plugins/jadx-script-kotlin/src/main/kotlin/jadx/plugins/script/kotlin/gui/ScriptCompletionRenderer.kt deleted file mode 100644 index f09e5a171..000000000 --- a/jadx-plugins/jadx-script-kotlin/src/main/kotlin/jadx/plugins/script/kotlin/gui/ScriptCompletionRenderer.kt +++ /dev/null @@ -1,26 +0,0 @@ -package jadx.plugins.script.kotlin.gui - -import jadx.gui.settings.JadxSettings -import jadx.gui.utils.UiUtils -import org.fife.ui.autocomplete.Completion -import org.fife.ui.autocomplete.CompletionCellRenderer -import javax.swing.JList - -class ScriptCompletionRenderer(settings: JadxSettings) : CompletionCellRenderer() { - init { - displayFont = settings.codeFont - } - - override fun prepareForOtherCompletion( - list: JList<*>?, - c: Completion?, - index: Int, - selected: Boolean, - hasFocus: Boolean, - ) { - val cmpl = c as ScriptCompletionData - setText( - UiUtils.wrapHtml((UiUtils.escapeHtml(cmpl.inputText) + " " + UiUtils.fadeHtml(UiUtils.escapeHtml(cmpl.summary)))), - ) - } -} diff --git a/jadx-plugins/jadx-script-kotlin/src/main/kotlin/jadx/plugins/script/kotlin/gui/ScriptContentPanel.kt b/jadx-plugins/jadx-script-kotlin/src/main/kotlin/jadx/plugins/script/kotlin/gui/ScriptContentPanel.kt deleted file mode 100644 index a65c8cbb4..000000000 --- a/jadx-plugins/jadx-script-kotlin/src/main/kotlin/jadx/plugins/script/kotlin/gui/ScriptContentPanel.kt +++ /dev/null @@ -1,251 +0,0 @@ -package jadx.plugins.script.kotlin.gui - -import jadx.api.plugins.JadxPluginContext -import jadx.gui.logs.LogOptions -import jadx.gui.settings.LineNumbersMode -import jadx.gui.ui.action.ActionModel -import jadx.gui.ui.action.JadxGuiAction -import jadx.gui.ui.codearea.AbstractCodeArea -import jadx.gui.ui.codearea.AbstractCodeContentPanel -import jadx.gui.ui.codearea.SearchBar -import jadx.gui.ui.tab.TabbedPane -import jadx.gui.utils.Icons -import jadx.gui.utils.NLS -import jadx.gui.utils.UiUtils -import jadx.gui.utils.ui.NodeLabel -import jadx.plugins.script.kotlin.ScriptServices -import jadx.plugins.script.kotlin.runtime.JadxScriptData.Companion.JADX_SCRIPT_LOG_PREFIX -import org.fife.ui.rsyntaxtextarea.ErrorStrip -import org.fife.ui.rtextarea.RTextScrollPane -import org.slf4j.Logger -import org.slf4j.LoggerFactory -import java.awt.BorderLayout -import java.awt.Component -import java.awt.Dimension -import java.awt.event.KeyEvent -import javax.swing.Box -import javax.swing.BoxLayout -import javax.swing.JButton -import javax.swing.JLabel -import javax.swing.JPanel -import javax.swing.KeyStroke -import javax.swing.border.EmptyBorder -import kotlin.script.experimental.api.ScriptDiagnostic - -class ScriptContentPanel( - private val pluginContext: JadxPluginContext, - panel: TabbedPane, - scriptNode: JInputScript, -) : AbstractCodeContentPanel(panel, scriptNode) { - private val scriptArea: ScriptCodeArea = ScriptCodeArea(this, scriptNode) - private val searchBar: SearchBar - private val codeScrollPane: RTextScrollPane - private val actionPanel: JPanel - private val resultLabel: JLabel = NodeLabel("") - private val errorService: ScriptErrorService = ScriptErrorService(scriptArea) - private val scriptLog: Logger = LoggerFactory.getLogger(JADX_SCRIPT_LOG_PREFIX + scriptNode.name) - - init { - actionPanel = buildScriptActionsPanel() - searchBar = SearchBar(scriptArea) - codeScrollPane = RTextScrollPane(scriptArea) - - initUI() - applySettings() - scriptArea.load() - } - - private fun initUI() { - val topPanel = JPanel(BorderLayout()) - topPanel.setBorder(EmptyBorder(5, 5, 5, 5)) - topPanel.add(actionPanel, BorderLayout.NORTH) - topPanel.add(searchBar, BorderLayout.SOUTH) - - val codePanel = JPanel(BorderLayout()) - codePanel.setBorder(EmptyBorder(0, 0, 0, 0)) - codePanel.add(codeScrollPane) - codePanel.add(ErrorStrip(scriptArea), BorderLayout.LINE_END) - - setLayout(BorderLayout()) - setBorder(EmptyBorder(0, 0, 0, 0)) - add(topPanel, BorderLayout.NORTH) - add(codeScrollPane, BorderLayout.CENTER) - - val key = KeyStroke.getKeyStroke(KeyEvent.VK_F, UiUtils.ctrlButton()) - UiUtils.addKeyBinding(scriptArea, key, "SearchAction") { searchBar.toggle() } - } - - private fun buildScriptActionsPanel(): JPanel { - val runAction = JadxGuiAction(ActionModel.SCRIPT_RUN, Runnable { this.runScript() }) - val saveAction = JadxGuiAction(ActionModel.SCRIPT_SAVE, Runnable { scriptArea.save() }) - - runAction.shortcutComponent = scriptArea - saveAction.shortcutComponent = scriptArea - - tabbedPane.mainWindow.shortcutsController.bindImmediate(runAction) - tabbedPane.mainWindow.shortcutsController.bindImmediate(saveAction) - - val save = saveAction.makeButton() - scriptArea.scriptNode.addChangeListener { save.setEnabled(it) } - - val check = JButton(NLS.str("script.check"), Icons.CHECK) - check.addActionListener { checkScript() } - val format = JButton(NLS.str("script.format"), Icons.FORMAT) - format.addActionListener { reformatCode() } - val scriptLog = JButton(NLS.str("script.log"), Icons.FORMAT) - scriptLog.addActionListener { showScriptLog() } - - val panel = JPanel() - panel.setLayout(BoxLayout(panel, BoxLayout.LINE_AXIS)) - panel.setBorder(EmptyBorder(0, 0, 0, 0)) - panel.add(runAction.makeButton()) - panel.add(Box.createRigidArea(Dimension(10, 0))) - panel.add(save) - panel.add(Box.createRigidArea(Dimension(10, 0))) - panel.add(check) - panel.add(Box.createRigidArea(Dimension(10, 0))) - panel.add(format) - panel.add(Box.createRigidArea(Dimension(30, 0))) - panel.add(resultLabel) - panel.add(Box.createHorizontalGlue()) - panel.add(scriptLog) - return panel - } - - private fun runScript() { - scriptArea.save() - if (!checkScript(runScript = true)) { - return - } - resetResultLabel() - - val tabbedPane = getTabbedPane() - val mainWindow = tabbedPane.mainWindow - mainWindow.backgroundExecutor.execute(NLS.str("script.run"), { - try { - mainWindow.wrapper.reloadPasses() - } catch (e: Exception) { - scriptLog.error("Passes reload failed", e) - } - }, { - mainWindow.passesReloaded() - }) - } - - private fun checkScript(runScript: Boolean = false): Boolean { - try { - resetResultLabel() - val code = scriptArea.getText() - - if (code.contains("@file:DependsOn")) { - if (!runScript) { - resultLabel.setText("Checks disabled for scripts with external dependencies") - } - return true - } - - val fileName = scriptArea.getNode().getName() - val scriptServices = ScriptServices(pluginContext) - val result = scriptServices.analyze(fileName, code) - var success = result.success - val issues: List = result.issues - for (issue in issues) { - val severity = issue.severity - if (severity == ScriptDiagnostic.Severity.ERROR || severity == ScriptDiagnostic.Severity.FATAL) { - scriptLog.error( - issue.render( - withSeverity = false, - withLocation = true, - withException = true, - withStackTrace = true, - ), - ) - success = false - } else if (severity == ScriptDiagnostic.Severity.WARNING) { - scriptLog.warn("Compile issue: {}", issue) - } - } - val lintErrs: List = when { - success -> getLintIssues(code) - else -> listOf() - } - - errorService.clearErrors() - errorService.addCompilerIssues(issues) - errorService.addLintErrors(lintErrs) - if (!success) { - resultLabel.setText("Compile issues: " + issues.size) - showScriptLog() - } else if (!lintErrs.isEmpty()) { - resultLabel.setText("Lint issues: " + lintErrs.size) - } else { - resultLabel.setText("OK") - } - errorService.apply() - return success - } catch (e: Throwable) { - scriptLog.error("Failed to check code", e) - return true - } - } - - private fun getLintIssues(code: String): List { - try { - val lintErrs = KtLintUtils.lint(code) - for (error in lintErrs) { - scriptLog.warn("Lint issue: {} ({}:{})(ruleId={})", error.detail, error.line, error.col, error.ruleId) - } - return lintErrs - } catch (e: Throwable) { // can throw initialization error - scriptLog.warn("KtLint failed", e) - return listOf() - } - } - - private fun reformatCode() { - resetResultLabel() - try { - val code = scriptArea.getText() - val formattedCode = KtLintUtils.format(code) - if (code != formattedCode) { - scriptArea.updateCode(formattedCode) - resultLabel.setText("Code updated") - errorService.clearErrors() - } - } catch (e: Throwable) { // can throw initialization error - scriptLog.error("Failed to reformat code", e) - } - } - - private fun resetResultLabel() { - resultLabel.setText("") - } - - private fun applySettings() { - val settings = getSettings() - codeScrollPane.setLineNumbersEnabled(settings.lineNumbersMode != LineNumbersMode.DISABLE) - codeScrollPane.gutter.setLineNumberFont(settings.codeFont) - scriptArea.loadSettings() - } - - private fun showScriptLog() { - mainWindow.showLogViewer(LogOptions.forScript(getNode().getName())) - } - - override fun getCodeArea(): AbstractCodeArea { - return scriptArea - } - - override fun getChildrenComponent(): Component { - return codeArea - } - - override fun loadSettings() { - applySettings() - updateUI() - } - - override fun dispose() { - scriptArea.dispose() - } -} diff --git a/jadx-plugins/jadx-script-kotlin/src/main/kotlin/jadx/plugins/script/kotlin/gui/ScriptErrorService.kt b/jadx-plugins/jadx-script-kotlin/src/main/kotlin/jadx/plugins/script/kotlin/gui/ScriptErrorService.kt deleted file mode 100644 index d702113d4..000000000 --- a/jadx-plugins/jadx-script-kotlin/src/main/kotlin/jadx/plugins/script/kotlin/gui/ScriptErrorService.kt +++ /dev/null @@ -1,108 +0,0 @@ -package jadx.plugins.script.kotlin.gui - -import org.fife.ui.rsyntaxtextarea.RSyntaxDocument -import org.fife.ui.rsyntaxtextarea.parser.AbstractParser -import org.fife.ui.rsyntaxtextarea.parser.DefaultParseResult -import org.fife.ui.rsyntaxtextarea.parser.DefaultParserNotice -import org.fife.ui.rsyntaxtextarea.parser.ParseResult -import org.fife.ui.rsyntaxtextarea.parser.ParserNotice -import org.slf4j.Logger -import org.slf4j.LoggerFactory -import kotlin.script.experimental.api.ScriptDiagnostic - -class ScriptErrorService(private val scriptArea: ScriptCodeArea) : AbstractParser() { - private val result: DefaultParseResult = DefaultParseResult(this) - - override fun parse(doc: RSyntaxDocument?, style: String?): ParseResult { - return result - } - - fun clearErrors() { - result.clearNotices() - scriptArea.removeParser(this) - } - - fun apply() { - scriptArea.removeParser(this) - scriptArea.addParser(this) - scriptArea.addNotify() - scriptArea.requestFocus() - jumpCaretToFirstError() - } - - private fun jumpCaretToFirstError() { - val parserNotices = result.notices - if (parserNotices.isEmpty()) { - return - } - val notice = parserNotices.get(0) - var offset = notice.offset - if (offset == -1) { - try { - offset = scriptArea.getLineStartOffset(notice.line) - } catch (e: Exception) { - LOG.error("Failed to jump to first error", e) - return - } - } - scriptArea.scrollToPos(offset) - } - - fun addCompilerIssues(issues: List) { - for (issue in issues) { - if (issue.severity == ScriptDiagnostic.Severity.DEBUG) { - continue - } - val notice: DefaultParserNotice? - val loc = issue.location - if (loc == null) { - notice = DefaultParserNotice(this, issue.message, 0) - } else { - try { - val line = loc.start.line - val offset = scriptArea.getLineStartOffset(line - 1) + loc.start.col - val len = if (loc.end == null) -1 else loc.end!!.col - loc.start.col - notice = DefaultParserNotice(this, issue.message, line, offset - 1, len) - notice.setLevel(convertLevel(issue.severity)) - } catch (e: Exception) { - LOG.error("Failed to convert script issue", e) - continue - } - } - addNotice(notice) - } - } - - fun addLintErrors(errors: List) { - for (error in errors) { - try { - val line = error.line - val offset = scriptArea.getLineStartOffset(line - 1) + error.col - 1 - val word = scriptArea.getWordByPosition(offset) - val len = word?.length ?: -1 - val notice = DefaultParserNotice(this, error.detail, line, offset, len) - notice.setLevel(ParserNotice.Level.WARNING) - addNotice(notice) - } catch (e: Exception) { - LOG.error("Failed to convert lint error", e) - } - } - } - - private fun addNotice(notice: DefaultParserNotice) { - LOG.debug("Add notice: {}:{}:{} - {}", notice.line, notice.offset, notice.length, notice.message) - result.addNotice(notice) - } - - companion object { - private val LOG: Logger = LoggerFactory.getLogger(ScriptErrorService::class.java) - - private fun convertLevel(severity: ScriptDiagnostic.Severity): ParserNotice.Level { - return when (severity) { - ScriptDiagnostic.Severity.FATAL, ScriptDiagnostic.Severity.ERROR -> ParserNotice.Level.ERROR - ScriptDiagnostic.Severity.WARNING -> ParserNotice.Level.WARNING - ScriptDiagnostic.Severity.INFO, ScriptDiagnostic.Severity.DEBUG -> ParserNotice.Level.INFO - } - } - } -} diff --git a/jadx-plugins/jadx-script-kotlin/src/main/kotlin/jadx/plugins/script/kotlin/passes/JadxScriptAfterLoadPass.kt b/jadx-plugins/jadx-script-kotlin/src/main/kotlin/jadx/plugins/script/kotlin/passes/JadxScriptAfterLoadPass.kt deleted file mode 100644 index d40fa47e5..000000000 --- a/jadx-plugins/jadx-script-kotlin/src/main/kotlin/jadx/plugins/script/kotlin/passes/JadxScriptAfterLoadPass.kt +++ /dev/null @@ -1,27 +0,0 @@ -package jadx.plugins.script.kotlin.passes - -import jadx.api.JadxDecompiler -import jadx.api.plugins.pass.impl.SimpleJadxPassInfo -import jadx.api.plugins.pass.types.JadxAfterLoadPass -import jadx.plugins.script.kotlin.runtime.JadxScriptData - -class JadxScriptAfterLoadPass(private val scripts: List) : JadxAfterLoadPass { - - override fun getInfo() = SimpleJadxPassInfo("JadxScriptAfterLoad", "Execute scripts 'afterLoad' block") - - override fun init(decompiler: JadxDecompiler) { - for (script in scripts) { - if (script.error) { - continue - } - try { - for (b in script.afterLoad) { - b.invoke() - } - } catch (e: Throwable) { - script.error = true - script.log.error(e) { "Error executing 'afterLoad' block in script: ${script.scriptFile.name}" } - } - } - } -} diff --git a/jadx-plugins/jadx-script-kotlin/src/main/kotlin/jadx/plugins/script/kotlin/runtime/JadxScriptTemplate.kt b/jadx-plugins/jadx-script-kotlin/src/main/kotlin/jadx/plugins/script/kotlin/runtime/JadxScriptTemplate.kt deleted file mode 100644 index 30c543b01..000000000 --- a/jadx-plugins/jadx-script-kotlin/src/main/kotlin/jadx/plugins/script/kotlin/runtime/JadxScriptTemplate.kt +++ /dev/null @@ -1,29 +0,0 @@ -package jadx.plugins.script.kotlin.runtime - -import jadx.plugins.script.kotlin.DefCompileConf -import kotlin.script.experimental.annotations.KotlinScript - -@KotlinScript( - displayName = "Jadx Script", - fileExtension = "jadx.kts", - filePathPattern = ".*\\.jadx\\.kts", - compilationConfiguration = DefCompileConf::class, -) -open class JadxScriptTemplate( - scriptData: JadxScriptData, -) { - val scriptName = scriptData.scriptName - val log = scriptData.log - - private val scriptInstance = JadxScriptInstance(scriptData, log) - - fun getJadxInstance() = scriptInstance - - fun println(message: Any?) { - log.info { message } - } - - fun print(message: Any?) { - log.info { message } - } -} diff --git a/jadx-plugins/jadx-script-kotlin/src/main/kotlin/jadx/plugins/script/kotlin/runtime/ScriptRuntime.kt b/jadx-plugins/jadx-script-kotlin/src/main/kotlin/jadx/plugins/script/kotlin/runtime/ScriptRuntime.kt deleted file mode 100644 index 680f8ec79..000000000 --- a/jadx-plugins/jadx-script-kotlin/src/main/kotlin/jadx/plugins/script/kotlin/runtime/ScriptRuntime.kt +++ /dev/null @@ -1,79 +0,0 @@ -@file:JvmName("ScriptRuntime") -@file:Suppress("unused", "MemberVisibilityCanBePrivate") - -package jadx.plugins.script.kotlin.runtime - -import io.github.oshai.kotlinlogging.KLogger -import io.github.oshai.kotlinlogging.KotlinLogging -import jadx.api.JadxArgs -import jadx.api.JadxDecompiler -import jadx.api.JavaClass -import jadx.api.plugins.JadxPluginContext -import jadx.api.plugins.events.IJadxEvents -import jadx.api.plugins.pass.JadxPass -import jadx.plugins.script.kotlin.runtime.data.Debug -import jadx.plugins.script.kotlin.runtime.data.Decompile -import jadx.plugins.script.kotlin.runtime.data.Gui -import jadx.plugins.script.kotlin.runtime.data.JadxScriptAllOptions -import jadx.plugins.script.kotlin.runtime.data.JadxScriptOptions -import jadx.plugins.script.kotlin.runtime.data.Rename -import jadx.plugins.script.kotlin.runtime.data.Replace -import jadx.plugins.script.kotlin.runtime.data.Search -import jadx.plugins.script.kotlin.runtime.data.Stages -import org.jetbrains.annotations.ApiStatus.Internal -import java.io.File - -class JadxScriptData( - val jadxInstance: JadxDecompiler, - val pluginContext: JadxPluginContext, - val options: JadxScriptAllOptions, - val scriptFile: File, -) { - companion object { - const val JADX_SCRIPT_LOG_PREFIX = "JadxScript:" - } - val scriptName = scriptFile.name.removeSuffix(".jadx.kts") - val log = KotlinLogging.logger("$JADX_SCRIPT_LOG_PREFIX$scriptName") - val afterLoad = mutableListOf<() -> Unit>() - var error: Boolean = false -} - -class JadxScriptInstance( - private val scriptData: JadxScriptData, - val log: KLogger, -) { - private val decompiler = scriptData.jadxInstance - - val options: JadxScriptOptions by lazy { JadxScriptOptions(this, scriptData.options) } - val rename: Rename by lazy { Rename(this) } - val stages: Stages by lazy { Stages(this) } - val replace: Replace by lazy { Replace(this) } - val decompile: Decompile by lazy { Decompile(this) } - val search: Search by lazy { Search(this) } - val gui: Gui by lazy { Gui(this, scriptData.pluginContext.guiContext) } - val debug: Debug by lazy { Debug(this) } - - val events: IJadxEvents - get() = scriptData.pluginContext.events() - - val args: JadxArgs - get() = decompiler.args - - val classes: List - get() = decompiler.classes - - val scriptFile get() = scriptData.scriptFile - - val scriptName get() = scriptData.scriptName - - fun afterLoad(block: () -> Unit) { - scriptData.afterLoad.add(block) - } - - fun addPass(pass: JadxPass) { - scriptData.pluginContext.addPass(pass) - } - - val internalDecompiler: JadxDecompiler - @Internal get() = decompiler -} diff --git a/jadx-plugins/jadx-script-kotlin/src/main/kotlin/jadx/plugins/script/kotlin/runtime/Utils.kt b/jadx-plugins/jadx-script-kotlin/src/main/kotlin/jadx/plugins/script/kotlin/runtime/Utils.kt deleted file mode 100644 index ed8c9bfb8..000000000 --- a/jadx-plugins/jadx-script-kotlin/src/main/kotlin/jadx/plugins/script/kotlin/runtime/Utils.kt +++ /dev/null @@ -1,10 +0,0 @@ -/** - * Utils for use in scripts. - * Located in default package to reduce imports. - */ - -import java.io.File - -fun String.asFile(): File = File(this) - -fun Int.hex(): String = Integer.toHexString(this) diff --git a/jadx-plugins/jadx-script-kotlin/src/main/kotlin/jadx/plugins/script/kotlin/runtime/data/Debug.kt b/jadx-plugins/jadx-script-kotlin/src/main/kotlin/jadx/plugins/script/kotlin/runtime/data/Debug.kt deleted file mode 100644 index 0cdfe69f0..000000000 --- a/jadx-plugins/jadx-script-kotlin/src/main/kotlin/jadx/plugins/script/kotlin/runtime/data/Debug.kt +++ /dev/null @@ -1,34 +0,0 @@ -package jadx.plugins.script.kotlin.runtime.data - -import jadx.core.dex.nodes.MethodNode -import jadx.core.dex.visitors.DotGraphVisitor -import jadx.core.utils.DebugUtils -import jadx.plugins.script.kotlin.runtime.JadxScriptInstance -import java.io.File - -class Debug(private val jadx: JadxScriptInstance) { - - fun printMethodRegions(mth: MethodNode, printInsns: Boolean = false) { - DebugUtils.printRegions(mth, printInsns) - } - - fun saveCFG(mth: MethodNode, file: File = File("dump-mth-raw")) { - DotGraphVisitor.dumpRaw().save(file, mth) - } - - fun printPreparePasses() { - jadx.internalDecompiler.root.preDecompilePasses.forEach { jadx.log.info { it.name } } - } - - fun printPasses() { - jadx.internalDecompiler.root.passes.forEach { jadx.log.info { it.name } } - } - - fun catchExceptions(label: String = "", code: () -> Unit) { - try { - code.invoke() - } catch (e: Throwable) { - jadx.log.error(e) { "Exception in '$label'" } - } - } -} diff --git a/jadx-plugins/jadx-script-kotlin/src/main/kotlin/jadx/plugins/script/kotlin/runtime/data/Decompile.kt b/jadx-plugins/jadx-script-kotlin/src/main/kotlin/jadx/plugins/script/kotlin/runtime/data/Decompile.kt deleted file mode 100644 index 7f4dc0259..000000000 --- a/jadx-plugins/jadx-script-kotlin/src/main/kotlin/jadx/plugins/script/kotlin/runtime/data/Decompile.kt +++ /dev/null @@ -1,27 +0,0 @@ -package jadx.plugins.script.kotlin.runtime.data - -import jadx.api.JadxArgs -import jadx.api.JavaClass -import jadx.plugins.script.kotlin.runtime.JadxScriptInstance -import java.util.concurrent.Executors - -class Decompile(private val jadx: JadxScriptInstance) { - - fun all(ignoreCache: Boolean = false) { - if (ignoreCache) { - jadx.classes.forEach(JavaClass::reload) - } else { - jadx.classes.forEach(JavaClass::decompile) - } - } - - fun allThreaded(threadsCount: Int = JadxArgs.DEFAULT_THREADS_COUNT) { - val executor = Executors.newFixedThreadPool(threadsCount) - val batches = jadx.internalDecompiler.decompileScheduler.buildBatches(jadx.classes) - for (batch in batches) { - executor.submit { - batch.forEach(JavaClass::decompile) - } - } - } -} diff --git a/jadx-plugins/jadx-script-kotlin/src/main/kotlin/jadx/plugins/script/kotlin/runtime/data/Gui.kt b/jadx-plugins/jadx-script-kotlin/src/main/kotlin/jadx/plugins/script/kotlin/runtime/data/Gui.kt deleted file mode 100644 index 07c6bad39..000000000 --- a/jadx-plugins/jadx-script-kotlin/src/main/kotlin/jadx/plugins/script/kotlin/runtime/data/Gui.kt +++ /dev/null @@ -1,65 +0,0 @@ -package jadx.plugins.script.kotlin.runtime.data - -import jadx.api.metadata.ICodeNodeRef -import jadx.api.plugins.gui.JadxGuiContext -import jadx.plugins.script.kotlin.runtime.JadxScriptInstance - -class Gui( - private val jadx: JadxScriptInstance, - private val guiContext: JadxGuiContext?, -) { - - fun isAvailable() = guiContext != null - - fun ifAvailable(block: Gui.() -> Unit) { - guiContext?.let { this.apply(block) } - } - - fun ui(block: () -> Unit) { - context().uiRun(block) - } - - fun addMenuAction(name: String, action: () -> Unit) { - context().addMenuAction(name, action) - } - - fun addPopupMenuAction( - name: String, - enabled: (ICodeNodeRef) -> Boolean = { _ -> true }, - keyBinding: String? = null, - action: (ICodeNodeRef) -> Unit, - ) { - context().addPopupMenuAction(name, enabled, keyBinding, action) - } - - fun registerGlobalKeyBinding(id: String, keyBinding: String, action: () -> Unit): Boolean { - return context().registerGlobalKeyBinding(id, keyBinding, action) - } - - fun copyToClipboard(str: String) { - context().copyToClipboard(str) - } - - fun open(ref: ICodeNodeRef): Boolean = context().open(ref) - - fun reloadActiveTab() = context().reloadActiveTab() - - fun reloadAllTabs() = context().reloadAllTabs() - - val nodeUnderCaret: ICodeNodeRef? - get() = context().nodeUnderCaret - val nodeUnderMouse: ICodeNodeRef? - get() = context().nodeUnderMouse - val enclosingNodeUnderCaret: ICodeNodeRef? - get() = context().enclosingNodeUnderCaret - val enclosingNodeUnderMouse: ICodeNodeRef? - get() = context().enclosingNodeUnderMouse - - /** - * Save node rename in a project and run all needed UI updates - */ - fun applyNodeRename(node: ICodeNodeRef) = context().applyNodeRename(node) - - private fun context(): JadxGuiContext = - guiContext ?: throw IllegalStateException("GUI plugins context not available!") -} diff --git a/jadx-plugins/jadx-script-kotlin/src/main/kotlin/jadx/plugins/script/kotlin/runtime/data/Options.kt b/jadx-plugins/jadx-script-kotlin/src/main/kotlin/jadx/plugins/script/kotlin/runtime/data/Options.kt deleted file mode 100644 index 56038e958..000000000 --- a/jadx-plugins/jadx-script-kotlin/src/main/kotlin/jadx/plugins/script/kotlin/runtime/data/Options.kt +++ /dev/null @@ -1,117 +0,0 @@ -package jadx.plugins.script.kotlin.runtime.data - -import jadx.api.plugins.options.JadxPluginOptions -import jadx.api.plugins.options.OptionDescription -import jadx.api.plugins.options.OptionFlag -import jadx.api.plugins.options.OptionType -import jadx.api.plugins.options.impl.JadxOptionDescription -import jadx.plugins.script.kotlin.runtime.JadxScriptInstance - -class JadxScriptAllOptions : JadxPluginOptions { - lateinit var values: Map - val descriptions: MutableList = mutableListOf() - - override fun setOptions(options: Map) { - values = options - } - - override fun getOptionsDescriptions(): List = descriptions -} - -class ScriptOptionDesc( - val script: String, - optName: String, - desc: String, - defaultValue: String?, - values: List, - type: OptionType, -) : JadxOptionDescription("jadx-script.$script.$optName", desc, defaultValue, values, type) - -class ScriptOption( - val name: String, - val id: String, - val optData: ScriptOptionDesc, - private val getter: () -> T, -) { - private var validate: ((T) -> Boolean)? = null - - val value: T - get() { - val v = getter.invoke() - validate?.let { predicate -> - if (!predicate.invoke(v)) { - throw IllegalArgumentException("Invalid value '$v' for option $id") - } - } - return v - } - - fun validate(predicate: (T) -> Boolean): ScriptOption { - validate = predicate - return this - } - - fun flags(vararg flags: OptionFlag): ScriptOption { - optData.flags += flags - return this - } -} - -class JadxScriptOptions( - private val jadx: JadxScriptInstance, - private val options: JadxScriptAllOptions, -) { - fun register( - name: String, - desc: String, - values: List, - defaultValue: String, - type: OptionType = OptionType.STRING, - convert: (String?) -> T, - ): ScriptOption { - val optData = ScriptOptionDesc(jadx.scriptName, name, desc, defaultValue, values, type) - options.descriptions.add(optData) - val optId = optData.name() - return ScriptOption(name, optId, optData) { convert.invoke(options.values[optId]) } - } - - fun registerString( - name: String, - desc: String = "", - values: List = emptyList(), - defaultValue: String = "", - ): ScriptOption { - return register(name, desc, values, defaultValue) { value -> - if (value == null) { - defaultValue - } else { - if (values.isEmpty() || values.contains(value)) { - value - } else { - throw IllegalArgumentException("Unknown value '$value' for option '$name', expect one of $values") - } - } - } - } - - fun registerYesNo(name: String, desc: String = "", defaultValue: Boolean = false): ScriptOption { - val defStr = if (defaultValue) "yes" else "no" - return register(name, desc, listOf("yes", "no"), defStr, OptionType.BOOLEAN) { value -> - when (value) { - null -> defaultValue - "yes", "true" -> true - "no", "false" -> false - else -> throw IllegalArgumentException("Unknown value '$value' for option '$name', expect: 'yes' or 'no'") - } - } - } - - fun registerInt(name: String, desc: String = "", defaultValue: Int = 0): ScriptOption { - return register(name, desc, emptyList(), defaultValue.toString(), OptionType.NUMBER) { value -> - when (value) { - null -> defaultValue - else -> value.toInt() - } - } - } -} diff --git a/jadx-plugins/jadx-script-kotlin/src/main/kotlin/jadx/plugins/script/kotlin/runtime/data/Rename.kt b/jadx-plugins/jadx-script-kotlin/src/main/kotlin/jadx/plugins/script/kotlin/runtime/data/Rename.kt deleted file mode 100644 index 869594e84..000000000 --- a/jadx-plugins/jadx-script-kotlin/src/main/kotlin/jadx/plugins/script/kotlin/runtime/data/Rename.kt +++ /dev/null @@ -1,52 +0,0 @@ -package jadx.plugins.script.kotlin.runtime.data - -import jadx.core.dex.attributes.AFlag -import jadx.core.dex.attributes.IAttributeNode -import jadx.core.dex.nodes.IDexNode -import jadx.core.dex.nodes.RootNode -import jadx.plugins.script.kotlin.runtime.JadxScriptInstance - -class Rename(private val jadx: JadxScriptInstance) { - - fun all(makeNewName: (String) -> String?) { - all { name, _ -> makeNewName.invoke(name) } - } - - fun all(makeNewName: (String, IDexNode) -> String?) { - jadx.addPass(object : ScriptOrderedPreparePass( - jadx, - "RenameAll", - runBefore = listOf("RenameVisitor"), - ) { - override fun init(root: RootNode) { - for (pkgNode in root.packages) { - rename(makeNewName, pkgNode, pkgNode.pkgInfo.name) - } - for (cls in root.classes) { - rename(makeNewName, cls, cls.name) - for (mth in cls.methods) { - if (!mth.isConstructor) { - rename(makeNewName, mth, mth.name) - } - } - for (fld in cls.fields) { - rename(makeNewName, fld, fld.name) - } - } - } - - private inline fun rename( - makeNewName: (String, IDexNode) -> String?, - node: T, - name: String, - ) { - if (node is IAttributeNode && node.contains(AFlag.DONT_RENAME)) { - return - } - makeNewName.invoke(name, node)?.let { - node.rename(it) - } - } - }) - } -} diff --git a/jadx-plugins/jadx-script-kotlin/src/main/kotlin/jadx/plugins/script/kotlin/runtime/data/Replace.kt b/jadx-plugins/jadx-script-kotlin/src/main/kotlin/jadx/plugins/script/kotlin/runtime/data/Replace.kt deleted file mode 100644 index 80475cfb9..000000000 --- a/jadx-plugins/jadx-script-kotlin/src/main/kotlin/jadx/plugins/script/kotlin/runtime/data/Replace.kt +++ /dev/null @@ -1,43 +0,0 @@ -package jadx.plugins.script.kotlin.runtime.data - -import jadx.core.dex.instructions.args.InsnArg -import jadx.core.dex.instructions.args.InsnWrapArg -import jadx.core.dex.nodes.InsnNode -import jadx.core.dex.nodes.MethodNode -import jadx.core.utils.InsnRemover -import jadx.plugins.script.kotlin.runtime.JadxScriptInstance - -class Replace(private val jadx: JadxScriptInstance) { - - fun insns(replace: (MethodNode, InsnNode) -> InsnNode?) { - jadx.stages.mthBlocks { mth, blocks -> - for (block in blocks) { - val insns = block.instructions - for ((i, insn) in insns.withIndex()) { - replaceSubInsns(mth, insn, replace) - replace.invoke(mth, insn)?.let { - insns[i] = it - } - } - } - } - } - - private fun replaceSubInsns(mth: MethodNode, insn: InsnNode, replace: (MethodNode, InsnNode) -> InsnNode?) { - val argsCount = insn.argsCount - if (argsCount == 0) { - return - } - for (i in 0 until argsCount) { - val arg = insn.getArg(i) - if (arg is InsnWrapArg) { - val wrapInsn = arg.wrapInsn - replaceSubInsns(mth, wrapInsn, replace) - replace.invoke(mth, wrapInsn)?.let { - InsnRemover.unbindArgUsage(mth, arg) - insn.setArg(i, InsnArg.wrapInsnIntoArg(it)) - } - } - } - } -} diff --git a/jadx-plugins/jadx-script-kotlin/src/main/kotlin/jadx/plugins/script/kotlin/runtime/data/Search.kt b/jadx-plugins/jadx-script-kotlin/src/main/kotlin/jadx/plugins/script/kotlin/runtime/data/Search.kt deleted file mode 100644 index ba68d722d..000000000 --- a/jadx-plugins/jadx-script-kotlin/src/main/kotlin/jadx/plugins/script/kotlin/runtime/data/Search.kt +++ /dev/null @@ -1,16 +0,0 @@ -package jadx.plugins.script.kotlin.runtime.data - -import jadx.core.dex.nodes.ClassNode -import jadx.plugins.script.kotlin.runtime.JadxScriptInstance - -class Search(jadx: JadxScriptInstance) { - private val dec = jadx.internalDecompiler - - fun classByFullName(fullName: String): ClassNode? { - return dec.searchClassNodeByOrigFullName(fullName) - } - - fun classesByShortName(fullName: String): List { - return dec.root.searchClassByShortName(fullName) - } -} diff --git a/jadx-plugins/jadx-script-kotlin/src/main/kotlin/jadx/plugins/script/kotlin/runtime/data/Stages.kt b/jadx-plugins/jadx-script-kotlin/src/main/kotlin/jadx/plugins/script/kotlin/runtime/data/Stages.kt deleted file mode 100644 index 640737153..000000000 --- a/jadx-plugins/jadx-script-kotlin/src/main/kotlin/jadx/plugins/script/kotlin/runtime/data/Stages.kt +++ /dev/null @@ -1,76 +0,0 @@ -package jadx.plugins.script.kotlin.runtime.data - -import jadx.core.dex.nodes.BlockNode -import jadx.core.dex.nodes.InsnNode -import jadx.core.dex.nodes.MethodNode -import jadx.core.dex.nodes.RootNode -import jadx.core.dex.regions.Region -import jadx.plugins.script.kotlin.runtime.JadxScriptInstance - -class Stages(private val jadx: JadxScriptInstance) { - - fun prepare(block: (RootNode) -> Unit) { - jadx.addPass(object : ScriptPreparePass(jadx, "StagePrepare") { - override fun init(root: RootNode) { - jadx.debug.catchExceptions("Prepare init block") { - block.invoke(root) - } - } - }) - } - - fun rawInsns(block: (MethodNode, Array) -> Unit) { - jadx.addPass(object : ScriptOrderedDecompilePass( - jadx, - "StageRawInsns", - runAfter = listOf("start"), - ) { - override fun visit(mth: MethodNode) { - mth.instructions?.let { - jadx.debug.catchExceptions("Method instructions visit") { - block.invoke(mth, it) - } - } - } - }) - } - - fun mthEarlyBlocks(block: (MethodNode, List) -> Unit) { - mthBlocks(beforePass = "SSATransform", block) - } - - fun mthBlocks( - beforePass: String = "RegionMakerVisitor", - block: (MethodNode, List) -> Unit, - ) { - jadx.addPass(object : ScriptOrderedDecompilePass( - jadx, - "StageMthBlocks", - runBefore = listOf(beforePass), - ) { - override fun visit(mth: MethodNode) { - mth.basicBlocks?.let { - jadx.debug.catchExceptions("Method blocks visit") { - block.invoke(mth, it) - } - } - } - }) - } - - fun mthRegions(block: (MethodNode, Region) -> Unit) { - jadx.addPass(object : ScriptOrderedDecompilePass( - jadx, - "StageMthRegions", - runBefore = listOf("PrepareForCodeGen"), - ) { - override fun visit(mth: MethodNode) { - mth.region?.let { - jadx.debug.catchExceptions("Method region visit") { - block.invoke(mth, it) - } - } - } - }) - } -} diff --git a/jadx-plugins/jadx-script-kotlin/src/main/kotlin/jadx/plugins/script/kotlin/runtime/data/Wrappers.kt b/jadx-plugins/jadx-script-kotlin/src/main/kotlin/jadx/plugins/script/kotlin/runtime/data/Wrappers.kt deleted file mode 100644 index 0fd0fb4f9..000000000 --- a/jadx-plugins/jadx-script-kotlin/src/main/kotlin/jadx/plugins/script/kotlin/runtime/data/Wrappers.kt +++ /dev/null @@ -1,77 +0,0 @@ -package jadx.plugins.script.kotlin.runtime.data - -import jadx.api.plugins.pass.JadxPass -import jadx.api.plugins.pass.impl.OrderedJadxPassInfo -import jadx.api.plugins.pass.impl.SimpleJadxPassInfo -import jadx.api.plugins.pass.types.JadxDecompilePass -import jadx.api.plugins.pass.types.JadxPreparePass -import jadx.core.dex.nodes.ClassNode -import jadx.core.dex.nodes.MethodNode -import jadx.core.dex.nodes.RootNode -import jadx.plugins.script.kotlin.runtime.JadxScriptInstance - -private fun buildScriptName(jadx: JadxScriptInstance, name: String) = "JadxScript$name(${jadx.scriptName})" - -private fun buildSimplePassInfo(jadx: JadxScriptInstance, name: String) = - SimpleJadxPassInfo(buildScriptName(jadx, name)) - -abstract class ScriptPreparePass( - private val jadx: JadxScriptInstance, - private val name: String, -) : JadxPreparePass { - override fun getInfo() = buildSimplePassInfo(jadx, name) -} - -abstract class ScriptDecompilePass( - private val jadx: JadxScriptInstance, - private val name: String, -) : JadxDecompilePass { - override fun getInfo() = buildSimplePassInfo(jadx, name) - - override fun init(root: RootNode) { - } - - override fun visit(cls: ClassNode): Boolean { - return true - } - - override fun visit(mth: MethodNode) { - } -} - -abstract class ScriptOrderedPass( - private val jadx: JadxScriptInstance, - private val name: String, - private val runAfter: List = listOf(), - private val runBefore: List = listOf(), -) : JadxPass { - override fun getInfo(): OrderedJadxPassInfo { - val scriptName = buildScriptName(jadx, name) - return OrderedJadxPassInfo(scriptName, scriptName, runAfter, runBefore) - } -} - -abstract class ScriptOrderedPreparePass( - jadx: JadxScriptInstance, - name: String, - runAfter: List = listOf(), - runBefore: List = listOf(), -) : ScriptOrderedPass(jadx, name, runAfter, runBefore), JadxPreparePass - -abstract class ScriptOrderedDecompilePass( - jadx: JadxScriptInstance, - name: String, - runAfter: List = listOf(), - runBefore: List = listOf(), -) : ScriptOrderedPass(jadx, name, runAfter, runBefore), JadxDecompilePass { - - override fun init(root: RootNode) { - } - - override fun visit(cls: ClassNode): Boolean { - return true - } - - override fun visit(mth: MethodNode) { - } -} diff --git a/jadx-plugins/jadx-script-kotlin/src/main/resources/META-INF/kotlin/script/templates/jadx.plugins.script.kotlin.runtime.JadxScriptTemplate.classname b/jadx-plugins/jadx-script-kotlin/src/main/resources/META-INF/kotlin/script/templates/jadx.plugins.script.kotlin.runtime.JadxScriptTemplate.classname deleted file mode 100644 index e69de29bb..000000000 diff --git a/jadx-plugins/jadx-script-kotlin/src/main/resources/META-INF/services/jadx.api.plugins.JadxPlugin b/jadx-plugins/jadx-script-kotlin/src/main/resources/META-INF/services/jadx.api.plugins.JadxPlugin deleted file mode 100644 index d1a553813..000000000 --- a/jadx-plugins/jadx-script-kotlin/src/main/resources/META-INF/services/jadx.api.plugins.JadxPlugin +++ /dev/null @@ -1 +0,0 @@ -jadx.plugins.script.kotlin.JadxScriptKotlinPlugin diff --git a/jadx-plugins/jadx-script-kotlin/src/test/kotlin/JadxScriptPluginTest.kt b/jadx-plugins/jadx-script-kotlin/src/test/kotlin/JadxScriptPluginTest.kt deleted file mode 100644 index e67236f43..000000000 --- a/jadx-plugins/jadx-script-kotlin/src/test/kotlin/JadxScriptPluginTest.kt +++ /dev/null @@ -1,51 +0,0 @@ -package jadx.plugins.script - -import jadx.api.JadxArgs -import jadx.api.JadxDecompiler -import org.assertj.core.api.Assertions.assertThat -import org.junit.jupiter.api.AfterAll -import org.junit.jupiter.api.BeforeAll -import org.junit.jupiter.api.Test -import org.junit.jupiter.api.TestInstance -import java.io.File -import kotlin.system.measureTimeMillis -import kotlin.time.DurationUnit -import kotlin.time.toDuration - -@TestInstance(TestInstance.Lifecycle.PER_CLASS) -class JadxScriptPluginTest { - - @BeforeAll - fun disableCache() { - System.setProperty("JADX_SCRIPT_CACHE_ENABLE", "false") - } - - @AfterAll - fun clear() { - System.clearProperty("JADX_SCRIPT_CACHE_ENABLE") - } - - @Test - fun integrationTest() { - val args = JadxArgs() - args.inputFiles.run { - add(getSampleFile("hello.smali")) - add(getSampleFile("test.jadx.kts")) - add(getSampleFile("test-deps.jadx.kts")) - } - val elapsed = measureTimeMillis { - JadxDecompiler(args).use { jadx -> - jadx.load() - assertThat(jadx.classes) - .hasSize(1) - .allMatch { it.name == "HelloJadx" } - } - } - println("Elapsed time: ${elapsed.toDuration(DurationUnit.MILLISECONDS)}") - } - - private fun getSampleFile(file: String): File { - val resFile = javaClass.classLoader.getResource("samples/$file") - return File(resFile!!.toURI()) - } -} diff --git a/jadx-plugins/jadx-script-kotlin/src/test/kotlin/ScriptServicesTest.kt b/jadx-plugins/jadx-script-kotlin/src/test/kotlin/ScriptServicesTest.kt deleted file mode 100644 index 0a46f78c1..000000000 --- a/jadx-plugins/jadx-script-kotlin/src/test/kotlin/ScriptServicesTest.kt +++ /dev/null @@ -1,67 +0,0 @@ -package jadx.plugins.script - -import jadx.plugins.script.kotlin.ScriptServices -import org.assertj.core.api.Assertions.assertThat -import org.junit.jupiter.api.Disabled -import org.junit.jupiter.api.Test -import kotlin.script.experimental.api.ScriptDiagnostic.Severity.ERROR - -class ScriptServicesTest { - - @Test - fun testAnalyzeSimple() { - val name = "simple" - val script = getSampleScript(name) - val result = ScriptServices().analyze(name, script) - println(result) - assertThat(result.success).isTrue() - assertThat(result.issues).noneMatch { it.severity == ERROR } - } - - @Disabled("External dependencies not resolved") - @Test - fun testAnalyzeDeps() { - val name = "test-deps" - val script = getSampleScript(name) - val result = ScriptServices().analyze(name, script) - println(result) - assertThat(result.success).isTrue() - assertThat(result.issues).noneMatch { it.severity == ERROR } - } - - @Test - fun testComplete() { - val name = "simple" - val script = getSampleScript(name) - val idx = script.indexOf("jadx.log.info") - val completePos = idx + 7 // jadx.lo| <- complete 'log' - val curScript = script.substring(0, completePos) - - val result = ScriptServices().complete(name, curScript, completePos) - println(result) - assertThat(result.completions) - .hasSize(1) - .allMatch { c -> c.text == "log" } - } - - @Disabled("External dependencies not resolved") - @Test - fun testCompleteDeps() { - val sampleName = "test-deps" - val script = getSampleScript(sampleName) - val startPos = script.indexOf("StringEscapeUtils.escapeJava") - val completePos = startPos + 26 // StringEscapeUtils.escapeJa| <- complete 'escapeJava(' - val exprEnd = script.indexOf('}', startIndex = completePos) - val curScript = script.removeRange(completePos, exprEnd) - val result = ScriptServices().complete(sampleName, curScript, completePos) - println(result) - assertThat(result.completions) - .hasSize(1) - .allMatch { c -> c.text == "escapeJava(" } - } - - private fun getSampleScript(scriptName: String): String { - val resFile = javaClass.classLoader.getResource("samples/$scriptName.jadx.kts") - return resFile!!.readText() - } -} diff --git a/jadx-plugins/jadx-script-kotlin/src/test/resources/samples/hello.smali b/jadx-plugins/jadx-script-kotlin/src/test/resources/samples/hello.smali deleted file mode 100644 index 255b70928..000000000 --- a/jadx-plugins/jadx-script-kotlin/src/test/resources/samples/hello.smali +++ /dev/null @@ -1,10 +0,0 @@ -.class LHelloWorld; -.super Ljava/lang/Object; - -.method public static main([Ljava/lang/String;)V - .registers 2 - sget-object p0, Ljava/lang/System;->out:Ljava/io/PrintStream; - const-string v0, "Hello, World" - invoke-virtual {p0, v0}, Ljava/io/PrintStream;->println(Ljava/lang/String;)V - return-void -.end method diff --git a/jadx-plugins/jadx-script-kotlin/src/test/resources/samples/simple.jadx.kts b/jadx-plugins/jadx-script-kotlin/src/test/resources/samples/simple.jadx.kts deleted file mode 100644 index 5f75ceb36..000000000 --- a/jadx-plugins/jadx-script-kotlin/src/test/resources/samples/simple.jadx.kts +++ /dev/null @@ -1,5 +0,0 @@ -val jadx = getJadxInstance() - -jadx.afterLoad { - jadx.log.info { "Hello" } -} diff --git a/jadx-plugins/jadx-script-kotlin/src/test/resources/samples/test-deps.jadx.kts b/jadx-plugins/jadx-script-kotlin/src/test/resources/samples/test-deps.jadx.kts deleted file mode 100644 index 39953db1c..000000000 --- a/jadx-plugins/jadx-script-kotlin/src/test/resources/samples/test-deps.jadx.kts +++ /dev/null @@ -1,11 +0,0 @@ -@file:DependsOn("org.apache.commons:commons-text:1.10.0") - -import org.apache.commons.text.StringEscapeUtils - -val jadx = getJadxInstance() - -jadx.afterLoad { - jadx.classes.forEach { - jadx.log.info { "Escaped name: ${StringEscapeUtils.escapeJava(it.fullName)}" } - } -} diff --git a/jadx-plugins/jadx-script-kotlin/src/test/resources/samples/test.jadx.kts b/jadx-plugins/jadx-script-kotlin/src/test/resources/samples/test.jadx.kts deleted file mode 100644 index f5f9462bf..000000000 --- a/jadx-plugins/jadx-script-kotlin/src/test/resources/samples/test.jadx.kts +++ /dev/null @@ -1,20 +0,0 @@ -import jadx.api.CommentsLevel - -val jadx = getJadxInstance() -jadx.args.commentsLevel = CommentsLevel.NONE -jadx.args.isDeobfuscationOn = false -jadx.args.renameFlags = emptySet() - -jadx.rename.all { name -> - when (name) { - "HelloWorld" -> "HelloJadx" - else -> null - } -} - -jadx.afterLoad { - println("Loaded classes: ${jadx.classes.size}") - jadx.classes.forEach { - println("Class '${it.name}':\n${it.code}") - } -} diff --git a/settings.gradle.kts b/settings.gradle.kts index c4353380e..207d3c8f4 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -26,7 +26,6 @@ include("jadx-plugins:jadx-java-convert") include("jadx-plugins:jadx-rename-mappings") include("jadx-plugins:jadx-kotlin-metadata") include("jadx-plugins:jadx-kotlin-source-debug-extension") -include("jadx-plugins:jadx-script-kotlin") include("jadx-plugins:jadx-xapk-input") include("jadx-plugins:jadx-aab-input") include("jadx-plugins:jadx-apkm-input")