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:
Leonid Startsev
2018-05-25 23:42:01 +03:00
parent 572e8873ca
commit 26ac6bd887
9 changed files with 62 additions and 28 deletions
@@ -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
@@ -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? {
@@ -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
}
@@ -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)
}
@@ -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)
}
@@ -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)
}
@@ -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(...)
@@ -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)
@@ -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()