From b82a589e568868191e9c9b3e62944957c99eb209 Mon Sep 17 00:00:00 2001 From: Marco Pennekamp Date: Mon, 8 May 2023 16:16:44 +0200 Subject: [PATCH] [LL FIR] KT-58325 Implement `JvmStubBasedFirDeserializedSymbolProvider` as `LLFirKotlinSymbolProvider` - This enables merging `JvmStubBasedFirDeserializedSymbolProvider` into `LLFirCombinedKotlinSymbolProvider` automatically. - Crucially, we have to also allow callable caches to take already found callables as a context. - We can also simplify the name cache boilerplate using `LLFirKotlinSymbolProviderWithNameCache`. --- ...FirModuleWithDependenciesSymbolProvider.kt | 25 ++-- ...mStubBasedFirDeserializedSymbolProvider.kt | 112 +++++++++++------- ...irDeclarationForCompiledElementSearcher.kt | 4 +- 3 files changed, 85 insertions(+), 56 deletions(-) diff --git a/analysis/low-level-api-fir/src/org/jetbrains/kotlin/analysis/low/level/api/fir/providers/LLFirModuleWithDependenciesSymbolProvider.kt b/analysis/low-level-api-fir/src/org/jetbrains/kotlin/analysis/low/level/api/fir/providers/LLFirModuleWithDependenciesSymbolProvider.kt index 9a57909581e..61a494cb8ac 100644 --- a/analysis/low-level-api-fir/src/org/jetbrains/kotlin/analysis/low/level/api/fir/providers/LLFirModuleWithDependenciesSymbolProvider.kt +++ b/analysis/low-level-api-fir/src/org/jetbrains/kotlin/analysis/low/level/api/fir/providers/LLFirModuleWithDependenciesSymbolProvider.kt @@ -30,18 +30,19 @@ internal class LLFirModuleWithDependenciesSymbolProvider( val dependencyProvider: LLFirDependenciesSymbolProvider, ) : FirSymbolProvider(session) { override fun getClassLikeSymbolByClassId(classId: ClassId): FirClassLikeSymbol<*>? = - getClassLikeSymbolByFqNameWithoutDependencies(classId) + getClassLikeSymbolByClassIdWithoutDependencies(classId) ?: dependencyProvider.getClassLikeSymbolByClassId(classId) - fun getClassLikeSymbolByFqNameWithoutDependencies(classId: ClassId): FirClassLikeSymbol<*>? = + fun getClassLikeSymbolByClassIdWithoutDependencies(classId: ClassId): FirClassLikeSymbol<*>? = providers.firstNotNullOfOrNull { it.getClassLikeSymbolByClassId(classId) } - fun getClassLikeSymbolByFqNameWithoutDependencies( + @OptIn(FirSymbolProviderInternals::class) + fun getDeserializedClassLikeSymbolByClassIdWithoutDependencies( + classId: ClassId, classLikeDeclaration: KtClassLikeDeclaration, - classId: ClassId - ): FirClassLikeSymbol<*>? { - return providers.filterIsInstance(JvmStubBasedFirDeserializedSymbolProvider::class.java) - .firstNotNullOfOrNull { it.getClassLikeSymbolByClassId(classLikeDeclaration, classId) } + ): FirClassLikeSymbol<*>? = providers.firstNotNullOfOrNull { provider -> + if (provider !is JvmStubBasedFirDeserializedSymbolProvider) return@firstNotNullOfOrNull null + provider.getClassLikeSymbolByClassId(classId, classLikeDeclaration) } @FirSymbolProviderInternals @@ -53,12 +54,14 @@ internal class LLFirModuleWithDependenciesSymbolProvider( @FirSymbolProviderInternals fun getTopLevelDeserializedCallableSymbolsToWithoutDependencies( destination: MutableList>, - callableDeclaration: KtCallableDeclaration, packageFqName: FqName, - shortName: Name + shortName: Name, + callableDeclaration: KtCallableDeclaration, ) { - providers.filterIsInstance(JvmStubBasedFirDeserializedSymbolProvider::class.java) - .forEach { destination.addIfNotNull(it.getTopLevelCallableSymbol(callableDeclaration, packageFqName, shortName)) } + providers.forEach { provider -> + if (provider !is JvmStubBasedFirDeserializedSymbolProvider) return@forEach + destination.addIfNotNull(provider.getTopLevelCallableSymbol(packageFqName, shortName, callableDeclaration)) + } } @FirSymbolProviderInternals diff --git a/analysis/low-level-api-fir/src/org/jetbrains/kotlin/analysis/low/level/api/fir/stubBased/deserialization/JvmStubBasedFirDeserializedSymbolProvider.kt b/analysis/low-level-api-fir/src/org/jetbrains/kotlin/analysis/low/level/api/fir/stubBased/deserialization/JvmStubBasedFirDeserializedSymbolProvider.kt index a0e21e62d30..7d7b0b44193 100644 --- a/analysis/low-level-api-fir/src/org/jetbrains/kotlin/analysis/low/level/api/fir/stubBased/deserialization/JvmStubBasedFirDeserializedSymbolProvider.kt +++ b/analysis/low-level-api-fir/src/org/jetbrains/kotlin/analysis/low/level/api/fir/stubBased/deserialization/JvmStubBasedFirDeserializedSymbolProvider.kt @@ -7,19 +7,19 @@ package org.jetbrains.kotlin.analysis.low.level.api.fir.stubBased.deserializatio import com.intellij.openapi.project.Project import com.intellij.psi.search.GlobalSearchScope +import org.jetbrains.kotlin.analysis.low.level.api.fir.providers.LLFirKotlinSymbolProviderWithNameCache import org.jetbrains.kotlin.analysis.low.level.api.fir.util.LLFirKotlinSymbolProviderNameCache import org.jetbrains.kotlin.analysis.providers.KotlinDeclarationProvider import org.jetbrains.kotlin.analysis.providers.createDeclarationProvider import org.jetbrains.kotlin.fir.FirSession import org.jetbrains.kotlin.fir.caches.FirCache -import org.jetbrains.kotlin.fir.caches.createCache import org.jetbrains.kotlin.fir.caches.firCachesFactory import org.jetbrains.kotlin.fir.caches.getValue import org.jetbrains.kotlin.fir.declarations.FirDeclarationOrigin import org.jetbrains.kotlin.fir.deserialization.SingleModuleDataProvider +import org.jetbrains.kotlin.fir.java.deserialization.JvmClassFileBasedSymbolProvider import org.jetbrains.kotlin.fir.java.deserialization.KotlinBuiltins import org.jetbrains.kotlin.fir.realPsi -import org.jetbrains.kotlin.fir.resolve.providers.FirSymbolProvider import org.jetbrains.kotlin.fir.resolve.providers.FirSymbolProviderInternals import org.jetbrains.kotlin.fir.scopes.FirKotlinScopeProvider import org.jetbrains.kotlin.fir.symbols.impl.* @@ -27,6 +27,7 @@ import org.jetbrains.kotlin.name.* import org.jetbrains.kotlin.psi.* import org.jetbrains.kotlin.resolve.jvm.JvmClassName import org.jetbrains.kotlin.serialization.deserialization.MetadataPackageFragment +import org.jetbrains.kotlin.utils.addToStdlib.ifNotEmpty typealias DeserializedTypeAliasPostProcessor = (FirTypeAliasSymbol) -> Unit @@ -49,16 +50,11 @@ internal open class JvmStubBasedFirDeserializedSymbolProvider( project: Project, scope: GlobalSearchScope, private val initialOrigin: FirDeclarationOrigin -) : FirSymbolProvider(session) { +) : LLFirKotlinSymbolProviderWithNameCache(session) { private val declarationProvider by lazy(LazyThreadSafetyMode.PUBLICATION) { project.createDeclarationProvider(scope, module = null) } private val moduleData = moduleDataProvider.getModuleData(null) - private val namesByPackageCache by lazy(LazyThreadSafetyMode.PUBLICATION) { - LLFirKotlinSymbolProviderNameCache( - session, - declarationProvider - ) - } + override val symbolNameCache: LLFirKotlinSymbolProviderNameCache = LLFirKotlinSymbolProviderNameCache(session, declarationProvider) private val typeAliasCache: FirCache = session.firCachesFactory.createCacheWithPostCompute( @@ -69,6 +65,7 @@ internal open class JvmStubBasedFirDeserializedSymbolProvider( } } ) + private val classCache: FirCache = session.firCachesFactory.createCache( createValue = { classId, context -> findAndDeserializeClass(classId, context) } @@ -77,23 +74,12 @@ internal open class JvmStubBasedFirDeserializedSymbolProvider( private val functionCache = session.firCachesFactory.createCache(::loadFunctionsByCallableId) private val propertyCache = session.firCachesFactory.createCache(::loadPropertiesByCallableId) - override fun computePackageSetWithTopLevelCallables(): Set? { - return namesByPackageCache.getPackageNamesWithTopLevelCallables() - } - - override fun computeCallableNamesInPackage(packageFqName: FqName): Set? = - namesByPackageCache.getTopLevelCallableNamesInPackage(packageFqName) - - override fun knownTopLevelClassifiersInPackage(packageFqName: FqName): Set? { - return namesByPackageCache.getTopLevelClassifierNamesInPackage(packageFqName) - } - private fun findAndDeserializeTypeAlias( classId: ClassId, - context: StubBasedFirDeserializationContext? + context: StubBasedFirDeserializationContext?, ): Pair { val classLikeDeclaration = - context?.classLikeDeclaration ?: declarationProvider.getClassLikeDeclarationByClassId(classId)?.originalElement + (context?.classLikeDeclaration ?: declarationProvider.getClassLikeDeclarationByClassId(classId))?.originalElement if (classLikeDeclaration is KtTypeAlias) { val symbol = FirTypeAliasSymbol(classId) val postProcessor: DeserializedTypeAliasPostProcessor = { @@ -114,14 +100,15 @@ internal open class JvmStubBasedFirDeserializedSymbolProvider( private fun findAndDeserializeClass( classId: ClassId, - parentContext: StubBasedFirDeserializationContext? = null + parentContext: StubBasedFirDeserializationContext?, ): FirRegularClassSymbol? { val (classLikeDeclaration, context) = if (parentContext?.classLikeDeclaration != null) { - parentContext.classLikeDeclaration to null + parentContext.classLikeDeclaration.originalElement to null } else { (declarationProvider.getClassLikeDeclarationByClassId(classId)?.originalElement ?: return null) to parentContext } + val symbol = FirRegularClassSymbol(classId) if (classLikeDeclaration is KtClassOrObject) { deserializeClassToSymbol( @@ -146,8 +133,11 @@ internal open class JvmStubBasedFirDeserializedSymbolProvider( return null } - private fun loadFunctionsByCallableId(callableId: CallableId): List { - val topLevelFunctions = declarationProvider.getTopLevelFunctions(callableId) + private fun loadFunctionsByCallableId( + callableId: CallableId, + foundFunctions: Collection?, + ): List { + val topLevelFunctions = foundFunctions ?: declarationProvider.getTopLevelFunctions(callableId) val origins = if (topLevelFunctions.size > 1) mutableSetOf() else null return topLevelFunctions .mapNotNull { function -> @@ -166,8 +156,8 @@ internal open class JvmStubBasedFirDeserializedSymbolProvider( } } - private fun loadPropertiesByCallableId(callableId: CallableId): List { - val topLevelProperties = declarationProvider.getTopLevelProperties(callableId) + private fun loadPropertiesByCallableId(callableId: CallableId, foundProperties: Collection?): List { + val topLevelProperties = foundProperties ?: declarationProvider.getTopLevelProperties(callableId) val origins = if (topLevelProperties.size > 1) mutableSetOf() else null return topLevelProperties .mapNotNull { property -> @@ -187,45 +177,81 @@ internal open class JvmStubBasedFirDeserializedSymbolProvider( return classCache.getValue(classId, parentContext) } - private fun getTypeAlias(classId: ClassId): FirTypeAliasSymbol? { + private fun getTypeAlias(classId: ClassId, context: StubBasedFirDeserializationContext? = null): FirTypeAliasSymbol? { if (!classId.relativeClassName.isOneSegmentFQN()) return null - return typeAliasCache.getValue(classId) + return typeAliasCache.getValue(classId, context) } @FirSymbolProviderInternals override fun getTopLevelCallableSymbolsTo(destination: MutableList>, packageFqName: FqName, name: Name) { val callableId = CallableId(packageFqName, name) - destination += functionCache.getCallables(callableId) - destination += propertyCache.getCallables(callableId) + destination += functionCache.getCallablesWithoutContext(callableId) + destination += propertyCache.getCallablesWithoutContext(callableId) } - private fun > FirCache, Nothing?>.getCallables(id: CallableId): List { - if (!namesByPackageCache.mayHaveTopLevelCallable(id.packageName, id.callableName)) return emptyList() - return getValue(id) + private fun , CONTEXT> FirCache, CONTEXT?>.getCallablesWithoutContext( + id: CallableId, + ): List { + if (!symbolNameCache.mayHaveTopLevelCallable(id.packageName, id.callableName)) return emptyList() + return getValue(id, null) + } + + @FirSymbolProviderInternals + override fun getTopLevelCallableSymbolsTo( + destination: MutableList>, + callableId: CallableId, + callables: Collection, + ) { + callables.filterIsInstance().ifNotEmpty { + destination += functionCache.getValue(callableId, this) + } + + callables.filterIsInstance().ifNotEmpty { + destination += propertyCache.getValue(callableId, this) + } } @FirSymbolProviderInternals override fun getTopLevelFunctionSymbolsTo(destination: MutableList, packageFqName: FqName, name: Name) { - destination += functionCache.getCallables(CallableId(packageFqName, name)) + destination += functionCache.getCallablesWithoutContext(CallableId(packageFqName, name)) + } + + @FirSymbolProviderInternals + override fun getTopLevelFunctionSymbolsTo( + destination: MutableList, + callableId: CallableId, + functions: Collection, + ) { + destination += functionCache.getValue(callableId, functions) } @FirSymbolProviderInternals override fun getTopLevelPropertySymbolsTo(destination: MutableList, packageFqName: FqName, name: Name) { - destination += propertyCache.getCallables(CallableId(packageFqName, name)) + destination += propertyCache.getCallablesWithoutContext(CallableId(packageFqName, name)) + } + + @FirSymbolProviderInternals + override fun getTopLevelPropertySymbolsTo( + destination: MutableList, + callableId: CallableId, + properties: Collection, + ) { + destination += propertyCache.getValue(callableId, properties) } override fun getPackage(fqName: FqName): FqName? = fqName.takeIf { - namesByPackageCache.getTopLevelClassifierNamesInPackage(fqName)?.isNotEmpty() == true || - namesByPackageCache.getPackageNamesWithTopLevelCallables()?.contains(fqName.asString()) == true + symbolNameCache.getTopLevelClassifierNamesInPackage(fqName)?.isNotEmpty() == true || + symbolNameCache.getPackageNamesWithTopLevelCallables()?.contains(fqName.asString()) == true } override fun getClassLikeSymbolByClassId(classId: ClassId): FirClassLikeSymbol<*>? { - if (!namesByPackageCache.mayHaveTopLevelClassifier(classId, mayHaveFunctionClass = false)) return null + if (!symbolNameCache.mayHaveTopLevelClassifier(classId, mayHaveFunctionClass = false)) return null return getClass(classId) ?: getTypeAlias(classId) } - fun getClassLikeSymbolByClassId(classLikeDeclaration: KtClassLikeDeclaration, classId: ClassId): FirClassLikeSymbol<*>? { + @FirSymbolProviderInternals + override fun getClassLikeSymbolByClassId(classId: ClassId, classLikeDeclaration: KtClassLikeDeclaration): FirClassLikeSymbol<*>? { val annotationDeserializer = StubBasedAnnotationDeserializer(session) val deserializationContext = StubBasedFirDeserializationContext( moduleData, @@ -256,9 +282,9 @@ internal open class JvmStubBasedFirDeserializedSymbolProvider( } fun getTopLevelCallableSymbol( - callableDeclaration: KtCallableDeclaration, packageFqName: FqName, - shortName: Name + shortName: Name, + callableDeclaration: KtCallableDeclaration, ): FirCallableSymbol<*>? { //possible overloads spoils here //we can't use only this callable instead of index access to fill the cache diff --git a/analysis/low-level-api-fir/src/org/jetbrains/kotlin/analysis/low/level/api/fir/util/FirDeclarationForCompiledElementSearcher.kt b/analysis/low-level-api-fir/src/org/jetbrains/kotlin/analysis/low/level/api/fir/util/FirDeclarationForCompiledElementSearcher.kt index f0a1b339875..d37990956e6 100644 --- a/analysis/low-level-api-fir/src/org/jetbrains/kotlin/analysis/low/level/api/fir/util/FirDeclarationForCompiledElementSearcher.kt +++ b/analysis/low-level-api-fir/src/org/jetbrains/kotlin/analysis/low/level/api/fir/util/FirDeclarationForCompiledElementSearcher.kt @@ -75,7 +75,7 @@ internal class FirDeclarationForCompiledElementSearcher(private val symbolProvid val classCandidate = when (symbolProvider) { is LLFirModuleWithDependenciesSymbolProvider -> { - symbolProvider.getClassLikeSymbolByFqNameWithoutDependencies(declaration, classId) + symbolProvider.getDeserializedClassLikeSymbolByClassIdWithoutDependencies(classId, declaration) ?: symbolProvider.friendBuiltinsProvider?.getClassLikeSymbolByClassId(classId) } else -> { @@ -170,7 +170,7 @@ private fun FirSymbolProvider.findCallableCandidates( @OptIn(FirSymbolProviderInternals::class) return when (this) { is LLFirModuleWithDependenciesSymbolProvider -> buildList { - getTopLevelDeserializedCallableSymbolsToWithoutDependencies(this, declaration, packageFqName, shortName) + getTopLevelDeserializedCallableSymbolsToWithoutDependencies(this, packageFqName, shortName, declaration) friendBuiltinsProvider?.getTopLevelCallableSymbolsTo(this, packageFqName, shortName) } else -> getTopLevelCallableSymbols(packageFqName, shortName)