Rework SerialInfo$Impl annotation implementation to be available in FIR:
- IR plugin does not use it anymore, regular annotation instantiation feature is used (insertion of IrConstructorCall(annotationCtorSymbol)). - IR plugin still generates `SerialInfo$Impl` class to be compatible with previously compiled code & libraries. - Custom implementation over descriptors in IR plugin is removed; plugin now delegates to the existing lowering with some tweaks. - $Impl descriptor is removed from FE 1.0 because it is no longer needed for IR plugin; it shouldn't be exposed to users so FIR doesn't need it either. - Since language lowering is now used, it is possible to correctly instantiate SerialInfo annotations with default values even if they are from other modules and even if they were not processed by the plugin. #KT-48733 Fixed Fixes https://github.com/Kotlin/kotlinx.serialization/issues/1574
This commit is contained in:
committed by
Space Team
parent
3c43416042
commit
f1b1837f40
+198
-159
@@ -5,6 +5,7 @@
|
||||
|
||||
package org.jetbrains.kotlin.backend.jvm.lower
|
||||
|
||||
import org.jetbrains.kotlin.backend.common.ir.BuiltinSymbolsBase
|
||||
import org.jetbrains.kotlin.ir.deepCopyWithVariables
|
||||
import org.jetbrains.kotlin.backend.common.lower.*
|
||||
import org.jetbrains.kotlin.backend.common.phaser.makeIrFilePhase
|
||||
@@ -15,6 +16,7 @@ import org.jetbrains.kotlin.backend.jvm.ir.javaClassReference
|
||||
import org.jetbrains.kotlin.backend.jvm.unboxInlineClass
|
||||
import org.jetbrains.kotlin.descriptors.DescriptorVisibilities
|
||||
import org.jetbrains.kotlin.descriptors.Modality
|
||||
import org.jetbrains.kotlin.ir.IrBuiltIns
|
||||
import org.jetbrains.kotlin.ir.builders.*
|
||||
import org.jetbrains.kotlin.ir.builders.declarations.*
|
||||
import org.jetbrains.kotlin.ir.declarations.*
|
||||
@@ -22,8 +24,11 @@ import org.jetbrains.kotlin.ir.expressions.*
|
||||
import org.jetbrains.kotlin.ir.expressions.impl.IrDelegatingConstructorCallImpl
|
||||
import org.jetbrains.kotlin.ir.symbols.IrClassSymbol
|
||||
import org.jetbrains.kotlin.ir.symbols.IrFunctionSymbol
|
||||
import org.jetbrains.kotlin.ir.symbols.IrSimpleFunctionSymbol
|
||||
import org.jetbrains.kotlin.ir.types.*
|
||||
import org.jetbrains.kotlin.ir.util.*
|
||||
import org.jetbrains.kotlin.ir.visitors.IrElementTransformerVoid
|
||||
import org.jetbrains.kotlin.ir.visitors.transformChildrenVoid
|
||||
import org.jetbrains.kotlin.util.OperatorNameConventions
|
||||
import org.jetbrains.kotlin.utils.addToStdlib.safeAs
|
||||
|
||||
@@ -41,6 +46,15 @@ class JvmAnnotationImplementationTransformer(val jvmContext: JvmBackendContext,
|
||||
private val inInlineFunctionScope: Boolean
|
||||
get() = allScopes.any { it.irElement.safeAs<IrDeclaration>()?.isInPublicInlineScope == true }
|
||||
|
||||
private val implementor = AnnotationPropertyImplementor(
|
||||
jvmContext.irFactory,
|
||||
jvmContext.irBuiltIns,
|
||||
jvmContext.ir.symbols,
|
||||
jvmContext.ir.symbols.javaLangClass,
|
||||
jvmContext.ir.symbols.kClassJavaPropertyGetter.symbol,
|
||||
ANNOTATION_IMPLEMENTATION
|
||||
)
|
||||
|
||||
@Suppress("UNUSED_PARAMETER")
|
||||
override fun chooseConstructor(implClass: IrClass, expression: IrConstructorCall) =
|
||||
implClass.constructors.single()
|
||||
@@ -53,88 +67,6 @@ class JvmAnnotationImplementationTransformer(val jvmContext: JvmBackendContext,
|
||||
return super.visitConstructorCall(expression)
|
||||
}
|
||||
|
||||
private fun IrType.kClassToJClassIfNeeded(): IrType = when {
|
||||
this.isKClass() -> jvmContext.ir.symbols.javaLangClass.starProjectedType
|
||||
this.isKClassArray() -> jvmContext.irBuiltIns.arrayClass.typeWith(
|
||||
jvmContext.ir.symbols.javaLangClass.starProjectedType
|
||||
)
|
||||
else -> this
|
||||
}
|
||||
|
||||
private fun IrType.isKClassArray() =
|
||||
this is IrSimpleType && isArray() && arguments.single().typeOrNull?.isKClass() == true
|
||||
|
||||
private fun IrBuilderWithScope.kClassToJClass(irExpression: IrExpression): IrExpression =
|
||||
irGet(
|
||||
jvmContext.ir.symbols.javaLangClass.starProjectedType,
|
||||
null,
|
||||
jvmContext.ir.symbols.kClassJavaPropertyGetter.symbol
|
||||
).apply {
|
||||
extensionReceiver = irExpression
|
||||
}
|
||||
|
||||
/**
|
||||
* Copies array by one element, roughly as following:
|
||||
* val size = kClassArray.size
|
||||
* val result = arrayOfNulls<java.lang.Class>(size)
|
||||
* var i = 0
|
||||
* while(i < size) {
|
||||
* result[i] = kClassArray[i].java
|
||||
* i++
|
||||
* }
|
||||
* Partially taken from ArrayConstructorLowering.kt
|
||||
*/
|
||||
private fun IrBuilderWithScope.kClassArrayToJClassArray(kClassArray: IrExpression): IrExpression {
|
||||
val javaLangClass = jvmContext.ir.symbols.javaLangClass.starProjectedType
|
||||
val jlcArray = jvmContext.ir.symbols.array.typeWith(javaLangClass)
|
||||
val arrayClass = jvmContext.ir.symbols.array.owner
|
||||
val arrayOfNulls = jvmContext.ir.symbols.arrayOfNulls
|
||||
val arraySizeSymbol = arrayClass.findDeclaration<IrProperty> { it.name.asString() == "size" }!!.getter!!
|
||||
|
||||
val block = irBlock {
|
||||
val sourceArray = createTmpVariable(kClassArray, "src", isMutable = false)
|
||||
val index = createTmpVariable(irInt(0), "i", isMutable = true)
|
||||
val size = createTmpVariable(
|
||||
irCall(arraySizeSymbol).apply { dispatchReceiver = irGet(sourceArray) },
|
||||
"size", isMutable = false
|
||||
)
|
||||
val result = createTmpVariable(irCall(arrayOfNulls, jlcArray).apply {
|
||||
listOf(javaLangClass)
|
||||
putValueArgument(0, irGet(size))
|
||||
})
|
||||
val comparison = primitiveOp2(
|
||||
startOffset, endOffset,
|
||||
context.irBuiltIns.lessFunByOperandType[context.irBuiltIns.intType.classifierOrFail]!!,
|
||||
context.irBuiltIns.booleanType,
|
||||
IrStatementOrigin.LT,
|
||||
irGet(index), irGet(size)
|
||||
)
|
||||
val setArraySymbol = arrayClass.functions.single { it.name == OperatorNameConventions.SET }
|
||||
val getArraySymbol = arrayClass.functions.single { it.name == OperatorNameConventions.GET }
|
||||
val inc = context.irBuiltIns.intType.getClass()!!.functions.single { it.name == OperatorNameConventions.INC }
|
||||
+irWhile().also { loop ->
|
||||
loop.condition = comparison
|
||||
loop.body = irBlock {
|
||||
val tempIndex = createTmpVariable(irGet(index))
|
||||
|
||||
val getArray = irCall(getArraySymbol).apply {
|
||||
dispatchReceiver = irGet(sourceArray)
|
||||
putValueArgument(0, irGet(tempIndex))
|
||||
}
|
||||
+irCall(setArraySymbol).apply {
|
||||
dispatchReceiver = irGet(result)
|
||||
putValueArgument(0, irGet(tempIndex))
|
||||
putValueArgument(1, kClassToJClass(getArray))
|
||||
}
|
||||
|
||||
+irSet(index.symbol, irCallOp(inc.symbol, index.type, irGet(index)))
|
||||
}
|
||||
}
|
||||
+irGet(result)
|
||||
}
|
||||
return block
|
||||
}
|
||||
|
||||
// There's no specialized Array.equals for unsigned arrays (as this is a Java function), so we force compiler not to box
|
||||
// result of property getter call
|
||||
override fun IrExpression.transformArrayEqualsArgument(type: IrType, irBuilder: IrBlockBodyBuilder): IrExpression =
|
||||
@@ -184,84 +116,12 @@ class JvmAnnotationImplementationTransformer(val jvmContext: JvmBackendContext,
|
||||
annotationClass: IrClass,
|
||||
generatedConstructor: IrConstructor
|
||||
) {
|
||||
val ctorBodyBuilder = context.createIrBuilder(generatedConstructor.symbol, SYNTHETIC_OFFSET, SYNTHETIC_OFFSET)
|
||||
val ctorBody = context.irFactory.createBlockBody(
|
||||
SYNTHETIC_OFFSET, SYNTHETIC_OFFSET, listOf(
|
||||
IrDelegatingConstructorCallImpl(
|
||||
SYNTHETIC_OFFSET, SYNTHETIC_OFFSET, context.irBuiltIns.unitType, context.irBuiltIns.anyClass.constructors.single(),
|
||||
typeArgumentsCount = 0, valueArgumentsCount = 0
|
||||
)
|
||||
)
|
||||
implementor.implementAnnotationPropertiesAndConstructor(
|
||||
annotationClass.getAnnotationProperties(),
|
||||
implClass,
|
||||
generatedConstructor,
|
||||
this
|
||||
)
|
||||
|
||||
generatedConstructor.body = ctorBody
|
||||
|
||||
annotationClass.getAnnotationProperties().forEach { property ->
|
||||
val propType = property.getter!!.returnType
|
||||
val storedFieldType = propType.kClassToJClassIfNeeded()
|
||||
val propName = property.name
|
||||
val field = context.irFactory.buildField {
|
||||
startOffset = SYNTHETIC_OFFSET
|
||||
endOffset = SYNTHETIC_OFFSET
|
||||
name = propName
|
||||
type = storedFieldType
|
||||
origin = ANNOTATION_IMPLEMENTATION
|
||||
isFinal = true
|
||||
visibility = DescriptorVisibilities.PRIVATE
|
||||
}.also { it.parent = implClass }
|
||||
|
||||
val parameter = generatedConstructor.addValueParameter(propName.asString(), propType)
|
||||
|
||||
val defaultExpression = property.backingField?.initializer?.expression
|
||||
val newDefaultValue: IrExpressionBody? =
|
||||
if (defaultExpression is IrGetValue && defaultExpression.symbol.owner is IrValueParameter) {
|
||||
// INITIALIZE_PROPERTY_FROM_PARAMETER
|
||||
(defaultExpression.symbol.owner as IrValueParameter).defaultValue
|
||||
} else if (defaultExpression != null) {
|
||||
property.backingField!!.initializer
|
||||
} else null
|
||||
parameter.defaultValue = newDefaultValue?.deepCopyWithVariables()?.also { it.transformChildrenVoid() }
|
||||
|
||||
ctorBody.statements += with(ctorBodyBuilder) {
|
||||
val param = irGet(parameter)
|
||||
val fieldValue = when {
|
||||
propType.isKClass() -> kClassToJClass(param)
|
||||
propType.isKClassArray() -> kClassArrayToJClassArray(param)
|
||||
else -> param
|
||||
}
|
||||
irSetField(irGet(implClass.thisReceiver!!), field, fieldValue)
|
||||
}
|
||||
|
||||
val prop = implClass.addProperty {
|
||||
startOffset = SYNTHETIC_OFFSET
|
||||
endOffset = SYNTHETIC_OFFSET
|
||||
name = propName
|
||||
isVar = false
|
||||
origin = ANNOTATION_IMPLEMENTATION
|
||||
}.apply {
|
||||
field.correspondingPropertySymbol = this.symbol
|
||||
backingField = field
|
||||
parent = implClass
|
||||
overriddenSymbols = listOf(property.symbol)
|
||||
}
|
||||
|
||||
prop.addGetter {
|
||||
startOffset = SYNTHETIC_OFFSET
|
||||
endOffset = SYNTHETIC_OFFSET
|
||||
name = propName // Annotation value getter should be named 'x', not 'getX'
|
||||
returnType = propType.kClassToJClassIfNeeded() // On JVM, annotation store j.l.Class even if declared with KClass
|
||||
origin = ANNOTATION_IMPLEMENTATION
|
||||
visibility = DescriptorVisibilities.PUBLIC
|
||||
modality = Modality.FINAL
|
||||
}.apply {
|
||||
correspondingPropertySymbol = prop.symbol
|
||||
dispatchReceiverParameter = implClass.thisReceiver!!.copyTo(this)
|
||||
body = context.createIrBuilder(symbol, SYNTHETIC_OFFSET, SYNTHETIC_OFFSET).irBlockBody {
|
||||
+irReturn(irGetField(irGet(dispatchReceiverParameter!!), field))
|
||||
}
|
||||
overriddenSymbols = listOf(property.getter!!.symbol)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun generateFunctionBodies(
|
||||
@@ -279,4 +139,183 @@ class JvmAnnotationImplementationTransformer(val jvmContext: JvmBackendContext,
|
||||
generator.generateToStringMethod(toStringFun, implProperties)
|
||||
}
|
||||
|
||||
class AnnotationPropertyImplementor(
|
||||
val irFactory: IrFactory,
|
||||
val irBuiltIns: IrBuiltIns,
|
||||
val symbols: BuiltinSymbolsBase,
|
||||
val javaLangClassSymbol: IrClassSymbol,
|
||||
val kClassJavaPropertyGetterSymbol: IrSimpleFunctionSymbol,
|
||||
val originForProp: IrDeclarationOrigin
|
||||
) {
|
||||
|
||||
/**
|
||||
* Copies array by one element, roughly as following:
|
||||
* val size = kClassArray.size
|
||||
* val result = arrayOfNulls<java.lang.Class>(size)
|
||||
* var i = 0
|
||||
* while(i < size) {
|
||||
* result[i] = kClassArray[i].java
|
||||
* i++
|
||||
* }
|
||||
* Partially taken from ArrayConstructorLowering.kt
|
||||
*/
|
||||
private fun IrBuilderWithScope.kClassArrayToJClassArray(kClassArray: IrExpression): IrExpression {
|
||||
val javaLangClassType = javaLangClassSymbol.starProjectedType
|
||||
val jlcArray = symbols.array.typeWith(javaLangClassType)
|
||||
val arrayClass = symbols.array.owner
|
||||
val arrayOfNulls = symbols.arrayOfNulls
|
||||
val arraySizeSymbol = arrayClass.findDeclaration<IrProperty> { it.name.asString() == "size" }!!.getter!!
|
||||
|
||||
val block = irBlock {
|
||||
val sourceArray = createTmpVariable(kClassArray, "src", isMutable = false)
|
||||
val index = createTmpVariable(irInt(0), "i", isMutable = true)
|
||||
val size = createTmpVariable(
|
||||
irCall(arraySizeSymbol).apply { dispatchReceiver = irGet(sourceArray) },
|
||||
"size", isMutable = false
|
||||
)
|
||||
val result = createTmpVariable(irCall(arrayOfNulls, jlcArray).apply {
|
||||
listOf(javaLangClassType)
|
||||
putValueArgument(0, irGet(size))
|
||||
})
|
||||
val comparison = primitiveOp2(
|
||||
startOffset, endOffset,
|
||||
context.irBuiltIns.lessFunByOperandType[context.irBuiltIns.intType.classifierOrFail]!!,
|
||||
context.irBuiltIns.booleanType,
|
||||
IrStatementOrigin.LT,
|
||||
irGet(index), irGet(size)
|
||||
)
|
||||
val setArraySymbol = arrayClass.functions.single { it.name == OperatorNameConventions.SET }
|
||||
val getArraySymbol = arrayClass.functions.single { it.name == OperatorNameConventions.GET }
|
||||
val inc = context.irBuiltIns.intType.getClass()!!.functions.single { it.name == OperatorNameConventions.INC }
|
||||
+irWhile().also { loop ->
|
||||
loop.condition = comparison
|
||||
loop.body = irBlock {
|
||||
val tempIndex = createTmpVariable(irGet(index))
|
||||
|
||||
val getArray = irCall(getArraySymbol).apply {
|
||||
dispatchReceiver = irGet(sourceArray)
|
||||
putValueArgument(0, irGet(tempIndex))
|
||||
}
|
||||
+irCall(setArraySymbol).apply {
|
||||
dispatchReceiver = irGet(result)
|
||||
putValueArgument(0, irGet(tempIndex))
|
||||
putValueArgument(1, kClassToJClass(getArray))
|
||||
}
|
||||
|
||||
+irSet(index.symbol, irCallOp(inc.symbol, index.type, irGet(index)))
|
||||
}
|
||||
}
|
||||
+irGet(result)
|
||||
}
|
||||
return block
|
||||
}
|
||||
|
||||
private fun IrType.kClassToJClassIfNeeded(): IrType = when {
|
||||
this.isKClass() -> javaLangClassSymbol.starProjectedType
|
||||
this.isKClassArray() -> symbols.array.typeWith(
|
||||
javaLangClassSymbol.starProjectedType
|
||||
)
|
||||
|
||||
else -> this
|
||||
}
|
||||
|
||||
private fun IrType.isKClassArray() =
|
||||
this is IrSimpleType && isArray() && arguments.single().typeOrNull?.isKClass() == true
|
||||
|
||||
private fun IrBuilderWithScope.kClassToJClass(irExpression: IrExpression): IrExpression =
|
||||
irGet(
|
||||
javaLangClassSymbol.starProjectedType,
|
||||
null,
|
||||
kClassJavaPropertyGetterSymbol
|
||||
).apply {
|
||||
extensionReceiver = irExpression
|
||||
}
|
||||
|
||||
fun implementAnnotationPropertiesAndConstructor(
|
||||
annotationProperties: List<IrProperty>,
|
||||
implClass: IrClass,
|
||||
generatedConstructor: IrConstructor,
|
||||
defaultValueTransformer: IrElementTransformerVoid?
|
||||
) {
|
||||
val ctorBodyBuilder = irBuiltIns.createIrBuilder(generatedConstructor.symbol, SYNTHETIC_OFFSET, SYNTHETIC_OFFSET)
|
||||
val ctorBody = irFactory.createBlockBody(
|
||||
SYNTHETIC_OFFSET, SYNTHETIC_OFFSET, listOf(
|
||||
IrDelegatingConstructorCallImpl(
|
||||
SYNTHETIC_OFFSET, SYNTHETIC_OFFSET, irBuiltIns.unitType, irBuiltIns.anyClass.constructors.single(),
|
||||
typeArgumentsCount = 0, valueArgumentsCount = 0
|
||||
)
|
||||
)
|
||||
)
|
||||
|
||||
generatedConstructor.body = ctorBody
|
||||
|
||||
annotationProperties.forEach { property ->
|
||||
val propType = property.getter!!.returnType
|
||||
val storedFieldType = propType.kClassToJClassIfNeeded()
|
||||
val propName = property.name
|
||||
val field = irFactory.buildField {
|
||||
startOffset = SYNTHETIC_OFFSET
|
||||
endOffset = SYNTHETIC_OFFSET
|
||||
name = propName
|
||||
type = storedFieldType
|
||||
origin = originForProp
|
||||
isFinal = true
|
||||
visibility = DescriptorVisibilities.PRIVATE
|
||||
}.also { it.parent = implClass }
|
||||
|
||||
val parameter = generatedConstructor.addValueParameter(propName.asString(), propType)
|
||||
|
||||
val defaultExpression = property.backingField?.initializer?.expression
|
||||
val newDefaultValue: IrExpressionBody? =
|
||||
if (defaultExpression is IrGetValue && defaultExpression.symbol.owner is IrValueParameter) {
|
||||
// INITIALIZE_PROPERTY_FROM_PARAMETER
|
||||
(defaultExpression.symbol.owner as IrValueParameter).defaultValue
|
||||
} else if (defaultExpression != null) {
|
||||
property.backingField!!.initializer
|
||||
} else null
|
||||
parameter.defaultValue = newDefaultValue?.deepCopyWithVariables()
|
||||
?.also { if (defaultValueTransformer != null) it.transformChildrenVoid(defaultValueTransformer) }
|
||||
|
||||
ctorBody.statements += with(ctorBodyBuilder) {
|
||||
val param = irGet(parameter)
|
||||
val fieldValue = when {
|
||||
propType.isKClass() -> kClassToJClass(param)
|
||||
propType.isKClassArray() -> kClassArrayToJClassArray(param)
|
||||
else -> param
|
||||
}
|
||||
irSetField(irGet(implClass.thisReceiver!!), field, fieldValue)
|
||||
}
|
||||
|
||||
val prop = implClass.addProperty {
|
||||
startOffset = SYNTHETIC_OFFSET
|
||||
endOffset = SYNTHETIC_OFFSET
|
||||
name = propName
|
||||
isVar = false
|
||||
origin = originForProp
|
||||
}.apply {
|
||||
field.correspondingPropertySymbol = this.symbol
|
||||
backingField = field
|
||||
parent = implClass
|
||||
overriddenSymbols = listOf(property.symbol)
|
||||
}
|
||||
|
||||
prop.addGetter {
|
||||
startOffset = SYNTHETIC_OFFSET
|
||||
endOffset = SYNTHETIC_OFFSET
|
||||
name = propName // Annotation value getter should be named 'x', not 'getX'
|
||||
returnType = propType.kClassToJClassIfNeeded() // On JVM, annotation store j.l.Class even if declared with KClass
|
||||
origin = originForProp
|
||||
visibility = DescriptorVisibilities.PUBLIC
|
||||
modality = Modality.FINAL
|
||||
}.apply {
|
||||
correspondingPropertySymbol = prop.symbol
|
||||
dispatchReceiverParameter = implClass.thisReceiver!!.copyTo(this)
|
||||
body = irBuiltIns.createIrBuilder(symbol, SYNTHETIC_OFFSET, SYNTHETIC_OFFSET).irBlockBody {
|
||||
+irReturn(irGetField(irGet(dispatchReceiverParameter!!), field))
|
||||
}
|
||||
overriddenSymbols = listOf(property.getter!!.symbol)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,6 +10,7 @@ dependencies {
|
||||
compileOnly(project(":compiler:ir.backend.common"))
|
||||
compileOnly(project(":compiler:backend.jvm"))
|
||||
compileOnly(project(":compiler:backend.jvm.codegen"))
|
||||
compileOnly(project(":compiler:backend.jvm.lower"))
|
||||
compileOnly(project(":compiler:ir.tree"))
|
||||
compileOnly(project(":js:js.frontend"))
|
||||
compileOnly(project(":js:js.translator"))
|
||||
|
||||
-1
@@ -366,7 +366,6 @@ interface IrBuilderWithPluginContext {
|
||||
annotations.mapNotNull { annotationCall ->
|
||||
val annotationClass = annotationCall.symbol.owner.parentAsClass
|
||||
if (!annotationClass.isSerialInfoAnnotation) return@mapNotNull null
|
||||
|
||||
annotationCall.deepCopyWithVariables()
|
||||
}
|
||||
|
||||
|
||||
+36
-336
@@ -5,138 +5,29 @@
|
||||
|
||||
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.backend.jvm.lower.JvmAnnotationImplementationTransformer
|
||||
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 {
|
||||
@@ -168,8 +59,40 @@ class SerialInfoImplJvmIrGenerator(
|
||||
}
|
||||
}.symbol
|
||||
|
||||
private fun IrType.isKClassArray() =
|
||||
this is IrSimpleType && isArray() && arguments.single().typeOrNull?.isKClass() == true
|
||||
private val implementor = JvmAnnotationImplementationTransformer.AnnotationPropertyImplementor(
|
||||
context.irFactory,
|
||||
context.irBuiltIns,
|
||||
context.symbols,
|
||||
javaLangClass,
|
||||
kClassJava.owner.getter!!.symbol,
|
||||
SERIALIZATION_PLUGIN_ORIGIN
|
||||
)
|
||||
|
||||
fun generateImplementationFor(annotationClass: IrClass) {
|
||||
|
||||
val properties = annotationClass.declarations.filterIsInstance<IrProperty>()
|
||||
|
||||
val subclass = context.irFactory.buildClass {
|
||||
startOffset = UNDEFINED_OFFSET
|
||||
endOffset = UNDEFINED_OFFSET
|
||||
name = SerialEntityNames.IMPL_NAME
|
||||
origin = SERIALIZATION_PLUGIN_ORIGIN
|
||||
visibility = DescriptorVisibilities.PUBLIC
|
||||
}.apply {
|
||||
parent = annotationClass
|
||||
createImplicitParameterDeclarationWithWrappedDescriptor()
|
||||
superTypes = listOf(annotationClass.defaultType)
|
||||
}
|
||||
annotationClass.declarations.add(subclass)
|
||||
|
||||
val ctor = subclass.addConstructor {
|
||||
startOffset = UNDEFINED_OFFSET
|
||||
endOffset = UNDEFINED_OFFSET
|
||||
visibility = DescriptorVisibilities.PUBLIC
|
||||
}
|
||||
|
||||
implementor.implementAnnotationPropertiesAndConstructor(properties, subclass, ctor, null)
|
||||
}
|
||||
|
||||
private fun createPackage(packageName: String): IrPackageFragment =
|
||||
IrExternalPackageFragmentImpl.createEmptyExternalPackageFragment(
|
||||
@@ -191,227 +114,4 @@ class SerialInfoImplJvmIrGenerator(
|
||||
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
|
||||
}
|
||||
}
|
||||
|
||||
+5
-8
@@ -17,7 +17,6 @@ import org.jetbrains.kotlin.backend.jvm.JvmBackendContext
|
||||
import org.jetbrains.kotlin.backend.jvm.ir.fileParent
|
||||
import org.jetbrains.kotlin.descriptors.findClassAcrossModuleDependencies
|
||||
import org.jetbrains.kotlin.ir.IrElement
|
||||
import org.jetbrains.kotlin.ir.ObsoleteDescriptorBasedAPI
|
||||
import org.jetbrains.kotlin.ir.declarations.*
|
||||
import org.jetbrains.kotlin.ir.symbols.IrClassSymbol
|
||||
import org.jetbrains.kotlin.ir.visitors.IrElementTransformerVoid
|
||||
@@ -29,7 +28,6 @@ import org.jetbrains.kotlin.name.ClassId
|
||||
import org.jetbrains.kotlin.platform.jvm.isJvm
|
||||
import org.jetbrains.kotlinx.serialization.compiler.backend.ir.*
|
||||
import org.jetbrains.kotlinx.serialization.compiler.backend.ir.SerializationJvmIrIntrinsicSupport
|
||||
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
|
||||
@@ -53,7 +51,6 @@ fun ClassLoweringPass.runOnFileInOrder(irFile: IrFile) {
|
||||
|
||||
class SerializationPluginContext(baseContext: IrPluginContext, val metadataPlugin: SerializationDescriptorSerializerPlugin?) :
|
||||
IrPluginContext by baseContext, SerializationBaseContext {
|
||||
lateinit var serialInfoImplJvmIrGenerator: SerialInfoImplJvmIrGenerator
|
||||
|
||||
internal val copiedStaticWriteSelf: MutableMap<IrSimpleFunction, IrSimpleFunction> = ConcurrentHashMap()
|
||||
|
||||
@@ -95,8 +92,9 @@ private class SerializerClassLowering(
|
||||
moduleFragment: IrModuleFragment
|
||||
) : IrElementTransformerVoid(), ClassLoweringPass {
|
||||
val context: SerializationPluginContext = SerializationPluginContext(baseContext, metadataPlugin)
|
||||
private val serialInfoJvmGenerator =
|
||||
SerialInfoImplJvmIrGenerator(context, moduleFragment).also { context.serialInfoImplJvmIrGenerator = it }
|
||||
|
||||
// Lazy to avoid creating generator in non-JVM backends
|
||||
private val serialInfoJvmGenerator by lazy(LazyThreadSafetyMode.NONE) { SerialInfoImplJvmIrGenerator(context, moduleFragment) }
|
||||
|
||||
override fun lower(irClass: IrClass) {
|
||||
irClass.runPluginSafe {
|
||||
@@ -104,9 +102,8 @@ private class SerializerClassLowering(
|
||||
SerializerIrGenerator.generate(irClass, context, context.metadataPlugin)
|
||||
SerializableCompanionIrGenerator.generate(irClass, context)
|
||||
|
||||
@OptIn(ObsoleteDescriptorBasedAPI::class)
|
||||
if (context.platform.isJvm() && KSerializerDescriptorResolver.isSerialInfoImpl(irClass.descriptor)) {
|
||||
serialInfoJvmGenerator.generate(irClass)
|
||||
if (context.platform.isJvm() && irClass.isSerialInfoAnnotation) {
|
||||
serialInfoJvmGenerator.generateImplementationFor(irClass)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+2
-7
@@ -24,14 +24,13 @@ 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)
|
||||
return listOf(SerialEntityNames.SERIALIZER_CLASS_NAME)
|
||||
}
|
||||
|
||||
override fun getSyntheticFunctionNames(thisDescriptor: ClassDescriptor): List<Name> = when {
|
||||
@@ -73,9 +72,7 @@ open class SerializationResolveExtension @JvmOverloads constructor(val metadataP
|
||||
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 &&
|
||||
if (thisDescriptor.shouldHaveGeneratedSerializer && name == SerialEntityNames.SERIALIZER_CLASS_NAME &&
|
||||
result.none { it.name == SerialEntityNames.SERIALIZER_CLASS_NAME }
|
||||
)
|
||||
result.add(KSerializerDescriptorResolver.addSerializerImplClass(thisDescriptor, declarationProvider, ctx))
|
||||
@@ -88,7 +85,6 @@ open class SerializationResolveExtension @JvmOverloads constructor(val metadataP
|
||||
else null
|
||||
|
||||
override fun addSyntheticSupertypes(thisDescriptor: ClassDescriptor, supertypes: MutableList<KotlinType>) {
|
||||
KSerializerDescriptorResolver.addSerialInfoSuperType(thisDescriptor, supertypes)
|
||||
KSerializerDescriptorResolver.addSerializerSupertypes(thisDescriptor, supertypes)
|
||||
KSerializerDescriptorResolver.addSerializerFactorySuperType(thisDescriptor, supertypes)
|
||||
}
|
||||
@@ -125,7 +121,6 @@ open class SerializationResolveExtension @JvmOverloads constructor(val metadataP
|
||||
fromSupertypes: ArrayList<PropertyDescriptor>,
|
||||
result: MutableSet<PropertyDescriptor>
|
||||
) {
|
||||
KSerializerDescriptorResolver.generateDescriptorsForAnnotationImpl(thisDescriptor, fromSupertypes, result)
|
||||
KSerializerDescriptorResolver.generateSerializerProperties(thisDescriptor, fromSupertypes, name, result)
|
||||
}
|
||||
}
|
||||
|
||||
-53
@@ -47,12 +47,6 @@ object KSerializerDescriptorResolver {
|
||||
&& (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 =
|
||||
@@ -72,36 +66,6 @@ object KSerializerDescriptorResolver {
|
||||
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,
|
||||
@@ -628,23 +592,6 @@ object KSerializerDescriptorResolver {
|
||||
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,
|
||||
|
||||
@@ -0,0 +1,32 @@
|
||||
// WITH_STDLIB
|
||||
|
||||
import kotlinx.serialization.*
|
||||
import kotlinx.serialization.json.*
|
||||
import kotlinx.serialization.encoding.*
|
||||
import kotlinx.serialization.descriptors.*
|
||||
|
||||
@SerialInfo
|
||||
@Target(AnnotationTarget.CLASS, AnnotationTarget.PROPERTY)
|
||||
annotation class MyId(val id: Int, val type: String = "foo")
|
||||
|
||||
@Serializable
|
||||
@MyId(10)
|
||||
class Foo(@MyId(20) val i: Int)
|
||||
|
||||
fun box(): String {
|
||||
val desc = Foo.serializer().descriptor
|
||||
val classId = desc.annotations.filterIsInstance<MyId>().single()
|
||||
val propId = desc.getElementAnnotations(0).filterIsInstance<MyId>().single().id
|
||||
if (classId.id != 10) return "Incorrect class annotation: ${classId}"
|
||||
if (classId.type != "foo") return "Incorrect default argument: ${classId}"
|
||||
if (!classId::class.java.toString().contains("annotationImpl")) return "Backend doesn't use annotation instantiation: ${classId::class}"
|
||||
if (propId != 20) return "Incorrect propery annotation: $propId"
|
||||
val implClassJava = Class.forName("MyId\$Impl")
|
||||
if (implClassJava.toString() != "class MyId\$Impl") return "Old annotation implementations are not preserved for compatibility"
|
||||
val ctorStr = implClassJava.constructors.toList().toString()
|
||||
if (!ctorStr.contains("public MyId\$Impl(int,java.lang.String)")) return "Compatibility impl does not contain correct constructor: $ctorStr"
|
||||
val methodsStr = implClassJava.methods.toList().toString()
|
||||
if (!methodsStr.contains("public final int MyId\$Impl.id()")) return "Compatibility impl does not contain correct methods: $methodsStr"
|
||||
if (!methodsStr.contains("public final java.lang.String MyId\$Impl.type()")) return "Compatibility impl does not contain correct methods: $methodsStr"
|
||||
return "OK"
|
||||
}
|
||||
+6
@@ -105,6 +105,12 @@ public class SerializationFirBlackBoxTestGenerated extends AbstractSerialization
|
||||
runTest("plugins/kotlinx-serialization/testData/boxIr/sealedInterfaces.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("serialInfo.kt")
|
||||
public void testSerialInfo() throws Exception {
|
||||
runTest("plugins/kotlinx-serialization/testData/boxIr/serialInfo.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("serializableOnPropertyType.kt")
|
||||
public void testSerializableOnPropertyType() throws Exception {
|
||||
|
||||
+6
@@ -103,6 +103,12 @@ public class SerializationIrBoxTestGenerated extends AbstractSerializationIrBoxT
|
||||
runTest("plugins/kotlinx-serialization/testData/boxIr/sealedInterfaces.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("serialInfo.kt")
|
||||
public void testSerialInfo() throws Exception {
|
||||
runTest("plugins/kotlinx-serialization/testData/boxIr/serialInfo.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("serializableOnPropertyType.kt")
|
||||
public void testSerializableOnPropertyType() throws Exception {
|
||||
|
||||
Reference in New Issue
Block a user