[KxSerialization] Implemented addition of annotations to the named companion
For a more efficient lookup of a companion with the functions of obtaining a serializer, a special annotation is added to the named companions. Merge-request: KT-MR-11003 Merged-by: Sergey Shanshin <Sergey.Shanshin@jetbrains.com>
This commit is contained in:
committed by
Space Team
parent
175c00e5c7
commit
6417f6456f
+19
@@ -23,8 +23,10 @@ import org.jetbrains.kotlin.ir.types.typeWith
|
||||
import org.jetbrains.kotlin.ir.util.*
|
||||
import org.jetbrains.kotlin.name.ClassId
|
||||
import org.jetbrains.kotlin.name.Name
|
||||
import org.jetbrains.kotlin.name.SpecialNames
|
||||
import org.jetbrains.kotlinx.serialization.compiler.extensions.SerializationPluginContext
|
||||
import org.jetbrains.kotlinx.serialization.compiler.resolve.SerialEntityNames
|
||||
import org.jetbrains.kotlinx.serialization.compiler.resolve.SerializationAnnotations
|
||||
import org.jetbrains.kotlinx.serialization.compiler.resolve.SerializationPackages
|
||||
|
||||
class SerializableCompanionIrGenerator(
|
||||
@@ -75,6 +77,23 @@ class SerializableCompanionIrGenerator(
|
||||
} else {
|
||||
generateSerializerGetter(serializerGetterFunction)
|
||||
}
|
||||
|
||||
patchNamedCompanionWithMarkerAnnotation()
|
||||
}
|
||||
|
||||
private fun patchNamedCompanionWithMarkerAnnotation() {
|
||||
if (serializableIrClass.kind == ClassKind.OBJECT || irClass.name == SpecialNames.DEFAULT_NAME_FOR_COMPANION_OBJECT) {
|
||||
return
|
||||
}
|
||||
|
||||
val annotationClass = compilerContext.referenceClass(SerializationAnnotations.namedCompanionClassId) ?: return
|
||||
|
||||
val annotationCtor = annotationClass.constructors.single { it.owner.isPrimary }
|
||||
val annotationType = annotationClass.defaultType
|
||||
|
||||
val annotationCall = IrConstructorCallImpl.fromSymbolOwner(irClass.startOffset, irClass.endOffset, annotationType, annotationCtor)
|
||||
|
||||
compilerContext.annotationsRegistrar.addMetadataVisibleAnnotationsToElement(irClass, annotationCall)
|
||||
}
|
||||
|
||||
private fun IrBuilderWithScope.patchSerializableClassWithMarkerAnnotation(serializer: IrClass) {
|
||||
|
||||
+3
@@ -43,6 +43,8 @@ object SerializationAnnotations {
|
||||
val polymorphicFqName = FqName("kotlinx.serialization.Polymorphic")
|
||||
val additionalSerializersFqName = FqName("kotlinx.serialization.UseSerializers")
|
||||
|
||||
val namedCompanionFqName = FqName("kotlinx.serialization.internal.NamedCompanion")
|
||||
|
||||
val serializableAnnotationClassId = ClassId.topLevel(serializableAnnotationFqName)
|
||||
val serializerAnnotationClassId = ClassId.topLevel(serializerAnnotationFqName)
|
||||
val serialNameAnnotationClassId = ClassId.topLevel(serialNameAnnotationFqName)
|
||||
@@ -58,6 +60,7 @@ object SerializationAnnotations {
|
||||
val contextualOnPropertyClassId = ClassId.topLevel(contextualOnPropertyFqName)
|
||||
val polymorphicClassId = ClassId.topLevel(polymorphicFqName)
|
||||
val additionalSerializersClassId = ClassId.topLevel(additionalSerializersFqName)
|
||||
val namedCompanionClassId = ClassId.topLevel(namedCompanionFqName)
|
||||
}
|
||||
|
||||
object SerialEntityNames {
|
||||
|
||||
+17
@@ -9,15 +9,21 @@ import org.jetbrains.kotlin.descriptors.ClassDescriptor
|
||||
import org.jetbrains.kotlin.descriptors.Modality
|
||||
import org.jetbrains.kotlin.metadata.ProtoBuf
|
||||
import org.jetbrains.kotlin.metadata.SerializationPluginMetadataExtensions
|
||||
import org.jetbrains.kotlin.metadata.deserialization.Flags
|
||||
import org.jetbrains.kotlin.metadata.serialization.MutableVersionRequirementTable
|
||||
import org.jetbrains.kotlin.name.Name
|
||||
import org.jetbrains.kotlin.name.SpecialNames
|
||||
import org.jetbrains.kotlin.serialization.DescriptorSerializer
|
||||
import org.jetbrains.kotlin.serialization.DescriptorSerializerPlugin
|
||||
import org.jetbrains.kotlin.serialization.SerializerExtension
|
||||
import org.jetbrains.kotlinx.serialization.compiler.resolve.SerializableProperties
|
||||
import org.jetbrains.kotlinx.serialization.compiler.resolve.findNamedCompanionAnnotation
|
||||
import org.jetbrains.kotlinx.serialization.compiler.resolve.isInternalSerializable
|
||||
import org.jetbrains.kotlinx.serialization.compiler.resolve.shouldHaveGeneratedMethodsInCompanion
|
||||
|
||||
class SerializationDescriptorSerializerPlugin : DescriptorSerializerPlugin {
|
||||
private val hasAnnotationFlag = Flags.HAS_ANNOTATIONS.toFlags(true)
|
||||
|
||||
private val descriptorMetadataMap: MutableMap<ClassDescriptor, SerializableProperties> = hashMapOf()
|
||||
|
||||
private val ClassDescriptor.needSaveProgramOrder: Boolean
|
||||
@@ -37,6 +43,17 @@ class SerializationDescriptorSerializerPlugin : DescriptorSerializerPlugin {
|
||||
) {
|
||||
fun Name.toIndex() = extension.stringTable.getStringIndex(asString())
|
||||
|
||||
if (descriptor.isCompanionObject
|
||||
&& descriptor.name != SpecialNames.DEFAULT_NAME_FOR_COMPANION_OBJECT
|
||||
&& (descriptor.containingDeclaration as? ClassDescriptor)?.shouldHaveGeneratedMethodsInCompanion == true
|
||||
) {
|
||||
// named companion objects are always marked by special annotation in backend
|
||||
// in order for this annotation to be visible in runtime through reflection, it is necessary to put down the hasAnnotation flag
|
||||
if (proto.flags and hasAnnotationFlag == 0 && descriptor.findNamedCompanionAnnotation() != null) {
|
||||
proto.flags = proto.flags or hasAnnotationFlag
|
||||
}
|
||||
}
|
||||
|
||||
if (!descriptor.needSaveProgramOrder) return
|
||||
|
||||
val propertiesCorrectOrder = (descriptorMetadataMap[descriptor] ?: return).serializableProperties
|
||||
|
||||
+3
@@ -82,6 +82,9 @@ fun ClassDescriptor.getKSerializer(): ClassDescriptor =
|
||||
)
|
||||
)!!
|
||||
|
||||
fun ClassDescriptor.findNamedCompanionAnnotation(): ClassDescriptor? =
|
||||
module.findClassAcrossModuleDependencies(SerializationAnnotations.namedCompanionClassId)
|
||||
|
||||
fun ModuleDescriptor.getJsExportIgnore(): ClassDescriptor? =
|
||||
findClassAcrossModuleDependencies(SerializationJsDependenciesClassIds.jsExportIgnore)
|
||||
|
||||
|
||||
@@ -0,0 +1,116 @@
|
||||
// WITH_STDLIB
|
||||
// WITH_REFLECT
|
||||
// TARGET_BACKEND: JVM_IR
|
||||
|
||||
package kotlinx.serialization.internal
|
||||
|
||||
import kotlin.annotation.*
|
||||
import kotlinx.serialization.*
|
||||
import kotlinx.serialization.descriptors.*
|
||||
import kotlinx.serialization.encoding.*
|
||||
import kotlin.reflect.KClass
|
||||
|
||||
/*
|
||||
Until the annotation is added to the serialization runtime,
|
||||
we have to create an annotation with that name in the project itself
|
||||
*/
|
||||
@Target(AnnotationTarget.CLASS)
|
||||
@Retention(AnnotationRetention.RUNTIME)
|
||||
annotation class NamedCompanion
|
||||
|
||||
|
||||
@Serializable
|
||||
class Plain(val i: Int)
|
||||
|
||||
@Serializable
|
||||
object Object
|
||||
|
||||
|
||||
@Serializable
|
||||
class WithStd(val i: Int) {
|
||||
companion object {
|
||||
var j: Int = 42
|
||||
}
|
||||
}
|
||||
|
||||
@Serializable
|
||||
class WithNamed(val i: Int) {
|
||||
companion object MyCompanion {
|
||||
var j: Int = 42
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
object InterfaceWithCustomSerializer: ToDoSerializer<InterfaceWithCustom>("interface")
|
||||
@Serializable(InterfaceWithCustomSerializer::class)
|
||||
interface InterfaceWithCustom {
|
||||
}
|
||||
|
||||
|
||||
object NamedInterfaceWithCustomSerializer: ToDoSerializer<NamedInterfaceWithCustom>("interface.name")
|
||||
@Serializable(NamedInterfaceWithCustomSerializer::class)
|
||||
interface NamedInterfaceWithCustom {
|
||||
companion object MyCompanion {
|
||||
var j: Int = 42
|
||||
}
|
||||
}
|
||||
|
||||
@Serializable
|
||||
sealed interface SealedInterface {
|
||||
}
|
||||
|
||||
@Serializable
|
||||
sealed interface NamedSealedInterface {
|
||||
companion object MyCompanion {
|
||||
var j: Int = 42
|
||||
}
|
||||
}
|
||||
|
||||
fun box(): String {
|
||||
if (Plain::class.annotations.any { it.annotationClass.simpleName == "NamedCompanion" }) {
|
||||
return "Annotation on Plain class"
|
||||
}
|
||||
if (Plain.Companion::class.annotations.any { it.annotationClass.simpleName == "NamedCompanion" }) {
|
||||
return "Annotation on Plain.Companion class"
|
||||
}
|
||||
|
||||
if (Object::class.annotations.any { it.annotationClass.simpleName == "NamedCompanion" }) {
|
||||
return "Annotation on Object"
|
||||
}
|
||||
|
||||
if (WithStd::class.annotations.any { it.annotationClass.simpleName == "NamedCompanion" }) {
|
||||
return "Annotation on WithStd class"
|
||||
}
|
||||
if (WithStd.Companion::class.annotations.any { it.annotationClass.simpleName == "NamedCompanion" }) {
|
||||
return "Annotation on WithStd.Companion class"
|
||||
}
|
||||
|
||||
if (WithNamed::class.annotations.any { it.annotationClass.simpleName == "NamedCompanion" }) {
|
||||
return "Annotation on WithNamed class"
|
||||
}
|
||||
if (WithNamed.MyCompanion::class.annotations.none { it.annotationClass.simpleName == "NamedCompanion" }) {
|
||||
return "Missed annotation on WithNamed.MyCompanion class ${WithNamed.MyCompanion::class.annotations}"
|
||||
}
|
||||
|
||||
if (InterfaceWithCustom::class.annotations.any { it.annotationClass.simpleName == "NamedCompanion" }) {
|
||||
return "Annotation on InterfaceWithCustom class"
|
||||
}
|
||||
if (NamedInterfaceWithCustom.MyCompanion::class.annotations.none { it.annotationClass.simpleName == "NamedCompanion" }) {
|
||||
return "Missed annotation on NamedInterfaceWithCustom.MyCompanion class ${WithNamed.MyCompanion::class.annotations}"
|
||||
}
|
||||
|
||||
if (SealedInterface::class.annotations.any { it.annotationClass.simpleName == "NamedCompanion" }) {
|
||||
return "Annotation on SealedInterface class"
|
||||
}
|
||||
if (NamedSealedInterface.MyCompanion::class.annotations.none { it.annotationClass.simpleName == "NamedCompanion" }) {
|
||||
return "Missed annotation on NamedSealedInterface.MyCompanion class ${WithNamed.MyCompanion::class.annotations}"
|
||||
}
|
||||
|
||||
return "OK"
|
||||
}
|
||||
|
||||
abstract class ToDoSerializer<T: Any>(descriptorName: String): KSerializer<T> {
|
||||
override val descriptor: SerialDescriptor = PrimitiveSerialDescriptor(descriptorName, PrimitiveKind.STRING)
|
||||
override fun deserialize(decoder: Decoder): T = TODO()
|
||||
override fun serialize(encoder: Encoder, value: T) = TODO()
|
||||
}
|
||||
+6
@@ -201,6 +201,12 @@ public class SerializationFirLightTreeBlackBoxTestGenerated extends AbstractSeri
|
||||
runTest("plugins/kotlinx-serialization/testData/boxIr/multipleGenericsPolymorphic.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("namedCompanions.kt")
|
||||
public void testNamedCompanions() throws Exception {
|
||||
runTest("plugins/kotlinx-serialization/testData/boxIr/namedCompanions.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("repeatableSerialInfo.kt")
|
||||
public void testRepeatableSerialInfo() throws Exception {
|
||||
|
||||
+6
@@ -199,6 +199,12 @@ public class SerializationIrBoxTestGenerated extends AbstractSerializationIrBoxT
|
||||
runTest("plugins/kotlinx-serialization/testData/boxIr/multipleGenericsPolymorphic.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("namedCompanions.kt")
|
||||
public void testNamedCompanions() throws Exception {
|
||||
runTest("plugins/kotlinx-serialization/testData/boxIr/namedCompanions.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("repeatableSerialInfo.kt")
|
||||
public void testRepeatableSerialInfo() throws Exception {
|
||||
|
||||
Reference in New Issue
Block a user