[Wasm] Support default parameter values in functions with js(code)

This commit is contained in:
Svyatoslav Kuzmich
2023-03-08 19:38:19 +01:00
committed by Space Team
parent d5e6102ed9
commit d5b958f744
2 changed files with 46 additions and 1 deletions
@@ -11,8 +11,8 @@ import org.jetbrains.kotlin.backend.wasm.WasmBackendContext
import org.jetbrains.kotlin.ir.builders.*
import org.jetbrains.kotlin.ir.declarations.*
import org.jetbrains.kotlin.ir.expressions.*
import org.jetbrains.kotlin.ir.types.defaultType
import org.jetbrains.kotlin.ir.util.*
import org.jetbrains.kotlin.ir.visitors.*
/**
* Lower calls to `js(code)` into `@JsFun(code) external` functions.
@@ -55,6 +55,31 @@ class JsCodeCallsLowering(val context: WasmBackendContext) : FileLoweringPass {
append(jsCode)
if (!isSingleExpressionJsCode) append(" }")
}
if (function.valueParameters.any { it.defaultValue != null }) {
// Create a separate external function without default arguments
// and delegate calls to it.
val externalFun = createExternalJsFunction(
context,
function.name,
"_js_code",
function.returnType,
jsCode = jsFunCode,
)
externalFun.copyTypeParametersFrom(function)
externalFun.valueParameters = function.valueParameters.map { it.copyTo(externalFun, defaultValue = null) }
function.body = context.createIrBuilder(function.symbol).irBlockBody {
val call = irCall(externalFun.symbol)
function.valueParameters.forEachIndexed { index, parameter ->
call.putValueArgument(index, irGet(parameter))
}
function.typeParameters.forEachIndexed { index, typeParameter ->
call.putTypeArgument(index, typeParameter.defaultType)
}
+irReturn(call)
}
return listOf(function, externalFun)
}
val builder = context.createIrBuilder(function.symbol)
function.annotations += builder.irCallConstructor(context.wasmSymbols.jsFunConstructor, typeArguments = emptyList()).also {
it.putValueArgument(0, builder.irString(jsFunCode))
+20
View File
@@ -2,10 +2,30 @@ fun foo(x: Int, y: Int, z: String): Int = js("x + y + Number(z)")
val x: String = js("typeof 10")
fun fooDefault(x: Int = 1, y: Int? = 2, z: String? = "3"): Int =
js("x + y + Number(z)")
external interface EIA
external class EIB : EIA
val eia10: EIA = js("'10'")
val eib1000: EIB = js("'1000'")
fun <T : EIA> fooDefaultT(x: T? = null): T? =
js("x")
fun box(): String {
val res = foo(10, 20, z = "30")
if (res != 60) return "Wrong foo: $res"
if (x != "number") return "Wrong x: $x"
if (fooDefault() != 6) return "Wrong fooDefault 1"
if (fooDefault(x = 100, y = 200, z = "300") != 600) return "Wrong fooDefault 2"
if (fooDefault(y = 200, z = "300") != 501) return "Wrong fooDefault 3"
if (fooDefaultT<EIA>() != null) return "Wrong fooDefaultT 1"
if (fooDefaultT<EIA>(eia10).toString() != "10") return "Wrong fooDefaultT 2"
if (fooDefaultT<EIB>(eib1000).toString() != "1000") return "Wrong fooDefaultT 3"
return "OK"
}