[Serialization] Reorganize module structure

This commit is contained in:
Dmitriy Novozhilov
2022-08-22 11:49:45 +03:00
parent 0a8cefc8a5
commit cc00dcc038
150 changed files with 493 additions and 322 deletions
+31
View File
@@ -0,0 +1,31 @@
# Kotlin serialization IDEA plugin
Kotlin serialization plugin consists of three parts: a compiler plugin, an IntelliJ plugin and a runtime library.
This is the folder with common source for all plugins, IDEA plugin is built from here. Gradle and Maven plugins can be found in `libraries` folder.
## Building and usage
### Prerequisites:
Before all, follow the instructions from root README.md to download dependencies and build Kotlin compiler. (`./gradlew dist`)
**Plugin works only with IntelliJIDEA 2017.2 and higher.**
Make sure you have latest dev version of Kotlin plugin installed.
### With gradle:
Run `./gradlew :kotlinx-serialization-compiler-plugin:dist`.
In IDEA, open `Settings - Plugins - Install plugin from disk...` and choose `$kotlin_root/dist/artifacts/Serialization/lib/kotlinx-serialization-compiler-plugin.jar`
### From within IDE (for development):
Run `./gradlew runIde` You'll get a fresh copy of IDEA with Kotlin and Kotlin-serialization plugins built from sources.
## Building gradle plugin
Run `./gradlew :kotlinx-gradle-serialization-plugin:install`
## Building maven plugin
Make all prerequisites from libraries' README.md for Maven projects. Go to `$kotlin_root/libraries/tools/kotlin-maven-serialization`. Run `mvn install`
@@ -0,0 +1,64 @@
description = "Kotlin Serialization Compiler Plugin"
plugins {
kotlin("jvm")
id("jps-compatible")
}
dependencies {
embedded(project(":kotlinx-serialization-compiler-plugin.common"))
embedded(project(":kotlinx-serialization-compiler-plugin.k1"))
embedded(project(":kotlinx-serialization-compiler-plugin.k2"))
embedded(project(":kotlinx-serialization-compiler-plugin.backend"))
embedded(project(":kotlinx-serialization-compiler-plugin.cli"))
testApi(project(":compiler:backend"))
testApi(project(":compiler:cli"))
testApi(project(":kotlinx-serialization-compiler-plugin.cli"))
testApi(projectTests(":compiler:tests-common"))
testApi(projectTests(":compiler:test-infrastructure"))
testApi(projectTests(":compiler:test-infrastructure-utils"))
testApi(projectTests(":compiler:tests-compiler-utils"))
testApi(projectTests(":compiler:tests-common-new"))
testImplementation(projectTests(":generators:test-generator"))
testApi(commonDependency("junit:junit"))
testApiJUnit5(vintageEngine = true)
testImplementation(project(":kotlinx-serialization-compiler-plugin.common"))
testImplementation(project(":kotlinx-serialization-compiler-plugin.k1"))
testImplementation(project(":kotlinx-serialization-compiler-plugin.k2"))
testImplementation(project(":kotlinx-serialization-compiler-plugin.backend"))
testImplementation(project(":kotlinx-serialization-compiler-plugin.cli"))
testImplementation("org.jetbrains.kotlinx:kotlinx-serialization-core:1.4.0-RC")
testImplementation("org.jetbrains.kotlinx:kotlinx-serialization-json:1.4.0-RC")
testRuntimeOnly(intellijCore())
testRuntimeOnly(project(":kotlin-reflect"))
testRuntimeOnly(project(":core:descriptors.runtime"))
}
optInToExperimentalCompilerApi()
sourceSets {
"main" { none() }
"test" {
projectDefault()
generatedTestDir()
}
}
optInToExperimentalCompilerApi()
runtimeJar()
sourcesJar()
javadocJar()
testsJar()
projectTest(parallel = true, jUnitMode = JUnitMode.JUnit5) {
workingDir = rootDir
useJUnitPlatform()
}
val generateTests by generator("org.jetbrains.kotlinx.serialization.TestGeneratorKt")
@@ -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()
@@ -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) {
}
}
@@ -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)
}
}
@@ -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)
}
}
@@ -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
)
}
}
@@ -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())
}
@@ -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)
}
@@ -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
}
@@ -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()
}
}
}
@@ -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)
@@ -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)
}
@@ -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
}
}
@@ -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)
}
}
}
@@ -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"
)
}
}
}
}
@@ -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)
)
}
}
}
@@ -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)
)
}
@@ -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)
}
}
}
@@ -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.")
@@ -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
}
@@ -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()
}
}
}
@@ -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()
)
}
}
}
}
@@ -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)
}
}
}
@@ -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()
}
}
}
}
@@ -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
}
@@ -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()
}
}
}
@@ -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
}
}
@@ -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)
}
}
}
@@ -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)
}
}
}
@@ -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)
}
}
}
@@ -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
@@ -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
}
@@ -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)
}
}
@@ -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)
}
}
@@ -0,0 +1,31 @@
description = "Kotlin Serialization Compiler Plugin (CLI)"
plugins {
kotlin("jvm")
id("jps-compatible")
}
dependencies {
compileOnly(project(":compiler:util"))
compileOnly(project(":compiler:cli"))
compileOnly(project(":compiler:plugin-api"))
compileOnly(project(":compiler:fir:entrypoint"))
implementation(project(":kotlinx-serialization-compiler-plugin.common"))
implementation(project(":kotlinx-serialization-compiler-plugin.k1"))
implementation(project(":kotlinx-serialization-compiler-plugin.k2"))
implementation(project(":kotlinx-serialization-compiler-plugin.backend"))
compileOnly(intellijCore())
}
optInToExperimentalCompilerApi()
sourceSets {
"main" { projectDefault() }
"test" { none() }
}
runtimeJar()
sourcesJar()
javadocJar()
@@ -0,0 +1,17 @@
#
# 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.
#
org.jetbrains.kotlinx.serialization.compiler.extensions.SerializationComponentRegistrar
@@ -0,0 +1,68 @@
/*
* 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.extensions
import org.jetbrains.kotlin.backend.common.extensions.IrGenerationExtension
import org.jetbrains.kotlin.codegen.extensions.ExpressionCodegenExtension
import org.jetbrains.kotlin.compiler.plugin.CompilerPluginRegistrar
import org.jetbrains.kotlin.config.CompilerConfiguration
import org.jetbrains.kotlin.container.StorageComponentContainer
import org.jetbrains.kotlin.container.useInstance
import org.jetbrains.kotlin.descriptors.ModuleDescriptor
import org.jetbrains.kotlin.extensions.StorageComponentContainerContributor
import org.jetbrains.kotlin.js.translate.extensions.JsSyntheticTranslateExtension
import org.jetbrains.kotlin.library.metadata.KlibMetadataSerializerProtocol
import org.jetbrains.kotlin.metadata.jvm.deserialization.JvmProtoBufUtil
import org.jetbrains.kotlin.platform.TargetPlatform
import org.jetbrains.kotlin.resolve.extensions.SyntheticResolveExtension
import org.jetbrains.kotlin.serialization.DescriptorSerializerPlugin
import org.jetbrains.kotlin.serialization.js.JsSerializerProtocol
import org.jetbrains.kotlinx.serialization.compiler.diagnostic.SerializationPluginDeclarationChecker
class SerializationComponentRegistrar : CompilerPluginRegistrar() {
override fun ExtensionStorage.registerExtensions(configuration: CompilerConfiguration) {
Companion.registerExtensions(this)
}
override val supportsK2: Boolean
get() = false
companion object {
fun registerExtensions(extensionStorage: ExtensionStorage) = with(extensionStorage) {
// This method is never called in the IDE, therefore this extension is not available there.
// Since IDE does not perform any serialization of descriptors, metadata written to the 'serializationDescriptorSerializer'
// is never deleted, effectively causing memory leaks.
// So we create SerializationDescriptorSerializerPlugin only outside of IDE.
val serializationDescriptorSerializer = SerializationDescriptorSerializerPlugin()
DescriptorSerializerPlugin.registerExtension(serializationDescriptorSerializer)
registerProtoExtensions()
SyntheticResolveExtension.registerExtension(SerializationResolveExtension(serializationDescriptorSerializer))
ExpressionCodegenExtension.registerExtension(SerializationCodegenExtension(serializationDescriptorSerializer))
JsSyntheticTranslateExtension.registerExtension(SerializationJsExtension(serializationDescriptorSerializer))
IrGenerationExtension.registerExtension(SerializationLoweringExtension(serializationDescriptorSerializer))
StorageComponentContainerContributor.registerExtension(SerializationPluginComponentContainerContributor())
}
private fun registerProtoExtensions() {
SerializationPluginMetadataExtensions.registerAllExtensions(JvmProtoBufUtil.EXTENSION_REGISTRY)
SerializationPluginMetadataExtensions.registerAllExtensions(JsSerializerProtocol.extensionRegistry)
SerializationPluginMetadataExtensions.registerAllExtensions(KlibMetadataSerializerProtocol.extensionRegistry)
}
}
}
class SerializationPluginComponentContainerContributor : StorageComponentContainerContributor {
override fun registerModuleComponents(
container: StorageComponentContainer,
platform: TargetPlatform,
moduleDescriptor: ModuleDescriptor
) {
container.useInstance(SerializationPluginDeclarationChecker())
}
}
@@ -0,0 +1,21 @@
description = "Kotlin Serialization Compiler Plugin (Common)"
plugins {
kotlin("jvm")
id("jps-compatible")
}
dependencies {
compileOnly(project(":compiler:util"))
compileOnly(project(":core:compiler.common"))
compileOnly(intellijCore())
}
sourceSets {
"main" { projectDefault() }
"test" { none() }
}
runtimeJar()
sourcesJar()
javadocJar()
@@ -0,0 +1,14 @@
/*
* 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.fir
import org.jetbrains.kotlin.GeneratedDeclarationKey
object SerializationPluginKey : GeneratedDeclarationKey() {
override fun toString(): String {
return "KotlinxSerializationPlugin"
}
}
@@ -0,0 +1,155 @@
/*
* 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.resolve
import org.jetbrains.kotlin.name.CallableId
import org.jetbrains.kotlin.name.ClassId
import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.name.Name
object SerializationPackages {
val packageFqName = FqName("kotlinx.serialization")
val internalPackageFqName = FqName("kotlinx.serialization.internal")
val encodingPackageFqName = FqName("kotlinx.serialization.encoding")
val descriptorsPackageFqName = FqName("kotlinx.serialization.descriptors")
val builtinsPackageFqName = FqName("kotlinx.serialization.builtins")
val allPublicPackages = listOf(packageFqName, encodingPackageFqName, descriptorsPackageFqName, builtinsPackageFqName)
}
object SerializationAnnotations {
// When changing names for these annotations, please change
// org.jetbrains.kotlin.idea.caches.lightClasses.annotations.KOTLINX_SERIALIZABLE_FQ_NAME and
// org.jetbrains.kotlin.idea.caches.lightClasses.annotations.KOTLINX_SERIALIZER_FQ_NAME accordingly.
// Otherwise, there it might lead to exceptions from light classes when building them for serializer/serializable classes
val serializableAnnotationFqName = FqName("kotlinx.serialization.Serializable")
val serializerAnnotationFqName = FqName("kotlinx.serialization.Serializer")
val serialNameAnnotationFqName = FqName("kotlinx.serialization.SerialName")
val requiredAnnotationFqName = FqName("kotlinx.serialization.Required")
val serialTransientFqName = FqName("kotlinx.serialization.Transient")
// Also implicitly used in kotlin-native.compiler.backend.native/CodeGenerationInfo.kt
val serialInfoFqName = FqName("kotlinx.serialization.SerialInfo")
val inheritableSerialInfoFqName = FqName("kotlinx.serialization.InheritableSerialInfo")
val metaSerializableAnnotationFqName = FqName("kotlinx.serialization.MetaSerializable")
val encodeDefaultFqName = FqName("kotlinx.serialization.EncodeDefault")
val contextualFqName = FqName("kotlinx.serialization.ContextualSerialization") // this one is deprecated
val contextualOnFileFqName = FqName("kotlinx.serialization.UseContextualSerialization")
val contextualOnPropertyFqName = FqName("kotlinx.serialization.Contextual")
val polymorphicFqName = FqName("kotlinx.serialization.Polymorphic")
val additionalSerializersFqName = FqName("kotlinx.serialization.UseSerializers")
}
object SerialEntityNames {
const val KSERIALIZER_CLASS = "KSerializer"
const val SERIAL_DESC_FIELD = "descriptor"
const val SAVE = "serialize"
const val LOAD = "deserialize"
const val SERIALIZER_CLASS = "\$serializer"
const val CACHED_DESCRIPTOR_FIELD = "\$cachedDescriptor"
const val CACHED_SERIALIZER_PROPERTY = "\$cachedSerializer"
// classes
val KCLASS_NAME_FQ = FqName("kotlin.reflect.KClass")
val KSERIALIZER_NAME = Name.identifier(KSERIALIZER_CLASS)
val SERIAL_CTOR_MARKER_NAME = Name.identifier("SerializationConstructorMarker")
val KSERIALIZER_NAME_FQ = SerializationPackages.packageFqName.child(KSERIALIZER_NAME)
val SERIALIZER_CLASS_NAME = Name.identifier(SERIALIZER_CLASS)
val IMPL_NAME = Name.identifier("Impl")
val GENERATED_SERIALIZER_CLASS = Name.identifier("GeneratedSerializer")
val GENERATED_SERIALIZER_FQ = SerializationPackages.internalPackageFqName.child(GENERATED_SERIALIZER_CLASS)
const val ENCODER_CLASS = "Encoder"
const val STRUCTURE_ENCODER_CLASS = "CompositeEncoder"
const val DECODER_CLASS = "Decoder"
const val STRUCTURE_DECODER_CLASS = "CompositeDecoder"
const val ANNOTATION_MARKER_CLASS = "SerializableWith"
const val SERIAL_SAVER_CLASS = "SerializationStrategy"
const val SERIAL_LOADER_CLASS = "DeserializationStrategy"
const val SERIAL_DESCRIPTOR_CLASS = "SerialDescriptor"
const val SERIAL_DESCRIPTOR_CLASS_IMPL = "PluginGeneratedSerialDescriptor"
const val SERIAL_DESCRIPTOR_FOR_ENUM = "EnumDescriptor"
const val SERIAL_DESCRIPTOR_FOR_INLINE = "InlineClassDescriptor"
const val PLUGIN_EXCEPTIONS_FILE = "PluginExceptions"
const val ENUMS_FILE = "Enums"
//exceptions
const val SERIAL_EXC = "SerializationException"
const val MISSING_FIELD_EXC = "MissingFieldException"
const val UNKNOWN_FIELD_EXC = "UnknownFieldException"
// functions
val SERIAL_DESC_FIELD_NAME = Name.identifier(SERIAL_DESC_FIELD)
val SAVE_NAME = Name.identifier(SAVE)
val LOAD_NAME = Name.identifier(LOAD)
val CHILD_SERIALIZERS_GETTER = Name.identifier("childSerializers")
val TYPE_PARAMS_SERIALIZERS_GETTER = Name.identifier("typeParametersSerializers")
val WRITE_SELF_NAME = Name.identifier("write\$Self")
val SERIALIZER_PROVIDER_NAME = Name.identifier("serializer")
val SINGLE_MASK_FIELD_MISSING_FUNC_NAME = Name.identifier("throwMissingFieldException")
val ARRAY_MASK_FIELD_MISSING_FUNC_NAME = Name.identifier("throwArrayMissingFieldException")
val ENUM_SERIALIZER_FACTORY_FUNC_NAME = Name.identifier("createSimpleEnumSerializer")
val MARKED_ENUM_SERIALIZER_FACTORY_FUNC_NAME = Name.identifier("createMarkedEnumSerializer")
val SINGLE_MASK_FIELD_MISSING_FUNC_FQ = SerializationPackages.internalPackageFqName.child(SINGLE_MASK_FIELD_MISSING_FUNC_NAME)
val ARRAY_MASK_FIELD_MISSING_FUNC_FQ = SerializationPackages.internalPackageFqName.child(ARRAY_MASK_FIELD_MISSING_FUNC_NAME)
val CACHED_SERIALIZER_PROPERTY_NAME = Name.identifier(CACHED_SERIALIZER_PROPERTY)
val CACHED_DESCRIPTOR_FIELD_NAME = Name.identifier(CACHED_DESCRIPTOR_FIELD)
val ENUM_SERIALIZER_FACTORY_FUNC_FQ = SerializationPackages.internalPackageFqName.child(ENUM_SERIALIZER_FACTORY_FUNC_NAME)
val MARKED_ENUM_SERIALIZER_FACTORY_FUNC_FQ = SerializationPackages.internalPackageFqName.child(MARKED_ENUM_SERIALIZER_FACTORY_FUNC_NAME)
// parameters
val dummyParamName = Name.identifier("serializationConstructorMarker")
const val typeArgPrefix = "typeSerial"
val wrapIntoNullableExt = SerializationPackages.builtinsPackageFqName.child(Name.identifier("nullable"))
val wrapIntoNullableCallableId = CallableId(SerializationPackages.builtinsPackageFqName, Name.identifier("nullable"))
}
object SpecialBuiltins {
const val referenceArraySerializer = "ReferenceArraySerializer"
const val objectSerializer = "ObjectSerializer"
const val enumSerializer = "EnumSerializer"
const val polymorphicSerializer = "PolymorphicSerializer"
const val sealedSerializer = "SealedClassSerializer"
const val contextSerializer = "ContextualSerializer"
const val nullableSerializer = "NullableSerializer"
}
object CallingConventions {
const val begin = "beginStructure"
const val end = "endStructure"
const val decode = "decode"
const val update = "update"
const val encode = "encode"
const val encodeEnum = "encodeEnum"
const val decodeEnum = "decodeEnum"
const val encodeInline = "encodeInline"
const val decodeInline = "decodeInline"
const val decodeElementIndex = "decodeElementIndex"
const val decodeSequentially = "decodeSequentially"
const val elementPostfix = "Element"
const val shouldEncodeDefault = "shouldEncodeElementDefault"
const val addElement = "addElement"
const val addAnnotation = "pushAnnotation"
const val addClassAnnotation = "pushClassAnnotation"
}
object SerializationDependencies {
val LAZY_FQ = FqName("kotlin.Lazy")
val LAZY_FUNC_FQ = FqName("kotlin.lazy")
val LAZY_MODE_FQ = FqName("kotlin.LazyThreadSafetyMode")
val FUNCTION0_FQ = FqName("kotlin.Function0")
val LAZY_PUBLICATION_MODE_NAME = Name.identifier("PUBLICATION")
}
@@ -0,0 +1,28 @@
description = "Kotlin Serialization Compiler Plugin (K1)"
plugins {
kotlin("jvm")
id("jps-compatible")
}
dependencies {
compileOnly(project(":core:compiler.common.jvm"))
compileOnly(project(":compiler:frontend"))
compileOnly(project(":js:js.frontend"))
compileOnly(project(":compiler:cli-common"))
compileOnly(project(":compiler:ir.backend.common")) // needed for CompilationException
compileOnly(project(":core:deserialization.common.jvm")) // needed for CompilationException
implementation(project(":kotlinx-serialization-compiler-plugin.common"))
compileOnly(intellijCore())
}
sourceSets {
"main" { projectDefault() }
"test" { none() }
}
runtimeJar()
sourcesJar()
javadocJar()
@@ -0,0 +1,11 @@
package org.jetbrains.kotlinx.serialization.compiler.extensions;
import "core/metadata/src/metadata.proto";
import "core/metadata/src/ext_options.proto";
option java_outer_classname = "SerializationPluginMetadataExtensions";
option optimize_for = LITE_RUNTIME;
extend org.jetbrains.kotlin.metadata.Class {
repeated int32 properties_names_in_program_order = 18000;
}
@@ -0,0 +1,66 @@
/*
* 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.common
import org.jetbrains.kotlin.descriptors.ClassDescriptor
import org.jetbrains.kotlin.descriptors.DeclarationDescriptor
import org.jetbrains.kotlin.descriptors.FunctionDescriptor
import org.jetbrains.kotlin.js.translate.utils.AnnotationsUtils
import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.resolve.BindingContext
import org.jetbrains.kotlin.resolve.constants.KClassValue
import org.jetbrains.kotlin.resolve.descriptorUtil.firstArgument
import org.jetbrains.kotlin.resolve.descriptorUtil.module
import org.jetbrains.kotlin.resolve.scopes.getDescriptorsFiltered
import org.jetbrains.kotlin.types.KotlinType
import org.jetbrains.kotlin.types.typeUtil.supertypes
import org.jetbrains.kotlinx.serialization.compiler.resolve.SerializationAnnotations
import org.jetbrains.kotlinx.serialization.compiler.resolve.isKSerializer
import org.jetbrains.kotlinx.serialization.compiler.resolve.toClassDescriptor
abstract class AbstractSerialGenerator(val bindingContext: BindingContext?, val currentDeclaration: ClassDescriptor) {
private fun getKClassListFromFileAnnotation(annotationFqName: FqName, declarationInFile: DeclarationDescriptor): List<KotlinType> {
if (bindingContext == null) return emptyList()
val annotation = AnnotationsUtils
.getContainingFileAnnotations(bindingContext, declarationInFile)
.find { it.fqName == annotationFqName }
?: return emptyList()
@Suppress("UNCHECKED_CAST")
val typeList: List<KClassValue> = annotation.firstArgument()?.value as? List<KClassValue> ?: return emptyList()
return typeList.map { it.getArgumentType(declarationInFile.module) }
}
val contextualKClassListInCurrentFile: Set<KotlinType> by lazy {
getKClassListFromFileAnnotation(
SerializationAnnotations.contextualFqName,
currentDeclaration
).plus(
getKClassListFromFileAnnotation(
SerializationAnnotations.contextualOnFileFqName,
currentDeclaration
)
).toSet()
}
val additionalSerializersInScopeOfCurrentFile: Map<Pair<ClassDescriptor, Boolean>, ClassDescriptor> by lazy {
getKClassListFromFileAnnotation(SerializationAnnotations.additionalSerializersFqName, currentDeclaration)
.associateBy(
{
val kotlinType = it.supertypes().find(::isKSerializer)?.arguments?.firstOrNull()?.type
val descriptor = kotlinType.toClassDescriptor
?: throw AssertionError("Argument for ${SerializationAnnotations.additionalSerializersFqName} does not implement KSerializer or does not provide serializer for concrete type")
descriptor to kotlinType!!.isMarkedNullable
},
{ it.toClassDescriptor!! }
)
}
protected fun ClassDescriptor.getFuncDesc(funcName: String): Sequence<FunctionDescriptor> =
unsubstitutedMemberScope.getDescriptorsFiltered { it == Name.identifier(funcName) }.asSequence()
.filterIsInstance<FunctionDescriptor>()
}
@@ -0,0 +1,23 @@
/*
* 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.common
import org.jetbrains.kotlin.backend.common.CodegenUtil
import org.jetbrains.kotlin.descriptors.ClassDescriptor
import org.jetbrains.kotlin.descriptors.FunctionDescriptor
import org.jetbrains.kotlinx.serialization.compiler.resolve.*
object SerializationDescriptorUtils {
fun getSyntheticLoadMember(serializerDescriptor: ClassDescriptor): FunctionDescriptor? = CodegenUtil.getMemberToGenerate(
serializerDescriptor, SerialEntityNames.LOAD,
serializerDescriptor::checkLoadMethodResult, serializerDescriptor::checkLoadMethodParameters
)
fun getSyntheticSaveMember(serializerDescriptor: ClassDescriptor): FunctionDescriptor? = CodegenUtil.getMemberToGenerate(
serializerDescriptor, SerialEntityNames.SAVE,
serializerDescriptor::checkSaveMethodResult, serializerDescriptor::checkSaveMethodParameters
)
}
@@ -0,0 +1,267 @@
/*
* 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.common
import com.intellij.psi.PsiElement
import org.jetbrains.kotlin.backend.common.CompilationException
import org.jetbrains.kotlin.builtins.KotlinBuiltIns
import org.jetbrains.kotlin.descriptors.*
import org.jetbrains.kotlin.descriptors.annotations.Annotations
import org.jetbrains.kotlin.js.descriptorUtils.getJetTypeFqName
import org.jetbrains.kotlin.js.resolve.diagnostics.findPsi
import org.jetbrains.kotlin.name.ClassId
import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.psi.KtAnonymousInitializer
import org.jetbrains.kotlin.psi.KtParameter
import org.jetbrains.kotlin.psi.KtProperty
import org.jetbrains.kotlin.psi.KtPureClassOrObject
import org.jetbrains.kotlin.resolve.BindingContext
import org.jetbrains.kotlin.resolve.descriptorUtil.fqNameUnsafe
import org.jetbrains.kotlin.types.KotlinType
import org.jetbrains.kotlin.types.typeUtil.*
import org.jetbrains.kotlinx.serialization.compiler.backend.jvm.enumSerializerId
import org.jetbrains.kotlinx.serialization.compiler.backend.jvm.referenceArraySerializerId
import org.jetbrains.kotlinx.serialization.compiler.resolve.*
import org.jetbrains.kotlinx.serialization.compiler.resolve.SerializationPackages.internalPackageFqName
open class SerialTypeInfo(
val property: SerializableProperty,
val elementMethodPrefix: String,
val serializer: ClassDescriptor? = null
)
fun AbstractSerialGenerator.findAddOnSerializer(propertyType: KotlinType, module: ModuleDescriptor): ClassDescriptor? {
additionalSerializersInScopeOfCurrentFile[propertyType.toClassDescriptor to propertyType.isMarkedNullable]?.let { return it }
if (propertyType in contextualKClassListInCurrentFile)
return module.getClassFromSerializationPackage(SpecialBuiltins.contextSerializer)
if (propertyType.toClassDescriptor?.annotations?.hasAnnotation(SerializationAnnotations.polymorphicFqName) == true)
return module.getClassFromSerializationPackage(SpecialBuiltins.polymorphicSerializer)
if (propertyType.isMarkedNullable) return findAddOnSerializer(propertyType.makeNotNullable(), module)
return null
}
fun KotlinType.isGeneratedSerializableObject() =
toClassDescriptor?.run { kind == ClassKind.OBJECT && hasSerializableOrMetaAnnotationWithoutArgs } == true
@Suppress("FunctionName", "LocalVariableName")
fun AbstractSerialGenerator.getSerialTypeInfo(property: SerializableProperty): SerialTypeInfo {
fun SerializableInfo(serializer: ClassDescriptor?) =
SerialTypeInfo(property, if (property.type.isMarkedNullable) "Nullable" else "", serializer)
val T = property.type
property.serializableWith?.toClassDescriptor?.let { return SerializableInfo(it) }
findAddOnSerializer(T, property.module)?.let { return SerializableInfo(it) }
T.overridenSerializer?.toClassDescriptor?.let { return SerializableInfo(it) }
return when {
T.isTypeParameter() -> SerialTypeInfo(property, if (property.type.isMarkedNullable) "Nullable" else "", null)
T.isPrimitiveNumberType() or T.isBoolean() -> SerialTypeInfo(
property,
T.getJetTypeFqName(false).removePrefix("kotlin.") // i don't feel so good about it...
// alternative: KotlinBuiltIns.getPrimitiveType(T)!!.typeName.identifier
)
KotlinBuiltIns.isString(T) -> SerialTypeInfo(property, "String")
KotlinBuiltIns.isNonPrimitiveArray(T.toClassDescriptor!!) -> {
val serializer = property.serializableWith?.toClassDescriptor ?: property.module.findClassAcrossModuleDependencies(
referenceArraySerializerId
)
SerializableInfo(serializer)
}
else -> {
val serializer =
findTypeSerializerOrContext(property.module, property.type, property.descriptor.findPsi())
SerializableInfo(serializer)
}
}
}
fun AbstractSerialGenerator.allSealedSerializableSubclassesFor(
klass: ClassDescriptor,
module: ModuleDescriptor
): Pair<List<KotlinType>, List<ClassDescriptor>> {
assert(klass.modality == Modality.SEALED)
fun recursiveSealed(klass: ClassDescriptor): Collection<ClassDescriptor> {
return klass.sealedSubclasses.flatMap { if (it.modality == Modality.SEALED) recursiveSealed(it) else setOf(it) }
}
val serializableSubtypes = recursiveSealed(klass).map { it.toSimpleType() }
return serializableSubtypes.mapNotNull { subtype ->
findTypeSerializerOrContextUnchecked(module, subtype)?.let { Pair(subtype, it) }
}.unzip()
}
fun KotlinType.serialName(): String {
val serializableDescriptor = this.toClassDescriptor!!
return serializableDescriptor.serialName()
}
fun ClassDescriptor.serialName(): String {
return annotations.serialNameValue ?: fqNameUnsafe.asString()
}
val ClassDescriptor.isStaticSerializable: Boolean get() = this.declaredTypeParameters.isEmpty()
/**
* Returns class descriptor for ContextSerializer or PolymorphicSerializer
* if [annotations] contains @Contextual or @Polymorphic annotation
*/
fun analyzeSpecialSerializers(
moduleDescriptor: ModuleDescriptor,
annotations: Annotations
): ClassDescriptor? = when {
annotations.hasAnnotation(SerializationAnnotations.contextualFqName) || annotations.hasAnnotation(SerializationAnnotations.contextualOnPropertyFqName) ->
moduleDescriptor.getClassFromSerializationPackage(SpecialBuiltins.contextSerializer)
// can be annotation on type usage, e.g. List<@Polymorphic Any>
annotations.hasAnnotation(SerializationAnnotations.polymorphicFqName) ->
moduleDescriptor.getClassFromSerializationPackage(SpecialBuiltins.polymorphicSerializer)
else -> null
}
fun AbstractSerialGenerator.findTypeSerializerOrContextUnchecked(
module: ModuleDescriptor,
kType: KotlinType
): ClassDescriptor? {
val annotations = kType.annotations
if (kType.isTypeParameter()) return null
annotations.serializableWith(module)?.let { return it.toClassDescriptor }
additionalSerializersInScopeOfCurrentFile[kType.toClassDescriptor to kType.isMarkedNullable]?.let { return it }
if (kType.isMarkedNullable) return findTypeSerializerOrContextUnchecked(module, kType.makeNotNullable())
if (kType in contextualKClassListInCurrentFile) return module.getClassFromSerializationPackage(SpecialBuiltins.contextSerializer)
return analyzeSpecialSerializers(module, annotations) ?: findTypeSerializer(module, kType)
}
fun AbstractSerialGenerator.findTypeSerializerOrContext(
module: ModuleDescriptor,
kType: KotlinType,
sourceElement: PsiElement? = null
): ClassDescriptor? {
if (kType.isTypeParameter()) return null
return findTypeSerializerOrContextUnchecked(module, kType) ?: throw CompilationException(
"Serializer for element of type $kType has not been found.\n" +
"To use context serializer as fallback, explicitly annotate element with @Contextual",
null,
sourceElement
)
}
fun findTypeSerializer(module: ModuleDescriptor, kType: KotlinType): ClassDescriptor? {
val userOverride = kType.overridenSerializer
if (userOverride != null) return userOverride.toClassDescriptor
if (kType.isTypeParameter()) return null
if (KotlinBuiltIns.isArray(kType)) return module.getClassFromInternalSerializationPackage(SpecialBuiltins.referenceArraySerializer)
if (kType.isGeneratedSerializableObject()) return module.getClassFromInternalSerializationPackage(SpecialBuiltins.objectSerializer)
val stdSer = findStandardKotlinTypeSerializer(module, kType) // see if there is a standard serializer
?: findEnumTypeSerializer(module, kType)
if (stdSer != null) return stdSer
if (kType.isInterface() && kType.toClassDescriptor?.isSealedSerializableInterface == false) return module.getClassFromSerializationPackage(
SpecialBuiltins.polymorphicSerializer
)
return kType.toClassDescriptor?.classSerializer // check for serializer defined on the type
}
fun findStandardKotlinTypeSerializer(module: ModuleDescriptor, kType: KotlinType): ClassDescriptor? {
val typeName = kType.getJetTypeFqName(false)
val name = when (typeName) {
"Z" -> if (kType.isBoolean()) "BooleanSerializer" else null
"B" -> if (kType.isByte()) "ByteSerializer" else null
"S" -> if (kType.isShort()) "ShortSerializer" else null
"I" -> if (kType.isInt()) "IntSerializer" else null
"J" -> if (kType.isLong()) "LongSerializer" else null
"F" -> if (kType.isFloat()) "FloatSerializer" else null
"D" -> if (kType.isDouble()) "DoubleSerializer" else null
"C" -> if (kType.isChar()) "CharSerializer" else null
else -> findStandardKotlinTypeSerializer(typeName)
} ?: return null
val identifier = Name.identifier(name)
return module.findClassAcrossModuleDependencies(ClassId(internalPackageFqName, identifier))
?: module.findClassAcrossModuleDependencies(ClassId(SerializationPackages.packageFqName, identifier))
}
fun findStandardKotlinTypeSerializer(typeName: String): String? {
return when (typeName) {
"kotlin.Unit" -> "UnitSerializer"
"kotlin.Nothing" -> "NothingSerializer"
"kotlin.Boolean" -> "BooleanSerializer"
"kotlin.Byte" -> "ByteSerializer"
"kotlin.Short" -> "ShortSerializer"
"kotlin.Int" -> "IntSerializer"
"kotlin.Long" -> "LongSerializer"
"kotlin.Float" -> "FloatSerializer"
"kotlin.Double" -> "DoubleSerializer"
"kotlin.Char" -> "CharSerializer"
"kotlin.UInt" -> "UIntSerializer"
"kotlin.ULong" -> "ULongSerializer"
"kotlin.UByte" -> "UByteSerializer"
"kotlin.UShort" -> "UShortSerializer"
"kotlin.String" -> "StringSerializer"
"kotlin.Pair" -> "PairSerializer"
"kotlin.Triple" -> "TripleSerializer"
"kotlin.collections.Collection", "kotlin.collections.List",
"kotlin.collections.ArrayList", "kotlin.collections.MutableList" -> "ArrayListSerializer"
"kotlin.collections.Set", "kotlin.collections.LinkedHashSet", "kotlin.collections.MutableSet" -> "LinkedHashSetSerializer"
"kotlin.collections.HashSet" -> "HashSetSerializer"
"kotlin.collections.Map", "kotlin.collections.LinkedHashMap", "kotlin.collections.MutableMap" -> "LinkedHashMapSerializer"
"kotlin.collections.HashMap" -> "HashMapSerializer"
"kotlin.collections.Map.Entry" -> "MapEntrySerializer"
"kotlin.ByteArray" -> "ByteArraySerializer"
"kotlin.ShortArray" -> "ShortArraySerializer"
"kotlin.IntArray" -> "IntArraySerializer"
"kotlin.LongArray" -> "LongArraySerializer"
"kotlin.UByteArray" -> "UByteArraySerializer"
"kotlin.UShortArray" -> "UShortArraySerializer"
"kotlin.UIntArray" -> "UIntArraySerializer"
"kotlin.ULongArray" -> "ULongArraySerializer"
"kotlin.CharArray" -> "CharArraySerializer"
"kotlin.FloatArray" -> "FloatArraySerializer"
"kotlin.DoubleArray" -> "DoubleArraySerializer"
"kotlin.BooleanArray" -> "BooleanArraySerializer"
"kotlin.time.Duration" -> "DurationSerializer"
"java.lang.Boolean" -> "BooleanSerializer"
"java.lang.Byte" -> "ByteSerializer"
"java.lang.Short" -> "ShortSerializer"
"java.lang.Integer" -> "IntSerializer"
"java.lang.Long" -> "LongSerializer"
"java.lang.Float" -> "FloatSerializer"
"java.lang.Double" -> "DoubleSerializer"
"java.lang.Character" -> "CharSerializer"
"java.lang.String" -> "StringSerializer"
"java.util.Collection", "java.util.List", "java.util.ArrayList" -> "ArrayListSerializer"
"java.util.Set", "java.util.LinkedHashSet" -> "LinkedHashSetSerializer"
"java.util.HashSet" -> "HashSetSerializer"
"java.util.Map", "java.util.LinkedHashMap" -> "LinkedHashMapSerializer"
"java.util.HashMap" -> "HashMapSerializer"
"java.util.Map.Entry" -> "MapEntrySerializer"
else -> return null
}
}
fun findEnumTypeSerializer(module: ModuleDescriptor, kType: KotlinType): ClassDescriptor? {
val classDescriptor = kType.toClassDescriptor ?: return null
return if (classDescriptor.kind == ClassKind.ENUM_CLASS && !classDescriptor.isEnumWithLegacyGeneratedSerializer())
module.findClassAcrossModuleDependencies(enumSerializerId)
else null
}
fun KtPureClassOrObject.bodyPropertiesDescriptorsMap(
bindingContext: BindingContext,
filterUninitialized: Boolean = true
): Map<PropertyDescriptor, KtProperty> = declarations
.asSequence()
.filterIsInstance<KtProperty>()
// can filter here because it's impossible to create body property w/ backing field w/o explicit delegating or initializing
.filter { if (filterUninitialized) it.delegateExpressionOrInitializer != null else true }
.associateBy { (bindingContext[BindingContext.DECLARATION_TO_DESCRIPTOR, it] as? PropertyDescriptor)!! }
fun KtPureClassOrObject.primaryConstructorPropertiesDescriptorsMap(bindingContext: BindingContext): Map<PropertyDescriptor, KtParameter> =
primaryConstructorParameters
.asSequence()
.filter { it.hasValOrVar() }
.associateBy { bindingContext[BindingContext.PRIMARY_CONSTRUCTOR_PARAMETER, it]!! }
fun KtPureClassOrObject.anonymousInitializers() = declarations
.asSequence()
.filterIsInstance<KtAnonymousInitializer>()
.mapNotNull { it.body }
.toList()
@@ -0,0 +1,18 @@
/*
* 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.jvm
import org.jetbrains.kotlin.name.ClassId
import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlinx.serialization.compiler.resolve.SerializationPackages
import org.jetbrains.kotlinx.serialization.compiler.resolve.SpecialBuiltins
val enumSerializerId = ClassId(SerializationPackages.internalPackageFqName, Name.identifier(SpecialBuiltins.enumSerializer))
val polymorphicSerializerId = ClassId(SerializationPackages.packageFqName, Name.identifier(SpecialBuiltins.polymorphicSerializer))
val referenceArraySerializerId = ClassId(SerializationPackages.internalPackageFqName, Name.identifier(SpecialBuiltins.referenceArraySerializer))
val objectSerializerId = ClassId(SerializationPackages.internalPackageFqName, Name.identifier(SpecialBuiltins.objectSerializer))
val sealedSerializerId = ClassId(SerializationPackages.packageFqName, Name.identifier(SpecialBuiltins.sealedSerializer))
val contextSerializerId = ClassId(SerializationPackages.packageFqName, Name.identifier(SpecialBuiltins.contextSerializer))
@@ -0,0 +1,56 @@
/*
* 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.diagnostic;
import com.intellij.psi.PsiElement;
import org.jetbrains.kotlin.diagnostics.*;
import org.jetbrains.kotlin.psi.KtAnnotationEntry;
import org.jetbrains.kotlin.types.KotlinType;
import static org.jetbrains.kotlin.diagnostics.Severity.ERROR;
import static org.jetbrains.kotlin.diagnostics.Severity.WARNING;
public interface SerializationErrors {
DiagnosticFactory2<PsiElement, String, String> INLINE_CLASSES_NOT_SUPPORTED = DiagnosticFactory2.create(ERROR);
DiagnosticFactory0<PsiElement> PLUGIN_IS_NOT_ENABLED = DiagnosticFactory0.create(WARNING);
DiagnosticFactory0<PsiElement> ANONYMOUS_OBJECTS_NOT_SUPPORTED = DiagnosticFactory0.create(ERROR);
DiagnosticFactory0<PsiElement> INNER_CLASSES_NOT_SUPPORTED = DiagnosticFactory0.create(ERROR);
DiagnosticFactory0<PsiElement> EXPLICIT_SERIALIZABLE_IS_REQUIRED = DiagnosticFactory0.create(WARNING);
DiagnosticFactory0<KtAnnotationEntry> SERIALIZABLE_ANNOTATION_IGNORED = DiagnosticFactory0.create(ERROR);
DiagnosticFactory0<KtAnnotationEntry> NON_SERIALIZABLE_PARENT_MUST_HAVE_NOARG_CTOR = DiagnosticFactory0.create(ERROR);
DiagnosticFactory0<KtAnnotationEntry> PRIMARY_CONSTRUCTOR_PARAMETER_IS_NOT_A_PROPERTY = DiagnosticFactory0.create(ERROR);
DiagnosticFactory1<KtAnnotationEntry, String> DUPLICATE_SERIAL_NAME = DiagnosticFactory1.create(ERROR);
DiagnosticFactory3<PsiElement, KotlinType, String, String> DUPLICATE_SERIAL_NAME_ENUM = DiagnosticFactory3.create(ERROR);
DiagnosticFactory1<PsiElement, KotlinType> SERIALIZER_NOT_FOUND = DiagnosticFactory1.create(ERROR);
DiagnosticFactory2<PsiElement, KotlinType, KotlinType> SERIALIZER_NULLABILITY_INCOMPATIBLE = DiagnosticFactory2.create(ERROR);
DiagnosticFactory3<PsiElement, KotlinType, KotlinType, KotlinType> SERIALIZER_TYPE_INCOMPATIBLE = DiagnosticFactory3.create(WARNING);
DiagnosticFactory1<PsiElement, KotlinType> LOCAL_SERIALIZER_USAGE = DiagnosticFactory1.create(ERROR);
DiagnosticFactory0<PsiElement> TRANSIENT_MISSING_INITIALIZER = DiagnosticFactory0.create(ERROR);
DiagnosticFactory0<PsiElement> TRANSIENT_IS_REDUNDANT = DiagnosticFactory0.create(WARNING);
DiagnosticFactory0<PsiElement> JSON_FORMAT_REDUNDANT_DEFAULT = DiagnosticFactory0.create(WARNING);
DiagnosticFactory0<PsiElement> JSON_FORMAT_REDUNDANT = DiagnosticFactory0.create(WARNING);
DiagnosticFactory0<PsiElement> INCORRECT_TRANSIENT = DiagnosticFactory0.create(WARNING);
DiagnosticFactory3<KtAnnotationEntry, String, String, String> REQUIRED_KOTLIN_TOO_HIGH = DiagnosticFactory3.create(ERROR);
DiagnosticFactory3<KtAnnotationEntry, String, String, String> PROVIDED_RUNTIME_TOO_LOW = DiagnosticFactory3.create(ERROR);
DiagnosticFactory2<PsiElement, KotlinType, KotlinType> INCONSISTENT_INHERITABLE_SERIALINFO = DiagnosticFactory2.create(ERROR);
DiagnosticFactory2<PsiElement, KotlinType, KotlinType> EXTERNAL_CLASS_NOT_SERIALIZABLE = DiagnosticFactory2.create(ERROR);
DiagnosticFactory2<PsiElement, KotlinType, KotlinType> EXTERNAL_CLASS_IN_ANOTHER_MODULE = DiagnosticFactory2.create(ERROR);
@SuppressWarnings("UnusedDeclaration")
Object _initializer = new Object() {
{
Errors.Initializer
.initializeFactoryNamesAndDefaultErrorMessages(SerializationErrors.class, SerializationPluginErrorsRendering.INSTANCE);
}
};
}
@@ -0,0 +1,480 @@
/*
* 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.diagnostic
import com.intellij.psi.PsiElement
import org.jetbrains.kotlin.builtins.KotlinBuiltIns
import org.jetbrains.kotlin.config.KotlinCompilerVersion
import org.jetbrains.kotlin.descriptors.*
import org.jetbrains.kotlin.descriptors.annotations.Annotated
import org.jetbrains.kotlin.descriptors.annotations.AnnotationDescriptor
import org.jetbrains.kotlin.descriptors.annotations.Annotations
import org.jetbrains.kotlin.diagnostics.DiagnosticFactory0
import org.jetbrains.kotlin.js.resolve.diagnostics.findPsi
import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.name.JvmNames.TRANSIENT_ANNOTATION_FQ_NAME
import org.jetbrains.kotlin.psi.*
import org.jetbrains.kotlin.resolve.*
import org.jetbrains.kotlin.resolve.checkers.DeclarationChecker
import org.jetbrains.kotlin.resolve.checkers.DeclarationCheckerContext
import org.jetbrains.kotlin.resolve.descriptorUtil.*
import org.jetbrains.kotlin.resolve.lazy.descriptors.LazyAnnotationDescriptor
import org.jetbrains.kotlin.resolve.source.getPsi
import org.jetbrains.kotlin.types.KotlinType
import org.jetbrains.kotlin.types.typeUtil.supertypes
import org.jetbrains.kotlin.util.slicedMap.Slices
import org.jetbrains.kotlin.util.slicedMap.WritableSlice
import org.jetbrains.kotlinx.serialization.compiler.backend.common.*
import org.jetbrains.kotlinx.serialization.compiler.backend.common.bodyPropertiesDescriptorsMap
import org.jetbrains.kotlinx.serialization.compiler.backend.common.primaryConstructorPropertiesDescriptorsMap
import org.jetbrains.kotlinx.serialization.compiler.resolve.*
val SERIALIZABLE_PROPERTIES: WritableSlice<ClassDescriptor, SerializableProperties> = Slices.createSimpleSlice()
open class SerializationPluginDeclarationChecker : DeclarationChecker {
private var useLegacyEnumSerializerCached: Boolean? = null
final override fun check(declaration: KtDeclaration, descriptor: DeclarationDescriptor, context: DeclarationCheckerContext) {
if (descriptor !is ClassDescriptor) return
checkEnum(descriptor, declaration, context.trace)
checkExternalSerializer(descriptor, declaration, context.trace)
if (!canBeSerializedInternally(descriptor, declaration, context.trace)) return
if (declaration !is KtPureClassOrObject) return
if (!isIde) {
// In IDE, BindingTrace is recreated each time code is modified, effectively resulting in JAR manifest read every time user types
// something, which may be very slow. So we perform this check only during CLI/Gradle compilation.
VersionReader.getVersionsForCurrentModuleFromTrace(descriptor.module, context.trace)?.let {
checkMinKotlin(it, descriptor, context.trace)
checkMinRuntime(it, descriptor, context.trace)
}
}
val props = buildSerializableProperties(descriptor, context.trace) ?: return
checkCorrectTransientAnnotationIsUsed(descriptor, props.serializableProperties, context.trace)
checkTransients(declaration, context.trace)
analyzePropertiesSerializers(context.trace, descriptor, props.serializableProperties)
checkInheritedAnnotations(descriptor, declaration, context.trace)
}
private fun checkExternalSerializer(classDescriptor: ClassDescriptor, declaration: KtDeclaration, trace: BindingTrace) {
val serializableKType = classDescriptor.serializerForClass ?: return
val serializableDescriptor = serializableKType.toClassDescriptor ?: return
val props = SerializableProperties(serializableDescriptor, trace.bindingContext)
if (!props.isExternallySerializable) {
val entry = classDescriptor.findAnnotationDeclaration(SerializationAnnotations.serializerAnnotationFqName)
val inSameModule =
trace.bindingContext[BindingContext.FQNAME_TO_CLASS_DESCRIPTOR, serializableDescriptor.fqNameUnsafe] != null
val diagnostic = if (inSameModule) SerializationErrors.EXTERNAL_CLASS_NOT_SERIALIZABLE else SerializationErrors.EXTERNAL_CLASS_IN_ANOTHER_MODULE
trace.report(diagnostic.on(entry ?: declaration, classDescriptor.defaultType, serializableKType))
}
}
private fun checkInheritedAnnotations(descriptor: ClassDescriptor, declaration: KtDeclaration, trace: BindingTrace) {
val annotationsFilter: (Annotations) -> List<Pair<FqName, AnnotationDescriptor>> = { an ->
an.map { it.annotationClass!!.fqNameSafe to it }
.filter { it.second.annotationClass?.isInheritableSerialInfoAnnotation == true }
}
val annotationByFq: MutableMap<FqName, AnnotationDescriptor> = mutableMapOf()
val reported: MutableSet<FqName> = mutableSetOf()
// my annotations
annotationByFq.putAll(annotationsFilter(descriptor.annotations))
// inherited
for (clazz in descriptor.getAllSuperClassifiers()) {
val annotations = annotationsFilter(clazz.annotations)
annotations.forEach { (fqname, call) ->
if (fqname in annotationByFq) {
val existing = annotationByFq.getValue(fqname)
if (existing.allValueArguments != call.allValueArguments) {
if (reported.add(fqname)) {
val entry = (existing as? LazyAnnotationDescriptor)?.annotationEntry ?: declaration
trace.report(
SerializationErrors.INCONSISTENT_INHERITABLE_SERIALINFO.on(
entry,
existing.type,
clazz.defaultType
)
)
}
}
}
}
}
}
private fun checkMinRuntime(versions: VersionReader.RuntimeVersions, descriptor: ClassDescriptor, trace: BindingTrace) {
// if RuntimeVersions are present, but implementation version is not,
// it means that we are reading from jar which does not have this manifest parameter - a pre-1.0 serialization runtime.
// For non-JAR distributions (klib, js) this method is not invoked, since getVersionsForCurrentModule
// unable to read from them
if (!versions.implementationVersionMatchSupported()) {
descriptor.onSerializableOrMetaAnnotation {
trace.report(
SerializationErrors.PROVIDED_RUNTIME_TOO_LOW.on(
it,
versions.implementationVersion?.toString() ?: "too low",
KotlinCompilerVersion.getVersion() ?: "unknown",
VersionReader.MINIMAL_SUPPORTED_VERSION.toString(),
)
)
}
}
}
private fun checkMinKotlin(versions: VersionReader.RuntimeVersions, descriptor: ClassDescriptor, trace: BindingTrace) {
if (versions.currentCompilerMatchRequired()) return
descriptor.onSerializableOrMetaAnnotation {
trace.report(
SerializationErrors.REQUIRED_KOTLIN_TOO_HIGH.on(
it,
KotlinCompilerVersion.getVersion() ?: "too low",
versions.implementationVersion?.toString() ?: "unknown",
versions.requireKotlinVersion?.toString() ?: "N/A",
)
)
}
}
protected open val isIde: Boolean get() = false
private fun checkCorrectTransientAnnotationIsUsed(
descriptor: ClassDescriptor,
properties: List<SerializableProperty>,
trace: BindingTrace
) {
if (descriptor.getSuperInterfaces().any { it.fqNameSafe.asString() == "java.io.Serializable" }) return // do not check
for (prop in properties) {
if (prop.transient) continue // correct annotation is used
val incorrectTransient = prop.descriptor.backingField?.annotations?.findAnnotation(TRANSIENT_ANNOTATION_FQ_NAME)
if (incorrectTransient != null) {
val elementToReport = incorrectTransient.source.getPsi() ?: prop.descriptor.findPsi() ?: continue
trace.report(SerializationErrors.INCORRECT_TRANSIENT.on(elementToReport))
}
}
}
private fun ClassDescriptor.useLegacyGeneratedEnumSerializer(): Boolean {
return useLegacyEnumSerializerCached ?: useGeneratedEnumSerializer.also { useLegacyEnumSerializerCached = it }
}
private fun canBeSerializedInternally(descriptor: ClassDescriptor, declaration: KtDeclaration, trace: BindingTrace): Boolean {
// if enum has meta or SerialInfo annotation on a class or entries and used plugin-generated serializer
if (descriptor.useLegacyGeneratedEnumSerializer() && descriptor.isSerializableEnumWithMissingSerializer()) {
val declarationToReport = declaration.modifierList ?: declaration
trace.report(SerializationErrors.EXPLICIT_SERIALIZABLE_IS_REQUIRED.on(declarationToReport))
return false
}
if (!descriptor.hasSerializableOrMetaAnnotation) return false
if (!serializationPluginEnabledOn(descriptor)) {
trace.reportOnSerializableOrMetaAnnotation(descriptor, SerializationErrors.PLUGIN_IS_NOT_ENABLED)
return false
}
if (descriptor.isAnonymousObjectOrContained) {
trace.reportOnSerializableOrMetaAnnotation(descriptor, SerializationErrors.ANONYMOUS_OBJECTS_NOT_SUPPORTED)
return false
}
if (descriptor.isInner) {
trace.reportOnSerializableOrMetaAnnotation(descriptor, SerializationErrors.INNER_CLASSES_NOT_SUPPORTED)
return false
}
if (descriptor.isInlineClass() && !canSupportInlineClasses(descriptor.module, trace)) {
descriptor.onSerializableOrMetaAnnotation {
trace.report(
SerializationErrors.INLINE_CLASSES_NOT_SUPPORTED.on(
it,
VersionReader.minVersionForInlineClasses.toString(),
VersionReader.getVersionsForCurrentModuleFromTrace(descriptor.module, trace)?.implementationVersion.toString()
)
)
}
return false
}
if (!descriptor.hasSerializableOrMetaAnnotationWithoutArgs) {
// defined custom serializer
checkClassWithCustomSerializer(descriptor, declaration, trace)
return false
}
if (descriptor.serializableAnnotationIsUseless) {
trace.reportOnSerializableOrMetaAnnotation(descriptor, SerializationErrors.SERIALIZABLE_ANNOTATION_IGNORED)
return false
}
// check that we can instantiate supertype
if (descriptor.kind != ClassKind.ENUM_CLASS) { // enums are inherited from java.lang.Enum and can't be inherited from other classes
val superClass = descriptor.getSuperClassOrAny()
if (!superClass.isInternalSerializable && superClass.constructors.singleOrNull { it.valueParameters.size == 0 } == null) {
trace.reportOnSerializableOrMetaAnnotation(descriptor, SerializationErrors.NON_SERIALIZABLE_PARENT_MUST_HAVE_NOARG_CTOR)
return false
}
}
return true
}
private fun checkClassWithCustomSerializer(descriptor: ClassDescriptor, declaration: KtDeclaration, trace: BindingTrace) {
val annotationPsi = descriptor.findSerializableOrMetaAnnotationDeclaration()
checkCustomSerializerMatch(descriptor.module, descriptor.defaultType, descriptor, annotationPsi, trace, declaration)
checkCustomSerializerIsNotLocal(descriptor.module, descriptor, trace, declaration)
}
private val ClassDescriptor.isAnonymousObjectOrContained: Boolean
get() {
var current: DeclarationDescriptor? = this
while (current != null) {
if (DescriptorUtils.isAnonymousObject(current)) {
return true
}
current = current.containingDeclaration
}
return false
}
private fun checkEnum(descriptor: ClassDescriptor, declaration: KtDeclaration, trace: BindingTrace) {
if (descriptor.kind != ClassKind.ENUM_CLASS) return
val entryBySerialName = mutableMapOf<String, ClassDescriptor?>()
descriptor.enumEntries().forEach { entryDescriptor ->
val serialNameAnnotation = entryDescriptor.annotations.serialNameAnnotation
val serialName = entryDescriptor.annotations.serialNameValue ?: entryDescriptor.name.asString()
val firstEntry = entryBySerialName[serialName]
if (firstEntry != null) {
trace.report(
SerializationErrors.DUPLICATE_SERIAL_NAME_ENUM.on(
serialNameAnnotation?.findAnnotationEntry() ?: firstEntry.annotations.serialNameAnnotation?.findAnnotationEntry()
?: declaration,
descriptor.defaultType,
serialName,
entryDescriptor.name.asString()
)
)
} else {
entryBySerialName[serialName] = entryDescriptor
}
}
}
private fun ClassDescriptor.isSerializableEnumWithMissingSerializer(): Boolean {
if (kind != ClassKind.ENUM_CLASS) return false
if (hasSerializableOrMetaAnnotation) return false
if (annotations.hasAnySerialAnnotation) return true
return enumEntries().any { (it.annotations.hasAnySerialAnnotation) }
}
open fun serializationPluginEnabledOn(descriptor: ClassDescriptor): Boolean {
// In the CLI/Gradle compiler, this diagnostic is located in the plugin itself.
// Therefore, if we are here, plugin is in the compile classpath and enabled.
// For the IDE case, see SerializationPluginIDEDeclarationChecker
return true
}
private fun buildSerializableProperties(descriptor: ClassDescriptor, trace: BindingTrace): SerializableProperties? {
if (!descriptor.hasSerializableOrMetaAnnotation) return null
if (!descriptor.isInternalSerializable) return null
if (descriptor.hasCompanionObjectAsSerializer) return null // customized by user
val props = SerializableProperties(descriptor, trace.bindingContext)
if (!props.isExternallySerializable) trace.reportOnSerializableOrMetaAnnotation(
descriptor,
SerializationErrors.PRIMARY_CONSTRUCTOR_PARAMETER_IS_NOT_A_PROPERTY
)
// check that all names are unique
val namesSet = mutableSetOf<String>()
props.serializableProperties.forEach {
if (!namesSet.add(it.name)) {
descriptor.onSerializableOrMetaAnnotation { a ->
trace.report(SerializationErrors.DUPLICATE_SERIAL_NAME.on(a, it.name))
}
}
}
trace.record(SERIALIZABLE_PROPERTIES, descriptor, props)
return props
}
private fun checkTransients(declaration: KtPureClassOrObject, trace: BindingTrace) {
val propertiesMap: Map<PropertyDescriptor, KtDeclaration> =
declaration.bodyPropertiesDescriptorsMap(
trace.bindingContext,
filterUninitialized = false
) + declaration.primaryConstructorPropertiesDescriptorsMap(trace.bindingContext)
propertiesMap.forEach { (descriptor, declaration) ->
val isInitialized = declarationHasInitializer(declaration) || descriptor.isLateInit
val isMarkedTransient = descriptor.annotations.serialTransient
val hasBackingField = descriptor.hasBackingField(trace.bindingContext)
if (!hasBackingField && isMarkedTransient) {
val transientPsi =
(descriptor.annotations.findAnnotation(SerializationAnnotations.serialTransientFqName) as? LazyAnnotationDescriptor)?.annotationEntry
trace.report(SerializationErrors.TRANSIENT_IS_REDUNDANT.on(transientPsi ?: declaration))
}
if (isMarkedTransient && !isInitialized && hasBackingField) {
trace.report(SerializationErrors.TRANSIENT_MISSING_INITIALIZER.on(declaration))
}
}
}
private fun declarationHasInitializer(declaration: KtDeclaration): Boolean = when (declaration) {
is KtParameter -> declaration.hasDefaultValue()
is KtProperty -> declaration.hasDelegateExpressionOrInitializer()
else -> false
}
private fun analyzePropertiesSerializers(trace: BindingTrace, serializableClass: ClassDescriptor, props: List<SerializableProperty>) {
val generatorContextForAnalysis = object : AbstractSerialGenerator(trace.bindingContext, serializableClass) {}
props.forEach {
val serializer = it.serializableWith?.toClassDescriptor
val propertyPsi = it.descriptor.findPsi() ?: return@forEach
val ktType = (propertyPsi as? KtCallableDeclaration)?.typeReference
if (serializer != null) {
val element = ktType?.typeElement
checkCustomSerializerMatch(it.module, it.type, it.descriptor, element, trace, propertyPsi)
checkCustomSerializerIsNotLocal(it.module, it.descriptor, trace, propertyPsi)
checkSerializerNullability(it.type, serializer.defaultType, element, trace, propertyPsi)
generatorContextForAnalysis.checkTypeArguments(it.module, it.type, element, trace, propertyPsi)
} else {
generatorContextForAnalysis.checkType(it.module, it.type, ktType, trace, propertyPsi)
}
}
}
private fun AbstractSerialGenerator.checkTypeArguments(
module: ModuleDescriptor,
type: KotlinType,
element: KtTypeElement?,
trace: BindingTrace,
fallbackElement: PsiElement
) {
type.arguments.forEachIndexed { i, it ->
checkType(
module,
it.type,
element?.typeArgumentsAsTypes?.getOrNull(i),
trace,
fallbackElement
)
}
}
private fun KotlinType.isUnsupportedInlineType() = isInlineClassType() && !KotlinBuiltIns.isPrimitiveTypeOrNullablePrimitiveType(this)
private fun canSupportInlineClasses(module: ModuleDescriptor, trace: BindingTrace): Boolean {
if (isIde) return true // do not get version from jar manifest in ide
return VersionReader.canSupportInlineClasses(module, trace)
}
private fun AbstractSerialGenerator.checkType(
module: ModuleDescriptor,
type: KotlinType,
ktType: KtTypeReference?,
trace: BindingTrace,
fallbackElement: PsiElement
) {
if (type.genericIndex != null) return // type arguments always have serializer stored in class' field
val element = ktType?.typeElement
if (type.isUnsupportedInlineType() && !canSupportInlineClasses(module, trace)) {
trace.report(
SerializationErrors.INLINE_CLASSES_NOT_SUPPORTED.on(
element ?: fallbackElement,
VersionReader.minVersionForInlineClasses.toString(),
VersionReader.getVersionsForCurrentModuleFromTrace(module, trace)?.implementationVersion.toString()
)
)
}
val serializer = findTypeSerializerOrContextUnchecked(module, type)
if (serializer != null) {
checkCustomSerializerMatch(module, type, type, element, trace, fallbackElement)
checkCustomSerializerIsNotLocal(module, type, trace, fallbackElement)
checkSerializerNullability(type, serializer.defaultType, element, trace, fallbackElement)
checkTypeArguments(module, type, element, trace, fallbackElement)
} else {
trace.report(SerializationErrors.SERIALIZER_NOT_FOUND.on(element ?: fallbackElement, type))
}
}
private fun checkCustomSerializerMatch(
module: ModuleDescriptor,
classType: KotlinType,
descriptor: Annotated,
element: KtElement?,
trace: BindingTrace,
fallbackElement: PsiElement
) {
val serializerType = descriptor.annotations.serializableWith(module) ?: return
val serializerForType = serializerType.supertypes().find { isKSerializer(it) }?.arguments?.first()?.type ?: return
// Compare constructors because we do not care about generic arguments and nullability
if (classType.constructor != serializerForType.constructor)
trace.report(
SerializationErrors.SERIALIZER_TYPE_INCOMPATIBLE.on(
element ?: fallbackElement,
classType,
serializerType,
serializerForType
)
)
}
private fun checkCustomSerializerIsNotLocal(
module: ModuleDescriptor,
declaration: Annotated,
trace: BindingTrace,
declarationElement: PsiElement
) {
val serializerType = declaration.annotations.serializableWith(module) ?: return
val serializerDescriptor = serializerType.toClassDescriptor ?: return
if (DescriptorUtils.isLocal(serializerDescriptor)) {
val element = declaration.findSerializableOrMetaAnnotationDeclaration() ?: declarationElement
trace.report(
SerializationErrors.LOCAL_SERIALIZER_USAGE.on(
element,
serializerType
)
)
}
}
private fun checkSerializerNullability(
classType: KotlinType,
serializerType: KotlinType,
element: KtTypeElement?,
trace: BindingTrace,
fallbackElement: PsiElement
) {
// @Serializable annotation has proper signature so this error would be caught in type checker
val castedToKSerial = serializerType.supertypes().find { isKSerializer(it) } ?: return
val serializerForType = castedToKSerial.arguments.first().type
if (!classType.isMarkedNullable && serializerForType.isMarkedNullable)
trace.report(
SerializationErrors.SERIALIZER_NULLABILITY_INCOMPATIBLE.on(element ?: fallbackElement, serializerType, classType),
)
}
private inline fun ClassDescriptor.onSerializableOrMetaAnnotation(report: (KtAnnotationEntry) -> Unit) {
findSerializableOrMetaAnnotationDeclaration()?.let(report)
}
private fun BindingTrace.reportOnSerializableOrMetaAnnotation(
descriptor: ClassDescriptor,
error: DiagnosticFactory0<in KtAnnotationEntry>
) {
descriptor.onSerializableOrMetaAnnotation { e ->
report(error.on(e))
}
}
}
val ClassDescriptor.serializableAnnotationIsUseless: Boolean
get() = hasSerializableOrMetaAnnotationWithoutArgs && !isInternalSerializable && !hasCompanionObjectAsSerializer && kind != ClassKind.ENUM_CLASS && !isSealedSerializableInterface
@@ -0,0 +1,149 @@
/*
* 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.diagnostic
import org.jetbrains.kotlin.diagnostics.rendering.CommonRenderers
import org.jetbrains.kotlin.diagnostics.rendering.DefaultErrorMessages
import org.jetbrains.kotlin.diagnostics.rendering.DiagnosticFactoryToRendererMap
import org.jetbrains.kotlin.diagnostics.rendering.Renderers
object SerializationPluginErrorsRendering : DefaultErrorMessages.Extension {
private val MAP = DiagnosticFactoryToRendererMap("SerializationPlugin")
override fun getMap() = MAP
init {
MAP.put(
SerializationErrors.INLINE_CLASSES_NOT_SUPPORTED,
"Inline classes require runtime serialization library version at least {0}, while your classpath has {1}.",
CommonRenderers.STRING,
CommonRenderers.STRING,
)
MAP.put(
SerializationErrors.PLUGIN_IS_NOT_ENABLED,
"kotlinx.serialization compiler plugin is not applied to the module, so this annotation would not be processed. " +
"Make sure that you've setup your buildscript correctly and re-import project."
)
MAP.put(
SerializationErrors.ANONYMOUS_OBJECTS_NOT_SUPPORTED,
"Anonymous objects or contained in it classes can not be serializable."
)
MAP.put(
SerializationErrors.INNER_CLASSES_NOT_SUPPORTED,
"Inner (with reference to outer this) serializable classes are not supported. Remove @Serializable annotation or 'inner' keyword."
)
MAP.put(
SerializationErrors.EXPLICIT_SERIALIZABLE_IS_REQUIRED,
"Explicit @Serializable annotation on enum class is required when @SerialName or @SerialInfo annotations are used on its members."
)
MAP.put(
SerializationErrors.SERIALIZABLE_ANNOTATION_IGNORED,
"@Serializable annotation without arguments can be used only on sealed interfaces." +
"Non-sealed interfaces are polymorphically serializable by default."
)
MAP.put(
SerializationErrors.NON_SERIALIZABLE_PARENT_MUST_HAVE_NOARG_CTOR,
"Impossible to make this class serializable because its parent is not serializable and does not have exactly one constructor without parameters"
)
MAP.put(
SerializationErrors.PRIMARY_CONSTRUCTOR_PARAMETER_IS_NOT_A_PROPERTY,
"This class is not serializable automatically because it has primary constructor parameters that are not properties"
)
MAP.put(
SerializationErrors.DUPLICATE_SERIAL_NAME,
"Serializable class has duplicate serial name of property ''{0}'', either in the class itself or its supertypes",
CommonRenderers.STRING
)
MAP.put(
SerializationErrors.DUPLICATE_SERIAL_NAME_ENUM,
"Enum class ''{0}'' has duplicate serial name ''{1}'' in entry ''{2}''",
Renderers.RENDER_TYPE,
CommonRenderers.STRING,
CommonRenderers.STRING
)
MAP.put(
SerializationErrors.SERIALIZER_NOT_FOUND,
"Serializer has not been found for type ''{0}''. " +
"To use context serializer as fallback, explicitly annotate type or property with @Contextual",
Renderers.RENDER_TYPE_WITH_ANNOTATIONS
)
MAP.put(
SerializationErrors.SERIALIZER_NULLABILITY_INCOMPATIBLE,
"Type ''{1}'' is non-nullable and therefore can not be serialized with serializer for nullable type ''{0}''",
Renderers.RENDER_TYPE,
Renderers.RENDER_TYPE
)
MAP.put(
SerializationErrors.SERIALIZER_TYPE_INCOMPATIBLE,
"Class ''{1}'', which is serializer for type ''{2}'', is applied here to type ''{0}''. This may lead to errors or incorrect behavior.",
Renderers.RENDER_TYPE,
Renderers.RENDER_TYPE,
Renderers.RENDER_TYPE
)
MAP.put(
SerializationErrors.LOCAL_SERIALIZER_USAGE,
"Class ''{0}'' can't be used as a serializer since it is local",
Renderers.RENDER_TYPE
)
MAP.put(
SerializationErrors.TRANSIENT_MISSING_INITIALIZER,
"This property is marked as @Transient and therefore must have an initializing expression"
)
MAP.put(
SerializationErrors.TRANSIENT_IS_REDUNDANT,
"Property does not have backing field which makes it non-serializable and therefore @Transient is redundant"
)
MAP.put(
SerializationErrors.JSON_FORMAT_REDUNDANT_DEFAULT,
"Redundant creation of Json default format. Creating instances for each usage can be slow."
)
MAP.put(
SerializationErrors.JSON_FORMAT_REDUNDANT,
"Redundant creation of Json format. Creating instances for each usage can be slow."
)
MAP.put(
SerializationErrors.INCORRECT_TRANSIENT,
"@kotlin.jvm.Transient does not affect @Serializable classes. Please use @kotlinx.serialization.Transient instead."
)
MAP.put(
SerializationErrors.REQUIRED_KOTLIN_TOO_HIGH,
"Your current Kotlin version is {0}, while kotlinx.serialization core runtime {1} requires at least Kotlin {2}. " +
"Please update your Kotlin compiler and IDE plugin.",
CommonRenderers.STRING,
CommonRenderers.STRING,
CommonRenderers.STRING
)
MAP.put(
SerializationErrors.PROVIDED_RUNTIME_TOO_LOW,
"Your current kotlinx.serialization core version is {0}, while current Kotlin compiler plugin {1} requires at least {2}. " +
"Please update your kotlinx.serialization runtime dependency.",
CommonRenderers.STRING,
CommonRenderers.STRING,
CommonRenderers.STRING
)
MAP.put(
SerializationErrors.INCONSISTENT_INHERITABLE_SERIALINFO,
"Argument values for inheritable serial info annotation ''{0}'' must be the same as the values in parent type ''{1}''",
Renderers.RENDER_TYPE,
Renderers.RENDER_TYPE
)
MAP.put(
SerializationErrors.EXTERNAL_CLASS_NOT_SERIALIZABLE,
"Cannot generate external serializer ''{0}'': class ''{1}'' have constructor parameters which are not properties and therefore it is not serializable automatically",
Renderers.RENDER_TYPE,
Renderers.RENDER_TYPE
)
MAP.put(
SerializationErrors.EXTERNAL_CLASS_IN_ANOTHER_MODULE,
"Cannot generate external serializer ''{0}'': class ''{1}'' is defined in another module",
Renderers.RENDER_TYPE,
Renderers.RENDER_TYPE
)
}
}
@@ -0,0 +1,77 @@
/*
* 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.diagnostic
import com.intellij.openapi.util.io.JarUtil
import org.jetbrains.kotlin.config.ApiVersion
import org.jetbrains.kotlin.config.KotlinCompilerVersion
import org.jetbrains.kotlin.descriptors.ModuleDescriptor
import org.jetbrains.kotlin.load.kotlin.KotlinJvmBinarySourceElement
import org.jetbrains.kotlin.resolve.BindingContext
import org.jetbrains.kotlin.resolve.BindingTrace
import org.jetbrains.kotlin.util.slicedMap.Slices
import org.jetbrains.kotlin.util.slicedMap.WritableSlice
import org.jetbrains.kotlinx.serialization.compiler.resolve.SerialEntityNames
import org.jetbrains.kotlinx.serialization.compiler.resolve.getClassFromSerializationPackage
import java.io.File
import java.util.jar.Attributes
object VersionReader {
data class RuntimeVersions(val implementationVersion: ApiVersion?, val requireKotlinVersion: ApiVersion?) {
fun currentCompilerMatchRequired(): Boolean {
val current = requireNotNull(KotlinCompilerVersion.getVersion()?.let(ApiVersion.Companion::parse))
return requireKotlinVersion == null || requireKotlinVersion <= current
}
fun implementationVersionMatchSupported(): Boolean {
return implementationVersion != null && implementationVersion >= MINIMAL_SUPPORTED_VERSION
}
}
fun getVersionsFromManifest(runtimeLibraryPath: File): RuntimeVersions {
val version = JarUtil.getJarAttribute(runtimeLibraryPath, Attributes.Name.IMPLEMENTATION_VERSION)?.let(ApiVersion.Companion::parse)
val kotlinVersion = JarUtil.getJarAttribute(runtimeLibraryPath, REQUIRE_KOTLIN_VERSION)?.let(ApiVersion.Companion::parse)
return RuntimeVersions(version, kotlinVersion)
}
val MINIMAL_SUPPORTED_VERSION = ApiVersion.parse("1.0-M1-SNAPSHOT")!!
private val REQUIRE_KOTLIN_VERSION = Attributes.Name("Require-Kotlin-Version")
private const val CLASS_SUFFIX = "!/kotlinx/serialization/KSerializer.class"
private val VERSIONS_SLICE: WritableSlice<ModuleDescriptor, RuntimeVersions> = Slices.createSimpleSlice()
fun getVersionsForCurrentModuleFromTrace(module: ModuleDescriptor, trace: BindingTrace): RuntimeVersions? {
trace.get(VERSIONS_SLICE, module)?.let { return it }
val versions = getVersionsForCurrentModule(module) ?: return null
trace.record(VERSIONS_SLICE, module, versions)
return versions
}
fun getVersionsForCurrentModuleFromContext(module: ModuleDescriptor, context: BindingContext?): RuntimeVersions? {
context?.get(VERSIONS_SLICE, module)?.let { return it }
return getVersionsForCurrentModule(module)
}
fun getVersionsForCurrentModule(module: ModuleDescriptor): RuntimeVersions? {
val markerClass = module.getClassFromSerializationPackage(SerialEntityNames.KSERIALIZER_CLASS)
val location = (markerClass.source as? KotlinJvmBinarySourceElement)?.binaryClass?.location ?: return null
val jarFile = location.removeSuffix(CLASS_SUFFIX)
if (!jarFile.endsWith(".jar")) return null
val file = File(jarFile)
if (!file.exists()) return null
return getVersionsFromManifest(file)
}
internal val minVersionForInlineClasses = ApiVersion.parse("1.1-M1-SNAPSHOT")!!
fun canSupportInlineClasses(module: ModuleDescriptor, trace: BindingTrace): Boolean {
// Klibs do not have manifest file, unfortunately, so we hope for the better
val currentVersion = getVersionsForCurrentModuleFromTrace(module, trace) ?: return true
val implVersion = currentVersion.implementationVersion ?: return false
return implVersion >= minVersionForInlineClasses
}
}
@@ -0,0 +1,48 @@
/*
* 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.extensions
import org.jetbrains.kotlin.descriptors.ClassDescriptor
import org.jetbrains.kotlin.descriptors.Modality
import org.jetbrains.kotlin.metadata.ProtoBuf
import org.jetbrains.kotlin.metadata.serialization.MutableVersionRequirementTable
import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.serialization.DescriptorSerializer
import org.jetbrains.kotlin.serialization.DescriptorSerializerPlugin
import org.jetbrains.kotlin.serialization.SerializerExtension
import org.jetbrains.kotlinx.serialization.compiler.resolve.SerializableProperties
import org.jetbrains.kotlinx.serialization.compiler.resolve.isInternalSerializable
class SerializationDescriptorSerializerPlugin : DescriptorSerializerPlugin {
private val descriptorMetadataMap: MutableMap<ClassDescriptor, SerializableProperties> = hashMapOf()
private val ClassDescriptor.needSaveProgramOrder: Boolean
get() = isInternalSerializable && (modality == Modality.OPEN || modality == Modality.ABSTRACT)
internal fun putIfNeeded(descriptor: ClassDescriptor, properties: SerializableProperties) {
if (!descriptor.needSaveProgramOrder) return
descriptorMetadataMap[descriptor] = properties
}
override fun afterClass(
descriptor: ClassDescriptor,
proto: ProtoBuf.Class.Builder,
versionRequirementTable: MutableVersionRequirementTable,
childSerializer: DescriptorSerializer,
extension: SerializerExtension
) {
fun Name.toIndex() = extension.stringTable.getStringIndex(asString())
if (!descriptor.needSaveProgramOrder) return
val propertiesCorrectOrder = (descriptorMetadataMap[descriptor] ?: return).serializableProperties
proto.setExtension(
SerializationPluginMetadataExtensions.propertiesNamesInProgramOrder,
propertiesCorrectOrder.map { it.descriptor.name.toIndex() }
)
descriptorMetadataMap.remove(descriptor)
}
}
@@ -0,0 +1,33 @@
// Generated by the protocol buffer compiler. DO NOT EDIT!
// source: plugins/kotlin-serialization/kotlin-serialization-compiler/src/class_extensions.proto
package org.jetbrains.kotlinx.serialization.compiler.extensions;
public final class SerializationPluginMetadataExtensions {
private SerializationPluginMetadataExtensions() {}
public static void registerAllExtensions(
org.jetbrains.kotlin.protobuf.ExtensionRegistryLite registry) {
registry.add(org.jetbrains.kotlinx.serialization.compiler.extensions.SerializationPluginMetadataExtensions.propertiesNamesInProgramOrder);
}
public static final int PROPERTIES_NAMES_IN_PROGRAM_ORDER_FIELD_NUMBER = 18000;
/**
* <code>extend .org.jetbrains.kotlin.metadata.Class { ... }</code>
*/
public static final
org.jetbrains.kotlin.protobuf.GeneratedMessageLite.GeneratedExtension<
org.jetbrains.kotlin.metadata.ProtoBuf.Class,
java.util.List<java.lang.Integer>> propertiesNamesInProgramOrder = org.jetbrains.kotlin.protobuf.GeneratedMessageLite
.newRepeatedGeneratedExtension(
org.jetbrains.kotlin.metadata.ProtoBuf.Class.getDefaultInstance(),
null,
null,
18000,
org.jetbrains.kotlin.protobuf.WireFormat.FieldType.INT32,
false,
java.lang.Integer.class);
static {
}
// @@protoc_insertion_point(outer_class_scope)
}
@@ -0,0 +1,131 @@
/*
* 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.extensions
import org.jetbrains.kotlin.descriptors.ClassConstructorDescriptor
import org.jetbrains.kotlin.descriptors.ClassDescriptor
import org.jetbrains.kotlin.descriptors.PropertyDescriptor
import org.jetbrains.kotlin.descriptors.SimpleFunctionDescriptor
import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.name.SpecialNames
import org.jetbrains.kotlin.platform.jvm.isJvm
import org.jetbrains.kotlin.resolve.BindingContext
import org.jetbrains.kotlin.resolve.descriptorUtil.platform
import org.jetbrains.kotlin.resolve.extensions.SyntheticResolveExtension
import org.jetbrains.kotlin.resolve.isInlineClass
import org.jetbrains.kotlin.resolve.lazy.LazyClassContext
import org.jetbrains.kotlin.resolve.lazy.declarations.ClassMemberDeclarationProvider
import org.jetbrains.kotlin.types.KotlinType
import org.jetbrains.kotlinx.serialization.compiler.backend.common.SerializationDescriptorUtils
import org.jetbrains.kotlinx.serialization.compiler.resolve.*
open class SerializationResolveExtension @JvmOverloads constructor(val metadataPlugin: SerializationDescriptorSerializerPlugin? = null) : SyntheticResolveExtension {
override fun getSyntheticNestedClassNames(thisDescriptor: ClassDescriptor): List<Name> = when {
thisDescriptor.isSerialInfoAnnotation && thisDescriptor.platform?.isJvm() == true -> listOf(SerialEntityNames.IMPL_NAME)
(thisDescriptor.shouldHaveGeneratedSerializer) && !thisDescriptor.hasCompanionObjectAsSerializer ->
listOf(SerialEntityNames.SERIALIZER_CLASS_NAME)
else -> listOf()
}
override fun getPossibleSyntheticNestedClassNames(thisDescriptor: ClassDescriptor): List<Name>? {
return listOf(SerialEntityNames.IMPL_NAME, SerialEntityNames.SERIALIZER_CLASS_NAME)
}
override fun getSyntheticFunctionNames(thisDescriptor: ClassDescriptor): List<Name> = when {
thisDescriptor.isSerializableObject || thisDescriptor.isCompanionObject && getSerializableClassDescriptorByCompanion(thisDescriptor) != null ->
listOf(SerialEntityNames.SERIALIZER_PROVIDER_NAME)
thisDescriptor.isInternalSerializable && !thisDescriptor.isInlineClass() && thisDescriptor.platform?.isJvm() == true && !hasCustomizedSerializeMethod(thisDescriptor) -> {
// add write$Self, but only if .serialize was not customized in companion.
// It works not only on JVM, but I see no reason to enable it on other platforms —
// private fields there have no access control, and additional function
// only increases compiled code size.
listOf(SerialEntityNames.WRITE_SELF_NAME)
}
else -> emptyList()
}
override fun getSyntheticPropertiesNames(thisDescriptor: ClassDescriptor): List<Name> {
// typeSerial0, typeSerial1, ... for serializers of parameterized classes
val count = thisDescriptor.declaredTypeParameters.size
if (count < 1) return emptyList()
val classDescriptor = getSerializableClassDescriptorBySerializer(thisDescriptor) ?: return emptyList()
if (!isAllowedToHaveAutoGeneratedSerializerMethods(thisDescriptor, classDescriptor)) return emptyList()
val propNames = (0 until count).map { "${SerialEntityNames.typeArgPrefix}$it" }.map { Name.identifier(it) }
return propNames
}
private fun hasCustomizedSerializeMethod(serializableClass: ClassDescriptor): Boolean {
// We cannot check whether companion has @Serializer(MyClass::class) annotation due to recursive resolve problems
// (apparently, resolve MyClass type asks for all function names, which leads us to this function again)
// so we rely on less strict check that companion just has non-empty @Serializer annotation.
// Anyway, I doubt that serializable class companion would ever be serializer for _another_ class.
val companion = serializableClass.companionObjectDescriptor ?: return false
return companion.annotations.hasAnnotation(SerializationAnnotations.serializerAnnotationFqName)
}
override fun generateSyntheticClasses(
thisDescriptor: ClassDescriptor,
name: Name,
ctx: LazyClassContext,
declarationProvider: ClassMemberDeclarationProvider,
result: MutableSet<ClassDescriptor>
) {
if (thisDescriptor.isSerialInfoAnnotation && name == SerialEntityNames.IMPL_NAME)
result.add(KSerializerDescriptorResolver.addSerialInfoImplClass(thisDescriptor, declarationProvider, ctx))
else if (thisDescriptor.shouldHaveGeneratedSerializer && name == SerialEntityNames.SERIALIZER_CLASS_NAME &&
result.none { it.name == SerialEntityNames.SERIALIZER_CLASS_NAME }
)
result.add(KSerializerDescriptorResolver.addSerializerImplClass(thisDescriptor, declarationProvider, ctx))
return
}
override fun getSyntheticCompanionObjectNameIfNeeded(thisDescriptor: ClassDescriptor): Name? =
if (thisDescriptor.shouldHaveGeneratedMethodsInCompanion && !thisDescriptor.isSerializableObject)
SpecialNames.DEFAULT_NAME_FOR_COMPANION_OBJECT
else null
override fun addSyntheticSupertypes(thisDescriptor: ClassDescriptor, supertypes: MutableList<KotlinType>) {
KSerializerDescriptorResolver.addSerialInfoSuperType(thisDescriptor, supertypes)
KSerializerDescriptorResolver.addSerializerSupertypes(thisDescriptor, supertypes)
KSerializerDescriptorResolver.addSerializerFactorySuperType(thisDescriptor, supertypes)
}
override fun generateSyntheticSecondaryConstructors(
thisDescriptor: ClassDescriptor,
bindingContext: BindingContext,
result: MutableCollection<ClassConstructorDescriptor>
) {
if (thisDescriptor.isInternalSerializable) {
// do not add synthetic deserialization constructor if .deserialize method is customized
if (thisDescriptor.hasCompanionObjectAsSerializer && SerializationDescriptorUtils.getSyntheticLoadMember(thisDescriptor.companionObjectDescriptor!!) == null) return
if (thisDescriptor.isInlineClass()) return
result.add(KSerializerDescriptorResolver.createLoadConstructorDescriptor(thisDescriptor, bindingContext, metadataPlugin))
}
}
override fun generateSyntheticMethods(
thisDescriptor: ClassDescriptor,
name: Name,
bindingContext: BindingContext,
fromSupertypes: List<SimpleFunctionDescriptor>,
result: MutableCollection<SimpleFunctionDescriptor>
) {
KSerializerDescriptorResolver.generateSerializerMethods(thisDescriptor, fromSupertypes, name, result)
KSerializerDescriptorResolver.generateCompanionObjectMethods(thisDescriptor, name, result)
KSerializerDescriptorResolver.generateSerializableClassMethods(thisDescriptor, name, result)
}
override fun generateSyntheticProperties(
thisDescriptor: ClassDescriptor,
name: Name,
bindingContext: BindingContext,
fromSupertypes: ArrayList<PropertyDescriptor>,
result: MutableSet<PropertyDescriptor>
) {
KSerializerDescriptorResolver.generateDescriptorsForAnnotationImpl(thisDescriptor, fromSupertypes, result)
KSerializerDescriptorResolver.generateSerializerProperties(thisDescriptor, fromSupertypes, name, result)
}
}
@@ -0,0 +1,306 @@
/*
* 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.resolve
import org.jetbrains.kotlin.builtins.KotlinBuiltIns
import org.jetbrains.kotlin.descriptors.*
import org.jetbrains.kotlin.descriptors.annotations.Annotated
import org.jetbrains.kotlin.descriptors.annotations.AnnotationDescriptor
import org.jetbrains.kotlin.descriptors.annotations.Annotations
import org.jetbrains.kotlin.name.ClassId
import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.platform.js.isJs
import org.jetbrains.kotlin.platform.konan.isNative
import org.jetbrains.kotlin.psi.KtAnnotationEntry
import org.jetbrains.kotlin.resolve.descriptorUtil.annotationClass
import org.jetbrains.kotlin.resolve.descriptorUtil.module
import org.jetbrains.kotlin.resolve.descriptorUtil.platform
import org.jetbrains.kotlin.resolve.lazy.descriptors.LazyAnnotationDescriptor
import org.jetbrains.kotlin.resolve.scopes.getDescriptorsFiltered
import org.jetbrains.kotlin.types.*
import org.jetbrains.kotlin.types.typeUtil.isSubtypeOf
import org.jetbrains.kotlin.types.typeUtil.representativeUpperBound
import org.jetbrains.kotlinx.serialization.compiler.resolve.SerialEntityNames.ENUM_SERIALIZER_FACTORY_FUNC_NAME
import org.jetbrains.kotlinx.serialization.compiler.resolve.SerialEntityNames.MARKED_ENUM_SERIALIZER_FACTORY_FUNC_NAME
import org.jetbrains.kotlinx.serialization.compiler.resolve.SerializationAnnotations.inheritableSerialInfoFqName
import org.jetbrains.kotlinx.serialization.compiler.resolve.SerializationAnnotations.metaSerializableAnnotationFqName
import org.jetbrains.kotlinx.serialization.compiler.resolve.SerializationAnnotations.serialInfoFqName
import org.jetbrains.kotlinx.serialization.compiler.resolve.SerializationAnnotations.serializableAnnotationFqName
fun isAllowedToHaveAutoGeneratedSerializerMethods(
classDescriptor: ClassDescriptor,
serializableClassDescriptor: ClassDescriptor
): Boolean {
if (serializableClassDescriptor.isSerializableEnum()) return true
// don't generate automatically anything for enums or interfaces or other strange things
if (serializableClassDescriptor.kind != ClassKind.CLASS) return false
// it is either GeneratedSerializer implementation
// or user implementation which does not have type parameters (to be able to correctly initialize descriptor)
return classDescriptor.typeConstructor.supertypes.any(::isGeneratedKSerializer) ||
(classDescriptor.typeConstructor.supertypes.any(::isKSerializer) && classDescriptor.declaredTypeParameters.isEmpty())
}
fun isKSerializer(type: KotlinType?): Boolean =
type != null && KotlinBuiltIns.isConstructedFromGivenClass(type, SerialEntityNames.KSERIALIZER_NAME_FQ)
fun isGeneratedKSerializer(type: KotlinType?): Boolean =
type != null && KotlinBuiltIns.isConstructedFromGivenClass(type, SerialEntityNames.GENERATED_SERIALIZER_FQ)
fun ClassDescriptor.getGeneratedSerializerDescriptor(): ClassDescriptor =
module.getClassFromInternalSerializationPackage(SerialEntityNames.GENERATED_SERIALIZER_CLASS.identifier)
fun ClassDescriptor.createSerializerTypeFor(argument: SimpleType, baseSerializerInterface: FqName): SimpleType {
val projectionType = Variance.INVARIANT
val types = listOf(TypeProjectionImpl(projectionType, argument))
val descriptor = module.findClassAcrossModuleDependencies(ClassId.topLevel(baseSerializerInterface))
?: throw IllegalArgumentException("Can't locate $baseSerializerInterface. Is kotlinx-serialization library present in compile classpath?")
return KotlinTypeFactory.simpleNotNullType(TypeAttributes.Empty, descriptor, types)
}
fun extractKSerializerArgumentFromImplementation(implementationClass: ClassDescriptor): KotlinType? {
val supertypes = implementationClass.typeConstructor.supertypes
val kSerializerSupertype = supertypes.find { isGeneratedKSerializer(it) }
?: supertypes.find { isKSerializer(it) }
?: return null
return kSerializerSupertype.arguments.first().type
}
val DeclarationDescriptor.serializableWith: KotlinType?
get() = annotations.serializableWith(module)
fun Annotations.serializableWith(module: ModuleDescriptor): KotlinType? =
this.findAnnotationKotlinTypeValue(serializableAnnotationFqName, module, "with")
val DeclarationDescriptor.serializerForClass: KotlinType?
get() = annotations.findAnnotationKotlinTypeValue(SerializationAnnotations.serializerAnnotationFqName, module, "forClass")
val ClassDescriptor.isSerialInfoAnnotation: Boolean
get() = annotations.hasAnnotation(serialInfoFqName)
|| annotations.hasAnnotation(inheritableSerialInfoFqName)
|| annotations.hasAnnotation(metaSerializableAnnotationFqName)
val ClassDescriptor.isInheritableSerialInfoAnnotation: Boolean
get() = annotations.hasAnnotation(inheritableSerialInfoFqName)
val Annotations.serialNameValue: String?
get() = findAnnotationConstantValue(SerializationAnnotations.serialNameAnnotationFqName, "value")
val Annotations.serialNameAnnotation: AnnotationDescriptor?
get() = findAnnotation(SerializationAnnotations.serialNameAnnotationFqName)
val Annotations.serialRequired: Boolean
get() = hasAnnotation(SerializationAnnotations.requiredAnnotationFqName)
val Annotations.serialTransient: Boolean
get() = hasAnnotation(SerializationAnnotations.serialTransientFqName)
// ----------------------------------------
val KotlinType?.toClassDescriptor: ClassDescriptor?
@JvmName("toClassDescriptor")
get() = this?.constructor?.declarationDescriptor?.let { descriptor ->
when (descriptor) {
is ClassDescriptor -> descriptor
is TypeParameterDescriptor -> descriptor.representativeUpperBound.toClassDescriptor
else -> null
}
}
val ClassDescriptor.shouldHaveGeneratedMethodsInCompanion: Boolean
get() = this.isSerializableObject || this.isSerializableEnum() || (this.kind == ClassKind.CLASS && hasSerializableOrMetaAnnotation) || this.isSealedSerializableInterface
val ClassDescriptor.isSerializableObject: Boolean
get() = kind == ClassKind.OBJECT && hasSerializableOrMetaAnnotation
val ClassDescriptor.isInternallySerializableObject: Boolean
get() = kind == ClassKind.OBJECT && hasSerializableOrMetaAnnotationWithoutArgs
val ClassDescriptor.isSealedSerializableInterface: Boolean
get() = kind == ClassKind.INTERFACE && modality == Modality.SEALED && hasSerializableOrMetaAnnotation
val ClassDescriptor.isInternalSerializable: Boolean //todo normal checking
get() {
if (kind != ClassKind.CLASS) return false
return hasSerializableOrMetaAnnotationWithoutArgs
}
fun ClassDescriptor.isSerializableEnum(): Boolean = kind == ClassKind.ENUM_CLASS && hasSerializableOrMetaAnnotation
fun ClassDescriptor.isEnumWithLegacyGeneratedSerializer(): Boolean = isInternallySerializableEnum() && useGeneratedEnumSerializer
fun ClassDescriptor.isInternallySerializableEnum(): Boolean =
kind == ClassKind.ENUM_CLASS && hasSerializableOrMetaAnnotationWithoutArgs
val ClassDescriptor.shouldHaveGeneratedSerializer: Boolean
get() = (isInternalSerializable && (modality == Modality.FINAL || modality == Modality.OPEN))
|| isEnumWithLegacyGeneratedSerializer()
val ClassDescriptor.useGeneratedEnumSerializer: Boolean
get() {
val functions = module.getPackage(SerializationPackages.internalPackageFqName).memberScope.getFunctionNames()
return !functions.contains(ENUM_SERIALIZER_FACTORY_FUNC_NAME) || !functions.contains(MARKED_ENUM_SERIALIZER_FACTORY_FUNC_NAME)
}
fun ClassDescriptor.enumEntries(): List<ClassDescriptor> {
check(this.kind == ClassKind.ENUM_CLASS)
return unsubstitutedMemberScope.getContributedDescriptors().asSequence()
.filterIsInstance<ClassDescriptor>()
.filter { it.kind == ClassKind.ENUM_ENTRY }
.toList()
}
// check enum or its elements has any SerialInfo annotation
fun ClassDescriptor.isEnumWithSerialInfoAnnotation(): Boolean {
if (kind != ClassKind.ENUM_CLASS) return false
if (annotations.hasAnySerialAnnotation) return true
return enumEntries().any { (it.annotations.hasAnySerialAnnotation) }
}
val Annotations.hasAnySerialAnnotation: Boolean
get() = serialNameValue != null || any { it.annotationClass?.isSerialInfoAnnotation == true }
val ClassDescriptor.hasSerializableOrMetaAnnotation
get() = hasSerializableAnnotation || hasMetaSerializableAnnotation
private val ClassDescriptor.hasSerializableAnnotation
get() = annotations.hasSerializableAnnotation
private val Annotations.hasSerializableAnnotation
get() = hasAnnotation(serializableAnnotationFqName)
val ClassDescriptor.hasMetaSerializableAnnotation: Boolean
get() = annotations.any { it.isMetaSerializableAnnotation }
val AnnotationDescriptor.isMetaSerializableAnnotation: Boolean
get() = annotationClass?.annotations?.hasAnnotation(metaSerializableAnnotationFqName) ?: false
val ClassDescriptor.hasSerializableOrMetaAnnotationWithoutArgs: Boolean
get() = hasSerializableAnnotationWithoutArgs
|| (!annotations.hasSerializableAnnotation && hasMetaSerializableAnnotation)
private val ClassDescriptor.hasSerializableAnnotationWithoutArgs: Boolean
get() {
if (!hasSerializableAnnotation) return false
// If provided descriptor is lazy, carefully look at psi in order not to trigger full resolve which may be recursive.
// Otherwise, this descriptor is deserialized from another module, and it is OK to check value right away.
val psi = findSerializableAnnotationDeclaration() ?: return (serializableWith == null)
return psi.valueArguments.isEmpty()
}
private fun Annotated.findSerializableAnnotationDeclaration(): KtAnnotationEntry? {
val lazyDesc = annotations.findAnnotation(serializableAnnotationFqName) as? LazyAnnotationDescriptor
return lazyDesc?.annotationEntry
}
fun Annotated.findSerializableOrMetaAnnotationDeclaration(): KtAnnotationEntry? {
val lazyDesc = (annotations.findAnnotation(serializableAnnotationFqName)
?: annotations.firstOrNull { it.isMetaSerializableAnnotation }) as? LazyAnnotationDescriptor
return lazyDesc?.annotationEntry
}
fun Annotated.findAnnotationDeclaration(fqName: FqName): KtAnnotationEntry? {
val lazyDesc = annotations.findAnnotation(fqName) as? LazyAnnotationDescriptor
return lazyDesc?.annotationEntry
}
// For abstract classes marked with @Serializable,
// methods are generated anyway, although they shouldn't have
// generated $serializer and use Polymorphic one.
fun ClassDescriptor.isAbstractOrSealedSerializableClass(): Boolean =
isInternalSerializable && (modality == Modality.ABSTRACT || modality == Modality.SEALED)
fun ClassDescriptor.polymorphicSerializerIfApplicableAutomatically(): ClassDescriptor? {
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 { module.getClassFromSerializationPackage(it) }
}
// serializer that was declared for this type
val ClassDescriptor?.classSerializer: ClassDescriptor?
get() = this?.let {
// serializer annotation on class?
serializableWith?.let { return it.toClassDescriptor }
// companion object serializer?
if (hasCompanionObjectAsSerializer) return companionObjectDescriptor
// can infer @Poly?
polymorphicSerializerIfApplicableAutomatically()?.let { return it }
// default serializable?
if (shouldHaveGeneratedSerializer) {
// $serializer nested class
return this.unsubstitutedMemberScope
.getDescriptorsFiltered(nameFilter = { it == SerialEntityNames.SERIALIZER_CLASS_NAME })
.filterIsInstance<ClassDescriptor>().singleOrNull()
}
return null
}
val ClassDescriptor.hasCompanionObjectAsSerializer: Boolean
get() = isInternallySerializableObject || companionObjectDescriptor?.serializerForClass == this.defaultType
// returns only user-overriden Serializer
val KotlinType.overridenSerializer: KotlinType?
get() {
val desc = this.toClassDescriptor ?: return null
desc.serializableWith?.let { return it }
return null
}
val KotlinType.genericIndex: Int?
get() = (this.constructor.declarationDescriptor as? TypeParameterDescriptor)?.index
fun getSerializableClassDescriptorByCompanion(thisDescriptor: ClassDescriptor): ClassDescriptor? {
if (thisDescriptor.isSerializableObject) return thisDescriptor
if (!thisDescriptor.isCompanionObject) return null
val classDescriptor = (thisDescriptor.containingDeclaration as? ClassDescriptor) ?: return null
if (!classDescriptor.shouldHaveGeneratedMethodsInCompanion) return null
return classDescriptor
}
fun ClassDescriptor.needSerializerFactory(): Boolean {
if (!(this.platform?.isNative() == true || this.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.declaredTypeParameters.isEmpty()) return false
return true
}
fun getSerializableClassDescriptorBySerializer(serializerDescriptor: ClassDescriptor): ClassDescriptor? {
val serializerForClass = serializerDescriptor.serializerForClass
if (serializerForClass != null) return serializerForClass.toClassDescriptor
if (serializerDescriptor.name !in setOf(
SerialEntityNames.SERIALIZER_CLASS_NAME,
SerialEntityNames.GENERATED_SERIALIZER_CLASS
)
) return null
val classDescriptor = (serializerDescriptor.containingDeclaration as? ClassDescriptor) ?: return null
if (!classDescriptor.shouldHaveGeneratedSerializer) return null
return classDescriptor
}
fun ClassDescriptor.checkSerializableClassPropertyResult(prop: PropertyDescriptor): Boolean =
prop.returnType!!.isSubtypeOf(getClassFromSerializationPackage(SerialEntityNames.SERIAL_DESCRIPTOR_CLASS).toSimpleType(false)) // todo: cache lookup
// todo: serialization: do an actual check better that just number of parameters
fun ClassDescriptor.checkSaveMethodParameters(parameters: List<ValueParameterDescriptor>): Boolean =
parameters.size == 2
fun ClassDescriptor.checkSaveMethodResult(type: KotlinType): Boolean =
KotlinBuiltIns.isUnit(type)
// todo: serialization: do an actual check better that just number of parameters
fun ClassDescriptor.checkLoadMethodParameters(parameters: List<ValueParameterDescriptor>): Boolean =
parameters.size == 1
fun ClassDescriptor.checkLoadMethodResult(type: KotlinType): Boolean =
getSerializableClassDescriptorBySerializer(this)?.defaultType == type
@@ -0,0 +1,667 @@
/*
* 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.resolve
import org.jetbrains.kotlin.builtins.KotlinBuiltIns
import org.jetbrains.kotlin.descriptors.*
import org.jetbrains.kotlin.descriptors.annotations.AnnotationDescriptor
import org.jetbrains.kotlin.descriptors.annotations.AnnotationDescriptorImpl
import org.jetbrains.kotlin.descriptors.annotations.Annotations
import org.jetbrains.kotlin.descriptors.annotations.createDeprecatedAnnotation
import org.jetbrains.kotlin.descriptors.impl.*
import org.jetbrains.kotlin.incremental.components.NoLookupLocation
import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.name.StandardClassIds
import org.jetbrains.kotlin.psi.synthetics.SyntheticClassOrObjectDescriptor
import org.jetbrains.kotlin.resolve.BindingContext
import org.jetbrains.kotlin.resolve.calls.components.isVararg
import org.jetbrains.kotlin.resolve.descriptorUtil.builtIns
import org.jetbrains.kotlin.resolve.descriptorUtil.module
import org.jetbrains.kotlin.resolve.lazy.LazyClassContext
import org.jetbrains.kotlin.resolve.lazy.declarations.ClassMemberDeclarationProvider
import org.jetbrains.kotlin.resolve.lazy.descriptors.LazyClassDescriptor
import org.jetbrains.kotlin.storage.LockBasedStorageManager
import org.jetbrains.kotlin.types.*
import org.jetbrains.kotlin.types.typeUtil.createProjection
import org.jetbrains.kotlin.types.typeUtil.makeNullable
import org.jetbrains.kotlinx.serialization.compiler.extensions.SerializationDescriptorSerializerPlugin
import org.jetbrains.kotlinx.serialization.compiler.resolve.SerialEntityNames.IMPL_NAME
import org.jetbrains.kotlinx.serialization.compiler.resolve.SerialEntityNames.SERIALIZER_CLASS_NAME
import org.jetbrains.kotlinx.serialization.compiler.resolve.SerialEntityNames.typeArgPrefix
object KSerializerDescriptorResolver {
fun createDeprecatedHiddenAnnotation(module: ModuleDescriptor): AnnotationDescriptor {
return module.builtIns.createDeprecatedAnnotation(
"This synthesized declaration should not be used directly",
level = "HIDDEN"
)
}
fun isSerialInfoImpl(thisDescriptor: ClassDescriptor): Boolean {
return thisDescriptor.name == IMPL_NAME
&& thisDescriptor.containingDeclaration is LazyClassDescriptor
&& (thisDescriptor.containingDeclaration as ClassDescriptor).isSerialInfoAnnotation
}
fun addSerialInfoSuperType(thisDescriptor: ClassDescriptor, supertypes: MutableList<KotlinType>) {
if (isSerialInfoImpl(thisDescriptor)) {
supertypes.add((thisDescriptor.containingDeclaration as LazyClassDescriptor).toSimpleType(false))
}
}
fun addSerializerFactorySuperType(classDescriptor: ClassDescriptor, supertypes: MutableList<KotlinType>) {
if (!classDescriptor.needSerializerFactory()) return
val serializerFactoryClass =
classDescriptor.module.getClassFromInternalSerializationPackage("SerializerFactory")
supertypes.add(KotlinTypeFactory.simpleNotNullType(TypeAttributes.Empty, serializerFactoryClass, listOf()))
}
fun addSerializerSupertypes(classDescriptor: ClassDescriptor, supertypes: MutableList<KotlinType>) {
val serializableClassDescriptor = getSerializableClassDescriptorBySerializer(classDescriptor) ?: return
if (supertypes.any(::isKSerializer)) return
// Add GeneratedSerializer as superinterface for generated $serializer class, and KSerializer to all others
val fqName = if (classDescriptor.name == SerialEntityNames.SERIALIZER_CLASS_NAME)
SerialEntityNames.GENERATED_SERIALIZER_FQ
else
SerialEntityNames.KSERIALIZER_NAME_FQ
supertypes.add(classDescriptor.createSerializerTypeFor(serializableClassDescriptor.defaultType, fqName))
}
fun addSerialInfoImplClass(
interfaceDesc: ClassDescriptor,
declarationProvider: ClassMemberDeclarationProvider,
ctx: LazyClassContext
): ClassDescriptor {
val interfaceDecl = declarationProvider.correspondingClassOrObject!!
val scope = ctx.declarationScopeProvider.getResolutionScopeForDeclaration(declarationProvider.ownerInfo!!.scopeAnchor)
val props = interfaceDecl.primaryConstructorParameters
// if there is some properties, there will be a public synthetic constructor at the codegen phase
val primaryCtorVisibility = if (props.isEmpty()) DescriptorVisibilities.PUBLIC else DescriptorVisibilities.PRIVATE
val descriptor = SyntheticClassOrObjectDescriptor(
ctx,
interfaceDecl,
interfaceDesc,
IMPL_NAME,
interfaceDesc.source,
scope,
Modality.FINAL,
DescriptorVisibilities.PUBLIC,
Annotations.create(listOf(createDeprecatedHiddenAnnotation(interfaceDesc.module))),
primaryCtorVisibility,
ClassKind.CLASS,
false
)
descriptor.initialize()
return descriptor
}
fun addSerializerImplClass(
thisDescriptor: ClassDescriptor,
declarationProvider: ClassMemberDeclarationProvider,
ctx: LazyClassContext
): ClassDescriptor {
val thisDeclaration = declarationProvider.correspondingClassOrObject!!
val scope = ctx.declarationScopeProvider.getResolutionScopeForDeclaration(declarationProvider.ownerInfo!!.scopeAnchor)
val hasTypeParams = thisDescriptor.declaredTypeParameters.isNotEmpty()
val serializerKind = if (hasTypeParams) ClassKind.CLASS else ClassKind.OBJECT
val serializerDescriptor = SyntheticClassOrObjectDescriptor(
ctx,
thisDeclaration,
thisDescriptor, SERIALIZER_CLASS_NAME, thisDescriptor.source,
scope,
Modality.FINAL, DescriptorVisibilities.PUBLIC,
Annotations.create(listOf(createDeprecatedHiddenAnnotation(thisDescriptor.module))),
DescriptorVisibilities.PRIVATE,
serializerKind, false
)
val typeParameters: List<TypeParameterDescriptor> =
thisDescriptor.declaredTypeParameters.mapIndexed { index, param ->
TypeParameterDescriptorImpl.createWithDefaultBound(
serializerDescriptor, Annotations.EMPTY, false, Variance.INVARIANT,
param.name, index, LockBasedStorageManager.NO_LOCKS
)
}
serializerDescriptor.initialize(typeParameters)
val secondaryCtors =
if (!hasTypeParams)
emptyList()
else
listOf(createTypedSerializerConstructorDescriptor(serializerDescriptor, thisDescriptor, typeParameters))
serializerDescriptor.secondaryConstructors = secondaryCtors
return serializerDescriptor
}
fun generateSerializerProperties(
thisDescriptor: ClassDescriptor,
fromSupertypes: ArrayList<PropertyDescriptor>,
name: Name,
result: MutableSet<PropertyDescriptor>
) {
val classDescriptor = getSerializableClassDescriptorBySerializer(thisDescriptor) ?: return
// Do not auto-generate anything for user serializers
if (!isAllowedToHaveAutoGeneratedSerializerMethods(thisDescriptor, classDescriptor)) return
if (name == SerialEntityNames.SERIAL_DESC_FIELD_NAME && result.none(thisDescriptor::checkSerializableClassPropertyResult) &&
fromSupertypes.none { thisDescriptor.checkSerializableClassPropertyResult(it) && it.modality == Modality.FINAL }
) {
result.add(createSerializableClassPropertyDescriptor(thisDescriptor, classDescriptor))
}
// don't add local serializer fields if typed constructor is not synthetic
if (classDescriptor.declaredTypeParameters.isNotEmpty() &&
findSerializerConstructorForTypeArgumentsSerializers(thisDescriptor, onlyIfSynthetic = true) != null
) {
result.addAll(createLocalSerializersFieldsDescriptor(name, classDescriptor, thisDescriptor))
}
}
fun generateCompanionObjectMethods(
thisDescriptor: ClassDescriptor,
name: Name,
result: MutableCollection<SimpleFunctionDescriptor>
) {
val classDescriptor = getSerializableClassDescriptorByCompanion(thisDescriptor) ?: return
if (name == SerialEntityNames.SERIALIZER_PROVIDER_NAME && result.none { it.valueParameters.size == classDescriptor.declaredTypeParameters.size }) {
result.add(createSerializerGetterDescriptor(thisDescriptor, classDescriptor))
}
if (thisDescriptor.needSerializerFactory() && name == SerialEntityNames.SERIALIZER_PROVIDER_NAME && result.none { it.valueParameters.size == 1 && it.valueParameters.first().isVararg }) {
result.add(createSerializerFactoryVarargDescriptor(thisDescriptor))
}
}
fun generateSerializerMethods(
thisDescriptor: ClassDescriptor,
fromSupertypes: List<SimpleFunctionDescriptor>,
name: Name,
result: MutableCollection<SimpleFunctionDescriptor>
) {
val classDescriptor = getSerializableClassDescriptorBySerializer(thisDescriptor) ?: return
// Do not auto-generate anything for user serializers
if (!isAllowedToHaveAutoGeneratedSerializerMethods(thisDescriptor, classDescriptor)) return
fun shouldAddSerializerFunction(checkParameters: (FunctionDescriptor) -> Boolean): Boolean {
// Add 'save' / 'load' iff there is no such declared member AND there is no such final member in supertypes
return result.none(checkParameters) &&
fromSupertypes.none { checkParameters(it) && it.modality == Modality.FINAL }
}
val isSave = name == SerialEntityNames.SAVE_NAME &&
shouldAddSerializerFunction { classDescriptor.checkSaveMethodParameters(it.valueParameters) }
val isLoad = name == SerialEntityNames.LOAD_NAME &&
shouldAddSerializerFunction { classDescriptor.checkLoadMethodParameters(it.valueParameters) }
val isDescriptorGetter = name == SerialEntityNames.CHILD_SERIALIZERS_GETTER &&
thisDescriptor.typeConstructor.supertypes.any(::isGeneratedKSerializer) &&
shouldAddSerializerFunction { true /* TODO? */ }
val isTypeParamsSerializersGetter = name == SerialEntityNames.TYPE_PARAMS_SERIALIZERS_GETTER &&
thisDescriptor.typeConstructor.supertypes.any(::isGeneratedKSerializer) &&
classDescriptor.declaredTypeParameters.isNotEmpty() &&
shouldAddSerializerFunction { true /* TODO? */ }
if (isSave || isLoad || isDescriptorGetter || isTypeParamsSerializersGetter) {
result.add(doCreateSerializerFunction(thisDescriptor, name))
}
}
fun generateSerializableClassMethods(thisDescriptor: ClassDescriptor, name: Name, result: MutableCollection<SimpleFunctionDescriptor>) {
if (thisDescriptor.isInternalSerializable && name == SerialEntityNames.WRITE_SELF_NAME)
result.add(createWriteSelfFunctionDescriptor(thisDescriptor))
}
private fun createSerializableClassPropertyDescriptor(
thisDescriptor: ClassDescriptor,
serializableClassDescriptor: ClassDescriptor
): PropertyDescriptor {
val typeParam = listOf(createProjection(serializableClassDescriptor.defaultType, Variance.INVARIANT, null))
val propertyFromSerializer = thisDescriptor.getGeneratedSerializerDescriptor().getMemberScope(typeParam)
.getContributedVariables(SerialEntityNames.SERIAL_DESC_FIELD_NAME, NoLookupLocation.FROM_BUILTINS).single()
val result = doCreateSerializerProperty(
thisDescriptor,
SerialEntityNames.SERIAL_DESC_FIELD_NAME,
propertyFromSerializer.type,
propertyFromSerializer.typeParameters,
DescriptorVisibilities.PUBLIC,
Modality.OPEN // TODO: it was historically OPEN, but I do not see the reasons not to change to FINAL
)
result.overriddenDescriptors = listOf(propertyFromSerializer)
return result
}
private fun doCreateSerializerProperty(
thisDescriptor: ClassDescriptor,
name: Name,
type: KotlinType,
typeParameters: List<TypeParameterDescriptor> = emptyList(),
visibility: DescriptorVisibility = DescriptorVisibilities.PRIVATE,
modality: Modality = Modality.FINAL,
needBackingField: Boolean = false
): PropertyDescriptor {
val propertyDescriptor = PropertyDescriptorImpl.create(
thisDescriptor, Annotations.EMPTY, modality, visibility, false, name,
CallableMemberDescriptor.Kind.SYNTHESIZED, thisDescriptor.source, false, false, false, false, false, false
)
val extensionReceiverParameter: ReceiverParameterDescriptor? = null // kludge to disambiguate call
propertyDescriptor.setType(
type,
typeParameters,
thisDescriptor.thisAsReceiverParameter,
extensionReceiverParameter,
emptyList()
)
val propertyGetter = PropertyGetterDescriptorImpl(
propertyDescriptor, Annotations.EMPTY, modality, visibility, false, false, false,
CallableMemberDescriptor.Kind.SYNTHESIZED, null, thisDescriptor.source
)
propertyGetter.initialize(type)
val backingField = if (needBackingField) FieldDescriptorImpl(Annotations.EMPTY, propertyDescriptor) else null
propertyDescriptor.initialize(propertyGetter, null, backingField, null)
return propertyDescriptor
}
private fun doCreateSerializerFunction(
companionDescriptor: ClassDescriptor,
name: Name
): SimpleFunctionDescriptor {
val functionDescriptor = SimpleFunctionDescriptorImpl.create(
companionDescriptor, Annotations.EMPTY, name, CallableMemberDescriptor.Kind.SYNTHESIZED, companionDescriptor.source
)
val serializableClassOnImplSite = extractKSerializerArgumentFromImplementation(companionDescriptor)
?: throw AssertionError("Serializer does not implement ${SerialEntityNames.KSERIALIZER_CLASS}??")
val typeParam = listOf(createProjection(serializableClassOnImplSite, Variance.INVARIANT, null))
val functionFromSerializer = companionDescriptor.getGeneratedSerializerDescriptor().getMemberScope(typeParam)
.getContributedFunctions(name, NoLookupLocation.FROM_BUILTINS).single()
functionDescriptor.initialize(
null,
companionDescriptor.thisAsReceiverParameter,
emptyList(),
functionFromSerializer.typeParameters,
functionFromSerializer.valueParameters.map { it.copy(functionDescriptor, it.name, it.index) },
functionFromSerializer.returnType,
Modality.OPEN,
DescriptorVisibilities.PUBLIC
)
return functionDescriptor
}
fun createValPropertyDescriptor(
name: Name,
containingClassDescriptor: ClassDescriptor,
type: KotlinType,
visibility: DescriptorVisibility = DescriptorVisibilities.PRIVATE,
createGetter: Boolean = false
): PropertyDescriptor {
val propertyDescriptor = PropertyDescriptorImpl.create(
containingClassDescriptor,
Annotations.EMPTY, Modality.FINAL, visibility, false, name,
CallableMemberDescriptor.Kind.SYNTHESIZED, containingClassDescriptor.source, false, false, false, false, false, false
)
val extensionReceiverParameter: ReceiverParameterDescriptor? = null // kludge to disambiguate call
propertyDescriptor.setType(
type,
emptyList(), // no need type parameters?
containingClassDescriptor.thisAsReceiverParameter,
extensionReceiverParameter,
emptyList()
)
val propertyGetter: PropertyGetterDescriptorImpl? = if (createGetter) {
PropertyGetterDescriptorImpl(
propertyDescriptor, Annotations.EMPTY, Modality.FINAL, visibility, false, false, false,
CallableMemberDescriptor.Kind.SYNTHESIZED, null, containingClassDescriptor.source
).apply { initialize(type) }
} else {
null
}
propertyDescriptor.initialize(propertyGetter, null)
return propertyDescriptor
}
fun createLoadConstructorDescriptor(
classDescriptor: ClassDescriptor,
bindingContext: BindingContext,
metadataPlugin: SerializationDescriptorSerializerPlugin?
): ClassConstructorDescriptor {
if (!classDescriptor.isInternalSerializable) throw IllegalArgumentException()
val functionDescriptor = ClassConstructorDescriptorImpl.createSynthesized(
classDescriptor,
Annotations.create(listOf(createDeprecatedHiddenAnnotation(classDescriptor.module))),
false,
SourceElement.NO_SOURCE
)
val markerDesc = classDescriptor.getKSerializerConstructorMarker()
val markerType = markerDesc.toSimpleType(nullable = true)
val serializableProperties = bindingContext.serializablePropertiesFor(classDescriptor, metadataPlugin).serializableProperties
val parameterDescsAsProps = serializableProperties.map { it.descriptor }
val bitMaskSlotsCount = serializableProperties.bitMaskSlotCount()
var i = 0
val consParams = mutableListOf<ValueParameterDescriptor>()
repeat(bitMaskSlotsCount) {
consParams.add(
ValueParameterDescriptorImpl(
functionDescriptor, null, i++, Annotations.EMPTY, Name.identifier("seen$i"), functionDescriptor.builtIns.intType, false,
false, false, null, functionDescriptor.source
)
)
}
for (prop in parameterDescsAsProps) {
consParams.add(
ValueParameterDescriptorImpl(
functionDescriptor, null, i++, prop.annotations, prop.name, prop.type.makeNullableIfNotPrimitive(), false, false,
false, null, functionDescriptor.source
)
)
}
consParams.add(
ValueParameterDescriptorImpl(
functionDescriptor, null, i, Annotations.EMPTY, SerialEntityNames.dummyParamName, markerType, false,
false, false, null, functionDescriptor.source
)
)
functionDescriptor.initialize(
consParams,
DescriptorVisibilities.PUBLIC
)
functionDescriptor.returnType = classDescriptor.defaultType
return functionDescriptor
}
private fun createTypedSerializerConstructorDescriptor(
classDescriptor: ClassDescriptor,
serializableDescriptor: ClassDescriptor,
typeParameters: List<TypeParameterDescriptor>
): ClassConstructorDescriptor {
val constrDesc = ClassConstructorDescriptorImpl.createSynthesized(
classDescriptor,
Annotations.create(listOf(createDeprecatedHiddenAnnotation(classDescriptor.module))),
false,
classDescriptor.source
)
val serializerClass = classDescriptor.getClassFromSerializationPackage(SerialEntityNames.KSERIALIZER_CLASS)
assert(serializableDescriptor.declaredTypeParameters.size == typeParameters.size)
val args = List(serializableDescriptor.declaredTypeParameters.size) { index ->
val pType = KotlinTypeFactory.simpleNotNullType(
TypeAttributes.Empty,
serializerClass,
listOf(TypeProjectionImpl(typeParameters[index].defaultType))
)
ValueParameterDescriptorImpl(
constrDesc, null, index, Annotations.EMPTY, Name.identifier("$typeArgPrefix$index"), pType,
false, false, false, null, constrDesc.source
)
}
constrDesc.initialize(args, DescriptorVisibilities.PUBLIC, typeParameters)
constrDesc.returnType = classDescriptor.defaultType
return constrDesc
}
/**
* Creates free type parameters T0, T1, ... for given serializable class
* Returns [T0, T1, ...] and [KSerializer<T0>, KSerializer<T1>,...]
*/
private fun createKSerializerParamsForEachGenericArgument(
parentFunction: FunctionDescriptor,
serializableClass: ClassDescriptor,
actualArgsOffset: Int = 0
): Pair<List<TypeParameterDescriptor>, List<ValueParameterDescriptor>> {
val serializerClass = serializableClass.getClassFromSerializationPackage(SerialEntityNames.KSERIALIZER_CLASS)
val args = mutableListOf<ValueParameterDescriptor>()
val typeArgs = mutableListOf<TypeParameterDescriptor>()
var i = 0
serializableClass.declaredTypeParameters.forEach { _ ->
val targ = TypeParameterDescriptorImpl.createWithDefaultBound(
parentFunction, Annotations.EMPTY, false, Variance.INVARIANT,
Name.identifier("T$i"), i, LockBasedStorageManager.NO_LOCKS
)
val pType =
KotlinTypeFactory.simpleNotNullType(TypeAttributes.Empty, serializerClass, listOf(TypeProjectionImpl(targ.defaultType)))
args.add(
ValueParameterDescriptorImpl(
containingDeclaration = parentFunction,
original = null,
index = actualArgsOffset + i,
annotations = Annotations.EMPTY,
name = Name.identifier("$typeArgPrefix$i"),
outType = pType,
declaresDefaultValue = false,
isCrossinline = false,
isNoinline = false,
varargElementType = null,
source = parentFunction.source
)
)
typeArgs.add(targ)
i++
}
return typeArgs to args
}
private fun createSerializerFactoryVarargDescriptor(thisClass: ClassDescriptor): SimpleFunctionDescriptor {
val f = SimpleFunctionDescriptorImpl.create(
thisClass,
Annotations.EMPTY,
SerialEntityNames.SERIALIZER_PROVIDER_NAME,
CallableMemberDescriptor.Kind.SYNTHESIZED,
thisClass.source
)
val serializerClass = thisClass.getClassFromSerializationPackage(SerialEntityNames.KSERIALIZER_CLASS)
val kSerializerStarType =
KotlinTypeFactory.simpleNotNullType(
TypeAttributes.Empty,
serializerClass,
listOf(StarProjectionImpl(serializerClass.typeConstructor.parameters.first()))
)
val varargType = thisClass.builtIns.getArrayType(Variance.OUT_VARIANCE, kSerializerStarType)
val vararg = ValueParameterDescriptorImpl(
containingDeclaration = f,
original = null,
index = 0,
annotations = Annotations.EMPTY,
name = Name.identifier("typeParamsSerializers"),
outType = varargType,
declaresDefaultValue = false,
isCrossinline = false,
isNoinline = false,
varargElementType = kSerializerStarType,
source = f.source
)
f.initialize(
null,
thisClass.thisAsReceiverParameter,
emptyList(),
listOf(),
listOf(vararg),
kSerializerStarType,
Modality.FINAL,
DescriptorVisibilities.PUBLIC
)
return f
}
private fun createSerializerGetterDescriptor(
thisClass: ClassDescriptor,
serializableClass: ClassDescriptor
): SimpleFunctionDescriptor {
val f = SimpleFunctionDescriptorImpl.create(
thisClass,
Annotations.EMPTY,
SerialEntityNames.SERIALIZER_PROVIDER_NAME,
CallableMemberDescriptor.Kind.SYNTHESIZED,
thisClass.source
)
val serializerClass = thisClass.getClassFromSerializationPackage(SerialEntityNames.KSERIALIZER_CLASS)
val (typeArgs, args) = createKSerializerParamsForEachGenericArgument(f, serializableClass)
val newSerializableType =
KotlinTypeFactory.simpleNotNullType(TypeAttributes.Empty, serializableClass, typeArgs.map { TypeProjectionImpl(it.defaultType) })
val serialReturnType =
KotlinTypeFactory.simpleNotNullType(TypeAttributes.Empty, serializerClass, listOf(TypeProjectionImpl(newSerializableType)))
f.initialize(null, thisClass.thisAsReceiverParameter, emptyList(), typeArgs, args, serialReturnType, Modality.FINAL, DescriptorVisibilities.PUBLIC)
return f
}
private fun KotlinType.makeNullableIfNotPrimitive() =
if (KotlinBuiltIns.isPrimitiveType(this)) this
else this.makeNullable()
fun createWriteSelfFunctionDescriptor(thisClass: ClassDescriptor): SimpleFunctionDescriptor {
val jvmStaticClass = thisClass.module.findClassAcrossModuleDependencies(StandardClassIds.Annotations.JvmStatic)!!
val jvmStaticAnnotation = AnnotationDescriptorImpl(jvmStaticClass.defaultType, mapOf(), jvmStaticClass.source)
val annotations = Annotations.create(listOf(jvmStaticAnnotation))
val f = SimpleFunctionDescriptorImpl.create(
thisClass,
annotations,
SerialEntityNames.WRITE_SELF_NAME,
CallableMemberDescriptor.Kind.SYNTHESIZED,
thisClass.source
)
val returnType = f.builtIns.unitType
val (typeArgs, argsKSer) = createKSerializerParamsForEachGenericArgument(f, thisClass, actualArgsOffset = 3)
val args = mutableListOf<ValueParameterDescriptor>()
// object
val objectType =
KotlinTypeFactory.simpleNotNullType(TypeAttributes.Empty, thisClass, typeArgs.map { TypeProjectionImpl(it.defaultType) })
args.add(
ValueParameterDescriptorImpl(
containingDeclaration = f,
original = null,
index = 0,
annotations = Annotations.EMPTY,
name = Name.identifier("self"),
outType = objectType,
declaresDefaultValue = false,
isCrossinline = false,
isNoinline = false,
varargElementType = null,
source = f.source
)
)
// encoder
args.add(
ValueParameterDescriptorImpl(
containingDeclaration = f,
original = null,
index = 1,
annotations = Annotations.EMPTY,
name = Name.identifier("output"),
outType = thisClass.getClassFromSerializationPackage(SerialEntityNames.STRUCTURE_ENCODER_CLASS).toSimpleType(false),
declaresDefaultValue = false,
isCrossinline = false,
isNoinline = false,
varargElementType = null,
source = f.source
)
)
//descriptor
args.add(
ValueParameterDescriptorImpl(
containingDeclaration = f,
original = null,
index = 2,
annotations = Annotations.EMPTY,
name = Name.identifier("serialDesc"),
outType = thisClass.getClassFromSerializationPackage(SerialEntityNames.SERIAL_DESCRIPTOR_CLASS).toSimpleType(false),
declaresDefaultValue = false,
isCrossinline = false,
isNoinline = false,
varargElementType = null,
source = f.source
)
)
args.addAll(argsKSer)
f.initialize(
null,
null,
emptyList(),
typeArgs,
args,
returnType,
Modality.FINAL,
DescriptorVisibilities.PUBLIC
)
return f
}
fun generateDescriptorsForAnnotationImpl(
thisDescriptor: ClassDescriptor,
fromSupertypes: List<PropertyDescriptor>,
result: MutableCollection<PropertyDescriptor>
) {
if (isSerialInfoImpl(thisDescriptor)) {
result.add(
fromSupertypes.first().newCopyBuilder().apply {
setOwner(thisDescriptor)
setModality(Modality.FINAL)
setKind(CallableMemberDescriptor.Kind.SYNTHESIZED)
setDispatchReceiverParameter(thisDescriptor.thisAsReceiverParameter)
}.build()!!
)
}
}
// create properties typeSerial0, typeSerial1, etc... for storing generic arguments' serializers
private fun createLocalSerializersFieldsDescriptor(
name: Name,
serializableDescriptor: ClassDescriptor,
serializerDescriptor: ClassDescriptor
): List<PropertyDescriptor> {
if (serializableDescriptor.declaredTypeParameters.isEmpty()) return emptyList()
val serializerClass = serializableDescriptor.getClassFromSerializationPackage(SerialEntityNames.KSERIALIZER_CLASS)
val index = name.identifier.removePrefix(typeArgPrefix).toIntOrNull() ?: return emptyList()
val param = serializerDescriptor.declaredTypeParameters[index]
val pType =
KotlinTypeFactory.simpleNotNullType(
TypeAttributes.Empty,
serializerClass,
listOf(TypeProjectionImpl(param.defaultType))
)
val desc = doCreateSerializerProperty(serializerDescriptor, Name.identifier("$typeArgPrefix$index"), pType, needBackingField = true)
return listOf(desc)
}
}
@@ -0,0 +1,137 @@
/*
* 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.resolve
import org.jetbrains.kotlin.descriptors.*
import org.jetbrains.kotlin.descriptors.annotations.Annotated
import org.jetbrains.kotlin.descriptors.annotations.AnnotationDescriptor
import org.jetbrains.kotlin.descriptors.annotations.Annotations
import org.jetbrains.kotlin.name.ClassId
import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.psi.KtAnnotationEntry
import org.jetbrains.kotlin.psi.ValueArgument
import org.jetbrains.kotlin.resolve.constants.KClassValue
import org.jetbrains.kotlin.resolve.descriptorUtil.classId
import org.jetbrains.kotlin.resolve.descriptorUtil.module
import org.jetbrains.kotlin.resolve.lazy.descriptors.LazyAnnotationDescriptor
import org.jetbrains.kotlin.types.KotlinType
import org.jetbrains.kotlin.types.KotlinTypeFactory
import org.jetbrains.kotlin.types.TypeAttributes
fun ClassConstructorDescriptor.isSerializationCtor(): Boolean {
/*kind == CallableMemberDescriptor.Kind.SYNTHESIZED does not work because DeserializedClassConstructorDescriptor loses its kind*/
return valueParameters.lastOrNull()?.run {
name == SerialEntityNames.dummyParamName && type.constructor.declarationDescriptor?.classId == ClassId(
SerializationPackages.internalPackageFqName,
SerialEntityNames.SERIAL_CTOR_MARKER_NAME
)
} == true
}
// finds constructor (KSerializer<T0>, KSerializer<T1>...) on a KSerializer<T<T0, T1...>>
fun findSerializerConstructorForTypeArgumentsSerializers(
serializerDescriptor: ClassDescriptor,
onlyIfSynthetic: Boolean = false
): ClassConstructorDescriptor? {
val serializableImplementationTypeArguments = extractKSerializerArgumentFromImplementation(serializerDescriptor)?.arguments
?: throw AssertionError("Serializer does not implement KSerializer??")
val typeParamsCount = serializableImplementationTypeArguments.size
if (typeParamsCount == 0) return null //don't need it
val ctor = serializerDescriptor.constructors.find { ctor ->
ctor.valueParameters.size == typeParamsCount && ctor.valueParameters.all { isKSerializer(it.type) }
}
return if (!onlyIfSynthetic) ctor else ctor?.takeIf { it.kind == CallableMemberDescriptor.Kind.SYNTHESIZED }
}
fun AnnotationDescriptor.findAnnotationEntry(): KtAnnotationEntry? = (this as? LazyAnnotationDescriptor)?.annotationEntry
inline fun <reified R> Annotations.findAnnotationConstantValue(annotationFqName: FqName, property: String): R? =
findAnnotation(annotationFqName)?.findConstantValue(property)
inline fun <reified R> AnnotationDescriptor.findConstantValue(property: String): R? =
allValueArguments.entries.singleOrNull { it.key.asString() == property }?.value?.value as? R
fun Annotations.findAnnotationKotlinTypeValue(
annotationFqName: FqName,
moduleForResolve: ModuleDescriptor,
property: String
): KotlinType? =
findAnnotation(annotationFqName)?.let { annotation ->
val maybeKClass = annotation.allValueArguments.entries.singleOrNull { it.key.asString() == property }?.value as? KClassValue
maybeKClass?.getArgumentType(moduleForResolve)
}
fun ClassDescriptor.getKSerializerConstructorMarker(): ClassDescriptor =
module.findClassAcrossModuleDependencies(
ClassId(
SerializationPackages.internalPackageFqName,
SerialEntityNames.SERIAL_CTOR_MARKER_NAME
)
)!!
fun ClassDescriptor.getKSerializer(): ClassDescriptor =
module.findClassAcrossModuleDependencies(
ClassId(
SerializationPackages.packageFqName,
SerialEntityNames.KSERIALIZER_NAME
)
)!!
fun getInternalPackageFqn(classSimpleName: String): FqName =
SerializationPackages.internalPackageFqName.child(Name.identifier(classSimpleName))
fun ModuleDescriptor.getClassFromInternalSerializationPackage(classSimpleName: String) =
requireNotNull(
findClassAcrossModuleDependencies(
ClassId(
SerializationPackages.internalPackageFqName,
Name.identifier(classSimpleName)
)
)
) { "Can't locate class $classSimpleName from package ${SerializationPackages.internalPackageFqName}" }
fun ModuleDescriptor.getClassFromSerializationDescriptorsPackage(classSimpleName: String) =
requireNotNull(
findClassAcrossModuleDependencies(
ClassId(
SerializationPackages.descriptorsPackageFqName,
Name.identifier(classSimpleName)
)
)
) { "Can't locate class $classSimpleName from package ${SerializationPackages.descriptorsPackageFqName}" }
fun getSerializationPackageFqn(classSimpleName: String): FqName =
SerializationPackages.packageFqName.child(Name.identifier(classSimpleName))
fun ModuleDescriptor.getClassFromSerializationPackage(classSimpleName: String) =
SerializationPackages.allPublicPackages.firstNotNullOfOrNull { pkg ->
module.findClassAcrossModuleDependencies(ClassId(
pkg,
Name.identifier(classSimpleName)
))
} ?: throw IllegalArgumentException("Can't locate class $classSimpleName")
fun ClassDescriptor.getClassFromSerializationPackage(classSimpleName: String) =
module.getClassFromSerializationPackage(classSimpleName)
fun ClassDescriptor.getClassFromInternalSerializationPackage(classSimpleName: String) =
module.getClassFromInternalSerializationPackage(classSimpleName)
fun ClassDescriptor.toSimpleType(nullable: Boolean = false) =
KotlinTypeFactory.simpleType(TypeAttributes.Empty, this.typeConstructor, emptyList(), nullable)
fun Annotated.annotationsWithArguments(): List<Triple<ClassDescriptor, List<ValueArgument>, List<ValueParameterDescriptor>>> =
annotations.asSequence()
.filter { it.type.toClassDescriptor?.isSerialInfoAnnotation == true }
.filterIsInstance<LazyAnnotationDescriptor>()
.mapNotNull { annDesc ->
annDesc.type.toClassDescriptor?.let {
Triple(it, annDesc.annotationEntry.valueArguments, it.unsubstitutedPrimaryConstructor?.valueParameters.orEmpty())
}
}
.toList()
@@ -0,0 +1,169 @@
/*
* 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.resolve
import org.jetbrains.kotlin.descriptors.*
import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.psi.KtDeclarationWithInitializer
import org.jetbrains.kotlin.psi.KtParameter
import org.jetbrains.kotlin.resolve.BindingContext
import org.jetbrains.kotlin.resolve.descriptorUtil.getSuperClassNotAny
import org.jetbrains.kotlin.resolve.hasBackingField
import org.jetbrains.kotlin.resolve.scopes.DescriptorKindFilter
import org.jetbrains.kotlin.resolve.source.getPsi
import org.jetbrains.kotlin.serialization.deserialization.descriptors.DeserializedClassDescriptor
import org.jetbrains.kotlin.serialization.deserialization.descriptors.DeserializedPropertyDescriptor
import org.jetbrains.kotlin.serialization.deserialization.getName
import org.jetbrains.kotlinx.serialization.compiler.diagnostic.SERIALIZABLE_PROPERTIES
import org.jetbrains.kotlinx.serialization.compiler.extensions.SerializationDescriptorSerializerPlugin
import org.jetbrains.kotlinx.serialization.compiler.extensions.SerializationPluginMetadataExtensions
interface ISerializableProperties<S : ISerializableProperty> {
val serializableProperties: List<S>
val isExternallySerializable: Boolean
val serializableConstructorProperties: List<S>
val serializableStandaloneProperties: List<S>
}
class SerializableProperties(private val serializableClass: ClassDescriptor, val bindingContext: BindingContext) :
ISerializableProperties<SerializableProperty> {
private val primaryConstructorParameters: List<ValueParameterDescriptor> =
serializableClass.unsubstitutedPrimaryConstructor?.valueParameters ?: emptyList()
override val serializableProperties: List<SerializableProperty>
override val isExternallySerializable: Boolean
private val primaryConstructorProperties: Map<PropertyDescriptor, Boolean>
init {
val descriptorsSequence = serializableClass.unsubstitutedMemberScope.getContributedDescriptors(DescriptorKindFilter.VARIABLES)
.asSequence()
// call to any BindingContext.get should be only AFTER MemberScope.getContributedDescriptors
primaryConstructorProperties =
primaryConstructorParameters.asSequence()
.map { parameter -> bindingContext[BindingContext.VALUE_PARAMETER_AS_PROPERTY, parameter] to parameter.declaresDefaultValue() }
.mapNotNull { (a, b) -> if (a == null) null else a to b }
.toMap()
fun isPropSerializable(it: PropertyDescriptor) =
if (serializableClass.isInternalSerializable) !it.annotations.serialTransient
else !DescriptorVisibilities.isPrivate(it.visibility) && ((it.isVar && !it.annotations.serialTransient) || primaryConstructorProperties.contains(
it
))
serializableProperties = descriptorsSequence.filterIsInstance<PropertyDescriptor>()
.filter { it.kind == CallableMemberDescriptor.Kind.DECLARATION }
.filter(::isPropSerializable)
.map { prop ->
val declaresDefaultValue = prop.declaresDefaultValue()
SerializableProperty(
prop,
primaryConstructorProperties[prop] ?: false,
prop.hasBackingField(bindingContext) || (prop is DeserializedPropertyDescriptor && prop.backingField != null) // workaround for TODO in .hasBackingField
// workaround for overridden getter (val) and getter+setter (var) - in this case hasBackingField returning false
// but initializer presents only for property with backing field
|| declaresDefaultValue,
declaresDefaultValue
)
}
.filterNot { it.transient }
.partition { primaryConstructorProperties.contains(it.descriptor) }
.run {
val supers = serializableClass.getSuperClassNotAny()
if (supers == null || !supers.isInternalSerializable)
first + second
else
SerializableProperties(supers, bindingContext).serializableProperties + first + second
}
.let { restoreCorrectOrderFromClassProtoExtension(serializableClass, it) }
isExternallySerializable =
serializableClass.isInternallySerializableEnum() || primaryConstructorParameters.size == primaryConstructorProperties.size
}
override val serializableConstructorProperties: List<SerializableProperty> =
serializableProperties.asSequence()
.filter { primaryConstructorProperties.contains(it.descriptor) }
.toList()
override val serializableStandaloneProperties: List<SerializableProperty> =
serializableProperties.minus(serializableConstructorProperties)
val size = serializableProperties.size
operator fun get(index: Int) = serializableProperties[index]
operator fun iterator() = serializableProperties.iterator()
val primaryConstructorWithDefaults = serializableClass.unsubstitutedPrimaryConstructor
?.original?.valueParameters?.any { it.declaresDefaultValue() } ?: false
}
fun PropertyDescriptor.declaresDefaultValue(): Boolean {
when (val declaration = this.source.getPsi()) {
is KtDeclarationWithInitializer -> return declaration.initializer != null
is KtParameter -> return declaration.defaultValue != null
is Any -> return false // Not-null check
}
// PSI is null, property is from another module
if (this !is DeserializedPropertyDescriptor) return false
val myClassCtor = (this.containingDeclaration as? ClassDescriptor)?.unsubstitutedPrimaryConstructor ?: return false
// If property is a constructor parameter, check parameter default value
// (serializable classes always have parameters-as-properties, so no name clash here)
if (myClassCtor.valueParameters.find { it.name == this.name }?.declaresDefaultValue() == true) return true
// If it is a body property, then it is likely to have initializer when getter is not specified
// note this approach is not working well if we have smth like `get() = field`, but such cases on cross-module boundaries
// should be very marginal. If we want to solve them, we need to add protobuf metadata extension.
if (getter?.isDefault == true) return true
return false
}
val ISerializableProperties<*>.goldenMask: Int
get() {
var goldenMask = 0
var requiredBit = 1
for (property in serializableProperties) {
if (!property.optional) {
goldenMask = goldenMask or requiredBit
}
requiredBit = requiredBit shl 1
}
return goldenMask
}
val ISerializableProperties<*>.goldenMaskList: List<Int>
get() {
val maskSlotCount = serializableProperties.bitMaskSlotCount()
val goldenMaskList = MutableList(maskSlotCount) { 0 }
for (i in serializableProperties.indices) {
if (!serializableProperties[i].optional) {
val slotNumber = i / 32
val bitInSlot = i % 32
goldenMaskList[slotNumber] = goldenMaskList[slotNumber] or (1 shl bitInSlot)
}
}
return goldenMaskList
}
fun List<ISerializableProperty>.bitMaskSlotCount() = size / 32 + 1
fun bitMaskSlotAt(propertyIndex: Int) = propertyIndex / 32
fun BindingContext.serializablePropertiesFor(
classDescriptor: ClassDescriptor,
serializationDescriptorSerializer: SerializationDescriptorSerializerPlugin? = null
): SerializableProperties {
val props = this.get(SERIALIZABLE_PROPERTIES, classDescriptor) ?: SerializableProperties(classDescriptor, this)
serializationDescriptorSerializer?.putIfNeeded(classDescriptor, props)
return props
}
fun <P: ISerializableProperty> restoreCorrectOrderFromClassProtoExtension(descriptor: ClassDescriptor, props: List<P>): List<P> {
if (descriptor !is DeserializedClassDescriptor) return props
val correctOrder: List<Name> = descriptor.classProto.getExtension(SerializationPluginMetadataExtensions.propertiesNamesInProgramOrder)
.map { descriptor.c.nameResolver.getName(it) }
val propsMap = props.associateBy { it.originalDescriptorName }
return correctOrder.map { propsMap.getValue(it) }
}
@@ -0,0 +1,47 @@
/*
* 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.resolve
import org.jetbrains.kotlin.descriptors.PropertyDescriptor
import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.resolve.descriptorUtil.module
import org.jetbrains.kotlinx.serialization.compiler.backend.common.analyzeSpecialSerializers
interface ISerializableProperty {
val isConstructorParameterWithDefault: Boolean
val name: String
val originalDescriptorName: Name
val optional: Boolean
val transient: Boolean
}
class SerializableProperty(
val descriptor: PropertyDescriptor,
override val isConstructorParameterWithDefault: Boolean,
hasBackingField: Boolean,
declaresDefaultValue: Boolean
) : ISerializableProperty {
override val name = descriptor.annotations.serialNameValue ?: descriptor.name.asString()
override val originalDescriptorName: Name = descriptor.name
val type = descriptor.type
val genericIndex = type.genericIndex
val module = descriptor.module
val serializableWith = descriptor.serializableWith ?: analyzeSpecialSerializers(module, descriptor.annotations)?.defaultType
override val optional = !descriptor.annotations.serialRequired && declaresDefaultValue
override val transient = descriptor.annotations.serialTransient || !hasBackingField
}
@@ -0,0 +1,26 @@
description = "Kotlin Serialization Compiler Plugin (K2)"
plugins {
kotlin("jvm")
id("jps-compatible")
}
dependencies {
compileOnly(project(":compiler:fir:cones"))
compileOnly(project(":compiler:fir:tree"))
compileOnly(project(":compiler:fir:resolve"))
compileOnly(project(":compiler:fir:entrypoint"))
implementation(project(":kotlinx-serialization-compiler-plugin.common"))
compileOnly(intellijCore())
}
sourceSets {
"main" { projectDefault() }
"test" { none() }
}
runtimeJar()
sourcesJar()
javadocJar()
@@ -0,0 +1,74 @@
/*
* 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.fir
import org.jetbrains.kotlin.GeneratedDeclarationKey
import org.jetbrains.kotlin.fir.FirSession
import org.jetbrains.kotlin.fir.containingClassForStaticMemberAttr
import org.jetbrains.kotlin.fir.declarations.*
import org.jetbrains.kotlin.fir.declarations.builder.buildPrimaryConstructor
import org.jetbrains.kotlin.fir.declarations.builder.buildTypeParameter
import org.jetbrains.kotlin.fir.declarations.builder.buildValueParameter
import org.jetbrains.kotlin.fir.declarations.utils.addDefaultBoundIfNecessary
import org.jetbrains.kotlin.fir.extensions.FirDeclarationGenerationExtension
import org.jetbrains.kotlin.fir.moduleData
import org.jetbrains.kotlin.fir.resolve.defaultType
import org.jetbrains.kotlin.fir.resolve.providers.symbolProvider
import org.jetbrains.kotlin.fir.symbols.FirBasedSymbol
import org.jetbrains.kotlin.fir.symbols.SymbolInternals
import org.jetbrains.kotlin.fir.symbols.impl.*
import org.jetbrains.kotlin.fir.types.FirResolvedTypeRef
import org.jetbrains.kotlin.fir.types.toFirResolvedTypeRef
import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.types.Variance
// FIXME KT-53096: this has to be shared (copied from plugin example)
@OptIn(SymbolInternals::class)
fun FirDeclarationGenerationExtension.buildPrimaryConstructor(owner: FirClassSymbol<*>, isInner: Boolean, key: GeneratedDeclarationKey, status: FirDeclarationStatus): FirConstructor {
val classId = owner.classId
val lookupTag = ConeClassLikeLookupTagImpl(classId)
return buildPrimaryConstructor {
moduleData = session.moduleData
origin = key.origin
returnTypeRef = run {
owner.defaultType().toFirResolvedTypeRef()
}
this.status = status
symbol = FirConstructorSymbol(classId)
if (isInner && classId.isNestedClass) {
dispatchReceiverType = classId.parentClassId?.let {
val firClass = session.symbolProvider.getClassLikeSymbolByClassId(it)?.fir as? FirClass
firClass?.defaultType()
}
}
}.also {
it.containingClassForStaticMemberAttr = lookupTag
}
}
fun newSimpleTypeParameter(firSession: FirSession, containingDeclarationSymbol: FirBasedSymbol<*>, name: Name) = buildTypeParameter {
moduleData = firSession.moduleData
origin = SerializationPluginKey.origin
resolvePhase = FirResolvePhase.BODY_RESOLVE
variance = Variance.INVARIANT
this.name = name
symbol = FirTypeParameterSymbol()
this.containingDeclarationSymbol = containingDeclarationSymbol
isReified = false
addDefaultBoundIfNecessary()
}
fun newSimpleValueParameter(firSession: FirSession, typeRef: FirResolvedTypeRef, name: Name) = buildValueParameter {
moduleData = firSession.moduleData
origin = SerializationPluginKey.origin
this.name = name
this.symbol = FirValueParameterSymbol(this.name)
returnTypeRef = typeRef
isCrossinline = false
isNoinline = false
isVararg = false
}
@@ -0,0 +1,15 @@
/*
* 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.fir
import org.jetbrains.kotlin.fir.extensions.FirExtensionRegistrar
class FirSerializationExtensionRegistrar : FirExtensionRegistrar() {
override fun ExtensionRegistrarContext.configurePlugin() {
+::SerializationFirResolveExtension
+::SerializationFirSupertypesExtension
}
}
@@ -0,0 +1,19 @@
/*
* 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.fir
import org.jetbrains.kotlin.fir.extensions.predicate.AnnotatedWith
import org.jetbrains.kotlin.fir.extensions.predicate.DeclarationPredicate
import org.jetbrains.kotlin.fir.extensions.predicate.ancestorAnnotated
import org.jetbrains.kotlinx.serialization.compiler.resolve.SerializationAnnotations
object FirSerializationPredicates {
internal val serializerFor: DeclarationPredicate =
AnnotatedWith(setOf(SerializationAnnotations.serializerAnnotationFqName)) // @Serializer(for=...)
internal val generatedSerializer: DeclarationPredicate =
ancestorAnnotated(SerializationAnnotations.serializableAnnotationFqName) // @Serializable X.$serializer
internal val annotatedWithSerializable = AnnotatedWith(setOf(SerializationAnnotations.serializableAnnotationFqName))
}
@@ -0,0 +1,332 @@
/*
* 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.fir
import org.jetbrains.kotlin.descriptors.ClassKind
import org.jetbrains.kotlin.descriptors.EffectiveVisibility
import org.jetbrains.kotlin.descriptors.Modality
import org.jetbrains.kotlin.descriptors.Visibilities
import org.jetbrains.kotlin.fir.FirSession
import org.jetbrains.kotlin.fir.copy
import org.jetbrains.kotlin.fir.declarations.FirDeclarationOrigin
import org.jetbrains.kotlin.fir.declarations.builder.*
import org.jetbrains.kotlin.fir.declarations.impl.FirResolvedDeclarationStatusImpl
import org.jetbrains.kotlin.fir.declarations.origin
import org.jetbrains.kotlin.fir.declarations.utils.superConeTypes
import org.jetbrains.kotlin.fir.extensions.FirDeclarationGenerationExtension
import org.jetbrains.kotlin.fir.extensions.FirDeclarationPredicateRegistrar
import org.jetbrains.kotlin.fir.extensions.MemberGenerationContext
import org.jetbrains.kotlin.fir.extensions.predicateBasedProvider
import org.jetbrains.kotlin.fir.moduleData
import org.jetbrains.kotlin.fir.resolve.*
import org.jetbrains.kotlin.fir.resolve.providers.symbolProvider
import org.jetbrains.kotlin.fir.scopes.*
import org.jetbrains.kotlin.fir.scopes.impl.toConeType
import org.jetbrains.kotlin.fir.symbols.SymbolInternals
import org.jetbrains.kotlin.fir.symbols.impl.*
import org.jetbrains.kotlin.fir.types.*
import org.jetbrains.kotlin.fir.types.builder.buildResolvedTypeRef
import org.jetbrains.kotlin.name.CallableId
import org.jetbrains.kotlin.name.ClassId
import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.name.SpecialNames
import org.jetbrains.kotlinx.serialization.compiler.resolve.SerialEntityNames
import org.jetbrains.kotlinx.serialization.compiler.resolve.SerializationPackages
val generatedSerializerClassId = ClassId(SerializationPackages.internalPackageFqName, SerialEntityNames.GENERATED_SERIALIZER_CLASS)
val kSerializerClassId = ClassId(SerializationPackages.packageFqName, SerialEntityNames.KSERIALIZER_NAME)
class SerializationFirResolveExtension(session: FirSession) : FirDeclarationGenerationExtension(session) {
internal val runtimeHasEnumSerializerFactory by lazy {
val hasFactory = session.symbolProvider.getTopLevelCallableSymbols(
SerializationPackages.internalPackageFqName,
SerialEntityNames.ENUM_SERIALIZER_FACTORY_FUNC_NAME
).isNotEmpty()
val hasMarkedFactory = session.symbolProvider.getTopLevelCallableSymbols(
SerializationPackages.internalPackageFqName,
SerialEntityNames.MARKED_ENUM_SERIALIZER_FACTORY_FUNC_NAME
).isNotEmpty()
hasFactory && hasMarkedFactory
}
internal val FirClassSymbol<*>.shouldHaveGeneratedSerializer: Boolean
get() = (isInternalSerializable && isFinalOrOpen()) || (classKind == ClassKind.ENUM_CLASS && hasSerializableAnnotationWithoutArgs && !runtimeHasEnumSerializerFactory)
override fun getNestedClassifiersNames(classSymbol: FirClassSymbol<*>): Set<Name> {
val result = mutableSetOf<Name>()
if (classSymbol.shouldHaveGeneratedMethodsInCompanion && !classSymbol.isSerializableObject)
result += SpecialNames.DEFAULT_NAME_FOR_COMPANION_OBJECT
if (classSymbol.shouldHaveGeneratedSerializer /* TODO && !classSymbol.hasCompanionObjectAsSerializer*/)
result += SerialEntityNames.SERIALIZER_CLASS_NAME
return result
}
override fun generateClassLikeDeclaration(classId: ClassId): FirClassLikeSymbol<*>? {
return when (classId.shortClassName) {
SpecialNames.DEFAULT_NAME_FOR_COMPANION_OBJECT -> {
generateCompanionDeclaration(classId)
}
SerialEntityNames.SERIALIZER_CLASS_NAME -> {
addSerializerImplClass(classId)
}
else -> error("Can't generate class ${classId.asSingleFqName()}")
}
}
override fun getCallableNamesForClass(classSymbol: FirClassSymbol<*>): Set<Name> {
val classId = classSymbol.classId
val result = mutableSetOf<Name>()
when (classId.shortClassName) {
SpecialNames.DEFAULT_NAME_FOR_COMPANION_OBJECT -> {
val origin = classSymbol.origin as? FirDeclarationOrigin.Plugin
if (origin?.key == SerializationPluginKey) {
result += SpecialNames.INIT
result += SerialEntityNames.SERIALIZER_PROVIDER_NAME
} else {
// TODO: handle user-written companions & named companions
}
}
SerialEntityNames.SERIALIZER_CLASS_NAME -> {
// TODO: check classSymbol for already added functions
// TODO: support user-defined serializers?
result += setOf(
SpecialNames.INIT,
SerialEntityNames.SAVE_NAME,
SerialEntityNames.LOAD_NAME,
SerialEntityNames.SERIAL_DESC_FIELD_NAME
)
if (classSymbol.superConeTypes.any {
it.classId == ClassId(
SerializationPackages.internalPackageFqName,
SerialEntityNames.GENERATED_SERIALIZER_CLASS
)
}) {
result += SerialEntityNames.CHILD_SERIALIZERS_GETTER
if (classSymbol.typeParameterSymbols.isNotEmpty()) {
result += SerialEntityNames.TYPE_PARAMS_SERIALIZERS_GETTER
}
}
}
else -> if (classSymbol.isSerializableObject) result += SerialEntityNames.SERIALIZER_PROVIDER_NAME
}
return result
}
@OptIn(SymbolInternals::class)
private fun <T> getFromSupertype(callableId: CallableId, owner: FirClassSymbol<*>, extractor: (FirTypeScope) -> List<T>): T {
val scopeSession = ScopeSession()
val scopes = lookupSuperTypes(
owner, lookupInterfaces = true, deep = false, useSiteSession = session
).mapNotNull { useSiteSuperType ->
useSiteSuperType.scopeForSupertype(session, scopeSession, owner.fir)
}
val targets = scopes.flatMap { extractor(it) }
val target = targets.singleOrNull() ?: error("Multiple overrides found for ${callableId.callableName}")
return target
}
// TODO: support @Serializer(for)
@OptIn(SymbolInternals::class)
override fun generateFunctions(callableId: CallableId, context: MemberGenerationContext?): List<FirNamedFunctionSymbol> {
val owner = context?.owner ?: return emptyList()
if (callableId.callableName == SerialEntityNames.SERIALIZER_PROVIDER_NAME) {
val serializableClass = session.getSerializableClassDescriptorByCompanion(owner) ?: return emptyList()
return listOf(generateSerializerGetterInCompanion(owner, serializableClass, callableId))
}
if (owner.name != SerialEntityNames.SERIALIZER_CLASS_NAME) return emptyList()
if (callableId.callableName !in setOf(
SpecialNames.INIT,
SerialEntityNames.SAVE_NAME,
SerialEntityNames.LOAD_NAME,
SerialEntityNames.CHILD_SERIALIZERS_GETTER,
SerialEntityNames.TYPE_PARAMS_SERIALIZERS_GETTER
)
) return emptyList()
val target = getFromSupertype(callableId, owner) { it.getFunctions(callableId.callableName) }
val original = target.fir
val copy = buildSimpleFunctionCopy(original) {
symbol = FirNamedFunctionSymbol(callableId)
origin = SerializationPluginKey.origin
status = original.status.copy(modality = Modality.FINAL)
}
return listOf(copy.symbol)
}
private fun generateSerializerGetterInCompanion(
owner: FirClassSymbol<*>,
serializableClassSymbol: FirClassSymbol<*>,
callableId: CallableId
): FirNamedFunctionSymbol {
val f = buildSimpleFunction {
moduleData = session.moduleData
symbol = FirNamedFunctionSymbol(callableId)
origin = SerializationPluginKey.origin
status = FirResolvedDeclarationStatusImpl(
Visibilities.Public,
Modality.FINAL,
EffectiveVisibility.Public
)
name = callableId.callableName
dispatchReceiverType = owner.defaultType()
typeParameters.addAll(serializableClassSymbol.typeParameterSymbols.map { newSimpleTypeParameter(session, symbol, it.name) })
val parametersAsArguments = typeParameters.map { it.toConeType() }.toTypedArray<ConeTypeProjection>()
valueParameters.addAll(List(serializableClassSymbol.typeParameterSymbols.size) { i ->
newSimpleValueParameter(
session,
kSerializerClassId.constructClassLikeType(arrayOf(parametersAsArguments[i]), false).toFirResolvedTypeRef(),
Name.identifier("${SerialEntityNames.typeArgPrefix}$i")
)
})
returnTypeRef = buildResolvedTypeRef {
type = kSerializerClassId.constructClassLikeType(
arrayOf(serializableClassSymbol.constructType(parametersAsArguments, false)),
isNullable = false
)
}
}
return f.symbol
}
@OptIn(SymbolInternals::class)
override fun generateProperties(callableId: CallableId, context: MemberGenerationContext?): List<FirPropertySymbol> {
val owner = context?.owner ?: return emptyList()
if (owner.name != SerialEntityNames.SERIALIZER_CLASS_NAME) return emptyList()
if (callableId.callableName != SerialEntityNames.SERIAL_DESC_FIELD_NAME) return emptyList()
val target = getFromSupertype(callableId, owner) { it.getProperties(callableId.callableName).filterIsInstance<FirPropertySymbol>() }
val original = target.fir
val copy = buildPropertyCopy(original) {
symbol = FirPropertySymbol(callableId)
origin = SerializationPluginKey.origin
status = original.status.copy(modality = Modality.FINAL)
getter = buildPropertyAccessor {
status = original.status.copy(modality = Modality.FINAL)
symbol = FirPropertyAccessorSymbol()
origin = SerializationPluginKey.origin
moduleData = session.moduleData
isGetter = true
returnTypeRef = original.returnTypeRef
dispatchReceiverType = owner.defaultType()
propertySymbol = this@buildPropertyCopy.symbol
}
}
return listOf(copy.symbol)
}
// FIXME: it seems that this list will always be used, why not provide it automatically?
private val matchedClasses by lazy {
session.predicateBasedProvider.getSymbolsByPredicate(FirSerializationPredicates.annotatedWithSerializable)
.filterIsInstance<FirRegularClassSymbol>()
}
override fun generateConstructors(context: MemberGenerationContext): List<FirConstructorSymbol> {
val owner = context.owner
val defaultObjectConstructor = buildPrimaryConstructor(
owner, isInner = false, SerializationPluginKey, status = FirResolvedDeclarationStatusImpl(
Visibilities.Private,
Modality.FINAL,
EffectiveVisibility.PrivateInClass
)
)
if (owner.name == SerialEntityNames.SERIALIZER_CLASS_NAME && owner.typeParameterSymbols.isNotEmpty()) {
val parameterizedConstructor = buildConstructor {
moduleData = session.moduleData
origin = SerializationPluginKey.origin
returnTypeRef = defaultObjectConstructor.returnTypeRef
symbol = FirConstructorSymbol(owner.classId)
dispatchReceiverType = defaultObjectConstructor.dispatchReceiverType
status = FirResolvedDeclarationStatusImpl(
Visibilities.Private,
Modality.FINAL,
EffectiveVisibility.PrivateInFile // accessed from a companion
)
valueParameters.addAll(owner.typeParameterSymbols.mapIndexed { i, typeParam ->
newSimpleValueParameter(
session,
kSerializerClassId.constructClassLikeType(arrayOf(typeParam.toConeType()), false).toFirResolvedTypeRef(),
Name.identifier("${SerialEntityNames.typeArgPrefix}$i")
)
})
}
return listOf(defaultObjectConstructor.symbol, parameterizedConstructor.symbol)
}
return listOf(defaultObjectConstructor.symbol)
}
fun addSerializerImplClass(
classId: ClassId
): FirClassLikeSymbol<*>? {
val owner = matchedClasses.firstOrNull { it.classId == classId.outerClassId } ?: return null
val hasTypeParams = owner.typeParameterSymbols.isNotEmpty()
val serializerKind = if (hasTypeParams) ClassKind.CLASS else ClassKind.OBJECT
val serializerFirClass = buildRegularClass {
moduleData = session.moduleData
origin = SerializationPluginKey.origin
classKind = serializerKind
scopeProvider = session.kotlinScopeProvider
name = SerialEntityNames.SERIALIZER_CLASS_NAME
symbol = FirRegularClassSymbol(classId)
status = FirResolvedDeclarationStatusImpl(
Visibilities.Public,
Modality.FINAL,
EffectiveVisibility.Public
)
// TODO: add deprecate hidden
// annotations = listOf(Annotations.create(listOf(KSerializerDescriptorResolver.createDeprecatedHiddenAnnotation(thisDescriptor.module))))
typeParameters.addAll(owner.typeParameterSymbols.map { param ->
newSimpleTypeParameter(session, symbol, param.name)
})
val parametersAsArguments = typeParameters.map { it.toConeType() }.toTypedArray<ConeTypeProjection>()
superTypeRefs += generatedSerializerClassId.constructClassLikeType(
arrayOf(
owner.constructType(
parametersAsArguments,
isNullable = false
)
), isNullable = false
).toFirResolvedTypeRef()
}
return serializerFirClass.symbol
}
fun generateCompanionDeclaration(classId: ClassId): FirClassLikeSymbol<*>? {
if (classId.shortClassName != SpecialNames.DEFAULT_NAME_FOR_COMPANION_OBJECT) return null
val owner = matchedClasses.firstOrNull { it.classId == classId.outerClassId } ?: return null
if (owner.companionObjectSymbol != null) return null
val regularClass = buildRegularClass {
moduleData = session.moduleData
origin = SerializationPluginKey.origin
classKind = ClassKind.OBJECT
scopeProvider = session.kotlinScopeProvider
status = FirResolvedDeclarationStatusImpl(
Visibilities.Public,
Modality.FINAL,
EffectiveVisibility.Public
).apply {
isCompanion = true
}
name = SpecialNames.DEFAULT_NAME_FOR_COMPANION_OBJECT
symbol = FirRegularClassSymbol(classId)
superTypeRefs += session.builtinTypes.anyType
}
return regularClass.symbol
}
override fun FirDeclarationPredicateRegistrar.registerPredicates() {
register(FirSerializationPredicates.annotatedWithSerializable)
}
}
@@ -0,0 +1,42 @@
/*
* 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.fir
import org.jetbrains.kotlin.fir.FirSession
import org.jetbrains.kotlin.fir.declarations.FirClassLikeDeclaration
import org.jetbrains.kotlin.fir.extensions.FirDeclarationPredicateRegistrar
import org.jetbrains.kotlin.fir.extensions.FirSupertypeGenerationExtension
import org.jetbrains.kotlin.fir.extensions.predicateBasedProvider
import org.jetbrains.kotlin.fir.types.FirResolvedTypeRef
import org.jetbrains.kotlin.fir.types.classId
import org.jetbrains.kotlin.name.ClassId
import org.jetbrains.kotlinx.serialization.compiler.fir.FirSerializationPredicates.serializerFor
import org.jetbrains.kotlinx.serialization.compiler.resolve.SerialEntityNames
import org.jetbrains.kotlinx.serialization.compiler.resolve.SerializationPackages
class SerializationFirSupertypesExtension(session: FirSession) : FirSupertypeGenerationExtension(session) {
override fun needTransformSupertypes(declaration: FirClassLikeDeclaration): Boolean =
session.predicateBasedProvider.matches(serializerFor, declaration)
override fun FirDeclarationPredicateRegistrar.registerPredicates() {
register(serializerFor)
}
override fun computeAdditionalSupertypes(
classLikeDeclaration: FirClassLikeDeclaration,
resolvedSupertypes: List<FirResolvedTypeRef>
): List<FirResolvedTypeRef> {
val kSerializerClassId = ClassId(SerializationPackages.packageFqName, SerialEntityNames.KSERIALIZER_NAME)
val generatedSerializerClassId = ClassId(SerializationPackages.internalPackageFqName, SerialEntityNames.GENERATED_SERIALIZER_CLASS)
if (resolvedSupertypes.any { it.type.classId == kSerializerClassId || it.type.classId == generatedSerializerClassId }) return emptyList()
return if (session.predicateBasedProvider.matches(serializerFor, classLikeDeclaration)) {
TODO("Support @Serializer(for=...) supertype generation")
} else emptyList()
}
}
@@ -0,0 +1,65 @@
/*
* 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.fir
import org.jetbrains.kotlin.descriptors.ClassKind
import org.jetbrains.kotlin.descriptors.Modality
import org.jetbrains.kotlin.fir.FirSession
import org.jetbrains.kotlin.fir.analysis.checkers.getContainingDeclarationSymbol
import org.jetbrains.kotlin.fir.declarations.findArgumentByName
import org.jetbrains.kotlin.fir.declarations.utils.isCompanion
import org.jetbrains.kotlin.fir.expressions.classId
import org.jetbrains.kotlin.fir.symbols.impl.FirClassSymbol
import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlinx.serialization.compiler.resolve.SerializationAnnotations
internal val FirClassSymbol<*>.hasSerializableAnnotation
get() = this.annotations.any {
it.classId?.asSingleFqName()?.asString() == SerializationAnnotations.serializableAnnotationFqName.asString()
}
internal val FirClassSymbol<*>.hasSerializableAnnotationWithoutArgs: Boolean
get() {
if (!hasSerializableAnnotation) return false
val target = this.resolvedAnnotationsWithArguments.find {
it.classId?.asSingleFqName()?.asString() == SerializationAnnotations.serializableAnnotationFqName.asString()
}!!
return target.findArgumentByName(Name.identifier("with")) == null
}
internal val FirClassSymbol<*>.shouldHaveGeneratedMethodsInCompanion: Boolean
get() = this.isSerializableObject || this.isSerializableEnum() || this.classKind == ClassKind.CLASS && hasSerializableAnnotation || this.isSealedSerializableInterface
internal val FirClassSymbol<*>.isSerializableObject: Boolean
get() = classKind == ClassKind.OBJECT && hasSerializableAnnotation
internal val FirClassSymbol<*>.isInternallySerializableObject: Boolean
get() = classKind == ClassKind.OBJECT && hasSerializableAnnotationWithoutArgs
internal val FirClassSymbol<*>.isSealedSerializableInterface: Boolean
get() = classKind == ClassKind.INTERFACE && rawStatus.modality == Modality.SEALED && hasSerializableAnnotation
internal val FirClassSymbol<*>.isInternalSerializable: Boolean
get() {
if (classKind != ClassKind.CLASS) return false
return hasSerializableAnnotationWithoutArgs
}
internal fun FirClassSymbol<*>.isSerializableEnum(): Boolean = classKind == ClassKind.ENUM_CLASS && hasSerializableAnnotation
internal fun FirClassSymbol<*>.isFinalOrOpen(): Boolean {
val modality = rawStatus.modality
// null means default modality, final
return (modality == null || modality == Modality.FINAL || modality == Modality.OPEN)
}
internal fun FirSession.getSerializableClassDescriptorByCompanion(thisDescriptor: FirClassSymbol<*>): FirClassSymbol<*>? {
if (thisDescriptor.isSerializableObject) return thisDescriptor
if (!thisDescriptor.isCompanion) return null
val classDescriptor = (thisDescriptor.getContainingDeclarationSymbol(this) as? FirClassSymbol<*>) ?: return null
if (!classDescriptor.shouldHaveGeneratedMethodsInCompanion) return null
return classDescriptor
}
@@ -0,0 +1,78 @@
// IGNORE_BACKEND_FIR: JVM_IR
// TARGET_BACKEND: JVM_IR
// WITH_STDLIB
// FILE: a.kt
package a
import kotlinx.serialization.*
import kotlinx.serialization.descriptors.*
import kotlinx.serialization.encoding.*
object MultiplyingIntSerializer : KSerializer<Int> {
override val descriptor: SerialDescriptor
get() = PrimitiveSerialDescriptor("MultiplyingInt", PrimitiveKind.INT)
override fun deserialize(decoder: Decoder): Int {
return decoder.decodeInt() / 2
}
override fun serialize(encoder: Encoder, value: Int) {
encoder.encodeInt(value * 2)
}
}
data class Cont(val i: Int)
object ContSerializer: KSerializer<Cont> {
override fun deserialize(decoder: Decoder): Cont {
return Cont(decoder.decodeInt())
}
override val descriptor: SerialDescriptor = PrimitiveSerialDescriptor("ContSerializer", PrimitiveKind.INT)
override fun serialize(encoder: Encoder, value: Cont) {
encoder.encodeInt(value.i)
}
}
// FILE: test.kt
@file:UseContextualSerialization(Cont::class)
@file:UseSerializers(MultiplyingIntSerializer::class)
package a
import kotlinx.serialization.*
import kotlinx.serialization.json.*
import kotlinx.serialization.modules.*
@Serializable
class Holder(
val i: Int,
val c: Cont
)
fun testOnFile(): String {
val j = Json {
serializersModule = SerializersModule {
contextual(ContSerializer)
}
}
val h = Holder(3, Cont(4))
val str = j.encodeToString(
Holder.serializer(),
h
)
if ("""{"i":6,"c":4}""" != str) return str
val decoded = j.decodeFromString(Holder.serializer(), str)
if (decoded.i != h.i) return "i: ${decoded.i}"
if (decoded.c.i != h.c.i) return "c.i: ${decoded.c.i}"
return "OK"
}
fun box(): String {
return testOnFile()
}
@@ -0,0 +1,68 @@
// IGNORE_BACKEND_FIR: JVM_IR
// TARGET_BACKEND: JVM_IR
// WITH_STDLIB
package a
import kotlinx.serialization.*
import kotlinx.serialization.descriptors.*
import kotlinx.serialization.encoding.*
import kotlin.test.assertEquals
class Dummy
class DummyBox<T>
@Serializable(ClassSerializerOnClass::class)
class DummySpecified
class ClassSerializerGeneric : KSerializer<DummyBox<String>> {
override val descriptor get() = PrimitiveSerialDescriptor("ClassSerializerGeneric", PrimitiveKind.INT)
override fun deserialize(decoder: Decoder): DummyBox<String> = TODO()
override fun serialize(encoder: Encoder, value:DummyBox<String>): Unit = TODO()
}
class ClassSerializerDummy : KSerializer<Dummy> {
override val descriptor get() = PrimitiveSerialDescriptor("ClassSerializerDummy", PrimitiveKind.INT)
override fun deserialize(decoder: Decoder): Dummy = TODO()
override fun serialize(encoder: Encoder, value: Dummy): Unit = TODO()
}
object ObjectSerializerGeneric: KSerializer<DummyBox<String>> {
override val descriptor get() = PrimitiveSerialDescriptor("ObjectSerializerGeneric", PrimitiveKind.INT)
override fun deserialize(decoder: Decoder): DummyBox<String> = TODO()
override fun serialize(encoder: Encoder, value: DummyBox<String>): Unit = TODO()
}
object ObjectSerializerDummy: KSerializer<Dummy> {
override val descriptor get() = PrimitiveSerialDescriptor("ObjectSerializerDummy", PrimitiveKind.INT)
override fun deserialize(decoder: Decoder): Dummy = TODO()
override fun serialize(encoder: Encoder, value:Dummy): Unit = TODO()
}
class ClassSerializerOnClass: KSerializer<DummySpecified> {
override val descriptor get() = PrimitiveSerialDescriptor("ClassSerializerOnClass", PrimitiveKind.INT)
override fun deserialize(decoder: Decoder): DummySpecified = TODO()
override fun serialize(encoder: Encoder, value:DummySpecified): Unit = TODO()
}
@Serializable
class Holder(
@Serializable(ClassSerializerGeneric::class) val a: DummyBox<String>,
@Serializable(ClassSerializerDummy::class) val b: Dummy,
@Serializable(ObjectSerializerGeneric::class) val c: DummyBox<String>,
@Serializable(ObjectSerializerDummy::class) val d: Dummy,
val e: DummySpecified
)
fun box(): String {
val descs = Holder.serializer().descriptor.elementDescriptors.toList()
assertEquals("ClassSerializerGeneric", descs[0].serialName)
assertEquals("ClassSerializerDummy", descs[1].serialName)
assertEquals("ObjectSerializerGeneric", descs[2].serialName)
assertEquals("ObjectSerializerDummy", descs[3].serialName)
assertEquals("ClassSerializerOnClass", descs[4].serialName)
return "OK"
}
@@ -0,0 +1,48 @@
// IGNORE_BACKEND_FIR: JVM_IR
// TARGET_BACKEND: JVM_IR
// WITH_STDLIB
import kotlinx.serialization.*
import kotlinx.serialization.json.*
import kotlinx.serialization.internal.*
enum class Plain {
A, B
}
@Serializable enum class WithNames {
@SerialName("A") ENTRY1,
@SerialName("B") ENTRY2
}
@Serializable
class Holder(val p: Plain, val w: WithNames)
@OptIn(InternalSerializationApi::class)
fun testSerializers(): String {
val cs = (Holder.serializer() as GeneratedSerializer<*>).childSerializers()
val str1 = cs[0].toString()
if (!str1.contains("kotlinx.serialization.internal.EnumSerializer")) return str1
/**
* Serialization 1.4.1+ have runtime factories to create EnumSerializer instead of synthetic $serializer, saving bytecode
* and bringing consistency. After updating the version, uncomment this block.
*/
// val str2 = cs[1].toString()
// if (!str2.contains("kotlinx.serialization.internal.EnumSerializer")) return str2
return "OK"
}
fun testSerialization(previous: String): String {
if (previous != "OK") return previous
val h = Holder(Plain.B, WithNames.ENTRY1)
val s = Json.encodeToString(Holder.serializer(), h)
if (s != """{"p":"B","w":"A"}""") return s
if (Json.decodeFromString(Holder.serializer(), s).w != WithNames.ENTRY1) return "Deserialization failure"
return "OK"
}
fun box(): String {
return testSerialization(testSerializers())
}
@@ -0,0 +1,22 @@
// IGNORE_BACKEND_FIR: JVM_IR
// TARGET_BACKEND: JVM_IR
// WITH_STDLIB
import kotlinx.serialization.*
import kotlinx.serialization.json.*
@Serializable
data class Foo<T>(val i: Int, val t: T? = null)
@Serializable
class Holder(val f1: Foo<String>, val f2: Foo<Int>)
fun box(): String {
val holder = Holder(Foo(1, "1"), Foo(2))
val str = Json.encodeToString(Holder.serializer(), holder)
if (str != """{"f1":{"i":1,"t":"1"},"f2":{"i":2}}""") return str
val decoded = Json.decodeFromString(Holder.serializer(), str)
if (decoded.f1.t != holder.f1.t) return "f1.t: ${decoded.f1.t}"
return "OK"
}
@@ -0,0 +1,22 @@
// IGNORE_BACKEND_FIR: JVM_IR
// TARGET_BACKEND: JVM_IR
// WITH_STDLIB
import kotlinx.serialization.*
import kotlinx.serialization.json.*
import kotlinx.serialization.internal.*
@Serializable
@JvmInline
value class Foo(val i: Int)
@Serializable
class Holder(val f: Foo)
fun box(): String {
if(!Foo.serializer().descriptor.isInline) return "Incorrect descriptor"
val s = Json.encodeToString(Holder.serializer(), Holder(Foo(42)))
if (s != """{"f":42}""") return s
return "OK"
}
@@ -0,0 +1,185 @@
// IGNORE_BACKEND_FIR: JVM_IR
// TARGET_BACKEND: JVM_IR
// WITH_STDLIB
import kotlinx.serialization.*
import kotlinx.serialization.encoding.*
import kotlinx.serialization.descriptors.*
import kotlinx.serialization.json.*
import kotlin.reflect.KClass
import kotlin.test.*
// TODO: for this test to work, runtime dependency should be updated to (yet unreleased) serialization with @MetaSerializable annotation
@MetaSerializable
@Target(AnnotationTarget.CLASS, AnnotationTarget.PROPERTY)
annotation class MySerializable
@MetaSerializable
@Target(AnnotationTarget.CLASS, AnnotationTarget.PROPERTY)
annotation class MySerializableWithInfo(
val value: Int,
val kclass: KClass<*>
)
@MySerializable
class Project1(val name: String, val language: String)
@MySerializableWithInfo(123, String::class)
class Project2(val name: String, val language: String)
@Serializable
class Wrapper(
@MySerializableWithInfo(234, Int::class) val project: Project2
)
@Serializable
@MySerializableWithInfo(123, String::class)
class Project3(val name: String, val language: String)
@Serializable(with = MySerializer::class)
@MySerializableWithInfo(123, String::class)
class Project4(val name: String, val language: String)
@MySerializableWithInfo(123, String::class)
sealed class TestSealed {
@MySerializableWithInfo(123, String::class)
class A(val value1: String) : TestSealed()
@MySerializableWithInfo(123, String::class)
class B(val value2: String) : TestSealed()
}
@MySerializable
abstract class TestAbstract {
@MySerializableWithInfo(123, String::class)
class A(val value1: String) : TestSealed()
@MySerializableWithInfo(123, String::class)
class B(val value2: String) : TestSealed()
}
@MySerializableWithInfo(123, String::class)
enum class TestEnum { Value1, Value2 }
@MySerializableWithInfo(123, String::class)
object TestObject
object MySerializer : KSerializer<Project4> {
override val descriptor: SerialDescriptor = PrimitiveSerialDescriptor("Project4", PrimitiveKind.STRING)
override fun serialize(encoder: Encoder, value: Project4) = encoder.encodeString("${value.name}:${value.language}")
override fun deserialize(decoder: Decoder): Project4 {
val params = decoder.decodeString().split(':')
return Project4(params[0], params[1])
}
}
fun testMetaSerializable() {
val string = Json.encodeToString(Project1.serializer(), Project1("name", "lang"))
assertEquals("""{"name":"name","language":"lang"}""", string)
val reconstructed = Json.decodeFromString(Project1.serializer(), string)
assertEquals("name", reconstructed.name)
assertEquals("lang", reconstructed.language)
}
fun testMetaSerializableWithInfo() {
val string = Json.encodeToString(Project2.serializer(), Project2("name", "lang"))
assertEquals("""{"name":"name","language":"lang"}""", string)
val reconstructed = Json.decodeFromString(Project2.serializer(), string)
assertEquals("name", reconstructed.name)
assertEquals("lang", reconstructed.language)
val info = Project2.serializer().descriptor.annotations.filterIsInstance<MySerializableWithInfo>().first()
assertEquals(123, info.value)
assertEquals(String::class, info.kclass)
}
fun testMetaSerializableOnProperty() {
val info = Wrapper.serializer().descriptor.getElementAnnotations(0).filterIsInstance<MySerializableWithInfo>().first()
assertEquals(234, info.value)
assertEquals(Int::class, info.kclass)
}
fun testSerializableAndMetaAnnotation() {
val string = Json.encodeToString(Project3.serializer(), Project3("name", "lang"))
assertEquals("""{"name":"name","language":"lang"}""", string)
val reconstructed = Json.decodeFromString(Project3.serializer(), string)
assertEquals("name", reconstructed.name)
assertEquals("lang", reconstructed.language)
val info = Project3.serializer().descriptor.annotations.filterIsInstance<MySerializableWithInfo>().first()
assertEquals(123, info.value)
assertEquals(String::class, info.kclass)
}
fun testCustomSerializerAndMetaAnnotation() {
val string = Json.encodeToString(Project4.serializer(), Project4("name", "lang"))
assertEquals("""name:lang""", string)
val reconstructed = Json.decodeFromString(Project4.serializer(), string)
assertEquals("name", reconstructed.name)
assertEquals("lang", reconstructed.language)
}
fun testSealed() {
val serializerA = TestSealed.A.serializer()
val serializerB = TestSealed.B.serializer()
assertNotNull(serializerA)
assertNotNull(serializerB)
val infoA = serializerA.descriptor.annotations.filterIsInstance<MySerializableWithInfo>().first()
val infoB = serializerB.descriptor.annotations.filterIsInstance<MySerializableWithInfo>().first()
assertEquals(123, infoA.value)
assertEquals(String::class, infoA.kclass)
assertEquals(123, infoB.value)
assertEquals(String::class, infoB.kclass)
}
fun testAbstract() {
val serializerA = TestAbstract.A.serializer()
val serializerB = TestAbstract.B.serializer()
assertNotNull(serializerA)
assertNotNull(serializerB)
val infoA = serializerA.descriptor.annotations.filterIsInstance<MySerializableWithInfo>().first()
val infoB = serializerB.descriptor.annotations.filterIsInstance<MySerializableWithInfo>().first()
assertEquals(123, infoA.value)
assertEquals(String::class, infoA.kclass)
assertEquals(123, infoB.value)
assertEquals(String::class, infoB.kclass)
}
fun testEnum() {
val serializer = TestEnum.serializer()
assertNotNull(serializer)
val info = serializer.descriptor.annotations.filterIsInstance<MySerializableWithInfo>().first()
assertEquals(123, info.value)
assertEquals(String::class, info.kclass)
}
fun testObject() {
val serializer = TestObject.serializer()
assertNotNull(serializer)
val info = serializer.descriptor.annotations.filterIsInstance<MySerializableWithInfo>().first()
assertEquals(123, info.value)
assertEquals(String::class, info.kclass)
}
fun box(): String {
testMetaSerializable()
testMetaSerializableWithInfo()
testMetaSerializableOnProperty()
testSealed()
testAbstract()
testEnum()
testObject()
return "OK"
}
@@ -0,0 +1,56 @@
// IGNORE_BACKEND_FIR: JVM_IR
// TARGET_BACKEND: JVM_IR
// WITH_STDLIB
// MODULE: lib
// FILE: lib.kt
package a
import kotlinx.serialization.*
@Serializable
open class OpenBody {
var optional: String? = "foo"
}
@Serializable
abstract class AbstractConstructor(var optional: String = "foo")
// MODULE: app(lib)
// FILE: app.kt
package test
import a.*
import kotlinx.serialization.*
import kotlinx.serialization.json.*
import kotlin.test.assertEquals
@Serializable
class Test1: OpenBody()
@Serializable
class Test2: AbstractConstructor()
fun test1() {
val string = Json.encodeToString(Test1.serializer(), Test1())
assertEquals("{}", string)
val reconstructed = Json.decodeFromString(Test1.serializer(), string)
assertEquals("foo", reconstructed.optional)
}
fun test2() {
val string = Json.encodeToString(Test2.serializer(), Test2())
assertEquals("{}", string)
val reconstructed = Json.decodeFromString(Test2.serializer(), string)
assertEquals("foo", reconstructed.optional)
}
fun box(): String {
test1()
test2()
return "OK"
}
@@ -0,0 +1,37 @@
// IGNORE_BACKEND_FIR: JVM_IR
// TARGET_BACKEND: JVM_IR
// WITH_STDLIB
package a
import kotlinx.serialization.*
import kotlinx.serialization.descriptors.*
import kotlin.test.assertEquals
interface I
sealed interface SI
@Serializable
sealed interface SSI
@Serializable
class Holder(
val i: I,
val si: SI,
val ssi: SSI
)
fun SerialDescriptor.checkKind(index: Int, kind: String) {
assertEquals(kind, getElementDescriptor(index).kind.toString())
}
fun box(): String {
val desc = Holder.serializer().descriptor
desc.checkKind(0, "OPEN")
desc.checkKind(1, "OPEN")
desc.checkKind(2, "SEALED")
return "OK"
}
@@ -0,0 +1,23 @@
// IGNORE_BACKEND_FIR: JVM_IR
// TARGET_BACKEND: JVM_IR
// WITH_STDLIB
/**
* This test checks that in the case when serialization plugin is applied, but kotlinx-serialization-core runtime is not present in compile classpath,
* compilation of regular Kotlin classes still finishes succesfully.
*
* Such requirement is needed for cases when plugin is applied to a Gradle module, but runtime dependency is provided only in certain configurations,
* e.g. only in `testImplementation` configuration (see :wasm:wasm-ir module). In such setup, production sources have plugin applied, but no runtime in classpath.
*/
data class X(val i: Int) {
companion object {
fun x(): X = X(42)
}
}
fun box(): String {
val i = X.x().i
return if (i == 42) "OK" else i.toString()
}
File diff suppressed because it is too large Load Diff
+13
View File
@@ -0,0 +1,13 @@
// CURIOUS_ABOUT serialize, deserialize, write$Self, childSerializers, <init>, <clinit>, getDescriptor
// WITH_STDLIB
import kotlinx.serialization.*
@Serializable
class User(val firstName: String, val lastName: String)
@Serializable
class OptionalUser(val user: User = User("", ""))
@Serializable
class ListOfUsers(val list: List<User>)
+969
View File
@@ -0,0 +1,969 @@
public final class ListOfUsers$$serializer : java/lang/Object, kotlinx/serialization/internal/GeneratedSerializer {
private final static kotlinx.serialization.descriptors.SerialDescriptor $$serialDesc
public final static ListOfUsers$$serializer INSTANCE
static void <clinit>() {
LABEL (L0)
LINENUMBER (13)
NEW (ListOfUsers$$serializer)
DUP
INVOKESPECIAL (ListOfUsers$$serializer, <init>, ()V)
ASTORE (0)
ALOAD (0)
PUTSTATIC (ListOfUsers$$serializer, INSTANCE, LListOfUsers$$serializer;)
NEW (kotlinx/serialization/internal/PluginGeneratedSerialDescriptor)
DUP
LDC (ListOfUsers)
GETSTATIC (ListOfUsers$$serializer, INSTANCE, LListOfUsers$$serializer;)
CHECKCAST (kotlinx/serialization/internal/GeneratedSerializer)
LDC (1)
INVOKESPECIAL (kotlinx/serialization/internal/PluginGeneratedSerialDescriptor, <init>, (Ljava/lang/String;Lkotlinx/serialization/internal/GeneratedSerializer;I)V)
ASTORE (0)
ALOAD (0)
LDC (list)
ICONST_0
INVOKEVIRTUAL (kotlinx/serialization/internal/PluginGeneratedSerialDescriptor, addElement, (Ljava/lang/String;Z)V)
ALOAD (0)
PUTSTATIC (ListOfUsers$$serializer, $$serialDesc, Lkotlinx/serialization/descriptors/SerialDescriptor;)
RETURN
}
private void <init>() {
LABEL (L0)
LINENUMBER (13)
ALOAD (0)
INVOKESPECIAL (java/lang/Object, <init>, ()V)
RETURN
LABEL (L1)
}
public kotlinx.serialization.KSerializer[] childSerializers() {
LABEL (L0)
ICONST_1
ANEWARRAY (kotlinx/serialization/KSerializer)
DUP
ICONST_0
NEW (kotlinx/serialization/internal/ArrayListSerializer)
DUP
GETSTATIC (User$$serializer, INSTANCE, LUser$$serializer;)
CHECKCAST (kotlinx/serialization/KSerializer)
INVOKESPECIAL (kotlinx/serialization/internal/ArrayListSerializer, <init>, (Lkotlinx/serialization/KSerializer;)V)
AASTORE
ARETURN
LABEL (L1)
}
public ListOfUsers deserialize(kotlinx.serialization.encoding.Decoder decoder) {
LABEL (L0)
ALOAD (1)
LDC (decoder)
INVOKESTATIC (kotlin/jvm/internal/Intrinsics, checkNotNullParameter, (Ljava/lang/Object;Ljava/lang/String;)V)
GETSTATIC (ListOfUsers$$serializer, $$serialDesc, Lkotlinx/serialization/descriptors/SerialDescriptor;)
ASTORE (2)
ICONST_0
ISTORE (4)
ACONST_NULL
ASTORE (5)
ALOAD (1)
ALOAD (2)
INVOKEINTERFACE (kotlinx/serialization/encoding/Decoder, beginStructure, (Lkotlinx/serialization/descriptors/SerialDescriptor;)Lkotlinx/serialization/encoding/CompositeDecoder;)
ASTORE (1)
ALOAD (1)
INVOKEINTERFACE (kotlinx/serialization/encoding/CompositeDecoder, decodeSequentially, ()Z)
IFEQ (L1)
ALOAD (1)
ALOAD (2)
ICONST_0
NEW (kotlinx/serialization/internal/ArrayListSerializer)
DUP
GETSTATIC (User$$serializer, INSTANCE, LUser$$serializer;)
CHECKCAST (kotlinx/serialization/KSerializer)
INVOKESPECIAL (kotlinx/serialization/internal/ArrayListSerializer, <init>, (Lkotlinx/serialization/KSerializer;)V)
ALOAD (5)
INVOKEINTERFACE (kotlinx/serialization/encoding/CompositeDecoder, decodeSerializableElement, (Lkotlinx/serialization/descriptors/SerialDescriptor;ILkotlinx/serialization/DeserializationStrategy;Ljava/lang/Object;)Ljava/lang/Object;)
CHECKCAST (java/util/List)
ASTORE (5)
LDC (2147483647)
ISTORE (4)
GOTO (L2)
LABEL (L1)
ALOAD (1)
ALOAD (2)
INVOKEINTERFACE (kotlinx/serialization/encoding/CompositeDecoder, decodeElementIndex, (Lkotlinx/serialization/descriptors/SerialDescriptor;)I)
ISTORE (3)
ILOAD (3)
TABLESWITCH
-1: L2
0: L3
default: L4
LABEL (L3)
ALOAD (1)
ALOAD (2)
ICONST_0
NEW (kotlinx/serialization/internal/ArrayListSerializer)
DUP
GETSTATIC (User$$serializer, INSTANCE, LUser$$serializer;)
CHECKCAST (kotlinx/serialization/KSerializer)
INVOKESPECIAL (kotlinx/serialization/internal/ArrayListSerializer, <init>, (Lkotlinx/serialization/KSerializer;)V)
ALOAD (5)
INVOKEINTERFACE (kotlinx/serialization/encoding/CompositeDecoder, decodeSerializableElement, (Lkotlinx/serialization/descriptors/SerialDescriptor;ILkotlinx/serialization/DeserializationStrategy;Ljava/lang/Object;)Ljava/lang/Object;)
CHECKCAST (java/util/List)
ASTORE (5)
ILOAD (4)
ICONST_1
IOR
ISTORE (4)
GOTO (L1)
LABEL (L2)
ALOAD (1)
ALOAD (2)
INVOKEINTERFACE (kotlinx/serialization/encoding/CompositeDecoder, endStructure, (Lkotlinx/serialization/descriptors/SerialDescriptor;)V)
NEW (ListOfUsers)
DUP
ILOAD (4)
ALOAD (5)
ACONST_NULL
INVOKESPECIAL (ListOfUsers, <init>, (ILjava/util/List;Lkotlinx/serialization/internal/SerializationConstructorMarker;)V)
ARETURN
LABEL (L4)
NEW (kotlinx/serialization/UnknownFieldException)
DUP
ILOAD (3)
INVOKESPECIAL (kotlinx/serialization/UnknownFieldException, <init>, (I)V)
CHECKCAST (java/lang/Throwable)
ATHROW
LABEL (L5)
}
public java.lang.Object deserialize(kotlinx.serialization.encoding.Decoder p0) {
LABEL (L0)
LINENUMBER (13)
ALOAD (0)
ALOAD (1)
INVOKEVIRTUAL (ListOfUsers$$serializer, deserialize, (Lkotlinx/serialization/encoding/Decoder;)LListOfUsers;)
ARETURN
}
public kotlinx.serialization.descriptors.SerialDescriptor getDescriptor() {
LABEL (L0)
GETSTATIC (ListOfUsers$$serializer, $$serialDesc, Lkotlinx/serialization/descriptors/SerialDescriptor;)
ARETURN
LABEL (L1)
}
public void serialize(kotlinx.serialization.encoding.Encoder encoder, ListOfUsers value) {
LABEL (L0)
ALOAD (1)
LDC (encoder)
INVOKESTATIC (kotlin/jvm/internal/Intrinsics, checkNotNullParameter, (Ljava/lang/Object;Ljava/lang/String;)V)
ALOAD (2)
LDC (value)
INVOKESTATIC (kotlin/jvm/internal/Intrinsics, checkNotNullParameter, (Ljava/lang/Object;Ljava/lang/String;)V)
GETSTATIC (ListOfUsers$$serializer, $$serialDesc, Lkotlinx/serialization/descriptors/SerialDescriptor;)
ASTORE (3)
ALOAD (1)
ALOAD (3)
INVOKEINTERFACE (kotlinx/serialization/encoding/Encoder, beginStructure, (Lkotlinx/serialization/descriptors/SerialDescriptor;)Lkotlinx/serialization/encoding/CompositeEncoder;)
ASTORE (1)
ALOAD (2)
ALOAD (1)
ALOAD (3)
INVOKESTATIC (ListOfUsers, write$Self, (LListOfUsers;Lkotlinx/serialization/encoding/CompositeEncoder;Lkotlinx/serialization/descriptors/SerialDescriptor;)V)
ALOAD (1)
ALOAD (3)
INVOKEINTERFACE (kotlinx/serialization/encoding/CompositeEncoder, endStructure, (Lkotlinx/serialization/descriptors/SerialDescriptor;)V)
RETURN
LABEL (L1)
}
public void serialize(kotlinx.serialization.encoding.Encoder p0, java.lang.Object p1) {
LABEL (L0)
LINENUMBER (13)
ALOAD (0)
ALOAD (1)
ALOAD (2)
CHECKCAST (ListOfUsers)
INVOKEVIRTUAL (ListOfUsers$$serializer, serialize, (Lkotlinx/serialization/encoding/Encoder;LListOfUsers;)V)
RETURN
}
public kotlinx.serialization.KSerializer[] typeParametersSerializers()
}
public final class ListOfUsers$Companion : java/lang/Object {
private void <init>() {
LABEL (L0)
LINENUMBER (13)
ALOAD (0)
INVOKESPECIAL (java/lang/Object, <init>, ()V)
RETURN
LABEL (L1)
}
public void <init>(kotlin.jvm.internal.DefaultConstructorMarker $constructor_marker) {
LABEL (L0)
LINENUMBER (13)
ALOAD (0)
INVOKESPECIAL (ListOfUsers$Companion, <init>, ()V)
RETURN
LABEL (L1)
}
public final kotlinx.serialization.KSerializer serializer()
}
public final class ListOfUsers : java/lang/Object {
public final static ListOfUsers$Companion Companion
private final java.util.List list
static void <clinit>() {
NEW (ListOfUsers$Companion)
DUP
ACONST_NULL
INVOKESPECIAL (ListOfUsers$Companion, <init>, (Lkotlin/jvm/internal/DefaultConstructorMarker;)V)
PUTSTATIC (ListOfUsers, Companion, LListOfUsers$Companion;)
RETURN
}
public void <init>(java.util.List list) {
LABEL (L0)
ALOAD (1)
LDC (list)
INVOKESTATIC (kotlin/jvm/internal/Intrinsics, checkNotNullParameter, (Ljava/lang/Object;Ljava/lang/String;)V)
LABEL (L1)
LINENUMBER (13)
ALOAD (0)
INVOKESPECIAL (java/lang/Object, <init>, ()V)
ALOAD (0)
ALOAD (1)
PUTFIELD (ListOfUsers, list, Ljava/util/List;)
RETURN
LABEL (L2)
}
public void <init>(int seen1, java.util.List list, kotlinx.serialization.internal.SerializationConstructorMarker serializationConstructorMarker) {
LABEL (L0)
ICONST_1
DUP
ILOAD (1)
IAND
IF_ICMPEQ (L1)
ILOAD (1)
ICONST_1
GETSTATIC (ListOfUsers$$serializer, INSTANCE, LListOfUsers$$serializer;)
CHECKCAST (kotlinx/serialization/KSerializer)
INVOKEINTERFACE (kotlinx/serialization/KSerializer, getDescriptor, ()Lkotlinx/serialization/descriptors/SerialDescriptor;)
INVOKESTATIC (kotlinx/serialization/internal/PluginExceptionsKt, throwMissingFieldException, (IILkotlinx/serialization/descriptors/SerialDescriptor;)V)
LABEL (L1)
ALOAD (0)
INVOKESPECIAL (java/lang/Object, <init>, ()V)
ALOAD (0)
ALOAD (2)
PUTFIELD (ListOfUsers, list, Ljava/util/List;)
RETURN
LABEL (L2)
}
public final java.util.List getList()
public final static void write$Self(ListOfUsers self, kotlinx.serialization.encoding.CompositeEncoder output, kotlinx.serialization.descriptors.SerialDescriptor serialDesc) {
LABEL (L0)
ALOAD (0)
LDC (self)
INVOKESTATIC (kotlin/jvm/internal/Intrinsics, checkNotNullParameter, (Ljava/lang/Object;Ljava/lang/String;)V)
ALOAD (1)
LDC (output)
INVOKESTATIC (kotlin/jvm/internal/Intrinsics, checkNotNullParameter, (Ljava/lang/Object;Ljava/lang/String;)V)
ALOAD (2)
LDC (serialDesc)
INVOKESTATIC (kotlin/jvm/internal/Intrinsics, checkNotNullParameter, (Ljava/lang/Object;Ljava/lang/String;)V)
ALOAD (1)
ALOAD (2)
ICONST_0
NEW (kotlinx/serialization/internal/ArrayListSerializer)
DUP
GETSTATIC (User$$serializer, INSTANCE, LUser$$serializer;)
CHECKCAST (kotlinx/serialization/KSerializer)
INVOKESPECIAL (kotlinx/serialization/internal/ArrayListSerializer, <init>, (Lkotlinx/serialization/KSerializer;)V)
ALOAD (0)
GETFIELD (ListOfUsers, list, Ljava/util/List;)
INVOKEINTERFACE (kotlinx/serialization/encoding/CompositeEncoder, encodeSerializableElement, (Lkotlinx/serialization/descriptors/SerialDescriptor;ILkotlinx/serialization/SerializationStrategy;Ljava/lang/Object;)V)
RETURN
LABEL (L1)
}
}
public final class OptionalUser$$serializer : java/lang/Object, kotlinx/serialization/internal/GeneratedSerializer {
private final static kotlinx.serialization.descriptors.SerialDescriptor $$serialDesc
public final static OptionalUser$$serializer INSTANCE
static void <clinit>() {
LABEL (L0)
LINENUMBER (10)
NEW (OptionalUser$$serializer)
DUP
INVOKESPECIAL (OptionalUser$$serializer, <init>, ()V)
ASTORE (0)
ALOAD (0)
PUTSTATIC (OptionalUser$$serializer, INSTANCE, LOptionalUser$$serializer;)
NEW (kotlinx/serialization/internal/PluginGeneratedSerialDescriptor)
DUP
LDC (OptionalUser)
GETSTATIC (OptionalUser$$serializer, INSTANCE, LOptionalUser$$serializer;)
CHECKCAST (kotlinx/serialization/internal/GeneratedSerializer)
LDC (1)
INVOKESPECIAL (kotlinx/serialization/internal/PluginGeneratedSerialDescriptor, <init>, (Ljava/lang/String;Lkotlinx/serialization/internal/GeneratedSerializer;I)V)
ASTORE (0)
ALOAD (0)
LDC (user)
ICONST_1
INVOKEVIRTUAL (kotlinx/serialization/internal/PluginGeneratedSerialDescriptor, addElement, (Ljava/lang/String;Z)V)
ALOAD (0)
PUTSTATIC (OptionalUser$$serializer, $$serialDesc, Lkotlinx/serialization/descriptors/SerialDescriptor;)
RETURN
}
private void <init>() {
LABEL (L0)
LINENUMBER (10)
ALOAD (0)
INVOKESPECIAL (java/lang/Object, <init>, ()V)
RETURN
LABEL (L1)
}
public kotlinx.serialization.KSerializer[] childSerializers() {
LABEL (L0)
ICONST_1
ANEWARRAY (kotlinx/serialization/KSerializer)
DUP
ICONST_0
GETSTATIC (User$$serializer, INSTANCE, LUser$$serializer;)
CHECKCAST (kotlinx/serialization/KSerializer)
AASTORE
ARETURN
LABEL (L1)
}
public OptionalUser deserialize(kotlinx.serialization.encoding.Decoder decoder) {
LABEL (L0)
ALOAD (1)
LDC (decoder)
INVOKESTATIC (kotlin/jvm/internal/Intrinsics, checkNotNullParameter, (Ljava/lang/Object;Ljava/lang/String;)V)
GETSTATIC (OptionalUser$$serializer, $$serialDesc, Lkotlinx/serialization/descriptors/SerialDescriptor;)
ASTORE (2)
ICONST_0
ISTORE (4)
ACONST_NULL
ASTORE (5)
ALOAD (1)
ALOAD (2)
INVOKEINTERFACE (kotlinx/serialization/encoding/Decoder, beginStructure, (Lkotlinx/serialization/descriptors/SerialDescriptor;)Lkotlinx/serialization/encoding/CompositeDecoder;)
ASTORE (1)
ALOAD (1)
INVOKEINTERFACE (kotlinx/serialization/encoding/CompositeDecoder, decodeSequentially, ()Z)
IFEQ (L1)
ALOAD (1)
ALOAD (2)
ICONST_0
GETSTATIC (User$$serializer, INSTANCE, LUser$$serializer;)
CHECKCAST (kotlinx/serialization/KSerializer)
ALOAD (5)
INVOKEINTERFACE (kotlinx/serialization/encoding/CompositeDecoder, decodeSerializableElement, (Lkotlinx/serialization/descriptors/SerialDescriptor;ILkotlinx/serialization/DeserializationStrategy;Ljava/lang/Object;)Ljava/lang/Object;)
CHECKCAST (User)
ASTORE (5)
LDC (2147483647)
ISTORE (4)
GOTO (L2)
LABEL (L1)
ALOAD (1)
ALOAD (2)
INVOKEINTERFACE (kotlinx/serialization/encoding/CompositeDecoder, decodeElementIndex, (Lkotlinx/serialization/descriptors/SerialDescriptor;)I)
ISTORE (3)
ILOAD (3)
TABLESWITCH
-1: L2
0: L3
default: L4
LABEL (L3)
ALOAD (1)
ALOAD (2)
ICONST_0
GETSTATIC (User$$serializer, INSTANCE, LUser$$serializer;)
CHECKCAST (kotlinx/serialization/KSerializer)
ALOAD (5)
INVOKEINTERFACE (kotlinx/serialization/encoding/CompositeDecoder, decodeSerializableElement, (Lkotlinx/serialization/descriptors/SerialDescriptor;ILkotlinx/serialization/DeserializationStrategy;Ljava/lang/Object;)Ljava/lang/Object;)
CHECKCAST (User)
ASTORE (5)
ILOAD (4)
ICONST_1
IOR
ISTORE (4)
GOTO (L1)
LABEL (L2)
ALOAD (1)
ALOAD (2)
INVOKEINTERFACE (kotlinx/serialization/encoding/CompositeDecoder, endStructure, (Lkotlinx/serialization/descriptors/SerialDescriptor;)V)
NEW (OptionalUser)
DUP
ILOAD (4)
ALOAD (5)
ACONST_NULL
INVOKESPECIAL (OptionalUser, <init>, (ILUser;Lkotlinx/serialization/internal/SerializationConstructorMarker;)V)
ARETURN
LABEL (L4)
NEW (kotlinx/serialization/UnknownFieldException)
DUP
ILOAD (3)
INVOKESPECIAL (kotlinx/serialization/UnknownFieldException, <init>, (I)V)
CHECKCAST (java/lang/Throwable)
ATHROW
LABEL (L5)
}
public java.lang.Object deserialize(kotlinx.serialization.encoding.Decoder p0) {
LABEL (L0)
LINENUMBER (10)
ALOAD (0)
ALOAD (1)
INVOKEVIRTUAL (OptionalUser$$serializer, deserialize, (Lkotlinx/serialization/encoding/Decoder;)LOptionalUser;)
ARETURN
}
public kotlinx.serialization.descriptors.SerialDescriptor getDescriptor() {
LABEL (L0)
GETSTATIC (OptionalUser$$serializer, $$serialDesc, Lkotlinx/serialization/descriptors/SerialDescriptor;)
ARETURN
LABEL (L1)
}
public void serialize(kotlinx.serialization.encoding.Encoder encoder, OptionalUser value) {
LABEL (L0)
ALOAD (1)
LDC (encoder)
INVOKESTATIC (kotlin/jvm/internal/Intrinsics, checkNotNullParameter, (Ljava/lang/Object;Ljava/lang/String;)V)
ALOAD (2)
LDC (value)
INVOKESTATIC (kotlin/jvm/internal/Intrinsics, checkNotNullParameter, (Ljava/lang/Object;Ljava/lang/String;)V)
GETSTATIC (OptionalUser$$serializer, $$serialDesc, Lkotlinx/serialization/descriptors/SerialDescriptor;)
ASTORE (3)
ALOAD (1)
ALOAD (3)
INVOKEINTERFACE (kotlinx/serialization/encoding/Encoder, beginStructure, (Lkotlinx/serialization/descriptors/SerialDescriptor;)Lkotlinx/serialization/encoding/CompositeEncoder;)
ASTORE (1)
ALOAD (2)
ALOAD (1)
ALOAD (3)
INVOKESTATIC (OptionalUser, write$Self, (LOptionalUser;Lkotlinx/serialization/encoding/CompositeEncoder;Lkotlinx/serialization/descriptors/SerialDescriptor;)V)
ALOAD (1)
ALOAD (3)
INVOKEINTERFACE (kotlinx/serialization/encoding/CompositeEncoder, endStructure, (Lkotlinx/serialization/descriptors/SerialDescriptor;)V)
RETURN
LABEL (L1)
}
public void serialize(kotlinx.serialization.encoding.Encoder p0, java.lang.Object p1) {
LABEL (L0)
LINENUMBER (10)
ALOAD (0)
ALOAD (1)
ALOAD (2)
CHECKCAST (OptionalUser)
INVOKEVIRTUAL (OptionalUser$$serializer, serialize, (Lkotlinx/serialization/encoding/Encoder;LOptionalUser;)V)
RETURN
}
public kotlinx.serialization.KSerializer[] typeParametersSerializers()
}
public final class OptionalUser$Companion : java/lang/Object {
private void <init>() {
LABEL (L0)
LINENUMBER (10)
ALOAD (0)
INVOKESPECIAL (java/lang/Object, <init>, ()V)
RETURN
LABEL (L1)
}
public void <init>(kotlin.jvm.internal.DefaultConstructorMarker $constructor_marker) {
LABEL (L0)
LINENUMBER (10)
ALOAD (0)
INVOKESPECIAL (OptionalUser$Companion, <init>, ()V)
RETURN
LABEL (L1)
}
public final kotlinx.serialization.KSerializer serializer()
}
public final class OptionalUser : java/lang/Object {
public final static OptionalUser$Companion Companion
private final User user
static void <clinit>() {
NEW (OptionalUser$Companion)
DUP
ACONST_NULL
INVOKESPECIAL (OptionalUser$Companion, <init>, (Lkotlin/jvm/internal/DefaultConstructorMarker;)V)
PUTSTATIC (OptionalUser, Companion, LOptionalUser$Companion;)
RETURN
}
public void <init>(User user) {
LABEL (L0)
ALOAD (1)
LDC (user)
INVOKESTATIC (kotlin/jvm/internal/Intrinsics, checkNotNullParameter, (Ljava/lang/Object;Ljava/lang/String;)V)
LABEL (L1)
LINENUMBER (10)
ALOAD (0)
INVOKESPECIAL (java/lang/Object, <init>, ()V)
ALOAD (0)
ALOAD (1)
PUTFIELD (OptionalUser, user, LUser;)
RETURN
LABEL (L2)
}
public void <init>(User p0, int p1, kotlin.jvm.internal.DefaultConstructorMarker p2) {
ILOAD (2)
ICONST_1
IAND
IFEQ (L0)
LABEL (L1)
LINENUMBER (10)
NEW (User)
DUP
LDC ()
LDC ()
INVOKESPECIAL (User, <init>, (Ljava/lang/String;Ljava/lang/String;)V)
ASTORE (1)
LABEL (L0)
ALOAD (0)
ALOAD (1)
INVOKESPECIAL (OptionalUser, <init>, (LUser;)V)
RETURN
}
public void <init>() {
ALOAD (0)
ACONST_NULL
ICONST_1
ACONST_NULL
INVOKESPECIAL (OptionalUser, <init>, (LUser;ILkotlin/jvm/internal/DefaultConstructorMarker;)V)
RETURN
}
public void <init>(int seen1, User user, kotlinx.serialization.internal.SerializationConstructorMarker serializationConstructorMarker) {
LABEL (L0)
ICONST_0
DUP
ILOAD (1)
IAND
IF_ICMPEQ (L1)
ILOAD (1)
ICONST_0
GETSTATIC (OptionalUser$$serializer, INSTANCE, LOptionalUser$$serializer;)
CHECKCAST (kotlinx/serialization/KSerializer)
INVOKEINTERFACE (kotlinx/serialization/KSerializer, getDescriptor, ()Lkotlinx/serialization/descriptors/SerialDescriptor;)
INVOKESTATIC (kotlinx/serialization/internal/PluginExceptionsKt, throwMissingFieldException, (IILkotlinx/serialization/descriptors/SerialDescriptor;)V)
LABEL (L1)
ALOAD (0)
INVOKESPECIAL (java/lang/Object, <init>, ()V)
ILOAD (1)
ICONST_1
IAND
IFEQ (L2)
ALOAD (0)
ALOAD (2)
PUTFIELD (OptionalUser, user, LUser;)
GOTO (L3)
LABEL (L2)
ALOAD (0)
LABEL (L4)
LINENUMBER (10)
NEW (User)
DUP
LDC ()
LDC ()
INVOKESPECIAL (User, <init>, (Ljava/lang/String;Ljava/lang/String;)V)
PUTFIELD (OptionalUser, user, LUser;)
LABEL (L3)
RETURN
LABEL (L5)
}
public final User getUser()
public final static void write$Self(OptionalUser self, kotlinx.serialization.encoding.CompositeEncoder output, kotlinx.serialization.descriptors.SerialDescriptor serialDesc) {
LABEL (L0)
ALOAD (0)
LDC (self)
INVOKESTATIC (kotlin/jvm/internal/Intrinsics, checkNotNullParameter, (Ljava/lang/Object;Ljava/lang/String;)V)
ALOAD (1)
LDC (output)
INVOKESTATIC (kotlin/jvm/internal/Intrinsics, checkNotNullParameter, (Ljava/lang/Object;Ljava/lang/String;)V)
ALOAD (2)
LDC (serialDesc)
INVOKESTATIC (kotlin/jvm/internal/Intrinsics, checkNotNullParameter, (Ljava/lang/Object;Ljava/lang/String;)V)
ALOAD (0)
GETFIELD (OptionalUser, user, LUser;)
LABEL (L1)
LINENUMBER (10)
NEW (User)
DUP
LDC ()
LDC ()
INVOKESPECIAL (User, <init>, (Ljava/lang/String;Ljava/lang/String;)V)
INVOKESTATIC (kotlin/jvm/internal/Intrinsics, areEqual, (Ljava/lang/Object;Ljava/lang/Object;)Z)
ICONST_1
IXOR
IFNE (L2)
ALOAD (1)
ALOAD (2)
ICONST_0
INVOKEINTERFACE (kotlinx/serialization/encoding/CompositeEncoder, shouldEncodeElementDefault, (Lkotlinx/serialization/descriptors/SerialDescriptor;I)Z)
IFEQ (L3)
LABEL (L2)
ALOAD (1)
ALOAD (2)
ICONST_0
GETSTATIC (User$$serializer, INSTANCE, LUser$$serializer;)
CHECKCAST (kotlinx/serialization/KSerializer)
ALOAD (0)
GETFIELD (OptionalUser, user, LUser;)
INVOKEINTERFACE (kotlinx/serialization/encoding/CompositeEncoder, encodeSerializableElement, (Lkotlinx/serialization/descriptors/SerialDescriptor;ILkotlinx/serialization/SerializationStrategy;Ljava/lang/Object;)V)
LABEL (L3)
RETURN
LABEL (L4)
}
}
public final class User$$serializer : java/lang/Object, kotlinx/serialization/internal/GeneratedSerializer {
private final static kotlinx.serialization.descriptors.SerialDescriptor $$serialDesc
public final static User$$serializer INSTANCE
static void <clinit>() {
LABEL (L0)
LINENUMBER (7)
NEW (User$$serializer)
DUP
INVOKESPECIAL (User$$serializer, <init>, ()V)
ASTORE (0)
ALOAD (0)
PUTSTATIC (User$$serializer, INSTANCE, LUser$$serializer;)
NEW (kotlinx/serialization/internal/PluginGeneratedSerialDescriptor)
DUP
LDC (User)
GETSTATIC (User$$serializer, INSTANCE, LUser$$serializer;)
CHECKCAST (kotlinx/serialization/internal/GeneratedSerializer)
LDC (2)
INVOKESPECIAL (kotlinx/serialization/internal/PluginGeneratedSerialDescriptor, <init>, (Ljava/lang/String;Lkotlinx/serialization/internal/GeneratedSerializer;I)V)
ASTORE (0)
ALOAD (0)
LDC (firstName)
ICONST_0
INVOKEVIRTUAL (kotlinx/serialization/internal/PluginGeneratedSerialDescriptor, addElement, (Ljava/lang/String;Z)V)
ALOAD (0)
LDC (lastName)
ICONST_0
INVOKEVIRTUAL (kotlinx/serialization/internal/PluginGeneratedSerialDescriptor, addElement, (Ljava/lang/String;Z)V)
ALOAD (0)
PUTSTATIC (User$$serializer, $$serialDesc, Lkotlinx/serialization/descriptors/SerialDescriptor;)
RETURN
}
private void <init>() {
LABEL (L0)
LINENUMBER (7)
ALOAD (0)
INVOKESPECIAL (java/lang/Object, <init>, ()V)
RETURN
LABEL (L1)
}
public kotlinx.serialization.KSerializer[] childSerializers() {
LABEL (L0)
ICONST_2
ANEWARRAY (kotlinx/serialization/KSerializer)
DUP
ICONST_0
GETSTATIC (kotlinx/serialization/internal/StringSerializer, INSTANCE, Lkotlinx/serialization/internal/StringSerializer;)
CHECKCAST (kotlinx/serialization/KSerializer)
AASTORE
DUP
ICONST_1
GETSTATIC (kotlinx/serialization/internal/StringSerializer, INSTANCE, Lkotlinx/serialization/internal/StringSerializer;)
CHECKCAST (kotlinx/serialization/KSerializer)
AASTORE
ARETURN
LABEL (L1)
}
public User deserialize(kotlinx.serialization.encoding.Decoder decoder) {
LABEL (L0)
ALOAD (1)
LDC (decoder)
INVOKESTATIC (kotlin/jvm/internal/Intrinsics, checkNotNullParameter, (Ljava/lang/Object;Ljava/lang/String;)V)
GETSTATIC (User$$serializer, $$serialDesc, Lkotlinx/serialization/descriptors/SerialDescriptor;)
ASTORE (2)
ICONST_0
ISTORE (4)
ACONST_NULL
ASTORE (5)
ACONST_NULL
ASTORE (6)
ALOAD (1)
ALOAD (2)
INVOKEINTERFACE (kotlinx/serialization/encoding/Decoder, beginStructure, (Lkotlinx/serialization/descriptors/SerialDescriptor;)Lkotlinx/serialization/encoding/CompositeDecoder;)
ASTORE (1)
ALOAD (1)
INVOKEINTERFACE (kotlinx/serialization/encoding/CompositeDecoder, decodeSequentially, ()Z)
IFEQ (L1)
ALOAD (1)
ALOAD (2)
ICONST_0
INVOKEINTERFACE (kotlinx/serialization/encoding/CompositeDecoder, decodeStringElement, (Lkotlinx/serialization/descriptors/SerialDescriptor;I)Ljava/lang/String;)
ASTORE (5)
ALOAD (1)
ALOAD (2)
ICONST_1
INVOKEINTERFACE (kotlinx/serialization/encoding/CompositeDecoder, decodeStringElement, (Lkotlinx/serialization/descriptors/SerialDescriptor;I)Ljava/lang/String;)
ASTORE (6)
LDC (2147483647)
ISTORE (4)
GOTO (L2)
LABEL (L1)
ALOAD (1)
ALOAD (2)
INVOKEINTERFACE (kotlinx/serialization/encoding/CompositeDecoder, decodeElementIndex, (Lkotlinx/serialization/descriptors/SerialDescriptor;)I)
ISTORE (3)
ILOAD (3)
TABLESWITCH
-1: L2
0: L3
1: L4
default: L5
LABEL (L3)
ALOAD (1)
ALOAD (2)
ICONST_0
INVOKEINTERFACE (kotlinx/serialization/encoding/CompositeDecoder, decodeStringElement, (Lkotlinx/serialization/descriptors/SerialDescriptor;I)Ljava/lang/String;)
ASTORE (5)
ILOAD (4)
ICONST_1
IOR
ISTORE (4)
GOTO (L1)
LABEL (L4)
ALOAD (1)
ALOAD (2)
ICONST_1
INVOKEINTERFACE (kotlinx/serialization/encoding/CompositeDecoder, decodeStringElement, (Lkotlinx/serialization/descriptors/SerialDescriptor;I)Ljava/lang/String;)
ASTORE (6)
ILOAD (4)
ICONST_2
IOR
ISTORE (4)
GOTO (L1)
LABEL (L2)
ALOAD (1)
ALOAD (2)
INVOKEINTERFACE (kotlinx/serialization/encoding/CompositeDecoder, endStructure, (Lkotlinx/serialization/descriptors/SerialDescriptor;)V)
NEW (User)
DUP
ILOAD (4)
ALOAD (5)
ALOAD (6)
ACONST_NULL
INVOKESPECIAL (User, <init>, (ILjava/lang/String;Ljava/lang/String;Lkotlinx/serialization/internal/SerializationConstructorMarker;)V)
ARETURN
LABEL (L5)
NEW (kotlinx/serialization/UnknownFieldException)
DUP
ILOAD (3)
INVOKESPECIAL (kotlinx/serialization/UnknownFieldException, <init>, (I)V)
CHECKCAST (java/lang/Throwable)
ATHROW
LABEL (L6)
}
public java.lang.Object deserialize(kotlinx.serialization.encoding.Decoder p0) {
LABEL (L0)
LINENUMBER (7)
ALOAD (0)
ALOAD (1)
INVOKEVIRTUAL (User$$serializer, deserialize, (Lkotlinx/serialization/encoding/Decoder;)LUser;)
ARETURN
}
public kotlinx.serialization.descriptors.SerialDescriptor getDescriptor() {
LABEL (L0)
GETSTATIC (User$$serializer, $$serialDesc, Lkotlinx/serialization/descriptors/SerialDescriptor;)
ARETURN
LABEL (L1)
}
public void serialize(kotlinx.serialization.encoding.Encoder encoder, User value) {
LABEL (L0)
ALOAD (1)
LDC (encoder)
INVOKESTATIC (kotlin/jvm/internal/Intrinsics, checkNotNullParameter, (Ljava/lang/Object;Ljava/lang/String;)V)
ALOAD (2)
LDC (value)
INVOKESTATIC (kotlin/jvm/internal/Intrinsics, checkNotNullParameter, (Ljava/lang/Object;Ljava/lang/String;)V)
GETSTATIC (User$$serializer, $$serialDesc, Lkotlinx/serialization/descriptors/SerialDescriptor;)
ASTORE (3)
ALOAD (1)
ALOAD (3)
INVOKEINTERFACE (kotlinx/serialization/encoding/Encoder, beginStructure, (Lkotlinx/serialization/descriptors/SerialDescriptor;)Lkotlinx/serialization/encoding/CompositeEncoder;)
ASTORE (1)
ALOAD (2)
ALOAD (1)
ALOAD (3)
INVOKESTATIC (User, write$Self, (LUser;Lkotlinx/serialization/encoding/CompositeEncoder;Lkotlinx/serialization/descriptors/SerialDescriptor;)V)
ALOAD (1)
ALOAD (3)
INVOKEINTERFACE (kotlinx/serialization/encoding/CompositeEncoder, endStructure, (Lkotlinx/serialization/descriptors/SerialDescriptor;)V)
RETURN
LABEL (L1)
}
public void serialize(kotlinx.serialization.encoding.Encoder p0, java.lang.Object p1) {
LABEL (L0)
LINENUMBER (7)
ALOAD (0)
ALOAD (1)
ALOAD (2)
CHECKCAST (User)
INVOKEVIRTUAL (User$$serializer, serialize, (Lkotlinx/serialization/encoding/Encoder;LUser;)V)
RETURN
}
public kotlinx.serialization.KSerializer[] typeParametersSerializers()
}
public final class User$Companion : java/lang/Object {
private void <init>() {
LABEL (L0)
LINENUMBER (7)
ALOAD (0)
INVOKESPECIAL (java/lang/Object, <init>, ()V)
RETURN
LABEL (L1)
}
public void <init>(kotlin.jvm.internal.DefaultConstructorMarker $constructor_marker) {
LABEL (L0)
LINENUMBER (7)
ALOAD (0)
INVOKESPECIAL (User$Companion, <init>, ()V)
RETURN
LABEL (L1)
}
public final kotlinx.serialization.KSerializer serializer()
}
public final class User : java/lang/Object {
public final static User$Companion Companion
private final java.lang.String firstName
private final java.lang.String lastName
static void <clinit>() {
NEW (User$Companion)
DUP
ACONST_NULL
INVOKESPECIAL (User$Companion, <init>, (Lkotlin/jvm/internal/DefaultConstructorMarker;)V)
PUTSTATIC (User, Companion, LUser$Companion;)
RETURN
}
public void <init>(java.lang.String firstName, java.lang.String lastName) {
LABEL (L0)
ALOAD (1)
LDC (firstName)
INVOKESTATIC (kotlin/jvm/internal/Intrinsics, checkNotNullParameter, (Ljava/lang/Object;Ljava/lang/String;)V)
ALOAD (2)
LDC (lastName)
INVOKESTATIC (kotlin/jvm/internal/Intrinsics, checkNotNullParameter, (Ljava/lang/Object;Ljava/lang/String;)V)
LABEL (L1)
LINENUMBER (7)
ALOAD (0)
INVOKESPECIAL (java/lang/Object, <init>, ()V)
ALOAD (0)
ALOAD (1)
PUTFIELD (User, firstName, Ljava/lang/String;)
ALOAD (0)
ALOAD (2)
PUTFIELD (User, lastName, Ljava/lang/String;)
RETURN
LABEL (L2)
}
public void <init>(int seen1, java.lang.String firstName, java.lang.String lastName, kotlinx.serialization.internal.SerializationConstructorMarker serializationConstructorMarker) {
LABEL (L0)
ICONST_3
DUP
ILOAD (1)
IAND
IF_ICMPEQ (L1)
ILOAD (1)
ICONST_3
GETSTATIC (User$$serializer, INSTANCE, LUser$$serializer;)
CHECKCAST (kotlinx/serialization/KSerializer)
INVOKEINTERFACE (kotlinx/serialization/KSerializer, getDescriptor, ()Lkotlinx/serialization/descriptors/SerialDescriptor;)
INVOKESTATIC (kotlinx/serialization/internal/PluginExceptionsKt, throwMissingFieldException, (IILkotlinx/serialization/descriptors/SerialDescriptor;)V)
LABEL (L1)
ALOAD (0)
INVOKESPECIAL (java/lang/Object, <init>, ()V)
ALOAD (0)
ALOAD (2)
PUTFIELD (User, firstName, Ljava/lang/String;)
ALOAD (0)
ALOAD (3)
PUTFIELD (User, lastName, Ljava/lang/String;)
RETURN
LABEL (L2)
}
public final java.lang.String getFirstName()
public final java.lang.String getLastName()
public final static void write$Self(User self, kotlinx.serialization.encoding.CompositeEncoder output, kotlinx.serialization.descriptors.SerialDescriptor serialDesc) {
LABEL (L0)
ALOAD (0)
LDC (self)
INVOKESTATIC (kotlin/jvm/internal/Intrinsics, checkNotNullParameter, (Ljava/lang/Object;Ljava/lang/String;)V)
ALOAD (1)
LDC (output)
INVOKESTATIC (kotlin/jvm/internal/Intrinsics, checkNotNullParameter, (Ljava/lang/Object;Ljava/lang/String;)V)
ALOAD (2)
LDC (serialDesc)
INVOKESTATIC (kotlin/jvm/internal/Intrinsics, checkNotNullParameter, (Ljava/lang/Object;Ljava/lang/String;)V)
ALOAD (1)
ALOAD (2)
ICONST_0
ALOAD (0)
GETFIELD (User, firstName, Ljava/lang/String;)
INVOKEINTERFACE (kotlinx/serialization/encoding/CompositeEncoder, encodeStringElement, (Lkotlinx/serialization/descriptors/SerialDescriptor;ILjava/lang/String;)V)
ALOAD (1)
ALOAD (2)
ICONST_1
ALOAD (0)
GETFIELD (User, lastName, Ljava/lang/String;)
INVOKEINTERFACE (kotlinx/serialization/encoding/CompositeEncoder, encodeStringElement, (Lkotlinx/serialization/descriptors/SerialDescriptor;ILjava/lang/String;)V)
RETURN
LABEL (L1)
}
}
@@ -0,0 +1,138 @@
public abstract interface A : java/lang/Object {
public abstract java.lang.String getText()
}
public final class DelegatedKt : java/lang/Object {
public final static A generateImpl()
private final static java.lang.String generateImpl$lambda$0()
}
public final class Test$$serializer : java/lang/Object, kotlinx/serialization/internal/GeneratedSerializer {
public final static Test$$serializer INSTANCE
private final static kotlinx.serialization.internal.PluginGeneratedSerialDescriptor descriptor
static void <clinit>() {
NEW (Test$$serializer)
DUP
INVOKESPECIAL (Test$$serializer, <init>, ()V)
PUTSTATIC (Test$$serializer, INSTANCE, LTest$$serializer;)
LABEL (L0)
LINENUMBER (12)
NEW (kotlinx/serialization/internal/PluginGeneratedSerialDescriptor)
DUP
LDC (Test)
GETSTATIC (Test$$serializer, INSTANCE, LTest$$serializer;)
CHECKCAST (kotlinx/serialization/internal/GeneratedSerializer)
ICONST_0
INVOKESPECIAL (kotlinx/serialization/internal/PluginGeneratedSerialDescriptor, <init>, (Ljava/lang/String;Lkotlinx/serialization/internal/GeneratedSerializer;I)V)
PUTSTATIC (Test$$serializer, descriptor, Lkotlinx/serialization/internal/PluginGeneratedSerialDescriptor;)
LABEL (L1)
LINENUMBER (13)
RETURN
}
private void <init>() {
LABEL (L0)
LINENUMBER (12)
ALOAD (0)
INVOKESPECIAL (java/lang/Object, <init>, ()V)
RETURN
LABEL (L1)
}
public kotlinx.serialization.KSerializer[] childSerializers()
public Test deserialize(kotlinx.serialization.encoding.Decoder decoder)
public java.lang.Object deserialize(kotlinx.serialization.encoding.Decoder decoder)
public kotlinx.serialization.descriptors.SerialDescriptor getDescriptor()
public void serialize(kotlinx.serialization.encoding.Encoder encoder, Test value)
public void serialize(kotlinx.serialization.encoding.Encoder encoder, java.lang.Object value)
public kotlinx.serialization.KSerializer[] typeParametersSerializers()
}
public final class Test$Companion : java/lang/Object {
private void <init>() {
LABEL (L0)
LINENUMBER (12)
ALOAD (0)
INVOKESPECIAL (java/lang/Object, <init>, ()V)
RETURN
LABEL (L1)
}
public void <init>(kotlin.jvm.internal.DefaultConstructorMarker $constructor_marker) {
LABEL (L0)
ALOAD (0)
INVOKESPECIAL (Test$Companion, <init>, ()V)
RETURN
LABEL (L1)
}
public final kotlinx.serialization.KSerializer serializer()
}
public final class Test : java/lang/Object, A {
private final A $$delegate_0
public final static Test$Companion Companion
static void <clinit>() {
NEW (Test$Companion)
DUP
ACONST_NULL
INVOKESPECIAL (Test$Companion, <init>, (Lkotlin/jvm/internal/DefaultConstructorMarker;)V)
PUTSTATIC (Test, Companion, LTest$Companion;)
RETURN
}
public void <init>() {
LABEL (L0)
LINENUMBER (12)
ALOAD (0)
INVOKESPECIAL (java/lang/Object, <init>, ()V)
LABEL (L1)
LINENUMBER (13)
ALOAD (0)
INVOKESTATIC (DelegatedKt, generateImpl, ()LA;)
PUTFIELD (Test, $$delegate_0, LA;)
RETURN
LABEL (L2)
}
public void <init>(int seen1, kotlinx.serialization.internal.SerializationConstructorMarker serializationConstructorMarker) {
LABEL (L0)
LINENUMBER (12)
ICONST_0
ILOAD (1)
IAND
IFEQ (L1)
ILOAD (1)
ICONST_0
GETSTATIC (Test$$serializer, INSTANCE, LTest$$serializer;)
INVOKEVIRTUAL (Test$$serializer, getDescriptor, ()Lkotlinx/serialization/descriptors/SerialDescriptor;)
INVOKESTATIC (kotlinx/serialization/internal/PluginExceptionsKt, throwMissingFieldException, (IILkotlinx/serialization/descriptors/SerialDescriptor;)V)
LABEL (L1)
ALOAD (0)
INVOKESPECIAL (java/lang/Object, <init>, ()V)
ALOAD (0)
LABEL (L2)
LINENUMBER (13)
INVOKESTATIC (DelegatedKt, generateImpl, ()LA;)
LABEL (L3)
LINENUMBER (12)
PUTFIELD (Test, $$delegate_0, LA;)
RETURN
LABEL (L4)
}
public java.lang.String getText()
public final static void write$Self(Test self, kotlinx.serialization.encoding.CompositeEncoder output, kotlinx.serialization.descriptors.SerialDescriptor serialDesc)
}
@@ -0,0 +1,13 @@
// CURIOUS_ABOUT <init>, <clinit>
// WITH_STDLIB
import kotlinx.serialization.*
fun interface A {
fun getText(): String
}
fun generateImpl() = A { "Hello, world!" }
@Serializable
class Test : A by generateImpl()
@@ -0,0 +1,158 @@
public abstract interface A : java/lang/Object {
public abstract java.lang.String getText()
}
final class DelegatedKt$generateImpl$1 : java/lang/Object, A {
public final static DelegatedKt$generateImpl$1 INSTANCE
static void <clinit>() {
NEW (DelegatedKt$generateImpl$1)
DUP
INVOKESPECIAL (DelegatedKt$generateImpl$1, <init>, ()V)
PUTSTATIC (DelegatedKt$generateImpl$1, INSTANCE, LDelegatedKt$generateImpl$1;)
RETURN
}
void <init>() {
ALOAD (0)
INVOKESPECIAL (java/lang/Object, <init>, ()V)
RETURN
}
public final java.lang.String getText()
}
public final class DelegatedKt : java/lang/Object {
public final static A generateImpl()
}
public final class Test$$serializer : java/lang/Object, kotlinx/serialization/internal/GeneratedSerializer {
private final static kotlinx.serialization.descriptors.SerialDescriptor $$serialDesc
public final static Test$$serializer INSTANCE
static void <clinit>() {
LABEL (L0)
LINENUMBER (13)
NEW (Test$$serializer)
DUP
INVOKESPECIAL (Test$$serializer, <init>, ()V)
ASTORE (0)
ALOAD (0)
PUTSTATIC (Test$$serializer, INSTANCE, LTest$$serializer;)
NEW (kotlinx/serialization/internal/PluginGeneratedSerialDescriptor)
DUP
LDC (Test)
GETSTATIC (Test$$serializer, INSTANCE, LTest$$serializer;)
CHECKCAST (kotlinx/serialization/internal/GeneratedSerializer)
LDC (0)
INVOKESPECIAL (kotlinx/serialization/internal/PluginGeneratedSerialDescriptor, <init>, (Ljava/lang/String;Lkotlinx/serialization/internal/GeneratedSerializer;I)V)
ASTORE (0)
ALOAD (0)
PUTSTATIC (Test$$serializer, $$serialDesc, Lkotlinx/serialization/descriptors/SerialDescriptor;)
RETURN
}
private void <init>() {
LABEL (L0)
LINENUMBER (13)
ALOAD (0)
INVOKESPECIAL (java/lang/Object, <init>, ()V)
RETURN
LABEL (L1)
}
public kotlinx.serialization.KSerializer[] childSerializers()
public Test deserialize(kotlinx.serialization.encoding.Decoder decoder)
public java.lang.Object deserialize(kotlinx.serialization.encoding.Decoder p0)
public kotlinx.serialization.descriptors.SerialDescriptor getDescriptor()
public void serialize(kotlinx.serialization.encoding.Encoder encoder, Test value)
public void serialize(kotlinx.serialization.encoding.Encoder p0, java.lang.Object p1)
public kotlinx.serialization.KSerializer[] typeParametersSerializers()
}
public final class Test$Companion : java/lang/Object {
private void <init>() {
LABEL (L0)
LINENUMBER (13)
ALOAD (0)
INVOKESPECIAL (java/lang/Object, <init>, ()V)
RETURN
LABEL (L1)
}
public void <init>(kotlin.jvm.internal.DefaultConstructorMarker $constructor_marker) {
LABEL (L0)
LINENUMBER (13)
ALOAD (0)
INVOKESPECIAL (Test$Companion, <init>, ()V)
RETURN
LABEL (L1)
}
public final kotlinx.serialization.KSerializer serializer()
}
public final class Test : java/lang/Object, A {
private final A $$delegate_0
public final static Test$Companion Companion
static void <clinit>() {
NEW (Test$Companion)
DUP
ACONST_NULL
INVOKESPECIAL (Test$Companion, <init>, (Lkotlin/jvm/internal/DefaultConstructorMarker;)V)
PUTSTATIC (Test, Companion, LTest$Companion;)
RETURN
}
public void <init>() {
LABEL (L0)
LINENUMBER (13)
ALOAD (0)
INVOKESPECIAL (java/lang/Object, <init>, ()V)
ALOAD (0)
LABEL (L1)
LINENUMBER (13)
INVOKESTATIC (DelegatedKt, generateImpl, ()LA;)
PUTFIELD (Test, $$delegate_0, LA;)
RETURN
LABEL (L2)
}
public void <init>(int seen1, kotlinx.serialization.internal.SerializationConstructorMarker serializationConstructorMarker) {
LABEL (L0)
ICONST_0
DUP
ILOAD (1)
IAND
IF_ICMPEQ (L1)
ILOAD (1)
ICONST_0
GETSTATIC (Test$$serializer, INSTANCE, LTest$$serializer;)
CHECKCAST (kotlinx/serialization/KSerializer)
INVOKEINTERFACE (kotlinx/serialization/KSerializer, getDescriptor, ()Lkotlinx/serialization/descriptors/SerialDescriptor;)
INVOKESTATIC (kotlinx/serialization/internal/PluginExceptionsKt, throwMissingFieldException, (IILkotlinx/serialization/descriptors/SerialDescriptor;)V)
LABEL (L1)
ALOAD (0)
INVOKESPECIAL (java/lang/Object, <init>, ()V)
ALOAD (0)
LABEL (L2)
LINENUMBER (13)
INVOKESTATIC (DelegatedKt, generateImpl, ()LA;)
PUTFIELD (Test, $$delegate_0, LA;)
RETURN
LABEL (L3)
}
public java.lang.String getText()
public final static void write$Self(Test self, kotlinx.serialization.encoding.CompositeEncoder output, kotlinx.serialization.descriptors.SerialDescriptor serialDesc)
}
@@ -0,0 +1,900 @@
public final class Container$$serializer : java/lang/Object, kotlinx/serialization/internal/GeneratedSerializer {
public final static Container$$serializer INSTANCE
private final static kotlinx.serialization.internal.PluginGeneratedSerialDescriptor descriptor
static void <clinit>() {
NEW (Container$$serializer)
DUP
INVOKESPECIAL (Container$$serializer, <init>, ()V)
PUTSTATIC (Container$$serializer, INSTANCE, LContainer$$serializer;)
LABEL (L0)
LINENUMBER (18)
NEW (kotlinx/serialization/internal/PluginGeneratedSerialDescriptor)
DUP
LDC (Container)
GETSTATIC (Container$$serializer, INSTANCE, LContainer$$serializer;)
CHECKCAST (kotlinx/serialization/internal/GeneratedSerializer)
ICONST_1
INVOKESPECIAL (kotlinx/serialization/internal/PluginGeneratedSerialDescriptor, <init>, (Ljava/lang/String;Lkotlinx/serialization/internal/GeneratedSerializer;I)V)
ASTORE (0)
ALOAD (0)
LDC (r)
ICONST_0
INVOKEVIRTUAL (kotlinx/serialization/internal/PluginGeneratedSerialDescriptor, addElement, (Ljava/lang/String;Z)V)
ALOAD (0)
PUTSTATIC (Container$$serializer, descriptor, Lkotlinx/serialization/internal/PluginGeneratedSerialDescriptor;)
LABEL (L1)
LINENUMBER (19)
RETURN
}
private void <init>() {
LABEL (L0)
LINENUMBER (18)
ALOAD (0)
INVOKESPECIAL (java/lang/Object, <init>, ()V)
RETURN
LABEL (L1)
}
public kotlinx.serialization.KSerializer[] childSerializers() {
LABEL (L0)
LINENUMBER (18)
ICONST_1
ANEWARRAY (kotlinx/serialization/KSerializer)
ASTORE (1)
ALOAD (1)
ICONST_0
GETSTATIC (Result, Companion, LResult$Companion;)
INVOKEVIRTUAL (Result$Companion, serializer, ()Lkotlinx/serialization/KSerializer;)
AASTORE
ALOAD (1)
ARETURN
LABEL (L1)
}
public Container deserialize(kotlinx.serialization.encoding.Decoder decoder) {
LABEL (L0)
ALOAD (1)
LDC (decoder)
INVOKESTATIC (kotlin/jvm/internal/Intrinsics, checkNotNullParameter, (Ljava/lang/Object;Ljava/lang/String;)V)
LABEL (L1)
LINENUMBER (18)
ALOAD (0)
INVOKEVIRTUAL (Container$$serializer, getDescriptor, ()Lkotlinx/serialization/descriptors/SerialDescriptor;)
ASTORE (2)
ICONST_1
ISTORE (3)
ICONST_0
ISTORE (5)
ACONST_NULL
ASTORE (6)
ALOAD (1)
ALOAD (2)
INVOKEINTERFACE (kotlinx/serialization/encoding/Decoder, beginStructure, (Lkotlinx/serialization/descriptors/SerialDescriptor;)Lkotlinx/serialization/encoding/CompositeDecoder;)
ASTORE (7)
ALOAD (7)
INVOKEINTERFACE (kotlinx/serialization/encoding/CompositeDecoder, decodeSequentially, ()Z)
IFEQ (L2)
ALOAD (7)
ALOAD (2)
ICONST_0
GETSTATIC (Result, Companion, LResult$Companion;)
INVOKEVIRTUAL (Result$Companion, serializer, ()Lkotlinx/serialization/KSerializer;)
CHECKCAST (kotlinx/serialization/DeserializationStrategy)
ALOAD (6)
INVOKEINTERFACE (kotlinx/serialization/encoding/CompositeDecoder, decodeSerializableElement, (Lkotlinx/serialization/descriptors/SerialDescriptor;ILkotlinx/serialization/DeserializationStrategy;Ljava/lang/Object;)Ljava/lang/Object;)
ASTORE (6)
ILOAD (5)
ICONST_1
IOR
ISTORE (5)
GOTO (L3)
LABEL (L2)
ILOAD (3)
IFEQ (L3)
ALOAD (7)
ALOAD (2)
INVOKEINTERFACE (kotlinx/serialization/encoding/CompositeDecoder, decodeElementIndex, (Lkotlinx/serialization/descriptors/SerialDescriptor;)I)
ISTORE (4)
ILOAD (4)
TABLESWITCH
-1: L4
0: L5
default: L6
LABEL (L4)
ICONST_0
ISTORE (3)
GOTO (L2)
LABEL (L5)
ALOAD (7)
ALOAD (2)
ICONST_0
GETSTATIC (Result, Companion, LResult$Companion;)
INVOKEVIRTUAL (Result$Companion, serializer, ()Lkotlinx/serialization/KSerializer;)
CHECKCAST (kotlinx/serialization/DeserializationStrategy)
ALOAD (6)
INVOKEINTERFACE (kotlinx/serialization/encoding/CompositeDecoder, decodeSerializableElement, (Lkotlinx/serialization/descriptors/SerialDescriptor;ILkotlinx/serialization/DeserializationStrategy;Ljava/lang/Object;)Ljava/lang/Object;)
ASTORE (6)
ILOAD (5)
ICONST_1
IOR
ISTORE (5)
GOTO (L2)
LABEL (L6)
NEW (kotlinx/serialization/UnknownFieldException)
DUP
ILOAD (4)
INVOKESPECIAL (kotlinx/serialization/UnknownFieldException, <init>, (I)V)
ATHROW
LABEL (L3)
ALOAD (7)
ALOAD (2)
INVOKEINTERFACE (kotlinx/serialization/encoding/CompositeDecoder, endStructure, (Lkotlinx/serialization/descriptors/SerialDescriptor;)V)
NEW (Container)
DUP
ILOAD (5)
ALOAD (6)
CHECKCAST (Result)
ACONST_NULL
INVOKESPECIAL (Container, <init>, (ILResult;Lkotlinx/serialization/internal/SerializationConstructorMarker;)V)
ARETURN
LABEL (L7)
}
public java.lang.Object deserialize(kotlinx.serialization.encoding.Decoder decoder) {
LABEL (L0)
LINENUMBER (18)
ALOAD (0)
ALOAD (1)
INVOKEVIRTUAL (Container$$serializer, deserialize, (Lkotlinx/serialization/encoding/Decoder;)LContainer;)
ARETURN
LABEL (L1)
}
public kotlinx.serialization.descriptors.SerialDescriptor getDescriptor()
public void serialize(kotlinx.serialization.encoding.Encoder encoder, Container value)
public void serialize(kotlinx.serialization.encoding.Encoder encoder, java.lang.Object value)
public kotlinx.serialization.KSerializer[] typeParametersSerializers()
}
public final class Container$Companion : java/lang/Object {
private void <init>() {
LABEL (L0)
LINENUMBER (18)
ALOAD (0)
INVOKESPECIAL (java/lang/Object, <init>, ()V)
RETURN
LABEL (L1)
}
public void <init>(kotlin.jvm.internal.DefaultConstructorMarker $constructor_marker) {
LABEL (L0)
ALOAD (0)
INVOKESPECIAL (Container$Companion, <init>, ()V)
RETURN
LABEL (L1)
}
public final kotlinx.serialization.KSerializer serializer() {
LABEL (L0)
LINENUMBER (18)
GETSTATIC (Container$$serializer, INSTANCE, LContainer$$serializer;)
CHECKCAST (kotlinx/serialization/KSerializer)
ARETURN
LABEL (L1)
}
}
public final class Container : java/lang/Object {
public final static Container$Companion Companion
private final Result r
static void <clinit>() {
NEW (Container$Companion)
DUP
ACONST_NULL
INVOKESPECIAL (Container$Companion, <init>, (Lkotlin/jvm/internal/DefaultConstructorMarker;)V)
PUTSTATIC (Container, Companion, LContainer$Companion;)
RETURN
}
public void <init>(Result r) {
LABEL (L0)
ALOAD (1)
LDC (r)
INVOKESTATIC (kotlin/jvm/internal/Intrinsics, checkNotNullParameter, (Ljava/lang/Object;Ljava/lang/String;)V)
LABEL (L1)
LINENUMBER (18)
ALOAD (0)
INVOKESPECIAL (java/lang/Object, <init>, ()V)
LABEL (L2)
LINENUMBER (19)
ALOAD (0)
ALOAD (1)
PUTFIELD (Container, r, LResult;)
RETURN
LABEL (L3)
}
public void <init>(int seen1, Result r, kotlinx.serialization.internal.SerializationConstructorMarker serializationConstructorMarker) {
LABEL (L0)
LINENUMBER (18)
ICONST_1
ICONST_1
ILOAD (1)
IAND
IF_ICMPEQ (L1)
ILOAD (1)
ICONST_1
GETSTATIC (Container$$serializer, INSTANCE, LContainer$$serializer;)
INVOKEVIRTUAL (Container$$serializer, getDescriptor, ()Lkotlinx/serialization/descriptors/SerialDescriptor;)
INVOKESTATIC (kotlinx/serialization/internal/PluginExceptionsKt, throwMissingFieldException, (IILkotlinx/serialization/descriptors/SerialDescriptor;)V)
LABEL (L1)
ALOAD (0)
INVOKESPECIAL (java/lang/Object, <init>, ()V)
ALOAD (0)
ALOAD (2)
PUTFIELD (Container, r, LResult;)
RETURN
LABEL (L2)
}
public final Result getR()
public final static void write$Self(Container self, kotlinx.serialization.encoding.CompositeEncoder output, kotlinx.serialization.descriptors.SerialDescriptor serialDesc) {
LABEL (L0)
ALOAD (0)
LDC (self)
INVOKESTATIC (kotlin/jvm/internal/Intrinsics, checkNotNullParameter, (Ljava/lang/Object;Ljava/lang/String;)V)
ALOAD (1)
LDC (output)
INVOKESTATIC (kotlin/jvm/internal/Intrinsics, checkNotNullParameter, (Ljava/lang/Object;Ljava/lang/String;)V)
ALOAD (2)
LDC (serialDesc)
INVOKESTATIC (kotlin/jvm/internal/Intrinsics, checkNotNullParameter, (Ljava/lang/Object;Ljava/lang/String;)V)
LABEL (L1)
LINENUMBER (18)
ALOAD (1)
ALOAD (2)
ICONST_0
GETSTATIC (Result, Companion, LResult$Companion;)
INVOKEVIRTUAL (Result$Companion, serializer, ()Lkotlinx/serialization/KSerializer;)
CHECKCAST (kotlinx/serialization/SerializationStrategy)
ALOAD (0)
GETFIELD (Container, r, LResult;)
INVOKEINTERFACE (kotlinx/serialization/encoding/CompositeEncoder, encodeSerializableElement, (Lkotlinx/serialization/descriptors/SerialDescriptor;ILkotlinx/serialization/SerializationStrategy;Ljava/lang/Object;)V)
RETURN
LABEL (L2)
}
}
final class Result$Companion$$cachedSerializer$delegate$1 : kotlin/jvm/internal/Lambda, kotlin/jvm/functions/Function0 {
public final static Result$Companion$$cachedSerializer$delegate$1 INSTANCE
static void <clinit>() {
NEW (Result$Companion$$cachedSerializer$delegate$1)
DUP
INVOKESPECIAL (Result$Companion$$cachedSerializer$delegate$1, <init>, ()V)
PUTSTATIC (Result$Companion$$cachedSerializer$delegate$1, INSTANCE, LResult$Companion$$cachedSerializer$delegate$1;)
RETURN
}
void <init>() {
LABEL (L0)
ALOAD (0)
ICONST_0
INVOKESPECIAL (kotlin/jvm/internal/Lambda, <init>, (I)V)
RETURN
LABEL (L1)
}
public final kotlinx.serialization.KSerializer invoke() {
LABEL (L0)
LINENUMBER (12)
NEW (kotlinx/serialization/SealedClassSerializer)
DUP
LDC (Result)
LDC (LResult;)
INVOKESTATIC (kotlin/jvm/internal/Reflection, getOrCreateKotlinClass, (Ljava/lang/Class;)Lkotlin/reflect/KClass;)
ICONST_2
ANEWARRAY (kotlin/reflect/KClass)
ASTORE (1)
ALOAD (1)
ICONST_0
LDC (LResult$Err;)
INVOKESTATIC (kotlin/jvm/internal/Reflection, getOrCreateKotlinClass, (Ljava/lang/Class;)Lkotlin/reflect/KClass;)
AASTORE
ALOAD (1)
ICONST_1
LDC (LResult$OK;)
INVOKESTATIC (kotlin/jvm/internal/Reflection, getOrCreateKotlinClass, (Ljava/lang/Class;)Lkotlin/reflect/KClass;)
AASTORE
ALOAD (1)
ICONST_2
ANEWARRAY (kotlinx/serialization/KSerializer)
ASTORE (1)
ALOAD (1)
ICONST_0
NEW (kotlinx/serialization/internal/ObjectSerializer)
DUP
LDC (Result.Err)
GETSTATIC (Result$Err, INSTANCE, LResult$Err;)
ICONST_0
ANEWARRAY (java/lang/annotation/Annotation)
INVOKESPECIAL (kotlinx/serialization/internal/ObjectSerializer, <init>, (Ljava/lang/String;Ljava/lang/Object;[Ljava/lang/annotation/Annotation;)V)
CHECKCAST (kotlinx/serialization/KSerializer)
AASTORE
ALOAD (1)
ICONST_1
GETSTATIC (Result$OK$$serializer, INSTANCE, LResult$OK$$serializer;)
CHECKCAST (kotlinx/serialization/KSerializer)
AASTORE
ALOAD (1)
ICONST_0
ANEWARRAY (java/lang/annotation/Annotation)
INVOKESPECIAL (kotlinx/serialization/SealedClassSerializer, <init>, (Ljava/lang/String;Lkotlin/reflect/KClass;[Lkotlin/reflect/KClass;[Lkotlinx/serialization/KSerializer;[Ljava/lang/annotation/Annotation;)V)
CHECKCAST (kotlinx/serialization/KSerializer)
ARETURN
LABEL (L1)
}
public java.lang.Object invoke() {
LABEL (L0)
LINENUMBER (12)
ALOAD (0)
INVOKEVIRTUAL (Result$Companion$$cachedSerializer$delegate$1, invoke, ()Lkotlinx/serialization/KSerializer;)
ARETURN
LABEL (L1)
}
}
public final class Result$Companion : java/lang/Object {
private void <init>() {
LABEL (L0)
LINENUMBER (12)
ALOAD (0)
INVOKESPECIAL (java/lang/Object, <init>, ()V)
RETURN
LABEL (L1)
}
public void <init>(kotlin.jvm.internal.DefaultConstructorMarker $constructor_marker) {
LABEL (L0)
ALOAD (0)
INVOKESPECIAL (Result$Companion, <init>, ()V)
RETURN
LABEL (L1)
}
private final kotlin.Lazy get$cachedSerializer$delegate()
public final kotlinx.serialization.KSerializer serializer() {
LABEL (L0)
LINENUMBER (12)
ALOAD (0)
INVOKESPECIAL (Result$Companion, get$cachedSerializer$delegate, ()Lkotlin/Lazy;)
INVOKEINTERFACE (kotlin/Lazy, getValue, ()Ljava/lang/Object;)
CHECKCAST (kotlinx/serialization/KSerializer)
ARETURN
LABEL (L1)
}
}
final class Result$Err$$cachedSerializer$delegate$1 : kotlin/jvm/internal/Lambda, kotlin/jvm/functions/Function0 {
public final static Result$Err$$cachedSerializer$delegate$1 INSTANCE
static void <clinit>() {
NEW (Result$Err$$cachedSerializer$delegate$1)
DUP
INVOKESPECIAL (Result$Err$$cachedSerializer$delegate$1, <init>, ()V)
PUTSTATIC (Result$Err$$cachedSerializer$delegate$1, INSTANCE, LResult$Err$$cachedSerializer$delegate$1;)
RETURN
}
void <init>() {
LABEL (L0)
ALOAD (0)
ICONST_0
INVOKESPECIAL (kotlin/jvm/internal/Lambda, <init>, (I)V)
RETURN
LABEL (L1)
}
public final kotlinx.serialization.KSerializer invoke() {
LABEL (L0)
LINENUMBER (15)
NEW (kotlinx/serialization/internal/ObjectSerializer)
DUP
LDC (Result.Err)
GETSTATIC (Result$Err, INSTANCE, LResult$Err;)
ICONST_0
ANEWARRAY (java/lang/annotation/Annotation)
INVOKESPECIAL (kotlinx/serialization/internal/ObjectSerializer, <init>, (Ljava/lang/String;Ljava/lang/Object;[Ljava/lang/annotation/Annotation;)V)
CHECKCAST (kotlinx/serialization/KSerializer)
ARETURN
LABEL (L1)
}
public java.lang.Object invoke() {
LABEL (L0)
LINENUMBER (15)
ALOAD (0)
INVOKEVIRTUAL (Result$Err$$cachedSerializer$delegate$1, invoke, ()Lkotlinx/serialization/KSerializer;)
ARETURN
LABEL (L1)
}
}
public final class Result$Err : Result {
private final static kotlin.Lazy $cachedSerializer$delegate
public final static Result$Err INSTANCE
static void <clinit>() {
NEW (Result$Err)
DUP
INVOKESPECIAL (Result$Err, <init>, ()V)
PUTSTATIC (Result$Err, INSTANCE, LResult$Err;)
LABEL (L0)
LINENUMBER (15)
GETSTATIC (kotlin/LazyThreadSafetyMode, PUBLICATION, Lkotlin/LazyThreadSafetyMode;)
GETSTATIC (Result$Err$$cachedSerializer$delegate$1, INSTANCE, LResult$Err$$cachedSerializer$delegate$1;)
CHECKCAST (kotlin/jvm/functions/Function0)
INVOKESTATIC (kotlin/LazyKt, lazy, (Lkotlin/LazyThreadSafetyMode;Lkotlin/jvm/functions/Function0;)Lkotlin/Lazy;)
PUTSTATIC (Result$Err, $cachedSerializer$delegate, Lkotlin/Lazy;)
RETURN
}
private void <init>() {
LABEL (L0)
LINENUMBER (15)
ALOAD (0)
ACONST_NULL
INVOKESPECIAL (Result, <init>, (Lkotlin/jvm/internal/DefaultConstructorMarker;)V)
RETURN
LABEL (L1)
}
private final kotlin.Lazy get$cachedSerializer$delegate()
public final kotlinx.serialization.KSerializer serializer() {
LABEL (L0)
LINENUMBER (15)
ALOAD (0)
INVOKESPECIAL (Result$Err, get$cachedSerializer$delegate, ()Lkotlin/Lazy;)
INVOKEINTERFACE (kotlin/Lazy, getValue, ()Ljava/lang/Object;)
CHECKCAST (kotlinx/serialization/KSerializer)
ARETURN
LABEL (L1)
}
}
public final class Result$OK$$serializer : java/lang/Object, kotlinx/serialization/internal/GeneratedSerializer {
public final static Result$OK$$serializer INSTANCE
private final static kotlinx.serialization.internal.PluginGeneratedSerialDescriptor descriptor
static void <clinit>() {
NEW (Result$OK$$serializer)
DUP
INVOKESPECIAL (Result$OK$$serializer, <init>, ()V)
PUTSTATIC (Result$OK$$serializer, INSTANCE, LResult$OK$$serializer;)
LABEL (L0)
LINENUMBER (14)
NEW (kotlinx/serialization/internal/PluginGeneratedSerialDescriptor)
DUP
LDC (Result.OK)
GETSTATIC (Result$OK$$serializer, INSTANCE, LResult$OK$$serializer;)
CHECKCAST (kotlinx/serialization/internal/GeneratedSerializer)
ICONST_1
INVOKESPECIAL (kotlinx/serialization/internal/PluginGeneratedSerialDescriptor, <init>, (Ljava/lang/String;Lkotlinx/serialization/internal/GeneratedSerializer;I)V)
ASTORE (0)
ALOAD (0)
LDC (s)
ICONST_0
INVOKEVIRTUAL (kotlinx/serialization/internal/PluginGeneratedSerialDescriptor, addElement, (Ljava/lang/String;Z)V)
ALOAD (0)
PUTSTATIC (Result$OK$$serializer, descriptor, Lkotlinx/serialization/internal/PluginGeneratedSerialDescriptor;)
RETURN
}
private void <init>() {
LABEL (L0)
LINENUMBER (14)
ALOAD (0)
INVOKESPECIAL (java/lang/Object, <init>, ()V)
RETURN
LABEL (L1)
}
public kotlinx.serialization.KSerializer[] childSerializers() {
LABEL (L0)
LINENUMBER (14)
ICONST_1
ANEWARRAY (kotlinx/serialization/KSerializer)
ASTORE (1)
ALOAD (1)
ICONST_0
GETSTATIC (kotlinx/serialization/internal/StringSerializer, INSTANCE, Lkotlinx/serialization/internal/StringSerializer;)
CHECKCAST (kotlinx/serialization/KSerializer)
AASTORE
ALOAD (1)
ARETURN
LABEL (L1)
}
public Result$OK deserialize(kotlinx.serialization.encoding.Decoder decoder) {
LABEL (L0)
ALOAD (1)
LDC (decoder)
INVOKESTATIC (kotlin/jvm/internal/Intrinsics, checkNotNullParameter, (Ljava/lang/Object;Ljava/lang/String;)V)
LABEL (L1)
LINENUMBER (14)
ALOAD (0)
INVOKEVIRTUAL (Result$OK$$serializer, getDescriptor, ()Lkotlinx/serialization/descriptors/SerialDescriptor;)
ASTORE (2)
ICONST_1
ISTORE (3)
ICONST_0
ISTORE (5)
ACONST_NULL
ASTORE (6)
ALOAD (1)
ALOAD (2)
INVOKEINTERFACE (kotlinx/serialization/encoding/Decoder, beginStructure, (Lkotlinx/serialization/descriptors/SerialDescriptor;)Lkotlinx/serialization/encoding/CompositeDecoder;)
ASTORE (7)
ALOAD (7)
INVOKEINTERFACE (kotlinx/serialization/encoding/CompositeDecoder, decodeSequentially, ()Z)
IFEQ (L2)
ALOAD (7)
ALOAD (2)
ICONST_0
INVOKEINTERFACE (kotlinx/serialization/encoding/CompositeDecoder, decodeStringElement, (Lkotlinx/serialization/descriptors/SerialDescriptor;I)Ljava/lang/String;)
ASTORE (6)
ILOAD (5)
ICONST_1
IOR
ISTORE (5)
GOTO (L3)
LABEL (L2)
ILOAD (3)
IFEQ (L3)
ALOAD (7)
ALOAD (2)
INVOKEINTERFACE (kotlinx/serialization/encoding/CompositeDecoder, decodeElementIndex, (Lkotlinx/serialization/descriptors/SerialDescriptor;)I)
ISTORE (4)
ILOAD (4)
TABLESWITCH
-1: L4
0: L5
default: L6
LABEL (L4)
ICONST_0
ISTORE (3)
GOTO (L2)
LABEL (L5)
ALOAD (7)
ALOAD (2)
ICONST_0
INVOKEINTERFACE (kotlinx/serialization/encoding/CompositeDecoder, decodeStringElement, (Lkotlinx/serialization/descriptors/SerialDescriptor;I)Ljava/lang/String;)
ASTORE (6)
ILOAD (5)
ICONST_1
IOR
ISTORE (5)
GOTO (L2)
LABEL (L6)
NEW (kotlinx/serialization/UnknownFieldException)
DUP
ILOAD (4)
INVOKESPECIAL (kotlinx/serialization/UnknownFieldException, <init>, (I)V)
ATHROW
LABEL (L3)
ALOAD (7)
ALOAD (2)
INVOKEINTERFACE (kotlinx/serialization/encoding/CompositeDecoder, endStructure, (Lkotlinx/serialization/descriptors/SerialDescriptor;)V)
NEW (Result$OK)
DUP
ILOAD (5)
ALOAD (6)
ACONST_NULL
INVOKESPECIAL (Result$OK, <init>, (ILjava/lang/String;Lkotlinx/serialization/internal/SerializationConstructorMarker;)V)
ARETURN
LABEL (L7)
}
public java.lang.Object deserialize(kotlinx.serialization.encoding.Decoder decoder) {
LABEL (L0)
LINENUMBER (14)
ALOAD (0)
ALOAD (1)
INVOKEVIRTUAL (Result$OK$$serializer, deserialize, (Lkotlinx/serialization/encoding/Decoder;)LResult$OK;)
ARETURN
LABEL (L1)
}
public kotlinx.serialization.descriptors.SerialDescriptor getDescriptor()
public void serialize(kotlinx.serialization.encoding.Encoder encoder, Result$OK value)
public void serialize(kotlinx.serialization.encoding.Encoder encoder, java.lang.Object value)
public kotlinx.serialization.KSerializer[] typeParametersSerializers()
}
public final class Result$OK$Companion : java/lang/Object {
private void <init>() {
LABEL (L0)
LINENUMBER (14)
ALOAD (0)
INVOKESPECIAL (java/lang/Object, <init>, ()V)
RETURN
LABEL (L1)
}
public void <init>(kotlin.jvm.internal.DefaultConstructorMarker $constructor_marker) {
LABEL (L0)
ALOAD (0)
INVOKESPECIAL (Result$OK$Companion, <init>, ()V)
RETURN
LABEL (L1)
}
public final kotlinx.serialization.KSerializer serializer() {
LABEL (L0)
LINENUMBER (14)
GETSTATIC (Result$OK$$serializer, INSTANCE, LResult$OK$$serializer;)
CHECKCAST (kotlinx/serialization/KSerializer)
ARETURN
LABEL (L1)
}
}
public final class Result$OK : Result {
public final static Result$OK$Companion Companion
private final java.lang.String s
static void <clinit>() {
NEW (Result$OK$Companion)
DUP
ACONST_NULL
INVOKESPECIAL (Result$OK$Companion, <init>, (Lkotlin/jvm/internal/DefaultConstructorMarker;)V)
PUTSTATIC (Result$OK, Companion, LResult$OK$Companion;)
RETURN
}
public void <init>(java.lang.String s) {
LABEL (L0)
ALOAD (1)
LDC (s)
INVOKESTATIC (kotlin/jvm/internal/Intrinsics, checkNotNullParameter, (Ljava/lang/Object;Ljava/lang/String;)V)
LABEL (L1)
LINENUMBER (14)
ALOAD (0)
ACONST_NULL
INVOKESPECIAL (Result, <init>, (Lkotlin/jvm/internal/DefaultConstructorMarker;)V)
ALOAD (0)
ALOAD (1)
PUTFIELD (Result$OK, s, Ljava/lang/String;)
RETURN
LABEL (L2)
}
public void <init>(int seen1, java.lang.String s, kotlinx.serialization.internal.SerializationConstructorMarker serializationConstructorMarker) {
LABEL (L0)
LINENUMBER (14)
ICONST_1
ICONST_1
ILOAD (1)
IAND
IF_ICMPEQ (L1)
ILOAD (1)
ICONST_1
GETSTATIC (Result$OK$$serializer, INSTANCE, LResult$OK$$serializer;)
INVOKEVIRTUAL (Result$OK$$serializer, getDescriptor, ()Lkotlinx/serialization/descriptors/SerialDescriptor;)
INVOKESTATIC (kotlinx/serialization/internal/PluginExceptionsKt, throwMissingFieldException, (IILkotlinx/serialization/descriptors/SerialDescriptor;)V)
LABEL (L1)
ALOAD (0)
ILOAD (1)
ALOAD (3)
INVOKESPECIAL (Result, <init>, (ILkotlinx/serialization/internal/SerializationConstructorMarker;)V)
ALOAD (0)
ALOAD (2)
PUTFIELD (Result$OK, s, Ljava/lang/String;)
RETURN
LABEL (L2)
}
public final java.lang.String getS()
public final static void write$Self(Result$OK self, kotlinx.serialization.encoding.CompositeEncoder output, kotlinx.serialization.descriptors.SerialDescriptor serialDesc) {
LABEL (L0)
ALOAD (0)
LDC (self)
INVOKESTATIC (kotlin/jvm/internal/Intrinsics, checkNotNullParameter, (Ljava/lang/Object;Ljava/lang/String;)V)
ALOAD (1)
LDC (output)
INVOKESTATIC (kotlin/jvm/internal/Intrinsics, checkNotNullParameter, (Ljava/lang/Object;Ljava/lang/String;)V)
ALOAD (2)
LDC (serialDesc)
INVOKESTATIC (kotlin/jvm/internal/Intrinsics, checkNotNullParameter, (Ljava/lang/Object;Ljava/lang/String;)V)
LABEL (L1)
LINENUMBER (14)
ALOAD (0)
CHECKCAST (Result)
ALOAD (1)
ALOAD (2)
INVOKESTATIC (Result, write$Self, (LResult;Lkotlinx/serialization/encoding/CompositeEncoder;Lkotlinx/serialization/descriptors/SerialDescriptor;)V)
ALOAD (1)
ALOAD (2)
ICONST_0
ALOAD (0)
GETFIELD (Result$OK, s, Ljava/lang/String;)
INVOKEINTERFACE (kotlinx/serialization/encoding/CompositeEncoder, encodeStringElement, (Lkotlinx/serialization/descriptors/SerialDescriptor;ILjava/lang/String;)V)
RETURN
LABEL (L2)
}
}
public abstract class Result : java/lang/Object, X {
private final static kotlin.Lazy $cachedSerializer$delegate
public final static Result$Companion Companion
static void <clinit>() {
NEW (Result$Companion)
DUP
ACONST_NULL
INVOKESPECIAL (Result$Companion, <init>, (Lkotlin/jvm/internal/DefaultConstructorMarker;)V)
PUTSTATIC (Result, Companion, LResult$Companion;)
LABEL (L0)
LINENUMBER (12)
GETSTATIC (kotlin/LazyThreadSafetyMode, PUBLICATION, Lkotlin/LazyThreadSafetyMode;)
GETSTATIC (Result$Companion$$cachedSerializer$delegate$1, INSTANCE, LResult$Companion$$cachedSerializer$delegate$1;)
CHECKCAST (kotlin/jvm/functions/Function0)
INVOKESTATIC (kotlin/LazyKt, lazy, (Lkotlin/LazyThreadSafetyMode;Lkotlin/jvm/functions/Function0;)Lkotlin/Lazy;)
PUTSTATIC (Result, $cachedSerializer$delegate, Lkotlin/Lazy;)
RETURN
}
private void <init>() {
LABEL (L0)
LINENUMBER (12)
ALOAD (0)
INVOKESPECIAL (java/lang/Object, <init>, ()V)
LABEL (L1)
LINENUMBER (13)
RETURN
LABEL (L2)
}
public void <init>(int seen1, kotlinx.serialization.internal.SerializationConstructorMarker serializationConstructorMarker) {
LABEL (L0)
LINENUMBER (12)
ALOAD (0)
INVOKESPECIAL (java/lang/Object, <init>, ()V)
RETURN
LABEL (L1)
}
public void <init>(kotlin.jvm.internal.DefaultConstructorMarker $constructor_marker) {
LABEL (L0)
ALOAD (0)
INVOKESPECIAL (Result, <init>, ()V)
RETURN
LABEL (L1)
}
public final static kotlin.Lazy access$get$cachedSerializer$delegate$cp()
public void def()
public final static void write$Self(Result self, kotlinx.serialization.encoding.CompositeEncoder output, kotlinx.serialization.descriptors.SerialDescriptor serialDesc) {
LABEL (L0)
ALOAD (0)
LDC (self)
INVOKESTATIC (kotlin/jvm/internal/Intrinsics, checkNotNullParameter, (Ljava/lang/Object;Ljava/lang/String;)V)
ALOAD (1)
LDC (output)
INVOKESTATIC (kotlin/jvm/internal/Intrinsics, checkNotNullParameter, (Ljava/lang/Object;Ljava/lang/String;)V)
ALOAD (2)
LDC (serialDesc)
INVOKESTATIC (kotlin/jvm/internal/Intrinsics, checkNotNullParameter, (Ljava/lang/Object;Ljava/lang/String;)V)
RETURN
LABEL (L1)
}
}
public final class X$Companion : java/lang/Object {
final static X$Companion $$INSTANCE
static void <clinit>() {
NEW (X$Companion)
DUP
INVOKESPECIAL (X$Companion, <init>, ()V)
PUTSTATIC (X$Companion, $$INSTANCE, LX$Companion;)
RETURN
}
private void <init>() {
LABEL (L0)
LINENUMBER (6)
ALOAD (0)
INVOKESPECIAL (java/lang/Object, <init>, ()V)
RETURN
LABEL (L1)
}
public final kotlinx.serialization.KSerializer serializer() {
LABEL (L0)
LINENUMBER (6)
NEW (kotlinx/serialization/SealedClassSerializer)
DUP
LDC (X)
LDC (LX;)
INVOKESTATIC (kotlin/jvm/internal/Reflection, getOrCreateKotlinClass, (Ljava/lang/Class;)Lkotlin/reflect/KClass;)
ICONST_2
ANEWARRAY (kotlin/reflect/KClass)
ASTORE (1)
ALOAD (1)
ICONST_0
LDC (LResult$Err;)
INVOKESTATIC (kotlin/jvm/internal/Reflection, getOrCreateKotlinClass, (Ljava/lang/Class;)Lkotlin/reflect/KClass;)
AASTORE
ALOAD (1)
ICONST_1
LDC (LResult$OK;)
INVOKESTATIC (kotlin/jvm/internal/Reflection, getOrCreateKotlinClass, (Ljava/lang/Class;)Lkotlin/reflect/KClass;)
AASTORE
ALOAD (1)
ICONST_2
ANEWARRAY (kotlinx/serialization/KSerializer)
ASTORE (1)
ALOAD (1)
ICONST_0
NEW (kotlinx/serialization/internal/ObjectSerializer)
DUP
LDC (Result.Err)
GETSTATIC (Result$Err, INSTANCE, LResult$Err;)
ICONST_0
ANEWARRAY (java/lang/annotation/Annotation)
INVOKESPECIAL (kotlinx/serialization/internal/ObjectSerializer, <init>, (Ljava/lang/String;Ljava/lang/Object;[Ljava/lang/annotation/Annotation;)V)
CHECKCAST (kotlinx/serialization/KSerializer)
AASTORE
ALOAD (1)
ICONST_1
GETSTATIC (Result$OK$$serializer, INSTANCE, LResult$OK$$serializer;)
CHECKCAST (kotlinx/serialization/KSerializer)
AASTORE
ALOAD (1)
ICONST_0
ANEWARRAY (java/lang/annotation/Annotation)
INVOKESPECIAL (kotlinx/serialization/SealedClassSerializer, <init>, (Ljava/lang/String;Lkotlin/reflect/KClass;[Lkotlin/reflect/KClass;[Lkotlinx/serialization/KSerializer;[Ljava/lang/annotation/Annotation;)V)
CHECKCAST (kotlinx/serialization/KSerializer)
ARETURN
LABEL (L1)
}
}
public final class X$DefaultImpls : java/lang/Object {
public static void def(X $this)
}
public abstract interface X : java/lang/Object {
public final static X$Companion Companion
static void <clinit>() {
GETSTATIC (X$Companion, $$INSTANCE, LX$Companion;)
PUTSTATIC (X, Companion, LX$Companion;)
RETURN
}
public abstract void def()
}
@@ -0,0 +1,19 @@
// CURIOUS_ABOUT deserialize, write$Self, childSerializers, <init>, <clinit>, invoke, serializer
// WITH_STDLIB
import kotlinx.serialization.*
@Serializable
sealed interface X {
fun def() {}
}
// do not forget to update this test with custom serialinfo annotation when serialization 1.3.0 is released
@Serializable
sealed class Result: X {
@Serializable class OK(val s: String): Result()
@Serializable object Err: Result()
}
@Serializable
class Container(val r: Result)
@@ -0,0 +1,956 @@
public final class Container$$serializer : java/lang/Object, kotlinx/serialization/internal/GeneratedSerializer {
private final static kotlinx.serialization.descriptors.SerialDescriptor $$serialDesc
public final static Container$$serializer INSTANCE
static void <clinit>() {
LABEL (L0)
LINENUMBER (19)
NEW (Container$$serializer)
DUP
INVOKESPECIAL (Container$$serializer, <init>, ()V)
ASTORE (0)
ALOAD (0)
PUTSTATIC (Container$$serializer, INSTANCE, LContainer$$serializer;)
NEW (kotlinx/serialization/internal/PluginGeneratedSerialDescriptor)
DUP
LDC (Container)
GETSTATIC (Container$$serializer, INSTANCE, LContainer$$serializer;)
CHECKCAST (kotlinx/serialization/internal/GeneratedSerializer)
LDC (1)
INVOKESPECIAL (kotlinx/serialization/internal/PluginGeneratedSerialDescriptor, <init>, (Ljava/lang/String;Lkotlinx/serialization/internal/GeneratedSerializer;I)V)
ASTORE (0)
ALOAD (0)
LDC (r)
ICONST_0
INVOKEVIRTUAL (kotlinx/serialization/internal/PluginGeneratedSerialDescriptor, addElement, (Ljava/lang/String;Z)V)
ALOAD (0)
PUTSTATIC (Container$$serializer, $$serialDesc, Lkotlinx/serialization/descriptors/SerialDescriptor;)
RETURN
}
private void <init>() {
LABEL (L0)
LINENUMBER (19)
ALOAD (0)
INVOKESPECIAL (java/lang/Object, <init>, ()V)
RETURN
LABEL (L1)
}
public kotlinx.serialization.KSerializer[] childSerializers() {
LABEL (L0)
ICONST_1
ANEWARRAY (kotlinx/serialization/KSerializer)
DUP
ICONST_0
NEW (kotlinx/serialization/SealedClassSerializer)
DUP
LDC (Result)
LDC (LResult;)
INVOKESTATIC (kotlin/jvm/internal/Reflection, getOrCreateKotlinClass, (Ljava/lang/Class;)Lkotlin/reflect/KClass;)
ICONST_2
ANEWARRAY (kotlin/reflect/KClass)
DUP
ICONST_0
LDC (LResult$Err;)
INVOKESTATIC (kotlin/jvm/internal/Reflection, getOrCreateKotlinClass, (Ljava/lang/Class;)Lkotlin/reflect/KClass;)
AASTORE
DUP
ICONST_1
LDC (LResult$OK;)
INVOKESTATIC (kotlin/jvm/internal/Reflection, getOrCreateKotlinClass, (Ljava/lang/Class;)Lkotlin/reflect/KClass;)
AASTORE
ICONST_2
ANEWARRAY (kotlinx/serialization/KSerializer)
DUP
ICONST_0
NEW (kotlinx/serialization/internal/ObjectSerializer)
DUP
LDC (Result.Err)
GETSTATIC (Result$Err, INSTANCE, LResult$Err;)
INVOKESPECIAL (kotlinx/serialization/internal/ObjectSerializer, <init>, (Ljava/lang/String;Ljava/lang/Object;)V)
AASTORE
DUP
ICONST_1
GETSTATIC (Result$OK$$serializer, INSTANCE, LResult$OK$$serializer;)
CHECKCAST (kotlinx/serialization/KSerializer)
AASTORE
INVOKESPECIAL (kotlinx/serialization/SealedClassSerializer, <init>, (Ljava/lang/String;Lkotlin/reflect/KClass;[Lkotlin/reflect/KClass;[Lkotlinx/serialization/KSerializer;)V)
AASTORE
ARETURN
LABEL (L1)
}
public Container deserialize(kotlinx.serialization.encoding.Decoder decoder) {
LABEL (L0)
ALOAD (1)
LDC (decoder)
INVOKESTATIC (kotlin/jvm/internal/Intrinsics, checkNotNullParameter, (Ljava/lang/Object;Ljava/lang/String;)V)
GETSTATIC (Container$$serializer, $$serialDesc, Lkotlinx/serialization/descriptors/SerialDescriptor;)
ASTORE (2)
ICONST_0
ISTORE (4)
ACONST_NULL
ASTORE (5)
ALOAD (1)
ALOAD (2)
INVOKEINTERFACE (kotlinx/serialization/encoding/Decoder, beginStructure, (Lkotlinx/serialization/descriptors/SerialDescriptor;)Lkotlinx/serialization/encoding/CompositeDecoder;)
ASTORE (1)
ALOAD (1)
INVOKEINTERFACE (kotlinx/serialization/encoding/CompositeDecoder, decodeSequentially, ()Z)
IFEQ (L1)
ALOAD (1)
ALOAD (2)
ICONST_0
NEW (kotlinx/serialization/SealedClassSerializer)
DUP
LDC (Result)
LDC (LResult;)
INVOKESTATIC (kotlin/jvm/internal/Reflection, getOrCreateKotlinClass, (Ljava/lang/Class;)Lkotlin/reflect/KClass;)
ICONST_2
ANEWARRAY (kotlin/reflect/KClass)
DUP
ICONST_0
LDC (LResult$Err;)
INVOKESTATIC (kotlin/jvm/internal/Reflection, getOrCreateKotlinClass, (Ljava/lang/Class;)Lkotlin/reflect/KClass;)
AASTORE
DUP
ICONST_1
LDC (LResult$OK;)
INVOKESTATIC (kotlin/jvm/internal/Reflection, getOrCreateKotlinClass, (Ljava/lang/Class;)Lkotlin/reflect/KClass;)
AASTORE
ICONST_2
ANEWARRAY (kotlinx/serialization/KSerializer)
DUP
ICONST_0
NEW (kotlinx/serialization/internal/ObjectSerializer)
DUP
LDC (Result.Err)
GETSTATIC (Result$Err, INSTANCE, LResult$Err;)
INVOKESPECIAL (kotlinx/serialization/internal/ObjectSerializer, <init>, (Ljava/lang/String;Ljava/lang/Object;)V)
AASTORE
DUP
ICONST_1
GETSTATIC (Result$OK$$serializer, INSTANCE, LResult$OK$$serializer;)
CHECKCAST (kotlinx/serialization/KSerializer)
AASTORE
INVOKESPECIAL (kotlinx/serialization/SealedClassSerializer, <init>, (Ljava/lang/String;Lkotlin/reflect/KClass;[Lkotlin/reflect/KClass;[Lkotlinx/serialization/KSerializer;)V)
ALOAD (5)
INVOKEINTERFACE (kotlinx/serialization/encoding/CompositeDecoder, decodeSerializableElement, (Lkotlinx/serialization/descriptors/SerialDescriptor;ILkotlinx/serialization/DeserializationStrategy;Ljava/lang/Object;)Ljava/lang/Object;)
CHECKCAST (Result)
ASTORE (5)
LDC (2147483647)
ISTORE (4)
GOTO (L2)
LABEL (L1)
ALOAD (1)
ALOAD (2)
INVOKEINTERFACE (kotlinx/serialization/encoding/CompositeDecoder, decodeElementIndex, (Lkotlinx/serialization/descriptors/SerialDescriptor;)I)
ISTORE (3)
ILOAD (3)
TABLESWITCH
-1: L2
0: L3
default: L4
LABEL (L3)
ALOAD (1)
ALOAD (2)
ICONST_0
NEW (kotlinx/serialization/SealedClassSerializer)
DUP
LDC (Result)
LDC (LResult;)
INVOKESTATIC (kotlin/jvm/internal/Reflection, getOrCreateKotlinClass, (Ljava/lang/Class;)Lkotlin/reflect/KClass;)
ICONST_2
ANEWARRAY (kotlin/reflect/KClass)
DUP
ICONST_0
LDC (LResult$Err;)
INVOKESTATIC (kotlin/jvm/internal/Reflection, getOrCreateKotlinClass, (Ljava/lang/Class;)Lkotlin/reflect/KClass;)
AASTORE
DUP
ICONST_1
LDC (LResult$OK;)
INVOKESTATIC (kotlin/jvm/internal/Reflection, getOrCreateKotlinClass, (Ljava/lang/Class;)Lkotlin/reflect/KClass;)
AASTORE
ICONST_2
ANEWARRAY (kotlinx/serialization/KSerializer)
DUP
ICONST_0
NEW (kotlinx/serialization/internal/ObjectSerializer)
DUP
LDC (Result.Err)
GETSTATIC (Result$Err, INSTANCE, LResult$Err;)
INVOKESPECIAL (kotlinx/serialization/internal/ObjectSerializer, <init>, (Ljava/lang/String;Ljava/lang/Object;)V)
AASTORE
DUP
ICONST_1
GETSTATIC (Result$OK$$serializer, INSTANCE, LResult$OK$$serializer;)
CHECKCAST (kotlinx/serialization/KSerializer)
AASTORE
INVOKESPECIAL (kotlinx/serialization/SealedClassSerializer, <init>, (Ljava/lang/String;Lkotlin/reflect/KClass;[Lkotlin/reflect/KClass;[Lkotlinx/serialization/KSerializer;)V)
ALOAD (5)
INVOKEINTERFACE (kotlinx/serialization/encoding/CompositeDecoder, decodeSerializableElement, (Lkotlinx/serialization/descriptors/SerialDescriptor;ILkotlinx/serialization/DeserializationStrategy;Ljava/lang/Object;)Ljava/lang/Object;)
CHECKCAST (Result)
ASTORE (5)
ILOAD (4)
ICONST_1
IOR
ISTORE (4)
GOTO (L1)
LABEL (L2)
ALOAD (1)
ALOAD (2)
INVOKEINTERFACE (kotlinx/serialization/encoding/CompositeDecoder, endStructure, (Lkotlinx/serialization/descriptors/SerialDescriptor;)V)
NEW (Container)
DUP
ILOAD (4)
ALOAD (5)
ACONST_NULL
INVOKESPECIAL (Container, <init>, (ILResult;Lkotlinx/serialization/internal/SerializationConstructorMarker;)V)
ARETURN
LABEL (L4)
NEW (kotlinx/serialization/UnknownFieldException)
DUP
ILOAD (3)
INVOKESPECIAL (kotlinx/serialization/UnknownFieldException, <init>, (I)V)
CHECKCAST (java/lang/Throwable)
ATHROW
LABEL (L5)
}
public java.lang.Object deserialize(kotlinx.serialization.encoding.Decoder p0) {
LABEL (L0)
LINENUMBER (19)
ALOAD (0)
ALOAD (1)
INVOKEVIRTUAL (Container$$serializer, deserialize, (Lkotlinx/serialization/encoding/Decoder;)LContainer;)
ARETURN
}
public kotlinx.serialization.descriptors.SerialDescriptor getDescriptor()
public void serialize(kotlinx.serialization.encoding.Encoder encoder, Container value)
public void serialize(kotlinx.serialization.encoding.Encoder p0, java.lang.Object p1)
public kotlinx.serialization.KSerializer[] typeParametersSerializers()
}
public final class Container$Companion : java/lang/Object {
private void <init>() {
LABEL (L0)
LINENUMBER (19)
ALOAD (0)
INVOKESPECIAL (java/lang/Object, <init>, ()V)
RETURN
LABEL (L1)
}
public void <init>(kotlin.jvm.internal.DefaultConstructorMarker $constructor_marker) {
LABEL (L0)
LINENUMBER (19)
ALOAD (0)
INVOKESPECIAL (Container$Companion, <init>, ()V)
RETURN
LABEL (L1)
}
public final kotlinx.serialization.KSerializer serializer() {
LABEL (L0)
GETSTATIC (Container$$serializer, INSTANCE, LContainer$$serializer;)
CHECKCAST (kotlinx/serialization/KSerializer)
ARETURN
LABEL (L1)
}
}
public final class Container : java/lang/Object {
public final static Container$Companion Companion
private final Result r
static void <clinit>() {
NEW (Container$Companion)
DUP
ACONST_NULL
INVOKESPECIAL (Container$Companion, <init>, (Lkotlin/jvm/internal/DefaultConstructorMarker;)V)
PUTSTATIC (Container, Companion, LContainer$Companion;)
RETURN
}
public void <init>(Result r) {
LABEL (L0)
ALOAD (1)
LDC (r)
INVOKESTATIC (kotlin/jvm/internal/Intrinsics, checkNotNullParameter, (Ljava/lang/Object;Ljava/lang/String;)V)
LABEL (L1)
LINENUMBER (19)
ALOAD (0)
INVOKESPECIAL (java/lang/Object, <init>, ()V)
ALOAD (0)
ALOAD (1)
PUTFIELD (Container, r, LResult;)
RETURN
LABEL (L2)
}
public void <init>(int seen1, Result r, kotlinx.serialization.internal.SerializationConstructorMarker serializationConstructorMarker) {
LABEL (L0)
ICONST_1
DUP
ILOAD (1)
IAND
IF_ICMPEQ (L1)
ILOAD (1)
ICONST_1
GETSTATIC (Container$$serializer, INSTANCE, LContainer$$serializer;)
CHECKCAST (kotlinx/serialization/KSerializer)
INVOKEINTERFACE (kotlinx/serialization/KSerializer, getDescriptor, ()Lkotlinx/serialization/descriptors/SerialDescriptor;)
INVOKESTATIC (kotlinx/serialization/internal/PluginExceptionsKt, throwMissingFieldException, (IILkotlinx/serialization/descriptors/SerialDescriptor;)V)
LABEL (L1)
ALOAD (0)
INVOKESPECIAL (java/lang/Object, <init>, ()V)
ALOAD (0)
ALOAD (2)
PUTFIELD (Container, r, LResult;)
RETURN
LABEL (L2)
}
public final Result getR()
public final static void write$Self(Container self, kotlinx.serialization.encoding.CompositeEncoder output, kotlinx.serialization.descriptors.SerialDescriptor serialDesc) {
LABEL (L0)
ALOAD (0)
LDC (self)
INVOKESTATIC (kotlin/jvm/internal/Intrinsics, checkNotNullParameter, (Ljava/lang/Object;Ljava/lang/String;)V)
ALOAD (1)
LDC (output)
INVOKESTATIC (kotlin/jvm/internal/Intrinsics, checkNotNullParameter, (Ljava/lang/Object;Ljava/lang/String;)V)
ALOAD (2)
LDC (serialDesc)
INVOKESTATIC (kotlin/jvm/internal/Intrinsics, checkNotNullParameter, (Ljava/lang/Object;Ljava/lang/String;)V)
ALOAD (1)
ALOAD (2)
ICONST_0
NEW (kotlinx/serialization/SealedClassSerializer)
DUP
LDC (Result)
LDC (LResult;)
INVOKESTATIC (kotlin/jvm/internal/Reflection, getOrCreateKotlinClass, (Ljava/lang/Class;)Lkotlin/reflect/KClass;)
ICONST_2
ANEWARRAY (kotlin/reflect/KClass)
DUP
ICONST_0
LDC (LResult$Err;)
INVOKESTATIC (kotlin/jvm/internal/Reflection, getOrCreateKotlinClass, (Ljava/lang/Class;)Lkotlin/reflect/KClass;)
AASTORE
DUP
ICONST_1
LDC (LResult$OK;)
INVOKESTATIC (kotlin/jvm/internal/Reflection, getOrCreateKotlinClass, (Ljava/lang/Class;)Lkotlin/reflect/KClass;)
AASTORE
ICONST_2
ANEWARRAY (kotlinx/serialization/KSerializer)
DUP
ICONST_0
NEW (kotlinx/serialization/internal/ObjectSerializer)
DUP
LDC (Result.Err)
GETSTATIC (Result$Err, INSTANCE, LResult$Err;)
INVOKESPECIAL (kotlinx/serialization/internal/ObjectSerializer, <init>, (Ljava/lang/String;Ljava/lang/Object;)V)
AASTORE
DUP
ICONST_1
GETSTATIC (Result$OK$$serializer, INSTANCE, LResult$OK$$serializer;)
CHECKCAST (kotlinx/serialization/KSerializer)
AASTORE
INVOKESPECIAL (kotlinx/serialization/SealedClassSerializer, <init>, (Ljava/lang/String;Lkotlin/reflect/KClass;[Lkotlin/reflect/KClass;[Lkotlinx/serialization/KSerializer;)V)
ALOAD (0)
GETFIELD (Container, r, LResult;)
INVOKEINTERFACE (kotlinx/serialization/encoding/CompositeEncoder, encodeSerializableElement, (Lkotlinx/serialization/descriptors/SerialDescriptor;ILkotlinx/serialization/SerializationStrategy;Ljava/lang/Object;)V)
RETURN
LABEL (L1)
}
}
final class Result$Companion$serializer$1 : kotlin/jvm/internal/Lambda, kotlin/jvm/functions/Function0 {
public final static Result$Companion$serializer$1 INSTANCE
static void <clinit>() {
NEW (Result$Companion$serializer$1)
DUP
INVOKESPECIAL (Result$Companion$serializer$1, <init>, ()V)
PUTSTATIC (Result$Companion$serializer$1, INSTANCE, LResult$Companion$serializer$1;)
RETURN
}
public void <init>() {
LABEL (L0)
ALOAD (0)
ICONST_0
INVOKESPECIAL (kotlin/jvm/internal/Lambda, <init>, (I)V)
RETURN
LABEL (L1)
}
public final kotlinx.serialization.KSerializer invoke() {
LABEL (L0)
NEW (kotlinx/serialization/SealedClassSerializer)
DUP
LDC (Result)
LDC (LResult;)
INVOKESTATIC (kotlin/jvm/internal/Reflection, getOrCreateKotlinClass, (Ljava/lang/Class;)Lkotlin/reflect/KClass;)
ICONST_2
ANEWARRAY (kotlin/reflect/KClass)
DUP
ICONST_0
LDC (LResult$Err;)
INVOKESTATIC (kotlin/jvm/internal/Reflection, getOrCreateKotlinClass, (Ljava/lang/Class;)Lkotlin/reflect/KClass;)
AASTORE
DUP
ICONST_1
LDC (LResult$OK;)
INVOKESTATIC (kotlin/jvm/internal/Reflection, getOrCreateKotlinClass, (Ljava/lang/Class;)Lkotlin/reflect/KClass;)
AASTORE
ICONST_2
ANEWARRAY (kotlinx/serialization/KSerializer)
DUP
ICONST_0
NEW (kotlinx/serialization/internal/ObjectSerializer)
DUP
LDC (Result.Err)
GETSTATIC (Result$Err, INSTANCE, LResult$Err;)
INVOKESPECIAL (kotlinx/serialization/internal/ObjectSerializer, <init>, (Ljava/lang/String;Ljava/lang/Object;)V)
AASTORE
DUP
ICONST_1
GETSTATIC (Result$OK$$serializer, INSTANCE, LResult$OK$$serializer;)
CHECKCAST (kotlinx/serialization/KSerializer)
AASTORE
INVOKESPECIAL (kotlinx/serialization/SealedClassSerializer, <init>, (Ljava/lang/String;Lkotlin/reflect/KClass;[Lkotlin/reflect/KClass;[Lkotlinx/serialization/KSerializer;)V)
ARETURN
LABEL (L1)
}
public final java.lang.Object invoke() {
LABEL (L0)
ALOAD (0)
INVOKEVIRTUAL (Result$Companion$serializer$1, invoke, ()Lkotlinx/serialization/KSerializer;)
ARETURN
LABEL (L1)
}
}
public final class Result$Companion : java/lang/Object {
private final static kotlin.Lazy $cachedSerializer$delegate
static void <clinit>() {
GETSTATIC (kotlin/LazyThreadSafetyMode, PUBLICATION, Lkotlin/LazyThreadSafetyMode;)
GETSTATIC (Result$Companion$serializer$1, INSTANCE, LResult$Companion$serializer$1;)
CHECKCAST (kotlin/jvm/functions/Function0)
INVOKESTATIC (kotlin/LazyKt, lazy, (Lkotlin/LazyThreadSafetyMode;Lkotlin/jvm/functions/Function0;)Lkotlin/Lazy;)
PUTSTATIC (Result$Companion, $cachedSerializer$delegate, Lkotlin/Lazy;)
RETURN
}
private void <init>() {
LABEL (L0)
LINENUMBER (13)
ALOAD (0)
INVOKESPECIAL (java/lang/Object, <init>, ()V)
RETURN
LABEL (L1)
}
public void <init>(kotlin.jvm.internal.DefaultConstructorMarker $constructor_marker) {
LABEL (L0)
LINENUMBER (13)
ALOAD (0)
INVOKESPECIAL (Result$Companion, <init>, ()V)
RETURN
LABEL (L1)
}
public final kotlinx.serialization.KSerializer serializer() {
LABEL (L0)
GETSTATIC (Result$Companion, $cachedSerializer$delegate, Lkotlin/Lazy;)
INVOKEINTERFACE (kotlin/Lazy, getValue, ()Ljava/lang/Object;)
CHECKCAST (kotlinx/serialization/KSerializer)
ARETURN
LABEL (L1)
}
}
final class Result$Err$serializer$1 : kotlin/jvm/internal/Lambda, kotlin/jvm/functions/Function0 {
public final static Result$Err$serializer$1 INSTANCE
static void <clinit>() {
NEW (Result$Err$serializer$1)
DUP
INVOKESPECIAL (Result$Err$serializer$1, <init>, ()V)
PUTSTATIC (Result$Err$serializer$1, INSTANCE, LResult$Err$serializer$1;)
RETURN
}
public void <init>() {
LABEL (L0)
ALOAD (0)
ICONST_0
INVOKESPECIAL (kotlin/jvm/internal/Lambda, <init>, (I)V)
RETURN
LABEL (L1)
}
public final kotlinx.serialization.KSerializer invoke() {
LABEL (L0)
NEW (kotlinx/serialization/internal/ObjectSerializer)
DUP
LDC (Result.Err)
GETSTATIC (Result$Err, INSTANCE, LResult$Err;)
INVOKESPECIAL (kotlinx/serialization/internal/ObjectSerializer, <init>, (Ljava/lang/String;Ljava/lang/Object;)V)
ARETURN
LABEL (L1)
}
public final java.lang.Object invoke() {
LABEL (L0)
ALOAD (0)
INVOKEVIRTUAL (Result$Err$serializer$1, invoke, ()Lkotlinx/serialization/KSerializer;)
ARETURN
LABEL (L1)
}
}
public final class Result$Err : Result {
private final static kotlin.Lazy $cachedSerializer$delegate
public final static Result$Err INSTANCE
static void <clinit>() {
LABEL (L0)
LINENUMBER (15)
NEW (Result$Err)
DUP
INVOKESPECIAL (Result$Err, <init>, ()V)
ASTORE (0)
ALOAD (0)
PUTSTATIC (Result$Err, INSTANCE, LResult$Err;)
GETSTATIC (kotlin/LazyThreadSafetyMode, PUBLICATION, Lkotlin/LazyThreadSafetyMode;)
GETSTATIC (Result$Err$serializer$1, INSTANCE, LResult$Err$serializer$1;)
CHECKCAST (kotlin/jvm/functions/Function0)
INVOKESTATIC (kotlin/LazyKt, lazy, (Lkotlin/LazyThreadSafetyMode;Lkotlin/jvm/functions/Function0;)Lkotlin/Lazy;)
PUTSTATIC (Result$Err, $cachedSerializer$delegate, Lkotlin/Lazy;)
RETURN
}
private void <init>() {
LABEL (L0)
LINENUMBER (15)
ALOAD (0)
ACONST_NULL
LABEL (L1)
LINENUMBER (15)
INVOKESPECIAL (Result, <init>, (Lkotlin/jvm/internal/DefaultConstructorMarker;)V)
RETURN
LABEL (L2)
}
public final kotlinx.serialization.KSerializer serializer() {
LABEL (L0)
GETSTATIC (Result$Err, $cachedSerializer$delegate, Lkotlin/Lazy;)
INVOKEINTERFACE (kotlin/Lazy, getValue, ()Ljava/lang/Object;)
CHECKCAST (kotlinx/serialization/KSerializer)
ARETURN
LABEL (L1)
}
}
public final class Result$OK$$serializer : java/lang/Object, kotlinx/serialization/internal/GeneratedSerializer {
private final static kotlinx.serialization.descriptors.SerialDescriptor $$serialDesc
public final static Result$OK$$serializer INSTANCE
static void <clinit>() {
LABEL (L0)
LINENUMBER (14)
NEW (Result$OK$$serializer)
DUP
INVOKESPECIAL (Result$OK$$serializer, <init>, ()V)
ASTORE (0)
ALOAD (0)
PUTSTATIC (Result$OK$$serializer, INSTANCE, LResult$OK$$serializer;)
NEW (kotlinx/serialization/internal/PluginGeneratedSerialDescriptor)
DUP
LDC (Result.OK)
GETSTATIC (Result$OK$$serializer, INSTANCE, LResult$OK$$serializer;)
CHECKCAST (kotlinx/serialization/internal/GeneratedSerializer)
LDC (1)
INVOKESPECIAL (kotlinx/serialization/internal/PluginGeneratedSerialDescriptor, <init>, (Ljava/lang/String;Lkotlinx/serialization/internal/GeneratedSerializer;I)V)
ASTORE (0)
ALOAD (0)
LDC (s)
ICONST_0
INVOKEVIRTUAL (kotlinx/serialization/internal/PluginGeneratedSerialDescriptor, addElement, (Ljava/lang/String;Z)V)
ALOAD (0)
PUTSTATIC (Result$OK$$serializer, $$serialDesc, Lkotlinx/serialization/descriptors/SerialDescriptor;)
RETURN
}
private void <init>() {
LABEL (L0)
LINENUMBER (14)
ALOAD (0)
INVOKESPECIAL (java/lang/Object, <init>, ()V)
RETURN
LABEL (L1)
}
public kotlinx.serialization.KSerializer[] childSerializers() {
LABEL (L0)
ICONST_1
ANEWARRAY (kotlinx/serialization/KSerializer)
DUP
ICONST_0
GETSTATIC (kotlinx/serialization/internal/StringSerializer, INSTANCE, Lkotlinx/serialization/internal/StringSerializer;)
CHECKCAST (kotlinx/serialization/KSerializer)
AASTORE
ARETURN
LABEL (L1)
}
public Result$OK deserialize(kotlinx.serialization.encoding.Decoder decoder) {
LABEL (L0)
ALOAD (1)
LDC (decoder)
INVOKESTATIC (kotlin/jvm/internal/Intrinsics, checkNotNullParameter, (Ljava/lang/Object;Ljava/lang/String;)V)
GETSTATIC (Result$OK$$serializer, $$serialDesc, Lkotlinx/serialization/descriptors/SerialDescriptor;)
ASTORE (2)
ICONST_0
ISTORE (4)
ACONST_NULL
ASTORE (5)
ALOAD (1)
ALOAD (2)
INVOKEINTERFACE (kotlinx/serialization/encoding/Decoder, beginStructure, (Lkotlinx/serialization/descriptors/SerialDescriptor;)Lkotlinx/serialization/encoding/CompositeDecoder;)
ASTORE (1)
ALOAD (1)
INVOKEINTERFACE (kotlinx/serialization/encoding/CompositeDecoder, decodeSequentially, ()Z)
IFEQ (L1)
ALOAD (1)
ALOAD (2)
ICONST_0
INVOKEINTERFACE (kotlinx/serialization/encoding/CompositeDecoder, decodeStringElement, (Lkotlinx/serialization/descriptors/SerialDescriptor;I)Ljava/lang/String;)
ASTORE (5)
LDC (2147483647)
ISTORE (4)
GOTO (L2)
LABEL (L1)
ALOAD (1)
ALOAD (2)
INVOKEINTERFACE (kotlinx/serialization/encoding/CompositeDecoder, decodeElementIndex, (Lkotlinx/serialization/descriptors/SerialDescriptor;)I)
ISTORE (3)
ILOAD (3)
TABLESWITCH
-1: L2
0: L3
default: L4
LABEL (L3)
ALOAD (1)
ALOAD (2)
ICONST_0
INVOKEINTERFACE (kotlinx/serialization/encoding/CompositeDecoder, decodeStringElement, (Lkotlinx/serialization/descriptors/SerialDescriptor;I)Ljava/lang/String;)
ASTORE (5)
ILOAD (4)
ICONST_1
IOR
ISTORE (4)
GOTO (L1)
LABEL (L2)
ALOAD (1)
ALOAD (2)
INVOKEINTERFACE (kotlinx/serialization/encoding/CompositeDecoder, endStructure, (Lkotlinx/serialization/descriptors/SerialDescriptor;)V)
NEW (Result$OK)
DUP
ILOAD (4)
ALOAD (5)
ACONST_NULL
INVOKESPECIAL (Result$OK, <init>, (ILjava/lang/String;Lkotlinx/serialization/internal/SerializationConstructorMarker;)V)
ARETURN
LABEL (L4)
NEW (kotlinx/serialization/UnknownFieldException)
DUP
ILOAD (3)
INVOKESPECIAL (kotlinx/serialization/UnknownFieldException, <init>, (I)V)
CHECKCAST (java/lang/Throwable)
ATHROW
LABEL (L5)
}
public java.lang.Object deserialize(kotlinx.serialization.encoding.Decoder p0) {
LABEL (L0)
LINENUMBER (14)
ALOAD (0)
ALOAD (1)
INVOKEVIRTUAL (Result$OK$$serializer, deserialize, (Lkotlinx/serialization/encoding/Decoder;)LResult$OK;)
ARETURN
}
public kotlinx.serialization.descriptors.SerialDescriptor getDescriptor()
public void serialize(kotlinx.serialization.encoding.Encoder encoder, Result$OK value)
public void serialize(kotlinx.serialization.encoding.Encoder p0, java.lang.Object p1)
public kotlinx.serialization.KSerializer[] typeParametersSerializers()
}
public final class Result$OK$Companion : java/lang/Object {
private void <init>() {
LABEL (L0)
LINENUMBER (14)
ALOAD (0)
INVOKESPECIAL (java/lang/Object, <init>, ()V)
RETURN
LABEL (L1)
}
public void <init>(kotlin.jvm.internal.DefaultConstructorMarker $constructor_marker) {
LABEL (L0)
LINENUMBER (14)
ALOAD (0)
INVOKESPECIAL (Result$OK$Companion, <init>, ()V)
RETURN
LABEL (L1)
}
public final kotlinx.serialization.KSerializer serializer() {
LABEL (L0)
GETSTATIC (Result$OK$$serializer, INSTANCE, LResult$OK$$serializer;)
CHECKCAST (kotlinx/serialization/KSerializer)
ARETURN
LABEL (L1)
}
}
public final class Result$OK : Result {
public final static Result$OK$Companion Companion
private final java.lang.String s
static void <clinit>() {
NEW (Result$OK$Companion)
DUP
ACONST_NULL
INVOKESPECIAL (Result$OK$Companion, <init>, (Lkotlin/jvm/internal/DefaultConstructorMarker;)V)
PUTSTATIC (Result$OK, Companion, LResult$OK$Companion;)
RETURN
}
public void <init>(java.lang.String s) {
LABEL (L0)
ALOAD (1)
LDC (s)
INVOKESTATIC (kotlin/jvm/internal/Intrinsics, checkNotNullParameter, (Ljava/lang/Object;Ljava/lang/String;)V)
LABEL (L1)
LINENUMBER (14)
ALOAD (0)
ACONST_NULL
INVOKESPECIAL (Result, <init>, (Lkotlin/jvm/internal/DefaultConstructorMarker;)V)
ALOAD (0)
ALOAD (1)
PUTFIELD (Result$OK, s, Ljava/lang/String;)
RETURN
LABEL (L2)
}
public void <init>(int seen1, java.lang.String s, kotlinx.serialization.internal.SerializationConstructorMarker serializationConstructorMarker) {
LABEL (L0)
ICONST_1
DUP
ILOAD (1)
IAND
IF_ICMPEQ (L1)
ILOAD (1)
ICONST_1
GETSTATIC (Result$OK$$serializer, INSTANCE, LResult$OK$$serializer;)
CHECKCAST (kotlinx/serialization/KSerializer)
INVOKEINTERFACE (kotlinx/serialization/KSerializer, getDescriptor, ()Lkotlinx/serialization/descriptors/SerialDescriptor;)
INVOKESTATIC (kotlinx/serialization/internal/PluginExceptionsKt, throwMissingFieldException, (IILkotlinx/serialization/descriptors/SerialDescriptor;)V)
LABEL (L1)
ALOAD (0)
ILOAD (1)
ACONST_NULL
INVOKESPECIAL (Result, <init>, (ILkotlinx/serialization/internal/SerializationConstructorMarker;)V)
ALOAD (0)
ALOAD (2)
PUTFIELD (Result$OK, s, Ljava/lang/String;)
RETURN
LABEL (L2)
}
public final java.lang.String getS()
public final static void write$Self(Result$OK self, kotlinx.serialization.encoding.CompositeEncoder output, kotlinx.serialization.descriptors.SerialDescriptor serialDesc) {
LABEL (L0)
ALOAD (0)
LDC (self)
INVOKESTATIC (kotlin/jvm/internal/Intrinsics, checkNotNullParameter, (Ljava/lang/Object;Ljava/lang/String;)V)
ALOAD (1)
LDC (output)
INVOKESTATIC (kotlin/jvm/internal/Intrinsics, checkNotNullParameter, (Ljava/lang/Object;Ljava/lang/String;)V)
ALOAD (2)
LDC (serialDesc)
INVOKESTATIC (kotlin/jvm/internal/Intrinsics, checkNotNullParameter, (Ljava/lang/Object;Ljava/lang/String;)V)
ALOAD (0)
ALOAD (1)
ALOAD (2)
INVOKESTATIC (Result, write$Self, (LResult;Lkotlinx/serialization/encoding/CompositeEncoder;Lkotlinx/serialization/descriptors/SerialDescriptor;)V)
ALOAD (1)
ALOAD (2)
ICONST_0
ALOAD (0)
GETFIELD (Result$OK, s, Ljava/lang/String;)
INVOKEINTERFACE (kotlinx/serialization/encoding/CompositeEncoder, encodeStringElement, (Lkotlinx/serialization/descriptors/SerialDescriptor;ILjava/lang/String;)V)
RETURN
LABEL (L1)
}
}
public abstract class Result : java/lang/Object, X {
public final static Result$Companion Companion
static void <clinit>() {
NEW (Result$Companion)
DUP
ACONST_NULL
INVOKESPECIAL (Result$Companion, <init>, (Lkotlin/jvm/internal/DefaultConstructorMarker;)V)
PUTSTATIC (Result, Companion, LResult$Companion;)
RETURN
}
private void <init>() {
LABEL (L0)
LINENUMBER (13)
ALOAD (0)
INVOKESPECIAL (java/lang/Object, <init>, ()V)
RETURN
LABEL (L1)
}
public void <init>(kotlin.jvm.internal.DefaultConstructorMarker $constructor_marker) {
LABEL (L0)
LINENUMBER (13)
ALOAD (0)
INVOKESPECIAL (Result, <init>, ()V)
RETURN
LABEL (L1)
}
public void <init>(int seen1, kotlinx.serialization.internal.SerializationConstructorMarker serializationConstructorMarker) {
LABEL (L0)
ALOAD (0)
INVOKESPECIAL (java/lang/Object, <init>, ()V)
RETURN
LABEL (L1)
}
public void def()
public final static void write$Self(Result self, kotlinx.serialization.encoding.CompositeEncoder output, kotlinx.serialization.descriptors.SerialDescriptor serialDesc) {
LABEL (L0)
ALOAD (0)
LDC (self)
INVOKESTATIC (kotlin/jvm/internal/Intrinsics, checkNotNullParameter, (Ljava/lang/Object;Ljava/lang/String;)V)
ALOAD (1)
LDC (output)
INVOKESTATIC (kotlin/jvm/internal/Intrinsics, checkNotNullParameter, (Ljava/lang/Object;Ljava/lang/String;)V)
ALOAD (2)
LDC (serialDesc)
INVOKESTATIC (kotlin/jvm/internal/Intrinsics, checkNotNullParameter, (Ljava/lang/Object;Ljava/lang/String;)V)
RETURN
LABEL (L1)
}
}
public final class X$Companion : java/lang/Object {
final static X$Companion $$INSTANCE
static void <clinit>() {
LABEL (L0)
LINENUMBER (7)
NEW (X$Companion)
DUP
INVOKESPECIAL (X$Companion, <init>, ()V)
ASTORE (0)
ALOAD (0)
PUTSTATIC (X$Companion, $$INSTANCE, LX$Companion;)
RETURN
}
private void <init>() {
LABEL (L0)
LINENUMBER (7)
ALOAD (0)
INVOKESPECIAL (java/lang/Object, <init>, ()V)
RETURN
LABEL (L1)
}
public final kotlinx.serialization.KSerializer serializer() {
LABEL (L0)
NEW (kotlinx/serialization/SealedClassSerializer)
DUP
LDC (X)
LDC (LX;)
INVOKESTATIC (kotlin/jvm/internal/Reflection, getOrCreateKotlinClass, (Ljava/lang/Class;)Lkotlin/reflect/KClass;)
ICONST_2
ANEWARRAY (kotlin/reflect/KClass)
DUP
ICONST_0
LDC (LResult$Err;)
INVOKESTATIC (kotlin/jvm/internal/Reflection, getOrCreateKotlinClass, (Ljava/lang/Class;)Lkotlin/reflect/KClass;)
AASTORE
DUP
ICONST_1
LDC (LResult$OK;)
INVOKESTATIC (kotlin/jvm/internal/Reflection, getOrCreateKotlinClass, (Ljava/lang/Class;)Lkotlin/reflect/KClass;)
AASTORE
ICONST_2
ANEWARRAY (kotlinx/serialization/KSerializer)
DUP
ICONST_0
NEW (kotlinx/serialization/internal/ObjectSerializer)
DUP
LDC (Result.Err)
GETSTATIC (Result$Err, INSTANCE, LResult$Err;)
INVOKESPECIAL (kotlinx/serialization/internal/ObjectSerializer, <init>, (Ljava/lang/String;Ljava/lang/Object;)V)
AASTORE
DUP
ICONST_1
GETSTATIC (Result$OK$$serializer, INSTANCE, LResult$OK$$serializer;)
CHECKCAST (kotlinx/serialization/KSerializer)
AASTORE
INVOKESPECIAL (kotlinx/serialization/SealedClassSerializer, <init>, (Ljava/lang/String;Lkotlin/reflect/KClass;[Lkotlin/reflect/KClass;[Lkotlinx/serialization/KSerializer;)V)
ARETURN
LABEL (L1)
}
}
public final class X$DefaultImpls : java/lang/Object {
public static void def(X $this)
}
public abstract interface X : java/lang/Object {
public final static X$Companion Companion
static void <clinit>() {
GETSTATIC (X$Companion, $$INSTANCE, LX$Companion;)
PUTSTATIC (X, Companion, LX$Companion;)
RETURN
}
public abstract void def()
}
@@ -0,0 +1,10 @@
// !DIAGNOSTICS: -UNUSED_PARAMETER,-UNUSED_VARIABLE
// WITH_STDLIB
// FILE: test.kt
import kotlinx.serialization.*
@Serializable
open class Parent(open val arg: Int)
<!DUPLICATE_SERIAL_NAME("arg")!>@Serializable<!>
class Derived(override val arg: Int): Parent(arg)
@@ -0,0 +1,62 @@
package
@kotlinx.serialization.Serializable public final class Derived : Parent {
public constructor Derived(/*0*/ arg: kotlin.Int)
@kotlin.Deprecated(level = DeprecationLevel.HIDDEN, message = "This synthesized declaration should not be used directly", replaceWith = kotlin.ReplaceWith(expression = "", imports = {})) public /*synthesized*/ constructor Derived(/*0*/ seen1: kotlin.Int, /*1*/ arg: kotlin.Int, /*2*/ arg: kotlin.Int, /*3*/ serializationConstructorMarker: kotlinx.serialization.internal.SerializationConstructorMarker?)
public open override /*1*/ val arg: kotlin.Int
public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
@kotlin.jvm.JvmStatic public final /*synthesized*/ fun `write$Self`(/*0*/ self: Derived, /*1*/ output: kotlinx.serialization.encoding.CompositeEncoder, /*2*/ serialDesc: kotlinx.serialization.descriptors.SerialDescriptor): kotlin.Unit
@kotlin.jvm.JvmStatic public final override /*1*/ /*fake_override*/ fun `write$Self`(/*0*/ self: Parent, /*1*/ output: kotlinx.serialization.encoding.CompositeEncoder, /*2*/ serialDesc: kotlinx.serialization.descriptors.SerialDescriptor): kotlin.Unit
@kotlin.Deprecated(level = DeprecationLevel.HIDDEN, message = "This synthesized declaration should not be used directly", replaceWith = kotlin.ReplaceWith(expression = "", imports = {})) public object `$serializer` : kotlinx.serialization.internal.GeneratedSerializer<Derived> {
private constructor `$serializer`()
public open override /*1*/ /*synthesized*/ val descriptor: kotlinx.serialization.descriptors.SerialDescriptor
public open override /*1*/ /*synthesized*/ fun childSerializers(): kotlin.Array<kotlinx.serialization.KSerializer<*>>
public open override /*1*/ /*synthesized*/ fun deserialize(/*0*/ decoder: kotlinx.serialization.encoding.Decoder): Derived
public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int
public open override /*1*/ /*synthesized*/ fun serialize(/*0*/ encoder: kotlinx.serialization.encoding.Encoder, /*1*/ value: Derived): kotlin.Unit
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
public open override /*1*/ /*fake_override*/ fun typeParametersSerializers(): kotlin.Array<kotlinx.serialization.KSerializer<*>>
}
public companion object Companion {
private constructor Companion()
public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int
public final /*synthesized*/ fun serializer(): kotlinx.serialization.KSerializer<Derived>
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
}
}
@kotlinx.serialization.Serializable public open class Parent {
public constructor Parent(/*0*/ arg: kotlin.Int)
@kotlin.Deprecated(level = DeprecationLevel.HIDDEN, message = "This synthesized declaration should not be used directly", replaceWith = kotlin.ReplaceWith(expression = "", imports = {})) public /*synthesized*/ constructor Parent(/*0*/ seen1: kotlin.Int, /*1*/ arg: kotlin.Int, /*2*/ serializationConstructorMarker: kotlinx.serialization.internal.SerializationConstructorMarker?)
public open val arg: kotlin.Int
public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
@kotlin.jvm.JvmStatic public final /*synthesized*/ fun `write$Self`(/*0*/ self: Parent, /*1*/ output: kotlinx.serialization.encoding.CompositeEncoder, /*2*/ serialDesc: kotlinx.serialization.descriptors.SerialDescriptor): kotlin.Unit
@kotlin.Deprecated(level = DeprecationLevel.HIDDEN, message = "This synthesized declaration should not be used directly", replaceWith = kotlin.ReplaceWith(expression = "", imports = {})) public object `$serializer` : kotlinx.serialization.internal.GeneratedSerializer<Parent> {
private constructor `$serializer`()
public open override /*1*/ /*synthesized*/ val descriptor: kotlinx.serialization.descriptors.SerialDescriptor
public open override /*1*/ /*synthesized*/ fun childSerializers(): kotlin.Array<kotlinx.serialization.KSerializer<*>>
public open override /*1*/ /*synthesized*/ fun deserialize(/*0*/ decoder: kotlinx.serialization.encoding.Decoder): Parent
public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int
public open override /*1*/ /*synthesized*/ fun serialize(/*0*/ encoder: kotlinx.serialization.encoding.Encoder, /*1*/ value: Parent): kotlin.Unit
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
public open override /*1*/ /*fake_override*/ fun typeParametersSerializers(): kotlin.Array<kotlinx.serialization.KSerializer<*>>
}
public companion object Companion {
private constructor Companion()
public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int
public final /*synthesized*/ fun serializer(): kotlinx.serialization.KSerializer<Parent>
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
}
}
@@ -0,0 +1,29 @@
// !DIAGNOSTICS: -UNUSED_PARAMETER,-UNUSED_VARIABLE
// WITH_STDLIB
// SKIP_TXT
import kotlinx.serialization.*
@Serializable
enum class ImplicitlyDuplicated {
@SerialName("foo")
FIRST,
<!DUPLICATE_SERIAL_NAME_ENUM!>@SerialName("foo")<!>
SECOND
}
@Serializable
enum class ExplicitlyDuplicated {
FIRST,
SECOND,
<!DUPLICATE_SERIAL_NAME_ENUM!>@SerialName("FIRST")<!>
THIRD
}
@Serializable
enum class ReversedExplicitlyDuplicated {
<!DUPLICATE_SERIAL_NAME_ENUM!>@SerialName("THIRD")<!>
FIRST,
SECOND,
THIRD
}
@@ -0,0 +1,10 @@
// !DIAGNOSTICS: -UNUSED_PARAMETER,-UNUSED_VARIABLE, -OPT_IN_USAGE
// SKIP_TXT
// FILE: test.kt
import kotlinx.serialization.*
class Foo(i: Int, val j: Int)
<!EXTERNAL_CLASS_NOT_SERIALIZABLE!>@Serializer(forClass = Foo::class)<!>
object ExternalSerializer
@@ -0,0 +1,12 @@
// !DIAGNOSTICS: -UNUSED_PARAMETER,-UNUSED_VARIABLE
// WITH_STDLIB
// SKIP_TXT
import kotlinx.serialization.Serializable
import java.io.Serializable as JavaSerializable
@Serializable
class Data(val x: Int, <!INCORRECT_TRANSIENT!>@Transient<!> val y: String)
@Serializable
class Data2(val x: Int, @Transient val y: String) : JavaSerializable
@@ -0,0 +1,20 @@
// !DIAGNOSTICS: -UNUSED_PARAMETER,-UNUSED_VARIABLE
// WITH_STDLIB
// SKIP_TXT
import kotlinx.serialization.Serializable
import kotlinx.serialization.Transient
import java.io.Serializable as JavaSerializable
import kotlin.jvm.Transient as JavaTransient
@Serializable
class Data(val x: Int, @Transient val y: String = "a")
@Serializable
class Data2(val x: Int, @Transient val y: String = "a") : JavaSerializable
@Serializable
class Data3(val x: Int, @Transient @JavaTransient val y: String = "a") : JavaSerializable
@Serializable
class Data4(val x: Int, <!INCORRECT_TRANSIENT!>@JavaTransient<!> val y: String)
@@ -0,0 +1,49 @@
// !DIAGNOSTICS: -UNUSED_PARAMETER,-UNUSED_VARIABLE
// WITH_STDLIB
// SKIP_TXT
// !USE_EXPERIMENTAL: kotlinx.serialization.ExperimentalSerializationApi
// FILE: test.kt
import kotlinx.serialization.*
import kotlin.reflect.KClass
// TODO: for this test to work, runtime dependency should be updated to (yet unreleased) serialization 1.3.0
//@InheritableSerialInfo
annotation class I(val value: String)
enum class E { A, B }
//@InheritableSerialInfo
annotation class I2(val e: E, val k: KClass<*>)
@Serializable
@I("a")
sealed class Result {
// @I("b")
@Serializable class OK(val s: String): Result()
}
@Serializable
@I("a")
@I2(E.A, E::class)
open class A
@Serializable
@I("a")
@I2(E.A, E::class)
open class Correct: A()
@Serializable
@I("a")
//@I2(E.B, E::class)
open class B: A()
@Serializable
@I("a")
//@I2(E.A, I::class)
open class B2: A()
@Serializable
//@I("b")
//@I2(E.A, E::class)
open class C: B()
@@ -0,0 +1,10 @@
// This test enshures that analysis ends up without compiler exceptions
// !DIAGNOSTICS: -OPT_IN_USAGE
import kotlinx.serialization.*
@Serializable
class Digest() {
@Serializer(forClass = Digest::class)
companion object : KSerializer<Digest> {}
}
@@ -0,0 +1,20 @@
package
@kotlinx.serialization.Serializable public final class Digest {
public constructor Digest()
@kotlin.Deprecated(level = DeprecationLevel.HIDDEN, message = "This synthesized declaration should not be used directly", replaceWith = kotlin.ReplaceWith(expression = "", imports = {})) public /*synthesized*/ constructor Digest(/*0*/ seen1: kotlin.Int, /*1*/ serializationConstructorMarker: kotlinx.serialization.internal.SerializationConstructorMarker?)
public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
@kotlinx.serialization.Serializer(forClass = Digest::class) public companion object Companion : kotlinx.serialization.KSerializer<Digest> {
private constructor Companion()
public open override /*1*/ /*synthesized*/ val descriptor: kotlinx.serialization.descriptors.SerialDescriptor
public open override /*1*/ /*synthesized*/ fun deserialize(/*0*/ decoder: kotlinx.serialization.encoding.Decoder): Digest
public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int
public open override /*1*/ /*synthesized*/ fun serialize(/*0*/ encoder: kotlinx.serialization.encoding.Encoder, /*1*/ value: Digest): kotlin.Unit
public final /*synthesized*/ fun serializer(): kotlinx.serialization.KSerializer<Digest>
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
}
}
@@ -0,0 +1,47 @@
// !DIAGNOSTICS: -UNUSED_PARAMETER,-UNUSED_VARIABLE,-OPT_IN_USAGE_ERROR,-OPT_IN_USAGE
// WITH_STDLIB
// SKIP_TXT
import kotlinx.serialization.*
import kotlinx.serialization.descriptors.*
import kotlinx.serialization.encoding.*
fun container() {
@Serializable
class X // local classes are allowed
val y = <!ANONYMOUS_OBJECTS_NOT_SUPPORTED!>@Serializable<!> object {
fun inObjectFun() {
<!ANONYMOUS_OBJECTS_NOT_SUPPORTED!>@Serializable<!>
class X // local classes in anonymous object functions are not allowed
}
}
class LocalSerializer : KSerializer<Any?> {
override val descriptor: SerialDescriptor = buildSerialDescriptor("tmp", PrimitiveKind.INT)
override fun serialize(encoder: Encoder, value: Any?) {
encoder.encodeNull()
}
override fun deserialize(decoder: Decoder): Any? {
return decoder.decodeNull()
}
}
@Serializable
class WithLocalSerializerInProperty(<!LOCAL_SERIALIZER_USAGE!>@Serializable(with = LocalSerializer::class)<!> val x: Any?)
<!LOCAL_SERIALIZER_USAGE, SERIALIZER_TYPE_INCOMPATIBLE!>@Serializable(with = LocalSerializer::class)<!>
data class WithLocalSerializer(val i: Int)
}
val topLevelAnon = <!ANONYMOUS_OBJECTS_NOT_SUPPORTED!>@Serializable<!> object {}
@Serializable class A {
@Serializable class B // nested classes are allowed
<!INNER_CLASSES_NOT_SUPPORTED!>@Serializable<!> inner class C // inner classes are not
@Serializable object F {} // regular named object, OK
}
@@ -0,0 +1,9 @@
// !DIAGNOSTICS: -UNUSED_PARAMETER,-UNUSED_VARIABLE
// WITH_STDLIB
// FILE: test.kt
import kotlinx.serialization.*
open class NonSerializableParent(val arg: Int)
<!NON_SERIALIZABLE_PARENT_MUST_HAVE_NOARG_CTOR!>@Serializable<!>
class Derived(val someData: String): NonSerializableParent(42)
@@ -0,0 +1,40 @@
package
@kotlinx.serialization.Serializable public final class Derived : NonSerializableParent {
@kotlin.Deprecated(level = DeprecationLevel.HIDDEN, message = "This synthesized declaration should not be used directly", replaceWith = kotlin.ReplaceWith(expression = "", imports = {})) public /*synthesized*/ constructor Derived(/*0*/ seen1: kotlin.Int, /*1*/ someData: kotlin.String?, /*2*/ serializationConstructorMarker: kotlinx.serialization.internal.SerializationConstructorMarker?)
public constructor Derived(/*0*/ someData: kotlin.String)
public final override /*1*/ /*fake_override*/ val arg: kotlin.Int
public final val someData: kotlin.String
public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
@kotlin.jvm.JvmStatic public final /*synthesized*/ fun `write$Self`(/*0*/ self: Derived, /*1*/ output: kotlinx.serialization.encoding.CompositeEncoder, /*2*/ serialDesc: kotlinx.serialization.descriptors.SerialDescriptor): kotlin.Unit
@kotlin.Deprecated(level = DeprecationLevel.HIDDEN, message = "This synthesized declaration should not be used directly", replaceWith = kotlin.ReplaceWith(expression = "", imports = {})) public object `$serializer` : kotlinx.serialization.internal.GeneratedSerializer<Derived> {
private constructor `$serializer`()
public open override /*1*/ /*synthesized*/ val descriptor: kotlinx.serialization.descriptors.SerialDescriptor
public open override /*1*/ /*synthesized*/ fun childSerializers(): kotlin.Array<kotlinx.serialization.KSerializer<*>>
public open override /*1*/ /*synthesized*/ fun deserialize(/*0*/ decoder: kotlinx.serialization.encoding.Decoder): Derived
public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int
public open override /*1*/ /*synthesized*/ fun serialize(/*0*/ encoder: kotlinx.serialization.encoding.Encoder, /*1*/ value: Derived): kotlin.Unit
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
public open override /*1*/ /*fake_override*/ fun typeParametersSerializers(): kotlin.Array<kotlinx.serialization.KSerializer<*>>
}
public companion object Companion {
private constructor Companion()
public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int
public final /*synthesized*/ fun serializer(): kotlinx.serialization.KSerializer<Derived>
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
}
}
public open class NonSerializableParent {
public constructor NonSerializableParent(/*0*/ arg: kotlin.Int)
public final val arg: kotlin.Int
public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
}
@@ -0,0 +1,17 @@
// !DIAGNOSTICS: -UNUSED_PARAMETER,-UNUSED_VARIABLE
// WITH_STDLIB
// FILE: test.kt
import kotlinx.serialization.*
class NonSerializable
@Serializable
class Basic(val foo: <!SERIALIZER_NOT_FOUND("NonSerializable")!>NonSerializable<!>)
@Serializable
class Inside(val foo: List<<!SERIALIZER_NOT_FOUND("NonSerializable")!>NonSerializable<!>>)
@Serializable
class WithImplicitType {
<!SERIALIZER_NOT_FOUND("NonSerializable")!>val foo = NonSerializable()<!>
}
@@ -0,0 +1,98 @@
package
@kotlinx.serialization.Serializable public final class Basic {
public constructor Basic(/*0*/ foo: NonSerializable)
@kotlin.Deprecated(level = DeprecationLevel.HIDDEN, message = "This synthesized declaration should not be used directly", replaceWith = kotlin.ReplaceWith(expression = "", imports = {})) public /*synthesized*/ constructor Basic(/*0*/ seen1: kotlin.Int, /*1*/ foo: NonSerializable?, /*2*/ serializationConstructorMarker: kotlinx.serialization.internal.SerializationConstructorMarker?)
public final val foo: NonSerializable
public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
@kotlin.jvm.JvmStatic public final /*synthesized*/ fun `write$Self`(/*0*/ self: Basic, /*1*/ output: kotlinx.serialization.encoding.CompositeEncoder, /*2*/ serialDesc: kotlinx.serialization.descriptors.SerialDescriptor): kotlin.Unit
@kotlin.Deprecated(level = DeprecationLevel.HIDDEN, message = "This synthesized declaration should not be used directly", replaceWith = kotlin.ReplaceWith(expression = "", imports = {})) public object `$serializer` : kotlinx.serialization.internal.GeneratedSerializer<Basic> {
private constructor `$serializer`()
public open override /*1*/ /*synthesized*/ val descriptor: kotlinx.serialization.descriptors.SerialDescriptor
public open override /*1*/ /*synthesized*/ fun childSerializers(): kotlin.Array<kotlinx.serialization.KSerializer<*>>
public open override /*1*/ /*synthesized*/ fun deserialize(/*0*/ decoder: kotlinx.serialization.encoding.Decoder): Basic
public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int
public open override /*1*/ /*synthesized*/ fun serialize(/*0*/ encoder: kotlinx.serialization.encoding.Encoder, /*1*/ value: Basic): kotlin.Unit
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
public open override /*1*/ /*fake_override*/ fun typeParametersSerializers(): kotlin.Array<kotlinx.serialization.KSerializer<*>>
}
public companion object Companion {
private constructor Companion()
public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int
public final /*synthesized*/ fun serializer(): kotlinx.serialization.KSerializer<Basic>
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
}
}
@kotlinx.serialization.Serializable public final class Inside {
@kotlin.Deprecated(level = DeprecationLevel.HIDDEN, message = "This synthesized declaration should not be used directly", replaceWith = kotlin.ReplaceWith(expression = "", imports = {})) public /*synthesized*/ constructor Inside(/*0*/ seen1: kotlin.Int, /*1*/ foo: kotlin.collections.List<NonSerializable>?, /*2*/ serializationConstructorMarker: kotlinx.serialization.internal.SerializationConstructorMarker?)
public constructor Inside(/*0*/ foo: kotlin.collections.List<NonSerializable>)
public final val foo: kotlin.collections.List<NonSerializable>
public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
@kotlin.jvm.JvmStatic public final /*synthesized*/ fun `write$Self`(/*0*/ self: Inside, /*1*/ output: kotlinx.serialization.encoding.CompositeEncoder, /*2*/ serialDesc: kotlinx.serialization.descriptors.SerialDescriptor): kotlin.Unit
@kotlin.Deprecated(level = DeprecationLevel.HIDDEN, message = "This synthesized declaration should not be used directly", replaceWith = kotlin.ReplaceWith(expression = "", imports = {})) public object `$serializer` : kotlinx.serialization.internal.GeneratedSerializer<Inside> {
private constructor `$serializer`()
public open override /*1*/ /*synthesized*/ val descriptor: kotlinx.serialization.descriptors.SerialDescriptor
public open override /*1*/ /*synthesized*/ fun childSerializers(): kotlin.Array<kotlinx.serialization.KSerializer<*>>
public open override /*1*/ /*synthesized*/ fun deserialize(/*0*/ decoder: kotlinx.serialization.encoding.Decoder): Inside
public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int
public open override /*1*/ /*synthesized*/ fun serialize(/*0*/ encoder: kotlinx.serialization.encoding.Encoder, /*1*/ value: Inside): kotlin.Unit
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
public open override /*1*/ /*fake_override*/ fun typeParametersSerializers(): kotlin.Array<kotlinx.serialization.KSerializer<*>>
}
public companion object Companion {
private constructor Companion()
public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int
public final /*synthesized*/ fun serializer(): kotlinx.serialization.KSerializer<Inside>
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
}
}
public final class NonSerializable {
public constructor NonSerializable()
public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
}
@kotlinx.serialization.Serializable public final class WithImplicitType {
public constructor WithImplicitType()
@kotlin.Deprecated(level = DeprecationLevel.HIDDEN, message = "This synthesized declaration should not be used directly", replaceWith = kotlin.ReplaceWith(expression = "", imports = {})) public /*synthesized*/ constructor WithImplicitType(/*0*/ seen1: kotlin.Int, /*1*/ foo: NonSerializable?, /*2*/ serializationConstructorMarker: kotlinx.serialization.internal.SerializationConstructorMarker?)
public final val foo: NonSerializable
public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
@kotlin.jvm.JvmStatic public final /*synthesized*/ fun `write$Self`(/*0*/ self: WithImplicitType, /*1*/ output: kotlinx.serialization.encoding.CompositeEncoder, /*2*/ serialDesc: kotlinx.serialization.descriptors.SerialDescriptor): kotlin.Unit
@kotlin.Deprecated(level = DeprecationLevel.HIDDEN, message = "This synthesized declaration should not be used directly", replaceWith = kotlin.ReplaceWith(expression = "", imports = {})) public object `$serializer` : kotlinx.serialization.internal.GeneratedSerializer<WithImplicitType> {
private constructor `$serializer`()
public open override /*1*/ /*synthesized*/ val descriptor: kotlinx.serialization.descriptors.SerialDescriptor
public open override /*1*/ /*synthesized*/ fun childSerializers(): kotlin.Array<kotlinx.serialization.KSerializer<*>>
public open override /*1*/ /*synthesized*/ fun deserialize(/*0*/ decoder: kotlinx.serialization.encoding.Decoder): WithImplicitType
public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int
public open override /*1*/ /*synthesized*/ fun serialize(/*0*/ encoder: kotlinx.serialization.encoding.Encoder, /*1*/ value: WithImplicitType): kotlin.Unit
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
public open override /*1*/ /*fake_override*/ fun typeParametersSerializers(): kotlin.Array<kotlinx.serialization.KSerializer<*>>
}
public companion object Companion {
private constructor Companion()
public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int
public final /*synthesized*/ fun serializer(): kotlinx.serialization.KSerializer<WithImplicitType>
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
}
}
@@ -0,0 +1,18 @@
// !DIAGNOSTICS: -UNUSED_PARAMETER,-UNUSED_VARIABLE
// WITH_STDLIB
// FILE: test.kt
import kotlinx.serialization.*
import kotlinx.serialization.descriptors.*
import kotlinx.serialization.encoding.*
@Serializable(NopeNullableSerializer::class)
class Nope {}
class NopeNullableSerializer: KSerializer<Nope?> {
override val descriptor: SerialDescriptor get() = TODO()
override fun deserialize(decoder: Decoder): Nope? = TODO()
override fun serialize(encoder: Encoder, value: Nope?) = TODO()
}
@Serializable
class Foo(val foo: <!SERIALIZER_NULLABILITY_INCOMPATIBLE("NopeNullableSerializer", "Nope")!>Nope<!>)
@@ -0,0 +1,56 @@
package
@kotlinx.serialization.Serializable public final class Foo {
public constructor Foo(/*0*/ foo: Nope)
@kotlin.Deprecated(level = DeprecationLevel.HIDDEN, message = "This synthesized declaration should not be used directly", replaceWith = kotlin.ReplaceWith(expression = "", imports = {})) public /*synthesized*/ constructor Foo(/*0*/ seen1: kotlin.Int, /*1*/ foo: Nope?, /*2*/ serializationConstructorMarker: kotlinx.serialization.internal.SerializationConstructorMarker?)
public final val foo: Nope
public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
@kotlin.jvm.JvmStatic public final /*synthesized*/ fun `write$Self`(/*0*/ self: Foo, /*1*/ output: kotlinx.serialization.encoding.CompositeEncoder, /*2*/ serialDesc: kotlinx.serialization.descriptors.SerialDescriptor): kotlin.Unit
@kotlin.Deprecated(level = DeprecationLevel.HIDDEN, message = "This synthesized declaration should not be used directly", replaceWith = kotlin.ReplaceWith(expression = "", imports = {})) public object `$serializer` : kotlinx.serialization.internal.GeneratedSerializer<Foo> {
private constructor `$serializer`()
public open override /*1*/ /*synthesized*/ val descriptor: kotlinx.serialization.descriptors.SerialDescriptor
public open override /*1*/ /*synthesized*/ fun childSerializers(): kotlin.Array<kotlinx.serialization.KSerializer<*>>
public open override /*1*/ /*synthesized*/ fun deserialize(/*0*/ decoder: kotlinx.serialization.encoding.Decoder): Foo
public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int
public open override /*1*/ /*synthesized*/ fun serialize(/*0*/ encoder: kotlinx.serialization.encoding.Encoder, /*1*/ value: Foo): kotlin.Unit
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
public open override /*1*/ /*fake_override*/ fun typeParametersSerializers(): kotlin.Array<kotlinx.serialization.KSerializer<*>>
}
public companion object Companion {
private constructor Companion()
public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int
public final /*synthesized*/ fun serializer(): kotlinx.serialization.KSerializer<Foo>
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
}
}
@kotlinx.serialization.Serializable(with = NopeNullableSerializer::class) public final class Nope {
public constructor Nope()
public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
public companion object Companion {
private constructor Companion()
public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int
public final /*synthesized*/ fun serializer(): kotlinx.serialization.KSerializer<Nope>
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
}
}
public final class NopeNullableSerializer : kotlinx.serialization.KSerializer<Nope?> {
public constructor NopeNullableSerializer()
public open override /*1*/ val descriptor: kotlinx.serialization.descriptors.SerialDescriptor
public open override /*1*/ fun deserialize(/*0*/ decoder: kotlinx.serialization.encoding.Decoder): Nope?
public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int
public open override /*1*/ fun serialize(/*0*/ encoder: kotlinx.serialization.encoding.Encoder, /*1*/ value: Nope?): kotlin.Nothing
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
}

Some files were not shown because too many files have changed in this diff Show More