Add SerializerFactory supertype for Native & JS in FIR serialization plugin.

This supertype is intended to be used with @AssociatedObject on platforms
where reflection possibilities are limited.
This commit is contained in:
Leonid Startsev
2022-09-08 16:53:45 +02:00
committed by Space
parent b388c7503b
commit 62f67e46fd
5 changed files with 49 additions and 6 deletions
@@ -7,6 +7,8 @@ package org.jetbrains.kotlinx.serialization.compiler.backend.ir
import org.jetbrains.kotlin.descriptors.ClassKind import org.jetbrains.kotlin.descriptors.ClassKind
import org.jetbrains.kotlin.ir.builders.IrBuilderWithScope import org.jetbrains.kotlin.ir.builders.IrBuilderWithScope
import org.jetbrains.kotlin.ir.builders.declarations.addFunction
import org.jetbrains.kotlin.ir.builders.declarations.addValueParameter
import org.jetbrains.kotlin.ir.builders.irGet import org.jetbrains.kotlin.ir.builders.irGet
import org.jetbrains.kotlin.ir.builders.irInt import org.jetbrains.kotlin.ir.builders.irInt
import org.jetbrains.kotlin.ir.builders.irReturn import org.jetbrains.kotlin.ir.builders.irReturn
@@ -16,14 +18,14 @@ import org.jetbrains.kotlin.ir.expressions.IrExpression
import org.jetbrains.kotlin.ir.expressions.impl.IrConstructorCallImpl import org.jetbrains.kotlin.ir.expressions.impl.IrConstructorCallImpl
import org.jetbrains.kotlin.ir.expressions.impl.IrGetValueImpl import org.jetbrains.kotlin.ir.expressions.impl.IrGetValueImpl
import org.jetbrains.kotlin.ir.types.defaultType import org.jetbrains.kotlin.ir.types.defaultType
import org.jetbrains.kotlin.ir.types.starProjectedType
import org.jetbrains.kotlin.ir.types.typeWith
import org.jetbrains.kotlin.ir.util.* import org.jetbrains.kotlin.ir.util.*
import org.jetbrains.kotlin.name.ClassId import org.jetbrains.kotlin.name.ClassId
import org.jetbrains.kotlin.name.Name import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.resolve.descriptorUtil.fqNameSafe
import org.jetbrains.kotlinx.serialization.compiler.extensions.SerializationPluginContext import org.jetbrains.kotlinx.serialization.compiler.extensions.SerializationPluginContext
import org.jetbrains.kotlinx.serialization.compiler.resolve.SerialEntityNames import org.jetbrains.kotlinx.serialization.compiler.resolve.SerialEntityNames
import org.jetbrains.kotlinx.serialization.compiler.resolve.SerializationPackages import org.jetbrains.kotlinx.serialization.compiler.resolve.SerializationPackages
import org.jetbrains.kotlinx.serialization.compiler.resolve.needSerializerFactory
class SerializableCompanionIrGenerator( class SerializableCompanionIrGenerator(
val irClass: IrClass, val irClass: IrClass,
@@ -149,14 +151,31 @@ class SerializableCompanionIrGenerator(
generateSerializerFactoryIfNeeded(methodDescriptor) generateSerializerFactoryIfNeeded(methodDescriptor)
} }
private fun generateSerializerFactoryIfNeeded(getterDescriptor: IrSimpleFunction) { private fun getOrCreateSerializerVarargFactory(): IrSimpleFunction {
if (!irClass.needSerializerFactory(compilerContext)) return irClass.findDeclaration<IrSimpleFunction> {
val serialFactoryDescriptor = irClass.findDeclaration<IrSimpleFunction> {
it.valueParameters.size == 1 it.valueParameters.size == 1
&& it.valueParameters.first().isVararg && it.valueParameters.first().isVararg
&& it.returnType.isKSerializer() && it.returnType.isKSerializer()
&& it.isFromPlugin() && it.isFromPlugin()
} ?: return }?.let { return it }
val kSerializerStarType = compilerContext.getClassFromRuntime(SerialEntityNames.KSERIALIZER_CLASS).starProjectedType
val f = irClass.addFunction(
SerialEntityNames.SERIALIZER_PROVIDER_NAME.asString(),
kSerializerStarType,
origin = SERIALIZATION_PLUGIN_ORIGIN
)
f.addValueParameter {
name = Name.identifier("typeParamsSerializers")
varargElementType = kSerializerStarType
type = compilerContext.irBuiltIns.arrayClass.typeWith(kSerializerStarType)
origin = SERIALIZATION_PLUGIN_ORIGIN
}
return f
}
private fun generateSerializerFactoryIfNeeded(getterDescriptor: IrSimpleFunction) {
if (!irClass.needSerializerFactory(compilerContext)) return
val serialFactoryDescriptor = getOrCreateSerializerVarargFactory()
addFunctionBody(serialFactoryDescriptor) { factory -> addFunctionBody(serialFactoryDescriptor) { factory ->
val kSerializerStarType = factory.returnType val kSerializerStarType = factory.returnType
val array = factory.valueParameters.first() val array = factory.valueParameters.first()
@@ -83,6 +83,8 @@ object SerialEntityNames {
val GENERATED_SERIALIZER_CLASS = Name.identifier("GeneratedSerializer") val GENERATED_SERIALIZER_CLASS = Name.identifier("GeneratedSerializer")
val GENERATED_SERIALIZER_FQ = SerializationPackages.internalPackageFqName.child(GENERATED_SERIALIZER_CLASS) val GENERATED_SERIALIZER_FQ = SerializationPackages.internalPackageFqName.child(GENERATED_SERIALIZER_CLASS)
val SERIALIZER_FACTORY_INTERFACE_NAME = Name.identifier("SerializerFactory")
const val ENCODER_CLASS = "Encoder" const val ENCODER_CLASS = "Encoder"
const val STRUCTURE_ENCODER_CLASS = "CompositeEncoder" const val STRUCTURE_ENCODER_CLASS = "CompositeEncoder"
const val DECODER_CLASS = "Decoder" const val DECODER_CLASS = "Decoder"
@@ -10,6 +10,8 @@ dependencies {
compileOnly(project(":compiler:fir:tree")) compileOnly(project(":compiler:fir:tree"))
compileOnly(project(":compiler:fir:resolve")) compileOnly(project(":compiler:fir:resolve"))
compileOnly(project(":compiler:fir:entrypoint")) compileOnly(project(":compiler:fir:entrypoint"))
compileOnly(project(":js:js.config"))
compileOnly(project(":compiler:cli-common"))
implementation(project(":kotlinx-serialization-compiler-plugin.common")) implementation(project(":kotlinx-serialization-compiler-plugin.common"))
@@ -32,7 +32,10 @@ import org.jetbrains.kotlin.name.CallableId
import org.jetbrains.kotlin.name.ClassId 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.platform.js.isJs
import org.jetbrains.kotlin.platform.konan.isNative
import org.jetbrains.kotlinx.serialization.compiler.resolve.SerialEntityNames import org.jetbrains.kotlinx.serialization.compiler.resolve.SerialEntityNames
import org.jetbrains.kotlinx.serialization.compiler.resolve.SerialEntityNames.SERIALIZER_FACTORY_INTERFACE_NAME
import org.jetbrains.kotlinx.serialization.compiler.resolve.SerializationPackages import org.jetbrains.kotlinx.serialization.compiler.resolve.SerializationPackages
val generatedSerializerClassId = ClassId(SerializationPackages.internalPackageFqName, SerialEntityNames.GENERATED_SERIALIZER_CLASS) val generatedSerializerClassId = ClassId(SerializationPackages.internalPackageFqName, SerialEntityNames.GENERATED_SERIALIZER_CLASS)
@@ -321,6 +324,10 @@ class SerializationFirResolveExtension(session: FirSession) : FirDeclarationGene
name = SpecialNames.DEFAULT_NAME_FOR_COMPANION_OBJECT name = SpecialNames.DEFAULT_NAME_FOR_COMPANION_OBJECT
symbol = FirRegularClassSymbol(classId) symbol = FirRegularClassSymbol(classId)
superTypeRefs += session.builtinTypes.anyType superTypeRefs += session.builtinTypes.anyType
if (owner.companionNeedsSerializerFactory()) {
val serializerFactoryClassId = ClassId(SerializationPackages.internalPackageFqName, SERIALIZER_FACTORY_INTERFACE_NAME)
superTypeRefs += serializerFactoryClassId.constructClassLikeType(emptyArray(), false).toFirResolvedTypeRef()
}
} }
return regularClass.symbol return regularClass.symbol
} }
@@ -328,4 +335,14 @@ class SerializationFirResolveExtension(session: FirSession) : FirDeclarationGene
override fun FirDeclarationPredicateRegistrar.registerPredicates() { override fun FirDeclarationPredicateRegistrar.registerPredicates() {
register(FirSerializationPredicates.annotatedWithSerializable) register(FirSerializationPredicates.annotatedWithSerializable)
} }
private fun FirClassSymbol<*>.companionNeedsSerializerFactory(): Boolean {
if (!(moduleData.platform.isNative() || moduleData.platform.isJs())) return false
if (isSerializableObject) return true
if (isSerializableEnum) return true
if (isAbstractOrSealedSerializableClass) return true
if (isSealedSerializableInterface) return true
if (typeParameterSymbols.isEmpty()) return false
return true
}
} }
@@ -157,6 +157,9 @@ internal val FirClassSymbol<*>.isInternalSerializable: Boolean
return hasSerializableAnnotationWithoutArgs return hasSerializableAnnotationWithoutArgs
} }
internal val FirClassSymbol<*>.isAbstractOrSealedSerializableClass: Boolean
get() = isInternalSerializable && (rawStatus.modality == Modality.ABSTRACT || rawStatus.modality == Modality.SEALED)
internal val FirClassSymbol<*>.isInternallySerializableEnum: Boolean internal val FirClassSymbol<*>.isInternallySerializableEnum: Boolean
get() = classKind.isEnumClass && hasSerializableAnnotationWithoutArgs get() = classKind.isEnumClass && hasSerializableAnnotationWithoutArgs