From f350c24846d541991a2fd4ac51cc42a054956bf5 Mon Sep 17 00:00:00 2001 From: Ilya Chernikov Date: Tue, 3 Sep 2019 14:46:28 +0200 Subject: [PATCH] Implement more general "apply defaults" functionality for host configurations --- build.gradle.kts | 1 + libraries/scripting/common/build.gradle.kts | 3 +- .../host/configurationFromTemplate.kt | 16 +++---- .../experimental/host/hostConfiguration.kt | 10 +++++ .../experimental/util/propertiesCollection.kt | 4 ++ .../experimental/test/ConfigurationsTest.kt | 42 +++++++++++++++++++ .../experimental/jvmhost/impl/jvmHostUtil.kt | 16 ------- .../jvmhost/jvmScriptCompilation.kt | 4 +- .../jvmhost/repl/legacyReplCompilation.kt | 4 +- 9 files changed, 71 insertions(+), 29 deletions(-) create mode 100644 libraries/scripting/common/test/kotlin/script/experimental/test/ConfigurationsTest.kt delete mode 100644 libraries/scripting/jvm-host/src/kotlin/script/experimental/jvmhost/impl/jvmHostUtil.kt diff --git a/build.gradle.kts b/build.gradle.kts index 8063950619a..b5a0b909c60 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -524,6 +524,7 @@ tasks { dependsOn("dist") dependsOn(":kotlin-script-util:test") dependsOn(":kotlin-scripting-compiler:test") + dependsOn(":kotlin-scripting-common:test") dependsOn(":kotlin-scripting-jvm-host-test:test") dependsOn(":kotlin-scripting-jsr223-test:test") dependsOn(":kotlin-scripting-jvm-host-test:embeddableTest") diff --git a/libraries/scripting/common/build.gradle.kts b/libraries/scripting/common/build.gradle.kts index 8b16936d519..335c4b20a36 100644 --- a/libraries/scripting/common/build.gradle.kts +++ b/libraries/scripting/common/build.gradle.kts @@ -11,11 +11,12 @@ dependencies { compileOnly(project(":kotlin-reflect-api")) compile(commonDep("org.jetbrains.kotlinx", "kotlinx-coroutines-core")) { isTransitive = false } runtime(project(":kotlin-reflect")) + testCompile(commonDep("junit")) } sourceSets { "main" { projectDefault() } - "test" {} + "test" { projectDefault() } } publish() diff --git a/libraries/scripting/common/src/kotlin/script/experimental/host/configurationFromTemplate.kt b/libraries/scripting/common/src/kotlin/script/experimental/host/configurationFromTemplate.kt index 550e5cbe18a..1cefd368696 100644 --- a/libraries/scripting/common/src/kotlin/script/experimental/host/configurationFromTemplate.kt +++ b/libraries/scripting/common/src/kotlin/script/experimental/host/configurationFromTemplate.kt @@ -23,23 +23,23 @@ private abstract class DummyScriptTemplate /** * Creates compilation configuration from annotated script base class * @param baseClassType the annotated script base class to construct the configuration from - * @param hostConfiguration scripting host configuration properties + * @param baseHostConfiguration scripting host configuration properties * @param contextClass optional context class to extract classloading strategy from * @param body optional configuration function to add more properties to the compilation configuration */ fun createCompilationConfigurationFromTemplate( baseClassType: KotlinType, - hostConfiguration: ScriptingHostConfiguration, + baseHostConfiguration: ScriptingHostConfiguration, contextClass: KClass<*> = ScriptCompilationConfiguration::class, body: ScriptCompilationConfiguration.Builder.() -> Unit = {} ): ScriptCompilationConfiguration { - val templateClass: KClass<*> = baseClassType.getTemplateClass(hostConfiguration, contextClass) + val templateClass: KClass<*> = baseClassType.getTemplateClass(baseHostConfiguration, contextClass) val mainAnnotation: KotlinScript = templateClass.kotlinScriptAnnotation return ScriptCompilationConfiguration(scriptConfigInstance(mainAnnotation.compilationConfiguration)) { - hostConfiguration(hostConfiguration) + hostConfiguration.update { it.withDefaultsFrom(baseHostConfiguration) } propertiesFromTemplate(templateClass, baseClassType, mainAnnotation) body() } @@ -48,23 +48,23 @@ fun createCompilationConfigurationFromTemplate( /** * Creates evaluation configuration from annotated script base class * @param baseClassType the annotated script base class to construct the configuration from - * @param hostConfiguration scripting host configuration properties + * @param baseHostConfiguration scripting host configuration properties * @param contextClass optional context class to extract classloading strategy from * @param body optional configuration function to add more properties to the evaluation configuration */ fun createEvaluationConfigurationFromTemplate( baseClassType: KotlinType, - hostConfiguration: ScriptingHostConfiguration, + baseHostConfiguration: ScriptingHostConfiguration, contextClass: KClass<*> = ScriptEvaluationConfiguration::class, body: ScriptEvaluationConfiguration.Builder.() -> Unit = {} ): ScriptEvaluationConfiguration { - val templateClass: KClass<*> = baseClassType.getTemplateClass(hostConfiguration, contextClass) + val templateClass: KClass<*> = baseClassType.getTemplateClass(baseHostConfiguration, contextClass) val mainAnnotation = templateClass.kotlinScriptAnnotation return ScriptEvaluationConfiguration(scriptConfigInstance(mainAnnotation.evaluationConfiguration)) { - hostConfiguration(hostConfiguration) + hostConfiguration.update { it.withDefaultsFrom(baseHostConfiguration) } body() } } diff --git a/libraries/scripting/common/src/kotlin/script/experimental/host/hostConfiguration.kt b/libraries/scripting/common/src/kotlin/script/experimental/host/hostConfiguration.kt index 62bc717952d..c39998d80ba 100644 --- a/libraries/scripting/common/src/kotlin/script/experimental/host/hostConfiguration.kt +++ b/libraries/scripting/common/src/kotlin/script/experimental/host/hostConfiguration.kt @@ -43,6 +43,16 @@ fun ScriptingHostConfiguration?.with(body: ScriptingHostConfiguration.Builder.() return if (newConfiguration != this) newConfiguration else this } +/** + * Add the values not explicitly set in the receiver from the [defaults] configuration + */ +fun ScriptingHostConfiguration?.withDefaultsFrom(defaults: ScriptingHostConfiguration): ScriptingHostConfiguration = + when { + this == defaults || defaults.isEmpty() -> this ?: defaults + this == null || this.isEmpty() -> defaults + else -> ScriptingHostConfiguration(defaults, this) + } + /** * The list of all dependencies required for the script base class and refinement callbacks */ diff --git a/libraries/scripting/common/src/kotlin/script/experimental/util/propertiesCollection.kt b/libraries/scripting/common/src/kotlin/script/experimental/util/propertiesCollection.kt index 4a475591ad5..2b8a3e70abb 100644 --- a/libraries/scripting/common/src/kotlin/script/experimental/util/propertiesCollection.kt +++ b/libraries/scripting/common/src/kotlin/script/experimental/util/propertiesCollection.kt @@ -123,6 +123,10 @@ open class PropertiesCollection(protected var properties: Map, Any?> = em } } + fun PropertiesCollection.Key.update(body: (T?) -> T?) { + putIfNotNull(body(data[this]?.let { it as T })) + } + // generic for lists fun PropertiesCollection.Key>.putIfAny(vals: Iterable?) { diff --git a/libraries/scripting/common/test/kotlin/script/experimental/test/ConfigurationsTest.kt b/libraries/scripting/common/test/kotlin/script/experimental/test/ConfigurationsTest.kt new file mode 100644 index 00000000000..e6aa9828544 --- /dev/null +++ b/libraries/scripting/common/test/kotlin/script/experimental/test/ConfigurationsTest.kt @@ -0,0 +1,42 @@ +/* + * 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.test + +import junit.framework.TestCase +import org.junit.Test +import kotlin.script.experimental.host.ScriptingHostConfiguration +import kotlin.script.experimental.host.ScriptingHostConfigurationKeys +import kotlin.script.experimental.host.withDefaultsFrom +import kotlin.script.experimental.util.PropertiesCollection + +class ConfigurationsTest : TestCase() { + + @Test + fun testHostConfigWithDefaultsFrom() { + val c0 = ScriptingHostConfiguration() + val c1 = ScriptingHostConfiguration { + p1(1) + } + val c2 = ScriptingHostConfiguration { + p1(2) + p2("yes") + } + + assertEquals(c1, c1.withDefaultsFrom(c0)) + assertEquals(c1, c0.withDefaultsFrom(c1)) + assertEquals(c1, c1.withDefaultsFrom(c1)) + + val c1c2 = c1.withDefaultsFrom(c2) + val c2c1 = c2.withDefaultsFrom(c1) + + assertEquals(1, c1c2[ScriptingHostConfiguration.p1]) + assertEquals("yes", c1c2[ScriptingHostConfiguration.p2]) + assertEquals(2, c2c1[ScriptingHostConfiguration.p1]) + } +} + +private val ScriptingHostConfigurationKeys.p1 by PropertiesCollection.key(-1) +private val ScriptingHostConfigurationKeys.p2 by PropertiesCollection.key("-") diff --git a/libraries/scripting/jvm-host/src/kotlin/script/experimental/jvmhost/impl/jvmHostUtil.kt b/libraries/scripting/jvm-host/src/kotlin/script/experimental/jvmhost/impl/jvmHostUtil.kt deleted file mode 100644 index a2e604f23f4..00000000000 --- a/libraries/scripting/jvm-host/src/kotlin/script/experimental/jvmhost/impl/jvmHostUtil.kt +++ /dev/null @@ -1,16 +0,0 @@ -/* - * Copyright 2010-2018 JetBrains s.r.o. and Kotlin Programming Language contributors. - * Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file. - */ - -package kotlin.script.experimental.jvmhost.impl - -import kotlin.script.experimental.host.ScriptingHostConfiguration -import kotlin.script.experimental.jvm.defaultJvmScriptingHostConfiguration - -fun ScriptingHostConfiguration.withDefaults(): ScriptingHostConfiguration = - if (this == defaultJvmScriptingHostConfiguration) { - this - } else { - ScriptingHostConfiguration(defaultJvmScriptingHostConfiguration, this) - } diff --git a/libraries/scripting/jvm-host/src/kotlin/script/experimental/jvmhost/jvmScriptCompilation.kt b/libraries/scripting/jvm-host/src/kotlin/script/experimental/jvmhost/jvmScriptCompilation.kt index 92f68c66ffb..e1e3726e81b 100644 --- a/libraries/scripting/jvm-host/src/kotlin/script/experimental/jvmhost/jvmScriptCompilation.kt +++ b/libraries/scripting/jvm-host/src/kotlin/script/experimental/jvmhost/jvmScriptCompilation.kt @@ -11,15 +11,15 @@ import org.jetbrains.kotlin.scripting.compiler.plugin.ScriptJvmCompilerProxy import org.jetbrains.kotlin.scripting.compiler.plugin.impl.ScriptJvmCompilerIsolated import kotlin.script.experimental.api.* import kotlin.script.experimental.host.ScriptingHostConfiguration +import kotlin.script.experimental.host.withDefaultsFrom import kotlin.script.experimental.jvm.defaultJvmScriptingHostConfiguration -import kotlin.script.experimental.jvmhost.impl.withDefaults open class JvmScriptCompiler( baseHostConfiguration: ScriptingHostConfiguration = defaultJvmScriptingHostConfiguration, compilerProxy: ScriptJvmCompilerProxy? = null ) : ScriptCompiler { - val hostConfiguration = baseHostConfiguration.withDefaults() + val hostConfiguration = baseHostConfiguration.withDefaultsFrom(defaultJvmScriptingHostConfiguration) val compilerProxy: ScriptJvmCompilerProxy = compilerProxy ?: ScriptJvmCompilerIsolated(hostConfiguration) diff --git a/libraries/scripting/jvm-host/src/kotlin/script/experimental/jvmhost/repl/legacyReplCompilation.kt b/libraries/scripting/jvm-host/src/kotlin/script/experimental/jvmhost/repl/legacyReplCompilation.kt index ba7243e334f..206ddc075c6 100644 --- a/libraries/scripting/jvm-host/src/kotlin/script/experimental/jvmhost/repl/legacyReplCompilation.kt +++ b/libraries/scripting/jvm-host/src/kotlin/script/experimental/jvmhost/repl/legacyReplCompilation.kt @@ -13,8 +13,8 @@ import java.util.concurrent.locks.ReentrantReadWriteLock import kotlin.concurrent.write import kotlin.script.experimental.api.* import kotlin.script.experimental.host.ScriptingHostConfiguration +import kotlin.script.experimental.host.withDefaultsFrom import kotlin.script.experimental.jvm.defaultJvmScriptingHostConfiguration -import kotlin.script.experimental.jvmhost.impl.withDefaults /** * REPL Compilation wrapper for "legacy" REPL APIs defined in the org.jetbrains.kotlin.cli.common.repl package @@ -23,7 +23,7 @@ class JvmReplCompiler( val scriptCompilationConfiguration: ScriptCompilationConfiguration, val hostConfiguration: ScriptingHostConfiguration = defaultJvmScriptingHostConfiguration, val replCompilerProxy: KJvmReplCompilerProxy = KJvmReplCompilerImpl( - hostConfiguration.withDefaults() + hostConfiguration.withDefaultsFrom(defaultJvmScriptingHostConfiguration) ) ) : ReplCompiler {