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.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()
)
}
}
}
}
@@ -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()
)
}
}
}
}
@@ -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()
)
}
}
}
@@ -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})
@@ -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) &&