Implement Invocable on base evaluator and locally-evaluating JSR223 sample engines

fixes #KT-14707
This commit is contained in:
Ilya Chernikov
2016-12-01 12:30:30 +01:00
parent fbd4c6eb61
commit c82e91eafe
9 changed files with 166 additions and 46 deletions
@@ -19,6 +19,7 @@ package org.jetbrains.kotlin.script.jsr223
import org.jetbrains.kotlin.config.KotlinCompilerVersion
import org.junit.Assert
import org.junit.Test
import javax.script.Invocable
import javax.script.ScriptEngine
import javax.script.ScriptEngineManager
import javax.script.SimpleBindings
@@ -65,4 +66,41 @@ class KotlinJsr223ScriptEngineIT {
val res2 = engine.eval("x + 2")
Assert.assertEquals(5, res2)
}
@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)
assertThrows(NoSuchMethodException::class.java) {
invocator!!.invokeMethod(res1, "fn", 3)
}
val res3 = invocator!!.invokeMethod(res1, "fn1", 3)
Assert.assertEquals(6, res3)
}
}
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}")
}
}
}
@@ -19,6 +19,7 @@ package org.jetbrains.kotlin.script.jsr223
import org.jetbrains.kotlin.config.KotlinCompilerVersion
import org.junit.Assert
import org.junit.Test
import javax.script.Invocable
import javax.script.ScriptEngine
import javax.script.ScriptEngineManager
import javax.script.SimpleBindings
@@ -65,4 +66,41 @@ class KotlinJsr223ScriptEngineIT {
val res2 = engine.eval("x + 2")
Assert.assertEquals(5, res2)
}
@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)
assertThrows(NoSuchMethodException::class.java) {
invocator!!.invokeMethod(res1, "fn", 3)
}
val res3 = invocator!!.invokeMethod(res1, "fn1", 3)
Assert.assertEquals(6, res3)
}
}
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}")
}
}
}
@@ -34,6 +34,11 @@
<artifactId>kotlin-stdlib</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.jetbrains.kotlin</groupId>
<artifactId>kotlin-runtime</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.jetbrains.kotlin</groupId>
<artifactId>kotlin-test-junit</artifactId>
@@ -37,7 +37,7 @@ class KotlinJsr223JvmDaemonLocalEvalScriptEngine(
getScriptArgs: (ScriptContext) -> Array<Any?>?,
scriptArgsTypes: Array<Class<*>>?,
compilerOut: OutputStream = System.err
) : KotlinJsr223JvmScriptEngineBase(factory) {
) : KotlinJsr223JvmScriptEngineBase(factory), KotlinJsr223JvmInvocableScriptEngine {
private val daemon by lazy { connectToCompileService(compilerJar) }
@@ -57,6 +57,9 @@ class KotlinJsr223JvmDaemonLocalEvalScriptEngine(
// TODO: bindings passing works only once on the first eval, subsequent setContext/setBindings call have no effect. Consider making it dynamic, but take history into account
val localEvaluator by lazy { GenericReplCompiledEvaluator(templateClasspath, Thread.currentThread().contextClassLoader, getScriptArgs(getContext()), scriptArgsTypes) }
override val replScriptInvoker: ReplScriptInvoker
get() = localEvaluator
override fun eval(codeLine: ReplCodeLine, history: List<ReplCodeLine>): ReplEvalResult {
fun ReplCompileResult.Error.locationString() = if (location == CompilerMessageLocation.NO_LOCATION) ""
@@ -21,9 +21,7 @@ import org.jetbrains.kotlin.cli.common.messages.CompilerMessageLocation
import org.jetbrains.kotlin.cli.common.messages.CompilerMessageSeverity
import org.jetbrains.kotlin.cli.common.messages.MessageCollector
import org.jetbrains.kotlin.cli.common.messages.MessageRenderer
import org.jetbrains.kotlin.cli.common.repl.KotlinJsr223JvmScriptEngineBase
import org.jetbrains.kotlin.cli.common.repl.ReplCodeLine
import org.jetbrains.kotlin.cli.common.repl.ReplEvalResult
import org.jetbrains.kotlin.cli.common.repl.*
import org.jetbrains.kotlin.cli.jvm.config.addJvmClasspathRoots
import org.jetbrains.kotlin.cli.jvm.repl.GenericRepl
import org.jetbrains.kotlin.config.CommonConfigurationKeys
@@ -44,7 +42,7 @@ class KotlinJsr223JvmLocalScriptEngine(
templateClassName: String,
getScriptArgs: (ScriptContext) -> Array<Any?>?,
scriptArgsTypes: Array<Class<*>>?
) : KotlinJsr223JvmScriptEngineBase(factory) {
) : KotlinJsr223JvmScriptEngineBase(factory), KotlinJsr223JvmInvocableScriptEngine {
data class MessageCollectorReport(val severity: CompilerMessageSeverity, val message: String, val location: CompilerMessageLocation)
@@ -105,6 +103,10 @@ class KotlinJsr223JvmLocalScriptEngine(
put(CommonConfigurationKeys.MODULE_NAME, "kotlin-script")
}
override val replScriptInvoker: ReplScriptInvoker
get() = repl.scriptInvoker
override fun eval(codeLine: ReplCodeLine, history: List<ReplCodeLine>): ReplEvalResult {
val evalResult = repl.eval(codeLine, history)
messageCollector.resetAndThrowOnErrors()