diff --git a/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/common/SerializerCodegen.kt b/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/common/SerializerCodegen.kt index a4a82e52fce..d1e1fb1a04b 100644 --- a/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/common/SerializerCodegen.kt +++ b/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/common/SerializerCodegen.kt @@ -21,7 +21,6 @@ import org.jetbrains.kotlin.descriptors.* import org.jetbrains.kotlin.incremental.components.NoLookupLocation import org.jetbrains.kotlin.name.Name import org.jetbrains.kotlin.resolve.BindingContext -import org.jetbrains.kotlin.resolve.descriptorUtil.fqNameUnsafe import org.jetbrains.kotlin.resolve.scopes.getDescriptorsFiltered import org.jetbrains.kotlinx.serialization.compiler.resolve.* @@ -30,7 +29,7 @@ abstract class SerializerCodegen( bindingContext: BindingContext ) : AbstractSerialGenerator(bindingContext, serializerDescriptor) { val serializableDescriptor: ClassDescriptor = getSerializableClassDescriptorBySerializer(serializerDescriptor)!! - protected val serialName: String = serializableDescriptor.annotations.serialNameValue ?: serializableDescriptor.fqNameUnsafe.asString() + protected val serialName: String = serializableDescriptor.serialName() protected val properties = bindingContext.serializablePropertiesFor(serializableDescriptor) protected val serializableProperties = properties.serializableProperties diff --git a/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/common/TypeUtil.kt b/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/common/TypeUtil.kt index 9db693ea9de..7530c4a7af8 100644 --- a/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/common/TypeUtil.kt +++ b/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/common/TypeUtil.kt @@ -96,7 +96,11 @@ fun AbstractSerialGenerator.getSerialTypeInfo(property: SerializableProperty): S fun KotlinType.serialName(): String { val serializableDescriptor = this.toClassDescriptor!! - return serializableDescriptor.annotations.serialNameValue ?: serializableDescriptor.fqNameUnsafe.asString() + return serializableDescriptor.serialName() +} + +fun ClassDescriptor.serialName(): String { + return annotations.serialNameValue ?: fqNameUnsafe.asString() } /** @@ -206,7 +210,9 @@ fun findStandardKotlinTypeSerializer(module: ModuleDescriptor, kType: KotlinType fun findEnumTypeSerializer(module: ModuleDescriptor, kType: KotlinType): ClassDescriptor? { val classDescriptor = kType.toClassDescriptor ?: return null - return if (classDescriptor.kind == ClassKind.ENUM_CLASS) module.findClassAcrossModuleDependencies(enumSerializerId) else null + return if (classDescriptor.kind == ClassKind.ENUM_CLASS && !classDescriptor.isSerializableEnum()) + module.findClassAcrossModuleDependencies(enumSerializerId) + else null } internal fun KtPureClassOrObject.bodyPropertiesDescriptorsMap( 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 c4c36d9f6c7..2fbbfd80334 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 @@ -27,6 +27,7 @@ import org.jetbrains.kotlin.resolve.descriptorUtil.getSuperClassOrAny import org.jetbrains.kotlin.resolve.descriptorUtil.module import org.jetbrains.kotlin.util.OperatorNameConventions import org.jetbrains.kotlinx.serialization.compiler.backend.common.SerializableCodegen +import org.jetbrains.kotlinx.serialization.compiler.diagnostic.serializableAnnotationIsUseless import org.jetbrains.kotlinx.serialization.compiler.resolve.* import org.jetbrains.kotlinx.serialization.compiler.resolve.SerialEntityNames.MISSING_FIELD_EXC @@ -176,7 +177,7 @@ class SerializableIrGenerator( SerializableIrGenerator(irClass, context, bindingContext).generate() irClass.patchDeclarationParents(irClass.parent) } - else if (serializableClass.hasSerializableAnnotationWithoutArgs && !serializableClass.hasCompanionObjectAsSerializer) { + else if (serializableClass.serializableAnnotationIsUseless) { throw CompilationException( "@Serializable annotation on $serializableClass would be ignored because it is impossible to serialize it automatically. " + "Provide serializer manually via e.g. companion object", null, serializableClass.findPsi() diff --git a/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/js/SerializableJsTranslator.kt b/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/js/SerializableJsTranslator.kt index c93201071c4..0b0f269933d 100644 --- a/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/js/SerializableJsTranslator.kt +++ b/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/js/SerializableJsTranslator.kt @@ -36,6 +36,7 @@ import org.jetbrains.kotlin.resolve.descriptorUtil.getSuperClassNotAny import org.jetbrains.kotlin.resolve.descriptorUtil.getSuperClassOrAny import org.jetbrains.kotlinx.serialization.compiler.backend.common.SerializableCodegen import org.jetbrains.kotlinx.serialization.compiler.backend.common.anonymousInitializers +import org.jetbrains.kotlinx.serialization.compiler.diagnostic.serializableAnnotationIsUseless import org.jetbrains.kotlinx.serialization.compiler.resolve.* import org.jetbrains.kotlinx.serialization.compiler.resolve.SerialEntityNames.MISSING_FIELD_EXC @@ -164,7 +165,7 @@ class SerializableJsTranslator( ) { if (serializableClass.isInternalSerializable) SerializableJsTranslator(declaration, serializableClass, context).generate() - else if (serializableClass.hasSerializableAnnotationWithoutArgs && !serializableClass.hasCompanionObjectAsSerializer) { + else if (serializableClass.serializableAnnotationIsUseless) { throw CompilationException( "@Serializable annotation on $serializableClass would be ignored because it is impossible to serialize it automatically. " + "Provide serializer manually via e.g. companion object", null, serializableClass.findPsi() diff --git a/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/jvm/JVMCodegenUtil.kt b/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/jvm/JVMCodegenUtil.kt index 3014c14fcb5..6529ac2fa3d 100644 --- a/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/jvm/JVMCodegenUtil.kt +++ b/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/jvm/JVMCodegenUtil.kt @@ -38,6 +38,7 @@ import org.jetbrains.kotlinx.serialization.compiler.resolve.SerialEntityNames.MI import org.jetbrains.kotlinx.serialization.compiler.resolve.SerialEntityNames.SERIAL_CTOR_MARKER_NAME import org.jetbrains.kotlinx.serialization.compiler.resolve.SerialEntityNames.SERIAL_DESCRIPTOR_CLASS import org.jetbrains.kotlinx.serialization.compiler.resolve.SerialEntityNames.SERIAL_DESCRIPTOR_CLASS_IMPL +import org.jetbrains.kotlinx.serialization.compiler.resolve.SerialEntityNames.SERIAL_DESCRIPTOR_FOR_ENUM import org.jetbrains.kotlinx.serialization.compiler.resolve.SerialEntityNames.SERIAL_EXC import org.jetbrains.kotlinx.serialization.compiler.resolve.SerialEntityNames.SERIAL_LOADER_CLASS import org.jetbrains.kotlinx.serialization.compiler.resolve.SerialEntityNames.SERIAL_SAVER_CLASS @@ -53,6 +54,7 @@ import org.jetbrains.org.objectweb.asm.commons.InstructionAdapter // todo: extract packages constants too? internal val descType = Type.getObjectType("kotlinx/serialization/$SERIAL_DESCRIPTOR_CLASS") internal val descImplType = Type.getObjectType("kotlinx/serialization/internal/$SERIAL_DESCRIPTOR_CLASS_IMPL") +internal val descriptorForEnumsType = Type.getObjectType("kotlinx/serialization/internal/$SERIAL_DESCRIPTOR_FOR_ENUM") internal val generatedSerializerType = Type.getObjectType("kotlinx/serialization/internal/${SerialEntityNames.GENERATED_SERIALIZER_CLASS}") internal val kOutputType = Type.getObjectType("kotlinx/serialization/$STRUCTURE_ENCODER_CLASS") internal val encoderType = Type.getObjectType("kotlinx/serialization/$ENCODER_CLASS") diff --git a/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/jvm/SerializableCodegenImpl.kt b/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/jvm/SerializableCodegenImpl.kt index 66f273ba3bd..798763fc551 100644 --- a/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/jvm/SerializableCodegenImpl.kt +++ b/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/jvm/SerializableCodegenImpl.kt @@ -29,6 +29,7 @@ import org.jetbrains.kotlin.resolve.DescriptorUtils import org.jetbrains.kotlin.resolve.descriptorUtil.getSuperClassOrAny import org.jetbrains.kotlin.resolve.descriptorUtil.module import org.jetbrains.kotlinx.serialization.compiler.backend.common.* +import org.jetbrains.kotlinx.serialization.compiler.diagnostic.serializableAnnotationIsUseless import org.jetbrains.kotlinx.serialization.compiler.resolve.* import org.jetbrains.org.objectweb.asm.Label import org.jetbrains.org.objectweb.asm.Type @@ -45,7 +46,7 @@ class SerializableCodegenImpl( val serializableClass = codegen.descriptor if (serializableClass.isInternalSerializable) SerializableCodegenImpl(codegen).generate() - else if (serializableClass.hasSerializableAnnotationWithoutArgs && !serializableClass.hasCompanionObjectAsSerializer) { + else if (serializableClass.serializableAnnotationIsUseless) { throw CompilationException( "@Serializable annotation on $serializableClass would be ignored because it is impossible to serialize it automatically. " + "Provide serializer manually via e.g. companion object", null, serializableClass.findPsi() diff --git a/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/jvm/SerializerCodegenImpl.kt b/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/jvm/SerializerCodegenImpl.kt index 434b66dfd26..a6e2ea2ac0b 100644 --- a/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/jvm/SerializerCodegenImpl.kt +++ b/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/jvm/SerializerCodegenImpl.kt @@ -18,6 +18,7 @@ package org.jetbrains.kotlinx.serialization.compiler.backend.jvm import org.jetbrains.kotlin.codegen.* import org.jetbrains.kotlin.descriptors.* +import org.jetbrains.kotlin.descriptors.annotations.Annotated import org.jetbrains.kotlin.psi.ValueArgument import org.jetbrains.kotlin.resolve.jvm.AsmTypes import org.jetbrains.kotlin.resolve.jvm.diagnostics.OtherOrigin @@ -29,16 +30,16 @@ import org.jetbrains.org.objectweb.asm.Opcodes.* import org.jetbrains.org.objectweb.asm.Type import org.jetbrains.org.objectweb.asm.commons.InstructionAdapter -class SerializerCodegenImpl( - private val codegen: ImplementationBodyCodegen, +open class SerializerCodegenImpl( + protected val codegen: ImplementationBodyCodegen, serializableClass: ClassDescriptor ) : SerializerCodegen(codegen.descriptor, codegen.bindingContext) { private val serialDescField = "\$\$serialDesc" - private val serializerAsmType = codegen.typeMapper.mapClass(codegen.descriptor) - private val serializableAsmType = codegen.typeMapper.mapClass(serializableClass) + protected val serializerAsmType = codegen.typeMapper.mapClass(codegen.descriptor) + protected val serializableAsmType = codegen.typeMapper.mapClass(serializableClass) // if we have type parameters, descriptor initializing must be performed in constructor private val staticDescriptor = serializableDescriptor.declaredTypeParameters.isEmpty() @@ -46,7 +47,12 @@ class SerializerCodegenImpl( companion object { fun generateSerializerExtensions(codegen: ImplementationBodyCodegen) { val serializableClass = getSerializableClassDescriptorBySerializer(codegen.descriptor) ?: return - SerializerCodegenImpl(codegen, serializableClass).generate() + val serializerCodegen = if (serializableClass.isSerializableEnum()) { + SerializerForEnumsCodegen(codegen, serializableClass) + } else { + SerializerCodegenImpl(codegen, serializableClass) + } + serializerCodegen.generate() } } @@ -74,6 +80,23 @@ class SerializerCodegenImpl( } private fun ExpressionCodegen.generateSerialDescriptor(descriptorVar: Int, isStatic: Boolean) = with(v) { + instantiateNewDescriptor(isStatic) + store(descriptorVar, descImplType) + // add contents + addElementsContentToDescriptor(descriptorVar) + // add annotations on class itself + addSyntheticAnnotationsToDescriptor(descriptorVar, serializableDescriptor, CallingConventions.addClassAnnotation) + if (isStatic) { + load(descriptorVar, descImplType) + putstatic(serializerAsmType.internalName, serialDescField, descType.descriptor) + } else { + load(0, serializerAsmType) + load(descriptorVar, descImplType) + putfield(serializerAsmType.internalName, serialDescField, descType.descriptor) + } + } + + protected open fun ExpressionCodegen.instantiateNewDescriptor(isStatic: Boolean) = with(v) { anew(descImplType) dup() aconst(serialName) @@ -88,7 +111,9 @@ class SerializerCodegenImpl( load(0, serializerAsmType) } invokespecial(descImplType.internalName, "", "(Ljava/lang/String;${generatedSerializerType.descriptor})V", false) - store(descriptorVar, descImplType) + } + + protected open fun ExpressionCodegen.addElementsContentToDescriptor(descriptorVar: Int) = with(v) { for (property in serializableProperties) { if (property.transient) continue load(descriptorVar, descImplType) @@ -96,39 +121,24 @@ class SerializerCodegenImpl( iconst(if (property.optional) 1 else 0) invokevirtual(descImplType.internalName, CallingConventions.addElement, "(Ljava/lang/String;Z)V", false) // pushing annotations - for ((annotationClass, args, consParams) in property.annotationsWithArguments) { + addSyntheticAnnotationsToDescriptor(descriptorVar, property.descriptor, CallingConventions.addAnnotation) + } + } + + protected fun ExpressionCodegen.addSyntheticAnnotationsToDescriptor(descriptorVar: Int, annotated: Annotated, functionToCall: String) = + with(v) { + for ((annotationClass, args, consParams) in annotated.annotationsWithArguments()) { if (args.size != consParams.size) throw IllegalArgumentException("Can't use arguments with defaults for serializable annotations yet") load(descriptorVar, descImplType) generateSyntheticAnnotationOnStack(annotationClass, args, consParams) invokevirtual( descImplType.internalName, - CallingConventions.addAnnotation, + functionToCall, "(Ljava/lang/annotation/Annotation;)V", false ) } } - // add annotations on class itself - for ((annotationClass, args, consParams) in serializableDescriptor.annotationsWithArguments()) { - if (args.size != consParams.size) throw IllegalArgumentException("Can't use arguments with defaults for serializable annotations yet") - load(descriptorVar, descImplType) - generateSyntheticAnnotationOnStack(annotationClass, args, consParams) - invokevirtual( - descImplType.internalName, - CallingConventions.addClassAnnotation, - "(Ljava/lang/annotation/Annotation;)V", - false - ) - } - if (isStatic) { - load(descriptorVar, descImplType) - putstatic(serializerAsmType.internalName, serialDescField, descType.descriptor) - } else { - load(0, serializerAsmType) - load(descriptorVar, descImplType) - putfield(serializerAsmType.internalName, serialDescField, descType.descriptor) - } - } override fun generateSerialDesc() { var flags = ACC_PRIVATE or ACC_FINAL or ACC_SYNTHETIC @@ -168,7 +178,7 @@ class SerializerCodegenImpl( } // use null to put value on stack, use number to store it to var - private fun InstructionAdapter.stackSerialClassDesc(classDescVar: Int?) { + protected fun InstructionAdapter.stackSerialClassDesc(classDescVar: Int?) { if (staticDescriptor) getstatic(serializerAsmType.internalName, serialDescField, descType.descriptor) else { diff --git a/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/jvm/SerializerForEnumsCodegen.kt b/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/jvm/SerializerForEnumsCodegen.kt new file mode 100644 index 00000000000..2fdfb205962 --- /dev/null +++ b/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/jvm/SerializerForEnumsCodegen.kt @@ -0,0 +1,65 @@ +package org.jetbrains.kotlinx.serialization.compiler.backend.jvm + +import org.jetbrains.kotlin.codegen.ExpressionCodegen +import org.jetbrains.kotlin.codegen.ImplementationBodyCodegen +import org.jetbrains.kotlin.descriptors.ClassDescriptor +import org.jetbrains.kotlin.descriptors.FunctionDescriptor +import org.jetbrains.kotlinx.serialization.compiler.resolve.CallingConventions +import org.jetbrains.kotlinx.serialization.compiler.resolve.enumEntries +import org.jetbrains.kotlinx.serialization.compiler.resolve.serialNameValue +import org.jetbrains.org.objectweb.asm.Type + +class SerializerForEnumsCodegen( + codegen: ImplementationBodyCodegen, + serializableClass: ClassDescriptor +) : SerializerCodegenImpl(codegen, serializableClass) { + override fun generateSave(function: FunctionDescriptor) = codegen.generateMethod(function) { _, _ -> + // fun save(output: KOutput, obj : T) + val outputVar = 1 + val objVar = 2 + // output.encodeEnum(descriptor, ordinal) + load(outputVar, encoderType) + stackSerialClassDesc(null) + load(objVar, serializableAsmType) + invokevirtual(serializableAsmType.internalName, "ordinal", "()I", false) + invokeinterface(encoderType.internalName, "encodeEnum", "(${descType.descriptor}I)V") + // return + areturn(Type.VOID_TYPE) + } + + override fun generateLoad(function: FunctionDescriptor) = codegen.generateMethod(function) { _, _ -> + // fun load(input: KInput): T + val inputVar = 1 + val serializableArrayType = Type.getType("[L${serializableAsmType.internalName};") + // T.values() + invokestatic(serializableAsmType.internalName, "values", "()${serializableArrayType.descriptor}", false) + // input.decodeEnum(descriptor) + load(inputVar, decoderType) + stackSerialClassDesc(null) + invokeinterface(decoderType.internalName, "decodeEnum", "(${descType.descriptor})I") + // return + aload(serializableAsmType) + areturn(serializableAsmType) + } + + override fun ExpressionCodegen.instantiateNewDescriptor(isStatic: Boolean) = with(v) { + anew(descriptorForEnumsType) + dup() + aconst(serialName) + invokespecial(descriptorForEnumsType.internalName, "", "(Ljava/lang/String;)V", false) + checkcast(descImplType) + } + + override fun ExpressionCodegen.addElementsContentToDescriptor(descriptorVar: Int) = with(v) { + val enumEntries = serializableDescriptor.enumEntries() + for (entry in enumEntries) { + load(descriptorVar, descImplType) + // regular .serialName() produces fqName here, which is kinda inconvenient for enum entry + val serialName = entry.annotations.serialNameValue ?: entry.name.toString() + aconst(serialName) + invokevirtual(descImplType.internalName, CallingConventions.addElement, "(Ljava/lang/String;)V", false) + // pushing annotations + addSyntheticAnnotationsToDescriptor(descriptorVar, entry, CallingConventions.addAnnotation) + } + } +} \ No newline at end of file diff --git a/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/diagnostic/SerializationPluginDeclarationChecker.kt b/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/diagnostic/SerializationPluginDeclarationChecker.kt index eb39475f943..30c43ba74be 100644 --- a/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/diagnostic/SerializationPluginDeclarationChecker.kt +++ b/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/diagnostic/SerializationPluginDeclarationChecker.kt @@ -58,16 +58,18 @@ open class SerializationPluginDeclarationChecker : DeclarationChecker { } if (!descriptor.hasSerializableAnnotationWithoutArgs) return false - if (!descriptor.isInternalSerializable && !descriptor.hasCompanionObjectAsSerializer) { + if (descriptor.serializableAnnotationIsUseless) { trace.reportOnSerializableAnnotation(descriptor, SerializationErrors.SERIALIZABLE_ANNOTATION_IGNORED) return false } // check that we can instantiate supertype - val superClass = descriptor.getSuperClassOrAny() - if (!superClass.isInternalSerializable && superClass.constructors.singleOrNull { it.valueParameters.size == 0 } == null) { - trace.reportOnSerializableAnnotation(descriptor, SerializationErrors.NON_SERIALIZABLE_PARENT_MUST_HAVE_NOARG_CTOR) - return false + if (!descriptor.isSerializableEnum()) { // enums are inherited from java.lang.Enum and can't be inherited from other classes + val superClass = descriptor.getSuperClassOrAny() + if (!superClass.isInternalSerializable && superClass.constructors.singleOrNull { it.valueParameters.size == 0 } == null) { + trace.reportOnSerializableAnnotation(descriptor, SerializationErrors.NON_SERIALIZABLE_PARENT_MUST_HAVE_NOARG_CTOR) + return false + } } return true } @@ -232,3 +234,6 @@ open class SerializationPluginDeclarationChecker : DeclarationChecker { } } } + +internal val ClassDescriptor.serializableAnnotationIsUseless: Boolean + get() = hasSerializableAnnotationWithoutArgs && !isInternalSerializable && !hasCompanionObjectAsSerializer && !isSerializableEnum() \ No newline at end of file diff --git a/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/extensions/SerializationResolveExtension.kt b/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/extensions/SerializationResolveExtension.kt index 02f162b7d09..f627fa879e3 100644 --- a/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/extensions/SerializationResolveExtension.kt +++ b/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/extensions/SerializationResolveExtension.kt @@ -18,7 +18,6 @@ package org.jetbrains.kotlinx.serialization.compiler.extensions import org.jetbrains.kotlin.descriptors.ClassConstructorDescriptor import org.jetbrains.kotlin.descriptors.ClassDescriptor -import org.jetbrains.kotlin.descriptors.ClassKind import org.jetbrains.kotlin.descriptors.PropertyDescriptor import org.jetbrains.kotlin.descriptors.SimpleFunctionDescriptor import org.jetbrains.kotlin.name.Name @@ -38,7 +37,7 @@ import java.util.* open class SerializationResolveExtension : SyntheticResolveExtension { override fun getSyntheticNestedClassNames(thisDescriptor: ClassDescriptor): List = when { thisDescriptor.annotations.hasAnnotation(serialInfoFqName) && thisDescriptor.platform?.isJvm() == true -> listOf(SerialEntityNames.IMPL_NAME) - thisDescriptor.isInternalSerializable && !thisDescriptor.hasCompanionObjectAsSerializer -> + (thisDescriptor.shouldHaveGeneratedSerializer) && !thisDescriptor.hasCompanionObjectAsSerializer -> listOf(SerialEntityNames.SERIALIZER_CLASS_NAME) else -> listOf() } @@ -58,7 +57,7 @@ open class SerializationResolveExtension : SyntheticResolveExtension { ) { if (thisDescriptor.annotations.hasAnnotation(serialInfoFqName) && name == SerialEntityNames.IMPL_NAME) result.add(KSerializerDescriptorResolver.addSerialInfoImplClass(thisDescriptor, declarationProvider, ctx)) - else if (thisDescriptor.isInternalSerializable && name == SerialEntityNames.SERIALIZER_CLASS_NAME && + else if (thisDescriptor.shouldHaveGeneratedSerializer && name == SerialEntityNames.SERIALIZER_CLASS_NAME && result.none { it.name == SerialEntityNames.SERIALIZER_CLASS_NAME } ) result.add(KSerializerDescriptorResolver.addSerializerImplClass(thisDescriptor, declarationProvider, ctx)) @@ -66,7 +65,7 @@ open class SerializationResolveExtension : SyntheticResolveExtension { } override fun getSyntheticCompanionObjectNameIfNeeded(thisDescriptor: ClassDescriptor): Name? = - if (thisDescriptor.kind == ClassKind.CLASS && thisDescriptor.annotations.hasAnnotation(SerializationAnnotations.serializableAnnotationFqName)) + if (thisDescriptor.shouldHaveGeneratedMethodsInCompanion && !thisDescriptor.isSerializableObject) SpecialNames.DEFAULT_NAME_FOR_COMPANION_OBJECT else null diff --git a/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/resolve/KSerializationUtil.kt b/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/resolve/KSerializationUtil.kt index af333f4722c..d10ede5cf97 100644 --- a/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/resolve/KSerializationUtil.kt +++ b/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/resolve/KSerializationUtil.kt @@ -23,6 +23,7 @@ import org.jetbrains.kotlin.descriptors.annotations.Annotations import org.jetbrains.kotlin.name.ClassId import org.jetbrains.kotlin.name.FqName import org.jetbrains.kotlin.psi.KtAnnotationEntry +import org.jetbrains.kotlin.resolve.descriptorUtil.annotationClass import org.jetbrains.kotlin.resolve.descriptorUtil.classId import org.jetbrains.kotlin.resolve.descriptorUtil.module import org.jetbrains.kotlin.resolve.lazy.descriptors.LazyAnnotationDescriptor @@ -37,6 +38,7 @@ internal fun isAllowedToHaveAutoGeneratedSerializerMethods( classDescriptor: ClassDescriptor, serializableClassDescriptor: ClassDescriptor ): Boolean { + if (serializableClassDescriptor.isSerializableEnum()) return true // don't generate automatically anything for enums or interfaces or other strange things if (serializableClassDescriptor.kind != ClassKind.CLASS) return false // it is either GeneratedSerializer implementation @@ -105,7 +107,7 @@ val KotlinType?.toClassDescriptor: ClassDescriptor? } internal val ClassDescriptor.shouldHaveGeneratedMethodsInCompanion: Boolean - get() = this.isSerializableObject || this.kind == ClassKind.CLASS && annotations.hasAnnotation(SerializationAnnotations.serializableAnnotationFqName) + get() = this.isSerializableObject || this.isSerializableEnum() || this.kind == ClassKind.CLASS && annotations.hasAnnotation(SerializationAnnotations.serializableAnnotationFqName) internal val ClassDescriptor.isSerializableObject: Boolean get() = kind == ClassKind.OBJECT && hasSerializableAnnotationWithoutArgs @@ -116,6 +118,30 @@ internal val ClassDescriptor.isInternalSerializable: Boolean //todo normal check return hasSerializableAnnotationWithoutArgs } +internal fun ClassDescriptor.isSerializableEnum(): Boolean { + if (this.kind != ClassKind.ENUM_CLASS) return false + if (hasSerializableAnnotationWithoutArgs) return true + if (annotations.hasAnySerialAnnotation) return true +// check entries + for (enumEntry in enumEntries()) { + if (enumEntry.annotations.hasAnySerialAnnotation) return true + } + return false +} + +internal val ClassDescriptor.shouldHaveGeneratedSerializer: Boolean get() = isInternalSerializable || isSerializableEnum() + +internal fun ClassDescriptor.enumEntries(): List { + check(this.kind == ClassKind.ENUM_CLASS) + return unsubstitutedMemberScope.getContributedDescriptors().asSequence() + .filterIsInstance() + .filter { it.kind == ClassKind.ENUM_ENTRY } + .toList() +} + +private val Annotations.hasAnySerialAnnotation: Boolean + get() = serialNameValue != null || any { it.annotationClass?.isSerialInfoAnnotation == true } + internal val ClassDescriptor.hasSerializableAnnotationWithoutArgs: Boolean get() { if (!annotations.hasAnnotation(SerializationAnnotations.serializableAnnotationFqName)) return false @@ -152,7 +178,7 @@ internal val ClassDescriptor?.classSerializer: ClassDescriptor? // can infer @Poly? polymorphicSerializerIfApplicableAutomatically()?.let { return it } // default serializable? - if (isInternalSerializable) { + if (shouldHaveGeneratedSerializer) { // $serializer nested class return this.unsubstitutedMemberScope .getDescriptorsFiltered(nameFilter = { it == SerialEntityNames.SERIALIZER_CLASS_NAME }) @@ -194,7 +220,7 @@ internal fun getSerializableClassDescriptorBySerializer(serializerDescriptor: Cl ) ) return null val classDescriptor = (serializerDescriptor.containingDeclaration as? ClassDescriptor) ?: return null - if (!classDescriptor.isInternalSerializable) return null + if (!classDescriptor.shouldHaveGeneratedSerializer) return null return classDescriptor } diff --git a/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/resolve/NamingConventions.kt b/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/resolve/NamingConventions.kt index 92f23b30643..7acb1fe4c54 100644 --- a/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/resolve/NamingConventions.kt +++ b/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/resolve/NamingConventions.kt @@ -55,6 +55,7 @@ object SerialEntityNames { const val SERIAL_DESCRIPTOR_CLASS = "SerialDescriptor" const val SERIAL_DESCRIPTOR_CLASS_IMPL = "SerialClassDescImpl" + const val SERIAL_DESCRIPTOR_FOR_ENUM = "EnumDescriptor" //exceptions const val SERIAL_EXC = "SerializationException"