[LL FIR] Symbol provider for binary library FIR session
diff --git a/analysis/low-level-api-fir/src/org/jetbrains/kotlin/analysis/low/level/api/fir/providers/LLFirBinaryLibraryDependenciesSymbolProvider.kt b/analysis/low-level-api-fir/src/org/jetbrains/kotlin/analysis/low/level/api/fir/providers/LLFirBinaryLibraryDependenciesSymbolProvider.kt new file mode 100644 index 0000000..1c1aba8 --- /dev/null +++ b/analysis/low-level-api-fir/src/org/jetbrains/kotlin/analysis/low/level/api/fir/providers/LLFirBinaryLibraryDependenciesSymbolProvider.kt
@@ -0,0 +1,131 @@ +/* + * Copyright 2010-2023 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.analysis.low.level.api.fir.providers + +import org.jetbrains.kotlin.analysis.low.level.api.fir.sessions.LLFirSessionCache +import org.jetbrains.kotlin.analysis.project.structure.KtLibraryModule +import org.jetbrains.kotlin.fir.FirSession +import org.jetbrains.kotlin.fir.resolve.providers.* +import org.jetbrains.kotlin.fir.symbols.impl.FirCallableSymbol +import org.jetbrains.kotlin.fir.symbols.impl.FirClassLikeSymbol +import org.jetbrains.kotlin.fir.symbols.impl.FirNamedFunctionSymbol +import org.jetbrains.kotlin.fir.symbols.impl.FirPropertySymbol +import org.jetbrains.kotlin.name.ClassId +import org.jetbrains.kotlin.name.FqName +import org.jetbrains.kotlin.name.Name +import org.jetbrains.kotlin.utils.addToStdlib.UnsafeCastFunction +import org.jetbrains.kotlin.utils.addToStdlib.safeAs + +internal class LLFirBinaryLibraryDependenciesSymbolProvider( + val symbolProvider: LLFirModuleWithDependenciesSymbolProvider, + val module: KtLibraryModule, + session: FirSession +) : FirSymbolProvider(session) { + val llFirSessionCache = LLFirSessionCache.getInstance(module.project) + val dependencies = buildSet { + addAll(module.directRegularDependencies) + addAll(module.transitiveDependsOnDependencies) + } + override val symbolNamesProvider: FirSymbolNamesProvider = FirNullSymbolNamesProvider + + @OptIn(UnsafeCastFunction::class) + private fun <T> withVisited ( + visitedModules: MutableSet<KtLibraryModule>, + body: FirSymbolProvider.() -> T, + ): T? { + if (module in visitedModules) + return null + visitedModules.add(module) + dependencies.minus(visitedModules).forEach { + val moduleSymbolProvider = llFirSessionCache.getSession(it, true).symbolProvider + val symbol = when(it) { + is KtLibraryModule -> { + moduleSymbolProvider + .safeAs<LLFirBinaryLibraryDependenciesSymbolProvider>()?.run { + symbolProvider.body() ?: withVisited(visitedModules, body) + } + } + else -> { + moduleSymbolProvider.body() + } + } + if (symbol != null) + return symbol + } + return null + } + + @OptIn(UnsafeCastFunction::class) + private fun withVisitedVoid( + visitedModules: MutableSet<KtLibraryModule>, + body: FirSymbolProvider.() -> Unit, + ) { + if (module in visitedModules) + return + visitedModules.add(module) + dependencies.minus(visitedModules).forEach { + val moduleSymbolProvider = llFirSessionCache.getSession(it, true).symbolProvider + when (it) { + is KtLibraryModule -> { + moduleSymbolProvider + .safeAs<LLFirBinaryLibraryDependenciesSymbolProvider>()?.run { + symbolProvider.body() + withVisited(visitedModules, body) + } + + } + else -> { + moduleSymbolProvider.body() + } + } + } + } + + override fun getClassLikeSymbolByClassId(classId: ClassId): FirClassLikeSymbol<*>? { + return symbolProvider.getClassLikeSymbolByClassId(classId) ?: withVisited(mutableSetOf()) { + getClassLikeSymbolByClassId(classId) + } + } + + @FirSymbolProviderInternals + override fun getTopLevelCallableSymbolsTo( + destination: MutableList<FirCallableSymbol<*>>, + packageFqName: FqName, + name: Name, + ) { + symbolProvider.getTopLevelCallableSymbolsTo(destination, packageFqName, name) + withVisitedVoid(mutableSetOf()) { + getTopLevelCallableSymbolsTo(destination, packageFqName, name) + } + } + + @FirSymbolProviderInternals + override fun getTopLevelFunctionSymbolsTo( + destination: MutableList<FirNamedFunctionSymbol>, + packageFqName: FqName, + name: Name, + ) { + symbolProvider.getTopLevelFunctionSymbolsTo(destination, packageFqName, name) + withVisitedVoid(mutableSetOf()) { + getTopLevelFunctionSymbolsTo(destination, packageFqName, name) + } + } + + @FirSymbolProviderInternals + override fun getTopLevelPropertySymbolsTo(destination: MutableList<FirPropertySymbol>, packageFqName: FqName, name: Name) { + symbolProvider.getTopLevelPropertySymbolsTo(destination, packageFqName, name) + withVisitedVoid(mutableSetOf()) { + getTopLevelPropertySymbolsTo(destination, packageFqName, name) + } + } + + override fun getPackage(fqName: FqName): FqName? { + return symbolProvider.getPackage(fqName) ?: withVisited(mutableSetOf()) { + getPackage(fqName) + } + } + +} \ No newline at end of file
diff --git a/analysis/low-level-api-fir/src/org/jetbrains/kotlin/analysis/low/level/api/fir/sessions/LLFirAbstractSessionFactory.kt b/analysis/low-level-api-fir/src/org/jetbrains/kotlin/analysis/low/level/api/fir/sessions/LLFirAbstractSessionFactory.kt index c9d1367..5f09657 100644 --- a/analysis/low-level-api-fir/src/org/jetbrains/kotlin/analysis/low/level/api/fir/sessions/LLFirAbstractSessionFactory.kt +++ b/analysis/low-level-api-fir/src/org/jetbrains/kotlin/analysis/low/level/api/fir/sessions/LLFirAbstractSessionFactory.kt
@@ -32,11 +32,8 @@ import org.jetbrains.kotlin.fir.backend.jvm.FirJvmTypeMapper import org.jetbrains.kotlin.fir.extensions.* import org.jetbrains.kotlin.fir.java.JavaSymbolProvider -import org.jetbrains.kotlin.fir.resolve.providers.DEPENDENCIES_SYMBOL_PROVIDER_QUALIFIED_KEY -import org.jetbrains.kotlin.fir.resolve.providers.FirProvider -import org.jetbrains.kotlin.fir.resolve.providers.FirSymbolProvider +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 @@ -412,7 +409,12 @@ builtinTypes, module.contentScope, builtinsSession.symbolProvider - ) + ).let { + if (module is KtLibraryModule) + LLFirBinaryLibraryDependenciesSymbolProvider(it, module, session) + else + it + } register(LLFirFirClassByPsiClassProvider::class, LLFirFirClassByPsiClassProvider(this)) register(FirProvider::class, LLFirLibrarySessionProvider(symbolProvider))
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 b47ff24..c9bf214 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
@@ -77,8 +77,8 @@ val sessionFactory = createPlatformAwareSessionFactory(module) return when (module) { is KtSourceModule -> sessionFactory.createSourcesSession(module) - is KtLibraryModule, is KtLibrarySourceModule -> sessionFactory.createLibrarySession(module) - is KtSdkModule -> sessionFactory.createBinaryLibrarySession(module) + is KtLibrarySourceModule -> sessionFactory.createLibrarySession(module) + is KtLibraryModule, is KtSdkModule -> sessionFactory.createBinaryLibrarySession(module as KtBinaryModule) is KtScriptModule -> sessionFactory.createScriptSession(module) is KtNotUnderContentRootModule -> sessionFactory.createNotUnderContentRootResolvableSession(module) else -> error("Unexpected module kind: ${module::class.simpleName}")