Add declarations from serialization plugin to FIR metadata declarations provider.
These declarations should not be visible to users (and therefore are not added to FIR), but plugin itself can reference them in already compiled serializable classes, and therefore they should be available in metadata: - synthetic deserialization constructor - static write$Self function See also: ^KT-55885
This commit is contained in:
committed by
Space Team
parent
8953b25c5b
commit
c564dd973b
+2
-1
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2010-2021 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.
|
||||
*/
|
||||
|
||||
@@ -17,6 +17,7 @@ class FirSerializationExtensionRegistrar : FirExtensionRegistrar() {
|
||||
+::SerializationFirResolveExtension
|
||||
+::SerializationFirSupertypesExtension
|
||||
+::FirSerializationCheckersComponent
|
||||
+::SerializationFirDeclarationsForMetadataProvider
|
||||
|
||||
// services
|
||||
+::DependencySerializationInfoProvider
|
||||
|
||||
+118
@@ -0,0 +1,118 @@
|
||||
/*
|
||||
* 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.fir.FirSession
|
||||
import org.jetbrains.kotlin.fir.declarations.FirClass
|
||||
import org.jetbrains.kotlin.fir.declarations.FirDeclaration
|
||||
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.Name
|
||||
import org.jetbrains.kotlin.name.StandardClassIds
|
||||
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) {
|
||||
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
|
||||
) {
|
||||
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(StandardClassIds.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)
|
||||
Reference in New Issue
Block a user