diff --git a/compiler/testData/codegen/box/reflection/methodsFromAny/fakeOverrideEqualsHashCode.kt b/compiler/testData/codegen/box/reflection/methodsFromAny/fakeOverrideEqualsHashCode.kt new file mode 100644 index 00000000000..d6b78b8c0ce --- /dev/null +++ b/compiler/testData/codegen/box/reflection/methodsFromAny/fakeOverrideEqualsHashCode.kt @@ -0,0 +1,25 @@ +// IGNORE_BACKEND: JS_IR, JS, NATIVE +// WITH_REFLECT + +import kotlin.test.assertNotEquals + +open class A { + fun foo(t: T) {} +} + +open class B : A() + +class C : B() + +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" +} diff --git a/compiler/testData/codegen/box/reflection/methodsFromAny/fakeOverrideToString.kt b/compiler/testData/codegen/box/reflection/methodsFromAny/fakeOverrideToString.kt new file mode 100644 index 00000000000..910716c5343 --- /dev/null +++ b/compiler/testData/codegen/box/reflection/methodsFromAny/fakeOverrideToString.kt @@ -0,0 +1,27 @@ +// IGNORE_BACKEND: JVM_IR, JS_IR, JS, NATIVE +// WITH_REFLECT + +import kotlin.test.assertEquals + +open class A { + fun foo(t: T) {} +} + +open class B : A() + +class C : B() + +fun box(): String { + assertEquals("fun A.foo(T): kotlin.Unit", A::foo.toString()) + assertEquals("fun B.foo(U): kotlin.Unit", B::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.foo(T): kotlin.Unit", afoo.toString()) + val bfoo = B::class.members.single { it.name == "foo" } + assertEquals("fun B.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" +} diff --git a/compiler/testData/codegen/box/reflection/methodsFromAny/fakeOverrideToString2.kt b/compiler/testData/codegen/box/reflection/methodsFromAny/fakeOverrideToString2.kt new file mode 100644 index 00000000000..045d9963772 --- /dev/null +++ b/compiler/testData/codegen/box/reflection/methodsFromAny/fakeOverrideToString2.kt @@ -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" +} diff --git a/compiler/testData/codegen/box/reflection/parameters/instanceParameterOfFakeOverride.kt b/compiler/testData/codegen/box/reflection/parameters/instanceParameterOfFakeOverride.kt new file mode 100644 index 00000000000..81605f77295 --- /dev/null +++ b/compiler/testData/codegen/box/reflection/parameters/instanceParameterOfFakeOverride.kt @@ -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" +} diff --git a/compiler/tests/org/jetbrains/kotlin/codegen/BlackBoxCodegenTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/codegen/BlackBoxCodegenTestGenerated.java index 4084bd2d48b..8a4d1d032aa 100644 --- a/compiler/tests/org/jetbrains/kotlin/codegen/BlackBoxCodegenTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/codegen/BlackBoxCodegenTestGenerated.java @@ -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"); diff --git a/compiler/tests/org/jetbrains/kotlin/codegen/LightAnalysisModeTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/codegen/LightAnalysisModeTestGenerated.java index 4cd86b59388..99d4d22a4ae 100644 --- a/compiler/tests/org/jetbrains/kotlin/codegen/LightAnalysisModeTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/codegen/LightAnalysisModeTestGenerated.java @@ -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"); diff --git a/compiler/tests/org/jetbrains/kotlin/codegen/ir/IrBlackBoxCodegenTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/codegen/ir/IrBlackBoxCodegenTestGenerated.java index d58420169b5..ba5a8521fc0 100644 --- a/compiler/tests/org/jetbrains/kotlin/codegen/ir/IrBlackBoxCodegenTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/codegen/ir/IrBlackBoxCodegenTestGenerated.java @@ -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"); diff --git a/core/reflection.jvm/src/kotlin/reflect/jvm/internal/KCallableImpl.kt b/core/reflection.jvm/src/kotlin/reflect/jvm/internal/KCallableImpl.kt index 23e227c00dc..243a530d1a1 100644 --- a/core/reflection.jvm/src/kotlin/reflect/jvm/internal/KCallableImpl.kt +++ b/core/reflection.jvm/src/kotlin/reflect/jvm/internal/KCallableImpl.kt @@ -40,12 +40,16 @@ internal abstract class KCallableImpl : KCallable { val result = ArrayList() 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) { 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 7c70d799c42..1c5e104fc78 100644 --- a/core/reflection.jvm/src/kotlin/reflect/jvm/internal/KClassImpl.kt +++ b/core/reflection.jvm/src/kotlin/reflect/jvm/internal/KClassImpl.kt @@ -182,6 +182,9 @@ internal class KClassImpl(override val jClass: Class) : 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" would have "A" as the receiver parameter even if a concrete type like "A" 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 diff --git a/core/reflection.jvm/src/kotlin/reflect/jvm/internal/KParameterImpl.kt b/core/reflection.jvm/src/kotlin/reflect/jvm/internal/KParameterImpl.kt index 1c821868a95..337c8018e02 100644 --- a/core/reflection.jvm/src/kotlin/reflect/jvm/internal/KParameterImpl.kt +++ b/core/reflection.jvm/src/kotlin/reflect/jvm/internal/KParameterImpl.kt @@ -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 diff --git a/core/reflection.jvm/src/kotlin/reflect/jvm/internal/ReflectionObjectRenderer.kt b/core/reflection.jvm/src/kotlin/reflect/jvm/internal/ReflectionObjectRenderer.kt index 7f7f3aae57d..7151726738c 100644 --- a/core/reflection.jvm/src/kotlin/reflect/jvm/internal/ReflectionObjectRenderer.kt +++ b/core/reflection.jvm/src/kotlin/reflect/jvm/internal/ReflectionObjectRenderer.kt @@ -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) diff --git a/core/reflection.jvm/src/kotlin/reflect/jvm/internal/util.kt b/core/reflection.jvm/src/kotlin/reflect/jvm/internal/util.kt index 5c0733d46cf..3a7fdbd831b 100644 --- a/core/reflection.jvm/src/kotlin/reflect/jvm/internal/util.kt +++ b/core/reflection.jvm/src/kotlin/reflect/jvm/internal/util.kt @@ -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 deserializeToDescriptor( moduleAnchor: Class<*>, proto: M, diff --git a/js/js.tests/test/org/jetbrains/kotlin/js/test/semantics/IrJsCodegenBoxTestGenerated.java b/js/js.tests/test/org/jetbrains/kotlin/js/test/semantics/IrJsCodegenBoxTestGenerated.java index 154484f23fa..a81115323a2 100644 --- a/js/js.tests/test/org/jetbrains/kotlin/js/test/semantics/IrJsCodegenBoxTestGenerated.java +++ b/js/js.tests/test/org/jetbrains/kotlin/js/test/semantics/IrJsCodegenBoxTestGenerated.java @@ -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"); diff --git a/js/js.tests/test/org/jetbrains/kotlin/js/test/semantics/JsCodegenBoxTestGenerated.java b/js/js.tests/test/org/jetbrains/kotlin/js/test/semantics/JsCodegenBoxTestGenerated.java index b14f299b864..eec6d56d513 100644 --- a/js/js.tests/test/org/jetbrains/kotlin/js/test/semantics/JsCodegenBoxTestGenerated.java +++ b/js/js.tests/test/org/jetbrains/kotlin/js/test/semantics/JsCodegenBoxTestGenerated.java @@ -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");