Generate Java @Repeatable on Kotlin-repeatable annotation classes
#KT-12794
This commit is contained in:
+6
@@ -523,6 +523,12 @@ public class FirBlackBoxCodegenTestGenerated extends AbstractFirBlackBoxCodegenT
|
|||||||
runTest("compiler/testData/codegen/box/annotations/repeatable/kotlinAnnotation.kt");
|
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
|
@Test
|
||||||
@TestMetadata("kotlinAnnotationWithBothRepeatables.kt")
|
@TestMetadata("kotlinAnnotationWithBothRepeatables.kt")
|
||||||
public void testKotlinAnnotationWithBothRepeatables() throws Exception {
|
public void testKotlinAnnotationWithBothRepeatables() throws Exception {
|
||||||
|
|||||||
+27
-5
@@ -17,14 +17,13 @@ import org.jetbrains.kotlin.ir.expressions.IrConstructorCall
|
|||||||
import org.jetbrains.kotlin.ir.expressions.IrExpression
|
import org.jetbrains.kotlin.ir.expressions.IrExpression
|
||||||
import org.jetbrains.kotlin.ir.expressions.IrGetEnumValue
|
import org.jetbrains.kotlin.ir.expressions.IrGetEnumValue
|
||||||
import org.jetbrains.kotlin.ir.expressions.IrVararg
|
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.IrConstructorCallImpl
|
||||||
import org.jetbrains.kotlin.ir.expressions.impl.IrGetEnumValueImpl
|
import org.jetbrains.kotlin.ir.expressions.impl.IrGetEnumValueImpl
|
||||||
import org.jetbrains.kotlin.ir.expressions.impl.IrVarargImpl
|
import org.jetbrains.kotlin.ir.expressions.impl.IrVarargImpl
|
||||||
import org.jetbrains.kotlin.ir.types.typeWith
|
import org.jetbrains.kotlin.ir.types.typeWith
|
||||||
import org.jetbrains.kotlin.ir.util.defaultType
|
import org.jetbrains.kotlin.ir.util.*
|
||||||
import org.jetbrains.kotlin.ir.util.getAnnotation
|
import org.jetbrains.kotlin.load.java.JvmAbi
|
||||||
import org.jetbrains.kotlin.ir.util.hasAnnotation
|
|
||||||
import org.jetbrains.kotlin.ir.util.isAnnotationClass
|
|
||||||
import org.jetbrains.kotlin.load.java.JvmAnnotationNames
|
import org.jetbrains.kotlin.load.java.JvmAnnotationNames
|
||||||
import org.jetbrains.kotlin.name.Name
|
import org.jetbrains.kotlin.name.Name
|
||||||
import java.lang.annotation.ElementType
|
import java.lang.annotation.ElementType
|
||||||
@@ -32,7 +31,8 @@ import java.lang.annotation.ElementType
|
|||||||
internal val additionalClassAnnotationPhase = makeIrFilePhase(
|
internal val additionalClassAnnotationPhase = makeIrFilePhase(
|
||||||
::AdditionalClassAnnotationLowering,
|
::AdditionalClassAnnotationLowering,
|
||||||
name = "AdditionalClassAnnotation",
|
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 {
|
private class AdditionalClassAnnotationLowering(private val context: JvmBackendContext) : ClassLoweringPass {
|
||||||
@@ -44,6 +44,7 @@ private class AdditionalClassAnnotationLowering(private val context: JvmBackendC
|
|||||||
generateDocumentedAnnotation(irClass)
|
generateDocumentedAnnotation(irClass)
|
||||||
generateRetentionAnnotation(irClass)
|
generateRetentionAnnotation(irClass)
|
||||||
generateTargetAnnotation(irClass)
|
generateTargetAnnotation(irClass)
|
||||||
|
generateRepeatableAnnotation(irClass)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun generateDocumentedAnnotation(irClass: 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? {
|
private fun IrConstructorCall.getValueArgument(name: Name): IrExpression? {
|
||||||
val index = symbol.owner.valueParameters.find { it.name == name }?.index ?: return null
|
val index = symbol.owner.valueParameters.find { it.name == name }?.index ?: return null
|
||||||
return getValueArgument(index)
|
return getValueArgument(index)
|
||||||
|
|||||||
+43
@@ -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();
|
||||||
|
}
|
||||||
|
}
|
||||||
+1
@@ -8,6 +8,7 @@ public annotation class test/A$Container {
|
|||||||
|
|
||||||
@kotlin.annotation.Repeatable
|
@kotlin.annotation.Repeatable
|
||||||
@java.lang.annotation.Retention(value=RUNTIME)
|
@java.lang.annotation.Retention(value=RUNTIME)
|
||||||
|
@java.lang.annotation.Repeatable(value=test.A$Container::class)
|
||||||
@kotlin.Metadata
|
@kotlin.Metadata
|
||||||
public annotation class test/A {
|
public annotation class test/A {
|
||||||
// source: 'kotlinAnnotation.kt'
|
// source: 'kotlinAnnotation.kt'
|
||||||
|
|||||||
+1
@@ -12,6 +12,7 @@ public annotation class test/A$Container {
|
|||||||
@kotlin.annotation.Target(allowedTargets=[FILE, TYPEALIAS])
|
@kotlin.annotation.Target(allowedTargets=[FILE, TYPEALIAS])
|
||||||
@java.lang.annotation.Retention(value=RUNTIME)
|
@java.lang.annotation.Retention(value=RUNTIME)
|
||||||
@java.lang.annotation.Target(value=[])
|
@java.lang.annotation.Target(value=[])
|
||||||
|
@java.lang.annotation.Repeatable(value=test.A$Container::class)
|
||||||
@kotlin.Metadata
|
@kotlin.Metadata
|
||||||
public annotation class test/A {
|
public annotation class test/A {
|
||||||
// source: 'kotlinSpecificTargets.kt'
|
// source: 'kotlinSpecificTargets.kt'
|
||||||
|
|||||||
Vendored
+3
@@ -8,6 +8,7 @@ public annotation class test/A$Container {
|
|||||||
|
|
||||||
@kotlin.annotation.Repeatable
|
@kotlin.annotation.Repeatable
|
||||||
@java.lang.annotation.Retention(value=RUNTIME)
|
@java.lang.annotation.Retention(value=RUNTIME)
|
||||||
|
@java.lang.annotation.Repeatable(value=test.A$Container::class)
|
||||||
@kotlin.Metadata
|
@kotlin.Metadata
|
||||||
public annotation class test/A {
|
public annotation class test/A {
|
||||||
// source: 'multipleRepeatableOrder.kt'
|
// source: 'multipleRepeatableOrder.kt'
|
||||||
@@ -25,6 +26,7 @@ public annotation class test/B$Container {
|
|||||||
|
|
||||||
@kotlin.annotation.Repeatable
|
@kotlin.annotation.Repeatable
|
||||||
@java.lang.annotation.Retention(value=RUNTIME)
|
@java.lang.annotation.Retention(value=RUNTIME)
|
||||||
|
@java.lang.annotation.Repeatable(value=test.B$Container::class)
|
||||||
@kotlin.Metadata
|
@kotlin.Metadata
|
||||||
public annotation class test/B {
|
public annotation class test/B {
|
||||||
// source: 'multipleRepeatableOrder.kt'
|
// source: 'multipleRepeatableOrder.kt'
|
||||||
@@ -42,6 +44,7 @@ public annotation class test/C$Container {
|
|||||||
|
|
||||||
@kotlin.annotation.Repeatable
|
@kotlin.annotation.Repeatable
|
||||||
@java.lang.annotation.Retention(value=RUNTIME)
|
@java.lang.annotation.Retention(value=RUNTIME)
|
||||||
|
@java.lang.annotation.Repeatable(value=test.C$Container::class)
|
||||||
@kotlin.Metadata
|
@kotlin.Metadata
|
||||||
public annotation class test/C {
|
public annotation class test/C {
|
||||||
// source: 'multipleRepeatableOrder.kt'
|
// source: 'multipleRepeatableOrder.kt'
|
||||||
|
|||||||
+6
@@ -10,6 +10,7 @@ public annotation class RetentionBinary$Container {
|
|||||||
@kotlin.annotation.Repeatable
|
@kotlin.annotation.Repeatable
|
||||||
@kotlin.annotation.Retention(value=BINARY)
|
@kotlin.annotation.Retention(value=BINARY)
|
||||||
@java.lang.annotation.Retention(value=CLASS)
|
@java.lang.annotation.Retention(value=CLASS)
|
||||||
|
@java.lang.annotation.Repeatable(value=RetentionBinary$Container::class)
|
||||||
@kotlin.Metadata
|
@kotlin.Metadata
|
||||||
public annotation class RetentionBinary {
|
public annotation class RetentionBinary {
|
||||||
// source: 'retentionAndTarget.kt'
|
// source: 'retentionAndTarget.kt'
|
||||||
@@ -26,6 +27,7 @@ public annotation class RetentionRuntime$Container {
|
|||||||
|
|
||||||
@kotlin.annotation.Repeatable
|
@kotlin.annotation.Repeatable
|
||||||
@java.lang.annotation.Retention(value=RUNTIME)
|
@java.lang.annotation.Retention(value=RUNTIME)
|
||||||
|
@java.lang.annotation.Repeatable(value=RetentionRuntime$Container::class)
|
||||||
@kotlin.Metadata
|
@kotlin.Metadata
|
||||||
public annotation class RetentionRuntime {
|
public annotation class RetentionRuntime {
|
||||||
// source: 'retentionAndTarget.kt'
|
// source: 'retentionAndTarget.kt'
|
||||||
@@ -44,6 +46,7 @@ public annotation class RetentionSource$Container {
|
|||||||
@kotlin.annotation.Repeatable
|
@kotlin.annotation.Repeatable
|
||||||
@kotlin.annotation.Retention(value=SOURCE)
|
@kotlin.annotation.Retention(value=SOURCE)
|
||||||
@java.lang.annotation.Retention(value=SOURCE)
|
@java.lang.annotation.Retention(value=SOURCE)
|
||||||
|
@java.lang.annotation.Repeatable(value=RetentionSource$Container::class)
|
||||||
@kotlin.Metadata
|
@kotlin.Metadata
|
||||||
public annotation class RetentionSource {
|
public annotation class RetentionSource {
|
||||||
// source: 'retentionAndTarget.kt'
|
// source: 'retentionAndTarget.kt'
|
||||||
@@ -64,6 +67,7 @@ public annotation class TargetAnnotationClassAndTypeOnly$Container {
|
|||||||
@kotlin.annotation.Target(allowedTargets=[ANNOTATION_CLASS, TYPE])
|
@kotlin.annotation.Target(allowedTargets=[ANNOTATION_CLASS, TYPE])
|
||||||
@java.lang.annotation.Retention(value=RUNTIME)
|
@java.lang.annotation.Retention(value=RUNTIME)
|
||||||
@java.lang.annotation.Target(value=[ANNOTATION_TYPE, TYPE_USE])
|
@java.lang.annotation.Target(value=[ANNOTATION_TYPE, TYPE_USE])
|
||||||
|
@java.lang.annotation.Repeatable(value=TargetAnnotationClassAndTypeOnly$Container::class)
|
||||||
@kotlin.Metadata
|
@kotlin.Metadata
|
||||||
public annotation class TargetAnnotationClassAndTypeOnly {
|
public annotation class TargetAnnotationClassAndTypeOnly {
|
||||||
// source: 'retentionAndTarget.kt'
|
// source: 'retentionAndTarget.kt'
|
||||||
@@ -84,6 +88,7 @@ public annotation class TargetClassOnly$Container {
|
|||||||
@kotlin.annotation.Target(allowedTargets=[CLASS])
|
@kotlin.annotation.Target(allowedTargets=[CLASS])
|
||||||
@java.lang.annotation.Retention(value=RUNTIME)
|
@java.lang.annotation.Retention(value=RUNTIME)
|
||||||
@java.lang.annotation.Target(value=[TYPE])
|
@java.lang.annotation.Target(value=[TYPE])
|
||||||
|
@java.lang.annotation.Repeatable(value=TargetClassOnly$Container::class)
|
||||||
@kotlin.Metadata
|
@kotlin.Metadata
|
||||||
public annotation class TargetClassOnly {
|
public annotation class TargetClassOnly {
|
||||||
// source: 'retentionAndTarget.kt'
|
// source: 'retentionAndTarget.kt'
|
||||||
@@ -104,6 +109,7 @@ public annotation class TargetEmpty$Container {
|
|||||||
@kotlin.annotation.Target(allowedTargets=[])
|
@kotlin.annotation.Target(allowedTargets=[])
|
||||||
@java.lang.annotation.Retention(value=RUNTIME)
|
@java.lang.annotation.Retention(value=RUNTIME)
|
||||||
@java.lang.annotation.Target(value=[])
|
@java.lang.annotation.Target(value=[])
|
||||||
|
@java.lang.annotation.Repeatable(value=TargetEmpty$Container::class)
|
||||||
@kotlin.Metadata
|
@kotlin.Metadata
|
||||||
public annotation class TargetEmpty {
|
public annotation class TargetEmpty {
|
||||||
// source: 'retentionAndTarget.kt'
|
// source: 'retentionAndTarget.kt'
|
||||||
|
|||||||
+6
@@ -523,6 +523,12 @@ public class IrBlackBoxCodegenTestGenerated extends AbstractIrBlackBoxCodegenTes
|
|||||||
runTest("compiler/testData/codegen/box/annotations/repeatable/kotlinAnnotation.kt");
|
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
|
@Test
|
||||||
@TestMetadata("kotlinAnnotationWithBothRepeatables.kt")
|
@TestMetadata("kotlinAnnotationWithBothRepeatables.kt")
|
||||||
public void testKotlinAnnotationWithBothRepeatables() throws Exception {
|
public void testKotlinAnnotationWithBothRepeatables() throws Exception {
|
||||||
|
|||||||
Reference in New Issue
Block a user