diff --git a/plugins/kotlinx-serialization/kotlinx-serialization.backend/src/org/jetbrains/kotlinx/serialization/compiler/backend/ir/SerializableIrGenerator.kt b/plugins/kotlinx-serialization/kotlinx-serialization.backend/src/org/jetbrains/kotlinx/serialization/compiler/backend/ir/SerializableIrGenerator.kt index 36c0b96da16..99c0cff7dc8 100644 --- a/plugins/kotlinx-serialization/kotlinx-serialization.backend/src/org/jetbrains/kotlinx/serialization/compiler/backend/ir/SerializableIrGenerator.kt +++ b/plugins/kotlinx-serialization/kotlinx-serialization.backend/src/org/jetbrains/kotlinx/serialization/compiler/backend/ir/SerializableIrGenerator.kt @@ -82,7 +82,7 @@ class SerializableIrGenerator( it is IrProperty && it.backingField != null -> { if (it in serialDescs) { current = it - } else if (it.backingField?.initializer != null) { + } else if (it.backingField?.initializer != null && !it.isDelegated) { // skip transient lateinit or deferred properties (with null initializer) val expression = initializerAdapter(it.backingField!!.initializer!!) diff --git a/plugins/kotlinx-serialization/testData/boxIr/delegatedProperty.kt b/plugins/kotlinx-serialization/testData/boxIr/delegatedProperty.kt new file mode 100644 index 00000000000..7161283c291 --- /dev/null +++ b/plugins/kotlinx-serialization/testData/boxIr/delegatedProperty.kt @@ -0,0 +1,109 @@ +// TARGET_BACKEND: JVM_IR + +// WITH_STDLIB + +import kotlinx.serialization.* +import kotlinx.serialization.descriptors.* +import kotlinx.serialization.encoding.* +import kotlinx.serialization.json.* +import kotlin.reflect.KProperty +import kotlin.properties.* + +@Serializable +data class SimpleDTO( + val realProp: Int, +) { + @Transient + private val additionalProperties: Map = mapOf("delegatedProp" to 123) + val delegatedProp: Int? by additionalProperties +} + +// optimized properties must also work +// https://kotlinlang.org/docs/whatsnew1720.html#more-optimized-cases-of-delegated-properties +// A named object: +object NamedObject { + operator fun getValue(thisRef: Any?, property: KProperty<*>): String = "test-string" +} + +@Serializable +data class DelegatedByObjectProperty( + val realProp: Int +) { + val delegatedProp: String by NamedObject +} + +// A final val property with a backing field and a default getter in the same module: +val impl: ReadOnlyProperty = object : ReadOnlyProperty { + override operator fun getValue(thisRef: Any?, property: KProperty<*>): String = "test-string" +} + +@Serializable +class DelegatedByFinalVal( + val realProp: Int +) { + val delegatedProp: String by impl +} + +// A var property with a backing field and a default getter in the same module: +var implvar: ReadWriteProperty = object : ReadWriteProperty { + private var value = "test-string" + override operator fun getValue(thisRef: Any?, property: KProperty<*>): String = value + override operator fun setValue( + thisRef: Any?, + property: KProperty<*>, + value: String) { this.value = value } +} + +@Serializable +class DelegatedByVar( + val realProp: Int +) { + var delegatedProp: String by implvar +} + +// delegated by this +@Serializable +class DelegatedByThis(val realProp: Int) { + operator fun getValue(thisRef: Any?, property: KProperty<*>) = "test-string" + + val delegatedProp by this +} + +fun box(): String { + val simpleDTO = SimpleDTO(123) + val simpleDTOJsonStr = Json.encodeToString(simpleDTO) + val simpleDTODecoded = Json.decodeFromString(simpleDTOJsonStr) + if (simpleDTOJsonStr != """{"realProp":123}""") return simpleDTOJsonStr + if (simpleDTODecoded.delegatedProp != simpleDTO.delegatedProp) return "SimpleDTO Delegate is incorrect!" + if (simpleDTODecoded.realProp !== 123) return "SimpleDTO Deserialization failed" + + val objProp = DelegatedByObjectProperty(123) + val objPropJsonStr = Json.encodeToString(objProp) + val objPropDecoded = Json.decodeFromString(objPropJsonStr) + if (objPropJsonStr != """{"realProp":123}""") return simpleDTOJsonStr + if (objPropDecoded.delegatedProp != objProp.delegatedProp) return "DelegatedByObjectProperty Delegate is incorrect!" + if (objPropDecoded.realProp !== 123) return "DelegatedByObjectProperty Deserialization failed" + + val byFinal = DelegatedByFinalVal(123) + val byFinalJsonStr = Json.encodeToString(byFinal) + val byFinalDecoded = Json.decodeFromString(byFinalJsonStr) + if (byFinalJsonStr != """{"realProp":123}""") return simpleDTOJsonStr + if (byFinalDecoded.delegatedProp != byFinal.delegatedProp) return "DelegatedByFinalVal Delegate is incorrect!" + if (byFinalDecoded.realProp !== 123) return "DelegatedByFinalVal Deserialization failed" + + val byVar = DelegatedByVar(123) + val byVarJsonStr = Json.encodeToString(byVar) + val byVarDecoded = Json.decodeFromString(byVarJsonStr) + if (byVarJsonStr != """{"realProp":123}""") return simpleDTOJsonStr + if (byVarDecoded.delegatedProp != byVar.delegatedProp) return "DelegatedByVar Delegate is incorrect!" + if (byVarDecoded.realProp !== 123) return "DelegatedByVar Deserialization failed" + + val byThisExp = DelegatedByThis(123) + val byThisJsonStr = Json.encodeToString(byThisExp) + val byThisDecoded = Json.decodeFromString(byThisJsonStr) + if (byThisJsonStr != """{"realProp":123}""") return simpleDTOJsonStr + if (byThisDecoded.delegatedProp != byThisExp.delegatedProp) return "DelegatedByThis Delegate is incorrect!" + if (byThisDecoded.realProp !== 123) return "DelegatedByThis Deserialization failed" + + return "OK" +} \ No newline at end of file diff --git a/plugins/kotlinx-serialization/tests-gen/org/jetbrains/kotlinx/serialization/runners/SerializationFirLightTreeBlackBoxTestGenerated.java b/plugins/kotlinx-serialization/tests-gen/org/jetbrains/kotlinx/serialization/runners/SerializationFirLightTreeBlackBoxTestGenerated.java index ed7befd0d05..6ec2466bd18 100644 --- a/plugins/kotlinx-serialization/tests-gen/org/jetbrains/kotlinx/serialization/runners/SerializationFirLightTreeBlackBoxTestGenerated.java +++ b/plugins/kotlinx-serialization/tests-gen/org/jetbrains/kotlinx/serialization/runners/SerializationFirLightTreeBlackBoxTestGenerated.java @@ -69,6 +69,12 @@ public class SerializationFirLightTreeBlackBoxTestGenerated extends AbstractSeri runTest("plugins/kotlinx-serialization/testData/boxIr/delegatedInterface.kt"); } + @Test + @TestMetadata("delegatedProperty.kt") + public void testDelegatedProperty() throws Exception { + runTest("plugins/kotlinx-serialization/testData/boxIr/delegatedProperty.kt"); + } + @Test @TestMetadata("enumsAreCached.kt") public void testEnumsAreCached() 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 9f48ddb9078..0603fc9c26d 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 @@ -67,6 +67,12 @@ public class SerializationIrBoxTestGenerated extends AbstractSerializationIrBoxT runTest("plugins/kotlinx-serialization/testData/boxIr/delegatedInterface.kt"); } + @Test + @TestMetadata("delegatedProperty.kt") + public void testDelegatedProperty() throws Exception { + runTest("plugins/kotlinx-serialization/testData/boxIr/delegatedProperty.kt"); + } + @Test @TestMetadata("enumsAreCached.kt") public void testEnumsAreCached() throws Exception {