diff --git a/plugins/kotlin-serialization/kotlin-serialization-compiler/kotlin-serialization-compiler.iml b/plugins/kotlin-serialization/kotlin-serialization-compiler/kotlin-serialization-compiler.iml
index 0946ca030d9..42883528a12 100644
--- a/plugins/kotlin-serialization/kotlin-serialization-compiler/kotlin-serialization-compiler.iml
+++ b/plugins/kotlin-serialization/kotlin-serialization-compiler/kotlin-serialization-compiler.iml
@@ -12,5 +12,6 @@
+
\ No newline at end of file
diff --git a/plugins/kotlin-serialization/kotlin-serialization-compiler/src/META-INF/plugin.xml b/plugins/kotlin-serialization/kotlin-serialization-compiler/src/META-INF/plugin.xml
index 79a80811a0d..47067b63923 100644
--- a/plugins/kotlin-serialization/kotlin-serialization-compiler/src/META-INF/plugin.xml
+++ b/plugins/kotlin-serialization/kotlin-serialization-compiler/src/META-INF/plugin.xml
@@ -9,5 +9,6 @@
+
\ No newline at end of file
diff --git a/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/common/SerializableCodegen.kt b/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/common/SerializableCodegen.kt
index 028ebf1ae87..d202dfdb6ab 100644
--- a/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/common/SerializableCodegen.kt
+++ b/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/common/SerializableCodegen.kt
@@ -25,11 +25,6 @@ import org.jetbrains.kotlin.psi.KtPureClassOrObject
import org.jetbrains.kotlin.psi.synthetics.findClassDescriptor
import org.jetbrains.kotlin.resolve.BindingContext
-/**
- * @author Leonid Startsev
- * sandwwraith@gmail.com
- */
-
abstract class SerializableCodegen(declaration: KtPureClassOrObject, private val bindingContext: BindingContext) {
protected val serializableDescriptor: ClassDescriptor = declaration.findClassDescriptor(bindingContext)
protected val properties = SerializableProperties(serializableDescriptor, bindingContext)
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 732adf7bcc6..2d85f0c6314 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
@@ -45,6 +45,8 @@ abstract class SerializerCodegen(declaration: KtPureClassOrObject, bindingContex
generateSerialDesc()
}
+ protected val serialDescPropertyDescriptor = getPropertyToGenerate(serializerDescriptor, KSerializerDescriptorResolver.SERIAL_DESC_FIELD,
+ serializerDescriptor::checkSerializableClassPropertyResult)
protected abstract fun generateSerialDesc()
protected abstract fun generateSerializableClassProperty(property: PropertyDescriptor)
@@ -54,8 +56,7 @@ abstract class SerializerCodegen(declaration: KtPureClassOrObject, bindingContex
protected abstract fun generateLoad(function: FunctionDescriptor)
private fun generateSerializableClassPropertyIfNeeded() {
- val property = getPropertyToGenerate(serializerDescriptor, KSerializerDescriptorResolver.SERIAL_DESC_FIELD,
- serializerDescriptor::checkSerializableClassPropertyResult)
+ val property = serialDescPropertyDescriptor
?: return
generateSerializableClassProperty(property)
}
diff --git a/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/common/TypeUtil.kt b/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/common/TypeUtil.kt
new file mode 100644
index 00000000000..436624024a4
--- /dev/null
+++ b/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/common/TypeUtil.kt
@@ -0,0 +1,137 @@
+/*
+ * Copyright 2010-2017 JetBrains s.r.o.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.jetbrains.kotlinx.serialization.compiler.backend.common
+
+import org.jetbrains.kotlin.builtins.KotlinBuiltIns
+import org.jetbrains.kotlin.descriptors.*
+import org.jetbrains.kotlin.js.descriptorUtils.getJetTypeFqName
+import org.jetbrains.kotlin.js.descriptorUtils.nameIfStandardType
+import org.jetbrains.kotlin.name.ClassId
+import org.jetbrains.kotlin.name.Name
+import org.jetbrains.kotlin.psi.*
+import org.jetbrains.kotlin.resolve.BindingContext
+import org.jetbrains.kotlin.resolve.descriptorUtil.fqNameSafe
+import org.jetbrains.kotlin.resolve.descriptorUtil.getSuperClassNotAny
+import org.jetbrains.kotlin.resolve.lazy.descriptors.LazyAnnotationDescriptor
+import org.jetbrains.kotlin.types.KotlinType
+import org.jetbrains.kotlin.types.typeUtil.containsTypeProjectionsInTopLevelArguments
+import org.jetbrains.kotlin.types.typeUtil.isBoolean
+import org.jetbrains.kotlin.types.typeUtil.isPrimitiveNumberType
+import org.jetbrains.kotlinx.serialization.compiler.backend.jvm.enumSerializerId
+import org.jetbrains.kotlinx.serialization.compiler.backend.jvm.polymorphicSerializerId
+import org.jetbrains.kotlinx.serialization.compiler.backend.jvm.referenceArraySerializerId
+import org.jetbrains.kotlinx.serialization.compiler.resolve.*
+
+open class SerialTypeInfo(
+ val property: SerializableProperty,
+ val elementMethodPrefix: String,
+ val serializer: ClassDescriptor? = null,
+ val unit: Boolean = false
+)
+
+fun getSerialTypeInfo(property: SerializableProperty): SerialTypeInfo {
+ val T = property.type
+ return when {
+ T.isPrimitiveNumberType() or T.isBoolean() -> SerialTypeInfo(property,
+ T.nameIfStandardType.toString().capitalize())
+ KotlinBuiltIns.isString(T) -> SerialTypeInfo(property, "String")
+ KotlinBuiltIns.isUnit(T) -> SerialTypeInfo(property, "Unit", unit = true)
+ KotlinBuiltIns.isPrimitiveArray(T) -> TODO("primitive arrays are not supported yet")
+ KotlinBuiltIns.isNonPrimitiveArray(T.toClassDescriptor!!) -> {
+ val serializer = property.serializer?.toClassDescriptor ?:
+ property.module.findClassAcrossModuleDependencies(referenceArraySerializerId)
+ SerialTypeInfo(property, if (property.type.isMarkedNullable) "Nullable" else "", serializer)
+ }
+ T.toClassDescriptor?.kind == ClassKind.ENUM_CLASS -> {
+ val serializer = property.serializer?.toClassDescriptor ?:
+ property.module.findClassAcrossModuleDependencies(enumSerializerId)
+ SerialTypeInfo(property, if (property.type.isMarkedNullable) "Nullable" else "", serializer)
+ }
+ else -> {
+ val serializer = findTypeSerializer(property.module, property.type)
+ SerialTypeInfo(property, if (property.type.isMarkedNullable) "Nullable" else "", serializer)
+ }
+ }
+}
+
+fun findTypeSerializer(module: ModuleDescriptor, kType: KotlinType): ClassDescriptor? {
+ return if (kType.requiresPolymorphism()) findPolymorphicSerializer(module)
+ else kType.typeSerializer.toClassDescriptor // check for serializer defined on the type
+ ?: findStandardKotlinTypeSerializer(module, kType) // otherwise see if there is a standard serializer
+ ?: findEnumTypeSerializer(module, kType)
+}
+
+fun findStandardKotlinTypeSerializer(module: ModuleDescriptor, kType: KotlinType): ClassDescriptor? {
+ val name = when (kType.getJetTypeFqName(false)) {
+ "kotlin.Unit" -> "UnitSerializer"
+ "Z", "kotlin.Boolean" -> "BooleanSerializer"
+ "B", "kotlin.Byte" -> "ByteSerializer"
+ "S", "kotlin.Short" -> "ShortSerializer"
+ "I", "kotlin.Int" -> "IntSerializer"
+ "J", "kotlin.Long" -> "LongSerializer"
+ "F", "kotlin.Float" -> "FloatSerializer"
+ "D", "kotlin.Double" -> "DoubleSerializer"
+ "C", "kotlin.Char" -> "CharSerializer"
+ "kotlin.String" -> "StringSerializer"
+ "kotlin.collections.Collection", "kotlin.collections.List", "kotlin.collections.ArrayList" -> "ArrayListSerializer"
+ "kotlin.collections.Set", "kotlin.collections.LinkedHashSet" -> "LinkedHashSetSerializer"
+ "kotlin.collections.HashSet" -> "HashSetSerializer"
+ "kotlin.collections.Map", "kotlin.collections.LinkedHashMap" -> "LinkedHashMapSerializer"
+ "kotlin.collections.HashMap" -> "HashMapSerializer"
+ "kotlin.collections.Map.Entry" -> "MapEntrySerializer"
+ else -> return null
+ }
+ return module.findClassAcrossModuleDependencies(ClassId(internalPackageFqName, Name.identifier(name)))
+}
+
+fun findEnumTypeSerializer(module: ModuleDescriptor, kType: KotlinType): ClassDescriptor? {
+ val classDescriptor = kType.toClassDescriptor ?: return null
+ return if (classDescriptor.kind == ClassKind.ENUM_CLASS) module.findClassAcrossModuleDependencies(enumSerializerId) else null
+}
+
+fun KotlinType.requiresPolymorphism(): Boolean {
+ return this.toClassDescriptor?.getSuperClassNotAny()?.isInternalSerializable == true
+ || this.toClassDescriptor?.modality == Modality.OPEN
+ || this.containsTypeProjectionsInTopLevelArguments() // List<*>
+}
+
+fun findPolymorphicSerializer(module: ModuleDescriptor): ClassDescriptor {
+ return requireNotNull(module.findClassAcrossModuleDependencies(polymorphicSerializerId)) { "Can't locate polymorphic serializer definition" }
+}
+
+fun KtPureClassOrObject.bodyPropertiesDescriptorsMap(bindingContext: BindingContext): Map = declarations
+ .asSequence()
+ .filterIsInstance()
+ .associateBy { (bindingContext[BindingContext.DECLARATION_TO_DESCRIPTOR, it] as? PropertyDescriptor)!! }
+
+fun KtPureClassOrObject.primaryPropertiesDescriptorsMap(bindingContext: BindingContext): Map = primaryConstructorParameters
+ .asSequence()
+ .filter { it.hasValOrVar() }
+ .associateBy { bindingContext[BindingContext.PRIMARY_CONSTRUCTOR_PARAMETER, it]!! }
+
+fun KtPureClassOrObject.anonymousInitializers() = declarations
+ .asSequence()
+ .filterIsInstance()
+ .mapNotNull { it.body }
+ .toList()
+
+fun SerializableProperty.annotationVarsAndDesc(annotationClass: ClassDescriptor): Pair, List> {
+ val args: List = (this.descriptor.annotations.findAnnotation(annotationClass.fqNameSafe) as? LazyAnnotationDescriptor)?.annotationEntry?.valueArguments.orEmpty()
+ val consParams = annotationClass.unsubstitutedPrimaryConstructor?.valueParameters.orEmpty()
+ if (args.size != consParams.size) throw IllegalArgumentException("Can't use arguments with defaults for serializable annotations yet")
+ return args to consParams
+}
\ No newline at end of file
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
new file mode 100644
index 00000000000..15cf66f413e
--- /dev/null
+++ b/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/js/JsCodegenUtil.kt
@@ -0,0 +1,84 @@
+/*
+ * Copyright 2010-2017 JetBrains s.r.o.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.jetbrains.kotlinx.serialization.compiler.backend.js
+
+import org.jetbrains.kotlin.descriptors.FunctionDescriptor
+import org.jetbrains.kotlin.js.backend.ast.*
+import org.jetbrains.kotlin.js.translate.context.TranslationContext
+import org.jetbrains.kotlin.js.translate.expression.translateAndAliasParameters
+
+internal class JsBlockBuilder {
+ val block: JsBlock = JsBlock()
+ operator fun JsStatement.unaryPlus() {
+ block.statements.add(this)
+ }
+
+ val body: List
+ get() = block.statements
+}
+
+internal fun JsBlockBuilder.jsWhile(condition: JsExpression, body: JsBlockBuilder.() -> Unit, label: JsLabel? = null) {
+ val b = JsBlockBuilder()
+ b.body()
+ val w = JsWhile(condition, b.block)
+ if (label == null) {
+ +w
+ } else {
+ label.statement = w
+ +label
+ }
+}
+
+internal class JsCasesBuilder() {
+ val caseList: MutableList = mutableListOf()
+ operator fun JsSwitchMember.unaryPlus() {
+ caseList.add(this)
+ }
+}
+
+internal fun JsCasesBuilder.case(condition: JsExpression, body: JsBlockBuilder.() -> Unit) {
+ val a = JsCase()
+ a.caseExpression = condition
+ val b = JsBlockBuilder()
+ b.body()
+ a.statements += b.body
+ +a
+}
+
+internal fun JsCasesBuilder.default(body: JsBlockBuilder.() -> Unit) {
+ val a = JsDefault()
+ val b = JsBlockBuilder()
+ b.body()
+ a.statements += b.body
+ +a
+}
+
+internal fun JsBlockBuilder.jsSwitch(condition: JsExpression, cases: JsCasesBuilder.() -> Unit) {
+ val b = JsCasesBuilder()
+ b.cases()
+ val sw = JsSwitch(condition, b.caseList)
+ +sw
+}
+
+internal fun TranslationContext.buildFunction(descriptor: FunctionDescriptor, bodyGen: JsBlockBuilder.(JsFunction, TranslationContext) -> Unit): JsFunction {
+ val functionObject = this.getFunctionObject(descriptor)
+ val innerCtx = this.newDeclaration(descriptor).translateAndAliasParameters(descriptor, functionObject.parameters)
+ val b = JsBlockBuilder()
+ b.bodyGen(functionObject, innerCtx)
+ functionObject.body.statements += b.body
+ return functionObject
+}
\ No newline at end of file
diff --git a/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/js/SerializableJsTranslator.kt b/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/js/SerializableJsTranslator.kt
new file mode 100644
index 00000000000..18291a2417c
--- /dev/null
+++ b/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/js/SerializableJsTranslator.kt
@@ -0,0 +1,112 @@
+/*
+ * Copyright 2010-2017 JetBrains s.r.o.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.jetbrains.kotlinx.serialization.compiler.backend.js
+
+import org.jetbrains.kotlin.descriptors.ClassConstructorDescriptor
+import org.jetbrains.kotlin.descriptors.ClassDescriptor
+import org.jetbrains.kotlin.descriptors.FunctionDescriptor
+import org.jetbrains.kotlin.descriptors.PropertyDescriptor
+import org.jetbrains.kotlin.js.backend.ast.*
+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.general.Translation
+import org.jetbrains.kotlin.js.translate.utils.JsAstUtils
+import org.jetbrains.kotlin.js.translate.utils.TranslationUtils
+import org.jetbrains.kotlin.psi.KtExpression
+import org.jetbrains.kotlin.psi.KtPureClassOrObject
+import org.jetbrains.kotlinx.serialization.compiler.backend.common.SerializableCodegen
+import org.jetbrains.kotlinx.serialization.compiler.backend.common.anonymousInitializers
+import org.jetbrains.kotlinx.serialization.compiler.backend.common.bodyPropertiesDescriptorsMap
+import org.jetbrains.kotlinx.serialization.compiler.backend.common.primaryPropertiesDescriptorsMap
+import org.jetbrains.kotlinx.serialization.compiler.resolve.getClassFromSerializationPackage
+import org.jetbrains.kotlinx.serialization.compiler.resolve.isInternalSerializable
+
+class SerializableJsTranslator(val declaration: KtPureClassOrObject,
+ val translator: DeclarationBodyVisitor,
+ val context: TranslationContext) : SerializableCodegen(declaration, context.bindingContext()) {
+
+ val initMap: Map = declaration.run {
+ (bodyPropertiesDescriptorsMap(context.bindingContext()).mapValues { it.value.delegateExpressionOrInitializer } +
+ primaryPropertiesDescriptorsMap(context.bindingContext()).mapValues { it.value.defaultValue })
+ }
+
+ override fun generateInternalConstructor(constructorDescriptor: ClassConstructorDescriptor) {
+
+ val missingExceptionClassRef = serializableDescriptor.getClassFromSerializationPackage("MissingFieldException")
+ .let { context.getQualifiedReference(it) }
+
+ val f = context.buildFunction(constructorDescriptor) { jsFun, context ->
+ val thiz = jsFun.scope.declareName(Namer.ANOTHER_THIS_PARAMETER_NAME).makeRef()
+ val context = context.innerContextWithAliased(serializableDescriptor, thiz)
+
+ +JsVars(JsVars.JsVar(thiz.name, Namer.createObjectWithPrototypeFrom(context.getQualifiedReference(serializableDescriptor))))
+ val seenVar = jsFun.parameters[0].name.makeRef()
+ for ((index, prop) in properties.serializableProperties.withIndex()) {
+ val paramRef = jsFun.parameters[index + 1].name.makeRef()
+ // assign this.a = a in else branch
+ val assignParamStmt = TranslationUtils.assignmentToBackingField(context, prop.descriptor, paramRef).makeStmt()
+
+ val ifNotSeenStmt: JsStatement = if (prop.optional) {
+ val initializer = initMap.getValue(prop.descriptor) ?: throw IllegalArgumentException("optional without an initializer")
+ val initExpr = Translation.translateAsExpression(initializer, context)
+ TranslationUtils.assignmentToBackingField(context, prop.descriptor, initExpr).makeStmt()
+ }
+ else {
+ JsThrow(JsNew(missingExceptionClassRef, listOf(JsStringLiteral(prop.name))))
+ }
+ // (seen & 1 << i == 0) -- not seen
+ val notSeenTest = JsAstUtils.equality(
+ JsBinaryOperation(
+ JsBinaryOperator.BIT_AND,
+ seenVar,
+ JsIntLiteral(1 shl (index % 32))
+ ),
+ JsIntLiteral(0)
+ )
+ +JsIf(notSeenTest, ifNotSeenStmt, assignParamStmt)
+ }
+
+ //transient initializers and init blocks
+ val serialDescs = properties.serializableProperties.map { it.descriptor }
+ (initMap - serialDescs).forEach { desc, expr ->
+ val e = requireNotNull(expr) {"transient without an initializer"}
+ val initExpr = Translation.translateAsExpression(e, context)
+ +TranslationUtils.assignmentToBackingField(context, desc, initExpr).makeStmt()
+ }
+
+ declaration.anonymousInitializers()
+ .forEach { Translation.translateAsExpression(it, context, this.block) }
+
+ +JsReturn(thiz)
+ }
+
+ f.name = context.getInnerNameForDescriptor(constructorDescriptor)
+ context.addDeclarationStatement(f.makeStmt())
+ }
+
+ override fun generateWriteSelfMethod(methodDescriptor: FunctionDescriptor) {
+ // no-op yet
+ }
+
+ companion object {
+ fun translate(declaration: KtPureClassOrObject, descriptor: ClassDescriptor, translator: DeclarationBodyVisitor, context: TranslationContext) {
+ if (descriptor.isInternalSerializable)
+ SerializableJsTranslator(declaration, translator, context).generate()
+ }
+ }
+}
\ 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
new file mode 100644
index 00000000000..974f8650bf2
--- /dev/null
+++ b/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/js/SerializerJsTranslator.kt
@@ -0,0 +1,316 @@
+/*
+ * Copyright 2010-2017 JetBrains s.r.o.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.jetbrains.kotlinx.serialization.compiler.backend.js
+
+import org.jetbrains.kotlin.builtins.KotlinBuiltIns
+import org.jetbrains.kotlin.descriptors.*
+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.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.TranslationUtils
+import org.jetbrains.kotlin.js.translate.utils.TranslationUtils.shouldBoxReturnValue
+import org.jetbrains.kotlin.name.Name
+import org.jetbrains.kotlin.psi.KtPureClassOrObject
+import org.jetbrains.kotlin.resolve.descriptorUtil.classId
+import org.jetbrains.kotlin.resolve.scopes.getDescriptorsFiltered
+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.annotationVarsAndDesc
+import org.jetbrains.kotlinx.serialization.compiler.backend.common.findTypeSerializer
+import org.jetbrains.kotlinx.serialization.compiler.backend.common.getSerialTypeInfo
+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.*
+
+class SerializerJsTranslator(declaration: KtPureClassOrObject,
+ val translator: DeclarationBodyVisitor,
+ val context: TranslationContext) : SerializerCodegen(declaration, context.bindingContext()) {
+
+ private fun generateFunction(descriptor: FunctionDescriptor, bodyGen: JsBlockBuilder.(JsFunction, TranslationContext) -> Unit) {
+ val f = context.buildFunction(descriptor, bodyGen)
+ translator.addFunction(descriptor, f, null)
+ }
+
+
+ override fun generateSerialDesc() {
+ val desc = serialDescPropertyDescriptor ?: return
+ val serialDescImplClass = serializerDescriptor
+ .getClassFromInternalSerializationPackage("SerialClassDescImpl")
+ val serialDescImplConstructor = serialDescImplClass
+ .unsubstitutedPrimaryConstructor!!
+
+ // this.serialDesc = new SerialDescImpl(...)
+ val value = JsNew(context.getInnerReference(serialDescImplConstructor), listOf(JsStringLiteral(serialName)))
+ val assgmnt = TranslationUtils.assignmentToBackingField(context, desc, value)
+ translator.addInitializerStatement(assgmnt.makeStmt())
+
+ // adding elements via serialDesc.addElement(...)
+ val addFunc = serialDescImplClass.getFuncDesc("addElement").single()
+ val pushFunc = serialDescImplClass.getFuncDesc("pushAnnotation").single()
+ val serialClassDescRef = JsNameRef(context.getNameForDescriptor(serialDescPropertyDescriptor), JsThisRef())
+
+ for (prop in orderedProperties) {
+ if (prop.transient) continue
+ val call = JsInvocation(JsNameRef(context.getNameForDescriptor(addFunc), serialClassDescRef), JsStringLiteral(prop.name))
+ translator.addInitializerStatement(call.makeStmt())
+ // serialDesc.pushAnnotation(...)
+ for (annotationClass in prop.annotations) {
+ val (args, _) = prop.annotationVarsAndDesc(annotationClass)
+ val argExprs = args.map { arg ->
+ Translation.translateAsExpression(arg.getArgumentExpression()!!, context)
+ }
+ val classRef = context.getQualifiedReference(annotationClass)
+ val invok = JsInvocation(JsNameRef(context.getNameForDescriptor(pushFunc), serialClassDescRef), JsNew(classRef, argExprs))
+ translator.addInitializerStatement(invok.makeStmt())
+ }
+ }
+ }
+
+ override fun generateSerializableClassProperty(property: PropertyDescriptor) {
+ val propDesc = serialDescPropertyDescriptor ?: return
+ val propTranslator = DefaultPropertyTranslator(propDesc, context,
+ translator.getBackingFieldReference(propDesc))
+ val getterDesc = propDesc.getter!!
+ val getterExpr = context.getFunctionObject(getterDesc)
+ .apply { propTranslator.generateDefaultGetterFunction(getterDesc, this) }
+ translator.addProperty(propDesc, getterExpr, null)
+ }
+
+ private fun ClassDescriptor.getFuncDesc(funcName: String) =
+ unsubstitutedMemberScope.getDescriptorsFiltered { it == Name.identifier(funcName) }
+
+ override fun generateSave(function: FunctionDescriptor) = generateFunction(function) { jsFun, ctx ->
+ val kOutputClass = serializerDescriptor.getClassFromSerializationPackage("KOutput")
+ val wBeginFunc = ctx.getNameForDescriptor(
+ kOutputClass.getFuncDesc("writeBegin").single { (it as FunctionDescriptor).valueParameters.size == 2 })
+ val serialClassDescRef = JsNameRef(context.getNameForDescriptor(serialDescPropertyDescriptor!!), JsThisRef())
+
+ // output.writeBegin(desc, [])
+ val call = JsInvocation(JsNameRef(wBeginFunc, JsNameRef(jsFun.parameters[0].name)),
+ serialClassDescRef,
+ JsArrayLiteral())
+ val objRef = JsNameRef(jsFun.parameters[1].name)
+ // output = output.writeBegin...
+ val localOutputName = jsFun.scope.declareFreshName("output")
+ val localOutputRef = JsNameRef(localOutputName)
+ +JsVars(JsVars.JsVar(localOutputName, call))
+
+ // todo: internal serialization via virtual calls
+ val labeledProperties = orderedProperties.filter { !it.transient }
+ for (index in labeledProperties.indices) {
+ val property = labeledProperties[index]
+ if (property.transient) continue
+ // output.writeXxxElementValue(classDesc, index, value)
+ val sti = getSerialTypeInfo(property)
+ val innerSerial = if (sti.serializer == null) null else serializerInstance(sti.serializer, property.module, property.type)
+ if (innerSerial == null) {
+ val writeFunc =
+ kOutputClass.getFuncDesc("write${sti.elementMethodPrefix}ElementValue").single()
+ .let { ctx.getNameForDescriptor(it) }
+ +JsInvocation(JsNameRef(writeFunc, localOutputRef),
+ serialClassDescRef,
+ JsIntLiteral(index),
+ JsNameRef(ctx.getNameForDescriptor(property.descriptor), objRef)).makeStmt()
+ }
+ else {
+ val writeFunc =
+ kOutputClass.getFuncDesc("write${sti.elementMethodPrefix}SerializableElementValue").single()
+ .let { ctx.getNameForDescriptor(it) }
+ +JsInvocation(JsNameRef(writeFunc, localOutputRef),
+ serialClassDescRef,
+ JsIntLiteral(index),
+ innerSerial,
+ JsNameRef(ctx.getNameForDescriptor(property.descriptor), objRef)).makeStmt()
+ }
+ }
+
+ // output.writeEnd(serialClassDesc)
+ val wEndFunc = kOutputClass.getFuncDesc("writeEnd").single()
+ .let { ctx.getNameForDescriptor(it) }
+ +JsInvocation(JsNameRef(wEndFunc, localOutputRef), serialClassDescRef).makeStmt()
+ }
+
+ private fun getQualifiedClassReferenceName(classDescriptor: ClassDescriptor): JsExpression {
+ return context.getQualifiedReference(classDescriptor)
+ }
+
+ private fun serializerInstance(serializerClass: ClassDescriptor, module: ModuleDescriptor, kType: KotlinType): JsExpression? {
+ val nullableSerClass = getQualifiedClassReferenceName(
+ serializerClass.getClassFromInternalSerializationPackage("NullableSerializer"))
+ if (serializerClass.kind == ClassKind.OBJECT) {
+ return getQualifiedClassReferenceName(serializerClass)
+ }
+ else {
+ var args = if (serializerClass.classId == enumSerializerId)
+ listOf(createGetKClassExpression(kType.toClassDescriptor!!))
+ else kType.arguments.map {
+ val argSer = findTypeSerializer(module, it.type) ?: return null
+ val expr = serializerInstance(argSer, module, it.type) ?: 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
+ return JsNew(getQualifiedClassReferenceName(serializerClass), args)
+ }
+ }
+
+ private fun createGetKClassExpression(classDescriptor: ClassDescriptor): JsExpression =
+ JsInvocation(context.namer().kotlin("getKClass"),
+ getQualifiedClassReferenceName(classDescriptor))
+
+
+ override fun generateLoad(function: FunctionDescriptor) = generateFunction(function) { jsFun, context ->
+ val inputClass = serializerDescriptor.getClassFromSerializationPackage("KInput")
+ val serialClassDescRef = JsNameRef(context.getNameForDescriptor(serialDescPropertyDescriptor!!), JsThisRef())
+
+ // var index = -1, readAll = false
+ val indexVar = JsNameRef(jsFun.scope.declareFreshName("index"))
+ val readAllVar = JsNameRef(jsFun.scope.declareFreshName("readAll"))
+ +JsVars(JsVars.JsVar(indexVar.name), JsVars.JsVar(readAllVar.name, JsBooleanLiteral(false)))
+
+ // calculating bit mask vars
+ val blocksCnt = orderedProperties.size / 32 + 1
+ fun bitMaskOff(i: Int) = i / 32
+
+ // var bitMask0 = 0, bitMask1 = 0...
+ val bitMasks = (0 until blocksCnt).map { JsNameRef(jsFun.scope.declareFreshName("bitMask$it")) }
+ +JsVars(bitMasks.map { JsVars.JsVar(it.name, JsIntLiteral(0)) }, false)
+
+ // var localProp0, localProp1, ...
+ val localProps = orderedProperties.map { JsNameRef(jsFun.scope.declareFreshName(it.name)) }
+ +JsVars(localProps.map { JsVars.JsVar(it.name) }, true)
+
+ //input = input.readBegin(...)
+ val inputVar = JsNameRef(jsFun.scope.declareFreshName("input"))
+ val readBeginF = inputClass.getFuncDesc("readBegin").single()
+ val call = JsInvocation(JsNameRef(context.getNameForDescriptor(readBeginF), JsNameRef(jsFun.parameters[0].name)),
+ serialClassDescRef, JsArrayLiteral())
+ +JsVars(JsVars.JsVar(inputVar.name, call))
+
+ // while(true) {
+ val loop = JsLabel(jsFun.scope.declareFreshName("loopLabel"))
+ val loopRef = JsNameRef(loop.name)
+ jsWhile(JsBooleanLiteral(true), {
+ // index = input.readElement(classDesc)
+ val readElementF = context.getNameForDescriptor(inputClass.getFuncDesc("readElement").single())
+ +JsAstUtils.assignment(
+ indexVar,
+ JsInvocation(JsNameRef(readElementF, inputVar), serialClassDescRef)
+ ).makeStmt()
+ // switch(index)
+ jsSwitch (indexVar) {
+ // -2: readAll = true
+ case(JsIntLiteral(-2)) {
+ +JsAstUtils.assignment(
+ readAllVar,
+ JsBooleanLiteral(true)
+ ).makeStmt()
+ }
+ // all properties
+ for ((i, property) in orderedProperties.withIndex()) {
+ case(JsIntLiteral(i)) {
+ // input.readXxxElementValue
+ val sti = getSerialTypeInfo(property)
+ val innerSerial = if (sti.serializer == null) null else serializerInstance(sti.serializer, property.module, property.type)
+ val call = if (innerSerial == null) {
+ val readFunc =
+ inputClass.getFuncDesc("read${sti.elementMethodPrefix}ElementValue").single()
+ .let { context.getNameForDescriptor(it) }
+ JsInvocation(JsNameRef(readFunc, inputVar),
+ serialClassDescRef,
+ JsIntLiteral(i))
+ }
+ else {
+ val readFunc =
+ inputClass.getFuncDesc("read${sti.elementMethodPrefix}SerializableElementValue").single()
+ .let { context.getNameForDescriptor(it) }
+ JsInvocation(JsNameRef(readFunc, inputVar),
+ serialClassDescRef,
+ JsIntLiteral(i),
+ innerSerial)
+ }
+ // localPropI = ...
+ +JsAstUtils.assignment(
+ localProps[i],
+ call
+ ).makeStmt()
+ // need explicit unit instance
+ if (sti.unit) {
+ +JsAstUtils.assignment(
+ localProps[i],
+ context.getQualifiedReference(property.type.builtIns.unit)
+ ).makeStmt()
+ }
+ // char unboxing crutch
+ if (KotlinBuiltIns.isCharOrNullableChar(property.type) && !shouldBoxReturnValue(property.descriptor.getter)) {
+ +JsAstUtils.assignment(
+ localProps[i],
+ Translation.unboxIfNeeded(context, localProps[i], true)
+ ).makeStmt()
+ }
+
+ // bitMask[i] |= 1 << x
+ val bitPos = 1 shl (i % 32)
+ +JsBinaryOperation(JsBinaryOperator.ASG_BIT_OR,
+ bitMasks[i / 32],
+ JsIntLiteral(bitPos)
+ ).makeStmt()
+ // if (!readAll) break -- but only if this is last prop
+ if (i != orderedProperties.lastIndex)
+ +JsIf(JsAstUtils.not(readAllVar), JsBreak())
+ else {
+ // if (readAll) breakLoop else break
+ +JsIf(readAllVar, JsBreak(loopRef), JsBreak())
+ }
+ }
+ }
+ // case -1, default: break loop
+ case(JsIntLiteral(-1)) {}
+ default {
+ +JsBreak(loopRef)
+ }
+ }
+ }, loop)
+
+ // input.readEnd(desc)
+ val readEndF = inputClass.getFuncDesc("readEnd").single()
+ .let { context.getNameForDescriptor(it) }
+ +JsInvocation(
+ JsNameRef(readEndF, inputVar),
+ serialClassDescRef
+ ).makeStmt()
+
+ // deserialization constructor call
+ val constrDesc = KSerializerDescriptorResolver.createLoadConstructorDescriptor(serializableDescriptor, context.bindingContext())
+ val constrRef = context.getInnerNameForDescriptor(constrDesc).makeRef()
+ val args: MutableList = mutableListOf(bitMasks[0])
+ args += localProps
+ args += JsNullLiteral()
+ +JsReturn(JsInvocation(constrRef, args))
+ }
+
+ companion object {
+ fun translate(declaration: KtPureClassOrObject, descriptor: ClassDescriptor, translator: DeclarationBodyVisitor, context: TranslationContext) {
+ if (getSerializableClassDescriptorBySerializer(descriptor) != null)
+ SerializerJsTranslator(declaration, translator, context).generate()
+ }
+ }
+}
\ No newline at end of file
diff --git a/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/jvm/JVMCodegenUtil.kt b/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/jvm/JVMCodegenUtil.kt
index 539e866fc99..49fa38c9d80 100644
--- a/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/jvm/JVMCodegenUtil.kt
+++ b/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/jvm/JVMCodegenUtil.kt
@@ -21,28 +21,23 @@ import org.jetbrains.kotlin.codegen.*
import org.jetbrains.kotlin.descriptors.*
import org.jetbrains.kotlin.load.kotlin.TypeMappingMode
import org.jetbrains.kotlin.name.ClassId
-import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.resolve.descriptorUtil.classId
-import org.jetbrains.kotlin.resolve.descriptorUtil.getSuperClassNotAny
import org.jetbrains.kotlin.resolve.jvm.AsmTypes
import org.jetbrains.kotlin.resolve.jvm.diagnostics.OtherOrigin
import org.jetbrains.kotlin.resolve.jvm.jvmSignature.JvmMethodSignature
import org.jetbrains.kotlin.types.KotlinType
-import org.jetbrains.kotlin.types.typeUtil.containsTypeProjectionsInTopLevelArguments
+import org.jetbrains.kotlinx.serialization.compiler.backend.common.SerialTypeInfo
+import org.jetbrains.kotlinx.serialization.compiler.backend.common.findEnumTypeSerializer
+import org.jetbrains.kotlinx.serialization.compiler.backend.common.findPolymorphicSerializer
+import org.jetbrains.kotlinx.serialization.compiler.backend.common.requiresPolymorphism
import org.jetbrains.kotlinx.serialization.compiler.resolve.SerializableProperty
-import org.jetbrains.kotlinx.serialization.compiler.resolve.isInternalSerializable
+import org.jetbrains.kotlinx.serialization.compiler.resolve.internalPackageFqName
import org.jetbrains.kotlinx.serialization.compiler.resolve.toClassDescriptor
import org.jetbrains.kotlinx.serialization.compiler.resolve.typeSerializer
import org.jetbrains.org.objectweb.asm.Type
import org.jetbrains.org.objectweb.asm.commons.InstructionAdapter
-/**
- * @author Leonid Startsev
- * sandwwraith@gmail.com
- */
-
-internal val internalPackageFqName = FqName("kotlinx.serialization.internal")
internal val descType = Type.getObjectType("kotlinx/serialization/KSerialClassDesc")
internal val descImplType = Type.getObjectType("kotlinx/serialization/internal/SerialClassDescImpl")
internal val kOutputType = Type.getObjectType("kotlinx/serialization/KOutput")
@@ -84,7 +79,7 @@ fun InstructionAdapter.genKOutputMethodCall(property: SerializableProperty, clas
val useSerializer = stackValueSerializerInstance(classCodegen, sti)
if (!sti.unit) classCodegen.genPropertyOnStack(this, expressionCodegen.context, property.descriptor, propertyOwnerType, ownerVar)
invokevirtual(kOutputType.internalName,
- "write" + sti.nn + (if (useSerializer) "Serializable" else "") + "ElementValue",
+ "write" + sti.elementMethodPrefix + (if (useSerializer) "Serializable" else "") + "ElementValue",
"(" + descType.descriptor + "I" +
(if (useSerializer) kSerialSaverType.descriptor else "") +
(if (sti.unit) "" else sti.type.descriptor) + ")V", false)
@@ -121,7 +116,7 @@ internal val polymorphicSerializerId = ClassId(internalPackageFqName, Name.ident
internal val referenceArraySerializerId = ClassId(internalPackageFqName, Name.identifier("ReferenceArraySerializer"))
// returns false is property should not use serializer
-internal fun InstructionAdapter.stackValueSerializerInstance(codegen: ClassBodyCodegen, sti: SerialTypeInfo): Boolean {
+internal fun InstructionAdapter.stackValueSerializerInstance(codegen: ClassBodyCodegen, sti: JVMSerialTypeInfo): Boolean {
val serializer = sti.serializer ?: return false
return stackValueSerializerInstance(codegen, sti.property.module, sti.property.type, serializer, this)
}
@@ -161,9 +156,10 @@ internal fun stackValueSerializerInstance(codegen: ClassBodyCodegen, module: Mod
signature.append(AsmTypes.K_CLASS_TYPE.descriptor)
}
referenceArraySerializerId -> {
- // a special way to instantiate reference array serializer -- need an element java.lang.Class reference
+ // a special way to instantiate reference array serializer -- need an element KClass reference
aconst(codegen.typeMapper.mapType(kType.arguments[0].type, null, TypeMappingMode.GENERIC_ARGUMENT))
- signature.append("Ljava/lang/Class;")
+ AsmUtil.wrapJavaClassIntoKClass(this)
+ signature.append(AsmTypes.K_CLASS_TYPE.descriptor)
}
}
// all serializers get arguments with serializers of their generic types
@@ -189,19 +185,19 @@ internal fun stackValueSerializerInstance(codegen: ClassBodyCodegen, module: Mod
//
-class SerialTypeInfo(
- val property: SerializableProperty,
+class JVMSerialTypeInfo(
+ property: SerializableProperty,
val type: Type,
- val nn: String,
- val serializer: ClassDescriptor? = null,
- val unit: Boolean = false
-)
+ nn: String,
+ serializer: ClassDescriptor? = null,
+ unit: Boolean = false
+) : SerialTypeInfo(property, nn, serializer, unit)
-fun getSerialTypeInfo(property: SerializableProperty, type: Type): SerialTypeInfo {
+fun getSerialTypeInfo(property: SerializableProperty, type: Type): JVMSerialTypeInfo {
when (type.sort) {
BOOLEAN, BYTE, SHORT, INT, LONG, FLOAT, DOUBLE, CHAR -> {
val name = type.className
- return SerialTypeInfo(property, type, Character.toUpperCase(name[0]) + name.substring(1))
+ return JVMSerialTypeInfo(property, type, Character.toUpperCase(name[0]) + name.substring(1))
}
ARRAY -> {
// check for explicit serialization annotation on this property
@@ -216,20 +212,20 @@ fun getSerialTypeInfo(property: SerializableProperty, type: Type): SerialTypeInf
// primitive elements are not supported yet
}
}
- return SerialTypeInfo(property, Type.getType("Ljava/lang/Object;"),
- if (property.type.isMarkedNullable) "Nullable" else "", serializer)
+ return JVMSerialTypeInfo(property, Type.getType("Ljava/lang/Object;"),
+ if (property.type.isMarkedNullable) "Nullable" else "", serializer)
}
OBJECT -> {
// no explicit serializer for this property. Check other built in types
if (KotlinBuiltIns.isString(property.type))
- return SerialTypeInfo(property, Type.getType("Ljava/lang/String;"), "String")
+ return JVMSerialTypeInfo(property, Type.getType("Ljava/lang/String;"), "String")
if (KotlinBuiltIns.isUnit(property.type))
- return SerialTypeInfo(property, Type.getType("Lkotlin/Unit;"), "Unit", unit = true)
+ return JVMSerialTypeInfo(property, Type.getType("Lkotlin/Unit;"), "Unit", unit = true)
// todo: more efficient enum support here, but only for enums that don't define custom serializer
// otherwise, it is a serializer for some other type
val serializer = findTypeSerializer(property.module, property.type, type)
- return SerialTypeInfo(property, Type.getType("Ljava/lang/Object;"),
- if (property.type.isMarkedNullable) "Nullable" else "", serializer)
+ return JVMSerialTypeInfo(property, Type.getType("Ljava/lang/Object;"),
+ if (property.type.isMarkedNullable) "Nullable" else "", serializer)
}
else -> throw AssertionError("Unexpected sort for $type") // should not happen
}
@@ -239,22 +235,7 @@ fun findTypeSerializer(module: ModuleDescriptor, kType: KotlinType, asmType: Typ
return if (kType.requiresPolymorphism()) findPolymorphicSerializer(module)
else kType.typeSerializer.toClassDescriptor // check for serializer defined on the type
?: findStandardAsmTypeSerializer(module, asmType) // otherwise see if there is a standard serializer
- ?: findStandardKotlinTypeSerializer(module, kType)
-}
-
-fun KotlinType.requiresPolymorphism(): Boolean {
- return this.toClassDescriptor?.getSuperClassNotAny()?.isInternalSerializable == true
- || this.toClassDescriptor?.modality == Modality.OPEN
- || this.containsTypeProjectionsInTopLevelArguments() // List<*>
-}
-
-fun findPolymorphicSerializer(module: ModuleDescriptor): ClassDescriptor {
- return requireNotNull(module.findClassAcrossModuleDependencies(polymorphicSerializerId)) { "Can't locate polymorphic serializer definition" }
-}
-
-fun findStandardKotlinTypeSerializer(module: ModuleDescriptor, kType: KotlinType): ClassDescriptor? {
- val classDescriptor = kType.constructor.declarationDescriptor as? ClassDescriptor ?: return null
- return if (classDescriptor.kind == ClassKind.ENUM_CLASS) module.findClassAcrossModuleDependencies(enumSerializerId) else null
+ ?: findEnumTypeSerializer(module, kType)
}
fun findStandardAsmTypeSerializer(module: ModuleDescriptor, asmType: Type): ClassDescriptor? {
diff --git a/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/jvm/SerialInfoCodegenImpl.kt b/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/jvm/SerialInfoCodegenImpl.kt
index cf23f80708b..7714114202d 100644
--- a/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/jvm/SerialInfoCodegenImpl.kt
+++ b/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/jvm/SerialInfoCodegenImpl.kt
@@ -29,11 +29,6 @@ import org.jetbrains.kotlin.resolve.scopes.getDescriptorsFiltered
import org.jetbrains.org.objectweb.asm.Opcodes
import org.jetbrains.org.objectweb.asm.Type
-/**
- * @author Leonid Startsev
- * sandwwraith@gmail.com
- */
-
class SerialInfoCodegenImpl(val codegen: ImplementationBodyCodegen, val thisClass: ClassDescriptor, val bindingContext: BindingContext) {
val thisAsmType = codegen.typeMapper.mapClass(thisClass)
diff --git a/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/jvm/SerializableCodegenImpl.kt b/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/jvm/SerializableCodegenImpl.kt
index 13214487fdd..b1e6cf5dd2d 100644
--- a/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/jvm/SerializableCodegenImpl.kt
+++ b/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/jvm/SerializableCodegenImpl.kt
@@ -23,13 +23,13 @@ import org.jetbrains.kotlin.descriptors.ClassConstructorDescriptor
import org.jetbrains.kotlin.descriptors.ClassDescriptor
import org.jetbrains.kotlin.descriptors.FunctionDescriptor
import org.jetbrains.kotlin.descriptors.PropertyDescriptor
-import org.jetbrains.kotlin.psi.KtAnonymousInitializer
import org.jetbrains.kotlin.psi.KtParameter
-import org.jetbrains.kotlin.psi.KtProperty
-import org.jetbrains.kotlin.resolve.BindingContext
import org.jetbrains.kotlin.resolve.descriptorUtil.getSuperClassOrAny
import org.jetbrains.kotlin.resolve.jvm.jvmSignature.JvmMethodSignature
import org.jetbrains.kotlinx.serialization.compiler.backend.common.SerializableCodegen
+import org.jetbrains.kotlinx.serialization.compiler.backend.common.anonymousInitializers
+import org.jetbrains.kotlinx.serialization.compiler.backend.common.bodyPropertiesDescriptorsMap
+import org.jetbrains.kotlinx.serialization.compiler.backend.common.primaryPropertiesDescriptorsMap
import org.jetbrains.kotlinx.serialization.compiler.resolve.SerializableProperties
import org.jetbrains.kotlinx.serialization.compiler.resolve.SerializableProperty
import org.jetbrains.kotlinx.serialization.compiler.resolve.isInternalSerializable
@@ -37,11 +37,6 @@ import org.jetbrains.org.objectweb.asm.Label
import org.jetbrains.org.objectweb.asm.Type
import org.jetbrains.org.objectweb.asm.commons.InstructionAdapter
-/**
- * @author Leonid Startsev
- * sandwwraith@gmail.com
- */
-
class SerializableCodegenImpl(
private val classCodegen: ImplementationBodyCodegen,
serializableClass: ClassDescriptor
@@ -57,15 +52,9 @@ class SerializableCodegenImpl(
}
}
- private val descToProps = classCodegen.myClass.declarations
- .asSequence()
- .filterIsInstance()
- .associateBy { classCodegen.bindingContext[BindingContext.VARIABLE, it]!! }
+ private val descToProps = classCodegen.myClass.bodyPropertiesDescriptorsMap(classCodegen.bindingContext)
- private val paramsToProps: Map = classCodegen.myClass.primaryConstructorParameters
- .asSequence()
- .filter { it.hasValOrVar() }
- .associateBy { classCodegen.bindingContext[BindingContext.PRIMARY_CONSTRUCTOR_PARAMETER, it]!! }
+ private val paramsToProps: Map = classCodegen.myClass.primaryPropertiesDescriptorsMap(classCodegen.bindingContext)
private fun getProp(prop: SerializableProperty) = descToProps[prop.descriptor]
private fun getParam(prop: SerializableProperty) = paramsToProps[prop.descriptor]
@@ -167,10 +156,7 @@ class SerializableCodegenImpl(
// init blocks
// todo: proper order with other initializers
- classCodegen.myClass.declarations
- .asSequence()
- .filterIsInstance()
- .mapNotNull { it.body }
+ classCodegen.myClass.anonymousInitializers()
.forEach { exprCodegen.gen(it, Type.VOID_TYPE) }
areturn(Type.VOID_TYPE)
}
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 831ca90ac62..b09058942d8 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
@@ -23,12 +23,10 @@ import org.jetbrains.kotlin.codegen.StackValue
import org.jetbrains.kotlin.descriptors.ClassDescriptor
import org.jetbrains.kotlin.descriptors.FunctionDescriptor
import org.jetbrains.kotlin.descriptors.PropertyDescriptor
-import org.jetbrains.kotlinx.serialization.compiler.backend.common.SerializerCodegen
-import org.jetbrains.kotlinx.serialization.compiler.resolve.KSerializerDescriptorResolver
-import org.jetbrains.kotlin.psi.ValueArgument
-import org.jetbrains.kotlin.resolve.descriptorUtil.fqNameSafe
import org.jetbrains.kotlin.resolve.jvm.diagnostics.OtherOrigin
-import org.jetbrains.kotlin.resolve.lazy.descriptors.LazyAnnotationDescriptor
+import org.jetbrains.kotlinx.serialization.compiler.backend.common.SerializerCodegen
+import org.jetbrains.kotlinx.serialization.compiler.backend.common.annotationVarsAndDesc
+import org.jetbrains.kotlinx.serialization.compiler.resolve.KSerializerDescriptorResolver
import org.jetbrains.kotlinx.serialization.compiler.resolve.getSerializableClassDescriptorBySerializer
import org.jetbrains.kotlinx.serialization.compiler.resolve.isInternalSerializable
import org.jetbrains.org.objectweb.asm.Label
@@ -79,9 +77,7 @@ class SerializerCodegenImpl(
anew(Type.getObjectType(implType))
dup()
val sb = StringBuilder("(")
- val args: List = (property.descriptor.annotations.findAnnotation(annotationClass.fqNameSafe) as? LazyAnnotationDescriptor)?.annotationEntry?.valueArguments.orEmpty()
- val consParams = annotationClass.unsubstitutedPrimaryConstructor?.valueParameters.orEmpty()
- if (args.size != consParams.size) throw IllegalArgumentException("Can't use arguments with defaults for serializable annotations yet")
+ val (args, consParams) = property.annotationVarsAndDesc(annotationClass)
for (i in consParams.indices) {
val decl = args[i]
val desc = consParams[i]
@@ -243,7 +239,7 @@ class SerializerCodegenImpl(
val sti = getSerialTypeInfo(property, propertyType)
val useSerializer = stackValueSerializerInstance(codegen, sti)
invokevirtual(kInputType.internalName,
- "read" + sti.nn + (if (useSerializer) "Serializable" else "") + "ElementValue",
+ "read" + sti.elementMethodPrefix + (if (useSerializer) "Serializable" else "") + "ElementValue",
"(" + descType.descriptor + "I" +
(if (useSerializer) kSerialLoaderType.descriptor else "")
+ ")" + (if (sti.unit) "V" else sti.type.descriptor), false)
diff --git a/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/extensions/SerializationComponentRegistrar.kt b/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/extensions/SerializationComponentRegistrar.kt
index a8840f595dd..89b79b98241 100644
--- a/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/extensions/SerializationComponentRegistrar.kt
+++ b/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/extensions/SerializationComponentRegistrar.kt
@@ -20,11 +20,13 @@ import com.intellij.mock.MockProject
import org.jetbrains.kotlin.codegen.extensions.ExpressionCodegenExtension
import org.jetbrains.kotlin.compiler.plugin.ComponentRegistrar
import org.jetbrains.kotlin.config.CompilerConfiguration
+import org.jetbrains.kotlin.js.translate.extensions.JsSyntheticTranslateExtension
import org.jetbrains.kotlin.resolve.extensions.SyntheticResolveExtension
class SerializationComponentRegistrar : ComponentRegistrar {
override fun registerProjectComponents(project: MockProject, configuration: CompilerConfiguration) {
ExpressionCodegenExtension.registerExtension(project, SerializationCodegenExtension())
SyntheticResolveExtension.registerExtension(project, SerializationResolveExtension())
+ JsSyntheticTranslateExtension.registerExtension(project, SerializationJsExtension())
}
}
\ No newline at end of file
diff --git a/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/extensions/SerializationJsExtension.kt b/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/extensions/SerializationJsExtension.kt
new file mode 100644
index 00000000000..1847bd7062b
--- /dev/null
+++ b/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/extensions/SerializationJsExtension.kt
@@ -0,0 +1,32 @@
+/*
+ * Copyright 2010-2017 JetBrains s.r.o.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.jetbrains.kotlinx.serialization.compiler.extensions
+
+import org.jetbrains.kotlin.descriptors.ClassDescriptor
+import org.jetbrains.kotlin.js.translate.context.TranslationContext
+import org.jetbrains.kotlin.js.translate.declaration.DeclarationBodyVisitor
+import org.jetbrains.kotlin.js.translate.extensions.JsSyntheticTranslateExtension
+import org.jetbrains.kotlin.psi.KtPureClassOrObject
+import org.jetbrains.kotlinx.serialization.compiler.backend.js.SerializableJsTranslator
+import org.jetbrains.kotlinx.serialization.compiler.backend.js.SerializerJsTranslator
+
+class SerializationJsExtension: JsSyntheticTranslateExtension {
+ override fun generateClassSyntheticParts(declaration: KtPureClassOrObject, descriptor: ClassDescriptor, translator: DeclarationBodyVisitor, context: TranslationContext) {
+ SerializerJsTranslator.translate(declaration, descriptor, translator, context)
+ SerializableJsTranslator.translate(declaration, descriptor, translator, context)
+ }
+}
\ No newline at end of file
diff --git a/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/resolve/KSerializationUtil.kt b/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/resolve/KSerializationUtil.kt
index f8a10646060..0a53f641ecf 100644
--- a/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/resolve/KSerializationUtil.kt
+++ b/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/resolve/KSerializationUtil.kt
@@ -29,6 +29,7 @@ import org.jetbrains.kotlin.resolve.descriptorUtil.module
import org.jetbrains.kotlin.types.*
internal val packageFqName = FqName("kotlinx.serialization")
+internal val internalPackageFqName = FqName("kotlinx.serialization.internal")
// ---- kotlin.serialization.KSerializer
@@ -58,12 +59,12 @@ internal val javaSerializableFqName = javaIOPackageFqName.child(javaSerializable
fun isJavaSerializable(type: KotlinType?): Boolean =
type != null && KotlinBuiltIns.isConstructedFromGivenClass(type, javaSerializableFqName)
-fun ClassDescriptor.getJavaSerializableDescriptor(): ClassDescriptor =
- module.findClassAcrossModuleDependencies(ClassId(javaIOPackageFqName, javaSerializableName))!!
+fun ClassDescriptor.getJavaSerializableDescriptor(): ClassDescriptor? =
+ module.findClassAcrossModuleDependencies(ClassId(javaIOPackageFqName, javaSerializableName))
-fun ClassDescriptor.getJavaSerializableType(): SimpleType {
- return KotlinTypeFactory.simpleNotNullType(Annotations.EMPTY, getJavaSerializableDescriptor(), emptyList())
-}
+// null on JS frontend
+fun ClassDescriptor.getJavaSerializableType(): SimpleType? =
+ getJavaSerializableDescriptor()?.let { KotlinTypeFactory.simpleNotNullType(Annotations.EMPTY, it, emptyList()) }
// ---- kotlin.serialization.Serializable(with=xxx)
@@ -175,6 +176,9 @@ fun ClassDescriptor.getKSerializerConstructorMarker(): ClassDescriptor =
module.findClassAcrossModuleDependencies(ClassId(packageFqName, kSerializerConstructorMarkerName))!!
fun ClassDescriptor.getClassFromSerializationPackage(classSimpleName: String) =
- module.findClassAcrossModuleDependencies(ClassId(packageFqName, Name.identifier(classSimpleName)))!!
+ requireNotNull(module.findClassAcrossModuleDependencies(ClassId(packageFqName, Name.identifier(classSimpleName)))) {"Can't locate class $classSimpleName"}
+
+fun ClassDescriptor.getClassFromInternalSerializationPackage(classSimpleName: String) =
+ requireNotNull(module.findClassAcrossModuleDependencies(ClassId(internalPackageFqName, Name.identifier(classSimpleName)))) {"Can't locate class $classSimpleName"}
fun ClassDescriptor.toSimpleType(nullable: Boolean = true) = KotlinTypeFactory.simpleType(Annotations.EMPTY, this.typeConstructor, emptyList(), nullable)
diff --git a/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/resolve/KSerializerDescriptorResolver.kt b/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/resolve/KSerializerDescriptorResolver.kt
index b7d970cd2c4..1c80daa7829 100644
--- a/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/resolve/KSerializerDescriptorResolver.kt
+++ b/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/resolve/KSerializerDescriptorResolver.kt
@@ -50,7 +50,7 @@ object KSerializerDescriptorResolver {
fun addSerializableSupertypes(classDescriptor: ClassDescriptor, supertypes: MutableList) {
if (classDescriptor.isInternalSerializable && supertypes.none(::isJavaSerializable)) {
- supertypes.add(classDescriptor.getJavaSerializableType())
+ classDescriptor.getJavaSerializableType()?.let { supertypes.add(it) }
}
}