diff --git a/compiler/cli/cli-common/src/org/jetbrains/kotlin/cli/common/repl/KotlinJsr223JvmScriptEngineBase.kt b/compiler/cli/cli-common/src/org/jetbrains/kotlin/cli/common/repl/KotlinJsr223JvmScriptEngineBase.kt index de387f9a6c1..40ec7c2d853 100644 --- a/compiler/cli/cli-common/src/org/jetbrains/kotlin/cli/common/repl/KotlinJsr223JvmScriptEngineBase.kt +++ b/compiler/cli/cli-common/src/org/jetbrains/kotlin/cli/common/repl/KotlinJsr223JvmScriptEngineBase.kt @@ -22,18 +22,20 @@ import java.util.concurrent.atomic.AtomicInteger import javax.script.* val KOTLIN_SCRIPT_HISTORY_BINDINGS_KEY = "kotlin.script.history" - +val KOTLIN_SCRIPT_LINE_NUMBER_BINDINGS_KEY = "kotlin.script.line.number" // TODO consider additional error handling -val Bindings.kotlinScriptHistory: MutableList +var Bindings.kotlinScriptHistory: MutableList @Suppress("UNCHECKED_CAST") get() = getOrPut(KOTLIN_SCRIPT_HISTORY_BINDINGS_KEY, { arrayListOf() }) as MutableList + set(v) { put(KOTLIN_SCRIPT_HISTORY_BINDINGS_KEY, v) } +val Bindings.kotlinScriptLineNumber: AtomicInteger + @Suppress("UNCHECKED_CAST") + get() = getOrPut(KOTLIN_SCRIPT_LINE_NUMBER_BINDINGS_KEY, { AtomicInteger(0) }) as AtomicInteger abstract class KotlinJsr223JvmScriptEngineBase(protected val myFactory: ScriptEngineFactory) : AbstractScriptEngine(), ScriptEngine, Compilable { - protected var codeLineNumber = AtomicInteger(0) - protected abstract val replCompiler: ReplCompileAction protected abstract val replScriptEvaluator: ReplFullEvaluator @@ -49,13 +51,20 @@ abstract class KotlinJsr223JvmScriptEngineBase(protected val myFactory: ScriptEn override fun getFactory(): ScriptEngineFactory = myFactory - fun nextCodeLine(code: String) = ReplCodeLine(codeLineNumber.incrementAndGet(), code) + // the parameter could be used in the future when we decide to keep state completely in the context and solve appropriate problems (now e.g. replCompiler keeps separate state) + fun nextCodeLine(@Suppress("UNUSED_PARAMETER") context: ScriptContext, code: String) = ReplCodeLine(this.context.getBindings(ScriptContext.ENGINE_SCOPE).kotlinScriptLineNumber.incrementAndGet(), code) + + private fun getCurrentHistory(@Suppress("UNUSED_PARAMETER") context: ScriptContext) = this.context.getBindings(ScriptContext.ENGINE_SCOPE).kotlinScriptHistory + + private fun setContextHistory(@Suppress("UNUSED_PARAMETER") context: ScriptContext, history: ArrayList) { + this.context.getBindings(ScriptContext.ENGINE_SCOPE).kotlinScriptHistory = history + } open fun overrideScriptArgs(context: ScriptContext): ScriptArgsWithTypes? = null open fun compileAndEval(script: String, context: ScriptContext): Any? { - val codeLine = nextCodeLine(script) - val history = context.getBindings(ScriptContext.ENGINE_SCOPE).kotlinScriptHistory + val codeLine = nextCodeLine(context, script) + val history = getCurrentHistory(context) val result = replScriptEvaluator.compileAndEval(codeLine, scriptArgs = overrideScriptArgs(context), verifyHistory = history) val ret = when (result) { is ReplEvalResult.ValueResult -> result.value @@ -64,13 +73,13 @@ abstract class KotlinJsr223JvmScriptEngineBase(protected val myFactory: ScriptEn is ReplEvalResult.Incomplete -> throw ScriptException("error: incomplete code") is ReplEvalResult.HistoryMismatch -> throw ScriptException("Repl history mismatch at line: ${result.lineNo}") } - context.getBindings(ScriptContext.ENGINE_SCOPE).put(KOTLIN_SCRIPT_HISTORY_BINDINGS_KEY, ArrayList(result.completedEvalHistory)) + setContextHistory(context, ArrayList(result.completedEvalHistory)) return ret } open fun compile(script: String, context: ScriptContext): CompiledScript { - val codeLine = nextCodeLine(script) - val history = context.getBindings(ScriptContext.ENGINE_SCOPE).kotlinScriptHistory + val codeLine = nextCodeLine(context, script) + val history = getCurrentHistory(context) val result = replCompiler.compile(codeLine, history) val compiled = when (result) { @@ -79,7 +88,8 @@ abstract class KotlinJsr223JvmScriptEngineBase(protected val myFactory: ScriptEn is ReplCompileResult.HistoryMismatch -> throw ScriptException("Repl history mismatch at line: ${result.lineNo}") is ReplCompileResult.CompiledClasses -> result } - context.getBindings(ScriptContext.ENGINE_SCOPE).put(KOTLIN_SCRIPT_HISTORY_BINDINGS_KEY, ArrayList(result.compiledHistory)) + // TODO: check if it is ok to keep compiled history in the same place as compiledEval one + setContextHistory(context, ArrayList(result.compiledHistory)) return CompiledKotlinScript(this, codeLine, compiled) } @@ -98,7 +108,7 @@ abstract class KotlinJsr223JvmScriptEngineBase(protected val myFactory: ScriptEn is ReplEvalResult.Incomplete -> throw ScriptException("error: incomplete code") is ReplEvalResult.HistoryMismatch -> throw ScriptException("Repl history mismatch at line: ${result.lineNo}") } - context.getBindings(ScriptContext.ENGINE_SCOPE).put(KOTLIN_SCRIPT_HISTORY_BINDINGS_KEY, ArrayList(result.completedEvalHistory)) + setContextHistory(context, ArrayList(result.completedEvalHistory)) return ret } diff --git a/compiler/cli/src/org/jetbrains/kotlin/cli/jvm/repl/GenericReplCompiler.kt b/compiler/cli/src/org/jetbrains/kotlin/cli/jvm/repl/GenericReplCompiler.kt index f0dd9592a38..0a163aa8663 100644 --- a/compiler/cli/src/org/jetbrains/kotlin/cli/jvm/repl/GenericReplCompiler.kt +++ b/compiler/cli/src/org/jetbrains/kotlin/cli/jvm/repl/GenericReplCompiler.kt @@ -58,7 +58,7 @@ open class GenericReplCompiler(disposable: Disposable, removedCompiledLines.zip(removedAnalyzedLines).forEach { if (it.first.first != it.second) { - throw IllegalStateException("History mistmatch when resetting lines") + throw IllegalStateException("History mismatch when resetting lines") } } diff --git a/libraries/examples/kotlin-jsr223-daemon-local-eval-example/src/test/kotlin/org/jetbrains/kotlin/script/jsr223/KotlinJsr223ScriptEngineIT.kt b/libraries/examples/kotlin-jsr223-daemon-local-eval-example/src/test/kotlin/org/jetbrains/kotlin/script/jsr223/KotlinJsr223ScriptEngineIT.kt index bb208d0dbe7..fa31e6abb28 100644 --- a/libraries/examples/kotlin-jsr223-daemon-local-eval-example/src/test/kotlin/org/jetbrains/kotlin/script/jsr223/KotlinJsr223ScriptEngineIT.kt +++ b/libraries/examples/kotlin-jsr223-daemon-local-eval-example/src/test/kotlin/org/jetbrains/kotlin/script/jsr223/KotlinJsr223ScriptEngineIT.kt @@ -92,6 +92,25 @@ obj val res3 = invocator!!.invokeMethod(res1, "fn1", 3) Assert.assertEquals(6, res3) } + + @Test + fun testEvalWithContext() { + val engine = ScriptEngineManager().getEngineByExtension("kts")!! + + engine.put("z", 33) + + engine.eval("""val x = 10 + bindings["z"] as Int""") + + val result = engine.eval("""x + 20""") + Assert.assertEquals(63, result) + + // in the current implementation the history is shared between contexts, so "x" could also be used in this line, + // but this behaviour probably will not be preserved in the future, since contexts may become completely isolated + val result2 = engine.eval("""11 + bindings["boundValue"] as Int""", engine.createBindings().apply { + put("boundValue", 100) + }) + Assert.assertEquals(111, result2) + } } fun assertThrows(exceptionClass: Class<*>, body: () -> Unit) { diff --git a/libraries/examples/kotlin-jsr223-local-example/src/test/kotlin/org/jetbrains/kotlin/script/jsr223/KotlinJsr223ScriptEngineIT.kt b/libraries/examples/kotlin-jsr223-local-example/src/test/kotlin/org/jetbrains/kotlin/script/jsr223/KotlinJsr223ScriptEngineIT.kt index 7f9f9bd6c63..793eee87849 100644 --- a/libraries/examples/kotlin-jsr223-local-example/src/test/kotlin/org/jetbrains/kotlin/script/jsr223/KotlinJsr223ScriptEngineIT.kt +++ b/libraries/examples/kotlin-jsr223-local-example/src/test/kotlin/org/jetbrains/kotlin/script/jsr223/KotlinJsr223ScriptEngineIT.kt @@ -92,6 +92,25 @@ obj val res3 = invocator!!.invokeMethod(res1, "fn1", 3) Assert.assertEquals(6, res3) } + + @Test + fun testEvalWithContext() { + val engine = ScriptEngineManager().getEngineByExtension("kts")!! + + engine.put("z", 33) + + engine.eval("""val x = 10 + bindings["z"] as Int""") + + val result = engine.eval("""x + 20""") + Assert.assertEquals(63, result) + + // in the current implementation the history is shared between contexts, so "x" could also be used in this line, + // but this behaviour probably will not be preserved in the future, since contexts may become completely isolated + val result2 = engine.eval("""11 + bindings["boundValue"] as Int""", engine.createBindings().apply { + put("boundValue", 100) + }) + Assert.assertEquals(111, result2) + } } fun assertThrows(exceptionClass: Class<*>, body: () -> Unit) { diff --git a/libraries/tools/kotlin-script-util/src/main/kotlin/org/jetbrains/kotlin/script/jsr223/KotlinJsr223JvmDaemonCompileScriptEngine.kt b/libraries/tools/kotlin-script-util/src/main/kotlin/org/jetbrains/kotlin/script/jsr223/KotlinJsr223JvmDaemonCompileScriptEngine.kt index 1088b7b3385..a891dcf8b1b 100644 --- a/libraries/tools/kotlin-script-util/src/main/kotlin/org/jetbrains/kotlin/script/jsr223/KotlinJsr223JvmDaemonCompileScriptEngine.kt +++ b/libraries/tools/kotlin-script-util/src/main/kotlin/org/jetbrains/kotlin/script/jsr223/KotlinJsr223JvmDaemonCompileScriptEngine.kt @@ -36,8 +36,8 @@ class KotlinJsr223JvmDaemonCompileScriptEngine( compilerJar: File, templateClasspath: List, templateClassName: String, - getScriptArgs: (ScriptContext, Array>?) -> ScriptArgsWithTypes?, - scriptArgsTypes: Array>?, + val getScriptArgs: (ScriptContext, Array>?) -> ScriptArgsWithTypes?, + val scriptArgsTypes: Array>?, compilerOut: OutputStream = System.err ) : KotlinJsr223JvmScriptEngineBase(factory), KotlinJsr223JvmInvocableScriptEngine { @@ -61,6 +61,8 @@ class KotlinJsr223JvmDaemonCompileScriptEngine( override val replScriptEvaluator: ReplFullEvaluator get() = localEvaluator + override fun overrideScriptArgs(context: ScriptContext): ScriptArgsWithTypes? = getScriptArgs(context, scriptArgsTypes) + private fun connectToCompileService(compilerJar: File): CompileService { val compilerId = CompilerId.makeCompilerId(compilerJar) val daemonOptions = configureDaemonOptions() diff --git a/libraries/tools/kotlin-script-util/src/main/kotlin/org/jetbrains/kotlin/script/jsr223/KotlinJsr223JvmLocalScriptEngine.kt b/libraries/tools/kotlin-script-util/src/main/kotlin/org/jetbrains/kotlin/script/jsr223/KotlinJsr223JvmLocalScriptEngine.kt index 417b63393af..4376cf1fabc 100644 --- a/libraries/tools/kotlin-script-util/src/main/kotlin/org/jetbrains/kotlin/script/jsr223/KotlinJsr223JvmLocalScriptEngine.kt +++ b/libraries/tools/kotlin-script-util/src/main/kotlin/org/jetbrains/kotlin/script/jsr223/KotlinJsr223JvmLocalScriptEngine.kt @@ -38,8 +38,8 @@ class KotlinJsr223JvmLocalScriptEngine( factory: ScriptEngineFactory, val templateClasspath: List, templateClassName: String, - getScriptArgs: (ScriptContext, Array>?) -> ScriptArgsWithTypes?, - scriptArgsTypes: Array>? + val getScriptArgs: (ScriptContext, Array>?) -> ScriptArgsWithTypes?, + val scriptArgsTypes: Array>? ) : KotlinJsr223JvmScriptEngineBase(factory), KotlinJsr223JvmInvocableScriptEngine { override val replCompiler: ReplCompiler by lazy { @@ -54,6 +54,8 @@ class KotlinJsr223JvmLocalScriptEngine( override val replScriptEvaluator: ReplFullEvaluator get() = localEvaluator + override fun overrideScriptArgs(context: ScriptContext): ScriptArgsWithTypes? = getScriptArgs(context, scriptArgsTypes) + private fun makeScriptDefinition(templateClasspath: List, templateClassName: String): KotlinScriptDefinition { val classloader = URLClassLoader(templateClasspath.map { it.toURI().toURL() }.toTypedArray(), this.javaClass.classLoader) val cls = classloader.loadClass(templateClassName)