diff --git a/compiler/backend/src/org/jetbrains/kotlin/codegen/ClassBodyCodegen.java b/compiler/backend/src/org/jetbrains/kotlin/codegen/ClassBodyCodegen.java index 430c82aa9bf..d4bff188e13 100644 --- a/compiler/backend/src/org/jetbrains/kotlin/codegen/ClassBodyCodegen.java +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/ClassBodyCodegen.java @@ -222,8 +222,11 @@ public abstract class ClassBodyCodegen extends MemberCodegen entry : CodegenUtil.getNonPrivateTraitMethods(descriptor).entrySet()) { FunctionDescriptor interfaceFun = entry.getKey(); //skip java 8 default methods - if (!CodegenUtilKt.isDefinitelyNotDefaultImplsMethod(interfaceFun) && !JvmAnnotationUtilKt - .isCompiledToJvmDefaultIfNoAbstract(interfaceFun, state.getJvmDefaultMode())) { + if (!CodegenUtilKt.isDefinitelyNotDefaultImplsMethod(interfaceFun) && + !JvmAnnotationUtilKt.isCallableMemberCompiledToJvmDefaultIfNoAbstract( + DescriptorUtils.unwrapFakeOverrideToAnyDeclaration(interfaceFun), state.getJvmDefaultMode() + ) + ) { generateDelegationToDefaultImpl(interfaceFun, entry.getValue()); } } diff --git a/compiler/backend/src/org/jetbrains/kotlin/codegen/FunctionCodegen.java b/compiler/backend/src/org/jetbrains/kotlin/codegen/FunctionCodegen.java index 8395cc30d0d..b72d71e9382 100644 --- a/compiler/backend/src/org/jetbrains/kotlin/codegen/FunctionCodegen.java +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/FunctionCodegen.java @@ -732,7 +732,9 @@ public class FunctionCodegen { @NotNull JvmDefaultMode jvmDefaultMode ) { return OwnerKind.DEFAULT_IMPLS == context.getContextKind() && - JvmAnnotationUtilKt.isCompiledToJvmDefaultIfNoAbstract(functionDescriptor, jvmDefaultMode) && + JvmAnnotationUtilKt + .isCompiledToJvmDefaultIfNoAbstract(DescriptorUtils.unwrapFakeOverrideToAnyDeclaration(functionDescriptor), + jvmDefaultMode) && jvmDefaultMode.isCompatibility(); } diff --git a/compiler/backend/src/org/jetbrains/kotlin/codegen/state/KotlinTypeMapper.kt b/compiler/backend/src/org/jetbrains/kotlin/codegen/state/KotlinTypeMapper.kt index ee796566f79..38280bb6644 100644 --- a/compiler/backend/src/org/jetbrains/kotlin/codegen/state/KotlinTypeMapper.kt +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/state/KotlinTypeMapper.kt @@ -1540,7 +1540,7 @@ class KotlinTypeMapper @JvmOverloads constructor( return if (descriptor is AccessorForConstructorDescriptor) false else isAccessor(descriptor) } - private fun findAnyDeclaration(function: FunctionDescriptor): FunctionDescriptor { + internal fun findAnyDeclaration(function: FunctionDescriptor): FunctionDescriptor { return if (function.kind == CallableMemberDescriptor.Kind.DECLARATION) { function } else findBaseDeclaration(function) diff --git a/compiler/frontend.java/src/org/jetbrains/kotlin/resolve/jvm/annotations/jvmAnnotationUtil.kt b/compiler/frontend.java/src/org/jetbrains/kotlin/resolve/jvm/annotations/jvmAnnotationUtil.kt index e33465d15c3..da58ad13c0b 100644 --- a/compiler/frontend.java/src/org/jetbrains/kotlin/resolve/jvm/annotations/jvmAnnotationUtil.kt +++ b/compiler/frontend.java/src/org/jetbrains/kotlin/resolve/jvm/annotations/jvmAnnotationUtil.kt @@ -8,13 +8,17 @@ package org.jetbrains.kotlin.resolve.jvm.annotations import org.jetbrains.kotlin.config.JvmDefaultMode import org.jetbrains.kotlin.descriptors.CallableMemberDescriptor import org.jetbrains.kotlin.descriptors.DeclarationDescriptor +import org.jetbrains.kotlin.descriptors.Modality import org.jetbrains.kotlin.descriptors.PropertyDescriptor import org.jetbrains.kotlin.descriptors.annotations.Annotated import org.jetbrains.kotlin.descriptors.annotations.AnnotationDescriptor import org.jetbrains.kotlin.descriptors.deserialization.PLATFORM_DEPENDENT_ANNOTATION_FQ_NAME import org.jetbrains.kotlin.load.java.JvmAbi.JVM_FIELD_ANNOTATION_FQ_NAME +import org.jetbrains.kotlin.metadata.jvm.deserialization.JvmProtoBufUtil import org.jetbrains.kotlin.name.FqName import org.jetbrains.kotlin.resolve.DescriptorUtils +import org.jetbrains.kotlin.resolve.DescriptorUtils.isInterface +import org.jetbrains.kotlin.serialization.deserialization.descriptors.DeserializedClassDescriptor val JVM_DEFAULT_FQ_NAME = FqName("kotlin.jvm.JvmDefault") val JVM_OVERLOADS_FQ_NAME = FqName("kotlin.jvm.JvmOverloads") @@ -44,8 +48,15 @@ fun DeclarationDescriptor.hasJvmFieldAnnotation(): Boolean = fun DeclarationDescriptor.isCallableMemberCompiledToJvmDefaultIfNoAbstract(jvmDefault: JvmDefaultMode): Boolean = this is CallableMemberDescriptor && isCompiledToJvmDefaultIfNoAbstract(jvmDefault) -fun CallableMemberDescriptor.isCompiledToJvmDefaultIfNoAbstract(jvmDefault: JvmDefaultMode): Boolean = - jvmDefault.forAllMehtodsWithBody || DescriptorUtils.getDirectMember(this).annotations.hasAnnotation(JVM_DEFAULT_FQ_NAME) +fun CallableMemberDescriptor.isCompiledToJvmDefaultIfNoAbstract(jvmDefault: JvmDefaultMode): Boolean { + val directMember = DescriptorUtils.getDirectMember(this) + + val clazz = directMember.containingDeclaration + + if (directMember.annotations.hasAnnotation(JVM_DEFAULT_FQ_NAME)) return true + if (clazz !is DeserializedClassDescriptor) return jvmDefault.forAllMethodsWithBody + return JvmProtoBufUtil.isNewPlaceForBodyGeneration(clazz.classProto) +} fun CallableMemberDescriptor.hasJvmDefaultAnnotation(): Boolean = DescriptorUtils.getDirectMember(this).annotations.hasAnnotation(JVM_DEFAULT_FQ_NAME) diff --git a/compiler/testData/compileKotlinAgainstKotlin/jvm8/defaults/interop/newAndOldSchemes.kt b/compiler/testData/compileKotlinAgainstKotlin/jvm8/defaults/interop/newAndOldSchemes.kt new file mode 100644 index 00000000000..733e39bdb70 --- /dev/null +++ b/compiler/testData/compileKotlinAgainstKotlin/jvm8/defaults/interop/newAndOldSchemes.kt @@ -0,0 +1,36 @@ +// FULL_JDK +// FILE: 1.kt +interface KInterface { + fun call(): List { + return Thread.currentThread().getStackTrace().map { it.className + "." + it.methodName } + } + + fun superCall() = Thread.currentThread().getStackTrace().map { it.className + "." + it.methodName } +} + +// FILE: main.kt +// !JVM_DEFAULT_MODE: all-compatibility +// JVM_TARGET: 1.8 +interface KInterface2 : KInterface { + override fun superCall() = super.superCall() +} + +class Foo: KInterface2 { + fun superCall2() = super.superCall() +} + +fun box(): String { + var result = Foo().call() + if (result[1] != "KInterface\$DefaultImpls.call") return "fail 1: ${result[1]}" + if (result[2] != "KInterface2\$DefaultImpls.call") return "fail 2: ${result[2]}" + if (result[3] != "Foo.call") return "fail 3: ${result[3]}" + if (result[4] != "MainKt.box") return "fail 4: ${result[4]}" + + result = Foo().superCall2() + if (result[1] != "KInterface\$DefaultImpls.superCall") return "fail 1: ${result[1]}" + if (result[2] != "KInterface2.superCall") return "fail 2: ${result[2]}" + if (result[3] != "Foo.superCall2") return "fail 3: ${result[3]}" + if (result[4] != "MainKt.box") return "fail 4: ${result[4]}" + + return "OK" +} diff --git a/compiler/testData/compileKotlinAgainstKotlin/jvm8/defaults/interop/newSchemeWithJvmDefault.kt b/compiler/testData/compileKotlinAgainstKotlin/jvm8/defaults/interop/newSchemeWithJvmDefault.kt new file mode 100644 index 00000000000..91a334437d9 --- /dev/null +++ b/compiler/testData/compileKotlinAgainstKotlin/jvm8/defaults/interop/newSchemeWithJvmDefault.kt @@ -0,0 +1,38 @@ +// FULL_JDK +// WITH_RUNTIME +// JVM_TARGET: 1.8 +// FILE: 1.kt +// !JVM_DEFAULT_MODE: enable +interface KInterface { + @JvmDefault + fun call(): List { + return Thread.currentThread().getStackTrace().map { it.className + "." + it.methodName } + } + + fun superCall() = Thread.currentThread().getStackTrace().map { it.className + "." + it.methodName } +} + +// FILE: main.kt +// !JVM_DEFAULT_MODE: all +interface KInterface2 : KInterface { + override fun superCall() = super.superCall() +} + +class Foo: KInterface2 { + fun superCall2() = super.superCall() +} + +fun box(): String { + var result = Foo().call() + if (result[1] != "KInterface.call") return "fail 1: ${result[1]}" + if (result[2] != "MainKt.box") return "fail 2: ${result[2]}" + + result = Foo().superCall2() + if (result[1] != "KInterface\$DefaultImpls.superCall") return "fail 1: ${result[1]}" + if (result[2] != "KInterface2.superCall") return "fail 2: ${result[2]}" + if (result[3] != "Foo.superCall2") return "fail 3: ${result[3]}" + if (result[4] != "MainKt.box") return "fail 4: ${result[4]}" + + + return "OK" +} diff --git a/compiler/tests/org/jetbrains/kotlin/codegen/CompileKotlinAgainstKotlinTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/codegen/CompileKotlinAgainstKotlinTestGenerated.java index 2652475ea6c..f086a95ff7e 100644 --- a/compiler/tests/org/jetbrains/kotlin/codegen/CompileKotlinAgainstKotlinTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/codegen/CompileKotlinAgainstKotlinTestGenerated.java @@ -459,6 +459,29 @@ public class CompileKotlinAgainstKotlinTestGenerated extends AbstractCompileKotl runTest("compiler/testData/compileKotlinAgainstKotlin/jvm8/defaults/allCompatibility/superPropAccessFromInterface2.kt"); } } + + @TestMetadata("compiler/testData/compileKotlinAgainstKotlin/jvm8/defaults/interop") + @TestDataPath("$PROJECT_ROOT") + @RunWith(JUnit3RunnerWithInners.class) + public static class Interop extends AbstractCompileKotlinAgainstKotlinTest { + private void runTest(String testDataFilePath) throws Exception { + KotlinTestUtils.runTest(this::doTest, this, testDataFilePath); + } + + public void testAllFilesPresentInInterop() throws Exception { + KotlinTestUtils.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("compiler/testData/compileKotlinAgainstKotlin/jvm8/defaults/interop"), Pattern.compile("^(.+)\\.kt$"), null, true); + } + + @TestMetadata("newAndOldSchemes.kt") + public void testNewAndOldSchemes() throws Exception { + runTest("compiler/testData/compileKotlinAgainstKotlin/jvm8/defaults/interop/newAndOldSchemes.kt"); + } + + @TestMetadata("newSchemeWithJvmDefault.kt") + public void testNewSchemeWithJvmDefault() throws Exception { + runTest("compiler/testData/compileKotlinAgainstKotlin/jvm8/defaults/interop/newSchemeWithJvmDefault.kt"); + } + } } @TestMetadata("compiler/testData/compileKotlinAgainstKotlin/jvm8/jvm8against6") diff --git a/compiler/tests/org/jetbrains/kotlin/codegen/ir/IrCompileKotlinAgainstKotlinTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/codegen/ir/IrCompileKotlinAgainstKotlinTestGenerated.java index bac2aea55e7..de862a26456 100644 --- a/compiler/tests/org/jetbrains/kotlin/codegen/ir/IrCompileKotlinAgainstKotlinTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/codegen/ir/IrCompileKotlinAgainstKotlinTestGenerated.java @@ -454,6 +454,29 @@ public class IrCompileKotlinAgainstKotlinTestGenerated extends AbstractIrCompile runTest("compiler/testData/compileKotlinAgainstKotlin/jvm8/defaults/allCompatibility/superPropAccessFromInterface2.kt"); } } + + @TestMetadata("compiler/testData/compileKotlinAgainstKotlin/jvm8/defaults/interop") + @TestDataPath("$PROJECT_ROOT") + @RunWith(JUnit3RunnerWithInners.class) + public static class Interop extends AbstractIrCompileKotlinAgainstKotlinTest { + private void runTest(String testDataFilePath) throws Exception { + KotlinTestUtils.runTest(this::doTest, TargetBackend.JVM_IR, testDataFilePath); + } + + public void testAllFilesPresentInInterop() throws Exception { + KotlinTestUtils.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("compiler/testData/compileKotlinAgainstKotlin/jvm8/defaults/interop"), Pattern.compile("^(.+)\\.kt$"), null, TargetBackend.JVM_IR, true); + } + + @TestMetadata("newAndOldSchemes.kt") + public void testNewAndOldSchemes() throws Exception { + runTest("compiler/testData/compileKotlinAgainstKotlin/jvm8/defaults/interop/newAndOldSchemes.kt"); + } + + @TestMetadata("newSchemeWithJvmDefault.kt") + public void testNewSchemeWithJvmDefault() throws Exception { + runTest("compiler/testData/compileKotlinAgainstKotlin/jvm8/defaults/interop/newSchemeWithJvmDefault.kt"); + } + } } @TestMetadata("compiler/testData/compileKotlinAgainstKotlin/jvm8/jvm8against6")