While creating descriptors, add type arguments not from serializable class definition
but from actual KSerializer implementation. This provides better support for user-defined or external generic serializers Fix order of resolving serializers: user-overriden should go before polymorphic and default Don't generate additional constructor if @SerialInfo has no properties Workaround for recursive resolve on @Serializable(with) and @Serializer(for) pair annotations Don't trigger the whole resolve if we need just to check that there is an argument on the annotation; check PSI instead. Also, return back check on serializable annotation constructor arguments
This commit is contained in:
+2
-3
@@ -48,9 +48,8 @@ abstract class SerializerCodegen(declaration: KtPureClassOrObject, bindingContex
|
||||
|
||||
// checks if user didn't declared constructor (KSerializer<T0>, KSerializer<T1>...) on a KSerializer<T<T0, T1...>>
|
||||
private fun typedSerializerConstructorNotDeclared(): Boolean {
|
||||
val kSerializerSupertype = serializerDescriptor.typeConstructor.supertypes
|
||||
.find { isKSerializer(it) } ?: throw AssertionError("Serializer does not implement KSerializer??")
|
||||
val serializableImplementationTypeArguments = kSerializerSupertype.arguments.first().type.arguments
|
||||
val serializableImplementationTypeArguments = extractKSerializerArgumentFromImplementation(serializerDescriptor)?.arguments
|
||||
?: throw AssertionError("Serializer does not implement KSerializer??")
|
||||
|
||||
val typeParamsCount = serializableImplementationTypeArguments.size
|
||||
if (typeParamsCount == 0) return false //don't need it
|
||||
|
||||
+9
-5
@@ -24,9 +24,7 @@ import org.jetbrains.kotlin.name.ClassId
|
||||
import org.jetbrains.kotlin.name.Name
|
||||
import org.jetbrains.kotlin.psi.*
|
||||
import org.jetbrains.kotlin.resolve.BindingContext
|
||||
import org.jetbrains.kotlin.resolve.descriptorUtil.fqNameSafe
|
||||
import org.jetbrains.kotlin.resolve.descriptorUtil.getSuperClassNotAny
|
||||
import org.jetbrains.kotlin.resolve.lazy.descriptors.LazyAnnotationDescriptor
|
||||
import org.jetbrains.kotlin.types.KotlinType
|
||||
import org.jetbrains.kotlin.types.typeUtil.containsTypeProjectionsInTopLevelArguments
|
||||
import org.jetbrains.kotlin.types.typeUtil.isBoolean
|
||||
@@ -69,20 +67,26 @@ fun getSerialTypeInfo(property: SerializableProperty): SerialTypeInfo {
|
||||
SerialTypeInfo(property, if (property.type.isMarkedNullable) "Nullable" else "", serializer)
|
||||
}
|
||||
else -> {
|
||||
val serializer = findTypeSerializer(property.module, property.type)
|
||||
val serializer = findTypeSerializerOrContext(property.module, property.type)
|
||||
SerialTypeInfo(property, if (property.type.isMarkedNullable) "Nullable" else "", serializer)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun findTypeSerializerOrContext(module: ModuleDescriptor, kType: KotlinType): ClassDescriptor? {
|
||||
return findTypeSerializer(module, kType)
|
||||
?: module.findClassAcrossModuleDependencies(contextSerializerId)
|
||||
}
|
||||
|
||||
fun findTypeSerializer(module: ModuleDescriptor, kType: KotlinType): ClassDescriptor? {
|
||||
val userOverride = kType.overridenSerializer
|
||||
if (userOverride != null) return userOverride.toClassDescriptor
|
||||
if (kType.requiresPolymorphism()) return findPolymorphicSerializer(module)
|
||||
if (kType.isTypeParameter()) return null
|
||||
if (KotlinBuiltIns.isArray(kType)) return module.findClassAcrossModuleDependencies(referenceArraySerializerId)
|
||||
if (KotlinBuiltIns.isArray(kType)) return module.getClassFromInternalSerializationPackage("ReferenceArraySerializer")
|
||||
return kType.typeSerializer.toClassDescriptor // check for serializer defined on the type
|
||||
?: findStandardKotlinTypeSerializer(module, kType) // otherwise see if there is a standard serializer
|
||||
?: findEnumTypeSerializer(module, kType)
|
||||
?: module.findClassAcrossModuleDependencies(contextSerializerId)
|
||||
}
|
||||
|
||||
fun findStandardKotlinTypeSerializer(module: ModuleDescriptor, kType: KotlinType): ClassDescriptor? {
|
||||
|
||||
+3
-4
@@ -34,7 +34,7 @@ import org.jetbrains.kotlin.resolve.scopes.getDescriptorsFiltered
|
||||
import org.jetbrains.kotlin.types.KotlinType
|
||||
import org.jetbrains.kotlin.types.typeUtil.builtIns
|
||||
import org.jetbrains.kotlinx.serialization.compiler.backend.common.SerializerCodegen
|
||||
import org.jetbrains.kotlinx.serialization.compiler.backend.common.findTypeSerializer
|
||||
import org.jetbrains.kotlinx.serialization.compiler.backend.common.findTypeSerializerOrContext
|
||||
import org.jetbrains.kotlinx.serialization.compiler.backend.common.getSerialTypeInfo
|
||||
import org.jetbrains.kotlinx.serialization.compiler.backend.jvm.contextSerializerId
|
||||
import org.jetbrains.kotlinx.serialization.compiler.backend.jvm.enumSerializerId
|
||||
@@ -169,8 +169,7 @@ class SerializerJsTranslator(declaration: KtPureClassOrObject,
|
||||
}
|
||||
|
||||
private fun serializerInstance(serializerClass: ClassDescriptor?, module: ModuleDescriptor, kType: KotlinType, genericIndex: Int? = null): JsExpression? {
|
||||
val nullableSerClass = context.translateQualifiedReference(requireNotNull(
|
||||
module.findClassAcrossModuleDependencies(ClassId(internalPackageFqName, Name.identifier("NullableSerializer")))))
|
||||
val nullableSerClass = context.translateQualifiedReference(module.getClassFromInternalSerializationPackage("NullableSerializer"))
|
||||
if (serializerClass == null) {
|
||||
if (genericIndex == null) return null
|
||||
return JsNameRef(context.scope().declareName("$typeArgPrefix$genericIndex"), JsThisRef())
|
||||
@@ -182,7 +181,7 @@ class SerializerJsTranslator(declaration: KtPureClassOrObject,
|
||||
var args = if (serializerClass.classId == enumSerializerId || serializerClass.classId == contextSerializerId)
|
||||
listOf(createGetKClassExpression(kType.toClassDescriptor!!))
|
||||
else kType.arguments.map {
|
||||
val argSer = findTypeSerializer(module, it.type)
|
||||
val argSer = findTypeSerializerOrContext(module, it.type)
|
||||
val expr = serializerInstance(argSer, module, it.type, it.type.genericIndex) ?: return null
|
||||
if (it.type.isMarkedNullable) JsNew(nullableSerClass, listOf(expr)) else expr
|
||||
}
|
||||
|
||||
+5
-10
@@ -28,9 +28,7 @@ import org.jetbrains.kotlin.resolve.jvm.diagnostics.OtherOrigin
|
||||
import org.jetbrains.kotlin.resolve.jvm.jvmSignature.JvmMethodSignature
|
||||
import org.jetbrains.kotlin.types.KotlinType
|
||||
import org.jetbrains.kotlinx.serialization.compiler.backend.common.SerialTypeInfo
|
||||
import org.jetbrains.kotlinx.serialization.compiler.backend.common.findEnumTypeSerializer
|
||||
import org.jetbrains.kotlinx.serialization.compiler.backend.common.findPolymorphicSerializer
|
||||
import org.jetbrains.kotlinx.serialization.compiler.backend.common.requiresPolymorphism
|
||||
import org.jetbrains.kotlinx.serialization.compiler.backend.common.findTypeSerializer
|
||||
import org.jetbrains.kotlinx.serialization.compiler.resolve.*
|
||||
import org.jetbrains.kotlinx.serialization.compiler.resolve.KSerializerDescriptorResolver.typeArgPrefix
|
||||
import org.jetbrains.org.objectweb.asm.Type
|
||||
@@ -153,7 +151,7 @@ internal fun stackValueSerializerInstance(codegen: ClassBodyCodegen, module: Mod
|
||||
val argSerializers = kType.arguments.map { projection ->
|
||||
// bail out from stackValueSerializerInstance if any type argument is not serializable
|
||||
val argType = projection.type
|
||||
val argSerializer = findTypeSerializer(module, argType, codegen.typeMapper.mapType(argType)) ?: return false
|
||||
val argSerializer = findTypeSerializerOrContext(module, argType, codegen.typeMapper.mapType(argType)) ?: return false
|
||||
// check if it can be properly serialized with its args recursively
|
||||
if (!stackValueSerializerInstance(codegen, module, argType, argSerializer, null, argType.genericIndex, genericSerializerFieldGetter))
|
||||
return false
|
||||
@@ -243,7 +241,7 @@ fun getSerialTypeInfo(property: SerializableProperty, type: Type): JVMSerialType
|
||||
// todo: more efficient enum support here, but only for enums that don't define custom serializer
|
||||
// otherwise, it is a serializer for some other type
|
||||
val serializer = property.serializableWith?.toClassDescriptor
|
||||
?: findTypeSerializer(property.module, property.type, type)
|
||||
?: findTypeSerializerOrContext(property.module, property.type, type)
|
||||
return JVMSerialTypeInfo(property, Type.getType("Ljava/lang/Object;"),
|
||||
if (property.type.isMarkedNullable) "Nullable" else "", serializer)
|
||||
}
|
||||
@@ -251,12 +249,9 @@ fun getSerialTypeInfo(property: SerializableProperty, type: Type): JVMSerialType
|
||||
}
|
||||
}
|
||||
|
||||
fun findTypeSerializer(module: ModuleDescriptor, kType: KotlinType, asmType: Type): ClassDescriptor? {
|
||||
if (kType.requiresPolymorphism()) return findPolymorphicSerializer(module)
|
||||
if (KotlinBuiltIns.isArray(kType)) return module.findClassAcrossModuleDependencies(referenceArraySerializerId)
|
||||
return kType.typeSerializer.toClassDescriptor // check for serializer defined on the type
|
||||
fun findTypeSerializerOrContext(module: ModuleDescriptor, kType: KotlinType, asmType: Type): ClassDescriptor? {
|
||||
return findTypeSerializer(module, kType)
|
||||
?: findStandardAsmTypeSerializer(module, asmType) // otherwise see if there is a standard serializer
|
||||
?: findEnumTypeSerializer(module, kType)
|
||||
?: module.findClassAcrossModuleDependencies(contextSerializerId)
|
||||
}
|
||||
|
||||
|
||||
+2
@@ -34,6 +34,8 @@ class SerialInfoCodegenImpl(val codegen: ImplementationBodyCodegen, val thisClas
|
||||
|
||||
fun generate() {
|
||||
val props = thisClass.unsubstitutedMemberScope.getDescriptorsFiltered().filterIsInstance<PropertyDescriptor>()
|
||||
if (props.isEmpty()) return
|
||||
|
||||
generateFieldsAndSetters(props)
|
||||
generateConstructor(props)
|
||||
}
|
||||
|
||||
+1
-1
@@ -86,7 +86,7 @@ class SerializableCodegenImpl(
|
||||
load(serialDescI, descType)
|
||||
superTypeArguments.forEach {
|
||||
val genericIdx = serializableDescriptor.defaultType.arguments.indexOf(it).let { if (it == -1) null else it }
|
||||
val serial = findTypeSerializer(serializableDescriptor.module, it.type, classCodegen.typeMapper.mapType(it.type))
|
||||
val serial = findTypeSerializerOrContext(serializableDescriptor.module, it.type, classCodegen.typeMapper.mapType(it.type))
|
||||
stackValueSerializerInstance(classCodegen, serializableDescriptor.module, it.type, serial, this, genericIdx) {
|
||||
load(offsetI + it, kSerializerType)
|
||||
}
|
||||
|
||||
+1
@@ -89,6 +89,7 @@ class SerializerCodegenImpl(
|
||||
invokevirtual(descImplType.internalName, "addElement", "(Ljava/lang/String;)V", false)
|
||||
// pushing annotations
|
||||
for ((annotationClass, args, consParams) in property.annotationsWithArguments) {
|
||||
if (args.size != consParams.size) throw IllegalArgumentException("Can't use arguments with defaults for serializable annotations yet")
|
||||
load(classDescVar, descImplType)
|
||||
val implType = codegen.typeMapper.mapType(annotationClass).internalName + "\$" + KSerializerDescriptorResolver.IMPL_NAME.identifier
|
||||
// new Annotation$Impl(...)
|
||||
|
||||
+30
-3
@@ -23,6 +23,7 @@ import org.jetbrains.kotlin.name.ClassId
|
||||
import org.jetbrains.kotlin.name.FqName
|
||||
import org.jetbrains.kotlin.name.Name
|
||||
import org.jetbrains.kotlin.resolve.descriptorUtil.module
|
||||
import org.jetbrains.kotlin.resolve.lazy.descriptors.LazyAnnotationDescriptor
|
||||
import org.jetbrains.kotlin.resolve.scopes.getDescriptorsFiltered
|
||||
import org.jetbrains.kotlin.types.*
|
||||
import org.jetbrains.kotlin.types.typeUtil.isSubtypeOf
|
||||
@@ -51,6 +52,12 @@ fun ClassDescriptor.getKSerializerType(argument: SimpleType): SimpleType {
|
||||
return KotlinTypeFactory.simpleNotNullType(Annotations.EMPTY, getKSerializerDescriptor(), types)
|
||||
}
|
||||
|
||||
internal fun extractKSerializerArgumentFromImplementation(implementationClass: ClassDescriptor): KotlinType? {
|
||||
val kSerializerSupertype = implementationClass.typeConstructor.supertypes
|
||||
.find { isKSerializer(it) } ?: return null
|
||||
return kSerializerSupertype.arguments.first().type
|
||||
}
|
||||
|
||||
// ---- java.io.Serializable
|
||||
|
||||
internal val javaIOPackageFqName = FqName("java.io")
|
||||
@@ -115,7 +122,13 @@ val KotlinType?.toClassDescriptor: ClassDescriptor?
|
||||
|
||||
|
||||
val ClassDescriptor.isInternalSerializable: Boolean //todo normal checking
|
||||
get() = annotations.hasAnnotation(serializableAnnotationFqName) && annotations.serializableWith == null
|
||||
get() {
|
||||
if (!annotations.hasAnnotation(serializableAnnotationFqName)) return false
|
||||
val lazyDesc = annotations.findAnnotation(serializableAnnotationFqName)
|
||||
as? LazyAnnotationDescriptor ?: return false
|
||||
val psi = lazyDesc.annotationEntry
|
||||
return psi.valueArguments.isEmpty()
|
||||
}
|
||||
|
||||
// serializer that was declared for this type
|
||||
internal val ClassDescriptor?.classSerializer: KotlinType?
|
||||
@@ -147,9 +160,13 @@ internal fun checkSerializerNullability(classType: KotlinType, serializerType: K
|
||||
return serializerType
|
||||
}
|
||||
|
||||
// returns only user-overriden Serializer
|
||||
val KotlinType.overridenSerializer: KotlinType?
|
||||
get() = (this.toClassDescriptor?.annotations?.serializableWith)?.let { checkSerializerNullability(this, it) }
|
||||
|
||||
// serializer that was declared for this specific type or annotation from a class declaration
|
||||
val KotlinType.typeSerializer: KotlinType?
|
||||
get() = (this.annotations.serializableWith ?: (this.toClassDescriptor).classSerializer)?.let { checkSerializerNullability(this, it) }
|
||||
get() = this.toClassDescriptor?.classSerializer
|
||||
|
||||
val KotlinType.genericIndex: Int?
|
||||
get() = (this.constructor.declarationDescriptor as? TypeParameterDescriptor)?.index
|
||||
@@ -198,10 +215,20 @@ inline fun <reified R> Annotations.findAnnotationValue(annotationFqName: FqName,
|
||||
fun ClassDescriptor.getKSerializerConstructorMarker(): ClassDescriptor =
|
||||
module.findClassAcrossModuleDependencies(ClassId(packageFqName, kSerializerConstructorMarkerName))!!
|
||||
|
||||
fun ModuleDescriptor.getClassFromInternalSerializationPackage(classSimpleName: String) =
|
||||
requireNotNull(
|
||||
findClassAcrossModuleDependencies(
|
||||
ClassId(
|
||||
internalPackageFqName,
|
||||
Name.identifier(classSimpleName)
|
||||
)
|
||||
)
|
||||
) { "Can't locate class $classSimpleName" }
|
||||
|
||||
fun ClassDescriptor.getClassFromSerializationPackage(classSimpleName: String) =
|
||||
requireNotNull(module.findClassAcrossModuleDependencies(ClassId(packageFqName, Name.identifier(classSimpleName)))) {"Can't locate class $classSimpleName"}
|
||||
|
||||
fun ClassDescriptor.getClassFromInternalSerializationPackage(classSimpleName: String) =
|
||||
requireNotNull(module.findClassAcrossModuleDependencies(ClassId(internalPackageFqName, Name.identifier(classSimpleName)))) {"Can't locate class $classSimpleName"}
|
||||
module.getClassFromInternalSerializationPackage(classSimpleName)
|
||||
|
||||
fun ClassDescriptor.toSimpleType(nullable: Boolean = true) = KotlinTypeFactory.simpleType(Annotations.EMPTY, this.typeConstructor, emptyList(), nullable)
|
||||
|
||||
+9
-2
@@ -77,6 +77,10 @@ object KSerializerDescriptorResolver {
|
||||
val interfaceDecl = declarationProvider.correspondingClassOrObject!!
|
||||
val scope = ctx.declarationScopeProvider.getResolutionScopeForDeclaration(declarationProvider.ownerInfo!!.scopeAnchor)
|
||||
|
||||
val props = interfaceDecl.primaryConstructorParameters
|
||||
// if there is some properties, there will be a public synthetic constructor at the codegen phase
|
||||
val primaryCtorVisibility = if (props.isEmpty()) Visibilities.PUBLIC else Visibilities.PRIVATE
|
||||
|
||||
val descriptor = SyntheticClassOrObjectDescriptor(ctx,
|
||||
interfaceDecl,
|
||||
interfaceDesc,
|
||||
@@ -85,7 +89,7 @@ object KSerializerDescriptorResolver {
|
||||
scope,
|
||||
Modality.FINAL,
|
||||
Visibilities.PUBLIC,
|
||||
Visibilities.PRIVATE,
|
||||
primaryCtorVisibility,
|
||||
ClassKind.CLASS,
|
||||
false)
|
||||
descriptor.initialize()
|
||||
@@ -216,7 +220,10 @@ object KSerializerDescriptorResolver {
|
||||
companionDescriptor, Annotations.EMPTY, name, CallableMemberDescriptor.Kind.SYNTHESIZED, companionDescriptor.source
|
||||
)
|
||||
|
||||
val typeParam = listOf(createProjection(classDescriptor.defaultType, Variance.INVARIANT, null))
|
||||
val serializableClassOnImplSite = extractKSerializerArgumentFromImplementation(companionDescriptor)
|
||||
?: throw AssertionError("Serializer does not implement KSerializer??")
|
||||
|
||||
val typeParam = listOf(createProjection(serializableClassOnImplSite, Variance.INVARIANT, null))
|
||||
val functionFromSerializer = companionDescriptor.getKSerializerDescriptor().getMemberScope(typeParam)
|
||||
.getContributedFunctions(name, NoLookupLocation.FROM_BUILTINS).single()
|
||||
|
||||
|
||||
Reference in New Issue
Block a user