Use proper flag for interface method invocation

This commit is contained in:
Mikhael Bogdanov
2017-01-10 16:40:23 +01:00
parent 0f2139f27d
commit 7f8acbb759
4 changed files with 59 additions and 5 deletions
@@ -20,6 +20,7 @@ import org.jetbrains.kotlin.load.java.JvmAbi
import org.jetbrains.kotlin.resolve.jvm.jvmSignature.JvmMethodParameterKind
import org.jetbrains.kotlin.resolve.jvm.jvmSignature.JvmMethodParameterSignature
import org.jetbrains.kotlin.resolve.jvm.jvmSignature.JvmMethodSignature
import org.jetbrains.org.objectweb.asm.Opcodes
import org.jetbrains.org.objectweb.asm.Opcodes.INVOKESPECIAL
import org.jetbrains.org.objectweb.asm.Opcodes.INVOKESTATIC
import org.jetbrains.org.objectweb.asm.Type
@@ -35,7 +36,8 @@ class CallableMethod(
private val invokeOpcode: Int,
override val dispatchReceiverType: Type?,
override val extensionReceiverType: Type?,
override val generateCalleeType: Type?
override val generateCalleeType: Type?,
private val isInterfaceMethod: Boolean = Opcodes.INVOKEINTERFACE == invokeOpcode
) : Callable {
fun getValueParameters(): List<JvmMethodParameterSignature> =
signature.valueParameters
@@ -51,7 +53,13 @@ class CallableMethod(
override fun genInvokeInstruction(v: InstructionAdapter) {
v.visitMethodInsn(invokeOpcode, owner.internalName, getAsmMethod().name, getAsmMethod().descriptor)
v.visitMethodInsn(
invokeOpcode,
owner.internalName,
getAsmMethod().name,
getAsmMethod().descriptor,
isInterfaceMethod
)
}
fun genInvokeDefaultInstruction(v: InstructionAdapter) {
@@ -61,7 +69,7 @@ class CallableMethod(
val method = getAsmMethod()
if ("<init>".equals(method.name)) {
if ("<init>" == method.name) {
v.visitMethodInsn(INVOKESPECIAL, defaultImplOwner.internalName, "<init>", defaultMethodDesc, false)
}
else {
@@ -702,7 +702,7 @@ public class KotlinTypeMapper {
JvmMethodSignature method = mapSignatureSkipGeneric(descriptor);
Type owner = mapClass(((ClassConstructorDescriptor) descriptor).getContainingDeclaration());
String defaultImplDesc = mapDefaultMethod(descriptor, OwnerKind.IMPLEMENTATION).getDescriptor();
return new CallableMethod(owner, owner, defaultImplDesc, method, INVOKESPECIAL, null, null, null);
return new CallableMethod(owner, owner, defaultImplDesc, method, INVOKESPECIAL, null, null, null, false);
}
if (descriptor instanceof LocalVariableAccessorDescriptor) {
@@ -722,6 +722,7 @@ public class KotlinTypeMapper {
FunctionDescriptor baseMethodDescriptor;
int invokeOpcode;
Type thisClass;
boolean isInterfaceMember = false;
if (functionParent instanceof ClassDescriptor) {
FunctionDescriptor declarationFunctionDescriptor = findAnyDeclaration(functionDescriptor);
@@ -746,6 +747,7 @@ public class KotlinTypeMapper {
invokeOpcode = INVOKESPECIAL;
signature = mapSignatureSkipGeneric(functionDescriptor);
owner = thisClass;
isInterfaceMember = true;
}
else {
invokeOpcode = INVOKESTATIC;
@@ -760,13 +762,16 @@ public class KotlinTypeMapper {
CodegenUtilKt.isJvmStaticInObjectOrClass(functionDescriptor);
if (isStaticInvocation) {
invokeOpcode = INVOKESTATIC;
isInterfaceMember = currentIsInterface && currentOwner instanceof JavaClassDescriptor;
}
else if (isInterface) {
invokeOpcode = INVOKEINTERFACE;
isInterfaceMember = true;
}
else {
boolean isPrivateFunInvocation = Visibilities.isPrivate(functionDescriptor.getVisibility());
invokeOpcode = superCall || isPrivateFunInvocation ? INVOKESPECIAL : INVOKEVIRTUAL;
isInterfaceMember = superCall && currentIsInterface;
}
FunctionDescriptor overriddenSpecialBuiltinFunction =
@@ -777,6 +782,7 @@ public class KotlinTypeMapper {
signature = mapSignatureSkipGeneric(functionToCall);
/*TODO: isInterfaceMember?*/
ClassDescriptor receiver = (currentIsInterface && !originalIsInterface) || currentOwner instanceof FunctionClassDescriptor
? declarationOwner
: currentOwner;
@@ -818,7 +824,8 @@ public class KotlinTypeMapper {
return new CallableMethod(
owner, ownerForDefaultImpl, defaultImplDesc, signature, invokeOpcode,
thisClass, receiverParameterType, calleeType);
thisClass, receiverParameterType, calleeType,
isJvm8Target ? isInterfaceMember : invokeOpcode == INVOKEINTERFACE );
}
private boolean isJvm8Interface(@NotNull ClassDescriptor ownerForDefault) {
@@ -0,0 +1,24 @@
// FILE: Simple.java
public interface Simple {
default String test() {
return "O";
}
static String testStatic() {
return "K";
}
}
// FILE: main.kt
// JVM_TARGET: 1.8
class TestClass : Simple {
override fun test(): String {
return super.test()
}
}
fun box(): String {
return TestClass().test() + Simple.testStatic()
}
@@ -257,6 +257,21 @@ public class BlackBoxWithJava8CodegenTestGenerated extends AbstractBlackBoxCodeg
doTest(fileName);
}
@TestMetadata("compiler/testData/codegen/java8/box/jvm8/interfaceFlag")
@TestDataPath("$PROJECT_ROOT")
@RunWith(JUnit3RunnerWithInners.class)
public static class InterfaceFlag extends AbstractBlackBoxCodegenTest {
public void testAllFilesPresentInInterfaceFlag() throws Exception {
KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("compiler/testData/codegen/java8/box/jvm8/interfaceFlag"), Pattern.compile("^(.+)\\.kt$"), TargetBackend.ANY, true);
}
@TestMetadata("superCall.kt")
public void testSuperCall() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/java8/box/jvm8/interfaceFlag/superCall.kt");
doTest(fileName);
}
}
@TestMetadata("compiler/testData/codegen/java8/box/jvm8/noDelegation")
@TestDataPath("$PROJECT_ROOT")
@RunWith(JUnit3RunnerWithInners.class)