diff --git a/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/ir/GeneratorHelpers.kt b/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/ir/GeneratorHelpers.kt index 972a97d209d..6ffb9801afd 100644 --- a/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/ir/GeneratorHelpers.kt +++ b/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/ir/GeneratorHelpers.kt @@ -558,7 +558,7 @@ interface IrBuilderExtension { serializerClass = serializableDescriptor.getClassFromInternalSerializationPackage("CommonEnumSerializer") args = kType.toClassDescriptor!!.let { enumDesc -> listOf( - irString(enumDesc.name.toString()), + irString(enumDesc.serialName()), irCall(findEnumValuesMethod(enumDesc)), createArrayOfExpression( compilerContext.irBuiltIns.stringType, diff --git a/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/ir/SerializerForEnumsGenerator.kt b/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/ir/SerializerForEnumsGenerator.kt new file mode 100644 index 00000000000..ac70d49fc6b --- /dev/null +++ b/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/ir/SerializerForEnumsGenerator.kt @@ -0,0 +1,102 @@ +/* + * Copyright 2010-2019 JetBrains s.r.o. and Kotlin Programming Language contributors. + * Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file. + */ + +package org.jetbrains.kotlinx.serialization.compiler.backend.ir + +import org.jetbrains.kotlin.backend.common.BackendContext +import org.jetbrains.kotlin.descriptors.ClassDescriptor +import org.jetbrains.kotlin.descriptors.FunctionDescriptor +import org.jetbrains.kotlin.ir.builders.* +import org.jetbrains.kotlin.ir.declarations.IrClass +import org.jetbrains.kotlin.ir.declarations.IrVariable +import org.jetbrains.kotlin.ir.expressions.IrExpression +import org.jetbrains.kotlin.ir.expressions.impl.IrGetValueImpl +import org.jetbrains.kotlin.ir.symbols.IrFunctionSymbol +import org.jetbrains.kotlin.ir.util.functions +import org.jetbrains.kotlin.ir.util.properties +import org.jetbrains.kotlin.ir.util.referenceFunction +import org.jetbrains.kotlin.name.Name +import org.jetbrains.kotlin.resolve.BindingContext +import org.jetbrains.kotlin.resolve.DescriptorUtils +import org.jetbrains.kotlinx.serialization.compiler.resolve.* + +class SerializerForEnumsGenerator(irClass: IrClass, compilerContext: BackendContext, bindingContext: BindingContext) : + SerializerIrGenerator(irClass, compilerContext, bindingContext) { + + override fun generateSave(function: FunctionDescriptor) = irClass.contributeFunction(function) { saveFunc -> + fun irThis(): IrExpression = + IrGetValueImpl(startOffset, endOffset, saveFunc.dispatchReceiverParameter!!.symbol) + + val encoderClass = serializerDescriptor.getClassFromSerializationPackage(SerialEntityNames.ENCODER_CLASS) + val descriptorGetterSymbol = compilerContext.externalSymbols.referenceFunction(anySerialDescProperty?.getter!!) + val encodeEnum = encoderClass.referenceMethod(CallingConventions.encodeEnum) + val serialDescGetter = irGet(descriptorGetterSymbol.owner.returnType, irThis(), descriptorGetterSymbol) + + val ordinalProp = serializableIrClass.properties.single { it.name == Name.identifier("ordinal") }.getter!! + val getOrdinal = irInvoke(irGet(saveFunc.valueParameters[1]), ordinalProp.symbol) + val call = irInvoke(irGet(saveFunc.valueParameters[0]), encodeEnum, serialDescGetter, getOrdinal) + +call + } + + override fun generateLoad(function: FunctionDescriptor) = irClass.contributeFunction(function) { loadFunc -> + fun irThis(): IrExpression = + IrGetValueImpl(startOffset, endOffset, loadFunc.dispatchReceiverParameter!!.symbol) + + val decoderClass = serializerDescriptor.getClassFromSerializationPackage(SerialEntityNames.DECODER_CLASS) + val descriptorGetterSymbol = compilerContext.externalSymbols.referenceFunction(anySerialDescProperty?.getter!!) + val decode = decoderClass.referenceMethod(CallingConventions.decodeEnum) + val serialDescGetter = irGet(descriptorGetterSymbol.owner.returnType, irThis(), descriptorGetterSymbol) + + val valuesF = serializableIrClass.functions.single { it.name == DescriptorUtils.ENUM_VALUES } + val getValues = irInvoke(dispatchReceiver = null, callee = valuesF.symbol) + + val arrayGet = + compilerContext.ir.symbols.array.owner.functions.single { it.name == Name.identifier("get") }.symbol + val getValueByOrdinal = + irInvoke(getValues, arrayGet, irInvoke(irGet(loadFunc.valueParameters[0]), decode, serialDescGetter)) + +irReturn(getValueByOrdinal) + } + + override fun IrBlockBodyBuilder.instantiateNewDescriptor( + serialDescImplClass: ClassDescriptor, + correctThis: IrExpression + ): IrExpression { + val serialDescForEnums = serializerDescriptor + .getClassFromInternalSerializationPackage(SerialEntityNames.SERIAL_DESCRIPTOR_FOR_ENUM) + val ctor = + compilerContext.externalSymbols.referenceConstructor(serialDescForEnums.unsubstitutedPrimaryConstructor!!) + return irInvoke( + null, ctor, + irString(serialName), + typeHint = ctor.owner.returnType + ) + } + + override fun IrBlockBodyBuilder.addElementsContentToDescriptor( + serialDescImplClass: ClassDescriptor, + localDescriptor: IrVariable, + addFunction: IrFunctionSymbol + ) { + val enumEntries = serializableDescriptor.enumEntries() + for (entry in enumEntries) { + // regular .serialName() produces fqName here, which is kinda inconvenient for enum entry + val serialName = entry.annotations.serialNameValue ?: entry.name.toString() + val call = irInvoke( + irGet(localDescriptor), + addFunction, + irString(serialName), + irBoolean(false), + typeHint = compilerContext.irBuiltIns.unitType + ) + +call + // serialDesc.pushAnnotation(...) + copySerialInfoAnnotationsToDescriptor( + compilerContext.externalSymbols.referenceClass(entry).owner.annotations, + localDescriptor, + serialDescImplClass.referenceMethod(CallingConventions.addAnnotation) + ) + } + } +} \ No newline at end of file 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 d52ee6a53b3..e6564c51325 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 @@ -36,22 +36,20 @@ import org.jetbrains.kotlinx.serialization.compiler.resolve.SerialEntityNames.UN // Is creating synthetic origin is a good idea or not? object SERIALIZABLE_PLUGIN_ORIGIN : IrDeclarationOriginImpl("SERIALIZER") -class SerializerIrGenerator(val irClass: IrClass, override val compilerContext: BackendContext, bindingContext: BindingContext) : +open class SerializerIrGenerator(val irClass: IrClass, final override val compilerContext: BackendContext, bindingContext: BindingContext) : SerializerCodegen(irClass.descriptor, bindingContext), IrBuilderExtension { - override val translator: TypeTranslator = compilerContext.createTypeTranslator(serializableDescriptor.module) + final override val translator: TypeTranslator = compilerContext.createTypeTranslator(serializableDescriptor.module) private val _table = SymbolTable() - override val BackendContext.localSymbolTable: SymbolTable + final override val BackendContext.localSymbolTable: SymbolTable get() = _table - private val serializableIrClass = compilerContext.externalSymbols.referenceClass(serializableDescriptor).owner + protected val serializableIrClass = compilerContext.externalSymbols.referenceClass(serializableDescriptor).owner override fun generateSerialDesc() { val desc: PropertyDescriptor = generatedSerialDescPropertyDescriptor ?: return val serialDescImplClass = serializerDescriptor .getClassFromInternalSerializationPackage(SERIAL_DESCRIPTOR_CLASS_IMPL) - val serialDescImplConstructor = serialDescImplClass - .unsubstitutedPrimaryConstructor!! val addFuncS = serialDescImplClass.referenceMethod(CallingConventions.addElement) @@ -76,36 +74,14 @@ class SerializerIrGenerator(val irClass: IrClass, override val compilerContext: initIrBody.parent = irClass val ctor = irClass.declarations.filterIsInstance().find { it.isPrimary } ?: throw AssertionError("Serializer must have primary constructor") - val serialClassDescImplCtor = compilerContext.externalSymbols.referenceConstructor(serialDescImplConstructor) compilerContext.localSymbolTable.withScope(initIrBody.descriptor) { initIrBody.body = compilerContext.createIrBuilder(initIrBody.symbol).irBlockBody { val localDesc = irTemporary( - irInvoke( - null, serialClassDescImplCtor, - irString(serialName), if (isGeneratedSerializer) irGet(thisAsReceiverParameter) else irNull(), - typeHint = serialClassDescImplCtor.owner.returnType - ), + instantiateNewDescriptor(serialDescImplClass, irGet(thisAsReceiverParameter)), nameHint = "serialDesc" ) - fun addFieldCall(prop: SerializableProperty) = irInvoke( - irGet(localDesc), - addFuncS, - irString(prop.name), - irBoolean(prop.optional), - typeHint = compilerContext.irBuiltIns.unitType - ) - - for (classProp in serializableProperties) { - if (classProp.transient) continue - +addFieldCall(classProp) - // add property annotations - copySerialInfoAnnotationsToDescriptor( - classProp.irField.correspondingProperty?.annotations.orEmpty(), - localDesc, - serialDescImplClass.referenceMethod(CallingConventions.addAnnotation) - ) - } + addElementsContentToDescriptor(serialDescImplClass, localDesc, addFuncS) // add class annotations copySerialInfoAnnotationsToDescriptor( serializableIrClass.annotations, @@ -128,7 +104,46 @@ class SerializerIrGenerator(val irClass: IrClass, override val compilerContext: } } - private fun IrBlockBodyBuilder.copySerialInfoAnnotationsToDescriptor( + protected open fun IrBlockBodyBuilder.instantiateNewDescriptor( + serialDescImplClass: ClassDescriptor, + correctThis: IrExpression + ): IrExpression { + val serialDescImplConstructor = serialDescImplClass + .unsubstitutedPrimaryConstructor!! + val serialClassDescImplCtor = compilerContext.externalSymbols.referenceConstructor(serialDescImplConstructor) + return irInvoke( + null, serialClassDescImplCtor, + irString(serialName), if (isGeneratedSerializer) correctThis else irNull(), + typeHint = serialClassDescImplCtor.owner.returnType + ) + } + + protected open fun IrBlockBodyBuilder.addElementsContentToDescriptor( + serialDescImplClass: ClassDescriptor, + localDescriptor: IrVariable, + addFunction: IrFunctionSymbol + ) { + fun addFieldCall(prop: SerializableProperty) = irInvoke( + irGet(localDescriptor), + addFunction, + irString(prop.name), + irBoolean(prop.optional), + typeHint = compilerContext.irBuiltIns.unitType + ) + + for (classProp in serializableProperties) { + if (classProp.transient) continue + +addFieldCall(classProp) + // add property annotations + copySerialInfoAnnotationsToDescriptor( + classProp.irField.correspondingProperty?.annotations.orEmpty(), + localDescriptor, + serialDescImplClass.referenceMethod(CallingConventions.addAnnotation) + ) + } + } + + protected fun IrBlockBodyBuilder.copySerialInfoAnnotationsToDescriptor( annotations: List, receiver: IrVariable, method: IrFunctionSymbol @@ -182,7 +197,7 @@ class SerializerIrGenerator(val irClass: IrClass, override val compilerContext: /* Already implemented in .generateSerialClassDesc ? */ } - fun ClassDescriptor.referenceMethod(methodName: String) = + protected fun ClassDescriptor.referenceMethod(methodName: String) = getFuncDesc(methodName).single().let { compilerContext.externalSymbols.referenceFunction(it) } override fun generateSave(function: FunctionDescriptor) = irClass.contributeFunction(function, fromStubs = true) { saveFunc -> @@ -242,8 +257,8 @@ class SerializerIrGenerator(val irClass: IrClass, override val compilerContext: val (writeFunc, args: List) = if (innerSerial == null) { val f = kOutputClass.referenceMethod("${CallingConventions.encode}${sti.elementMethodPrefix}${CallingConventions.elementPostfix}") - val args = mutableListOf(irGet(localSerialDesc), irInt(index)) - if (sti.elementMethodPrefix != "Unit") args += property.irGet() + val args: MutableList = mutableListOf(irGet(localSerialDesc), irInt(index)) + if (sti.elementMethodPrefix != "Unit") args.add(property.irGet()) f to args } else { val f = @@ -444,10 +459,13 @@ class SerializerIrGenerator(val irClass: IrClass, override val compilerContext: context: BackendContext, bindingContext: BindingContext ) { - if (getSerializableClassDescriptorBySerializer(irClass.descriptor) != null) { + val serializableDesc = getSerializableClassDescriptorBySerializer(irClass.descriptor) ?: return + if (serializableDesc.isSerializableEnum()) { + SerializerForEnumsGenerator(irClass, context, bindingContext).generate() + } else { SerializerIrGenerator(irClass, context, bindingContext).generate() - irClass.patchDeclarationParents(irClass.parent) } + irClass.patchDeclarationParents(irClass.parent) } } } diff --git a/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/js/SerializerForEnumsTranslator.kt b/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/js/SerializerForEnumsTranslator.kt index 03f1d67a847..18c9df30325 100644 --- a/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/js/SerializerForEnumsTranslator.kt +++ b/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/js/SerializerForEnumsTranslator.kt @@ -28,7 +28,7 @@ class SerializerForEnumsTranslator( NoLookupLocation.FROM_BACKEND ).single() val ordinalRef = JsNameRef(context.getNameForDescriptor(ordinalProp), JsNameRef(jsFun.parameters[1].name)) - val encodeEnumF = ctx.getNameForDescriptor(encoderClass.getFuncDesc("encodeEnum").single()) + val encodeEnumF = ctx.getNameForDescriptor(encoderClass.getFuncDesc(CallingConventions.encodeEnum).single()) val call = JsInvocation(JsNameRef(encodeEnumF, JsNameRef(jsFun.parameters[0].name)), serialClassDescRef, ordinalRef) +call.makeStmt() } @@ -36,7 +36,7 @@ class SerializerForEnumsTranslator( override fun generateLoad(function: FunctionDescriptor) = generateFunction(function) { jsFun, ctx -> val decoderClass = serializerDescriptor.getClassFromSerializationPackage(SerialEntityNames.DECODER_CLASS) val serialClassDescRef = JsNameRef(context.getNameForDescriptor(anySerialDescProperty!!), JsThisRef()) - val decodeEnumF = ctx.getNameForDescriptor(decoderClass.getFuncDesc("decodeEnum").single()) + val decodeEnumF = ctx.getNameForDescriptor(decoderClass.getFuncDesc(CallingConventions.decodeEnum).single()) val valuesFunc = DescriptorUtils.getFunctionByName(serializableDescriptor.staticScope, DescriptorUtils.ENUM_VALUES) val decodeEnumCall = JsInvocation(JsNameRef(decodeEnumF, JsNameRef(jsFun.parameters[0].name)), serialClassDescRef) val resultCall = JsArrayAccess(JsInvocation(ctx.getInnerNameForDescriptor(valuesFunc).makeRef()), decodeEnumCall) diff --git a/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/jvm/SerializerForEnumsCodegen.kt b/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/jvm/SerializerForEnumsCodegen.kt index 2fdfb205962..2af773256df 100644 --- a/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/jvm/SerializerForEnumsCodegen.kt +++ b/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/jvm/SerializerForEnumsCodegen.kt @@ -22,7 +22,7 @@ class SerializerForEnumsCodegen( stackSerialClassDesc(null) load(objVar, serializableAsmType) invokevirtual(serializableAsmType.internalName, "ordinal", "()I", false) - invokeinterface(encoderType.internalName, "encodeEnum", "(${descType.descriptor}I)V") + invokeinterface(encoderType.internalName, CallingConventions.encodeEnum, "(${descType.descriptor}I)V") // return areturn(Type.VOID_TYPE) } @@ -36,7 +36,7 @@ class SerializerForEnumsCodegen( // input.decodeEnum(descriptor) load(inputVar, decoderType) stackSerialClassDesc(null) - invokeinterface(decoderType.internalName, "decodeEnum", "(${descType.descriptor})I") + invokeinterface(decoderType.internalName, CallingConventions.decodeEnum, "(${descType.descriptor})I") // return aload(serializableAsmType) areturn(serializableAsmType) diff --git a/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/resolve/NamingConventions.kt b/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/resolve/NamingConventions.kt index 7acb1fe4c54..6f79ec97b70 100644 --- a/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/resolve/NamingConventions.kt +++ b/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/resolve/NamingConventions.kt @@ -91,6 +91,8 @@ object CallingConventions { const val decode = "decode" const val update = "update" const val encode = "encode" + const val encodeEnum = "encodeEnum" + const val decodeEnum = "decodeEnum" const val decodeElementIndex = "decodeElementIndex" const val elementPostfix = "Element" const val shouldEncodeDefault = "shouldEncodeElementDefault"