diff --git a/analysis/low-level-api-fir/src/org/jetbrains/kotlin/analysis/low/level/api/fir/project/structure/LLFirLibraryProviderFactory.kt b/analysis/low-level-api-fir/src/org/jetbrains/kotlin/analysis/low/level/api/fir/project/structure/LLFirLibraryProviderFactory.kt index 7ecf956627f..e2860d30d1a 100644 --- a/analysis/low-level-api-fir/src/org/jetbrains/kotlin/analysis/low/level/api/fir/project/structure/LLFirLibraryProviderFactory.kt +++ b/analysis/low-level-api-fir/src/org/jetbrains/kotlin/analysis/low/level/api/fir/project/structure/LLFirLibraryProviderFactory.kt @@ -7,11 +7,10 @@ package org.jetbrains.kotlin.analysis.low.level.api.fir.project.structure import com.intellij.openapi.project.Project import com.intellij.psi.search.GlobalSearchScope -import org.jetbrains.kotlin.analysis.low.level.api.fir.providers.LLFirDependentModuleProvidersByProviders +import org.jetbrains.kotlin.analysis.low.level.api.fir.providers.LLFirDependenciesSymbolProvider import org.jetbrains.kotlin.analysis.low.level.api.fir.providers.LLFirModuleWithDependenciesSymbolProvider import org.jetbrains.kotlin.analysis.low.level.api.fir.sessions.LLFirJavaFacadeForBinaries import org.jetbrains.kotlin.analysis.low.level.api.fir.sessions.LLFirSession -import org.jetbrains.kotlin.analysis.project.structure.KtBinaryModule import org.jetbrains.kotlin.analysis.providers.createPackagePartProvider import org.jetbrains.kotlin.fir.BuiltinTypes import org.jetbrains.kotlin.fir.deserialization.SingleModuleDataProvider @@ -36,7 +35,6 @@ internal object LLFirLibraryProviderFactory { val packagePartProvider = project.createPackagePartProvider(scope) return LLFirModuleWithDependenciesSymbolProvider( session, - LLFirDependentModuleProvidersByProviders(session, listOf(builtinSymbolProvider)), providers = listOf( JvmClassFileBasedSymbolProvider( session, @@ -48,6 +46,7 @@ internal object LLFirLibraryProviderFactory { ), OptionalAnnotationClassesProvider(session, moduleDataProvider, kotlinScopeProvider, packagePartProvider), ), + LLFirDependenciesSymbolProvider(session, listOf(builtinSymbolProvider)), ) } @@ -75,5 +74,4 @@ internal object LLFirLibraryProviderFactory { add(OptionalAnnotationClassesProvider(session, moduleDataProvider, kotlinScopeProvider, packagePartProvider)) } } - } \ No newline at end of file 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 3f15f214b11..5e4a6db46c1 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 @@ -5,12 +5,10 @@ package org.jetbrains.kotlin.analysis.low.level.api.fir.providers -import org.jetbrains.kotlin.analysis.low.level.api.fir.sessions.LLFirSession import org.jetbrains.kotlin.analysis.utils.collections.buildSmartList import org.jetbrains.kotlin.fir.FirSession import org.jetbrains.kotlin.fir.resolve.providers.FirSymbolProvider import org.jetbrains.kotlin.fir.resolve.providers.FirSymbolProviderInternals -import org.jetbrains.kotlin.fir.resolve.providers.symbolProvider import org.jetbrains.kotlin.fir.symbols.impl.FirCallableSymbol import org.jetbrains.kotlin.fir.symbols.impl.FirClassLikeSymbol import org.jetbrains.kotlin.fir.symbols.impl.FirNamedFunctionSymbol @@ -24,15 +22,13 @@ import org.jetbrains.kotlin.utils.SmartSet internal class LLFirModuleWithDependenciesSymbolProvider( session: FirSession, - val dependencyProvider: LLFirDependentModuleProviders, - private val providers: List, + val providers: List, + val dependencyProvider: LLFirDependenciesSymbolProvider, ) : FirSymbolProvider(session) { - override fun getClassLikeSymbolByClassId(classId: ClassId): FirClassLikeSymbol<*>? = getClassLikeSymbolByFqNameWithoutDependencies(classId) ?: dependencyProvider.getClassLikeSymbolByClassId(classId) - fun getClassLikeSymbolByFqNameWithoutDependencies(classId: ClassId): FirClassLikeSymbol<*>? = providers.firstNotNullOfOrNull { it.getClassLikeSymbolByClassId(classId) } @@ -82,42 +78,34 @@ internal class LLFirModuleWithDependenciesSymbolProvider( getPackageWithoutDependencies(fqName) ?: dependencyProvider.getPackage(fqName) - override fun computePackageSetWithTopLevelCallables(): Set? = null - - override fun knownTopLevelClassifiersInPackage(packageFqName: FqName): Set? = null - - override fun computeCallableNamesInPackage(packageFqName: FqName): Set? = null - - fun getPackageWithoutDependencies(fqName: FqName): FqName? = providers.firstNotNullOfOrNull { it.getPackage(fqName) } + + override fun computePackageSetWithTopLevelCallables(): Set? = null + override fun knownTopLevelClassifiersInPackage(packageFqName: FqName): Set? = null + override fun computeCallableNamesInPackage(packageFqName: FqName): Set? = null } -internal abstract class LLFirDependentModuleProviders( +internal class LLFirDependenciesSymbolProvider( session: FirSession, + val providers: List, ) : FirSymbolProvider(session) { - - abstract val dependentProviders: List - abstract val dependentSessions: List + init { + require(providers.all { it !is LLFirModuleWithDependenciesSymbolProvider }) { + "${LLFirDependenciesSymbolProvider::class.simpleName} may not contain ${LLFirModuleWithDependenciesSymbolProvider::class.simpleName}:" + + " dependency providers must be flattened during session creation." + } + } override fun getClassLikeSymbolByClassId(classId: ClassId): FirClassLikeSymbol<*>? = - dependentProviders.firstNotNullOfOrNull { provider -> - when (provider) { - is LLFirModuleWithDependenciesSymbolProvider -> provider.getClassLikeSymbolByFqNameWithoutDependencies(classId) - else -> provider.getClassLikeSymbolByClassId(classId) - } - } - + providers.firstNotNullOfOrNull { it.getClassLikeSymbolByClassId(classId) } @FirSymbolProviderInternals override fun getTopLevelCallableSymbolsTo(destination: MutableList>, packageFqName: FqName, name: Name) { val facades = SmartSet.create() - for (provider in dependentProviders) { + for (provider in providers) { val newSymbols = buildSmartList { - when (provider) { - is LLFirModuleWithDependenciesSymbolProvider -> provider.getTopLevelCallableSymbolsToWithoutDependencies(this, packageFqName, name) - else -> provider.getTopLevelCallableSymbolsTo(this, packageFqName, name) - } + provider.getTopLevelCallableSymbolsTo(this, packageFqName, name) } addNewSymbolsConsideringJvmFacades(destination, newSymbols, facades) } @@ -126,42 +114,27 @@ internal abstract class LLFirDependentModuleProviders( @FirSymbolProviderInternals override fun getTopLevelFunctionSymbolsTo(destination: MutableList, packageFqName: FqName, name: Name) { val facades = SmartSet.create() - for (provider in dependentProviders) { + for (provider in providers) { val newSymbols = buildSmartList { - when (provider) { - is LLFirModuleWithDependenciesSymbolProvider -> provider.getTopLevelFunctionSymbolsToWithoutDependencies(this, packageFqName, name) - else -> provider.getTopLevelFunctionSymbolsTo(this, packageFqName, name) - } + provider.getTopLevelFunctionSymbolsTo(this, packageFqName, name) } - addNewSymbolsConsideringJvmFacades(destination, newSymbols, facades) } - } @FirSymbolProviderInternals override fun getTopLevelPropertySymbolsTo(destination: MutableList, packageFqName: FqName, name: Name) { val facades = SmartSet.create() - for (provider in dependentProviders) { + for (provider in providers) { val newSymbols = buildSmartList { - when (provider) { - is LLFirModuleWithDependenciesSymbolProvider -> provider.getTopLevelPropertySymbolsToWithoutDependencies(this, packageFqName, name) - else -> provider.getTopLevelPropertySymbolsTo(this, packageFqName, name) - } + provider.getTopLevelPropertySymbolsTo(this, packageFqName, name) } addNewSymbolsConsideringJvmFacades(destination, newSymbols, facades) } } - override fun getPackage(fqName: FqName): FqName? = - dependentProviders.firstNotNullOfOrNull { provider -> - when (provider) { - is LLFirModuleWithDependenciesSymbolProvider -> provider.getPackageWithoutDependencies(fqName) - else -> provider.getPackage(fqName) - } - } + override fun getPackage(fqName: FqName): FqName? = providers.firstNotNullOfOrNull { it.getPackage(fqName) } - // TODO: Consider having proper implementations for sake of optimizations override fun computePackageSetWithTopLevelCallables(): Set? = null override fun knownTopLevelClassifiersInPackage(packageFqName: FqName): Set? = null override fun computeCallableNamesInPackage(packageFqName: FqName): Set? = null @@ -191,31 +164,4 @@ internal abstract class LLFirDependentModuleProviders( val jvmPackagePartSource = fir.containerSource as? JvmPackagePartSource ?: return null return jvmPackagePartSource.facadeClassName ?: jvmPackagePartSource.className } - -} - -internal class LLFirDependentModuleProvidersBySessions( - session: FirSession, - override val dependentSessions: List -) : LLFirDependentModuleProviders(session) { - - override val dependentProviders: List by lazy(LazyThreadSafetyMode.PUBLICATION) { - dependentSessions.map { it.symbolProvider } - } - - constructor(session: FirSession, createSessions: MutableList.() -> Unit) - : this(session, buildList { createSessions() }) -} - - -internal class LLFirDependentModuleProvidersByProviders( - session: FirSession, - override val dependentProviders: List, -) : LLFirDependentModuleProviders(session) { - - override val dependentSessions: List - get() = dependentProviders.map { it.session as LLFirSession } - - constructor(session: FirSession, createProviders: MutableList.() -> Unit) - : this(session, buildList { createProviders() }) } 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 7155146fe29..adf79a647d8 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 @@ -38,6 +38,7 @@ import org.jetbrains.kotlin.fir.extensions.* import org.jetbrains.kotlin.fir.java.JavaSymbolProvider import org.jetbrains.kotlin.fir.resolve.providers.* import org.jetbrains.kotlin.fir.resolve.providers.impl.FirExtensionSyntheticFunctionInterfaceProvider +import org.jetbrains.kotlin.fir.resolve.providers.symbolProvider import org.jetbrains.kotlin.fir.resolve.scopes.wrapScopeWithJvmMapped import org.jetbrains.kotlin.fir.resolve.transformers.FirDummyCompilerLazyDeclarationResolver import org.jetbrains.kotlin.fir.scopes.FirKotlinScopeProvider @@ -157,10 +158,10 @@ internal class LLFirSessionCache(private val project: Project) { register(FirSwitchableExtensionDeclarationsSymbolProvider::class, it) } - val dependencyProvider = LLFirDependentModuleProvidersBySessions(this) { - addAll(dependencies) - add(builtinsSession) - } + val dependencyProvider = LLFirDependenciesSymbolProvider(this, buildList { + addDependencySymbolProvidersTo(session, dependencies, this) + add(builtinsSession.symbolProvider) + }) val javaSymbolProvider = createJavaSymbolProvider(this, moduleData, project, contentScope) val syntheticFunctionalInterfaceProvider = FirExtensionSyntheticFunctionInterfaceProvider(this, moduleData, scopeProvider) @@ -168,13 +169,13 @@ internal class LLFirSessionCache(private val project: Project) { FirSymbolProvider::class, LLFirModuleWithDependenciesSymbolProvider( this, - dependencyProvider, providers = listOfNotNull( provider.symbolProvider, switchableExtensionDeclarationsSymbolProvider, javaSymbolProvider, syntheticFunctionalInterfaceProvider, ), + dependencyProvider, ) ) register(JavaSymbolProvider::class, javaSymbolProvider) @@ -239,7 +240,7 @@ internal class LLFirSessionCache(private val project: Project) { register(FirRegisteredPluginAnnotations::class, LLFirIdeRegisteredPluginAnnotations(this, annotationsResolver)) register(FirPredicateBasedProvider::class, FirEmptyPredicateBasedProvider) - val dependencyProvider = LLFirDependentModuleProvidersByProviders(this) { + val dependencyProvider = LLFirDependenciesSymbolProvider(this, buildList { add(builtinsSession.symbolProvider) // Script dependencies are self-contained and should not depend on other libraries @@ -255,18 +256,18 @@ internal class LLFirSessionCache(private val project: Project) { addAll(restLibrariesProvider) } - } + }) val javaSymbolProvider = createJavaSymbolProvider(this, moduleData, project, contentScope) register( FirSymbolProvider::class, LLFirModuleWithDependenciesSymbolProvider( this, - dependencyProvider, providers = listOf( provider.symbolProvider, javaSymbolProvider, ), + dependencyProvider, ) ) register(JavaSymbolProvider::class, javaSymbolProvider) @@ -358,10 +359,10 @@ internal class LLFirSessionCache(private val project: Project) { register(FirProvider::class, provider) register(FirLazyDeclarationResolver::class, LLFirLazyDeclarationResolver()) - val dependencyProvider = LLFirDependentModuleProvidersBySessions(this) { - addAll(dependencies) - add(builtinsSession) - } + val dependencyProvider = LLFirDependenciesSymbolProvider(this, buildList { + addDependencySymbolProvidersTo(session, dependencies, this) + add(builtinsSession.symbolProvider) + }) val javaSymbolProvider = createJavaSymbolProvider(this, moduleData, project, contentScope) register(JavaSymbolProvider::class, javaSymbolProvider) @@ -370,11 +371,11 @@ internal class LLFirSessionCache(private val project: Project) { FirSymbolProvider::class, LLFirModuleWithDependenciesSymbolProvider( this, - dependencyProvider, providers = listOfNotNull( javaSymbolProvider, provider.symbolProvider, - ) + ), + dependencyProvider, ) ) @@ -423,18 +424,16 @@ internal class LLFirSessionCache(private val project: Project) { register(FirProvider::class, provider) register(FirLazyDeclarationResolver::class, LLFirLazyDeclarationResolver()) - val dependencyProvider = LLFirDependentModuleProvidersBySessions(this) { - add(builtinsSession) - } + val dependencyProvider = LLFirDependenciesSymbolProvider(this, listOf(builtinsSession.symbolProvider)) register( FirSymbolProvider::class, LLFirModuleWithDependenciesSymbolProvider( this, - dependencyProvider, - providers = listOfNotNull( + providers = listOf( provider.symbolProvider, - ) + ), + dependencyProvider, ) ) @@ -466,8 +465,6 @@ internal class LLFirSessionCache(private val project: Project) { } private fun collectSourceModuleDependencies(module: KtModule): List { - val dependencies = mutableListOf() - fun getOrCreateSessionForDependency(dependency: KtModule): LLFirSession? = when (dependency) { is KtBuiltinsModule -> null // Built-ins are already added is KtBinaryModule -> getSession(dependency, preferBinary = true) @@ -479,18 +476,15 @@ internal class LLFirSessionCache(private val project: Project) { is KtLibrarySourceModule -> error("Module $module cannot depend on ${dependency::class}: $dependency") } - module.directRegularDependencies.mapNotNullTo(dependencies, ::getOrCreateSessionForDependency) + val dependencyModules = buildSet { + addAll(module.directRegularDependencies) - // The dependency provider needs to have access to all direct and indirect `dependsOn` dependencies, as `dependsOn` - // dependencies are transitive. - val directRegularDependenciesSet = module.directRegularDependencies.toSet() - module.transitiveDependsOnDependencies.forEach { dependency -> - if (dependency !in directRegularDependenciesSet) { - getOrCreateSessionForDependency(dependency)?.let(dependencies::add) - } + // The dependency provider needs to have access to all direct and indirect `dependsOn` dependencies, as `dependsOn` + // dependencies are transitive. + addAll(module.transitiveDependsOnDependencies) } - return dependencies + return dependencyModules.mapNotNull(::getOrCreateSessionForDependency) } private fun createSourceModuleDependencyTracker(module: KtModule, exposedDependencies: List): ModificationTracker { @@ -506,6 +500,43 @@ internal class LLFirSessionCache(private val project: Project) { private fun createModuleData(session: LLFirSession): LLFirModuleData { return LLFirModuleData(session.ktModule).apply { bindSession(session) } } + + /** + * Adds dependency symbol providers from [dependencies] to [destination]. The function might combine, reorder, or exclude specific + * symbol providers for optimization. + */ + private fun addDependencySymbolProvidersTo( + session: LLFirSession, + dependencies: List, + destination: MutableList, + ) { + val dependencyProviders = buildList { + dependencies.forEach { session -> + when (val dependencyProvider = session.symbolProvider) { + is LLFirModuleWithDependenciesSymbolProvider -> addAll(dependencyProvider.providers) + else -> add(dependencyProvider) + } + } + } + + dependencyProviders.mergeDependencySymbolProvidersInto(session, destination) + } + + /** + * Merges dependency symbol providers of the same kind if possible. The merged symbol provider usually delegates to its subordinate + * symbol providers to preserve session semantics, but it will have some form of advantage over individual symbol providers (such as + * querying an index once instead of N times). + * + * [session] should be the session of the dependent module. Because all symbol providers are tied to a session, we need a session to + * create a combined symbol provider. + */ + @Suppress("UNUSED_PARAMETER") + private fun List.mergeDependencySymbolProvidersInto( + session: FirSession, + destination: MutableList, + ) { + destination.addAll(this) + } } internal fun LLFirSessionConfigurator.Companion.configure(session: LLFirSession) { 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 719186a3c98..6f8e9db6c8f 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 @@ -125,7 +125,7 @@ internal class FirDeclarationForCompiledElementSearcher(private val symbolProvid private val LLFirModuleWithDependenciesSymbolProvider.friendBuiltinsProvider: FirSymbolProvider? get() { if (getPackageWithoutDependencies(StandardClassIds.BASE_KOTLIN_PACKAGE) != null) { - return dependencyProvider.dependentProviders.find { it.session is LLFirBuiltinsAndCloneableSession } + return dependencyProvider.providers.find { it.session is LLFirBuiltinsAndCloneableSession } } return null