Files
kotlin-fork/js/js.translator/testData/box/vararg/jsExternalInterfaceVararg.kt
T
Sergej Jaskiewicz 4a724611d8 [JS IR] Extract varargs in bridges generated for external methods
The issue this commit fixes occurs when we have an external interface
implemented by a Kotlin class, if that interface has methods with
varargs.

Kotlin functions expect varargs passed as arrays, but JavaScript code
may be unaware of this convention.

So, when generating a bridge for external interface method
implementaion, we insert some additional logic for extracting varargs
using the JavaScript `arguments` object.

A simplified example:

```kotlin
external interface Adder {
    fun sum(vararg numbers: Int): Int
}

class AdderImpl: Adder {
    override fun sum(vararg numbers: Int) = numbers.sum()
}
```

For `AdderImpl` we generate the following JS code:
```js
AdderImpl.prototype.sum_69wd7h_k$ = function (numbers) {
  return sum(numbers);
};
AdderImpl.prototype.sum = function () {
  var numbers = new Int32Array([].slice.call(arguments));
  return this.sum_69wd7h_k$(numbers);
};
```

#KT-15223 Fixed
2022-01-27 11:06:17 +00:00

160 lines
6.8 KiB
Kotlin
Vendored

// DONT_TARGET_EXACT_BACKEND: JS
// KJS_WITH_FULL_RUNTIME
// Generated briges must not contain any casts!
// CHECK_NOT_CALLED_IN_SCOPE: function=THROW_CCE scope=collect1
// CHECK_NOT_CALLED_IN_SCOPE: function=THROW_CCE scope=collect2
// CHECK_NOT_CALLED_IN_SCOPE: function=THROW_CCE scope=collect3
// CHECK_NOT_CALLED_IN_SCOPE: function=THROW_CCE scope=collect4
// CHECK_NOT_CALLED_IN_SCOPE: function=THROW_CCE scope=collect4
// CHECK_NOT_CALLED_IN_SCOPE: function=THROW_CCE scope=sumBools
// CHECK_NOT_CALLED_IN_SCOPE: function=THROW_CCE scope=sumBytes
// CHECK_NOT_CALLED_IN_SCOPE: function=THROW_CCE scope=sumShorts
// CHECK_NOT_CALLED_IN_SCOPE: function=THROW_CCE scope=sumChars
// CHECK_NOT_CALLED_IN_SCOPE: function=THROW_CCE scope=sumInts
// CHECK_NOT_CALLED_IN_SCOPE: function=THROW_CCE scope=sumFloats
// CHECK_NOT_CALLED_IN_SCOPE: function=THROW_CCE scope=sumDoubles
// CHECK_NOT_CALLED_IN_SCOPE: function=THROW_CCE scope=sumLongs
// CHECK_NOT_CALLED_IN_SCOPE: function=THROW_CCE scope=sumUByte
// CHECK_NOT_CALLED_IN_SCOPE: function=THROW_CCE scope=sumUShort
// CHECK_NOT_CALLED_IN_SCOPE: function=THROW_CCE scope=sumUInt
// CHECK_NOT_CALLED_IN_SCOPE: function=THROW_CCE scope=sumULong
external interface Collector {
// Both leading and trailing parameters are present
fun collect1(leading1: String, leading2: String, vararg xs: String, trailing1: String, trailing2: String, trailing3: String): String
// No trailing parameters
fun collect2(leading: String, vararg xs: String): String
// No leading parameters
fun collect3(vararg xs: String, trailing: String): String
// Vararg only
fun collect4(vararg xs: String): String
}
class CollectorImpl : Collector {
override fun collect1(
leading1: String,
leading2: String,
vararg xs: String,
trailing1: String,
trailing2: String,
trailing3: String
): String {
var result = "[$leading1, $leading2, ["
for (i in 0 until xs.size) {
if (i > 0) result += ", "
result += xs[i]
}
result += "], $trailing1, $trailing2, $trailing3]"
return result
}
override fun collect2(leading: String, vararg xs: String) =
collect1("", leading, *xs, trailing1 = "", trailing2 = "", trailing3 = "")
override fun collect3(vararg xs: String, trailing: String) =
collect1("", "", *xs, trailing1 = trailing, trailing2 = "", trailing3 = "")
override fun collect4(vararg xs: String) = collect1("", "", *xs, trailing1 = "", trailing2 = "", trailing3 = "")
}
fun collectNames1(collector: Collector, names: Array<String>) =
collector.collect1("Jack", "Kate", *names, trailing2 = "Winsent", trailing1 = "Claire", trailing3 = "Sawyer")
fun collectNames2(collector: Collector, names: Array<String>) =
collector.collect2("Jack", *names)
fun collectNames3(collector: Collector, names: Array<String>) =
collector.collect3(*names, trailing = "Winsent")
fun collectNames4(collector: Collector, names: Array<String>) =
collector.collect4(*names)
external interface Folder {
fun sumBools(vararg xs: Boolean): Boolean
fun sumBytes(vararg xs: Byte): Int
fun sumShorts(vararg xs: Short): Int
fun sumChars(vararg xs: Char): String
fun sumInts(vararg xs: Int): Int
fun sumFloats(vararg xs: Float): Float
fun sumDoubles(vararg xs: Double): Double
fun sumLongs(vararg xs: Long): Long
fun sumUByte(vararg xs: UByte): UInt
fun sumUShort(vararg xs: UShort): UInt
fun sumUInt(vararg xs: UInt): UInt
fun sumULong(vararg xs: ULong): ULong
}
class FolderImpl: Folder {
// Test with primitive types. Varargs of primitive types expect primitive arrays.
override fun sumBools(vararg xs: Boolean) = xs.fold(true, Boolean::or)
override fun sumBytes(vararg xs: Byte) = xs.fold(0, Int::plus)
override fun sumShorts(vararg xs: Short) = xs.fold(0, Int::plus)
override fun sumChars(vararg xs: Char) = xs.fold("", String::plus)
override fun sumInts(vararg xs: Int) = xs.fold(0, Int::plus)
override fun sumFloats(vararg xs: Float) = xs.fold(0.0f, Float::plus)
override fun sumDoubles(vararg xs: Double) = xs.fold(0.0, Double::plus)
override fun sumLongs(vararg xs: Long) = xs.fold(0L, Long::plus)
// Test with inline classes
override fun sumUByte(vararg xs: UByte) = xs.fold(0U, UInt::plus)
override fun sumUShort(vararg xs: UShort) = xs.fold(0U, UInt::plus)
override fun sumUInt(vararg xs: UInt) = xs.fold(0U, UInt::plus)
override fun sumULong(vararg xs: ULong) = xs.fold(0UL, ULong::plus)
}
fun box(): String {
val collector: Collector = CollectorImpl()
assertEquals("[1, 2, [3, 4], 5, 6, 7]", collector.collect1("1", "2", "3", "4", trailing2 = "6", trailing1 = "5", trailing3 = "7"))
assertEquals("[∅, 1, [2, 3, 4, 5, 6, 7], ∅, ∅, ∅]", collector.collect2("1", "2", "3", "4", "5", "6", "7"))
assertEquals("[∅, ∅, [1, 2, 3], 4, ∅, ∅]", collector.collect3("1", "2", "3", trailing = "4"))
assertEquals("[∅, ∅, [], ∅, ∅, ∅]", collector.collect4())
assertEquals("[Jack, Kate, [Hugo, Jin], Claire, Winsent, Sawyer]", collectNames1(collector, arrayOf("Hugo", "Jin")))
assertEquals("[∅, Jack, [Kate, Hugo, Jin], ∅, ∅, ∅]", collectNames2(collector, arrayOf("Kate", "Hugo", "Jin")))
assertEquals("[∅, ∅, [Claire], Winsent, ∅, ∅]", collectNames3(collector, arrayOf("Claire")))
assertEquals("[∅, ∅, [], ∅, ∅, ∅]", collectNames4(collector, arrayOf()))
assertEquals(
"[4, 8, [15], 16, 23, 42]",
js("collector.collect1('4', '8', '15', '16', '23', '42')").unsafeCast<String>()
)
assertEquals(
"[∅, 4, [8, 15, 16, 23, 42], ∅, ∅, ∅]",
js("collector.collect2('4', '8', '15', '16', '23', '42')").unsafeCast<String>()
)
assertEquals(
"[∅, ∅, [4, 8, 15, 16, 23], 42, ∅, ∅]",
js("collector.collect3('4', '8', '15', '16', '23', '42')").unsafeCast<String>()
)
assertEquals(
"[∅, ∅, [], ∅, ∅, ∅]",
js("collector.collect4()").unsafeCast<String>()
)
val folder: Folder = FolderImpl()
assertEquals(true, folder.sumBools(false, true, false, false))
assertEquals(10, folder.sumBytes(1, 2, 3, 4))
assertEquals(10, folder.sumShorts(1, 2, 3, 4))
assertEquals("Hello", folder.sumChars('H', 'e', 'l', 'l', 'o'))
assertEquals(10, folder.sumInts(1, 2, 3, 4))
assertEquals(10.0f, folder.sumFloats(1.0f, 2.0f, 3.0f, 4.0f))
assertEquals(10.0, folder.sumDoubles(1.0, 2.0, 3.0, 4.0))
assertEquals(10L, folder.sumLongs(1L, 2L, 3L, 4L))
assertEquals(10U, folder.sumUByte(1U, 2U, 3U, 4U))
assertEquals(10U, folder.sumUShort(1U, 2U, 3U, 4U))
assertEquals(10U, folder.sumUInt(1U, 2U, 3U, 4U))
assertEquals(10U, folder.sumULong(1U, 2U, 3U, 4U))
return "OK"
}