From fbb2b260ddf697952c76b1ac4c0834409101307a Mon Sep 17 00:00:00 2001 From: Sergey Rostov Date: Mon, 4 Nov 2019 13:35:12 +0300 Subject: [PATCH] Split script configuration state into applied and loaded to make markUpToDate more clear --- .../AbstractScriptConfigurationManager.kt | 33 ++++++++------- .../DefaultScriptConfigurationManager.kt | 16 ++++---- .../cache/ScriptConfigurationCache.kt | 18 +++++--- .../cache/ScriptConfigurationMemoryCache.kt | 41 +++++++++++-------- .../utils/ScriptClassRootsCache.kt | 4 +- 5 files changed, 65 insertions(+), 47 deletions(-) diff --git a/idea/idea-core/src/org/jetbrains/kotlin/idea/core/script/configuration/AbstractScriptConfigurationManager.kt b/idea/idea-core/src/org/jetbrains/kotlin/idea/core/script/configuration/AbstractScriptConfigurationManager.kt index 4ac9478f5f0..2fff0534bd1 100644 --- a/idea/idea-core/src/org/jetbrains/kotlin/idea/core/script/configuration/AbstractScriptConfigurationManager.kt +++ b/idea/idea-core/src/org/jetbrains/kotlin/idea/core/script/configuration/AbstractScriptConfigurationManager.kt @@ -20,9 +20,9 @@ import kotlinx.coroutines.launch import org.jetbrains.annotations.TestOnly import org.jetbrains.kotlin.idea.core.script.* import org.jetbrains.kotlin.idea.core.script.ScriptConfigurationManager.Companion.toVfsRoots -import org.jetbrains.kotlin.idea.core.script.configuration.cache.CachedConfigurationInputs import org.jetbrains.kotlin.idea.core.script.configuration.cache.ScriptConfigurationCache import org.jetbrains.kotlin.idea.core.script.configuration.cache.ScriptConfigurationSnapshot +import org.jetbrains.kotlin.idea.core.script.configuration.cache.ScriptConfigurationState import org.jetbrains.kotlin.idea.core.script.configuration.listener.ScriptConfigurationUpdater import org.jetbrains.kotlin.idea.core.script.configuration.utils.ScriptClassRootsCache import org.jetbrains.kotlin.idea.core.script.configuration.utils.ScriptClassRootsIndexer @@ -40,7 +40,7 @@ import kotlin.concurrent.withLock * Among this two methods concrete implementation should provide script changes listening (by calling [updater] on some event). * * Basically all requests routed to [cache]. If there is no entry in [cache] or it is considered out-of-date, - * then [reloadOutOfDateConfiguration] will be called, which, in turn, should call [saveChangedConfiguration] + * then [reloadOutOfDateConfiguration] will be called, which, in turn, should call [setAppliedConfiguration] * immediately or in some future (e.g. after user will click "apply context" or/and configuration will * be calculated by some background thread). * @@ -61,7 +61,7 @@ internal abstract class AbstractScriptConfigurationManager( /** * Will be called on [cache] miss or when [file] is changed. - * Implementation should initiate loading of [file]'s script configuration and call [saveChangedConfiguration] + * Implementation should initiate loading of [file]'s script configuration and call [setAppliedConfiguration] * immediately or in some future * (e.g. after user will click "apply context" or/and configuration will be calculated by some background thread). * @@ -73,7 +73,7 @@ internal abstract class AbstractScriptConfigurationManager( */ protected abstract fun reloadOutOfDateConfiguration( file: KtFile, - isFirstLoad: Boolean = getCachedConfiguration(file.originalFile.virtualFile) == null, + isFirstLoad: Boolean = getAppliedConfiguration(file.originalFile.virtualFile) == null, loadEvenWillNotBeApplied: Boolean = false, forceSync: Boolean = false ) @@ -87,13 +87,16 @@ internal abstract class AbstractScriptConfigurationManager( override fun getScriptClasspath(file: KtFile): List = toVfsRoots(getConfiguration(file)?.dependenciesClassPath.orEmpty()) - fun getCachedConfiguration(file: VirtualFile?): ScriptConfigurationSnapshot? { + fun getCachedConfigurationState(file: VirtualFile?): ScriptConfigurationState? { if (file == null) return null return cache[file] } + fun getAppliedConfiguration(file: VirtualFile?): ScriptConfigurationSnapshot? = + getCachedConfigurationState(file)?.applied + override fun hasConfiguration(file: KtFile): Boolean { - return getCachedConfiguration(file.originalFile.virtualFile) != null + return getAppliedConfiguration(file.originalFile.virtualFile) != null } override fun getConfiguration(file: KtFile): ScriptCompilationConfigurationWrapper? { @@ -104,7 +107,7 @@ internal abstract class AbstractScriptConfigurationManager( virtualFile: VirtualFile, preloadedKtFile: KtFile? = null ): ScriptCompilationConfigurationWrapper? { - val cached = getCachedConfiguration(virtualFile) + val cached = getAppliedConfiguration(virtualFile) if (cached != null) return cached.configuration val ktFile = project.getKtFile(virtualFile, preloadedKtFile) ?: return null @@ -112,7 +115,7 @@ internal abstract class AbstractScriptConfigurationManager( reloadOutOfDateConfiguration(ktFile, isFirstLoad = true) } - return getCachedConfiguration(virtualFile)?.configuration + return getAppliedConfiguration(virtualFile)?.configuration } override val updater: ScriptConfigurationUpdater = object : ScriptConfigurationUpdater { @@ -143,7 +146,7 @@ internal abstract class AbstractScriptConfigurationManager( val virtualFile = file.originalFile.virtualFile if (virtualFile != null) { val state = cache[virtualFile] - if (state == null || !state.inputs.isUpToDate(project, virtualFile, file)) { + if (state == null || !state.isUpToDate(project, virtualFile, file)) { upToDate = false reloadOutOfDateConfiguration( file, @@ -165,14 +168,14 @@ internal abstract class AbstractScriptConfigurationManager( file as? KtFile ?: error("PsiFile $file should be a KtFile, otherwise script dependencies cannot be loaded") val virtualFile = file.virtualFile - if (cache[virtualFile]?.inputs?.isUpToDate(project, virtualFile, file) == true) return + if (cache[virtualFile]?.isUpToDate(project, virtualFile, file) == true) return rootsIndexer.transaction { reloadOutOfDateConfiguration(file, isFirstLoad = true, forceSync = true) } } - protected open fun saveChangedConfiguration( + protected open fun setAppliedConfiguration( file: VirtualFile, newConfigurationSnapshot: ScriptConfigurationSnapshot? ) { @@ -185,7 +188,7 @@ internal abstract class AbstractScriptConfigurationManager( rootsIndexer.markNewRoot(file, newConfiguration) } - cache[file] = newConfigurationSnapshot + cache.setApplied(file, newConfigurationSnapshot) clearClassRootsCaches() } @@ -193,11 +196,11 @@ internal abstract class AbstractScriptConfigurationManager( updateHighlighting(listOf(file)) } - protected fun markUpToDate( + protected fun setLoadedConfiguration( file: VirtualFile, - inputs: CachedConfigurationInputs + configurationSnapshot: ScriptConfigurationSnapshot ) { - cache.markUpToDate(file, inputs) + cache.setLoaded(file, configurationSnapshot) } private fun hasNotCachedRoots(configuration: ScriptCompilationConfigurationWrapper): Boolean { diff --git a/idea/idea-core/src/org/jetbrains/kotlin/idea/core/script/configuration/DefaultScriptConfigurationManager.kt b/idea/idea-core/src/org/jetbrains/kotlin/idea/core/script/configuration/DefaultScriptConfigurationManager.kt index 389d014c543..87b761250fc 100644 --- a/idea/idea-core/src/org/jetbrains/kotlin/idea/core/script/configuration/DefaultScriptConfigurationManager.kt +++ b/idea/idea-core/src/org/jetbrains/kotlin/idea/core/script/configuration/DefaultScriptConfigurationManager.kt @@ -141,7 +141,7 @@ internal class DefaultScriptConfigurationManager(project: Project) : * and file will be up-to-date checked again. This will happen after current loading, * because only `backgroundExecutor` execute tasks in one thread. * - `invalid, waiting for apply`: - * Loading will not be queued, since we are marking file is up to date with + * Loading will not be queued, since we are marking file as up-to-date with * not yet applied configuration. * * Sync: @@ -177,8 +177,8 @@ internal class DefaultScriptConfigurationManager(project: Project) : backgroundExecutor.ensureScheduled(virtualFile) { // don't start loading if nothing was changed // (in case we checking for up-to-date and loading concurrently) - val cached = getCachedConfiguration(virtualFile) - if (cached == null || !cached.inputs.isUpToDate(project, virtualFile)) { + val cached = getCachedConfigurationState(virtualFile) + if (cached == null || !cached.isUpToDate(project, virtualFile)) { val actualIsFirstLoad = cached == null async.first { it.loadDependencies(actualIsFirstLoad, virtualFile, scriptDefinition, loadingContext) } } @@ -202,7 +202,7 @@ internal class DefaultScriptConfigurationManager(project: Project) : private val loadingContext = object : ScriptConfigurationLoadingContext { override fun getCachedConfiguration(file: VirtualFile): ScriptConfigurationSnapshot? = - this@DefaultScriptConfigurationManager.getCachedConfiguration(file) + this@DefaultScriptConfigurationManager.getAppliedConfiguration(file) override fun suggestNewConfiguration(file: VirtualFile, newResult: ScriptConfigurationSnapshot) { suggestOrSaveConfiguration(file, newResult, false) @@ -221,13 +221,13 @@ internal class DefaultScriptConfigurationManager(project: Project) : saveLock.withLock { debug(file) { "configuration received = $newResult" } - markUpToDate(file, newResult.inputs) + setLoadedConfiguration(file, newResult) val newConfiguration = newResult.configuration if (newConfiguration == null) { saveReports(file, newResult.reports) } else { - val oldConfiguration = getCachedConfiguration(file)?.configuration + val oldConfiguration = getAppliedConfiguration(file)?.configuration if (oldConfiguration == newConfiguration) { saveReports(file, newResult.reports) file.removeScriptDependenciesNotificationPanel(project) @@ -242,7 +242,7 @@ internal class DefaultScriptConfigurationManager(project: Project) : file.removeScriptDependenciesNotificationPanel(project) } saveReports(file, newResult.reports) - saveChangedConfiguration(file, newResult) + setAppliedConfiguration(file, newResult) } else { debug(file) { "configuration changed, notification is shown: old = $oldConfiguration, new = $newConfiguration" @@ -253,7 +253,7 @@ internal class DefaultScriptConfigurationManager(project: Project) : saveReports(file, newResult.reports) file.removeScriptDependenciesNotificationPanel(project) rootsIndexer.transaction { - saveChangedConfiguration(file, newResult) + setAppliedConfiguration(file, newResult) } } ) diff --git a/idea/idea-core/src/org/jetbrains/kotlin/idea/core/script/configuration/cache/ScriptConfigurationCache.kt b/idea/idea-core/src/org/jetbrains/kotlin/idea/core/script/configuration/cache/ScriptConfigurationCache.kt index fd1b5845586..2718c07cbcb 100644 --- a/idea/idea-core/src/org/jetbrains/kotlin/idea/core/script/configuration/cache/ScriptConfigurationCache.kt +++ b/idea/idea-core/src/org/jetbrains/kotlin/idea/core/script/configuration/cache/ScriptConfigurationCache.kt @@ -30,6 +30,14 @@ data class ScriptConfigurationSnapshot( val configuration: ScriptCompilationConfigurationWrapper? ) +data class ScriptConfigurationState( + val applied: ScriptConfigurationSnapshot? = null, + val loaded: ScriptConfigurationSnapshot? = null +) { + fun isUpToDate(project: Project, file: VirtualFile, ktFile: KtFile? = null): Boolean = + (loaded ?: applied)?.inputs?.isUpToDate(project, file, ktFile) ?: false +} + /** * Cached configurations for file's specific snapshot state. * @@ -37,11 +45,11 @@ data class ScriptConfigurationSnapshot( * Reader may do up-to-date check for existed entry. */ interface ScriptConfigurationCache { - operator fun get(file: VirtualFile): ScriptConfigurationSnapshot? - operator fun set(file: VirtualFile, configurationSnapshot: ScriptConfigurationSnapshot) + operator fun get(file: VirtualFile): ScriptConfigurationState? - fun markUpToDate(file: VirtualFile, inputs: CachedConfigurationInputs) - fun markOutOfDate(file: VirtualFile) = markUpToDate(file, CachedConfigurationInputs.OutOfDate) + fun setApplied(file: VirtualFile, configurationSnapshot: ScriptConfigurationSnapshot) + fun setLoaded(file: VirtualFile, configurationSnapshot: ScriptConfigurationSnapshot) + fun markOutOfDate(file: VirtualFile) - fun all(): Collection> + fun allApplied(): Collection> } \ No newline at end of file diff --git a/idea/idea-core/src/org/jetbrains/kotlin/idea/core/script/configuration/cache/ScriptConfigurationMemoryCache.kt b/idea/idea-core/src/org/jetbrains/kotlin/idea/core/script/configuration/cache/ScriptConfigurationMemoryCache.kt index 8ee31b1ed74..1f2fd78f919 100644 --- a/idea/idea-core/src/org/jetbrains/kotlin/idea/core/script/configuration/cache/ScriptConfigurationMemoryCache.kt +++ b/idea/idea-core/src/org/jetbrains/kotlin/idea/core/script/configuration/cache/ScriptConfigurationMemoryCache.kt @@ -17,34 +17,41 @@ class ScriptConfigurationMemoryCache( const val MAX_SCRIPTS_CACHED = 50 } - private val memoryCache = SLRUMap(MAX_SCRIPTS_CACHED, MAX_SCRIPTS_CACHED) + private val memoryCache = SLRUMap(MAX_SCRIPTS_CACHED, MAX_SCRIPTS_CACHED) @Synchronized - override operator fun get(file: VirtualFile): ScriptConfigurationSnapshot? { + override operator fun get(file: VirtualFile): ScriptConfigurationState? { return memoryCache.get(file) } - @Synchronized - override operator fun set(file: VirtualFile, configurationSnapshot: ScriptConfigurationSnapshot) { - memoryCache.put( - file, - configurationSnapshot - ) + override fun setApplied(file: VirtualFile, configurationSnapshot: ScriptConfigurationSnapshot) { + val old = memoryCache[file] ?: ScriptConfigurationState() + memoryCache.put(file, old.copy(applied = configurationSnapshot)) } - override fun markUpToDate(file: VirtualFile, inputs: CachedConfigurationInputs) { - val old = memoryCache[file] - if (old != null) { - memoryCache.put(file, old.copy(inputs = inputs)) - } + @Synchronized + override fun setLoaded(file: VirtualFile, configurationSnapshot: ScriptConfigurationSnapshot) { + val old = memoryCache[file] ?: ScriptConfigurationState() + memoryCache.put(file, old.copy(loaded = configurationSnapshot)) + } + + @Synchronized + override fun markOutOfDate(file: VirtualFile) { + val old = memoryCache[file] ?: ScriptConfigurationState() + memoryCache.put( + file, old.copy( + applied = old.applied?.copy(inputs = CachedConfigurationInputs.OutOfDate), + loaded = old.loaded?.copy(inputs = CachedConfigurationInputs.OutOfDate) + ) + ) } @Synchronized @Suppress("UNCHECKED_CAST") - override fun all() = - memoryCache.entrySet().map { - if (it.value.configuration == null) null - else it.key to it.value.configuration + override fun allApplied() = + memoryCache.entrySet().mapNotNull { + if (it.value.applied?.configuration == null) null + else it.key to it.value.applied?.configuration } as Collection> } \ No newline at end of file diff --git a/idea/idea-core/src/org/jetbrains/kotlin/idea/core/script/configuration/utils/ScriptClassRootsCache.kt b/idea/idea-core/src/org/jetbrains/kotlin/idea/core/script/configuration/utils/ScriptClassRootsCache.kt index c7618ae6b0e..82dc8d28ec1 100644 --- a/idea/idea-core/src/org/jetbrains/kotlin/idea/core/script/configuration/utils/ScriptClassRootsCache.kt +++ b/idea/idea-core/src/org/jetbrains/kotlin/idea/core/script/configuration/utils/ScriptClassRootsCache.kt @@ -25,8 +25,8 @@ internal class ScriptClassRootsCache( private val project: Project, private val cache: ScriptConfigurationCache ) { - private val all: Collection> get() = cache.all() - private fun getConfiguration(file: VirtualFile): ScriptCompilationConfigurationWrapper? = cache[file]?.configuration + private val all: Collection> get() = cache.allApplied() + private fun getConfiguration(file: VirtualFile): ScriptCompilationConfigurationWrapper? = cache[file]?.applied?.configuration private fun getScriptSdk(compilationConfiguration: ScriptCompilationConfigurationWrapper?): Sdk? { // workaround for mismatched gradle wrapper and plugin version