Support full pipeline for basic kotlinx.serialization case:

FIR frontend and IR backend plugins.

Note: IR plugin is now broken for old FE, fixed in subsequent commits
This commit is contained in:
Leonid Startsev
2022-07-22 19:09:01 +02:00
committed by Space
parent cefc372632
commit 467ff30aac
18 changed files with 324 additions and 27 deletions
@@ -148,6 +148,7 @@ class Fir2IrPluginContext(private val components: Fir2IrComponents) : IrPluginCo
error(ERROR_MESSAGE)
}
@Deprecated("Use classId overload instead")
override fun referenceConstructors(classFqn: FqName): Collection<IrConstructorSymbol> {
error(ERROR_MESSAGE)
}
@@ -54,6 +54,7 @@ interface IrPluginContext : IrGeneratorContext {
@Deprecated("Use classId overload instead")
fun referenceClass(fqName: FqName): IrClassSymbol?
fun referenceTypeAlias(fqName: FqName): IrTypeAliasSymbol?
@Deprecated("Use classId overload instead")
fun referenceConstructors(classFqn: FqName): Collection<IrConstructorSymbol>
@Deprecated("Use callableId overload instead")
fun referenceFunctions(fqName: FqName): Collection<IrSimpleFunctionSymbol>
@@ -121,6 +121,7 @@ open class IrPluginContextImpl constructor(
}
}
@Deprecated("Use classId overload instead")
override fun referenceConstructors(classFqn: FqName): Collection<IrConstructorSymbol> {
@Suppress("DEPRECATION")
val classSymbol = referenceClass(classFqn) ?: error("Cannot find class $classFqn")
@@ -157,6 +158,7 @@ open class IrPluginContextImpl constructor(
}
override fun referenceConstructors(classId: ClassId): Collection<IrConstructorSymbol> {
@Suppress("DEPRECATION")
return referenceConstructors(classId.asSingleFqName())
}
@@ -294,6 +294,7 @@ internal fun IrPluginContext.getArrayConstructorSymbol(
): IrConstructorSymbol {
val jsArrayName = irType.getArrayClassFqName()
return try {
@Suppress("DEPRECATION") // caution: referenceConstructors(fqName) doesn't work with FIR
referenceConstructors(jsArrayName).single(predicate)
} catch (e: RuntimeException) {
error("Array constructor $jsArrayName matching the predicate was not found in the context")
@@ -10,6 +10,9 @@ import org.jetbrains.kotlin.codegen.CompilationException
import org.jetbrains.kotlin.descriptors.*
import org.jetbrains.kotlin.ir.declarations.*
import org.jetbrains.kotlin.ir.expressions.IrConstructorCall
import org.jetbrains.kotlin.ir.expressions.IrExpression
import org.jetbrains.kotlin.ir.expressions.IrStatementOrigin
import org.jetbrains.kotlin.ir.expressions.impl.IrGetValueImpl
import org.jetbrains.kotlin.ir.symbols.IrClassSymbol
import org.jetbrains.kotlin.ir.symbols.IrTypeParameterSymbol
import org.jetbrains.kotlin.ir.types.*
@@ -323,4 +326,6 @@ internal fun IrConstructor.isSerializationCtor(): Boolean {
SerialEntityNames.SERIAL_CTOR_MARKER_NAME
)
} == true
}
}
internal fun IrExpression.isInitializePropertyFromParameter(): Boolean = this is IrGetValueImpl && this.origin == IrStatementOrigin.INITIALIZE_PROPERTY_FROM_PARAMETER
@@ -742,12 +742,12 @@ interface IrBuilderExtension {
fun IrBuilderWithScope.classReference(classSymbol: IrType): IrClassReference =
createClassReference(classSymbol, startOffset, endOffset)
private fun extractDefaultValuesFromConstructor(irClass: IrClass?): Map<ParameterDescriptor, IrExpression?> {
private fun extractDefaultValuesFromConstructor(irClass: IrClass?): Map<IrValueSymbol, IrExpression?> {
if (irClass == null) return emptyMap()
val original = irClass.constructors.singleOrNull { it.isPrimary }
// default arguments of original constructor
val defaultsMap: Map<ParameterDescriptor, IrExpression?> =
original?.valueParameters?.associate { it.descriptor to it.defaultValue?.expression } ?: emptyMap()
val defaultsMap: Map<IrValueSymbol, IrExpression?> =
original?.valueParameters?.associate { it.symbol to it.defaultValue?.expression } ?: emptyMap()
return defaultsMap + extractDefaultValuesFromConstructor(irClass.getSuperClassNotAny())
}
@@ -783,9 +783,9 @@ interface IrBuilderExtension {
return fun(initializer: IrExpressionBody): IrExpression {
val rawExpression = initializer.expression
val expression =
if (rawExpression is IrGetValueImpl && rawExpression.origin == IrStatementOrigin.INITIALIZE_PROPERTY_FROM_PARAMETER) {
if (rawExpression.isInitializePropertyFromParameter()) {
// this is a primary constructor property, use corresponding default of value parameter
defaultsMap.getValue(rawExpression.symbol.descriptor as ParameterDescriptor)!!
defaultsMap.getValue((rawExpression as IrGetValue).symbol)!!
} else {
rawExpression
}
@@ -1252,8 +1252,8 @@ interface IrBuilderExtension {
} == true
}
fun serializableSyntheticConstructor(forClass: IrClass): IrConstructorSymbol {
return forClass.declarations.filterIsInstance<IrConstructor>().single { it.isSerializationCtor() }.symbol
fun serializableSyntheticConstructor(forClass: IrClass): IrConstructorSymbol? { // todo: remove this W/A when FIR plugin will add proper ctor
return forClass.declarations.filterIsInstance<IrConstructor>().singleOrNull { it.isSerializationCtor() }?.symbol
}
fun IrClass.getSuperClassOrAny(): IrClass = getSuperClassNotAny() ?: compilerContext.irBuiltIns.anyClass.owner
@@ -105,8 +105,7 @@ class SerializableIrGenerator(
}
// Missing field exception parts
val exceptionFqn = getSerializationPackageFqn(MISSING_FIELD_EXC)
val exceptionCtorRef = compilerContext.referenceConstructors(exceptionFqn)
val exceptionCtorRef = compilerContext.referenceConstructors(ClassId(SerializationPackages.packageFqName, Name.identifier(MISSING_FIELD_EXC)))
.single { it.owner.valueParameters.singleOrNull()?.type?.isString() == true }
val exceptionType = exceptionCtorRef.owner.returnType
@@ -230,8 +229,8 @@ class SerializableIrGenerator(
}
private fun IrBlockBodyBuilder.getInstantiateDescriptorExpr(): IrExpression {
val classConstructors = compilerContext.referenceConstructors(serialDescriptorImplClass.kotlinFqName) //todo: remove reference
val serialClassDescImplCtor = classConstructors.single { it.owner.isPrimary }
val classConstructors = serialDescriptorImplClass.constructors //todo: remove reference
val serialClassDescImplCtor = classConstructors.single { it.isPrimary }.symbol
return irInvoke(
null, serialClassDescImplCtor,
irString(irClass.serialName()), irNull(), irInt(properties.serializableProperties.size)
@@ -282,7 +281,7 @@ class SerializableIrGenerator(
propertiesStart: Int
): Int {
check(superClass.isInternalSerializable)
val superCtorRef = serializableSyntheticConstructor(superClass)
val superCtorRef = serializableSyntheticConstructor(superClass)!!
val superProperties = bindingContext.serializablePropertiesFor(superClass.descriptor).serializableProperties
val superSlots = superProperties.bitMaskSlotCount()
val arguments = allValueParameters.subList(0, superSlots) +
@@ -23,6 +23,8 @@ import org.jetbrains.kotlin.ir.symbols.IrFunctionSymbol
import org.jetbrains.kotlin.ir.symbols.impl.IrAnonymousInitializerSymbolImpl
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.resolve.calls.components.hasDefaultValue
import org.jetbrains.kotlin.resolve.isInlineClass
import org.jetbrains.kotlin.util.OperatorNameConventions
@@ -34,6 +36,7 @@ import org.jetbrains.kotlinx.serialization.compiler.fir.SerializationPluginKey
import org.jetbrains.kotlinx.serialization.compiler.resolve.*
import org.jetbrains.kotlinx.serialization.compiler.resolve.SerialEntityNames.DECODER_CLASS
import org.jetbrains.kotlinx.serialization.compiler.resolve.SerialEntityNames.ENCODER_CLASS
import org.jetbrains.kotlinx.serialization.compiler.resolve.SerialEntityNames.LOAD
import org.jetbrains.kotlinx.serialization.compiler.resolve.SerialEntityNames.SAVE
import org.jetbrains.kotlinx.serialization.compiler.resolve.SerialEntityNames.STRUCTURE_DECODER_CLASS
import org.jetbrains.kotlinx.serialization.compiler.resolve.SerialEntityNames.STRUCTURE_ENCODER_CLASS
@@ -458,8 +461,7 @@ open class SerializerIrGenerator(
// throw exception on unknown field
val exceptionFqn = getSerializationPackageFqn(UNKNOWN_FIELD_EXC)
val excClassRef = compilerContext.referenceConstructors(exceptionFqn)
val excClassRef = compilerContext.referenceConstructors(ClassId(SerializationPackages.packageFqName, Name.identifier(UNKNOWN_FIELD_EXC)))
.single { it.owner.valueParameters.singleOrNull()?.type?.isInt() == true }
+elseBranch(
irThrow(
@@ -485,11 +487,11 @@ open class SerializerIrGenerator(
)
val typeArgs = (loadFunc.returnType as IrSimpleType).arguments.map { (it as IrTypeProjection).type }
if (serializableIrClass.isInternalSerializable) {
val deserCtor: IrConstructorSymbol? = serializableSyntheticConstructor(serializableIrClass)
if (serializableIrClass.isInternalSerializable && deserCtor != null) {
var args: List<IrExpression> = serializableProperties.map { serialPropertiesMap.getValue(it.descriptor).get() }
args = bitMasks.map { irGet(it) } + args + irNull()
val ctor: IrConstructorSymbol = serializableSyntheticConstructor(serializableIrClass)
+irReturn(irInvoke(null, ctor, typeArgs, args))
+irReturn(irInvoke(null, deserCtor, typeArgs, args))
} else {
if (irClass.isLocal) {
// if the serializer is local, then the serializable class too, since they must be in the same scope
@@ -592,7 +594,7 @@ open class SerializerIrGenerator(
if (prop)
generateSerialDesc()
val save = irClass.findPluginGeneratedMethod(SAVE)?.let { generateSave(it); true } ?: false
val load = irClass.findPluginGeneratedMethod(SAVE)?.let { generateSave(it); true } ?: false
val load = irClass.findPluginGeneratedMethod(LOAD)?.let { generateLoad(it); true } ?: false
irClass.findPluginGeneratedMethod(SerialEntityNames.CHILD_SERIALIZERS_GETTER.identifier)?.let { generateChildSerializersGetter(it) }
irClass.findPluginGeneratedMethod(SerialEntityNames.TYPE_PARAMS_SERIALIZERS_GETTER.identifier)?.let { generateTypeParamsSerializersGetter(it) }
if (!prop && (save || load))
@@ -28,6 +28,7 @@ import org.jetbrains.kotlin.serialization.deserialization.descriptors.Deserializ
import org.jetbrains.kotlin.serialization.deserialization.getName
import org.jetbrains.kotlin.types.KotlinType
import org.jetbrains.kotlin.types.checker.SimpleClassicTypeSystemContext.isInterface
import org.jetbrains.kotlinx.serialization.compiler.backend.common.isInitializePropertyFromParameter
import org.jetbrains.kotlinx.serialization.compiler.backend.common.isInternalSerializable
import org.jetbrains.kotlinx.serialization.compiler.backend.common.isInternallySerializableEnum
import org.jetbrains.kotlinx.serialization.compiler.diagnostic.SERIALIZABLE_PROPERTIES
@@ -227,9 +228,10 @@ internal fun BindingContext?.serializablePropertiesForIrBackend(
it,
isConstructorParameterWithDefault,
it.backingField != null,
it.backingField?.initializer != null || isConstructorParameterWithDefault
it.backingField?.initializer.let { init -> init != null && !init.expression.isInitializePropertyFromParameter() } || isConstructorParameterWithDefault
)
}
.filterNot { it.transient }
.partition { primaryParamsAsProps.contains(it.descriptor) }
val serializableProps = run {
@@ -26,6 +26,8 @@ import org.jetbrains.kotlinx.serialization.compiler.fir.FirSerializationExtensio
import java.io.File
abstract class AbstractSerializationFirMembersTest: AbstractKotlinCompilerTest() {
private val librariesList = listOf(getSerializationCoreLibraryJar()!!, getSerializationLibraryJar("kotlinx.serialization.json.Json")!!)
override fun TestConfigurationBuilder.configuration() {
baseFirDiagnosticTestConfiguration()
@@ -34,22 +36,25 @@ abstract class AbstractSerializationFirMembersTest: AbstractKotlinCompilerTest()
+FirDiagnosticsDirectives.FIR_DUMP
}
configureForKotlinxSerialization(listOf(getSerializationCoreLibraryJar()!!)) {
configureForKotlinxSerialization(librariesList) {
FirExtensionRegistrarAdapter.registerExtension(FirSerializationExtensionRegistrar())
}
}
}
open class AbstractSerializationFirBlackBoxTest : AbstractKotlinCompilerWithTargetBackendTest(TargetBackend.JVM_IR) {
private val librariesList = listOf(getSerializationCoreLibraryJar()!!, getSerializationLibraryJar("kotlinx.serialization.json.Json")!!)
override fun TestConfigurationBuilder.configuration() {
baseFirDiagnosticTestConfiguration()
configureForKotlinxSerialization(listOf(getSerializationCoreLibraryJar()!!)) {
configureForKotlinxSerialization(librariesList) {
FirExtensionRegistrarAdapter.registerExtension(FirSerializationExtensionRegistrar())
}
useCustomRuntimeClasspathProviders({ ts ->
object : RuntimeClasspathProvider(ts) {
override fun runtimeClassPaths(module: TestModule): List<File> {
return listOf(getSerializationCoreLibraryJar()!!)
return librariesList
}
}
})
@@ -30,4 +30,16 @@ public class SerializationFirBlackBoxTestGenerated extends AbstractSerialization
public void testClassWithCompanionObject() throws Exception {
runTest("plugins/kotlin-serialization/kotlin-serialization-compiler/testData/firMembers/classWithCompanionObject.kt");
}
@Test
@TestMetadata("defaultProperties.kt")
public void testDefaultProperties() throws Exception {
runTest("plugins/kotlin-serialization/kotlin-serialization-compiler/testData/firMembers/defaultProperties.kt");
}
@Test
@TestMetadata("multipleProperties.kt")
public void testMultipleProperties() throws Exception {
runTest("plugins/kotlin-serialization/kotlin-serialization-compiler/testData/firMembers/multipleProperties.kt");
}
}
@@ -29,4 +29,16 @@ public class SerializationFirMembersTestGenerated extends AbstractSerializationF
public void testClassWithCompanionObject() throws Exception {
runTest("plugins/kotlin-serialization/kotlin-serialization-compiler/testData/firMembers/classWithCompanionObject.kt");
}
@Test
@TestMetadata("defaultProperties.kt")
public void testDefaultProperties() throws Exception {
runTest("plugins/kotlin-serialization/kotlin-serialization-compiler/testData/firMembers/defaultProperties.kt");
}
@Test
@TestMetadata("multipleProperties.kt")
public void testMultipleProperties() throws Exception {
runTest("plugins/kotlin-serialization/kotlin-serialization-compiler/testData/firMembers/multipleProperties.kt");
}
}
@@ -1,9 +1,12 @@
FILE: classWithCompanionObject.kt
@R|kotlinx/serialization/Serializable|() public final class SomeClass : R|kotlin/Any| {
public constructor(): R|SomeClass| {
public constructor(i: R|kotlin/Int|): R|SomeClass| {
super<R|kotlin/Any|>()
}
public final val i: R|kotlin/Int| = R|<local>/i|
public get(): R|kotlin/Int|
public final companion object Companion : R|kotlin/Any| {
public final fun serializer(): R|kotlinx/serialization/KSerializer<SomeClass>|
@@ -27,5 +30,29 @@ FILE: classWithCompanionObject.kt
}
public final fun box(): R|kotlin/String| {
lval targetString: R|kotlin/String| = String({"i":42})
lval serializer: R|kotlinx/serialization/KSerializer<SomeClass>| = Q|SomeClass.Companion|.R|/SomeClass.Companion.serializer|()
lval descriptor: R|kotlinx/serialization/descriptors/SerialDescriptor| = R|<local>/serializer|.R|SubstitutionOverride<kotlinx/serialization/KSerializer.descriptor: R|kotlinx/serialization/descriptors/SerialDescriptor|>|
when () {
!=(R|<local>/descriptor|.R|kotlin/Any.toString|(), String(SomeClass(i: kotlin.Int))) -> {
^box <strcat>(String(Incorrect SerialDescriptor.toString(): ), R|<local>/descriptor|)
}
}
lval instance: R|SomeClass| = R|/SomeClass.SomeClass|(Int(42))
lval string: R|kotlin/String| = Q|kotlinx/serialization/json/Json|.R|kotlinx/serialization/json/Json.encodeToString|<R|SomeClass|>(R|<local>/serializer|, R|<local>/instance|)
when () {
!=(R|<local>/string|, R|<local>/targetString|) -> {
^box <strcat>(String(Incorrect serialization result: expected ), R|<local>/targetString|, String(, got ), R|<local>/string|)
}
}
lval instance2: R|SomeClass| = Q|kotlinx/serialization/json/Json|.R|kotlinx/serialization/json/Json.decodeFromString|<R|SomeClass|>(R|<local>/serializer|, R|<local>/string|)
when () {
!=(R|<local>/instance2|.R|/SomeClass.i|, R|<local>/instance|.R|/SomeClass.i|) -> {
^box <strcat>(String(Incorrect deserialization result: expected ), R|<local>/instance|.R|/SomeClass.i|, String(, got ), R|<local>/instance2|.R|/SomeClass.i|)
}
}
^box String(OK)
}
@@ -1,11 +1,21 @@
// WITH_STDLIB
import kotlinx.serialization.*
import kotlinx.serialization.json.*
@Serializable
class SomeClass
class SomeClass(val i: Int)
fun box(): String {
// SomeClass.serializer()
// SomeClass.Companion.serializer()
val targetString = """{"i":42}"""
val serializer = SomeClass.Companion.serializer()
val descriptor = serializer.descriptor
if (descriptor.toString() != "SomeClass(i: kotlin.Int)") return "Incorrect SerialDescriptor.toString(): $descriptor"
val instance = SomeClass(42)
val string = Json.encodeToString(serializer, instance)
if (string != targetString) return "Incorrect serialization result: expected $targetString, got $string"
val instance2 = Json.decodeFromString(serializer, string)
if (instance2.i != instance.i) return "Incorrect deserialization result: expected ${instance.i}, got ${instance2.i}"
return "OK"
}
@@ -0,0 +1,94 @@
FILE: defaultProperties.kt
@R|kotlinx/serialization/Serializable|() public final data class SomeClass : R|kotlin/Any| {
public constructor(ctor: R|kotlin/Int| = Int(42), ctorDependent: R|kotlin/Int| = R|<local>/ctor|.R|kotlin/Int.plus|(Int(1))): R|SomeClass| {
super<R|kotlin/Any|>()
}
public final val ctor: R|kotlin/Int| = R|<local>/ctor|
public get(): R|kotlin/Int|
public final val ctorDependent: R|kotlin/Int| = R|<local>/ctorDependent|
public get(): R|kotlin/Int|
public final var body: R|kotlin/String| = String()
public get(): R|kotlin/String|
public set(value: R|kotlin/String|): R|kotlin/Unit|
public final var bodyDependent: R|kotlin/Int| = this@R|/SomeClass|.R|/SomeClass.ctor|.R|kotlin/Int.plus|(Int(2))
public get(): R|kotlin/Int|
public set(value: R|kotlin/Int|): R|kotlin/Unit|
public final operator fun component1(): R|kotlin/Int|
public final operator fun component2(): R|kotlin/Int|
public final fun copy(ctor: R|kotlin/Int| = this@R|/SomeClass|.R|/SomeClass.ctor|, ctorDependent: R|kotlin/Int| = this@R|/SomeClass|.R|/SomeClass.ctorDependent|): R|SomeClass|
public final companion object Companion : R|kotlin/Any| {
public final fun serializer(): R|kotlinx/serialization/KSerializer<SomeClass>|
public constructor(): R|SomeClass.Companion|
}
public final object $serializer : R|kotlinx/serialization/internal/GeneratedSerializer<SomeClass>| {
public final fun serialize(encoder: R|kotlinx/serialization/encoding/Encoder|, value: R|SomeClass|): R|kotlin/Unit|
public final fun deserialize(decoder: R|kotlinx/serialization/encoding/Decoder|): R|SomeClass|
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<*>>|
public constructor(): R|SomeClass.$serializer|
}
}
public final fun test(targetString: R|kotlin/String|): R|kotlin/String| {
lval json: 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 c: R|SomeClass| = R|/SomeClass.SomeClass|(Int(1)).R|kotlin/apply|<R|SomeClass|>(<L> = apply@fun R|SomeClass|.<anonymous>(): R|kotlin/Unit| <inline=Inline, kind=EXACTLY_ONCE> {
this@R|special/anonymous|.R|/SomeClass.body| = String(x)
}
)
lval s: R|kotlin/String| = R|<local>/json|.R|kotlinx/serialization/json/Json.encodeToString|<R|SomeClass|>(Q|SomeClass|.R|/SomeClass.Companion.serializer|(), R|<local>/c|)
when () {
!=(R|<local>/s|, R|<local>/targetString|) -> {
^test R|<local>/s|
}
}
lval i2: R|SomeClass| = R|<local>/json|.R|kotlinx/serialization/json/Json.decodeFromString|<R|SomeClass|>(Q|SomeClass|.R|/SomeClass.Companion.serializer|(), String({}))
when () {
!=(R|<local>/i2|.R|/SomeClass.ctor|, Int(42)) -> {
^test String(Incorrect default ctor)
}
}
when () {
!=(R|<local>/i2|.R|/SomeClass.ctorDependent|, Int(43)) -> {
^test String(Incorrect default ctorDependent)
}
}
when () {
!=(R|<local>/i2|.R|/SomeClass.body|, String()) -> {
^test String(Incorrect default body)
}
}
when () {
!=(R|<local>/i2|.R|/SomeClass.bodyDependent|, Int(44)) -> {
^test String(Incorrect default bodyDependent)
}
}
^test String(OK)
}
public final fun box(): R|kotlin/String| {
^box R|/test|(String({"ctor":1,"ctorDependent":2,"body":"x","bodyDependent":3}))
}
@@ -0,0 +1,28 @@
// WITH_STDLIB
import kotlinx.serialization.*
import kotlinx.serialization.json.*
@Serializable
data class SomeClass(val ctor: Int = 42, val ctorDependent: Int = ctor + 1) {
var body: String = ""
var bodyDependent = ctor + 2
}
fun test(targetString: String): String {
val json = Json { encodeDefaults = true }
val c = SomeClass(1).apply { body = "x" }
val s = json.encodeToString(SomeClass.serializer(), c)
if (s != targetString) return s
val i2 = json.decodeFromString(SomeClass.serializer(), "{}")
if (i2.ctor != 42) return "Incorrect default ctor"
if (i2.ctorDependent != 43) return "Incorrect default ctorDependent"
if (i2.body != "") return "Incorrect default body"
if (i2.bodyDependent != 44) return "Incorrect default bodyDependent"
return "OK"
}
fun box(): String {
return test("""{"ctor":1,"ctorDependent":2,"body":"x","bodyDependent":3}""")
}
@@ -0,0 +1,70 @@
FILE: multipleProperties.kt
@R|kotlinx/serialization/Serializable|() public final class SomeClass : R|kotlin/Any| {
public constructor(ctor: R|kotlin/Int|): R|SomeClass| {
super<R|kotlin/Any|>()
}
public final val ctor: R|kotlin/Int| = R|<local>/ctor|
public get(): R|kotlin/Int|
public final var body: R|kotlin/String| = String()
public get(): R|kotlin/String|
public set(value: R|kotlin/String|): R|kotlin/Unit|
public final val getter: R|kotlin/Int|
public get(): R|kotlin/Int| {
^ Int(42)
}
public final companion object Companion : R|kotlin/Any| {
public final fun serializer(): R|kotlinx/serialization/KSerializer<SomeClass>|
public constructor(): R|SomeClass.Companion|
}
public final object $serializer : R|kotlinx/serialization/internal/GeneratedSerializer<SomeClass>| {
public final fun serialize(encoder: R|kotlinx/serialization/encoding/Encoder|, value: R|SomeClass|): R|kotlin/Unit|
public final fun deserialize(decoder: R|kotlinx/serialization/encoding/Decoder|): R|SomeClass|
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<*>>|
public constructor(): R|SomeClass.$serializer|
}
}
public final fun test(targetString: R|kotlin/String|): R|kotlin/String| {
lval c: R|SomeClass| = R|/SomeClass.SomeClass|(Int(1)).R|kotlin/apply|<R|SomeClass|>(<L> = apply@fun R|SomeClass|.<anonymous>(): R|kotlin/Unit| <inline=Inline, kind=EXACTLY_ONCE> {
this@R|special/anonymous|.R|/SomeClass.body| = String(x)
}
)
lval s: R|kotlin/String| = Q|kotlinx/serialization/json/Json|.R|kotlinx/serialization/json/Json.encodeToString|<R|SomeClass|>(Q|SomeClass|.R|/SomeClass.Companion.serializer|(), R|<local>/c|)
when () {
!=(R|<local>/s|, R|<local>/targetString|) -> {
^test R|<local>/s|
}
}
lval i: R|SomeClass| = Q|kotlinx/serialization/json/Json|.R|kotlinx/serialization/json/Json.decodeFromString|<R|SomeClass|>(Q|SomeClass|.R|/SomeClass.Companion.serializer|(), R|<local>/s|)
when () {
!=(R|<local>/i|.R|/SomeClass.ctor|, R|<local>/c|.R|/SomeClass.ctor|) -> {
^test String(Incorrect ctor)
}
}
when () {
!=(R|<local>/i|.R|/SomeClass.body|, R|<local>/c|.R|/SomeClass.body|) -> {
^test String(Incorrect body)
}
}
^test String(OK)
}
public final fun box(): R|kotlin/String| {
^box R|/test|(String({"ctor":1,"body":"x"}))
}
@@ -0,0 +1,26 @@
// WITH_STDLIB
import kotlinx.serialization.*
import kotlinx.serialization.json.*
@Serializable
class SomeClass(val ctor: Int) {
var body: String = ""
// Not serializable: no backing field
val getter: Int get() = 42
}
fun test(targetString: String): String {
val c = SomeClass(1).apply { body = "x" }
val s = Json.encodeToString(SomeClass.serializer(), c)
if (s != targetString) return s
val i = Json.decodeFromString(SomeClass.serializer(), s)
if (i.ctor != c.ctor) return "Incorrect ctor"
if (i.body != c.body) return "Incorrect body"
return "OK"
}
fun box(): String {
return test("""{"ctor":1,"body":"x"}""")
}