From 441b22c0fd2397a7612fb73724a00d80e8dbbea8 Mon Sep 17 00:00:00 2001 From: "Sergey.Shanshin" Date: Fri, 9 Jun 2023 15:32:28 +0000 Subject: [PATCH] [KxSerialization] Fixed error accessing the descriptor from companion Relates #KT-57647 If a serialization descriptor is used in the companion, then when accessing the child elements, an array of cached child serializers may be read. Since the serialization plugin adds its declarations to the class after the declarations from the source code, the initialization of the array of child serializers occurs after the execution of the user code. This can lead to N PE errors when trying to access an unfilled cached child serializer. The solution is to change the order of declarations so that the declaration with the property of cached child serializers comes first. Merge-request: KT-MR-10545 Merged-by: Sergey Shanshin --- .../compiler/backend/ir/BaseIrGenerator.kt | 10 +++++++++- .../kotlinx-serialization/testData/boxIr/caching.kt | 13 +++++++++++++ 2 files changed, 22 insertions(+), 1 deletion(-) diff --git a/plugins/kotlinx-serialization/kotlinx-serialization.backend/src/org/jetbrains/kotlinx/serialization/compiler/backend/ir/BaseIrGenerator.kt b/plugins/kotlinx-serialization/kotlinx-serialization.backend/src/org/jetbrains/kotlinx/serialization/compiler/backend/ir/BaseIrGenerator.kt index 59334939952..45c8f839e49 100644 --- a/plugins/kotlinx-serialization/kotlinx-serialization.backend/src/org/jetbrains/kotlinx/serialization/compiler/backend/ir/BaseIrGenerator.kt +++ b/plugins/kotlinx-serialization/kotlinx-serialization.backend/src/org/jetbrains/kotlinx/serialization/compiler/backend/ir/BaseIrGenerator.kt @@ -398,9 +398,17 @@ abstract class BaseIrGenerator(private val currentClass: IrClass, final override val kSerializerType = kSerializerClass.typeWith(compilerContext.irBuiltIns.anyType) val arrayType = compilerContext.irBuiltIns.arrayClass.typeWith(kSerializerType) - return addValPropertyWithJvmFieldInitializer(arrayType, SerialEntityNames.CACHED_CHILD_SERIALIZERS_PROPERTY_NAME) { + val property = addValPropertyWithJvmFieldInitializer(arrayType, SerialEntityNames.CACHED_CHILD_SERIALIZERS_PROPERTY_NAME) { createArrayOfExpression(kSerializerType, cacheableSerializers.map { it ?: irNull() }) } + + if (declarations.removeIf { declaration -> declaration === property }) { + // adding the property very first because children can be used even in first constructor + declarations.add(0, property) + } + + + return property } /** diff --git a/plugins/kotlinx-serialization/testData/boxIr/caching.kt b/plugins/kotlinx-serialization/testData/boxIr/caching.kt index 04c0e469222..bac772946eb 100644 --- a/plugins/kotlinx-serialization/testData/boxIr/caching.kt +++ b/plugins/kotlinx-serialization/testData/boxIr/caching.kt @@ -32,6 +32,18 @@ sealed class SealedListGeneric { var holder = GenericHolder>>() } +@Serializable +class AccessFromCompanion(@Contextual val category: Any) { + companion object { + init { + serializer().descriptor.getElementDescriptor(0).isNullable + } + var isNullable = serializer().descriptor.getElementDescriptor(0).isNullable + val isNullable2 = serializer().descriptor.getElementDescriptor(0).isNullable + } +} + + fun box(): String { // A correctly cached class must be initialized correctly in order to exclude cyclic nesting of caches Plain.serializer() @@ -40,6 +52,7 @@ fun box(): String { Abstract.serializer() SealedGeneric.serializer() SealedListGeneric.serializer() + AccessFromCompanion("any") return "OK" }