From e7f19c531aa98892181353ab165457f58814dee6 Mon Sep 17 00:00:00 2001 From: Alexander Udalov Date: Thu, 5 Jun 2014 19:15:09 +0400 Subject: [PATCH] Support 'accessible' for reflected properties on JVM Calls Java reflection's isAccessible/setAccessible --- .../property/privateClassVal.kt | 14 +++++- .../property/privateClassVar.kt | 29 +++++++++++ .../property/protectedClassVar.kt | 28 +++++++++++ .../property/publicClassValAccessible.kt | 12 +++++ ...lackBoxWithStdlibCodegenTestGenerated.java | 15 ++++++ .../jvm/internal/KMemberPropertyImpl.kt | 6 +-- .../src/kotlin/reflect/jvm/properties.kt | 50 +++++++++++++++++++ 7 files changed, 150 insertions(+), 4 deletions(-) create mode 100644 compiler/testData/codegen/boxWithStdlib/callableReference/property/privateClassVar.kt create mode 100644 compiler/testData/codegen/boxWithStdlib/callableReference/property/protectedClassVar.kt create mode 100644 compiler/testData/codegen/boxWithStdlib/callableReference/property/publicClassValAccessible.kt create mode 100644 core/runtime.jvm/src/kotlin/reflect/jvm/properties.kt diff --git a/compiler/testData/codegen/boxWithStdlib/callableReference/property/privateClassVal.kt b/compiler/testData/codegen/boxWithStdlib/callableReference/property/privateClassVal.kt index eaafec45535..21971bd98a0 100644 --- a/compiler/testData/codegen/boxWithStdlib/callableReference/property/privateClassVal.kt +++ b/compiler/testData/codegen/boxWithStdlib/callableReference/property/privateClassVal.kt @@ -1,3 +1,5 @@ +import kotlin.reflect.jvm.accessible + class Result { private val value = "OK" @@ -11,5 +13,15 @@ fun box(): String { return "Fail: private property is accessible by default" } catch(e: IllegalAccessException) { } - return "OK" + p.accessible = true + + val r = p.get(Result()) + + p.accessible = false + try { + p.get(Result()) + return "Fail: setAccessible(false) had no effect" + } catch(e: IllegalAccessException) { } + + return r } diff --git a/compiler/testData/codegen/boxWithStdlib/callableReference/property/privateClassVar.kt b/compiler/testData/codegen/boxWithStdlib/callableReference/property/privateClassVar.kt new file mode 100644 index 00000000000..25d98f9e51a --- /dev/null +++ b/compiler/testData/codegen/boxWithStdlib/callableReference/property/privateClassVar.kt @@ -0,0 +1,29 @@ +import kotlin.reflect.jvm.accessible + +class A { + private var value = 0 + + fun ref(): KMutableMemberProperty = ::value +} + +fun box(): String { + val a = A() + val p = a.ref() + try { + p.set(a, 1) + return "Fail: private property is accessible by default" + } catch(e: IllegalAccessException) { } + + p.accessible = true + + p.set(a, 2) + p.get(a) + + p.accessible = false + try { + p.set(a, 3) + return "Fail: setAccessible(false) had no effect" + } catch(e: IllegalAccessException) { } + + return "OK" +} diff --git a/compiler/testData/codegen/boxWithStdlib/callableReference/property/protectedClassVar.kt b/compiler/testData/codegen/boxWithStdlib/callableReference/property/protectedClassVar.kt new file mode 100644 index 00000000000..112038072a4 --- /dev/null +++ b/compiler/testData/codegen/boxWithStdlib/callableReference/property/protectedClassVar.kt @@ -0,0 +1,28 @@ +import kotlin.reflect.jvm.accessible + +class A(param: String) { + protected var v: String = param + + fun ref() = ::v +} + +fun box(): String { + val a = A(":(") + val f = a.ref() + + try { + f.get(a) + return "Fail: protected property getter is accessible by default" + } catch (e: IllegalAccessException) { } + + try { + f.set(a, ":D") + return "Fail: protected property setter is accessible by default" + } catch (e: IllegalAccessException) { } + + f.accessible = true + + f.set(a, ":)") + + return if (f[a] != ":)") "Fail: ${f[a]}" else "OK" +} diff --git a/compiler/testData/codegen/boxWithStdlib/callableReference/property/publicClassValAccessible.kt b/compiler/testData/codegen/boxWithStdlib/callableReference/property/publicClassValAccessible.kt new file mode 100644 index 00000000000..3fe95c7c7e0 --- /dev/null +++ b/compiler/testData/codegen/boxWithStdlib/callableReference/property/publicClassValAccessible.kt @@ -0,0 +1,12 @@ +import kotlin.reflect.jvm.accessible + +class Result { + public val value: String = "OK" +} + +fun box(): String { + val p = Result::value + p.accessible = false + // setAccessible(false) should have no effect on the accessibility of a public reflection object + return p.get(Result()) +} diff --git a/compiler/tests/org/jetbrains/jet/codegen/generated/BlackBoxWithStdlibCodegenTestGenerated.java b/compiler/tests/org/jetbrains/jet/codegen/generated/BlackBoxWithStdlibCodegenTestGenerated.java index 9080f40c293..cbdf3206a6a 100644 --- a/compiler/tests/org/jetbrains/jet/codegen/generated/BlackBoxWithStdlibCodegenTestGenerated.java +++ b/compiler/tests/org/jetbrains/jet/codegen/generated/BlackBoxWithStdlibCodegenTestGenerated.java @@ -459,6 +459,21 @@ public class BlackBoxWithStdlibCodegenTestGenerated extends AbstractBlackBoxCode doTestWithStdlib("compiler/testData/codegen/boxWithStdlib/callableReference/property/privateClassVal.kt"); } + @TestMetadata("privateClassVar.kt") + public void testPrivateClassVar() throws Exception { + doTestWithStdlib("compiler/testData/codegen/boxWithStdlib/callableReference/property/privateClassVar.kt"); + } + + @TestMetadata("protectedClassVar.kt") + public void testProtectedClassVar() throws Exception { + doTestWithStdlib("compiler/testData/codegen/boxWithStdlib/callableReference/property/protectedClassVar.kt"); + } + + @TestMetadata("publicClassValAccessible.kt") + public void testPublicClassValAccessible() throws Exception { + doTestWithStdlib("compiler/testData/codegen/boxWithStdlib/callableReference/property/publicClassValAccessible.kt"); + } + @TestMetadata("simpleExtension.kt") public void testSimpleExtension() throws Exception { doTestWithStdlib("compiler/testData/codegen/boxWithStdlib/callableReference/property/simpleExtension.kt"); diff --git a/core/runtime.jvm/src/kotlin/reflect/jvm/internal/KMemberPropertyImpl.kt b/core/runtime.jvm/src/kotlin/reflect/jvm/internal/KMemberPropertyImpl.kt index 482a274f0c6..86ad94691aa 100644 --- a/core/runtime.jvm/src/kotlin/reflect/jvm/internal/KMemberPropertyImpl.kt +++ b/core/runtime.jvm/src/kotlin/reflect/jvm/internal/KMemberPropertyImpl.kt @@ -23,14 +23,14 @@ open class KMemberPropertyImpl( name: String, protected val owner: KClassImpl ) : KMemberProperty, KPropertyImpl(name) { - protected val field: Field? = + val field: Field? = if (owner.origin == KClassOrigin.FOREIGN) { owner.jClass.getField(name) } else null // TODO: extract, make lazy (weak?), use our descriptors knowledge - protected val getter: Method? = + val getter: Method? = if (owner.origin == KClassOrigin.KOTLIN) { owner.jClass.getMaybeDeclaredMethod(getterName(name)) } @@ -49,7 +49,7 @@ class KMutableMemberPropertyImpl( name: String, owner: KClassImpl ) : KMutableMemberProperty, KMemberPropertyImpl(name, owner) { - private val setter: Method? = + val setter: Method? = if (owner.origin == KClassOrigin.KOTLIN) { owner.jClass.getMaybeDeclaredMethod(setterName(name), getter!!.getReturnType()!!) } diff --git a/core/runtime.jvm/src/kotlin/reflect/jvm/properties.kt b/core/runtime.jvm/src/kotlin/reflect/jvm/properties.kt new file mode 100644 index 00000000000..e7e4abdd452 --- /dev/null +++ b/core/runtime.jvm/src/kotlin/reflect/jvm/properties.kt @@ -0,0 +1,50 @@ +/* + * Copyright 2010-2014 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 + +import kotlin.reflect.jvm.internal.KMemberPropertyImpl +import kotlin.reflect.jvm.internal.KMutableMemberPropertyImpl + +public var KProperty.accessible: Boolean + get() { + return when (this) { + is KMutableMemberPropertyImpl<*, R> -> + field?.isAccessible() ?: true && + getter?.isAccessible() ?: true && + setter?.isAccessible() ?: true + is KMemberPropertyImpl<*, R> -> + field?.isAccessible() ?: true && + getter?.isAccessible() ?: true + else -> { + // Non-member properties always have public visibility on JVM, thus accessible has no effect on them + true + } + } + } + set(value) { + when (this) { + is KMutableMemberPropertyImpl<*, R> -> { + field?.setAccessible(value) + getter?.setAccessible(value) + setter?.setAccessible(value) + } + is KMemberPropertyImpl<*, R> -> { + field?.setAccessible(value) + getter?.setAccessible(value) + } + } + }