diff --git a/compiler/testData/codegen/box/reflection/parameters/findParameterByName.kt b/compiler/testData/codegen/box/reflection/parameters/findParameterByName.kt new file mode 100644 index 00000000000..f3f098273c7 --- /dev/null +++ b/compiler/testData/codegen/box/reflection/parameters/findParameterByName.kt @@ -0,0 +1,24 @@ +// WITH_REFLECT +// FILE: J.java + +public class J { + public void bar(int x) {} +} + +// FILE: K.kt + +import kotlin.reflect.findParameterByName +import kotlin.test.assertEquals +import kotlin.test.assertNull + +fun foo(x: Int) = x + +fun box(): String { + assertEquals(::foo.parameters.single(), ::foo.findParameterByName("x")) + assertNull(::foo.findParameterByName("y")) + + assertNull(J::bar.findParameterByName("x")) + assertNull(J::bar.findParameterByName("y")) + + return "OK" +} diff --git a/compiler/testData/codegen/box/reflection/parameters/instanceExtensionReceiverAndValueParameters.kt b/compiler/testData/codegen/box/reflection/parameters/instanceExtensionReceiverAndValueParameters.kt new file mode 100644 index 00000000000..23ee43eb742 --- /dev/null +++ b/compiler/testData/codegen/box/reflection/parameters/instanceExtensionReceiverAndValueParameters.kt @@ -0,0 +1,36 @@ +// WITH_REFLECT + +import kotlin.reflect.* +import kotlin.test.assertEquals +import kotlin.test.assertNotNull +import kotlin.test.assertNull + +class A { + fun String.memExt(param: Int) {} +} + +fun topLevel() {} + +fun Int.ext(vararg o: Any) {} + +fun box(): String { + A::class.members.single { it.name == "memExt" }.let { + assertNotNull(it.instanceParameter) + assertNotNull(it.extensionReceiverParameter) + assertEquals(1, it.valueParameters.size) + } + + ::topLevel.let { + assertNull(it.instanceParameter) + assertNull(it.extensionReceiverParameter) + assertEquals(0, it.valueParameters.size) + } + + Int::ext.let { + assertNull(it.instanceParameter) + assertNotNull(it.extensionReceiverParameter) + assertEquals(1, it.valueParameters.size) + } + + return "OK" +} diff --git a/compiler/tests/org/jetbrains/kotlin/codegen/BlackBoxCodegenTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/codegen/BlackBoxCodegenTestGenerated.java index 6349e48a3d9..61a03b1f1f2 100644 --- a/compiler/tests/org/jetbrains/kotlin/codegen/BlackBoxCodegenTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/codegen/BlackBoxCodegenTestGenerated.java @@ -12789,12 +12789,24 @@ public class BlackBoxCodegenTestGenerated extends AbstractBlackBoxCodegenTest { KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("compiler/testData/codegen/box/reflection/parameters"), Pattern.compile("^(.+)\\.kt$"), true); } + @TestMetadata("findParameterByName.kt") + public void testFindParameterByName() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/reflection/parameters/findParameterByName.kt"); + doTest(fileName); + } + @TestMetadata("functionParameterNameAndIndex.kt") public void testFunctionParameterNameAndIndex() throws Exception { String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/reflection/parameters/functionParameterNameAndIndex.kt"); doTest(fileName); } + @TestMetadata("instanceExtensionReceiverAndValueParameters.kt") + public void testInstanceExtensionReceiverAndValueParameters() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/reflection/parameters/instanceExtensionReceiverAndValueParameters.kt"); + doTest(fileName); + } + @TestMetadata("isMarkedNullable.kt") public void testIsMarkedNullable() throws Exception { String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/reflection/parameters/isMarkedNullable.kt"); diff --git a/core/reflection.jvm/src/kotlin/reflect/KCallables.kt b/core/reflection.jvm/src/kotlin/reflect/KCallables.kt new file mode 100644 index 00000000000..2f304529872 --- /dev/null +++ b/core/reflection.jvm/src/kotlin/reflect/KCallables.kt @@ -0,0 +1,45 @@ +/* + * Copyright 2010-2016 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("KCallables") +package kotlin.reflect + +/** + * Returns a parameter representing the `this` instance needed to call this callable, + * or `null` if this callable is not a member of a class and thus doesn't take such parameter. + */ +val KCallable<*>.instanceParameter: KParameter? + get() = parameters.singleOrNull { it.kind == KParameter.Kind.INSTANCE } + +/** + * Returns a parameter representing the extension receiver instance needed to call this callable, + * or `null` if this callable is not an extension. + */ +val KCallable<*>.extensionReceiverParameter: KParameter? + get() = parameters.singleOrNull { it.kind == KParameter.Kind.EXTENSION_RECEIVER } + +/** + * Returns parameters of this callable, excluding the `this` instance and the extension receiver parameter. + */ +val KCallable<*>.valueParameters: List + get() = parameters.filter { it.kind == KParameter.Kind.VALUE } + +/** + * Returns the parameter of this callable with the given name, or `null` if there's no such parameter. + */ +fun KCallable<*>.findParameterByName(name: String): KParameter? { + return parameters.singleOrNull { it.name == name } +}