From 2a5c4a2409b170f127758786ada43f33e71b82b1 Mon Sep 17 00:00:00 2001 From: Ilya Chernikov Date: Wed, 24 Jul 2019 20:34:26 +0200 Subject: [PATCH] Move caching to after all configuration refinements and ... move compilation cache setting to the scripting host configuration The cache implementation can now take into account all external dependencies that may appear during refinement. Also change initial refinement location accordingly and rearrange caching interfaces to make it available in scripting plugin. --- .../experimental/jvmhost/test/CachingTest.kt | 9 ++-- ...tCaching.kt => CompiledScriptJarsCache.kt} | 4 +- .../jvmhost/jvmScriptCompilation.kt | 43 +++------------- .../experimental/jvm/jvmScriptCaching.kt | 30 +++++++++++ .../plugin/impl/ScriptJvmCompilerImpls.kt | 51 ++++++++++++++++--- 5 files changed, 88 insertions(+), 49 deletions(-) rename libraries/scripting/jvm-host/src/kotlin/script/experimental/jvmhost/{jvmScriptCaching.kt => CompiledScriptJarsCache.kt} (94%) create mode 100644 libraries/scripting/jvm/src/kotlin/script/experimental/jvm/jvmScriptCaching.kt diff --git a/libraries/scripting/jvm-host-test/test/kotlin/script/experimental/jvmhost/test/CachingTest.kt b/libraries/scripting/jvm-host-test/test/kotlin/script/experimental/jvmhost/test/CachingTest.kt index 188fd2c5d93..4b4182f681c 100644 --- a/libraries/scripting/jvm-host-test/test/kotlin/script/experimental/jvmhost/test/CachingTest.kt +++ b/libraries/scripting/jvm-host-test/test/kotlin/script/experimental/jvmhost/test/CachingTest.kt @@ -18,12 +18,10 @@ import kotlin.script.experimental.api.* import kotlin.script.experimental.host.toScriptSource import kotlin.script.experimental.host.with import kotlin.script.experimental.jvm.* +import kotlin.script.experimental.jvm.BasicJvmScriptEvaluator import kotlin.script.experimental.jvm.impl.KJvmCompiledScript import kotlin.script.experimental.jvm.util.KotlinJars -import kotlin.script.experimental.jvmhost.BasicJvmScriptingHost -import kotlin.script.experimental.jvmhost.CompiledJvmScriptsCache -import kotlin.script.experimental.jvmhost.CompiledScriptJarsCache -import kotlin.script.experimental.jvmhost.JvmScriptCompiler +import kotlin.script.experimental.jvmhost.* class CachingTest : TestCase() { @@ -104,9 +102,10 @@ class CachingTest : TestCase() { val hostConfiguration = defaultJvmScriptingHostConfiguration.with { jvm { baseClassLoader.replaceOnlyDefault(null) + compilationCache(cache) } } - val compiler = JvmScriptCompiler(hostConfiguration, cache = cache) + val compiler = JvmScriptCompiler(hostConfiguration) val evaluator = BasicJvmScriptEvaluator() val host = BasicJvmScriptingHost(compiler = compiler, evaluator = evaluator) diff --git a/libraries/scripting/jvm-host/src/kotlin/script/experimental/jvmhost/jvmScriptCaching.kt b/libraries/scripting/jvm-host/src/kotlin/script/experimental/jvmhost/CompiledScriptJarsCache.kt similarity index 94% rename from libraries/scripting/jvm-host/src/kotlin/script/experimental/jvmhost/jvmScriptCaching.kt rename to libraries/scripting/jvm-host/src/kotlin/script/experimental/jvmhost/CompiledScriptJarsCache.kt index 84a54f01f80..cba693e6e18 100644 --- a/libraries/scripting/jvm-host/src/kotlin/script/experimental/jvmhost/jvmScriptCaching.kt +++ b/libraries/scripting/jvm-host/src/kotlin/script/experimental/jvmhost/CompiledScriptJarsCache.kt @@ -13,12 +13,14 @@ import kotlin.script.experimental.api.ScriptCompilationConfiguration import kotlin.script.experimental.api.SourceCode import kotlin.script.experimental.api.hostConfiguration import kotlin.script.experimental.host.ScriptingHostConfiguration +import kotlin.script.experimental.jvm.CompiledJvmScriptsCache import kotlin.script.experimental.jvm.baseClassLoader import kotlin.script.experimental.jvm.impl.KJvmCompiledScript import kotlin.script.experimental.jvm.impl.createScriptFromClassLoader import kotlin.script.experimental.jvm.jvm -open class CompiledScriptJarsCache(val scriptToFile: (SourceCode, ScriptCompilationConfiguration) -> File?) : CompiledJvmScriptsCache { +open class CompiledScriptJarsCache(val scriptToFile: (SourceCode, ScriptCompilationConfiguration) -> File?) : + CompiledJvmScriptsCache { override fun get(script: SourceCode, scriptCompilationConfiguration: ScriptCompilationConfiguration): CompiledScript<*>? { val file = scriptToFile(script, scriptCompilationConfiguration) 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 3ca7c6d1a7b..92f68c66ffb 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 @@ -14,26 +14,9 @@ import kotlin.script.experimental.host.ScriptingHostConfiguration import kotlin.script.experimental.jvm.defaultJvmScriptingHostConfiguration import kotlin.script.experimental.jvmhost.impl.withDefaults -interface CompiledJvmScriptsCache { - fun get(script: SourceCode, scriptCompilationConfiguration: ScriptCompilationConfiguration): CompiledScript<*>? - fun store(compiledScript: CompiledScript<*>, script: SourceCode, scriptCompilationConfiguration: ScriptCompilationConfiguration) - - object NoCache : CompiledJvmScriptsCache { - override fun get( - script: SourceCode, scriptCompilationConfiguration: ScriptCompilationConfiguration - ): CompiledScript<*>? = null - - override fun store( - compiledScript: CompiledScript<*>, script: SourceCode, scriptCompilationConfiguration: ScriptCompilationConfiguration - ) { - } - } -} - open class JvmScriptCompiler( baseHostConfiguration: ScriptingHostConfiguration = defaultJvmScriptingHostConfiguration, - compilerProxy: ScriptJvmCompilerProxy? = null, - val cache: CompiledJvmScriptsCache = CompiledJvmScriptsCache.NoCache + compilerProxy: ScriptJvmCompilerProxy? = null ) : ScriptCompiler { val hostConfiguration = baseHostConfiguration.withDefaults() @@ -43,24 +26,12 @@ open class JvmScriptCompiler( override suspend operator fun invoke( script: SourceCode, scriptCompilationConfiguration: ScriptCompilationConfiguration - ): ResultWithDiagnostics> { - - // TODO: implement caching deeper in the compilation pipeline - the actual configuration should be calculated first, with dependencies and imported scripts - // Note that previous implementation wasn't a solution for that and added problems with cache usage. Now it is consistent although shallow. - - val cached = cache.get(script, scriptCompilationConfiguration) - if (cached != null) return cached.asSuccess() - - return scriptCompilationConfiguration.with { - hostConfiguration(this@JvmScriptCompiler.hostConfiguration) - }.refineBeforeParsing(script).onSuccess { refinedConfiguration -> - - compilerProxy.compile(script, refinedConfiguration).also { - if (it is ResultWithDiagnostics.Success) { - cache.store(it.value, script, scriptCompilationConfiguration) - } + ): ResultWithDiagnostics> = + compilerProxy.compile( + script, + scriptCompilationConfiguration.with { + hostConfiguration(this@JvmScriptCompiler.hostConfiguration) } - } - } + ) } diff --git a/libraries/scripting/jvm/src/kotlin/script/experimental/jvm/jvmScriptCaching.kt b/libraries/scripting/jvm/src/kotlin/script/experimental/jvm/jvmScriptCaching.kt new file mode 100644 index 00000000000..c1afc9f9a95 --- /dev/null +++ b/libraries/scripting/jvm/src/kotlin/script/experimental/jvm/jvmScriptCaching.kt @@ -0,0 +1,30 @@ +/* + * 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.jvm + +import kotlin.script.experimental.api.CompiledScript +import kotlin.script.experimental.api.ScriptCompilationConfiguration +import kotlin.script.experimental.api.SourceCode +import kotlin.script.experimental.util.PropertiesCollection + +interface CompiledJvmScriptsCache { + fun get(script: SourceCode, scriptCompilationConfiguration: ScriptCompilationConfiguration): CompiledScript<*>? + fun store(compiledScript: CompiledScript<*>, script: SourceCode, scriptCompilationConfiguration: ScriptCompilationConfiguration) + + object NoCache : CompiledJvmScriptsCache { + override fun get( + script: SourceCode, scriptCompilationConfiguration: ScriptCompilationConfiguration + ): CompiledScript<*>? = null + + override fun store( + compiledScript: CompiledScript<*>, script: SourceCode, scriptCompilationConfiguration: ScriptCompilationConfiguration + ) { + } + } +} + +val JvmScriptingHostConfigurationKeys.compilationCache by PropertiesCollection.key(isTransient = true) + diff --git a/plugins/scripting/scripting-compiler/src/org/jetbrains/kotlin/scripting/compiler/plugin/impl/ScriptJvmCompilerImpls.kt b/plugins/scripting/scripting-compiler/src/org/jetbrains/kotlin/scripting/compiler/plugin/impl/ScriptJvmCompilerImpls.kt index 562776374e0..32ee8e746e1 100644 --- a/plugins/scripting/scripting-compiler/src/org/jetbrains/kotlin/scripting/compiler/plugin/impl/ScriptJvmCompilerImpls.kt +++ b/plugins/scripting/scripting-compiler/src/org/jetbrains/kotlin/scripting/compiler/plugin/impl/ScriptJvmCompilerImpls.kt @@ -18,9 +18,13 @@ import org.jetbrains.kotlin.config.CompilerConfiguration import org.jetbrains.kotlin.config.languageVersionSettings import org.jetbrains.kotlin.psi.KtFile import org.jetbrains.kotlin.scripting.compiler.plugin.ScriptJvmCompilerProxy +import org.jetbrains.kotlin.scripting.compiler.plugin.dependencies.ScriptsCompilationDependencies import org.jetbrains.kotlin.scripting.definitions.ScriptDependenciesProvider import kotlin.script.experimental.api.* import kotlin.script.experimental.host.ScriptingHostConfiguration +import kotlin.script.experimental.jvm.compilationCache +import kotlin.script.experimental.jvm.impl.KJvmCompiledScript +import kotlin.script.experimental.jvm.jvm class ScriptJvmCompilerIsolated(val hostConfiguration: ScriptingHostConfiguration) : ScriptJvmCompilerProxy { @@ -30,8 +34,12 @@ class ScriptJvmCompilerIsolated(val hostConfiguration: ScriptingHostConfiguratio ): ResultWithDiagnostics> = withMessageCollectorAndDisposable(script = script) { messageCollector, disposable -> + val initialConfiguration = scriptCompilationConfiguration.refineBeforeParsing(script).valueOr { + return it + } + val context = createIsolatedCompilationContext( - scriptCompilationConfiguration, hostConfiguration, messageCollector, disposable + initialConfiguration, hostConfiguration, messageCollector, disposable ) compileImpl(script, context, messageCollector) @@ -47,7 +55,11 @@ class ScriptJvmCompilerFromEnvironment(val environment: KotlinCoreEnvironment) : val parentMessageCollector = environment.configuration[CLIConfigurationKeys.MESSAGE_COLLECTOR_KEY] return withMessageCollector(script = script, parentMessageCollector = parentMessageCollector) { messageCollector -> - val context = createCompilationContextFromEnvironment(scriptCompilationConfiguration, environment, messageCollector) + val initialConfiguration = scriptCompilationConfiguration.refineBeforeParsing(script).valueOr { + return it + } + + val context = createCompilationContextFromEnvironment(initialConfiguration, environment, messageCollector) try { environment.configuration.put(CLIConfigurationKeys.MESSAGE_COLLECTOR_KEY, messageCollector) @@ -83,6 +95,33 @@ private fun compileImpl( ) val dependenciesProvider = ScriptDependenciesProvider.getInstance(context.environment.project) + val getScriptConfiguration = { ktFile: KtFile -> + dependenciesProvider?.getScriptConfigurationResult(ktFile)?.valueOrNull()?.configuration + ?: context.baseScriptCompilationConfiguration + } + + val refinedConfiguration = getScriptConfiguration(mainKtFile) + + val cache = refinedConfiguration[ScriptCompilationConfiguration.hostConfiguration]?.get(ScriptingHostConfiguration.jvm.compilationCache) + + val cached = cache?.get(script, refinedConfiguration) + + return cached?.asSuccess(messageCollector.diagnostics) + ?: doCompile(context, script, sourceFiles, sourceDependencies, messageCollector, getScriptConfiguration).also { + if (cache != null && it is ResultWithDiagnostics.Success) { + cache.store(it.value, script, refinedConfiguration) + } + } +} + +private fun doCompile( + context: SharedScriptCompilationContext, + script: SourceCode, + sourceFiles: List, + sourceDependencies: List, + messageCollector: ScriptDiagnosticsMessageCollector, + getScriptConfiguration: (KtFile) -> ScriptCompilationConfiguration +): ResultWithDiagnostics> { val analysisResult = analyze(sourceFiles, context.environment) if (!analysisResult.shouldGenerateCode) return failure( @@ -102,11 +141,9 @@ private fun compileImpl( generationState, script, sourceFiles.first(), - sourceDependencies - ) { ktFile -> - dependenciesProvider?.getScriptConfigurationResult(ktFile)?.valueOrNull()?.configuration - ?: context.baseScriptCompilationConfiguration - } + sourceDependencies, + getScriptConfiguration + ) return ResultWithDiagnostics.Success(compiledScript, messageCollector.diagnostics) }