diff --git a/idea/ide-common/src/org/jetbrains/kotlin/caches/project/cacheUtil.kt b/idea/ide-common/src/org/jetbrains/kotlin/caches/project/cacheUtil.kt new file mode 100644 index 00000000000..44748966452 --- /dev/null +++ b/idea/ide-common/src/org/jetbrains/kotlin/caches/project/cacheUtil.kt @@ -0,0 +1,62 @@ +/* + * 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 org.jetbrains.kotlin.caches.project + +import com.intellij.openapi.module.Module +import com.intellij.openapi.project.Project +import com.intellij.openapi.roots.ProjectRootModificationTracker +import com.intellij.openapi.util.UserDataHolder +import com.intellij.psi.util.CachedValueProvider +import com.intellij.psi.util.CachedValuesManager + +fun Module.cacheByClass(classForKey: Class<*>, vararg dependencies: Any, provider: () -> T): T { + return CachedValuesManager.getManager(project).cache(this, dependencies, classForKey, provider) +} + +fun Module.cacheByClassInvalidatingOnRootModifications(classForKey: Class<*>, provider: () -> T): T { + return cacheByClass(classForKey, ProjectRootModificationTracker.getInstance(project), provider = provider) +} + +/** + * Note that it uses lambda's class for caching (essentially, anonymous class), which means that all invocations will be cached + * by the one and the same key. + * It is encouraged to use explicit class, just for the sake of readability. + */ +fun Module.cacheInvalidatingOnRootModifications(provider: () -> T): T { + return cacheByClassInvalidatingOnRootModifications(provider::class.java, provider) +} + +fun Project.cacheByClass(classForKey: Class<*>, vararg dependencies: Any, provider: () -> T): T { + return CachedValuesManager.getManager(this).cache(this, dependencies, classForKey, provider) +} + +fun Project.cacheByClassInvalidatingOnRootModifications(classForKey: Class<*>, provider: () -> T): T { + return cacheByClass(classForKey, ProjectRootModificationTracker.getInstance(this), provider = provider) +} + +/** + * Note that it uses lambda's class for caching (essentially, anonymous class), which means that all invocations will be cached + * by the one and the same key. + * It is encouraged to use explicit class, just for the sake of readability. + */ +fun Project.cacheInvalidatingOnRootModifications(provider: () -> T): T { + return cacheByClassInvalidatingOnRootModifications(provider::class.java, provider) +} + +private fun CachedValuesManager.cache( + holder: UserDataHolder, + dependencies: Array, + classForKey: Class<*>, + provider: () -> T +): T { + return getCachedValue( + holder, + getKeyForClass(classForKey), + { CachedValueProvider.Result.create(provider(), dependencies) }, + false + ) +} + diff --git a/idea/idea-analysis/src/org/jetbrains/kotlin/idea/caches/project/IdeaModuleInfos.kt b/idea/idea-analysis/src/org/jetbrains/kotlin/idea/caches/project/IdeaModuleInfos.kt index 9f9935e5f84..3aaf626cd0d 100644 --- a/idea/idea-analysis/src/org/jetbrains/kotlin/idea/caches/project/IdeaModuleInfos.kt +++ b/idea/idea-analysis/src/org/jetbrains/kotlin/idea/caches/project/IdeaModuleInfos.kt @@ -17,7 +17,6 @@ import com.intellij.openapi.roots.libraries.Library import com.intellij.openapi.util.ModificationTracker import com.intellij.openapi.vfs.VirtualFile import com.intellij.psi.search.GlobalSearchScope -import com.intellij.psi.util.CachedValueProvider import com.intellij.util.PathUtil import com.intellij.util.SmartList import org.jetbrains.jps.model.java.JavaSourceRootType @@ -26,6 +25,7 @@ import org.jetbrains.kotlin.analyzer.CombinedModuleInfo import org.jetbrains.kotlin.analyzer.LibraryModuleInfo import org.jetbrains.kotlin.analyzer.ModuleInfo import org.jetbrains.kotlin.analyzer.TrackableModuleInfo +import org.jetbrains.kotlin.caches.project.cacheByClassInvalidatingOnRootModifications import org.jetbrains.kotlin.caches.resolve.resolution import org.jetbrains.kotlin.config.SourceKotlinRootType import org.jetbrains.kotlin.config.TestSourceKotlinRootType @@ -177,16 +177,9 @@ sealed class ModuleSourceInfoWithExpectedBy(private val forProduction: Boolean) return expectedByModules.mapNotNull { if (forProduction) it.productionSourceInfo() else it.testSourceInfo() } } - override fun dependencies(): List = module.cached(createCachedValueProvider { - CachedValueProvider.Result( - ideaModelDependencies(module, forProduction, platform), - ProjectRootModificationTracker.getInstance(module.project) - ) - }) - - // NB: CachedValueProvider must exist separately in Production / Test source info, - // otherwise caching does not work properly - protected abstract fun createCachedValueProvider(f: () -> CachedValueProvider.Result): CachedValueProvider + override fun dependencies(): List = module.cacheByClassInvalidatingOnRootModifications(this::class.java) { + ideaModelDependencies(module, forProduction, platform) + } } data class ModuleProductionSourceInfo internal constructor( @@ -200,8 +193,6 @@ data class ModuleProductionSourceInfo internal constructor( override fun contentScope(): GlobalSearchScope { return enlargedSearchScope(ModuleProductionSourceScope(module), module, isTestScope = false) } - - override fun createCachedValueProvider(f: () -> CachedValueProvider.Result) = CachedValueProvider { f() } } //TODO: (module refactoring) do not create ModuleTestSourceInfo when there are no test roots for module @@ -217,19 +208,20 @@ data class ModuleTestSourceInfo internal constructor(override val module: Module override fun contentScope(): GlobalSearchScope = enlargedSearchScope(ModuleTestSourceScope(module), module, isTestScope = true) - override fun modulesWhoseInternalsAreVisible() = module.cached(CachedValueProvider { - val list = SmartList() + override fun modulesWhoseInternalsAreVisible(): Collection = + module.cacheByClassInvalidatingOnRootModifications(KeyForModulesWhoseInternalsAreVisible::class.java) { + val list = SmartList() - list.addIfNotNull(module.productionSourceInfo()) + list.addIfNotNull(module.productionSourceInfo()) - TestModuleProperties.getInstance(module).productionModule?.let { - list.addIfNotNull(it.productionSourceInfo()) + TestModuleProperties.getInstance(module).productionModule?.let { + list.addIfNotNull(it.productionSourceInfo()) + } + + list } - CachedValueProvider.Result(list, ProjectRootModificationTracker.getInstance(module.project)) - }) - - override fun createCachedValueProvider(f: () -> CachedValueProvider.Result) = CachedValueProvider { f() } + private object KeyForModulesWhoseInternalsAreVisible } fun Module.productionSourceInfo(): ModuleProductionSourceInfo? = if (hasProductionRoots()) ModuleProductionSourceInfo(this) else null diff --git a/idea/idea-analysis/src/org/jetbrains/kotlin/idea/caches/project/cacheUtil.kt b/idea/idea-analysis/src/org/jetbrains/kotlin/idea/caches/project/cacheUtil.kt deleted file mode 100644 index fb7a2e1e84e..00000000000 --- a/idea/idea-analysis/src/org/jetbrains/kotlin/idea/caches/project/cacheUtil.kt +++ /dev/null @@ -1,19 +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 org.jetbrains.kotlin.idea.caches.project - -import com.intellij.openapi.module.Module -import com.intellij.openapi.project.Project -import com.intellij.psi.util.CachedValueProvider -import com.intellij.psi.util.CachedValuesManager - -fun Module.cached(provider: CachedValueProvider): T { - return CachedValuesManager.getManager(project).getCachedValue(this, provider) -} - -fun Project.cached(provider: CachedValueProvider): T { - return CachedValuesManager.getManager(this).getCachedValue(this, provider) -} diff --git a/idea/idea-analysis/src/org/jetbrains/kotlin/idea/caches/project/moduleInfosFromIdeaModel.kt b/idea/idea-analysis/src/org/jetbrains/kotlin/idea/caches/project/moduleInfosFromIdeaModel.kt index 72b3bcdac51..a7d15d7db9f 100644 --- a/idea/idea-analysis/src/org/jetbrains/kotlin/idea/caches/project/moduleInfosFromIdeaModel.kt +++ b/idea/idea-analysis/src/org/jetbrains/kotlin/idea/caches/project/moduleInfosFromIdeaModel.kt @@ -13,17 +13,17 @@ import com.intellij.openapi.projectRoots.Sdk import com.intellij.openapi.roots.JdkOrderEntry import com.intellij.openapi.roots.LibraryOrderEntry import com.intellij.openapi.roots.ModuleRootManager -import com.intellij.openapi.roots.ProjectRootModificationTracker -import com.intellij.psi.util.CachedValueProvider +import org.jetbrains.kotlin.caches.project.cacheInvalidatingOnRootModifications import org.jetbrains.kotlin.platform.TargetPlatform import org.jetbrains.kotlin.platform.isCommon import org.jetbrains.kotlin.types.typeUtil.closure import java.util.concurrent.ConcurrentHashMap fun getModuleInfosFromIdeaModel(project: Project, platform: TargetPlatform): List { - val modelInfosCache = project.cached(CachedValueProvider { - CachedValueProvider.Result(collectModuleInfosFromIdeaModel(project), ProjectRootModificationTracker.getInstance(project)) - }) + val modelInfosCache = project.cacheInvalidatingOnRootModifications { + collectModuleInfosFromIdeaModel(project) + } + return modelInfosCache.forPlatform(platform) } diff --git a/idea/idea-analysis/src/org/jetbrains/kotlin/idea/caches/project/multiplatformUtil.kt b/idea/idea-analysis/src/org/jetbrains/kotlin/idea/caches/project/multiplatformUtil.kt index 50fbc6e7680..3760f2f96cc 100644 --- a/idea/idea-analysis/src/org/jetbrains/kotlin/idea/caches/project/multiplatformUtil.kt +++ b/idea/idea-analysis/src/org/jetbrains/kotlin/idea/caches/project/multiplatformUtil.kt @@ -10,10 +10,9 @@ import com.intellij.facet.FacetTypeRegistry import com.intellij.openapi.externalSystem.service.project.IdeModelsProviderImpl import com.intellij.openapi.module.Module import com.intellij.openapi.module.ModuleManager -import com.intellij.openapi.roots.ProjectRootModificationTracker import com.intellij.psi.PsiElement -import com.intellij.psi.util.CachedValueProvider import org.jetbrains.kotlin.analyzer.ModuleInfo +import org.jetbrains.kotlin.caches.project.cacheInvalidatingOnRootModifications import org.jetbrains.kotlin.caches.resolve.KotlinCacheService import org.jetbrains.kotlin.config.KotlinFacetSettings import org.jetbrains.kotlin.descriptors.ModuleDescriptor @@ -49,37 +48,30 @@ val KotlinFacetSettings.isMPPModule: Boolean private val Module.facetSettings get() = KotlinFacet.get(this)?.configuration?.settings val Module.implementingModules: List - get() = cached(CachedValueProvider { + get() = cacheInvalidatingOnRootModifications { val moduleManager = ModuleManager.getInstance(project) - CachedValueProvider.Result( - if (isNewMPPModule) { - moduleManager.getModuleDependentModules(this).filter { - it.isNewMPPModule && it.externalProjectId == externalProjectId - } - } else { - moduleManager.modules.filter { name in it.findOldFashionedImplementedModuleNames() } - }, - ProjectRootModificationTracker.getInstance(project) - ) - }) + + if (isNewMPPModule) { + moduleManager.getModuleDependentModules(this).filter { + it.isNewMPPModule && it.externalProjectId == externalProjectId + } + } else { + moduleManager.modules.filter { name in it.findOldFashionedImplementedModuleNames() } + } + } val Module.implementedModules: List - get() = cached>( - CachedValueProvider { - CachedValueProvider.Result( - if (isNewMPPModule) { - rootManager.dependencies.filter { - // TODO: remove additional android check + get() = cacheInvalidatingOnRootModifications { + if (isNewMPPModule) { + rootManager.dependencies.filter { + // TODO: remove additional android check it.isNewMPPModule && it.platform.isCommon() && it.externalProjectId == externalProjectId && (isAndroidModule() || it.isTestModule == isTestModule) - } - } else { - val modelsProvider = IdeModelsProviderImpl(project) - findOldFashionedImplementedModuleNames().mapNotNull { modelsProvider.findIdeModule(it) } - }, - ProjectRootModificationTracker.getInstance(project) - ) + } + } else { + val modelsProvider = IdeModelsProviderImpl(project) + findOldFashionedImplementedModuleNames().mapNotNull { modelsProvider.findIdeModule(it) } } - ) + } private fun Module.findOldFashionedImplementedModuleNames(): List { val facet = FacetManager.getInstance(this).findFacet( diff --git a/idea/idea-analysis/src/org/jetbrains/kotlin/idea/caches/trackers/KotlinModuleOutOfCodeBlockModificationTracker.kt b/idea/idea-analysis/src/org/jetbrains/kotlin/idea/caches/trackers/KotlinModuleOutOfCodeBlockModificationTracker.kt index bc5bf16398b..c19b92a4b50 100644 --- a/idea/idea-analysis/src/org/jetbrains/kotlin/idea/caches/trackers/KotlinModuleOutOfCodeBlockModificationTracker.kt +++ b/idea/idea-analysis/src/org/jetbrains/kotlin/idea/caches/trackers/KotlinModuleOutOfCodeBlockModificationTracker.kt @@ -9,12 +9,10 @@ import com.intellij.openapi.module.Module import com.intellij.openapi.module.ModuleUtil import com.intellij.openapi.project.Project import com.intellij.openapi.roots.ModuleRootManager -import com.intellij.openapi.roots.ProjectRootModificationTracker import com.intellij.openapi.util.ModificationTracker -import com.intellij.psi.util.CachedValueProvider import com.intellij.util.CommonProcessors import org.jetbrains.annotations.TestOnly -import org.jetbrains.kotlin.idea.caches.project.cached +import org.jetbrains.kotlin.caches.project.cacheByClassInvalidatingOnRootModifications import org.jetbrains.kotlin.psi.KtFile class KotlinModuleOutOfCodeBlockModificationTracker private constructor(private val module: Module, private val updater: Updater) : @@ -29,18 +27,17 @@ class KotlinModuleOutOfCodeBlockModificationTracker private constructor(private // Avoid implicit capturing for this to make CachedValueStabilityChecker happy val module = module - module.cached(CachedValueProvider { - CachedValueProvider.Result.create( - HashSet().also { resultModuleSet -> - ModuleRootManager.getInstance(module).orderEntries().recursively().forEachModule( - CommonProcessors.CollectProcessor(resultModuleSet) - ) - }, - ProjectRootModificationTracker.getInstance(module.project) - ) - }) + module.cacheByClassInvalidatingOnRootModifications(KeyForCachedDependencies::class.java) { + HashSet().also { resultModuleSet -> + ModuleRootManager.getInstance(module).orderEntries().recursively().forEachModule( + CommonProcessors.CollectProcessor(resultModuleSet) + ) + } + } } + object KeyForCachedDependencies + override fun getModificationCount(): Long { val currentGlobalCount = kotlinOutOfCodeBlockTracker.modificationCount