From 081cd4a4a861ac61236dca4ac552e0e7dae588b2 Mon Sep 17 00:00:00 2001 From: Igor Yakovlev Date: Tue, 13 Sep 2022 20:37:06 +0200 Subject: [PATCH] [WASM] Support nullable types for external functions --- .../kotlin/backend/wasm/WasmSymbols.kt | 20 ++- .../wasm/lower/JsInteropFunctionsLowering.kt | 124 ++++++++++++-- .../boxWasmJsInterop/jsToKotlinAdapters.kt | 161 ++++++++++++++++++ .../boxWasmJsInterop/kotlinToJsAdapters.kt | 126 ++++++++++++++ .../boxWasmJsInterop/nullableExternRefs.kt | 17 +- ...IrCodegenWasmJsInteropJsTestGenerated.java | 12 ++ ...CodegenWasmJsInteropWasmTestGenerated.java | 10 ++ .../kotlin/wasm/internal/ExternalWrapper.kt | 73 +++++++- 8 files changed, 519 insertions(+), 24 deletions(-) create mode 100644 compiler/testData/codegen/boxWasmJsInterop/jsToKotlinAdapters.kt create mode 100644 compiler/testData/codegen/boxWasmJsInterop/kotlinToJsAdapters.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 4ef4f190730..0487dd5b830 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 @@ -271,13 +271,29 @@ class WasmSymbols( inner class JsInteropAdapters { val kotlinToJsStringAdapter = getInternalFunction("kotlinToJsStringAdapter") - val kotlinToJsBooleanAdapter = getInternalFunction("kotlinToJsBooleanAdapter") val kotlinToJsAnyAdapter = getInternalFunction("kotlinToJsAnyAdapter") - val jsToKotlinAnyAdapter = getInternalFunction("jsToKotlinAnyAdapter") + val jsToKotlinStringAdapter = getInternalFunction("jsToKotlinStringAdapter") + val jsToKotlinAnyAdapter = getInternalFunction("jsToKotlinAnyAdapter") + val jsToKotlinByteAdapter = getInternalFunction("jsToKotlinByteAdapter") val jsToKotlinShortAdapter = getInternalFunction("jsToKotlinShortAdapter") val jsToKotlinCharAdapter = getInternalFunction("jsToKotlinCharAdapter") + + val externRefToKotlinIntAdapter = getInternalFunction("externRefToKotlinIntAdapter") + val externRefToKotlinBooleanAdapter = getInternalFunction("externRefToKotlinBooleanAdapter") + val externRefToKotlinLongAdapter = getInternalFunction("externRefToKotlinLongAdapter") + val externRefToKotlinFloatAdapter = getInternalFunction("externRefToKotlinFloatAdapter") + val externRefToKotlinDoubleAdapter = getInternalFunction("externRefToKotlinDoubleAdapter") + + val kotlinIntToExternRefAdapter = getInternalFunction("kotlinIntToExternRefAdapter") + val kotlinBooleanToExternRefAdapter = getInternalFunction("kotlinBooleanToExternRefAdapter") + val kotlinLongToExternRefAdapter = getInternalFunction("kotlinLongToExternRefAdapter") + val kotlinFloatToExternRefAdapter = getInternalFunction("kotlinFloatToExternRefAdapter") + val kotlinDoubleToExternRefAdapter = getInternalFunction("kotlinDoubleToExternRefAdapter") + val kotlinByteToExternRefAdapter = getInternalFunction("kotlinByteToExternRefAdapter") + val kotlinShortToExternRefAdapter = getInternalFunction("kotlinShortToExternRefAdapter") + val kotlinCharToExternRefAdapter = getInternalFunction("kotlinCharToExternRefAdapter") } val jsInteropAdapters = JsInteropAdapters() 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 f67f97b78ed..2ef12141335 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 @@ -191,10 +191,42 @@ class JsInteropFunctionsLowering(val context: WasmBackendContext) : DeclarationT if (isReturn && this == builtIns.unitType) return null + if (this == builtIns.nothingType) + return null + + if (!isNullable()) { + return kotlinToJsAdapterIfNeededNotNullable(isReturn) + } + + val notNullType = makeNotNull() + 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 typeAdapter = primitiveToExternRefAdapter?.let(::FunctionBasedAdapter) + ?: notNullType.kotlinToJsAdapterIfNeededNotNullable(isReturn) + ?: return null + + return NullOrAdapter(typeAdapter) + } + + private fun IrType.kotlinToJsAdapterIfNeededNotNullable(isReturn: Boolean): InteropTypeAdapter? { + if (isReturn && this == builtIns.unitType) + return null + + if (this == builtIns.nothingType) + return null + when (this) { builtIns.stringType -> return FunctionBasedAdapter(adapters.kotlinToJsStringAdapter.owner) - builtIns.stringType.makeNullable() -> return NullOrAdapter(FunctionBasedAdapter(adapters.kotlinToJsStringAdapter.owner)) - builtIns.booleanType -> return FunctionBasedAdapter(adapters.kotlinToJsBooleanAdapter.owner) + builtIns.booleanType -> return FunctionBasedAdapter(adapters.kotlinBooleanToExternRefAdapter.owner) builtIns.anyType -> return FunctionBasedAdapter(adapters.kotlinToJsAnyAdapter.owner) builtIns.byteType, @@ -258,9 +290,42 @@ class JsInteropFunctionsLowering(val context: WasmBackendContext) : DeclarationT if (isReturn && this == builtIns.unitType) return null + val notNullType = makeNotNull() + val notNullAdapter = notNullType.jsToKotlinAdapterIfNeededNotNullable(isReturn) + val isPrimitive = notNullAdapter?.fromType?.isPrimitiveType() ?: notNullType.isPrimitiveType() + + return if (isNullable()) { + if (isPrimitive) { //nullable primitive should be checked and adapt to target type + val externRefToPrimitiveAdapter = when (notNullType) { + builtIns.floatType -> adapters.externRefToKotlinFloatAdapter.owner + builtIns.doubleType -> adapters.externRefToKotlinDoubleAdapter.owner + builtIns.longType -> adapters.externRefToKotlinLongAdapter.owner + builtIns.booleanType -> adapters.externRefToKotlinBooleanAdapter.owner + else -> adapters.externRefToKotlinIntAdapter.owner + } + val externalToPrimitiveAdapter = FunctionBasedAdapter(externRefToPrimitiveAdapter) + NullOrAdapter( + adapter = notNullAdapter?.let { CombineAdapter(it, externalToPrimitiveAdapter) } ?: externalToPrimitiveAdapter + ) + } else { //nullable reference should not be checked + notNullAdapter?.let(::NullOrAdapter) + } + } else { + if (isPrimitive) { // !nullable primitive checked by wasm signature + notNullAdapter + } else { // !nullable reference should be null checked + notNullAdapter?.let(::CheckNotNullAndAdapter) + ?: CheckNotNullNoAdapter(this) + } + } + } + + private fun IrType.jsToKotlinAdapterIfNeededNotNullable(isReturn: Boolean): InteropTypeAdapter? { + if (isReturn && (this == builtIns.unitType || this == builtIns.nothingType)) + return null + when (this) { - builtIns.stringType -> return CheckNotNullAndAdapter(FunctionBasedAdapter(adapters.jsToKotlinStringAdapter.owner)) - builtIns.stringType.makeNullable() -> return NullOrAdapter(FunctionBasedAdapter(adapters.jsToKotlinStringAdapter.owner)) + builtIns.stringType -> return FunctionBasedAdapter(adapters.jsToKotlinStringAdapter.owner) builtIns.anyType -> return FunctionBasedAdapter(adapters.jsToKotlinAnyAdapter.owner) builtIns.byteType -> return FunctionBasedAdapter(adapters.jsToKotlinByteAdapter.owner) builtIns.shortType -> return FunctionBasedAdapter(adapters.jsToKotlinShortAdapter.owner) @@ -561,6 +626,17 @@ class JsInteropFunctionsLowering(val context: WasmBackendContext) : DeclarationT } } + class CombineAdapter( + private val outerAdapter: InteropTypeAdapter, + private val innerAdapter: InteropTypeAdapter, + ) : InteropTypeAdapter { + override val fromType = innerAdapter.fromType + override val toType = outerAdapter.toType + override fun adapt(expression: IrExpression, builder: IrBuilderWithScope): IrExpression { + return outerAdapter.adapt(innerAdapter.adapt(expression, builder), builder) + } + } + /** * Current V8 Wasm GC mandates dataref type instead of structs and arrays */ @@ -588,18 +664,37 @@ class JsInteropFunctionsLowering(val context: WasmBackendContext) : DeclarationT } } + /** + * Current V8 Wasm GC mandates dataref type instead of structs and arrays + */ + inner class CheckNotNullNoAdapter(type: IrType) : InteropTypeAdapter { + override val fromType: IrType = type.makeNullable() + override val toType: IrType = type.makeNotNull() + override fun adapt(expression: IrExpression, builder: IrBuilderWithScope): IrExpression { + return builder.irCall(context.irBuiltIns.checkNotNullSymbol).also { + it.putValueArgument(0, expression) + it.putTypeArgument(0, fromType) + } + } + } + /** * Effectively `value?.let { adapter(it) }` */ inner class NullOrAdapter( - val adapter: InteropTypeAdapter + private val adapter: InteropTypeAdapter ) : InteropTypeAdapter { override val fromType: IrType = adapter.fromType.makeNullable() override val toType: IrType = adapter.toType.makeNullable() override fun adapt(expression: IrExpression, builder: IrBuilderWithScope): IrExpression { return builder.irComposite { val tmp = irTemporary(expression) - +irIfNull(toType, irGet(tmp), irNull(toType), irImplicitCast(adapter.adapt(irGet(tmp), builder), toType)) + +irIfNull( + type = toType, + subject = irGet(tmp), + thenPart = irNull(toType), + elsePart = irImplicitCast(adapter.adapt(irGet(tmp), builder), toType) + ) } } } @@ -608,18 +703,19 @@ class JsInteropFunctionsLowering(val context: WasmBackendContext) : DeclarationT * Effectively `adapter(value!!)` */ inner class CheckNotNullAndAdapter( - val adapter: InteropTypeAdapter + private val adapter: InteropTypeAdapter ) : InteropTypeAdapter { override val fromType: IrType = adapter.fromType.makeNullable() - override val toType: IrType = adapter.toType.makeNotNull() + override val toType: IrType = adapter.toType override fun adapt(expression: IrExpression, builder: IrBuilderWithScope): IrExpression { return builder.irComposite { - val tmp = irTemporary(expression) - +irCall(context.irBuiltIns.checkNotNullSymbol).also { - it.putValueArgument(0, irGet(tmp)) - it.putTypeArgument(0, fromType) - } - +adapter.adapt(irGet(tmp), builder) + val temp = irTemporary(expression) + +irIfNull( + type = toType, + subject = irGet(temp), + thenPart = irCall(this@JsInteropFunctionsLowering.context.wasmSymbols.throwNullPointerException), + elsePart = adapter.adapt(irImplicitCast(irGet(temp), adapter.fromType.makeNotNull()), builder), + ) } } } diff --git a/compiler/testData/codegen/boxWasmJsInterop/jsToKotlinAdapters.kt b/compiler/testData/codegen/boxWasmJsInterop/jsToKotlinAdapters.kt new file mode 100644 index 00000000000..5985859de02 --- /dev/null +++ b/compiler/testData/codegen/boxWasmJsInterop/jsToKotlinAdapters.kt @@ -0,0 +1,161 @@ +// IGNORE_BACKEND: JS_IR, JS + +inline fun checkNPE(body: () -> Unit) { + var throwed = false + try { + body() + } catch (e: NullPointerException) { + throwed = true + } + check(throwed) +} + +@JsFun("() => 'abc'") +external fun notNullString(): String + +@JsFun("() => null") +external fun notNull2String(): String + +@JsFun("() => 'abc'") +external fun nullString(): String? + +@JsFun("() => null") +external fun null2String(): String? + +fun testString() { + check(notNullString() == "abc") + checkNPE { notNull2String() } + check(nullString() == "abc") + check(null2String() == null) +} + +external interface ExternRef + +@JsFun("() => 'abc'") +external fun notNullExternRef(): ExternRef + +@JsFun("() => null") +external fun notNull2ExternRef(): ExternRef + +@JsFun("() => 'abc'") +external fun nullExternRef(): ExternRef? + +@JsFun("() => null") +external fun null2ExternRef(): ExternRef? + + +fun testExterRef() { + check(notNullExternRef() != null) + checkNPE { notNull2ExternRef() } + check(nullExternRef() != null) + check(null2ExternRef() == null) +} + +class DataRef + +@JsFun("(x) => x") +external fun notNullDataRef(x: DataRef): DataRef + +@JsFun("(x) => null") +external fun notNull2DataRef(x: DataRef): DataRef + +@JsFun("(x) => x") +external fun nullDataRef(x: DataRef): DataRef? + +@JsFun("(x) => null") +external fun null2DataRef(x: DataRef): DataRef? + +fun testDataRef() { + val dataRef = DataRef() + check(notNullDataRef(dataRef) == dataRef) + checkNPE { notNull2DataRef(dataRef) } + check (nullDataRef(dataRef) == dataRef) + check (null2DataRef(dataRef) == null) +} + +@JsFun("() => 123") +external fun notNullInt(): Int + +@JsFun("() => null") +external fun notNull2Int(): Int + +@JsFun("() => 123") +external fun nullInt(): Int? + +@JsFun("() => null") +external fun null2Int(): Int? + +fun testInt() { + check(notNullInt() == 123) + check(notNull2Int() == 0) + check(nullInt() == 123) + check(null2Int() == null) +} + +@JsFun("() => true") +external fun notNullBoolean(): Boolean + +@JsFun("() => null") +external fun notNull2Boolean(): Boolean + +@JsFun("() => true") +external fun nullBoolean(): Boolean? + +@JsFun("() => null") +external fun null2Boolean(): Boolean? + +fun testBoolean() { + check(notNullBoolean() == true) + check(notNull2Boolean() == false) + check(nullBoolean() == true) + check(null2Boolean() == null) +} + +@JsFun("() => 123") +external fun notNullShort(): Short + +@JsFun("() => null") +external fun notNull2Short(): Short + +@JsFun("() => 123") +external fun nullShort(): Short? + +@JsFun("() => null") +external fun null2Short(): Short? + +fun testShort() { + check(notNullShort() == 123.toShort()) + check(notNull2Short() == 0.toShort()) + check(nullShort() == 123.toShort()) + check(null2Short() == null) +} + +@JsFun("() => 123.5") +external fun notNullFloat(): Float + +@JsFun("() => null") +external fun notNull2Float(): Float + +@JsFun("() => 123.5") +external fun nullFloat(): Float? + +@JsFun("() => null") +external fun null2Float(): Float? + +fun testFloat() { + check(notNullFloat() == 123.5f) + check(notNull2Float() == 0.0f) + check(nullFloat() == 123.5f) + check(null2Float() == null) +} + +fun box(): String { + testString() + testExterRef() + testDataRef() + testInt() + testBoolean() + testShort() + testFloat() + return "OK" +} diff --git a/compiler/testData/codegen/boxWasmJsInterop/kotlinToJsAdapters.kt b/compiler/testData/codegen/boxWasmJsInterop/kotlinToJsAdapters.kt new file mode 100644 index 00000000000..887521cd0da --- /dev/null +++ b/compiler/testData/codegen/boxWasmJsInterop/kotlinToJsAdapters.kt @@ -0,0 +1,126 @@ +// IGNORE_BACKEND: JS_IR, JS + +@JsFun("(x) => { if (x !== 'abc') throw 'error' }") +external fun notNullString(x: String) + +@JsFun("(x) => { if (x !== 'abc') throw 'error' }") +external fun nullString(x: String?) + +@JsFun("(x) => { if (x !== null) throw 'error' }") +external fun null2String(x: String?) + +fun testString() { + notNullString("abc") + nullString("abc") + null2String(null) +} + +external interface ExternRef + +@JsFun("(x) => { if (x !== 'abc') throw 'error' }") +external fun notNullExternRef(x: ExternRef) + +@JsFun("(x) => { if (x !== 'abc') throw 'error' }") +external fun nullExternRef(x: ExternRef?) + +@JsFun("(x) => { if (x !== null) throw 'error' }") +external fun null2ExternRef(x: ExternRef?) + +@JsFun("() => 'abc'") +external fun getExternRef(): ExternRef + +fun testExterRef() { + val externRef = getExternRef() + notNullExternRef(externRef) + nullExternRef(externRef) + null2ExternRef(null) +} + +class DataRef + +@JsFun("(x, y) => { if (x === null) throw 'error' }") +external fun notNullDataRef(x: DataRef) + +@JsFun("(x, y) => { if (x === null) throw 'error' }") +external fun nullDataRef(x: DataRef?) + +@JsFun("(x, y) => { if (x !== null) throw 'error' }") +external fun null2DataRef(x: DataRef?) + +fun testDataRef() { + val dataRef = DataRef() + notNullDataRef(dataRef) + nullDataRef(dataRef) + null2DataRef(null) +} + +@JsFun("(x) => { if (x !== 123) throw 'error' }") +external fun notNullInt(x: Int) + +@JsFun("(x) => { if (x !== 123) throw 'error' }") +external fun nullInt(x: Int?) + +@JsFun("(x) => { if (x !== null) throw 'error' }") +external fun null2Int(x: Int?) + +fun testInt() { + notNullInt(123) + nullInt(123) + null2Int(null) +} + +@JsFun("(x) => { if (x !== true) throw 'error' }") +external fun notNullBoolean(x: Boolean) + +@JsFun("(x) => { if (x !== true) throw 'error' }") +external fun nullBoolean(x: Boolean?) + +@JsFun("(x) => { if (x !== null) throw 'error' }") +external fun null2Boolean(x: Boolean?) + +fun testBoolean() { + notNullBoolean(true) + nullBoolean(true) + null2Boolean(null) +} + +@JsFun("(x) => { x == 123 }") +external fun notNullShort(x: Short) + +@JsFun("(x) => { if (x !== 123) throw 'error' }") +external fun nullShort(x: Short?) + +@JsFun("(x) => { if (x !== null) throw 'error' }") +external fun null2Short(x: Short?) + +fun testShort() { + notNullShort(123.toShort()) + nullShort(123.toShort()) + null2Short(null) +} + +@JsFun("(x) => { if (x !== 123.5) throw 'error' }") +external fun notNullFloat(x: Float) + +@JsFun("(x) => { if (x !== 123.5) throw 'error' }") +external fun nullFloat(x: Float?) + +@JsFun("(x) => { if (x !== null) throw 'error' }") +external fun null2Float(x: Float?) + +fun testFloat() { + notNullFloat(123.5f) + nullFloat(123.5f) + null2Float(null) +} + +fun box(): String { + testString() + testExterRef() + testDataRef() + testInt() + testBoolean() + testShort() + testFloat() + return "OK" +} \ No newline at end of file diff --git a/compiler/testData/codegen/boxWasmJsInterop/nullableExternRefs.kt b/compiler/testData/codegen/boxWasmJsInterop/nullableExternRefs.kt index a9380f3e592..2df55dbee90 100644 --- a/compiler/testData/codegen/boxWasmJsInterop/nullableExternRefs.kt +++ b/compiler/testData/codegen/boxWasmJsInterop/nullableExternRefs.kt @@ -37,6 +37,16 @@ external fun getJsNullAsNonNullable(): EI @JsFun("() => undefined") external fun getJsUndefinedAsNonNullable(): EI +inline fun checkNPE(body: () -> Unit) { + var throwed = false + try { + body() + } catch (e: NullPointerException) { + throwed = true + } + assertTrue(throwed) +} + fun box(): String { val jsNull = getNull() val jsUndefined = getUndefined() @@ -55,11 +65,8 @@ fun box(): String { assertFalse(isJsUndefined(null)) assertTrue(isJsUndefined(jsUndefined)) - // TODO: Should these fail? - val n2 = getJsNullAsNonNullable() - val ud2 = getJsUndefinedAsNonNullable() - assertTrue(isJsNull(n2)) - assertTrue(isJsUndefined(ud2)) + checkNPE(::getJsNullAsNonNullable) + checkNPE(::getJsUndefinedAsNonNullable) return "OK" } \ No newline at end of file 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 88148be88d7..779755af838 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 @@ -55,6 +55,18 @@ public class IrCodegenWasmJsInteropJsTestGenerated extends AbstractIrCodegenWasm runTest("compiler/testData/codegen/boxWasmJsInterop/jsExport.kt"); } + @Test + @TestMetadata("jsToKotlinAdapters.kt") + public void testJsToKotlinAdapters() throws Exception { + runTest("compiler/testData/codegen/boxWasmJsInterop/jsToKotlinAdapters.kt"); + } + + @Test + @TestMetadata("kotlinToJsAdapters.kt") + public void testKotlinToJsAdapters() throws Exception { + runTest("compiler/testData/codegen/boxWasmJsInterop/kotlinToJsAdapters.kt"); + } + @Test @TestMetadata("longStrings.kt") public void testLongStrings() throws Exception { 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 2cfb94336ca..55c368cfbe5 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 @@ -55,6 +55,16 @@ public class IrCodegenWasmJsInteropWasmTestGenerated extends AbstractIrCodegenWa runTest("compiler/testData/codegen/boxWasmJsInterop/jsExport.kt"); } + @TestMetadata("jsToKotlinAdapters.kt") + public void testJsToKotlinAdapters() throws Exception { + runTest("compiler/testData/codegen/boxWasmJsInterop/jsToKotlinAdapters.kt"); + } + + @TestMetadata("kotlinToJsAdapters.kt") + public void testKotlinToJsAdapters() throws Exception { + runTest("compiler/testData/codegen/boxWasmJsInterop/kotlinToJsAdapters.kt"); + } + @TestMetadata("longStrings.kt") public void testLongStrings() throws Exception { runTest("compiler/testData/codegen/boxWasmJsInterop/longStrings.kt"); diff --git a/libraries/stdlib/wasm/internal/kotlin/wasm/internal/ExternalWrapper.kt b/libraries/stdlib/wasm/internal/kotlin/wasm/internal/ExternalWrapper.kt index 6432f81153f..51fd94d5a8b 100644 --- a/libraries/stdlib/wasm/internal/kotlin/wasm/internal/ExternalWrapper.kt +++ b/libraries/stdlib/wasm/internal/kotlin/wasm/internal/ExternalWrapper.kt @@ -81,6 +81,36 @@ private external fun externrefHashCode(ref: ExternalInterfaceType): Int @JsFun("ref => String(ref)") private external fun externrefToString(ref: ExternalInterfaceType): String +@JsFun("ref => Number(ref)") +private external fun externrefToInt(ref: ExternalInterfaceType): Int + +@JsFun("ref => Number(ref)") +private external fun externrefToLong(ref: ExternalInterfaceType): Long + +@JsFun("ref => Boolean(ref)") +private external fun externrefToBoolean(ref: ExternalInterfaceType): Boolean + +@JsFun("ref => Number(ref)") +private external fun externrefToFloat(ref: ExternalInterfaceType): Float + +@JsFun("ref => Number(ref)") +private external fun externrefToDouble(ref: ExternalInterfaceType): Double + +@JsFun("x => x") +private external fun intToExternref(x: Int): ExternalInterfaceType + +@JsFun("x => x") +private external fun longToExternref(x: Long): ExternalInterfaceType + +@JsFun("x => x") +private external fun booleanToExternref(x: Boolean): ExternalInterfaceType + +@JsFun("x => x") +private external fun floatToExternref(x: Float): ExternalInterfaceType + +@JsFun("x => x") +private external fun doubleToExternref(x: Double): ExternalInterfaceType + @JsFun("(lhs, rhs) => lhs === rhs") private external fun externrefEquals(lhs: ExternalInterfaceType, rhs: ExternalInterfaceType): Boolean @@ -222,9 +252,6 @@ internal external fun jsTrue(): ExternalInterfaceType @JsFun("() => false") internal external fun jsFalse(): ExternalInterfaceType -internal fun kotlinToJsBooleanAdapter(x: Boolean): ExternalInterfaceType = - if (x) jsTrue() else jsFalse() - internal fun kotlinToJsAnyAdapter(x: Any): ExternalInterfaceType = anyToExternRef(x) @@ -234,3 +261,43 @@ internal fun jsToKotlinAnyAdapter(x: ExternalInterfaceType): Any? = internal fun jsToKotlinByteAdapter(x: Int): Byte = x.toByte() internal fun jsToKotlinShortAdapter(x: Int): Short = x.toShort() internal fun jsToKotlinCharAdapter(x: Int): Char = x.toChar() + +internal fun externRefToKotlinIntAdapter(x: ExternalInterfaceType): Int = + externrefToInt(x) + +internal fun externRefToKotlinBooleanAdapter(x: ExternalInterfaceType): Boolean = + externrefToBoolean(x) + +internal fun externRefToKotlinLongAdapter(x: ExternalInterfaceType): Long = + externrefToLong(x) + +internal fun externRefToKotlinFloatAdapter(x: ExternalInterfaceType): Float = + externrefToFloat(x) + +internal fun externRefToKotlinDoubleAdapter(x: ExternalInterfaceType): Double = + externrefToDouble(x) + + +internal fun kotlinIntToExternRefAdapter(x: Int): ExternalInterfaceType = + intToExternref(x) + +internal fun kotlinBooleanToExternRefAdapter(x: Boolean): ExternalInterfaceType = + if (x) jsTrue() else jsFalse() + +internal fun kotlinLongToExternRefAdapter(x: Long): ExternalInterfaceType = + longToExternref(x) + +internal fun kotlinFloatToExternRefAdapter(x: Float): ExternalInterfaceType = + floatToExternref(x) + +internal fun kotlinDoubleToExternRefAdapter(x: Double): ExternalInterfaceType = + doubleToExternref(x) + +internal fun kotlinByteToExternRefAdapter(x: Byte): ExternalInterfaceType = + intToExternref(x.toInt()) + +internal fun kotlinShortToExternRefAdapter(x: Short): ExternalInterfaceType = + intToExternref(x.toInt()) + +internal fun kotlinCharToExternRefAdapter(x: Char): ExternalInterfaceType = + intToExternref(x.toInt())