Refactor script definitions and resolving/refining infrastructure:

- implement wrappers to wrap old and new API providers and resolvers
- make old API deprecated (with error where possible)
- drop old internal classes related to the old API
- refactor usages accordingly
- fix and add missing features to the scripting API where necessary
This commit is contained in:
Ilya Chernikov
2019-05-12 22:26:37 +02:00
parent e5054f9648
commit e542c9ea84
94 changed files with 1554 additions and 764 deletions
@@ -43,7 +43,7 @@ class JvmCliScriptEvaluationExtension : ScriptEvaluationExtension {
val scriptFile = File(sourcePath)
if (scriptFile.isDirectory || !scriptDefinitionProvider.isScript(scriptFile.name)) {
val extensionHint =
if (configuration.get(ScriptingConfigurationKeys.SCRIPT_DEFINITIONS) == listOf(StandardScriptDefinition)) " (.kts)"
if (configuration.get(ScriptingConfigurationKeys.SCRIPT_DEFINITIONS)?.let { it.size == 1 && it.first().isDefault } == true) " (.kts)"
else ""
messageCollector.report(ERROR, "Specify path to the script file$extensionHint as the first argument")
return COMPILATION_ERROR
@@ -18,13 +18,14 @@ import org.jetbrains.kotlin.idea.KotlinFileType
import org.jetbrains.kotlin.scripting.compiler.plugin.definitions.CliScriptDefinitionProvider
import org.jetbrains.kotlin.scripting.configuration.ScriptingConfigurationKeys
import org.jetbrains.kotlin.scripting.configuration.configureScriptDefinitions
import org.jetbrains.kotlin.scripting.definitions.ScriptDefinitionProvider
import org.jetbrains.kotlin.scripting.definitions.ScriptDefinitionsFromClasspathDiscoverySource
import org.jetbrains.kotlin.scripting.definitions.StandardScriptDefinition
import org.jetbrains.kotlin.scripting.definitions.reporter
import org.jetbrains.kotlin.scripting.definitions.*
import java.io.File
import kotlin.script.experimental.host.ScriptingHostConfiguration
class ScriptingCompilerConfigurationExtension(val project: MockProject) : CompilerConfigurationExtension {
class ScriptingCompilerConfigurationExtension(
val project: MockProject,
val baseHostConfiguration: ScriptingHostConfiguration
) : CompilerConfigurationExtension {
override fun updateConfiguration(configuration: CompilerConfiguration) {
@@ -39,7 +40,11 @@ class ScriptingCompilerConfigurationExtension(val project: MockProject) : Compil
projectRoot
)
}
val scriptResolverEnv = configuration.getMap(ScriptingConfigurationKeys.LEGACY_SCRIPT_RESOLVER_ENVIRONMENT_OPTION)
val hostConfiguration = ScriptingHostConfiguration(baseHostConfiguration) {
getEnvironment {
configuration.getMap(ScriptingConfigurationKeys.LEGACY_SCRIPT_RESOLVER_ENVIRONMENT_OPTION)
}
}
val explicitScriptDefinitions = configuration.getList(ScriptingConfigurationKeys.SCRIPT_DEFINITIONS_CLASSES)
@@ -49,16 +54,16 @@ class ScriptingCompilerConfigurationExtension(val project: MockProject) : Compil
configuration,
this::class.java.classLoader,
messageCollector,
scriptResolverEnv
hostConfiguration
)
}
// If not disabled explicitly, we should always support at least the standard script definition
if (!configuration.getBoolean(JVMConfigurationKeys.DISABLE_STANDARD_SCRIPT_DEFINITION) &&
!configuration.getList(ScriptingConfigurationKeys.SCRIPT_DEFINITIONS).contains(StandardScriptDefinition)
configuration.getList(ScriptingConfigurationKeys.SCRIPT_DEFINITIONS).none { it.isDefault }
) {
configuration.add(
ScriptingConfigurationKeys.SCRIPT_DEFINITIONS,
StandardScriptDefinition
ScriptDefinition.getDefault(hostConfiguration)
)
}
@@ -66,37 +71,26 @@ class ScriptingCompilerConfigurationExtension(val project: MockProject) : Compil
ScriptingConfigurationKeys.SCRIPT_DEFINITIONS_SOURCES,
ScriptDefinitionsFromClasspathDiscoverySource(
configuration.jvmClasspathRoots,
configuration.get(ScriptingConfigurationKeys.LEGACY_SCRIPT_RESOLVER_ENVIRONMENT_OPTION) ?: emptyMap(),
hostConfiguration,
messageCollector.reporter
)
)
}
// If not disabled explicitly, we should always support at least the standard script definition
if (!configuration.getBoolean(JVMConfigurationKeys.DISABLE_STANDARD_SCRIPT_DEFINITION) &&
StandardScriptDefinition !in configuration.getList(ScriptingConfigurationKeys.SCRIPT_DEFINITIONS)
) {
configuration.add(
ScriptingConfigurationKeys.SCRIPT_DEFINITIONS,
StandardScriptDefinition
)
}
val scriptDefinitionProvider = ScriptDefinitionProvider.getInstance(project) as? CliScriptDefinitionProvider
if (scriptDefinitionProvider != null) {
scriptDefinitionProvider.setScriptDefinitionsSources(configuration.getList(ScriptingConfigurationKeys.SCRIPT_DEFINITIONS_SOURCES))
scriptDefinitionProvider.setScriptDefinitions(configuration.getList(ScriptingConfigurationKeys.SCRIPT_DEFINITIONS))
val scriptDefinitionProvider = ScriptDefinitionProvider.getInstance(project) as? CliScriptDefinitionProvider
if (scriptDefinitionProvider != null) {
scriptDefinitionProvider.setScriptDefinitionsSources(configuration.getList(ScriptingConfigurationKeys.SCRIPT_DEFINITIONS_SOURCES))
scriptDefinitionProvider.setScriptDefinitions(configuration.getList(ScriptingConfigurationKeys.SCRIPT_DEFINITIONS))
// Register new file extensions
val fileTypeRegistry = FileTypeRegistry.getInstance() as CoreFileTypeRegistry
// Register new file extensions
val fileTypeRegistry = FileTypeRegistry.getInstance() as CoreFileTypeRegistry
scriptDefinitionProvider.getKnownFilenameExtensions().filter {
fileTypeRegistry.getFileTypeByExtension(it) != KotlinFileType.INSTANCE
}.forEach {
fileTypeRegistry.registerFileType(KotlinFileType.INSTANCE, it)
scriptDefinitionProvider.getKnownFilenameExtensions().filter {
fileTypeRegistry.getFileTypeByExtension(it) != KotlinFileType.INSTANCE
}.forEach {
fileTypeRegistry.registerFileType(KotlinFileType.INSTANCE, it)
}
}
}
}
}
@@ -5,33 +5,29 @@
package org.jetbrains.kotlin.scripting.compiler.plugin.definitions
import org.jetbrains.kotlin.scripting.definitions.KotlinScriptDefinition
import org.jetbrains.kotlin.scripting.definitions.LazyScriptDefinitionProvider
import org.jetbrains.kotlin.scripting.definitions.ScriptDefinition
import org.jetbrains.kotlin.scripting.definitions.ScriptDefinitionsSource
import org.jetbrains.kotlin.scripting.definitions.StandardScriptDefinition
import kotlin.concurrent.write
open class CliScriptDefinitionProvider : LazyScriptDefinitionProvider() {
private val definitionsFromSources: MutableList<Sequence<KotlinScriptDefinition>> = arrayListOf()
private val definitions: MutableList<KotlinScriptDefinition> = arrayListOf()
private var hasStandardDefinition = true
private val definitionsFromSources: MutableList<Sequence<ScriptDefinition>> = arrayListOf()
private val definitions: MutableList<ScriptDefinition> = arrayListOf()
private var defaultDefinition: ScriptDefinition? = null
override val currentDefinitions: Sequence<KotlinScriptDefinition>
override val currentDefinitions: Sequence<ScriptDefinition>
get() {
val base = definitions.asSequence() + definitionsFromSources.asSequence().flatMap { it }
return if (hasStandardDefinition) base + getDefaultScriptDefinition() else base
return base + getDefaultDefinition()
}
override fun getDefaultScriptDefinition(): KotlinScriptDefinition {
return StandardScriptDefinition
}
fun setScriptDefinitions(newDefinitions: List<KotlinScriptDefinition>) {
fun setScriptDefinitions(newDefinitions: List<ScriptDefinition>) {
lock.write {
definitions.clear()
val (withoutStdDef, stdDef) = newDefinitions.partition { it != getDefaultScriptDefinition() }
val (withoutStdDef, stdDef) = newDefinitions.partition { !it.isDefault }
definitions.addAll(withoutStdDef)
hasStandardDefinition = stdDef.isNotEmpty()
// TODO: consider reporting an error when several default definitions are supplied
defaultDefinition = stdDef.firstOrNull()
}
}
@@ -43,4 +39,6 @@ open class CliScriptDefinitionProvider : LazyScriptDefinitionProvider() {
}
}
}
override fun getDefaultDefinition(): ScriptDefinition = defaultDefinition ?: super.getDefaultDefinition()
}
@@ -11,44 +11,45 @@ import com.intellij.openapi.project.Project
import com.intellij.openapi.vfs.VirtualFile
import org.jetbrains.kotlin.scripting.definitions.ScriptDependenciesProvider
import org.jetbrains.kotlin.scripting.definitions.findScriptDefinition
import org.jetbrains.kotlin.scripting.resolve.ScriptContentLoader
import org.jetbrains.kotlin.scripting.resolve.ScriptCompilationConfigurationResult
import org.jetbrains.kotlin.scripting.resolve.ScriptReportSink
import org.jetbrains.kotlin.scripting.resolve.adjustByDefinition
import org.jetbrains.kotlin.scripting.resolve.VirtualFileScriptSource
import org.jetbrains.kotlin.scripting.resolve.refineScriptCompilationConfiguration
import java.io.File
import java.util.concurrent.locks.ReentrantReadWriteLock
import kotlin.concurrent.read
import kotlin.concurrent.write
import kotlin.script.experimental.dependencies.ScriptDependencies
import kotlin.script.experimental.api.ResultWithDiagnostics
import kotlin.script.experimental.jvm.compat.mapToLegacyReports
class CliScriptDependenciesProvider(private val project: Project) : ScriptDependenciesProvider {
private val cacheLock = ReentrantReadWriteLock()
private val cache = hashMapOf<String, ScriptDependencies?>()
private val scriptContentLoader = ScriptContentLoader(project)
private val cache = hashMapOf<String, ScriptCompilationConfigurationResult?>()
override fun getScriptDependencies(file: VirtualFile): ScriptDependencies? = cacheLock.read {
calculateExternalDependencies(file)
override fun getScriptConfigurationResult(file: VirtualFile): ScriptCompilationConfigurationResult? = cacheLock.read {
calculateRefinedConfiguration(file)
}
private fun calculateExternalDependencies(file: VirtualFile): ScriptDependencies? {
private fun calculateRefinedConfiguration(file: VirtualFile): ScriptCompilationConfigurationResult? {
val path = file.path
val cached = cache[path]
return if (cached != null) cached
else {
val scriptDef = file.findScriptDefinition(project)
if (scriptDef != null) {
val result = scriptContentLoader.loadContentsAndResolveDependencies(scriptDef, file)
val result = refineScriptCompilationConfiguration(VirtualFileScriptSource(file), scriptDef, project)
ServiceManager.getService(project, ScriptReportSink::class.java)?.attachReports(file, result.reports)
ServiceManager.getService(project, ScriptReportSink::class.java)?.attachReports(file, result.reports.mapToLegacyReports())
val deps = result.dependencies?.adjustByDefinition(scriptDef)
if (deps != null) {
log.info("[kts] new cached deps for $path: ${deps.classpath.joinToString(File.pathSeparator)}")
if (result is ResultWithDiagnostics.Success) {
log.info("[kts] new cached deps for $path: ${result.value.dependenciesClassPath.joinToString(File.pathSeparator)}")
} else {
log.info("[kts] new cached errors for $path:\n ${result.reports.joinToString("\n ") { it.message + if (it.exception == null) "" else ": ${it.exception}" }}")
}
cacheLock.write {
cache.put(path, deps)
cache.put(path, result)
}
deps
result
} else null
}
}
@@ -22,6 +22,7 @@ import org.jetbrains.kotlin.resolve.extensions.SyntheticResolveExtension
import org.jetbrains.kotlin.scripting.compiler.plugin.definitions.CliScriptDefinitionProvider
import org.jetbrains.kotlin.scripting.compiler.plugin.definitions.CliScriptDependenciesProvider
import org.jetbrains.kotlin.scripting.compiler.plugin.definitions.CliScriptReportSink
import org.jetbrains.kotlin.scripting.configuration.makeHostConfiguration
import org.jetbrains.kotlin.scripting.definitions.ScriptDefinitionProvider
import org.jetbrains.kotlin.scripting.definitions.ScriptDependenciesProvider
import org.jetbrains.kotlin.scripting.extensions.*
@@ -39,8 +40,9 @@ private fun <T> ProjectExtensionDescriptor<T>.registerExtensionIfRequired(projec
class ScriptingCompilerConfigurationComponentRegistrar : ComponentRegistrar {
override fun registerProjectComponents(project: MockProject, configuration: CompilerConfiguration) {
val messageCollector = configuration.get(CLIConfigurationKeys.MESSAGE_COLLECTOR_KEY)
val hostConfiguration = makeHostConfiguration(project, configuration)
withClassloadingProblemsReporting(messageCollector) {
CompilerConfigurationExtension.registerExtension(project, ScriptingCompilerConfigurationExtension(project))
CompilerConfigurationExtension.registerExtension(project, ScriptingCompilerConfigurationExtension(project, hostConfiguration))
CollectAdditionalSourcesExtension.registerExtension(project, ScriptingCollectAdditionalSourcesExtension(project))
ScriptEvaluationExtension.registerExtensionIfRequired(project, JvmCliScriptEvaluationExtension())
ShellExtension.registerExtensionIfRequired(project, JvmCliReplShellExtension())
@@ -7,11 +7,13 @@ package org.jetbrains.kotlin.scripting.compiler.plugin
import org.jetbrains.kotlin.scripting.compiler.plugin.definitions.CliScriptDefinitionProvider
import org.jetbrains.kotlin.scripting.definitions.KotlinScriptDefinition
import org.jetbrains.kotlin.scripting.definitions.ScriptDefinition
import org.jetbrains.kotlin.scripting.definitions.ScriptDefinitionsSource
import org.jetbrains.kotlin.test.testFramework.KtUsefulTestCase
import org.junit.Assert
import org.junit.Test
import java.util.concurrent.atomic.AtomicInteger
import kotlin.script.experimental.jvm.defaultJvmScriptingHostConfiguration
import kotlin.script.templates.standard.ScriptTemplateWithArgs
class ScriptProviderTest : KtUsefulTestCase() {
@@ -64,11 +66,16 @@ class ScriptProviderTest : KtUsefulTestCase() {
}
}
private open class FakeScriptDefinition(val suffix: String = ".kts") : KotlinScriptDefinition(ScriptTemplateWithArgs::class) {
private open class FakeScriptDefinition(val suffix: String = ".kts") :
ScriptDefinition.FromLegacy(defaultJvmScriptingHostConfiguration, KotlinScriptDefinition(ScriptTemplateWithArgs::class))
{
val matchCounter = AtomicInteger()
override fun isScript(fileName: String): Boolean = fileName.endsWith(suffix).also {
if (it) matchCounter.incrementAndGet()
}
override val isDefault: Boolean
get() = suffix == ".kts"
}
private class TestScriptDefinitionSource(val counter: AtomicInteger, val defGens: Iterable<() -> FakeScriptDefinition>) :
@@ -80,7 +87,7 @@ private class TestScriptDefinitionSource(val counter: AtomicInteger, val defGens
)
} })
override val definitions: Sequence<KotlinScriptDefinition> = sequence {
override val definitions: Sequence<ScriptDefinition> = sequence {
for (gen in defGens) {
counter.incrementAndGet()
yield(gen())
@@ -88,6 +95,6 @@ private class TestScriptDefinitionSource(val counter: AtomicInteger, val defGens
}
}
private class TestCliScriptDefinitionProvider(private val standardDef: KotlinScriptDefinition) : CliScriptDefinitionProvider() {
override fun getDefaultScriptDefinition(): KotlinScriptDefinition = standardDef
private class TestCliScriptDefinitionProvider(private val standardDef: ScriptDefinition) : CliScriptDefinitionProvider() {
override fun getDefaultScriptDefinition(): KotlinScriptDefinition = standardDef.legacyDefinition
}
@@ -35,6 +35,7 @@ import org.jetbrains.kotlin.utils.KotlinPaths
import org.jetbrains.kotlin.utils.PathUtil
import org.junit.Assert
import java.io.File
import kotlin.script.experimental.jvm.defaultJvmScriptingHostConfiguration
class ScriptingCompilerPluginTest : TestCaseWithTmpdir() {
@@ -116,7 +117,7 @@ class ScriptingCompilerPluginTest : TestCaseWithTmpdir() {
loadScriptTemplatesFromClasspath(
listOf("TestScriptWithReceivers", "TestScriptWithSimpleEnvVars"),
listOf(defsOut), emptyList(), this::class.java.classLoader, emptyMap(), messageCollector.reporter
listOf(defsOut), emptyList(), this::class.java.classLoader, defaultJvmScriptingHostConfiguration, messageCollector.reporter
).toList()
for (def in defClasses) {
@@ -141,7 +142,7 @@ class ScriptingCompilerPluginTest : TestCaseWithTmpdir() {
discoverScriptTemplatesInClasspath(
listOf(defsOut),
this::class.java.classLoader,
emptyMap(),
defaultJvmScriptingHostConfiguration,
messageCollector.reporter
)
@@ -212,7 +213,7 @@ class ScriptingCompilerPluginTest : TestCaseWithTmpdir() {
discoverScriptTemplatesInClasspath(
listOf(defsOut),
this::class.java.classLoader,
emptyMap(),
defaultJvmScriptingHostConfiguration,
messageCollector.reporter
).toList()