[KxSerialization] Fix NPE when accessing delegated property

Kotlin 1.7.20 added optimizations for delegated properties on the JVM,
which broke serialization for optimized properties. Commit bfeff81
tried to fix that, but broke non-optimized delegated properties. This
commit restores correct serialization for optimized and non-optimized
properties, also ensuring that it only affects the JVM target.

 #KT-58954 Fixed
 #KT-59113 Fixed
This commit is contained in:
OliverO2
2023-06-02 16:23:06 +02:00
committed by Alexander Udalov
parent 733ca5a358
commit 027593cd78
6 changed files with 138 additions and 46 deletions
@@ -6,6 +6,7 @@
package org.jetbrains.kotlinx.serialization.compiler.backend.ir
import org.jetbrains.kotlin.backend.common.lower.irThrow
import org.jetbrains.kotlin.backend.jvm.lower.isJvmOptimizableDelegate
import org.jetbrains.kotlin.descriptors.ClassKind
import org.jetbrains.kotlin.descriptors.DescriptorVisibilities
import org.jetbrains.kotlin.descriptors.ValueParameterDescriptor
@@ -21,6 +22,7 @@ import org.jetbrains.kotlin.ir.types.*
import org.jetbrains.kotlin.ir.util.*
import org.jetbrains.kotlin.name.ClassId
import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.platform.jvm.isJvm
import org.jetbrains.kotlin.util.OperatorNameConventions
import org.jetbrains.kotlin.utils.getOrPutNullable
import org.jetbrains.kotlinx.serialization.compiler.extensions.SerializationPluginContext
@@ -82,8 +84,10 @@ class SerializableIrGenerator(
it is IrProperty && it.backingField != null -> {
if (it in serialDescs) {
current = it
} else if (it.backingField?.initializer != null && !it.isDelegated) {
// skip transient lateinit or deferred properties (with null initializer)
} else if (it.backingField?.initializer != null &&
!(it.isJvmOptimizableDelegate() && compilerContext.platform.isJvm())
) {
// skip transient lateinit or deferred properties (with null initializer) and optimized delegations
val expression = initializerAdapter(it.backingField!!.initializer!!)
statementsAfterSerializableProperty.getOrPutNullable(current, { mutableListOf() })
@@ -1,5 +1,3 @@
// TARGET_BACKEND: JVM_IR
// WITH_STDLIB
import kotlinx.serialization.*
@@ -69,6 +67,34 @@ class DelegatedByThis(val realProp: Int) {
val delegatedProp by this
}
// delegating directly to another property
@Serializable
class DelegatedByDirectProperty(var targetProperty: Int = 5) {
var delegatingProperty: Int by ::targetProperty
}
// generic delegate
@Serializable
open class GenericDelegate<Target> {
private var target: Target? = null
operator fun getValue(thisRef: Any?, property: KProperty<*>): Target? = target
operator fun setValue(thisRef: Any?, property: KProperty<*>, value: Target?) {
target = value
}
}
@Serializable
class GenericDelegateHolder(val id: String) {
private var _delegatingProperty = GenericDelegate<String>()
var delegatingProperty: String? by _delegatingProperty
constructor(id: String, delegatingProperty: String?) : this(id) {
this.delegatingProperty = delegatingProperty
}
}
fun box(): String {
val simpleDTO = SimpleDTO(123)
val simpleDTOJsonStr = Json.encodeToString(simpleDTO)
@@ -105,5 +131,21 @@ fun box(): String {
if (byThisDecoded.delegatedProp != byThisExp.delegatedProp) return "DelegatedByThis Delegate is incorrect!"
if (byThisDecoded.realProp !== 123) return "DelegatedByThis Deserialization failed"
for (original in listOf(
GenericDelegateHolder("#1", "stuff"),
GenericDelegateHolder("#2", null)
)) {
val json = Json.encodeToString(original)
val deserialized = Json.decodeFromString(GenericDelegateHolder.serializer(), json)
if (deserialized.delegatingProperty != original.delegatingProperty) return "Generic delegate fail: $json"
}
val byDirectPropertyExp = DelegatedByDirectProperty(123)
val byDirectPropertyJsonStr = Json.encodeToString(byDirectPropertyExp)
val byDirectPropertyDecoded = Json.decodeFromString<DelegatedByDirectProperty>(byDirectPropertyJsonStr)
if (byDirectPropertyJsonStr != """{"targetProperty":123}""") return simpleDTOJsonStr
if (byDirectPropertyDecoded.delegatingProperty != byDirectPropertyExp.delegatingProperty) return "Direct property delegation, delegatingProperty fail: $byDirectPropertyJsonStr"
if (byDirectPropertyDecoded.targetProperty !== 123) return "Direct property delegation, targetProperty fail: $byDirectPropertyJsonStr"
return "OK"
}
}
@@ -31,6 +31,12 @@ public class SerializationIrJsBoxTestGenerated extends AbstractSerializationIrJs
runTest("plugins/kotlinx-serialization/testData/boxIr/constValInSerialName.kt");
}
@Test
@TestMetadata("delegatedProperty.kt")
public void testDelegatedProperty() throws Exception {
runTest("plugins/kotlinx-serialization/testData/boxIr/delegatedProperty.kt");
}
@Test
@TestMetadata("excludedFromExport.kt")
public void testExcludedFromExport() throws Exception {