Reintroduce history check between compile and eval, place generation into code line and id
This commit is contained in:
+4
@@ -80,5 +80,9 @@ open class AggregatedReplStageState<T1, T2>(val state1: IReplStageState<T1>, val
|
|||||||
target.isAssignableFrom(state2::class.java) -> state2 as StateT
|
target.isAssignableFrom(state2::class.java) -> state2 as StateT
|
||||||
else -> super.asState(target)
|
else -> super.asState(target)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun getNextLineNo() = state1.getNextLineNo()
|
||||||
|
|
||||||
|
override val currentGeneration: Int get() = state1.currentGeneration
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -18,18 +18,19 @@ package org.jetbrains.kotlin.cli.common.repl
|
|||||||
|
|
||||||
import java.io.Serializable
|
import java.io.Serializable
|
||||||
import java.util.*
|
import java.util.*
|
||||||
|
import java.util.concurrent.atomic.AtomicInteger
|
||||||
import java.util.concurrent.locks.ReentrantReadWriteLock
|
import java.util.concurrent.locks.ReentrantReadWriteLock
|
||||||
import kotlin.concurrent.write
|
import kotlin.concurrent.write
|
||||||
|
|
||||||
data class LineId(override val no: Int, private val codeHash: Int) : ILineId, Serializable {
|
data class LineId(override val no: Int, override val generation: Int, private val codeHash: Int) : ILineId, Serializable {
|
||||||
|
|
||||||
constructor(no: Int, code: String): this(no, code.hashCode())
|
constructor(no: Int, generation: Int, code: String): this(no, generation, code.hashCode())
|
||||||
constructor(codeLine: ReplCodeLine): this(codeLine.no, codeLine.code.hashCode())
|
constructor(codeLine: ReplCodeLine): this(codeLine.no, codeLine.generation, codeLine.code.hashCode())
|
||||||
|
|
||||||
override fun compareTo(other: ILineId): Int = (other as? LineId)?.let {
|
override fun compareTo(other: ILineId): Int = (other as? LineId)?.let {
|
||||||
val c = no.compareTo(it.no)
|
no.compareTo(it.no).takeIf { it != 0 }
|
||||||
if (c == 0) codeHash.compareTo(it.codeHash)
|
?: generation.compareTo(it.generation).takeIf { it != 0 }
|
||||||
else c
|
?: codeHash.compareTo(it.codeHash)
|
||||||
} ?: -1 // TODO: check if it doesn't break something
|
} ?: -1 // TODO: check if it doesn't break something
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
@@ -39,6 +40,8 @@ data class LineId(override val no: Int, private val codeHash: Int) : ILineId, Se
|
|||||||
|
|
||||||
open class BasicReplStageHistory<T>(override val lock: ReentrantReadWriteLock = ReentrantReadWriteLock()) : IReplStageHistory<T>, ArrayList<ReplHistoryRecord<T>>() {
|
open class BasicReplStageHistory<T>(override val lock: ReentrantReadWriteLock = ReentrantReadWriteLock()) : IReplStageHistory<T>, ArrayList<ReplHistoryRecord<T>>() {
|
||||||
|
|
||||||
|
val currentGeneration = AtomicInteger(REPL_CODE_LINE_FIRST_GEN)
|
||||||
|
|
||||||
override fun push(id: ILineId, item: T) {
|
override fun push(id: ILineId, item: T) {
|
||||||
lock.write {
|
lock.write {
|
||||||
add(ReplHistoryRecord(id, item))
|
add(ReplHistoryRecord(id, item))
|
||||||
@@ -54,6 +57,7 @@ open class BasicReplStageHistory<T>(override val lock: ReentrantReadWriteLock =
|
|||||||
if (idx < lastIndex) {
|
if (idx < lastIndex) {
|
||||||
val removed = asSequence().drop(idx + 1).map { it.id }.toList()
|
val removed = asSequence().drop(idx + 1).map { it.id }.toList()
|
||||||
removeRange(idx + 1, lastIndex)
|
removeRange(idx + 1, lastIndex)
|
||||||
|
currentGeneration.incrementAndGet()
|
||||||
return removed
|
return removed
|
||||||
}
|
}
|
||||||
else return emptyList()
|
else return emptyList()
|
||||||
@@ -61,7 +65,9 @@ open class BasicReplStageHistory<T>(override val lock: ReentrantReadWriteLock =
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class BasicReplStageState<HistoryItemT>: IReplStageState<HistoryItemT> {
|
open class BasicReplStageState<HistoryItemT>: IReplStageState<HistoryItemT> {
|
||||||
|
|
||||||
|
override val currentGeneration: Int get() = history.currentGeneration.get()
|
||||||
|
|
||||||
override val lock = ReentrantReadWriteLock()
|
override val lock = ReentrantReadWriteLock()
|
||||||
|
|
||||||
|
|||||||
+2
@@ -26,6 +26,8 @@ open class GenericReplEvaluatorState(baseClasspath: Iterable<File>, baseClassloa
|
|||||||
{
|
{
|
||||||
override val history: IReplStageHistory<EvalClassWithInstanceAndLoader> = BasicReplStageHistory(lock)
|
override val history: IReplStageHistory<EvalClassWithInstanceAndLoader> = BasicReplStageHistory(lock)
|
||||||
|
|
||||||
|
override val currentGeneration: Int get() = (history as BasicReplStageHistory<*>).currentGeneration.get()
|
||||||
|
|
||||||
val topClassLoader: ReplClassLoader = makeReplClassLoader(baseClassloader, baseClasspath)
|
val topClassLoader: ReplClassLoader = makeReplClassLoader(baseClassloader, baseClasspath)
|
||||||
|
|
||||||
val currentClasspath: List<File> get() = lock.read {
|
val currentClasspath: List<File> get() = lock.read {
|
||||||
|
|||||||
-1
@@ -36,7 +36,6 @@ class GenericReplCompilingEvaluator(val compiler: ReplCompiler,
|
|||||||
val compiled = compiler.compile(state, codeLine)
|
val compiled = compiler.compile(state, codeLine)
|
||||||
when (compiled) {
|
when (compiled) {
|
||||||
is ReplCompileResult.Error -> ReplEvalResult.Error.CompileTime(compiled.message, compiled.location)
|
is ReplCompileResult.Error -> ReplEvalResult.Error.CompileTime(compiled.message, compiled.location)
|
||||||
is ReplCompileResult.HistoryMismatch -> ReplEvalResult.HistoryMismatch(compiled.lineNo)
|
|
||||||
is ReplCompileResult.Incomplete -> ReplEvalResult.Incomplete()
|
is ReplCompileResult.Incomplete -> ReplEvalResult.Incomplete()
|
||||||
is ReplCompileResult.CompiledClasses -> {
|
is ReplCompileResult.CompiledClasses -> {
|
||||||
val result = eval(state, compiled, scriptArgs, invokeWrapper)
|
val result = eval(state, compiled, scriptArgs, invokeWrapper)
|
||||||
|
|||||||
+7
-7
@@ -113,10 +113,10 @@ open class GenericReplEvaluator(val baseClasspath: Iterable<File>,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// val firstMismatch = historyActor.firstMismatch(verifyHistory)
|
val firstMismatch = historyActor.firstMismatch(compileResult.previousLines.asSequence())
|
||||||
// if (firstMismatch != null) {
|
if (firstMismatch != null) {
|
||||||
// return@eval ReplEvalResult.HistoryMismatch(firstMismatch)
|
return@eval ReplEvalResult.HistoryMismatch(firstMismatch.first?.id?.no ?: firstMismatch.second?.no ?: -1 /* means error? */)
|
||||||
// }
|
}
|
||||||
|
|
||||||
val (classLoader, scriptClass) = try {
|
val (classLoader, scriptClass) = try {
|
||||||
historyActor.processClasses(compileResult)
|
historyActor.processClasses(compileResult)
|
||||||
@@ -172,7 +172,7 @@ private open class HistoryActionsForNoRepeat(val state: GenericReplEvaluatorStat
|
|||||||
|
|
||||||
open val effectiveHistory: List<EvalClassWithInstanceAndLoader> get() = state.history.map { it.item }
|
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 firstMismatch(other: Sequence<ILineId>): Pair<ReplHistoryRecord<EvalClassWithInstanceAndLoader>?, ILineId?>? = state.history.firstMismatch(other)
|
||||||
|
|
||||||
open fun addPlaceholder(lineId: ILineId, value: EvalClassWithInstanceAndLoader) { state.history.push(lineId, value) }
|
open fun addPlaceholder(lineId: ILineId, value: EvalClassWithInstanceAndLoader) { state.history.push(lineId, value) }
|
||||||
|
|
||||||
@@ -215,7 +215,7 @@ private open class HistoryActionsForRepeatRecentOnly(state: GenericReplEvaluator
|
|||||||
|
|
||||||
override val effectiveHistory: List<EvalClassWithInstanceAndLoader> get() = super.effectiveHistory.dropLast(1)
|
override val effectiveHistory: List<EvalClassWithInstanceAndLoader> get() = super.effectiveHistory.dropLast(1)
|
||||||
|
|
||||||
override fun<OtherT> firstMismatch(other: IReplStageHistory<OtherT>): Pair<ReplHistoryRecord<EvalClassWithInstanceAndLoader>?, ReplHistoryRecord<OtherT>?>? =
|
override fun firstMismatch(other: Sequence<ILineId>): Pair<ReplHistoryRecord<EvalClassWithInstanceAndLoader>?, ILineId?>? =
|
||||||
state.history.firstMismatchFiltered(other) { it.id != currentLast.id }
|
state.history.firstMismatchFiltered(other) { it.id != currentLast.id }
|
||||||
|
|
||||||
override fun addPlaceholder(lineId: ILineId, value: EvalClassWithInstanceAndLoader) {}
|
override fun addPlaceholder(lineId: ILineId, value: EvalClassWithInstanceAndLoader) {}
|
||||||
@@ -235,7 +235,7 @@ private open class HistoryActionsForRepeatAny(state: GenericReplEvaluatorState,
|
|||||||
|
|
||||||
override val effectiveHistory: List<EvalClassWithInstanceAndLoader> get() = state.history.takeWhile { it.id != matchingLine.id }.map { it.item }
|
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>?>? =
|
override fun firstMismatch(other: Sequence<ILineId>): Pair<ReplHistoryRecord<EvalClassWithInstanceAndLoader>?, ILineId?>? =
|
||||||
state.history.firstMismatchWhile(other) { it.id != matchingLine.id }
|
state.history.firstMismatchWhile(other) { it.id != matchingLine.id }
|
||||||
|
|
||||||
override fun addPlaceholder(lineId: ILineId, value: EvalClassWithInstanceAndLoader) {}
|
override fun addPlaceholder(lineId: ILineId, value: EvalClassWithInstanceAndLoader) {}
|
||||||
|
|||||||
+1
-13
@@ -23,17 +23,6 @@ import java.util.concurrent.locks.ReentrantReadWriteLock
|
|||||||
import javax.script.*
|
import javax.script.*
|
||||||
|
|
||||||
val KOTLIN_SCRIPT_STATE_BINDINGS_KEY = "kotlin.script.state"
|
val KOTLIN_SCRIPT_STATE_BINDINGS_KEY = "kotlin.script.state"
|
||||||
val KOTLIN_SCRIPT_LINE_NUMBER_BINDINGS_KEY = "kotlin.script.line.number"
|
|
||||||
|
|
||||||
// TODO consider additional error handling
|
|
||||||
var Bindings.kotlinScriptState: IReplStageState<*>
|
|
||||||
@Suppress("UNCHECKED_CAST")
|
|
||||||
get() = get(KOTLIN_SCRIPT_STATE_BINDINGS_KEY) as IReplStageState<*>
|
|
||||||
set(v) { put(KOTLIN_SCRIPT_STATE_BINDINGS_KEY, v) }
|
|
||||||
|
|
||||||
val Bindings.kotlinScriptLineNumber: AtomicInteger
|
|
||||||
@Suppress("UNCHECKED_CAST")
|
|
||||||
get() = getOrPut(KOTLIN_SCRIPT_LINE_NUMBER_BINDINGS_KEY, { AtomicInteger(0) }) as AtomicInteger
|
|
||||||
|
|
||||||
abstract class KotlinJsr223JvmScriptEngineBase(protected val myFactory: ScriptEngineFactory) : AbstractScriptEngine(), ScriptEngine, Compilable {
|
abstract class KotlinJsr223JvmScriptEngineBase(protected val myFactory: ScriptEngineFactory) : AbstractScriptEngine(), ScriptEngine, Compilable {
|
||||||
|
|
||||||
@@ -53,7 +42,7 @@ 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(context: ScriptContext, code: String) = ReplCodeLine(context.getBindings(ScriptContext.ENGINE_SCOPE).kotlinScriptLineNumber.incrementAndGet(), code)
|
fun nextCodeLine(context: ScriptContext, code: String) = getCurrentState(context).let { ReplCodeLine(it.getNextLineNo(), it.currentGeneration, code) }
|
||||||
|
|
||||||
protected open fun createState(lock: ReentrantReadWriteLock = ReentrantReadWriteLock()): IReplStageState<*> = AggregatedReplStageState(replCompiler.createState(lock), replEvaluator.createState(lock), lock)
|
protected open fun createState(lock: ReentrantReadWriteLock = ReentrantReadWriteLock()): IReplStageState<*> = AggregatedReplStageState(replCompiler.createState(lock), replEvaluator.createState(lock), lock)
|
||||||
|
|
||||||
@@ -83,7 +72,6 @@ abstract class KotlinJsr223JvmScriptEngineBase(protected val myFactory: ScriptEn
|
|||||||
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.CompiledClasses -> result
|
is ReplCompileResult.CompiledClasses -> result
|
||||||
}
|
}
|
||||||
return CompiledKotlinScript(this, codeLine, compiled)
|
return CompiledKotlinScript(this, codeLine, compiled)
|
||||||
|
|||||||
@@ -23,7 +23,10 @@ import java.util.*
|
|||||||
import java.util.concurrent.locks.ReentrantReadWriteLock
|
import java.util.concurrent.locks.ReentrantReadWriteLock
|
||||||
import kotlin.reflect.KClass
|
import kotlin.reflect.KClass
|
||||||
|
|
||||||
data class ReplCodeLine(val no: Int, val code: String) : Serializable {
|
const val REPL_CODE_LINE_FIRST_NO = 1
|
||||||
|
const val REPL_CODE_LINE_FIRST_GEN = 1
|
||||||
|
|
||||||
|
data class ReplCodeLine(val no: Int, val generation: Int, val code: String) : Serializable {
|
||||||
companion object {
|
companion object {
|
||||||
private val serialVersionUID: Long = 8228357578L
|
private val serialVersionUID: Long = 8228357578L
|
||||||
}
|
}
|
||||||
@@ -77,6 +80,7 @@ interface ReplCompileAction {
|
|||||||
|
|
||||||
sealed class ReplCompileResult : Serializable {
|
sealed class ReplCompileResult : Serializable {
|
||||||
class CompiledClasses(val lineId: LineId,
|
class CompiledClasses(val lineId: LineId,
|
||||||
|
val previousLines: List<ILineId>,
|
||||||
val mainClassName: String,
|
val mainClassName: String,
|
||||||
val classes: List<CompiledClassData>,
|
val classes: List<CompiledClassData>,
|
||||||
val hasResult: Boolean,
|
val hasResult: Boolean,
|
||||||
@@ -84,8 +88,6 @@ sealed class ReplCompileResult : Serializable {
|
|||||||
|
|
||||||
class Incomplete : ReplCompileResult()
|
class Incomplete : ReplCompileResult()
|
||||||
|
|
||||||
class HistoryMismatch(val lineNo: Int) : ReplCompileResult()
|
|
||||||
|
|
||||||
class Error(val message: String,
|
class Error(val message: String,
|
||||||
val location: CompilerMessageLocation = CompilerMessageLocation.NO_LOCATION) : ReplCompileResult() {
|
val location: CompilerMessageLocation = CompilerMessageLocation.NO_LOCATION) : ReplCompileResult() {
|
||||||
override fun toString(): String = "Error(message = \"$message\""
|
override fun toString(): String = "Error(message = \"$message\""
|
||||||
|
|||||||
@@ -23,14 +23,13 @@ import kotlin.concurrent.write
|
|||||||
|
|
||||||
interface ILineId : Comparable<ILineId> {
|
interface ILineId : Comparable<ILineId> {
|
||||||
val no: Int
|
val no: Int
|
||||||
|
val generation: Int
|
||||||
}
|
}
|
||||||
|
|
||||||
data class ReplHistoryRecord<out T> (val id: ILineId, val item: T)
|
data class ReplHistoryRecord<out T> (val id: ILineId, val item: T)
|
||||||
|
|
||||||
interface IReplStageHistory<T> : List<ReplHistoryRecord<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 peek(): ReplHistoryRecord<T>? = lock.read { lastOrNull() }
|
||||||
|
|
||||||
fun push(id: ILineId, item: T)
|
fun push(id: ILineId, item: T)
|
||||||
@@ -44,31 +43,6 @@ interface IReplStageHistory<T> : List<ReplHistoryRecord<T>> {
|
|||||||
|
|
||||||
fun resetTo(id: ILineId): Iterable<ILineId>
|
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
|
val lock: ReentrantReadWriteLock
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -77,9 +51,28 @@ interface IReplStageState<T> {
|
|||||||
|
|
||||||
val lock: ReentrantReadWriteLock
|
val lock: ReentrantReadWriteLock
|
||||||
|
|
||||||
|
val currentGeneration: Int
|
||||||
|
|
||||||
|
fun getNextLineNo(): Int = history.peek()?.id?.no?.let { it + 1 } ?: REPL_CODE_LINE_FIRST_NO // TODO: it should be more robust downstream (e.g. use atomic)
|
||||||
|
|
||||||
fun <StateT : IReplStageState<*>> asState(target: Class<out StateT>): StateT =
|
fun <StateT : IReplStageState<*>> asState(target: Class<out StateT>): StateT =
|
||||||
if (target.isAssignableFrom(this::class.java)) this as StateT
|
if (target.isAssignableFrom(this::class.java)) this as StateT
|
||||||
else throw IllegalArgumentException("$this is not an expected instance of IReplStageState")
|
else throw IllegalArgumentException("$this is not an expected instance of IReplStageState")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
fun <T> IReplStageHistory<T>.firstMismatch(other: Sequence<ILineId>): Pair<ReplHistoryRecord<T>?, ILineId?>? =
|
||||||
|
lock.read {
|
||||||
|
iterator().asSequence().zip(other.asSequence()).firstOrNull { it.first.id != it.second }?.let { it.first to it.second }
|
||||||
|
}
|
||||||
|
|
||||||
|
fun<T> IReplStageHistory<T>.firstMismatchFiltered(other: Sequence<ILineId>, predicate: (ReplHistoryRecord<T>) -> Boolean): Pair<ReplHistoryRecord<T>?, ILineId?>? =
|
||||||
|
lock.read {
|
||||||
|
iterator().asSequence().filter(predicate).zip(other.asSequence()).firstOrNull { it.first.id != it.second }?.let { it.first to it.second }
|
||||||
|
}
|
||||||
|
|
||||||
|
fun<T> IReplStageHistory<T>.firstMismatchWhile(other: Sequence<ILineId>, predicate: (ReplHistoryRecord<T>) -> Boolean): Pair<ReplHistoryRecord<T>?, ILineId?>? =
|
||||||
|
lock.read {
|
||||||
|
iterator().asSequence().takeWhile(predicate).zip(other.asSequence()).firstOrNull { it.first.id != it.second }?.let { it.first to it.second }
|
||||||
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -20,8 +20,8 @@ import com.google.common.base.Throwables
|
|||||||
import java.io.File
|
import java.io.File
|
||||||
import java.net.URLClassLoader
|
import java.net.URLClassLoader
|
||||||
|
|
||||||
fun makeScriptBaseName(codeLine: ReplCodeLine, generation: Long) =
|
fun makeScriptBaseName(codeLine: ReplCodeLine) =
|
||||||
"Line_${codeLine.no}${if (generation > 1) "_gen_$generation" else ""}"
|
"Line_${codeLine.no}${if (codeLine.generation > REPL_CODE_LINE_FIRST_GEN) "_gen_${codeLine.generation}" else ""}"
|
||||||
|
|
||||||
fun renderReplStackTrace(cause: Throwable, startFromMethodName: String): String {
|
fun renderReplStackTrace(cause: Throwable, startFromMethodName: String): String {
|
||||||
val newTrace = arrayListOf<StackTraceElement>()
|
val newTrace = arrayListOf<StackTraceElement>()
|
||||||
|
|||||||
@@ -28,7 +28,6 @@ import java.util.concurrent.locks.ReentrantReadWriteLock
|
|||||||
class ReplCompilerStageHistory(private val state: GenericReplCompilerState) : BasicReplStageHistory<ScriptDescriptor>(state.lock) {
|
class ReplCompilerStageHistory(private val state: GenericReplCompilerState) : BasicReplStageHistory<ScriptDescriptor>(state.lock) {
|
||||||
|
|
||||||
override fun resetTo(id: ILineId): Iterable<ILineId> {
|
override fun resetTo(id: ILineId): Iterable<ILineId> {
|
||||||
state.generation.incrementAndGet()
|
|
||||||
val removedCompiledLines = super.resetTo(id)
|
val removedCompiledLines = super.resetTo(id)
|
||||||
val removedAnalyzedLines = state.analyzerEngine.resetToLine(id.no)
|
val removedAnalyzedLines = state.analyzerEngine.resetToLine(id.no)
|
||||||
|
|
||||||
@@ -49,13 +48,14 @@ abstract class GenericReplCheckerState: IReplStageState<ScriptDescriptor> {
|
|||||||
val psiFile: KtFile,
|
val psiFile: KtFile,
|
||||||
val errorHolder: DiagnosticMessageHolder)
|
val errorHolder: DiagnosticMessageHolder)
|
||||||
|
|
||||||
val generation = AtomicLong(1)
|
|
||||||
var lastLineState: LineState? = null // for transferring state to the compiler in most typical case
|
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() {
|
class GenericReplCompilerState(environment: KotlinCoreEnvironment, override val lock: ReentrantReadWriteLock = ReentrantReadWriteLock()) : IReplStageState<ScriptDescriptor>, GenericReplCheckerState() {
|
||||||
|
|
||||||
override val history: IReplStageHistory<ScriptDescriptor> = ReplCompilerStageHistory(this)
|
override val history = ReplCompilerStageHistory(this)
|
||||||
|
|
||||||
|
override val currentGeneration: Int get() = (history as BasicReplStageHistory<*>).currentGeneration.get()
|
||||||
|
|
||||||
val analyzerEngine = ReplCodeAnalyzer(environment)
|
val analyzerEngine = ReplCodeAnalyzer(environment)
|
||||||
|
|
||||||
|
|||||||
@@ -71,7 +71,7 @@ open class GenericReplChecker(
|
|||||||
override fun check(state: IReplStageState<*>, codeLine: ReplCodeLine): ReplCheckResult {
|
override fun check(state: IReplStageState<*>, codeLine: ReplCodeLine): ReplCheckResult {
|
||||||
state.lock.write {
|
state.lock.write {
|
||||||
val checkerState = state.asState(GenericReplCheckerState::class.java)
|
val checkerState = state.asState(GenericReplCheckerState::class.java)
|
||||||
val scriptFileName = makeScriptBaseName(codeLine, checkerState.generation.get())
|
val scriptFileName = makeScriptBaseName(codeLine)
|
||||||
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
|
||||||
|
|||||||
@@ -49,8 +49,6 @@ open class GenericReplCompiler(disposable: Disposable,
|
|||||||
state.lock.write {
|
state.lock.write {
|
||||||
val compilerState = state.asState(GenericReplCompilerState::class.java)
|
val compilerState = state.asState(GenericReplCompilerState::class.java)
|
||||||
|
|
||||||
val currentGeneration = compilerState.generation.get()
|
|
||||||
|
|
||||||
val (psiFile, errorHolder) = run {
|
val (psiFile, errorHolder) = run {
|
||||||
if (compilerState.lastLineState == null || compilerState.lastLineState!!.codeLine != codeLine) {
|
if (compilerState.lastLineState == null || compilerState.lastLineState!!.codeLine != codeLine) {
|
||||||
val res = checker.check(state, codeLine)
|
val res = checker.check(state, codeLine)
|
||||||
@@ -95,10 +93,11 @@ open class GenericReplCompiler(disposable: Disposable,
|
|||||||
setOf(psiFile.script!!.containingKtFile),
|
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)
|
||||||
compilerState.history.push(LineId(codeLine), scriptDescriptor)
|
compilerState.history.push(LineId(codeLine), scriptDescriptor)
|
||||||
|
|
||||||
return ReplCompileResult.CompiledClasses(LineId(codeLine),
|
return ReplCompileResult.CompiledClasses(LineId(codeLine),
|
||||||
|
compilerState.history.map { it.id },
|
||||||
generatedClassname,
|
generatedClassname,
|
||||||
generationState.factory.asList().map { CompiledClassData(it.relativePath, it.asByteArray()) },
|
generationState.factory.asList().map { CompiledClassData(it.relativePath, it.asByteArray()) },
|
||||||
generationState.replSpecific.hasResult,
|
generationState.replSpecific.hasResult,
|
||||||
|
|||||||
@@ -98,7 +98,7 @@ class ReplInterpreter(
|
|||||||
return LineResult.Error.CompileTime(errorHolder.renderedDiagnostics)
|
return LineResult.Error.CompileTime(errorHolder.renderedDiagnostics)
|
||||||
}
|
}
|
||||||
|
|
||||||
val analysisResult = analyzerEngine.analyzeReplLine(psiFile, ReplCodeLine(lineNumber, "fake line"))
|
val analysisResult = analyzerEngine.analyzeReplLine(psiFile, ReplCodeLine(lineNumber, 0, "fake line"))
|
||||||
AnalyzerWithCompilerReport.reportDiagnostics(analysisResult.diagnostics, errorHolder)
|
AnalyzerWithCompilerReport.reportDiagnostics(analysisResult.diagnostics, errorHolder)
|
||||||
val scriptDescriptor = when (analysisResult) {
|
val scriptDescriptor = when (analysisResult) {
|
||||||
is ReplCodeAnalyzer.ReplLineAnalysisResult.WithErrors -> return LineResult.Error.CompileTime(errorHolder.renderedDiagnostics)
|
is ReplCodeAnalyzer.ReplLineAnalysisResult.WithErrors -> return LineResult.Error.CompileTime(errorHolder.renderedDiagnostics)
|
||||||
|
|||||||
+11
-5
@@ -16,11 +16,9 @@
|
|||||||
|
|
||||||
package org.jetbrains.kotlin.daemon.client
|
package org.jetbrains.kotlin.daemon.client
|
||||||
|
|
||||||
import org.jetbrains.kotlin.cli.common.repl.ILineId
|
import org.jetbrains.kotlin.cli.common.repl.*
|
||||||
import org.jetbrains.kotlin.cli.common.repl.IReplStageHistory
|
|
||||||
import org.jetbrains.kotlin.cli.common.repl.IReplStageState
|
|
||||||
import org.jetbrains.kotlin.cli.common.repl.ReplHistoryRecord
|
|
||||||
import org.jetbrains.kotlin.daemon.common.ReplStateFacade
|
import org.jetbrains.kotlin.daemon.common.ReplStateFacade
|
||||||
|
import java.util.concurrent.atomic.AtomicInteger
|
||||||
import java.util.concurrent.locks.ReentrantReadWriteLock
|
import java.util.concurrent.locks.ReentrantReadWriteLock
|
||||||
|
|
||||||
// NOTE: the lock is local
|
// NOTE: the lock is local
|
||||||
@@ -39,12 +37,20 @@ class RemoteReplCompilerStateHistory(private val state: RemoteReplCompilerState)
|
|||||||
throw NotImplementedError("pop from remote history is not supported")
|
throw NotImplementedError("pop from remote history is not supported")
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun resetTo(id: ILineId): Iterable<ILineId> = state.replStateFacade.historyResetTo(id)
|
override fun resetTo(id: ILineId): Iterable<ILineId> = state.replStateFacade.historyResetTo(id).apply {
|
||||||
|
if (isNotEmpty()) {
|
||||||
|
currentGeneration.incrementAndGet()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
val currentGeneration = AtomicInteger(REPL_CODE_LINE_FIRST_GEN)
|
||||||
|
|
||||||
override val lock: ReentrantReadWriteLock get() = state.lock
|
override val lock: ReentrantReadWriteLock get() = state.lock
|
||||||
}
|
}
|
||||||
|
|
||||||
class RemoteReplCompilerState(internal val replStateFacade: ReplStateFacade, override val lock: ReentrantReadWriteLock = ReentrantReadWriteLock()) : IReplStageState<Unit> {
|
class RemoteReplCompilerState(internal val replStateFacade: ReplStateFacade, override val lock: ReentrantReadWriteLock = ReentrantReadWriteLock()) : IReplStageState<Unit> {
|
||||||
|
|
||||||
|
override val currentGeneration: Int get() = (history as RemoteReplCompilerStateHistory).currentGeneration.get()
|
||||||
|
|
||||||
override val history: IReplStageHistory<Unit> = RemoteReplCompilerStateHistory(this)
|
override val history: IReplStageHistory<Unit> = RemoteReplCompilerStateHistory(this)
|
||||||
}
|
}
|
||||||
@@ -47,10 +47,10 @@ class GenericReplTest : TestCase() {
|
|||||||
|
|
||||||
val state = repl.createState()
|
val state = repl.createState()
|
||||||
|
|
||||||
val res1 = repl.replCompiler.check(state, ReplCodeLine(0, "val x ="))
|
val res1 = repl.replCompiler.check(state, ReplCodeLine(0, 0, "val x ="))
|
||||||
TestCase.assertTrue("Unexpected check results: $res1", res1 is ReplCheckResult.Incomplete)
|
TestCase.assertTrue("Unexpected check results: $res1", res1 is ReplCheckResult.Incomplete)
|
||||||
|
|
||||||
val codeLine0 = ReplCodeLine(0, "val l1 = listOf(1 + 2)\nl1.first()")
|
val codeLine0 = ReplCodeLine(0, 0, "val l1 = listOf(1 + 2)\nl1.first()")
|
||||||
val res2 = repl.replCompiler.compile(state, codeLine0)
|
val res2 = repl.replCompiler.compile(state, codeLine0)
|
||||||
val res2c = res2 as? ReplCompileResult.CompiledClasses
|
val res2c = res2 as? ReplCompileResult.CompiledClasses
|
||||||
TestCase.assertNotNull("Unexpected compile result: $res2", res2c)
|
TestCase.assertNotNull("Unexpected compile result: $res2", res2c)
|
||||||
@@ -60,7 +60,7 @@ class GenericReplTest : TestCase() {
|
|||||||
TestCase.assertNotNull("Unexpected eval result: $res21", res21e)
|
TestCase.assertNotNull("Unexpected eval result: $res21", res21e)
|
||||||
TestCase.assertEquals(3, res21e!!.value)
|
TestCase.assertEquals(3, res21e!!.value)
|
||||||
|
|
||||||
val codeLine1 = ReplCodeLine(1, "val x = 5")
|
val codeLine1 = ReplCodeLine(1, 0, "val x = 5")
|
||||||
val res3 = repl.replCompiler.compile(state, codeLine1)
|
val res3 = repl.replCompiler.compile(state, codeLine1)
|
||||||
val res3c = res3 as? ReplCompileResult.CompiledClasses
|
val res3c = res3 as? ReplCompileResult.CompiledClasses
|
||||||
TestCase.assertNotNull("Unexpected compile result: $res3", res3c)
|
TestCase.assertNotNull("Unexpected compile result: $res3", res3c)
|
||||||
@@ -69,10 +69,7 @@ class GenericReplTest : TestCase() {
|
|||||||
val res31e = res31 as? ReplEvalResult.UnitResult
|
val res31e = res31 as? ReplEvalResult.UnitResult
|
||||||
TestCase.assertNotNull("Unexpected eval result: $res31", res31e)
|
TestCase.assertNotNull("Unexpected eval result: $res31", res31e)
|
||||||
|
|
||||||
val codeLine2 = ReplCodeLine(2, "x + 2")
|
val codeLine2 = ReplCodeLine(2, 0, "x + 2")
|
||||||
val res4x = repl.replCompiler.compile(state, codeLine2)
|
|
||||||
TestCase.assertNotNull("Unexpected compile result: $res4x", res4x as? ReplCompileResult.HistoryMismatch)
|
|
||||||
|
|
||||||
val res4 = repl.replCompiler.compile(state, codeLine2)
|
val res4 = repl.replCompiler.compile(state, codeLine2)
|
||||||
val res4c = res4 as? ReplCompileResult.CompiledClasses
|
val res4c = res4 as? ReplCompileResult.CompiledClasses
|
||||||
TestCase.assertNotNull("Unexpected compile result: $res4", res4c)
|
TestCase.assertNotNull("Unexpected compile result: $res4", res4c)
|
||||||
@@ -113,7 +110,7 @@ class GenericReplTest : TestCase() {
|
|||||||
|
|
||||||
val state = repl.createState()
|
val state = repl.createState()
|
||||||
|
|
||||||
val codeLine1 = ReplCodeLine(0, "package mypackage\n\nval x = 1\nx+2")
|
val codeLine1 = ReplCodeLine(0, 0, "package mypackage\n\nval x = 1\nx+2")
|
||||||
val res1 = repl.replCompiler.compile(state, codeLine1)
|
val res1 = repl.replCompiler.compile(state, codeLine1)
|
||||||
val res1c = res1 as? ReplCompileResult.CompiledClasses
|
val res1c = res1 as? ReplCompileResult.CompiledClasses
|
||||||
TestCase.assertNotNull("Unexpected compile result: $res1", res1c)
|
TestCase.assertNotNull("Unexpected compile result: $res1", res1c)
|
||||||
@@ -123,7 +120,7 @@ class GenericReplTest : TestCase() {
|
|||||||
TestCase.assertNotNull("Unexpected eval result: $res11", res11e)
|
TestCase.assertNotNull("Unexpected eval result: $res11", res11e)
|
||||||
TestCase.assertEquals(3, res11e!!.value)
|
TestCase.assertEquals(3, res11e!!.value)
|
||||||
|
|
||||||
val codeLine2 = ReplCodeLine(1, "x+4")
|
val codeLine2 = ReplCodeLine(1, 0, "x+4")
|
||||||
val res2 = repl.replCompiler.compile(state, codeLine2)
|
val res2 = repl.replCompiler.compile(state, codeLine2)
|
||||||
val res2c = res2 as? ReplCompileResult.CompiledClasses
|
val res2c = res2 as? ReplCompileResult.CompiledClasses
|
||||||
TestCase.assertNotNull("Unexpected compile result: $res2", res2c)
|
TestCase.assertNotNull("Unexpected compile result: $res2", res2c)
|
||||||
|
|||||||
@@ -569,10 +569,10 @@ class CompilerDaemonTest : KotlinIntegrationTestBase() {
|
|||||||
val compilerState = replCompiler.createState()
|
val compilerState = replCompiler.createState()
|
||||||
val evaluatorState = localEvaluator.createState()
|
val evaluatorState = localEvaluator.createState()
|
||||||
|
|
||||||
val res0 = replCompiler.check(compilerState, ReplCodeLine(0, "val x ="))
|
val res0 = replCompiler.check(compilerState, ReplCodeLine(0, 0, "val x ="))
|
||||||
TestCase.assertTrue("Unexpected check results: $res0", res0 is ReplCheckResult.Incomplete)
|
TestCase.assertTrue("Unexpected check results: $res0", res0 is ReplCheckResult.Incomplete)
|
||||||
|
|
||||||
val codeLine1 = ReplCodeLine(1, "val lst = listOf(1)\nval x = 5")
|
val codeLine1 = ReplCodeLine(1, 0, "val lst = listOf(1)\nval x = 5")
|
||||||
val res1 = replCompiler.compile(compilerState, codeLine1)
|
val res1 = replCompiler.compile(compilerState, codeLine1)
|
||||||
val res1c = res1 as? ReplCompileResult.CompiledClasses
|
val res1c = res1 as? ReplCompileResult.CompiledClasses
|
||||||
TestCase.assertNotNull("Unexpected compile result: $res1", res1c)
|
TestCase.assertNotNull("Unexpected compile result: $res1", res1c)
|
||||||
@@ -581,10 +581,7 @@ class CompilerDaemonTest : KotlinIntegrationTestBase() {
|
|||||||
val res11e = res11 as? ReplEvalResult.UnitResult
|
val res11e = res11 as? ReplEvalResult.UnitResult
|
||||||
TestCase.assertNotNull("Unexpected eval result: $res11", res11e)
|
TestCase.assertNotNull("Unexpected eval result: $res11", res11e)
|
||||||
|
|
||||||
val codeLine2 = ReplCodeLine(2, "x + 2")
|
val codeLine2 = ReplCodeLine(2, 0, "x + 2")
|
||||||
val res2x = replCompiler.compile(compilerState, codeLine2)
|
|
||||||
TestCase.assertNotNull("Unexpected compile result: $res2x", res2x as? ReplCompileResult.HistoryMismatch)
|
|
||||||
|
|
||||||
val res2 = replCompiler.compile(compilerState, codeLine2)
|
val res2 = replCompiler.compile(compilerState, codeLine2)
|
||||||
val res2c = res2 as? ReplCompileResult.CompiledClasses
|
val res2c = res2 as? ReplCompileResult.CompiledClasses
|
||||||
TestCase.assertNotNull("Unexpected compile result: $res2", res2c)
|
TestCase.assertNotNull("Unexpected compile result: $res2", res2c)
|
||||||
@@ -619,7 +616,7 @@ class CompilerDaemonTest : KotlinIntegrationTestBase() {
|
|||||||
|
|
||||||
// use repl compiler for >> 1s, making sure that idle/unused timeouts are not firing
|
// use repl compiler for >> 1s, making sure that idle/unused timeouts are not firing
|
||||||
for (attempts in 1..10) {
|
for (attempts in 1..10) {
|
||||||
val codeLine1 = ReplCodeLine(attempts, "3 + 5")
|
val codeLine1 = ReplCodeLine(attempts, 0, "3 + 5")
|
||||||
val res1 = replCompiler.compile(compilerState, codeLine1)
|
val res1 = replCompiler.compile(compilerState, codeLine1)
|
||||||
val res1c = res1 as? ReplCompileResult.CompiledClasses
|
val res1c = res1 as? ReplCompileResult.CompiledClasses
|
||||||
TestCase.assertNotNull("Unexpected compile result: $res1", res1c)
|
TestCase.assertNotNull("Unexpected compile result: $res1", res1c)
|
||||||
|
|||||||
Reference in New Issue
Block a user