diff --git a/compiler/testData/codegen/boxWithStdlib/reflection/annotations/annotationRetentionAnnotation.kt b/compiler/testData/codegen/boxWithStdlib/reflection/annotations/annotationRetentionAnnotation.kt new file mode 100644 index 00000000000..298a96cd594 --- /dev/null +++ b/compiler/testData/codegen/boxWithStdlib/reflection/annotations/annotationRetentionAnnotation.kt @@ -0,0 +1,15 @@ +import kotlin.test.assertEquals + +annotation(retention = AnnotationRetention.RUNTIME) class Anno + +fun box(): String { + val a = Anno::class.annotations +/* + // TODO: support kotlin.annotation.annotation + if (a.size() != 1) return "Fail 1: $a" + val ann = a.single() as? annotation ?: return "Fail 2: ${a.single()}" + assertEquals(AnnotationRetention.RUNTIME, ann.retention) +*/ + assert(a.isEmpty()) + return "OK" +} diff --git a/compiler/testData/codegen/boxWithStdlib/reflection/annotations/propertyAccessors.kt b/compiler/testData/codegen/boxWithStdlib/reflection/annotations/propertyAccessors.kt new file mode 100644 index 00000000000..38b46e756f8 --- /dev/null +++ b/compiler/testData/codegen/boxWithStdlib/reflection/annotations/propertyAccessors.kt @@ -0,0 +1,15 @@ +annotation class Get +annotation class Set +annotation class SetParam + +var foo: String + @Get get() = "" + @Set set(@SetParam value) {} + +fun box(): String { + assert(::foo.getter.annotations.single() is Get) + assert(::foo.setter.annotations.single() is Set) + assert(::foo.setter.parameters.single().annotations.single() is SetParam) + + return "OK" +} diff --git a/compiler/testData/codegen/boxWithStdlib/reflection/annotations/propertyWithoutBackingField.kt b/compiler/testData/codegen/boxWithStdlib/reflection/annotations/propertyWithoutBackingField.kt new file mode 100644 index 00000000000..80cd7da73ab --- /dev/null +++ b/compiler/testData/codegen/boxWithStdlib/reflection/annotations/propertyWithoutBackingField.kt @@ -0,0 +1,9 @@ +annotation class Ann(val value: String) + +@Ann("OK") +val property: String + get() = "" + +fun box(): String { + return (::property.annotations.single() as Ann).value +} diff --git a/compiler/testData/codegen/boxWithStdlib/reflection/annotations/retentions.kt b/compiler/testData/codegen/boxWithStdlib/reflection/annotations/retentions.kt new file mode 100644 index 00000000000..a2d14b57ed6 --- /dev/null +++ b/compiler/testData/codegen/boxWithStdlib/reflection/annotations/retentions.kt @@ -0,0 +1,13 @@ +import kotlin.test.assertEquals + +annotation(retention = AnnotationRetention.SOURCE) class SourceAnno +annotation(retention = AnnotationRetention.BINARY) class BinaryAnno +annotation(retention = AnnotationRetention.RUNTIME) class RuntimeAnno + +@SourceAnno +@BinaryAnno +@RuntimeAnno +fun box(): String { + assertEquals(listOf(javaClass()), ::box.annotations.map { it.annotationType() }) + return "OK" +} diff --git a/compiler/testData/codegen/boxWithStdlib/reflection/annotations/simpleClassAnnotation.kt b/compiler/testData/codegen/boxWithStdlib/reflection/annotations/simpleClassAnnotation.kt new file mode 100644 index 00000000000..1e0104741fc --- /dev/null +++ b/compiler/testData/codegen/boxWithStdlib/reflection/annotations/simpleClassAnnotation.kt @@ -0,0 +1,8 @@ +annotation(retention = AnnotationRetention.RUNTIME) class Simple(val value: String) + +@Simple("OK") +class A + +fun box(): String { + return (A::class.annotations.single() as Simple).value +} diff --git a/compiler/testData/codegen/boxWithStdlib/reflection/annotations/simpleConstructorAnnotation.kt b/compiler/testData/codegen/boxWithStdlib/reflection/annotations/simpleConstructorAnnotation.kt new file mode 100644 index 00000000000..c189d554f89 --- /dev/null +++ b/compiler/testData/codegen/boxWithStdlib/reflection/annotations/simpleConstructorAnnotation.kt @@ -0,0 +1,13 @@ +annotation class Primary +annotation class Secondary + +class C @Primary constructor() { + @Secondary + constructor(s: String): this() +} + +fun box(): String { + val ans = C::class.constructors.map { it.annotations.single().annotationType().simpleName }.toSortedList() + if (ans != listOf("Primary", "Secondary")) return "Fail: $ans" + return "OK" +} diff --git a/compiler/testData/codegen/boxWithStdlib/reflection/annotations/simpleFunAnnotation.kt b/compiler/testData/codegen/boxWithStdlib/reflection/annotations/simpleFunAnnotation.kt new file mode 100644 index 00000000000..2295a82366c --- /dev/null +++ b/compiler/testData/codegen/boxWithStdlib/reflection/annotations/simpleFunAnnotation.kt @@ -0,0 +1,6 @@ +annotation(retention = AnnotationRetention.RUNTIME) class Simple(val value: String) + +@Simple("OK") +fun box(): String { + return (::box.annotations.single() as Simple).value +} diff --git a/compiler/testData/codegen/boxWithStdlib/reflection/annotations/simpleParamAnnotation.kt b/compiler/testData/codegen/boxWithStdlib/reflection/annotations/simpleParamAnnotation.kt new file mode 100644 index 00000000000..fba54d761cb --- /dev/null +++ b/compiler/testData/codegen/boxWithStdlib/reflection/annotations/simpleParamAnnotation.kt @@ -0,0 +1,7 @@ +annotation(retention = AnnotationRetention.RUNTIME) class Simple(val value: String) + +fun test(@Simple("OK") x: Int) {} + +fun box(): String { + return (::test.parameters.single().annotations.single() as Simple).value +} diff --git a/compiler/testData/codegen/boxWithStdlib/reflection/annotations/simpleValAnnotation.kt b/compiler/testData/codegen/boxWithStdlib/reflection/annotations/simpleValAnnotation.kt new file mode 100644 index 00000000000..329a0eabc40 --- /dev/null +++ b/compiler/testData/codegen/boxWithStdlib/reflection/annotations/simpleValAnnotation.kt @@ -0,0 +1,8 @@ +annotation(retention = AnnotationRetention.RUNTIME) class Simple(val value: String) + +@Simple("OK") +val foo: Int = 0 + +fun box(): String { + return (::foo.annotations.single() as Simple).value +} diff --git a/compiler/tests/org/jetbrains/kotlin/codegen/generated/BlackBoxWithStdlibCodegenTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/codegen/generated/BlackBoxWithStdlibCodegenTestGenerated.java index ba9bee1d5dd..ec2f8c8b6db 100644 --- a/compiler/tests/org/jetbrains/kotlin/codegen/generated/BlackBoxWithStdlibCodegenTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/codegen/generated/BlackBoxWithStdlibCodegenTestGenerated.java @@ -2767,6 +2767,69 @@ public class BlackBoxWithStdlibCodegenTestGenerated extends AbstractBlackBoxCode JetTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("compiler/testData/codegen/boxWithStdlib/reflection"), Pattern.compile("^(.+)\\.kt$"), true); } + @TestMetadata("compiler/testData/codegen/boxWithStdlib/reflection/annotations") + @TestDataPath("$PROJECT_ROOT") + @RunWith(JUnit3RunnerWithInners.class) + public static class Annotations extends AbstractBlackBoxCodegenTest { + public void testAllFilesPresentInAnnotations() throws Exception { + JetTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("compiler/testData/codegen/boxWithStdlib/reflection/annotations"), Pattern.compile("^(.+)\\.kt$"), true); + } + + @TestMetadata("annotationRetentionAnnotation.kt") + public void testAnnotationRetentionAnnotation() throws Exception { + String fileName = JetTestUtils.navigationMetadata("compiler/testData/codegen/boxWithStdlib/reflection/annotations/annotationRetentionAnnotation.kt"); + doTestWithStdlib(fileName); + } + + @TestMetadata("propertyAccessors.kt") + public void testPropertyAccessors() throws Exception { + String fileName = JetTestUtils.navigationMetadata("compiler/testData/codegen/boxWithStdlib/reflection/annotations/propertyAccessors.kt"); + doTestWithStdlib(fileName); + } + + @TestMetadata("propertyWithoutBackingField.kt") + public void testPropertyWithoutBackingField() throws Exception { + String fileName = JetTestUtils.navigationMetadata("compiler/testData/codegen/boxWithStdlib/reflection/annotations/propertyWithoutBackingField.kt"); + doTestWithStdlib(fileName); + } + + @TestMetadata("retentions.kt") + public void testRetentions() throws Exception { + String fileName = JetTestUtils.navigationMetadata("compiler/testData/codegen/boxWithStdlib/reflection/annotations/retentions.kt"); + doTestWithStdlib(fileName); + } + + @TestMetadata("simpleClassAnnotation.kt") + public void testSimpleClassAnnotation() throws Exception { + String fileName = JetTestUtils.navigationMetadata("compiler/testData/codegen/boxWithStdlib/reflection/annotations/simpleClassAnnotation.kt"); + doTestWithStdlib(fileName); + } + + @TestMetadata("simpleConstructorAnnotation.kt") + public void testSimpleConstructorAnnotation() throws Exception { + String fileName = JetTestUtils.navigationMetadata("compiler/testData/codegen/boxWithStdlib/reflection/annotations/simpleConstructorAnnotation.kt"); + doTestWithStdlib(fileName); + } + + @TestMetadata("simpleFunAnnotation.kt") + public void testSimpleFunAnnotation() throws Exception { + String fileName = JetTestUtils.navigationMetadata("compiler/testData/codegen/boxWithStdlib/reflection/annotations/simpleFunAnnotation.kt"); + doTestWithStdlib(fileName); + } + + @TestMetadata("simpleParamAnnotation.kt") + public void testSimpleParamAnnotation() throws Exception { + String fileName = JetTestUtils.navigationMetadata("compiler/testData/codegen/boxWithStdlib/reflection/annotations/simpleParamAnnotation.kt"); + doTestWithStdlib(fileName); + } + + @TestMetadata("simpleValAnnotation.kt") + public void testSimpleValAnnotation() throws Exception { + String fileName = JetTestUtils.navigationMetadata("compiler/testData/codegen/boxWithStdlib/reflection/annotations/simpleValAnnotation.kt"); + doTestWithStdlib(fileName); + } + } + @TestMetadata("compiler/testData/codegen/boxWithStdlib/reflection/call") @TestDataPath("$PROJECT_ROOT") @RunWith(JUnit3RunnerWithInners.class) diff --git a/core/builtins/src/kotlin/reflect/KAnnotatedElement.kt b/core/builtins/src/kotlin/reflect/KAnnotatedElement.kt new file mode 100644 index 00000000000..ecbf62adc5c --- /dev/null +++ b/core/builtins/src/kotlin/reflect/KAnnotatedElement.kt @@ -0,0 +1,29 @@ +/* + * Copyright 2010-2015 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package kotlin.reflect + +/** + * Represents an annotated element and allows to obtain its annotations. + * See the [Kotlin language documentation](http://kotlinlang.org/docs/reference/annotations.html) + * for more information. + */ +public interface KAnnotatedElement { + /** + * Annotations which are present on this element. + */ + public val annotations: List +} diff --git a/core/builtins/src/kotlin/reflect/KCallable.kt b/core/builtins/src/kotlin/reflect/KCallable.kt index 232ace3cab5..51060c533c7 100644 --- a/core/builtins/src/kotlin/reflect/KCallable.kt +++ b/core/builtins/src/kotlin/reflect/KCallable.kt @@ -21,7 +21,7 @@ package kotlin.reflect * * @param R return type of the callable. */ -public interface KCallable { +public interface KCallable : KAnnotatedElement { /** * The name of this callable as it was declared in the source code. * If the callable has no name, a special invented name is created. diff --git a/core/builtins/src/kotlin/reflect/KClass.kt b/core/builtins/src/kotlin/reflect/KClass.kt index 81660d6de15..43bdc10f907 100644 --- a/core/builtins/src/kotlin/reflect/KClass.kt +++ b/core/builtins/src/kotlin/reflect/KClass.kt @@ -24,7 +24,7 @@ package kotlin.reflect * * @param T the type of the class. */ -public interface KClass : KDeclarationContainer { +public interface KClass : KDeclarationContainer, KAnnotatedElement { /** * The simple name of the class as it was declared in the source code, * or `null` if the class has no name (if, for example, it is an anonymous object literal). diff --git a/core/builtins/src/kotlin/reflect/KParameter.kt b/core/builtins/src/kotlin/reflect/KParameter.kt index dd5430ad580..f2c5e32eb57 100644 --- a/core/builtins/src/kotlin/reflect/KParameter.kt +++ b/core/builtins/src/kotlin/reflect/KParameter.kt @@ -20,7 +20,7 @@ package kotlin.reflect * Represents a parameter passed to a function or a property getter/setter, * including `this` and extension receiver parameters. */ -public interface KParameter { +public interface KParameter : KAnnotatedElement { /** * 0-based index of this parameter in the parameter list of its containing callable. */ diff --git a/core/reflection.jvm/src/kotlin/reflect/jvm/internal/KAnnotatedElementImpl.kt b/core/reflection.jvm/src/kotlin/reflect/jvm/internal/KAnnotatedElementImpl.kt new file mode 100644 index 00000000000..57e2d1fe204 --- /dev/null +++ b/core/reflection.jvm/src/kotlin/reflect/jvm/internal/KAnnotatedElementImpl.kt @@ -0,0 +1,30 @@ +/* + * Copyright 2010-2015 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package kotlin.reflect.jvm.internal + +import org.jetbrains.kotlin.descriptors.annotations.Annotated +import org.jetbrains.kotlin.load.kotlin.reflect.ReflectAnnotationSource +import kotlin.reflect.KAnnotatedElement + +interface KAnnotatedElementImpl : KAnnotatedElement { + val annotated: Annotated + + override val annotations: List + get() = annotated.annotations.map { + (it.source as? ReflectAnnotationSource)?.annotation + }.filterNotNull() +} diff --git a/core/reflection.jvm/src/kotlin/reflect/jvm/internal/KCallableImpl.kt b/core/reflection.jvm/src/kotlin/reflect/jvm/internal/KCallableImpl.kt index 26cf3d0ddca..1716113ae0f 100644 --- a/core/reflection.jvm/src/kotlin/reflect/jvm/internal/KCallableImpl.kt +++ b/core/reflection.jvm/src/kotlin/reflect/jvm/internal/KCallableImpl.kt @@ -17,14 +17,17 @@ package kotlin.reflect.jvm.internal import org.jetbrains.kotlin.descriptors.CallableMemberDescriptor +import org.jetbrains.kotlin.descriptors.annotations.Annotated import java.util.ArrayList import kotlin.reflect.KCallable import kotlin.reflect.KParameter import kotlin.reflect.KType -interface KCallableImpl : KCallable { +interface KCallableImpl : KCallable, KAnnotatedElementImpl { val descriptor: CallableMemberDescriptor + override val annotated: Annotated get() = descriptor + override val parameters: List get() { val result = ArrayList() diff --git a/core/reflection.jvm/src/kotlin/reflect/jvm/internal/KClassImpl.kt b/core/reflection.jvm/src/kotlin/reflect/jvm/internal/KClassImpl.kt index a814fd44568..d28422d1341 100644 --- a/core/reflection.jvm/src/kotlin/reflect/jvm/internal/KClassImpl.kt +++ b/core/reflection.jvm/src/kotlin/reflect/jvm/internal/KClassImpl.kt @@ -18,6 +18,7 @@ package kotlin.reflect.jvm.internal import org.jetbrains.kotlin.descriptors.ClassKind import org.jetbrains.kotlin.descriptors.ConstructorDescriptor +import org.jetbrains.kotlin.descriptors.annotations.Annotated import org.jetbrains.kotlin.name.ClassId import org.jetbrains.kotlin.resolve.scopes.ChainedScope import org.jetbrains.kotlin.resolve.scopes.JetScope @@ -26,7 +27,7 @@ import kotlin.reflect.KClass import kotlin.reflect.KFunction import kotlin.reflect.KotlinReflectionInternalError -class KClassImpl(override val jClass: Class) : KCallableContainerImpl(), KClass { +class KClassImpl(override val jClass: Class) : KCallableContainerImpl(), KClass, KAnnotatedElementImpl { val descriptor by ReflectProperties.lazySoft { val classId = classId @@ -37,6 +38,8 @@ class KClassImpl(override val jClass: Class) : KCallableContainerImpl(), K descriptor ?: throw KotlinReflectionInternalError("Class not resolved: $jClass") } + override val annotated: Annotated get() = descriptor + private val classId: ClassId get() = RuntimeTypeMapper.mapJvmClassToKotlinClassId(jClass) override val scope: JetScope get() = ChainedScope( diff --git a/core/reflection.jvm/src/kotlin/reflect/jvm/internal/KParameterImpl.kt b/core/reflection.jvm/src/kotlin/reflect/jvm/internal/KParameterImpl.kt index 58344eedbf6..3688b0e1c37 100644 --- a/core/reflection.jvm/src/kotlin/reflect/jvm/internal/KParameterImpl.kt +++ b/core/reflection.jvm/src/kotlin/reflect/jvm/internal/KParameterImpl.kt @@ -18,15 +18,18 @@ package kotlin.reflect.jvm.internal import org.jetbrains.kotlin.descriptors.ParameterDescriptor import org.jetbrains.kotlin.descriptors.ValueParameterDescriptor +import org.jetbrains.kotlin.descriptors.annotations.Annotated import kotlin.reflect.KParameter import kotlin.reflect.KType class KParameterImpl( override val index: Int, private val computeDescriptor: () -> ParameterDescriptor -) : KParameter { +) : KParameter, KAnnotatedElementImpl { private val descriptor: ParameterDescriptor by ReflectProperties.lazySoft(computeDescriptor) + override val annotated: Annotated get() = descriptor + override val name: String? get() { val valueParameter = descriptor as? ValueParameterDescriptor ?: return null if (valueParameter.containingDeclaration.hasSynthesizedParameterNames()) return null diff --git a/core/runtime.jvm/src/kotlin/jvm/internal/CallableReference.java b/core/runtime.jvm/src/kotlin/jvm/internal/CallableReference.java index a24cf0ad853..1db0736aedb 100644 --- a/core/runtime.jvm/src/kotlin/jvm/internal/CallableReference.java +++ b/core/runtime.jvm/src/kotlin/jvm/internal/CallableReference.java @@ -23,6 +23,7 @@ import kotlin.reflect.KParameter; import kotlin.reflect.KType; import org.jetbrains.annotations.NotNull; +import java.lang.annotation.Annotation; import java.util.List; /** @@ -75,6 +76,11 @@ public abstract class CallableReference implements KCallable { throw error(); } + @Override + public List getAnnotations() { + throw error(); + } + @Override public Object call(@NotNull Object... args) { throw error(); diff --git a/core/runtime.jvm/src/kotlin/jvm/internal/ClassReference.kt b/core/runtime.jvm/src/kotlin/jvm/internal/ClassReference.kt index 93c19b27d63..e0ceb5e644e 100644 --- a/core/runtime.jvm/src/kotlin/jvm/internal/ClassReference.kt +++ b/core/runtime.jvm/src/kotlin/jvm/internal/ClassReference.kt @@ -33,5 +33,8 @@ public class ClassReference(override val jClass: Class<*>) : KClass, Declar override val constructors: Collection> get() = error() + override val annotations: List + get() = error() + private fun error(): Nothing = throw KotlinReflectionNotSupportedError() } diff --git a/core/runtime.jvm/src/kotlin/jvm/internal/FunctionReference.java b/core/runtime.jvm/src/kotlin/jvm/internal/FunctionReference.java index c868feee1ca..2aadabb524b 100644 --- a/core/runtime.jvm/src/kotlin/jvm/internal/FunctionReference.java +++ b/core/runtime.jvm/src/kotlin/jvm/internal/FunctionReference.java @@ -20,6 +20,7 @@ import kotlin.jvm.KotlinReflectionNotSupportedError; import kotlin.reflect.*; import org.jetbrains.annotations.NotNull; +import java.lang.annotation.Annotation; import java.util.List; @SuppressWarnings("deprecation") @@ -71,6 +72,11 @@ public class FunctionReference throw error(); } + @Override + public List getAnnotations() { + throw error(); + } + @Override public Object call(@NotNull Object... args) { throw error();