Custom-serializable enums for IR
This commit is contained in:
+1
-1
@@ -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,
|
||||
|
||||
+102
@@ -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)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
+54
-36
@@ -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<IrConstructor>().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<IrConstructorCall>,
|
||||
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<IrExpression>) = 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<IrExpression> = 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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+2
-2
@@ -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)
|
||||
|
||||
+2
-2
@@ -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)
|
||||
|
||||
+2
@@ -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"
|
||||
|
||||
Reference in New Issue
Block a user