diff --git a/compiler/testData/codegen/boxWithStdlib/reflection/properties/accessors/accessorNames.kt b/compiler/testData/codegen/boxWithStdlib/reflection/properties/accessors/accessorNames.kt new file mode 100644 index 00000000000..fdb3103603b --- /dev/null +++ b/compiler/testData/codegen/boxWithStdlib/reflection/properties/accessors/accessorNames.kt @@ -0,0 +1,30 @@ +import kotlin.reflect.* +import kotlin.test.assertEquals + +var foo = "" +var String.bar: String + get() = this + set(value) {} + +class A(var baz: Int) { + var String.quux: String + get() = this + set(value) {} +} + +fun box(): String { + assertEquals("", ::foo.getter.name) + assertEquals("", ::foo.setter.name) + + assertEquals("", String::bar.getter.name) + assertEquals("", String::bar.setter.name) + + assertEquals("", A::baz.getter.name) + assertEquals("", A::baz.setter.name) + + val me = A::class.extensionProperties.single() as KMutableProperty2 + assertEquals("", me.getter.name) + assertEquals("", me.setter.name) + + return "OK" +} diff --git a/compiler/testData/codegen/boxWithStdlib/reflection/properties/accessors/extensionPropertyAccessors.kt b/compiler/testData/codegen/boxWithStdlib/reflection/properties/accessors/extensionPropertyAccessors.kt new file mode 100644 index 00000000000..42cbcc74e80 --- /dev/null +++ b/compiler/testData/codegen/boxWithStdlib/reflection/properties/accessors/extensionPropertyAccessors.kt @@ -0,0 +1,18 @@ +import kotlin.test.assertEquals + +var state: String = "" + +var String.prop: String + get() = length().toString() + set(value) { state = this + value } + +fun box(): String { + val prop = String::prop + + assertEquals("3", prop.getter.invoke("abc")) + assertEquals("5", prop.getter("defgh")) + + prop.setter("O", "K") + + return state +} diff --git a/compiler/testData/codegen/boxWithStdlib/reflection/properties/accessors/memberExtensions.kt b/compiler/testData/codegen/boxWithStdlib/reflection/properties/accessors/memberExtensions.kt new file mode 100644 index 00000000000..fcd53d24ef5 --- /dev/null +++ b/compiler/testData/codegen/boxWithStdlib/reflection/properties/accessors/memberExtensions.kt @@ -0,0 +1,20 @@ +import kotlin.reflect.KMutableProperty2 +import kotlin.test.assertEquals + +class C(var state: String) { + var String.prop: String + get() = length().toString() + set(value) { state = this + value } +} + +fun box(): String { + val prop = C::class.extensionProperties.single() as KMutableProperty2 + + val c = C("") + assertEquals("3", prop.getter.invoke(c, "abc")) + assertEquals("1", prop.getter(c, "d")) + + prop.setter(c, "O", "K") + + return c.state +} diff --git a/compiler/testData/codegen/boxWithStdlib/reflection/properties/accessors/memberPropertyAccessors.kt b/compiler/testData/codegen/boxWithStdlib/reflection/properties/accessors/memberPropertyAccessors.kt new file mode 100644 index 00000000000..dcf3c560b42 --- /dev/null +++ b/compiler/testData/codegen/boxWithStdlib/reflection/properties/accessors/memberPropertyAccessors.kt @@ -0,0 +1,15 @@ +import kotlin.test.assertEquals + +class C(var state: String) + +fun box(): String { + val prop = C::state + + val c = C("1") + assertEquals("1", prop.getter.invoke(c)) + assertEquals("1", prop.getter(c)) + + prop.setter(c, "OK") + + return prop.get(c) +} diff --git a/compiler/testData/codegen/boxWithStdlib/reflection/properties/accessors/topLevelPropertyAccessors.kt b/compiler/testData/codegen/boxWithStdlib/reflection/properties/accessors/topLevelPropertyAccessors.kt new file mode 100644 index 00000000000..49ab1f5ef8e --- /dev/null +++ b/compiler/testData/codegen/boxWithStdlib/reflection/properties/accessors/topLevelPropertyAccessors.kt @@ -0,0 +1,14 @@ +import kotlin.test.assertEquals + +var state: String = "" + +fun box(): String { + val prop = ::state + + assertEquals("", prop.getter.invoke()) + assertEquals("", prop.getter()) + + prop.setter("OK") + + return prop.get() +} diff --git a/compiler/tests/org/jetbrains/kotlin/codegen/generated/BlackBoxWithStdlibCodegenTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/codegen/generated/BlackBoxWithStdlibCodegenTestGenerated.java index 561b9c1c6ea..455bd514b20 100644 --- a/compiler/tests/org/jetbrains/kotlin/codegen/generated/BlackBoxWithStdlibCodegenTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/codegen/generated/BlackBoxWithStdlibCodegenTestGenerated.java @@ -3251,6 +3251,45 @@ public class BlackBoxWithStdlibCodegenTestGenerated extends AbstractBlackBoxCode String fileName = JetTestUtils.navigationMetadata("compiler/testData/codegen/boxWithStdlib/reflection/properties/simpleGetProperties.kt"); doTestWithStdlib(fileName); } + + @TestMetadata("compiler/testData/codegen/boxWithStdlib/reflection/properties/accessors") + @TestDataPath("$PROJECT_ROOT") + @RunWith(JUnit3RunnerWithInners.class) + public static class Accessors extends AbstractBlackBoxCodegenTest { + @TestMetadata("accessorNames.kt") + public void testAccessorNames() throws Exception { + String fileName = JetTestUtils.navigationMetadata("compiler/testData/codegen/boxWithStdlib/reflection/properties/accessors/accessorNames.kt"); + doTestWithStdlib(fileName); + } + + public void testAllFilesPresentInAccessors() throws Exception { + JetTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("compiler/testData/codegen/boxWithStdlib/reflection/properties/accessors"), Pattern.compile("^(.+)\\.kt$"), true); + } + + @TestMetadata("extensionPropertyAccessors.kt") + public void testExtensionPropertyAccessors() throws Exception { + String fileName = JetTestUtils.navigationMetadata("compiler/testData/codegen/boxWithStdlib/reflection/properties/accessors/extensionPropertyAccessors.kt"); + doTestWithStdlib(fileName); + } + + @TestMetadata("memberExtensions.kt") + public void testMemberExtensions() throws Exception { + String fileName = JetTestUtils.navigationMetadata("compiler/testData/codegen/boxWithStdlib/reflection/properties/accessors/memberExtensions.kt"); + doTestWithStdlib(fileName); + } + + @TestMetadata("memberPropertyAccessors.kt") + public void testMemberPropertyAccessors() throws Exception { + String fileName = JetTestUtils.navigationMetadata("compiler/testData/codegen/boxWithStdlib/reflection/properties/accessors/memberPropertyAccessors.kt"); + doTestWithStdlib(fileName); + } + + @TestMetadata("topLevelPropertyAccessors.kt") + public void testTopLevelPropertyAccessors() throws Exception { + String fileName = JetTestUtils.navigationMetadata("compiler/testData/codegen/boxWithStdlib/reflection/properties/accessors/topLevelPropertyAccessors.kt"); + doTestWithStdlib(fileName); + } + } } } diff --git a/core/builtins/src/kotlin/reflect/KCallable.kt b/core/builtins/src/kotlin/reflect/KCallable.kt index 554d3d46fbb..b5e23954ab4 100644 --- a/core/builtins/src/kotlin/reflect/KCallable.kt +++ b/core/builtins/src/kotlin/reflect/KCallable.kt @@ -24,6 +24,11 @@ package kotlin.reflect public interface KCallable { /** * 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. + * Nameless callables include: + * - constructors have the name "", + * - property accessors: the getter for a property named "foo" will have the name "", + * the setter, similarly, will have the name "". */ public val name: String } diff --git a/core/builtins/src/kotlin/reflect/KProperty.kt b/core/builtins/src/kotlin/reflect/KProperty.kt index b068eff3f55..32cca5e069d 100644 --- a/core/builtins/src/kotlin/reflect/KProperty.kt +++ b/core/builtins/src/kotlin/reflect/KProperty.kt @@ -24,12 +24,40 @@ package kotlin.reflect * * @param R the type of the property. */ -public interface KProperty : KCallable +public interface KProperty : KCallable { + /** The getter of this property, used to obtain the value of the property. */ + public val getter: Getter + + /** + * Represents a property accessor, which is a `get` or `set` method declared alongside the property. + * See the [Kotlin language documentation](http://kotlinlang.org/docs/reference/properties.html#getters-and-setters) + * for more information. + * + * @param R the type of the property, which it is an accessor of. + */ + public interface Accessor { + /** The property which this accessor is originated from. */ + public val property: KProperty + } + + /** + * Getter of the property is a `get` method declared alongside the property. + */ + public interface Getter : Accessor, KFunction +} /** * Represents a property declared as a `var`. */ -public interface KMutableProperty : KProperty +public interface KMutableProperty : KProperty { + /** The setter of this mutable property, used to change the value of the property. */ + public val setter: Setter + + /** + * Setter of the property is a `set` method declared alongside the property. + */ + public interface Setter : KProperty.Accessor, KFunction +} /** @@ -42,6 +70,10 @@ public interface KProperty0 : KProperty { * Returns the current value of the property. */ public fun get(): R + + override val getter: Getter + + public interface Getter : KProperty.Getter, () -> R } /** @@ -54,6 +86,10 @@ public interface KMutableProperty0 : KProperty0, KMutableProperty { * @param value the new value to be assigned to this property. */ public fun set(value: R) + + override val setter: Setter + + public interface Setter : KMutableProperty.Setter, (R) -> Unit } @@ -72,6 +108,10 @@ public interface KProperty1 : KProperty { * or an extension receiver if this is a top level extension property. */ public fun get(receiver: T): R + + override val getter: Getter + + public interface Getter : KProperty.Getter, (T) -> R } /** @@ -87,6 +127,10 @@ public interface KMutableProperty1 : KProperty1, KMutableProperty * @param value the new value to be assigned to this property. */ public fun set(receiver: T, value: R) + + override val setter: Setter + + public interface Setter : KMutableProperty.Setter, (T, R) -> Unit } @@ -109,6 +153,10 @@ public interface KProperty2 : KProperty { * @param receiver2 the instance of the second receiver. */ public fun get(receiver1: D, receiver2: E): R + + override val getter: Getter + + public interface Getter : KProperty.Getter, (D, E) -> R } /** @@ -123,4 +171,8 @@ public interface KMutableProperty2 : KProperty2, KMutablePrope * @param value the new value to be assigned to this property. */ public fun set(receiver1: D, receiver2: E, value: R) + + override val setter: Setter + + public interface Setter : KMutableProperty.Setter, (D, E, R) -> Unit } diff --git a/core/reflection.jvm/src/kotlin/reflect/jvm/internal/KProperty0Impl.kt b/core/reflection.jvm/src/kotlin/reflect/jvm/internal/KProperty0Impl.kt index f67e5b5c04e..2877a9111ed 100644 --- a/core/reflection.jvm/src/kotlin/reflect/jvm/internal/KProperty0Impl.kt +++ b/core/reflection.jvm/src/kotlin/reflect/jvm/internal/KProperty0Impl.kt @@ -28,6 +28,8 @@ open class KProperty0Impl : DescriptorBasedProperty, KProperty0, KProp override val name: String get() = descriptor.getName().asString() + override val getter by ReflectProperties.lazy { Getter(this) } + override val javaGetter: Method get() = super.javaGetter!! override fun get(): R { @@ -39,11 +41,17 @@ open class KProperty0Impl : DescriptorBasedProperty, KProperty0, KProp throw IllegalPropertyAccessException(e) } } + + class Getter(override val property: KProperty0Impl) : KPropertyImpl.Getter, KProperty0.Getter { + override fun invoke(): R = property.get() + } } open class KMutableProperty0Impl : KProperty0Impl, KMutableProperty0, KMutablePropertyImpl { constructor(container: KCallableContainerImpl, name: String, signature: String) : super(container, name, signature) + override val setter by ReflectProperties.lazy { Setter(this) } + override val javaSetter: Method get() = super.javaSetter!! override fun set(value: R) { @@ -54,6 +62,10 @@ open class KMutableProperty0Impl : KProperty0Impl, KMutableProperty0, K throw IllegalPropertyAccessException(e) } } + + class Setter(override val property: KMutableProperty0Impl) : KMutablePropertyImpl.Setter, KMutableProperty0.Setter { + override fun invoke(value: R): Unit = property.set(value) + } } diff --git a/core/reflection.jvm/src/kotlin/reflect/jvm/internal/KProperty1Impl.kt b/core/reflection.jvm/src/kotlin/reflect/jvm/internal/KProperty1Impl.kt index b42441dc27f..a4bde8fcfd9 100644 --- a/core/reflection.jvm/src/kotlin/reflect/jvm/internal/KProperty1Impl.kt +++ b/core/reflection.jvm/src/kotlin/reflect/jvm/internal/KProperty1Impl.kt @@ -18,11 +18,11 @@ package kotlin.reflect.jvm.internal import org.jetbrains.kotlin.descriptors.PropertyDescriptor import java.lang.reflect.Modifier +import kotlin.jvm.internal.MutablePropertyReference1 +import kotlin.jvm.internal.PropertyReference1 import kotlin.reflect.IllegalPropertyAccessException import kotlin.reflect.KMutableProperty1 import kotlin.reflect.KProperty1 -import kotlin.jvm.internal.MutablePropertyReference1 -import kotlin.jvm.internal.PropertyReference1 open class KProperty1Impl : DescriptorBasedProperty, KProperty1, KPropertyImpl { constructor(container: KCallableContainerImpl, name: String, signature: String) : super(container, name, signature) @@ -31,6 +31,8 @@ open class KProperty1Impl : DescriptorBasedProperty, KProperty1, override val name: String get() = descriptor.getName().asString() + override val getter by ReflectProperties.lazy { Getter(this) } + // TODO: consider optimizing this, not to do complex checks on every access @suppress("UNCHECKED_CAST") override fun get(receiver: T): R { @@ -53,6 +55,10 @@ open class KProperty1Impl : DescriptorBasedProperty, KProperty1, throw IllegalPropertyAccessException(e) } } + + class Getter(override val property: KProperty1Impl) : KPropertyImpl.Getter, KProperty1.Getter { + override fun invoke(receiver: T): R = property.get(receiver) + } } @@ -61,6 +67,8 @@ open class KMutableProperty1Impl : KProperty1Impl, KMutableProperty1 constructor(container: KCallableContainerImpl, descriptor: PropertyDescriptor) : super(container, descriptor) + override val setter by ReflectProperties.lazy { Setter(this) } + override fun set(receiver: T, value: R) { try { val setter = javaSetter ?: @@ -83,6 +91,10 @@ open class KMutableProperty1Impl : KProperty1Impl, KMutableProperty1 throw IllegalPropertyAccessException(e) } } + + class Setter(override val property: KMutableProperty1Impl) : KMutablePropertyImpl.Setter, KMutableProperty1.Setter { + override fun invoke(receiver: T, value: R): Unit = property.set(receiver, value) + } } diff --git a/core/reflection.jvm/src/kotlin/reflect/jvm/internal/KProperty2Impl.kt b/core/reflection.jvm/src/kotlin/reflect/jvm/internal/KProperty2Impl.kt index e1fd983f1c5..02de0088c96 100644 --- a/core/reflection.jvm/src/kotlin/reflect/jvm/internal/KProperty2Impl.kt +++ b/core/reflection.jvm/src/kotlin/reflect/jvm/internal/KProperty2Impl.kt @@ -30,6 +30,8 @@ open class KProperty2Impl : DescriptorBasedProperty, KProperty2 : DescriptorBasedProperty, KProperty2(override val property: KProperty2Impl) : KPropertyImpl.Getter, KProperty2.Getter { + override fun invoke(receiver1: D, receiver2: E): R = property.get(receiver1, receiver2) + } } @@ -51,6 +57,8 @@ class KMutableProperty2Impl : KProperty2Impl, KMutableProperty constructor(container: KClassImpl, descriptor: PropertyDescriptor) : super(container, descriptor) + override val setter by ReflectProperties.lazy { Setter(this) } + override val javaSetter: Method get() = super.javaSetter!! override fun set(receiver1: D, receiver2: E, value: R) { @@ -61,4 +69,8 @@ class KMutableProperty2Impl : KProperty2Impl, KMutableProperty throw IllegalPropertyAccessException(e) } } + + class Setter(override val property: KMutableProperty2Impl) : KMutablePropertyImpl.Setter, KMutableProperty2.Setter { + override fun invoke(receiver1: D, receiver2: E, value: R): Unit = property.set(receiver1, receiver2, value) + } } diff --git a/core/reflection.jvm/src/kotlin/reflect/jvm/internal/KPropertyImpl.kt b/core/reflection.jvm/src/kotlin/reflect/jvm/internal/KPropertyImpl.kt index c19511af775..cca0803d778 100644 --- a/core/reflection.jvm/src/kotlin/reflect/jvm/internal/KPropertyImpl.kt +++ b/core/reflection.jvm/src/kotlin/reflect/jvm/internal/KPropertyImpl.kt @@ -23,9 +23,25 @@ interface KPropertyImpl : KProperty, KCallableImpl { val javaField: Field? val javaGetter: Method? + + override val getter: Getter + + interface Accessor : KProperty.Accessor { + override val property: KPropertyImpl + } + + interface Getter : KProperty.Getter, KCallableImpl { + override val name: String get() = "" + } } interface KMutablePropertyImpl : KMutableProperty, KPropertyImpl { val javaSetter: Method? + + override val setter: Setter + + interface Setter : KMutableProperty.Setter, KPropertyImpl.Accessor, KCallableImpl { + override val name: String get() = "" + } } diff --git a/core/runtime.jvm/src/kotlin/jvm/internal/MutablePropertyReference0.java b/core/runtime.jvm/src/kotlin/jvm/internal/MutablePropertyReference0.java index 6b19fdcac72..0e8840ee65f 100644 --- a/core/runtime.jvm/src/kotlin/jvm/internal/MutablePropertyReference0.java +++ b/core/runtime.jvm/src/kotlin/jvm/internal/MutablePropertyReference0.java @@ -17,6 +17,7 @@ package kotlin.jvm.internal; import kotlin.reflect.KMutableProperty0; +import kotlin.reflect.KProperty0; public class MutablePropertyReference0 extends MutablePropertyReference implements KMutableProperty0 { @Override @@ -28,4 +29,14 @@ public class MutablePropertyReference0 extends MutablePropertyReference implemen public void set(Object value) { throw error(); } + + @Override + public KProperty0.Getter getGetter() { + throw error(); + } + + @Override + public KMutableProperty0.Setter getSetter() { + throw error(); + } } diff --git a/core/runtime.jvm/src/kotlin/jvm/internal/MutablePropertyReference1.java b/core/runtime.jvm/src/kotlin/jvm/internal/MutablePropertyReference1.java index 44bd0b747db..ec5dcb604fc 100644 --- a/core/runtime.jvm/src/kotlin/jvm/internal/MutablePropertyReference1.java +++ b/core/runtime.jvm/src/kotlin/jvm/internal/MutablePropertyReference1.java @@ -17,6 +17,7 @@ package kotlin.jvm.internal; import kotlin.reflect.KMutableProperty1; +import kotlin.reflect.KProperty1; public class MutablePropertyReference1 extends MutablePropertyReference implements KMutableProperty1 { @Override @@ -28,4 +29,14 @@ public class MutablePropertyReference1 extends MutablePropertyReference implemen public void set(Object receiver, Object value) { throw error(); } + + @Override + public KProperty1.Getter getGetter() { + throw error(); + } + + @Override + public KMutableProperty1.Setter getSetter() { + throw error(); + } } diff --git a/core/runtime.jvm/src/kotlin/jvm/internal/MutablePropertyReference2.java b/core/runtime.jvm/src/kotlin/jvm/internal/MutablePropertyReference2.java index eb6688fb5c1..b4c53be92a1 100644 --- a/core/runtime.jvm/src/kotlin/jvm/internal/MutablePropertyReference2.java +++ b/core/runtime.jvm/src/kotlin/jvm/internal/MutablePropertyReference2.java @@ -17,6 +17,7 @@ package kotlin.jvm.internal; import kotlin.reflect.KMutableProperty2; +import kotlin.reflect.KProperty2; public class MutablePropertyReference2 extends MutablePropertyReference implements KMutableProperty2 { @Override @@ -28,4 +29,14 @@ public class MutablePropertyReference2 extends MutablePropertyReference implemen public void set(Object receiver1, Object receiver2, Object value) { throw error(); } + + @Override + public KProperty2.Getter getGetter() { + throw error(); + } + + @Override + public KMutableProperty2.Setter getSetter() { + throw error(); + } } diff --git a/core/runtime.jvm/src/kotlin/jvm/internal/PropertyReference0.java b/core/runtime.jvm/src/kotlin/jvm/internal/PropertyReference0.java index 0c07baf59ff..027d3fb2dec 100644 --- a/core/runtime.jvm/src/kotlin/jvm/internal/PropertyReference0.java +++ b/core/runtime.jvm/src/kotlin/jvm/internal/PropertyReference0.java @@ -23,4 +23,9 @@ public class PropertyReference0 extends PropertyReference implements KProperty0 public Object get() { throw error(); } + + @Override + public KProperty0.Getter getGetter() { + throw error(); + } } diff --git a/core/runtime.jvm/src/kotlin/jvm/internal/PropertyReference1.java b/core/runtime.jvm/src/kotlin/jvm/internal/PropertyReference1.java index 3a5df99f07d..ab45224ef95 100644 --- a/core/runtime.jvm/src/kotlin/jvm/internal/PropertyReference1.java +++ b/core/runtime.jvm/src/kotlin/jvm/internal/PropertyReference1.java @@ -23,4 +23,9 @@ public class PropertyReference1 extends PropertyReference implements KProperty1 public Object get(Object receiver) { throw error(); } + + @Override + public KProperty1.Getter getGetter() { + throw error(); + } } diff --git a/core/runtime.jvm/src/kotlin/jvm/internal/PropertyReference2.java b/core/runtime.jvm/src/kotlin/jvm/internal/PropertyReference2.java index b04fe85ebaa..f6404e32c2c 100644 --- a/core/runtime.jvm/src/kotlin/jvm/internal/PropertyReference2.java +++ b/core/runtime.jvm/src/kotlin/jvm/internal/PropertyReference2.java @@ -23,4 +23,9 @@ public class PropertyReference2 extends PropertyReference implements KProperty2 public Object get(Object receiver1, Object receiver2) { throw error(); } + + @Override + public KProperty2.Getter getGetter() { + throw error(); + } }