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:
committed by
Space Team
parent
de5fd767d4
commit
715a730819
+58
-34
@@ -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"
|
||||
}
|
||||
+6
@@ -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 {
|
||||
|
||||
+6
@@ -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 {
|
||||
|
||||
Reference in New Issue
Block a user