diff --git a/compiler/testData/codegen/box/reflection/modifiers/callableVisibility.kt b/compiler/testData/codegen/box/reflection/modifiers/callableVisibility.kt new file mode 100644 index 00000000000..e02f3d901ee --- /dev/null +++ b/compiler/testData/codegen/box/reflection/modifiers/callableVisibility.kt @@ -0,0 +1,55 @@ +// WITH_REFLECT + +import kotlin.reflect.KFunction +import kotlin.reflect.KProperty +import kotlin.reflect.KVisibility +import kotlin.test.assertEquals + +open class Foo { + public fun publicFun() {} + protected fun protectedFun() {} + internal fun internalFun() {} + private fun privateFun() {} + private fun privateToThisFun(): T = null!! + + fun getProtectedFun() = this::protectedFun + fun getPrivateFun() = this::privateFun + fun getPrivateToThisFun(): KFunction<*> = this::privateToThisFun + + public val publicVal = Unit + protected val protectedVar = Unit + internal val internalVal = Unit + private val privateVal = Unit + private val privateToThisVal: T? = null + + fun getProtectedVar() = this::protectedVar + fun getPrivateVal() = this::privateVal + fun getPrivateToThisVal(): KProperty<*> = this::privateToThisVal + + public var publicVarPrivateSetter = Unit + private set + + fun getPublicVarPrivateSetter() = this::publicVarPrivateSetter +} + +fun box(): String { + val f = Foo() + + assertEquals(KVisibility.PUBLIC, f::publicFun.visibility) + assertEquals(KVisibility.PROTECTED, f.getProtectedFun().visibility) + assertEquals(KVisibility.INTERNAL, f::internalFun.visibility) + assertEquals(KVisibility.PRIVATE, f.getPrivateFun().visibility) + assertEquals(KVisibility.PRIVATE, f.getPrivateToThisFun().visibility) + + assertEquals(KVisibility.PUBLIC, f::publicVal.visibility) + assertEquals(KVisibility.PROTECTED, f.getProtectedVar().visibility) + assertEquals(KVisibility.INTERNAL, f::internalVal.visibility) + assertEquals(KVisibility.PRIVATE, f.getPrivateVal().visibility) + assertEquals(KVisibility.PRIVATE, f.getPrivateToThisVal().visibility) + + assertEquals(KVisibility.PUBLIC, f.getPublicVarPrivateSetter().visibility) + assertEquals(KVisibility.PUBLIC, f.getPublicVarPrivateSetter().getter.visibility) + assertEquals(KVisibility.PRIVATE, f.getPublicVarPrivateSetter().setter.visibility) + + return "OK" +} diff --git a/compiler/testData/codegen/box/reflection/modifiers/classVisibility.kt b/compiler/testData/codegen/box/reflection/modifiers/classVisibility.kt new file mode 100644 index 00000000000..bd5370a76d2 --- /dev/null +++ b/compiler/testData/codegen/box/reflection/modifiers/classVisibility.kt @@ -0,0 +1,29 @@ +// WITH_REFLECT + +import kotlin.reflect.KClass +import kotlin.reflect.KVisibility +import kotlin.test.assertEquals + +class DefaultVisibilityClass +public class PublicClass { + protected class ProtectedClass + fun getProtectedClass(): KClass<*> = ProtectedClass::class +} +internal class InternalClass +private class PrivateClass + +fun box(): String { + assertEquals(KVisibility.PUBLIC, DefaultVisibilityClass::class.visibility) + assertEquals(KVisibility.PUBLIC, PublicClass::class.visibility) + assertEquals(KVisibility.PROTECTED, PublicClass().getProtectedClass().visibility) + assertEquals(KVisibility.INTERNAL, InternalClass::class.visibility) + assertEquals(KVisibility.PRIVATE, PrivateClass::class.visibility) + + class Local + assertEquals(null, Local::class.visibility) + + val anonymous = object {} + assertEquals(null, anonymous::class.visibility) + + return "OK" +} diff --git a/compiler/testData/codegen/box/reflection/modifiers/javaVisibility.kt b/compiler/testData/codegen/box/reflection/modifiers/javaVisibility.kt new file mode 100644 index 00000000000..d1831be7fee --- /dev/null +++ b/compiler/testData/codegen/box/reflection/modifiers/javaVisibility.kt @@ -0,0 +1,33 @@ +// WITH_REFLECT +// FILE: J.java + +class J { + protected class C {} + protected static class D {} + + void foo() {} + protected void bar() {} + protected static void baz() {} +} + +// FILE: K.kt + +import kotlin.test.assertEquals + +fun box(): String { + // Package-private class + assertEquals(null, J::class.visibility) + // Protected+package class + assertEquals(null, J.C::class.visibility) + // Protected static class + assertEquals(null, J.D::class.visibility) + + // Package-private method + assertEquals(null, J::foo.visibility) + // Protected+package method + assertEquals(null, J::bar.visibility) + // Protected static method + assertEquals(null, J::baz.visibility) + + return "OK" +} diff --git a/compiler/tests/org/jetbrains/kotlin/codegen/BlackBoxCodegenTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/codegen/BlackBoxCodegenTestGenerated.java index 23863912886..6349e48a3d9 100644 --- a/compiler/tests/org/jetbrains/kotlin/codegen/BlackBoxCodegenTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/codegen/BlackBoxCodegenTestGenerated.java @@ -12633,12 +12633,24 @@ public class BlackBoxCodegenTestGenerated extends AbstractBlackBoxCodegenTest { doTest(fileName); } + @TestMetadata("callableVisibility.kt") + public void testCallableVisibility() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/reflection/modifiers/callableVisibility.kt"); + doTest(fileName); + } + @TestMetadata("classModality.kt") public void testClassModality() throws Exception { String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/reflection/modifiers/classModality.kt"); doTest(fileName); } + @TestMetadata("classVisibility.kt") + public void testClassVisibility() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/reflection/modifiers/classVisibility.kt"); + doTest(fileName); + } + @TestMetadata("classes.kt") public void testClasses() throws Exception { String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/reflection/modifiers/classes.kt"); @@ -12651,6 +12663,12 @@ public class BlackBoxCodegenTestGenerated extends AbstractBlackBoxCodegenTest { doTest(fileName); } + @TestMetadata("javaVisibility.kt") + public void testJavaVisibility() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/reflection/modifiers/javaVisibility.kt"); + doTest(fileName); + } + @TestMetadata("parameters.kt") public void testParameters() throws Exception { String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/reflection/modifiers/parameters.kt"); diff --git a/core/builtins/src/kotlin/reflect/KCallable.kt b/core/builtins/src/kotlin/reflect/KCallable.kt index a22d9386a11..4f9510f4e62 100644 --- a/core/builtins/src/kotlin/reflect/KCallable.kt +++ b/core/builtins/src/kotlin/reflect/KCallable.kt @@ -63,6 +63,11 @@ public interface KCallable : KAnnotatedElement { */ public fun callBy(args: Map): R + /** + * Visibility of this callable, or `null` if its visibility cannot be represented in Kotlin. + */ + public val visibility: KVisibility? + /** * `true` if this callable is `final`. */ diff --git a/core/builtins/src/kotlin/reflect/KClass.kt b/core/builtins/src/kotlin/reflect/KClass.kt index 9cba0def335..46590f103f5 100644 --- a/core/builtins/src/kotlin/reflect/KClass.kt +++ b/core/builtins/src/kotlin/reflect/KClass.kt @@ -73,6 +73,11 @@ public interface KClass : KDeclarationContainer, KAnnotatedElement, KCl */ public val supertypes: List + /** + * Visibility of this class, or `null` if its visibility cannot be represented in Kotlin. + */ + public val visibility: KVisibility? + /** * `true` if this class is `final`. */ diff --git a/core/builtins/src/kotlin/reflect/KVisibility.kt b/core/builtins/src/kotlin/reflect/KVisibility.kt new file mode 100644 index 00000000000..031188e762b --- /dev/null +++ b/core/builtins/src/kotlin/reflect/KVisibility.kt @@ -0,0 +1,49 @@ +/* + * Copyright 2010-2016 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 + +/** + * Visibility is an aspect of a Kotlin declaration regulating where that declaration is accessible in the source code. + * Visibility can be changed with one of the following modifiers: `public`, `protected`, `internal`, `private`. + * + * Note that some Java visibilities such as package-private and protected (which also gives access to items from the same package) + * cannot be represented in Kotlin, so there's no [KVisibility] value corresponding to them. + * + * See the [Kotlin language documentation](https://kotlinlang.org/docs/reference/visibility-modifiers.html) + * for more information. + */ +enum class KVisibility { + /** + * Visibility of declarations marked with the `public` modifier, or with no modifier at all. + */ + PUBLIC, + + /** + * Visibility of declarations marked with the `protected` modifier. + */ + PROTECTED, + + /** + * Visibility of declarations marked with the `internal` modifier. + */ + INTERNAL, + + /** + * Visibility of declarations marked with the `private` modifier. + */ + PRIVATE, +} 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 d06d940943f..8a14a9679d6 100644 --- a/core/reflection.jvm/src/kotlin/reflect/jvm/internal/KCallableImpl.kt +++ b/core/reflection.jvm/src/kotlin/reflect/jvm/internal/KCallableImpl.kt @@ -63,6 +63,9 @@ internal interface KCallableImpl : KCallable, KAnnotatedElementImpl { override val typeParameters: List get() = descriptor.typeParameters.map(::KTypeParameterImpl) + override val visibility: KVisibility? + get() = descriptor.visibility.toKVisibility() + override val isFinal: Boolean get() = descriptor.modality == Modality.FINAL 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 7e3ca8646b4..b44d3d5b0b8 100644 --- a/core/reflection.jvm/src/kotlin/reflect/jvm/internal/KClassImpl.kt +++ b/core/reflection.jvm/src/kotlin/reflect/jvm/internal/KClassImpl.kt @@ -170,6 +170,9 @@ internal class KClassImpl(override val jClass: Class) : } } + override val visibility: KVisibility? + get() = descriptor.visibility.toKVisibility() + override val isFinal: Boolean get() = descriptor.modality == Modality.FINAL diff --git a/core/reflection.jvm/src/kotlin/reflect/jvm/internal/util.kt b/core/reflection.jvm/src/kotlin/reflect/jvm/internal/util.kt index b79f52b6968..9a9f0d5c43d 100644 --- a/core/reflection.jvm/src/kotlin/reflect/jvm/internal/util.kt +++ b/core/reflection.jvm/src/kotlin/reflect/jvm/internal/util.kt @@ -17,6 +17,8 @@ package kotlin.reflect.jvm.internal import org.jetbrains.kotlin.descriptors.ClassDescriptor +import org.jetbrains.kotlin.descriptors.Visibilities +import org.jetbrains.kotlin.descriptors.Visibility import org.jetbrains.kotlin.load.java.components.RuntimeSourceElementFactory import org.jetbrains.kotlin.load.java.reflect.tryLoadClass import org.jetbrains.kotlin.load.java.structure.reflect.ReflectJavaClass @@ -30,6 +32,7 @@ import org.jetbrains.kotlin.resolve.descriptorUtil.classId import kotlin.jvm.internal.FunctionReference import kotlin.jvm.internal.PropertyReference import kotlin.reflect.IllegalCallableAccessException +import kotlin.reflect.KVisibility internal val JVM_STATIC = FqName("kotlin.jvm.JvmStatic") @@ -73,6 +76,15 @@ internal fun loadClass(classLoader: ClassLoader, packageName: String, className: return classLoader.tryLoadClass("$packageName.${className.replace('.', '$')}") } +internal fun Visibility.toKVisibility(): KVisibility? = + when (this) { + Visibilities.PUBLIC -> KVisibility.PUBLIC + Visibilities.PROTECTED -> KVisibility.PROTECTED + Visibilities.INTERNAL -> KVisibility.INTERNAL + Visibilities.PRIVATE, Visibilities.PRIVATE_TO_THIS -> KVisibility.PRIVATE + else -> null + } + // TODO: wrap other exceptions internal inline fun reflectionCall(block: () -> R): R = try { diff --git a/core/runtime.jvm/src/kotlin/jvm/internal/CallableReference.java b/core/runtime.jvm/src/kotlin/jvm/internal/CallableReference.java index d4914f31145..1e493b8a609 100644 --- a/core/runtime.jvm/src/kotlin/jvm/internal/CallableReference.java +++ b/core/runtime.jvm/src/kotlin/jvm/internal/CallableReference.java @@ -19,6 +19,7 @@ package kotlin.jvm.internal; import kotlin.jvm.KotlinReflectionNotSupportedError; import kotlin.reflect.*; import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; import java.lang.annotation.Annotation; import java.util.List; @@ -99,6 +100,12 @@ public abstract class CallableReference implements KCallable { return getReflected().callBy(args); } + @Nullable + @Override + public KVisibility getVisibility() { + return getReflected().getVisibility(); + } + @Override public boolean isFinal() { return getReflected().isFinal(); diff --git a/core/runtime.jvm/src/kotlin/jvm/internal/ClassReference.kt b/core/runtime.jvm/src/kotlin/jvm/internal/ClassReference.kt index fd4e3de987a..fa9a750e150 100644 --- a/core/runtime.jvm/src/kotlin/jvm/internal/ClassReference.kt +++ b/core/runtime.jvm/src/kotlin/jvm/internal/ClassReference.kt @@ -48,6 +48,9 @@ class ClassReference(override val jClass: Class<*>) : KClass, ClassBasedDec override val supertypes: List get() = error() + override val visibility: KVisibility? + get() = error() + override val isFinal: Boolean get() = error() diff --git a/core/runtime.jvm/src/kotlin/jvm/internal/FunctionReference.java b/core/runtime.jvm/src/kotlin/jvm/internal/FunctionReference.java index 19c0dab1471..771c26c17a8 100644 --- a/core/runtime.jvm/src/kotlin/jvm/internal/FunctionReference.java +++ b/core/runtime.jvm/src/kotlin/jvm/internal/FunctionReference.java @@ -19,6 +19,7 @@ package kotlin.jvm.internal; import kotlin.jvm.KotlinReflectionNotSupportedError; import kotlin.reflect.*; import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; import java.lang.annotation.Annotation; import java.util.List; @@ -84,6 +85,12 @@ public class FunctionReference extends FunctionImpl implements KFunction { return getReflected().callBy(args); } + @Nullable + @Override + public KVisibility getVisibility() { + return getReflected().getVisibility(); + } + @Override public boolean isFinal() { return getReflected().isFinal();