feat: add base scripting support

This commit is contained in:
Skylot
2022-07-09 17:57:44 +01:00
parent fdf170529f
commit e5e64365fc
64 changed files with 1488 additions and 53 deletions
@@ -0,0 +1,19 @@
package jadx.plugins.script
import jadx.api.plugins.JadxPlugin
import jadx.api.plugins.JadxPluginContext
import jadx.api.plugins.JadxPluginInfo
import jadx.api.plugins.gui.JadxGuiContext
import jadx.api.plugins.pass.JadxPassContext
import jadx.plugins.script.passes.JadxScriptAfterLoadPass
import jadx.plugins.script.runner.ScriptEval
class JadxScriptPlugin : JadxPlugin {
override fun getPluginInfo() = JadxPluginInfo("jadx-script", "Jadx Script", "Scripting support for jadx")
override fun init(init: JadxPluginContext) {
val scriptStates = ScriptEval().process(init) ?: return
init.passContext.addPass(JadxScriptAfterLoadPass(scriptStates))
}
}
@@ -0,0 +1,30 @@
package jadx.plugins.script.passes
import jadx.api.core.nodes.IJadxDecompiler
import jadx.api.plugins.pass.impl.SimpleJadxPassInfo
import jadx.api.plugins.pass.types.JadxAfterLoadPass
import jadx.plugins.script.runner.ScriptStates
import mu.KotlinLogging
private val LOG = KotlinLogging.logger {}
class JadxScriptAfterLoadPass(private val scriptStates: ScriptStates) : JadxAfterLoadPass {
override fun getInfo() = SimpleJadxPassInfo("JadxScriptAfterLoad", "Execute scripts 'afterLoad' block")
override fun init(decompiler: IJadxDecompiler) {
for (script in scriptStates.getScripts()) {
if (script.error) {
continue
}
try {
for (b in script.scriptData.afterLoad) {
b.invoke()
}
} catch (e: Throwable) {
script.error = true
LOG.error(e) { "Error executing 'afterLoad' block in script: ${script.scriptFile.name}" }
}
}
}
}
@@ -0,0 +1,67 @@
package jadx.plugins.script.runner
import jadx.api.JadxDecompiler
import jadx.api.plugins.JadxPluginContext
import jadx.api.plugins.pass.JadxPassContext
import jadx.plugins.script.runtime.JadxScript
import jadx.plugins.script.runtime.JadxScriptData
import mu.KotlinLogging
import java.io.File
import kotlin.script.experimental.api.*
import kotlin.script.experimental.host.toScriptSource
import kotlin.script.experimental.jvmhost.BasicJvmScriptingHost
import kotlin.script.experimental.jvmhost.createJvmCompilationConfigurationFromTemplate
import kotlin.script.experimental.jvmhost.createJvmEvaluationConfigurationFromTemplate
private val LOG = KotlinLogging.logger {}
class ScriptEval {
fun process(init: JadxPluginContext): ScriptStates? {
val jadx = init.decompiler as JadxDecompiler
val scripts = jadx.args.inputFiles.filter { f -> f.name.endsWith(".jadx.kts") }
if (scripts.isEmpty()) {
return null
}
val scriptStates = ScriptStates()
for (scriptFile in scripts) {
val scriptData = JadxScriptData(jadx, init, scriptFile)
load(scriptFile, scriptData)
scriptStates.add(scriptFile, scriptData)
}
return scriptStates
}
private fun load(scriptFile: File, scriptData: JadxScriptData) {
LOG.debug { "Loading script: ${scriptFile.absolutePath}" }
val result = eval(scriptFile, scriptData)
processEvalResult(result, scriptFile)
}
private fun eval(scriptFile: File, scriptData: JadxScriptData): ResultWithDiagnostics<EvaluationResult> {
val compilationConf = createJvmCompilationConfigurationFromTemplate<JadxScript>()
val evalConf = createJvmEvaluationConfigurationFromTemplate<JadxScript> {
constructorArgs(scriptData)
}
return BasicJvmScriptingHost().eval(scriptFile.toScriptSource(), compilationConf, evalConf)
}
private fun processEvalResult(res: ResultWithDiagnostics<EvaluationResult>, scriptFile: File) {
when (res) {
is ResultWithDiagnostics.Success -> {
val result = res.value.returnValue
if (result is ResultValue.Error) {
result.error.printStackTrace()
}
}
is ResultWithDiagnostics.Failure -> {
LOG.error { "Script execution failed: ${scriptFile.name}" }
res.reports
.filter { it.severity >= ScriptDiagnostic.Severity.ERROR }
.forEach { r ->
LOG.error(r.exception) { r.render(withSeverity = false) }
}
}
}
}
}
@@ -0,0 +1,21 @@
package jadx.plugins.script.runner
import jadx.plugins.script.runtime.JadxScriptData
import java.io.File
data class ScriptStateData(
val scriptFile: File,
val scriptData: JadxScriptData,
var error: Boolean = false
)
class ScriptStates {
private val data: MutableList<ScriptStateData> = ArrayList()
fun add(scriptFile: File, scriptData: JadxScriptData) {
data.add(ScriptStateData(scriptFile, scriptData))
}
fun getScripts() = data
}
@@ -0,0 +1 @@
jadx.plugins.script.JadxScriptPlugin