From 41500c916b9246a7891af7942c4eebb321b50e16 Mon Sep 17 00:00:00 2001 From: Mikhail Glukhikh Date: Tue, 28 Jul 2015 19:47:38 +0300 Subject: [PATCH] java Retention + Repeatable = kotlin.annotation.annotation (but java Repeatable is still intact), a few tests --- .../JetDiagnosticsWithJava8TestGenerated.java | 18 ++++++ .../annotations/javaRepeatable.kt | 19 ++++++ .../annotations/javaRepeatable.txt | 24 ++++++++ .../annotations/javaRepeatableRetention.kt | 21 +++++++ .../annotations/javaRepeatableRetention.txt | 24 ++++++++ .../annotations/javaUnrepeatable.kt | 9 +++ .../annotations/javaUnrepeatable.txt | 15 +++++ .../kotlin/load/java/JvmAnnotationNames.java | 1 + .../java/components/JavaAnnotationMapper.kt | 59 ++++++++++++++----- .../load/java/lazy/LazyJavaAnnotations.kt | 9 +-- 10 files changed, 181 insertions(+), 18 deletions(-) create mode 100644 compiler/testData/diagnostics/testsWithJava8/annotations/javaRepeatable.kt create mode 100644 compiler/testData/diagnostics/testsWithJava8/annotations/javaRepeatable.txt create mode 100644 compiler/testData/diagnostics/testsWithJava8/annotations/javaRepeatableRetention.kt create mode 100644 compiler/testData/diagnostics/testsWithJava8/annotations/javaRepeatableRetention.txt create mode 100644 compiler/testData/diagnostics/testsWithJava8/annotations/javaUnrepeatable.kt create mode 100644 compiler/testData/diagnostics/testsWithJava8/annotations/javaUnrepeatable.txt diff --git a/compiler/java8-tests/tests/org/jetbrains/kotlin/checkers/JetDiagnosticsWithJava8TestGenerated.java b/compiler/java8-tests/tests/org/jetbrains/kotlin/checkers/JetDiagnosticsWithJava8TestGenerated.java index cefdc57f706..0459c0ea97d 100644 --- a/compiler/java8-tests/tests/org/jetbrains/kotlin/checkers/JetDiagnosticsWithJava8TestGenerated.java +++ b/compiler/java8-tests/tests/org/jetbrains/kotlin/checkers/JetDiagnosticsWithJava8TestGenerated.java @@ -48,5 +48,23 @@ public class JetDiagnosticsWithJava8TestGenerated extends AbstractJetDiagnostics String fileName = JetTestUtils.navigationMetadata("compiler/testData/diagnostics/testsWithJava8/annotations/deprecatedRepeatable.kt"); doTest(fileName); } + + @TestMetadata("javaRepeatable.kt") + public void testJavaRepeatable() throws Exception { + String fileName = JetTestUtils.navigationMetadata("compiler/testData/diagnostics/testsWithJava8/annotations/javaRepeatable.kt"); + doTest(fileName); + } + + @TestMetadata("javaRepeatableRetention.kt") + public void testJavaRepeatableRetention() throws Exception { + String fileName = JetTestUtils.navigationMetadata("compiler/testData/diagnostics/testsWithJava8/annotations/javaRepeatableRetention.kt"); + doTest(fileName); + } + + @TestMetadata("javaUnrepeatable.kt") + public void testJavaUnrepeatable() throws Exception { + String fileName = JetTestUtils.navigationMetadata("compiler/testData/diagnostics/testsWithJava8/annotations/javaUnrepeatable.kt"); + doTest(fileName); + } } } diff --git a/compiler/testData/diagnostics/testsWithJava8/annotations/javaRepeatable.kt b/compiler/testData/diagnostics/testsWithJava8/annotations/javaRepeatable.kt new file mode 100644 index 00000000000..c04418c4718 --- /dev/null +++ b/compiler/testData/diagnostics/testsWithJava8/annotations/javaRepeatable.kt @@ -0,0 +1,19 @@ +// FILE: RepeatableAnnotation.java + +import java.lang.annotation.Repeatable + +@Repeatable(RepeatableAnnotations.class) +public @interface RepeatableAnnotation { + +} + +// FILE: RepeatableAnnotations.java + +public @interface RepeatableAnnotations { + RepeatableAnnotation[] value(); +} + +// FILE: RepeatableUse.kt + +RepeatableAnnotation RepeatableAnnotation class My + diff --git a/compiler/testData/diagnostics/testsWithJava8/annotations/javaRepeatable.txt b/compiler/testData/diagnostics/testsWithJava8/annotations/javaRepeatable.txt new file mode 100644 index 00000000000..09ef9013468 --- /dev/null +++ b/compiler/testData/diagnostics/testsWithJava8/annotations/javaRepeatable.txt @@ -0,0 +1,24 @@ +package + +RepeatableAnnotation() RepeatableAnnotation() internal final class My { + public constructor My() + public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean + public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int + public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String +} + +java.lang.annotation.Repeatable(value = RepeatableAnnotations::class) kotlin.annotation.annotation(repeatable = true) public final class RepeatableAnnotation : kotlin.Annotation { + public constructor RepeatableAnnotation() + public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean + public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int + public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String +} + +public final class RepeatableAnnotations : kotlin.Annotation { + public constructor RepeatableAnnotations(/*0*/ vararg value: RepeatableAnnotation /*kotlin.Array*/) + public final val value: kotlin.Array + public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean + public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int + public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String + public abstract fun value(): kotlin.Array +} diff --git a/compiler/testData/diagnostics/testsWithJava8/annotations/javaRepeatableRetention.kt b/compiler/testData/diagnostics/testsWithJava8/annotations/javaRepeatableRetention.kt new file mode 100644 index 00000000000..7659bc7a112 --- /dev/null +++ b/compiler/testData/diagnostics/testsWithJava8/annotations/javaRepeatableRetention.kt @@ -0,0 +1,21 @@ +// FILE: RepeatableAnnotation.java + +import java.lang.annotation.*; + +@Repeatable(RepeatableAnnotations.class) +@Retention(RetentionPolicy.RUNTIME) +public @interface RepeatableAnnotation { + +} + +// FILE: RepeatableAnnotations.java + +public @interface RepeatableAnnotations { + RepeatableAnnotation[] value(); +} + +// FILE: RepeatableUse.kt + +RepeatableAnnotation RepeatableAnnotation class My + + diff --git a/compiler/testData/diagnostics/testsWithJava8/annotations/javaRepeatableRetention.txt b/compiler/testData/diagnostics/testsWithJava8/annotations/javaRepeatableRetention.txt new file mode 100644 index 00000000000..0228d156a9c --- /dev/null +++ b/compiler/testData/diagnostics/testsWithJava8/annotations/javaRepeatableRetention.txt @@ -0,0 +1,24 @@ +package + +RepeatableAnnotation() RepeatableAnnotation() internal final class My { + public constructor My() + public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean + public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int + public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String +} + +java.lang.annotation.Repeatable(value = RepeatableAnnotations::class) kotlin.annotation.annotation(repeatable = true, retention = AnnotationRetention.RUNTIME) public final class RepeatableAnnotation : kotlin.Annotation { + public constructor RepeatableAnnotation() + public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean + public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int + public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String +} + +public final class RepeatableAnnotations : kotlin.Annotation { + public constructor RepeatableAnnotations(/*0*/ vararg value: RepeatableAnnotation /*kotlin.Array*/) + public final val value: kotlin.Array + public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean + public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int + public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String + public abstract fun value(): kotlin.Array +} diff --git a/compiler/testData/diagnostics/testsWithJava8/annotations/javaUnrepeatable.kt b/compiler/testData/diagnostics/testsWithJava8/annotations/javaUnrepeatable.kt new file mode 100644 index 00000000000..dc58f6fe060 --- /dev/null +++ b/compiler/testData/diagnostics/testsWithJava8/annotations/javaUnrepeatable.kt @@ -0,0 +1,9 @@ +// FILE: UnrepeatableAnnotation.java + +public @interface UnrepeatableAnnotation { + +} + +// FILE: UnrepeatableUse.kt + +UnrepeatableAnnotation UnrepeatableAnnotation class My \ No newline at end of file diff --git a/compiler/testData/diagnostics/testsWithJava8/annotations/javaUnrepeatable.txt b/compiler/testData/diagnostics/testsWithJava8/annotations/javaUnrepeatable.txt new file mode 100644 index 00000000000..891ba230839 --- /dev/null +++ b/compiler/testData/diagnostics/testsWithJava8/annotations/javaUnrepeatable.txt @@ -0,0 +1,15 @@ +package + +UnrepeatableAnnotation() UnrepeatableAnnotation() internal final class My { + public constructor My() + public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean + public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int + public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String +} + +public final class UnrepeatableAnnotation : kotlin.Annotation { + public constructor UnrepeatableAnnotation() + public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean + public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int + public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String +} diff --git a/core/descriptor.loader.java/src/org/jetbrains/kotlin/load/java/JvmAnnotationNames.java b/core/descriptor.loader.java/src/org/jetbrains/kotlin/load/java/JvmAnnotationNames.java index fccd277c6ac..ede6c7a0f3d 100644 --- a/core/descriptor.loader.java/src/org/jetbrains/kotlin/load/java/JvmAnnotationNames.java +++ b/core/descriptor.loader.java/src/org/jetbrains/kotlin/load/java/JvmAnnotationNames.java @@ -40,6 +40,7 @@ public final class JvmAnnotationNames { public static final Name DEFAULT_ANNOTATION_MEMBER_NAME = Name.identifier("value"); public static final Name TARGET_ANNOTATION_MEMBER_NAME = Name.identifier("allowedTargets"); public static final Name RETENTION_ANNOTATION_PARAMETER_NAME = Name.identifier("retention"); + public static final Name REPEATABLE_ANNOTATION_PARAMETER_NAME = Name.identifier("repeatable"); public static final FqName TARGET_ANNOTATION = new FqName("java.lang.annotation.Target"); public static final FqName RETENTION_ANNOTATION = new FqName("java.lang.annotation.Retention"); diff --git a/core/descriptor.loader.java/src/org/jetbrains/kotlin/load/java/components/JavaAnnotationMapper.kt b/core/descriptor.loader.java/src/org/jetbrains/kotlin/load/java/components/JavaAnnotationMapper.kt index a537faa51b7..279ea8667f6 100644 --- a/core/descriptor.loader.java/src/org/jetbrains/kotlin/load/java/components/JavaAnnotationMapper.kt +++ b/core/descriptor.loader.java/src/org/jetbrains/kotlin/load/java/components/JavaAnnotationMapper.kt @@ -22,6 +22,7 @@ import org.jetbrains.kotlin.descriptors.ValueParameterDescriptor import org.jetbrains.kotlin.descriptors.annotations.AnnotationDescriptor import org.jetbrains.kotlin.load.java.JvmAnnotationNames import org.jetbrains.kotlin.load.java.lazy.LazyJavaResolverContext +import org.jetbrains.kotlin.load.java.lazy.descriptors.resolveAnnotation import org.jetbrains.kotlin.load.java.structure.JavaAnnotation import org.jetbrains.kotlin.load.java.structure.JavaAnnotationArgument import org.jetbrains.kotlin.load.java.structure.JavaArrayAnnotationArgument @@ -34,6 +35,8 @@ import org.jetbrains.kotlin.descriptors.annotations.KotlinTarget import org.jetbrains.kotlin.name.ClassId import org.jetbrains.kotlin.name.FqName import org.jetbrains.kotlin.descriptors.annotations.KotlinRetention +import org.jetbrains.kotlin.load.java.structure.* +import org.jetbrains.kotlin.resolve.constants.* import org.jetbrains.kotlin.resolve.constants.ConstantValueFactory import java.lang.annotation.Retention import java.lang.annotation.Target @@ -47,17 +50,39 @@ public object JavaAnnotationMapper { // Java8-specific thing private val javaRepeatableFqName = FqName("java.lang.annotation.Repeatable") - public fun mapJavaAnnotation(annotation: JavaAnnotation, c: LazyJavaResolverContext): AnnotationDescriptor? = + public fun mapOrResolveJavaAnnotation(annotation: JavaAnnotation, c: LazyJavaResolverContext): AnnotationDescriptor? = when (annotation.classId) { ClassId.topLevel(javaTargetFqName) -> JavaTargetAnnotationDescriptor(annotation, c) - ClassId.topLevel(javaRetentionFqName) -> JavaRetentionAnnotationDescriptor(annotation, c) ClassId.topLevel(javaDeprecatedFqName) -> JavaDeprecatedAnnotationDescriptor(annotation, c) - else -> null + ClassId.topLevel(javaRetentionFqName) -> null + else -> c.resolveAnnotation(annotation) } - public val kotlinToJavaNameMap: Map = + public fun findMappedJavaAnnotation(kotlinName: FqName, + annotationOwner: JavaAnnotationOwner, + c: LazyJavaResolverContext + ): AnnotationDescriptor? { + if (kotlinName == KotlinBuiltIns.FQ_NAMES.annotation) { + // Construct kotlin.annotation.annotation from Retention & Repeatable + val retentionAnnotation = annotationOwner.findAnnotation(javaRetentionFqName) + val repeatableAnnotation = annotationOwner.findAnnotation(javaRepeatableFqName) + return if (retentionAnnotation != null || repeatableAnnotation != null) { + JavaRetentionRepeatableAnnotationDescriptor(retentionAnnotation, repeatableAnnotation != null, c) + } + else { + null + } + } + return kotlinToJavaNameMap[kotlinName]?.let { + annotationOwner.findAnnotation(it)?.let { + mapOrResolveJavaAnnotation(it, c) + } + } + } + + // kotlin.annotation.annotation is treated separately + private val kotlinToJavaNameMap: Map = mapOf(KotlinBuiltIns.FQ_NAMES.target to javaTargetFqName, - KotlinBuiltIns.FQ_NAMES.annotation to javaRetentionFqName, KotlinBuiltIns.FQ_NAMES.deprecated to javaDeprecatedFqName) public val javaToKotlinNameMap: Map = @@ -68,7 +93,7 @@ public object JavaAnnotationMapper { } abstract class AbstractJavaAnnotationDescriptor( - annotation: JavaAnnotation, + annotation: JavaAnnotation?, private val kotlinAnnotationClassDescriptor: ClassDescriptor ): AnnotationDescriptor { override fun getType() = kotlinAnnotationClassDescriptor.defaultType @@ -76,7 +101,7 @@ abstract class AbstractJavaAnnotationDescriptor( protected val valueParameters: List get() = kotlinAnnotationClassDescriptor.constructors.single().valueParameters - protected val firstArgument: JavaAnnotationArgument? = annotation.arguments.firstOrNull() + protected val firstArgument: JavaAnnotationArgument? = annotation?.arguments?.firstOrNull() } class JavaDeprecatedAnnotationDescriptor( @@ -94,20 +119,26 @@ class JavaDeprecatedAnnotationDescriptor( override fun getAllValueArguments() = valueArguments() } -class JavaRetentionAnnotationDescriptor( - annotation: JavaAnnotation, +class JavaRetentionRepeatableAnnotationDescriptor( + retentionAnnotation: JavaAnnotation?, + repeatable: Boolean, c: LazyJavaResolverContext -): AbstractJavaAnnotationDescriptor(annotation, c.module.builtIns.annotationAnnotation) { +): AbstractJavaAnnotationDescriptor(retentionAnnotation, c.module.builtIns.annotationAnnotation) { private val valueArguments = c.storageManager.createLazyValue { - val targetArgument = when (firstArgument) { + val retentionArgument = when (firstArgument) { is JavaEnumValueAnnotationArgument -> JavaAnnotationTargetMapper.mapJavaRetentionArgument(firstArgument, c.module.builtIns) - else -> return@createLazyValue emptyMap>() + else -> null } - val parameterDescriptor = valueParameters.firstOrNull { + val retentionParameterDescriptor = valueParameters.first { it.name == JvmAnnotationNames.RETENTION_ANNOTATION_PARAMETER_NAME } - parameterDescriptor?.let { mapOf(it to targetArgument) } ?: emptyMap() + val repeatableArgument = if (repeatable) BooleanValue(true, c.module.builtIns) else null + val repeatableParameterDescriptor = valueParameters.first { + it.name == JvmAnnotationNames.REPEATABLE_ANNOTATION_PARAMETER_NAME + } + (retentionArgument?.let { mapOf(retentionParameterDescriptor to it) } ?: emptyMap()) + + (repeatableArgument?.let { mapOf(repeatableParameterDescriptor to it) } ?: emptyMap()) } override fun getAllValueArguments() = valueArguments() diff --git a/core/descriptor.loader.java/src/org/jetbrains/kotlin/load/java/lazy/LazyJavaAnnotations.kt b/core/descriptor.loader.java/src/org/jetbrains/kotlin/load/java/lazy/LazyJavaAnnotations.kt index f0be45995bc..bb913b1e115 100644 --- a/core/descriptor.loader.java/src/org/jetbrains/kotlin/load/java/lazy/LazyJavaAnnotations.kt +++ b/core/descriptor.loader.java/src/org/jetbrains/kotlin/load/java/lazy/LazyJavaAnnotations.kt @@ -16,6 +16,7 @@ package org.jetbrains.kotlin.load.java.lazy +import org.jetbrains.kotlin.builtins.KotlinBuiltIns import org.jetbrains.kotlin.descriptors.annotations.Annotations import org.jetbrains.kotlin.load.java.components.JavaAnnotationMapper import org.jetbrains.kotlin.load.java.lazy.descriptors.resolveAnnotation @@ -29,19 +30,19 @@ class LazyJavaAnnotations( private val annotationOwner: JavaAnnotationOwner ) : Annotations { private val annotationDescriptors = c.storageManager.createMemoizedFunctionWithNullableValues { - annotation: JavaAnnotation -> - JavaAnnotationMapper.mapJavaAnnotation(annotation, c) ?: c.resolveAnnotation(annotation) + annotation: JavaAnnotation -> JavaAnnotationMapper.mapOrResolveJavaAnnotation(annotation, c) } override fun findAnnotation(fqName: FqName) = annotationOwner.findAnnotation(fqName)?.let(annotationDescriptors) - ?: JavaAnnotationMapper.kotlinToJavaNameMap[fqName]?.let { annotationOwner.findAnnotation(it)?.let(annotationDescriptors) } + ?: JavaAnnotationMapper.findMappedJavaAnnotation(fqName, annotationOwner, c) override fun findExternalAnnotation(fqName: FqName) = c.externalAnnotationResolver.findExternalAnnotation(annotationOwner, fqName)?.let(annotationDescriptors) override fun iterator() = - annotationOwner.annotations.asSequence().map(annotationDescriptors).filterNotNull().iterator() + (annotationOwner.annotations.asSequence().map(annotationDescriptors) + + JavaAnnotationMapper.findMappedJavaAnnotation(KotlinBuiltIns.FQ_NAMES.annotation, annotationOwner, c)).filterNotNull().iterator() override fun isEmpty() = !iterator().hasNext() }