[FIR] Remove FirDeclarationsForMetadataProviderExtension extension

`IrGeneratedDeclarationsRegistrar` should be now used instead to register
  IR declarations in resulting metadata
This commit is contained in:
Dmitriy Novozhilov
2023-11-28 18:09:45 +02:00
parent d94ce4dcb8
commit c70c8f927e
8 changed files with 49 additions and 299 deletions
@@ -34,7 +34,6 @@ abstract class FirExtensionRegistrar : FirExtensionRegistrarAdapter() {
FirScriptConfiguratorExtension::class,
Fir2IrScriptConfiguratorExtension::class,
FirFunctionTypeKindExtension::class,
FirDeclarationsForMetadataProviderExtension::class,
)
internal val ALLOWED_EXTENSIONS_FOR_LIBRARY_SESSION = listOf(
@@ -108,11 +107,6 @@ abstract class FirExtensionRegistrar : FirExtensionRegistrarAdapter() {
registerExtension(FirFunctionTypeKindExtension::class, this)
}
@JvmName("plusDeclarationForMetadataProviderExtension")
operator fun (FirDeclarationsForMetadataProviderExtension.Factory).unaryPlus() {
registerExtension(FirDeclarationsForMetadataProviderExtension::class, this)
}
// ------------------ reference methods ------------------
@JvmName("plusStatusTransformerExtension")
@@ -175,11 +169,6 @@ abstract class FirExtensionRegistrar : FirExtensionRegistrarAdapter() {
FirFunctionTypeKindExtension.Factory { this.invoke(it) }.unaryPlus()
}
@JvmName("plusDeclarationForMetadataProviderExtension")
operator fun ((FirSession) -> FirDeclarationsForMetadataProviderExtension).unaryPlus() {
FirDeclarationsForMetadataProviderExtension.Factory { this.invoke(it) }.unaryPlus()
}
// ------------------ utilities ------------------
@JvmName("bindLeft")
@@ -9,22 +9,57 @@ import org.jetbrains.kotlin.fir.FirSession
import org.jetbrains.kotlin.fir.FirSessionComponent
import org.jetbrains.kotlin.fir.containingClassLookupTag
import org.jetbrains.kotlin.fir.declarations.*
import org.jetbrains.kotlin.fir.extensions.FirDeclarationsForMetadataProviderExtension
import org.jetbrains.kotlin.fir.extensions.declarationForMetadataProviders
import org.jetbrains.kotlin.fir.extensions.extensionService
import org.jetbrains.kotlin.fir.resolve.ScopeSession
import org.jetbrains.kotlin.fir.resolve.toFirRegularClass
import org.jetbrains.kotlin.fir.symbols.impl.FirClassLikeSymbol
import org.jetbrains.kotlin.fir.symbols.impl.FirClassSymbol
import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.utils.addToStdlib.getOrPut
import org.jetbrains.kotlin.utils.addToStdlib.shouldNotBeCalled
/**
* This extension can be used to provide information about declarations, which should be written in Kotlin metadata (i.e. be part of a module's ABI),
* but for some reason can not (or should not) be generated with [FirDeclarationGenerationExtension]
*
* Example: assume your plugin generates some constructor in [IrGenerationExtension] which have value parameters matching
* all properties of a class, and this constructor is used only as an implementation detail (so the only actor who accesses it
* is a plugin itself). The constructor should be accessible from another module, so it should be present in the metadata. But you
* can not generate this constructor in [FirDeclarationGenerationExtension], because it depends on types of properties,
* which may be not resolved at the moment of constructor creation.
*
*
* // MODULE: a
* open class Base {
* val x: Int = 1
* val y = "hello"
*
* constructor()
*
* // generated
* constructor(x: Int, y: String) { ... } // (1)
* }
*
* // MODULE: b(a)
* class Derived : Base {
* val z = 1.0
*
* constructor() : super()
*
* // generated
* constructor(x: Int, y: String, z: Double) : super(x, y) { ... } // (2)
*
* // constructor (1) should be presented in metadata of class Base, so IR plugin
* // can reference it during generation of constructor (2)
* }
*
* All declarations provided by this extension should be fully resolved and contain all sub declarations if they exist.
* E.g. if you want to provide some class then this class should contain all declarations of this class you want to be
* present in metadata in `declarations` field of a `FirRegularClass`.
* [FirDeclarationGenerationExtension] won't be called for classes provided by this extension.
*/
abstract class FirProvidedDeclarationsForMetadataService : FirSessionComponent {
companion object {
fun create(session: FirSession): FirProvidedDeclarationsForMetadataService {
val extensionProviders = session.extensionService.declarationForMetadataProviders
return if (extensionProviders.isEmpty()) Empty else FirProvidedDeclarationsForMetadataServiceImpl(session, extensionProviders)
return FirProvidedDeclarationsForMetadataServiceImpl(session)
}
}
@@ -35,33 +70,9 @@ abstract class FirProvidedDeclarationsForMetadataService : FirSessionComponent {
abstract fun registerDeclaration(declaration: FirCallableDeclaration)
private object Empty : FirProvidedDeclarationsForMetadataService() {
override fun getProvidedTopLevelDeclarations(packageFqName: FqName, scopeSession: ScopeSession): List<FirDeclaration> {
return emptyList()
}
override fun getProvidedConstructors(owner: FirClassSymbol<*>, scopeSession: ScopeSession): List<FirConstructor> {
return emptyList()
}
override fun getProvidedCallables(owner: FirClassSymbol<*>, scopeSession: ScopeSession): List<FirCallableDeclaration> {
return emptyList()
}
override fun getProvidedNestedClassifiers(owner: FirClassSymbol<*>, scopeSession: ScopeSession): List<FirClassLikeSymbol<*>> {
return emptyList()
}
override fun registerDeclaration(declaration: FirCallableDeclaration) {
shouldNotBeCalled()
}
}
}
private class FirProvidedDeclarationsForMetadataServiceImpl(
private val session: FirSession,
private val extensionDeclarationProviders: List<FirDeclarationsForMetadataProviderExtension>
) : FirProvidedDeclarationsForMetadataService() {
private class FirProvidedDeclarationsForMetadataServiceImpl(private val session: FirSession) : FirProvidedDeclarationsForMetadataService() {
private val topLevelsCache: MutableMap<FqName, MutableList<FirDeclaration>> =
mutableMapOf()
@@ -1,75 +0,0 @@
/*
* 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.
*/
package org.jetbrains.kotlin.fir.extensions
import org.jetbrains.kotlin.fir.FirSession
import org.jetbrains.kotlin.fir.declarations.FirClass
import org.jetbrains.kotlin.fir.declarations.FirDeclaration
import org.jetbrains.kotlin.fir.resolve.ScopeSession
import org.jetbrains.kotlin.name.FqName
import kotlin.reflect.KClass
/**
* This extension can be used to provide information about declarations, which should be written in Kotlin metadata (i.e. be part of a module's ABI),
* but for some reason can not (or should not) be generated with [FirDeclarationGenerationExtension]
*
* Example: assume your plugin generates some constructor in [IrGenerationExtension] which have value parameters matching
* all properties of a class, and this constructor is used only as an implementation detail (so the only actor who accesses it
* is a plugin itself). The constructor should be accessible from another module, so it should be present in the metadata. But you
* can not generate this constructor in [FirDeclarationGenerationExtension], because it depends on types of properties,
* which may be not resolved at the moment of constructor creation.
*
*
* // MODULE: a
* open class Base {
* val x: Int = 1
* val y = "hello"
*
* constructor()
*
* // generated
* constructor(x: Int, y: String) { ... } // (1)
* }
*
* // MODULE: b(a)
* class Derived : Base {
* val z = 1.0
*
* constructor() : super()
*
* // generated
* constructor(x: Int, y: String, z: Double) : super(x, y) { ... } // (2)
*
* // constructor (1) should be presented in metadata of class Base, so IR plugin
* // can reference it during generation of constructor (2)
* }
*
* All declarations provided by this extension should be fully resolved and contain all sub declarations if they exist.
* E.g. if you want to provide some class then this class should contain all declarations of this class you want to be
* present in metadata in `declarations` field of a `FirRegularClass`.
* [FirDeclarationGenerationExtension] won't be called for classes provided by this extension.
*/
abstract class FirDeclarationsForMetadataProviderExtension(session: FirSession) : FirExtension(session) {
companion object {
val NAME = FirExtensionPointName("SyntheticDeclarationsForMetadataGenerationExtension")
}
final override val name: FirExtensionPointName
get() = NAME
final override val extensionType: KClass<out FirExtension> = FirDeclarationsForMetadataProviderExtension::class
/**
* It's allowed to provide only functions, properties and type aliases
*/
open fun provideTopLevelDeclarations(packageFqName: FqName, scopeSession: ScopeSession): List<FirDeclaration> = emptyList()
open fun provideDeclarationsForClass(klass: FirClass, scopeSession: ScopeSession): List<FirDeclaration> = emptyList()
fun interface Factory : FirExtension.Factory<FirDeclarationsForMetadataProviderExtension>
}
val FirExtensionService.declarationForMetadataProviders: List<FirDeclarationsForMetadataProviderExtension> by FirExtensionService.registeredExtensions()
@@ -34,8 +34,6 @@ class FirPluginPrototypeExtensionRegistrar : FirExtensionRegistrar() {
+::AdditionalMembersGenerator
+::CompanionGenerator
+::MembersOfSerializerGenerator
+::AllPropertiesConstructorMetadataProvider
}
}
@@ -1,48 +0,0 @@
/*
* 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.
*/
package org.jetbrains.kotlin.fir.plugin.generators
import org.jetbrains.kotlin.GeneratedDeclarationKey
import org.jetbrains.kotlin.fir.FirSession
import org.jetbrains.kotlin.fir.containingClassLookupTag
import org.jetbrains.kotlin.fir.declarations.FirClass
import org.jetbrains.kotlin.fir.declarations.FirDeclaration
import org.jetbrains.kotlin.fir.extensions.FirDeclarationPredicateRegistrar
import org.jetbrains.kotlin.fir.extensions.FirDeclarationsForMetadataProviderExtension
import org.jetbrains.kotlin.fir.extensions.predicate.DeclarationPredicate
import org.jetbrains.kotlin.fir.extensions.predicateBasedProvider
import org.jetbrains.kotlin.fir.plugin.createConstructor
import org.jetbrains.kotlin.fir.plugin.fqn
import org.jetbrains.kotlin.fir.resolve.ScopeSession
import org.jetbrains.kotlin.fir.scopes.getProperties
import org.jetbrains.kotlin.fir.scopes.unsubstitutedScope
class AllPropertiesConstructorMetadataProvider(session: FirSession) : FirDeclarationsForMetadataProviderExtension(session) {
companion object {
val ANNOTATION_FQN = "AllPropertiesConstructor".fqn()
val PREDICATE = DeclarationPredicate.create { annotated(ANNOTATION_FQN) }
}
private object Key : GeneratedDeclarationKey()
override fun provideDeclarationsForClass(klass: FirClass, scopeSession: ScopeSession): List<FirDeclaration> {
if (!session.predicateBasedProvider.matches(PREDICATE, klass)) return emptyList()
val scope = klass.unsubstitutedScope(session, scopeSession, withForcedTypeCalculator = false, memberRequiredPhase = null)
val properties = scope.getCallableNames()
.flatMap { scope.getProperties(it) }
.sortedBy { it.containingClassLookupTag() == klass.symbol.toLookupTag() }
val constructor = createConstructor(klass.symbol, Key) {
for (property in properties) {
valueParameter(property.name, property.resolvedReturnType)
}
}
return listOf(constructor)
}
override fun FirDeclarationPredicateRegistrar.registerPredicates() {
register(PREDICATE)
}
}
@@ -7,7 +7,7 @@ package org.jetbrains.kotlin.ir.plugin
import org.jetbrains.kotlin.backend.common.extensions.IrPluginContext
import org.jetbrains.kotlin.descriptors.ClassKind
import org.jetbrains.kotlin.fir.plugin.generators.AllPropertiesConstructorMetadataProvider
import org.jetbrains.kotlin.fir.plugin.fqn
import org.jetbrains.kotlin.ir.IrElement
import org.jetbrains.kotlin.ir.builders.declarations.buildConstructor
import org.jetbrains.kotlin.ir.builders.declarations.buildValueParameter
@@ -29,6 +29,10 @@ import java.util.Comparator
* Parent class should be Any or class, annotated with @AllPropertiesConstructor
*/
class AllPropertiesConstructorIrGenerator(val context: IrPluginContext) : IrElementVisitorVoid {
companion object {
private val ANNOTATION_FQN = "AllPropertiesConstructor".fqn()
}
override fun visitElement(element: IrElement) {
element.acceptChildrenVoid(this)
}
@@ -89,8 +93,6 @@ class AllPropertiesConstructorIrGenerator(val context: IrPluginContext) : IrElem
}
private fun IrClass.hasAnnotation(): Boolean {
return annotations.findAnnotation(AllPropertiesConstructorMetadataProvider.ANNOTATION_FQN) != null
return annotations.hasAnnotation(ANNOTATION_FQN)
}
}
@@ -17,7 +17,6 @@ class FirSerializationExtensionRegistrar : FirExtensionRegistrar() {
+::SerializationFirResolveExtension
+::SerializationFirSupertypesExtension
+::FirSerializationCheckersComponent
+::SerializationFirDeclarationsForMetadataProvider
// services
+::DependencySerializationInfoProvider
@@ -1,126 +0,0 @@
/*
* 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.
*/
package org.jetbrains.kotlinx.serialization.compiler.fir
import org.jetbrains.kotlin.descriptors.Visibilities
import org.jetbrains.kotlin.fir.FirSession
import org.jetbrains.kotlin.fir.declarations.FirClass
import org.jetbrains.kotlin.fir.declarations.FirDeclaration
import org.jetbrains.kotlin.fir.declarations.utils.isFinal
import org.jetbrains.kotlin.fir.declarations.utils.isInline
import org.jetbrains.kotlin.fir.expressions.FirAnnotation
import org.jetbrains.kotlin.fir.expressions.builder.buildAnnotationCall
import org.jetbrains.kotlin.fir.extensions.FirDeclarationsForMetadataProviderExtension
import org.jetbrains.kotlin.fir.moduleData
import org.jetbrains.kotlin.fir.plugin.createConstructor
import org.jetbrains.kotlin.fir.plugin.createMemberFunction
import org.jetbrains.kotlin.fir.references.builder.buildResolvedNamedReference
import org.jetbrains.kotlin.fir.resolve.ScopeSession
import org.jetbrains.kotlin.fir.resolve.defaultType
import org.jetbrains.kotlin.fir.resolve.providers.symbolProvider
import org.jetbrains.kotlin.fir.scopes.impl.toConeType
import org.jetbrains.kotlin.fir.symbols.impl.FirConstructorSymbol
import org.jetbrains.kotlin.fir.symbols.impl.FirRegularClassSymbol
import org.jetbrains.kotlin.fir.types.*
import org.jetbrains.kotlin.name.ClassId
import org.jetbrains.kotlin.name.JvmStandardClassIds
import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.platform.jvm.isJvm
import org.jetbrains.kotlin.utils.addToStdlib.firstIsInstanceOrNull
import org.jetbrains.kotlinx.serialization.compiler.fir.services.serializablePropertiesProvider
import org.jetbrains.kotlinx.serialization.compiler.resolve.*
class SerializationFirDeclarationsForMetadataProvider(session: FirSession) : FirDeclarationsForMetadataProviderExtension(session) {
override fun provideDeclarationsForClass(klass: FirClass, scopeSession: ScopeSession): List<FirDeclaration> {
// FIXME: multi-field value classes require additional 'if' here, but they're not supported for now in serialization K2.
return if (klass.symbol.isInline || with(session) { !klass.symbol.isInternalSerializable }) emptyList()
// As we deprecated 'customize serializer via companion', we know for sure
// that serialize/deserialize functions are synthetic and generated by our plugin.
else listOfNotNull(generateDeserializationConstructor(klass), generateWriteSelf(klass))
}
private fun generateDeserializationConstructor(klass: FirClass): FirDeclaration =
createConstructor(klass.symbol, SerializationPluginKey, isPrimary = false) {
// deserialization constructor for final classes could be internal, because it can't be called in inheritors
visibility = if (klass.isFinal) Visibilities.Internal else Visibilities.Public
val serializableProperties =
session.serializablePropertiesProvider.getSerializablePropertiesForClass(klass.symbol).serializableProperties
val bitMaskSlotCount = serializableProperties.bitMaskSlotCount()
repeat(bitMaskSlotCount) {
valueParameter(Name.identifier("seen$it"), session.builtinTypes.intType.type)
}
serializableProperties.forEach { prop ->
valueParameter(
prop.originalDescriptorName,
prop.propertySymbol.resolvedReturnType.makeNullableIfNotPrimitive(session.typeContext)
)
}
val markerType =
ClassId(SerializationPackages.internalPackageFqName, SerialEntityNames.SERIAL_CTOR_MARKER_NAME).constructClassLikeType(
emptyArray(),
isNullable = true
)
valueParameter(SerialEntityNames.dummyParamName, markerType)
}
private fun generateWriteSelf(klass: FirClass): FirDeclaration? {
// write$Self in K1 is created only on JVM (see SerializationResolveExtension)
if (!session.moduleData.platform.isJvm()) return null
return createMemberFunction(
klass.symbol,
SerializationPluginKey,
SerialEntityNames.WRITE_SELF_NAME,
session.builtinTypes.unitType.type
) {
// write$Self for final classes could be internal, because it can't be called in inheritors
visibility = if (klass.isFinal) Visibilities.Internal else Visibilities.Public
klass.typeParameters.forEach {
typeParameter(it.symbol.name)
}
valueParameter(Name.identifier("self"), { functionTypeParams ->
klass.symbol.constructType(functionTypeParams.map { it.toConeType() }.toTypedArray(), false)
})
valueParameter(
Name.identifier("output"),
SerializationRuntimeClassIds.compositeEncoderClassId.constructClassLikeType(emptyArray(), false)
)
valueParameter(
Name.identifier("serialDesc"),
SerializationRuntimeClassIds.descriptorClassId.constructClassLikeType(emptyArray(), false)
)
klass.typeParameters.forEachIndexed { i, _ ->
valueParameter(Name.identifier("${SerialEntityNames.typeArgPrefix}$i"), { functionTps ->
SerializersClassIds.kSerializerId.constructClassLikeType(arrayOf(functionTps[i].toConeType()), false)
})
}
}.apply { replaceAnnotations(listOfNotNull(createJvmStaticAnnotation())) }
}
private fun createJvmStaticAnnotation(): FirAnnotation? {
val jvmStatic =
session.symbolProvider.getClassLikeSymbolByClassId(JvmStandardClassIds.Annotations.JvmStatic) as? FirRegularClassSymbol
?: return null
val jvmStaticCtor =
jvmStatic.declarationSymbols.firstIsInstanceOrNull<FirConstructorSymbol>() ?: return null
return buildAnnotationCall {
annotationTypeRef = jvmStatic.defaultType().toFirResolvedTypeRef()
calleeReference = buildResolvedNamedReference {
name = jvmStatic.name
resolvedSymbol = jvmStaticCtor
}
}
}
}
internal fun ConeKotlinType.makeNullableIfNotPrimitive(typeContext: ConeTypeContext): ConeKotlinType =
if (isPrimitive) this else withNullability(ConeNullability.NULLABLE, typeContext)