Custom-serializable enums for JS
This commit is contained in:
-4
@@ -82,10 +82,6 @@ fun AbstractSerialGenerator.getSerialTypeInfo(property: SerializableProperty): S
|
|||||||
)
|
)
|
||||||
SerializableInfo(serializer)
|
SerializableInfo(serializer)
|
||||||
}
|
}
|
||||||
T.isEnum() -> {
|
|
||||||
val serializer = property.module.findClassAcrossModuleDependencies(enumSerializerId)
|
|
||||||
SerializableInfo(serializer)
|
|
||||||
}
|
|
||||||
else -> {
|
else -> {
|
||||||
val serializer =
|
val serializer =
|
||||||
findTypeSerializerOrContext(property.module, property.type, property.descriptor.findPsi())
|
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.KotlinType
|
||||||
import org.jetbrains.kotlin.types.typeUtil.isTypeParameter
|
import org.jetbrains.kotlin.types.typeUtil.isTypeParameter
|
||||||
import org.jetbrains.kotlinx.serialization.compiler.backend.common.*
|
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.*
|
||||||
import org.jetbrains.kotlinx.serialization.compiler.backend.jvm.referenceArraySerializerId
|
|
||||||
import org.jetbrains.kotlinx.serialization.compiler.resolve.*
|
import org.jetbrains.kotlinx.serialization.compiler.resolve.*
|
||||||
|
|
||||||
internal class JsBlockBuilder {
|
internal class JsBlockBuilder {
|
||||||
@@ -147,9 +146,13 @@ internal fun AbstractSerialGenerator.serializerInstance(
|
|||||||
return context.serializerObjectGetter(serializerClass)
|
return context.serializerObjectGetter(serializerClass)
|
||||||
} else {
|
} else {
|
||||||
var args = when {
|
var args = when {
|
||||||
serializerClass.isSerializerWhichRequiersKClass() -> listOf(
|
serializerClass.classId == contextSerializerId || serializerClass.classId == polymorphicSerializerId -> listOf(
|
||||||
ExpressionVisitor.getObjectKClass(context, kType.toClassDescriptor!!)
|
ExpressionVisitor.getObjectKClass(context, kType.toClassDescriptor!!)
|
||||||
)
|
)
|
||||||
|
serializerClass.classId == enumSerializerId -> listOf(
|
||||||
|
ExpressionVisitor.getObjectKClass(context, kType.toClassDescriptor!!),
|
||||||
|
JsStringLiteral(kType.serialName())
|
||||||
|
)
|
||||||
serializerClass.classId == objectSerializerId -> listOf(
|
serializerClass.classId == objectSerializerId -> listOf(
|
||||||
JsStringLiteral(kType.serialName()),
|
JsStringLiteral(kType.serialName()),
|
||||||
context.serializerObjectGetter(kType.toClassDescriptor!!)
|
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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
+55
-27
@@ -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.SERIAL_DESCRIPTOR_CLASS_IMPL
|
||||||
import org.jetbrains.kotlinx.serialization.compiler.resolve.SerialEntityNames.typeArgPrefix
|
import org.jetbrains.kotlinx.serialization.compiler.resolve.SerialEntityNames.typeArgPrefix
|
||||||
|
|
||||||
class SerializerJsTranslator(descriptor: ClassDescriptor,
|
open class SerializerJsTranslator(
|
||||||
|
descriptor: ClassDescriptor,
|
||||||
val translator: DeclarationBodyVisitor,
|
val translator: DeclarationBodyVisitor,
|
||||||
val context: TranslationContext) : SerializerCodegen(descriptor, context.bindingContext()) {
|
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)
|
val f = context.buildFunction(descriptor, bodyGen)
|
||||||
translator.addFunction(descriptor, f, null)
|
translator.addFunction(descriptor, f, null)
|
||||||
}
|
}
|
||||||
@@ -55,15 +57,9 @@ class SerializerJsTranslator(descriptor: ClassDescriptor,
|
|||||||
val desc = generatedSerialDescPropertyDescriptor ?: return
|
val desc = generatedSerialDescPropertyDescriptor ?: return
|
||||||
val serialDescImplClass = serializerDescriptor
|
val serialDescImplClass = serializerDescriptor
|
||||||
.getClassFromInternalSerializationPackage(SERIAL_DESCRIPTOR_CLASS_IMPL)
|
.getClassFromInternalSerializationPackage(SERIAL_DESCRIPTOR_CLASS_IMPL)
|
||||||
val serialDescImplConstructor = serialDescImplClass
|
|
||||||
.unsubstitutedPrimaryConstructor!!
|
|
||||||
|
|
||||||
// this.serialDesc = new SerialDescImpl(...)
|
// this.serialDesc = new SerialDescImpl(...)
|
||||||
val correctThis = context.getDispatchReceiver(JsDescriptorUtils.getReceiverParameterForDeclaration(desc.containingDeclaration))
|
val correctThis = context.getDispatchReceiver(JsDescriptorUtils.getReceiverParameterForDeclaration(desc.containingDeclaration))
|
||||||
val value = JsNew(
|
val value = instantiateNewDescriptor(context, correctThis, serialDescImplClass)
|
||||||
context.getInnerReference(serialDescImplConstructor),
|
|
||||||
listOf(JsStringLiteral(serialName), if (isGeneratedSerializer) correctThis else JsNullLiteral())
|
|
||||||
)
|
|
||||||
val assgmnt = TranslationUtils.assignmentToBackingField(context, desc, value)
|
val assgmnt = TranslationUtils.assignmentToBackingField(context, desc, value)
|
||||||
translator.addInitializerStatement(assgmnt.makeStmt())
|
translator.addInitializerStatement(assgmnt.makeStmt())
|
||||||
|
|
||||||
@@ -73,23 +69,44 @@ class SerializerJsTranslator(descriptor: ClassDescriptor,
|
|||||||
val pushClassFunc = serialDescImplClass.getFuncDesc(CallingConventions.addClassAnnotation).single()
|
val pushClassFunc = serialDescImplClass.getFuncDesc(CallingConventions.addClassAnnotation).single()
|
||||||
val serialClassDescRef = JsNameRef(context.getNameForDescriptor(generatedSerialDescPropertyDescriptor), JsThisRef())
|
val serialClassDescRef = JsNameRef(context.getNameForDescriptor(generatedSerialDescPropertyDescriptor), JsThisRef())
|
||||||
|
|
||||||
for (prop in serializableProperties) {
|
addElementsContentToDescriptor(context, serialClassDescRef, addFunc, pushFunc)
|
||||||
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)
|
|
||||||
}
|
|
||||||
|
|
||||||
// push class annotations
|
// push class annotations
|
||||||
pushAnnotationsInto(serializableDescriptor, pushClassFunc, serialClassDescRef)
|
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()) {
|
for ((annotationClass , args, _) in annotated.annotationsWithArguments()) {
|
||||||
val argExprs = args.map { arg ->
|
val argExprs = args.map { arg ->
|
||||||
Translation.translateAsExpression(arg.getArgumentExpression()!!, context)
|
Translation.translateAsExpression(arg.getArgumentExpression()!!, context)
|
||||||
@@ -107,8 +124,10 @@ class SerializerJsTranslator(descriptor: ClassDescriptor,
|
|||||||
|
|
||||||
override fun generateSerializableClassProperty(property: PropertyDescriptor) {
|
override fun generateSerializableClassProperty(property: PropertyDescriptor) {
|
||||||
val propDesc = generatedSerialDescPropertyDescriptor ?: return
|
val propDesc = generatedSerialDescPropertyDescriptor ?: return
|
||||||
val propTranslator = DefaultPropertyTranslator(propDesc, context,
|
val propTranslator = DefaultPropertyTranslator(
|
||||||
translator.getBackingFieldReference(propDesc))
|
propDesc, context,
|
||||||
|
translator.getBackingFieldReference(propDesc)
|
||||||
|
)
|
||||||
val getterDesc = propDesc.getter!!
|
val getterDesc = propDesc.getter!!
|
||||||
val getterExpr = context.getFunctionObject(getterDesc)
|
val getterExpr = context.getFunctionObject(getterDesc)
|
||||||
.apply { propTranslator.generateDefaultGetterFunction(getterDesc, this) }
|
.apply { propTranslator.generateDefaultGetterFunction(getterDesc, this) }
|
||||||
@@ -131,7 +150,7 @@ class SerializerJsTranslator(descriptor: ClassDescriptor,
|
|||||||
context.addDeclarationStatement(f.makeStmt())
|
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())
|
getNameForDescriptor(clazz.getFuncDesc(name).single())
|
||||||
|
|
||||||
override fun generateSave(function: FunctionDescriptor) = generateFunction(function) { jsFun, ctx ->
|
override fun generateSave(function: FunctionDescriptor) = generateFunction(function) { jsFun, ctx ->
|
||||||
@@ -382,9 +401,18 @@ class SerializerJsTranslator(descriptor: ClassDescriptor,
|
|||||||
}
|
}
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
fun translate(declaration: KtPureClassOrObject, descriptor: ClassDescriptor, translator: DeclarationBodyVisitor, context: TranslationContext) {
|
fun translate(
|
||||||
if (getSerializableClassDescriptorBySerializer(descriptor) != null)
|
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()
|
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.name.FqName
|
||||||
import org.jetbrains.kotlin.psi.KtAnnotationEntry
|
import org.jetbrains.kotlin.psi.KtAnnotationEntry
|
||||||
import org.jetbrains.kotlin.resolve.descriptorUtil.annotationClass
|
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.descriptorUtil.module
|
||||||
import org.jetbrains.kotlin.resolve.lazy.descriptors.LazyAnnotationDescriptor
|
import org.jetbrains.kotlin.resolve.lazy.descriptors.LazyAnnotationDescriptor
|
||||||
import org.jetbrains.kotlin.resolve.scopes.getDescriptorsFiltered
|
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 (this.kind != ClassKind.ENUM_CLASS) return false
|
||||||
if (hasSerializableAnnotationWithoutArgs) return true
|
if (hasSerializableAnnotationWithoutArgs) return true
|
||||||
if (annotations.hasAnySerialAnnotation) return true
|
if (annotations.hasAnySerialAnnotation) return true
|
||||||
// check entries
|
// todo: check entries without 'recursive call in lazy class member scope'
|
||||||
for (enumEntry in enumEntries()) {
|
// for (enumEntry in enumEntries()) {
|
||||||
if (enumEntry.annotations.hasAnySerialAnnotation) return true
|
// if (enumEntry.annotations.hasAnySerialAnnotation) return true
|
||||||
}
|
// }
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -190,8 +189,6 @@ internal val ClassDescriptor?.classSerializer: ClassDescriptor?
|
|||||||
internal val ClassDescriptor.hasCompanionObjectAsSerializer: Boolean
|
internal val ClassDescriptor.hasCompanionObjectAsSerializer: Boolean
|
||||||
get() = isSerializableObject || companionObjectDescriptor?.serializerForClass == this.defaultType
|
get() = isSerializableObject || companionObjectDescriptor?.serializerForClass == this.defaultType
|
||||||
|
|
||||||
internal fun ClassDescriptor.isSerializerWhichRequiersKClass() = classId in setOf(enumSerializerId, contextSerializerId, polymorphicSerializerId)
|
|
||||||
|
|
||||||
// returns only user-overriden Serializer
|
// returns only user-overriden Serializer
|
||||||
internal val KotlinType.overridenSerializer: KotlinType?
|
internal val KotlinType.overridenSerializer: KotlinType?
|
||||||
get() {
|
get() {
|
||||||
|
|||||||
Reference in New Issue
Block a user