From 8eeb9d4faab68b4821bbad62ae38a59b03f16930 Mon Sep 17 00:00:00 2001 From: Leonid Startsev Date: Mon, 28 Jan 2019 17:19:17 +0300 Subject: [PATCH] [kx.serialization] Fix serializer resolving for file-level annotations: Instantiating ContextSerializer for primitives with boxing (fixes https://github.com/Kotlin/kotlinx.serialization/issues/349) Unwrap nullable type when looking for general-defined serializer (fixes https://github.com/Kotlin/kotlinx.serialization/issues/351) Add lost java collection types and fix annotation name (fixes https://github.com/Kotlin/kotlinx.serialization/issues/343) --- .../compiler/backend/common/TypeUtil.kt | 36 ++++++++++++++----- .../compiler/backend/jvm/JVMCodegenUtil.kt | 5 +-- 2 files changed, 30 insertions(+), 11 deletions(-) diff --git a/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/common/TypeUtil.kt b/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/common/TypeUtil.kt index fd8ae98359a..323ca0c433a 100644 --- a/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/common/TypeUtil.kt +++ b/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/common/TypeUtil.kt @@ -32,10 +32,7 @@ import org.jetbrains.kotlin.psi.KtPureClassOrObject import org.jetbrains.kotlin.resolve.BindingContext import org.jetbrains.kotlin.resolve.descriptorUtil.getSuperClassNotAny import org.jetbrains.kotlin.types.KotlinType -import org.jetbrains.kotlin.types.typeUtil.containsTypeProjectionsInTopLevelArguments -import org.jetbrains.kotlin.types.typeUtil.isBoolean -import org.jetbrains.kotlin.types.typeUtil.isPrimitiveNumberType -import org.jetbrains.kotlin.types.typeUtil.isTypeParameter +import org.jetbrains.kotlin.types.typeUtil.* import org.jetbrains.kotlinx.serialization.compiler.backend.jvm.enumSerializerId import org.jetbrains.kotlinx.serialization.compiler.backend.jvm.polymorphicSerializerId import org.jetbrains.kotlinx.serialization.compiler.backend.jvm.referenceArraySerializerId @@ -49,8 +46,13 @@ open class SerialTypeInfo( val unit: Boolean = false ) -fun AbstractSerialGenerator.findAddOnSerializer(propertyType: KotlinType): ClassDescriptor? = - additionalSerializersInScopeOfCurrentFile[propertyType] +fun AbstractSerialGenerator.findAddOnSerializer(propertyType: KotlinType, module: ModuleDescriptor): ClassDescriptor? { + additionalSerializersInScopeOfCurrentFile[propertyType]?.let { return it } + if (propertyType in contextualKClassListInCurrentFile) + return module.getClassFromSerializationPackage(SpecialBuiltins.contextSerializer) + if (propertyType.isMarkedNullable) return findAddOnSerializer(propertyType.makeNotNullable(), module) + return null +} @Suppress("FunctionName", "LocalVariableName") fun AbstractSerialGenerator.getSerialTypeInfo(property: SerializableProperty): SerialTypeInfo { @@ -59,8 +61,8 @@ fun AbstractSerialGenerator.getSerialTypeInfo(property: SerializableProperty): S val T = property.type property.serializableWith?.toClassDescriptor?.let { return SerializableInfo(it) } + findAddOnSerializer(T, property.module)?.let { return SerializableInfo(it) } T.overridenSerializer?.toClassDescriptor?.let { return SerializableInfo(it) } - findAddOnSerializer(T)?.let { return SerializableInfo(it) } return when { T.isTypeParameter() -> SerialTypeInfo(property, if (property.type.isMarkedNullable) "Nullable" else "", null) T.isPrimitiveNumberType() or T.isBoolean() -> SerialTypeInfo( @@ -95,8 +97,9 @@ fun AbstractSerialGenerator.findTypeSerializerOrContext( annotations: Annotations = kType.annotations, sourceElement: PsiElement? = null ): ClassDescriptor? { - additionalSerializersInScopeOfCurrentFile[kType]?.let { return it } if (kType.isTypeParameter()) return null + if (kType.isMarkedNullable) return findTypeSerializerOrContext(module, kType.makeNotNullable(), annotations, sourceElement) + additionalSerializersInScopeOfCurrentFile[kType]?.let { return it } fun getContextualSerializer() = if (annotations.hasAnnotation(SerializationAnnotations.contextualFqName) || kType in contextualKClassListInCurrentFile) module.getClassFromSerializationPackage(SpecialBuiltins.contextSerializer) @@ -104,7 +107,7 @@ fun AbstractSerialGenerator.findTypeSerializerOrContext( null return getContextualSerializer() ?: findTypeSerializer(module, kType) ?: throw CompilationException( "Serializer for element of type $kType has not been found.\n" + - "To use context serializer as fallback, explicitly annotate element with @ContextualSerializer", + "To use context serializer as fallback, explicitly annotate element with @ContextualSerialization", null, sourceElement ) @@ -142,6 +145,21 @@ fun findStandardKotlinTypeSerializer(module: ModuleDescriptor, kType: KotlinType "kotlin.collections.Map", "kotlin.collections.LinkedHashMap", "kotlin.collections.MutableMap" -> "LinkedHashMapSerializer" "kotlin.collections.HashMap" -> "HashMapSerializer" "kotlin.collections.Map.Entry" -> "MapEntrySerializer" + "java.lang.Boolean" -> "BooleanSerializer" + "java.lang.Byte" -> "ByteSerializer" + "java.lang.Short" -> "ShortSerializer" + "java.lang.Integer" -> "IntSerializer" + "java.lang.Long" -> "LongSerializer" + "java.lang.Float" -> "FloatSerializer" + "java.lang.Double" -> "DoubleSerializer" + "java.lang.Character" -> "CharSerializer" + "java.lang.String" -> "StringSerializer" + "java.util.Collection", "java.util.List", "java.util.ArrayList" -> "ArrayListSerializer" + "java.util.Set", "java.util.LinkedHashSet" -> "LinkedHashSetSerializer" + "java.util.HashSet" -> "HashSetSerializer" + "java.util.Map", "java.util.LinkedHashMap" -> "LinkedHashMapSerializer" + "java.util.HashMap" -> "HashMapSerializer" + "java.util.Map.Entry" -> "MapEntrySerializer" else -> return null } return module.findClassAcrossModuleDependencies(ClassId(internalPackageFqName, Name.identifier(name))) diff --git a/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/jvm/JVMCodegenUtil.kt b/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/jvm/JVMCodegenUtil.kt index e806c27abe7..d22fe021d9b 100644 --- a/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/jvm/JVMCodegenUtil.kt +++ b/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/jvm/JVMCodegenUtil.kt @@ -256,7 +256,8 @@ internal fun AbstractSerialGenerator.stackValueSerializerInstance(codegen: Class when (serializer.classId) { enumSerializerId, contextSerializerId -> { // a special way to instantiate enum -- need a enum KClass reference - aconst(codegen.typeMapper.mapType(kType)) + // GENERIC_ARGUMENT forces boxing in order to obtain KClass + aconst(codegen.typeMapper.mapType(kType, null, TypeMappingMode.GENERIC_ARGUMENT)) AsmUtil.wrapJavaClassIntoKClass(this) signature.append(AsmTypes.K_CLASS_TYPE.descriptor) } @@ -320,8 +321,8 @@ fun AbstractSerialGenerator.getSerialTypeInfo(property: SerializableProperty, ty ) property.serializableWith?.toClassDescriptor?.let { return SerializableInfo(it) } + findAddOnSerializer(property.type, property.module)?.let { return SerializableInfo(it) } property.type.overridenSerializer?.toClassDescriptor?.let { return SerializableInfo(it) } - findAddOnSerializer(property.type)?.let { return SerializableInfo(it) } if (property.type.isTypeParameter()) return JVMSerialTypeInfo( property,