Custom-serializable enums for JS
This commit is contained in:
-4
@@ -82,10 +82,6 @@ fun AbstractSerialGenerator.getSerialTypeInfo(property: SerializableProperty): S
|
||||
)
|
||||
SerializableInfo(serializer)
|
||||
}
|
||||
T.isEnum() -> {
|
||||
val serializer = property.module.findClassAcrossModuleDependencies(enumSerializerId)
|
||||
SerializableInfo(serializer)
|
||||
}
|
||||
else -> {
|
||||
val serializer =
|
||||
findTypeSerializerOrContext(property.module, property.type, property.descriptor.findPsi())
|
||||
|
||||
+6
-3
@@ -31,8 +31,7 @@ import org.jetbrains.kotlin.resolve.descriptorUtil.getSuperClassNotAny
|
||||
import org.jetbrains.kotlin.types.KotlinType
|
||||
import org.jetbrains.kotlin.types.typeUtil.isTypeParameter
|
||||
import org.jetbrains.kotlinx.serialization.compiler.backend.common.*
|
||||
import org.jetbrains.kotlinx.serialization.compiler.backend.jvm.objectSerializerId
|
||||
import org.jetbrains.kotlinx.serialization.compiler.backend.jvm.referenceArraySerializerId
|
||||
import org.jetbrains.kotlinx.serialization.compiler.backend.jvm.*
|
||||
import org.jetbrains.kotlinx.serialization.compiler.resolve.*
|
||||
|
||||
internal class JsBlockBuilder {
|
||||
@@ -147,9 +146,13 @@ internal fun AbstractSerialGenerator.serializerInstance(
|
||||
return context.serializerObjectGetter(serializerClass)
|
||||
} else {
|
||||
var args = when {
|
||||
serializerClass.isSerializerWhichRequiersKClass() -> listOf(
|
||||
serializerClass.classId == contextSerializerId || serializerClass.classId == polymorphicSerializerId -> listOf(
|
||||
ExpressionVisitor.getObjectKClass(context, kType.toClassDescriptor!!)
|
||||
)
|
||||
serializerClass.classId == enumSerializerId -> listOf(
|
||||
ExpressionVisitor.getObjectKClass(context, kType.toClassDescriptor!!),
|
||||
JsStringLiteral(kType.serialName())
|
||||
)
|
||||
serializerClass.classId == objectSerializerId -> listOf(
|
||||
JsStringLiteral(kType.serialName()),
|
||||
context.serializerObjectGetter(kType.toClassDescriptor!!)
|
||||
|
||||
+79
@@ -0,0 +1,79 @@
|
||||
/*
|
||||
* 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.js
|
||||
|
||||
import org.jetbrains.kotlin.descriptors.ClassDescriptor
|
||||
import org.jetbrains.kotlin.descriptors.FunctionDescriptor
|
||||
import org.jetbrains.kotlin.incremental.components.NoLookupLocation
|
||||
import org.jetbrains.kotlin.js.backend.ast.*
|
||||
import org.jetbrains.kotlin.js.translate.context.TranslationContext
|
||||
import org.jetbrains.kotlin.js.translate.declaration.DeclarationBodyVisitor
|
||||
import org.jetbrains.kotlin.name.Name
|
||||
import org.jetbrains.kotlin.resolve.DescriptorUtils
|
||||
import org.jetbrains.kotlinx.serialization.compiler.resolve.*
|
||||
|
||||
class SerializerForEnumsTranslator(
|
||||
descriptor: ClassDescriptor,
|
||||
translator: DeclarationBodyVisitor,
|
||||
context: TranslationContext
|
||||
) : SerializerJsTranslator(descriptor, translator, context) {
|
||||
override fun generateSave(function: FunctionDescriptor) = generateFunction(function) { jsFun, ctx ->
|
||||
val encoderClass = serializerDescriptor.getClassFromSerializationPackage(SerialEntityNames.ENCODER_CLASS)
|
||||
val serialClassDescRef = JsNameRef(context.getNameForDescriptor(anySerialDescProperty!!), JsThisRef())
|
||||
val ordinalProp = serializableDescriptor.unsubstitutedMemberScope.getContributedVariables(
|
||||
Name.identifier("ordinal"),
|
||||
NoLookupLocation.FROM_BACKEND
|
||||
).single()
|
||||
val ordinalRef = JsNameRef(context.getNameForDescriptor(ordinalProp), JsNameRef(jsFun.parameters[1].name))
|
||||
val encodeEnumF = ctx.getNameForDescriptor(encoderClass.getFuncDesc("encodeEnum").single())
|
||||
val call = JsInvocation(JsNameRef(encodeEnumF, JsNameRef(jsFun.parameters[0].name)), serialClassDescRef, ordinalRef)
|
||||
+call.makeStmt()
|
||||
}
|
||||
|
||||
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 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)
|
||||
+JsReturn(resultCall)
|
||||
}
|
||||
|
||||
override fun instantiateNewDescriptor(
|
||||
context: TranslationContext,
|
||||
correctThis: JsExpression,
|
||||
baseSerialDescImplClass: ClassDescriptor
|
||||
): JsExpression {
|
||||
val serialDescForEnums = serializerDescriptor
|
||||
.getClassFromInternalSerializationPackage(SerialEntityNames.SERIAL_DESCRIPTOR_FOR_ENUM)
|
||||
val ctor = serialDescForEnums.unsubstitutedPrimaryConstructor!!
|
||||
return JsNew(
|
||||
context.getInnerReference(ctor),
|
||||
listOf(JsStringLiteral(serialName))
|
||||
)
|
||||
}
|
||||
|
||||
override fun addElementsContentToDescriptor(
|
||||
context: TranslationContext,
|
||||
serialDescriptorInThis: JsNameRef,
|
||||
addElementFunction: FunctionDescriptor,
|
||||
pushAnnotationFunction: FunctionDescriptor
|
||||
) {
|
||||
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 = JsInvocation(
|
||||
JsNameRef(context.getNameForDescriptor(addElementFunction), serialDescriptorInThis),
|
||||
JsStringLiteral(serialName)
|
||||
)
|
||||
translator.addInitializerStatement(call.makeStmt())
|
||||
// serialDesc.pushAnnotation(...)
|
||||
pushAnnotationsInto(entry, pushAnnotationFunction, serialDescriptorInThis)
|
||||
}
|
||||
}
|
||||
}
|
||||
+58
-30
@@ -41,11 +41,13 @@ import org.jetbrains.kotlinx.serialization.compiler.resolve.*
|
||||
import org.jetbrains.kotlinx.serialization.compiler.resolve.SerialEntityNames.SERIAL_DESCRIPTOR_CLASS_IMPL
|
||||
import org.jetbrains.kotlinx.serialization.compiler.resolve.SerialEntityNames.typeArgPrefix
|
||||
|
||||
class SerializerJsTranslator(descriptor: ClassDescriptor,
|
||||
val translator: DeclarationBodyVisitor,
|
||||
val context: TranslationContext) : SerializerCodegen(descriptor, context.bindingContext()) {
|
||||
open class SerializerJsTranslator(
|
||||
descriptor: ClassDescriptor,
|
||||
val translator: DeclarationBodyVisitor,
|
||||
val context: TranslationContext
|
||||
) : SerializerCodegen(descriptor, context.bindingContext()) {
|
||||
|
||||
private fun generateFunction(descriptor: FunctionDescriptor, bodyGen: JsBlockBuilder.(JsFunction, TranslationContext) -> Unit) {
|
||||
internal fun generateFunction(descriptor: FunctionDescriptor, bodyGen: JsBlockBuilder.(JsFunction, TranslationContext) -> Unit) {
|
||||
val f = context.buildFunction(descriptor, bodyGen)
|
||||
translator.addFunction(descriptor, f, null)
|
||||
}
|
||||
@@ -54,16 +56,10 @@ class SerializerJsTranslator(descriptor: ClassDescriptor,
|
||||
override fun generateSerialDesc() {
|
||||
val desc = generatedSerialDescPropertyDescriptor ?: return
|
||||
val serialDescImplClass = serializerDescriptor
|
||||
.getClassFromInternalSerializationPackage(SERIAL_DESCRIPTOR_CLASS_IMPL)
|
||||
val serialDescImplConstructor = serialDescImplClass
|
||||
.unsubstitutedPrimaryConstructor!!
|
||||
|
||||
.getClassFromInternalSerializationPackage(SERIAL_DESCRIPTOR_CLASS_IMPL)
|
||||
// this.serialDesc = new SerialDescImpl(...)
|
||||
val correctThis = context.getDispatchReceiver(JsDescriptorUtils.getReceiverParameterForDeclaration(desc.containingDeclaration))
|
||||
val value = JsNew(
|
||||
context.getInnerReference(serialDescImplConstructor),
|
||||
listOf(JsStringLiteral(serialName), if (isGeneratedSerializer) correctThis else JsNullLiteral())
|
||||
)
|
||||
val value = instantiateNewDescriptor(context, correctThis, serialDescImplClass)
|
||||
val assgmnt = TranslationUtils.assignmentToBackingField(context, desc, value)
|
||||
translator.addInitializerStatement(assgmnt.makeStmt())
|
||||
|
||||
@@ -73,23 +69,44 @@ class SerializerJsTranslator(descriptor: ClassDescriptor,
|
||||
val pushClassFunc = serialDescImplClass.getFuncDesc(CallingConventions.addClassAnnotation).single()
|
||||
val serialClassDescRef = JsNameRef(context.getNameForDescriptor(generatedSerialDescPropertyDescriptor), JsThisRef())
|
||||
|
||||
for (prop in serializableProperties) {
|
||||
if (prop.transient) continue
|
||||
val call = JsInvocation(
|
||||
JsNameRef(context.getNameForDescriptor(addFunc), serialClassDescRef),
|
||||
JsStringLiteral(prop.name),
|
||||
JsBooleanLiteral(prop.optional)
|
||||
)
|
||||
translator.addInitializerStatement(call.makeStmt())
|
||||
// serialDesc.pushAnnotation(...)
|
||||
pushAnnotationsInto(prop.descriptor, pushFunc, serialClassDescRef)
|
||||
}
|
||||
addElementsContentToDescriptor(context, serialClassDescRef, addFunc, pushFunc)
|
||||
|
||||
// push class annotations
|
||||
pushAnnotationsInto(serializableDescriptor, pushClassFunc, serialClassDescRef)
|
||||
}
|
||||
|
||||
private fun pushAnnotationsInto(annotated: Annotated, pushFunction: DeclarationDescriptor, intoRef: JsNameRef) {
|
||||
protected open fun instantiateNewDescriptor(
|
||||
context: TranslationContext,
|
||||
correctThis: JsExpression,
|
||||
baseSerialDescImplClass: ClassDescriptor
|
||||
): JsExpression {
|
||||
val serialDescImplConstructor = baseSerialDescImplClass.unsubstitutedPrimaryConstructor!!
|
||||
return JsNew(
|
||||
context.getInnerReference(serialDescImplConstructor),
|
||||
listOf(JsStringLiteral(serialName), if (isGeneratedSerializer) correctThis else JsNullLiteral())
|
||||
)
|
||||
}
|
||||
|
||||
protected open fun addElementsContentToDescriptor(
|
||||
context: TranslationContext,
|
||||
serialDescriptorInThis: JsNameRef,
|
||||
addElementFunction: FunctionDescriptor,
|
||||
pushAnnotationFunction: FunctionDescriptor
|
||||
) {
|
||||
for (prop in serializableProperties) {
|
||||
if (prop.transient) continue
|
||||
val call = JsInvocation(
|
||||
JsNameRef(context.getNameForDescriptor(addElementFunction), serialDescriptorInThis),
|
||||
JsStringLiteral(prop.name),
|
||||
JsBooleanLiteral(prop.optional)
|
||||
)
|
||||
translator.addInitializerStatement(call.makeStmt())
|
||||
// serialDesc.pushAnnotation(...)
|
||||
pushAnnotationsInto(prop.descriptor, pushAnnotationFunction, serialDescriptorInThis)
|
||||
}
|
||||
}
|
||||
|
||||
protected fun pushAnnotationsInto(annotated: Annotated, pushFunction: DeclarationDescriptor, intoRef: JsNameRef) {
|
||||
for ((annotationClass , args, _) in annotated.annotationsWithArguments()) {
|
||||
val argExprs = args.map { arg ->
|
||||
Translation.translateAsExpression(arg.getArgumentExpression()!!, context)
|
||||
@@ -107,11 +124,13 @@ class SerializerJsTranslator(descriptor: ClassDescriptor,
|
||||
|
||||
override fun generateSerializableClassProperty(property: PropertyDescriptor) {
|
||||
val propDesc = generatedSerialDescPropertyDescriptor ?: return
|
||||
val propTranslator = DefaultPropertyTranslator(propDesc, context,
|
||||
translator.getBackingFieldReference(propDesc))
|
||||
val propTranslator = DefaultPropertyTranslator(
|
||||
propDesc, context,
|
||||
translator.getBackingFieldReference(propDesc)
|
||||
)
|
||||
val getterDesc = propDesc.getter!!
|
||||
val getterExpr = context.getFunctionObject(getterDesc)
|
||||
.apply { propTranslator.generateDefaultGetterFunction(getterDesc, this) }
|
||||
.apply { propTranslator.generateDefaultGetterFunction(getterDesc, this) }
|
||||
translator.addProperty(propDesc, getterExpr, null)
|
||||
}
|
||||
|
||||
@@ -131,7 +150,7 @@ class SerializerJsTranslator(descriptor: ClassDescriptor,
|
||||
context.addDeclarationStatement(f.makeStmt())
|
||||
}
|
||||
|
||||
private fun TranslationContext.referenceMethod(clazz: ClassDescriptor, name: String) =
|
||||
protected fun TranslationContext.referenceMethod(clazz: ClassDescriptor, name: String) =
|
||||
getNameForDescriptor(clazz.getFuncDesc(name).single())
|
||||
|
||||
override fun generateSave(function: FunctionDescriptor) = generateFunction(function) { jsFun, ctx ->
|
||||
@@ -382,9 +401,18 @@ class SerializerJsTranslator(descriptor: ClassDescriptor,
|
||||
}
|
||||
|
||||
companion object {
|
||||
fun translate(declaration: KtPureClassOrObject, descriptor: ClassDescriptor, translator: DeclarationBodyVisitor, context: TranslationContext) {
|
||||
if (getSerializableClassDescriptorBySerializer(descriptor) != null)
|
||||
fun translate(
|
||||
declaration: KtPureClassOrObject,
|
||||
descriptor: ClassDescriptor,
|
||||
translator: DeclarationBodyVisitor,
|
||||
context: TranslationContext
|
||||
) {
|
||||
val serializableDesc = getSerializableClassDescriptorBySerializer(descriptor) ?: return
|
||||
if (serializableDesc.isSerializableEnum()) {
|
||||
SerializerForEnumsTranslator(descriptor, translator, context).generate()
|
||||
} else {
|
||||
SerializerJsTranslator(descriptor, translator, context).generate()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+4
-7
@@ -24,7 +24,6 @@ import org.jetbrains.kotlin.name.ClassId
|
||||
import org.jetbrains.kotlin.name.FqName
|
||||
import org.jetbrains.kotlin.psi.KtAnnotationEntry
|
||||
import org.jetbrains.kotlin.resolve.descriptorUtil.annotationClass
|
||||
import org.jetbrains.kotlin.resolve.descriptorUtil.classId
|
||||
import org.jetbrains.kotlin.resolve.descriptorUtil.module
|
||||
import org.jetbrains.kotlin.resolve.lazy.descriptors.LazyAnnotationDescriptor
|
||||
import org.jetbrains.kotlin.resolve.scopes.getDescriptorsFiltered
|
||||
@@ -122,10 +121,10 @@ internal fun ClassDescriptor.isSerializableEnum(): Boolean {
|
||||
if (this.kind != ClassKind.ENUM_CLASS) return false
|
||||
if (hasSerializableAnnotationWithoutArgs) return true
|
||||
if (annotations.hasAnySerialAnnotation) return true
|
||||
// check entries
|
||||
for (enumEntry in enumEntries()) {
|
||||
if (enumEntry.annotations.hasAnySerialAnnotation) return true
|
||||
}
|
||||
// todo: check entries without 'recursive call in lazy class member scope'
|
||||
// for (enumEntry in enumEntries()) {
|
||||
// if (enumEntry.annotations.hasAnySerialAnnotation) return true
|
||||
// }
|
||||
return false
|
||||
}
|
||||
|
||||
@@ -190,8 +189,6 @@ internal val ClassDescriptor?.classSerializer: ClassDescriptor?
|
||||
internal val ClassDescriptor.hasCompanionObjectAsSerializer: Boolean
|
||||
get() = isSerializableObject || companionObjectDescriptor?.serializerForClass == this.defaultType
|
||||
|
||||
internal fun ClassDescriptor.isSerializerWhichRequiersKClass() = classId in setOf(enumSerializerId, contextSerializerId, polymorphicSerializerId)
|
||||
|
||||
// returns only user-overriden Serializer
|
||||
internal val KotlinType.overridenSerializer: KotlinType?
|
||||
get() {
|
||||
|
||||
Reference in New Issue
Block a user