diff --git a/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/ir/SerializerIrGenerator.kt b/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/ir/SerializerIrGenerator.kt index 5b1971137d5..6002aa69cb3 100644 --- a/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/ir/SerializerIrGenerator.kt +++ b/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/ir/SerializerIrGenerator.kt @@ -17,9 +17,11 @@ import org.jetbrains.kotlin.ir.expressions.impl.* import org.jetbrains.kotlin.ir.symbols.IrConstructorSymbol import org.jetbrains.kotlin.ir.symbols.IrFunctionSymbol import org.jetbrains.kotlin.ir.types.* -import org.jetbrains.kotlin.ir.util.* +import org.jetbrains.kotlin.ir.util.constructors +import org.jetbrains.kotlin.ir.util.patchDeclarationParents +import org.jetbrains.kotlin.ir.util.referenceFunction +import org.jetbrains.kotlin.ir.util.withScope import org.jetbrains.kotlin.resolve.BindingContext -import org.jetbrains.kotlin.resolve.descriptorUtil.module import org.jetbrains.kotlin.types.KotlinType import org.jetbrains.kotlin.util.OperatorNameConventions import org.jetbrains.kotlinx.serialization.compiler.backend.common.SerializerCodegen @@ -331,8 +333,7 @@ open class SerializerIrGenerator(val irClass: IrClass, final override val compil val flagVar = irTemporaryVar(irBoolean(true), "flag") val indexVar = irTemporaryVar(irInt(0), "index") -// val readAll = irTemporaryVar(irBoolean(false), "readAll", parent = loadFunc) -// + // calculating bit mask vars val blocksCnt = serializableProperties.bitMaskSlotCount() @@ -358,54 +359,61 @@ open class SerializerIrGenerator(val irClass: IrClass, final override val compil ) val localInput = irTemporary(call, "input") - +irWhile().also { loop -> + // prepare all .decodeXxxElement calls + val decoderCalls: List> = + serializableProperties.mapIndexed { index, property -> + val body = irBlock { + val sti = getSerialTypeInfo(property) + val innerSerial = serializerInstance( + this@SerializerIrGenerator, + loadFunc.dispatchReceiverParameter!!, + sti.serializer, + property.module, + property.type, + genericIndex = property.genericIndex + ) + // todo: update + val decodeFuncToCall = + (if (innerSerial != null) "${CallingConventions.decode}${sti.elementMethodPrefix}Serializable${CallingConventions.elementPostfix}" + else "${CallingConventions.decode}${sti.elementMethodPrefix}${CallingConventions.elementPostfix}") + .let { + inputClass.referenceMethod(it) + } + val typeArgs = + if (decodeFuncToCall.descriptor.typeParameters.isNotEmpty()) listOf(property.type.toIrType()) else listOf() + val args = mutableListOf(localSerialDesc.get(), irInt(index)) + if (innerSerial != null) + args.add(innerSerial) + // local$i = localInput.decode...(...) + +irSetVar( + localProps[index].symbol, + irInvoke(localInput.get(), decodeFuncToCall, typeArgs, args, returnTypeHint = property.type.toIrType()) + ) + // bitMask[i] |= 1 << x + val bitPos = 1 shl (index % 32) + val or = irBinOp(OperatorNameConventions.OR, bitMasks[index / 32].get(), irInt(bitPos)) + +irSetVar(bitMasks[index / 32].symbol, or) + } + index to body + } + + // if (decoder.decodeSequentially()) + val decodeSequentiallyCall = irInvoke(localInput.get(), inputClass.referenceMethod(CallingConventions.decodeSequentially)) + + val sequentialPart = irBlock { + decoderCalls.forEach { (_, expr) -> +expr.deepCopyWithVariables() } + } + + val byIndexPart: IrExpression = irWhile().also { loop -> loop.condition = flagVar.get() loop.body = irBlock { val readElementF = inputClass.referenceMethod(CallingConventions.decodeElementIndex) +irSetVar(indexVar.symbol, irInvoke(localInput.get(), readElementF, localSerialDesc.get())) +irWhen { - // if index == -2 (READ_ALL) todo... - // if index == -1 (READ_DONE) break loop +IrBranchImpl(irEquals(indexVar.get(), irInt(-1)), irSetVar(flagVar.symbol, irBoolean(false))) - val branchBodies: List> = - serializableProperties.mapIndexed { index, property -> - val body = irBlock { - val sti = getSerialTypeInfo(property) - val innerSerial = serializerInstance( - this@SerializerIrGenerator, - loadFunc.dispatchReceiverParameter!!, - sti.serializer, - property.module, - property.type, - genericIndex = property.genericIndex - ) - // todo: update - val decodeFuncToCall = - (if (innerSerial != null) "${CallingConventions.decode}${sti.elementMethodPrefix}Serializable${CallingConventions.elementPostfix}" - else "${CallingConventions.decode}${sti.elementMethodPrefix}${CallingConventions.elementPostfix}") - .let { - inputClass.referenceMethod(it) - } - val typeArgs = - if (decodeFuncToCall.descriptor.typeParameters.isNotEmpty()) listOf(property.type.toIrType()) else listOf() - val args = mutableListOf(localSerialDesc.get(), irInt(index)) - if (innerSerial != null) - args.add(innerSerial) - // local$i = localInput.decode...(...) - +irSetVar( - localProps[index].symbol, - irInvoke(localInput.get(), decodeFuncToCall, typeArgs, args, returnTypeHint = property.type.toIrType()) - ) - // bitMask[i] |= 1 << x - val bitPos = 1 shl (index % 32) - val or = irBinOp(OperatorNameConventions.OR, bitMasks[index / 32].get(), irInt(bitPos)) - +irSetVar(bitMasks[index / 32].symbol, or) - } - index to body - } - branchBodies.forEach { (i, e) -> +IrBranchImpl(irEquals(indexVar.get(), irInt(i)), e) } + decoderCalls.forEach { (i, e) -> +IrBranchImpl(irEquals(indexVar.get(), irInt(i)), e) } // throw exception on unknown field val exceptionCtor = @@ -425,6 +433,8 @@ open class SerializerIrGenerator(val irClass: IrClass, final override val compil } } + +irIfThenElse(compilerContext.irBuiltIns.unitType, decodeSequentiallyCall, sequentialPart, byIndexPart) + //input.endStructure(...) val endFunc = inputClass.referenceMethod(CallingConventions.end) +irInvoke( diff --git a/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/js/SerializerJsTranslator.kt b/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/js/SerializerJsTranslator.kt index 241f399981a..6442a8c7b00 100644 --- a/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/js/SerializerJsTranslator.kt +++ b/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/js/SerializerJsTranslator.kt @@ -244,8 +244,7 @@ open class SerializerJsTranslator( // var index = -1, readAll = false val indexVar = JsNameRef(jsFun.scope.declareFreshName("index")) - val readAllVar = JsNameRef(jsFun.scope.declareFreshName("readAll")) - +JsVars(JsVars.JsVar(indexVar.name), JsVars.JsVar(readAllVar.name, JsBooleanLiteral(false))) + +JsVars(JsVars.JsVar(indexVar.name)) // calculating bit mask vars val blocksCnt = serializableProperties.bitMaskSlotCount() @@ -283,14 +282,7 @@ open class SerializerJsTranslator( ).makeStmt() // switch(index) jsSwitch(indexVar) { - // -2: readAll = true - case(JsIntLiteral(-2)) { - +JsAstUtils.assignment( - readAllVar, - JsBooleanLiteral(true) - ).makeStmt() - } - // all properties +// all properties for ((i, property) in serializableProperties.withIndex()) { case(JsIntLiteral(i)) { // input.readXxxElementValue @@ -366,8 +358,7 @@ open class SerializerJsTranslator( bitMasks[bitMaskOff(i)], JsIntLiteral(bitPos) ).makeStmt() - // if (!readAll) break - +JsIf(JsAstUtils.not(readAllVar), JsBreak()) + +JsBreak() } } // case -1: break loop diff --git a/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/jvm/JVMCodegenUtil.kt b/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/jvm/JVMCodegenUtil.kt index e3ea9b4b300..15b9fc7300b 100644 --- a/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/jvm/JVMCodegenUtil.kt +++ b/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/jvm/JVMCodegenUtil.kt @@ -76,9 +76,8 @@ val OPT_MASK_TYPE: Type = Type.INT_TYPE val OPT_MASK_BITS = 32 // compare with zero. if result == 0, property was not seen. -internal fun InstructionAdapter.genValidateProperty(index: Int, bitMaskPos: (Int) -> Int) { - val addr = bitMaskPos(index) - load(addr, OPT_MASK_TYPE) +internal fun InstructionAdapter.genValidateProperty(index: Int, bitMaskAddress: Int) { + load(bitMaskAddress, OPT_MASK_TYPE) iconst(1 shl (index % OPT_MASK_BITS)) and(OPT_MASK_TYPE) iconst(0) diff --git a/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/jvm/SerializableCodegenImpl.kt b/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/jvm/SerializableCodegenImpl.kt index 28b047cdd4a..e63e2fd782f 100644 --- a/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/jvm/SerializableCodegenImpl.kt +++ b/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/jvm/SerializableCodegenImpl.kt @@ -188,7 +188,7 @@ class SerializableCodegenImpl( val propType = prop.asmType if (!prop.optional) { // primary were validated before constructor call - genValidateProperty(i, bitMaskOff) + genValidateProperty(i, bitMaskOff(i)) val nonThrowLabel = Label() ificmpne(nonThrowLabel) genExceptionThrow(serializationExceptionMissingFieldName, prop.name) @@ -198,7 +198,7 @@ class SerializableCodegenImpl( load(propOffset, propType) putfield(thisAsmType.internalName, prop.descriptor.name.asString(), propType.descriptor) } else { - genValidateProperty(i, bitMaskOff) + genValidateProperty(i, bitMaskOff(i)) val setLbl = Label() val nextLabel = Label() ificmpeq(setLbl) diff --git a/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/jvm/SerializerCodegenImpl.kt b/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/jvm/SerializerCodegenImpl.kt index a6e2ea2ac0b..4d3688a7212 100644 --- a/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/jvm/SerializerCodegenImpl.kt +++ b/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/jvm/SerializerCodegenImpl.kt @@ -299,15 +299,11 @@ open class SerializerCodegenImpl( val inputVar = 1 val descVar = 2 val indexVar = 3 - val readAllVar = 4 - val bitMaskBase = 5 + val bitMaskBase = 4 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) - store(readAllVar, Type.BOOLEAN_TYPE) // initialize bit mask for (i in 0 until blocksCnt) { //int bitMaskN = 0 @@ -332,8 +328,31 @@ open class SerializerCodegenImpl( ")" + kInputType.descriptor ) store(inputVar, kInputType) - // readElement: int index = input.readElement(classDesc) val readElementLabel = Label() + val readEndLabel = Label() + // if (decoder.decodeSequentially) + load(inputVar, kInputType) + invokeinterface( + kInputType.internalName, CallingConventions.decodeSequentially, + "()Z" + ) + ifeq(readElementLabel) + // decodeSequentially = true + propVar = propsStartVar + for ((index, property) in serializableProperties.withIndex()) { + val propertyType = codegen.typeMapper.mapType(property.type) + callReadProperty(property, propertyType, index, inputVar, descVar, -1, propVar) + propVar += propertyType.size + } + // set all bit masks to true + for (maskVar in bitMaskBase until propsStartVar) { + iconst(Int.MAX_VALUE) + store(maskVar, OPT_MASK_TYPE) + } + // go to end + goTo(readEndLabel) + // branch with decodeSequentially = false + // readElement: int index = input.readElement(classDesc) visitLabel(readElementLabel) load(inputVar, kInputType) load(descVar, descType) @@ -344,78 +363,24 @@ open class SerializerCodegenImpl( store(indexVar, Type.INT_TYPE) // switch(index) val labeledProperties = serializableProperties.filter { !it.transient } - val readAllLabel = Label() - val readEndLabel = Label() val incorrectIndLabel = Label() - val labels = arrayOfNulls