PR-1021: Merge Keplin project scripting code into Kotlin core.
Overhauls the scripting layers (GenericRepl and related, and JSR223 and related) Adds repeating modes (none, only latest eval'd line, or random order) Also adds better thread-safe IO capturing, default imports, SimpleRepl wrapper, more unit tests NOTE: the script-util part of the pull request was rejected due to various problems and incompatibilities. It may be incorporated into the code later. (originally cherry picked from commit 6f7d517)
This commit is contained in:
+14
-15
@@ -28,6 +28,7 @@ import java.io.OutputStream
|
||||
import javax.script.ScriptContext
|
||||
import javax.script.ScriptEngineFactory
|
||||
import javax.script.ScriptException
|
||||
import kotlin.reflect.KClass
|
||||
|
||||
class KotlinJsr223JvmDaemonCompileScriptEngine(
|
||||
disposable: Disposable,
|
||||
@@ -35,8 +36,8 @@ class KotlinJsr223JvmDaemonCompileScriptEngine(
|
||||
compilerJar: File,
|
||||
templateClasspath: List<File>,
|
||||
templateClassName: String,
|
||||
getScriptArgs: (ScriptContext) -> Array<Any?>?,
|
||||
scriptArgsTypes: Array<Class<*>>?,
|
||||
getScriptArgs: (ScriptContext, Array<out KClass<out Any>>?) -> ScriptArgsWithTypes?,
|
||||
scriptArgsTypes: Array<out KClass<out Any>>?,
|
||||
compilerOut: OutputStream = System.err
|
||||
) : KotlinJsr223JvmScriptEngineBase(factory), KotlinJsr223JvmInvocableScriptEngine {
|
||||
|
||||
@@ -56,20 +57,18 @@ class KotlinJsr223JvmDaemonCompileScriptEngine(
|
||||
}
|
||||
|
||||
// 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) }
|
||||
val localEvaluator by lazy { GenericReplCompilingEvaluator(replCompiler, templateClasspath, Thread.currentThread().contextClassLoader, getScriptArgs(getContext(), scriptArgsTypes)) }
|
||||
|
||||
override val replEvaluator: ReplCompiledEvaluator get() = localEvaluator
|
||||
override val replScriptEvaluator: ReplEvaluatorBase get() = localEvaluator
|
||||
}
|
||||
override val replScriptEvaluator: ReplFullEvaluator get() = localEvaluator
|
||||
|
||||
private fun connectToCompileService(compilerJar: File): CompileService {
|
||||
val compilerId = CompilerId.makeCompilerId(compilerJar)
|
||||
val daemonOptions = configureDaemonOptions()
|
||||
val daemonJVMOptions = DaemonJVMOptions()
|
||||
|
||||
private fun connectToCompileService(compilerJar: File): CompileService {
|
||||
val compilerId = CompilerId.makeCompilerId(compilerJar)
|
||||
val daemonOptions = configureDaemonOptions()
|
||||
val daemonJVMOptions = DaemonJVMOptions()
|
||||
val daemonReportMessages = arrayListOf<DaemonReportMessage>()
|
||||
|
||||
val daemonReportMessages = arrayListOf<DaemonReportMessage>()
|
||||
|
||||
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}" })
|
||||
}
|
||||
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}" })
|
||||
}
|
||||
}
|
||||
+5
-5
@@ -31,14 +31,15 @@ import java.io.File
|
||||
import java.net.URLClassLoader
|
||||
import javax.script.ScriptContext
|
||||
import javax.script.ScriptEngineFactory
|
||||
import kotlin.reflect.KClass
|
||||
|
||||
class KotlinJsr223JvmLocalScriptEngine(
|
||||
disposable: Disposable,
|
||||
factory: ScriptEngineFactory,
|
||||
val templateClasspath: List<File>,
|
||||
templateClassName: String,
|
||||
getScriptArgs: (ScriptContext) -> Array<Any?>?,
|
||||
scriptArgsTypes: Array<Class<*>>?
|
||||
getScriptArgs: (ScriptContext, Array<out KClass<out Any>>?) -> ScriptArgsWithTypes?,
|
||||
scriptArgsTypes: Array<out KClass<out Any>>?
|
||||
) : KotlinJsr223JvmScriptEngineBase(factory), KotlinJsr223JvmInvocableScriptEngine {
|
||||
|
||||
override val replCompiler: ReplCompiler by lazy {
|
||||
@@ -49,10 +50,9 @@ class KotlinJsr223JvmLocalScriptEngine(
|
||||
PrintingMessageCollector(System.out, MessageRenderer.WITHOUT_PATHS, false))
|
||||
}
|
||||
// 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) }
|
||||
private val localEvaluator by lazy { GenericReplCompilingEvaluator(replCompiler, templateClasspath, Thread.currentThread().contextClassLoader, getScriptArgs(getContext(), scriptArgsTypes)) }
|
||||
|
||||
override val replEvaluator: ReplCompiledEvaluator get() = localEvaluator
|
||||
override val replScriptEvaluator: ReplEvaluatorBase get() = localEvaluator
|
||||
override val replScriptEvaluator: ReplFullEvaluator get() = localEvaluator
|
||||
|
||||
private fun makeScriptDefinition(templateClasspath: List<File>, templateClassName: String): KotlinScriptDefinition {
|
||||
val classloader = URLClassLoader(templateClasspath.map { it.toURI().toURL() }.toTypedArray(), this.javaClass.classLoader)
|
||||
|
||||
+24
-30
@@ -20,6 +20,7 @@ package org.jetbrains.kotlin.script.jsr223
|
||||
|
||||
import com.intellij.openapi.util.Disposer
|
||||
import org.jetbrains.kotlin.cli.common.repl.KotlinJsr223JvmScriptEngineFactoryBase
|
||||
import org.jetbrains.kotlin.cli.common.repl.ScriptArgsWithTypes
|
||||
import org.jetbrains.kotlin.cli.jvm.K2JVMCompiler
|
||||
import org.jetbrains.kotlin.script.util.classpathFromClass
|
||||
import org.jetbrains.kotlin.script.util.classpathFromClassloader
|
||||
@@ -40,8 +41,8 @@ class KotlinJsr223JvmLocalScriptEngineFactory : KotlinJsr223JvmScriptEngineFacto
|
||||
this,
|
||||
scriptCompilationClasspathFromContext(),
|
||||
"kotlin.script.templates.standard.ScriptTemplateWithBindings",
|
||||
{ ctx -> arrayOf(ctx.getBindings(ScriptContext.ENGINE_SCOPE)) },
|
||||
arrayOf(Map::class.java)
|
||||
{ ctx, types -> ScriptArgsWithTypes(arrayOf(ctx.getBindings(ScriptContext.ENGINE_SCOPE)), types ?: emptyArray()) },
|
||||
arrayOf(Map::class)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -54,33 +55,15 @@ class KotlinJsr223JvmDaemonLocalEvalScriptEngineFactory : KotlinJsr223JvmScriptE
|
||||
kotlinCompilerJar,
|
||||
scriptCompilationClasspathFromContext(),
|
||||
"kotlin.script.templates.standard.ScriptTemplateWithBindings",
|
||||
{ ctx -> arrayOf(ctx.getBindings(ScriptContext.ENGINE_SCOPE)) },
|
||||
arrayOf(Map::class.java)
|
||||
{ ctx, types -> ScriptArgsWithTypes(arrayOf(ctx.getBindings(ScriptContext.ENGINE_SCOPE)), types ?: emptyArray()) },
|
||||
arrayOf(Map::class)
|
||||
)
|
||||
}
|
||||
|
||||
private fun makeSerializableArgumentsForTemplateWithBindings(ctx: ScriptContext): Array<Any?> {
|
||||
val bindings = ctx.getBindings(ScriptContext.ENGINE_SCOPE)
|
||||
val serializableBindings = linkedMapOf<String, Any>()
|
||||
// TODO: consider deeper analysis and copying to serializable data if possible
|
||||
serializableBindings.putAll(bindings)
|
||||
return arrayOf(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: File by lazy {
|
||||
// highest prio - explicit property
|
||||
System.getProperty("kotlin.compiler.jar")?.let(::File)?.existsOrNull()
|
||||
// search classpath from context classloader and `java.class.path` property
|
||||
?: (classpathFromClass(Thread.currentThread().contextClassLoader, K2JVMCompiler::class)
|
||||
?: contextClasspath(KOTLIN_COMPILER_JAR, Thread.currentThread().contextClassLoader)
|
||||
?: classpathFromClasspathProperty()
|
||||
)?.firstOrNull { it.matchMaybeVersionedFile(KOTLIN_COMPILER_JAR) }
|
||||
?: throw FileNotFoundException("Cannot find kotlin compiler jar, set kotlin.compiler.jar property to proper location")
|
||||
}
|
||||
|
||||
private fun <T> Iterable<T>.anyOrNull(predicate: (T) -> Boolean) = if (any(predicate)) this else null
|
||||
|
||||
private fun File.matchMaybeVersionedFile(baseName: String) =
|
||||
@@ -89,20 +72,30 @@ private fun File.matchMaybeVersionedFile(baseName: String) =
|
||||
name.startsWith(baseName.removeSuffix(".jar") + "-")
|
||||
|
||||
private fun contextClasspath(keyName: String, classLoader: ClassLoader): List<File>? =
|
||||
( classpathFromClassloader(classLoader)?.anyOrNull { it.matchMaybeVersionedFile(keyName) }
|
||||
?: manifestClassPath(classLoader)?.anyOrNull { it.matchMaybeVersionedFile(keyName) }
|
||||
(classpathFromClassloader(classLoader)?.anyOrNull { it.matchMaybeVersionedFile(keyName) }
|
||||
?: manifestClassPath(classLoader)?.anyOrNull { it.matchMaybeVersionedFile(keyName) }
|
||||
)?.toList()
|
||||
|
||||
|
||||
private fun scriptCompilationClasspathFromContext(classLoader: ClassLoader = Thread.currentThread().contextClassLoader): List<File> =
|
||||
( System.getProperty("kotlin.script.classpath")?.split(File.pathSeparator)?.map(::File)
|
||||
?: contextClasspath(KOTLIN_JAVA_RUNTIME_JAR, classLoader)
|
||||
?: listOf(kotlinRuntimeJar, kotlinScriptRuntimeJar)
|
||||
(System.getProperty("kotlin.script.classpath")?.split(File.pathSeparator)?.map(::File)
|
||||
?: contextClasspath(KOTLIN_JAVA_RUNTIME_JAR, classLoader)
|
||||
?: listOf(kotlinRuntimeJar, kotlinScriptRuntimeJar)
|
||||
)
|
||||
.map { it?.canonicalFile }
|
||||
.distinct()
|
||||
.mapNotNull { it?.existsOrNull() }
|
||||
.map { it?.canonicalFile }
|
||||
.distinct()
|
||||
.mapNotNull { it?.existsOrNull() }
|
||||
|
||||
private val kotlinCompilerJar: File by lazy {
|
||||
// highest prio - explicit property
|
||||
System.getProperty("kotlin.compiler.jar")?.let(::File)?.existsOrNull()
|
||||
// search classpath from context classloader and `java.class.path` property
|
||||
?: (classpathFromClass(Thread.currentThread().contextClassLoader, K2JVMCompiler::class)
|
||||
?: contextClasspath(KOTLIN_COMPILER_JAR, Thread.currentThread().contextClassLoader)
|
||||
?: classpathFromClasspathProperty()
|
||||
)?.firstOrNull { it.matchMaybeVersionedFile(KOTLIN_COMPILER_JAR) }
|
||||
?: throw FileNotFoundException("Cannot find kotlin compiler jar, set kotlin.compiler.jar property to proper location")
|
||||
}
|
||||
|
||||
private val kotlinRuntimeJar: File? by lazy {
|
||||
System.getProperty("kotlin.java.runtime.jar")?.let(::File)?.existsOrNull()
|
||||
@@ -117,3 +110,4 @@ private val kotlinScriptRuntimeJar: File? by lazy {
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
+1
-1
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2010-2016 JetBrains s.r.o.
|
||||
* Copyright 2010-2017 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.
|
||||
|
||||
+1
-1
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2010-2016 JetBrains s.r.o.
|
||||
* Copyright 2010-2017 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.
|
||||
|
||||
+1
-1
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2010-2016 JetBrains s.r.o.
|
||||
* Copyright 2010-2017 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.
|
||||
|
||||
+1
-1
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2010-2016 JetBrains s.r.o.
|
||||
* Copyright 2010-2017 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.
|
||||
|
||||
+1
-1
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2010-2016 JetBrains s.r.o.
|
||||
* Copyright 2010-2017 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.
|
||||
|
||||
Reference in New Issue
Block a user