[FIR IDE] Fix SOE for FIR deserialized declarations provider

This commit is contained in:
Igor Yakovlev
2021-08-10 15:11:22 +02:00
parent 035f1ff89f
commit 2ff4f10ff0
3 changed files with 52 additions and 4 deletions
@@ -67,7 +67,7 @@ abstract class AbstractFirDeserializedSymbolsProvider(
private val packagePartsCache = session.firCachesFactory.createCache(::tryComputePackagePartInfos) private val packagePartsCache = session.firCachesFactory.createCache(::tryComputePackagePartInfos)
private val typeAliasCache = session.firCachesFactory.createCache(::findAndDeserializeTypeAlias) private val typeAliasCache = session.firCachesFactory.createCache(::findAndDeserializeTypeAlias)
private val classCache: FirCache<ClassId, FirRegularClassSymbol?, FirDeserializationContext?> = protected val classCache: FirCache<ClassId, FirRegularClassSymbol?, FirDeserializationContext?> =
session.firCachesFactory.createCacheWithPostCompute( session.firCachesFactory.createCacheWithPostCompute(
createValue = { classId, context -> findAndDeserializeClass(classId, context) }, createValue = { classId, context -> findAndDeserializeClass(classId, context) },
postCompute = { _, symbol, postProcessor -> postCompute = { _, symbol, postProcessor ->
@@ -154,6 +154,9 @@ abstract class AbstractFirDeserializedSymbolsProvider(
private fun findAndDeserializeClassViaParent(classId: ClassId): FirRegularClassSymbol? { private fun findAndDeserializeClassViaParent(classId: ClassId): FirRegularClassSymbol? {
val outerClassId = classId.outerClassId ?: return null 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 getClass(outerClassId) ?: return null
return classCache.getValueIfComputed(classId) return classCache.getValueIfComputed(classId)
} }
@@ -180,7 +183,7 @@ abstract class AbstractFirDeserializedSymbolsProvider(
return packagePartsCache.getValue(packageFqName) return packagePartsCache.getValue(packageFqName)
} }
private fun getClass( protected open fun getClass(
classId: ClassId, classId: ClassId,
parentContext: FirDeserializationContext? = null parentContext: FirDeserializationContext? = null
): FirRegularClassSymbol? { ): FirRegularClassSymbol? {
@@ -36,7 +36,7 @@ import java.nio.file.Path
import java.nio.file.Paths import java.nio.file.Paths
@ThreadSafeMutableState @ThreadSafeMutableState
class KotlinDeserializedJvmSymbolsProvider( open class KotlinDeserializedJvmSymbolsProvider(
session: FirSession, session: FirSession,
moduleDataProvider: ModuleDataProvider, moduleDataProvider: ModuleDataProvider,
kotlinScopeProvider: FirKotlinScopeProvider, kotlinScopeProvider: FirKotlinScopeProvider,
@@ -14,6 +14,8 @@ import org.jetbrains.kotlin.fir.backend.jvm.FirJvmTypeMapper
import org.jetbrains.kotlin.fir.caches.FirCachesFactory import org.jetbrains.kotlin.fir.caches.FirCachesFactory
import org.jetbrains.kotlin.fir.checkers.registerExtendedCommonCheckers import org.jetbrains.kotlin.fir.checkers.registerExtendedCommonCheckers
import org.jetbrains.kotlin.fir.declarations.SealedClassInheritorsProvider 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.JavaSymbolProvider
import org.jetbrains.kotlin.fir.java.deserialization.KotlinDeserializedJvmSymbolsProvider import org.jetbrains.kotlin.fir.java.deserialization.KotlinDeserializedJvmSymbolsProvider
import org.jetbrains.kotlin.fir.resolve.providers.FirDependenciesSymbolProvider 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.symbols.FirPhaseManager
import org.jetbrains.kotlin.fir.scopes.FirKotlinScopeProvider import org.jetbrains.kotlin.fir.scopes.FirKotlinScopeProvider
import org.jetbrains.kotlin.fir.session.* 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.*
import org.jetbrains.kotlin.idea.fir.low.level.api.FirPhaseRunner import org.jetbrains.kotlin.idea.fir.low.level.api.FirPhaseRunner
import org.jetbrains.kotlin.idea.fir.low.level.api.IdeFirPhaseManager 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.FirModuleWithDependenciesSymbolProvider
import org.jetbrains.kotlin.idea.fir.low.level.api.providers.FirThreadSafeSymbolProviderWrapper 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.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.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.load.kotlin.VirtualFileFinderFactory
import org.jetbrains.kotlin.name.ClassId
import org.jetbrains.kotlin.name.Name import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.platform.jvm.JvmPlatforms import org.jetbrains.kotlin.platform.jvm.JvmPlatforms
import org.jetbrains.kotlin.resolve.jvm.platform.JvmPlatformAnalyzerServices import org.jetbrains.kotlin.resolve.jvm.platform.JvmPlatformAnalyzerServices
@@ -202,7 +209,7 @@ internal object FirIdeSessionFactory {
buildList { buildList {
add( add(
FirThreadSafeSymbolProviderWrapper( FirThreadSafeSymbolProviderWrapper(
KotlinDeserializedJvmSymbolsProvider( KotlinDeserializedJvmSymbolsProviderForIde(
this@session, this@session,
moduleDataProvider, moduleDataProvider,
kotlinScopeProvider, 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( fun createBuiltinsAndCloneableSession(
project: Project, project: Project,