[FIR] Store generated declarations in the session component

Also extract generated declarations for classes from declaration site
  session, not use site

^KT-57821 Fixed
This commit is contained in:
Dmitriy Novozhilov
2023-04-20 15:39:26 +03:00
committed by Space Team
parent c6f5cf3e62
commit 2087a7c899
5 changed files with 217 additions and 155 deletions
@@ -71,6 +71,7 @@ fun FirSession.registerCommonComponents(languageVersionSettings: LanguageVersion
register(FirSubstitutionOverrideStorage::class, FirSubstitutionOverrideStorage(this)) register(FirSubstitutionOverrideStorage::class, FirSubstitutionOverrideStorage(this))
register(FirIntersectionOverrideStorage::class, FirIntersectionOverrideStorage(this)) register(FirIntersectionOverrideStorage::class, FirIntersectionOverrideStorage(this))
register(FirGeneratedMemberDeclarationsStorage::class, FirGeneratedMemberDeclarationsStorage(this))
register(FirSamConstructorStorage::class, FirSamConstructorStorage(this)) register(FirSamConstructorStorage::class, FirSamConstructorStorage(this))
register(FirOverrideService::class, FirOverrideService(this)) register(FirOverrideService::class, FirOverrideService(this))
register(FirDynamicMembersStorage::class, FirDynamicMembersStorage(this)) register(FirDynamicMembersStorage::class, FirDynamicMembersStorage(this))
@@ -12,7 +12,6 @@ import org.jetbrains.kotlin.fir.caches.FirCache
import org.jetbrains.kotlin.fir.caches.firCachesFactory import org.jetbrains.kotlin.fir.caches.firCachesFactory
import org.jetbrains.kotlin.fir.caches.getValue import org.jetbrains.kotlin.fir.caches.getValue
import org.jetbrains.kotlin.fir.declarations.FirClass import org.jetbrains.kotlin.fir.declarations.FirClass
import org.jetbrains.kotlin.fir.extensions.MemberGenerationContext
import org.jetbrains.kotlin.fir.resolve.providers.FirSymbolProvider import org.jetbrains.kotlin.fir.resolve.providers.FirSymbolProvider
import org.jetbrains.kotlin.fir.scopes.FirContainingNamesAwareScope import org.jetbrains.kotlin.fir.scopes.FirContainingNamesAwareScope
import org.jetbrains.kotlin.fir.scopes.FirNameAwareCompositeScope import org.jetbrains.kotlin.fir.scopes.FirNameAwareCompositeScope
@@ -58,8 +57,9 @@ class FirDeclaredMemberScopeProvider(val useSiteSession: FirSession) : FirSessio
origin.generated -> { origin.generated -> {
FirGeneratedClassDeclaredMemberScope.create( FirGeneratedClassDeclaredMemberScope.create(
useSiteSession, useSiteSession,
MemberGenerationContext(klass.symbol, declaredScope = null), klass.symbol,
needNestedClassifierScope = true regularDeclaredScope = null,
scopeForGeneratedClass = true
) ?: FirTypeScope.Empty ) ?: FirTypeScope.Empty
} }
else -> { else -> {
@@ -73,8 +73,9 @@ class FirDeclaredMemberScopeProvider(val useSiteSession: FirSession) : FirSessio
val generatedScope = runIf(origin.fromSource || origin.generated) { val generatedScope = runIf(origin.fromSource || origin.generated) {
FirGeneratedClassDeclaredMemberScope.create( FirGeneratedClassDeclaredMemberScope.create(
useSiteSession, useSiteSession,
MemberGenerationContext(klass.symbol, baseScope), klass.symbol,
needNestedClassifierScope = false regularDeclaredScope = baseScope,
scopeForGeneratedClass = false
) )
} }
if (generatedScope != null) { if (generatedScope != null) {
@@ -93,11 +94,11 @@ class FirDeclaredMemberScopeProvider(val useSiteSession: FirSession) : FirSessio
private fun createNestedClassifierScope(klass: FirClass): FirNestedClassifierScope? { private fun createNestedClassifierScope(klass: FirClass): FirNestedClassifierScope? {
val origin = klass.origin val origin = klass.origin
return if (origin.generated) { return if (origin.generated) {
FirGeneratedClassNestedClassifierScope.create(useSiteSession, klass, baseScope = null) FirGeneratedClassNestedClassifierScope.create(useSiteSession, klass.symbol, regularNestedClassifierScope = null)
} else { } else {
val baseScope = FirNestedClassifierScopeImpl(klass, useSiteSession) val baseScope = FirNestedClassifierScopeImpl(klass, useSiteSession)
val generatedScope = runIf(origin.fromSource) { val generatedScope = runIf(origin.fromSource) {
FirGeneratedClassNestedClassifierScope.create(useSiteSession, klass, baseScope) FirGeneratedClassNestedClassifierScope.create(useSiteSession, klass.symbol, regularNestedClassifierScope = baseScope)
} }
if (generatedScope != null) { if (generatedScope != null) {
FirCompositeNestedClassifierScope( FirCompositeNestedClassifierScope(
@@ -6,13 +6,9 @@
package org.jetbrains.kotlin.fir.scopes.impl package org.jetbrains.kotlin.fir.scopes.impl
import org.jetbrains.kotlin.fir.FirSession import org.jetbrains.kotlin.fir.FirSession
import org.jetbrains.kotlin.fir.caches.FirCache import org.jetbrains.kotlin.fir.FirSessionComponent
import org.jetbrains.kotlin.fir.caches.FirLazyValue import org.jetbrains.kotlin.fir.caches.*
import org.jetbrains.kotlin.fir.caches.firCachesFactory
import org.jetbrains.kotlin.fir.caches.getValue
import org.jetbrains.kotlin.fir.declarations.FirClass import org.jetbrains.kotlin.fir.declarations.FirClass
import org.jetbrains.kotlin.fir.declarations.FirRegularClass
import org.jetbrains.kotlin.fir.declarations.utils.classId
import org.jetbrains.kotlin.fir.declarations.validate import org.jetbrains.kotlin.fir.declarations.validate
import org.jetbrains.kotlin.fir.extensions.* import org.jetbrains.kotlin.fir.extensions.*
import org.jetbrains.kotlin.fir.ownerGenerator import org.jetbrains.kotlin.fir.ownerGenerator
@@ -20,90 +16,52 @@ import org.jetbrains.kotlin.fir.render
import org.jetbrains.kotlin.fir.resolve.substitution.ConeSubstitutor import org.jetbrains.kotlin.fir.resolve.substitution.ConeSubstitutor
import org.jetbrains.kotlin.fir.symbols.impl.* import org.jetbrains.kotlin.fir.symbols.impl.*
import org.jetbrains.kotlin.name.CallableId import org.jetbrains.kotlin.name.CallableId
import org.jetbrains.kotlin.name.ClassId
import org.jetbrains.kotlin.name.Name import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.name.SpecialNames import org.jetbrains.kotlin.name.SpecialNames
import org.jetbrains.kotlin.utils.addToStdlib.flatGroupBy import org.jetbrains.kotlin.utils.addToStdlib.flatGroupBy
import org.jetbrains.kotlin.utils.addToStdlib.runIf import org.jetbrains.kotlin.utils.addToStdlib.runIf
class FirGeneratedClassDeclaredMemberScope private constructor( class FirGeneratedClassDeclaredMemberScope private constructor(
val useSiteSession: FirSession, classId: ClassId,
private val generationContext: MemberGenerationContext, private val storage: FirGeneratedMemberDeclarationsStorage.CallableStorage,
needNestedClassifierScope: Boolean, private val nestedClassifierScope: FirNestedClassifierScope?
val extensionsByCallableName: Map<Name, List<FirDeclarationGenerationExtension>>, ) : FirClassDeclaredMemberScope(classId) {
val allCallableNames: Set<Name>
) : FirClassDeclaredMemberScope(generationContext.owner.classId) {
companion object { companion object {
fun create( fun create(
session: FirSession, useSiteSession: FirSession,
generationContext: MemberGenerationContext, classSymbol: FirClassSymbol<*>,
needNestedClassifierScope: Boolean regularDeclaredScope: FirClassDeclaredMemberScope?,
scopeForGeneratedClass: Boolean
): FirGeneratedClassDeclaredMemberScope? { ): FirGeneratedClassDeclaredMemberScope? {
val extensionsByCallableName = session.groupExtensionsByName( /*
generationContext.owner.fir, * Extensions can modify source classes of the same session in which they are enabled
nameExtractor = { getCallableNamesForClass(it, generationContext) }, * This implies the contract that if declaration-site session and use-site session
nameTransformer = { it } * differs for some class, generated declarations should be provided by extensions
) * of declaration-site session
val allCallableNames = extensionsByCallableName.keys */
if (allCallableNames.isEmpty()) return null val storage = classSymbol.moduleData
.session
.generatedDeclarationsStorage
.getCallableStorage(classSymbol, regularDeclaredScope, scopeForGeneratedClass)
?: return null
val nestedClassifierScope = runIf(scopeForGeneratedClass) {
useSiteSession.nestedClassifierScope(classSymbol.fir)
}
return FirGeneratedClassDeclaredMemberScope( return FirGeneratedClassDeclaredMemberScope(
session, classSymbol.classId,
generationContext, storage,
needNestedClassifierScope, nestedClassifierScope
extensionsByCallableName,
allCallableNames
) )
} }
} }
private val firClass: FirClass
get() = generationContext.owner.fir
private val nestedClassifierScope: FirNestedClassifierScope? = runIf(needNestedClassifierScope) {
useSiteSession.nestedClassifierScope(firClass)
}
private val firCachesFactory = useSiteSession.firCachesFactory
// ------------------------------------------ caches ------------------------------------------
private val functionCache: FirCache<Name, List<FirNamedFunctionSymbol>, Nothing?> = firCachesFactory.createCache { callableId, _ ->
generateMemberFunctions(callableId)
}
private val propertyCache: FirCache<Name, List<FirPropertySymbol>, Nothing?> = firCachesFactory.createCache { callableId, _ ->
generateMemberProperties(callableId)
}
private val constructorCache: FirLazyValue<List<FirConstructorSymbol>> = firCachesFactory.createLazyValue {
generateConstructors()
}
// ------------------------------------------ generators ------------------------------------------
private fun generateMemberFunctions(name: Name): List<FirNamedFunctionSymbol> {
if (name == SpecialNames.INIT) return emptyList()
return extensionsByCallableName[name].orEmpty()
.flatMap { it.generateFunctions(CallableId(firClass.classId, name), generationContext) }
.onEach { it.fir.validate() }
}
private fun generateMemberProperties(name: Name): List<FirPropertySymbol> {
if (name == SpecialNames.INIT) return emptyList()
return extensionsByCallableName[name].orEmpty()
.flatMap { it.generateProperties(CallableId(firClass.classId, name), generationContext) }
.onEach { it.fir.validate() }
}
private fun generateConstructors(): List<FirConstructorSymbol> {
return extensionsByCallableName[SpecialNames.INIT].orEmpty()
.flatMap { it.generateConstructors(generationContext) }
.onEach { it.fir.validate() }
}
// ------------------------------------------ scope methods ------------------------------------------ // ------------------------------------------ scope methods ------------------------------------------
override fun getCallableNames(): Set<Name> { override fun getCallableNames(): Set<Name> {
return allCallableNames return storage.allCallableNames
} }
override fun getClassifierNames(): Set<Name> { override fun getClassifierNames(): Set<Name> {
@@ -116,88 +74,169 @@ class FirGeneratedClassDeclaredMemberScope private constructor(
override fun processFunctionsByName(name: Name, processor: (FirNamedFunctionSymbol) -> Unit) { override fun processFunctionsByName(name: Name, processor: (FirNamedFunctionSymbol) -> Unit) {
if (name !in getCallableNames()) return if (name !in getCallableNames()) return
for (functionSymbol in functionCache.getValue(name)) { for (functionSymbol in storage.functionCache.getValue(name)) {
processor(functionSymbol) processor(functionSymbol)
} }
} }
override fun processPropertiesByName(name: Name, processor: (FirVariableSymbol<*>) -> Unit) { override fun processPropertiesByName(name: Name, processor: (FirVariableSymbol<*>) -> Unit) {
if (name !in getCallableNames()) return if (name !in getCallableNames()) return
for (propertySymbol in propertyCache.getValue(name)) { for (propertySymbol in storage.propertyCache.getValue(name)) {
processor(propertySymbol) processor(propertySymbol)
} }
} }
override fun processDeclaredConstructors(processor: (FirConstructorSymbol) -> Unit) { override fun processDeclaredConstructors(processor: (FirConstructorSymbol) -> Unit) {
for (constructorSymbol in constructorCache.getValue()) { for (constructorSymbol in storage.constructorCache.getValue()) {
processor(constructorSymbol) processor(constructorSymbol)
} }
} }
} }
internal inline fun <T, V> FirSession.groupExtensionsByName(
klass: FirClass,
nameExtractor: FirDeclarationGenerationExtension.(FirClassSymbol<*>) -> Set<T>,
nameTransformer: (T) -> V
): Map<V, List<FirDeclarationGenerationExtension>> {
val extensions = getExtensionsForClass(klass)
val symbol = klass.symbol
return extensions.flatGroupBy(
keySelector = { extension -> extension.nameExtractor(symbol) },
keyTransformer = nameTransformer,
valueTransformer = { it }
)
}
internal fun FirSession.getExtensionsForClass(klass: FirClass): List<FirDeclarationGenerationExtension> {
val extensions = extensionService.declarationGenerators
return if (klass.origin.generated) {
listOf(klass.ownerGenerator!!)
} else {
extensions
}
}
class FirGeneratedClassNestedClassifierScope private constructor( class FirGeneratedClassNestedClassifierScope private constructor(
useSiteSession: FirSession, useSiteSession: FirSession,
klass: FirClass, klass: FirClass,
private val extensionsByName: Map<Name, List<FirDeclarationGenerationExtension>>, private val storage: FirGeneratedMemberDeclarationsStorage.ClassifierStorage
private val context: NestedClassGenerationContext
) : FirNestedClassifierScope(klass, useSiteSession) { ) : FirNestedClassifierScope(klass, useSiteSession) {
companion object { companion object {
@OptIn(FirExtensionApiInternals::class)
fun create( fun create(
useSiteSession: FirSession, useSiteSession: FirSession,
klass: FirClass, classSymbol: FirClassSymbol<*>,
baseScope: FirNestedClassifierScope? regularNestedClassifierScope: FirNestedClassifierScope?,
): FirGeneratedClassNestedClassifierScope? { ): FirGeneratedClassNestedClassifierScope? {
val symbol = klass.symbol /*
val context = NestedClassGenerationContext(klass.symbol, baseScope) * Extensions can modify source classes of the same session in which they are enabled
val extensionsByName = useSiteSession.getExtensionsForClass(klass).flatGroupBy { * This implies the contract that if declaration-site session and use-site session
it.nestedClassifierNamesCache.getValue(symbol, context) * differs for some class, generated declarations should be provided by extensions
} * of declaration-site session
if (extensionsByName.isEmpty()) return null */
return FirGeneratedClassNestedClassifierScope(useSiteSession, klass, extensionsByName, context) val storage = classSymbol.moduleData
.session
.generatedDeclarationsStorage
.getClassifierStorage(classSymbol, regularNestedClassifierScope)
?: return null
return FirGeneratedClassNestedClassifierScope(useSiteSession, classSymbol.fir, storage,)
} }
} }
private val nestedClassifierCache: FirCache<Name, FirRegularClassSymbol?, Nothing?> = override fun getNestedClassSymbol(name: Name): FirRegularClassSymbol? {
useSiteSession.firCachesFactory.createCache { name, _ -> return storage.classifiersCache.getValue(name)
generateNestedClassifier(name)
} }
override fun isEmpty(): Boolean {
return false
}
override fun getClassifierNames(): Set<Name> {
return storage.allClassifierNames
}
}
class FirGeneratedMemberDeclarationsStorage(private val session: FirSession) : FirSessionComponent {
private val cachesFactory = session.firCachesFactory
internal fun getCallableStorage(
classSymbol: FirClassSymbol<*>,
regularDeclaredScope: FirClassDeclaredMemberScope?,
scopeForGeneratedClass: Boolean
): CallableStorage? {
val generationContext = MemberGenerationContext(classSymbol, regularDeclaredScope)
val extensionsByCallableName = groupExtensionsByName(classSymbol) { getCallableNamesForClass(it, generationContext) }
if (extensionsByCallableName.isEmpty() && !scopeForGeneratedClass) return null
return callableStorageByClass.getValue(classSymbol, StorageContext(generationContext, extensionsByCallableName))
}
internal fun getClassifierStorage(
classSymbol: FirClassSymbol<*>,
regularNestedClassifierScope: FirNestedClassifierScope?
): ClassifierStorage? {
val generationContext = NestedClassGenerationContext(classSymbol, regularNestedClassifierScope)
val extensionsByClassifierName = groupExtensionsByName(classSymbol) { getNestedClassifiersNames(it, generationContext) }
if (extensionsByClassifierName.isEmpty()) return null
return classifierStorageByClass.getValue(classSymbol, StorageContext(generationContext, extensionsByClassifierName))
}
private data class StorageContext<C>(
val generationContext: C,
val extensionsByName: Map<Name, List<FirDeclarationGenerationExtension>>
)
private val callableStorageByClass: FirCache<FirClassSymbol<*>, CallableStorage, StorageContext<MemberGenerationContext>> =
cachesFactory.createCache { _, (context, extensionsMap) ->
CallableStorage(cachesFactory, context, extensionsMap)
}
private val classifierStorageByClass: FirCache<FirClassSymbol<*>, ClassifierStorage, StorageContext<NestedClassGenerationContext>> =
cachesFactory.createCache { classSymbol, (context, extensionsMap) ->
ClassifierStorage(cachesFactory, classSymbol, context, extensionsMap)
}
internal class CallableStorage(
cachesFactory: FirCachesFactory,
private val generationContext: MemberGenerationContext,
private val extensionsByCallableName: Map<Name, List<FirDeclarationGenerationExtension>>
) {
val functionCache: FirCache<Name, List<FirNamedFunctionSymbol>, Nothing?> =
cachesFactory.createCache { name -> generateMemberFunctions(name) }
val propertyCache: FirCache<Name, List<FirPropertySymbol>, Nothing?> =
cachesFactory.createCache { name -> generateMemberProperties(name) }
val constructorCache: FirLazyValue<List<FirConstructorSymbol>> =
cachesFactory.createLazyValue { generateConstructors() }
val allCallableNames: Set<Name>
get() = extensionsByCallableName.keys
private val classSymbol: FirClassSymbol<*>
get() = generationContext.owner
private fun generateMemberFunctions(name: Name): List<FirNamedFunctionSymbol> {
if (name == SpecialNames.INIT) return emptyList()
return extensionsByCallableName[name].orEmpty()
.flatMap { it.generateFunctions(CallableId(classSymbol.classId, name), generationContext) }
.onEach { it.fir.validate() }
}
private fun generateMemberProperties(name: Name): List<FirPropertySymbol> {
if (name == SpecialNames.INIT) return emptyList()
return extensionsByCallableName[name].orEmpty()
.flatMap { it.generateProperties(CallableId(classSymbol.classId, name), generationContext) }
.onEach { it.fir.validate() }
}
private fun generateConstructors(): List<FirConstructorSymbol> {
return extensionsByCallableName[SpecialNames.INIT].orEmpty()
.flatMap { it.generateConstructors(generationContext) }
.onEach { it.fir.validate() }
}
}
internal class ClassifierStorage(
cachesFactory: FirCachesFactory,
private val classSymbol: FirClassSymbol<*>,
private val generationContext: NestedClassGenerationContext,
private val extensionsByClassifierName: Map<Name, List<FirDeclarationGenerationExtension>>
) {
val classifiersCache: FirCache<Name, FirRegularClassSymbol?, Nothing?> =
cachesFactory.createCache { name -> generateNestedClassifier(name) }
val allClassifierNames: Set<Name>
get() = extensionsByClassifierName.keys
private fun generateNestedClassifier(name: Name): FirRegularClassSymbol? { private fun generateNestedClassifier(name: Name): FirRegularClassSymbol? {
if (klass is FirRegularClass) { if (classSymbol is FirRegularClassSymbol) {
val companion = klass.companionObjectSymbol val companion = classSymbol.companionObjectSymbol
if (companion != null && companion.origin.generated && companion.classId.shortClassName == name) { if (companion != null && companion.origin.generated && companion.classId.shortClassName == name) {
return companion return companion
} }
} }
val extensions = extensionsByName[name] ?: return null val extensions = extensionsByClassifierName[name] ?: return null
val generatedClasses = extensions.mapNotNull { extension -> val generatedClasses = extensions.mapNotNull { extension ->
extension.generateNestedClassLikeDeclaration(klass.symbol, name, context)?.also { symbol -> extension.generateNestedClassLikeDeclaration(classSymbol, name, generationContext)?.also { symbol ->
symbol.fir.ownerGenerator = extension symbol.fir.ownerGenerator = extension
} }
} }
@@ -207,7 +246,7 @@ class FirGeneratedClassNestedClassifierScope private constructor(
1 -> generatedClasses.first() 1 -> generatedClasses.first()
else -> error( else -> error(
""" """
Multiple plugins generated nested class with same name $name for class ${klass.classId}: Multiple plugins generated nested class with same name $name for class ${classSymbol.classId}:
${generatedClasses.joinToString("\n") { it.fir.render() }} ${generatedClasses.joinToString("\n") { it.fir.render() }}
""".trimIndent() """.trimIndent()
) )
@@ -215,16 +254,26 @@ class FirGeneratedClassNestedClassifierScope private constructor(
require(generatedClass is FirRegularClassSymbol) { "Only regular class are allowed as nested classes" } require(generatedClass is FirRegularClassSymbol) { "Only regular class are allowed as nested classes" }
return generatedClass return generatedClass
} }
override fun getNestedClassSymbol(name: Name): FirRegularClassSymbol? {
return nestedClassifierCache.getValue(name)
} }
override fun isEmpty(): Boolean { private inline fun groupExtensionsByName(
return extensionsByName.isEmpty() classSymbol: FirClassSymbol<*>,
nameExtractor: FirDeclarationGenerationExtension.(FirClassSymbol<*>) -> Set<Name>,
): Map<Name, List<FirDeclarationGenerationExtension>> {
val extensions = getExtensionsForClass(classSymbol)
return extensions.flatGroupBy { it.nameExtractor(classSymbol) }
} }
override fun getClassifierNames(): Set<Name> { private fun getExtensionsForClass(classSymbol: FirClassSymbol<*>): List<FirDeclarationGenerationExtension> {
return extensionsByName.keys require(session === classSymbol.moduleData.session) {
"Class $classSymbol is declared in ${classSymbol.moduleData.session}, but generated storage for it taken from $session"
}
return if (classSymbol.origin.generated) {
listOf(classSymbol.fir.ownerGenerator!!)
} else {
session.extensionService.declarationGenerators
}
} }
} }
private val FirSession.generatedDeclarationsStorage: FirGeneratedMemberDeclarationsStorage by FirSession.sessionComponentAccessor()
@@ -69,4 +69,12 @@ class K2KotlinxSerializationIT : KGPBaseTest() {
build(":compileTestKotlinJs") build(":compileTestKotlinJs")
} }
} }
@DisplayName("Compile MPP project to JS kotlinx.serialization and K2")
@GradleTest
fun `test kotlinx serialization mpp to JS`(gradleVersion: GradleVersion) {
project("kotlinxSerializationMppK2", gradleVersion) {
build(":compileKotlinJs")
}
}
} }
@@ -0,0 +1,3 @@
fun someSerializerCall() {
ValidateViolation.serializer()
}