Grand refactoring of the scripting DSL + many minor usability refactorings around it
This commit is contained in:
+33
-29
@@ -40,21 +40,28 @@ import kotlin.script.experimental.api.*
|
||||
import kotlin.script.experimental.dependencies.DependenciesResolver
|
||||
import kotlin.script.experimental.host.getMergedScriptText
|
||||
import kotlin.script.experimental.jvm.JvmDependency
|
||||
import kotlin.script.experimental.jvm.JvmScriptCompileConfigurationProperties
|
||||
import kotlin.script.experimental.jvm.JvmScriptCompilationConfiguration
|
||||
import kotlin.script.experimental.jvm.impl.BridgeDependenciesResolver
|
||||
import kotlin.script.experimental.jvmhost.JvmScriptEvaluationEnvironmentProperties
|
||||
import kotlin.script.experimental.jvm.javaHome
|
||||
import kotlin.script.experimental.jvmhost.JvmScriptEvaluationEnvironment
|
||||
import kotlin.script.experimental.jvmhost.KJvmCompilerProxy
|
||||
import kotlin.script.experimental.util.chainPropertyBags
|
||||
import kotlin.script.experimental.jvmhost.baseClassLoader
|
||||
import kotlin.script.experimental.util.getFirstFromChainOrNull
|
||||
import kotlin.script.experimental.util.getMergedFromChainOrNull
|
||||
import kotlin.script.experimental.util.getOrError
|
||||
import kotlin.script.experimental.util.getOrNull
|
||||
|
||||
class KJvmCompiledScript<out ScriptBase : Any>(
|
||||
override val configuration: ScriptCompileConfiguration,
|
||||
val generationState: GenerationState,
|
||||
val scriptClassFQName: String
|
||||
override val definition: ScriptDefinition,
|
||||
override val additionalConfiguration: ScriptCompileConfiguration?,
|
||||
private val generationState: GenerationState,
|
||||
private val scriptClassFQName: String
|
||||
) : CompiledScript<ScriptBase> {
|
||||
|
||||
override suspend fun instantiate(scriptEvaluationEnvironment: ScriptEvaluationEnvironment): ResultWithDiagnostics<ScriptBase> = try {
|
||||
val baseClassLoader = scriptEvaluationEnvironment.getOrNull(JvmScriptEvaluationEnvironmentProperties.baseClassLoader)
|
||||
val dependencies = configuration.getOrNull(ScriptDefinitionProperties.dependencies)
|
||||
override suspend fun instantiate(scriptEvaluationEnvironment: ScriptEvaluationEnvironment?): ResultWithDiagnostics<ScriptBase> = try {
|
||||
val baseClassLoader = scriptEvaluationEnvironment?.getOrNull(JvmScriptEvaluationEnvironment.baseClassLoader)
|
||||
?: Thread.currentThread().contextClassLoader
|
||||
val dependencies = additionalConfiguration?.getOrNull(ScriptDefinition.dependencies)
|
||||
?.flatMap { (it as? JvmDependency)?.classpath?.map { it.toURI().toURL() } ?: emptyList() }
|
||||
// TODO: previous dependencies and classloaders should be taken into account here
|
||||
val classLoaderWithDeps =
|
||||
@@ -75,7 +82,7 @@ class KJvmCompilerImpl(val hostEnvironment: ScriptingEnvironment) : KJvmCompiler
|
||||
override fun compile(
|
||||
script: ScriptSource,
|
||||
scriptDefinition: ScriptDefinition,
|
||||
additionalConfiguration: ScriptCompileConfiguration
|
||||
additionalConfiguration: ScriptCompileConfiguration?
|
||||
): ResultWithDiagnostics<CompiledScript<*>> {
|
||||
val messageCollector = ScriptDiagnosticsMessageCollector()
|
||||
|
||||
@@ -85,37 +92,35 @@ class KJvmCompilerImpl(val hostEnvironment: ScriptingEnvironment) : KJvmCompiler
|
||||
try {
|
||||
setIdeaIoUseFallback()
|
||||
|
||||
val scriptCompileConfiguration = chainPropertyBags(additionalConfiguration, scriptDefinition)
|
||||
var environment: KotlinCoreEnvironment? = null
|
||||
var updatedScriptCompileConfiguration = scriptCompileConfiguration
|
||||
var updatedScriptCompileConfiguration = additionalConfiguration
|
||||
|
||||
fun updateClasspath(classpath: List<File>) {
|
||||
environment!!.updateClasspath(classpath.map(::JvmClasspathRoot))
|
||||
val updatedDeps = updatedScriptCompileConfiguration.getOrNull(ScriptDefinitionProperties.dependencies)?.plus(
|
||||
JvmDependency(classpath)
|
||||
) ?: listOf(JvmDependency(classpath))
|
||||
updatedScriptCompileConfiguration = ScriptCompileConfiguration(
|
||||
updatedScriptCompileConfiguration,
|
||||
ScriptDefinitionProperties.dependencies to updatedDeps
|
||||
)
|
||||
if (classpath.isNotEmpty()) {
|
||||
updatedScriptCompileConfiguration = ScriptCompileConfiguration.create {
|
||||
include(updatedScriptCompileConfiguration)
|
||||
dependencies.append(JvmDependency(classpath))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
val disposable = Disposer.newDisposable()
|
||||
val kotlinCompilerConfiguration = org.jetbrains.kotlin.config.CompilerConfiguration().apply {
|
||||
add(
|
||||
JVMConfigurationKeys.SCRIPT_DEFINITIONS,
|
||||
BridgeScriptDefinition(scriptDefinition, scriptCompileConfiguration, hostEnvironment, ::updateClasspath)
|
||||
BridgeScriptDefinition(scriptDefinition, additionalConfiguration, hostEnvironment, ::updateClasspath)
|
||||
)
|
||||
put<MessageCollector>(CLIConfigurationKeys.MESSAGE_COLLECTOR_KEY, messageCollector)
|
||||
put(JVMConfigurationKeys.RETAIN_OUTPUT_IN_MEMORY, true)
|
||||
|
||||
var isModularJava = false
|
||||
scriptCompileConfiguration.getOrNull(JvmScriptCompileConfigurationProperties.javaHomeDir)?.let {
|
||||
getFirstFromChainOrNull(JvmScriptCompilationConfiguration.javaHome, updatedScriptCompileConfiguration, hostEnvironment)?.let {
|
||||
put(JVMConfigurationKeys.JDK_HOME, it)
|
||||
isModularJava = CoreJrtFileSystem.isModularJdk(it)
|
||||
}
|
||||
|
||||
scriptCompileConfiguration.getOrNull(ScriptDefinitionProperties.dependencies)?.let {
|
||||
getMergedFromChainOrNull(ScriptDefinition.dependencies, scriptDefinition, updatedScriptCompileConfiguration)?.let {
|
||||
addJvmClasspathRoots(
|
||||
it.flatMap {
|
||||
(it as JvmDependency).classpath
|
||||
@@ -147,7 +152,7 @@ class KJvmCompilerImpl(val hostEnvironment: ScriptingEnvironment) : KJvmCompiler
|
||||
val analyzerWithCompilerReport = AnalyzerWithCompilerReport(messageCollector, environment.configuration.languageVersionSettings)
|
||||
|
||||
val psiFileFactory: PsiFileFactoryImpl = PsiFileFactory.getInstance(environment.project) as PsiFileFactoryImpl
|
||||
val scriptText = getMergedScriptText(script, scriptCompileConfiguration)
|
||||
val scriptText = getMergedScriptText(script, updatedScriptCompileConfiguration)
|
||||
val scriptFileName = "script" // TODO: extract from file/url if available
|
||||
val virtualFile = LightVirtualFile(
|
||||
"$scriptFileName${KotlinParserDefinition.STD_SCRIPT_EXT}",
|
||||
@@ -192,7 +197,7 @@ class KJvmCompilerImpl(val hostEnvironment: ScriptingEnvironment) : KJvmCompiler
|
||||
org.jetbrains.kotlin.codegen.CompilationErrorHandler.THROW_EXCEPTION
|
||||
)
|
||||
|
||||
val res = KJvmCompiledScript<Any>(updatedScriptCompileConfiguration, generationState, scriptFileName.capitalize())
|
||||
val res = KJvmCompiledScript<Any>(scriptDefinition, updatedScriptCompileConfiguration, generationState, scriptFileName.capitalize())
|
||||
|
||||
return ResultWithDiagnostics.Success(res, messageCollector.diagnostics)
|
||||
} catch (ex: Throwable) {
|
||||
@@ -238,23 +243,22 @@ class ScriptDiagnosticsMessageCollector : MessageCollector {
|
||||
|
||||
internal class BridgeScriptDefinition(
|
||||
scriptDefinition: ScriptDefinition,
|
||||
calculatedScriptCompilerConfiguration: ScriptCompileConfiguration,
|
||||
additionalCompilationConfiguration: ScriptCompileConfiguration?,
|
||||
hostEnvironment: ScriptingEnvironment,
|
||||
updateClasspath: (List<File>) -> Unit
|
||||
) : KotlinScriptDefinition(
|
||||
hostEnvironment.getScriptingClass(
|
||||
scriptDefinition[ScriptDefinitionProperties.baseClass],
|
||||
scriptDefinition.getOrError(ScriptDefinition.baseClass),
|
||||
BridgeScriptDefinition::class
|
||||
)
|
||||
) {
|
||||
override val acceptedAnnotations = run {
|
||||
val cl = this::class.java.classLoader
|
||||
calculatedScriptCompilerConfiguration.getOrNull(ScriptDefinitionProperties.refineConfigurationOnAnnotations)
|
||||
additionalCompilationConfiguration?.getOrNull(ScriptDefinition.refineConfigurationOnAnnotations)
|
||||
?.map { (cl.loadClass(it.typeName) as Class<out Annotation>).kotlin }
|
||||
?: emptyList()
|
||||
}
|
||||
|
||||
override val dependencyResolver: DependenciesResolver =
|
||||
BridgeDependenciesResolver(scriptDefinition, calculatedScriptCompilerConfiguration, updateClasspath)
|
||||
BridgeDependenciesResolver(scriptDefinition, additionalCompilationConfiguration, updateClasspath)
|
||||
}
|
||||
|
||||
|
||||
+35
@@ -0,0 +1,35 @@
|
||||
/*
|
||||
* Copyright 2010-2018 JetBrains s.r.o. 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 kotlin.script.experimental.api.ScriptCompileConfiguration
|
||||
import kotlin.script.experimental.api.ScriptingEnvironment
|
||||
import kotlin.script.experimental.jvm.defaultJvmScriptingEnvironment
|
||||
|
||||
internal fun mergeConfigurations(vararg configurations: ScriptCompileConfiguration?): ScriptCompileConfiguration? {
|
||||
val nonEmptyConfigurations = configurations.filter { it != null && it.properties.isNotEmpty() }
|
||||
return when {
|
||||
nonEmptyConfigurations.isEmpty() -> null
|
||||
nonEmptyConfigurations.size == 1 -> nonEmptyConfigurations.first()!!
|
||||
else -> ScriptCompileConfiguration.create {
|
||||
for (configuration in nonEmptyConfigurations) {
|
||||
include(configuration!!)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun ScriptingEnvironment.withDefaults(): ScriptingEnvironment =
|
||||
if (this == defaultJvmScriptingEnvironment || defaultJvmScriptingEnvironment.properties.all {
|
||||
this.properties.containsKey(it.key)
|
||||
}) {
|
||||
this
|
||||
} else {
|
||||
ScriptingEnvironment.create {
|
||||
include(defaultJvmScriptingEnvironment)
|
||||
include(this)
|
||||
}
|
||||
}
|
||||
+23
-15
@@ -10,21 +10,29 @@ package kotlin.script.experimental.jvmhost
|
||||
import kotlin.script.experimental.api.*
|
||||
import kotlin.script.experimental.jvm.defaultJvmScriptingEnvironment
|
||||
import kotlin.script.experimental.jvmhost.impl.KJvmCompilerImpl
|
||||
import kotlin.script.experimental.util.chainPropertyBags
|
||||
import kotlin.script.experimental.jvmhost.impl.mergeConfigurations
|
||||
import kotlin.script.experimental.jvmhost.impl.withDefaults
|
||||
import kotlin.script.experimental.util.getOrNull
|
||||
|
||||
interface CompiledJvmScriptsCache {
|
||||
fun get(script: ScriptSource, configuration: ScriptCompileConfiguration): CompiledScript<*>?
|
||||
fun store(compiledScript: CompiledScript<*>, configuration: ScriptCompileConfiguration)
|
||||
fun get(script: ScriptSource, scriptDefinition: ScriptDefinition, configuration: ScriptCompileConfiguration?): CompiledScript<*>?
|
||||
fun store(compiledScript: CompiledScript<*>, scriptDefinition: ScriptDefinition, configuration: ScriptCompileConfiguration?)
|
||||
|
||||
object NoCache : CompiledJvmScriptsCache {
|
||||
override fun get(script: ScriptSource, configuration: ScriptCompileConfiguration): CompiledScript<*>? = null
|
||||
override fun store(compiledScript: CompiledScript<*>, configuration: ScriptCompileConfiguration) {}
|
||||
override fun get(
|
||||
script: ScriptSource, scriptDefinition: ScriptDefinition, configuration: ScriptCompileConfiguration?
|
||||
): CompiledScript<*>? = null
|
||||
|
||||
override fun store(
|
||||
compiledScript: CompiledScript<*>, scriptDefinition: ScriptDefinition, configuration: ScriptCompileConfiguration?
|
||||
) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
open class JvmScriptCompiler(
|
||||
hostEnvironment: ScriptingEnvironment = defaultJvmScriptingEnvironment,
|
||||
val compilerProxy: KJvmCompilerProxy = KJvmCompilerImpl(hostEnvironment.cloneWithNewParent(defaultJvmScriptingEnvironment)),
|
||||
val compilerProxy: KJvmCompilerProxy = KJvmCompilerImpl(hostEnvironment.withDefaults()),
|
||||
val cache: CompiledJvmScriptsCache = CompiledJvmScriptsCache.NoCache
|
||||
) : ScriptCompiler {
|
||||
|
||||
@@ -33,29 +41,29 @@ open class JvmScriptCompiler(
|
||||
scriptDefinition: ScriptDefinition,
|
||||
additionalConfiguration: ScriptCompileConfiguration?
|
||||
): ResultWithDiagnostics<CompiledScript<*>> {
|
||||
val baseConfiguration = chainPropertyBags(additionalConfiguration, scriptDefinition)
|
||||
val refineConfigurationFn = baseConfiguration.getOrNull(ScriptDefinitionProperties.refineConfigurationHandler)
|
||||
val refineConfigurationFn = scriptDefinition.getOrNull(ScriptDefinition.refineConfigurationHandler)
|
||||
val refinedConfiguration =
|
||||
if (baseConfiguration.getOrNull(ScriptDefinitionProperties.refineConfigurationBeforeParsing) == true) {
|
||||
if (scriptDefinition.getOrNull(ScriptDefinition.refineConfigurationBeforeParsing) == true) {
|
||||
if (refineConfigurationFn == null) {
|
||||
return ResultWithDiagnostics.Failure("Non-null configurator expected".asErrorDiagnostics())
|
||||
}
|
||||
refineConfigurationFn(script, baseConfiguration).let {
|
||||
refineConfigurationFn(script, scriptDefinition, additionalConfiguration).let {
|
||||
when (it) {
|
||||
is ResultWithDiagnostics.Failure -> return it
|
||||
is ResultWithDiagnostics.Success -> it.value
|
||||
}
|
||||
}
|
||||
} else {
|
||||
baseConfiguration
|
||||
null
|
||||
}
|
||||
val cached = cache.get(script, refinedConfiguration)
|
||||
val actualConfiguration = mergeConfigurations(additionalConfiguration, refinedConfiguration)
|
||||
val cached = cache.get(script, scriptDefinition, actualConfiguration)
|
||||
|
||||
if (cached != null) return cached.asSuccess()
|
||||
|
||||
return compilerProxy.compile(script, scriptDefinition, refinedConfiguration).also {
|
||||
return compilerProxy.compile(script, scriptDefinition, actualConfiguration).also {
|
||||
if (it is ResultWithDiagnostics.Success) {
|
||||
cache.store(it.value, refinedConfiguration)
|
||||
cache.store(it.value, scriptDefinition, actualConfiguration)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -65,7 +73,7 @@ interface KJvmCompilerProxy {
|
||||
fun compile(
|
||||
script: ScriptSource,
|
||||
scriptDefinition: ScriptDefinition,
|
||||
additionalConfiguration: ScriptCompileConfiguration
|
||||
additionalConfiguration: ScriptCompileConfiguration?
|
||||
): ResultWithDiagnostics<CompiledScript<*>>
|
||||
}
|
||||
|
||||
|
||||
+11
-5
@@ -8,17 +8,23 @@
|
||||
package kotlin.script.experimental.jvmhost
|
||||
|
||||
import kotlin.script.experimental.api.*
|
||||
import kotlin.script.experimental.util.typedKey
|
||||
import kotlin.script.experimental.util.PropertiesCollection
|
||||
import kotlin.script.experimental.util.getOrNull
|
||||
|
||||
object JvmScriptEvaluationEnvironmentProperties {
|
||||
val baseClassLoader by typedKey<ClassLoader?>(Thread.currentThread().contextClassLoader)
|
||||
open class JvmScriptEvaluationEnvironment : PropertiesCollection.Builder() {
|
||||
|
||||
companion object : JvmScriptEvaluationEnvironment()
|
||||
}
|
||||
|
||||
val JvmScriptEvaluationEnvironment.baseClassLoader by PropertiesCollection.key<ClassLoader?>(Thread.currentThread().contextClassLoader)
|
||||
|
||||
val ScriptEvaluationEnvironment.jvm get() = JvmScriptEvaluationEnvironment()
|
||||
|
||||
open class BasicJvmScriptEvaluator : ScriptEvaluator {
|
||||
|
||||
override suspend operator fun invoke(
|
||||
compiledScript: CompiledScript<*>,
|
||||
scriptEvaluationEnvironment: ScriptEvaluationEnvironment
|
||||
scriptEvaluationEnvironment: ScriptEvaluationEnvironment?
|
||||
): ResultWithDiagnostics<EvaluationResult> =
|
||||
try {
|
||||
val obj = compiledScript.instantiate(scriptEvaluationEnvironment)
|
||||
@@ -32,7 +38,7 @@ open class BasicJvmScriptEvaluator : ScriptEvaluator {
|
||||
if (scriptObject !is Class<*>)
|
||||
ResultWithDiagnostics.Failure(ScriptDiagnostic("expecting class in this implementation, got ${scriptObject?.javaClass}"))
|
||||
else {
|
||||
val receivers = scriptEvaluationEnvironment.getOrNull(ScriptEvaluationEnvironmentParams.implicitReceivers)
|
||||
val receivers = scriptEvaluationEnvironment?.getOrNull(ScriptEvaluationEnvironment.implicitReceivers)
|
||||
val instance = if (receivers == null) {
|
||||
scriptObject.getConstructor().newInstance()
|
||||
} else {
|
||||
|
||||
Reference in New Issue
Block a user