diff --git a/compiler/backend/src/org/jetbrains/kotlin/codegen/ImplementationBodyCodegen.java b/compiler/backend/src/org/jetbrains/kotlin/codegen/ImplementationBodyCodegen.java index d35a5f8a8db..4d780d2f9ea 100644 --- a/compiler/backend/src/org/jetbrains/kotlin/codegen/ImplementationBodyCodegen.java +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/ImplementationBodyCodegen.java @@ -1326,7 +1326,7 @@ public class ImplementationBodyCodegen extends ClassBodyCodegen { for (Map.Entry entry : CodegenUtil.getNonPrivateTraitMethods(descriptor).entrySet()) { FunctionDescriptor traitFun = entry.getKey(); //skip java 8 default methods - if (!(traitFun instanceof JavaCallableMemberDescriptor)) { + if (!(traitFun instanceof JavaCallableMemberDescriptor || isJvm8InterfaceMember(traitFun, state))) { generateDelegationToTraitImpl(traitFun, entry.getValue()); } } diff --git a/compiler/backend/src/org/jetbrains/kotlin/codegen/JvmCodegenUtil.java b/compiler/backend/src/org/jetbrains/kotlin/codegen/JvmCodegenUtil.java index b1ca978267e..3d26cd77b99 100644 --- a/compiler/backend/src/org/jetbrains/kotlin/codegen/JvmCodegenUtil.java +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/JvmCodegenUtil.java @@ -28,11 +28,11 @@ import org.jetbrains.kotlin.codegen.context.CodegenContext; import org.jetbrains.kotlin.codegen.context.FacadePartWithSourceFile; import org.jetbrains.kotlin.codegen.context.MethodContext; import org.jetbrains.kotlin.codegen.context.RootContext; +import org.jetbrains.kotlin.codegen.state.GenerationState; import org.jetbrains.kotlin.codegen.state.KotlinTypeMapper; import org.jetbrains.kotlin.descriptors.*; import org.jetbrains.kotlin.load.java.descriptors.JavaPropertyDescriptor; -import org.jetbrains.kotlin.load.kotlin.ModuleMapping; -import org.jetbrains.kotlin.load.kotlin.ModuleVisibilityUtilsKt; +import org.jetbrains.kotlin.load.kotlin.*; import org.jetbrains.kotlin.psi.Call; import org.jetbrains.kotlin.psi.KtFile; import org.jetbrains.kotlin.psi.KtFunction; @@ -43,7 +43,9 @@ import org.jetbrains.kotlin.resolve.DescriptorUtils; import org.jetbrains.kotlin.resolve.inline.InlineUtil; import org.jetbrains.kotlin.resolve.scopes.receivers.ReceiverValue; import org.jetbrains.kotlin.serialization.deserialization.descriptors.DeserializedCallableMemberDescriptor; +import org.jetbrains.kotlin.serialization.deserialization.descriptors.DeserializedClassDescriptor; import org.jetbrains.kotlin.types.KotlinType; +import org.jetbrains.org.objectweb.asm.Opcodes; import java.io.File; @@ -58,6 +60,32 @@ public class JvmCodegenUtil { private JvmCodegenUtil() { } + public static boolean isJvm6Interface(@NotNull DeclarationDescriptor descriptor, @NotNull GenerationState state) { + if (!DescriptorUtils.isInterface(descriptor)) { + return false; + } + + if (descriptor instanceof DeserializedClassDescriptor) { + SourceElement source = ((DeserializedClassDescriptor) descriptor).getSource(); + if (source instanceof KotlinJvmBinarySourceElement) { + KotlinJvmBinaryClass binaryClass = ((KotlinJvmBinarySourceElement) source).getBinaryClass(); + assert binaryClass instanceof FileBasedKotlinClass : + "KotlinJvmBinaryClass should be subclass of FileBasedKotlinClass, but " + binaryClass; + return ((FileBasedKotlinClass) binaryClass).getClassVersion() == Opcodes.V1_6; + } + } + return !state.isJvm8Target(); + } + + public static boolean isJvm8Interface(@NotNull DeclarationDescriptor descriptor, @NotNull GenerationState state) { + return DescriptorUtils.isInterface(descriptor) && !isJvm6Interface(descriptor, state); + } + + public static boolean isJvm8InterfaceMember(@NotNull CallableMemberDescriptor descriptor, @NotNull GenerationState state) { + DeclarationDescriptor declaration = descriptor.getContainingDeclaration(); + return DescriptorUtils.isInterface(declaration) && !isJvm6Interface(declaration, state); + } + public static boolean isJvmInterface(DeclarationDescriptor descriptor) { if (descriptor instanceof ClassDescriptor) { ClassKind kind = ((ClassDescriptor) descriptor).getKind(); diff --git a/compiler/testData/codegen/java8/box/jvm8/noDelegation/noDelegationToDefaultMethodInClass.kt b/compiler/testData/codegen/java8/box/jvm8/noDelegation/noDelegationToDefaultMethodInClass.kt new file mode 100644 index 00000000000..9e53d8d7249 --- /dev/null +++ b/compiler/testData/codegen/java8/box/jvm8/noDelegation/noDelegationToDefaultMethodInClass.kt @@ -0,0 +1,21 @@ +// KOTLIN_CONFIGURATION_FLAGS: +JVM.JVM_8_TARGET +// WITH_RUNTIME +// FULL_JDK +interface Test { + fun test() { + } +} + +class TestClass : Test { + +} + +fun box(): String { + try { + TestClass::class.java.getDeclaredMethod("test") + } + catch (e: NoSuchMethodException) { + return "OK" + } + return "fail" +} \ No newline at end of file diff --git a/compiler/testData/codegen/java8/box/jvm8/noDelegation/noDelegationToDefaultMethodInInterface.kt b/compiler/testData/codegen/java8/box/jvm8/noDelegation/noDelegationToDefaultMethodInInterface.kt new file mode 100644 index 00000000000..2c5148312ec --- /dev/null +++ b/compiler/testData/codegen/java8/box/jvm8/noDelegation/noDelegationToDefaultMethodInInterface.kt @@ -0,0 +1,21 @@ +// KOTLIN_CONFIGURATION_FLAGS: +JVM.JVM_8_TARGET +// WITH_RUNTIME +// FULL_JDK +interface Test { + fun test() { + } +} + +interface Test2 : Test { + +} + +fun box(): String { + try { + Test2::class.java.getDeclaredMethod("test") + } + catch (e: NoSuchMethodException) { + return "OK" + } + return "fail" +} \ No newline at end of file diff --git a/compiler/testData/codegen/java8/box/jvm8/noDelegation/noDelegationToDefaultMethodInInterface2.kt b/compiler/testData/codegen/java8/box/jvm8/noDelegation/noDelegationToDefaultMethodInInterface2.kt new file mode 100644 index 00000000000..29aae95f634 --- /dev/null +++ b/compiler/testData/codegen/java8/box/jvm8/noDelegation/noDelegationToDefaultMethodInInterface2.kt @@ -0,0 +1,25 @@ +// KOTLIN_CONFIGURATION_FLAGS: +JVM.JVM_8_TARGET +// WITH_RUNTIME +// FULL_JDK +interface Test { + fun test() { + } +} + +interface Test2 : Test { + +} + +interface Test3 : Test2 { + +} + +fun box(): String { + try { + Test3::class.java.getDeclaredMethod("test") + } + catch (e: NoSuchMethodException) { + return "OK" + } + return "fail" +} \ No newline at end of file diff --git a/compiler/testData/codegen/java8/box/jvm8/simpleCall.kt b/compiler/testData/codegen/java8/box/jvm8/simpleCall.kt new file mode 100644 index 00000000000..e2015b39958 --- /dev/null +++ b/compiler/testData/codegen/java8/box/jvm8/simpleCall.kt @@ -0,0 +1,14 @@ +// KOTLIN_CONFIGURATION_FLAGS: +JVM.JVM_8_TARGET +interface Test { + fun test(): String { + return "OK" + } +} + +class TestClass : Test { + +} + +fun box(): String { + return TestClass().test() +} \ No newline at end of file diff --git a/compiler/tests-java8/tests/org/jetbrains/kotlin/codegen/BlackBoxWithJava8CodegenTestGenerated.java b/compiler/tests-java8/tests/org/jetbrains/kotlin/codegen/BlackBoxWithJava8CodegenTestGenerated.java index 0b2cb9b7655..fe45c0996d5 100644 --- a/compiler/tests-java8/tests/org/jetbrains/kotlin/codegen/BlackBoxWithJava8CodegenTestGenerated.java +++ b/compiler/tests-java8/tests/org/jetbrains/kotlin/codegen/BlackBoxWithJava8CodegenTestGenerated.java @@ -119,6 +119,48 @@ public class BlackBoxWithJava8CodegenTestGenerated extends AbstractBlackBoxCodeg doTest(fileName); } + @TestMetadata("compiler/testData/codegen/java8/box/jvm8") + @TestDataPath("$PROJECT_ROOT") + @RunWith(JUnit3RunnerWithInners.class) + public static class Jvm8 extends AbstractBlackBoxCodegenTest { + public void testAllFilesPresentInJvm8() throws Exception { + KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("compiler/testData/codegen/java8/box/jvm8"), Pattern.compile("^(.+)\\.kt$"), true); + } + + @TestMetadata("simpleCall.kt") + public void testSimpleCall() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/java8/box/jvm8/simpleCall.kt"); + doTest(fileName); + } + + @TestMetadata("compiler/testData/codegen/java8/box/jvm8/noDelegation") + @TestDataPath("$PROJECT_ROOT") + @RunWith(JUnit3RunnerWithInners.class) + public static class NoDelegation extends AbstractBlackBoxCodegenTest { + public void testAllFilesPresentInNoDelegation() throws Exception { + KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("compiler/testData/codegen/java8/box/jvm8/noDelegation"), Pattern.compile("^(.+)\\.kt$"), true); + } + + @TestMetadata("noDelegationToDefaultMethodInClass.kt") + public void testNoDelegationToDefaultMethodInClass() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/java8/box/jvm8/noDelegation/noDelegationToDefaultMethodInClass.kt"); + doTest(fileName); + } + + @TestMetadata("noDelegationToDefaultMethodInInterface.kt") + public void testNoDelegationToDefaultMethodInInterface() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/java8/box/jvm8/noDelegation/noDelegationToDefaultMethodInInterface.kt"); + doTest(fileName); + } + + @TestMetadata("noDelegationToDefaultMethodInInterface2.kt") + public void testNoDelegationToDefaultMethodInInterface2() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/java8/box/jvm8/noDelegation/noDelegationToDefaultMethodInInterface2.kt"); + doTest(fileName); + } + } + } + @TestMetadata("compiler/testData/codegen/java8/box/mapRemove") @TestDataPath("$PROJECT_ROOT") @RunWith(JUnit3RunnerWithInners.class)