From 1564de5859775d67bcd7f8f479c9e707c728bd42 Mon Sep 17 00:00:00 2001 From: Svyatoslav Kuzmich Date: Mon, 9 Jan 2023 15:32:15 +0100 Subject: [PATCH] [Wasm] Support external vararg Initial implementation copies Wasm array to JS array and spreads it --- .../kotlin/backend/wasm/WasmSymbols.kt | 4 + ...DeclarationsToTopLevelFunctionsLowering.kt | 22 +- .../wasm/lower/JsInteropFunctionsLowering.kt | 96 +++++++-- .../codegen/boxWasmJsInterop/vararg.js | 77 +++++++ .../codegen/boxWasmJsInterop/vararg.kt | 197 ++++++++++++++++++ ...irJsCodegenWasmJsInteropTestGenerated.java | 6 + ...IrCodegenWasmJsInteropJsTestGenerated.java | 6 + ...CodegenWasmJsInteropWasmTestGenerated.java | 5 + .../box/native/objectFunWithVararg.kt | 1 - .../kotlin/wasm/internal/ExternalWrapper.kt | 6 + 10 files changed, 402 insertions(+), 18 deletions(-) create mode 100644 compiler/testData/codegen/boxWasmJsInterop/vararg.js create mode 100644 compiler/testData/codegen/boxWasmJsInterop/vararg.kt diff --git a/compiler/ir/backend.wasm/src/org/jetbrains/kotlin/backend/wasm/WasmSymbols.kt b/compiler/ir/backend.wasm/src/org/jetbrains/kotlin/backend/wasm/WasmSymbols.kt index dfc303fda3b..062f08c2166 100644 --- a/compiler/ir/backend.wasm/src/org/jetbrains/kotlin/backend/wasm/WasmSymbols.kt +++ b/compiler/ir/backend.wasm/src/org/jetbrains/kotlin/backend/wasm/WasmSymbols.kt @@ -211,6 +211,10 @@ class WasmSymbols( val nullableDoubleIeee754Equals = getInternalFunction("nullableDoubleIeee754Equals") val unsafeGetScratchRawMemory = getInternalFunction("unsafeGetScratchRawMemory") + + val newJsArray = getInternalFunction("newJsArray") + val jsArrayPush = getInternalFunction("jsArrayPush") + val startCoroutineUninterceptedOrReturnIntrinsics = (0..2).map { getInternalFunction("startCoroutineUninterceptedOrReturnIntrinsic$it") } diff --git a/compiler/ir/backend.wasm/src/org/jetbrains/kotlin/backend/wasm/lower/ComplexExternalDeclarationsToTopLevelFunctionsLowering.kt b/compiler/ir/backend.wasm/src/org/jetbrains/kotlin/backend/wasm/lower/ComplexExternalDeclarationsToTopLevelFunctionsLowering.kt index f4099718331..647746ca570 100644 --- a/compiler/ir/backend.wasm/src/org/jetbrains/kotlin/backend/wasm/lower/ComplexExternalDeclarationsToTopLevelFunctionsLowering.kt +++ b/compiler/ir/backend.wasm/src/org/jetbrains/kotlin/backend/wasm/lower/ComplexExternalDeclarationsToTopLevelFunctionsLowering.kt @@ -183,7 +183,7 @@ class ComplexExternalDeclarationsToTopLevelFunctionsLowering(val context: WasmBa val jsFun = function.getJsFunAnnotation() // Wrap external functions without @JsFun to lambdas `foo` -> `(a, b) => foo(a, b)`. // This way we wouldn't fail if we don't call them. - if (jsFun != null && function.valueParameters.all { it.defaultValue == null }) + if (jsFun != null && function.valueParameters.all { it.defaultValue == null && it.varargElementType == null }) return processFunctionOrConstructor( function = function, @@ -241,9 +241,23 @@ class ComplexExternalDeclarationsToTopLevelFunctionsLowering(val context: WasmBa } append(jsFunctionReference) append("(") - appendParameterList(numValueParameters - numDefaultParameters, isEnd = numDefaultParameters == 0) + + val numNonDefaultParamters = numValueParameters - numDefaultParameters + repeat(numNonDefaultParamters) { + if (function.valueParameters[it].isVararg) { + append("...") + } + append("p$it") + if (numDefaultParameters != 0 || it + 1 < numNonDefaultParamters) + append(", ") + } repeat(numDefaultParameters) { - append("isDefault$it ? undefined : p${numValueParameters - numDefaultParameters + it}, ") + if (function.valueParameters[numNonDefaultParamters + it].isVararg) { + append("...") + } else { + append("isDefault$it ? undefined : ") + } + append("p${numNonDefaultParamters + it}, ") } append(")") } @@ -276,7 +290,7 @@ class ComplexExternalDeclarationsToTopLevelFunctionsLowering(val context: WasmBa if (dispatchReceiver != null) { res.addValueParameter("_this", dispatchReceiver.type) } - function.valueParameters.forEach { res.addValueParameter(it.name, it.type) } + function.valueParameters.forEach { res.addValueParameter(it.name, it.type).apply { varargElementType = it.varargElementType } } // Using Int type with 0 and 1 values to prevent overhead of converting Boolean to true and false repeat(numDefaultParameters) { res.addValueParameter("isDefault$it", context.irBuiltIns.intType) } externalFunToTopLevelMapping[function] = res diff --git a/compiler/ir/backend.wasm/src/org/jetbrains/kotlin/backend/wasm/lower/JsInteropFunctionsLowering.kt b/compiler/ir/backend.wasm/src/org/jetbrains/kotlin/backend/wasm/lower/JsInteropFunctionsLowering.kt index bca09b97595..6e13cd6781c 100644 --- a/compiler/ir/backend.wasm/src/org/jetbrains/kotlin/backend/wasm/lower/JsInteropFunctionsLowering.kt +++ b/compiler/ir/backend.wasm/src/org/jetbrains/kotlin/backend/wasm/lower/JsInteropFunctionsLowering.kt @@ -23,12 +23,14 @@ import org.jetbrains.kotlin.ir.declarations.* import org.jetbrains.kotlin.ir.expressions.IrBody import org.jetbrains.kotlin.ir.expressions.IrCall import org.jetbrains.kotlin.ir.expressions.IrExpression +import org.jetbrains.kotlin.ir.expressions.IrStatementOrigin import org.jetbrains.kotlin.ir.expressions.impl.IrInstanceInitializerCallImpl import org.jetbrains.kotlin.ir.types.* import org.jetbrains.kotlin.ir.util.* import org.jetbrains.kotlin.ir.visitors.IrElementTransformerVoid import org.jetbrains.kotlin.ir.visitors.transformChildrenVoid import org.jetbrains.kotlin.name.Name +import org.jetbrains.kotlin.util.OperatorNameConventions import kotlin.math.absoluteValue /** @@ -89,8 +91,13 @@ class JsInteropFunctionsLowering(val context: WasmBackendContext) : DeclarationT // Patch function types for Number parameters as double function.returnType = doubleIfNumber(function.returnType) - val valueParametersAdapters = function.valueParameters.map { - it.type.kotlinToJsAdapterIfNeeded(isReturn = false) + val valueParametersAdapters = function.valueParameters.map { parameter -> + val varargElementType = parameter.varargElementType + if (varargElementType != null) { + CopyToJsArrayAdapter(parameter.type, varargElementType) + } else { + parameter.type.kotlinToJsAdapterIfNeeded(isReturn = false) + } } val resultAdapter = function.returnType.jsToKotlinAdapterIfNeeded(isReturn = true) @@ -191,6 +198,16 @@ class JsInteropFunctionsLowering(val context: WasmBackendContext) : DeclarationT ) } + val primitivesToExternRefAdapters: Map = mapOf( + builtIns.byteType to adapters.kotlinByteToExternRefAdapter, + builtIns.shortType to adapters.kotlinShortToExternRefAdapter, + builtIns.charType to adapters.kotlinCharToExternRefAdapter, + builtIns.intType to adapters.kotlinIntToExternRefAdapter, + builtIns.longType to adapters.kotlinLongToExternRefAdapter, + builtIns.floatType to adapters.kotlinFloatToExternRefAdapter, + builtIns.doubleType to adapters.kotlinDoubleToExternRefAdapter, + ).mapValues { FunctionBasedAdapter(it.value.owner) } + private fun IrType.kotlinToJsAdapterIfNeeded(isReturn: Boolean): InteropTypeAdapter? { if (isReturn && this == builtIns.unitType) return null @@ -213,18 +230,9 @@ class JsInteropFunctionsLowering(val context: WasmBackendContext) : DeclarationT ) } - val primitiveToExternRefAdapter = when (notNullType) { - builtIns.byteType -> adapters.kotlinByteToExternRefAdapter.owner - builtIns.shortType -> adapters.kotlinShortToExternRefAdapter.owner - builtIns.charType -> adapters.kotlinCharToExternRefAdapter.owner - builtIns.intType -> adapters.kotlinIntToExternRefAdapter.owner - builtIns.longType -> adapters.kotlinLongToExternRefAdapter.owner - builtIns.floatType -> adapters.kotlinFloatToExternRefAdapter.owner - builtIns.doubleType -> adapters.kotlinDoubleToExternRefAdapter.owner - else -> null - } + val primitiveToExternRefAdapter = primitivesToExternRefAdapters[notNullType] - val typeAdapter = primitiveToExternRefAdapter?.let(::FunctionBasedAdapter) + val typeAdapter = primitiveToExternRefAdapter ?: notNullType.kotlinToJsAdapterIfNeededNotNullable(isReturn) ?: return null @@ -770,6 +778,68 @@ class JsInteropFunctionsLowering(val context: WasmBackendContext) : DeclarationT } } } + + /** + * Vararg parameter adapter + */ + inner class CopyToJsArrayAdapter( + override val fromType: IrType, + private val fromElementType: IrType, + ) : InteropTypeAdapter { + override val toType: IrType = + context.wasmSymbols.externalInterfaceType + + private val elementAdapter = + primitivesToExternRefAdapters[fromElementType] + ?: fromElementType.kotlinToJsAdapterIfNeeded(false) + + private val arrayClass = fromType.classOrNull!! + private val getMethod = arrayClass.getSimpleFunction("get")!!.owner + private val sizeMethod = arrayClass.getPropertyGetter("size")!!.owner + + override fun adapt(expression: IrExpression, builder: IrBuilderWithScope): IrExpression { + return builder.irComposite { + val originalArrayVar = irTemporary(expression) + + // val newJsArray = [] + // var index = 0 + // while(index != size) { + // newJsArray.push(adapt(originalArray[index])); + // index++ + // } + val newJsArrayVar = irTemporary(irCall(symbols.newJsArray)) + val indexVar = irTemporary(irInt(0), isMutable = true) + val arraySizeVar = irTemporary(irCall(sizeMethod).apply { dispatchReceiver = irGet(originalArrayVar) }) + + +irWhile().apply { + condition = irNotEquals(irGet(indexVar), irGet(arraySizeVar)) + body = irBlock { + val adaptedValue = elementAdapter.adaptIfNeeded( + irImplicitCast( + irCall(getMethod).apply { + dispatchReceiver = irGet(originalArrayVar) + putValueArgument(0, irGet(indexVar)) + }, + fromElementType + ), + this@irBlock + ) + +irCall(symbols.jsArrayPush).apply { + putValueArgument(0, irGet(newJsArrayVar)) + putValueArgument(1, adaptedValue) + } + val inc = indexVar.type.getClass()!!.functions.single { it.name == OperatorNameConventions.INC } + +irSet( + indexVar, + irCallOp(inc.symbol, indexVar.type, irGet(indexVar)), + origin = IrStatementOrigin.PREFIX_INCR + ) + } + } + +irGet(newJsArrayVar) + } + } + } } internal fun StringBuilder.appendParameterList(size: Int, name: String = "p", isEnd: Boolean = true) = diff --git a/compiler/testData/codegen/boxWasmJsInterop/vararg.js b/compiler/testData/codegen/boxWasmJsInterop/vararg.js new file mode 100644 index 00000000000..ca7e81507ad --- /dev/null +++ b/compiler/testData/codegen/boxWasmJsInterop/vararg.js @@ -0,0 +1,77 @@ +function paramCount() { + return arguments.length +} + +function Bar(size, order) { + this.size = size; + Bar.checkOrder(order); +} + +Bar.order = 0; +Bar.hasOrderProblem = false; +Bar.checkOrder = function (expectedOrder) { + var curOrder = Bar.order++; + Bar.hasOrderProblem = Bar.hasOrderProblem || curOrder !== expectedOrder; +}; + +Bar.startNewTest = function () { + Bar.hasOrderProblem = false; + Bar.order = 0; + return true; +}; + + +Bar.prototype.test = function (order, dummy /*, args */) { + Bar.checkOrder(order); + return dummy === 1 && (arguments.length - 2) === this.size; +}; +Bar.prototype.test2 = Bar.prototype.test; + +function test3(bar, dummy /*, args */) { + return dummy === 1 && (arguments.length - 2) === bar.size; +} + +var obj = { + test: function (size /*, args */) { + return (arguments.length - 1) === size; + } +}; + +function testNativeVarargWithFunLit(/* args, f */) { + var args = Array.prototype.slice.call(arguments, 0, arguments.length - 1); + var f = arguments[arguments.length - 1]; + return typeof f === "function" && f(args); +} + +function sumOfParameters() { + var size = arguments.length; + var result = 0; + for (var i = 0; i < size; i++) { + result += arguments[i]; + } + return result; +} + +function sumFunValuesOnParameters() { + var size = arguments.length - 1; + var f = arguments[arguments.length - 1]; + var result = 0; + for (var i = 0; i < size; i++) { + var u = arguments[i]; + result += f(u); + } + return result; +} + +function idArrayVarArg() { + var args = Array.prototype.slice.call(arguments, 0, arguments.length); + return args; +} + +function join(...args) { + return args.join("-"); +} + +function mapJoin(f, ...args) { + return args.map(x => f(x)).join("-") +} \ No newline at end of file diff --git a/compiler/testData/codegen/boxWasmJsInterop/vararg.kt b/compiler/testData/codegen/boxWasmJsInterop/vararg.kt new file mode 100644 index 00000000000..4493d0f177a --- /dev/null +++ b/compiler/testData/codegen/boxWasmJsInterop/vararg.kt @@ -0,0 +1,197 @@ +// IGNORE_BACKEND_K2: JS_IR + +// Partial copy of js/js.translator/testData/box/native/vararg.kt +// With some additions for concrete number types, strings and function references + +package foo + +import kotlin.test.assertEquals + +external fun paramCount(vararg a: Int): Int = definedExternally + +@JsName("paramCount") +external fun anotherParamCount(vararg a: Int): Int = definedExternally + +// test spread operator +fun count(vararg a: Int) = paramCount(*a) + +// test spread operator +fun anotherCount(vararg a: Int) = anotherParamCount(*a) + +external fun test3(bar: Bar, dummy: Int, vararg args: Int): Boolean = definedExternally + +external class Bar(size: Int, order: Int = definedExternally) { + val size: Int + fun test(order: Int, dummy: Int, vararg args: Int): Boolean = definedExternally + companion object { + fun startNewTest(): Boolean = definedExternally + var hasOrderProblem: Boolean = definedExternally + } +} + +external object obj { + fun test(size: Int, vararg args: Int): Boolean = definedExternally +} + +fun spreadInMethodCall(size: Int, vararg args: Int) = Bar(size).test(0, 1, *args) + +fun spreadInObjectMethodCall(size: Int, vararg args: Int) = obj.test(size, *args) + +fun spreadInPackageMethodCall(size: Int, vararg args: Int) = test3(Bar(size), 1, *args) + +fun testSpreadOperatorWithSafeCall(a: Bar?, expected: Boolean?, vararg args: Int): Boolean { + return a?.test(0, 1, *args) == expected +} + +fun testSpreadOperatorWithSureCall(a: Bar?, vararg args: Int): Boolean { + return a!!.test(0, 1, *args) +} + +fun testCallOrder(vararg args: Int) = + Bar.startNewTest() && + Bar(args.size, 0).test(1, 1, *args) && Bar(args.size, 2).test(3, 1, *args) && + !Bar.hasOrderProblem + +external fun sumOfParameters(x: Int, y: Int, vararg a: Int): Int = definedExternally + +external fun sumFunValuesOnParameters(x: Int, y: Int, vararg a: Int, f: (Int) -> Int): Int = definedExternally + +fun runCallable(fn: (Int, Int, Int, Int, Int) -> Int, a1: Int, a2: Int, a3: Int, a4: Int, a5: Int): Int = fn(a1, a2, a3, a4, a5) + +@JsName("join") +external fun joinBoolean(vararg x: Boolean): String + +@JsName("join") +external fun joinByte(vararg x: Byte): String + +@JsName("join") +external fun joinChar(vararg x: Char): String + +@JsName("join") +external fun joinShort(vararg x: Short): String + +@JsName("join") +external fun joinInt(vararg x: Int): String + +@JsName("join") +external fun joinLong(vararg x: Long): String + +@JsName("join") +external fun joinFloat(vararg x: Float): String + +@JsName("join") +external fun joinDouble(vararg x: Double): String + +@JsName("join") +external fun joinString(vararg x: String): String + +@JsName("mapJoin") +external fun mapJoin(f: ((String) -> String) -> String, vararg x: (String) -> String): String + +@JsName("join") +external fun joinWithDefault(a: Int = definedExternally, b: Int = definedExternally, vararg c: Int, d: Int = definedExternally): String + +fun box(): String { + if (paramCount() != 0) + return "failed when call native function without args" + + if (paramCount(1) != 1) return "failed when call native function with single vararg" + + if (paramCount(1, 2, 3) != 3) + return "failed when call native function with some args" + + if (anotherParamCount(1, 2, 3) != 3) + return "failed when call native function with some args witch declareted with custom name" + + if (count() != 0) + return "failed when call native function without args using spread operator" + + if (count(1, 1, 1, 1) != 4) + return "failed when call native function with some args using spread operator" + + if (anotherCount(1, 2, 3) != 3) + return "failed when call native function with some args using spread operator witch declareted with custom name" + + if (!Bar(5).test(0, 1, 1, 2, 3, 4, 5)) + return "failed when call method with some args" + + if (!spreadInMethodCall(2, 1, 2)) + return "failed when call method using spread operator" + + if (!Bar(1).test(0, 1, 1)) + return "failed when call method with single arg" + + if (!spreadInMethodCall(2, 1, 2)) + return "failed when call method using spread operator" + + if (!(obj.test(5, 1, 2, 3, 4, 5))) + return "failed when call method of object" + + if (!(spreadInObjectMethodCall(2, 1, 2))) + return "failed when call method of object using spread operator" + + if (!spreadInPackageMethodCall(2, 1, 2)) + return "failed when call package method using spread operator" + + if (!(testSpreadOperatorWithSafeCall(null, null))) + return "failed when test spread operator with SafeCall (?.) using null receiver" + + if (!(testSpreadOperatorWithSafeCall(Bar(3), true, 1, 2, 3))) + return "failed when test spread operator with SafeCall (?.)" + + if (!(testSpreadOperatorWithSureCall(Bar(3), 1, 2, 3))) + return "failed when test spread operator with SureCall (!!)" + + if (!(testCallOrder())) + return "failed when test calling order when using spread operator without args" + + if (!(testCallOrder(1, 2, 3, 4))) + return "failed when test calling order when using spread operator with some args" + + val baz: Bar? = Bar(1) + if (!(baz!!)?.test(0, 1, 1)!!) + return "failed when combined SureCall and SafeCall, maybe we lost cached expression" + + assertEquals(45, sumOfParameters(1, 2, 3, 4, 5, 6, 7, 8, 9)) + assertEquals(45, sumOfParameters(1, 2, *intArrayOf(3, 4, 5, 6, 7, 8, 9))) + assertEquals(45, sumOfParameters(1, 2, a = *intArrayOf(3, 4, 5, 6, 7, 8, 9))) + assertEquals(45, sumOfParameters(1, 2, a = intArrayOf(3, 4, 5, 6, 7, 8, 9))) + assertEquals(45, sumOfParameters(1, 2, 3, 4, *intArrayOf(5, 6, 7, 8, 9))) + assertEquals(90, sumFunValuesOnParameters(1, 2, 3, 4, 5, 6, 7, 8, 9) { 2*it }) + assertEquals(90, sumFunValuesOnParameters(1, 2, *intArrayOf(3, 4, 5, 6, 7, 8, 9)) { 2*it }) + assertEquals(90, sumFunValuesOnParameters(1, 2, a = *intArrayOf(3, 4, 5, 6, 7, 8, 9)) { 2*it }) + assertEquals(90, sumFunValuesOnParameters(1, 2, a = intArrayOf(3, 4, 5, 6, 7, 8, 9)) { 2*it }) + assertEquals(90, sumFunValuesOnParameters(1, 2, 3, 4, *intArrayOf(5, 6, 7, 8, 9)) { 2*it }) + assertEquals(90, sumFunValuesOnParameters(1, 2, *intArrayOf(3, 4, 5, 6, 7), 8, 9) { 2*it }) + assertEquals(90, sumFunValuesOnParameters(1, 2, *intArrayOf(3, 4, 5), *intArrayOf(6, 7, 8, 9)) { 2*it }) + assertEquals(90, sumFunValuesOnParameters(1, 2, *intArrayOf(3, 4), 5, 6, *intArrayOf(7, 8, 9)) { 2*it }) + + assertEquals(5, runCallable(::paramCount, 1, 2, 3, 4, 5)) + assertEquals(5, runCallable(::anotherParamCount, 1, 2, 3, 4, 5)) + assertEquals(5, runCallable(::anotherCount, 1, 2, 3, 4, 5)) + assertEquals(11111, runCallable(::sumOfParameters, 1, 10, 100, 1000, 10000)) + + if (joinBoolean(true, false, true) != "true-false-true") return "Fail1" + if (joinByte(10.toByte(), 20.toByte(), 30.toByte()) != "10-20-30") return "Fail2" + if (joinChar(10.toChar(), 20.toChar(), 30.toChar()) != "10-20-30") return "Fail3" + if (joinShort(10.toShort(), 20.toShort(), 30.toShort()) != "10-20-30") return "Fail4" + if (joinInt(10.toInt(), 20.toInt(), 30.toInt()) != "10-20-30") return "Fail5" + if (joinLong(10.toLong(), 20.toLong(), 30.toLong()) != "10-20-30") return "Fail6" + if (joinFloat(10.toFloat(), 20.toFloat(), 30.toFloat()) != "10-20-30") return "Fail7" + if (joinDouble(10.toDouble(), 20.toDouble(), 30.toDouble()) != "10-20-30") return "Fail8" + + if (joinString("a", "b", "c") != "a-b-c") return "Fail9" + + if ( + mapJoin( + f = { f -> f("a")}, + { it + "10" }, + { it + "20" }, + { it + "30" } + ) != "a10-a20-a30" + ) { return "Fail10" } + + if (joinWithDefault(10, 20, 30, 40, d = 50) != "10-20-30-40-50") return "Fail11" + + return "OK" +} diff --git a/js/js.tests/tests-gen/org/jetbrains/kotlin/js/test/fir/FirJsCodegenWasmJsInteropTestGenerated.java b/js/js.tests/tests-gen/org/jetbrains/kotlin/js/test/fir/FirJsCodegenWasmJsInteropTestGenerated.java index 22b12ff5e59..b9d35c4e9a9 100644 --- a/js/js.tests/tests-gen/org/jetbrains/kotlin/js/test/fir/FirJsCodegenWasmJsInteropTestGenerated.java +++ b/js/js.tests/tests-gen/org/jetbrains/kotlin/js/test/fir/FirJsCodegenWasmJsInteropTestGenerated.java @@ -84,4 +84,10 @@ public class FirJsCodegenWasmJsInteropTestGenerated extends AbstractFirJsCodegen public void testTypes() throws Exception { runTest("compiler/testData/codegen/boxWasmJsInterop/types.kt"); } + + @Test + @TestMetadata("vararg.kt") + public void testVararg() throws Exception { + runTest("compiler/testData/codegen/boxWasmJsInterop/vararg.kt"); + } } diff --git a/js/js.tests/tests-gen/org/jetbrains/kotlin/js/test/ir/IrCodegenWasmJsInteropJsTestGenerated.java b/js/js.tests/tests-gen/org/jetbrains/kotlin/js/test/ir/IrCodegenWasmJsInteropJsTestGenerated.java index 49783d8e97b..01ab2f479ca 100644 --- a/js/js.tests/tests-gen/org/jetbrains/kotlin/js/test/ir/IrCodegenWasmJsInteropJsTestGenerated.java +++ b/js/js.tests/tests-gen/org/jetbrains/kotlin/js/test/ir/IrCodegenWasmJsInteropJsTestGenerated.java @@ -84,4 +84,10 @@ public class IrCodegenWasmJsInteropJsTestGenerated extends AbstractIrCodegenWasm public void testTypes() throws Exception { runTest("compiler/testData/codegen/boxWasmJsInterop/types.kt"); } + + @Test + @TestMetadata("vararg.kt") + public void testVararg() throws Exception { + runTest("compiler/testData/codegen/boxWasmJsInterop/vararg.kt"); + } } diff --git a/js/js.tests/tests-gen/org/jetbrains/kotlin/js/testOld/wasm/semantics/IrCodegenWasmJsInteropWasmTestGenerated.java b/js/js.tests/tests-gen/org/jetbrains/kotlin/js/testOld/wasm/semantics/IrCodegenWasmJsInteropWasmTestGenerated.java index 72c6b04f63d..542200c1b9d 100644 --- a/js/js.tests/tests-gen/org/jetbrains/kotlin/js/testOld/wasm/semantics/IrCodegenWasmJsInteropWasmTestGenerated.java +++ b/js/js.tests/tests-gen/org/jetbrains/kotlin/js/testOld/wasm/semantics/IrCodegenWasmJsInteropWasmTestGenerated.java @@ -100,6 +100,11 @@ public class IrCodegenWasmJsInteropWasmTestGenerated extends AbstractIrCodegenWa runTest("compiler/testData/codegen/boxWasmJsInterop/types.kt"); } + @TestMetadata("vararg.kt") + public void testVararg() throws Exception { + runTest("compiler/testData/codegen/boxWasmJsInterop/vararg.kt"); + } + @TestMetadata("wasmImport.kt") public void testWasmImport() throws Exception { runTest("compiler/testData/codegen/boxWasmJsInterop/wasmImport.kt"); diff --git a/js/js.translator/testData/box/native/objectFunWithVararg.kt b/js/js.translator/testData/box/native/objectFunWithVararg.kt index fe4304db671..94433915ba8 100644 --- a/js/js.translator/testData/box/native/objectFunWithVararg.kt +++ b/js/js.translator/testData/box/native/objectFunWithVararg.kt @@ -1,4 +1,3 @@ -// IGNORE_BACKEND: WASM // EXPECTED_REACHABLE_NODES: 1281 import Test.test diff --git a/libraries/stdlib/wasm/internal/kotlin/wasm/internal/ExternalWrapper.kt b/libraries/stdlib/wasm/internal/kotlin/wasm/internal/ExternalWrapper.kt index d5e4a35b215..8d0ddaf7700 100644 --- a/libraries/stdlib/wasm/internal/kotlin/wasm/internal/ExternalWrapper.kt +++ b/libraries/stdlib/wasm/internal/kotlin/wasm/internal/ExternalWrapper.kt @@ -324,3 +324,9 @@ internal fun kotlinShortToExternRefAdapter(x: Short): ExternalInterfaceType = internal fun kotlinCharToExternRefAdapter(x: Char): ExternalInterfaceType = intToExternref(x.toInt()) + +@JsFun("() => []") +internal external fun newJsArray(): ExternalInterfaceType + +@JsFun("(array, element) => { array.push(element); }") +internal external fun jsArrayPush(array: ExternalInterfaceType, element: ExternalInterfaceType) \ No newline at end of file