From b23911fd59897656ff7469722f7c9807a96c18f5 Mon Sep 17 00:00:00 2001 From: Ilya Chernikov Date: Tue, 7 Feb 2017 12:35:04 +0100 Subject: [PATCH] Reintroduce history check between compile and eval, place generation into code line and id --- .../cli/common/repl/AggregatedReplState.kt | 4 ++ .../kotlin/cli/common/repl/BasicReplState.kt | 20 +++++--- .../cli/common/repl/GenericEvaluatorState.kt | 2 + .../repl/GenericReplCompilingEvaluator.kt | 1 - .../cli/common/repl/GenericReplEvaluator.kt | 14 +++--- .../repl/KotlinJsr223JvmScriptEngineBase.kt | 14 +----- .../kotlin/cli/common/repl/ReplApi.kt | 8 ++-- .../kotlin/cli/common/repl/ReplState.kt | 47 ++++++++----------- .../kotlin/cli/common/repl/replUtil.kt | 4 +- .../cli/jvm/repl/GenericCompilerState.kt | 6 +-- .../kotlin/cli/jvm/repl/GenericReplChecker.kt | 2 +- .../cli/jvm/repl/GenericReplCompiler.kt | 5 +- .../kotlin/cli/jvm/repl/ReplInterpreter.kt | 2 +- .../daemon/client/RemoteReplCompilerState.kt | 16 +++++-- .../kotlin/cli/jvm/repl/GenericReplTest.kt | 15 +++--- .../kotlin/daemon/CompilerDaemonTest.kt | 11 ++--- 16 files changed, 82 insertions(+), 89 deletions(-) diff --git a/compiler/cli/cli-common/src/org/jetbrains/kotlin/cli/common/repl/AggregatedReplState.kt b/compiler/cli/cli-common/src/org/jetbrains/kotlin/cli/common/repl/AggregatedReplState.kt index 98088c6e02d..a70e9badb51 100644 --- a/compiler/cli/cli-common/src/org/jetbrains/kotlin/cli/common/repl/AggregatedReplState.kt +++ b/compiler/cli/cli-common/src/org/jetbrains/kotlin/cli/common/repl/AggregatedReplState.kt @@ -80,5 +80,9 @@ open class AggregatedReplStageState(val state1: IReplStageState, val target.isAssignableFrom(state2::class.java) -> state2 as StateT else -> super.asState(target) } + + override fun getNextLineNo() = state1.getNextLineNo() + + override val currentGeneration: Int get() = state1.currentGeneration } diff --git a/compiler/cli/cli-common/src/org/jetbrains/kotlin/cli/common/repl/BasicReplState.kt b/compiler/cli/cli-common/src/org/jetbrains/kotlin/cli/common/repl/BasicReplState.kt index a93a8ac686d..1f8b21a87b5 100644 --- a/compiler/cli/cli-common/src/org/jetbrains/kotlin/cli/common/repl/BasicReplState.kt +++ b/compiler/cli/cli-common/src/org/jetbrains/kotlin/cli/common/repl/BasicReplState.kt @@ -18,18 +18,19 @@ package org.jetbrains.kotlin.cli.common.repl import java.io.Serializable import java.util.* +import java.util.concurrent.atomic.AtomicInteger import java.util.concurrent.locks.ReentrantReadWriteLock 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(codeLine: ReplCodeLine): this(codeLine.no, codeLine.code.hashCode()) + constructor(no: Int, generation: Int, code: String): this(no, generation, code.hashCode()) + constructor(codeLine: ReplCodeLine): this(codeLine.no, codeLine.generation, 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 + no.compareTo(it.no).takeIf { it != 0 } + ?: generation.compareTo(it.generation).takeIf { it != 0 } + ?: codeHash.compareTo(it.codeHash) } ?: -1 // TODO: check if it doesn't break something companion object { @@ -39,6 +40,8 @@ data class LineId(override val no: Int, private val codeHash: Int) : ILineId, Se open class BasicReplStageHistory(override val lock: ReentrantReadWriteLock = ReentrantReadWriteLock()) : IReplStageHistory, ArrayList>() { + val currentGeneration = AtomicInteger(REPL_CODE_LINE_FIRST_GEN) + override fun push(id: ILineId, item: T) { lock.write { add(ReplHistoryRecord(id, item)) @@ -54,6 +57,7 @@ open class BasicReplStageHistory(override val lock: ReentrantReadWriteLock = if (idx < lastIndex) { val removed = asSequence().drop(idx + 1).map { it.id }.toList() removeRange(idx + 1, lastIndex) + currentGeneration.incrementAndGet() return removed } else return emptyList() @@ -61,7 +65,9 @@ open class BasicReplStageHistory(override val lock: ReentrantReadWriteLock = } } -class BasicReplStageState: IReplStageState { +open class BasicReplStageState: IReplStageState { + + override val currentGeneration: Int get() = history.currentGeneration.get() override val lock = ReentrantReadWriteLock() diff --git a/compiler/cli/cli-common/src/org/jetbrains/kotlin/cli/common/repl/GenericEvaluatorState.kt b/compiler/cli/cli-common/src/org/jetbrains/kotlin/cli/common/repl/GenericEvaluatorState.kt index 4aa677e333e..9e12d4b18b8 100644 --- a/compiler/cli/cli-common/src/org/jetbrains/kotlin/cli/common/repl/GenericEvaluatorState.kt +++ b/compiler/cli/cli-common/src/org/jetbrains/kotlin/cli/common/repl/GenericEvaluatorState.kt @@ -26,6 +26,8 @@ open class GenericReplEvaluatorState(baseClasspath: Iterable, baseClassloa { override val history: IReplStageHistory = BasicReplStageHistory(lock) + override val currentGeneration: Int get() = (history as BasicReplStageHistory<*>).currentGeneration.get() + val topClassLoader: ReplClassLoader = makeReplClassLoader(baseClassloader, baseClasspath) val currentClasspath: List get() = lock.read { diff --git a/compiler/cli/cli-common/src/org/jetbrains/kotlin/cli/common/repl/GenericReplCompilingEvaluator.kt b/compiler/cli/cli-common/src/org/jetbrains/kotlin/cli/common/repl/GenericReplCompilingEvaluator.kt index d231d8f25ea..691ba5ac5c4 100644 --- a/compiler/cli/cli-common/src/org/jetbrains/kotlin/cli/common/repl/GenericReplCompilingEvaluator.kt +++ b/compiler/cli/cli-common/src/org/jetbrains/kotlin/cli/common/repl/GenericReplCompilingEvaluator.kt @@ -36,7 +36,6 @@ class GenericReplCompilingEvaluator(val compiler: ReplCompiler, val compiled = compiler.compile(state, codeLine) when (compiled) { is ReplCompileResult.Error -> ReplEvalResult.Error.CompileTime(compiled.message, compiled.location) - is ReplCompileResult.HistoryMismatch -> ReplEvalResult.HistoryMismatch(compiled.lineNo) is ReplCompileResult.Incomplete -> ReplEvalResult.Incomplete() is ReplCompileResult.CompiledClasses -> { val result = eval(state, compiled, scriptArgs, invokeWrapper) diff --git a/compiler/cli/cli-common/src/org/jetbrains/kotlin/cli/common/repl/GenericReplEvaluator.kt b/compiler/cli/cli-common/src/org/jetbrains/kotlin/cli/common/repl/GenericReplEvaluator.kt index a33cc71efcd..392231fba77 100644 --- a/compiler/cli/cli-common/src/org/jetbrains/kotlin/cli/common/repl/GenericReplEvaluator.kt +++ b/compiler/cli/cli-common/src/org/jetbrains/kotlin/cli/common/repl/GenericReplEvaluator.kt @@ -113,10 +113,10 @@ open class GenericReplEvaluator(val baseClasspath: Iterable, } } -// val firstMismatch = historyActor.firstMismatch(verifyHistory) -// if (firstMismatch != null) { -// return@eval ReplEvalResult.HistoryMismatch(firstMismatch) -// } + val firstMismatch = historyActor.firstMismatch(compileResult.previousLines.asSequence()) + if (firstMismatch != null) { + return@eval ReplEvalResult.HistoryMismatch(firstMismatch.first?.id?.no ?: firstMismatch.second?.no ?: -1 /* means error? */) + } val (classLoader, scriptClass) = try { historyActor.processClasses(compileResult) @@ -172,7 +172,7 @@ private open class HistoryActionsForNoRepeat(val state: GenericReplEvaluatorStat open val effectiveHistory: List get() = state.history.map { it.item } - open fun firstMismatch(other: IReplStageHistory): Pair?, ReplHistoryRecord?>? = state.history.firstMismatch(other) + open fun firstMismatch(other: Sequence): Pair?, ILineId?>? = state.history.firstMismatch(other) 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 get() = super.effectiveHistory.dropLast(1) - override fun firstMismatch(other: IReplStageHistory): Pair?, ReplHistoryRecord?>? = + override fun firstMismatch(other: Sequence): Pair?, ILineId?>? = state.history.firstMismatchFiltered(other) { it.id != currentLast.id } override fun addPlaceholder(lineId: ILineId, value: EvalClassWithInstanceAndLoader) {} @@ -235,7 +235,7 @@ private open class HistoryActionsForRepeatAny(state: GenericReplEvaluatorState, override val effectiveHistory: List get() = state.history.takeWhile { it.id != matchingLine.id }.map { it.item } - override fun firstMismatch(other: IReplStageHistory): Pair?, ReplHistoryRecord?>? = + override fun firstMismatch(other: Sequence): Pair?, ILineId?>? = state.history.firstMismatchWhile(other) { it.id != matchingLine.id } override fun addPlaceholder(lineId: ILineId, value: EvalClassWithInstanceAndLoader) {} diff --git a/compiler/cli/cli-common/src/org/jetbrains/kotlin/cli/common/repl/KotlinJsr223JvmScriptEngineBase.kt b/compiler/cli/cli-common/src/org/jetbrains/kotlin/cli/common/repl/KotlinJsr223JvmScriptEngineBase.kt index 2965d042c74..dbf3c7690ba 100644 --- a/compiler/cli/cli-common/src/org/jetbrains/kotlin/cli/common/repl/KotlinJsr223JvmScriptEngineBase.kt +++ b/compiler/cli/cli-common/src/org/jetbrains/kotlin/cli/common/repl/KotlinJsr223JvmScriptEngineBase.kt @@ -23,17 +23,6 @@ import java.util.concurrent.locks.ReentrantReadWriteLock import javax.script.* 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 { @@ -53,7 +42,7 @@ abstract class KotlinJsr223JvmScriptEngineBase(protected val myFactory: ScriptEn 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) - 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) @@ -83,7 +72,6 @@ abstract class KotlinJsr223JvmScriptEngineBase(protected val myFactory: ScriptEn val compiled = when (result) { is ReplCompileResult.Error -> throw ScriptException("Error${result.locationString()}: ${result.message}") is ReplCompileResult.Incomplete -> throw ScriptException("error: incomplete code") - is ReplCompileResult.HistoryMismatch -> throw ScriptException("Repl history mismatch at line: ${result.lineNo}") is ReplCompileResult.CompiledClasses -> result } return CompiledKotlinScript(this, codeLine, compiled) diff --git a/compiler/cli/cli-common/src/org/jetbrains/kotlin/cli/common/repl/ReplApi.kt b/compiler/cli/cli-common/src/org/jetbrains/kotlin/cli/common/repl/ReplApi.kt index 8c2cd52fef7..c2d0514433f 100644 --- a/compiler/cli/cli-common/src/org/jetbrains/kotlin/cli/common/repl/ReplApi.kt +++ b/compiler/cli/cli-common/src/org/jetbrains/kotlin/cli/common/repl/ReplApi.kt @@ -23,7 +23,10 @@ import java.util.* import java.util.concurrent.locks.ReentrantReadWriteLock 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 { private val serialVersionUID: Long = 8228357578L } @@ -77,6 +80,7 @@ interface ReplCompileAction { sealed class ReplCompileResult : Serializable { class CompiledClasses(val lineId: LineId, + val previousLines: List, val mainClassName: String, val classes: List, val hasResult: Boolean, @@ -84,8 +88,6 @@ sealed class ReplCompileResult : Serializable { class Incomplete : ReplCompileResult() - class HistoryMismatch(val lineNo: Int) : ReplCompileResult() - class Error(val message: String, val location: CompilerMessageLocation = CompilerMessageLocation.NO_LOCATION) : ReplCompileResult() { override fun toString(): String = "Error(message = \"$message\"" diff --git a/compiler/cli/cli-common/src/org/jetbrains/kotlin/cli/common/repl/ReplState.kt b/compiler/cli/cli-common/src/org/jetbrains/kotlin/cli/common/repl/ReplState.kt index 3a121482045..3648c6bcca7 100644 --- a/compiler/cli/cli-common/src/org/jetbrains/kotlin/cli/common/repl/ReplState.kt +++ b/compiler/cli/cli-common/src/org/jetbrains/kotlin/cli/common/repl/ReplState.kt @@ -23,14 +23,13 @@ import kotlin.concurrent.write interface ILineId : Comparable { val no: Int + val generation: Int } data class ReplHistoryRecord (val id: ILineId, val item: T) interface IReplStageHistory : List> { - fun backwardIterator(): Iterator> = lock.read { asReversed().iterator() } // TODO: check perf - fun peek(): ReplHistoryRecord? = lock.read { lastOrNull() } fun push(id: ILineId, item: T) @@ -44,31 +43,6 @@ interface IReplStageHistory : List> { fun resetTo(id: ILineId): Iterable - fun firstMismatch(other: IReplStageHistory): Pair?, ReplHistoryRecord?>? = - lock.read { other.lock.read { - iterator().asSequence().zip(other.asSequence()).firstOrNull { it.first.id != it.second.id }?.let { it.first to it.second } - } } - - fun firstMismatchFiltered(other: IReplStageHistory, predicate: (ReplHistoryRecord) -> Boolean): Pair?, ReplHistoryRecord?>? = - 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 firstMismatchWhile(other: IReplStageHistory, predicate: (ReplHistoryRecord) -> Boolean): Pair?, ReplHistoryRecord?>? = - 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 matches(other: IReplStageHistory): Boolean = - lock.read { other.lock.read { - size == other.size && firstMismatch(other) == null - } } - - fun isProperPrefix(other: IReplStageHistory): Boolean = - lock.read { other.lock.read { - size == other.size - 1 && firstMismatch(other) == null - } } - val lock: ReentrantReadWriteLock } @@ -77,9 +51,28 @@ interface IReplStageState { 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 > asState(target: Class): StateT = if (target.isAssignableFrom(this::class.java)) this as StateT else throw IllegalArgumentException("$this is not an expected instance of IReplStageState") } +fun IReplStageHistory.firstMismatch(other: Sequence): Pair?, ILineId?>? = + lock.read { + iterator().asSequence().zip(other.asSequence()).firstOrNull { it.first.id != it.second }?.let { it.first to it.second } + } + +fun IReplStageHistory.firstMismatchFiltered(other: Sequence, predicate: (ReplHistoryRecord) -> Boolean): Pair?, ILineId?>? = + lock.read { + iterator().asSequence().filter(predicate).zip(other.asSequence()).firstOrNull { it.first.id != it.second }?.let { it.first to it.second } + } + +fun IReplStageHistory.firstMismatchWhile(other: Sequence, predicate: (ReplHistoryRecord) -> Boolean): Pair?, ILineId?>? = + lock.read { + iterator().asSequence().takeWhile(predicate).zip(other.asSequence()).firstOrNull { it.first.id != it.second }?.let { it.first to it.second } + } + diff --git a/compiler/cli/cli-common/src/org/jetbrains/kotlin/cli/common/repl/replUtil.kt b/compiler/cli/cli-common/src/org/jetbrains/kotlin/cli/common/repl/replUtil.kt index cf32181297f..3405b73c549 100644 --- a/compiler/cli/cli-common/src/org/jetbrains/kotlin/cli/common/repl/replUtil.kt +++ b/compiler/cli/cli-common/src/org/jetbrains/kotlin/cli/common/repl/replUtil.kt @@ -20,8 +20,8 @@ import com.google.common.base.Throwables import java.io.File import java.net.URLClassLoader -fun makeScriptBaseName(codeLine: ReplCodeLine, generation: Long) = - "Line_${codeLine.no}${if (generation > 1) "_gen_$generation" else ""}" +fun makeScriptBaseName(codeLine: ReplCodeLine) = + "Line_${codeLine.no}${if (codeLine.generation > REPL_CODE_LINE_FIRST_GEN) "_gen_${codeLine.generation}" else ""}" fun renderReplStackTrace(cause: Throwable, startFromMethodName: String): String { val newTrace = arrayListOf() diff --git a/compiler/cli/src/org/jetbrains/kotlin/cli/jvm/repl/GenericCompilerState.kt b/compiler/cli/src/org/jetbrains/kotlin/cli/jvm/repl/GenericCompilerState.kt index 4d15482d883..d20e9aa7091 100644 --- a/compiler/cli/src/org/jetbrains/kotlin/cli/jvm/repl/GenericCompilerState.kt +++ b/compiler/cli/src/org/jetbrains/kotlin/cli/jvm/repl/GenericCompilerState.kt @@ -28,7 +28,6 @@ import java.util.concurrent.locks.ReentrantReadWriteLock class ReplCompilerStageHistory(private val state: GenericReplCompilerState) : BasicReplStageHistory(state.lock) { override fun resetTo(id: ILineId): Iterable { - state.generation.incrementAndGet() val removedCompiledLines = super.resetTo(id) val removedAnalyzedLines = state.analyzerEngine.resetToLine(id.no) @@ -49,13 +48,14 @@ abstract class GenericReplCheckerState: IReplStageState { 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, GenericReplCheckerState() { - override val history: IReplStageHistory = ReplCompilerStageHistory(this) + override val history = ReplCompilerStageHistory(this) + + override val currentGeneration: Int get() = (history as BasicReplStageHistory<*>).currentGeneration.get() val analyzerEngine = ReplCodeAnalyzer(environment) diff --git a/compiler/cli/src/org/jetbrains/kotlin/cli/jvm/repl/GenericReplChecker.kt b/compiler/cli/src/org/jetbrains/kotlin/cli/jvm/repl/GenericReplChecker.kt index 7f8d378762a..274cafa81f9 100644 --- a/compiler/cli/src/org/jetbrains/kotlin/cli/jvm/repl/GenericReplChecker.kt +++ b/compiler/cli/src/org/jetbrains/kotlin/cli/jvm/repl/GenericReplChecker.kt @@ -71,7 +71,7 @@ open class GenericReplChecker( override fun check(state: IReplStageState<*>, codeLine: ReplCodeLine): ReplCheckResult { state.lock.write { val checkerState = state.asState(GenericReplCheckerState::class.java) - val scriptFileName = makeScriptBaseName(codeLine, checkerState.generation.get()) + val scriptFileName = makeScriptBaseName(codeLine) val virtualFile = LightVirtualFile("$scriptFileName${KotlinParserDefinition.STD_SCRIPT_EXT}", KotlinLanguage.INSTANCE, StringUtil.convertLineSeparators(codeLine.code)).apply { charset = CharsetToolkit.UTF8_CHARSET diff --git a/compiler/cli/src/org/jetbrains/kotlin/cli/jvm/repl/GenericReplCompiler.kt b/compiler/cli/src/org/jetbrains/kotlin/cli/jvm/repl/GenericReplCompiler.kt index 5bbd9a74f49..19677675d47 100644 --- a/compiler/cli/src/org/jetbrains/kotlin/cli/jvm/repl/GenericReplCompiler.kt +++ b/compiler/cli/src/org/jetbrains/kotlin/cli/jvm/repl/GenericReplCompiler.kt @@ -49,8 +49,6 @@ open class GenericReplCompiler(disposable: Disposable, state.lock.write { val compilerState = state.asState(GenericReplCompilerState::class.java) - val currentGeneration = compilerState.generation.get() - val (psiFile, errorHolder) = run { if (compilerState.lastLineState == null || compilerState.lastLineState!!.codeLine != codeLine) { val res = checker.check(state, codeLine) @@ -95,10 +93,11 @@ open class GenericReplCompiler(disposable: Disposable, setOf(psiFile.script!!.containingKtFile), org.jetbrains.kotlin.codegen.CompilationErrorHandler.THROW_EXCEPTION) - val generatedClassname = makeScriptBaseName(codeLine, currentGeneration) + val generatedClassname = makeScriptBaseName(codeLine) compilerState.history.push(LineId(codeLine), scriptDescriptor) return ReplCompileResult.CompiledClasses(LineId(codeLine), + compilerState.history.map { it.id }, generatedClassname, generationState.factory.asList().map { CompiledClassData(it.relativePath, it.asByteArray()) }, generationState.replSpecific.hasResult, diff --git a/compiler/cli/src/org/jetbrains/kotlin/cli/jvm/repl/ReplInterpreter.kt b/compiler/cli/src/org/jetbrains/kotlin/cli/jvm/repl/ReplInterpreter.kt index dae3b6a81b7..8a1634bae55 100644 --- a/compiler/cli/src/org/jetbrains/kotlin/cli/jvm/repl/ReplInterpreter.kt +++ b/compiler/cli/src/org/jetbrains/kotlin/cli/jvm/repl/ReplInterpreter.kt @@ -98,7 +98,7 @@ class ReplInterpreter( 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) val scriptDescriptor = when (analysisResult) { is ReplCodeAnalyzer.ReplLineAnalysisResult.WithErrors -> return LineResult.Error.CompileTime(errorHolder.renderedDiagnostics) diff --git a/compiler/daemon/daemon-client/src/org/jetbrains/kotlin/daemon/client/RemoteReplCompilerState.kt b/compiler/daemon/daemon-client/src/org/jetbrains/kotlin/daemon/client/RemoteReplCompilerState.kt index f297dfa70ff..7c60d6da4e2 100644 --- a/compiler/daemon/daemon-client/src/org/jetbrains/kotlin/daemon/client/RemoteReplCompilerState.kt +++ b/compiler/daemon/daemon-client/src/org/jetbrains/kotlin/daemon/client/RemoteReplCompilerState.kt @@ -16,11 +16,9 @@ package org.jetbrains.kotlin.daemon.client -import org.jetbrains.kotlin.cli.common.repl.ILineId -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.cli.common.repl.* import org.jetbrains.kotlin.daemon.common.ReplStateFacade +import java.util.concurrent.atomic.AtomicInteger import java.util.concurrent.locks.ReentrantReadWriteLock // NOTE: the lock is local @@ -39,12 +37,20 @@ class RemoteReplCompilerStateHistory(private val state: RemoteReplCompilerState) throw NotImplementedError("pop from remote history is not supported") } - override fun resetTo(id: ILineId): Iterable = state.replStateFacade.historyResetTo(id) + override fun resetTo(id: ILineId): Iterable = state.replStateFacade.historyResetTo(id).apply { + if (isNotEmpty()) { + currentGeneration.incrementAndGet() + } + } + + val currentGeneration = AtomicInteger(REPL_CODE_LINE_FIRST_GEN) override val lock: ReentrantReadWriteLock get() = state.lock } class RemoteReplCompilerState(internal val replStateFacade: ReplStateFacade, override val lock: ReentrantReadWriteLock = ReentrantReadWriteLock()) : IReplStageState { + override val currentGeneration: Int get() = (history as RemoteReplCompilerStateHistory).currentGeneration.get() + override val history: IReplStageHistory = RemoteReplCompilerStateHistory(this) } \ No newline at end of file diff --git a/compiler/tests/org/jetbrains/kotlin/cli/jvm/repl/GenericReplTest.kt b/compiler/tests/org/jetbrains/kotlin/cli/jvm/repl/GenericReplTest.kt index e08ba78cc19..c85c900db7d 100644 --- a/compiler/tests/org/jetbrains/kotlin/cli/jvm/repl/GenericReplTest.kt +++ b/compiler/tests/org/jetbrains/kotlin/cli/jvm/repl/GenericReplTest.kt @@ -47,10 +47,10 @@ class GenericReplTest : TestCase() { 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) - 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 res2c = res2 as? ReplCompileResult.CompiledClasses TestCase.assertNotNull("Unexpected compile result: $res2", res2c) @@ -60,7 +60,7 @@ class GenericReplTest : TestCase() { TestCase.assertNotNull("Unexpected eval result: $res21", res21e) 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 res3c = res3 as? ReplCompileResult.CompiledClasses TestCase.assertNotNull("Unexpected compile result: $res3", res3c) @@ -69,10 +69,7 @@ class GenericReplTest : TestCase() { val res31e = res31 as? ReplEvalResult.UnitResult TestCase.assertNotNull("Unexpected eval result: $res31", res31e) - val codeLine2 = ReplCodeLine(2, "x + 2") - val res4x = repl.replCompiler.compile(state, codeLine2) - TestCase.assertNotNull("Unexpected compile result: $res4x", res4x as? ReplCompileResult.HistoryMismatch) - + val codeLine2 = ReplCodeLine(2, 0, "x + 2") val res4 = repl.replCompiler.compile(state, codeLine2) val res4c = res4 as? ReplCompileResult.CompiledClasses TestCase.assertNotNull("Unexpected compile result: $res4", res4c) @@ -113,7 +110,7 @@ class GenericReplTest : TestCase() { 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 res1c = res1 as? ReplCompileResult.CompiledClasses TestCase.assertNotNull("Unexpected compile result: $res1", res1c) @@ -123,7 +120,7 @@ class GenericReplTest : TestCase() { TestCase.assertNotNull("Unexpected eval result: $res11", res11e) 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 res2c = res2 as? ReplCompileResult.CompiledClasses TestCase.assertNotNull("Unexpected compile result: $res2", res2c) diff --git a/compiler/tests/org/jetbrains/kotlin/daemon/CompilerDaemonTest.kt b/compiler/tests/org/jetbrains/kotlin/daemon/CompilerDaemonTest.kt index 3f31035eaeb..a201e14687a 100644 --- a/compiler/tests/org/jetbrains/kotlin/daemon/CompilerDaemonTest.kt +++ b/compiler/tests/org/jetbrains/kotlin/daemon/CompilerDaemonTest.kt @@ -569,10 +569,10 @@ class CompilerDaemonTest : KotlinIntegrationTestBase() { val compilerState = replCompiler.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) - 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 res1c = res1 as? ReplCompileResult.CompiledClasses TestCase.assertNotNull("Unexpected compile result: $res1", res1c) @@ -581,10 +581,7 @@ class CompilerDaemonTest : KotlinIntegrationTestBase() { val res11e = res11 as? ReplEvalResult.UnitResult TestCase.assertNotNull("Unexpected eval result: $res11", res11e) - val codeLine2 = ReplCodeLine(2, "x + 2") - val res2x = replCompiler.compile(compilerState, codeLine2) - TestCase.assertNotNull("Unexpected compile result: $res2x", res2x as? ReplCompileResult.HistoryMismatch) - + val codeLine2 = ReplCodeLine(2, 0, "x + 2") val res2 = replCompiler.compile(compilerState, codeLine2) val res2c = res2 as? ReplCompileResult.CompiledClasses 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 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 res1c = res1 as? ReplCompileResult.CompiledClasses TestCase.assertNotNull("Unexpected compile result: $res1", res1c)