diff --git a/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/common/SerializerCodegen.kt b/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/common/SerializerCodegen.kt index 3fd34b85971..35801ae664d 100644 --- a/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/common/SerializerCodegen.kt +++ b/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/common/SerializerCodegen.kt @@ -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 diff --git a/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/js/JsCodegenUtil.kt b/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/js/JsCodegenUtil.kt index 32d67258f13..941330189b5 100644 --- a/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/js/JsCodegenUtil.kt +++ b/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/js/JsCodegenUtil.kt @@ -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) -} \ No newline at end of file +} + +// 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) + ) \ No newline at end of file diff --git a/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/js/SerializerJsTranslator.kt b/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/js/SerializerJsTranslator.kt index c018fa232ae..24e573247d9 100644 --- a/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/js/SerializerJsTranslator.kt +++ b/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/js/SerializerJsTranslator.kt @@ -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) diff --git a/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/jvm/SerializerCodegenImpl.kt b/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/jvm/SerializerCodegenImpl.kt index 99b9396ddb2..bd875db1c31 100644 --- a/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/jvm/SerializerCodegenImpl.kt +++ b/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/jvm/SerializerCodegenImpl.kt @@ -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)