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="frontend.java" scope="PROVIDED" />
|
||||||
<orderEntry type="module" module-name="util" scope="PROVIDED" />
|
<orderEntry type="module" module-name="util" scope="PROVIDED" />
|
||||||
<orderEntry type="module" module-name="backend" scope="PROVIDED" />
|
<orderEntry type="module" module-name="backend" scope="PROVIDED" />
|
||||||
|
<orderEntry type="module" module-name="js.translator" scope="PROVIDED" />
|
||||||
</component>
|
</component>
|
||||||
</module>
|
</module>
|
||||||
@@ -9,5 +9,6 @@
|
|||||||
<extensions defaultExtensionNs="org.jetbrains.kotlin">
|
<extensions defaultExtensionNs="org.jetbrains.kotlin">
|
||||||
<expressionCodegenExtension implementation="org.jetbrains.kotlinx.serialization.compiler.extensions.SerializationCodegenExtension"/>
|
<expressionCodegenExtension implementation="org.jetbrains.kotlinx.serialization.compiler.extensions.SerializationCodegenExtension"/>
|
||||||
<syntheticResolveExtension implementation="org.jetbrains.kotlinx.serialization.compiler.extensions.SerializationResolveExtension"/>
|
<syntheticResolveExtension implementation="org.jetbrains.kotlinx.serialization.compiler.extensions.SerializationResolveExtension"/>
|
||||||
|
<jsSyntheticTranslateExtension implementation="org.jetbrains.kotlinx.serialization.compiler.extensions.SerializationJsExtension"/>
|
||||||
</extensions>
|
</extensions>
|
||||||
</idea-plugin>
|
</idea-plugin>
|
||||||
-5
@@ -25,11 +25,6 @@ import org.jetbrains.kotlin.psi.KtPureClassOrObject
|
|||||||
import org.jetbrains.kotlin.psi.synthetics.findClassDescriptor
|
import org.jetbrains.kotlin.psi.synthetics.findClassDescriptor
|
||||||
import org.jetbrains.kotlin.resolve.BindingContext
|
import org.jetbrains.kotlin.resolve.BindingContext
|
||||||
|
|
||||||
/**
|
|
||||||
* @author Leonid Startsev
|
|
||||||
* sandwwraith@gmail.com
|
|
||||||
*/
|
|
||||||
|
|
||||||
abstract class SerializableCodegen(declaration: KtPureClassOrObject, private val bindingContext: BindingContext) {
|
abstract class SerializableCodegen(declaration: KtPureClassOrObject, private val bindingContext: BindingContext) {
|
||||||
protected val serializableDescriptor: ClassDescriptor = declaration.findClassDescriptor(bindingContext)
|
protected val serializableDescriptor: ClassDescriptor = declaration.findClassDescriptor(bindingContext)
|
||||||
protected val properties = SerializableProperties(serializableDescriptor, bindingContext)
|
protected val properties = SerializableProperties(serializableDescriptor, bindingContext)
|
||||||
|
|||||||
+3
-2
@@ -45,6 +45,8 @@ abstract class SerializerCodegen(declaration: KtPureClassOrObject, bindingContex
|
|||||||
generateSerialDesc()
|
generateSerialDesc()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected val serialDescPropertyDescriptor = getPropertyToGenerate(serializerDescriptor, KSerializerDescriptorResolver.SERIAL_DESC_FIELD,
|
||||||
|
serializerDescriptor::checkSerializableClassPropertyResult)
|
||||||
protected abstract fun generateSerialDesc()
|
protected abstract fun generateSerialDesc()
|
||||||
|
|
||||||
protected abstract fun generateSerializableClassProperty(property: PropertyDescriptor)
|
protected abstract fun generateSerializableClassProperty(property: PropertyDescriptor)
|
||||||
@@ -54,8 +56,7 @@ abstract class SerializerCodegen(declaration: KtPureClassOrObject, bindingContex
|
|||||||
protected abstract fun generateLoad(function: FunctionDescriptor)
|
protected abstract fun generateLoad(function: FunctionDescriptor)
|
||||||
|
|
||||||
private fun generateSerializableClassPropertyIfNeeded() {
|
private fun generateSerializableClassPropertyIfNeeded() {
|
||||||
val property = getPropertyToGenerate(serializerDescriptor, KSerializerDescriptorResolver.SERIAL_DESC_FIELD,
|
val property = serialDescPropertyDescriptor
|
||||||
serializerDescriptor::checkSerializableClassPropertyResult)
|
|
||||||
?: return
|
?: return
|
||||||
generateSerializableClassProperty(property)
|
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.descriptors.*
|
||||||
import org.jetbrains.kotlin.load.kotlin.TypeMappingMode
|
import org.jetbrains.kotlin.load.kotlin.TypeMappingMode
|
||||||
import org.jetbrains.kotlin.name.ClassId
|
import org.jetbrains.kotlin.name.ClassId
|
||||||
import org.jetbrains.kotlin.name.FqName
|
|
||||||
import org.jetbrains.kotlin.name.Name
|
import org.jetbrains.kotlin.name.Name
|
||||||
import org.jetbrains.kotlin.resolve.descriptorUtil.classId
|
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.AsmTypes
|
||||||
import org.jetbrains.kotlin.resolve.jvm.diagnostics.OtherOrigin
|
import org.jetbrains.kotlin.resolve.jvm.diagnostics.OtherOrigin
|
||||||
import org.jetbrains.kotlin.resolve.jvm.jvmSignature.JvmMethodSignature
|
import org.jetbrains.kotlin.resolve.jvm.jvmSignature.JvmMethodSignature
|
||||||
import org.jetbrains.kotlin.types.KotlinType
|
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.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.toClassDescriptor
|
||||||
import org.jetbrains.kotlinx.serialization.compiler.resolve.typeSerializer
|
import org.jetbrains.kotlinx.serialization.compiler.resolve.typeSerializer
|
||||||
import org.jetbrains.org.objectweb.asm.Type
|
import org.jetbrains.org.objectweb.asm.Type
|
||||||
import org.jetbrains.org.objectweb.asm.commons.InstructionAdapter
|
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 descType = Type.getObjectType("kotlinx/serialization/KSerialClassDesc")
|
||||||
internal val descImplType = Type.getObjectType("kotlinx/serialization/internal/SerialClassDescImpl")
|
internal val descImplType = Type.getObjectType("kotlinx/serialization/internal/SerialClassDescImpl")
|
||||||
internal val kOutputType = Type.getObjectType("kotlinx/serialization/KOutput")
|
internal val kOutputType = Type.getObjectType("kotlinx/serialization/KOutput")
|
||||||
@@ -84,7 +79,7 @@ fun InstructionAdapter.genKOutputMethodCall(property: SerializableProperty, clas
|
|||||||
val useSerializer = stackValueSerializerInstance(classCodegen, sti)
|
val useSerializer = stackValueSerializerInstance(classCodegen, sti)
|
||||||
if (!sti.unit) classCodegen.genPropertyOnStack(this, expressionCodegen.context, property.descriptor, propertyOwnerType, ownerVar)
|
if (!sti.unit) classCodegen.genPropertyOnStack(this, expressionCodegen.context, property.descriptor, propertyOwnerType, ownerVar)
|
||||||
invokevirtual(kOutputType.internalName,
|
invokevirtual(kOutputType.internalName,
|
||||||
"write" + sti.nn + (if (useSerializer) "Serializable" else "") + "ElementValue",
|
"write" + sti.elementMethodPrefix + (if (useSerializer) "Serializable" else "") + "ElementValue",
|
||||||
"(" + descType.descriptor + "I" +
|
"(" + descType.descriptor + "I" +
|
||||||
(if (useSerializer) kSerialSaverType.descriptor else "") +
|
(if (useSerializer) kSerialSaverType.descriptor else "") +
|
||||||
(if (sti.unit) "" else sti.type.descriptor) + ")V", false)
|
(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"))
|
internal val referenceArraySerializerId = ClassId(internalPackageFqName, Name.identifier("ReferenceArraySerializer"))
|
||||||
|
|
||||||
// returns false is property should not use serializer
|
// 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
|
val serializer = sti.serializer ?: return false
|
||||||
return stackValueSerializerInstance(codegen, sti.property.module, sti.property.type, serializer, this)
|
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)
|
signature.append(AsmTypes.K_CLASS_TYPE.descriptor)
|
||||||
}
|
}
|
||||||
referenceArraySerializerId -> {
|
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))
|
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
|
// all serializers get arguments with serializers of their generic types
|
||||||
@@ -189,19 +185,19 @@ internal fun stackValueSerializerInstance(codegen: ClassBodyCodegen, module: Mod
|
|||||||
//
|
//
|
||||||
|
|
||||||
|
|
||||||
class SerialTypeInfo(
|
class JVMSerialTypeInfo(
|
||||||
val property: SerializableProperty,
|
property: SerializableProperty,
|
||||||
val type: Type,
|
val type: Type,
|
||||||
val nn: String,
|
nn: String,
|
||||||
val serializer: ClassDescriptor? = null,
|
serializer: ClassDescriptor? = null,
|
||||||
val unit: Boolean = false
|
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) {
|
when (type.sort) {
|
||||||
BOOLEAN, BYTE, SHORT, INT, LONG, FLOAT, DOUBLE, CHAR -> {
|
BOOLEAN, BYTE, SHORT, INT, LONG, FLOAT, DOUBLE, CHAR -> {
|
||||||
val name = type.className
|
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 -> {
|
ARRAY -> {
|
||||||
// check for explicit serialization annotation on this property
|
// 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
|
// primitive elements are not supported yet
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return SerialTypeInfo(property, Type.getType("Ljava/lang/Object;"),
|
return JVMSerialTypeInfo(property, Type.getType("Ljava/lang/Object;"),
|
||||||
if (property.type.isMarkedNullable) "Nullable" else "", serializer)
|
if (property.type.isMarkedNullable) "Nullable" else "", serializer)
|
||||||
}
|
}
|
||||||
OBJECT -> {
|
OBJECT -> {
|
||||||
// no explicit serializer for this property. Check other built in types
|
// no explicit serializer for this property. Check other built in types
|
||||||
if (KotlinBuiltIns.isString(property.type))
|
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))
|
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
|
// 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
|
// otherwise, it is a serializer for some other type
|
||||||
val serializer = findTypeSerializer(property.module, property.type, type)
|
val serializer = findTypeSerializer(property.module, property.type, type)
|
||||||
return SerialTypeInfo(property, Type.getType("Ljava/lang/Object;"),
|
return JVMSerialTypeInfo(property, Type.getType("Ljava/lang/Object;"),
|
||||||
if (property.type.isMarkedNullable) "Nullable" else "", serializer)
|
if (property.type.isMarkedNullable) "Nullable" else "", serializer)
|
||||||
}
|
}
|
||||||
else -> throw AssertionError("Unexpected sort for $type") // should not happen
|
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)
|
return if (kType.requiresPolymorphism()) findPolymorphicSerializer(module)
|
||||||
else kType.typeSerializer.toClassDescriptor // check for serializer defined on the type
|
else kType.typeSerializer.toClassDescriptor // check for serializer defined on the type
|
||||||
?: findStandardAsmTypeSerializer(module, asmType) // otherwise see if there is a standard serializer
|
?: findStandardAsmTypeSerializer(module, asmType) // otherwise see if there is a standard serializer
|
||||||
?: findStandardKotlinTypeSerializer(module, kType)
|
?: findEnumTypeSerializer(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
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fun findStandardAsmTypeSerializer(module: ModuleDescriptor, asmType: Type): ClassDescriptor? {
|
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.Opcodes
|
||||||
import org.jetbrains.org.objectweb.asm.Type
|
import org.jetbrains.org.objectweb.asm.Type
|
||||||
|
|
||||||
/**
|
|
||||||
* @author Leonid Startsev
|
|
||||||
* sandwwraith@gmail.com
|
|
||||||
*/
|
|
||||||
|
|
||||||
class SerialInfoCodegenImpl(val codegen: ImplementationBodyCodegen, val thisClass: ClassDescriptor, val bindingContext: BindingContext) {
|
class SerialInfoCodegenImpl(val codegen: ImplementationBodyCodegen, val thisClass: ClassDescriptor, val bindingContext: BindingContext) {
|
||||||
val thisAsmType = codegen.typeMapper.mapClass(thisClass)
|
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.ClassDescriptor
|
||||||
import org.jetbrains.kotlin.descriptors.FunctionDescriptor
|
import org.jetbrains.kotlin.descriptors.FunctionDescriptor
|
||||||
import org.jetbrains.kotlin.descriptors.PropertyDescriptor
|
import org.jetbrains.kotlin.descriptors.PropertyDescriptor
|
||||||
import org.jetbrains.kotlin.psi.KtAnonymousInitializer
|
|
||||||
import org.jetbrains.kotlin.psi.KtParameter
|
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.descriptorUtil.getSuperClassOrAny
|
||||||
import org.jetbrains.kotlin.resolve.jvm.jvmSignature.JvmMethodSignature
|
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.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.SerializableProperties
|
||||||
import org.jetbrains.kotlinx.serialization.compiler.resolve.SerializableProperty
|
import org.jetbrains.kotlinx.serialization.compiler.resolve.SerializableProperty
|
||||||
import org.jetbrains.kotlinx.serialization.compiler.resolve.isInternalSerializable
|
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.Type
|
||||||
import org.jetbrains.org.objectweb.asm.commons.InstructionAdapter
|
import org.jetbrains.org.objectweb.asm.commons.InstructionAdapter
|
||||||
|
|
||||||
/**
|
|
||||||
* @author Leonid Startsev
|
|
||||||
* sandwwraith@gmail.com
|
|
||||||
*/
|
|
||||||
|
|
||||||
class SerializableCodegenImpl(
|
class SerializableCodegenImpl(
|
||||||
private val classCodegen: ImplementationBodyCodegen,
|
private val classCodegen: ImplementationBodyCodegen,
|
||||||
serializableClass: ClassDescriptor
|
serializableClass: ClassDescriptor
|
||||||
@@ -57,15 +52,9 @@ class SerializableCodegenImpl(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private val descToProps = classCodegen.myClass.declarations
|
private val descToProps = classCodegen.myClass.bodyPropertiesDescriptorsMap(classCodegen.bindingContext)
|
||||||
.asSequence()
|
|
||||||
.filterIsInstance<KtProperty>()
|
|
||||||
.associateBy { classCodegen.bindingContext[BindingContext.VARIABLE, it]!! }
|
|
||||||
|
|
||||||
private val paramsToProps: Map<PropertyDescriptor, KtParameter> = classCodegen.myClass.primaryConstructorParameters
|
private val paramsToProps: Map<PropertyDescriptor, KtParameter> = classCodegen.myClass.primaryPropertiesDescriptorsMap(classCodegen.bindingContext)
|
||||||
.asSequence()
|
|
||||||
.filter { it.hasValOrVar() }
|
|
||||||
.associateBy { classCodegen.bindingContext[BindingContext.PRIMARY_CONSTRUCTOR_PARAMETER, it]!! }
|
|
||||||
|
|
||||||
private fun getProp(prop: SerializableProperty) = descToProps[prop.descriptor]
|
private fun getProp(prop: SerializableProperty) = descToProps[prop.descriptor]
|
||||||
private fun getParam(prop: SerializableProperty) = paramsToProps[prop.descriptor]
|
private fun getParam(prop: SerializableProperty) = paramsToProps[prop.descriptor]
|
||||||
@@ -167,10 +156,7 @@ class SerializableCodegenImpl(
|
|||||||
|
|
||||||
// init blocks
|
// init blocks
|
||||||
// todo: proper order with other initializers
|
// todo: proper order with other initializers
|
||||||
classCodegen.myClass.declarations
|
classCodegen.myClass.anonymousInitializers()
|
||||||
.asSequence()
|
|
||||||
.filterIsInstance<KtAnonymousInitializer>()
|
|
||||||
.mapNotNull { it.body }
|
|
||||||
.forEach { exprCodegen.gen(it, Type.VOID_TYPE) }
|
.forEach { exprCodegen.gen(it, Type.VOID_TYPE) }
|
||||||
areturn(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.ClassDescriptor
|
||||||
import org.jetbrains.kotlin.descriptors.FunctionDescriptor
|
import org.jetbrains.kotlin.descriptors.FunctionDescriptor
|
||||||
import org.jetbrains.kotlin.descriptors.PropertyDescriptor
|
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.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.getSerializableClassDescriptorBySerializer
|
||||||
import org.jetbrains.kotlinx.serialization.compiler.resolve.isInternalSerializable
|
import org.jetbrains.kotlinx.serialization.compiler.resolve.isInternalSerializable
|
||||||
import org.jetbrains.org.objectweb.asm.Label
|
import org.jetbrains.org.objectweb.asm.Label
|
||||||
@@ -79,9 +77,7 @@ class SerializerCodegenImpl(
|
|||||||
anew(Type.getObjectType(implType))
|
anew(Type.getObjectType(implType))
|
||||||
dup()
|
dup()
|
||||||
val sb = StringBuilder("(")
|
val sb = StringBuilder("(")
|
||||||
val args: List<ValueArgument> = (property.descriptor.annotations.findAnnotation(annotationClass.fqNameSafe) as? LazyAnnotationDescriptor)?.annotationEntry?.valueArguments.orEmpty()
|
val (args, consParams) = property.annotationVarsAndDesc(annotationClass)
|
||||||
val consParams = annotationClass.unsubstitutedPrimaryConstructor?.valueParameters.orEmpty()
|
|
||||||
if (args.size != consParams.size) throw IllegalArgumentException("Can't use arguments with defaults for serializable annotations yet")
|
|
||||||
for (i in consParams.indices) {
|
for (i in consParams.indices) {
|
||||||
val decl = args[i]
|
val decl = args[i]
|
||||||
val desc = consParams[i]
|
val desc = consParams[i]
|
||||||
@@ -243,7 +239,7 @@ class SerializerCodegenImpl(
|
|||||||
val sti = getSerialTypeInfo(property, propertyType)
|
val sti = getSerialTypeInfo(property, propertyType)
|
||||||
val useSerializer = stackValueSerializerInstance(codegen, sti)
|
val useSerializer = stackValueSerializerInstance(codegen, sti)
|
||||||
invokevirtual(kInputType.internalName,
|
invokevirtual(kInputType.internalName,
|
||||||
"read" + sti.nn + (if (useSerializer) "Serializable" else "") + "ElementValue",
|
"read" + sti.elementMethodPrefix + (if (useSerializer) "Serializable" else "") + "ElementValue",
|
||||||
"(" + descType.descriptor + "I" +
|
"(" + descType.descriptor + "I" +
|
||||||
(if (useSerializer) kSerialLoaderType.descriptor else "")
|
(if (useSerializer) kSerialLoaderType.descriptor else "")
|
||||||
+ ")" + (if (sti.unit) "V" else sti.type.descriptor), false)
|
+ ")" + (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.codegen.extensions.ExpressionCodegenExtension
|
||||||
import org.jetbrains.kotlin.compiler.plugin.ComponentRegistrar
|
import org.jetbrains.kotlin.compiler.plugin.ComponentRegistrar
|
||||||
import org.jetbrains.kotlin.config.CompilerConfiguration
|
import org.jetbrains.kotlin.config.CompilerConfiguration
|
||||||
|
import org.jetbrains.kotlin.js.translate.extensions.JsSyntheticTranslateExtension
|
||||||
import org.jetbrains.kotlin.resolve.extensions.SyntheticResolveExtension
|
import org.jetbrains.kotlin.resolve.extensions.SyntheticResolveExtension
|
||||||
|
|
||||||
class SerializationComponentRegistrar : ComponentRegistrar {
|
class SerializationComponentRegistrar : ComponentRegistrar {
|
||||||
override fun registerProjectComponents(project: MockProject, configuration: CompilerConfiguration) {
|
override fun registerProjectComponents(project: MockProject, configuration: CompilerConfiguration) {
|
||||||
ExpressionCodegenExtension.registerExtension(project, SerializationCodegenExtension())
|
ExpressionCodegenExtension.registerExtension(project, SerializationCodegenExtension())
|
||||||
SyntheticResolveExtension.registerExtension(project, SerializationResolveExtension())
|
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.*
|
import org.jetbrains.kotlin.types.*
|
||||||
|
|
||||||
internal val packageFqName = FqName("kotlinx.serialization")
|
internal val packageFqName = FqName("kotlinx.serialization")
|
||||||
|
internal val internalPackageFqName = FqName("kotlinx.serialization.internal")
|
||||||
|
|
||||||
// ---- kotlin.serialization.KSerializer
|
// ---- kotlin.serialization.KSerializer
|
||||||
|
|
||||||
@@ -58,12 +59,12 @@ internal val javaSerializableFqName = javaIOPackageFqName.child(javaSerializable
|
|||||||
fun isJavaSerializable(type: KotlinType?): Boolean =
|
fun isJavaSerializable(type: KotlinType?): Boolean =
|
||||||
type != null && KotlinBuiltIns.isConstructedFromGivenClass(type, javaSerializableFqName)
|
type != null && KotlinBuiltIns.isConstructedFromGivenClass(type, javaSerializableFqName)
|
||||||
|
|
||||||
fun ClassDescriptor.getJavaSerializableDescriptor(): ClassDescriptor =
|
fun ClassDescriptor.getJavaSerializableDescriptor(): ClassDescriptor? =
|
||||||
module.findClassAcrossModuleDependencies(ClassId(javaIOPackageFqName, javaSerializableName))!!
|
module.findClassAcrossModuleDependencies(ClassId(javaIOPackageFqName, javaSerializableName))
|
||||||
|
|
||||||
fun ClassDescriptor.getJavaSerializableType(): SimpleType {
|
// null on JS frontend
|
||||||
return KotlinTypeFactory.simpleNotNullType(Annotations.EMPTY, getJavaSerializableDescriptor(), emptyList())
|
fun ClassDescriptor.getJavaSerializableType(): SimpleType? =
|
||||||
}
|
getJavaSerializableDescriptor()?.let { KotlinTypeFactory.simpleNotNullType(Annotations.EMPTY, it, emptyList()) }
|
||||||
|
|
||||||
// ---- kotlin.serialization.Serializable(with=xxx)
|
// ---- kotlin.serialization.Serializable(with=xxx)
|
||||||
|
|
||||||
@@ -175,6 +176,9 @@ fun ClassDescriptor.getKSerializerConstructorMarker(): ClassDescriptor =
|
|||||||
module.findClassAcrossModuleDependencies(ClassId(packageFqName, kSerializerConstructorMarkerName))!!
|
module.findClassAcrossModuleDependencies(ClassId(packageFqName, kSerializerConstructorMarkerName))!!
|
||||||
|
|
||||||
fun ClassDescriptor.getClassFromSerializationPackage(classSimpleName: String) =
|
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)
|
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>) {
|
fun addSerializableSupertypes(classDescriptor: ClassDescriptor, supertypes: MutableList<KotlinType>) {
|
||||||
if (classDescriptor.isInternalSerializable && supertypes.none(::isJavaSerializable)) {
|
if (classDescriptor.isInternalSerializable && supertypes.none(::isJavaSerializable)) {
|
||||||
supertypes.add(classDescriptor.getJavaSerializableType())
|
classDescriptor.getJavaSerializableType()?.let { supertypes.add(it) }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user