[JS IR] Fix clashes between bridge and delegated function call

The patch fixes the js function signature rules to avoid clashes
 between bridge and delegated call. Use overridden symbols dfs of
 JsName annotation in order to get the correct bridge name.

^KT-52968 Fixed
This commit is contained in:
Alexander Korepanov
2022-07-20 16:01:18 +02:00
committed by Space
parent 6525f7a7ac
commit caa1570e25
10 changed files with 345 additions and 3 deletions
@@ -146,8 +146,10 @@ abstract class BridgesConstruction<T : JsCommonBackendContext>(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) {
@@ -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
}
}
@@ -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 {
@@ -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 {
@@ -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 {
@@ -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"
}
@@ -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"
}
@@ -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"
}
@@ -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"
}
@@ -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"
}