Use EnumSerializer for explicitly serializable enum instead of auto-generated

Resolves Kotlin/kotlinx.serialization#683 Kotlin/kotlinx.serialization#1372
This commit is contained in:
Sergey.Shanshin
2022-06-14 10:21:48 +00:00
committed by Space
parent f8310a4763
commit f192c2a541
14 changed files with 313 additions and 84 deletions
@@ -33,7 +33,10 @@ abstract class SerializableCompanionCodegen(
"probably clash with user-defined function has occurred" "probably clash with user-defined function has occurred"
) )
if (serializableDescriptor.isSerializableObject || serializableDescriptor.isAbstractOrSealedSerializableClass()) { if (serializableDescriptor.isSerializableObject
|| serializableDescriptor.isAbstractOrSealedSerializableClass()
|| serializableDescriptor.isSerializableEnum()
) {
generateLazySerializerGetter(serializerGetterDescriptor) generateLazySerializerGetter(serializerGetterDescriptor)
} else { } else {
generateSerializerGetter(serializerGetterDescriptor) generateSerializerGetter(serializerGetterDescriptor)
@@ -225,7 +225,7 @@ 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 && !classDescriptor.isInternallySerializableEnum()) return if (classDescriptor.kind == ClassKind.ENUM_CLASS && !classDescriptor.isEnumWithLegacyGeneratedSerializer())
module.findClassAcrossModuleDependencies(enumSerializerId) module.findClassAcrossModuleDependencies(enumSerializerId)
else null else null
} }
@@ -52,6 +52,12 @@ interface IrBuilderExtension {
private val throwMissedFieldExceptionArrayFunc private val throwMissedFieldExceptionArrayFunc
get() = compilerContext.referenceFunctions(SerialEntityNames.ARRAY_MASK_FIELD_MISSING_FUNC_FQ).singleOrNull() get() = compilerContext.referenceFunctions(SerialEntityNames.ARRAY_MASK_FIELD_MISSING_FUNC_FQ).singleOrNull()
private val enumSerializerFactoryFunc
get() = compilerContext.referenceFunctions(SerialEntityNames.ENUM_SERIALIZER_FACTORY_FUNC_FQ).singleOrNull()
private val markedEnumSerializerFactoryFunc
get() = compilerContext.referenceFunctions(SerialEntityNames.MARKED_ENUM_SERIALIZER_FACTORY_FUNC_FQ).singleOrNull()
private inline fun <reified T : IrDeclaration> IrClass.searchForDeclaration(descriptor: DeclarationDescriptor): T? { private inline fun <reified T : IrDeclaration> IrClass.searchForDeclaration(descriptor: DeclarationDescriptor): T? {
return declarations.singleOrNull { it.descriptor == descriptor } as? T return declarations.singleOrNull { it.descriptor == descriptor } as? T
} }
@@ -1041,13 +1047,55 @@ interface IrBuilderExtension {
} }
enumSerializerId -> { enumSerializerId -> {
serializerClass = module.getClassFromInternalSerializationPackage(SpecialBuiltins.enumSerializer) serializerClass = module.getClassFromInternalSerializationPackage(SpecialBuiltins.enumSerializer)
args = kType.toClassDescriptor!!.let { enumDesc -> val enumDescriptor = kType.toClassDescriptor!!
listOf(
irString(enumDesc.serialName()),
irCall(findEnumValuesMethod(enumDesc))
)
}
typeArgs = listOf(thisIrType) typeArgs = listOf(thisIrType)
// instantiate serializer only inside enum Companion
if (enclosingGenerator !is SerializableCompanionIrGenerator) {
// otherwise call Companion.serializer()
callSerializerFromCompanion(thisIrType, typeArgs, emptyList())?.let { return it }
}
val enumArgs = mutableListOf(
irString(enumDescriptor.serialName()),
irCall(findEnumValuesMethod(enumDescriptor)),
)
val enumSerializerFactoryFunc = enumSerializerFactoryFunc
val markedEnumSerializerFactoryFunc = markedEnumSerializerFactoryFunc
if (enumSerializerFactoryFunc != null && markedEnumSerializerFactoryFunc != null) {
// runtime contains enum serializer factory functions
val factoryFunc: IrSimpleFunctionSymbol = if (enumDescriptor.isEnumWithSerialInfoAnnotation()) {
// need to store SerialInfo annotation in descriptor
val enumEntries = enumDescriptor.enumEntries()
val entriesNames = enumEntries.map { it.annotations.serialNameValue?.let { n -> irString(n) } ?: irNull() }
val entriesAnnotations = enumEntries.map {
val annotationConstructors = it.annotations.mapNotNull { a ->
compilerContext.typeTranslator.constantValueGenerator.generateAnnotationConstructorCall(a)
}
val annotationsConstructors = copyAnnotationsFrom(annotationConstructors)
if (annotationsConstructors.isEmpty()) {
irNull()
} else {
createArrayOfExpression(compilerContext.irBuiltIns.annotationType, annotationsConstructors)
}
}
val annotationArrayType =
compilerContext.irBuiltIns.arrayClass.typeWith(compilerContext.irBuiltIns.annotationType.makeNullable())
enumArgs += createArrayOfExpression(compilerContext.irBuiltIns.stringType.makeNullable(), entriesNames)
enumArgs += createArrayOfExpression(annotationArrayType, entriesAnnotations)
markedEnumSerializerFactoryFunc
} else {
enumSerializerFactoryFunc
}
val factoryReturnType = factoryFunc.owner.returnType.substitute(factoryFunc.owner.typeParameters, typeArgs)
return irInvoke(null, factoryFunc, typeArgs, enumArgs, factoryReturnType)
} else {
// support legacy serializer instantiation by constructor for old runtimes
args = enumArgs
}
} }
else -> { else -> {
args = kType.arguments.map { args = kType.arguments.map {
@@ -566,7 +566,7 @@ open class SerializerIrGenerator(
) { ) {
val serializableDesc = getSerializableClassDescriptorBySerializer(irClass.symbol.descriptor) ?: return val serializableDesc = getSerializableClassDescriptorBySerializer(irClass.symbol.descriptor) ?: return
val generator = when { val generator = when {
serializableDesc.isInternallySerializableEnum() -> SerializerForEnumsGenerator(irClass, context, bindingContext, serialInfoJvmGenerator) serializableDesc.isEnumWithLegacyGeneratedSerializer() -> SerializerForEnumsGenerator(irClass, context, bindingContext, serialInfoJvmGenerator)
serializableDesc.isInlineClass() -> SerializerForInlineClassGenerator(irClass, context, bindingContext, serialInfoJvmGenerator) serializableDesc.isInlineClass() -> SerializerForInlineClassGenerator(irClass, context, bindingContext, serialInfoJvmGenerator)
else -> SerializerIrGenerator(irClass, context, bindingContext, metadataPlugin, serialInfoJvmGenerator) else -> SerializerIrGenerator(irClass, context, bindingContext, metadataPlugin, serialInfoJvmGenerator)
} }
@@ -12,6 +12,7 @@ import org.jetbrains.kotlin.js.resolve.diagnostics.findPsi
import org.jetbrains.kotlin.js.translate.context.TranslationContext import org.jetbrains.kotlin.js.translate.context.TranslationContext
import org.jetbrains.kotlin.js.translate.expression.ExpressionVisitor import org.jetbrains.kotlin.js.translate.expression.ExpressionVisitor
import org.jetbrains.kotlin.js.translate.expression.translateAndAliasParameters import org.jetbrains.kotlin.js.translate.expression.translateAndAliasParameters
import org.jetbrains.kotlin.js.translate.general.Translation
import org.jetbrains.kotlin.js.translate.reference.ReferenceTranslator import org.jetbrains.kotlin.js.translate.reference.ReferenceTranslator
import org.jetbrains.kotlin.js.translate.utils.JsAstUtils import org.jetbrains.kotlin.js.translate.utils.JsAstUtils
import org.jetbrains.kotlin.psi.KtExpression import org.jetbrains.kotlin.psi.KtExpression
@@ -165,18 +166,65 @@ internal fun AbstractSerialGenerator.serializerInstance(
serializerClass.classId == contextSerializerId || serializerClass.classId == polymorphicSerializerId -> listOf( serializerClass.classId == contextSerializerId || serializerClass.classId == polymorphicSerializerId -> listOf(
ExpressionVisitor.getObjectKClass(context, kType.toClassDescriptor!!) ExpressionVisitor.getObjectKClass(context, kType.toClassDescriptor!!)
) )
serializerClass.classId == enumSerializerId -> listOf( serializerClass.classId == enumSerializerId -> {
JsStringLiteral(kType.serialName()), val enumDescriptor = kType.toClassDescriptor!!
// EnumClass.values() invocation
JsInvocation( val enumArgs = mutableListOf(
context.getInnerNameForDescriptor( JsStringLiteral(enumDescriptor.serialName()),
DescriptorUtils.getFunctionByName( // EnumClass.values() invocation
kType.toClassDescriptor!!.staticScope, JsInvocation(
StandardNames.ENUM_VALUES context.getInnerNameForDescriptor(
) DescriptorUtils.getFunctionByName(
).makeRef() enumDescriptor.staticScope,
StandardNames.ENUM_VALUES
)
).makeRef()
)
) )
)
val packageScope = context.currentModule.getPackage(SerializationPackages.internalPackageFqName).memberScope
val enumSerializerFactoryFunc = DescriptorUtils.getFunctionByNameOrNull(
packageScope,
SerialEntityNames.ENUM_SERIALIZER_FACTORY_FUNC_NAME
)
val markedEnumSerializerFactoryFunc = DescriptorUtils.getFunctionByNameOrNull(
packageScope,
SerialEntityNames.MARKED_ENUM_SERIALIZER_FACTORY_FUNC_NAME
)
if (enumSerializerFactoryFunc != null && markedEnumSerializerFactoryFunc != null) {
// runtime contains enum serializer factory functions
val factoryFunc = if (enumDescriptor.isEnumWithSerialInfoAnnotation()) {
val enumEntries = enumDescriptor.enumEntries()
val entriesNames =
enumEntries.map { it.annotations.serialNameValue?.let { n -> JsStringLiteral(n) } ?: JsNullLiteral() }
val entriesAnnotations = enumEntries.map {
val annotationsConstructors = it.annotationsWithArguments().map { (annotationClass, args, _) ->
val argExprs = args.map { arg ->
Translation.translateAsExpression(arg.getArgumentExpression()!!, context)
}
val classRef = context.translateQualifiedReference(annotationClass)
JsNew(classRef, argExprs)
}
if (annotationsConstructors.isEmpty()) {
JsNullLiteral()
} else {
JsArrayLiteral(annotationsConstructors)
}
}
enumArgs += JsArrayLiteral(entriesNames)
enumArgs += JsArrayLiteral(entriesAnnotations)
markedEnumSerializerFactoryFunc
} else {
enumSerializerFactoryFunc
}
return JsInvocation(context.getInnerReference(factoryFunc), enumArgs)
} else {
// support legacy serializer instantiation by constructor for old runtimes
enumArgs
}
}
serializerClass.classId == objectSerializerId -> listOf( serializerClass.classId == objectSerializerId -> listOf(
JsStringLiteral(kType.serialName()), JsStringLiteral(kType.serialName()),
context.serializerObjectGetter(kType.toClassDescriptor!!) context.serializerObjectGetter(kType.toClassDescriptor!!)
@@ -373,7 +373,7 @@ open class SerializerJsTranslator(
metadataPlugin: SerializationDescriptorSerializerPlugin? metadataPlugin: SerializationDescriptorSerializerPlugin?
) { ) {
val serializableDesc = getSerializableClassDescriptorBySerializer(descriptor) ?: return val serializableDesc = getSerializableClassDescriptorBySerializer(descriptor) ?: return
if (serializableDesc.isInternallySerializableEnum()) { if (serializableDesc.isEnumWithLegacyGeneratedSerializer()) {
SerializerForEnumsTranslator(descriptor, translator, context).generate() SerializerForEnumsTranslator(descriptor, translator, context).generate()
} else { } else {
SerializerJsTranslator(descriptor, translator, context, metadataPlugin).generate() SerializerJsTranslator(descriptor, translator, context, metadataPlugin).generate()
@@ -19,6 +19,7 @@ import org.jetbrains.kotlin.load.kotlin.TypeMappingMode
import org.jetbrains.kotlin.load.kotlin.internalName import org.jetbrains.kotlin.load.kotlin.internalName
import org.jetbrains.kotlin.name.ClassId import org.jetbrains.kotlin.name.ClassId
import org.jetbrains.kotlin.name.Name import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.psi.ValueArgument
import org.jetbrains.kotlin.resolve.DescriptorFactory import org.jetbrains.kotlin.resolve.DescriptorFactory
import org.jetbrains.kotlin.resolve.descriptorUtil.builtIns import org.jetbrains.kotlin.resolve.descriptorUtil.builtIns
import org.jetbrains.kotlin.resolve.descriptorUtil.classId import org.jetbrains.kotlin.resolve.descriptorUtil.classId
@@ -38,7 +39,10 @@ import org.jetbrains.kotlinx.serialization.compiler.backend.common.*
import org.jetbrains.kotlinx.serialization.compiler.resolve.* import org.jetbrains.kotlinx.serialization.compiler.resolve.*
import org.jetbrains.kotlinx.serialization.compiler.resolve.SerialEntityNames.DECODER_CLASS import org.jetbrains.kotlinx.serialization.compiler.resolve.SerialEntityNames.DECODER_CLASS
import org.jetbrains.kotlinx.serialization.compiler.resolve.SerialEntityNames.ENCODER_CLASS import org.jetbrains.kotlinx.serialization.compiler.resolve.SerialEntityNames.ENCODER_CLASS
import org.jetbrains.kotlinx.serialization.compiler.resolve.SerialEntityNames.ENUMS_FILE
import org.jetbrains.kotlinx.serialization.compiler.resolve.SerialEntityNames.ENUM_SERIALIZER_FACTORY_FUNC_NAME
import org.jetbrains.kotlinx.serialization.compiler.resolve.SerialEntityNames.KSERIALIZER_CLASS import org.jetbrains.kotlinx.serialization.compiler.resolve.SerialEntityNames.KSERIALIZER_CLASS
import org.jetbrains.kotlinx.serialization.compiler.resolve.SerialEntityNames.MARKED_ENUM_SERIALIZER_FACTORY_FUNC_NAME
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.SerialEntityNames.PLUGIN_EXCEPTIONS_FILE import org.jetbrains.kotlinx.serialization.compiler.resolve.SerialEntityNames.PLUGIN_EXCEPTIONS_FILE
import org.jetbrains.kotlinx.serialization.compiler.resolve.SerialEntityNames.SERIAL_CTOR_MARKER_NAME import org.jetbrains.kotlinx.serialization.compiler.resolve.SerialEntityNames.SERIAL_CTOR_MARKER_NAME
@@ -68,7 +72,9 @@ internal val kOutputType = Type.getObjectType("kotlinx/serialization/encoding/$S
internal val encoderType = Type.getObjectType("kotlinx/serialization/encoding/$ENCODER_CLASS") internal val encoderType = Type.getObjectType("kotlinx/serialization/encoding/$ENCODER_CLASS")
internal val decoderType = Type.getObjectType("kotlinx/serialization/encoding/$DECODER_CLASS") internal val decoderType = Type.getObjectType("kotlinx/serialization/encoding/$DECODER_CLASS")
internal val kInputType = Type.getObjectType("kotlinx/serialization/encoding/$STRUCTURE_DECODER_CLASS") internal val kInputType = Type.getObjectType("kotlinx/serialization/encoding/$STRUCTURE_DECODER_CLASS")
internal val pluginUtilsType = Type.getObjectType("kotlinx/serialization/internal/${PLUGIN_EXCEPTIONS_FILE}Kt") internal val pluginUtilsType = Type.getObjectType("kotlinx/serialization/internal/${PLUGIN_EXCEPTIONS_FILE}Kt")
internal val enumFactoriesType = Type.getObjectType("kotlinx/serialization/internal/${ENUMS_FILE}Kt")
internal val jvmLambdaType = Type.getObjectType("kotlin/jvm/internal/Lambda") internal val jvmLambdaType = Type.getObjectType("kotlin/jvm/internal/Lambda")
internal val kotlinLazyType = Type.getObjectType("kotlin/Lazy") internal val kotlinLazyType = Type.getObjectType("kotlin/Lazy")
@@ -84,6 +90,12 @@ internal val serializationExceptionName = "kotlinx/serialization/$SERIAL_EXC"
internal val serializationExceptionMissingFieldName = "kotlinx/serialization/$MISSING_FIELD_EXC" internal val serializationExceptionMissingFieldName = "kotlinx/serialization/$MISSING_FIELD_EXC"
internal val serializationExceptionUnknownIndexName = "kotlinx/serialization/$UNKNOWN_FIELD_EXC" internal val serializationExceptionUnknownIndexName = "kotlinx/serialization/$UNKNOWN_FIELD_EXC"
private val annotationType = Type.getObjectType("java/lang/annotation/Annotation")
private val annotationArrayType = Type.getObjectType("[${annotationType.descriptor}")
private val doubleAnnotationArrayType = Type.getObjectType("[${annotationArrayType.descriptor}")
private val stringType = AsmTypes.JAVA_STRING_TYPE
private val stringArrayType = Type.getObjectType("[${stringType.descriptor}")
internal val descriptorGetterName = JvmAbi.getterName(SERIAL_DESC_FIELD) internal val descriptorGetterName = JvmAbi.getterName(SERIAL_DESC_FIELD)
internal val getLazyValueName = JvmAbi.getterName("value") internal val getLazyValueName = JvmAbi.getterName("value")
@@ -114,8 +126,8 @@ fun InstructionAdapter.genKOutputMethodCall(
) { ) {
val propertyType = classCodegen.typeMapper.mapType(property.type) val propertyType = classCodegen.typeMapper.mapType(property.type)
val sti = generator.getSerialTypeInfo(property, propertyType) val sti = generator.getSerialTypeInfo(property, propertyType)
val useSerializer = if (fromClassStartVar == null) stackValueSerializerInstanceFromSerializer(classCodegen, sti, generator) val useSerializer = if (fromClassStartVar == null) stackValueSerializerInstanceFromSerializer(expressionCodegen, classCodegen, sti, generator)
else stackValueSerializerInstanceFromClass(classCodegen, sti, fromClassStartVar, generator) else stackValueSerializerInstanceFromClass(expressionCodegen, classCodegen, sti, fromClassStartVar, generator)
val actualType = ImplementationBodyCodegen.genPropertyOnStack( val actualType = ImplementationBodyCodegen.genPropertyOnStack(
this, this,
expressionCodegen.context, expressionCodegen.context,
@@ -179,14 +191,16 @@ internal val contextSerializerId = ClassId(packageFqName, Name.identifier(Specia
internal fun InstructionAdapter.stackValueSerializerInstanceFromClass( internal fun InstructionAdapter.stackValueSerializerInstanceFromClass(
codegen: ClassBodyCodegen, expressionCodegen: ExpressionCodegen,
classCodegen: ClassBodyCodegen,
sti: JVMSerialTypeInfo, sti: JVMSerialTypeInfo,
varIndexStart: Int, varIndexStart: Int,
serializerCodegen: AbstractSerialGenerator serializerCodegen: AbstractSerialGenerator
): Boolean { ): Boolean {
val serializer = sti.serializer val serializer = sti.serializer
return serializerCodegen.stackValueSerializerInstance( return serializerCodegen.stackValueSerializerInstance(
codegen, expressionCodegen,
classCodegen,
sti.property.module, sti.property.module,
sti.property.type, sti.property.type,
serializer, serializer,
@@ -198,6 +212,7 @@ internal fun InstructionAdapter.stackValueSerializerInstanceFromClass(
} }
internal fun InstructionAdapter.stackValueSerializerInstanceFromSerializerWithoutSti( internal fun InstructionAdapter.stackValueSerializerInstanceFromSerializerWithoutSti(
expressionCodegen: ExpressionCodegen,
codegen: ClassBodyCodegen, codegen: ClassBodyCodegen,
property: SerializableProperty, property: SerializableProperty,
serializerCodegen: AbstractSerialGenerator serializerCodegen: AbstractSerialGenerator
@@ -210,6 +225,7 @@ internal fun InstructionAdapter.stackValueSerializerInstanceFromSerializerWithou
property.descriptor.findPsi() property.descriptor.findPsi()
) else null ) else null
return serializerCodegen.stackValueSerializerInstance( return serializerCodegen.stackValueSerializerInstance(
expressionCodegen,
codegen, codegen,
property.module, property.module,
property.type, property.type,
@@ -222,9 +238,14 @@ internal fun InstructionAdapter.stackValueSerializerInstanceFromSerializerWithou
}.also { if (it && property.type.isMarkedNullable) wrapStackValueIntoNullableSerializer() } }.also { if (it && property.type.isMarkedNullable) wrapStackValueIntoNullableSerializer() }
} }
internal fun InstructionAdapter.stackValueSerializerInstanceFromSerializer(codegen: ClassBodyCodegen, sti: JVMSerialTypeInfo, serializerCodegen: AbstractSerialGenerator): Boolean { internal fun InstructionAdapter.stackValueSerializerInstanceFromSerializer(
expressionCodegen: ExpressionCodegen,
codegen: ClassBodyCodegen,
sti: JVMSerialTypeInfo,
serializerCodegen: AbstractSerialGenerator
): Boolean {
return serializerCodegen.stackValueSerializerInstance( return serializerCodegen.stackValueSerializerInstance(
codegen, sti.property.module, sti.property.type, expressionCodegen, codegen, sti.property.module, sti.property.type,
sti.serializer, this, sti.property.genericIndex sti.serializer, this, sti.property.genericIndex
) { idx, _ -> ) { idx, _ ->
load(0, kSerializerType) load(0, kSerializerType)
@@ -234,7 +255,7 @@ internal fun InstructionAdapter.stackValueSerializerInstanceFromSerializer(codeg
// returns false is cannot not use serializer // returns false is cannot not use serializer
// use iv == null to check only (do not emit serializer onto stack) // use iv == null to check only (do not emit serializer onto stack)
internal fun AbstractSerialGenerator.stackValueSerializerInstance(codegen: ClassBodyCodegen, module: ModuleDescriptor, kType: KotlinType, maybeSerializer: ClassDescriptor?, internal fun AbstractSerialGenerator.stackValueSerializerInstance(expressionCodegen: ExpressionCodegen, classCodegen: ClassBodyCodegen, module: ModuleDescriptor, kType: KotlinType, maybeSerializer: ClassDescriptor?,
iv: InstructionAdapter?, iv: InstructionAdapter?,
genericIndex: Int? = null, genericIndex: Int? = null,
genericSerializerFieldGetter: (InstructionAdapter.(Int, KotlinType) -> Unit)? = null genericSerializerFieldGetter: (InstructionAdapter.(Int, KotlinType) -> Unit)? = null
@@ -248,7 +269,7 @@ internal fun AbstractSerialGenerator.stackValueSerializerInstance(codegen: Class
if (serializer.kind == ClassKind.OBJECT) { if (serializer.kind == ClassKind.OBJECT) {
// singleton serializer -- just get it // singleton serializer -- just get it
if (iv != null) if (iv != null)
StackValue.singleton(serializer, codegen.typeMapper).put(kSerializerType, iv) StackValue.singleton(serializer, classCodegen.typeMapper).put(kSerializerType, iv)
return true return true
} }
// serializer is not singleton object and shall be instantiated // serializer is not singleton object and shall be instantiated
@@ -256,12 +277,13 @@ internal fun AbstractSerialGenerator.stackValueSerializerInstance(codegen: Class
// bail out from stackValueSerializerInstance if any type argument is not serializable // bail out from stackValueSerializerInstance if any type argument is not serializable
val argType = projection.type val argType = projection.type
val argSerializer = if (argType.isTypeParameter()) null else { val argSerializer = if (argType.isTypeParameter()) null else {
findTypeSerializerOrContext(module, argType, sourceElement = codegen.descriptor.findPsi()) findTypeSerializerOrContext(module, argType, sourceElement = classCodegen.descriptor.findPsi())
?: return false ?: return false
} }
// check if it can be properly serialized with its args recursively // check if it can be properly serialized with its args recursively
if (!stackValueSerializerInstance( if (!stackValueSerializerInstance(
codegen, expressionCodegen,
classCodegen,
module, module,
argType, argType,
argSerializer, argSerializer,
@@ -275,7 +297,65 @@ internal fun AbstractSerialGenerator.stackValueSerializerInstance(codegen: Class
} }
// new serializer if needed // new serializer if needed
iv?.apply { iv?.apply {
val serializerType = codegen.typeMapper.mapClass(serializer) val serializerType = classCodegen.typeMapper.mapClass(serializer)
val classDescriptor = kType.toClassDescriptor!!
if (serializer.classId == enumSerializerId && !classDescriptor.useGeneratedEnumSerializer) {
// runtime contains enum serializer factory functions
val javaEnumArray = Type.getType("[Ljava/lang/Enum;")
val enumJavaType = classCodegen.typeMapper.mapType(kType, null, TypeMappingMode.GENERIC_ARGUMENT)
val serialName = classDescriptor.serialName()
if (classDescriptor.isEnumWithSerialInfoAnnotation()) {
aconst(serialName)
invokestatic(enumJavaType.internalName, "values", "()[${enumJavaType.descriptor}", false)
checkcast(javaEnumArray)
val entries = classDescriptor.enumEntries()
fillArray(stringType, entries) { _, entry ->
entry.annotations.serialNameValue.let {
if (it == null) {
aconst(null)
} else {
aconst(it)
}
}
}
checkcast(stringArrayType)
fillArray(annotationArrayType, entries) { _, entry ->
val annotations = entry.annotationsWithArguments()
if (annotations.isEmpty()) {
aconst(null)
} else {
fillArray(annotationType, annotations) { _, annotation ->
val (annotationClass, args, consParams) = annotation
expressionCodegen.generateSyntheticAnnotationOnStack(annotationClass, args, consParams)
}
}
}
checkcast(doubleAnnotationArrayType)
invokestatic(
enumFactoriesType.internalName,
MARKED_ENUM_SERIALIZER_FACTORY_FUNC_NAME.asString(),
"(${stringType.descriptor}${javaEnumArray.descriptor}${stringArrayType.descriptor}${doubleAnnotationArrayType.descriptor})${kSerializerType.descriptor}",
false
)
} else {
aconst(serialName)
invokestatic(enumJavaType.internalName, "values", "()[${enumJavaType.descriptor}", false)
checkcast(javaEnumArray)
invokestatic(
enumFactoriesType.internalName,
ENUM_SERIALIZER_FACTORY_FUNC_NAME.asString(),
"(${stringType.descriptor}${javaEnumArray.descriptor})${kSerializerType.descriptor}",
false
)
}
return true
}
// todo: support static factory methods for serializers for shorter bytecode // todo: support static factory methods for serializers for shorter bytecode
anew(serializerType) anew(serializerType)
dup() dup()
@@ -286,7 +366,8 @@ internal fun AbstractSerialGenerator.stackValueSerializerInstance(codegen: Class
val (argType, argSerializer) = typeArgument val (argType, argSerializer) = typeArgument
assert( assert(
stackValueSerializerInstance( stackValueSerializerInstance(
codegen, expressionCodegen,
classCodegen,
module, module,
argType, argType,
argSerializer, argSerializer,
@@ -303,9 +384,10 @@ internal fun AbstractSerialGenerator.stackValueSerializerInstance(codegen: Class
val serialName = kType.serialName() val serialName = kType.serialName()
when (serializer.classId) { when (serializer.classId) {
enumSerializerId -> { enumSerializerId -> {
// support legacy serializer instantiation by constructor for old runtimes
aconst(serialName) aconst(serialName)
signature.append("Ljava/lang/String;") signature.append("Ljava/lang/String;")
val enumJavaType = codegen.typeMapper.mapType(kType, null, TypeMappingMode.GENERIC_ARGUMENT) val enumJavaType = classCodegen.typeMapper.mapType(kType, null, TypeMappingMode.GENERIC_ARGUMENT)
val javaEnumArray = Type.getType("[Ljava/lang/Enum;") val javaEnumArray = Type.getType("[Ljava/lang/Enum;")
invokestatic(enumJavaType.internalName, "values","()[${enumJavaType.descriptor}", false) invokestatic(enumJavaType.internalName, "values","()[${enumJavaType.descriptor}", false)
checkcast(javaEnumArray) checkcast(javaEnumArray)
@@ -314,7 +396,7 @@ internal fun AbstractSerialGenerator.stackValueSerializerInstance(codegen: Class
contextSerializerId, polymorphicSerializerId -> { contextSerializerId, polymorphicSerializerId -> {
// a special way to instantiate enum -- need a enum KClass reference // a special way to instantiate enum -- need a enum KClass reference
// GENERIC_ARGUMENT forces boxing in order to obtain KClass // GENERIC_ARGUMENT forces boxing in order to obtain KClass
aconst(codegen.typeMapper.mapType(kType, null, TypeMappingMode.GENERIC_ARGUMENT)) aconst(classCodegen.typeMapper.mapType(kType, null, TypeMappingMode.GENERIC_ARGUMENT))
AsmUtil.wrapJavaClassIntoKClass(this) AsmUtil.wrapJavaClassIntoKClass(this)
signature.append(AsmTypes.K_CLASS_TYPE.descriptor) signature.append(AsmTypes.K_CLASS_TYPE.descriptor)
if (serializer.classId == contextSerializerId && serializer.constructors.any { it.valueParameters.size == 3 }) { if (serializer.classId == contextSerializerId && serializer.constructors.any { it.valueParameters.size == 3 }) {
@@ -334,7 +416,7 @@ internal fun AbstractSerialGenerator.stackValueSerializerInstance(codegen: Class
} }
referenceArraySerializerId -> { referenceArraySerializerId -> {
// a special way to instantiate reference array serializer -- need an element KClass reference // a special way to instantiate reference array serializer -- need an element KClass reference
aconst(codegen.typeMapper.mapType(kType.arguments[0].type, null, TypeMappingMode.GENERIC_ARGUMENT)) aconst(classCodegen.typeMapper.mapType(kType.arguments[0].type, null, TypeMappingMode.GENERIC_ARGUMENT))
AsmUtil.wrapJavaClassIntoKClass(this) AsmUtil.wrapJavaClassIntoKClass(this)
signature.append(AsmTypes.K_CLASS_TYPE.descriptor) signature.append(AsmTypes.K_CLASS_TYPE.descriptor)
// Reference array serializer still needs serializer for its argument type // Reference array serializer still needs serializer for its argument type
@@ -343,13 +425,13 @@ internal fun AbstractSerialGenerator.stackValueSerializerInstance(codegen: Class
sealedSerializerId -> { sealedSerializerId -> {
aconst(serialName) aconst(serialName)
signature.append("Ljava/lang/String;") signature.append("Ljava/lang/String;")
aconst(codegen.typeMapper.mapType(kType, null, TypeMappingMode.GENERIC_ARGUMENT)) aconst(classCodegen.typeMapper.mapType(kType, null, TypeMappingMode.GENERIC_ARGUMENT))
AsmUtil.wrapJavaClassIntoKClass(this) AsmUtil.wrapJavaClassIntoKClass(this)
signature.append(AsmTypes.K_CLASS_TYPE.descriptor) signature.append(AsmTypes.K_CLASS_TYPE.descriptor)
val (subClasses, subSerializers) = allSealedSerializableSubclassesFor(kType.toClassDescriptor!!, module) val (subClasses, subSerializers) = allSealedSerializableSubclassesFor(kType.toClassDescriptor!!, module)
// KClasses vararg // KClasses vararg
fillArray(AsmTypes.K_CLASS_TYPE, subClasses) { _, type -> fillArray(AsmTypes.K_CLASS_TYPE, subClasses) { _, type ->
aconst(codegen.typeMapper.mapType(type, null, TypeMappingMode.GENERIC_ARGUMENT)) aconst(classCodegen.typeMapper.mapType(type, null, TypeMappingMode.GENERIC_ARGUMENT))
AsmUtil.wrapJavaClassIntoKClass(this) AsmUtil.wrapJavaClassIntoKClass(this)
} }
signature.append(AsmTypes.K_CLASS_ARRAY_TYPE.descriptor) signature.append(AsmTypes.K_CLASS_ARRAY_TYPE.descriptor)
@@ -358,7 +440,8 @@ internal fun AbstractSerialGenerator.stackValueSerializerInstance(codegen: Class
val (argType, argSerializer) = subClasses[i] to serializer val (argType, argSerializer) = subClasses[i] to serializer
assert( assert(
stackValueSerializerInstance( stackValueSerializerInstance(
codegen, expressionCodegen,
classCodegen,
module, module,
argType, argType,
argSerializer, argSerializer,
@@ -368,7 +451,8 @@ internal fun AbstractSerialGenerator.stackValueSerializerInstance(codegen: Class
// if we encountered generic type parameter in one of subclasses of sealed class, use polymorphism from upper bound // if we encountered generic type parameter in one of subclasses of sealed class, use polymorphism from upper bound
assert( assert(
stackValueSerializerInstance( stackValueSerializerInstance(
codegen, expressionCodegen,
classCodegen,
module, module,
(genericType.constructor.declarationDescriptor as TypeParameterDescriptor).representativeUpperBound, (genericType.constructor.declarationDescriptor as TypeParameterDescriptor).representativeUpperBound,
module.getClassFromSerializationPackage(SpecialBuiltins.polymorphicSerializer), module.getClassFromSerializationPackage(SpecialBuiltins.polymorphicSerializer),
@@ -384,7 +468,7 @@ internal fun AbstractSerialGenerator.stackValueSerializerInstance(codegen: Class
objectSerializerId -> { objectSerializerId -> {
aconst(serialName) aconst(serialName)
signature.append("Ljava/lang/String;") signature.append("Ljava/lang/String;")
StackValue.singleton(kType.toClassDescriptor!!, codegen.typeMapper).put(Type.getType("Ljava/lang/Object;"), iv) StackValue.singleton(kType.toClassDescriptor!!, classCodegen.typeMapper).put(Type.getType("Ljava/lang/Object;"), iv)
signature.append("Ljava/lang/Object;") signature.append("Ljava/lang/Object;")
} }
// all serializers get arguments with serializers of their generic types // all serializers get arguments with serializers of their generic types
@@ -397,6 +481,29 @@ internal fun AbstractSerialGenerator.stackValueSerializerInstance(codegen: Class
return true return true
} }
internal fun ExpressionCodegen.generateSyntheticAnnotationOnStack(
annotationClass: ClassDescriptor,
args: List<ValueArgument>,
ctorParams: List<ValueParameterDescriptor>
) {
val implType = typeMapper.mapType(annotationClass).internalName + "\$" + SerialEntityNames.IMPL_NAME.identifier
with(v) {
// new Annotation$Impl(...)
anew(Type.getObjectType(implType))
dup()
val sb = StringBuilder("(")
for (i in ctorParams.indices) {
val decl = args[i]
val desc = ctorParams[i]
val valAsmType = typeMapper.mapType(desc.type)
this@generateSyntheticAnnotationOnStack.gen(decl.getArgumentExpression(), valAsmType)
sb.append(valAsmType.descriptor)
}
sb.append(")V")
invokespecial(implType, "<init>", sb.toString(), false)
}
}
fun InstructionAdapter.wrapStackValueIntoNullableSerializer() = fun InstructionAdapter.wrapStackValueIntoNullableSerializer() =
invokestatic( invokestatic(
"kotlinx/serialization/builtins/BuiltinSerializersKt", "getNullable", "kotlinx/serialization/builtins/BuiltinSerializersKt", "getNullable",
@@ -406,10 +513,10 @@ fun InstructionAdapter.wrapStackValueIntoNullableSerializer() =
fun <T> InstructionAdapter.fillArray(type: Type, args: List<T>, onEach: (Int, T) -> Unit) { fun <T> InstructionAdapter.fillArray(type: Type, args: List<T>, onEach: (Int, T) -> Unit) {
iconst(args.size) iconst(args.size)
newarray(type) newarray(type)
args.forEachIndexed { i, serializer -> args.forEachIndexed { i, arg ->
dup() dup()
iconst(i) iconst(i)
onEach(i, serializer) onEach(i, arg)
astore(type) astore(type)
} }
} }
@@ -510,7 +617,7 @@ internal fun createSingletonLambda(
lambdaName: String, lambdaName: String,
outerClassCodegen: ImplementationBodyCodegen, outerClassCodegen: ImplementationBodyCodegen,
resultSimpleType: SimpleType, resultSimpleType: SimpleType,
block: InstructionAdapter.(ImplementationBodyCodegen) -> Unit block: InstructionAdapter.(ImplementationBodyCodegen, ExpressionCodegen) -> Unit
): Type { ): Type {
val lambdaType = Type.getObjectType("${outerClassCodegen.className}\$$lambdaName") val lambdaType = Type.getObjectType("${outerClassCodegen.className}\$$lambdaName")
@@ -631,8 +738,8 @@ internal fun createSingletonLambda(
DescriptorVisibilities.PUBLIC DescriptorVisibilities.PUBLIC
) )
lambdaCodegen.generateMethod(invokeFunction) { _, _ -> lambdaCodegen.generateMethod(invokeFunction) { _, expressionCodegen ->
block(lambdaCodegen) block(lambdaCodegen, expressionCodegen)
} }
val bridgeInvokeFunction = SimpleFunctionDescriptorImpl.create( val bridgeInvokeFunction = SimpleFunctionDescriptorImpl.create(
@@ -103,7 +103,15 @@ class SerializableCodegenImpl(
superTypeArguments.forEach { superTypeArguments.forEach {
val genericIdx = serializableDescriptor.defaultType.arguments.indexOf(it).let { if (it == -1) null else it } val genericIdx = serializableDescriptor.defaultType.arguments.indexOf(it).let { if (it == -1) null else it }
val serial = findTypeSerializerOrContext(serializableDescriptor.module, it.type) val serial = findTypeSerializerOrContext(serializableDescriptor.module, it.type)
stackValueSerializerInstance(classCodegen, serializableDescriptor.module, it.type, serial, this, genericIdx) { i, _ -> stackValueSerializerInstance(
exprCodegen,
classCodegen,
serializableDescriptor.module,
it.type,
serial,
this,
genericIdx
) { i, _ ->
load(offsetI + i, kSerializerType) load(offsetI + i, kSerializerType)
} }
} }
@@ -57,7 +57,11 @@ class SerializableCompanionCodegenImpl(private val classCodegen: ImplementationB
// create singleton lambda class // create singleton lambda class
val lambdaType = val lambdaType =
createSingletonLambda("serializer\$1", classCodegen, companionDescriptor.getKSerializer().defaultType) { lambdaCodegen -> createSingletonLambda(
"serializer\$1",
classCodegen,
companionDescriptor.getKSerializer().defaultType
) { lambdaCodegen, expressionCodegen ->
val serializerDescriptor = requireNotNull( val serializerDescriptor = requireNotNull(
findTypeSerializer( findTypeSerializer(
serializableDescriptor.module, serializableDescriptor.module,
@@ -65,6 +69,7 @@ class SerializableCompanionCodegenImpl(private val classCodegen: ImplementationB
) )
) )
stackValueSerializerInstance( stackValueSerializerInstance(
expressionCodegen,
lambdaCodegen, lambdaCodegen,
serializableDescriptor.module, serializableDescriptor.module,
serializableDescriptor.defaultType, serializableDescriptor.defaultType,
@@ -106,8 +111,9 @@ class SerializableCompanionCodegenImpl(private val classCodegen: ImplementationB
serializableDescriptor.toSimpleType() serializableDescriptor.toSimpleType()
) )
) )
classCodegen.generateMethod(methodDescriptor) { _, _ -> classCodegen.generateMethod(methodDescriptor) { _, expressionCodegen ->
stackValueSerializerInstance( stackValueSerializerInstance(
expressionCodegen,
classCodegen, classCodegen,
serializableDescriptor.module, serializableDescriptor.module,
serializableDescriptor.defaultType, serializableDescriptor.defaultType,
@@ -8,7 +8,6 @@ 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.descriptors.annotations.Annotated
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
import org.jetbrains.kotlinx.serialization.compiler.backend.common.SerializerCodegen import org.jetbrains.kotlinx.serialization.compiler.backend.common.SerializerCodegen
@@ -37,7 +36,7 @@ open class SerializerCodegenImpl(
companion object { companion object {
fun generateSerializerExtensions(codegen: ImplementationBodyCodegen, metadataPlugin: SerializationDescriptorSerializerPlugin?) { fun generateSerializerExtensions(codegen: ImplementationBodyCodegen, metadataPlugin: SerializationDescriptorSerializerPlugin?) {
val serializableClass = getSerializableClassDescriptorBySerializer(codegen.descriptor) ?: return val serializableClass = getSerializableClassDescriptorBySerializer(codegen.descriptor) ?: return
val serializerCodegen = if (serializableClass.isInternallySerializableEnum()) { val serializerCodegen = if (serializableClass.isEnumWithLegacyGeneratedSerializer()) {
SerializerForEnumsCodegen(codegen, serializableClass) SerializerForEnumsCodegen(codegen, serializableClass)
} else { } else {
SerializerCodegenImpl(codegen, serializableClass, metadataPlugin) SerializerCodegenImpl(codegen, serializableClass, metadataPlugin)
@@ -144,30 +143,6 @@ open class SerializerCodegenImpl(
expr.generateSerialDescriptor(0, true) expr.generateSerialDescriptor(0, true)
} }
private fun ExpressionCodegen.generateSyntheticAnnotationOnStack(
annotationClass: ClassDescriptor,
args: List<ValueArgument>,
ctorParams: List<ValueParameterDescriptor>
) {
val implType =
codegen.typeMapper.mapType(annotationClass).internalName + "\$" + SerialEntityNames.IMPL_NAME.identifier
with(v) {
// new Annotation$Impl(...)
anew(Type.getObjectType(implType))
dup()
val sb = StringBuilder("(")
for (i in ctorParams.indices) {
val decl = args[i]
val desc = ctorParams[i]
val valAsmType = codegen.typeMapper.mapType(desc.type)
this@generateSyntheticAnnotationOnStack.gen(decl.getArgumentExpression(), valAsmType)
sb.append(valAsmType.descriptor)
}
sb.append(")V")
invokespecial(implType, "<init>", sb.toString(), false)
}
}
// 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
protected fun InstructionAdapter.stackSerialClassDesc(classDescVar: Int?) { protected fun InstructionAdapter.stackSerialClassDesc(classDescVar: Int?) {
if (staticDescriptor) if (staticDescriptor)
@@ -192,7 +167,7 @@ open class SerializerCodegenImpl(
} }
override fun generateChildSerializersGetter(function: FunctionDescriptor) { override fun generateChildSerializersGetter(function: FunctionDescriptor) {
codegen.generateMethod(function) { _, _ -> codegen.generateMethod(function) { _, expressionCodegen ->
val size = serializableProperties.size val size = serializableProperties.size
iconst(size) iconst(size)
newarray(kSerializerType) newarray(kSerializerType)
@@ -202,6 +177,7 @@ open class SerializerCodegenImpl(
val prop = serializableProperties[i] val prop = serializableProperties[i]
assert( assert(
stackValueSerializerInstanceFromSerializerWithoutSti( stackValueSerializerInstanceFromSerializerWithoutSti(
expressionCodegen,
codegen, codegen,
prop, prop,
this@SerializerCodegenImpl this@SerializerCodegenImpl
@@ -335,7 +311,7 @@ open class SerializerCodegenImpl(
propVar = propsStartVar propVar = propsStartVar
for ((index, property) in serializableProperties.withIndex()) { for ((index, property) in serializableProperties.withIndex()) {
val propertyType = codegen.typeMapper.mapType(property.type) val propertyType = codegen.typeMapper.mapType(property.type)
callReadProperty(property, propertyType, index, inputVar, descVar, propVar) callReadProperty(expressionCodegen, property, propertyType, index, inputVar, descVar, propVar)
propVar += propertyType.size propVar += propertyType.size
} }
// set all bit masks to true // set all bit masks to true
@@ -373,7 +349,7 @@ open class SerializerCodegenImpl(
if (!property.transient) { if (!property.transient) {
// labelI: // labelI:
visitLabel(labels[labelNum + 1]) visitLabel(labels[labelNum + 1])
callReadProperty(property, propertyType, index, inputVar, descVar, propVar) callReadProperty(expressionCodegen, property, propertyType, index, inputVar, descVar, propVar)
// mark read bit in mask // mark read bit in mask
// bitMask = bitMask | 1 << index // bitMask = bitMask | 1 << index
@@ -449,6 +425,7 @@ open class SerializerCodegenImpl(
} }
private fun InstructionAdapter.callReadProperty( private fun InstructionAdapter.callReadProperty(
expressionCodegen: ExpressionCodegen,
property: SerializableProperty, property: SerializableProperty,
propertyType: Type, propertyType: Type,
index: Int, index: Int,
@@ -462,7 +439,7 @@ open class SerializerCodegenImpl(
iconst(index) iconst(index)
val sti = getSerialTypeInfo(property, propertyType) val sti = getSerialTypeInfo(property, propertyType)
val useSerializer = stackValueSerializerInstanceFromSerializer(codegen, sti, this@SerializerCodegenImpl) val useSerializer = stackValueSerializerInstanceFromSerializer(expressionCodegen, codegen, sti, this@SerializerCodegenImpl)
val unknownSer = (!useSerializer && sti.elementMethodPrefix.isEmpty()) val unknownSer = (!useSerializer && sti.elementMethodPrefix.isEmpty())
if (unknownSer) { if (unknownSer) {
aconst(codegen.typeMapper.mapType(property.type)) aconst(codegen.typeMapper.mapType(property.type))
@@ -36,6 +36,8 @@ import org.jetbrains.kotlinx.serialization.compiler.resolve.*
internal val SERIALIZABLE_PROPERTIES: WritableSlice<ClassDescriptor, SerializableProperties> = Slices.createSimpleSlice() internal val SERIALIZABLE_PROPERTIES: WritableSlice<ClassDescriptor, SerializableProperties> = Slices.createSimpleSlice()
open class SerializationPluginDeclarationChecker : DeclarationChecker { open class SerializationPluginDeclarationChecker : DeclarationChecker {
private var useLegacyEnumSerializerCached: Boolean? = null
final override fun check(declaration: KtDeclaration, descriptor: DeclarationDescriptor, context: DeclarationCheckerContext) { final override fun check(declaration: KtDeclaration, descriptor: DeclarationDescriptor, context: DeclarationCheckerContext) {
if (descriptor !is ClassDescriptor) return if (descriptor !is ClassDescriptor) return
@@ -139,8 +141,13 @@ open class SerializationPluginDeclarationChecker : DeclarationChecker {
} }
} }
private fun ClassDescriptor.useLegacyGeneratedEnumSerializer(): Boolean {
return useLegacyEnumSerializerCached ?: useGeneratedEnumSerializer.also { useLegacyEnumSerializerCached = it }
}
private fun canBeSerializedInternally(descriptor: ClassDescriptor, declaration: KtDeclaration, trace: BindingTrace): Boolean { private fun canBeSerializedInternally(descriptor: ClassDescriptor, declaration: KtDeclaration, trace: BindingTrace): Boolean {
if (descriptor.isSerializableEnumWithMissingSerializer()) { // if enum has meta or SerialInfo annotation on a class or entries and used plugin-generated serializer
if (descriptor.isSerializableEnumWithMissingSerializer() && descriptor.useLegacyGeneratedEnumSerializer()) {
val declarationToReport = declaration.modifierList ?: declaration val declarationToReport = declaration.modifierList ?: declaration
trace.report(SerializationErrors.EXPLICIT_SERIALIZABLE_IS_REQUIRED.on(declarationToReport)) trace.report(SerializationErrors.EXPLICIT_SERIALIZABLE_IS_REQUIRED.on(declarationToReport))
return false return false
@@ -187,7 +194,7 @@ open class SerializationPluginDeclarationChecker : DeclarationChecker {
} }
// check that we can instantiate supertype // check that we can instantiate supertype
if (!descriptor.isSerializableEnum()) { // enums are inherited from java.lang.Enum and can't be inherited from other classes if (descriptor.kind != ClassKind.ENUM_CLASS) { // enums are inherited from java.lang.Enum and can't be inherited from other classes
val superClass = descriptor.getSuperClassOrAny() val superClass = descriptor.getSuperClassOrAny()
if (!superClass.isInternalSerializable && superClass.constructors.singleOrNull { it.valueParameters.size == 0 } == null) { if (!superClass.isInternalSerializable && superClass.constructors.singleOrNull { it.valueParameters.size == 0 } == null) {
trace.reportOnSerializableOrMetaAnnotation(descriptor, SerializationErrors.NON_SERIALIZABLE_PARENT_MUST_HAVE_NOARG_CTOR) trace.reportOnSerializableOrMetaAnnotation(descriptor, SerializationErrors.NON_SERIALIZABLE_PARENT_MUST_HAVE_NOARG_CTOR)
@@ -429,4 +436,4 @@ open class SerializationPluginDeclarationChecker : DeclarationChecker {
} }
internal val ClassDescriptor.serializableAnnotationIsUseless: Boolean internal val ClassDescriptor.serializableAnnotationIsUseless: Boolean
get() = hasSerializableOrMetaAnnotationWithoutArgs && !isInternalSerializable && !hasCompanionObjectAsSerializer && !isSerializableEnum() && !isSealedSerializableInterface get() = hasSerializableOrMetaAnnotationWithoutArgs && !isInternalSerializable && !hasCompanionObjectAsSerializer && kind != ClassKind.ENUM_CLASS && !isSealedSerializableInterface
@@ -23,6 +23,8 @@ import org.jetbrains.kotlin.resolve.scopes.getDescriptorsFiltered
import org.jetbrains.kotlin.types.* import org.jetbrains.kotlin.types.*
import org.jetbrains.kotlin.types.typeUtil.isSubtypeOf import org.jetbrains.kotlin.types.typeUtil.isSubtypeOf
import org.jetbrains.kotlin.types.typeUtil.representativeUpperBound import org.jetbrains.kotlin.types.typeUtil.representativeUpperBound
import org.jetbrains.kotlinx.serialization.compiler.resolve.SerialEntityNames.ENUM_SERIALIZER_FACTORY_FUNC_NAME
import org.jetbrains.kotlinx.serialization.compiler.resolve.SerialEntityNames.MARKED_ENUM_SERIALIZER_FACTORY_FUNC_NAME
import org.jetbrains.kotlinx.serialization.compiler.resolve.SerializationAnnotations.inheritableSerialInfoFqName import org.jetbrains.kotlinx.serialization.compiler.resolve.SerializationAnnotations.inheritableSerialInfoFqName
import org.jetbrains.kotlinx.serialization.compiler.resolve.SerializationAnnotations.metaSerializableAnnotationFqName import org.jetbrains.kotlinx.serialization.compiler.resolve.SerializationAnnotations.metaSerializableAnnotationFqName
import org.jetbrains.kotlinx.serialization.compiler.resolve.SerializationAnnotations.serialInfoFqName import org.jetbrains.kotlinx.serialization.compiler.resolve.SerializationAnnotations.serialInfoFqName
@@ -124,11 +126,20 @@ internal val ClassDescriptor.isInternalSerializable: Boolean //todo normal check
internal fun ClassDescriptor.isSerializableEnum(): Boolean = kind == ClassKind.ENUM_CLASS && hasSerializableOrMetaAnnotation internal fun ClassDescriptor.isSerializableEnum(): Boolean = kind == ClassKind.ENUM_CLASS && hasSerializableOrMetaAnnotation
internal fun ClassDescriptor.isEnumWithLegacyGeneratedSerializer(): Boolean = isInternallySerializableEnum() && useGeneratedEnumSerializer
internal fun ClassDescriptor.isInternallySerializableEnum(): Boolean = internal fun ClassDescriptor.isInternallySerializableEnum(): Boolean =
kind == ClassKind.ENUM_CLASS && hasSerializableOrMetaAnnotationWithoutArgs kind == ClassKind.ENUM_CLASS && hasSerializableOrMetaAnnotationWithoutArgs
internal val ClassDescriptor.shouldHaveGeneratedSerializer: Boolean internal val ClassDescriptor.shouldHaveGeneratedSerializer: Boolean
get() = (isInternalSerializable && (modality == Modality.FINAL || modality == Modality.OPEN)) || isInternallySerializableEnum() get() = (isInternalSerializable && (modality == Modality.FINAL || modality == Modality.OPEN))
|| isEnumWithLegacyGeneratedSerializer()
internal val ClassDescriptor.useGeneratedEnumSerializer: Boolean
get() {
val functions = module.getPackage(SerializationPackages.internalPackageFqName).memberScope.getFunctionNames()
return !functions.contains(ENUM_SERIALIZER_FACTORY_FUNC_NAME) || !functions.contains(MARKED_ENUM_SERIALIZER_FACTORY_FUNC_NAME)
}
internal fun ClassDescriptor.enumEntries(): List<ClassDescriptor> { internal fun ClassDescriptor.enumEntries(): List<ClassDescriptor> {
check(this.kind == ClassKind.ENUM_CLASS) check(this.kind == ClassKind.ENUM_CLASS)
@@ -138,6 +149,13 @@ internal fun ClassDescriptor.enumEntries(): List<ClassDescriptor> {
.toList() .toList()
} }
// check enum or its elements has any SerialInfo annotation
internal fun ClassDescriptor.isEnumWithSerialInfoAnnotation(): Boolean {
if (kind != ClassKind.ENUM_CLASS) return false
if (annotations.hasAnySerialAnnotation) return true
return enumEntries().any { (it.annotations.hasAnySerialAnnotation) }
}
internal val Annotations.hasAnySerialAnnotation: Boolean internal val Annotations.hasAnySerialAnnotation: Boolean
get() = serialNameValue != null || any { it.annotationClass?.isSerialInfoAnnotation == true } get() = serialNameValue != null || any { it.annotationClass?.isSerialInfoAnnotation == true }
@@ -242,6 +260,7 @@ internal fun ClassDescriptor.needSerializerFactory(): Boolean {
if (!(this.platform?.isNative() == true || this.platform.isJs())) return false if (!(this.platform?.isNative() == true || this.platform.isJs())) return false
val serializableClass = getSerializableClassDescriptorByCompanion(this) ?: return false val serializableClass = getSerializableClassDescriptorByCompanion(this) ?: return false
if (serializableClass.isSerializableObject) return true if (serializableClass.isSerializableObject) return true
if (serializableClass.isSerializableEnum()) return true
if (serializableClass.isAbstractOrSealedSerializableClass()) return true if (serializableClass.isAbstractOrSealedSerializableClass()) return true
if (serializableClass.isSealedSerializableInterface) return true if (serializableClass.isSealedSerializableInterface) return true
if (serializableClass.declaredTypeParameters.isEmpty()) return false if (serializableClass.declaredTypeParameters.isEmpty()) return false
@@ -78,6 +78,7 @@ object SerialEntityNames {
const val SERIAL_DESCRIPTOR_FOR_INLINE = "InlineClassDescriptor" const val SERIAL_DESCRIPTOR_FOR_INLINE = "InlineClassDescriptor"
const val PLUGIN_EXCEPTIONS_FILE = "PluginExceptions" const val PLUGIN_EXCEPTIONS_FILE = "PluginExceptions"
const val ENUMS_FILE = "Enums"
//exceptions //exceptions
const val SERIAL_EXC = "SerializationException" const val SERIAL_EXC = "SerializationException"
@@ -94,11 +95,16 @@ object SerialEntityNames {
val SERIALIZER_PROVIDER_NAME = Name.identifier("serializer") val SERIALIZER_PROVIDER_NAME = Name.identifier("serializer")
val SINGLE_MASK_FIELD_MISSING_FUNC_NAME = Name.identifier("throwMissingFieldException") val SINGLE_MASK_FIELD_MISSING_FUNC_NAME = Name.identifier("throwMissingFieldException")
val ARRAY_MASK_FIELD_MISSING_FUNC_NAME = Name.identifier("throwArrayMissingFieldException") val ARRAY_MASK_FIELD_MISSING_FUNC_NAME = Name.identifier("throwArrayMissingFieldException")
val ENUM_SERIALIZER_FACTORY_FUNC_NAME = Name.identifier("createSimpleEnumSerializer")
val MARKED_ENUM_SERIALIZER_FACTORY_FUNC_NAME = Name.identifier("createMarkedEnumSerializer")
val SINGLE_MASK_FIELD_MISSING_FUNC_FQ = SerializationPackages.internalPackageFqName.child(SINGLE_MASK_FIELD_MISSING_FUNC_NAME) val SINGLE_MASK_FIELD_MISSING_FUNC_FQ = SerializationPackages.internalPackageFqName.child(SINGLE_MASK_FIELD_MISSING_FUNC_NAME)
val ARRAY_MASK_FIELD_MISSING_FUNC_FQ = SerializationPackages.internalPackageFqName.child(ARRAY_MASK_FIELD_MISSING_FUNC_NAME) val ARRAY_MASK_FIELD_MISSING_FUNC_FQ = SerializationPackages.internalPackageFqName.child(ARRAY_MASK_FIELD_MISSING_FUNC_NAME)
val CACHED_SERIALIZER_PROPERTY_NAME = Name.identifier(CACHED_SERIALIZER_PROPERTY) val CACHED_SERIALIZER_PROPERTY_NAME = Name.identifier(CACHED_SERIALIZER_PROPERTY)
val CACHED_DESCRIPTOR_FIELD_NAME = Name.identifier(CACHED_DESCRIPTOR_FIELD) val CACHED_DESCRIPTOR_FIELD_NAME = Name.identifier(CACHED_DESCRIPTOR_FIELD)
val ENUM_SERIALIZER_FACTORY_FUNC_FQ = SerializationPackages.internalPackageFqName.child(ENUM_SERIALIZER_FACTORY_FUNC_NAME)
val MARKED_ENUM_SERIALIZER_FACTORY_FUNC_FQ = SerializationPackages.internalPackageFqName.child(MARKED_ENUM_SERIALIZER_FACTORY_FUNC_NAME)
// parameters // parameters
val dummyParamName = Name.identifier("serializationConstructorMarker") val dummyParamName = Name.identifier("serializationConstructorMarker")
internal const val typeArgPrefix = "typeSerial" internal const val typeArgPrefix = "typeSerial"