Refactor scripting infrastructure - convert usages to the new entities

This commit is contained in:
Ilya Chernikov
2019-07-02 11:58:39 +02:00
parent 80ada5113e
commit 4940f64265
11 changed files with 145 additions and 212 deletions
@@ -1,145 +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.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
}
}
}
@@ -17,6 +17,7 @@ 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
@@ -35,10 +36,9 @@ class KJvmCompilerImpl(val hostConfiguration: ScriptingHostConfiguration) : KJvm
getScriptKtFile(script, context.baseScriptCompilationConfiguration, context.environment.project, messageCollector)
.valueOr { return it }
context.scriptCompilationState.configureFor(script, context.baseScriptCompilationConfiguration)
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")
@@ -48,7 +48,7 @@ class KJvmCompilerImpl(val hostConfiguration: ScriptingHostConfiguration) : KJvm
val compiledScript =
makeCompiledScript(generationState, script, sourceFiles.first(), sourceDependencies) { ktFile ->
context.scriptCompilationState.configurations.entries.find { ktFile.name == it.key.name }?.value
dependenciesProvider?.getScriptConfigurationResult(ktFile)?.valueOrNull()?.configuration
?: context.baseScriptCompilationConfiguration
}
@@ -20,6 +20,7 @@ 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.definitions.ScriptDependenciesProvider
import org.jetbrains.kotlin.scripting.repl.ReplCodeAnalyzer
import kotlin.script.experimental.api.*
import kotlin.script.experimental.host.ScriptingHostConfiguration
@@ -118,9 +119,10 @@ class KJvmReplCompilerImpl(val hostConfiguration: ScriptingHostConfiguration) :
history.push(LineId(codeLine), scriptDescriptor)
val dependenciesProvider = ScriptDependenciesProvider.getInstance(context.environment.project)
val compiledScript =
makeCompiledScript(generationState, snippet, sourceFiles.first(), sourceDependencies) { ktFile ->
context.scriptCompilationState.configurations.entries.find { ktFile.name == it.key.name }?.value
dependenciesProvider?.getScriptConfigurationResult(ktFile)?.valueOrNull()?.configuration
?: context.baseScriptCompilationConfiguration
}
@@ -27,12 +27,13 @@ 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.definitions.ScriptDefinition
import org.jetbrains.kotlin.scripting.definitions.ScriptDependenciesProvider
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.api.valueOrNull
import kotlin.script.experimental.host.ScriptingHostConfiguration
import kotlin.script.experimental.jvm.JvmDependency
import kotlin.script.experimental.jvm.jdkHome
@@ -44,8 +45,7 @@ internal class SharedScriptCompilationContext(
val disposable: Disposable,
val baseScriptCompilationConfiguration: ScriptCompilationConfiguration,
val environment: KotlinCoreEnvironment,
val ignoredOptionsReportingState: IgnoredOptionsReportingState,
val scriptCompilationState: BridgeScriptDefinitionDynamicState
val ignoredOptionsReportingState: IgnoredOptionsReportingState
)
internal fun createSharedCompilationContext(
@@ -56,11 +56,9 @@ internal fun createSharedCompilationContext(
): SharedScriptCompilationContext {
val ignoredOptionsReportingState = IgnoredOptionsReportingState()
val scriptCompilationState = BridgeScriptDefinitionDynamicState()
val (initialScriptCompilationConfiguration, kotlinCompilerConfiguration) =
createInitialConfigurations(
scriptCompilationConfiguration, hostConfiguration, scriptCompilationState, messageCollector, ignoredOptionsReportingState
scriptCompilationConfiguration, hostConfiguration, messageCollector, ignoredOptionsReportingState
)
val environment =
KotlinCoreEnvironment.createForProduction(
@@ -68,14 +66,13 @@ internal fun createSharedCompilationContext(
)
return SharedScriptCompilationContext(
disposable, initialScriptCompilationConfiguration, environment, ignoredOptionsReportingState, scriptCompilationState
disposable, initialScriptCompilationConfiguration, environment, ignoredOptionsReportingState
)
}
internal fun createInitialConfigurations(
scriptCompilationConfiguration: ScriptCompilationConfiguration,
hostConfiguration: ScriptingHostConfiguration,
scriptCompilationState: BridgeScriptDefinitionDynamicState,
messageCollector: ScriptDiagnosticsMessageCollector,
ignoredOptionsReportingState: IgnoredOptionsReportingState
): Pair<ScriptCompilationConfiguration, CompilerConfiguration> {
@@ -89,14 +86,7 @@ internal fun createInitialConfigurations(
kotlinCompilerConfiguration.add(
ScriptingConfigurationKeys.SCRIPT_DEFINITIONS,
ScriptDefinition.FromLegacy(
hostConfiguration,
BridgeScriptDefinition(
scriptCompilationConfiguration,
hostConfiguration,
scriptCompilationState
)
)
ScriptDefinition.FromConfigurations(hostConfiguration, scriptCompilationConfiguration, null)
)
initialScriptCompilationConfiguration[ScriptCompilationConfiguration.compilerOptions]?.let { compilerOptions ->
@@ -217,26 +207,25 @@ internal fun collectRefinedSourcesAndUpdateEnvironment(
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
)
// collectScriptsCompilationDependencies calls resolver for every file, so at this point all updated configurations are collected in the ScriptDependenciesProvider
context.environment.configuration.updateWithRefinedConfigurations(context, sourceFiles, messageCollector)
return sourceFiles to sourceDependencies
}
private fun CompilerConfiguration.updateWithRefinedConfigurations(
initialScriptCompilationConfiguration: ScriptCompilationConfiguration,
refinedScriptCompilationConfigurations: Map<SourceCode, ScriptCompilationConfiguration>,
messageCollector: ScriptDiagnosticsMessageCollector,
reportingState: IgnoredOptionsReportingState
context: SharedScriptCompilationContext,
sourceFiles: List<KtFile>,
messageCollector: ScriptDiagnosticsMessageCollector
) {
val updatedCompilerOptions = refinedScriptCompilationConfigurations.flatMap {
it.value[ScriptCompilationConfiguration.compilerOptions] ?: emptyList()
val dependenciesProvider = ScriptDependenciesProvider.getInstance(context.environment.project)
val updatedCompilerOptions = sourceFiles.flatMap {
dependenciesProvider?.getScriptConfigurationResult(it)?.valueOrNull()?.configuration?.get(
ScriptCompilationConfiguration.compilerOptions
) ?: emptyList()
}
if (updatedCompilerOptions.isNotEmpty() &&
updatedCompilerOptions != initialScriptCompilationConfiguration[ScriptCompilationConfiguration.compilerOptions]
updatedCompilerOptions != context.baseScriptCompilationConfiguration[ScriptCompilationConfiguration.compilerOptions]
) {
updateWithCompilerOptions(updatedCompilerOptions, messageCollector, reportingState, true)
updateWithCompilerOptions(updatedCompilerOptions, messageCollector, context.ignoredOptionsReportingState, true)
}
}