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:
apatrida
2017-01-18 04:17:41 +01:00
committed by Ilya Chernikov
parent 8caf607378
commit 5ad06e1e92
29 changed files with 1146 additions and 548 deletions
@@ -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}" })
}
}
@@ -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)
@@ -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,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,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,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,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,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.