Scripting: refactor history handling in legacy REPL using new infra
#KT-47187 fixed
This commit is contained in:
@@ -36,6 +36,7 @@ data class LineId(override val no: Int, override val generation: Int, private va
|
||||
|
||||
open class BasicReplStageHistory<T>(override val lock: ReentrantReadWriteLock = ReentrantReadWriteLock()) : IReplStageHistory<T>, ArrayList<ReplHistoryRecord<T>>() {
|
||||
|
||||
val currentLineNumber = AtomicInteger(REPL_CODE_LINE_FIRST_NO)
|
||||
val currentGeneration = AtomicInteger(REPL_CODE_LINE_FIRST_GEN)
|
||||
|
||||
override fun push(id: ILineId, item: T) {
|
||||
@@ -51,6 +52,7 @@ open class BasicReplStageHistory<T>(override val lock: ReentrantReadWriteLock =
|
||||
val removed = map { it.id }
|
||||
clear()
|
||||
currentGeneration.incrementAndGet()
|
||||
currentLineNumber.set(REPL_CODE_LINE_FIRST_NO)
|
||||
return removed
|
||||
}
|
||||
}
|
||||
@@ -68,9 +70,13 @@ open class BasicReplStageHistory<T>(override val lock: ReentrantReadWriteLock =
|
||||
val removed = asSequence().drop(idx + 1).map { it.id }.toList()
|
||||
removeRange(idx + 1, size)
|
||||
currentGeneration.incrementAndGet()
|
||||
removed.lastOrNull()?.no?.let {
|
||||
currentLineNumber.set(it)
|
||||
}
|
||||
removed
|
||||
} else {
|
||||
currentGeneration.incrementAndGet()
|
||||
currentLineNumber.set(id.no + 1)
|
||||
emptyList()
|
||||
}
|
||||
}
|
||||
@@ -80,5 +86,7 @@ open class BasicReplStageState<HistoryItemT>(override final val lock: ReentrantR
|
||||
|
||||
override val currentGeneration: Int get() = history.currentGeneration.get()
|
||||
|
||||
override fun getNextLineNo(): Int = history.currentLineNumber.getAndIncrement()
|
||||
|
||||
override val history: BasicReplStageHistory<HistoryItemT> = BasicReplStageHistory(lock)
|
||||
}
|
||||
|
||||
+12
@@ -441,6 +441,18 @@ obj
|
||||
tempDir.toFile().deleteRecursively()
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testEvalWithCompilationError() {
|
||||
val engine = ScriptEngineManager().getEngineByExtension("kts")
|
||||
val compilable: Compilable = engine as Compilable
|
||||
assertThrows(ScriptException::class.java) {
|
||||
compilable.compile("foo")
|
||||
}
|
||||
compilable.compile("true")
|
||||
engine.eval("val x = 3")
|
||||
compilable.compile("x")
|
||||
}
|
||||
}
|
||||
|
||||
fun assertThrows(exceptionClass: Class<*>, body: () -> Unit) {
|
||||
|
||||
+13
@@ -369,6 +369,19 @@ class ReplTest : TestCase() {
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testCompileWithoutEval() {
|
||||
val replCompiler = KJvmReplCompilerBase<ReplCodeAnalyzerBase>()
|
||||
val replEvaluator = BasicJvmReplEvaluator()
|
||||
val compCfg = simpleScriptCompilationConfiguration
|
||||
runBlocking {
|
||||
replCompiler.compile("false".toScriptSource(), compCfg)
|
||||
replCompiler.compile("true".toScriptSource(), compCfg).onSuccess {
|
||||
replEvaluator.eval(it, simpleScriptEvaluationConfiguration)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
private fun positionsEqual(a: SourceCode.Position?, b: SourceCode.Position?): Boolean {
|
||||
if (a == null || b == null) {
|
||||
|
||||
+14
-2
@@ -7,6 +7,7 @@ package kotlin.script.experimental.jvmhost.repl
|
||||
|
||||
import org.jetbrains.kotlin.cli.common.repl.*
|
||||
import org.jetbrains.kotlin.scripting.compiler.plugin.impl.KJvmReplCompilerBase
|
||||
import org.jetbrains.kotlin.scripting.compiler.plugin.impl.currentLineId
|
||||
import org.jetbrains.kotlin.scripting.compiler.plugin.repl.JvmReplCompilerState
|
||||
import org.jetbrains.kotlin.scripting.compiler.plugin.repl.ReplCodeAnalyzerBase
|
||||
import java.util.concurrent.locks.ReentrantReadWriteLock
|
||||
@@ -38,10 +39,21 @@ class JvmReplCompiler(
|
||||
replCompilerState
|
||||
)
|
||||
|
||||
val lineId = LineId(codeLine.no, codeLine.generation, snippet.hashCode())
|
||||
|
||||
@Suppress("DEPRECATION_ERROR")
|
||||
when (val res = internalScriptingRunSuspend { replCompiler.compile(listOf(snippet), scriptCompilationConfiguration) }) {
|
||||
val res = internalScriptingRunSuspend {
|
||||
replCompiler.compile(
|
||||
listOf(snippet),
|
||||
scriptCompilationConfiguration.with {
|
||||
repl {
|
||||
currentLineId(lineId)
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
when (res) {
|
||||
is ResultWithDiagnostics.Success -> {
|
||||
val lineId = LineId(codeLine.no, 0, snippet.hashCode())
|
||||
ReplCompileResult.CompiledClasses(
|
||||
lineId,
|
||||
replCompiler.state.history.map { it.id },
|
||||
|
||||
+11
-3
@@ -45,6 +45,7 @@ import kotlin.script.experimental.jvm.defaultJvmScriptingHostConfiguration
|
||||
import kotlin.script.experimental.jvm.impl.KJvmCompiledScript
|
||||
import kotlin.script.experimental.util.LinkedSnippet
|
||||
import kotlin.script.experimental.util.LinkedSnippetImpl
|
||||
import kotlin.script.experimental.util.PropertiesCollection
|
||||
import kotlin.script.experimental.util.add
|
||||
|
||||
// NOTE: this implementation, as it is used in the REPL infrastructure, may be created for every snippet and provided with the state
|
||||
@@ -105,14 +106,16 @@ open class KJvmReplCompilerBase<AnalyzerT : ReplCodeAnalyzerBase>(
|
||||
)
|
||||
}
|
||||
|
||||
val snippetNo = state.getNextLineNo()
|
||||
// TODO: ensure that currentLineId passing is only used for single snippet compilation
|
||||
val lineId = configuration[ScriptCompilationConfiguration.repl.currentLineId]
|
||||
?: LineId(state.getNextLineNo(), state.currentGeneration, snippet.hashCode())
|
||||
|
||||
val analysisResult =
|
||||
compilationState.analyzerEngine.analyzeReplLineWithImportedScripts(
|
||||
snippetKtFile,
|
||||
sourceFiles.drop(1),
|
||||
snippet,
|
||||
snippetNo
|
||||
lineId.no
|
||||
)
|
||||
AnalyzerWithCompilerReport.reportDiagnostics(analysisResult.diagnostics, errorHolder, renderDiagnosticName = false)
|
||||
|
||||
@@ -148,7 +151,7 @@ open class KJvmReplCompilerBase<AnalyzerT : ReplCodeAnalyzerBase>(
|
||||
return failure(messageCollector, *scriptDiagnostics.toTypedArray())
|
||||
}
|
||||
|
||||
state.history.push(LineId(snippetNo, 0, snippet.hashCode()), scriptDescriptor)
|
||||
state.history.push(lineId, scriptDescriptor)
|
||||
|
||||
val dependenciesProvider = ScriptDependenciesProvider.getInstance(context.environment.project)
|
||||
makeCompiledScript(
|
||||
@@ -366,3 +369,8 @@ class ReplCompilationState<AnalyzerT : ReplCodeAnalyzerBase>(
|
||||
val mangler: JvmDescriptorMangler get() = manglerAndSymbolTable.first
|
||||
val symbolTable: SymbolTable get() = manglerAndSymbolTable.second
|
||||
}
|
||||
|
||||
/**
|
||||
* Internal property for transferring line id information when using new repl infrastructure with legacy one
|
||||
*/
|
||||
val ReplScriptCompilationConfigurationKeys.currentLineId by PropertiesCollection.key<LineId>(isTransient = true)
|
||||
|
||||
+1
@@ -26,6 +26,7 @@ class JvmReplCompilerState<CompilationT : JvmReplCompilerState.Compilation>(
|
||||
override val history = JvmReplCompilerStageHistory(this)
|
||||
|
||||
override val currentGeneration: Int get() = (history as BasicReplStageHistory<*>).currentGeneration.get()
|
||||
override fun getNextLineNo(): Int = history.currentLineNumber.getAndIncrement()
|
||||
|
||||
override fun dispose() {
|
||||
lock.write {
|
||||
|
||||
Reference in New Issue
Block a user