Add new REPL API JVM implementation
This commit is contained in:
committed by
Ilya Chernikov
parent
4c2c44b106
commit
d2fec96f38
+248
@@ -0,0 +1,248 @@
|
||||
/*
|
||||
* Copyright 2010-2020 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.scripting.compiler.plugin.impl
|
||||
|
||||
|
||||
import com.intellij.openapi.Disposable
|
||||
import org.jetbrains.kotlin.cli.common.environment.setIdeaIoUseFallback
|
||||
import org.jetbrains.kotlin.cli.common.messages.AnalyzerWithCompilerReport
|
||||
import org.jetbrains.kotlin.cli.common.messages.MessageCollector
|
||||
import org.jetbrains.kotlin.cli.common.messages.MessageCollectorBasedReporter
|
||||
import org.jetbrains.kotlin.cli.common.repl.LineId
|
||||
import org.jetbrains.kotlin.cli.jvm.compiler.KotlinCoreEnvironment
|
||||
import org.jetbrains.kotlin.codegen.ClassBuilderFactories
|
||||
import org.jetbrains.kotlin.codegen.KotlinCodegenFacade
|
||||
import org.jetbrains.kotlin.codegen.state.GenerationState
|
||||
import org.jetbrains.kotlin.descriptors.ScriptDescriptor
|
||||
import org.jetbrains.kotlin.psi.KtFile
|
||||
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.ReplCodeAnalyzerBase
|
||||
import org.jetbrains.kotlin.scripting.definitions.ScriptDependenciesProvider
|
||||
import java.util.concurrent.atomic.AtomicInteger
|
||||
import kotlin.script.experimental.api.*
|
||||
import kotlin.script.experimental.host.ScriptingHostConfiguration
|
||||
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.add
|
||||
|
||||
open class KJvmReplCompilerBase<AnalyzerT : ReplCodeAnalyzerBase> protected constructor(
|
||||
protected val hostConfiguration: ScriptingHostConfiguration = defaultJvmScriptingHostConfiguration,
|
||||
val initAnalyzer: (SharedScriptCompilationContext) -> AnalyzerT
|
||||
) : ReplCompiler<KJvmCompiledScript>, ScriptCompiler {
|
||||
val state = JvmReplCompilerState({ createReplCompilationState(it, initAnalyzer) })
|
||||
val history = JvmReplCompilerStageHistory(state)
|
||||
protected val scriptPriority = AtomicInteger()
|
||||
|
||||
override var lastCompiledSnippet: LinkedSnippetImpl<KJvmCompiledScript>? = null
|
||||
protected set
|
||||
|
||||
fun createReplCompilationState(
|
||||
scriptCompilationConfiguration: ScriptCompilationConfiguration,
|
||||
initAnalyzer: (SharedScriptCompilationContext) -> 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 ")}") }
|
||||
return ReplCompilationState(context, initAnalyzer)
|
||||
}
|
||||
|
||||
override suspend fun compile(
|
||||
snippets: Iterable<SourceCode>,
|
||||
configuration: ScriptCompilationConfiguration
|
||||
): ResultWithDiagnostics<LinkedSnippet<KJvmCompiledScript>> =
|
||||
snippets.map { snippet ->
|
||||
withMessageCollector(snippet) { messageCollector ->
|
||||
val initialConfiguration = configuration.refineBeforeParsing(snippet).valueOr {
|
||||
return it
|
||||
}
|
||||
|
||||
val compilationState = state.getCompilationState(initialConfiguration)
|
||||
|
||||
val (context, errorHolder, snippetKtFile) = prepareForAnalyze(
|
||||
snippet,
|
||||
messageCollector,
|
||||
compilationState,
|
||||
checkSyntaxErrors = true
|
||||
).valueOr { return@withMessageCollector it }
|
||||
|
||||
val (sourceFiles, sourceDependencies) = collectRefinedSourcesAndUpdateEnvironment(
|
||||
context,
|
||||
snippetKtFile,
|
||||
messageCollector
|
||||
)
|
||||
|
||||
val firstFailure = sourceDependencies.firstOrNull { it.sourceDependencies is ResultWithDiagnostics.Failure }
|
||||
?.let { it.sourceDependencies as ResultWithDiagnostics.Failure }
|
||||
|
||||
if (firstFailure != null)
|
||||
return firstFailure
|
||||
|
||||
if (history.isEmpty()) {
|
||||
val updatedConfiguration = ScriptDependenciesProvider.getInstance(context.environment.project)
|
||||
?.getScriptConfiguration(snippetKtFile)?.configuration
|
||||
?: context.baseScriptCompilationConfiguration
|
||||
registerPackageFragmentProvidersIfNeeded(
|
||||
updatedConfiguration,
|
||||
context.environment
|
||||
)
|
||||
}
|
||||
|
||||
val no = scriptPriority.getAndIncrement()
|
||||
|
||||
val analysisResult =
|
||||
compilationState.analyzerEngine.analyzeReplLineWithImportedScripts(snippetKtFile, sourceFiles.drop(1), snippet, no)
|
||||
AnalyzerWithCompilerReport.reportDiagnostics(analysisResult.diagnostics, errorHolder)
|
||||
|
||||
val scriptDescriptor = when (analysisResult) {
|
||||
is ReplCodeAnalyzerBase.ReplLineAnalysisResult.WithErrors -> return failure(
|
||||
messageCollector
|
||||
)
|
||||
is ReplCodeAnalyzerBase.ReplLineAnalysisResult.Successful -> {
|
||||
(analysisResult.scriptDescriptor as? ScriptDescriptor)
|
||||
?: return failure(
|
||||
snippet,
|
||||
messageCollector,
|
||||
"Unexpected script descriptor type ${analysisResult.scriptDescriptor::class}"
|
||||
)
|
||||
}
|
||||
else -> return failure(
|
||||
snippet,
|
||||
messageCollector,
|
||||
"Unexpected result ${analysisResult::class.java}"
|
||||
)
|
||||
}
|
||||
|
||||
val generationState = GenerationState.Builder(
|
||||
snippetKtFile.project,
|
||||
ClassBuilderFactories.BINARIES,
|
||||
compilationState.analyzerEngine.module,
|
||||
compilationState.analyzerEngine.trace.bindingContext,
|
||||
sourceFiles,
|
||||
compilationState.environment.configuration
|
||||
).build().apply {
|
||||
scriptSpecific.earlierScriptsForReplInterpreter = history.map { it.item }
|
||||
beforeCompile()
|
||||
}
|
||||
KotlinCodegenFacade.generatePackage(generationState, snippetKtFile.script!!.containingKtFile.packageFqName, sourceFiles)
|
||||
|
||||
history.push(LineId(no, 0, snippet.hashCode()), scriptDescriptor)
|
||||
|
||||
val dependenciesProvider = ScriptDependenciesProvider.getInstance(context.environment.project)
|
||||
val compiledScript =
|
||||
makeCompiledScript(
|
||||
generationState,
|
||||
snippet,
|
||||
sourceFiles.first(),
|
||||
sourceDependencies
|
||||
) { ktFile ->
|
||||
dependenciesProvider?.getScriptConfiguration(ktFile)?.configuration
|
||||
?: context.baseScriptCompilationConfiguration
|
||||
}
|
||||
|
||||
lastCompiledSnippet = lastCompiledSnippet.add(compiledScript)
|
||||
|
||||
lastCompiledSnippet?.asSuccess(messageCollector.diagnostics)
|
||||
?: failure(
|
||||
snippet,
|
||||
messageCollector,
|
||||
"last compiled snippet should not be null"
|
||||
)
|
||||
}
|
||||
}.last()
|
||||
|
||||
override suspend fun invoke(
|
||||
script: SourceCode,
|
||||
scriptCompilationConfiguration: ScriptCompilationConfiguration
|
||||
): ResultWithDiagnostics<CompiledScript> {
|
||||
return when (val res = compile(script, scriptCompilationConfiguration)) {
|
||||
is ResultWithDiagnostics.Success -> res.value.get().asSuccess(res.reports)
|
||||
is ResultWithDiagnostics.Failure -> res
|
||||
}
|
||||
}
|
||||
|
||||
protected data class AnalyzePreparationResult(
|
||||
val context: SharedScriptCompilationContext,
|
||||
val errorHolder: MessageCollectorBasedReporter,
|
||||
val snippetKtFile: KtFile
|
||||
)
|
||||
|
||||
protected fun prepareForAnalyze(
|
||||
snippet: SourceCode,
|
||||
parentMessageCollector: MessageCollector,
|
||||
compilationState: JvmReplCompilerState.Compilation,
|
||||
checkSyntaxErrors: Boolean
|
||||
): ResultWithDiagnostics<AnalyzePreparationResult> =
|
||||
withMessageCollector(
|
||||
snippet,
|
||||
parentMessageCollector
|
||||
) { messageCollector ->
|
||||
val context =
|
||||
(compilationState as? ReplCompilationState<*>)?.context
|
||||
?: return failure(
|
||||
snippet, messageCollector, "Internal error: unknown parameter passed as compilationState: $compilationState"
|
||||
)
|
||||
|
||||
setIdeaIoUseFallback()
|
||||
|
||||
val errorHolder = object : MessageCollectorBasedReporter {
|
||||
override val messageCollector = messageCollector
|
||||
}
|
||||
|
||||
val snippetKtFile =
|
||||
getScriptKtFile(
|
||||
snippet,
|
||||
context.baseScriptCompilationConfiguration,
|
||||
context.environment.project,
|
||||
messageCollector
|
||||
)
|
||||
.valueOr { return it }
|
||||
|
||||
if (checkSyntaxErrors) {
|
||||
val syntaxErrorReport = AnalyzerWithCompilerReport.reportSyntaxErrors(snippetKtFile, errorHolder)
|
||||
if (syntaxErrorReport.isHasErrors && syntaxErrorReport.isAllErrorsAtEof) return failure(
|
||||
messageCollector, ScriptDiagnostic(ScriptDiagnostic.incompleteCode, "Incomplete code")
|
||||
)
|
||||
if (syntaxErrorReport.isHasErrors) return failure(
|
||||
messageCollector
|
||||
)
|
||||
}
|
||||
|
||||
return AnalyzePreparationResult(
|
||||
context,
|
||||
errorHolder,
|
||||
snippetKtFile
|
||||
).asSuccess()
|
||||
}
|
||||
|
||||
companion object {
|
||||
fun create(hostConfiguration: ScriptingHostConfiguration = defaultJvmScriptingHostConfiguration) =
|
||||
KJvmReplCompilerBase(hostConfiguration) {
|
||||
ReplCodeAnalyzerBase(it.environment)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
class ReplCompilationState<AnalyzerT : ReplCodeAnalyzerBase>(
|
||||
val context: SharedScriptCompilationContext,
|
||||
val analyzerInit: (context: SharedScriptCompilationContext) -> AnalyzerT
|
||||
) : JvmReplCompilerState.Compilation {
|
||||
override val disposable: Disposable? get() = context.disposable
|
||||
override val baseScriptCompilationConfiguration: ScriptCompilationConfiguration get() = context.baseScriptCompilationConfiguration
|
||||
override val environment: KotlinCoreEnvironment get() = context.environment
|
||||
override val analyzerEngine: AnalyzerT by lazy {
|
||||
// ReplCodeAnalyzer1(context.environment)
|
||||
analyzerInit(context)
|
||||
}
|
||||
}
|
||||
-186
@@ -1,186 +0,0 @@
|
||||
/*
|
||||
* Copyright 2010-2019 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.scripting.compiler.plugin.impl
|
||||
|
||||
import com.intellij.openapi.Disposable
|
||||
import com.intellij.openapi.project.Project
|
||||
import org.jetbrains.kotlin.cli.common.environment.setIdeaIoUseFallback
|
||||
import org.jetbrains.kotlin.cli.common.messages.AnalyzerWithCompilerReport
|
||||
import org.jetbrains.kotlin.cli.common.messages.MessageCollectorBasedReporter
|
||||
import org.jetbrains.kotlin.cli.common.repl.IReplStageHistory
|
||||
import org.jetbrains.kotlin.cli.common.repl.LineId
|
||||
import org.jetbrains.kotlin.cli.common.repl.ReplCodeLine
|
||||
import org.jetbrains.kotlin.cli.jvm.compiler.KotlinCoreEnvironment
|
||||
import org.jetbrains.kotlin.codegen.ClassBuilderFactories
|
||||
import org.jetbrains.kotlin.codegen.KotlinCodegenFacade
|
||||
import org.jetbrains.kotlin.codegen.state.GenerationState
|
||||
import org.jetbrains.kotlin.descriptors.ScriptDescriptor
|
||||
import org.jetbrains.kotlin.scripting.compiler.plugin.repl.JvmReplCompilerState
|
||||
import org.jetbrains.kotlin.scripting.compiler.plugin.repl.KJvmReplCompilerProxy
|
||||
import org.jetbrains.kotlin.scripting.compiler.plugin.repl.ReplCodeAnalyzer
|
||||
import org.jetbrains.kotlin.scripting.definitions.ScriptDependenciesProvider
|
||||
import kotlin.script.experimental.api.*
|
||||
import kotlin.script.experimental.host.ScriptingHostConfiguration
|
||||
|
||||
class KJvmReplCompilerImpl(val hostConfiguration: ScriptingHostConfiguration) : KJvmReplCompilerProxy {
|
||||
|
||||
override fun createReplCompilationState(scriptCompilationConfiguration: ScriptCompilationConfiguration): JvmReplCompilerState.Compilation {
|
||||
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)
|
||||
}
|
||||
|
||||
override fun checkSyntax(
|
||||
script: SourceCode,
|
||||
scriptCompilationConfiguration: ScriptCompilationConfiguration,
|
||||
project: Project
|
||||
): ResultWithDiagnostics<Boolean> =
|
||||
withMessageCollector(script) { messageCollector ->
|
||||
val ktFile = getScriptKtFile(
|
||||
script,
|
||||
scriptCompilationConfiguration,
|
||||
project,
|
||||
messageCollector
|
||||
)
|
||||
.valueOr { return it }
|
||||
val errorHolder = object : MessageCollectorBasedReporter {
|
||||
override val messageCollector = messageCollector
|
||||
}
|
||||
val syntaxErrorReport = AnalyzerWithCompilerReport.reportSyntaxErrors(ktFile, errorHolder)
|
||||
when {
|
||||
syntaxErrorReport.isHasErrors && syntaxErrorReport.isAllErrorsAtEof -> false.asSuccess(messageCollector.diagnostics)
|
||||
syntaxErrorReport.isHasErrors -> failure(messageCollector)
|
||||
else -> true.asSuccess()
|
||||
}
|
||||
}
|
||||
|
||||
override fun compileReplSnippet(
|
||||
compilationState: JvmReplCompilerState.Compilation,
|
||||
snippet: SourceCode,
|
||||
snippetId: ReplSnippetId,
|
||||
// TODO: replace history with some interface based on CompiledScript
|
||||
history: IReplStageHistory<ScriptDescriptor>
|
||||
): ResultWithDiagnostics<CompiledScript> =
|
||||
withMessageCollector(snippet) { messageCollector ->
|
||||
|
||||
val context = (compilationState as? ReplCompilationState)?.context
|
||||
?: return failure(
|
||||
snippet, messageCollector, "Internal error: unknown parameter passed as compilationState: $compilationState"
|
||||
)
|
||||
|
||||
setIdeaIoUseFallback()
|
||||
|
||||
// NOTE: converting between REPL entities from compiler and "new" scripting entities
|
||||
// TODO: (big) move REPL API from compiler to the new scripting infrastructure and streamline ops
|
||||
val codeLine = makeReplCodeLine(snippetId, snippet)
|
||||
|
||||
val errorHolder = object : MessageCollectorBasedReporter {
|
||||
override val messageCollector = messageCollector
|
||||
}
|
||||
|
||||
val snippetKtFile =
|
||||
getScriptKtFile(
|
||||
snippet,
|
||||
context.baseScriptCompilationConfiguration,
|
||||
context.environment.project,
|
||||
messageCollector
|
||||
)
|
||||
.valueOr { return it }
|
||||
|
||||
val syntaxErrorReport = AnalyzerWithCompilerReport.reportSyntaxErrors(snippetKtFile, errorHolder)
|
||||
if (syntaxErrorReport.isHasErrors) return failure(messageCollector)
|
||||
|
||||
val (sourceFiles, sourceDependencies) = collectRefinedSourcesAndUpdateEnvironment(
|
||||
context,
|
||||
snippetKtFile,
|
||||
messageCollector
|
||||
)
|
||||
|
||||
val firstFailure = sourceDependencies.firstOrNull { it.sourceDependencies is ResultWithDiagnostics.Failure }
|
||||
?.let { it.sourceDependencies as ResultWithDiagnostics.Failure }
|
||||
|
||||
if (firstFailure != null)
|
||||
return firstFailure
|
||||
|
||||
if (history.isEmpty()) {
|
||||
val updatedConfiguration = ScriptDependenciesProvider.getInstance(context.environment.project)
|
||||
?.getScriptConfiguration(snippetKtFile)?.configuration
|
||||
?: context.baseScriptCompilationConfiguration
|
||||
registerPackageFragmentProvidersIfNeeded(updatedConfiguration, context.environment)
|
||||
}
|
||||
|
||||
val analysisResult =
|
||||
compilationState.analyzerEngine.analyzeReplLineWithImportedScripts(snippetKtFile, sourceFiles.drop(1), codeLine)
|
||||
AnalyzerWithCompilerReport.reportDiagnostics(analysisResult.diagnostics, errorHolder)
|
||||
|
||||
val scriptDescriptor = when (analysisResult) {
|
||||
is ReplCodeAnalyzer.ReplLineAnalysisResult.WithErrors -> return failure(
|
||||
messageCollector
|
||||
)
|
||||
is ReplCodeAnalyzer.ReplLineAnalysisResult.Successful -> {
|
||||
(analysisResult.scriptDescriptor as? ScriptDescriptor)
|
||||
?: return failure(
|
||||
snippet,
|
||||
messageCollector,
|
||||
"Unexpected script descriptor type ${analysisResult.scriptDescriptor::class}"
|
||||
)
|
||||
}
|
||||
else -> return failure(
|
||||
snippet,
|
||||
messageCollector,
|
||||
"Unexpected result ${analysisResult::class.java}"
|
||||
)
|
||||
}
|
||||
|
||||
val generationState = GenerationState.Builder(
|
||||
snippetKtFile.project,
|
||||
ClassBuilderFactories.BINARIES,
|
||||
compilationState.analyzerEngine.module,
|
||||
compilationState.analyzerEngine.trace.bindingContext,
|
||||
sourceFiles,
|
||||
compilationState.environment.configuration
|
||||
).build().apply {
|
||||
scriptSpecific.earlierScriptsForReplInterpreter = history.map { it.item }
|
||||
beforeCompile()
|
||||
}
|
||||
KotlinCodegenFacade.generatePackage(generationState, snippetKtFile.script!!.containingKtFile.packageFqName, sourceFiles)
|
||||
|
||||
history.push(LineId(codeLine), scriptDescriptor)
|
||||
|
||||
val dependenciesProvider = ScriptDependenciesProvider.getInstance(context.environment.project)
|
||||
val compiledScript =
|
||||
makeCompiledScript(
|
||||
generationState,
|
||||
snippet,
|
||||
sourceFiles.first(),
|
||||
sourceDependencies
|
||||
) { ktFile ->
|
||||
dependenciesProvider?.getScriptConfiguration(ktFile)?.configuration
|
||||
?: context.baseScriptCompilationConfiguration
|
||||
}
|
||||
|
||||
compiledScript.asSuccess(messageCollector.diagnostics)
|
||||
}
|
||||
}
|
||||
|
||||
internal class ReplCompilationState(val context: SharedScriptCompilationContext) : JvmReplCompilerState.Compilation {
|
||||
override val disposable: Disposable? get() = context.disposable
|
||||
override val baseScriptCompilationConfiguration: ScriptCompilationConfiguration get() = context.baseScriptCompilationConfiguration
|
||||
override val environment: KotlinCoreEnvironment get() = context.environment
|
||||
override val analyzerEngine: ReplCodeAnalyzer by lazy {
|
||||
ReplCodeAnalyzer(context.environment)
|
||||
}
|
||||
}
|
||||
|
||||
internal fun makeReplCodeLine(id: ReplSnippetId, code: SourceCode): ReplCodeLine =
|
||||
ReplCodeLine(id.no, id.generation, code.text)
|
||||
|
||||
+2
-2
@@ -52,14 +52,14 @@ import kotlin.script.experimental.jvm.jvm
|
||||
import kotlin.script.experimental.jvm.util.KotlinJars
|
||||
import kotlin.script.experimental.jvm.withUpdatedClasspath
|
||||
|
||||
internal class SharedScriptCompilationContext(
|
||||
class SharedScriptCompilationContext(
|
||||
val disposable: Disposable?,
|
||||
val baseScriptCompilationConfiguration: ScriptCompilationConfiguration,
|
||||
val environment: KotlinCoreEnvironment,
|
||||
val ignoredOptionsReportingState: IgnoredOptionsReportingState
|
||||
)
|
||||
|
||||
internal fun createIsolatedCompilationContext(
|
||||
fun createIsolatedCompilationContext(
|
||||
baseScriptCompilationConfiguration: ScriptCompilationConfiguration,
|
||||
hostConfiguration: ScriptingHostConfiguration,
|
||||
messageCollector: ScriptDiagnosticsMessageCollector,
|
||||
|
||||
+4
-4
@@ -18,7 +18,7 @@ import kotlin.script.experimental.api.ScriptDiagnostic
|
||||
import kotlin.script.experimental.api.SourceCode
|
||||
import kotlin.script.experimental.api.asErrorDiagnostics
|
||||
|
||||
internal class ScriptDiagnosticsMessageCollector(private val parentMessageCollector: MessageCollector?) : MessageCollector {
|
||||
class ScriptDiagnosticsMessageCollector(private val parentMessageCollector: MessageCollector?) : MessageCollector {
|
||||
|
||||
private val _diagnostics = arrayListOf<ScriptDiagnostic>()
|
||||
|
||||
@@ -78,17 +78,17 @@ private fun ScriptDiagnostic.Severity.toCompilerMessageSeverity(): CompilerMessa
|
||||
ScriptDiagnostic.Severity.FATAL -> CompilerMessageSeverity.EXCEPTION
|
||||
}
|
||||
|
||||
internal fun failure(
|
||||
fun failure(
|
||||
messageCollector: ScriptDiagnosticsMessageCollector, vararg diagnostics: ScriptDiagnostic
|
||||
): ResultWithDiagnostics.Failure =
|
||||
ResultWithDiagnostics.Failure(*messageCollector.diagnostics.toTypedArray(), *diagnostics)
|
||||
|
||||
internal fun failure(
|
||||
fun failure(
|
||||
script: SourceCode, messageCollector: ScriptDiagnosticsMessageCollector, message: String
|
||||
): ResultWithDiagnostics.Failure =
|
||||
failure(messageCollector, message.asErrorDiagnostics(path = script.locationId))
|
||||
|
||||
internal class IgnoredOptionsReportingState {
|
||||
class IgnoredOptionsReportingState {
|
||||
var currentArguments = K2JVMCompilerArguments()
|
||||
}
|
||||
|
||||
|
||||
+14
-3
@@ -21,6 +21,7 @@ import org.jetbrains.kotlin.scripting.compiler.plugin.dependencies.ScriptsCompil
|
||||
import org.jetbrains.kotlin.scripting.resolve.ScriptLightVirtualFile
|
||||
import org.jetbrains.kotlin.scripting.scriptFileName
|
||||
import org.jetbrains.kotlin.utils.addToStdlib.firstIsInstanceOrNull
|
||||
import java.io.Serializable
|
||||
import java.util.*
|
||||
import kotlin.script.experimental.api.*
|
||||
import kotlin.script.experimental.host.FileBasedScriptSource
|
||||
@@ -33,7 +34,7 @@ internal fun makeCompiledModule(generationState: GenerationState) =
|
||||
.associateTo(sortedMapOf<String, ByteArray>()) { it.relativePath to it.asByteArray() }
|
||||
)
|
||||
|
||||
internal inline fun <T> withMessageCollectorAndDisposable(
|
||||
inline fun <T> withMessageCollectorAndDisposable(
|
||||
script: SourceCode? = null,
|
||||
parentMessageCollector: MessageCollector? = null,
|
||||
disposable: Disposable = Disposer.newDisposable(),
|
||||
@@ -57,7 +58,7 @@ internal inline fun <T> withMessageCollectorAndDisposable(
|
||||
}
|
||||
}
|
||||
|
||||
internal inline fun <T> withMessageCollector(
|
||||
inline fun <T> withMessageCollector(
|
||||
script: SourceCode? = null,
|
||||
parentMessageCollector: MessageCollector? = null,
|
||||
body: (ScriptDiagnosticsMessageCollector) -> ResultWithDiagnostics<T>
|
||||
@@ -99,6 +100,16 @@ internal fun getScriptKtFile(
|
||||
}
|
||||
}
|
||||
|
||||
class SourceCodeImpl(file: KtFile) : SourceCode, Serializable {
|
||||
override val text: String = file.text
|
||||
override val name: String? = file.name
|
||||
override val locationId: String? = file.virtualFilePath
|
||||
|
||||
companion object {
|
||||
private const val serialVersionUID = 1L
|
||||
}
|
||||
}
|
||||
|
||||
internal fun makeCompiledScript(
|
||||
generationState: GenerationState,
|
||||
script: SourceCode,
|
||||
@@ -122,7 +133,7 @@ internal fun makeCompiledScript(
|
||||
sourceDependencies.find { it.scriptFile == containingKtFile }?.sourceDependencies?.valueOrThrow()?.mapNotNull { sourceFile ->
|
||||
sourceFile.declarations.firstIsInstanceOrNull<KtScript>()?.let {
|
||||
KJvmCompiledScript(
|
||||
containingKtFile.virtualFile?.path,
|
||||
containingKtFile.virtualFilePath,
|
||||
getScriptConfiguration(sourceFile),
|
||||
it.fqName.asString(),
|
||||
null,
|
||||
|
||||
+17
-11
@@ -11,29 +11,35 @@ import org.jetbrains.kotlin.descriptors.ScriptDescriptor
|
||||
import org.jetbrains.kotlin.psi.KtFile
|
||||
import org.jetbrains.kotlin.scripting.compiler.plugin.repl.messages.DiagnosticMessageHolder
|
||||
import java.util.concurrent.locks.ReentrantReadWriteLock
|
||||
import kotlin.concurrent.write
|
||||
import kotlin.script.experimental.dependencies.ScriptDependencies
|
||||
|
||||
class ReplCompilerStageHistory(private val state: GenericReplCompilerState) : BasicReplStageHistory<ScriptDescriptor>(state.lock) {
|
||||
|
||||
override fun reset(): Iterable<ILineId> {
|
||||
val removedCompiledLines = super.reset()
|
||||
val removedAnalyzedLines = state.analyzerEngine.reset()
|
||||
lock.write {
|
||||
val removedCompiledLines = super.reset()
|
||||
val removedAnalyzedLines = state.analyzerEngine.reset()
|
||||
|
||||
checkConsistent(removedCompiledLines, removedAnalyzedLines)
|
||||
return removedCompiledLines
|
||||
checkConsistent(removedCompiledLines, removedAnalyzedLines)
|
||||
return removedCompiledLines
|
||||
}
|
||||
}
|
||||
|
||||
override fun resetTo(id: ILineId): Iterable<ILineId> {
|
||||
val removedCompiledLines = super.resetTo(id)
|
||||
val removedAnalyzedLines = state.analyzerEngine.resetToLine(id)
|
||||
lock.write {
|
||||
val removedCompiledLines = super.resetTo(id)
|
||||
val removedAnalyzedLines = state.analyzerEngine.resetToLine(id)
|
||||
|
||||
checkConsistent(removedCompiledLines, removedAnalyzedLines)
|
||||
return removedCompiledLines
|
||||
checkConsistent(removedCompiledLines, removedAnalyzedLines)
|
||||
return removedCompiledLines
|
||||
}
|
||||
}
|
||||
|
||||
private fun checkConsistent(removedCompiledLines: Iterable<ILineId>, removedAnalyzedLines: List<ReplCodeLine>) {
|
||||
|
||||
private fun checkConsistent(removedCompiledLines: Iterable<ILineId>, removedAnalyzedLines: List<SourceCodeByReplLine>) {
|
||||
removedCompiledLines.zip(removedAnalyzedLines).forEach { (removedCompiledLine, removedAnalyzedLine) ->
|
||||
if (removedCompiledLine != LineId(removedAnalyzedLine)) {
|
||||
if (removedCompiledLine.no != removedAnalyzedLine.no) {
|
||||
throw IllegalStateException("History mismatch when resetting lines: ${removedCompiledLine.no} != $removedAnalyzedLine")
|
||||
}
|
||||
}
|
||||
@@ -59,7 +65,7 @@ class GenericReplCompilerState(environment: KotlinCoreEnvironment, override val
|
||||
|
||||
override val currentGeneration: Int get() = (history as BasicReplStageHistory<*>).currentGeneration.get()
|
||||
|
||||
val analyzerEngine = ReplCodeAnalyzer(environment)
|
||||
val analyzerEngine = ReplCodeAnalyzerBase(environment)
|
||||
|
||||
var lastDependencies: ScriptDependencies? = null
|
||||
}
|
||||
|
||||
+5
-5
@@ -59,7 +59,7 @@ open class GenericReplCompiler(
|
||||
if (compilerState.lastLineState == null || compilerState.lastLineState!!.codeLine != codeLine) {
|
||||
val res = checker.check(state, codeLine)
|
||||
when (res) {
|
||||
is ReplCheckResult.Incomplete -> return@compile ReplCompileResult.Incomplete()
|
||||
is ReplCheckResult.Incomplete -> return@compile ReplCompileResult.Incomplete("Code is incomplete")
|
||||
is ReplCheckResult.Error -> return@compile ReplCompileResult.Error(res.message, res.location)
|
||||
is ReplCheckResult.Ok -> {
|
||||
} // continue
|
||||
@@ -81,10 +81,10 @@ open class GenericReplCompiler(
|
||||
val analysisResult = compilerState.analyzerEngine.analyzeReplLine(psiFile, codeLine)
|
||||
AnalyzerWithCompilerReport.reportDiagnostics(analysisResult.diagnostics, errorHolder)
|
||||
val scriptDescriptor = when (analysisResult) {
|
||||
is ReplCodeAnalyzer.ReplLineAnalysisResult.WithErrors -> {
|
||||
is ReplCodeAnalyzerBase.ReplLineAnalysisResult.WithErrors -> {
|
||||
return ReplCompileResult.Error(errorHolder.renderMessage())
|
||||
}
|
||||
is ReplCodeAnalyzer.ReplLineAnalysisResult.Successful -> {
|
||||
is ReplCodeAnalyzerBase.ReplLineAnalysisResult.Successful -> {
|
||||
(analysisResult.scriptDescriptor as? ScriptDescriptor)
|
||||
?: error("Unexpected script descriptor type ${analysisResult.scriptDescriptor::class}")
|
||||
}
|
||||
@@ -108,12 +108,12 @@ open class GenericReplCompiler(
|
||||
setOf(psiFile.script!!.containingKtFile)
|
||||
)
|
||||
|
||||
compilerState.history.push(LineId(codeLine), scriptDescriptor)
|
||||
compilerState.history.push(LineId(codeLine.no, 0, codeLine.hashCode()), scriptDescriptor)
|
||||
|
||||
val classes = generationState.factory.asList().map { CompiledClassData(it.relativePath, it.asByteArray()) }
|
||||
|
||||
return ReplCompileResult.CompiledClasses(
|
||||
LineId(codeLine),
|
||||
LineId(codeLine.no, 0, codeLine.hashCode()),
|
||||
compilerState.history.map { it.id },
|
||||
scriptDescriptor.name.identifier,
|
||||
classes,
|
||||
|
||||
+90
-36
@@ -7,13 +7,12 @@
|
||||
|
||||
package org.jetbrains.kotlin.scripting.compiler.plugin.repl
|
||||
|
||||
import org.jetbrains.kotlin.cli.common.repl.CompiledReplCodeLine
|
||||
import org.jetbrains.kotlin.cli.common.repl.ILineId
|
||||
import org.jetbrains.kotlin.cli.common.repl.ReplCodeLine
|
||||
import org.jetbrains.kotlin.cli.common.repl.ReplHistory
|
||||
import org.jetbrains.kotlin.cli.jvm.compiler.KotlinCoreEnvironment
|
||||
import org.jetbrains.kotlin.cli.jvm.compiler.NoScopeRecordCliBindingTrace
|
||||
import org.jetbrains.kotlin.cli.jvm.compiler.TopDownAnalyzerFacadeForJVM
|
||||
import org.jetbrains.kotlin.container.ComponentProvider
|
||||
import org.jetbrains.kotlin.container.get
|
||||
import org.jetbrains.kotlin.descriptors.ClassDescriptorWithResolutionScopes
|
||||
import org.jetbrains.kotlin.descriptors.impl.ModuleDescriptorImpl
|
||||
@@ -33,23 +32,30 @@ import org.jetbrains.kotlin.resolve.scopes.ImportingScope
|
||||
import org.jetbrains.kotlin.resolve.scopes.utils.parentsWithSelf
|
||||
import org.jetbrains.kotlin.resolve.scopes.utils.replaceImportingScopes
|
||||
import org.jetbrains.kotlin.scripting.definitions.ScriptPriorities
|
||||
import kotlin.script.experimental.api.SourceCode
|
||||
import kotlin.script.experimental.jvm.util.CompiledHistoryItem
|
||||
import kotlin.script.experimental.jvm.util.CompiledHistoryList
|
||||
import kotlin.script.experimental.jvm.util.SnippetsHistory
|
||||
|
||||
class ReplCodeAnalyzer(environment: KotlinCoreEnvironment) {
|
||||
private val topDownAnalysisContext: TopDownAnalysisContext
|
||||
private val topDownAnalyzer: LazyTopDownAnalyzer
|
||||
private val resolveSession: ResolveSession
|
||||
private val scriptDeclarationFactory: ScriptMutableDeclarationProviderFactory
|
||||
private val replState = ResettableAnalyzerState()
|
||||
open class ReplCodeAnalyzerBase(
|
||||
environment: KotlinCoreEnvironment,
|
||||
val trace: BindingTraceContext = NoScopeRecordCliBindingTrace()
|
||||
) {
|
||||
protected val scriptDeclarationFactory: ScriptMutableDeclarationProviderFactory
|
||||
|
||||
protected val container: ComponentProvider
|
||||
protected val topDownAnalysisContext: TopDownAnalysisContext
|
||||
protected val topDownAnalyzer: LazyTopDownAnalyzer
|
||||
protected val resolveSession: ResolveSession
|
||||
protected val replState = ResettableAnalyzerState()
|
||||
|
||||
val module: ModuleDescriptorImpl
|
||||
|
||||
val trace: BindingTraceContext = NoScopeRecordCliBindingTrace()
|
||||
|
||||
init {
|
||||
// Module source scope is empty because all binary classes are in the dependency module, and all source classes are guaranteed
|
||||
// to be found via ResolveSession. The latter is true as long as light classes are not needed in REPL (which is currently true
|
||||
// because no symbol declared in the REPL session can be used from Java)
|
||||
val container = TopDownAnalyzerFacadeForJVM.createContainer(
|
||||
container = TopDownAnalyzerFacadeForJVM.createContainer(
|
||||
environment.project,
|
||||
emptyList(),
|
||||
trace,
|
||||
@@ -71,7 +77,10 @@ class ReplCodeAnalyzer(environment: KotlinCoreEnvironment) {
|
||||
val scriptDescriptor: ClassDescriptorWithResolutionScopes?
|
||||
val diagnostics: Diagnostics
|
||||
|
||||
data class Successful(override val scriptDescriptor: ClassDescriptorWithResolutionScopes, override val diagnostics: Diagnostics) :
|
||||
data class Successful(
|
||||
override val scriptDescriptor: ClassDescriptorWithResolutionScopes,
|
||||
override val diagnostics: Diagnostics
|
||||
) :
|
||||
ReplLineAnalysisResult
|
||||
|
||||
data class WithErrors(override val diagnostics: Diagnostics) :
|
||||
@@ -80,9 +89,9 @@ class ReplCodeAnalyzer(environment: KotlinCoreEnvironment) {
|
||||
}
|
||||
}
|
||||
|
||||
fun resetToLine(lineId: ILineId): List<ReplCodeLine> = replState.resetToLine(lineId)
|
||||
fun resetToLine(lineId: ILineId): List<SourceCodeByReplLine> = replState.resetToLine(lineId)
|
||||
|
||||
fun reset(): List<ReplCodeLine> = replState.reset()
|
||||
fun reset(): List<SourceCodeByReplLine> = replState.reset()
|
||||
|
||||
fun analyzeReplLine(psiFile: KtFile, codeLine: ReplCodeLine): ReplLineAnalysisResult {
|
||||
topDownAnalysisContext.scripts.clear()
|
||||
@@ -90,31 +99,38 @@ class ReplCodeAnalyzer(environment: KotlinCoreEnvironment) {
|
||||
|
||||
psiFile.script!!.putUserData(ScriptPriorities.PRIORITY_KEY, codeLine.no)
|
||||
|
||||
return doAnalyze(psiFile, emptyList(), codeLine)
|
||||
return doAnalyze(psiFile, emptyList(), codeLine.toSourceCode())
|
||||
}
|
||||
|
||||
fun analyzeReplLineWithImportedScripts(psiFile: KtFile, importedScripts: List<KtFile>, codeLine: ReplCodeLine): ReplLineAnalysisResult {
|
||||
fun analyzeReplLineWithImportedScripts(
|
||||
psiFile: KtFile,
|
||||
importedScripts: List<KtFile>,
|
||||
codeLine: SourceCode,
|
||||
priority: Int
|
||||
): ReplLineAnalysisResult {
|
||||
topDownAnalysisContext.scripts.clear()
|
||||
trace.clearDiagnostics()
|
||||
|
||||
psiFile.script!!.putUserData(ScriptPriorities.PRIORITY_KEY, codeLine.no)
|
||||
psiFile.script!!.putUserData(ScriptPriorities.PRIORITY_KEY, priority)
|
||||
|
||||
return doAnalyze(psiFile, importedScripts, codeLine)
|
||||
return doAnalyze(psiFile, importedScripts, codeLine.addNo(priority))
|
||||
}
|
||||
|
||||
private fun doAnalyze(linePsi: KtFile, importedScripts: List<KtFile>, codeLine: ReplCodeLine): ReplLineAnalysisResult {
|
||||
private fun doAnalyze(linePsi: KtFile, importedScripts: List<KtFile>, codeLine: SourceCodeByReplLine): ReplLineAnalysisResult {
|
||||
scriptDeclarationFactory.setDelegateFactory(
|
||||
FileBasedDeclarationProviderFactory(resolveSession.storageManager, listOf(linePsi) + importedScripts)
|
||||
)
|
||||
replState.submitLine(linePsi, codeLine)
|
||||
replState.submitLine(linePsi)
|
||||
|
||||
val context = topDownAnalyzer.analyzeDeclarations(topDownAnalysisContext.topDownAnalysisMode, listOf(linePsi) + importedScripts)
|
||||
|
||||
val diagnostics = trace.bindingContext.diagnostics
|
||||
val hasErrors = diagnostics.any { it.severity == Severity.ERROR }
|
||||
return if (hasErrors) {
|
||||
replState.lineFailure(linePsi, codeLine)
|
||||
ReplLineAnalysisResult.WithErrors(diagnostics)
|
||||
replState.lineFailure(linePsi)
|
||||
ReplLineAnalysisResult.WithErrors(
|
||||
diagnostics
|
||||
)
|
||||
} else {
|
||||
val scriptDescriptor = context.scripts[linePsi.script]!!
|
||||
replState.lineSuccess(linePsi, codeLine, scriptDescriptor)
|
||||
@@ -123,10 +139,9 @@ class ReplCodeAnalyzer(environment: KotlinCoreEnvironment) {
|
||||
diagnostics
|
||||
)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private class ScriptMutableDeclarationProviderFactory : DeclarationProviderFactory {
|
||||
protected class ScriptMutableDeclarationProviderFactory : DeclarationProviderFactory {
|
||||
private lateinit var delegateFactory: DeclarationProviderFactory
|
||||
private lateinit var rootPackageProvider: AdaptablePackageMemberDeclarationProvider
|
||||
|
||||
@@ -175,28 +190,31 @@ class ReplCodeAnalyzer(environment: KotlinCoreEnvironment) {
|
||||
}
|
||||
}
|
||||
|
||||
data class CompiledCode(val className: String, val source: SourceCodeByReplLine)
|
||||
|
||||
// TODO: merge with org.jetbrains.kotlin.resolve.repl.ReplState when switching to new REPL infrastructure everywhere
|
||||
// TODO: review its place in the extracted state infrastructure (now the analyzer itself is a part of the state)
|
||||
class ResettableAnalyzerState {
|
||||
private val successfulLines = ReplHistory<LineInfo.SuccessfulLine>()
|
||||
private val successfulLines = ResettableSnippetsHistory<LineInfo.SuccessfulLine>()
|
||||
private val submittedLines = hashMapOf<KtFile, LineInfo>()
|
||||
|
||||
fun resetToLine(lineId: ILineId): List<ReplCodeLine> {
|
||||
val removed = successfulLines.resetToLine(lineId.no)
|
||||
fun resetToLine(lineId: ILineId): List<SourceCodeByReplLine> {
|
||||
val removed = successfulLines.resetToLine(lineId)
|
||||
removed.forEach { submittedLines.remove(it.second.linePsi) }
|
||||
return removed.map { it.first }
|
||||
}
|
||||
|
||||
fun reset(): List<ReplCodeLine> {
|
||||
fun reset(): List<SourceCodeByReplLine> {
|
||||
submittedLines.clear()
|
||||
return successfulLines.reset().map { it.first }
|
||||
}
|
||||
|
||||
fun submitLine(ktFile: KtFile, codeLine: ReplCodeLine) {
|
||||
val line = LineInfo.SubmittedLine(
|
||||
ktFile,
|
||||
successfulLines.lastValue()
|
||||
)
|
||||
fun submitLine(ktFile: KtFile) {
|
||||
val line =
|
||||
LineInfo.SubmittedLine(
|
||||
ktFile,
|
||||
successfulLines.lastValue()
|
||||
)
|
||||
submittedLines[ktFile] = line
|
||||
ktFile.fileScopesCustomizer = object : FileScopesCustomizer {
|
||||
override fun createFileScopes(fileScopeFactory: FileScopeFactory): FileScopes {
|
||||
@@ -205,7 +223,7 @@ class ReplCodeAnalyzer(environment: KotlinCoreEnvironment) {
|
||||
}
|
||||
}
|
||||
|
||||
fun lineSuccess(ktFile: KtFile, codeLine: ReplCodeLine, scriptDescriptor: ClassDescriptorWithResolutionScopes) {
|
||||
fun lineSuccess(ktFile: KtFile, codeLine: SourceCodeByReplLine, scriptDescriptor: ClassDescriptorWithResolutionScopes) {
|
||||
val successfulLine =
|
||||
LineInfo.SuccessfulLine(
|
||||
ktFile,
|
||||
@@ -213,10 +231,15 @@ class ReplCodeAnalyzer(environment: KotlinCoreEnvironment) {
|
||||
scriptDescriptor
|
||||
)
|
||||
submittedLines[ktFile] = successfulLine
|
||||
successfulLines.add(CompiledReplCodeLine(ktFile.name, codeLine), successfulLine)
|
||||
successfulLines.add(
|
||||
CompiledCode(
|
||||
ktFile.name,
|
||||
codeLine
|
||||
), successfulLine
|
||||
)
|
||||
}
|
||||
|
||||
fun lineFailure(ktFile: KtFile, codeLine: ReplCodeLine) {
|
||||
fun lineFailure(ktFile: KtFile) {
|
||||
submittedLines[ktFile] =
|
||||
LineInfo.FailedLine(
|
||||
ktFile,
|
||||
@@ -251,3 +274,34 @@ class ReplCodeAnalyzer(environment: KotlinCoreEnvironment) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun ReplCodeLine.toSourceCode() = SourceCodeByReplLine(code, no)
|
||||
internal fun SourceCode.addNo(no: Int) = SourceCodeByReplLine(text, no, name, locationId)
|
||||
|
||||
data class SourceCodeByReplLine(
|
||||
override val text: String,
|
||||
val no: Int,
|
||||
override val name: String? = null,
|
||||
override val locationId: String? = null
|
||||
) : SourceCode
|
||||
|
||||
private typealias ReplSourceHistoryList<ResultT> = List<CompiledHistoryItem<SourceCodeByReplLine, ResultT>>
|
||||
|
||||
@Deprecated("This functionality is left for backwards compatibility only", ReplaceWith("SnippetsHistory"))
|
||||
private class ResettableSnippetsHistory<ResultT>(startingHistory: CompiledHistoryList<ReplCodeAnalyzerBase.CompiledCode, ResultT> = emptyList()) :
|
||||
SnippetsHistory<ReplCodeAnalyzerBase.CompiledCode, ResultT>(startingHistory) {
|
||||
|
||||
fun resetToLine(line: ILineId): ReplSourceHistoryList<ResultT> {
|
||||
val removed = arrayListOf<Pair<SourceCodeByReplLine, ResultT>>()
|
||||
while ((history.lastOrNull()?.first?.source?.no ?: -1) > line.no) {
|
||||
removed.add(history.removeAt(history.size - 1).let { Pair(it.first.source, it.second) })
|
||||
}
|
||||
return removed.reversed()
|
||||
}
|
||||
|
||||
fun reset(): ReplSourceHistoryList<ResultT> {
|
||||
val removed = history.map { Pair(it.first.source, it.second) }
|
||||
history.clear()
|
||||
return removed
|
||||
}
|
||||
}
|
||||
|
||||
+1
-1
@@ -76,7 +76,7 @@ class ReplInterpreter(
|
||||
}
|
||||
|
||||
// TODO: add script definition with project-based resolving for IDEA repl
|
||||
private val scriptCompiler: ReplCompiler by lazy {
|
||||
private val scriptCompiler: ReplCompilerWithoutCheck by lazy {
|
||||
GenericReplCompiler(
|
||||
disposable,
|
||||
REPL_LINE_AS_SCRIPT_DEFINITION,
|
||||
|
||||
+9
-53
@@ -6,7 +6,6 @@
|
||||
package org.jetbrains.kotlin.scripting.compiler.plugin.repl
|
||||
|
||||
import com.intellij.openapi.Disposable
|
||||
import com.intellij.openapi.project.Project
|
||||
import com.intellij.openapi.util.Disposer
|
||||
import org.jetbrains.kotlin.cli.common.repl.*
|
||||
import org.jetbrains.kotlin.cli.jvm.compiler.KotlinCoreEnvironment
|
||||
@@ -15,54 +14,11 @@ import java.util.concurrent.locks.ReentrantReadWriteLock
|
||||
import kotlin.concurrent.write
|
||||
import kotlin.script.experimental.api.*
|
||||
|
||||
interface KJvmReplCompilerProxy {
|
||||
fun createReplCompilationState(scriptCompilationConfiguration: ScriptCompilationConfiguration): JvmReplCompilerState.Compilation
|
||||
class JvmReplCompilerStageHistory<CompilationT : JvmReplCompilerState.Compilation>(private val state: JvmReplCompilerState<CompilationT>) :
|
||||
BasicReplStageHistory<ScriptDescriptor>(state.lock)
|
||||
|
||||
fun checkSyntax(
|
||||
script: SourceCode,
|
||||
scriptCompilationConfiguration: ScriptCompilationConfiguration,
|
||||
project: Project
|
||||
): ResultWithDiagnostics<Boolean>
|
||||
|
||||
|
||||
fun compileReplSnippet(
|
||||
compilationState: JvmReplCompilerState.Compilation,
|
||||
snippet: SourceCode,
|
||||
snippetId: ReplSnippetId,
|
||||
history: IReplStageHistory<ScriptDescriptor>
|
||||
): ResultWithDiagnostics<CompiledScript>
|
||||
}
|
||||
|
||||
class JvmReplCompilerStageHistory(private val state: JvmReplCompilerState) :
|
||||
BasicReplStageHistory<ScriptDescriptor>(state.lock) {
|
||||
|
||||
override fun reset(): Iterable<ILineId> {
|
||||
val removedCompiledLines = super.reset()
|
||||
val removedAnalyzedLines = state.compilation.analyzerEngine.reset()
|
||||
|
||||
checkConsistent(removedCompiledLines, removedAnalyzedLines)
|
||||
return removedCompiledLines
|
||||
}
|
||||
|
||||
override fun resetTo(id: ILineId): Iterable<ILineId> {
|
||||
val removedCompiledLines = super.resetTo(id)
|
||||
val removedAnalyzedLines = state.compilation.analyzerEngine.resetToLine(id)
|
||||
|
||||
checkConsistent(removedCompiledLines, removedAnalyzedLines)
|
||||
return removedCompiledLines
|
||||
}
|
||||
|
||||
private fun checkConsistent(removedCompiledLines: Iterable<ILineId>, removedAnalyzedLines: List<ReplCodeLine>) {
|
||||
removedCompiledLines.zip(removedAnalyzedLines).forEach { (removedCompiledLine, removedAnalyzedLine) ->
|
||||
if (removedCompiledLine != LineId(removedAnalyzedLine)) {
|
||||
throw IllegalStateException("History mismatch when resetting lines: ${removedCompiledLine.no} != $removedAnalyzedLine")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class JvmReplCompilerState(
|
||||
val replCompilerProxy: KJvmReplCompilerProxy,
|
||||
class JvmReplCompilerState<CompilationT : JvmReplCompilerState.Compilation>(
|
||||
val createCompilation: (ScriptCompilationConfiguration) -> CompilationT,
|
||||
override val lock: ReentrantReadWriteLock = ReentrantReadWriteLock()
|
||||
) : IReplStageState<ScriptDescriptor> {
|
||||
|
||||
@@ -80,29 +36,29 @@ class JvmReplCompilerState(
|
||||
}
|
||||
}
|
||||
|
||||
fun getCompilationState(scriptCompilationConfiguration: ScriptCompilationConfiguration): Compilation = lock.write {
|
||||
fun getCompilationState(scriptCompilationConfiguration: ScriptCompilationConfiguration): CompilationT = lock.write {
|
||||
if (_compilation == null) {
|
||||
initializeCompilation(scriptCompilationConfiguration)
|
||||
}
|
||||
_compilation!!
|
||||
}
|
||||
|
||||
internal val compilation: Compilation
|
||||
internal val compilation: CompilationT
|
||||
get() = _compilation ?: throw IllegalStateException("Compilation state is either not initializad or already destroyed")
|
||||
|
||||
private var _compilation: Compilation? = null
|
||||
private var _compilation: CompilationT? = null
|
||||
|
||||
val isCompilationInitialized get() = _compilation != null
|
||||
|
||||
private fun initializeCompilation(scriptCompilationConfiguration: ScriptCompilationConfiguration) {
|
||||
if (_compilation != null) throw IllegalStateException("Compilation state is already initialized")
|
||||
_compilation = replCompilerProxy.createReplCompilationState(scriptCompilationConfiguration)
|
||||
_compilation = createCompilation(scriptCompilationConfiguration)
|
||||
}
|
||||
|
||||
interface Compilation {
|
||||
val disposable: Disposable?
|
||||
val baseScriptCompilationConfiguration: ScriptCompilationConfiguration
|
||||
val environment: KotlinCoreEnvironment
|
||||
val analyzerEngine: ReplCodeAnalyzer
|
||||
val analyzerEngine: ReplCodeAnalyzerBase
|
||||
}
|
||||
}
|
||||
|
||||
+3
-3
@@ -25,7 +25,7 @@ import org.jetbrains.kotlin.ir.util.SymbolTable
|
||||
import org.jetbrains.kotlin.ir.util.generateTypicalIrProviderList
|
||||
import org.jetbrains.kotlin.js.config.JSConfigurationKeys
|
||||
import org.jetbrains.kotlin.psi2ir.Psi2IrTranslator
|
||||
import org.jetbrains.kotlin.scripting.compiler.plugin.repl.ReplCodeAnalyzer
|
||||
import org.jetbrains.kotlin.scripting.compiler.plugin.repl.ReplCodeAnalyzerBase
|
||||
import org.jetbrains.kotlin.serialization.js.ModuleKind
|
||||
import kotlin.script.experimental.api.valueOr
|
||||
import kotlin.script.experimental.host.StringScriptSource
|
||||
@@ -35,7 +35,7 @@ class JsCoreScriptingCompiler(
|
||||
private val nameTables: NameTables,
|
||||
private val symbolTable: SymbolTable,
|
||||
private val dependencyDescriptors: List<ModuleDescriptor>,
|
||||
private val replState: ReplCodeAnalyzer.ResettableAnalyzerState = ReplCodeAnalyzer.ResettableAnalyzerState()
|
||||
private val replState: ReplCodeAnalyzerBase.ResettableAnalyzerState = ReplCodeAnalyzerBase.ResettableAnalyzerState()
|
||||
) {
|
||||
fun compile(codeLine: ReplCodeLine): ReplCompileResult {
|
||||
val snippet = codeLine.code
|
||||
@@ -91,6 +91,6 @@ class JsCoreScriptingCompiler(
|
||||
|
||||
val code = generateJsCode(context, irModuleFragment, nameTables)
|
||||
|
||||
return createCompileResult(LineId(codeLine), code)
|
||||
return createCompileResult(LineId(codeLine.no, 0, codeLine.hashCode()), code)
|
||||
}
|
||||
}
|
||||
|
||||
+6
-5
@@ -11,26 +11,27 @@ import org.jetbrains.kotlin.cli.jvm.compiler.KotlinCoreEnvironment
|
||||
import org.jetbrains.kotlin.descriptors.ModuleDescriptor
|
||||
import org.jetbrains.kotlin.psi.KtFile
|
||||
import org.jetbrains.kotlin.scripting.compiler.plugin.impl.AbstractJsScriptlikeCodeAnalyser
|
||||
import org.jetbrains.kotlin.scripting.compiler.plugin.repl.ReplCodeAnalyzer
|
||||
import org.jetbrains.kotlin.scripting.compiler.plugin.repl.ReplCodeAnalyzerBase
|
||||
import org.jetbrains.kotlin.scripting.compiler.plugin.repl.toSourceCode
|
||||
import org.jetbrains.kotlin.scripting.definitions.ScriptPriorities
|
||||
|
||||
class JsReplCodeAnalyzer(
|
||||
environment: KotlinCoreEnvironment,
|
||||
dependencies: List<ModuleDescriptor>,
|
||||
private val replState: ReplCodeAnalyzer.ResettableAnalyzerState
|
||||
private val replState: ReplCodeAnalyzerBase.ResettableAnalyzerState
|
||||
) : AbstractJsScriptlikeCodeAnalyser(environment, dependencies) {
|
||||
|
||||
fun analyzeReplLine(linePsi: KtFile, codeLine: ReplCodeLine): AnalysisResult {
|
||||
linePsi.script!!.putUserData(ScriptPriorities.PRIORITY_KEY, codeLine.no)
|
||||
replState.submitLine(linePsi, codeLine)
|
||||
replState.submitLine(linePsi)
|
||||
|
||||
val result = analysisImpl(linePsi)
|
||||
|
||||
return if (result.isSuccess) {
|
||||
replState.lineSuccess(linePsi, codeLine, result.script)
|
||||
replState.lineSuccess(linePsi, codeLine.toSourceCode(), result.script)
|
||||
AnalysisResult.success(result.bindingContext, result.moduleDescriptor)
|
||||
} else {
|
||||
replState.lineFailure(linePsi, codeLine)
|
||||
replState.lineFailure(linePsi)
|
||||
AnalysisResult.compilationError(result.bindingContext)
|
||||
}
|
||||
}
|
||||
|
||||
+2
-2
@@ -11,7 +11,7 @@ import org.jetbrains.kotlin.cli.jvm.compiler.KotlinCoreEnvironment
|
||||
import org.jetbrains.kotlin.ir.backend.js.lower.serialization.ir.JsManglerDesc
|
||||
import org.jetbrains.kotlin.ir.backend.js.utils.NameTables
|
||||
import org.jetbrains.kotlin.ir.util.SymbolTable
|
||||
import org.jetbrains.kotlin.scripting.compiler.plugin.repl.ReplCodeAnalyzer
|
||||
import org.jetbrains.kotlin.scripting.compiler.plugin.repl.ReplCodeAnalyzerBase
|
||||
import java.util.concurrent.locks.ReentrantReadWriteLock
|
||||
|
||||
// Used to compile REPL code lines
|
||||
@@ -22,7 +22,7 @@ class JsReplCompiler(private val environment: KotlinCoreEnvironment) : ReplCompi
|
||||
lock,
|
||||
NameTables(emptyList()),
|
||||
readLibrariesFromConfiguration(environment.configuration),
|
||||
ReplCodeAnalyzer.ResettableAnalyzerState(),
|
||||
ReplCodeAnalyzerBase.ResettableAnalyzerState(),
|
||||
SymbolTable(IdSignatureDescriptor(JsManglerDesc))
|
||||
)
|
||||
}
|
||||
|
||||
+3
-3
@@ -27,7 +27,7 @@ import org.jetbrains.kotlin.ir.util.SymbolTable
|
||||
import org.jetbrains.kotlin.library.resolver.TopologicalLibraryOrder
|
||||
import org.jetbrains.kotlin.psi.KtFile
|
||||
import org.jetbrains.kotlin.psi.KtScript
|
||||
import org.jetbrains.kotlin.scripting.compiler.plugin.repl.ReplCodeAnalyzer
|
||||
import org.jetbrains.kotlin.scripting.compiler.plugin.repl.ReplCodeAnalyzerBase
|
||||
import org.jetbrains.kotlin.scripting.configuration.ScriptingConfigurationKeys
|
||||
import org.jetbrains.kotlin.scripting.resolve.ScriptLightVirtualFile
|
||||
import org.jetbrains.kotlin.util.Logger
|
||||
@@ -132,7 +132,7 @@ fun readLibrariesFromConfiguration(configuration: CompilerConfiguration): List<M
|
||||
.map { descriptorMap.getOrPut(it.libraryName) { getModuleDescriptorByLibrary(it, descriptorMap) } }
|
||||
}
|
||||
|
||||
fun createCompileResult(code: String) = createCompileResult(LineId(ReplCodeLine(0, 0, "")), code)
|
||||
fun createCompileResult(code: String) = createCompileResult(LineId(0, 0, 0), code)
|
||||
|
||||
fun createCompileResult(lineId: LineId, code: String): ReplCompileResult.CompiledClasses {
|
||||
return ReplCompileResult.CompiledClasses(
|
||||
@@ -219,7 +219,7 @@ class JsReplCompilationState(
|
||||
lock: ReentrantReadWriteLock,
|
||||
nameTables: NameTables,
|
||||
dependencies: List<ModuleDescriptor>,
|
||||
val replState: ReplCodeAnalyzer.ResettableAnalyzerState,
|
||||
val replState: ReplCodeAnalyzerBase.ResettableAnalyzerState,
|
||||
val symbolTable: SymbolTable
|
||||
) : JsCompilationState(lock, nameTables, dependencies)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user