From 62f67e46fda21e27d4965987f93167bab3aacd5f Mon Sep 17 00:00:00 2001 From: Leonid Startsev Date: Thu, 8 Sep 2022 16:53:45 +0200 Subject: [PATCH] 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. --- .../ir/SerializableCompanionIrGenerator.kt | 31 +++++++++++++++---- .../compiler/resolve/NamingConventions.kt | 2 ++ .../kotlinx-serialization.k2/build.gradle.kts | 2 ++ .../fir/SerializationFirResolveExtension.kt | 17 ++++++++++ .../compiler/fir/SerializationFirUtils.kt | 3 ++ 5 files changed, 49 insertions(+), 6 deletions(-) diff --git a/plugins/kotlinx-serialization/kotlinx-serialization.backend/src/org/jetbrains/kotlinx/serialization/compiler/backend/ir/SerializableCompanionIrGenerator.kt b/plugins/kotlinx-serialization/kotlinx-serialization.backend/src/org/jetbrains/kotlinx/serialization/compiler/backend/ir/SerializableCompanionIrGenerator.kt index e55a6562470..ff46b35f4ae 100644 --- a/plugins/kotlinx-serialization/kotlinx-serialization.backend/src/org/jetbrains/kotlinx/serialization/compiler/backend/ir/SerializableCompanionIrGenerator.kt +++ b/plugins/kotlinx-serialization/kotlinx-serialization.backend/src/org/jetbrains/kotlinx/serialization/compiler/backend/ir/SerializableCompanionIrGenerator.kt @@ -7,6 +7,8 @@ package org.jetbrains.kotlinx.serialization.compiler.backend.ir import org.jetbrains.kotlin.descriptors.ClassKind 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.irInt 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.IrGetValueImpl 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.name.ClassId 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.resolve.SerialEntityNames import org.jetbrains.kotlinx.serialization.compiler.resolve.SerializationPackages -import org.jetbrains.kotlinx.serialization.compiler.resolve.needSerializerFactory class SerializableCompanionIrGenerator( val irClass: IrClass, @@ -149,14 +151,31 @@ class SerializableCompanionIrGenerator( generateSerializerFactoryIfNeeded(methodDescriptor) } - private fun generateSerializerFactoryIfNeeded(getterDescriptor: IrSimpleFunction) { - if (!irClass.needSerializerFactory(compilerContext)) return - val serialFactoryDescriptor = irClass.findDeclaration { + private fun getOrCreateSerializerVarargFactory(): IrSimpleFunction { + irClass.findDeclaration { it.valueParameters.size == 1 && it.valueParameters.first().isVararg && it.returnType.isKSerializer() && 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 -> val kSerializerStarType = factory.returnType val array = factory.valueParameters.first() diff --git a/plugins/kotlinx-serialization/kotlinx-serialization.common/src/org/jetbrains/kotlinx/serialization/compiler/resolve/NamingConventions.kt b/plugins/kotlinx-serialization/kotlinx-serialization.common/src/org/jetbrains/kotlinx/serialization/compiler/resolve/NamingConventions.kt index 8ca5526b9df..bf47e60096a 100644 --- a/plugins/kotlinx-serialization/kotlinx-serialization.common/src/org/jetbrains/kotlinx/serialization/compiler/resolve/NamingConventions.kt +++ b/plugins/kotlinx-serialization/kotlinx-serialization.common/src/org/jetbrains/kotlinx/serialization/compiler/resolve/NamingConventions.kt @@ -83,6 +83,8 @@ object SerialEntityNames { val GENERATED_SERIALIZER_CLASS = Name.identifier("GeneratedSerializer") 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 STRUCTURE_ENCODER_CLASS = "CompositeEncoder" const val DECODER_CLASS = "Decoder" diff --git a/plugins/kotlinx-serialization/kotlinx-serialization.k2/build.gradle.kts b/plugins/kotlinx-serialization/kotlinx-serialization.k2/build.gradle.kts index 3fc8189fd3c..e8d586bf5e2 100644 --- a/plugins/kotlinx-serialization/kotlinx-serialization.k2/build.gradle.kts +++ b/plugins/kotlinx-serialization/kotlinx-serialization.k2/build.gradle.kts @@ -10,6 +10,8 @@ dependencies { compileOnly(project(":compiler:fir:tree")) compileOnly(project(":compiler:fir:resolve")) compileOnly(project(":compiler:fir:entrypoint")) + compileOnly(project(":js:js.config")) + compileOnly(project(":compiler:cli-common")) implementation(project(":kotlinx-serialization-compiler-plugin.common")) diff --git a/plugins/kotlinx-serialization/kotlinx-serialization.k2/src/org/jetbrains/kotlinx/serialization/compiler/fir/SerializationFirResolveExtension.kt b/plugins/kotlinx-serialization/kotlinx-serialization.k2/src/org/jetbrains/kotlinx/serialization/compiler/fir/SerializationFirResolveExtension.kt index a887778a714..c64f5ca4e7c 100644 --- a/plugins/kotlinx-serialization/kotlinx-serialization.k2/src/org/jetbrains/kotlinx/serialization/compiler/fir/SerializationFirResolveExtension.kt +++ b/plugins/kotlinx-serialization/kotlinx-serialization.k2/src/org/jetbrains/kotlinx/serialization/compiler/fir/SerializationFirResolveExtension.kt @@ -32,7 +32,10 @@ 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.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.SERIALIZER_FACTORY_INTERFACE_NAME import org.jetbrains.kotlinx.serialization.compiler.resolve.SerializationPackages 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 symbol = FirRegularClassSymbol(classId) 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 } @@ -328,4 +335,14 @@ class SerializationFirResolveExtension(session: FirSession) : FirDeclarationGene override fun FirDeclarationPredicateRegistrar.registerPredicates() { 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 + } } diff --git a/plugins/kotlinx-serialization/kotlinx-serialization.k2/src/org/jetbrains/kotlinx/serialization/compiler/fir/SerializationFirUtils.kt b/plugins/kotlinx-serialization/kotlinx-serialization.k2/src/org/jetbrains/kotlinx/serialization/compiler/fir/SerializationFirUtils.kt index 4387d096ba4..3591b7ff8e5 100644 --- a/plugins/kotlinx-serialization/kotlinx-serialization.k2/src/org/jetbrains/kotlinx/serialization/compiler/fir/SerializationFirUtils.kt +++ b/plugins/kotlinx-serialization/kotlinx-serialization.k2/src/org/jetbrains/kotlinx/serialization/compiler/fir/SerializationFirUtils.kt @@ -157,6 +157,9 @@ internal val FirClassSymbol<*>.isInternalSerializable: Boolean return hasSerializableAnnotationWithoutArgs } +internal val FirClassSymbol<*>.isAbstractOrSealedSerializableClass: Boolean + get() = isInternalSerializable && (rawStatus.modality == Modality.ABSTRACT || rawStatus.modality == Modality.SEALED) + internal val FirClassSymbol<*>.isInternallySerializableEnum: Boolean get() = classKind.isEnumClass && hasSerializableAnnotationWithoutArgs