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
This commit is contained in:
@@ -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 ("<init>".equals(method.getName())) {
|
||||
v.aconst(null)
|
||||
v.visitMethodInsn(INVOKESPECIAL, defaultImplOwner.getInternalName(), "<init>", desc, false)
|
||||
v.visitMethodInsn(INVOKESPECIAL, defaultImplOwner.getInternalName(), "<init>", 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)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -0,0 +1,23 @@
|
||||
interface FooTrait<T> {
|
||||
fun make(size: Int = 16) : T
|
||||
|
||||
fun makeFromTraitImpl() : T = make()
|
||||
}
|
||||
|
||||
class FooClass : FooTrait<String> {
|
||||
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"
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
open class Player(val name: String)
|
||||
open class SlashPlayer(name: String) : Player(name)
|
||||
|
||||
public abstract class Game<T : Player> {
|
||||
abstract fun getPlayer(name: String, create: Boolean = true): T?
|
||||
}
|
||||
|
||||
class SimpleGame : Game<SlashPlayer>() {
|
||||
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
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
abstract class A<T> {
|
||||
abstract fun test(a: T, b:Boolean = false) : String
|
||||
}
|
||||
|
||||
class B : A<String>() {
|
||||
override fun test(a: String, b: Boolean): String {
|
||||
return a
|
||||
}
|
||||
}
|
||||
|
||||
fun box(): String {
|
||||
return B().test("OK")
|
||||
}
|
||||
+27
@@ -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")
|
||||
|
||||
Reference in New Issue
Block a user