Running Kotlin Scripts with bindings from a Kotlin program using ScriptEngine


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.lang.IllegalStateException
import java.nio.charset.Charset
import javax.script.*

object KotlinScriptRunner {

init {

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

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?
1 Like

You can have a look at a simple implementation I played with today. I am giving context by using an implicit receiver:

1 Like

@bjonnh I’ve tried to wrap my head around this solution. I kinda understand what you did there, but where did you found the documentation for this ? I’ve looked through the web and couldn’t find any documentation about implicit receivers.

Do you have any source for this ?

Also how “stable” is going to be this approach in the long run, looking at the code I see “experimental” in the package name, this means that the API might break in the future, correct ?

All in all, thanks for the help, I really appreciate it!

@nomemory there is really little doc so i looked at kotlinls scripting repository where they have examples and used github’s code search (super useful for poorly documented things). For the implicit receivers i found that by looking in intellij code completion and found it that way then looked into kotlin’s code to understand how it works…

As it is not documented i expect those things to break yeah…

As you can see it was quite an adventure…

@bjonnh I did the same, and I believe I found a small bug regarding the implementation.

I eventually end-up using your repo and the kotlin repos to understand how to use the API. Eventually I made it work. Quite an adventure.

Thanks for the suggestion.