[Serialization] Reorganize module structure
This commit is contained in:
@@ -0,0 +1,31 @@
|
||||
description = "Kotlin Serialization Compiler Plugin (Backend)"
|
||||
|
||||
plugins {
|
||||
kotlin("jvm")
|
||||
id("jps-compatible")
|
||||
}
|
||||
|
||||
dependencies {
|
||||
compileOnly(project(":compiler:backend"))
|
||||
compileOnly(project(":compiler:ir.backend.common"))
|
||||
compileOnly(project(":compiler:backend.jvm"))
|
||||
compileOnly(project(":compiler:ir.tree"))
|
||||
compileOnly(project(":js:js.frontend"))
|
||||
compileOnly(project(":js:js.translator"))
|
||||
compileOnly(project(":kotlin-util-klib-metadata"))
|
||||
compileOnly(project(":compiler:cli-common"))
|
||||
|
||||
implementation(project(":kotlinx-serialization-compiler-plugin.common"))
|
||||
implementation(project(":kotlinx-serialization-compiler-plugin.k1"))
|
||||
|
||||
compileOnly(intellijCore())
|
||||
}
|
||||
|
||||
sourceSets {
|
||||
"main" { projectDefault() }
|
||||
"test" { none() }
|
||||
}
|
||||
|
||||
runtimeJar()
|
||||
sourcesJar()
|
||||
javadocJar()
|
||||
+60
@@ -0,0 +1,60 @@
|
||||
/*
|
||||
* Copyright 2010-2021 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlinx.serialization.compiler.backend.common
|
||||
|
||||
import org.jetbrains.kotlin.descriptors.*
|
||||
import org.jetbrains.kotlin.incremental.components.NoLookupLocation
|
||||
import org.jetbrains.kotlin.name.Name
|
||||
import org.jetbrains.kotlin.resolve.BindingContext
|
||||
import org.jetbrains.kotlin.resolve.descriptorUtil.secondaryConstructors
|
||||
import org.jetbrains.kotlin.resolve.isInlineClass
|
||||
import org.jetbrains.kotlin.types.typeUtil.isUnit
|
||||
import org.jetbrains.kotlinx.serialization.compiler.resolve.*
|
||||
|
||||
abstract class SerializableCodegen(
|
||||
protected val serializableDescriptor: ClassDescriptor,
|
||||
bindingContext: BindingContext
|
||||
) : AbstractSerialGenerator(bindingContext, serializableDescriptor) {
|
||||
protected val properties = bindingContext.serializablePropertiesFor(serializableDescriptor)
|
||||
|
||||
fun generate() {
|
||||
generateSyntheticInternalConstructor()
|
||||
generateSyntheticMethods()
|
||||
}
|
||||
|
||||
private inline fun ClassDescriptor.shouldHaveSpecificSyntheticMethods(functionPresenceChecker: () -> FunctionDescriptor?) =
|
||||
!isInlineClass() && (isAbstractOrSealedSerializableClass() || functionPresenceChecker() != null)
|
||||
|
||||
private fun generateSyntheticInternalConstructor() {
|
||||
val serializerDescriptor = serializableDescriptor.classSerializer ?: return
|
||||
if (serializableDescriptor.shouldHaveSpecificSyntheticMethods { SerializationDescriptorUtils.getSyntheticLoadMember(serializerDescriptor) }) {
|
||||
val constrDesc = serializableDescriptor.secondaryConstructors.find(ClassConstructorDescriptor::isSerializationCtor) ?: return
|
||||
generateInternalConstructor(constrDesc)
|
||||
}
|
||||
}
|
||||
|
||||
private fun generateSyntheticMethods() {
|
||||
val serializerDescriptor = serializableDescriptor.classSerializer ?: return
|
||||
if (serializableDescriptor.shouldHaveSpecificSyntheticMethods { SerializationDescriptorUtils.getSyntheticSaveMember(serializerDescriptor) }) {
|
||||
val func =
|
||||
serializableDescriptor.unsubstitutedMemberScope.getContributedFunctions(
|
||||
Name.identifier(SerialEntityNames.WRITE_SELF_NAME.toString()),
|
||||
NoLookupLocation.FROM_BACKEND
|
||||
).singleOrNull { function ->
|
||||
function.kind == CallableMemberDescriptor.Kind.SYNTHESIZED &&
|
||||
function.modality == Modality.FINAL &&
|
||||
function.returnType?.isUnit() ?: false
|
||||
} ?: return
|
||||
generateWriteSelfMethod(func)
|
||||
}
|
||||
}
|
||||
|
||||
protected abstract fun generateInternalConstructor(constructorDescriptor: ClassConstructorDescriptor)
|
||||
|
||||
protected open fun generateWriteSelfMethod(methodDescriptor: FunctionDescriptor) {
|
||||
|
||||
}
|
||||
}
|
||||
+55
@@ -0,0 +1,55 @@
|
||||
/*
|
||||
* Copyright 2010-2021 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlinx.serialization.compiler.backend.common
|
||||
|
||||
import org.jetbrains.kotlin.descriptors.CallableMemberDescriptor
|
||||
import org.jetbrains.kotlin.descriptors.ClassDescriptor
|
||||
import org.jetbrains.kotlin.descriptors.FunctionDescriptor
|
||||
import org.jetbrains.kotlin.incremental.components.NoLookupLocation
|
||||
import org.jetbrains.kotlin.resolve.BindingContext
|
||||
import org.jetbrains.kotlinx.serialization.compiler.resolve.*
|
||||
import org.jetbrains.kotlinx.serialization.compiler.resolve.SerialEntityNames.SERIALIZER_PROVIDER_NAME
|
||||
|
||||
abstract class SerializableCompanionCodegen(
|
||||
protected val companionDescriptor: ClassDescriptor,
|
||||
bindingContext: BindingContext?
|
||||
) : AbstractSerialGenerator(bindingContext, companionDescriptor) {
|
||||
protected val serializableDescriptor: ClassDescriptor = getSerializableClassDescriptorByCompanion(companionDescriptor)!!
|
||||
|
||||
open fun getSerializerGetterDescriptor(): FunctionDescriptor {
|
||||
return companionDescriptor.unsubstitutedMemberScope.getContributedFunctions(
|
||||
SERIALIZER_PROVIDER_NAME,
|
||||
NoLookupLocation.FROM_BACKEND
|
||||
).firstOrNull {
|
||||
it.valueParameters.size == serializableDescriptor.declaredTypeParameters.size
|
||||
&& it.kind == CallableMemberDescriptor.Kind.SYNTHESIZED
|
||||
&& it.valueParameters.all { p -> isKSerializer(p.type) }
|
||||
&& it.returnType != null && isKSerializer(it.returnType)
|
||||
} ?: throw IllegalStateException(
|
||||
"Can't find synthesized 'Companion.serializer()' function to generate, " +
|
||||
"probably clash with user-defined function has occurred"
|
||||
)
|
||||
}
|
||||
|
||||
fun generate() {
|
||||
val serializerGetterDescriptor = getSerializerGetterDescriptor()
|
||||
|
||||
if (serializableDescriptor.isSerializableObject
|
||||
|| serializableDescriptor.isAbstractOrSealedSerializableClass()
|
||||
|| serializableDescriptor.isSerializableEnum()
|
||||
) {
|
||||
generateLazySerializerGetter(serializerGetterDescriptor)
|
||||
} else {
|
||||
generateSerializerGetter(serializerGetterDescriptor)
|
||||
}
|
||||
}
|
||||
|
||||
protected abstract fun generateSerializerGetter(methodDescriptor: FunctionDescriptor)
|
||||
|
||||
protected open fun generateLazySerializerGetter(methodDescriptor: FunctionDescriptor) {
|
||||
generateSerializerGetter(methodDescriptor)
|
||||
}
|
||||
}
|
||||
+148
@@ -0,0 +1,148 @@
|
||||
/*
|
||||
* Copyright 2010-2021 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlinx.serialization.compiler.backend.common
|
||||
|
||||
import org.jetbrains.kotlin.backend.common.CodegenUtil.getMemberToGenerate
|
||||
import org.jetbrains.kotlin.descriptors.*
|
||||
import org.jetbrains.kotlin.incremental.components.NoLookupLocation
|
||||
import org.jetbrains.kotlin.ir.declarations.IrProperty
|
||||
import org.jetbrains.kotlin.name.Name
|
||||
import org.jetbrains.kotlin.resolve.BindingContext
|
||||
import org.jetbrains.kotlinx.serialization.compiler.backend.common.SerializationDescriptorUtils.getSyntheticLoadMember
|
||||
import org.jetbrains.kotlinx.serialization.compiler.backend.common.SerializationDescriptorUtils.getSyntheticSaveMember
|
||||
import org.jetbrains.kotlinx.serialization.compiler.extensions.SerializationDescriptorSerializerPlugin
|
||||
import org.jetbrains.kotlinx.serialization.compiler.resolve.*
|
||||
|
||||
abstract class SerializerCodegen(
|
||||
protected val serializerDescriptor: ClassDescriptor,
|
||||
bindingContext: BindingContext,
|
||||
metadataPlugin: SerializationDescriptorSerializerPlugin?
|
||||
) : AbstractSerialGenerator(bindingContext, serializerDescriptor) {
|
||||
val serializableDescriptor: ClassDescriptor = getSerializableClassDescriptorBySerializer(serializerDescriptor)!!
|
||||
protected val serialName: String = serializableDescriptor.serialName()
|
||||
protected val properties = bindingContext.serializablePropertiesFor(serializableDescriptor, metadataPlugin)
|
||||
protected val serializableProperties = properties.serializableProperties
|
||||
|
||||
private fun checkSerializability() {
|
||||
check(properties.isExternallySerializable) {
|
||||
"Class ${serializableDescriptor.name} have constructor parameters which are not properties and therefore it is not serializable automatically"
|
||||
}
|
||||
}
|
||||
|
||||
fun generate() {
|
||||
val prop = generateSerializableClassPropertyIfNeeded()
|
||||
if (prop)
|
||||
generateSerialDesc()
|
||||
val save = generateSaveIfNeeded()
|
||||
val load = generateLoadIfNeeded()
|
||||
generateMembersFromGeneratedSerializer()
|
||||
if (!prop && (save || load))
|
||||
generateSerialDesc()
|
||||
if (serializableDescriptor.declaredTypeParameters.isNotEmpty()) {
|
||||
findSerializerConstructorForTypeArgumentsSerializers(serializerDescriptor, onlyIfSynthetic = true)?.let {
|
||||
generateGenericFieldsAndConstructor(it)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun generateMembersFromGeneratedSerializer() {
|
||||
getMemberToGenerate(
|
||||
serializerDescriptor, SerialEntityNames.CHILD_SERIALIZERS_GETTER.identifier,
|
||||
{ true }, { it.isEmpty() }
|
||||
)?.let { generateChildSerializersGetter(it) }
|
||||
getMemberToGenerate(
|
||||
serializerDescriptor, SerialEntityNames.TYPE_PARAMS_SERIALIZERS_GETTER.identifier,
|
||||
{ true }, { it.isEmpty() }
|
||||
)?.takeIf { it.kind != CallableMemberDescriptor.Kind.FAKE_OVERRIDE }?.let { generateTypeParamsSerializersGetter(it) }
|
||||
}
|
||||
|
||||
protected abstract fun generateTypeParamsSerializersGetter(function: FunctionDescriptor)
|
||||
|
||||
protected abstract fun generateChildSerializersGetter(function: FunctionDescriptor)
|
||||
|
||||
protected val generatedSerialDescPropertyDescriptor = getPropertyToGenerate(
|
||||
serializerDescriptor, SerialEntityNames.SERIAL_DESC_FIELD,
|
||||
serializerDescriptor::checkSerializableClassPropertyResult
|
||||
)
|
||||
protected val anySerialDescProperty = getProperty(
|
||||
serializerDescriptor, SerialEntityNames.SERIAL_DESC_FIELD,
|
||||
serializerDescriptor::checkSerializableClassPropertyResult
|
||||
) { true }
|
||||
|
||||
var localSerializersFieldsDescriptors: List<Pair<PropertyDescriptor, IrProperty>> = emptyList()
|
||||
protected set
|
||||
|
||||
// Can be false if user specified inheritance from KSerializer explicitly
|
||||
protected val isGeneratedSerializer = serializerDescriptor.typeConstructor.supertypes.any(::isGeneratedKSerializer)
|
||||
|
||||
protected fun findLocalSerializersFieldDescriptors(): List<PropertyDescriptor> {
|
||||
val count = serializableDescriptor.declaredTypeParameters.size
|
||||
if (count == 0) return emptyList()
|
||||
val propNames = (0 until count).map { "${SerialEntityNames.typeArgPrefix}$it" }
|
||||
return propNames.mapNotNull { name ->
|
||||
getPropertyToGenerate(serializerDescriptor, name) { isKSerializer(it.returnType) }
|
||||
}
|
||||
}
|
||||
|
||||
protected abstract fun generateSerialDesc()
|
||||
|
||||
protected abstract fun generateGenericFieldsAndConstructor(typedConstructorDescriptor: ClassConstructorDescriptor)
|
||||
|
||||
protected abstract fun generateSerializableClassProperty(property: PropertyDescriptor)
|
||||
|
||||
protected abstract fun generateSave(function: FunctionDescriptor)
|
||||
|
||||
protected abstract fun generateLoad(function: FunctionDescriptor)
|
||||
|
||||
private fun generateSerializableClassPropertyIfNeeded(): Boolean {
|
||||
val property = generatedSerialDescPropertyDescriptor
|
||||
?: return false
|
||||
checkSerializability()
|
||||
generateSerializableClassProperty(property)
|
||||
return true
|
||||
}
|
||||
|
||||
private fun generateSaveIfNeeded(): Boolean {
|
||||
val function = getSyntheticSaveMember(serializerDescriptor) ?: return false
|
||||
checkSerializability()
|
||||
generateSave(function)
|
||||
return true
|
||||
}
|
||||
|
||||
private fun generateLoadIfNeeded(): Boolean {
|
||||
val function = getSyntheticLoadMember(serializerDescriptor) ?: return false
|
||||
checkSerializability()
|
||||
generateLoad(function)
|
||||
return true
|
||||
}
|
||||
|
||||
private fun getPropertyToGenerate(
|
||||
classDescriptor: ClassDescriptor,
|
||||
name: String,
|
||||
isReturnTypeOk: (PropertyDescriptor) -> Boolean
|
||||
): PropertyDescriptor? = getProperty(
|
||||
classDescriptor,
|
||||
name,
|
||||
isReturnTypeOk
|
||||
) { kind ->
|
||||
kind == CallableMemberDescriptor.Kind.SYNTHESIZED || kind == CallableMemberDescriptor.Kind.FAKE_OVERRIDE
|
||||
}
|
||||
|
||||
private fun getProperty(
|
||||
classDescriptor: ClassDescriptor,
|
||||
name: String,
|
||||
isReturnTypeOk: (PropertyDescriptor) -> Boolean,
|
||||
isKindOk: (CallableMemberDescriptor.Kind) -> Boolean
|
||||
): PropertyDescriptor? = classDescriptor.unsubstitutedMemberScope.getContributedVariables(
|
||||
Name.identifier(name),
|
||||
NoLookupLocation.FROM_BACKEND
|
||||
)
|
||||
.singleOrNull { property ->
|
||||
isKindOk(property.kind) &&
|
||||
property.returnType != null &&
|
||||
isReturnTypeOk(property)
|
||||
}
|
||||
}
|
||||
+584
@@ -0,0 +1,584 @@
|
||||
/*
|
||||
* Copyright 2010-2022 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlinx.serialization.compiler.backend.ir
|
||||
|
||||
import org.jetbrains.kotlin.backend.common.lower.irIfThen
|
||||
import org.jetbrains.kotlin.backend.jvm.functionByName
|
||||
import org.jetbrains.kotlin.backend.jvm.ir.fileParent
|
||||
import org.jetbrains.kotlin.backend.jvm.ir.representativeUpperBound
|
||||
import org.jetbrains.kotlin.descriptors.ClassKind
|
||||
import org.jetbrains.kotlin.descriptors.Modality
|
||||
import org.jetbrains.kotlin.ir.builders.*
|
||||
import org.jetbrains.kotlin.ir.declarations.IrClass
|
||||
import org.jetbrains.kotlin.ir.declarations.IrFunction
|
||||
import org.jetbrains.kotlin.ir.declarations.IrValueDeclaration
|
||||
import org.jetbrains.kotlin.ir.declarations.IrValueParameter
|
||||
import org.jetbrains.kotlin.ir.deepCopyWithVariables
|
||||
import org.jetbrains.kotlin.ir.expressions.IrClassReference
|
||||
import org.jetbrains.kotlin.ir.expressions.IrExpression
|
||||
import org.jetbrains.kotlin.ir.expressions.IrExpressionBody
|
||||
import org.jetbrains.kotlin.ir.expressions.IrVararg
|
||||
import org.jetbrains.kotlin.ir.symbols.IrClassSymbol
|
||||
import org.jetbrains.kotlin.ir.symbols.IrPropertySymbol
|
||||
import org.jetbrains.kotlin.ir.symbols.IrSimpleFunctionSymbol
|
||||
import org.jetbrains.kotlin.ir.symbols.IrTypeParameterSymbol
|
||||
import org.jetbrains.kotlin.ir.types.*
|
||||
import org.jetbrains.kotlin.ir.types.impl.IrSimpleTypeImpl
|
||||
import org.jetbrains.kotlin.ir.types.impl.makeTypeProjection
|
||||
import org.jetbrains.kotlin.ir.util.*
|
||||
import org.jetbrains.kotlin.name.CallableId
|
||||
import org.jetbrains.kotlin.name.ClassId
|
||||
import org.jetbrains.kotlin.name.FqName
|
||||
import org.jetbrains.kotlin.types.Variance
|
||||
import org.jetbrains.kotlin.util.OperatorNameConventions
|
||||
import org.jetbrains.kotlinx.serialization.compiler.backend.jvm.*
|
||||
import org.jetbrains.kotlinx.serialization.compiler.extensions.SerializationPluginContext
|
||||
import org.jetbrains.kotlinx.serialization.compiler.resolve.*
|
||||
|
||||
abstract class BaseIrGenerator(private val currentClass: IrClass, final override val compilerContext: SerializationPluginContext) :
|
||||
IrBuilderWithPluginContext {
|
||||
|
||||
private val throwMissedFieldExceptionFunc = compilerContext.referenceFunctions(
|
||||
CallableId(
|
||||
SerializationPackages.internalPackageFqName,
|
||||
SerialEntityNames.SINGLE_MASK_FIELD_MISSING_FUNC_NAME
|
||||
)
|
||||
).singleOrNull()
|
||||
|
||||
private val throwMissedFieldExceptionArrayFunc = compilerContext.referenceFunctions(
|
||||
CallableId(
|
||||
SerializationPackages.internalPackageFqName,
|
||||
SerialEntityNames.ARRAY_MASK_FIELD_MISSING_FUNC_NAME
|
||||
)
|
||||
).singleOrNull()
|
||||
|
||||
private val enumSerializerFactoryFunc = compilerContext.enumSerializerFactoryFunc
|
||||
|
||||
private val markedEnumSerializerFactoryFunc = compilerContext.markedEnumSerializerFactoryFunc
|
||||
|
||||
fun useFieldMissingOptimization(): Boolean {
|
||||
return throwMissedFieldExceptionFunc != null && throwMissedFieldExceptionArrayFunc != null
|
||||
}
|
||||
|
||||
private fun getClassListFromFileAnnotation(annotationFqName: FqName): List<IrClassSymbol> {
|
||||
val annotation = currentClass.fileParent.annotations.findAnnotation(annotationFqName) ?: return emptyList()
|
||||
val vararg = annotation.getValueArgument(0) as? IrVararg ?: return emptyList()
|
||||
return vararg.elements
|
||||
.mapNotNull { (it as? IrClassReference)?.symbol as? IrClassSymbol }
|
||||
}
|
||||
|
||||
val contextualKClassListInCurrentFile: Set<IrClassSymbol> by lazy {
|
||||
getClassListFromFileAnnotation(
|
||||
SerializationAnnotations.contextualFqName,
|
||||
).plus(
|
||||
getClassListFromFileAnnotation(
|
||||
SerializationAnnotations.contextualOnFileFqName,
|
||||
)
|
||||
).toSet()
|
||||
}
|
||||
|
||||
val additionalSerializersInScopeOfCurrentFile: Map<Pair<IrClassSymbol, Boolean>, IrClassSymbol> by lazy {
|
||||
getClassListFromFileAnnotation(SerializationAnnotations.additionalSerializersFqName,)
|
||||
.associateBy(
|
||||
{ serializerSymbol ->
|
||||
val kotlinType = (serializerSymbol.owner.superTypes.find(IrType::isKSerializer) as? IrSimpleType)?.arguments?.firstOrNull()?.typeOrNull
|
||||
val classSymbol = kotlinType?.classOrNull
|
||||
?: throw AssertionError("Argument for ${SerializationAnnotations.additionalSerializersFqName} does not implement KSerializer or does not provide serializer for concrete type")
|
||||
classSymbol to kotlinType.isNullable()
|
||||
},
|
||||
{ it }
|
||||
)
|
||||
}
|
||||
|
||||
fun IrBlockBodyBuilder.generateGoldenMaskCheck(
|
||||
seenVars: List<IrValueDeclaration>,
|
||||
properties: IrSerializableProperties,
|
||||
serialDescriptor: IrExpression
|
||||
) {
|
||||
val fieldsMissedTest: IrExpression
|
||||
val throwErrorExpr: IrExpression
|
||||
|
||||
val maskSlotCount = seenVars.size
|
||||
if (maskSlotCount == 1) {
|
||||
val goldenMask = properties.goldenMask
|
||||
|
||||
|
||||
throwErrorExpr = irInvoke(
|
||||
null,
|
||||
throwMissedFieldExceptionFunc!!,
|
||||
irGet(seenVars[0]),
|
||||
irInt(goldenMask),
|
||||
serialDescriptor,
|
||||
typeHint = compilerContext.irBuiltIns.unitType
|
||||
)
|
||||
|
||||
fieldsMissedTest = irNotEquals(
|
||||
irInt(goldenMask),
|
||||
irBinOp(
|
||||
OperatorNameConventions.AND,
|
||||
irInt(goldenMask),
|
||||
irGet(seenVars[0])
|
||||
)
|
||||
)
|
||||
} else {
|
||||
val goldenMaskList = properties.goldenMaskList
|
||||
|
||||
var compositeExpression: IrExpression? = null
|
||||
for (i in goldenMaskList.indices) {
|
||||
val singleCheckExpr = irNotEquals(
|
||||
irInt(goldenMaskList[i]),
|
||||
irBinOp(
|
||||
OperatorNameConventions.AND,
|
||||
irInt(goldenMaskList[i]),
|
||||
irGet(seenVars[i])
|
||||
)
|
||||
)
|
||||
|
||||
compositeExpression = if (compositeExpression == null) {
|
||||
singleCheckExpr
|
||||
} else {
|
||||
irBinOp(
|
||||
OperatorNameConventions.OR,
|
||||
compositeExpression,
|
||||
singleCheckExpr
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
fieldsMissedTest = compositeExpression!!
|
||||
|
||||
throwErrorExpr = irBlock {
|
||||
+irInvoke(
|
||||
null,
|
||||
throwMissedFieldExceptionArrayFunc!!,
|
||||
createPrimitiveArrayOfExpression(compilerContext.irBuiltIns.intType, goldenMaskList.indices.map { irGet(seenVars[it]) }),
|
||||
createPrimitiveArrayOfExpression(compilerContext.irBuiltIns.intType, goldenMaskList.map { irInt(it) }),
|
||||
serialDescriptor,
|
||||
typeHint = compilerContext.irBuiltIns.unitType
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
+irIfThen(compilerContext.irBuiltIns.unitType, fieldsMissedTest, throwErrorExpr)
|
||||
}
|
||||
|
||||
fun IrBlockBodyBuilder.serializeAllProperties(
|
||||
serializableProperties: List<IrSerializableProperty>,
|
||||
objectToSerialize: IrValueDeclaration,
|
||||
localOutput: IrValueDeclaration,
|
||||
localSerialDesc: IrValueDeclaration,
|
||||
kOutputClass: IrClassSymbol,
|
||||
ignoreIndexTo: Int,
|
||||
initializerAdapter: (IrExpressionBody) -> IrExpression,
|
||||
genericGetter: ((Int, IrType) -> IrExpression)?
|
||||
) {
|
||||
|
||||
fun IrSerializableProperty.irGet(): IrExpression {
|
||||
val ownerType = objectToSerialize.symbol.owner.type
|
||||
return getProperty(
|
||||
irGet(
|
||||
type = ownerType,
|
||||
variable = objectToSerialize.symbol
|
||||
), ir
|
||||
)
|
||||
}
|
||||
|
||||
for ((index, property) in serializableProperties.withIndex()) {
|
||||
if (index < ignoreIndexTo) continue
|
||||
// output.writeXxxElementValue(classDesc, index, value)
|
||||
val elementCall = formEncodeDecodePropertyCall(
|
||||
irGet(localOutput),
|
||||
property, { innerSerial, sti ->
|
||||
val f =
|
||||
kOutputClass.functionByName("${CallingConventions.encode}${sti.elementMethodPrefix}Serializable${CallingConventions.elementPostfix}")
|
||||
f to listOf(
|
||||
irGet(localSerialDesc),
|
||||
irInt(index),
|
||||
innerSerial,
|
||||
property.irGet()
|
||||
)
|
||||
}, {
|
||||
val f =
|
||||
kOutputClass.functionByName("${CallingConventions.encode}${it.elementMethodPrefix}${CallingConventions.elementPostfix}")
|
||||
val args: MutableList<IrExpression> = mutableListOf(irGet(localSerialDesc), irInt(index))
|
||||
if (it.elementMethodPrefix != "Unit") args.add(property.irGet())
|
||||
f to args
|
||||
},
|
||||
genericGetter
|
||||
)
|
||||
|
||||
// check for call to .shouldEncodeElementDefault
|
||||
val encodeDefaults = property.ir.getEncodeDefaultAnnotationValue()
|
||||
val field =
|
||||
property.ir.backingField // Nullable when property from another module; can't compare it with default value on JS or Native
|
||||
if (!property.optional || encodeDefaults == true || field == null) {
|
||||
// emit call right away
|
||||
+elementCall
|
||||
} else {
|
||||
val partB = irNotEquals(property.irGet(), initializerAdapter(field.initializer!!))
|
||||
|
||||
val condition = if (encodeDefaults == false) {
|
||||
// drop default without call to .shouldEncodeElementDefault
|
||||
partB
|
||||
} else {
|
||||
// emit check:
|
||||
// if (if (output.shouldEncodeElementDefault(this.descriptor, i)) true else {obj.prop != DEFAULT_VALUE} ) {
|
||||
// output.encodeIntElement(this.descriptor, i, obj.prop)// block {obj.prop != DEFAULT_VALUE} may contain several statements
|
||||
val shouldEncodeFunc = kOutputClass.functionByName(CallingConventions.shouldEncodeDefault)
|
||||
val partA = irInvoke(irGet(localOutput), shouldEncodeFunc, irGet(localSerialDesc), irInt(index))
|
||||
// Ir infrastructure does not have dedicated symbol for ||, so
|
||||
// `a || b == if (a) true else b`, see org.jetbrains.kotlin.ir.builders.PrimitivesKt.oror
|
||||
irIfThenElse(compilerContext.irBuiltIns.booleanType, partA, irTrue(), partB)
|
||||
}
|
||||
+irIfThen(condition, elementCall)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
fun IrBlockBodyBuilder.formEncodeDecodePropertyCall(
|
||||
encoder: IrExpression,
|
||||
property: IrSerializableProperty,
|
||||
whenHaveSerializer: (serializer: IrExpression, sti: IrSerialTypeInfo) -> FunctionWithArgs,
|
||||
whenDoNot: (sti: IrSerialTypeInfo) -> FunctionWithArgs,
|
||||
genericGetter: ((Int, IrType) -> IrExpression)? = null,
|
||||
returnTypeHint: IrType? = null
|
||||
): IrExpression {
|
||||
val sti = getIrSerialTypeInfo(property, compilerContext)
|
||||
val innerSerial = serializerInstance(
|
||||
sti.serializer,
|
||||
compilerContext,
|
||||
property.type,
|
||||
property.genericIndex,
|
||||
genericGetter
|
||||
)
|
||||
val (functionToCall, args: List<IrExpression>) = if (innerSerial != null) whenHaveSerializer(innerSerial, sti) else whenDoNot(sti)
|
||||
val typeArgs = if (functionToCall.owner.typeParameters.isNotEmpty()) listOf(property.type) else listOf()
|
||||
return irInvoke(encoder, functionToCall, typeArguments = typeArgs, valueArguments = args, returnTypeHint = returnTypeHint)
|
||||
}
|
||||
|
||||
fun IrBuilderWithScope.callSerializerFromCompanion(
|
||||
thisIrType: IrType,
|
||||
typeArgs: List<IrType>,
|
||||
args: List<IrExpression>
|
||||
): IrExpression? {
|
||||
val baseClass = thisIrType.getClass() ?: return null
|
||||
val companionClass = baseClass.companionObject() ?: return null
|
||||
val serializerProviderFunction = companionClass.declarations.singleOrNull {
|
||||
it is IrFunction && it.name == SerialEntityNames.SERIALIZER_PROVIDER_NAME && it.valueParameters.size == baseClass.typeParameters.size
|
||||
} ?: return null
|
||||
|
||||
val adjustedArgs: List<IrExpression> =
|
||||
// if typeArgs.size == args.size then the serializer is custom - we need to use the actual serializers from the arguments
|
||||
if ((typeArgs.size != args.size) && (baseClass.modality == Modality.SEALED || baseClass.modality == Modality.ABSTRACT)) {
|
||||
val serializer = findStandardKotlinTypeSerializer(compilerContext, context.irBuiltIns.unitType)!!
|
||||
// workaround for sealed and abstract classes - the `serializer` function expects non-null serializers, but does not use them, so serializers of any type can be passed
|
||||
List(baseClass.typeParameters.size) { irGetObject(serializer) }
|
||||
} else {
|
||||
args
|
||||
}
|
||||
|
||||
with(serializerProviderFunction as IrFunction) {
|
||||
// Note that [typeArgs] may be unused if we short-cut to e.g. SealedClassSerializer
|
||||
return irInvoke(
|
||||
irGetObject(companionClass),
|
||||
symbol,
|
||||
typeArgs.takeIf { it.size == typeParameters.size }.orEmpty(),
|
||||
adjustedArgs.takeIf { it.size == valueParameters.size }.orEmpty()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
// Does not use sti and therefore does not perform encoder calls optimization
|
||||
fun IrBuilderWithScope.serializerTower(
|
||||
generator: SerializerIrGenerator,
|
||||
dispatchReceiverParameter: IrValueParameter,
|
||||
property: IrSerializableProperty
|
||||
): IrExpression? {
|
||||
val nullableSerClass = compilerContext.referenceProperties(SerialEntityNames.wrapIntoNullableCallableId).single()
|
||||
val serializer =
|
||||
property.serializableWith(compilerContext)
|
||||
?: if (!property.type.isTypeParameter()) generator.findTypeSerializerOrContext(
|
||||
compilerContext,
|
||||
property.type
|
||||
) else null
|
||||
return serializerInstance(
|
||||
serializer,
|
||||
compilerContext,
|
||||
property.type,
|
||||
genericIndex = property.genericIndex
|
||||
) { it, _ ->
|
||||
val ir = generator.localSerializersFieldsDescriptors[it]
|
||||
irGetField(irGet(dispatchReceiverParameter), ir.backingField!!)
|
||||
}?.let { expr -> wrapWithNullableSerializerIfNeeded(property.type, expr, nullableSerClass) }
|
||||
}
|
||||
|
||||
private fun IrBuilderWithScope.wrapWithNullableSerializerIfNeeded(
|
||||
type: IrType,
|
||||
expression: IrExpression,
|
||||
nullableProp: IrPropertySymbol
|
||||
): IrExpression = if (type.isMarkedNullable()) {
|
||||
val resultType = type.makeNotNull()
|
||||
val typeArguments = listOf(resultType)
|
||||
val callee = nullableProp.owner.getter!!
|
||||
|
||||
val returnType = callee.returnType.substitute(callee.typeParameters, typeArguments)
|
||||
|
||||
irInvoke(
|
||||
callee = callee.symbol,
|
||||
typeArguments = typeArguments,
|
||||
valueArguments = emptyList(),
|
||||
returnTypeHint = returnType
|
||||
).apply { extensionReceiver = expression }
|
||||
} else {
|
||||
expression
|
||||
}
|
||||
|
||||
fun wrapIrTypeIntoKSerializerIrType(
|
||||
type: IrType,
|
||||
variance: Variance = Variance.INVARIANT
|
||||
): IrType {
|
||||
val kSerClass = compilerContext.referenceClass(ClassId(SerializationPackages.packageFqName, SerialEntityNames.KSERIALIZER_NAME))
|
||||
?: error("Couldn't find class ${SerialEntityNames.KSERIALIZER_NAME}")
|
||||
return IrSimpleTypeImpl(
|
||||
kSerClass, hasQuestionMark = false, arguments = listOf(
|
||||
makeTypeProjection(type, variance)
|
||||
), annotations = emptyList()
|
||||
)
|
||||
}
|
||||
|
||||
fun IrBuilderWithScope.serializerInstance(
|
||||
serializerClassOriginal: IrClassSymbol?,
|
||||
pluginContext: SerializationPluginContext,
|
||||
kType: IrType,
|
||||
genericIndex: Int? = null,
|
||||
genericGetter: ((Int, IrType) -> IrExpression)? = null
|
||||
): IrExpression? {
|
||||
val nullableSerClass = compilerContext.referenceProperties(SerialEntityNames.wrapIntoNullableCallableId).single()
|
||||
if (serializerClassOriginal == null) {
|
||||
if (genericIndex == null) return null
|
||||
return genericGetter?.invoke(genericIndex, kType)
|
||||
}
|
||||
if (serializerClassOriginal.owner.kind == ClassKind.OBJECT) {
|
||||
return irGetObject(serializerClassOriginal)
|
||||
}
|
||||
fun instantiate(serializer: IrClassSymbol?, type: IrType): IrExpression? {
|
||||
val expr = serializerInstance(
|
||||
serializer,
|
||||
pluginContext,
|
||||
type,
|
||||
type.genericIndex,
|
||||
genericGetter
|
||||
) ?: return null
|
||||
return wrapWithNullableSerializerIfNeeded(type, expr, nullableSerClass)
|
||||
}
|
||||
|
||||
var serializerClass = serializerClassOriginal
|
||||
var args: List<IrExpression>
|
||||
var typeArgs: List<IrType>
|
||||
val thisIrType = (kType as? IrSimpleType) ?: error("Don't know how to work with type ${kType::class}")
|
||||
var needToCopyAnnotations = false
|
||||
|
||||
when (serializerClassOriginal.owner.classId) {
|
||||
polymorphicSerializerId -> {
|
||||
needToCopyAnnotations = true
|
||||
args = listOf(classReference(kType.classOrUpperBound()!!))
|
||||
typeArgs = listOf(thisIrType)
|
||||
}
|
||||
contextSerializerId -> {
|
||||
args = listOf(classReference(kType.classOrUpperBound()!!))
|
||||
typeArgs = listOf(thisIrType)
|
||||
|
||||
val hasNewCtxSerCtor = compilerContext.referenceConstructors(contextSerializerId).any { it.owner.valueParameters.size == 3 }
|
||||
|
||||
if (hasNewCtxSerCtor) {
|
||||
// new signature of context serializer
|
||||
args = args + mutableListOf<IrExpression>().apply {
|
||||
val fallbackDefaultSerializer = findTypeSerializer(pluginContext, kType)
|
||||
add(instantiate(fallbackDefaultSerializer, kType) ?: irNull())
|
||||
add(
|
||||
createArrayOfExpression(
|
||||
wrapIrTypeIntoKSerializerIrType(
|
||||
thisIrType,
|
||||
variance = Variance.OUT_VARIANCE
|
||||
),
|
||||
thisIrType.arguments.map {
|
||||
val argSer = findTypeSerializerOrContext(
|
||||
compilerContext,
|
||||
it.typeOrNull!! //todo: handle star projections here?
|
||||
)
|
||||
instantiate(argSer, it.typeOrNull!!)!!
|
||||
})
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
objectSerializerId -> {
|
||||
needToCopyAnnotations = true
|
||||
args = listOf(irString(kType.serialName()), irGetObject(kType.classOrUpperBound()!!))
|
||||
typeArgs = listOf(thisIrType)
|
||||
}
|
||||
sealedSerializerId -> {
|
||||
needToCopyAnnotations = true
|
||||
args = mutableListOf<IrExpression>().apply {
|
||||
add(irString(kType.serialName()))
|
||||
add(classReference(kType.classOrUpperBound()!!))
|
||||
val (subclasses, subSerializers) = allSealedSerializableSubclassesFor(
|
||||
kType.classOrUpperBound()!!.owner,
|
||||
pluginContext
|
||||
)
|
||||
val projectedOutCurrentKClass =
|
||||
compilerContext.irBuiltIns.kClassClass.typeWithArguments(
|
||||
listOf(makeTypeProjection(thisIrType, Variance.OUT_VARIANCE))
|
||||
)
|
||||
add(
|
||||
createArrayOfExpression(
|
||||
projectedOutCurrentKClass,
|
||||
subclasses.map { classReference(it.classOrUpperBound()!!) }
|
||||
)
|
||||
)
|
||||
add(
|
||||
createArrayOfExpression(
|
||||
wrapIrTypeIntoKSerializerIrType(thisIrType, variance = Variance.OUT_VARIANCE),
|
||||
subSerializers.mapIndexed { i, serializer ->
|
||||
val type = subclasses[i]
|
||||
val expr = serializerInstance(
|
||||
serializer,
|
||||
pluginContext,
|
||||
type,
|
||||
type.genericIndex
|
||||
) { _, genericType ->
|
||||
serializerInstance(
|
||||
pluginContext.referenceClass(polymorphicSerializerId),
|
||||
pluginContext,
|
||||
(genericType.classifierOrNull as IrTypeParameterSymbol).owner.representativeUpperBound
|
||||
)!!
|
||||
}!!
|
||||
wrapWithNullableSerializerIfNeeded(type, expr, nullableSerClass)
|
||||
}
|
||||
)
|
||||
)
|
||||
}
|
||||
typeArgs = listOf(thisIrType)
|
||||
}
|
||||
enumSerializerId -> {
|
||||
serializerClass = pluginContext.referenceClass(enumSerializerId)
|
||||
val enumDescriptor = kType.classOrNull!!
|
||||
typeArgs = listOf(thisIrType)
|
||||
// instantiate serializer only inside enum Companion
|
||||
if (this@BaseIrGenerator !is SerializableCompanionIrGenerator) {
|
||||
// otherwise call Companion.serializer()
|
||||
callSerializerFromCompanion(thisIrType, typeArgs, emptyList())?.let { return it }
|
||||
}
|
||||
|
||||
val enumArgs = mutableListOf(
|
||||
irString(thisIrType.serialName()),
|
||||
irCall(enumDescriptor.owner.findEnumValuesMethod()),
|
||||
)
|
||||
|
||||
val enumSerializerFactoryFunc = enumSerializerFactoryFunc
|
||||
val markedEnumSerializerFactoryFunc = markedEnumSerializerFactoryFunc
|
||||
if (enumSerializerFactoryFunc != null && markedEnumSerializerFactoryFunc != null) {
|
||||
// runtime contains enum serializer factory functions
|
||||
val factoryFunc: IrSimpleFunctionSymbol = if (enumDescriptor.owner.isEnumWithSerialInfoAnnotation()) {
|
||||
// need to store SerialInfo annotation in descriptor
|
||||
val enumEntries = enumDescriptor.owner.enumEntries()
|
||||
val entriesNames = enumEntries.map { it.annotations.serialNameValue?.let { n -> irString(n) } ?: irNull() }
|
||||
val entriesAnnotations = enumEntries.map {
|
||||
val annotationConstructors = it.annotations.map { a ->
|
||||
a.deepCopyWithVariables()
|
||||
}
|
||||
val annotationsConstructors = copyAnnotationsFrom(annotationConstructors)
|
||||
if (annotationsConstructors.isEmpty()) {
|
||||
irNull()
|
||||
} else {
|
||||
createArrayOfExpression(compilerContext.irBuiltIns.annotationType, annotationsConstructors)
|
||||
}
|
||||
}
|
||||
val annotationArrayType =
|
||||
compilerContext.irBuiltIns.arrayClass.typeWith(compilerContext.irBuiltIns.annotationType.makeNullable())
|
||||
|
||||
enumArgs += createArrayOfExpression(compilerContext.irBuiltIns.stringType.makeNullable(), entriesNames)
|
||||
enumArgs += createArrayOfExpression(annotationArrayType, entriesAnnotations)
|
||||
|
||||
markedEnumSerializerFactoryFunc
|
||||
} else {
|
||||
enumSerializerFactoryFunc
|
||||
}
|
||||
|
||||
val factoryReturnType = factoryFunc.owner.returnType.substitute(factoryFunc.owner.typeParameters, typeArgs)
|
||||
return irInvoke(null, factoryFunc, typeArgs, enumArgs, factoryReturnType)
|
||||
} else {
|
||||
// support legacy serializer instantiation by constructor for old runtimes
|
||||
args = enumArgs
|
||||
}
|
||||
}
|
||||
else -> {
|
||||
args = kType.arguments.map {
|
||||
val argSer = findTypeSerializerOrContext(
|
||||
pluginContext,
|
||||
it.typeOrNull!! // todo: stars?
|
||||
)
|
||||
instantiate(argSer, it.typeOrNull!!) ?: return null
|
||||
}
|
||||
typeArgs = kType.arguments.map { it.typeOrNull!! }
|
||||
}
|
||||
|
||||
}
|
||||
if (serializerClassOriginal.owner.classId == referenceArraySerializerId) {
|
||||
args = listOf(wrapperClassReference(kType.arguments.single().typeOrNull!!)) + args
|
||||
typeArgs = listOf(typeArgs[0].makeNotNull()) + typeArgs
|
||||
}
|
||||
|
||||
// If KType is interface, .classSerializer always yields PolymorphicSerializer, which may be unavailable for interfaces from other modules
|
||||
if (!kType.isInterface() && serializerClassOriginal == kType.classOrUpperBound()?.owner.classSerializer(pluginContext) && this@BaseIrGenerator !is SerializableCompanionIrGenerator) {
|
||||
// This is default type serializer, we can shortcut through Companion.serializer()
|
||||
// BUT not during generation of this method itself
|
||||
callSerializerFromCompanion(thisIrType, typeArgs, args)?.let { return it }
|
||||
}
|
||||
|
||||
|
||||
val serializable = serializerClass?.owner?.let { compilerContext.getSerializableClassDescriptorBySerializer(it) }
|
||||
requireNotNull(serializerClass)
|
||||
val ctor = if (serializable?.typeParameters?.isNotEmpty() == true) {
|
||||
requireNotNull(
|
||||
findSerializerConstructorForTypeArgumentsSerializers(serializerClass.owner)
|
||||
) { "Generated serializer does not have constructor with required number of arguments" }
|
||||
} else {
|
||||
val constructors = serializerClass.constructors
|
||||
// search for new signature of polymorphic/sealed/contextual serializer
|
||||
if (!needToCopyAnnotations) {
|
||||
constructors.single { it.owner.isPrimary }
|
||||
} else {
|
||||
constructors.find { it.owner.lastArgumentIsAnnotationArray() } ?: run {
|
||||
// not found - we are using old serialization runtime without this feature
|
||||
// todo: optimize allocating an empty array when no annotations defined, maybe use old constructor?
|
||||
needToCopyAnnotations = false
|
||||
constructors.single { it.owner.isPrimary }
|
||||
}
|
||||
}
|
||||
}
|
||||
// Return type should be correctly substituted
|
||||
assert(ctor.isBound)
|
||||
val ctorDecl = ctor.owner
|
||||
if (needToCopyAnnotations) {
|
||||
val classAnnotations = copyAnnotationsFrom(thisIrType.getClass()?.let { collectSerialInfoAnnotations(it) }.orEmpty())
|
||||
args = args + createArrayOfExpression(compilerContext.irBuiltIns.annotationType, classAnnotations)
|
||||
}
|
||||
|
||||
val typeParameters = ctorDecl.parentAsClass.typeParameters
|
||||
val substitutedReturnType = ctorDecl.returnType.substitute(typeParameters, typeArgs)
|
||||
return irInvoke(
|
||||
null,
|
||||
ctor,
|
||||
// User may declare serializer with fixed type arguments, e.g. class SomeSerializer : KSerializer<ClosedRange<Float>>
|
||||
typeArguments = typeArgs.takeIf { it.size == ctorDecl.typeParameters.size }.orEmpty(),
|
||||
valueArguments = args.takeIf { it.size == ctorDecl.valueParameters.size }.orEmpty(),
|
||||
returnTypeHint = substitutedReturnType
|
||||
)
|
||||
}
|
||||
|
||||
}
|
||||
+128
@@ -0,0 +1,128 @@
|
||||
/*
|
||||
* Copyright 2010-2022 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlinx.serialization.compiler.backend.ir
|
||||
|
||||
import org.jetbrains.kotlin.descriptors.ValueParameterDescriptor
|
||||
import org.jetbrains.kotlin.ir.ObsoleteDescriptorBasedAPI
|
||||
import org.jetbrains.kotlin.ir.builders.IrBuilderWithScope
|
||||
import org.jetbrains.kotlin.ir.builders.irGet
|
||||
import org.jetbrains.kotlin.ir.builders.irGetField
|
||||
import org.jetbrains.kotlin.ir.declarations.IrClass
|
||||
import org.jetbrains.kotlin.ir.declarations.IrProperty
|
||||
import org.jetbrains.kotlin.ir.declarations.IrValueParameter
|
||||
import org.jetbrains.kotlin.ir.deepCopyWithVariables
|
||||
import org.jetbrains.kotlin.ir.expressions.IrExpression
|
||||
import org.jetbrains.kotlin.ir.expressions.IrExpressionBody
|
||||
import org.jetbrains.kotlin.ir.expressions.IrGetValue
|
||||
import org.jetbrains.kotlin.ir.symbols.IrValueSymbol
|
||||
import org.jetbrains.kotlin.ir.util.constructors
|
||||
import org.jetbrains.kotlin.ir.util.properties
|
||||
import org.jetbrains.kotlin.ir.visitors.IrElementTransformerVoid
|
||||
|
||||
fun IrBuilderWithScope.getProperty(receiver: IrExpression, property: IrProperty): IrExpression {
|
||||
return if (property.getter != null)
|
||||
irGet(property.getter!!.returnType, receiver, property.getter!!.symbol)
|
||||
else
|
||||
irGetField(receiver, property.backingField!!)
|
||||
}
|
||||
|
||||
/*
|
||||
Create a function that creates `get property value expressions` for given corresponded constructor's param
|
||||
(constructor_params) -> get_property_value_expression
|
||||
*/
|
||||
fun IrBuilderWithScope.createPropertyByParamReplacer(
|
||||
irClass: IrClass,
|
||||
serialProperties: List<IrSerializableProperty>,
|
||||
instance: IrValueParameter
|
||||
): (ValueParameterDescriptor) -> IrExpression? {
|
||||
fun IrSerializableProperty.irGet(): IrExpression {
|
||||
val ownerType = instance.symbol.owner.type
|
||||
return getProperty(
|
||||
irGet(
|
||||
type = ownerType,
|
||||
variable = instance.symbol
|
||||
), ir
|
||||
)
|
||||
}
|
||||
|
||||
val serialPropertiesMap = serialProperties.associateBy { it.ir }
|
||||
|
||||
val transientPropertiesSet =
|
||||
irClass.declarations.asSequence()
|
||||
.filterIsInstance<IrProperty>()
|
||||
.filter { it.backingField != null }
|
||||
.filter { !serialPropertiesMap.containsKey(it) }
|
||||
.toSet()
|
||||
|
||||
return { vpd ->
|
||||
val propertyDescriptor = irClass.properties.find { it.name == vpd.name }
|
||||
if (propertyDescriptor != null) {
|
||||
val value = serialPropertiesMap[propertyDescriptor]
|
||||
value?.irGet() ?: run {
|
||||
if (propertyDescriptor in transientPropertiesSet)
|
||||
getProperty(
|
||||
irGet(instance),
|
||||
propertyDescriptor
|
||||
)
|
||||
else null
|
||||
}
|
||||
} else {
|
||||
null
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
Creates an initializer adapter function that can replace IR expressions of getting constructor parameter value by some other expression.
|
||||
Also adapter may replace IR expression of getting `this` value by another expression.
|
||||
*/
|
||||
@OptIn(ObsoleteDescriptorBasedAPI::class)
|
||||
fun createInitializerAdapter(
|
||||
irClass: IrClass,
|
||||
paramGetReplacer: (ValueParameterDescriptor) -> IrExpression?,
|
||||
thisGetReplacer: Pair<IrValueSymbol, () -> IrExpression>? = null
|
||||
): (IrExpressionBody) -> IrExpression {
|
||||
val initializerTransformer = object : IrElementTransformerVoid() {
|
||||
// try to replace `get some value` expression
|
||||
override fun visitGetValue(expression: IrGetValue): IrExpression {
|
||||
val symbol = expression.symbol
|
||||
if (thisGetReplacer != null && thisGetReplacer.first == symbol) {
|
||||
// replace `get this value` expression
|
||||
return thisGetReplacer.second()
|
||||
}
|
||||
|
||||
val descriptor = symbol.descriptor
|
||||
if (descriptor is ValueParameterDescriptor) {
|
||||
// replace `get parameter value` expression
|
||||
paramGetReplacer(descriptor)?.let { return it }
|
||||
}
|
||||
|
||||
// otherwise leave expression as it is
|
||||
return super.visitGetValue(expression)
|
||||
}
|
||||
}
|
||||
val defaultsMap = extractDefaultValuesFromConstructor(irClass)
|
||||
return fun(initializer: IrExpressionBody): IrExpression {
|
||||
val rawExpression = initializer.expression
|
||||
val expression =
|
||||
if (rawExpression.isInitializePropertyFromParameter()) {
|
||||
// this is a primary constructor property, use corresponding default of value parameter
|
||||
defaultsMap.getValue((rawExpression as IrGetValue).symbol)!!
|
||||
} else {
|
||||
rawExpression
|
||||
}
|
||||
return expression.deepCopyWithVariables().transform(initializerTransformer, null)
|
||||
}
|
||||
}
|
||||
|
||||
private fun extractDefaultValuesFromConstructor(irClass: IrClass?): Map<IrValueSymbol, IrExpression?> {
|
||||
if (irClass == null) return emptyMap()
|
||||
val original = irClass.constructors.singleOrNull { it.isPrimary }
|
||||
// default arguments of original constructor
|
||||
val defaultsMap: Map<IrValueSymbol, IrExpression?> =
|
||||
original?.valueParameters?.associate { it.symbol to it.defaultValue?.expression } ?: emptyMap()
|
||||
return defaultsMap + extractDefaultValuesFromConstructor(irClass.getSuperClassNotAny())
|
||||
}
|
||||
+46
@@ -0,0 +1,46 @@
|
||||
/*
|
||||
* Copyright 2010-2022 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlinx.serialization.compiler.backend.ir
|
||||
|
||||
import org.jetbrains.kotlin.backend.common.extensions.IrPluginContext
|
||||
import org.jetbrains.kotlin.ir.declarations.IrClass
|
||||
import org.jetbrains.kotlin.ir.declarations.IrConstructor
|
||||
import org.jetbrains.kotlin.ir.expressions.IrBody
|
||||
import org.jetbrains.kotlin.ir.expressions.impl.IrDelegatingConstructorCallImpl
|
||||
import org.jetbrains.kotlin.ir.expressions.impl.IrInstanceInitializerCallImpl
|
||||
import org.jetbrains.kotlin.ir.types.IrSimpleType
|
||||
import org.jetbrains.kotlin.ir.util.constructors
|
||||
import org.jetbrains.kotlin.ir.util.primaryConstructor
|
||||
|
||||
// TODO KT-53096
|
||||
fun IrPluginContext.generateBodyForDefaultConstructor(declaration: IrConstructor): IrBody? {
|
||||
val type = declaration.returnType as? IrSimpleType ?: return null
|
||||
|
||||
val delegatingAnyCall = IrDelegatingConstructorCallImpl(
|
||||
-1,
|
||||
-1,
|
||||
irBuiltIns.anyType,
|
||||
irBuiltIns.anyClass.owner.primaryConstructor?.symbol ?: return null,
|
||||
typeArgumentsCount = 0,
|
||||
valueArgumentsCount = 0
|
||||
)
|
||||
|
||||
val initializerCall = IrInstanceInitializerCallImpl(
|
||||
-1,
|
||||
-1,
|
||||
(declaration.parent as? IrClass)?.symbol ?: return null,
|
||||
type
|
||||
)
|
||||
|
||||
return irFactory.createBlockBody(-1, -1, listOf(delegatingAnyCall, initializerCall))
|
||||
}
|
||||
|
||||
val Sequence<IrConstructor>.primary get() = find { it.isPrimary } ?: error("Expected to have a primary constructor")
|
||||
|
||||
fun IrClass.addDefaultConstructorIfAbsent(ctx: IrPluginContext) {
|
||||
val declaration = constructors.primary
|
||||
if (declaration.body == null) declaration.body = ctx.generateBodyForDefaultConstructor(declaration)
|
||||
}
|
||||
+403
@@ -0,0 +1,403 @@
|
||||
/*
|
||||
* Copyright 2010-2021 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlinx.serialization.compiler.backend.ir
|
||||
|
||||
import org.jetbrains.kotlin.backend.common.extensions.FirIncompatiblePluginAPI
|
||||
import org.jetbrains.kotlin.backend.common.lower.DeclarationIrBuilder
|
||||
import org.jetbrains.kotlin.backend.common.lower.createIrBuilder
|
||||
import org.jetbrains.kotlin.builtins.KotlinBuiltIns
|
||||
import org.jetbrains.kotlin.builtins.StandardNames
|
||||
import org.jetbrains.kotlin.descriptors.*
|
||||
import org.jetbrains.kotlin.ir.ObsoleteDescriptorBasedAPI
|
||||
import org.jetbrains.kotlin.ir.builders.*
|
||||
import org.jetbrains.kotlin.ir.builders.declarations.*
|
||||
import org.jetbrains.kotlin.ir.declarations.*
|
||||
import org.jetbrains.kotlin.ir.deepCopyWithVariables
|
||||
import org.jetbrains.kotlin.ir.expressions.*
|
||||
import org.jetbrains.kotlin.ir.expressions.impl.*
|
||||
import org.jetbrains.kotlin.ir.symbols.IrClassSymbol
|
||||
import org.jetbrains.kotlin.ir.symbols.IrFunctionSymbol
|
||||
import org.jetbrains.kotlin.ir.symbols.impl.*
|
||||
import org.jetbrains.kotlin.ir.types.*
|
||||
import org.jetbrains.kotlin.ir.util.*
|
||||
import org.jetbrains.kotlin.name.CallableId
|
||||
import org.jetbrains.kotlin.name.ClassId
|
||||
import org.jetbrains.kotlin.name.FqName
|
||||
import org.jetbrains.kotlin.name.Name
|
||||
import org.jetbrains.kotlin.platform.jvm.isJvm
|
||||
import org.jetbrains.kotlin.resolve.jvm.JvmPrimitiveType
|
||||
import org.jetbrains.kotlin.types.KotlinType
|
||||
import org.jetbrains.kotlinx.serialization.compiler.extensions.SerializationPluginContext
|
||||
import org.jetbrains.kotlinx.serialization.compiler.resolve.SerializationDependencies.LAZY_FQ
|
||||
import org.jetbrains.kotlinx.serialization.compiler.resolve.SerializationDependencies.LAZY_MODE_FQ
|
||||
import org.jetbrains.kotlinx.serialization.compiler.resolve.SerializationDependencies.LAZY_PUBLICATION_MODE_NAME
|
||||
|
||||
|
||||
interface IrBuilderWithPluginContext {
|
||||
val compilerContext: SerializationPluginContext
|
||||
|
||||
fun <F: IrFunction> addFunctionBody(function: F, bodyGen: IrBlockBodyBuilder.(F) -> Unit) {
|
||||
val parentClass = function.parent
|
||||
val startOffset = function.startOffset.takeIf { it >= 0 } ?: parentClass.startOffset
|
||||
val endOffset = function.endOffset.takeIf { it >= 0 } ?: parentClass.endOffset
|
||||
function.body = DeclarationIrBuilder(compilerContext, function.symbol, startOffset, endOffset).irBlockBody(
|
||||
startOffset,
|
||||
endOffset
|
||||
) { bodyGen(function) }
|
||||
}
|
||||
|
||||
fun IrClass.createLambdaExpression(
|
||||
type: IrType,
|
||||
bodyGen: IrBlockBodyBuilder.() -> Unit
|
||||
): IrFunctionExpression {
|
||||
val function = compilerContext.irFactory.buildFun {
|
||||
this.startOffset = this@createLambdaExpression.startOffset
|
||||
this.endOffset = this@createLambdaExpression.endOffset
|
||||
this.returnType = type
|
||||
name = Name.identifier("<anonymous>")
|
||||
visibility = DescriptorVisibilities.LOCAL
|
||||
origin = SERIALIZATION_PLUGIN_ORIGIN
|
||||
}
|
||||
function.body =
|
||||
DeclarationIrBuilder(compilerContext, function.symbol, startOffset, endOffset).irBlockBody(startOffset, endOffset, bodyGen)
|
||||
function.parent = this
|
||||
|
||||
val f0Type = compilerContext.irBuiltIns.functionN(0)
|
||||
val f0ParamSymbol = f0Type.typeParameters[0].symbol
|
||||
val f0IrType = f0Type.defaultType.substitute(mapOf(f0ParamSymbol to type))
|
||||
|
||||
return IrFunctionExpressionImpl(
|
||||
startOffset,
|
||||
endOffset,
|
||||
f0IrType,
|
||||
function,
|
||||
IrStatementOrigin.LAMBDA
|
||||
)
|
||||
}
|
||||
|
||||
fun createLazyProperty(
|
||||
containingClass: IrClass,
|
||||
targetIrType: IrType,
|
||||
name: Name,
|
||||
initializerBuilder: IrBlockBodyBuilder.() -> Unit
|
||||
): IrProperty {
|
||||
val lazySafeModeClassDescriptor = compilerContext.referenceClass(ClassId.topLevel(LAZY_MODE_FQ))!!.owner
|
||||
val lazyFunctionSymbol = compilerContext.referenceFunctions(CallableId(StandardNames.BUILT_INS_PACKAGE_FQ_NAME, Name.identifier("lazy"))).single {
|
||||
it.owner.valueParameters.size == 2 && it.owner.valueParameters[0].type == lazySafeModeClassDescriptor.defaultType
|
||||
}
|
||||
val publicationEntryDescriptor = lazySafeModeClassDescriptor.enumEntries().single { it.name == LAZY_PUBLICATION_MODE_NAME }
|
||||
|
||||
val lazyIrClass = compilerContext.referenceClass(ClassId.topLevel(LAZY_FQ))!!.owner
|
||||
val lazyIrType = lazyIrClass.defaultType.substitute(mapOf(lazyIrClass.typeParameters[0].symbol to targetIrType))
|
||||
|
||||
return generateSimplePropertyWithBackingField(Name.identifier(name.asString() + "\$delegate"), lazyIrType, containingClass).apply {
|
||||
val builder = DeclarationIrBuilder(compilerContext, containingClass.symbol, startOffset, endOffset)
|
||||
val initializerBody = builder.run {
|
||||
val enumElement = IrGetEnumValueImpl(
|
||||
startOffset,
|
||||
endOffset,
|
||||
lazySafeModeClassDescriptor.defaultType,
|
||||
publicationEntryDescriptor.symbol
|
||||
)
|
||||
|
||||
val lambdaExpression = containingClass.createLambdaExpression(targetIrType, initializerBuilder)
|
||||
|
||||
irExprBody(
|
||||
irInvoke(null, lazyFunctionSymbol, listOf(targetIrType), listOf(enumElement, lambdaExpression), lazyIrType)
|
||||
)
|
||||
}
|
||||
backingField!!.initializer = initializerBody
|
||||
}
|
||||
}
|
||||
|
||||
fun createCompanionValProperty(
|
||||
companionClass: IrClass,
|
||||
type: IrType,
|
||||
name: Name,
|
||||
initializerBuilder: IrBlockBodyBuilder.() -> Unit
|
||||
): IrProperty {
|
||||
return generateSimplePropertyWithBackingField(name, type, companionClass).apply {
|
||||
companionClass.contributeAnonymousInitializer {
|
||||
val irBlockBody = irBlockBody(startOffset, endOffset, initializerBuilder)
|
||||
irBlockBody.statements.dropLast(1).forEach { +it }
|
||||
val expression = irBlockBody.statements.last() as? IrExpression
|
||||
?: throw AssertionError("Last statement in property initializer builder is not an a expression")
|
||||
+irSetField(irGetObject(companionClass), backingField!!, expression)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun IrClass.contributeAnonymousInitializer(bodyGen: IrBlockBodyBuilder.() -> Unit) {
|
||||
val symbol = IrAnonymousInitializerSymbolImpl(symbol)
|
||||
factory.createAnonymousInitializer(startOffset, endOffset, SERIALIZATION_PLUGIN_ORIGIN, symbol).also {
|
||||
it.parent = this
|
||||
declarations.add(it)
|
||||
it.body = DeclarationIrBuilder(compilerContext, symbol, startOffset, endOffset).irBlockBody(startOffset, endOffset, bodyGen)
|
||||
}
|
||||
}
|
||||
|
||||
fun IrBlockBodyBuilder.getLazyValueExpression(thisParam: IrValueParameter, property: IrProperty, type: IrType): IrExpression {
|
||||
val lazyIrClass = compilerContext.referenceClass(ClassId.topLevel(LAZY_FQ))!!.owner
|
||||
val valueGetter = lazyIrClass.getPropertyGetter("value")!!
|
||||
|
||||
val propertyGetter = property.getter!!
|
||||
|
||||
return irInvoke(
|
||||
irGet(propertyGetter.returnType, irGet(thisParam), propertyGetter.symbol),
|
||||
valueGetter,
|
||||
typeHint = type
|
||||
)
|
||||
}
|
||||
|
||||
fun IrBuilderWithScope.irInvoke(
|
||||
dispatchReceiver: IrExpression? = null,
|
||||
callee: IrFunctionSymbol,
|
||||
vararg args: IrExpression,
|
||||
typeHint: IrType? = null
|
||||
): IrMemberAccessExpression<*> {
|
||||
assert(callee.isBound) { "Symbol $callee expected to be bound" }
|
||||
val returnType = typeHint ?: callee.owner.returnType
|
||||
val call = irCall(callee, type = returnType)
|
||||
call.dispatchReceiver = dispatchReceiver
|
||||
args.forEachIndexed(call::putValueArgument)
|
||||
return call
|
||||
}
|
||||
|
||||
fun IrBuilderWithScope.irInvoke(
|
||||
dispatchReceiver: IrExpression? = null,
|
||||
callee: IrFunctionSymbol,
|
||||
typeArguments: List<IrType?>,
|
||||
valueArguments: List<IrExpression>,
|
||||
returnTypeHint: IrType? = null
|
||||
): IrMemberAccessExpression<*> =
|
||||
irInvoke(
|
||||
dispatchReceiver,
|
||||
callee,
|
||||
*valueArguments.toTypedArray(),
|
||||
typeHint = returnTypeHint
|
||||
).also { call -> typeArguments.forEachIndexed(call::putTypeArgument) }
|
||||
|
||||
fun IrBuilderWithScope.createArrayOfExpression(
|
||||
arrayElementType: IrType,
|
||||
arrayElements: List<IrExpression>
|
||||
): IrExpression {
|
||||
|
||||
val arrayType = compilerContext.irBuiltIns.arrayClass.typeWith(arrayElementType)
|
||||
val arg0 = IrVarargImpl(startOffset, endOffset, arrayType, arrayElementType, arrayElements)
|
||||
val typeArguments = listOf(arrayElementType)
|
||||
|
||||
return irCall(compilerContext.irBuiltIns.arrayOf, arrayType, typeArguments = typeArguments).apply {
|
||||
putValueArgument(0, arg0)
|
||||
}
|
||||
}
|
||||
|
||||
fun IrBuilderWithScope.createPrimitiveArrayOfExpression(
|
||||
elementPrimitiveType: IrType,
|
||||
arrayElements: List<IrExpression>
|
||||
): IrExpression {
|
||||
val arrayType = compilerContext.irBuiltIns.primitiveArrayForType.getValue(elementPrimitiveType).defaultType
|
||||
val arg0 = IrVarargImpl(startOffset, endOffset, arrayType, elementPrimitiveType, arrayElements)
|
||||
val typeArguments = listOf(elementPrimitiveType)
|
||||
|
||||
return irCall(compilerContext.irBuiltIns.arrayOf, arrayType, typeArguments = typeArguments).apply {
|
||||
putValueArgument(0, arg0)
|
||||
}
|
||||
}
|
||||
|
||||
fun IrBuilderWithScope.irBinOp(name: Name, lhs: IrExpression, rhs: IrExpression): IrExpression {
|
||||
val classFqName = (lhs.type as IrSimpleType).classOrNull!!.owner.fqNameWhenAvailable!!
|
||||
val symbol = compilerContext.referenceFunctions(CallableId(ClassId.topLevel(classFqName), name)).single()
|
||||
return irInvoke(lhs, symbol, rhs)
|
||||
}
|
||||
|
||||
fun IrBuilderWithScope.irGetObject(irObject: IrClass) =
|
||||
IrGetObjectValueImpl(
|
||||
startOffset,
|
||||
endOffset,
|
||||
irObject.defaultType,
|
||||
irObject.symbol
|
||||
)
|
||||
|
||||
fun <T : IrDeclaration> T.buildWithScope(builder: (T) -> Unit): T =
|
||||
also { irDeclaration ->
|
||||
compilerContext.symbolTable.withReferenceScope(irDeclaration) {
|
||||
builder(irDeclaration)
|
||||
}
|
||||
}
|
||||
|
||||
class BranchBuilder(
|
||||
val irWhen: IrWhen,
|
||||
context: IrGeneratorContext,
|
||||
scope: Scope,
|
||||
startOffset: Int,
|
||||
endOffset: Int
|
||||
) : IrBuilderWithScope(context, scope, startOffset, endOffset) {
|
||||
operator fun IrBranch.unaryPlus() {
|
||||
irWhen.branches.add(this)
|
||||
}
|
||||
}
|
||||
|
||||
fun IrBuilderWithScope.irWhen(typeHint: IrType? = null, block: BranchBuilder.() -> Unit): IrWhen {
|
||||
val whenExpr = IrWhenImpl(startOffset, endOffset, typeHint ?: compilerContext.irBuiltIns.unitType)
|
||||
val builder = BranchBuilder(whenExpr, context, scope, startOffset, endOffset)
|
||||
builder.block()
|
||||
return whenExpr
|
||||
}
|
||||
|
||||
fun BranchBuilder.elseBranch(result: IrExpression): IrElseBranch =
|
||||
IrElseBranchImpl(
|
||||
IrConstImpl.boolean(result.startOffset, result.endOffset, compilerContext.irBuiltIns.booleanType, true),
|
||||
result
|
||||
)
|
||||
|
||||
fun IrBuilderWithScope.setProperty(receiver: IrExpression, property: IrProperty, value: IrExpression): IrExpression {
|
||||
return if (property.setter != null)
|
||||
irSet(property.setter!!.returnType, receiver, property.setter!!.symbol, value)
|
||||
else
|
||||
irSetField(receiver, property.backingField!!, value)
|
||||
}
|
||||
|
||||
fun IrBuilderWithScope.generateAnySuperConstructorCall(toBuilder: IrBlockBodyBuilder) {
|
||||
val anyConstructor = compilerContext.irBuiltIns.anyClass.owner.declarations.single { it is IrConstructor } as IrConstructor
|
||||
with(toBuilder) {
|
||||
+IrDelegatingConstructorCallImpl.fromSymbolOwner(
|
||||
startOffset, endOffset,
|
||||
compilerContext.irBuiltIns.unitType,
|
||||
anyConstructor.symbol
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
fun generateSimplePropertyWithBackingField(
|
||||
propertyName: Name,
|
||||
propertyType: IrType,
|
||||
propertyParent: IrClass,
|
||||
visibility: DescriptorVisibility = DescriptorVisibilities.PRIVATE
|
||||
): IrProperty = generatePropertyMissingParts(null, propertyName, propertyType, propertyParent, visibility)
|
||||
|
||||
fun generatePropertyMissingParts(
|
||||
property: IrProperty?,
|
||||
propertyName: Name,
|
||||
propertyType: IrType,
|
||||
propertyParent: IrClass,
|
||||
visibility: DescriptorVisibility = DescriptorVisibilities.PRIVATE
|
||||
): IrProperty {
|
||||
val field = property?.backingField ?: propertyParent.factory.buildField {
|
||||
startOffset = propertyParent.startOffset
|
||||
endOffset = propertyParent.endOffset
|
||||
name = propertyName
|
||||
type = propertyType
|
||||
origin = SERIALIZATION_PLUGIN_ORIGIN
|
||||
isFinal = true
|
||||
this.visibility = DescriptorVisibilities.PRIVATE
|
||||
}.also { it.parent = propertyParent }
|
||||
|
||||
val prop = property ?: propertyParent.addProperty {
|
||||
startOffset = propertyParent.startOffset
|
||||
endOffset = propertyParent.endOffset
|
||||
name = propertyName
|
||||
this.isVar = false
|
||||
origin = SERIALIZATION_PLUGIN_ORIGIN
|
||||
}
|
||||
|
||||
prop.apply {
|
||||
field.correspondingPropertySymbol = this.symbol
|
||||
backingField = field
|
||||
}
|
||||
|
||||
val getter = prop.getter ?: prop.addGetter {
|
||||
startOffset = propertyParent.startOffset
|
||||
endOffset = propertyParent.endOffset
|
||||
returnType = propertyType
|
||||
origin = SERIALIZATION_PLUGIN_ORIGIN
|
||||
this.visibility = visibility
|
||||
modality = Modality.FINAL
|
||||
}
|
||||
|
||||
getter.apply {
|
||||
if (dispatchReceiverParameter == null)
|
||||
dispatchReceiverParameter = propertyParent.thisReceiver!!.copyTo(this, type = propertyParent.defaultType)
|
||||
if (body == null)
|
||||
body = compilerContext.irBuiltIns.createIrBuilder(symbol, propertyParent.startOffset, propertyParent.endOffset).irBlockBody {
|
||||
+irReturn(irGetField(irGet(dispatchReceiverParameter!!), field))
|
||||
}
|
||||
}
|
||||
return prop
|
||||
}
|
||||
|
||||
fun createClassReference(classType: IrType, startOffset: Int, endOffset: Int): IrClassReference {
|
||||
return IrClassReferenceImpl(
|
||||
startOffset,
|
||||
endOffset,
|
||||
compilerContext.irBuiltIns.kClassClass.starProjectedType,
|
||||
classType.classifierOrFail,
|
||||
classType
|
||||
)
|
||||
}
|
||||
|
||||
fun IrBuilderWithScope.classReference(classSymbol: IrClassSymbol): IrClassReference =
|
||||
createClassReference(classSymbol.starProjectedType, startOffset, endOffset)
|
||||
|
||||
fun collectSerialInfoAnnotations(irClass: IrClass): List<IrConstructorCall> {
|
||||
if (!(irClass.isInterface || irClass.hasSerializableOrMetaAnnotation())) return emptyList()
|
||||
val annotationByFq: MutableMap<FqName, IrConstructorCall> =
|
||||
irClass.annotations.associateBy { it.symbol.owner.parentAsClass.fqNameWhenAvailable!! }.toMutableMap()
|
||||
for (clazz in irClass.getAllSuperclasses()) {
|
||||
val annotations = clazz.annotations
|
||||
.mapNotNull {
|
||||
val parent = it.symbol.owner.parentAsClass
|
||||
if (parent.isInheritableSerialInfoAnnotation) parent.fqNameWhenAvailable!! to it else null
|
||||
}
|
||||
annotations.forEach { (fqname, call) ->
|
||||
if (fqname !in annotationByFq) {
|
||||
annotationByFq[fqname] = call
|
||||
} else {
|
||||
// SerializationPluginDeclarationChecker already reported inconsistency
|
||||
}
|
||||
}
|
||||
}
|
||||
return annotationByFq.values.toList()
|
||||
}
|
||||
|
||||
fun IrBuilderWithScope.copyAnnotationsFrom(annotations: List<IrConstructorCall>): List<IrExpression> =
|
||||
annotations.mapNotNull { annotationCall ->
|
||||
val annotationClass = annotationCall.symbol.owner.parentAsClass
|
||||
if (!annotationClass.isSerialInfoAnnotation) return@mapNotNull null
|
||||
|
||||
if (compilerContext.platform.isJvm()) {
|
||||
val implClass = compilerContext.serialInfoImplJvmIrGenerator.getImplClass(annotationClass)
|
||||
val ctor = implClass.constructors.singleOrNull { it.valueParameters.size == annotationCall.valueArgumentsCount }
|
||||
?: error("No constructor args found for SerialInfo annotation Impl class: ${implClass.render()}")
|
||||
irCall(ctor).apply {
|
||||
for (i in 0 until annotationCall.valueArgumentsCount) {
|
||||
val argument = annotationCall.getValueArgument(i)
|
||||
?: annotationClass.primaryConstructor!!.valueParameters[i].defaultValue?.expression
|
||||
putValueArgument(i, argument!!.deepCopyWithVariables())
|
||||
}
|
||||
}
|
||||
} else {
|
||||
annotationCall.deepCopyWithVariables()
|
||||
}
|
||||
}
|
||||
|
||||
@OptIn(ObsoleteDescriptorBasedAPI::class)
|
||||
fun IrBuilderWithScope.wrapperClassReference(classType: IrType): IrClassReference {
|
||||
if (compilerContext.platform.isJvm()) {
|
||||
// "Byte::class" -> "java.lang.Byte::class"
|
||||
// TODO: get rid of descriptor
|
||||
val wrapperFqName =
|
||||
KotlinBuiltIns.getPrimitiveType(classType.classOrNull!!.descriptor)?.let(JvmPrimitiveType::get)?.wrapperFqName
|
||||
if (wrapperFqName != null) {
|
||||
val wrapperClass = compilerContext.referenceClass(ClassId.topLevel(wrapperFqName))
|
||||
?: error("Primitive wrapper class for $classType not found: $wrapperFqName")
|
||||
return createClassReference(wrapperClass.defaultType, startOffset, endOffset)
|
||||
}
|
||||
}
|
||||
return createClassReference(classType, startOffset, endOffset)
|
||||
}
|
||||
|
||||
fun IrClass.getSuperClassOrAny(): IrClass = getSuperClassNotAny() ?: compilerContext.irBuiltIns.anyClass.owner
|
||||
}
|
||||
+136
@@ -0,0 +1,136 @@
|
||||
/*
|
||||
* Copyright 2010-2022 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlinx.serialization.compiler.backend.ir
|
||||
|
||||
import org.jetbrains.kotlin.descriptors.DescriptorVisibilities
|
||||
import org.jetbrains.kotlin.descriptors.Modality
|
||||
import org.jetbrains.kotlin.ir.builders.declarations.addConstructor
|
||||
import org.jetbrains.kotlin.ir.builders.declarations.addFunction
|
||||
import org.jetbrains.kotlin.ir.builders.declarations.addTypeParameter
|
||||
import org.jetbrains.kotlin.ir.builders.declarations.addValueParameter
|
||||
import org.jetbrains.kotlin.ir.declarations.IrClass
|
||||
import org.jetbrains.kotlin.ir.types.*
|
||||
import org.jetbrains.kotlin.ir.util.companionObject
|
||||
import org.jetbrains.kotlin.name.Name
|
||||
import org.jetbrains.kotlin.resolve.isInlineClass
|
||||
import org.jetbrains.kotlinx.serialization.compiler.backend.common.SerializerCodegen
|
||||
import org.jetbrains.kotlinx.serialization.compiler.extensions.SerializationPluginContext
|
||||
import org.jetbrains.kotlinx.serialization.compiler.resolve.SerialEntityNames
|
||||
import org.jetbrains.kotlinx.serialization.compiler.resolve.bitMaskSlotCount
|
||||
import org.jetbrains.kotlinx.serialization.compiler.resolve.hasCompanionObjectAsSerializer
|
||||
|
||||
/**
|
||||
* Generates only specific declarations, but NOT their bodies.
|
||||
* This pass is needed to be able to reference these declarations from other generated bodies
|
||||
* (e.g. to if we want to reference write$Self() from serialize(), we need to make sure that at least declaration of write$Self is already created.
|
||||
*
|
||||
* These functions were usually stubbed from descriptors, but since FIR discourages purely synthetic functions,
|
||||
* we manually add them here.
|
||||
*/
|
||||
class IrPreGenerator(
|
||||
val irClass: IrClass,
|
||||
compilerContext: SerializationPluginContext,
|
||||
) : BaseIrGenerator(irClass, compilerContext) {
|
||||
|
||||
private fun generate() {
|
||||
preGenerateWriteSelfMethodIfNeeded()
|
||||
preGenerateDeserializationConstructorIfNeeded()
|
||||
}
|
||||
|
||||
private fun preGenerateWriteSelfMethodIfNeeded() {
|
||||
if (!irClass.isInternalSerializable) return
|
||||
val serializerDescriptor = irClass.classSerializer(compilerContext)?.owner ?: return
|
||||
if (!irClass.shouldHaveSpecificSyntheticMethods { serializerDescriptor.findPluginGeneratedMethod(SerialEntityNames.SAVE) }) return
|
||||
if (irClass.findWriteSelfMethod() != null) return
|
||||
val method = irClass.addFunction {
|
||||
name = SerialEntityNames.WRITE_SELF_NAME
|
||||
returnType = compilerContext.irBuiltIns.unitType
|
||||
visibility = DescriptorVisibilities.PUBLIC
|
||||
modality = Modality.FINAL
|
||||
origin = SERIALIZATION_PLUGIN_ORIGIN
|
||||
}
|
||||
method.apply {
|
||||
dispatchReceiverParameter = null // function is static
|
||||
}
|
||||
|
||||
val typeParams = irClass.typeParameters.map {
|
||||
method.addTypeParameter(
|
||||
it.name.asString(), compilerContext.irBuiltIns.anyNType
|
||||
)
|
||||
}
|
||||
val typeParamsAsArguments = typeParams.map { it.defaultType }
|
||||
|
||||
// object
|
||||
method.addValueParameter(
|
||||
Name.identifier("self"), irClass.typeWith(typeParamsAsArguments),
|
||||
SERIALIZATION_PLUGIN_ORIGIN
|
||||
)
|
||||
// encoder
|
||||
method.addValueParameter(
|
||||
Name.identifier("output"),
|
||||
compilerContext.getClassFromRuntime(SerialEntityNames.STRUCTURE_ENCODER_CLASS).defaultType,
|
||||
SERIALIZATION_PLUGIN_ORIGIN
|
||||
)
|
||||
// descriptor
|
||||
val serialDescriptorSymbol = compilerContext.getClassFromRuntime(SerialEntityNames.SERIAL_DESCRIPTOR_CLASS)
|
||||
method.addValueParameter(
|
||||
Name.identifier("serialDesc"), serialDescriptorSymbol.defaultType,
|
||||
SERIALIZATION_PLUGIN_ORIGIN
|
||||
)
|
||||
// KSerializer<Tn>
|
||||
val kSerializerSymbol = compilerContext.getClassFromRuntime(SerialEntityNames.KSERIALIZER_CLASS)
|
||||
typeParamsAsArguments.forEachIndexed { i, it ->
|
||||
method.addValueParameter(
|
||||
Name.identifier("${SerialEntityNames.typeArgPrefix}$i"),
|
||||
kSerializerSymbol.typeWith(it),
|
||||
SERIALIZATION_PLUGIN_ORIGIN
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private fun preGenerateDeserializationConstructorIfNeeded() {
|
||||
if (!irClass.isInternalSerializable) return
|
||||
// do not add synthetic deserialization constructor if .deserialize method is customized
|
||||
if (irClass.hasCompanionObjectAsSerializer && irClass.companionObject()
|
||||
?.findPluginGeneratedMethod(SerialEntityNames.LOAD) == null
|
||||
) return
|
||||
if (irClass.isValue) return
|
||||
if (irClass.findSerializableSyntheticConstructor() != null) return
|
||||
val ctor = irClass.addConstructor {
|
||||
origin = SERIALIZATION_PLUGIN_ORIGIN
|
||||
visibility = DescriptorVisibilities.PUBLIC
|
||||
}
|
||||
val markerClassSymbol =
|
||||
compilerContext.getClassFromInternalSerializationPackage(SerialEntityNames.SERIAL_CTOR_MARKER_NAME.asString())
|
||||
val serializableProperties = serializablePropertiesForIrBackend(irClass).serializableProperties
|
||||
val bitMaskSlotsCount = serializableProperties.bitMaskSlotCount()
|
||||
|
||||
repeat(bitMaskSlotsCount) {
|
||||
ctor.addValueParameter(Name.identifier("seen$it"), compilerContext.irBuiltIns.intType, SERIALIZATION_PLUGIN_ORIGIN)
|
||||
}
|
||||
|
||||
for (prop in serializableProperties) {
|
||||
ctor.addValueParameter(prop.name, prop.type.makeNullableIfNotPrimitive(), SERIALIZATION_PLUGIN_ORIGIN)
|
||||
}
|
||||
|
||||
ctor.addValueParameter(SerialEntityNames.dummyParamName, markerClassSymbol.defaultType, SERIALIZATION_PLUGIN_ORIGIN)
|
||||
}
|
||||
|
||||
private fun IrType.makeNullableIfNotPrimitive() =
|
||||
if (this.isPrimitiveType(false)) this
|
||||
else this.makeNullable()
|
||||
|
||||
companion object {
|
||||
fun generate(
|
||||
irClass: IrClass,
|
||||
compilerContext: SerializationPluginContext
|
||||
) {
|
||||
if (!irClass.isInternalSerializable) return
|
||||
IrPreGenerator(irClass, compilerContext).generate()
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
+240
@@ -0,0 +1,240 @@
|
||||
/*
|
||||
* Copyright 2010-2022 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlinx.serialization.compiler.backend.ir
|
||||
|
||||
import org.jetbrains.kotlin.backend.jvm.ir.getStringConstArgument
|
||||
import org.jetbrains.kotlin.backend.jvm.ir.representativeUpperBound
|
||||
import org.jetbrains.kotlin.descriptors.CallableMemberDescriptor
|
||||
import org.jetbrains.kotlin.descriptors.ClassKind
|
||||
import org.jetbrains.kotlin.descriptors.Modality
|
||||
import org.jetbrains.kotlin.ir.ObsoleteDescriptorBasedAPI
|
||||
import org.jetbrains.kotlin.ir.declarations.*
|
||||
import org.jetbrains.kotlin.ir.expressions.IrConstructorCall
|
||||
import org.jetbrains.kotlin.ir.expressions.IrExpression
|
||||
import org.jetbrains.kotlin.ir.expressions.IrGetEnumValue
|
||||
import org.jetbrains.kotlin.ir.expressions.IrStatementOrigin
|
||||
import org.jetbrains.kotlin.ir.expressions.impl.IrGetValueImpl
|
||||
import org.jetbrains.kotlin.ir.symbols.IrClassSymbol
|
||||
import org.jetbrains.kotlin.ir.symbols.IrConstructorSymbol
|
||||
import org.jetbrains.kotlin.ir.symbols.IrScriptSymbol
|
||||
import org.jetbrains.kotlin.ir.symbols.IrTypeParameterSymbol
|
||||
import org.jetbrains.kotlin.ir.types.*
|
||||
import org.jetbrains.kotlin.ir.util.*
|
||||
import org.jetbrains.kotlin.name.Name
|
||||
import org.jetbrains.kotlin.platform.js.isJs
|
||||
import org.jetbrains.kotlin.platform.konan.isNative
|
||||
import org.jetbrains.kotlinx.serialization.compiler.extensions.SerializationPluginContext
|
||||
import org.jetbrains.kotlinx.serialization.compiler.fir.SerializationPluginKey
|
||||
import org.jetbrains.kotlinx.serialization.compiler.resolve.*
|
||||
|
||||
internal fun IrType.isKSerializer(): Boolean {
|
||||
val simpleType = this as? IrSimpleType ?: return false
|
||||
val classifier = simpleType.classifier as? IrClassSymbol ?: return false
|
||||
val fqName = classifier.owner.fqNameWhenAvailable
|
||||
return fqName == SerialEntityNames.KSERIALIZER_NAME_FQ || fqName == SerialEntityNames.GENERATED_SERIALIZER_FQ
|
||||
}
|
||||
|
||||
internal fun IrType.isGeneratedKSerializer(): Boolean = classifierOrNull?.isClassWithFqName(SerialEntityNames.GENERATED_SERIALIZER_FQ.toUnsafe()) == true
|
||||
|
||||
internal val IrClass.isInternalSerializable: Boolean
|
||||
get() {
|
||||
if (kind != ClassKind.CLASS) return false
|
||||
return hasSerializableOrMetaAnnotationWithoutArgs()
|
||||
}
|
||||
|
||||
internal val IrClass.isAbstractOrSealedSerializableClass: Boolean get() = isInternalSerializable && (modality == Modality.ABSTRACT || modality == Modality.SEALED)
|
||||
|
||||
internal val IrClass.isStaticSerializable: Boolean get() = this.typeParameters.isEmpty()
|
||||
|
||||
|
||||
internal val IrClass.hasCompanionObjectAsSerializer: Boolean
|
||||
get() = isInternallySerializableObject || companionObject()?.serializerForClass == this.symbol
|
||||
|
||||
internal val IrClass.isInternallySerializableObject: Boolean
|
||||
get() = kind == ClassKind.OBJECT && hasSerializableOrMetaAnnotationWithoutArgs()
|
||||
|
||||
|
||||
internal fun IrClass.findPluginGeneratedMethod(name: String): IrSimpleFunction? {
|
||||
return this.functions.find {
|
||||
it.name.asString() == name && it.isFromPlugin()
|
||||
}
|
||||
}
|
||||
|
||||
internal fun IrClass.isEnumWithLegacyGeneratedSerializer(context: SerializationPluginContext): Boolean = isInternallySerializableEnum() && !context.runtimeHasEnumSerializerFactoryFunctions
|
||||
|
||||
internal val IrClass.isSealedSerializableInterface: Boolean
|
||||
get() = kind == ClassKind.INTERFACE && modality == Modality.SEALED && hasSerializableOrMetaAnnotation()
|
||||
|
||||
internal fun IrClass.isInternallySerializableEnum(): Boolean =
|
||||
kind == ClassKind.ENUM_CLASS && hasSerializableOrMetaAnnotationWithoutArgs()
|
||||
|
||||
fun IrType.isGeneratedSerializableObject(): Boolean {
|
||||
return classOrNull?.run { owner.kind == ClassKind.OBJECT && owner.hasSerializableOrMetaAnnotationWithoutArgs() } == true
|
||||
}
|
||||
|
||||
internal val IrClass.isSerializableObject: Boolean
|
||||
get() = kind == ClassKind.OBJECT && hasSerializableOrMetaAnnotation()
|
||||
|
||||
internal fun IrClass.hasSerializableOrMetaAnnotationWithoutArgs(): Boolean = checkSerializableOrMetaAnnotationArgs(mustDoNotHaveArgs = true)
|
||||
|
||||
fun IrClass.hasSerializableOrMetaAnnotation() = checkSerializableOrMetaAnnotationArgs(mustDoNotHaveArgs = false)
|
||||
|
||||
private fun IrClass.checkSerializableOrMetaAnnotationArgs(mustDoNotHaveArgs: Boolean): Boolean {
|
||||
val annot = getAnnotation(SerializationAnnotations.serializableAnnotationFqName)
|
||||
if (annot != null) { // @Serializable have higher priority
|
||||
if (!mustDoNotHaveArgs) return true
|
||||
if (annot.getValueArgument(0) != null) return false
|
||||
return true
|
||||
}
|
||||
return annotations
|
||||
.map { it.constructedClass.annotations }
|
||||
.any { it.hasAnnotation(SerializationAnnotations.metaSerializableAnnotationFqName) }
|
||||
}
|
||||
|
||||
internal val IrClass.isSerialInfoAnnotation: Boolean
|
||||
get() = annotations.hasAnnotation(SerializationAnnotations.serialInfoFqName)
|
||||
|| annotations.hasAnnotation(SerializationAnnotations.inheritableSerialInfoFqName)
|
||||
|| annotations.hasAnnotation(SerializationAnnotations.metaSerializableAnnotationFqName)
|
||||
|
||||
internal val IrClass.isInheritableSerialInfoAnnotation: Boolean
|
||||
get() = annotations.hasAnnotation(SerializationAnnotations.inheritableSerialInfoFqName)
|
||||
|
||||
internal fun IrClass.shouldHaveGeneratedSerializer(context: SerializationPluginContext): Boolean
|
||||
= (isInternalSerializable && (modality == Modality.FINAL || modality == Modality.OPEN))
|
||||
|| isEnumWithLegacyGeneratedSerializer(context)
|
||||
|
||||
internal val IrClass.shouldHaveGeneratedMethodsInCompanion: Boolean
|
||||
get() = this.isSerializableObject || this.isSerializableEnum() || (this.kind == ClassKind.CLASS && hasSerializableOrMetaAnnotation()) || this.isSealedSerializableInterface
|
||||
|
||||
internal fun IrClass.isSerializableEnum(): Boolean = kind == ClassKind.ENUM_CLASS && hasSerializableOrMetaAnnotation()
|
||||
|
||||
internal val IrType.genericIndex: Int?
|
||||
get() = (this.classifierOrNull as? IrTypeParameterSymbol)?.owner?.index
|
||||
|
||||
fun IrType.serialName(): String = this.classOrUpperBound()!!.owner.serialName()
|
||||
|
||||
fun IrClass.serialName(): String {
|
||||
return annotations.serialNameValue ?: fqNameWhenAvailable?.asString() ?: error("${this.render()} does not have fqName")
|
||||
}
|
||||
|
||||
fun IrClass.findEnumValuesMethod() = this.functions.singleOrNull { f ->
|
||||
f.name == Name.identifier("values") && f.valueParameters.isEmpty() && f.extensionReceiverParameter == null && f.dispatchReceiverParameter == null
|
||||
} ?: throw AssertionError("Enum class does not have single .values() function")
|
||||
|
||||
internal fun IrClass.enumEntries(): List<IrEnumEntry> {
|
||||
check(this.kind == ClassKind.ENUM_CLASS)
|
||||
return declarations.filterIsInstance<IrEnumEntry>().toList()
|
||||
}
|
||||
|
||||
internal fun IrClass.isEnumWithSerialInfoAnnotation(): Boolean {
|
||||
if (kind != ClassKind.ENUM_CLASS) return false
|
||||
if (annotations.hasAnySerialAnnotation) return true
|
||||
return enumEntries().any { (it.annotations.hasAnySerialAnnotation) }
|
||||
}
|
||||
|
||||
fun IrClass.findWriteSelfMethod(): IrSimpleFunction? =
|
||||
functions.singleOrNull { it.name == SerialEntityNames.WRITE_SELF_NAME && !it.isFakeOverride }
|
||||
|
||||
fun IrClass.getSuperClassNotAny(): IrClass? {
|
||||
val parentClass =
|
||||
superTypes
|
||||
.mapNotNull { it.classOrNull?.owner }
|
||||
.singleOrNull { it.kind == ClassKind.CLASS || it.kind == ClassKind.ENUM_CLASS } ?: return null
|
||||
return if (parentClass.defaultType.isAny()) null else parentClass
|
||||
}
|
||||
|
||||
@OptIn(ObsoleteDescriptorBasedAPI::class)
|
||||
internal fun IrDeclaration.isFromPlugin(): Boolean =
|
||||
this.origin == IrDeclarationOrigin.GeneratedByPlugin(SerializationPluginKey) || (this.descriptor as? CallableMemberDescriptor)?.kind == CallableMemberDescriptor.Kind.SYNTHESIZED // old FE doesn't specify origin
|
||||
|
||||
internal fun IrConstructor.isSerializationCtor(): Boolean {
|
||||
/*kind == CallableMemberDescriptor.Kind.SYNTHESIZED does not work because DeserializedClassConstructorDescriptor loses its kind*/
|
||||
return valueParameters.lastOrNull()?.run {
|
||||
name == SerialEntityNames.dummyParamName && type.classFqName == SerializationPackages.internalPackageFqName.child(
|
||||
SerialEntityNames.SERIAL_CTOR_MARKER_NAME
|
||||
)
|
||||
} == true
|
||||
}
|
||||
|
||||
|
||||
internal fun IrConstructor.lastArgumentIsAnnotationArray(): Boolean {
|
||||
val lastArgType = valueParameters.lastOrNull()?.type
|
||||
if (lastArgType == null || !lastArgType.isArray()) return false
|
||||
return ((lastArgType as? IrSimpleType)?.arguments?.firstOrNull()?.typeOrNull?.classFqName?.toString() == "kotlin.Annotation")
|
||||
}
|
||||
|
||||
fun IrClass.findSerializableSyntheticConstructor(): IrConstructorSymbol? {
|
||||
return declarations.filterIsInstance<IrConstructor>().singleOrNull { it.isSerializationCtor() }?.symbol
|
||||
}
|
||||
|
||||
internal fun IrClass.needSerializerFactory(compilerContext: SerializationPluginContext): Boolean {
|
||||
if (!(compilerContext.platform?.isNative() == true || compilerContext.platform.isJs())) return false
|
||||
val serializableClass = getSerializableClassDescriptorByCompanion(this) ?: return false
|
||||
if (serializableClass.isSerializableObject) return true
|
||||
if (serializableClass.isSerializableEnum()) return true
|
||||
if (serializableClass.isAbstractOrSealedSerializableClass) return true
|
||||
if (serializableClass.isSealedSerializableInterface) return true
|
||||
if (serializableClass.typeParameters.isEmpty()) return false
|
||||
return true
|
||||
}
|
||||
|
||||
|
||||
internal fun getSerializableClassDescriptorByCompanion(companion: IrClass): IrClass? {
|
||||
if (companion.isSerializableObject) return companion
|
||||
if (!companion.isCompanion) return null
|
||||
val classDescriptor = (companion.parent as? IrClass) ?: return null
|
||||
if (!classDescriptor.shouldHaveGeneratedMethodsInCompanion) return null
|
||||
return classDescriptor
|
||||
}
|
||||
|
||||
|
||||
internal fun IrExpression.isInitializePropertyFromParameter(): Boolean =
|
||||
this is IrGetValueImpl && this.origin == IrStatementOrigin.INITIALIZE_PROPERTY_FROM_PARAMETER
|
||||
|
||||
internal val IrConstructorCall.constructedClass
|
||||
get() = this.symbol.owner.constructedClass
|
||||
|
||||
internal val List<IrConstructorCall>.hasAnySerialAnnotation: Boolean
|
||||
get() = serialNameValue != null || any { it.constructedClass.isSerialInfoAnnotation }
|
||||
|
||||
internal val List<IrConstructorCall>.serialNameValue: String?
|
||||
get() = findAnnotation(SerializationAnnotations.serialNameAnnotationFqName)?.getStringConstArgument(0) // @SerialName("foo")
|
||||
|
||||
/**
|
||||
* True — ALWAYS
|
||||
* False — NEVER
|
||||
* null — not specified
|
||||
*/
|
||||
fun IrProperty.getEncodeDefaultAnnotationValue(): Boolean? {
|
||||
val call = annotations.findAnnotation(SerializationAnnotations.encodeDefaultFqName) ?: return null
|
||||
val arg = call.getValueArgument(0) ?: return true // ALWAYS by default
|
||||
val argValue = (arg as? IrGetEnumValue
|
||||
?: error("Argument of enum constructor expected to implement IrGetEnumValue, got $arg")).symbol.owner.name.toString()
|
||||
return when (argValue) {
|
||||
"ALWAYS" -> true
|
||||
"NEVER" -> false
|
||||
else -> error("Unknown EncodeDefaultMode enum value: $argValue")
|
||||
}
|
||||
}
|
||||
|
||||
fun findSerializerConstructorForTypeArgumentsSerializers(serializer: IrClass): IrConstructorSymbol? {
|
||||
val typeParamsCount = ((serializer.superTypes.find { it.isKSerializer() } as IrSimpleType).arguments.first().typeOrNull!! as IrSimpleType).arguments.size
|
||||
if (typeParamsCount == 0) return null //don't need it
|
||||
|
||||
return serializer.constructors.singleOrNull {
|
||||
it.valueParameters.let { vps -> vps.size == typeParamsCount && vps.all { vp -> vp.type.isKSerializer() } }
|
||||
}?.symbol
|
||||
}
|
||||
|
||||
fun IrType.classOrUpperBound(): IrClassSymbol? = when(val cls = classifierOrNull) {
|
||||
is IrClassSymbol -> cls
|
||||
is IrScriptSymbol -> cls.owner.targetClass
|
||||
is IrTypeParameterSymbol -> cls.owner.representativeUpperBound.classOrUpperBound()
|
||||
else -> null
|
||||
}
|
||||
|
||||
internal inline fun IrClass.shouldHaveSpecificSyntheticMethods(functionPresenceChecker: () -> IrSimpleFunction?) =
|
||||
!isValue && (isAbstractOrSealedSerializableClass || functionPresenceChecker() != null)
|
||||
+102
@@ -0,0 +1,102 @@
|
||||
/*
|
||||
* Copyright 2010-2022 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlinx.serialization.compiler.backend.ir
|
||||
|
||||
import org.jetbrains.kotlin.descriptors.DescriptorVisibilities
|
||||
import org.jetbrains.kotlin.ir.ObsoleteDescriptorBasedAPI
|
||||
import org.jetbrains.kotlin.ir.declarations.IrClass
|
||||
import org.jetbrains.kotlin.ir.declarations.IrProperty
|
||||
import org.jetbrains.kotlin.ir.types.IrSimpleType
|
||||
import org.jetbrains.kotlin.ir.util.hasAnnotation
|
||||
import org.jetbrains.kotlin.ir.util.hasDefaultValue
|
||||
import org.jetbrains.kotlin.ir.util.primaryConstructor
|
||||
import org.jetbrains.kotlin.ir.util.properties
|
||||
import org.jetbrains.kotlin.name.Name
|
||||
import org.jetbrains.kotlin.serialization.deserialization.descriptors.DeserializedPropertyDescriptor
|
||||
import org.jetbrains.kotlinx.serialization.compiler.extensions.SerializationDescriptorSerializerPlugin
|
||||
import org.jetbrains.kotlinx.serialization.compiler.extensions.SerializationPluginContext
|
||||
import org.jetbrains.kotlinx.serialization.compiler.resolve.*
|
||||
|
||||
class IrSerializableProperty(
|
||||
val ir: IrProperty,
|
||||
override val isConstructorParameterWithDefault: Boolean,
|
||||
hasBackingField: Boolean,
|
||||
declaresDefaultValue: Boolean
|
||||
) : ISerializableProperty {
|
||||
override val name = ir.annotations.serialNameValue ?: ir.name.asString()
|
||||
override val originalDescriptorName: Name = ir.name
|
||||
val type = ir.getter!!.returnType as IrSimpleType
|
||||
val genericIndex = type.genericIndex
|
||||
fun serializableWith(ctx: SerializationPluginContext) = ir.annotations.serializableWith() ?: analyzeSpecialSerializers(ctx, ir.annotations)
|
||||
override val optional = !ir.annotations.hasAnnotation(SerializationAnnotations.requiredAnnotationFqName) && declaresDefaultValue
|
||||
override val transient = ir.annotations.hasAnnotation(SerializationAnnotations.serialTransientFqName) || !hasBackingField
|
||||
}
|
||||
|
||||
class IrSerializableProperties(
|
||||
override val serializableProperties: List<IrSerializableProperty>,
|
||||
override val isExternallySerializable: Boolean,
|
||||
override val serializableConstructorProperties: List<IrSerializableProperty>,
|
||||
override val serializableStandaloneProperties: List<IrSerializableProperty>
|
||||
) : ISerializableProperties<IrSerializableProperty>
|
||||
|
||||
@OptIn(ObsoleteDescriptorBasedAPI::class)
|
||||
internal fun serializablePropertiesForIrBackend(
|
||||
irClass: IrClass,
|
||||
serializationDescriptorSerializer: SerializationDescriptorSerializerPlugin? = null
|
||||
): IrSerializableProperties {
|
||||
val properties = irClass.properties.toList()
|
||||
val primaryConstructorParams = irClass.primaryConstructor?.valueParameters.orEmpty()
|
||||
val primaryParamsAsProps = properties.associateBy { it.name }.let { namesMap ->
|
||||
primaryConstructorParams.mapNotNull {
|
||||
if (it.name !in namesMap) null else namesMap.getValue(it.name) to it.hasDefaultValue()
|
||||
}.toMap()
|
||||
}
|
||||
|
||||
fun isPropSerializable(it: IrProperty) =
|
||||
if (irClass.isInternalSerializable) !it.annotations.hasAnnotation(SerializationAnnotations.serialTransientFqName)
|
||||
else !DescriptorVisibilities.isPrivate(it.visibility) && ((it.isVar && !it.annotations.hasAnnotation(SerializationAnnotations.serialTransientFqName)) || primaryParamsAsProps.contains(
|
||||
it
|
||||
))
|
||||
|
||||
val (primaryCtorSerializableProps, bodySerializableProps) = properties
|
||||
.asSequence()
|
||||
.filter { !it.isFakeOverride && !it.isDelegated }
|
||||
.filter(::isPropSerializable)
|
||||
.map {
|
||||
val isConstructorParameterWithDefault = primaryParamsAsProps[it] ?: false
|
||||
// FIXME: workaround because IrLazyProperty doesn't deserialize information about backing fields. Fallback to descriptor won't work with FIR.
|
||||
val isPropertyFromAnotherModuleDeclaresDefaultValue = it.descriptor is DeserializedPropertyDescriptor && it.descriptor.declaresDefaultValue()
|
||||
val isPropertyWithBackingFieldFromAnotherModule = it.descriptor is DeserializedPropertyDescriptor && (it.descriptor.backingField != null || isPropertyFromAnotherModuleDeclaresDefaultValue)
|
||||
IrSerializableProperty(
|
||||
it,
|
||||
isConstructorParameterWithDefault,
|
||||
it.backingField != null || isPropertyWithBackingFieldFromAnotherModule,
|
||||
it.backingField?.initializer.let { init -> init != null && !init.expression.isInitializePropertyFromParameter() } || isConstructorParameterWithDefault
|
||||
|| isPropertyFromAnotherModuleDeclaresDefaultValue
|
||||
)
|
||||
}
|
||||
.filterNot { it.transient }
|
||||
.partition { primaryParamsAsProps.contains(it.ir) }
|
||||
|
||||
var serializableProps = run {
|
||||
val supers = irClass.getSuperClassNotAny()
|
||||
if (supers == null || !supers.isInternalSerializable)
|
||||
primaryCtorSerializableProps + bodySerializableProps
|
||||
else
|
||||
serializablePropertiesForIrBackend(
|
||||
supers,
|
||||
serializationDescriptorSerializer
|
||||
).serializableProperties + primaryCtorSerializableProps + bodySerializableProps
|
||||
}
|
||||
|
||||
// FIXME: since descriptor from FIR does not have classProto in it(?), this line won't do anything
|
||||
serializableProps = restoreCorrectOrderFromClassProtoExtension(irClass.descriptor, serializableProps)
|
||||
|
||||
val isExternallySerializable =
|
||||
irClass.isInternallySerializableEnum() || primaryConstructorParams.size == primaryParamsAsProps.size
|
||||
|
||||
return IrSerializableProperties(serializableProps, isExternallySerializable, primaryCtorSerializableProps, bodySerializableProps)
|
||||
}
|
||||
+417
@@ -0,0 +1,417 @@
|
||||
/*
|
||||
* Copyright 2010-2020 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlinx.serialization.compiler.backend.ir
|
||||
|
||||
import org.jetbrains.kotlin.backend.common.extensions.FirIncompatiblePluginAPI
|
||||
import org.jetbrains.kotlin.backend.common.ir.addExtensionReceiver
|
||||
import org.jetbrains.kotlin.descriptors.*
|
||||
import org.jetbrains.kotlin.ir.ObsoleteDescriptorBasedAPI
|
||||
import org.jetbrains.kotlin.ir.UNDEFINED_OFFSET
|
||||
import org.jetbrains.kotlin.ir.builders.declarations.*
|
||||
import org.jetbrains.kotlin.ir.declarations.*
|
||||
import org.jetbrains.kotlin.ir.declarations.impl.IrExternalPackageFragmentImpl
|
||||
import org.jetbrains.kotlin.ir.declarations.impl.IrFactoryImpl
|
||||
import org.jetbrains.kotlin.ir.expressions.IrBlockBody
|
||||
import org.jetbrains.kotlin.ir.expressions.IrConstructorCall
|
||||
import org.jetbrains.kotlin.ir.expressions.IrExpression
|
||||
import org.jetbrains.kotlin.ir.expressions.IrStatementOrigin
|
||||
import org.jetbrains.kotlin.ir.expressions.impl.*
|
||||
import org.jetbrains.kotlin.ir.symbols.IrClassSymbol
|
||||
import org.jetbrains.kotlin.ir.symbols.IrFieldSymbol
|
||||
import org.jetbrains.kotlin.ir.symbols.IrPropertySymbol
|
||||
import org.jetbrains.kotlin.ir.symbols.IrValueSymbol
|
||||
import org.jetbrains.kotlin.ir.symbols.impl.*
|
||||
import org.jetbrains.kotlin.ir.types.*
|
||||
import org.jetbrains.kotlin.ir.util.*
|
||||
import org.jetbrains.kotlin.name.ClassId
|
||||
import org.jetbrains.kotlin.name.FqName
|
||||
import org.jetbrains.kotlin.name.Name
|
||||
import org.jetbrains.kotlin.resolve.DescriptorUtils
|
||||
import org.jetbrains.kotlin.resolve.descriptorUtil.isEffectivelyExternal
|
||||
import org.jetbrains.kotlin.types.KotlinType
|
||||
import org.jetbrains.kotlinx.serialization.compiler.extensions.SerializationPluginContext
|
||||
import org.jetbrains.kotlinx.serialization.compiler.resolve.SerialEntityNames
|
||||
|
||||
// This doesn't support annotation arguments of type KClass and Array<KClass> because the codegen doesn't compute JVM signatures for
|
||||
// such cases correctly (because inheriting from annotation classes is prohibited in Kotlin).
|
||||
// Currently it results in an "accidental override" error where a method with return type KClass conflicts with the one with Class.
|
||||
@OptIn(ObsoleteDescriptorBasedAPI::class)
|
||||
class SerialInfoImplJvmIrGenerator(
|
||||
private val context: SerializationPluginContext,
|
||||
private val moduleFragment: IrModuleFragment,
|
||||
) : IrBuilderWithPluginContext {
|
||||
override val compilerContext: SerializationPluginContext
|
||||
get() = context
|
||||
|
||||
private val jvmNameClass get() = context.referenceClass(ClassId.topLevel(DescriptorUtils.JVM_NAME))!!.owner
|
||||
|
||||
private val javaLangClass = createClass(createPackage("java.lang"), "Class", ClassKind.CLASS)
|
||||
private val javaLangType = javaLangClass.starProjectedType
|
||||
|
||||
private val implGenerated = mutableSetOf<IrClass>()
|
||||
private val annotationToImpl = mutableMapOf<IrClass, IrClass>()
|
||||
|
||||
fun getImplClass(serialInfoAnnotationClass: IrClass): IrClass =
|
||||
annotationToImpl.getOrPut(serialInfoAnnotationClass) {
|
||||
@OptIn(FirIncompatiblePluginAPI::class)
|
||||
val implClassSymbol = context.referenceClass(serialInfoAnnotationClass.kotlinFqName.child(SerialEntityNames.IMPL_NAME))
|
||||
implClassSymbol!!.owner.apply(this::generate)
|
||||
}
|
||||
|
||||
fun generate(irClass: IrClass) {
|
||||
if (!implGenerated.add(irClass)) return
|
||||
|
||||
val properties = irClass.declarations.filterIsInstance<IrProperty>()
|
||||
if (properties.isEmpty()) return
|
||||
|
||||
val startOffset = UNDEFINED_OFFSET
|
||||
val endOffset = UNDEFINED_OFFSET
|
||||
|
||||
val ctor = irClass.addConstructor {
|
||||
visibility = DescriptorVisibilities.PUBLIC
|
||||
}
|
||||
val ctorBody = context.irFactory.createBlockBody(
|
||||
startOffset, endOffset, listOf(
|
||||
IrDelegatingConstructorCallImpl(
|
||||
startOffset, endOffset, context.irBuiltIns.unitType, context.irBuiltIns.anyClass.constructors.single(),
|
||||
typeArgumentsCount = 0, valueArgumentsCount = 0
|
||||
)
|
||||
)
|
||||
)
|
||||
ctor.body = ctorBody
|
||||
|
||||
for (property in properties) {
|
||||
generateSimplePropertyWithBackingField(property.descriptor, irClass, Name.identifier("_" + property.name.asString()))
|
||||
|
||||
val getter = property.getter!!
|
||||
getter.origin = SERIALIZATION_PLUGIN_ORIGIN
|
||||
// Add JvmName annotation to property getters to force the resulting JVM method name for 'x' be 'x', instead of 'getX',
|
||||
// and to avoid having useless bridges for it generated in BridgeLowering.
|
||||
// Unfortunately, this results in an extra `@JvmName` annotation in the bytecode, but it shouldn't matter very much.
|
||||
getter.annotations += jvmName(property.name.asString())
|
||||
|
||||
val field = property.backingField!!
|
||||
field.visibility = DescriptorVisibilities.PRIVATE
|
||||
field.origin = SERIALIZATION_PLUGIN_ORIGIN
|
||||
|
||||
val parameter = ctor.addValueParameter(property.name.asString(), field.type)
|
||||
ctorBody.statements += IrSetFieldImpl(
|
||||
startOffset, endOffset, field.symbol,
|
||||
IrGetValueImpl(startOffset, endOffset, irClass.thisReceiver!!.symbol),
|
||||
IrGetValueImpl(startOffset, endOffset, parameter.symbol),
|
||||
context.irBuiltIns.unitType,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private fun jvmName(name: String): IrConstructorCall =
|
||||
IrConstructorCallImpl(
|
||||
UNDEFINED_OFFSET, UNDEFINED_OFFSET, jvmNameClass.defaultType, jvmNameClass.constructors.single().symbol,
|
||||
typeArgumentsCount = 0, constructorTypeArgumentsCount = 0, valueArgumentsCount = 1,
|
||||
).apply {
|
||||
putValueArgument(0, IrConstImpl.string(UNDEFINED_OFFSET, UNDEFINED_OFFSET, context.irBuiltIns.stringType, name))
|
||||
}
|
||||
|
||||
@FirIncompatiblePluginAPI
|
||||
fun KotlinType.toIrType() = compilerContext.typeTranslator.translateType(this)
|
||||
|
||||
private fun IrType.kClassToJClassIfNeeded(): IrType = when {
|
||||
this.isKClass() -> javaLangType
|
||||
this.isKClassArray() -> compilerContext.irBuiltIns.arrayClass.typeWith(javaLangType)
|
||||
else -> this
|
||||
}
|
||||
|
||||
private fun kClassExprToJClassIfNeeded(startOffset: Int, endOffset: Int, irExpression: IrExpression): IrExpression {
|
||||
val getterSymbol = kClassJava.owner.getter!!.symbol
|
||||
return IrCallImpl(
|
||||
startOffset, endOffset,
|
||||
javaLangClass.starProjectedType,
|
||||
getterSymbol,
|
||||
typeArgumentsCount = getterSymbol.owner.typeParameters.size,
|
||||
valueArgumentsCount = 0,
|
||||
origin = IrStatementOrigin.GET_PROPERTY
|
||||
).apply {
|
||||
this.extensionReceiver = irExpression
|
||||
}
|
||||
}
|
||||
|
||||
private val jvmName: IrClassSymbol = createClass(createPackage("kotlin.jvm"), "JvmName", ClassKind.ANNOTATION_CLASS) { klass ->
|
||||
klass.addConstructor().apply {
|
||||
addValueParameter("name", context.irBuiltIns.stringType)
|
||||
}
|
||||
}
|
||||
|
||||
private val kClassJava: IrPropertySymbol =
|
||||
IrFactoryImpl.buildProperty {
|
||||
name = Name.identifier("java")
|
||||
}.apply {
|
||||
parent = createClass(createPackage("kotlin.jvm"), "JvmClassMappingKt", ClassKind.CLASS).owner
|
||||
addGetter().apply {
|
||||
annotations = listOf(
|
||||
IrConstructorCallImpl.fromSymbolOwner(jvmName.typeWith(), jvmName.constructors.single()).apply {
|
||||
putValueArgument(
|
||||
0,
|
||||
IrConstImpl.string(
|
||||
UNDEFINED_OFFSET,
|
||||
UNDEFINED_OFFSET,
|
||||
context.irBuiltIns.stringType,
|
||||
"getJavaClass"
|
||||
)
|
||||
)
|
||||
}
|
||||
)
|
||||
addExtensionReceiver(context.irBuiltIns.kClassClass.starProjectedType)
|
||||
returnType = javaLangClass.starProjectedType
|
||||
}
|
||||
}.symbol
|
||||
|
||||
private fun IrType.isKClassArray() =
|
||||
this is IrSimpleType && isArray() && arguments.single().typeOrNull?.isKClass() == true
|
||||
|
||||
private fun createPackage(packageName: String): IrPackageFragment =
|
||||
IrExternalPackageFragmentImpl.createEmptyExternalPackageFragment(
|
||||
moduleFragment.descriptor,
|
||||
FqName(packageName)
|
||||
)
|
||||
|
||||
private fun createClass(
|
||||
irPackage: IrPackageFragment,
|
||||
shortName: String,
|
||||
classKind: ClassKind,
|
||||
block: (IrClass) -> Unit = {}
|
||||
): IrClassSymbol = IrFactoryImpl.buildClass {
|
||||
name = Name.identifier(shortName)
|
||||
kind = classKind
|
||||
modality = Modality.FINAL
|
||||
}.apply {
|
||||
parent = irPackage
|
||||
createImplicitParameterDeclarationWithWrappedDescriptor()
|
||||
block(this)
|
||||
}.symbol
|
||||
|
||||
private inline fun <reified T : IrDeclaration> IrClass.searchForDeclaration(descriptor: DeclarationDescriptor): T? {
|
||||
return declarations.singleOrNull { it.descriptor == descriptor } as? T
|
||||
}
|
||||
|
||||
private fun generateSimplePropertyWithBackingField(
|
||||
propertyDescriptor: PropertyDescriptor,
|
||||
propertyParent: IrClass,
|
||||
fieldName: Name = propertyDescriptor.name,
|
||||
): IrProperty {
|
||||
val irProperty = propertyParent.searchForDeclaration(propertyDescriptor) ?: run {
|
||||
with(propertyDescriptor) {
|
||||
propertyParent.factory.createProperty(
|
||||
propertyParent.startOffset,
|
||||
propertyParent.endOffset,
|
||||
SERIALIZATION_PLUGIN_ORIGIN,
|
||||
IrPropertySymbolImpl(propertyDescriptor),
|
||||
name,
|
||||
visibility,
|
||||
modality,
|
||||
isVar,
|
||||
isConst,
|
||||
isLateInit,
|
||||
isDelegated,
|
||||
isExternal
|
||||
).also {
|
||||
it.parent = propertyParent
|
||||
propertyParent.addMember(it)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
propertyParent.generatePropertyBackingFieldIfNeeded(propertyDescriptor, irProperty, fieldName)
|
||||
val fieldSymbol = irProperty.backingField!!.symbol
|
||||
irProperty.getter = propertyDescriptor.getter?.let {
|
||||
propertyParent.generatePropertyAccessor(propertyDescriptor, irProperty, it, fieldSymbol, isGetter = true)
|
||||
}?.apply { parent = propertyParent }
|
||||
irProperty.setter = propertyDescriptor.setter?.let {
|
||||
propertyParent.generatePropertyAccessor(propertyDescriptor, irProperty, it, fieldSymbol, isGetter = false)
|
||||
}?.apply { parent = propertyParent }
|
||||
return irProperty
|
||||
}
|
||||
|
||||
private fun IrClass.generatePropertyBackingFieldIfNeeded(
|
||||
propertyDescriptor: PropertyDescriptor,
|
||||
originProperty: IrProperty,
|
||||
name: Name,
|
||||
) {
|
||||
if (originProperty.backingField != null) return
|
||||
|
||||
val field = with(propertyDescriptor) {
|
||||
@OptIn(FirIncompatiblePluginAPI::class)// should be called only with old FE
|
||||
originProperty.factory.createField(
|
||||
originProperty.startOffset,
|
||||
originProperty.endOffset,
|
||||
SERIALIZATION_PLUGIN_ORIGIN,
|
||||
IrFieldSymbolImpl(propertyDescriptor),
|
||||
name,
|
||||
type.toIrType(),
|
||||
visibility,
|
||||
!isVar,
|
||||
isEffectivelyExternal(),
|
||||
dispatchReceiverParameter == null
|
||||
)
|
||||
}
|
||||
field.apply {
|
||||
parent = this@generatePropertyBackingFieldIfNeeded
|
||||
correspondingPropertySymbol = originProperty.symbol
|
||||
}
|
||||
|
||||
originProperty.backingField = field
|
||||
}
|
||||
|
||||
private fun IrClass.generatePropertyAccessor(
|
||||
propertyDescriptor: PropertyDescriptor,
|
||||
property: IrProperty,
|
||||
descriptor: PropertyAccessorDescriptor,
|
||||
fieldSymbol: IrFieldSymbol,
|
||||
isGetter: Boolean,
|
||||
): IrSimpleFunction {
|
||||
val irAccessor: IrSimpleFunction = when (isGetter) {
|
||||
true -> searchForDeclaration<IrProperty>(propertyDescriptor)?.getter
|
||||
false -> searchForDeclaration<IrProperty>(propertyDescriptor)?.setter
|
||||
} ?: run {
|
||||
with(descriptor) {
|
||||
@OptIn(FirIncompatiblePluginAPI::class) // should never be called after FIR frontend
|
||||
property.factory.createFunction(
|
||||
fieldSymbol.owner.startOffset,
|
||||
fieldSymbol.owner.endOffset,
|
||||
SERIALIZATION_PLUGIN_ORIGIN, IrSimpleFunctionSymbolImpl(descriptor),
|
||||
name, visibility, modality, returnType!!.toIrType(),
|
||||
isInline, isEffectivelyExternal(), isTailrec, isSuspend, isOperator, isInfix, isExpect
|
||||
)
|
||||
}.also { f ->
|
||||
generateOverriddenFunctionSymbols(f, compilerContext.symbolTable)
|
||||
f.createParameterDeclarations(descriptor)
|
||||
@OptIn(FirIncompatiblePluginAPI::class) // should never be called after FIR frontend
|
||||
f.returnType = descriptor.returnType!!.toIrType()
|
||||
f.correspondingPropertySymbol = fieldSymbol.owner.correspondingPropertySymbol
|
||||
}
|
||||
}
|
||||
|
||||
irAccessor.body = when (isGetter) {
|
||||
true -> generateDefaultGetterBody(irAccessor)
|
||||
false -> generateDefaultSetterBody(irAccessor)
|
||||
}
|
||||
|
||||
return irAccessor
|
||||
}
|
||||
|
||||
private fun generateDefaultGetterBody(
|
||||
irAccessor: IrSimpleFunction
|
||||
): IrBlockBody {
|
||||
val irProperty =
|
||||
irAccessor.correspondingPropertySymbol?.owner ?: error("Expected corresponding property for accessor ${irAccessor.render()}")
|
||||
|
||||
val startOffset = irAccessor.startOffset
|
||||
val endOffset = irAccessor.endOffset
|
||||
val irBody = irAccessor.factory.createBlockBody(startOffset, endOffset)
|
||||
|
||||
val receiver = generateReceiverExpressionForFieldAccess(irAccessor.dispatchReceiverParameter!!.symbol)
|
||||
|
||||
val propertyIrType = irAccessor.returnType
|
||||
irBody.statements.add(
|
||||
IrReturnImpl(
|
||||
startOffset, endOffset, compilerContext.irBuiltIns.nothingType,
|
||||
irAccessor.symbol,
|
||||
IrGetFieldImpl(
|
||||
startOffset, endOffset,
|
||||
irProperty.backingField?.symbol ?: error("Property expected to have backing field"),
|
||||
propertyIrType,
|
||||
receiver
|
||||
).let {
|
||||
if (propertyIrType.isKClass()) {
|
||||
irAccessor.returnType = irAccessor.returnType.kClassToJClassIfNeeded()
|
||||
kClassExprToJClassIfNeeded(startOffset, endOffset, it)
|
||||
} else it
|
||||
}
|
||||
)
|
||||
)
|
||||
return irBody
|
||||
}
|
||||
|
||||
private fun generateDefaultSetterBody(
|
||||
irAccessor: IrSimpleFunction
|
||||
): IrBlockBody {
|
||||
val irProperty =
|
||||
irAccessor.correspondingPropertySymbol?.owner ?: error("Expected corresponding property for accessor ${irAccessor.render()}")
|
||||
val startOffset = irAccessor.startOffset
|
||||
val endOffset = irAccessor.endOffset
|
||||
val irBody = irAccessor.factory.createBlockBody(startOffset, endOffset)
|
||||
|
||||
val receiver = generateReceiverExpressionForFieldAccess(irAccessor.dispatchReceiverParameter!!.symbol)
|
||||
|
||||
val irValueParameter = irAccessor.valueParameters.single()
|
||||
irBody.statements.add(
|
||||
IrSetFieldImpl(
|
||||
startOffset, endOffset,
|
||||
irProperty.backingField?.symbol ?: error("Property ${irProperty.render()} expected to have backing field"),
|
||||
receiver,
|
||||
IrGetValueImpl(startOffset, endOffset, irValueParameter.type, irValueParameter.symbol),
|
||||
compilerContext.irBuiltIns.unitType
|
||||
)
|
||||
)
|
||||
return irBody
|
||||
}
|
||||
|
||||
private fun generateReceiverExpressionForFieldAccess(
|
||||
ownerSymbol: IrValueSymbol
|
||||
): IrExpression = IrGetValueImpl(
|
||||
ownerSymbol.owner.startOffset, ownerSymbol.owner.endOffset,
|
||||
ownerSymbol
|
||||
)
|
||||
|
||||
private fun IrFunction.createParameterDeclarations(
|
||||
descriptor: FunctionDescriptor,
|
||||
overwriteValueParameters: Boolean = false,
|
||||
copyTypeParameters: Boolean = true
|
||||
) {
|
||||
val function = this
|
||||
fun irValueParameter(descriptor: ParameterDescriptor): IrValueParameter = with(descriptor) {
|
||||
@OptIn(FirIncompatiblePluginAPI::class) // should never be called after FIR frontend
|
||||
factory.createValueParameter(
|
||||
function.startOffset, function.endOffset, SERIALIZATION_PLUGIN_ORIGIN, IrValueParameterSymbolImpl(this),
|
||||
name, indexOrMinusOne, type.toIrType(), varargElementType?.toIrType(), isCrossinline, isNoinline,
|
||||
isHidden = false, isAssignable = false
|
||||
).also {
|
||||
it.parent = function
|
||||
}
|
||||
}
|
||||
|
||||
if (copyTypeParameters) {
|
||||
assert(typeParameters.isEmpty())
|
||||
copyTypeParamsFromDescriptor(descriptor)
|
||||
}
|
||||
|
||||
dispatchReceiverParameter = descriptor.dispatchReceiverParameter?.let { irValueParameter(it) }
|
||||
extensionReceiverParameter = descriptor.extensionReceiverParameter?.let { irValueParameter(it) }
|
||||
|
||||
if (!overwriteValueParameters)
|
||||
assert(valueParameters.isEmpty())
|
||||
|
||||
valueParameters = descriptor.valueParameters.map { irValueParameter(it) }
|
||||
}
|
||||
|
||||
private fun IrFunction.copyTypeParamsFromDescriptor(descriptor: FunctionDescriptor) {
|
||||
val newTypeParameters = descriptor.typeParameters.map {
|
||||
factory.createTypeParameter(
|
||||
startOffset, endOffset,
|
||||
SERIALIZATION_PLUGIN_ORIGIN,
|
||||
IrTypeParameterSymbolImpl(it),
|
||||
it.name, it.index, it.isReified, it.variance
|
||||
).also { typeParameter ->
|
||||
typeParameter.parent = this
|
||||
}
|
||||
}
|
||||
@OptIn(FirIncompatiblePluginAPI::class) // should never be called after FIR frontend
|
||||
newTypeParameters.forEach { typeParameter ->
|
||||
typeParameter.superTypes = typeParameter.descriptor.upperBounds.map { it.toIrType() }
|
||||
}
|
||||
|
||||
typeParameters = newTypeParameters
|
||||
}
|
||||
}
|
||||
+184
@@ -0,0 +1,184 @@
|
||||
/*
|
||||
* Copyright 2010-2020 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlinx.serialization.compiler.backend.ir
|
||||
|
||||
import org.jetbrains.kotlin.descriptors.ClassKind
|
||||
import org.jetbrains.kotlin.ir.builders.IrBuilderWithScope
|
||||
import org.jetbrains.kotlin.ir.builders.irGet
|
||||
import org.jetbrains.kotlin.ir.builders.irInt
|
||||
import org.jetbrains.kotlin.ir.builders.irReturn
|
||||
import org.jetbrains.kotlin.ir.declarations.IrClass
|
||||
import org.jetbrains.kotlin.ir.declarations.IrSimpleFunction
|
||||
import org.jetbrains.kotlin.ir.expressions.IrExpression
|
||||
import org.jetbrains.kotlin.ir.expressions.impl.IrConstructorCallImpl
|
||||
import org.jetbrains.kotlin.ir.expressions.impl.IrGetValueImpl
|
||||
import org.jetbrains.kotlin.ir.types.defaultType
|
||||
import org.jetbrains.kotlin.ir.util.*
|
||||
import org.jetbrains.kotlin.name.ClassId
|
||||
import org.jetbrains.kotlin.name.Name
|
||||
import org.jetbrains.kotlin.resolve.descriptorUtil.fqNameSafe
|
||||
import org.jetbrains.kotlinx.serialization.compiler.extensions.SerializationPluginContext
|
||||
import org.jetbrains.kotlinx.serialization.compiler.resolve.SerialEntityNames
|
||||
import org.jetbrains.kotlinx.serialization.compiler.resolve.SerializationPackages
|
||||
import org.jetbrains.kotlinx.serialization.compiler.resolve.needSerializerFactory
|
||||
|
||||
class SerializableCompanionIrGenerator(
|
||||
val irClass: IrClass,
|
||||
val serializableIrClass: IrClass,
|
||||
compilerContext: SerializationPluginContext,
|
||||
) : BaseIrGenerator(irClass, compilerContext) {
|
||||
|
||||
private fun getSerializerGetterFunction(): IrSimpleFunction {
|
||||
return irClass.findDeclaration<IrSimpleFunction> {
|
||||
(it.valueParameters.size == serializableIrClass.typeParameters.size
|
||||
&& it.valueParameters.all { p -> p.type.isKSerializer() }) && it.returnType.isKSerializer()
|
||||
} ?: throw IllegalStateException(
|
||||
"Can't find synthesized 'Companion.serializer()' function to generate, " +
|
||||
"probably clash with user-defined function has occurred"
|
||||
)
|
||||
}
|
||||
|
||||
fun generate() {
|
||||
val serializerGetterFunction = getSerializerGetterFunction()
|
||||
|
||||
if (serializableIrClass.isSerializableObject
|
||||
|| serializableIrClass.isAbstractOrSealedSerializableClass
|
||||
|| serializableIrClass.isSerializableEnum()
|
||||
) {
|
||||
generateLazySerializerGetter(serializerGetterFunction)
|
||||
} else {
|
||||
generateSerializerGetter(serializerGetterFunction)
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
fun generate(
|
||||
irClass: IrClass,
|
||||
context: SerializationPluginContext,
|
||||
) {
|
||||
val companionDescriptor = irClass
|
||||
val serializableClass = getSerializableClassByCompanion(companionDescriptor) ?: return
|
||||
if (serializableClass.shouldHaveGeneratedMethodsInCompanion) {
|
||||
SerializableCompanionIrGenerator(irClass, getSerializableClassByCompanion(irClass)!!, context).generate()
|
||||
irClass.addDefaultConstructorIfAbsent(context)
|
||||
irClass.patchDeclarationParents(irClass.parent)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun IrBuilderWithScope.patchSerializableClassWithMarkerAnnotation(serializer: IrClass) {
|
||||
if (serializer.kind != ClassKind.OBJECT) {
|
||||
return
|
||||
}
|
||||
|
||||
val annotationMarkerClass = compilerContext.referenceClass(
|
||||
ClassId(
|
||||
SerializationPackages.packageFqName,
|
||||
Name.identifier(SerialEntityNames.ANNOTATION_MARKER_CLASS)
|
||||
)
|
||||
) ?: return
|
||||
|
||||
val irSerializableClass = if (irClass.isCompanion) irClass.parentAsClass else irClass
|
||||
val serializableWithAlreadyPresent = irSerializableClass.annotations.any {
|
||||
it.constructedClass.fqNameWhenAvailable == annotationMarkerClass.owner.fqNameWhenAvailable
|
||||
}
|
||||
if (serializableWithAlreadyPresent) return
|
||||
|
||||
val annotationCtor = annotationMarkerClass.constructors.single { it.owner.isPrimary }
|
||||
val annotationType = annotationMarkerClass.defaultType
|
||||
|
||||
val annotationCtorCall = IrConstructorCallImpl.fromSymbolOwner(startOffset, endOffset, annotationType, annotationCtor).apply {
|
||||
putValueArgument(
|
||||
0,
|
||||
createClassReference(
|
||||
serializer.defaultType,
|
||||
startOffset,
|
||||
endOffset
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
irSerializableClass.annotations += annotationCtorCall
|
||||
}
|
||||
|
||||
fun generateLazySerializerGetter(methodDescriptor: IrSimpleFunction) {
|
||||
val serializer = requireNotNull(
|
||||
findTypeSerializer(
|
||||
compilerContext,
|
||||
serializableIrClass.defaultType
|
||||
)
|
||||
)
|
||||
|
||||
val kSerializerIrClass = compilerContext.referenceClass(ClassId(SerializationPackages.packageFqName, SerialEntityNames.KSERIALIZER_NAME))!!.owner
|
||||
val targetIrType =
|
||||
kSerializerIrClass.defaultType.substitute(mapOf(kSerializerIrClass.typeParameters[0].symbol to compilerContext.irBuiltIns.anyType))
|
||||
|
||||
val property = createLazyProperty(irClass, targetIrType, SerialEntityNames.CACHED_SERIALIZER_PROPERTY_NAME) {
|
||||
val expr = serializerInstance(
|
||||
serializer, compilerContext, serializableIrClass.defaultType
|
||||
)
|
||||
patchSerializableClassWithMarkerAnnotation(kSerializerIrClass)
|
||||
+irReturn(requireNotNull(expr))
|
||||
}
|
||||
|
||||
addFunctionBody(methodDescriptor) {
|
||||
+irReturn(getLazyValueExpression(it.dispatchReceiverParameter!!, property, targetIrType))
|
||||
}
|
||||
generateSerializerFactoryIfNeeded(methodDescriptor)
|
||||
}
|
||||
|
||||
fun generateSerializerGetter(methodDescriptor: IrSimpleFunction) {
|
||||
addFunctionBody(methodDescriptor) { getter ->
|
||||
val serializer = requireNotNull(
|
||||
findTypeSerializer(
|
||||
compilerContext,
|
||||
serializableIrClass.defaultType
|
||||
)
|
||||
)
|
||||
val args: List<IrExpression> = getter.valueParameters.map { irGet(it) }
|
||||
val expr = serializerInstance(
|
||||
serializer, compilerContext,
|
||||
serializableIrClass.defaultType
|
||||
) { it, _ -> args[it] }
|
||||
patchSerializableClassWithMarkerAnnotation(serializer.owner)
|
||||
+irReturn(requireNotNull(expr))
|
||||
}
|
||||
generateSerializerFactoryIfNeeded(methodDescriptor)
|
||||
}
|
||||
|
||||
private fun generateSerializerFactoryIfNeeded(getterDescriptor: IrSimpleFunction) {
|
||||
if (!irClass.needSerializerFactory(compilerContext)) return
|
||||
val serialFactoryDescriptor = irClass.findDeclaration<IrSimpleFunction> {
|
||||
it.valueParameters.size == 1
|
||||
&& it.valueParameters.first().isVararg
|
||||
&& it.returnType.isKSerializer()
|
||||
&& it.isFromPlugin()
|
||||
} ?: return
|
||||
addFunctionBody(serialFactoryDescriptor) { factory ->
|
||||
val kSerializerStarType = factory.returnType
|
||||
val array = factory.valueParameters.first()
|
||||
val argsSize = serializableIrClass.typeParameters.size
|
||||
val arrayGet = compilerContext.irBuiltIns.arrayClass.owner.declarations.filterIsInstance<IrSimpleFunction>()
|
||||
.single { it.name.asString() == "get" }
|
||||
|
||||
val serializers: List<IrExpression> = (0 until argsSize).map {
|
||||
irInvoke(irGet(array), arrayGet.symbol, irInt(it), typeHint = kSerializerStarType)
|
||||
}
|
||||
val serializerCall = getterDescriptor.symbol
|
||||
val call = irInvoke(
|
||||
IrGetValueImpl(startOffset, endOffset, factory.dispatchReceiverParameter!!.symbol),
|
||||
serializerCall,
|
||||
List(argsSize) { compilerContext.irBuiltIns.anyNType },
|
||||
serializers,
|
||||
returnTypeHint = kSerializerStarType
|
||||
)
|
||||
+irReturn(call)
|
||||
patchSerializableClassWithMarkerAnnotation(irClass)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+397
@@ -0,0 +1,397 @@
|
||||
/*
|
||||
* Copyright 2010-2021 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlinx.serialization.compiler.backend.ir
|
||||
|
||||
import org.jetbrains.kotlin.backend.common.lower.irThrow
|
||||
import org.jetbrains.kotlin.descriptors.ClassKind
|
||||
import org.jetbrains.kotlin.descriptors.ValueParameterDescriptor
|
||||
import org.jetbrains.kotlin.ir.IrStatement
|
||||
import org.jetbrains.kotlin.ir.builders.*
|
||||
import org.jetbrains.kotlin.ir.declarations.*
|
||||
import org.jetbrains.kotlin.ir.deepCopyWithVariables
|
||||
import org.jetbrains.kotlin.ir.expressions.IrExpression
|
||||
import org.jetbrains.kotlin.ir.expressions.IrExpressionBody
|
||||
import org.jetbrains.kotlin.ir.expressions.IrStatementOrigin
|
||||
import org.jetbrains.kotlin.ir.expressions.impl.IrDelegatingConstructorCallImpl
|
||||
import org.jetbrains.kotlin.ir.types.*
|
||||
import org.jetbrains.kotlin.ir.util.*
|
||||
import org.jetbrains.kotlin.name.ClassId
|
||||
import org.jetbrains.kotlin.name.Name
|
||||
import org.jetbrains.kotlin.util.OperatorNameConventions
|
||||
import org.jetbrains.kotlin.utils.getOrPutNullable
|
||||
import org.jetbrains.kotlinx.serialization.compiler.extensions.SerializationPluginContext
|
||||
import org.jetbrains.kotlinx.serialization.compiler.resolve.*
|
||||
import org.jetbrains.kotlinx.serialization.compiler.resolve.SerialEntityNames.CACHED_DESCRIPTOR_FIELD_NAME
|
||||
import org.jetbrains.kotlinx.serialization.compiler.resolve.SerialEntityNames.LOAD
|
||||
import org.jetbrains.kotlinx.serialization.compiler.resolve.SerialEntityNames.MISSING_FIELD_EXC
|
||||
import org.jetbrains.kotlinx.serialization.compiler.resolve.SerialEntityNames.SAVE
|
||||
import org.jetbrains.kotlinx.serialization.compiler.resolve.SerialEntityNames.SERIAL_DESC_FIELD
|
||||
|
||||
class SerializableIrGenerator(
|
||||
val irClass: IrClass,
|
||||
compilerContext: SerializationPluginContext
|
||||
) : BaseIrGenerator(irClass, compilerContext) {
|
||||
|
||||
protected val properties = serializablePropertiesForIrBackend(irClass)
|
||||
|
||||
private val serialDescriptorClass = compilerContext.referenceClass(
|
||||
ClassId(
|
||||
SerializationPackages.descriptorsPackageFqName,
|
||||
Name.identifier(SerialEntityNames.SERIAL_DESCRIPTOR_CLASS)
|
||||
)
|
||||
)!!.owner
|
||||
|
||||
private val serialDescriptorImplClass = compilerContext.referenceClass(
|
||||
ClassId(
|
||||
SerializationPackages.internalPackageFqName,
|
||||
Name.identifier(SerialEntityNames.SERIAL_DESCRIPTOR_CLASS_IMPL)
|
||||
)
|
||||
)!!.owner
|
||||
|
||||
private val addElementFun =
|
||||
serialDescriptorImplClass.findDeclaration<IrFunction> { it.name.toString() == CallingConventions.addElement }!!.symbol
|
||||
|
||||
private val IrClass.isInternalSerializable: Boolean get() = kind == ClassKind.CLASS && hasSerializableOrMetaAnnotationWithoutArgs()
|
||||
|
||||
fun generateInternalConstructor(constructorDescriptor: IrConstructor) =
|
||||
addFunctionBody(constructorDescriptor) { ctor ->
|
||||
val thiz = irClass.thisReceiver!!
|
||||
val serializableProperties = properties.serializableProperties
|
||||
|
||||
val serialDescs = serializableProperties.map { it.ir }.toSet()
|
||||
|
||||
val propertyByParamReplacer: (ValueParameterDescriptor) -> IrExpression? =
|
||||
createPropertyByParamReplacer(irClass, serializableProperties, thiz)
|
||||
|
||||
val initializerAdapter: (IrExpressionBody) -> IrExpression = createInitializerAdapter(irClass, propertyByParamReplacer)
|
||||
|
||||
|
||||
var current: IrProperty? = null
|
||||
val statementsAfterSerializableProperty: MutableMap<IrProperty?, MutableList<IrStatement>> = mutableMapOf()
|
||||
irClass.declarations.asSequence().forEach {
|
||||
when {
|
||||
// only properties with backing field
|
||||
it is IrProperty && it.backingField != null -> {
|
||||
if (it in serialDescs) {
|
||||
current = it
|
||||
} else if (it.backingField?.initializer != null) {
|
||||
// skip transient lateinit or deferred properties (with null initializer)
|
||||
val expression = initializerAdapter(it.backingField!!.initializer!!)
|
||||
|
||||
statementsAfterSerializableProperty.getOrPutNullable(current, { mutableListOf() })
|
||||
.add(irSetField(irGet(thiz), it.backingField!!, expression))
|
||||
}
|
||||
}
|
||||
it is IrAnonymousInitializer -> {
|
||||
val statements = it.body.deepCopyWithVariables().statements
|
||||
statementsAfterSerializableProperty.getOrPutNullable(current, { mutableListOf() })
|
||||
.addAll(statements)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Missing field exception parts
|
||||
val exceptionCtorRef =
|
||||
compilerContext.referenceConstructors(ClassId(SerializationPackages.packageFqName, Name.identifier(MISSING_FIELD_EXC)))
|
||||
.single { it.owner.valueParameters.singleOrNull()?.type?.isString() == true }
|
||||
val exceptionType = exceptionCtorRef.owner.returnType
|
||||
|
||||
val seenVarsOffset = serializableProperties.bitMaskSlotCount()
|
||||
val seenVars = (0 until seenVarsOffset).map { ctor.valueParameters[it] }
|
||||
|
||||
|
||||
val superClass = irClass.getSuperClassOrAny()
|
||||
var startPropOffset: Int = 0
|
||||
|
||||
|
||||
if (useFieldMissingOptimization() &&
|
||||
// for abstract classes fields MUST BE checked in child classes
|
||||
!irClass.isAbstractOrSealedSerializableClass
|
||||
) {
|
||||
val getDescriptorExpr = if (irClass.isStaticSerializable) {
|
||||
getStaticSerialDescriptorExpr()
|
||||
} else {
|
||||
// synthetic constructor is created only for internally serializable classes - so companion definitely exists
|
||||
val companionObject = irClass.companionObject()!!
|
||||
getParametrizedSerialDescriptorExpr(companionObject, createCachedDescriptorProperty(companionObject))
|
||||
}
|
||||
generateGoldenMaskCheck(seenVars, properties, getDescriptorExpr)
|
||||
}
|
||||
when {
|
||||
superClass.symbol == compilerContext.irBuiltIns.anyClass -> generateAnySuperConstructorCall(toBuilder = this@addFunctionBody)
|
||||
superClass.isInternalSerializable -> {
|
||||
startPropOffset = generateSuperSerializableCall(superClass, ctor.valueParameters, seenVarsOffset)
|
||||
}
|
||||
else -> generateSuperNonSerializableCall(superClass)
|
||||
}
|
||||
|
||||
statementsAfterSerializableProperty[null]?.forEach { +it }
|
||||
for (index in startPropOffset until serializableProperties.size) {
|
||||
val prop = serializableProperties[index]
|
||||
val paramRef = ctor.valueParameters[index + seenVarsOffset]
|
||||
// Assign this.a = a in else branch
|
||||
// Set field directly w/o setter to match behavior of old backend plugin
|
||||
val backingFieldToAssign = prop.ir.backingField!!
|
||||
val assignParamExpr = irSetField(irGet(thiz), backingFieldToAssign, irGet(paramRef))
|
||||
|
||||
val ifNotSeenExpr: IrExpression = if (prop.optional) {
|
||||
val initializerBody =
|
||||
requireNotNull(initializerAdapter(prop.ir.backingField?.initializer!!)) { "Optional value without an initializer" } // todo: filter abstract here
|
||||
irSetField(irGet(thiz), backingFieldToAssign, initializerBody)
|
||||
} else {
|
||||
// property required
|
||||
if (useFieldMissingOptimization()) {
|
||||
// field definitely not empty as it's checked before - no need another IF, only assign property from param
|
||||
+assignParamExpr
|
||||
statementsAfterSerializableProperty[prop.ir]?.forEach { +it }
|
||||
continue
|
||||
} else {
|
||||
irThrow(irInvoke(null, exceptionCtorRef, irString(prop.name), typeHint = exceptionType))
|
||||
}
|
||||
}
|
||||
|
||||
val propNotSeenTest =
|
||||
irEquals(
|
||||
irInt(0),
|
||||
irBinOp(
|
||||
OperatorNameConventions.AND,
|
||||
irGet(seenVars[bitMaskSlotAt(index)]),
|
||||
irInt(1 shl (index % 32))
|
||||
)
|
||||
)
|
||||
|
||||
+irIfThenElse(compilerContext.irBuiltIns.unitType, propNotSeenTest, ifNotSeenExpr, assignParamExpr)
|
||||
|
||||
statementsAfterSerializableProperty[prop.ir]?.forEach { +it }
|
||||
}
|
||||
|
||||
// Handle function-intialized interface delegates
|
||||
irClass.declarations
|
||||
.filterIsInstance<IrField>()
|
||||
.filter { it.origin == IrDeclarationOrigin.DELEGATE }
|
||||
.forEach {
|
||||
val receiver = if (!it.isStatic) irGet(thiz) else null
|
||||
+irSetField(
|
||||
receiver,
|
||||
it,
|
||||
initializerAdapter(it.initializer!!),
|
||||
IrStatementOrigin.INITIALIZE_FIELD
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private fun IrBlockBodyBuilder.getStaticSerialDescriptorExpr(): IrExpression {
|
||||
val serializerIrClass = irClass.classSerializer(compilerContext)!!.owner
|
||||
// internally generated serializer always declared inside serializable class
|
||||
|
||||
val serialDescriptorGetter =
|
||||
serializerIrClass.getPropertyGetter(SERIAL_DESC_FIELD)!!
|
||||
return irGet(
|
||||
serializerIrClass.defaultType,
|
||||
irGetObject(serializerIrClass),
|
||||
serialDescriptorGetter.owner.symbol
|
||||
)
|
||||
}
|
||||
|
||||
private fun IrBlockBodyBuilder.getParametrizedSerialDescriptorExpr(companionObject: IrClass, property: IrProperty): IrExpression {
|
||||
return irGetField(irGetObject(companionObject), property.backingField!!)
|
||||
}
|
||||
|
||||
private fun IrBlockBodyBuilder.createCachedDescriptorProperty(companionObject: IrClass): IrProperty {
|
||||
val serialDescIrType = serialDescriptorClass.defaultType
|
||||
|
||||
return createCompanionValProperty(companionObject, serialDescIrType, CACHED_DESCRIPTOR_FIELD_NAME) {
|
||||
val serialDescVar = irTemporary(
|
||||
getInstantiateDescriptorExpr(),
|
||||
nameHint = "serialDesc"
|
||||
)
|
||||
for (property in properties.serializableProperties) {
|
||||
+getAddElementToDescriptorExpr(property, serialDescVar)
|
||||
}
|
||||
+irGet(serialDescVar)
|
||||
}
|
||||
}
|
||||
|
||||
private fun IrBlockBodyBuilder.getInstantiateDescriptorExpr(): IrExpression {
|
||||
val classConstructors = serialDescriptorImplClass.constructors
|
||||
val serialClassDescImplCtor = classConstructors.single { it.isPrimary }.symbol
|
||||
return irInvoke(
|
||||
null, serialClassDescImplCtor,
|
||||
irString(irClass.serialName()), irNull(), irInt(properties.serializableProperties.size)
|
||||
)
|
||||
}
|
||||
|
||||
private fun IrBlockBodyBuilder.getAddElementToDescriptorExpr(
|
||||
property: IrSerializableProperty,
|
||||
serialDescVar: IrVariable
|
||||
): IrExpression {
|
||||
return irInvoke(
|
||||
irGet(serialDescVar),
|
||||
addElementFun,
|
||||
irString(property.name),
|
||||
irBoolean(property.optional),
|
||||
typeHint = compilerContext.irBuiltIns.unitType
|
||||
)
|
||||
}
|
||||
|
||||
private fun IrBlockBodyBuilder.generateSuperNonSerializableCall(superClass: IrClass) {
|
||||
val ctorRef = superClass.declarations.filterIsInstance<IrConstructor>().singleOrNull { it.valueParameters.isEmpty() }
|
||||
?: error("Non-serializable parent of serializable $irClass must have no arg constructor")
|
||||
|
||||
|
||||
val call = IrDelegatingConstructorCallImpl.fromSymbolOwner(
|
||||
startOffset,
|
||||
endOffset,
|
||||
compilerContext.irBuiltIns.unitType,
|
||||
ctorRef.symbol
|
||||
)
|
||||
call.insertTypeArgumentsForSuperClass(superClass)
|
||||
+call
|
||||
}
|
||||
|
||||
private fun IrDelegatingConstructorCallImpl.insertTypeArgumentsForSuperClass(superClass: IrClass) {
|
||||
val superTypeCallArguments = (irClass.superTypes.find { it.classOrNull == superClass.symbol } as IrSimpleType?)?.arguments
|
||||
superTypeCallArguments?.forEachIndexed { index, irTypeArgument ->
|
||||
val argType =
|
||||
irTypeArgument as? IrTypeProjection ?: throw IllegalStateException("Star projection in immediate argument for supertype")
|
||||
putTypeArgument(index, argType.type)
|
||||
}
|
||||
}
|
||||
|
||||
// returns offset in serializable properties array
|
||||
private fun IrBlockBodyBuilder.generateSuperSerializableCall(
|
||||
superClass: IrClass,
|
||||
allValueParameters: List<IrValueParameter>,
|
||||
propertiesStart: Int
|
||||
): Int {
|
||||
check(superClass.isInternalSerializable)
|
||||
val superCtorRef = superClass.findSerializableSyntheticConstructor()
|
||||
?: error("Class serializable internally should have special constructor with marker")
|
||||
val superProperties = serializablePropertiesForIrBackend(superClass).serializableProperties
|
||||
val superSlots = superProperties.bitMaskSlotCount()
|
||||
val arguments = allValueParameters.subList(0, superSlots) +
|
||||
allValueParameters.subList(propertiesStart, propertiesStart + superProperties.size) +
|
||||
allValueParameters.last() // SerializationConstructorMarker
|
||||
val call = IrDelegatingConstructorCallImpl.fromSymbolOwner(
|
||||
startOffset,
|
||||
endOffset,
|
||||
compilerContext.irBuiltIns.unitType,
|
||||
superCtorRef
|
||||
)
|
||||
arguments.forEachIndexed { index, parameter -> call.putValueArgument(index, irGet(parameter)) }
|
||||
call.insertTypeArgumentsForSuperClass(superClass)
|
||||
+call
|
||||
return superProperties.size
|
||||
}
|
||||
|
||||
fun generateWriteSelfMethod(methodDescriptor: IrSimpleFunction) {
|
||||
addFunctionBody(methodDescriptor) { writeSelfFunction ->
|
||||
val objectToSerialize = writeSelfFunction.valueParameters[0]
|
||||
val localOutput = writeSelfFunction.valueParameters[1]
|
||||
val localSerialDesc = writeSelfFunction.valueParameters[2]
|
||||
val serializableProperties = properties.serializableProperties
|
||||
val kOutputClass = compilerContext.getClassFromRuntime(SerialEntityNames.STRUCTURE_ENCODER_CLASS)
|
||||
|
||||
val propertyByParamReplacer: (ValueParameterDescriptor) -> IrExpression? =
|
||||
createPropertyByParamReplacer(irClass, serializableProperties, objectToSerialize)
|
||||
|
||||
// Since writeSelf is a static method, we have to replace all references to this in property initializers
|
||||
val thisSymbol = irClass.thisReceiver!!.symbol
|
||||
val initializerAdapter: (IrExpressionBody) -> IrExpression =
|
||||
createInitializerAdapter(irClass, propertyByParamReplacer, thisSymbol to { irGet(objectToSerialize) })
|
||||
|
||||
// Compute offset of properties in superclass
|
||||
var ignoreIndexTo = -1
|
||||
val superClass = irClass.getSuperClassOrAny()
|
||||
if (superClass.isInternalSerializable) {
|
||||
ignoreIndexTo = serializablePropertiesForIrBackend(superClass).serializableProperties.size
|
||||
|
||||
// call super.writeSelf
|
||||
var superWriteSelfF = superClass.findWriteSelfMethod()
|
||||
|
||||
if (superWriteSelfF != null) {
|
||||
// Workaround for incorrect DeserializedClassDescriptor on JVM (see MemberDeserializer#getDispatchReceiverParameter):
|
||||
// Because Kotlin does not have static functions, descriptors from other modules are deserialized with dispatch receiver,
|
||||
// even if they were created without it
|
||||
if (superWriteSelfF.dispatchReceiverParameter != null) {
|
||||
superWriteSelfF = compilerContext.copiedStaticWriteSelf.getOrPut(superWriteSelfF) {
|
||||
superWriteSelfF!!.deepCopyWithSymbols(initialParent = superClass).also { it.dispatchReceiverParameter = null }
|
||||
}
|
||||
}
|
||||
|
||||
val args = mutableListOf<IrExpression>(irGet(objectToSerialize), irGet(localOutput), irGet(localSerialDesc))
|
||||
|
||||
val typeArgsForParent =
|
||||
(irClass.superTypes.single { it.classOrNull?.owner?.isInternalSerializable == true } as? IrSimpleType)?.arguments.orEmpty()
|
||||
val parentWriteSelfSerializers = typeArgsForParent.map { arg ->
|
||||
val genericIdx = irClass.defaultType.arguments.indexOf(arg).let { if (it == -1) null else it }
|
||||
val serial = findTypeSerializerOrContext(compilerContext, arg.typeOrNull!!)
|
||||
serializerInstance(
|
||||
serial,
|
||||
compilerContext,
|
||||
arg.typeOrNull!!,
|
||||
genericIdx
|
||||
) { it, _ ->
|
||||
irGet(writeSelfFunction.valueParameters[3 + it])
|
||||
}!!
|
||||
}
|
||||
+irInvoke(null, superWriteSelfF.symbol, typeArgsForParent.map { it.typeOrNull!! }, args + parentWriteSelfSerializers)
|
||||
}
|
||||
}
|
||||
|
||||
serializeAllProperties(
|
||||
serializableProperties, objectToSerialize,
|
||||
localOutput, localSerialDesc, kOutputClass,
|
||||
ignoreIndexTo, initializerAdapter
|
||||
) { it, _ ->
|
||||
irGet(writeSelfFunction.valueParameters[3 + it])
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun generate() {
|
||||
generateSyntheticInternalConstructor()
|
||||
generateSyntheticMethods()
|
||||
}
|
||||
|
||||
private fun generateSyntheticInternalConstructor() {
|
||||
val serializerDescriptor = irClass.classSerializer(compilerContext)?.owner ?: return
|
||||
if (irClass.shouldHaveSpecificSyntheticMethods { serializerDescriptor.findPluginGeneratedMethod(LOAD) }) {
|
||||
val constrDesc = irClass.constructors.find(IrConstructor::isSerializationCtor) ?: return
|
||||
generateInternalConstructor(constrDesc)
|
||||
}
|
||||
}
|
||||
|
||||
private fun generateSyntheticMethods() {
|
||||
val serializerDescriptor = irClass.classSerializer(compilerContext)?.owner ?: return
|
||||
if (irClass.shouldHaveSpecificSyntheticMethods { serializerDescriptor.findPluginGeneratedMethod(SAVE) }) {
|
||||
val func = irClass.findWriteSelfMethod() ?: return
|
||||
generateWriteSelfMethod(func)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
companion object {
|
||||
fun generate(
|
||||
irClass: IrClass,
|
||||
context: SerializationPluginContext,
|
||||
) {
|
||||
if (irClass.isInternalSerializable) {
|
||||
SerializableIrGenerator(irClass, context).generate()
|
||||
irClass.patchDeclarationParents(irClass.parent)
|
||||
} else {
|
||||
val serializableAnnotationIsUseless = with(irClass) {
|
||||
hasSerializableOrMetaAnnotationWithoutArgs() && !isInternalSerializable && !hasCompanionObjectAsSerializer && kind != ClassKind.ENUM_CLASS && !isSealedSerializableInterface
|
||||
}
|
||||
if (serializableAnnotationIsUseless)
|
||||
throw AssertionError(
|
||||
"@Serializable annotation on $irClass would be ignored because it is impossible to serialize it automatically. " +
|
||||
"Provide serializer manually via e.g. companion object"
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+110
@@ -0,0 +1,110 @@
|
||||
/*
|
||||
* Copyright 2010-2020 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlinx.serialization.compiler.backend.ir
|
||||
|
||||
import org.jetbrains.kotlin.backend.jvm.functionByName
|
||||
import org.jetbrains.kotlin.builtins.StandardNames
|
||||
import org.jetbrains.kotlin.ir.builders.*
|
||||
import org.jetbrains.kotlin.ir.declarations.IrClass
|
||||
import org.jetbrains.kotlin.ir.declarations.IrSimpleFunction
|
||||
import org.jetbrains.kotlin.ir.declarations.IrVariable
|
||||
import org.jetbrains.kotlin.ir.deepCopyWithVariables
|
||||
import org.jetbrains.kotlin.ir.expressions.IrExpression
|
||||
import org.jetbrains.kotlin.ir.expressions.impl.IrGetValueImpl
|
||||
import org.jetbrains.kotlin.ir.symbols.IrClassSymbol
|
||||
import org.jetbrains.kotlin.ir.symbols.IrFunctionSymbol
|
||||
import org.jetbrains.kotlin.ir.util.constructors
|
||||
import org.jetbrains.kotlin.ir.util.defaultType
|
||||
import org.jetbrains.kotlin.ir.util.functions
|
||||
import org.jetbrains.kotlin.ir.util.properties
|
||||
import org.jetbrains.kotlin.name.Name
|
||||
import org.jetbrains.kotlinx.serialization.compiler.extensions.SerializationPluginContext
|
||||
import org.jetbrains.kotlinx.serialization.compiler.resolve.CallingConventions
|
||||
import org.jetbrains.kotlinx.serialization.compiler.resolve.SerialEntityNames
|
||||
|
||||
class SerializerForEnumsGenerator(
|
||||
irClass: IrClass,
|
||||
compilerContext: SerializationPluginContext,
|
||||
) : SerializerIrGenerator(irClass, compilerContext, null) {
|
||||
override fun generateSave(function: IrSimpleFunction) = addFunctionBody(function) { saveFunc ->
|
||||
fun irThis(): IrExpression =
|
||||
IrGetValueImpl(startOffset, endOffset, saveFunc.dispatchReceiverParameter!!.symbol)
|
||||
|
||||
val encoderClass = compilerContext.getClassFromRuntime(SerialEntityNames.ENCODER_CLASS)
|
||||
val descriptorGetterSymbol = irAnySerialDescProperty?.getter!!.symbol
|
||||
val encodeEnum = encoderClass.functionByName(CallingConventions.encodeEnum)
|
||||
val serialDescGetter = irGet(descriptorGetterSymbol.owner.returnType, irThis(), descriptorGetterSymbol)
|
||||
|
||||
val serializableIrClass = requireNotNull(serializableIrClass) { "Enums do not support external serialization" }
|
||||
val ordinalProp = serializableIrClass.properties.single { it.name == Name.identifier("ordinal") }.getter!!
|
||||
val getOrdinal = irInvoke(irGet(saveFunc.valueParameters[1]), ordinalProp.symbol)
|
||||
val call = irInvoke(irGet(saveFunc.valueParameters[0]), encodeEnum, serialDescGetter, getOrdinal)
|
||||
+call
|
||||
}
|
||||
|
||||
override fun generateLoad(function: IrSimpleFunction) = addFunctionBody(function) { loadFunc ->
|
||||
fun irThis(): IrExpression =
|
||||
IrGetValueImpl(startOffset, endOffset, loadFunc.dispatchReceiverParameter!!.symbol)
|
||||
|
||||
val decoderClass = compilerContext.getClassFromRuntime(SerialEntityNames.DECODER_CLASS)
|
||||
val descriptorGetterSymbol = irAnySerialDescProperty?.getter!!.symbol
|
||||
val decode = decoderClass.functionByName(CallingConventions.decodeEnum)
|
||||
val serialDescGetter = irGet(descriptorGetterSymbol.owner.returnType, irThis(), descriptorGetterSymbol)
|
||||
|
||||
val valuesF = this@SerializerForEnumsGenerator.serializableIrClass.functions.single { it.name == StandardNames.ENUM_VALUES }
|
||||
val getValues = irInvoke(dispatchReceiver = null, callee = valuesF.symbol)
|
||||
|
||||
|
||||
val arrayGet = compilerContext.irBuiltIns.arrayClass.owner.declarations.filterIsInstance<IrSimpleFunction>()
|
||||
.single { it.name.asString() == "get" }
|
||||
|
||||
val getValueByOrdinal =
|
||||
irInvoke(
|
||||
getValues,
|
||||
arrayGet.symbol,
|
||||
irInvoke(irGet(loadFunc.valueParameters[0]), decode, serialDescGetter),
|
||||
typeHint = this@SerializerForEnumsGenerator.serializableIrClass.defaultType
|
||||
)
|
||||
+irReturn(getValueByOrdinal)
|
||||
}
|
||||
|
||||
override val serialDescImplClass: IrClassSymbol = compilerContext.getClassFromInternalSerializationPackage(SerialEntityNames.SERIAL_DESCRIPTOR_FOR_ENUM)
|
||||
|
||||
override fun IrBlockBodyBuilder.instantiateNewDescriptor(serialDescImplClass: IrClassSymbol, correctThis: IrExpression): IrExpression {
|
||||
val ctor = serialDescImplClass.constructors.single { it.owner.isPrimary }
|
||||
return irInvoke(
|
||||
null, ctor,
|
||||
irString(serialName),
|
||||
irInt(serializableIrClass.enumEntries().size)
|
||||
)
|
||||
}
|
||||
|
||||
override fun IrBlockBodyBuilder.addElementsContentToDescriptor(
|
||||
serialDescImplClass: IrClassSymbol,
|
||||
localDescriptor: IrVariable,
|
||||
addFunction: IrFunctionSymbol
|
||||
) {
|
||||
val enumEntries = serializableIrClass.enumEntries()
|
||||
for (entry in enumEntries) {
|
||||
// regular .serialName() produces fqName here, which is kinda inconvenient for enum entry
|
||||
val serialName = entry.annotations.serialNameValue ?: entry.name.toString()
|
||||
val call = irInvoke(
|
||||
irGet(localDescriptor),
|
||||
addFunction,
|
||||
irString(serialName),
|
||||
irBoolean(false),
|
||||
typeHint = compilerContext.irBuiltIns.unitType
|
||||
)
|
||||
+call
|
||||
// serialDesc.pushAnnotation(...)
|
||||
copySerialInfoAnnotationsToDescriptor(
|
||||
entry.annotations.map {it.deepCopyWithVariables()},
|
||||
localDescriptor,
|
||||
serialDescImplClass.functionByName(CallingConventions.addAnnotation)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
+106
@@ -0,0 +1,106 @@
|
||||
/*
|
||||
* Copyright 2010-2020 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlinx.serialization.compiler.backend.ir
|
||||
|
||||
import org.jetbrains.kotlin.backend.jvm.functionByName
|
||||
import org.jetbrains.kotlin.ir.builders.*
|
||||
import org.jetbrains.kotlin.ir.declarations.IrClass
|
||||
import org.jetbrains.kotlin.ir.declarations.IrSimpleFunction
|
||||
import org.jetbrains.kotlin.ir.expressions.IrExpression
|
||||
import org.jetbrains.kotlin.ir.expressions.impl.IrGetValueImpl
|
||||
import org.jetbrains.kotlin.ir.symbols.IrClassSymbol
|
||||
import org.jetbrains.kotlin.ir.types.IrSimpleType
|
||||
import org.jetbrains.kotlin.ir.types.IrType
|
||||
import org.jetbrains.kotlin.ir.types.typeOrNull
|
||||
import org.jetbrains.kotlin.ir.util.constructors
|
||||
import org.jetbrains.kotlinx.serialization.compiler.extensions.SerializationPluginContext
|
||||
import org.jetbrains.kotlinx.serialization.compiler.resolve.CallingConventions
|
||||
import org.jetbrains.kotlinx.serialization.compiler.resolve.SerialEntityNames
|
||||
|
||||
class SerializerForInlineClassGenerator(
|
||||
irClass: IrClass,
|
||||
compilerContext: SerializationPluginContext,
|
||||
) : SerializerIrGenerator(irClass, compilerContext, null) {
|
||||
override fun generateSave(function: IrSimpleFunction) = addFunctionBody(function) { saveFunc ->
|
||||
fun irThis(): IrExpression =
|
||||
IrGetValueImpl(startOffset, endOffset, saveFunc.dispatchReceiverParameter!!.symbol)
|
||||
|
||||
val encoderClass = compilerContext.getClassFromRuntime(SerialEntityNames.ENCODER_CLASS)
|
||||
val descriptorGetterSymbol = irAnySerialDescProperty?.getter!!.symbol
|
||||
val encodeInline = encoderClass.functionByName(CallingConventions.encodeInline)
|
||||
val serialDescGetter = irGet(descriptorGetterSymbol.owner.returnType, irThis(), descriptorGetterSymbol)
|
||||
|
||||
// val inlineEncoder = encoder.encodeInline()
|
||||
val encodeInlineCall: IrExpression = irInvoke(irGet(saveFunc.valueParameters[0]), encodeInline, serialDescGetter)
|
||||
val inlineEncoder = irTemporary(encodeInlineCall, nameHint = "inlineEncoder")
|
||||
|
||||
val property = serializableProperties.first()
|
||||
val value = getProperty(irGet(saveFunc.valueParameters[1]), property.ir)
|
||||
|
||||
// inlineEncoder.encodeInt/String/SerializableValue
|
||||
val elementCall = formEncodeDecodePropertyCall(irGet(inlineEncoder), saveFunc.dispatchReceiverParameter!!, property, {innerSerial, sti ->
|
||||
val f =
|
||||
encoderClass.functionByName("${CallingConventions.encode}${sti.elementMethodPrefix}SerializableValue")
|
||||
f to listOf(
|
||||
innerSerial,
|
||||
value
|
||||
)
|
||||
}, {
|
||||
val f =
|
||||
encoderClass.functionByName("${CallingConventions.encode}${it.elementMethodPrefix}")
|
||||
val args = if (it.elementMethodPrefix != "Unit") listOf(value) else emptyList()
|
||||
f to args
|
||||
})
|
||||
|
||||
val actualEncodeCall = irIfNull(compilerContext.irBuiltIns.unitType, irGet(inlineEncoder), irNull(), elementCall)
|
||||
+actualEncodeCall
|
||||
}
|
||||
|
||||
override fun generateLoad(function: IrSimpleFunction) = addFunctionBody(function) { loadFunc ->
|
||||
fun irThis(): IrExpression =
|
||||
IrGetValueImpl(startOffset, endOffset, loadFunc.dispatchReceiverParameter!!.symbol)
|
||||
|
||||
val decoderClass = compilerContext.getClassFromRuntime(SerialEntityNames.DECODER_CLASS)
|
||||
val descriptorGetterSymbol = irAnySerialDescProperty?.getter!!.symbol
|
||||
val decodeInline = decoderClass.functionByName(CallingConventions.decodeInline)
|
||||
val serialDescGetter = irGet(descriptorGetterSymbol.owner.returnType, irThis(), descriptorGetterSymbol)
|
||||
|
||||
// val inlineDecoder = decoder.decodeInline()
|
||||
val inlineDecoder: IrExpression = irInvoke(irGet(loadFunc.valueParameters[0]), decodeInline, serialDescGetter)
|
||||
|
||||
val property = serializableProperties.first()
|
||||
val inlinedType = property.type
|
||||
val actualCall = formEncodeDecodePropertyCall(inlineDecoder, loadFunc.dispatchReceiverParameter!!, property, { innerSerial, sti ->
|
||||
decoderClass.functionByName( "${CallingConventions.decode}${sti.elementMethodPrefix}SerializableValue") to listOf(innerSerial)
|
||||
}, {
|
||||
decoderClass.functionByName("${CallingConventions.decode}${it.elementMethodPrefix}") to listOf()
|
||||
}, returnTypeHint = inlinedType)
|
||||
val value = coerceToBox(actualCall, loadFunc.returnType)
|
||||
+irReturn(value)
|
||||
}
|
||||
|
||||
override val serialDescImplClass: IrClassSymbol = compilerContext.getClassFromInternalSerializationPackage(SerialEntityNames.SERIAL_DESCRIPTOR_FOR_INLINE)
|
||||
|
||||
override fun IrBlockBodyBuilder.instantiateNewDescriptor(serialDescImplClass: IrClassSymbol, correctThis: IrExpression): IrExpression {
|
||||
val ctor = serialDescImplClass.constructors.single { it.owner.isPrimary }
|
||||
return irInvoke(
|
||||
null, ctor,
|
||||
irString(serialName),
|
||||
correctThis
|
||||
)
|
||||
}
|
||||
|
||||
// Compiler will elide these in corresponding inline class lowerings (when serialize/deserialize functions will be split in two)
|
||||
|
||||
private fun IrBlockBodyBuilder.coerceToBox(expression: IrExpression, inlineClassBoxType: IrType): IrExpression =
|
||||
irInvoke(
|
||||
null,
|
||||
serializableIrClass.constructors.single { it.isPrimary }.symbol,
|
||||
(inlineClassBoxType as IrSimpleType).arguments.map { it.typeOrNull },
|
||||
listOf(expression)
|
||||
)
|
||||
|
||||
}
|
||||
+630
@@ -0,0 +1,630 @@
|
||||
/*
|
||||
* Copyright 2010-2021 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlinx.serialization.compiler.backend.ir
|
||||
|
||||
import org.jetbrains.kotlin.backend.common.lower.DeclarationIrBuilder
|
||||
import org.jetbrains.kotlin.backend.common.lower.irIfThen
|
||||
import org.jetbrains.kotlin.backend.common.lower.irThrow
|
||||
import org.jetbrains.kotlin.backend.jvm.functionByName
|
||||
import org.jetbrains.kotlin.builtins.PrimitiveType
|
||||
import org.jetbrains.kotlin.codegen.CompilationException
|
||||
import org.jetbrains.kotlin.descriptors.Modality
|
||||
import org.jetbrains.kotlin.descriptors.ValueParameterDescriptor
|
||||
import org.jetbrains.kotlin.ir.builders.*
|
||||
import org.jetbrains.kotlin.ir.declarations.*
|
||||
import org.jetbrains.kotlin.ir.deepCopyWithVariables
|
||||
import org.jetbrains.kotlin.ir.expressions.*
|
||||
import org.jetbrains.kotlin.ir.expressions.impl.IrBranchImpl
|
||||
import org.jetbrains.kotlin.ir.expressions.impl.IrConstImpl
|
||||
import org.jetbrains.kotlin.ir.expressions.impl.IrDelegatingConstructorCallImpl
|
||||
import org.jetbrains.kotlin.ir.expressions.impl.IrGetValueImpl
|
||||
import org.jetbrains.kotlin.ir.symbols.IrClassSymbol
|
||||
import org.jetbrains.kotlin.ir.symbols.IrConstructorSymbol
|
||||
import org.jetbrains.kotlin.ir.symbols.IrFunctionSymbol
|
||||
import org.jetbrains.kotlin.ir.symbols.impl.IrAnonymousInitializerSymbolImpl
|
||||
import org.jetbrains.kotlin.ir.types.*
|
||||
import org.jetbrains.kotlin.ir.util.*
|
||||
import org.jetbrains.kotlin.name.ClassId
|
||||
import org.jetbrains.kotlin.name.Name
|
||||
import org.jetbrains.kotlin.resolve.isInlineClass
|
||||
import org.jetbrains.kotlin.util.OperatorNameConventions
|
||||
import org.jetbrains.kotlin.utils.addToStdlib.cast
|
||||
import org.jetbrains.kotlinx.serialization.compiler.extensions.SerializationDescriptorSerializerPlugin
|
||||
import org.jetbrains.kotlinx.serialization.compiler.extensions.SerializationPluginContext
|
||||
import org.jetbrains.kotlinx.serialization.compiler.resolve.*
|
||||
import org.jetbrains.kotlinx.serialization.compiler.resolve.SerialEntityNames.DECODER_CLASS
|
||||
import org.jetbrains.kotlinx.serialization.compiler.resolve.SerialEntityNames.ENCODER_CLASS
|
||||
import org.jetbrains.kotlinx.serialization.compiler.resolve.SerialEntityNames.KSERIALIZER_CLASS
|
||||
import org.jetbrains.kotlinx.serialization.compiler.resolve.SerialEntityNames.LOAD
|
||||
import org.jetbrains.kotlinx.serialization.compiler.resolve.SerialEntityNames.SAVE
|
||||
import org.jetbrains.kotlinx.serialization.compiler.resolve.SerialEntityNames.STRUCTURE_DECODER_CLASS
|
||||
import org.jetbrains.kotlinx.serialization.compiler.resolve.SerialEntityNames.STRUCTURE_ENCODER_CLASS
|
||||
import org.jetbrains.kotlinx.serialization.compiler.resolve.SerialEntityNames.UNKNOWN_FIELD_EXC
|
||||
|
||||
object SERIALIZATION_PLUGIN_ORIGIN : IrDeclarationOriginImpl("KOTLINX_SERIALIZATION", true)
|
||||
|
||||
internal typealias FunctionWithArgs = Pair<IrFunctionSymbol, List<IrExpression>>
|
||||
|
||||
open class SerializerIrGenerator(
|
||||
val irClass: IrClass,
|
||||
compilerContext: SerializationPluginContext,
|
||||
metadataPlugin: SerializationDescriptorSerializerPlugin?,
|
||||
) : BaseIrGenerator(irClass, compilerContext) {
|
||||
protected val serializableIrClass = compilerContext.getSerializableClassDescriptorBySerializer(irClass)!!
|
||||
|
||||
protected val serialName: String = serializableIrClass.serialName()
|
||||
protected val properties = serializablePropertiesForIrBackend(serializableIrClass, metadataPlugin)
|
||||
protected val serializableProperties = properties.serializableProperties
|
||||
protected val isGeneratedSerializer = irClass.superTypes.any(IrType::isGeneratedKSerializer)
|
||||
|
||||
protected val generatedSerialDescPropertyDescriptor = getProperty(
|
||||
SerialEntityNames.SERIAL_DESC_FIELD,
|
||||
{ true }
|
||||
)?.takeIf { it.isFromPlugin() }
|
||||
|
||||
protected val anySerialDescProperty = getProperty(
|
||||
SerialEntityNames.SERIAL_DESC_FIELD,
|
||||
) { true } // remove true?
|
||||
|
||||
protected val irAnySerialDescProperty = anySerialDescProperty
|
||||
|
||||
fun getProperty(
|
||||
name: String,
|
||||
isReturnTypeOk: (IrProperty) -> Boolean
|
||||
): IrProperty? {
|
||||
return irClass.properties.singleOrNull { it.name.asString() == name && isReturnTypeOk(it) }
|
||||
}
|
||||
|
||||
var localSerializersFieldsDescriptors: List<IrProperty> = emptyList()
|
||||
private set
|
||||
|
||||
// null if was not found — we're in FIR
|
||||
private fun findLocalSerializersFieldDescriptors(): List<IrProperty?> {
|
||||
val count = serializableIrClass.typeParameters.size
|
||||
if (count == 0) return emptyList()
|
||||
val propNames = (0 until count).map { "${SerialEntityNames.typeArgPrefix}$it" }
|
||||
return propNames.map { name ->
|
||||
getProperty(name) { it.getter!!.returnType.isKSerializer() }
|
||||
}
|
||||
}
|
||||
|
||||
protected open val serialDescImplClass: IrClassSymbol =
|
||||
compilerContext.getClassFromInternalSerializationPackage(SerialEntityNames.SERIAL_DESCRIPTOR_CLASS_IMPL)
|
||||
|
||||
fun generateSerialDesc() {
|
||||
val desc = generatedSerialDescPropertyDescriptor ?: return
|
||||
val addFuncS = serialDescImplClass.functionByName(CallingConventions.addElement)
|
||||
|
||||
val thisAsReceiverParameter = irClass.thisReceiver!!
|
||||
lateinit var prop: IrProperty
|
||||
|
||||
// how to (auto)create backing field and getter/setter?
|
||||
compilerContext.symbolTable.withReferenceScope(irClass) {
|
||||
prop = generatePropertyMissingParts(desc, desc.name, serialDescImplClass.starProjectedType, irClass, desc.visibility)
|
||||
|
||||
localSerializersFieldsDescriptors = findLocalSerializersFieldDescriptors().mapIndexed { i, prop ->
|
||||
generatePropertyMissingParts(
|
||||
prop, Name.identifier("${SerialEntityNames.typeArgPrefix}$i"),
|
||||
compilerContext.getClassFromRuntime(KSERIALIZER_CLASS).starProjectedType, irClass
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
val anonymousInit = irClass.run {
|
||||
val symbol = IrAnonymousInitializerSymbolImpl(symbol)
|
||||
irClass.factory.createAnonymousInitializer(startOffset, endOffset, SERIALIZATION_PLUGIN_ORIGIN, symbol).also {
|
||||
it.parent = this
|
||||
declarations.add(it)
|
||||
}
|
||||
}
|
||||
|
||||
anonymousInit.buildWithScope { initIrBody ->
|
||||
compilerContext.symbolTable.withReferenceScope(initIrBody) {
|
||||
initIrBody.body =
|
||||
DeclarationIrBuilder(compilerContext, initIrBody.symbol, initIrBody.startOffset, initIrBody.endOffset).irBlockBody {
|
||||
val localDesc = irTemporary(
|
||||
instantiateNewDescriptor(serialDescImplClass, irGet(thisAsReceiverParameter)),
|
||||
nameHint = "serialDesc"
|
||||
)
|
||||
|
||||
addElementsContentToDescriptor(serialDescImplClass, localDesc, addFuncS)
|
||||
// add class annotations
|
||||
copySerialInfoAnnotationsToDescriptor(
|
||||
collectSerialInfoAnnotations(serializableIrClass),
|
||||
localDesc,
|
||||
serialDescImplClass.functionByName(CallingConventions.addClassAnnotation)
|
||||
)
|
||||
|
||||
// save local descriptor to field
|
||||
+irSetField(
|
||||
IrGetValueImpl(
|
||||
startOffset, endOffset,
|
||||
thisAsReceiverParameter.symbol
|
||||
),
|
||||
prop.backingField!!,
|
||||
irGet(localDesc)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected open fun IrBlockBodyBuilder.instantiateNewDescriptor(
|
||||
serialDescImplClass: IrClassSymbol,
|
||||
correctThis: IrExpression
|
||||
): IrExpression {
|
||||
// val classConstructors = compilerContext.referenceConstructors(serialDescImplClass.fqNameSafe)
|
||||
val serialClassDescImplCtor = serialDescImplClass.constructors.single { it.owner.isPrimary }
|
||||
return irInvoke(
|
||||
null, serialClassDescImplCtor,
|
||||
irString(serialName), if (isGeneratedSerializer) correctThis else irNull(), irInt(serializableProperties.size)
|
||||
)
|
||||
}
|
||||
|
||||
protected open fun IrBlockBodyBuilder.addElementsContentToDescriptor(
|
||||
serialDescImplClass: IrClassSymbol,
|
||||
localDescriptor: IrVariable,
|
||||
addFunction: IrFunctionSymbol
|
||||
) {
|
||||
fun addFieldCall(prop: IrSerializableProperty) = irInvoke(
|
||||
irGet(localDescriptor),
|
||||
addFunction,
|
||||
irString(prop.name),
|
||||
irBoolean(prop.optional),
|
||||
typeHint = compilerContext.irBuiltIns.unitType
|
||||
)
|
||||
|
||||
for (classProp in serializableProperties) {
|
||||
if (classProp.transient) continue
|
||||
+addFieldCall(classProp)
|
||||
// add property annotations
|
||||
val property = classProp.ir//.getIrPropertyFrom(serializableIrClass)
|
||||
copySerialInfoAnnotationsToDescriptor(
|
||||
property.annotations,
|
||||
localDescriptor,
|
||||
serialDescImplClass.functionByName(CallingConventions.addAnnotation)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
protected fun IrBlockBodyBuilder.copySerialInfoAnnotationsToDescriptor(
|
||||
annotations: List<IrConstructorCall>,
|
||||
receiver: IrVariable,
|
||||
method: IrFunctionSymbol
|
||||
) {
|
||||
copyAnnotationsFrom(annotations).forEach {
|
||||
+irInvoke(irGet(receiver), method, it)
|
||||
}
|
||||
}
|
||||
|
||||
fun generateGenericFieldsAndConstructor(typedConstructorDescriptor: IrConstructor) =
|
||||
addFunctionBody(typedConstructorDescriptor) { ctor ->
|
||||
// generate call to primary ctor to init serialClassDesc and super()
|
||||
val primaryCtor = irClass.constructors.primary
|
||||
+IrDelegatingConstructorCallImpl.fromSymbolOwner(
|
||||
startOffset,
|
||||
endOffset,
|
||||
compilerContext.irBuiltIns.unitType,
|
||||
primaryCtor.symbol
|
||||
).apply {
|
||||
irClass.typeParameters.forEachIndexed { index, irTypeParameter ->
|
||||
putTypeArgument(index, irTypeParameter.defaultType)
|
||||
}
|
||||
}
|
||||
|
||||
// store type arguments serializers in fields
|
||||
val thisAsReceiverParameter = irClass.thisReceiver!!
|
||||
ctor.valueParameters.forEachIndexed { index, param ->
|
||||
val localSerial = localSerializersFieldsDescriptors[index].backingField!!
|
||||
+irSetField(
|
||||
IrGetValueImpl(startOffset, endOffset, thisAsReceiverParameter.symbol), localSerial, irGet(param)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
open fun generateChildSerializersGetter(function: IrSimpleFunction) = addFunctionBody(function) { irFun ->
|
||||
val allSerializers = serializableProperties.map {
|
||||
requireNotNull(
|
||||
serializerTower(this@SerializerIrGenerator, irFun.dispatchReceiverParameter!!, it)
|
||||
) { "Property ${it.name} must have a serializer" }
|
||||
}
|
||||
|
||||
val kSerType = ((irFun.returnType as IrSimpleType).arguments.first() as IrTypeProjection).type
|
||||
val array = createArrayOfExpression(kSerType, allSerializers)
|
||||
+irReturn(array)
|
||||
}
|
||||
|
||||
open fun generateTypeParamsSerializersGetter(function: IrSimpleFunction) = addFunctionBody(function) { irFun ->
|
||||
val typeParams = serializableIrClass.typeParameters.mapIndexed { idx, _ ->
|
||||
irGetField(
|
||||
irGet(irFun.dispatchReceiverParameter!!),
|
||||
localSerializersFieldsDescriptors[idx].backingField!!
|
||||
)
|
||||
}
|
||||
val kSerType = ((irFun.returnType as IrSimpleType).arguments.first() as IrTypeProjection).type
|
||||
val array = createArrayOfExpression(kSerType, typeParams)
|
||||
+irReturn(array)
|
||||
}
|
||||
|
||||
open fun generateSerializableClassProperty(property: IrProperty) {
|
||||
/* Already implemented in .generateSerialClassDesc ? */
|
||||
}
|
||||
|
||||
open fun generateSave(function: IrSimpleFunction) = addFunctionBody(function) { saveFunc ->
|
||||
|
||||
fun irThis(): IrExpression =
|
||||
IrGetValueImpl(startOffset, endOffset, saveFunc.dispatchReceiverParameter!!.symbol)
|
||||
|
||||
val kOutputClass = compilerContext.getClassFromRuntime(STRUCTURE_ENCODER_CLASS)
|
||||
val encoderClass = compilerContext.getClassFromRuntime(ENCODER_CLASS)
|
||||
|
||||
val descriptorGetterSymbol = irAnySerialDescProperty?.getter!!.symbol
|
||||
|
||||
val localSerialDesc = irTemporary(irGet(descriptorGetterSymbol.owner.returnType, irThis(), descriptorGetterSymbol), "desc")
|
||||
|
||||
// public fun beginStructure(descriptor: SerialDescriptor): CompositeDecoder
|
||||
val beginFunc =
|
||||
encoderClass.functions.single { it.owner.name.asString() == CallingConventions.begin && it.owner.valueParameters.size == 1 }
|
||||
|
||||
val call = irInvoke(irGet(saveFunc.valueParameters[0]), beginFunc, irGet(localSerialDesc), typeHint = kOutputClass.defaultType)
|
||||
val objectToSerialize = saveFunc.valueParameters[1]
|
||||
val localOutput = irTemporary(call, "output")
|
||||
|
||||
val writeSelfFunction = serializableIrClass.findWriteSelfMethod()
|
||||
|
||||
if (writeSelfFunction != null) {
|
||||
// extract Tx from KSerializer<Tx> list
|
||||
val typeArgs =
|
||||
localSerializersFieldsDescriptors.map { ir -> ir.backingField!!.type.cast<IrSimpleType>().arguments.single().typeOrNull }
|
||||
val args = mutableListOf<IrExpression>(irGet(objectToSerialize), irGet(localOutput), irGet(localSerialDesc))
|
||||
args.addAll(localSerializersFieldsDescriptors.map { ir ->
|
||||
irGetField(
|
||||
irGet(saveFunc.dispatchReceiverParameter!!),
|
||||
ir.backingField!!
|
||||
)
|
||||
})
|
||||
+irInvoke(null, writeSelfFunction.symbol, typeArgs, args)
|
||||
} else {
|
||||
val propertyByParamReplacer: (ValueParameterDescriptor) -> IrExpression? =
|
||||
createPropertyByParamReplacer(serializableIrClass, serializableProperties, objectToSerialize)
|
||||
|
||||
val thisSymbol = serializableIrClass.thisReceiver!!.symbol
|
||||
val initializerAdapter: (IrExpressionBody) -> IrExpression =
|
||||
createInitializerAdapter(serializableIrClass, propertyByParamReplacer, thisSymbol to { irGet(objectToSerialize) })
|
||||
|
||||
serializeAllProperties(
|
||||
serializableProperties, objectToSerialize, localOutput,
|
||||
localSerialDesc, kOutputClass, ignoreIndexTo = -1, initializerAdapter
|
||||
) { it, _ ->
|
||||
val ir = localSerializersFieldsDescriptors[it]
|
||||
irGetField(irGet(saveFunc.dispatchReceiverParameter!!), ir.backingField!!)
|
||||
}
|
||||
}
|
||||
|
||||
// output.writeEnd(serialClassDesc)
|
||||
val wEndFunc = kOutputClass.functionByName(CallingConventions.end)
|
||||
+irInvoke(irGet(localOutput), wEndFunc, irGet(localSerialDesc))
|
||||
}
|
||||
|
||||
protected fun IrBlockBodyBuilder.formEncodeDecodePropertyCall(
|
||||
encoder: IrExpression,
|
||||
dispatchReceiver: IrValueParameter,
|
||||
property: IrSerializableProperty,
|
||||
whenHaveSerializer: (serializer: IrExpression, sti: IrSerialTypeInfo) -> FunctionWithArgs,
|
||||
whenDoNot: (sti: IrSerialTypeInfo) -> FunctionWithArgs,
|
||||
returnTypeHint: IrType? = null
|
||||
): IrExpression = formEncodeDecodePropertyCall(
|
||||
encoder,
|
||||
property,
|
||||
whenHaveSerializer,
|
||||
whenDoNot,
|
||||
{ it, _ ->
|
||||
val ir = localSerializersFieldsDescriptors[it]
|
||||
irGetField(irGet(dispatchReceiver), ir.backingField!!)
|
||||
},
|
||||
returnTypeHint
|
||||
)
|
||||
|
||||
// returns null: Any? for boxed types and 0: <number type> for primitives
|
||||
private fun IrBuilderWithScope.defaultValueAndType(descriptor: IrProperty): Pair<IrExpression, IrType> {
|
||||
val T = descriptor.getter!!.returnType
|
||||
val defaultPrimitive: IrExpression? =
|
||||
if (T.isMarkedNullable()) null
|
||||
else when (T.getPrimitiveType()) {
|
||||
PrimitiveType.BOOLEAN -> IrConstImpl.boolean(startOffset, endOffset, T, false)
|
||||
PrimitiveType.CHAR -> IrConstImpl.char(startOffset, endOffset, T, 0.toChar())
|
||||
PrimitiveType.BYTE -> IrConstImpl.byte(startOffset, endOffset, T, 0)
|
||||
PrimitiveType.SHORT -> IrConstImpl.short(startOffset, endOffset, T, 0)
|
||||
PrimitiveType.INT -> IrConstImpl.int(startOffset, endOffset, T, 0)
|
||||
PrimitiveType.FLOAT -> IrConstImpl.float(startOffset, endOffset, T, 0.0f)
|
||||
PrimitiveType.LONG -> IrConstImpl.long(startOffset, endOffset, T, 0)
|
||||
PrimitiveType.DOUBLE -> IrConstImpl.double(startOffset, endOffset, T, 0.0)
|
||||
else -> null
|
||||
}
|
||||
return if (defaultPrimitive == null)
|
||||
irNull(compilerContext.irBuiltIns.anyNType) to (compilerContext.irBuiltIns.anyNType)
|
||||
else
|
||||
defaultPrimitive to T
|
||||
}
|
||||
|
||||
open fun generateLoad(function: IrSimpleFunction) = addFunctionBody(function) { loadFunc ->
|
||||
if (serializableIrClass.modality == Modality.ABSTRACT || serializableIrClass.modality == Modality.SEALED) {
|
||||
return@addFunctionBody
|
||||
}
|
||||
|
||||
fun irThis(): IrExpression =
|
||||
IrGetValueImpl(startOffset, endOffset, loadFunc.dispatchReceiverParameter!!.symbol)
|
||||
|
||||
fun IrVariable.get() = irGet(this)
|
||||
|
||||
val inputClass = compilerContext.getClassFromRuntime(STRUCTURE_DECODER_CLASS)
|
||||
val decoderClass = compilerContext.getClassFromRuntime(DECODER_CLASS)
|
||||
val descriptorGetterSymbol = irAnySerialDescProperty?.getter!!.symbol
|
||||
val localSerialDesc = irTemporary(irGet(descriptorGetterSymbol.owner.returnType, irThis(), descriptorGetterSymbol), "desc")
|
||||
|
||||
// workaround due to unavailability of labels (KT-25386)
|
||||
val flagVar = irTemporary(irBoolean(true), "flag", isMutable = true)
|
||||
|
||||
val indexVar = irTemporary(irInt(0), "index", isMutable = true)
|
||||
|
||||
// calculating bit mask vars
|
||||
val blocksCnt = serializableProperties.bitMaskSlotCount()
|
||||
|
||||
val serialPropertiesIndexes = serializableProperties
|
||||
.mapIndexed { i, property -> property to i }
|
||||
.associate { (p, i) -> p.ir to i }
|
||||
|
||||
val transients = serializableIrClass.declarations.asSequence()
|
||||
.filterIsInstance<IrProperty>()
|
||||
.filter { !serialPropertiesIndexes.contains(it) }
|
||||
.filter { it.backingField != null }
|
||||
|
||||
// var bitMask0 = 0, bitMask1 = 0...
|
||||
val bitMasks = (0 until blocksCnt).map { irTemporary(irInt(0), "bitMask$it", isMutable = true) }
|
||||
// var local0 = null, local1 = null ...
|
||||
val serialPropertiesMap = serializableProperties.mapIndexed { i, prop -> i to prop.ir }.associate { (i, descriptor) ->
|
||||
val (expr, type) = defaultValueAndType(descriptor)
|
||||
descriptor to irTemporary(expr, "local$i", type, isMutable = true)
|
||||
}
|
||||
// var transient0 = null, transient0 = null ...
|
||||
val transientsPropertiesMap = transients.mapIndexed { i, prop -> i to prop }.associate { (i, descriptor) ->
|
||||
val (expr, type) = defaultValueAndType(descriptor)
|
||||
descriptor to irTemporary(expr, "transient$i", type, isMutable = true)
|
||||
}
|
||||
|
||||
//input = input.beginStructure(...)
|
||||
val beginFunc =
|
||||
decoderClass.functions.single { it.owner.name.asString() == CallingConventions.begin && it.owner.valueParameters.size == 1 }
|
||||
val call = irInvoke(
|
||||
irGet(loadFunc.valueParameters[0]),
|
||||
beginFunc,
|
||||
irGet(localSerialDesc),
|
||||
typeHint = inputClass.defaultType
|
||||
)
|
||||
val localInput = irTemporary(call, "input")
|
||||
|
||||
// prepare all .decodeXxxElement calls
|
||||
val decoderCalls: List<Pair<Int, IrExpression>> =
|
||||
serializableProperties.mapIndexed { index, property ->
|
||||
val body = irBlock {
|
||||
val decodeFuncToCall =
|
||||
formEncodeDecodePropertyCall(localInput.get(), loadFunc.dispatchReceiverParameter!!, property, { innerSerial, sti ->
|
||||
inputClass.functions.single {
|
||||
it.owner.name.asString() == "${CallingConventions.decode}${sti.elementMethodPrefix}Serializable${CallingConventions.elementPostfix}" &&
|
||||
it.owner.valueParameters.size == 4
|
||||
} to listOf(
|
||||
localSerialDesc.get(), irInt(index), innerSerial, serialPropertiesMap.getValue(property.ir).get()
|
||||
)
|
||||
}, { sti ->
|
||||
inputClass.functions.single {
|
||||
it.owner.name.asString() == "${CallingConventions.decode}${sti.elementMethodPrefix}${CallingConventions.elementPostfix}" &&
|
||||
it.owner.valueParameters.size == 2
|
||||
} to listOf(localSerialDesc.get(), irInt(index))
|
||||
}, returnTypeHint = property.type)
|
||||
// local$i = localInput.decode...(...)
|
||||
+irSet(
|
||||
serialPropertiesMap.getValue(property.ir).symbol,
|
||||
decodeFuncToCall
|
||||
)
|
||||
// bitMask[i] |= 1 << x
|
||||
val bitPos = 1 shl (index % 32)
|
||||
val or = irBinOp(OperatorNameConventions.OR, bitMasks[index / 32].get(), irInt(bitPos))
|
||||
+irSet(bitMasks[index / 32].symbol, or)
|
||||
}
|
||||
index to body
|
||||
}
|
||||
|
||||
// if (decoder.decodeSequentially())
|
||||
val decodeSequentiallyCall = irInvoke(localInput.get(), inputClass.functionByName(CallingConventions.decodeSequentially))
|
||||
|
||||
val sequentialPart = irBlock {
|
||||
decoderCalls.forEach { (_, expr) -> +expr.deepCopyWithVariables() }
|
||||
}
|
||||
|
||||
val byIndexPart: IrExpression = irWhile().also { loop ->
|
||||
loop.condition = flagVar.get()
|
||||
loop.body = irBlock {
|
||||
val readElementF = inputClass.functionByName(CallingConventions.decodeElementIndex)
|
||||
+irSet(indexVar.symbol, irInvoke(localInput.get(), readElementF, localSerialDesc.get()))
|
||||
+irWhen {
|
||||
// if index == -1 (READ_DONE) break loop
|
||||
+IrBranchImpl(irEquals(indexVar.get(), irInt(-1)), irSet(flagVar.symbol, irBoolean(false)))
|
||||
|
||||
decoderCalls.forEach { (i, e) -> +IrBranchImpl(irEquals(indexVar.get(), irInt(i)), e) }
|
||||
|
||||
// throw exception on unknown field
|
||||
|
||||
val excClassRef = compilerContext.referenceConstructors(
|
||||
ClassId(
|
||||
SerializationPackages.packageFqName,
|
||||
Name.identifier(UNKNOWN_FIELD_EXC)
|
||||
)
|
||||
)
|
||||
.single { it.owner.valueParameters.singleOrNull()?.type?.isInt() == true }
|
||||
+elseBranch(
|
||||
irThrow(
|
||||
irInvoke(
|
||||
null,
|
||||
excClassRef,
|
||||
indexVar.get()
|
||||
)
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+irIfThenElse(compilerContext.irBuiltIns.unitType, decodeSequentiallyCall, sequentialPart, byIndexPart)
|
||||
|
||||
//input.endStructure(...)
|
||||
val endFunc = inputClass.functionByName(CallingConventions.end)
|
||||
+irInvoke(
|
||||
localInput.get(),
|
||||
endFunc,
|
||||
irGet(localSerialDesc)
|
||||
)
|
||||
|
||||
val typeArgs = (loadFunc.returnType as IrSimpleType).arguments.map { (it as IrTypeProjection).type }
|
||||
val deserCtor: IrConstructorSymbol? = serializableIrClass.findSerializableSyntheticConstructor()
|
||||
if (serializableIrClass.isInternalSerializable && deserCtor != null) {
|
||||
var args: List<IrExpression> = serializableProperties.map { serialPropertiesMap.getValue(it.ir).get() }
|
||||
args = bitMasks.map { irGet(it) } + args + irNull()
|
||||
+irReturn(irInvoke(null, deserCtor, typeArgs, args))
|
||||
} else {
|
||||
if (irClass.isLocal) {
|
||||
// if the serializer is local, then the serializable class too, since they must be in the same scope
|
||||
throw CompilationException(
|
||||
"External serializer class `${irClass.fqNameWhenAvailable}` is local. Local external serializers are not supported yet.",
|
||||
null,
|
||||
null
|
||||
)
|
||||
}
|
||||
|
||||
generateGoldenMaskCheck(bitMasks, properties, localSerialDesc.get())
|
||||
|
||||
val ctor: IrConstructorSymbol = serializableIrClass.constructors.primary.symbol
|
||||
val params = ctor.owner.valueParameters
|
||||
|
||||
val variableByParamReplacer: (ValueParameterDescriptor) -> IrExpression? = { vpd ->
|
||||
val propertyDescriptor = serializableIrClass.properties.find { it.name == vpd.name }
|
||||
if (propertyDescriptor != null) {
|
||||
val serializable = serialPropertiesMap[propertyDescriptor]
|
||||
(serializable ?: transientsPropertiesMap[propertyDescriptor])?.get()
|
||||
} else {
|
||||
null
|
||||
}
|
||||
}
|
||||
val initializerAdapter: (IrExpressionBody) -> IrExpression =
|
||||
createInitializerAdapter(serializableIrClass, variableByParamReplacer)
|
||||
|
||||
// constructor args:
|
||||
val ctorArgs = params.map { parameter ->
|
||||
val propertyDescriptor = serializableIrClass.properties.find { it.name == parameter.name }!!
|
||||
val serialProperty = serialPropertiesMap[propertyDescriptor]
|
||||
|
||||
// null if transient
|
||||
if (serialProperty != null) {
|
||||
val index = serialPropertiesIndexes.getValue(propertyDescriptor)
|
||||
if (parameter.hasDefaultValue()) {
|
||||
val propNotSeenTest =
|
||||
irEquals(
|
||||
irInt(0),
|
||||
irBinOp(
|
||||
OperatorNameConventions.AND,
|
||||
bitMasks[index / 32].get(),
|
||||
irInt(1 shl (index % 32))
|
||||
)
|
||||
)
|
||||
|
||||
// if(mask$j && propertyMask == 0) local$i = <initializer>
|
||||
val defaultValueExp = parameter.defaultValue!!
|
||||
val expr = initializerAdapter(defaultValueExp)
|
||||
+irIfThen(propNotSeenTest, irSet(serialProperty.symbol, expr))
|
||||
}
|
||||
serialProperty.get()
|
||||
} else {
|
||||
val transientVar = transientsPropertiesMap.getValue(propertyDescriptor)
|
||||
if (parameter.hasDefaultValue()) {
|
||||
val defaultValueExp = parameter.defaultValue!!
|
||||
val expr = initializerAdapter(defaultValueExp)
|
||||
+irSet(transientVar.symbol, expr)
|
||||
}
|
||||
transientVar.get()
|
||||
}
|
||||
}
|
||||
|
||||
val serializerVar = irTemporary(irInvoke(null, ctor, typeArgs, ctorArgs), "serializable")
|
||||
generateSetStandaloneProperties(serializerVar, serialPropertiesMap::getValue, serialPropertiesIndexes::getValue, bitMasks)
|
||||
+irReturn(irGet(serializerVar))
|
||||
}
|
||||
}
|
||||
|
||||
private fun IrBlockBodyBuilder.generateSetStandaloneProperties(
|
||||
serializableVar: IrVariable,
|
||||
propVars: (IrProperty) -> IrVariable,
|
||||
propIndexes: (IrProperty) -> Int,
|
||||
bitMasks: List<IrVariable>
|
||||
) {
|
||||
for (property in properties.serializableStandaloneProperties) {
|
||||
val localPropIndex = propIndexes(property.ir)
|
||||
// generate setter call
|
||||
val setter = property.ir.setter!!
|
||||
val propSeenTest =
|
||||
irNotEquals(
|
||||
irInt(0),
|
||||
irBinOp(
|
||||
OperatorNameConventions.AND,
|
||||
irGet(bitMasks[localPropIndex / 32]),
|
||||
irInt(1 shl (localPropIndex % 32))
|
||||
)
|
||||
)
|
||||
|
||||
val setterInvokeExpr = irSet(setter.returnType, irGet(serializableVar), setter.symbol, irGet(propVars(property.ir)))
|
||||
|
||||
+irIfThen(propSeenTest, setterInvokeExpr)
|
||||
}
|
||||
}
|
||||
|
||||
fun generate() {
|
||||
val prop = generatedSerialDescPropertyDescriptor?.let { generateSerializableClassProperty(it); true } ?: false
|
||||
if (prop)
|
||||
generateSerialDesc()
|
||||
val save = irClass.findPluginGeneratedMethod(SAVE)?.let { generateSave(it); true } ?: false
|
||||
val load = irClass.findPluginGeneratedMethod(LOAD)?.let { generateLoad(it); true } ?: false
|
||||
irClass.findPluginGeneratedMethod(SerialEntityNames.CHILD_SERIALIZERS_GETTER.identifier)?.let { generateChildSerializersGetter(it) }
|
||||
irClass.findPluginGeneratedMethod(SerialEntityNames.TYPE_PARAMS_SERIALIZERS_GETTER.identifier)
|
||||
?.let { generateTypeParamsSerializersGetter(it) }
|
||||
if (!prop && (save || load))
|
||||
generateSerialDesc()
|
||||
if (serializableIrClass.typeParameters.isNotEmpty()) {
|
||||
findSerializerConstructorForTypeArgumentsSerializers(irClass)?.takeIf { it.owner.isFromPlugin() }?.let {
|
||||
generateGenericFieldsAndConstructor(it.owner)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
companion object {
|
||||
fun generate(
|
||||
irClass: IrClass,
|
||||
context: SerializationPluginContext,
|
||||
metadataPlugin: SerializationDescriptorSerializerPlugin?,
|
||||
) {
|
||||
val serializableDesc = context.getSerializableClassDescriptorBySerializer(irClass) ?: return
|
||||
val generator = when {
|
||||
serializableDesc.isEnumWithLegacyGeneratedSerializer(context) -> SerializerForEnumsGenerator(
|
||||
irClass,
|
||||
context
|
||||
)
|
||||
serializableDesc.isValue -> SerializerForInlineClassGenerator(irClass, context)
|
||||
else -> SerializerIrGenerator(irClass, context, metadataPlugin)
|
||||
}
|
||||
generator.generate()
|
||||
irClass.addDefaultConstructorIfAbsent(context)
|
||||
irClass.patchDeclarationParents(irClass.parent)
|
||||
}
|
||||
}
|
||||
}
|
||||
+251
@@ -0,0 +1,251 @@
|
||||
/*
|
||||
* Copyright 2010-2022 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlinx.serialization.compiler.backend.ir
|
||||
|
||||
import org.jetbrains.kotlin.descriptors.ClassKind
|
||||
import org.jetbrains.kotlin.descriptors.Modality
|
||||
import org.jetbrains.kotlin.ir.declarations.IrClass
|
||||
import org.jetbrains.kotlin.ir.expressions.IrClassReference
|
||||
import org.jetbrains.kotlin.ir.expressions.IrConstructorCall
|
||||
import org.jetbrains.kotlin.ir.symbols.IrClassSymbol
|
||||
import org.jetbrains.kotlin.ir.types.*
|
||||
import org.jetbrains.kotlin.ir.util.*
|
||||
import org.jetbrains.kotlin.name.ClassId
|
||||
import org.jetbrains.kotlin.name.FqName
|
||||
import org.jetbrains.kotlin.name.Name
|
||||
import org.jetbrains.kotlinx.serialization.compiler.backend.common.findStandardKotlinTypeSerializer
|
||||
import org.jetbrains.kotlinx.serialization.compiler.backend.jvm.*
|
||||
import org.jetbrains.kotlinx.serialization.compiler.extensions.SerializationPluginContext
|
||||
import org.jetbrains.kotlinx.serialization.compiler.resolve.SerialEntityNames
|
||||
import org.jetbrains.kotlinx.serialization.compiler.resolve.SerializationAnnotations
|
||||
import org.jetbrains.kotlinx.serialization.compiler.resolve.SerializationPackages
|
||||
import org.jetbrains.kotlinx.serialization.compiler.resolve.SpecialBuiltins
|
||||
|
||||
class IrSerialTypeInfo(
|
||||
val property: IrSerializableProperty,
|
||||
val elementMethodPrefix: String,
|
||||
val serializer: IrClassSymbol? = null
|
||||
)
|
||||
|
||||
fun BaseIrGenerator.getIrSerialTypeInfo(property: IrSerializableProperty, ctx: SerializationPluginContext): IrSerialTypeInfo {
|
||||
fun SerializableInfo(serializer: IrClassSymbol?) =
|
||||
IrSerialTypeInfo(property, if (property.type.isNullable()) "Nullable" else "", serializer)
|
||||
|
||||
val T = property.type
|
||||
property.serializableWith(ctx)?.let { return SerializableInfo(it) }
|
||||
findAddOnSerializer(T, ctx)?.let { return SerializableInfo(it) }
|
||||
T.overridenSerializer?.let { return SerializableInfo(it) }
|
||||
return when {
|
||||
T.isTypeParameter() -> IrSerialTypeInfo(property, if (property.type.isMarkedNullable()) "Nullable" else "", null)
|
||||
T.isPrimitiveType() -> IrSerialTypeInfo(
|
||||
property,
|
||||
T.classFqName!!.asString().removePrefix("kotlin.")
|
||||
)
|
||||
T.isString() -> IrSerialTypeInfo(property, "String")
|
||||
T.isArray() -> {
|
||||
val serializer = property.serializableWith(ctx) ?: ctx.getClassFromInternalSerializationPackage(SpecialBuiltins.referenceArraySerializer)
|
||||
SerializableInfo(serializer)
|
||||
}
|
||||
else -> {
|
||||
val serializer =
|
||||
findTypeSerializerOrContext(ctx, property.type)
|
||||
SerializableInfo(serializer)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun BaseIrGenerator.findAddOnSerializer(propertyType: IrType, ctx: SerializationPluginContext): IrClassSymbol? {
|
||||
val classSymbol = propertyType.classOrNull ?: return null
|
||||
additionalSerializersInScopeOfCurrentFile[classSymbol to propertyType.isNullable()]?.let { return it }
|
||||
if (classSymbol in contextualKClassListInCurrentFile)
|
||||
return ctx.getClassFromRuntime(SpecialBuiltins.contextSerializer)
|
||||
if (classSymbol.owner.annotations.hasAnnotation(SerializationAnnotations.polymorphicFqName))
|
||||
return ctx.getClassFromRuntime(SpecialBuiltins.polymorphicSerializer)
|
||||
if (propertyType.isNullable()) return findAddOnSerializer(propertyType.makeNotNull(), ctx)
|
||||
return null
|
||||
}
|
||||
|
||||
fun BaseIrGenerator.findTypeSerializerOrContext(
|
||||
context: SerializationPluginContext, kType: IrType
|
||||
): IrClassSymbol? {
|
||||
if (kType.isTypeParameter()) return null
|
||||
return findTypeSerializerOrContextUnchecked(context, kType) ?: error("Serializer for element of type ${kType.render()} has not been found")
|
||||
}
|
||||
|
||||
fun BaseIrGenerator.findTypeSerializerOrContextUnchecked(
|
||||
context: SerializationPluginContext, kType: IrType
|
||||
): IrClassSymbol? {
|
||||
val annotations = kType.annotations
|
||||
if (kType.isTypeParameter()) return null
|
||||
annotations.serializableWith()?.let { return it }
|
||||
additionalSerializersInScopeOfCurrentFile[kType.classOrNull!! to kType.isNullable()]?.let {
|
||||
return it
|
||||
}
|
||||
if (kType.isMarkedNullable()) return findTypeSerializerOrContextUnchecked(context, kType.makeNotNull())
|
||||
if (kType.classOrNull in contextualKClassListInCurrentFile) return context.referenceClass(contextSerializerId)
|
||||
return analyzeSpecialSerializers(context, annotations) ?: findTypeSerializer(context, kType)
|
||||
}
|
||||
|
||||
fun analyzeSpecialSerializers(
|
||||
context: SerializationPluginContext,
|
||||
annotations: List<IrConstructorCall>
|
||||
): IrClassSymbol? = when {
|
||||
annotations.hasAnnotation(SerializationAnnotations.contextualFqName) || annotations.hasAnnotation(SerializationAnnotations.contextualOnPropertyFqName) ->
|
||||
context.referenceClass(contextSerializerId)
|
||||
// can be annotation on type usage, e.g. List<@Polymorphic Any>
|
||||
annotations.hasAnnotation(SerializationAnnotations.polymorphicFqName) ->
|
||||
context.referenceClass(polymorphicSerializerId)
|
||||
else -> null
|
||||
}
|
||||
|
||||
|
||||
fun findTypeSerializer(context: SerializationPluginContext, type: IrType): IrClassSymbol? {
|
||||
type.overridenSerializer?.let { return it }
|
||||
if (type.isTypeParameter()) return null
|
||||
if (type.isArray()) return context.referenceClass(referenceArraySerializerId)
|
||||
if (type.isGeneratedSerializableObject()) return context.referenceClass(objectSerializerId)
|
||||
val stdSer = findStandardKotlinTypeSerializer(context, type) // see if there is a standard serializer
|
||||
?: findEnumTypeSerializer(context, type)
|
||||
if (stdSer != null) return stdSer
|
||||
if (type.isInterface() && type.classOrNull?.owner?.isSealedSerializableInterface == false) return context.referenceClass(
|
||||
polymorphicSerializerId
|
||||
)
|
||||
return type.classOrNull?.owner.classSerializer(context) // check for serializer defined on the type
|
||||
}
|
||||
fun findEnumTypeSerializer(context: SerializationPluginContext, type: IrType): IrClassSymbol? {
|
||||
val classSymbol = type.classOrNull?.owner ?: return null
|
||||
return if (classSymbol.kind == ClassKind.ENUM_CLASS && !classSymbol.isEnumWithLegacyGeneratedSerializer(context))
|
||||
context.referenceClass(enumSerializerId)
|
||||
else null
|
||||
}
|
||||
|
||||
internal fun IrClass?.classSerializer(context: SerializationPluginContext): IrClassSymbol? = this?.let {
|
||||
// serializer annotation on class?
|
||||
serializableWith?.let { return it }
|
||||
// companion object serializer?
|
||||
if (hasCompanionObjectAsSerializer) return companionObject()?.symbol
|
||||
// can infer @Poly?
|
||||
polymorphicSerializerIfApplicableAutomatically(context)?.let { return it }
|
||||
// default serializable?
|
||||
if (shouldHaveGeneratedSerializer(context)) {
|
||||
// $serializer nested class
|
||||
return this.declarations
|
||||
.filterIsInstance<IrClass>()
|
||||
.singleOrNull { it.name == SerialEntityNames.SERIALIZER_CLASS_NAME }?.symbol
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
||||
internal fun IrClass.polymorphicSerializerIfApplicableAutomatically(context: SerializationPluginContext): IrClassSymbol? {
|
||||
val serializer = when {
|
||||
kind == ClassKind.INTERFACE && modality == Modality.SEALED -> SpecialBuiltins.sealedSerializer
|
||||
kind == ClassKind.INTERFACE -> SpecialBuiltins.polymorphicSerializer
|
||||
isInternalSerializable && modality == Modality.ABSTRACT -> SpecialBuiltins.polymorphicSerializer
|
||||
isInternalSerializable && modality == Modality.SEALED -> SpecialBuiltins.sealedSerializer
|
||||
else -> null
|
||||
}
|
||||
return serializer?.let {
|
||||
context.getClassFromRuntimeOrNull(
|
||||
it,
|
||||
SerializationPackages.packageFqName,
|
||||
SerializationPackages.internalPackageFqName
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
internal val IrType.overridenSerializer: IrClassSymbol?
|
||||
get() {
|
||||
val desc = this.classOrNull ?: return null
|
||||
desc.owner.serializableWith?.let { return it }
|
||||
return null
|
||||
}
|
||||
|
||||
internal val IrClass.serializableWith: IrClassSymbol?
|
||||
get() = annotations.serializableWith()
|
||||
|
||||
internal val IrClass.serializerForClass: IrClassSymbol?
|
||||
get() = (annotations.findAnnotation(SerializationAnnotations.serializerAnnotationFqName)
|
||||
?.getValueArgument(0) as? IrClassReference)?.symbol as? IrClassSymbol
|
||||
|
||||
fun findStandardKotlinTypeSerializer(context: SerializationPluginContext, type: IrType): IrClassSymbol? {
|
||||
val typeName = type.classFqName?.toString()
|
||||
val name = when (typeName) {
|
||||
"Z" -> if (type.isBoolean()) "BooleanSerializer" else null
|
||||
"B" -> if (type.isByte()) "ByteSerializer" else null
|
||||
"S" -> if (type.isShort()) "ShortSerializer" else null
|
||||
"I" -> if (type.isInt()) "IntSerializer" else null
|
||||
"J" -> if (type.isLong()) "LongSerializer" else null
|
||||
"F" -> if (type.isFloat()) "FloatSerializer" else null
|
||||
"D" -> if (type.isDouble()) "DoubleSerializer" else null
|
||||
"C" -> if (type.isChar()) "CharSerializer" else null
|
||||
null -> null
|
||||
else -> findStandardKotlinTypeSerializer(typeName)
|
||||
} ?: return null
|
||||
return context.getClassFromRuntimeOrNull(name, SerializationPackages.internalPackageFqName, SerializationPackages.packageFqName)
|
||||
}
|
||||
|
||||
// @Serializable(X::class) -> X
|
||||
internal fun List<IrConstructorCall>.serializableWith(): IrClassSymbol? {
|
||||
val annotation = findAnnotation(SerializationAnnotations.serializableAnnotationFqName) ?: return null
|
||||
val arg = annotation.getValueArgument(0) as? IrClassReference ?: return null
|
||||
return arg.symbol as? IrClassSymbol
|
||||
}
|
||||
|
||||
internal fun getSerializableClassByCompanion(companionClass: IrClass): IrClass? {
|
||||
if (companionClass.isSerializableObject) return companionClass
|
||||
if (!companionClass.isCompanion) return null
|
||||
val classDescriptor = (companionClass.parent as? IrClass) ?: return null
|
||||
if (!classDescriptor.shouldHaveGeneratedMethodsInCompanion) return null
|
||||
return classDescriptor
|
||||
}
|
||||
|
||||
fun BaseIrGenerator.allSealedSerializableSubclassesFor(
|
||||
irClass: IrClass,
|
||||
context: SerializationPluginContext
|
||||
): Pair<List<IrSimpleType>, List<IrClassSymbol>> {
|
||||
assert(irClass.modality == Modality.SEALED)
|
||||
fun recursiveSealed(klass: IrClass): Collection<IrClass> {
|
||||
return klass.sealedSubclasses.map { it.owner }.flatMap { if (it.modality == Modality.SEALED) recursiveSealed(it) else setOf(it) }
|
||||
}
|
||||
|
||||
val serializableSubtypes = recursiveSealed(irClass).map { it.defaultType }
|
||||
return serializableSubtypes.mapNotNull { subtype ->
|
||||
findTypeSerializerOrContextUnchecked(context, subtype)?.let { Pair(subtype, it) }
|
||||
}.unzip()
|
||||
}
|
||||
|
||||
internal fun SerializationPluginContext.getSerializableClassDescriptorBySerializer(serializer: IrClass): IrClass? {
|
||||
val serializerForClass = serializer.serializerForClass
|
||||
if (serializerForClass != null) return serializerForClass.owner
|
||||
if (serializer.name !in setOf(
|
||||
SerialEntityNames.SERIALIZER_CLASS_NAME,
|
||||
SerialEntityNames.GENERATED_SERIALIZER_CLASS
|
||||
)
|
||||
) return null
|
||||
val classDescriptor = (serializer.parent as? IrClass) ?: return null
|
||||
if (!classDescriptor.shouldHaveGeneratedSerializer(this)) return null
|
||||
return classDescriptor
|
||||
}
|
||||
|
||||
fun SerializationPluginContext.getClassFromRuntimeOrNull(className: String, vararg packages: FqName): IrClassSymbol? {
|
||||
val listToSearch = if (packages.isEmpty()) SerializationPackages.allPublicPackages else packages.toList()
|
||||
for (pkg in listToSearch) {
|
||||
referenceClass(ClassId(pkg, Name.identifier(className)))?.let { return it }
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
||||
fun SerializationPluginContext.getClassFromRuntime(className: String, vararg packages: FqName): IrClassSymbol {
|
||||
return getClassFromRuntimeOrNull(className, *packages) ?: error(
|
||||
"Class $className wasn't found in ${packages.toList().ifEmpty { SerializationPackages.allPublicPackages }}. " +
|
||||
"Check that you have correct version of serialization runtime in classpath."
|
||||
)
|
||||
}
|
||||
|
||||
fun SerializationPluginContext.getClassFromInternalSerializationPackage(className: String): IrClassSymbol =
|
||||
getClassFromRuntimeOrNull(className, SerializationPackages.internalPackageFqName)
|
||||
?: error("Class $className wasn't found in ${SerializationPackages.internalPackageFqName}. Check that you have correct version of serialization runtime in classpath.")
|
||||
|
||||
+289
@@ -0,0 +1,289 @@
|
||||
/*
|
||||
* Copyright 2010-2020 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlinx.serialization.compiler.backend.js
|
||||
|
||||
import org.jetbrains.kotlin.builtins.StandardNames
|
||||
import org.jetbrains.kotlin.descriptors.*
|
||||
import org.jetbrains.kotlin.js.backend.ast.*
|
||||
import org.jetbrains.kotlin.js.resolve.diagnostics.findPsi
|
||||
import org.jetbrains.kotlin.js.translate.context.TranslationContext
|
||||
import org.jetbrains.kotlin.js.translate.expression.ExpressionVisitor
|
||||
import org.jetbrains.kotlin.js.translate.expression.translateAndAliasParameters
|
||||
import org.jetbrains.kotlin.js.translate.general.Translation
|
||||
import org.jetbrains.kotlin.js.translate.reference.ReferenceTranslator
|
||||
import org.jetbrains.kotlin.js.translate.utils.JsAstUtils
|
||||
import org.jetbrains.kotlin.psi.KtExpression
|
||||
import org.jetbrains.kotlin.psi.KtPureClassOrObject
|
||||
import org.jetbrains.kotlin.resolve.DescriptorUtils
|
||||
import org.jetbrains.kotlin.resolve.descriptorUtil.classId
|
||||
import org.jetbrains.kotlin.resolve.descriptorUtil.getSuperClassNotAny
|
||||
import org.jetbrains.kotlin.types.KotlinType
|
||||
import org.jetbrains.kotlin.types.typeUtil.isTypeParameter
|
||||
import org.jetbrains.kotlin.types.typeUtil.representativeUpperBound
|
||||
import org.jetbrains.kotlinx.serialization.compiler.backend.common.*
|
||||
import org.jetbrains.kotlinx.serialization.compiler.backend.jvm.*
|
||||
import org.jetbrains.kotlinx.serialization.compiler.resolve.*
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
internal fun propNotSeenTest(seenVar: JsNameRef, index: Int): JsBinaryOperation = JsAstUtils.equality(
|
||||
JsBinaryOperation(
|
||||
JsBinaryOperator.BIT_AND,
|
||||
seenVar,
|
||||
JsIntLiteral(1 shl (index % 32))
|
||||
),
|
||||
JsIntLiteral(0)
|
||||
)
|
||||
|
||||
internal fun TranslationContext.serializerObjectGetter(serializer: ClassDescriptor): JsExpression {
|
||||
return ReferenceTranslator.translateAsValueReference(serializer, this)
|
||||
}
|
||||
|
||||
internal fun TranslationContext.translateQualifiedReference(clazz: ClassDescriptor): JsExpression {
|
||||
return ReferenceTranslator.translateAsTypeReference(clazz, this)
|
||||
}
|
||||
|
||||
// Does not use sti and therefore does not perform encoder calls optimization
|
||||
internal fun SerializerJsTranslator.serializerTower(property: SerializableProperty): JsExpression? {
|
||||
val nullableSerClass =
|
||||
context.translateQualifiedReference(property.module.getClassFromInternalSerializationPackage(SpecialBuiltins.nullableSerializer))
|
||||
val serializer =
|
||||
property.serializableWith?.toClassDescriptor
|
||||
?: if (!property.type.isTypeParameter()) findTypeSerializerOrContext(
|
||||
property.module,
|
||||
property.type,
|
||||
property.descriptor.findPsi()
|
||||
) else null
|
||||
return serializerInstance(context, serializer, property.module, property.type, property.genericIndex)
|
||||
?.let { expr -> if (property.type.isMarkedNullable) JsNew(nullableSerClass, listOf(expr)) else expr }
|
||||
}
|
||||
|
||||
internal fun AbstractSerialGenerator.serializerInstance(
|
||||
context: TranslationContext,
|
||||
serializerClass: ClassDescriptor?,
|
||||
module: ModuleDescriptor,
|
||||
kType: KotlinType,
|
||||
genericIndex: Int? = null,
|
||||
genericGetter: (Int, KotlinType) -> JsExpression = { it, _ ->
|
||||
JsNameRef(
|
||||
context.scope().declareName("${SerialEntityNames.typeArgPrefix}$it"),
|
||||
JsThisRef()
|
||||
)
|
||||
}
|
||||
): JsExpression? {
|
||||
val nullableSerClass =
|
||||
context.translateQualifiedReference(module.getClassFromInternalSerializationPackage(SpecialBuiltins.nullableSerializer))
|
||||
if (serializerClass == null) {
|
||||
if (genericIndex == null) return null
|
||||
return genericGetter(genericIndex, kType)
|
||||
}
|
||||
if (serializerClass.kind == ClassKind.OBJECT) {
|
||||
return context.serializerObjectGetter(serializerClass)
|
||||
}
|
||||
val hasNewCtxSerCtor =
|
||||
serializerClass.classId == contextSerializerId && serializerClass.constructors.any { it.valueParameters.size == 3 }
|
||||
|
||||
fun instantiate(serializer: ClassDescriptor?, type: KotlinType): JsExpression? {
|
||||
val expr = serializerInstance(context, serializer, module, type, type.genericIndex, genericGetter) ?: return null
|
||||
return if (type.isMarkedNullable) JsNew(nullableSerClass, listOf(expr)) else expr
|
||||
}
|
||||
|
||||
var args = when {
|
||||
hasNewCtxSerCtor -> {
|
||||
mutableListOf<JsExpression>().apply {
|
||||
add(ExpressionVisitor.getObjectKClass(context, kType.toClassDescriptor!!))
|
||||
val fallbackDefaultSerializer = findTypeSerializer(module, kType)
|
||||
add(instantiate(fallbackDefaultSerializer, kType) ?: JsNullLiteral())
|
||||
add(JsArrayLiteral(kType.arguments.map {
|
||||
val argSer = findTypeSerializerOrContext(module, it.type, sourceElement = serializerClass.findPsi())
|
||||
instantiate(argSer, it.type)!!
|
||||
}))
|
||||
}
|
||||
}
|
||||
serializerClass.classId == contextSerializerId || serializerClass.classId == polymorphicSerializerId -> listOf(
|
||||
ExpressionVisitor.getObjectKClass(context, kType.toClassDescriptor!!)
|
||||
)
|
||||
serializerClass.classId == enumSerializerId -> {
|
||||
val enumDescriptor = kType.toClassDescriptor!!
|
||||
|
||||
val enumArgs = mutableListOf(
|
||||
JsStringLiteral(enumDescriptor.serialName()),
|
||||
// EnumClass.values() invocation
|
||||
JsInvocation(
|
||||
context.getInnerNameForDescriptor(
|
||||
DescriptorUtils.getFunctionByName(
|
||||
enumDescriptor.staticScope,
|
||||
StandardNames.ENUM_VALUES
|
||||
)
|
||||
).makeRef()
|
||||
)
|
||||
)
|
||||
|
||||
val packageScope = context.currentModule.getPackage(SerializationPackages.internalPackageFqName).memberScope
|
||||
val enumSerializerFactoryFunc = DescriptorUtils.getFunctionByNameOrNull(
|
||||
packageScope,
|
||||
SerialEntityNames.ENUM_SERIALIZER_FACTORY_FUNC_NAME
|
||||
)
|
||||
val markedEnumSerializerFactoryFunc = DescriptorUtils.getFunctionByNameOrNull(
|
||||
packageScope,
|
||||
SerialEntityNames.MARKED_ENUM_SERIALIZER_FACTORY_FUNC_NAME
|
||||
)
|
||||
if (enumSerializerFactoryFunc != null && markedEnumSerializerFactoryFunc != null) {
|
||||
// runtime contains enum serializer factory functions
|
||||
val factoryFunc = if (enumDescriptor.isEnumWithSerialInfoAnnotation()) {
|
||||
val enumEntries = enumDescriptor.enumEntries()
|
||||
val entriesNames =
|
||||
enumEntries.map { it.annotations.serialNameValue?.let { n -> JsStringLiteral(n) } ?: JsNullLiteral() }
|
||||
|
||||
val entriesAnnotations = enumEntries.map {
|
||||
val annotationsConstructors = it.annotationsWithArguments().map { (annotationClass, args, _) ->
|
||||
val argExprs = args.map { arg ->
|
||||
Translation.translateAsExpression(arg.getArgumentExpression()!!, context)
|
||||
}
|
||||
val classRef = context.translateQualifiedReference(annotationClass)
|
||||
JsNew(classRef, argExprs)
|
||||
}
|
||||
|
||||
if (annotationsConstructors.isEmpty()) {
|
||||
JsNullLiteral()
|
||||
} else {
|
||||
JsArrayLiteral(annotationsConstructors)
|
||||
}
|
||||
}
|
||||
enumArgs += JsArrayLiteral(entriesNames)
|
||||
enumArgs += JsArrayLiteral(entriesAnnotations)
|
||||
markedEnumSerializerFactoryFunc
|
||||
} else {
|
||||
enumSerializerFactoryFunc
|
||||
}
|
||||
return JsInvocation(context.getInnerReference(factoryFunc), enumArgs)
|
||||
} else {
|
||||
// support legacy serializer instantiation by constructor for old runtimes
|
||||
enumArgs
|
||||
}
|
||||
}
|
||||
serializerClass.classId == objectSerializerId -> listOf(
|
||||
JsStringLiteral(kType.serialName()),
|
||||
context.serializerObjectGetter(kType.toClassDescriptor!!)
|
||||
)
|
||||
serializerClass.classId == sealedSerializerId -> mutableListOf<JsExpression>().apply {
|
||||
add(JsStringLiteral(kType.serialName()))
|
||||
add(ExpressionVisitor.getObjectKClass(context, kType.toClassDescriptor!!))
|
||||
val (subclasses, subSerializers) = allSealedSerializableSubclassesFor(
|
||||
kType.toClassDescriptor!!,
|
||||
module
|
||||
)
|
||||
add(JsArrayLiteral(subclasses.map {
|
||||
ExpressionVisitor.getObjectKClass(
|
||||
context,
|
||||
it.toClassDescriptor!!
|
||||
)
|
||||
}))
|
||||
add(JsArrayLiteral(subSerializers.mapIndexed { i, serializer ->
|
||||
val type = subclasses[i]
|
||||
val expr = serializerInstance(context, serializer, module, type, type.genericIndex) { _, genericType ->
|
||||
serializerInstance(
|
||||
context,
|
||||
module.getClassFromSerializationPackage(SpecialBuiltins.polymorphicSerializer),
|
||||
module,
|
||||
(genericType.constructor.declarationDescriptor as TypeParameterDescriptor).representativeUpperBound
|
||||
)!!
|
||||
}!!
|
||||
if (type.isMarkedNullable) JsNew(nullableSerClass, listOf(expr)) else expr
|
||||
}))
|
||||
}
|
||||
else -> kType.arguments.map {
|
||||
val argSer = findTypeSerializerOrContext(module, it.type, sourceElement = serializerClass.findPsi())
|
||||
instantiate(argSer, it.type) ?: return null
|
||||
}
|
||||
}
|
||||
if (serializerClass.classId == referenceArraySerializerId)
|
||||
args = listOf(ExpressionVisitor.getObjectKClass(context, kType.arguments[0].type.toClassDescriptor!!)) + args
|
||||
val serializable = getSerializableClassDescriptorBySerializer(serializerClass)
|
||||
val ref = if (serializable?.declaredTypeParameters?.isNotEmpty() == true) {
|
||||
val desc = requireNotNull(
|
||||
findSerializerConstructorForTypeArgumentsSerializers(serializerClass)
|
||||
) { "Generated serializer does not have constructor with required number of arguments" }
|
||||
if (!desc.isPrimary)
|
||||
JsInvocation(context.getInnerReference(desc), args)
|
||||
else
|
||||
JsNew(context.getInnerReference(desc), args)
|
||||
} else {
|
||||
JsNew(context.translateQualifiedReference(serializerClass), args)
|
||||
}
|
||||
return ref
|
||||
}
|
||||
|
||||
fun TranslationContext.buildInitializersRemapping(
|
||||
forClass: KtPureClassOrObject,
|
||||
superClass: ClassDescriptor?
|
||||
): Map<PropertyDescriptor, KtExpression?> {
|
||||
val myMap = (forClass.bodyPropertiesDescriptorsMap(bindingContext()).mapValues { it.value.delegateExpressionOrInitializer } +
|
||||
forClass.primaryConstructorPropertiesDescriptorsMap(bindingContext()).mapValues { it.value.defaultValue })
|
||||
val parentPsi = superClass?.takeIf { it.isInternalSerializable }?.findPsi() as? KtPureClassOrObject ?: return myMap
|
||||
val parentMap = buildInitializersRemapping(parentPsi, superClass.getSuperClassNotAny())
|
||||
return myMap + parentMap
|
||||
}
|
||||
+70
@@ -0,0 +1,70 @@
|
||||
/*
|
||||
* 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.ClassDescriptor
|
||||
import org.jetbrains.kotlin.descriptors.FunctionDescriptor
|
||||
import org.jetbrains.kotlin.js.backend.ast.JsNameRef
|
||||
import org.jetbrains.kotlin.js.backend.ast.JsReturn
|
||||
import org.jetbrains.kotlin.js.translate.context.TranslationContext
|
||||
import org.jetbrains.kotlin.js.translate.declaration.DeclarationBodyVisitor
|
||||
import org.jetbrains.kotlin.resolve.descriptorUtil.module
|
||||
import org.jetbrains.kotlinx.serialization.compiler.backend.common.SerializableCompanionCodegen
|
||||
import org.jetbrains.kotlinx.serialization.compiler.backend.common.findTypeSerializer
|
||||
import org.jetbrains.kotlinx.serialization.compiler.resolve.getSerializableClassDescriptorByCompanion
|
||||
import org.jetbrains.kotlinx.serialization.compiler.resolve.shouldHaveGeneratedMethodsInCompanion
|
||||
import org.jetbrains.kotlinx.serialization.compiler.resolve.toSimpleType
|
||||
|
||||
class SerializableCompanionJsTranslator(
|
||||
declaration: ClassDescriptor,
|
||||
val translator: DeclarationBodyVisitor,
|
||||
val context: TranslationContext
|
||||
) : SerializableCompanionCodegen(declaration, context.bindingContext()) {
|
||||
|
||||
override fun generateSerializerGetter(methodDescriptor: FunctionDescriptor) {
|
||||
val f = context.buildFunction(methodDescriptor) { jsFun, context ->
|
||||
val serializer = requireNotNull(
|
||||
findTypeSerializer(
|
||||
serializableDescriptor.module,
|
||||
serializableDescriptor.toSimpleType()
|
||||
)
|
||||
)
|
||||
val args = jsFun.parameters.map { JsNameRef(it.name) }
|
||||
val stmt =
|
||||
requireNotNull(
|
||||
serializerInstance(
|
||||
context,
|
||||
serializer,
|
||||
serializableDescriptor.module,
|
||||
serializableDescriptor.defaultType,
|
||||
genericGetter = { it, _ ->
|
||||
args[it]
|
||||
})
|
||||
)
|
||||
+JsReturn(stmt)
|
||||
}
|
||||
translator.addFunction(methodDescriptor, f, null)
|
||||
}
|
||||
|
||||
companion object {
|
||||
fun translate(descriptor: ClassDescriptor, translator: DeclarationBodyVisitor, context: TranslationContext) {
|
||||
val serializableClass = getSerializableClassDescriptorByCompanion(descriptor) ?: return
|
||||
if (serializableClass.shouldHaveGeneratedMethodsInCompanion)
|
||||
SerializableCompanionJsTranslator(descriptor, translator, context).generate()
|
||||
}
|
||||
}
|
||||
}
|
||||
+175
@@ -0,0 +1,175 @@
|
||||
/*
|
||||
* Copyright 2010-2020 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlinx.serialization.compiler.backend.js
|
||||
|
||||
import org.jetbrains.kotlin.builtins.KotlinBuiltIns
|
||||
import org.jetbrains.kotlin.codegen.CompilationException
|
||||
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.resolve.diagnostics.findPsi
|
||||
import org.jetbrains.kotlin.js.translate.context.Namer
|
||||
import org.jetbrains.kotlin.js.translate.context.TranslationContext
|
||||
import org.jetbrains.kotlin.js.translate.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.kotlin.resolve.descriptorUtil.getSuperClassNotAny
|
||||
import org.jetbrains.kotlin.resolve.descriptorUtil.getSuperClassOrAny
|
||||
import org.jetbrains.kotlinx.serialization.compiler.backend.common.SerializableCodegen
|
||||
import org.jetbrains.kotlinx.serialization.compiler.backend.common.anonymousInitializers
|
||||
import org.jetbrains.kotlinx.serialization.compiler.diagnostic.serializableAnnotationIsUseless
|
||||
import org.jetbrains.kotlinx.serialization.compiler.resolve.*
|
||||
import org.jetbrains.kotlinx.serialization.compiler.resolve.SerialEntityNames.MISSING_FIELD_EXC
|
||||
|
||||
class SerializableJsTranslator(
|
||||
val declaration: KtPureClassOrObject,
|
||||
val descriptor: ClassDescriptor,
|
||||
val context: TranslationContext
|
||||
) : SerializableCodegen(descriptor, context.bindingContext()) {
|
||||
|
||||
private val initMap: Map<PropertyDescriptor, KtExpression?> = context.buildInitializersRemapping(declaration, descriptor.getSuperClassNotAny())
|
||||
|
||||
override fun generateInternalConstructor(constructorDescriptor: ClassConstructorDescriptor) {
|
||||
|
||||
val missingExceptionClassRef = serializableDescriptor.getClassFromSerializationPackage(MISSING_FIELD_EXC)
|
||||
.constructors.single { it.valueParameters.size == 1 }
|
||||
|
||||
val f = context.buildFunction(constructorDescriptor) { jsFun, context ->
|
||||
val thiz = jsFun.scope.declareName(Namer.ANOTHER_THIS_PARAMETER_NAME).makeRef()
|
||||
@Suppress("NAME_SHADOWING")
|
||||
val context = context.innerContextWithAliased(serializableDescriptor.thisAsReceiverParameter, thiz)
|
||||
|
||||
// use serializationConstructorMarker for passing "this" from inheritors to base class
|
||||
val markerAsThis = jsFun.parameters.last().name.makeRef()
|
||||
|
||||
+JsVars(
|
||||
JsVars.JsVar(
|
||||
thiz.name,
|
||||
JsAstUtils.or(
|
||||
markerAsThis,
|
||||
Namer.createObjectWithPrototypeFrom(context.getInnerNameForDescriptor(serializableDescriptor).makeRef())
|
||||
)
|
||||
)
|
||||
)
|
||||
val serializableProperties = properties.serializableProperties
|
||||
val seenVarsOffset = serializableProperties.bitMaskSlotCount()
|
||||
val seenVars = (0 until seenVarsOffset).map { jsFun.parameters[it].name.makeRef() }
|
||||
val superClass = serializableDescriptor.getSuperClassOrAny()
|
||||
var startPropOffset: Int = 0
|
||||
when {
|
||||
KotlinBuiltIns.isAny(superClass) -> { /* no=op */ }
|
||||
superClass.isInternalSerializable -> {
|
||||
startPropOffset = generateSuperSerializableCall(
|
||||
superClass,
|
||||
jsFun.parameters.map { it.name.makeRef() },
|
||||
thiz,
|
||||
seenVarsOffset
|
||||
)
|
||||
}
|
||||
else -> generateSuperNonSerializableCall(superClass, thiz)
|
||||
}
|
||||
|
||||
for (index in startPropOffset until serializableProperties.size) {
|
||||
val prop = serializableProperties[index]
|
||||
val paramRef = jsFun.parameters[index + seenVarsOffset].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(
|
||||
if (missingExceptionClassRef.isPrimary) {
|
||||
JsNew(
|
||||
context.translateQualifiedReference(missingExceptionClassRef.containingDeclaration),
|
||||
listOf(JsStringLiteral(prop.name))
|
||||
)
|
||||
} else {
|
||||
JsInvocation(
|
||||
context.getInnerNameForDescriptor(missingExceptionClassRef).makeRef(),
|
||||
listOf(JsStringLiteral(prop.name))
|
||||
)
|
||||
}
|
||||
)
|
||||
}
|
||||
// (seen & 1 << i == 0) -- not seen
|
||||
val notSeenTest = propNotSeenTest(seenVars[bitMaskSlotAt(index)], index)
|
||||
+JsIf(notSeenTest, ifNotSeenStmt, assignParamStmt)
|
||||
}
|
||||
|
||||
//transient initializers and init blocks
|
||||
val serialDescs = 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())
|
||||
context.export(constructorDescriptor)
|
||||
}
|
||||
|
||||
private fun JsBlockBuilder.generateSuperNonSerializableCall(superClass: ClassDescriptor, thisParameter: JsExpression) {
|
||||
val suitableCtor = superClass.constructors.singleOrNull { it.valueParameters.size == 0 }
|
||||
?: throw IllegalArgumentException("Non-serializable parent of serializable $serializableDescriptor must have no arg constructor")
|
||||
if (suitableCtor.isPrimary) {
|
||||
+JsInvocation(Namer.getFunctionCallRef(context.getInnerReference(superClass)), thisParameter).makeStmt()
|
||||
} else {
|
||||
+JsAstUtils.assignment(thisParameter, JsInvocation(context.getInnerReference(suitableCtor), thisParameter)).makeStmt()
|
||||
}
|
||||
}
|
||||
|
||||
private fun JsBlockBuilder.generateSuperSerializableCall(
|
||||
superClass: ClassDescriptor,
|
||||
parameters: List<JsExpression>,
|
||||
thisParameter: JsExpression,
|
||||
propertiesStart: Int
|
||||
): Int {
|
||||
val constrDesc = superClass.constructors.single(ClassConstructorDescriptor::isSerializationCtor)
|
||||
val constrRef = context.getInnerNameForDescriptor(constrDesc).makeRef()
|
||||
val superProperties = bindingContext!!.serializablePropertiesFor(superClass).serializableProperties
|
||||
val superSlots = superProperties.bitMaskSlotCount()
|
||||
val arguments = parameters.subList(0, superSlots) +
|
||||
parameters.subList(propertiesStart, propertiesStart + superProperties.size) +
|
||||
thisParameter // SerializationConstructorMarker
|
||||
+JsAstUtils.assignment(thisParameter, JsInvocation(constrRef, arguments)).makeStmt()
|
||||
return superProperties.size
|
||||
}
|
||||
|
||||
override fun generateWriteSelfMethod(methodDescriptor: FunctionDescriptor) {
|
||||
// no-op yet
|
||||
}
|
||||
|
||||
companion object {
|
||||
fun translate(
|
||||
declaration: KtPureClassOrObject,
|
||||
serializableClass: ClassDescriptor,
|
||||
context: TranslationContext
|
||||
) {
|
||||
if (serializableClass.isInternalSerializable)
|
||||
SerializableJsTranslator(declaration, serializableClass, context).generate()
|
||||
else if (serializableClass.serializableAnnotationIsUseless) {
|
||||
throw CompilationException(
|
||||
"@Serializable annotation on $serializableClass would be ignored because it is impossible to serialize it automatically. " +
|
||||
"Provide serializer manually via e.g. companion object", null, serializableClass.findPsi()
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+80
@@ -0,0 +1,80 @@
|
||||
/*
|
||||
* Copyright 2010-2019 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlinx.serialization.compiler.backend.js
|
||||
|
||||
import org.jetbrains.kotlin.builtins.StandardNames
|
||||
import org.jetbrains.kotlin.descriptors.ClassDescriptor
|
||||
import org.jetbrains.kotlin.descriptors.FunctionDescriptor
|
||||
import org.jetbrains.kotlin.incremental.components.NoLookupLocation
|
||||
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.name.Name
|
||||
import org.jetbrains.kotlin.resolve.DescriptorUtils
|
||||
import org.jetbrains.kotlinx.serialization.compiler.resolve.*
|
||||
|
||||
class SerializerForEnumsTranslator(
|
||||
descriptor: ClassDescriptor,
|
||||
translator: DeclarationBodyVisitor,
|
||||
context: TranslationContext
|
||||
) : SerializerJsTranslator(descriptor, translator, context, null) {
|
||||
override fun generateSave(function: FunctionDescriptor) = generateFunction(function) { jsFun, ctx ->
|
||||
val encoderClass = serializerDescriptor.getClassFromSerializationPackage(SerialEntityNames.ENCODER_CLASS)
|
||||
val serialClassDescRef = JsNameRef(context.getNameForDescriptor(anySerialDescProperty!!), JsThisRef())
|
||||
val ordinalProp = serializableDescriptor.unsubstitutedMemberScope.getContributedVariables(
|
||||
Name.identifier("ordinal"),
|
||||
NoLookupLocation.FROM_BACKEND
|
||||
).single()
|
||||
val ordinalRef = JsNameRef(context.getNameForDescriptor(ordinalProp), JsNameRef(jsFun.parameters[1].name))
|
||||
val encodeEnumF = ctx.getNameForDescriptor(encoderClass.getFuncDesc(CallingConventions.encodeEnum).single())
|
||||
val call = JsInvocation(JsNameRef(encodeEnumF, JsNameRef(jsFun.parameters[0].name)), serialClassDescRef, ordinalRef)
|
||||
+call.makeStmt()
|
||||
}
|
||||
|
||||
override fun generateLoad(function: FunctionDescriptor) = generateFunction(function) { jsFun, ctx ->
|
||||
val decoderClass = serializerDescriptor.getClassFromSerializationPackage(SerialEntityNames.DECODER_CLASS)
|
||||
val serialClassDescRef = JsNameRef(context.getNameForDescriptor(anySerialDescProperty!!), JsThisRef())
|
||||
val decodeEnumF = ctx.getNameForDescriptor(decoderClass.getFuncDesc(CallingConventions.decodeEnum).single())
|
||||
val valuesFunc = DescriptorUtils.getFunctionByName(serializableDescriptor.staticScope, StandardNames.ENUM_VALUES)
|
||||
val decodeEnumCall = JsInvocation(JsNameRef(decodeEnumF, JsNameRef(jsFun.parameters[0].name)), serialClassDescRef)
|
||||
val resultCall = JsArrayAccess(JsInvocation(ctx.getInnerNameForDescriptor(valuesFunc).makeRef()), decodeEnumCall)
|
||||
+JsReturn(resultCall)
|
||||
}
|
||||
|
||||
override fun instantiateNewDescriptor(
|
||||
context: TranslationContext,
|
||||
correctThis: JsExpression,
|
||||
baseSerialDescImplClass: ClassDescriptor
|
||||
): JsExpression {
|
||||
val serialDescForEnums = serializerDescriptor
|
||||
.getClassFromInternalSerializationPackage(SerialEntityNames.SERIAL_DESCRIPTOR_FOR_ENUM)
|
||||
val ctor = serialDescForEnums.unsubstitutedPrimaryConstructor!!
|
||||
return JsNew(
|
||||
context.getInnerReference(ctor),
|
||||
listOf(JsStringLiteral(serialName), JsIntLiteral(serializableDescriptor.enumEntries().size))
|
||||
)
|
||||
}
|
||||
|
||||
override fun addElementsContentToDescriptor(
|
||||
context: TranslationContext,
|
||||
serialDescriptorInThis: JsNameRef,
|
||||
addElementFunction: FunctionDescriptor,
|
||||
pushAnnotationFunction: FunctionDescriptor
|
||||
) {
|
||||
val enumEntries = serializableDescriptor.enumEntries()
|
||||
for (entry in enumEntries) {
|
||||
// regular .serialName() produces fqName here, which is kinda inconvenient for enum entry
|
||||
val serialName = entry.annotations.serialNameValue ?: entry.name.toString()
|
||||
val call = JsInvocation(
|
||||
JsNameRef(context.getNameForDescriptor(addElementFunction), serialDescriptorInThis),
|
||||
JsStringLiteral(serialName)
|
||||
)
|
||||
translator.addInitializerStatement(call.makeStmt())
|
||||
// serialDesc.pushAnnotation(...)
|
||||
pushAnnotationsInto(entry, pushAnnotationFunction, serialDescriptorInThis)
|
||||
}
|
||||
}
|
||||
}
|
||||
+383
@@ -0,0 +1,383 @@
|
||||
/*
|
||||
* Copyright 2010-2020 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlinx.serialization.compiler.backend.js
|
||||
|
||||
import org.jetbrains.kotlin.builtins.KotlinBuiltIns
|
||||
import org.jetbrains.kotlin.descriptors.*
|
||||
import org.jetbrains.kotlin.descriptors.annotations.Annotated
|
||||
import org.jetbrains.kotlin.js.backend.ast.*
|
||||
import org.jetbrains.kotlin.js.resolve.diagnostics.findPsi
|
||||
import org.jetbrains.kotlin.js.translate.context.Namer
|
||||
import org.jetbrains.kotlin.js.translate.context.TranslationContext
|
||||
import org.jetbrains.kotlin.js.translate.declaration.DeclarationBodyVisitor
|
||||
import org.jetbrains.kotlin.js.translate.declaration.DefaultPropertyTranslator
|
||||
import org.jetbrains.kotlin.js.translate.expression.ExpressionVisitor
|
||||
import org.jetbrains.kotlin.js.translate.general.Translation
|
||||
import org.jetbrains.kotlin.js.translate.intrinsic.functions.factories.TopLevelFIF.KOTLIN_EQUALS
|
||||
import org.jetbrains.kotlin.js.translate.utils.JsAstUtils
|
||||
import org.jetbrains.kotlin.js.translate.utils.JsDescriptorUtils
|
||||
import org.jetbrains.kotlin.js.translate.utils.TranslationUtils
|
||||
import org.jetbrains.kotlin.psi.KtExpression
|
||||
import org.jetbrains.kotlin.psi.KtPureClassOrObject
|
||||
import org.jetbrains.kotlin.resolve.descriptorUtil.getSuperClassNotAny
|
||||
import org.jetbrains.kotlinx.serialization.compiler.backend.common.SerializerCodegen
|
||||
import org.jetbrains.kotlinx.serialization.compiler.backend.common.getSerialTypeInfo
|
||||
import org.jetbrains.kotlinx.serialization.compiler.extensions.SerializationDescriptorSerializerPlugin
|
||||
import org.jetbrains.kotlinx.serialization.compiler.resolve.*
|
||||
import org.jetbrains.kotlinx.serialization.compiler.resolve.SerialEntityNames.SERIAL_DESCRIPTOR_CLASS_IMPL
|
||||
import org.jetbrains.kotlinx.serialization.compiler.resolve.SerialEntityNames.typeArgPrefix
|
||||
|
||||
open class SerializerJsTranslator(
|
||||
descriptor: ClassDescriptor,
|
||||
val translator: DeclarationBodyVisitor,
|
||||
val context: TranslationContext,
|
||||
metadataPlugin: SerializationDescriptorSerializerPlugin?
|
||||
) : SerializerCodegen(descriptor, context.bindingContext(), metadataPlugin) {
|
||||
|
||||
internal 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 = generatedSerialDescPropertyDescriptor ?: return
|
||||
val serialDescImplClass = serializerDescriptor
|
||||
.getClassFromInternalSerializationPackage(SERIAL_DESCRIPTOR_CLASS_IMPL)
|
||||
// this.serialDesc = new SerialDescImpl(...)
|
||||
val correctThis = context.getDispatchReceiver(JsDescriptorUtils.getReceiverParameterForDeclaration(desc.containingDeclaration))
|
||||
val value = instantiateNewDescriptor(context, correctThis, serialDescImplClass)
|
||||
val assgmnt = TranslationUtils.assignmentToBackingField(context, desc, value)
|
||||
translator.addInitializerStatement(assgmnt.makeStmt())
|
||||
|
||||
// adding elements via serialDesc.addElement(...)
|
||||
val addFunc = serialDescImplClass.getFuncDesc(CallingConventions.addElement).single()
|
||||
val pushFunc = serialDescImplClass.getFuncDesc(CallingConventions.addAnnotation).single()
|
||||
val pushClassFunc = serialDescImplClass.getFuncDesc(CallingConventions.addClassAnnotation).single()
|
||||
val serialClassDescRef = JsNameRef(context.getNameForDescriptor(generatedSerialDescPropertyDescriptor), JsThisRef())
|
||||
|
||||
addElementsContentToDescriptor(context, serialClassDescRef, addFunc, pushFunc)
|
||||
|
||||
// push class annotations
|
||||
pushAnnotationsInto(serializableDescriptor, pushClassFunc, serialClassDescRef)
|
||||
}
|
||||
|
||||
protected open fun instantiateNewDescriptor(
|
||||
context: TranslationContext,
|
||||
correctThis: JsExpression,
|
||||
baseSerialDescImplClass: ClassDescriptor
|
||||
): JsExpression {
|
||||
val serialDescImplConstructor = baseSerialDescImplClass.unsubstitutedPrimaryConstructor!!
|
||||
return JsNew(
|
||||
context.getInnerReference(serialDescImplConstructor),
|
||||
listOf(JsStringLiteral(serialName), if (isGeneratedSerializer) correctThis else JsNullLiteral(), JsIntLiteral(serializableProperties.size))
|
||||
)
|
||||
}
|
||||
|
||||
protected open fun addElementsContentToDescriptor(
|
||||
context: TranslationContext,
|
||||
serialDescriptorInThis: JsNameRef,
|
||||
addElementFunction: FunctionDescriptor,
|
||||
pushAnnotationFunction: FunctionDescriptor
|
||||
) {
|
||||
for (prop in serializableProperties) {
|
||||
if (prop.transient) continue
|
||||
val call = JsInvocation(
|
||||
JsNameRef(context.getNameForDescriptor(addElementFunction), serialDescriptorInThis),
|
||||
JsStringLiteral(prop.name),
|
||||
JsBooleanLiteral(prop.optional)
|
||||
)
|
||||
translator.addInitializerStatement(call.makeStmt())
|
||||
// serialDesc.pushAnnotation(...)
|
||||
pushAnnotationsInto(prop.descriptor, pushAnnotationFunction, serialDescriptorInThis)
|
||||
}
|
||||
}
|
||||
|
||||
protected fun pushAnnotationsInto(annotated: Annotated, pushFunction: DeclarationDescriptor, intoRef: JsNameRef) {
|
||||
for ((annotationClass , args, _) in annotated.annotationsWithArguments()) {
|
||||
val argExprs = args.map { arg ->
|
||||
Translation.translateAsExpression(arg.getArgumentExpression()!!, context)
|
||||
}
|
||||
val classRef = context.translateQualifiedReference(annotationClass)
|
||||
val invok = JsInvocation(JsNameRef(context.getNameForDescriptor(pushFunction), intoRef), JsNew(classRef, argExprs))
|
||||
translator.addInitializerStatement(invok.makeStmt())
|
||||
}
|
||||
}
|
||||
|
||||
override fun generateChildSerializersGetter(function: FunctionDescriptor) = generateFunction(function) { _, _ ->
|
||||
val allSerializers = serializableProperties.map { requireNotNull(serializerTower(it)) { "Property ${it.name} must have a serializer" } }
|
||||
+JsReturn(JsArrayLiteral(allSerializers))
|
||||
}
|
||||
|
||||
override fun generateTypeParamsSerializersGetter(function: FunctionDescriptor) = generateFunction(function) { _, _ ->
|
||||
val typeParams = serializableDescriptor.declaredTypeParameters.mapIndexed { idx, _ ->
|
||||
JsNameRef(context.scope().declareName("$typeArgPrefix$idx"), JsThisRef())
|
||||
}
|
||||
+JsReturn(JsArrayLiteral(typeParams))
|
||||
}
|
||||
|
||||
override fun generateSerializableClassProperty(property: PropertyDescriptor) {
|
||||
val propDesc = generatedSerialDescPropertyDescriptor ?: 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)
|
||||
}
|
||||
|
||||
override fun generateGenericFieldsAndConstructor(typedConstructorDescriptor: ClassConstructorDescriptor) {
|
||||
val f = context.buildFunction(typedConstructorDescriptor) { jsFun, context ->
|
||||
val thiz = jsFun.scope.declareName(Namer.ANOTHER_THIS_PARAMETER_NAME).makeRef()
|
||||
|
||||
+JsVars(JsVars.JsVar(thiz.name, JsNew(context.getInnerNameForDescriptor(serializerDescriptor).makeRef())))
|
||||
jsFun.parameters.forEachIndexed { i, parameter ->
|
||||
val thisFRef = JsNameRef(context.scope().declareName("$typeArgPrefix$i"), thiz)
|
||||
+JsAstUtils.assignment(thisFRef, JsNameRef(parameter.name)).makeStmt()
|
||||
}
|
||||
+JsReturn(thiz)
|
||||
}
|
||||
|
||||
f.name = context.getInnerNameForDescriptor(typedConstructorDescriptor);
|
||||
context.addDeclarationStatement(f.makeStmt())
|
||||
context.export(typedConstructorDescriptor)
|
||||
}
|
||||
|
||||
protected fun TranslationContext.referenceMethod(clazz: ClassDescriptor, name: String) =
|
||||
getNameForDescriptor(clazz.getFuncDesc(name).single())
|
||||
|
||||
override fun generateSave(function: FunctionDescriptor) = generateFunction(function) { jsFun, ctx ->
|
||||
val encoderClass = serializerDescriptor.getClassFromSerializationPackage(SerialEntityNames.ENCODER_CLASS)
|
||||
val kOutputClass = serializerDescriptor.getClassFromSerializationPackage(SerialEntityNames.STRUCTURE_ENCODER_CLASS)
|
||||
val wBeginFunc = ctx.getNameForDescriptor(
|
||||
encoderClass.getFuncDesc(CallingConventions.begin).single { it.valueParameters.size == 1 })
|
||||
val serialClassDescRef = JsNameRef(context.getNameForDescriptor(anySerialDescProperty!!), JsThisRef())
|
||||
|
||||
val serializableSource = ((serializableDescriptor.findPsi() as? KtPureClassOrObject)
|
||||
?: throw AssertionError("Serializable descriptor $serializableDescriptor must have source file to build initializers map"))
|
||||
val initializersMap: Map<PropertyDescriptor, KtExpression?> =
|
||||
context.buildInitializersRemapping(serializableSource, serializableDescriptor.getSuperClassNotAny())
|
||||
|
||||
// output.writeBegin(desc, [])
|
||||
val call = JsInvocation(
|
||||
JsNameRef(wBeginFunc, JsNameRef(jsFun.parameters[0].name)),
|
||||
serialClassDescRef
|
||||
)
|
||||
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))
|
||||
|
||||
fun SerializableProperty.jsNameRef() = JsNameRef(ctx.getNameForDescriptor(descriptor), objRef)
|
||||
|
||||
// todo: internal serialization via virtual calls
|
||||
val labeledProperties = serializableProperties.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 = serializerInstance(context, sti.serializer, property.module, property.type, property.genericIndex)
|
||||
val invocation = if (innerSerial == null) {
|
||||
val writeFunc =
|
||||
kOutputClass.getFuncDesc("${CallingConventions.encode}${sti.elementMethodPrefix}${CallingConventions.elementPostfix}").single()
|
||||
.let { ctx.getNameForDescriptor(it) }
|
||||
JsInvocation(
|
||||
JsNameRef(writeFunc, localOutputRef),
|
||||
serialClassDescRef,
|
||||
JsIntLiteral(index),
|
||||
property.jsNameRef()
|
||||
).makeStmt()
|
||||
}
|
||||
else {
|
||||
val writeFunc =
|
||||
kOutputClass.getFuncDesc("${CallingConventions.encode}${sti.elementMethodPrefix}Serializable${CallingConventions.elementPostfix}").single()
|
||||
.let { ctx.getNameForDescriptor(it) }
|
||||
JsInvocation(
|
||||
JsNameRef(writeFunc, localOutputRef),
|
||||
serialClassDescRef,
|
||||
JsIntLiteral(index),
|
||||
innerSerial,
|
||||
property.jsNameRef()
|
||||
).makeStmt()
|
||||
}
|
||||
|
||||
if (!property.optional) {
|
||||
+invocation
|
||||
} else {
|
||||
val shouldEncodeFunc = ctx.referenceMethod(kOutputClass, CallingConventions.shouldEncodeDefault)
|
||||
val defaultValue =
|
||||
initializersMap.getValue(property.descriptor)?.let { Translation.translateAsExpression(it, ctx) }
|
||||
?: throw IllegalStateException("Optional property does not have an initializer?")
|
||||
val partA = JsAstUtils.not(KOTLIN_EQUALS.apply(property.jsNameRef(), listOf(defaultValue), ctx))
|
||||
val partB =
|
||||
JsInvocation(JsNameRef(shouldEncodeFunc, localOutputRef), serialClassDescRef, JsIntLiteral(index))
|
||||
val cond = JsBinaryOperation(JsBinaryOperator.OR, partA, partB)
|
||||
+JsIf(cond, invocation)
|
||||
}
|
||||
}
|
||||
|
||||
// output.writeEnd(serialClassDesc)
|
||||
val wEndFunc = kOutputClass.getFuncDesc(CallingConventions.end).single()
|
||||
.let { ctx.getNameForDescriptor(it) }
|
||||
+JsInvocation(JsNameRef(wEndFunc, localOutputRef), serialClassDescRef).makeStmt()
|
||||
}
|
||||
|
||||
|
||||
override fun generateLoad(function: FunctionDescriptor) = generateFunction(function) { jsFun, context ->
|
||||
val inputClass = serializerDescriptor.getClassFromSerializationPackage(SerialEntityNames.STRUCTURE_DECODER_CLASS)
|
||||
val decoderClass = serializerDescriptor.getClassFromSerializationPackage(SerialEntityNames.DECODER_CLASS)
|
||||
val serialClassDescRef = JsNameRef(context.getNameForDescriptor(anySerialDescProperty!!), JsThisRef())
|
||||
|
||||
// var index = -1, readAll = false
|
||||
val indexVar = JsNameRef(jsFun.scope.declareFreshName("index"))
|
||||
+JsVars(JsVars.JsVar(indexVar.name))
|
||||
|
||||
// calculating bit mask vars
|
||||
val blocksCnt = serializableProperties.bitMaskSlotCount()
|
||||
fun bitMaskOff(i: Int) = bitMaskSlotAt(i)
|
||||
|
||||
// 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 = serializableProperties.mapIndexed { i, _ -> JsNameRef(jsFun.scope.declareFreshName("local$i")) }
|
||||
+JsVars(localProps.map { JsVars.JsVar(it.name) }, true)
|
||||
|
||||
//input = input.readBegin(...)
|
||||
val inputVar = JsNameRef(jsFun.scope.declareFreshName("input"))
|
||||
val readBeginF = decoderClass.getFuncDesc(CallingConventions.begin).single { it.valueParameters.size == 1 }
|
||||
val readBeginCall = JsInvocation(
|
||||
JsNameRef(context.getNameForDescriptor(readBeginF), JsNameRef(jsFun.parameters[0].name)),
|
||||
serialClassDescRef
|
||||
)
|
||||
+JsVars(JsVars.JsVar(inputVar.name, readBeginCall))
|
||||
|
||||
// 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(CallingConventions.decodeElementIndex).single())
|
||||
+JsAstUtils.assignment(
|
||||
indexVar,
|
||||
JsInvocation(JsNameRef(readElementF, inputVar), serialClassDescRef)
|
||||
).makeStmt()
|
||||
// switch(index)
|
||||
jsSwitch(indexVar) {
|
||||
// all properties
|
||||
for ((i, property) in serializableProperties.withIndex()) {
|
||||
case(JsIntLiteral(i)) {
|
||||
// input.readXxxElementValue
|
||||
val sti = getSerialTypeInfo(property)
|
||||
val innerSerial = serializerInstance(context, sti.serializer, property.module, property.type, property.genericIndex)
|
||||
val call: JsExpression = if (innerSerial == null) {
|
||||
val unknownSer = (sti.elementMethodPrefix.isEmpty())
|
||||
val readFunc =
|
||||
inputClass.getFuncDesc("${CallingConventions.decode}${sti.elementMethodPrefix}${CallingConventions.elementPostfix}")
|
||||
// if readElementValue, must have 3 parameters, if readXXXElementValue - 2
|
||||
.single { !unknownSer || (it.valueParameters.size == 3) }
|
||||
.let { context.getNameForDescriptor(it) }
|
||||
val readArgs = mutableListOf(serialClassDescRef, JsIntLiteral(i))
|
||||
if (unknownSer) readArgs.add(
|
||||
ExpressionVisitor.getObjectKClass(
|
||||
this@SerializerJsTranslator.context,
|
||||
property.type.toClassDescriptor!!
|
||||
)
|
||||
)
|
||||
JsInvocation(JsNameRef(readFunc, inputVar), readArgs)
|
||||
} else {
|
||||
val readFunc =
|
||||
inputClass.getFuncDesc("${CallingConventions.decode}${sti.elementMethodPrefix}Serializable${CallingConventions.elementPostfix}")
|
||||
.single { it.valueParameters.size == 4 }
|
||||
.let { context.getNameForDescriptor(it) }
|
||||
JsInvocation(
|
||||
JsNameRef(readFunc, inputVar),
|
||||
serialClassDescRef,
|
||||
JsIntLiteral(i),
|
||||
innerSerial,
|
||||
localProps[i]
|
||||
)
|
||||
}
|
||||
// localPropI = ...
|
||||
+JsAstUtils.assignment(
|
||||
localProps[i],
|
||||
call
|
||||
).makeStmt()
|
||||
// char unboxing crutch
|
||||
if (KotlinBuiltIns.isCharOrNullableChar(property.type)) {
|
||||
val coerceTo = TranslationUtils.getReturnTypeForCoercion(property.descriptor)
|
||||
+JsAstUtils.assignment(
|
||||
localProps[i],
|
||||
TranslationUtils.coerce(context, localProps[i], coerceTo)
|
||||
).makeStmt()
|
||||
}
|
||||
|
||||
// bitMask[i] |= 1 << x
|
||||
val bitPos = 1 shl (i % 32)
|
||||
+JsBinaryOperation(
|
||||
JsBinaryOperator.ASG_BIT_OR,
|
||||
bitMasks[bitMaskOff(i)],
|
||||
JsIntLiteral(bitPos)
|
||||
).makeStmt()
|
||||
+JsBreak()
|
||||
}
|
||||
}
|
||||
// case -1: break loop
|
||||
case(JsIntLiteral(-1)) {
|
||||
+JsBreak(loopRef)
|
||||
}
|
||||
// default: throw
|
||||
default {
|
||||
val excClassRef = serializableDescriptor.getClassFromSerializationPackage(SerialEntityNames.UNKNOWN_FIELD_EXC)
|
||||
.let { context.translateQualifiedReference(it) }
|
||||
+JsThrow(JsNew(excClassRef, listOf(indexVar)))
|
||||
}
|
||||
}
|
||||
}, loop)
|
||||
|
||||
// input.readEnd(desc)
|
||||
val readEndF = inputClass.getFuncDesc(CallingConventions.end).single()
|
||||
.let { context.getNameForDescriptor(it) }
|
||||
+JsInvocation(
|
||||
JsNameRef(readEndF, inputVar),
|
||||
serialClassDescRef
|
||||
).makeStmt()
|
||||
|
||||
// deserialization constructor call
|
||||
// todo: external deserialization with primary constructor and setters calls after resolution of KT-11586
|
||||
val constrDesc = KSerializerDescriptorResolver.createLoadConstructorDescriptor(
|
||||
serializableDescriptor,
|
||||
context.bindingContext(),
|
||||
null
|
||||
)
|
||||
val constrRef = context.getInnerNameForDescriptor(constrDesc).makeRef()
|
||||
val args: MutableList<JsExpression> = bitMasks.toMutableList()
|
||||
args += localProps
|
||||
args += JsNullLiteral()
|
||||
+JsReturn(JsInvocation(constrRef, args))
|
||||
}
|
||||
|
||||
companion object {
|
||||
fun translate(
|
||||
descriptor: ClassDescriptor,
|
||||
translator: DeclarationBodyVisitor,
|
||||
context: TranslationContext,
|
||||
metadataPlugin: SerializationDescriptorSerializerPlugin?
|
||||
) {
|
||||
val serializableDesc = getSerializableClassDescriptorBySerializer(descriptor) ?: return
|
||||
if (serializableDesc.isEnumWithLegacyGeneratedSerializer()) {
|
||||
SerializerForEnumsTranslator(descriptor, translator, context).generate()
|
||||
} else {
|
||||
SerializerJsTranslator(descriptor, translator, context, metadataPlugin).generate()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+765
@@ -0,0 +1,765 @@
|
||||
/*
|
||||
* Copyright 2010-2020 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlinx.serialization.compiler.backend.jvm
|
||||
|
||||
import org.jetbrains.kotlin.builtins.KotlinBuiltIns
|
||||
import org.jetbrains.kotlin.codegen.*
|
||||
import org.jetbrains.kotlin.codegen.context.ClassContext
|
||||
import org.jetbrains.kotlin.descriptors.*
|
||||
import org.jetbrains.kotlin.descriptors.annotations.Annotations
|
||||
import org.jetbrains.kotlin.descriptors.impl.ClassConstructorDescriptorImpl
|
||||
import org.jetbrains.kotlin.descriptors.impl.ClassDescriptorImpl
|
||||
import org.jetbrains.kotlin.descriptors.impl.SimpleFunctionDescriptorImpl
|
||||
import org.jetbrains.kotlin.js.resolve.diagnostics.findPsi
|
||||
import org.jetbrains.kotlin.load.java.JvmAbi
|
||||
import org.jetbrains.kotlin.load.kotlin.TypeMappingMode
|
||||
import org.jetbrains.kotlin.load.kotlin.internalName
|
||||
import org.jetbrains.kotlin.name.ClassId
|
||||
import org.jetbrains.kotlin.name.Name
|
||||
import org.jetbrains.kotlin.psi.ValueArgument
|
||||
import org.jetbrains.kotlin.resolve.DescriptorFactory
|
||||
import org.jetbrains.kotlin.resolve.descriptorUtil.builtIns
|
||||
import org.jetbrains.kotlin.resolve.descriptorUtil.classId
|
||||
import org.jetbrains.kotlin.resolve.descriptorUtil.module
|
||||
import org.jetbrains.kotlin.resolve.jvm.AsmTypes
|
||||
import org.jetbrains.kotlin.resolve.jvm.diagnostics.JvmDeclarationOrigin
|
||||
import org.jetbrains.kotlin.resolve.jvm.diagnostics.JvmDeclarationOriginKind
|
||||
import org.jetbrains.kotlin.resolve.jvm.diagnostics.OtherOrigin
|
||||
import org.jetbrains.kotlin.resolve.jvm.jvmSignature.JvmMethodSignature
|
||||
import org.jetbrains.kotlin.resolve.scopes.MemberScope
|
||||
import org.jetbrains.kotlin.storage.LockBasedStorageManager
|
||||
import org.jetbrains.kotlin.types.KotlinType
|
||||
import org.jetbrains.kotlin.types.SimpleType
|
||||
import org.jetbrains.kotlin.types.typeUtil.isTypeParameter
|
||||
import org.jetbrains.kotlin.types.typeUtil.representativeUpperBound
|
||||
import org.jetbrains.kotlinx.serialization.compiler.backend.common.*
|
||||
import org.jetbrains.kotlinx.serialization.compiler.resolve.*
|
||||
import org.jetbrains.kotlinx.serialization.compiler.resolve.SerialEntityNames.DECODER_CLASS
|
||||
import org.jetbrains.kotlinx.serialization.compiler.resolve.SerialEntityNames.ENCODER_CLASS
|
||||
import org.jetbrains.kotlinx.serialization.compiler.resolve.SerialEntityNames.ENUMS_FILE
|
||||
import org.jetbrains.kotlinx.serialization.compiler.resolve.SerialEntityNames.ENUM_SERIALIZER_FACTORY_FUNC_NAME
|
||||
import org.jetbrains.kotlinx.serialization.compiler.resolve.SerialEntityNames.KSERIALIZER_CLASS
|
||||
import org.jetbrains.kotlinx.serialization.compiler.resolve.SerialEntityNames.MARKED_ENUM_SERIALIZER_FACTORY_FUNC_NAME
|
||||
import org.jetbrains.kotlinx.serialization.compiler.resolve.SerialEntityNames.MISSING_FIELD_EXC
|
||||
import org.jetbrains.kotlinx.serialization.compiler.resolve.SerialEntityNames.PLUGIN_EXCEPTIONS_FILE
|
||||
import org.jetbrains.kotlinx.serialization.compiler.resolve.SerialEntityNames.SERIAL_CTOR_MARKER_NAME
|
||||
import org.jetbrains.kotlinx.serialization.compiler.resolve.SerialEntityNames.SERIAL_DESCRIPTOR_CLASS
|
||||
import org.jetbrains.kotlinx.serialization.compiler.resolve.SerialEntityNames.SERIAL_DESCRIPTOR_CLASS_IMPL
|
||||
import org.jetbrains.kotlinx.serialization.compiler.resolve.SerialEntityNames.SERIAL_DESCRIPTOR_FOR_ENUM
|
||||
import org.jetbrains.kotlinx.serialization.compiler.resolve.SerialEntityNames.SERIAL_DESC_FIELD
|
||||
import org.jetbrains.kotlinx.serialization.compiler.resolve.SerialEntityNames.SERIAL_EXC
|
||||
import org.jetbrains.kotlinx.serialization.compiler.resolve.SerialEntityNames.SERIAL_LOADER_CLASS
|
||||
import org.jetbrains.kotlinx.serialization.compiler.resolve.SerialEntityNames.SERIAL_SAVER_CLASS
|
||||
import org.jetbrains.kotlinx.serialization.compiler.resolve.SerialEntityNames.STRUCTURE_DECODER_CLASS
|
||||
import org.jetbrains.kotlinx.serialization.compiler.resolve.SerialEntityNames.STRUCTURE_ENCODER_CLASS
|
||||
import org.jetbrains.kotlinx.serialization.compiler.resolve.SerialEntityNames.UNKNOWN_FIELD_EXC
|
||||
import org.jetbrains.kotlinx.serialization.compiler.resolve.SerialEntityNames.typeArgPrefix
|
||||
import org.jetbrains.kotlinx.serialization.compiler.resolve.SerializationPackages.internalPackageFqName
|
||||
import org.jetbrains.kotlinx.serialization.compiler.resolve.SerializationPackages.packageFqName
|
||||
import org.jetbrains.org.objectweb.asm.Opcodes
|
||||
import org.jetbrains.org.objectweb.asm.Type
|
||||
import org.jetbrains.org.objectweb.asm.commons.InstructionAdapter
|
||||
|
||||
// todo: extract packages constants too?
|
||||
internal val descType = Type.getObjectType("kotlinx/serialization/descriptors/$SERIAL_DESCRIPTOR_CLASS")
|
||||
internal val descImplType = Type.getObjectType("kotlinx/serialization/internal/$SERIAL_DESCRIPTOR_CLASS_IMPL")
|
||||
internal val descriptorForEnumsType = Type.getObjectType("kotlinx/serialization/internal/$SERIAL_DESCRIPTOR_FOR_ENUM")
|
||||
internal val generatedSerializerType = Type.getObjectType("kotlinx/serialization/internal/${SerialEntityNames.GENERATED_SERIALIZER_CLASS}")
|
||||
internal val kOutputType = Type.getObjectType("kotlinx/serialization/encoding/$STRUCTURE_ENCODER_CLASS")
|
||||
internal val encoderType = Type.getObjectType("kotlinx/serialization/encoding/$ENCODER_CLASS")
|
||||
internal val decoderType = Type.getObjectType("kotlinx/serialization/encoding/$DECODER_CLASS")
|
||||
internal val kInputType = Type.getObjectType("kotlinx/serialization/encoding/$STRUCTURE_DECODER_CLASS")
|
||||
|
||||
internal val pluginUtilsType = Type.getObjectType("kotlinx/serialization/internal/${PLUGIN_EXCEPTIONS_FILE}Kt")
|
||||
internal val enumFactoriesType = Type.getObjectType("kotlinx/serialization/internal/${ENUMS_FILE}Kt")
|
||||
|
||||
internal val jvmLambdaType = Type.getObjectType("kotlin/jvm/internal/Lambda")
|
||||
internal val kotlinLazyType = Type.getObjectType("kotlin/Lazy")
|
||||
internal val function0Type = Type.getObjectType("kotlin/jvm/functions/Function0")
|
||||
internal val threadSafeModeType = Type.getObjectType("kotlin/LazyThreadSafetyMode")
|
||||
|
||||
internal val kSerialSaverType = Type.getObjectType("kotlinx/serialization/$SERIAL_SAVER_CLASS")
|
||||
internal val kSerialLoaderType = Type.getObjectType("kotlinx/serialization/$SERIAL_LOADER_CLASS")
|
||||
internal val kSerializerType = Type.getObjectType("kotlinx/serialization/$KSERIALIZER_CLASS")
|
||||
internal val kSerializerArrayType = Type.getObjectType("[Lkotlinx/serialization/$KSERIALIZER_CLASS;")
|
||||
|
||||
internal val serializationExceptionName = "kotlinx/serialization/$SERIAL_EXC"
|
||||
internal val serializationExceptionMissingFieldName = "kotlinx/serialization/$MISSING_FIELD_EXC"
|
||||
internal val serializationExceptionUnknownIndexName = "kotlinx/serialization/$UNKNOWN_FIELD_EXC"
|
||||
|
||||
private val annotationType = Type.getObjectType("java/lang/annotation/Annotation")
|
||||
private val annotationArrayType = Type.getObjectType("[${annotationType.descriptor}")
|
||||
private val doubleAnnotationArrayType = Type.getObjectType("[${annotationArrayType.descriptor}")
|
||||
private val stringType = AsmTypes.JAVA_STRING_TYPE
|
||||
private val stringArrayType = Type.getObjectType("[${stringType.descriptor}")
|
||||
|
||||
internal val descriptorGetterName = JvmAbi.getterName(SERIAL_DESC_FIELD)
|
||||
internal val getLazyValueName = JvmAbi.getterName("value")
|
||||
|
||||
val OPT_MASK_TYPE: Type = Type.INT_TYPE
|
||||
val OPT_MASK_BITS = 32
|
||||
|
||||
// compare with zero. if result == 0, property was not seen.
|
||||
internal fun InstructionAdapter.genValidateProperty(index: Int, bitMaskAddress: Int) {
|
||||
load(bitMaskAddress, OPT_MASK_TYPE)
|
||||
iconst(1 shl (index % OPT_MASK_BITS))
|
||||
and(OPT_MASK_TYPE)
|
||||
iconst(0)
|
||||
}
|
||||
|
||||
internal fun InstructionAdapter.genMissingFieldExceptionThrow(fieldName: String) {
|
||||
anew(Type.getObjectType(serializationExceptionMissingFieldName))
|
||||
dup()
|
||||
aconst(fieldName)
|
||||
invokespecial(serializationExceptionMissingFieldName, "<init>", "(Ljava/lang/String;)V", false)
|
||||
checkcast(Type.getObjectType("java/lang/Throwable"))
|
||||
athrow()
|
||||
}
|
||||
|
||||
fun InstructionAdapter.genKOutputMethodCall(
|
||||
property: SerializableProperty, classCodegen: ImplementationBodyCodegen, expressionCodegen: ExpressionCodegen,
|
||||
propertyOwnerType: Type, ownerVar: Int, fromClassStartVar: Int? = null,
|
||||
generator: AbstractSerialGenerator
|
||||
) {
|
||||
val propertyType = classCodegen.typeMapper.mapType(property.type)
|
||||
val sti = generator.getSerialTypeInfo(property, propertyType)
|
||||
val useSerializer = if (fromClassStartVar == null) stackValueSerializerInstanceFromSerializer(expressionCodegen, classCodegen, sti, generator)
|
||||
else stackValueSerializerInstanceFromClass(expressionCodegen, classCodegen, sti, fromClassStartVar, generator)
|
||||
val actualType = ImplementationBodyCodegen.genPropertyOnStack(
|
||||
this,
|
||||
expressionCodegen.context,
|
||||
property.descriptor,
|
||||
propertyOwnerType,
|
||||
ownerVar,
|
||||
classCodegen.state
|
||||
)
|
||||
actualType?.type?.let { type -> StackValue.coerce(type, sti.type, this) }
|
||||
invokeinterface(
|
||||
kOutputType.internalName,
|
||||
CallingConventions.encode + sti.elementMethodPrefix + (if (useSerializer) "Serializable" else "") + CallingConventions.elementPostfix,
|
||||
"(" + descType.descriptor + "I" +
|
||||
(if (useSerializer) kSerialSaverType.descriptor else "") +
|
||||
(sti.type.descriptor) + ")V"
|
||||
)
|
||||
}
|
||||
|
||||
internal fun InstructionAdapter.buildInternalConstructorDesc(
|
||||
propsStartVar: Int,
|
||||
bitMaskBase: Int,
|
||||
codegen: ClassBodyCodegen,
|
||||
args: List<SerializableProperty>
|
||||
): String {
|
||||
val constructorDesc = StringBuilder("(")
|
||||
repeat(args.bitMaskSlotCount()) {
|
||||
constructorDesc.append("I")
|
||||
load(bitMaskBase + it, Type.INT_TYPE)
|
||||
}
|
||||
var propVar = propsStartVar
|
||||
for (property in args) {
|
||||
val propertyType = codegen.typeMapper.mapType(property.type)
|
||||
constructorDesc.append(propertyType.descriptor)
|
||||
load(propVar, propertyType)
|
||||
propVar += propertyType.size
|
||||
}
|
||||
constructorDesc.append("Lkotlinx/serialization/internal/$SERIAL_CTOR_MARKER_NAME;)V")
|
||||
aconst(null)
|
||||
return constructorDesc.toString()
|
||||
}
|
||||
|
||||
internal fun ImplementationBodyCodegen.generateMethod(
|
||||
function: FunctionDescriptor,
|
||||
block: InstructionAdapter.(JvmMethodSignature, ExpressionCodegen) -> Unit
|
||||
) {
|
||||
this.functionCodegen.generateMethod(OtherOrigin(this.myClass.psiOrParent, function), function,
|
||||
object : FunctionGenerationStrategy.CodegenBased(this.state) {
|
||||
override fun doGenerateBody(codegen: ExpressionCodegen, signature: JvmMethodSignature) {
|
||||
codegen.v.block(signature, codegen)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
internal fun InstructionAdapter.stackValueSerializerInstanceFromClass(
|
||||
expressionCodegen: ExpressionCodegen,
|
||||
classCodegen: ClassBodyCodegen,
|
||||
sti: JVMSerialTypeInfo,
|
||||
varIndexStart: Int,
|
||||
serializerCodegen: AbstractSerialGenerator
|
||||
): Boolean {
|
||||
val serializer = sti.serializer
|
||||
return serializerCodegen.stackValueSerializerInstance(
|
||||
expressionCodegen,
|
||||
classCodegen,
|
||||
sti.property.module,
|
||||
sti.property.type,
|
||||
serializer,
|
||||
this,
|
||||
sti.property.genericIndex
|
||||
) { idx, _ ->
|
||||
load(varIndexStart + idx, kSerializerType)
|
||||
}
|
||||
}
|
||||
|
||||
internal fun InstructionAdapter.stackValueSerializerInstanceFromSerializerWithoutSti(
|
||||
expressionCodegen: ExpressionCodegen,
|
||||
codegen: ClassBodyCodegen,
|
||||
property: SerializableProperty,
|
||||
serializerCodegen: AbstractSerialGenerator
|
||||
): Boolean {
|
||||
val serializer =
|
||||
property.serializableWith?.toClassDescriptor
|
||||
?: if (!property.type.isTypeParameter()) serializerCodegen.findTypeSerializerOrContext(
|
||||
property.module,
|
||||
property.type,
|
||||
property.descriptor.findPsi()
|
||||
) else null
|
||||
return serializerCodegen.stackValueSerializerInstance(
|
||||
expressionCodegen,
|
||||
codegen,
|
||||
property.module,
|
||||
property.type,
|
||||
serializer,
|
||||
this,
|
||||
property.genericIndex
|
||||
) { idx, _ ->
|
||||
load(0, kSerializerType)
|
||||
getfield(codegen.typeMapper.mapClass(codegen.descriptor).internalName, "$typeArgPrefix$idx", kSerializerType.descriptor)
|
||||
}.also { if (it && property.type.isMarkedNullable) wrapStackValueIntoNullableSerializer() }
|
||||
}
|
||||
|
||||
internal fun InstructionAdapter.stackValueSerializerInstanceFromSerializer(
|
||||
expressionCodegen: ExpressionCodegen,
|
||||
codegen: ClassBodyCodegen,
|
||||
sti: JVMSerialTypeInfo,
|
||||
serializerCodegen: AbstractSerialGenerator
|
||||
): Boolean {
|
||||
return serializerCodegen.stackValueSerializerInstance(
|
||||
expressionCodegen, codegen, sti.property.module, sti.property.type,
|
||||
sti.serializer, this, sti.property.genericIndex
|
||||
) { idx, _ ->
|
||||
load(0, kSerializerType)
|
||||
getfield(codegen.typeMapper.mapClass(codegen.descriptor).internalName, "$typeArgPrefix$idx", kSerializerType.descriptor)
|
||||
}
|
||||
}
|
||||
|
||||
// returns false is cannot not use serializer
|
||||
// use iv == null to check only (do not emit serializer onto stack)
|
||||
internal fun AbstractSerialGenerator.stackValueSerializerInstance(expressionCodegen: ExpressionCodegen, classCodegen: ClassBodyCodegen, module: ModuleDescriptor, kType: KotlinType, maybeSerializer: ClassDescriptor?,
|
||||
iv: InstructionAdapter?,
|
||||
genericIndex: Int? = null,
|
||||
genericSerializerFieldGetter: (InstructionAdapter.(Int, KotlinType) -> Unit)? = null
|
||||
): Boolean {
|
||||
if (maybeSerializer == null && genericIndex != null) {
|
||||
// get field from serializer object
|
||||
iv?.run { genericSerializerFieldGetter?.invoke(this, genericIndex, kType) }
|
||||
return true
|
||||
}
|
||||
val serializer = maybeSerializer ?: return false
|
||||
if (serializer.kind == ClassKind.OBJECT) {
|
||||
// singleton serializer -- just get it
|
||||
if (iv != null)
|
||||
StackValue.singleton(serializer, classCodegen.typeMapper).put(kSerializerType, iv)
|
||||
return true
|
||||
}
|
||||
// serializer is not singleton object and shall be instantiated
|
||||
val argSerializers = kType.arguments.map { projection ->
|
||||
// bail out from stackValueSerializerInstance if any type argument is not serializable
|
||||
val argType = projection.type
|
||||
val argSerializer = if (argType.isTypeParameter()) null else {
|
||||
findTypeSerializerOrContext(module, argType, sourceElement = classCodegen.descriptor.findPsi())
|
||||
?: return false
|
||||
}
|
||||
// check if it can be properly serialized with its args recursively
|
||||
if (!stackValueSerializerInstance(
|
||||
expressionCodegen,
|
||||
classCodegen,
|
||||
module,
|
||||
argType,
|
||||
argSerializer,
|
||||
null,
|
||||
argType.genericIndex,
|
||||
genericSerializerFieldGetter
|
||||
)
|
||||
)
|
||||
return false
|
||||
Pair(argType, argSerializer)
|
||||
}
|
||||
// new serializer if needed
|
||||
iv?.apply {
|
||||
val serializerType = classCodegen.typeMapper.mapClass(serializer)
|
||||
val classDescriptor = kType.toClassDescriptor!!
|
||||
if (serializer.classId == enumSerializerId && !classDescriptor.useGeneratedEnumSerializer) {
|
||||
// runtime contains enum serializer factory functions
|
||||
val javaEnumArray = Type.getType("[Ljava/lang/Enum;")
|
||||
val enumJavaType = classCodegen.typeMapper.mapType(kType, null, TypeMappingMode.GENERIC_ARGUMENT)
|
||||
val serialName = classDescriptor.serialName()
|
||||
|
||||
if (classDescriptor.isEnumWithSerialInfoAnnotation()) {
|
||||
aconst(serialName)
|
||||
invokestatic(enumJavaType.internalName, "values", "()[${enumJavaType.descriptor}", false)
|
||||
checkcast(javaEnumArray)
|
||||
|
||||
val entries = classDescriptor.enumEntries()
|
||||
fillArray(stringType, entries) { _, entry ->
|
||||
entry.annotations.serialNameValue.let {
|
||||
if (it == null) {
|
||||
aconst(null)
|
||||
} else {
|
||||
aconst(it)
|
||||
}
|
||||
}
|
||||
}
|
||||
checkcast(stringArrayType)
|
||||
|
||||
fillArray(annotationArrayType, entries) { _, entry ->
|
||||
val annotations = entry.annotationsWithArguments()
|
||||
if (annotations.isEmpty()) {
|
||||
aconst(null)
|
||||
} else {
|
||||
fillArray(annotationType, annotations) { _, annotation ->
|
||||
val (annotationClass, args, consParams) = annotation
|
||||
expressionCodegen.generateSyntheticAnnotationOnStack(annotationClass, args, consParams)
|
||||
}
|
||||
}
|
||||
}
|
||||
checkcast(doubleAnnotationArrayType)
|
||||
|
||||
invokestatic(
|
||||
enumFactoriesType.internalName,
|
||||
MARKED_ENUM_SERIALIZER_FACTORY_FUNC_NAME.asString(),
|
||||
"(${stringType.descriptor}${javaEnumArray.descriptor}${stringArrayType.descriptor}${doubleAnnotationArrayType.descriptor})${kSerializerType.descriptor}",
|
||||
false
|
||||
)
|
||||
} else {
|
||||
aconst(serialName)
|
||||
invokestatic(enumJavaType.internalName, "values", "()[${enumJavaType.descriptor}", false)
|
||||
checkcast(javaEnumArray)
|
||||
|
||||
invokestatic(
|
||||
enumFactoriesType.internalName,
|
||||
ENUM_SERIALIZER_FACTORY_FUNC_NAME.asString(),
|
||||
"(${stringType.descriptor}${javaEnumArray.descriptor})${kSerializerType.descriptor}",
|
||||
false
|
||||
)
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// todo: support static factory methods for serializers for shorter bytecode
|
||||
anew(serializerType)
|
||||
dup()
|
||||
// instantiate all arg serializers on stack
|
||||
val signature = StringBuilder("(")
|
||||
|
||||
fun instantiate(typeArgument: Pair<KotlinType, ClassDescriptor?>, writeSignature: Boolean = true) {
|
||||
val (argType, argSerializer) = typeArgument
|
||||
assert(
|
||||
stackValueSerializerInstance(
|
||||
expressionCodegen,
|
||||
classCodegen,
|
||||
module,
|
||||
argType,
|
||||
argSerializer,
|
||||
this,
|
||||
argType.genericIndex,
|
||||
genericSerializerFieldGetter
|
||||
)
|
||||
)
|
||||
// wrap into nullable serializer if argType is nullable
|
||||
if (argType.isMarkedNullable) wrapStackValueIntoNullableSerializer()
|
||||
if (writeSignature) signature.append(kSerializerType.descriptor)
|
||||
}
|
||||
|
||||
val serialName = kType.serialName()
|
||||
when (serializer.classId) {
|
||||
enumSerializerId -> {
|
||||
// support legacy serializer instantiation by constructor for old runtimes
|
||||
aconst(serialName)
|
||||
signature.append("Ljava/lang/String;")
|
||||
val enumJavaType = classCodegen.typeMapper.mapType(kType, null, TypeMappingMode.GENERIC_ARGUMENT)
|
||||
val javaEnumArray = Type.getType("[Ljava/lang/Enum;")
|
||||
invokestatic(enumJavaType.internalName, "values","()[${enumJavaType.descriptor}", false)
|
||||
checkcast(javaEnumArray)
|
||||
signature.append(javaEnumArray.descriptor)
|
||||
}
|
||||
contextSerializerId, polymorphicSerializerId -> {
|
||||
// a special way to instantiate enum -- need a enum KClass reference
|
||||
// GENERIC_ARGUMENT forces boxing in order to obtain KClass
|
||||
aconst(classCodegen.typeMapper.mapType(kType, null, TypeMappingMode.GENERIC_ARGUMENT))
|
||||
AsmUtil.wrapJavaClassIntoKClass(this)
|
||||
signature.append(AsmTypes.K_CLASS_TYPE.descriptor)
|
||||
if (serializer.classId == contextSerializerId && serializer.constructors.any { it.valueParameters.size == 3 }) {
|
||||
// append new additional arguments
|
||||
val fallbackDefaultSerializer = findTypeSerializer(module, kType)
|
||||
if (fallbackDefaultSerializer != null && fallbackDefaultSerializer != serializer) {
|
||||
instantiate(kType to fallbackDefaultSerializer, writeSignature = false)
|
||||
} else {
|
||||
aconst(null)
|
||||
}
|
||||
signature.append(kSerializerType.descriptor)
|
||||
fillArray(kSerializerType, argSerializers) { _, serializer ->
|
||||
instantiate(serializer, writeSignature = false)
|
||||
}
|
||||
signature.append(kSerializerArrayType.descriptor)
|
||||
}
|
||||
}
|
||||
referenceArraySerializerId -> {
|
||||
// a special way to instantiate reference array serializer -- need an element KClass reference
|
||||
aconst(classCodegen.typeMapper.mapType(kType.arguments[0].type, null, TypeMappingMode.GENERIC_ARGUMENT))
|
||||
AsmUtil.wrapJavaClassIntoKClass(this)
|
||||
signature.append(AsmTypes.K_CLASS_TYPE.descriptor)
|
||||
// Reference array serializer still needs serializer for its argument type
|
||||
instantiate(argSerializers[0])
|
||||
}
|
||||
sealedSerializerId -> {
|
||||
aconst(serialName)
|
||||
signature.append("Ljava/lang/String;")
|
||||
aconst(classCodegen.typeMapper.mapType(kType, null, TypeMappingMode.GENERIC_ARGUMENT))
|
||||
AsmUtil.wrapJavaClassIntoKClass(this)
|
||||
signature.append(AsmTypes.K_CLASS_TYPE.descriptor)
|
||||
val (subClasses, subSerializers) = allSealedSerializableSubclassesFor(kType.toClassDescriptor!!, module)
|
||||
// KClasses vararg
|
||||
fillArray(AsmTypes.K_CLASS_TYPE, subClasses) { _, type ->
|
||||
aconst(classCodegen.typeMapper.mapType(type, null, TypeMappingMode.GENERIC_ARGUMENT))
|
||||
AsmUtil.wrapJavaClassIntoKClass(this)
|
||||
}
|
||||
signature.append(AsmTypes.K_CLASS_ARRAY_TYPE.descriptor)
|
||||
// Serializers vararg
|
||||
fillArray(kSerializerType, subSerializers) { i, serializer ->
|
||||
val (argType, argSerializer) = subClasses[i] to serializer
|
||||
assert(
|
||||
stackValueSerializerInstance(
|
||||
expressionCodegen,
|
||||
classCodegen,
|
||||
module,
|
||||
argType,
|
||||
argSerializer,
|
||||
this,
|
||||
argType.genericIndex
|
||||
) { _, genericType ->
|
||||
// if we encountered generic type parameter in one of subclasses of sealed class, use polymorphism from upper bound
|
||||
assert(
|
||||
stackValueSerializerInstance(
|
||||
expressionCodegen,
|
||||
classCodegen,
|
||||
module,
|
||||
(genericType.constructor.declarationDescriptor as TypeParameterDescriptor).representativeUpperBound,
|
||||
module.getClassFromSerializationPackage(SpecialBuiltins.polymorphicSerializer),
|
||||
this
|
||||
)
|
||||
)
|
||||
}
|
||||
)
|
||||
if (argType.isMarkedNullable) wrapStackValueIntoNullableSerializer()
|
||||
}
|
||||
signature.append(kSerializerArrayType.descriptor)
|
||||
}
|
||||
objectSerializerId -> {
|
||||
aconst(serialName)
|
||||
signature.append("Ljava/lang/String;")
|
||||
StackValue.singleton(kType.toClassDescriptor!!, classCodegen.typeMapper).put(Type.getType("Ljava/lang/Object;"), iv)
|
||||
signature.append("Ljava/lang/Object;")
|
||||
}
|
||||
// all serializers get arguments with serializers of their generic types
|
||||
else -> argSerializers.forEach { instantiate(it) }
|
||||
}
|
||||
signature.append(")V")
|
||||
// invoke constructor
|
||||
invokespecial(serializerType.internalName, "<init>", signature.toString(), false)
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
internal fun ExpressionCodegen.generateSyntheticAnnotationOnStack(
|
||||
annotationClass: ClassDescriptor,
|
||||
args: List<ValueArgument>,
|
||||
ctorParams: List<ValueParameterDescriptor>
|
||||
) {
|
||||
val implType = typeMapper.mapType(annotationClass).internalName + "\$" + SerialEntityNames.IMPL_NAME.identifier
|
||||
with(v) {
|
||||
// new Annotation$Impl(...)
|
||||
anew(Type.getObjectType(implType))
|
||||
dup()
|
||||
val sb = StringBuilder("(")
|
||||
for (i in ctorParams.indices) {
|
||||
val decl = args[i]
|
||||
val desc = ctorParams[i]
|
||||
val valAsmType = typeMapper.mapType(desc.type)
|
||||
this@generateSyntheticAnnotationOnStack.gen(decl.getArgumentExpression(), valAsmType)
|
||||
sb.append(valAsmType.descriptor)
|
||||
}
|
||||
sb.append(")V")
|
||||
invokespecial(implType, "<init>", sb.toString(), false)
|
||||
}
|
||||
}
|
||||
|
||||
fun InstructionAdapter.wrapStackValueIntoNullableSerializer() =
|
||||
invokestatic(
|
||||
"kotlinx/serialization/builtins/BuiltinSerializersKt", "getNullable",
|
||||
"(" + kSerializerType.descriptor + ")" + kSerializerType.descriptor, false
|
||||
)
|
||||
|
||||
fun <T> InstructionAdapter.fillArray(type: Type, args: List<T>, onEach: (Int, T) -> Unit) {
|
||||
iconst(args.size)
|
||||
newarray(type)
|
||||
args.forEachIndexed { i, arg ->
|
||||
dup()
|
||||
iconst(i)
|
||||
onEach(i, arg)
|
||||
astore(type)
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// ======= Serializers Resolving =======
|
||||
//
|
||||
|
||||
|
||||
class JVMSerialTypeInfo(
|
||||
property: SerializableProperty,
|
||||
val type: Type,
|
||||
nn: String,
|
||||
serializer: ClassDescriptor? = null
|
||||
) : SerialTypeInfo(property, nn, serializer)
|
||||
|
||||
fun AbstractSerialGenerator.getSerialTypeInfo(property: SerializableProperty, type: Type): JVMSerialTypeInfo {
|
||||
fun SerializableInfo(serializer: ClassDescriptor?) =
|
||||
JVMSerialTypeInfo(
|
||||
property,
|
||||
Type.getType("Ljava/lang/Object;"),
|
||||
if (property.type.isMarkedNullable) "Nullable" else "",
|
||||
serializer
|
||||
)
|
||||
|
||||
property.serializableWith?.toClassDescriptor?.let { return SerializableInfo(it) }
|
||||
findAddOnSerializer(property.type, property.module)?.let { return SerializableInfo(it) }
|
||||
property.type.overridenSerializer?.toClassDescriptor?.let { return SerializableInfo(it) }
|
||||
|
||||
if (property.type.isTypeParameter()) return JVMSerialTypeInfo(
|
||||
property,
|
||||
Type.getType("Ljava/lang/Object;"),
|
||||
if (property.type.isMarkedNullable) "Nullable" else "",
|
||||
null
|
||||
)
|
||||
when (type.sort) {
|
||||
BOOLEAN, BYTE, SHORT, INT, LONG, FLOAT, DOUBLE, CHAR -> {
|
||||
val name = type.className
|
||||
return JVMSerialTypeInfo(property, type, Character.toUpperCase(name[0]) + name.substring(1))
|
||||
}
|
||||
ARRAY -> {
|
||||
// check for explicit serialization annotation on this property
|
||||
var serializer = property.serializableWith.toClassDescriptor
|
||||
if (serializer == null) {
|
||||
// no explicit serializer for this property. Select strategy by element type
|
||||
when (type.elementType.sort) {
|
||||
OBJECT, ARRAY -> {
|
||||
// reference elements
|
||||
serializer = property.module.findClassAcrossModuleDependencies(referenceArraySerializerId)
|
||||
}
|
||||
else -> {
|
||||
serializer = findTypeSerializerOrContext(
|
||||
property.module,
|
||||
property.type,
|
||||
property.descriptor.findPsi()
|
||||
)
|
||||
}
|
||||
// primitive elements are not supported yet
|
||||
}
|
||||
}
|
||||
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 JVMSerialTypeInfo(property, Type.getType("Ljava/lang/String;"), "String")
|
||||
// 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 = property.serializableWith?.toClassDescriptor
|
||||
?: findTypeSerializerOrContext(
|
||||
property.module,
|
||||
property.type,
|
||||
property.descriptor.findPsi()
|
||||
)
|
||||
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
|
||||
}
|
||||
}
|
||||
|
||||
fun InstructionAdapter.stackValueDefault(type: Type) {
|
||||
when (type.sort) {
|
||||
BOOLEAN, BYTE, SHORT, CHAR, INT -> iconst(0)
|
||||
LONG -> lconst(0)
|
||||
FLOAT -> fconst(0f)
|
||||
DOUBLE -> dconst(0.0)
|
||||
else -> aconst(null)
|
||||
}
|
||||
}
|
||||
|
||||
internal fun createSingletonLambda(
|
||||
lambdaName: String,
|
||||
outerClassCodegen: ImplementationBodyCodegen,
|
||||
resultSimpleType: SimpleType,
|
||||
block: InstructionAdapter.(ImplementationBodyCodegen, ExpressionCodegen) -> Unit
|
||||
): Type {
|
||||
val lambdaType = Type.getObjectType("${outerClassCodegen.className}\$$lambdaName")
|
||||
|
||||
val lambdaClass = ClassDescriptorImpl(
|
||||
outerClassCodegen.descriptor,
|
||||
Name.identifier(lambdaName),
|
||||
Modality.FINAL,
|
||||
ClassKind.CLASS,
|
||||
listOf(outerClassCodegen.descriptor.module.builtIns.anyType),
|
||||
SourceElement.NO_SOURCE,
|
||||
false,
|
||||
LockBasedStorageManager.NO_LOCKS
|
||||
)
|
||||
lambdaClass.initialize(
|
||||
MemberScope.Empty, emptySet(),
|
||||
DescriptorFactory.createPrimaryConstructorForObject(lambdaClass, lambdaClass.source)
|
||||
)
|
||||
val lambdaClassBuilder = outerClassCodegen.state.factory.newVisitor(
|
||||
JvmDeclarationOrigin(JvmDeclarationOriginKind.OTHER, null, lambdaClass),
|
||||
Type.getObjectType(lambdaType.internalName),
|
||||
outerClassCodegen.myClass.containingKtFile
|
||||
)
|
||||
val classContextForCreator = ClassContext(
|
||||
outerClassCodegen.typeMapper, lambdaClass, OwnerKind.IMPLEMENTATION, outerClassCodegen.context.parentContext, null
|
||||
)
|
||||
val lambdaCodegen = ImplementationBodyCodegen(
|
||||
outerClassCodegen.myClass,
|
||||
classContextForCreator,
|
||||
lambdaClassBuilder,
|
||||
outerClassCodegen.state,
|
||||
outerClassCodegen.parentCodegen,
|
||||
false
|
||||
)
|
||||
lambdaCodegen.v.defineClass(
|
||||
null,
|
||||
outerClassCodegen.state.classFileVersion,
|
||||
Opcodes.ACC_FINAL or Opcodes.ACC_SUPER or Opcodes.ACC_SYNTHETIC,
|
||||
lambdaType.internalName,
|
||||
"L${jvmLambdaType.internalName};L${function0Type.internalName}<L${kSerializerType.internalName}<*>;>;",
|
||||
jvmLambdaType.internalName,
|
||||
arrayOf(function0Type.internalName)
|
||||
)
|
||||
|
||||
outerClassCodegen.v.visitInnerClass(
|
||||
lambdaType.internalName,
|
||||
null,
|
||||
null,
|
||||
Opcodes.ACC_FINAL or Opcodes.ACC_SUPER or Opcodes.ACC_SYNTHETIC or Opcodes.ACC_STATIC
|
||||
)
|
||||
lambdaCodegen.v.visitInnerClass(
|
||||
lambdaType.internalName,
|
||||
null,
|
||||
null,
|
||||
Opcodes.ACC_FINAL or Opcodes.ACC_SUPER or Opcodes.ACC_SYNTHETIC or Opcodes.ACC_STATIC
|
||||
)
|
||||
lambdaCodegen.v.visitOuterClass(
|
||||
outerClassCodegen.className,
|
||||
null,
|
||||
null
|
||||
)
|
||||
lambdaCodegen.v.visitSource(
|
||||
outerClassCodegen.myClass.containingKtFile.name,
|
||||
null
|
||||
)
|
||||
|
||||
val constr = ClassConstructorDescriptorImpl.createSynthesized(
|
||||
lambdaClass,
|
||||
Annotations.EMPTY,
|
||||
false,
|
||||
lambdaClass.source
|
||||
)
|
||||
constr.initialize(
|
||||
emptyList(),
|
||||
DescriptorVisibilities.PUBLIC
|
||||
)
|
||||
constr.returnType = lambdaClass.defaultType
|
||||
lambdaCodegen.generateMethod(constr) { _, _ ->
|
||||
load(0, lambdaType)
|
||||
iconst(0)
|
||||
invokespecial(jvmLambdaType.internalName, "<init>", "(I)V", false)
|
||||
areturn(Type.VOID_TYPE)
|
||||
}
|
||||
|
||||
lambdaCodegen.v.newField(
|
||||
OtherOrigin(lambdaCodegen.myClass.psiOrParent),
|
||||
Opcodes.ACC_PUBLIC or Opcodes.ACC_FINAL or Opcodes.ACC_STATIC or Opcodes.ACC_SYNTHETIC,
|
||||
JvmAbi.INSTANCE_FIELD,
|
||||
lambdaType.descriptor,
|
||||
null,
|
||||
null
|
||||
)
|
||||
val lambdaClInit = lambdaCodegen.createOrGetClInitCodegen()
|
||||
with(lambdaClInit.v) {
|
||||
anew(lambdaType)
|
||||
dup()
|
||||
invokespecial(lambdaType.internalName, "<init>", "()V", false)
|
||||
putstatic(lambdaType.internalName, JvmAbi.INSTANCE_FIELD, lambdaType.descriptor)
|
||||
areturn(Type.VOID_TYPE)
|
||||
visitEnd()
|
||||
}
|
||||
|
||||
val invokeFunction = SimpleFunctionDescriptorImpl.create(
|
||||
lambdaCodegen.descriptor,
|
||||
Annotations.EMPTY,
|
||||
Name.identifier("invoke"),
|
||||
CallableMemberDescriptor.Kind.SYNTHESIZED,
|
||||
lambdaCodegen.descriptor.source
|
||||
)
|
||||
|
||||
invokeFunction.initialize(
|
||||
null,
|
||||
lambdaCodegen.descriptor.thisAsReceiverParameter,
|
||||
emptyList(),
|
||||
emptyList(),
|
||||
emptyList(),
|
||||
resultSimpleType,
|
||||
Modality.FINAL,
|
||||
DescriptorVisibilities.PUBLIC
|
||||
)
|
||||
|
||||
lambdaCodegen.generateMethod(invokeFunction) { _, expressionCodegen ->
|
||||
block(lambdaCodegen, expressionCodegen)
|
||||
}
|
||||
|
||||
val bridgeInvokeFunction = SimpleFunctionDescriptorImpl.create(
|
||||
lambdaCodegen.descriptor,
|
||||
Annotations.EMPTY,
|
||||
Name.identifier("invoke"),
|
||||
CallableMemberDescriptor.Kind.SYNTHESIZED,
|
||||
lambdaCodegen.descriptor.source
|
||||
)
|
||||
|
||||
bridgeInvokeFunction.initialize(
|
||||
null,
|
||||
lambdaCodegen.descriptor.thisAsReceiverParameter,
|
||||
emptyList(),
|
||||
emptyList(),
|
||||
emptyList(),
|
||||
lambdaCodegen.descriptor.builtIns.anyType,
|
||||
Modality.FINAL,
|
||||
DescriptorVisibilities.PUBLIC
|
||||
)
|
||||
|
||||
lambdaCodegen.generateMethod(bridgeInvokeFunction) { _, _ ->
|
||||
load(0, lambdaType)
|
||||
invokevirtual(lambdaType.internalName, "invoke", "()L${resultSimpleType.toClassDescriptor.classId!!.internalName};", false)
|
||||
areturn(kSerializerType)
|
||||
}
|
||||
|
||||
writeSyntheticClassMetadata(lambdaClassBuilder, lambdaCodegen.state, false)
|
||||
lambdaClassBuilder.done()
|
||||
|
||||
return lambdaType
|
||||
}
|
||||
+102
@@ -0,0 +1,102 @@
|
||||
/*
|
||||
* 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.jvm
|
||||
|
||||
import org.jetbrains.kotlin.codegen.ImplementationBodyCodegen
|
||||
import org.jetbrains.kotlin.descriptors.*
|
||||
import org.jetbrains.kotlin.descriptors.annotations.Annotations
|
||||
import org.jetbrains.kotlin.descriptors.impl.ClassConstructorDescriptorImpl
|
||||
import org.jetbrains.kotlin.descriptors.impl.SimpleFunctionDescriptorImpl
|
||||
import org.jetbrains.kotlin.descriptors.impl.ValueParameterDescriptorImpl
|
||||
import org.jetbrains.kotlinx.serialization.compiler.resolve.KSerializerDescriptorResolver
|
||||
import org.jetbrains.kotlin.resolve.BindingContext
|
||||
import org.jetbrains.kotlin.resolve.jvm.diagnostics.OtherOrigin
|
||||
import org.jetbrains.kotlin.resolve.scopes.getDescriptorsFiltered
|
||||
import org.jetbrains.org.objectweb.asm.Opcodes
|
||||
import org.jetbrains.org.objectweb.asm.Type
|
||||
|
||||
class SerialInfoCodegenImpl(val codegen: ImplementationBodyCodegen, val thisClass: ClassDescriptor, val bindingContext: BindingContext) {
|
||||
val thisAsmType = codegen.typeMapper.mapClass(thisClass)
|
||||
|
||||
fun generate() {
|
||||
val props = thisClass.unsubstitutedMemberScope.getDescriptorsFiltered().filterIsInstance<PropertyDescriptor>()
|
||||
if (props.isEmpty()) return
|
||||
|
||||
generateFieldsAndSetters(props)
|
||||
generateConstructor(props)
|
||||
}
|
||||
|
||||
private fun generateFieldsAndSetters(props: List<PropertyDescriptor>) {
|
||||
props.forEach { prop ->
|
||||
val propType = codegen.typeMapper.mapType(prop.type)
|
||||
val propFieldName = "_" + prop.name.identifier
|
||||
codegen.v.newField(OtherOrigin(codegen.myClass.psiOrParent), Opcodes.ACC_PRIVATE or Opcodes.ACC_FINAL or Opcodes.ACC_SYNTHETIC,
|
||||
propFieldName, propType.descriptor, null, null)
|
||||
val f = SimpleFunctionDescriptorImpl.create(thisClass, Annotations.EMPTY, prop.name, CallableMemberDescriptor.Kind.SYNTHESIZED, thisClass.source)
|
||||
f.initialize(null, thisClass.thisAsReceiverParameter, emptyList(), emptyList(), emptyList(), prop.type, Modality.FINAL, DescriptorVisibilities.PUBLIC)
|
||||
codegen.generateMethod(f, { _, _ ->
|
||||
load(0, thisAsmType)
|
||||
getfield(thisAsmType.internalName, propFieldName, propType.descriptor)
|
||||
areturn(propType)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
private fun generateConstructor(props: List<PropertyDescriptor>) {
|
||||
val constr = ClassConstructorDescriptorImpl.createSynthesized(
|
||||
thisClass,
|
||||
Annotations.EMPTY,
|
||||
false,
|
||||
thisClass.source
|
||||
)
|
||||
val args = mutableListOf<ValueParameterDescriptor>()
|
||||
var i = 0
|
||||
props.forEach { prop ->
|
||||
args.add(ValueParameterDescriptorImpl(constr, null, i++, Annotations.EMPTY, prop.name, prop.type, false, false, false, null, constr.source))
|
||||
}
|
||||
constr.initialize(
|
||||
args,
|
||||
DescriptorVisibilities.PUBLIC
|
||||
)
|
||||
|
||||
constr.returnType = thisClass.defaultType
|
||||
|
||||
codegen.generateMethod(constr, { _, _ ->
|
||||
load(0, thisAsmType)
|
||||
invokespecial("java/lang/Object", "<init>", "()V", false)
|
||||
var varOffset = 1
|
||||
props.forEach { prop ->
|
||||
val propType = codegen.typeMapper.mapType(prop.type)
|
||||
val propFieldName = "_" + prop.name.identifier
|
||||
load(0, thisAsmType)
|
||||
load(varOffset, propType)
|
||||
putfield(thisAsmType.internalName, propFieldName, propType.descriptor)
|
||||
varOffset += propType.size
|
||||
}
|
||||
areturn(Type.VOID_TYPE)
|
||||
})
|
||||
}
|
||||
|
||||
companion object {
|
||||
fun generateSerialInfoImplBody(codegen: ImplementationBodyCodegen) {
|
||||
val thisClass = codegen.descriptor
|
||||
if (KSerializerDescriptorResolver.isSerialInfoImpl(thisClass))
|
||||
SerialInfoCodegenImpl(codegen, thisClass, codegen.bindingContext).generate()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+435
@@ -0,0 +1,435 @@
|
||||
/*
|
||||
* Copyright 2010-2021 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlinx.serialization.compiler.backend.jvm
|
||||
|
||||
import org.jetbrains.kotlin.codegen.*
|
||||
import org.jetbrains.kotlin.config.ApiVersion
|
||||
import org.jetbrains.kotlin.descriptors.ClassConstructorDescriptor
|
||||
import org.jetbrains.kotlin.descriptors.FunctionDescriptor
|
||||
import org.jetbrains.kotlin.descriptors.PropertyDescriptor
|
||||
import org.jetbrains.kotlin.js.resolve.diagnostics.findPsi
|
||||
import org.jetbrains.kotlin.lexer.KtTokens
|
||||
import org.jetbrains.kotlin.psi.KtDelegatedSuperTypeEntry
|
||||
import org.jetbrains.kotlin.psi.KtExpression
|
||||
import org.jetbrains.kotlin.psi.KtParameter
|
||||
import org.jetbrains.kotlin.resolve.BindingContext
|
||||
import org.jetbrains.kotlin.resolve.DescriptorUtils
|
||||
import org.jetbrains.kotlin.resolve.descriptorUtil.getSuperClassOrAny
|
||||
import org.jetbrains.kotlin.resolve.descriptorUtil.module
|
||||
import org.jetbrains.kotlin.resolve.jvm.diagnostics.OtherOrigin
|
||||
import org.jetbrains.kotlinx.serialization.compiler.backend.common.*
|
||||
import org.jetbrains.kotlinx.serialization.compiler.diagnostic.VersionReader
|
||||
import org.jetbrains.kotlinx.serialization.compiler.diagnostic.serializableAnnotationIsUseless
|
||||
import org.jetbrains.kotlinx.serialization.compiler.resolve.*
|
||||
import org.jetbrains.kotlinx.serialization.compiler.resolve.SerialEntityNames.ARRAY_MASK_FIELD_MISSING_FUNC_NAME
|
||||
import org.jetbrains.kotlinx.serialization.compiler.resolve.SerialEntityNames.CACHED_DESCRIPTOR_FIELD
|
||||
import org.jetbrains.kotlinx.serialization.compiler.resolve.SerialEntityNames.SINGLE_MASK_FIELD_MISSING_FUNC_NAME
|
||||
import org.jetbrains.org.objectweb.asm.Label
|
||||
import org.jetbrains.org.objectweb.asm.Opcodes
|
||||
import org.jetbrains.org.objectweb.asm.Type
|
||||
import org.jetbrains.org.objectweb.asm.commons.InstructionAdapter
|
||||
|
||||
class SerializableCodegenImpl(
|
||||
private val classCodegen: ImplementationBodyCodegen
|
||||
) : SerializableCodegen(classCodegen.descriptor, classCodegen.bindingContext) {
|
||||
|
||||
private val thisAsmType = classCodegen.typeMapper.mapClass(serializableDescriptor)
|
||||
private val fieldMissingOptimizationVersion = ApiVersion.parse("1.1")!!
|
||||
private val useFieldMissingOptimization = canUseFieldMissingOptimization()
|
||||
|
||||
companion object {
|
||||
fun generateSerializableExtensions(codegen: ImplementationBodyCodegen) {
|
||||
val serializableClass = codegen.descriptor
|
||||
if (serializableClass.isInternalSerializable) {
|
||||
SerializableCodegenImpl(codegen).generate()
|
||||
} else if (serializableClass.serializableAnnotationIsUseless) {
|
||||
throw CompilationException(
|
||||
"@Serializable annotation on $serializableClass would be ignored because it is impossible to serialize it automatically. " +
|
||||
"Provide serializer manually via e.g. companion object", null, serializableClass.findPsi()
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private val descToProps = classCodegen.myClass.bodyPropertiesDescriptorsMap(classCodegen.bindingContext)
|
||||
|
||||
private val paramsToProps: Map<PropertyDescriptor, KtParameter> =
|
||||
classCodegen.myClass.primaryConstructorPropertiesDescriptorsMap(classCodegen.bindingContext)
|
||||
|
||||
private fun getProp(prop: SerializableProperty) = descToProps[prop.descriptor]
|
||||
private fun getParam(prop: SerializableProperty) = paramsToProps[prop.descriptor]
|
||||
|
||||
private fun initializersMapper(prop: SerializableProperty): Pair<KtExpression, Type> {
|
||||
val maybeInit =
|
||||
getProp(prop)?.let { it.delegateExpressionOrInitializer ?: throw AssertionError("${it.name} property must have initializer") }
|
||||
|
||||
val initializer = maybeInit ?: getParam(prop)?.let {
|
||||
it.defaultValue ?: throw AssertionError("${it.name} property must have initializer")
|
||||
}
|
||||
|
||||
return if (initializer == null) throw AssertionError("Can't find initializer for property ${prop.descriptor}")
|
||||
else initializer to classCodegen.typeMapper.mapType(prop.type)
|
||||
}
|
||||
|
||||
private val SerializableProperty.asmType get() = classCodegen.typeMapper.mapType(this.type)
|
||||
|
||||
override fun generateInternalConstructor(constructorDescriptor: ClassConstructorDescriptor) {
|
||||
classCodegen.generateMethod(constructorDescriptor) { _, expr -> doGenerateConstructorImpl(expr) }
|
||||
}
|
||||
|
||||
override fun generateWriteSelfMethod(methodDescriptor: FunctionDescriptor) {
|
||||
classCodegen.generateMethod(methodDescriptor) { _, expr -> doGenerateWriteSelf(expr) }
|
||||
}
|
||||
|
||||
private fun InstructionAdapter.doGenerateWriteSelf(exprCodegen: ExpressionCodegen) {
|
||||
val thisI = 0
|
||||
val outputI = 1
|
||||
val serialDescI = 2
|
||||
val offsetI = 3
|
||||
|
||||
val superClass = serializableDescriptor.getSuperClassOrAny()
|
||||
val myPropsStart: Int
|
||||
if (superClass.isInternalSerializable) {
|
||||
myPropsStart = bindingContext!!.serializablePropertiesFor(superClass).serializableProperties.size
|
||||
val superTypeArguments =
|
||||
serializableDescriptor.typeConstructor.supertypes.single { it.toClassDescriptor?.isInternalSerializable == true }.arguments
|
||||
//super.writeSelf(output, serialDesc)
|
||||
load(thisI, thisAsmType)
|
||||
load(outputI, kOutputType)
|
||||
load(serialDescI, descType)
|
||||
superTypeArguments.forEach {
|
||||
val genericIdx = serializableDescriptor.defaultType.arguments.indexOf(it).let { if (it == -1) null else it }
|
||||
val serial = findTypeSerializerOrContext(serializableDescriptor.module, it.type)
|
||||
stackValueSerializerInstance(
|
||||
exprCodegen,
|
||||
classCodegen,
|
||||
serializableDescriptor.module,
|
||||
it.type,
|
||||
serial,
|
||||
this,
|
||||
genericIdx
|
||||
) { i, _ ->
|
||||
load(offsetI + i, kSerializerType)
|
||||
}
|
||||
}
|
||||
val superSignature =
|
||||
classCodegen.typeMapper.mapSignatureSkipGeneric(KSerializerDescriptorResolver.createWriteSelfFunctionDescriptor(superClass))
|
||||
invokestatic(
|
||||
classCodegen.typeMapper.mapType(superClass).internalName,
|
||||
superSignature.asmMethod.name,
|
||||
superSignature.asmMethod.descriptor,
|
||||
false
|
||||
)
|
||||
} else {
|
||||
myPropsStart = 0
|
||||
}
|
||||
|
||||
fun emitEncoderCall(property: SerializableProperty, index: Int) {
|
||||
// output.writeXxxElementValue (desc, index, value)
|
||||
load(outputI, kOutputType)
|
||||
load(serialDescI, descType)
|
||||
iconst(index)
|
||||
genKOutputMethodCall(
|
||||
property,
|
||||
classCodegen,
|
||||
exprCodegen,
|
||||
thisAsmType,
|
||||
thisI,
|
||||
offsetI,
|
||||
generator = this@SerializableCodegenImpl
|
||||
)
|
||||
}
|
||||
|
||||
for (i in myPropsStart until properties.serializableProperties.size) {
|
||||
val property = properties[i]
|
||||
if (!property.optional) {
|
||||
emitEncoderCall(property, i)
|
||||
} else {
|
||||
val writeLabel = Label()
|
||||
val nonWriteLabel = Label()
|
||||
// obj.prop != DEFAULT_VAL
|
||||
val propAsmType = classCodegen.typeMapper.mapType(property.type)
|
||||
val actualType: JvmKotlinType = ImplementationBodyCodegen.genPropertyOnStack(
|
||||
this,
|
||||
exprCodegen.context,
|
||||
property.descriptor,
|
||||
thisAsmType,
|
||||
thisI,
|
||||
classCodegen.state
|
||||
)
|
||||
StackValue.coerce(actualType.type, propAsmType, this)
|
||||
val lhs = StackValue.onStack(propAsmType)
|
||||
val (expr, _) = initializersMapper(property)
|
||||
exprCodegen.gen(expr, propAsmType)
|
||||
val rhs = StackValue.onStack(propAsmType)
|
||||
// INVOKESTATIC kotlin/jvm/internal/Intrinsics.areEqual (Ljava/lang/Object;Ljava/lang/Object;)Z
|
||||
DescriptorAsmUtil.genEqualsForExpressionsOnStack(KtTokens.EXCLEQ, lhs, rhs).put(Type.BOOLEAN_TYPE, null, this)
|
||||
ifne(writeLabel)
|
||||
|
||||
// output.shouldEncodeElementDefault(descriptor, i)
|
||||
load(outputI, kOutputType)
|
||||
load(serialDescI, descType)
|
||||
iconst(i)
|
||||
invokeinterface(kOutputType.internalName, CallingConventions.shouldEncodeDefault, "(${descType.descriptor}I)Z")
|
||||
ifeq(nonWriteLabel)
|
||||
|
||||
visitLabel(writeLabel)
|
||||
emitEncoderCall(property, i)
|
||||
visitLabel(nonWriteLabel)
|
||||
}
|
||||
}
|
||||
|
||||
areturn(Type.VOID_TYPE)
|
||||
}
|
||||
|
||||
private fun InstructionAdapter.doGenerateConstructorImpl(exprCodegen: ExpressionCodegen) {
|
||||
val seenMaskVar = 1
|
||||
val bitMaskOff = fun(it: Int): Int { return seenMaskVar + bitMaskSlotAt(it) }
|
||||
val bitMaskEnd = seenMaskVar + properties.serializableProperties.bitMaskSlotCount()
|
||||
|
||||
if (useFieldMissingOptimization) {
|
||||
generateOptimizedGoldenMaskCheck(seenMaskVar)
|
||||
}
|
||||
|
||||
var (propIndex, propOffset) = generateSuperSerializableCall(seenMaskVar, bitMaskEnd)
|
||||
for (i in propIndex until properties.serializableProperties.size) {
|
||||
val prop = properties[i]
|
||||
val propType = prop.asmType
|
||||
if (!prop.optional) {
|
||||
if (!useFieldMissingOptimization) {
|
||||
// primary were validated before constructor call
|
||||
genValidateProperty(i, bitMaskOff(i))
|
||||
val nonThrowLabel = Label()
|
||||
ificmpne(nonThrowLabel)
|
||||
genMissingFieldExceptionThrow(prop.name)
|
||||
visitLabel(nonThrowLabel)
|
||||
}
|
||||
|
||||
// setting field
|
||||
load(0, thisAsmType)
|
||||
load(propOffset, propType)
|
||||
putfield(thisAsmType.internalName, prop.descriptor.name.asString(), propType.descriptor)
|
||||
} else {
|
||||
genValidateProperty(i, bitMaskOff(i))
|
||||
val setLbl = Label()
|
||||
val nextLabel = Label()
|
||||
ificmpeq(setLbl)
|
||||
// setting field
|
||||
load(0, thisAsmType)
|
||||
load(propOffset, propType)
|
||||
putfield(thisAsmType.internalName, prop.descriptor.name.asString(), propType.descriptor)
|
||||
goTo(nextLabel)
|
||||
visitLabel(setLbl)
|
||||
// setting defaultValue
|
||||
if (classCodegen.bindingContext[BindingContext.BACKING_FIELD_REQUIRED, prop.descriptor] != true)
|
||||
throw CompilationException(
|
||||
"Optional properties without backing fields doesn't have much sense, maybe you want transient?",
|
||||
null,
|
||||
getProp(prop)
|
||||
)
|
||||
exprCodegen.genInitProperty(prop)
|
||||
visitLabel(nextLabel)
|
||||
}
|
||||
propOffset += prop.asmType.size
|
||||
}
|
||||
|
||||
// these properties required to be manually invoked, because they are not in serializableProperties
|
||||
val serializedProps = properties.serializableProperties.map { it.descriptor }.toSet()
|
||||
|
||||
(descToProps - serializedProps)
|
||||
.filter { classCodegen.shouldInitializeProperty(it.value) }
|
||||
.forEach { (_, prop) -> classCodegen.initializeProperty(exprCodegen, prop) }
|
||||
(paramsToProps - serializedProps)
|
||||
.forEach { (t, u) -> exprCodegen.genInitParam(t, u) }
|
||||
|
||||
// Initialize delegates
|
||||
var delegate = 0
|
||||
for (specifier in classCodegen.myClass.superTypeListEntries) {
|
||||
if (specifier is KtDelegatedSuperTypeEntry) {
|
||||
val expr = specifier.delegateExpression!!
|
||||
|
||||
load(0, thisAsmType)
|
||||
val stackValue = exprCodegen.gen(expr)
|
||||
stackValue.put(exprCodegen.v)
|
||||
|
||||
putfield(
|
||||
thisAsmType.internalName,
|
||||
"\$\$delegate_${delegate++}",
|
||||
stackValue.type.descriptor
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
// init blocks
|
||||
// todo: proper order with other initializers?
|
||||
classCodegen.myClass.anonymousInitializers()
|
||||
.forEach { exprCodegen.gen(it, Type.VOID_TYPE) }
|
||||
areturn(Type.VOID_TYPE)
|
||||
}
|
||||
|
||||
private fun InstructionAdapter.generateSuperSerializableCall(maskVar: Int, propStartVar: Int): Pair<Int, Int> {
|
||||
val superClass = serializableDescriptor.getSuperClassOrAny()
|
||||
val superType = classCodegen.typeMapper.mapType(superClass).internalName
|
||||
|
||||
load(0, thisAsmType)
|
||||
|
||||
if (!superClass.isInternalSerializable) {
|
||||
require(superClass.constructors.firstOrNull { it.valueParameters.isEmpty() } != null) {
|
||||
"Non-serializable parent of serializable $serializableDescriptor must have no arg constructor"
|
||||
}
|
||||
|
||||
// call
|
||||
// Sealed classes have private <init> so they cannot be inherited from Java src
|
||||
// public <init> is synthetic and contains additional parameter
|
||||
val desc = if (DescriptorUtils.isSealedClass(superClass)) {
|
||||
aconst(null)
|
||||
"(Lkotlin/jvm/internal/DefaultConstructorMarker;)V"
|
||||
} else {
|
||||
"()V"
|
||||
}
|
||||
invokespecial(superType, "<init>", desc, false)
|
||||
return 0 to propStartVar
|
||||
} else {
|
||||
val superProps = bindingContext!!.serializablePropertiesFor(superClass).serializableProperties
|
||||
val creator = buildInternalConstructorDesc(propStartVar, maskVar, classCodegen, superProps)
|
||||
invokespecial(superType, "<init>", creator, false)
|
||||
return superProps.size to propStartVar + superProps.sumOf { it.asmType.size }
|
||||
}
|
||||
}
|
||||
|
||||
private fun InstructionAdapter.generateOptimizedGoldenMaskCheck(maskVar: Int) {
|
||||
if (serializableDescriptor.isAbstractOrSealedSerializableClass()) {
|
||||
// for abstract classes fields MUST BE checked in child classes
|
||||
return
|
||||
}
|
||||
|
||||
val allPresentsLabel = Label()
|
||||
val maskSlotCount = properties.serializableProperties.bitMaskSlotCount()
|
||||
if (maskSlotCount == 1) {
|
||||
val goldenMask = properties.goldenMask
|
||||
|
||||
iconst(goldenMask)
|
||||
dup()
|
||||
load(maskVar, OPT_MASK_TYPE)
|
||||
and(OPT_MASK_TYPE)
|
||||
ificmpeq(allPresentsLabel)
|
||||
|
||||
load(maskVar, OPT_MASK_TYPE)
|
||||
iconst(goldenMask)
|
||||
|
||||
stackSerialDescriptor()
|
||||
invokestatic(
|
||||
pluginUtilsType.internalName,
|
||||
SINGLE_MASK_FIELD_MISSING_FUNC_NAME.asString(),
|
||||
"(II${descType.descriptor})V",
|
||||
false
|
||||
)
|
||||
} else {
|
||||
val fieldsMissingLabel = Label()
|
||||
|
||||
val goldenMaskList = properties.goldenMaskList
|
||||
goldenMaskList.forEachIndexed { i, goldenMask ->
|
||||
val maskIndex = maskVar + i
|
||||
// if( (goldenMask & seen) != goldenMask )
|
||||
iconst(goldenMask)
|
||||
dup()
|
||||
load(maskIndex, OPT_MASK_TYPE)
|
||||
and(OPT_MASK_TYPE)
|
||||
ificmpne(fieldsMissingLabel)
|
||||
}
|
||||
goTo(allPresentsLabel)
|
||||
|
||||
visitLabel(fieldsMissingLabel)
|
||||
// prepare seen array
|
||||
fillArray(OPT_MASK_TYPE, goldenMaskList) { i, _ ->
|
||||
load(maskVar + i, OPT_MASK_TYPE)
|
||||
}
|
||||
// prepare golden mask array
|
||||
fillArray(OPT_MASK_TYPE, goldenMaskList) { _, goldenMask ->
|
||||
iconst(goldenMask)
|
||||
}
|
||||
stackSerialDescriptor()
|
||||
invokestatic(
|
||||
pluginUtilsType.internalName,
|
||||
ARRAY_MASK_FIELD_MISSING_FUNC_NAME.asString(),
|
||||
"([I[I${descType.descriptor})V",
|
||||
false
|
||||
)
|
||||
}
|
||||
visitLabel(allPresentsLabel)
|
||||
}
|
||||
|
||||
private fun InstructionAdapter.stackSerialDescriptor() {
|
||||
if (serializableDescriptor.isStaticSerializable) {
|
||||
val serializer = serializableDescriptor.classSerializer!!
|
||||
StackValue.singleton(serializer, classCodegen.typeMapper).put(kSerializerType, this)
|
||||
invokeinterface(kSerializerType.internalName, descriptorGetterName, "()${descType.descriptor}")
|
||||
} else {
|
||||
generateStaticDescriptorField()
|
||||
|
||||
getstatic(thisAsmType.internalName, CACHED_DESCRIPTOR_FIELD, descType.descriptor)
|
||||
}
|
||||
}
|
||||
|
||||
private fun generateStaticDescriptorField() {
|
||||
val flags = Opcodes.ACC_PRIVATE or Opcodes.ACC_FINAL or Opcodes.ACC_SYNTHETIC or Opcodes.ACC_STATIC
|
||||
classCodegen.v.newField(
|
||||
OtherOrigin(classCodegen.myClass.psiOrParent), flags,
|
||||
CACHED_DESCRIPTOR_FIELD, descType.descriptor, null, null
|
||||
)
|
||||
|
||||
val clInit = classCodegen.createOrGetClInitCodegen()
|
||||
with(clInit.v) {
|
||||
anew(descImplType)
|
||||
dup()
|
||||
aconst(serializableDescriptor.serialName())
|
||||
aconst(null)
|
||||
aconst(properties.serializableProperties.size)
|
||||
invokespecial(descImplType.internalName, "<init>", "(Ljava/lang/String;${generatedSerializerType.descriptor}I)V", false)
|
||||
for (property in properties.serializableProperties) {
|
||||
dup()
|
||||
aconst(property.name)
|
||||
iconst(if (property.optional) 1 else 0)
|
||||
invokevirtual(descImplType.internalName, CallingConventions.addElement, "(Ljava/lang/String;Z)V", false)
|
||||
}
|
||||
|
||||
putstatic(thisAsmType.internalName, CACHED_DESCRIPTOR_FIELD, descType.descriptor)
|
||||
}
|
||||
}
|
||||
|
||||
private fun ExpressionCodegen.genInitProperty(prop: SerializableProperty) = getProp(prop)?.let {
|
||||
classCodegen.initializeProperty(this, it)
|
||||
}
|
||||
?: getParam(prop)?.let {
|
||||
this.v.load(0, thisAsmType)
|
||||
if (!it.hasDefaultValue()) throw CompilationException(
|
||||
"Optional field ${it.name} in primary constructor of serializable " +
|
||||
"$serializableDescriptor must have default value", null, it
|
||||
)
|
||||
this.gen(it.defaultValue, prop.asmType)
|
||||
this.v.putfield(thisAsmType.internalName, prop.descriptor.name.asString(), prop.asmType.descriptor)
|
||||
}
|
||||
?: throw IllegalStateException()
|
||||
|
||||
private fun ExpressionCodegen.genInitParam(prop: PropertyDescriptor, param: KtParameter) {
|
||||
this.v.load(0, thisAsmType)
|
||||
val mapType = classCodegen.typeMapper.mapType(prop.type)
|
||||
if (!param.hasDefaultValue()) throw CompilationException(
|
||||
"Transient field ${param.name} in primary constructor of serializable " +
|
||||
"$serializableDescriptor must have default value", null, param
|
||||
)
|
||||
this.gen(param.defaultValue, mapType)
|
||||
this.v.putfield(thisAsmType.internalName, prop.name.asString(), mapType.descriptor)
|
||||
}
|
||||
|
||||
private fun canUseFieldMissingOptimization(): Boolean {
|
||||
val implementationVersion = VersionReader.getVersionsForCurrentModuleFromContext(
|
||||
currentDeclaration.module,
|
||||
bindingContext
|
||||
)?.implementationVersion
|
||||
return if (implementationVersion != null) implementationVersion >= fieldMissingOptimizationVersion else false
|
||||
}
|
||||
}
|
||||
+129
@@ -0,0 +1,129 @@
|
||||
/*
|
||||
* 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.jvm
|
||||
|
||||
import org.jetbrains.kotlin.codegen.ImplementationBodyCodegen
|
||||
import org.jetbrains.kotlin.descriptors.FunctionDescriptor
|
||||
import org.jetbrains.kotlin.load.java.JvmAbi
|
||||
import org.jetbrains.kotlin.resolve.descriptorUtil.module
|
||||
import org.jetbrains.kotlin.resolve.jvm.diagnostics.OtherOrigin
|
||||
import org.jetbrains.kotlinx.serialization.compiler.backend.common.SerializableCompanionCodegen
|
||||
import org.jetbrains.kotlinx.serialization.compiler.backend.common.findTypeSerializer
|
||||
import org.jetbrains.kotlinx.serialization.compiler.resolve.*
|
||||
import org.jetbrains.kotlinx.serialization.compiler.resolve.SerialEntityNames.CACHED_SERIALIZER_PROPERTY
|
||||
import org.jetbrains.kotlinx.serialization.compiler.resolve.SerializationDependencies.LAZY_PUBLICATION_MODE_NAME
|
||||
import org.jetbrains.kotlinx.serialization.compiler.resolve.getKSerializer
|
||||
import org.jetbrains.kotlinx.serialization.compiler.resolve.getSerializableClassDescriptorByCompanion
|
||||
import org.jetbrains.kotlinx.serialization.compiler.resolve.shouldHaveGeneratedMethodsInCompanion
|
||||
import org.jetbrains.org.objectweb.asm.Opcodes
|
||||
|
||||
class SerializableCompanionCodegenImpl(private val classCodegen: ImplementationBodyCodegen) :
|
||||
SerializableCompanionCodegen(classCodegen.descriptor, classCodegen.bindingContext) {
|
||||
|
||||
companion object {
|
||||
fun generateSerializableExtensions(codegen: ImplementationBodyCodegen) {
|
||||
val serializableClass = getSerializableClassDescriptorByCompanion(codegen.descriptor) ?: return
|
||||
if (serializableClass.shouldHaveGeneratedMethodsInCompanion)
|
||||
SerializableCompanionCodegenImpl(codegen).generate()
|
||||
}
|
||||
}
|
||||
|
||||
override fun generateLazySerializerGetter(methodDescriptor: FunctionDescriptor) {
|
||||
val fieldName = "$CACHED_SERIALIZER_PROPERTY\$delegate"
|
||||
|
||||
// Create field for lazy delegate
|
||||
classCodegen.v.newField(
|
||||
OtherOrigin(classCodegen.myClass.psiOrParent),
|
||||
Opcodes.ACC_PRIVATE or Opcodes.ACC_FINAL or Opcodes.ACC_SYNTHETIC or Opcodes.ACC_STATIC,
|
||||
fieldName,
|
||||
kotlinLazyType.descriptor,
|
||||
"L${kotlinLazyType.internalName}<L${kSerializerType.internalName}<*>;>;",
|
||||
null
|
||||
)
|
||||
|
||||
// create singleton lambda class
|
||||
val lambdaType =
|
||||
createSingletonLambda(
|
||||
"serializer\$1",
|
||||
classCodegen,
|
||||
companionDescriptor.getKSerializer().defaultType
|
||||
) { lambdaCodegen, expressionCodegen ->
|
||||
val serializerDescriptor = requireNotNull(
|
||||
findTypeSerializer(
|
||||
serializableDescriptor.module,
|
||||
serializableDescriptor.toSimpleType()
|
||||
)
|
||||
)
|
||||
stackValueSerializerInstance(
|
||||
expressionCodegen,
|
||||
lambdaCodegen,
|
||||
serializableDescriptor.module,
|
||||
serializableDescriptor.defaultType,
|
||||
serializerDescriptor,
|
||||
this,
|
||||
null
|
||||
)
|
||||
areturn(kSerializerType)
|
||||
}
|
||||
|
||||
// initialize lazy delegate
|
||||
val clInit = classCodegen.createOrGetClInitCodegen()
|
||||
with(clInit.v) {
|
||||
getstatic(threadSafeModeType.internalName, LAZY_PUBLICATION_MODE_NAME.identifier, threadSafeModeType.descriptor)
|
||||
getstatic(lambdaType.internalName, JvmAbi.INSTANCE_FIELD, lambdaType.descriptor)
|
||||
checkcast(function0Type)
|
||||
invokestatic(
|
||||
"kotlin/LazyKt",
|
||||
"lazy",
|
||||
"(${threadSafeModeType.descriptor}${function0Type.descriptor})${kotlinLazyType.descriptor}",
|
||||
false
|
||||
)
|
||||
putstatic(classCodegen.className, fieldName, kotlinLazyType.descriptor)
|
||||
}
|
||||
|
||||
// create serializer getter
|
||||
classCodegen.generateMethod(methodDescriptor) { _, _ ->
|
||||
getstatic(classCodegen.className, fieldName, kotlinLazyType.descriptor)
|
||||
invokeinterface(kotlinLazyType.internalName, getLazyValueName, "()Ljava/lang/Object;")
|
||||
checkcast(kSerializerType)
|
||||
areturn(kSerializerType)
|
||||
}
|
||||
}
|
||||
|
||||
override fun generateSerializerGetter(methodDescriptor: FunctionDescriptor) {
|
||||
val serial = requireNotNull(
|
||||
findTypeSerializer(
|
||||
serializableDescriptor.module,
|
||||
serializableDescriptor.toSimpleType()
|
||||
)
|
||||
)
|
||||
classCodegen.generateMethod(methodDescriptor) { _, expressionCodegen ->
|
||||
stackValueSerializerInstance(
|
||||
expressionCodegen,
|
||||
classCodegen,
|
||||
serializableDescriptor.module,
|
||||
serializableDescriptor.defaultType,
|
||||
serial,
|
||||
this,
|
||||
null
|
||||
) { it, _ ->
|
||||
load(it + 1, kSerializerType)
|
||||
}
|
||||
areturn(kSerializerType)
|
||||
}
|
||||
}
|
||||
}
|
||||
+530
@@ -0,0 +1,530 @@
|
||||
/*
|
||||
* Copyright 2010-2020 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlinx.serialization.compiler.backend.jvm
|
||||
|
||||
import org.jetbrains.kotlin.codegen.*
|
||||
import org.jetbrains.kotlin.descriptors.*
|
||||
import org.jetbrains.kotlin.descriptors.annotations.Annotated
|
||||
import org.jetbrains.kotlin.resolve.jvm.AsmTypes
|
||||
import org.jetbrains.kotlin.resolve.jvm.diagnostics.OtherOrigin
|
||||
import org.jetbrains.kotlinx.serialization.compiler.backend.common.SerializerCodegen
|
||||
import org.jetbrains.kotlinx.serialization.compiler.extensions.SerializationDescriptorSerializerPlugin
|
||||
import org.jetbrains.kotlinx.serialization.compiler.resolve.*
|
||||
import org.jetbrains.kotlinx.serialization.compiler.resolve.SerialEntityNames.typeArgPrefix
|
||||
import org.jetbrains.org.objectweb.asm.Label
|
||||
import org.jetbrains.org.objectweb.asm.Opcodes.*
|
||||
import org.jetbrains.org.objectweb.asm.Type
|
||||
import org.jetbrains.org.objectweb.asm.commons.InstructionAdapter
|
||||
|
||||
open class SerializerCodegenImpl(
|
||||
protected val codegen: ImplementationBodyCodegen,
|
||||
serializableClass: ClassDescriptor,
|
||||
metadataPlugin: SerializationDescriptorSerializerPlugin?
|
||||
) : SerializerCodegen(codegen.descriptor, codegen.bindingContext, metadataPlugin) {
|
||||
|
||||
private val serialDescField = "\$\$serialDesc"
|
||||
|
||||
protected val serializerAsmType = codegen.typeMapper.mapClass(codegen.descriptor)
|
||||
protected val serializableAsmType = codegen.typeMapper.mapClass(serializableClass)
|
||||
|
||||
// if we have type parameters, descriptor initializing must be performed in constructor
|
||||
private val staticDescriptor = serializableDescriptor.declaredTypeParameters.isEmpty()
|
||||
|
||||
companion object {
|
||||
fun generateSerializerExtensions(codegen: ImplementationBodyCodegen, metadataPlugin: SerializationDescriptorSerializerPlugin?) {
|
||||
val serializableClass = getSerializableClassDescriptorBySerializer(codegen.descriptor) ?: return
|
||||
val serializerCodegen = if (serializableClass.isEnumWithLegacyGeneratedSerializer()) {
|
||||
SerializerForEnumsCodegen(codegen, serializableClass)
|
||||
} else {
|
||||
SerializerCodegenImpl(codegen, serializableClass, metadataPlugin)
|
||||
}
|
||||
serializerCodegen.generate()
|
||||
}
|
||||
}
|
||||
|
||||
override fun generateGenericFieldsAndConstructor(typedConstructorDescriptor: ClassConstructorDescriptor) {
|
||||
serializableDescriptor.declaredTypeParameters.forEachIndexed { i, _ ->
|
||||
codegen.v.newField(
|
||||
OtherOrigin(codegen.myClass.psiOrParent), ACC_PRIVATE or ACC_SYNTHETIC,
|
||||
"$typeArgPrefix$i", kSerializerType.descriptor, null, null
|
||||
)
|
||||
}
|
||||
|
||||
var locals: Int = 0
|
||||
codegen.generateMethod(typedConstructorDescriptor) { _, exprGen ->
|
||||
load(0, serializerAsmType)
|
||||
invokespecial("java/lang/Object", "<init>", "()V", false)
|
||||
serializableDescriptor.declaredTypeParameters.forEachIndexed { i, _ ->
|
||||
load(0, serializerAsmType)
|
||||
load(++locals, kSerializerType)
|
||||
putfield(serializerAsmType.internalName, "$typeArgPrefix$i", kSerializerType.descriptor)
|
||||
}
|
||||
if (!staticDescriptor) exprGen.generateSerialDescriptor(++locals, false)
|
||||
areturn(Type.VOID_TYPE)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private fun ExpressionCodegen.generateSerialDescriptor(descriptorVar: Int, isStatic: Boolean) = with(v) {
|
||||
instantiateNewDescriptor(isStatic)
|
||||
store(descriptorVar, descImplType)
|
||||
// add contents
|
||||
addElementsContentToDescriptor(descriptorVar)
|
||||
// add annotations on class itself
|
||||
addSyntheticAnnotationsToDescriptor(descriptorVar, serializableDescriptor, CallingConventions.addClassAnnotation)
|
||||
if (isStatic) {
|
||||
load(descriptorVar, descImplType)
|
||||
putstatic(serializerAsmType.internalName, serialDescField, descType.descriptor)
|
||||
} else {
|
||||
load(0, serializerAsmType)
|
||||
load(descriptorVar, descImplType)
|
||||
putfield(serializerAsmType.internalName, serialDescField, descType.descriptor)
|
||||
}
|
||||
}
|
||||
|
||||
protected open fun ExpressionCodegen.instantiateNewDescriptor(isStatic: Boolean) = with(v) {
|
||||
anew(descImplType)
|
||||
dup()
|
||||
aconst(serialName)
|
||||
if (isStatic) {
|
||||
assert(serializerDescriptor.kind == ClassKind.OBJECT) { "Serializer for type without type parameters must be an object" }
|
||||
// static descriptor means serializer is an object. it is safer to get it from correct field
|
||||
if (isGeneratedSerializer)
|
||||
StackValue.singleton(serializerDescriptor, codegen.typeMapper).put(generatedSerializerType, this)
|
||||
else
|
||||
aconst(null)
|
||||
} else {
|
||||
load(0, serializerAsmType)
|
||||
}
|
||||
aconst(serializableProperties.size)
|
||||
invokespecial(descImplType.internalName, "<init>", "(Ljava/lang/String;${generatedSerializerType.descriptor}I)V", false)
|
||||
}
|
||||
|
||||
protected open fun ExpressionCodegen.addElementsContentToDescriptor(descriptorVar: Int) = with(v) {
|
||||
for (property in serializableProperties) {
|
||||
if (property.transient) continue
|
||||
load(descriptorVar, descImplType)
|
||||
aconst(property.name)
|
||||
iconst(if (property.optional) 1 else 0)
|
||||
invokevirtual(descImplType.internalName, CallingConventions.addElement, "(Ljava/lang/String;Z)V", false)
|
||||
// pushing annotations
|
||||
addSyntheticAnnotationsToDescriptor(descriptorVar, property.descriptor, CallingConventions.addAnnotation)
|
||||
}
|
||||
}
|
||||
|
||||
protected fun ExpressionCodegen.addSyntheticAnnotationsToDescriptor(descriptorVar: Int, annotated: Annotated, functionToCall: String) =
|
||||
with(v) {
|
||||
for ((annotationClass, args, consParams) in annotated.annotationsWithArguments()) {
|
||||
if (args.size != consParams.size) throw IllegalArgumentException("Can't use arguments with defaults for serializable annotations yet")
|
||||
load(descriptorVar, descImplType)
|
||||
generateSyntheticAnnotationOnStack(annotationClass, args, consParams)
|
||||
invokevirtual(
|
||||
descImplType.internalName,
|
||||
functionToCall,
|
||||
"(Ljava/lang/annotation/Annotation;)V",
|
||||
false
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
override fun generateSerialDesc() {
|
||||
var flags = ACC_PRIVATE or ACC_FINAL or ACC_SYNTHETIC
|
||||
if (staticDescriptor) flags = flags or ACC_STATIC
|
||||
codegen.v.newField(
|
||||
OtherOrigin(codegen.myClass.psiOrParent), flags,
|
||||
serialDescField, descType.descriptor, null, null
|
||||
)
|
||||
// todo: lazy initialization of $$serialDesc ?
|
||||
if (!staticDescriptor) return
|
||||
val expr = codegen.createOrGetClInitCodegen()
|
||||
expr.generateSerialDescriptor(0, true)
|
||||
}
|
||||
|
||||
// use null to put value on stack, use number to store it to var
|
||||
protected fun InstructionAdapter.stackSerialClassDesc(classDescVar: Int?) {
|
||||
if (staticDescriptor)
|
||||
getstatic(serializerAsmType.internalName, serialDescField, descType.descriptor)
|
||||
else {
|
||||
load(0, serializerAsmType)
|
||||
getfield(serializerAsmType.internalName, serialDescField, descType.descriptor)
|
||||
}
|
||||
classDescVar?.let { store(it, descType) }
|
||||
}
|
||||
|
||||
override fun generateSerializableClassProperty(property: PropertyDescriptor) {
|
||||
codegen.generateMethod(property.getter!!) { _, _ ->
|
||||
stackSerialClassDesc(null)
|
||||
areturn(descType)
|
||||
}
|
||||
}
|
||||
|
||||
override fun generateTypeParamsSerializersGetter(function: FunctionDescriptor) = codegen.generateMethod(function) { _, _ ->
|
||||
genArrayOfTypeParametersSerializers()
|
||||
areturn(kSerializerArrayType)
|
||||
}
|
||||
|
||||
override fun generateChildSerializersGetter(function: FunctionDescriptor) {
|
||||
codegen.generateMethod(function) { _, expressionCodegen ->
|
||||
val size = serializableProperties.size
|
||||
iconst(size)
|
||||
newarray(kSerializerType)
|
||||
for (i in 0 until size) {
|
||||
dup() // array
|
||||
iconst(i) // index
|
||||
val prop = serializableProperties[i]
|
||||
assert(
|
||||
stackValueSerializerInstanceFromSerializerWithoutSti(
|
||||
expressionCodegen,
|
||||
codegen,
|
||||
prop,
|
||||
this@SerializerCodegenImpl
|
||||
)
|
||||
) { "Property ${prop.name} must have serializer" }
|
||||
astore(kSerializerType)
|
||||
}
|
||||
areturn(kSerializerArrayType)
|
||||
}
|
||||
}
|
||||
|
||||
override fun generateSave(
|
||||
function: FunctionDescriptor
|
||||
) {
|
||||
codegen.generateMethod(function) { signature, expressionCodegen ->
|
||||
// fun save(output: KOutput, obj : T)
|
||||
val outputVar = 1
|
||||
val objVar = 2
|
||||
val descVar = 3
|
||||
stackSerialClassDesc(descVar)
|
||||
val objType = signature.valueParameters[1].asmType
|
||||
// output = output.writeBegin(classDesc, new KSerializer[0])
|
||||
load(outputVar, encoderType)
|
||||
load(descVar, descType)
|
||||
invokeinterface(
|
||||
encoderType.internalName, CallingConventions.begin,
|
||||
"(" + descType.descriptor +
|
||||
")" + kOutputType.descriptor
|
||||
)
|
||||
store(outputVar, kOutputType)
|
||||
if (serializableDescriptor.isInternalSerializable) {
|
||||
val sig = StringBuilder("(${objType.descriptor}${kOutputType.descriptor}${descType.descriptor}")
|
||||
// call obj.write$Self(output, classDesc)
|
||||
load(objVar, objType)
|
||||
load(outputVar, kOutputType)
|
||||
load(descVar, descType)
|
||||
serializableDescriptor.declaredTypeParameters.forEachIndexed { i, _ ->
|
||||
load(0, kSerializerType)
|
||||
getfield(codegen.typeMapper.mapClass(codegen.descriptor).internalName, "$typeArgPrefix$i", kSerializerType.descriptor)
|
||||
sig.append(kSerializerType.descriptor)
|
||||
}
|
||||
sig.append(")V")
|
||||
invokestatic(
|
||||
objType.internalName, SerialEntityNames.WRITE_SELF_NAME.asString(),
|
||||
sig.toString(), false
|
||||
)
|
||||
} else {
|
||||
// loop for all properties
|
||||
val labeledProperties = serializableProperties.filter { !it.transient }
|
||||
for (index in labeledProperties.indices) {
|
||||
val property = labeledProperties[index]
|
||||
if (property.transient) continue
|
||||
// output.writeXxxElementValue(classDesc, index, value)
|
||||
load(outputVar, kOutputType)
|
||||
load(descVar, descType)
|
||||
iconst(index)
|
||||
genKOutputMethodCall(property, codegen, expressionCodegen, objType, objVar, generator = this@SerializerCodegenImpl)
|
||||
}
|
||||
}
|
||||
// output.writeEnd(classDesc)
|
||||
load(outputVar, kOutputType)
|
||||
load(descVar, descType)
|
||||
invokeinterface(
|
||||
kOutputType.internalName, CallingConventions.end,
|
||||
"(" + descType.descriptor + ")V"
|
||||
)
|
||||
// return
|
||||
areturn(Type.VOID_TYPE)
|
||||
}
|
||||
}
|
||||
|
||||
internal fun InstructionAdapter.genArrayOfTypeParametersSerializers() {
|
||||
val size = serializableDescriptor.declaredTypeParameters.size
|
||||
iconst(size)
|
||||
newarray(kSerializerType) // todo: use some predefined empty array, if size is 0
|
||||
for (i in 0 until size) {
|
||||
dup() // array
|
||||
iconst(i) // index
|
||||
load(0, kSerializerType) // this.serialTypeI
|
||||
getfield(codegen.typeMapper.mapClass(codegen.descriptor).internalName, "$typeArgPrefix$i", kSerializerType.descriptor)
|
||||
astore(kSerializerType)
|
||||
}
|
||||
}
|
||||
|
||||
override fun generateLoad(
|
||||
function: FunctionDescriptor
|
||||
) {
|
||||
codegen.generateMethod(function) { _, expressionCodegen ->
|
||||
// fun load(input: KInput): T
|
||||
val inputVar = 1
|
||||
val descVar = 2
|
||||
val indexVar = 3
|
||||
val bitMaskBase = 4
|
||||
val blocksCnt = serializableProperties.bitMaskSlotCount()
|
||||
val bitMaskOff = fun(it: Int): Int { return bitMaskBase + bitMaskSlotAt(it) }
|
||||
val propsStartVar = bitMaskBase + blocksCnt
|
||||
stackSerialClassDesc(descVar)
|
||||
// initialize bit mask
|
||||
for (i in 0 until blocksCnt) {
|
||||
//int bitMaskN = 0
|
||||
iconst(0)
|
||||
store(bitMaskBase + i * OPT_MASK_TYPE.size, OPT_MASK_TYPE)
|
||||
}
|
||||
// initialize all prop vars
|
||||
var propVar = propsStartVar
|
||||
for (property in serializableProperties) {
|
||||
val propertyType = codegen.typeMapper.mapType(property.type)
|
||||
stackValueDefault(propertyType)
|
||||
store(propVar, propertyType)
|
||||
propVar += propertyType.size
|
||||
}
|
||||
// input = input.readBegin(classDesc, new KSerializer[0])
|
||||
load(inputVar, decoderType)
|
||||
load(descVar, descType)
|
||||
invokeinterface(
|
||||
decoderType.internalName, CallingConventions.begin,
|
||||
"(" + descType.descriptor +
|
||||
")" + kInputType.descriptor
|
||||
)
|
||||
store(inputVar, kInputType)
|
||||
val readElementLabel = Label()
|
||||
val readEndLabel = Label()
|
||||
// if (decoder.decodeSequentially)
|
||||
load(inputVar, kInputType)
|
||||
invokeinterface(
|
||||
kInputType.internalName, CallingConventions.decodeSequentially,
|
||||
"()Z"
|
||||
)
|
||||
ifeq(readElementLabel)
|
||||
// decodeSequentially = true
|
||||
propVar = propsStartVar
|
||||
for ((index, property) in serializableProperties.withIndex()) {
|
||||
val propertyType = codegen.typeMapper.mapType(property.type)
|
||||
callReadProperty(expressionCodegen, property, propertyType, index, inputVar, descVar, propVar)
|
||||
propVar += propertyType.size
|
||||
}
|
||||
// set all bit masks to true
|
||||
for (maskVar in bitMaskBase until propsStartVar) {
|
||||
iconst(Int.MAX_VALUE)
|
||||
store(maskVar, OPT_MASK_TYPE)
|
||||
}
|
||||
// go to end
|
||||
goTo(readEndLabel)
|
||||
// branch with decodeSequentially = false
|
||||
// readElement: int index = input.readElement(classDesc)
|
||||
visitLabel(readElementLabel)
|
||||
load(inputVar, kInputType)
|
||||
load(descVar, descType)
|
||||
invokeinterface(
|
||||
kInputType.internalName, CallingConventions.decodeElementIndex,
|
||||
"(" + descType.descriptor + ")I"
|
||||
)
|
||||
store(indexVar, Type.INT_TYPE)
|
||||
// switch(index)
|
||||
val labeledProperties = serializableProperties.filter { !it.transient }
|
||||
val incorrectIndLabel = Label()
|
||||
val labels = arrayOfNulls<Label>(labeledProperties.size + 1)
|
||||
labels[0] = readEndLabel // READ_DONE
|
||||
for (i in labeledProperties.indices) {
|
||||
labels[i + 1] = Label()
|
||||
}
|
||||
load(indexVar, Type.INT_TYPE)
|
||||
tableswitch(-1, labeledProperties.size - 1, incorrectIndLabel, *labels)
|
||||
// loop for all properties
|
||||
propVar = propsStartVar
|
||||
var labelNum = 0
|
||||
for ((index, property) in serializableProperties.withIndex()) {
|
||||
val propertyType = codegen.typeMapper.mapType(property.type)
|
||||
if (!property.transient) {
|
||||
// labelI:
|
||||
visitLabel(labels[labelNum + 1])
|
||||
callReadProperty(expressionCodegen, property, propertyType, index, inputVar, descVar, propVar)
|
||||
|
||||
// mark read bit in mask
|
||||
// bitMask = bitMask | 1 << index
|
||||
val addr = bitMaskOff(index)
|
||||
load(addr, OPT_MASK_TYPE)
|
||||
iconst(1 shl (index % OPT_MASK_BITS))
|
||||
or(OPT_MASK_TYPE)
|
||||
store(addr, OPT_MASK_TYPE)
|
||||
goTo(readElementLabel)
|
||||
labelNum++
|
||||
}
|
||||
// next
|
||||
propVar += propertyType.size
|
||||
}
|
||||
val resultVar = propVar
|
||||
// readEnd: input.readEnd(classDesc)
|
||||
visitLabel(readEndLabel)
|
||||
load(inputVar, kInputType)
|
||||
load(descVar, descType)
|
||||
invokeinterface(
|
||||
kInputType.internalName, CallingConventions.end,
|
||||
"(" + descType.descriptor + ")V"
|
||||
)
|
||||
if (!serializableDescriptor.isInternalSerializable) {
|
||||
//validate all required (constructor) fields
|
||||
for ((i, property) in properties.serializableConstructorProperties.withIndex()) {
|
||||
if (property.optional || property.transient) {
|
||||
if (!property.isConstructorParameterWithDefault)
|
||||
throw CompilationException(
|
||||
"Property ${property.name} was declared as optional/transient but has no default value",
|
||||
null,
|
||||
null
|
||||
)
|
||||
} else {
|
||||
genValidateProperty(i, bitMaskOff(i))
|
||||
val nonThrowLabel = Label()
|
||||
ificmpne(nonThrowLabel)
|
||||
genMissingFieldExceptionThrow(property.name)
|
||||
visitLabel(nonThrowLabel)
|
||||
}
|
||||
}
|
||||
}
|
||||
// create object with constructor
|
||||
anew(serializableAsmType)
|
||||
dup()
|
||||
val constructorDesc = if (serializableDescriptor.isInternalSerializable)
|
||||
buildInternalConstructorDesc(propsStartVar, bitMaskBase, codegen, properties.serializableProperties)
|
||||
else buildExternalConstructorDesc(propsStartVar, bitMaskBase)
|
||||
invokespecial(serializableAsmType.internalName, "<init>", constructorDesc, false)
|
||||
if (!serializableDescriptor.isInternalSerializable && !properties.serializableStandaloneProperties.isEmpty()) {
|
||||
// result := ... <created object>
|
||||
store(resultVar, serializableAsmType)
|
||||
// set other properties
|
||||
propVar = propsStartVar +
|
||||
properties.serializableConstructorProperties.map { codegen.typeMapper.mapType(it.type).size }.sum()
|
||||
genSetSerializableStandaloneProperties(expressionCodegen, propVar, resultVar, bitMaskOff)
|
||||
// load result
|
||||
load(resultVar, serializableAsmType)
|
||||
// will return result
|
||||
}
|
||||
// return
|
||||
areturn(serializableAsmType)
|
||||
|
||||
// throwing an exception in default branch (if no index matched)
|
||||
visitLabel(incorrectIndLabel)
|
||||
anew(Type.getObjectType(serializationExceptionUnknownIndexName))
|
||||
dup()
|
||||
load(indexVar, Type.INT_TYPE)
|
||||
invokespecial(serializationExceptionUnknownIndexName, "<init>", "(I)V", false)
|
||||
checkcast(Type.getObjectType("java/lang/Throwable"))
|
||||
athrow()
|
||||
}
|
||||
}
|
||||
|
||||
private fun InstructionAdapter.callReadProperty(
|
||||
expressionCodegen: ExpressionCodegen,
|
||||
property: SerializableProperty,
|
||||
propertyType: Type,
|
||||
index: Int,
|
||||
inputVar: Int,
|
||||
descriptorVar: Int,
|
||||
propertyVar: Int
|
||||
) {
|
||||
// propX := input.readXxxValue(value)
|
||||
load(inputVar, kInputType)
|
||||
load(descriptorVar, descType)
|
||||
iconst(index)
|
||||
|
||||
val sti = getSerialTypeInfo(property, propertyType)
|
||||
val useSerializer = stackValueSerializerInstanceFromSerializer(expressionCodegen, codegen, sti, this@SerializerCodegenImpl)
|
||||
val unknownSer = (!useSerializer && sti.elementMethodPrefix.isEmpty())
|
||||
if (unknownSer) {
|
||||
aconst(codegen.typeMapper.mapType(property.type))
|
||||
AsmUtil.wrapJavaClassIntoKClass(this)
|
||||
}
|
||||
|
||||
fun produceCall(isUpdatable: Boolean) {
|
||||
invokeinterface(
|
||||
kInputType.internalName,
|
||||
(CallingConventions.decode) + sti.elementMethodPrefix + (if (useSerializer) "Serializable" else "") + CallingConventions.elementPostfix,
|
||||
"(" + descType.descriptor + "I" +
|
||||
(if (useSerializer) kSerialLoaderType.descriptor else "")
|
||||
+ (if (unknownSer) AsmTypes.K_CLASS_TYPE.descriptor else "")
|
||||
+ (if (isUpdatable) sti.type.descriptor else "")
|
||||
+ ")" + (sti.type.descriptor)
|
||||
)
|
||||
}
|
||||
|
||||
if (useSerializer) {
|
||||
// then it is not a primitive and can be updated via `oldValue` parameter in decodeSerializableElement
|
||||
load(propertyVar, propertyType)
|
||||
StackValue.coerce(propertyType, sti.type, this)
|
||||
}
|
||||
produceCall(useSerializer)
|
||||
|
||||
StackValue.coerce(sti.type, propertyType, this)
|
||||
store(propertyVar, propertyType)
|
||||
}
|
||||
|
||||
private fun InstructionAdapter.buildExternalConstructorDesc(propsStartVar: Int, bitMaskBase: Int): String {
|
||||
val constructorDesc = StringBuilder("(")
|
||||
var propVar = propsStartVar
|
||||
for (property in properties.serializableConstructorProperties) {
|
||||
val propertyType = codegen.typeMapper.mapType(property.type)
|
||||
constructorDesc.append(propertyType.descriptor)
|
||||
load(propVar, propertyType)
|
||||
propVar += propertyType.size
|
||||
}
|
||||
if (!properties.primaryConstructorWithDefaults) {
|
||||
constructorDesc.append(")V")
|
||||
} else {
|
||||
val cnt = properties.serializableConstructorProperties.size.coerceAtMost(32) //only 32 default values are supported
|
||||
val mask = if (cnt == 32) -1 else ((1 shl cnt) - 1)
|
||||
load(bitMaskBase, OPT_MASK_TYPE)
|
||||
iconst(mask)
|
||||
xor(Type.INT_TYPE)
|
||||
aconst(null)
|
||||
constructorDesc.append("ILkotlin/jvm/internal/DefaultConstructorMarker;)V")
|
||||
}
|
||||
return constructorDesc.toString()
|
||||
}
|
||||
|
||||
private fun InstructionAdapter.genSetSerializableStandaloneProperties(
|
||||
expressionCodegen: ExpressionCodegen, propVarStart: Int, resultVar: Int, bitMaskPos: (Int) -> Int
|
||||
) {
|
||||
var propVar = propVarStart
|
||||
val offset = properties.serializableConstructorProperties.size
|
||||
for ((index, property) in properties.serializableStandaloneProperties.withIndex()) {
|
||||
val i = index + offset
|
||||
//check if property has been seen and should be set
|
||||
val nextLabel = Label()
|
||||
// seen = bitMask & 1 << pos != 0
|
||||
genValidateProperty(i, bitMaskPos(i))
|
||||
if (property.optional) {
|
||||
// if (seen)
|
||||
// set
|
||||
ificmpeq(nextLabel)
|
||||
} else {
|
||||
// if (!seen)
|
||||
// throw
|
||||
// set
|
||||
ificmpne(nextLabel)
|
||||
genMissingFieldExceptionThrow(property.name)
|
||||
visitLabel(nextLabel)
|
||||
}
|
||||
|
||||
// generate setter call
|
||||
val propertyType = codegen.typeMapper.mapType(property.type)
|
||||
expressionCodegen.intermediateValueForProperty(
|
||||
property.descriptor, false, null,
|
||||
StackValue.local(resultVar, serializableAsmType)
|
||||
).store(StackValue.local(propVar, propertyType), this)
|
||||
propVar += propertyType.size
|
||||
if (property.optional)
|
||||
visitLabel(nextLabel)
|
||||
}
|
||||
}
|
||||
}
|
||||
+67
@@ -0,0 +1,67 @@
|
||||
package org.jetbrains.kotlinx.serialization.compiler.backend.jvm
|
||||
|
||||
import org.jetbrains.kotlin.codegen.ExpressionCodegen
|
||||
import org.jetbrains.kotlin.codegen.ImplementationBodyCodegen
|
||||
import org.jetbrains.kotlin.descriptors.ClassDescriptor
|
||||
import org.jetbrains.kotlin.descriptors.FunctionDescriptor
|
||||
import org.jetbrains.kotlinx.serialization.compiler.resolve.CallingConventions
|
||||
import org.jetbrains.kotlinx.serialization.compiler.resolve.enumEntries
|
||||
import org.jetbrains.kotlinx.serialization.compiler.resolve.serialNameValue
|
||||
import org.jetbrains.org.objectweb.asm.Type
|
||||
|
||||
class SerializerForEnumsCodegen(
|
||||
codegen: ImplementationBodyCodegen,
|
||||
serializableClass: ClassDescriptor
|
||||
) : SerializerCodegenImpl(codegen, serializableClass, null) {
|
||||
override fun generateSave(function: FunctionDescriptor) = codegen.generateMethod(function) { _, _ ->
|
||||
// fun save(output: KOutput, obj : T)
|
||||
val outputVar = 1
|
||||
val objVar = 2
|
||||
// output.encodeEnum(descriptor, ordinal)
|
||||
load(outputVar, encoderType)
|
||||
stackSerialClassDesc(null)
|
||||
load(objVar, serializableAsmType)
|
||||
invokevirtual(serializableAsmType.internalName, "ordinal", "()I", false)
|
||||
invokeinterface(encoderType.internalName, CallingConventions.encodeEnum, "(${descType.descriptor}I)V")
|
||||
// return
|
||||
areturn(Type.VOID_TYPE)
|
||||
}
|
||||
|
||||
override fun generateLoad(function: FunctionDescriptor) = codegen.generateMethod(function) { _, _ ->
|
||||
// fun load(input: KInput): T
|
||||
val inputVar = 1
|
||||
val serializableArrayType = Type.getType("[L${serializableAsmType.internalName};")
|
||||
// T.values()
|
||||
invokestatic(serializableAsmType.internalName, "values", "()${serializableArrayType.descriptor}", false)
|
||||
// input.decodeEnum(descriptor)
|
||||
load(inputVar, decoderType)
|
||||
stackSerialClassDesc(null)
|
||||
invokeinterface(decoderType.internalName, CallingConventions.decodeEnum, "(${descType.descriptor})I")
|
||||
// return
|
||||
aload(serializableAsmType)
|
||||
areturn(serializableAsmType)
|
||||
}
|
||||
|
||||
override fun ExpressionCodegen.instantiateNewDescriptor(isStatic: Boolean) = with(v) {
|
||||
anew(descriptorForEnumsType)
|
||||
dup()
|
||||
aconst(serialName)
|
||||
aconst(serializableDescriptor.enumEntries().size)
|
||||
invokespecial(descriptorForEnumsType.internalName, "<init>", "(Ljava/lang/String;I)V", false)
|
||||
checkcast(descImplType)
|
||||
}
|
||||
|
||||
override fun ExpressionCodegen.addElementsContentToDescriptor(descriptorVar: Int) = with(v) {
|
||||
val enumEntries = serializableDescriptor.enumEntries()
|
||||
for (entry in enumEntries) {
|
||||
load(descriptorVar, descImplType)
|
||||
// regular .serialName() produces fqName here, which is kinda inconvenient for enum entry
|
||||
val serialName = entry.annotations.serialNameValue ?: entry.name.toString()
|
||||
aconst(serialName)
|
||||
iconst(0)
|
||||
invokevirtual(descImplType.internalName, CallingConventions.addElement, "(Ljava/lang/String;Z)V", false)
|
||||
// pushing annotations
|
||||
addSyntheticAnnotationsToDescriptor(descriptorVar, entry, CallingConventions.addAnnotation)
|
||||
}
|
||||
}
|
||||
}
|
||||
+30
@@ -0,0 +1,30 @@
|
||||
/*
|
||||
* 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.jvm
|
||||
|
||||
// :kludge: for stripped-down version of ASM inside kotlin-compiler-embeddable.jar
|
||||
const val VOID = 0
|
||||
const val BOOLEAN = 1
|
||||
const val CHAR = 2
|
||||
const val BYTE = 3
|
||||
const val SHORT = 4
|
||||
const val INT = 5
|
||||
const val FLOAT = 6
|
||||
const val LONG = 7
|
||||
const val DOUBLE = 8
|
||||
const val ARRAY = 9
|
||||
const val OBJECT = 10
|
||||
+36
@@ -0,0 +1,36 @@
|
||||
/*
|
||||
* 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.codegen.ImplementationBodyCodegen
|
||||
import org.jetbrains.kotlin.codegen.extensions.ExpressionCodegenExtension
|
||||
import org.jetbrains.kotlinx.serialization.compiler.backend.jvm.SerialInfoCodegenImpl
|
||||
import org.jetbrains.kotlinx.serialization.compiler.backend.jvm.SerializableCodegenImpl
|
||||
import org.jetbrains.kotlinx.serialization.compiler.backend.jvm.SerializableCompanionCodegenImpl
|
||||
import org.jetbrains.kotlinx.serialization.compiler.backend.jvm.SerializerCodegenImpl
|
||||
|
||||
open class SerializationCodegenExtension @JvmOverloads constructor(val metadataPlugin: SerializationDescriptorSerializerPlugin? = null) : ExpressionCodegenExtension {
|
||||
override fun generateClassSyntheticParts(codegen: ImplementationBodyCodegen) {
|
||||
SerialInfoCodegenImpl.generateSerialInfoImplBody(codegen)
|
||||
SerializableCodegenImpl.generateSerializableExtensions(codegen)
|
||||
SerializerCodegenImpl.generateSerializerExtensions(codegen, metadataPlugin)
|
||||
SerializableCompanionCodegenImpl.generateSerializableExtensions(codegen)
|
||||
}
|
||||
|
||||
override val shouldGenerateClassSyntheticPartsInLightClassesMode: Boolean
|
||||
get() = false
|
||||
}
|
||||
+34
@@ -0,0 +1,34 @@
|
||||
/*
|
||||
* 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.SerializableCompanionJsTranslator
|
||||
import org.jetbrains.kotlinx.serialization.compiler.backend.js.SerializableJsTranslator
|
||||
import org.jetbrains.kotlinx.serialization.compiler.backend.js.SerializerJsTranslator
|
||||
|
||||
open class SerializationJsExtension @JvmOverloads constructor(val metadataPlugin: SerializationDescriptorSerializerPlugin? = null): JsSyntheticTranslateExtension {
|
||||
override fun generateClassSyntheticParts(declaration: KtPureClassOrObject, descriptor: ClassDescriptor, translator: DeclarationBodyVisitor, context: TranslationContext) {
|
||||
SerializerJsTranslator.translate(descriptor, translator, context, metadataPlugin)
|
||||
SerializableJsTranslator.translate(declaration, descriptor, context)
|
||||
SerializableCompanionJsTranslator.translate(descriptor, translator, context)
|
||||
}
|
||||
}
|
||||
+129
@@ -0,0 +1,129 @@
|
||||
/*
|
||||
* Copyright 2010-2021 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlinx.serialization.compiler.extensions
|
||||
|
||||
import org.jetbrains.kotlin.backend.common.ClassLoweringPass
|
||||
import org.jetbrains.kotlin.backend.common.CompilationException
|
||||
import org.jetbrains.kotlin.backend.common.extensions.IrGenerationExtension
|
||||
import org.jetbrains.kotlin.backend.common.extensions.IrPluginContext
|
||||
import org.jetbrains.kotlin.backend.common.runOnFilePostfix
|
||||
import org.jetbrains.kotlin.backend.jvm.ir.fileParent
|
||||
import org.jetbrains.kotlin.ir.IrElement
|
||||
import org.jetbrains.kotlin.ir.ObsoleteDescriptorBasedAPI
|
||||
import org.jetbrains.kotlin.ir.declarations.*
|
||||
import org.jetbrains.kotlin.ir.visitors.IrElementTransformerVoid
|
||||
import org.jetbrains.kotlin.ir.visitors.IrElementVisitorVoid
|
||||
import org.jetbrains.kotlin.ir.visitors.acceptChildrenVoid
|
||||
import org.jetbrains.kotlin.ir.visitors.acceptVoid
|
||||
import org.jetbrains.kotlin.name.CallableId
|
||||
import org.jetbrains.kotlin.platform.jvm.isJvm
|
||||
import org.jetbrains.kotlinx.serialization.compiler.backend.ir.*
|
||||
import org.jetbrains.kotlinx.serialization.compiler.resolve.KSerializerDescriptorResolver
|
||||
import org.jetbrains.kotlinx.serialization.compiler.resolve.SerialEntityNames
|
||||
import org.jetbrains.kotlinx.serialization.compiler.resolve.SerializationPackages
|
||||
import java.util.concurrent.ConcurrentHashMap
|
||||
|
||||
/**
|
||||
* Copy of [runOnFilePostfix], but this implementation first lowers declaration, then its children.
|
||||
*/
|
||||
fun ClassLoweringPass.runOnFileInOrder(irFile: IrFile) {
|
||||
irFile.acceptVoid(object : IrElementVisitorVoid {
|
||||
override fun visitElement(element: IrElement) {
|
||||
element.acceptChildrenVoid(this)
|
||||
}
|
||||
|
||||
override fun visitClass(declaration: IrClass) {
|
||||
lower(declaration)
|
||||
declaration.acceptChildrenVoid(this)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
class SerializationPluginContext(baseContext: IrPluginContext, val metadataPlugin: SerializationDescriptorSerializerPlugin?) :
|
||||
IrPluginContext by baseContext {
|
||||
lateinit var serialInfoImplJvmIrGenerator: SerialInfoImplJvmIrGenerator
|
||||
|
||||
internal val copiedStaticWriteSelf: MutableMap<IrSimpleFunction, IrSimpleFunction> = ConcurrentHashMap()
|
||||
|
||||
internal val enumSerializerFactoryFunc = baseContext.referenceFunctions(
|
||||
CallableId(
|
||||
SerializationPackages.internalPackageFqName,
|
||||
SerialEntityNames.ENUM_SERIALIZER_FACTORY_FUNC_NAME
|
||||
)
|
||||
).singleOrNull()
|
||||
|
||||
internal val markedEnumSerializerFactoryFunc = baseContext.referenceFunctions(
|
||||
CallableId(
|
||||
SerializationPackages.internalPackageFqName,
|
||||
SerialEntityNames.MARKED_ENUM_SERIALIZER_FACTORY_FUNC_NAME
|
||||
)
|
||||
).singleOrNull()
|
||||
|
||||
val runtimeHasEnumSerializerFactoryFunctions = enumSerializerFactoryFunc != null && markedEnumSerializerFactoryFunc != null
|
||||
}
|
||||
|
||||
private inline fun IrClass.runPluginSafe(block: () -> Unit) {
|
||||
try {
|
||||
block()
|
||||
} catch (e: Exception) {
|
||||
throw CompilationException(
|
||||
"kotlinx.serialization compiler plugin internal error: unable to transform declaration, see cause",
|
||||
this.fileParent,
|
||||
this,
|
||||
e
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private class SerializerClassLowering(
|
||||
baseContext: IrPluginContext,
|
||||
metadataPlugin: SerializationDescriptorSerializerPlugin?,
|
||||
moduleFragment: IrModuleFragment
|
||||
) : IrElementTransformerVoid(), ClassLoweringPass {
|
||||
val context: SerializationPluginContext = SerializationPluginContext(baseContext, metadataPlugin)
|
||||
private val serialInfoJvmGenerator =
|
||||
SerialInfoImplJvmIrGenerator(context, moduleFragment).also { context.serialInfoImplJvmIrGenerator = it }
|
||||
|
||||
override fun lower(irClass: IrClass) {
|
||||
irClass.runPluginSafe {
|
||||
SerializableIrGenerator.generate(irClass, context)
|
||||
SerializerIrGenerator.generate(irClass, context, context.metadataPlugin)
|
||||
SerializableCompanionIrGenerator.generate(irClass, context)
|
||||
|
||||
@OptIn(ObsoleteDescriptorBasedAPI::class)
|
||||
if (context.platform.isJvm() && KSerializerDescriptorResolver.isSerialInfoImpl(irClass.descriptor)) {
|
||||
serialInfoJvmGenerator.generate(irClass)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private class SerializerClassPreLowering(
|
||||
baseContext: IrPluginContext
|
||||
) : IrElementTransformerVoid(), ClassLoweringPass {
|
||||
val context: SerializationPluginContext = SerializationPluginContext(baseContext, null)
|
||||
|
||||
override fun lower(irClass: IrClass) {
|
||||
irClass.runPluginSafe {
|
||||
IrPreGenerator.generate(irClass, context)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
open class SerializationLoweringExtension @JvmOverloads constructor(
|
||||
val metadataPlugin: SerializationDescriptorSerializerPlugin? = null
|
||||
) : IrGenerationExtension {
|
||||
override fun generate(
|
||||
moduleFragment: IrModuleFragment,
|
||||
pluginContext: IrPluginContext
|
||||
) {
|
||||
val pass1 = SerializerClassPreLowering(pluginContext)
|
||||
val pass2 = SerializerClassLowering(pluginContext, metadataPlugin, moduleFragment)
|
||||
moduleFragment.files.forEach(pass1::runOnFileInOrder)
|
||||
moduleFragment.files.forEach(pass2::runOnFileInOrder)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user