Implement JSR-223 functionality on top of the "new" REPL

This commit is contained in:
Ilya Chernikov
2019-04-16 21:00:02 +02:00
parent 46915df56f
commit ea068e483c
13 changed files with 592 additions and 33 deletions
@@ -60,18 +60,21 @@ open class BasicReplStageHistory<T>(override val lock: ReentrantReadWriteLock =
override fun resetTo(id: ILineId): Iterable<ILineId> {
lock.write {
val idx = indexOfFirst { it.id == id }
if (idx < 0) throw java.util.NoSuchElementException("Cannot rest to inexistent line ${id.no}")
return if (idx < lastIndex) {
val removed = asSequence().drop(idx + 1).map { it.id }.toList()
removeRange(idx + 1, size)
currentGeneration.incrementAndGet()
removed
}
else {
currentGeneration.incrementAndGet()
emptyList()
}
return tryResetTo(id) ?: throw NoSuchElementException("Cannot reset to non-existent line ${id.no}")
}
}
protected fun tryResetTo(id: ILineId): List<ILineId>? {
val idx = indexOfFirst { it.id == id }
if (idx < 0) return null
return if (idx < lastIndex) {
val removed = asSequence().drop(idx + 1).map { it.id }.toList()
removeRange(idx + 1, size)
currentGeneration.incrementAndGet()
removed
} else {
currentGeneration.incrementAndGet()
emptyList()
}
}
}
@@ -20,13 +20,11 @@ import java.io.File
import java.util.concurrent.locks.ReentrantReadWriteLock
import kotlin.concurrent.write
class GenericReplCompilingEvaluator(val compiler: ReplCompiler,
baseClasspath: Iterable<File>,
baseClassloader: ClassLoader? = Thread.currentThread().contextClassLoader,
private val fallbackScriptArgs: ScriptArgsWithTypes? = null,
repeatingMode: ReplRepeatingMode = ReplRepeatingMode.REPEAT_ONLY_MOST_RECENT
open class GenericReplCompilingEvaluatorBase(
val compiler: ReplCompiler,
val evaluator: ReplEvaluator,
private val fallbackScriptArgs: ScriptArgsWithTypes? = null
) : ReplFullEvaluator {
private val evaluator = GenericReplEvaluator(baseClasspath, baseClassloader, fallbackScriptArgs, repeatingMode)
override fun createState(lock: ReentrantReadWriteLock): IReplStageState<*> = AggregatedReplStageState(compiler.createState(lock), evaluator.createState(lock), lock)
@@ -75,7 +73,7 @@ class GenericReplCompilingEvaluator(val compiler: ReplCompiler,
}
override fun eval(state: IReplStageState<*>, compileResult: ReplCompileResult.CompiledClasses, scriptArgs: ScriptArgsWithTypes?, invokeWrapper: InvokeWrapper?): ReplEvalResult =
evaluator.eval(state, compileResult, scriptArgs, invokeWrapper)
evaluator.eval(state, compileResult, scriptArgs, invokeWrapper)
override fun check(state: IReplStageState<*>, codeLine: ReplCodeLine): ReplCheckResult = compiler.check(state, codeLine)
@@ -93,12 +91,24 @@ class GenericReplCompilingEvaluator(val compiler: ReplCompiler,
private val evaluator: ReplEvaluator,
private val defaultScriptArgs: ScriptArgsWithTypes?) : Evaluable {
override fun eval(scriptArgs: ScriptArgsWithTypes?, invokeWrapper: InvokeWrapper?): ReplEvalResult =
evaluator.eval(state, compiledCode, scriptArgs ?: defaultScriptArgs, invokeWrapper)
evaluator.eval(state, compiledCode, scriptArgs ?: defaultScriptArgs, invokeWrapper)
}
}
class GenericReplCompilingEvaluator(
compiler: ReplCompiler,
baseClasspath: Iterable<File>,
baseClassloader: ClassLoader? = Thread.currentThread().contextClassLoader,
fallbackScriptArgs: ScriptArgsWithTypes? = null,
repeatingMode: ReplRepeatingMode = ReplRepeatingMode.REPEAT_ONLY_MOST_RECENT
) : GenericReplCompilingEvaluatorBase(
compiler,
GenericReplEvaluator(baseClasspath, baseClassloader, fallbackScriptArgs, repeatingMode),
fallbackScriptArgs
)
private fun AggregatedReplStageState<*, *>.adjustHistories(): Iterable<ILineId>? =
state2.history.peek()?.let {
state1.history.resetTo(it.id)
}
state2.history.peek()?.let {
state1.history.resetTo(it.id)
}
?: state1.history.reset()
@@ -0,0 +1,37 @@
import com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar
import org.gradle.jvm.tasks.Jar
description = "Kotlin Scripting JSR-223 support"
plugins { java }
val packedJars by configurations.creating
dependencies {
packedJars(project(":kotlin-scripting-jsr223")) { isTransitive = false }
runtime(project(":kotlin-script-runtime"))
runtime(kotlinStdlib())
runtime(project(":kotlin-scripting-common"))
runtime(project(":kotlin-scripting-jvm"))
runtime(project(":kotlin-script-util"))
runtime(projectRuntimeJar(":kotlin-compiler-embeddable"))
runtime(project(":kotlin-scripting-compiler-embeddable"))
}
sourceSets {
"main" {}
"test" {}
}
publish()
noDefaultJar()
runtimeJar(rewriteDepsToShadedCompiler(
task<ShadowJar>("shadowJar") {
from(packedJars)
}
))
sourcesJar()
javadocJar()
@@ -0,0 +1,32 @@
plugins {
kotlin("jvm")
}
jvmTarget = "1.6"
dependencies {
compile(project(":kotlin-script-runtime"))
compile(kotlinStdlib())
compile(project(":kotlin-scripting-common"))
compile(project(":kotlin-scripting-jvm"))
compile(project(":kotlin-scripting-jvm-host"))
compile(project(":kotlin-script-util"))
compile(project(":kotlin-scripting-compiler"))
compile(project(":kotlin-scripting-compiler-impl"))
compileOnly(project(":compiler:cli"))
compileOnly(project(":kotlin-reflect-api"))
compileOnly(intellijCoreDep())
runtime(projectRuntimeJar(":kotlin-compiler"))
runtime(project(":kotlin-reflect"))
}
sourceSets {
"main" { projectDefault() }
"test" { projectDefault() }
}
publish()
standardPublicJars()
@@ -0,0 +1,2 @@
kotlin.script.experimental.jsr223.KotlinJsr223JvmScriptEngineFactory
@@ -0,0 +1,50 @@
/*
* Copyright 2010-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license
* that can be found in the license/LICENSE.txt file.
*/
package kotlin.script.experimental.jsr223
import org.jetbrains.kotlin.cli.common.repl.KOTLIN_SCRIPT_ENGINE_BINDINGS_KEY
import org.jetbrains.kotlin.cli.common.repl.KOTLIN_SCRIPT_STATE_BINDINGS_KEY
import javax.script.Bindings
import javax.script.ScriptEngine
import kotlin.script.experimental.annotations.KotlinScript
import kotlin.script.templates.ScriptTemplateDefinition
import kotlin.script.templates.standard.ScriptTemplateWithBindings
@Suppress("unused")
@KotlinScript
abstract class KotlinJsr223DefaultScript(val jsr223Bindings: Bindings) : ScriptTemplateWithBindings(jsr223Bindings) {
private val myEngine: ScriptEngine? get() = bindings[KOTLIN_SCRIPT_ENGINE_BINDINGS_KEY]?.let { it as? ScriptEngine }
private inline fun <T> withMyEngine(body: (ScriptEngine) -> T): T =
myEngine?.let(body) ?: throw IllegalStateException("Script engine for `eval` call is not found")
fun eval(script: String, newBindings: Bindings): Any? =
withMyEngine {
val savedState =
newBindings[KOTLIN_SCRIPT_STATE_BINDINGS_KEY]?.takeIf { it === this.jsr223Bindings[KOTLIN_SCRIPT_STATE_BINDINGS_KEY] }
?.apply {
newBindings[KOTLIN_SCRIPT_STATE_BINDINGS_KEY] = null
}
val res = it.eval(script, newBindings)
savedState?.apply {
newBindings[KOTLIN_SCRIPT_STATE_BINDINGS_KEY] = savedState
}
res
}
fun eval(script: String): Any? =
withMyEngine {
val savedState = jsr223Bindings.remove(KOTLIN_SCRIPT_STATE_BINDINGS_KEY)
val res = it.eval(script, jsr223Bindings)
savedState?.apply {
jsr223Bindings[KOTLIN_SCRIPT_STATE_BINDINGS_KEY] = savedState
}
res
}
fun createBindings(): Bindings = withMyEngine { it.createBindings() }
}
@@ -0,0 +1,93 @@
/*
* Copyright 2010-2019 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package kotlin.script.experimental.jsr223
import org.jetbrains.kotlin.cli.common.repl.InvokeWrapper
import org.jetbrains.kotlin.cli.common.repl.renderReplStackTrace
import org.jetbrains.kotlin.utils.tryCreateCallableMapping
import java.lang.reflect.Proxy
import javax.script.Invocable
import javax.script.ScriptException
import kotlin.reflect.KFunction
import kotlin.reflect.KParameter
import kotlin.reflect.full.functions
import kotlin.reflect.full.safeCast
interface KotlinJsr223InvocableScriptEngine : Invocable {
val invokeWrapper: InvokeWrapper?
val backwardInstancesHistory: Sequence<Any>
val baseClassLoader: ClassLoader
fun instancesForInvokeSearch(requestedReceiver: Any) =
sequenceOf(requestedReceiver) + backwardInstancesHistory.filterNot { it == requestedReceiver }
override fun invokeFunction(name: String?, vararg args: Any?): Any? {
if (name == null) throw java.lang.NullPointerException("function name cannot be null")
return invokeImpl(backwardInstancesHistory, name, args)
}
override fun invokeMethod(thiz: Any?, name: String?, vararg args: Any?): Any? {
if (name == null) throw java.lang.NullPointerException("method name cannot be null")
if (thiz == null) throw IllegalArgumentException("cannot invoke method on the null object")
return invokeImpl(instancesForInvokeSearch(thiz), name, args)
}
private fun invokeImpl(possibleReceivers: Sequence<Any>, name: String, args: Array<out Any?>): Any? {
// TODO: cache the method lookups?
val (fn, mapping) = possibleReceivers.mapNotNull { instance ->
val candidates = instance::class.functions.filter { it.name == name }
candidates.findMapping(listOf(instance) + args)
}.filterNotNull().firstOrNull() ?: throw NoSuchMethodException("no suitable function '$name' found")
val res = try {
if (invokeWrapper != null) {
invokeWrapper!!.invoke {
fn.callBy(mapping)
}
} else {
fn.callBy(mapping)
}
} catch (e: Throwable) {
// ignore everything in the stack trace until this constructor call
throw ScriptException(renderReplStackTrace(e.cause!!, startFromMethodName = fn.name))
}
return if (fn.returnType.classifier == Unit::class) Unit else res
}
override fun <T : Any> getInterface(clasz: Class<T>?): T? {
return proxyInterface(backwardInstancesHistory, clasz)
}
override fun <T : Any> getInterface(thiz: Any?, clasz: Class<T>?): T? {
if (thiz == null) throw IllegalArgumentException("object cannot be null")
return proxyInterface(instancesForInvokeSearch(thiz), clasz)
}
private fun <T : Any> proxyInterface(possibleReceivers: Sequence<Any>, clasz: Class<T>?): T? {
if (clasz == null) throw IllegalArgumentException("class object cannot be null")
if (!clasz.isInterface) throw IllegalArgumentException("expecting interface")
// TODO: cache the method lookups?
val proxy = Proxy.newProxyInstance(baseClassLoader, arrayOf(clasz)) { _, method, args ->
invokeImpl(possibleReceivers, method.name, args ?: emptyArray())
}
return clasz.kotlin.safeCast(proxy)
}
}
private fun Iterable<KFunction<*>>.findMapping(args: List<Any?>): Pair<KFunction<*>, Map<KParameter, Any?>>? {
for (fn in this) {
val mapping = tryCreateCallableMapping(fn, args)
if (mapping != null) return fn to mapping
}
return null
}
@@ -0,0 +1,61 @@
/*
* Copyright 2010-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license
* that can be found in the license/LICENSE.txt file.
*/
package kotlin.script.experimental.jsr223
import org.jetbrains.kotlin.cli.common.repl.*
import java.util.concurrent.locks.ReentrantReadWriteLock
import javax.script.ScriptContext
import javax.script.ScriptEngineFactory
import kotlin.reflect.KClass
import kotlin.script.experimental.api.ScriptEvaluationConfiguration
import kotlin.script.experimental.jvm.baseClassLoader
import kotlin.script.experimental.jvm.dependenciesFromCurrentContext
import kotlin.script.experimental.jvm.jvm
import kotlin.script.experimental.jvmhost.createJvmCompilationConfigurationFromTemplate
import kotlin.script.experimental.jvmhost.createJvmEvaluationConfigurationFromTemplate
import kotlin.script.experimental.jvmhost.repl.JvmReplCompiler
import kotlin.script.experimental.jvmhost.repl.JvmReplEvaluator
import kotlin.script.experimental.jvmhost.repl.JvmReplEvaluatorState
class KotlinJsr223ScriptEngine(
factory: ScriptEngineFactory,
val getScriptArgs: (ScriptContext, Array<out KClass<out Any>>?) -> ScriptArgsWithTypes?,
val scriptArgsTypes: Array<out KClass<out Any>>?
) : KotlinJsr223JvmScriptEngineBase(factory), KotlinJsr223InvocableScriptEngine {
val compilationConfiguration = createJvmCompilationConfigurationFromTemplate<KotlinJsr223DefaultScript> {
jvm {
dependenciesFromCurrentContext(wholeClasspath = true)
}
}
val evaluationConfiguration = createJvmEvaluationConfigurationFromTemplate<KotlinJsr223DefaultScript>()
override val replCompiler: ReplCompiler by lazy {
JvmReplCompiler(compilationConfiguration)
}
private val localEvaluator by lazy {
GenericReplCompilingEvaluatorBase(replCompiler, JvmReplEvaluator(evaluationConfiguration))
}
override val replEvaluator: ReplFullEvaluator get() = localEvaluator
internal val state: IReplStageState<*> get() = getCurrentState(getContext())
override fun createState(lock: ReentrantReadWriteLock): IReplStageState<*> = replEvaluator.createState(lock)
override fun overrideScriptArgs(context: ScriptContext): ScriptArgsWithTypes? = getScriptArgs(context, scriptArgsTypes)
override val invokeWrapper: InvokeWrapper?
get() = null
override val backwardInstancesHistory: Sequence<Any>
get() = getCurrentState(getContext()).asState(JvmReplEvaluatorState::class.java).history.asReversed().asSequence().map { it.item }
override val baseClassLoader: ClassLoader
get() = evaluationConfiguration[ScriptEvaluationConfiguration.jvm.baseClassLoader]!!
}
@@ -0,0 +1,28 @@
/*
* Copyright 2010-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license
* that can be found in the license/LICENSE.txt file.
*/
@file:Suppress("unused")
// could be used externally in javax.script.ScriptEngineFactory META-INF file
package kotlin.script.experimental.jsr223
import org.jetbrains.kotlin.cli.common.repl.KotlinJsr223JvmScriptEngineFactoryBase
import org.jetbrains.kotlin.cli.common.repl.ScriptArgsWithTypes
import org.jetbrains.kotlin.script.util.*
import javax.script.Bindings
import javax.script.ScriptContext
import javax.script.ScriptEngine
class KotlinJsr223JvmScriptEngineFactory : KotlinJsr223JvmScriptEngineFactoryBase() {
override fun getScriptEngine(): ScriptEngine =
KotlinJsr223ScriptEngine(
this,
{ ctx, types -> ScriptArgsWithTypes(arrayOf(ctx.getBindings(ScriptContext.ENGINE_SCOPE)), types ?: emptyArray()) },
arrayOf(Bindings::class)
)
}
@@ -0,0 +1,214 @@
/*
* Copyright 2010-2019 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package kotlin.script.experimental.jsr223.test
import org.jetbrains.kotlin.cli.common.environment.setIdeaIoUseFallback
import org.jetbrains.kotlin.config.KotlinCompilerVersion
import org.junit.Assert
import org.junit.Test
import javax.script.*
import kotlin.script.experimental.jsr223.KotlinJsr223ScriptEngine
class KotlinJsr223ScriptEngineIT {
init {
setIdeaIoUseFallback()
}
@Test
fun testEngineFactory() {
val factory = ScriptEngineManager().getEngineByExtension("kts").factory
Assert.assertNotNull(factory)
factory!!.apply {
Assert.assertEquals("kotlin", languageName)
Assert.assertEquals(KotlinCompilerVersion.VERSION, languageVersion)
Assert.assertEquals("kotlin", engineName)
Assert.assertEquals(KotlinCompilerVersion.VERSION, engineVersion)
Assert.assertEquals(listOf("kts"), extensions)
Assert.assertEquals(listOf("text/x-kotlin"), mimeTypes)
Assert.assertEquals(listOf("kotlin"), names)
Assert.assertEquals("obj.method(arg1, arg2, arg3)", getMethodCallSyntax("obj", "method", "arg1", "arg2", "arg3"))
Assert.assertEquals("print(\"Hello, world!\")", getOutputStatement("Hello, world!"))
Assert.assertEquals(KotlinCompilerVersion.VERSION, getParameter(ScriptEngine.LANGUAGE_VERSION))
val sep = System.getProperty("line.separator")
val prog = arrayOf("val x: Int = 3", "var y = x + 2")
Assert.assertEquals(prog.joinToString(sep) + sep, getProgram(*prog))
}
}
@Test
fun testEngine() {
val factory = ScriptEngineManager().getEngineByExtension("kts").factory
Assert.assertNotNull(factory)
val engine = factory!!.scriptEngine
Assert.assertNotNull(engine as? KotlinJsr223ScriptEngine)
Assert.assertSame(factory, engine!!.factory)
val bindings = engine.createBindings()
Assert.assertTrue(bindings is SimpleBindings)
}
@Test
fun testSimpleEval() {
val engine = ScriptEngineManager().getEngineByExtension("kts")!!
val res1 = engine.eval("val x = 3")
Assert.assertNull(res1)
val res2 = engine.eval("x + 2")
Assert.assertEquals(5, res2)
}
@Test
fun testEvalWithError() {
val engine = ScriptEngineManager().getEngineByExtension("kts")!!
try {
engine.eval("java.lang.fish")
Assert.fail("Script error expected")
} catch (e: ScriptException) {}
val res1 = engine.eval("val x = 3")
Assert.assertNull(res1)
try {
engine.eval("y")
Assert.fail("Script error expected")
} catch (e: ScriptException) {
Assert.assertTrue(
"Expected message to contain \"Unresolved reference: y\", actual: \"${e.message}\"",
e.message?.contains("Unresolved reference: y") ?: false
)
}
val res3 = engine.eval("x + 2")
Assert.assertEquals(5, res3)
}
@Test
fun testEngineRepeatWithReset() {
val code = "open class A {}\n" +
"class B : A() {}"
val engine = ScriptEngineManager().getEngineByExtension("kts") as KotlinJsr223ScriptEngine
val res1 = engine.eval(code)
Assert.assertNull(res1)
engine.state.history.reset()
engine.eval(code)
}
@Test
fun testInvocable() {
val engine = ScriptEngineManager().getEngineByExtension("kts")!!
val res1 = engine.eval("""
fun fn(x: Int) = x + 2
val obj = object {
fun fn1(x: Int) = x + 3
}
obj
""")
Assert.assertNotNull(res1)
val invocator = engine as? Invocable
Assert.assertNotNull(invocator)
assertThrows(NoSuchMethodException::class.java) {
invocator!!.invokeFunction("fn1", 3)
}
val res2 = invocator!!.invokeFunction("fn", 3)
Assert.assertEquals(5, res2)
// TODO: fix and restore
// assertThrows(NoSuchMethodException::class.java) {
// invocator!!.invokeMethod(res1, "fn", 3)
// }
val res3 = invocator!!.invokeMethod(res1, "fn1", 3)
Assert.assertEquals(6, res3)
}
@Test
fun testSimpleCompilable() {
val engine = ScriptEngineManager().getEngineByExtension("kts") as KotlinJsr223ScriptEngine
val comp1 = engine.compile("val x = 3")
val comp2 = engine.compile("x + 2")
val res1 = comp1.eval()
Assert.assertNull(res1)
val res2 = comp2.eval()
Assert.assertEquals(5, res2)
}
@Test
fun testMultipleCompilable() {
val engine = ScriptEngineManager().getEngineByExtension("kts") as KotlinJsr223ScriptEngine
val compiled1 = engine.compile("""listOf(1,2,3).joinToString(",")""")
val compiled2 = engine.compile("""val x = bindings["boundValue"] as Int + bindings["z"] as Int""")
val compiled3 = engine.compile("""x""")
Assert.assertEquals("1,2,3", compiled1.eval())
Assert.assertEquals("1,2,3", compiled1.eval())
Assert.assertEquals("1,2,3", compiled1.eval())
Assert.assertEquals("1,2,3", compiled1.eval())
engine.getBindings(ScriptContext.ENGINE_SCOPE).apply {
put("boundValue", 100)
put("z", 33)
}
compiled2.eval()
Assert.assertEquals(133, compiled3.eval())
Assert.assertEquals(133, compiled3.eval())
Assert.assertEquals(133, compiled3.eval())
}
@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)
}
@Test
fun testSimpleEvalInEval() {
val engine = ScriptEngineManager().getEngineByExtension("kts")!!
val res1 = engine.eval("val x = 3")
Assert.assertNull(res1)
val res2 = engine.eval("val y = eval(\"\$x + 2\") as Int\ny")
Assert.assertEquals(5, res2)
val res3 = engine.eval("y + 2")
Assert.assertEquals(7, res3)
}
@Test
fun `kotlin script evaluation should support functional return types`() {
val scriptEngine = ScriptEngineManager().getEngineByExtension("kts")!!
val script = "{1 + 2}"
val result = scriptEngine.eval(script)
Assert.assertTrue(result is Function0<*>)
Assert.assertEquals(3, (result as Function0<*>).invoke())
}
}
fun assertThrows(exceptionClass: Class<*>, body: () -> Unit) {
try {
body()
Assert.fail("Expecting an exception of type ${exceptionClass.name}")
} catch (e: Throwable) {
if (!exceptionClass.isAssignableFrom(e.javaClass)) {
Assert.fail("Expecting an exception of type ${exceptionClass.name} but got ${e.javaClass.name}")
}
}
}
@@ -33,19 +33,24 @@ class JvmReplEvaluator(
invokeWrapper: InvokeWrapper?
): ReplEvalResult = state.lock.write {
val evalState = state.asState(JvmReplEvaluatorState::class.java)
val history = evalState.history as ReplStageHistoryWithReplace
val compiledScript = (compileResult.data as? KJvmCompiledScript<*>)
?: return ReplEvalResult.Error.CompileTime("Unable to access compiled script: ${compileResult.data}")
val lastSnippetInstance = evalState.history.peek()?.item
val lastSnippetInstance = history.peek()?.item
val historyBeforeSnippet = history.previousItems(compileResult.lineId)
val currentConfiguration = ScriptEvaluationConfiguration(baseScriptEvaluationConfiguration) {
if (evalState.history.isNotEmpty()) {
previousSnippets.put(evalState.history.map { it.item })
if (historyBeforeSnippet.any()) {
previousSnippets.put(historyBeforeSnippet.toList())
}
if (lastSnippetInstance != null) {
jvm {
baseClassLoader(lastSnippetInstance::class.java.classLoader)
}
}
if (scriptArgs != null) {
constructorArgs(*scriptArgs.scriptArgs)
}
}
val res = runBlocking { scriptEvaluator(compiledScript, currentConfiguration) }
@@ -53,7 +58,7 @@ class JvmReplEvaluator(
when (res) {
is ResultWithDiagnostics.Success -> when (val retVal = res.value.returnValue) {
is ResultValue.Value -> {
evalState.history.push(compileResult.lineId, retVal.scriptInstance)
history.replaceOrPush(compileResult.lineId, retVal.scriptInstance)
// TODO: the latter check is temporary while the result is used to return the instance too
if (retVal.type.isNotBlank())
ReplEvalResult.ValueResult(retVal.name, retVal.value, retVal.type)
@@ -61,12 +66,12 @@ class JvmReplEvaluator(
ReplEvalResult.UnitResult()
}
is ResultValue.UnitValue -> {
evalState.history.push(compileResult.lineId, retVal.scriptInstance)
history.replaceOrPush(compileResult.lineId, retVal.scriptInstance)
ReplEvalResult.UnitResult()
}
else -> throw IllegalStateException("Expecting value with script instance, got $retVal")
}
else -> ReplEvalResult.Error.Runtime(res.reports.joinToString("\n") { it.message })
else -> ReplEvalResult.Error.Runtime(res.reports.joinToString("\n") { it.message + (it.exception?.let { e -> ": $e" } ?: "") })
}
}
}
@@ -75,10 +80,29 @@ open class JvmReplEvaluatorState(
scriptEvaluationConfiguration: ScriptEvaluationConfiguration,
override val lock: ReentrantReadWriteLock = ReentrantReadWriteLock()
) : IReplStageState<Any> {
override val history: IReplStageHistory<Any> = BasicReplStageHistory(lock)
override val history: IReplStageHistory<Any> = ReplStageHistoryWithReplace(lock)
override val currentGeneration: Int get() = (history as BasicReplStageHistory<*>).currentGeneration.get()
val topClassLoader: ClassLoader = scriptEvaluationConfiguration[ScriptEvaluationConfiguration.jvm.baseClassLoader]!!
}
open class ReplStageHistoryWithReplace<T>(lock: ReentrantReadWriteLock = ReentrantReadWriteLock()) : BasicReplStageHistory<T>(lock) {
fun replace(id: ILineId, item: T): Boolean = lock.write {
for (idx in indices) {
if (get(idx).id == id) {
set(idx, ReplHistoryRecord(id, item))
return true
}
}
return false
}
fun replaceOrPush(id: ILineId, item: T) {
if (!replace(id, item)) {
tryResetTo(id)
push(id, item)
}
}
fun previousItems(id: ILineId): Sequence<T> = asSequence().takeWhile { it.id.no < id.no }.map { it.item }
}
@@ -160,6 +160,7 @@ class SubpluginsIT : BaseGradleIT() {
assertCompiledKotlinSources(project.relativize(bobGreet, aliceGreet, worldGreet, greetScriptTemplateKt))
}
} else {
assertFailed()
val usedGradleVersion =
GradleVersion.version(
System.getProperty("kotlin.gradle.version.for.tests")
+4
View File
@@ -185,6 +185,8 @@ include ":kotlin-build-common",
":kotlin-scripting-compiler-embeddable",
":kotlin-scripting-compiler-impl",
":kotlin-scripting-compiler-impl-embeddable",
":kotlin-scripting-jsr223",
":kotlin-scripting-jsr223-embeddable",
":kotlin-scripting-idea",
":kotlin-main-kts",
":kotlin-main-kts-test",
@@ -357,6 +359,8 @@ project(':kotlin-scripting-common').projectDir = "$rootDir/libraries/scripting/c
project(':kotlin-scripting-jvm').projectDir = "$rootDir/libraries/scripting/jvm" as File
project(':kotlin-scripting-jvm-host').projectDir = "$rootDir/libraries/scripting/jvm-host" as File
project(':kotlin-scripting-jvm-host-embeddable').projectDir = "$rootDir/libraries/scripting/jvm-host-embeddable" as File
project(':kotlin-scripting-jsr223').projectDir = "$rootDir/libraries/scripting/jsr223" as File
project(':kotlin-scripting-jsr223-embeddable').projectDir = "$rootDir/libraries/scripting/jsr223-embeddable" as File
project(':kotlin-scripting-intellij').projectDir = "$rootDir/libraries/scripting/intellij" as File
project(':kotlin-scripting-compiler').projectDir = "$rootDir/plugins/scripting/scripting-compiler" as File
project(':kotlin-scripting-compiler-embeddable').projectDir = "$rootDir/plugins/scripting/scripting-compiler-embeddable" as File