Hello,
I am currently implementing a library that is able to run Kotlin scripts (*.kts) from a Kotlin program. The code kept in the .kts files is mostly configuration that I want to change it at runtime if needed.
The class responsible with running the scripts looks like this:
package net.andreinc.mapneat.scripting
import org.jetbrains.kotlin.cli.common.environment.setIdeaIoUseFallback
import java.io.File
import java.lang.IllegalStateException
import java.nio.charset.Charset
import javax.script.*
object KotlinScriptRunner {
init {
setIdeaIoUseFallback()
}
private val scriptManager = ScriptEngineManager(currentClassLoader())
private val scriptEngine : ScriptEngine = scriptManager.getEngineByExtension("kts")!!
fun getScriptEngine() : ScriptEngine {
return this.scriptEngine
}
private fun readResource(resource: String, charset: Charset = Charsets.UTF_8) : String {
val resourceUrl = javaClass.classLoader?.getResource(resource)
return resourceUrl!!.readText(charset)
}
private fun currentClassLoader() : ClassLoader {
return Thread.currentThread().contextClassLoader
}
fun eval(scriptContent: String, compileFirst: Boolean = true, bindings: Map<String, String> = mapOf()) : Any {
if (compileFirst) {
when (scriptEngine) {
is Compilable -> {
val compiledCode = scriptEngine.compile(scriptContent)
return compiledCode.eval(scriptEngine.createBindings().apply { putAll(bindings) })
}
else -> throw IllegalStateException("Kotlin is not an instance of Compilable. Cannot compile script.")
}
}
else {
return scriptEngine.eval(scriptContent, scriptEngine.createBindings().apply { putAll(bindings) })
}
}
fun evalFile(file: String, readAsResource: Boolean, compileFirst: Boolean = true, bindings: Map<String, String> = mapOf()) : Any {
val content : String = if (readAsResource) readResource(file) else File(file).readText()
return eval(content, compileFirst, bindings)
}
}
As you can see the eval
method allows me to send bindings as a map (variables that I can pass to the script).
Problem is in the Script those variables have to be accessed from bindings[variableName]
. E.g .kts:
json(bindings["json"] as String) {
"name" *= "$.name"
"a" /= bindings["a"] as String
}.getPrettyString()
Problem is that in IntelliJ bindings[]
is an unresolved reference and the Scripts gets invalidated. I can run the script from the command-line, but in IntelliJ i have problems, because this invalidates my project:
- Has anyone of you knows how to handle this situation in IntelliJ?
- Is there another way to pass bindings to the Kotlin Script?
- Given the experimental nature of the code I am using, is the code looking ok from your perspective?