[LL FIR] Add top-level callable package name set support to LLFirSymbolProviderNameCache

- `KotlinDeclarationProvider` supports computing the top-level callable
  package name set now, so we can propagate it to the symbol name cache
  implementation.
- Adds the package name check to `getTopLevelCallableNamesInPackage`
  directly, which will likely reduce the size of
  `topLevelCallableNamesByPackage`, because it needs to store fewer
  empty sets.
This commit is contained in:
Marco Pennekamp
2023-05-08 15:24:33 +02:00
committed by Space Team
parent e10a9263d0
commit 63f29b5d24
6 changed files with 57 additions and 36 deletions
@@ -17,6 +17,7 @@ import org.jetbrains.kotlin.analysis.providers.createDeclarationProvider
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.flatMapToNullableSet
import org.jetbrains.kotlin.fir.symbols.impl.FirCallableSymbol
import org.jetbrains.kotlin.fir.symbols.impl.FirClassLikeSymbol
import org.jetbrains.kotlin.fir.symbols.impl.FirNamedFunctionSymbol
@@ -47,13 +48,14 @@ internal class LLFirCombinedKotlinSymbolProvider private constructor(
private val declarationProvider: KotlinDeclarationProvider,
) : LLFirSelectingCombinedSymbolProvider<LLFirProvider.SymbolProvider>(session, project, providers) {
private val symbolNameCache = object : LLFirSymbolProviderNameCacheBase(session) {
override fun computeClassifierNames(packageFqName: FqName): Set<String>? = buildSet {
providers.forEach { addAll(it.knownTopLevelClassifiersInPackage(packageFqName) ?: return null) }
}
override fun computeClassifierNames(packageFqName: FqName): Set<String>? =
providers.flatMapToNullableSet { it.knownTopLevelClassifiersInPackage(packageFqName) }
override fun computeCallableNames(packageFqName: FqName): Set<Name>? = buildSet {
providers.forEach { addAll(it.computeCallableNamesInPackage(packageFqName) ?: return null) }
}
override fun computeCallableNames(packageFqName: FqName): Set<Name>? =
providers.flatMapToNullableSet { it.computeCallableNamesInPackage(packageFqName) }
override fun computePackageNamesWithTopLevelCallables(): Set<String>? =
providers.flatMapToNullableSet { it.computePackageSetWithTopLevelCallables() }
}
private val classifierCache = NullableCaffeineCache<ClassId, FirClassLikeSymbol<*>> { it.maximumSize(500) }
@@ -195,8 +195,8 @@ internal class LLFirProvider(
override fun getPackage(fqName: FqName): FqName? =
providerHelper.getPackage(fqName)
// Computing the set of such package names is expensive and would require a new index. For now, it is not worth the marginal gains.
override fun computePackageSetWithTopLevelCallables(): Set<String>? = null
override fun computePackageSetWithTopLevelCallables(): Set<String>? =
providerHelper.symbolNameCache.getPackageNamesWithTopLevelCallables()
override fun knownTopLevelClassifiersInPackage(packageFqName: FqName): Set<String>? =
providerHelper.symbolNameCache.getTopLevelClassifierNamesInPackage(packageFqName)
@@ -70,30 +70,34 @@ private class LLFirResolveExtensionToolNameCache(
private val packageFilter: LLFirResolveExtensionToolPackageFilter,
private val fileProvider: LLFirResolveExtensionsFileProvider,
) : LLFirSymbolProviderNameCache() {
override fun getTopLevelClassifierNamesInPackage(packageFqName: FqName): Set<String>? = forbidAnalysis {
override fun getTopLevelClassifierNamesInPackage(packageFqName: FqName): Set<String> = forbidAnalysis {
if (!packageFilter.packageExists(packageFqName)) return emptySet()
return fileProvider.getFilesByPackage(packageFqName)
fileProvider.getFilesByPackage(packageFqName)
.flatMap { it.getTopLevelClassifierNames() }
.mapTo(mutableSetOf()) { it.asString() }
}
override fun getPackageNamesWithTopLevelCallables(): Set<String> = forbidAnalysis {
packageFilter.getAllPackages().mapTo(mutableSetOf()) { it.asString() }
}
override fun getTopLevelCallableNamesInPackage(packageFqName: FqName): Set<Name> = forbidAnalysis {
if (!packageFilter.packageExists(packageFqName)) return emptySet()
return fileProvider.getFilesByPackage(packageFqName)
fileProvider.getFilesByPackage(packageFqName)
.flatMapTo(mutableSetOf()) { it.getTopLevelCallableNames() }
}
override fun mayHaveTopLevelClassifier(classId: ClassId, mayHaveFunctionClass: Boolean): Boolean = forbidAnalysis {
if (!packageFilter.packageExists(classId.packageFqName)) return false
return fileProvider.getFilesByPackage(classId.packageFqName)
fileProvider.getFilesByPackage(classId.packageFqName)
.any { it.mayHaveTopLevelClassifier(classId.getTopLevelShortClassName()) }
}
override fun mayHaveTopLevelCallable(packageFqName: FqName, name: Name): Boolean = forbidAnalysis {
if (!packageFilter.packageExists(packageFqName)) return false
return fileProvider.getFilesByPackage(packageFqName)
fileProvider.getFilesByPackage(packageFqName)
.any { it.mayHaveTopLevelCallable(name) }
}
}
@@ -52,9 +52,6 @@ internal open class JvmStubBasedFirDeserializedSymbolProvider(
) : FirSymbolProvider(session) {
private val declarationProvider by lazy(LazyThreadSafetyMode.PUBLICATION) { project.createDeclarationProvider(scope, module = null) }
private val moduleData = moduleDataProvider.getModuleData(null)
private val packageSetWithTopLevelCallableDeclarations: Set<String> by lazy(LazyThreadSafetyMode.PUBLICATION) {
declarationProvider.computePackageSetWithTopLevelCallableDeclarations()
}
private val namesByPackageCache by lazy(LazyThreadSafetyMode.PUBLICATION) {
LLFirKotlinSymbolProviderNameCache(
@@ -80,8 +77,8 @@ internal open class JvmStubBasedFirDeserializedSymbolProvider(
private val functionCache = session.firCachesFactory.createCache(::loadFunctionsByCallableId)
private val propertyCache = session.firCachesFactory.createCache(::loadPropertiesByCallableId)
override fun computePackageSetWithTopLevelCallables(): Set<String> {
return packageSetWithTopLevelCallableDeclarations
override fun computePackageSetWithTopLevelCallables(): Set<String>? {
return namesByPackageCache.getPackageNamesWithTopLevelCallables()
}
override fun computeCallableNamesInPackage(packageFqName: FqName): Set<Name>? =
@@ -203,7 +200,6 @@ internal open class JvmStubBasedFirDeserializedSymbolProvider(
}
private fun <C : FirCallableSymbol<*>> FirCache<CallableId, List<C>, Nothing?>.getCallables(id: CallableId): List<C> {
if (id.packageName.asString() !in packageSetWithTopLevelCallableDeclarations) return emptyList()
if (!namesByPackageCache.mayHaveTopLevelCallable(id.packageName, id.callableName)) return emptyList()
return getValue(id)
}
@@ -218,13 +214,11 @@ internal open class JvmStubBasedFirDeserializedSymbolProvider(
destination += propertyCache.getCallables(CallableId(packageFqName, name))
}
override fun getPackage(fqName: FqName): FqName? {
return if (!namesByPackageCache.getTopLevelClassifierNamesInPackage(fqName)
.isNullOrEmpty() || packageSetWithTopLevelCallableDeclarations.contains(fqName.asString())
) {
fqName
} else null
}
override fun getPackage(fqName: FqName): FqName? =
fqName.takeIf {
namesByPackageCache.getTopLevelClassifierNamesInPackage(fqName)?.isNotEmpty() == true ||
namesByPackageCache.getPackageNamesWithTopLevelCallables()?.contains(fqName.asString()) == true
}
override fun getClassLikeSymbolByClassId(classId: ClassId): FirClassLikeSymbol<*>? {
if (!namesByPackageCache.mayHaveTopLevelClassifier(classId, mayHaveFunctionClass = false)) return null
@@ -22,6 +22,9 @@ internal class LLFirKotlinSymbolProviderNameCache(
.getTopLevelKotlinClassLikeDeclarationNamesInPackage(packageFqName)
.mapTo(mutableSetOf()) { it.asString() }
override fun computePackageNamesWithTopLevelCallables(): Set<String> =
declarationProvider.computePackageSetWithTopLevelCallableDeclarations()
override fun computeCallableNames(packageFqName: FqName): Set<Name> =
declarationProvider.getTopLevelCallableNamesInPackage(packageFqName)
}
@@ -19,7 +19,7 @@ import org.jetbrains.kotlin.name.Name
* Caches the names of classifiers and callables contained in a package. [LLFirSymbolProviderNameCache] is used by symbol providers to abort
* symbol finding early if the symbol name isn't contained in the symbol provider's domain.
*
* For the [getTopLevelClassifierNamesInPackage] and [getTopLevelCallableNamesInPackage] functions,
* For the [getTopLevelClassifierNamesInPackage], [getTopLevelCallableNamesInPackage], and [getPackageNamesWithTopLevelCallables] functions,
* the result may have false-positive entries but cannot have false-negative entries.
* It should contain all the names in the package but may have some additional names that are not there.
* Also, the `null` value might be returned when it's too heavyweight to calculate the results.
@@ -35,6 +35,10 @@ internal abstract class LLFirSymbolProviderNameCache {
*/
abstract fun getTopLevelClassifierNamesInPackage(packageFqName: FqName): Set<String>?
/**
* Returns the set of package names which contain a top-level callable declaration within the cache's scope.
*/
abstract fun getPackageNamesWithTopLevelCallables(): Set<String>?
/**
* Returns the set of top-level callable (functions and properties) names in a given scope inside the [packageFqName].
@@ -59,24 +63,34 @@ internal abstract class LLFirSymbolProviderNameCache {
abstract fun mayHaveTopLevelCallable(packageFqName: FqName, name: Name): Boolean
}
internal abstract class LLFirSymbolProviderNameCacheBase(
private val firSession: FirSession
) : LLFirSymbolProviderNameCache() {
abstract fun computeClassifierNames(packageFqName: FqName): Set<String>?
abstract fun computePackageNamesWithTopLevelCallables(): Set<String>?
abstract fun computeCallableNames(packageFqName: FqName): Set<Name>?
private val topLevelClassifierNamesByPackage =
firSession.firCachesFactory.createCache<FqName, Set<String>?>(::computeClassifierNames)
firSession.firCachesFactory.createCache(::computeClassifierNames)
private val topLevelCallablePackageNames by lazy(LazyThreadSafetyMode.PUBLICATION) {
computePackageNamesWithTopLevelCallables()
}
private val topLevelCallableNamesByPackage =
firSession.firCachesFactory.createCache<FqName, Set<Name>?>(::computeCallableNames)
firSession.firCachesFactory.createCache(::computeCallableNames)
final override fun getTopLevelClassifierNamesInPackage(packageFqName: FqName): Set<String>? =
topLevelClassifierNamesByPackage.getValue(packageFqName)
final override fun getTopLevelCallableNamesInPackage(packageFqName: FqName): Set<Name>? =
topLevelCallableNamesByPackage.getValue(packageFqName)
final override fun getTopLevelCallableNamesInPackage(packageFqName: FqName): Set<Name>? {
val packageNames = getPackageNamesWithTopLevelCallables()
if (packageNames != null && packageFqName.asString() !in packageNames) return null
return topLevelCallableNamesByPackage.getValue(packageFqName)
}
final override fun getPackageNamesWithTopLevelCallables(): Set<String>? = topLevelCallablePackageNames
final override fun mayHaveTopLevelClassifier(classId: ClassId, mayHaveFunctionClass: Boolean): Boolean {
val names = getTopLevelClassifierNamesInPackage(classId.packageFqName) ?: return true
@@ -90,10 +104,10 @@ internal abstract class LLFirSymbolProviderNameCacheBase(
}
}
internal object LLFirEmptySymbolProviderNameCache : LLFirSymbolProviderNameCache() {
override fun getTopLevelClassifierNamesInPackage(packageFqName: FqName): Set<String>? = emptySet()
override fun getTopLevelCallableNamesInPackage(packageFqName: FqName): Set<Name>? = emptySet()
override fun getTopLevelClassifierNamesInPackage(packageFqName: FqName): Set<String> = emptySet()
override fun getTopLevelCallableNamesInPackage(packageFqName: FqName): Set<Name> = emptySet()
override fun getPackageNamesWithTopLevelCallables(): Set<String> = emptySet()
override fun mayHaveTopLevelClassifier(classId: ClassId, mayHaveFunctionClass: Boolean): Boolean = false
override fun mayHaveTopLevelCallable(packageFqName: FqName, name: Name): Boolean = false
}
@@ -106,6 +120,10 @@ private constructor(
return caches.flatMapToNullableSet { it.getTopLevelClassifierNamesInPackage(packageFqName) }
}
override fun getPackageNamesWithTopLevelCallables(): Set<String>? {
return caches.flatMapToNullableSet { it.getPackageNamesWithTopLevelCallables() }
}
override fun getTopLevelCallableNamesInPackage(packageFqName: FqName): Set<Name>? {
return caches.flatMapToNullableSet { it.getTopLevelCallableNamesInPackage(packageFqName) }
}
@@ -127,4 +145,4 @@ private constructor(
}
}
}
}
}