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:
-124
@@ -1,124 +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.cli.common.repl
|
||||
|
||||
import org.jetbrains.kotlin.resolve.jvm.JvmClassName
|
||||
import java.io.File
|
||||
import java.net.URLClassLoader
|
||||
import java.util.concurrent.locks.ReentrantReadWriteLock
|
||||
import kotlin.concurrent.read
|
||||
import kotlin.concurrent.write
|
||||
|
||||
open class GenericReplCompiledEvaluator(baseClasspath: Iterable<File>,
|
||||
baseClassloader: ClassLoader?,
|
||||
val scriptArgs: Array<Any?>? = null,
|
||||
val scriptArgsTypes: Array<Class<*>>? = null
|
||||
) : ReplCompiledEvaluator {
|
||||
|
||||
private var classLoader: org.jetbrains.kotlin.cli.common.repl.ReplClassLoader = makeReplClassLoader(baseClassloader, baseClasspath)
|
||||
private val classLoaderLock = ReentrantReadWriteLock()
|
||||
|
||||
// TODO: consider to expose it as a part of (evaluator, invoker) interface
|
||||
private val evalStateLock = ReentrantReadWriteLock()
|
||||
|
||||
private val compiledLoadedClassesHistory = ReplHistory<ClassWithInstance>()
|
||||
|
||||
override fun eval(codeLine: ReplCodeLine,
|
||||
history: List<ReplCodeLine>,
|
||||
compiledClasses: List<CompiledClassData>,
|
||||
hasResult: Boolean,
|
||||
classpathAddendum: List<File>,
|
||||
invokeWrapper: InvokeWrapper?
|
||||
): ReplEvalResult = evalStateLock.write {
|
||||
checkAndUpdateReplHistoryCollection(compiledLoadedClassesHistory, history)?.let {
|
||||
return@eval ReplEvalResult.HistoryMismatch(compiledLoadedClassesHistory.lines, it)
|
||||
}
|
||||
|
||||
var mainLineClassName: String? = null
|
||||
|
||||
fun classNameFromPath(path: String) = JvmClassName.byInternalName(path.replaceFirst("\\.class$".toRegex(), ""))
|
||||
|
||||
fun processCompiledClasses() {
|
||||
val expectedClassName = "Line${codeLine.no}"
|
||||
compiledClasses.filter { it.path.endsWith(".class") }
|
||||
.forEach {
|
||||
val className = classNameFromPath(it.path)
|
||||
if (className.internalName == expectedClassName || className.internalName.endsWith("/$expectedClassName")) {
|
||||
mainLineClassName = className.internalName.replace('/', '.')
|
||||
}
|
||||
classLoader.addClass(className, it.bytes)
|
||||
}
|
||||
}
|
||||
|
||||
classLoaderLock.read {
|
||||
if (classpathAddendum.isNotEmpty()) {
|
||||
classLoaderLock.write {
|
||||
classLoader = makeReplClassLoader(classLoader, classpathAddendum)
|
||||
}
|
||||
}
|
||||
processCompiledClasses()
|
||||
}
|
||||
|
||||
fun compiledClassesNames() = compiledClasses.map { classNameFromPath(it.path).fqNameForClassNameWithoutDollars.asString() }
|
||||
|
||||
val scriptClass = classLoaderLock.read {
|
||||
try {
|
||||
classLoader.loadClass(mainLineClassName!!)
|
||||
}
|
||||
catch (e: Throwable) {
|
||||
return ReplEvalResult.Error.Runtime(compiledLoadedClassesHistory.lines, "Error loading class $mainLineClassName: known classes: ${compiledClassesNames()}",
|
||||
e as? Exception)
|
||||
}
|
||||
}
|
||||
|
||||
fun getConstructorParams(): Array<Class<*>> =
|
||||
(compiledLoadedClassesHistory.values.map { it.klass.java } +
|
||||
(scriptArgs?.mapIndexed { i, it -> scriptArgsTypes?.getOrNull(i) ?: it?.javaClass ?: Any::class.java } ?: emptyList())
|
||||
).toTypedArray()
|
||||
fun getConstructorArgs() = (compiledLoadedClassesHistory.values.map { it.instance } + scriptArgs.orEmpty()).toTypedArray()
|
||||
|
||||
val constructorParams: Array<Class<*>> = getConstructorParams()
|
||||
val constructorArgs: Array<Any?> = getConstructorArgs()
|
||||
|
||||
val scriptInstanceConstructor = scriptClass.getConstructor(*constructorParams)
|
||||
val scriptInstance =
|
||||
try {
|
||||
invokeWrapper?.invoke { scriptInstanceConstructor.newInstance(*constructorArgs) } ?: scriptInstanceConstructor.newInstance(*constructorArgs)
|
||||
}
|
||||
catch (e: Throwable) {
|
||||
// ignore everything in the stack trace until this constructor call
|
||||
return ReplEvalResult.Error.Runtime(compiledLoadedClassesHistory.lines, renderReplStackTrace(e.cause!!, startFromMethodName = "${scriptClass.name}.<init>"), e as? Exception)
|
||||
}
|
||||
|
||||
compiledLoadedClassesHistory.add(codeLine, ClassWithInstance(scriptClass.kotlin, scriptInstance))
|
||||
|
||||
val rvField = scriptClass.getDeclaredField(SCRIPT_RESULT_FIELD_NAME).apply { isAccessible = true }
|
||||
val rv: Any? = rvField.get(scriptInstance)
|
||||
|
||||
return if (hasResult) ReplEvalResult.ValueResult(compiledLoadedClassesHistory.lines, rv) else ReplEvalResult.UnitResult(compiledLoadedClassesHistory.lines)
|
||||
}
|
||||
|
||||
override val lastEvaluatedScript: ClassWithInstance? get() = evalStateLock.read { compiledLoadedClassesHistory.values.lastOrNull() }
|
||||
|
||||
companion object {
|
||||
private val SCRIPT_RESULT_FIELD_NAME = "\$\$result"
|
||||
}
|
||||
}
|
||||
|
||||
private fun makeReplClassLoader(baseClassloader: ClassLoader?, baseClasspath: Iterable<File>) =
|
||||
ReplClassLoader(URLClassLoader(baseClasspath.map { it.toURI().toURL() }.toTypedArray(), baseClassloader))
|
||||
|
||||
+112
@@ -0,0 +1,112 @@
|
||||
/*
|
||||
* 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.
|
||||
* 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.File
|
||||
import java.util.concurrent.locks.ReentrantReadWriteLock
|
||||
import kotlin.concurrent.write
|
||||
|
||||
class GenericReplCompilingEvaluator(val compiler: ReplCompiler,
|
||||
baseClasspath: Iterable<File>,
|
||||
baseClassloader: ClassLoader?,
|
||||
protected val fallbackScriptArgs: ScriptArgsWithTypes? = null,
|
||||
repeatingMode: ReplRepeatingMode = ReplRepeatingMode.REPEAT_ONLY_MOST_RECENT,
|
||||
protected val stateLock: ReentrantReadWriteLock = ReentrantReadWriteLock()) : ReplFullEvaluator {
|
||||
val evaluator = GenericReplEvaluator(baseClasspath, baseClassloader, fallbackScriptArgs, repeatingMode, stateLock)
|
||||
|
||||
override fun compileAndEval(codeLine: ReplCodeLine, scriptArgs: ScriptArgsWithTypes?, verifyHistory: List<ReplCodeLine>?, invokeWrapper: InvokeWrapper?): ReplEvalResult {
|
||||
return stateLock.write {
|
||||
@Suppress("DEPRECATION")
|
||||
val compiled = compiler.compile(codeLine, verifyHistory)
|
||||
when (compiled) {
|
||||
is ReplCompileResult.Error -> ReplEvalResult.Error.CompileTime(compiled.compiledHistory, compiled.message, compiled.location)
|
||||
is ReplCompileResult.HistoryMismatch -> ReplEvalResult.HistoryMismatch(compiled.compiledHistory, compiled.lineNo)
|
||||
is ReplCompileResult.Incomplete -> ReplEvalResult.Incomplete(compiled.compiledHistory)
|
||||
is ReplCompileResult.CompiledClasses -> {
|
||||
@Suppress("DEPRECATION")
|
||||
val result = eval(compiled, scriptArgs, invokeWrapper)
|
||||
when (result) {
|
||||
is ReplEvalResult.Error,
|
||||
is ReplEvalResult.HistoryMismatch,
|
||||
is ReplEvalResult.Incomplete -> {
|
||||
result.completedEvalHistory.lastOrNull()?.let { compiler.resetToLine(it) }
|
||||
result
|
||||
}
|
||||
is ReplEvalResult.ValueResult,
|
||||
is ReplEvalResult.UnitResult ->
|
||||
result
|
||||
else -> throw IllegalStateException("Unknown evaluator result type ${compiled}")
|
||||
}
|
||||
}
|
||||
else -> throw IllegalStateException("Unknown compiler result type ${compiled}")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun resetToLine(lineNumber: Int): List<ReplCodeLine> {
|
||||
stateLock.write {
|
||||
val removedCompiledLines = compiler.resetToLine(lineNumber)
|
||||
val removedEvaluatorLines = evaluator.resetToLine(lineNumber)
|
||||
|
||||
removedCompiledLines.zip(removedEvaluatorLines).forEach {
|
||||
if (it.first != it.second) {
|
||||
throw IllegalStateException("History mistmatch when resetting lines")
|
||||
}
|
||||
}
|
||||
|
||||
return removedCompiledLines
|
||||
}
|
||||
}
|
||||
|
||||
override fun resetToLine(line: ReplCodeLine): List<ReplCodeLine> = resetToLine(line.no)
|
||||
|
||||
|
||||
override val lastEvaluatedScripts: List<EvalHistoryType> get() = evaluator.lastEvaluatedScripts
|
||||
override val history: List<ReplCodeLine> get() = evaluator.history
|
||||
override val currentClasspath: List<File> get() = evaluator.currentClasspath
|
||||
|
||||
override val compiledHistory: List<ReplCodeLine> get() = compiler.history
|
||||
override val evaluatedHistory: List<ReplCodeLine> get() = evaluator.history
|
||||
|
||||
override fun eval(compileResult: ReplCompileResult.CompiledClasses, scriptArgs: ScriptArgsWithTypes?, invokeWrapper: InvokeWrapper?): ReplEvalResult {
|
||||
return evaluator.eval(compileResult, scriptArgs, invokeWrapper)
|
||||
}
|
||||
|
||||
override fun check(codeLine: ReplCodeLine): ReplCheckResult {
|
||||
return compiler.check(codeLine)
|
||||
}
|
||||
|
||||
override fun compileToEvaluable(codeLine: ReplCodeLine, defaultScriptArgs: ScriptArgsWithTypes?, verifyHistory: List<ReplCodeLine>?): Pair<ReplCompileResult, Evaluable?> {
|
||||
val compiled = compiler.compile(codeLine, verifyHistory)
|
||||
return if (compiled is ReplCompileResult.CompiledClasses) {
|
||||
Pair(compiled, DelayedEvaluation(compiled, stateLock, evaluator, defaultScriptArgs ?: fallbackScriptArgs))
|
||||
}
|
||||
else {
|
||||
Pair(compiled, null)
|
||||
}
|
||||
}
|
||||
|
||||
class DelayedEvaluation(override val compiledCode: ReplCompileResult.CompiledClasses,
|
||||
private val stateLock: ReentrantReadWriteLock,
|
||||
private val evaluator: ReplEvaluator,
|
||||
private val defaultScriptArgs: ScriptArgsWithTypes?) : Evaluable {
|
||||
override fun eval(scriptArgs: ScriptArgsWithTypes?, invokeWrapper: InvokeWrapper?): ReplEvalResult {
|
||||
return stateLock.write { evaluator.eval(compiledCode, scriptArgs ?: defaultScriptArgs, invokeWrapper) }
|
||||
}
|
||||
}
|
||||
}
|
||||
+209
@@ -0,0 +1,209 @@
|
||||
/*
|
||||
* 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 org.jetbrains.kotlin.resolve.jvm.JvmClassName
|
||||
import java.io.File
|
||||
import java.net.URLClassLoader
|
||||
import java.util.concurrent.locks.ReentrantReadWriteLock
|
||||
import kotlin.concurrent.read
|
||||
import kotlin.concurrent.write
|
||||
|
||||
open class GenericReplEvaluator(baseClasspath: Iterable<File>,
|
||||
baseClassloader: ClassLoader?,
|
||||
protected val fallbackScriptArgs: ScriptArgsWithTypes? = null,
|
||||
protected val repeatingMode: ReplRepeatingMode = ReplRepeatingMode.REPEAT_ONLY_MOST_RECENT,
|
||||
protected val stateLock: ReentrantReadWriteLock = ReentrantReadWriteLock()) : ReplEvaluator {
|
||||
|
||||
private val topClassLoader: ReplClassLoader = makeReplClassLoader(baseClassloader, baseClasspath)
|
||||
|
||||
private val evaluatedHistory = ReplHistory<EvalClassWithInstanceAndLoader>()
|
||||
|
||||
override fun resetToLine(lineNumber: Int): List<ReplCodeLine> {
|
||||
return stateLock.write {
|
||||
evaluatedHistory.resetToLine(lineNumber)
|
||||
}.map { it.first }
|
||||
}
|
||||
|
||||
override val history: List<ReplCodeLine> get() = stateLock.read { evaluatedHistory.copySources() }
|
||||
|
||||
override val currentClasspath: List<File> get() = stateLock.read {
|
||||
evaluatedHistory.copyValues().lastOrNull()?.let { it.classLoader.listAllUrlsAsFiles() }
|
||||
?: topClassLoader.listAllUrlsAsFiles()
|
||||
}
|
||||
|
||||
private class HistoryActions(val effectiveHistory: List<EvalClassWithInstanceAndLoader>,
|
||||
val verify: (compareHistory: SourceList?) -> Int?,
|
||||
val addPlaceholder: (line: CompiledReplCodeLine, value: EvalClassWithInstanceAndLoader) -> Unit,
|
||||
val removePlaceholder: (line: CompiledReplCodeLine) -> Boolean,
|
||||
val addFinal: (line: CompiledReplCodeLine, value: EvalClassWithInstanceAndLoader) -> Unit,
|
||||
val processClasses: (compileResult: ReplCompileResult.CompiledClasses) -> Pair<ClassLoader, Class<out Any>>)
|
||||
|
||||
private fun prependClassLoaderWithNewClasses(effectiveHistory: List<EvalClassWithInstanceAndLoader>, compileResult: ReplCompileResult.CompiledClasses): Pair<ClassLoader, Class<out Any>> {
|
||||
return stateLock.write {
|
||||
var mainLineClassName: String? = null
|
||||
val classLoader = makeReplClassLoader(effectiveHistory.lastOrNull()?.classLoader ?: topClassLoader, compileResult.classpathAddendum)
|
||||
fun classNameFromPath(path: String) = JvmClassName.byInternalName(path.replaceFirst("\\.class$".toRegex(), ""))
|
||||
fun compiledClassesNames() = compileResult.classes.map { classNameFromPath(it.path).fqNameForClassNameWithoutDollars.asString() }
|
||||
val expectedClassName = compileResult.generatedClassname
|
||||
compileResult.classes.filter { it.path.endsWith(".class") }
|
||||
.forEach {
|
||||
val className = classNameFromPath(it.path)
|
||||
if (className.internalName == expectedClassName || className.internalName.endsWith("/$expectedClassName")) {
|
||||
mainLineClassName = className.internalName.replace('/', '.')
|
||||
}
|
||||
classLoader.addClass(className, it.bytes)
|
||||
}
|
||||
|
||||
val scriptClass = try {
|
||||
classLoader.loadClass(mainLineClassName!!)
|
||||
}
|
||||
catch (t: Throwable) {
|
||||
throw Exception("Error loading class $mainLineClassName: known classes: ${compiledClassesNames()}", t)
|
||||
}
|
||||
Pair(classLoader, scriptClass)
|
||||
}
|
||||
}
|
||||
|
||||
override fun eval(compileResult: ReplCompileResult.CompiledClasses,
|
||||
scriptArgs: ScriptArgsWithTypes?,
|
||||
invokeWrapper: InvokeWrapper?): ReplEvalResult {
|
||||
stateLock.write {
|
||||
val verifyHistory = compileResult.compiledHistory.dropLast(1)
|
||||
val defaultHistoryActor = HistoryActions(
|
||||
effectiveHistory = evaluatedHistory.copyValues(),
|
||||
verify = { line -> evaluatedHistory.firstMismatchingHistory(line) },
|
||||
addPlaceholder = { line, value -> evaluatedHistory.add(line, value) },
|
||||
removePlaceholder = { line -> evaluatedHistory.removeLast(line) },
|
||||
addFinal = { line, value -> evaluatedHistory.add(line, value) },
|
||||
processClasses = { compiled ->
|
||||
prependClassLoaderWithNewClasses(evaluatedHistory.copyValues(), compiled)
|
||||
})
|
||||
|
||||
val historyActor: HistoryActions = when (repeatingMode) {
|
||||
ReplRepeatingMode.NONE -> defaultHistoryActor
|
||||
ReplRepeatingMode.REPEAT_ONLY_MOST_RECENT -> {
|
||||
val lastItem = evaluatedHistory.lastItem()
|
||||
if (lastItem == null || lastItem.first.source != compileResult.compiledCodeLine.source) {
|
||||
defaultHistoryActor
|
||||
}
|
||||
else {
|
||||
val trimmedHistory = ReplHistory(evaluatedHistory.copyAll().dropLast(1))
|
||||
HistoryActions(
|
||||
effectiveHistory = trimmedHistory.copyValues(),
|
||||
verify = { trimmedHistory.firstMismatchingHistory(it) },
|
||||
addPlaceholder = { _, _ -> NO_ACTION() },
|
||||
removePlaceholder = { NO_ACTION_THAT_RETURNS(true) },
|
||||
addFinal = { line, value ->
|
||||
evaluatedHistory.removeLast(line)
|
||||
evaluatedHistory.add(line, value)
|
||||
},
|
||||
processClasses = { _ ->
|
||||
Pair(lastItem.second.classLoader, lastItem.second.klass.java)
|
||||
})
|
||||
}
|
||||
}
|
||||
ReplRepeatingMode.REPEAT_ANY_PREVIOUS -> {
|
||||
if (evaluatedHistory.isEmpty() || !evaluatedHistory.contains(compileResult.compiledCodeLine.source)) {
|
||||
defaultHistoryActor
|
||||
}
|
||||
else {
|
||||
val historyCopy = evaluatedHistory.copyAll()
|
||||
val matchingItem = historyCopy.first { it.first.source == compileResult.compiledCodeLine.source }
|
||||
val trimmedHistory = ReplHistory(evaluatedHistory.copyAll().takeWhile { it != matchingItem })
|
||||
HistoryActions(
|
||||
effectiveHistory = trimmedHistory.copyValues(),
|
||||
verify = { trimmedHistory.firstMismatchingHistory(it) },
|
||||
addPlaceholder = { _, _ -> NO_ACTION() },
|
||||
removePlaceholder = { NO_ACTION_THAT_RETURNS(true) },
|
||||
addFinal = { line, value ->
|
||||
val extraLines = evaluatedHistory.resetToLine(line)
|
||||
evaluatedHistory.removeLast(line)
|
||||
evaluatedHistory.add(line, value)
|
||||
extraLines.forEach {
|
||||
evaluatedHistory.add(it.first, it.second)
|
||||
}
|
||||
},
|
||||
processClasses = { _ ->
|
||||
Pair(matchingItem.second.classLoader, matchingItem.second.klass.java)
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
val firstMismatch = historyActor.verify(verifyHistory)
|
||||
if (firstMismatch != null) {
|
||||
return@eval ReplEvalResult.HistoryMismatch(evaluatedHistory.copySources(), firstMismatch)
|
||||
}
|
||||
|
||||
val (classLoader, scriptClass) = try {
|
||||
historyActor.processClasses(compileResult)
|
||||
}
|
||||
catch (e: Exception) {
|
||||
return@eval ReplEvalResult.Error.Runtime(evaluatedHistory.copySources(),
|
||||
e.message!!, e)
|
||||
}
|
||||
|
||||
val currentScriptArgs = scriptArgs ?: fallbackScriptArgs
|
||||
val useScriptArgs = currentScriptArgs?.scriptArgs
|
||||
val useScriptArgsTypes = currentScriptArgs?.scriptArgsTypes?.map { it.java }
|
||||
|
||||
val constructorParams: Array<Class<*>> = (historyActor.effectiveHistory.map { it.klass.java } +
|
||||
(useScriptArgs?.mapIndexed { i, it -> useScriptArgsTypes?.getOrNull(i) ?: it?.javaClass ?: Any::class.java } ?: emptyList())
|
||||
).toTypedArray()
|
||||
val constructorArgs: Array<Any?> = (historyActor.effectiveHistory.map { it.instance } + useScriptArgs.orEmpty()).toTypedArray()
|
||||
|
||||
val scriptInstanceConstructor = scriptClass.getConstructor(*constructorParams)
|
||||
|
||||
historyActor.addPlaceholder(compileResult.compiledCodeLine, EvalClassWithInstanceAndLoader(scriptClass.kotlin, null, classLoader, invokeWrapper))
|
||||
|
||||
val scriptInstance =
|
||||
try {
|
||||
if (invokeWrapper != null) invokeWrapper.invoke { scriptInstanceConstructor.newInstance(*constructorArgs) }
|
||||
else scriptInstanceConstructor.newInstance(*constructorArgs)
|
||||
}
|
||||
catch (e: Throwable) {
|
||||
historyActor.removePlaceholder(compileResult.compiledCodeLine)
|
||||
|
||||
// ignore everything in the stack trace until this constructor call
|
||||
return@eval ReplEvalResult.Error.Runtime(evaluatedHistory.copySources(),
|
||||
renderReplStackTrace(e.cause!!, startFromMethodName = "${scriptClass.name}.<init>"), e as? Exception)
|
||||
}
|
||||
|
||||
historyActor.removePlaceholder(compileResult.compiledCodeLine)
|
||||
historyActor.addFinal(compileResult.compiledCodeLine, EvalClassWithInstanceAndLoader(scriptClass.kotlin, scriptInstance, classLoader, invokeWrapper))
|
||||
|
||||
val resultField = scriptClass.getDeclaredField(SCRIPT_RESULT_FIELD_NAME).apply { isAccessible = true }
|
||||
val resultValue: Any? = resultField.get(scriptInstance)
|
||||
|
||||
return if (compileResult.hasResult) ReplEvalResult.ValueResult(evaluatedHistory.copySources(), resultValue)
|
||||
else ReplEvalResult.UnitResult(evaluatedHistory.copySources())
|
||||
}
|
||||
}
|
||||
|
||||
override val lastEvaluatedScripts: List<EvalHistoryType> get() {
|
||||
return stateLock.read { evaluatedHistory.copyAll() }
|
||||
}
|
||||
|
||||
companion object {
|
||||
private val SCRIPT_RESULT_FIELD_NAME = "\$\$result"
|
||||
}
|
||||
|
||||
private fun makeReplClassLoader(baseClassloader: ClassLoader?, baseClasspath: Iterable<File>) =
|
||||
ReplClassLoader(URLClassLoader(baseClasspath.map { it.toURI().toURL() }.toTypedArray(), baseClassloader))
|
||||
}
|
||||
|
||||
+78
-21
@@ -17,60 +17,117 @@
|
||||
package org.jetbrains.kotlin.cli.common.repl
|
||||
|
||||
import org.jetbrains.kotlin.utils.tryCreateCallableMapping
|
||||
import java.lang.reflect.Proxy
|
||||
import javax.script.Invocable
|
||||
import javax.script.ScriptException
|
||||
import kotlin.reflect.*
|
||||
import kotlin.reflect.KClass
|
||||
import kotlin.reflect.KFunction
|
||||
import kotlin.reflect.KParameter
|
||||
import kotlin.reflect.full.functions
|
||||
import kotlin.reflect.full.safeCast
|
||||
|
||||
@Suppress("unused") // used externally (kotlin.script.utils)
|
||||
interface KotlinJsr223JvmInvocableScriptEngine : Invocable {
|
||||
|
||||
val replScriptEvaluator: ReplEvaluatorBase
|
||||
val replScriptEvaluator: ReplEvaluatorExposedInternalHistory
|
||||
|
||||
fun <T: Any> getInterface(klass: KClass<T>): Any? {
|
||||
val (_, instance) = replScriptEvaluator.lastEvaluatedScript ?: throw IllegalArgumentException("no script ")
|
||||
return getInterface(instance, klass)
|
||||
}
|
||||
|
||||
fun <T: Any> getInterface(receiver: Any, klass: KClass<T>): Any? {
|
||||
return klass.safeCast(receiver)
|
||||
private fun prioritizedHistory(receiverClass: KClass<*>?, receiverInstance: Any?): List<EvalClassWithInstanceAndLoader> {
|
||||
return replScriptEvaluator.lastEvaluatedScripts.map { it.second }.filter { it.instance != null }.reversed().assertNotEmpty("no script ").let { history ->
|
||||
if (receiverInstance != null) {
|
||||
val receiverKlass = receiverClass ?: receiverInstance.javaClass.kotlin
|
||||
val receiverInHistory = history.find { it.instance == receiverInstance } ?:
|
||||
EvalClassWithInstanceAndLoader(receiverKlass, receiverInstance, receiverKlass.java.classLoader, history.first().invokeWrapper)
|
||||
listOf(receiverInHistory) + history.filterNot { it == receiverInHistory }
|
||||
}
|
||||
else {
|
||||
history
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun invokeFunction(name: String?, vararg args: Any?): Any? {
|
||||
if (name == null) throw java.lang.NullPointerException("function name cannot be null")
|
||||
val (klass, instance) = replScriptEvaluator.lastEvaluatedScript ?: throw IllegalArgumentException("no script ")
|
||||
return invokeImpl(klass, instance, name, args, invokeWrapper = null)
|
||||
return invokeImpl(prioritizedHistory(null, null), 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(thiz.javaClass.kotlin, thiz, name, args, invokeWrapper = null)
|
||||
return invokeImpl(prioritizedHistory(thiz.javaClass.kotlin, thiz), name, args)
|
||||
}
|
||||
|
||||
private fun invokeImpl(prioritizedCallOrder: List<EvalClassWithInstanceAndLoader>, name: String, args: Array<out Any?>): Any? {
|
||||
// TODO: cache the method lookups?
|
||||
|
||||
val (fn, mapping, invokeWrapper) = prioritizedCallOrder.asSequence().map { attempt ->
|
||||
val candidates = attempt.klass.functions.filter { it.name == name }
|
||||
candidates.findMapping(listOf<Any?>(attempt.instance) + args)?.let {
|
||||
Triple(it.first, it.second, attempt.invokeWrapper)
|
||||
}
|
||||
}.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? {
|
||||
val (_, instance) = replScriptEvaluator.lastEvaluatedScript ?: throw IllegalArgumentException("no script ")
|
||||
return getInterface(instance, clasz)
|
||||
return proxyInterface(null, clasz)
|
||||
}
|
||||
|
||||
override fun <T : Any> getInterface(thiz: Any?, clasz: Class<T>?): T? {
|
||||
if (thiz == null) throw IllegalArgumentException("object cannot be null")
|
||||
return proxyInterface(thiz, clasz)
|
||||
}
|
||||
|
||||
private fun <T : Any> proxyInterface(thiz: Any?, clasz: Class<T>?): T? {
|
||||
replScriptEvaluator.lastEvaluatedScripts.assertNotEmpty("no script")
|
||||
val priority = prioritizedHistory(thiz?.javaClass?.kotlin, thiz)
|
||||
|
||||
if (clasz == null) throw IllegalArgumentException("class object cannot be null")
|
||||
if (!clasz.isInterface) throw IllegalArgumentException("expecting interface")
|
||||
return clasz.kotlin.safeCast(thiz)
|
||||
|
||||
// TODO: cache the method lookups?
|
||||
|
||||
val proxy = Proxy.newProxyInstance(Thread.currentThread().contextClassLoader, arrayOf(clasz)) { _, method, args ->
|
||||
invokeImpl(priority, method.name, args ?: emptyArray())
|
||||
}
|
||||
return clasz.kotlin.safeCast(proxy)
|
||||
}
|
||||
}
|
||||
|
||||
private fun invokeImpl(receiverClass: KClass<*>, receiverInstance: Any, name: String, args: Array<out Any?>, invokeWrapper: InvokeWrapper?): Any? {
|
||||
private fun invokeImpl(prioritizedCallOrder: List<EvalClassWithInstanceAndLoader>, name: String, args: Array<out Any?>): Any? {
|
||||
// TODO: cache the method lookups?
|
||||
|
||||
val (fn, mapping, invokeWrapper) = prioritizedCallOrder.asSequence().map { attempt ->
|
||||
val candidates = attempt.klass.functions.filter { it.name == name }
|
||||
candidates.findMapping(listOf<Any?>(attempt.instance) + args)?.let {
|
||||
Triple(it.first, it.second, attempt.invokeWrapper)
|
||||
}
|
||||
}.filterNotNull().firstOrNull() ?: throw NoSuchMethodException("no suitable function '$name' found")
|
||||
|
||||
val candidates = receiverClass.functions.filter { it.name == name }
|
||||
val (fn, mapping) = candidates.findMapping(listOf<Any?>(receiverInstance) + args) ?:
|
||||
throw NoSuchMethodException("no suitable function '$name' found")
|
||||
val res = try {
|
||||
invokeWrapper?.invoke {
|
||||
if (invokeWrapper != null) {
|
||||
invokeWrapper.invoke {
|
||||
fn.callBy(mapping)
|
||||
}
|
||||
}
|
||||
else {
|
||||
fn.callBy(mapping)
|
||||
} ?: fn.callBy(mapping)
|
||||
}
|
||||
}
|
||||
catch (e: Throwable) {
|
||||
// ignore everything in the stack trace until this constructor call
|
||||
|
||||
+41
-25
@@ -18,25 +18,27 @@ package org.jetbrains.kotlin.cli.common.repl
|
||||
|
||||
import org.jetbrains.kotlin.cli.common.messages.CompilerMessageLocation
|
||||
import java.io.Reader
|
||||
import java.util.concurrent.atomic.AtomicInteger
|
||||
import javax.script.*
|
||||
|
||||
val KOTLIN_SCRIPT_HISTORY_BINDINGS_KEY = "kotlin.script.history"
|
||||
|
||||
|
||||
// TODO consider additional error handling
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
val Bindings.kotlinScriptHistory: MutableList<ReplCodeLine>
|
||||
get() = getOrPut(KOTLIN_SCRIPT_HISTORY_BINDINGS_KEY, { arrayListOf<ReplCodeLine>() }) as MutableList<ReplCodeLine>
|
||||
|
||||
abstract class KotlinJsr223JvmScriptEngineBase(protected val myFactory: ScriptEngineFactory) : AbstractScriptEngine(), ScriptEngine, Compilable {
|
||||
|
||||
private var lineCount = 0
|
||||
protected var codeLineNumber = AtomicInteger(0)
|
||||
|
||||
protected abstract val replCompiler: ReplCompiler
|
||||
protected abstract val replCompiler: ReplCompileAction
|
||||
protected abstract val replScriptEvaluator: ReplFullEvaluator
|
||||
|
||||
protected abstract val replEvaluator: ReplCompiledEvaluator
|
||||
override fun eval(script: String, context: ScriptContext): Any? = compileAndEval(script, context)
|
||||
|
||||
override fun eval(script: String, context: ScriptContext): Any? = compile(script, context).eval(context)
|
||||
|
||||
override fun eval(script: Reader, context: ScriptContext): Any? = compile(script.readText(), context).eval()
|
||||
override fun eval(script: Reader, context: ScriptContext): Any? = compileAndEval(script.readText(), context)
|
||||
|
||||
override fun compile(script: String): CompiledScript = compile(script, getContext())
|
||||
|
||||
@@ -46,42 +48,56 @@ abstract class KotlinJsr223JvmScriptEngineBase(protected val myFactory: ScriptEn
|
||||
|
||||
override fun getFactory(): ScriptEngineFactory = myFactory
|
||||
|
||||
open fun compile(script: String, context: ScriptContext): CompiledScript {
|
||||
lineCount += 1
|
||||
fun nextCodeLine(code: String) = ReplCodeLine(codeLineNumber.incrementAndGet(), code)
|
||||
|
||||
val codeLine = ReplCodeLine(lineCount, script)
|
||||
open fun overrideScriptArgs(context: ScriptContext): ScriptArgsWithTypes? = null
|
||||
|
||||
open fun compileAndEval(script: String, context: ScriptContext): Any? {
|
||||
val codeLine = nextCodeLine(script)
|
||||
val history = context.getBindings(ScriptContext.ENGINE_SCOPE).kotlinScriptHistory
|
||||
val result = replScriptEvaluator.compileAndEval(codeLine, scriptArgs = overrideScriptArgs(context), verifyHistory = history)
|
||||
val ret = when (result) {
|
||||
is ReplEvalResult.ValueResult -> result.value
|
||||
is ReplEvalResult.UnitResult -> null
|
||||
is ReplEvalResult.Error -> throw ScriptException(result.message)
|
||||
is ReplEvalResult.Incomplete -> throw ScriptException("error: incomplete code")
|
||||
is ReplEvalResult.HistoryMismatch -> throw ScriptException("Repl history mismatch at line: ${result.lineNo}")
|
||||
}
|
||||
context.getBindings(ScriptContext.ENGINE_SCOPE).put(KOTLIN_SCRIPT_HISTORY_BINDINGS_KEY, ArrayList(result.completedEvalHistory))
|
||||
return ret
|
||||
}
|
||||
|
||||
open fun compile(script: String, context: ScriptContext): CompiledScript {
|
||||
val codeLine = nextCodeLine(script)
|
||||
val history = context.getBindings(ScriptContext.ENGINE_SCOPE).kotlinScriptHistory
|
||||
|
||||
val compileResult = replCompiler.compile(codeLine, history)
|
||||
|
||||
val compiled = when (compileResult) {
|
||||
is ReplCompileResult.Error -> throw ScriptException("Error${compileResult.locationString()}: ${compileResult.message}")
|
||||
val result = replCompiler.compile(codeLine, history)
|
||||
val compiled = when (result) {
|
||||
is ReplCompileResult.Error -> throw ScriptException("Error${result.locationString()}: ${result.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
|
||||
is ReplCompileResult.HistoryMismatch -> throw ScriptException("Repl history mismatch at line: ${result.lineNo}")
|
||||
is ReplCompileResult.CompiledClasses -> result
|
||||
}
|
||||
|
||||
context.getBindings(ScriptContext.ENGINE_SCOPE).put(KOTLIN_SCRIPT_HISTORY_BINDINGS_KEY, ArrayList(result.compiledHistory))
|
||||
return CompiledKotlinScript(this, codeLine, compiled)
|
||||
}
|
||||
|
||||
open fun eval(compiledScript: CompiledKotlinScript, context: ScriptContext): Any? {
|
||||
val history = context.getBindings(ScriptContext.ENGINE_SCOPE).kotlinScriptHistory
|
||||
|
||||
val evalResult = try {
|
||||
replEvaluator.eval(compiledScript.codeLine, history, compiledScript.compiledData.classes, compiledScript.compiledData.hasResult, compiledScript.compiledData.classpathAddendum)
|
||||
val result = try {
|
||||
replScriptEvaluator.eval(compiledScript.compiledData, scriptArgs = overrideScriptArgs(context))
|
||||
}
|
||||
catch (e: Exception) {
|
||||
throw ScriptException(e)
|
||||
}
|
||||
|
||||
val ret = when (evalResult) {
|
||||
is ReplEvalResult.ValueResult -> evalResult.value
|
||||
val ret = when (result) {
|
||||
is ReplEvalResult.ValueResult -> result.value
|
||||
is ReplEvalResult.UnitResult -> null
|
||||
is ReplEvalResult.Error -> throw ScriptException(evalResult.message)
|
||||
is ReplEvalResult.Error -> throw ScriptException(result.message)
|
||||
is ReplEvalResult.Incomplete -> throw ScriptException("error: incomplete code")
|
||||
is ReplEvalResult.HistoryMismatch -> throw ScriptException("Repl history mismatch at line: ${evalResult.lineNo}")
|
||||
is ReplEvalResult.HistoryMismatch -> throw ScriptException("Repl history mismatch at line: ${result.lineNo}")
|
||||
}
|
||||
history.add(compiledScript.codeLine)
|
||||
context.getBindings(ScriptContext.ENGINE_SCOPE).put(KOTLIN_SCRIPT_HISTORY_BINDINGS_KEY, ArrayList(result.completedEvalHistory))
|
||||
return ret
|
||||
}
|
||||
|
||||
|
||||
@@ -28,105 +28,173 @@ data class ReplCodeLine(val no: Int, val code: String) : Serializable {
|
||||
}
|
||||
}
|
||||
|
||||
data class ClassWithInstance(val klass: KClass<*>, val instance: Any)
|
||||
|
||||
// TODO: consider storing code hash where source is not needed
|
||||
data class CompiledReplCodeLine(val className: String, val source: ReplCodeLine) : Serializable {
|
||||
companion object {
|
||||
private val serialVersionUID: Long = 8228307678L
|
||||
}
|
||||
}
|
||||
|
||||
data class CompiledClassData(val path: String, val bytes: ByteArray) : Serializable {
|
||||
override fun equals(other: Any?): Boolean = (other as? CompiledClassData)?.let { path == it.path && Arrays.equals(bytes, it.bytes) } ?: false
|
||||
override fun hashCode(): Int = path.hashCode() + Arrays.hashCode(bytes)
|
||||
|
||||
companion object {
|
||||
private val serialVersionUID: Long = 8228357578L
|
||||
}
|
||||
}
|
||||
|
||||
sealed class ReplCheckResult(val updatedHistory: List<ReplCodeLine>) : Serializable {
|
||||
class Ok(updatedHistory: List<ReplCodeLine>) : ReplCheckResult(updatedHistory)
|
||||
class Incomplete(updatedHistory: List<ReplCodeLine>) : ReplCheckResult(updatedHistory)
|
||||
class Error(updatedHistory: List<ReplCodeLine>,
|
||||
val message: String,
|
||||
val location: CompilerMessageLocation = CompilerMessageLocation.NO_LOCATION
|
||||
) : ReplCheckResult(updatedHistory)
|
||||
{
|
||||
|
||||
interface ReplCheckAction {
|
||||
fun check(codeLine: ReplCodeLine): ReplCheckResult
|
||||
}
|
||||
|
||||
sealed class ReplCheckResult : Serializable {
|
||||
class Ok : ReplCheckResult()
|
||||
|
||||
class Incomplete : ReplCheckResult()
|
||||
|
||||
class Error(val message: String,
|
||||
val location: CompilerMessageLocation = CompilerMessageLocation.NO_LOCATION) : ReplCheckResult() {
|
||||
override fun toString(): String = "Error(message = \"$message\""
|
||||
}
|
||||
|
||||
companion object {
|
||||
private val serialVersionUID: Long = 8228357578L
|
||||
private val serialVersionUID: Long = 8228307678L
|
||||
}
|
||||
}
|
||||
|
||||
sealed class ReplCompileResult(val updatedHistory: List<ReplCodeLine>) : Serializable {
|
||||
class CompiledClasses(updatedHistory: List<ReplCodeLine>,
|
||||
interface ReplResettableCodeLine {
|
||||
fun resetToLine(lineNumber: Int): List<ReplCodeLine>
|
||||
|
||||
fun resetToLine(line: ReplCodeLine): List<ReplCodeLine> = resetToLine(line.no)
|
||||
}
|
||||
|
||||
interface ReplCodeLineHistory {
|
||||
val history: List<ReplCodeLine>
|
||||
}
|
||||
|
||||
interface ReplCombinedHistory {
|
||||
val compiledHistory: List<ReplCodeLine>
|
||||
val evaluatedHistory: List<ReplCodeLine>
|
||||
}
|
||||
|
||||
interface ReplCompileAction {
|
||||
fun compile(codeLine: ReplCodeLine, verifyHistory: List<ReplCodeLine>? = null): ReplCompileResult
|
||||
}
|
||||
|
||||
sealed class ReplCompileResult(val compiledHistory: List<ReplCodeLine>) : Serializable {
|
||||
class CompiledClasses(compiledHistory: List<ReplCodeLine>,
|
||||
val compiledCodeLine: CompiledReplCodeLine,
|
||||
val generatedClassname: String,
|
||||
val classes: List<CompiledClassData>,
|
||||
val hasResult: Boolean,
|
||||
val classpathAddendum: List<File>
|
||||
) : ReplCompileResult(updatedHistory)
|
||||
class Incomplete(updatedHistory: List<ReplCodeLine>) : ReplCompileResult(updatedHistory)
|
||||
class HistoryMismatch(updatedHistory: List<ReplCodeLine>, val lineNo: Int): ReplCompileResult(updatedHistory)
|
||||
class Error(updatedHistory: List<ReplCodeLine>,
|
||||
val classpathAddendum: List<File>) : ReplCompileResult(compiledHistory)
|
||||
|
||||
class Incomplete(compiledHistory: List<ReplCodeLine>) : ReplCompileResult(compiledHistory)
|
||||
|
||||
class HistoryMismatch(compiledHistory: List<ReplCodeLine>, val lineNo: Int) : ReplCompileResult(compiledHistory)
|
||||
|
||||
class Error(compiledHistory: List<ReplCodeLine>,
|
||||
val message: String,
|
||||
val location: CompilerMessageLocation = CompilerMessageLocation.NO_LOCATION
|
||||
) : ReplCompileResult(updatedHistory)
|
||||
{
|
||||
val location: CompilerMessageLocation = CompilerMessageLocation.NO_LOCATION) : ReplCompileResult(compiledHistory) {
|
||||
override fun toString(): String = "Error(message = \"$message\""
|
||||
}
|
||||
|
||||
companion object {
|
||||
private val serialVersionUID: Long = 8228357578L
|
||||
private val serialVersionUID: Long = 8228307678L
|
||||
}
|
||||
}
|
||||
|
||||
sealed class ReplEvalResult(val updatedHistory: List<ReplCodeLine>) : Serializable {
|
||||
class ValueResult(updatedHistory: List<ReplCodeLine>, val value: Any?) : ReplEvalResult(updatedHistory) {
|
||||
interface ReplCompiler : ReplResettableCodeLine, ReplCodeLineHistory, ReplCompileAction, ReplCheckAction
|
||||
|
||||
typealias EvalHistoryType = Pair<CompiledReplCodeLine, EvalClassWithInstanceAndLoader>
|
||||
|
||||
interface ReplEvaluatorExposedInternalHistory {
|
||||
val lastEvaluatedScripts: List<EvalHistoryType>
|
||||
}
|
||||
|
||||
interface ReplClasspath {
|
||||
val currentClasspath: List<File>
|
||||
}
|
||||
|
||||
data class EvalClassWithInstanceAndLoader(val klass: KClass<*>, val instance: Any?, val classLoader: ClassLoader, val invokeWrapper: InvokeWrapper?)
|
||||
|
||||
interface ReplEvalAction {
|
||||
fun eval(compileResult: ReplCompileResult.CompiledClasses,
|
||||
scriptArgs: ScriptArgsWithTypes? = null,
|
||||
invokeWrapper: InvokeWrapper? = null): ReplEvalResult
|
||||
}
|
||||
|
||||
sealed class ReplEvalResult(val completedEvalHistory: List<ReplCodeLine>) : Serializable {
|
||||
class ValueResult(completedEvalHistory: List<ReplCodeLine>, val value: Any?) : ReplEvalResult(completedEvalHistory) {
|
||||
override fun toString(): String = "Result: $value"
|
||||
}
|
||||
class UnitResult(updatedHistory: List<ReplCodeLine>) : ReplEvalResult(updatedHistory)
|
||||
class Incomplete(updatedHistory: List<ReplCodeLine>) : ReplEvalResult(updatedHistory)
|
||||
class HistoryMismatch(updatedHistory: List<ReplCodeLine>, val lineNo: Int): ReplEvalResult(updatedHistory)
|
||||
sealed class Error(updatedHistory: List<ReplCodeLine>, val message: String) : ReplEvalResult(updatedHistory) {
|
||||
class Runtime(updatedHistory: List<ReplCodeLine>, message: String, val cause: Exception? = null) : Error(updatedHistory, message)
|
||||
class CompileTime(updatedHistory: List<ReplCodeLine>,
|
||||
|
||||
class UnitResult(completedEvalHistory: List<ReplCodeLine>) : ReplEvalResult(completedEvalHistory)
|
||||
|
||||
class Incomplete(completedEvalHistory: List<ReplCodeLine>) : ReplEvalResult(completedEvalHistory)
|
||||
|
||||
class HistoryMismatch(completedEvalHistory: List<ReplCodeLine>, val lineNo: Int) : ReplEvalResult(completedEvalHistory)
|
||||
|
||||
sealed class Error(completedEvalHistory: List<ReplCodeLine>, val message: String) : ReplEvalResult(completedEvalHistory) {
|
||||
class Runtime(completedEvalHistory: List<ReplCodeLine>, message: String, val cause: Exception? = null) : Error(completedEvalHistory, message)
|
||||
|
||||
class CompileTime(completedEvalHistory: List<ReplCodeLine>,
|
||||
message: String,
|
||||
val location: CompilerMessageLocation = CompilerMessageLocation.NO_LOCATION
|
||||
) : Error(updatedHistory, message)
|
||||
val location: CompilerMessageLocation = CompilerMessageLocation.NO_LOCATION) : Error(completedEvalHistory, message)
|
||||
|
||||
override fun toString(): String = "${this::class.simpleName}Error(message = \"$message\""
|
||||
}
|
||||
|
||||
companion object {
|
||||
private val serialVersionUID: Long = 8228357578L
|
||||
private val serialVersionUID: Long = 8228307678L
|
||||
}
|
||||
}
|
||||
|
||||
interface ReplChecker {
|
||||
fun check(codeLine: ReplCodeLine, history: List<ReplCodeLine>): ReplCheckResult
|
||||
interface ReplEvaluator : ReplResettableCodeLine, ReplCodeLineHistory, ReplEvaluatorExposedInternalHistory, ReplEvalAction, ReplClasspath
|
||||
|
||||
interface ReplAtomicEvalAction {
|
||||
fun compileAndEval(codeLine: ReplCodeLine,
|
||||
scriptArgs: ScriptArgsWithTypes? = null,
|
||||
verifyHistory: List<ReplCodeLine>? = null,
|
||||
invokeWrapper: InvokeWrapper? = null): ReplEvalResult
|
||||
}
|
||||
|
||||
interface ReplCompiler : ReplChecker {
|
||||
fun compile(codeLine: ReplCodeLine, history: List<ReplCodeLine>): ReplCompileResult
|
||||
interface ReplAtomicEvaluator : ReplResettableCodeLine, ReplCombinedHistory, ReplEvaluatorExposedInternalHistory, ReplAtomicEvalAction, ReplCheckAction, ReplClasspath
|
||||
|
||||
interface ReplDelayedEvalAction {
|
||||
fun compileToEvaluable(codeLine: ReplCodeLine, defaultScriptArgs: ScriptArgsWithTypes? = null, verifyHistory: List<ReplCodeLine>?): Pair<ReplCompileResult, Evaluable?>
|
||||
}
|
||||
|
||||
interface ReplEvaluatorBase {
|
||||
val lastEvaluatedScript: ClassWithInstance?
|
||||
interface Evaluable {
|
||||
val compiledCode: ReplCompileResult.CompiledClasses
|
||||
fun eval(scriptArgs: ScriptArgsWithTypes? = null, invokeWrapper: InvokeWrapper? = null): ReplEvalResult
|
||||
}
|
||||
|
||||
interface ReplCompiledEvaluator : ReplEvaluatorBase {
|
||||
interface ReplFullEvaluator : ReplEvaluator, ReplAtomicEvaluator, ReplDelayedEvalAction, ReplCombinedHistory
|
||||
|
||||
fun eval(codeLine: ReplCodeLine,
|
||||
history: List<ReplCodeLine>,
|
||||
compiledClasses: List<CompiledClassData>,
|
||||
hasResult: Boolean,
|
||||
classpathAddendum: List<File>,
|
||||
invokeWrapper: InvokeWrapper? = null
|
||||
): ReplEvalResult
|
||||
/**
|
||||
* Keep args and arg types together, so as a whole they are present or absent
|
||||
*/
|
||||
class ScriptArgsWithTypes(val scriptArgs: Array<out Any?>, val scriptArgsTypes: Array<out KClass<out Any>>) : Serializable {
|
||||
companion object {
|
||||
private val serialVersionUID: Long = 8529357500L
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
interface ReplEvaluator : ReplChecker, ReplEvaluatorBase {
|
||||
|
||||
fun eval(codeLine: ReplCodeLine,
|
||||
history: List<ReplCodeLine>,
|
||||
invokeWrapper: InvokeWrapper? = null
|
||||
): ReplEvalResult
|
||||
interface ScriptTemplateEmptyArgsProvider {
|
||||
val defaultEmptyArgs: ScriptArgsWithTypes?
|
||||
}
|
||||
|
||||
class SimpleScriptTemplateEmptyArgsProvider(override val defaultEmptyArgs: ScriptArgsWithTypes? = null) : ScriptTemplateEmptyArgsProvider
|
||||
|
||||
enum class ReplRepeatingMode {
|
||||
NONE,
|
||||
REPEAT_ONLY_MOST_RECENT,
|
||||
REPEAT_ANY_PREVIOUS
|
||||
}
|
||||
|
||||
|
||||
interface InvokeWrapper {
|
||||
operator fun<T> invoke(body: () -> T): T // e.g. for capturing io
|
||||
operator fun <T> invoke(body: () -> T): T // e.g. for capturing io
|
||||
}
|
||||
@@ -0,0 +1,105 @@
|
||||
/*
|
||||
* 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.
|
||||
* 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.Serializable
|
||||
import java.util.concurrent.ConcurrentLinkedDeque
|
||||
|
||||
|
||||
typealias CompiledHistoryItem<T> = Pair<CompiledReplCodeLine, T>
|
||||
typealias SourceHistoryItem<T> = Pair<ReplCodeLine, T>
|
||||
|
||||
typealias CompiledHistoryStorage<T> = ConcurrentLinkedDeque<CompiledHistoryItem<T>>
|
||||
typealias CompiledHistoryList<T> = List<CompiledHistoryItem<T>>
|
||||
typealias SourceHistoryList<T> = List<SourceHistoryItem<T>>
|
||||
typealias SourceList = List<ReplCodeLine>
|
||||
|
||||
/*
|
||||
Not thread safe, the caller is assumed to lock access. Even though ConcurrentLinkedDeque is used, there are mutliple
|
||||
actions that require locking.
|
||||
*/
|
||||
class ReplHistory<T>(startingHistory: CompiledHistoryList<T> = emptyList()) : Serializable {
|
||||
private val history: CompiledHistoryStorage<T> = ConcurrentLinkedDeque(startingHistory)
|
||||
|
||||
fun isEmpty(): Boolean = history.isEmpty()
|
||||
fun isNotEmpty(): Boolean = history.isNotEmpty()
|
||||
|
||||
fun add(line: CompiledReplCodeLine, value: T) {
|
||||
history.add(line to value)
|
||||
}
|
||||
|
||||
/* remove last line only if it is the line we think it is */
|
||||
fun removeLast(line: CompiledReplCodeLine): Boolean {
|
||||
return if (history.peekLast().first == line) {
|
||||
history.removeLast()
|
||||
true
|
||||
}
|
||||
else {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
/* resets back to a previous line number and returns the lines removed */
|
||||
fun resetToLine(lineNumber: Int): SourceHistoryList<T> {
|
||||
val removed = arrayListOf<SourceHistoryItem<T>>()
|
||||
while ((history.peekLast()?.first?.source?.no ?: -1) > lineNumber) {
|
||||
removed.add(history.removeLast().let { Pair(it.first.source, it.second) })
|
||||
}
|
||||
return removed.reversed()
|
||||
}
|
||||
|
||||
fun resetToLine(line: ReplCodeLine): SourceHistoryList<T> {
|
||||
return resetToLine(line.no)
|
||||
}
|
||||
|
||||
fun resetToLine(line: CompiledReplCodeLine): CompiledHistoryList<T> {
|
||||
val removed = arrayListOf<CompiledHistoryItem<T>>()
|
||||
while ((history.peekLast()?.first?.source?.no ?: -1) > line.source.no) {
|
||||
removed.add(history.removeLast())
|
||||
}
|
||||
return removed.reversed()
|
||||
}
|
||||
|
||||
fun contains(line: ReplCodeLine): Boolean = history.any { it.first.source == line }
|
||||
fun contains(line: CompiledReplCodeLine): Boolean = history.any { it.first == line }
|
||||
|
||||
fun lastItem(): CompiledHistoryItem<T>? = history.peekLast()
|
||||
fun lastCodeLine(): CompiledReplCodeLine? = lastItem()?.first
|
||||
fun lastValue(): T? = lastItem()?.second
|
||||
|
||||
fun checkHistoryIsInSync(compareHistory: SourceList?): Boolean {
|
||||
return firstMismatchingHistory(compareHistory) == null
|
||||
}
|
||||
|
||||
// return from the compareHistory the first line that does not match or null
|
||||
fun firstMismatchingHistory(compareHistory: SourceList?): Int? {
|
||||
if (compareHistory == null) return null
|
||||
|
||||
val firstMismatch = history.zip(compareHistory).firstOrNull { it.first.first.source != it.second }?.second?.no
|
||||
if (compareHistory.size == history.size) return firstMismatch
|
||||
if (compareHistory.size > history.size) return compareHistory[history.size].no
|
||||
return history.toList()[compareHistory.size].first.source.no
|
||||
}
|
||||
|
||||
fun copySources(): SourceList = history.map { it.first.source }
|
||||
fun copyValues(): List<T> = history.map { it.second }
|
||||
fun copyAll(): CompiledHistoryList<T> = history.toList()
|
||||
|
||||
companion object {
|
||||
private val serialVersionUID: Long = 8328353000L
|
||||
}
|
||||
}
|
||||
@@ -17,44 +17,11 @@
|
||||
package org.jetbrains.kotlin.cli.common.repl
|
||||
|
||||
import com.google.common.base.Throwables
|
||||
import java.io.Serializable
|
||||
import java.io.File
|
||||
import java.net.URLClassLoader
|
||||
|
||||
// TODO: thread safety!!
|
||||
data class ReplHistory<T>(
|
||||
val lines: MutableList<ReplCodeLine> = arrayListOf(),
|
||||
val values: MutableList<T> = arrayListOf()
|
||||
) : Serializable
|
||||
{
|
||||
init { assert(isValid()) }
|
||||
fun isValid() = lines.size == values.size
|
||||
|
||||
fun add(line: ReplCodeLine, value: T) {
|
||||
lines.add(line)
|
||||
values.add(value)
|
||||
}
|
||||
|
||||
fun trimAt(idx: Int) {
|
||||
lines.dropLast(lines.size - idx)
|
||||
values.dropLast(lines.size - idx)
|
||||
}
|
||||
|
||||
companion object {
|
||||
private val serialVersionUID: Long = 8228357578L
|
||||
}
|
||||
}
|
||||
|
||||
fun <T> checkAndUpdateReplHistoryCollection(history: ReplHistory<T>, linesHistory: Iterable<ReplCodeLine>): Int? {
|
||||
assert(history.isValid())
|
||||
val linesHistoryIt = linesHistory.iterator()
|
||||
var idx = 0
|
||||
while (linesHistoryIt.hasNext()) {
|
||||
val curLine = linesHistoryIt.next()
|
||||
if (history.lines[idx] != curLine) return curLine.no
|
||||
idx += 1
|
||||
}
|
||||
history.trimAt(idx)
|
||||
return null
|
||||
}
|
||||
fun makeSriptBaseName(codeLine: ReplCodeLine, generation: Long) =
|
||||
"Line_${codeLine.no}" + if (generation > 1) "_gen_${generation}" else ""
|
||||
|
||||
fun renderReplStackTrace(cause: Throwable, startFromMethodName: String): String {
|
||||
val newTrace = arrayListOf<StackTraceElement>()
|
||||
@@ -76,3 +43,22 @@ fun renderReplStackTrace(cause: Throwable, startFromMethodName: String): String
|
||||
return Throwables.getStackTraceAsString(cause)
|
||||
}
|
||||
|
||||
fun NO_ACTION(): Unit = Unit
|
||||
fun <T> NO_ACTION_THAT_RETURNS(v: T): T = v
|
||||
|
||||
|
||||
internal fun ClassLoader.listAllUrlsAsFiles(): List<File> {
|
||||
val parents = generateSequence(this) { loader -> loader.parent }.filterIsInstance(URLClassLoader::class.java)
|
||||
return parents.fold(emptyList<File>()) { accum, loader ->
|
||||
loader.listLocalUrlsAsFiles() + accum
|
||||
}.distinct()
|
||||
}
|
||||
|
||||
internal fun URLClassLoader.listLocalUrlsAsFiles(): List<File> {
|
||||
return this.urLs.map { it.toString().removePrefix("file:") }.filterNotNull().map { File(it) }
|
||||
}
|
||||
|
||||
internal fun <T : Any> List<T>.assertNotEmpty(error: String): List<T> {
|
||||
if (this.isEmpty()) throw IllegalStateException(error)
|
||||
return this
|
||||
}
|
||||
@@ -17,43 +17,71 @@
|
||||
package org.jetbrains.kotlin.cli.jvm.repl
|
||||
|
||||
import com.intellij.openapi.Disposable
|
||||
import com.intellij.openapi.diagnostic.Logger
|
||||
import org.jetbrains.kotlin.cli.common.messages.MessageCollector
|
||||
import org.jetbrains.kotlin.cli.common.repl.*
|
||||
import org.jetbrains.kotlin.cli.jvm.config.jvmClasspathRoots
|
||||
import org.jetbrains.kotlin.config.CompilerConfiguration
|
||||
import org.jetbrains.kotlin.script.KotlinScriptDefinition
|
||||
import java.io.File
|
||||
import java.util.concurrent.atomic.AtomicInteger
|
||||
import java.util.concurrent.locks.ReentrantReadWriteLock
|
||||
|
||||
private val logger = Logger.getInstance(GenericRepl::class.java)
|
||||
|
||||
open class GenericRepl(
|
||||
open class GenericRepl protected constructor(
|
||||
disposable: Disposable,
|
||||
scriptDefinition: KotlinScriptDefinition,
|
||||
compilerConfiguration: CompilerConfiguration,
|
||||
messageCollector: MessageCollector,
|
||||
baseClassloader: ClassLoader?,
|
||||
scriptArgs: Array<Any?>? = null,
|
||||
scriptArgsTypes: Array<Class<*>>? = null
|
||||
) : ReplEvaluator, GenericReplCompiler(disposable, scriptDefinition, compilerConfiguration, messageCollector) {
|
||||
protected val fallbackScriptArgs: ScriptArgsWithTypes?,
|
||||
protected val repeatingMode: ReplRepeatingMode = ReplRepeatingMode.NONE,
|
||||
protected val stateLock: ReentrantReadWriteLock
|
||||
) : ReplCompiler, ReplEvaluator, ReplAtomicEvaluator {
|
||||
constructor (
|
||||
disposable: Disposable,
|
||||
scriptDefinition: KotlinScriptDefinition,
|
||||
compilerConfiguration: CompilerConfiguration,
|
||||
messageCollector: MessageCollector,
|
||||
baseClassloader: ClassLoader?,
|
||||
fallbackScriptArgs: ScriptArgsWithTypes? = null
|
||||
) : this(disposable, scriptDefinition, compilerConfiguration, messageCollector, baseClassloader, fallbackScriptArgs, stateLock = ReentrantReadWriteLock())
|
||||
|
||||
private val compiledEvaluator = GenericReplCompiledEvaluator(compilerConfiguration.jvmClasspathRoots, baseClassloader, scriptArgs, scriptArgsTypes)
|
||||
protected val compiler: ReplCompiler by lazy { GenericReplCompiler(disposable, scriptDefinition, compilerConfiguration, messageCollector, stateLock) }
|
||||
protected val evaluator: ReplFullEvaluator by lazy { GenericReplCompilingEvaluator(compiler, compilerConfiguration.jvmClasspathRoots, baseClassloader, fallbackScriptArgs, repeatingMode, stateLock) }
|
||||
protected var codeLineNumber = AtomicInteger(0)
|
||||
|
||||
override val lastEvaluatedScript: ClassWithInstance? get() = compiledEvaluator.lastEvaluatedScript
|
||||
override fun resetToLine(lineNumber: Int): List<ReplCodeLine> {
|
||||
return evaluator.resetToLine(lineNumber)
|
||||
}
|
||||
|
||||
@Synchronized
|
||||
override fun eval(codeLine: ReplCodeLine, history: List<ReplCodeLine>, invokeWrapper: InvokeWrapper?): ReplEvalResult =
|
||||
compileAndEval(this, compiledEvaluator, codeLine, history, invokeWrapper)
|
||||
}
|
||||
override fun resetToLine(line: ReplCodeLine): List<ReplCodeLine> {
|
||||
return evaluator.resetToLine(line)
|
||||
}
|
||||
|
||||
override val history: List<ReplCodeLine> get() = evaluator.history
|
||||
override val compiledHistory: List<ReplCodeLine> get() = compiler.history
|
||||
override val evaluatedHistory: List<ReplCodeLine> get() = evaluator.history
|
||||
|
||||
fun compileAndEval(replCompiler: ReplCompiler, replCompiledEvaluator: ReplCompiledEvaluator, codeLine: ReplCodeLine, history: List<ReplCodeLine>, invokeWrapper: InvokeWrapper?): ReplEvalResult =
|
||||
replCompiler.compile(codeLine, history).let {
|
||||
when (it) {
|
||||
is ReplCompileResult.Incomplete -> ReplEvalResult.Incomplete(it.updatedHistory)
|
||||
is ReplCompileResult.HistoryMismatch -> ReplEvalResult.HistoryMismatch(it.updatedHistory, it.lineNo)
|
||||
is ReplCompileResult.Error -> ReplEvalResult.Error.CompileTime(it.updatedHistory, it.message, it.location)
|
||||
is ReplCompileResult.CompiledClasses -> replCompiledEvaluator.eval(codeLine, history, it.classes, it.hasResult, it.classpathAddendum, invokeWrapper)
|
||||
}
|
||||
}
|
||||
override val currentClasspath: List<File>
|
||||
get() = evaluator.currentClasspath
|
||||
|
||||
override val lastEvaluatedScripts: List<EvalHistoryType>
|
||||
get() = evaluator.lastEvaluatedScripts
|
||||
|
||||
override fun check(codeLine: ReplCodeLine): ReplCheckResult {
|
||||
return compiler.check(codeLine)
|
||||
}
|
||||
|
||||
override fun compile(codeLine: ReplCodeLine, verifyHistory: List<ReplCodeLine>?): ReplCompileResult {
|
||||
return compiler.compile(codeLine, verifyHistory)
|
||||
}
|
||||
|
||||
override fun eval(compileResult: ReplCompileResult.CompiledClasses, scriptArgs: ScriptArgsWithTypes?, invokeWrapper: InvokeWrapper?): ReplEvalResult {
|
||||
return evaluator.eval(compileResult, scriptArgs, invokeWrapper)
|
||||
}
|
||||
|
||||
override fun compileAndEval(codeLine: ReplCodeLine, scriptArgs: ScriptArgsWithTypes?, verifyHistory: List<ReplCodeLine>?, invokeWrapper: InvokeWrapper?): ReplEvalResult {
|
||||
return evaluator.compileAndEval(codeLine, scriptArgs, verifyHistory, invokeWrapper)
|
||||
}
|
||||
|
||||
fun nextCodeLine(code: String) = ReplCodeLine(codeLineNumber.incrementAndGet(), code)
|
||||
}
|
||||
+98
-18
@@ -14,9 +14,14 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
@file:Suppress("UNUSED_PARAMETER")
|
||||
|
||||
package org.jetbrains.kotlin.cli.jvm.repl
|
||||
|
||||
import com.intellij.psi.search.GlobalSearchScope
|
||||
import org.jetbrains.kotlin.cli.common.repl.CompiledReplCodeLine
|
||||
import org.jetbrains.kotlin.cli.common.repl.ReplCodeLine
|
||||
import org.jetbrains.kotlin.cli.common.repl.ReplHistory
|
||||
import org.jetbrains.kotlin.cli.common.repl.ReplResettableCodeLine
|
||||
import org.jetbrains.kotlin.cli.jvm.compiler.CliLightClassGenerationSupport
|
||||
import org.jetbrains.kotlin.cli.jvm.compiler.JvmPackagePartProvider
|
||||
import org.jetbrains.kotlin.cli.jvm.compiler.KotlinCoreEnvironment
|
||||
@@ -30,25 +35,36 @@ import org.jetbrains.kotlin.resolve.*
|
||||
import org.jetbrains.kotlin.resolve.calls.smartcasts.DataFlowInfoFactory
|
||||
import org.jetbrains.kotlin.resolve.diagnostics.Diagnostics
|
||||
import org.jetbrains.kotlin.resolve.jvm.TopDownAnalyzerFacadeForJVM
|
||||
import org.jetbrains.kotlin.resolve.lazy.ResolveSession
|
||||
import org.jetbrains.kotlin.resolve.lazy.*
|
||||
import org.jetbrains.kotlin.resolve.lazy.data.KtClassLikeInfo
|
||||
import org.jetbrains.kotlin.resolve.lazy.declarations.*
|
||||
import org.jetbrains.kotlin.resolve.repl.ReplState
|
||||
import org.jetbrains.kotlin.resolve.lazy.descriptors.LazyScriptDescriptor
|
||||
import org.jetbrains.kotlin.resolve.scopes.ImportingScope
|
||||
import org.jetbrains.kotlin.resolve.scopes.utils.parentsWithSelf
|
||||
import org.jetbrains.kotlin.resolve.scopes.utils.replaceImportingScopes
|
||||
import org.jetbrains.kotlin.script.ScriptPriorities
|
||||
import java.util.concurrent.locks.ReentrantReadWriteLock
|
||||
import kotlin.concurrent.read
|
||||
import kotlin.concurrent.write
|
||||
|
||||
class CliReplAnalyzerEngine(environment: KotlinCoreEnvironment) {
|
||||
class GenericReplAnalyzer(environment: KotlinCoreEnvironment,
|
||||
protected val stateLock: ReentrantReadWriteLock = ReentrantReadWriteLock()) : ReplResettableCodeLine {
|
||||
private val topDownAnalysisContext: TopDownAnalysisContext
|
||||
private val topDownAnalyzer: LazyTopDownAnalyzer
|
||||
private val resolveSession: ResolveSession
|
||||
private val scriptDeclarationFactory: ScriptMutableDeclarationProviderFactory
|
||||
private val replState = ResetableReplState()
|
||||
|
||||
val module: ModuleDescriptorImpl
|
||||
private val replState = ReplState()
|
||||
get() = stateLock.read { field }
|
||||
|
||||
val trace: BindingTraceContext = CliLightClassGenerationSupport.NoScopeRecordCliBindingTrace()
|
||||
get() = stateLock.read { field }
|
||||
|
||||
init {
|
||||
// Module source scope is empty because all binary classes are in the dependency module, and all source classes are guaranteed
|
||||
// to be found via ResolveSession. The latter is true as long as light classes are not needed in REPL (which is currently true
|
||||
// because no symbol declared in the REPL session can be used from Java)
|
||||
// to be found via ResolveSession. The latter is true as long as light classes are not needed in NONE (which is currently true
|
||||
// because no symbol declared in the NONE session can be used from Java)
|
||||
val container = TopDownAnalyzerFacadeForJVM.createContainer(
|
||||
environment.project,
|
||||
emptyList(),
|
||||
@@ -78,18 +94,26 @@ class CliReplAnalyzerEngine(environment: KotlinCoreEnvironment) {
|
||||
}
|
||||
}
|
||||
|
||||
fun analyzeReplLine(psiFile: KtFile, priority: Int): ReplLineAnalysisResult {
|
||||
topDownAnalysisContext.scripts.clear()
|
||||
trace.clearDiagnostics()
|
||||
|
||||
psiFile.script!!.putUserData(ScriptPriorities.PRIORITY_KEY, priority)
|
||||
|
||||
return doAnalyze(psiFile)
|
||||
override fun resetToLine(lineNumber: Int): List<ReplCodeLine> {
|
||||
return stateLock.write { replState.resetToLine(lineNumber) }
|
||||
}
|
||||
|
||||
private fun doAnalyze(linePsi: KtFile): ReplLineAnalysisResult {
|
||||
override fun resetToLine(line: ReplCodeLine): List<ReplCodeLine> = resetToLine(line.no)
|
||||
|
||||
fun analyzeReplLine(psiFile: KtFile, codeLine: ReplCodeLine): ReplLineAnalysisResult {
|
||||
return stateLock.write {
|
||||
topDownAnalysisContext.scripts.clear()
|
||||
trace.clearDiagnostics()
|
||||
|
||||
psiFile.script!!.putUserData(ScriptPriorities.PRIORITY_KEY, codeLine.no)
|
||||
|
||||
doAnalyze(psiFile, codeLine)
|
||||
}
|
||||
}
|
||||
|
||||
private fun doAnalyze(linePsi: KtFile, codeLine: ReplCodeLine): ReplLineAnalysisResult {
|
||||
scriptDeclarationFactory.setDelegateFactory(FileBasedDeclarationProviderFactory(resolveSession.storageManager, listOf(linePsi)))
|
||||
replState.submitLine(linePsi)
|
||||
replState.submitLine(linePsi, codeLine)
|
||||
|
||||
val context = topDownAnalyzer.analyzeDeclarations(topDownAnalysisContext.topDownAnalysisMode, listOf(linePsi))
|
||||
|
||||
@@ -100,12 +124,12 @@ class CliReplAnalyzerEngine(environment: KotlinCoreEnvironment) {
|
||||
val diagnostics = trace.bindingContext.diagnostics
|
||||
val hasErrors = diagnostics.any { it.severity == Severity.ERROR }
|
||||
if (hasErrors) {
|
||||
replState.lineFailure(linePsi)
|
||||
replState.lineFailure(linePsi, codeLine)
|
||||
return ReplLineAnalysisResult.WithErrors(diagnostics)
|
||||
}
|
||||
else {
|
||||
val scriptDescriptor = context.scripts[linePsi.script]!!
|
||||
replState.lineSuccess(linePsi, scriptDescriptor)
|
||||
replState.lineSuccess(linePsi, codeLine, scriptDescriptor)
|
||||
return ReplLineAnalysisResult.Successful(scriptDescriptor, diagnostics)
|
||||
}
|
||||
|
||||
@@ -153,4 +177,60 @@ class CliReplAnalyzerEngine(environment: KotlinCoreEnvironment) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class ResetableReplState() {
|
||||
private val successfulLines = ReplHistory<LineInfo.SuccessfulLine>()
|
||||
private val submittedLines = hashMapOf<KtFile, LineInfo>()
|
||||
|
||||
fun resetToLine(lineNumber: Int): List<ReplCodeLine> {
|
||||
val removed = successfulLines.resetToLine(lineNumber)
|
||||
removed.forEach { submittedLines.remove(it.second.linePsi) }
|
||||
return removed.map { it.first }
|
||||
}
|
||||
|
||||
fun resetToLine(line: ReplCodeLine): List<ReplCodeLine> = resetToLine(line.no)
|
||||
|
||||
fun submitLine(ktFile: KtFile, codeLine: ReplCodeLine) {
|
||||
val line = LineInfo.SubmittedLine(ktFile, successfulLines.lastValue())
|
||||
submittedLines[ktFile] = line
|
||||
ktFile.fileScopesCustomizer = object : FileScopesCustomizer {
|
||||
override fun createFileScopes(fileScopeFactory: FileScopeFactory): FileScopes {
|
||||
return lineInfo(ktFile)?.let { computeFileScopes(it, fileScopeFactory) } ?: fileScopeFactory.createScopesForFile(ktFile)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun lineSuccess(ktFile: KtFile, codeLine: ReplCodeLine, scriptDescriptor: LazyScriptDescriptor) {
|
||||
val successfulLine = LineInfo.SuccessfulLine(ktFile, successfulLines.lastValue(), scriptDescriptor)
|
||||
submittedLines[ktFile] = successfulLine
|
||||
successfulLines.add(CompiledReplCodeLine(ktFile.name, codeLine), successfulLine)
|
||||
}
|
||||
|
||||
fun lineFailure(ktFile: KtFile, codeLine: ReplCodeLine) {
|
||||
submittedLines[ktFile] = LineInfo.FailedLine(ktFile, successfulLines.lastValue())
|
||||
}
|
||||
|
||||
private fun lineInfo(ktFile: KtFile) = submittedLines[ktFile]
|
||||
|
||||
// use sealed?
|
||||
private sealed class LineInfo {
|
||||
abstract val linePsi: KtFile
|
||||
abstract val parentLine: SuccessfulLine?
|
||||
|
||||
class SubmittedLine(override val linePsi: KtFile, override val parentLine: SuccessfulLine?) : LineInfo()
|
||||
class SuccessfulLine(override val linePsi: KtFile, override val parentLine: SuccessfulLine?, val lineDescriptor: LazyScriptDescriptor) : LineInfo()
|
||||
class FailedLine(override val linePsi: KtFile, override val parentLine: SuccessfulLine?) : LineInfo()
|
||||
}
|
||||
|
||||
private fun computeFileScopes(lineInfo: LineInfo, fileScopeFactory: FileScopeFactory): FileScopes? {
|
||||
// create scope that wraps previous line lexical scope and adds imports from this line
|
||||
val lexicalScopeAfterLastLine = lineInfo.parentLine?.lineDescriptor?.scopeForInitializerResolution ?: return null
|
||||
val lastLineImports = lexicalScopeAfterLastLine.parentsWithSelf.first { it is ImportingScope } as ImportingScope
|
||||
val scopesForThisLine = fileScopeFactory.createScopesForFile(lineInfo.linePsi, lastLineImports)
|
||||
val combinedLexicalScopes = lexicalScopeAfterLastLine.replaceImportingScopes(scopesForThisLine.importingScope)
|
||||
return FileScopes(combinedLexicalScopes, scopesForThisLine.importingScope, scopesForThisLine.importResolver)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
|
||||
package org.jetbrains.kotlin.cli.jvm.repl
|
||||
|
||||
|
||||
import com.intellij.openapi.Disposable
|
||||
import com.intellij.openapi.vfs.CharsetToolkit
|
||||
import com.intellij.psi.PsiFileFactory
|
||||
@@ -25,8 +26,8 @@ import org.jetbrains.kotlin.cli.common.CLIConfigurationKeys
|
||||
import org.jetbrains.kotlin.cli.common.messages.AnalyzerWithCompilerReport
|
||||
import org.jetbrains.kotlin.cli.common.messages.MessageCollector
|
||||
import org.jetbrains.kotlin.cli.common.repl.ReplCheckResult
|
||||
import org.jetbrains.kotlin.cli.common.repl.ReplChecker
|
||||
import org.jetbrains.kotlin.cli.common.repl.ReplCodeLine
|
||||
import org.jetbrains.kotlin.cli.common.repl.makeSriptBaseName
|
||||
import org.jetbrains.kotlin.cli.jvm.compiler.EnvironmentConfigFiles
|
||||
import org.jetbrains.kotlin.cli.jvm.compiler.KotlinCoreEnvironment
|
||||
import org.jetbrains.kotlin.cli.jvm.repl.messages.DiagnosticMessageHolder
|
||||
@@ -37,14 +38,18 @@ import org.jetbrains.kotlin.idea.KotlinLanguage
|
||||
import org.jetbrains.kotlin.parsing.KotlinParserDefinition
|
||||
import org.jetbrains.kotlin.psi.KtFile
|
||||
import org.jetbrains.kotlin.script.KotlinScriptDefinition
|
||||
import java.util.concurrent.locks.ReentrantReadWriteLock
|
||||
import kotlin.concurrent.read
|
||||
import kotlin.concurrent.write
|
||||
|
||||
open class GenericReplChecker(
|
||||
disposable: Disposable,
|
||||
val scriptDefinition: KotlinScriptDefinition,
|
||||
val compilerConfiguration: CompilerConfiguration,
|
||||
messageCollector: MessageCollector
|
||||
) : ReplChecker {
|
||||
protected val environment = run {
|
||||
messageCollector: MessageCollector,
|
||||
protected val stateLock: ReentrantReadWriteLock = ReentrantReadWriteLock()
|
||||
) {
|
||||
internal val environment = run {
|
||||
compilerConfiguration.apply {
|
||||
add(JVMConfigurationKeys.SCRIPT_DEFINITIONS, scriptDefinition)
|
||||
put<MessageCollector>(CLIConfigurationKeys.MESSAGE_COLLECTOR_KEY, messageCollector)
|
||||
@@ -53,41 +58,43 @@ open class GenericReplChecker(
|
||||
KotlinCoreEnvironment.createForProduction(disposable, compilerConfiguration, EnvironmentConfigFiles.JVM_CONFIG_FILES)
|
||||
}
|
||||
|
||||
protected val psiFileFactory: PsiFileFactoryImpl = PsiFileFactory.getInstance(environment.project) as PsiFileFactoryImpl
|
||||
private val psiFileFactory: PsiFileFactoryImpl = PsiFileFactory.getInstance(environment.project) as PsiFileFactoryImpl
|
||||
|
||||
// "line" - is the unit of evaluation here, could in fact consists of several character lines
|
||||
protected class LineState(
|
||||
internal class LineState(
|
||||
val codeLine: ReplCodeLine,
|
||||
val psiFile: KtFile,
|
||||
val errorHolder: DiagnosticMessageHolder)
|
||||
|
||||
protected var lineState: LineState? = null
|
||||
private var _lineState: LineState? = null
|
||||
|
||||
internal val lineState: LineState? get() = stateLock.read { _lineState }
|
||||
|
||||
fun createDiagnosticHolder() = ReplTerminalDiagnosticMessageHolder()
|
||||
|
||||
@Synchronized
|
||||
override fun check(codeLine: ReplCodeLine, history: List<ReplCodeLine>): ReplCheckResult {
|
||||
val virtualFile =
|
||||
LightVirtualFile("line${codeLine.no}${KotlinParserDefinition.STD_SCRIPT_EXT}", KotlinLanguage.INSTANCE, codeLine.code).apply {
|
||||
charset = CharsetToolkit.UTF8_CHARSET
|
||||
}
|
||||
val psiFile: KtFile = psiFileFactory.trySetupPsiForFile(virtualFile, KotlinLanguage.INSTANCE, true, false) as KtFile?
|
||||
?: error("Script file not analyzed at line ${codeLine.no}: ${codeLine.code}")
|
||||
fun check(codeLine: ReplCodeLine, generation: Long): ReplCheckResult {
|
||||
stateLock.write {
|
||||
val scriptFileName = makeSriptBaseName(codeLine, generation)
|
||||
val virtualFile =
|
||||
LightVirtualFile("${scriptFileName}${KotlinParserDefinition.STD_SCRIPT_EXT}", KotlinLanguage.INSTANCE, codeLine.code).apply {
|
||||
charset = CharsetToolkit.UTF8_CHARSET
|
||||
}
|
||||
val psiFile: KtFile = psiFileFactory.trySetupPsiForFile(virtualFile, KotlinLanguage.INSTANCE, true, false) as KtFile?
|
||||
?: error("Script file not analyzed at line ${codeLine.no}: ${codeLine.code}")
|
||||
|
||||
val errorHolder = createDiagnosticHolder()
|
||||
val errorHolder = createDiagnosticHolder()
|
||||
|
||||
val syntaxErrorReport = AnalyzerWithCompilerReport.Companion.reportSyntaxErrors(psiFile, errorHolder)
|
||||
val syntaxErrorReport = AnalyzerWithCompilerReport.reportSyntaxErrors(psiFile, errorHolder)
|
||||
|
||||
if (!syntaxErrorReport.isHasErrors) {
|
||||
lineState = LineState(codeLine, psiFile, errorHolder)
|
||||
}
|
||||
if (!syntaxErrorReport.isHasErrors) {
|
||||
_lineState = LineState(codeLine, psiFile, errorHolder)
|
||||
}
|
||||
|
||||
return when {
|
||||
syntaxErrorReport.isHasErrors && syntaxErrorReport.isAllErrorsAtEof -> ReplCheckResult.Incomplete(history)
|
||||
syntaxErrorReport.isHasErrors -> ReplCheckResult.Error(history, errorHolder.renderedDiagnostics)
|
||||
else -> ReplCheckResult.Ok(history)
|
||||
return when {
|
||||
syntaxErrorReport.isHasErrors && syntaxErrorReport.isAllErrorsAtEof -> ReplCheckResult.Incomplete()
|
||||
syntaxErrorReport.isHasErrors -> ReplCheckResult.Error(errorHolder.renderedDiagnostics)
|
||||
else -> ReplCheckResult.Ok()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
|
||||
package org.jetbrains.kotlin.cli.jvm.repl
|
||||
|
||||
|
||||
import com.intellij.openapi.Disposable
|
||||
import org.jetbrains.kotlin.cli.common.messages.AnalyzerWithCompilerReport
|
||||
import org.jetbrains.kotlin.cli.common.messages.MessageCollector
|
||||
@@ -29,78 +30,115 @@ import org.jetbrains.kotlin.descriptors.ScriptDescriptor
|
||||
import org.jetbrains.kotlin.script.KotlinScriptDefinition
|
||||
import org.jetbrains.kotlin.script.KotlinScriptExternalDependencies
|
||||
import java.io.File
|
||||
import java.util.concurrent.atomic.AtomicLong
|
||||
import java.util.concurrent.locks.ReentrantReadWriteLock
|
||||
import kotlin.concurrent.read
|
||||
import kotlin.concurrent.write
|
||||
|
||||
open class GenericReplCompiler(
|
||||
disposable: Disposable,
|
||||
scriptDefinition: KotlinScriptDefinition,
|
||||
compilerConfiguration: CompilerConfiguration,
|
||||
messageCollector: MessageCollector
|
||||
) : ReplCompiler, GenericReplChecker(disposable, scriptDefinition, compilerConfiguration, messageCollector) {
|
||||
private val analyzerEngine = CliReplAnalyzerEngine(environment)
|
||||
open class GenericReplCompiler(disposable: Disposable,
|
||||
protected val scriptDefinition: KotlinScriptDefinition,
|
||||
protected val compilerConfiguration: CompilerConfiguration,
|
||||
messageCollector: MessageCollector,
|
||||
protected val stateLock: ReentrantReadWriteLock = ReentrantReadWriteLock()) : ReplCompiler {
|
||||
private val checker = GenericReplChecker(disposable, scriptDefinition, compilerConfiguration, messageCollector, stateLock)
|
||||
|
||||
private val analyzerEngine = GenericReplAnalyzer(checker.environment, stateLock)
|
||||
|
||||
private var lastDependencies: KotlinScriptExternalDependencies? = null
|
||||
|
||||
private val descriptorsHistory = ReplHistory<ScriptDescriptor>()
|
||||
|
||||
@Synchronized
|
||||
override fun compile(codeLine: ReplCodeLine, history: List<ReplCodeLine>): ReplCompileResult {
|
||||
checkAndUpdateReplHistoryCollection(descriptorsHistory, history)?.let {
|
||||
return@compile ReplCompileResult.HistoryMismatch(descriptorsHistory.lines, it)
|
||||
}
|
||||
private val generation = AtomicLong(1)
|
||||
|
||||
val (psiFile, errorHolder) = run {
|
||||
if (lineState == null || lineState!!.codeLine != codeLine) {
|
||||
val res = check(codeLine, history)
|
||||
when (res) {
|
||||
is ReplCheckResult.Incomplete -> return@compile ReplCompileResult.Incomplete(res.updatedHistory)
|
||||
is ReplCheckResult.Error -> return@compile ReplCompileResult.Error(res.updatedHistory, res.message, res.location)
|
||||
is ReplCheckResult.Ok -> {} // continue
|
||||
override fun resetToLine(lineNumber: Int): List<ReplCodeLine> {
|
||||
return stateLock.write {
|
||||
generation.incrementAndGet()
|
||||
val removedCompiledLines = descriptorsHistory.resetToLine(lineNumber)
|
||||
val removedAnalyzedLines = analyzerEngine.resetToLine(lineNumber)
|
||||
|
||||
removedCompiledLines.zip(removedAnalyzedLines).forEach {
|
||||
if (it.first.first != it.second) {
|
||||
throw IllegalStateException("History mistmatch when resetting lines")
|
||||
}
|
||||
}
|
||||
Pair(lineState!!.psiFile, lineState!!.errorHolder)
|
||||
|
||||
removedCompiledLines
|
||||
}.map { it.first }
|
||||
}
|
||||
|
||||
override val history: List<ReplCodeLine> get() = stateLock.read { descriptorsHistory.copySources() }
|
||||
|
||||
override fun check(codeLine: ReplCodeLine): ReplCheckResult {
|
||||
return checker.check(codeLine, generation.get())
|
||||
}
|
||||
|
||||
override fun compile(codeLine: ReplCodeLine, verifyHistory: List<ReplCodeLine>?): ReplCompileResult {
|
||||
stateLock.write {
|
||||
val firstMismatch = descriptorsHistory.firstMismatchingHistory(verifyHistory)
|
||||
if (firstMismatch != null) {
|
||||
return@compile ReplCompileResult.HistoryMismatch(descriptorsHistory.copySources(), firstMismatch)
|
||||
}
|
||||
|
||||
val currentGeneration = generation.get()
|
||||
|
||||
val (psiFile, errorHolder) = run {
|
||||
if (checker.lineState == null || checker.lineState!!.codeLine != codeLine) {
|
||||
val res = checker.check(codeLine, currentGeneration)
|
||||
when (res) {
|
||||
is ReplCheckResult.Incomplete -> return@compile ReplCompileResult.Incomplete(descriptorsHistory.copySources())
|
||||
is ReplCheckResult.Error -> return@compile ReplCompileResult.Error(descriptorsHistory.copySources(), res.message, res.location)
|
||||
is ReplCheckResult.Ok -> NO_ACTION()
|
||||
}
|
||||
}
|
||||
Pair(checker.lineState!!.psiFile, checker.lineState!!.errorHolder)
|
||||
}
|
||||
|
||||
val newDependencies = scriptDefinition.getDependenciesFor(psiFile, checker.environment.project, lastDependencies)
|
||||
var classpathAddendum: List<File>? = null
|
||||
if (lastDependencies != newDependencies) {
|
||||
lastDependencies = newDependencies
|
||||
classpathAddendum = newDependencies?.let { checker.environment.updateClasspath(it.classpath.map(::JvmClasspathRoot)) }
|
||||
}
|
||||
|
||||
val analysisResult = analyzerEngine.analyzeReplLine(psiFile, codeLine)
|
||||
AnalyzerWithCompilerReport.reportDiagnostics(analysisResult.diagnostics, errorHolder)
|
||||
val scriptDescriptor = when (analysisResult) {
|
||||
is GenericReplAnalyzer.ReplLineAnalysisResult.WithErrors -> return ReplCompileResult.Error(descriptorsHistory.copySources(), errorHolder.renderedDiagnostics)
|
||||
is GenericReplAnalyzer.ReplLineAnalysisResult.Successful -> analysisResult.scriptDescriptor
|
||||
else -> error("Unexpected result ${analysisResult.javaClass}")
|
||||
}
|
||||
|
||||
val state = GenerationState(
|
||||
psiFile.project,
|
||||
ClassBuilderFactories.binaries(false),
|
||||
analyzerEngine.module,
|
||||
analyzerEngine.trace.bindingContext,
|
||||
listOf(psiFile),
|
||||
compilerConfiguration
|
||||
)
|
||||
state.replSpecific.scriptResultFieldName = SCRIPT_RESULT_FIELD_NAME
|
||||
state.replSpecific.earlierScriptsForReplInterpreter = descriptorsHistory.copyValues()
|
||||
state.beforeCompile()
|
||||
KotlinCodegenFacade.generatePackage(
|
||||
state,
|
||||
psiFile.script!!.getContainingKtFile().packageFqName,
|
||||
setOf(psiFile.script!!.getContainingKtFile()),
|
||||
org.jetbrains.kotlin.codegen.CompilationErrorHandler.THROW_EXCEPTION)
|
||||
|
||||
val generatedClassname = makeSriptBaseName(codeLine, currentGeneration)
|
||||
val compiledCodeLine = CompiledReplCodeLine(generatedClassname, codeLine)
|
||||
descriptorsHistory.add(compiledCodeLine, scriptDescriptor)
|
||||
|
||||
return ReplCompileResult.CompiledClasses(descriptorsHistory.copySources(),
|
||||
compiledCodeLine,
|
||||
generatedClassname,
|
||||
state.factory.asList().map { CompiledClassData(it.relativePath, it.asByteArray()) },
|
||||
state.replSpecific.hasResult,
|
||||
classpathAddendum ?: emptyList())
|
||||
}
|
||||
|
||||
val newDependencies = scriptDefinition.getDependenciesFor(psiFile, environment.project, lastDependencies)
|
||||
var classpathAddendum: List<File>? = null
|
||||
if (lastDependencies != newDependencies) {
|
||||
lastDependencies = newDependencies
|
||||
classpathAddendum = newDependencies?.let { environment.updateClasspath(it.classpath.map(::JvmClasspathRoot)) }
|
||||
}
|
||||
|
||||
val analysisResult = analyzerEngine.analyzeReplLine(psiFile, codeLine.no)
|
||||
AnalyzerWithCompilerReport.Companion.reportDiagnostics(analysisResult.diagnostics, errorHolder)
|
||||
val scriptDescriptor = when (analysisResult) {
|
||||
is CliReplAnalyzerEngine.ReplLineAnalysisResult.WithErrors -> return ReplCompileResult.Error(descriptorsHistory.lines, errorHolder.renderedDiagnostics)
|
||||
is CliReplAnalyzerEngine.ReplLineAnalysisResult.Successful -> analysisResult.scriptDescriptor
|
||||
else -> error("Unexpected result ${analysisResult.javaClass}")
|
||||
}
|
||||
|
||||
val state = GenerationState(
|
||||
psiFile.project,
|
||||
ClassBuilderFactories.binaries(false),
|
||||
analyzerEngine.module,
|
||||
analyzerEngine.trace.bindingContext,
|
||||
listOf(psiFile),
|
||||
compilerConfiguration
|
||||
)
|
||||
state.replSpecific.scriptResultFieldName = SCRIPT_RESULT_FIELD_NAME
|
||||
state.replSpecific.earlierScriptsForReplInterpreter = descriptorsHistory.values
|
||||
state.beforeCompile()
|
||||
KotlinCodegenFacade.generatePackage(
|
||||
state,
|
||||
psiFile.script!!.getContainingKtFile().packageFqName,
|
||||
setOf(psiFile.script!!.getContainingKtFile()),
|
||||
org.jetbrains.kotlin.codegen.CompilationErrorHandler.THROW_EXCEPTION)
|
||||
|
||||
descriptorsHistory.add(codeLine, scriptDescriptor)
|
||||
|
||||
return ReplCompileResult.CompiledClasses(descriptorsHistory.lines,
|
||||
state.factory.asList().map { CompiledClassData(it.relativePath, it.asByteArray()) },
|
||||
state.replSpecific.hasResult,
|
||||
classpathAddendum ?: emptyList())
|
||||
}
|
||||
|
||||
companion object {
|
||||
private val SCRIPT_RESULT_FIELD_NAME = "\$\$result"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -24,11 +24,10 @@ import com.intellij.psi.impl.PsiFileFactoryImpl
|
||||
import com.intellij.testFramework.LightVirtualFile
|
||||
import org.jetbrains.kotlin.cli.common.messages.AnalyzerWithCompilerReport
|
||||
import org.jetbrains.kotlin.cli.common.repl.ReplClassLoader
|
||||
import org.jetbrains.kotlin.cli.common.repl.ReplCodeLine
|
||||
import org.jetbrains.kotlin.cli.jvm.compiler.EnvironmentConfigFiles
|
||||
import org.jetbrains.kotlin.cli.jvm.compiler.KotlinCoreEnvironment
|
||||
import org.jetbrains.kotlin.cli.jvm.config.jvmClasspathRoots
|
||||
import org.jetbrains.kotlin.cli.jvm.repl.CliReplAnalyzerEngine.ReplLineAnalysisResult.Successful
|
||||
import org.jetbrains.kotlin.cli.jvm.repl.CliReplAnalyzerEngine.ReplLineAnalysisResult.WithErrors
|
||||
import org.jetbrains.kotlin.codegen.ClassBuilderFactories
|
||||
import org.jetbrains.kotlin.codegen.CompilationErrorHandler
|
||||
import org.jetbrains.kotlin.codegen.KotlinCodegenFacade
|
||||
@@ -65,7 +64,7 @@ class ReplInterpreter(
|
||||
}
|
||||
|
||||
private val psiFileFactory: PsiFileFactoryImpl = PsiFileFactory.getInstance(environment.project) as PsiFileFactoryImpl
|
||||
private val analyzerEngine = CliReplAnalyzerEngine(environment)
|
||||
private val analyzerEngine = GenericReplAnalyzer(environment)
|
||||
|
||||
fun eval(line: String): LineResult {
|
||||
++lineNumber
|
||||
@@ -99,11 +98,11 @@ class ReplInterpreter(
|
||||
return LineResult.Error.CompileTime(errorHolder.renderedDiagnostics)
|
||||
}
|
||||
|
||||
val analysisResult = analyzerEngine.analyzeReplLine(psiFile, lineNumber)
|
||||
val analysisResult = analyzerEngine.analyzeReplLine(psiFile, ReplCodeLine(lineNumber, "fake line"))
|
||||
AnalyzerWithCompilerReport.reportDiagnostics(analysisResult.diagnostics, errorHolder)
|
||||
val scriptDescriptor = when (analysisResult) {
|
||||
is WithErrors -> return LineResult.Error.CompileTime(errorHolder.renderedDiagnostics)
|
||||
is Successful -> analysisResult.scriptDescriptor
|
||||
is GenericReplAnalyzer.ReplLineAnalysisResult.WithErrors -> return LineResult.Error.CompileTime(errorHolder.renderedDiagnostics)
|
||||
is GenericReplAnalyzer.ReplLineAnalysisResult.Successful -> analysisResult.scriptDescriptor
|
||||
else -> error("Unexpected result ${analysisResult.javaClass}")
|
||||
}
|
||||
|
||||
|
||||
+30
-16
@@ -35,15 +35,15 @@ open class KotlinRemoteReplClientBase(
|
||||
targetPlatform: CompileService.TargetPlatform,
|
||||
templateClasspath: List<File>,
|
||||
templateClassName: String,
|
||||
scriptArgs: Array<Any?>? = null,
|
||||
scriptArgsTypes: Array<Class<*>>? = null,
|
||||
scriptArgs: Array<out Any?>? = null,
|
||||
scriptArgsTypes: Array<out Class<out Any>>? = null,
|
||||
compilerMessagesOutputStream: OutputStream = System.err,
|
||||
evalOutputStream: OutputStream? = null,
|
||||
evalErrorStream: OutputStream? = null,
|
||||
evalInputStream: InputStream? = null,
|
||||
port: Int = SOCKET_ANY_FREE_PORT,
|
||||
operationsTracer: RemoteOperationsTracer? = null
|
||||
) : ReplChecker {
|
||||
) {
|
||||
|
||||
val sessionId = compileService.leaseReplSession(
|
||||
clientAliveFlagFile?.absolutePath,
|
||||
@@ -70,10 +70,6 @@ open class KotlinRemoteReplClientBase(
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
override fun check(codeLine: ReplCodeLine, history: List<ReplCodeLine>): ReplCheckResult {
|
||||
return compileService.remoteReplLineCheck(sessionId, codeLine, history).get()
|
||||
}
|
||||
}
|
||||
|
||||
class KotlinRemoteReplCompiler(
|
||||
@@ -99,10 +95,20 @@ class KotlinRemoteReplCompiler(
|
||||
evalInputStream = null,
|
||||
port = port,
|
||||
operationsTracer = operationsTracer
|
||||
), ReplCompiler {
|
||||
), ReplCompiler, ReplCheckAction {
|
||||
override fun resetToLine(lineNumber: Int): List<ReplCodeLine> {
|
||||
return emptyList() // TODO: not implemented, no current need
|
||||
}
|
||||
|
||||
override fun compile(codeLine: ReplCodeLine, history: List<ReplCodeLine>): ReplCompileResult {
|
||||
return compileService.remoteReplLineCompile(sessionId, codeLine, history).get()
|
||||
override val history: List<ReplCodeLine>
|
||||
get() = emptyList() // TODO: not implemented, no current need
|
||||
|
||||
override fun check(codeLine: ReplCodeLine): ReplCheckResult {
|
||||
return compileService.remoteReplLineCheck(sessionId, codeLine).get()
|
||||
}
|
||||
|
||||
override fun compile(codeLine: ReplCodeLine, verifyHistory: List<ReplCodeLine>?): ReplCompileResult {
|
||||
return compileService.remoteReplLineCompile(sessionId, codeLine, verifyHistory).get()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -114,8 +120,8 @@ class KotlinRemoteReplEvaluator(
|
||||
targetPlatform: CompileService.TargetPlatform,
|
||||
templateClasspath: List<File>,
|
||||
templateClassName: String,
|
||||
scriptArgs: Array<Any?>?,
|
||||
scriptArgsTypes: Array<Class<*>>?,
|
||||
scriptArgs: Array<out Any?>? = null,
|
||||
scriptArgsTypes: Array<out Class<out Any>>? = null,
|
||||
compilerMessagesOutputStream: OutputStream,
|
||||
evalOutputStream: OutputStream?,
|
||||
evalErrorStream: OutputStream?,
|
||||
@@ -137,12 +143,20 @@ class KotlinRemoteReplEvaluator(
|
||||
evalInputStream = evalInputStream,
|
||||
port = port,
|
||||
operationsTracer = operationsTracer
|
||||
), ReplEvaluator {
|
||||
), ReplAtomicEvalAction, ReplEvaluatorExposedInternalHistory, ReplCheckAction {
|
||||
|
||||
override val lastEvaluatedScript: ClassWithInstance? = null // not implemented, no need so far
|
||||
override val lastEvaluatedScripts: List<EvalHistoryType> = emptyList() // not implemented, no need so far
|
||||
|
||||
// TODO: invokeWrapper is ignored here, and in the daemon the session wrapper is used instead; So consider to make it per call (avoid performance penalties though)
|
||||
override fun eval(codeLine: ReplCodeLine, history: List<ReplCodeLine>, invokeWrapper: InvokeWrapper?): ReplEvalResult {
|
||||
return compileService.remoteReplLineEval(sessionId, codeLine, history).get()
|
||||
// TODO: scriptArgs are ignored here, they should be passed through
|
||||
override fun compileAndEval(codeLine: ReplCodeLine,
|
||||
scriptArgs: ScriptArgsWithTypes?,
|
||||
verifyHistory: List<ReplCodeLine>?,
|
||||
invokeWrapper: InvokeWrapper?): ReplEvalResult {
|
||||
return compileService.remoteReplLineEval(sessionId, codeLine, verifyHistory).get()
|
||||
}
|
||||
|
||||
override fun check(codeLine: ReplCodeLine): ReplCheckResult {
|
||||
return compileService.remoteReplLineCheck(sessionId, codeLine).get()
|
||||
}
|
||||
}
|
||||
|
||||
+9
-8
@@ -16,8 +16,10 @@
|
||||
|
||||
package org.jetbrains.kotlin.daemon.common
|
||||
|
||||
import org.jetbrains.kotlin.cli.common.arguments.CommonCompilerArguments
|
||||
import org.jetbrains.kotlin.cli.common.repl.*
|
||||
import org.jetbrains.kotlin.cli.common.repl.ReplCheckResult
|
||||
import org.jetbrains.kotlin.cli.common.repl.ReplCodeLine
|
||||
import org.jetbrains.kotlin.cli.common.repl.ReplCompileResult
|
||||
import org.jetbrains.kotlin.cli.common.repl.ReplEvalResult
|
||||
import java.io.File
|
||||
import java.io.Serializable
|
||||
import java.rmi.Remote
|
||||
@@ -148,8 +150,8 @@ interface CompileService : Remote {
|
||||
servicesFacade: CompilerCallbackServicesFacade,
|
||||
templateClasspath: List<File>,
|
||||
templateClassName: String,
|
||||
scriptArgs: Array<Any?>?,
|
||||
scriptArgsTypes: Array<Class<*>>?,
|
||||
scriptArgs: Array<out Any?>?,
|
||||
scriptArgsTypes: Array<out Class<out Any>>?,
|
||||
compilerMessagesOutputStream: RemoteOutputStream,
|
||||
evalOutputStream: RemoteOutputStream?,
|
||||
evalErrorStream: RemoteOutputStream?,
|
||||
@@ -163,21 +165,20 @@ interface CompileService : Remote {
|
||||
@Throws(RemoteException::class)
|
||||
fun remoteReplLineCheck(
|
||||
sessionId: Int,
|
||||
codeLine: ReplCodeLine,
|
||||
history: List<ReplCodeLine>
|
||||
codeLine: ReplCodeLine
|
||||
): CallResult<ReplCheckResult>
|
||||
|
||||
@Throws(RemoteException::class)
|
||||
fun remoteReplLineCompile(
|
||||
sessionId: Int,
|
||||
codeLine: ReplCodeLine,
|
||||
history: List<ReplCodeLine>
|
||||
history: List<ReplCodeLine>?
|
||||
): CallResult<ReplCompileResult>
|
||||
|
||||
@Throws(RemoteException::class)
|
||||
fun remoteReplLineEval(
|
||||
sessionId: Int,
|
||||
codeLine: ReplCodeLine,
|
||||
history: List<ReplCodeLine>
|
||||
history: List<ReplCodeLine>?
|
||||
): CallResult<ReplEvalResult>
|
||||
}
|
||||
|
||||
@@ -23,29 +23,36 @@ import com.intellij.openapi.vfs.impl.jar.CoreJarFileSystem
|
||||
import org.jetbrains.kotlin.cli.common.CLICompiler
|
||||
import org.jetbrains.kotlin.cli.common.ExitCode
|
||||
import org.jetbrains.kotlin.cli.common.KOTLIN_COMPILER_ENVIRONMENT_KEEPALIVE_PROPERTY
|
||||
import org.jetbrains.kotlin.cli.common.arguments.*
|
||||
import org.jetbrains.kotlin.cli.common.arguments.CommonCompilerArguments
|
||||
import org.jetbrains.kotlin.cli.common.arguments.K2JSCompilerArguments
|
||||
import org.jetbrains.kotlin.cli.common.arguments.K2JVMCompilerArguments
|
||||
import org.jetbrains.kotlin.cli.common.arguments.K2MetadataCompilerArguments
|
||||
import org.jetbrains.kotlin.cli.common.messages.*
|
||||
import org.jetbrains.kotlin.cli.common.modules.ModuleXmlParser
|
||||
import org.jetbrains.kotlin.cli.common.repl.ReplCheckResult
|
||||
import org.jetbrains.kotlin.cli.common.repl.ReplCodeLine
|
||||
import org.jetbrains.kotlin.cli.common.repl.ReplCompileResult
|
||||
import org.jetbrains.kotlin.cli.common.repl.ReplEvalResult
|
||||
import org.jetbrains.kotlin.cli.common.repl.*
|
||||
import org.jetbrains.kotlin.cli.js.K2JSCompiler
|
||||
import org.jetbrains.kotlin.cli.jvm.K2JVMCompiler
|
||||
import org.jetbrains.kotlin.cli.jvm.compiler.KotlinCoreEnvironment
|
||||
import org.jetbrains.kotlin.cli.metadata.K2MetadataCompiler
|
||||
import org.jetbrains.kotlin.config.IncrementalCompilation
|
||||
import org.jetbrains.kotlin.config.Services
|
||||
import org.jetbrains.kotlin.daemon.common.*
|
||||
import org.jetbrains.kotlin.daemon.incremental.*
|
||||
import org.jetbrains.kotlin.daemon.report.*
|
||||
import org.jetbrains.kotlin.daemon.incremental.RemoteAnnotationsFileUpdater
|
||||
import org.jetbrains.kotlin.daemon.incremental.RemoteArtifactChangesProvider
|
||||
import org.jetbrains.kotlin.daemon.incremental.RemoteChangesRegostry
|
||||
import org.jetbrains.kotlin.daemon.report.CompileServicesFacadeMessageCollector
|
||||
import org.jetbrains.kotlin.daemon.report.DaemonMessageReporter
|
||||
import org.jetbrains.kotlin.daemon.report.DaemonMessageReporterPrintStreamAdapter
|
||||
import org.jetbrains.kotlin.daemon.report.RemoteICReporter
|
||||
import org.jetbrains.kotlin.incremental.*
|
||||
import org.jetbrains.kotlin.load.kotlin.incremental.components.IncrementalCompilationComponents
|
||||
import org.jetbrains.kotlin.modules.Module
|
||||
import org.jetbrains.kotlin.progress.CompilationCanceledStatus
|
||||
import org.jetbrains.kotlin.utils.addToStdlib.check
|
||||
import org.jetbrains.kotlin.utils.stackTraceStr
|
||||
import java.io.*
|
||||
import java.io.BufferedOutputStream
|
||||
import java.io.ByteArrayOutputStream
|
||||
import java.io.File
|
||||
import java.io.PrintStream
|
||||
import java.rmi.NoSuchObjectException
|
||||
import java.rmi.registry.Registry
|
||||
import java.rmi.server.UnicastRemoteObject
|
||||
@@ -445,8 +452,8 @@ class CompileServiceImpl(
|
||||
servicesFacade: CompilerCallbackServicesFacade,
|
||||
templateClasspath: List<File>,
|
||||
templateClassName: String,
|
||||
scriptArgs: Array<Any?>?,
|
||||
scriptArgsTypes: Array<Class<*>>?,
|
||||
scriptArgs: Array<out Any?>?,
|
||||
scriptArgsTypes: Array<out Class<out Any>>?,
|
||||
compilerMessagesOutputStream: RemoteOutputStream,
|
||||
evalOutputStream: RemoteOutputStream?,
|
||||
evalErrorStream: RemoteOutputStream?,
|
||||
@@ -457,7 +464,9 @@ class CompileServiceImpl(
|
||||
CompileService.CallResult.Error("Sorry, only JVM target platform is supported now")
|
||||
else {
|
||||
val disposable = Disposer.newDisposable()
|
||||
val repl = KotlinJvmReplService(disposable, templateClasspath, templateClassName, scriptArgs, scriptArgsTypes, compilerMessagesOutputStream, operationsTracer)
|
||||
val repl = KotlinJvmReplService(disposable, templateClasspath, templateClassName,
|
||||
scriptArgs?.let { ScriptArgsWithTypes(it, scriptArgsTypes?.map { it.kotlin }?.toTypedArray() ?: emptyArray()) },
|
||||
compilerMessagesOutputStream, operationsTracer)
|
||||
val sessionId = state.sessions.leaseSession(ClientOrSessionProxy(aliveFlagPath, repl, disposable))
|
||||
|
||||
CompileService.CallResult.Good(sessionId)
|
||||
@@ -467,14 +476,14 @@ class CompileServiceImpl(
|
||||
// TODO: add more checks (e.g. is it a repl session)
|
||||
override fun releaseReplSession(sessionId: Int): CompileService.CallResult<Nothing> = releaseCompileSession(sessionId)
|
||||
|
||||
override fun remoteReplLineCheck(sessionId: Int, codeLine: ReplCodeLine, history: List<ReplCodeLine>): CompileService.CallResult<ReplCheckResult> =
|
||||
override fun remoteReplLineCheck(sessionId: Int, codeLine: ReplCodeLine): CompileService.CallResult<ReplCheckResult> =
|
||||
ifAlive(minAliveness = Aliveness.Alive) {
|
||||
withValidRepl(sessionId) {
|
||||
check(codeLine, history)
|
||||
check(codeLine)
|
||||
}
|
||||
}
|
||||
|
||||
override fun remoteReplLineCompile(sessionId: Int, codeLine: ReplCodeLine, history: List<ReplCodeLine>): CompileService.CallResult<ReplCompileResult> =
|
||||
override fun remoteReplLineCompile(sessionId: Int, codeLine: ReplCodeLine, history: List<ReplCodeLine>?): CompileService.CallResult<ReplCompileResult> =
|
||||
ifAlive(minAliveness = Aliveness.Alive) {
|
||||
withValidRepl(sessionId) {
|
||||
compile(codeLine, history)
|
||||
@@ -484,11 +493,11 @@ class CompileServiceImpl(
|
||||
override fun remoteReplLineEval(
|
||||
sessionId: Int,
|
||||
codeLine: ReplCodeLine,
|
||||
history: List<ReplCodeLine>
|
||||
history: List<ReplCodeLine>?
|
||||
): CompileService.CallResult<ReplEvalResult> =
|
||||
ifAlive(minAliveness = Aliveness.Alive) {
|
||||
withValidRepl(sessionId) {
|
||||
eval(codeLine, history)
|
||||
compileAndEval(codeLine, verifyHistory = history)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -22,7 +22,6 @@ import org.jetbrains.kotlin.cli.common.repl.*
|
||||
import org.jetbrains.kotlin.cli.jvm.config.addJvmClasspathRoots
|
||||
import org.jetbrains.kotlin.cli.jvm.config.jvmClasspathRoots
|
||||
import org.jetbrains.kotlin.cli.jvm.repl.GenericReplCompiler
|
||||
import org.jetbrains.kotlin.cli.jvm.repl.compileAndEval
|
||||
import org.jetbrains.kotlin.config.CommonConfigurationKeys
|
||||
import org.jetbrains.kotlin.config.CompilerConfiguration
|
||||
import org.jetbrains.kotlin.daemon.common.DummyProfiler
|
||||
@@ -40,12 +39,10 @@ open class KotlinJvmReplService(
|
||||
disposable: Disposable,
|
||||
templateClasspath: List<File>,
|
||||
templateClassName: String,
|
||||
scriptArgs: Array<Any?>?,
|
||||
scriptArgsTypes: Array<Class<*>>?,
|
||||
protected val fallbackScriptArgs: ScriptArgsWithTypes?,
|
||||
compilerOutputStreamProxy: RemoteOutputStream,
|
||||
val operationsTracer: RemoteOperationsTracer?
|
||||
) : ReplCompiler, ReplEvaluator {
|
||||
|
||||
protected val operationsTracer: RemoteOperationsTracer?
|
||||
) : ReplCompileAction, ReplAtomicEvalAction, ReplCheckAction, ReplEvaluatorExposedInternalHistory {
|
||||
protected val compilerMessagesStream = PrintStream(BufferedOutputStream(RemoteOutputStreamClient(compilerOutputStreamProxy, DummyProfiler()), REMOTE_STREAM_BUFFER_SIZE))
|
||||
|
||||
protected class KeepFirstErrorMessageCollector(compilerMessagesStream: PrintStream) : MessageCollector {
|
||||
@@ -105,23 +102,24 @@ open class KotlinJvmReplService(
|
||||
|
||||
private val scriptDef = makeScriptDefinition(templateClasspath, templateClassName)
|
||||
|
||||
private val replCompiler : GenericReplCompiler? by lazy {
|
||||
private val replCompiler: ReplCompiler? by lazy {
|
||||
if (scriptDef == null) null
|
||||
else GenericReplCompiler(disposable, scriptDef, configuration, messageCollector)
|
||||
}
|
||||
|
||||
private val compiledEvaluator : GenericReplCompiledEvaluator by lazy {
|
||||
GenericReplCompiledEvaluator(configuration.jvmClasspathRoots, null, scriptArgs, scriptArgsTypes)
|
||||
private val replEvaluator: ReplFullEvaluator? by lazy {
|
||||
replCompiler?.let { compiler ->
|
||||
GenericReplCompilingEvaluator(compiler, configuration.jvmClasspathRoots, null, fallbackScriptArgs, ReplRepeatingMode.NONE)
|
||||
}
|
||||
}
|
||||
|
||||
override val lastEvaluatedScript: ClassWithInstance? get() = compiledEvaluator.lastEvaluatedScript
|
||||
override val lastEvaluatedScripts: List<EvalHistoryType> get() = replEvaluator?.lastEvaluatedScripts ?: emptyList()
|
||||
|
||||
override fun check(codeLine: ReplCodeLine, history: List<ReplCodeLine>): ReplCheckResult {
|
||||
override fun check(codeLine: ReplCodeLine): ReplCheckResult {
|
||||
operationsTracer?.before("check")
|
||||
try {
|
||||
return replCompiler?.check(codeLine, history)
|
||||
?: ReplCheckResult.Error(history,
|
||||
messageCollector.firstErrorMessage ?: "Unknown error",
|
||||
return replCompiler?.check(codeLine)
|
||||
?: ReplCheckResult.Error(messageCollector.firstErrorMessage ?: "Unknown error",
|
||||
messageCollector.firstErrorLocation ?: CompilerMessageLocation.NO_LOCATION)
|
||||
}
|
||||
finally {
|
||||
@@ -129,11 +127,11 @@ open class KotlinJvmReplService(
|
||||
}
|
||||
}
|
||||
|
||||
override fun compile(codeLine: ReplCodeLine, history: List<ReplCodeLine>): ReplCompileResult {
|
||||
override fun compile(codeLine: ReplCodeLine, verifyHistory: List<ReplCodeLine>?): ReplCompileResult {
|
||||
operationsTracer?.before("compile")
|
||||
try {
|
||||
return replCompiler?.compile(codeLine, history)
|
||||
?: ReplCompileResult.Error(history,
|
||||
return replCompiler?.compile(codeLine, verifyHistory)
|
||||
?: ReplCompileResult.Error(verifyHistory ?: emptyList(),
|
||||
messageCollector.firstErrorMessage ?: "Unknown error",
|
||||
messageCollector.firstErrorLocation ?: CompilerMessageLocation.NO_LOCATION)
|
||||
}
|
||||
@@ -142,13 +140,13 @@ open class KotlinJvmReplService(
|
||||
}
|
||||
}
|
||||
|
||||
override fun eval(codeLine: ReplCodeLine, history: List<ReplCodeLine>, invokeWrapper: InvokeWrapper?): ReplEvalResult = synchronized(this) {
|
||||
override fun compileAndEval(codeLine: ReplCodeLine, scriptArgs: ScriptArgsWithTypes?, verifyHistory: List<ReplCodeLine>?, invokeWrapper: InvokeWrapper?): ReplEvalResult {
|
||||
operationsTracer?.before("eval")
|
||||
try {
|
||||
return replCompiler?.let { compileAndEval(it, compiledEvaluator, codeLine, history, invokeWrapper) }
|
||||
?: ReplEvalResult.Error.CompileTime(history,
|
||||
messageCollector.firstErrorMessage ?: "Unknown error",
|
||||
messageCollector.firstErrorLocation ?: CompilerMessageLocation.NO_LOCATION)
|
||||
return replEvaluator?.compileAndEval(codeLine, scriptArgs ?: fallbackScriptArgs, verifyHistory, invokeWrapper)
|
||||
?: ReplEvalResult.Error.CompileTime(verifyHistory ?: replEvaluator?.history ?: emptyList(),
|
||||
messageCollector.firstErrorMessage ?: "Unknown error",
|
||||
messageCollector.firstErrorLocation ?: CompilerMessageLocation.NO_LOCATION)
|
||||
}
|
||||
finally {
|
||||
operationsTracer?.after("eval")
|
||||
|
||||
@@ -35,7 +35,6 @@ import java.io.File
|
||||
import java.net.URLClassLoader
|
||||
|
||||
class GenericReplTest : TestCase() {
|
||||
|
||||
@Test
|
||||
fun testReplBasics() {
|
||||
|
||||
@@ -45,7 +44,7 @@ class GenericReplTest : TestCase() {
|
||||
listOf(File(KotlinIntegrationTestBase.getCompilerLib(), "kotlin-runtime.jar")),
|
||||
"kotlin.script.templates.standard.ScriptTemplateWithArgs")
|
||||
|
||||
val res1 = repl.replCompiler?.check(ReplCodeLine(0, "val x ="), emptyList())
|
||||
val res1 = repl.replCompiler?.check(ReplCodeLine(0, "val x ="))
|
||||
TestCase.assertTrue("Unexpected check results: $res1", res1 is ReplCheckResult.Incomplete)
|
||||
|
||||
val codeLine0 = ReplCodeLine(0, "val l1 = listOf(1 + 2)\nl1.first()")
|
||||
@@ -53,7 +52,7 @@ class GenericReplTest : TestCase() {
|
||||
val res2c = res2 as? ReplCompileResult.CompiledClasses
|
||||
TestCase.assertNotNull("Unexpected compile result: $res2", res2c)
|
||||
|
||||
val res21 = repl.compiledEvaluator.eval(codeLine0, emptyList(), res2c!!.classes, res2c.hasResult, res2c.classpathAddendum)
|
||||
val res21 = repl.compiledEvaluator.eval(res2c!!)
|
||||
val res21e = res21 as? ReplEvalResult.ValueResult
|
||||
TestCase.assertNotNull("Unexpected eval result: $res21", res21e)
|
||||
TestCase.assertEquals(3, res21e!!.value)
|
||||
@@ -63,7 +62,7 @@ class GenericReplTest : TestCase() {
|
||||
val res3c = res3 as? ReplCompileResult.CompiledClasses
|
||||
TestCase.assertNotNull("Unexpected compile result: $res3", res3c)
|
||||
|
||||
val res31 = repl.compiledEvaluator.eval(codeLine1, listOf(codeLine0), res3c!!.classes, res3c.hasResult, res3c.classpathAddendum)
|
||||
val res31 = repl.compiledEvaluator.eval(res3c!!)
|
||||
val res31e = res31 as? ReplEvalResult.UnitResult
|
||||
TestCase.assertNotNull("Unexpected eval result: $res31", res31e)
|
||||
|
||||
@@ -75,7 +74,7 @@ class GenericReplTest : TestCase() {
|
||||
val res4c = res4 as? ReplCompileResult.CompiledClasses
|
||||
TestCase.assertNotNull("Unexpected compile result: $res4", res4c)
|
||||
|
||||
val res41 = repl.compiledEvaluator.eval(codeLine2, emptyList(), res4c!!.classes, res4c.hasResult, res4c.classpathAddendum)
|
||||
val res41 = repl.compiledEvaluator.eval(res4c!!)
|
||||
val res41e = res41 as? ReplEvalResult.ValueResult
|
||||
TestCase.assertNotNull("Unexpected eval result: $res41", res41e)
|
||||
TestCase.assertEquals(7, res41e!!.value)
|
||||
@@ -97,7 +96,7 @@ class GenericReplTest : TestCase() {
|
||||
val res1c = res1 as? ReplCompileResult.CompiledClasses
|
||||
TestCase.assertNotNull("Unexpected compile result: $res1", res1c)
|
||||
|
||||
val res11 = repl.compiledEvaluator.eval(codeLine1, emptyList(), res1c!!.classes, res1c.hasResult, res1c.classpathAddendum)
|
||||
val res11 = repl.compiledEvaluator.eval(res1c!!)
|
||||
val res11e = res11 as? ReplEvalResult.ValueResult
|
||||
TestCase.assertNotNull("Unexpected eval result: $res11", res11e)
|
||||
TestCase.assertEquals(3, res11e!!.value)
|
||||
@@ -107,7 +106,7 @@ class GenericReplTest : TestCase() {
|
||||
val res2c = res2 as? ReplCompileResult.CompiledClasses
|
||||
TestCase.assertNotNull("Unexpected compile result: $res2", res2c)
|
||||
|
||||
val res21 = repl.compiledEvaluator.eval(codeLine2, listOf(codeLine1), res2c!!.classes, res2c.hasResult, res2c.classpathAddendum)
|
||||
val res21 = repl.compiledEvaluator.eval(res2c!!)
|
||||
val res21e = res21 as? ReplEvalResult.ValueResult
|
||||
TestCase.assertNotNull("Unexpected eval result: $res21", res21e)
|
||||
TestCase.assertEquals(5, res21e!!.value)
|
||||
@@ -121,6 +120,8 @@ internal class TestRepl(
|
||||
templateClasspath: List<File>,
|
||||
templateClassName: String
|
||||
) {
|
||||
val emptyScriptArgs = ScriptArgsWithTypes(arrayOf(emptyArray<String>()), arrayOf(Array<String>::class))
|
||||
|
||||
private val configuration = KotlinTestUtils.newConfiguration(ConfigurationKind.ALL, TestJdkKind.MOCK_JDK, *templateClasspath.toTypedArray()).apply {
|
||||
put(CommonConfigurationKeys.MODULE_NAME, "kotlin-script")
|
||||
}
|
||||
@@ -137,8 +138,8 @@ internal class TestRepl(
|
||||
GenericReplCompiler(disposable, scriptDef, configuration, PrintingMessageCollector(System.out, MessageRenderer.WITHOUT_PATHS, false))
|
||||
}
|
||||
|
||||
val compiledEvaluator : GenericReplCompiledEvaluator by lazy {
|
||||
GenericReplCompiledEvaluator(configuration.jvmClasspathRoots, null, arrayOf(emptyArray<String>()))
|
||||
val compiledEvaluator: ReplEvaluator by lazy {
|
||||
GenericReplEvaluator(configuration.jvmClasspathRoots, null, emptyScriptArgs, ReplRepeatingMode.NONE)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -486,7 +486,7 @@ class CompilerDaemonTest : KotlinIntegrationTestBase() {
|
||||
ScriptWithNoParam::class.qualifiedName!!,
|
||||
System.err)
|
||||
|
||||
val localEvaluator = GenericReplCompiledEvaluator(emptyList(), Thread.currentThread().contextClassLoader)
|
||||
val localEvaluator = GenericReplEvaluator(emptyList(), Thread.currentThread().contextClassLoader)
|
||||
|
||||
doReplTestWithLocalEval(repl, localEvaluator)
|
||||
}
|
||||
@@ -501,17 +501,16 @@ class CompilerDaemonTest : KotlinIntegrationTestBase() {
|
||||
"kotlin.script.templates.standard.ScriptTemplateWithArgs",
|
||||
System.err)
|
||||
|
||||
val localEvaluator = GenericReplCompiledEvaluator(emptyList(),
|
||||
Thread.currentThread().contextClassLoader,
|
||||
arrayOf(emptyArray<String>()))
|
||||
val localEvaluator = GenericReplEvaluator(emptyList(), Thread.currentThread().contextClassLoader,
|
||||
ScriptArgsWithTypes(arrayOf(emptyArray<String>()), arrayOf(Array<String>::class)))
|
||||
|
||||
doReplTestWithLocalEval(repl, localEvaluator)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun doReplTestWithLocalEval(repl: KotlinRemoteReplCompiler, localEvaluator: GenericReplCompiledEvaluator) {
|
||||
val res0 = repl.check(ReplCodeLine(0, "val x ="), emptyList())
|
||||
private fun doReplTestWithLocalEval(repl: KotlinRemoteReplCompiler, localEvaluator: ReplEvaluator) {
|
||||
val res0 = repl.check(ReplCodeLine(0, "val x ="))
|
||||
TestCase.assertTrue("Unexpected check results: $res0", res0 is ReplCheckResult.Incomplete)
|
||||
|
||||
val codeLine1 = ReplCodeLine(1, "val lst = listOf(1)\nval x = 5")
|
||||
@@ -519,7 +518,7 @@ class CompilerDaemonTest : KotlinIntegrationTestBase() {
|
||||
val res1c = res1 as? ReplCompileResult.CompiledClasses
|
||||
TestCase.assertNotNull("Unexpected compile result: $res1", res1c)
|
||||
|
||||
val res11 = localEvaluator.eval(codeLine1, emptyList(), res1c!!.classes, res1c.hasResult, res1c.classpathAddendum)
|
||||
val res11 = localEvaluator.eval(res1c!!)
|
||||
val res11e = res11 as? ReplEvalResult.UnitResult
|
||||
TestCase.assertNotNull("Unexpected eval result: $res11", res11e)
|
||||
|
||||
@@ -531,7 +530,7 @@ class CompilerDaemonTest : KotlinIntegrationTestBase() {
|
||||
val res2c = res2 as? ReplCompileResult.CompiledClasses
|
||||
TestCase.assertNotNull("Unexpected compile result: $res2", res2c)
|
||||
|
||||
val res21 = localEvaluator.eval(codeLine2, listOf(codeLine1), res2c!!.classes, res2c.hasResult, res2c.classpathAddendum)
|
||||
val res21 = localEvaluator.eval(res2c!!)
|
||||
val res21e = res21 as? ReplEvalResult.ValueResult
|
||||
TestCase.assertNotNull("Unexpected eval result: $res21", res21e)
|
||||
TestCase.assertEquals(7, res21e!!.value)
|
||||
@@ -548,22 +547,23 @@ class CompilerDaemonTest : KotlinIntegrationTestBase() {
|
||||
val repl = KotlinRemoteReplEvaluator(disposable, daemon!!, null, CompileService.TargetPlatform.JVM,
|
||||
listOf(File(KotlinIntegrationTestBase.getCompilerLib(), "kotlin-runtime.jar")),
|
||||
"kotlin.script.templates.standard.ScriptTemplateWithArgs",
|
||||
arrayOf(emptyArray<String>()), null,
|
||||
arrayOf(emptyArray<String>()),
|
||||
arrayOf(Array<String>::class.java),
|
||||
System.err, evalOut, evalErr, evalIn)
|
||||
|
||||
val res0 = repl.check(ReplCodeLine(0, "val x ="), emptyList())
|
||||
val res0 = repl.check(ReplCodeLine(0, "val x ="))
|
||||
TestCase.assertTrue("Unexpected check results: $res0", res0 is ReplCheckResult.Incomplete)
|
||||
|
||||
val codeLine1 = ReplCodeLine(1, "val x = 5")
|
||||
val res1 = repl.eval(codeLine1, emptyList())
|
||||
val res1 = repl.compileAndEval(codeLine1, verifyHistory = emptyList())
|
||||
val res1e = res1 as? ReplEvalResult.UnitResult
|
||||
TestCase.assertNotNull("Unexpected eval result: $res1", res1e)
|
||||
|
||||
val codeLine2 = ReplCodeLine(2, "x + 2")
|
||||
val res2x = repl.eval(codeLine2, listOf(codeLine2))
|
||||
val res2x = repl.compileAndEval(codeLine2, verifyHistory = listOf(codeLine2))
|
||||
TestCase.assertNotNull("Unexpected compile result: $res2x", res2x as? ReplEvalResult.HistoryMismatch)
|
||||
|
||||
val res2 = repl.eval(codeLine2, listOf(codeLine1))
|
||||
val res2 = repl.compileAndEval(codeLine2, verifyHistory = listOf(codeLine1))
|
||||
val res2e = res2 as? ReplEvalResult.ValueResult
|
||||
TestCase.assertNotNull("Unexpected eval result: $res2", res2e)
|
||||
TestCase.assertEquals(7, res2e!!.value)
|
||||
@@ -592,8 +592,8 @@ class CompilerDaemonTest : KotlinIntegrationTestBase() {
|
||||
|
||||
// use repl compiler for >> 1s, making sure that idle/unused timeouts are not firing
|
||||
for (attempts in 1..10) {
|
||||
val codeLine1 = ReplCodeLine(1, "3 + 5")
|
||||
val res1 = repl.compile(codeLine1, emptyList())
|
||||
val codeLine1 = ReplCodeLine(attempts, "3 + 5")
|
||||
val res1 = repl.compile(codeLine1)
|
||||
val res1c = res1 as? ReplCompileResult.CompiledClasses
|
||||
TestCase.assertNotNull("Unexpected compile result: $res1", res1c)
|
||||
Thread.sleep(200)
|
||||
|
||||
@@ -17,10 +17,7 @@
|
||||
package org.jetbrains.kotlin.jsr223
|
||||
|
||||
import com.intellij.openapi.Disposable
|
||||
import org.jetbrains.kotlin.cli.common.repl.GenericReplCompiledEvaluator
|
||||
import org.jetbrains.kotlin.cli.common.repl.KotlinJsr223JvmScriptEngineBase
|
||||
import org.jetbrains.kotlin.cli.common.repl.ReplCompiledEvaluator
|
||||
import org.jetbrains.kotlin.cli.common.repl.ReplCompiler
|
||||
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
|
||||
@@ -31,14 +28,15 @@ import java.io.File
|
||||
import javax.script.ScriptContext
|
||||
import javax.script.ScriptEngineFactory
|
||||
import javax.script.ScriptException
|
||||
import kotlin.reflect.KClass
|
||||
|
||||
class KotlinJsr223JvmScriptEngine4Idea(
|
||||
disposable: Disposable,
|
||||
factory: ScriptEngineFactory,
|
||||
templateClasspath: List<File>,
|
||||
templateClassName: String,
|
||||
getScriptArgs: (ScriptContext) -> Array<Any?>?,
|
||||
scriptArgsTypes: Array<Class<*>>?
|
||||
private val getScriptArgs: (ScriptContext, Array<out KClass<out Any>>?) -> ScriptArgsWithTypes?,
|
||||
private val scriptArgsTypes: Array<out KClass<out Any>>?
|
||||
) : KotlinJsr223JvmScriptEngineBase(factory) {
|
||||
|
||||
private val daemon by lazy {
|
||||
@@ -66,8 +64,10 @@ class KotlinJsr223JvmScriptEngine4Idea(
|
||||
}
|
||||
}
|
||||
|
||||
// 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: ReplCompiledEvaluator by lazy { GenericReplCompiledEvaluator(templateClasspath, Thread.currentThread().contextClassLoader, getScriptArgs(getContext()), scriptArgsTypes) }
|
||||
override fun overrideScriptArgs(context: ScriptContext): ScriptArgsWithTypes? =
|
||||
getScriptArgs(getContext(), scriptArgsTypes)
|
||||
|
||||
override val replEvaluator: ReplCompiledEvaluator get() = localEvaluator
|
||||
val localEvaluator: ReplFullEvaluator by lazy { GenericReplCompilingEvaluator(replCompiler, templateClasspath, Thread.currentThread().contextClassLoader) }
|
||||
|
||||
override val replScriptEvaluator: ReplFullEvaluator get() = localEvaluator
|
||||
}
|
||||
|
||||
+3
-2
@@ -18,6 +18,7 @@ package org.jetbrains.kotlin.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.utils.PathUtil
|
||||
import org.jetbrains.kotlin.utils.PathUtil.KOTLIN_JAVA_RUNTIME_JAR
|
||||
import org.jetbrains.kotlin.utils.PathUtil.KOTLIN_JAVA_SCRIPT_RUNTIME_JAR
|
||||
@@ -37,8 +38,8 @@ class KotlinJsr223StandardScriptEngineFactory4Idea : KotlinJsr223JvmScriptEngine
|
||||
this,
|
||||
scriptCompilationClasspathFromContext(Thread.currentThread().contextClassLoader),
|
||||
"kotlin.script.templates.standard.ScriptTemplateWithBindings",
|
||||
{ ctx -> arrayOf(ctx.getBindings(ScriptContext.ENGINE_SCOPE)) },
|
||||
arrayOf(Map::class.java)
|
||||
{ ctx, argTypes -> ScriptArgsWithTypes(arrayOf(ctx.getBindings(ScriptContext.ENGINE_SCOPE)), argTypes ?: emptyArray()) },
|
||||
arrayOf(Map::class)
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
+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