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