From 2087a7c8993191a14c9bfcbeb790f5fe4eedd0fe Mon Sep 17 00:00:00 2001 From: Dmitriy Novozhilov Date: Thu, 20 Apr 2023 15:39:26 +0300 Subject: [PATCH] [FIR] Store generated declarations in the session component Also extract generated declarations for classes from declaration site session, not use site ^KT-57821 Fixed --- .../fir/session/ComponentsContainers.kt | 1 + .../impl/FirDeclaredMemberScopeProvider.kt | 15 +- .../fir/scopes/impl/FirGeneratedScopes.kt | 345 ++++++++++-------- .../kotlin/gradle/K2KotlinxSerializationIT.kt | 8 + .../src/jsMain/kotlin/JsMain.kt | 3 + 5 files changed, 217 insertions(+), 155 deletions(-) create mode 100644 libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/kotlinxSerializationMppK2/src/jsMain/kotlin/JsMain.kt diff --git a/compiler/fir/entrypoint/src/org/jetbrains/kotlin/fir/session/ComponentsContainers.kt b/compiler/fir/entrypoint/src/org/jetbrains/kotlin/fir/session/ComponentsContainers.kt index 43f5f5bb561..108c7a9ca97 100644 --- a/compiler/fir/entrypoint/src/org/jetbrains/kotlin/fir/session/ComponentsContainers.kt +++ b/compiler/fir/entrypoint/src/org/jetbrains/kotlin/fir/session/ComponentsContainers.kt @@ -71,6 +71,7 @@ fun FirSession.registerCommonComponents(languageVersionSettings: LanguageVersion register(FirSubstitutionOverrideStorage::class, FirSubstitutionOverrideStorage(this)) register(FirIntersectionOverrideStorage::class, FirIntersectionOverrideStorage(this)) + register(FirGeneratedMemberDeclarationsStorage::class, FirGeneratedMemberDeclarationsStorage(this)) register(FirSamConstructorStorage::class, FirSamConstructorStorage(this)) register(FirOverrideService::class, FirOverrideService(this)) register(FirDynamicMembersStorage::class, FirDynamicMembersStorage(this)) diff --git a/compiler/fir/providers/src/org/jetbrains/kotlin/fir/scopes/impl/FirDeclaredMemberScopeProvider.kt b/compiler/fir/providers/src/org/jetbrains/kotlin/fir/scopes/impl/FirDeclaredMemberScopeProvider.kt index 7f4d6a17d90..12056227bfd 100644 --- a/compiler/fir/providers/src/org/jetbrains/kotlin/fir/scopes/impl/FirDeclaredMemberScopeProvider.kt +++ b/compiler/fir/providers/src/org/jetbrains/kotlin/fir/scopes/impl/FirDeclaredMemberScopeProvider.kt @@ -12,7 +12,6 @@ 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.FirClass -import org.jetbrains.kotlin.fir.extensions.MemberGenerationContext import org.jetbrains.kotlin.fir.resolve.providers.FirSymbolProvider import org.jetbrains.kotlin.fir.scopes.FirContainingNamesAwareScope import org.jetbrains.kotlin.fir.scopes.FirNameAwareCompositeScope @@ -58,8 +57,9 @@ class FirDeclaredMemberScopeProvider(val useSiteSession: FirSession) : FirSessio origin.generated -> { FirGeneratedClassDeclaredMemberScope.create( useSiteSession, - MemberGenerationContext(klass.symbol, declaredScope = null), - needNestedClassifierScope = true + klass.symbol, + regularDeclaredScope = null, + scopeForGeneratedClass = true ) ?: FirTypeScope.Empty } else -> { @@ -73,8 +73,9 @@ class FirDeclaredMemberScopeProvider(val useSiteSession: FirSession) : FirSessio val generatedScope = runIf(origin.fromSource || origin.generated) { FirGeneratedClassDeclaredMemberScope.create( useSiteSession, - MemberGenerationContext(klass.symbol, baseScope), - needNestedClassifierScope = false + klass.symbol, + regularDeclaredScope = baseScope, + scopeForGeneratedClass = false ) } if (generatedScope != null) { @@ -93,11 +94,11 @@ class FirDeclaredMemberScopeProvider(val useSiteSession: FirSession) : FirSessio private fun createNestedClassifierScope(klass: FirClass): FirNestedClassifierScope? { val origin = klass.origin return if (origin.generated) { - FirGeneratedClassNestedClassifierScope.create(useSiteSession, klass, baseScope = null) + FirGeneratedClassNestedClassifierScope.create(useSiteSession, klass.symbol, regularNestedClassifierScope = null) } else { val baseScope = FirNestedClassifierScopeImpl(klass, useSiteSession) val generatedScope = runIf(origin.fromSource) { - FirGeneratedClassNestedClassifierScope.create(useSiteSession, klass, baseScope) + FirGeneratedClassNestedClassifierScope.create(useSiteSession, klass.symbol, regularNestedClassifierScope = baseScope) } if (generatedScope != null) { FirCompositeNestedClassifierScope( diff --git a/compiler/fir/providers/src/org/jetbrains/kotlin/fir/scopes/impl/FirGeneratedScopes.kt b/compiler/fir/providers/src/org/jetbrains/kotlin/fir/scopes/impl/FirGeneratedScopes.kt index 7d4a55b029c..e0880705a83 100644 --- a/compiler/fir/providers/src/org/jetbrains/kotlin/fir/scopes/impl/FirGeneratedScopes.kt +++ b/compiler/fir/providers/src/org/jetbrains/kotlin/fir/scopes/impl/FirGeneratedScopes.kt @@ -6,13 +6,9 @@ package org.jetbrains.kotlin.fir.scopes.impl import org.jetbrains.kotlin.fir.FirSession -import org.jetbrains.kotlin.fir.caches.FirCache -import org.jetbrains.kotlin.fir.caches.FirLazyValue -import org.jetbrains.kotlin.fir.caches.firCachesFactory -import org.jetbrains.kotlin.fir.caches.getValue +import org.jetbrains.kotlin.fir.FirSessionComponent +import org.jetbrains.kotlin.fir.caches.* 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.extensions.* 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.symbols.impl.* import org.jetbrains.kotlin.name.CallableId +import org.jetbrains.kotlin.name.ClassId import org.jetbrains.kotlin.name.Name import org.jetbrains.kotlin.name.SpecialNames import org.jetbrains.kotlin.utils.addToStdlib.flatGroupBy import org.jetbrains.kotlin.utils.addToStdlib.runIf class FirGeneratedClassDeclaredMemberScope private constructor( - val useSiteSession: FirSession, - private val generationContext: MemberGenerationContext, - needNestedClassifierScope: Boolean, - val extensionsByCallableName: Map>, - val allCallableNames: Set -) : FirClassDeclaredMemberScope(generationContext.owner.classId) { + classId: ClassId, + private val storage: FirGeneratedMemberDeclarationsStorage.CallableStorage, + private val nestedClassifierScope: FirNestedClassifierScope? +) : FirClassDeclaredMemberScope(classId) { companion object { fun create( - session: FirSession, - generationContext: MemberGenerationContext, - needNestedClassifierScope: Boolean + useSiteSession: FirSession, + classSymbol: FirClassSymbol<*>, + regularDeclaredScope: FirClassDeclaredMemberScope?, + scopeForGeneratedClass: Boolean ): FirGeneratedClassDeclaredMemberScope? { - val extensionsByCallableName = session.groupExtensionsByName( - generationContext.owner.fir, - nameExtractor = { getCallableNamesForClass(it, generationContext) }, - nameTransformer = { it } - ) - val allCallableNames = extensionsByCallableName.keys - if (allCallableNames.isEmpty()) return null + /* + * Extensions can modify source classes of the same session in which they are enabled + * This implies the contract that if declaration-site session and use-site session + * differs for some class, generated declarations should be provided by extensions + * of declaration-site session + */ + val storage = classSymbol.moduleData + .session + .generatedDeclarationsStorage + .getCallableStorage(classSymbol, regularDeclaredScope, scopeForGeneratedClass) + ?: return null + + val nestedClassifierScope = runIf(scopeForGeneratedClass) { + useSiteSession.nestedClassifierScope(classSymbol.fir) + } + return FirGeneratedClassDeclaredMemberScope( - session, - generationContext, - needNestedClassifierScope, - extensionsByCallableName, - allCallableNames + classSymbol.classId, + storage, + nestedClassifierScope ) } } - 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, Nothing?> = firCachesFactory.createCache { callableId, _ -> - generateMemberFunctions(callableId) - } - - private val propertyCache: FirCache, Nothing?> = firCachesFactory.createCache { callableId, _ -> - generateMemberProperties(callableId) - } - - private val constructorCache: FirLazyValue> = firCachesFactory.createLazyValue { - generateConstructors() - } - - // ------------------------------------------ generators ------------------------------------------ - - private fun generateMemberFunctions(name: Name): List { - 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 { - 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 { - return extensionsByCallableName[SpecialNames.INIT].orEmpty() - .flatMap { it.generateConstructors(generationContext) } - .onEach { it.fir.validate() } - } - // ------------------------------------------ scope methods ------------------------------------------ override fun getCallableNames(): Set { - return allCallableNames + return storage.allCallableNames } override fun getClassifierNames(): Set { @@ -116,115 +74,206 @@ class FirGeneratedClassDeclaredMemberScope private constructor( override fun processFunctionsByName(name: Name, processor: (FirNamedFunctionSymbol) -> Unit) { if (name !in getCallableNames()) return - for (functionSymbol in functionCache.getValue(name)) { + for (functionSymbol in storage.functionCache.getValue(name)) { processor(functionSymbol) } } override fun processPropertiesByName(name: Name, processor: (FirVariableSymbol<*>) -> Unit) { if (name !in getCallableNames()) return - for (propertySymbol in propertyCache.getValue(name)) { + for (propertySymbol in storage.propertyCache.getValue(name)) { processor(propertySymbol) } } override fun processDeclaredConstructors(processor: (FirConstructorSymbol) -> Unit) { - for (constructorSymbol in constructorCache.getValue()) { + for (constructorSymbol in storage.constructorCache.getValue()) { processor(constructorSymbol) } } } -internal inline fun FirSession.groupExtensionsByName( - klass: FirClass, - nameExtractor: FirDeclarationGenerationExtension.(FirClassSymbol<*>) -> Set, - nameTransformer: (T) -> V -): Map> { - 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 { - val extensions = extensionService.declarationGenerators - return if (klass.origin.generated) { - listOf(klass.ownerGenerator!!) - } else { - extensions - } -} - class FirGeneratedClassNestedClassifierScope private constructor( useSiteSession: FirSession, klass: FirClass, - private val extensionsByName: Map>, - private val context: NestedClassGenerationContext + private val storage: FirGeneratedMemberDeclarationsStorage.ClassifierStorage ) : FirNestedClassifierScope(klass, useSiteSession) { companion object { - @OptIn(FirExtensionApiInternals::class) fun create( useSiteSession: FirSession, - klass: FirClass, - baseScope: FirNestedClassifierScope? + classSymbol: FirClassSymbol<*>, + regularNestedClassifierScope: FirNestedClassifierScope?, ): FirGeneratedClassNestedClassifierScope? { - val symbol = klass.symbol - val context = NestedClassGenerationContext(klass.symbol, baseScope) - val extensionsByName = useSiteSession.getExtensionsForClass(klass).flatGroupBy { - it.nestedClassifierNamesCache.getValue(symbol, context) - } - if (extensionsByName.isEmpty()) return null - return FirGeneratedClassNestedClassifierScope(useSiteSession, klass, extensionsByName, context) - } - } + /* + * Extensions can modify source classes of the same session in which they are enabled + * This implies the contract that if declaration-site session and use-site session + * differs for some class, generated declarations should be provided by extensions + * of declaration-site session + */ + val storage = classSymbol.moduleData + .session + .generatedDeclarationsStorage + .getClassifierStorage(classSymbol, regularNestedClassifierScope) + ?: return null - private val nestedClassifierCache: FirCache = - useSiteSession.firCachesFactory.createCache { name, _ -> - generateNestedClassifier(name) + return FirGeneratedClassNestedClassifierScope(useSiteSession, classSymbol.fir, storage,) } - - 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? { - return nestedClassifierCache.getValue(name) + return storage.classifiersCache.getValue(name) } override fun isEmpty(): Boolean { - return extensionsByName.isEmpty() + return false } override fun getClassifierNames(): Set { - 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( + val generationContext: C, + val extensionsByName: Map> + ) + + private val callableStorageByClass: FirCache, CallableStorage, StorageContext> = + cachesFactory.createCache { _, (context, extensionsMap) -> + CallableStorage(cachesFactory, context, extensionsMap) + } + + private val classifierStorageByClass: FirCache, ClassifierStorage, StorageContext> = + cachesFactory.createCache { classSymbol, (context, extensionsMap) -> + ClassifierStorage(cachesFactory, classSymbol, context, extensionsMap) + } + + internal class CallableStorage( + cachesFactory: FirCachesFactory, + private val generationContext: MemberGenerationContext, + private val extensionsByCallableName: Map> + ) { + val functionCache: FirCache, Nothing?> = + cachesFactory.createCache { name -> generateMemberFunctions(name) } + + val propertyCache: FirCache, Nothing?> = + cachesFactory.createCache { name -> generateMemberProperties(name) } + + val constructorCache: FirLazyValue> = + cachesFactory.createLazyValue { generateConstructors() } + + val allCallableNames: Set + get() = extensionsByCallableName.keys + + private val classSymbol: FirClassSymbol<*> + get() = generationContext.owner + + private fun generateMemberFunctions(name: Name): List { + 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 { + 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 { + 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> + ) { + val classifiersCache: FirCache = + cachesFactory.createCache { name -> generateNestedClassifier(name) } + + val allClassifierNames: Set + 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, + ): Map> { + val extensions = getExtensionsForClass(classSymbol) + return extensions.flatGroupBy { it.nameExtractor(classSymbol) } + } + + private fun getExtensionsForClass(classSymbol: FirClassSymbol<*>): List { + 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() diff --git a/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/kotlin/org/jetbrains/kotlin/gradle/K2KotlinxSerializationIT.kt b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/kotlin/org/jetbrains/kotlin/gradle/K2KotlinxSerializationIT.kt index 42c91879074..b0e9665a7ab 100644 --- a/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/kotlin/org/jetbrains/kotlin/gradle/K2KotlinxSerializationIT.kt +++ b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/kotlin/org/jetbrains/kotlin/gradle/K2KotlinxSerializationIT.kt @@ -69,4 +69,12 @@ class K2KotlinxSerializationIT : KGPBaseTest() { 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") + } + } } diff --git a/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/kotlinxSerializationMppK2/src/jsMain/kotlin/JsMain.kt b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/kotlinxSerializationMppK2/src/jsMain/kotlin/JsMain.kt new file mode 100644 index 00000000000..30d985a96c0 --- /dev/null +++ b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/kotlinxSerializationMppK2/src/jsMain/kotlin/JsMain.kt @@ -0,0 +1,3 @@ +fun someSerializerCall() { + ValidateViolation.serializer() +}