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:
Leonid Startsev
2018-12-24 17:01:25 +03:00
parent c41d6c427d
commit cca03e9586
5 changed files with 51 additions and 15 deletions
@@ -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()
)
}
} }
} }
} }
@@ -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()
)
}
} }
} }
} }
@@ -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()
)
}
} }
} }
@@ -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})
@@ -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) &&