From e6b0cc64bde1cd600c197bbed290ebc01c3abb03 Mon Sep 17 00:00:00 2001 From: Alexey Andreev Date: Tue, 30 May 2017 17:17:40 +0300 Subject: [PATCH] Fix copying of non-abstract functions of interfaces in JS BE Fix copying of non-abstract functions of interfaces to implementing classes when implementing class inherits interface via 2+ paths. See KT-18187 --- .../interfaceWithNonAbstractFunIndirect.kt | 29 +++++++++++++++++++ .../testData/codegen/box/traits/kt3500.kt | 3 -- .../ir/IrBlackBoxCodegenTestGenerated.java | 6 ++++ .../codegen/BlackBoxCodegenTestGenerated.java | 6 ++++ .../LightAnalysisModeTestGenerated.java | 6 ++++ .../semantics/JsCodegenBoxTestGenerated.java | 14 ++++----- .../declaration/ClassModelGenerator.kt | 21 ++++++++++++-- 7 files changed, 73 insertions(+), 12 deletions(-) create mode 100644 compiler/testData/codegen/box/traits/interfaceWithNonAbstractFunIndirect.kt diff --git a/compiler/testData/codegen/box/traits/interfaceWithNonAbstractFunIndirect.kt b/compiler/testData/codegen/box/traits/interfaceWithNonAbstractFunIndirect.kt new file mode 100644 index 00000000000..074f6d18a2f --- /dev/null +++ b/compiler/testData/codegen/box/traits/interfaceWithNonAbstractFunIndirect.kt @@ -0,0 +1,29 @@ +interface I { + fun foo(): String = "foo" + + fun bar(x: String = "default") = "bar:$x" +} + +interface J : I + +interface K : J + +class A : I, J + +class B : K, I + +fun box(): String { + val foo = A().foo() + if (foo != "foo") return "fail1: $foo" + + val bar1 = A().bar() + if (bar1 != "bar:default") return "fail2: $bar1" + + val bar2 = A().bar("q") + if (bar2 != "bar:q") return "fail3: $bar1" + + val foo2 = B().foo() + if (foo2 != "foo") return "fail4: $foo2" + + return "OK" +} \ No newline at end of file diff --git a/compiler/testData/codegen/box/traits/kt3500.kt b/compiler/testData/codegen/box/traits/kt3500.kt index 92954f58c8d..18433df514a 100644 --- a/compiler/testData/codegen/box/traits/kt3500.kt +++ b/compiler/testData/codegen/box/traits/kt3500.kt @@ -1,6 +1,3 @@ -// TODO: muted automatically, investigate should it be ran for JS or not -// IGNORE_BACKEND: JS - interface BK { fun foo(): String = 10.toString() } diff --git a/compiler/tests-ir-jvm/tests/org/jetbrains/kotlin/codegen/ir/IrBlackBoxCodegenTestGenerated.java b/compiler/tests-ir-jvm/tests/org/jetbrains/kotlin/codegen/ir/IrBlackBoxCodegenTestGenerated.java index d75f2a2b4ed..5a7499c86ec 100644 --- a/compiler/tests-ir-jvm/tests/org/jetbrains/kotlin/codegen/ir/IrBlackBoxCodegenTestGenerated.java +++ b/compiler/tests-ir-jvm/tests/org/jetbrains/kotlin/codegen/ir/IrBlackBoxCodegenTestGenerated.java @@ -18044,6 +18044,12 @@ public class IrBlackBoxCodegenTestGenerated extends AbstractIrBlackBoxCodegenTes doTest(fileName); } + @TestMetadata("interfaceWithNonAbstractFunIndirect.kt") + public void testInterfaceWithNonAbstractFunIndirect() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/traits/interfaceWithNonAbstractFunIndirect.kt"); + doTest(fileName); + } + @TestMetadata("kt1936.kt") public void testKt1936() throws Exception { String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/traits/kt1936.kt"); diff --git a/compiler/tests/org/jetbrains/kotlin/codegen/BlackBoxCodegenTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/codegen/BlackBoxCodegenTestGenerated.java index 390c8955e3b..6f532a96e58 100644 --- a/compiler/tests/org/jetbrains/kotlin/codegen/BlackBoxCodegenTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/codegen/BlackBoxCodegenTestGenerated.java @@ -18044,6 +18044,12 @@ public class BlackBoxCodegenTestGenerated extends AbstractBlackBoxCodegenTest { doTest(fileName); } + @TestMetadata("interfaceWithNonAbstractFunIndirect.kt") + public void testInterfaceWithNonAbstractFunIndirect() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/traits/interfaceWithNonAbstractFunIndirect.kt"); + doTest(fileName); + } + @TestMetadata("kt1936.kt") public void testKt1936() throws Exception { String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/traits/kt1936.kt"); diff --git a/compiler/tests/org/jetbrains/kotlin/codegen/LightAnalysisModeTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/codegen/LightAnalysisModeTestGenerated.java index a9c710f0b62..c2e3316f629 100644 --- a/compiler/tests/org/jetbrains/kotlin/codegen/LightAnalysisModeTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/codegen/LightAnalysisModeTestGenerated.java @@ -18044,6 +18044,12 @@ public class LightAnalysisModeTestGenerated extends AbstractLightAnalysisModeTes doTest(fileName); } + @TestMetadata("interfaceWithNonAbstractFunIndirect.kt") + public void testInterfaceWithNonAbstractFunIndirect() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/traits/interfaceWithNonAbstractFunIndirect.kt"); + doTest(fileName); + } + @TestMetadata("kt1936.kt") public void testKt1936() throws Exception { String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/traits/kt1936.kt"); diff --git a/js/js.tests/test/org/jetbrains/kotlin/js/test/semantics/JsCodegenBoxTestGenerated.java b/js/js.tests/test/org/jetbrains/kotlin/js/test/semantics/JsCodegenBoxTestGenerated.java index 5bcef3105b0..98d40bdc74c 100644 --- a/js/js.tests/test/org/jetbrains/kotlin/js/test/semantics/JsCodegenBoxTestGenerated.java +++ b/js/js.tests/test/org/jetbrains/kotlin/js/test/semantics/JsCodegenBoxTestGenerated.java @@ -22148,6 +22148,12 @@ public class JsCodegenBoxTestGenerated extends AbstractJsCodegenBoxTest { throw new AssertionError("Looks like this test can be unmuted. Remove IGNORE_BACKEND directive for that."); } + @TestMetadata("interfaceWithNonAbstractFunIndirect.kt") + public void testInterfaceWithNonAbstractFunIndirect() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/traits/interfaceWithNonAbstractFunIndirect.kt"); + doTest(fileName); + } + @TestMetadata("kt1936.kt") public void testKt1936() throws Exception { String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/traits/kt1936.kt"); @@ -22193,13 +22199,7 @@ public class JsCodegenBoxTestGenerated extends AbstractJsCodegenBoxTest { @TestMetadata("kt3500.kt") public void testKt3500() throws Exception { String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/traits/kt3500.kt"); - try { - doTest(fileName); - } - catch (Throwable ignore) { - return; - } - throw new AssertionError("Looks like this test can be unmuted. Remove IGNORE_BACKEND directive for that."); + doTest(fileName); } @TestMetadata("kt3579.kt") diff --git a/js/js.translator/src/org/jetbrains/kotlin/js/translate/declaration/ClassModelGenerator.kt b/js/js.translator/src/org/jetbrains/kotlin/js/translate/declaration/ClassModelGenerator.kt index 4c8af069a4d..9aad236c5ea 100644 --- a/js/js.translator/src/org/jetbrains/kotlin/js/translate/declaration/ClassModelGenerator.kt +++ b/js/js.translator/src/org/jetbrains/kotlin/js/translate/declaration/ClassModelGenerator.kt @@ -156,7 +156,7 @@ class ClassModelGenerator(val context: StaticContext) { // If one of overridden members is non-abstract, copy it. // When none found, we have nothing to copy, ignore. // When multiple found, our current class should provide implementation, ignore. - val memberToCopy = member.overriddenDescriptors + val memberToCopy = member.findNonRepeatingOverriddenDescriptors { overriddenDescriptors } .filter { it.modality != Modality.ABSTRACT } .singleOrNull() ?: return null @@ -171,7 +171,7 @@ class ClassModelGenerator(val context: StaticContext) { // If one of overridden members has parameters with default value, copy it. // When non found, we have nothing to copy, ignore. // When multiple found, our current class should provide implementation, ignore. - val memberToCopy = member.overriddenDescriptors + val memberToCopy = member.findNonRepeatingOverriddenDescriptors { overriddenDescriptors } .filter { it.hasOrInheritsParametersWithDefaultValue() } .singleOrNull() ?: return null @@ -182,6 +182,23 @@ class ClassModelGenerator(val context: StaticContext) { return if (!memberToCopy.kind.isReal) findOptionalArgsMemberToCopy(memberToCopy) else memberToCopy } + private fun T.findNonRepeatingOverriddenDescriptors( + getTypedOverriddenDescriptors: T.() -> Collection + ): List { + val allDescriptors = mutableSetOf() + val repeatedDescriptors = mutableSetOf() + fun walk(descriptor: T) { + if (!allDescriptors.add(descriptor)) return + val overridden = descriptor.getTypedOverriddenDescriptors() + repeatedDescriptors += overridden + overridden.forEach { walk(it) } + } + + val directOverriddenDescriptors = getTypedOverriddenDescriptors() + directOverriddenDescriptors.forEach { walk(it) } + return directOverriddenDescriptors.filter { it !in repeatedDescriptors } + } + private fun generateBridgeMethods(descriptor: ClassDescriptor, model: JsClassModel) { generateBridgesToTraitImpl(descriptor, model) generateOtherBridges(descriptor, model)