Support Array<KClass> in instantiated annotations
#KT-47703 Fixed
This commit is contained in:
-2
@@ -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 =
|
||||
|
||||
+83
-25
@@ -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<JvmBackendContext>(
|
||||
@@ -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<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
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
|
||||
@@ -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<KClass> to Array<j.l.Class> conversion
|
||||
assert(anno.arrOfK.size == 2)
|
||||
val ann = anno.a
|
||||
assertEquals(ann.b, 1.toByte())
|
||||
assertEquals(ann.s, 1.toShort())
|
||||
|
||||
+2
@@ -21,6 +21,7 @@ annotation class C(
|
||||
val i: Int = 42,
|
||||
val b: B = B(),
|
||||
val kClass: KClass<*> = B::class,
|
||||
val kClassArray: Array<KClass<*>> = [E::class, A::class],
|
||||
val e: E = E.B,
|
||||
val aS: Array<String> = 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))
|
||||
|
||||
@@ -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)", "")
|
||||
|
||||
Reference in New Issue
Block a user