Custom-serializable enums for JVM

This commit is contained in:
Leonid Startsev
2019-06-18 20:00:16 +03:00
parent e9e7d1479a
commit e76ab06029
12 changed files with 165 additions and 49 deletions
@@ -21,7 +21,6 @@ import org.jetbrains.kotlin.descriptors.*
import org.jetbrains.kotlin.incremental.components.NoLookupLocation import org.jetbrains.kotlin.incremental.components.NoLookupLocation
import org.jetbrains.kotlin.name.Name import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.resolve.BindingContext import org.jetbrains.kotlin.resolve.BindingContext
import org.jetbrains.kotlin.resolve.descriptorUtil.fqNameUnsafe
import org.jetbrains.kotlin.resolve.scopes.getDescriptorsFiltered import org.jetbrains.kotlin.resolve.scopes.getDescriptorsFiltered
import org.jetbrains.kotlinx.serialization.compiler.resolve.* import org.jetbrains.kotlinx.serialization.compiler.resolve.*
@@ -30,7 +29,7 @@ abstract class SerializerCodegen(
bindingContext: BindingContext bindingContext: BindingContext
) : AbstractSerialGenerator(bindingContext, serializerDescriptor) { ) : AbstractSerialGenerator(bindingContext, serializerDescriptor) {
val serializableDescriptor: ClassDescriptor = getSerializableClassDescriptorBySerializer(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 properties = bindingContext.serializablePropertiesFor(serializableDescriptor)
protected val serializableProperties = properties.serializableProperties protected val serializableProperties = properties.serializableProperties
@@ -96,7 +96,11 @@ fun AbstractSerialGenerator.getSerialTypeInfo(property: SerializableProperty): S
fun KotlinType.serialName(): String { fun KotlinType.serialName(): String {
val serializableDescriptor = this.toClassDescriptor!! 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? { fun findEnumTypeSerializer(module: ModuleDescriptor, kType: KotlinType): ClassDescriptor? {
val classDescriptor = kType.toClassDescriptor ?: return null 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( internal fun KtPureClassOrObject.bodyPropertiesDescriptorsMap(
@@ -27,6 +27,7 @@ import org.jetbrains.kotlin.resolve.descriptorUtil.getSuperClassOrAny
import org.jetbrains.kotlin.resolve.descriptorUtil.module import org.jetbrains.kotlin.resolve.descriptorUtil.module
import org.jetbrains.kotlin.util.OperatorNameConventions 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.diagnostic.serializableAnnotationIsUseless
import org.jetbrains.kotlinx.serialization.compiler.resolve.* import org.jetbrains.kotlinx.serialization.compiler.resolve.*
import org.jetbrains.kotlinx.serialization.compiler.resolve.SerialEntityNames.MISSING_FIELD_EXC import org.jetbrains.kotlinx.serialization.compiler.resolve.SerialEntityNames.MISSING_FIELD_EXC
@@ -176,7 +177,7 @@ class SerializableIrGenerator(
SerializableIrGenerator(irClass, context, bindingContext).generate() SerializableIrGenerator(irClass, context, bindingContext).generate()
irClass.patchDeclarationParents(irClass.parent) irClass.patchDeclarationParents(irClass.parent)
} }
else if (serializableClass.hasSerializableAnnotationWithoutArgs && !serializableClass.hasCompanionObjectAsSerializer) { else if (serializableClass.serializableAnnotationIsUseless) {
throw CompilationException( throw CompilationException(
"@Serializable annotation on $serializableClass would be ignored because it is impossible to serialize it automatically. " + "@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() "Provide serializer manually via e.g. companion object", null, serializableClass.findPsi()
@@ -36,6 +36,7 @@ import org.jetbrains.kotlin.resolve.descriptorUtil.getSuperClassNotAny
import org.jetbrains.kotlin.resolve.descriptorUtil.getSuperClassOrAny import org.jetbrains.kotlin.resolve.descriptorUtil.getSuperClassOrAny
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.diagnostic.serializableAnnotationIsUseless
import org.jetbrains.kotlinx.serialization.compiler.resolve.* import org.jetbrains.kotlinx.serialization.compiler.resolve.*
import org.jetbrains.kotlinx.serialization.compiler.resolve.SerialEntityNames.MISSING_FIELD_EXC import org.jetbrains.kotlinx.serialization.compiler.resolve.SerialEntityNames.MISSING_FIELD_EXC
@@ -164,7 +165,7 @@ class SerializableJsTranslator(
) { ) {
if (serializableClass.isInternalSerializable) if (serializableClass.isInternalSerializable)
SerializableJsTranslator(declaration, serializableClass, context).generate() SerializableJsTranslator(declaration, serializableClass, context).generate()
else if (serializableClass.hasSerializableAnnotationWithoutArgs && !serializableClass.hasCompanionObjectAsSerializer) { else if (serializableClass.serializableAnnotationIsUseless) {
throw CompilationException( throw CompilationException(
"@Serializable annotation on $serializableClass would be ignored because it is impossible to serialize it automatically. " + "@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() "Provide serializer manually via e.g. companion object", null, serializableClass.findPsi()
@@ -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_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
import org.jetbrains.kotlinx.serialization.compiler.resolve.SerialEntityNames.SERIAL_DESCRIPTOR_CLASS_IMPL 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_EXC
import org.jetbrains.kotlinx.serialization.compiler.resolve.SerialEntityNames.SERIAL_LOADER_CLASS import org.jetbrains.kotlinx.serialization.compiler.resolve.SerialEntityNames.SERIAL_LOADER_CLASS
import org.jetbrains.kotlinx.serialization.compiler.resolve.SerialEntityNames.SERIAL_SAVER_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? // todo: extract packages constants too?
internal val descType = Type.getObjectType("kotlinx/serialization/$SERIAL_DESCRIPTOR_CLASS") internal val descType = Type.getObjectType("kotlinx/serialization/$SERIAL_DESCRIPTOR_CLASS")
internal val descImplType = Type.getObjectType("kotlinx/serialization/internal/$SERIAL_DESCRIPTOR_CLASS_IMPL") 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 generatedSerializerType = Type.getObjectType("kotlinx/serialization/internal/${SerialEntityNames.GENERATED_SERIALIZER_CLASS}")
internal val kOutputType = Type.getObjectType("kotlinx/serialization/$STRUCTURE_ENCODER_CLASS") internal val kOutputType = Type.getObjectType("kotlinx/serialization/$STRUCTURE_ENCODER_CLASS")
internal val encoderType = Type.getObjectType("kotlinx/serialization/$ENCODER_CLASS") internal val encoderType = Type.getObjectType("kotlinx/serialization/$ENCODER_CLASS")
@@ -29,6 +29,7 @@ import org.jetbrains.kotlin.resolve.DescriptorUtils
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
import org.jetbrains.kotlinx.serialization.compiler.backend.common.* 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.kotlinx.serialization.compiler.resolve.*
import org.jetbrains.org.objectweb.asm.Label import org.jetbrains.org.objectweb.asm.Label
import org.jetbrains.org.objectweb.asm.Type import org.jetbrains.org.objectweb.asm.Type
@@ -45,7 +46,7 @@ class SerializableCodegenImpl(
val serializableClass = codegen.descriptor val serializableClass = codegen.descriptor
if (serializableClass.isInternalSerializable) if (serializableClass.isInternalSerializable)
SerializableCodegenImpl(codegen).generate() SerializableCodegenImpl(codegen).generate()
else if (serializableClass.hasSerializableAnnotationWithoutArgs && !serializableClass.hasCompanionObjectAsSerializer) { else if (serializableClass.serializableAnnotationIsUseless) {
throw CompilationException( throw CompilationException(
"@Serializable annotation on $serializableClass would be ignored because it is impossible to serialize it automatically. " + "@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() "Provide serializer manually via e.g. companion object", null, serializableClass.findPsi()
@@ -18,6 +18,7 @@ package org.jetbrains.kotlinx.serialization.compiler.backend.jvm
import org.jetbrains.kotlin.codegen.* import org.jetbrains.kotlin.codegen.*
import org.jetbrains.kotlin.descriptors.* import org.jetbrains.kotlin.descriptors.*
import org.jetbrains.kotlin.descriptors.annotations.Annotated
import org.jetbrains.kotlin.psi.ValueArgument import org.jetbrains.kotlin.psi.ValueArgument
import org.jetbrains.kotlin.resolve.jvm.AsmTypes import org.jetbrains.kotlin.resolve.jvm.AsmTypes
import org.jetbrains.kotlin.resolve.jvm.diagnostics.OtherOrigin 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.Type
import org.jetbrains.org.objectweb.asm.commons.InstructionAdapter import org.jetbrains.org.objectweb.asm.commons.InstructionAdapter
class SerializerCodegenImpl( open class SerializerCodegenImpl(
private val codegen: ImplementationBodyCodegen, protected val codegen: ImplementationBodyCodegen,
serializableClass: ClassDescriptor serializableClass: ClassDescriptor
) : SerializerCodegen(codegen.descriptor, codegen.bindingContext) { ) : SerializerCodegen(codegen.descriptor, codegen.bindingContext) {
private val serialDescField = "\$\$serialDesc" private val serialDescField = "\$\$serialDesc"
private val serializerAsmType = codegen.typeMapper.mapClass(codegen.descriptor) protected val serializerAsmType = codegen.typeMapper.mapClass(codegen.descriptor)
private val serializableAsmType = codegen.typeMapper.mapClass(serializableClass) protected val serializableAsmType = codegen.typeMapper.mapClass(serializableClass)
// if we have type parameters, descriptor initializing must be performed in constructor // if we have type parameters, descriptor initializing must be performed in constructor
private val staticDescriptor = serializableDescriptor.declaredTypeParameters.isEmpty() private val staticDescriptor = serializableDescriptor.declaredTypeParameters.isEmpty()
@@ -46,7 +47,12 @@ class SerializerCodegenImpl(
companion object { companion object {
fun generateSerializerExtensions(codegen: ImplementationBodyCodegen) { fun generateSerializerExtensions(codegen: ImplementationBodyCodegen) {
val serializableClass = getSerializableClassDescriptorBySerializer(codegen.descriptor) ?: return 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) { 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) anew(descImplType)
dup() dup()
aconst(serialName) aconst(serialName)
@@ -88,7 +111,9 @@ class SerializerCodegenImpl(
load(0, serializerAsmType) load(0, serializerAsmType)
} }
invokespecial(descImplType.internalName, "<init>", "(Ljava/lang/String;${generatedSerializerType.descriptor})V", false) invokespecial(descImplType.internalName, "<init>", "(Ljava/lang/String;${generatedSerializerType.descriptor})V", false)
store(descriptorVar, descImplType) }
protected open fun ExpressionCodegen.addElementsContentToDescriptor(descriptorVar: Int) = with(v) {
for (property in serializableProperties) { for (property in serializableProperties) {
if (property.transient) continue if (property.transient) continue
load(descriptorVar, descImplType) load(descriptorVar, descImplType)
@@ -96,39 +121,24 @@ class SerializerCodegenImpl(
iconst(if (property.optional) 1 else 0) iconst(if (property.optional) 1 else 0)
invokevirtual(descImplType.internalName, CallingConventions.addElement, "(Ljava/lang/String;Z)V", false) invokevirtual(descImplType.internalName, CallingConventions.addElement, "(Ljava/lang/String;Z)V", false)
// pushing annotations // 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") if (args.size != consParams.size) throw IllegalArgumentException("Can't use arguments with defaults for serializable annotations yet")
load(descriptorVar, descImplType) load(descriptorVar, descImplType)
generateSyntheticAnnotationOnStack(annotationClass, args, consParams) generateSyntheticAnnotationOnStack(annotationClass, args, consParams)
invokevirtual( invokevirtual(
descImplType.internalName, descImplType.internalName,
CallingConventions.addAnnotation, functionToCall,
"(Ljava/lang/annotation/Annotation;)V", "(Ljava/lang/annotation/Annotation;)V",
false 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() { override fun generateSerialDesc() {
var flags = ACC_PRIVATE or ACC_FINAL or ACC_SYNTHETIC 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 // 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) if (staticDescriptor)
getstatic(serializerAsmType.internalName, serialDescField, descType.descriptor) getstatic(serializerAsmType.internalName, serialDescField, descType.descriptor)
else { else {
@@ -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, "<init>", "(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)
}
}
}
@@ -58,16 +58,18 @@ open class SerializationPluginDeclarationChecker : DeclarationChecker {
} }
if (!descriptor.hasSerializableAnnotationWithoutArgs) return false if (!descriptor.hasSerializableAnnotationWithoutArgs) return false
if (!descriptor.isInternalSerializable && !descriptor.hasCompanionObjectAsSerializer) { if (descriptor.serializableAnnotationIsUseless) {
trace.reportOnSerializableAnnotation(descriptor, SerializationErrors.SERIALIZABLE_ANNOTATION_IGNORED) trace.reportOnSerializableAnnotation(descriptor, SerializationErrors.SERIALIZABLE_ANNOTATION_IGNORED)
return false return false
} }
// check that we can instantiate supertype // check that we can instantiate supertype
val superClass = descriptor.getSuperClassOrAny() if (!descriptor.isSerializableEnum()) { // enums are inherited from java.lang.Enum and can't be inherited from other classes
if (!superClass.isInternalSerializable && superClass.constructors.singleOrNull { it.valueParameters.size == 0 } == null) { val superClass = descriptor.getSuperClassOrAny()
trace.reportOnSerializableAnnotation(descriptor, SerializationErrors.NON_SERIALIZABLE_PARENT_MUST_HAVE_NOARG_CTOR) if (!superClass.isInternalSerializable && superClass.constructors.singleOrNull { it.valueParameters.size == 0 } == null) {
return false trace.reportOnSerializableAnnotation(descriptor, SerializationErrors.NON_SERIALIZABLE_PARENT_MUST_HAVE_NOARG_CTOR)
return false
}
} }
return true return true
} }
@@ -232,3 +234,6 @@ open class SerializationPluginDeclarationChecker : DeclarationChecker {
} }
} }
} }
internal val ClassDescriptor.serializableAnnotationIsUseless: Boolean
get() = hasSerializableAnnotationWithoutArgs && !isInternalSerializable && !hasCompanionObjectAsSerializer && !isSerializableEnum()
@@ -18,7 +18,6 @@ package org.jetbrains.kotlinx.serialization.compiler.extensions
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.ClassKind
import org.jetbrains.kotlin.descriptors.PropertyDescriptor import org.jetbrains.kotlin.descriptors.PropertyDescriptor
import org.jetbrains.kotlin.descriptors.SimpleFunctionDescriptor import org.jetbrains.kotlin.descriptors.SimpleFunctionDescriptor
import org.jetbrains.kotlin.name.Name import org.jetbrains.kotlin.name.Name
@@ -38,7 +37,7 @@ import java.util.*
open class SerializationResolveExtension : SyntheticResolveExtension { open class SerializationResolveExtension : SyntheticResolveExtension {
override fun getSyntheticNestedClassNames(thisDescriptor: ClassDescriptor): List<Name> = when { override fun getSyntheticNestedClassNames(thisDescriptor: ClassDescriptor): List<Name> = when {
thisDescriptor.annotations.hasAnnotation(serialInfoFqName) && thisDescriptor.platform?.isJvm() == true -> listOf(SerialEntityNames.IMPL_NAME) 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) listOf(SerialEntityNames.SERIALIZER_CLASS_NAME)
else -> listOf() else -> listOf()
} }
@@ -58,7 +57,7 @@ open class SerializationResolveExtension : SyntheticResolveExtension {
) { ) {
if (thisDescriptor.annotations.hasAnnotation(serialInfoFqName) && name == SerialEntityNames.IMPL_NAME) if (thisDescriptor.annotations.hasAnnotation(serialInfoFqName) && name == SerialEntityNames.IMPL_NAME)
result.add(KSerializerDescriptorResolver.addSerialInfoImplClass(thisDescriptor, declarationProvider, ctx)) 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.none { it.name == SerialEntityNames.SERIALIZER_CLASS_NAME }
) )
result.add(KSerializerDescriptorResolver.addSerializerImplClass(thisDescriptor, declarationProvider, ctx)) result.add(KSerializerDescriptorResolver.addSerializerImplClass(thisDescriptor, declarationProvider, ctx))
@@ -66,7 +65,7 @@ open class SerializationResolveExtension : SyntheticResolveExtension {
} }
override fun getSyntheticCompanionObjectNameIfNeeded(thisDescriptor: ClassDescriptor): Name? = 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 SpecialNames.DEFAULT_NAME_FOR_COMPANION_OBJECT
else null else null
@@ -23,6 +23,7 @@ import org.jetbrains.kotlin.descriptors.annotations.Annotations
import org.jetbrains.kotlin.name.ClassId import org.jetbrains.kotlin.name.ClassId
import org.jetbrains.kotlin.name.FqName import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.psi.KtAnnotationEntry 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.classId
import org.jetbrains.kotlin.resolve.descriptorUtil.module import org.jetbrains.kotlin.resolve.descriptorUtil.module
import org.jetbrains.kotlin.resolve.lazy.descriptors.LazyAnnotationDescriptor import org.jetbrains.kotlin.resolve.lazy.descriptors.LazyAnnotationDescriptor
@@ -37,6 +38,7 @@ internal fun isAllowedToHaveAutoGeneratedSerializerMethods(
classDescriptor: ClassDescriptor, classDescriptor: ClassDescriptor,
serializableClassDescriptor: ClassDescriptor serializableClassDescriptor: ClassDescriptor
): Boolean { ): Boolean {
if (serializableClassDescriptor.isSerializableEnum()) return true
// don't generate automatically anything for enums or interfaces or other strange things // don't generate automatically anything for enums or interfaces or other strange things
if (serializableClassDescriptor.kind != ClassKind.CLASS) return false if (serializableClassDescriptor.kind != ClassKind.CLASS) return false
// it is either GeneratedSerializer implementation // it is either GeneratedSerializer implementation
@@ -105,7 +107,7 @@ val KotlinType?.toClassDescriptor: ClassDescriptor?
} }
internal val ClassDescriptor.shouldHaveGeneratedMethodsInCompanion: Boolean 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 internal val ClassDescriptor.isSerializableObject: Boolean
get() = kind == ClassKind.OBJECT && hasSerializableAnnotationWithoutArgs get() = kind == ClassKind.OBJECT && hasSerializableAnnotationWithoutArgs
@@ -116,6 +118,30 @@ internal val ClassDescriptor.isInternalSerializable: Boolean //todo normal check
return hasSerializableAnnotationWithoutArgs 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<ClassDescriptor> {
check(this.kind == ClassKind.ENUM_CLASS)
return unsubstitutedMemberScope.getContributedDescriptors().asSequence()
.filterIsInstance<ClassDescriptor>()
.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 internal val ClassDescriptor.hasSerializableAnnotationWithoutArgs: Boolean
get() { get() {
if (!annotations.hasAnnotation(SerializationAnnotations.serializableAnnotationFqName)) return false if (!annotations.hasAnnotation(SerializationAnnotations.serializableAnnotationFqName)) return false
@@ -152,7 +178,7 @@ internal val ClassDescriptor?.classSerializer: ClassDescriptor?
// can infer @Poly? // can infer @Poly?
polymorphicSerializerIfApplicableAutomatically()?.let { return it } polymorphicSerializerIfApplicableAutomatically()?.let { return it }
// default serializable? // default serializable?
if (isInternalSerializable) { if (shouldHaveGeneratedSerializer) {
// $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 })
@@ -194,7 +220,7 @@ internal fun getSerializableClassDescriptorBySerializer(serializerDescriptor: Cl
) )
) return null ) return null
val classDescriptor = (serializerDescriptor.containingDeclaration as? ClassDescriptor) ?: return null val classDescriptor = (serializerDescriptor.containingDeclaration as? ClassDescriptor) ?: return null
if (!classDescriptor.isInternalSerializable) return null if (!classDescriptor.shouldHaveGeneratedSerializer) return null
return classDescriptor return classDescriptor
} }
@@ -55,6 +55,7 @@ object SerialEntityNames {
const val SERIAL_DESCRIPTOR_CLASS = "SerialDescriptor" const val SERIAL_DESCRIPTOR_CLASS = "SerialDescriptor"
const val SERIAL_DESCRIPTOR_CLASS_IMPL = "SerialClassDescImpl" const val SERIAL_DESCRIPTOR_CLASS_IMPL = "SerialClassDescImpl"
const val SERIAL_DESCRIPTOR_FOR_ENUM = "EnumDescriptor"
//exceptions //exceptions
const val SERIAL_EXC = "SerializationException" const val SERIAL_EXC = "SerializationException"