Implement support for new script customizations in command line compiler

refactor and fix internals in many places to make it work
add appropriate tests for main-kts case
This commit is contained in:
Ilya Chernikov
2019-07-01 13:41:08 +02:00
parent 1ab255eafb
commit dc4370ff08
23 changed files with 477 additions and 272 deletions
@@ -35,21 +35,23 @@ open class ScriptCompilationConfiguration(baseConfigurations: Iterable<ScriptCom
object Default : ScriptCompilationConfiguration()
/**
* An alternative to the constructor with base configuration, which returns a new configuration only if [body] adds anything
* to the original one, otherwise returns original
*/
fun withUpdates(body: Builder.() -> Unit = {}): ScriptCompilationConfiguration {
val newConfiguration = ScriptCompilationConfiguration(this, body = body)
return if (newConfiguration == this) this
else newConfiguration
}
override fun toString(): String {
return "ScriptCompilationConfiguration($providedProperties)"
}
}
/**
* An alternative to the constructor with base configuration, which returns a new configuration only if [body] adds anything
* to the original one, otherwise returns original
*/
fun ScriptCompilationConfiguration?.with(body: ScriptCompilationConfiguration.Builder.() -> Unit): ScriptCompilationConfiguration {
val newConfiguration =
if (this == null) ScriptCompilationConfiguration(body = body)
else ScriptCompilationConfiguration(this, body = body)
return if (newConfiguration == this) this else newConfiguration
}
/**
* The script type display name
*/
@@ -35,6 +35,18 @@ open class ScriptEvaluationConfiguration(baseEvaluationConfigurations: Iterable<
object Default : ScriptEvaluationConfiguration()
}
/**
* An alternative to the constructor with base configuration, which returns a new configuration only if [body] adds anything
* to the original one, otherwise returns original
*/
fun ScriptEvaluationConfiguration?.with(body: ScriptEvaluationConfiguration.Builder.() -> Unit): ScriptEvaluationConfiguration {
val newConfiguration =
if (this == null) ScriptEvaluationConfiguration(body = body)
else ScriptEvaluationConfiguration(this, body = body)
return if (newConfiguration != this) newConfiguration else this
}
/**
* The list of actual script implicit receiver object, in the same order as specified in {@link ScriptCompilationConfigurationKeys#implicitReceivers}
*/
@@ -18,10 +18,10 @@ dependencies {
allTestsRuntime(intellijDep()) { includeJars("openapi", "idea", "idea_rt", "log4j") }
testCompile(project(":kotlin-scripting-jvm-host"))
testCompile(projectTests(":compiler:tests-common"))
testCompile(project(":kotlin-scripting-compiler"))
testCompile(project(":daemon-common")) // TODO: fix import (workaround for jps build)
testRuntimeOnly(project(":kotlin-compiler"))
testRuntimeOnly(project(":kotlin-scripting-compiler"))
testRuntimeOnly(project(":kotlin-reflect"))
testRuntimeOnly(commonDep("org.jetbrains.intellij.deps", "trove4j"))
@@ -9,6 +9,7 @@ import junit.framework.TestCase
import kotlinx.coroutines.runBlocking
import org.jetbrains.kotlin.cli.common.repl.BasicReplStageHistory
import org.jetbrains.kotlin.descriptors.ScriptDescriptor
import org.jetbrains.kotlin.scripting.compiler.plugin.impl.KJvmReplCompilerImpl
import org.junit.Assert
import org.junit.Test
import kotlin.script.experimental.annotations.KotlinScript
@@ -16,7 +17,6 @@ import kotlin.script.experimental.api.*
import kotlin.script.experimental.host.toScriptSource
import kotlin.script.experimental.jvm.*
import kotlin.script.experimental.jvmhost.createJvmCompilationConfigurationFromTemplate
import kotlin.script.experimental.jvmhost.impl.KJvmReplCompilerImpl
class ReplTest : TestCase() {
@@ -46,7 +46,8 @@ class ReplTest : TestCase() {
evaluationConfiguration: ScriptEvaluationConfiguration,
snippets: Sequence<String>
): Sequence<ResultWithDiagnostics<EvaluationResult>> {
val replCompilerProxy = KJvmReplCompilerImpl(defaultJvmScriptingHostConfiguration)
val replCompilerProxy =
KJvmReplCompilerImpl(defaultJvmScriptingHostConfiguration)
val compilationState = replCompilerProxy.createReplCompilationState(compilationConfiguration)
val compilationHistory = BasicReplStageHistory<ScriptDescriptor>()
val replEvaluator = BasicJvmScriptEvaluator()
@@ -7,6 +7,8 @@ package kotlin.script.experimental.jvmhost.test
import junit.framework.TestCase
import kotlinx.coroutines.runBlocking
import org.jetbrains.kotlin.scripting.compiler.plugin.impl.CompiledScriptClassLoader
import org.jetbrains.kotlin.scripting.compiler.plugin.impl.KJvmCompiledModuleInMemory
import org.jetbrains.org.objectweb.asm.ClassReader
import org.jetbrains.org.objectweb.asm.ClassVisitor
import org.jetbrains.org.objectweb.asm.Opcodes
@@ -27,8 +29,6 @@ import kotlin.script.experimental.jvm.BasicJvmScriptEvaluator
import kotlin.script.experimental.jvm.defaultJvmScriptingHostConfiguration
import kotlin.script.experimental.jvm.impl.KJvmCompiledScript
import kotlin.script.experimental.jvmhost.*
import kotlin.script.experimental.jvmhost.impl.CompiledScriptClassLoader
import kotlin.script.experimental.jvmhost.impl.KJvmCompiledModuleInMemory
import kotlin.script.templates.standard.SimpleScriptTemplate
class ScriptingHostTest : TestCase() {
@@ -1,91 +0,0 @@
/*
* Copyright 2010-2018 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 kotlin.script.experimental.jvmhost.impl
import org.jetbrains.kotlin.analyzer.AnalysisResult
import org.jetbrains.kotlin.cli.common.CLIConfigurationKeys
import org.jetbrains.kotlin.cli.common.messages.AnalyzerWithCompilerReport
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.codegen.ClassBuilderFactories
import org.jetbrains.kotlin.codegen.CompilationErrorHandler
import org.jetbrains.kotlin.codegen.KotlinCodegenFacade
import org.jetbrains.kotlin.codegen.state.GenerationState
import org.jetbrains.kotlin.config.CompilerConfiguration
import org.jetbrains.kotlin.config.languageVersionSettings
import org.jetbrains.kotlin.psi.KtFile
import org.jetbrains.kotlin.scripting.definitions.ScriptDependenciesProvider
import kotlin.script.experimental.api.*
import kotlin.script.experimental.host.ScriptingHostConfiguration
import kotlin.script.experimental.jvmhost.KJvmCompilerProxy
class KJvmCompilerImpl(val hostConfiguration: ScriptingHostConfiguration) : KJvmCompilerProxy {
override fun compile(
script: SourceCode,
scriptCompilationConfiguration: ScriptCompilationConfiguration
): ResultWithDiagnostics<CompiledScript<*>> =
withMessageCollectorAndDisposable(locationId = script.locationId) { messageCollector, disposable ->
val context = createSharedCompilationContext(scriptCompilationConfiguration, hostConfiguration, messageCollector, disposable)
val mainKtFile =
getScriptKtFile(script, context.baseScriptCompilationConfiguration, context.environment.project, messageCollector)
.valueOr { return it }
val (sourceFiles, sourceDependencies) = collectRefinedSourcesAndUpdateEnvironment(context, mainKtFile, messageCollector)
val dependenciesProvider = ScriptDependenciesProvider.getInstance(context.environment.project)
val analysisResult = analyze(sourceFiles, context.environment)
if (!analysisResult.shouldGenerateCode) return failure(script, messageCollector, "no code to generate")
if (analysisResult.isError() || messageCollector.hasErrors()) return failure(messageCollector)
val generationState = generate(analysisResult, sourceFiles, context.environment.configuration)
val compiledScript =
makeCompiledScript(generationState, script, sourceFiles.first(), sourceDependencies) { ktFile ->
dependenciesProvider?.getScriptConfigurationResult(ktFile)?.valueOrNull()?.configuration
?: context.baseScriptCompilationConfiguration
}
ResultWithDiagnostics.Success(compiledScript, messageCollector.diagnostics)
}
}
private fun analyze(sourceFiles: Collection<KtFile>, environment: KotlinCoreEnvironment): AnalysisResult {
val messageCollector = environment.configuration[CLIConfigurationKeys.MESSAGE_COLLECTOR_KEY]!!
val analyzerWithCompilerReport = AnalyzerWithCompilerReport(messageCollector, environment.configuration.languageVersionSettings)
analyzerWithCompilerReport.analyzeAndReport(sourceFiles) {
val project = environment.project
TopDownAnalyzerFacadeForJVM.analyzeFilesWithJavaIntegration(
project,
sourceFiles,
NoScopeRecordCliBindingTrace(),
environment.configuration,
environment::createPackagePartProvider
)
}
return analyzerWithCompilerReport.analysisResult
}
private fun generate(
analysisResult: AnalysisResult, sourceFiles: List<KtFile>, kotlinCompilerConfiguration: CompilerConfiguration
): GenerationState {
val generationState = GenerationState.Builder(
sourceFiles.first().project,
ClassBuilderFactories.BINARIES,
analysisResult.moduleDescriptor,
analysisResult.bindingContext,
sourceFiles,
kotlinCompilerConfiguration
).build()
KotlinCodegenFacade.compileCorrectFiles(generationState, CompilationErrorHandler.THROW_EXCEPTION)
return generationState
}
@@ -7,10 +7,11 @@
package kotlin.script.experimental.jvmhost
import org.jetbrains.kotlin.scripting.compiler.plugin.ScriptJvmCompilerProxy
import org.jetbrains.kotlin.scripting.compiler.plugin.impl.ScriptJvmCompilerIsolated
import kotlin.script.experimental.api.*
import kotlin.script.experimental.host.ScriptingHostConfiguration
import kotlin.script.experimental.jvm.defaultJvmScriptingHostConfiguration
import kotlin.script.experimental.jvmhost.impl.KJvmCompilerImpl
import kotlin.script.experimental.jvmhost.impl.withDefaults
interface CompiledJvmScriptsCache {
@@ -31,7 +32,7 @@ interface CompiledJvmScriptsCache {
open class JvmScriptCompiler(
hostConfiguration: ScriptingHostConfiguration = defaultJvmScriptingHostConfiguration,
val compilerProxy: KJvmCompilerProxy = KJvmCompilerImpl(hostConfiguration.withDefaults()),
val compilerProxy: ScriptJvmCompilerProxy = ScriptJvmCompilerIsolated(hostConfiguration.withDefaults()),
val cache: CompiledJvmScriptsCache = CompiledJvmScriptsCache.NoCache
) : ScriptCompiler {
@@ -59,10 +60,3 @@ open class JvmScriptCompiler(
}
}
interface KJvmCompilerProxy {
fun compile(
script: SourceCode,
scriptCompilationConfiguration: ScriptCompilationConfiguration
): ResultWithDiagnostics<CompiledScript<*>>
}
@@ -5,16 +5,20 @@
package kotlin.script.experimental.jvmhost
import org.jetbrains.kotlin.scripting.compiler.plugin.impl.KJvmCompiledModuleInMemory
import org.jetbrains.kotlin.utils.KotlinPaths
import java.io.*
import java.io.File
import java.io.FileOutputStream
import java.util.jar.JarEntry
import java.util.jar.JarOutputStream
import java.util.jar.Manifest
import kotlin.script.experimental.api.*
import kotlin.script.experimental.jvm.JvmDependency
import kotlin.script.experimental.jvm.impl.*
import kotlin.script.experimental.jvm.impl.KJvmCompiledScript
import kotlin.script.experimental.jvm.impl.copyWithoutModule
import kotlin.script.experimental.jvm.impl.scriptMetadataPath
import kotlin.script.experimental.jvm.impl.toBytes
import kotlin.script.experimental.jvm.util.scriptCompilationClasspathFromContext
import kotlin.script.experimental.jvmhost.impl.KJvmCompiledModuleInMemory
// TODO: generate execution code (main)
@@ -1,29 +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 kotlin.script.experimental.jvmhost.repl
import com.intellij.openapi.project.Project
import org.jetbrains.kotlin.cli.common.repl.IReplStageHistory
import org.jetbrains.kotlin.descriptors.ScriptDescriptor
import kotlin.script.experimental.api.*
interface KJvmReplCompilerProxy {
fun createReplCompilationState(scriptCompilationConfiguration: ScriptCompilationConfiguration): JvmReplCompilerState.Compilation
fun checkSyntax(
script: SourceCode,
scriptCompilationConfiguration: ScriptCompilationConfiguration,
project: Project
): ResultWithDiagnostics<Boolean>
fun compileReplSnippet(
compilationState: JvmReplCompilerState.Compilation,
snippet: SourceCode,
snippetId: ReplSnippetId,
history: IReplStageHistory<ScriptDescriptor>
): ResultWithDiagnostics<CompiledScript<*>>
}
@@ -5,18 +5,15 @@
package kotlin.script.experimental.jvmhost.repl
import com.intellij.openapi.Disposable
import com.intellij.openapi.util.Disposer
import org.jetbrains.kotlin.cli.common.repl.*
import org.jetbrains.kotlin.cli.jvm.compiler.KotlinCoreEnvironment
import org.jetbrains.kotlin.descriptors.ScriptDescriptor
import org.jetbrains.kotlin.scripting.repl.ReplCodeAnalyzer
import org.jetbrains.kotlin.scripting.compiler.plugin.impl.KJvmReplCompilerImpl
import org.jetbrains.kotlin.scripting.compiler.plugin.repl.JvmReplCompilerState
import org.jetbrains.kotlin.scripting.compiler.plugin.repl.KJvmReplCompilerProxy
import java.util.concurrent.locks.ReentrantReadWriteLock
import kotlin.concurrent.write
import kotlin.script.experimental.api.*
import kotlin.script.experimental.host.ScriptingHostConfiguration
import kotlin.script.experimental.jvm.defaultJvmScriptingHostConfiguration
import kotlin.script.experimental.jvmhost.impl.KJvmReplCompilerImpl
import kotlin.script.experimental.jvmhost.impl.withDefaults
/**
@@ -25,7 +22,9 @@ import kotlin.script.experimental.jvmhost.impl.withDefaults
class JvmReplCompiler(
val scriptCompilationConfiguration: ScriptCompilationConfiguration,
val hostConfiguration: ScriptingHostConfiguration = defaultJvmScriptingHostConfiguration,
val replCompilerProxy: KJvmReplCompilerProxy = KJvmReplCompilerImpl(hostConfiguration.withDefaults())
val replCompilerProxy: KJvmReplCompilerProxy = KJvmReplCompilerImpl(
hostConfiguration.withDefaults()
)
) : ReplCompiler {
override fun createState(lock: ReentrantReadWriteLock): IReplStageState<*> = JvmReplCompilerState(replCompilerProxy, lock)
@@ -69,79 +68,6 @@ class JvmReplCompiler(
}
}
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,
override val lock: ReentrantReadWriteLock = ReentrantReadWriteLock()
) : IReplStageState<ScriptDescriptor> {
override val history = JvmReplCompilerStageHistory(this)
override val currentGeneration: Int get() = (history as BasicReplStageHistory<*>).currentGeneration.get()
override fun dispose() {
lock.write {
_compilation?.disposable?.let {
Disposer.dispose(it)
}
_compilation = null
super.dispose()
}
}
fun getCompilationState(scriptCompilationConfiguration: ScriptCompilationConfiguration): Compilation = lock.write {
if (_compilation == null) {
initializeCompilation(scriptCompilationConfiguration)
}
_compilation!!
}
internal val compilation: Compilation
get() = _compilation ?: throw IllegalStateException("Compilation state is either not initializad or already destroyed")
private var _compilation: Compilation? = 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)
}
interface Compilation {
val disposable: Disposable
val baseScriptCompilationConfiguration: ScriptCompilationConfiguration
val environment: KotlinCoreEnvironment
val analyzerEngine: ReplCodeAnalyzer
}
}
internal class SourceCodeFromReplCodeLine(
val codeLine: ReplCodeLine,
@@ -7,8 +7,10 @@ plugins {
dependencies {
testCompile(project(":kotlin-main-kts"))
testCompileOnly(project(":compiler:cli"))
testCompile(project(":kotlin-scripting-jvm-host-embeddable"))
testCompile(commonDep("junit"))
testCompile(projectTests(":kotlin-scripting-compiler")) { isTransitive = false }
}
sourceSets {
@@ -6,6 +6,7 @@
package org.jetbrains.kotlin.mainKts.test
import junit.framework.Assert.*
import org.jetbrains.kotlin.scripting.compiler.plugin.runWithK2JVMCompiler
import org.junit.Test
import java.io.File
import java.io.InputStream
@@ -71,9 +72,7 @@ class mainKtsIT {
assertFalse("stderr thread not finished", stderrThread.isAlive)
assertNull(stderrException.value)
assertEquals(expectedOutPatterns.size, processOut.size)
for (i in 0 until expectedOutPatterns.size) {
val expectedPattern = expectedOutPatterns[i]
val actualLine = processOut[i]
for ((expectedPattern, actualLine) in expectedOutPatterns.zip(processOut)) {
assertTrue(
"line \"$actualLine\" do not match with expected pattern \"$expectedPattern\"",
Regex(expectedPattern).matches(actualLine)
@@ -92,4 +91,9 @@ class mainKtsIT {
fun testResolveJunit() {
runWithKotlinc("$TEST_DATA_ROOT/hello-resolve-junit.main.kts", listOf("Hello, World!"))
}
@Test
fun testImport() {
runWithK2JVMCompiler("$TEST_DATA_ROOT/import-test.main.kts", listOf("Hi from common", "Hi from middle", "sharedVar == 5"))
}
}
@@ -23,7 +23,11 @@ import kotlin.script.experimental.jvm.jvm
import kotlin.script.experimental.jvm.updateClasspath
@Suppress("unused")
@KotlinScript(fileExtension = "main.kts", compilationConfiguration = MainKtsScriptDefinition::class)
@KotlinScript(
fileExtension = "main.kts",
compilationConfiguration = MainKtsScriptDefinition::class,
evaluationConfiguration = MainKtsEvaluationConfiguration::class
)
abstract class MainKtsScript(val args: Array<String>)
object MainKtsScriptDefinition : ScriptCompilationConfiguration(
@@ -40,6 +44,12 @@ object MainKtsScriptDefinition : ScriptCompilationConfiguration(
}
})
object MainKtsEvaluationConfiguration : ScriptEvaluationConfiguration(
{
scriptsInstancesSharing(true)
}
)
class MainKtsConfigurator : RefineScriptCompilationConfigurationHandler {
private val resolver = FilesAndIvyResolver()
@@ -6,6 +6,7 @@
package org.jetbrains.kotlin.scripting.compiler.plugin
import com.intellij.core.JavaCoreProjectEnvironment
import kotlinx.coroutines.runBlocking
import org.jetbrains.kotlin.cli.common.CLIConfigurationKeys
import org.jetbrains.kotlin.cli.common.ExitCode
import org.jetbrains.kotlin.cli.common.ExitCode.COMPILATION_ERROR
@@ -16,12 +17,17 @@ import org.jetbrains.kotlin.cli.common.extensions.ScriptEvaluationExtension
import org.jetbrains.kotlin.cli.common.messages.CompilerMessageSeverity.ERROR
import org.jetbrains.kotlin.cli.jvm.compiler.EnvironmentConfigFiles
import org.jetbrains.kotlin.cli.jvm.compiler.KotlinCoreEnvironment
import org.jetbrains.kotlin.cli.jvm.compiler.KotlinToJVMBytecodeCompiler
import org.jetbrains.kotlin.config.CompilerConfiguration
import org.jetbrains.kotlin.config.JVMConfigurationKeys
import org.jetbrains.kotlin.scripting.compiler.plugin.impl.ScriptJvmCompilerFromEnvironment
import org.jetbrains.kotlin.scripting.configuration.ScriptingConfigurationKeys
import org.jetbrains.kotlin.scripting.definitions.ScriptDefinitionProvider
import java.io.File
import kotlin.script.experimental.api.constructorArgs
import kotlin.script.experimental.api.valueOr
import kotlin.script.experimental.api.with
import kotlin.script.experimental.host.toScriptSource
import kotlin.script.experimental.jvm.BasicJvmScriptEvaluator
class JvmCliScriptEvaluationExtension : ScriptEvaluationExtension {
override fun isAccepted(arguments: CommonCompilerArguments): Boolean =
@@ -42,7 +48,7 @@ class JvmCliScriptEvaluationExtension : ScriptEvaluationExtension {
configuration.addKotlinSourceRoot(sourcePath)
configuration.put(JVMConfigurationKeys.RETAIN_OUTPUT_IN_MEMORY, true)
val coreEnvironment =
val environment =
KotlinCoreEnvironment.createForProduction(projectEnvironment, configuration, EnvironmentConfigFiles.JVM_CONFIG_FILES)
val scriptFile = File(sourcePath)
@@ -54,8 +60,32 @@ class JvmCliScriptEvaluationExtension : ScriptEvaluationExtension {
return COMPILATION_ERROR
}
val script = scriptFile.toScriptSource()
val definition = scriptDefinitionProvider.findDefinition(scriptFile) ?: scriptDefinitionProvider.getDefaultDefinition()
val scriptArgs = arguments.freeArgs.subList(1, arguments.freeArgs.size)
return KotlinToJVMBytecodeCompiler.compileAndExecuteScript(coreEnvironment, scriptArgs)
val evaluationConfiguration = definition.evaluationConfiguration.with {
constructorArgs(scriptArgs.toTypedArray())
}
val scriptCompilationConfiguration = definition.compilationConfiguration
val scriptCompiler = ScriptJvmCompilerFromEnvironment(environment)
return runBlocking {
val compiledScript =
scriptCompiler.compile(script, scriptCompilationConfiguration)
.valueOr { return@runBlocking COMPILATION_ERROR }
/*val evalResult = */
BasicJvmScriptEvaluator().invoke(compiledScript, evaluationConfiguration)
.valueOr {
for (report in it.reports) {
messageCollector.report(ERROR, report.toString())
}
return@runBlocking ExitCode.SCRIPT_EXECUTION_ERROR
}
ExitCode.OK
}
}
}
@@ -0,0 +1,18 @@
/*
* 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
import kotlin.script.experimental.api.CompiledScript
import kotlin.script.experimental.api.ResultWithDiagnostics
import kotlin.script.experimental.api.ScriptCompilationConfiguration
import kotlin.script.experimental.api.SourceCode
interface ScriptJvmCompilerProxy {
fun compile(
script: SourceCode,
scriptCompilationConfiguration: ScriptCompilationConfiguration
): ResultWithDiagnostics<CompiledScript<*>>
}
@@ -3,7 +3,7 @@
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package kotlin.script.experimental.jvmhost.impl
package org.jetbrains.kotlin.scripting.compiler.plugin.impl
import org.jetbrains.kotlin.codegen.BytesUrlUtils
import java.io.ByteArrayInputStream
@@ -3,7 +3,7 @@
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package kotlin.script.experimental.jvmhost.impl
package org.jetbrains.kotlin.scripting.compiler.plugin.impl
import java.io.Serializable
import kotlin.script.experimental.jvm.impl.KJvmCompiledModule
@@ -3,7 +3,7 @@
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package kotlin.script.experimental.jvmhost.impl
package org.jetbrains.kotlin.scripting.compiler.plugin.impl
import com.intellij.openapi.Disposable
import com.intellij.openapi.project.Project
@@ -20,18 +20,23 @@ import org.jetbrains.kotlin.codegen.CompilationErrorHandler
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.definitions.ScriptDependenciesProvider
import org.jetbrains.kotlin.scripting.repl.ReplCodeAnalyzer
import kotlin.script.experimental.api.*
import kotlin.script.experimental.host.ScriptingHostConfiguration
import kotlin.script.experimental.jvmhost.repl.JvmReplCompilerState
import kotlin.script.experimental.jvmhost.repl.KJvmReplCompilerProxy
class KJvmReplCompilerImpl(val hostConfiguration: ScriptingHostConfiguration) : KJvmReplCompilerProxy {
override fun createReplCompilationState(scriptCompilationConfiguration: ScriptCompilationConfiguration): JvmReplCompilerState.Compilation {
val context = withMessageCollectorAndDisposable(disposeOnSuccess = false) { messageCollector, disposable ->
createSharedCompilationContext(scriptCompilationConfiguration, hostConfiguration, messageCollector, disposable).asSuccess()
createIsolatedCompilationContext(
scriptCompilationConfiguration,
hostConfiguration,
messageCollector,
disposable
).asSuccess()
}.valueOr { throw IllegalStateException("Unable to initialize repl compiler:\n ${it.reports.joinToString("\n ")}") }
return ReplCompilationState(context)
}
@@ -42,7 +47,12 @@ class KJvmReplCompilerImpl(val hostConfiguration: ScriptingHostConfiguration) :
project: Project
): ResultWithDiagnostics<Boolean> =
withMessageCollector(script) { messageCollector ->
val ktFile = getScriptKtFile(script, scriptCompilationConfiguration, project, messageCollector)
val ktFile = getScriptKtFile(
script,
scriptCompilationConfiguration,
project,
messageCollector
)
.valueOr { return it }
val errorHolder = object : MessageCollectorBasedReporter {
override val messageCollector = messageCollector
@@ -80,19 +90,34 @@ class KJvmReplCompilerImpl(val hostConfiguration: ScriptingHostConfiguration) :
}
val snippetKtFile =
getScriptKtFile(snippet, context.baseScriptCompilationConfiguration, context.environment.project, messageCollector)
getScriptKtFile(
snippet,
context.baseScriptCompilationConfiguration,
context.environment.project,
messageCollector
)
.valueOr { return it }
val (sourceFiles, sourceDependencies) = collectRefinedSourcesAndUpdateEnvironment(context, snippetKtFile, messageCollector)
val (sourceFiles, sourceDependencies) = collectRefinedSourcesAndUpdateEnvironment(
context,
snippetKtFile,
messageCollector
)
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.WithErrors -> return failure(
messageCollector
)
is ReplCodeAnalyzer.ReplLineAnalysisResult.Successful -> analysisResult.scriptDescriptor
else -> return failure(snippet, messageCollector, "Unexpected result ${analysisResult::class.java}")
else -> return failure(
snippet,
messageCollector,
"Unexpected result ${analysisResult::class.java}"
)
}
val type = (scriptDescriptor as ScriptDescriptor).resultValue?.returnType
@@ -121,7 +146,12 @@ class KJvmReplCompilerImpl(val hostConfiguration: ScriptingHostConfiguration) :
val dependenciesProvider = ScriptDependenciesProvider.getInstance(context.environment.project)
val compiledScript =
makeCompiledScript(generationState, snippet, sourceFiles.first(), sourceDependencies) { ktFile ->
makeCompiledScript(
generationState,
snippet,
sourceFiles.first(),
sourceDependencies
) { ktFile ->
dependenciesProvider?.getScriptConfigurationResult(ktFile)?.valueOrNull()?.configuration
?: context.baseScriptCompilationConfiguration
}
@@ -131,7 +161,7 @@ class KJvmReplCompilerImpl(val hostConfiguration: ScriptingHostConfiguration) :
}
internal class ReplCompilationState(val context: SharedScriptCompilationContext) : 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 environment: KotlinCoreEnvironment get() = context.environment
override val analyzerEngine: ReplCodeAnalyzer by lazy {
@@ -0,0 +1,146 @@
/*
* 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 org.jetbrains.kotlin.analyzer.AnalysisResult
import org.jetbrains.kotlin.cli.common.CLIConfigurationKeys
import org.jetbrains.kotlin.cli.common.messages.AnalyzerWithCompilerReport
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.codegen.ClassBuilderFactories
import org.jetbrains.kotlin.codegen.CompilationErrorHandler
import org.jetbrains.kotlin.codegen.KotlinCodegenFacade
import org.jetbrains.kotlin.codegen.state.GenerationState
import org.jetbrains.kotlin.config.CompilerConfiguration
import org.jetbrains.kotlin.config.languageVersionSettings
import org.jetbrains.kotlin.psi.KtFile
import org.jetbrains.kotlin.scripting.compiler.plugin.ScriptJvmCompilerProxy
import org.jetbrains.kotlin.scripting.definitions.ScriptDependenciesProvider
import kotlin.script.experimental.api.*
import kotlin.script.experimental.host.ScriptingHostConfiguration
class ScriptJvmCompilerIsolated(val hostConfiguration: ScriptingHostConfiguration) : ScriptJvmCompilerProxy {
override fun compile(
script: SourceCode,
scriptCompilationConfiguration: ScriptCompilationConfiguration
): ResultWithDiagnostics<CompiledScript<*>> =
withMessageCollectorAndDisposable(script = script) { messageCollector, disposable ->
val context = createIsolatedCompilationContext(
scriptCompilationConfiguration, hostConfiguration, messageCollector, disposable
)
compileImpl(script, context, messageCollector)
}
}
class ScriptJvmCompilerFromEnvironment(val environment: KotlinCoreEnvironment) : ScriptJvmCompilerProxy {
override fun compile(
script: SourceCode,
scriptCompilationConfiguration: ScriptCompilationConfiguration
): ResultWithDiagnostics<CompiledScript<*>> {
val parentMessageCollector = environment.configuration[CLIConfigurationKeys.MESSAGE_COLLECTOR_KEY]
return withMessageCollector(script = script, parentMessageCollector = parentMessageCollector) { messageCollector ->
val context = createCompilationContextFromEnvironment(scriptCompilationConfiguration, environment, messageCollector)
try {
environment.configuration.put(CLIConfigurationKeys.MESSAGE_COLLECTOR_KEY, messageCollector)
compileImpl(script, context, messageCollector)
} finally {
if (parentMessageCollector != null)
environment.configuration.put(CLIConfigurationKeys.MESSAGE_COLLECTOR_KEY, parentMessageCollector)
}
}
}
}
private fun compileImpl(
script: SourceCode,
context: SharedScriptCompilationContext,
messageCollector: ScriptDiagnosticsMessageCollector
): ResultWithDiagnostics<CompiledScript<*>> {
val mainKtFile =
getScriptKtFile(
script,
context.baseScriptCompilationConfiguration,
context.environment.project,
messageCollector
)
.valueOr { return it }
val (sourceFiles, sourceDependencies) = collectRefinedSourcesAndUpdateEnvironment(
context,
mainKtFile,
messageCollector
)
val dependenciesProvider = ScriptDependenciesProvider.getInstance(context.environment.project)
val analysisResult = analyze(sourceFiles, context.environment)
if (!analysisResult.shouldGenerateCode) return failure(
script,
messageCollector,
"no code to generate"
)
if (analysisResult.isError() || messageCollector.hasErrors()) return failure(
messageCollector
)
val generationState =
generate(analysisResult, sourceFiles, context.environment.configuration)
val compiledScript =
makeCompiledScript(
generationState,
script,
sourceFiles.first(),
sourceDependencies
) { ktFile ->
dependenciesProvider?.getScriptConfigurationResult(ktFile)?.valueOrNull()?.configuration
?: context.baseScriptCompilationConfiguration
}
return ResultWithDiagnostics.Success(compiledScript, messageCollector.diagnostics)
}
private fun analyze(sourceFiles: Collection<KtFile>, environment: KotlinCoreEnvironment): AnalysisResult {
val messageCollector = environment.configuration[CLIConfigurationKeys.MESSAGE_COLLECTOR_KEY]!!
val analyzerWithCompilerReport = AnalyzerWithCompilerReport(messageCollector, environment.configuration.languageVersionSettings)
analyzerWithCompilerReport.analyzeAndReport(sourceFiles) {
val project = environment.project
TopDownAnalyzerFacadeForJVM.analyzeFilesWithJavaIntegration(
project,
sourceFiles,
NoScopeRecordCliBindingTrace(),
environment.configuration,
environment::createPackagePartProvider
)
}
return analyzerWithCompilerReport.analysisResult
}
private fun generate(
analysisResult: AnalysisResult, sourceFiles: List<KtFile>, kotlinCompilerConfiguration: CompilerConfiguration
): GenerationState {
val generationState = GenerationState.Builder(
sourceFiles.first().project,
ClassBuilderFactories.BINARIES,
analysisResult.moduleDescriptor,
analysisResult.bindingContext,
sourceFiles,
kotlinCompilerConfiguration
).build()
KotlinCodegenFacade.compileCorrectFiles(generationState, CompilationErrorHandler.THROW_EXCEPTION)
return generationState
}
@@ -3,7 +3,7 @@
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package kotlin.script.experimental.jvmhost.impl
package org.jetbrains.kotlin.scripting.compiler.plugin.impl
import com.intellij.openapi.Disposable
import org.jetbrains.kotlin.cli.common.CLIConfigurationKeys
@@ -42,14 +42,14 @@ import kotlin.script.experimental.jvm.util.KotlinJars
import kotlin.script.experimental.jvm.withUpdatedClasspath
internal class SharedScriptCompilationContext(
val disposable: Disposable,
val disposable: Disposable?,
val baseScriptCompilationConfiguration: ScriptCompilationConfiguration,
val environment: KotlinCoreEnvironment,
val ignoredOptionsReportingState: IgnoredOptionsReportingState
)
internal fun createSharedCompilationContext(
scriptCompilationConfiguration: ScriptCompilationConfiguration,
internal fun createIsolatedCompilationContext(
baseScriptCompilationConfiguration: ScriptCompilationConfiguration,
hostConfiguration: ScriptingHostConfiguration,
messageCollector: ScriptDiagnosticsMessageCollector,
disposable: Disposable
@@ -57,9 +57,7 @@ internal fun createSharedCompilationContext(
val ignoredOptionsReportingState = IgnoredOptionsReportingState()
val (initialScriptCompilationConfiguration, kotlinCompilerConfiguration) =
createInitialConfigurations(
scriptCompilationConfiguration, hostConfiguration, messageCollector, ignoredOptionsReportingState
)
createInitialConfigurations(baseScriptCompilationConfiguration, hostConfiguration, messageCollector, ignoredOptionsReportingState)
val environment =
KotlinCoreEnvironment.createForProduction(
disposable, kotlinCompilerConfiguration, EnvironmentConfigFiles.JVM_CONFIG_FILES
@@ -70,6 +68,25 @@ internal fun createSharedCompilationContext(
)
}
internal fun createCompilationContextFromEnvironment(
baseScriptCompilationConfiguration: ScriptCompilationConfiguration,
environment: KotlinCoreEnvironment,
messageCollector: ScriptDiagnosticsMessageCollector
): SharedScriptCompilationContext {
val ignoredOptionsReportingState = IgnoredOptionsReportingState()
val initialScriptCompilationConfiguration =
baseScriptCompilationConfiguration.withUpdatesFromCompilerConfiguration(environment.configuration)
initialScriptCompilationConfiguration[ScriptCompilationConfiguration.compilerOptions]?.let { compilerOptions ->
environment.configuration.updateWithCompilerOptions(compilerOptions, messageCollector, ignoredOptionsReportingState, false)
}
return SharedScriptCompilationContext(
null, initialScriptCompilationConfiguration, environment, ignoredOptionsReportingState
)
}
internal fun createInitialConfigurations(
scriptCompilationConfiguration: ScriptCompilationConfiguration,
hostConfiguration: ScriptingHostConfiguration,
@@ -105,9 +122,17 @@ private fun CompilerConfiguration.updateWithCompilerOptions(
val compilerArguments = K2JVMCompilerArguments()
parseCommandLineArguments(compilerOptions, compilerArguments)
reportArgumentsIgnoredGenerally(compilerArguments, messageCollector, ignoredOptionsReportingState)
reportArgumentsIgnoredGenerally(
compilerArguments,
messageCollector,
ignoredOptionsReportingState
)
if (isRefinement) {
reportArgumentsIgnoredFromRefinement(compilerArguments, messageCollector, ignoredOptionsReportingState)
reportArgumentsIgnoredFromRefinement(
compilerArguments,
messageCollector,
ignoredOptionsReportingState
)
}
setupCommonArguments(compilerArguments)
@@ -3,7 +3,7 @@
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package kotlin.script.experimental.jvmhost.impl
package org.jetbrains.kotlin.scripting.compiler.plugin.impl
import org.jetbrains.kotlin.cli.common.arguments.Argument
import org.jetbrains.kotlin.cli.common.arguments.K2JVMCompilerArguments
@@ -17,7 +17,7 @@ import kotlin.script.experimental.api.ScriptDiagnostic
import kotlin.script.experimental.api.SourceCode
import kotlin.script.experimental.api.asErrorDiagnostics
internal class ScriptDiagnosticsMessageCollector : MessageCollector {
internal class ScriptDiagnosticsMessageCollector(private val parentMessageCollector: MessageCollector?) : MessageCollector {
private val _diagnostics = arrayListOf<ScriptDiagnostic>()
@@ -25,11 +25,11 @@ internal class ScriptDiagnosticsMessageCollector : MessageCollector {
override fun clear() {
_diagnostics.clear()
parentMessageCollector?.clear()
}
override fun hasErrors(): Boolean =
_diagnostics.any { it.severity == ScriptDiagnostic.Severity.ERROR }
_diagnostics.any { it.severity == ScriptDiagnostic.Severity.ERROR } || parentMessageCollector?.hasErrors() == true
override fun report(severity: CompilerMessageSeverity, message: String, location: CompilerMessageLocation?) {
val mappedSeverity = when (severity) {
@@ -53,6 +53,7 @@ internal class ScriptDiagnosticsMessageCollector : MessageCollector {
}
_diagnostics.add(ScriptDiagnostic(message, mappedSeverity, location?.path, mappedLocation))
}
parentMessageCollector?.report(severity, message, location)
}
}
@@ -3,7 +3,7 @@
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package kotlin.script.experimental.jvmhost.impl
package org.jetbrains.kotlin.scripting.compiler.plugin.impl
import com.intellij.openapi.Disposable
import com.intellij.openapi.project.Project
@@ -11,6 +11,7 @@ import com.intellij.openapi.util.Disposer
import com.intellij.psi.PsiFileFactory
import com.intellij.psi.impl.PsiFileFactoryImpl
import org.jetbrains.kotlin.cli.common.environment.setIdeaIoUseFallback
import org.jetbrains.kotlin.cli.common.messages.MessageCollector
import org.jetbrains.kotlin.codegen.state.GenerationState
import org.jetbrains.kotlin.idea.KotlinLanguage
import org.jetbrains.kotlin.psi.KtFile
@@ -22,7 +23,6 @@ import org.jetbrains.kotlin.utils.addToStdlib.firstIsInstanceOrNull
import java.util.*
import kotlin.script.experimental.api.*
import kotlin.script.experimental.host.FileBasedScriptSource
import kotlin.script.experimental.host.FileScriptSource
import kotlin.script.experimental.host.getMergedScriptText
import kotlin.script.experimental.jvm.impl.KJvmCompiledScript
@@ -43,13 +43,14 @@ internal fun SourceCode.scriptFileName(
}
internal inline fun <T> withMessageCollectorAndDisposable(
messageCollector: ScriptDiagnosticsMessageCollector = ScriptDiagnosticsMessageCollector(),
script: SourceCode? = null,
parentMessageCollector: MessageCollector? = null,
disposable: Disposable = Disposer.newDisposable(),
disposeOnSuccess: Boolean = true,
locationId: String? = null,
body: (ScriptDiagnosticsMessageCollector, Disposable) -> ResultWithDiagnostics<T>
): ResultWithDiagnostics<T> {
var failed = false
val messageCollector = ScriptDiagnosticsMessageCollector(parentMessageCollector)
return try {
setIdeaIoUseFallback()
body(messageCollector, disposable).also {
@@ -57,7 +58,7 @@ internal inline fun <T> withMessageCollectorAndDisposable(
}
} catch (ex: Throwable) {
failed = true
failure(messageCollector, ex.asDiagnostics(path = locationId))
failure(messageCollector, ex.asDiagnostics(path = script?.locationId))
} finally {
if (disposeOnSuccess || failed) {
Disposer.dispose(disposable)
@@ -66,15 +67,17 @@ internal inline fun <T> withMessageCollectorAndDisposable(
}
internal inline fun <T> withMessageCollector(
script: SourceCode,
messageCollector: ScriptDiagnosticsMessageCollector = ScriptDiagnosticsMessageCollector(),
script: SourceCode? = null,
parentMessageCollector: MessageCollector? = null,
body: (ScriptDiagnosticsMessageCollector) -> ResultWithDiagnostics<T>
): ResultWithDiagnostics<T> =
try {
): ResultWithDiagnostics<T> {
val messageCollector = ScriptDiagnosticsMessageCollector(parentMessageCollector)
return try {
body(messageCollector)
} catch (ex: Throwable) {
failure(messageCollector, ex.asDiagnostics(path = script.locationId))
failure(messageCollector, ex.asDiagnostics(path = script?.locationId))
}
}
internal fun getScriptKtFile(
script: SourceCode,
@@ -91,8 +94,16 @@ internal fun getScriptKtFile(
)
val ktFile = psiFileFactory.trySetupPsiForFile(virtualFile, KotlinLanguage.INSTANCE, true, false) as KtFile?
return when {
ktFile == null -> failure(script, messageCollector, "Unable to make PSI file from script")
ktFile.declarations.firstIsInstanceOrNull<KtScript>() == null -> failure(script, messageCollector, "Not a script file")
ktFile == null -> failure(
script,
messageCollector,
"Unable to make PSI file from script"
)
ktFile.declarations.firstIsInstanceOrNull<KtScript>() == null -> failure(
script,
messageCollector,
"Not a script file"
)
else -> ktFile.asSuccess()
}
}
@@ -0,0 +1,109 @@
/*
* 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.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
import org.jetbrains.kotlin.descriptors.ScriptDescriptor
import org.jetbrains.kotlin.scripting.repl.ReplCodeAnalyzer
import java.util.concurrent.locks.ReentrantReadWriteLock
import kotlin.concurrent.write
import kotlin.script.experimental.api.*
interface KJvmReplCompilerProxy {
fun createReplCompilationState(scriptCompilationConfiguration: ScriptCompilationConfiguration): JvmReplCompilerState.Compilation
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,
override val lock: ReentrantReadWriteLock = ReentrantReadWriteLock()
) : IReplStageState<ScriptDescriptor> {
override val history = JvmReplCompilerStageHistory(this)
override val currentGeneration: Int get() = (history as BasicReplStageHistory<*>).currentGeneration.get()
override fun dispose() {
lock.write {
_compilation?.disposable?.let {
Disposer.dispose(it)
}
_compilation = null
super.dispose()
}
}
fun getCompilationState(scriptCompilationConfiguration: ScriptCompilationConfiguration): Compilation = lock.write {
if (_compilation == null) {
initializeCompilation(scriptCompilationConfiguration)
}
_compilation!!
}
internal val compilation: Compilation
get() = _compilation ?: throw IllegalStateException("Compilation state is either not initializad or already destroyed")
private var _compilation: Compilation? = 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)
}
interface Compilation {
val disposable: Disposable?
val baseScriptCompilationConfiguration: ScriptCompilationConfiguration
val environment: KotlinCoreEnvironment
val analyzerEngine: ReplCodeAnalyzer
}
}