From da209e673cc976ae50adabd73d1fafe6260f01fe Mon Sep 17 00:00:00 2001 From: Alexander Udalov Date: Fri, 20 Feb 2015 19:40:47 +0300 Subject: [PATCH] Implement KClass.getProperties() #KT-6570 In Progress --- .../callPrivatePropertyFromGetProperties.kt | 19 +++++++++++ .../properties/fakeOverridesInSubclass.kt | 12 +++++++ .../getPropertiesMutableVsReadonly.kt | 20 +++++++++++ .../properties/simpleGetProperties.kt | 23 +++++++++++++ ...lackBoxWithStdlibCodegenTestGenerated.java | 34 +++++++++++++++++++ .../kotlin/reflect/jvm/internal/KClassImpl.kt | 14 ++++++++ core/reflection/src/kotlin/reflect/KClass.kt | 4 ++- 7 files changed, 125 insertions(+), 1 deletion(-) create mode 100644 compiler/testData/codegen/boxWithStdlib/reflection/properties/callPrivatePropertyFromGetProperties.kt create mode 100644 compiler/testData/codegen/boxWithStdlib/reflection/properties/fakeOverridesInSubclass.kt create mode 100644 compiler/testData/codegen/boxWithStdlib/reflection/properties/getPropertiesMutableVsReadonly.kt create mode 100644 compiler/testData/codegen/boxWithStdlib/reflection/properties/simpleGetProperties.kt diff --git a/compiler/testData/codegen/boxWithStdlib/reflection/properties/callPrivatePropertyFromGetProperties.kt b/compiler/testData/codegen/boxWithStdlib/reflection/properties/callPrivatePropertyFromGetProperties.kt new file mode 100644 index 00000000000..8a18c441f61 --- /dev/null +++ b/compiler/testData/codegen/boxWithStdlib/reflection/properties/callPrivatePropertyFromGetProperties.kt @@ -0,0 +1,19 @@ +import kotlin.reflect.* +import kotlin.reflect.jvm.* + +class K(private val value: String) + +fun box(): String { + val p = javaClass().kotlin.getProperties().single() as KMemberProperty + + try { + return p.get(K("Fail: private property should not be accessible by default")) + } + catch (e: IllegalPropertyAccessException) { + // OK + } + + p.accessible = true + + return p.get(K("OK")) +} diff --git a/compiler/testData/codegen/boxWithStdlib/reflection/properties/fakeOverridesInSubclass.kt b/compiler/testData/codegen/boxWithStdlib/reflection/properties/fakeOverridesInSubclass.kt new file mode 100644 index 00000000000..fad0ee62bd3 --- /dev/null +++ b/compiler/testData/codegen/boxWithStdlib/reflection/properties/fakeOverridesInSubclass.kt @@ -0,0 +1,12 @@ +import kotlin.reflect.jvm.* +import kotlin.test.* + +open class Super(val r: String) + +class Sub(r: String) : Super(r) + +fun box(): String { + val props = javaClass().kotlin.getProperties() + assertEquals(listOf("r"), props.map { it.name }) + return props.single().get(Sub("OK")).toString() +} diff --git a/compiler/testData/codegen/boxWithStdlib/reflection/properties/getPropertiesMutableVsReadonly.kt b/compiler/testData/codegen/boxWithStdlib/reflection/properties/getPropertiesMutableVsReadonly.kt new file mode 100644 index 00000000000..74b464d6f72 --- /dev/null +++ b/compiler/testData/codegen/boxWithStdlib/reflection/properties/getPropertiesMutableVsReadonly.kt @@ -0,0 +1,20 @@ +import kotlin.reflect.jvm.kotlin +import kotlin.reflect.KMutableMemberProperty + +class A(val readonly: String) { + var mutable: String = "before" +} + +fun box(): String { + val props = javaClass().kotlin.getProperties() + val readonly = props.single { it.name == "readonly" } + assert(readonly !is KMutableMemberProperty) { "Fail 1: $readonly" } + val mutable = props.single { it.name == "mutable" } + assert(mutable is KMutableMemberProperty) { "Fail 2: $mutable" } + + val a = A("") + mutable as KMutableMemberProperty + assert(mutable[a] == "before") { "Fail 3: ${mutable.get(a)}" } + mutable[a] = "OK" + return mutable.get(a) +} diff --git a/compiler/testData/codegen/boxWithStdlib/reflection/properties/simpleGetProperties.kt b/compiler/testData/codegen/boxWithStdlib/reflection/properties/simpleGetProperties.kt new file mode 100644 index 00000000000..6ddfd38390b --- /dev/null +++ b/compiler/testData/codegen/boxWithStdlib/reflection/properties/simpleGetProperties.kt @@ -0,0 +1,23 @@ +import kotlin.reflect.jvm.kotlin + +class A(param: String) { + val int: Int get() = 42 + val string: String = param + var anyVar: Any? = null + + val List.extensionToList: Unit get() {} + + fun notAProperty() {} +} + +fun box(): String { + val klass = javaClass().kotlin + + val props = klass.getProperties() + + val names = props.map { it.name }.toSortedList() + assert(names == listOf("anyVar", "int", "string")) { "Fail names: $props" } + + val stringProp = props.firstOrNull { it.name == "string" } ?: return "Fail, string not found: $props" + return stringProp.get(A("OK")) as String +} diff --git a/compiler/tests/org/jetbrains/kotlin/codegen/generated/BlackBoxWithStdlibCodegenTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/codegen/generated/BlackBoxWithStdlibCodegenTestGenerated.java index 9c8d1477e79..9173b0c387a 100644 --- a/compiler/tests/org/jetbrains/kotlin/codegen/generated/BlackBoxWithStdlibCodegenTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/codegen/generated/BlackBoxWithStdlibCodegenTestGenerated.java @@ -2528,6 +2528,7 @@ public class BlackBoxWithStdlibCodegenTestGenerated extends AbstractBlackBoxCode Reflection.GenericSignature.class, Reflection.Mapping.class, Reflection.MethodsFromAny.class, + Reflection.Properties.class, }) @RunWith(JUnit3RunnerWithInners.class) public static class Reflection extends AbstractBlackBoxCodegenTest { @@ -2780,6 +2781,39 @@ public class BlackBoxWithStdlibCodegenTestGenerated extends AbstractBlackBoxCode doTestWithStdlib(fileName); } } + + @TestMetadata("compiler/testData/codegen/boxWithStdlib/reflection/properties") + @TestDataPath("$PROJECT_ROOT") + @RunWith(JUnit3RunnerWithInners.class) + public static class Properties extends AbstractBlackBoxCodegenTest { + public void testAllFilesPresentInProperties() throws Exception { + JetTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("compiler/testData/codegen/boxWithStdlib/reflection/properties"), Pattern.compile("^(.+)\\.kt$"), true); + } + + @TestMetadata("callPrivatePropertyFromGetProperties.kt") + public void testCallPrivatePropertyFromGetProperties() throws Exception { + String fileName = JetTestUtils.navigationMetadata("compiler/testData/codegen/boxWithStdlib/reflection/properties/callPrivatePropertyFromGetProperties.kt"); + doTestWithStdlib(fileName); + } + + @TestMetadata("fakeOverridesInSubclass.kt") + public void testFakeOverridesInSubclass() throws Exception { + String fileName = JetTestUtils.navigationMetadata("compiler/testData/codegen/boxWithStdlib/reflection/properties/fakeOverridesInSubclass.kt"); + doTestWithStdlib(fileName); + } + + @TestMetadata("getPropertiesMutableVsReadonly.kt") + public void testGetPropertiesMutableVsReadonly() throws Exception { + String fileName = JetTestUtils.navigationMetadata("compiler/testData/codegen/boxWithStdlib/reflection/properties/getPropertiesMutableVsReadonly.kt"); + doTestWithStdlib(fileName); + } + + @TestMetadata("simpleGetProperties.kt") + public void testSimpleGetProperties() throws Exception { + String fileName = JetTestUtils.navigationMetadata("compiler/testData/codegen/boxWithStdlib/reflection/properties/simpleGetProperties.kt"); + doTestWithStdlib(fileName); + } + } } @TestMetadata("compiler/testData/codegen/boxWithStdlib/regressions") 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 a13c8c961ac..c88cb4ac509 100644 --- a/core/reflection.jvm/src/kotlin/reflect/jvm/internal/KClassImpl.kt +++ b/core/reflection.jvm/src/kotlin/reflect/jvm/internal/KClassImpl.kt @@ -16,6 +16,7 @@ package kotlin.reflect.jvm.internal +import org.jetbrains.kotlin.descriptors.PropertyDescriptor import org.jetbrains.kotlin.resolve.scopes.JetScope import org.jetbrains.kotlin.serialization.deserialization.findClassAcrossModuleDependencies import kotlin.reflect.KClass @@ -40,6 +41,19 @@ class KClassImpl(override val jClass: Class) : KCallableContainerImpl(), K override val scope: JetScope get() = descriptor.getDefaultType().getMemberScope() + override fun getProperties(): Collection> { + return scope.getAllDescriptors().stream() + .filterIsInstance() + .filter { descriptor -> + descriptor.getExtensionReceiverParameter() == null + } + .map { descriptor -> + if (descriptor.isVar()) KMutableMemberPropertyImpl(this) { descriptor } + else KMemberPropertyImpl(this) { descriptor } + } + .toList() + } + fun memberProperty(name: String): KMemberProperty { return KMemberPropertyImpl(this, findPropertyDescriptor(name)) } diff --git a/core/reflection/src/kotlin/reflect/KClass.kt b/core/reflection/src/kotlin/reflect/KClass.kt index 2cff307af83..296b272cfc9 100644 --- a/core/reflection/src/kotlin/reflect/KClass.kt +++ b/core/reflection/src/kotlin/reflect/KClass.kt @@ -16,4 +16,6 @@ package kotlin.reflect -public trait KClass +public trait KClass { + public fun getProperties(): Collection> +}