Custom-serializable enums for JVM
This commit is contained in:
+1
-2
@@ -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
|
||||||
|
|
||||||
|
|||||||
+8
-2
@@ -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(
|
||||||
|
|||||||
+2
-1
@@ -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()
|
||||||
|
|||||||
+2
-1
@@ -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()
|
||||||
|
|||||||
+2
@@ -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")
|
||||||
|
|||||||
+2
-1
@@ -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()
|
||||||
|
|||||||
+40
-30
@@ -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 {
|
||||||
|
|||||||
+65
@@ -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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
+10
-5
@@ -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()
|
||||||
+3
-4
@@ -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
|
||||||
|
|
||||||
|
|||||||
+29
-3
@@ -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
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+1
@@ -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"
|
||||||
|
|||||||
Reference in New Issue
Block a user