From 467ff30aacaca5d85237766d85341ec7482adf8a Mon Sep 17 00:00:00 2001 From: Leonid Startsev Date: Fri, 22 Jul 2022 19:09:01 +0200 Subject: [PATCH] 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 --- .../kotlin/fir/backend/Fir2IrPluginContext.kt | 1 + .../common/extensions/IrPluginContext.kt | 1 + .../common/extensions/IrPluginContextImpl.kt | 2 + .../compiler/backend/TransformerUtil.kt | 1 + .../compiler/backend/common/TypeUtilIr.kt | 7 +- .../compiler/backend/ir/GeneratorHelpers.kt | 14 +-- .../backend/ir/SerializableIrGenerator.kt | 9 +- .../backend/ir/SerializerIrGenerator.kt | 14 +-- .../resolve/SerializableProperties.kt | 4 +- .../AbstractSerializationFirMembersTest.kt | 11 ++- ...SerializationFirBlackBoxTestGenerated.java | 12 +++ .../SerializationFirMembersTestGenerated.java | 12 +++ .../classWithCompanionObject.fir.txt | 29 +++++- .../firMembers/classWithCompanionObject.kt | 16 +++- .../firMembers/defaultProperties.fir.txt | 94 +++++++++++++++++++ .../testData/firMembers/defaultProperties.kt | 28 ++++++ .../firMembers/multipleProperties.fir.txt | 70 ++++++++++++++ .../testData/firMembers/multipleProperties.kt | 26 +++++ 18 files changed, 324 insertions(+), 27 deletions(-) create mode 100644 plugins/kotlin-serialization/kotlin-serialization-compiler/testData/firMembers/defaultProperties.fir.txt create mode 100644 plugins/kotlin-serialization/kotlin-serialization-compiler/testData/firMembers/defaultProperties.kt create mode 100644 plugins/kotlin-serialization/kotlin-serialization-compiler/testData/firMembers/multipleProperties.fir.txt create mode 100644 plugins/kotlin-serialization/kotlin-serialization-compiler/testData/firMembers/multipleProperties.kt diff --git a/compiler/fir/fir2ir/src/org/jetbrains/kotlin/fir/backend/Fir2IrPluginContext.kt b/compiler/fir/fir2ir/src/org/jetbrains/kotlin/fir/backend/Fir2IrPluginContext.kt index 489d07efec7..e5fda0fbfe4 100644 --- a/compiler/fir/fir2ir/src/org/jetbrains/kotlin/fir/backend/Fir2IrPluginContext.kt +++ b/compiler/fir/fir2ir/src/org/jetbrains/kotlin/fir/backend/Fir2IrPluginContext.kt @@ -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 { error(ERROR_MESSAGE) } diff --git a/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/extensions/IrPluginContext.kt b/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/extensions/IrPluginContext.kt index fdea8bf3492..24afe174750 100644 --- a/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/extensions/IrPluginContext.kt +++ b/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/extensions/IrPluginContext.kt @@ -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 @Deprecated("Use callableId overload instead") fun referenceFunctions(fqName: FqName): Collection diff --git a/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/extensions/IrPluginContextImpl.kt b/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/extensions/IrPluginContextImpl.kt index a9c8759c485..711ec84923b 100644 --- a/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/extensions/IrPluginContextImpl.kt +++ b/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/extensions/IrPluginContextImpl.kt @@ -121,6 +121,7 @@ open class IrPluginContextImpl constructor( } } + @Deprecated("Use classId overload instead") override fun referenceConstructors(classFqn: FqName): Collection { @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 { + @Suppress("DEPRECATION") return referenceConstructors(classId.asSingleFqName()) } diff --git a/plugins/atomicfu/atomicfu-compiler/src/org/jetbrains/kotlinx/atomicfu/compiler/backend/TransformerUtil.kt b/plugins/atomicfu/atomicfu-compiler/src/org/jetbrains/kotlinx/atomicfu/compiler/backend/TransformerUtil.kt index 1b17468df61..87f39dbedf2 100644 --- a/plugins/atomicfu/atomicfu-compiler/src/org/jetbrains/kotlinx/atomicfu/compiler/backend/TransformerUtil.kt +++ b/plugins/atomicfu/atomicfu-compiler/src/org/jetbrains/kotlinx/atomicfu/compiler/backend/TransformerUtil.kt @@ -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") diff --git a/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/common/TypeUtilIr.kt b/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/common/TypeUtilIr.kt index cfccde7d9d4..9e7a1376d5f 100644 --- a/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/common/TypeUtilIr.kt +++ b/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/common/TypeUtilIr.kt @@ -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 -} \ No newline at end of file +} + +internal fun IrExpression.isInitializePropertyFromParameter(): Boolean = this is IrGetValueImpl && this.origin == IrStatementOrigin.INITIALIZE_PROPERTY_FROM_PARAMETER \ No newline at end of file diff --git a/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/ir/GeneratorHelpers.kt b/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/ir/GeneratorHelpers.kt index 8a6e37484b6..5831fe9727b 100644 --- a/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/ir/GeneratorHelpers.kt +++ b/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/ir/GeneratorHelpers.kt @@ -742,12 +742,12 @@ interface IrBuilderExtension { fun IrBuilderWithScope.classReference(classSymbol: IrType): IrClassReference = createClassReference(classSymbol, startOffset, endOffset) - private fun extractDefaultValuesFromConstructor(irClass: IrClass?): Map { + private fun extractDefaultValuesFromConstructor(irClass: IrClass?): Map { if (irClass == null) return emptyMap() val original = irClass.constructors.singleOrNull { it.isPrimary } // default arguments of original constructor - val defaultsMap: Map = - original?.valueParameters?.associate { it.descriptor to it.defaultValue?.expression } ?: emptyMap() + val defaultsMap: Map = + 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().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().singleOrNull { it.isSerializationCtor() }?.symbol } fun IrClass.getSuperClassOrAny(): IrClass = getSuperClassNotAny() ?: compilerContext.irBuiltIns.anyClass.owner diff --git a/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/ir/SerializableIrGenerator.kt b/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/ir/SerializableIrGenerator.kt index 66e7ed5269c..43210c67a0e 100644 --- a/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/ir/SerializableIrGenerator.kt +++ b/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/ir/SerializableIrGenerator.kt @@ -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) + diff --git a/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/ir/SerializerIrGenerator.kt b/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/ir/SerializerIrGenerator.kt index 4f4161f5933..ac5b8583602 100644 --- a/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/ir/SerializerIrGenerator.kt +++ b/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/ir/SerializerIrGenerator.kt @@ -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 = 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)) diff --git a/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/resolve/SerializableProperties.kt b/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/resolve/SerializableProperties.kt index 5edff32aed9..5764cc1cc30 100644 --- a/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/resolve/SerializableProperties.kt +++ b/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/resolve/SerializableProperties.kt @@ -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 { diff --git a/plugins/kotlin-serialization/kotlin-serialization-compiler/test/org/jetbrains/kotlinx/serialization/AbstractSerializationFirMembersTest.kt b/plugins/kotlin-serialization/kotlin-serialization-compiler/test/org/jetbrains/kotlinx/serialization/AbstractSerializationFirMembersTest.kt index 036c80310a3..f6951e5d27c 100644 --- a/plugins/kotlin-serialization/kotlin-serialization-compiler/test/org/jetbrains/kotlinx/serialization/AbstractSerializationFirMembersTest.kt +++ b/plugins/kotlin-serialization/kotlin-serialization-compiler/test/org/jetbrains/kotlinx/serialization/AbstractSerializationFirMembersTest.kt @@ -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 { - return listOf(getSerializationCoreLibraryJar()!!) + return librariesList } } }) diff --git a/plugins/kotlin-serialization/kotlin-serialization-compiler/test/org/jetbrains/kotlinx/serialization/SerializationFirBlackBoxTestGenerated.java b/plugins/kotlin-serialization/kotlin-serialization-compiler/test/org/jetbrains/kotlinx/serialization/SerializationFirBlackBoxTestGenerated.java index b293201c499..3f56587a15a 100644 --- a/plugins/kotlin-serialization/kotlin-serialization-compiler/test/org/jetbrains/kotlinx/serialization/SerializationFirBlackBoxTestGenerated.java +++ b/plugins/kotlin-serialization/kotlin-serialization-compiler/test/org/jetbrains/kotlinx/serialization/SerializationFirBlackBoxTestGenerated.java @@ -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"); + } } diff --git a/plugins/kotlin-serialization/kotlin-serialization-compiler/test/org/jetbrains/kotlinx/serialization/SerializationFirMembersTestGenerated.java b/plugins/kotlin-serialization/kotlin-serialization-compiler/test/org/jetbrains/kotlinx/serialization/SerializationFirMembersTestGenerated.java index 600ded809b8..422688c265b 100644 --- a/plugins/kotlin-serialization/kotlin-serialization-compiler/test/org/jetbrains/kotlinx/serialization/SerializationFirMembersTestGenerated.java +++ b/plugins/kotlin-serialization/kotlin-serialization-compiler/test/org/jetbrains/kotlinx/serialization/SerializationFirMembersTestGenerated.java @@ -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"); + } } diff --git a/plugins/kotlin-serialization/kotlin-serialization-compiler/testData/firMembers/classWithCompanionObject.fir.txt b/plugins/kotlin-serialization/kotlin-serialization-compiler/testData/firMembers/classWithCompanionObject.fir.txt index 4972dc86019..b2df063dd82 100644 --- a/plugins/kotlin-serialization/kotlin-serialization-compiler/testData/firMembers/classWithCompanionObject.fir.txt +++ b/plugins/kotlin-serialization/kotlin-serialization-compiler/testData/firMembers/classWithCompanionObject.fir.txt @@ -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() } + public final val i: R|kotlin/Int| = R|/i| + public get(): R|kotlin/Int| + public final companion object Companion : R|kotlin/Any| { public final fun serializer(): R|kotlinx/serialization/KSerializer| @@ -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| = Q|SomeClass.Companion|.R|/SomeClass.Companion.serializer|() + lval descriptor: R|kotlinx/serialization/descriptors/SerialDescriptor| = R|/serializer|.R|SubstitutionOverride| + when () { + !=(R|/descriptor|.R|kotlin/Any.toString|(), String(SomeClass(i: kotlin.Int))) -> { + ^box (String(Incorrect SerialDescriptor.toString(): ), R|/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|/serializer|, R|/instance|) + when () { + !=(R|/string|, R|/targetString|) -> { + ^box (String(Incorrect serialization result: expected ), R|/targetString|, String(, got ), R|/string|) + } + } + + lval instance2: R|SomeClass| = Q|kotlinx/serialization/json/Json|.R|kotlinx/serialization/json/Json.decodeFromString|(R|/serializer|, R|/string|) + when () { + !=(R|/instance2|.R|/SomeClass.i|, R|/instance|.R|/SomeClass.i|) -> { + ^box (String(Incorrect deserialization result: expected ), R|/instance|.R|/SomeClass.i|, String(, got ), R|/instance2|.R|/SomeClass.i|) + } + } + ^box String(OK) } diff --git a/plugins/kotlin-serialization/kotlin-serialization-compiler/testData/firMembers/classWithCompanionObject.kt b/plugins/kotlin-serialization/kotlin-serialization-compiler/testData/firMembers/classWithCompanionObject.kt index 7f853b4b0bf..7536eb85736 100644 --- a/plugins/kotlin-serialization/kotlin-serialization-compiler/testData/firMembers/classWithCompanionObject.kt +++ b/plugins/kotlin-serialization/kotlin-serialization-compiler/testData/firMembers/classWithCompanionObject.kt @@ -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" } diff --git a/plugins/kotlin-serialization/kotlin-serialization-compiler/testData/firMembers/defaultProperties.fir.txt b/plugins/kotlin-serialization/kotlin-serialization-compiler/testData/firMembers/defaultProperties.fir.txt new file mode 100644 index 00000000000..1b122476bec --- /dev/null +++ b/plugins/kotlin-serialization/kotlin-serialization-compiler/testData/firMembers/defaultProperties.fir.txt @@ -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|/ctor|.R|kotlin/Int.plus|(Int(1))): R|SomeClass| { + super() + } + + public final val ctor: R|kotlin/Int| = R|/ctor| + public get(): R|kotlin/Int| + + public final val ctorDependent: R|kotlin/Int| = R|/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| + + public constructor(): R|SomeClass.Companion| + + } + + public final object $serializer : R|kotlinx/serialization/internal/GeneratedSerializer| { + 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>| + + 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|( = Json@fun R|kotlinx/serialization/json/JsonBuilder|.(): R|kotlin/Unit| { + this@R|special/anonymous|.R|kotlinx/serialization/json/JsonBuilder.encodeDefaults| = Boolean(true) + } + ) + lval c: R|SomeClass| = R|/SomeClass.SomeClass|(Int(1)).R|kotlin/apply|( = apply@fun R|SomeClass|.(): R|kotlin/Unit| { + this@R|special/anonymous|.R|/SomeClass.body| = String(x) + } + ) + lval s: R|kotlin/String| = R|/json|.R|kotlinx/serialization/json/Json.encodeToString|(Q|SomeClass|.R|/SomeClass.Companion.serializer|(), R|/c|) + when () { + !=(R|/s|, R|/targetString|) -> { + ^test R|/s| + } + } + + lval i2: R|SomeClass| = R|/json|.R|kotlinx/serialization/json/Json.decodeFromString|(Q|SomeClass|.R|/SomeClass.Companion.serializer|(), String({})) + when () { + !=(R|/i2|.R|/SomeClass.ctor|, Int(42)) -> { + ^test String(Incorrect default ctor) + } + } + + when () { + !=(R|/i2|.R|/SomeClass.ctorDependent|, Int(43)) -> { + ^test String(Incorrect default ctorDependent) + } + } + + when () { + !=(R|/i2|.R|/SomeClass.body|, String()) -> { + ^test String(Incorrect default body) + } + } + + when () { + !=(R|/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})) + } diff --git a/plugins/kotlin-serialization/kotlin-serialization-compiler/testData/firMembers/defaultProperties.kt b/plugins/kotlin-serialization/kotlin-serialization-compiler/testData/firMembers/defaultProperties.kt new file mode 100644 index 00000000000..7ebd202736c --- /dev/null +++ b/plugins/kotlin-serialization/kotlin-serialization-compiler/testData/firMembers/defaultProperties.kt @@ -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}""") +} \ No newline at end of file diff --git a/plugins/kotlin-serialization/kotlin-serialization-compiler/testData/firMembers/multipleProperties.fir.txt b/plugins/kotlin-serialization/kotlin-serialization-compiler/testData/firMembers/multipleProperties.fir.txt new file mode 100644 index 00000000000..d1a49ddb9b2 --- /dev/null +++ b/plugins/kotlin-serialization/kotlin-serialization-compiler/testData/firMembers/multipleProperties.fir.txt @@ -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() + } + + public final val ctor: R|kotlin/Int| = R|/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| + + public constructor(): R|SomeClass.Companion| + + } + + public final object $serializer : R|kotlinx/serialization/internal/GeneratedSerializer| { + 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>| + + 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|( = apply@fun R|SomeClass|.(): R|kotlin/Unit| { + 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|(Q|SomeClass|.R|/SomeClass.Companion.serializer|(), R|/c|) + when () { + !=(R|/s|, R|/targetString|) -> { + ^test R|/s| + } + } + + lval i: R|SomeClass| = Q|kotlinx/serialization/json/Json|.R|kotlinx/serialization/json/Json.decodeFromString|(Q|SomeClass|.R|/SomeClass.Companion.serializer|(), R|/s|) + when () { + !=(R|/i|.R|/SomeClass.ctor|, R|/c|.R|/SomeClass.ctor|) -> { + ^test String(Incorrect ctor) + } + } + + when () { + !=(R|/i|.R|/SomeClass.body|, R|/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"})) + } diff --git a/plugins/kotlin-serialization/kotlin-serialization-compiler/testData/firMembers/multipleProperties.kt b/plugins/kotlin-serialization/kotlin-serialization-compiler/testData/firMembers/multipleProperties.kt new file mode 100644 index 00000000000..765b94af5a5 --- /dev/null +++ b/plugins/kotlin-serialization/kotlin-serialization-compiler/testData/firMembers/multipleProperties.kt @@ -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"}""") +} \ No newline at end of file