kotlinx.serialization: Support @MetaSerializable (#4583)

This commit is contained in:
Rustam
2022-03-25 17:07:42 +01:00
committed by GitHub
parent 25c5d99a0c
commit e09f30f442
15 changed files with 404 additions and 76 deletions
@@ -14,6 +14,7 @@ dependencies {
compileOnly(project(":compiler:frontend")) compileOnly(project(":compiler:frontend"))
compileOnly(project(":compiler:backend")) compileOnly(project(":compiler:backend"))
compileOnly(project(":compiler:ir.backend.common")) compileOnly(project(":compiler:ir.backend.common"))
compileOnly(project(":compiler:ir.tree.impl"))
compileOnly(project(":js:js.frontend")) compileOnly(project(":js:js.frontend"))
compileOnly(project(":js:js.translator")) compileOnly(project(":js:js.translator"))
compileOnly(project(":kotlin-util-klib-metadata")) compileOnly(project(":kotlin-util-klib-metadata"))
@@ -44,7 +44,7 @@ fun AbstractSerialGenerator.findAddOnSerializer(propertyType: KotlinType, module
} }
fun KotlinType.isGeneratedSerializableObject() = fun KotlinType.isGeneratedSerializableObject() =
toClassDescriptor?.run { kind == ClassKind.OBJECT && hasSerializableAnnotationWithoutArgs } == true toClassDescriptor?.run { kind == ClassKind.OBJECT && hasSerializableOrMetaAnnotationWithoutArgs } == true
@Suppress("FunctionName", "LocalVariableName") @Suppress("FunctionName", "LocalVariableName")
fun AbstractSerialGenerator.getSerialTypeInfo(property: SerializableProperty): SerialTypeInfo { fun AbstractSerialGenerator.getSerialTypeInfo(property: SerializableProperty): SerialTypeInfo {
@@ -497,7 +497,7 @@ interface IrBuilderExtension {
propertyParent: IrClass, propertyParent: IrClass,
fieldName: Name = propertyDescriptor.name, fieldName: Name = propertyDescriptor.name,
): IrProperty { ): IrProperty {
val irProperty = propertyParent.searchForDeclaration<IrProperty>(propertyDescriptor) ?: run { val irProperty = propertyParent.searchForDeclaration(propertyDescriptor) ?: run {
with(propertyDescriptor) { with(propertyDescriptor) {
propertyParent.factory.createProperty( propertyParent.factory.createProperty(
propertyParent.startOffset, propertyParent.endOffset, SERIALIZABLE_PLUGIN_ORIGIN, IrPropertySymbolImpl(propertyDescriptor), propertyParent.startOffset, propertyParent.endOffset, SERIALIZABLE_PLUGIN_ORIGIN, IrPropertySymbolImpl(propertyDescriptor),
@@ -520,6 +520,10 @@ interface IrBuilderExtension {
return irProperty return irProperty
} }
fun IrType.kClassToJClassIfNeeded(): IrType = this
fun kClassExprToJClassIfNeeded(startOffset: Int, endOffset: Int, irExpression: IrExpression): IrExpression = irExpression
private fun IrClass.generatePropertyBackingFieldIfNeeded( private fun IrClass.generatePropertyBackingFieldIfNeeded(
propertyDescriptor: PropertyDescriptor, propertyDescriptor: PropertyDescriptor,
originProperty: IrProperty, originProperty: IrProperty,
@@ -568,7 +572,10 @@ interface IrBuilderExtension {
} }
irAccessor.body = when (isGetter) { irAccessor.body = when (isGetter) {
true -> generateDefaultGetterBody(descriptor as PropertyGetterDescriptor, irAccessor) true -> {
irAccessor.returnType = irAccessor.returnType.kClassToJClassIfNeeded()
generateDefaultGetterBody(descriptor as PropertyGetterDescriptor, irAccessor)
}
false -> generateDefaultSetterBody(descriptor as PropertySetterDescriptor, irAccessor) false -> generateDefaultSetterBody(descriptor as PropertySetterDescriptor, irAccessor)
} }
@@ -588,6 +595,7 @@ interface IrBuilderExtension {
val receiver = generateReceiverExpressionForFieldAccess(irAccessor.dispatchReceiverParameter!!.symbol, property) val receiver = generateReceiverExpressionForFieldAccess(irAccessor.dispatchReceiverParameter!!.symbol, property)
val propertyIrType = property.type.toIrType()
irBody.statements.add( irBody.statements.add(
IrReturnImpl( IrReturnImpl(
startOffset, endOffset, compilerContext.irBuiltIns.nothingType, startOffset, endOffset, compilerContext.irBuiltIns.nothingType,
@@ -595,9 +603,11 @@ interface IrBuilderExtension {
IrGetFieldImpl( IrGetFieldImpl(
startOffset, endOffset, startOffset, endOffset,
irProperty.backingField?.symbol ?: error("Property expected to have backing field"), irProperty.backingField?.symbol ?: error("Property expected to have backing field"),
property.type.toIrType(), propertyIrType,
receiver receiver
) ).let {
if (propertyIrType.isKClass()) kClassExprToJClassIfNeeded(startOffset, endOffset, it) else it
}
) )
) )
return irBody return irBody
@@ -1104,7 +1114,7 @@ interface IrBuilderExtension {
} }
fun collectSerialInfoAnnotations(irClass: IrClass): List<IrConstructorCall> { fun collectSerialInfoAnnotations(irClass: IrClass): List<IrConstructorCall> {
if (!(irClass.isInterface || irClass.descriptor.hasSerializableAnnotation)) return emptyList() if (!(irClass.isInterface || irClass.descriptor.hasSerializableOrMetaAnnotation)) return emptyList()
val annotationByFq: MutableMap<FqName, IrConstructorCall> = irClass.annotations.associateBy { it.symbol.owner.parentAsClass.descriptor.fqNameSafe }.toMutableMap() val annotationByFq: MutableMap<FqName, IrConstructorCall> = irClass.annotations.associateBy { it.symbol.owner.parentAsClass.descriptor.fqNameSafe }.toMutableMap()
for (clazz in irClass.getAllSuperclasses()) { for (clazz in irClass.getAllSuperclasses()) {
val annotations = clazz.annotations val annotations = clazz.annotations
@@ -5,17 +5,29 @@
package org.jetbrains.kotlinx.serialization.compiler.backend.ir package org.jetbrains.kotlinx.serialization.compiler.backend.ir
import org.jetbrains.kotlin.backend.common.ir.createImplicitParameterDeclarationWithWrappedDescriptor
import org.jetbrains.kotlin.descriptors.ClassKind
import org.jetbrains.kotlin.descriptors.DescriptorVisibilities import org.jetbrains.kotlin.descriptors.DescriptorVisibilities
import org.jetbrains.kotlin.descriptors.Modality
import org.jetbrains.kotlin.ir.UNDEFINED_OFFSET import org.jetbrains.kotlin.ir.UNDEFINED_OFFSET
import org.jetbrains.kotlin.ir.builders.declarations.addConstructor import org.jetbrains.kotlin.ir.builders.declarations.*
import org.jetbrains.kotlin.ir.builders.declarations.addValueParameter
import org.jetbrains.kotlin.ir.declarations.IrClass import org.jetbrains.kotlin.ir.declarations.IrClass
import org.jetbrains.kotlin.ir.declarations.IrModuleFragment
import org.jetbrains.kotlin.ir.declarations.IrPackageFragment
import org.jetbrains.kotlin.ir.declarations.IrProperty import org.jetbrains.kotlin.ir.declarations.IrProperty
import org.jetbrains.kotlin.ir.declarations.impl.IrExternalPackageFragmentImpl
import org.jetbrains.kotlin.ir.declarations.impl.IrFactoryImpl
import org.jetbrains.kotlin.ir.expressions.IrConstructorCall 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.* import org.jetbrains.kotlin.ir.expressions.impl.*
import org.jetbrains.kotlin.ir.symbols.IrClassSymbol
import org.jetbrains.kotlin.ir.symbols.IrPropertySymbol
import org.jetbrains.kotlin.ir.types.*
import org.jetbrains.kotlin.ir.util.constructors import org.jetbrains.kotlin.ir.util.constructors
import org.jetbrains.kotlin.ir.util.defaultType import org.jetbrains.kotlin.ir.util.defaultType
import org.jetbrains.kotlin.ir.util.kotlinFqName import org.jetbrains.kotlin.ir.util.kotlinFqName
import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.name.Name import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.resolve.DescriptorUtils import org.jetbrains.kotlin.resolve.DescriptorUtils
import org.jetbrains.kotlinx.serialization.compiler.extensions.SerializationPluginContext import org.jetbrains.kotlinx.serialization.compiler.extensions.SerializationPluginContext
@@ -24,15 +36,18 @@ import org.jetbrains.kotlinx.serialization.compiler.resolve.SerialEntityNames
// This doesn't support annotation arguments of type KClass and Array<KClass> because the codegen doesn't compute JVM signatures for // This doesn't support annotation arguments of type KClass and Array<KClass> because the codegen doesn't compute JVM signatures for
// such cases correctly (because inheriting from annotation classes is prohibited in Kotlin). // such cases correctly (because inheriting from annotation classes is prohibited in Kotlin).
// Currently it results in an "accidental override" error where a method with return type KClass conflicts with the one with Class. // Currently it results in an "accidental override" error where a method with return type KClass conflicts with the one with Class.
// TODO: support annotation properties of types KClass<...> and Array<KClass<...>>.
class SerialInfoImplJvmIrGenerator( class SerialInfoImplJvmIrGenerator(
private val context: SerializationPluginContext, private val context: SerializationPluginContext,
private val moduleFragment: IrModuleFragment,
) : IrBuilderExtension { ) : IrBuilderExtension {
override val compilerContext: SerializationPluginContext override val compilerContext: SerializationPluginContext
get() = context get() = context
private val jvmNameClass get() = context.referenceClass(DescriptorUtils.JVM_NAME)!!.owner private val jvmNameClass get() = context.referenceClass(DescriptorUtils.JVM_NAME)!!.owner
private val javaLangClass = createClass(createPackage("java.lang"), "Class", ClassKind.CLASS)
private val javaLangType = javaLangClass.starProjectedType
private val implGenerated = mutableSetOf<IrClass>() private val implGenerated = mutableSetOf<IrClass>()
private val annotationToImpl = mutableMapOf<IrClass, IrClass>() private val annotationToImpl = mutableMapOf<IrClass, IrClass>()
@@ -78,7 +93,7 @@ class SerialInfoImplJvmIrGenerator(
field.visibility = DescriptorVisibilities.PRIVATE field.visibility = DescriptorVisibilities.PRIVATE
field.origin = SERIALIZABLE_PLUGIN_ORIGIN field.origin = SERIALIZABLE_PLUGIN_ORIGIN
val parameter = ctor.addValueParameter(property.name.asString(), getter.returnType) val parameter = ctor.addValueParameter(property.name.asString(), field.type)
ctorBody.statements += IrSetFieldImpl( ctorBody.statements += IrSetFieldImpl(
startOffset, endOffset, field.symbol, startOffset, endOffset, field.symbol,
IrGetValueImpl(startOffset, endOffset, irClass.thisReceiver!!.symbol), IrGetValueImpl(startOffset, endOffset, irClass.thisReceiver!!.symbol),
@@ -95,4 +110,70 @@ class SerialInfoImplJvmIrGenerator(
).apply { ).apply {
putValueArgument(0, IrConstImpl.string(UNDEFINED_OFFSET, UNDEFINED_OFFSET, context.irBuiltIns.stringType, name)) putValueArgument(0, IrConstImpl.string(UNDEFINED_OFFSET, UNDEFINED_OFFSET, context.irBuiltIns.stringType, name))
} }
override fun IrType.kClassToJClassIfNeeded(): IrType = when {
this.isKClass() -> javaLangType
this.isKClassArray() -> compilerContext.irBuiltIns.arrayClass.typeWith(javaLangType)
else -> this
}
override fun kClassExprToJClassIfNeeded(startOffset: Int, endOffset: Int, irExpression: IrExpression): IrExpression {
val getterSymbol = kClassJava.owner.getter!!.symbol
return IrCallImpl(
startOffset, endOffset,
javaLangClass.starProjectedType,
getterSymbol,
typeArgumentsCount = getterSymbol.owner.typeParameters.size,
valueArgumentsCount = 0,
origin = IrStatementOrigin.GET_PROPERTY
).apply {
this.extensionReceiver = irExpression
}
}
private val jvmName: IrClassSymbol = createClass(createPackage("kotlin.jvm"), "JvmName", ClassKind.ANNOTATION_CLASS) { klass ->
klass.addConstructor().apply {
addValueParameter("name", context.irBuiltIns.stringType)
}
}
private val kClassJava: IrPropertySymbol =
IrFactoryImpl.buildProperty {
name = Name.identifier("java")
}.apply {
parent = createClass(createPackage("kotlin.jvm"), "JvmClassMappingKt", ClassKind.CLASS).owner
addGetter().apply {
annotations = listOf(
IrConstructorCallImpl.fromSymbolOwner(jvmName.typeWith(), jvmName.constructors.single()).apply {
putValueArgument(0, IrConstImpl.string(UNDEFINED_OFFSET, UNDEFINED_OFFSET, context.irBuiltIns.stringType, "getJavaClass"))
}
)
addExtensionReceiver(context.irBuiltIns.kClassClass.starProjectedType)
returnType = javaLangClass.starProjectedType
}
}.symbol
private fun IrType.isKClassArray() =
this is IrSimpleType && isArray() && arguments.single().typeOrNull?.isKClass() == true
private fun createPackage(packageName: String): IrPackageFragment =
IrExternalPackageFragmentImpl.createEmptyExternalPackageFragment(
moduleFragment.descriptor,
FqName(packageName)
)
private fun createClass(
irPackage: IrPackageFragment,
shortName: String,
classKind: ClassKind,
block: (IrClass) -> Unit = {}
): IrClassSymbol = IrFactoryImpl.buildClass {
name = Name.identifier(shortName)
kind = classKind
modality = Modality.FINAL
}.apply {
parent = irPackage
createImplicitParameterDeclarationWithWrappedDescriptor()
block(this)
}.symbol
} }
@@ -52,17 +52,21 @@ class SerializableIrGenerator(
private val addElementFun = serialDescImplClass.referenceFunctionSymbol(CallingConventions.addElement) private val addElementFun = serialDescImplClass.referenceFunctionSymbol(CallingConventions.addElement)
private fun IrClass.hasSerializableAnnotationWithoutArgs(): Boolean { private fun IrClass.hasSerializableOrMetaAnnotationWithoutArgs(): Boolean {
val annot = getAnnotation(SerializationAnnotations.serializableAnnotationFqName) ?: return false val annot = getAnnotation(SerializationAnnotations.serializableAnnotationFqName)
if (annot != null) {
for (i in 0 until annot.valueArgumentsCount) { for (i in 0 until annot.valueArgumentsCount) {
if (annot.getValueArgument(i) != null) return false if (annot.getValueArgument(i) != null) return false
}
return true
} }
val metaAnnotation = annotations
return true .flatMap { it.symbol.owner.constructedClass.annotations }
.find { it.isAnnotation(SerializationAnnotations.metaSerializableAnnotationFqName) }
return metaAnnotation != null
} }
private val IrClass.isInternalSerializable: Boolean get() = kind == ClassKind.CLASS && hasSerializableAnnotationWithoutArgs() private val IrClass.isInternalSerializable: Boolean get() = kind == ClassKind.CLASS && hasSerializableOrMetaAnnotationWithoutArgs()
override fun generateInternalConstructor(constructorDescriptor: ClassConstructorDescriptor) = override fun generateInternalConstructor(constructorDescriptor: ClassConstructorDescriptor) =
irClass.contributeConstructor(constructorDescriptor) { ctor -> irClass.contributeConstructor(constructorDescriptor) { ctor ->
@@ -26,7 +26,6 @@ open class SerializerCodegenImpl(
metadataPlugin: SerializationDescriptorSerializerPlugin? metadataPlugin: SerializationDescriptorSerializerPlugin?
) : SerializerCodegen(codegen.descriptor, codegen.bindingContext, metadataPlugin) { ) : SerializerCodegen(codegen.descriptor, codegen.bindingContext, metadataPlugin) {
private val serialDescField = "\$\$serialDesc" private val serialDescField = "\$\$serialDesc"
protected val serializerAsmType = codegen.typeMapper.mapClass(codegen.descriptor) protected val serializerAsmType = codegen.typeMapper.mapClass(codegen.descriptor)
@@ -32,7 +32,6 @@ import org.jetbrains.kotlinx.serialization.compiler.backend.common.bodyPropertie
import org.jetbrains.kotlinx.serialization.compiler.backend.common.findTypeSerializerOrContextUnchecked import org.jetbrains.kotlinx.serialization.compiler.backend.common.findTypeSerializerOrContextUnchecked
import org.jetbrains.kotlinx.serialization.compiler.backend.common.primaryConstructorPropertiesDescriptorsMap import org.jetbrains.kotlinx.serialization.compiler.backend.common.primaryConstructorPropertiesDescriptorsMap
import org.jetbrains.kotlinx.serialization.compiler.resolve.* import org.jetbrains.kotlinx.serialization.compiler.resolve.*
import org.jetbrains.kotlinx.serialization.compiler.resolve.SerializationAnnotations.serializableAnnotationFqName
internal val SERIALIZABLE_PROPERTIES: WritableSlice<ClassDescriptor, SerializableProperties> = Slices.createSimpleSlice() internal val SERIALIZABLE_PROPERTIES: WritableSlice<ClassDescriptor, SerializableProperties> = Slices.createSimpleSlice()
@@ -75,7 +74,13 @@ open class SerializationPluginDeclarationChecker : DeclarationChecker {
if (existing.allValueArguments != call.allValueArguments) { if (existing.allValueArguments != call.allValueArguments) {
if (reported.add(fqname)) { if (reported.add(fqname)) {
val entry = (existing as? LazyAnnotationDescriptor)?.annotationEntry ?: declaration val entry = (existing as? LazyAnnotationDescriptor)?.annotationEntry ?: declaration
trace.report(SerializationErrors.INCONSISTENT_INHERITABLE_SERIALINFO.on(entry, existing.type, clazz.defaultType)) trace.report(
SerializationErrors.INCONSISTENT_INHERITABLE_SERIALINFO.on(
entry,
existing.type,
clazz.defaultType
)
)
} }
} }
} }
@@ -89,7 +94,7 @@ open class SerializationPluginDeclarationChecker : DeclarationChecker {
// For non-JAR distributions (klib, js) this method is not invoked, since getVersionsForCurrentModule // For non-JAR distributions (klib, js) this method is not invoked, since getVersionsForCurrentModule
// unable to read from them // unable to read from them
if (!versions.implementationVersionMatchSupported()) { if (!versions.implementationVersionMatchSupported()) {
descriptor.onSerializableAnnotation { descriptor.onSerializableOrMetaAnnotation {
trace.report( trace.report(
SerializationErrors.PROVIDED_RUNTIME_TOO_LOW.on( SerializationErrors.PROVIDED_RUNTIME_TOO_LOW.on(
it, it,
@@ -104,7 +109,7 @@ open class SerializationPluginDeclarationChecker : DeclarationChecker {
private fun checkMinKotlin(versions: VersionReader.RuntimeVersions, descriptor: ClassDescriptor, trace: BindingTrace) { private fun checkMinKotlin(versions: VersionReader.RuntimeVersions, descriptor: ClassDescriptor, trace: BindingTrace) {
if (versions.currentCompilerMatchRequired()) return if (versions.currentCompilerMatchRequired()) return
descriptor.onSerializableAnnotation { descriptor.onSerializableOrMetaAnnotation {
trace.report( trace.report(
SerializationErrors.REQUIRED_KOTLIN_TOO_HIGH.on( SerializationErrors.REQUIRED_KOTLIN_TOO_HIGH.on(
it, it,
@@ -141,26 +146,25 @@ open class SerializationPluginDeclarationChecker : DeclarationChecker {
return false return false
} }
if (!descriptor.annotations.hasAnnotation(serializableAnnotationFqName)) return false if (!descriptor.hasSerializableOrMetaAnnotation) return false
if (!serializationPluginEnabledOn(descriptor)) { if (!serializationPluginEnabledOn(descriptor)) {
trace.reportOnSerializableAnnotation(descriptor, SerializationErrors.PLUGIN_IS_NOT_ENABLED) trace.reportOnSerializableOrMetaAnnotation(descriptor, SerializationErrors.PLUGIN_IS_NOT_ENABLED)
return false return false
} }
if (descriptor.isAnonymousObjectOrContained) { if (descriptor.isAnonymousObjectOrContained) {
trace.reportOnSerializableAnnotation(descriptor, SerializationErrors.ANONYMOUS_OBJECTS_NOT_SUPPORTED) trace.reportOnSerializableOrMetaAnnotation(descriptor, SerializationErrors.ANONYMOUS_OBJECTS_NOT_SUPPORTED)
return false return false
} }
if (descriptor.isInner) { if (descriptor.isInner) {
trace.reportOnSerializableAnnotation(descriptor, SerializationErrors.INNER_CLASSES_NOT_SUPPORTED) trace.reportOnSerializableOrMetaAnnotation(descriptor, SerializationErrors.INNER_CLASSES_NOT_SUPPORTED)
return false return false
} }
if (descriptor.isInlineClass() && !canSupportInlineClasses(descriptor.module, trace)) { if (descriptor.isInlineClass() && !canSupportInlineClasses(descriptor.module, trace)) {
descriptor.onSerializableAnnotation { descriptor.onSerializableOrMetaAnnotation {
trace.report( trace.report(
SerializationErrors.INLINE_CLASSES_NOT_SUPPORTED.on( SerializationErrors.INLINE_CLASSES_NOT_SUPPORTED.on(
it, it,
@@ -171,14 +175,14 @@ open class SerializationPluginDeclarationChecker : DeclarationChecker {
} }
return false return false
} }
if (!descriptor.hasSerializableAnnotationWithoutArgs) { if (!descriptor.hasSerializableOrMetaAnnotationWithoutArgs) {
// defined custom serializer // defined custom serializer
checkClassWithCustomSerializer(descriptor, declaration, trace) checkClassWithCustomSerializer(descriptor, declaration, trace)
return false return false
} }
if (descriptor.serializableAnnotationIsUseless) { if (descriptor.serializableAnnotationIsUseless) {
trace.reportOnSerializableAnnotation(descriptor, SerializationErrors.SERIALIZABLE_ANNOTATION_IGNORED) trace.reportOnSerializableOrMetaAnnotation(descriptor, SerializationErrors.SERIALIZABLE_ANNOTATION_IGNORED)
return false return false
} }
@@ -186,7 +190,7 @@ open class SerializationPluginDeclarationChecker : DeclarationChecker {
if (!descriptor.isSerializableEnum()) { // enums are inherited from java.lang.Enum and can't be inherited from other classes if (!descriptor.isSerializableEnum()) { // enums are inherited from java.lang.Enum and can't be inherited from other classes
val superClass = descriptor.getSuperClassOrAny() val superClass = descriptor.getSuperClassOrAny()
if (!superClass.isInternalSerializable && superClass.constructors.singleOrNull { it.valueParameters.size == 0 } == null) { if (!superClass.isInternalSerializable && superClass.constructors.singleOrNull { it.valueParameters.size == 0 } == null) {
trace.reportOnSerializableAnnotation(descriptor, SerializationErrors.NON_SERIALIZABLE_PARENT_MUST_HAVE_NOARG_CTOR) trace.reportOnSerializableOrMetaAnnotation(descriptor, SerializationErrors.NON_SERIALIZABLE_PARENT_MUST_HAVE_NOARG_CTOR)
return false return false
} }
} }
@@ -194,7 +198,7 @@ open class SerializationPluginDeclarationChecker : DeclarationChecker {
} }
private fun checkClassWithCustomSerializer(descriptor: ClassDescriptor, declaration: KtDeclaration, trace: BindingTrace) { private fun checkClassWithCustomSerializer(descriptor: ClassDescriptor, declaration: KtDeclaration, trace: BindingTrace) {
val annotationPsi = descriptor.findSerializableAnnotationDeclaration() val annotationPsi = descriptor.findSerializableOrMetaAnnotationDeclaration()
checkCustomSerializerMatch(descriptor.module, descriptor.defaultType, descriptor, annotationPsi, trace, declaration) checkCustomSerializerMatch(descriptor.module, descriptor.defaultType, descriptor, annotationPsi, trace, declaration)
checkCustomSerializerIsNotLocal(descriptor.module, descriptor, trace, declaration) checkCustomSerializerIsNotLocal(descriptor.module, descriptor, trace, declaration)
} }
@@ -213,7 +217,7 @@ open class SerializationPluginDeclarationChecker : DeclarationChecker {
private fun ClassDescriptor.isSerializableEnumWithMissingSerializer(): Boolean { private fun ClassDescriptor.isSerializableEnumWithMissingSerializer(): Boolean {
if (kind != ClassKind.ENUM_CLASS) return false if (kind != ClassKind.ENUM_CLASS) return false
if (annotations.hasAnnotation(serializableAnnotationFqName)) return false if (hasSerializableOrMetaAnnotation) return false
if (annotations.hasAnySerialAnnotation) return true if (annotations.hasAnySerialAnnotation) return true
return enumEntries().any { (it.annotations.hasAnySerialAnnotation) } return enumEntries().any { (it.annotations.hasAnySerialAnnotation) }
} }
@@ -226,12 +230,12 @@ open class SerializationPluginDeclarationChecker : DeclarationChecker {
} }
private fun buildSerializableProperties(descriptor: ClassDescriptor, trace: BindingTrace): SerializableProperties? { private fun buildSerializableProperties(descriptor: ClassDescriptor, trace: BindingTrace): SerializableProperties? {
if (!descriptor.annotations.hasAnnotation(serializableAnnotationFqName)) return null if (!descriptor.hasSerializableOrMetaAnnotation) return null
if (!descriptor.isInternalSerializable) return null if (!descriptor.isInternalSerializable) return null
if (descriptor.hasCompanionObjectAsSerializer) return null // customized by user if (descriptor.hasCompanionObjectAsSerializer) return null // customized by user
val props = SerializableProperties(descriptor, trace.bindingContext) val props = SerializableProperties(descriptor, trace.bindingContext)
if (!props.isExternallySerializable) trace.reportOnSerializableAnnotation( if (!props.isExternallySerializable) trace.reportOnSerializableOrMetaAnnotation(
descriptor, descriptor,
SerializationErrors.PRIMARY_CONSTRUCTOR_PARAMETER_IS_NOT_A_PROPERTY SerializationErrors.PRIMARY_CONSTRUCTOR_PARAMETER_IS_NOT_A_PROPERTY
) )
@@ -240,7 +244,7 @@ open class SerializationPluginDeclarationChecker : DeclarationChecker {
val namesSet = mutableSetOf<String>() val namesSet = mutableSetOf<String>()
props.serializableProperties.forEach { props.serializableProperties.forEach {
if (!namesSet.add(it.name)) { if (!namesSet.add(it.name)) {
descriptor.onSerializableAnnotation { a -> descriptor.onSerializableOrMetaAnnotation { a ->
trace.report(SerializationErrors.DUPLICATE_SERIAL_NAME.on(a, it.name)) trace.report(SerializationErrors.DUPLICATE_SERIAL_NAME.on(a, it.name))
} }
} }
@@ -331,11 +335,13 @@ open class SerializationPluginDeclarationChecker : DeclarationChecker {
if (type.genericIndex != null) return // type arguments always have serializer stored in class' field if (type.genericIndex != null) return // type arguments always have serializer stored in class' field
val element = ktType?.typeElement val element = ktType?.typeElement
if (type.isUnsupportedInlineType() && !canSupportInlineClasses(module, trace)) { if (type.isUnsupportedInlineType() && !canSupportInlineClasses(module, trace)) {
trace.report(SerializationErrors.INLINE_CLASSES_NOT_SUPPORTED.on( trace.report(
element ?: fallbackElement, SerializationErrors.INLINE_CLASSES_NOT_SUPPORTED.on(
VersionReader.minVersionForInlineClasses.toString(), element ?: fallbackElement,
VersionReader.getVersionsForCurrentModuleFromTrace(module, trace)?.implementationVersion.toString() VersionReader.minVersionForInlineClasses.toString(),
)) VersionReader.getVersionsForCurrentModuleFromTrace(module, trace)?.implementationVersion.toString()
)
)
} }
val serializer = findTypeSerializerOrContextUnchecked(module, type) val serializer = findTypeSerializerOrContextUnchecked(module, type)
if (serializer != null) { if (serializer != null) {
@@ -380,7 +386,7 @@ open class SerializationPluginDeclarationChecker : DeclarationChecker {
val serializerDescriptor = serializerType.toClassDescriptor ?: return val serializerDescriptor = serializerType.toClassDescriptor ?: return
if (DescriptorUtils.isLocal(serializerDescriptor)) { if (DescriptorUtils.isLocal(serializerDescriptor)) {
val element = declaration.findSerializableAnnotationDeclaration() ?: declarationElement val element = declaration.findSerializableOrMetaAnnotationDeclaration() ?: declarationElement
trace.report( trace.report(
SerializationErrors.LOCAL_SERIALIZER_USAGE.on( SerializationErrors.LOCAL_SERIALIZER_USAGE.on(
@@ -408,16 +414,19 @@ open class SerializationPluginDeclarationChecker : DeclarationChecker {
) )
} }
private inline fun ClassDescriptor.onSerializableAnnotation(report: (KtAnnotationEntry) -> Unit) { private inline fun ClassDescriptor.onSerializableOrMetaAnnotation(report: (KtAnnotationEntry) -> Unit) {
findSerializableAnnotationDeclaration()?.let(report) findSerializableOrMetaAnnotationDeclaration()?.let(report)
} }
private fun BindingTrace.reportOnSerializableAnnotation(descriptor: ClassDescriptor, error: DiagnosticFactory0<in KtAnnotationEntry>) { private fun BindingTrace.reportOnSerializableOrMetaAnnotation(
descriptor.onSerializableAnnotation { e -> descriptor: ClassDescriptor,
error: DiagnosticFactory0<in KtAnnotationEntry>
) {
descriptor.onSerializableOrMetaAnnotation { e ->
report(error.on(e)) report(error.on(e))
} }
} }
} }
internal val ClassDescriptor.serializableAnnotationIsUseless: Boolean internal val ClassDescriptor.serializableAnnotationIsUseless: Boolean
get() = hasSerializableAnnotationWithoutArgs && !isInternalSerializable && !hasCompanionObjectAsSerializer && !isSerializableEnum() && !isSealedSerializableInterface get() = hasSerializableOrMetaAnnotationWithoutArgs && !isInternalSerializable && !hasCompanionObjectAsSerializer && !isSerializableEnum() && !isSealedSerializableInterface
@@ -48,13 +48,11 @@ class SerializationComponentRegistrar : ComponentRegistrar {
StorageComponentContainerContributor.registerExtension(project, SerializationPluginComponentContainerContributor()) StorageComponentContainerContributor.registerExtension(project, SerializationPluginComponentContainerContributor())
} }
private fun registerProtoExtensions() { private fun registerProtoExtensions() {
SerializationPluginMetadataExtensions.registerAllExtensions(JvmProtoBufUtil.EXTENSION_REGISTRY) SerializationPluginMetadataExtensions.registerAllExtensions(JvmProtoBufUtil.EXTENSION_REGISTRY)
SerializationPluginMetadataExtensions.registerAllExtensions(JsSerializerProtocol.extensionRegistry) SerializationPluginMetadataExtensions.registerAllExtensions(JsSerializerProtocol.extensionRegistry)
SerializationPluginMetadataExtensions.registerAllExtensions(KlibMetadataSerializerProtocol.extensionRegistry) SerializationPluginMetadataExtensions.registerAllExtensions(KlibMetadataSerializerProtocol.extensionRegistry)
} }
} }
} }
@@ -49,10 +49,11 @@ class SerializationPluginContext(baseContext: IrPluginContext, val metadataPlugi
private class SerializerClassLowering( private class SerializerClassLowering(
baseContext: IrPluginContext, baseContext: IrPluginContext,
metadataPlugin: SerializationDescriptorSerializerPlugin? metadataPlugin: SerializationDescriptorSerializerPlugin?,
moduleFragment: IrModuleFragment
) : IrElementTransformerVoid(), ClassLoweringPass { ) : IrElementTransformerVoid(), ClassLoweringPass {
val context: SerializationPluginContext = SerializationPluginContext(baseContext, metadataPlugin) val context: SerializationPluginContext = SerializationPluginContext(baseContext, metadataPlugin)
private val serialInfoJvmGenerator = SerialInfoImplJvmIrGenerator(context).also { context.serialInfoImplJvmIrGenerator = it } private val serialInfoJvmGenerator = SerialInfoImplJvmIrGenerator(context, moduleFragment).also { context.serialInfoImplJvmIrGenerator = it }
override fun lower(irClass: IrClass) { override fun lower(irClass: IrClass) {
SerializableIrGenerator.generate(irClass, context, context.bindingContext) SerializableIrGenerator.generate(irClass, context, context.bindingContext)
@@ -72,7 +73,7 @@ open class SerializationLoweringExtension @JvmOverloads constructor(
moduleFragment: IrModuleFragment, moduleFragment: IrModuleFragment,
pluginContext: IrPluginContext pluginContext: IrPluginContext
) { ) {
val serializerClassLowering = SerializerClassLowering(pluginContext, metadataPlugin) val serializerClassLowering = SerializerClassLowering(pluginContext, metadataPlugin, moduleFragment)
for (file in moduleFragment.files) for (file in moduleFragment.files)
serializerClassLowering.runOnFileInOrder(file) serializerClassLowering.runOnFileInOrder(file)
} }
@@ -8,6 +8,7 @@ package org.jetbrains.kotlinx.serialization.compiler.resolve
import org.jetbrains.kotlin.builtins.KotlinBuiltIns import org.jetbrains.kotlin.builtins.KotlinBuiltIns
import org.jetbrains.kotlin.descriptors.* import org.jetbrains.kotlin.descriptors.*
import org.jetbrains.kotlin.descriptors.annotations.Annotated import org.jetbrains.kotlin.descriptors.annotations.Annotated
import org.jetbrains.kotlin.descriptors.annotations.AnnotationDescriptor
import org.jetbrains.kotlin.descriptors.annotations.Annotations import org.jetbrains.kotlin.descriptors.annotations.Annotations
import org.jetbrains.kotlin.name.ClassId import org.jetbrains.kotlin.name.ClassId
import org.jetbrains.kotlin.name.FqName import org.jetbrains.kotlin.name.FqName
@@ -23,7 +24,9 @@ import org.jetbrains.kotlin.types.*
import org.jetbrains.kotlin.types.typeUtil.isSubtypeOf import org.jetbrains.kotlin.types.typeUtil.isSubtypeOf
import org.jetbrains.kotlin.types.typeUtil.representativeUpperBound import org.jetbrains.kotlin.types.typeUtil.representativeUpperBound
import org.jetbrains.kotlinx.serialization.compiler.resolve.SerializationAnnotations.inheritableSerialInfoFqName import org.jetbrains.kotlinx.serialization.compiler.resolve.SerializationAnnotations.inheritableSerialInfoFqName
import org.jetbrains.kotlinx.serialization.compiler.resolve.SerializationAnnotations.metaSerializableAnnotationFqName
import org.jetbrains.kotlinx.serialization.compiler.resolve.SerializationAnnotations.serialInfoFqName import org.jetbrains.kotlinx.serialization.compiler.resolve.SerializationAnnotations.serialInfoFqName
import org.jetbrains.kotlinx.serialization.compiler.resolve.SerializationAnnotations.serializableAnnotationFqName
internal fun isAllowedToHaveAutoGeneratedSerializerMethods( internal fun isAllowedToHaveAutoGeneratedSerializerMethods(
classDescriptor: ClassDescriptor, classDescriptor: ClassDescriptor,
@@ -47,7 +50,6 @@ internal fun isGeneratedKSerializer(type: KotlinType?): Boolean =
internal fun ClassDescriptor.getGeneratedSerializerDescriptor(): ClassDescriptor = internal fun ClassDescriptor.getGeneratedSerializerDescriptor(): ClassDescriptor =
module.getClassFromInternalSerializationPackage(SerialEntityNames.GENERATED_SERIALIZER_CLASS.identifier) module.getClassFromInternalSerializationPackage(SerialEntityNames.GENERATED_SERIALIZER_CLASS.identifier)
internal fun ClassDescriptor.createSerializerTypeFor(argument: SimpleType, baseSerializerInterface: FqName): SimpleType { internal fun ClassDescriptor.createSerializerTypeFor(argument: SimpleType, baseSerializerInterface: FqName): SimpleType {
val projectionType = Variance.INVARIANT val projectionType = Variance.INVARIANT
val types = listOf(TypeProjectionImpl(projectionType, argument)) val types = listOf(TypeProjectionImpl(projectionType, argument))
@@ -68,13 +70,15 @@ internal val DeclarationDescriptor.serializableWith: KotlinType?
get() = annotations.serializableWith(module) get() = annotations.serializableWith(module)
internal fun Annotations.serializableWith(module: ModuleDescriptor): KotlinType? = internal fun Annotations.serializableWith(module: ModuleDescriptor): KotlinType? =
this.findAnnotationKotlinTypeValue(SerializationAnnotations.serializableAnnotationFqName, module,"with") this.findAnnotationKotlinTypeValue(serializableAnnotationFqName, module, "with")
internal val DeclarationDescriptor.serializerForClass: KotlinType? internal val DeclarationDescriptor.serializerForClass: KotlinType?
get() = annotations.findAnnotationKotlinTypeValue(SerializationAnnotations.serializerAnnotationFqName, module, "forClass") get() = annotations.findAnnotationKotlinTypeValue(SerializationAnnotations.serializerAnnotationFqName, module, "forClass")
internal val ClassDescriptor.isSerialInfoAnnotation: Boolean internal val ClassDescriptor.isSerialInfoAnnotation: Boolean
get() = annotations.hasAnnotation(serialInfoFqName) || annotations.hasAnnotation(inheritableSerialInfoFqName) get() = annotations.hasAnnotation(serialInfoFqName)
|| annotations.hasAnnotation(inheritableSerialInfoFqName)
|| annotations.hasAnnotation(metaSerializableAnnotationFqName)
internal val ClassDescriptor.isInheritableSerialInfoAnnotation: Boolean internal val ClassDescriptor.isInheritableSerialInfoAnnotation: Boolean
get() = annotations.hasAnnotation(inheritableSerialInfoFqName) get() = annotations.hasAnnotation(inheritableSerialInfoFqName)
@@ -93,7 +97,7 @@ internal val Annotations.serialTransient: Boolean
val KotlinType?.toClassDescriptor: ClassDescriptor? val KotlinType?.toClassDescriptor: ClassDescriptor?
@JvmName("toClassDescriptor") @JvmName("toClassDescriptor")
get() = this?.constructor?.declarationDescriptor?.let { descriptor -> get() = this?.constructor?.declarationDescriptor?.let { descriptor ->
when(descriptor) { when (descriptor) {
is ClassDescriptor -> descriptor is ClassDescriptor -> descriptor
is TypeParameterDescriptor -> descriptor.representativeUpperBound.toClassDescriptor is TypeParameterDescriptor -> descriptor.representativeUpperBound.toClassDescriptor
else -> null else -> null
@@ -101,26 +105,27 @@ val KotlinType?.toClassDescriptor: ClassDescriptor?
} }
internal val ClassDescriptor.shouldHaveGeneratedMethodsInCompanion: Boolean internal val ClassDescriptor.shouldHaveGeneratedMethodsInCompanion: Boolean
get() = this.isSerializableObject || this.isSerializableEnum() || this.kind == ClassKind.CLASS && hasSerializableAnnotation || this.isSealedSerializableInterface get() = this.isSerializableObject || this.isSerializableEnum() || (this.kind == ClassKind.CLASS && hasSerializableOrMetaAnnotation) || this.isSealedSerializableInterface
internal val ClassDescriptor.isSerializableObject: Boolean internal val ClassDescriptor.isSerializableObject: Boolean
get() = kind == ClassKind.OBJECT && hasSerializableAnnotation get() = kind == ClassKind.OBJECT && hasSerializableOrMetaAnnotation
internal val ClassDescriptor.isInternallySerializableObject: Boolean internal val ClassDescriptor.isInternallySerializableObject: Boolean
get() = kind == ClassKind.OBJECT && hasSerializableAnnotationWithoutArgs get() = kind == ClassKind.OBJECT && hasSerializableOrMetaAnnotationWithoutArgs
internal val ClassDescriptor.isSealedSerializableInterface: Boolean internal val ClassDescriptor.isSealedSerializableInterface: Boolean
get() = kind == ClassKind.INTERFACE && modality == Modality.SEALED && hasSerializableAnnotation get() = kind == ClassKind.INTERFACE && modality == Modality.SEALED && hasSerializableOrMetaAnnotation
internal val ClassDescriptor.isInternalSerializable: Boolean //todo normal checking internal val ClassDescriptor.isInternalSerializable: Boolean //todo normal checking
get() { get() {
if (kind != ClassKind.CLASS) return false if (kind != ClassKind.CLASS) return false
return hasSerializableAnnotationWithoutArgs return hasSerializableOrMetaAnnotationWithoutArgs
} }
internal fun ClassDescriptor.isSerializableEnum(): Boolean = kind == ClassKind.ENUM_CLASS && hasSerializableAnnotation internal fun ClassDescriptor.isSerializableEnum(): Boolean = kind == ClassKind.ENUM_CLASS && hasSerializableOrMetaAnnotation
internal fun ClassDescriptor.isInternallySerializableEnum(): Boolean = kind == ClassKind.ENUM_CLASS && hasSerializableAnnotationWithoutArgs internal fun ClassDescriptor.isInternallySerializableEnum(): Boolean =
kind == ClassKind.ENUM_CLASS && hasSerializableOrMetaAnnotationWithoutArgs
internal val ClassDescriptor.shouldHaveGeneratedSerializer: Boolean internal val ClassDescriptor.shouldHaveGeneratedSerializer: Boolean
get() = (isInternalSerializable && (modality == Modality.FINAL || modality == Modality.OPEN)) || isInternallySerializableEnum() get() = (isInternalSerializable && (modality == Modality.FINAL || modality == Modality.OPEN)) || isInternallySerializableEnum()
@@ -136,25 +141,47 @@ internal fun ClassDescriptor.enumEntries(): List<ClassDescriptor> {
internal val Annotations.hasAnySerialAnnotation: Boolean internal val Annotations.hasAnySerialAnnotation: Boolean
get() = serialNameValue != null || any { it.annotationClass?.isSerialInfoAnnotation == true } get() = serialNameValue != null || any { it.annotationClass?.isSerialInfoAnnotation == true }
internal val ClassDescriptor.hasSerializableAnnotation internal val ClassDescriptor.hasSerializableOrMetaAnnotation
get() = annotations.hasAnnotation(SerializationAnnotations.serializableAnnotationFqName) get() = hasSerializableAnnotation || hasMetaSerializableAnnotation
internal val ClassDescriptor.hasSerializableAnnotationWithoutArgs: Boolean private val ClassDescriptor.hasSerializableAnnotation
get() = annotations.hasSerializableAnnotation
private val Annotations.hasSerializableAnnotation
get() = hasAnnotation(serializableAnnotationFqName)
internal val ClassDescriptor.hasMetaSerializableAnnotation: Boolean
get() = annotations.any { it.isMetaSerializableAnnotation }
internal val AnnotationDescriptor.isMetaSerializableAnnotation: Boolean
get() = annotationClass?.annotations?.hasAnnotation(metaSerializableAnnotationFqName) ?: false
internal val ClassDescriptor.hasSerializableOrMetaAnnotationWithoutArgs: Boolean
get() = hasSerializableAnnotationWithoutArgs
|| (!annotations.hasSerializableAnnotation && hasMetaSerializableAnnotation)
private val ClassDescriptor.hasSerializableAnnotationWithoutArgs: Boolean
get() { get() {
if (!hasSerializableAnnotation) return false if (!hasSerializableAnnotation) return false
// If provided descriptor is lazy, carefully look at psi in order not to trigger full resolve which may be recursive. // If provided descriptor is lazy, carefully look at psi in order not to trigger full resolve which may be recursive.
// Otherwise, this descriptor is deserialized from another module and it is OK to check value right away. // Otherwise, this descriptor is deserialized from another module, and it is OK to check value right away.
val psi = findSerializableAnnotationDeclaration() ?: return (serializableWith == null) val psi = findSerializableAnnotationDeclaration() ?: return (serializableWith == null)
return psi.valueArguments.isEmpty() return psi.valueArguments.isEmpty()
} }
internal fun Annotated.findSerializableAnnotationDeclaration(): KtAnnotationEntry? { private fun Annotated.findSerializableAnnotationDeclaration(): KtAnnotationEntry? {
val lazyDesc = annotations.findAnnotation(SerializationAnnotations.serializableAnnotationFqName) as? LazyAnnotationDescriptor val lazyDesc = annotations.findAnnotation(serializableAnnotationFqName) as? LazyAnnotationDescriptor
return lazyDesc?.annotationEntry
}
internal fun Annotated.findSerializableOrMetaAnnotationDeclaration(): KtAnnotationEntry? {
val lazyDesc = (annotations.findAnnotation(serializableAnnotationFqName)
?: annotations.firstOrNull { it.isMetaSerializableAnnotation }) as? LazyAnnotationDescriptor
return lazyDesc?.annotationEntry return lazyDesc?.annotationEntry
} }
// For abstract classes marked with @Serializable, // For abstract classes marked with @Serializable,
// methods are generated anyway although they shouldn't have // methods are generated anyway, although they shouldn't have
// generated $serializer and use Polymorphic one. // generated $serializer and use Polymorphic one.
internal fun ClassDescriptor.isAbstractOrSealedSerializableClass(): Boolean = internal fun ClassDescriptor.isAbstractOrSealedSerializableClass(): Boolean =
isInternalSerializable && (modality == Modality.ABSTRACT || modality == Modality.SEALED) isInternalSerializable && (modality == Modality.ABSTRACT || modality == Modality.SEALED)
@@ -235,18 +262,18 @@ internal fun getSerializableClassDescriptorBySerializer(serializerDescriptor: Cl
} }
internal fun ClassDescriptor.checkSerializableClassPropertyResult(prop: PropertyDescriptor): Boolean = internal fun ClassDescriptor.checkSerializableClassPropertyResult(prop: PropertyDescriptor): Boolean =
prop.returnType!!.isSubtypeOf(getClassFromSerializationPackage(SerialEntityNames.SERIAL_DESCRIPTOR_CLASS).toSimpleType(false)) // todo: cache lookup prop.returnType!!.isSubtypeOf(getClassFromSerializationPackage(SerialEntityNames.SERIAL_DESCRIPTOR_CLASS).toSimpleType(false)) // todo: cache lookup
// todo: serialization: do an actual check better that just number of parameters // todo: serialization: do an actual check better that just number of parameters
internal fun ClassDescriptor.checkSaveMethodParameters(parameters: List<ValueParameterDescriptor>): Boolean = internal fun ClassDescriptor.checkSaveMethodParameters(parameters: List<ValueParameterDescriptor>): Boolean =
parameters.size == 2 parameters.size == 2
internal fun ClassDescriptor.checkSaveMethodResult(type: KotlinType): Boolean = internal fun ClassDescriptor.checkSaveMethodResult(type: KotlinType): Boolean =
KotlinBuiltIns.isUnit(type) KotlinBuiltIns.isUnit(type)
// todo: serialization: do an actual check better that just number of parameters // todo: serialization: do an actual check better that just number of parameters
internal fun ClassDescriptor.checkLoadMethodParameters(parameters: List<ValueParameterDescriptor>): Boolean = internal fun ClassDescriptor.checkLoadMethodParameters(parameters: List<ValueParameterDescriptor>): Boolean =
parameters.size == 1 parameters.size == 1
internal fun ClassDescriptor.checkLoadMethodResult(type: KotlinType): Boolean = internal fun ClassDescriptor.checkLoadMethodResult(type: KotlinType): Boolean =
getSerializableClassDescriptorBySerializer(this)?.defaultType == type getSerializableClassDescriptorBySerializer(this)?.defaultType == type
@@ -31,6 +31,7 @@ object SerializationAnnotations {
// Also implicitly used in kotlin-native.compiler.backend.native/CodeGenerationInfo.kt // Also implicitly used in kotlin-native.compiler.backend.native/CodeGenerationInfo.kt
internal val serialInfoFqName = FqName("kotlinx.serialization.SerialInfo") internal val serialInfoFqName = FqName("kotlinx.serialization.SerialInfo")
internal val inheritableSerialInfoFqName = FqName("kotlinx.serialization.InheritableSerialInfo") internal val inheritableSerialInfoFqName = FqName("kotlinx.serialization.InheritableSerialInfo")
internal val metaSerializableAnnotationFqName = FqName("kotlinx.serialization.MetaSerializable")
internal val encodeDefaultFqName = FqName("kotlinx.serialization.EncodeDefault") internal val encodeDefaultFqName = FqName("kotlinx.serialization.EncodeDefault")
internal val contextualFqName = FqName("kotlinx.serialization.ContextualSerialization") // this one is deprecated internal val contextualFqName = FqName("kotlinx.serialization.ContextualSerialization") // this one is deprecated
@@ -51,6 +52,7 @@ object SerialEntityNames {
const val CACHED_SERIALIZER_PROPERTY = "\$cachedSerializer" const val CACHED_SERIALIZER_PROPERTY = "\$cachedSerializer"
// classes // classes
val KCLASS_NAME_FQ = FqName("kotlin.reflect.KClass")
val KSERIALIZER_NAME = Name.identifier(KSERIALIZER_CLASS) val KSERIALIZER_NAME = Name.identifier(KSERIALIZER_CLASS)
val SERIAL_CTOR_MARKER_NAME = Name.identifier("SerializationConstructorMarker") val SERIAL_CTOR_MARKER_NAME = Name.identifier("SerializationConstructorMarker")
val KSERIALIZER_NAME_FQ = SerializationPackages.packageFqName.child(KSERIALIZER_NAME) val KSERIALIZER_NAME_FQ = SerializationPackages.packageFqName.child(KSERIALIZER_NAME)
@@ -29,7 +29,6 @@ class SerializableProperties(private val serializableClass: ClassDescriptor, val
val isExternallySerializable: Boolean val isExternallySerializable: Boolean
private val primaryConstructorProperties: Map<PropertyDescriptor, Boolean> private val primaryConstructorProperties: Map<PropertyDescriptor, Boolean>
init { init {
val descriptorsSequence = serializableClass.unsubstitutedMemberScope.getContributedDescriptors(DescriptorKindFilter.VARIABLES) val descriptorsSequence = serializableClass.unsubstitutedMemberScope.getContributedDescriptors(DescriptorKindFilter.VARIABLES)
.asSequence() .asSequence()
@@ -31,6 +31,12 @@ public class SerializationIrBoxTestGenerated extends AbstractSerializationIrBoxT
runTest("plugins/kotlin-serialization/kotlin-serialization-compiler/testData/boxIr/classSerializerAsObject.kt"); runTest("plugins/kotlin-serialization/kotlin-serialization-compiler/testData/boxIr/classSerializerAsObject.kt");
} }
@Test
@TestMetadata("metaSerializable.kt")
public void testMetaSerializable() throws Exception {
runTest("plugins/kotlin-serialization/kotlin-serialization-compiler/testData/boxIr/metaSerializable.kt");
}
@Test @Test
@TestMetadata("multimoduleInheritance.kt") @TestMetadata("multimoduleInheritance.kt")
public void testMultimoduleInheritance() throws Exception { public void testMultimoduleInheritance() throws Exception {
@@ -59,6 +59,11 @@ public class SerializationPluginDiagnosticTestGenerated extends AbstractSerializ
runTest("plugins/kotlin-serialization/kotlin-serialization-compiler/testData/diagnostics/LocalAndAnonymous.kt"); runTest("plugins/kotlin-serialization/kotlin-serialization-compiler/testData/diagnostics/LocalAndAnonymous.kt");
} }
@TestMetadata("MetaSerializable.kt")
public void testMetaSerializable() throws Exception {
runTest("plugins/kotlin-serialization/kotlin-serialization-compiler/testData/diagnostics/MetaSerializable.kt");
}
@TestMetadata("NoSuitableCtorInParent.kt") @TestMetadata("NoSuitableCtorInParent.kt")
public void testNoSuitableCtorInParent() throws Exception { public void testNoSuitableCtorInParent() throws Exception {
runTest("plugins/kotlin-serialization/kotlin-serialization-compiler/testData/diagnostics/NoSuitableCtorInParent.kt"); runTest("plugins/kotlin-serialization/kotlin-serialization-compiler/testData/diagnostics/NoSuitableCtorInParent.kt");
@@ -0,0 +1,186 @@
// IGNORE_BACKEND_FIR: JVM_IR
// TARGET_BACKEND: JVM_IR
// WITH_STDLIB
import kotlinx.serialization.*
import kotlinx.serialization.*
import kotlinx.serialization.encoding.*
import kotlinx.serialization.descriptors.*
import kotlinx.serialization.json.*
import kotlin.reflect.KClass
import kotlin.test.*
// TODO: for this test to work, runtime dependency should be updated to (yet unreleased) serialization with @MetaSerializable annotation
//@MetaSerializable
@Target(AnnotationTarget.CLASS, AnnotationTarget.PROPERTY)
annotation class MySerializable
//@MetaSerializable
@Target(AnnotationTarget.CLASS, AnnotationTarget.PROPERTY)
annotation class MySerializableWithInfo(
val value: Int,
val kclass: KClass<*>
)
@MySerializable
class Project1(val name: String, val language: String)
@MySerializableWithInfo(123, String::class)
class Project2(val name: String, val language: String)
@Serializable
class Wrapper(
// @MySerializableWithInfo(234, Int::class) val project: Project2
)
@Serializable
@MySerializableWithInfo(123, String::class)
class Project3(val name: String, val language: String)
@Serializable(with = MySerializer::class)
//@MySerializableWithInfo(123, String::class)
class Project4(val name: String, val language: String)
@MySerializableWithInfo(123, String::class)
sealed class TestSealed {
@MySerializableWithInfo(123, String::class)
class A(val value1: String) : TestSealed()
@MySerializableWithInfo(123, String::class)
class B(val value2: String) : TestSealed()
}
@MySerializable
abstract class TestAbstract {
@MySerializableWithInfo(123, String::class)
class A(val value1: String) : TestSealed()
@MySerializableWithInfo(123, String::class)
class B(val value2: String) : TestSealed()
}
@MySerializableWithInfo(123, String::class)
enum class TestEnum { Value1, Value2 }
@MySerializableWithInfo(123, String::class)
object TestObject
object MySerializer : KSerializer<Project4> {
override val descriptor: SerialDescriptor = PrimitiveSerialDescriptor("Project4", PrimitiveKind.STRING)
override fun serialize(encoder: Encoder, value: Project4) = encoder.encodeString("${value.name}:${value.language}")
override fun deserialize(decoder: Decoder): Project4 {
val params = decoder.decodeString().split(':')
return Project4(params[0], params[1])
}
}
fun testMetaSerializable() {
// val string = Json.encodeToString(Project1.serializer(), Project1("name", "lang"))
// assertEquals("""{"name":"name","language":"lang"}""", string)
//
// val reconstructed = Json.decodeFromString(Project1.serializer(), string)
// assertEquals("name", reconstructed.name)
// assertEquals("lang", reconstructed.language)
}
fun testMetaSerializableWithInfo() {
// val string = Json.encodeToString(Project2.serializer(), Project2("name", "lang"))
// assertEquals("""{"name":"name","language":"lang"}""", string)
//
// val reconstructed = Json.decodeFromString(Project2.serializer(), string)
// assertEquals("name", reconstructed.name)
// assertEquals("lang", reconstructed.language)
//
// val info = Project2.serializer().descriptor.annotations.filterIsInstance<MySerializableWithInfo>().first()
// assertEquals(123, info.value)
// assertEquals(String::class, info.kclass)
}
fun testMetaSerializableOnProperty() {
// val info = Wrapper.serializer().descriptor.getElementAnnotations(0).filterIsInstance<MySerializableWithInfo>().first()
// assertEquals(234, info.value)
// assertEquals(Int::class, info.kclass)
}
fun testSerializableAndMetaAnnotation() {
// val string = Json.encodeToString(Project3.serializer(), Project3("name", "lang"))
// assertEquals("""{"name":"name","language":"lang"}""", string)
//
// val reconstructed = Json.decodeFromString(Project3.serializer(), string)
// assertEquals("name", reconstructed.name)
// assertEquals("lang", reconstructed.language)
//
// val info = Project3.serializer().descriptor.annotations.filterIsInstance<MySerializableWithInfo>().first()
// assertEquals(123, info.value)
// assertEquals(String::class, info.kclass)
}
fun testCustomSerializerAndMetaAnnotation() {
// val string = Json.encodeToString(Project4.serializer(), Project4("name", "lang"))
// assertEquals("""name:lang""", string)
//
// val reconstructed = Json.decodeFromString(Project4.serializer(), string)
// assertEquals("name", reconstructed.name)
// assertEquals("lang", reconstructed.language)
}
fun testSealed() {
// val serializerA = TestSealed.A.serializer()
// val serializerB = TestSealed.B.serializer()
// assertNotNull(serializerA)
// assertNotNull(serializerB)
//
// val infoA = serializerA.descriptor.annotations.filterIsInstance<MySerializableWithInfo>().first()
// val infoB = serializerB.descriptor.annotations.filterIsInstance<MySerializableWithInfo>().first()
// assertEquals(123, infoA.value)
// assertEquals(String::class, infoA.kclass)
// assertEquals(123, infoB.value)
// assertEquals(String::class, infoB.kclass)
}
fun testAbstract() {
// val serializerA = TestAbstract.A.serializer()
// val serializerB = TestAbstract.B.serializer()
// assertNotNull(serializerA)
// assertNotNull(serializerB)
//
// val infoA = serializerA.descriptor.annotations.filterIsInstance<MySerializableWithInfo>().first()
// val infoB = serializerB.descriptor.annotations.filterIsInstance<MySerializableWithInfo>().first()
// assertEquals(123, infoA.value)
// assertEquals(String::class, infoA.kclass)
// assertEquals(123, infoB.value)
// assertEquals(String::class, infoB.kclass)
}
fun testEnum() {
// val serializer = TestEnum.serializer()
// assertNotNull(serializer)
//
// val info = serializer.descriptor.annotations.filterIsInstance<MySerializableWithInfo>().first()
// assertEquals(123, info.value)
// assertEquals(String::class, info.kclass)
}
fun testObject() {
// val serializer = TestObject.serializer()
// assertNotNull(serializer)
//
// val info = serializer.descriptor.annotations.filterIsInstance<MySerializableWithInfo>().first()
// assertEquals(123, info.value)
// assertEquals(String::class, info.kclass)
}
fun box(): String {
testMetaSerializable()
testMetaSerializableWithInfo()
testMetaSerializableOnProperty()
testSealed()
testAbstract()
testEnum()
testObject()
return "OK"
}