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 new file mode 100644 index 00000000000..fcb1383b70f --- /dev/null +++ b/compiler/cli/cli-common/src/org/jetbrains/kotlin/cli/common/repl/KotlinJsr223JvmScriptEngineBase.kt @@ -0,0 +1,55 @@ +/* + * Copyright 2010-2016 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.jetbrains.kotlin.cli.common.repl + +import java.io.Reader +import javax.script.* + +abstract class KotlinJsr223JvmScriptEngineBase(protected val myFactory: ScriptEngineFactory) : AbstractScriptEngine(), ScriptEngine { + + protected var lineCount = 0 + + protected val history = arrayListOf() + + abstract fun eval(codeLine: ReplCodeLine, history: Iterable): ReplEvalResult + + override fun eval(script: String, context: ScriptContext?): Any? { + + lineCount += 1 + // TODO bind to context + val codeLine = ReplCodeLine(lineCount, script) + + val evalResult = eval(codeLine, history) + + val ret = when (evalResult) { + is ReplEvalResult.ValueResult -> evalResult.value + is ReplEvalResult.UnitResult -> null + is ReplEvalResult.Error -> throw ScriptException(evalResult.message) + is ReplEvalResult.Incomplete -> throw ScriptException("error: incomplete code") + is ReplEvalResult.HistoryMismatch -> throw ScriptException("Repl history mismatch at line: ${evalResult.lineNo}") + } + history.add(codeLine) + // TODO update context + return ret + } + + override fun eval(script: Reader, context: ScriptContext?): Any? = eval(script.readText(), context) + + override fun createBindings(): Bindings = SimpleBindings() + + override fun getFactory(): ScriptEngineFactory = myFactory +} diff --git a/idea/idea-repl/src/org/jetbrains/kotlin/jsr223/KotlinJvmJsr223StandardScriptEngineFactory4Idea.kt b/compiler/cli/cli-common/src/org/jetbrains/kotlin/cli/common/repl/KotlinJsr223JvmScriptEngineFactoryBase.kt similarity index 65% rename from idea/idea-repl/src/org/jetbrains/kotlin/jsr223/KotlinJvmJsr223StandardScriptEngineFactory4Idea.kt rename to compiler/cli/cli-common/src/org/jetbrains/kotlin/cli/common/repl/KotlinJsr223JvmScriptEngineFactoryBase.kt index 6fbb7badf56..9e0f7bfd0b5 100644 --- a/idea/idea-repl/src/org/jetbrains/kotlin/jsr223/KotlinJvmJsr223StandardScriptEngineFactory4Idea.kt +++ b/compiler/cli/cli-common/src/org/jetbrains/kotlin/cli/common/repl/KotlinJsr223JvmScriptEngineFactoryBase.kt @@ -14,18 +14,13 @@ * limitations under the License. */ -package org.jetbrains.kotlin.jsr223 +package org.jetbrains.kotlin.cli.common.repl -import com.intellij.openapi.util.Disposer import org.jetbrains.kotlin.cli.common.KotlinVersion -import org.jetbrains.kotlin.utils.PathUtil -import javax.script.Bindings -import javax.script.ScriptContext import javax.script.ScriptEngine import javax.script.ScriptEngineFactory -@Suppress("unused") // used in javax.script.ScriptEngineFactory META-INF file -class KotlinJvmJsr223StandardScriptEngineFactory4Idea : ScriptEngineFactory { +abstract class KotlinJsr223JvmScriptEngineFactoryBase : ScriptEngineFactory { override fun getLanguageName(): String = "kotlin" override fun getLanguageVersion(): String = KotlinVersion.VERSION @@ -35,20 +30,6 @@ class KotlinJvmJsr223StandardScriptEngineFactory4Idea : ScriptEngineFactory { override fun getMimeTypes(): List = listOf("text/x-kotlin") override fun getNames(): List = listOf("kotlin") - override fun getScriptEngine(): ScriptEngine = - KotlinJvmJsr223ScriptEngine4Idea( - Disposer.newDisposable(), - this, - listOf(PathUtil.getKotlinPathsForIdeaPlugin().runtimePath), - "kotlin.script.ScriptTemplateWithArgsAndBindings", - { ctx -> - val bindings = ctx.getBindings(ScriptContext.ENGINE_SCOPE) - arrayOf( - (bindings[ScriptEngine.ARGV] as? Array<*>) ?: emptyArray(), - bindings) }, - arrayOf(Array::class.java, java.util.Map::class.java) - ) - override fun getOutputStatement(toDisplay: String?): String = "print(\"$toDisplay\")" override fun getMethodCallSyntax(obj: String, m: String, vararg args: String): String = "$obj.$m(${args.joinToString()})" diff --git a/compiler/cli/src/org/jetbrains/kotlin/cli/jvm/repl/GenericRepl.kt b/compiler/cli/src/org/jetbrains/kotlin/cli/jvm/repl/GenericRepl.kt index ee4b51fe545..bfd8c480e52 100644 --- a/compiler/cli/src/org/jetbrains/kotlin/cli/jvm/repl/GenericRepl.kt +++ b/compiler/cli/src/org/jetbrains/kotlin/cli/jvm/repl/GenericRepl.kt @@ -181,10 +181,12 @@ open class GenericRepl( scriptDefinition: KotlinScriptDefinition, compilerConfiguration: CompilerConfiguration, messageCollector: MessageCollector, - baseClassloader: ClassLoader? + baseClassloader: ClassLoader?, + scriptArgs: Array? = null, + scriptArgsTypes: Array>? = null ) : ReplEvaluator, GenericReplCompiler(disposable, scriptDefinition, compilerConfiguration, messageCollector) { - private val compiledEvaluator = GenericReplCompiledEvaluator(compilerConfiguration.jvmClasspathRoots, baseClassloader) + private val compiledEvaluator = GenericReplCompiledEvaluator(compilerConfiguration.jvmClasspathRoots, baseClassloader, scriptArgs, scriptArgsTypes) override fun eval(codeLine: ReplCodeLine, history: Iterable): ReplEvalResult = synchronized(this) { return compileAndEval(this, compiledEvaluator, codeLine, history) diff --git a/idea/idea-repl/src/META-INF/services/javax.script.ScriptEngineFactory b/idea/idea-repl/src/META-INF/services/javax.script.ScriptEngineFactory index 8765a13624f..26e0288ad32 100644 --- a/idea/idea-repl/src/META-INF/services/javax.script.ScriptEngineFactory +++ b/idea/idea-repl/src/META-INF/services/javax.script.ScriptEngineFactory @@ -1 +1 @@ -org.jetbrains.kotlin.jsr223.KotlinJvmJsr223StandardScriptEngineFactory4Idea +org.jetbrains.kotlin.jsr223.KotlinJsr223StandardScriptEngineFactory4Idea diff --git a/idea/idea-repl/src/org/jetbrains/kotlin/jsr223/KotlinJsr223JvmScriptEngine4Idea.kt b/idea/idea-repl/src/org/jetbrains/kotlin/jsr223/KotlinJsr223JvmScriptEngine4Idea.kt new file mode 100644 index 00000000000..c620e9d6b3c --- /dev/null +++ b/idea/idea-repl/src/org/jetbrains/kotlin/jsr223/KotlinJsr223JvmScriptEngine4Idea.kt @@ -0,0 +1,85 @@ +/* + * Copyright 2010-2016 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.jetbrains.kotlin.jsr223 + +import com.intellij.openapi.Disposable +import org.jetbrains.kotlin.cli.common.messages.CompilerMessageLocation +import org.jetbrains.kotlin.cli.common.repl.* +import org.jetbrains.kotlin.daemon.client.DaemonReportMessage +import org.jetbrains.kotlin.daemon.client.DaemonReportingTargets +import org.jetbrains.kotlin.daemon.client.KotlinCompilerClient +import org.jetbrains.kotlin.daemon.client.KotlinRemoteReplCompiler +import org.jetbrains.kotlin.daemon.common.* +import org.jetbrains.kotlin.utils.PathUtil +import java.io.File +import javax.script.ScriptContext +import javax.script.ScriptEngineFactory +import javax.script.ScriptException + +class KotlinJsr223JvmScriptEngine4Idea( + disposable: Disposable, + factory: ScriptEngineFactory, + templateClasspath: List, + templateClassName: String, + getScriptArgs: (ScriptContext) -> Array?, + scriptArgsTypes: Array>? +) : KotlinJsr223JvmScriptEngineBase(factory) { + + private val daemon by lazy { + val path = PathUtil.getKotlinPathsForIdeaPlugin().compilerPath + assert(path.exists()) + val compilerId = CompilerId.makeCompilerId(path) + val daemonOptions = configureDaemonOptions() + val daemonJVMOptions = DaemonJVMOptions() + + val daemonReportMessages = arrayListOf() + + KotlinCompilerClient.connectToCompileService(compilerId, daemonJVMOptions, daemonOptions, DaemonReportingTargets(null, daemonReportMessages), true, true) + ?: throw ScriptException("Unable to connect to repl server:" + daemonReportMessages.joinToString("\n ", prefix = "\n ") { "${it.category.name} ${it.message}" }) + } + + private val replCompiler by lazy { + daemon.let { + KotlinRemoteReplCompiler(disposable, + it, + makeAutodeletingFlagFile("idea-jsr223-repl-session"), + CompileService.TargetPlatform.JVM, + templateClasspath, + templateClassName, + System.out) + } + } + + // 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 fun eval(codeLine: ReplCodeLine, history: Iterable): ReplEvalResult { + + fun ReplCompileResult.Error.locationString() = if (location == CompilerMessageLocation.NO_LOCATION) "" + else " at ${location.line}:${location.column}:" + + val compileResult = replCompiler.compile(codeLine, history) + val compiled = when (compileResult) { + is ReplCompileResult.Error -> throw ScriptException("Error${compileResult.locationString()}: ${compileResult.message}") + is ReplCompileResult.Incomplete -> throw ScriptException("error: incomplete code") + is ReplCompileResult.HistoryMismatch -> throw ScriptException("Repl history mismatch at line: ${compileResult.lineNo}") + is ReplCompileResult.CompiledClasses -> compileResult + } + + return localEvaluator.eval(codeLine, history, compiled.classes, compiled.hasResult, compiled.newClasspath) + } +} diff --git a/idea/idea-repl/src/org/jetbrains/kotlin/jsr223/KotlinJsr223StandardScriptEngineFactory4Idea.kt b/idea/idea-repl/src/org/jetbrains/kotlin/jsr223/KotlinJsr223StandardScriptEngineFactory4Idea.kt new file mode 100644 index 00000000000..5370e4b78ee --- /dev/null +++ b/idea/idea-repl/src/org/jetbrains/kotlin/jsr223/KotlinJsr223StandardScriptEngineFactory4Idea.kt @@ -0,0 +1,41 @@ +/* + * Copyright 2010-2016 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.jetbrains.kotlin.jsr223 + +import com.intellij.openapi.util.Disposer +import org.jetbrains.kotlin.cli.common.repl.KotlinJsr223JvmScriptEngineFactoryBase +import org.jetbrains.kotlin.utils.PathUtil +import javax.script.ScriptContext +import javax.script.ScriptEngine + +@Suppress("unused") // used in javax.script.ScriptEngineFactory META-INF file +class KotlinJsr223StandardScriptEngineFactory4Idea : KotlinJsr223JvmScriptEngineFactoryBase() { + + override fun getScriptEngine(): ScriptEngine = + KotlinJsr223JvmScriptEngine4Idea( + Disposer.newDisposable(), + this, + listOf(PathUtil.getKotlinPathsForIdeaPlugin().runtimePath), + "kotlin.script.ScriptTemplateWithArgsAndBindings", + { ctx -> + val bindings = ctx.getBindings(ScriptContext.ENGINE_SCOPE) + arrayOf( + (bindings[ScriptEngine.ARGV] as? Array<*>) ?: emptyArray(), + bindings) }, + arrayOf(Array::class.java, java.util.Map::class.java) + ) +} diff --git a/libraries/examples/kotlin-jsr223-daemon-local-eval-example/pom.xml b/libraries/examples/kotlin-jsr223-daemon-local-eval-example/pom.xml new file mode 100644 index 00000000000..32fc14bd9e0 --- /dev/null +++ b/libraries/examples/kotlin-jsr223-daemon-local-eval-example/pom.xml @@ -0,0 +1,104 @@ + + + + 4.0.0 + + 1.4.1 + 3.0.4 + + + + org.jetbrains.kotlin + kotlin-project + 1.1-SNAPSHOT + ../../pom.xml + + + kotlin-jsr223-daemon-local-eval-example + jar + + Sample Kotlin JSR 223 scripting jar with daemon (out-of-process) compilation and local (in-process) evaluation + + + + org.jetbrains.kotlin + kotlin-runtime + ${project.version} + + + org.jetbrains.kotlin + kotlin-compiler + ${project.version} + + + org.jetbrains.kotlin + kotlin-script-util + ${project.version} + + + + + ${project.basedir}/src/test/kotlin + + + + src/main/resources + true + + + + + + org.apache.maven.plugins + maven-dependency-plugin + 2.3 + + + + properties + + + + + + kotlin-maven-plugin + org.jetbrains.kotlin + ${project.version} + + + + compile + compile + compile + + + + test-compile + test-compile + test-compile + + + + + maven-failsafe-plugin + 2.6 + + + ${org.jetbrains.kotlin:kotlin-compiler:jar} + ${org.jetbrains.kotlin:kotlin-runtime:jar} + + + + + + integration-test + verify + + + + + + + diff --git a/libraries/examples/kotlin-jsr223-daemon-local-eval-example/src/main/resources/META-INF/services/javax.script.ScriptEngineFactory b/libraries/examples/kotlin-jsr223-daemon-local-eval-example/src/main/resources/META-INF/services/javax.script.ScriptEngineFactory new file mode 100644 index 00000000000..1fc4404b1cd --- /dev/null +++ b/libraries/examples/kotlin-jsr223-daemon-local-eval-example/src/main/resources/META-INF/services/javax.script.ScriptEngineFactory @@ -0,0 +1 @@ +org.jetbrains.kotlin.script.jsr223.KotlinJsr223JvmDaemonLocalEvalScriptEngineFactory diff --git a/libraries/tools/kotlin-script-util/src/test/kotlin/org/jetbrains/kotlin/script/jsr223/KotlinJsr223ScriptEngineTest.kt b/libraries/examples/kotlin-jsr223-daemon-local-eval-example/src/test/kotlin/org/jetbrains/kotlin/script/jsr223/KotlinJsr223ScriptEngineIT.kt similarity index 76% rename from libraries/tools/kotlin-script-util/src/test/kotlin/org/jetbrains/kotlin/script/jsr223/KotlinJsr223ScriptEngineTest.kt rename to libraries/examples/kotlin-jsr223-daemon-local-eval-example/src/test/kotlin/org/jetbrains/kotlin/script/jsr223/KotlinJsr223ScriptEngineIT.kt index 1a3e542a71a..100b36122d2 100644 --- a/libraries/tools/kotlin-script-util/src/test/kotlin/org/jetbrains/kotlin/script/jsr223/KotlinJsr223ScriptEngineTest.kt +++ b/libraries/examples/kotlin-jsr223-daemon-local-eval-example/src/test/kotlin/org/jetbrains/kotlin/script/jsr223/KotlinJsr223ScriptEngineIT.kt @@ -16,33 +16,18 @@ package org.jetbrains.kotlin.script.jsr223 -import junit.framework.TestCase import org.jetbrains.kotlin.cli.common.KotlinVersion import org.junit.Assert import org.junit.Test import javax.script.ScriptEngine -import javax.script.ScriptEngineFactory import javax.script.ScriptEngineManager import javax.script.SimpleBindings -class KotlinJsr223ScriptEngineTest : TestCase() { - - private var factory: ScriptEngineFactory? = null - private var engine: ScriptEngine? = null - - override fun setUp() { - super.setUp() - factory = ScriptEngineManager().getEngineByExtension("kts").factory - engine = factory?.scriptEngine - } - - override fun tearDown() { - factory = null - super.tearDown() - } +class KotlinJsr223ScriptEngineIT { @Test fun testEngineFactory() { + val factory = ScriptEngineManager().getEngineByExtension("kts").factory Assert.assertNotNull(factory) factory!!.apply { Assert.assertEquals("kotlin", languageName) @@ -63,17 +48,21 @@ class KotlinJsr223ScriptEngineTest : TestCase() { @Test fun testEngine() { - Assert.assertNotNull(engine as? KotlinJsr232ScriptEngine) + val factory = ScriptEngineManager().getEngineByExtension("kts").factory + Assert.assertNotNull(factory) + val engine = factory!!.scriptEngine + Assert.assertNotNull(engine as? KotlinJsr223JvmDaemonLocalEvalScriptEngine) Assert.assertSame(factory, engine!!.factory) - val bindings = engine!!.createBindings() + val bindings = engine.createBindings() Assert.assertTrue(bindings is SimpleBindings) } @Test fun testSimpleEval() { - val res1 = engine!!.eval("val x = 3") + val engine = ScriptEngineManager().getEngineByExtension("kts")!! + val res1 = engine.eval("val x = 3") Assert.assertNull(res1) - val res2 = engine!!.eval("x + 2") + val res2 = engine.eval("x + 2") Assert.assertEquals(5, res2) } } \ No newline at end of file diff --git a/libraries/examples/kotlin-jsr223-daemon-remote-eval-example/pom.xml b/libraries/examples/kotlin-jsr223-daemon-remote-eval-example/pom.xml new file mode 100644 index 00000000000..6c11742f0a1 --- /dev/null +++ b/libraries/examples/kotlin-jsr223-daemon-remote-eval-example/pom.xml @@ -0,0 +1,104 @@ + + + + 4.0.0 + + 1.4.1 + 3.0.4 + + + + org.jetbrains.kotlin + kotlin-project + 1.1-SNAPSHOT + ../../pom.xml + + + kotlin-jsr223-daemon-remote-eval-example + jar + + Sample Kotlin JSR 223 scripting jar with daemon (out-of-process) compilation and evaluation + + + + org.jetbrains.kotlin + kotlin-runtime + ${project.version} + + + org.jetbrains.kotlin + kotlin-compiler + ${project.version} + + + org.jetbrains.kotlin + kotlin-script-util + ${project.version} + + + + + ${project.basedir}/src/test/kotlin + + + + src/main/resources + true + + + + + + org.apache.maven.plugins + maven-dependency-plugin + 2.3 + + + + properties + + + + + + kotlin-maven-plugin + org.jetbrains.kotlin + ${project.version} + + + + compile + compile + compile + + + + test-compile + test-compile + test-compile + + + + + maven-failsafe-plugin + 2.6 + + + ${org.jetbrains.kotlin:kotlin-compiler:jar} + ${org.jetbrains.kotlin:kotlin-runtime:jar} + + + + + + integration-test + verify + + + + + + + diff --git a/libraries/examples/kotlin-jsr223-daemon-remote-eval-example/src/main/resources/META-INF/services/javax.script.ScriptEngineFactory b/libraries/examples/kotlin-jsr223-daemon-remote-eval-example/src/main/resources/META-INF/services/javax.script.ScriptEngineFactory new file mode 100644 index 00000000000..e41de55be55 --- /dev/null +++ b/libraries/examples/kotlin-jsr223-daemon-remote-eval-example/src/main/resources/META-INF/services/javax.script.ScriptEngineFactory @@ -0,0 +1 @@ +org.jetbrains.kotlin.script.jsr223.KotlinJsr223JvmDaemonRemoteEvalScriptEngineFactory diff --git a/libraries/examples/kotlin-jsr223-daemon-remote-eval-example/src/test/kotlin/org/jetbrains/kotlin/script/jsr223/KotlinJsr223ScriptEngineIT.kt b/libraries/examples/kotlin-jsr223-daemon-remote-eval-example/src/test/kotlin/org/jetbrains/kotlin/script/jsr223/KotlinJsr223ScriptEngineIT.kt new file mode 100644 index 00000000000..4645e6cf413 --- /dev/null +++ b/libraries/examples/kotlin-jsr223-daemon-remote-eval-example/src/test/kotlin/org/jetbrains/kotlin/script/jsr223/KotlinJsr223ScriptEngineIT.kt @@ -0,0 +1,68 @@ +/* + * Copyright 2010-2016 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.jetbrains.kotlin.script.jsr223 + +import org.jetbrains.kotlin.cli.common.KotlinVersion +import org.junit.Assert +import org.junit.Test +import javax.script.ScriptEngine +import javax.script.ScriptEngineManager +import javax.script.SimpleBindings + +class KotlinJsr223ScriptEngineIT { + + @Test + fun testEngineFactory() { + val factory = ScriptEngineManager().getEngineByExtension("kts").factory + Assert.assertNotNull(factory) + factory!!.apply { + Assert.assertEquals("kotlin", languageName) + Assert.assertEquals(KotlinVersion.VERSION, languageVersion) + Assert.assertEquals("kotlin", engineName) + Assert.assertEquals(KotlinVersion.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(KotlinVersion.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? KotlinJsr223JvmDaemonRemoteEvalScriptEngine) + 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) + } +} \ No newline at end of file diff --git a/libraries/examples/kotlin-jsr223-local-example/pom.xml b/libraries/examples/kotlin-jsr223-local-example/pom.xml new file mode 100644 index 00000000000..241eda41e85 --- /dev/null +++ b/libraries/examples/kotlin-jsr223-local-example/pom.xml @@ -0,0 +1,104 @@ + + + + 4.0.0 + + 1.4.1 + 3.0.4 + + + + org.jetbrains.kotlin + kotlin-project + 1.1-SNAPSHOT + ../../pom.xml + + + kotlin-jsr223-local-example + jar + + Sample Kotlin JSR 223 scripting jar with local (in-process) compilation and evaluation + + + + org.jetbrains.kotlin + kotlin-runtime + ${project.version} + + + org.jetbrains.kotlin + kotlin-compiler + ${project.version} + + + org.jetbrains.kotlin + kotlin-script-util + ${project.version} + + + + + ${project.basedir}/src/test/kotlin + + + + src/main/resources + true + + + + + + org.apache.maven.plugins + maven-dependency-plugin + 2.3 + + + + properties + + + + + + kotlin-maven-plugin + org.jetbrains.kotlin + ${project.version} + + + + compile + compile + compile + + + + test-compile + test-compile + test-compile + + + + + maven-failsafe-plugin + 2.6 + + + ${org.jetbrains.kotlin:kotlin-compiler:jar} + ${org.jetbrains.kotlin:kotlin-runtime:jar} + + + + + + integration-test + verify + + + + + + + diff --git a/libraries/examples/kotlin-jsr223-local-example/src/main/resources/META-INF/services/javax.script.ScriptEngineFactory b/libraries/examples/kotlin-jsr223-local-example/src/main/resources/META-INF/services/javax.script.ScriptEngineFactory new file mode 100644 index 00000000000..8a5ab31ff5e --- /dev/null +++ b/libraries/examples/kotlin-jsr223-local-example/src/main/resources/META-INF/services/javax.script.ScriptEngineFactory @@ -0,0 +1 @@ +org.jetbrains.kotlin.script.jsr223.KotlinJsr232JvmLocalScriptEngineFactory 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 new file mode 100644 index 00000000000..b79d0d094bf --- /dev/null +++ b/libraries/examples/kotlin-jsr223-local-example/src/test/kotlin/org/jetbrains/kotlin/script/jsr223/KotlinJsr223ScriptEngineIT.kt @@ -0,0 +1,68 @@ +/* + * Copyright 2010-2016 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.jetbrains.kotlin.script.jsr223 + +import org.jetbrains.kotlin.cli.common.KotlinVersion +import org.junit.Assert +import org.junit.Test +import javax.script.ScriptEngine +import javax.script.ScriptEngineManager +import javax.script.SimpleBindings + +class KotlinJsr223ScriptEngineIT { + + @Test + fun testEngineFactory() { + val factory = ScriptEngineManager().getEngineByExtension("kts").factory + Assert.assertNotNull(factory) + factory!!.apply { + Assert.assertEquals("kotlin", languageName) + Assert.assertEquals(KotlinVersion.VERSION, languageVersion) + Assert.assertEquals("kotlin", engineName) + Assert.assertEquals(KotlinVersion.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(KotlinVersion.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? KotlinJsr232JvmLocalScriptEngine) + 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) + } +} \ No newline at end of file diff --git a/libraries/pom.xml b/libraries/pom.xml index d8588a8b22c..340b02a982e 100644 --- a/libraries/pom.xml +++ b/libraries/pom.xml @@ -74,6 +74,7 @@ tools/kotlin-compiler tools/kotlin-compiler-embeddable + tools/kotlin-daemon-client tools/kotlin-build-common tools/kotlin-build-common-test tools/kotlin-maven-plugin @@ -112,6 +113,9 @@ examples/kotlin-gradle-subplugin-example examples/browser-example examples/browser-example-with-library + examples/kotlin-jsr223-local-example + examples/kotlin-jsr223-daemon-local-eval-example + examples/kotlin-jsr223-daemon-remote-eval-example diff --git a/libraries/tools/kotlin-script-util/pom.xml b/libraries/tools/kotlin-script-util/pom.xml index 9a56fd9caf4..e3bf2acd0d3 100644 --- a/libraries/tools/kotlin-script-util/pom.xml +++ b/libraries/tools/kotlin-script-util/pom.xml @@ -39,6 +39,11 @@ kotlin-compiler ${project.version} + + org.jetbrains.kotlin + kotlin-daemon-client + ${project.version} + com.jcabi jcabi-aether @@ -97,7 +102,8 @@ 2.6 - ${org.jetbrains.kotlin:kotlin-stdlib:jar} + ${env.JDK_17}/bin/java + ${org.jetbrains.kotlin:kotlin-stdlib:jar} diff --git a/libraries/tools/kotlin-script-util/src/main/kotlin/org/jetbrains/kotlin/script/jsr223/KotlinJsr223JvmDaemonScriptEngines.kt b/libraries/tools/kotlin-script-util/src/main/kotlin/org/jetbrains/kotlin/script/jsr223/KotlinJsr223JvmDaemonScriptEngines.kt new file mode 100644 index 00000000000..21500bee019 --- /dev/null +++ b/libraries/tools/kotlin-script-util/src/main/kotlin/org/jetbrains/kotlin/script/jsr223/KotlinJsr223JvmDaemonScriptEngines.kt @@ -0,0 +1,125 @@ +/* + * Copyright 2010-2016 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.jetbrains.kotlin.script.jsr223 + +import com.intellij.openapi.Disposable +import org.jetbrains.kotlin.cli.common.messages.CompilerMessageLocation +import org.jetbrains.kotlin.cli.common.repl.* +import org.jetbrains.kotlin.daemon.client.* +import org.jetbrains.kotlin.daemon.common.* +import java.io.File +import java.io.InputStream +import java.io.OutputStream +import javax.script.ScriptContext +import javax.script.ScriptEngineFactory +import javax.script.ScriptException + +class KotlinJsr223JvmDaemonLocalEvalScriptEngine( + disposable: Disposable, + factory: ScriptEngineFactory, + compilerJar: File, + templateClasspath: List, + templateClassName: String, + getScriptArgs: (ScriptContext) -> Array?, + scriptArgsTypes: Array>?, + compilerOut: OutputStream = System.err +) : KotlinJsr223JvmScriptEngineBase(factory) { + + private val daemon by lazy { connectToCompileService(compilerJar) } + + private val replCompiler by lazy { + daemon.let { + KotlinRemoteReplCompiler( + disposable, + it, + makeAutodeletingFlagFile("jsr223-repl-session"), + CompileService.TargetPlatform.JVM, + templateClasspath, + templateClassName, + compilerOut) + } + } + + // 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 fun eval(codeLine: ReplCodeLine, history: Iterable): ReplEvalResult { + + fun ReplCompileResult.Error.locationString() = if (location == CompilerMessageLocation.NO_LOCATION) "" + else " at ${location.line}:${location.column}:" + + val compileResult = replCompiler.compile(codeLine, history) + val compiled = when (compileResult) { + is ReplCompileResult.Error -> throw ScriptException("Error${compileResult.locationString()}: ${compileResult.message}") + is ReplCompileResult.Incomplete -> throw ScriptException("error: incomplete code") + is ReplCompileResult.HistoryMismatch -> throw ScriptException("Repl history mismatch at line: ${compileResult.lineNo}") + is ReplCompileResult.CompiledClasses -> compileResult + } + + return localEvaluator.eval(codeLine, history, compiled.classes, compiled.hasResult, compiled.newClasspath) + } +} + + +class KotlinJsr223JvmDaemonRemoteEvalScriptEngine( + disposable: Disposable, + factory: ScriptEngineFactory, + compilerJar: File, + templateClasspath: List, + templateClassName: String, + getScriptArgs: (ScriptContext) -> Array?, + scriptArgsTypes: Array>?, + compilerOutputStream: OutputStream = System.err, + evallOutputStream: OutputStream = System.out, + evalErrrorStream: OutputStream = System.err, + evalInputStream: InputStream = System.`in` +) : KotlinJsr223JvmScriptEngineBase(factory) { + + private val daemon by lazy { connectToCompileService(compilerJar) } + + // 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 + private val repl by lazy { + daemon.let { + KotlinRemoteReplEvaluator( + disposable, + it, + makeAutodeletingFlagFile("jsr223-repl-session"), + CompileService.TargetPlatform.JVM, + templateClasspath, + templateClassName, + getScriptArgs(getContext()), + scriptArgsTypes, + compilerOutputStream, + evallOutputStream, + evalErrrorStream, + evalInputStream) + } + } + + override fun eval(codeLine: ReplCodeLine, history: Iterable): ReplEvalResult = repl.eval(codeLine, history) +} + +private fun connectToCompileService(compilerJar: File): CompileService { + val compilerId = CompilerId.makeCompilerId(compilerJar) + val daemonOptions = configureDaemonOptions() + val daemonJVMOptions = DaemonJVMOptions() + + val daemonReportMessages = arrayListOf() + + return KotlinCompilerClient.connectToCompileService(compilerId, daemonJVMOptions, daemonOptions, DaemonReportingTargets(null, daemonReportMessages), true, true) + ?: throw ScriptException("Unable to connect to repl server:" + daemonReportMessages.joinToString("\n ", prefix = "\n ") { "${it.category.name} ${it.message}" }) +} diff --git a/libraries/tools/kotlin-script-util/src/main/kotlin/org/jetbrains/kotlin/script/jsr223/KotlinJsr232ScriptEngine.kt b/libraries/tools/kotlin-script-util/src/main/kotlin/org/jetbrains/kotlin/script/jsr223/KotlinJsr232JvmLocalScriptEngine.kt similarity index 59% rename from libraries/tools/kotlin-script-util/src/main/kotlin/org/jetbrains/kotlin/script/jsr223/KotlinJsr232ScriptEngine.kt rename to libraries/tools/kotlin-script-util/src/main/kotlin/org/jetbrains/kotlin/script/jsr223/KotlinJsr232JvmLocalScriptEngine.kt index cf801d89307..2aa96bb006a 100644 --- a/libraries/tools/kotlin-script-util/src/main/kotlin/org/jetbrains/kotlin/script/jsr223/KotlinJsr232ScriptEngine.kt +++ b/libraries/tools/kotlin-script-util/src/main/kotlin/org/jetbrains/kotlin/script/jsr223/KotlinJsr232JvmLocalScriptEngine.kt @@ -21,21 +21,30 @@ 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.jvm.config.addJvmClasspathRoots import org.jetbrains.kotlin.cli.jvm.repl.GenericRepl +import org.jetbrains.kotlin.config.CommonConfigurationKeys import org.jetbrains.kotlin.config.CompilerConfiguration import org.jetbrains.kotlin.script.KotlinScriptDefinition -import java.io.Reader -import javax.script.* +import org.jetbrains.kotlin.script.KotlinScriptDefinitionFromAnnotatedTemplate +import org.jetbrains.kotlin.utils.PathUtil +import java.io.File +import java.net.URLClassLoader +import javax.script.ScriptContext +import javax.script.ScriptEngineFactory +import javax.script.ScriptException -class KotlinJsr232ScriptEngine( +class KotlinJsr232JvmLocalScriptEngine( disposable: Disposable, - private val factory: ScriptEngineFactory, - private val scriptDefinition: KotlinScriptDefinition, - private val compilerConfiguration: CompilerConfiguration, - baseClassLoader: ClassLoader -) : AbstractScriptEngine(), ScriptEngine { + factory: ScriptEngineFactory, + val templateClasspath: List, + templateClassName: String, + getScriptArgs: (ScriptContext) -> Array?, + scriptArgsTypes: Array>? +) : KotlinJsr223JvmScriptEngineBase(factory) { data class MessageCollectorReport(val severity: CompilerMessageSeverity, val message: String, val location: CompilerMessageLocation) @@ -74,33 +83,32 @@ class KotlinJsr232ScriptEngine( } } - private val repl = object : GenericRepl(disposable, scriptDefinition, compilerConfiguration, messageCollector, baseClassLoader) {} - - private var lineCount = 0 - - private val history = arrayListOf() - - override fun eval(script: String, context: ScriptContext?): Any? { - lineCount += 1 - // TODO bind to context - val codeLine = ReplCodeLine(lineCount, script) - val evalResult = repl.eval(codeLine, history) - messageCollector.resetAndThrowOnErrors() - val ret = when (evalResult) { - is ReplEvalResult.ValueResult -> evalResult.value - is ReplEvalResult.UnitResult -> null - is ReplEvalResult.Error -> throw ScriptException(evalResult.message) - is ReplEvalResult.Incomplete -> throw ScriptException("error: incomplete code") - is ReplEvalResult.HistoryMismatch -> throw ScriptException("Repl history mismatch at line: ${evalResult.lineNo}") - } - history.add(codeLine) - // TODO update context - return ret + private val repl by lazy { + GenericRepl( + disposable, + makeScriptDefinition(templateClasspath, templateClassName), + makeCompilerConfiguration(), + messageCollector, + Thread.currentThread().contextClassLoader, + getScriptArgs(getContext()), + scriptArgsTypes) } - override fun eval(script: Reader, context: ScriptContext?): Any? = eval(script.readText(), context) + 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) + return KotlinScriptDefinitionFromAnnotatedTemplate(cls.kotlin, null, null, emptyMap()) + } - override fun createBindings(): Bindings = SimpleBindings() + private fun makeCompilerConfiguration() = CompilerConfiguration().apply { + addJvmClasspathRoots(PathUtil.getJdkClassesRoots()) + addJvmClasspathRoots(templateClasspath) + put(CommonConfigurationKeys.MODULE_NAME, "kotlin-script") + } - override fun getFactory(): ScriptEngineFactory = factory + override fun eval(codeLine: ReplCodeLine, history: Iterable): ReplEvalResult { + val evalResult = repl.eval(codeLine, history) + messageCollector.resetAndThrowOnErrors() + return evalResult + } } diff --git a/libraries/tools/kotlin-script-util/src/main/kotlin/org/jetbrains/kotlin/script/jsr223/KotlinJsr232ScriptEngineFactoryExamples.kt b/libraries/tools/kotlin-script-util/src/main/kotlin/org/jetbrains/kotlin/script/jsr223/KotlinJsr232ScriptEngineFactoryExamples.kt new file mode 100644 index 00000000000..0db2122bb21 --- /dev/null +++ b/libraries/tools/kotlin-script-util/src/main/kotlin/org/jetbrains/kotlin/script/jsr223/KotlinJsr232ScriptEngineFactoryExamples.kt @@ -0,0 +1,98 @@ +/* + * Copyright 2010-2016 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +@file:Suppress("unused") // could be used externally in javax.script.ScriptEngineFactory META-INF file + +package org.jetbrains.kotlin.script.jsr223 + +import com.intellij.openapi.util.Disposer +import org.jetbrains.kotlin.cli.common.repl.KotlinJsr223JvmScriptEngineFactoryBase +import org.jetbrains.kotlin.utils.PathUtil.* +import java.io.File +import java.io.FileNotFoundException +import javax.script.ScriptContext +import javax.script.ScriptEngine +import kotlin.script.StandardScriptTemplate + +class KotlinJsr232JvmLocalScriptEngineFactory : KotlinJsr223JvmScriptEngineFactoryBase() { + + override fun getScriptEngine(): ScriptEngine = + KotlinJsr232JvmLocalScriptEngine( + Disposer.newDisposable(), + this, + listOf(kotlinRuntimeJar), + "kotlin.script.ScriptTemplateWithArgsAndBindings", + ::makeArgumentsForTemplateWithArgsAndBindings, + arrayOf(Array::class.java, java.util.Map::class.java) + ) +} + +class KotlinJsr223JvmDaemonLocalEvalScriptEngineFactory : KotlinJsr223JvmScriptEngineFactoryBase() { + + override fun getScriptEngine(): ScriptEngine = + KotlinJsr223JvmDaemonLocalEvalScriptEngine( + Disposer.newDisposable(), + this, + kotlinCompilerJar, + listOf(kotlinRuntimeJar), + "kotlin.script.ScriptTemplateWithArgsAndBindings", + ::makeArgumentsForTemplateWithArgsAndBindings, + arrayOf(Array::class.java, java.util.Map::class.java) + ) +} + +class KotlinJsr223JvmDaemonRemoteEvalScriptEngineFactory : KotlinJsr223JvmScriptEngineFactoryBase() { + + override fun getScriptEngine(): ScriptEngine = + KotlinJsr223JvmDaemonRemoteEvalScriptEngine( + Disposer.newDisposable(), + this, + kotlinCompilerJar, + listOf(kotlinRuntimeJar), + "kotlin.script.ScriptTemplateWithArgsAndBindings", + ::makeSerializableArgumentsForTemplateWithArgsAndBindings, + arrayOf(Array::class.java, java.util.Map::class.java) + ) +} + +private fun makeArgumentsForTemplateWithArgsAndBindings(ctx: ScriptContext): Array { + val bindings = ctx.getBindings(ScriptContext.ENGINE_SCOPE) + return arrayOf( + (bindings[ScriptEngine.ARGV] as? Array<*>) ?: emptyArray(), + bindings) +} + +private fun makeSerializableArgumentsForTemplateWithArgsAndBindings(ctx: ScriptContext): Array { + val bindings = ctx.getBindings(ScriptContext.ENGINE_SCOPE) + val serializableBindings = linkedMapOf() + // TODO: consider deeper analysis and copying to serializable data if possible + serializableBindings.putAll(bindings) + return arrayOf( + (bindings[ScriptEngine.ARGV] as? Array<*>) ?: emptyArray(), + serializableBindings) +} + +private fun File.existsOrNull(): File? = existsAndCheckOrNull { true } +private inline fun File.existsAndCheckOrNull(check: (File.() -> Boolean)): File? = if (exists() && check()) this else null + +private val kotlinCompilerJar = System.getProperty("KOTLIN_COMPILER_JAR")?.let(::File)?.existsOrNull() + ?: getPathUtilJar().existsAndCheckOrNull { name == KOTLIN_COMPILER_JAR } + ?: throw FileNotFoundException("Cannot find kotlin compiler jar, set KOTLIN_COMPILER_JAR property to proper location") + +private val kotlinRuntimeJar = System.getProperty("KOTLIN_JAVA_RUNTIME_JAR")?.let(::File)?.existsOrNull() + ?: kotlinCompilerJar.let { File(it.parentFile, KOTLIN_JAVA_RUNTIME_JAR) }.existsOrNull() + ?: getResourcePathForClass(StandardScriptTemplate::class.java).existsOrNull() + ?: throw FileNotFoundException("Cannot find kotlin runtime jar, set KOTLIN_JAVA_RUNTIME_JAR property to proper location") diff --git a/libraries/tools/kotlin-script-util/src/main/kotlin/org/jetbrains/kotlin/script/jsr223/KotlinJsr232StandardScriptEngineFactory.kt b/libraries/tools/kotlin-script-util/src/main/kotlin/org/jetbrains/kotlin/script/jsr223/KotlinJsr232StandardScriptEngineFactory.kt deleted file mode 100644 index a2a79e777d8..00000000000 --- a/libraries/tools/kotlin-script-util/src/main/kotlin/org/jetbrains/kotlin/script/jsr223/KotlinJsr232StandardScriptEngineFactory.kt +++ /dev/null @@ -1,72 +0,0 @@ -/* - * Copyright 2010-2016 JetBrains s.r.o. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.jetbrains.kotlin.script.jsr223 - -import com.intellij.openapi.util.Disposer -import org.jetbrains.kotlin.cli.common.KotlinVersion -import org.jetbrains.kotlin.cli.jvm.config.addJvmClasspathRoots -import org.jetbrains.kotlin.config.CommonConfigurationKeys -import org.jetbrains.kotlin.config.CompilerConfiguration -import org.jetbrains.kotlin.config.JVMConfigurationKeys -import org.jetbrains.kotlin.script.StandardScriptDefinition -import org.jetbrains.kotlin.utils.PathUtil -import javax.script.ScriptEngine -import javax.script.ScriptEngineFactory - -@Suppress("unused") // used in javax.script.ScriptEngineFactory META-INF file -class KotlinJsr232StandardScriptEngineFactory: ScriptEngineFactory { - - override fun getLanguageName(): String = "kotlin" - override fun getLanguageVersion(): String = KotlinVersion.VERSION - override fun getEngineName(): String = "kotlin" - override fun getEngineVersion(): String = KotlinVersion.VERSION - override fun getExtensions(): List = listOf("kts") - override fun getMimeTypes(): List = listOf("text/x-kotlin") - override fun getNames(): List = listOf("kotlin") - - override fun getScriptEngine(): ScriptEngine = - KotlinJsr232ScriptEngine( - Disposer.newDisposable(), - this, - StandardScriptDefinition, - CompilerConfiguration().apply { - addJvmClasspathRoots(PathUtil.getJdkClassesRoots()) - addJvmClasspathRoots(PathUtil.getKotlinPathsForCompiler().let { listOf(it.runtimePath, it.reflectPath) }) - // TODO: addJvmClasspathRoots(config.classpath) - put(CommonConfigurationKeys.MODULE_NAME, "kotlin-script") - put(JVMConfigurationKeys.INCLUDE_RUNTIME, true) - }, - Thread.currentThread().contextClassLoader) - - override fun getOutputStatement(toDisplay: String?): String = "print(\"$toDisplay\")" - override fun getMethodCallSyntax(obj: String, m: String, vararg args: String): String = "$obj.$m(${args.joinToString()})" - - override fun getProgram(vararg statements: String): String { - val sep = System.getProperty("line.separator") - return statements.joinToString(sep) + sep - } - - override fun getParameter(key: String?): Any? = - when (key) { - ScriptEngine.NAME -> engineName - ScriptEngine.LANGUAGE -> languageName - ScriptEngine.LANGUAGE_VERSION -> languageVersion - ScriptEngine.ENGINE -> engineName - ScriptEngine.ENGINE_VERSION -> engineVersion - else -> null - } -} \ No newline at end of file diff --git a/libraries/tools/kotlin-script-util/src/test/kotlin/org/jetbrains/kotlin/script/util/ScriptUtilIT.kt b/libraries/tools/kotlin-script-util/src/test/kotlin/org/jetbrains/kotlin/script/util/ScriptUtilIT.kt index f43265dbc60..f53de86a8ca 100644 --- a/libraries/tools/kotlin-script-util/src/test/kotlin/org/jetbrains/kotlin/script/util/ScriptUtilIT.kt +++ b/libraries/tools/kotlin-script-util/src/test/kotlin/org/jetbrains/kotlin/script/util/ScriptUtilIT.kt @@ -116,7 +116,7 @@ done try { val configuration = CompilerConfiguration().apply { addJvmClasspathRoots(PathUtil.getJdkClassesRoots()) - val rtJar = System.getProperty("runtimeJar") + val rtJar = System.getProperty("KOTLIN_JAVA_RUNTIME_JAR") Assert.assertNotNull(rtJar) addJvmClasspathRoot(File(rtJar)) put(CLIConfigurationKeys.MESSAGE_COLLECTOR_KEY, messageCollector)