Reintroduce history check between compile and eval, place generation into code line and id

This commit is contained in:
Ilya Chernikov
2017-02-07 12:35:04 +01:00
parent 7b2ea001c1
commit b23911fd59
16 changed files with 82 additions and 89 deletions
@@ -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()
@@ -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 {
@@ -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)
@@ -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) {}
@@ -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)
@@ -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)