diff --git a/compiler/fir/fir2ir/tests-gen/org/jetbrains/kotlin/test/runners/codegen/FirBlackBoxCodegenTestGenerated.java b/compiler/fir/fir2ir/tests-gen/org/jetbrains/kotlin/test/runners/codegen/FirBlackBoxCodegenTestGenerated.java index e45ce7c579c..fe501f75592 100644 --- a/compiler/fir/fir2ir/tests-gen/org/jetbrains/kotlin/test/runners/codegen/FirBlackBoxCodegenTestGenerated.java +++ b/compiler/fir/fir2ir/tests-gen/org/jetbrains/kotlin/test/runners/codegen/FirBlackBoxCodegenTestGenerated.java @@ -523,6 +523,12 @@ public class FirBlackBoxCodegenTestGenerated extends AbstractFirBlackBoxCodegenT runTest("compiler/testData/codegen/box/annotations/repeatable/kotlinAnnotation.kt"); } + @Test + @TestMetadata("kotlinAnnotationInJava.kt") + public void testKotlinAnnotationInJava() throws Exception { + runTest("compiler/testData/codegen/box/annotations/repeatable/kotlinAnnotationInJava.kt"); + } + @Test @TestMetadata("kotlinAnnotationWithBothRepeatables.kt") public void testKotlinAnnotationWithBothRepeatables() throws Exception { diff --git a/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/lower/AdditionalClassAnnotationLowering.kt b/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/lower/AdditionalClassAnnotationLowering.kt index 5d7e459e76c..838a9247ce2 100644 --- a/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/lower/AdditionalClassAnnotationLowering.kt +++ b/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/lower/AdditionalClassAnnotationLowering.kt @@ -17,14 +17,13 @@ import org.jetbrains.kotlin.ir.expressions.IrConstructorCall import org.jetbrains.kotlin.ir.expressions.IrExpression import org.jetbrains.kotlin.ir.expressions.IrGetEnumValue import org.jetbrains.kotlin.ir.expressions.IrVararg +import org.jetbrains.kotlin.ir.expressions.impl.IrClassReferenceImpl import org.jetbrains.kotlin.ir.expressions.impl.IrConstructorCallImpl import org.jetbrains.kotlin.ir.expressions.impl.IrGetEnumValueImpl import org.jetbrains.kotlin.ir.expressions.impl.IrVarargImpl import org.jetbrains.kotlin.ir.types.typeWith -import org.jetbrains.kotlin.ir.util.defaultType -import org.jetbrains.kotlin.ir.util.getAnnotation -import org.jetbrains.kotlin.ir.util.hasAnnotation -import org.jetbrains.kotlin.ir.util.isAnnotationClass +import org.jetbrains.kotlin.ir.util.* +import org.jetbrains.kotlin.load.java.JvmAbi import org.jetbrains.kotlin.load.java.JvmAnnotationNames import org.jetbrains.kotlin.name.Name import java.lang.annotation.ElementType @@ -32,7 +31,8 @@ import java.lang.annotation.ElementType internal val additionalClassAnnotationPhase = makeIrFilePhase( ::AdditionalClassAnnotationLowering, name = "AdditionalClassAnnotation", - description = "Add Documented, Retention and Target annotations to annotation classes" + description = "Add Documented, Retention, Target, Repeatable annotations to annotation classes", + prerequisite = setOf(repeatedAnnotationPhase) ) private class AdditionalClassAnnotationLowering(private val context: JvmBackendContext) : ClassLoweringPass { @@ -44,6 +44,7 @@ private class AdditionalClassAnnotationLowering(private val context: JvmBackendC generateDocumentedAnnotation(irClass) generateRetentionAnnotation(irClass) generateTargetAnnotation(irClass) + generateRepeatableAnnotation(irClass) } private fun generateDocumentedAnnotation(irClass: IrClass) { @@ -105,6 +106,27 @@ private class AdditionalClassAnnotationLowering(private val context: JvmBackendC } } + private fun generateRepeatableAnnotation(irClass: IrClass) { + if (!irClass.hasAnnotation(StandardNames.FqNames.repeatable) || + irClass.hasAnnotation(JvmAnnotationNames.REPEATABLE_ANNOTATION) + ) return + + val containerClass = + irClass.declarations.singleOrNull { + it is IrClass && it.name.asString() == JvmAbi.REPEATABLE_ANNOTATION_CONTAINER_NAME + } as IrClass? ?: error("Repeatable annotation class should have a container generated: ${irClass.render()}") + val containerReference = IrClassReferenceImpl( + UNDEFINED_OFFSET, UNDEFINED_OFFSET, context.irBuiltIns.kClassClass.typeWith(containerClass.defaultType), + containerClass.symbol, containerClass.defaultType + ) + irClass.annotations += + IrConstructorCallImpl.fromSymbolOwner( + UNDEFINED_OFFSET, UNDEFINED_OFFSET, symbols.repeatableConstructor.returnType, symbols.repeatableConstructor.symbol, 0 + ).apply { + putValueArgument(0, containerReference) + } + } + private fun IrConstructorCall.getValueArgument(name: Name): IrExpression? { val index = symbol.owner.valueParameters.find { it.name == name }?.index ?: return null return getValueArgument(index) diff --git a/compiler/testData/codegen/box/annotations/repeatable/kotlinAnnotationInJava.kt b/compiler/testData/codegen/box/annotations/repeatable/kotlinAnnotationInJava.kt new file mode 100644 index 00000000000..352035068e3 --- /dev/null +++ b/compiler/testData/codegen/box/annotations/repeatable/kotlinAnnotationInJava.kt @@ -0,0 +1,43 @@ +// !LANGUAGE: +RepeatableAnnotations +// TARGET_BACKEND: JVM_IR +// JVM_TARGET: 1.8 +// WITH_RUNTIME +// FULL_JDK + +// java.lang.NoSuchMethodError: java.lang.Class.getAnnotationsByType +// IGNORE_BACKEND: ANDROID + +// FILE: box.kt + +@Repeatable +annotation class A(val value: String) + +fun box(): String { + val annotations = Z::class.java.annotations + val aa = annotations.singleOrNull() ?: return "Fail 1: $annotations" + + val a = ContainerSupport.load(aa) + if (a.size != 3) return "Fail 2: $a" + + val bytype = Z::class.java.getAnnotationsByType(A::class.java) + if (a.toList() != bytype.toList()) return "Fail 3: ${a.toList()} != ${bytype.toList()}" + + return a.fold("") { acc, it -> acc + it.value } +} + +// FILE: Z.java + +@A("O") +@A("") +@A("K") +public class Z {} + +// FILE: ContainerSupport.java + +import java.lang.annotation.Annotation; + +public class ContainerSupport { + public static A[] load(Annotation container) { + return ((A.Container) container).value(); + } +} diff --git a/compiler/testData/codegen/bytecodeListing/annotations/repeatable/kotlinAnnotation.txt b/compiler/testData/codegen/bytecodeListing/annotations/repeatable/kotlinAnnotation.txt index 78f54453983..aca990eba40 100644 --- a/compiler/testData/codegen/bytecodeListing/annotations/repeatable/kotlinAnnotation.txt +++ b/compiler/testData/codegen/bytecodeListing/annotations/repeatable/kotlinAnnotation.txt @@ -8,6 +8,7 @@ public annotation class test/A$Container { @kotlin.annotation.Repeatable @java.lang.annotation.Retention(value=RUNTIME) +@java.lang.annotation.Repeatable(value=test.A$Container::class) @kotlin.Metadata public annotation class test/A { // source: 'kotlinAnnotation.kt' diff --git a/compiler/testData/codegen/bytecodeListing/annotations/repeatable/kotlinSpecificTargets.txt b/compiler/testData/codegen/bytecodeListing/annotations/repeatable/kotlinSpecificTargets.txt index 437b0e9636d..3bb1befc085 100644 --- a/compiler/testData/codegen/bytecodeListing/annotations/repeatable/kotlinSpecificTargets.txt +++ b/compiler/testData/codegen/bytecodeListing/annotations/repeatable/kotlinSpecificTargets.txt @@ -12,6 +12,7 @@ public annotation class test/A$Container { @kotlin.annotation.Target(allowedTargets=[FILE, TYPEALIAS]) @java.lang.annotation.Retention(value=RUNTIME) @java.lang.annotation.Target(value=[]) +@java.lang.annotation.Repeatable(value=test.A$Container::class) @kotlin.Metadata public annotation class test/A { // source: 'kotlinSpecificTargets.kt' diff --git a/compiler/testData/codegen/bytecodeListing/annotations/repeatable/multipleRepeatableOrder.txt b/compiler/testData/codegen/bytecodeListing/annotations/repeatable/multipleRepeatableOrder.txt index 8011dc56334..fedc16c240f 100644 --- a/compiler/testData/codegen/bytecodeListing/annotations/repeatable/multipleRepeatableOrder.txt +++ b/compiler/testData/codegen/bytecodeListing/annotations/repeatable/multipleRepeatableOrder.txt @@ -8,6 +8,7 @@ public annotation class test/A$Container { @kotlin.annotation.Repeatable @java.lang.annotation.Retention(value=RUNTIME) +@java.lang.annotation.Repeatable(value=test.A$Container::class) @kotlin.Metadata public annotation class test/A { // source: 'multipleRepeatableOrder.kt' @@ -25,6 +26,7 @@ public annotation class test/B$Container { @kotlin.annotation.Repeatable @java.lang.annotation.Retention(value=RUNTIME) +@java.lang.annotation.Repeatable(value=test.B$Container::class) @kotlin.Metadata public annotation class test/B { // source: 'multipleRepeatableOrder.kt' @@ -42,6 +44,7 @@ public annotation class test/C$Container { @kotlin.annotation.Repeatable @java.lang.annotation.Retention(value=RUNTIME) +@java.lang.annotation.Repeatable(value=test.C$Container::class) @kotlin.Metadata public annotation class test/C { // source: 'multipleRepeatableOrder.kt' diff --git a/compiler/testData/codegen/bytecodeListing/annotations/repeatable/retentionAndTarget.txt b/compiler/testData/codegen/bytecodeListing/annotations/repeatable/retentionAndTarget.txt index a5908a27f25..e39c4014fff 100644 --- a/compiler/testData/codegen/bytecodeListing/annotations/repeatable/retentionAndTarget.txt +++ b/compiler/testData/codegen/bytecodeListing/annotations/repeatable/retentionAndTarget.txt @@ -10,6 +10,7 @@ public annotation class RetentionBinary$Container { @kotlin.annotation.Repeatable @kotlin.annotation.Retention(value=BINARY) @java.lang.annotation.Retention(value=CLASS) +@java.lang.annotation.Repeatable(value=RetentionBinary$Container::class) @kotlin.Metadata public annotation class RetentionBinary { // source: 'retentionAndTarget.kt' @@ -26,6 +27,7 @@ public annotation class RetentionRuntime$Container { @kotlin.annotation.Repeatable @java.lang.annotation.Retention(value=RUNTIME) +@java.lang.annotation.Repeatable(value=RetentionRuntime$Container::class) @kotlin.Metadata public annotation class RetentionRuntime { // source: 'retentionAndTarget.kt' @@ -44,6 +46,7 @@ public annotation class RetentionSource$Container { @kotlin.annotation.Repeatable @kotlin.annotation.Retention(value=SOURCE) @java.lang.annotation.Retention(value=SOURCE) +@java.lang.annotation.Repeatable(value=RetentionSource$Container::class) @kotlin.Metadata public annotation class RetentionSource { // source: 'retentionAndTarget.kt' @@ -64,6 +67,7 @@ public annotation class TargetAnnotationClassAndTypeOnly$Container { @kotlin.annotation.Target(allowedTargets=[ANNOTATION_CLASS, TYPE]) @java.lang.annotation.Retention(value=RUNTIME) @java.lang.annotation.Target(value=[ANNOTATION_TYPE, TYPE_USE]) +@java.lang.annotation.Repeatable(value=TargetAnnotationClassAndTypeOnly$Container::class) @kotlin.Metadata public annotation class TargetAnnotationClassAndTypeOnly { // source: 'retentionAndTarget.kt' @@ -84,6 +88,7 @@ public annotation class TargetClassOnly$Container { @kotlin.annotation.Target(allowedTargets=[CLASS]) @java.lang.annotation.Retention(value=RUNTIME) @java.lang.annotation.Target(value=[TYPE]) +@java.lang.annotation.Repeatable(value=TargetClassOnly$Container::class) @kotlin.Metadata public annotation class TargetClassOnly { // source: 'retentionAndTarget.kt' @@ -104,6 +109,7 @@ public annotation class TargetEmpty$Container { @kotlin.annotation.Target(allowedTargets=[]) @java.lang.annotation.Retention(value=RUNTIME) @java.lang.annotation.Target(value=[]) +@java.lang.annotation.Repeatable(value=TargetEmpty$Container::class) @kotlin.Metadata public annotation class TargetEmpty { // source: 'retentionAndTarget.kt' diff --git a/compiler/tests-common-new/tests-gen/org/jetbrains/kotlin/test/runners/codegen/IrBlackBoxCodegenTestGenerated.java b/compiler/tests-common-new/tests-gen/org/jetbrains/kotlin/test/runners/codegen/IrBlackBoxCodegenTestGenerated.java index 69b5c307ba0..4a11e0d2369 100644 --- a/compiler/tests-common-new/tests-gen/org/jetbrains/kotlin/test/runners/codegen/IrBlackBoxCodegenTestGenerated.java +++ b/compiler/tests-common-new/tests-gen/org/jetbrains/kotlin/test/runners/codegen/IrBlackBoxCodegenTestGenerated.java @@ -523,6 +523,12 @@ public class IrBlackBoxCodegenTestGenerated extends AbstractIrBlackBoxCodegenTes runTest("compiler/testData/codegen/box/annotations/repeatable/kotlinAnnotation.kt"); } + @Test + @TestMetadata("kotlinAnnotationInJava.kt") + public void testKotlinAnnotationInJava() throws Exception { + runTest("compiler/testData/codegen/box/annotations/repeatable/kotlinAnnotationInJava.kt"); + } + @Test @TestMetadata("kotlinAnnotationWithBothRepeatables.kt") public void testKotlinAnnotationWithBothRepeatables() throws Exception {