From dc4370ff08416b1e8ee9952c6f012a9206e9cbac Mon Sep 17 00:00:00 2001 From: Ilya Chernikov Date: Mon, 1 Jul 2019 13:41:08 +0200 Subject: [PATCH] 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 --- .../experimental/api/scriptCompilation.kt | 22 +-- .../experimental/api/scriptEvaluation.kt | 12 ++ .../scripting/jvm-host-test/build.gradle.kts | 2 +- .../experimental/jvmhost/test/ReplTest.kt | 5 +- .../jvmhost/test/ScriptingHostTest.kt | 4 +- .../jvmhost/impl/KJvmCompilerImpl.kt | 91 ----------- .../jvmhost/jvmScriptCompilation.kt | 12 +- .../experimental/jvmhost/jvmScriptSaving.kt | 10 +- .../jvmhost/repl/jvmReplCompilation.kt | 29 ---- .../jvmhost/repl/legacyReplCompilation.kt | 86 +---------- .../kotlin-main-kts-test/build.gradle.kts | 2 + .../kotlin/mainKts/test/mainKtsIT.kt | 10 +- .../org/jetbrains/kotlin/mainKts/scriptDef.kt | 12 +- .../plugin/JvmCliScriptEvaluationExtension.kt | 36 ++++- .../compiler/plugin/ScriptJvmCompilerProxy.kt | 18 +++ .../plugin}/impl/CompiledScriptClassLoader.kt | 2 +- .../impl/KJvmCompiledModuleInMemory.kt | 2 +- .../plugin}/impl/KJvmReplCompilerImpl.kt | 52 +++++-- .../plugin/impl/ScriptJvmCompilerImpls.kt | 146 ++++++++++++++++++ .../plugin}/impl/compilationContext.kt | 43 ++++-- .../compiler/plugin}/impl/errorReporting.kt | 9 +- .../plugin}/impl/jvmCompilationUtil.kt | 35 +++-- .../plugin/repl/jvmReplCompilation.kt | 109 +++++++++++++ 23 files changed, 477 insertions(+), 272 deletions(-) delete mode 100644 libraries/scripting/jvm-host/src/kotlin/script/experimental/jvmhost/impl/KJvmCompilerImpl.kt delete mode 100644 libraries/scripting/jvm-host/src/kotlin/script/experimental/jvmhost/repl/jvmReplCompilation.kt create mode 100644 plugins/scripting/scripting-compiler/src/org/jetbrains/kotlin/scripting/compiler/plugin/ScriptJvmCompilerProxy.kt rename {libraries/scripting/jvm-host/src/kotlin/script/experimental/jvmhost => plugins/scripting/scripting-compiler/src/org/jetbrains/kotlin/scripting/compiler/plugin}/impl/CompiledScriptClassLoader.kt (95%) rename {libraries/scripting/jvm-host/src/kotlin/script/experimental/jvmhost => plugins/scripting/scripting-compiler/src/org/jetbrains/kotlin/scripting/compiler/plugin}/impl/KJvmCompiledModuleInMemory.kt (91%) rename {libraries/scripting/jvm-host/src/kotlin/script/experimental/jvmhost => plugins/scripting/scripting-compiler/src/org/jetbrains/kotlin/scripting/compiler/plugin}/impl/KJvmReplCompilerImpl.kt (81%) create mode 100644 plugins/scripting/scripting-compiler/src/org/jetbrains/kotlin/scripting/compiler/plugin/impl/ScriptJvmCompilerImpls.kt rename {libraries/scripting/jvm-host/src/kotlin/script/experimental/jvmhost => plugins/scripting/scripting-compiler/src/org/jetbrains/kotlin/scripting/compiler/plugin}/impl/compilationContext.kt (86%) rename {libraries/scripting/jvm-host/src/kotlin/script/experimental/jvmhost => plugins/scripting/scripting-compiler/src/org/jetbrains/kotlin/scripting/compiler/plugin}/impl/errorReporting.kt (93%) rename {libraries/scripting/jvm-host/src/kotlin/script/experimental/jvmhost => plugins/scripting/scripting-compiler/src/org/jetbrains/kotlin/scripting/compiler/plugin}/impl/jvmCompilationUtil.kt (87%) create mode 100644 plugins/scripting/scripting-compiler/src/org/jetbrains/kotlin/scripting/compiler/plugin/repl/jvmReplCompilation.kt diff --git a/libraries/scripting/common/src/kotlin/script/experimental/api/scriptCompilation.kt b/libraries/scripting/common/src/kotlin/script/experimental/api/scriptCompilation.kt index b3733cdc061..50942839f23 100644 --- a/libraries/scripting/common/src/kotlin/script/experimental/api/scriptCompilation.kt +++ b/libraries/scripting/common/src/kotlin/script/experimental/api/scriptCompilation.kt @@ -35,21 +35,23 @@ open class ScriptCompilationConfiguration(baseConfigurations: Iterable 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 */ diff --git a/libraries/scripting/common/src/kotlin/script/experimental/api/scriptEvaluation.kt b/libraries/scripting/common/src/kotlin/script/experimental/api/scriptEvaluation.kt index 5e5a551c95b..cb761a0ff55 100644 --- a/libraries/scripting/common/src/kotlin/script/experimental/api/scriptEvaluation.kt +++ b/libraries/scripting/common/src/kotlin/script/experimental/api/scriptEvaluation.kt @@ -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} */ diff --git a/libraries/scripting/jvm-host-test/build.gradle.kts b/libraries/scripting/jvm-host-test/build.gradle.kts index c054d320f25..bb47ac13067 100644 --- a/libraries/scripting/jvm-host-test/build.gradle.kts +++ b/libraries/scripting/jvm-host-test/build.gradle.kts @@ -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")) diff --git a/libraries/scripting/jvm-host-test/test/kotlin/script/experimental/jvmhost/test/ReplTest.kt b/libraries/scripting/jvm-host-test/test/kotlin/script/experimental/jvmhost/test/ReplTest.kt index 8b8dc814baf..8d298418d41 100644 --- a/libraries/scripting/jvm-host-test/test/kotlin/script/experimental/jvmhost/test/ReplTest.kt +++ b/libraries/scripting/jvm-host-test/test/kotlin/script/experimental/jvmhost/test/ReplTest.kt @@ -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 ): Sequence> { - val replCompilerProxy = KJvmReplCompilerImpl(defaultJvmScriptingHostConfiguration) + val replCompilerProxy = + KJvmReplCompilerImpl(defaultJvmScriptingHostConfiguration) val compilationState = replCompilerProxy.createReplCompilationState(compilationConfiguration) val compilationHistory = BasicReplStageHistory() val replEvaluator = BasicJvmScriptEvaluator() diff --git a/libraries/scripting/jvm-host-test/test/kotlin/script/experimental/jvmhost/test/ScriptingHostTest.kt b/libraries/scripting/jvm-host-test/test/kotlin/script/experimental/jvmhost/test/ScriptingHostTest.kt index 7416a62fc36..37279b995ae 100644 --- a/libraries/scripting/jvm-host-test/test/kotlin/script/experimental/jvmhost/test/ScriptingHostTest.kt +++ b/libraries/scripting/jvm-host-test/test/kotlin/script/experimental/jvmhost/test/ScriptingHostTest.kt @@ -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() { diff --git a/libraries/scripting/jvm-host/src/kotlin/script/experimental/jvmhost/impl/KJvmCompilerImpl.kt b/libraries/scripting/jvm-host/src/kotlin/script/experimental/jvmhost/impl/KJvmCompilerImpl.kt deleted file mode 100644 index 664bea96ae9..00000000000 --- a/libraries/scripting/jvm-host/src/kotlin/script/experimental/jvmhost/impl/KJvmCompilerImpl.kt +++ /dev/null @@ -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> = - 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, 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, 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 -} diff --git a/libraries/scripting/jvm-host/src/kotlin/script/experimental/jvmhost/jvmScriptCompilation.kt b/libraries/scripting/jvm-host/src/kotlin/script/experimental/jvmhost/jvmScriptCompilation.kt index a69f83fb771..2a785c73041 100644 --- a/libraries/scripting/jvm-host/src/kotlin/script/experimental/jvmhost/jvmScriptCompilation.kt +++ b/libraries/scripting/jvm-host/src/kotlin/script/experimental/jvmhost/jvmScriptCompilation.kt @@ -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> -} - diff --git a/libraries/scripting/jvm-host/src/kotlin/script/experimental/jvmhost/jvmScriptSaving.kt b/libraries/scripting/jvm-host/src/kotlin/script/experimental/jvmhost/jvmScriptSaving.kt index 704787bec38..a1d0288d543 100644 --- a/libraries/scripting/jvm-host/src/kotlin/script/experimental/jvmhost/jvmScriptSaving.kt +++ b/libraries/scripting/jvm-host/src/kotlin/script/experimental/jvmhost/jvmScriptSaving.kt @@ -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) diff --git a/libraries/scripting/jvm-host/src/kotlin/script/experimental/jvmhost/repl/jvmReplCompilation.kt b/libraries/scripting/jvm-host/src/kotlin/script/experimental/jvmhost/repl/jvmReplCompilation.kt deleted file mode 100644 index 8614d7fba1a..00000000000 --- a/libraries/scripting/jvm-host/src/kotlin/script/experimental/jvmhost/repl/jvmReplCompilation.kt +++ /dev/null @@ -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 - - - fun compileReplSnippet( - compilationState: JvmReplCompilerState.Compilation, - snippet: SourceCode, - snippetId: ReplSnippetId, - history: IReplStageHistory - ): ResultWithDiagnostics> -} \ No newline at end of file diff --git a/libraries/scripting/jvm-host/src/kotlin/script/experimental/jvmhost/repl/legacyReplCompilation.kt b/libraries/scripting/jvm-host/src/kotlin/script/experimental/jvmhost/repl/legacyReplCompilation.kt index ba8f53ef7e2..ba7243e334f 100644 --- a/libraries/scripting/jvm-host/src/kotlin/script/experimental/jvmhost/repl/legacyReplCompilation.kt +++ b/libraries/scripting/jvm-host/src/kotlin/script/experimental/jvmhost/repl/legacyReplCompilation.kt @@ -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(state.lock) { - - override fun reset(): Iterable { - val removedCompiledLines = super.reset() - val removedAnalyzedLines = state.compilation.analyzerEngine.reset() - - checkConsistent(removedCompiledLines, removedAnalyzedLines) - return removedCompiledLines - } - - override fun resetTo(id: ILineId): Iterable { - val removedCompiledLines = super.resetTo(id) - val removedAnalyzedLines = state.compilation.analyzerEngine.resetToLine(id) - - checkConsistent(removedCompiledLines, removedAnalyzedLines) - return removedCompiledLines - } - - private fun checkConsistent(removedCompiledLines: Iterable, removedAnalyzedLines: List) { - 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 { - - 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, diff --git a/libraries/tools/kotlin-main-kts-test/build.gradle.kts b/libraries/tools/kotlin-main-kts-test/build.gradle.kts index e438a834aaa..d4d7c385011 100644 --- a/libraries/tools/kotlin-main-kts-test/build.gradle.kts +++ b/libraries/tools/kotlin-main-kts-test/build.gradle.kts @@ -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 { diff --git a/libraries/tools/kotlin-main-kts-test/test/org/jetbrains/kotlin/mainKts/test/mainKtsIT.kt b/libraries/tools/kotlin-main-kts-test/test/org/jetbrains/kotlin/mainKts/test/mainKtsIT.kt index 51f66dd0369..7f717e05d21 100644 --- a/libraries/tools/kotlin-main-kts-test/test/org/jetbrains/kotlin/mainKts/test/mainKtsIT.kt +++ b/libraries/tools/kotlin-main-kts-test/test/org/jetbrains/kotlin/mainKts/test/mainKtsIT.kt @@ -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")) + } } diff --git a/libraries/tools/kotlin-main-kts/src/org/jetbrains/kotlin/mainKts/scriptDef.kt b/libraries/tools/kotlin-main-kts/src/org/jetbrains/kotlin/mainKts/scriptDef.kt index 63d00386c21..2584387e4c7 100644 --- a/libraries/tools/kotlin-main-kts/src/org/jetbrains/kotlin/mainKts/scriptDef.kt +++ b/libraries/tools/kotlin-main-kts/src/org/jetbrains/kotlin/mainKts/scriptDef.kt @@ -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) object MainKtsScriptDefinition : ScriptCompilationConfiguration( @@ -40,6 +44,12 @@ object MainKtsScriptDefinition : ScriptCompilationConfiguration( } }) +object MainKtsEvaluationConfiguration : ScriptEvaluationConfiguration( + { + scriptsInstancesSharing(true) + } +) + class MainKtsConfigurator : RefineScriptCompilationConfigurationHandler { private val resolver = FilesAndIvyResolver() diff --git a/plugins/scripting/scripting-compiler/src/org/jetbrains/kotlin/scripting/compiler/plugin/JvmCliScriptEvaluationExtension.kt b/plugins/scripting/scripting-compiler/src/org/jetbrains/kotlin/scripting/compiler/plugin/JvmCliScriptEvaluationExtension.kt index f96c01e6df5..9f7ab703881 100644 --- a/plugins/scripting/scripting-compiler/src/org/jetbrains/kotlin/scripting/compiler/plugin/JvmCliScriptEvaluationExtension.kt +++ b/plugins/scripting/scripting-compiler/src/org/jetbrains/kotlin/scripting/compiler/plugin/JvmCliScriptEvaluationExtension.kt @@ -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 + } } } \ No newline at end of file diff --git a/plugins/scripting/scripting-compiler/src/org/jetbrains/kotlin/scripting/compiler/plugin/ScriptJvmCompilerProxy.kt b/plugins/scripting/scripting-compiler/src/org/jetbrains/kotlin/scripting/compiler/plugin/ScriptJvmCompilerProxy.kt new file mode 100644 index 00000000000..1705f69897b --- /dev/null +++ b/plugins/scripting/scripting-compiler/src/org/jetbrains/kotlin/scripting/compiler/plugin/ScriptJvmCompilerProxy.kt @@ -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> +} \ No newline at end of file diff --git a/libraries/scripting/jvm-host/src/kotlin/script/experimental/jvmhost/impl/CompiledScriptClassLoader.kt b/plugins/scripting/scripting-compiler/src/org/jetbrains/kotlin/scripting/compiler/plugin/impl/CompiledScriptClassLoader.kt similarity index 95% rename from libraries/scripting/jvm-host/src/kotlin/script/experimental/jvmhost/impl/CompiledScriptClassLoader.kt rename to plugins/scripting/scripting-compiler/src/org/jetbrains/kotlin/scripting/compiler/plugin/impl/CompiledScriptClassLoader.kt index 2ed43812403..4d8cde442b9 100644 --- a/libraries/scripting/jvm-host/src/kotlin/script/experimental/jvmhost/impl/CompiledScriptClassLoader.kt +++ b/plugins/scripting/scripting-compiler/src/org/jetbrains/kotlin/scripting/compiler/plugin/impl/CompiledScriptClassLoader.kt @@ -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 diff --git a/libraries/scripting/jvm-host/src/kotlin/script/experimental/jvmhost/impl/KJvmCompiledModuleInMemory.kt b/plugins/scripting/scripting-compiler/src/org/jetbrains/kotlin/scripting/compiler/plugin/impl/KJvmCompiledModuleInMemory.kt similarity index 91% rename from libraries/scripting/jvm-host/src/kotlin/script/experimental/jvmhost/impl/KJvmCompiledModuleInMemory.kt rename to plugins/scripting/scripting-compiler/src/org/jetbrains/kotlin/scripting/compiler/plugin/impl/KJvmCompiledModuleInMemory.kt index 4c32b0188a9..a0f97925f07 100644 --- a/libraries/scripting/jvm-host/src/kotlin/script/experimental/jvmhost/impl/KJvmCompiledModuleInMemory.kt +++ b/plugins/scripting/scripting-compiler/src/org/jetbrains/kotlin/scripting/compiler/plugin/impl/KJvmCompiledModuleInMemory.kt @@ -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 diff --git a/libraries/scripting/jvm-host/src/kotlin/script/experimental/jvmhost/impl/KJvmReplCompilerImpl.kt b/plugins/scripting/scripting-compiler/src/org/jetbrains/kotlin/scripting/compiler/plugin/impl/KJvmReplCompilerImpl.kt similarity index 81% rename from libraries/scripting/jvm-host/src/kotlin/script/experimental/jvmhost/impl/KJvmReplCompilerImpl.kt rename to plugins/scripting/scripting-compiler/src/org/jetbrains/kotlin/scripting/compiler/plugin/impl/KJvmReplCompilerImpl.kt index d239cdc4a3d..2fb1a4c056f 100644 --- a/libraries/scripting/jvm-host/src/kotlin/script/experimental/jvmhost/impl/KJvmReplCompilerImpl.kt +++ b/plugins/scripting/scripting-compiler/src/org/jetbrains/kotlin/scripting/compiler/plugin/impl/KJvmReplCompilerImpl.kt @@ -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 = 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 { diff --git a/plugins/scripting/scripting-compiler/src/org/jetbrains/kotlin/scripting/compiler/plugin/impl/ScriptJvmCompilerImpls.kt b/plugins/scripting/scripting-compiler/src/org/jetbrains/kotlin/scripting/compiler/plugin/impl/ScriptJvmCompilerImpls.kt new file mode 100644 index 00000000000..562776374e0 --- /dev/null +++ b/plugins/scripting/scripting-compiler/src/org/jetbrains/kotlin/scripting/compiler/plugin/impl/ScriptJvmCompilerImpls.kt @@ -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> = + 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> { + 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> { + 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, 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, 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 +} diff --git a/libraries/scripting/jvm-host/src/kotlin/script/experimental/jvmhost/impl/compilationContext.kt b/plugins/scripting/scripting-compiler/src/org/jetbrains/kotlin/scripting/compiler/plugin/impl/compilationContext.kt similarity index 86% rename from libraries/scripting/jvm-host/src/kotlin/script/experimental/jvmhost/impl/compilationContext.kt rename to plugins/scripting/scripting-compiler/src/org/jetbrains/kotlin/scripting/compiler/plugin/impl/compilationContext.kt index f63cc339bee..08259d68452 100644 --- a/libraries/scripting/jvm-host/src/kotlin/script/experimental/jvmhost/impl/compilationContext.kt +++ b/plugins/scripting/scripting-compiler/src/org/jetbrains/kotlin/scripting/compiler/plugin/impl/compilationContext.kt @@ -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) diff --git a/libraries/scripting/jvm-host/src/kotlin/script/experimental/jvmhost/impl/errorReporting.kt b/plugins/scripting/scripting-compiler/src/org/jetbrains/kotlin/scripting/compiler/plugin/impl/errorReporting.kt similarity index 93% rename from libraries/scripting/jvm-host/src/kotlin/script/experimental/jvmhost/impl/errorReporting.kt rename to plugins/scripting/scripting-compiler/src/org/jetbrains/kotlin/scripting/compiler/plugin/impl/errorReporting.kt index 685dc838a2a..be3bcfbab61 100644 --- a/libraries/scripting/jvm-host/src/kotlin/script/experimental/jvmhost/impl/errorReporting.kt +++ b/plugins/scripting/scripting-compiler/src/org/jetbrains/kotlin/scripting/compiler/plugin/impl/errorReporting.kt @@ -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() @@ -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) } } diff --git a/libraries/scripting/jvm-host/src/kotlin/script/experimental/jvmhost/impl/jvmCompilationUtil.kt b/plugins/scripting/scripting-compiler/src/org/jetbrains/kotlin/scripting/compiler/plugin/impl/jvmCompilationUtil.kt similarity index 87% rename from libraries/scripting/jvm-host/src/kotlin/script/experimental/jvmhost/impl/jvmCompilationUtil.kt rename to plugins/scripting/scripting-compiler/src/org/jetbrains/kotlin/scripting/compiler/plugin/impl/jvmCompilationUtil.kt index aeb8822ae85..70a6e02c01e 100644 --- a/libraries/scripting/jvm-host/src/kotlin/script/experimental/jvmhost/impl/jvmCompilationUtil.kt +++ b/plugins/scripting/scripting-compiler/src/org/jetbrains/kotlin/scripting/compiler/plugin/impl/jvmCompilationUtil.kt @@ -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 withMessageCollectorAndDisposable( - messageCollector: ScriptDiagnosticsMessageCollector = ScriptDiagnosticsMessageCollector(), + script: SourceCode? = null, + parentMessageCollector: MessageCollector? = null, disposable: Disposable = Disposer.newDisposable(), disposeOnSuccess: Boolean = true, - locationId: String? = null, body: (ScriptDiagnosticsMessageCollector, Disposable) -> ResultWithDiagnostics ): ResultWithDiagnostics { var failed = false + val messageCollector = ScriptDiagnosticsMessageCollector(parentMessageCollector) return try { setIdeaIoUseFallback() body(messageCollector, disposable).also { @@ -57,7 +58,7 @@ internal inline fun 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 withMessageCollectorAndDisposable( } internal inline fun withMessageCollector( - script: SourceCode, - messageCollector: ScriptDiagnosticsMessageCollector = ScriptDiagnosticsMessageCollector(), + script: SourceCode? = null, + parentMessageCollector: MessageCollector? = null, body: (ScriptDiagnosticsMessageCollector) -> ResultWithDiagnostics -): ResultWithDiagnostics = - try { +): ResultWithDiagnostics { + 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() == null -> failure(script, messageCollector, "Not a script file") + ktFile == null -> failure( + script, + messageCollector, + "Unable to make PSI file from script" + ) + ktFile.declarations.firstIsInstanceOrNull() == null -> failure( + script, + messageCollector, + "Not a script file" + ) else -> ktFile.asSuccess() } } diff --git a/plugins/scripting/scripting-compiler/src/org/jetbrains/kotlin/scripting/compiler/plugin/repl/jvmReplCompilation.kt b/plugins/scripting/scripting-compiler/src/org/jetbrains/kotlin/scripting/compiler/plugin/repl/jvmReplCompilation.kt new file mode 100644 index 00000000000..e7c29ba1867 --- /dev/null +++ b/plugins/scripting/scripting-compiler/src/org/jetbrains/kotlin/scripting/compiler/plugin/repl/jvmReplCompilation.kt @@ -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 + + + fun compileReplSnippet( + compilationState: JvmReplCompilerState.Compilation, + snippet: SourceCode, + snippetId: ReplSnippetId, + history: IReplStageHistory + ): ResultWithDiagnostics> +} + +class JvmReplCompilerStageHistory(private val state: JvmReplCompilerState) : + BasicReplStageHistory(state.lock) { + + override fun reset(): Iterable { + val removedCompiledLines = super.reset() + val removedAnalyzedLines = state.compilation.analyzerEngine.reset() + + checkConsistent(removedCompiledLines, removedAnalyzedLines) + return removedCompiledLines + } + + override fun resetTo(id: ILineId): Iterable { + val removedCompiledLines = super.resetTo(id) + val removedAnalyzedLines = state.compilation.analyzerEngine.resetToLine(id) + + checkConsistent(removedCompiledLines, removedAnalyzedLines) + return removedCompiledLines + } + + private fun checkConsistent(removedCompiledLines: Iterable, removedAnalyzedLines: List) { + 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 { + + 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 + } +}