From 2ff4f10ff08db14a31a76cb3201c2030c4080d2f Mon Sep 17 00:00:00 2001 From: Igor Yakovlev Date: Tue, 10 Aug 2021 15:11:22 +0200 Subject: [PATCH] [FIR IDE] Fix SOE for FIR deserialized declarations provider --- .../AbstractFirDeserializedSymbolsProvider.kt | 7 ++- .../KotlinDeserializedJvmSymbolsProvider.kt | 2 +- .../api/sessions/FirIdeSessionFactory.kt | 47 ++++++++++++++++++- 3 files changed, 52 insertions(+), 4 deletions(-) diff --git a/compiler/fir/fir-deserialization/src/org/jetbrains/kotlin/fir/deserialization/AbstractFirDeserializedSymbolsProvider.kt b/compiler/fir/fir-deserialization/src/org/jetbrains/kotlin/fir/deserialization/AbstractFirDeserializedSymbolsProvider.kt index f303ef8729f..8500b5dab6a 100644 --- a/compiler/fir/fir-deserialization/src/org/jetbrains/kotlin/fir/deserialization/AbstractFirDeserializedSymbolsProvider.kt +++ b/compiler/fir/fir-deserialization/src/org/jetbrains/kotlin/fir/deserialization/AbstractFirDeserializedSymbolsProvider.kt @@ -67,7 +67,7 @@ abstract class AbstractFirDeserializedSymbolsProvider( private val packagePartsCache = session.firCachesFactory.createCache(::tryComputePackagePartInfos) private val typeAliasCache = session.firCachesFactory.createCache(::findAndDeserializeTypeAlias) - private val classCache: FirCache = + protected val classCache: FirCache = session.firCachesFactory.createCacheWithPostCompute( createValue = { classId, context -> findAndDeserializeClass(classId, context) }, postCompute = { _, symbol, postProcessor -> @@ -154,6 +154,9 @@ abstract class AbstractFirDeserializedSymbolsProvider( private fun findAndDeserializeClassViaParent(classId: ClassId): FirRegularClassSymbol? { val outerClassId = classId.outerClassId ?: return null + //This will cause cyclic cache request that is highly observable in IDE (but not in the compiler - but possible SOE bug also) + //To avoid it in IDE there is special implementation that forces load parent class before any nested class request: + //[org.jetbrains.kotlin.idea.fir.low.level.api.sessions.FirIdeSessionFactory.KotlinDeserializedJvmSymbolsProviderForIde] getClass(outerClassId) ?: return null return classCache.getValueIfComputed(classId) } @@ -180,7 +183,7 @@ abstract class AbstractFirDeserializedSymbolsProvider( return packagePartsCache.getValue(packageFqName) } - private fun getClass( + protected open fun getClass( classId: ClassId, parentContext: FirDeserializationContext? = null ): FirRegularClassSymbol? { diff --git a/compiler/fir/java/src/org/jetbrains/kotlin/fir/java/deserialization/KotlinDeserializedJvmSymbolsProvider.kt b/compiler/fir/java/src/org/jetbrains/kotlin/fir/java/deserialization/KotlinDeserializedJvmSymbolsProvider.kt index 95f05ea664a..eb8ef821206 100644 --- a/compiler/fir/java/src/org/jetbrains/kotlin/fir/java/deserialization/KotlinDeserializedJvmSymbolsProvider.kt +++ b/compiler/fir/java/src/org/jetbrains/kotlin/fir/java/deserialization/KotlinDeserializedJvmSymbolsProvider.kt @@ -36,7 +36,7 @@ import java.nio.file.Path import java.nio.file.Paths @ThreadSafeMutableState -class KotlinDeserializedJvmSymbolsProvider( +open class KotlinDeserializedJvmSymbolsProvider( session: FirSession, moduleDataProvider: ModuleDataProvider, kotlinScopeProvider: FirKotlinScopeProvider, diff --git a/idea/idea-frontend-fir/idea-fir-low-level-api/src/org/jetbrains/kotlin/idea/fir/low/level/api/sessions/FirIdeSessionFactory.kt b/idea/idea-frontend-fir/idea-fir-low-level-api/src/org/jetbrains/kotlin/idea/fir/low/level/api/sessions/FirIdeSessionFactory.kt index 12eaa65f534..035f50455dc 100644 --- a/idea/idea-frontend-fir/idea-fir-low-level-api/src/org/jetbrains/kotlin/idea/fir/low/level/api/sessions/FirIdeSessionFactory.kt +++ b/idea/idea-frontend-fir/idea-fir-low-level-api/src/org/jetbrains/kotlin/idea/fir/low/level/api/sessions/FirIdeSessionFactory.kt @@ -14,6 +14,8 @@ import org.jetbrains.kotlin.fir.backend.jvm.FirJvmTypeMapper import org.jetbrains.kotlin.fir.caches.FirCachesFactory import org.jetbrains.kotlin.fir.checkers.registerExtendedCommonCheckers import org.jetbrains.kotlin.fir.declarations.SealedClassInheritorsProvider +import org.jetbrains.kotlin.fir.deserialization.FirDeserializationContext +import org.jetbrains.kotlin.fir.deserialization.ModuleDataProvider import org.jetbrains.kotlin.fir.java.JavaSymbolProvider import org.jetbrains.kotlin.fir.java.deserialization.KotlinDeserializedJvmSymbolsProvider import org.jetbrains.kotlin.fir.resolve.providers.FirDependenciesSymbolProvider @@ -27,6 +29,7 @@ import org.jetbrains.kotlin.fir.resolve.transformers.FirPhaseCheckingPhaseManage import org.jetbrains.kotlin.fir.symbols.FirPhaseManager import org.jetbrains.kotlin.fir.scopes.FirKotlinScopeProvider import org.jetbrains.kotlin.fir.session.* +import org.jetbrains.kotlin.fir.symbols.impl.FirRegularClassSymbol import org.jetbrains.kotlin.idea.fir.low.level.api.* import org.jetbrains.kotlin.idea.fir.low.level.api.FirPhaseRunner import org.jetbrains.kotlin.idea.fir.low.level.api.IdeFirPhaseManager @@ -43,8 +46,12 @@ import org.jetbrains.kotlin.idea.fir.low.level.api.providers.FirIdeProvider import org.jetbrains.kotlin.idea.fir.low.level.api.providers.FirModuleWithDependenciesSymbolProvider import org.jetbrains.kotlin.idea.fir.low.level.api.providers.FirThreadSafeSymbolProviderWrapper import org.jetbrains.kotlin.idea.fir.low.level.api.util.checkCanceled +import org.jetbrains.kotlin.load.java.JavaClassFinder import org.jetbrains.kotlin.load.java.JavaClassFinderImpl +import org.jetbrains.kotlin.load.kotlin.KotlinClassFinder +import org.jetbrains.kotlin.load.kotlin.PackagePartProvider import org.jetbrains.kotlin.load.kotlin.VirtualFileFinderFactory +import org.jetbrains.kotlin.name.ClassId import org.jetbrains.kotlin.name.Name import org.jetbrains.kotlin.platform.jvm.JvmPlatforms import org.jetbrains.kotlin.resolve.jvm.platform.JvmPlatformAnalyzerServices @@ -202,7 +209,7 @@ internal object FirIdeSessionFactory { buildList { add( FirThreadSafeSymbolProviderWrapper( - KotlinDeserializedJvmSymbolsProvider( + KotlinDeserializedJvmSymbolsProviderForIde( this@session, moduleDataProvider, kotlinScopeProvider, @@ -224,6 +231,44 @@ internal object FirIdeSessionFactory { } } + /** + * Workaround of SOE for loading classes that loads via parent. + * It is not observable in compiler but it is in IDE + * Remove this when cyclic cache request will be fixed in the compiler provider + */ + private class KotlinDeserializedJvmSymbolsProviderForIde( + session: FirSession, + moduleDataProvider: ModuleDataProvider, + kotlinScopeProvider: FirKotlinScopeProvider, + packagePartProvider: PackagePartProvider, + kotlinClassFinder: KotlinClassFinder, + javaSymbolProvider: JavaSymbolProvider, + javaClassFinder: JavaClassFinder + ) : KotlinDeserializedJvmSymbolsProvider( + session, moduleDataProvider, kotlinScopeProvider, packagePartProvider, kotlinClassFinder, + javaSymbolProvider, javaClassFinder + ) { + override fun getClass( + classId: ClassId, + parentContext: FirDeserializationContext? + ): FirRegularClassSymbol? { + if (parentContext !== null) { + return super.getClass(classId, parentContext) + } + val computedClass = classCache.getValueIfComputed(classId) + if (computedClass != null) { + return computedClass + } + + var parentClassId = classId + while (true) { + parentClassId = parentClassId.outerClassId ?: break + } + + classCache.getValue(parentClassId, parentContext) + return classCache.getValue(classId, parentContext) + } + } fun createBuiltinsAndCloneableSession( project: Project,