[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,115 +74,206 @@ 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
private val nestedClassifierCache: FirCache<Name, FirRegularClassSymbol?, Nothing?> = return FirGeneratedClassNestedClassifierScope(useSiteSession, classSymbol.fir, storage,)
useSiteSession.firCachesFactory.createCache { name, _ ->
generateNestedClassifier(name)
} }
private fun generateNestedClassifier(name: Name): FirRegularClassSymbol? {
if (klass is FirRegularClass) {
val companion = klass.companionObjectSymbol
if (companion != null && companion.origin.generated && companion.classId.shortClassName == name) {
return companion
}
}
val extensions = extensionsByName[name] ?: return null
val generatedClasses = extensions.mapNotNull { extension ->
extension.generateNestedClassLikeDeclaration(klass.symbol, name, context)?.also { symbol ->
symbol.fir.ownerGenerator = extension
}
}
val generatedClass = when (generatedClasses.size) {
0 -> return null
1 -> generatedClasses.first()
else -> error(
"""
Multiple plugins generated nested class with same name $name for class ${klass.classId}:
${generatedClasses.joinToString("\n") { it.fir.render() }}
""".trimIndent()
)
}
require(generatedClass is FirRegularClassSymbol) { "Only regular class are allowed as nested classes" }
return generatedClass
} }
override fun getNestedClassSymbol(name: Name): FirRegularClassSymbol? { override fun getNestedClassSymbol(name: Name): FirRegularClassSymbol? {
return nestedClassifierCache.getValue(name) return storage.classifiersCache.getValue(name)
} }
override fun isEmpty(): Boolean { override fun isEmpty(): Boolean {
return extensionsByName.isEmpty() return false
} }
override fun getClassifierNames(): Set<Name> { override fun getClassifierNames(): Set<Name> {
return extensionsByName.keys 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? {
if (classSymbol is FirRegularClassSymbol) {
val companion = classSymbol.companionObjectSymbol
if (companion != null && companion.origin.generated && companion.classId.shortClassName == name) {
return companion
}
}
val extensions = extensionsByClassifierName[name] ?: return null
val generatedClasses = extensions.mapNotNull { extension ->
extension.generateNestedClassLikeDeclaration(classSymbol, name, generationContext)?.also { symbol ->
symbol.fir.ownerGenerator = extension
}
}
val generatedClass = when (generatedClasses.size) {
0 -> return null
1 -> generatedClasses.first()
else -> error(
"""
Multiple plugins generated nested class with same name $name for class ${classSymbol.classId}:
${generatedClasses.joinToString("\n") { it.fir.render() }}
""".trimIndent()
)
}
require(generatedClass is FirRegularClassSymbol) { "Only regular class are allowed as nested classes" }
return generatedClass
}
}
private inline fun groupExtensionsByName(
classSymbol: FirClassSymbol<*>,
nameExtractor: FirDeclarationGenerationExtension.(FirClassSymbol<*>) -> Set<Name>,
): Map<Name, List<FirDeclarationGenerationExtension>> {
val extensions = getExtensionsForClass(classSymbol)
return extensions.flatGroupBy { it.nameExtractor(classSymbol) }
}
private fun getExtensionsForClass(classSymbol: FirClassSymbol<*>): List<FirDeclarationGenerationExtension> {
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()
}