diff --git a/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/common/SerializerCodegen.kt b/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/common/SerializerCodegen.kt index 9aefbd96984..3fc779ec541 100644 --- a/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/common/SerializerCodegen.kt +++ b/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/common/SerializerCodegen.kt @@ -48,9 +48,8 @@ abstract class SerializerCodegen(declaration: KtPureClassOrObject, bindingContex // checks if user didn't declared constructor (KSerializer, KSerializer...) on a KSerializer> 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 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 6336a2345ab..e1f2289def7 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 @@ -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? { diff --git a/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/js/SerializerJsTranslator.kt b/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/js/SerializerJsTranslator.kt index 5f27437b2e1..6c4296a6696 100644 --- a/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/js/SerializerJsTranslator.kt +++ b/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/js/SerializerJsTranslator.kt @@ -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 } 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 412f016bc6d..dbdabaf9368 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 @@ -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) } diff --git a/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/jvm/SerialInfoCodegenImpl.kt b/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/jvm/SerialInfoCodegenImpl.kt index e20f846e995..afdc9f3be98 100644 --- a/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/jvm/SerialInfoCodegenImpl.kt +++ b/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/jvm/SerialInfoCodegenImpl.kt @@ -34,6 +34,8 @@ class SerialInfoCodegenImpl(val codegen: ImplementationBodyCodegen, val thisClas fun generate() { val props = thisClass.unsubstitutedMemberScope.getDescriptorsFiltered().filterIsInstance() + if (props.isEmpty()) return + generateFieldsAndSetters(props) generateConstructor(props) } diff --git a/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/jvm/SerializableCodegenImpl.kt b/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/jvm/SerializableCodegenImpl.kt index 35bfa72c4c4..3080fff6840 100644 --- a/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/jvm/SerializableCodegenImpl.kt +++ b/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/jvm/SerializableCodegenImpl.kt @@ -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) } diff --git a/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/jvm/SerializerCodegenImpl.kt b/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/jvm/SerializerCodegenImpl.kt index a4249d42526..9d533cb06d3 100644 --- a/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/jvm/SerializerCodegenImpl.kt +++ b/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/jvm/SerializerCodegenImpl.kt @@ -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(...) diff --git a/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/resolve/KSerializationUtil.kt b/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/resolve/KSerializationUtil.kt index d665e1c8bef..8ce11e9609e 100644 --- a/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/resolve/KSerializationUtil.kt +++ b/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/resolve/KSerializationUtil.kt @@ -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 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) diff --git a/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/resolve/KSerializerDescriptorResolver.kt b/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/resolve/KSerializerDescriptorResolver.kt index 60cd5855c7b..ee5d733d439 100644 --- a/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/resolve/KSerializerDescriptorResolver.kt +++ b/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/resolve/KSerializerDescriptorResolver.kt @@ -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()