FIR IDE: invalidate sessions on exception

This commit is contained in:
Ilya Kirillov
2020-09-15 17:46:07 +03:00
parent a743b80d17
commit 74298aae32
4 changed files with 72 additions and 6 deletions
@@ -14,11 +14,13 @@ import org.jetbrains.kotlin.fir.resolve.transformers.FirPhaseManager
import org.jetbrains.kotlin.fir.symbols.AbstractFirBasedSymbol import org.jetbrains.kotlin.fir.symbols.AbstractFirBasedSymbol
import org.jetbrains.kotlin.idea.fir.low.level.api.file.builder.ModuleFileCache import org.jetbrains.kotlin.idea.fir.low.level.api.file.builder.ModuleFileCache
import org.jetbrains.kotlin.idea.fir.low.level.api.lazy.resolve.FirLazyDeclarationResolver import org.jetbrains.kotlin.idea.fir.low.level.api.lazy.resolve.FirLazyDeclarationResolver
import org.jetbrains.kotlin.idea.fir.low.level.api.sessions.FirSessionInvalidator
@ThreadSafeMutableState @ThreadSafeMutableState
internal class IdeFirPhaseManager( internal class IdeFirPhaseManager(
private val lazyDeclarationResolver: FirLazyDeclarationResolver, private val lazyDeclarationResolver: FirLazyDeclarationResolver,
private val cache: ModuleFileCache private val cache: ModuleFileCache,
private val sessionInvalidator: FirSessionInvalidator,
) : FirPhaseManager() { ) : FirPhaseManager() {
override fun ensureResolved( override fun ensureResolved(
symbol: AbstractFirBasedSymbol<*>, symbol: AbstractFirBasedSymbol<*>,
@@ -34,6 +36,11 @@ internal class IdeFirPhaseManager(
"Incorrect resolvePhase: actual: $availablePhase, expected: $requiredPhase\n For: ${symbol.fir.render()}" "Incorrect resolvePhase: actual: $availablePhase, expected: $requiredPhase\n For: ${symbol.fir.render()}"
} }
lazyDeclarationResolver.lazyResolveDeclaration(result, cache, requiredPhase, checkPCE = false) try {
lazyDeclarationResolver.lazyResolveDeclaration(result, cache, requiredPhase, checkPCE = true)
} catch (e: Throwable) {
sessionInvalidator.invalidate((symbol.fir as FirDeclaration).session)
throw e
}
} }
} }
@@ -49,6 +49,7 @@ internal object FirIdeSessionFactory {
moduleInfo: ModuleSourceInfo, moduleInfo: ModuleSourceInfo,
builtinsAndCloneableSession: FirIdeBuiltinsAndCloneableSession, builtinsAndCloneableSession: FirIdeBuiltinsAndCloneableSession,
firPhaseRunner: FirPhaseRunner, firPhaseRunner: FirPhaseRunner,
sessionInvalidator: FirSessionInvalidator,
builtinTypes: BuiltinTypes, builtinTypes: BuiltinTypes,
sessionsCache: MutableMap<ModuleSourceInfo, FirIdeSourcesSession>, sessionsCache: MutableMap<ModuleSourceInfo, FirIdeSourcesSession>,
isRootModule: Boolean, isRootModule: Boolean,
@@ -65,7 +66,7 @@ internal object FirIdeSessionFactory {
return session.apply { return session.apply {
val cache = ModuleFileCacheImpl(this) val cache = ModuleFileCacheImpl(this)
val firPhaseManager = IdeFirPhaseManager(FirLazyDeclarationResolver(firFileBuilder), cache) val firPhaseManager = IdeFirPhaseManager(FirLazyDeclarationResolver(firFileBuilder), cache, sessionInvalidator)
registerCommonComponents() registerCommonComponents()
registerResolveComponents() registerResolveComponents()
@@ -104,6 +105,7 @@ internal object FirIdeSessionFactory {
it, it,
builtinsAndCloneableSession, builtinsAndCloneableSession,
firPhaseRunner, firPhaseRunner,
sessionInvalidator,
builtinTypes, builtinTypes,
sessionsCache, sessionsCache,
isRootModule = false isRootModule = false
@@ -32,6 +32,7 @@ internal class FirIdeSessionProviderStorage(private val project: Project) {
rootModule, rootModule,
builtinsAndCloneableSession, builtinsAndCloneableSession,
firPhaseRunner, firPhaseRunner,
cache.sessionInvalidator,
builtinTypes, builtinTypes,
sessions, sessions,
isRootModule = true isRootModule = true
@@ -45,11 +46,16 @@ internal class FirIdeSessionProviderStorage(private val project: Project) {
} }
private class FromModuleViewSessionCache( private class FromModuleViewSessionCache(
val root: ModuleSourceInfo val root: ModuleSourceInfo,
) { ) {
@Volatile @Volatile
private var mappings: Map<ModuleSourceInfo, FirSessionWithModificationTracker> = emptyMap() private var mappings: Map<ModuleSourceInfo, FirSessionWithModificationTracker> = emptyMap()
val sessionInvalidator: FirSessionInvalidator = FirSessionInvalidator { session ->
mappings[session.moduleInfo]?.invalidate()
}
inline fun <R> withMappings( inline fun <R> withMappings(
action: (Map<ModuleSourceInfo, FirIdeSourcesSession>) -> Pair<Map<ModuleSourceInfo, FirIdeSourcesSession>, R> action: (Map<ModuleSourceInfo, FirIdeSourcesSession>) -> Pair<Map<ModuleSourceInfo, FirIdeSourcesSession>, R>
): Pair<Map<ModuleSourceInfo, FirIdeSourcesSession>, R> { ): Pair<Map<ModuleSourceInfo, FirIdeSourcesSession>, R> {
@@ -60,7 +66,36 @@ private class FromModuleViewSessionCache(
@OptIn(ExperimentalStdlibApi::class) @OptIn(ExperimentalStdlibApi::class)
private fun getSessions(): Map<ModuleSourceInfo, FirIdeSourcesSession> = buildMap { private fun getSessions(): Map<ModuleSourceInfo, FirIdeSourcesSession> = buildMap {
mappings.forEach { (module, session) -> if (session.isValid) put(module, session.firSession) } val sessions = mappings.values
val sessionToValidity = hashMapOf<FirSessionWithModificationTracker, Boolean>()
var isValid = true
fun dfs(session: FirSessionWithModificationTracker) {
sessionToValidity[session]?.let { valid ->
if (!valid) isValid = false
return
}
sessionToValidity[session] = session.isValid
session.firSession.dependencies.forEach { dependency ->
mappings[dependency]?.let(::dfs)
}
if (!session.isValid) {
isValid = false
}
if (!isValid) {
sessionToValidity[session] = false
}
}
for (session in sessions) {
if (session !in sessionToValidity) {
isValid = true
dfs(session)
}
}
return sessionToValidity.entries
.mapNotNull { (session, valid) -> session.takeIf { valid } }
.associate { session -> session.firSession.moduleInfo to session.firSession }
} }
} }
@@ -70,5 +105,12 @@ private class FirSessionWithModificationTracker(
private val modificationTracker = KotlinModuleOutOfCodeBlockModificationTracker(firSession.moduleInfo.module) private val modificationTracker = KotlinModuleOutOfCodeBlockModificationTracker(firSession.moduleInfo.module)
private val timeStamp = modificationTracker.modificationCount private val timeStamp = modificationTracker.modificationCount
val isValid: Boolean get() = modificationTracker.modificationCount == timeStamp @Volatile
private var isInvalidated = false
fun invalidate() {
isInvalidated = true
}
val isValid: Boolean get() = isInvalidated || modificationTracker.modificationCount == timeStamp
} }
@@ -0,0 +1,15 @@
/*
* Copyright 2010-2020 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.fir.low.level.api.sessions
import org.jetbrains.kotlin.fir.FirSession
internal class FirSessionInvalidator(private val invalidateSourcesSession: (FirIdeSourcesSession) -> Unit) {
fun invalidate(session: FirSession) {
require(session is FirIdeSourcesSession)
invalidateSourcesSession(session)
}
}