Js synthetic generation plugin.

Fix for JS frontend resolving

Generating serialClassDesc property and save method (w/o virtual calls).

JS Serialization: Load function and synthetic constructor

User-defined serial annotations support

Reference array serialization in JS

Reference array serializer now requires KClass instance as first constructor argument

Finding enum serializer in common module for support in JS
This commit is contained in:
Leonid Startsev
2017-08-14 14:18:18 +03:00
parent 841693643a
commit ef11fb5857
16 changed files with 735 additions and 92 deletions
@@ -12,5 +12,6 @@
<orderEntry type="module" module-name="frontend.java" scope="PROVIDED" />
<orderEntry type="module" module-name="util" scope="PROVIDED" />
<orderEntry type="module" module-name="backend" scope="PROVIDED" />
<orderEntry type="module" module-name="js.translator" scope="PROVIDED" />
</component>
</module>
@@ -9,5 +9,6 @@
<extensions defaultExtensionNs="org.jetbrains.kotlin">
<expressionCodegenExtension implementation="org.jetbrains.kotlinx.serialization.compiler.extensions.SerializationCodegenExtension"/>
<syntheticResolveExtension implementation="org.jetbrains.kotlinx.serialization.compiler.extensions.SerializationResolveExtension"/>
<jsSyntheticTranslateExtension implementation="org.jetbrains.kotlinx.serialization.compiler.extensions.SerializationJsExtension"/>
</extensions>
</idea-plugin>
@@ -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)
@@ -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)
}
@@ -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<PropertyDescriptor, KtProperty> = declarations
.asSequence()
.filterIsInstance<KtProperty>()
.associateBy { (bindingContext[BindingContext.DECLARATION_TO_DESCRIPTOR, it] as? PropertyDescriptor)!! }
fun KtPureClassOrObject.primaryPropertiesDescriptorsMap(bindingContext: BindingContext): Map<PropertyDescriptor, KtParameter> = primaryConstructorParameters
.asSequence()
.filter { it.hasValOrVar() }
.associateBy { bindingContext[BindingContext.PRIMARY_CONSTRUCTOR_PARAMETER, it]!! }
fun KtPureClassOrObject.anonymousInitializers() = declarations
.asSequence()
.filterIsInstance<KtAnonymousInitializer>()
.mapNotNull { it.body }
.toList()
fun SerializableProperty.annotationVarsAndDesc(annotationClass: ClassDescriptor): Pair<List<ValueArgument>, List<ValueParameterDescriptor>> {
val args: List<ValueArgument> = (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
}
@@ -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<JsStatement>
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<JsSwitchMember> = 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
}
@@ -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<PropertyDescriptor, KtExpression?> = 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()
}
}
}
@@ -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<JsExpression> = 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()
}
}
}
@@ -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? {
@@ -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)
@@ -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<KtProperty>()
.associateBy { classCodegen.bindingContext[BindingContext.VARIABLE, it]!! }
private val descToProps = classCodegen.myClass.bodyPropertiesDescriptorsMap(classCodegen.bindingContext)
private val paramsToProps: Map<PropertyDescriptor, KtParameter> = classCodegen.myClass.primaryConstructorParameters
.asSequence()
.filter { it.hasValOrVar() }
.associateBy { classCodegen.bindingContext[BindingContext.PRIMARY_CONSTRUCTOR_PARAMETER, it]!! }
private val paramsToProps: Map<PropertyDescriptor, KtParameter> = 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<KtAnonymousInitializer>()
.mapNotNull { it.body }
classCodegen.myClass.anonymousInitializers()
.forEach { exprCodegen.gen(it, Type.VOID_TYPE) }
areturn(Type.VOID_TYPE)
}
@@ -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<ValueArgument> = (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)
@@ -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())
}
}
@@ -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)
}
}
@@ -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)
@@ -50,7 +50,7 @@ object KSerializerDescriptorResolver {
fun addSerializableSupertypes(classDescriptor: ClassDescriptor, supertypes: MutableList<KotlinType>) {
if (classDescriptor.isInternalSerializable && supertypes.none(::isJavaSerializable)) {
supertypes.add(classDescriptor.getJavaSerializableType())
classDescriptor.getJavaSerializableType()?.let { supertypes.add(it) }
}
}