Generate writeSelf and load constructor even if they were not created by frontend

which is true in case for FIR which discourages purely synthetic declarations.
This commit is contained in:
Leonid Startsev
2022-08-11 18:59:05 +02:00
committed by Space
parent acd6180c30
commit edff4d43bf
12 changed files with 328 additions and 45 deletions
@@ -18,9 +18,7 @@ import org.jetbrains.kotlin.ir.deepCopyWithVariables
import org.jetbrains.kotlin.ir.expressions.*
import org.jetbrains.kotlin.ir.expressions.impl.*
import org.jetbrains.kotlin.ir.symbols.IrClassSymbol
import org.jetbrains.kotlin.ir.symbols.IrFieldSymbol
import org.jetbrains.kotlin.ir.symbols.IrFunctionSymbol
import org.jetbrains.kotlin.ir.symbols.IrValueSymbol
import org.jetbrains.kotlin.ir.symbols.impl.*
import org.jetbrains.kotlin.ir.types.*
import org.jetbrains.kotlin.ir.util.*
@@ -30,7 +28,6 @@ import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.platform.jvm.isJvm
import org.jetbrains.kotlin.resolve.descriptorUtil.fqNameSafe
import org.jetbrains.kotlin.resolve.descriptorUtil.isEffectivelyExternal
import org.jetbrains.kotlin.resolve.jvm.JvmPrimitiveType
import org.jetbrains.kotlin.types.KotlinType
import org.jetbrains.kotlinx.serialization.compiler.extensions.SerializationPluginContext
@@ -65,7 +62,7 @@ interface IrBuilderWithPluginContext {
this.returnType = type
name = Name.identifier("<anonymous>")
visibility = DescriptorVisibilities.LOCAL
origin = SERIALIZABLE_PLUGIN_ORIGIN
origin = SERIALIZATION_PLUGIN_ORIGIN
}
function.body =
DeclarationIrBuilder(compilerContext, function.symbol, startOffset, endOffset).irBlockBody(startOffset, endOffset, bodyGen)
@@ -138,7 +135,7 @@ interface IrBuilderWithPluginContext {
fun IrClass.contributeAnonymousInitializer(bodyGen: IrBlockBodyBuilder.() -> Unit) {
val symbol = IrAnonymousInitializerSymbolImpl(descriptor)
factory.createAnonymousInitializer(startOffset, endOffset, SERIALIZABLE_PLUGIN_ORIGIN, symbol).also {
factory.createAnonymousInitializer(startOffset, endOffset, SERIALIZATION_PLUGIN_ORIGIN, symbol).also {
it.parent = this
declarations.add(it)
it.body = DeclarationIrBuilder(compilerContext, symbol, startOffset, endOffset).irBlockBody(startOffset, endOffset, bodyGen)
@@ -299,7 +296,7 @@ interface IrBuilderWithPluginContext {
endOffset = propertyParent.endOffset
name = propertyName
type = propertyType
origin = SERIALIZABLE_PLUGIN_ORIGIN
origin = SERIALIZATION_PLUGIN_ORIGIN
isFinal = true
this.visibility = DescriptorVisibilities.PRIVATE
}.also { it.parent = propertyParent }
@@ -309,7 +306,7 @@ interface IrBuilderWithPluginContext {
endOffset = propertyParent.endOffset
name = propertyName
this.isVar = false
origin = SERIALIZABLE_PLUGIN_ORIGIN
origin = SERIALIZATION_PLUGIN_ORIGIN
}
prop.apply {
@@ -321,7 +318,7 @@ interface IrBuilderWithPluginContext {
startOffset = propertyParent.startOffset
endOffset = propertyParent.endOffset
returnType = propertyType
origin = SERIALIZABLE_PLUGIN_ORIGIN
origin = SERIALIZATION_PLUGIN_ORIGIN
this.visibility = visibility
modality = Modality.FINAL
}
@@ -0,0 +1,123 @@
/*
* Copyright 2010-2022 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlinx.serialization.compiler.backend.ir
import org.jetbrains.kotlin.descriptors.DescriptorVisibilities
import org.jetbrains.kotlin.descriptors.Modality
import org.jetbrains.kotlin.ir.builders.declarations.addConstructor
import org.jetbrains.kotlin.ir.builders.declarations.addFunction
import org.jetbrains.kotlin.ir.builders.declarations.addTypeParameter
import org.jetbrains.kotlin.ir.builders.declarations.addValueParameter
import org.jetbrains.kotlin.ir.declarations.IrClass
import org.jetbrains.kotlin.ir.types.*
import org.jetbrains.kotlin.ir.util.companionObject
import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.resolve.isInlineClass
import org.jetbrains.kotlinx.serialization.compiler.backend.common.SerializerCodegen
import org.jetbrains.kotlinx.serialization.compiler.extensions.SerializationPluginContext
import org.jetbrains.kotlinx.serialization.compiler.resolve.SerialEntityNames
import org.jetbrains.kotlinx.serialization.compiler.resolve.bitMaskSlotCount
import org.jetbrains.kotlinx.serialization.compiler.resolve.hasCompanionObjectAsSerializer
/**
* Generates only specific declarations, but NOT their bodies.
* This pass is needed to be able to reference these declarations from other generated bodies
* (e.g. to if we want to reference write$Self() from serialize(), we need to make sure that at least declaration of write$Self is already created.
*
* These functions were usually stubbed from descriptors, but since FIR discourages purely synthetic functions,
* we manually add them here.
*/
class IrPreGenerator(
val irClass: IrClass,
compilerContext: SerializationPluginContext,
) : BaseIrGenerator(irClass, compilerContext) {
private val serialDescriptorSymbol = compilerContext.getClassFromRuntime(SerialEntityNames.SERIAL_DESCRIPTOR_CLASS)
fun generate() {
preGenerateWriteSelfMethodIfNeeded()
preGenerateDeserializationConstructorIfNeeded()
}
private fun preGenerateWriteSelfMethodIfNeeded() {
if (!irClass.isInternalSerializable) return
val serializerDescriptor = irClass.classSerializer(compilerContext)?.owner ?: return
if (!irClass.shouldHaveSpecificSyntheticMethods { serializerDescriptor.findPluginGeneratedMethod(SerialEntityNames.SAVE) }) return
if (irClass.findWriteSelfMethod() != null) return
val method = irClass.addFunction {
name = SerialEntityNames.WRITE_SELF_NAME
returnType = compilerContext.irBuiltIns.unitType
visibility = DescriptorVisibilities.PUBLIC
modality = Modality.FINAL
origin = SERIALIZATION_PLUGIN_ORIGIN
}
method.apply {
dispatchReceiverParameter = null // function is static
}
val typeParams = serializerDescriptor.typeParameters.map {
method.addTypeParameter(
it.name.asString(), compilerContext.irBuiltIns.anyNType
)
}
val typeParamsAsArguments = typeParams.map { it.defaultType }
// object
method.addValueParameter(
Name.identifier("self"), irClass.typeWith(typeParamsAsArguments),
SERIALIZATION_PLUGIN_ORIGIN
)
// encoder
method.addValueParameter(
Name.identifier("output"),
compilerContext.getClassFromRuntime(SerialEntityNames.STRUCTURE_ENCODER_CLASS).defaultType,
SERIALIZATION_PLUGIN_ORIGIN
)
// descriptor
method.addValueParameter(
Name.identifier("serialDesc"), serialDescriptorSymbol.defaultType,
SERIALIZATION_PLUGIN_ORIGIN
)
// KSerializer<Tn>
val kSerializerSymbol = compilerContext.getClassFromRuntime(SerialEntityNames.KSERIALIZER_CLASS)
typeParamsAsArguments.forEachIndexed { i, it ->
method.addValueParameter(
Name.identifier("${SerialEntityNames.typeArgPrefix}$i"),
kSerializerSymbol.typeWith(it),
SERIALIZATION_PLUGIN_ORIGIN
)
}
}
private fun preGenerateDeserializationConstructorIfNeeded() {
if (!irClass.isInternalSerializable) return
// do not add synthetic deserialization constructor if .deserialize method is customized
if (irClass.hasCompanionObjectAsSerializer && irClass.companionObject()?.findPluginGeneratedMethod(SerialEntityNames.LOAD) == null) return
if (irClass.isValue) return
if (irClass.findSerializableSyntheticConstructor() != null) return
val ctor = irClass.addConstructor {
origin = SERIALIZATION_PLUGIN_ORIGIN
visibility = DescriptorVisibilities.PUBLIC
}
val markerClassSymbol =
compilerContext.getClassFromInternalSerializationPackage(SerialEntityNames.SERIAL_CTOR_MARKER_NAME.asString())
val serializableProperties = serializablePropertiesForIrBackend(irClass).serializableProperties
val bitMaskSlotsCount = serializableProperties.bitMaskSlotCount()
repeat(bitMaskSlotsCount) {
ctor.addValueParameter(Name.identifier("seen$it"), compilerContext.irBuiltIns.intType, SERIALIZATION_PLUGIN_ORIGIN)
}
for (prop in serializableProperties) {
ctor.addValueParameter(prop.name, prop.type.makeNullableIfNotPrimitive(), SERIALIZATION_PLUGIN_ORIGIN)
}
ctor.addValueParameter(SerialEntityNames.dummyParamName, markerClassSymbol.defaultType, SERIALIZATION_PLUGIN_ORIGIN)
}
private fun IrType.makeNullableIfNotPrimitive() =
if (this.isPrimitiveType(false)) this
else this.makeNullable()
}
@@ -163,7 +163,7 @@ internal fun IrConstructor.lastArgumentIsAnnotationArray(): Boolean {
return ((lastArgType as? IrSimpleType)?.arguments?.firstOrNull()?.typeOrNull?.classFqName?.toString() == "kotlin.Annotation")
}
fun IrClass.serializableSyntheticConstructor(): IrConstructorSymbol? { // FIXME: remove nullability W/A when FIR plugin will add proper deserialization ctor
fun IrClass.findSerializableSyntheticConstructor(): IrConstructorSymbol? {
return declarations.filterIsInstance<IrConstructor>().singleOrNull { it.isSerializationCtor() }?.symbol
}
@@ -210,4 +210,7 @@ fun IrType.classOrUpperBound(): IrClassSymbol? = when(val cls = classifierOrNull
is IrScriptSymbol -> cls.owner.targetClass
is IrTypeParameterSymbol -> cls.owner.representativeUpperBound.classOrUpperBound()
else -> null
}
}
internal inline fun IrClass.shouldHaveSpecificSyntheticMethods(functionPresenceChecker: () -> IrSimpleFunction?) =
!isValue && (isAbstractOrSealedSerializableClass || functionPresenceChecker() != null)
@@ -84,7 +84,7 @@ class SerialInfoImplJvmIrGenerator(
generateSimplePropertyWithBackingField(property.descriptor, irClass, Name.identifier("_" + property.name.asString()))
val getter = property.getter!!
getter.origin = SERIALIZABLE_PLUGIN_ORIGIN
getter.origin = SERIALIZATION_PLUGIN_ORIGIN
// Add JvmName annotation to property getters to force the resulting JVM method name for 'x' be 'x', instead of 'getX',
// and to avoid having useless bridges for it generated in BridgeLowering.
// Unfortunately, this results in an extra `@JvmName` annotation in the bytecode, but it shouldn't matter very much.
@@ -92,7 +92,7 @@ class SerialInfoImplJvmIrGenerator(
val field = property.backingField!!
field.visibility = DescriptorVisibilities.PRIVATE
field.origin = SERIALIZABLE_PLUGIN_ORIGIN
field.origin = SERIALIZATION_PLUGIN_ORIGIN
val parameter = ctor.addValueParameter(property.name.asString(), field.type)
ctorBody.statements += IrSetFieldImpl(
@@ -200,7 +200,7 @@ class SerialInfoImplJvmIrGenerator(
propertyParent.factory.createProperty(
propertyParent.startOffset,
propertyParent.endOffset,
SERIALIZABLE_PLUGIN_ORIGIN,
SERIALIZATION_PLUGIN_ORIGIN,
IrPropertySymbolImpl(propertyDescriptor),
name,
visibility,
@@ -240,7 +240,7 @@ class SerialInfoImplJvmIrGenerator(
originProperty.factory.createField(
originProperty.startOffset,
originProperty.endOffset,
SERIALIZABLE_PLUGIN_ORIGIN,
SERIALIZATION_PLUGIN_ORIGIN,
IrFieldSymbolImpl(propertyDescriptor),
name,
type.toIrType(),
@@ -274,7 +274,7 @@ class SerialInfoImplJvmIrGenerator(
property.factory.createFunction(
fieldSymbol.owner.startOffset,
fieldSymbol.owner.endOffset,
SERIALIZABLE_PLUGIN_ORIGIN, IrSimpleFunctionSymbolImpl(descriptor),
SERIALIZATION_PLUGIN_ORIGIN, IrSimpleFunctionSymbolImpl(descriptor),
name, visibility, modality, returnType!!.toIrType(),
isInline, isEffectivelyExternal(), isTailrec, isSuspend, isOperator, isInfix, isExpect
)
@@ -368,7 +368,7 @@ class SerialInfoImplJvmIrGenerator(
fun irValueParameter(descriptor: ParameterDescriptor): IrValueParameter = with(descriptor) {
@OptIn(FirIncompatiblePluginAPI::class) // should never be called after FIR frontend
factory.createValueParameter(
function.startOffset, function.endOffset, SERIALIZABLE_PLUGIN_ORIGIN, IrValueParameterSymbolImpl(this),
function.startOffset, function.endOffset, SERIALIZATION_PLUGIN_ORIGIN, IrValueParameterSymbolImpl(this),
name, indexOrMinusOne, type.toIrType(), varargElementType?.toIrType(), isCrossinline, isNoinline,
isHidden = false, isAssignable = false
).also {
@@ -394,7 +394,7 @@ class SerialInfoImplJvmIrGenerator(
val newTypeParameters = descriptor.typeParameters.map {
factory.createTypeParameter(
startOffset, endOffset,
SERIALIZABLE_PLUGIN_ORIGIN,
SERIALIZATION_PLUGIN_ORIGIN,
IrTypeParameterSymbolImpl(it),
it.name, it.index, it.isReified, it.variance
).also { typeParameter ->
@@ -270,7 +270,7 @@ class SerializableIrGenerator(
propertiesStart: Int
): Int {
check(superClass.isInternalSerializable)
val superCtorRef = superClass.serializableSyntheticConstructor()!!
val superCtorRef = superClass.findSerializableSyntheticConstructor() ?: error("Class serializable internally should have special constructor with marker")
val superProperties = serializablePropertiesForIrBackend(superClass).serializableProperties
val superSlots = superProperties.bitMaskSlotCount()
val arguments = allValueParameters.subList(0, superSlots) +
@@ -358,10 +358,7 @@ class SerializableIrGenerator(
generateSyntheticMethods()
}
private inline fun IrClass.shouldHaveSpecificSyntheticMethods(functionPresenceChecker: () -> IrSimpleFunction?) =
!isValue && (isAbstractOrSealedSerializableClass || functionPresenceChecker() != null)
private fun generateSyntheticInternalConstructor() { // TODO: this doesn't work with OLD FE
private fun generateSyntheticInternalConstructor() {
val serializerDescriptor = irClass.classSerializer(compilerContext)?.owner ?: return
if (irClass.shouldHaveSpecificSyntheticMethods { serializerDescriptor.findPluginGeneratedMethod(LOAD) }) {
val constrDesc = irClass.constructors.find(IrConstructor::isSerializationCtor) ?: return
@@ -28,7 +28,6 @@ import org.jetbrains.kotlinx.serialization.compiler.resolve.SerialEntityNames
class SerializerForEnumsGenerator(
irClass: IrClass,
compilerContext: SerializationPluginContext,
serialInfoJvmGenerator: SerialInfoImplJvmIrGenerator,
) : SerializerIrGenerator(irClass, compilerContext, null) {
override fun generateSave(function: IrSimpleFunction) = addFunctionBody(function) { saveFunc ->
fun irThis(): IrExpression =
@@ -47,7 +47,7 @@ import org.jetbrains.kotlinx.serialization.compiler.resolve.SerialEntityNames.ST
import org.jetbrains.kotlinx.serialization.compiler.resolve.SerialEntityNames.STRUCTURE_ENCODER_CLASS
import org.jetbrains.kotlinx.serialization.compiler.resolve.SerialEntityNames.UNKNOWN_FIELD_EXC
object SERIALIZABLE_PLUGIN_ORIGIN : IrDeclarationOriginImpl("SERIALIZER", true)
object SERIALIZATION_PLUGIN_ORIGIN : IrDeclarationOriginImpl("KOTLINX_SERIALIZATION", true)
internal typealias FunctionWithArgs = Pair<IrFunctionSymbol, List<IrExpression>>
@@ -118,7 +118,7 @@ open class SerializerIrGenerator(
val anonymousInit = irClass.run {
val symbol = IrAnonymousInitializerSymbolImpl(descriptor)
irClass.factory.createAnonymousInitializer(startOffset, endOffset, SERIALIZABLE_PLUGIN_ORIGIN, symbol).also {
irClass.factory.createAnonymousInitializer(startOffset, endOffset, SERIALIZATION_PLUGIN_ORIGIN, symbol).also {
it.parent = this
declarations.add(it)
}
@@ -495,7 +495,7 @@ open class SerializerIrGenerator(
)
val typeArgs = (loadFunc.returnType as IrSimpleType).arguments.map { (it as IrTypeProjection).type }
val deserCtor: IrConstructorSymbol? = serializableIrClass.serializableSyntheticConstructor()
val deserCtor: IrConstructorSymbol? = serializableIrClass.findSerializableSyntheticConstructor()
if (serializableIrClass.isInternalSerializable && deserCtor != null) {
var args: List<IrExpression> = serializableProperties.map { serialPropertiesMap.getValue(it.ir).get() }
args = bitMasks.map { irGet(it) } + args + irNull()
@@ -619,14 +619,12 @@ open class SerializerIrGenerator(
irClass: IrClass,
context: SerializationPluginContext,
metadataPlugin: SerializationDescriptorSerializerPlugin?,
serialInfoJvmGenerator: SerialInfoImplJvmIrGenerator,
) {
val serializableDesc = getSerializableClassDescriptorBySerializer(irClass.symbol.descriptor) ?: return
val generator = when {
serializableDesc.isEnumWithLegacyGeneratedSerializer() -> SerializerForEnumsGenerator(
irClass,
context,
serialInfoJvmGenerator
context
)
serializableDesc.isInlineClass() -> SerializerForInlineClassGenerator(irClass, context)
else -> SerializerIrGenerator(irClass, context, metadataPlugin)
@@ -12,19 +12,13 @@ import org.jetbrains.kotlin.backend.common.extensions.IrPluginContext
import org.jetbrains.kotlin.backend.common.runOnFilePostfix
import org.jetbrains.kotlin.backend.jvm.ir.fileParent
import org.jetbrains.kotlin.ir.IrElement
import org.jetbrains.kotlin.ir.declarations.IrClass
import org.jetbrains.kotlin.ir.declarations.IrFile
import org.jetbrains.kotlin.ir.declarations.IrModuleFragment
import org.jetbrains.kotlin.ir.declarations.IrSimpleFunction
import org.jetbrains.kotlin.ir.declarations.*
import org.jetbrains.kotlin.ir.visitors.IrElementTransformerVoid
import org.jetbrains.kotlin.ir.visitors.IrElementVisitorVoid
import org.jetbrains.kotlin.ir.visitors.acceptChildrenVoid
import org.jetbrains.kotlin.ir.visitors.acceptVoid
import org.jetbrains.kotlin.platform.jvm.isJvm
import org.jetbrains.kotlinx.serialization.compiler.backend.ir.SerialInfoImplJvmIrGenerator
import org.jetbrains.kotlinx.serialization.compiler.backend.ir.SerializableCompanionIrGenerator
import org.jetbrains.kotlinx.serialization.compiler.backend.ir.SerializableIrGenerator
import org.jetbrains.kotlinx.serialization.compiler.backend.ir.SerializerIrGenerator
import org.jetbrains.kotlinx.serialization.compiler.backend.ir.*
import org.jetbrains.kotlinx.serialization.compiler.resolve.KSerializerDescriptorResolver
import java.util.concurrent.ConcurrentHashMap
@@ -52,25 +46,49 @@ class SerializationPluginContext(baseContext: IrPluginContext, val metadataPlugi
internal val copiedStaticWriteSelf: MutableMap<IrSimpleFunction, IrSimpleFunction> = ConcurrentHashMap()
}
private inline fun IrClass.runPluginSafe(block: () -> Unit) {
try {
block()
} catch (e: Exception) {
throw CompilationException(
"kotlinx.serialization compiler plugin internal error: unable to transform declaration, see cause",
this.fileParent,
this,
e
)
}
}
private class SerializerClassLowering(
baseContext: IrPluginContext,
metadataPlugin: SerializationDescriptorSerializerPlugin?,
moduleFragment: IrModuleFragment
) : IrElementTransformerVoid(), ClassLoweringPass {
val context: SerializationPluginContext = SerializationPluginContext(baseContext, metadataPlugin)
private val serialInfoJvmGenerator = SerialInfoImplJvmIrGenerator(context, moduleFragment).also { context.serialInfoImplJvmIrGenerator = it }
private val serialInfoJvmGenerator =
SerialInfoImplJvmIrGenerator(context, moduleFragment).also { context.serialInfoImplJvmIrGenerator = it }
override fun lower(irClass: IrClass) {
try {
irClass.runPluginSafe {
SerializableIrGenerator.generate(irClass, context)
SerializerIrGenerator.generate(irClass, context, context.metadataPlugin, serialInfoJvmGenerator)
SerializerIrGenerator.generate(irClass, context, context.metadataPlugin)
SerializableCompanionIrGenerator.generate(irClass, context)
if (context.platform.isJvm() && KSerializerDescriptorResolver.isSerialInfoImpl(irClass.descriptor)) {
serialInfoJvmGenerator.generate(irClass)
}
} catch (e: Exception) {
throw CompilationException("kotlinx.serialization compiler plugin internal error: unable to transform declaration, see cause", irClass.fileParent, irClass, e)
}
}
}
private class SerializerClassPreLowering(
baseContext: IrPluginContext
) : IrElementTransformerVoid(), ClassLoweringPass {
val context: SerializationPluginContext = SerializationPluginContext(baseContext, null)
override fun lower(irClass: IrClass) {
irClass.runPluginSafe {
IrPreGenerator(irClass, context).generate()
}
}
}
@@ -82,8 +100,9 @@ open class SerializationLoweringExtension @JvmOverloads constructor(
moduleFragment: IrModuleFragment,
pluginContext: IrPluginContext
) {
val serializerClassLowering = SerializerClassLowering(pluginContext, metadataPlugin, moduleFragment)
for (file in moduleFragment.files)
serializerClassLowering.runOnFileInOrder(file)
val pass1 = SerializerClassPreLowering(pluginContext)
val pass2 = SerializerClassLowering(pluginContext, metadataPlugin, moduleFragment)
moduleFragment.files.forEach(pass1::runOnFileInOrder)
moduleFragment.files.forEach(pass2::runOnFileInOrder)
}
}
@@ -48,4 +48,10 @@ public class SerializationFirBlackBoxTestGenerated extends AbstractSerialization
public void testMultipleProperties() throws Exception {
runTest("plugins/kotlin-serialization/kotlin-serialization-compiler/testData/firMembers/multipleProperties.kt");
}
@Test
@TestMetadata("privatePropertiesSerialization.kt")
public void testPrivatePropertiesSerialization() throws Exception {
runTest("plugins/kotlin-serialization/kotlin-serialization-compiler/testData/firMembers/privatePropertiesSerialization.kt");
}
}
@@ -47,4 +47,10 @@ public class SerializationFirMembersTestGenerated extends AbstractSerializationF
public void testMultipleProperties() throws Exception {
runTest("plugins/kotlin-serialization/kotlin-serialization-compiler/testData/firMembers/multipleProperties.kt");
}
@Test
@TestMetadata("privatePropertiesSerialization.kt")
public void testPrivatePropertiesSerialization() throws Exception {
runTest("plugins/kotlin-serialization/kotlin-serialization-compiler/testData/firMembers/privatePropertiesSerialization.kt");
}
}
@@ -0,0 +1,98 @@
FILE: privatePropertiesSerialization.kt
@R|kotlinx/serialization/Serializable|() public open class Parent : R|kotlin/Any| {
public constructor(ctor: R|kotlin/Int|): R|Parent| {
super<R|kotlin/Any|>()
}
private final val ctor: R|kotlin/Int| = R|<local>/ctor|
private get(): R|kotlin/Int|
private final var body: R|kotlin/String| = String(42)
private get(): R|kotlin/String|
private set(value: R|kotlin/String|): R|kotlin/Unit|
public final fun checkDeser(c: R|kotlin/Int|, b: R|kotlin/String|): R|kotlin/String| {
when () {
!=(this@R|/Parent|.R|/Parent.ctor|, R|<local>/c|) -> {
^checkDeser <strcat>(String(Ctor : ), this@R|/Parent|.R|/Parent.ctor|)
}
}
when () {
!=(this@R|/Parent|.R|/Parent.body|, R|<local>/b|) -> {
^checkDeser <strcat>(String(Body : ), this@R|/Parent|.R|/Parent.body|)
}
}
^checkDeser String(OK)
}
public final companion object Companion : R|kotlin/Any| {
public final fun serializer(): R|kotlinx/serialization/KSerializer<Parent>|
private constructor(): R|Parent.Companion|
}
public final object $serializer : R|kotlinx/serialization/internal/GeneratedSerializer<Parent>| {
public final fun serialize(encoder: R|kotlinx/serialization/encoding/Encoder|, value: R|Parent|): R|kotlin/Unit|
public final fun deserialize(decoder: R|kotlinx/serialization/encoding/Decoder|): R|Parent|
public final val descriptor: R|kotlinx/serialization/descriptors/SerialDescriptor|
public get(): R|kotlinx/serialization/descriptors/SerialDescriptor|
public final fun childSerializers(): R|kotlin/Array<kotlinx/serialization/KSerializer<*>>|
private constructor(): R|Parent.$serializer|
}
}
@R|kotlinx/serialization/Serializable|() public final class Derived : R|Parent| {
public constructor(): R|Derived| {
super<R|Parent|>(Int(42))
}
public final companion object Companion : R|kotlin/Any| {
public final fun serializer(): R|kotlinx/serialization/KSerializer<Derived>|
private constructor(): R|Derived.Companion|
}
public final object $serializer : R|kotlinx/serialization/internal/GeneratedSerializer<Derived>| {
public final fun serialize(encoder: R|kotlinx/serialization/encoding/Encoder|, value: R|Derived|): R|kotlin/Unit|
public final fun deserialize(decoder: R|kotlinx/serialization/encoding/Decoder|): R|Derived|
public final val descriptor: R|kotlinx/serialization/descriptors/SerialDescriptor|
public get(): R|kotlinx/serialization/descriptors/SerialDescriptor|
public final fun childSerializers(): R|kotlin/Array<kotlinx/serialization/KSerializer<*>>|
private constructor(): R|Derived.$serializer|
}
}
public final fun test(targetString: R|kotlin/String|): R|kotlin/String| {
lval c: R|Derived| = R|/Derived.Derived|()
lval j: R|kotlinx/serialization/json/Json| = R|kotlinx/serialization/json/Json|(<L> = Json@fun R|kotlinx/serialization/json/JsonBuilder|.<anonymous>(): R|kotlin/Unit| <inline=NoInline> {
this@R|special/anonymous|.R|kotlinx/serialization/json/JsonBuilder.encodeDefaults| = Boolean(true)
}
)
lval s: R|kotlin/String| = R|<local>/j|.R|kotlinx/serialization/json/Json.encodeToString|<R|Derived|>(Q|Derived|.R|/Derived.Companion.serializer|(), R|<local>/c|)
when () {
!=(R|<local>/s|, R|<local>/targetString|) -> {
^test R|<local>/s|
}
}
lval d: R|Derived| = R|<local>/j|.R|kotlinx/serialization/json/Json.decodeFromString|<R|Derived|>(Q|Derived|.R|/Derived.Companion.serializer|(), String({"ctor":43,"body":"43"}))
^test R|<local>/d|.R|/Parent.checkDeser|(Int(43), String(43))
^test String(OK)
}
public final fun box(): R|kotlin/String| {
^box R|/test|(String({"ctor":42,"body":"42"}))
}
@@ -0,0 +1,37 @@
// WITH_STDLIB
/**
* write$Self and synthetic constructor are required for correct serialization and deserialization of private fields,
* otherwise inaccessible from e.g. Derived.$serializer.serialize().
* This test verifies that.
*/
import kotlinx.serialization.*
import kotlinx.serialization.json.*
@Serializable
open class Parent(private val ctor: Int) {
private var body: String = "42"
fun checkDeser(c: Int, b: String): String {
if (this.ctor != c) return "Ctor : ${this.ctor}"
if (this.body != b) return "Body : ${this.body}"
return "OK"
}
}
@Serializable
class Derived: Parent(42)
fun test(targetString: String): String {
val c = Derived()
val j = Json { encodeDefaults = true }
val s = j.encodeToString(Derived.serializer(), c)
if (s != targetString) return s
val d = j.decodeFromString(Derived.serializer(), """{"ctor":43,"body":"43"}""")
return d.checkDeser(43, "43")
return "OK"
}
fun box(): String {
return test("""{"ctor":42,"body":"42"}""")
}