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.BackendContext
|
||||||
import org.jetbrains.kotlin.backend.common.lower.irThrow
|
import org.jetbrains.kotlin.backend.common.lower.irThrow
|
||||||
import org.jetbrains.kotlin.builtins.KotlinBuiltIns
|
import org.jetbrains.kotlin.builtins.KotlinBuiltIns
|
||||||
|
import org.jetbrains.kotlin.codegen.CompilationException
|
||||||
import org.jetbrains.kotlin.descriptors.ClassConstructorDescriptor
|
import org.jetbrains.kotlin.descriptors.ClassConstructorDescriptor
|
||||||
import org.jetbrains.kotlin.descriptors.FunctionDescriptor
|
import org.jetbrains.kotlin.descriptors.FunctionDescriptor
|
||||||
import org.jetbrains.kotlin.ir.builders.*
|
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.expressions.IrExpression
|
||||||
import org.jetbrains.kotlin.ir.util.SymbolTable
|
import org.jetbrains.kotlin.ir.util.SymbolTable
|
||||||
import org.jetbrains.kotlin.ir.util.TypeTranslator
|
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.BindingContext
|
||||||
import org.jetbrains.kotlin.resolve.descriptorUtil.getSuperClassOrAny
|
import org.jetbrains.kotlin.resolve.descriptorUtil.getSuperClassOrAny
|
||||||
import org.jetbrains.kotlin.resolve.descriptorUtil.module
|
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.backend.common.SerializableCodegen
|
||||||
import org.jetbrains.kotlinx.serialization.compiler.resolve.SerialEntityNames.MISSING_FIELD_EXC
|
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.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
|
import org.jetbrains.kotlinx.serialization.compiler.resolve.isInternalSerializable
|
||||||
|
|
||||||
class SerializableIrGenerator(
|
class SerializableIrGenerator(
|
||||||
@@ -98,8 +102,16 @@ class SerializableIrGenerator(
|
|||||||
context: BackendContext,
|
context: BackendContext,
|
||||||
bindingContext: BindingContext
|
bindingContext: BindingContext
|
||||||
) {
|
) {
|
||||||
if (irClass.descriptor.isInternalSerializable)
|
val serializableClass = irClass.descriptor
|
||||||
|
|
||||||
|
if (serializableClass.isInternalSerializable)
|
||||||
SerializableIrGenerator(irClass, context, bindingContext).generate()
|
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
|
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.ClassConstructorDescriptor
|
||||||
import org.jetbrains.kotlin.descriptors.ClassDescriptor
|
import org.jetbrains.kotlin.descriptors.ClassDescriptor
|
||||||
import org.jetbrains.kotlin.descriptors.FunctionDescriptor
|
import org.jetbrains.kotlin.descriptors.FunctionDescriptor
|
||||||
import org.jetbrains.kotlin.descriptors.PropertyDescriptor
|
import org.jetbrains.kotlin.descriptors.PropertyDescriptor
|
||||||
import org.jetbrains.kotlin.js.backend.ast.*
|
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.Namer
|
||||||
import org.jetbrains.kotlin.js.translate.context.TranslationContext
|
import org.jetbrains.kotlin.js.translate.context.TranslationContext
|
||||||
import org.jetbrains.kotlin.js.translate.declaration.DeclarationBodyVisitor
|
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.kotlin.psi.KtPureClassOrObject
|
||||||
import org.jetbrains.kotlinx.serialization.compiler.backend.common.SerializableCodegen
|
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.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.SerialEntityNames.MISSING_FIELD_EXC
|
||||||
import org.jetbrains.kotlinx.serialization.compiler.resolve.getClassFromSerializationPackage
|
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
|
import org.jetbrains.kotlinx.serialization.compiler.resolve.isInternalSerializable
|
||||||
|
|
||||||
class SerializableJsTranslator(
|
class SerializableJsTranslator(
|
||||||
@@ -104,12 +106,18 @@ class SerializableJsTranslator(
|
|||||||
companion object {
|
companion object {
|
||||||
fun translate(
|
fun translate(
|
||||||
declaration: KtPureClassOrObject,
|
declaration: KtPureClassOrObject,
|
||||||
descriptor: ClassDescriptor,
|
serializableClass: ClassDescriptor,
|
||||||
translator: DeclarationBodyVisitor,
|
translator: DeclarationBodyVisitor,
|
||||||
context: TranslationContext
|
context: TranslationContext
|
||||||
) {
|
) {
|
||||||
if (descriptor.isInternalSerializable)
|
if (serializableClass.isInternalSerializable)
|
||||||
SerializableJsTranslator(declaration, descriptor, translator, context).generate()
|
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.ClassDescriptor
|
||||||
import org.jetbrains.kotlin.descriptors.FunctionDescriptor
|
import org.jetbrains.kotlin.descriptors.FunctionDescriptor
|
||||||
import org.jetbrains.kotlin.descriptors.PropertyDescriptor
|
import org.jetbrains.kotlin.descriptors.PropertyDescriptor
|
||||||
|
import org.jetbrains.kotlin.js.resolve.diagnostics.findPsi
|
||||||
import org.jetbrains.kotlin.lexer.KtTokens
|
import org.jetbrains.kotlin.lexer.KtTokens
|
||||||
import org.jetbrains.kotlin.psi.KtExpression
|
import org.jetbrains.kotlin.psi.KtExpression
|
||||||
import org.jetbrains.kotlin.psi.KtParameter
|
import org.jetbrains.kotlin.psi.KtParameter
|
||||||
@@ -46,6 +47,12 @@ class SerializableCodegenImpl(
|
|||||||
val serializableClass = codegen.descriptor
|
val serializableClass = codegen.descriptor
|
||||||
if (serializableClass.isInternalSerializable)
|
if (serializableClass.isInternalSerializable)
|
||||||
SerializableCodegenImpl(codegen, serializableClass).generate()
|
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.SerializationAnnotations.serialInfoFqName
|
||||||
import org.jetbrains.kotlinx.serialization.compiler.resolve.SerializationPackages.packageFqName
|
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
|
// it is either GeneratedSerializer implementation
|
||||||
// or user implementation which does not have type parameters (to be able to correctly initialize descriptor)
|
// or user implementation which does not have type parameters (to be able to correctly initialize descriptor)
|
||||||
return classDescriptor.typeConstructor.supertypes.any(::isGeneratedKSerializer) ||
|
return classDescriptor.typeConstructor.supertypes.any(::isGeneratedKSerializer) ||
|
||||||
@@ -97,6 +99,12 @@ val KotlinType?.toClassDescriptor: ClassDescriptor?
|
|||||||
|
|
||||||
|
|
||||||
val ClassDescriptor.isInternalSerializable: Boolean //todo normal checking
|
val ClassDescriptor.isInternalSerializable: Boolean //todo normal checking
|
||||||
|
get() {
|
||||||
|
if (kind != ClassKind.CLASS) return false
|
||||||
|
return hasSerializableAnnotationWithoutArgs
|
||||||
|
}
|
||||||
|
|
||||||
|
internal val ClassDescriptor.hasSerializableAnnotationWithoutArgs: Boolean
|
||||||
get() {
|
get() {
|
||||||
if (!annotations.hasAnnotation(SerializationAnnotations.serializableAnnotationFqName)) return false
|
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.
|
// 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 {
|
get() = this?.let {
|
||||||
// serializer annotation on class?
|
// serializer annotation on class?
|
||||||
serializableWith?.let { return it }
|
serializableWith?.let { return it }
|
||||||
|
// companion object serializer?
|
||||||
|
if (hasCompanionObjectAsSerializer) return companionObjectDescriptor?.defaultType
|
||||||
// default serializable?
|
// default serializable?
|
||||||
if (isInternalSerializable) {
|
if (isInternalSerializable) {
|
||||||
// companion object serializer?
|
|
||||||
if (hasCompanionObjectAsSerializer) return companionObjectDescriptor?.defaultType
|
|
||||||
// $serializer nested class
|
// $serializer nested class
|
||||||
return this.unsubstitutedMemberScope
|
return this.unsubstitutedMemberScope
|
||||||
.getDescriptorsFiltered(nameFilter = {it == SerialEntityNames.SERIALIZER_CLASS_NAME})
|
.getDescriptorsFiltered(nameFilter = {it == SerialEntityNames.SERIALIZER_CLASS_NAME})
|
||||||
|
|||||||
+7
-6
@@ -136,10 +136,11 @@ object KSerializerDescriptorResolver {
|
|||||||
name: Name,
|
name: Name,
|
||||||
result: MutableSet<PropertyDescriptor>
|
result: MutableSet<PropertyDescriptor>
|
||||||
) {
|
) {
|
||||||
// Do not auto-generate anything for user serializers
|
|
||||||
if (!isAllowedToHaveAutoGeneratedSerializerMethods(thisDescriptor)) return
|
|
||||||
|
|
||||||
val classDescriptor = getSerializableClassDescriptorBySerializer(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) &&
|
if (name == SerialEntityNames.SERIAL_DESC_FIELD_NAME && result.none(thisDescriptor::checkSerializableClassPropertyResult) &&
|
||||||
fromSupertypes.none { thisDescriptor.checkSerializableClassPropertyResult(it) && it.modality == Modality.FINAL }
|
fromSupertypes.none { thisDescriptor.checkSerializableClassPropertyResult(it) && it.modality == Modality.FINAL }
|
||||||
) {
|
) {
|
||||||
@@ -171,11 +172,11 @@ object KSerializerDescriptorResolver {
|
|||||||
name: Name,
|
name: Name,
|
||||||
result: MutableCollection<SimpleFunctionDescriptor>
|
result: MutableCollection<SimpleFunctionDescriptor>
|
||||||
) {
|
) {
|
||||||
// Do not auto-generate anything for user serializers
|
|
||||||
if (!isAllowedToHaveAutoGeneratedSerializerMethods(thisDescriptor)) return
|
|
||||||
|
|
||||||
val classDescriptor = getSerializableClassDescriptorBySerializer(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 {
|
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
|
// Add 'save' / 'load' iff there is no such declared member AND there is no such final member in supertypes
|
||||||
return result.none(checkParameters) &&
|
return result.none(checkParameters) &&
|
||||||
|
|||||||
Reference in New Issue
Block a user