From c07d0d48d30de62320ced013c2445133a396abcb Mon Sep 17 00:00:00 2001 From: Alexander Udalov Date: Thu, 30 Jun 2016 18:07:15 +0300 Subject: [PATCH] Fix KCallable#callBy to JvmStatic companion object members #KT-12915 Fixed --- .../box/reflection/callBy/companionObject.kt | 31 +++++++++++++++++ .../callBy/jvmStaticInCompanionObject.kt | 34 +++++++++++++++++++ .../codegen/BlackBoxCodegenTestGenerated.java | 12 +++++++ .../reflect/jvm/internal/KFunctionImpl.kt | 7 +++- 4 files changed, 83 insertions(+), 1 deletion(-) create mode 100644 compiler/testData/codegen/box/reflection/callBy/companionObject.kt create mode 100644 compiler/testData/codegen/box/reflection/callBy/jvmStaticInCompanionObject.kt diff --git a/compiler/testData/codegen/box/reflection/callBy/companionObject.kt b/compiler/testData/codegen/box/reflection/callBy/companionObject.kt new file mode 100644 index 00000000000..867def43568 --- /dev/null +++ b/compiler/testData/codegen/box/reflection/callBy/companionObject.kt @@ -0,0 +1,31 @@ +// WITH_REFLECT + +import kotlin.test.assertEquals + +class C { + companion object { + fun foo(a: String, b: String = "b") = a + b + } +} + +fun box(): String { + val f = C.Companion::class.members.single { it.name == "foo" } + + // Any object method currently requires the object instance passed + try { + f.callBy(mapOf( + f.parameters.single { it.name == "a" } to "a" + )) + return "Fail: IllegalArgumentException should have been thrown" + } + catch (e: IllegalArgumentException) { + // OK + } + + assertEquals("ab", f.callBy(mapOf( + f.parameters.first() to C, + f.parameters.single { it.name == "a" } to "a" + ))) + + return "OK" +} diff --git a/compiler/testData/codegen/box/reflection/callBy/jvmStaticInCompanionObject.kt b/compiler/testData/codegen/box/reflection/callBy/jvmStaticInCompanionObject.kt new file mode 100644 index 00000000000..4a61d876323 --- /dev/null +++ b/compiler/testData/codegen/box/reflection/callBy/jvmStaticInCompanionObject.kt @@ -0,0 +1,34 @@ +// WITH_REFLECT + +// KT-12915 IAE on callBy of JvmStatic function with default arguments + +import kotlin.test.assertEquals + +class C { + companion object { + @JvmStatic + fun foo(a: String, b: String = "b") = a + b + } +} + +fun box(): String { + val f = C.Companion::class.members.single { it.name == "foo" } + + // Any object method currently requires the object instance passed + try { + f.callBy(mapOf( + f.parameters.single { it.name == "a" } to "a" + )) + return "Fail: IllegalArgumentException should have been thrown" + } + catch (e: IllegalArgumentException) { + // OK + } + + assertEquals("ab", f.callBy(mapOf( + f.parameters.first() to C, + f.parameters.single { it.name == "a" } to "a" + ))) + + return "OK" +} diff --git a/compiler/tests/org/jetbrains/kotlin/codegen/BlackBoxCodegenTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/codegen/BlackBoxCodegenTestGenerated.java index a552d366272..9ce11e72dbf 100644 --- a/compiler/tests/org/jetbrains/kotlin/codegen/BlackBoxCodegenTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/codegen/BlackBoxCodegenTestGenerated.java @@ -11259,6 +11259,12 @@ public class BlackBoxCodegenTestGenerated extends AbstractBlackBoxCodegenTest { KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("compiler/testData/codegen/box/reflection/callBy"), Pattern.compile("^(.+)\\.kt$"), true); } + @TestMetadata("companionObject.kt") + public void testCompanionObject() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/reflection/callBy/companionObject.kt"); + doTest(fileName); + } + @TestMetadata("defaultAndNonDefaultIntertwined.kt") public void testDefaultAndNonDefaultIntertwined() throws Exception { String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/reflection/callBy/defaultAndNonDefaultIntertwined.kt"); @@ -11271,6 +11277,12 @@ public class BlackBoxCodegenTestGenerated extends AbstractBlackBoxCodegenTest { doTest(fileName); } + @TestMetadata("jvmStaticInCompanionObject.kt") + public void testJvmStaticInCompanionObject() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/reflection/callBy/jvmStaticInCompanionObject.kt"); + doTest(fileName); + } + @TestMetadata("jvmStaticInObject.kt") public void testJvmStaticInObject() throws Exception { String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/reflection/callBy/jvmStaticInObject.kt"); diff --git a/core/reflection.jvm/src/kotlin/reflect/jvm/internal/KFunctionImpl.kt b/core/reflection.jvm/src/kotlin/reflect/jvm/internal/KFunctionImpl.kt index d7af505d155..ba4a4557cb7 100644 --- a/core/reflection.jvm/src/kotlin/reflect/jvm/internal/KFunctionImpl.kt +++ b/core/reflection.jvm/src/kotlin/reflect/jvm/internal/KFunctionImpl.kt @@ -16,6 +16,7 @@ package kotlin.reflect.jvm.internal +import org.jetbrains.kotlin.descriptors.ClassDescriptor import org.jetbrains.kotlin.descriptors.FunctionDescriptor import org.jetbrains.kotlin.descriptors.Visibilities import java.lang.reflect.Constructor @@ -89,7 +90,11 @@ internal open class KFunctionImpl protected constructor( when (member) { is Constructor<*> -> FunctionCaller.Constructor(member) is Method -> when { - descriptor.annotations.findAnnotation(JVM_STATIC) != null -> + // Note that static $default methods for @JvmStatic functions are generated differently in objects and companion objects. + // In objects, $default's signature does _not_ contain the additional object instance parameter, + // as opposed to companion objects where the first parameter is the companion object instance. + descriptor.annotations.findAnnotation(JVM_STATIC) != null && + !(descriptor.containingDeclaration as ClassDescriptor).isCompanionObject -> FunctionCaller.JvmStaticInObject(member) else -> FunctionCaller.StaticMethod(member)