Call a specified function in intrinsic if polymorphic serializer is provided for interface.
This prioritizes module contents over default polymorphic serializer. See https://github.com/Kotlin/kotlinx.serialization/issues/2060 and https://github.com/Kotlin/kotlinx.serialization/pull/2565
This commit is contained in:
committed by
Space Team
parent
c8f84a74b9
commit
7c8c65d291
+68
-24
@@ -27,7 +27,10 @@ import org.jetbrains.kotlin.ir.symbols.IrTypeParameterSymbol
|
||||
import org.jetbrains.kotlin.ir.types.*
|
||||
import org.jetbrains.kotlin.ir.util.*
|
||||
import org.jetbrains.kotlin.load.kotlin.TypeMappingMode
|
||||
import org.jetbrains.kotlin.name.CallableId
|
||||
import org.jetbrains.kotlin.name.ClassId
|
||||
import org.jetbrains.kotlin.name.FqName
|
||||
import org.jetbrains.kotlin.name.Name
|
||||
import org.jetbrains.kotlin.resolve.jvm.AsmTypes
|
||||
import org.jetbrains.kotlinx.serialization.compiler.backend.jvm.*
|
||||
import org.jetbrains.kotlinx.serialization.compiler.backend.jvm.annotationArrayType
|
||||
@@ -113,6 +116,7 @@ class SerializationJvmIrIntrinsicSupport(
|
||||
const val serializersKtInternalName = "kotlinx/serialization/SerializersKt"
|
||||
const val callMethodName = "serializer"
|
||||
const val noCompiledSerializerMethodName = "noCompiledSerializer"
|
||||
const val moduleOverPolymorphicName = "moduleThenPolymorphic"
|
||||
|
||||
const val magicMarkerStringPrefix = "kotlinx.serialization.serializer."
|
||||
|
||||
@@ -164,6 +168,11 @@ class SerializationJvmIrIntrinsicSupport(
|
||||
private val hasNewContextSerializerSignature: Boolean
|
||||
get() = currentVersion != null && currentVersion!! >= ApiVersion.parse("1.2.0")!!
|
||||
|
||||
private val useModuleOverContextualForInterfaces: Boolean by lazy {
|
||||
irPluginContext.referenceFunctions(CallableId(FqName("kotlinx.serialization"), Name.identifier(moduleOverPolymorphicName)))
|
||||
.isNotEmpty()
|
||||
}
|
||||
|
||||
private fun findTypeSerializerOrContext(argType: IrType): IrClassSymbol? =
|
||||
emptyGenerator.findTypeSerializerOrContextUnchecked(this, argType, useTypeAnnotations = false)
|
||||
|
||||
@@ -320,6 +329,63 @@ class SerializationJvmIrIntrinsicSupport(
|
||||
if (type.isMarkedNullable()) adapter.wrapStackValueIntoNullableSerializer()
|
||||
}
|
||||
|
||||
private fun InstructionAdapter.insertNoCompiledSerializerCall(
|
||||
kType: IrType,
|
||||
argSerializers: List<Pair<IrType, IrClassSymbol?>>,
|
||||
intrinsicType: IntrinsicType,
|
||||
): Boolean {
|
||||
require(intrinsicType is IntrinsicType.WithModule) // SIMPLE is covered in previous if
|
||||
// SerializersModule
|
||||
load(intrinsicType.storedIndex, serializersModuleType)
|
||||
// KClass
|
||||
aconst(typeMapper.mapTypeCommon(kType, TypeMappingMode.GENERIC_ARGUMENT))
|
||||
AsmUtil.wrapJavaClassIntoKClass(this)
|
||||
|
||||
val descriptor = StringBuilder("(${serializersModuleType.descriptor}${AsmTypes.K_CLASS_TYPE.descriptor}")
|
||||
// Generic args (if present)
|
||||
if (argSerializers.isNotEmpty()) {
|
||||
fillArray(kSerializerType, argSerializers) { _, (type, _) ->
|
||||
generateSerializerForType(type, this, intrinsicType)
|
||||
}
|
||||
descriptor.append(kSerializerArrayType.descriptor)
|
||||
}
|
||||
descriptor.append(")${kSerializerType.descriptor}")
|
||||
invokestatic(
|
||||
serializersKtInternalName,
|
||||
noCompiledSerializerMethodName,
|
||||
descriptor.toString(),
|
||||
false
|
||||
)
|
||||
return false
|
||||
}
|
||||
|
||||
private fun InstructionAdapter.moduleOverPolymorphic(serializer: IrClassSymbol, kType: IrType, intrinsicType: IntrinsicType, argSerializers: List<Pair<IrType, IrClassSymbol?>>): Boolean {
|
||||
if (serializer.owner.classId == polymorphicSerializerId && kType.isInterface() && intrinsicType is IntrinsicType.WithModule && useModuleOverContextualForInterfaces) {
|
||||
load(intrinsicType.storedIndex, serializersModuleType)
|
||||
// KClass
|
||||
aconst(typeMapper.mapTypeCommon(kType, TypeMappingMode.GENERIC_ARGUMENT))
|
||||
AsmUtil.wrapJavaClassIntoKClass(this)
|
||||
|
||||
val descriptor = StringBuilder("(${serializersModuleType.descriptor}${AsmTypes.K_CLASS_TYPE.descriptor}")
|
||||
// Generic args (if present)
|
||||
if (argSerializers.isNotEmpty()) {
|
||||
fillArray(kSerializerType, argSerializers) { _, (type, _) ->
|
||||
generateSerializerForType(type, this, intrinsicType)
|
||||
}
|
||||
descriptor.append(kSerializerArrayType.descriptor)
|
||||
}
|
||||
descriptor.append(")${kSerializerType.descriptor}")
|
||||
invokestatic(
|
||||
serializersKtInternalName,
|
||||
moduleOverPolymorphicName,
|
||||
descriptor.toString(),
|
||||
false
|
||||
)
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
private fun stackValueSerializerInstance(
|
||||
kType: IrType, maybeSerializer: IrClassSymbol?,
|
||||
iv: InstructionAdapter,
|
||||
@@ -376,31 +442,9 @@ class SerializationJvmIrIntrinsicSupport(
|
||||
signature?.append(kSerializerType.descriptor)
|
||||
}
|
||||
|
||||
val serializer = maybeSerializer ?: iv.run {// insert noCompilerSerializer(module, kClass, arguments)
|
||||
require(intrinsicType is IntrinsicType.WithModule) // SIMPLE is covered in previous if
|
||||
// SerializersModule
|
||||
load(intrinsicType.storedIndex, serializersModuleType)
|
||||
// KClass
|
||||
aconst(typeMapper.mapTypeCommon(kType, TypeMappingMode.GENERIC_ARGUMENT))
|
||||
AsmUtil.wrapJavaClassIntoKClass(this)
|
||||
val serializer = maybeSerializer ?: return iv.insertNoCompiledSerializerCall(kType, argSerializers, intrinsicType)
|
||||
|
||||
val descriptor = StringBuilder("(${serializersModuleType.descriptor}${AsmTypes.K_CLASS_TYPE.descriptor}")
|
||||
// Generic args (if present)
|
||||
if (argSerializers.isNotEmpty()) {
|
||||
fillArray(kSerializerType, argSerializers) { _, (type, _) ->
|
||||
generateSerializerForType(type, this, intrinsicType)
|
||||
}
|
||||
descriptor.append(kSerializerArrayType.descriptor)
|
||||
}
|
||||
descriptor.append(")${kSerializerType.descriptor}")
|
||||
invokestatic(
|
||||
serializersKtInternalName,
|
||||
noCompiledSerializerMethodName,
|
||||
descriptor.toString(),
|
||||
false
|
||||
)
|
||||
return false
|
||||
}
|
||||
if (iv.moduleOverPolymorphic(serializer, kType, intrinsicType, argSerializers)) return true
|
||||
|
||||
// new serializer if needed
|
||||
iv.apply {
|
||||
|
||||
+83
@@ -0,0 +1,83 @@
|
||||
// TARGET_BACKEND: JVM_IR
|
||||
|
||||
// WITH_STDLIB
|
||||
|
||||
// FILE: stub.kt
|
||||
|
||||
@file:JvmName("SerializersKt")
|
||||
|
||||
package kotlinx.serialization
|
||||
|
||||
import kotlin.reflect.KClass
|
||||
import kotlinx.serialization.modules.*
|
||||
|
||||
// Copy of runtime function from kotlinx-serialization 1.7.0
|
||||
fun moduleThenPolymorphic(module: SerializersModule, kClass: KClass<*>): KSerializer<*> {
|
||||
return module.getContextual(kClass) ?: PolymorphicSerializer(kClass)
|
||||
}
|
||||
|
||||
fun moduleThenPolymorphic(module: SerializersModule, kClass: KClass<*>, argSerializers: Array<KSerializer<*>>): KSerializer<*> {
|
||||
return module.getContextual(kClass, argSerializers.asList()) ?: PolymorphicSerializer(kClass)
|
||||
}
|
||||
|
||||
// FILE: test.kt
|
||||
|
||||
package a
|
||||
|
||||
import kotlinx.serialization.*
|
||||
import kotlinx.serialization.descriptors.*
|
||||
import kotlinx.serialization.encoding.*
|
||||
import kotlinx.serialization.modules.*
|
||||
import kotlin.reflect.KClass
|
||||
import kotlin.test.*
|
||||
|
||||
interface IApiError {
|
||||
val code: Int
|
||||
}
|
||||
|
||||
object MyApiErrorSerializer : KSerializer<IApiError> {
|
||||
override val descriptor: SerialDescriptor = PrimitiveSerialDescriptor("IApiError", PrimitiveKind.INT)
|
||||
|
||||
override fun serialize(encoder: Encoder, value: IApiError) {
|
||||
TODO()
|
||||
}
|
||||
|
||||
override fun deserialize(decoder: Decoder): IApiError {
|
||||
TODO()
|
||||
}
|
||||
}
|
||||
|
||||
interface Parametrized<T> {
|
||||
val param: List<T>
|
||||
}
|
||||
|
||||
class PSer<T>(val tSer: KSerializer<T>) : KSerializer<Parametrized<T>> {
|
||||
override val descriptor: SerialDescriptor
|
||||
get() = buildClassSerialDescriptor("PSer<${tSer.descriptor.serialName}>")
|
||||
|
||||
override fun serialize(encoder: Encoder, value: Parametrized<T>) {
|
||||
TODO("Not yet implemented")
|
||||
}
|
||||
|
||||
override fun deserialize(decoder: Decoder): Parametrized<T> {
|
||||
TODO("Not yet implemented")
|
||||
}
|
||||
}
|
||||
|
||||
fun testParametrized() {
|
||||
val md = SerializersModule {
|
||||
contextual(Parametrized::class) { PSer(it[0]) }
|
||||
}
|
||||
assertEquals("PSer<kotlin.String>", md.serializer<Parametrized<String>>().descriptor.serialName)
|
||||
}
|
||||
|
||||
fun box(): String {
|
||||
val module = serializersModuleOf(IApiError::class, MyApiErrorSerializer)
|
||||
assertSame(MyApiErrorSerializer, module.serializer<IApiError>() as KSerializer<IApiError>)
|
||||
assertEquals(
|
||||
MyApiErrorSerializer.descriptor,
|
||||
module.serializer<List<IApiError>>().descriptor.elementDescriptors.first()
|
||||
)
|
||||
testParametrized()
|
||||
return "OK"
|
||||
}
|
||||
+6
@@ -189,6 +189,12 @@ public class SerializationFirLightTreeBlackBoxTestGenerated extends AbstractSeri
|
||||
runTest("plugins/kotlinx-serialization/testData/boxIr/intrinsicsNullable.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("intrinsicsPolymorphicPriority.kt")
|
||||
public void testIntrinsicsPolymorphicPriority() {
|
||||
runTest("plugins/kotlinx-serialization/testData/boxIr/intrinsicsPolymorphicPriority.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("intrinsicsStarProjections.kt")
|
||||
public void testIntrinsicsStarProjections() {
|
||||
|
||||
+6
@@ -186,6 +186,12 @@ public class SerializationIrBoxTestGenerated extends AbstractSerializationIrBoxT
|
||||
runTest("plugins/kotlinx-serialization/testData/boxIr/intrinsicsNullable.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("intrinsicsPolymorphicPriority.kt")
|
||||
public void testIntrinsicsPolymorphicPriority() {
|
||||
runTest("plugins/kotlinx-serialization/testData/boxIr/intrinsicsPolymorphicPriority.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("intrinsicsStarProjections.kt")
|
||||
public void testIntrinsicsStarProjections() {
|
||||
|
||||
Reference in New Issue
Block a user