Prepare scripting infrastructure for REPL:
- refactor script compiler to simplify extending it for repl - add repl snippet compilation functions to the new scripting compiler - extract util functions into appropriate files - extract repl part into separate class - extract bridge definition and related definitions into separate file
This commit is contained in:
@@ -61,6 +61,10 @@ interface IReplStageState<T> {
|
|||||||
fun <StateT : IReplStageState<*>> asState(target: Class<out StateT>): StateT =
|
fun <StateT : IReplStageState<*>> asState(target: Class<out StateT>): StateT =
|
||||||
if (target.isAssignableFrom(this::class.java)) this as StateT
|
if (target.isAssignableFrom(this::class.java)) this as StateT
|
||||||
else throw IllegalArgumentException("$this is not an expected instance of IReplStageState")
|
else throw IllegalArgumentException("$this is not an expected instance of IReplStageState")
|
||||||
|
|
||||||
|
fun dispose() {
|
||||||
|
history.reset()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -161,3 +161,11 @@ fun <R> ResultWithDiagnostics<R>.resultOrNull(): R? = when (this) {
|
|||||||
is ResultWithDiagnostics.Success<R> -> value
|
is ResultWithDiagnostics.Success<R> -> value
|
||||||
else -> null
|
else -> null
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Extracts the result value from the receiver wrapper or run non-returning lambda if receiver represents a Failure
|
||||||
|
*/
|
||||||
|
inline fun <R> ResultWithDiagnostics<R>.resultOr(body: (ResultWithDiagnostics.Failure) -> Nothing): R = when (this) {
|
||||||
|
is ResultWithDiagnostics.Success<R> -> value
|
||||||
|
else -> body(this as ResultWithDiagnostics.Failure)
|
||||||
|
}
|
||||||
|
|||||||
@@ -0,0 +1,31 @@
|
|||||||
|
/*
|
||||||
|
* 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.api
|
||||||
|
|
||||||
|
import java.io.Serializable
|
||||||
|
|
||||||
|
const val REPL_SNIPPET_FIRST_NO = 1
|
||||||
|
const val REPL_Snippet_FIRST_GEN = 1
|
||||||
|
|
||||||
|
interface ReplSnippetId : Serializable, Comparable<ReplSnippetId> {
|
||||||
|
val no: Int
|
||||||
|
val generation: Int
|
||||||
|
}
|
||||||
|
|
||||||
|
data class ReplSnippetIdImpl(override val no: Int, override val generation: Int, private val codeHash: Int) : ReplSnippetId, Serializable {
|
||||||
|
|
||||||
|
constructor(no: Int, generation: Int, code: SourceCode) : this(no, generation, code.text.hashCode())
|
||||||
|
|
||||||
|
override fun compareTo(other: ReplSnippetId): Int = (other as? ReplSnippetIdImpl)?.let { otherId ->
|
||||||
|
no.compareTo(otherId.no).takeIf { it != 0 }
|
||||||
|
?: generation.compareTo(otherId.generation).takeIf { it != 0 }
|
||||||
|
?: codeHash.compareTo(otherId.codeHash)
|
||||||
|
} ?: -1
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
private val serialVersionUID: Long = 1L
|
||||||
|
}
|
||||||
|
}
|
||||||
+145
@@ -0,0 +1,145 @@
|
|||||||
|
/*
|
||||||
|
* 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.impl
|
||||||
|
|
||||||
|
import com.intellij.openapi.fileTypes.LanguageFileType
|
||||||
|
import org.jetbrains.kotlin.idea.KotlinFileType
|
||||||
|
import org.jetbrains.kotlin.name.Name
|
||||||
|
import org.jetbrains.kotlin.name.NameUtils
|
||||||
|
import org.jetbrains.kotlin.psi.KtScript
|
||||||
|
import org.jetbrains.kotlin.scripting.definitions.KotlinScriptDefinition
|
||||||
|
import kotlin.reflect.KClass
|
||||||
|
import kotlin.reflect.KType
|
||||||
|
import kotlin.reflect.full.starProjectedType
|
||||||
|
import kotlin.script.dependencies.ScriptContents
|
||||||
|
import kotlin.script.experimental.api.*
|
||||||
|
import kotlin.script.experimental.dependencies.DependenciesResolver
|
||||||
|
import kotlin.script.experimental.host.ScriptingHostConfiguration
|
||||||
|
import kotlin.script.experimental.host.getScriptingClass
|
||||||
|
import kotlin.script.experimental.jvm.impl.BridgeDependenciesResolver
|
||||||
|
import kotlin.script.experimental.util.getOrError
|
||||||
|
|
||||||
|
// A bridge to the current scripting
|
||||||
|
// mostly copies functionality from KotlinScriptDefinitionAdapterFromNewAPI[Base]
|
||||||
|
// reusing it requires structural changes that doesn't seem justified now, since the internals of the scripting should be reworked soon anyway
|
||||||
|
// TODO: either finish refactoring of the scripting internals or reuse KotlinScriptDefinitionAdapterFromNewAPI[Base] here
|
||||||
|
// NOTE: since KotlinScriptDefinition is not designed to separate static (script definition related) and dynamic (actual script compilation
|
||||||
|
// configuration) parameters, the implementation is quite hacky, especially for cases as REPL
|
||||||
|
// TODO: finish refactoring and replace KotlinScriptDefinition with right abstractions
|
||||||
|
internal class BridgeScriptDefinition(
|
||||||
|
val scriptCompilationConfiguration: ScriptCompilationConfiguration,
|
||||||
|
val hostConfiguration: ScriptingHostConfiguration,
|
||||||
|
dynamicState: BridgeScriptDefinitionDynamicState
|
||||||
|
) : KotlinScriptDefinition(Any::class) {
|
||||||
|
|
||||||
|
val baseClass: KClass<*> by lazy(LazyThreadSafetyMode.PUBLICATION) {
|
||||||
|
getScriptingClass(scriptCompilationConfiguration.getOrError(ScriptCompilationConfiguration.baseClass))
|
||||||
|
}
|
||||||
|
|
||||||
|
override val template: KClass<*> get() = baseClass
|
||||||
|
|
||||||
|
override val name: String
|
||||||
|
get() = scriptCompilationConfiguration[ScriptCompilationConfiguration.displayName] ?: "Kotlin Script"
|
||||||
|
|
||||||
|
override val fileType: LanguageFileType = KotlinFileType.INSTANCE
|
||||||
|
|
||||||
|
override fun isScript(fileName: String): Boolean =
|
||||||
|
fileName.endsWith(".$fileExtension")
|
||||||
|
|
||||||
|
override fun getScriptName(script: KtScript): Name {
|
||||||
|
val fileBasedName = NameUtils.getScriptNameForFile(script.containingKtFile.name)
|
||||||
|
return Name.identifier(fileBasedName.identifier.removeSuffix(".$fileExtension"))
|
||||||
|
}
|
||||||
|
|
||||||
|
override val fileExtension: String
|
||||||
|
get() = scriptCompilationConfiguration[ScriptCompilationConfiguration.fileExtension] ?: super.fileExtension
|
||||||
|
|
||||||
|
override val acceptedAnnotations = run {
|
||||||
|
val cl = this::class.java.classLoader
|
||||||
|
scriptCompilationConfiguration[ScriptCompilationConfiguration.refineConfigurationOnAnnotations]?.annotations
|
||||||
|
?.map { (cl.loadClass(it.typeName) as Class<out Annotation>).kotlin }
|
||||||
|
?: emptyList()
|
||||||
|
}
|
||||||
|
|
||||||
|
override val implicitReceivers: List<KType> by lazy(LazyThreadSafetyMode.PUBLICATION) {
|
||||||
|
scriptCompilationConfiguration[ScriptCompilationConfiguration.implicitReceivers]
|
||||||
|
.orEmpty()
|
||||||
|
.map { getScriptingClass(it).starProjectedType }
|
||||||
|
}
|
||||||
|
|
||||||
|
override val providedProperties: List<Pair<String, KType>> by lazy(LazyThreadSafetyMode.PUBLICATION) {
|
||||||
|
scriptCompilationConfiguration[ScriptCompilationConfiguration.providedProperties]
|
||||||
|
?.map { (k, v) -> k to getScriptingClass(v).starProjectedType }.orEmpty()
|
||||||
|
}
|
||||||
|
|
||||||
|
override val additionalCompilerArguments: List<String>
|
||||||
|
get() = scriptCompilationConfiguration[ScriptCompilationConfiguration.compilerOptions]
|
||||||
|
.orEmpty()
|
||||||
|
|
||||||
|
override val dependencyResolver: DependenciesResolver =
|
||||||
|
BridgeDependenciesResolver(
|
||||||
|
scriptCompilationConfiguration,
|
||||||
|
dynamicState::updateConfiguration,
|
||||||
|
dynamicState::getScriptSource
|
||||||
|
)
|
||||||
|
|
||||||
|
private val scriptingClassGetter by lazy(LazyThreadSafetyMode.PUBLICATION) {
|
||||||
|
hostConfiguration[ScriptingHostConfiguration.getScriptingClass]
|
||||||
|
?: throw IllegalArgumentException("Expecting 'getScriptingClass' property in the scripting environment")
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun getScriptingClass(type: KotlinType) =
|
||||||
|
scriptingClassGetter(
|
||||||
|
type,
|
||||||
|
KotlinScriptDefinition::class, // Assuming that the KotlinScriptDefinition class is loaded in the proper classloader
|
||||||
|
hostConfiguration
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: consider synchronization, since it is mutable (or finish the refactoring after all)
|
||||||
|
internal class BridgeScriptDefinitionDynamicState {
|
||||||
|
|
||||||
|
private val _sources = linkedSetOf<SourceCode>()
|
||||||
|
private val _configurations = hashMapOf<SourceCode, ScriptCompilationConfiguration>()
|
||||||
|
private var _baseScriptCompilationConfiguration: ScriptCompilationConfiguration? = null
|
||||||
|
|
||||||
|
val sources: Set<SourceCode> get() = _sources
|
||||||
|
|
||||||
|
val configurations: Map<SourceCode, ScriptCompilationConfiguration> get() = _configurations
|
||||||
|
|
||||||
|
val baseScriptCompilationConfiguration: ScriptCompilationConfiguration get() = _baseScriptCompilationConfiguration!!
|
||||||
|
|
||||||
|
val mainScript: SourceCode get() = sources.first()
|
||||||
|
|
||||||
|
val mainScriptCompilationConfiguration: ScriptCompilationConfiguration
|
||||||
|
get() = configurations[mainScript] ?: baseScriptCompilationConfiguration
|
||||||
|
|
||||||
|
fun configureFor(script: SourceCode, scriptCompilationConfiguration: ScriptCompilationConfiguration) {
|
||||||
|
_sources.clear()
|
||||||
|
_sources.add(script)
|
||||||
|
_configurations.clear()
|
||||||
|
_baseScriptCompilationConfiguration = scriptCompilationConfiguration
|
||||||
|
}
|
||||||
|
|
||||||
|
fun updateConfiguration(script: SourceCode, updatedConfiguration: ScriptCompilationConfiguration) {
|
||||||
|
_configurations[script] = updatedConfiguration
|
||||||
|
updatedConfiguration[ScriptCompilationConfiguration.importScripts]?.let {
|
||||||
|
_sources.addAll(it)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getScriptSource(scriptContents: ScriptContents): SourceCode? {
|
||||||
|
val name = scriptContents.file?.name
|
||||||
|
return sources.find {
|
||||||
|
// TODO: consider using merged text (likely should be cached)
|
||||||
|
// on the other hand it may become obsolete when scripting internals will be redesigned properly
|
||||||
|
(name != null && name == it.scriptFileName(
|
||||||
|
sources.first(),
|
||||||
|
mainScriptCompilationConfiguration
|
||||||
|
)) || it.text == scriptContents.text
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
+44
-531
@@ -4,575 +4,88 @@
|
|||||||
*/
|
*/
|
||||||
package kotlin.script.experimental.jvmhost.impl
|
package kotlin.script.experimental.jvmhost.impl
|
||||||
|
|
||||||
import com.intellij.openapi.fileTypes.LanguageFileType
|
|
||||||
import com.intellij.openapi.util.Disposer
|
|
||||||
import com.intellij.openapi.util.text.StringUtil
|
|
||||||
import com.intellij.openapi.vfs.CharsetToolkit
|
|
||||||
import com.intellij.psi.PsiFileFactory
|
|
||||||
import com.intellij.psi.impl.PsiFileFactoryImpl
|
|
||||||
import com.intellij.testFramework.LightVirtualFile
|
|
||||||
import org.jetbrains.kotlin.analyzer.AnalysisResult
|
import org.jetbrains.kotlin.analyzer.AnalysisResult
|
||||||
import org.jetbrains.kotlin.cli.common.CLIConfigurationKeys
|
import org.jetbrains.kotlin.cli.common.CLIConfigurationKeys
|
||||||
import org.jetbrains.kotlin.cli.common.arguments.Argument
|
|
||||||
import org.jetbrains.kotlin.cli.common.arguments.K2JVMCompilerArguments
|
|
||||||
import org.jetbrains.kotlin.cli.common.arguments.parseCommandLineArguments
|
|
||||||
import org.jetbrains.kotlin.cli.common.environment.setIdeaIoUseFallback
|
|
||||||
import org.jetbrains.kotlin.cli.common.messages.AnalyzerWithCompilerReport
|
import org.jetbrains.kotlin.cli.common.messages.AnalyzerWithCompilerReport
|
||||||
import org.jetbrains.kotlin.cli.common.messages.CompilerMessageLocation
|
|
||||||
import org.jetbrains.kotlin.cli.common.messages.CompilerMessageSeverity
|
|
||||||
import org.jetbrains.kotlin.cli.common.messages.MessageCollector
|
|
||||||
import org.jetbrains.kotlin.cli.common.setupCommonArguments
|
|
||||||
import org.jetbrains.kotlin.cli.jvm.*
|
|
||||||
import org.jetbrains.kotlin.cli.jvm.compiler.EnvironmentConfigFiles
|
|
||||||
import org.jetbrains.kotlin.cli.jvm.compiler.KotlinCoreEnvironment
|
import org.jetbrains.kotlin.cli.jvm.compiler.KotlinCoreEnvironment
|
||||||
import org.jetbrains.kotlin.cli.jvm.compiler.NoScopeRecordCliBindingTrace
|
import org.jetbrains.kotlin.cli.jvm.compiler.NoScopeRecordCliBindingTrace
|
||||||
import org.jetbrains.kotlin.cli.jvm.compiler.TopDownAnalyzerFacadeForJVM
|
import org.jetbrains.kotlin.cli.jvm.compiler.TopDownAnalyzerFacadeForJVM
|
||||||
import org.jetbrains.kotlin.cli.jvm.config.JvmClasspathRoot
|
|
||||||
import org.jetbrains.kotlin.cli.jvm.config.addJvmClasspathRoots
|
|
||||||
import org.jetbrains.kotlin.cli.jvm.config.jvmClasspathRoots
|
|
||||||
import org.jetbrains.kotlin.codegen.ClassBuilderFactories
|
import org.jetbrains.kotlin.codegen.ClassBuilderFactories
|
||||||
import org.jetbrains.kotlin.codegen.CompilationErrorHandler
|
import org.jetbrains.kotlin.codegen.CompilationErrorHandler
|
||||||
import org.jetbrains.kotlin.codegen.KotlinCodegenFacade
|
import org.jetbrains.kotlin.codegen.KotlinCodegenFacade
|
||||||
import org.jetbrains.kotlin.codegen.state.GenerationState
|
import org.jetbrains.kotlin.codegen.state.GenerationState
|
||||||
import org.jetbrains.kotlin.compiler.plugin.ComponentRegistrar
|
import org.jetbrains.kotlin.config.CompilerConfiguration
|
||||||
import org.jetbrains.kotlin.config.*
|
import org.jetbrains.kotlin.config.languageVersionSettings
|
||||||
import org.jetbrains.kotlin.idea.KotlinFileType
|
|
||||||
import org.jetbrains.kotlin.idea.KotlinLanguage
|
|
||||||
import org.jetbrains.kotlin.name.Name
|
|
||||||
import org.jetbrains.kotlin.name.NameUtils
|
|
||||||
import org.jetbrains.kotlin.psi.KtFile
|
import org.jetbrains.kotlin.psi.KtFile
|
||||||
import org.jetbrains.kotlin.psi.KtScript
|
|
||||||
import org.jetbrains.kotlin.scripting.compiler.plugin.ScriptingCompilerConfigurationComponentRegistrar
|
|
||||||
import org.jetbrains.kotlin.scripting.configuration.ScriptingConfigurationKeys
|
|
||||||
import org.jetbrains.kotlin.scripting.definitions.KotlinScriptDefinition
|
|
||||||
import org.jetbrains.kotlin.scripting.dependencies.ScriptsCompilationDependencies
|
|
||||||
import org.jetbrains.kotlin.scripting.dependencies.collectScriptsCompilationDependencies
|
|
||||||
import org.jetbrains.kotlin.utils.addToStdlib.firstIsInstanceOrNull
|
|
||||||
import java.util.*
|
|
||||||
import kotlin.reflect.KClass
|
|
||||||
import kotlin.reflect.KMutableProperty1
|
|
||||||
import kotlin.reflect.KType
|
|
||||||
import kotlin.reflect.full.findAnnotation
|
|
||||||
import kotlin.reflect.full.starProjectedType
|
|
||||||
import kotlin.script.dependencies.ScriptContents
|
|
||||||
import kotlin.script.experimental.api.*
|
import kotlin.script.experimental.api.*
|
||||||
import kotlin.script.experimental.dependencies.DependenciesResolver
|
|
||||||
import kotlin.script.experimental.host.FileScriptSource
|
|
||||||
import kotlin.script.experimental.host.ScriptingHostConfiguration
|
import kotlin.script.experimental.host.ScriptingHostConfiguration
|
||||||
import kotlin.script.experimental.host.getMergedScriptText
|
|
||||||
import kotlin.script.experimental.host.getScriptingClass
|
|
||||||
import kotlin.script.experimental.jvm.JvmDependency
|
|
||||||
import kotlin.script.experimental.jvm.impl.BridgeDependenciesResolver
|
|
||||||
import kotlin.script.experimental.jvm.impl.KJvmCompiledScript
|
|
||||||
import kotlin.script.experimental.jvm.jdkHome
|
|
||||||
import kotlin.script.experimental.jvm.jvm
|
|
||||||
import kotlin.script.experimental.jvm.util.KotlinJars
|
|
||||||
import kotlin.script.experimental.jvm.withUpdatedClasspath
|
|
||||||
import kotlin.script.experimental.jvmhost.KJvmCompilerProxy
|
import kotlin.script.experimental.jvmhost.KJvmCompilerProxy
|
||||||
import kotlin.script.experimental.util.getOrError
|
|
||||||
|
|
||||||
class KJvmCompilerImpl(val hostConfiguration: ScriptingHostConfiguration) : KJvmCompilerProxy {
|
class KJvmCompilerImpl(val hostConfiguration: ScriptingHostConfiguration) : KJvmCompilerProxy {
|
||||||
|
|
||||||
override fun compile(
|
override fun compile(
|
||||||
script: SourceCode,
|
script: SourceCode,
|
||||||
scriptCompilationConfiguration: ScriptCompilationConfiguration
|
scriptCompilationConfiguration: ScriptCompilationConfiguration
|
||||||
): ResultWithDiagnostics<CompiledScript<*>> {
|
): ResultWithDiagnostics<CompiledScript<*>> =
|
||||||
val messageCollector = ScriptDiagnosticsMessageCollector()
|
withMessageCollectorAndDisposable(locationId = script.locationId) { messageCollector, disposable ->
|
||||||
val reportingState = ReportingState()
|
|
||||||
|
|
||||||
fun failure(vararg diagnostics: ScriptDiagnostic): ResultWithDiagnostics.Failure =
|
val context = createSharedCompilationContext(scriptCompilationConfiguration, hostConfiguration, messageCollector, disposable)
|
||||||
ResultWithDiagnostics.Failure(*messageCollector.diagnostics.toTypedArray(), *diagnostics)
|
|
||||||
|
|
||||||
fun failure(message: String): ResultWithDiagnostics.Failure =
|
val mainKtFile =
|
||||||
failure(message.asErrorDiagnostics(path = script.locationId))
|
getScriptKtFile(script, context.baseScriptCompilationConfiguration, context.environment.project, messageCollector)
|
||||||
|
.resultOr { return it }
|
||||||
|
|
||||||
val disposable = Disposer.newDisposable()
|
context.scriptCompilationState.configureFor(script, context.baseScriptCompilationConfiguration)
|
||||||
|
|
||||||
try {
|
val (sourceFiles, sourceDependencies) = collectRefinedSourcesAndUpdateEnvironment(context, mainKtFile, messageCollector)
|
||||||
setIdeaIoUseFallback()
|
|
||||||
|
|
||||||
// TODO: refactor/cleanup when the internal resolving API will allow easier info passing between resolver and compiler
|
val analysisResult = analyze(sourceFiles, context.environment)
|
||||||
|
|
||||||
val kotlinCompilerConfiguration =
|
if (!analysisResult.shouldGenerateCode) return failure(script, messageCollector, "no code to generate")
|
||||||
createInitialCompilerConfiguration(scriptCompilationConfiguration, messageCollector, reportingState)
|
if (analysisResult.isError() || messageCollector.hasErrors()) return failure(messageCollector)
|
||||||
|
|
||||||
val initialScriptCompilationConfiguration =
|
val generationState = generate(analysisResult, sourceFiles, context.environment.configuration)
|
||||||
scriptCompilationConfiguration.withUpdatesFromCompilerConfiguration(kotlinCompilerConfiguration)
|
|
||||||
|
|
||||||
val sourcesWithRefinementsState = SourcesWithRefinedConfigurations(script)
|
|
||||||
|
|
||||||
kotlinCompilerConfiguration.add(
|
|
||||||
ScriptingConfigurationKeys.SCRIPT_DEFINITIONS,
|
|
||||||
makeScriptDefinition(initialScriptCompilationConfiguration, script, sourcesWithRefinementsState)
|
|
||||||
)
|
|
||||||
|
|
||||||
val environment = KotlinCoreEnvironment.createForProduction(
|
|
||||||
disposable, kotlinCompilerConfiguration, EnvironmentConfigFiles.JVM_CONFIG_FILES
|
|
||||||
)
|
|
||||||
|
|
||||||
val mainKtFile = getMainKtFile(script, initialScriptCompilationConfiguration, environment)
|
|
||||||
?: return failure("Unable to make PSI file from script")
|
|
||||||
val ktScript = mainKtFile.declarations.firstIsInstanceOrNull<KtScript>()
|
|
||||||
?: return failure("Not a script file")
|
|
||||||
|
|
||||||
val sourceFiles = arrayListOf(mainKtFile)
|
|
||||||
val (classpath, newSources, sourceDependencies) =
|
|
||||||
collectScriptsCompilationDependencies(
|
|
||||||
kotlinCompilerConfiguration,
|
|
||||||
environment.project,
|
|
||||||
sourceFiles
|
|
||||||
)
|
|
||||||
|
|
||||||
// TODO: consider removing, it is probably redundant: the actual index update is performed with environment.updateClasspath
|
|
||||||
kotlinCompilerConfiguration.addJvmClasspathRoots(classpath)
|
|
||||||
environment.updateClasspath(classpath.map(::JvmClasspathRoot))
|
|
||||||
|
|
||||||
sourceFiles.addAll(newSources)
|
|
||||||
|
|
||||||
// collectScriptsCompilationDependencies calls resolver for every file, so at this point all updated configurations are collected
|
|
||||||
environment.configuration.updateWithRefinedConfigurations(
|
|
||||||
initialScriptCompilationConfiguration, sourcesWithRefinementsState.refinedConfigurations, messageCollector, reportingState
|
|
||||||
)
|
|
||||||
|
|
||||||
val analysisResult = analyze(sourceFiles, environment)
|
|
||||||
|
|
||||||
if (!analysisResult.shouldGenerateCode) return failure("no code to generate")
|
|
||||||
if (analysisResult.isError() || messageCollector.hasErrors()) return failure()
|
|
||||||
|
|
||||||
val generationState = generate(analysisResult, sourceFiles, kotlinCompilerConfiguration)
|
|
||||||
|
|
||||||
val compiledScript =
|
val compiledScript =
|
||||||
makeCompiledScript(generationState, script, ktScript, sourceDependencies) { ktFile ->
|
makeCompiledScript(generationState, script, sourceFiles.first(), sourceDependencies) { ktFile ->
|
||||||
sourcesWithRefinementsState.refinedConfigurations.entries.find { ktFile.name == it.key.name }?.value
|
context.scriptCompilationState.configurations.entries.find { ktFile.name == it.key.name }?.value
|
||||||
?: initialScriptCompilationConfiguration
|
?: context.baseScriptCompilationConfiguration
|
||||||
}
|
}
|
||||||
|
|
||||||
return ResultWithDiagnostics.Success(compiledScript, messageCollector.diagnostics)
|
ResultWithDiagnostics.Success(compiledScript, messageCollector.diagnostics)
|
||||||
} catch (ex: Throwable) {
|
|
||||||
return failure(ex.asDiagnostics(path = script.locationId))
|
|
||||||
} finally {
|
|
||||||
disposable.dispose()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private class SourcesWithRefinedConfigurations(rootScript: SourceCode) {
|
private fun analyze(sourceFiles: Collection<KtFile>, environment: KotlinCoreEnvironment): AnalysisResult {
|
||||||
val knownSources = hashSetOf(rootScript)
|
val messageCollector = environment.configuration[CLIConfigurationKeys.MESSAGE_COLLECTOR_KEY]!!
|
||||||
val refinedConfigurations = hashMapOf<SourceCode, ScriptCompilationConfiguration>()
|
|
||||||
}
|
|
||||||
|
|
||||||
private class ReportingState {
|
val analyzerWithCompilerReport = AnalyzerWithCompilerReport(messageCollector, environment.configuration.languageVersionSettings)
|
||||||
var currentArguments = K2JVMCompilerArguments()
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun makeScriptDefinition(
|
analyzerWithCompilerReport.analyzeAndReport(sourceFiles) {
|
||||||
scriptCompilationConfiguration: ScriptCompilationConfiguration,
|
val project = environment.project
|
||||||
mainScript: SourceCode,
|
TopDownAnalyzerFacadeForJVM.analyzeFilesWithJavaIntegration(
|
||||||
sourcsesWithConfigurationsState: SourcesWithRefinedConfigurations
|
project,
|
||||||
): BridgeScriptDefinition =
|
sourceFiles,
|
||||||
BridgeScriptDefinition(
|
NoScopeRecordCliBindingTrace(),
|
||||||
scriptCompilationConfiguration,
|
environment.configuration,
|
||||||
hostConfiguration,
|
environment::createPackagePartProvider
|
||||||
{ script, updatedConfiguration ->
|
|
||||||
sourcsesWithConfigurationsState.refinedConfigurations[script] = updatedConfiguration
|
|
||||||
updatedConfiguration[ScriptCompilationConfiguration.importScripts]?.let {
|
|
||||||
sourcsesWithConfigurationsState.knownSources.addAll(it)
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{ scriptContents ->
|
|
||||||
val name = scriptContents.file?.name
|
|
||||||
sourcsesWithConfigurationsState.knownSources.find {
|
|
||||||
// TODO: consider using merged text (likely should be cached)
|
|
||||||
// on the other hand it may become obsolete when scripting internals will be redesigned properly
|
|
||||||
(name != null && name == it.scriptFileName(
|
|
||||||
mainScript,
|
|
||||||
scriptCompilationConfiguration
|
|
||||||
)) || it.text == scriptContents.text
|
|
||||||
}
|
|
||||||
}
|
|
||||||
)
|
)
|
||||||
|
|
||||||
private fun ScriptCompilationConfiguration.withUpdatesFromCompilerConfiguration(kotlinCompilerConfiguration: CompilerConfiguration) =
|
|
||||||
withUpdatedClasspath(kotlinCompilerConfiguration.jvmClasspathRoots)
|
|
||||||
|
|
||||||
private fun createInitialCompilerConfiguration(
|
|
||||||
scriptCompilationConfiguration: ScriptCompilationConfiguration,
|
|
||||||
messageCollector: MessageCollector,
|
|
||||||
reportingState: ReportingState
|
|
||||||
): CompilerConfiguration {
|
|
||||||
|
|
||||||
val baseArguments = K2JVMCompilerArguments()
|
|
||||||
parseCommandLineArguments(
|
|
||||||
scriptCompilationConfiguration[ScriptCompilationConfiguration.compilerOptions] ?: emptyList(),
|
|
||||||
baseArguments
|
|
||||||
)
|
|
||||||
|
|
||||||
reportArgumentsIgnoredGenerally(baseArguments, messageCollector, reportingState)
|
|
||||||
reportingState.currentArguments = baseArguments
|
|
||||||
|
|
||||||
return org.jetbrains.kotlin.config.CompilerConfiguration().apply {
|
|
||||||
put(CLIConfigurationKeys.MESSAGE_COLLECTOR_KEY, messageCollector)
|
|
||||||
setupCommonArguments(baseArguments)
|
|
||||||
|
|
||||||
setupJvmSpecificArguments(baseArguments)
|
|
||||||
|
|
||||||
// Default value differs from the argument's default (see #KT-29405 and #KT-29319)
|
|
||||||
put(JVMConfigurationKeys.JVM_TARGET, JvmTarget.JVM_1_8)
|
|
||||||
|
|
||||||
val jdkHomeFromConfigurations = scriptCompilationConfiguration.getNoDefault(ScriptCompilationConfiguration.jvm.jdkHome)
|
|
||||||
?: hostConfiguration[ScriptingHostConfiguration.jvm.jdkHome]
|
|
||||||
if (jdkHomeFromConfigurations != null) {
|
|
||||||
messageCollector.report(CompilerMessageSeverity.LOGGING, "Using JDK home directory $jdkHomeFromConfigurations")
|
|
||||||
put(JVMConfigurationKeys.JDK_HOME, jdkHomeFromConfigurations)
|
|
||||||
} else {
|
|
||||||
configureJdkHome(baseArguments)
|
|
||||||
}
|
|
||||||
|
|
||||||
put(JVMConfigurationKeys.RETAIN_OUTPUT_IN_MEMORY, true)
|
|
||||||
|
|
||||||
val isModularJava = isModularJava()
|
|
||||||
|
|
||||||
scriptCompilationConfiguration[ScriptCompilationConfiguration.dependencies]?.let { dependencies ->
|
|
||||||
addJvmClasspathRoots(
|
|
||||||
dependencies.flatMap {
|
|
||||||
(it as JvmDependency).classpath
|
|
||||||
}
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
add(ComponentRegistrar.PLUGIN_COMPONENT_REGISTRARS, ScriptingCompilerConfigurationComponentRegistrar())
|
|
||||||
|
|
||||||
configureExplicitContentRoots(baseArguments)
|
|
||||||
|
|
||||||
if (!baseArguments.noStdlib) {
|
|
||||||
addModularRootIfNotNull(isModularJava, "kotlin.stdlib", KotlinJars.stdlib)
|
|
||||||
addModularRootIfNotNull(isModularJava, "kotlin.script.runtime", KotlinJars.scriptRuntimeOrNull)
|
|
||||||
}
|
|
||||||
// see comments about logic in CompilerConfiguration.configureStandardLibs
|
|
||||||
if (!baseArguments.noReflect && !baseArguments.noStdlib) {
|
|
||||||
addModularRootIfNotNull(isModularJava, "kotlin.reflect", KotlinJars.reflectOrNull)
|
|
||||||
}
|
|
||||||
|
|
||||||
put(CommonConfigurationKeys.MODULE_NAME, baseArguments.moduleName ?: "kotlin-script")
|
|
||||||
|
|
||||||
configureAdvancedJvmOptions(baseArguments)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
companion object {
|
|
||||||
|
|
||||||
private fun SourceCode.scriptFileName(
|
|
||||||
mainScript: SourceCode,
|
|
||||||
scriptCompilationConfiguration: ScriptCompilationConfiguration
|
|
||||||
): String =
|
|
||||||
when {
|
|
||||||
name != null -> name!!
|
|
||||||
mainScript == this -> "script.${scriptCompilationConfiguration[ScriptCompilationConfiguration.fileExtension]}"
|
|
||||||
else -> throw Exception("Unexpected script without name: $this")
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun getMainKtFile(
|
|
||||||
mainScript: SourceCode,
|
|
||||||
scriptCompilationConfiguration: ScriptCompilationConfiguration,
|
|
||||||
environment: KotlinCoreEnvironment
|
|
||||||
): KtFile? {
|
|
||||||
val psiFileFactory: PsiFileFactoryImpl = PsiFileFactory.getInstance(environment.project) as PsiFileFactoryImpl
|
|
||||||
val scriptText = getMergedScriptText(mainScript, scriptCompilationConfiguration)
|
|
||||||
val virtualFile = ScriptLightVirtualFile(
|
|
||||||
mainScript.scriptFileName(mainScript, scriptCompilationConfiguration),
|
|
||||||
(mainScript as? FileScriptSource)?.file?.path,
|
|
||||||
scriptText
|
|
||||||
)
|
|
||||||
return psiFileFactory.trySetupPsiForFile(virtualFile, KotlinLanguage.INSTANCE, true, false) as KtFile?
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun CompilerConfiguration.updateWithRefinedConfigurations(
|
|
||||||
initialScriptCompilationConfiguration: ScriptCompilationConfiguration,
|
|
||||||
refinedScriptCompilationConfigurations: HashMap<SourceCode, ScriptCompilationConfiguration>,
|
|
||||||
messageCollector: ScriptDiagnosticsMessageCollector,
|
|
||||||
reportingState: ReportingState
|
|
||||||
) {
|
|
||||||
val updatedCompilerOptions = refinedScriptCompilationConfigurations.flatMap {
|
|
||||||
it.value[ScriptCompilationConfiguration.compilerOptions] ?: emptyList()
|
|
||||||
}
|
|
||||||
if (updatedCompilerOptions.isNotEmpty() &&
|
|
||||||
updatedCompilerOptions != initialScriptCompilationConfiguration[ScriptCompilationConfiguration.compilerOptions]
|
|
||||||
) {
|
|
||||||
|
|
||||||
val updatedArguments = K2JVMCompilerArguments()
|
|
||||||
parseCommandLineArguments(updatedCompilerOptions, updatedArguments)
|
|
||||||
|
|
||||||
reportArgumentsIgnoredGenerally(updatedArguments, messageCollector, reportingState)
|
|
||||||
reportArgumentsIgnoredFromRefinement(updatedArguments, messageCollector, reportingState)
|
|
||||||
|
|
||||||
setupCommonArguments(updatedArguments)
|
|
||||||
|
|
||||||
setupJvmSpecificArguments(updatedArguments)
|
|
||||||
|
|
||||||
configureAdvancedJvmOptions(updatedArguments)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
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
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun makeCompiledScript(
|
|
||||||
generationState: GenerationState,
|
|
||||||
script: SourceCode,
|
|
||||||
ktScript: KtScript,
|
|
||||||
sourceDependencies: List<ScriptsCompilationDependencies.SourceDependencies>,
|
|
||||||
getScriptConfiguration: (KtFile) -> ScriptCompilationConfiguration
|
|
||||||
): KJvmCompiledScript<Any> {
|
|
||||||
val scriptDependenciesStack = ArrayDeque<KtScript>()
|
|
||||||
|
|
||||||
fun makeOtherScripts(script: KtScript): List<KJvmCompiledScript<*>> {
|
|
||||||
|
|
||||||
// TODO: ensure that it is caught earlier (as well) since it would be more economical
|
|
||||||
if (scriptDependenciesStack.contains(script))
|
|
||||||
throw IllegalArgumentException("Unable to handle recursive script dependencies")
|
|
||||||
scriptDependenciesStack.push(script)
|
|
||||||
|
|
||||||
val containingKtFile = script.containingKtFile
|
|
||||||
val otherScripts: List<KJvmCompiledScript<*>> =
|
|
||||||
sourceDependencies.find { it.scriptFile == containingKtFile }?.sourceDependencies?.mapNotNull { sourceFile ->
|
|
||||||
sourceFile.declarations.firstIsInstanceOrNull<KtScript>()?.let {
|
|
||||||
KJvmCompiledScript<Any>(
|
|
||||||
containingKtFile.virtualFile?.path,
|
|
||||||
getScriptConfiguration(sourceFile),
|
|
||||||
it.fqName.asString(),
|
|
||||||
makeOtherScripts(it),
|
|
||||||
null
|
|
||||||
)
|
|
||||||
}
|
|
||||||
} ?: emptyList()
|
|
||||||
|
|
||||||
scriptDependenciesStack.pop()
|
|
||||||
return otherScripts
|
|
||||||
}
|
|
||||||
|
|
||||||
val module = makeCompiledModule(generationState)
|
|
||||||
return KJvmCompiledScript(
|
|
||||||
script.locationId,
|
|
||||||
getScriptConfiguration(ktScript.containingKtFile),
|
|
||||||
ktScript.fqName.asString(),
|
|
||||||
makeOtherScripts(ktScript),
|
|
||||||
module
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun reportArgumentsIgnoredGenerally(
|
|
||||||
arguments: K2JVMCompilerArguments,
|
|
||||||
messageCollector: MessageCollector,
|
|
||||||
reportingState: ReportingState
|
|
||||||
) {
|
|
||||||
|
|
||||||
reportIgnoredArguments(
|
|
||||||
arguments,
|
|
||||||
"The following compiler arguments are ignored on script compilation: ",
|
|
||||||
messageCollector,
|
|
||||||
reportingState,
|
|
||||||
K2JVMCompilerArguments::version,
|
|
||||||
K2JVMCompilerArguments::destination,
|
|
||||||
K2JVMCompilerArguments::buildFile,
|
|
||||||
K2JVMCompilerArguments::commonSources,
|
|
||||||
K2JVMCompilerArguments::allWarningsAsErrors,
|
|
||||||
K2JVMCompilerArguments::script,
|
|
||||||
K2JVMCompilerArguments::scriptTemplates,
|
|
||||||
K2JVMCompilerArguments::scriptResolverEnvironment,
|
|
||||||
K2JVMCompilerArguments::disableStandardScript,
|
|
||||||
K2JVMCompilerArguments::disableDefaultScriptingPlugin,
|
|
||||||
K2JVMCompilerArguments::pluginClasspaths,
|
|
||||||
K2JVMCompilerArguments::pluginOptions,
|
|
||||||
K2JVMCompilerArguments::useJavac,
|
|
||||||
K2JVMCompilerArguments::compileJava,
|
|
||||||
K2JVMCompilerArguments::reportPerf,
|
|
||||||
K2JVMCompilerArguments::dumpPerf
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun reportArgumentsIgnoredFromRefinement(
|
|
||||||
arguments: K2JVMCompilerArguments, messageCollector: MessageCollector, reportingState: ReportingState
|
|
||||||
) {
|
|
||||||
reportIgnoredArguments(
|
|
||||||
arguments,
|
|
||||||
"The following compiler arguments are ignored when configured from refinement callbacks: ",
|
|
||||||
messageCollector,
|
|
||||||
reportingState,
|
|
||||||
K2JVMCompilerArguments::noJdk,
|
|
||||||
K2JVMCompilerArguments::jdkHome,
|
|
||||||
K2JVMCompilerArguments::javaModulePath,
|
|
||||||
K2JVMCompilerArguments::classpath,
|
|
||||||
K2JVMCompilerArguments::noStdlib,
|
|
||||||
K2JVMCompilerArguments::noReflect
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun reportIgnoredArguments(
|
|
||||||
arguments: K2JVMCompilerArguments, message: String,
|
|
||||||
messageCollector: MessageCollector, reportingState: ReportingState,
|
|
||||||
vararg toIgnore: KMutableProperty1<K2JVMCompilerArguments, *>
|
|
||||||
) {
|
|
||||||
val ignoredArgKeys = toIgnore.mapNotNull { argProperty ->
|
|
||||||
if (argProperty.get(arguments) != argProperty.get(reportingState.currentArguments)) {
|
|
||||||
argProperty.findAnnotation<Argument>()?.value
|
|
||||||
?: throw IllegalStateException("unknown compiler argument property: $argProperty: no Argument annotation found")
|
|
||||||
} else null
|
|
||||||
}
|
|
||||||
|
|
||||||
if (ignoredArgKeys.isNotEmpty()) {
|
|
||||||
messageCollector.report(CompilerMessageSeverity.STRONG_WARNING, "$message${ignoredArgKeys.joinToString(", ")}")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
return analyzerWithCompilerReport.analysisResult
|
||||||
}
|
}
|
||||||
|
|
||||||
internal class ScriptDiagnosticsMessageCollector : MessageCollector {
|
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()
|
||||||
|
|
||||||
private val _diagnostics = arrayListOf<ScriptDiagnostic>()
|
KotlinCodegenFacade.compileCorrectFiles(generationState, CompilationErrorHandler.THROW_EXCEPTION)
|
||||||
|
return generationState
|
||||||
val diagnostics: List<ScriptDiagnostic> get() = _diagnostics
|
|
||||||
|
|
||||||
override fun clear() {
|
|
||||||
_diagnostics.clear()
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun hasErrors(): Boolean =
|
|
||||||
_diagnostics.any { it.severity == ScriptDiagnostic.Severity.ERROR }
|
|
||||||
|
|
||||||
|
|
||||||
override fun report(severity: CompilerMessageSeverity, message: String, location: CompilerMessageLocation?) {
|
|
||||||
val mappedSeverity = when (severity) {
|
|
||||||
CompilerMessageSeverity.EXCEPTION,
|
|
||||||
CompilerMessageSeverity.ERROR -> ScriptDiagnostic.Severity.ERROR
|
|
||||||
CompilerMessageSeverity.STRONG_WARNING,
|
|
||||||
CompilerMessageSeverity.WARNING -> ScriptDiagnostic.Severity.WARNING
|
|
||||||
CompilerMessageSeverity.INFO -> ScriptDiagnostic.Severity.INFO
|
|
||||||
CompilerMessageSeverity.LOGGING -> ScriptDiagnostic.Severity.DEBUG
|
|
||||||
else -> null
|
|
||||||
}
|
|
||||||
if (mappedSeverity != null) {
|
|
||||||
val mappedLocation = location?.let {
|
|
||||||
if (it.line < 0 && it.column < 0) null // special location created by CompilerMessageLocation.create
|
|
||||||
else SourceCode.Location(SourceCode.Position(it.line, it.column))
|
|
||||||
}
|
|
||||||
_diagnostics.add(ScriptDiagnostic(message, mappedSeverity, location?.path, mappedLocation))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// A bridge to the current scripting
|
|
||||||
// mostly copies functionality from KotlinScriptDefinitionAdapterFromNewAPI[Base]
|
|
||||||
// reusing it requires structural changes that doesn't seem justified now, since the internals of the scripting should be reworked soon anyway
|
|
||||||
// TODO: either finish refactoring of the scripting internals or reuse KotlinScriptDefinitionAdapterFromNewAPI[BAse] here
|
|
||||||
internal class BridgeScriptDefinition(
|
|
||||||
val scriptCompilationConfiguration: ScriptCompilationConfiguration,
|
|
||||||
val hostConfiguration: ScriptingHostConfiguration,
|
|
||||||
updateConfiguration: (SourceCode, ScriptCompilationConfiguration) -> Unit,
|
|
||||||
getScriptSource: (ScriptContents) -> SourceCode?
|
|
||||||
) : KotlinScriptDefinition(Any::class) {
|
|
||||||
|
|
||||||
val baseClass: KClass<*> by lazy(LazyThreadSafetyMode.PUBLICATION) {
|
|
||||||
getScriptingClass(scriptCompilationConfiguration.getOrError(ScriptCompilationConfiguration.baseClass))
|
|
||||||
}
|
|
||||||
|
|
||||||
override val template: KClass<*> get() = baseClass
|
|
||||||
|
|
||||||
override val name: String
|
|
||||||
get() = scriptCompilationConfiguration[ScriptCompilationConfiguration.displayName] ?: "Kotlin Script"
|
|
||||||
|
|
||||||
override val fileType: LanguageFileType = KotlinFileType.INSTANCE
|
|
||||||
|
|
||||||
override fun isScript(fileName: String): Boolean =
|
|
||||||
fileName.endsWith(".$fileExtension")
|
|
||||||
|
|
||||||
override fun getScriptName(script: KtScript): Name {
|
|
||||||
val fileBasedName = NameUtils.getScriptNameForFile(script.containingKtFile.name)
|
|
||||||
return Name.identifier(fileBasedName.identifier.removeSuffix(".$fileExtension"))
|
|
||||||
}
|
|
||||||
|
|
||||||
override val fileExtension: String
|
|
||||||
get() = scriptCompilationConfiguration[ScriptCompilationConfiguration.fileExtension] ?: super.fileExtension
|
|
||||||
|
|
||||||
override val acceptedAnnotations = run {
|
|
||||||
val cl = this::class.java.classLoader
|
|
||||||
scriptCompilationConfiguration[ScriptCompilationConfiguration.refineConfigurationOnAnnotations]?.annotations
|
|
||||||
?.map { (cl.loadClass(it.typeName) as Class<out Annotation>).kotlin }
|
|
||||||
?: emptyList()
|
|
||||||
}
|
|
||||||
|
|
||||||
override val implicitReceivers: List<KType> by lazy(LazyThreadSafetyMode.PUBLICATION) {
|
|
||||||
scriptCompilationConfiguration[ScriptCompilationConfiguration.implicitReceivers]
|
|
||||||
.orEmpty()
|
|
||||||
.map { getScriptingClass(it).starProjectedType }
|
|
||||||
}
|
|
||||||
|
|
||||||
override val providedProperties: List<Pair<String, KType>> by lazy(LazyThreadSafetyMode.PUBLICATION) {
|
|
||||||
scriptCompilationConfiguration[ScriptCompilationConfiguration.providedProperties]
|
|
||||||
?.map { (k, v) -> k to getScriptingClass(v).starProjectedType }.orEmpty()
|
|
||||||
}
|
|
||||||
|
|
||||||
override val additionalCompilerArguments: List<String>
|
|
||||||
get() = scriptCompilationConfiguration[ScriptCompilationConfiguration.compilerOptions]
|
|
||||||
.orEmpty()
|
|
||||||
|
|
||||||
override val dependencyResolver: DependenciesResolver =
|
|
||||||
BridgeDependenciesResolver(scriptCompilationConfiguration, updateConfiguration, getScriptSource)
|
|
||||||
|
|
||||||
private val scriptingClassGetter by lazy(LazyThreadSafetyMode.PUBLICATION) {
|
|
||||||
hostConfiguration[ScriptingHostConfiguration.getScriptingClass]
|
|
||||||
?: throw IllegalArgumentException("Expecting 'getScriptingClass' property in the scripting environment")
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun getScriptingClass(type: KotlinType) =
|
|
||||||
scriptingClassGetter(
|
|
||||||
type,
|
|
||||||
KotlinScriptDefinition::class, // Assuming that the KotlinScriptDefinition class is loaded in the proper classloader
|
|
||||||
hostConfiguration
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
internal class ScriptLightVirtualFile(name: String, private val _path: String?, text: String) :
|
|
||||||
LightVirtualFile(name, KotlinLanguage.INSTANCE, StringUtil.convertLineSeparators(text)) {
|
|
||||||
|
|
||||||
init {
|
|
||||||
charset = CharsetToolkit.UTF8_CHARSET
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun getPath(): String = _path ?: super.getPath()
|
|
||||||
override fun getCanonicalPath(): String? = path
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun makeCompiledModule(generationState: GenerationState) = KJvmCompiledModuleInMemory(
|
|
||||||
generationState.factory.asList()
|
|
||||||
.associateTo(sortedMapOf<String, ByteArray>()) { it.relativePath to it.asByteArray() }
|
|
||||||
)
|
|
||||||
|
|||||||
+136
@@ -0,0 +1,136 @@
|
|||||||
|
/*
|
||||||
|
* 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.impl
|
||||||
|
|
||||||
|
import com.intellij.openapi.Disposable
|
||||||
|
import com.intellij.openapi.project.Project
|
||||||
|
import org.jetbrains.kotlin.cli.common.environment.setIdeaIoUseFallback
|
||||||
|
import org.jetbrains.kotlin.cli.common.messages.AnalyzerWithCompilerReport
|
||||||
|
import org.jetbrains.kotlin.cli.common.messages.MessageCollectorBasedReporter
|
||||||
|
import org.jetbrains.kotlin.cli.common.repl.*
|
||||||
|
import org.jetbrains.kotlin.cli.jvm.compiler.KotlinCoreEnvironment
|
||||||
|
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.descriptors.ScriptDescriptor
|
||||||
|
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()
|
||||||
|
}.resultOr { throw IllegalStateException("Unable to initialize repl compiler:\n ${it.reports.joinToString("\n ")}") }
|
||||||
|
return ReplCompilationState(context)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun checkSyntax(
|
||||||
|
script: SourceCode,
|
||||||
|
scriptCompilationConfiguration: ScriptCompilationConfiguration,
|
||||||
|
project: Project
|
||||||
|
): ResultWithDiagnostics<Boolean> =
|
||||||
|
withMessageCollector(script) { messageCollector ->
|
||||||
|
val ktFile = getScriptKtFile(script, scriptCompilationConfiguration, project, messageCollector)
|
||||||
|
.resultOr { return it }
|
||||||
|
val errorHolder = object : MessageCollectorBasedReporter {
|
||||||
|
override val messageCollector = messageCollector
|
||||||
|
}
|
||||||
|
val syntaxErrorReport = AnalyzerWithCompilerReport.reportSyntaxErrors(ktFile, errorHolder)
|
||||||
|
if (syntaxErrorReport.isHasErrors) failure(messageCollector) else true.asSuccess()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun compileReplSnippet(
|
||||||
|
compilationState: JvmReplCompilerState.Compilation,
|
||||||
|
snippet: SourceCode,
|
||||||
|
snippetId: ReplSnippetId,
|
||||||
|
history: IReplStageHistory<ScriptDescriptor>
|
||||||
|
): ResultWithDiagnostics<CompiledScript<*>> =
|
||||||
|
withMessageCollector(snippet) { messageCollector ->
|
||||||
|
|
||||||
|
val context = (compilationState as? ReplCompilationState)?.context
|
||||||
|
?: return failure(
|
||||||
|
snippet, messageCollector, "Internal error: unknown parameter passed as compilationState: $compilationState"
|
||||||
|
)
|
||||||
|
|
||||||
|
setIdeaIoUseFallback()
|
||||||
|
|
||||||
|
// NOTE: converting between REPL entities from compiler and "new" scripting entities
|
||||||
|
// TODO: (big) move REPL API from compiler to the new scripting infrastructure and streamline ops
|
||||||
|
val codeLine = makeReplCodeLine(snippetId, snippet)
|
||||||
|
|
||||||
|
val errorHolder = object : MessageCollectorBasedReporter {
|
||||||
|
override val messageCollector = messageCollector
|
||||||
|
}
|
||||||
|
|
||||||
|
val snippetKtFile =
|
||||||
|
getScriptKtFile(snippet, context.baseScriptCompilationConfiguration, context.environment.project, messageCollector)
|
||||||
|
.resultOr { return it }
|
||||||
|
|
||||||
|
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.Successful -> analysisResult.scriptDescriptor
|
||||||
|
else -> return failure(snippet, messageCollector, "Unexpected result ${analysisResult::class.java}")
|
||||||
|
}
|
||||||
|
|
||||||
|
val type = (scriptDescriptor as ScriptDescriptor).resultValue?.returnType
|
||||||
|
|
||||||
|
val generationState = GenerationState.Builder(
|
||||||
|
snippetKtFile.project,
|
||||||
|
ClassBuilderFactories.BINARIES,
|
||||||
|
compilationState.analyzerEngine.module,
|
||||||
|
compilationState.analyzerEngine.trace.bindingContext,
|
||||||
|
sourceFiles,
|
||||||
|
compilationState.environment.configuration
|
||||||
|
).build().apply {
|
||||||
|
replSpecific.resultType = type
|
||||||
|
replSpecific.scriptResultFieldName = scriptResultFieldName(codeLine.no)
|
||||||
|
replSpecific.earlierScriptsForReplInterpreter = history.map { it.item }
|
||||||
|
beforeCompile()
|
||||||
|
}
|
||||||
|
KotlinCodegenFacade.generatePackage(
|
||||||
|
generationState,
|
||||||
|
snippetKtFile.script!!.containingKtFile.packageFqName,
|
||||||
|
setOf(snippetKtFile.script!!.containingKtFile),
|
||||||
|
CompilationErrorHandler.THROW_EXCEPTION
|
||||||
|
)
|
||||||
|
|
||||||
|
val generatedClassname = makeScriptBaseName(codeLine)
|
||||||
|
history.push(LineId(codeLine), scriptDescriptor)
|
||||||
|
|
||||||
|
val compiledScript =
|
||||||
|
makeCompiledScript(generationState, snippet, sourceFiles.first(), sourceDependencies) { ktFile ->
|
||||||
|
context.scriptCompilationState.configurations.entries.find { ktFile.name == it.key.name }?.value
|
||||||
|
?: context.baseScriptCompilationConfiguration
|
||||||
|
}
|
||||||
|
|
||||||
|
ResultWithDiagnostics.Success(compiledScript, messageCollector.diagnostics)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
internal class ReplCompilationState(val context: SharedScriptCompilationContext) : JvmReplCompilerState.Compilation {
|
||||||
|
override val disposable: Disposable get() = context.disposable
|
||||||
|
override val baseScriptCompilationConfiguration: ScriptCompilationConfiguration get() = context.baseScriptCompilationConfiguration
|
||||||
|
override val environment: KotlinCoreEnvironment get() = context.environment
|
||||||
|
override val analyzerEngine: ReplCodeAnalyzer by lazy {
|
||||||
|
ReplCodeAnalyzer(context.environment)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
internal fun makeReplCodeLine(id: ReplSnippetId, code: SourceCode): ReplCodeLine =
|
||||||
|
ReplCodeLine(id.no, id.generation, code.text)
|
||||||
|
|
||||||
|
|
||||||
+223
@@ -0,0 +1,223 @@
|
|||||||
|
/*
|
||||||
|
* 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.impl
|
||||||
|
|
||||||
|
import com.intellij.openapi.Disposable
|
||||||
|
import org.jetbrains.kotlin.cli.common.CLIConfigurationKeys
|
||||||
|
import org.jetbrains.kotlin.cli.common.arguments.K2JVMCompilerArguments
|
||||||
|
import org.jetbrains.kotlin.cli.common.arguments.parseCommandLineArguments
|
||||||
|
import org.jetbrains.kotlin.cli.common.messages.CompilerMessageSeverity
|
||||||
|
import org.jetbrains.kotlin.cli.common.messages.MessageCollector
|
||||||
|
import org.jetbrains.kotlin.cli.common.setupCommonArguments
|
||||||
|
import org.jetbrains.kotlin.cli.jvm.*
|
||||||
|
import org.jetbrains.kotlin.cli.jvm.compiler.EnvironmentConfigFiles
|
||||||
|
import org.jetbrains.kotlin.cli.jvm.compiler.KotlinCoreEnvironment
|
||||||
|
import org.jetbrains.kotlin.cli.jvm.config.JvmClasspathRoot
|
||||||
|
import org.jetbrains.kotlin.cli.jvm.config.addJvmClasspathRoots
|
||||||
|
import org.jetbrains.kotlin.cli.jvm.config.jvmClasspathRoots
|
||||||
|
import org.jetbrains.kotlin.compiler.plugin.ComponentRegistrar
|
||||||
|
import org.jetbrains.kotlin.config.CommonConfigurationKeys
|
||||||
|
import org.jetbrains.kotlin.config.CompilerConfiguration
|
||||||
|
import org.jetbrains.kotlin.config.JVMConfigurationKeys
|
||||||
|
import org.jetbrains.kotlin.config.JvmTarget
|
||||||
|
import org.jetbrains.kotlin.psi.KtFile
|
||||||
|
import org.jetbrains.kotlin.scripting.compiler.plugin.ScriptingCompilerConfigurationComponentRegistrar
|
||||||
|
import org.jetbrains.kotlin.scripting.configuration.ScriptingConfigurationKeys
|
||||||
|
import org.jetbrains.kotlin.scripting.dependencies.ScriptsCompilationDependencies
|
||||||
|
import org.jetbrains.kotlin.scripting.dependencies.collectScriptsCompilationDependencies
|
||||||
|
import kotlin.script.experimental.api.ScriptCompilationConfiguration
|
||||||
|
import kotlin.script.experimental.api.SourceCode
|
||||||
|
import kotlin.script.experimental.api.compilerOptions
|
||||||
|
import kotlin.script.experimental.api.dependencies
|
||||||
|
import kotlin.script.experimental.host.ScriptingHostConfiguration
|
||||||
|
import kotlin.script.experimental.jvm.JvmDependency
|
||||||
|
import kotlin.script.experimental.jvm.jdkHome
|
||||||
|
import kotlin.script.experimental.jvm.jvm
|
||||||
|
import kotlin.script.experimental.jvm.util.KotlinJars
|
||||||
|
import kotlin.script.experimental.jvm.withUpdatedClasspath
|
||||||
|
|
||||||
|
internal class SharedScriptCompilationContext(
|
||||||
|
val disposable: Disposable,
|
||||||
|
val baseScriptCompilationConfiguration: ScriptCompilationConfiguration,
|
||||||
|
val environment: KotlinCoreEnvironment,
|
||||||
|
val ignoredOptionsReportingState: IgnoredOptionsReportingState,
|
||||||
|
val scriptCompilationState: BridgeScriptDefinitionDynamicState
|
||||||
|
)
|
||||||
|
|
||||||
|
internal fun createSharedCompilationContext(
|
||||||
|
scriptCompilationConfiguration: ScriptCompilationConfiguration,
|
||||||
|
hostConfiguration: ScriptingHostConfiguration,
|
||||||
|
messageCollector: ScriptDiagnosticsMessageCollector,
|
||||||
|
disposable: Disposable
|
||||||
|
): SharedScriptCompilationContext {
|
||||||
|
val ignoredOptionsReportingState = IgnoredOptionsReportingState()
|
||||||
|
|
||||||
|
val scriptCompilationState = BridgeScriptDefinitionDynamicState()
|
||||||
|
|
||||||
|
val (initialScriptCompilationConfiguration, kotlinCompilerConfiguration) =
|
||||||
|
createInitialConfigurations(
|
||||||
|
scriptCompilationConfiguration, hostConfiguration, scriptCompilationState, messageCollector, ignoredOptionsReportingState
|
||||||
|
)
|
||||||
|
val environment = KotlinCoreEnvironment.createForProduction(
|
||||||
|
disposable, kotlinCompilerConfiguration, EnvironmentConfigFiles.JVM_CONFIG_FILES
|
||||||
|
)
|
||||||
|
|
||||||
|
return SharedScriptCompilationContext(
|
||||||
|
disposable, initialScriptCompilationConfiguration, environment, ignoredOptionsReportingState, scriptCompilationState
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
internal fun createInitialConfigurations(
|
||||||
|
scriptCompilationConfiguration: ScriptCompilationConfiguration,
|
||||||
|
hostConfiguration: ScriptingHostConfiguration,
|
||||||
|
scriptCompilationState: BridgeScriptDefinitionDynamicState,
|
||||||
|
messageCollector: ScriptDiagnosticsMessageCollector,
|
||||||
|
ignoredOptionsReportingState: IgnoredOptionsReportingState
|
||||||
|
): Pair<ScriptCompilationConfiguration, CompilerConfiguration> {
|
||||||
|
val kotlinCompilerConfiguration =
|
||||||
|
createInitialCompilerConfiguration(
|
||||||
|
scriptCompilationConfiguration, hostConfiguration, messageCollector, ignoredOptionsReportingState
|
||||||
|
)
|
||||||
|
|
||||||
|
val initialScriptCompilationConfiguration =
|
||||||
|
scriptCompilationConfiguration.withUpdatesFromCompilerConfiguration(kotlinCompilerConfiguration)
|
||||||
|
|
||||||
|
kotlinCompilerConfiguration.add(
|
||||||
|
ScriptingConfigurationKeys.SCRIPT_DEFINITIONS,
|
||||||
|
BridgeScriptDefinition(
|
||||||
|
scriptCompilationConfiguration,
|
||||||
|
hostConfiguration,
|
||||||
|
scriptCompilationState
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
return Pair(initialScriptCompilationConfiguration, kotlinCompilerConfiguration)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun ScriptCompilationConfiguration.withUpdatesFromCompilerConfiguration(kotlinCompilerConfiguration: CompilerConfiguration) =
|
||||||
|
withUpdatedClasspath(kotlinCompilerConfiguration.jvmClasspathRoots)
|
||||||
|
|
||||||
|
private fun createInitialCompilerConfiguration(
|
||||||
|
scriptCompilationConfiguration: ScriptCompilationConfiguration,
|
||||||
|
hostConfiguration: ScriptingHostConfiguration,
|
||||||
|
messageCollector: MessageCollector,
|
||||||
|
reportingState: IgnoredOptionsReportingState
|
||||||
|
): CompilerConfiguration {
|
||||||
|
|
||||||
|
val baseArguments = K2JVMCompilerArguments()
|
||||||
|
parseCommandLineArguments(
|
||||||
|
scriptCompilationConfiguration[ScriptCompilationConfiguration.compilerOptions] ?: emptyList(),
|
||||||
|
baseArguments
|
||||||
|
)
|
||||||
|
|
||||||
|
reportArgumentsIgnoredGenerally(baseArguments, messageCollector, reportingState)
|
||||||
|
reportingState.currentArguments = baseArguments
|
||||||
|
|
||||||
|
return CompilerConfiguration().apply {
|
||||||
|
put(CLIConfigurationKeys.MESSAGE_COLLECTOR_KEY, messageCollector)
|
||||||
|
setupCommonArguments(baseArguments)
|
||||||
|
|
||||||
|
setupJvmSpecificArguments(baseArguments)
|
||||||
|
|
||||||
|
// Default value differs from the argument's default (see #KT-29405 and #KT-29319)
|
||||||
|
put(JVMConfigurationKeys.JVM_TARGET, JvmTarget.JVM_1_8)
|
||||||
|
|
||||||
|
val jdkHomeFromConfigurations = scriptCompilationConfiguration.getNoDefault(ScriptCompilationConfiguration.jvm.jdkHome)
|
||||||
|
?: hostConfiguration[ScriptingHostConfiguration.jvm.jdkHome]
|
||||||
|
if (jdkHomeFromConfigurations != null) {
|
||||||
|
messageCollector.report(CompilerMessageSeverity.LOGGING, "Using JDK home directory $jdkHomeFromConfigurations")
|
||||||
|
put(JVMConfigurationKeys.JDK_HOME, jdkHomeFromConfigurations)
|
||||||
|
} else {
|
||||||
|
configureJdkHome(baseArguments)
|
||||||
|
}
|
||||||
|
|
||||||
|
put(JVMConfigurationKeys.RETAIN_OUTPUT_IN_MEMORY, true)
|
||||||
|
|
||||||
|
val isModularJava = isModularJava()
|
||||||
|
|
||||||
|
scriptCompilationConfiguration[ScriptCompilationConfiguration.dependencies]?.let { dependencies ->
|
||||||
|
addJvmClasspathRoots(
|
||||||
|
dependencies.flatMap {
|
||||||
|
(it as JvmDependency).classpath
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
add(
|
||||||
|
ComponentRegistrar.PLUGIN_COMPONENT_REGISTRARS,
|
||||||
|
ScriptingCompilerConfigurationComponentRegistrar()
|
||||||
|
)
|
||||||
|
|
||||||
|
configureExplicitContentRoots(baseArguments)
|
||||||
|
|
||||||
|
if (!baseArguments.noStdlib) {
|
||||||
|
addModularRootIfNotNull(isModularJava, "kotlin.stdlib", KotlinJars.stdlib)
|
||||||
|
addModularRootIfNotNull(isModularJava, "kotlin.script.runtime", KotlinJars.scriptRuntimeOrNull)
|
||||||
|
}
|
||||||
|
// see comments about logic in CompilerConfiguration.configureStandardLibs
|
||||||
|
if (!baseArguments.noReflect && !baseArguments.noStdlib) {
|
||||||
|
addModularRootIfNotNull(isModularJava, "kotlin.reflect", KotlinJars.reflectOrNull)
|
||||||
|
}
|
||||||
|
|
||||||
|
put(CommonConfigurationKeys.MODULE_NAME, baseArguments.moduleName ?: "kotlin-script")
|
||||||
|
|
||||||
|
configureAdvancedJvmOptions(baseArguments)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
internal fun collectRefinedSourcesAndUpdateEnvironment(
|
||||||
|
context: SharedScriptCompilationContext,
|
||||||
|
mainKtFile: KtFile,
|
||||||
|
messageCollector: ScriptDiagnosticsMessageCollector
|
||||||
|
): Pair<List<KtFile>, List<ScriptsCompilationDependencies.SourceDependencies>> {
|
||||||
|
val sourceFiles = arrayListOf(mainKtFile)
|
||||||
|
val (classpath, newSources, sourceDependencies) =
|
||||||
|
collectScriptsCompilationDependencies(
|
||||||
|
context.environment.configuration,
|
||||||
|
context.environment.project,
|
||||||
|
sourceFiles
|
||||||
|
)
|
||||||
|
|
||||||
|
// TODO: consider removing, it is probably redundant: the actual index update is performed with environment.updateClasspath
|
||||||
|
context.environment.configuration.addJvmClasspathRoots(classpath)
|
||||||
|
context.environment.updateClasspath(classpath.map(::JvmClasspathRoot))
|
||||||
|
|
||||||
|
sourceFiles.addAll(newSources)
|
||||||
|
|
||||||
|
// collectScriptsCompilationDependencies calls resolver for every file, so at this point all updated configurations are collected
|
||||||
|
context.environment.configuration.updateWithRefinedConfigurations(
|
||||||
|
context.baseScriptCompilationConfiguration, context.scriptCompilationState.configurations,
|
||||||
|
messageCollector, context.ignoredOptionsReportingState
|
||||||
|
)
|
||||||
|
return sourceFiles to sourceDependencies
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun CompilerConfiguration.updateWithRefinedConfigurations(
|
||||||
|
initialScriptCompilationConfiguration: ScriptCompilationConfiguration,
|
||||||
|
refinedScriptCompilationConfigurations: Map<SourceCode, ScriptCompilationConfiguration>,
|
||||||
|
messageCollector: ScriptDiagnosticsMessageCollector,
|
||||||
|
reportingState: IgnoredOptionsReportingState
|
||||||
|
) {
|
||||||
|
val updatedCompilerOptions = refinedScriptCompilationConfigurations.flatMap {
|
||||||
|
it.value[ScriptCompilationConfiguration.compilerOptions] ?: emptyList()
|
||||||
|
}
|
||||||
|
if (updatedCompilerOptions.isNotEmpty() &&
|
||||||
|
updatedCompilerOptions != initialScriptCompilationConfiguration[ScriptCompilationConfiguration.compilerOptions]
|
||||||
|
) {
|
||||||
|
|
||||||
|
val updatedArguments = K2JVMCompilerArguments()
|
||||||
|
parseCommandLineArguments(updatedCompilerOptions, updatedArguments)
|
||||||
|
|
||||||
|
reportArgumentsIgnoredGenerally(updatedArguments, messageCollector, reportingState)
|
||||||
|
reportArgumentsIgnoredFromRefinement(updatedArguments, messageCollector, reportingState)
|
||||||
|
|
||||||
|
setupCommonArguments(updatedArguments)
|
||||||
|
|
||||||
|
setupJvmSpecificArguments(updatedArguments)
|
||||||
|
|
||||||
|
configureAdvancedJvmOptions(updatedArguments)
|
||||||
|
}
|
||||||
|
}
|
||||||
+135
@@ -0,0 +1,135 @@
|
|||||||
|
/*
|
||||||
|
* 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.impl
|
||||||
|
|
||||||
|
import org.jetbrains.kotlin.cli.common.arguments.Argument
|
||||||
|
import org.jetbrains.kotlin.cli.common.arguments.K2JVMCompilerArguments
|
||||||
|
import org.jetbrains.kotlin.cli.common.messages.CompilerMessageLocation
|
||||||
|
import org.jetbrains.kotlin.cli.common.messages.CompilerMessageSeverity
|
||||||
|
import org.jetbrains.kotlin.cli.common.messages.MessageCollector
|
||||||
|
import kotlin.reflect.KMutableProperty1
|
||||||
|
import kotlin.reflect.full.findAnnotation
|
||||||
|
import kotlin.script.experimental.api.ResultWithDiagnostics
|
||||||
|
import kotlin.script.experimental.api.ScriptDiagnostic
|
||||||
|
import kotlin.script.experimental.api.SourceCode
|
||||||
|
import kotlin.script.experimental.api.asErrorDiagnostics
|
||||||
|
|
||||||
|
internal class ScriptDiagnosticsMessageCollector : MessageCollector {
|
||||||
|
|
||||||
|
private val _diagnostics = arrayListOf<ScriptDiagnostic>()
|
||||||
|
|
||||||
|
val diagnostics: List<ScriptDiagnostic> get() = _diagnostics
|
||||||
|
|
||||||
|
override fun clear() {
|
||||||
|
_diagnostics.clear()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun hasErrors(): Boolean =
|
||||||
|
_diagnostics.any { it.severity == ScriptDiagnostic.Severity.ERROR }
|
||||||
|
|
||||||
|
|
||||||
|
override fun report(severity: CompilerMessageSeverity, message: String, location: CompilerMessageLocation?) {
|
||||||
|
val mappedSeverity = when (severity) {
|
||||||
|
CompilerMessageSeverity.EXCEPTION,
|
||||||
|
CompilerMessageSeverity.ERROR -> ScriptDiagnostic.Severity.ERROR
|
||||||
|
CompilerMessageSeverity.STRONG_WARNING,
|
||||||
|
CompilerMessageSeverity.WARNING -> ScriptDiagnostic.Severity.WARNING
|
||||||
|
CompilerMessageSeverity.INFO -> ScriptDiagnostic.Severity.INFO
|
||||||
|
CompilerMessageSeverity.LOGGING -> ScriptDiagnostic.Severity.DEBUG
|
||||||
|
else -> null
|
||||||
|
}
|
||||||
|
if (mappedSeverity != null) {
|
||||||
|
val mappedLocation = location?.let {
|
||||||
|
if (it.line < 0 && it.column < 0) null // special location created by CompilerMessageLocation.create
|
||||||
|
else SourceCode.Location(
|
||||||
|
SourceCode.Position(
|
||||||
|
it.line,
|
||||||
|
it.column
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
_diagnostics.add(ScriptDiagnostic(message, mappedSeverity, location?.path, mappedLocation))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
internal fun failure(
|
||||||
|
messageCollector: ScriptDiagnosticsMessageCollector, vararg diagnostics: ScriptDiagnostic
|
||||||
|
): ResultWithDiagnostics.Failure =
|
||||||
|
ResultWithDiagnostics.Failure(*messageCollector.diagnostics.toTypedArray(), *diagnostics)
|
||||||
|
|
||||||
|
internal fun failure(
|
||||||
|
script: SourceCode, messageCollector: ScriptDiagnosticsMessageCollector, message: String
|
||||||
|
): ResultWithDiagnostics.Failure =
|
||||||
|
failure(messageCollector, message.asErrorDiagnostics(path = script.locationId))
|
||||||
|
|
||||||
|
internal class IgnoredOptionsReportingState {
|
||||||
|
var currentArguments = K2JVMCompilerArguments()
|
||||||
|
}
|
||||||
|
|
||||||
|
internal fun reportArgumentsIgnoredGenerally(
|
||||||
|
arguments: K2JVMCompilerArguments,
|
||||||
|
messageCollector: MessageCollector,
|
||||||
|
reportingState: IgnoredOptionsReportingState
|
||||||
|
) {
|
||||||
|
|
||||||
|
reportIgnoredArguments(
|
||||||
|
arguments,
|
||||||
|
"The following compiler arguments are ignored on script compilation: ",
|
||||||
|
messageCollector,
|
||||||
|
reportingState,
|
||||||
|
K2JVMCompilerArguments::version,
|
||||||
|
K2JVMCompilerArguments::destination,
|
||||||
|
K2JVMCompilerArguments::buildFile,
|
||||||
|
K2JVMCompilerArguments::commonSources,
|
||||||
|
K2JVMCompilerArguments::allWarningsAsErrors,
|
||||||
|
K2JVMCompilerArguments::script,
|
||||||
|
K2JVMCompilerArguments::scriptTemplates,
|
||||||
|
K2JVMCompilerArguments::scriptResolverEnvironment,
|
||||||
|
K2JVMCompilerArguments::disableStandardScript,
|
||||||
|
K2JVMCompilerArguments::disableDefaultScriptingPlugin,
|
||||||
|
K2JVMCompilerArguments::pluginClasspaths,
|
||||||
|
K2JVMCompilerArguments::pluginOptions,
|
||||||
|
K2JVMCompilerArguments::useJavac,
|
||||||
|
K2JVMCompilerArguments::compileJava,
|
||||||
|
K2JVMCompilerArguments::reportPerf,
|
||||||
|
K2JVMCompilerArguments::dumpPerf
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
internal fun reportArgumentsIgnoredFromRefinement(
|
||||||
|
arguments: K2JVMCompilerArguments, messageCollector: MessageCollector, reportingState: IgnoredOptionsReportingState
|
||||||
|
) {
|
||||||
|
reportIgnoredArguments(
|
||||||
|
arguments,
|
||||||
|
"The following compiler arguments are ignored when configured from refinement callbacks: ",
|
||||||
|
messageCollector,
|
||||||
|
reportingState,
|
||||||
|
K2JVMCompilerArguments::noJdk,
|
||||||
|
K2JVMCompilerArguments::jdkHome,
|
||||||
|
K2JVMCompilerArguments::javaModulePath,
|
||||||
|
K2JVMCompilerArguments::classpath,
|
||||||
|
K2JVMCompilerArguments::noStdlib,
|
||||||
|
K2JVMCompilerArguments::noReflect
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun reportIgnoredArguments(
|
||||||
|
arguments: K2JVMCompilerArguments, message: String,
|
||||||
|
messageCollector: MessageCollector, reportingState: IgnoredOptionsReportingState,
|
||||||
|
vararg toIgnore: KMutableProperty1<K2JVMCompilerArguments, *>
|
||||||
|
) {
|
||||||
|
val ignoredArgKeys = toIgnore.mapNotNull { argProperty ->
|
||||||
|
if (argProperty.get(arguments) != argProperty.get(reportingState.currentArguments)) {
|
||||||
|
argProperty.findAnnotation<Argument>()?.value
|
||||||
|
?: throw IllegalStateException("unknown compiler argument property: $argProperty: no Argument annotation found")
|
||||||
|
} else null
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ignoredArgKeys.isNotEmpty()) {
|
||||||
|
messageCollector.report(CompilerMessageSeverity.STRONG_WARNING, "$message${ignoredArgKeys.joinToString(", ")}")
|
||||||
|
}
|
||||||
|
}
|
||||||
+156
@@ -0,0 +1,156 @@
|
|||||||
|
/*
|
||||||
|
* 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.impl
|
||||||
|
|
||||||
|
import com.intellij.openapi.Disposable
|
||||||
|
import com.intellij.openapi.project.Project
|
||||||
|
import com.intellij.openapi.util.Disposer
|
||||||
|
import com.intellij.openapi.util.text.StringUtil
|
||||||
|
import com.intellij.openapi.vfs.CharsetToolkit
|
||||||
|
import com.intellij.psi.PsiFileFactory
|
||||||
|
import com.intellij.psi.impl.PsiFileFactoryImpl
|
||||||
|
import com.intellij.testFramework.LightVirtualFile
|
||||||
|
import org.jetbrains.kotlin.cli.common.environment.setIdeaIoUseFallback
|
||||||
|
import org.jetbrains.kotlin.codegen.state.GenerationState
|
||||||
|
import org.jetbrains.kotlin.idea.KotlinLanguage
|
||||||
|
import org.jetbrains.kotlin.psi.KtFile
|
||||||
|
import org.jetbrains.kotlin.psi.KtScript
|
||||||
|
import org.jetbrains.kotlin.scripting.dependencies.ScriptsCompilationDependencies
|
||||||
|
import org.jetbrains.kotlin.utils.addToStdlib.firstIsInstanceOrNull
|
||||||
|
import java.util.*
|
||||||
|
import kotlin.script.experimental.api.*
|
||||||
|
import kotlin.script.experimental.host.FileScriptSource
|
||||||
|
import kotlin.script.experimental.host.getMergedScriptText
|
||||||
|
import kotlin.script.experimental.jvm.impl.KJvmCompiledScript
|
||||||
|
|
||||||
|
internal class ScriptLightVirtualFile(name: String, private val _path: String?, text: String) :
|
||||||
|
LightVirtualFile(name, KotlinLanguage.INSTANCE, StringUtil.convertLineSeparators(text)) {
|
||||||
|
|
||||||
|
init {
|
||||||
|
charset = CharsetToolkit.UTF8_CHARSET
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun getPath(): String = _path ?: super.getPath()
|
||||||
|
override fun getCanonicalPath(): String? = path
|
||||||
|
}
|
||||||
|
|
||||||
|
internal fun makeCompiledModule(generationState: GenerationState) =
|
||||||
|
KJvmCompiledModuleInMemory(
|
||||||
|
generationState.factory.asList()
|
||||||
|
.associateTo(sortedMapOf<String, ByteArray>()) { it.relativePath to it.asByteArray() }
|
||||||
|
)
|
||||||
|
|
||||||
|
internal fun SourceCode.scriptFileName(
|
||||||
|
mainScript: SourceCode,
|
||||||
|
scriptCompilationConfiguration: ScriptCompilationConfiguration
|
||||||
|
): String =
|
||||||
|
when {
|
||||||
|
name != null -> name!!
|
||||||
|
mainScript == this -> "script.${scriptCompilationConfiguration[ScriptCompilationConfiguration.fileExtension]}"
|
||||||
|
else -> throw Exception("Unexpected script without name: $this")
|
||||||
|
}
|
||||||
|
|
||||||
|
internal inline fun <T> withMessageCollectorAndDisposable(
|
||||||
|
messageCollector: ScriptDiagnosticsMessageCollector = ScriptDiagnosticsMessageCollector(),
|
||||||
|
disposable: Disposable = Disposer.newDisposable(),
|
||||||
|
disposeOnSuccess: Boolean = true,
|
||||||
|
locationId: String? = null,
|
||||||
|
body: (ScriptDiagnosticsMessageCollector, Disposable) -> ResultWithDiagnostics<T>
|
||||||
|
): ResultWithDiagnostics<T> {
|
||||||
|
var failed = false
|
||||||
|
return try {
|
||||||
|
setIdeaIoUseFallback()
|
||||||
|
body(messageCollector, disposable).also {
|
||||||
|
failed = it is ResultWithDiagnostics.Failure
|
||||||
|
}
|
||||||
|
} catch (ex: Throwable) {
|
||||||
|
failed = true
|
||||||
|
failure(messageCollector, ex.asDiagnostics(path = locationId))
|
||||||
|
} finally {
|
||||||
|
if (disposeOnSuccess || failed) {
|
||||||
|
disposable.dispose()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
internal inline fun <T> withMessageCollector(
|
||||||
|
script: SourceCode,
|
||||||
|
messageCollector: ScriptDiagnosticsMessageCollector = ScriptDiagnosticsMessageCollector(),
|
||||||
|
body: (ScriptDiagnosticsMessageCollector) -> ResultWithDiagnostics<T>
|
||||||
|
): ResultWithDiagnostics<T> =
|
||||||
|
try {
|
||||||
|
body(messageCollector)
|
||||||
|
} catch (ex: Throwable) {
|
||||||
|
failure(messageCollector, ex.asDiagnostics(path = script.locationId))
|
||||||
|
}
|
||||||
|
|
||||||
|
internal fun getScriptKtFile(
|
||||||
|
script: SourceCode,
|
||||||
|
scriptCompilationConfiguration: ScriptCompilationConfiguration,
|
||||||
|
project: Project,
|
||||||
|
messageCollector: ScriptDiagnosticsMessageCollector
|
||||||
|
): ResultWithDiagnostics<KtFile> {
|
||||||
|
val psiFileFactory: PsiFileFactoryImpl = PsiFileFactory.getInstance(project) as PsiFileFactoryImpl
|
||||||
|
val scriptText = getMergedScriptText(script, scriptCompilationConfiguration)
|
||||||
|
val virtualFile = ScriptLightVirtualFile(
|
||||||
|
script.scriptFileName(script, scriptCompilationConfiguration),
|
||||||
|
(script as? FileScriptSource)?.file?.path,
|
||||||
|
scriptText
|
||||||
|
)
|
||||||
|
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")
|
||||||
|
else -> ktFile.asSuccess()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
internal fun makeCompiledScript(
|
||||||
|
generationState: GenerationState,
|
||||||
|
script: SourceCode,
|
||||||
|
ktFile: KtFile,
|
||||||
|
sourceDependencies: List<ScriptsCompilationDependencies.SourceDependencies>,
|
||||||
|
getScriptConfiguration: (KtFile) -> ScriptCompilationConfiguration
|
||||||
|
): KJvmCompiledScript<Any> {
|
||||||
|
val scriptDependenciesStack = ArrayDeque<KtScript>()
|
||||||
|
val ktScript = ktFile.declarations.firstIsInstanceOrNull<KtScript>()
|
||||||
|
?: throw IllegalStateException("Expecting script file: KtScript is not found in ${ktFile.name}")
|
||||||
|
|
||||||
|
fun makeOtherScripts(script: KtScript): List<KJvmCompiledScript<*>> {
|
||||||
|
|
||||||
|
// TODO: ensure that it is caught earlier (as well) since it would be more economical
|
||||||
|
if (scriptDependenciesStack.contains(script))
|
||||||
|
throw IllegalArgumentException("Unable to handle recursive script dependencies")
|
||||||
|
scriptDependenciesStack.push(script)
|
||||||
|
|
||||||
|
val containingKtFile = script.containingKtFile
|
||||||
|
val otherScripts: List<KJvmCompiledScript<*>> =
|
||||||
|
sourceDependencies.find { it.scriptFile == containingKtFile }?.sourceDependencies?.mapNotNull { sourceFile ->
|
||||||
|
sourceFile.declarations.firstIsInstanceOrNull<KtScript>()?.let {
|
||||||
|
KJvmCompiledScript<Any>(
|
||||||
|
containingKtFile.virtualFile?.path,
|
||||||
|
getScriptConfiguration(sourceFile),
|
||||||
|
it.fqName.asString(),
|
||||||
|
makeOtherScripts(it),
|
||||||
|
null
|
||||||
|
)
|
||||||
|
}
|
||||||
|
} ?: emptyList()
|
||||||
|
|
||||||
|
scriptDependenciesStack.pop()
|
||||||
|
return otherScripts
|
||||||
|
}
|
||||||
|
|
||||||
|
val module = makeCompiledModule(generationState)
|
||||||
|
return KJvmCompiledScript(
|
||||||
|
script.locationId,
|
||||||
|
getScriptConfiguration(ktScript.containingKtFile),
|
||||||
|
ktScript.fqName.asString(),
|
||||||
|
makeOtherScripts(ktScript),
|
||||||
|
module
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
+116
@@ -0,0 +1,116 @@
|
|||||||
|
/*
|
||||||
|
* 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.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 java.util.concurrent.locks.ReentrantReadWriteLock
|
||||||
|
import kotlin.concurrent.read
|
||||||
|
import kotlin.concurrent.write
|
||||||
|
import kotlin.script.experimental.api.ScriptCompilationConfiguration
|
||||||
|
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
|
||||||
|
|
||||||
|
class JvmReplCompiler(
|
||||||
|
val scriptCompilationConfiguration: ScriptCompilationConfiguration,
|
||||||
|
val hostConfiguration: ScriptingHostConfiguration = defaultJvmScriptingHostConfiguration,
|
||||||
|
val replCompilerProxy: KJvmReplCompilerProxy = KJvmCompilerImpl(hostConfiguration.withDefaults())
|
||||||
|
) : ReplCompiler {
|
||||||
|
|
||||||
|
override fun createState(lock: ReentrantReadWriteLock): IReplStageState<*> = JvmReplCompilerState(replCompilerProxy, lock)
|
||||||
|
|
||||||
|
override fun check(state: IReplStageState<*>, codeLine: ReplCodeLine): ReplCheckResult {
|
||||||
|
TODO("not implemented") //To change body of created functions use File | Settings | File Templates.
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun compile(state: IReplStageState<*>, codeLine: ReplCodeLine): ReplCompileResult {
|
||||||
|
TODO("not implemented") //To change body of created functions use File | Settings | File Templates.
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
interface KJvmReplCompilerProxy {
|
||||||
|
fun createReplCompilationState(scriptCompilationConfiguration: ScriptCompilationConfiguration): JvmReplCompilerState.Compilation
|
||||||
|
}
|
||||||
|
|
||||||
|
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?.dispose()
|
||||||
|
_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
|
||||||
|
}
|
||||||
|
}
|
||||||
+12
-3
@@ -90,14 +90,23 @@ class ReplCodeAnalyzer(environment: KotlinCoreEnvironment) {
|
|||||||
|
|
||||||
psiFile.script!!.putUserData(ScriptPriorities.PRIORITY_KEY, codeLine.no)
|
psiFile.script!!.putUserData(ScriptPriorities.PRIORITY_KEY, codeLine.no)
|
||||||
|
|
||||||
return doAnalyze(psiFile, codeLine)
|
return doAnalyze(psiFile, emptyList(), codeLine)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun doAnalyze(linePsi: KtFile, codeLine: ReplCodeLine): ReplLineAnalysisResult {
|
fun analyzeReplLineWithImportedScripts(psiFile: KtFile, importedScripts: List<KtFile>, codeLine: ReplCodeLine): ReplLineAnalysisResult {
|
||||||
|
topDownAnalysisContext.scripts.clear()
|
||||||
|
trace.clearDiagnostics()
|
||||||
|
|
||||||
|
psiFile.script!!.putUserData(ScriptPriorities.PRIORITY_KEY, codeLine.no)
|
||||||
|
|
||||||
|
return doAnalyze(psiFile, importedScripts, codeLine)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun doAnalyze(linePsi: KtFile, importedScripts: List<KtFile>, codeLine: ReplCodeLine): ReplLineAnalysisResult {
|
||||||
scriptDeclarationFactory.setDelegateFactory(FileBasedDeclarationProviderFactory(resolveSession.storageManager, listOf(linePsi)))
|
scriptDeclarationFactory.setDelegateFactory(FileBasedDeclarationProviderFactory(resolveSession.storageManager, listOf(linePsi)))
|
||||||
replState.submitLine(linePsi, codeLine)
|
replState.submitLine(linePsi, codeLine)
|
||||||
|
|
||||||
val context = topDownAnalyzer.analyzeDeclarations(topDownAnalysisContext.topDownAnalysisMode, listOf(linePsi))
|
val context = topDownAnalyzer.analyzeDeclarations(topDownAnalysisContext.topDownAnalysisMode, listOf(linePsi) + importedScripts)
|
||||||
|
|
||||||
val diagnostics = trace.bindingContext.diagnostics
|
val diagnostics = trace.bindingContext.diagnostics
|
||||||
val hasErrors = diagnostics.any { it.severity == Severity.ERROR }
|
val hasErrors = diagnostics.any { it.severity == Severity.ERROR }
|
||||||
|
|||||||
Reference in New Issue
Block a user