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"
)
if (serializableDescriptor.isSerializableObject || serializableDescriptor.isAbstractOrSealedSerializableClass()) {
if (serializableDescriptor.isSerializableObject
|| serializableDescriptor.isAbstractOrSealedSerializableClass()
|| serializableDescriptor.isSerializableEnum()
) {
generateLazySerializerGetter(serializerGetterDescriptor)
} else {
generateSerializerGetter(serializerGetterDescriptor)
@@ -225,7 +225,7 @@ fun findStandardKotlinTypeSerializer(module: ModuleDescriptor, kType: KotlinType
fun findEnumTypeSerializer(module: ModuleDescriptor, kType: KotlinType): ClassDescriptor? {
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)
else null
}
@@ -52,6 +52,12 @@ interface IrBuilderExtension {
private val throwMissedFieldExceptionArrayFunc
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? {
return declarations.singleOrNull { it.descriptor == descriptor } as? T
}
@@ -1041,13 +1047,55 @@ interface IrBuilderExtension {
}
enumSerializerId -> {
serializerClass = module.getClassFromInternalSerializationPackage(SpecialBuiltins.enumSerializer)
args = kType.toClassDescriptor!!.let { enumDesc ->
listOf(
irString(enumDesc.serialName()),
irCall(findEnumValuesMethod(enumDesc))
)
}
val enumDescriptor = kType.toClassDescriptor!!
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 -> {
args = kType.arguments.map {
@@ -566,7 +566,7 @@ open class SerializerIrGenerator(
) {
val serializableDesc = getSerializableClassDescriptorBySerializer(irClass.symbol.descriptor) ?: return
val generator = when {
serializableDesc.isInternallySerializableEnum() -> SerializerForEnumsGenerator(irClass, context, bindingContext, serialInfoJvmGenerator)
serializableDesc.isEnumWithLegacyGeneratedSerializer() -> SerializerForEnumsGenerator(irClass, context, bindingContext, serialInfoJvmGenerator)
serializableDesc.isInlineClass() -> SerializerForInlineClassGenerator(irClass, context, bindingContext, 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.expression.ExpressionVisitor
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.utils.JsAstUtils
import org.jetbrains.kotlin.psi.KtExpression
@@ -165,18 +166,65 @@ internal fun AbstractSerialGenerator.serializerInstance(
serializerClass.classId == contextSerializerId || serializerClass.classId == polymorphicSerializerId -> listOf(
ExpressionVisitor.getObjectKClass(context, kType.toClassDescriptor!!)
)
serializerClass.classId == enumSerializerId -> listOf(
JsStringLiteral(kType.serialName()),
// EnumClass.values() invocation
JsInvocation(
context.getInnerNameForDescriptor(
DescriptorUtils.getFunctionByName(
kType.toClassDescriptor!!.staticScope,
StandardNames.ENUM_VALUES
)
).makeRef()
serializerClass.classId == enumSerializerId -> {
val enumDescriptor = kType.toClassDescriptor!!
val enumArgs = mutableListOf(
JsStringLiteral(enumDescriptor.serialName()),
// EnumClass.values() invocation
JsInvocation(
context.getInnerNameForDescriptor(
DescriptorUtils.getFunctionByName(
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(
JsStringLiteral(kType.serialName()),
context.serializerObjectGetter(kType.toClassDescriptor!!)
@@ -373,7 +373,7 @@ open class SerializerJsTranslator(
metadataPlugin: SerializationDescriptorSerializerPlugin?
) {
val serializableDesc = getSerializableClassDescriptorBySerializer(descriptor) ?: return
if (serializableDesc.isInternallySerializableEnum()) {
if (serializableDesc.isEnumWithLegacyGeneratedSerializer()) {
SerializerForEnumsTranslator(descriptor, translator, context).generate()
} else {
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.name.ClassId
import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.psi.ValueArgument
import org.jetbrains.kotlin.resolve.DescriptorFactory
import org.jetbrains.kotlin.resolve.descriptorUtil.builtIns
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.SerialEntityNames.DECODER_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.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.PLUGIN_EXCEPTIONS_FILE
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 decoderType = Type.getObjectType("kotlinx/serialization/encoding/$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 enumFactoriesType = Type.getObjectType("kotlinx/serialization/internal/${ENUMS_FILE}Kt")
internal val jvmLambdaType = Type.getObjectType("kotlin/jvm/internal/Lambda")
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 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 getLazyValueName = JvmAbi.getterName("value")
@@ -114,8 +126,8 @@ fun InstructionAdapter.genKOutputMethodCall(
) {
val propertyType = classCodegen.typeMapper.mapType(property.type)
val sti = generator.getSerialTypeInfo(property, propertyType)
val useSerializer = if (fromClassStartVar == null) stackValueSerializerInstanceFromSerializer(classCodegen, sti, generator)
else stackValueSerializerInstanceFromClass(classCodegen, sti, fromClassStartVar, generator)
val useSerializer = if (fromClassStartVar == null) stackValueSerializerInstanceFromSerializer(expressionCodegen, classCodegen, sti, generator)
else stackValueSerializerInstanceFromClass(expressionCodegen, classCodegen, sti, fromClassStartVar, generator)
val actualType = ImplementationBodyCodegen.genPropertyOnStack(
this,
expressionCodegen.context,
@@ -179,14 +191,16 @@ internal val contextSerializerId = ClassId(packageFqName, Name.identifier(Specia
internal fun InstructionAdapter.stackValueSerializerInstanceFromClass(
codegen: ClassBodyCodegen,
expressionCodegen: ExpressionCodegen,
classCodegen: ClassBodyCodegen,
sti: JVMSerialTypeInfo,
varIndexStart: Int,
serializerCodegen: AbstractSerialGenerator
): Boolean {
val serializer = sti.serializer
return serializerCodegen.stackValueSerializerInstance(
codegen,
expressionCodegen,
classCodegen,
sti.property.module,
sti.property.type,
serializer,
@@ -198,6 +212,7 @@ internal fun InstructionAdapter.stackValueSerializerInstanceFromClass(
}
internal fun InstructionAdapter.stackValueSerializerInstanceFromSerializerWithoutSti(
expressionCodegen: ExpressionCodegen,
codegen: ClassBodyCodegen,
property: SerializableProperty,
serializerCodegen: AbstractSerialGenerator
@@ -210,6 +225,7 @@ internal fun InstructionAdapter.stackValueSerializerInstanceFromSerializerWithou
property.descriptor.findPsi()
) else null
return serializerCodegen.stackValueSerializerInstance(
expressionCodegen,
codegen,
property.module,
property.type,
@@ -222,9 +238,14 @@ internal fun InstructionAdapter.stackValueSerializerInstanceFromSerializerWithou
}.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(
codegen, sti.property.module, sti.property.type,
expressionCodegen, codegen, sti.property.module, sti.property.type,
sti.serializer, this, sti.property.genericIndex
) { idx, _ ->
load(0, kSerializerType)
@@ -234,7 +255,7 @@ internal fun InstructionAdapter.stackValueSerializerInstanceFromSerializer(codeg
// returns false is cannot not use serializer
// 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?,
genericIndex: Int? = null,
genericSerializerFieldGetter: (InstructionAdapter.(Int, KotlinType) -> Unit)? = null
@@ -248,7 +269,7 @@ internal fun AbstractSerialGenerator.stackValueSerializerInstance(codegen: Class
if (serializer.kind == ClassKind.OBJECT) {
// singleton serializer -- just get it
if (iv != null)
StackValue.singleton(serializer, codegen.typeMapper).put(kSerializerType, iv)
StackValue.singleton(serializer, classCodegen.typeMapper).put(kSerializerType, iv)
return true
}
// 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
val argType = projection.type
val argSerializer = if (argType.isTypeParameter()) null else {
findTypeSerializerOrContext(module, argType, sourceElement = codegen.descriptor.findPsi())
findTypeSerializerOrContext(module, argType, sourceElement = classCodegen.descriptor.findPsi())
?: return false
}
// check if it can be properly serialized with its args recursively
if (!stackValueSerializerInstance(
codegen,
expressionCodegen,
classCodegen,
module,
argType,
argSerializer,
@@ -275,7 +297,65 @@ internal fun AbstractSerialGenerator.stackValueSerializerInstance(codegen: Class
}
// new serializer if needed
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
anew(serializerType)
dup()
@@ -286,7 +366,8 @@ internal fun AbstractSerialGenerator.stackValueSerializerInstance(codegen: Class
val (argType, argSerializer) = typeArgument
assert(
stackValueSerializerInstance(
codegen,
expressionCodegen,
classCodegen,
module,
argType,
argSerializer,
@@ -303,9 +384,10 @@ internal fun AbstractSerialGenerator.stackValueSerializerInstance(codegen: Class
val serialName = kType.serialName()
when (serializer.classId) {
enumSerializerId -> {
// support legacy serializer instantiation by constructor for old runtimes
aconst(serialName)
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;")
invokestatic(enumJavaType.internalName, "values","()[${enumJavaType.descriptor}", false)
checkcast(javaEnumArray)
@@ -314,7 +396,7 @@ internal fun AbstractSerialGenerator.stackValueSerializerInstance(codegen: Class
contextSerializerId, polymorphicSerializerId -> {
// a special way to instantiate enum -- need a enum KClass reference
// 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)
signature.append(AsmTypes.K_CLASS_TYPE.descriptor)
if (serializer.classId == contextSerializerId && serializer.constructors.any { it.valueParameters.size == 3 }) {
@@ -334,7 +416,7 @@ internal fun AbstractSerialGenerator.stackValueSerializerInstance(codegen: Class
}
referenceArraySerializerId -> {
// 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)
signature.append(AsmTypes.K_CLASS_TYPE.descriptor)
// Reference array serializer still needs serializer for its argument type
@@ -343,13 +425,13 @@ internal fun AbstractSerialGenerator.stackValueSerializerInstance(codegen: Class
sealedSerializerId -> {
aconst(serialName)
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)
signature.append(AsmTypes.K_CLASS_TYPE.descriptor)
val (subClasses, subSerializers) = allSealedSerializableSubclassesFor(kType.toClassDescriptor!!, module)
// KClasses vararg
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)
}
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
assert(
stackValueSerializerInstance(
codegen,
expressionCodegen,
classCodegen,
module,
argType,
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
assert(
stackValueSerializerInstance(
codegen,
expressionCodegen,
classCodegen,
module,
(genericType.constructor.declarationDescriptor as TypeParameterDescriptor).representativeUpperBound,
module.getClassFromSerializationPackage(SpecialBuiltins.polymorphicSerializer),
@@ -384,7 +468,7 @@ internal fun AbstractSerialGenerator.stackValueSerializerInstance(codegen: Class
objectSerializerId -> {
aconst(serialName)
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;")
}
// all serializers get arguments with serializers of their generic types
@@ -397,6 +481,29 @@ internal fun AbstractSerialGenerator.stackValueSerializerInstance(codegen: Class
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() =
invokestatic(
"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) {
iconst(args.size)
newarray(type)
args.forEachIndexed { i, serializer ->
args.forEachIndexed { i, arg ->
dup()
iconst(i)
onEach(i, serializer)
onEach(i, arg)
astore(type)
}
}
@@ -510,7 +617,7 @@ internal fun createSingletonLambda(
lambdaName: String,
outerClassCodegen: ImplementationBodyCodegen,
resultSimpleType: SimpleType,
block: InstructionAdapter.(ImplementationBodyCodegen) -> Unit
block: InstructionAdapter.(ImplementationBodyCodegen, ExpressionCodegen) -> Unit
): Type {
val lambdaType = Type.getObjectType("${outerClassCodegen.className}\$$lambdaName")
@@ -631,8 +738,8 @@ internal fun createSingletonLambda(
DescriptorVisibilities.PUBLIC
)
lambdaCodegen.generateMethod(invokeFunction) { _, _ ->
block(lambdaCodegen)
lambdaCodegen.generateMethod(invokeFunction) { _, expressionCodegen ->
block(lambdaCodegen, expressionCodegen)
}
val bridgeInvokeFunction = SimpleFunctionDescriptorImpl.create(
@@ -103,7 +103,15 @@ class SerializableCodegenImpl(
superTypeArguments.forEach {
val genericIdx = serializableDescriptor.defaultType.arguments.indexOf(it).let { if (it == -1) null else it }
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)
}
}
@@ -57,7 +57,11 @@ class SerializableCompanionCodegenImpl(private val classCodegen: ImplementationB
// create singleton lambda class
val lambdaType =
createSingletonLambda("serializer\$1", classCodegen, companionDescriptor.getKSerializer().defaultType) { lambdaCodegen ->
createSingletonLambda(
"serializer\$1",
classCodegen,
companionDescriptor.getKSerializer().defaultType
) { lambdaCodegen, expressionCodegen ->
val serializerDescriptor = requireNotNull(
findTypeSerializer(
serializableDescriptor.module,
@@ -65,6 +69,7 @@ class SerializableCompanionCodegenImpl(private val classCodegen: ImplementationB
)
)
stackValueSerializerInstance(
expressionCodegen,
lambdaCodegen,
serializableDescriptor.module,
serializableDescriptor.defaultType,
@@ -106,8 +111,9 @@ class SerializableCompanionCodegenImpl(private val classCodegen: ImplementationB
serializableDescriptor.toSimpleType()
)
)
classCodegen.generateMethod(methodDescriptor) { _, _ ->
classCodegen.generateMethod(methodDescriptor) { _, expressionCodegen ->
stackValueSerializerInstance(
expressionCodegen,
classCodegen,
serializableDescriptor.module,
serializableDescriptor.defaultType,
@@ -8,7 +8,6 @@ package org.jetbrains.kotlinx.serialization.compiler.backend.jvm
import org.jetbrains.kotlin.codegen.*
import org.jetbrains.kotlin.descriptors.*
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.diagnostics.OtherOrigin
import org.jetbrains.kotlinx.serialization.compiler.backend.common.SerializerCodegen
@@ -37,7 +36,7 @@ open class SerializerCodegenImpl(
companion object {
fun generateSerializerExtensions(codegen: ImplementationBodyCodegen, metadataPlugin: SerializationDescriptorSerializerPlugin?) {
val serializableClass = getSerializableClassDescriptorBySerializer(codegen.descriptor) ?: return
val serializerCodegen = if (serializableClass.isInternallySerializableEnum()) {
val serializerCodegen = if (serializableClass.isEnumWithLegacyGeneratedSerializer()) {
SerializerForEnumsCodegen(codegen, serializableClass)
} else {
SerializerCodegenImpl(codegen, serializableClass, metadataPlugin)
@@ -144,30 +143,6 @@ open class SerializerCodegenImpl(
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
protected fun InstructionAdapter.stackSerialClassDesc(classDescVar: Int?) {
if (staticDescriptor)
@@ -192,7 +167,7 @@ open class SerializerCodegenImpl(
}
override fun generateChildSerializersGetter(function: FunctionDescriptor) {
codegen.generateMethod(function) { _, _ ->
codegen.generateMethod(function) { _, expressionCodegen ->
val size = serializableProperties.size
iconst(size)
newarray(kSerializerType)
@@ -202,6 +177,7 @@ open class SerializerCodegenImpl(
val prop = serializableProperties[i]
assert(
stackValueSerializerInstanceFromSerializerWithoutSti(
expressionCodegen,
codegen,
prop,
this@SerializerCodegenImpl
@@ -335,7 +311,7 @@ open class SerializerCodegenImpl(
propVar = propsStartVar
for ((index, property) in serializableProperties.withIndex()) {
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
}
// set all bit masks to true
@@ -373,7 +349,7 @@ open class SerializerCodegenImpl(
if (!property.transient) {
// labelI:
visitLabel(labels[labelNum + 1])
callReadProperty(property, propertyType, index, inputVar, descVar, propVar)
callReadProperty(expressionCodegen, property, propertyType, index, inputVar, descVar, propVar)
// mark read bit in mask
// bitMask = bitMask | 1 << index
@@ -449,6 +425,7 @@ open class SerializerCodegenImpl(
}
private fun InstructionAdapter.callReadProperty(
expressionCodegen: ExpressionCodegen,
property: SerializableProperty,
propertyType: Type,
index: Int,
@@ -462,7 +439,7 @@ open class SerializerCodegenImpl(
iconst(index)
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())
if (unknownSer) {
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()
open class SerializationPluginDeclarationChecker : DeclarationChecker {
private var useLegacyEnumSerializerCached: Boolean? = null
final override fun check(declaration: KtDeclaration, descriptor: DeclarationDescriptor, context: DeclarationCheckerContext) {
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 {
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
trace.report(SerializationErrors.EXPLICIT_SERIALIZABLE_IS_REQUIRED.on(declarationToReport))
return false
@@ -187,7 +194,7 @@ open class SerializationPluginDeclarationChecker : DeclarationChecker {
}
// 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()
if (!superClass.isInternalSerializable && superClass.constructors.singleOrNull { it.valueParameters.size == 0 } == null) {
trace.reportOnSerializableOrMetaAnnotation(descriptor, SerializationErrors.NON_SERIALIZABLE_PARENT_MUST_HAVE_NOARG_CTOR)
@@ -429,4 +436,4 @@ open class SerializationPluginDeclarationChecker : DeclarationChecker {
}
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.typeUtil.isSubtypeOf
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.metaSerializableAnnotationFqName
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.isEnumWithLegacyGeneratedSerializer(): Boolean = isInternallySerializableEnum() && useGeneratedEnumSerializer
internal fun ClassDescriptor.isInternallySerializableEnum(): Boolean =
kind == ClassKind.ENUM_CLASS && hasSerializableOrMetaAnnotationWithoutArgs
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> {
check(this.kind == ClassKind.ENUM_CLASS)
@@ -138,6 +149,13 @@ internal fun ClassDescriptor.enumEntries(): List<ClassDescriptor> {
.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
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
val serializableClass = getSerializableClassDescriptorByCompanion(this) ?: return false
if (serializableClass.isSerializableObject) return true
if (serializableClass.isSerializableEnum()) return true
if (serializableClass.isAbstractOrSealedSerializableClass()) return true
if (serializableClass.isSealedSerializableInterface) return true
if (serializableClass.declaredTypeParameters.isEmpty()) return false
@@ -78,6 +78,7 @@ object SerialEntityNames {
const val SERIAL_DESCRIPTOR_FOR_INLINE = "InlineClassDescriptor"
const val PLUGIN_EXCEPTIONS_FILE = "PluginExceptions"
const val ENUMS_FILE = "Enums"
//exceptions
const val SERIAL_EXC = "SerializationException"
@@ -94,11 +95,16 @@ object SerialEntityNames {
val SERIALIZER_PROVIDER_NAME = Name.identifier("serializer")
val SINGLE_MASK_FIELD_MISSING_FUNC_NAME = Name.identifier("throwMissingFieldException")
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 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_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
val dummyParamName = Name.identifier("serializationConstructorMarker")
internal const val typeArgPrefix = "typeSerial"
@@ -21,4 +21,4 @@ object EnumSerializer: KSerializer<ExplicitlyMarkedEnumCustom> {
}
@Serializable
data class EnumUsage(val s: SimpleEnum, val m: MarkedNameEnum, val e: ExplicitlyMarkedEnum)
data class EnumUsage(val s: SimpleEnum, val m: MarkedNameEnum, val e: ExplicitlyMarkedEnum)