From 7e8e4e9e1b0b8ff47335c11f7138a3452a733b40 Mon Sep 17 00:00:00 2001 From: Michael Bogdanov Date: Thu, 12 Nov 2015 13:04:56 +0300 Subject: [PATCH] Fix for KT-2789: NoSuchMethodError when calling trait function with default arguments that returns generic type, KT-9428 Abstract method with one parameter and one default parameter produces NoSuchMethodError, KT-9924 NoSuchMethod when replacing generic with specific type #KT-2789 Fixed #KT-9428 Fixed #KT-9924 Fixed --- .../kotlin/codegen/CallableMethod.kt | 16 ++++------ .../kotlin/codegen/state/JetTypeMapper.java | 32 ++++++++++++++----- .../box/defaultArguments/signature/kt2789.kt | 23 +++++++++++++ .../box/defaultArguments/signature/kt9428.kt | 23 +++++++++++++ .../box/defaultArguments/signature/kt9924.kt | 13 ++++++++ .../BlackBoxCodegenTestGenerated.java | 27 ++++++++++++++++ 6 files changed, 116 insertions(+), 18 deletions(-) create mode 100644 compiler/testData/codegen/box/defaultArguments/signature/kt2789.kt create mode 100644 compiler/testData/codegen/box/defaultArguments/signature/kt9428.kt create mode 100644 compiler/testData/codegen/box/defaultArguments/signature/kt9924.kt diff --git a/compiler/backend/src/org/jetbrains/kotlin/codegen/CallableMethod.kt b/compiler/backend/src/org/jetbrains/kotlin/codegen/CallableMethod.kt index 5f92c0d690d..627b591ddd2 100644 --- a/compiler/backend/src/org/jetbrains/kotlin/codegen/CallableMethod.kt +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/CallableMethod.kt @@ -16,7 +16,6 @@ package org.jetbrains.kotlin.codegen; -import org.jetbrains.kotlin.codegen.state.JetTypeMapper import org.jetbrains.kotlin.load.java.JvmAbi import org.jetbrains.kotlin.resolve.jvm.jvmSignature.JvmMethodParameterKind import org.jetbrains.kotlin.resolve.jvm.jvmSignature.JvmMethodParameterSignature @@ -31,7 +30,7 @@ import org.jetbrains.org.objectweb.asm.util.Printer public class CallableMethod( override val owner: Type, private val defaultImplOwner: Type?, - private val defaultImplParam: Type?, + private val defaultMethodDesc: String, private val signature: JvmMethodSignature, private val invokeOpcode: Int, override val dispatchReceiverType: Type?, @@ -56,24 +55,21 @@ public class CallableMethod( } public fun genInvokeDefaultInstruction(v: InstructionAdapter) { - if (defaultImplOwner == null || defaultImplParam == null) { + if (defaultImplOwner == null) { throw IllegalStateException() } val method = getAsmMethod() - val desc = JetTypeMapper.getDefaultDescriptor( - method, - if (invokeOpcode == INVOKESTATIC) null else defaultImplParam.getDescriptor(), - extensionReceiverType != null - ) if ("".equals(method.getName())) { v.aconst(null) - v.visitMethodInsn(INVOKESPECIAL, defaultImplOwner.getInternalName(), "", desc, false) + v.visitMethodInsn(INVOKESPECIAL, defaultImplOwner.getInternalName(), "", defaultMethodDesc, false) } else { v.visitMethodInsn(INVOKESTATIC, defaultImplOwner.getInternalName(), - method.getName() + JvmAbi.DEFAULT_PARAMS_IMPL_SUFFIX, desc, false) + method.getName() + JvmAbi.DEFAULT_PARAMS_IMPL_SUFFIX, defaultMethodDesc, false) + + StackValue.coerce(Type.getReturnType(defaultMethodDesc), Type.getReturnType(signature.asmMethod.descriptor), v) } } diff --git a/compiler/backend/src/org/jetbrains/kotlin/codegen/state/JetTypeMapper.java b/compiler/backend/src/org/jetbrains/kotlin/codegen/state/JetTypeMapper.java index cff300670be..f8911386388 100644 --- a/compiler/backend/src/org/jetbrains/kotlin/codegen/state/JetTypeMapper.java +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/state/JetTypeMapper.java @@ -734,7 +734,8 @@ public class JetTypeMapper { if (descriptor instanceof ConstructorDescriptor) { JvmMethodSignature method = mapSignature(descriptor); Type owner = mapClass(((ConstructorDescriptor) descriptor).getContainingDeclaration()); - return new CallableMethod(owner, owner, owner, method, INVOKESPECIAL, null, null, null); + String defaultImplDesc = mapDefaultMethod(descriptor, OwnerKind.IMPLEMENTATION).getDescriptor(); + return new CallableMethod(owner, owner, defaultImplDesc, method, INVOKESPECIAL, null, null, null); } DeclarationDescriptor functionParent = descriptor.getOriginal().getContainingDeclaration(); @@ -744,7 +745,7 @@ public class JetTypeMapper { JvmMethodSignature signature; Type owner; Type ownerForDefaultImpl; - Type ownerForDefaultParam; + FunctionDescriptor baseMethodDescriptor; int invokeOpcode; Type thisClass; @@ -759,9 +760,9 @@ public class JetTypeMapper { boolean isInterface = currentIsInterface && originalIsInterface; - ClassDescriptor ownerForDefault = (ClassDescriptor) findBaseDeclaration(functionDescriptor).getContainingDeclaration(); - ownerForDefaultParam = mapClass(ownerForDefault); - ownerForDefaultImpl = isJvmInterface(ownerForDefault) ? mapDefaultImpls(ownerForDefault) : ownerForDefaultParam; + baseMethodDescriptor = findBaseDeclaration(functionDescriptor).getOriginal(); + ClassDescriptor ownerForDefault = (ClassDescriptor) baseMethodDescriptor.getContainingDeclaration(); + ownerForDefaultImpl = isJvmInterface(ownerForDefault) ? mapDefaultImpls(ownerForDefault) : mapClass(ownerForDefault); if (isInterface && (superCall || descriptor.getVisibility() == Visibilities.PRIVATE)) { thisClass = mapClass(currentOwner); @@ -810,8 +811,8 @@ public class JetTypeMapper { else { signature = mapSignature(functionDescriptor.getOriginal()); owner = mapOwner(functionDescriptor); - ownerForDefaultParam = owner; ownerForDefaultImpl = owner; + baseMethodDescriptor = functionDescriptor; if (functionParent instanceof PackageFragmentDescriptor) { invokeOpcode = INVOKESTATIC; thisClass = null; @@ -836,8 +837,11 @@ public class JetTypeMapper { else { receiverParameterType = null; } + + String defaultImplDesc = mapDefaultMethod(baseMethodDescriptor, getKindForDefaultImplCall(baseMethodDescriptor)).getDescriptor(); + return new CallableMethod( - owner, ownerForDefaultImpl, ownerForDefaultParam, signature, invokeOpcode, + owner, ownerForDefaultImpl, defaultImplDesc, signature, invokeOpcode, thisClass, receiverParameterType, calleeType); } @@ -918,6 +922,18 @@ public class JetTypeMapper { } } + @NotNull + private static OwnerKind getKindForDefaultImplCall(@NotNull FunctionDescriptor baseMethodDescriptor) { + DeclarationDescriptor containingDeclaration = baseMethodDescriptor.getContainingDeclaration(); + if (containingDeclaration instanceof PackageFragmentDescriptor) { + return OwnerKind.PACKAGE; + } + else if (isInterface(containingDeclaration)) { + return OwnerKind.DEFAULT_IMPLS; + } + return OwnerKind.IMPLEMENTATION; + } + @NotNull public static String mapDefaultFieldName(@NotNull PropertyDescriptor propertyDescriptor, boolean isDelegated) { String name; @@ -1047,7 +1063,7 @@ public class JetTypeMapper { } @NotNull - public static String getDefaultDescriptor(@NotNull Method method, @Nullable String dispatchReceiverDescriptor, boolean isExtension) { + private static String getDefaultDescriptor(@NotNull Method method, @Nullable String dispatchReceiverDescriptor, boolean isExtension) { String descriptor = method.getDescriptor(); int argumentsCount = Type.getArgumentTypes(descriptor).length; if (isExtension) { diff --git a/compiler/testData/codegen/box/defaultArguments/signature/kt2789.kt b/compiler/testData/codegen/box/defaultArguments/signature/kt2789.kt new file mode 100644 index 00000000000..043b0d071a3 --- /dev/null +++ b/compiler/testData/codegen/box/defaultArguments/signature/kt2789.kt @@ -0,0 +1,23 @@ +interface FooTrait { + fun make(size: Int = 16) : T + + fun makeFromTraitImpl() : T = make() +} + +class FooClass : FooTrait { + override fun make(size: Int): String { + return "$size" + } +} + +fun box(): String { + val explicitParam = FooClass().make(16) + val defaultRes = FooClass().make() + val defaultTraitRes = FooClass().makeFromTraitImpl() + if (explicitParam != defaultRes) return "fail 1: ${explicitParam} != ${defaultRes}" + if (explicitParam != "16") return "fail 2: ${explicitParam}" + + if (explicitParam != defaultTraitRes) return "fail 3: ${explicitParam} != ${defaultTraitRes}" + + return "OK" +} \ No newline at end of file diff --git a/compiler/testData/codegen/box/defaultArguments/signature/kt9428.kt b/compiler/testData/codegen/box/defaultArguments/signature/kt9428.kt new file mode 100644 index 00000000000..b9d89105d37 --- /dev/null +++ b/compiler/testData/codegen/box/defaultArguments/signature/kt9428.kt @@ -0,0 +1,23 @@ +open class Player(val name: String) +open class SlashPlayer(name: String) : Player(name) + +public abstract class Game { + abstract fun getPlayer(name: String, create: Boolean = true): T? +} + +class SimpleGame : Game() { + override fun getPlayer(name: String, create: Boolean): SlashPlayer? { + return if (create) { + SlashPlayer(name) + } + else null + } +} + +fun box(): String { + val player1 = SimpleGame().getPlayer("fail", false) + if (player1 != null) return "fail 1" + + val player2 = SimpleGame().getPlayer("OK") + return player2!!.name +} \ No newline at end of file diff --git a/compiler/testData/codegen/box/defaultArguments/signature/kt9924.kt b/compiler/testData/codegen/box/defaultArguments/signature/kt9924.kt new file mode 100644 index 00000000000..8366f26008e --- /dev/null +++ b/compiler/testData/codegen/box/defaultArguments/signature/kt9924.kt @@ -0,0 +1,13 @@ +abstract class A { + abstract fun test(a: T, b:Boolean = false) : String +} + +class B : A() { + override fun test(a: String, b: Boolean): String { + return a + } +} + +fun box(): String { + return B().test("OK") +} \ No newline at end of file diff --git a/compiler/tests/org/jetbrains/kotlin/codegen/generated/BlackBoxCodegenTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/codegen/generated/BlackBoxCodegenTestGenerated.java index cdaac91c5ce..d1d80bdb4fa 100644 --- a/compiler/tests/org/jetbrains/kotlin/codegen/generated/BlackBoxCodegenTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/codegen/generated/BlackBoxCodegenTestGenerated.java @@ -2937,6 +2937,33 @@ public class BlackBoxCodegenTestGenerated extends AbstractBlackBoxCodegenTest { doTest(fileName); } } + + @TestMetadata("compiler/testData/codegen/box/defaultArguments/signature") + @TestDataPath("$PROJECT_ROOT") + @RunWith(JUnit3RunnerWithInners.class) + public static class Signature extends AbstractBlackBoxCodegenTest { + public void testAllFilesPresentInSignature() throws Exception { + KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("compiler/testData/codegen/box/defaultArguments/signature"), Pattern.compile("^(.+)\\.kt$"), true); + } + + @TestMetadata("kt2789.kt") + public void testKt2789() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/defaultArguments/signature/kt2789.kt"); + doTest(fileName); + } + + @TestMetadata("kt9428.kt") + public void testKt9428() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/defaultArguments/signature/kt9428.kt"); + doTest(fileName); + } + + @TestMetadata("kt9924.kt") + public void testKt9924() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/defaultArguments/signature/kt9924.kt"); + doTest(fileName); + } + } } @TestMetadata("compiler/testData/codegen/box/delegatedProperty")