Semantic change:

Now properties with default values are @Optional by default, and properties without backing fields are @Transient by default.
This commit is contained in:
Leonid Startsev
2019-01-16 17:37:32 +03:00
parent cca03e9586
commit 08f983ef52
3 changed files with 44 additions and 33 deletions
@@ -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, "<init>", desc, false)
return 0 to propStartVar
}
else {
} else {
val superProps = SerializableProperties(superClass, classCodegen.bindingContext).serializableProperties
val creator = buildInternalConstructorDesc(propStartVar, 1, classCodegen, superProps)
invokespecial(superType, "<init>", 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)
}
@@ -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<PropertyDescriptor>()
.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()
@@ -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<Triple<ClassDescriptor, List<ValueArgument>, List<ValueParameterDescriptor>>> =
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
}