Make state of the default REPL compiler explicit and replaceable

restores proper object hierarchy in case the legacy REPL infrastructure
This commit is contained in:
Ilya Chernikov
2021-08-20 15:11:48 +02:00
committed by TeamCityServer
parent e7c9a46329
commit a45e31720c
9 changed files with 121 additions and 117 deletions
@@ -24,7 +24,7 @@ import java.util.concurrent.locks.ReentrantReadWriteLock
import kotlin.jvm.internal.TypeIntrinsics import kotlin.jvm.internal.TypeIntrinsics
import kotlin.reflect.KClass import kotlin.reflect.KClass
const val REPL_CODE_LINE_FIRST_NO = 1 const val REPL_CODE_LINE_FIRST_NO = 0
const val REPL_CODE_LINE_FIRST_GEN = 1 const val REPL_CODE_LINE_FIRST_GEN = 1
data class ReplCodeLine(val no: Int, val generation: Int, val code: String) : Serializable { data class ReplCodeLine(val no: Int, val generation: Int, val code: String) : Serializable {
@@ -125,7 +125,7 @@ internal class LegacyTestRepl : Closeable {
fun nextCodeLine(code: String): ReplCodeLine = ReplCodeLine(currentLineCounter.getAndIncrement(), 0, code) fun nextCodeLine(code: String): ReplCodeLine = ReplCodeLine(currentLineCounter.getAndIncrement(), 0, code)
val replCompiler: JvmReplCompiler by lazy { val replCompiler: JvmReplCompiler by lazy {
JvmReplCompiler(simpleScriptCompilationConfiguration, false) JvmReplCompiler(simpleScriptCompilationConfiguration)
} }
val compiledEvaluator: ReplEvaluator by lazy { val compiledEvaluator: ReplEvaluator by lazy {
@@ -8,12 +8,12 @@ package kotlin.script.experimental.jvmhost.test
import junit.framework.TestCase import junit.framework.TestCase
import kotlinx.coroutines.runBlocking import kotlinx.coroutines.runBlocking
import org.jetbrains.kotlin.scripting.compiler.plugin.impl.KJvmReplCompilerBase import org.jetbrains.kotlin.scripting.compiler.plugin.impl.KJvmReplCompilerBase
import org.jetbrains.kotlin.scripting.compiler.plugin.repl.ReplCodeAnalyzerBase
import org.junit.Assert import org.junit.Assert
import org.junit.Test import org.junit.Test
import kotlin.script.experimental.api.* import kotlin.script.experimental.api.*
import kotlin.script.experimental.host.toScriptSource import kotlin.script.experimental.host.toScriptSource
import kotlin.script.experimental.jvm.BasicJvmReplEvaluator import kotlin.script.experimental.jvm.BasicJvmReplEvaluator
import kotlin.script.experimental.jvm.defaultJvmScriptingHostConfiguration
import kotlin.script.experimental.jvm.updateClasspath import kotlin.script.experimental.jvm.updateClasspath
import kotlin.script.experimental.jvm.util.classpathFromClass import kotlin.script.experimental.jvm.util.classpathFromClass
import kotlin.script.experimental.jvmhost.createJvmScriptDefinitionFromTemplate import kotlin.script.experimental.jvmhost.createJvmScriptDefinitionFromTemplate
@@ -212,7 +212,7 @@ class ReplTest : TestCase() {
@Test @Test
fun testAddNewAnnotationHandler() { fun testAddNewAnnotationHandler() {
val replCompiler = KJvmReplCompilerBase.create(defaultJvmScriptingHostConfiguration) val replCompiler = KJvmReplCompilerBase<ReplCodeAnalyzerBase>()
val replEvaluator = BasicJvmReplEvaluator() val replEvaluator = BasicJvmReplEvaluator()
val compilationConfiguration = ScriptCompilationConfiguration().with { val compilationConfiguration = ScriptCompilationConfiguration().with {
updateClasspath(classpathFromClass<NewAnn>()) updateClasspath(classpathFromClass<NewAnn>())
@@ -224,7 +224,10 @@ class ReplTest : TestCase() {
replEvaluator.eval(it, evaluationConfiguration) replEvaluator.eval(it, evaluationConfiguration)
} }
} }
assertTrue("Expecting 1 got $res0", res0 is ResultWithDiagnostics.Success && (res0.value.get().result as ResultValue.Value).value == 1) assertTrue(
"Expecting 1 got $res0",
res0 is ResultWithDiagnostics.Success && (res0.value.get().result as ResultValue.Value).value == 1
)
var handlerInvoked = false var handlerInvoked = false
@@ -239,11 +242,17 @@ class ReplTest : TestCase() {
} }
val res1 = runBlocking { val res1 = runBlocking {
replCompiler.compile("@file:kotlin.script.experimental.jvmhost.test.NewAnn()\n2".toScriptSource("Line_1.kts"), compilationConfiguration2).onSuccess { replCompiler.compile(
"@file:kotlin.script.experimental.jvmhost.test.NewAnn()\n2".toScriptSource("Line_1.kts"),
compilationConfiguration2
).onSuccess {
replEvaluator.eval(it, evaluationConfiguration) replEvaluator.eval(it, evaluationConfiguration)
} }
} }
assertTrue("Expecting 2 got $res1", res1 is ResultWithDiagnostics.Success && (res1.value.get().result as ResultValue.Value).value == 2) assertTrue(
"Expecting 2 got $res1",
res1 is ResultWithDiagnostics.Success && (res1.value.get().result as ResultValue.Value).value == 2
)
assertTrue("Refinement handler on annotation is not invoked", handlerInvoked) assertTrue("Refinement handler on annotation is not invoked", handlerInvoked)
} }
@@ -275,7 +284,7 @@ class ReplTest : TestCase() {
evaluationConfiguration: ScriptEvaluationConfiguration? = simpleScriptEvaluationConfiguration, evaluationConfiguration: ScriptEvaluationConfiguration? = simpleScriptEvaluationConfiguration,
limit: Int = 0 limit: Int = 0
): Sequence<ResultWithDiagnostics<EvaluatedSnippet>> { ): Sequence<ResultWithDiagnostics<EvaluatedSnippet>> {
val replCompiler = KJvmReplCompilerBase.create(defaultJvmScriptingHostConfiguration) val replCompiler = KJvmReplCompilerBase<ReplCodeAnalyzerBase>()
val replEvaluator = BasicJvmReplEvaluator() val replEvaluator = BasicJvmReplEvaluator()
val currentEvalConfig = evaluationConfiguration ?: ScriptEvaluationConfiguration() val currentEvalConfig = evaluationConfiguration ?: ScriptEvaluationConfiguration()
val snipetsLimited = if (limit == 0) snippets else snippets.take(limit) val snipetsLimited = if (limit == 0) snippets else snippets.take(limit)
@@ -6,7 +6,6 @@
package kotlin.script.experimental.jvmhost.jsr223 package kotlin.script.experimental.jvmhost.jsr223
import org.jetbrains.kotlin.cli.common.repl.* import org.jetbrains.kotlin.cli.common.repl.*
import org.jetbrains.kotlin.cli.common.repl.ReplCompilerWithoutCheck
import java.util.concurrent.locks.ReentrantReadWriteLock import java.util.concurrent.locks.ReentrantReadWriteLock
import javax.script.ScriptContext import javax.script.ScriptContext
import javax.script.ScriptEngineFactory import javax.script.ScriptEngineFactory
@@ -63,7 +62,7 @@ class KotlinJsr223ScriptEngineImpl(
} }
override val replCompiler: ReplCompilerWithoutCheck by lazy { override val replCompiler: ReplCompilerWithoutCheck by lazy {
JvmReplCompiler(compilationConfiguration, true) JvmReplCompiler(compilationConfiguration)
} }
private val localEvaluator by lazy { private val localEvaluator by lazy {
@@ -5,18 +5,16 @@
package kotlin.script.experimental.jvmhost.repl package kotlin.script.experimental.jvmhost.repl
import org.jetbrains.kotlin.backend.common.push
import org.jetbrains.kotlin.cli.common.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.KJvmReplCompilerBase
import org.jetbrains.kotlin.scripting.compiler.plugin.repl.JvmReplCompilerStageHistory
import org.jetbrains.kotlin.scripting.compiler.plugin.repl.JvmReplCompilerState import org.jetbrains.kotlin.scripting.compiler.plugin.repl.JvmReplCompilerState
import org.jetbrains.kotlin.scripting.compiler.plugin.repl.ReplCodeAnalyzerBase import org.jetbrains.kotlin.scripting.compiler.plugin.repl.ReplCodeAnalyzerBase
import java.util.concurrent.locks.ReentrantReadWriteLock import java.util.concurrent.locks.ReentrantReadWriteLock
import kotlin.concurrent.write import kotlin.concurrent.write
import kotlin.script.experimental.api.* import kotlin.script.experimental.api.*
import kotlin.script.experimental.impl.internalScriptingRunSuspend
import kotlin.script.experimental.host.ScriptingHostConfiguration import kotlin.script.experimental.host.ScriptingHostConfiguration
import kotlin.script.experimental.host.withDefaultsFrom import kotlin.script.experimental.host.withDefaultsFrom
import kotlin.script.experimental.impl.internalScriptingRunSuspend
import kotlin.script.experimental.jvm.defaultJvmScriptingHostConfiguration import kotlin.script.experimental.jvm.defaultJvmScriptingHostConfiguration
import kotlin.script.experimental.jvm.util.isIncomplete import kotlin.script.experimental.jvm.util.isIncomplete
@@ -25,50 +23,28 @@ import kotlin.script.experimental.jvm.util.isIncomplete
*/ */
class JvmReplCompiler( class JvmReplCompiler(
val scriptCompilationConfiguration: ScriptCompilationConfiguration, val scriptCompilationConfiguration: ScriptCompilationConfiguration,
val allowReInit: Boolean = true, val hostConfiguration: ScriptingHostConfiguration = defaultJvmScriptingHostConfiguration
val hostConfiguration: ScriptingHostConfiguration = defaultJvmScriptingHostConfiguration,
var replCompiler: KJvmReplCompilerBase<ReplCodeAnalyzerBase> = KJvmReplCompilerBase.create(
hostConfiguration.withDefaultsFrom(defaultJvmScriptingHostConfiguration)
)
) : ReplCompilerWithoutCheck { ) : ReplCompilerWithoutCheck {
private val compilers = mutableListOf(replCompiler)
override fun createState(lock: ReentrantReadWriteLock): IReplStageState<*> = override fun createState(lock: ReentrantReadWriteLock): IReplStageState<*> =
if (allowReInit) { JvmReplCompilerState({ KJvmReplCompilerBase.createCompilationState(it, hostConfiguration) }, lock)
JvmReplCompilerState({ replCompiler.createReplCompilationState(it, replCompiler.initAnalyzer) }, lock)
} else {
replCompiler.state
}
override fun compile(state: IReplStageState<*>, codeLine: ReplCodeLine): ReplCompileResult = replCompiler.state.lock.write { override fun compile(state: IReplStageState<*>, codeLine: ReplCodeLine): ReplCompileResult = state.lock.write {
val replCompilerState = state.asState(JvmReplCompilerState::class.java) val replCompilerState = state.asState(JvmReplCompilerState::class.java)
val snippet = codeLine.toSourceCode(scriptCompilationConfiguration) val snippet = codeLine.toSourceCode(scriptCompilationConfiguration)
if (allowReInit) { val replCompiler = KJvmReplCompilerBase<ReplCodeAnalyzerBase>(
replCompiler = compilers.find { historiesEq(it.history, replCompilerState.history) } ?: { hostConfiguration.withDefaultsFrom(defaultJvmScriptingHostConfiguration),
compilers.push( replCompilerState
KJvmReplCompilerBase.create( )
hostConfiguration.withDefaultsFrom(defaultJvmScriptingHostConfiguration)
)
)
compilers.last()
}()
}
@Suppress("DEPRECATION_ERROR") @Suppress("DEPRECATION_ERROR")
when (val res = internalScriptingRunSuspend { replCompiler.compile(listOf(snippet), scriptCompilationConfiguration) }) { when (val res = internalScriptingRunSuspend { replCompiler.compile(listOf(snippet), scriptCompilationConfiguration) }) {
is ResultWithDiagnostics.Success -> { is ResultWithDiagnostics.Success -> {
val lineId = LineId(codeLine.no, 0, snippet.hashCode()) val lineId = LineId(codeLine.no, 0, snippet.hashCode())
replCompilerState.apply {
lock.write {
val compilerHistory = history as JvmReplCompilerStageHistory<*>
compilerHistory.push(lineId, replCompiler.history.last().item)
}
}
ReplCompileResult.CompiledClasses( ReplCompileResult.CompiledClasses(
lineId, lineId,
replCompiler.history.map { it.id }, replCompiler.state.history.map { it.id },
snippet.name!!, snippet.name!!,
emptyList(), emptyList(),
res.value.get().resultField != null, res.value.get().resultField != null,
@@ -87,15 +63,6 @@ class JvmReplCompiler(
} }
} }
} }
companion object {
fun historiesEq(history1: IReplStageHistory<*>, history2: IReplStageHistory<*>) =
history1.count() == history2.count() &&
history1.zip(history2).all {
val (it1, it2) = it
it1.item === it2.item
}
}
} }
@@ -105,9 +72,11 @@ internal class SourceCodeFromReplCodeLine(
) : SourceCode { ) : SourceCode {
override val text: String get() = codeLine.code override val text: String get() = codeLine.code
override val name: String = override val name: String =
"${compilationConfiguration[ScriptCompilationConfiguration.repl.makeSnippetIdentifier]!!( "${
compilationConfiguration, ReplSnippetIdImpl(codeLine.no, codeLine.generation, 0) compilationConfiguration[ScriptCompilationConfiguration.repl.makeSnippetIdentifier]!!(
)}.${compilationConfiguration[ScriptCompilationConfiguration.fileExtension]}" compilationConfiguration, ReplSnippetIdImpl(codeLine.no, codeLine.generation, 0)
)
}.${compilationConfiguration[ScriptCompilationConfiguration.fileExtension]}"
override val locationId: String? = null override val locationId: String? = null
} }
@@ -8,7 +8,10 @@ package org.jetbrains.kotlin.scripting.compiler.plugin.impl
import com.intellij.openapi.Disposable import com.intellij.openapi.Disposable
import org.jetbrains.kotlin.backend.common.phaser.PhaseConfig import org.jetbrains.kotlin.backend.common.phaser.PhaseConfig
import org.jetbrains.kotlin.backend.jvm.* import org.jetbrains.kotlin.backend.jvm.JvmGeneratorExtensionsImpl
import org.jetbrains.kotlin.backend.jvm.JvmIrCodegenFactory
import org.jetbrains.kotlin.backend.jvm.JvmNameProvider
import org.jetbrains.kotlin.backend.jvm.jvmPhases
import org.jetbrains.kotlin.backend.jvm.serialization.JvmIdSignatureDescriptor import org.jetbrains.kotlin.backend.jvm.serialization.JvmIdSignatureDescriptor
import org.jetbrains.kotlin.cli.common.CLIConfigurationKeys import org.jetbrains.kotlin.cli.common.CLIConfigurationKeys
import org.jetbrains.kotlin.cli.common.environment.setIdeaIoUseFallback import org.jetbrains.kotlin.cli.common.environment.setIdeaIoUseFallback
@@ -30,11 +33,12 @@ import org.jetbrains.kotlin.ir.util.SymbolTable
import org.jetbrains.kotlin.psi.KtFile import org.jetbrains.kotlin.psi.KtFile
import org.jetbrains.kotlin.resolve.calls.tower.ImplicitsExtensionsResolutionFilter import org.jetbrains.kotlin.resolve.calls.tower.ImplicitsExtensionsResolutionFilter
import org.jetbrains.kotlin.resolve.jvm.KotlinJavaPsiFacade import org.jetbrains.kotlin.resolve.jvm.KotlinJavaPsiFacade
import org.jetbrains.kotlin.scripting.compiler.plugin.repl.* import org.jetbrains.kotlin.scripting.compiler.plugin.repl.JvmReplCompilerState
import org.jetbrains.kotlin.scripting.compiler.plugin.repl.ReplCodeAnalyzerBase
import org.jetbrains.kotlin.scripting.compiler.plugin.repl.ReplImplicitsExtensionsResolutionFilter
import org.jetbrains.kotlin.scripting.definitions.ScriptDependenciesProvider import org.jetbrains.kotlin.scripting.definitions.ScriptDependenciesProvider
import org.jetbrains.kotlin.scripting.resolve.skipExtensionsResolutionForImplicits import org.jetbrains.kotlin.scripting.resolve.skipExtensionsResolutionForImplicits
import org.jetbrains.kotlin.scripting.resolve.skipExtensionsResolutionForImplicitsExceptInnermost import org.jetbrains.kotlin.scripting.resolve.skipExtensionsResolutionForImplicitsExceptInnermost
import java.util.concurrent.atomic.AtomicInteger
import kotlin.script.experimental.api.* import kotlin.script.experimental.api.*
import kotlin.script.experimental.host.ScriptingHostConfiguration import kotlin.script.experimental.host.ScriptingHostConfiguration
import kotlin.script.experimental.jvm.defaultJvmScriptingHostConfiguration import kotlin.script.experimental.jvm.defaultJvmScriptingHostConfiguration
@@ -43,49 +47,30 @@ import kotlin.script.experimental.util.LinkedSnippet
import kotlin.script.experimental.util.LinkedSnippetImpl import kotlin.script.experimental.util.LinkedSnippetImpl
import kotlin.script.experimental.util.add import kotlin.script.experimental.util.add
open class KJvmReplCompilerBase<AnalyzerT : ReplCodeAnalyzerBase> protected constructor( // NOTE: this implementation, as it is used in the REPL infrastructure, may be created for every snippet and provided with the state
// so it should not keep any compilation state outside of the stste field
open class KJvmReplCompilerBase<AnalyzerT : ReplCodeAnalyzerBase>(
protected val hostConfiguration: ScriptingHostConfiguration = defaultJvmScriptingHostConfiguration, protected val hostConfiguration: ScriptingHostConfiguration = defaultJvmScriptingHostConfiguration,
val initAnalyzer: (SharedScriptCompilationContext, ImplicitsExtensionsResolutionFilter) -> AnalyzerT val state: JvmReplCompilerState<*> = JvmReplCompilerState({ createCompilationState(it, hostConfiguration) })
) : ReplCompiler<KJvmCompiledScript>, ScriptCompiler { ) : ReplCompiler<KJvmCompiledScript>, ScriptCompiler {
val state = JvmReplCompilerState({ createReplCompilationState(it, initAnalyzer) })
val history = JvmReplCompilerStageHistory(state)
protected val scriptPriority = AtomicInteger()
private val resolutionFilter = ReplImplicitsExtensionsResolutionFilter()
override var lastCompiledSnippet: LinkedSnippetImpl<KJvmCompiledScript>? = null override var lastCompiledSnippet: LinkedSnippetImpl<KJvmCompiledScript>? = null
protected set protected set
fun createReplCompilationState(
scriptCompilationConfiguration: ScriptCompilationConfiguration,
initAnalyzer: (SharedScriptCompilationContext, ImplicitsExtensionsResolutionFilter) -> AnalyzerT /* = { ReplCodeAnalyzer1(it.environment) } */
): ReplCompilationState<AnalyzerT> {
val context = withMessageCollectorAndDisposable(disposeOnSuccess = false) { messageCollector, disposable ->
createIsolatedCompilationContext(
scriptCompilationConfiguration,
hostConfiguration,
messageCollector,
disposable
).asSuccess()
}.valueOr { throw IllegalStateException("Unable to initialize repl compiler:\n ${it.reports.joinToString("\n ")}") }
updateResolutionFilter(scriptCompilationConfiguration)
return ReplCompilationState(context, initAnalyzer, resolutionFilter)
}
override suspend fun compile( override suspend fun compile(
snippets: Iterable<SourceCode>, snippets: Iterable<SourceCode>,
configuration: ScriptCompilationConfiguration configuration: ScriptCompilationConfiguration
): ResultWithDiagnostics<LinkedSnippet<KJvmCompiledScript>> = ): ResultWithDiagnostics<LinkedSnippet<KJvmCompiledScript>> =
snippets.map { snippet -> snippets.map { snippet ->
withMessageCollector(snippet) { messageCollector -> withMessageCollector(snippet) { messageCollector ->
updateResolutionFilter(configuration)
val initialConfiguration = configuration.refineBeforeParsing(snippet).valueOr { val initialConfiguration = configuration.refineBeforeParsing(snippet).valueOr {
return it return it
} }
val compilationState = state.getCompilationState(initialConfiguration) @Suppress("UNCHECKED_CAST")
val compilationState = state.getCompilationState(initialConfiguration) as ReplCompilationState<AnalyzerT>
updateResolutionFilterWithHistory(configuration)
val (context, errorHolder, snippetKtFile) = prepareForAnalyze( val (context, errorHolder, snippetKtFile) = prepareForAnalyze(
snippet, snippet,
@@ -109,7 +94,7 @@ open class KJvmReplCompilerBase<AnalyzerT : ReplCodeAnalyzerBase> protected cons
if (messageCollector.hasErrors()) return failure(messageCollector) if (messageCollector.hasErrors()) return failure(messageCollector)
if (history.isEmpty()) { if (state.history.isEmpty()) {
val updatedConfiguration = ScriptDependenciesProvider.getInstance(context.environment.project) val updatedConfiguration = ScriptDependenciesProvider.getInstance(context.environment.project)
?.getScriptConfiguration(snippetKtFile)?.configuration ?.getScriptConfiguration(snippetKtFile)?.configuration
?: context.baseScriptCompilationConfiguration ?: context.baseScriptCompilationConfiguration
@@ -119,7 +104,7 @@ open class KJvmReplCompilerBase<AnalyzerT : ReplCodeAnalyzerBase> protected cons
) )
} }
val snippetNo = scriptPriority.getAndIncrement() val snippetNo = state.getNextLineNo()
val analysisResult = val analysisResult =
compilationState.analyzerEngine.analyzeReplLineWithImportedScripts( compilationState.analyzerEngine.analyzeReplLineWithImportedScripts(
@@ -147,7 +132,7 @@ open class KJvmReplCompilerBase<AnalyzerT : ReplCodeAnalyzerBase> protected cons
generateWithOldBackend(compilationState, snippetKtFile, sourceFiles) generateWithOldBackend(compilationState, snippetKtFile, sourceFiles)
} }
history.push(LineId(snippetNo, 0, snippet.hashCode()), scriptDescriptor) state.history.push(LineId(snippetNo, 0, snippet.hashCode()), scriptDescriptor)
val dependenciesProvider = ScriptDependenciesProvider.getInstance(context.environment.project) val dependenciesProvider = ScriptDependenciesProvider.getInstance(context.environment.project)
makeCompiledScript( makeCompiledScript(
@@ -185,7 +170,7 @@ open class KJvmReplCompilerBase<AnalyzerT : ReplCodeAnalyzerBase> protected cons
sourceFiles, sourceFiles,
compilationState.environment.configuration compilationState.environment.configuration
).build().also { generationState -> ).build().also { generationState ->
generationState.scriptSpecific.earlierScriptsForReplInterpreter = history.map { it.item } generationState.scriptSpecific.earlierScriptsForReplInterpreter = state.history.map { it.item }
generationState.beforeCompile() generationState.beforeCompile()
} }
KotlinCodegenFacade.generatePackage(generationState, snippetKtFile.script!!.containingKtFile.packageFqName, sourceFiles) KotlinCodegenFacade.generatePackage(generationState, snippetKtFile.script!!.containingKtFile.packageFqName, sourceFiles)
@@ -199,7 +184,7 @@ open class KJvmReplCompilerBase<AnalyzerT : ReplCodeAnalyzerBase> protected cons
sourceFiles: List<KtFile> sourceFiles: List<KtFile>
): GenerationState { ): GenerationState {
val generatorExtensions = object : JvmGeneratorExtensionsImpl(compilationState.environment.configuration) { val generatorExtensions = object : JvmGeneratorExtensionsImpl(compilationState.environment.configuration) {
override fun getPreviousScripts() = history.map { compilationState.symbolTable.referenceScript(it.item) } override fun getPreviousScripts() = state.history.map { compilationState.symbolTable.referenceScript(it.item) }
} }
val codegenFactory = JvmIrCodegenFactory( val codegenFactory = JvmIrCodegenFactory(
compilationState.environment.configuration, compilationState.environment.configuration,
@@ -282,7 +267,7 @@ open class KJvmReplCompilerBase<AnalyzerT : ReplCodeAnalyzerBase> protected cons
).asSuccess() ).asSuccess()
} }
protected fun updateResolutionFilter(configuration: ScriptCompilationConfiguration) { protected fun updateResolutionFilterWithHistory(configuration: ScriptCompilationConfiguration) {
val updatedConfiguration = updateConfigurationWithPreviousScripts(configuration) val updatedConfiguration = updateConfigurationWithPreviousScripts(configuration)
val classesToSkip = val classesToSkip =
@@ -290,9 +275,10 @@ open class KJvmReplCompilerBase<AnalyzerT : ReplCodeAnalyzerBase> protected cons
val classesToSkipAfterFirstTime = val classesToSkipAfterFirstTime =
updatedConfiguration[ScriptCompilationConfiguration.skipExtensionsResolutionForImplicitsExceptInnermost]!! updatedConfiguration[ScriptCompilationConfiguration.skipExtensionsResolutionForImplicitsExceptInnermost]!!
resolutionFilter.update(classesToSkip, classesToSkipAfterFirstTime) (state.compilation as ReplCompilationState<*>).implicitsResolutionFilter.update(classesToSkip, classesToSkipAfterFirstTime)
} }
private fun updateConfigurationWithPreviousScripts( private fun updateConfigurationWithPreviousScripts(
configuration: ScriptCompilationConfiguration configuration: ScriptCompilationConfiguration
): ScriptCompilationConfiguration { ): ScriptCompilationConfiguration {
@@ -313,23 +299,51 @@ open class KJvmReplCompilerBase<AnalyzerT : ReplCodeAnalyzerBase> protected cons
} }
companion object { companion object {
fun create(hostConfiguration: ScriptingHostConfiguration = defaultJvmScriptingHostConfiguration) =
KJvmReplCompilerBase(hostConfiguration) { context, resolutionFilter ->
ReplCodeAnalyzerBase(context.environment, implicitsResolutionFilter = resolutionFilter)
}
}
fun createCompilationState(
scriptCompilationConfiguration: ScriptCompilationConfiguration,
hostConfiguration: ScriptingHostConfiguration = defaultJvmScriptingHostConfiguration
): ReplCompilationState<ReplCodeAnalyzerBase> =
createCompilationState(scriptCompilationConfiguration, hostConfiguration) { context1, resolutionFilter ->
ReplCodeAnalyzerBase(context1.environment, implicitsResolutionFilter = resolutionFilter)
}
fun <AnalyzerT : ReplCodeAnalyzerBase> createCompilationState(
scriptCompilationConfiguration: ScriptCompilationConfiguration,
hostConfiguration: ScriptingHostConfiguration = defaultJvmScriptingHostConfiguration,
initAnalyzer: (SharedScriptCompilationContext, ImplicitsExtensionsResolutionFilter) -> AnalyzerT
): ReplCompilationState<AnalyzerT> {
val context = withMessageCollectorAndDisposable(disposeOnSuccess = false) { messageCollector, disposable ->
createIsolatedCompilationContext(
scriptCompilationConfiguration,
hostConfiguration,
messageCollector,
disposable
).asSuccess()
}.valueOr { throw IllegalStateException("Unable to initialize repl compiler:\n ${it.reports.joinToString("\n ")}") }
return ReplCompilationState(
context,
initAnalyzer,
ReplImplicitsExtensionsResolutionFilter(
scriptCompilationConfiguration[ScriptCompilationConfiguration.skipExtensionsResolutionForImplicits].orEmpty(),
scriptCompilationConfiguration[ScriptCompilationConfiguration.skipExtensionsResolutionForImplicitsExceptInnermost].orEmpty()
)
)
}
}
} }
class ReplCompilationState<AnalyzerT : ReplCodeAnalyzerBase>( class ReplCompilationState<AnalyzerT : ReplCodeAnalyzerBase>(
val context: SharedScriptCompilationContext, val context: SharedScriptCompilationContext,
val analyzerInit: (context: SharedScriptCompilationContext, implicitsResolutionFilter: ImplicitsExtensionsResolutionFilter) -> AnalyzerT, val analyzerInit: (context: SharedScriptCompilationContext, implicitsResolutionFilter: ImplicitsExtensionsResolutionFilter) -> AnalyzerT,
override val implicitsResolutionFilter: ImplicitsExtensionsResolutionFilter val implicitsResolutionFilter: ReplImplicitsExtensionsResolutionFilter
) : JvmReplCompilerState.Compilation { ) : JvmReplCompilerState.Compilation {
override val disposable: Disposable? get() = context.disposable override val disposable: Disposable? get() = context.disposable
override val baseScriptCompilationConfiguration: ScriptCompilationConfiguration get() = context.baseScriptCompilationConfiguration override val baseScriptCompilationConfiguration: ScriptCompilationConfiguration get() = context.baseScriptCompilationConfiguration
override val environment: KotlinCoreEnvironment get() = context.environment override val environment: KotlinCoreEnvironment get() = context.environment
override val analyzerEngine: AnalyzerT by lazy {
val analyzerEngine: AnalyzerT by lazy {
val analyzer = analyzerInit(context, implicitsResolutionFilter) val analyzer = analyzerInit(context, implicitsResolutionFilter)
val psiFacade = KotlinJavaPsiFacade.getInstance(environment.project) val psiFacade = KotlinJavaPsiFacade.getInstance(environment.project)
psiFacade.setNotFoundPackagesCachingStrategy(ReplNotFoundPackagesCachingStrategy) psiFacade.setNotFoundPackagesCachingStrategy(ReplNotFoundPackagesCachingStrategy)
@@ -344,6 +358,6 @@ class ReplCompilationState<AnalyzerT : ReplCodeAnalyzerBase>(
mangler to symbolTable mangler to symbolTable
} }
override val mangler: JvmDescriptorMangler get() = manglerAndSymbolTable.first val mangler: JvmDescriptorMangler get() = manglerAndSymbolTable.first
override val symbolTable: SymbolTable get() = manglerAndSymbolTable.second val symbolTable: SymbolTable get() = manglerAndSymbolTable.second
} }
@@ -16,7 +16,10 @@ import kotlin.concurrent.read
import kotlin.concurrent.write import kotlin.concurrent.write
import kotlin.script.experimental.api.KotlinType import kotlin.script.experimental.api.KotlinType
class ReplImplicitsExtensionsResolutionFilter : ImplicitsExtensionsResolutionFilter { class ReplImplicitsExtensionsResolutionFilter(
classesToSkip: Collection<KotlinType> = emptyList(),
classesToSkipAfterFirstTime: Collection<KotlinType> = emptyList()
) : ImplicitsExtensionsResolutionFilter {
private val lock = ReentrantReadWriteLock() private val lock = ReentrantReadWriteLock()
private var classesToSkipNames: Set<String> = emptySet() private var classesToSkipNames: Set<String> = emptySet()
private var classesToSkipFirstTimeNames: Set<String> = emptySet() private var classesToSkipFirstTimeNames: Set<String> = emptySet()
@@ -29,6 +32,10 @@ class ReplImplicitsExtensionsResolutionFilter : ImplicitsExtensionsResolutionFil
classesToSkipFirstTimeNames = classesToSkipAfterFirstTime.mapTo(hashSetOf()) { it.typeName } classesToSkipFirstTimeNames = classesToSkipAfterFirstTime.mapTo(hashSetOf()) { it.typeName }
} }
init {
update(classesToSkip, classesToSkipAfterFirstTime)
}
override fun getScopesWithInfo( override fun getScopesWithInfo(
scopes: Sequence<HierarchicalScope> scopes: Sequence<HierarchicalScope>
): Sequence<ScopeWithImplicitsExtensionsResolutionInfo> { ): Sequence<ScopeWithImplicitsExtensionsResolutionInfo> {
@@ -11,14 +11,11 @@ import org.jetbrains.kotlin.cli.common.repl.BasicReplStageHistory
import org.jetbrains.kotlin.cli.common.repl.IReplStageState import org.jetbrains.kotlin.cli.common.repl.IReplStageState
import org.jetbrains.kotlin.cli.jvm.compiler.KotlinCoreEnvironment import org.jetbrains.kotlin.cli.jvm.compiler.KotlinCoreEnvironment
import org.jetbrains.kotlin.descriptors.ScriptDescriptor import org.jetbrains.kotlin.descriptors.ScriptDescriptor
import org.jetbrains.kotlin.ir.backend.jvm.serialization.JvmDescriptorMangler
import org.jetbrains.kotlin.ir.util.SymbolTable
import org.jetbrains.kotlin.resolve.calls.tower.ImplicitsExtensionsResolutionFilter
import java.util.concurrent.locks.ReentrantReadWriteLock import java.util.concurrent.locks.ReentrantReadWriteLock
import kotlin.concurrent.write import kotlin.concurrent.write
import kotlin.script.experimental.api.ScriptCompilationConfiguration import kotlin.script.experimental.api.ScriptCompilationConfiguration
class JvmReplCompilerStageHistory<CompilationT : JvmReplCompilerState.Compilation>(private val state: JvmReplCompilerState<CompilationT>) : class JvmReplCompilerStageHistory<CompilationT : JvmReplCompilerState.Compilation>(state: JvmReplCompilerState<CompilationT>) :
BasicReplStageHistory<ScriptDescriptor>(state.lock) BasicReplStageHistory<ScriptDescriptor>(state.lock)
class JvmReplCompilerState<CompilationT : JvmReplCompilerState.Compilation>( class JvmReplCompilerState<CompilationT : JvmReplCompilerState.Compilation>(
@@ -63,9 +60,5 @@ class JvmReplCompilerState<CompilationT : JvmReplCompilerState.Compilation>(
val disposable: Disposable? val disposable: Disposable?
val baseScriptCompilationConfiguration: ScriptCompilationConfiguration val baseScriptCompilationConfiguration: ScriptCompilationConfiguration
val environment: KotlinCoreEnvironment val environment: KotlinCoreEnvironment
val analyzerEngine: ReplCodeAnalyzerBase
val implicitsResolutionFilter: ImplicitsExtensionsResolutionFilter
val mangler: JvmDescriptorMangler
val symbolTable: SymbolTable
} }
} }
@@ -12,16 +12,27 @@ import org.jetbrains.kotlin.psi.KtFile
import org.jetbrains.kotlin.renderer.DescriptorRenderer import org.jetbrains.kotlin.renderer.DescriptorRenderer
import org.jetbrains.kotlin.resolve.BindingContext import org.jetbrains.kotlin.resolve.BindingContext
import org.jetbrains.kotlin.scripting.compiler.plugin.impl.* import org.jetbrains.kotlin.scripting.compiler.plugin.impl.*
import org.jetbrains.kotlin.scripting.ide_services.compiler.impl.* import org.jetbrains.kotlin.scripting.compiler.plugin.repl.JvmReplCompilerState
import org.jetbrains.kotlin.scripting.ide_services.compiler.impl.IdeLikeReplCodeAnalyzer
import org.jetbrains.kotlin.scripting.ide_services.compiler.impl.KotlinResolutionFacadeForRepl
import org.jetbrains.kotlin.scripting.ide_services.compiler.impl.getKJvmCompletion
import org.jetbrains.kotlin.scripting.ide_services.compiler.impl.prepareCodeForCompletion
import kotlin.script.experimental.api.* import kotlin.script.experimental.api.*
import kotlin.script.experimental.host.ScriptingHostConfiguration import kotlin.script.experimental.host.ScriptingHostConfiguration
import kotlin.script.experimental.jvm.defaultJvmScriptingHostConfiguration import kotlin.script.experimental.jvm.defaultJvmScriptingHostConfiguration
import kotlin.script.experimental.jvm.util.calcAbsolute import kotlin.script.experimental.jvm.util.calcAbsolute
class KJvmReplCompilerWithIdeServices(hostConfiguration: ScriptingHostConfiguration = defaultJvmScriptingHostConfiguration) : class KJvmReplCompilerWithIdeServices(hostConfiguration: ScriptingHostConfiguration = defaultJvmScriptingHostConfiguration) :
KJvmReplCompilerBase<IdeLikeReplCodeAnalyzer>(hostConfiguration, { sharedScriptCompilationContext, scopeProcessor -> KJvmReplCompilerBase<IdeLikeReplCodeAnalyzer>(
IdeLikeReplCodeAnalyzer(sharedScriptCompilationContext.environment, scopeProcessor) hostConfiguration,
}), JvmReplCompilerState(
{
createCompilationState(it, hostConfiguration) { sharedScriptCompilationContext, scopeProcessor ->
IdeLikeReplCodeAnalyzer(sharedScriptCompilationContext.environment, scopeProcessor)
}
}
)
),
ReplCompleter, ReplCodeAnalyzer { ReplCompleter, ReplCodeAnalyzer {
override suspend fun complete( override suspend fun complete(
@@ -96,7 +107,6 @@ class KJvmReplCompilerWithIdeServices(hostConfiguration: ScriptingHostConfigurat
cursor: SourceCode.Position? = null, cursor: SourceCode.Position? = null,
getNewSnippet: (SourceCode, Int) -> SourceCode = { code, _ -> code } getNewSnippet: (SourceCode, Int) -> SourceCode = { code, _ -> code }
): ResultWithDiagnostics<AnalyzeWithCursorResult> { ): ResultWithDiagnostics<AnalyzeWithCursorResult> {
updateResolutionFilter(configuration)
val initialConfiguration = configuration.refineBeforeParsing(snippet).valueOr { val initialConfiguration = configuration.refineBeforeParsing(snippet).valueOr {
return it return it
@@ -105,7 +115,9 @@ class KJvmReplCompilerWithIdeServices(hostConfiguration: ScriptingHostConfigurat
val cursorAbs = cursor?.calcAbsolute(snippet) ?: -1 val cursorAbs = cursor?.calcAbsolute(snippet) ?: -1
val newSnippet = if (cursorAbs == -1) snippet else getNewSnippet(snippet, cursorAbs) val newSnippet = if (cursorAbs == -1) snippet else getNewSnippet(snippet, cursorAbs)
val compilationState = state.getCompilationState(initialConfiguration) val compilationState = state.getCompilationState(initialConfiguration) as ReplCompilationState<*>
updateResolutionFilterWithHistory(configuration)
val (_, errorHolder, snippetKtFile) = prepareForAnalyze( val (_, errorHolder, snippetKtFile) = prepareForAnalyze(
newSnippet, newSnippet,
@@ -114,8 +126,9 @@ class KJvmReplCompilerWithIdeServices(hostConfiguration: ScriptingHostConfigurat
failOnSyntaxErrors = false failOnSyntaxErrors = false
).valueOr { return it } ).valueOr { return it }
val analyzerEngine = compilationState.analyzerEngine as IdeLikeReplCodeAnalyzer
val analysisResult = val analysisResult =
compilationState.analyzerEngine.statelessAnalyzeWithImportedScripts(snippetKtFile, emptyList(), scriptPriority.get() + 1) analyzerEngine.statelessAnalyzeWithImportedScripts(snippetKtFile, emptyList(), state.getNextLineNo() + 1)
AnalyzerWithCompilerReport.reportDiagnostics(analysisResult.diagnostics, errorHolder) AnalyzerWithCompilerReport.reportDiagnostics(analysisResult.diagnostics, errorHolder)
val (_, bindingContext, resolutionFacade, moduleDescriptor, resultProperty) = when (analysisResult) { val (_, bindingContext, resolutionFacade, moduleDescriptor, resultProperty) = when (analysisResult) {