diff --git a/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/fir/SerializationFirResolveExtension.kt b/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/fir/SerializationFirResolveExtension.kt index 98aad444293..2c138a023c6 100644 --- a/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/fir/SerializationFirResolveExtension.kt +++ b/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/fir/SerializationFirResolveExtension.kt @@ -132,8 +132,10 @@ class SerializationFirResolveExtension(session: FirSession) : FirDeclarationGene @OptIn(SymbolInternals::class) override fun generateFunctions(callableId: CallableId, context: MemberGenerationContext?): List { val owner = context?.owner ?: return emptyList() - if (owner.name == SpecialNames.DEFAULT_NAME_FOR_COMPANION_OBJECT && callableId.callableName == SerialEntityNames.SERIALIZER_PROVIDER_NAME) - return listOf(generateSerializerGetterInCompanion(owner, callableId)) + if (callableId.callableName == SerialEntityNames.SERIALIZER_PROVIDER_NAME) { + val serializableClass = session.getSerializableClassDescriptorByCompanion(owner) ?: return emptyList() + return listOf(generateSerializerGetterInCompanion(owner, serializableClass, callableId)) + } if (owner.name != SerialEntityNames.SERIALIZER_CLASS_NAME) return emptyList() if (callableId.callableName !in setOf( SpecialNames.INIT, @@ -154,7 +156,7 @@ class SerializationFirResolveExtension(session: FirSession) : FirDeclarationGene return listOf(copy.symbol) } - private fun generateSerializerGetterInCompanion(owner: FirClassSymbol<*>, callableId: CallableId): FirNamedFunctionSymbol { + private fun generateSerializerGetterInCompanion(owner: FirClassSymbol<*>, serializableClassSymbol: FirClassSymbol<*>, callableId: CallableId): FirNamedFunctionSymbol { val f = buildSimpleFunction { moduleData = session.moduleData symbol = FirNamedFunctionSymbol(callableId) @@ -167,10 +169,6 @@ class SerializationFirResolveExtension(session: FirSession) : FirDeclarationGene name = callableId.callableName dispatchReceiverType = owner.defaultType() - // TODO: handle serializable objects - val serializableClassSymbol = - (owner.getContainingDeclarationSymbol(session) as? FirClassSymbol<*>) ?: error("Can't get outer class for $owner") - typeParameters.addAll(serializableClassSymbol.typeParameterSymbols.map { newSimpleTypeParameter(session, symbol, it.name) }) val parametersAsArguments = typeParameters.map { it.toConeType() }.toTypedArray() @@ -193,7 +191,7 @@ class SerializationFirResolveExtension(session: FirSession) : FirDeclarationGene return f.symbol } - @OptIn(SymbolInternals::class) // TODO: localSerializersFieldDescriptors + @OptIn(SymbolInternals::class) override fun generateProperties(callableId: CallableId, context: MemberGenerationContext?): List { val owner = context?.owner ?: return emptyList() if (owner.name != SerialEntityNames.SERIALIZER_CLASS_NAME) return emptyList() diff --git a/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/fir/SerializationFirUtils.kt b/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/fir/SerializationFirUtils.kt index 44a9a389322..dab892a38a6 100644 --- a/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/fir/SerializationFirUtils.kt +++ b/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/fir/SerializationFirUtils.kt @@ -5,15 +5,22 @@ package org.jetbrains.kotlinx.serialization.compiler.fir +import org.jetbrains.kotlin.descriptors.ClassDescriptor import org.jetbrains.kotlin.descriptors.ClassKind import org.jetbrains.kotlin.descriptors.Modality +import org.jetbrains.kotlin.fir.FirSession +import org.jetbrains.kotlin.fir.analysis.checkers.getContainingDeclarationSymbol +import org.jetbrains.kotlin.fir.analysis.checkers.outerClassSymbol import org.jetbrains.kotlin.fir.declarations.findArgumentByName +import org.jetbrains.kotlin.fir.declarations.utils.isCompanion import org.jetbrains.kotlin.fir.declarations.utils.modality import org.jetbrains.kotlin.fir.expressions.classId import org.jetbrains.kotlin.fir.symbols.SymbolInternals import org.jetbrains.kotlin.fir.symbols.impl.FirClassSymbol import org.jetbrains.kotlin.name.Name import org.jetbrains.kotlinx.serialization.compiler.resolve.SerializationAnnotations +import org.jetbrains.kotlinx.serialization.compiler.resolve.isSerializableObject +import org.jetbrains.kotlinx.serialization.compiler.resolve.shouldHaveGeneratedMethodsInCompanion internal val FirClassSymbol<*>.hasSerializableAnnotation get() = this.annotations.any { @@ -59,4 +66,12 @@ private fun FirClassSymbol<*>.isFinalOrOpen(): Boolean { val modality = rawStatus.modality // null means default modality, final return (modality == null || modality == Modality.FINAL || modality == Modality.OPEN) +} + +internal fun FirSession.getSerializableClassDescriptorByCompanion(thisDescriptor: FirClassSymbol<*>): FirClassSymbol<*>? { + if (thisDescriptor.isSerializableObject) return thisDescriptor + if (!thisDescriptor.isCompanion) return null + val classDescriptor = (thisDescriptor.getContainingDeclarationSymbol(this) as? FirClassSymbol<*>) ?: return null + if (!classDescriptor.shouldHaveGeneratedMethodsInCompanion) return null + return classDescriptor } \ No newline at end of file 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 1963b39ba4f..2acb1603e16 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 @@ -54,4 +54,10 @@ public class SerializationFirBlackBoxTestGenerated extends AbstractSerialization public void testPrivatePropertiesSerialization() throws Exception { runTest("plugins/kotlin-serialization/kotlin-serialization-compiler/testData/firMembers/privatePropertiesSerialization.kt"); } + + @Test + @TestMetadata("serializableObject.kt") + public void testSerializableObject() throws Exception { + runTest("plugins/kotlin-serialization/kotlin-serialization-compiler/testData/firMembers/serializableObject.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 d8c54e154e1..ed67b2592d8 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 @@ -53,4 +53,10 @@ public class SerializationFirMembersTestGenerated extends AbstractSerializationF public void testPrivatePropertiesSerialization() throws Exception { runTest("plugins/kotlin-serialization/kotlin-serialization-compiler/testData/firMembers/privatePropertiesSerialization.kt"); } + + @Test + @TestMetadata("serializableObject.kt") + public void testSerializableObject() throws Exception { + runTest("plugins/kotlin-serialization/kotlin-serialization-compiler/testData/firMembers/serializableObject.kt"); + } } diff --git a/plugins/kotlin-serialization/kotlin-serialization-compiler/testData/firMembers/serializableObject.fir.txt b/plugins/kotlin-serialization/kotlin-serialization-compiler/testData/firMembers/serializableObject.fir.txt new file mode 100644 index 00000000000..906d065ed22 --- /dev/null +++ b/plugins/kotlin-serialization/kotlin-serialization-compiler/testData/firMembers/serializableObject.fir.txt @@ -0,0 +1,26 @@ +FILE: serializableObject.kt + @R|kotlinx/serialization/Serializable|() public final object FooBar : R|kotlin/Any| { + private constructor(): R|FooBar| { + super() + } + + public final fun serializer(): R|kotlinx/serialization/KSerializer| + + } + public final fun box(): R|kotlin/String| { + lval encoded: R|kotlin/String| = Q|kotlinx/serialization/json/Json|.R|kotlinx/serialization/json/Json.encodeToString|(Q|FooBar|.R|/FooBar.serializer|(), Q|FooBar|) + when () { + !=(R|/encoded|, String({})) -> { + ^box R|/encoded| + } + } + + lval decoded: R|FooBar| = Q|kotlinx/serialization/json/Json|.R|kotlinx/serialization/json/Json.decodeFromString|(Q|FooBar|.R|/FooBar.serializer|(), R|/encoded|) + when () { + !==(R|/decoded|, Q|FooBar|) -> { + ^box String(Incorrect object instance) + } + } + + ^box String(OK) + } diff --git a/plugins/kotlin-serialization/kotlin-serialization-compiler/testData/firMembers/serializableObject.kt b/plugins/kotlin-serialization/kotlin-serialization-compiler/testData/firMembers/serializableObject.kt new file mode 100644 index 00000000000..4775f7f0b01 --- /dev/null +++ b/plugins/kotlin-serialization/kotlin-serialization-compiler/testData/firMembers/serializableObject.kt @@ -0,0 +1,15 @@ +// WITH_STDLIB + +import kotlinx.serialization.* +import kotlinx.serialization.json.* + +@Serializable +object FooBar + +fun box(): String { + val encoded = Json.encodeToString(FooBar.serializer(), FooBar) + if (encoded != "{}") return encoded + val decoded = Json.decodeFromString(FooBar.serializer(), encoded) + if (decoded !== FooBar) return "Incorrect object instance" + return "OK" +} \ No newline at end of file