From 715a73081938247578d3651bc89558dde7f9ab90 Mon Sep 17 00:00:00 2001 From: Leonid Startsev Date: Thu, 10 Nov 2022 12:09:57 +0100 Subject: [PATCH] Insert a runtime exception if star-projection type is encountered in serializer() intrinsic This makes behavior consistent with non-intrinsified version of this function and serializer(KType). #KT-54878 Fixed --- .../ir/SerializationJvmIrIntrinsicSupport.kt | 92 ++++++++++++------- .../boxIr/intrinsicsStarProjections.kt | 51 ++++++++++ ...SerializationFirBlackBoxTestGenerated.java | 6 ++ .../SerializationIrBoxTestGenerated.java | 6 ++ 4 files changed, 121 insertions(+), 34 deletions(-) create mode 100644 plugins/kotlinx-serialization/testData/boxIr/intrinsicsStarProjections.kt diff --git a/plugins/kotlinx-serialization/kotlinx-serialization.backend/src/org/jetbrains/kotlinx/serialization/compiler/backend/ir/SerializationJvmIrIntrinsicSupport.kt b/plugins/kotlinx-serialization/kotlinx-serialization.backend/src/org/jetbrains/kotlinx/serialization/compiler/backend/ir/SerializationJvmIrIntrinsicSupport.kt index c77605d683c..cdc1393dd62 100644 --- a/plugins/kotlinx-serialization/kotlinx-serialization.backend/src/org/jetbrains/kotlinx/serialization/compiler/backend/ir/SerializationJvmIrIntrinsicSupport.kt +++ b/plugins/kotlinx-serialization/kotlinx-serialization.backend/src/org/jetbrains/kotlinx/serialization/compiler/backend/ir/SerializationJvmIrIntrinsicSupport.kt @@ -64,7 +64,7 @@ class SerializationJvmIrIntrinsicSupport(val jvmBackendContext: JvmBackendContex class WithModule(val storedIndex: Int) : IntrinsicType(stubCallDescriptorWithModule) - fun magicMarkerString(): String = magicMarkerStringPrefix + when(this) { + fun magicMarkerString(): String = magicMarkerStringPrefix + when (this) { is Simple -> "simple" is WithModule -> "withModule" } @@ -242,45 +242,60 @@ class SerializationJvmIrIntrinsicSupport(val jvmBackendContext: JvmBackendContex return false } + private fun generateThrowOnStarProjection(parentType: IrSimpleType, adapter: InstructionAdapter) { + val iaeName = "java/lang/IllegalArgumentException" + with(adapter) { + anew(Type.getObjectType(iaeName)) + dup() + aconst("Star projections in type arguments are not allowed, but had ${parentType.render()}") + invokespecial(iaeName, "", "(Ljava/lang/String;)V", false) + checkcast(Type.getObjectType("java/lang/Throwable")) + athrow() + } + } + fun generateSerializerForType( type: IrType, adapter: InstructionAdapter, intrinsicType: IntrinsicType ) { - with(typeSystemContext) { - if (adapter.putReifyMarkerIfNeeded(type, intrinsicType)) return - val typeDescriptor: IrClass = type.classOrNull!!.owner + if (adapter.putReifyMarkerIfNeeded(type, intrinsicType)) return + val typeDescriptor: IrClass = type.classOrNull!!.owner - val support = this@SerializationJvmIrIntrinsicSupport + val support = this@SerializationJvmIrIntrinsicSupport - val serializerMethod = SerializableCompanionIrGenerator.getSerializerGetterFunction(typeDescriptor) - if (serializerMethod != null) { - // fast path - val companionType = if (typeDescriptor.isSerializableObject) typeDescriptor else typeDescriptor.companionObject()!! - support.instantiateObject(adapter, companionType.symbol) - val args = type.getArguments().map { it.getType() } - args.forEach { generateSerializerForType(it, adapter, intrinsicType) } - val signature = kSerializerType.descriptor.repeat(args.size) - adapter.invokevirtual( - typeMapper.mapClass(companionType).internalName, - "serializer", - "(${signature})${kSerializerType.descriptor}", - false - ) - } else { - // More general path, including special or built-in serializers for e.g. List - val serializer = support.findTypeSerializerOrContext(type) - support.stackValueSerializerInstance( - type, - serializer, - adapter, - intrinsicType - ) { genericArg -> - assert(putReifyMarkerIfNeeded(genericArg, intrinsicType)) + val serializerMethod = SerializableCompanionIrGenerator.getSerializerGetterFunction(typeDescriptor) + if (serializerMethod != null) { + // fast path + val companionType = if (typeDescriptor.isSerializableObject) typeDescriptor else typeDescriptor.companionObject()!! + support.instantiateObject(adapter, companionType.symbol) + val args = (type as IrSimpleType).arguments.map { + it.typeOrNull ?: run { + generateThrowOnStarProjection(type, adapter) + return } } - if (type.isMarkedNullable()) adapter.wrapStackValueIntoNullableSerializer() + args.forEach { generateSerializerForType(it, adapter, intrinsicType) } + val signature = kSerializerType.descriptor.repeat(args.size) + adapter.invokevirtual( + typeMapper.mapClass(companionType).internalName, + "serializer", + "(${signature})${kSerializerType.descriptor}", + false + ) + } else { + // More general path, including special or built-in serializers for e.g. List + val serializer = support.findTypeSerializerOrContext(type) + support.stackValueSerializerInstance( + type, + serializer, + adapter, + intrinsicType + ) { genericArg -> + assert(putReifyMarkerIfNeeded(genericArg, intrinsicType)) + } } + if (type.isMarkedNullable()) adapter.wrapStackValueIntoNullableSerializer() } private fun stackValueSerializerInstance( @@ -289,7 +304,7 @@ class SerializationJvmIrIntrinsicSupport(val jvmBackendContext: JvmBackendContex intrinsicType: IntrinsicType, genericIndex: Int? = null, genericSerializerFieldGetter: (InstructionAdapter.(IrType) -> Unit)? = null, - ): Boolean = with(typeSystemContext) { + ): Boolean { if (maybeSerializer == null && genericIndex != null) { // get field from serializer object genericSerializerFieldGetter?.invoke(iv, kType) @@ -313,9 +328,14 @@ class SerializationJvmIrIntrinsicSupport(val jvmBackendContext: JvmBackendContex return true } // serializer is not singleton object and shall be instantiated - val argSerializers = (kType as IrSimpleType).arguments.map { projection -> + val typeArgumentsAsTypes = (kType as IrSimpleType).arguments.map { + it.typeOrNull ?: run { + generateThrowOnStarProjection(kType, iv) + return false + } + } + val argSerializers = typeArgumentsAsTypes.map { argType -> // check if any type argument is not serializable - val argType = projection.typeOrNull!! val argSerializer = if (argType.isTypeParameter()) null else findTypeSerializerOrContext(argType) // check if it can be properly serialized with its args recursively @@ -434,6 +454,7 @@ class SerializationJvmIrIntrinsicSupport(val jvmBackendContext: JvmBackendContex checkcast(javaEnumArray) signature.append(javaEnumArray.descriptor) } + contextSerializerId, polymorphicSerializerId -> { // a special way to instantiate enum -- need a enum KClass reference // GENERIC_ARGUMENT forces boxing in order to obtain KClass @@ -455,14 +476,16 @@ class SerializationJvmIrIntrinsicSupport(val jvmBackendContext: JvmBackendContex signature.append(kSerializerArrayType.descriptor) } } + referenceArraySerializerId -> { // a special way to instantiate reference array serializer -- need an element KClass reference - aconst(typeMapper.mapTypeCommon(kType.getArguments().first().getType(), TypeMappingMode.GENERIC_ARGUMENT)) + aconst(typeMapper.mapTypeCommon(typeArgumentsAsTypes.first(), TypeMappingMode.GENERIC_ARGUMENT)) AsmUtil.wrapJavaClassIntoKClass(this) signature.append(AsmTypes.K_CLASS_TYPE.descriptor) // Reference array serializer still needs serializer for its argument type instantiate(argSerializers[0], signature) } + sealedSerializerId -> { aconst(serialName) signature.append("Ljava/lang/String;") @@ -504,6 +527,7 @@ class SerializationJvmIrIntrinsicSupport(val jvmBackendContext: JvmBackendContex } signature.append(kSerializerArrayType.descriptor) } + objectSerializerId -> { aconst(serialName) signature.append("Ljava/lang/String;") diff --git a/plugins/kotlinx-serialization/testData/boxIr/intrinsicsStarProjections.kt b/plugins/kotlinx-serialization/testData/boxIr/intrinsicsStarProjections.kt new file mode 100644 index 00000000000..34dac569ec5 --- /dev/null +++ b/plugins/kotlinx-serialization/testData/boxIr/intrinsicsStarProjections.kt @@ -0,0 +1,51 @@ +// IGNORE_BACKEND_K2: JVM_IR +// TARGET_BACKEND: JVM_IR + +// WITH_STDLIB + +import kotlinx.serialization.* +import kotlinx.serialization.json.* +import kotlinx.serialization.internal.* +import kotlinx.serialization.descriptors.* +import kotlinx.serialization.modules.* +import java.lang.AssertionError +import java.lang.IllegalArgumentException + +interface I + +@Serializable +data class Box(val boxed: T) + +inline fun getSer(): KSerializer { + return serializer() +} + +inline fun getListSer(): KSerializer> { + return serializer>() +} + +fun checkBlock(name: String, block:() -> Unit) { + try { + block() + } catch (e: IllegalArgumentException) { + if (!e.message!!.contains("Star projections in type arguments are not allowed")) throw e + return + } + throw AssertionError("Expected exception to be thrown in block $name") +} + +fun box(): String { + checkBlock("direct") { + serializer>() + } + checkBlock("direct list") { + serializer>>() + } + checkBlock("getSer") { + getSer>() + } + checkBlock("getListSer") { + getListSer>() + } + return "OK" +} \ No newline at end of file diff --git a/plugins/kotlinx-serialization/tests-gen/org/jetbrains/kotlinx/serialization/runners/SerializationFirBlackBoxTestGenerated.java b/plugins/kotlinx-serialization/tests-gen/org/jetbrains/kotlinx/serialization/runners/SerializationFirBlackBoxTestGenerated.java index f7d56efe080..7f7bc7e9d38 100644 --- a/plugins/kotlinx-serialization/tests-gen/org/jetbrains/kotlinx/serialization/runners/SerializationFirBlackBoxTestGenerated.java +++ b/plugins/kotlinx-serialization/tests-gen/org/jetbrains/kotlinx/serialization/runners/SerializationFirBlackBoxTestGenerated.java @@ -81,6 +81,12 @@ public class SerializationFirBlackBoxTestGenerated extends AbstractSerialization runTest("plugins/kotlinx-serialization/testData/boxIr/intrinsicsBox.kt"); } + @Test + @TestMetadata("intrinsicsStarProjections.kt") + public void testIntrinsicsStarProjections() throws Exception { + runTest("plugins/kotlinx-serialization/testData/boxIr/intrinsicsStarProjections.kt"); + } + @Test @TestMetadata("metaSerializable.kt") public void testMetaSerializable() throws Exception { diff --git a/plugins/kotlinx-serialization/tests-gen/org/jetbrains/kotlinx/serialization/runners/SerializationIrBoxTestGenerated.java b/plugins/kotlinx-serialization/tests-gen/org/jetbrains/kotlinx/serialization/runners/SerializationIrBoxTestGenerated.java index 1640cbfaf67..1dde0cf9edc 100644 --- a/plugins/kotlinx-serialization/tests-gen/org/jetbrains/kotlinx/serialization/runners/SerializationIrBoxTestGenerated.java +++ b/plugins/kotlinx-serialization/tests-gen/org/jetbrains/kotlinx/serialization/runners/SerializationIrBoxTestGenerated.java @@ -79,6 +79,12 @@ public class SerializationIrBoxTestGenerated extends AbstractSerializationIrBoxT runTest("plugins/kotlinx-serialization/testData/boxIr/intrinsicsBox.kt"); } + @Test + @TestMetadata("intrinsicsStarProjections.kt") + public void testIntrinsicsStarProjections() throws Exception { + runTest("plugins/kotlinx-serialization/testData/boxIr/intrinsicsStarProjections.kt"); + } + @Test @TestMetadata("metaSerializable.kt") public void testMetaSerializable() throws Exception {