# Add bindings to Kotlin JSR 223 engine

**URL:** <https://discuss.kotlinlang.org/t/add-bindings-to-kotlin-jsr-223-engine/20317>\
**Category:** Support\
**Created:** [December 20, 2020, 8:09pm UTC](https://discuss.kotlinlang.org/t/add-bindings-to-kotlin-jsr-223-engine/20317 "2020-12-20T20:09:46Z")\
**Posts on this page:** 5\
**Page:** 1

<div class="post-metadata">

**Author:** ![hanslovsky](https://avatars.discourse-cdn.com/v4/letter/h/eb8c5e/32.png) [@hanslovsky](https://discuss.kotlinlang.org/u/hanslovsky)\
**Post date:** [December 20, 2020, 8:09pm UTC](https://discuss.kotlinlang.org/t/add-bindings-to-kotlin-jsr-223-engine/20317/1 "2020-12-20T20:09:47Z")

</div>

I am trying to use a Kotlin JSR 223 engine to expose Kotlin as a scripting language in a Java GUI. The GUI has support for multiple languages already and injects variables through the engine scope bindings. This works for all languages but Kotlin does not see any of the bindings. Conversely, any variables that are defined in Kotlin, are not available to other languages. I assume that these issues are related.

I created a minimum working example. I used Java because the code base is Java but I also translated to Kotlin to confirm that the issue also occurs there.

1. Java MWE

```java
import kotlin.jvm.JvmClassMappingKt;
import kotlin.reflect.KClass;
import org.jetbrains.kotlin.cli.common.repl.KotlinJsr223JvmScriptEngineFactoryBase;
import org.jetbrains.kotlin.cli.common.repl.ScriptArgsWithTypes;
import org.jetbrains.kotlin.script.jsr223.KotlinJsr223JvmLocalScriptEngine;
import org.jetbrains.kotlin.script.jsr223.KotlinStandardJsr223ScriptTemplate;

import javax.script.Bindings;
import javax.script.ScriptContext;
import javax.script.ScriptEngine;
import javax.script.ScriptException;
import java.io.File;
import java.net.URL;
import java.net.URLClassLoader;
import java.util.Arrays;
import java.util.stream.Collectors;

public class MWE {

    private static class Factory extends KotlinJsr223JvmScriptEngineFactoryBase {

        @Override
        public ScriptEngine getScriptEngine() {
            return new KotlinJsr223JvmLocalScriptEngine(
                    this,
                    Arrays.stream(((URLClassLoader)ClassLoader.getSystemClassLoader()).getURLs()).map(URL::getFile).map(File::new).collect(Collectors.toList()),
                    KotlinStandardJsr223ScriptTemplate.class.getCanonicalName(),
                    (ctx, types) -> new ScriptArgsWithTypes(new Object[]{ctx.getBindings(ScriptContext.ENGINE_SCOPE)}, types == null ? new KClass[0] : types),
                    new KClass[] {JvmClassMappingKt.getKotlinClass(Bindings.class) }
            );
        }
    }

    public static void main(String... args) throws ScriptException {
        final ScriptEngine engine = new Factory().getScriptEngine();
        engine.getBindings(ScriptContext.ENGINE_SCOPE).put("abc", 1);
        System.out.println(engine.eval("1"));
        System.out.println(engine.eval("abc"));
    }
}

```

2. Kotlin MWE

```auto
import org.jetbrains.kotlin.cli.common.repl.KotlinJsr223JvmScriptEngineFactoryBase
import org.jetbrains.kotlin.cli.common.repl.ScriptArgsWithTypes
import org.jetbrains.kotlin.script.jsr223.KotlinJsr223JvmLocalScriptEngine
import org.jetbrains.kotlin.script.jsr223.KotlinStandardJsr223ScriptTemplate
import java.io.File
import java.net.URL
import java.net.URLClassLoader
import java.util.*
import java.util.stream.Collectors
import javax.script.Bindings
import javax.script.ScriptContext
import javax.script.ScriptEngine
import kotlin.reflect.KClass

private class Factory : KotlinJsr223JvmScriptEngineFactoryBase() {
    override fun getScriptEngine(): ScriptEngine {
        return KotlinJsr223JvmLocalScriptEngine(
                this,
                Arrays.stream((ClassLoader.getSystemClassLoader() as URLClassLoader).urLs).map { obj: URL -> obj.file }.map { File(it) }.collect(Collectors.toList()),
                KotlinStandardJsr223ScriptTemplate::class.java.canonicalName,
                { ctx: ScriptContext, types: Array<out KClass<out Any>>? ->
                    ScriptArgsWithTypes(arrayOf<Any?>(ctx.getBindings(ScriptContext.ENGINE_SCOPE)), types ?: emptyArray())
                },
                arrayOf(Bindings::class))
    }
}

fun main() {
    val engine = Factory().scriptEngine
    engine.getBindings(ScriptContext.ENGINE_SCOPE)["abc"] = 1
    println(engine.eval("1"))
    println(engine.eval("abc"))
}

```

3. relevant output (equivalent in both cases, just posting the Kotlin MWE output):

```auto
1
Exception in thread "main" javax.script.ScriptException: error: unresolved reference: abc
abc
^

    at org.jetbrains.kotlin.cli.common.repl.KotlinJsr223JvmScriptEngineBase.asJsr223EvalResult(KotlinJsr223JvmScriptEngineBase.kt:104)
    at org.jetbrains.kotlin.cli.common.repl.KotlinJsr223JvmScriptEngineBase.compileAndEval(KotlinJsr223JvmScriptEngineBase.kt:63)
    at org.jetbrains.kotlin.cli.common.repl.KotlinJsr223JvmScriptEngineBase.eval(KotlinJsr223JvmScriptEngineBase.kt:31)
    at javax.script.AbstractScriptEngine.eval(AbstractScriptEngine.java:264)
    at MWEKt.main(MWE.kt:33)
    at MWEKt.main(MWE.kt)

```

The engine can clearly evaluate a statement that does not rely on information in the bindings: It prints `1` for the first call `engine.eval("1")`. For the second call, `engine.eval("abc")`, it does not find/use `abc` in the bindings.

Do I use the engine incorrectly?

There is also another exception

```auto
java.lang.NoClassDefFoundError: com/sun/jna/Native

```

but it does not crash the application so I don’t know if it is relevant. I can share the entire stack trace if this seems relevant.

---

<div class="post-metadata">

**Author:** ![hanslovsky](https://avatars.discourse-cdn.com/v4/letter/h/eb8c5e/32.png) [@hanslovsky](https://discuss.kotlinlang.org/u/hanslovsky)\
**Post date:** [December 21, 2020, 12:53am UTC](https://discuss.kotlinlang.org/t/add-bindings-to-kotlin-jsr-223-engine/20317/2 "2020-12-21T00:53:45Z")

</div>

For completeness, I use kotlin version `1.4.10`:

```auto
$ mvn dependency:tree
org.scijava:scripting-kotlin:jar:0.1.2-SNAPSHOT
+- org.scijava:scijava-common:jar:2.83.3:compile
| +- org.scijava:parsington:jar:2.0.0:compile
| \- org.bushe:eventbus:jar:1.4:compile
+- org.jetbrains.kotlin:kotlin-compiler-embeddable:jar:1.4.10:compile
| +- org.jetbrains.kotlin:kotlin-stdlib:jar:1.4.10:compile
| | +- org.jetbrains.kotlin:kotlin-stdlib-common:jar:1.4.10:compile
| | \- org.jetbrains:annotations:jar:13.0:compile
| +- org.jetbrains.kotlin:kotlin-reflect:jar:1.4.10:runtime
| +- org.jetbrains.kotlin:kotlin-daemon-embeddable:jar:1.4.10:runtime
| \- org.jetbrains.intellij.deps:trove4j:jar:1.0.20181211:compile
+- org.jetbrains.kotlin:kotlin-script-util:jar:1.4.10:compile
| +- org.jetbrains.kotlin:kotlin-scripting-jvm:jar:1.4.10:compile
| | \- org.jetbrains.kotlin:kotlin-scripting-common:jar:1.4.10:compile
| \- org.jetbrains.kotlin:kotlin-daemon-client:jar:1.4.10:compile
| \- org.jetbrains.kotlinx:kotlinx-coroutines-core:jar:1.3.7:compile
+- org.jetbrains.kotlin:kotlin-script-runtime:jar:1.4.10:runtime
+- org.jetbrains.kotlin:kotlin-scripting-compiler-embeddable:jar:1.4.10:runtime
| \- org.jetbrains.kotlin:kotlin-scripting-compiler-impl-embeddable:jar:1.4.10:runtime
+- junit:junit:jar:4.13:test
| \- org.hamcrest:hamcrest-core:jar:1.3:test
\- org.scijava:scijava-common:jar:tests:2.83.3:test

```

---

<div class="post-metadata">

**Author:** ![hanslovsky](https://avatars.discourse-cdn.com/v4/letter/h/eb8c5e/32.png) [@hanslovsky](https://discuss.kotlinlang.org/u/hanslovsky)\
**Post date:** [December 21, 2020, 1:47am UTC](https://discuss.kotlinlang.org/t/add-bindings-to-kotlin-jsr-223-engine/20317/3 "2020-12-21T01:47:02Z")

</div>

I found out I can access the bindings via

```kotlin
engine.eval("bindings[\"abc\"]")

```

This gives me the expected result but I wonder what the best way forward is. Should I wrap the `KotlinJsr223JvmLocalScriptEngine` and replace all words `xyz` in the evaluation string with `bindings["xyz"]` if `xyz` is in bindings? That way, I would also need to make sure that there is no clash between values in `bindings` and reserved Kotlin keywords like `when` (the bindings are shared between engines).

If there is any way the `KotlinJsr223JvmLocalScriptEngine` could do all that for me, that would be much preferred.

---

<div class="post-metadata">

**Author:** ![nickallendev](https://avatars.discourse-cdn.com/v4/letter/n/cdc98d/32.png) [@nickallendev](https://discuss.kotlinlang.org/u/nickallendev)\
**Post date:** [December 21, 2020, 12:08pm UTC](https://discuss.kotlinlang.org/t/add-bindings-to-kotlin-jsr-223-engine/20317/4 "2020-12-21T12:08:51Z")

</div>

Perhaps your dependencies are out of date?

YouTrack suggests this was fixed : [Support JSR 223 bindings directly via script variables](https://youtrack.jetbrains.com/issue/KT-15125)

And the dependencies Kotlin’s source examples look quite different: [Kotlin Scripting Examples: using Kotlin scripting via JSR 223 API](https://github.com/Kotlin/kotlin-script-examples/blob/master/jvm/jsr223/jsr223.md)

Note: I haven’t used the scripting engine, just looked around out of curiosity.

---

<div class="post-metadata">

**Author:** ![hanslovsky](https://avatars.discourse-cdn.com/v4/letter/h/eb8c5e/32.png) [@hanslovsky](https://discuss.kotlinlang.org/u/hanslovsky)\
**Post date:** [December 22, 2020, 1:35am UTC](https://discuss.kotlinlang.org/t/add-bindings-to-kotlin-jsr-223-engine/20317/5 "2020-12-22T01:35:03Z")

</div>

That was indeed the solution. I first tried to update to the latest Kotlin release but that didn’t solve it, I had to update the actual dependencies. This is my `mvn dependency:tree` after the updates:

```auto
org.scijava:scripting-kotlin:jar:0.1.2-SNAPSHOT
+- org.scijava:scijava-common:jar:2.83.3:compile
| +- org.scijava:parsington:jar:2.0.0:compile
| \- org.bushe:eventbus:jar:1.4:compile
+- org.jetbrains.kotlin:kotlin-compiler-embeddable:jar:1.4.21:compile
| +- org.jetbrains.kotlin:kotlin-script-runtime:jar:1.4.21:runtime
| +- org.jetbrains.kotlin:kotlin-reflect:jar:1.4.21:runtime
| +- org.jetbrains.kotlin:kotlin-daemon-embeddable:jar:1.4.21:runtime
| \- org.jetbrains.intellij.deps:trove4j:jar:1.0.20181211:runtime
+- org.jetbrains.kotlin:kotlin-stdlib:jar:1.4.21:compile
| +- org.jetbrains.kotlin:kotlin-stdlib-common:jar:1.4.21:compile
| \- org.jetbrains:annotations:jar:13.0:compile
+- org.jetbrains.kotlin:kotlin-scripting-jsr223:jar:1.4.21:runtime
| +- org.jetbrains.kotlin:kotlin-scripting-common:jar:1.4.21:runtime
| | \- org.jetbrains.kotlinx:kotlinx-coroutines-core:jar:1.3.7:runtime
| +- org.jetbrains.kotlin:kotlin-scripting-jvm:jar:1.4.21:runtime
| +- org.jetbrains.kotlin:kotlin-scripting-jvm-host:jar:1.4.21:runtime
| \- org.jetbrains.kotlin:kotlin-scripting-compiler-embeddable:jar:1.4.21:runtime
| \- org.jetbrains.kotlin:kotlin-scripting-compiler-impl-embeddable:jar:1.4.21:runtime
+- junit:junit:jar:4.13:test
| \- org.hamcrest:hamcrest-core:jar:1.3:test
\- org.scijava:scijava-common:jar:tests:2.83.3:test

```

I don’t believe I can mark an answer as a solution here, otherwise I’d mark yours @nickallendev
