diff --git a/analysis/analysis-api-impl-base/src/org/jetbrains/kotlin/analysis/api/impl/base/CachingKtAnalysisSessionProvider.kt b/analysis/analysis-api-impl-base/src/org/jetbrains/kotlin/analysis/api/impl/base/CachingKtAnalysisSessionProvider.kt index fe3275f58d8..f88f19c9c25 100644 --- a/analysis/analysis-api-impl-base/src/org/jetbrains/kotlin/analysis/api/impl/base/CachingKtAnalysisSessionProvider.kt +++ b/analysis/analysis-api-impl-base/src/org/jetbrains/kotlin/analysis/api/impl/base/CachingKtAnalysisSessionProvider.kt @@ -7,21 +7,18 @@ package org.jetbrains.kotlin.analysis.api.impl.base import com.intellij.openapi.project.Project import com.intellij.openapi.roots.ProjectRootModificationTracker -import com.intellij.psi.util.CachedValueProvider -import com.intellij.psi.util.CachedValuesManager import com.intellij.psi.util.PsiModificationTracker import org.jetbrains.annotations.TestOnly import org.jetbrains.kotlin.analysis.api.KtAnalysisApiInternals -import org.jetbrains.kotlin.analysis.providers.createProjectWideOutOfBlockModificationTracker import org.jetbrains.kotlin.analysis.api.KtAnalysisSession -import org.jetbrains.kotlin.analysis.api.session.KtAnalysisSessionProvider -import org.jetbrains.kotlin.analysis.api.symbols.KtSymbol -import org.jetbrains.kotlin.analysis.project.structure.KtModule -import org.jetbrains.kotlin.analysis.project.structure.getKtModule import org.jetbrains.kotlin.analysis.api.lifetime.KtLifetimeToken import org.jetbrains.kotlin.analysis.api.lifetime.KtLifetimeTokenFactory +import org.jetbrains.kotlin.analysis.api.session.KtAnalysisSessionProvider +import org.jetbrains.kotlin.analysis.project.structure.KtModule +import org.jetbrains.kotlin.analysis.project.structure.getKtModule +import org.jetbrains.kotlin.analysis.providers.createProjectWideOutOfBlockModificationTracker +import org.jetbrains.kotlin.analysis.utils.caches.SoftCachedMap import org.jetbrains.kotlin.psi.KtElement -import java.util.concurrent.ConcurrentHashMap import kotlin.reflect.KClass @KtAnalysisApiInternals @@ -47,10 +44,6 @@ abstract class CachingKtAnalysisSessionProvider(project: Project) : } } - private fun getCachedAnalysisSession(firResolveSession: State, token: KtLifetimeToken): KtAnalysisSession? { - return cache.getCachedAnalysisSession(firResolveSession to token::class) - } - @TestOnly final override fun clearCaches() { cache.clear() @@ -58,23 +51,24 @@ abstract class CachingKtAnalysisSessionProvider(project: Project) : } private class KtAnalysisSessionCache(project: Project) { - private val cache = CachedValuesManager.getManager(project).createCachedValue { - CachedValueProvider.Result( - ConcurrentHashMap>, KtAnalysisSession>(), + private val cache = SoftCachedMap.create>, KtAnalysisSession>( + project, + SoftCachedMap.Kind.STRONG_KEYS_SOFT_VALUES, + listOf( PsiModificationTracker.MODIFICATION_COUNT, ProjectRootModificationTracker.getInstance(project), project.createProjectWideOutOfBlockModificationTracker() ) - } + ) @TestOnly fun clear() { - cache.value.clear() + cache.clear() } - inline fun getAnalysisSession(key: Pair>, create: () -> KtAnalysisSession): KtAnalysisSession = - cache.value.getOrPut(key) { create() } - - fun getCachedAnalysisSession(key: KEY): KtAnalysisSession? = - cache.value[key] + fun getAnalysisSession( + key: Pair>, + create: () -> KtAnalysisSession + ): KtAnalysisSession = + cache.getOrPut(key) { create() } } \ No newline at end of file diff --git a/analysis/analysis-internal-utils/src/org/jetbrains/kotlin/analysis/utils/caches/SoftCachedMap.kt b/analysis/analysis-internal-utils/src/org/jetbrains/kotlin/analysis/utils/caches/SoftCachedMap.kt new file mode 100644 index 00000000000..de3038e9544 --- /dev/null +++ b/analysis/analysis-internal-utils/src/org/jetbrains/kotlin/analysis/utils/caches/SoftCachedMap.kt @@ -0,0 +1,75 @@ +/* + * Copyright 2010-2022 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.analysis.utils.caches + +import com.intellij.openapi.project.Project +import com.intellij.psi.util.CachedValue +import com.intellij.psi.util.CachedValueProvider +import com.intellij.psi.util.CachedValuesManager +import com.intellij.util.containers.ContainerUtil +import org.jetbrains.annotations.TestOnly +import java.util.concurrent.ConcurrentHashMap + +public abstract class SoftCachedMap { + public abstract fun getOrPut(key: K, create: () -> V): V + + @TestOnly + public abstract fun clear() + + public companion object { + public fun create( + project: Project, + kind: Kind, + trackers: List + ): SoftCachedMap = when { + trackers.isEmpty() -> SoftCachedMapWithoutTrackers(kind) + else -> SoftCachedMapWithTrackers(project, kind, trackers.toTypedArray()) + } + } + + public enum class Kind { + SOFT_KEYS_SOFT_VALUES, + STRONG_KEYS_SOFT_VALUES + } +} + +private class SoftCachedMapWithTrackers( + private val project: Project, + kind: Kind, + private val trackers: Array +) : SoftCachedMap() { + private val cache = when (kind) { + Kind.SOFT_KEYS_SOFT_VALUES -> ContainerUtil.createConcurrentSoftMap>() + Kind.STRONG_KEYS_SOFT_VALUES -> ConcurrentHashMap>() + } + + override fun clear() { + cache.clear() + } + + override fun getOrPut(key: K, create: () -> V): V { + return cache.getOrPut(key) { + CachedValuesManager.getManager(project).createCachedValue { + CachedValueProvider.Result(create(), *trackers) + } + }.value + } +} + +private class SoftCachedMapWithoutTrackers(kind: Kind) : SoftCachedMap() { + private val cache = when (kind) { + Kind.SOFT_KEYS_SOFT_VALUES -> ContainerUtil.createConcurrentSoftKeySoftValueMap() + Kind.STRONG_KEYS_SOFT_VALUES -> ContainerUtil.createSoftValueMap() + } + + override fun clear() { + cache.clear() + } + + override fun getOrPut(key: K, create: () -> V): V { + return cache.getOrPut(key, create) + } +} \ No newline at end of file diff --git a/analysis/low-level-api-fir/src/org/jetbrains/kotlin/analysis/low/level/api/fir/LLFirResolveSessionDepended.kt b/analysis/low-level-api-fir/src/org/jetbrains/kotlin/analysis/low/level/api/fir/LLFirResolveSessionDepended.kt index f7e467209f2..b108aa91898 100644 --- a/analysis/low-level-api-fir/src/org/jetbrains/kotlin/analysis/low/level/api/fir/LLFirResolveSessionDepended.kt +++ b/analysis/low-level-api-fir/src/org/jetbrains/kotlin/analysis/low/level/api/fir/LLFirResolveSessionDepended.kt @@ -41,13 +41,14 @@ internal class LLFirResolveSessionDepended( override val useSiteKtModule: KtModule get() = originalFirResolveSession.useSiteKtModule override val useSiteFirSession get() = originalFirResolveSession.useSiteFirSession - private val scopeSessionProviderCache by softCachedValue( + private val scopeSessionProviderCache = SoftCachedMap.create( project, - PsiModificationTracker.MODIFICATION_COUNT, - ProjectRootModificationTracker.getInstance(project), - ) { - ConcurrentHashMap() - } + SoftCachedMap.Kind.SOFT_KEYS_SOFT_VALUES, + listOf( + PsiModificationTracker.MODIFICATION_COUNT, + ProjectRootModificationTracker.getInstance(project) + ) + ) override fun getScopeSessionFor(firSession: FirSession): ScopeSession { return scopeSessionProviderCache diff --git a/analysis/low-level-api-fir/src/org/jetbrains/kotlin/analysis/low/level/api/fir/LLFirResolveStateService.kt b/analysis/low-level-api-fir/src/org/jetbrains/kotlin/analysis/low/level/api/fir/LLFirResolveStateService.kt index b4a8b3d7df4..30ebaa8cc6e 100644 --- a/analysis/low-level-api-fir/src/org/jetbrains/kotlin/analysis/low/level/api/fir/LLFirResolveStateService.kt +++ b/analysis/low-level-api-fir/src/org/jetbrains/kotlin/analysis/low/level/api/fir/LLFirResolveStateService.kt @@ -8,6 +8,7 @@ package org.jetbrains.kotlin.analysis.low.level.api.fir import com.intellij.openapi.components.ServiceManager import com.intellij.openapi.project.Project import com.intellij.openapi.roots.ProjectRootModificationTracker +import com.intellij.psi.util.PsiModificationTracker import org.jetbrains.annotations.TestOnly import org.jetbrains.kotlin.analysis.low.level.api.fir.api.LLFirResolveSession import org.jetbrains.kotlin.analysis.low.level.api.fir.sessions.LLFirSession @@ -19,33 +20,23 @@ import org.jetbrains.kotlin.analysis.low.level.api.fir.state.LLFirSourceResolveS import org.jetbrains.kotlin.analysis.low.level.api.fir.util.errorWithFirSpecificEntries import org.jetbrains.kotlin.analysis.project.structure.* import org.jetbrains.kotlin.analysis.providers.createProjectWideOutOfBlockModificationTracker -import org.jetbrains.kotlin.analysis.utils.caches.getValue -import org.jetbrains.kotlin.analysis.utils.caches.softCachedValue -import java.util.concurrent.locks.ReentrantReadWriteLock -import kotlin.concurrent.withLock +import org.jetbrains.kotlin.analysis.utils.caches.SoftCachedMap internal class LLFirResolveSessionService(project: Project) { private val sessionProviderStorage = LLFirSessionProviderStorage(project) - private val stateCache by softCachedValue( + private val cache = SoftCachedMap.create( project, - project.createProjectWideOutOfBlockModificationTracker(), - ProjectRootModificationTracker.getInstance(project), - ) { - mutableMapOf() - } - - private val cacheLock = ReentrantReadWriteLock() + SoftCachedMap.Kind.STRONG_KEYS_SOFT_VALUES, + listOf( + ProjectRootModificationTracker.getInstance(project), + project.createProjectWideOutOfBlockModificationTracker(), + ) + ) fun getFirResolveSession(module: KtModule): LLFirResolvableResolveSession { - cacheLock.readLock().withLock { - stateCache[module]?.let { return it } - } - cacheLock.writeLock().withLock { - stateCache[module]?.let { return it } - val session = createFirResolveSessionFor(module, sessionProviderStorage) - stateCache[module] = session - return session + return cache.getOrPut(module) { + createFirResolveSessionFor(module, sessionProviderStorage) } } diff --git a/analysis/low-level-api-fir/src/org/jetbrains/kotlin/analysis/low/level/api/fir/providers/LLFirIdePredicateBasedProvider.kt b/analysis/low-level-api-fir/src/org/jetbrains/kotlin/analysis/low/level/api/fir/providers/LLFirIdePredicateBasedProvider.kt index f16a40f1498..c3bd72e847d 100644 --- a/analysis/low-level-api-fir/src/org/jetbrains/kotlin/analysis/low/level/api/fir/providers/LLFirIdePredicateBasedProvider.kt +++ b/analysis/low-level-api-fir/src/org/jetbrains/kotlin/analysis/low/level/api/fir/providers/LLFirIdePredicateBasedProvider.kt @@ -9,6 +9,7 @@ import kotlinx.collections.immutable.PersistentList import kotlinx.collections.immutable.persistentListOf import org.jetbrains.kotlin.analysis.low.level.api.fir.api.getFirResolveSession import org.jetbrains.kotlin.analysis.low.level.api.fir.api.resolveToFirSymbol +import org.jetbrains.kotlin.analysis.low.level.api.fir.sessions.LLFirSourcesSession import org.jetbrains.kotlin.analysis.low.level.api.fir.util.getContainingFile import org.jetbrains.kotlin.analysis.providers.KotlinAnnotationsResolver import org.jetbrains.kotlin.analysis.providers.KotlinDeclarationProvider @@ -40,7 +41,7 @@ import org.jetbrains.kotlin.psi.* * PSI index based implementation of [FirPredicateBasedProvider]. */ internal class LLFirIdePredicateBasedProvider( - private val session: FirSession, + private val session: LLFirSourcesSession, private val annotationsResolver: KotlinAnnotationsResolver, private val declarationProvider: KotlinDeclarationProvider, ) : FirPredicateBasedProvider() { diff --git a/analysis/low-level-api-fir/src/org/jetbrains/kotlin/analysis/low/level/api/fir/util/LLFirScopeSessionProvider.kt b/analysis/low-level-api-fir/src/org/jetbrains/kotlin/analysis/low/level/api/fir/util/LLFirScopeSessionProvider.kt index f848e987016..e867697d2fa 100644 --- a/analysis/low-level-api-fir/src/org/jetbrains/kotlin/analysis/low/level/api/fir/util/LLFirScopeSessionProvider.kt +++ b/analysis/low-level-api-fir/src/org/jetbrains/kotlin/analysis/low/level/api/fir/util/LLFirScopeSessionProvider.kt @@ -6,10 +6,7 @@ package org.jetbrains.kotlin.analysis.low.level.api.fir.util import com.intellij.openapi.project.Project -import com.intellij.openapi.roots.ProjectRootModificationTracker -import com.intellij.psi.util.PsiModificationTracker -import org.jetbrains.kotlin.analysis.utils.caches.getValue -import org.jetbrains.kotlin.analysis.utils.caches.softCachedValue +import org.jetbrains.kotlin.analysis.utils.caches.SoftCachedMap import org.jetbrains.kotlin.fir.resolve.ScopeSession import java.util.concurrent.ConcurrentHashMap @@ -28,12 +25,11 @@ abstract class LLFirScopeSessionProvider { private class LLFirInvalidatableScopeSessionProvider(project: Project, invalidationTrackers: List) : LLFirScopeSessionProvider() { // ScopeSession is thread-local, so we use Thread id as a key // We cannot use thread locals here as it may lead to memory leaks - private val cache by softCachedValue( + private val cache = SoftCachedMap.create( project, - *invalidationTrackers.toTypedArray(), - ) { - ConcurrentHashMap() - } + SoftCachedMap.Kind.STRONG_KEYS_SOFT_VALUES, + invalidationTrackers + ) override fun getScopeSession(): ScopeSession { return cache.getOrPut(Thread.currentThread().id) { ScopeSession() }