diff --git a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/lower/BridgesConstruction.kt b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/lower/BridgesConstruction.kt index c3e673252b6..b70388435c0 100644 --- a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/lower/BridgesConstruction.kt +++ b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/lower/BridgesConstruction.kt @@ -146,8 +146,10 @@ abstract class BridgesConstruction(val context: T) : copyReceiverParametersFrom(bridge, substitutionMap) copyValueParametersFrom(bridge, substitutionMap) annotations += bridge.annotations - overriddenSymbols += delegateTo.overriddenSymbols + // the js function signature building process (jsFunctionSignature()) uses dfs throught overriddenSymbols for getting js name, + // therefore it is very important to put bridge symbol at the beginning, it allows to get correct js function name overriddenSymbols += bridge.symbol + overriddenSymbols += delegateTo.overriddenSymbols } irFunction.body = context.irFactory.createBlockBody(UNDEFINED_OFFSET, UNDEFINED_OFFSET) { diff --git a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/utils/AnnotationUtils.kt b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/utils/AnnotationUtils.kt index e5281f476e7..0a3ee739b55 100644 --- a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/utils/AnnotationUtils.kt +++ b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/utils/AnnotationUtils.kt @@ -66,13 +66,25 @@ fun IrAnnotationContainer.isJsNativeInvoke(): Boolean = hasAnnotation(JsAnnotati fun IrAnnotationContainer.isAnnotatedWithJsFun(): Boolean = hasAnnotation(JsAnnotations.jsFunFqn) +private fun IrOverridableDeclaration<*>.dfsOverridableJsNameOrNull(): String? { + for (overriddenSymbol in overriddenSymbols) { + val symbolOwner = overriddenSymbol.owner + if (symbolOwner is IrAnnotationContainer) { + symbolOwner.getJsName()?.let { return it } + } + if (symbolOwner is IrOverridableDeclaration<*>) { + symbolOwner.dfsOverridableJsNameOrNull()?.let { return it } + } + } + return null +} + fun IrDeclarationWithName.getJsNameForOverriddenDeclaration(): String? { val jsName = getJsName() return when { jsName != null -> jsName - this is IrOverridableDeclaration<*> -> - overriddenSymbols.firstNotNullOfOrNull { (it.owner as? IrAnnotationContainer)?.getJsName() } + this is IrOverridableDeclaration<*> -> dfsOverridableJsNameOrNull() else -> null } } diff --git a/js/js.tests/tests-gen/org/jetbrains/kotlin/js/test/BoxJsTestGenerated.java b/js/js.tests/tests-gen/org/jetbrains/kotlin/js/test/BoxJsTestGenerated.java index 0f6b108220e..7d9834761af 100644 --- a/js/js.tests/tests-gen/org/jetbrains/kotlin/js/test/BoxJsTestGenerated.java +++ b/js/js.tests/tests-gen/org/jetbrains/kotlin/js/test/BoxJsTestGenerated.java @@ -6570,6 +6570,36 @@ public class BoxJsTestGenerated extends AbstractBoxJsTest { runTest("js/js.translator/testData/box/jsName/jsTopLevelRenameReserved.kt"); } + @Test + @TestMetadata("methodOfAbstractClass.kt") + public void testMethodOfAbstractClass() throws Exception { + runTest("js/js.translator/testData/box/jsName/methodOfAbstractClass.kt"); + } + + @Test + @TestMetadata("methodOfInterface.kt") + public void testMethodOfInterface() throws Exception { + runTest("js/js.translator/testData/box/jsName/methodOfInterface.kt"); + } + + @Test + @TestMetadata("methodOfInterfaceWithDefinition.kt") + public void testMethodOfInterfaceWithDefinition() throws Exception { + runTest("js/js.translator/testData/box/jsName/methodOfInterfaceWithDefinition.kt"); + } + + @Test + @TestMetadata("methodOfMultipleInterface.kt") + public void testMethodOfMultipleInterface() throws Exception { + runTest("js/js.translator/testData/box/jsName/methodOfMultipleInterface.kt"); + } + + @Test + @TestMetadata("methodOfOpenClass.kt") + public void testMethodOfOpenClass() throws Exception { + runTest("js/js.translator/testData/box/jsName/methodOfOpenClass.kt"); + } + @Test @TestMetadata("overriddenMethod.kt") public void testOverriddenMethod() throws Exception { diff --git a/js/js.tests/tests-gen/org/jetbrains/kotlin/js/test/ir/FirJsTestGenerated.java b/js/js.tests/tests-gen/org/jetbrains/kotlin/js/test/ir/FirJsTestGenerated.java index 8ba8296713d..6525ba1080e 100644 --- a/js/js.tests/tests-gen/org/jetbrains/kotlin/js/test/ir/FirJsTestGenerated.java +++ b/js/js.tests/tests-gen/org/jetbrains/kotlin/js/test/ir/FirJsTestGenerated.java @@ -7036,6 +7036,36 @@ public class FirJsTestGenerated extends AbstractFirJsTest { runTest("js/js.translator/testData/box/jsName/jsTopLevelRenameReserved.kt"); } + @Test + @TestMetadata("methodOfAbstractClass.kt") + public void testMethodOfAbstractClass() throws Exception { + runTest("js/js.translator/testData/box/jsName/methodOfAbstractClass.kt"); + } + + @Test + @TestMetadata("methodOfInterface.kt") + public void testMethodOfInterface() throws Exception { + runTest("js/js.translator/testData/box/jsName/methodOfInterface.kt"); + } + + @Test + @TestMetadata("methodOfInterfaceWithDefinition.kt") + public void testMethodOfInterfaceWithDefinition() throws Exception { + runTest("js/js.translator/testData/box/jsName/methodOfInterfaceWithDefinition.kt"); + } + + @Test + @TestMetadata("methodOfMultipleInterface.kt") + public void testMethodOfMultipleInterface() throws Exception { + runTest("js/js.translator/testData/box/jsName/methodOfMultipleInterface.kt"); + } + + @Test + @TestMetadata("methodOfOpenClass.kt") + public void testMethodOfOpenClass() throws Exception { + runTest("js/js.translator/testData/box/jsName/methodOfOpenClass.kt"); + } + @Test @TestMetadata("overriddenMethod.kt") public void testOverriddenMethod() throws Exception { diff --git a/js/js.tests/tests-gen/org/jetbrains/kotlin/js/test/ir/IrBoxJsTestGenerated.java b/js/js.tests/tests-gen/org/jetbrains/kotlin/js/test/ir/IrBoxJsTestGenerated.java index d42915fa64f..4e2cfa56be3 100644 --- a/js/js.tests/tests-gen/org/jetbrains/kotlin/js/test/ir/IrBoxJsTestGenerated.java +++ b/js/js.tests/tests-gen/org/jetbrains/kotlin/js/test/ir/IrBoxJsTestGenerated.java @@ -7036,6 +7036,36 @@ public class IrBoxJsTestGenerated extends AbstractIrBoxJsTest { runTest("js/js.translator/testData/box/jsName/jsTopLevelRenameReserved.kt"); } + @Test + @TestMetadata("methodOfAbstractClass.kt") + public void testMethodOfAbstractClass() throws Exception { + runTest("js/js.translator/testData/box/jsName/methodOfAbstractClass.kt"); + } + + @Test + @TestMetadata("methodOfInterface.kt") + public void testMethodOfInterface() throws Exception { + runTest("js/js.translator/testData/box/jsName/methodOfInterface.kt"); + } + + @Test + @TestMetadata("methodOfInterfaceWithDefinition.kt") + public void testMethodOfInterfaceWithDefinition() throws Exception { + runTest("js/js.translator/testData/box/jsName/methodOfInterfaceWithDefinition.kt"); + } + + @Test + @TestMetadata("methodOfMultipleInterface.kt") + public void testMethodOfMultipleInterface() throws Exception { + runTest("js/js.translator/testData/box/jsName/methodOfMultipleInterface.kt"); + } + + @Test + @TestMetadata("methodOfOpenClass.kt") + public void testMethodOfOpenClass() throws Exception { + runTest("js/js.translator/testData/box/jsName/methodOfOpenClass.kt"); + } + @Test @TestMetadata("overriddenMethod.kt") public void testOverriddenMethod() throws Exception { diff --git a/js/js.translator/testData/box/jsName/methodOfAbstractClass.kt b/js/js.translator/testData/box/jsName/methodOfAbstractClass.kt new file mode 100644 index 00000000000..a7f64a27211 --- /dev/null +++ b/js/js.translator/testData/box/jsName/methodOfAbstractClass.kt @@ -0,0 +1,36 @@ +@JsExport +abstract class TestOpenClass { + @JsName("testName") + abstract fun testFunction(): String + abstract fun testFunction(x: String): String +} + +abstract class TestOpenClassA : TestOpenClass() { + override abstract fun testFunction(): String + override abstract fun testFunction(x: String): String +} + +class TestClassA : TestOpenClassA() { + override fun testFunction(): String = "TestClassA" + override fun testFunction(x: String): String = "TestClassA: ${x}" +} + +fun testTestOpenClass1(x: TestOpenClass) = x.testFunction() +fun testTestOpenClass2(x: TestOpenClass) = x.testFunction("OK") + +fun testTestOpenClassA1(x: TestOpenClassA) = x.testFunction() +fun testTestOpenClassA2(x: TestOpenClassA) = x.testFunction("OK") + +fun testTestClassA1(x: TestClassA) = x.testFunction() +fun testTestClassA2(x: TestClassA) = x.testFunction("OK") + +fun box(): String { + assertEquals("TestClassA", testTestOpenClass1(TestClassA())) + assertEquals("TestClassA: OK", testTestOpenClass2(TestClassA())) + assertEquals("TestClassA", testTestOpenClassA1(TestClassA())) + assertEquals("TestClassA: OK", testTestOpenClassA2(TestClassA())) + assertEquals("TestClassA", testTestClassA1(TestClassA())) + assertEquals("TestClassA: OK", testTestClassA2(TestClassA())) + + return "OK" +} diff --git a/js/js.translator/testData/box/jsName/methodOfInterface.kt b/js/js.translator/testData/box/jsName/methodOfInterface.kt new file mode 100644 index 00000000000..235718b3a08 --- /dev/null +++ b/js/js.translator/testData/box/jsName/methodOfInterface.kt @@ -0,0 +1,37 @@ +interface TestInterface { + @JsName("testName") + fun testFunction(): String + fun testFunction(x: String): String +} + +interface TestInterfaceA : TestInterface { + override fun testFunction(): String + override fun testFunction(x: String): String +} + +class TestClassA : TestInterfaceA { + override fun testFunction(): String = "TestClassA" + override fun testFunction(x: String): String = "TestClassA: $x" +} + +fun testTestInterface1(x: TestInterface) = x.testFunction() +fun testTestInterface2(x: TestInterface) = x.testFunction("OK") + +fun testTestInterfaceA1(x: TestInterfaceA) = x.testFunction() +fun testTestInterfaceA2(x: TestInterfaceA) = x.testFunction("OK") + +fun testTestClassA1(x: TestClassA) = x.testFunction() +fun testTestClassA2(x: TestClassA) = x.testFunction("OK") + +fun box(): String { + assertEquals("TestClassA", testTestInterface1(TestClassA())) + assertEquals("TestClassA: OK", testTestInterface2(TestClassA())) + + assertEquals("TestClassA", testTestInterfaceA1(TestClassA())) + assertEquals("TestClassA: OK", testTestInterfaceA2(TestClassA())) + + assertEquals("TestClassA", testTestClassA1(TestClassA())) + assertEquals("TestClassA: OK", testTestClassA2(TestClassA())) + + return "OK" +} diff --git a/js/js.translator/testData/box/jsName/methodOfInterfaceWithDefinition.kt b/js/js.translator/testData/box/jsName/methodOfInterfaceWithDefinition.kt new file mode 100644 index 00000000000..c637f282e9b --- /dev/null +++ b/js/js.translator/testData/box/jsName/methodOfInterfaceWithDefinition.kt @@ -0,0 +1,43 @@ +interface TestInterface { + @JsName("testName") + open fun testFunction(): String = "TestInterface" + open fun testFunction(x: String): String = "TestInterface: ${x}" +} + +interface TestInterfaceA : TestInterface { + override fun testFunction(): String = "TestInterfaceA" + override fun testFunction(x: String): String = "TestInterfaceA: ${x}" +} + +class TestClassA : TestInterfaceA { + override fun testFunction(): String = "TestClassA" + override fun testFunction(x: String): String = "TestClassA: ${x}" +} + +fun testTestInterface1(x: TestInterface) = x.testFunction() +fun testTestInterface2(x: TestInterface) = x.testFunction("OK") + +fun testTestInterfaceA1(x: TestInterfaceA) = x.testFunction() +fun testTestInterfaceA2(x: TestInterfaceA) = x.testFunction("OK") + +fun testTestClassA1(x: TestClassA) = x.testFunction() +fun testTestClassA2(x: TestClassA) = x.testFunction("OK") + +fun box(): String { + assertEquals("TestInterface", testTestInterface1(object : TestInterface {})) + assertEquals("TestInterface: OK", testTestInterface2(object : TestInterface {})) + + assertEquals("TestInterfaceA", testTestInterface1(object : TestInterfaceA {})) + assertEquals("TestInterfaceA: OK", testTestInterface2(object : TestInterfaceA {})) + assertEquals("TestInterfaceA", testTestInterfaceA1(object : TestInterfaceA {})) + assertEquals("TestInterfaceA: OK", testTestInterfaceA2(object : TestInterfaceA {})) + + assertEquals("TestClassA", testTestInterface1(TestClassA())) + assertEquals("TestClassA: OK", testTestInterface2(TestClassA())) + assertEquals("TestClassA", testTestInterfaceA1(TestClassA())) + assertEquals("TestClassA: OK", testTestInterfaceA2(TestClassA())) + assertEquals("TestClassA", testTestClassA1(TestClassA())) + assertEquals("TestClassA: OK", testTestClassA2(TestClassA())) + + return "OK" +} diff --git a/js/js.translator/testData/box/jsName/methodOfMultipleInterface.kt b/js/js.translator/testData/box/jsName/methodOfMultipleInterface.kt new file mode 100644 index 00000000000..5ff474f34cb --- /dev/null +++ b/js/js.translator/testData/box/jsName/methodOfMultipleInterface.kt @@ -0,0 +1,78 @@ +interface TestInterfaceA { + @JsName("testNameA") + fun testFunction(): String +} +fun testTestInterfaceA(x: TestInterfaceA) = x.testFunction() + +interface TestInterfaceB { + @JsName("testNameB") + fun testFunction(): String +} +fun testTestInterfaceB(x: TestInterfaceB) = x.testFunction() + +interface TestInterfaceAA: TestInterfaceA { + override fun testFunction(): String +} +fun testTestInterfaceAA(x: TestInterfaceAA) = x.testFunction() + +interface TestInterfaceBAA : TestInterfaceB, TestInterfaceAA { + override fun testFunction(): String +} +fun testTestInterfaceBAA(x: TestInterfaceBAA) = x.testFunction() + +class TestClassBAA : TestInterfaceBAA { + override fun testFunction(): String = "TestClassBAA" +} +fun testTestClassBAA(x: TestClassBAA) = x.testFunction() + +interface TestInterfaceBB: TestInterfaceB { + override fun testFunction(): String +} +fun testTestInterfaceBB(x: TestInterfaceBB) = x.testFunction() + +interface TestInterfaceABB : TestInterfaceA, TestInterfaceBB { + override fun testFunction(): String +} +fun testTestInterfaceABB(x: TestInterfaceABB) = x.testFunction() + +class TestClassABB : TestInterfaceABB { + override fun testFunction(): String = "TestClassABB" +} +fun testTestClassABB(x: TestClassABB) = x.testFunction() + +class TestClassABBAA : TestInterfaceABB, TestInterfaceAA { + override fun testFunction(): String = "TestClassABBAA" +} +fun testTestClassABBAA(x: TestClassABBAA) = x.testFunction() + +fun box(): String { + TestClassBAA().also { + assertEquals("TestClassBAA", testTestInterfaceA(it)) + assertEquals("TestClassBAA", testTestInterfaceB(it)) + + assertEquals("TestClassBAA", testTestInterfaceAA(it)) + assertEquals("TestClassBAA", testTestInterfaceBAA(it)) + assertEquals("TestClassBAA", testTestClassBAA(it)) + } + + TestClassABB().also { + assertEquals("TestClassABB", testTestInterfaceA(it)) + assertEquals("TestClassABB", testTestInterfaceB(it)) + + assertEquals("TestClassABB", testTestInterfaceBB(it)) + assertEquals("TestClassABB", testTestInterfaceABB(it)) + assertEquals("TestClassABB", testTestClassABB(it)) + } + + TestClassABBAA().also { + assertEquals("TestClassABBAA", testTestInterfaceA(it)) + assertEquals("TestClassABBAA", testTestInterfaceB(it)) + + assertEquals("TestClassABBAA", testTestInterfaceAA(it)) + assertEquals("TestClassABBAA", testTestInterfaceBB(it)) + assertEquals("TestClassABBAA", testTestInterfaceABB(it)) + + assertEquals("TestClassABBAA", testTestClassABBAA(it)) + } + return "OK" +} diff --git a/js/js.translator/testData/box/jsName/methodOfOpenClass.kt b/js/js.translator/testData/box/jsName/methodOfOpenClass.kt new file mode 100644 index 00000000000..813c32593b5 --- /dev/null +++ b/js/js.translator/testData/box/jsName/methodOfOpenClass.kt @@ -0,0 +1,44 @@ +@JsExport +open class TestOpenClass { + @JsName("testName") + open fun testFunction(): String = "TestOpenClass" + open fun testFunction(x: String): String = "TestOpenClass: ${x}" +} + +open class TestOpenClassA : TestOpenClass() { + override fun testFunction(): String = "TestOpenClassA" + override fun testFunction(x: String): String = "TestOpenClassA: ${x}" +} + +class TestClassA : TestOpenClassA() { + override fun testFunction(): String = "TestClassA" + override fun testFunction(x: String): String = "TestClassA: ${x}" +} + +fun testTestOpenClass1(x: TestOpenClass) = x.testFunction() +fun testTestOpenClass2(x: TestOpenClass) = x.testFunction("OK") + +fun testTestOpenClassA1(x: TestOpenClassA) = x.testFunction() +fun testTestOpenClassA2(x: TestOpenClassA) = x.testFunction("OK") + +fun testTestClassA1(x: TestClassA) = x.testFunction() +fun testTestClassA2(x: TestClassA) = x.testFunction("OK") + +fun box(): String { + assertEquals("TestOpenClass", testTestOpenClass1(TestOpenClass())) + assertEquals("TestOpenClass: OK", testTestOpenClass2(TestOpenClass())) + + assertEquals("TestOpenClassA", testTestOpenClass1(TestOpenClassA())) + assertEquals("TestOpenClassA: OK", testTestOpenClass2(TestOpenClassA())) + assertEquals("TestOpenClassA", testTestOpenClassA1(TestOpenClassA())) + assertEquals("TestOpenClassA: OK", testTestOpenClassA2(TestOpenClassA())) + + assertEquals("TestClassA", testTestOpenClass1(TestClassA())) + assertEquals("TestClassA: OK", testTestOpenClass2(TestClassA())) + assertEquals("TestClassA", testTestOpenClassA1(TestClassA())) + assertEquals("TestClassA: OK", testTestOpenClassA2(TestClassA())) + assertEquals("TestClassA", testTestClassA1(TestClassA())) + assertEquals("TestClassA: OK", testTestClassA2(TestClassA())) + + return "OK" +}