Fix instance parameter type of inherited declaration in reflection

Both for callables obtained via reflection API (KClass.members etc) and
for callables obtained via ::-references, the instance parameter is now
the class which was used to construct the type at the left-hand side of
the reference, NOT the class where the callable is originally declared
as is known at compile-time. The reason is to reduce the difference in
behavior of KCallable.call vs FunctionN.invoke: the latter always
required the subclass instance for a fake override, and it's reasonable
that the former would require it as well.

Note that in Java reflection, behavior could differ in a similar case.
For a simple fake override, Class.getMethod would return the method
declared in the base class and that method will accept instances of the
base class in invoke. However, it's difficult to rely on this behavior
because if there's a bridge for a fake override in the derived class
(e.g. when overridden members have different signatures), the returned
Method object is accepting the derived class as the receiver. This just
confirms the fact that Java reflection operates on a different level of
abstraction, namely JVM methods in .class files, which is not applicable
to our use cases directly. Another reason not to replicate Java
reflection's behavior is the uncertainty as to which member is returned
in case there are several in the hierarchy for a given fake override:
see the "otherwise one of the methods is chosen arbitrarily" note in
javadoc on Class.getMethod.

 #KT-24170 Fixed
This commit is contained in:
Alexander Udalov
2018-09-12 18:27:46 +03:00
parent 3ca81b95c2
commit 9d27771f86
14 changed files with 251 additions and 9 deletions
@@ -0,0 +1,25 @@
// IGNORE_BACKEND: JS_IR, JS, NATIVE
// WITH_REFLECT
import kotlin.test.assertNotEquals
open class A<T> {
fun foo(t: T) {}
}
open class B<U> : A<U>()
class C : B<String>()
fun box(): String {
val afoo = A::class.members.single { it.name == "foo" }
val bfoo = B::class.members.single { it.name == "foo" }
val cfoo = C::class.members.single { it.name == "foo" }
assertNotEquals(afoo, bfoo)
assertNotEquals(afoo.hashCode(), bfoo.hashCode())
assertNotEquals(bfoo, cfoo)
assertNotEquals(bfoo.hashCode(), cfoo.hashCode())
return "OK"
}
@@ -0,0 +1,27 @@
// IGNORE_BACKEND: JVM_IR, JS_IR, JS, NATIVE
// WITH_REFLECT
import kotlin.test.assertEquals
open class A<T> {
fun foo(t: T) {}
}
open class B<U> : A<U>()
class C : B<String>()
fun box(): String {
assertEquals("fun A<T>.foo(T): kotlin.Unit", A<Double>::foo.toString())
assertEquals("fun B<U>.foo(U): kotlin.Unit", B<Float>::foo.toString())
assertEquals("fun C.foo(kotlin.String): kotlin.Unit", C::foo.toString())
val afoo = A::class.members.single { it.name == "foo" }
assertEquals("fun A<T>.foo(T): kotlin.Unit", afoo.toString())
val bfoo = B::class.members.single { it.name == "foo" }
assertEquals("fun B<U>.foo(U): kotlin.Unit", bfoo.toString())
val cfoo = C::class.members.single { it.name == "foo" }
assertEquals("fun C.foo(kotlin.String): kotlin.Unit", cfoo.toString())
return "OK"
}
@@ -0,0 +1,33 @@
// IGNORE_BACKEND: JVM_IR, JS_IR, JS, NATIVE
// WITH_REFLECT
import kotlin.test.assertEquals
interface I1 {
fun f()
val x: Int
}
interface I2 {
fun f()
val x: Int
}
interface I3 {
fun f()
val x: Int
}
interface I : I2, I1, I3
fun box(): String {
assertEquals("fun I.f(): kotlin.Unit", I::f.toString())
assertEquals("val I.x: kotlin.Int", I::x.toString())
val f = I::class.members.single { it.name == "f" }
assertEquals("fun I.f(): kotlin.Unit", f.toString())
val x = I::class.members.single { it.name == "x" }
assertEquals("val I.x: kotlin.Int", x.toString())
return "OK"
}
@@ -0,0 +1,31 @@
// IGNORE_BACKEND: JS, NATIVE, JS_IR, JVM_IR
// WITH_REFLECT
import kotlin.reflect.full.instanceParameter
import kotlin.reflect.jvm.jvmErasure
import kotlin.reflect.jvm.javaType
import kotlin.test.assertEquals
open class A {
val property = "OK"
fun function() {}
}
class B : A()
fun box(): String {
assertEquals(B::class, B::property.instanceParameter!!.type.jvmErasure)
assertEquals(B::class.java, B::property.instanceParameter!!.type.javaType)
assertEquals(B::class, B::function.instanceParameter!!.type.jvmErasure)
assertEquals(B::class.java, B::function.instanceParameter!!.type.javaType)
val property = B::class.members.single { it.name == "property" }
val function = B::class.members.single { it.name == "function" }
assertEquals(B::class, property.instanceParameter!!.type.jvmErasure)
assertEquals(B::class.java, property.instanceParameter!!.type.javaType)
assertEquals(B::class, function.instanceParameter!!.type.jvmErasure)
assertEquals(B::class.java, function.instanceParameter!!.type.javaType)
return "OK"
}
@@ -19097,6 +19097,21 @@ public class BlackBoxCodegenTestGenerated extends AbstractBlackBoxCodegenTest {
runTest("compiler/testData/codegen/box/reflection/methodsFromAny/extensionPropertyReceiverToString.kt");
}
@TestMetadata("fakeOverrideEqualsHashCode.kt")
public void testFakeOverrideEqualsHashCode() throws Exception {
runTest("compiler/testData/codegen/box/reflection/methodsFromAny/fakeOverrideEqualsHashCode.kt");
}
@TestMetadata("fakeOverrideToString.kt")
public void testFakeOverrideToString() throws Exception {
runTest("compiler/testData/codegen/box/reflection/methodsFromAny/fakeOverrideToString.kt");
}
@TestMetadata("fakeOverrideToString2.kt")
public void testFakeOverrideToString2() throws Exception {
runTest("compiler/testData/codegen/box/reflection/methodsFromAny/fakeOverrideToString2.kt");
}
@TestMetadata("functionEqualsHashCode.kt")
public void testFunctionEqualsHashCode() throws Exception {
runTest("compiler/testData/codegen/box/reflection/methodsFromAny/functionEqualsHashCode.kt");
@@ -19377,6 +19392,11 @@ public class BlackBoxCodegenTestGenerated extends AbstractBlackBoxCodegenTest {
runTest("compiler/testData/codegen/box/reflection/parameters/instanceExtensionReceiverAndValueParameters.kt");
}
@TestMetadata("instanceParameterOfFakeOverride.kt")
public void testInstanceParameterOfFakeOverride() throws Exception {
runTest("compiler/testData/codegen/box/reflection/parameters/instanceParameterOfFakeOverride.kt");
}
@TestMetadata("isMarkedNullable.kt")
public void testIsMarkedNullable() throws Exception {
runTest("compiler/testData/codegen/box/reflection/parameters/isMarkedNullable.kt");
@@ -19097,6 +19097,21 @@ public class LightAnalysisModeTestGenerated extends AbstractLightAnalysisModeTes
runTest("compiler/testData/codegen/box/reflection/methodsFromAny/extensionPropertyReceiverToString.kt");
}
@TestMetadata("fakeOverrideEqualsHashCode.kt")
public void testFakeOverrideEqualsHashCode() throws Exception {
runTest("compiler/testData/codegen/box/reflection/methodsFromAny/fakeOverrideEqualsHashCode.kt");
}
@TestMetadata("fakeOverrideToString.kt")
public void testFakeOverrideToString() throws Exception {
runTest("compiler/testData/codegen/box/reflection/methodsFromAny/fakeOverrideToString.kt");
}
@TestMetadata("fakeOverrideToString2.kt")
public void testFakeOverrideToString2() throws Exception {
runTest("compiler/testData/codegen/box/reflection/methodsFromAny/fakeOverrideToString2.kt");
}
@TestMetadata("functionEqualsHashCode.kt")
public void testFunctionEqualsHashCode() throws Exception {
runTest("compiler/testData/codegen/box/reflection/methodsFromAny/functionEqualsHashCode.kt");
@@ -19377,6 +19392,11 @@ public class LightAnalysisModeTestGenerated extends AbstractLightAnalysisModeTes
runTest("compiler/testData/codegen/box/reflection/parameters/instanceExtensionReceiverAndValueParameters.kt");
}
@TestMetadata("instanceParameterOfFakeOverride.kt")
public void testInstanceParameterOfFakeOverride() throws Exception {
runTest("compiler/testData/codegen/box/reflection/parameters/instanceParameterOfFakeOverride.kt");
}
@TestMetadata("isMarkedNullable.kt")
public void testIsMarkedNullable() throws Exception {
runTest("compiler/testData/codegen/box/reflection/parameters/isMarkedNullable.kt");
@@ -19102,6 +19102,21 @@ public class IrBlackBoxCodegenTestGenerated extends AbstractIrBlackBoxCodegenTes
runTest("compiler/testData/codegen/box/reflection/methodsFromAny/extensionPropertyReceiverToString.kt");
}
@TestMetadata("fakeOverrideEqualsHashCode.kt")
public void testFakeOverrideEqualsHashCode() throws Exception {
runTest("compiler/testData/codegen/box/reflection/methodsFromAny/fakeOverrideEqualsHashCode.kt");
}
@TestMetadata("fakeOverrideToString.kt")
public void testFakeOverrideToString() throws Exception {
runTest("compiler/testData/codegen/box/reflection/methodsFromAny/fakeOverrideToString.kt");
}
@TestMetadata("fakeOverrideToString2.kt")
public void testFakeOverrideToString2() throws Exception {
runTest("compiler/testData/codegen/box/reflection/methodsFromAny/fakeOverrideToString2.kt");
}
@TestMetadata("functionEqualsHashCode.kt")
public void testFunctionEqualsHashCode() throws Exception {
runTest("compiler/testData/codegen/box/reflection/methodsFromAny/functionEqualsHashCode.kt");
@@ -19382,6 +19397,11 @@ public class IrBlackBoxCodegenTestGenerated extends AbstractIrBlackBoxCodegenTes
runTest("compiler/testData/codegen/box/reflection/parameters/instanceExtensionReceiverAndValueParameters.kt");
}
@TestMetadata("instanceParameterOfFakeOverride.kt")
public void testInstanceParameterOfFakeOverride() throws Exception {
runTest("compiler/testData/codegen/box/reflection/parameters/instanceParameterOfFakeOverride.kt");
}
@TestMetadata("isMarkedNullable.kt")
public void testIsMarkedNullable() throws Exception {
runTest("compiler/testData/codegen/box/reflection/parameters/isMarkedNullable.kt");
@@ -40,12 +40,16 @@ internal abstract class KCallableImpl<out R> : KCallable<R> {
val result = ArrayList<KParameter>()
var index = 0
if (descriptor.dispatchReceiverParameter != null && !isBound) {
result.add(KParameterImpl(this, index++, KParameter.Kind.INSTANCE) { descriptor.dispatchReceiverParameter!! })
}
if (!isBound) {
val instanceReceiver = descriptor.instanceReceiverParameter
if (instanceReceiver != null) {
result.add(KParameterImpl(this, index++, KParameter.Kind.INSTANCE) { instanceReceiver })
}
if (descriptor.extensionReceiverParameter != null && !isBound) {
result.add(KParameterImpl(this, index++, KParameter.Kind.EXTENSION_RECEIVER) { descriptor.extensionReceiverParameter!! })
val extensionReceiver = descriptor.extensionReceiverParameter
if (extensionReceiver != null) {
result.add(KParameterImpl(this, index++, KParameter.Kind.EXTENSION_RECEIVER) { extensionReceiver })
}
}
for (i in descriptor.valueParameters.indices) {
@@ -182,6 +182,9 @@ internal class KClassImpl<T : Any>(override val jClass: Class<T>) : KDeclaration
private val classId: ClassId get() = RuntimeTypeMapper.mapJvmClassToKotlinClassId(jClass)
// Note that we load members from the container's default type, which might be confusing. For example, a function declared in a
// generic class "A<T>" would have "A<T>" as the receiver parameter even if a concrete type like "A<String>" was specified
// in the function reference. Another, maybe slightly less confusing, approach would be to use the star-projected type ("A<*>").
internal val memberScope: MemberScope get() = descriptor.defaultType.memberScope
internal val staticScope: MemberScope get() = descriptor.staticScope
@@ -16,8 +16,7 @@
package kotlin.reflect.jvm.internal
import org.jetbrains.kotlin.descriptors.ParameterDescriptor
import org.jetbrains.kotlin.descriptors.ValueParameterDescriptor
import org.jetbrains.kotlin.descriptors.*
import org.jetbrains.kotlin.resolve.descriptorUtil.declaresOrInheritsDefaultValue
import kotlin.reflect.KParameter
import kotlin.reflect.KType
@@ -41,7 +40,22 @@ internal class KParameterImpl(
}
override val type: KType
get() = KTypeImpl(descriptor.type) { callable.caller.parameterTypes[index] }
get() = KTypeImpl(descriptor.type) {
val descriptor = descriptor
if (descriptor is ReceiverParameterDescriptor &&
callable.descriptor.instanceReceiverParameter == descriptor &&
callable.descriptor.kind == CallableMemberDescriptor.Kind.FAKE_OVERRIDE
) {
// In case of fake overrides, dispatch receiver type should be computed manually because Caller.parameterTypes returns
// types from Java reflection where receiver is always the declaring class of the original declaration
// (not the class where the fake override is generated, which is returned by KParameter.type)
(callable.descriptor.containingDeclaration as ClassDescriptor).toJavaClass()
?: throw KotlinReflectionInternalError("Cannot determine receiver Java type of inherited declaration: $descriptor")
} else {
callable.caller.parameterTypes[index]
}
}
override val isOptional: Boolean
get() = (descriptor as? ValueParameterDescriptor)?.declaresOrInheritsDefaultValue() ?: false
@@ -33,7 +33,7 @@ internal object ReflectionObjectRenderer {
}
private fun StringBuilder.appendReceivers(callable: CallableDescriptor) {
val dispatchReceiver = callable.dispatchReceiverParameter
val dispatchReceiver = callable.instanceReceiverParameter
val extensionReceiver = callable.extensionReceiverParameter
appendReceiverType(dispatchReceiver)
@@ -182,6 +182,11 @@ internal val CallableMemberDescriptor.isPublicInBytecode: Boolean
return (visibility == Visibilities.PUBLIC || visibility == Visibilities.INTERNAL) && !isEffectivelyInlineOnly()
}
internal val CallableDescriptor.instanceReceiverParameter: ReceiverParameterDescriptor?
get() =
if (dispatchReceiverParameter != null) (containingDeclaration as ClassDescriptor).thisAsReceiverParameter
else null
internal fun <M : MessageLite, D : CallableDescriptor> deserializeToDescriptor(
moduleAnchor: Class<*>,
proto: M,
@@ -17187,6 +17187,21 @@ public class IrJsCodegenBoxTestGenerated extends AbstractIrJsCodegenBoxTest {
runTest("compiler/testData/codegen/box/reflection/methodsFromAny/extensionPropertyReceiverToString.kt");
}
@TestMetadata("fakeOverrideEqualsHashCode.kt")
public void testFakeOverrideEqualsHashCode() throws Exception {
runTest("compiler/testData/codegen/box/reflection/methodsFromAny/fakeOverrideEqualsHashCode.kt");
}
@TestMetadata("fakeOverrideToString.kt")
public void testFakeOverrideToString() throws Exception {
runTest("compiler/testData/codegen/box/reflection/methodsFromAny/fakeOverrideToString.kt");
}
@TestMetadata("fakeOverrideToString2.kt")
public void testFakeOverrideToString2() throws Exception {
runTest("compiler/testData/codegen/box/reflection/methodsFromAny/fakeOverrideToString2.kt");
}
@TestMetadata("functionEqualsHashCode.kt")
public void testFunctionEqualsHashCode() throws Exception {
runTest("compiler/testData/codegen/box/reflection/methodsFromAny/functionEqualsHashCode.kt");
@@ -17467,6 +17482,11 @@ public class IrJsCodegenBoxTestGenerated extends AbstractIrJsCodegenBoxTest {
runTest("compiler/testData/codegen/box/reflection/parameters/instanceExtensionReceiverAndValueParameters.kt");
}
@TestMetadata("instanceParameterOfFakeOverride.kt")
public void testInstanceParameterOfFakeOverride() throws Exception {
runTest("compiler/testData/codegen/box/reflection/parameters/instanceParameterOfFakeOverride.kt");
}
@TestMetadata("isMarkedNullable.kt")
public void testIsMarkedNullable() throws Exception {
runTest("compiler/testData/codegen/box/reflection/parameters/isMarkedNullable.kt");
@@ -18247,6 +18247,21 @@ public class JsCodegenBoxTestGenerated extends AbstractJsCodegenBoxTest {
runTest("compiler/testData/codegen/box/reflection/methodsFromAny/extensionPropertyReceiverToString.kt");
}
@TestMetadata("fakeOverrideEqualsHashCode.kt")
public void testFakeOverrideEqualsHashCode() throws Exception {
runTest("compiler/testData/codegen/box/reflection/methodsFromAny/fakeOverrideEqualsHashCode.kt");
}
@TestMetadata("fakeOverrideToString.kt")
public void testFakeOverrideToString() throws Exception {
runTest("compiler/testData/codegen/box/reflection/methodsFromAny/fakeOverrideToString.kt");
}
@TestMetadata("fakeOverrideToString2.kt")
public void testFakeOverrideToString2() throws Exception {
runTest("compiler/testData/codegen/box/reflection/methodsFromAny/fakeOverrideToString2.kt");
}
@TestMetadata("functionEqualsHashCode.kt")
public void testFunctionEqualsHashCode() throws Exception {
runTest("compiler/testData/codegen/box/reflection/methodsFromAny/functionEqualsHashCode.kt");
@@ -18527,6 +18542,11 @@ public class JsCodegenBoxTestGenerated extends AbstractJsCodegenBoxTest {
runTest("compiler/testData/codegen/box/reflection/parameters/instanceExtensionReceiverAndValueParameters.kt");
}
@TestMetadata("instanceParameterOfFakeOverride.kt")
public void testInstanceParameterOfFakeOverride() throws Exception {
runTest("compiler/testData/codegen/box/reflection/parameters/instanceParameterOfFakeOverride.kt");
}
@TestMetadata("isMarkedNullable.kt")
public void testIsMarkedNullable() throws Exception {
runTest("compiler/testData/codegen/box/reflection/parameters/isMarkedNullable.kt");