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:
+1
@@ -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>
|
||||
-5
@@ -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)
|
||||
|
||||
+3
-2
@@ -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)
|
||||
}
|
||||
|
||||
+137
@@ -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
|
||||
}
|
||||
+84
@@ -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
|
||||
}
|
||||
+112
@@ -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()
|
||||
}
|
||||
}
|
||||
}
|
||||
+316
@@ -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()
|
||||
}
|
||||
}
|
||||
}
|
||||
+25
-44
@@ -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? {
|
||||
|
||||
-5
@@ -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)
|
||||
|
||||
|
||||
+6
-20
@@ -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)
|
||||
}
|
||||
|
||||
+5
-9
@@ -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)
|
||||
|
||||
+2
@@ -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())
|
||||
}
|
||||
}
|
||||
+32
@@ -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)
|
||||
}
|
||||
}
|
||||
+10
-6
@@ -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)
|
||||
|
||||
+1
-1
@@ -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) }
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user