Introduce KProperty{1,2}.getExtensionDelegate
These two functions are supposed to fix an inconvenience in the design of
KProperty{1,2}.getDelegate for extension properties, where you have to pass an
instance of the extension receiver which is going to be completely ignored
#KT-8384 Fixed
This commit is contained in:
+38
@@ -0,0 +1,38 @@
|
|||||||
|
// IGNORE_BACKEND: JS
|
||||||
|
// WITH_REFLECT
|
||||||
|
|
||||||
|
import kotlin.reflect.KProperty
|
||||||
|
import kotlin.reflect.KProperty2
|
||||||
|
import kotlin.reflect.jvm.isAccessible
|
||||||
|
import kotlin.reflect.full.getExtensionDelegate
|
||||||
|
import kotlin.test.*
|
||||||
|
|
||||||
|
object Delegate {
|
||||||
|
operator fun getValue(instance: Any?, property: KProperty<*>) = true
|
||||||
|
}
|
||||||
|
|
||||||
|
class Foo {
|
||||||
|
val member: Boolean by Delegate
|
||||||
|
val String.memberExtension: Boolean by Delegate
|
||||||
|
}
|
||||||
|
|
||||||
|
val Foo.extension: Boolean by Delegate
|
||||||
|
|
||||||
|
fun box(): String {
|
||||||
|
// Top level extension
|
||||||
|
assertEquals(Delegate, Foo::extension.apply { isAccessible = true }.getExtensionDelegate())
|
||||||
|
|
||||||
|
// Member extension
|
||||||
|
val me = Foo::class.members.single { it.name == "memberExtension" } as KProperty2<Foo, String, Boolean>
|
||||||
|
assertEquals(Delegate, me.apply { isAccessible = true }.getExtensionDelegate(Foo()))
|
||||||
|
|
||||||
|
// Member (should fail)
|
||||||
|
try {
|
||||||
|
Foo::member.apply { isAccessible = true }.getExtensionDelegate()
|
||||||
|
return "Fail: getExtensionDelegate() should fail on a non-extension property"
|
||||||
|
} catch (e: Exception) {
|
||||||
|
// OK
|
||||||
|
}
|
||||||
|
|
||||||
|
return "OK"
|
||||||
|
}
|
||||||
Vendored
+24
@@ -0,0 +1,24 @@
|
|||||||
|
@kotlin.Metadata
|
||||||
|
public final class Delegate {
|
||||||
|
public final static field INSTANCE: Delegate
|
||||||
|
private method <init>(): void
|
||||||
|
public final method getValue(@org.jetbrains.annotations.Nullable p0: java.lang.Object, @org.jetbrains.annotations.NotNull p1: kotlin.reflect.KProperty): boolean
|
||||||
|
}
|
||||||
|
|
||||||
|
@kotlin.Metadata
|
||||||
|
public final class Foo {
|
||||||
|
private synthetic final static field $$delegatedProperties: kotlin.reflect.KProperty[]
|
||||||
|
private final @org.jetbrains.annotations.NotNull field member$delegate: Delegate
|
||||||
|
private final @org.jetbrains.annotations.NotNull field memberExtension$delegate: Delegate
|
||||||
|
public method <init>(): void
|
||||||
|
public final method getMember(): boolean
|
||||||
|
public final method getMemberExtension(@org.jetbrains.annotations.NotNull p0: java.lang.String): boolean
|
||||||
|
}
|
||||||
|
|
||||||
|
@kotlin.Metadata
|
||||||
|
public final class GetExtensionDelegateKt {
|
||||||
|
private synthetic final static field $$delegatedProperties: kotlin.reflect.KProperty[]
|
||||||
|
private final static @org.jetbrains.annotations.NotNull field extension$delegate: Delegate
|
||||||
|
public final static @org.jetbrains.annotations.NotNull method box(): java.lang.String
|
||||||
|
public final static method getExtension(@org.jetbrains.annotations.NotNull p0: Foo): boolean
|
||||||
|
}
|
||||||
+6
@@ -14601,6 +14601,12 @@ public class IrBlackBoxCodegenTestGenerated extends AbstractIrBlackBoxCodegenTes
|
|||||||
doTest(fileName);
|
doTest(fileName);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@TestMetadata("getExtensionDelegate.kt")
|
||||||
|
public void testGetExtensionDelegate() throws Exception {
|
||||||
|
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/reflection/properties/getDelegate/getExtensionDelegate.kt");
|
||||||
|
doTest(fileName);
|
||||||
|
}
|
||||||
|
|
||||||
@TestMetadata("kPropertyForDelegatedProperty.kt")
|
@TestMetadata("kPropertyForDelegatedProperty.kt")
|
||||||
public void testKPropertyForDelegatedProperty() throws Exception {
|
public void testKPropertyForDelegatedProperty() throws Exception {
|
||||||
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/reflection/properties/getDelegate/kPropertyForDelegatedProperty.kt");
|
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/reflection/properties/getDelegate/kPropertyForDelegatedProperty.kt");
|
||||||
|
|||||||
@@ -14601,6 +14601,12 @@ public class BlackBoxCodegenTestGenerated extends AbstractBlackBoxCodegenTest {
|
|||||||
doTest(fileName);
|
doTest(fileName);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@TestMetadata("getExtensionDelegate.kt")
|
||||||
|
public void testGetExtensionDelegate() throws Exception {
|
||||||
|
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/reflection/properties/getDelegate/getExtensionDelegate.kt");
|
||||||
|
doTest(fileName);
|
||||||
|
}
|
||||||
|
|
||||||
@TestMetadata("kPropertyForDelegatedProperty.kt")
|
@TestMetadata("kPropertyForDelegatedProperty.kt")
|
||||||
public void testKPropertyForDelegatedProperty() throws Exception {
|
public void testKPropertyForDelegatedProperty() throws Exception {
|
||||||
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/reflection/properties/getDelegate/kPropertyForDelegatedProperty.kt");
|
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/reflection/properties/getDelegate/kPropertyForDelegatedProperty.kt");
|
||||||
|
|||||||
@@ -14601,6 +14601,12 @@ public class LightAnalysisModeCodegenTestGenerated extends AbstractLightAnalysis
|
|||||||
doTest(fileName);
|
doTest(fileName);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@TestMetadata("getExtensionDelegate.kt")
|
||||||
|
public void testGetExtensionDelegate() throws Exception {
|
||||||
|
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/reflection/properties/getDelegate/getExtensionDelegate.kt");
|
||||||
|
doTest(fileName);
|
||||||
|
}
|
||||||
|
|
||||||
@TestMetadata("kPropertyForDelegatedProperty.kt")
|
@TestMetadata("kPropertyForDelegatedProperty.kt")
|
||||||
public void testKPropertyForDelegatedProperty() throws Exception {
|
public void testKPropertyForDelegatedProperty() throws Exception {
|
||||||
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/reflection/properties/getDelegate/kPropertyForDelegatedProperty.kt");
|
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/reflection/properties/getDelegate/kPropertyForDelegatedProperty.kt");
|
||||||
|
|||||||
@@ -145,6 +145,8 @@ public interface KProperty1<T, out R> : KProperty<R>, (T) -> R {
|
|||||||
* @param receiver the receiver which is used to obtain the value of the property delegate.
|
* @param receiver the receiver which is used to obtain the value of the property delegate.
|
||||||
* For example, it should be a class instance if this is a member property of that class,
|
* For example, it should be a class instance if this is a member property of that class,
|
||||||
* or an extension receiver if this is a top level extension property.
|
* or an extension receiver if this is a top level extension property.
|
||||||
|
*
|
||||||
|
* @see [KProperty1.getExtensionDelegate]
|
||||||
*/
|
*/
|
||||||
@SinceKotlin("1.1")
|
@SinceKotlin("1.1")
|
||||||
public fun getDelegate(receiver: T): Any?
|
public fun getDelegate(receiver: T): Any?
|
||||||
@@ -204,6 +206,8 @@ public interface KProperty2<D, E, out R> : KProperty<R>, (D, E) -> R {
|
|||||||
*
|
*
|
||||||
* @param receiver1 the instance of the first receiver.
|
* @param receiver1 the instance of the first receiver.
|
||||||
* @param receiver2 the instance of the second receiver.
|
* @param receiver2 the instance of the second receiver.
|
||||||
|
*
|
||||||
|
* @see [KProperty2.getExtensionDelegate]
|
||||||
*/
|
*/
|
||||||
@SinceKotlin("1.1")
|
@SinceKotlin("1.1")
|
||||||
public fun getDelegate(receiver1: D, receiver2: E): Any?
|
public fun getDelegate(receiver1: D, receiver2: E): Any?
|
||||||
|
|||||||
@@ -0,0 +1,48 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2010-2017 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
@file:JvmName("KProperties")
|
||||||
|
package kotlin.reflect.full
|
||||||
|
|
||||||
|
import kotlin.reflect.KProperty1
|
||||||
|
import kotlin.reflect.KProperty2
|
||||||
|
import kotlin.reflect.jvm.internal.KPropertyImpl
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the instance of a delegated **extension property**, or `null` if this property is not delegated.
|
||||||
|
* Throws an exception if this is not an extension property.
|
||||||
|
*
|
||||||
|
* @see [KProperty1.getDelegate]
|
||||||
|
*/
|
||||||
|
@SinceKotlin("1.1")
|
||||||
|
fun KProperty1<*, *>.getExtensionDelegate(): Any? {
|
||||||
|
@Suppress("UNCHECKED_CAST")
|
||||||
|
return (this as KProperty1<Any?, *>).getDelegate(KPropertyImpl.EXTENSION_PROPERTY_DELEGATE)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the instance of a delegated **member extension property**, or `null` if this property is not delegated.
|
||||||
|
* Throws an exception if this is not an extension property.
|
||||||
|
*
|
||||||
|
* @param receiver the instance of the class used to retrieve the value of the property delegate.
|
||||||
|
*
|
||||||
|
* @see [KProperty2.getDelegate]
|
||||||
|
*/
|
||||||
|
@SinceKotlin("1.1")
|
||||||
|
fun <D> KProperty2<D, *, *>.getExtensionDelegate(receiver: D): Any? {
|
||||||
|
@Suppress("UNCHECKED_CAST")
|
||||||
|
return (this as KProperty2<D, Any?, *>).getDelegate(receiver, KPropertyImpl.EXTENSION_PROPERTY_DELEGATE)
|
||||||
|
}
|
||||||
@@ -87,6 +87,12 @@ internal abstract class KPropertyImpl<out R> private constructor(
|
|||||||
|
|
||||||
protected fun getDelegate(field: Field?, receiver: Any?): Any? =
|
protected fun getDelegate(field: Field?, receiver: Any?): Any? =
|
||||||
try {
|
try {
|
||||||
|
if (receiver === EXTENSION_PROPERTY_DELEGATE) {
|
||||||
|
if (descriptor.extensionReceiverParameter == null) {
|
||||||
|
throw RuntimeException("'$this' is not an extension property and thus getExtensionDelegate() " +
|
||||||
|
"is not going to work, use getDelegate() instead")
|
||||||
|
}
|
||||||
|
}
|
||||||
field?.get(receiver)
|
field?.get(receiver)
|
||||||
}
|
}
|
||||||
catch (e: IllegalAccessException) {
|
catch (e: IllegalAccessException) {
|
||||||
@@ -164,6 +170,10 @@ internal abstract class KPropertyImpl<out R> private constructor(
|
|||||||
computeCallerForAccessor(isGetter = false)
|
computeCallerForAccessor(isGetter = false)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
val EXTENSION_PROPERTY_DELEGATE = Any()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
+12
@@ -18448,6 +18448,18 @@ public class JsCodegenBoxTestGenerated extends AbstractJsCodegenBoxTest {
|
|||||||
throw new AssertionError("Looks like this test can be unmuted. Remove IGNORE_BACKEND directive for that.");
|
throw new AssertionError("Looks like this test can be unmuted. Remove IGNORE_BACKEND directive for that.");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@TestMetadata("getExtensionDelegate.kt")
|
||||||
|
public void testGetExtensionDelegate() throws Exception {
|
||||||
|
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/reflection/properties/getDelegate/getExtensionDelegate.kt");
|
||||||
|
try {
|
||||||
|
doTest(fileName);
|
||||||
|
}
|
||||||
|
catch (Throwable ignore) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
throw new AssertionError("Looks like this test can be unmuted. Remove IGNORE_BACKEND directive for that.");
|
||||||
|
}
|
||||||
|
|
||||||
@TestMetadata("kPropertyForDelegatedProperty.kt")
|
@TestMetadata("kPropertyForDelegatedProperty.kt")
|
||||||
public void testKPropertyForDelegatedProperty() throws Exception {
|
public void testKPropertyForDelegatedProperty() throws Exception {
|
||||||
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/reflection/properties/getDelegate/kPropertyForDelegatedProperty.kt");
|
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/reflection/properties/getDelegate/kPropertyForDelegatedProperty.kt");
|
||||||
|
|||||||
Reference in New Issue
Block a user