From 2444a6680e68386dfaf9f74476cc482ee788d5fc Mon Sep 17 00:00:00 2001 From: Leonid Startsev Date: Wed, 17 Oct 2018 18:45:00 +0300 Subject: [PATCH] .shouldEncodeElementDefault for JVM --- .../backend/js/SerializerJsTranslator.kt | 3 +- .../compiler/backend/jvm/JVMCodegenUtil.kt | 10 ++ .../backend/jvm/SerializableCodegenImpl.kt | 102 ++++++++++++++---- .../backend/jvm/SerializerCodegenImpl.kt | 13 --- 4 files changed, 94 insertions(+), 34 deletions(-) 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 a84ec8c73a6..820ea16e6da 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 @@ -26,6 +26,7 @@ import org.jetbrains.kotlin.js.translate.context.TranslationContext import org.jetbrains.kotlin.js.translate.declaration.DeclarationBodyVisitor import org.jetbrains.kotlin.js.translate.declaration.DefaultPropertyTranslator import org.jetbrains.kotlin.js.translate.general.Translation +import org.jetbrains.kotlin.js.translate.intrinsic.functions.factories.TopLevelFIF.KOTLIN_EQUALS import org.jetbrains.kotlin.js.translate.utils.JsAstUtils import org.jetbrains.kotlin.js.translate.utils.JsDescriptorUtils import org.jetbrains.kotlin.js.translate.utils.TranslationUtils @@ -193,7 +194,7 @@ class SerializerJsTranslator(descriptor: ClassDescriptor, val defaultValue = initializersMap.getValue(property.descriptor)?.let { Translation.translateAsExpression(it, ctx) } ?: throw IllegalStateException("Optional property does not have an initializer?") - val partA = JsBinaryOperation(JsBinaryOperator.NEQ, property.jsNameRef(), defaultValue) + val partA = JsAstUtils.not(KOTLIN_EQUALS.apply(property.jsNameRef(), listOf(defaultValue), ctx)) val partB = JsInvocation(JsNameRef(shouldEncodeFunc, localOutputRef), serialClassDescRef, JsIntLiteral(index)) val cond = JsBinaryOperation(JsBinaryOperator.OR, partA, partB) 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 1b1b8b64fbc..f363b3fe1d7 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 @@ -361,3 +361,13 @@ fun AbstractSerialGenerator.getSerialTypeInfo(property: SerializableProperty, ty else -> throw AssertionError("Unexpected sort for $type") // should not happen } } + +fun InstructionAdapter.stackValueDefault(type: Type) { + when (type.sort) { + BOOLEAN, BYTE, SHORT, CHAR, INT -> iconst(0) + LONG -> lconst(0) + FLOAT -> fconst(0f) + DOUBLE -> dconst(0.0) + else -> aconst(null) + } +} \ No newline at end of file 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 1984346a84a..505d4980877 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 @@ -16,13 +16,13 @@ package org.jetbrains.kotlinx.serialization.compiler.backend.jvm -import org.jetbrains.kotlin.codegen.CompilationException -import org.jetbrains.kotlin.codegen.ExpressionCodegen -import org.jetbrains.kotlin.codegen.ImplementationBodyCodegen +import org.jetbrains.kotlin.codegen.* import org.jetbrains.kotlin.descriptors.ClassConstructorDescriptor import org.jetbrains.kotlin.descriptors.ClassDescriptor import org.jetbrains.kotlin.descriptors.FunctionDescriptor import org.jetbrains.kotlin.descriptors.PropertyDescriptor +import org.jetbrains.kotlin.lexer.KtTokens +import org.jetbrains.kotlin.psi.KtExpression import org.jetbrains.kotlin.psi.KtParameter import org.jetbrains.kotlin.resolve.BindingContext import org.jetbrains.kotlin.resolve.DescriptorUtils @@ -51,10 +51,24 @@ class SerializableCodegenImpl( private val descToProps = classCodegen.myClass.bodyPropertiesDescriptorsMap(classCodegen.bindingContext) - private val paramsToProps: Map = classCodegen.myClass.primaryPropertiesDescriptorsMap(classCodegen.bindingContext) + private val paramsToProps: Map = + classCodegen.myClass.primaryPropertiesDescriptorsMap(classCodegen.bindingContext) private fun getProp(prop: SerializableProperty) = descToProps[prop.descriptor] private fun getParam(prop: SerializableProperty) = paramsToProps[prop.descriptor] + + private fun initializersMapper(prop: SerializableProperty): Pair { + val maybeInit = + getProp(prop)?.let { it.delegateExpressionOrInitializer ?: throw AssertionError("${it.name} property must have initializer") } + + val initializer = maybeInit ?: getParam(prop)?.let { + it.defaultValue ?: throw AssertionError("${it.name} property must have initializer") + } + + return if (initializer == null) throw AssertionError("Can't find initializer for property ${prop.descriptor}") + else initializer to classCodegen.typeMapper.mapType(prop.type) + } + private val SerializableProperty.asmType get() = classCodegen.typeMapper.mapType(this.type) override fun generateInternalConstructor(constructorDescriptor: ClassConstructorDescriptor) { @@ -94,13 +108,58 @@ class SerializableCodegenImpl( myPropsStart = 0 } - for (i in myPropsStart until properties.serializableProperties.size) { - val property = properties[i] + fun emitEncoderCall(property: SerializableProperty, index: Int) { // output.writeXxxElementValue (desc, index, value) load(outputI, kOutputType) load(serialDescI, descType) - iconst(i) - genKOutputMethodCall(property, classCodegen, exprCodegen, thisAsmType, thisI, offsetI, generator = this@SerializableCodegenImpl) + iconst(index) + genKOutputMethodCall( + property, + classCodegen, + exprCodegen, + thisAsmType, + thisI, + offsetI, + generator = this@SerializableCodegenImpl + ) + } + + for (i in myPropsStart until properties.serializableProperties.size) { + val property = properties[i] + if (!property.optional) { + emitEncoderCall(property, i) + } else { + val writeLabel = Label() + val nonWriteLabel = Label() + // obj.prop != DEFAULT_VAL + ImplementationBodyCodegen.genPropertyOnStack( + this, + exprCodegen.context, + property.descriptor, + thisAsmType, + thisI, + classCodegen.state + ) + val propAsmType = classCodegen.typeMapper.mapType(property.type) + val lhs = StackValue.onStack(propAsmType) + val (expr, _) = initializersMapper(property) + exprCodegen.gen(expr, propAsmType) + val rhs = StackValue.onStack(propAsmType) + // INVOKESTATIC kotlin/jvm/internal/Intrinsics.areEqual (Ljava/lang/Object;Ljava/lang/Object;)Z + AsmUtil.genEqualsForExpressionsOnStack(KtTokens.EXCLEQ, lhs, rhs).put(Type.BOOLEAN_TYPE, null, this) + ifne(writeLabel) + + // output.shouldEncodeElementDefault(descriptor, i) + load(outputI, kOutputType) + load(serialDescI, descType) + iconst(i) + invokeinterface(kOutputType.internalName, CallingConventions.shouldEncodeDefault, "(${descType.descriptor}I)Z") + ifeq(nonWriteLabel) + + visitLabel(writeLabel) + emitEncoderCall(property, i) + visitLabel(nonWriteLabel) + } } areturn(Type.VOID_TYPE) @@ -201,24 +260,27 @@ class SerializableCodegenImpl( private fun needInitProperty(prop: SerializableProperty) = getProp(prop)?.let { classCodegen.shouldInitializeProperty(it) } ?: getParam(prop)?.hasDefaultValue() ?: throw IllegalStateException() - private fun ExpressionCodegen.genInitProperty(prop: SerializableProperty) - = getProp(prop)?.let { + private fun ExpressionCodegen.genInitProperty(prop: SerializableProperty) = getProp(prop)?.let { classCodegen.initializeProperty(this, it) } - ?: 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.descriptor.name.asString(), prop.asmType.descriptor) - } - ?: throw IllegalStateException() + ?: 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.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) + 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) } 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 d35dcf1de5f..e1b939548d8 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 @@ -534,17 +534,4 @@ class SerializerCodegenImpl( visitLabel(nextLabel) } } - - // todo: move to StackValue? - private fun InstructionAdapter.stackValueDefault(type: Type) { - when (type.sort) { - BOOLEAN, BYTE, SHORT, CHAR, INT -> iconst(0) - LONG -> lconst(0) - FLOAT -> fconst(0f) - DOUBLE -> dconst(0.0) - else -> aconst(null) - } - } - - }