From 137c500e3ad54fba489c3755424e291d2a1f7f8b Mon Sep 17 00:00:00 2001 From: Leonid Startsev Date: Fri, 20 Dec 2019 18:21:41 +0300 Subject: [PATCH] Support SerializerFactory in Native kotlinx.serialization plugin now adds additional function to companions of generic classes in order to overcome lack of reflection. This helps retrieving generic serializers from KType. --- .../backend/common/AbstractSerialGenerator.kt | 6 ++ .../backend/common/SerializerCodegen.kt | 3 - .../ir/SerializableCompanionIrGenerator.kt | 55 ++++++++++-- .../SerializationResolveExtension.kt | 1 + .../resolve/KSerializerDescriptorResolver.kt | 89 +++++++++++++++++-- 5 files changed, 133 insertions(+), 21 deletions(-) diff --git a/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/common/AbstractSerialGenerator.kt b/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/common/AbstractSerialGenerator.kt index b44546be349..76c46f666f4 100644 --- a/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/common/AbstractSerialGenerator.kt +++ b/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/common/AbstractSerialGenerator.kt @@ -7,12 +7,15 @@ package org.jetbrains.kotlinx.serialization.compiler.backend.common import org.jetbrains.kotlin.descriptors.ClassDescriptor import org.jetbrains.kotlin.descriptors.DeclarationDescriptor +import org.jetbrains.kotlin.descriptors.FunctionDescriptor import org.jetbrains.kotlin.js.translate.utils.AnnotationsUtils import org.jetbrains.kotlin.name.FqName +import org.jetbrains.kotlin.name.Name import org.jetbrains.kotlin.resolve.BindingContext import org.jetbrains.kotlin.resolve.constants.KClassValue import org.jetbrains.kotlin.resolve.descriptorUtil.firstArgument import org.jetbrains.kotlin.resolve.descriptorUtil.module +import org.jetbrains.kotlin.resolve.scopes.getDescriptorsFiltered import org.jetbrains.kotlin.types.KotlinType import org.jetbrains.kotlin.types.typeUtil.supertypes import org.jetbrains.kotlinx.serialization.compiler.resolve.SerializationAnnotations @@ -48,4 +51,7 @@ abstract class AbstractSerialGenerator(val bindingContext: BindingContext, val c { it.toClassDescriptor!! } ) } + + protected fun ClassDescriptor.getFuncDesc(funcName: String): Sequence = + unsubstitutedMemberScope.getDescriptorsFiltered { it == Name.identifier(funcName) }.asSequence().filterIsInstance() } \ No newline at end of file diff --git a/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/common/SerializerCodegen.kt b/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/common/SerializerCodegen.kt index d1e1fb1a04b..97033d447fa 100644 --- a/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/common/SerializerCodegen.kt +++ b/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/common/SerializerCodegen.kt @@ -148,9 +148,6 @@ abstract class SerializerCodegen( isReturnTypeOk(property) } - protected fun ClassDescriptor.getFuncDesc(funcName: String): Sequence = - unsubstitutedMemberScope.getDescriptorsFiltered { it == Name.identifier(funcName) }.asSequence().filterIsInstance() - companion object { fun getSyntheticLoadMember(serializerDescriptor: ClassDescriptor): FunctionDescriptor? = getMemberToGenerate( serializerDescriptor, SerialEntityNames.LOAD, diff --git a/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/ir/SerializableCompanionIrGenerator.kt b/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/ir/SerializableCompanionIrGenerator.kt index 0df8a4295e3..4d50e45260e 100644 --- a/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/ir/SerializableCompanionIrGenerator.kt +++ b/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/ir/SerializableCompanionIrGenerator.kt @@ -1,24 +1,26 @@ package org.jetbrains.kotlinx.serialization.compiler.backend.ir -import org.jetbrains.kotlin.backend.common.BackendContext -import org.jetbrains.kotlin.descriptors.ClassDescriptor -import org.jetbrains.kotlin.descriptors.ClassKind -import org.jetbrains.kotlin.descriptors.FunctionDescriptor -import org.jetbrains.kotlin.descriptors.findClassAcrossModuleDependencies +import org.jetbrains.kotlin.descriptors.* +import org.jetbrains.kotlin.incremental.components.NoLookupLocation import org.jetbrains.kotlin.ir.builders.IrBuilderWithScope import org.jetbrains.kotlin.ir.builders.irGet +import org.jetbrains.kotlin.ir.builders.irInt import org.jetbrains.kotlin.ir.builders.irReturn import org.jetbrains.kotlin.ir.declarations.IrClass import org.jetbrains.kotlin.ir.expressions.IrExpression import org.jetbrains.kotlin.ir.expressions.impl.IrConstructorCallImpl -import org.jetbrains.kotlin.ir.util.SymbolTable -import org.jetbrains.kotlin.ir.util.TypeTranslator -import org.jetbrains.kotlin.ir.util.defaultType +import org.jetbrains.kotlin.ir.expressions.impl.IrGetValueImpl +import org.jetbrains.kotlin.ir.types.impl.IrStarProjectionImpl +import org.jetbrains.kotlin.ir.types.impl.IrTypeProjectionImpl +import org.jetbrains.kotlin.ir.types.impl.makeTypeProjection import org.jetbrains.kotlin.ir.util.patchDeclarationParents +import org.jetbrains.kotlin.ir.util.referenceFunction import org.jetbrains.kotlin.name.ClassId import org.jetbrains.kotlin.name.Name import org.jetbrains.kotlin.resolve.BindingContext +import org.jetbrains.kotlin.resolve.calls.components.isVararg import org.jetbrains.kotlin.resolve.descriptorUtil.module +import org.jetbrains.kotlin.types.Variance import org.jetbrains.kotlinx.serialization.compiler.backend.common.SerializableCompanionCodegen import org.jetbrains.kotlinx.serialization.compiler.backend.common.findTypeSerializer import org.jetbrains.kotlinx.serialization.compiler.extensions.SerializationPluginContext @@ -77,7 +79,7 @@ class SerializableCompanionIrGenerator( irSerializableClass.annotations.add(annotationCtorCall) } - override fun generateSerializerGetter(methodDescriptor: FunctionDescriptor) = + override fun generateSerializerGetter(methodDescriptor: FunctionDescriptor) { irClass.contributeFunction(methodDescriptor) { getter -> val serializer = requireNotNull( findTypeSerializer( @@ -94,5 +96,40 @@ class SerializableCompanionIrGenerator( patchSerializableClassWithMarkerAnnotation(serializer) +irReturn(requireNotNull(expr)) } + generateSerializerFactoryIfNeeded(methodDescriptor) + } + + private fun generateSerializerFactoryIfNeeded(getterDescriptor: FunctionDescriptor) { + if (serializableDescriptor.declaredTypeParameters.isEmpty()) return + val serialFactoryDescriptor = companionDescriptor.unsubstitutedMemberScope.getContributedFunctions( + SerialEntityNames.SERIALIZER_PROVIDER_NAME, + NoLookupLocation.FROM_BACKEND + ).firstOrNull { + it.valueParameters.size == 1 + && it.valueParameters.first().isVararg + && it.kind == CallableMemberDescriptor.Kind.SYNTHESIZED + && it.returnType != null && isKSerializer(it.returnType) + } ?: return + irClass.contributeFunction(serialFactoryDescriptor) { factory -> + val array = factory.valueParameters.first() + val argsSize = serializableDescriptor.declaredTypeParameters.size + val arrayGet = + compilerContext.builtIns.array.getFuncDesc("get").single() + val arrayGetSymbol = compilerContext.symbolTable.referenceFunction(arrayGet) + + val outAnyNullable = makeTypeProjection(compilerContext.irBuiltIns.anyNType, Variance.OUT_VARIANCE).type + + val serializers: List = (0 until argsSize).map { irInvoke(irGet(array), arrayGetSymbol, irInt(it)) } + val serializerCall = compilerContext.symbolTable.referenceSimpleFunction(getterDescriptor) + val call = irInvoke( + IrGetValueImpl(startOffset, endOffset, factory.dispatchReceiverParameter!!.symbol), + serializerCall, + List(argsSize) { outAnyNullable }, + serializers + ) + +irReturn(call) + patchSerializableClassWithMarkerAnnotation(companionDescriptor) + } + } } \ No newline at end of file diff --git a/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/extensions/SerializationResolveExtension.kt b/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/extensions/SerializationResolveExtension.kt index f627fa879e3..238c843fc6b 100644 --- a/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/extensions/SerializationResolveExtension.kt +++ b/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/extensions/SerializationResolveExtension.kt @@ -72,6 +72,7 @@ open class SerializationResolveExtension : SyntheticResolveExtension { override fun addSyntheticSupertypes(thisDescriptor: ClassDescriptor, supertypes: MutableList) { KSerializerDescriptorResolver.addSerialInfoSuperType(thisDescriptor, supertypes) KSerializerDescriptorResolver.addSerializerSupertypes(thisDescriptor, supertypes) + KSerializerDescriptorResolver.addSerializerFactorySuperType(thisDescriptor, supertypes) } override fun generateSyntheticSecondaryConstructors( diff --git a/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/resolve/KSerializerDescriptorResolver.kt b/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/resolve/KSerializerDescriptorResolver.kt index ddf7f0386ce..0bc31675b79 100644 --- a/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/resolve/KSerializerDescriptorResolver.kt +++ b/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/resolve/KSerializerDescriptorResolver.kt @@ -27,18 +27,20 @@ import org.jetbrains.kotlin.incremental.components.NoLookupLocation import org.jetbrains.kotlin.name.ClassId import org.jetbrains.kotlin.name.FqName import org.jetbrains.kotlin.name.Name +import org.jetbrains.kotlin.platform.js.isJs +import org.jetbrains.kotlin.platform.jvm.isJvm +import org.jetbrains.kotlin.platform.konan.isNative import org.jetbrains.kotlin.psi.synthetics.SyntheticClassOrObjectDescriptor import org.jetbrains.kotlin.resolve.BindingContext +import org.jetbrains.kotlin.resolve.calls.components.isVararg import org.jetbrains.kotlin.resolve.descriptorUtil.builtIns import org.jetbrains.kotlin.resolve.descriptorUtil.module +import org.jetbrains.kotlin.resolve.descriptorUtil.platform import org.jetbrains.kotlin.resolve.lazy.LazyClassContext import org.jetbrains.kotlin.resolve.lazy.declarations.ClassMemberDeclarationProvider import org.jetbrains.kotlin.resolve.lazy.descriptors.LazyClassDescriptor import org.jetbrains.kotlin.storage.LockBasedStorageManager -import org.jetbrains.kotlin.types.KotlinType -import org.jetbrains.kotlin.types.KotlinTypeFactory -import org.jetbrains.kotlin.types.TypeProjectionImpl -import org.jetbrains.kotlin.types.Variance +import org.jetbrains.kotlin.types.* import org.jetbrains.kotlin.types.typeUtil.createProjection import org.jetbrains.kotlin.types.typeUtil.makeNullable import org.jetbrains.kotlinx.serialization.compiler.backend.ir.SimpleSyntheticPropertyDescriptor @@ -50,14 +52,17 @@ import java.util.* object KSerializerDescriptorResolver { - private fun createDeprecatedHiddenAnnotation(module: ModuleDescriptor): AnnotationDescriptor { - return module.builtIns.createDeprecatedAnnotation("This synthesized declaration should not be used directly", level = "HIDDEN") + fun createDeprecatedHiddenAnnotation(module: ModuleDescriptor): AnnotationDescriptor { + return module.builtIns.createDeprecatedAnnotation( + "This synthesized declaration should not be used directly", + level = "HIDDEN" + ) } fun isSerialInfoImpl(thisDescriptor: ClassDescriptor): Boolean { return thisDescriptor.name == IMPL_NAME - && thisDescriptor.containingDeclaration is LazyClassDescriptor - && thisDescriptor.containingDeclaration.annotations.hasAnnotation(serialInfoFqName) + && thisDescriptor.containingDeclaration is LazyClassDescriptor + && thisDescriptor.containingDeclaration.annotations.hasAnnotation(serialInfoFqName) } fun addSerialInfoSuperType(thisDescriptor: ClassDescriptor, supertypes: MutableList) { @@ -66,6 +71,20 @@ object KSerializerDescriptorResolver { } } + private fun ClassDescriptor.needSerializerFactory(): Boolean { + if (this.platform?.isNative() != true) return false + val serializableClass = getSerializableClassDescriptorByCompanion(this) ?: return false + if (serializableClass.declaredTypeParameters.isEmpty()) return false + return true + } + + fun addSerializerFactorySuperType(classDescriptor: ClassDescriptor, supertypes: MutableList) { + if (!classDescriptor.needSerializerFactory()) return + val serializerFactoryClass = + classDescriptor.module.getClassFromInternalSerializationPackage("SerializerFactory") + supertypes.add(KotlinTypeFactory.simpleNotNullType(Annotations.EMPTY, serializerFactoryClass, listOf())) + } + fun addSerializerSupertypes(classDescriptor: ClassDescriptor, supertypes: MutableList) { val serializableClassDescriptor = getSerializableClassDescriptorBySerializer(classDescriptor) ?: return if (supertypes.any(::isKSerializer)) return @@ -178,6 +197,10 @@ object KSerializerDescriptorResolver { if (name == SerialEntityNames.SERIALIZER_PROVIDER_NAME && result.none { it.valueParameters.size == classDescriptor.declaredTypeParameters.size }) { result.add(createSerializerGetterDescriptor(thisDescriptor, classDescriptor)) } + + if (thisDescriptor.needSerializerFactory() && name == SerialEntityNames.SERIALIZER_PROVIDER_NAME && result.none { it.valueParameters.size == 1 && it.valueParameters.first().isVararg }) { + result.add(createSerializerFactoryVarargDescriptor(thisDescriptor)) + } } fun generateSerializerMethods( @@ -408,7 +431,55 @@ object KSerializerDescriptorResolver { return typeArgs to args } - private fun createSerializerGetterDescriptor(thisClass: ClassDescriptor, serializableClass: ClassDescriptor): SimpleFunctionDescriptor { + private fun createSerializerFactoryVarargDescriptor(thisClass: ClassDescriptor): SimpleFunctionDescriptor { + val f = SimpleFunctionDescriptorImpl.create( + thisClass, + Annotations.EMPTY, + SerialEntityNames.SERIALIZER_PROVIDER_NAME, + CallableMemberDescriptor.Kind.SYNTHESIZED, + thisClass.source + ) + val serializerClass = thisClass.getClassFromSerializationPackage(SerialEntityNames.KSERIALIZER_CLASS) + + val kSerializerStarType = + KotlinTypeFactory.simpleNotNullType( + Annotations.EMPTY, + serializerClass, + listOf(StarProjectionImpl(serializerClass.typeConstructor.parameters.first())) + ) + + val varargType = thisClass.builtIns.getArrayType(Variance.OUT_VARIANCE, kSerializerStarType) + + val vararg = ValueParameterDescriptorImpl( + containingDeclaration = f, + original = null, + index = 0, + annotations = Annotations.EMPTY, + name = Name.identifier("typeParamsSerializers"), + outType = varargType, + declaresDefaultValue = false, + isCrossinline = false, + isNoinline = false, + varargElementType = serializerClass.defaultType, + source = f.source + ) + + f.initialize( + null, + thisClass.thisAsReceiverParameter, + listOf(), + listOf(vararg), + kSerializerStarType, + Modality.FINAL, + Visibilities.PUBLIC + ) + return f + } + + private fun createSerializerGetterDescriptor( + thisClass: ClassDescriptor, + serializableClass: ClassDescriptor + ): SimpleFunctionDescriptor { val f = SimpleFunctionDescriptorImpl.create( thisClass, Annotations.EMPTY,