K2: Introduce FirCachingCompositeSymbolProvider

The main idea is pre-computing the sets of names that might be
met there, that helps to decrease the sizes of the backing maps
(by avoiding irrelevant keys)

Totally, this branch with previous commits speeds up MT Full Kotlin
approximately on 3 seconds (~5%)
This commit is contained in:
Denis.Zharkov
2023-01-06 17:29:44 +01:00
committed by Space Team
parent 6705d211a6
commit 9c988fd8d8
18 changed files with 437 additions and 44 deletions
@@ -77,6 +77,12 @@ internal class LLFirModuleWithDependenciesSymbolProvider(
getPackageWithoutDependencies(fqName) getPackageWithoutDependencies(fqName)
?: dependencyProvider.getPackage(fqName) ?: dependencyProvider.getPackage(fqName)
override fun computePackageSetWithTopLevelCallables(): Set<String>? = null
override fun knownTopLevelClassifiersInPackage(packageFqName: FqName): Set<String>? = null
override fun computeCallableNamesInPackage(packageFqName: FqName): Set<Name>? = null
fun getPackageWithoutDependencies(fqName: FqName): FqName? = fun getPackageWithoutDependencies(fqName: FqName): FqName? =
providers.firstNotNullOfOrNull { it.getPackage(fqName) } providers.firstNotNullOfOrNull { it.getPackage(fqName) }
@@ -150,6 +156,10 @@ internal abstract class LLFirDependentModuleProviders(
} }
} }
// TODO: Consider having proper implementations for sake of optimizations
override fun computePackageSetWithTopLevelCallables(): Set<String>? = null
override fun knownTopLevelClassifiersInPackage(packageFqName: FqName): Set<String>? = null
override fun computeCallableNamesInPackage(packageFqName: FqName): Set<Name>? = null
private fun <S : FirCallableSymbol<*>> addNewSymbolsConsideringJvmFacades( private fun <S : FirCallableSymbol<*>> addNewSymbolsConsideringJvmFacades(
destination: MutableList<S>, destination: MutableList<S>,
@@ -113,6 +113,11 @@ internal class LLFirProvider(
override fun getPackage(fqName: FqName): FqName? = override fun getPackage(fqName: FqName): FqName? =
providerHelper.getPackage(fqName) providerHelper.getPackage(fqName)
// TODO: Consider having proper implementations for sake of optimizations
override fun computePackageSetWithTopLevelCallables(): Set<String>? = null
override fun knownTopLevelClassifiersInPackage(packageFqName: FqName): Set<String>? = null
override fun computeCallableNamesInPackage(packageFqName: FqName): Set<Name>? = null
override fun getClassLikeSymbolByClassId(classId: ClassId): FirClassLikeSymbol<*>? { override fun getClassLikeSymbolByClassId(classId: ClassId): FirClassLikeSymbol<*>? {
return getFirClassifierByFqName(classId)?.symbol return getFirClassifierByFqName(classId)?.symbol
} }
@@ -13,10 +13,13 @@ import org.jetbrains.kotlin.fir.extensions.FirExtensionRegistrar
import org.jetbrains.kotlin.fir.extensions.FirSwitchableExtensionDeclarationsSymbolProvider import org.jetbrains.kotlin.fir.extensions.FirSwitchableExtensionDeclarationsSymbolProvider
import org.jetbrains.kotlin.fir.java.FirCliSession import org.jetbrains.kotlin.fir.java.FirCliSession
import org.jetbrains.kotlin.fir.java.FirProjectSessionProvider import org.jetbrains.kotlin.fir.java.FirProjectSessionProvider
import org.jetbrains.kotlin.fir.resolve.providers.* import org.jetbrains.kotlin.fir.resolve.providers.DEPENDENCIES_SYMBOL_PROVIDER_QUALIFIED_KEY
import org.jetbrains.kotlin.fir.resolve.providers.impl.FirCompositeSymbolProvider import org.jetbrains.kotlin.fir.resolve.providers.FirProvider
import org.jetbrains.kotlin.fir.resolve.providers.FirSymbolProvider
import org.jetbrains.kotlin.fir.resolve.providers.impl.FirCachingCompositeSymbolProvider
import org.jetbrains.kotlin.fir.resolve.providers.impl.FirLibrarySessionProvider import org.jetbrains.kotlin.fir.resolve.providers.impl.FirLibrarySessionProvider
import org.jetbrains.kotlin.fir.resolve.providers.impl.FirProviderImpl import org.jetbrains.kotlin.fir.resolve.providers.impl.FirProviderImpl
import org.jetbrains.kotlin.fir.resolve.providers.symbolProvider
import org.jetbrains.kotlin.fir.scopes.FirKotlinScopeProvider import org.jetbrains.kotlin.fir.scopes.FirKotlinScopeProvider
import org.jetbrains.kotlin.incremental.components.EnumWhenTracker import org.jetbrains.kotlin.incremental.components.EnumWhenTracker
import org.jetbrains.kotlin.incremental.components.LookupTracker import org.jetbrains.kotlin.incremental.components.LookupTracker
@@ -55,7 +58,7 @@ abstract class FirAbstractSessionFactory {
val providers = createProviders(this, builtinsModuleData, kotlinScopeProvider) val providers = createProviders(this, builtinsModuleData, kotlinScopeProvider)
val symbolProvider = FirCompositeSymbolProvider(this, providers) val symbolProvider = FirCachingCompositeSymbolProvider(this, providers)
register(FirSymbolProvider::class, symbolProvider) register(FirSymbolProvider::class, symbolProvider)
register(FirProvider::class, FirLibrarySessionProvider(symbolProvider)) register(FirProvider::class, FirLibrarySessionProvider(symbolProvider))
} }
@@ -111,10 +114,16 @@ abstract class FirAbstractSessionFactory {
dependencyProviders, dependencyProviders,
) )
register(FirSymbolProvider::class, FirCompositeSymbolProvider(this, providers)) register(
FirSymbolProvider::class,
FirCachingCompositeSymbolProvider(
this, providers,
expectedCachesToBeCleanedOnce = generatedSymbolsProvider != null
)
)
generatedSymbolsProvider?.let { register(FirSwitchableExtensionDeclarationsSymbolProvider::class, it) } generatedSymbolsProvider?.let { register(FirSwitchableExtensionDeclarationsSymbolProvider::class, it) }
register(DEPENDENCIES_SYMBOL_PROVIDER_QUALIFIED_KEY, FirCompositeSymbolProvider(this, dependencyProviders)) register(DEPENDENCIES_SYMBOL_PROVIDER_QUALIFIED_KEY, FirCachingCompositeSymbolProvider(this, dependencyProviders))
} }
} }
@@ -139,7 +148,7 @@ abstract class FirAbstractSessionFactory {
fun FirSymbolProvider.collectProviders() { fun FirSymbolProvider.collectProviders() {
if (!visited.add(this)) return if (!visited.add(this)) return
when { when {
this is FirCompositeSymbolProvider -> { this is FirCachingCompositeSymbolProvider -> {
for (provider in providers) { for (provider in providers) {
provider.collectProviders() provider.collectProviders()
} }
@@ -6,6 +6,9 @@
package org.jetbrains.kotlin.fir.session package org.jetbrains.kotlin.fir.session
import org.jetbrains.kotlin.fir.FirSession import org.jetbrains.kotlin.fir.FirSession
import org.jetbrains.kotlin.fir.caches.FirCache
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.declarations.FirDeclarationOrigin
import org.jetbrains.kotlin.fir.deserialization.* import org.jetbrains.kotlin.fir.deserialization.*
import org.jetbrains.kotlin.fir.isNewPlaceForBodyGeneration import org.jetbrains.kotlin.fir.isNewPlaceForBodyGeneration
@@ -14,13 +17,16 @@ import org.jetbrains.kotlin.fir.scopes.FirKotlinScopeProvider
import org.jetbrains.kotlin.fir.symbols.SymbolInternals import org.jetbrains.kotlin.fir.symbols.SymbolInternals
import org.jetbrains.kotlin.library.metadata.KlibDeserializedContainerSource import org.jetbrains.kotlin.library.metadata.KlibDeserializedContainerSource
import org.jetbrains.kotlin.library.metadata.KlibMetadataClassDataFinder import org.jetbrains.kotlin.library.metadata.KlibMetadataClassDataFinder
import org.jetbrains.kotlin.library.metadata.KlibMetadataProtoBuf
import org.jetbrains.kotlin.library.metadata.KlibMetadataSerializerProtocol import org.jetbrains.kotlin.library.metadata.KlibMetadataSerializerProtocol
import org.jetbrains.kotlin.library.metadata.resolver.KotlinResolvedLibrary import org.jetbrains.kotlin.library.metadata.resolver.KotlinResolvedLibrary
import org.jetbrains.kotlin.metadata.ProtoBuf import org.jetbrains.kotlin.metadata.ProtoBuf
import org.jetbrains.kotlin.metadata.deserialization.NameResolver
import org.jetbrains.kotlin.metadata.deserialization.NameResolverImpl import org.jetbrains.kotlin.metadata.deserialization.NameResolverImpl
import org.jetbrains.kotlin.name.ClassId import org.jetbrains.kotlin.name.ClassId
import org.jetbrains.kotlin.name.FqName import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.resolve.CompilerDeserializationConfiguration import org.jetbrains.kotlin.resolve.CompilerDeserializationConfiguration
import org.jetbrains.kotlin.serialization.deserialization.getClassId
import org.jetbrains.kotlin.utils.SmartList import org.jetbrains.kotlin.utils.SmartList
import java.nio.file.Paths import java.nio.file.Paths
@@ -96,18 +102,63 @@ class KlibBasedSymbolProvider(
override fun computePackageSetWithNonClassDeclarations(): Set<String> = fragmentNamesInLibraries.keys override fun computePackageSetWithNonClassDeclarations(): Set<String> = fragmentNamesInLibraries.keys
// Looks like it's expensive to compute the presence of a class properly for KLib override fun knownTopLevelClassesInPackage(packageFqName: FqName): Set<String> =
override fun mayHaveTopLevelClass(classId: ClassId): Boolean = true buildSet {
forEachFragmentInPackage(packageFqName) { _, fragment, nameResolver ->
for (classNameId in fragment.getExtension(KlibMetadataProtoBuf.className).orEmpty()) {
add(nameResolver.getClassId(classNameId).shortClassName.asString())
}
}
}
@OptIn(SymbolInternals::class) @OptIn(SymbolInternals::class)
override fun extractClassMetadata(classId: ClassId, parentContext: FirDeserializationContext?): ClassMetadataFindResult? { override fun extractClassMetadata(classId: ClassId, parentContext: FirDeserializationContext?): ClassMetadataFindResult? {
val packageStringName = classId.packageFqName.asString() forEachFragmentInPackage(classId.packageFqName) { resolvedLibrary, fragment, nameResolver ->
val finder = KlibMetadataClassDataFinder(fragment, nameResolver)
val classProto = finder.findClassData(classId)?.classProto ?: return@forEachFragmentInPackage
val librariesWithFragment = fragmentNamesInLibraries[packageStringName] ?: return null val libraryPath = Paths.get(resolvedLibrary.library.libraryFile.path)
val moduleData = moduleDataProvider.getModuleData(libraryPath) ?: return null
return ClassMetadataFindResult.NoMetadata { symbol ->
val source = createDeserializedContainerSource(resolvedLibrary,
classId.packageFqName
)
deserializeClassToSymbol(
classId,
classProto,
symbol,
nameResolver,
session,
moduleData,
annotationDeserializer,
kotlinScopeProvider,
KlibMetadataSerializerProtocol,
parentContext,
source,
origin = defaultDeserializationOrigin,
deserializeNestedClass = this::getClass,
)
symbol.fir.isNewPlaceForBodyGeneration = isNewPlaceForBodyGeneration(classProto)
}
}
return null
}
private inline fun forEachFragmentInPackage(
packageFqName: FqName,
f: (KotlinResolvedLibrary, ProtoBuf.PackageFragment, NameResolver) -> Unit
) {
val packageStringName = packageFqName.asString()
val librariesWithFragment = fragmentNamesInLibraries[packageStringName] ?: return
for (resolvedLibrary in librariesWithFragment) { for (resolvedLibrary in librariesWithFragment) {
for (packageMetadataPart in resolvedLibrary.library.packageMetadataParts(packageStringName)) { for (packageMetadataPart in resolvedLibrary.library.packageMetadataParts(packageStringName)) {
val libraryPath = Paths.get(resolvedLibrary.library.libraryFile.path)
val fragment = getPackageFragment(resolvedLibrary, packageStringName, packageMetadataPart) val fragment = getPackageFragment(resolvedLibrary, packageStringName, packageMetadataPart)
val nameResolver = NameResolverImpl( val nameResolver = NameResolverImpl(
@@ -115,35 +166,9 @@ class KlibBasedSymbolProvider(
fragment.qualifiedNames, fragment.qualifiedNames,
) )
val finder = KlibMetadataClassDataFinder(fragment, nameResolver) f(resolvedLibrary, fragment, nameResolver)
val classProto = finder.findClassData(classId)?.classProto ?: continue
val moduleData = moduleDataProvider.getModuleData(libraryPath) ?: return null
return ClassMetadataFindResult.NoMetadata { symbol ->
val source = createDeserializedContainerSource(resolvedLibrary, classId.packageFqName)
deserializeClassToSymbol(
classId,
classProto,
symbol,
nameResolver,
session,
moduleData,
annotationDeserializer,
kotlinScopeProvider,
KlibMetadataSerializerProtocol,
parentContext,
source,
origin = defaultDeserializationOrigin,
deserializeNestedClass = this::getClass,
)
symbol.fir.isNewPlaceForBodyGeneration = isNewPlaceForBodyGeneration(classProto)
}
} }
} }
return null
} }
private fun createDeserializedContainerSource( private fun createDeserializedContainerSource(
@@ -124,7 +124,25 @@ abstract class AbstractFirDeserializedSymbolProvider(
// This method should only be used for sake of optimization to avoid having too many empty-list/null values in our caches // This method should only be used for sake of optimization to avoid having too many empty-list/null values in our caches
protected abstract fun computePackageSetWithNonClassDeclarations(): Set<String> protected abstract fun computePackageSetWithNonClassDeclarations(): Set<String>
protected abstract fun mayHaveTopLevelClass(classId: ClassId): Boolean override fun computePackageSetWithTopLevelCallables(): Set<String> = computePackageSetWithNonClassDeclarations()
override fun computeCallableNamesInPackage(packageFqName: FqName): Set<Name> = allNamesByPackage.getValue(packageFqName)
override fun knownTopLevelClassifiersInPackage(packageFqName: FqName): Set<String>? {
val classesInPackage = knownTopLevelClassesInPackage(packageFqName) ?: return null
if (packageFqName.asString() !in packageNamesForNonClassDeclarations) return classesInPackage
val typeAliasNames = typeAliasesNamesByPackage.getValue(packageFqName)
if (typeAliasNames.isEmpty()) return classesInPackage
return buildSet {
addAll(classesInPackage)
typeAliasNames.mapTo(this) { it.asString() }
}
}
protected abstract fun knownTopLevelClassesInPackage(packageFqName: FqName): Set<String>?
protected abstract fun extractClassMetadata( protected abstract fun extractClassMetadata(
classId: ClassId, classId: ClassId,
@@ -240,6 +258,11 @@ abstract class AbstractFirDeserializedSymbolProvider(
return classCache.getValue(classId, parentContext) return classCache.getValue(classId, parentContext)
} }
private fun mayHaveTopLevelClass(topLevelClassId: ClassId): Boolean {
val knownClassNames = knownTopLevelClassifiersInPackage(topLevelClassId.packageFqName) ?: return true
return topLevelClassId.shortClassName.asString() in knownClassNames
}
private fun getTypeAlias(classId: ClassId): FirTypeAliasSymbol? { private fun getTypeAlias(classId: ClassId): FirTypeAliasSymbol? {
if (!classId.relativeClassName.isOneSegmentFQN()) return null if (!classId.relativeClassName.isOneSegmentFQN()) return null
@@ -77,6 +77,18 @@ open class FirBuiltinSymbolProvider(
} ?: syntheticFunctionalInterfaceCache.tryGetSyntheticFunctionalInterface(classId) } ?: syntheticFunctionalInterfaceCache.tryGetSyntheticFunctionalInterface(classId)
} }
override fun computePackageSetWithTopLevelCallables(): Set<String> =
allPackageFragments.keys.mapTo(mutableSetOf()) { it.asString() }
override fun knownTopLevelClassifiersInPackage(packageFqName: FqName): Set<String> =
allPackageFragments[packageFqName]?.flatMapTo(mutableSetOf()) { fragment ->
fragment.classDataFinder.allClassIds.map { it.shortClassName.asString() }
}.orEmpty()
override fun computeCallableNamesInPackage(packageFqName: FqName): Set<Name> =
allPackageFragments[packageFqName]?.flatMapTo(mutableSetOf()) {
it.getTopLevelCallableNames()
}.orEmpty()
@FirSymbolProviderInternals @FirSymbolProviderInternals
override fun getTopLevelCallableSymbolsTo(destination: MutableList<FirCallableSymbol<*>>, packageFqName: FqName, name: Name) { override fun getTopLevelCallableSymbolsTo(destination: MutableList<FirCallableSymbol<*>>, packageFqName: FqName, name: Name) {
@@ -154,6 +166,9 @@ open class FirBuiltinSymbolProvider(
return getTopLevelFunctionSymbols(name) return getTopLevelFunctionSymbols(name)
} }
fun getTopLevelCallableNames(): Collection<Name> =
packageProto.`package`.functionList.map { nameResolver.getName(it.name) }
fun getTopLevelFunctionSymbols(name: Name): List<FirNamedFunctionSymbol> { fun getTopLevelFunctionSymbols(name: Name): List<FirNamedFunctionSymbol> {
return packageProto.`package`.functionList.filter { nameResolver.getName(it.name) == name }.map { return packageProto.`package`.functionList.filter { nameResolver.getName(it.name) == name }.map {
memberDeserializer.loadFunction(it).symbol memberDeserializer.loadFunction(it).symbol
@@ -85,6 +85,8 @@ abstract class FirJavaFacade(
return classId.relativeClassName.topLevelName() in knownNames return classId.relativeClassName.topLevelName() in knownNames
} }
fun knownClassNamesInPackage(packageFqName: FqName): Set<String>? = knownClassNamesInPackage.getValue(packageFqName)
abstract fun getModuleDataForClass(javaClass: JavaClass): FirModuleData abstract fun getModuleDataForClass(javaClass: JavaClass): FirModuleData
private fun JavaTypeParameter.toFirTypeParameter( private fun JavaTypeParameter.toFirTypeParameter(
@@ -45,6 +45,12 @@ class JavaSymbolProvider(
override fun getClassLikeSymbolByClassId(classId: ClassId): FirRegularClassSymbol? = override fun getClassLikeSymbolByClassId(classId: ClassId): FirRegularClassSymbol? =
if (javaFacade.hasTopLevelClassOf(classId)) getFirJavaClass(classId) else null if (javaFacade.hasTopLevelClassOf(classId)) getFirJavaClass(classId) else null
override fun computePackageSetWithTopLevelCallables(): Set<String> = emptySet()
override fun knownTopLevelClassifiersInPackage(packageFqName: FqName): Set<String>? = javaFacade.knownClassNamesInPackage(packageFqName)
override fun computeCallableNamesInPackage(packageFqName: FqName): Set<Name> = emptySet()
private fun getFirJavaClass(classId: ClassId): FirRegularClassSymbol? = private fun getFirJavaClass(classId: ClassId): FirRegularClassSymbol? =
classCache.getValue(classId, classId.outerClassId?.let { getFirJavaClass(it) }) classCache.getValue(classId, classId.outerClassId?.let { getFirJavaClass(it) })
@@ -99,7 +99,7 @@ class JvmClassFileBasedSymbolProvider(
override fun computePackageSetWithNonClassDeclarations(): Set<String> = packagePartProvider.computePackageSetWithNonClassDeclarations() override fun computePackageSetWithNonClassDeclarations(): Set<String> = packagePartProvider.computePackageSetWithNonClassDeclarations()
override fun mayHaveTopLevelClass(classId: ClassId): Boolean = javaFacade.hasTopLevelClassOf(classId) override fun knownTopLevelClassesInPackage(packageFqName: FqName): Set<String>? = javaFacade.knownClassNamesInPackage(packageFqName)
private val KotlinJvmBinaryClass.incompatibility: IncompatibleVersionErrorData<JvmMetadataVersion>? private val KotlinJvmBinaryClass.incompatibility: IncompatibleVersionErrorData<JvmMetadataVersion>?
get() { get() {
@@ -45,13 +45,22 @@ class OptionalAnnotationClassesProvider(
return@lazy Pair(optionalAnnotationClasses, optionalAnnotationPackages) return@lazy Pair(optionalAnnotationClasses, optionalAnnotationPackages)
} }
private val optionalAnnotationClassNamesByPackage: Map<FqName, Set<String>> by lazy(LazyThreadSafetyMode.PUBLICATION) {
buildMap<FqName, MutableSet<String>> {
for (classId in optionalAnnotationClassesAndPackages.first.keys) {
getOrPut(classId.packageFqName, ::mutableSetOf).add(classId.shortClassName.asString())
}
}
}
override fun computePackagePartsInfos(packageFqName: FqName): List<PackagePartsCacheData> { override fun computePackagePartsInfos(packageFqName: FqName): List<PackagePartsCacheData> {
return emptyList() return emptyList()
} }
override fun computePackageSetWithNonClassDeclarations(): Set<String> = optionalAnnotationClassesAndPackages.second override fun computePackageSetWithNonClassDeclarations(): Set<String> = optionalAnnotationClassesAndPackages.second
override fun mayHaveTopLevelClass(classId: ClassId): Boolean = classId in optionalAnnotationClassesAndPackages.first override fun knownTopLevelClassesInPackage(packageFqName: FqName): Set<String> =
optionalAnnotationClassNamesByPackage[packageFqName] ?: emptySet()
override fun extractClassMetadata( override fun extractClassMetadata(
classId: ClassId, classId: ClassId,
@@ -53,6 +53,35 @@ class FirExtensionDeclarationsSymbolProvider private constructor(
hasPackage(packageFqName) hasPackage(packageFqName)
} }
private val callableNamesInPackageCache: FirLazyValue<Map<FqName, Set<Name>>, Nothing?> =
cachesFactory.createLazyValue {
computeNamesGroupedByPackage(
FirDeclarationGenerationExtension::getTopLevelCallableIds,
CallableId::packageName, CallableId::callableName
)
}
private val classNamesInPackageCache: FirLazyValue<Map<FqName, Set<String>>, Nothing?> =
cachesFactory.createLazyValue {
computeNamesGroupedByPackage(
FirDeclarationGenerationExtension::getTopLevelClassIds,
ClassId::getPackageFqName
) { it.shortClassName.asString() }
}
private fun <I, N> computeNamesGroupedByPackage(
ids: FirDeclarationGenerationExtension.() -> Collection<I>,
packageFqName: (I) -> FqName,
shortName: (I) -> N,
): Map<FqName, Set<N>> =
buildMap<FqName, MutableSet<N>> {
for (extension in extensions) {
for (id in extension.ids()) {
getOrPut(packageFqName(id)) { mutableSetOf() }.add(shortName(id))
}
}
}
private val extensionsByTopLevelClassId: FirLazyValue<Map<ClassId, List<FirDeclarationGenerationExtension>>, Nothing?> = private val extensionsByTopLevelClassId: FirLazyValue<Map<ClassId, List<FirDeclarationGenerationExtension>>, Nothing?> =
session.firCachesFactory.createLazyValue { session.firCachesFactory.createLazyValue {
extensions.flatGroupBy { it.topLevelClassIdsCache.getValue() } extensions.flatGroupBy { it.topLevelClassIdsCache.getValue() }
@@ -139,4 +168,15 @@ class FirExtensionDeclarationsSymbolProvider private constructor(
override fun getPackage(fqName: FqName): FqName? { override fun getPackage(fqName: FqName): FqName? {
return fqName.takeIf { packageCache.getValue(fqName, null) } return fqName.takeIf { packageCache.getValue(fqName, null) }
} }
override fun computePackageSetWithTopLevelCallables(): Set<String> =
extensions.flatMapTo(mutableSetOf()) { extension ->
extension.topLevelCallableIdsCache.getValue(null).map { it.packageName.asString() }
}
override fun knownTopLevelClassifiersInPackage(packageFqName: FqName): Set<String> =
classNamesInPackageCache.getValue()[packageFqName] ?: emptySet()
override fun computeCallableNamesInPackage(packageFqName: FqName): Set<Name> =
callableNamesInPackageCache.getValue()[packageFqName].orEmpty()
} }
@@ -68,6 +68,16 @@ class FirSwitchableExtensionDeclarationsSymbolProvider private constructor(
fun enable() { fun enable() {
disabled = false disabled = false
} }
override fun computePackageSetWithTopLevelCallables(): Set<String>? =
if (disabled) null else delegate.computePackageSetWithTopLevelCallables()
override fun knownTopLevelClassifiersInPackage(packageFqName: FqName): Set<String>? =
if (disabled) null else delegate.knownTopLevelClassifiersInPackage(packageFqName)
override fun computeCallableNamesInPackage(packageFqName: FqName): Set<Name>? =
if (disabled) null else delegate.computeCallableNamesInPackage(packageFqName)
} }
val FirSession.generatedDeclarationsSymbolProvider: FirSwitchableExtensionDeclarationsSymbolProvider? by FirSession.nullableSessionComponentAccessor() val FirSession.generatedDeclarationsSymbolProvider: FirSwitchableExtensionDeclarationsSymbolProvider? by FirSession.nullableSessionComponentAccessor()
@@ -55,8 +55,37 @@ abstract class FirSymbolProvider(val session: FirSession) : FirSessionComponent
abstract fun getTopLevelPropertySymbolsTo(destination: MutableList<FirPropertySymbol>, packageFqName: FqName, name: Name) abstract fun getTopLevelPropertySymbolsTo(destination: MutableList<FirPropertySymbol>, packageFqName: FqName, name: Name)
abstract fun getPackage(fqName: FqName): FqName? // TODO: Replace to symbol sometime abstract fun getPackage(fqName: FqName): FqName? // TODO: Replace to symbol sometime
/**
* All the three "compute*" functions below have the following common contract:
* - They return null in case necessary name set is too hard/impossible to compute.
* - They might return a strict superset of the name set, i.e. the resulting set might contain some names that do not belong to the provider.
* - It might be non-cheap to compute them on each query, thus their result should be cached properly.
*
* @returns full package names that contain some top-level callables
*/
abstract fun computePackageSetWithTopLevelCallables(): Set<String>?
/**
* @returns top-level classifier names that belong to `packageFqName` or null if it's complicated to compute the set
*
* All usages must take into account that the result might not include kotlin.FunctionN
* (and others for which org.jetbrains.kotlin.builtins.functions.FunctionClassKind.Companion.byClassNamePrefix not-null)
*/
abstract fun knownTopLevelClassifiersInPackage(packageFqName: FqName): Set<String>?
/**
* @returns top-level callable names that belong to `packageFqName` or null if it's complicated to compute the set
*/
abstract fun computeCallableNamesInPackage(packageFqName: FqName): Set<Name>?
} }
/**
* Works almost as regular flatMap, but returns a set and returns null if any lambda call returned null
*/
inline fun <T, R> Iterable<T>.flatMapToNullableSet(transform: (T) -> Iterable<R>?): Set<R>? =
flatMapTo(mutableSetOf()) { transform(it) ?: return null }
private fun FirSymbolProvider.getClassDeclaredMemberScope(classId: ClassId): FirScope? { private fun FirSymbolProvider.getClassDeclaredMemberScope(classId: ClassId): FirScope? {
val classSymbol = getClassLikeSymbolByClassId(classId) as? FirRegularClassSymbol ?: return null val classSymbol = getClassLikeSymbolByClassId(classId) as? FirRegularClassSymbol ?: return null
return session.declaredMemberScope(classSymbol.fir) return session.declaredMemberScope(classSymbol.fir)
@@ -0,0 +1,151 @@
/*
* Copyright 2010-2018 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.fir.resolve.providers.impl
import org.jetbrains.kotlin.builtins.functions.FunctionClassKind
import org.jetbrains.kotlin.fir.FirSession
import org.jetbrains.kotlin.fir.NoMutableState
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.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
import org.jetbrains.kotlin.fir.symbols.impl.FirPropertySymbol
import org.jetbrains.kotlin.name.CallableId
import org.jetbrains.kotlin.name.ClassId
import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.name.Name
@NoMutableState
class FirCachingCompositeSymbolProvider(
session: FirSession,
val providers: List<FirSymbolProvider>,
// This property is necessary just to make sure we don't use the hack at `createCopyWithCleanCaches` more than once or in cases
// we are not assumed to use it.
private val expectedCachesToBeCleanedOnce: Boolean = false,
) : FirSymbolProvider(session) {
private val classLikeCache = session.firCachesFactory.createCache(::computeClass)
private val topLevelCallableCache = session.firCachesFactory.createCache(::computeTopLevelCallables)
private val topLevelFunctionCache = session.firCachesFactory.createCache(::computeTopLevelFunctions)
private val topLevelPropertyCache = session.firCachesFactory.createCache(::computeTopLevelProperties)
private val packageCache = session.firCachesFactory.createCache(::computePackage)
private val callablePackageSet: Set<String>? by lazy(LazyThreadSafetyMode.PUBLICATION) {
computePackageSetWithTopLevelCallables().also {
ensureNotNull(it) { "package names with callables" }
}
}
private val knownTopLevelClassifierNamesInPackage: FirCache<FqName, Set<String>?, Nothing?> =
session.firCachesFactory.createCache { packageFqName ->
knownTopLevelClassifiersInPackage(packageFqName).also {
ensureNotNull(it) { "classifier names in package $packageFqName" }
}
}
private val callableNamesInPackage: FirCache<FqName, Set<Name>?, Nothing?> =
session.firCachesFactory.createCache { packageFqName ->
computeCallableNamesInPackage(packageFqName).also {
ensureNotNull(it) { "callable names in package $packageFqName" }
}
}
private inline fun ensureNotNull(v: Any?, representation: () -> String) {
require(v != null || expectedCachesToBeCleanedOnce) {
"${representation()} is expected to be not null in CLI"
}
}
// Unfortunately, this is a part of a hack for overcoming the problem of plugin's generated entities
// (for more details see its usage at org.jetbrains.kotlin.fir.resolve.transformers.plugin.FirCompilerRequiredAnnotationsResolveProcessor.afterPhase)
fun createCopyWithCleanCaches(): FirCachingCompositeSymbolProvider {
require(expectedCachesToBeCleanedOnce) { "Unexpected caches clearing" }
return FirCachingCompositeSymbolProvider(session, providers, expectedCachesToBeCleanedOnce = false)
}
override fun getTopLevelCallableSymbols(packageFqName: FqName, name: Name): List<FirCallableSymbol<*>> {
if (!mayHaveTopLevelCallablesInPackage(packageFqName, name)) return emptyList()
return topLevelCallableCache.getValue(CallableId(packageFqName, name))
}
private fun mayHaveTopLevelCallablesInPackage(packageFqName: FqName, name: Name): Boolean {
if (callablePackageSet != null && packageFqName.asString() !in callablePackageSet!!) return false
val callableNamesInPackage = callableNamesInPackage.getValue(packageFqName) ?: return true
return name in callableNamesInPackage
}
@FirSymbolProviderInternals
override fun getTopLevelCallableSymbolsTo(destination: MutableList<FirCallableSymbol<*>>, packageFqName: FqName, name: Name) {
destination += getTopLevelCallableSymbols(packageFqName, name)
}
@FirSymbolProviderInternals
override fun getTopLevelFunctionSymbolsTo(destination: MutableList<FirNamedFunctionSymbol>, packageFqName: FqName, name: Name) {
if (!mayHaveTopLevelCallablesInPackage(packageFqName, name)) return
destination += topLevelFunctionCache.getValue(CallableId(packageFqName, name))
}
@FirSymbolProviderInternals
override fun getTopLevelPropertySymbolsTo(destination: MutableList<FirPropertySymbol>, packageFqName: FqName, name: Name) {
if (!mayHaveTopLevelCallablesInPackage(packageFqName, name)) return
destination += topLevelPropertyCache.getValue(CallableId(packageFqName, name))
}
override fun getPackage(fqName: FqName): FqName? {
return packageCache.getValue(fqName)
}
override fun getClassLikeSymbolByClassId(classId: ClassId): FirClassLikeSymbol<*>? {
val knownClassifierNames = knownTopLevelClassifierNamesInPackage.getValue(classId.packageFqName)
if (knownClassifierNames != null && !isNameForFunctionClass(classId)) {
val outerClassId = classId.outerClassId
if (outerClassId == null && classId.shortClassName.asString() !in knownClassifierNames) return null
if (outerClassId != null && classId.outermostClassId.shortClassName.asString() !in knownClassifierNames) return null
}
return classLikeCache.getValue(classId)
}
private fun isNameForFunctionClass(classId: ClassId): Boolean {
return FunctionClassKind.byClassNamePrefix(classId.packageFqName, classId.shortClassName.asString()) != null
}
@OptIn(FirSymbolProviderInternals::class)
private fun computeTopLevelCallables(callableId: CallableId): List<FirCallableSymbol<*>> = buildList {
providers.forEach { it.getTopLevelCallableSymbolsTo(this, callableId.packageName, callableId.callableName) }
}
@OptIn(FirSymbolProviderInternals::class)
private fun computeTopLevelFunctions(callableId: CallableId): List<FirNamedFunctionSymbol> = buildList {
providers.forEach { it.getTopLevelFunctionSymbolsTo(this, callableId.packageName, callableId.callableName) }
}
@OptIn(FirSymbolProviderInternals::class)
private fun computeTopLevelProperties(callableId: CallableId): List<FirPropertySymbol> = buildList {
providers.forEach { it.getTopLevelPropertySymbolsTo(this, callableId.packageName, callableId.callableName) }
}
private fun computePackage(it: FqName): FqName? =
providers.firstNotNullOfOrNull { provider -> provider.getPackage(it) }
private fun computeClass(classId: ClassId): FirClassLikeSymbol<*>? =
providers.firstNotNullOfOrNull { provider -> provider.getClassLikeSymbolByClassId(classId) }
override fun computePackageSetWithTopLevelCallables(): Set<String>? =
providers.flatMapToNullableSet { it.computePackageSetWithTopLevelCallables() }
override fun knownTopLevelClassifiersInPackage(packageFqName: FqName): Set<String>? =
providers.flatMapToNullableSet { it.knownTopLevelClassifiersInPackage(packageFqName) }
override fun computeCallableNamesInPackage(packageFqName: FqName): Set<Name>? =
providers.flatMapToNullableSet { it.computeCallableNamesInPackage(packageFqName) }
}
@@ -1,5 +1,5 @@
/* /*
* Copyright 2010-2018 JetBrains s.r.o. and Kotlin Programming Language contributors. * 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. * Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/ */
@@ -9,6 +9,7 @@ import org.jetbrains.kotlin.fir.FirSession
import org.jetbrains.kotlin.fir.NoMutableState import org.jetbrains.kotlin.fir.NoMutableState
import org.jetbrains.kotlin.fir.resolve.providers.FirSymbolProvider import org.jetbrains.kotlin.fir.resolve.providers.FirSymbolProvider
import org.jetbrains.kotlin.fir.resolve.providers.FirSymbolProviderInternals 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.FirCallableSymbol
import org.jetbrains.kotlin.fir.symbols.impl.FirClassLikeSymbol import org.jetbrains.kotlin.fir.symbols.impl.FirClassLikeSymbol
import org.jetbrains.kotlin.fir.symbols.impl.FirNamedFunctionSymbol import org.jetbrains.kotlin.fir.symbols.impl.FirNamedFunctionSymbol
@@ -49,4 +50,13 @@ class FirCompositeSymbolProvider(session: FirSession, val providers: List<FirSym
override fun getClassLikeSymbolByClassId(classId: ClassId): FirClassLikeSymbol<*>? { override fun getClassLikeSymbolByClassId(classId: ClassId): FirClassLikeSymbol<*>? {
return providers.firstNotNullOfOrNull { it.getClassLikeSymbolByClassId(classId) } return providers.firstNotNullOfOrNull { it.getClassLikeSymbolByClassId(classId) }
} }
override fun computePackageSetWithTopLevelCallables(): Set<String>? =
providers.flatMapToNullableSet { it.computePackageSetWithTopLevelCallables() }
override fun knownTopLevelClassifiersInPackage(packageFqName: FqName): Set<String>? =
providers.flatMapToNullableSet { it.knownTopLevelClassifiersInPackage(packageFqName) }
override fun computeCallableNamesInPackage(packageFqName: FqName): Set<Name>? =
providers.flatMapToNullableSet { it.computeCallableNamesInPackage(packageFqName) }
} }
@@ -77,4 +77,13 @@ class FirCloneableSymbolProvider(
override fun getPackage(fqName: FqName): FqName? { override fun getPackage(fqName: FqName): FqName? {
return null return null
} }
override fun computePackageSetWithTopLevelCallables(): Set<String> = emptySet()
override fun knownTopLevelClassifiersInPackage(packageFqName: FqName): Set<String> =
if (packageFqName == StandardClassIds.Cloneable.packageFqName)
setOf(StandardClassIds.Cloneable.shortClassName.asString())
else
emptySet()
override fun computeCallableNamesInPackage(packageFqName: FqName): Set<Name> = emptySet()
} }
@@ -75,6 +75,26 @@ class FirProviderImpl(val session: FirSession, val kotlinScopeProvider: FirKotli
if (fqName in state.allSubPackages) return fqName if (fqName in state.allSubPackages) return fqName
return null return null
} }
override fun computePackageSetWithTopLevelCallables(): Set<String> =
state.allSubPackages.mapTo(mutableSetOf()) { it.asString() }
override fun knownTopLevelClassifiersInPackage(packageFqName: FqName): Set<String> =
state.classifierInPackage[packageFqName].orEmpty().mapTo(mutableSetOf()) { it.asString() }
override fun computeCallableNamesInPackage(packageFqName: FqName): Set<Name> = buildSet {
for (key in state.functionMap.keys) {
if (key.packageName == packageFqName) {
add(key.callableName)
}
}
for (key in state.propertyMap.keys) {
if (key.packageName == packageFqName) {
add(key.callableName)
}
}
}
} }
private val FirDeclaration.file: FirFile private val FirDeclaration.file: FirFile
@@ -109,6 +129,7 @@ class FirProviderImpl(val session: FirSession, val kotlinScopeProvider: FirKotli
if (!classId.isNestedClass && !classId.isLocal) { if (!classId.isNestedClass && !classId.isLocal) {
data.state.classesInPackage.getOrPut(classId.packageFqName, ::mutableSetOf).add(classId.shortClassName) data.state.classesInPackage.getOrPut(classId.packageFqName, ::mutableSetOf).add(classId.shortClassName)
data.state.classifierInPackage.getOrPut(classId.packageFqName, ::mutableSetOf).add(classId.shortClassName)
} }
regularClass.acceptChildren(this, data) regularClass.acceptChildren(this, data)
@@ -120,6 +141,8 @@ class FirProviderImpl(val session: FirSession, val kotlinScopeProvider: FirKotli
data.state.classifierMap.put(classId, typeAlias)?.let { data.state.classifierMap.put(classId, typeAlias)?.let {
data.nameConflictsTracker?.registerClassifierRedeclaration(classId, typeAlias.symbol, data.file, it.symbol, prevFile) data.nameConflictsTracker?.registerClassifierRedeclaration(classId, typeAlias.symbol, data.file, it.symbol, prevFile)
} }
data.state.classifierInPackage.getOrPut(classId.packageFqName, ::mutableSetOf).add(classId.shortClassName)
} }
override fun visitPropertyAccessor( override fun visitPropertyAccessor(
@@ -166,10 +189,11 @@ class FirProviderImpl(val session: FirSession, val kotlinScopeProvider: FirKotli
private val state = State() private val state = State()
private class State { private class State {
val fileMap = mutableMapOf<FqName, List<FirFile>>() val fileMap: MutableMap<FqName, List<FirFile>> = mutableMapOf<FqName, List<FirFile>>()
val allSubPackages = mutableSetOf<FqName>() val allSubPackages = mutableSetOf<FqName>()
val classifierMap = mutableMapOf<ClassId, FirClassLikeDeclaration>() val classifierMap = mutableMapOf<ClassId, FirClassLikeDeclaration>()
val classifierContainerFileMap = mutableMapOf<ClassId, FirFile>() val classifierContainerFileMap = mutableMapOf<ClassId, FirFile>()
val classifierInPackage = mutableMapOf<FqName, MutableSet<Name>>()
val classesInPackage = mutableMapOf<FqName, MutableSet<Name>>() val classesInPackage = mutableMapOf<FqName, MutableSet<Name>>()
val functionMap = mutableMapOf<CallableId, List<FirNamedFunctionSymbol>>() val functionMap = mutableMapOf<CallableId, List<FirNamedFunctionSymbol>>()
val propertyMap = mutableMapOf<CallableId, List<FirPropertySymbol>>() val propertyMap = mutableMapOf<CallableId, List<FirPropertySymbol>>()
@@ -195,6 +219,7 @@ class FirProviderImpl(val session: FirSession, val kotlinScopeProvider: FirKotli
constructorMap.putAll(other.constructorMap) constructorMap.putAll(other.constructorMap)
callableContainerMap.putAll(other.callableContainerMap) callableContainerMap.putAll(other.callableContainerMap)
classesInPackage.putAll(other.classesInPackage) classesInPackage.putAll(other.classesInPackage)
classifierInPackage.putAll(other.classifierInPackage)
} }
} }
@@ -7,12 +7,16 @@ package org.jetbrains.kotlin.fir.resolve.transformers.plugin
import org.jetbrains.kotlin.fir.FirElement import org.jetbrains.kotlin.fir.FirElement
import org.jetbrains.kotlin.fir.FirSession import org.jetbrains.kotlin.fir.FirSession
import org.jetbrains.kotlin.fir.SessionConfiguration
import org.jetbrains.kotlin.fir.declarations.* import org.jetbrains.kotlin.fir.declarations.*
import org.jetbrains.kotlin.fir.declarations.FirResolvePhase.COMPILER_REQUIRED_ANNOTATIONS import org.jetbrains.kotlin.fir.declarations.FirResolvePhase.COMPILER_REQUIRED_ANNOTATIONS
import org.jetbrains.kotlin.fir.expressions.FirStatement import org.jetbrains.kotlin.fir.expressions.FirStatement
import org.jetbrains.kotlin.fir.extensions.* import org.jetbrains.kotlin.fir.extensions.*
import org.jetbrains.kotlin.fir.resolve.ScopeSession import org.jetbrains.kotlin.fir.resolve.ScopeSession
import org.jetbrains.kotlin.fir.resolve.providers.FirSymbolProvider
import org.jetbrains.kotlin.fir.resolve.providers.FirSymbolProviderInternals import org.jetbrains.kotlin.fir.resolve.providers.FirSymbolProviderInternals
import org.jetbrains.kotlin.fir.resolve.providers.impl.FirCachingCompositeSymbolProvider
import org.jetbrains.kotlin.fir.resolve.providers.symbolProvider
import org.jetbrains.kotlin.fir.resolve.transformers.* import org.jetbrains.kotlin.fir.resolve.transformers.*
import org.jetbrains.kotlin.fir.resolve.transformers.body.resolve.LocalClassesNavigationInfo import org.jetbrains.kotlin.fir.resolve.transformers.body.resolve.LocalClassesNavigationInfo
import org.jetbrains.kotlin.fir.types.* import org.jetbrains.kotlin.fir.types.*
@@ -44,7 +48,18 @@ class FirCompilerRequiredAnnotationsResolveProcessor(
@OptIn(FirSymbolProviderInternals::class) @OptIn(FirSymbolProviderInternals::class)
override fun afterPhase() { override fun afterPhase() {
super.afterPhase() super.afterPhase()
session.generatedDeclarationsSymbolProvider?.enable()
val generatedDeclarationsSymbolProvider = session.generatedDeclarationsSymbolProvider
generatedDeclarationsSymbolProvider?.enable()
// This part is a bit hacky way to clear the caches in FirCachingCompositeSymbolProvider when there are plugins that may generate new entities.
// It's necessary because otherwise, when symbol provider is being queried on the stage of compiler-required annotations resolution
// we record incorrect (incomplete) results to its cache, so after the phase is completed we just start from the scratch
val symbolProvider = session.symbolProvider
if (generatedDeclarationsSymbolProvider != null && symbolProvider is FirCachingCompositeSymbolProvider) {
@OptIn(SessionConfiguration::class)
session.register(FirSymbolProvider::class, symbolProvider.createCopyWithCleanCaches())
}
} }
} }