.childSerializers for JS

This commit is contained in:
Leonid Startsev
2018-09-25 14:24:14 +03:00
committed by Leonid Startsev
parent baf8cb6e9b
commit 3010814327
4 changed files with 90 additions and 50 deletions
@@ -31,7 +31,7 @@ abstract class SerializerCodegen(
bindingContext: BindingContext
) : AbstractSerialGenerator(bindingContext, serializerDescriptor) {
// protected val serializerDescriptor: ClassDescriptor = declaration.findClassDescriptor(bindingContext)
protected val serializableDescriptor: ClassDescriptor = getSerializableClassDescriptorBySerializer(serializerDescriptor)!!
val serializableDescriptor: ClassDescriptor = getSerializableClassDescriptorBySerializer(serializerDescriptor)!!
protected val serialName: String = serializableDescriptor.annotations.serialNameValue ?: serializableDescriptor.fqNameUnsafe.asString()
protected val properties = SerializableProperties(serializableDescriptor, bindingContext)
protected val orderedProperties = properties.serializableProperties
@@ -17,12 +17,23 @@
package org.jetbrains.kotlinx.serialization.compiler.backend.js
import org.jetbrains.kotlin.descriptors.ClassDescriptor
import org.jetbrains.kotlin.descriptors.ClassKind
import org.jetbrains.kotlin.descriptors.FunctionDescriptor
import org.jetbrains.kotlin.descriptors.ModuleDescriptor
import org.jetbrains.kotlin.js.backend.ast.*
import org.jetbrains.kotlin.js.resolve.diagnostics.findPsi
import org.jetbrains.kotlin.js.translate.context.TranslationContext
import org.jetbrains.kotlin.js.translate.expression.translateAndAliasParameters
import org.jetbrains.kotlin.js.translate.reference.ReferenceTranslator
import org.jetbrains.kotlin.js.translate.utils.JsAstUtils
import org.jetbrains.kotlin.resolve.descriptorUtil.classId
import org.jetbrains.kotlin.types.KotlinType
import org.jetbrains.kotlin.types.typeUtil.isTypeParameter
import org.jetbrains.kotlinx.serialization.compiler.backend.common.findTypeSerializerOrContext
import org.jetbrains.kotlinx.serialization.compiler.backend.jvm.contextSerializerId
import org.jetbrains.kotlinx.serialization.compiler.backend.jvm.enumSerializerId
import org.jetbrains.kotlinx.serialization.compiler.backend.jvm.referenceArraySerializerId
import org.jetbrains.kotlinx.serialization.compiler.resolve.*
internal class JsBlockBuilder {
val block: JsBlock = JsBlock()
@@ -101,4 +112,61 @@ internal fun TranslationContext.serializerObjectGetter(serializer: ClassDescript
internal fun TranslationContext.translateQualifiedReference(clazz: ClassDescriptor): JsExpression {
return ReferenceTranslator.translateAsTypeReference(clazz, this)
}
}
// Does not use sti and therefore does not perform encoder calls optimization
internal fun SerializerJsTranslator.serializerTower(property: SerializableProperty): JsExpression? {
val nullableSerClass =
context.translateQualifiedReference(property.module.getClassFromInternalSerializationPackage(SpecialBuiltins.nullableSerializer))
val serializer =
property.serializableWith?.toClassDescriptor
?: if (!property.type.isTypeParameter()) findTypeSerializerOrContext(
property.module,
property.type,
property.descriptor.annotations,
property.descriptor.findPsi()
) else null
return serializerInstance(serializer, property.module, property.type, property.genericIndex)
?.let { expr -> if (property.type.isMarkedNullable) JsNew(nullableSerClass, listOf(expr)) else expr }
}
internal fun SerializerJsTranslator.serializerInstance(
serializerClass: ClassDescriptor?,
module: ModuleDescriptor,
kType: KotlinType,
genericIndex: Int? = null
): JsExpression? {
val nullableSerClass =
context.translateQualifiedReference(module.getClassFromInternalSerializationPackage(SpecialBuiltins.nullableSerializer))
if (serializerClass == null) {
if (genericIndex == null) return null
return JsNameRef(context.scope().declareName("${SerialEntityNames.typeArgPrefix}$genericIndex"), JsThisRef())
}
if (serializerClass.kind == ClassKind.OBJECT) {
return context.serializerObjectGetter(serializerClass)
} else {
var args = if (serializerClass.classId == enumSerializerId || serializerClass.classId == contextSerializerId)
listOf(createGetKClassExpression(kType.toClassDescriptor!!))
else kType.arguments.map {
val argSer = findTypeSerializerOrContext(module, it.type, sourceElement = serializerClass.findPsi())
val expr = serializerInstance(argSer, module, it.type, it.type.genericIndex) ?: return null
if (it.type.isMarkedNullable) JsNew(nullableSerClass, listOf(expr)) else expr
}
if (serializerClass.classId == referenceArraySerializerId)
args = listOf(createGetKClassExpression(kType.arguments[0].type.toClassDescriptor!!)) + args
val serializable = getSerializableClassDescriptorBySerializer(serializerClass)
val ref = if (serializable?.declaredTypeParameters?.isNotEmpty() == true) {
val desc = KSerializerDescriptorResolver.createTypedSerializerConstructorDescriptor(serializerClass, serializableDescriptor)
JsInvocation(context.getInnerReference(desc), args)
} else {
JsNew(context.translateQualifiedReference(serializerClass), args)
}
return ref
}
}
internal fun SerializerJsTranslator.createGetKClassExpression(classDescriptor: ClassDescriptor): JsExpression =
JsInvocation(
context.namer().kotlin("getKClass"),
context.translateQualifiedReference(classDescriptor)
)
@@ -20,24 +20,18 @@ import org.jetbrains.kotlin.builtins.KotlinBuiltIns
import org.jetbrains.kotlin.descriptors.*
import org.jetbrains.kotlin.descriptors.annotations.Annotated
import org.jetbrains.kotlin.js.backend.ast.*
import org.jetbrains.kotlin.js.resolve.diagnostics.findPsi
import org.jetbrains.kotlin.js.translate.context.Namer
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.utils.JsAstUtils
import org.jetbrains.kotlin.js.translate.utils.JsDescriptorUtils
import org.jetbrains.kotlin.js.translate.utils.TranslationUtils
import org.jetbrains.kotlin.psi.KtPureClassOrObject
import org.jetbrains.kotlin.resolve.descriptorUtil.classId
import org.jetbrains.kotlin.types.KotlinType
import org.jetbrains.kotlin.types.typeUtil.builtIns
import org.jetbrains.kotlinx.serialization.compiler.backend.common.SerializerCodegen
import org.jetbrains.kotlinx.serialization.compiler.backend.common.findTypeSerializerOrContext
import org.jetbrains.kotlinx.serialization.compiler.backend.common.getSerialTypeInfo
import org.jetbrains.kotlinx.serialization.compiler.backend.jvm.contextSerializerId
import org.jetbrains.kotlinx.serialization.compiler.backend.jvm.enumSerializerId
import org.jetbrains.kotlinx.serialization.compiler.backend.jvm.referenceArraySerializerId
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
@@ -60,7 +54,8 @@ class SerializerJsTranslator(descriptor: ClassDescriptor,
.unsubstitutedPrimaryConstructor!!
// this.serialDesc = new SerialDescImpl(...)
val value = JsNew(context.getInnerReference(serialDescImplConstructor), listOf(JsStringLiteral(serialName)))
val correctThis = context.getDispatchReceiver(JsDescriptorUtils.getReceiverParameterForDeclaration(desc.containingDeclaration))
val value = JsNew(context.getInnerReference(serialDescImplConstructor), listOf(JsStringLiteral(serialName), correctThis))
val assgmnt = TranslationUtils.assignmentToBackingField(context, desc, value)
translator.addInitializerStatement(assgmnt.makeStmt())
@@ -72,7 +67,11 @@ class SerializerJsTranslator(descriptor: ClassDescriptor,
for (prop in orderedProperties) {
if (prop.transient) continue
val call = JsInvocation(JsNameRef(context.getNameForDescriptor(addFunc), serialClassDescRef), JsStringLiteral(prop.name))
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)
@@ -93,6 +92,11 @@ class SerializerJsTranslator(descriptor: ClassDescriptor,
}
}
override fun generateChildSerializersGetter(function: FunctionDescriptor) = generateFunction(function) { _, _ ->
val allSerializers = orderedProperties.map { requireNotNull(serializerTower(it)) { "Property ${it.name} must have a serializer" } }
+JsReturn(JsArrayLiteral(allSerializers))
}
override fun generateSerializableClassProperty(property: PropertyDescriptor) {
val propDesc = generatedSerialDescPropertyDescriptor ?: return
val propTranslator = DefaultPropertyTranslator(propDesc, context,
@@ -174,40 +178,6 @@ class SerializerJsTranslator(descriptor: ClassDescriptor,
+JsInvocation(JsNameRef(wEndFunc, localOutputRef), serialClassDescRef).makeStmt()
}
private fun serializerInstance(serializerClass: ClassDescriptor?, module: ModuleDescriptor, kType: KotlinType, genericIndex: Int? = null): JsExpression? {
val nullableSerClass = context.translateQualifiedReference(module.getClassFromInternalSerializationPackage(SpecialBuiltins.nullableSerializer))
if (serializerClass == null) {
if (genericIndex == null) return null
return JsNameRef(context.scope().declareName("$typeArgPrefix$genericIndex"), JsThisRef())
}
if (serializerClass.kind == ClassKind.OBJECT) {
return context.serializerObjectGetter(serializerClass)
}
else {
var args = if (serializerClass.classId == enumSerializerId || serializerClass.classId == contextSerializerId)
listOf(createGetKClassExpression(kType.toClassDescriptor!!))
else kType.arguments.map {
val argSer = findTypeSerializerOrContext(module, it.type, sourceElement = serializerClass.findPsi())
val expr = serializerInstance(argSer, module, it.type, it.type.genericIndex) ?: return null
if (it.type.isMarkedNullable) JsNew(nullableSerClass, listOf(expr)) else expr
}
if (serializerClass.classId == referenceArraySerializerId)
args = listOf(createGetKClassExpression(kType.arguments[0].type.toClassDescriptor!!)) + args
val serializable = getSerializableClassDescriptorBySerializer(serializerClass)
val ref = if (serializable?.declaredTypeParameters?.isNotEmpty() == true) {
val desc = KSerializerDescriptorResolver.createTypedSerializerConstructorDescriptor(serializerClass, serializableDescriptor)
JsInvocation(context.getInnerReference(desc), args)
} else {
JsNew(context.translateQualifiedReference(serializerClass), args)
}
return ref
}
}
private fun createGetKClassExpression(classDescriptor: ClassDescriptor): JsExpression =
JsInvocation(context.namer().kotlin("getKClass"),
context.translateQualifiedReference(classDescriptor))
override fun generateLoad(function: FunctionDescriptor) = generateFunction(function) { jsFun, context ->
val inputClass = serializerDescriptor.getClassFromSerializationPackage(SerialEntityNames.STRUCTURE_DECODER_CLASS)
@@ -191,11 +191,13 @@ class SerializerCodegenImpl(
dup() // array
iconst(i) // index
val prop = orderedProperties[i]
stackValueSerializerInstanceFromSerializerWithoutSti(
codegen,
prop,
this@SerializerCodegenImpl
)
assert(
stackValueSerializerInstanceFromSerializerWithoutSti(
codegen,
prop,
this@SerializerCodegenImpl
)
) { "Property ${prop.name} must have serializer" }
astore(kSerializerType)
}
areturn(kSerializerArrayType)