From 2eac2ff3a245ed9d09f61c60cba764fc1b59720e Mon Sep 17 00:00:00 2001 From: Leonid Startsev Date: Wed, 15 Dec 2021 16:05:29 +0000 Subject: [PATCH] Support Array in instantiated annotations #KT-47703 Fixed --- .../AnnotationImplementationTransformer.kt | 2 - .../JvmAnnotationImplementationTransformer.kt | 108 ++++++++++++++---- .../instances/annotationInstances.kt | 2 +- .../annotationInstancesEmptyDefault.kt | 2 + .../instances/annotationToString.kt | 2 +- 5 files changed, 87 insertions(+), 29 deletions(-) diff --git a/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/lower/AnnotationImplementationTransformer.kt b/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/lower/AnnotationImplementationTransformer.kt index 4a551431929..1c9f8400526 100644 --- a/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/lower/AnnotationImplementationTransformer.kt +++ b/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/lower/AnnotationImplementationTransformer.kt @@ -147,8 +147,6 @@ abstract class AnnotationImplementationTransformer(val context: BackendContext, .mapNotNull { it.correspondingPropertySymbol?.owner } } - open fun IrBuilderWithScope.kClassExprToJClassIfNeeded(irExpression: IrExpression): IrExpression = irExpression - abstract fun getArrayContentEqualsSymbol(type: IrType): IrFunctionSymbol fun generatedEquals(irBuilder: IrBlockBodyBuilder, type: IrType, arg1: IrExpression, arg2: IrExpression): IrExpression = diff --git a/compiler/ir/backend.jvm/lower/src/org/jetbrains/kotlin/backend/jvm/lower/JvmAnnotationImplementationTransformer.kt b/compiler/ir/backend.jvm/lower/src/org/jetbrains/kotlin/backend/jvm/lower/JvmAnnotationImplementationTransformer.kt index 194c927ec51..3c7371e6927 100644 --- a/compiler/ir/backend.jvm/lower/src/org/jetbrains/kotlin/backend/jvm/lower/JvmAnnotationImplementationTransformer.kt +++ b/compiler/ir/backend.jvm/lower/src/org/jetbrains/kotlin/backend/jvm/lower/JvmAnnotationImplementationTransformer.kt @@ -18,17 +18,13 @@ import org.jetbrains.kotlin.descriptors.Modality import org.jetbrains.kotlin.ir.builders.* import org.jetbrains.kotlin.ir.builders.declarations.* import org.jetbrains.kotlin.ir.declarations.* -import org.jetbrains.kotlin.ir.expressions.IrConstructorCall -import org.jetbrains.kotlin.ir.expressions.IrExpression -import org.jetbrains.kotlin.ir.expressions.IrExpressionBody -import org.jetbrains.kotlin.ir.expressions.IrGetValue +import org.jetbrains.kotlin.ir.expressions.* import org.jetbrains.kotlin.ir.expressions.impl.IrDelegatingConstructorCallImpl -import org.jetbrains.kotlin.ir.expressions.impl.IrGetValueImpl -import org.jetbrains.kotlin.ir.expressions.impl.IrSetFieldImpl import org.jetbrains.kotlin.ir.symbols.IrClassSymbol import org.jetbrains.kotlin.ir.symbols.IrFunctionSymbol import org.jetbrains.kotlin.ir.types.* import org.jetbrains.kotlin.ir.util.* +import org.jetbrains.kotlin.util.OperatorNameConventions import org.jetbrains.kotlin.utils.addToStdlib.safeAs internal val annotationImplementationPhase = makeIrFilePhase( @@ -64,16 +60,75 @@ class JvmAnnotationImplementationTransformer(val jvmContext: JvmBackendContext, private fun IrType.isKClassArray() = this is IrSimpleType && isArray() && arguments.single().typeOrNull?.isKClass() == true - override fun IrBuilderWithScope.kClassExprToJClassIfNeeded(irExpression: IrExpression): IrExpression { - with(this) { - return irGet( - jvmContext.ir.symbols.javaLangClass.starProjectedType, - null, - jvmContext.ir.symbols.kClassJava.owner.getter!!.symbol - ).apply { - extensionReceiver = irExpression - } + private fun IrBuilderWithScope.kClassToJClass(irExpression: IrExpression): IrExpression = + irGet( + jvmContext.ir.symbols.javaLangClass.starProjectedType, + null, + jvmContext.ir.symbols.kClassJava.owner.getter!!.symbol + ).apply { + extensionReceiver = irExpression } + + /** + * Copies array by one element, roughly as following: + * val size = kClassArray.size + * val result = arrayOfNulls(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 { 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 } override fun getArrayContentEqualsSymbol(type: IrType): IrFunctionSymbol { @@ -111,6 +166,7 @@ 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( @@ -124,12 +180,13 @@ class JvmAnnotationImplementationTransformer(val jvmContext: JvmBackendContext, 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 = propType + type = storedFieldType origin = ANNOTATION_IMPLEMENTATION isFinal = true visibility = DescriptorVisibilities.PRIVATE @@ -147,12 +204,15 @@ class JvmAnnotationImplementationTransformer(val jvmContext: JvmBackendContext, } else null parameter.defaultValue = newDefaultValue?.deepCopyWithVariables()?.also { it.transformChildrenVoid() } - ctorBody.statements += IrSetFieldImpl( - SYNTHETIC_OFFSET, SYNTHETIC_OFFSET, field.symbol, - IrGetValueImpl(SYNTHETIC_OFFSET, SYNTHETIC_OFFSET, implClass.thisReceiver!!.symbol), - IrGetValueImpl(SYNTHETIC_OFFSET, SYNTHETIC_OFFSET, parameter.symbol), - context.irBuiltIns.unitType, - ) + 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 @@ -179,9 +239,7 @@ class JvmAnnotationImplementationTransformer(val jvmContext: JvmBackendContext, correspondingPropertySymbol = prop.symbol dispatchReceiverParameter = implClass.thisReceiver!!.copyTo(this) body = context.createIrBuilder(symbol, SYNTHETIC_OFFSET, SYNTHETIC_OFFSET).irBlockBody { - var value: IrExpression = irGetField(irGet(dispatchReceiverParameter!!), field) - if (propType.isKClass()) value = this.kClassExprToJClassIfNeeded(value) - +irReturn(value) + +irReturn(irGetField(irGet(dispatchReceiverParameter!!), field)) } overriddenSymbols = listOf(property.getter!!.symbol) } diff --git a/compiler/testData/codegen/box/annotations/instances/annotationInstances.kt b/compiler/testData/codegen/box/annotations/instances/annotationInstances.kt index 4be8db8907f..356fa4972c7 100644 --- a/compiler/testData/codegen/box/annotations/instances/annotationInstances.kt +++ b/compiler/testData/codegen/box/annotations/instances/annotationInstances.kt @@ -60,7 +60,7 @@ fun box(): String { assert(anno.intArr.contentEquals(intArrayOf(1, 2))) assert(anno.arrOfE.contentEquals(arrayOf(E.E0))) assert(anno.arrOfA.size == 1) -// assert(anno.arrOfK.size == 2) TODO(KT-47703): Array to Array conversion + assert(anno.arrOfK.size == 2) val ann = anno.a assertEquals(ann.b, 1.toByte()) assertEquals(ann.s, 1.toShort()) diff --git a/compiler/testData/codegen/box/annotations/instances/annotationInstancesEmptyDefault.kt b/compiler/testData/codegen/box/annotations/instances/annotationInstancesEmptyDefault.kt index 554182c1d0a..d83ab670760 100644 --- a/compiler/testData/codegen/box/annotations/instances/annotationInstancesEmptyDefault.kt +++ b/compiler/testData/codegen/box/annotations/instances/annotationInstancesEmptyDefault.kt @@ -21,6 +21,7 @@ annotation class C( val i: Int = 42, val b: B = B(), val kClass: KClass<*> = B::class, + val kClassArray: Array> = [E::class, A::class], val e: E = E.B, val aS: Array = arrayOf("a", "b"), val aI: IntArray = intArrayOf(1, 2) @@ -37,6 +38,7 @@ fun box(): String { assertEquals(42, c.i) assertEquals(A(), c.b.a) assertEquals(B::class, c.kClass) + assertEquals(2, c.kClassArray.size) assertEquals(E.B, c.e) assert(arrayOf("a", "b").contentEquals(c.aS)) assert(intArrayOf(1, 2).contentEquals(c.aI)) diff --git a/compiler/testData/codegen/box/annotations/instances/annotationToString.kt b/compiler/testData/codegen/box/annotations/instances/annotationToString.kt index a9330aefb87..f1002fac613 100644 --- a/compiler/testData/codegen/box/annotations/instances/annotationToString.kt +++ b/compiler/testData/codegen/box/annotations/instances/annotationToString.kt @@ -45,7 +45,7 @@ fun box(): String { ) val s = anno.toString() val targetJVM = "@test.Anno(s=OK, i=42, f=2.718281828, u=43, e=E0, a=@test.A(b=1, s=1, i=1, f=1.0, d=1.0, l=1, c=c, bool=true), " + - "k=interface test.A (Kotlin reflection is not available), arr=[], intArr=[1, 2], arrOfE=[E0], arrOfA=[@test.Empty()])" + "k=interface test.A, arr=[], intArr=[1, 2], arrOfE=[E0], arrOfA=[@test.Empty()])" val targetJS = "@test.Anno(s=OK, i=42, f=2.718281828, u=43, e=E0, a=@test.A(b=1, s=1, i=1, f=1, d=1, l=1, c=c, bool=true), k=class A, arr=[...], intArr=[...], arrOfE=[...], arrOfA=[...])" val targetNative = targetJVM .replace(" (Kotlin reflection is not available)", "")