Support classes with more than 32 serializable properties

Via increasing count of int bit masks in synthetic constructor
This commit is contained in:
Leonid Startsev
2019-01-18 18:19:08 +03:00
parent 08f983ef52
commit 04706f4473
10 changed files with 174 additions and 138 deletions
@@ -33,7 +33,7 @@ abstract class SerializerCodegen(
val serializableDescriptor: ClassDescriptor = getSerializableClassDescriptorBySerializer(serializerDescriptor)!!
protected val serialName: String = serializableDescriptor.annotations.serialNameValue ?: serializableDescriptor.fqNameUnsafe.asString()
protected val properties = SerializableProperties(serializableDescriptor, bindingContext)
protected val orderedProperties = properties.serializableProperties
protected val serializableProperties = properties.serializableProperties
private fun checkSerializability() {
check(properties.isExternallySerializable) {
@@ -19,11 +19,8 @@ import org.jetbrains.kotlin.resolve.descriptorUtil.getSuperClassOrAny
import org.jetbrains.kotlin.resolve.descriptorUtil.module
import org.jetbrains.kotlin.util.OperatorNameConventions
import org.jetbrains.kotlinx.serialization.compiler.backend.common.SerializableCodegen
import org.jetbrains.kotlinx.serialization.compiler.resolve.*
import org.jetbrains.kotlinx.serialization.compiler.resolve.SerialEntityNames.MISSING_FIELD_EXC
import org.jetbrains.kotlinx.serialization.compiler.resolve.getClassFromSerializationPackage
import org.jetbrains.kotlinx.serialization.compiler.resolve.hasCompanionObjectAsSerializer
import org.jetbrains.kotlinx.serialization.compiler.resolve.hasSerializableAnnotationWithoutArgs
import org.jetbrains.kotlinx.serialization.compiler.resolve.isInternalSerializable
class SerializableIrGenerator(
val irClass: IrClass,
@@ -51,10 +48,11 @@ class SerializableIrGenerator(
generateAnySuperConstructorCall(toBuilder = this@contributeConstructor)
else
TODO("Serializable classes with inheritance")
val seenVar = ctor.valueParameters[0]
val seenVarsOffset = properties.serializableProperties.bitMaskSlotCount()
val seenVars = (0 until seenVarsOffset).map { ctor.valueParameters[it] }
for ((index, prop) in properties.serializableProperties.withIndex()) {
val paramRef = ctor.valueParameters[index + 1]
val paramRef = ctor.valueParameters[index + seenVarsOffset]
// assign this.a = a in else branch
val assignParamExpr = irSetField(irGet(thiz), prop.irField, irGet(paramRef))
@@ -69,7 +67,11 @@ class SerializableIrGenerator(
val propNotSeenTest =
irEquals(
irInt(0),
irBinOp(OperatorNameConventions.AND, irGet(seenVar), irInt(1 shl (index % 32)))
irBinOp(
OperatorNameConventions.AND,
irGet(seenVars[bitMaskSlotAt(index)]),
irInt(1 shl (index % 32))
)
)
+irIfThenElse(compilerContext.irBuiltIns.unitType, propNotSeenTest, ifNotSeenExpr, assignParamExpr)
@@ -95,7 +95,7 @@ class SerializerIrGenerator(val irClass: IrClass, override val compilerContext:
dispatchReceiver = irGet(localDesc)
}.mapValueParameters { irString(fieldName) }
for (classProp in orderedProperties) {
for (classProp in serializableProperties) {
if (classProp.transient) continue
+addFieldCall(classProp.name)
// add property annotations
@@ -163,7 +163,7 @@ class SerializerIrGenerator(val irClass: IrClass, override val compilerContext:
}
override fun generateChildSerializersGetter(function: FunctionDescriptor) = irClass.contributeFunction(function) { irFun ->
val allSerializers = orderedProperties.map { requireNotNull(
val allSerializers = serializableProperties.map { requireNotNull(
serializerTower(this@SerializerIrGenerator, it)) { "Property ${it.name} must have a serializer" }
}
@@ -214,7 +214,7 @@ class SerializerIrGenerator(val irClass: IrClass, override val compilerContext:
fun SerializableProperty.irGet(): IrGetField = irGetField(irGet(serialObjectSymbol), irField)
// internal serialization via virtual calls?
for ((index, property) in orderedProperties.filter { !it.transient }.withIndex()) {
for ((index, property) in serializableProperties.filter { !it.transient }.withIndex()) {
// output.writeXxxElementValue(classDesc, index, value)
val sti = getSerialTypeInfo(property)
val innerSerial = serializerInstance(
@@ -252,7 +252,7 @@ class SerializerIrGenerator(val irClass: IrClass, override val compilerContext:
// if (obj.prop != DEFAULT_VALUE || output.shouldEncodeElementDefault(this.descriptor, i))
// output.encodeIntElement(this.descriptor, i, obj.prop)
val shouldEncodeFunc = kOutputClass.referenceMethod(CallingConventions.shouldEncodeDefault)
val partA = irNotEquals(property.irGet(), fieldInitializer(property)!!) // todo: props w/o backing fields
val partA = irNotEquals(property.irGet(), fieldInitializer(property)!!)
val partB = irInvoke(irGet(localOutput), shouldEncodeFunc, irGet(localSerialDesc), irInt(index))
// Ir infrastructure does not have dedicated symbol for ||, so
// `a || b == if (a) true else b`, see org.jetbrains.kotlin.ir.builders.PrimitivesKt.oror
@@ -305,12 +305,12 @@ class SerializerIrGenerator(val irClass: IrClass, override val compilerContext:
// val readAll = irTemporaryVar(irBoolean(false), "readAll", parent = loadFunc)
//
// calculating bit mask vars
val blocksCnt = orderedProperties.size / 32 + 1
val blocksCnt = serializableProperties.bitMaskSlotCount()
// var bitMask0 = 0, bitMask1 = 0...
val bitMasks = (0 until blocksCnt).map { irTemporaryVar(irInt(0), "bitMask$it") }
// var local0 = null, local1 = null ...
val localProps = orderedProperties.mapIndexed { i, prop ->
val localProps = serializableProperties.mapIndexed { i, prop ->
val (expr, type) = defaultValueAndType(prop)
irTemporaryVar(
expr,
@@ -341,7 +341,7 @@ class SerializerIrGenerator(val irClass: IrClass, override val compilerContext:
// if index == -1 (READ_DONE) break loop
+IrBranchImpl(irEquals(indexVar.get(), irInt(-1)), irSetVar(flagVar.symbol, irBoolean(false)))
val branchBodies: List<Pair<Int, IrExpression>> = orderedProperties.mapIndexed { index, property ->
val branchBodies: List<Pair<Int, IrExpression>> = serializableProperties.mapIndexed { index, property ->
val body = irBlock {
val sti = getSerialTypeInfo(property)
val innerSerial = serializerInstance(this@SerializerIrGenerator, serializableDescriptor, sti.serializer, property.module, property.type, property.genericIndex)
@@ -398,7 +398,7 @@ class SerializerIrGenerator(val irClass: IrClass, override val compilerContext:
val ctor: IrConstructorSymbol = if (serializableDescriptor.isInternalSerializable) {
val ctorDesc = serializableIrClass
.constructors.single { it.origin == SERIALIZABLE_PLUGIN_ORIGIN }
args = listOf(irGet(bitMasks[0])) + args + irNull()
args = bitMasks.map { irGet(it) } + args + irNull()
ctorDesc.symbol
} else {
compilerContext.externalSymbols.referenceConstructor(serializableDescriptor.unsubstitutedPrimaryConstructor!!)
@@ -32,16 +32,12 @@ import org.jetbrains.kotlin.psi.KtExpression
import org.jetbrains.kotlin.psi.KtPureClassOrObject
import org.jetbrains.kotlinx.serialization.compiler.backend.common.SerializableCodegen
import org.jetbrains.kotlinx.serialization.compiler.backend.common.anonymousInitializers
import org.jetbrains.kotlinx.serialization.compiler.resolve.*
import org.jetbrains.kotlinx.serialization.compiler.resolve.SerialEntityNames.MISSING_FIELD_EXC
import org.jetbrains.kotlinx.serialization.compiler.resolve.getClassFromSerializationPackage
import org.jetbrains.kotlinx.serialization.compiler.resolve.hasCompanionObjectAsSerializer
import org.jetbrains.kotlinx.serialization.compiler.resolve.hasSerializableAnnotationWithoutArgs
import org.jetbrains.kotlinx.serialization.compiler.resolve.isInternalSerializable
class SerializableJsTranslator(
val declaration: KtPureClassOrObject,
val descriptor: ClassDescriptor,
val translator: DeclarationBodyVisitor,
val context: TranslationContext
) : SerializableCodegen(descriptor, context.bindingContext()) {
@@ -63,9 +59,10 @@ class SerializableJsTranslator(
Namer.createObjectWithPrototypeFrom(context.getInnerNameForDescriptor(serializableDescriptor).makeRef())
)
)
val seenVar = jsFun.parameters[0].name.makeRef()
val seenVarsOffset = properties.serializableProperties.bitMaskSlotCount()
val seenVars = (0 until seenVarsOffset).map { jsFun.parameters[it].name.makeRef() }
for ((index, prop) in properties.serializableProperties.withIndex()) {
val paramRef = jsFun.parameters[index + 1].name.makeRef()
val paramRef = jsFun.parameters[index + seenVarsOffset].name.makeRef()
// assign this.a = a in else branch
val assignParamStmt = TranslationUtils.assignmentToBackingField(context, prop.descriptor, paramRef).makeStmt()
@@ -77,7 +74,7 @@ class SerializableJsTranslator(
JsThrow(JsNew(missingExceptionClassRef, listOf(JsStringLiteral(prop.name))))
}
// (seen & 1 << i == 0) -- not seen
val notSeenTest = propNotSeenTest(seenVar, index)
val notSeenTest = propNotSeenTest(seenVars[bitMaskSlotAt(index)], index)
+JsIf(notSeenTest, ifNotSeenStmt, assignParamStmt)
}
@@ -111,7 +108,7 @@ class SerializableJsTranslator(
context: TranslationContext
) {
if (serializableClass.isInternalSerializable)
SerializableJsTranslator(declaration, serializableClass, translator, context).generate()
SerializableJsTranslator(declaration, serializableClass, context).generate()
else if (serializableClass.hasSerializableAnnotationWithoutArgs && !serializableClass.hasCompanionObjectAsSerializer) {
throw CompilationException(
"@Serializable annotation on $serializableClass would be ignored because it is impossible to serialize it automatically. " +
@@ -72,7 +72,7 @@ class SerializerJsTranslator(descriptor: ClassDescriptor,
val pushClassFunc = serialDescImplClass.getFuncDesc(CallingConventions.addClassAnnotation).single()
val serialClassDescRef = JsNameRef(context.getNameForDescriptor(generatedSerialDescPropertyDescriptor), JsThisRef())
for (prop in orderedProperties) {
for (prop in serializableProperties) {
if (prop.transient) continue
val call = JsInvocation(
JsNameRef(context.getNameForDescriptor(addFunc), serialClassDescRef),
@@ -100,7 +100,7 @@ class SerializerJsTranslator(descriptor: ClassDescriptor,
}
override fun generateChildSerializersGetter(function: FunctionDescriptor) = generateFunction(function) { _, _ ->
val allSerializers = orderedProperties.map { requireNotNull(serializerTower(it)) { "Property ${it.name} must have a serializer" } }
val allSerializers = serializableProperties.map { requireNotNull(serializerTower(it)) { "Property ${it.name} must have a serializer" } }
+JsReturn(JsArrayLiteral(allSerializers))
}
@@ -160,7 +160,7 @@ class SerializerJsTranslator(descriptor: ClassDescriptor,
fun SerializableProperty.jsNameRef() = JsNameRef(ctx.getNameForDescriptor(descriptor), objRef)
// todo: internal serialization via virtual calls
val labeledProperties = orderedProperties.filter { !it.transient }
val labeledProperties = serializableProperties.filter { !it.transient }
for (index in labeledProperties.indices) {
val property = labeledProperties[index]
if (property.transient) continue
@@ -224,15 +224,15 @@ class SerializerJsTranslator(descriptor: ClassDescriptor,
+JsVars(JsVars.JsVar(indexVar.name), JsVars.JsVar(readAllVar.name, JsBooleanLiteral(false)))
// calculating bit mask vars
val blocksCnt = orderedProperties.size / 32 + 1
fun bitMaskOff(i: Int) = i / 32
val blocksCnt = serializableProperties.bitMaskSlotCount()
fun bitMaskOff(i: Int) = bitMaskSlotAt(i)
// var bitMask0 = 0, bitMask1 = 0...
val bitMasks = (0 until blocksCnt).map { JsNameRef(jsFun.scope.declareFreshName("bitMask$it")) }
+JsVars(bitMasks.map { JsVars.JsVar(it.name, JsIntLiteral(0)) }, false)
// var localProp0, localProp1, ...
val localProps = orderedProperties.mapIndexed {i, _ -> JsNameRef(jsFun.scope.declareFreshName("local$i")) }
val localProps = serializableProperties.mapIndexed { i, _ -> JsNameRef(jsFun.scope.declareFreshName("local$i")) }
+JsVars(localProps.map { JsVars.JsVar(it.name) }, true)
//input = input.readBegin(...)
@@ -241,8 +241,10 @@ class SerializerJsTranslator(descriptor: ClassDescriptor,
}
val inputVar = JsNameRef(jsFun.scope.declareFreshName("input"))
val readBeginF = decoderClass.getFuncDesc(CallingConventions.begin).single()
val readBeginCall = JsInvocation(JsNameRef(context.getNameForDescriptor(readBeginF), JsNameRef(jsFun.parameters[0].name)),
serialClassDescRef, JsArrayLiteral(typeParams))
val readBeginCall = JsInvocation(
JsNameRef(context.getNameForDescriptor(readBeginF), JsNameRef(jsFun.parameters[0].name)),
serialClassDescRef, JsArrayLiteral(typeParams)
)
+JsVars(JsVars.JsVar(inputVar.name, readBeginCall))
// while(true) {
@@ -252,20 +254,20 @@ class SerializerJsTranslator(descriptor: ClassDescriptor,
// index = input.readElement(classDesc)
val readElementF = context.getNameForDescriptor(inputClass.getFuncDesc(CallingConventions.decodeElementIndex).single())
+JsAstUtils.assignment(
indexVar,
JsInvocation(JsNameRef(readElementF, inputVar), serialClassDescRef)
indexVar,
JsInvocation(JsNameRef(readElementF, inputVar), serialClassDescRef)
).makeStmt()
// switch(index)
jsSwitch(indexVar) {
// -2: readAll = true
case(JsIntLiteral(-2)) {
+JsAstUtils.assignment(
readAllVar,
JsBooleanLiteral(true)
readAllVar,
JsBooleanLiteral(true)
).makeStmt()
}
// all properties
for ((i, property) in orderedProperties.withIndex()) {
for ((i, property) in serializableProperties.withIndex()) {
case(JsIntLiteral(i)) {
// input.readXxxElementValue
val sti = getSerialTypeInfo(property)
@@ -273,64 +275,72 @@ class SerializerJsTranslator(descriptor: ClassDescriptor,
val call: JsExpression = if (innerSerial == null) {
val unknownSer = (sti.elementMethodPrefix.isEmpty())
val readFunc =
inputClass.getFuncDesc("${CallingConventions.decode}${sti.elementMethodPrefix}${CallingConventions.elementPostfix}")
// if readElementValue, must have 3 parameters, if readXXXElementValue - 2
.single { !unknownSer || (it.valueParameters.size == 3) }
.let { context.getNameForDescriptor(it) }
inputClass.getFuncDesc("${CallingConventions.decode}${sti.elementMethodPrefix}${CallingConventions.elementPostfix}")
// if readElementValue, must have 3 parameters, if readXXXElementValue - 2
.single { !unknownSer || (it.valueParameters.size == 3) }
.let { context.getNameForDescriptor(it) }
val readArgs = mutableListOf(serialClassDescRef, JsIntLiteral(i))
if (unknownSer) readArgs.add(ExpressionVisitor.getObjectKClass(
this@SerializerJsTranslator.context,
property.type.toClassDescriptor!!
))
if (unknownSer) readArgs.add(
ExpressionVisitor.getObjectKClass(
this@SerializerJsTranslator.context,
property.type.toClassDescriptor!!
)
)
JsInvocation(JsNameRef(readFunc, inputVar), readArgs)
}
else {
val notSeenTest = propNotSeenTest(bitMasks[i / 32], i)
} else {
val notSeenTest = propNotSeenTest(bitMasks[bitMaskOff(i)], i)
val readFunc =
inputClass.getFuncDesc("${CallingConventions.decode}${sti.elementMethodPrefix}Serializable${CallingConventions.elementPostfix}").single()
.let { context.getNameForDescriptor(it) }
inputClass.getFuncDesc("${CallingConventions.decode}${sti.elementMethodPrefix}Serializable${CallingConventions.elementPostfix}")
.single()
.let { context.getNameForDescriptor(it) }
val updateFunc =
inputClass.getFuncDesc("${CallingConventions.update}${sti.elementMethodPrefix}Serializable${CallingConventions.elementPostfix}").single()
.let { context.getNameForDescriptor(it) }
JsConditional(notSeenTest,
JsInvocation(JsNameRef(readFunc, inputVar),
serialClassDescRef,
JsIntLiteral(i),
innerSerial),
JsInvocation(JsNameRef(updateFunc, inputVar),
serialClassDescRef,
JsIntLiteral(i),
innerSerial,
localProps[i]
)
inputClass.getFuncDesc("${CallingConventions.update}${sti.elementMethodPrefix}Serializable${CallingConventions.elementPostfix}")
.single()
.let { context.getNameForDescriptor(it) }
JsConditional(
notSeenTest,
JsInvocation(
JsNameRef(readFunc, inputVar),
serialClassDescRef,
JsIntLiteral(i),
innerSerial
),
JsInvocation(
JsNameRef(updateFunc, inputVar),
serialClassDescRef,
JsIntLiteral(i),
innerSerial,
localProps[i]
)
)
}
// localPropI = ...
+JsAstUtils.assignment(
localProps[i],
call
localProps[i],
call
).makeStmt()
// need explicit unit instance
if (sti.unit) {
+JsAstUtils.assignment(
localProps[i],
context.getQualifiedReference(property.type.builtIns.unit)
localProps[i],
context.getQualifiedReference(property.type.builtIns.unit)
).makeStmt()
}
// char unboxing crutch
if (KotlinBuiltIns.isCharOrNullableChar(property.type)) {
val coerceTo = TranslationUtils.getReturnTypeForCoercion(property.descriptor)
+JsAstUtils.assignment(
localProps[i],
TranslationUtils.coerce(context, localProps[i], coerceTo)
localProps[i],
TranslationUtils.coerce(context, localProps[i], coerceTo)
).makeStmt()
}
// bitMask[i] |= 1 << x
val bitPos = 1 shl (i % 32)
+JsBinaryOperation(JsBinaryOperator.ASG_BIT_OR,
bitMasks[i / 32],
JsIntLiteral(bitPos)
+JsBinaryOperation(
JsBinaryOperator.ASG_BIT_OR,
bitMasks[bitMaskOff(i)],
JsIntLiteral(bitPos)
).makeStmt()
// if (!readAll) break
+JsIf(JsAstUtils.not(readAllVar), JsBreak())
@@ -343,7 +353,7 @@ class SerializerJsTranslator(descriptor: ClassDescriptor,
// default: throw
default {
val excClassRef = serializableDescriptor.getClassFromSerializationPackage(SerialEntityNames.UNKNOWN_FIELD_EXC)
.let { context.translateQualifiedReference(it) }
.let { context.translateQualifiedReference(it) }
+JsThrow(JsNew(excClassRef, listOf(indexVar)))
}
}
@@ -351,17 +361,17 @@ class SerializerJsTranslator(descriptor: ClassDescriptor,
// input.readEnd(desc)
val readEndF = inputClass.getFuncDesc(CallingConventions.end).single()
.let { context.getNameForDescriptor(it) }
.let { context.getNameForDescriptor(it) }
+JsInvocation(
JsNameRef(readEndF, inputVar),
serialClassDescRef
JsNameRef(readEndF, inputVar),
serialClassDescRef
).makeStmt()
// deserialization constructor call
// todo: external deserialization with primary constructor and setters calls after resolution of KT-11586
val constrDesc = KSerializerDescriptorResolver.createLoadConstructorDescriptor(serializableDescriptor, context.bindingContext())
val constrRef = context.getInnerNameForDescriptor(constrDesc).makeRef()
val args: MutableList<JsExpression> = mutableListOf(bitMasks[0])
val args: MutableList<JsExpression> = bitMasks.toMutableList()
args += localProps
args += JsNullLiteral()
+JsReturn(JsInvocation(constrRef, args))
@@ -120,9 +120,17 @@ fun InstructionAdapter.genKOutputMethodCall(
)
}
internal fun InstructionAdapter.buildInternalConstructorDesc(propsStartVar: Int, bitMaskBase: Int, codegen: ClassBodyCodegen, args: List<SerializableProperty>): String {
val constructorDesc = StringBuilder("(I")
load(bitMaskBase, OPT_MASK_TYPE)
internal fun InstructionAdapter.buildInternalConstructorDesc(
propsStartVar: Int,
bitMaskBase: Int,
codegen: ClassBodyCodegen,
args: List<SerializableProperty>
): String {
val constructorDesc = StringBuilder("(")
repeat(args.bitMaskSlotCount()) {
constructorDesc.append("I")
load(bitMaskBase + it, Type.INT_TYPE)
}
var propVar = propsStartVar
for (property in args) {
val propertyType = codegen.typeMapper.mapType(property.type)
@@ -135,8 +143,10 @@ internal fun InstructionAdapter.buildInternalConstructorDesc(propsStartVar: Int,
return constructorDesc.toString()
}
internal fun ImplementationBodyCodegen.generateMethod(function: FunctionDescriptor,
block: InstructionAdapter.(JvmMethodSignature, ExpressionCodegen) -> Unit) {
internal fun ImplementationBodyCodegen.generateMethod(
function: FunctionDescriptor,
block: InstructionAdapter.(JvmMethodSignature, ExpressionCodegen) -> Unit
) {
this.functionCodegen.generateMethod(OtherOrigin(this.myClass.psiOrParent, function), function,
object : FunctionGenerationStrategy.CodegenBased(this.state) {
override fun doGenerateBody(codegen: ExpressionCodegen, signature: JvmMethodSignature) {
@@ -178,13 +178,15 @@ class SerializableCodegenImpl(
private fun InstructionAdapter.doGenerateConstructorImpl(exprCodegen: ExpressionCodegen) {
val seenMask = 1
var (propIndex, propOffset) = generateSuperSerializableCall(2)
val bitMaskOff = fun(it: Int): Int { return seenMask + bitMaskSlotAt(it) }
val bitMaskEnd = seenMask + properties.serializableProperties.bitMaskSlotCount()
var (propIndex, propOffset) = generateSuperSerializableCall(bitMaskEnd)
for (i in propIndex until properties.serializableProperties.size) {
val prop = properties[i]
val propType = prop.asmType
if (!prop.optional) {
// primary were validated before constructor call
genValidateProperty(i) { seenMask }
genValidateProperty(i, bitMaskOff)
val nonThrowLabel = Label()
ificmpne(nonThrowLabel)
genExceptionThrow(serializationExceptionMissingFieldName, prop.name)
@@ -194,12 +196,11 @@ class SerializableCodegenImpl(
load(propOffset, propType)
putfield(thisAsmType.internalName, prop.descriptor.name.asString(), propType.descriptor)
} else {
genValidateProperty(i) { seenMask }
genValidateProperty(i, bitMaskOff)
val setLbl = Label()
val nextLabel = Label()
ificmpeq(setLbl)
// setting field
// todo: validate nullability
load(0, thisAsmType)
load(propOffset, propType)
putfield(thisAsmType.internalName, prop.descriptor.name.asString(), propType.descriptor)
@@ -228,7 +229,7 @@ class SerializableCodegenImpl(
.forEach { (t, u) -> exprCodegen.genInitParam(t, u) }
// init blocks
// todo: proper order with other initializers
// todo: proper order with other initializers?
classCodegen.myClass.anonymousInitializers()
.forEach { exprCodegen.gen(it, Type.VOID_TYPE) }
areturn(Type.VOID_TYPE)
@@ -30,8 +30,8 @@ import org.jetbrains.org.objectweb.asm.Type
import org.jetbrains.org.objectweb.asm.commons.InstructionAdapter
class SerializerCodegenImpl(
private val codegen: ImplementationBodyCodegen,
serializableClass: ClassDescriptor
private val codegen: ImplementationBodyCodegen,
serializableClass: ClassDescriptor
) : SerializerCodegen(codegen.descriptor, codegen.bindingContext) {
@@ -89,7 +89,7 @@ class SerializerCodegenImpl(
}
invokespecial(descImplType.internalName, "<init>", "(Ljava/lang/String;${generatedSerializerType.descriptor})V", false)
store(descriptorVar, descImplType)
for (property in orderedProperties) {
for (property in serializableProperties) {
if (property.transient) continue
load(descriptorVar, descImplType)
aconst(property.name)
@@ -187,13 +187,13 @@ class SerializerCodegenImpl(
override fun generateChildSerializersGetter(function: FunctionDescriptor) {
codegen.generateMethod(function) { _, _ ->
val size = orderedProperties.size
val size = serializableProperties.size
iconst(size)
newarray(kSerializerType)
for (i in 0 until size) {
dup() // array
iconst(i) // index
val prop = orderedProperties[i]
val prop = serializableProperties[i]
assert(
stackValueSerializerInstanceFromSerializerWithoutSti(
codegen,
@@ -208,7 +208,7 @@ class SerializerCodegenImpl(
}
override fun generateSave(
function: FunctionDescriptor
function: FunctionDescriptor
) {
codegen.generateMethod(function) { signature, expressionCodegen ->
// fun save(output: KOutput, obj : T)
@@ -223,8 +223,9 @@ class SerializerCodegenImpl(
genArrayOfTypeParametersSerializers()
invokeinterface(
encoderType.internalName, CallingConventions.begin,
"(" + descType.descriptor + kSerializerArrayType.descriptor +
")" + kOutputType.descriptor)
"(" + descType.descriptor + kSerializerArrayType.descriptor +
")" + kOutputType.descriptor
)
store(outputVar, kOutputType)
if (serializableDescriptor.isInternalSerializable) {
val sig = StringBuilder("(${kOutputType.descriptor}${descType.descriptor}")
@@ -232,7 +233,7 @@ class SerializerCodegenImpl(
load(objVar, objType)
load(outputVar, kOutputType)
load(descVar, descType)
serializableDescriptor.declaredTypeParameters.forEachIndexed {i, _ ->
serializableDescriptor.declaredTypeParameters.forEachIndexed { i, _ ->
load(0, kSerializerType)
getfield(codegen.typeMapper.mapClass(codegen.descriptor).internalName, "$typeArgPrefix$i", kSerializerType.descriptor)
sig.append(kSerializerType.descriptor)
@@ -240,11 +241,11 @@ class SerializerCodegenImpl(
sig.append(")V")
invokevirtual(
objType.internalName, SerialEntityNames.WRITE_SELF_NAME.asString(),
sig.toString(), false)
}
else {
sig.toString(), false
)
} else {
// loop for all properties
val labeledProperties = orderedProperties.filter { !it.transient }
val labeledProperties = serializableProperties.filter { !it.transient }
for (index in labeledProperties.indices) {
val property = labeledProperties[index]
if (property.transient) continue
@@ -260,7 +261,8 @@ class SerializerCodegenImpl(
load(descVar, descType)
invokeinterface(
kOutputType.internalName, CallingConventions.end,
"(" + descType.descriptor + ")V")
"(" + descType.descriptor + ")V"
)
// return
areturn(Type.VOID_TYPE)
}
@@ -280,7 +282,7 @@ class SerializerCodegenImpl(
}
override fun generateLoad(
function: FunctionDescriptor
function: FunctionDescriptor
) {
codegen.generateMethod(function) { _, expressionCodegen ->
// fun load(input: KInput): T
@@ -289,9 +291,9 @@ class SerializerCodegenImpl(
val indexVar = 3
val readAllVar = 4
val bitMaskBase = 5
val blocksCnt = orderedProperties.size / OPT_MASK_BITS + 1
fun bitMaskOff(i: Int) = bitMaskBase + (i / OPT_MASK_BITS) * OPT_MASK_TYPE.size
val propsStartVar = bitMaskBase + OPT_MASK_TYPE.size * blocksCnt
val blocksCnt = serializableProperties.bitMaskSlotCount()
val bitMaskOff = fun(it: Int): Int { return bitMaskBase + bitMaskSlotAt(it) }
val propsStartVar = bitMaskBase + blocksCnt
stackSerialClassDesc(descVar)
// boolean readAll = false
iconst(0)
@@ -304,7 +306,7 @@ class SerializerCodegenImpl(
}
// initialize all prop vars
var propVar = propsStartVar
for (property in orderedProperties) {
for (property in serializableProperties) {
val propertyType = codegen.typeMapper.mapType(property.type)
stackValueDefault(propertyType)
store(propVar, propertyType)
@@ -316,8 +318,9 @@ class SerializerCodegenImpl(
genArrayOfTypeParametersSerializers()
invokeinterface(
decoderType.internalName, CallingConventions.begin,
"(" + descType.descriptor + kSerializerArrayType.descriptor +
")" + kInputType.descriptor)
"(" + descType.descriptor + kSerializerArrayType.descriptor +
")" + kInputType.descriptor
)
store(inputVar, kInputType)
// readElement: int index = input.readElement(classDesc)
val readElementLabel = Label()
@@ -326,10 +329,11 @@ class SerializerCodegenImpl(
load(descVar, descType)
invokeinterface(
kInputType.internalName, CallingConventions.decodeElementIndex,
"(" + descType.descriptor + ")I")
"(" + descType.descriptor + ")I"
)
store(indexVar, Type.INT_TYPE)
// switch(index)
val labeledProperties = orderedProperties.filter { !it.transient }
val labeledProperties = serializableProperties.filter { !it.transient }
val readAllLabel = Label()
val readEndLabel = Label()
val incorrectIndLabel = Label()
@@ -348,7 +352,7 @@ class SerializerCodegenImpl(
// loop for all properties
propVar = propsStartVar
var labelNum = 0
for ((index, property) in orderedProperties.withIndex()) {
for ((index, property) in serializableProperties.withIndex()) {
val propertyType = codegen.typeMapper.mapType(property.type)
if (!property.transient) {
// labelI:
@@ -382,7 +386,7 @@ class SerializerCodegenImpl(
// we can choose either it is read or update
val readLabel = Label()
val endL = Label()
genValidateProperty(index, ::bitMaskOff)
genValidateProperty(index, bitMaskOff)
ificmpeq(readLabel)
load(propVar, propertyType)
StackValue.coerce(propertyType, sti.type, this)
@@ -398,8 +402,7 @@ class SerializerCodegenImpl(
if (sti.unit) {
StackValue.putUnitInstance(this)
}
else {
} else {
StackValue.coerce(sti.type, propertyType, this)
}
store(propVar, propertyType)
@@ -425,8 +428,10 @@ class SerializerCodegenImpl(
visitLabel(readEndLabel)
load(inputVar, kInputType)
load(descVar, descType)
invokeinterface(kInputType.internalName, CallingConventions.end,
"(" + descType.descriptor + ")V")
invokeinterface(
kInputType.internalName, CallingConventions.end,
"(" + descType.descriptor + ")V"
)
if (!serializableDescriptor.isInternalSerializable) {
//validate all required (constructor) fields
val nonThrowLabel = Label()
@@ -435,10 +440,13 @@ class SerializerCodegenImpl(
if (property.optional || property.transient) {
// todo: Normal reporting of error
if (!property.isConstructorParameterWithDefault)
throw CompilationException("Property ${property.name} was declared as optional/transient but has no default value", null, null)
}
else {
genValidateProperty(i, ::bitMaskOff)
throw CompilationException(
"Property ${property.name} was declared as optional/transient but has no default value",
null,
null
)
} else {
genValidateProperty(i, bitMaskOff)
// todo: print name of each variable?
ificmpeq(throwLabel)
}
@@ -460,8 +468,9 @@ class SerializerCodegenImpl(
// result := ... <created object>
store(resultVar, serializableAsmType)
// set other properties
propVar = propsStartVar + properties.serializableConstructorProperties.map { codegen.typeMapper.mapType(it.type).size }.sum()
genSetSerializableStandaloneProperties(expressionCodegen, propVar, resultVar, ::bitMaskOff)
propVar = propsStartVar +
properties.serializableConstructorProperties.map { codegen.typeMapper.mapType(it.type).size }.sum()
genSetSerializableStandaloneProperties(expressionCodegen, propVar, resultVar, bitMaskOff)
// load result
load(resultVar, serializableAsmType)
// will return result
@@ -491,8 +500,7 @@ class SerializerCodegenImpl(
}
if (!properties.primaryConstructorWithDefaults) {
constructorDesc.append(")V")
}
else {
} else {
val cnt = properties.serializableConstructorProperties.size.coerceAtMost(32) //only 32 default values are supported
val mask = if (cnt == 32) -1 else ((1 shl cnt) - 1)
load(bitMaskBase, OPT_MASK_TYPE)
@@ -505,7 +513,8 @@ class SerializerCodegenImpl(
}
private fun InstructionAdapter.genSetSerializableStandaloneProperties(
expressionCodegen: ExpressionCodegen, propVarStart: Int, resultVar: Int, bitMaskPos: (Int) -> Int) {
expressionCodegen: ExpressionCodegen, propVarStart: Int, resultVar: Int, bitMaskPos: (Int) -> Int
) {
var propVar = propVarStart
val offset = properties.serializableConstructorProperties.size
for ((index, property) in properties.serializableStandaloneProperties.withIndex()) {
@@ -518,8 +527,7 @@ class SerializerCodegenImpl(
// if (seen)
// set
ificmpeq(nextLabel)
}
else {
} else {
// if (!seen)
// throw
// set
@@ -530,9 +538,10 @@ class SerializerCodegenImpl(
// generate setter call
val propertyType = codegen.typeMapper.mapType(property.type)
expressionCodegen.intermediateValueForProperty(property.descriptor, false, null,
StackValue.local(resultVar, serializableAsmType)).
store(StackValue.local(propVar, propertyType), this)
expressionCodegen.intermediateValueForProperty(
property.descriptor, false, null,
StackValue.local(resultVar, serializableAsmType)
).store(StackValue.local(propVar, propertyType), this)
propVar += propertyType.size
if (property.optional)
visitLabel(nextLabel)
@@ -280,15 +280,19 @@ object KSerializerDescriptorResolver {
val markerDesc = classDescriptor.getKSerializerConstructorMarker()
val markerType = markerDesc.toSimpleType()
val parameterDescsAsProps = SerializableProperties(classDescriptor, bindingContext).serializableProperties.map { it.descriptor }
val serializableProperties = SerializableProperties(classDescriptor, bindingContext).serializableProperties
val parameterDescsAsProps = serializableProperties.map { it.descriptor }
val bitMaskSlotsCount = serializableProperties.bitMaskSlotCount()
var i = 0
val consParams = mutableListOf<ValueParameterDescriptor>()
consParams.add(
ValueParameterDescriptorImpl(
functionDescriptor, null, i++, Annotations.EMPTY, Name.identifier("seen"), functionDescriptor.builtIns.intType, false,
false, false, null, functionDescriptor.source
repeat(bitMaskSlotsCount) {
consParams.add(
ValueParameterDescriptorImpl(
functionDescriptor, null, i++, Annotations.EMPTY, Name.identifier("seen$i"), functionDescriptor.builtIns.intType, false,
false, false, null, functionDescriptor.source
)
)
)
}
for (prop in parameterDescsAsProps) {
consParams.add(
ValueParameterDescriptorImpl(
@@ -73,4 +73,7 @@ class SerializableProperties(private val serializableClass: ClassDescriptor, val
val primaryConstructorWithDefaults = serializableClass.unsubstitutedPrimaryConstructor
?.original?.valueParameters?.any { it.declaresDefaultValue() } ?: false
}
}
internal fun List<SerializableProperty>.bitMaskSlotCount() = size / 32 + 1
internal fun bitMaskSlotAt(propertyIndex: Int) = propertyIndex / 32