Insert a runtime exception if star-projection type is encountered in serializer<T>() intrinsic

This makes behavior consistent with non-intrinsified version of this function and serializer(KType).

#KT-54878 Fixed
This commit is contained in:
Leonid Startsev
2022-11-10 12:09:57 +01:00
committed by Space Team
parent de5fd767d4
commit 715a730819
4 changed files with 121 additions and 34 deletions
@@ -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, "<init>", "(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;")
@@ -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<T: I>(val boxed: T)
inline fun <reified T: Any> getSer(): KSerializer<T> {
return serializer<T>()
}
inline fun <reified T: Any> getListSer(): KSerializer<List<T>> {
return serializer<List<T>>()
}
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<Box<*>>()
}
checkBlock("direct list") {
serializer<List<Box<*>>>()
}
checkBlock("getSer") {
getSer<Box<*>>()
}
checkBlock("getListSer") {
getListSer<Box<*>>()
}
return "OK"
}
@@ -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 {
@@ -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 {