Default case in "load" switch-case throws exception instead of silent read ending.
Improved error descriptions, fixed issue with transient properties without backing field (Kotlin/kotlinx.serialization#2) Don't add serialClassDesc property if user already defined one (Kotlin/kotlinx.serialization#10) Bump version to 0.1.1 Fix issue with mixing serial name with real property name (Kotlin/kotlinx.serialization#13)
This commit is contained in:
@@ -17,7 +17,7 @@
|
||||
<groupId>org.jetbrains.kotlinx</groupId>
|
||||
<artifactId>kotlinx-maven-serialization-plugin</artifactId>
|
||||
<packaging>jar</packaging>
|
||||
<version>0.1</version>
|
||||
<version>0.1.1</version>
|
||||
|
||||
<description>Serialization plugin for Maven</description>
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@ plugins {
|
||||
}
|
||||
|
||||
group = 'org.jetbrains.kotlinx'
|
||||
version = '0.1'
|
||||
version = '0.1.1'
|
||||
|
||||
apply plugin: 'kotlin'
|
||||
|
||||
|
||||
+2
-2
@@ -80,13 +80,13 @@ abstract class SerializerCodegen(declaration: KtPureClassOrObject, bindingContex
|
||||
fun getPropertyToGenerate(
|
||||
classDescriptor: ClassDescriptor,
|
||||
name: String,
|
||||
isReturnTypeOk: (KotlinType) -> Boolean
|
||||
isReturnTypeOk: (PropertyDescriptor) -> Boolean
|
||||
): PropertyDescriptor? =
|
||||
classDescriptor.unsubstitutedMemberScope.getContributedVariables(Name.identifier(name), NoLookupLocation.FROM_BACKEND)
|
||||
.singleOrNull { property ->
|
||||
property.kind.let { kind -> kind == CallableMemberDescriptor.Kind.SYNTHESIZED || kind == CallableMemberDescriptor.Kind.FAKE_OVERRIDE } &&
|
||||
property.modality != Modality.FINAL &&
|
||||
property.returnType != null &&
|
||||
isReturnTypeOk(property.returnType!!)
|
||||
isReturnTypeOk(property)
|
||||
}
|
||||
}
|
||||
|
||||
+10
-10
@@ -272,20 +272,20 @@ class SerializerJsTranslator(declaration: KtPureClassOrObject,
|
||||
bitMasks[i / 32],
|
||||
JsIntLiteral(bitPos)
|
||||
).makeStmt()
|
||||
// if (!readAll) break -- but only if this is last prop
|
||||
if (i != orderedProperties.lastIndex)
|
||||
+JsIf(JsAstUtils.not(readAllVar), JsBreak())
|
||||
else {
|
||||
// if (readAll) breakLoop else break
|
||||
+JsIf(readAllVar, JsBreak(loopRef), JsBreak())
|
||||
}
|
||||
// if (!readAll) break
|
||||
+JsIf(JsAstUtils.not(readAllVar), JsBreak())
|
||||
}
|
||||
}
|
||||
// case -1, default: break loop
|
||||
case(JsIntLiteral(-1)) {}
|
||||
default {
|
||||
// case -1: break loop
|
||||
case(JsIntLiteral(-1)) {
|
||||
+JsBreak(loopRef)
|
||||
}
|
||||
// default: throw
|
||||
default {
|
||||
val excClassRef = serializableDescriptor.getClassFromSerializationPackage("UnknownFieldException")
|
||||
.let { context.getQualifiedReference(it) }
|
||||
+JsThrow(JsNew(excClassRef, listOf(indexVar)))
|
||||
}
|
||||
}
|
||||
}, loop)
|
||||
|
||||
|
||||
+1
@@ -51,6 +51,7 @@ internal val kSerializerArrayType = Type.getObjectType("[Lkotlinx/serialization/
|
||||
|
||||
internal val serializationExceptionName = "kotlinx/serialization/SerializationException"
|
||||
internal val serializationExceptionMissingFieldName = "kotlinx/serialization/MissingFieldException"
|
||||
internal val serializationExceptionUnknownIndexName = "kotlinx/serialization/UnknownFieldException"
|
||||
|
||||
val OPT_MASK_TYPE: Type = Type.INT_TYPE
|
||||
val OPT_MASK_BITS = 32
|
||||
|
||||
+11
-2
@@ -24,6 +24,7 @@ import org.jetbrains.kotlin.descriptors.ClassDescriptor
|
||||
import org.jetbrains.kotlin.descriptors.FunctionDescriptor
|
||||
import org.jetbrains.kotlin.descriptors.PropertyDescriptor
|
||||
import org.jetbrains.kotlin.psi.KtParameter
|
||||
import org.jetbrains.kotlin.resolve.BindingContext
|
||||
import org.jetbrains.kotlin.resolve.descriptorUtil.getSuperClassOrAny
|
||||
import org.jetbrains.kotlin.resolve.jvm.jvmSignature.JvmMethodSignature
|
||||
import org.jetbrains.kotlinx.serialization.compiler.backend.common.SerializableCodegen
|
||||
@@ -140,6 +141,8 @@ class SerializableCodegenImpl(
|
||||
goTo(nextLabel)
|
||||
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))
|
||||
exprCodegen.genInitProperty(prop)
|
||||
visitLabel(nextLabel)
|
||||
}
|
||||
@@ -150,6 +153,7 @@ class SerializableCodegenImpl(
|
||||
val serializedProps = properties.serializableProperties.map { it.descriptor }
|
||||
|
||||
(descToProps - serializedProps)
|
||||
.filter { classCodegen.shouldInitializeProperty(it.value) }
|
||||
.forEach { (_, prop) -> classCodegen.initializeProperty(exprCodegen, prop) }
|
||||
(paramsToProps - serializedProps)
|
||||
.forEach { (t, u) -> exprCodegen.genInitParam(t, u) }
|
||||
@@ -168,7 +172,8 @@ class SerializableCodegenImpl(
|
||||
load(0, thisAsmType)
|
||||
|
||||
if (!superClass.isInternalSerializable) {
|
||||
require(superClass.constructors.firstOrNull { it.valueParameters.isEmpty() } != null) { "Non-serializable parent of serializable class must have no arg constructor" }
|
||||
require(superClass.constructors.firstOrNull { it.valueParameters.isEmpty() } != null,
|
||||
{ "Non-serializable parent of serializable $serializableDescriptor must have no arg constructor" })
|
||||
|
||||
// call
|
||||
invokespecial(superType, "<init>", "()V", false)
|
||||
@@ -191,14 +196,18 @@ class SerializableCodegenImpl(
|
||||
}
|
||||
?: getParam(prop)?.let {
|
||||
this.v.load(0, thisAsmType)
|
||||
if (!it.hasDefaultValue()) throw CompilationException("Optional field ${it.name} in primary constructor of serializable " +
|
||||
"$serializableDescriptor must have default value", null, it)
|
||||
this.gen(it.defaultValue, prop.asmType)
|
||||
this.v.putfield(thisAsmType.internalName, prop.name, prop.asmType.descriptor)
|
||||
this.v.putfield(thisAsmType.internalName, prop.descriptor.name.asString(), prop.asmType.descriptor)
|
||||
}
|
||||
?: throw IllegalStateException()
|
||||
|
||||
private fun ExpressionCodegen.genInitParam(prop: PropertyDescriptor, param: KtParameter) {
|
||||
this.v.load(0, thisAsmType)
|
||||
val mapType = classCodegen.typeMapper.mapType(prop.type)
|
||||
if (!param.hasDefaultValue()) throw CompilationException("Transient field ${param.name} in primary constructor of serializable " +
|
||||
"$serializableDescriptor must have default value", null, param)
|
||||
this.gen(param.defaultValue, mapType)
|
||||
this.v.putfield(thisAsmType.internalName, prop.name.asString(), mapType.descriptor)
|
||||
}
|
||||
|
||||
+11
-2
@@ -210,6 +210,7 @@ class SerializerCodegenImpl(
|
||||
val labeledProperties = orderedProperties.filter { !it.transient }
|
||||
val readAllLabel = Label()
|
||||
val readEndLabel = Label()
|
||||
val incorrectIndLabel = Label()
|
||||
val labels = arrayOfNulls<Label>(labeledProperties.size + 2)
|
||||
labels[0] = readAllLabel // READ_ALL
|
||||
labels[1] = readEndLabel // READ_DONE
|
||||
@@ -217,8 +218,7 @@ class SerializerCodegenImpl(
|
||||
labels[i + 2] = Label()
|
||||
}
|
||||
load(indexVar, Type.INT_TYPE)
|
||||
// todo: readEnd is currently default, should probably throw exception instead
|
||||
tableswitch(-2, labeledProperties.size - 1, readEndLabel, *labels)
|
||||
tableswitch(-2, labeledProperties.size - 1, incorrectIndLabel, *labels)
|
||||
// readAll: readAll := true
|
||||
visitLabel(readAllLabel)
|
||||
iconst(1)
|
||||
@@ -315,6 +315,15 @@ class SerializerCodegenImpl(
|
||||
}
|
||||
// return
|
||||
areturn(serializableAsmType)
|
||||
|
||||
// throwing an exception in default branch (if no index matched)
|
||||
visitLabel(incorrectIndLabel)
|
||||
anew(Type.getObjectType(serializationExceptionUnknownIndexName))
|
||||
dup()
|
||||
load(indexVar, Type.INT_TYPE)
|
||||
invokespecial(serializationExceptionUnknownIndexName, "<init>", "(I)V", false)
|
||||
checkcast(Type.getObjectType("java/lang/Throwable"))
|
||||
athrow()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+3
-2
@@ -27,6 +27,7 @@ import org.jetbrains.kotlin.name.FqName
|
||||
import org.jetbrains.kotlin.name.Name
|
||||
import org.jetbrains.kotlin.resolve.descriptorUtil.module
|
||||
import org.jetbrains.kotlin.types.*
|
||||
import org.jetbrains.kotlin.types.typeUtil.isSubtypeOf
|
||||
|
||||
internal val packageFqName = FqName("kotlinx.serialization")
|
||||
internal val internalPackageFqName = FqName("kotlinx.serialization.internal")
|
||||
@@ -147,8 +148,8 @@ fun getSerializableClassDescriptorBySerializer(serializerDescriptor: ClassDescri
|
||||
return classDescriptor
|
||||
}
|
||||
|
||||
// todo: serialization: do an actual check
|
||||
fun ClassDescriptor.checkSerializableClassPropertyResult(type: KotlinType): Boolean = true
|
||||
fun ClassDescriptor.checkSerializableClassPropertyResult(prop: PropertyDescriptor): Boolean =
|
||||
prop.returnType!!.isSubtypeOf(getClassFromSerializationPackage("KSerialClassDesc").toSimpleType(false)) // todo: cache lookup
|
||||
|
||||
// todo: serialization: do an actual check better that just number of parameters
|
||||
fun ClassDescriptor.checkSaveMethodParameters(parameters: List<ValueParameterDescriptor>): Boolean =
|
||||
|
||||
+4
-4
@@ -94,10 +94,10 @@ object KSerializerDescriptorResolver {
|
||||
fromSupertypes: ArrayList<PropertyDescriptor>,
|
||||
name: Name,
|
||||
result: MutableSet<PropertyDescriptor>) {
|
||||
val classDescriptor = getSerializableClassDescriptorBySerializer(thisDescriptor) ?: return
|
||||
// todo: check if it is already defined
|
||||
if (name == SERIAL_DESC_FIELD_NAME)
|
||||
result.add(createSerializableClassPropertyDescriptor(thisDescriptor, classDescriptor))
|
||||
val classDescriptor = getSerializableClassDescriptorBySerializer(thisDescriptor) ?: return
|
||||
if (name == SERIAL_DESC_FIELD_NAME && result.none(thisDescriptor::checkSerializableClassPropertyResult) &&
|
||||
fromSupertypes.none { thisDescriptor.checkSerializableClassPropertyResult(it) && it.modality == Modality.FINAL} )
|
||||
result.add(createSerializableClassPropertyDescriptor(thisDescriptor, classDescriptor))
|
||||
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user