diff --git a/compiler/testData/codegen/boxWithStdlib/reflection/methodsFromAny/parametersEqualsHashCode.kt b/compiler/testData/codegen/boxWithStdlib/reflection/methodsFromAny/parametersEqualsHashCode.kt new file mode 100644 index 00000000000..bcc19a09e9d --- /dev/null +++ b/compiler/testData/codegen/boxWithStdlib/reflection/methodsFromAny/parametersEqualsHashCode.kt @@ -0,0 +1,26 @@ +import kotlin.test.* + +class A { + fun foo(s: String, x: Int) {} + fun bar(x: Int) {} + val baz = 42 +} + +fun box(): String { + // Dispatch receiver parameters of different callables are not equal + assertNotEquals(A::foo.parameters[0], A::bar.parameters[0]) + assertNotEquals(A::foo.parameters[0], A::baz.parameters[0]) + + assertNotEquals(A::foo.parameters[1], A::bar.parameters[1]) + assertNotEquals(A::foo.parameters[1], A::foo.parameters[2]) + assertNotEquals(A::bar.parameters[1], A::foo.parameters[2]) + + assertEquals(A::foo.parameters[0], A::foo.parameters[0]) + assertEquals(A::foo.parameters[0].hashCode(), A::foo.parameters[0].hashCode()) + assertEquals(A::foo.parameters[1], A::foo.parameters[1]) + assertEquals(A::foo.parameters[1].hashCode(), A::foo.parameters[1].hashCode()) + assertEquals(A::bar.parameters[0], A::bar.parameters[0]) + assertEquals(A::bar.parameters[0].hashCode(), A::bar.parameters[0].hashCode()) + + return "OK" +} diff --git a/compiler/testData/codegen/boxWithStdlib/reflection/methodsFromAny/parametersToString.kt b/compiler/testData/codegen/boxWithStdlib/reflection/methodsFromAny/parametersToString.kt new file mode 100644 index 00000000000..f3857901927 --- /dev/null +++ b/compiler/testData/codegen/boxWithStdlib/reflection/methodsFromAny/parametersToString.kt @@ -0,0 +1,28 @@ +import kotlin.test.* + +fun Int.foo(s: String) {} + +class A { + fun bar() {} +} + +fun baz(name: String) {} + +fun box(): String { + assertEquals( + listOf("extension receiver of ${Int::foo}", "parameter #1 s of ${Int::foo}"), + Int::foo.parameters.map(Any::toString) + ) + + assertEquals( + listOf("instance of ${A::bar}"), + A::bar.parameters.map(Any::toString) + ) + + assertEquals( + listOf("parameter #0 name of ${::baz}"), + ::baz.parameters.map(Any::toString) + ) + + return "OK" +} diff --git a/compiler/tests/org/jetbrains/kotlin/codegen/generated/BlackBoxWithStdlibCodegenTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/codegen/generated/BlackBoxWithStdlibCodegenTestGenerated.java index df0c4a237ce..a88db02fb82 100644 --- a/compiler/tests/org/jetbrains/kotlin/codegen/generated/BlackBoxWithStdlibCodegenTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/codegen/generated/BlackBoxWithStdlibCodegenTestGenerated.java @@ -3492,6 +3492,18 @@ public class BlackBoxWithStdlibCodegenTestGenerated extends AbstractBlackBoxCode doTestWithStdlib(fileName); } + @TestMetadata("parametersEqualsHashCode.kt") + public void testParametersEqualsHashCode() throws Exception { + String fileName = JetTestUtils.navigationMetadata("compiler/testData/codegen/boxWithStdlib/reflection/methodsFromAny/parametersEqualsHashCode.kt"); + doTestWithStdlib(fileName); + } + + @TestMetadata("parametersToString.kt") + public void testParametersToString() throws Exception { + String fileName = JetTestUtils.navigationMetadata("compiler/testData/codegen/boxWithStdlib/reflection/methodsFromAny/parametersToString.kt"); + doTestWithStdlib(fileName); + } + @TestMetadata("propertyEqualsHashCode.kt") public void testPropertyEqualsHashCode() throws Exception { String fileName = JetTestUtils.navigationMetadata("compiler/testData/codegen/boxWithStdlib/reflection/methodsFromAny/propertyEqualsHashCode.kt"); 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 62611238249..60a48e9ac79 100644 --- a/core/reflection.jvm/src/kotlin/reflect/jvm/internal/KParameterImpl.kt +++ b/core/reflection.jvm/src/kotlin/reflect/jvm/internal/KParameterImpl.kt @@ -41,4 +41,13 @@ class KParameterImpl( override val type: KType get() = KTypeImpl(descriptor.type) { callable.caller.parameterTypes[index] } + + override fun equals(other: Any?) = + other is KParameterImpl && callable == other.callable && descriptor == other.descriptor + + override fun hashCode() = + (callable.hashCode() * 31) + descriptor.hashCode() + + override fun toString() = + ReflectionObjectRenderer.renderParameter(this) } 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 edefb19c0b5..7cd70919978 100644 --- a/core/reflection.jvm/src/kotlin/reflect/jvm/internal/ReflectionObjectRenderer.kt +++ b/core/reflection.jvm/src/kotlin/reflect/jvm/internal/ReflectionObjectRenderer.kt @@ -22,6 +22,7 @@ import org.jetbrains.kotlin.descriptors.PropertyDescriptor import org.jetbrains.kotlin.descriptors.ReceiverParameterDescriptor import org.jetbrains.kotlin.renderer.DescriptorRenderer import org.jetbrains.kotlin.types.JetType +import kotlin.reflect.KParameter object ReflectionObjectRenderer { private val renderer = DescriptorRenderer.FQ_NAMES_IN_TYPES @@ -47,6 +48,14 @@ object ReflectionObjectRenderer { append(renderer.renderName(callable.name)) } + fun renderCallable(descriptor: CallableDescriptor): String { + return when (descriptor) { + is PropertyDescriptor -> renderProperty(descriptor) + is FunctionDescriptor -> renderFunction(descriptor) + else -> error("Illegal callable: $descriptor") + } + } + // TODO: include visibility, return type fun renderProperty(descriptor: PropertyDescriptor): String { return StringBuilder { @@ -69,6 +78,19 @@ object ReflectionObjectRenderer { }.toString() } + fun renderParameter(parameter: KParameterImpl): String { + return StringBuilder { + when (parameter.kind) { + KParameter.Kind.EXTENSION_RECEIVER -> append("extension receiver") + KParameter.Kind.INSTANCE -> append("instance") + KParameter.Kind.VALUE -> append("parameter #${parameter.index} ${parameter.name}") + } + + append(" of ") + append(renderCallable(parameter.callable.descriptor)) + }.toString() + } + fun renderType(type: JetType): String { return renderer.renderType(type) }