Make enums and interfaces not serializable internally. However, they
still can be serialized using custom companion object. Fixes https://github.com/Kotlin/kotlinx.serialization/issues/138 and https://github.com/Kotlin/kotlinx.serialization/issues/304
This commit is contained in:
+13
-1
@@ -3,6 +3,7 @@ package org.jetbrains.kotlinx.serialization.compiler.backend.ir
|
||||
import org.jetbrains.kotlin.backend.common.BackendContext
|
||||
import org.jetbrains.kotlin.backend.common.lower.irThrow
|
||||
import org.jetbrains.kotlin.builtins.KotlinBuiltIns
|
||||
import org.jetbrains.kotlin.codegen.CompilationException
|
||||
import org.jetbrains.kotlin.descriptors.ClassConstructorDescriptor
|
||||
import org.jetbrains.kotlin.descriptors.FunctionDescriptor
|
||||
import org.jetbrains.kotlin.ir.builders.*
|
||||
@@ -12,6 +13,7 @@ import org.jetbrains.kotlin.ir.declarations.IrProperty
|
||||
import org.jetbrains.kotlin.ir.expressions.IrExpression
|
||||
import org.jetbrains.kotlin.ir.util.SymbolTable
|
||||
import org.jetbrains.kotlin.ir.util.TypeTranslator
|
||||
import org.jetbrains.kotlin.js.resolve.diagnostics.findPsi
|
||||
import org.jetbrains.kotlin.resolve.BindingContext
|
||||
import org.jetbrains.kotlin.resolve.descriptorUtil.getSuperClassOrAny
|
||||
import org.jetbrains.kotlin.resolve.descriptorUtil.module
|
||||
@@ -19,6 +21,8 @@ import org.jetbrains.kotlin.util.OperatorNameConventions
|
||||
import org.jetbrains.kotlinx.serialization.compiler.backend.common.SerializableCodegen
|
||||
import org.jetbrains.kotlinx.serialization.compiler.resolve.SerialEntityNames.MISSING_FIELD_EXC
|
||||
import org.jetbrains.kotlinx.serialization.compiler.resolve.getClassFromSerializationPackage
|
||||
import org.jetbrains.kotlinx.serialization.compiler.resolve.hasCompanionObjectAsSerializer
|
||||
import org.jetbrains.kotlinx.serialization.compiler.resolve.hasSerializableAnnotationWithoutArgs
|
||||
import org.jetbrains.kotlinx.serialization.compiler.resolve.isInternalSerializable
|
||||
|
||||
class SerializableIrGenerator(
|
||||
@@ -98,8 +102,16 @@ class SerializableIrGenerator(
|
||||
context: BackendContext,
|
||||
bindingContext: BindingContext
|
||||
) {
|
||||
if (irClass.descriptor.isInternalSerializable)
|
||||
val serializableClass = irClass.descriptor
|
||||
|
||||
if (serializableClass.isInternalSerializable)
|
||||
SerializableIrGenerator(irClass, context, bindingContext).generate()
|
||||
else if (serializableClass.hasSerializableAnnotationWithoutArgs && !serializableClass.hasCompanionObjectAsSerializer) {
|
||||
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()
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+13
-5
@@ -16,11 +16,13 @@
|
||||
|
||||
package org.jetbrains.kotlinx.serialization.compiler.backend.js
|
||||
|
||||
import org.jetbrains.kotlin.codegen.CompilationException
|
||||
import org.jetbrains.kotlin.descriptors.ClassConstructorDescriptor
|
||||
import org.jetbrains.kotlin.descriptors.ClassDescriptor
|
||||
import org.jetbrains.kotlin.descriptors.FunctionDescriptor
|
||||
import org.jetbrains.kotlin.descriptors.PropertyDescriptor
|
||||
import org.jetbrains.kotlin.js.backend.ast.*
|
||||
import org.jetbrains.kotlin.js.resolve.diagnostics.findPsi
|
||||
import org.jetbrains.kotlin.js.translate.context.Namer
|
||||
import org.jetbrains.kotlin.js.translate.context.TranslationContext
|
||||
import org.jetbrains.kotlin.js.translate.declaration.DeclarationBodyVisitor
|
||||
@@ -30,10 +32,10 @@ import org.jetbrains.kotlin.psi.KtExpression
|
||||
import org.jetbrains.kotlin.psi.KtPureClassOrObject
|
||||
import org.jetbrains.kotlinx.serialization.compiler.backend.common.SerializableCodegen
|
||||
import org.jetbrains.kotlinx.serialization.compiler.backend.common.anonymousInitializers
|
||||
import org.jetbrains.kotlinx.serialization.compiler.backend.common.bodyPropertiesDescriptorsMap
|
||||
import org.jetbrains.kotlinx.serialization.compiler.backend.common.primaryPropertiesDescriptorsMap
|
||||
import org.jetbrains.kotlinx.serialization.compiler.resolve.SerialEntityNames.MISSING_FIELD_EXC
|
||||
import org.jetbrains.kotlinx.serialization.compiler.resolve.getClassFromSerializationPackage
|
||||
import org.jetbrains.kotlinx.serialization.compiler.resolve.hasCompanionObjectAsSerializer
|
||||
import org.jetbrains.kotlinx.serialization.compiler.resolve.hasSerializableAnnotationWithoutArgs
|
||||
import org.jetbrains.kotlinx.serialization.compiler.resolve.isInternalSerializable
|
||||
|
||||
class SerializableJsTranslator(
|
||||
@@ -104,12 +106,18 @@ class SerializableJsTranslator(
|
||||
companion object {
|
||||
fun translate(
|
||||
declaration: KtPureClassOrObject,
|
||||
descriptor: ClassDescriptor,
|
||||
serializableClass: ClassDescriptor,
|
||||
translator: DeclarationBodyVisitor,
|
||||
context: TranslationContext
|
||||
) {
|
||||
if (descriptor.isInternalSerializable)
|
||||
SerializableJsTranslator(declaration, descriptor, translator, context).generate()
|
||||
if (serializableClass.isInternalSerializable)
|
||||
SerializableJsTranslator(declaration, serializableClass, translator, context).generate()
|
||||
else if (serializableClass.hasSerializableAnnotationWithoutArgs && !serializableClass.hasCompanionObjectAsSerializer) {
|
||||
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()
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+7
@@ -21,6 +21,7 @@ import org.jetbrains.kotlin.descriptors.ClassConstructorDescriptor
|
||||
import org.jetbrains.kotlin.descriptors.ClassDescriptor
|
||||
import org.jetbrains.kotlin.descriptors.FunctionDescriptor
|
||||
import org.jetbrains.kotlin.descriptors.PropertyDescriptor
|
||||
import org.jetbrains.kotlin.js.resolve.diagnostics.findPsi
|
||||
import org.jetbrains.kotlin.lexer.KtTokens
|
||||
import org.jetbrains.kotlin.psi.KtExpression
|
||||
import org.jetbrains.kotlin.psi.KtParameter
|
||||
@@ -46,6 +47,12 @@ class SerializableCodegenImpl(
|
||||
val serializableClass = codegen.descriptor
|
||||
if (serializableClass.isInternalSerializable)
|
||||
SerializableCodegenImpl(codegen, serializableClass).generate()
|
||||
else if (serializableClass.hasSerializableAnnotationWithoutArgs && !serializableClass.hasCompanionObjectAsSerializer) {
|
||||
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()
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+11
-3
@@ -35,7 +35,9 @@ import org.jetbrains.kotlinx.serialization.compiler.resolve.SerialEntityNames.KS
|
||||
import org.jetbrains.kotlinx.serialization.compiler.resolve.SerializationAnnotations.serialInfoFqName
|
||||
import org.jetbrains.kotlinx.serialization.compiler.resolve.SerializationPackages.packageFqName
|
||||
|
||||
fun isAllowedToHaveAutoGeneratedSerializerMethods(classDescriptor: ClassDescriptor): Boolean {
|
||||
fun isAllowedToHaveAutoGeneratedSerializerMethods(classDescriptor: ClassDescriptor, serializableClassDescriptor: ClassDescriptor): Boolean {
|
||||
// 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
|
||||
// or user implementation which does not have type parameters (to be able to correctly initialize descriptor)
|
||||
return classDescriptor.typeConstructor.supertypes.any(::isGeneratedKSerializer) ||
|
||||
@@ -97,6 +99,12 @@ val KotlinType?.toClassDescriptor: ClassDescriptor?
|
||||
|
||||
|
||||
val ClassDescriptor.isInternalSerializable: Boolean //todo normal checking
|
||||
get() {
|
||||
if (kind != ClassKind.CLASS) return false
|
||||
return hasSerializableAnnotationWithoutArgs
|
||||
}
|
||||
|
||||
internal val ClassDescriptor.hasSerializableAnnotationWithoutArgs: Boolean
|
||||
get() {
|
||||
if (!annotations.hasAnnotation(SerializationAnnotations.serializableAnnotationFqName)) return false
|
||||
// If provided descriptor is lazy, carefully look at psi in order not to trigger full resolve which may be recursive.
|
||||
@@ -112,10 +120,10 @@ internal val ClassDescriptor?.classSerializer: KotlinType?
|
||||
get() = this?.let {
|
||||
// serializer annotation on class?
|
||||
serializableWith?.let { return it }
|
||||
// companion object serializer?
|
||||
if (hasCompanionObjectAsSerializer) return companionObjectDescriptor?.defaultType
|
||||
// default serializable?
|
||||
if (isInternalSerializable) {
|
||||
// companion object serializer?
|
||||
if (hasCompanionObjectAsSerializer) return companionObjectDescriptor?.defaultType
|
||||
// $serializer nested class
|
||||
return this.unsubstitutedMemberScope
|
||||
.getDescriptorsFiltered(nameFilter = {it == SerialEntityNames.SERIALIZER_CLASS_NAME})
|
||||
|
||||
+7
-6
@@ -136,10 +136,11 @@ object KSerializerDescriptorResolver {
|
||||
name: Name,
|
||||
result: MutableSet<PropertyDescriptor>
|
||||
) {
|
||||
// Do not auto-generate anything for user serializers
|
||||
if (!isAllowedToHaveAutoGeneratedSerializerMethods(thisDescriptor)) return
|
||||
|
||||
val classDescriptor = getSerializableClassDescriptorBySerializer(thisDescriptor) ?: return
|
||||
|
||||
// Do not auto-generate anything for user serializers
|
||||
if (!isAllowedToHaveAutoGeneratedSerializerMethods(thisDescriptor, classDescriptor)) return
|
||||
|
||||
if (name == SerialEntityNames.SERIAL_DESC_FIELD_NAME && result.none(thisDescriptor::checkSerializableClassPropertyResult) &&
|
||||
fromSupertypes.none { thisDescriptor.checkSerializableClassPropertyResult(it) && it.modality == Modality.FINAL }
|
||||
) {
|
||||
@@ -171,11 +172,11 @@ object KSerializerDescriptorResolver {
|
||||
name: Name,
|
||||
result: MutableCollection<SimpleFunctionDescriptor>
|
||||
) {
|
||||
// Do not auto-generate anything for user serializers
|
||||
if (!isAllowedToHaveAutoGeneratedSerializerMethods(thisDescriptor)) return
|
||||
|
||||
val classDescriptor = getSerializableClassDescriptorBySerializer(thisDescriptor) ?: return
|
||||
|
||||
// Do not auto-generate anything for user serializers
|
||||
if (!isAllowedToHaveAutoGeneratedSerializerMethods(thisDescriptor, classDescriptor)) return
|
||||
|
||||
fun shouldAddSerializerFunction(checkParameters: (FunctionDescriptor) -> Boolean): Boolean {
|
||||
// Add 'save' / 'load' iff there is no such declared member AND there is no such final member in supertypes
|
||||
return result.none(checkParameters) &&
|
||||
|
||||
Reference in New Issue
Block a user