[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.ir.util.*
|
||||||
import org.jetbrains.kotlin.name.ClassId
|
import org.jetbrains.kotlin.name.ClassId
|
||||||
import org.jetbrains.kotlin.name.Name
|
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.extensions.SerializationPluginContext
|
||||||
import org.jetbrains.kotlinx.serialization.compiler.resolve.SerialEntityNames
|
import org.jetbrains.kotlinx.serialization.compiler.resolve.SerialEntityNames
|
||||||
|
import org.jetbrains.kotlinx.serialization.compiler.resolve.SerializationAnnotations
|
||||||
import org.jetbrains.kotlinx.serialization.compiler.resolve.SerializationPackages
|
import org.jetbrains.kotlinx.serialization.compiler.resolve.SerializationPackages
|
||||||
|
|
||||||
class SerializableCompanionIrGenerator(
|
class SerializableCompanionIrGenerator(
|
||||||
@@ -75,6 +77,23 @@ class SerializableCompanionIrGenerator(
|
|||||||
} else {
|
} else {
|
||||||
generateSerializerGetter(serializerGetterFunction)
|
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) {
|
private fun IrBuilderWithScope.patchSerializableClassWithMarkerAnnotation(serializer: IrClass) {
|
||||||
|
|||||||
+3
@@ -43,6 +43,8 @@ object SerializationAnnotations {
|
|||||||
val polymorphicFqName = FqName("kotlinx.serialization.Polymorphic")
|
val polymorphicFqName = FqName("kotlinx.serialization.Polymorphic")
|
||||||
val additionalSerializersFqName = FqName("kotlinx.serialization.UseSerializers")
|
val additionalSerializersFqName = FqName("kotlinx.serialization.UseSerializers")
|
||||||
|
|
||||||
|
val namedCompanionFqName = FqName("kotlinx.serialization.internal.NamedCompanion")
|
||||||
|
|
||||||
val serializableAnnotationClassId = ClassId.topLevel(serializableAnnotationFqName)
|
val serializableAnnotationClassId = ClassId.topLevel(serializableAnnotationFqName)
|
||||||
val serializerAnnotationClassId = ClassId.topLevel(serializerAnnotationFqName)
|
val serializerAnnotationClassId = ClassId.topLevel(serializerAnnotationFqName)
|
||||||
val serialNameAnnotationClassId = ClassId.topLevel(serialNameAnnotationFqName)
|
val serialNameAnnotationClassId = ClassId.topLevel(serialNameAnnotationFqName)
|
||||||
@@ -58,6 +60,7 @@ object SerializationAnnotations {
|
|||||||
val contextualOnPropertyClassId = ClassId.topLevel(contextualOnPropertyFqName)
|
val contextualOnPropertyClassId = ClassId.topLevel(contextualOnPropertyFqName)
|
||||||
val polymorphicClassId = ClassId.topLevel(polymorphicFqName)
|
val polymorphicClassId = ClassId.topLevel(polymorphicFqName)
|
||||||
val additionalSerializersClassId = ClassId.topLevel(additionalSerializersFqName)
|
val additionalSerializersClassId = ClassId.topLevel(additionalSerializersFqName)
|
||||||
|
val namedCompanionClassId = ClassId.topLevel(namedCompanionFqName)
|
||||||
}
|
}
|
||||||
|
|
||||||
object SerialEntityNames {
|
object SerialEntityNames {
|
||||||
|
|||||||
+17
@@ -9,15 +9,21 @@ import org.jetbrains.kotlin.descriptors.ClassDescriptor
|
|||||||
import org.jetbrains.kotlin.descriptors.Modality
|
import org.jetbrains.kotlin.descriptors.Modality
|
||||||
import org.jetbrains.kotlin.metadata.ProtoBuf
|
import org.jetbrains.kotlin.metadata.ProtoBuf
|
||||||
import org.jetbrains.kotlin.metadata.SerializationPluginMetadataExtensions
|
import org.jetbrains.kotlin.metadata.SerializationPluginMetadataExtensions
|
||||||
|
import org.jetbrains.kotlin.metadata.deserialization.Flags
|
||||||
import org.jetbrains.kotlin.metadata.serialization.MutableVersionRequirementTable
|
import org.jetbrains.kotlin.metadata.serialization.MutableVersionRequirementTable
|
||||||
import org.jetbrains.kotlin.name.Name
|
import org.jetbrains.kotlin.name.Name
|
||||||
|
import org.jetbrains.kotlin.name.SpecialNames
|
||||||
import org.jetbrains.kotlin.serialization.DescriptorSerializer
|
import org.jetbrains.kotlin.serialization.DescriptorSerializer
|
||||||
import org.jetbrains.kotlin.serialization.DescriptorSerializerPlugin
|
import org.jetbrains.kotlin.serialization.DescriptorSerializerPlugin
|
||||||
import org.jetbrains.kotlin.serialization.SerializerExtension
|
import org.jetbrains.kotlin.serialization.SerializerExtension
|
||||||
import org.jetbrains.kotlinx.serialization.compiler.resolve.SerializableProperties
|
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.isInternalSerializable
|
||||||
|
import org.jetbrains.kotlinx.serialization.compiler.resolve.shouldHaveGeneratedMethodsInCompanion
|
||||||
|
|
||||||
class SerializationDescriptorSerializerPlugin : DescriptorSerializerPlugin {
|
class SerializationDescriptorSerializerPlugin : DescriptorSerializerPlugin {
|
||||||
|
private val hasAnnotationFlag = Flags.HAS_ANNOTATIONS.toFlags(true)
|
||||||
|
|
||||||
private val descriptorMetadataMap: MutableMap<ClassDescriptor, SerializableProperties> = hashMapOf()
|
private val descriptorMetadataMap: MutableMap<ClassDescriptor, SerializableProperties> = hashMapOf()
|
||||||
|
|
||||||
private val ClassDescriptor.needSaveProgramOrder: Boolean
|
private val ClassDescriptor.needSaveProgramOrder: Boolean
|
||||||
@@ -37,6 +43,17 @@ class SerializationDescriptorSerializerPlugin : DescriptorSerializerPlugin {
|
|||||||
) {
|
) {
|
||||||
fun Name.toIndex() = extension.stringTable.getStringIndex(asString())
|
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
|
if (!descriptor.needSaveProgramOrder) return
|
||||||
|
|
||||||
val propertiesCorrectOrder = (descriptorMetadataMap[descriptor] ?: return).serializableProperties
|
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? =
|
fun ModuleDescriptor.getJsExportIgnore(): ClassDescriptor? =
|
||||||
findClassAcrossModuleDependencies(SerializationJsDependenciesClassIds.jsExportIgnore)
|
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");
|
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
|
@Test
|
||||||
@TestMetadata("repeatableSerialInfo.kt")
|
@TestMetadata("repeatableSerialInfo.kt")
|
||||||
public void testRepeatableSerialInfo() throws Exception {
|
public void testRepeatableSerialInfo() throws Exception {
|
||||||
|
|||||||
+6
@@ -199,6 +199,12 @@ public class SerializationIrBoxTestGenerated extends AbstractSerializationIrBoxT
|
|||||||
runTest("plugins/kotlinx-serialization/testData/boxIr/multipleGenericsPolymorphic.kt");
|
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
|
@Test
|
||||||
@TestMetadata("repeatableSerialInfo.kt")
|
@TestMetadata("repeatableSerialInfo.kt")
|
||||||
public void testRepeatableSerialInfo() throws Exception {
|
public void testRepeatableSerialInfo() throws Exception {
|
||||||
|
|||||||
Reference in New Issue
Block a user