Adapt IR serialization plugin so it works both with FIR and old FE

(except some places that are not currently supported by FIR part of plugin)
This commit is contained in:
Leonid Startsev
2022-07-25 19:36:47 +02:00
committed by Space
parent 89329a0e1b
commit b0716fe725
16 changed files with 436 additions and 224 deletions
@@ -264,6 +264,7 @@ internal fun IrCall.getCorrespondingProperty(): IrProperty =
symbol.owner.correspondingPropertySymbol?.owner
?: error("Atomic property accessor ${this.render()} expected to have non-null correspondingPropertySymbol")
@OptIn(FirIncompatiblePluginAPI::class)
internal fun IrPluginContext.referencePackageFunction(
packageName: String,
name: String,
@@ -274,6 +275,7 @@ internal fun IrPluginContext.referencePackageFunction(
error("Exception while looking for the function `$name` in package `$packageName`: ${e.message}")
}
@OptIn(FirIncompatiblePluginAPI::class)
internal fun IrPluginContext.referenceFunction(classSymbol: IrClassSymbol, functionName: String): IrSimpleFunctionSymbol {
val functionId = FqName("$KOTLIN.${classSymbol.owner.name}.$functionName")
return try {
@@ -283,18 +285,19 @@ internal fun IrPluginContext.referenceFunction(classSymbol: IrClassSymbol, funct
}
}
@OptIn(FirIncompatiblePluginAPI::class)
private fun IrPluginContext.referenceArrayClass(irType: IrSimpleType): IrClassSymbol {
val jsArrayName = irType.getArrayClassFqName()
return referenceClass(jsArrayName) ?: error("Array class $jsArrayName was not found in the context")
}
@OptIn(FirIncompatiblePluginAPI::class)
internal fun IrPluginContext.getArrayConstructorSymbol(
irType: IrSimpleType,
predicate: (IrConstructorSymbol) -> Boolean = { true }
): IrConstructorSymbol {
val jsArrayName = irType.getArrayClassFqName()
return try {
@Suppress("DEPRECATION") // caution: referenceConstructors(fqName) doesn't work with FIR
referenceConstructors(jsArrayName).single(predicate)
} catch (e: RuntimeException) {
error("Array constructor $jsArrayName matching the predicate was not found in the context")
@@ -5,9 +5,19 @@
package org.jetbrains.kotlinx.serialization.compiler.backend.common
import org.jetbrains.kotlin.backend.jvm.ir.fileParent
import org.jetbrains.kotlin.descriptors.ClassDescriptor
import org.jetbrains.kotlin.descriptors.DeclarationDescriptor
import org.jetbrains.kotlin.descriptors.FunctionDescriptor
import org.jetbrains.kotlin.ir.declarations.IrClass
import org.jetbrains.kotlin.ir.expressions.IrClassReference
import org.jetbrains.kotlin.ir.expressions.IrVararg
import org.jetbrains.kotlin.ir.symbols.IrClassSymbol
import org.jetbrains.kotlin.ir.types.IrSimpleType
import org.jetbrains.kotlin.ir.types.classOrNull
import org.jetbrains.kotlin.ir.types.isNullable
import org.jetbrains.kotlin.ir.types.typeOrNull
import org.jetbrains.kotlin.ir.util.findAnnotation
import org.jetbrains.kotlin.js.translate.utils.AnnotationsUtils
import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.name.Name
@@ -21,6 +31,37 @@ 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 AbstractIrGenerator(private val currentClass: IrClass) {
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(::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 }
)
}
}
abstract class AbstractSerialGenerator(val bindingContext: BindingContext?, val currentDeclaration: ClassDescriptor) {
@@ -6,7 +6,6 @@
package org.jetbrains.kotlinx.serialization.compiler.backend.common
import com.intellij.psi.PsiElement
import org.jetbrains.kotlin.backend.common.extensions.IrPluginContext
import org.jetbrains.kotlin.builtins.KotlinBuiltIns
import org.jetbrains.kotlin.codegen.CompilationException
import org.jetbrains.kotlin.descriptors.*
@@ -14,6 +13,7 @@ import org.jetbrains.kotlin.descriptors.annotations.Annotations
import org.jetbrains.kotlin.ir.declarations.IrProperty
import org.jetbrains.kotlin.ir.symbols.IrClassSymbol
import org.jetbrains.kotlin.ir.types.*
import org.jetbrains.kotlin.ir.util.hasAnnotation
import org.jetbrains.kotlin.ir.util.isTypeParameter
import org.jetbrains.kotlin.js.descriptorUtils.getJetTypeFqName
import org.jetbrains.kotlin.js.resolve.diagnostics.findPsi
@@ -63,17 +63,29 @@ fun AbstractSerialGenerator.findAddOnSerializer(propertyType: KotlinType, module
return null
}
fun AbstractIrGenerator.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 KotlinType.isGeneratedSerializableObject() =
toClassDescriptor?.run { kind == ClassKind.OBJECT && hasSerializableOrMetaAnnotationWithoutArgs } == true
fun AbstractSerialGenerator.getIrSerialTypeInfo(property: IrSerializableProperty, ctx: SerializationPluginContext): IrSerialTypeInfo {
fun AbstractIrGenerator.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?.let { return SerializableInfo(it.classOrNull!!) }
// TODO findAddOnSerializer(T, property.module)?.let { return SerializableInfo(it) }
T.overridenSerializer?.let { return SerializableInfo(it.classOrNull!!) }
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(
@@ -82,7 +94,7 @@ fun AbstractSerialGenerator.getIrSerialTypeInfo(property: IrSerializablePropert
)
T.isString() -> IrSerialTypeInfo(property, "String")
T.isArray() -> {
val serializer = property.serializableWith?.classOrNull ?: ctx.getClassFromRuntime(SpecialBuiltins.referenceArraySerializer)
val serializer = property.serializableWith(ctx) ?: ctx.getClassFromInternalSerializationPackage(SpecialBuiltins.referenceArraySerializer)
SerializableInfo(serializer)
}
else -> {
@@ -5,10 +5,12 @@
package org.jetbrains.kotlinx.serialization.compiler.backend.common
import com.intellij.psi.PsiElement
import org.jetbrains.kotlin.codegen.CompilationException
import org.jetbrains.kotlin.descriptors.*
import org.jetbrains.kotlin.backend.jvm.ir.getStringConstArgument
import org.jetbrains.kotlin.descriptors.CallableMemberDescriptor
import org.jetbrains.kotlin.descriptors.ClassKind
import org.jetbrains.kotlin.descriptors.Modality
import org.jetbrains.kotlin.ir.declarations.*
import org.jetbrains.kotlin.ir.expressions.IrClassReference
import org.jetbrains.kotlin.ir.expressions.IrConstructorCall
import org.jetbrains.kotlin.ir.expressions.IrExpression
import org.jetbrains.kotlin.ir.expressions.IrStatementOrigin
@@ -20,30 +22,22 @@ 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.descriptorUtil.classId
import org.jetbrains.kotlin.resolve.descriptorUtil.fqNameSafe
import org.jetbrains.kotlinx.serialization.compiler.backend.jvm.contextSerializerId
import org.jetbrains.kotlinx.serialization.compiler.backend.jvm.enumSerializerId
import org.jetbrains.kotlinx.serialization.compiler.backend.jvm.objectSerializerId
import org.jetbrains.kotlinx.serialization.compiler.backend.jvm.polymorphicSerializerId
import org.jetbrains.kotlinx.serialization.compiler.backend.jvm.referenceArraySerializerId
import org.jetbrains.kotlinx.serialization.compiler.backend.jvm.*
import org.jetbrains.kotlinx.serialization.compiler.extensions.SerializationPluginContext
import org.jetbrains.kotlinx.serialization.compiler.fir.SerializationPluginKey
import org.jetbrains.kotlinx.serialization.compiler.resolve.*
fun AbstractSerialGenerator.findTypeSerializerOrContextUnchecked(
fun AbstractIrGenerator.findTypeSerializerOrContextUnchecked(
context: SerializationPluginContext, kType: IrType
): IrClassSymbol? {
val annotations = kType.annotations
if (kType.isTypeParameter()) return null
annotations.serializableWith()?.let { return it.classOrNull }
additionalSerializersInScopeOfCurrentFile[kType.classOrNull?.descriptor to kType.isMarkedNullable()]?.let {
return context.referenceClass(
ClassId.topLevel(it.fqNameSafe)
)
annotations.serializableWith()?.let { return it }
additionalSerializersInScopeOfCurrentFile[kType.classOrNull!! to kType.isNullable()]?.let {
return it
}
if (kType.isMarkedNullable()) return findTypeSerializerOrContextUnchecked(context, kType.makeNotNull())
if (kType.toKotlinType() in contextualKClassListInCurrentFile) return context.referenceClass(contextSerializerId)
if (kType.classOrNull in contextualKClassListInCurrentFile) return context.referenceClass(contextSerializerId)
return analyzeSpecialSerializers(context, annotations) ?: findTypeSerializer(context, kType)
}
@@ -59,22 +53,15 @@ fun analyzeSpecialSerializers(
else -> null
}
fun AbstractSerialGenerator.findTypeSerializerOrContext(
context: SerializationPluginContext, kType: IrType,
sourceElement: PsiElement? = null
fun AbstractIrGenerator.findTypeSerializerOrContext(
context: SerializationPluginContext, kType: IrType
): IrClassSymbol? {
if (kType.isTypeParameter()) return null
return findTypeSerializerOrContextUnchecked(context, 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
)
return findTypeSerializerOrContextUnchecked(context, kType) ?: error("Serializer for element of type ${kType.render()} has not been found")
}
fun findTypeSerializer(context: SerializationPluginContext, type: IrType): IrClassSymbol? {
val userOverride = type.overridenSerializer
if (userOverride != null) return userOverride.classOrNull
type.overridenSerializer?.let { return it }
if (type.isTypeParameter()) return null
if (type.isArray()) return context.referenceClass(referenceArraySerializerId)
if (type.isGeneratedSerializableObject()) return context.referenceClass(objectSerializerId)
@@ -89,7 +76,7 @@ fun findTypeSerializer(context: SerializationPluginContext, type: IrType): IrCla
internal fun IrClass?.classSerializer(context: SerializationPluginContext): IrClassSymbol? = this?.let {
// serializer annotation on class?
serializableWith?.let { return it.classOrNull }
serializableWith?.let { return it }
// companion object serializer?
if (hasCompanionObjectAsSerializer) return companionObject()?.symbol
// can infer @Poly?
@@ -104,6 +91,11 @@ internal fun IrClass?.classSerializer(context: SerializationPluginContext): IrCl
return null
}
private val IrClass.isSerialInfoAnnotation: Boolean
get() = annotations.hasAnnotation(SerializationAnnotations.serialInfoFqName)
|| annotations.hasAnnotation(SerializationAnnotations.inheritableSerialInfoFqName)
|| annotations.hasAnnotation(SerializationAnnotations.metaSerializableAnnotationFqName)
internal val IrClass.shouldHaveGeneratedSerializer: Boolean
get() = (isInternalSerializable && (modality == Modality.FINAL || modality == Modality.OPEN))
|| isEnumWithLegacyGeneratedSerializer()
@@ -116,7 +108,13 @@ internal fun IrClass.polymorphicSerializerIfApplicableAutomatically(context: Ser
isInternalSerializable && modality == Modality.SEALED -> SpecialBuiltins.sealedSerializer
else -> null
}
return serializer?.let { context.getClassFromRuntimeOrNull(it, SerializationPackages.packageFqName, SerializationPackages.internalPackageFqName) }
return serializer?.let {
context.getClassFromRuntimeOrNull(
it,
SerializationPackages.packageFqName,
SerializationPackages.internalPackageFqName
)
}
}
internal val IrClass.isInternalSerializable: Boolean
@@ -131,13 +129,14 @@ internal val IrClass.isStaticSerializable: Boolean get() = this.typeParameters.i
internal val IrClass.hasCompanionObjectAsSerializer: Boolean
get() = isInternallySerializableObject || companionObject()?.serializerForClass == this.defaultType
get() = isInternallySerializableObject || companionObject()?.serializerForClass == this.symbol
internal val IrClass.isInternallySerializableObject: Boolean
get() = kind == ClassKind.OBJECT && hasSerializableOrMetaAnnotationWithoutArgs()
internal val IrClass.serializerForClass: IrType?
get() = null // TODO("Serializer(forClass)")
internal val IrClass.serializerForClass: IrClassSymbol?
get() = (annotations.findAnnotation(SerializationAnnotations.serializerAnnotationFqName)
?.getValueArgument(0) as? IrClassReference)?.symbol as? IrClassSymbol
fun findEnumTypeSerializer(context: SerializationPluginContext, type: IrType): IrClassSymbol? {
val classSymbol = type.classOrNull?.owner ?: return null
@@ -149,10 +148,10 @@ fun findEnumTypeSerializer(context: SerializationPluginContext, type: IrType): I
internal fun IrClass.isEnumWithLegacyGeneratedSerializer(): Boolean = isInternallySerializableEnum() && useGeneratedEnumSerializer
internal val IrClass.useGeneratedEnumSerializer: Boolean
get() = true // todo ????
get() = true // FIXME This would break if we try to use this new IR compiler with pre-1.4.1 serialization versions. I think we just need to raise min runtime version.
internal val IrClass.isSealedSerializableInterface: Boolean
get() = kind == ClassKind.INTERFACE && modality == Modality.SEALED && hasSerializableOrMetaAnnotationWithoutArgs() // in previous version, it was just 'serializableOrMeta'
get() = kind == ClassKind.INTERFACE && modality == Modality.SEALED && hasSerializableOrMetaAnnotation()
internal fun IrClass.isInternallySerializableEnum(): Boolean =
kind == ClassKind.ENUM_CLASS && hasSerializableOrMetaAnnotationWithoutArgs()
@@ -181,7 +180,7 @@ fun IrType.isGeneratedSerializableObject(): Boolean {
internal val IrClass.isSerializableObject: Boolean
get() = kind == ClassKind.OBJECT && hasSerializableOrMetaAnnotation()
// todo: optimize & unify
// todo: optimize & unify with hasSerializableOrMeta
internal fun IrClass.hasSerializableOrMetaAnnotationWithoutArgs(): Boolean {
val annot = getAnnotation(SerializationAnnotations.serializableAnnotationFqName)
if (annot != null) {
@@ -205,9 +204,10 @@ fun SerializationPluginContext.getClassFromRuntimeOrNull(className: String, vara
}
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.")
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 =
@@ -215,23 +215,22 @@ fun SerializationPluginContext.getClassFromInternalSerializationPackage(classNam
?: error("Class $className wasn't found in ${SerializationPackages.internalPackageFqName}. Check that you have correct version of serialization runtime in classpath.")
internal val IrType.overridenSerializer: IrSimpleType?
internal val IrType.overridenSerializer: IrClassSymbol?
get() {
val desc = this.classOrNull ?: return null
desc.owner.serializableWith?.let { return it }
return null
}
internal val IrClass.serializableWith: IrSimpleType?
internal val IrClass.serializableWith: IrClassSymbol?
get() = annotations.serializableWith()
internal fun List<IrConstructorCall>.serializableWith(): IrSimpleType? {
// XXX::class
// val annotationArg =
// firstOrNull { it.type.classFqName == SerializationAnnotations.serializerAnnotationFqName }?.getValueArgument(0) ?: return null
// TODO("SerializableWith")
return null
// @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? {
@@ -252,12 +251,12 @@ fun IrClass.hasSerializableOrMetaAnnotation() = descriptor.hasSerializableOrMeta
internal val IrType.genericIndex: Int?
get() = (this.classifierOrNull as? IrTypeParameterSymbol)?.owner?.index
fun IrType.serialName(): String = this.classOrNull!!.owner.serialName()
fun IrType.serialName(): String = this.classOrNull!!.owner.serialName()
fun IrClass.serialName(): String {
return descriptor.serialName() // TODO
return annotations.serialNameValue ?: fqNameWhenAvailable?.asString() ?: error("${this.render()} does not have fqName")
}
fun AbstractSerialGenerator.allSealedSerializableSubclassesFor(
fun AbstractIrGenerator.allSealedSerializableSubclassesFor(
irClass: IrClass,
context: SerializationPluginContext
): Pair<List<IrSimpleType>, List<IrClassSymbol>> {
@@ -278,9 +277,7 @@ fun IrClass.findEnumValuesMethod() = this.functions.singleOrNull { f ->
internal fun IrClass.enumEntries(): List<IrEnumEntry> {
check(this.kind == ClassKind.ENUM_CLASS)
return declarations.filterIsInstance<IrEnumEntry>()
// .filter { it.kind == ClassKind.ENUM_ENTRY }
.toList()
return declarations.filterIsInstance<IrEnumEntry>().toList()
}
internal fun IrClass.isEnumWithSerialInfoAnnotation(): Boolean {
@@ -290,14 +287,14 @@ internal fun IrClass.isEnumWithSerialInfoAnnotation(): Boolean {
}
internal val List<IrConstructorCall>.hasAnySerialAnnotation: Boolean
get() = false // TODO serialNameValue != null || any { it.annotationClass?.isSerialInfoAnnotation == true }
get() = serialNameValue != null || any { it.annotationClass.isSerialInfoAnnotation == true }
internal val List<IrConstructorCall>.serialNameValue: String?
get() = null // todo("List<IrConstructorCall>.serialNameValue") @SerialName
get() = findAnnotation(SerializationAnnotations.serialNameAnnotationFqName)?.getStringConstArgument(0) // @SerialName("foo")
internal fun getSerializableClassDescriptorBySerializer(serializer: IrClass): IrClass? {
val serializerForClass = serializer.serializerForClass
if (serializerForClass != null) return serializerForClass.classOrNull?.owner
if (serializerForClass != null) return serializerForClass.owner
if (serializer.name !in setOf(
SerialEntityNames.SERIALIZER_CLASS_NAME,
SerialEntityNames.GENERATED_SERIALIZER_CLASS
@@ -316,9 +313,14 @@ internal fun isKSerializer(type: IrType): Boolean {
}
internal fun IrClass.findPluginGeneratedMethod(name: String): IrSimpleFunction? {
return this.functions.find { it.name.asString() == name && it.origin == IrDeclarationOrigin.GeneratedByPlugin(SerializationPluginKey) }
return this.functions.find {
it.name.asString() == name && it.isFromPlugin()
}
}
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 {
@@ -328,4 +330,8 @@ internal fun IrConstructor.isSerializationCtor(): Boolean {
} == true
}
internal fun IrExpression.isInitializePropertyFromParameter(): Boolean = this is IrGetValueImpl && this.origin == IrStatementOrigin.INITIALIZE_PROPERTY_FROM_PARAMETER
internal fun IrExpression.isInitializePropertyFromParameter(): Boolean =
this is IrGetValueImpl && this.origin == IrStatementOrigin.INITIALIZE_PROPERTY_FROM_PARAMETER
internal val IrConstructorCall.annotationClass
get() = this.symbol.owner.constructedClass
@@ -7,15 +7,16 @@ 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.ir.deepCopyWithVariables
import org.jetbrains.kotlin.backend.common.lower.irIfThen
import org.jetbrains.kotlin.backend.common.sourceElement
import org.jetbrains.kotlin.backend.jvm.functionByName
import org.jetbrains.kotlin.backend.jvm.ir.representativeUpperBound
import org.jetbrains.kotlin.builtins.KotlinBuiltIns
import org.jetbrains.kotlin.builtins.StandardNames
import org.jetbrains.kotlin.descriptors.*
import org.jetbrains.kotlin.ir.builders.*
import org.jetbrains.kotlin.ir.builders.declarations.buildFun
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.*
@@ -25,19 +26,20 @@ 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.ir.visitors.IrElementTransformerVoid
import org.jetbrains.kotlin.backend.jvm.ir.representativeUpperBound
import org.jetbrains.kotlin.builtins.StandardNames
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.psi
import org.jetbrains.kotlin.resolve.descriptorUtil.*
import org.jetbrains.kotlin.resolve.descriptorUtil.fqNameSafe
import org.jetbrains.kotlin.resolve.descriptorUtil.isEffectivelyExternal
import org.jetbrains.kotlin.resolve.jvm.JvmPrimitiveType
import org.jetbrains.kotlin.resolve.source.getPsi
import org.jetbrains.kotlin.types.*
import org.jetbrains.kotlin.types.typeUtil.*
import org.jetbrains.kotlin.types.KotlinType
import org.jetbrains.kotlin.types.TypeProjectionImpl
import org.jetbrains.kotlin.types.Variance
import org.jetbrains.kotlin.types.replace
import org.jetbrains.kotlin.types.typeUtil.replaceArgumentsWithStarProjections
import org.jetbrains.kotlin.types.typeUtil.representativeUpperBound
import org.jetbrains.kotlin.util.OperatorNameConventions
import org.jetbrains.kotlinx.serialization.compiler.backend.common.*
import org.jetbrains.kotlinx.serialization.compiler.backend.jvm.*
@@ -71,9 +73,12 @@ interface IrBuilderExtension {
}
fun <F: IrFunction> addFunctionBody(function: F, bodyGen: IrBlockBodyBuilder.(F) -> Unit) {
function.body = DeclarationIrBuilder(compilerContext, function.symbol, function.startOffset, function.endOffset).irBlockBody(
function.startOffset,
function.endOffset
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) }
}
@@ -136,7 +141,7 @@ interface IrBuilderExtension {
): 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.descriptor.valueParameters.size == 2 && it.descriptor.valueParameters[0].type == lazySafeModeClassDescriptor.defaultType
it.owner.valueParameters.size == 2 && it.owner.valueParameters[0].type == lazySafeModeClassDescriptor.defaultType
}
val publicationEntryDescriptor = lazySafeModeClassDescriptor.enumEntries().single { it.name == LAZY_PUBLICATION_MODE_NAME }
@@ -814,7 +819,7 @@ interface IrBuilderExtension {
): IrExpression? {
val nullableSerClass = compilerContext.referenceProperties(SerialEntityNames.wrapIntoNullableCallableId).single()
val serializer =
property.serializableWith?.classOrNull
property.serializableWith(compilerContext)
?: if (!property.type.isTypeParameter()) generator.findTypeSerializerOrContext(
compilerContext,
property.type
@@ -883,7 +888,7 @@ interface IrBuilderExtension {
}
fun IrBuilderWithScope.serializerInstance(
enclosingGenerator: AbstractSerialGenerator,
enclosingGenerator: AbstractIrGenerator,
serializerClassOriginal: ClassDescriptor?,
module: ModuleDescriptor,
kType: IrType,
@@ -901,7 +906,7 @@ interface IrBuilderExtension {
}
fun IrBuilderWithScope.serializerInstance(
enclosingGenerator: AbstractSerialGenerator,
enclosingGenerator: AbstractIrGenerator,
serializerClassOriginal: IrClassSymbol?,
pluginContext: SerializationPluginContext,
kType: IrType,
@@ -960,8 +965,7 @@ interface IrBuilderExtension {
thisIrType.arguments.map {
val argSer = enclosingGenerator.findTypeSerializerOrContext(
compilerContext,
it.typeOrNull!!, //todo: handle star projections here
sourceElement = serializerClassOriginal.owner.sourceElement()?.psi
it.typeOrNull!! //todo: handle star projections here?
)
instantiate(argSer, it.typeOrNull!!)!!
})
@@ -1076,8 +1080,7 @@ interface IrBuilderExtension {
args = kType.arguments.map {
val argSer = enclosingGenerator.findTypeSerializerOrContext(
pluginContext,
it.typeOrNull!!, // todo: stars
sourceElement = serializerClassOriginal.owner.source.getPsi()
it.typeOrNull!! // todo: stars?
)
instantiate(argSer, it.typeOrNull!!) ?: return null
}
@@ -1242,11 +1245,10 @@ interface IrBuilderExtension {
}
fun IrClass.findWriteSelfMethod(): IrSimpleFunction? =
declarations.filter { it is IrSimpleFunction && it.name == SerialEntityNames.WRITE_SELF_NAME && !it.isFakeOverride }
.takeUnless(Collection<*>::isEmpty)?.single() as IrSimpleFunction?
declarations.singleOrNull { it is IrSimpleFunction && it.name == SerialEntityNames.WRITE_SELF_NAME && !it.isFakeOverride } as IrSimpleFunction?
fun IrBlockBodyBuilder.serializeAllProperties(
generator: AbstractSerialGenerator,
generator: AbstractIrGenerator,
serializableIrClass: IrClass,
serializableProperties: List<IrSerializableProperty>,
objectToSerialize: IrValueDeclaration,
@@ -1339,7 +1341,7 @@ interface IrBuilderExtension {
fun IrBlockBodyBuilder.formEncodeDecodePropertyCall(
enclosingGenerator: AbstractSerialGenerator,
enclosingGenerator: AbstractIrGenerator,
encoder: IrExpression,
property: IrSerializableProperty,
whenHaveSerializer: (serializer: IrExpression, sti: IrSerialTypeInfo) -> FunctionWithArgs,
@@ -5,25 +5,23 @@
package org.jetbrains.kotlinx.serialization.compiler.backend.ir
import org.jetbrains.kotlin.descriptors.*
import org.jetbrains.kotlin.incremental.components.NoLookupLocation
import org.jetbrains.kotlin.ir.builders.*
import org.jetbrains.kotlin.descriptors.ClassKind
import org.jetbrains.kotlin.descriptors.FunctionDescriptor
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.types.impl.IrErrorClassImpl.endOffset
import org.jetbrains.kotlin.ir.types.impl.IrErrorClassImpl.startOffset
import org.jetbrains.kotlin.ir.types.toKotlinType
import org.jetbrains.kotlin.ir.util.*
import org.jetbrains.kotlin.name.ClassId
import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.resolve.calls.components.isVararg
import org.jetbrains.kotlin.resolve.descriptorUtil.fqNameSafe
import org.jetbrains.kotlin.resolve.descriptorUtil.module
import org.jetbrains.kotlinx.serialization.compiler.backend.common.SerializableCompanionCodegen
import org.jetbrains.kotlinx.serialization.compiler.backend.common.AbstractIrGenerator
import org.jetbrains.kotlinx.serialization.compiler.backend.common.findTypeSerializer
import org.jetbrains.kotlinx.serialization.compiler.backend.common.getSerializableClassByCompanion
import org.jetbrains.kotlinx.serialization.compiler.backend.common.isKSerializer
@@ -34,9 +32,11 @@ class SerializableCompanionIrGenerator(
val irClass: IrClass,
val serializableIrClass: IrClass,
override val compilerContext: SerializationPluginContext,
) : SerializableCompanionCodegen(irClass.descriptor, null), IrBuilderExtension {
) : AbstractIrGenerator(irClass), IrBuilderExtension {
override fun getSerializerGetterDescriptor(): FunctionDescriptor {
private val serializableDescriptor = serializableIrClass.descriptor
private fun getSerializerGetterDescriptor(): FunctionDescriptor {
return irClass.findDeclaration<IrSimpleFunction> {
(it.valueParameters.size == serializableDescriptor.declaredTypeParameters.size
&& it.valueParameters.all { p -> isKSerializer(p.type) }) && isKSerializer(it.returnType)
@@ -46,6 +46,19 @@ class SerializableCompanionIrGenerator(
)
}
fun generate() {
val serializerGetterDescriptor = getSerializerGetterDescriptor()
if (serializableDescriptor.isSerializableObject
|| serializableDescriptor.isAbstractOrSealedSerializableClass()
|| serializableDescriptor.isSerializableEnum()
) {
generateLazySerializerGetter(serializerGetterDescriptor)
} else {
generateSerializerGetter(serializerGetterDescriptor)
}
}
companion object {
fun generate(
irClass: IrClass,
@@ -97,7 +110,7 @@ class SerializableCompanionIrGenerator(
irSerializableClass.annotations += annotationCtorCall
}
override fun generateLazySerializerGetter(methodDescriptor: FunctionDescriptor) {
fun generateLazySerializerGetter(methodDescriptor: FunctionDescriptor) {
val serializer = requireNotNull(
findTypeSerializer(
compilerContext,
@@ -105,14 +118,14 @@ class SerializableCompanionIrGenerator(
)
)
val kSerializerIrClass = serializer.owner
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(
this@SerializableCompanionIrGenerator,
serializer, compilerContext, kSerializerIrClass.defaultType
serializer, compilerContext, serializableIrClass.defaultType
)
patchSerializableClassWithMarkerAnnotation(kSerializerIrClass)
+irReturn(requireNotNull(expr))
@@ -124,7 +137,7 @@ class SerializableCompanionIrGenerator(
generateSerializerFactoryIfNeeded(methodDescriptor)
}
override fun generateSerializerGetter(methodDescriptor: FunctionDescriptor) {
fun generateSerializerGetter(methodDescriptor: FunctionDescriptor) {
irClass.contributeFunction(methodDescriptor) { getter ->
val serializer = requireNotNull(
findTypeSerializer(
@@ -145,7 +158,7 @@ class SerializableCompanionIrGenerator(
}
private fun generateSerializerFactoryIfNeeded(getterDescriptor: FunctionDescriptor) {
if (!companionDescriptor.needSerializerFactory()) return
if (!irClass.descriptor.needSerializerFactory()) return
val serialFactoryDescriptor = irClass.findDeclaration<IrSimpleFunction> {
it.valueParameters.size == 1
&& it.valueParameters.first().isVararg
@@ -7,7 +7,8 @@ package org.jetbrains.kotlinx.serialization.compiler.backend.ir
import org.jetbrains.kotlin.backend.common.lower.irThrow
import org.jetbrains.kotlin.codegen.CompilationException
import org.jetbrains.kotlin.descriptors.*
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.*
@@ -24,7 +25,6 @@ import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.util.OperatorNameConventions
import org.jetbrains.kotlin.utils.getOrPutNullable
import org.jetbrains.kotlinx.serialization.compiler.backend.common.*
import org.jetbrains.kotlinx.serialization.compiler.backend.common.isStaticSerializable
import org.jetbrains.kotlinx.serialization.compiler.diagnostic.serializableAnnotationIsUseless
import org.jetbrains.kotlinx.serialization.compiler.extensions.SerializationPluginContext
import org.jetbrains.kotlinx.serialization.compiler.resolve.*
@@ -37,9 +37,9 @@ import org.jetbrains.kotlinx.serialization.compiler.resolve.SerialEntityNames.SE
class SerializableIrGenerator(
val irClass: IrClass,
override val compilerContext: SerializationPluginContext
) : AbstractSerialGenerator(null, irClass.descriptor), IrBuilderExtension {
) : AbstractIrGenerator(irClass), IrBuilderExtension {
protected val properties = bindingContext.serializablePropertiesForIrBackend(irClass)
protected val properties = serializablePropertiesForIrBackend(irClass)
private val serialDescriptorClass = compilerContext.referenceClass(
ClassId(
@@ -272,7 +272,7 @@ class SerializableIrGenerator(
): Int {
check(superClass.isInternalSerializable)
val superCtorRef = serializableSyntheticConstructor(superClass)!!
val superProperties = bindingContext.serializablePropertiesForIrBackend(superClass).serializableProperties
val superProperties = serializablePropertiesForIrBackend(superClass).serializableProperties
val superSlots = superProperties.bitMaskSlotCount()
val arguments = allValueParameters.subList(0, superSlots) +
allValueParameters.subList(propertiesStart, propertiesStart + superProperties.size) +
@@ -309,7 +309,7 @@ class SerializableIrGenerator(
var ignoreIndexTo = -1
val superClass = irClass.getSuperClassOrAny()
if (superClass.descriptor.isInternalSerializable) {
ignoreIndexTo = bindingContext.serializablePropertiesForIrBackend(superClass).serializableProperties.size
ignoreIndexTo = serializablePropertiesForIrBackend(superClass).serializableProperties.size
// call super.writeSelf
var superWriteSelfF = superClass.findWriteSelfMethod()
@@ -374,12 +374,7 @@ class SerializableIrGenerator(
private fun generateSyntheticMethods() {
val serializerDescriptor = irClass.classSerializer(compilerContext)?.owner ?: return
if (irClass.shouldHaveSpecificSyntheticMethods { serializerDescriptor.findPluginGeneratedMethod(SAVE) }) {
val func =
irClass.functions.singleOrNull { function ->
function.name == SerialEntityNames.WRITE_SELF_NAME &&
function.modality == Modality.FINAL &&
function.returnType.isUnit()
} ?: return
val func = irClass.findWriteSelfMethod() ?: return
generateWriteSelfMethod(func)
}
}
@@ -5,18 +5,26 @@
package org.jetbrains.kotlinx.serialization.compiler.backend.ir
import org.jetbrains.kotlin.ir.deepCopyWithVariables
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.*
import org.jetbrains.kotlin.descriptors.Modality
import org.jetbrains.kotlin.descriptors.PropertyDescriptor
import org.jetbrains.kotlin.descriptors.ValueParameterDescriptor
import org.jetbrains.kotlin.ir.builders.*
import org.jetbrains.kotlin.ir.declarations.*
import org.jetbrains.kotlin.ir.expressions.*
import org.jetbrains.kotlin.ir.expressions.impl.*
import org.jetbrains.kotlin.ir.deepCopyWithVariables
import org.jetbrains.kotlin.ir.expressions.IrConstructorCall
import org.jetbrains.kotlin.ir.expressions.IrExpression
import org.jetbrains.kotlin.ir.expressions.IrExpressionBody
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.expressions.mapValueParametersIndexed
import org.jetbrains.kotlin.ir.symbols.IrClassSymbol
import org.jetbrains.kotlin.ir.symbols.IrConstructorSymbol
import org.jetbrains.kotlin.ir.symbols.IrFunctionSymbol
@@ -32,7 +40,6 @@ import org.jetbrains.kotlin.utils.addToStdlib.cast
import org.jetbrains.kotlinx.serialization.compiler.backend.common.*
import org.jetbrains.kotlinx.serialization.compiler.extensions.SerializationDescriptorSerializerPlugin
import org.jetbrains.kotlinx.serialization.compiler.extensions.SerializationPluginContext
import org.jetbrains.kotlinx.serialization.compiler.fir.SerializationPluginKey
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
@@ -41,8 +48,6 @@ import org.jetbrains.kotlinx.serialization.compiler.resolve.SerialEntityNames.SA
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.getSerializableClassDescriptorBySerializer
import org.jetbrains.kotlinx.serialization.compiler.resolve.isEnumWithLegacyGeneratedSerializer
object SERIALIZABLE_PLUGIN_ORIGIN : IrDeclarationOriginImpl("SERIALIZER", true)
@@ -53,18 +58,18 @@ open class SerializerIrGenerator(
final override val compilerContext: SerializationPluginContext,
metadataPlugin: SerializationDescriptorSerializerPlugin?,
private val serialInfoJvmGenerator: SerialInfoImplJvmIrGenerator,
) : AbstractSerialGenerator(null, irClass.descriptor), IrBuilderExtension {
) : AbstractIrGenerator(irClass), IrBuilderExtension {
protected val serializableIrClass = getSerializableClassDescriptorBySerializer(irClass)!!
protected val serialName: String = serializableIrClass.serialName()
protected val properties = bindingContext.serializablePropertiesForIrBackend(serializableIrClass, metadataPlugin)
protected val properties = serializablePropertiesForIrBackend(serializableIrClass, metadataPlugin)
protected val serializableProperties = properties.serializableProperties
protected val isGeneratedSerializer = irClass.descriptor.typeConstructor.supertypes.any(::isGeneratedKSerializer) // TODO TODO
protected val generatedSerialDescPropertyDescriptor = getProperty(
SerialEntityNames.SERIAL_DESC_FIELD,
{ true }
)?.takeIf { it.origin == IrDeclarationOrigin.GeneratedByPlugin(SerializationPluginKey) }
)?.takeIf { it.isFromPlugin() }
protected val anySerialDescProperty = getProperty(
SerialEntityNames.SERIAL_DESC_FIELD,
@@ -103,7 +108,7 @@ open class SerializerIrGenerator(
// how to (auto)create backing field and getter/setter?
compilerContext.symbolTable.withReferenceScope(irClass) {
prop = generateSimplePropertyWithBackingField(desc.descriptor, irClass) // TODO check if this works correctly with old FE
prop = generateSimplePropertyWithBackingField(desc.descriptor, irClass)
// TODO: Do not use descriptors here
localSerializersFieldsDescriptors = findLocalSerializersFieldDescriptors().map { prop ->
@@ -600,7 +605,7 @@ open class SerializerIrGenerator(
if (!prop && (save || load))
generateSerialDesc()
if (serializableIrClass.typeParameters.isNotEmpty()) {
findSerializerConstructorForTypeArgumentsSerializers(irClass)?.let {
findSerializerConstructorForTypeArgumentsSerializers(irClass)?.takeIf { it.owner.isFromPlugin() }?.let {
generateGenericFieldsAndConstructor(it.owner)
}
}
@@ -6,11 +6,16 @@
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.declarations.*
import org.jetbrains.kotlin.ir.declarations.IrClass
import org.jetbrains.kotlin.ir.declarations.IrFile
import org.jetbrains.kotlin.ir.declarations.IrModuleFragment
import org.jetbrains.kotlin.ir.declarations.IrSimpleFunction
import org.jetbrains.kotlin.ir.visitors.IrElementTransformerVoid
import org.jetbrains.kotlin.ir.visitors.IrElementVisitorVoid
import org.jetbrains.kotlin.ir.visitors.acceptChildrenVoid
@@ -56,12 +61,16 @@ private class SerializerClassLowering(
private val serialInfoJvmGenerator = SerialInfoImplJvmIrGenerator(context, moduleFragment).also { context.serialInfoImplJvmIrGenerator = it }
override fun lower(irClass: IrClass) {
SerializableIrGenerator.generate(irClass, context)
SerializerIrGenerator.generate(irClass, context, context.metadataPlugin, serialInfoJvmGenerator)
SerializableCompanionIrGenerator.generate(irClass, context)
try {
SerializableIrGenerator.generate(irClass, context)
SerializerIrGenerator.generate(irClass, context, context.metadataPlugin, serialInfoJvmGenerator)
SerializableCompanionIrGenerator.generate(irClass, context)
if (context.platform.isJvm() && KSerializerDescriptorResolver.isSerialInfoImpl(irClass.descriptor)) {
serialInfoJvmGenerator.generate(irClass)
if (context.platform.isJvm() && KSerializerDescriptorResolver.isSerialInfoImpl(irClass.descriptor)) {
serialInfoJvmGenerator.generate(irClass)
}
} catch (e: Exception) {
throw CompilationException("kotlinx.serialization compiler plugin internal error: unable to transform declaration, see cause", irClass.fileParent, irClass, e)
}
}
}
@@ -21,7 +21,6 @@ import org.jetbrains.kotlin.resolve.lazy.declarations.ClassMemberDeclarationProv
import org.jetbrains.kotlin.types.KotlinType
import org.jetbrains.kotlinx.serialization.compiler.backend.common.SerializerCodegen
import org.jetbrains.kotlinx.serialization.compiler.resolve.*
import java.util.*
open class SerializationResolveExtension @JvmOverloads constructor(val metadataPlugin: SerializationDescriptorSerializerPlugin? = null) : SyntheticResolveExtension {
override fun getSyntheticNestedClassNames(thisDescriptor: ClassDescriptor): List<Name> = when {
@@ -48,6 +47,16 @@ open class SerializationResolveExtension @JvmOverloads constructor(val metadataP
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)
@@ -27,7 +27,6 @@ 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.backend.ir.SimpleSyntheticPropertyDescriptor
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
@@ -220,43 +219,55 @@ object KSerializerDescriptorResolver {
}
private fun createSerializableClassPropertyDescriptor(
companionDescriptor: ClassDescriptor,
classDescriptor: ClassDescriptor
): PropertyDescriptor =
doCreateSerializerProperty(companionDescriptor, classDescriptor, SerialEntityNames.SERIAL_DESC_FIELD_NAME)
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(
companionDescriptor: ClassDescriptor,
classDescriptor: ClassDescriptor,
name: Name
thisDescriptor: ClassDescriptor,
name: Name,
type: KotlinType,
typeParameters: List<TypeParameterDescriptor> = emptyList(),
visibility: DescriptorVisibility = DescriptorVisibilities.PRIVATE,
modality: Modality = Modality.FINAL,
needBackingField: Boolean = false
): PropertyDescriptor {
val typeParam = listOf(createProjection(classDescriptor.defaultType, Variance.INVARIANT, null))
val propertyFromSerializer = companionDescriptor.getGeneratedSerializerDescriptor().getMemberScope(typeParam)
.getContributedVariables(name, NoLookupLocation.FROM_BUILTINS).single()
val propertyDescriptor = PropertyDescriptorImpl.create(
companionDescriptor, Annotations.EMPTY, Modality.OPEN, DescriptorVisibilities.PUBLIC, false, name,
CallableMemberDescriptor.Kind.SYNTHESIZED, companionDescriptor.source, false, false, false, false, false, false
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(
propertyFromSerializer.type,
propertyFromSerializer.typeParameters,
companionDescriptor.thisAsReceiverParameter,
type,
typeParameters,
thisDescriptor.thisAsReceiverParameter,
extensionReceiverParameter,
emptyList()
)
val propertyGetter = PropertyGetterDescriptorImpl(
propertyDescriptor, Annotations.EMPTY, Modality.OPEN, DescriptorVisibilities.PUBLIC, false, false, false,
CallableMemberDescriptor.Kind.SYNTHESIZED, null, companionDescriptor.source
propertyDescriptor, Annotations.EMPTY, modality, visibility, false, false, false,
CallableMemberDescriptor.Kind.SYNTHESIZED, null, thisDescriptor.source
)
propertyGetter.initialize(type)
propertyGetter.initialize(propertyFromSerializer.type)
propertyDescriptor.initialize(propertyGetter, null)
propertyDescriptor.overriddenDescriptors = listOf(propertyFromSerializer)
val backingField = if (needBackingField) FieldDescriptorImpl(Annotations.EMPTY, propertyDescriptor) else null
propertyDescriptor.initialize(propertyGetter, null, backingField, null)
return propertyDescriptor
}
@@ -650,7 +661,7 @@ object KSerializerDescriptorResolver {
serializerClass,
listOf(TypeProjectionImpl(param.defaultType))
)
val desc = SimpleSyntheticPropertyDescriptor(serializerDescriptor, "$typeArgPrefix$index", pType)
val desc = doCreateSerializerProperty(serializerDescriptor, Name.identifier("$typeArgPrefix$index"), pType, needBackingField = true)
return listOf(desc)
}
}
@@ -165,7 +165,10 @@ internal val ISerializableProperties<*, *, *>.goldenMaskList: List<Int>
internal fun List<ISerializableProperty<*, *>>.bitMaskSlotCount() = size / 32 + 1
internal fun bitMaskSlotAt(propertyIndex: Int) = propertyIndex / 32
internal fun BindingContext.serializablePropertiesFor(classDescriptor: ClassDescriptor, serializationDescriptorSerializer: SerializationDescriptorSerializerPlugin? = null): SerializableProperties {
internal 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
@@ -187,59 +190,59 @@ class IrSerializableProperties(
) : ISerializableProperties<IrProperty, IrSimpleType, IrSerializableProperty> {
}
internal fun BindingContext?.serializablePropertiesForIrBackend(
internal fun serializablePropertiesForIrBackend(
classDescriptor: IrClass,
serializationDescriptorSerializer: SerializationDescriptorSerializerPlugin? = null
): IrSerializableProperties {
if (this != null) {
// can work with old FE
TODO("Support this for old FE")
} else {
val properties = classDescriptor.properties.toList()
val primaryConstructorParams = classDescriptor.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 (classDescriptor.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
.filter { !it.isFakeOverride && !it.isDelegated }
.filter(::isPropSerializable)
.map {
val isConstructorParameterWithDefault = primaryParamsAsProps[it] ?: false
IrSerializableProperty(
it,
isConstructorParameterWithDefault,
it.backingField != null,
it.backingField?.initializer.let { init -> init != null && !init.expression.isInitializePropertyFromParameter() } || isConstructorParameterWithDefault
)
}
.filterNot { it.transient }
.partition { primaryParamsAsProps.contains(it.descriptor) }
val serializableProps = run {
val supers = classDescriptor.getParentClassNotAny()
if (supers == null || !supers.isInternalSerializable)
primaryCtorSerializableProps + bodySerializableProps
else
serializablePropertiesForIrBackend(
supers,
serializationDescriptorSerializer
).serializableProperties + primaryCtorSerializableProps + bodySerializableProps
} // todo: implement unsorting
val isExternallySerializable =
classDescriptor.isInternallySerializableEnum() || primaryConstructorParams.size == primaryParamsAsProps.size
return IrSerializableProperties(serializableProps, isExternallySerializable, primaryCtorSerializableProps, bodySerializableProps)
val properties = classDescriptor.properties.toList()
val primaryConstructorParams = classDescriptor.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 (classDescriptor.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.descriptor) }
val serializableProps = run {
val supers = classDescriptor.getParentClassNotAny()
if (supers == null || !supers.isInternalSerializable)
primaryCtorSerializableProps + bodySerializableProps
else
serializablePropertiesForIrBackend(
supers,
serializationDescriptorSerializer
).serializableProperties + primaryCtorSerializableProps + bodySerializableProps
} // todo: implement unsorting
val isExternallySerializable =
classDescriptor.isInternallySerializableEnum() || primaryConstructorParams.size == primaryParamsAsProps.size
return IrSerializableProperties(serializableProps, isExternallySerializable, primaryCtorSerializableProps, bodySerializableProps)
}
fun IrClass.getParentClassNotAny(): IrClass? {
@@ -19,24 +19,17 @@ package org.jetbrains.kotlinx.serialization.compiler.resolve
import org.jetbrains.kotlin.descriptors.ClassDescriptor
import org.jetbrains.kotlin.descriptors.PropertyDescriptor
import org.jetbrains.kotlin.descriptors.ValueParameterDescriptor
import org.jetbrains.kotlin.fir.scopes.impl.overrides
import org.jetbrains.kotlin.ir.declarations.IrProperty
import org.jetbrains.kotlin.ir.types.IrSimpleType
import org.jetbrains.kotlin.ir.types.IrType
import org.jetbrains.kotlin.ir.types.defaultType
import org.jetbrains.kotlin.ir.util.hasAnnotation
import org.jetbrains.kotlin.ir.util.module
import org.jetbrains.kotlin.psi.KtDeclarationWithInitializer
import org.jetbrains.kotlin.psi.KtParameter
import org.jetbrains.kotlin.psi.ValueArgument
import org.jetbrains.kotlin.resolve.descriptorUtil.module
import org.jetbrains.kotlin.resolve.source.getPsi
import org.jetbrains.kotlin.types.KotlinType
import org.jetbrains.kotlin.types.checker.SimpleClassicTypeSystemContext.asSimpleType
import org.jetbrains.kotlinx.serialization.compiler.backend.common.analyzeSpecialSerializers
import org.jetbrains.kotlinx.serialization.compiler.backend.common.genericIndex
import org.jetbrains.kotlinx.serialization.compiler.backend.common.overridenSerializer
import org.jetbrains.kotlinx.serialization.compiler.backend.common.serialNameValue
import org.jetbrains.kotlinx.serialization.compiler.backend.common.serializableWith
import org.jetbrains.kotlinx.serialization.compiler.extensions.SerializationPluginContext
class SerializableProperty(
override val descriptor: PropertyDescriptor,
@@ -48,7 +41,7 @@ class SerializableProperty(
override val type = descriptor.type
override val genericIndex = type.genericIndex
val module = descriptor.module
override val serializableWith = descriptor.serializableWith ?: analyzeSpecialSerializers(module, descriptor.annotations)?.defaultType
val serializableWith = descriptor.serializableWith ?: analyzeSpecialSerializers(module, descriptor.annotations)?.defaultType
override val optional = !descriptor.annotations.serialRequired && declaresDefaultValue
override val transient = descriptor.annotations.serialTransient || !hasBackingField
val annotationsWithArguments: List<Triple<ClassDescriptor, List<ValueArgument>, List<ValueParameterDescriptor>>> =
@@ -61,7 +54,6 @@ interface ISerializableProperty<D, T> {
val name: String
val type: T
val genericIndex: Int?
val serializableWith: T?
val optional: Boolean
val transient: Boolean
}
@@ -75,7 +67,7 @@ class IrSerializableProperty(
override val name = descriptor.annotations.serialNameValue ?: descriptor.name.asString()
override val type = descriptor.getter!!.returnType as IrSimpleType
override val genericIndex = type.genericIndex
override val serializableWith = type.overridenSerializer /* ?:analyzeSpecialSerializers(module, descriptor.annotations)?.defaultType */ // TODO
fun serializableWith(ctx: SerializationPluginContext) = descriptor.annotations.serializableWith() ?: analyzeSpecialSerializers(ctx, descriptor.annotations)
override val optional = !descriptor.annotations.hasAnnotation(SerializationAnnotations.requiredAnnotationFqName) && declaresDefaultValue
override val transient = descriptor.annotations.hasAnnotation(SerializationAnnotations.serialTransientFqName) || !hasBackingField
}
@@ -6,10 +6,9 @@
package org.jetbrains.kotlinx.serialization;
import com.intellij.testFramework.TestDataPath;
import org.jetbrains.kotlin.test.util.KtTestUtil;
import org.jetbrains.kotlin.test.TargetBackend;
import org.jetbrains.kotlin.test.TestMetadata;
import org.junit.jupiter.api.Nested;
import org.jetbrains.kotlin.test.util.KtTestUtil;
import org.junit.jupiter.api.Test;
import java.io.File;
@@ -25,12 +24,24 @@ public class SerializationIrBoxTestGenerated extends AbstractSerializationIrBoxT
KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("plugins/kotlin-serialization/kotlin-serialization-compiler/testData/boxIr"), Pattern.compile("^(.+)\\.kt$"), null, TargetBackend.JVM_IR, true);
}
@Test
@TestMetadata("annotationsOnFile.kt")
public void testAnnotationsOnFile() throws Exception {
runTest("plugins/kotlin-serialization/kotlin-serialization-compiler/testData/boxIr/annotationsOnFile.kt");
}
@Test
@TestMetadata("classSerializerAsObject.kt")
public void testClassSerializerAsObject() throws Exception {
runTest("plugins/kotlin-serialization/kotlin-serialization-compiler/testData/boxIr/classSerializerAsObject.kt");
}
@Test
@TestMetadata("generics.kt")
public void testGenerics() throws Exception {
runTest("plugins/kotlin-serialization/kotlin-serialization-compiler/testData/boxIr/generics.kt");
}
@Test
@TestMetadata("metaSerializable.kt")
public void testMetaSerializable() throws Exception {
@@ -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,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"
}