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 db8bce66089..56ccf880b90 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 @@ -18,7 +18,6 @@ package org.jetbrains.kotlinx.serialization.compiler.backend.jvm import org.jetbrains.kotlin.codegen.* import org.jetbrains.kotlin.descriptors.ClassConstructorDescriptor -import org.jetbrains.kotlin.descriptors.ClassDescriptor import org.jetbrains.kotlin.descriptors.FunctionDescriptor import org.jetbrains.kotlin.descriptors.PropertyDescriptor import org.jetbrains.kotlin.js.resolve.diagnostics.findPsi @@ -36,8 +35,7 @@ import org.jetbrains.org.objectweb.asm.Type import org.jetbrains.org.objectweb.asm.commons.InstructionAdapter class SerializableCodegenImpl( - private val classCodegen: ImplementationBodyCodegen, - serializableClass: ClassDescriptor + private val classCodegen: ImplementationBodyCodegen ) : SerializableCodegen(classCodegen.descriptor, classCodegen.bindingContext) { private val thisAsmType = classCodegen.typeMapper.mapClass(serializableDescriptor) @@ -46,7 +44,7 @@ class SerializableCodegenImpl( fun generateSerializableExtensions(codegen: ImplementationBodyCodegen) { val serializableClass = codegen.descriptor if (serializableClass.isInternalSerializable) - SerializableCodegenImpl(codegen, serializableClass).generate() + SerializableCodegenImpl(codegen).generate() else if (serializableClass.hasSerializableAnnotationWithoutArgs && !serializableClass.hasCompanionObjectAsSerializer) { throw CompilationException( "@Serializable annotation on $serializableClass would be ignored because it is impossible to serialize it automatically. " + @@ -96,7 +94,8 @@ class SerializableCodegenImpl( val myPropsStart: Int if (superClass.isInternalSerializable) { myPropsStart = SerializableProperties(superClass, classCodegen.bindingContext).serializableProperties.size - val superTypeArguments = serializableDescriptor.typeConstructor.supertypes.single { it.toClassDescriptor?.isInternalSerializable == true }.arguments + val superTypeArguments = + serializableDescriptor.typeConstructor.supertypes.single { it.toClassDescriptor?.isInternalSerializable == true }.arguments //super.writeSelf(output, serialDesc) load(thisI, thisAsmType) load(outputI, kOutputType) @@ -108,10 +107,15 @@ class SerializableCodegenImpl( load(offsetI + it, kSerializerType) } } - val superSignature = classCodegen.typeMapper.mapSignatureSkipGeneric(KSerializerDescriptorResolver.createWriteSelfFunctionDescriptor(superClass)) - invokespecial(classCodegen.typeMapper.mapType(superClass).internalName, superSignature.asmMethod.name, superSignature.asmMethod.descriptor, false) - } - else { + val superSignature = + classCodegen.typeMapper.mapSignatureSkipGeneric(KSerializerDescriptorResolver.createWriteSelfFunctionDescriptor(superClass)) + invokespecial( + classCodegen.typeMapper.mapType(superClass).internalName, + superSignature.asmMethod.name, + superSignature.asmMethod.descriptor, + false + ) + } else { myPropsStart = 0 } @@ -174,16 +178,9 @@ class SerializableCodegenImpl( private fun InstructionAdapter.doGenerateConstructorImpl(exprCodegen: ExpressionCodegen) { val seenMask = 1 -// var propOffset = 2 var (propIndex, propOffset) = generateSuperSerializableCall(2) for (i in propIndex until properties.serializableProperties.size) { val prop = properties[i] - if (prop.transient) { - if (!needInitProperty(prop)) throw CompilationException("transient without default value", null, null) - exprCodegen.genInitProperty(prop) - propOffset += prop.asmType.size - continue - } val propType = prop.asmType if (!prop.optional) { // primary were validated before constructor call @@ -196,8 +193,7 @@ class SerializableCodegenImpl( load(0, thisAsmType) load(propOffset, propType) putfield(thisAsmType.internalName, prop.descriptor.name.asString(), propType.descriptor) - } - else { + } else { genValidateProperty(i) { seenMask } val setLbl = Label() val nextLabel = Label() @@ -211,7 +207,11 @@ class SerializableCodegenImpl( visitLabel(setLbl) // setting defaultValue if (classCodegen.bindingContext[BindingContext.BACKING_FIELD_REQUIRED, prop.descriptor] != true) - throw CompilationException("Optional properties without backing fields doesn't have much sense, maybe you want transient?", null, getProp(prop)) + throw CompilationException( + "Optional properties without backing fields doesn't have much sense, maybe you want transient?", + null, + getProp(prop) + ) exprCodegen.genInitProperty(prop) visitLabel(nextLabel) } @@ -222,15 +222,15 @@ class SerializableCodegenImpl( val serializedProps = properties.serializableProperties.map { it.descriptor } (descToProps - serializedProps) - .filter { classCodegen.shouldInitializeProperty(it.value) } - .forEach { (_, prop) -> classCodegen.initializeProperty(exprCodegen, prop) } + .filter { classCodegen.shouldInitializeProperty(it.value) } + .forEach { (_, prop) -> classCodegen.initializeProperty(exprCodegen, prop) } (paramsToProps - serializedProps) - .forEach { (t, u) -> exprCodegen.genInitParam(t, u) } + .forEach { (t, u) -> exprCodegen.genInitParam(t, u) } // init blocks // todo: proper order with other initializers classCodegen.myClass.anonymousInitializers() - .forEach { exprCodegen.gen(it, Type.VOID_TYPE) } + .forEach { exprCodegen.gen(it, Type.VOID_TYPE) } areturn(Type.VOID_TYPE) } @@ -255,8 +255,7 @@ class SerializableCodegenImpl( } invokespecial(superType, "", desc, false) return 0 to propStartVar - } - else { + } else { val superProps = SerializableProperties(superClass, classCodegen.bindingContext).serializableProperties val creator = buildInternalConstructorDesc(propStartVar, 1, classCodegen, superProps) invokespecial(superType, "", creator, false) @@ -264,9 +263,6 @@ class SerializableCodegenImpl( } } - private fun needInitProperty(prop: SerializableProperty) = getProp(prop)?.let { classCodegen.shouldInitializeProperty(it) } - ?: getParam(prop)?.hasDefaultValue() ?: throw IllegalStateException() - private fun ExpressionCodegen.genInitProperty(prop: SerializableProperty) = getProp(prop)?.let { classCodegen.initializeProperty(this, it) } diff --git a/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/resolve/SerializableProperties.kt b/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/resolve/SerializableProperties.kt index c4a7a7dc057..12364361f92 100644 --- a/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/resolve/SerializableProperties.kt +++ b/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/resolve/SerializableProperties.kt @@ -19,6 +19,7 @@ package org.jetbrains.kotlinx.serialization.compiler.resolve import org.jetbrains.kotlin.descriptors.* import org.jetbrains.kotlin.resolve.BindingContext import org.jetbrains.kotlin.resolve.descriptorUtil.getSuperClassNotAny +import org.jetbrains.kotlin.resolve.hasBackingField import org.jetbrains.kotlin.resolve.scopes.DescriptorKindFilter class SerializableProperties(private val serializableClass: ClassDescriptor, val bindingContext: BindingContext) { @@ -40,7 +41,8 @@ class SerializableProperties(private val serializableClass: ClassDescriptor, val .filterIsInstance() .filter { it.kind == CallableMemberDescriptor.Kind.DECLARATION } .filter(this::isPropSerializable) - .map { prop -> SerializableProperty(prop, primaryConstructorProperties[prop] ?: false) } + .map { prop -> SerializableProperty(prop, primaryConstructorProperties[prop] ?: false, prop.hasBackingField(bindingContext)) } + .filterNot { it.transient } .partition { primaryConstructorProperties.contains(it.descriptor) } .run { val supers = serializableClass.getSuperClassNotAny() @@ -66,7 +68,6 @@ class SerializableProperties(private val serializableClass: ClassDescriptor, val serializableProperties.minus(serializableConstructorProperties) val size = serializableProperties.size - val indices = serializableProperties.indices operator fun get(index: Int) = serializableProperties[index] operator fun iterator() = serializableProperties.iterator() diff --git a/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/resolve/SerializableProperty.kt b/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/resolve/SerializableProperty.kt index 656815fc087..51964370cf4 100644 --- a/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/resolve/SerializableProperty.kt +++ b/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/resolve/SerializableProperty.kt @@ -20,18 +20,25 @@ import org.jetbrains.kotlin.descriptors.ClassDescriptor import org.jetbrains.kotlin.descriptors.PropertyDescriptor import org.jetbrains.kotlin.descriptors.ValueParameterDescriptor import org.jetbrains.kotlin.descriptors.annotations.Annotations +import org.jetbrains.kotlin.psi.KtDeclarationWithInitializer +import org.jetbrains.kotlin.psi.KtParameter import org.jetbrains.kotlin.psi.ValueArgument import org.jetbrains.kotlin.resolve.descriptorUtil.module +import org.jetbrains.kotlin.resolve.source.getPsi import org.jetbrains.kotlin.types.KotlinType -class SerializableProperty(val descriptor: PropertyDescriptor, val isConstructorParameterWithDefault: Boolean) { +class SerializableProperty( + val descriptor: PropertyDescriptor, + val isConstructorParameterWithDefault: Boolean, + hasBackingField: Boolean +) { val name = descriptor.annotations.serialNameValue ?: descriptor.name.asString() val type = descriptor.type val genericIndex = type.genericIndex val module = descriptor.module val serializableWith = extractSerializableWith(descriptor.annotations) - val optional = descriptor.annotations.serialOptional - val transient = descriptor.annotations.serialTransient + val optional = descriptor.declaresDefaultValue + val transient = descriptor.annotations.serialTransient || !hasBackingField val annotationsWithArguments: List, List>> = descriptor.annotationsWithArguments() @@ -42,3 +49,10 @@ class SerializableProperty(val descriptor: PropertyDescriptor, val isConstructor else null } } + +val PropertyDescriptor.declaresDefaultValue: Boolean + get() = when (val declaration = this.source.getPsi()) { + is KtDeclarationWithInitializer -> declaration.initializer != null + is KtParameter -> declaration.defaultValue != null + else -> false + } \ No newline at end of file