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 416cecc4c97..e45ce7c579c 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 @@ -516,6 +516,18 @@ public class FirBlackBoxCodegenTestGenerated extends AbstractFirBlackBoxCodegenT public void testJvmRepeatableKotlinAnnotation() throws Exception { runTest("compiler/testData/codegen/box/annotations/repeatable/jvmRepeatableKotlinAnnotation.kt"); } + + @Test + @TestMetadata("kotlinAnnotation.kt") + public void testKotlinAnnotation() throws Exception { + runTest("compiler/testData/codegen/box/annotations/repeatable/kotlinAnnotation.kt"); + } + + @Test + @TestMetadata("kotlinAnnotationWithBothRepeatables.kt") + public void testKotlinAnnotationWithBothRepeatables() throws Exception { + runTest("compiler/testData/codegen/box/annotations/repeatable/kotlinAnnotationWithBothRepeatables.kt"); + } } @Nested diff --git a/compiler/ir/backend.common/src/org/jetbrains/kotlin/ir/builders/declarations/declarationBuilders.kt b/compiler/ir/backend.common/src/org/jetbrains/kotlin/ir/builders/declarations/declarationBuilders.kt index 2e42ba90d48..a51582cecf0 100644 --- a/compiler/ir/backend.common/src/org/jetbrains/kotlin/ir/builders/declarations/declarationBuilders.kt +++ b/compiler/ir/backend.common/src/org/jetbrains/kotlin/ir/builders/declarations/declarationBuilders.kt @@ -7,11 +7,16 @@ package org.jetbrains.kotlin.ir.builders.declarations import org.jetbrains.kotlin.backend.common.descriptors.synthesizedName import org.jetbrains.kotlin.backend.common.ir.copyTo -import org.jetbrains.kotlin.descriptors.* +import org.jetbrains.kotlin.descriptors.DescriptorVisibilities +import org.jetbrains.kotlin.descriptors.DescriptorVisibility +import org.jetbrains.kotlin.descriptors.Modality +import org.jetbrains.kotlin.ir.IrBuiltIns import org.jetbrains.kotlin.ir.UNDEFINED_OFFSET import org.jetbrains.kotlin.ir.declarations.* import org.jetbrains.kotlin.ir.declarations.impl.IrVariableImpl -import org.jetbrains.kotlin.ir.descriptors.* +import org.jetbrains.kotlin.ir.expressions.impl.IrGetFieldImpl +import org.jetbrains.kotlin.ir.expressions.impl.IrGetValueImpl +import org.jetbrains.kotlin.ir.expressions.impl.IrReturnImpl import org.jetbrains.kotlin.ir.symbols.impl.* import org.jetbrains.kotlin.ir.types.IrType import org.jetbrains.kotlin.ir.util.defaultType @@ -106,6 +111,35 @@ inline fun IrProperty.addGetter(builder: IrFunctionBuilder.() -> Unit = {}): IrS } } +fun IrProperty.addDefaultGetter(parentClass: IrClass, builtIns: IrBuiltIns) { + val field = backingField!! + addGetter { + origin = IrDeclarationOrigin.DEFAULT_PROPERTY_ACCESSOR + returnType = field.type + }.apply { + dispatchReceiverParameter = parentClass.thisReceiver!!.copyTo(this) + body = factory.createBlockBody( + UNDEFINED_OFFSET, UNDEFINED_OFFSET, listOf( + IrReturnImpl( + UNDEFINED_OFFSET, UNDEFINED_OFFSET, + builtIns.nothingType, + symbol, + IrGetFieldImpl( + UNDEFINED_OFFSET, UNDEFINED_OFFSET, + field.symbol, + field.type, + IrGetValueImpl( + UNDEFINED_OFFSET, UNDEFINED_OFFSET, + dispatchReceiverParameter!!.type, + dispatchReceiverParameter!!.symbol + ) + ) + ) + ) + ) + } +} + @PublishedApi internal fun IrFactory.buildFunction(builder: IrFunctionBuilder): IrSimpleFunction = with(builder) { createFunction( diff --git a/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/JvmCachedDeclarations.kt b/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/JvmCachedDeclarations.kt index 9589a5a8940..409c0339fb6 100644 --- a/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/JvmCachedDeclarations.kt +++ b/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/JvmCachedDeclarations.kt @@ -13,16 +13,17 @@ import org.jetbrains.kotlin.backend.jvm.ir.copyCorrespondingPropertyFrom import org.jetbrains.kotlin.backend.jvm.ir.createJvmIrBuilder import org.jetbrains.kotlin.backend.jvm.ir.replaceThisByStaticReference import org.jetbrains.kotlin.builtins.CompanionObjectMapping +import org.jetbrains.kotlin.builtins.StandardNames import org.jetbrains.kotlin.builtins.isMappedIntrinsicCompanionObjectClassId import org.jetbrains.kotlin.config.LanguageFeature import org.jetbrains.kotlin.config.LanguageVersionSettings +import org.jetbrains.kotlin.descriptors.ClassKind import org.jetbrains.kotlin.descriptors.DescriptorVisibilities import org.jetbrains.kotlin.descriptors.Modality import org.jetbrains.kotlin.ir.builders.* -import org.jetbrains.kotlin.ir.builders.declarations.buildClass -import org.jetbrains.kotlin.ir.builders.declarations.buildField -import org.jetbrains.kotlin.ir.builders.declarations.buildFun +import org.jetbrains.kotlin.ir.builders.declarations.* import org.jetbrains.kotlin.ir.declarations.* +import org.jetbrains.kotlin.ir.types.typeWith import org.jetbrains.kotlin.ir.util.* import org.jetbrains.kotlin.load.java.JavaDescriptorVisibilities import org.jetbrains.kotlin.load.java.JvmAbi @@ -43,6 +44,8 @@ class JvmCachedDeclarations( private val defaultImplsRedirections = ConcurrentHashMap() private val defaultImplsOriginalMethods = ConcurrentHashMap() + private val repeatedAnnotationSyntheticContainers = ConcurrentHashMap() + fun getFieldForEnumEntry(enumEntry: IrEnumEntry): IrField = singletonFieldDeclarations.getOrPut(enumEntry) { context.irFactory.buildField { @@ -272,6 +275,49 @@ class JvmCachedDeclarations( copyCorrespondingPropertyFrom(fakeOverride) } } + + fun getRepeatedAnnotationSyntheticContainer(annotationClass: IrClass): IrClass = + repeatedAnnotationSyntheticContainers.getOrPut(annotationClass) { + val containerClass = context.irFactory.buildClass { + kind = ClassKind.ANNOTATION_CLASS + name = Name.identifier(JvmAbi.REPEATABLE_ANNOTATION_CONTAINER_NAME) + }.apply { + createImplicitParameterDeclarationWithWrappedDescriptor() + parent = annotationClass + superTypes = listOf(context.irBuiltIns.annotationType) + } + + val propertyName = Name.identifier("value") + val propertyType = context.irBuiltIns.arrayClass.typeWith(annotationClass.typeWith()) + + containerClass.addConstructor { + isPrimary = true + }.apply { + addValueParameter(propertyName, propertyType) + } + + containerClass.addProperty { + name = propertyName + }.apply property@{ + backingField = context.irFactory.buildField { + name = propertyName + type = propertyType + }.apply { + parent = containerClass + correspondingPropertySymbol = this@property.symbol + } + addDefaultGetter(containerClass, context.irBuiltIns) + } + + containerClass.annotations = annotationClass.annotations + .filter { + it.isAnnotationWithEqualFqName(StandardNames.FqNames.retention) || + it.isAnnotationWithEqualFqName(StandardNames.FqNames.target) + } + .map { it.deepCopyWithSymbols(containerClass) } + + containerClass + } } /* 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 63e4d1ab5c3..5d7e459e76c 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 @@ -8,8 +8,8 @@ package org.jetbrains.kotlin.backend.jvm.lower import org.jetbrains.kotlin.backend.common.ClassLoweringPass import org.jetbrains.kotlin.backend.common.phaser.makeIrFilePhase import org.jetbrains.kotlin.backend.jvm.JvmBackendContext +import org.jetbrains.kotlin.backend.jvm.codegen.getAnnotationRetention import org.jetbrains.kotlin.builtins.StandardNames -import org.jetbrains.kotlin.descriptors.annotations.KotlinRetention import org.jetbrains.kotlin.descriptors.annotations.KotlinTarget import org.jetbrains.kotlin.ir.UNDEFINED_OFFSET import org.jetbrains.kotlin.ir.declarations.IrClass @@ -27,7 +27,6 @@ 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.name.Name -import org.jetbrains.kotlin.utils.addToStdlib.safeAs import java.lang.annotation.ElementType internal val additionalClassAnnotationPhase = makeIrFilePhase( @@ -60,10 +59,7 @@ private class AdditionalClassAnnotationLowering(private val context: JvmBackendC private fun generateRetentionAnnotation(irClass: IrClass) { if (irClass.hasAnnotation(JvmAnnotationNames.RETENTION_ANNOTATION)) return - val kotlinRetentionPolicyCall = irClass.getAnnotation(StandardNames.FqNames.retention) - val kotlinRetentionPolicyName = - kotlinRetentionPolicyCall?.getValueArgument(0)?.safeAs()?.symbol?.owner?.name?.asString() - val kotlinRetentionPolicy = kotlinRetentionPolicyName?.let { KotlinRetention.valueOf(it) } + val kotlinRetentionPolicy = irClass.getAnnotationRetention() val javaRetentionPolicy = kotlinRetentionPolicy?.let { symbols.annotationRetentionMap[it] } ?: symbols.rpRuntime irClass.annotations += diff --git a/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/lower/RepeatedAnnotationLowering.kt b/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/lower/RepeatedAnnotationLowering.kt index cacc2cf6a53..c3ea5e11b41 100644 --- a/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/lower/RepeatedAnnotationLowering.kt +++ b/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/lower/RepeatedAnnotationLowering.kt @@ -9,6 +9,8 @@ import org.jetbrains.kotlin.backend.common.FileLoweringPass import org.jetbrains.kotlin.backend.common.phaser.makeIrFilePhase import org.jetbrains.kotlin.backend.jvm.JvmBackendContext import org.jetbrains.kotlin.backend.jvm.codegen.AnnotationCodegen.Companion.annotationClass +import org.jetbrains.kotlin.builtins.StandardNames +import org.jetbrains.kotlin.descriptors.ClassKind import org.jetbrains.kotlin.ir.IrElement import org.jetbrains.kotlin.ir.UNDEFINED_OFFSET import org.jetbrains.kotlin.ir.declarations.IrClass @@ -21,6 +23,7 @@ import org.jetbrains.kotlin.ir.expressions.impl.IrVarargImpl import org.jetbrains.kotlin.ir.symbols.IrClassSymbol import org.jetbrains.kotlin.ir.types.typeWith import org.jetbrains.kotlin.ir.util.defaultType +import org.jetbrains.kotlin.ir.util.hasAnnotation import org.jetbrains.kotlin.ir.util.isAnnotation import org.jetbrains.kotlin.ir.util.primaryConstructor import org.jetbrains.kotlin.ir.visitors.IrElementVisitorVoid @@ -53,6 +56,16 @@ class RepeatedAnnotationLowering(private val context: JvmBackendContext) : FileL super.visitDeclaration(declaration) } + override fun visitClass(declaration: IrClass) { + if (declaration.kind == ClassKind.ANNOTATION_CLASS && + declaration.hasAnnotation(StandardNames.FqNames.repeatable) && + !declaration.hasAnnotation(JvmAnnotationNames.REPEATABLE_ANNOTATION) + ) { + declaration.declarations.add(context.cachedDeclarations.getRepeatedAnnotationSyntheticContainer(declaration)) + } + super.visitClass(declaration) + } + private fun transformAnnotations(annotations: List): List { if (annotations.size < 2) return annotations @@ -85,7 +98,7 @@ class RepeatedAnnotationLowering(private val context: JvmBackendContext) : FileL (containerClassReference.symbol as? IrClassSymbol)?.owner ?: error("Repeatable annotation container must be a class: $annotationClass") } else { - TODO("Kotlin repeatable annotations") + context.cachedDeclarations.getRepeatedAnnotationSyntheticContainer(annotationClass) } } diff --git a/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/lower/ScriptLowering.kt b/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/lower/ScriptLowering.kt index 683b62def1c..f5670010651 100644 --- a/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/lower/ScriptLowering.kt +++ b/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/lower/ScriptLowering.kt @@ -5,7 +5,6 @@ package org.jetbrains.kotlin.backend.jvm.lower -import org.jetbrains.kotlin.backend.common.ir.copyTo import org.jetbrains.kotlin.backend.common.ir.createImplicitParameterDeclarationWithWrappedDescriptor import org.jetbrains.kotlin.backend.common.lower.createIrBuilder import org.jetbrains.kotlin.backend.common.phaser.makeCustomPhase @@ -19,11 +18,14 @@ import org.jetbrains.kotlin.ir.UNDEFINED_OFFSET import org.jetbrains.kotlin.ir.builders.* import org.jetbrains.kotlin.ir.builders.declarations.* import org.jetbrains.kotlin.ir.declarations.* -import org.jetbrains.kotlin.ir.declarations.impl.* +import org.jetbrains.kotlin.ir.declarations.impl.IrAnonymousInitializerImpl +import org.jetbrains.kotlin.ir.declarations.impl.IrExternalPackageFragmentImpl import org.jetbrains.kotlin.ir.expressions.* import org.jetbrains.kotlin.ir.expressions.impl.* import org.jetbrains.kotlin.ir.interpreter.toIrConst -import org.jetbrains.kotlin.ir.symbols.* +import org.jetbrains.kotlin.ir.symbols.IrClassSymbol +import org.jetbrains.kotlin.ir.symbols.IrClassifierSymbol +import org.jetbrains.kotlin.ir.symbols.IrSimpleFunctionSymbol import org.jetbrains.kotlin.ir.symbols.impl.IrAnonymousInitializerSymbolImpl import org.jetbrains.kotlin.ir.types.* import org.jetbrains.kotlin.ir.util.* @@ -276,36 +278,10 @@ private class ScriptsToClassesLowering(val context: JvmBackendContext) { field.initializer = initializer } - property.addSimpleFieldGetter(from.type, this, field) + property.addDefaultGetter(this, context.irBuiltIns) } } } - - private fun IrProperty.addSimpleFieldGetter(type: IrType, irScriptClass: IrClass, field: IrField) = - addGetter { - returnType = type - }.apply { - dispatchReceiverParameter = irScriptClass.thisReceiver!!.copyTo(this) - body = IrBlockBodyImpl( - UNDEFINED_OFFSET, UNDEFINED_OFFSET, listOf( - IrReturnImpl( - UNDEFINED_OFFSET, UNDEFINED_OFFSET, - context.irBuiltIns.nothingType, - symbol, - IrGetFieldImpl( - UNDEFINED_OFFSET, UNDEFINED_OFFSET, - field.symbol, - type, - IrGetValueImpl( - UNDEFINED_OFFSET, UNDEFINED_OFFSET, - dispatchReceiverParameter!!.type, - dispatchReceiverParameter!!.symbol - ) - ) - ) - ) - ) - } } data class ScriptToClassTransformerContext( diff --git a/compiler/testData/codegen/box/annotations/repeatable/kotlinAnnotation.kt b/compiler/testData/codegen/box/annotations/repeatable/kotlinAnnotation.kt new file mode 100644 index 00000000000..c1f6e1fff17 --- /dev/null +++ b/compiler/testData/codegen/box/annotations/repeatable/kotlinAnnotation.kt @@ -0,0 +1,41 @@ +// !LANGUAGE: +RepeatableAnnotations +// TARGET_BACKEND: JVM_IR +// WITH_RUNTIME +// FULL_JDK +// JVM_TARGET: 1.8 + +// java.lang.NoSuchMethodError: java.lang.Class.getAnnotationsByType +// IGNORE_BACKEND: ANDROID + +// FILE: box.kt + +@Repeatable +annotation class A(val value: String) + +@A("O") +@A("") +@A("K") +class Z + +fun box(): String { + val annotations = Z::class.java.annotations.filter { it.annotationClass != Metadata::class } + 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: 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/box/annotations/repeatable/kotlinAnnotationWithBothRepeatables.kt b/compiler/testData/codegen/box/annotations/repeatable/kotlinAnnotationWithBothRepeatables.kt new file mode 100644 index 00000000000..32339868a53 --- /dev/null +++ b/compiler/testData/codegen/box/annotations/repeatable/kotlinAnnotationWithBothRepeatables.kt @@ -0,0 +1,33 @@ +// !LANGUAGE: +RepeatableAnnotations +// TARGET_BACKEND: JVM_IR +// WITH_RUNTIME +// FULL_JDK +// JVM_TARGET: 1.8 + +// java.lang.NoSuchMethodError: java.lang.Class.getAnnotationsByType +// IGNORE_BACKEND: ANDROID + +@Repeatable +@java.lang.annotation.Repeatable(As::class) +annotation class A(val value: String) + +annotation class As(val value: Array) + +@A("O") +@A("") +@A("K") +class Z + +fun box(): String { + val annotations = Z::class.java.annotations.filter { it.annotationClass != Metadata::class } + val aa = annotations.singleOrNull() ?: return "Fail 1: $annotations" + if (aa !is As) return "Fail 2: $aa" + + val a = aa.value.asList() + if (a.size != 3) return "Fail 3: $a" + + val bytype = Z::class.java.getAnnotationsByType(A::class.java) + if (a.toList() != bytype.toList()) return "Fail 4: ${a.toList()} != ${bytype.toList()}" + + return a.fold("") { acc, it -> acc + it.value } +} diff --git a/compiler/testData/codegen/bytecodeListing/annotations/repeatable/kotlinAnnotation.kt b/compiler/testData/codegen/bytecodeListing/annotations/repeatable/kotlinAnnotation.kt new file mode 100644 index 00000000000..1120309e5c8 --- /dev/null +++ b/compiler/testData/codegen/bytecodeListing/annotations/repeatable/kotlinAnnotation.kt @@ -0,0 +1,45 @@ +// !LANGUAGE: +RepeatableAnnotations +// TARGET_BACKEND: JVM_IR +// WITH_RUNTIME +// FULL_JDK +// JVM_TARGET: 1.8 + +package test + +@Repeatable +annotation class A(val value: String) + +@A("class1") @A("class2") +class Z @A("constructor1") @A("constructor2") constructor() { + @A("nestedClass1") @A("nestedClass2") + annotation class Nested + + @A("memberFunction1") @A("memberFunction2") + fun memberFunction() {} + + @A("memberProperty1") @A("memberProperty2") + var memberProperty: Int + @A("propertyGetter1") @A("propertyGetter2") + get() = 0 + @A("propertySetter1") @A("propertySetter2") + set(value) {} +} + +@A("topLevelFunction1") @A("topLevelFunction2") +fun topLevelFunction2( + @A("parameter1") @A("parameter2") + parameter: String +) {} + +@A("topLevelProperty1") @A("topLevelProperty2") +var String.z: Z + @A("propertyGetter1") @A("propertyGetter2") + get() = Z() + @A("propertySetter1") @A("propertySetter2") + set(value) {} + +@get:A("useSitePropertyGetter1") +@get:A("useSitePropertyGetter2") +@field:A("field1") +@field:A("field2") +val o: String = "" diff --git a/compiler/testData/codegen/bytecodeListing/annotations/repeatable/kotlinAnnotation.txt b/compiler/testData/codegen/bytecodeListing/annotations/repeatable/kotlinAnnotation.txt new file mode 100644 index 00000000000..78f54453983 --- /dev/null +++ b/compiler/testData/codegen/bytecodeListing/annotations/repeatable/kotlinAnnotation.txt @@ -0,0 +1,51 @@ +@java.lang.annotation.Retention(value=RUNTIME) +@kotlin.Metadata +public annotation class test/A$Container { + // source: 'kotlinAnnotation.kt' + public abstract method value(): test.A[] + public inner class test/A$Container +} + +@kotlin.annotation.Repeatable +@java.lang.annotation.Retention(value=RUNTIME) +@kotlin.Metadata +public annotation class test/A { + // source: 'kotlinAnnotation.kt' + public abstract method value(): java.lang.String + public inner class test/A$Container +} + +@kotlin.Metadata +public final class test/KotlinAnnotationKt { + // source: 'kotlinAnnotation.kt' + private final static @test.A$Container(value=[test.A(value="field1"), test.A(value="field2")]) @org.jetbrains.annotations.NotNull field o: java.lang.String + static method (): void + public final static @test.A$Container(value=[test.A(value="useSitePropertyGetter1"), test.A(value="useSitePropertyGetter2")]) @org.jetbrains.annotations.NotNull method getO(): java.lang.String + public synthetic deprecated static @test.A$Container(value=[test.A(value="topLevelProperty1"), test.A(value="topLevelProperty2")]) method getZ$annotations(p0: java.lang.String): void + public final static @test.A$Container(value=[test.A(value="propertyGetter1"), test.A(value="propertyGetter2")]) @org.jetbrains.annotations.NotNull method getZ(@org.jetbrains.annotations.NotNull p0: java.lang.String): test.Z + public final static @test.A$Container(value=[test.A(value="propertySetter1"), test.A(value="propertySetter2")]) method setZ(@org.jetbrains.annotations.NotNull p0: java.lang.String, @org.jetbrains.annotations.NotNull p1: test.Z): void + public final static @test.A$Container(value=[test.A(value="topLevelFunction1"), test.A(value="topLevelFunction2")]) method topLevelFunction2(@test.A$Container(value=[test.A(value="parameter1"), test.A(value="parameter2")]) @org.jetbrains.annotations.NotNull p0: java.lang.String): void + public inner class test/A$Container +} + +@test.A$Container(value=[test.A(value="nestedClass1"), test.A(value="nestedClass2")]) +@java.lang.annotation.Retention(value=RUNTIME) +@kotlin.Metadata +public annotation class test/Z$Nested { + // source: 'kotlinAnnotation.kt' + public inner class test/A$Container + public inner class test/Z$Nested +} + +@test.A$Container(value=[test.A(value="class1"), test.A(value="class2")]) +@kotlin.Metadata +public final class test/Z { + // source: 'kotlinAnnotation.kt' + public @test.A$Container(value=[test.A(value="constructor1"), test.A(value="constructor2")]) method (): void + public synthetic deprecated static @test.A$Container(value=[test.A(value="memberProperty1"), test.A(value="memberProperty2")]) method getMemberProperty$annotations(): void + public final @test.A$Container(value=[test.A(value="propertyGetter1"), test.A(value="propertyGetter2")]) method getMemberProperty(): int + public final @test.A$Container(value=[test.A(value="memberFunction1"), test.A(value="memberFunction2")]) method memberFunction(): void + public final @test.A$Container(value=[test.A(value="propertySetter1"), test.A(value="propertySetter2")]) method setMemberProperty(p0: int): void + public inner class test/A$Container + public inner class test/Z$Nested +} diff --git a/compiler/testData/codegen/bytecodeListing/annotations/repeatable/kotlinAnnotationWithBothRepeatables.kt b/compiler/testData/codegen/bytecodeListing/annotations/repeatable/kotlinAnnotationWithBothRepeatables.kt new file mode 100644 index 00000000000..5bc1806f9f5 --- /dev/null +++ b/compiler/testData/codegen/bytecodeListing/annotations/repeatable/kotlinAnnotationWithBothRepeatables.kt @@ -0,0 +1,17 @@ +// !LANGUAGE: +RepeatableAnnotations +// TARGET_BACKEND: JVM_IR +// WITH_RUNTIME +// FULL_JDK +// JVM_TARGET: 1.8 + +package test + +@Repeatable +@java.lang.annotation.Repeatable(As::class) +annotation class A(val value: String) + +annotation class As(val value: Array) + +@A("a1") +@A("a2") +class Z diff --git a/compiler/testData/codegen/bytecodeListing/annotations/repeatable/kotlinAnnotationWithBothRepeatables.txt b/compiler/testData/codegen/bytecodeListing/annotations/repeatable/kotlinAnnotationWithBothRepeatables.txt new file mode 100644 index 00000000000..838dde4d438 --- /dev/null +++ b/compiler/testData/codegen/bytecodeListing/annotations/repeatable/kotlinAnnotationWithBothRepeatables.txt @@ -0,0 +1,22 @@ +@kotlin.annotation.Repeatable +@java.lang.annotation.Repeatable(value=test.As::class) +@java.lang.annotation.Retention(value=RUNTIME) +@kotlin.Metadata +public annotation class test/A { + // source: 'kotlinAnnotationWithBothRepeatables.kt' + public abstract method value(): java.lang.String +} + +@java.lang.annotation.Retention(value=RUNTIME) +@kotlin.Metadata +public annotation class test/As { + // source: 'kotlinAnnotationWithBothRepeatables.kt' + public abstract method value(): test.A[] +} + +@test.As(value=[test.A(value="a1"), test.A(value="a2")]) +@kotlin.Metadata +public final class test/Z { + // source: 'kotlinAnnotationWithBothRepeatables.kt' + public method (): void +} diff --git a/compiler/testData/codegen/bytecodeListing/annotations/repeatable/kotlinSpecificTargets.kt b/compiler/testData/codegen/bytecodeListing/annotations/repeatable/kotlinSpecificTargets.kt new file mode 100644 index 00000000000..5b4abc66778 --- /dev/null +++ b/compiler/testData/codegen/bytecodeListing/annotations/repeatable/kotlinSpecificTargets.kt @@ -0,0 +1,17 @@ +// !LANGUAGE: +RepeatableAnnotations +// TARGET_BACKEND: JVM_IR +// WITH_RUNTIME +// FULL_JDK +// JVM_TARGET: 1.8 + +@file:A("file1") +@file:A("file2") +package test + +@Repeatable +@Target(AnnotationTarget.FILE, AnnotationTarget.TYPEALIAS) +annotation class A(val value: String) + +@A("typealias1") +@A("typealias2") +typealias TA = List diff --git a/compiler/testData/codegen/bytecodeListing/annotations/repeatable/kotlinSpecificTargets.txt b/compiler/testData/codegen/bytecodeListing/annotations/repeatable/kotlinSpecificTargets.txt new file mode 100644 index 00000000000..437b0e9636d --- /dev/null +++ b/compiler/testData/codegen/bytecodeListing/annotations/repeatable/kotlinSpecificTargets.txt @@ -0,0 +1,28 @@ +@kotlin.annotation.Target(allowedTargets=[FILE, TYPEALIAS]) +@java.lang.annotation.Retention(value=RUNTIME) +@java.lang.annotation.Target(value=[]) +@kotlin.Metadata +public annotation class test/A$Container { + // source: 'kotlinSpecificTargets.kt' + public abstract method value(): test.A[] + public inner class test/A$Container +} + +@kotlin.annotation.Repeatable +@kotlin.annotation.Target(allowedTargets=[FILE, TYPEALIAS]) +@java.lang.annotation.Retention(value=RUNTIME) +@java.lang.annotation.Target(value=[]) +@kotlin.Metadata +public annotation class test/A { + // source: 'kotlinSpecificTargets.kt' + public abstract method value(): java.lang.String + public inner class test/A$Container +} + +@test.A$Container(value=[test.A(value="file1"), test.A(value="file2")]) +@kotlin.Metadata +public final class test/KotlinSpecificTargetsKt { + // source: 'kotlinSpecificTargets.kt' + public synthetic deprecated static @test.A$Container(value=[test.A(value="typealias1"), test.A(value="typealias2")]) method TA$annotations(): void + public inner class test/A$Container +} diff --git a/compiler/testData/codegen/bytecodeListing/annotations/repeatable/multipleRepeatableOrder.kt b/compiler/testData/codegen/bytecodeListing/annotations/repeatable/multipleRepeatableOrder.kt new file mode 100644 index 00000000000..a849ea7b7c8 --- /dev/null +++ b/compiler/testData/codegen/bytecodeListing/annotations/repeatable/multipleRepeatableOrder.kt @@ -0,0 +1,26 @@ +// !LANGUAGE: +RepeatableAnnotations +// TARGET_BACKEND: JVM_IR +// WITH_RUNTIME +// FULL_JDK +// JVM_TARGET: 1.8 + +package test + +@Repeatable +annotation class A(val value: String) +@Repeatable +annotation class B(val value: String) +@Repeatable +annotation class C(val value: String) + +annotation class Z(val value: String) + +// Expected annotation order (as in Java): all @A, then all @B, then @Z, then all @C. +@A("a1") +@B("b1") +@A("a2") +@Z("z") +@C("c1") +@C("c2") +@B("b2") +class Test diff --git a/compiler/testData/codegen/bytecodeListing/annotations/repeatable/multipleRepeatableOrder.txt b/compiler/testData/codegen/bytecodeListing/annotations/repeatable/multipleRepeatableOrder.txt new file mode 100644 index 00000000000..8011dc56334 --- /dev/null +++ b/compiler/testData/codegen/bytecodeListing/annotations/repeatable/multipleRepeatableOrder.txt @@ -0,0 +1,70 @@ +@java.lang.annotation.Retention(value=RUNTIME) +@kotlin.Metadata +public annotation class test/A$Container { + // source: 'multipleRepeatableOrder.kt' + public abstract method value(): test.A[] + public inner class test/A$Container +} + +@kotlin.annotation.Repeatable +@java.lang.annotation.Retention(value=RUNTIME) +@kotlin.Metadata +public annotation class test/A { + // source: 'multipleRepeatableOrder.kt' + public abstract method value(): java.lang.String + public inner class test/A$Container +} + +@java.lang.annotation.Retention(value=RUNTIME) +@kotlin.Metadata +public annotation class test/B$Container { + // source: 'multipleRepeatableOrder.kt' + public abstract method value(): test.B[] + public inner class test/B$Container +} + +@kotlin.annotation.Repeatable +@java.lang.annotation.Retention(value=RUNTIME) +@kotlin.Metadata +public annotation class test/B { + // source: 'multipleRepeatableOrder.kt' + public abstract method value(): java.lang.String + public inner class test/B$Container +} + +@java.lang.annotation.Retention(value=RUNTIME) +@kotlin.Metadata +public annotation class test/C$Container { + // source: 'multipleRepeatableOrder.kt' + public abstract method value(): test.C[] + public inner class test/C$Container +} + +@kotlin.annotation.Repeatable +@java.lang.annotation.Retention(value=RUNTIME) +@kotlin.Metadata +public annotation class test/C { + // source: 'multipleRepeatableOrder.kt' + public abstract method value(): java.lang.String + public inner class test/C$Container +} + +@test.A$Container(value=[test.A(value="a1"), test.A(value="a2")]) +@test.B$Container(value=[test.B(value="b1"), test.B(value="b2")]) +@test.Z(value="z") +@test.C$Container(value=[test.C(value="c1"), test.C(value="c2")]) +@kotlin.Metadata +public final class test/Test { + // source: 'multipleRepeatableOrder.kt' + public method (): void + public inner class test/A$Container + public inner class test/B$Container + public inner class test/C$Container +} + +@java.lang.annotation.Retention(value=RUNTIME) +@kotlin.Metadata +public annotation class test/Z { + // source: 'multipleRepeatableOrder.kt' + public abstract method value(): java.lang.String +} diff --git a/compiler/testData/codegen/bytecodeListing/annotations/repeatable/retentionAndTarget.kt b/compiler/testData/codegen/bytecodeListing/annotations/repeatable/retentionAndTarget.kt new file mode 100644 index 00000000000..06612cefa05 --- /dev/null +++ b/compiler/testData/codegen/bytecodeListing/annotations/repeatable/retentionAndTarget.kt @@ -0,0 +1,28 @@ +// !LANGUAGE: +RepeatableAnnotations +// TARGET_BACKEND: JVM_IR +// WITH_RUNTIME +// FULL_JDK +// JVM_TARGET: 1.8 + +@Repeatable +annotation class RetentionRuntime + +@Repeatable +@Retention(AnnotationRetention.BINARY) +annotation class RetentionBinary + +@Repeatable +@Retention(AnnotationRetention.SOURCE) +annotation class RetentionSource + +@Repeatable +@Target(AnnotationTarget.CLASS) +annotation class TargetClassOnly + +@Repeatable +@Target(AnnotationTarget.ANNOTATION_CLASS, AnnotationTarget.TYPE) +annotation class TargetAnnotationClassAndTypeOnly + +@Repeatable +@Target() +annotation class TargetEmpty diff --git a/compiler/testData/codegen/bytecodeListing/annotations/repeatable/retentionAndTarget.txt b/compiler/testData/codegen/bytecodeListing/annotations/repeatable/retentionAndTarget.txt new file mode 100644 index 00000000000..a5908a27f25 --- /dev/null +++ b/compiler/testData/codegen/bytecodeListing/annotations/repeatable/retentionAndTarget.txt @@ -0,0 +1,111 @@ +@kotlin.annotation.Retention(value=BINARY) +@java.lang.annotation.Retention(value=CLASS) +@kotlin.Metadata +public annotation class RetentionBinary$Container { + // source: 'retentionAndTarget.kt' + public abstract method value(): RetentionBinary[] + public inner class RetentionBinary$Container +} + +@kotlin.annotation.Repeatable +@kotlin.annotation.Retention(value=BINARY) +@java.lang.annotation.Retention(value=CLASS) +@kotlin.Metadata +public annotation class RetentionBinary { + // source: 'retentionAndTarget.kt' + public inner class RetentionBinary$Container +} + +@java.lang.annotation.Retention(value=RUNTIME) +@kotlin.Metadata +public annotation class RetentionRuntime$Container { + // source: 'retentionAndTarget.kt' + public abstract method value(): RetentionRuntime[] + public inner class RetentionRuntime$Container +} + +@kotlin.annotation.Repeatable +@java.lang.annotation.Retention(value=RUNTIME) +@kotlin.Metadata +public annotation class RetentionRuntime { + // source: 'retentionAndTarget.kt' + public inner class RetentionRuntime$Container +} + +@kotlin.annotation.Retention(value=SOURCE) +@java.lang.annotation.Retention(value=SOURCE) +@kotlin.Metadata +public annotation class RetentionSource$Container { + // source: 'retentionAndTarget.kt' + public abstract method value(): RetentionSource[] + public inner class RetentionSource$Container +} + +@kotlin.annotation.Repeatable +@kotlin.annotation.Retention(value=SOURCE) +@java.lang.annotation.Retention(value=SOURCE) +@kotlin.Metadata +public annotation class RetentionSource { + // source: 'retentionAndTarget.kt' + public inner class RetentionSource$Container +} + +@kotlin.annotation.Target(allowedTargets=[ANNOTATION_CLASS, TYPE]) +@java.lang.annotation.Retention(value=RUNTIME) +@java.lang.annotation.Target(value=[ANNOTATION_TYPE, TYPE_USE]) +@kotlin.Metadata +public annotation class TargetAnnotationClassAndTypeOnly$Container { + // source: 'retentionAndTarget.kt' + public abstract method value(): TargetAnnotationClassAndTypeOnly[] + public inner class TargetAnnotationClassAndTypeOnly$Container +} + +@kotlin.annotation.Repeatable +@kotlin.annotation.Target(allowedTargets=[ANNOTATION_CLASS, TYPE]) +@java.lang.annotation.Retention(value=RUNTIME) +@java.lang.annotation.Target(value=[ANNOTATION_TYPE, TYPE_USE]) +@kotlin.Metadata +public annotation class TargetAnnotationClassAndTypeOnly { + // source: 'retentionAndTarget.kt' + public inner class TargetAnnotationClassAndTypeOnly$Container +} + +@kotlin.annotation.Target(allowedTargets=[CLASS]) +@java.lang.annotation.Retention(value=RUNTIME) +@java.lang.annotation.Target(value=[TYPE]) +@kotlin.Metadata +public annotation class TargetClassOnly$Container { + // source: 'retentionAndTarget.kt' + public abstract method value(): TargetClassOnly[] + public inner class TargetClassOnly$Container +} + +@kotlin.annotation.Repeatable +@kotlin.annotation.Target(allowedTargets=[CLASS]) +@java.lang.annotation.Retention(value=RUNTIME) +@java.lang.annotation.Target(value=[TYPE]) +@kotlin.Metadata +public annotation class TargetClassOnly { + // source: 'retentionAndTarget.kt' + public inner class TargetClassOnly$Container +} + +@kotlin.annotation.Target(allowedTargets=[]) +@java.lang.annotation.Retention(value=RUNTIME) +@java.lang.annotation.Target(value=[]) +@kotlin.Metadata +public annotation class TargetEmpty$Container { + // source: 'retentionAndTarget.kt' + public abstract method value(): TargetEmpty[] + public inner class TargetEmpty$Container +} + +@kotlin.annotation.Repeatable +@kotlin.annotation.Target(allowedTargets=[]) +@java.lang.annotation.Retention(value=RUNTIME) +@java.lang.annotation.Target(value=[]) +@kotlin.Metadata +public annotation class TargetEmpty { + // source: 'retentionAndTarget.kt' + public inner class TargetEmpty$Container +} 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 3a36d5d046f..69b5c307ba0 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 @@ -516,6 +516,18 @@ public class IrBlackBoxCodegenTestGenerated extends AbstractIrBlackBoxCodegenTes public void testJvmRepeatableKotlinAnnotation() throws Exception { runTest("compiler/testData/codegen/box/annotations/repeatable/jvmRepeatableKotlinAnnotation.kt"); } + + @Test + @TestMetadata("kotlinAnnotation.kt") + public void testKotlinAnnotation() throws Exception { + runTest("compiler/testData/codegen/box/annotations/repeatable/kotlinAnnotation.kt"); + } + + @Test + @TestMetadata("kotlinAnnotationWithBothRepeatables.kt") + public void testKotlinAnnotationWithBothRepeatables() throws Exception { + runTest("compiler/testData/codegen/box/annotations/repeatable/kotlinAnnotationWithBothRepeatables.kt"); + } } @Nested diff --git a/compiler/tests-common-new/tests-gen/org/jetbrains/kotlin/test/runners/codegen/IrBytecodeListingTestGenerated.java b/compiler/tests-common-new/tests-gen/org/jetbrains/kotlin/test/runners/codegen/IrBytecodeListingTestGenerated.java index ce6a98d118d..367b6b625a6 100644 --- a/compiler/tests-common-new/tests-gen/org/jetbrains/kotlin/test/runners/codegen/IrBytecodeListingTestGenerated.java +++ b/compiler/tests-common-new/tests-gen/org/jetbrains/kotlin/test/runners/codegen/IrBytecodeListingTestGenerated.java @@ -439,11 +439,41 @@ public class IrBytecodeListingTestGenerated extends AbstractIrBytecodeListingTes runTest("compiler/testData/codegen/bytecodeListing/annotations/repeatable/javaAnnotation.kt"); } + @Test + @TestMetadata("kotlinAnnotation.kt") + public void testKotlinAnnotation() throws Exception { + runTest("compiler/testData/codegen/bytecodeListing/annotations/repeatable/kotlinAnnotation.kt"); + } + + @Test + @TestMetadata("kotlinAnnotationWithBothRepeatables.kt") + public void testKotlinAnnotationWithBothRepeatables() throws Exception { + runTest("compiler/testData/codegen/bytecodeListing/annotations/repeatable/kotlinAnnotationWithBothRepeatables.kt"); + } + + @Test + @TestMetadata("kotlinSpecificTargets.kt") + public void testKotlinSpecificTargets() throws Exception { + runTest("compiler/testData/codegen/bytecodeListing/annotations/repeatable/kotlinSpecificTargets.kt"); + } + + @Test + @TestMetadata("multipleRepeatableOrder.kt") + public void testMultipleRepeatableOrder() throws Exception { + runTest("compiler/testData/codegen/bytecodeListing/annotations/repeatable/multipleRepeatableOrder.kt"); + } + @Test @TestMetadata("nonRepeatedAnnotationWithItsContainer.kt") public void testNonRepeatedAnnotationWithItsContainer() throws Exception { runTest("compiler/testData/codegen/bytecodeListing/annotations/repeatable/nonRepeatedAnnotationWithItsContainer.kt"); } + + @Test + @TestMetadata("retentionAndTarget.kt") + public void testRetentionAndTarget() throws Exception { + runTest("compiler/testData/codegen/bytecodeListing/annotations/repeatable/retentionAndTarget.kt"); + } } } diff --git a/core/compiler.common.jvm/src/org/jetbrains/kotlin/load/java/JvmAbi.kt b/core/compiler.common.jvm/src/org/jetbrains/kotlin/load/java/JvmAbi.kt index 674eddd9fce..ae1c2f8cca5 100644 --- a/core/compiler.common.jvm/src/org/jetbrains/kotlin/load/java/JvmAbi.kt +++ b/core/compiler.common.jvm/src/org/jetbrains/kotlin/load/java/JvmAbi.kt @@ -46,6 +46,8 @@ object JvmAbi { const val IMPL_SUFFIX_FOR_INLINE_CLASS_MEMBERS = "-impl" + const val REPEATABLE_ANNOTATION_CONTAINER_NAME = "Container" + /** * @param baseName JVM name of the property getter since Kotlin 1.4, or Kotlin name of the property otherwise. */