From b24c75d04cd3fb2b5c41aa6db7b7e2877446a4f9 Mon Sep 17 00:00:00 2001 From: Marco Pennekamp Date: Thu, 8 Feb 2024 17:07:45 +0100 Subject: [PATCH] [LL] Fix `computeIfAbsent` contract violation during dangling file session creation - To avoid recursive updates, we can create sessions which cannot be created in isolation outside `computeIfAbsent`. Note that this is the current behavior of the default implementation of `ConcurrentMap.computeIfAbsent`, which will be replaced soon with the stricter behavior of `CleanableSoftValueCache` (see KT-61222). ^KT-65683 fixed --- .../api/fir/sessions/LLFirSessionCache.kt | 20 +++++++++++++++++-- 1 file changed, 18 insertions(+), 2 deletions(-) diff --git a/analysis/low-level-api-fir/src/org/jetbrains/kotlin/analysis/low/level/api/fir/sessions/LLFirSessionCache.kt b/analysis/low-level-api-fir/src/org/jetbrains/kotlin/analysis/low/level/api/fir/sessions/LLFirSessionCache.kt index ccb9542f0ba..71f534b71d2 100644 --- a/analysis/low-level-api-fir/src/org/jetbrains/kotlin/analysis/low/level/api/fir/sessions/LLFirSessionCache.kt +++ b/analysis/low-level-api-fir/src/org/jetbrains/kotlin/analysis/low/level/api/fir/sessions/LLFirSessionCache.kt @@ -88,9 +88,18 @@ class LLFirSessionCache(private val project: Project) { private fun getCachedSession(module: T, storage: SessionStorage, factory: (T) -> LLFirSession): LLFirSession { checkCanceled() - val session = storage.computeIfAbsent(module) { factory(module) } - checkSessionValidity(session) + val session = if (module.supportsIsolatedSessionCreation) { + storage.computeIfAbsent(module) { factory(module) } + } else { + // Non-isolated session creation may need to access other sessions, so we should create the session outside `computeIfAbsent` to + // avoid recursive update exceptions. + storage[module] ?: run { + val danglingSession = factory(module) + storage.computeIfAbsent(module) { danglingSession } + } + } + checkSessionValidity(session) return session } @@ -199,6 +208,13 @@ class LLFirSessionCache(private val project: Project) { } } + /** + * Whether the session for [module] can be created without getting other sessions from the cache. Should be kept in sync with + * [createSession]. + */ + private val KtModule.supportsIsolatedSessionCreation: Boolean + get() = this !is KtDanglingFileModule + private fun createSession(module: KtModule): LLFirSession { val sessionFactory = createPlatformAwareSessionFactory(module) return when (module) {