Proper equality comparison for bound callable references represented as reflection objects

(including references to property accessors).
This commit is contained in:
Dmitry Petrov
2016-11-23 16:24:30 +03:00
parent 3dd0c9d1c7
commit 5879e201cd
14 changed files with 148 additions and 21 deletions
@@ -0,0 +1,32 @@
// TODO: investigate should it be ran for JS or not
// IGNORE_BACKEND: JS
// WITH_REFLECT
import kotlin.reflect.*
class C {
var prop = 42
}
val C_propReflect = C::class.memberProperties.find { it.name == "prop" } as? KMutableProperty1 ?: throw AssertionError()
val C_prop = C::prop
val cProp = C()::prop
fun box() =
when {
C_prop.getter != C_prop.getter -> "C_prop.getter != C_prop.getter"
C_propReflect.getter != C_propReflect.getter -> "C_propReflect.getter != C_propReflect.getter"
cProp.getter != cProp.getter -> "cProp.getter != cProp.getter"
cProp.getter == C_prop.getter -> "cProp.getter == C_prop.getter"
C_prop.getter == cProp.getter -> "C_prop.getter == cProp.getter"
cProp.getter == C_propReflect.getter -> "cProp.getter == C_propReflect.getter"
C_propReflect.getter == cProp.getter -> "C_propReflect.getter == cProp.getter"
// TODO https://youtrack.jetbrains.com/issue/KT-13490
// cProp.getter != C()::prop.getter -> "cProp.getter != C()::prop.getter"
// cProp.setter != C()::prop.setter -> "cProp.setter != C()::prop.setter"
else -> "OK"
}
@@ -0,0 +1,35 @@
// TODO: investigate should it be ran for JS or not
// IGNORE_BACKEND: JS
// WITH_REFLECT
import kotlin.reflect.*
class C {
fun foo() {}
val bar = 42
}
val C_fooReflect = C::class.functions.find { it.name == "foo" }!!
val C_foo = C::foo
val cFoo = C()::foo
val C_barReflect = C::class.memberProperties.find { it.name == "bar" }!!
val C_bar = C::bar
val cBar= C()::bar
val Any.className: String
get() = this::class.qualifiedName!!
fun box(): String =
when {
C_fooReflect != C_foo -> "C_fooReflect != C_foo, ${C_fooReflect.className}"
C_foo != C_fooReflect -> "C_foo != C_fooReflect, ${C_foo.className}"
C_fooReflect == cFoo -> "C_fooReflect == cFoo, ${C_fooReflect.className}"
cFoo == C_fooReflect -> "cFoo == C_fooReflect, ${cFoo.className}"
C_barReflect != C_bar -> "C_barReflect != C_bar, ${C_barReflect.className}"
C_bar != C_barReflect -> "C_bar != C_barReflect, ${C_bar.className}"
C_barReflect == cBar -> "C_barReflect == cBar, ${C_barReflect.className}"
cBar == C_barReflect -> "cBar == C_barReflect, ${cBar.className}"
else -> "OK"
}
@@ -1575,11 +1575,23 @@ public class IrBlackBoxCodegenTestGenerated extends AbstractIrBlackBoxCodegenTes
doTest(fileName);
}
@TestMetadata("propertyAccessors.kt")
public void testPropertyAccessors() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/callableReference/bound/equals/propertyAccessors.kt");
doTest(fileName);
}
@TestMetadata("receiverInEquals.kt")
public void testReceiverInEquals() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/callableReference/bound/equals/receiverInEquals.kt");
doTest(fileName);
}
@TestMetadata("reflectionReference.kt")
public void testReflectionReference() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/callableReference/bound/equals/reflectionReference.kt");
doTest(fileName);
}
}
@TestMetadata("compiler/testData/codegen/box/callableReference/bound/inline")
@@ -1575,11 +1575,23 @@ public class BlackBoxCodegenTestGenerated extends AbstractBlackBoxCodegenTest {
doTest(fileName);
}
@TestMetadata("propertyAccessors.kt")
public void testPropertyAccessors() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/callableReference/bound/equals/propertyAccessors.kt");
doTest(fileName);
}
@TestMetadata("receiverInEquals.kt")
public void testReceiverInEquals() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/callableReference/bound/equals/receiverInEquals.kt");
doTest(fileName);
}
@TestMetadata("reflectionReference.kt")
public void testReflectionReference() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/callableReference/bound/equals/reflectionReference.kt");
doTest(fileName);
}
}
@TestMetadata("compiler/testData/codegen/box/callableReference/bound/inline")
@@ -23,6 +23,7 @@ import java.lang.reflect.Constructor
import java.lang.reflect.Member
import java.lang.reflect.Method
import java.lang.reflect.Modifier
import kotlin.jvm.internal.CallableReference
import kotlin.jvm.internal.FunctionImpl
import kotlin.reflect.KFunction
import kotlin.reflect.KotlinReflectionInternalError
@@ -36,12 +37,17 @@ internal class KFunctionImpl private constructor(
override val container: KDeclarationContainerImpl,
name: String,
private val signature: String,
descriptorInitialValue: FunctionDescriptor?
descriptorInitialValue: FunctionDescriptor?,
private val boundReceiver: Any? = CallableReference.NO_RECEIVER
) : KCallableImpl<Any?>(), KFunction<Any?>, FunctionImpl, FunctionWithAllInvokes {
constructor(container: KDeclarationContainerImpl, name: String, signature: String) : this(container, name, signature, null)
constructor(container: KDeclarationContainerImpl, name: String, signature: String, boundReceiver: Any?)
: this(container, name, signature, null, boundReceiver)
constructor(container: KDeclarationContainerImpl, descriptor: FunctionDescriptor) : this(
container, descriptor.name.asString(), RuntimeTypeMapper.mapSignature(descriptor).asString(), descriptor
container,
descriptor.name.asString(),
RuntimeTypeMapper.mapSignature(descriptor).asString(),
descriptor
)
override val descriptor: FunctionDescriptor by ReflectProperties.lazySoft(descriptorInitialValue) {
@@ -140,7 +146,7 @@ internal class KFunctionImpl private constructor(
override fun equals(other: Any?): Boolean {
val that = other.asKFunctionImpl() ?: return false
return container == that.container && name == that.name && signature == that.signature
return container == that.container && name == that.name && signature == that.signature && boundReceiver == that.boundReceiver
}
override fun hashCode(): Int =
@@ -23,7 +23,7 @@ import kotlin.reflect.KProperty0
internal open class KProperty0Impl<out R> : KProperty0<R>, KPropertyImpl<R> {
constructor(container: KDeclarationContainerImpl, descriptor: PropertyDescriptor) : super(container, descriptor)
constructor(container: KDeclarationContainerImpl, name: String, signature: String) : super(container, name, signature)
constructor(container: KDeclarationContainerImpl, name: String, signature: String, boundReceiver: Any?) : super(container, name, signature, boundReceiver)
private val getter_ = ReflectProperties.lazy { Getter(this) }
@@ -41,7 +41,7 @@ internal open class KProperty0Impl<out R> : KProperty0<R>, KPropertyImpl<R> {
internal class KMutableProperty0Impl<R> : KProperty0Impl<R>, KMutableProperty0<R> {
constructor(container: KDeclarationContainerImpl, descriptor: PropertyDescriptor) : super(container, descriptor)
constructor(container: KDeclarationContainerImpl, name: String, signature: String) : super(container, name, signature)
constructor(container: KDeclarationContainerImpl, name: String, signature: String, boundReceiver: Any?) : super(container, name, signature, boundReceiver)
private val setter_ = ReflectProperties.lazy { Setter(this) }
@@ -21,7 +21,7 @@ import kotlin.reflect.KMutableProperty1
import kotlin.reflect.KProperty1
internal open class KProperty1Impl<T, out R> : KProperty1<T, R>, KPropertyImpl<R> {
constructor(container: KDeclarationContainerImpl, name: String, signature: String) : super(container, name, signature)
constructor(container: KDeclarationContainerImpl, name: String, signature: String, boundReceiver: Any?) : super(container, name, signature, boundReceiver)
constructor(container: KDeclarationContainerImpl, descriptor: PropertyDescriptor) : super(container, descriptor)
@@ -39,7 +39,7 @@ internal open class KProperty1Impl<T, out R> : KProperty1<T, R>, KPropertyImpl<R
}
internal class KMutableProperty1Impl<T, R> : KProperty1Impl<T, R>, KMutableProperty1<T, R> {
constructor(container: KDeclarationContainerImpl, name: String, signature: String) : super(container, name, signature)
constructor(container: KDeclarationContainerImpl, name: String, signature: String, boundReceiver: Any?) : super(container, name, signature, boundReceiver)
constructor(container: KDeclarationContainerImpl, descriptor: PropertyDescriptor) : super(container, descriptor)
@@ -21,7 +21,7 @@ import kotlin.reflect.KMutableProperty2
import kotlin.reflect.KProperty2
internal open class KProperty2Impl<D, E, out R> : KProperty2<D, E, R>, KPropertyImpl<R> {
constructor(container: KDeclarationContainerImpl, name: String, signature: String) : super(container, name, signature)
constructor(container: KDeclarationContainerImpl, name: String, signature: String) : super(container, name, signature, null)
constructor(container: KDeclarationContainerImpl, descriptor: PropertyDescriptor) : super(container, descriptor)
@@ -25,6 +25,7 @@ import org.jetbrains.kotlin.serialization.jvm.JvmProtoBufUtil
import org.jetbrains.kotlin.types.TypeUtils
import java.lang.reflect.Field
import java.lang.reflect.Modifier
import kotlin.jvm.internal.CallableReference
import kotlin.reflect.KFunction
import kotlin.reflect.KMutableProperty
import kotlin.reflect.KProperty
@@ -35,10 +36,11 @@ internal abstract class KPropertyImpl<out R> private constructor(
override val container: KDeclarationContainerImpl,
override val name: String,
val signature: String,
descriptorInitialValue: PropertyDescriptor?
descriptorInitialValue: PropertyDescriptor?,
private val boundReceiver: Any? = CallableReference.NO_RECEIVER
) : KCallableImpl<R>(), KProperty<R> {
constructor(container: KDeclarationContainerImpl, name: String, signature: String) : this(
container, name, signature, null
constructor(container: KDeclarationContainerImpl, name: String, signature: String, boundReceiver: Any?) : this(
container, name, signature, null, boundReceiver
)
constructor(container: KDeclarationContainerImpl, descriptor: PropertyDescriptor) : this(
@@ -95,7 +97,7 @@ internal abstract class KPropertyImpl<out R> private constructor(
override fun equals(other: Any?): Boolean {
val that = other.asKPropertyImpl() ?: return false
return container == that.container && name == that.name && signature == that.signature
return container == that.container && name == that.name && signature == that.signature && boundReceiver == that.boundReceiver
}
override fun hashCode(): Int =
@@ -66,29 +66,29 @@ public class ReflectionFactoryImpl extends ReflectionFactory {
@Override
public KFunction function(FunctionReference f) {
return new KFunctionImpl(getOwner(f), f.getName(), f.getSignature());
return new KFunctionImpl(getOwner(f), f.getName(), f.getSignature(), f.getBoundReceiver());
}
// Properties
@Override
public KProperty0 property0(PropertyReference0 p) {
return new KProperty0Impl(getOwner(p), p.getName(), p.getSignature());
return new KProperty0Impl(getOwner(p), p.getName(), p.getSignature(), p.getBoundReceiver());
}
@Override
public KMutableProperty0 mutableProperty0(MutablePropertyReference0 p) {
return new KMutableProperty0Impl(getOwner(p), p.getName(), p.getSignature());
return new KMutableProperty0Impl(getOwner(p), p.getName(), p.getSignature(), p.getBoundReceiver());
}
@Override
public KProperty1 property1(PropertyReference1 p) {
return new KProperty1Impl(getOwner(p), p.getName(), p.getSignature());
return new KProperty1Impl(getOwner(p), p.getName(), p.getSignature(), p.getBoundReceiver());
}
@Override
public KMutableProperty1 mutableProperty1(MutablePropertyReference1 p) {
return new KMutableProperty1Impl(getOwner(p), p.getName(), p.getSignature());
return new KMutableProperty1Impl(getOwner(p), p.getName(), p.getSignature(), p.getBoundReceiver());
}
@Override
@@ -40,7 +40,7 @@ public abstract class CallableReference implements KCallable {
private KCallable reflected;
protected final Object receiver$0;
private static final Object NO_RECEIVER = new Object();
public static final Object NO_RECEIVER = new Object();
public CallableReference() {
this(NO_RECEIVER);
@@ -52,6 +52,10 @@ public abstract class CallableReference implements KCallable {
protected abstract KCallable computeReflected();
public Object getBoundReceiver() {
return receiver$0;
}
public KCallable compute() {
KCallable result = reflected;
if (result == null) {
@@ -80,7 +80,7 @@ public class FunctionReference extends CallableReference implements FunctionImpl
return getOwner().equals(other.getOwner()) &&
getName().equals(other.getName()) &&
getSignature().equals(other.getSignature()) &&
Intrinsics.areEqual(receiver$0, other.receiver$0);
Intrinsics.areEqual(getBoundReceiver(), other.getBoundReceiver());
}
if (obj instanceof KFunction) {
return obj.equals(compute());
@@ -51,7 +51,7 @@ public abstract class PropertyReference extends CallableReference implements KPr
return getOwner().equals(other.getOwner()) &&
getName().equals(other.getName()) &&
getSignature().equals(other.getSignature()) &&
Intrinsics.areEqual(receiver$0, other.receiver$0);
Intrinsics.areEqual(getBoundReceiver(), other.getBoundReceiver());
}
if (obj instanceof KProperty) {
return obj.equals(compute());
@@ -2002,6 +2002,18 @@ public class JsCodegenBoxTestGenerated extends AbstractJsCodegenBoxTest {
throw new AssertionError("Looks like this test can be unmuted. Remove IGNORE_BACKEND directive for that.");
}
@TestMetadata("propertyAccessors.kt")
public void testPropertyAccessors() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/callableReference/bound/equals/propertyAccessors.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("receiverInEquals.kt")
public void testReceiverInEquals() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/callableReference/bound/equals/receiverInEquals.kt");
@@ -2013,6 +2025,18 @@ public class JsCodegenBoxTestGenerated extends AbstractJsCodegenBoxTest {
}
throw new AssertionError("Looks like this test can be unmuted. Remove IGNORE_BACKEND directive for that.");
}
@TestMetadata("reflectionReference.kt")
public void testReflectionReference() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/callableReference/bound/equals/reflectionReference.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("compiler/testData/codegen/box/callableReference/bound/inline")