Extract repl state as a separate object
This commit is contained in:
+79
@@ -0,0 +1,79 @@
|
|||||||
|
/*
|
||||||
|
* 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.util.concurrent.locks.ReentrantReadWriteLock
|
||||||
|
import kotlin.concurrent.read
|
||||||
|
import kotlin.concurrent.write
|
||||||
|
|
||||||
|
open class AggregatedReplStateHistory<T1, T2>(val history1: IReplStageHistory<T1>, val history2: IReplStageHistory<T2>, override val lock: ReentrantReadWriteLock = ReentrantReadWriteLock())
|
||||||
|
: IReplStageHistory<Pair<T1, T2>>, AbstractList<ReplHistoryRecord<Pair<T1, T2>>>()
|
||||||
|
{
|
||||||
|
override val size: Int
|
||||||
|
get() = minOf(history1.size, history2.size)
|
||||||
|
|
||||||
|
override fun push(id: ILineId, item: Pair<T1, T2>) {
|
||||||
|
lock.write {
|
||||||
|
assertSameSize()
|
||||||
|
history1.push(id, item.first)
|
||||||
|
history2.push(id, item.second)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun get(index: Int): ReplHistoryRecord<Pair<T1, T2>> = lock.read {
|
||||||
|
assertSameSize()
|
||||||
|
val r1 = history1[index]
|
||||||
|
val r2 = history2[index]
|
||||||
|
assertSameId(r1, r2)
|
||||||
|
ReplHistoryRecord(r1.id, r1.item to r2.item)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun pop(): ReplHistoryRecord<Pair<T1, T2>>? = lock.write {
|
||||||
|
assertSameSize()
|
||||||
|
val r1 = history1.pop()
|
||||||
|
val r2 = history2.pop()
|
||||||
|
if (r1 == null && r2 == null) return null
|
||||||
|
if (r1 == null || r2 == null) throw IllegalStateException("Aggregated history mismatch: $r1 vs $r2")
|
||||||
|
assertSameId(r1, r2)
|
||||||
|
ReplHistoryRecord(r1.id, r1.item to r2.item)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun resetTo(id: ILineId): Iterable<ILineId> = lock.write {
|
||||||
|
assertSameSize()
|
||||||
|
val i1 = history1.resetTo(id).toList()
|
||||||
|
val i2 = history2.resetTo(id).toList()
|
||||||
|
if (i1 != i2) throw IllegalStateException("Aggregated history resetted lines mismatch: $i1 != $i2")
|
||||||
|
i1
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun assertSameSize() {
|
||||||
|
if (history1.size != history2.size) throw IllegalStateException("Aggregated history sizes mismatch: ${history1.size} != ${history2.size}")
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun assertSameId(r1: ReplHistoryRecord<T1>, r2: ReplHistoryRecord<T2>) {
|
||||||
|
if (r1.id != r2.id) throw IllegalStateException("Aggregated history mismatch: ${r1.id} != ${r2.id}")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
open class AggregatedReplStageState<T1, T2>(val state1: IReplStageState<T1>, val state2: IReplStageState<T2>, override val lock: ReentrantReadWriteLock = ReentrantReadWriteLock())
|
||||||
|
: IReplStageState<Pair<T1, T2>>
|
||||||
|
{
|
||||||
|
override val history: IReplStageHistory<Pair<T1, T2>> = AggregatedReplStateHistory(state1.history, state2.history, lock)
|
||||||
|
|
||||||
|
override fun <StateT : IReplStageState<*>> asState(): StateT = (state1 as? StateT) ?: (state2 as StateT) ?: super.asState()
|
||||||
|
}
|
||||||
|
|
||||||
@@ -0,0 +1,69 @@
|
|||||||
|
/*
|
||||||
|
* 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.*
|
||||||
|
import java.util.concurrent.locks.ReentrantReadWriteLock
|
||||||
|
import kotlin.concurrent.write
|
||||||
|
|
||||||
|
data class LineId(override val no: Int, private val codeHash: Int) : ILineId, Serializable {
|
||||||
|
|
||||||
|
constructor(no: Int, code: String): this(no, code.hashCode())
|
||||||
|
constructor(codeLine: ReplCodeLine): this(codeLine.no, codeLine.code.hashCode())
|
||||||
|
|
||||||
|
override fun compareTo(other: ILineId): Int = (other as? LineId)?.let {
|
||||||
|
val c = no.compareTo(it.no)
|
||||||
|
if (c == 0) codeHash.compareTo(it.codeHash)
|
||||||
|
else c
|
||||||
|
} ?: -1 // TODO: check if it doesn't break something
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
private val serialVersionUID: Long = 8328353000L
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
open class BasicReplStageHistory<T>(override val lock: ReentrantReadWriteLock = ReentrantReadWriteLock()) : IReplStageHistory<T>, ArrayList<ReplHistoryRecord<T>>() {
|
||||||
|
|
||||||
|
override fun push(id: ILineId, item: T) {
|
||||||
|
lock.write {
|
||||||
|
add(ReplHistoryRecord(id, item))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun pop(): ReplHistoryRecord<T>? = lock.write { if (isEmpty()) null else removeAt(lastIndex) }
|
||||||
|
|
||||||
|
override fun resetTo(id: ILineId): Iterable<ILineId> {
|
||||||
|
lock.write {
|
||||||
|
val idx = indexOfFirst { it.id == id }
|
||||||
|
if (idx < 0) throw java.util.NoSuchElementException("Cannot rest to inexistent line ${id.no}")
|
||||||
|
if (idx < lastIndex) {
|
||||||
|
val removed = asSequence().drop(idx + 1).map { it.id }.toList()
|
||||||
|
removeRange(idx + 1, lastIndex)
|
||||||
|
return removed
|
||||||
|
}
|
||||||
|
else return emptyList()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class BasicReplStageState<HistoryItemT>: IReplStageState<HistoryItemT> {
|
||||||
|
|
||||||
|
override val lock = ReentrantReadWriteLock()
|
||||||
|
|
||||||
|
override val history: BasicReplStageHistory<HistoryItemT> = BasicReplStageHistory(lock)
|
||||||
|
}
|
||||||
+38
@@ -0,0 +1,38 @@
|
|||||||
|
/*
|
||||||
|
* 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.net.URLClassLoader
|
||||||
|
import java.util.concurrent.locks.ReentrantReadWriteLock
|
||||||
|
import kotlin.concurrent.read
|
||||||
|
|
||||||
|
open class GenericReplEvaluatorState(baseClasspath: Iterable<File>, baseClassloader: ClassLoader?, override val lock: ReentrantReadWriteLock = ReentrantReadWriteLock())
|
||||||
|
: IReplStageState<EvalClassWithInstanceAndLoader>
|
||||||
|
{
|
||||||
|
override val history: IReplStageHistory<EvalClassWithInstanceAndLoader> = BasicReplStageHistory(lock)
|
||||||
|
|
||||||
|
val topClassLoader: ReplClassLoader = makeReplClassLoader(baseClassloader, baseClasspath)
|
||||||
|
|
||||||
|
val currentClasspath: List<File> get() = lock.read {
|
||||||
|
history.peek()?.item?.classLoader?.listAllUrlsAsFiles()
|
||||||
|
?: topClassLoader.listAllUrlsAsFiles()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
internal fun makeReplClassLoader(baseClassloader: ClassLoader?, baseClasspath: Iterable<File>) =
|
||||||
|
ReplClassLoader(URLClassLoader(baseClasspath.map { it.toURI().toURL() }.toTypedArray(), baseClassloader))
|
||||||
+34
-49
@@ -22,27 +22,36 @@ import kotlin.concurrent.write
|
|||||||
|
|
||||||
class GenericReplCompilingEvaluator(val compiler: ReplCompiler,
|
class GenericReplCompilingEvaluator(val compiler: ReplCompiler,
|
||||||
baseClasspath: Iterable<File>,
|
baseClasspath: Iterable<File>,
|
||||||
baseClassloader: ClassLoader?,
|
baseClassloader: ClassLoader? = Thread.currentThread().contextClassLoader,
|
||||||
protected val fallbackScriptArgs: ScriptArgsWithTypes? = null,
|
private val fallbackScriptArgs: ScriptArgsWithTypes? = null,
|
||||||
repeatingMode: ReplRepeatingMode = ReplRepeatingMode.REPEAT_ONLY_MOST_RECENT,
|
repeatingMode: ReplRepeatingMode = ReplRepeatingMode.REPEAT_ONLY_MOST_RECENT
|
||||||
protected val stateLock: ReentrantReadWriteLock = ReentrantReadWriteLock()
|
|
||||||
) : ReplFullEvaluator {
|
) : ReplFullEvaluator {
|
||||||
val evaluator = GenericReplEvaluator(baseClasspath, baseClassloader, fallbackScriptArgs, repeatingMode, stateLock)
|
val evaluator = GenericReplEvaluator(baseClasspath, baseClassloader, fallbackScriptArgs, repeatingMode)
|
||||||
|
|
||||||
override fun compileAndEval(codeLine: ReplCodeLine, scriptArgs: ScriptArgsWithTypes?, verifyHistory: List<ReplCodeLine>?, invokeWrapper: InvokeWrapper?): ReplEvalResult {
|
override fun createState(lock: ReentrantReadWriteLock): IReplStageState<*> = AggregatedReplStageState(compiler.createState(lock), evaluator.createState(lock), lock)
|
||||||
return stateLock.write {
|
|
||||||
val compiled = compiler.compile(codeLine, verifyHistory)
|
override fun compileAndEval(state: IReplStageState<*>, codeLine: ReplCodeLine, scriptArgs: ScriptArgsWithTypes?, invokeWrapper: InvokeWrapper?): ReplEvalResult {
|
||||||
|
return state.lock.write {
|
||||||
|
val aggregatedState = state.asState<AggregatedReplStageState<*, *>>()
|
||||||
|
val compiled = compiler.compile(state, codeLine)
|
||||||
when (compiled) {
|
when (compiled) {
|
||||||
is ReplCompileResult.Error -> ReplEvalResult.Error.CompileTime(compiled.compiledHistory, compiled.message, compiled.location)
|
is ReplCompileResult.Error -> ReplEvalResult.Error.CompileTime(compiled.message, compiled.location)
|
||||||
is ReplCompileResult.HistoryMismatch -> ReplEvalResult.HistoryMismatch(compiled.compiledHistory, compiled.lineNo)
|
is ReplCompileResult.HistoryMismatch -> ReplEvalResult.HistoryMismatch(compiled.lineNo)
|
||||||
is ReplCompileResult.Incomplete -> ReplEvalResult.Incomplete(compiled.compiledHistory)
|
is ReplCompileResult.Incomplete -> ReplEvalResult.Incomplete()
|
||||||
is ReplCompileResult.CompiledClasses -> {
|
is ReplCompileResult.CompiledClasses -> {
|
||||||
val result = eval(compiled, scriptArgs, invokeWrapper)
|
val result = eval(state, compiled, scriptArgs, invokeWrapper)
|
||||||
when (result) {
|
when (result) {
|
||||||
is ReplEvalResult.Error,
|
is ReplEvalResult.Error,
|
||||||
is ReplEvalResult.HistoryMismatch,
|
is ReplEvalResult.HistoryMismatch,
|
||||||
is ReplEvalResult.Incomplete -> {
|
is ReplEvalResult.Incomplete -> {
|
||||||
result.completedEvalHistory.lastOrNull()?.let { compiler.resetToLine(it) }
|
aggregatedState.apply {
|
||||||
|
lock.write {
|
||||||
|
if (state1.history.size > state2.history.size) {
|
||||||
|
state2.history.peek()?.let { state1.history.resetTo(it.id) }
|
||||||
|
assert(state1.history.size == state2.history.size)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
result
|
result
|
||||||
}
|
}
|
||||||
is ReplEvalResult.ValueResult,
|
is ReplEvalResult.ValueResult,
|
||||||
@@ -56,49 +65,25 @@ class GenericReplCompilingEvaluator(val compiler: ReplCompiler,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun resetToLine(lineNumber: Int): List<ReplCodeLine> {
|
override fun eval(state: IReplStageState<*>, compileResult: ReplCompileResult.CompiledClasses, scriptArgs: ScriptArgsWithTypes?, invokeWrapper: InvokeWrapper?): ReplEvalResult =
|
||||||
stateLock.write {
|
evaluator.eval(state, compileResult, scriptArgs, invokeWrapper)
|
||||||
val removedCompiledLines = compiler.resetToLine(lineNumber)
|
|
||||||
val removedEvaluatorLines = evaluator.resetToLine(lineNumber)
|
|
||||||
|
|
||||||
removedCompiledLines.zip(removedEvaluatorLines).forEach {
|
override fun check(state: IReplStageState<*>, codeLine: ReplCodeLine): ReplCheckResult = compiler.check(state, codeLine)
|
||||||
if (it.first != it.second) {
|
|
||||||
throw IllegalStateException("History mismatch when resetting lines")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return removedCompiledLines
|
override fun compileToEvaluable(state: IReplStageState<*>, codeLine: ReplCodeLine, defaultScriptArgs: ScriptArgsWithTypes?): Pair<ReplCompileResult, Evaluable?> {
|
||||||
}
|
val compiled = compiler.compile(state, codeLine)
|
||||||
}
|
|
||||||
|
|
||||||
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 =
|
|
||||||
evaluator.eval(compileResult, scriptArgs, invokeWrapper)
|
|
||||||
|
|
||||||
override fun check(codeLine: ReplCodeLine): ReplCheckResult = compiler.check(codeLine)
|
|
||||||
|
|
||||||
override fun compileToEvaluable(codeLine: ReplCodeLine, defaultScriptArgs: ScriptArgsWithTypes?, verifyHistory: List<ReplCodeLine>?): Pair<ReplCompileResult, Evaluable?> {
|
|
||||||
val compiled = compiler.compile(codeLine, verifyHistory)
|
|
||||||
return when (compiled) {
|
return when (compiled) {
|
||||||
is ReplCompileResult.CompiledClasses -> Pair(compiled, DelayedEvaluation(compiled, stateLock, evaluator, defaultScriptArgs ?: fallbackScriptArgs))
|
// TODO: seems usafe when delayed evaluation may happen after some more compileAndEval calls on the same state; check and fix or protect
|
||||||
|
is ReplCompileResult.CompiledClasses -> Pair(compiled, DelayedEvaluation(state, compiled, evaluator, defaultScriptArgs ?: fallbackScriptArgs))
|
||||||
else -> Pair(compiled, null)
|
else -> Pair(compiled, null)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class DelayedEvaluation(override val compiledCode: ReplCompileResult.CompiledClasses,
|
class DelayedEvaluation(private val state: IReplStageState<*>,
|
||||||
private val stateLock: ReentrantReadWriteLock,
|
override val compiledCode: ReplCompileResult.CompiledClasses,
|
||||||
private val evaluator: ReplEvaluator,
|
private val evaluator: ReplEvaluator,
|
||||||
private val defaultScriptArgs: ScriptArgsWithTypes?) : Evaluable {
|
private val defaultScriptArgs: ScriptArgsWithTypes?) : Evaluable {
|
||||||
override fun eval(scriptArgs: ScriptArgsWithTypes?, invokeWrapper: InvokeWrapper?): ReplEvalResult {
|
override fun eval(scriptArgs: ScriptArgsWithTypes?, invokeWrapper: InvokeWrapper?): ReplEvalResult =
|
||||||
return stateLock.write { evaluator.eval(compiledCode, scriptArgs ?: defaultScriptArgs, invokeWrapper) }
|
evaluator.eval(state, compiledCode, scriptArgs ?: defaultScriptArgs, invokeWrapper)
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+169
-122
@@ -18,144 +18,111 @@ package org.jetbrains.kotlin.cli.common.repl
|
|||||||
|
|
||||||
import org.jetbrains.kotlin.resolve.jvm.JvmClassName
|
import org.jetbrains.kotlin.resolve.jvm.JvmClassName
|
||||||
import java.io.File
|
import java.io.File
|
||||||
import java.net.URLClassLoader
|
|
||||||
import java.util.concurrent.locks.ReentrantReadWriteLock
|
import java.util.concurrent.locks.ReentrantReadWriteLock
|
||||||
import kotlin.concurrent.read
|
|
||||||
import kotlin.concurrent.write
|
import kotlin.concurrent.write
|
||||||
|
|
||||||
open class GenericReplEvaluator(baseClasspath: Iterable<File>,
|
open class GenericReplEvaluator(val baseClasspath: Iterable<File>,
|
||||||
baseClassloader: ClassLoader?,
|
val baseClassloader: ClassLoader? = Thread.currentThread().contextClassLoader,
|
||||||
protected val fallbackScriptArgs: ScriptArgsWithTypes? = null,
|
protected val fallbackScriptArgs: ScriptArgsWithTypes? = null,
|
||||||
protected val repeatingMode: ReplRepeatingMode = ReplRepeatingMode.REPEAT_ONLY_MOST_RECENT,
|
protected val repeatingMode: ReplRepeatingMode = ReplRepeatingMode.REPEAT_ONLY_MOST_RECENT
|
||||||
protected val stateLock: ReentrantReadWriteLock = ReentrantReadWriteLock()
|
|
||||||
) : ReplEvaluator {
|
) : ReplEvaluator {
|
||||||
|
|
||||||
private val topClassLoader: ReplClassLoader = makeReplClassLoader(baseClassloader, baseClasspath)
|
// private val evaluatedHistory = ReplHistory<EvalClassWithInstanceAndLoader>()
|
||||||
|
|
||||||
private val evaluatedHistory = ReplHistory<EvalClassWithInstanceAndLoader>()
|
// override val history: List<ReplCodeLine> get() = stateLock.read { evaluatedHistory.copySources() }
|
||||||
|
|
||||||
override fun resetToLine(lineNumber: Int): List<ReplCodeLine> {
|
// private class HistoryActions(
|
||||||
return stateLock.write {
|
// val effectiveHistory: List<EvalClassWithInstanceAndLoader>,
|
||||||
evaluatedHistory.resetToLine(lineNumber)
|
// val verify: (compareHistory: SourceList?) -> Int?,
|
||||||
}.map { it.first }
|
// 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>>
|
||||||
|
// )
|
||||||
|
|
||||||
override val history: List<ReplCodeLine> get() = stateLock.read { evaluatedHistory.copySources() }
|
override fun createState(lock: ReentrantReadWriteLock): IReplStageState<*> = GenericReplEvaluatorState(baseClasspath, baseClassloader, lock)
|
||||||
|
|
||||||
override val currentClasspath: List<File> get() = stateLock.read {
|
override fun eval(state: IReplStageState<*>,
|
||||||
evaluatedHistory.copyValues().lastOrNull()?.classLoader?.listAllUrlsAsFiles()
|
compileResult: ReplCompileResult.CompiledClasses,
|
||||||
?: 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.removeSuffix(".class"))
|
|
||||||
fun compiledClassesNames() = compileResult.classes.map { classNameFromPath(it.path).internalName.replace('/', '.') }
|
|
||||||
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?,
|
scriptArgs: ScriptArgsWithTypes?,
|
||||||
invokeWrapper: InvokeWrapper?): ReplEvalResult {
|
invokeWrapper: InvokeWrapper?): ReplEvalResult {
|
||||||
stateLock.write {
|
state.lock.write {
|
||||||
val verifyHistory = compileResult.compiledHistory.dropLast(1)
|
val evalState = state.asState<GenericReplEvaluatorState>()
|
||||||
val defaultHistoryActor = HistoryActions(
|
// val verifyHistory = compileResult.state.dropLast(1)
|
||||||
effectiveHistory = evaluatedHistory.copyValues(),
|
// val defaultHistoryActor = HistoryActions(
|
||||||
verify = { line -> evaluatedHistory.firstMismatchingHistory(line) },
|
// effectiveHistory = evalState.history.map { it.item },
|
||||||
addPlaceholder = { line, value -> evaluatedHistory.add(line, value) },
|
// verify = { line -> evalState.history.firstMismatchingHistory(line) },
|
||||||
removePlaceholder = { line -> evaluatedHistory.removeLast(line) },
|
// addPlaceholder = { line, value -> evalState.history.add(line, value) },
|
||||||
addFinal = { line, value -> evaluatedHistory.add(line, value) },
|
// removePlaceholder = { line -> evalState.history.removeLast(line) },
|
||||||
processClasses = { compiled ->
|
// addFinal = { line, value -> evalState.history.add(line, value) },
|
||||||
prependClassLoaderWithNewClasses(evaluatedHistory.copyValues(), compiled)
|
// processClasses = { compiled ->
|
||||||
})
|
// prependClassLoaderWithNewClasses(evalState.history.copyValues(), compiled, )
|
||||||
|
// })
|
||||||
|
|
||||||
val historyActor: HistoryActions = when (repeatingMode) {
|
val historyActor = when (repeatingMode) {
|
||||||
ReplRepeatingMode.NONE -> defaultHistoryActor
|
ReplRepeatingMode.NONE -> HistoryActionsForNoRepeat(evalState)
|
||||||
ReplRepeatingMode.REPEAT_ONLY_MOST_RECENT -> {
|
ReplRepeatingMode.REPEAT_ONLY_MOST_RECENT -> {
|
||||||
val lastItem = evaluatedHistory.lastItem()
|
val lastItem = evalState.history.peek()
|
||||||
if (lastItem == null || lastItem.first.source != compileResult.compiledCodeLine.source) {
|
if (lastItem == null || lastItem.id != compileResult.lineId) {
|
||||||
defaultHistoryActor
|
HistoryActionsForNoRepeat(evalState)
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
val trimmedHistory = ReplHistory(evaluatedHistory.copyAll().dropLast(1))
|
HistoryActionsForRepeatRecentOnly(evalState)
|
||||||
HistoryActions(
|
// val trimmedHistory = ReplHistory(evalState.history.copyAll().dropLast(1))
|
||||||
effectiveHistory = trimmedHistory.copyValues(),
|
// HistoryActions
|
||||||
verify = { trimmedHistory.firstMismatchingHistory(it) },
|
// effectiveHistory = trimmedHistory.copyValues(),
|
||||||
addPlaceholder = { _, _ -> NO_ACTION() },
|
// verify = { trimmedHistory.firstMismatchingHistory(it) },
|
||||||
removePlaceholder = { NO_ACTION_THAT_RETURNS(true) },
|
// addPlaceholder = { _, _ -> NO_ACTION() },
|
||||||
addFinal = { line, value ->
|
// removePlaceholder = { NO_ACTION_THAT_RETURNS(true) },
|
||||||
evaluatedHistory.removeLast(line)
|
// addFinal = { line, value ->
|
||||||
evaluatedHistory.add(line, value)
|
// evalState.history.removeLast(line)
|
||||||
},
|
// evalState.history.add(line, value)
|
||||||
processClasses = { _ ->
|
// },
|
||||||
Pair(lastItem.second.classLoader, lastItem.second.klass.java)
|
// processClasses = { _ ->
|
||||||
})
|
// Pair(lastItem.second.classLoader, lastItem.second.klass.java)
|
||||||
|
// })
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
ReplRepeatingMode.REPEAT_ANY_PREVIOUS -> {
|
ReplRepeatingMode.REPEAT_ANY_PREVIOUS -> {
|
||||||
if (evaluatedHistory.isEmpty() || !evaluatedHistory.contains(compileResult.compiledCodeLine.source)) {
|
val matchingItem = evalState.history.firstOrNull { it.id == compileResult.lineId }
|
||||||
defaultHistoryActor
|
if (matchingItem == null) {
|
||||||
|
HistoryActionsForNoRepeat(evalState)
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
val historyCopy = evaluatedHistory.copyAll()
|
HistoryActionsForRepeatAny(evalState, matchingItem)
|
||||||
val matchingItem = historyCopy.first { it.first.source == compileResult.compiledCodeLine.source }
|
// val historyCopy = evalState.history.copyAll()
|
||||||
val trimmedHistory = ReplHistory(evaluatedHistory.copyAll().takeWhile { it != matchingItem })
|
// val matchingItem = historyCopy.first { it.first.source == compileResult.compiledCodeLine.source }
|
||||||
HistoryActions(
|
// val trimmedHistory = ReplHistory(evalState.history.copyAll().takeWhile { it != matchingItem })
|
||||||
effectiveHistory = trimmedHistory.copyValues(),
|
// HistoryActions(
|
||||||
verify = { trimmedHistory.firstMismatchingHistory(it) },
|
// effectiveHistory = trimmedHistory.copyValues(),
|
||||||
addPlaceholder = { _, _ -> NO_ACTION() },
|
// verify = { trimmedHistory.firstMismatchingHistory(it) },
|
||||||
removePlaceholder = { NO_ACTION_THAT_RETURNS(true) },
|
// addPlaceholder = { _, _ -> NO_ACTION() },
|
||||||
addFinal = { line, value ->
|
// removePlaceholder = { NO_ACTION_THAT_RETURNS(true) },
|
||||||
val extraLines = evaluatedHistory.resetToLine(line)
|
// addFinal = { line, value ->
|
||||||
evaluatedHistory.removeLast(line)
|
// val extraLines = evalState.history.resetToLine(line)
|
||||||
evaluatedHistory.add(line, value)
|
// evalState.history.removeLast(line)
|
||||||
extraLines.forEach {
|
// evalState.history.add(line, value)
|
||||||
evaluatedHistory.add(it.first, it.second)
|
// extraLines.forEach {
|
||||||
}
|
// evalState.history.add(it.first, it.second)
|
||||||
},
|
// }
|
||||||
processClasses = { _ ->
|
// },
|
||||||
Pair(matchingItem.second.classLoader, matchingItem.second.klass.java)
|
// processClasses = { _ ->
|
||||||
})
|
// Pair(matchingItem.second.classLoader, matchingItem.second.klass.java)
|
||||||
|
// })
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
val firstMismatch = historyActor.verify(verifyHistory)
|
// val firstMismatch = historyActor.firstMismatch(verifyHistory)
|
||||||
if (firstMismatch != null) {
|
// if (firstMismatch != null) {
|
||||||
return@eval ReplEvalResult.HistoryMismatch(evaluatedHistory.copySources(), firstMismatch)
|
// return@eval ReplEvalResult.HistoryMismatch(firstMismatch)
|
||||||
}
|
// }
|
||||||
|
|
||||||
val (classLoader, scriptClass) = try {
|
val (classLoader, scriptClass) = try {
|
||||||
historyActor.processClasses(compileResult)
|
historyActor.processClasses(compileResult)
|
||||||
}
|
}
|
||||||
catch (e: Exception) {
|
catch (e: Exception) {
|
||||||
return@eval ReplEvalResult.Error.Runtime(evaluatedHistory.copySources(), e.message ?: "unknown", e)
|
return@eval ReplEvalResult.Error.Runtime(e.message ?: "unknown", e)
|
||||||
}
|
}
|
||||||
|
|
||||||
val currentScriptArgs = scriptArgs ?: fallbackScriptArgs
|
val currentScriptArgs = scriptArgs ?: fallbackScriptArgs
|
||||||
@@ -170,7 +137,7 @@ open class GenericReplEvaluator(baseClasspath: Iterable<File>,
|
|||||||
// TODO: try/catch ?
|
// TODO: try/catch ?
|
||||||
val scriptInstanceConstructor = scriptClass.getConstructor(*constructorParams)
|
val scriptInstanceConstructor = scriptClass.getConstructor(*constructorParams)
|
||||||
|
|
||||||
historyActor.addPlaceholder(compileResult.compiledCodeLine, EvalClassWithInstanceAndLoader(scriptClass.kotlin, null, classLoader, invokeWrapper))
|
historyActor.addPlaceholder(compileResult.lineId, EvalClassWithInstanceAndLoader(scriptClass.kotlin, null, classLoader, invokeWrapper))
|
||||||
|
|
||||||
val scriptInstance =
|
val scriptInstance =
|
||||||
try {
|
try {
|
||||||
@@ -178,33 +145,113 @@ open class GenericReplEvaluator(baseClasspath: Iterable<File>,
|
|||||||
else scriptInstanceConstructor.newInstance(*constructorArgs)
|
else scriptInstanceConstructor.newInstance(*constructorArgs)
|
||||||
}
|
}
|
||||||
catch (e: Throwable) {
|
catch (e: Throwable) {
|
||||||
historyActor.removePlaceholder(compileResult.compiledCodeLine)
|
historyActor.removePlaceholder(compileResult.lineId)
|
||||||
|
|
||||||
// ignore everything in the stack trace until this constructor call
|
// ignore everything in the stack trace until this constructor call
|
||||||
return@eval ReplEvalResult.Error.Runtime(evaluatedHistory.copySources(),
|
return@eval ReplEvalResult.Error.Runtime(renderReplStackTrace(e.cause!!, startFromMethodName = "${scriptClass.name}.<init>"), e as? Exception)
|
||||||
renderReplStackTrace(e.cause!!, startFromMethodName = "${scriptClass.name}.<init>"), e as? Exception)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
historyActor.removePlaceholder(compileResult.compiledCodeLine)
|
historyActor.removePlaceholder(compileResult.lineId)
|
||||||
historyActor.addFinal(compileResult.compiledCodeLine, EvalClassWithInstanceAndLoader(scriptClass.kotlin, scriptInstance, classLoader, invokeWrapper))
|
historyActor.addFinal(compileResult.lineId, EvalClassWithInstanceAndLoader(scriptClass.kotlin, scriptInstance, classLoader, invokeWrapper))
|
||||||
|
|
||||||
val resultField = scriptClass.getDeclaredField(SCRIPT_RESULT_FIELD_NAME).apply { isAccessible = true }
|
val resultField = scriptClass.getDeclaredField(SCRIPT_RESULT_FIELD_NAME).apply { isAccessible = true }
|
||||||
val resultValue: Any? = resultField.get(scriptInstance)
|
val resultValue: Any? = resultField.get(scriptInstance)
|
||||||
|
|
||||||
return if (compileResult.hasResult) ReplEvalResult.ValueResult(evaluatedHistory.copySources(), resultValue)
|
return if (compileResult.hasResult) ReplEvalResult.ValueResult(resultValue)
|
||||||
else ReplEvalResult.UnitResult(evaluatedHistory.copySources())
|
else ReplEvalResult.UnitResult()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override val lastEvaluatedScripts: List<EvalHistoryType> get() {
|
|
||||||
return stateLock.read { evaluatedHistory.copyAll() }
|
|
||||||
}
|
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
private val SCRIPT_RESULT_FIELD_NAME = "\$\$result"
|
private val SCRIPT_RESULT_FIELD_NAME = "\$\$result"
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun makeReplClassLoader(baseClassloader: ClassLoader?, baseClasspath: Iterable<File>) =
|
|
||||||
ReplClassLoader(URLClassLoader(baseClasspath.map { it.toURI().toURL() }.toTypedArray(), baseClassloader))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private open class HistoryActionsForNoRepeat(val state: GenericReplEvaluatorState) {
|
||||||
|
|
||||||
|
open val effectiveHistory: List<EvalClassWithInstanceAndLoader> get() = state.history.map { it.item }
|
||||||
|
|
||||||
|
open fun<OtherT> firstMismatch(other: IReplStageHistory<OtherT>): Pair<ReplHistoryRecord<EvalClassWithInstanceAndLoader>?, ReplHistoryRecord<OtherT>?>? = state.history.firstMismatch(other)
|
||||||
|
|
||||||
|
open fun addPlaceholder(lineId: ILineId, value: EvalClassWithInstanceAndLoader) { state.history.push(lineId, value) }
|
||||||
|
|
||||||
|
open fun removePlaceholder(lineId: ILineId): Boolean = state.history.verifiedPop(lineId) != null
|
||||||
|
|
||||||
|
open fun addFinal(lineId: ILineId, value: EvalClassWithInstanceAndLoader) { state.history.push(lineId, value) }
|
||||||
|
|
||||||
|
open fun processClasses(compileResult: ReplCompileResult.CompiledClasses): Pair<ClassLoader, Class<out Any>> = prependClassLoaderWithNewClasses(effectiveHistory, compileResult)
|
||||||
|
|
||||||
|
private fun prependClassLoaderWithNewClasses(effectiveHistory: List<EvalClassWithInstanceAndLoader>,
|
||||||
|
compileResult: ReplCompileResult.CompiledClasses
|
||||||
|
): Pair<ClassLoader, Class<out Any>> {
|
||||||
|
var mainLineClassName: String? = null
|
||||||
|
val classLoader = makeReplClassLoader(effectiveHistory.lastOrNull()?.classLoader ?: state.topClassLoader, compileResult.classpathAddendum)
|
||||||
|
fun classNameFromPath(path: String) = JvmClassName.byInternalName(path.removeSuffix(".class"))
|
||||||
|
fun compiledClassesNames() = compileResult.classes.map { classNameFromPath(it.path).internalName.replace('/', '.') }
|
||||||
|
val expectedClassName = compileResult.mainClassName
|
||||||
|
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)
|
||||||
|
}
|
||||||
|
return Pair(classLoader, scriptClass)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private open class HistoryActionsForRepeatRecentOnly(state: GenericReplEvaluatorState) : HistoryActionsForNoRepeat(state) {
|
||||||
|
|
||||||
|
val currentLast = state.history.peek()!!
|
||||||
|
|
||||||
|
override val effectiveHistory: List<EvalClassWithInstanceAndLoader> get() = super.effectiveHistory.dropLast(1)
|
||||||
|
|
||||||
|
override fun<OtherT> firstMismatch(other: IReplStageHistory<OtherT>): Pair<ReplHistoryRecord<EvalClassWithInstanceAndLoader>?, ReplHistoryRecord<OtherT>?>? =
|
||||||
|
state.history.firstMismatchFiltered(other) { it.id != currentLast.id }
|
||||||
|
|
||||||
|
override fun addPlaceholder(lineId: ILineId, value: EvalClassWithInstanceAndLoader) {}
|
||||||
|
|
||||||
|
override fun removePlaceholder(lineId: ILineId): Boolean = true
|
||||||
|
|
||||||
|
override fun addFinal(lineId: ILineId, value: EvalClassWithInstanceAndLoader) {
|
||||||
|
state.history.pop()
|
||||||
|
state.history.push(lineId, value)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun processClasses(compileResult: ReplCompileResult.CompiledClasses): Pair<ClassLoader, Class<out Any>> =
|
||||||
|
currentLast.item.classLoader to currentLast.item.klass.java
|
||||||
|
}
|
||||||
|
|
||||||
|
private open class HistoryActionsForRepeatAny(state: GenericReplEvaluatorState, val matchingLine: ReplHistoryRecord<EvalClassWithInstanceAndLoader>): HistoryActionsForNoRepeat(state) {
|
||||||
|
|
||||||
|
override val effectiveHistory: List<EvalClassWithInstanceAndLoader> get() = state.history.takeWhile { it.id != matchingLine.id }.map { it.item }
|
||||||
|
|
||||||
|
override fun<OtherT> firstMismatch(other: IReplStageHistory<OtherT>): Pair<ReplHistoryRecord<EvalClassWithInstanceAndLoader>?, ReplHistoryRecord<OtherT>?>? =
|
||||||
|
state.history.firstMismatchWhile(other) { it.id != matchingLine.id }
|
||||||
|
|
||||||
|
override fun addPlaceholder(lineId: ILineId, value: EvalClassWithInstanceAndLoader) {}
|
||||||
|
|
||||||
|
override fun removePlaceholder(lineId: ILineId): Boolean = true
|
||||||
|
|
||||||
|
override fun addFinal(lineId: ILineId, value: EvalClassWithInstanceAndLoader) {
|
||||||
|
val extraLines = state.history.takeLastWhile { it.id == matchingLine.id }
|
||||||
|
state.history.resetTo(lineId)
|
||||||
|
state.history.pop()
|
||||||
|
state.history.push(lineId, value)
|
||||||
|
extraLines.forEach {
|
||||||
|
state.history.push(it.id, it.item)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun processClasses(compileResult: ReplCompileResult.CompiledClasses): Pair<ClassLoader, Class<out Any>> =
|
||||||
|
matchingLine.item.classLoader to matchingLine.item.klass.java
|
||||||
|
}
|
||||||
|
|||||||
+16
-20
@@ -19,16 +19,17 @@ package org.jetbrains.kotlin.cli.common.repl
|
|||||||
import org.jetbrains.kotlin.cli.common.messages.CompilerMessageLocation
|
import org.jetbrains.kotlin.cli.common.messages.CompilerMessageLocation
|
||||||
import java.io.Reader
|
import java.io.Reader
|
||||||
import java.util.concurrent.atomic.AtomicInteger
|
import java.util.concurrent.atomic.AtomicInteger
|
||||||
|
import java.util.concurrent.locks.ReentrantReadWriteLock
|
||||||
import javax.script.*
|
import javax.script.*
|
||||||
|
|
||||||
val KOTLIN_SCRIPT_HISTORY_BINDINGS_KEY = "kotlin.script.history"
|
val KOTLIN_SCRIPT_STATE_BINDINGS_KEY = "kotlin.script.state"
|
||||||
val KOTLIN_SCRIPT_LINE_NUMBER_BINDINGS_KEY = "kotlin.script.line.number"
|
val KOTLIN_SCRIPT_LINE_NUMBER_BINDINGS_KEY = "kotlin.script.line.number"
|
||||||
|
|
||||||
// TODO consider additional error handling
|
// TODO consider additional error handling
|
||||||
var Bindings.kotlinScriptHistory: MutableList<ReplCodeLine>
|
var Bindings.kotlinScriptState: IReplStageState<*>
|
||||||
@Suppress("UNCHECKED_CAST")
|
@Suppress("UNCHECKED_CAST")
|
||||||
get() = getOrPut(KOTLIN_SCRIPT_HISTORY_BINDINGS_KEY, { arrayListOf<ReplCodeLine>() }) as MutableList<ReplCodeLine>
|
get() = get(KOTLIN_SCRIPT_STATE_BINDINGS_KEY) as IReplStageState<*>
|
||||||
set(v) { put(KOTLIN_SCRIPT_HISTORY_BINDINGS_KEY, v) }
|
set(v) { put(KOTLIN_SCRIPT_STATE_BINDINGS_KEY, v) }
|
||||||
|
|
||||||
val Bindings.kotlinScriptLineNumber: AtomicInteger
|
val Bindings.kotlinScriptLineNumber: AtomicInteger
|
||||||
@Suppress("UNCHECKED_CAST")
|
@Suppress("UNCHECKED_CAST")
|
||||||
@@ -36,8 +37,8 @@ val Bindings.kotlinScriptLineNumber: AtomicInteger
|
|||||||
|
|
||||||
abstract class KotlinJsr223JvmScriptEngineBase(protected val myFactory: ScriptEngineFactory) : AbstractScriptEngine(), ScriptEngine, Compilable {
|
abstract class KotlinJsr223JvmScriptEngineBase(protected val myFactory: ScriptEngineFactory) : AbstractScriptEngine(), ScriptEngine, Compilable {
|
||||||
|
|
||||||
protected abstract val replCompiler: ReplCompileAction
|
protected abstract val replCompiler: ReplCompiler
|
||||||
protected abstract val replScriptEvaluator: ReplFullEvaluator
|
protected abstract val replEvaluator: ReplFullEvaluator
|
||||||
|
|
||||||
override fun eval(script: String, context: ScriptContext): Any? = compileAndEval(script, context)
|
override fun eval(script: String, context: ScriptContext): Any? = compileAndEval(script, context)
|
||||||
|
|
||||||
@@ -52,20 +53,18 @@ abstract class KotlinJsr223JvmScriptEngineBase(protected val myFactory: ScriptEn
|
|||||||
override fun getFactory(): ScriptEngineFactory = myFactory
|
override fun getFactory(): ScriptEngineFactory = myFactory
|
||||||
|
|
||||||
// the parameter could be used in the future when we decide to keep state completely in the context and solve appropriate problems (now e.g. replCompiler keeps separate state)
|
// the parameter could be used in the future when we decide to keep state completely in the context and solve appropriate problems (now e.g. replCompiler keeps separate state)
|
||||||
fun nextCodeLine(@Suppress("UNUSED_PARAMETER") context: ScriptContext, code: String) = ReplCodeLine(this.context.getBindings(ScriptContext.ENGINE_SCOPE).kotlinScriptLineNumber.incrementAndGet(), code)
|
fun nextCodeLine(context: ScriptContext, code: String) = ReplCodeLine(context.getBindings(ScriptContext.ENGINE_SCOPE).kotlinScriptLineNumber.incrementAndGet(), code)
|
||||||
|
|
||||||
private fun getCurrentHistory(@Suppress("UNUSED_PARAMETER") context: ScriptContext) = this.context.getBindings(ScriptContext.ENGINE_SCOPE).kotlinScriptHistory
|
protected open fun createState(lock: ReentrantReadWriteLock = ReentrantReadWriteLock()): IReplStageState<*> = AggregatedReplStageState(replCompiler.createState(lock), replEvaluator.createState(lock), lock)
|
||||||
|
|
||||||
private fun setContextHistory(@Suppress("UNUSED_PARAMETER") context: ScriptContext, history: ArrayList<ReplCodeLine>) {
|
private fun getCurrentState(context: ScriptContext) = context.getBindings(ScriptContext.ENGINE_SCOPE).getOrPut(KOTLIN_SCRIPT_STATE_BINDINGS_KEY, { replEvaluator.createState() }) as IReplStageState<*>
|
||||||
this.context.getBindings(ScriptContext.ENGINE_SCOPE).kotlinScriptHistory = history
|
|
||||||
}
|
|
||||||
|
|
||||||
open fun overrideScriptArgs(context: ScriptContext): ScriptArgsWithTypes? = null
|
open fun overrideScriptArgs(context: ScriptContext): ScriptArgsWithTypes? = null
|
||||||
|
|
||||||
open fun compileAndEval(script: String, context: ScriptContext): Any? {
|
open fun compileAndEval(script: String, context: ScriptContext): Any? {
|
||||||
val codeLine = nextCodeLine(context, script)
|
val codeLine = nextCodeLine(context, script)
|
||||||
val history = getCurrentHistory(context)
|
val state = getCurrentState(context)
|
||||||
val result = replScriptEvaluator.compileAndEval(codeLine, scriptArgs = overrideScriptArgs(context), verifyHistory = history)
|
val result = replEvaluator.compileAndEval(state, codeLine, scriptArgs = overrideScriptArgs(context))
|
||||||
val ret = when (result) {
|
val ret = when (result) {
|
||||||
is ReplEvalResult.ValueResult -> result.value
|
is ReplEvalResult.ValueResult -> result.value
|
||||||
is ReplEvalResult.UnitResult -> null
|
is ReplEvalResult.UnitResult -> null
|
||||||
@@ -73,29 +72,27 @@ abstract class KotlinJsr223JvmScriptEngineBase(protected val myFactory: ScriptEn
|
|||||||
is ReplEvalResult.Incomplete -> throw ScriptException("error: incomplete code")
|
is ReplEvalResult.Incomplete -> throw ScriptException("error: incomplete code")
|
||||||
is ReplEvalResult.HistoryMismatch -> throw ScriptException("Repl history mismatch at line: ${result.lineNo}")
|
is ReplEvalResult.HistoryMismatch -> throw ScriptException("Repl history mismatch at line: ${result.lineNo}")
|
||||||
}
|
}
|
||||||
setContextHistory(context, ArrayList(result.completedEvalHistory))
|
|
||||||
return ret
|
return ret
|
||||||
}
|
}
|
||||||
|
|
||||||
open fun compile(script: String, context: ScriptContext): CompiledScript {
|
open fun compile(script: String, context: ScriptContext): CompiledScript {
|
||||||
val codeLine = nextCodeLine(context, script)
|
val codeLine = nextCodeLine(context, script)
|
||||||
val history = getCurrentHistory(context)
|
val state = getCurrentState(context)
|
||||||
|
|
||||||
val result = replCompiler.compile(codeLine, history)
|
val result = replCompiler.compile(state, codeLine)
|
||||||
val compiled = when (result) {
|
val compiled = when (result) {
|
||||||
is ReplCompileResult.Error -> throw ScriptException("Error${result.locationString()}: ${result.message}")
|
is ReplCompileResult.Error -> throw ScriptException("Error${result.locationString()}: ${result.message}")
|
||||||
is ReplCompileResult.Incomplete -> throw ScriptException("error: incomplete code")
|
is ReplCompileResult.Incomplete -> throw ScriptException("error: incomplete code")
|
||||||
is ReplCompileResult.HistoryMismatch -> throw ScriptException("Repl history mismatch at line: ${result.lineNo}")
|
is ReplCompileResult.HistoryMismatch -> throw ScriptException("Repl history mismatch at line: ${result.lineNo}")
|
||||||
is ReplCompileResult.CompiledClasses -> result
|
is ReplCompileResult.CompiledClasses -> result
|
||||||
}
|
}
|
||||||
// TODO: check if it is ok to keep compiled history in the same place as compiledEval one
|
|
||||||
setContextHistory(context, ArrayList(result.compiledHistory))
|
|
||||||
return CompiledKotlinScript(this, codeLine, compiled)
|
return CompiledKotlinScript(this, codeLine, compiled)
|
||||||
}
|
}
|
||||||
|
|
||||||
open fun eval(compiledScript: CompiledKotlinScript, context: ScriptContext): Any? {
|
open fun eval(compiledScript: CompiledKotlinScript, context: ScriptContext): Any? {
|
||||||
|
val state = getCurrentState(context)
|
||||||
val result = try {
|
val result = try {
|
||||||
replScriptEvaluator.eval(compiledScript.compiledData, scriptArgs = overrideScriptArgs(context))
|
replEvaluator.eval(state, compiledScript.compiledData, scriptArgs = overrideScriptArgs(context))
|
||||||
}
|
}
|
||||||
catch (e: Exception) {
|
catch (e: Exception) {
|
||||||
throw ScriptException(e)
|
throw ScriptException(e)
|
||||||
@@ -108,7 +105,6 @@ abstract class KotlinJsr223JvmScriptEngineBase(protected val myFactory: ScriptEn
|
|||||||
is ReplEvalResult.Incomplete -> throw ScriptException("error: incomplete code")
|
is ReplEvalResult.Incomplete -> throw ScriptException("error: incomplete code")
|
||||||
is ReplEvalResult.HistoryMismatch -> throw ScriptException("Repl history mismatch at line: ${result.lineNo}")
|
is ReplEvalResult.HistoryMismatch -> throw ScriptException("Repl history mismatch at line: ${result.lineNo}")
|
||||||
}
|
}
|
||||||
setContextHistory(context, ArrayList(result.completedEvalHistory))
|
|
||||||
return ret
|
return ret
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -20,6 +20,7 @@ import org.jetbrains.kotlin.cli.common.messages.CompilerMessageLocation
|
|||||||
import java.io.File
|
import java.io.File
|
||||||
import java.io.Serializable
|
import java.io.Serializable
|
||||||
import java.util.*
|
import java.util.*
|
||||||
|
import java.util.concurrent.locks.ReentrantReadWriteLock
|
||||||
import kotlin.reflect.KClass
|
import kotlin.reflect.KClass
|
||||||
|
|
||||||
data class ReplCodeLine(val no: Int, val code: String) : Serializable {
|
data class ReplCodeLine(val no: Int, val code: String) : Serializable {
|
||||||
@@ -43,9 +44,14 @@ data class CompiledClassData(val path: String, val bytes: ByteArray) : Serializa
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
interface CreateReplStageStateAction {
|
||||||
|
fun createState(lock: ReentrantReadWriteLock = ReentrantReadWriteLock()): IReplStageState<*>
|
||||||
|
}
|
||||||
|
|
||||||
|
// --- check
|
||||||
|
|
||||||
interface ReplCheckAction {
|
interface ReplCheckAction {
|
||||||
fun check(codeLine: ReplCodeLine): ReplCheckResult
|
fun check(state: IReplStageState<*>, codeLine: ReplCodeLine): ReplCheckResult
|
||||||
}
|
}
|
||||||
|
|
||||||
sealed class ReplCheckResult : Serializable {
|
sealed class ReplCheckResult : Serializable {
|
||||||
@@ -63,40 +69,25 @@ sealed class ReplCheckResult : Serializable {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
interface ReplResettableCodeLine {
|
// --- compile
|
||||||
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 {
|
interface ReplCompileAction {
|
||||||
fun compile(codeLine: ReplCodeLine, verifyHistory: List<ReplCodeLine>? = null): ReplCompileResult
|
fun compile(state: IReplStageState<*>, codeLine: ReplCodeLine): ReplCompileResult
|
||||||
}
|
}
|
||||||
|
|
||||||
sealed class ReplCompileResult(val compiledHistory: List<ReplCodeLine>) : Serializable {
|
sealed class ReplCompileResult : Serializable {
|
||||||
class CompiledClasses(compiledHistory: List<ReplCodeLine>,
|
class CompiledClasses(val lineId: LineId,
|
||||||
val compiledCodeLine: CompiledReplCodeLine,
|
val mainClassName: String,
|
||||||
val generatedClassname: String,
|
|
||||||
val classes: List<CompiledClassData>,
|
val classes: List<CompiledClassData>,
|
||||||
val hasResult: Boolean,
|
val hasResult: Boolean,
|
||||||
val classpathAddendum: List<File>) : ReplCompileResult(compiledHistory)
|
val classpathAddendum: List<File>) : ReplCompileResult()
|
||||||
|
|
||||||
class Incomplete(compiledHistory: List<ReplCodeLine>) : ReplCompileResult(compiledHistory)
|
class Incomplete : ReplCompileResult()
|
||||||
|
|
||||||
class HistoryMismatch(compiledHistory: List<ReplCodeLine>, val lineNo: Int) : ReplCompileResult(compiledHistory)
|
class HistoryMismatch(val lineNo: Int) : ReplCompileResult()
|
||||||
|
|
||||||
class Error(compiledHistory: List<ReplCodeLine>,
|
class Error(val message: String,
|
||||||
val message: String,
|
val location: CompilerMessageLocation = CompilerMessageLocation.NO_LOCATION) : ReplCompileResult() {
|
||||||
val location: CompilerMessageLocation = CompilerMessageLocation.NO_LOCATION) : ReplCompileResult(compiledHistory) {
|
|
||||||
override fun toString(): String = "Error(message = \"$message\""
|
override fun toString(): String = "Error(message = \"$message\""
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -105,43 +96,35 @@ sealed class ReplCompileResult(val compiledHistory: List<ReplCodeLine>) : Serial
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
interface ReplCompiler : ReplResettableCodeLine, ReplCodeLineHistory, ReplCompileAction, ReplCheckAction
|
interface ReplCompiler : ReplCompileAction, ReplCheckAction, CreateReplStageStateAction
|
||||||
|
|
||||||
typealias EvalHistoryType = Pair<CompiledReplCodeLine, EvalClassWithInstanceAndLoader>
|
// --- eval
|
||||||
|
|
||||||
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?)
|
data class EvalClassWithInstanceAndLoader(val klass: KClass<*>, val instance: Any?, val classLoader: ClassLoader, val invokeWrapper: InvokeWrapper?)
|
||||||
|
|
||||||
interface ReplEvalAction {
|
interface ReplEvalAction {
|
||||||
fun eval(compileResult: ReplCompileResult.CompiledClasses,
|
fun eval(state: IReplStageState<*>,
|
||||||
|
compileResult: ReplCompileResult.CompiledClasses,
|
||||||
scriptArgs: ScriptArgsWithTypes? = null,
|
scriptArgs: ScriptArgsWithTypes? = null,
|
||||||
invokeWrapper: InvokeWrapper? = null): ReplEvalResult
|
invokeWrapper: InvokeWrapper? = null): ReplEvalResult
|
||||||
}
|
}
|
||||||
|
|
||||||
sealed class ReplEvalResult(val completedEvalHistory: List<ReplCodeLine>) : Serializable {
|
sealed class ReplEvalResult : Serializable {
|
||||||
class ValueResult(completedEvalHistory: List<ReplCodeLine>, val value: Any?) : ReplEvalResult(completedEvalHistory) {
|
class ValueResult(val value: Any?) : ReplEvalResult() {
|
||||||
override fun toString(): String = "Result: $value"
|
override fun toString(): String = "Result: $value"
|
||||||
}
|
}
|
||||||
|
|
||||||
class UnitResult(completedEvalHistory: List<ReplCodeLine>) : ReplEvalResult(completedEvalHistory)
|
class UnitResult : ReplEvalResult()
|
||||||
|
|
||||||
class Incomplete(completedEvalHistory: List<ReplCodeLine>) : ReplEvalResult(completedEvalHistory)
|
class Incomplete : ReplEvalResult()
|
||||||
|
|
||||||
class HistoryMismatch(completedEvalHistory: List<ReplCodeLine>, val lineNo: Int) : ReplEvalResult(completedEvalHistory)
|
class HistoryMismatch(val lineNo: Int) : ReplEvalResult()
|
||||||
|
|
||||||
sealed class Error(completedEvalHistory: List<ReplCodeLine>, val message: String) : ReplEvalResult(completedEvalHistory) {
|
sealed class Error(val message: String) : ReplEvalResult() {
|
||||||
class Runtime(completedEvalHistory: List<ReplCodeLine>, message: String, val cause: Exception? = null) : Error(completedEvalHistory, message)
|
class Runtime(message: String, val cause: Exception? = null) : Error(message)
|
||||||
|
|
||||||
class CompileTime(completedEvalHistory: List<ReplCodeLine>,
|
class CompileTime(message: String,
|
||||||
message: String,
|
val location: CompilerMessageLocation = CompilerMessageLocation.NO_LOCATION) : Error(message)
|
||||||
val location: CompilerMessageLocation = CompilerMessageLocation.NO_LOCATION) : Error(completedEvalHistory, message)
|
|
||||||
|
|
||||||
override fun toString(): String = "${this::class.simpleName}Error(message = \"$message\""
|
override fun toString(): String = "${this::class.simpleName}Error(message = \"$message\""
|
||||||
}
|
}
|
||||||
@@ -151,43 +134,44 @@ sealed class ReplEvalResult(val completedEvalHistory: List<ReplCodeLine>) : Seri
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
interface ReplEvaluator : ReplResettableCodeLine, ReplCodeLineHistory, ReplEvaluatorExposedInternalHistory, ReplEvalAction, ReplClasspath
|
interface ReplEvaluator : ReplEvalAction, CreateReplStageStateAction
|
||||||
|
|
||||||
|
// --- compileAdnEval
|
||||||
|
|
||||||
interface ReplAtomicEvalAction {
|
interface ReplAtomicEvalAction {
|
||||||
fun compileAndEval(codeLine: ReplCodeLine,
|
fun compileAndEval(state: IReplStageState<*>,
|
||||||
|
codeLine: ReplCodeLine,
|
||||||
scriptArgs: ScriptArgsWithTypes? = null,
|
scriptArgs: ScriptArgsWithTypes? = null,
|
||||||
verifyHistory: List<ReplCodeLine>? = null,
|
|
||||||
invokeWrapper: InvokeWrapper? = null): ReplEvalResult
|
invokeWrapper: InvokeWrapper? = null): ReplEvalResult
|
||||||
}
|
}
|
||||||
|
|
||||||
interface ReplAtomicEvaluator : ReplResettableCodeLine, ReplCombinedHistory, ReplEvaluatorExposedInternalHistory, ReplAtomicEvalAction, ReplCheckAction, ReplClasspath
|
interface ReplAtomicEvaluator : ReplAtomicEvalAction, ReplCheckAction
|
||||||
|
|
||||||
interface ReplDelayedEvalAction {
|
interface ReplDelayedEvalAction {
|
||||||
fun compileToEvaluable(codeLine: ReplCodeLine, defaultScriptArgs: ScriptArgsWithTypes? = null, verifyHistory: List<ReplCodeLine>?): Pair<ReplCompileResult, Evaluable?>
|
fun compileToEvaluable(state: IReplStageState<*>,
|
||||||
|
codeLine: ReplCodeLine,
|
||||||
|
defaultScriptArgs: ScriptArgsWithTypes? = null): Pair<ReplCompileResult, Evaluable?>
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// other
|
||||||
|
|
||||||
interface Evaluable {
|
interface Evaluable {
|
||||||
val compiledCode: ReplCompileResult.CompiledClasses
|
val compiledCode: ReplCompileResult.CompiledClasses
|
||||||
fun eval(scriptArgs: ScriptArgsWithTypes? = null, invokeWrapper: InvokeWrapper? = null): ReplEvalResult
|
fun eval(scriptArgs: ScriptArgsWithTypes? = null, invokeWrapper: InvokeWrapper? = null): ReplEvalResult
|
||||||
}
|
}
|
||||||
|
|
||||||
interface ReplFullEvaluator : ReplEvaluator, ReplAtomicEvaluator, ReplDelayedEvalAction, ReplCombinedHistory
|
interface ReplFullEvaluator : ReplEvaluator, ReplAtomicEvaluator, ReplDelayedEvalAction
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Keep args and arg types together, so as a whole they are present or absent
|
* 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 {
|
class ScriptArgsWithTypes(val scriptArgs: Array<out Any?>, val scriptArgsTypes: Array<out KClass<out Any>>) : Serializable {
|
||||||
|
init { assert(scriptArgs.size == scriptArgsTypes.size) }
|
||||||
companion object {
|
companion object {
|
||||||
private val serialVersionUID: Long = 8529357500L
|
private val serialVersionUID: Long = 8529357500L
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
interface ScriptTemplateEmptyArgsProvider {
|
|
||||||
val defaultEmptyArgs: ScriptArgsWithTypes?
|
|
||||||
}
|
|
||||||
|
|
||||||
class SimpleScriptTemplateEmptyArgsProvider(override val defaultEmptyArgs: ScriptArgsWithTypes? = null) : ScriptTemplateEmptyArgsProvider
|
|
||||||
|
|
||||||
enum class ReplRepeatingMode {
|
enum class ReplRepeatingMode {
|
||||||
NONE,
|
NONE,
|
||||||
REPEAT_ONLY_MOST_RECENT,
|
REPEAT_ONLY_MOST_RECENT,
|
||||||
|
|||||||
@@ -0,0 +1,83 @@
|
|||||||
|
/*
|
||||||
|
* 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.util.concurrent.locks.ReentrantReadWriteLock
|
||||||
|
import kotlin.concurrent.read
|
||||||
|
import kotlin.concurrent.write
|
||||||
|
|
||||||
|
|
||||||
|
interface ILineId : Comparable<ILineId> {
|
||||||
|
val no: Int
|
||||||
|
}
|
||||||
|
|
||||||
|
data class ReplHistoryRecord<out T> (val id: ILineId, val item: T)
|
||||||
|
|
||||||
|
interface IReplStageHistory<T> : List<ReplHistoryRecord<T>> {
|
||||||
|
|
||||||
|
fun backwardIterator(): Iterator<ReplHistoryRecord<T>> = lock.read { asReversed().iterator() } // TODO: check perf
|
||||||
|
|
||||||
|
fun peek(): ReplHistoryRecord<T>? = lock.read { lastOrNull() }
|
||||||
|
|
||||||
|
fun push(id: ILineId, item: T)
|
||||||
|
|
||||||
|
fun pop(): ReplHistoryRecord<T>?
|
||||||
|
|
||||||
|
fun verifiedPop(id: ILineId): ReplHistoryRecord<T>? = lock.write {
|
||||||
|
if (lastOrNull()?.id == id) pop()
|
||||||
|
else null
|
||||||
|
}
|
||||||
|
|
||||||
|
fun resetTo(id: ILineId): Iterable<ILineId>
|
||||||
|
|
||||||
|
fun <OtherT> firstMismatch(other: IReplStageHistory<OtherT>): Pair<ReplHistoryRecord<T>?, ReplHistoryRecord<OtherT>?>? =
|
||||||
|
lock.read { other.lock.read {
|
||||||
|
iterator().asSequence().zip(other.asSequence()).firstOrNull { it.first.id != it.second.id }?.let { it.first to it.second }
|
||||||
|
} }
|
||||||
|
|
||||||
|
fun<OtherT> firstMismatchFiltered(other: IReplStageHistory<OtherT>, predicate: (ReplHistoryRecord<T>) -> Boolean): Pair<ReplHistoryRecord<T>?, ReplHistoryRecord<OtherT>?>? =
|
||||||
|
lock.read { other.lock.read {
|
||||||
|
iterator().asSequence().filter(predicate).zip(other.asSequence()).firstOrNull { it.first.id != it.second.id }?.let { it.first to it.second }
|
||||||
|
} }
|
||||||
|
|
||||||
|
fun<OtherT> firstMismatchWhile(other: IReplStageHistory<OtherT>, predicate: (ReplHistoryRecord<T>) -> Boolean): Pair<ReplHistoryRecord<T>?, ReplHistoryRecord<OtherT>?>? =
|
||||||
|
lock.read { other.lock.read {
|
||||||
|
iterator().asSequence().takeWhile(predicate).zip(other.asSequence()).firstOrNull { it.first.id != it.second.id }?.let { it.first to it.second }
|
||||||
|
} }
|
||||||
|
|
||||||
|
fun <OtherT> matches(other: IReplStageHistory<OtherT>): Boolean =
|
||||||
|
lock.read { other.lock.read {
|
||||||
|
size == other.size && firstMismatch(other) == null
|
||||||
|
} }
|
||||||
|
|
||||||
|
fun <OtherT> isProperPrefix(other: IReplStageHistory<OtherT>): Boolean =
|
||||||
|
lock.read { other.lock.read {
|
||||||
|
size == other.size - 1 && firstMismatch(other) == null
|
||||||
|
} }
|
||||||
|
|
||||||
|
val lock: ReentrantReadWriteLock
|
||||||
|
}
|
||||||
|
|
||||||
|
interface IReplStageState<T> {
|
||||||
|
val history: IReplStageHistory<T>
|
||||||
|
|
||||||
|
val lock: ReentrantReadWriteLock
|
||||||
|
|
||||||
|
fun <StateT : IReplStageState<*>> asState(): StateT = (this as? StateT) ?: throw IllegalArgumentException("$this is not an expected instance of IReplStageState")
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -0,0 +1,63 @@
|
|||||||
|
/*
|
||||||
|
* 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.jvm.repl
|
||||||
|
|
||||||
|
import org.jetbrains.kotlin.cli.common.repl.*
|
||||||
|
import org.jetbrains.kotlin.cli.jvm.compiler.KotlinCoreEnvironment
|
||||||
|
import org.jetbrains.kotlin.cli.jvm.repl.messages.DiagnosticMessageHolder
|
||||||
|
import org.jetbrains.kotlin.descriptors.ScriptDescriptor
|
||||||
|
import org.jetbrains.kotlin.psi.KtFile
|
||||||
|
import org.jetbrains.kotlin.script.KotlinScriptExternalDependencies
|
||||||
|
import java.util.concurrent.atomic.AtomicLong
|
||||||
|
import java.util.concurrent.locks.ReentrantReadWriteLock
|
||||||
|
|
||||||
|
class ReplCompilerStageHistory(private val state: GenericReplCompilerState) : BasicReplStageHistory<ScriptDescriptor>(state.lock) {
|
||||||
|
|
||||||
|
override fun resetTo(id: ILineId): Iterable<ILineId> {
|
||||||
|
state.generation.incrementAndGet()
|
||||||
|
val removedCompiledLines = super.resetTo(id)
|
||||||
|
val removedAnalyzedLines = state.analyzerEngine.resetToLine(id.no)
|
||||||
|
|
||||||
|
removedCompiledLines.zip(removedAnalyzedLines).forEach {
|
||||||
|
if (it.first != LineId(it.second)) {
|
||||||
|
throw IllegalStateException("History mismatch when resetting lines: ${it.first.no} != ${it.second}")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return removedCompiledLines
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
abstract class GenericReplCheckerState: IReplStageState<ScriptDescriptor> {
|
||||||
|
|
||||||
|
// "line" - is the unit of evaluation here, could in fact consists of several character lines
|
||||||
|
class LineState(
|
||||||
|
val codeLine: ReplCodeLine,
|
||||||
|
val psiFile: KtFile,
|
||||||
|
val errorHolder: DiagnosticMessageHolder)
|
||||||
|
|
||||||
|
val generation = AtomicLong(1)
|
||||||
|
var lastLineState: LineState? = null // for transferring state to the compiler in most typical case
|
||||||
|
}
|
||||||
|
|
||||||
|
class GenericReplCompilerState(environment: KotlinCoreEnvironment, override val lock: ReentrantReadWriteLock = ReentrantReadWriteLock()) : IReplStageState<ScriptDescriptor>, GenericReplCheckerState() {
|
||||||
|
|
||||||
|
override val history: IReplStageHistory<ScriptDescriptor> = ReplCompilerStageHistory(this)
|
||||||
|
|
||||||
|
val analyzerEngine = ReplCodeAnalyzer(environment)
|
||||||
|
|
||||||
|
var lastDependencies: KotlinScriptExternalDependencies? = null
|
||||||
|
}
|
||||||
@@ -22,8 +22,6 @@ import org.jetbrains.kotlin.cli.common.repl.*
|
|||||||
import org.jetbrains.kotlin.cli.jvm.config.jvmClasspathRoots
|
import org.jetbrains.kotlin.cli.jvm.config.jvmClasspathRoots
|
||||||
import org.jetbrains.kotlin.config.CompilerConfiguration
|
import org.jetbrains.kotlin.config.CompilerConfiguration
|
||||||
import org.jetbrains.kotlin.script.KotlinScriptDefinition
|
import org.jetbrains.kotlin.script.KotlinScriptDefinition
|
||||||
import java.io.File
|
|
||||||
import java.util.concurrent.atomic.AtomicInteger
|
|
||||||
import java.util.concurrent.locks.ReentrantReadWriteLock
|
import java.util.concurrent.locks.ReentrantReadWriteLock
|
||||||
|
|
||||||
open class GenericRepl protected constructor(
|
open class GenericRepl protected constructor(
|
||||||
@@ -32,56 +30,22 @@ open class GenericRepl protected constructor(
|
|||||||
compilerConfiguration: CompilerConfiguration,
|
compilerConfiguration: CompilerConfiguration,
|
||||||
messageCollector: MessageCollector,
|
messageCollector: MessageCollector,
|
||||||
baseClassloader: ClassLoader?,
|
baseClassloader: ClassLoader?,
|
||||||
protected val fallbackScriptArgs: ScriptArgsWithTypes?,
|
protected val fallbackScriptArgs: ScriptArgsWithTypes? = null,
|
||||||
protected val repeatingMode: ReplRepeatingMode = ReplRepeatingMode.NONE,
|
protected val repeatingMode: ReplRepeatingMode = ReplRepeatingMode.NONE
|
||||||
protected val stateLock: ReentrantReadWriteLock
|
|
||||||
) : ReplCompiler, ReplEvaluator, ReplAtomicEvaluator {
|
) : 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())
|
|
||||||
|
|
||||||
protected val compiler: ReplCompiler by lazy { GenericReplCompiler(disposable, scriptDefinition, compilerConfiguration, messageCollector, stateLock) }
|
protected val compiler: ReplCompiler by lazy { GenericReplCompiler(disposable, scriptDefinition, compilerConfiguration, messageCollector) }
|
||||||
protected val evaluator: ReplFullEvaluator by lazy { GenericReplCompilingEvaluator(compiler, compilerConfiguration.jvmClasspathRoots, baseClassloader, fallbackScriptArgs, repeatingMode, stateLock) }
|
protected val evaluator: ReplFullEvaluator by lazy { GenericReplCompilingEvaluator(compiler, compilerConfiguration.jvmClasspathRoots, baseClassloader, fallbackScriptArgs, repeatingMode) }
|
||||||
protected var codeLineNumber = AtomicInteger(0)
|
|
||||||
|
|
||||||
override fun resetToLine(lineNumber: Int): List<ReplCodeLine> {
|
override fun createState(lock: ReentrantReadWriteLock): IReplStageState<*> = AggregatedReplStageState(compiler.createState(lock), evaluator.createState(lock), lock)
|
||||||
return evaluator.resetToLine(lineNumber)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun resetToLine(line: ReplCodeLine): List<ReplCodeLine> {
|
override fun check(state: IReplStageState<*>, codeLine: ReplCodeLine): ReplCheckResult = compiler.check(state, codeLine)
|
||||||
return evaluator.resetToLine(line)
|
|
||||||
}
|
|
||||||
|
|
||||||
override val history: List<ReplCodeLine> get() = evaluator.history
|
override fun compile(state: IReplStageState<*>, codeLine: ReplCodeLine): ReplCompileResult = compiler.compile(state, codeLine)
|
||||||
override val compiledHistory: List<ReplCodeLine> get() = compiler.history
|
|
||||||
override val evaluatedHistory: List<ReplCodeLine> get() = evaluator.history
|
|
||||||
|
|
||||||
override val currentClasspath: List<File>
|
override fun eval(state: IReplStageState<*>, compileResult: ReplCompileResult.CompiledClasses, scriptArgs: ScriptArgsWithTypes?, invokeWrapper: InvokeWrapper?): ReplEvalResult =
|
||||||
get() = evaluator.currentClasspath
|
evaluator.eval(state, compileResult, scriptArgs, invokeWrapper)
|
||||||
|
|
||||||
override val lastEvaluatedScripts: List<EvalHistoryType>
|
override fun compileAndEval(state: IReplStageState<*>, codeLine: ReplCodeLine, scriptArgs: ScriptArgsWithTypes?, invokeWrapper: InvokeWrapper?): ReplEvalResult =
|
||||||
get() = evaluator.lastEvaluatedScripts
|
evaluator.compileAndEval(state, codeLine, scriptArgs, invokeWrapper)
|
||||||
|
|
||||||
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)
|
|
||||||
}
|
}
|
||||||
@@ -26,22 +26,18 @@ import com.intellij.testFramework.LightVirtualFile
|
|||||||
import org.jetbrains.kotlin.cli.common.CLIConfigurationKeys
|
import org.jetbrains.kotlin.cli.common.CLIConfigurationKeys
|
||||||
import org.jetbrains.kotlin.cli.common.messages.AnalyzerWithCompilerReport
|
import org.jetbrains.kotlin.cli.common.messages.AnalyzerWithCompilerReport
|
||||||
import org.jetbrains.kotlin.cli.common.messages.MessageCollector
|
import org.jetbrains.kotlin.cli.common.messages.MessageCollector
|
||||||
import org.jetbrains.kotlin.cli.common.repl.ReplCheckResult
|
import org.jetbrains.kotlin.cli.common.repl.*
|
||||||
import org.jetbrains.kotlin.cli.common.repl.ReplCodeLine
|
|
||||||
import org.jetbrains.kotlin.cli.common.repl.makeScriptBaseName
|
|
||||||
import org.jetbrains.kotlin.cli.jvm.compiler.EnvironmentConfigFiles
|
import org.jetbrains.kotlin.cli.jvm.compiler.EnvironmentConfigFiles
|
||||||
import org.jetbrains.kotlin.cli.jvm.compiler.KotlinCoreEnvironment
|
import org.jetbrains.kotlin.cli.jvm.compiler.KotlinCoreEnvironment
|
||||||
import org.jetbrains.kotlin.cli.jvm.repl.messages.DiagnosticMessageHolder
|
|
||||||
import org.jetbrains.kotlin.cli.jvm.repl.messages.ReplTerminalDiagnosticMessageHolder
|
import org.jetbrains.kotlin.cli.jvm.repl.messages.ReplTerminalDiagnosticMessageHolder
|
||||||
import org.jetbrains.kotlin.config.CompilerConfiguration
|
import org.jetbrains.kotlin.config.CompilerConfiguration
|
||||||
import org.jetbrains.kotlin.config.JVMConfigurationKeys
|
import org.jetbrains.kotlin.config.JVMConfigurationKeys
|
||||||
import org.jetbrains.kotlin.config.JvmTarget
|
import org.jetbrains.kotlin.config.JvmTarget
|
||||||
|
import org.jetbrains.kotlin.descriptors.ScriptDescriptor
|
||||||
import org.jetbrains.kotlin.idea.KotlinLanguage
|
import org.jetbrains.kotlin.idea.KotlinLanguage
|
||||||
import org.jetbrains.kotlin.parsing.KotlinParserDefinition
|
import org.jetbrains.kotlin.parsing.KotlinParserDefinition
|
||||||
import org.jetbrains.kotlin.psi.KtFile
|
import org.jetbrains.kotlin.psi.KtFile
|
||||||
import org.jetbrains.kotlin.script.KotlinScriptDefinition
|
import org.jetbrains.kotlin.script.KotlinScriptDefinition
|
||||||
import java.util.concurrent.locks.ReentrantReadWriteLock
|
|
||||||
import kotlin.concurrent.read
|
|
||||||
import kotlin.concurrent.write
|
import kotlin.concurrent.write
|
||||||
|
|
||||||
const val KOTLIN_REPL_JVM_TARGET_PROPERTY = "kotlin.repl.jvm.target"
|
const val KOTLIN_REPL_JVM_TARGET_PROPERTY = "kotlin.repl.jvm.target"
|
||||||
@@ -50,9 +46,9 @@ open class GenericReplChecker(
|
|||||||
disposable: Disposable,
|
disposable: Disposable,
|
||||||
val scriptDefinition: KotlinScriptDefinition,
|
val scriptDefinition: KotlinScriptDefinition,
|
||||||
val compilerConfiguration: CompilerConfiguration,
|
val compilerConfiguration: CompilerConfiguration,
|
||||||
messageCollector: MessageCollector,
|
messageCollector: MessageCollector
|
||||||
protected val stateLock: ReentrantReadWriteLock = ReentrantReadWriteLock()
|
) : ReplCheckAction {
|
||||||
) {
|
|
||||||
internal val environment = run {
|
internal val environment = run {
|
||||||
compilerConfiguration.apply {
|
compilerConfiguration.apply {
|
||||||
add(JVMConfigurationKeys.SCRIPT_DEFINITIONS, scriptDefinition)
|
add(JVMConfigurationKeys.SCRIPT_DEFINITIONS, scriptDefinition)
|
||||||
@@ -70,21 +66,12 @@ open class GenericReplChecker(
|
|||||||
|
|
||||||
private 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
|
internal fun createDiagnosticHolder() = ReplTerminalDiagnosticMessageHolder()
|
||||||
internal class LineState(
|
|
||||||
val codeLine: ReplCodeLine,
|
|
||||||
val psiFile: KtFile,
|
|
||||||
val errorHolder: DiagnosticMessageHolder)
|
|
||||||
|
|
||||||
private var _lineState: LineState? = null
|
override fun check(state: IReplStageState<*>, codeLine: ReplCodeLine): ReplCheckResult {
|
||||||
|
state.lock.write {
|
||||||
internal val lineState: LineState? get() = stateLock.read { _lineState }
|
val checkerState = state.asState<GenericReplCheckerState>()
|
||||||
|
val scriptFileName = makeScriptBaseName(codeLine, checkerState.generation.get())
|
||||||
fun createDiagnosticHolder() = ReplTerminalDiagnosticMessageHolder()
|
|
||||||
|
|
||||||
fun check(codeLine: ReplCodeLine, generation: Long): ReplCheckResult {
|
|
||||||
stateLock.write {
|
|
||||||
val scriptFileName = makeScriptBaseName(codeLine, generation)
|
|
||||||
val virtualFile =
|
val virtualFile =
|
||||||
LightVirtualFile("$scriptFileName${KotlinParserDefinition.STD_SCRIPT_EXT}", KotlinLanguage.INSTANCE, StringUtil.convertLineSeparators(codeLine.code)).apply {
|
LightVirtualFile("$scriptFileName${KotlinParserDefinition.STD_SCRIPT_EXT}", KotlinLanguage.INSTANCE, StringUtil.convertLineSeparators(codeLine.code)).apply {
|
||||||
charset = CharsetToolkit.UTF8_CHARSET
|
charset = CharsetToolkit.UTF8_CHARSET
|
||||||
@@ -97,7 +84,7 @@ open class GenericReplChecker(
|
|||||||
val syntaxErrorReport = AnalyzerWithCompilerReport.reportSyntaxErrors(psiFile, errorHolder)
|
val syntaxErrorReport = AnalyzerWithCompilerReport.reportSyntaxErrors(psiFile, errorHolder)
|
||||||
|
|
||||||
if (!syntaxErrorReport.isHasErrors) {
|
if (!syntaxErrorReport.isHasErrors) {
|
||||||
_lineState = LineState(codeLine, psiFile, errorHolder)
|
checkerState.lastLineState = GenericReplCheckerState.LineState(codeLine, psiFile, errorHolder)
|
||||||
}
|
}
|
||||||
|
|
||||||
return when {
|
return when {
|
||||||
|
|||||||
@@ -26,115 +26,83 @@ import org.jetbrains.kotlin.codegen.ClassBuilderFactories
|
|||||||
import org.jetbrains.kotlin.codegen.KotlinCodegenFacade
|
import org.jetbrains.kotlin.codegen.KotlinCodegenFacade
|
||||||
import org.jetbrains.kotlin.codegen.state.GenerationState
|
import org.jetbrains.kotlin.codegen.state.GenerationState
|
||||||
import org.jetbrains.kotlin.config.CompilerConfiguration
|
import org.jetbrains.kotlin.config.CompilerConfiguration
|
||||||
import org.jetbrains.kotlin.descriptors.ScriptDescriptor
|
|
||||||
import org.jetbrains.kotlin.script.KotlinScriptDefinition
|
import org.jetbrains.kotlin.script.KotlinScriptDefinition
|
||||||
import org.jetbrains.kotlin.script.KotlinScriptExternalDependencies
|
|
||||||
import java.io.File
|
import java.io.File
|
||||||
import java.util.concurrent.atomic.AtomicLong
|
|
||||||
import java.util.concurrent.locks.ReentrantReadWriteLock
|
import java.util.concurrent.locks.ReentrantReadWriteLock
|
||||||
import kotlin.concurrent.read
|
|
||||||
import kotlin.concurrent.write
|
import kotlin.concurrent.write
|
||||||
|
|
||||||
|
// WARNING: not thread safe, assuming external synchronization
|
||||||
|
|
||||||
open class GenericReplCompiler(disposable: Disposable,
|
open class GenericReplCompiler(disposable: Disposable,
|
||||||
protected val scriptDefinition: KotlinScriptDefinition,
|
protected val scriptDefinition: KotlinScriptDefinition,
|
||||||
protected val compilerConfiguration: CompilerConfiguration,
|
protected val compilerConfiguration: CompilerConfiguration,
|
||||||
messageCollector: MessageCollector,
|
messageCollector: MessageCollector
|
||||||
protected val stateLock: ReentrantReadWriteLock = ReentrantReadWriteLock()) : ReplCompiler {
|
) : ReplCompiler {
|
||||||
private val checker = GenericReplChecker(disposable, scriptDefinition, compilerConfiguration, messageCollector, stateLock)
|
|
||||||
|
|
||||||
private val analyzerEngine = GenericReplAnalyzer(checker.environment, stateLock)
|
private val checker = GenericReplChecker(disposable, scriptDefinition, compilerConfiguration, messageCollector)
|
||||||
|
|
||||||
private var lastDependencies: KotlinScriptExternalDependencies? = null
|
override fun createState(lock: ReentrantReadWriteLock): IReplStageState<*> = GenericReplCompilerState(checker.environment, lock)
|
||||||
|
|
||||||
private val descriptorsHistory = ReplHistory<ScriptDescriptor>()
|
override fun check(state: IReplStageState<*>, codeLine: ReplCodeLine): ReplCheckResult = checker.check(state, codeLine)
|
||||||
|
|
||||||
private val generation = AtomicLong(1)
|
override fun compile(state: IReplStageState<*>, codeLine: ReplCodeLine): ReplCompileResult {
|
||||||
|
state.lock.write {
|
||||||
|
val compilerState = state.asState<GenericReplCompilerState>()
|
||||||
|
|
||||||
override fun resetToLine(lineNumber: Int): List<ReplCodeLine> {
|
val currentGeneration = compilerState.generation.get()
|
||||||
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 mismatch when resetting lines")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
removedCompiledLines
|
|
||||||
}.map { it.first }
|
|
||||||
}
|
|
||||||
|
|
||||||
override val history: List<ReplCodeLine> get() = stateLock.read { descriptorsHistory.copySources() }
|
|
||||||
|
|
||||||
override fun check(codeLine: ReplCodeLine): ReplCheckResult = stateLock.read {
|
|
||||||
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 {
|
val (psiFile, errorHolder) = run {
|
||||||
if (checker.lineState == null || checker.lineState!!.codeLine != codeLine) {
|
if (compilerState.lastLineState == null || compilerState.lastLineState!!.codeLine != codeLine) {
|
||||||
val res = checker.check(codeLine, currentGeneration)
|
val res = checker.check(state, codeLine)
|
||||||
when (res) {
|
when (res) {
|
||||||
is ReplCheckResult.Incomplete -> return@compile ReplCompileResult.Incomplete(descriptorsHistory.copySources())
|
is ReplCheckResult.Incomplete -> return@compile ReplCompileResult.Incomplete()
|
||||||
is ReplCheckResult.Error -> return@compile ReplCompileResult.Error(descriptorsHistory.copySources(), res.message, res.location)
|
is ReplCheckResult.Error -> return@compile ReplCompileResult.Error(res.message, res.location)
|
||||||
is ReplCheckResult.Ok -> {} // continue
|
is ReplCheckResult.Ok -> {} // continue
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Pair(checker.lineState!!.psiFile, checker.lineState!!.errorHolder)
|
Pair(compilerState.lastLineState!!.psiFile, compilerState.lastLineState!!.errorHolder)
|
||||||
}
|
}
|
||||||
|
|
||||||
val newDependencies = scriptDefinition.getDependenciesFor(psiFile, checker.environment.project, lastDependencies)
|
val newDependencies = scriptDefinition.getDependenciesFor(psiFile, checker.environment.project, compilerState.lastDependencies)
|
||||||
var classpathAddendum: List<File>? = null
|
var classpathAddendum: List<File>? = null
|
||||||
if (lastDependencies != newDependencies) {
|
if (compilerState.lastDependencies != newDependencies) {
|
||||||
lastDependencies = newDependencies
|
compilerState.lastDependencies = newDependencies
|
||||||
classpathAddendum = newDependencies?.let { checker.environment.updateClasspath(it.classpath.map(::JvmClasspathRoot)) }
|
classpathAddendum = newDependencies?.let { checker.environment.updateClasspath(it.classpath.map(::JvmClasspathRoot)) }
|
||||||
}
|
}
|
||||||
|
|
||||||
val analysisResult = analyzerEngine.analyzeReplLine(psiFile, codeLine)
|
val analysisResult = compilerState.analyzerEngine.analyzeReplLine(psiFile, codeLine)
|
||||||
AnalyzerWithCompilerReport.reportDiagnostics(analysisResult.diagnostics, errorHolder)
|
AnalyzerWithCompilerReport.reportDiagnostics(analysisResult.diagnostics, errorHolder)
|
||||||
val scriptDescriptor = when (analysisResult) {
|
val scriptDescriptor = when (analysisResult) {
|
||||||
is GenericReplAnalyzer.ReplLineAnalysisResult.WithErrors -> return ReplCompileResult.Error(descriptorsHistory.copySources(), errorHolder.renderedDiagnostics)
|
is ReplCodeAnalyzer.ReplLineAnalysisResult.WithErrors -> return ReplCompileResult.Error(errorHolder.renderedDiagnostics)
|
||||||
is GenericReplAnalyzer.ReplLineAnalysisResult.Successful -> analysisResult.scriptDescriptor
|
is ReplCodeAnalyzer.ReplLineAnalysisResult.Successful -> analysisResult.scriptDescriptor
|
||||||
else -> error("Unexpected result ${analysisResult.javaClass}")
|
else -> error("Unexpected result ${analysisResult.javaClass}")
|
||||||
}
|
}
|
||||||
|
|
||||||
val state = GenerationState(
|
val generationState = GenerationState(
|
||||||
psiFile.project,
|
psiFile.project,
|
||||||
ClassBuilderFactories.binaries(false),
|
ClassBuilderFactories.binaries(false),
|
||||||
analyzerEngine.module,
|
compilerState.analyzerEngine.module,
|
||||||
analyzerEngine.trace.bindingContext,
|
compilerState.analyzerEngine.trace.bindingContext,
|
||||||
listOf(psiFile),
|
listOf(psiFile),
|
||||||
compilerConfiguration
|
compilerConfiguration
|
||||||
)
|
)
|
||||||
state.replSpecific.scriptResultFieldName = SCRIPT_RESULT_FIELD_NAME
|
generationState.replSpecific.scriptResultFieldName = SCRIPT_RESULT_FIELD_NAME
|
||||||
state.replSpecific.earlierScriptsForReplInterpreter = descriptorsHistory.copyValues()
|
generationState.replSpecific.earlierScriptsForReplInterpreter = compilerState.history.map { it.item }
|
||||||
state.beforeCompile()
|
generationState.beforeCompile()
|
||||||
KotlinCodegenFacade.generatePackage(
|
KotlinCodegenFacade.generatePackage(
|
||||||
state,
|
generationState,
|
||||||
psiFile.script!!.getContainingKtFile().packageFqName,
|
psiFile.script!!.containingKtFile.packageFqName,
|
||||||
setOf(psiFile.script!!.getContainingKtFile()),
|
setOf(psiFile.script!!.containingKtFile),
|
||||||
org.jetbrains.kotlin.codegen.CompilationErrorHandler.THROW_EXCEPTION)
|
org.jetbrains.kotlin.codegen.CompilationErrorHandler.THROW_EXCEPTION)
|
||||||
|
|
||||||
val generatedClassname = makeScriptBaseName(codeLine, currentGeneration)
|
val generatedClassname = makeScriptBaseName(codeLine, currentGeneration)
|
||||||
val compiledCodeLine = CompiledReplCodeLine(generatedClassname, codeLine)
|
compilerState.history.push(LineId(codeLine), scriptDescriptor)
|
||||||
descriptorsHistory.add(compiledCodeLine, scriptDescriptor)
|
|
||||||
|
|
||||||
return ReplCompileResult.CompiledClasses(descriptorsHistory.copySources(),
|
return ReplCompileResult.CompiledClasses(LineId(codeLine),
|
||||||
compiledCodeLine,
|
|
||||||
generatedClassname,
|
generatedClassname,
|
||||||
state.factory.asList().map { CompiledClassData(it.relativePath, it.asByteArray()) },
|
generationState.factory.asList().map { CompiledClassData(it.relativePath, it.asByteArray()) },
|
||||||
state.replSpecific.hasResult,
|
generationState.replSpecific.hasResult,
|
||||||
classpathAddendum ?: emptyList())
|
classpathAddendum ?: emptyList())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+10
-23
@@ -21,7 +21,6 @@ package org.jetbrains.kotlin.cli.jvm.repl
|
|||||||
import org.jetbrains.kotlin.cli.common.repl.CompiledReplCodeLine
|
import org.jetbrains.kotlin.cli.common.repl.CompiledReplCodeLine
|
||||||
import org.jetbrains.kotlin.cli.common.repl.ReplCodeLine
|
import org.jetbrains.kotlin.cli.common.repl.ReplCodeLine
|
||||||
import org.jetbrains.kotlin.cli.common.repl.ReplHistory
|
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.CliLightClassGenerationSupport
|
||||||
import org.jetbrains.kotlin.cli.jvm.compiler.JvmPackagePartProvider
|
import org.jetbrains.kotlin.cli.jvm.compiler.JvmPackagePartProvider
|
||||||
import org.jetbrains.kotlin.cli.jvm.compiler.KotlinCoreEnvironment
|
import org.jetbrains.kotlin.cli.jvm.compiler.KotlinCoreEnvironment
|
||||||
@@ -43,23 +42,18 @@ import org.jetbrains.kotlin.resolve.scopes.ImportingScope
|
|||||||
import org.jetbrains.kotlin.resolve.scopes.utils.parentsWithSelf
|
import org.jetbrains.kotlin.resolve.scopes.utils.parentsWithSelf
|
||||||
import org.jetbrains.kotlin.resolve.scopes.utils.replaceImportingScopes
|
import org.jetbrains.kotlin.resolve.scopes.utils.replaceImportingScopes
|
||||||
import org.jetbrains.kotlin.script.ScriptPriorities
|
import org.jetbrains.kotlin.script.ScriptPriorities
|
||||||
import java.util.concurrent.locks.ReentrantReadWriteLock
|
|
||||||
import kotlin.concurrent.read
|
|
||||||
import kotlin.concurrent.write
|
|
||||||
|
|
||||||
class GenericReplAnalyzer(environment: KotlinCoreEnvironment,
|
class ReplCodeAnalyzer(environment: KotlinCoreEnvironment) {
|
||||||
protected val stateLock: ReentrantReadWriteLock = ReentrantReadWriteLock()) : ReplResettableCodeLine {
|
|
||||||
private val topDownAnalysisContext: TopDownAnalysisContext
|
private val topDownAnalysisContext: TopDownAnalysisContext
|
||||||
private val topDownAnalyzer: LazyTopDownAnalyzer
|
private val topDownAnalyzer: LazyTopDownAnalyzer
|
||||||
private val resolveSession: ResolveSession
|
private val resolveSession: ResolveSession
|
||||||
private val scriptDeclarationFactory: ScriptMutableDeclarationProviderFactory
|
private val scriptDeclarationFactory: ScriptMutableDeclarationProviderFactory
|
||||||
private val replState = ResettableReplState()
|
private val replState = ResettableAnalyzerState()
|
||||||
|
|
||||||
val module: ModuleDescriptorImpl
|
val module: ModuleDescriptorImpl
|
||||||
get() = stateLock.read { field }
|
|
||||||
|
|
||||||
val trace: BindingTraceContext = CliLightClassGenerationSupport.NoScopeRecordCliBindingTrace()
|
val trace: BindingTraceContext = CliLightClassGenerationSupport.NoScopeRecordCliBindingTrace()
|
||||||
get() = stateLock.read { field }
|
|
||||||
|
|
||||||
init {
|
init {
|
||||||
// Module source scope is empty because all binary classes are in the dependency module, and all source classes are guaranteed
|
// Module source scope is empty because all binary classes are in the dependency module, and all source classes are guaranteed
|
||||||
@@ -94,21 +88,15 @@ class GenericReplAnalyzer(environment: KotlinCoreEnvironment,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun resetToLine(lineNumber: Int): List<ReplCodeLine> {
|
fun resetToLine(lineNumber: Int): List<ReplCodeLine> = replState.resetToLine(lineNumber)
|
||||||
return stateLock.write { replState.resetToLine(lineNumber) }
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun resetToLine(line: ReplCodeLine): List<ReplCodeLine> = resetToLine(line.no)
|
|
||||||
|
|
||||||
fun analyzeReplLine(psiFile: KtFile, codeLine: ReplCodeLine): ReplLineAnalysisResult {
|
fun analyzeReplLine(psiFile: KtFile, codeLine: ReplCodeLine): ReplLineAnalysisResult {
|
||||||
return stateLock.write {
|
topDownAnalysisContext.scripts.clear()
|
||||||
topDownAnalysisContext.scripts.clear()
|
trace.clearDiagnostics()
|
||||||
trace.clearDiagnostics()
|
|
||||||
|
|
||||||
psiFile.script!!.putUserData(ScriptPriorities.PRIORITY_KEY, codeLine.no)
|
psiFile.script!!.putUserData(ScriptPriorities.PRIORITY_KEY, codeLine.no)
|
||||||
|
|
||||||
doAnalyze(psiFile, codeLine)
|
return doAnalyze(psiFile, codeLine)
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun doAnalyze(linePsi: KtFile, codeLine: ReplCodeLine): ReplLineAnalysisResult {
|
private fun doAnalyze(linePsi: KtFile, codeLine: ReplCodeLine): ReplLineAnalysisResult {
|
||||||
@@ -179,7 +167,8 @@ class GenericReplAnalyzer(environment: KotlinCoreEnvironment,
|
|||||||
}
|
}
|
||||||
|
|
||||||
// TODO: merge with org.jetbrains.kotlin.resolve.repl.ReplState when switching to new REPL infrastruct everywhere
|
// TODO: merge with org.jetbrains.kotlin.resolve.repl.ReplState when switching to new REPL infrastruct everywhere
|
||||||
class ResettableReplState {
|
// TODO: review it's place in the extracted state infrastruct (now the analyzer itself is a part of the state
|
||||||
|
class ResettableAnalyzerState {
|
||||||
private val successfulLines = ReplHistory<LineInfo.SuccessfulLine>()
|
private val successfulLines = ReplHistory<LineInfo.SuccessfulLine>()
|
||||||
private val submittedLines = hashMapOf<KtFile, LineInfo>()
|
private val submittedLines = hashMapOf<KtFile, LineInfo>()
|
||||||
|
|
||||||
@@ -189,8 +178,6 @@ class GenericReplAnalyzer(environment: KotlinCoreEnvironment,
|
|||||||
return removed.map { it.first }
|
return removed.map { it.first }
|
||||||
}
|
}
|
||||||
|
|
||||||
fun resetToLine(line: ReplCodeLine): List<ReplCodeLine> = resetToLine(line.no)
|
|
||||||
|
|
||||||
fun submitLine(ktFile: KtFile, codeLine: ReplCodeLine) {
|
fun submitLine(ktFile: KtFile, codeLine: ReplCodeLine) {
|
||||||
val line = LineInfo.SubmittedLine(ktFile, successfulLines.lastValue())
|
val line = LineInfo.SubmittedLine(ktFile, successfulLines.lastValue())
|
||||||
submittedLines[ktFile] = line
|
submittedLines[ktFile] = line
|
||||||
@@ -64,7 +64,7 @@ class ReplInterpreter(
|
|||||||
}
|
}
|
||||||
|
|
||||||
private val psiFileFactory: PsiFileFactoryImpl = PsiFileFactory.getInstance(environment.project) as PsiFileFactoryImpl
|
private val psiFileFactory: PsiFileFactoryImpl = PsiFileFactory.getInstance(environment.project) as PsiFileFactoryImpl
|
||||||
private val analyzerEngine = GenericReplAnalyzer(environment)
|
private val analyzerEngine = ReplCodeAnalyzer(environment)
|
||||||
|
|
||||||
fun eval(line: String): LineResult {
|
fun eval(line: String): LineResult {
|
||||||
++lineNumber
|
++lineNumber
|
||||||
@@ -101,8 +101,8 @@ class ReplInterpreter(
|
|||||||
val analysisResult = analyzerEngine.analyzeReplLine(psiFile, ReplCodeLine(lineNumber, "fake line"))
|
val analysisResult = analyzerEngine.analyzeReplLine(psiFile, ReplCodeLine(lineNumber, "fake line"))
|
||||||
AnalyzerWithCompilerReport.reportDiagnostics(analysisResult.diagnostics, errorHolder)
|
AnalyzerWithCompilerReport.reportDiagnostics(analysisResult.diagnostics, errorHolder)
|
||||||
val scriptDescriptor = when (analysisResult) {
|
val scriptDescriptor = when (analysisResult) {
|
||||||
is GenericReplAnalyzer.ReplLineAnalysisResult.WithErrors -> return LineResult.Error.CompileTime(errorHolder.renderedDiagnostics)
|
is ReplCodeAnalyzer.ReplLineAnalysisResult.WithErrors -> return LineResult.Error.CompileTime(errorHolder.renderedDiagnostics)
|
||||||
is GenericReplAnalyzer.ReplLineAnalysisResult.Successful -> analysisResult.scriptDescriptor
|
is ReplCodeAnalyzer.ReplLineAnalysisResult.Successful -> analysisResult.scriptDescriptor
|
||||||
else -> error("Unexpected result ${analysisResult.javaClass}")
|
else -> error("Unexpected result ${analysisResult.javaClass}")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -68,5 +68,5 @@ class KotlinJsr223JvmScriptEngine4Idea(
|
|||||||
|
|
||||||
val localEvaluator: ReplFullEvaluator by lazy { GenericReplCompilingEvaluator(replCompiler, templateClasspath, Thread.currentThread().contextClassLoader) }
|
val localEvaluator: ReplFullEvaluator by lazy { GenericReplCompilingEvaluator(replCompiler, templateClasspath, Thread.currentThread().contextClassLoader) }
|
||||||
|
|
||||||
override val replScriptEvaluator: ReplFullEvaluator get() = localEvaluator
|
override val replEvaluator: ReplFullEvaluator get() = localEvaluator
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user