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