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 e5f3914c231..80697d6f3c8 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 @@ -14,7 +14,10 @@ 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.types.KotlinType +import org.jetbrains.kotlin.types.typeUtil.supertypes import org.jetbrains.kotlinx.serialization.compiler.resolve.SerializationAnnotations +import org.jetbrains.kotlinx.serialization.compiler.resolve.isKSerializer +import org.jetbrains.kotlinx.serialization.compiler.resolve.toClassDescriptor abstract class AbstractSerialGenerator(val bindingContext: BindingContext, val currentDeclaration: ClassDescriptor) { @@ -34,4 +37,15 @@ abstract class AbstractSerialGenerator(val bindingContext: BindingContext, val c currentDeclaration ).toSet() } + + val additionalSerializersInScopeOfCurrentFile: Map by lazy { + getKClassListFromFileAnnotation(SerializationAnnotations.additionalSerializersFqName, currentDeclaration) + .associateBy( + { + it.supertypes().find(::isKSerializer)?.arguments?.firstOrNull()?.type + ?: throw AssertionError("Argument for ${SerializationAnnotations.additionalSerializersFqName} does not implement KSerializer") + }, + { it.toClassDescriptor!! } + ) + } } \ No newline at end of file diff --git a/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/common/TypeUtil.kt b/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/common/TypeUtil.kt index 830796a57ca..84df00669e9 100644 --- a/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/common/TypeUtil.kt +++ b/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/common/TypeUtil.kt @@ -49,14 +49,18 @@ open class SerialTypeInfo( val unit: Boolean = false ) +fun AbstractSerialGenerator.findAddOnSerializer(propertyType: KotlinType): ClassDescriptor? = + additionalSerializersInScopeOfCurrentFile[propertyType] + @Suppress("FunctionName", "LocalVariableName") fun AbstractSerialGenerator.getSerialTypeInfo(property: SerializableProperty): SerialTypeInfo { fun SerializableInfo(serializer: ClassDescriptor?) = SerialTypeInfo(property, if (property.type.isMarkedNullable) "Nullable" else "", serializer) val T = property.type - T.overridenSerializer?.toClassDescriptor?.let { return SerializableInfo(it) } property.serializableWith?.toClassDescriptor?.let { return SerializableInfo(it) } + T.overridenSerializer?.toClassDescriptor?.let { return SerializableInfo(it) } + findAddOnSerializer(T)?.let { return SerializableInfo(it) } return when { T.isTypeParameter() -> SerialTypeInfo(property, if (property.type.isMarkedNullable) "Nullable" else "", null) T.isPrimitiveNumberType() or T.isBoolean() -> SerialTypeInfo( @@ -91,6 +95,7 @@ fun AbstractSerialGenerator.findTypeSerializerOrContext( annotations: Annotations = kType.annotations, sourceElement: PsiElement? = null ): ClassDescriptor? { + additionalSerializersInScopeOfCurrentFile[kType]?.let { return it } if (kType.isTypeParameter()) return null fun getContextualSerializer() = if (annotations.hasAnnotation(SerializationAnnotations.contextualFqName) || kType in contextualKClassListInCurrentFile) diff --git a/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/jvm/JVMCodegenUtil.kt b/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/jvm/JVMCodegenUtil.kt index f363b3fe1d7..e806c27abe7 100644 --- a/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/jvm/JVMCodegenUtil.kt +++ b/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/jvm/JVMCodegenUtil.kt @@ -31,6 +31,7 @@ import org.jetbrains.kotlin.types.KotlinType import org.jetbrains.kotlin.types.typeUtil.isTypeParameter import org.jetbrains.kotlinx.serialization.compiler.backend.common.AbstractSerialGenerator import org.jetbrains.kotlinx.serialization.compiler.backend.common.SerialTypeInfo +import org.jetbrains.kotlinx.serialization.compiler.backend.common.findAddOnSerializer import org.jetbrains.kotlinx.serialization.compiler.backend.common.findTypeSerializerOrContext import org.jetbrains.kotlinx.serialization.compiler.resolve.* import org.jetbrains.kotlinx.serialization.compiler.resolve.SerialEntityNames.DECODER_CLASS @@ -109,6 +110,7 @@ fun InstructionAdapter.genKOutputMethodCall( ownerVar, classCodegen.state ) + StackValue.coerce(propertyType, sti.type, this) invokeinterface( kOutputType.internalName, CallingConventions.encode + sti.elementMethodPrefix + (if (useSerializer) "Serializable" else "") + CallingConventions.elementPostfix, @@ -309,6 +311,18 @@ class JVMSerialTypeInfo( ) : SerialTypeInfo(property, nn, serializer, unit) fun AbstractSerialGenerator.getSerialTypeInfo(property: SerializableProperty, type: Type): JVMSerialTypeInfo { + fun SerializableInfo(serializer: ClassDescriptor?) = + JVMSerialTypeInfo( + property, + Type.getType("Ljava/lang/Object;"), + if (property.type.isMarkedNullable) "Nullable" else "", + serializer + ) + + property.serializableWith?.toClassDescriptor?.let { return SerializableInfo(it) } + property.type.overridenSerializer?.toClassDescriptor?.let { return SerializableInfo(it) } + findAddOnSerializer(property.type)?.let { return SerializableInfo(it) } + if (property.type.isTypeParameter()) return JVMSerialTypeInfo( property, Type.getType("Ljava/lang/Object;"), diff --git a/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/jvm/SerializerCodegenImpl.kt b/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/jvm/SerializerCodegenImpl.kt index 8dfc1261235..7f9e367e1b3 100644 --- a/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/jvm/SerializerCodegenImpl.kt +++ b/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/jvm/SerializerCodegenImpl.kt @@ -385,6 +385,7 @@ class SerializerCodegenImpl( genValidateProperty(index, ::bitMaskOff) ificmpeq(readLabel) load(propVar, propertyType) + StackValue.coerce(propertyType, sti.type, this) produceCall(true) goTo(endL) visitLabel(readLabel) diff --git a/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/resolve/NamingConventions.kt b/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/resolve/NamingConventions.kt index 00482b96c8c..a5bcea3ede0 100644 --- a/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/resolve/NamingConventions.kt +++ b/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/resolve/NamingConventions.kt @@ -22,6 +22,7 @@ object SerializationAnnotations { internal val serialInfoFqName = FqName("kotlinx.serialization.SerialInfo") internal val contextualFqName = FqName("kotlinx.serialization.ContextualSerialization") + internal val additionalSerializersFqName = FqName("kotlinx.serialization.UseSerializers") } object SerialEntityNames {