From 1b2b7ce34cb56c4950a9bbed5270c3905a58d664 Mon Sep 17 00:00:00 2001 From: Igor Yakovlev Date: Tue, 27 Dec 2022 16:22:57 +0100 Subject: [PATCH] [Wasm] Support Number type for external functions --- .../kotlin/backend/wasm/WasmSymbols.kt | 1 + .../wasm/lower/JsInteropFunctionsLowering.kt | 21 ++++++++++++++ .../boxWasmJsInterop/jsToKotlinAdapters.kt | 21 ++++++++++++++ .../boxWasmJsInterop/kotlinToJsAdapters.kt | 28 +++++++++++++++++++ .../kotlin/wasm/internal/ExternalWrapper.kt | 3 ++ 5 files changed, 74 insertions(+) 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 2a819f70165..7b37d256a54 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 @@ -287,6 +287,7 @@ class WasmSymbols( inner class JsInteropAdapters { val kotlinToJsStringAdapter = getInternalFunction("kotlinToJsStringAdapter") val kotlinToJsAnyAdapter = getInternalFunction("kotlinToJsAnyAdapter") + val numberToDoubleAdapter = getInternalFunction("numberToDoubleAdapter") val jsCheckIsNullOrUndefinedAdapter = getInternalFunction("jsCheckIsNullOrUndefinedAdapter") 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 a46f61175ce..bca09b97595 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 @@ -65,6 +65,13 @@ class JsInteropFunctionsLowering(val context: WasmBackendContext) : DeclarationT return (newDeclarations ?: listOf(declaration)) + additionalDeclarations } + private fun doubleIfNumber(possiblyNumber: IrType): IrType { + val isNullable = possiblyNumber.isNullable() + val notNullType = possiblyNumber.makeNotNull() + if (notNullType != builtIns.numberType) return possiblyNumber + return if (isNullable) builtIns.doubleType.makeNullable() else builtIns.doubleType + } + /** * external fun foo(x: KotlinType): KotlinType * @@ -79,6 +86,9 @@ class JsInteropFunctionsLowering(val context: WasmBackendContext) : DeclarationT if (function.valueParameters.any { it.defaultValue != null }) return null + // Patch function types for Number parameters as double + function.returnType = doubleIfNumber(function.returnType) + val valueParametersAdapters = function.valueParameters.map { it.type.kotlinToJsAdapterIfNeeded(isReturn = false) } @@ -193,6 +203,16 @@ class JsInteropFunctionsLowering(val context: WasmBackendContext) : DeclarationT } val notNullType = makeNotNull() + + if (notNullType == builtIns.numberType) { + return NullOrAdapter( + CombineAdapter( + FunctionBasedAdapter(adapters.kotlinDoubleToExternRefAdapter.owner), + FunctionBasedAdapter(adapters.numberToDoubleAdapter.owner) + ) + ) + } + val primitiveToExternRefAdapter = when (notNullType) { builtIns.byteType -> adapters.kotlinByteToExternRefAdapter.owner builtIns.shortType -> adapters.kotlinShortToExternRefAdapter.owner @@ -222,6 +242,7 @@ class JsInteropFunctionsLowering(val context: WasmBackendContext) : DeclarationT builtIns.stringType -> return FunctionBasedAdapter(adapters.kotlinToJsStringAdapter.owner) builtIns.booleanType -> return FunctionBasedAdapter(adapters.kotlinBooleanToExternRefAdapter.owner) builtIns.anyType -> return FunctionBasedAdapter(adapters.kotlinToJsAnyAdapter.owner) + builtIns.numberType -> return FunctionBasedAdapter(adapters.numberToDoubleAdapter.owner) builtIns.byteType, builtIns.shortType, diff --git a/compiler/testData/codegen/boxWasmJsInterop/jsToKotlinAdapters.kt b/compiler/testData/codegen/boxWasmJsInterop/jsToKotlinAdapters.kt index 5985859de02..3a086ac080d 100644 --- a/compiler/testData/codegen/boxWasmJsInterop/jsToKotlinAdapters.kt +++ b/compiler/testData/codegen/boxWasmJsInterop/jsToKotlinAdapters.kt @@ -149,6 +149,26 @@ fun testFloat() { check(null2Float() == null) } + +@JsFun("() => 123.5") +external fun notNullNumber(): Number + +@JsFun("() => null") +external fun notNull2Number(): Number + +@JsFun("() => 123.5") +external fun nullNumber(): Number? + +@JsFun("() => null") +external fun null2Number(): Number? + +fun testNumber() { + check(notNullNumber() == 123.5) + check(notNull2Number() == 0.0) + check(nullNumber() == 123.5) + check(null2Number() == null) +} + fun box(): String { testString() testExterRef() @@ -157,5 +177,6 @@ fun box(): String { testBoolean() testShort() testFloat() + testNumber() return "OK" } diff --git a/compiler/testData/codegen/boxWasmJsInterop/kotlinToJsAdapters.kt b/compiler/testData/codegen/boxWasmJsInterop/kotlinToJsAdapters.kt index 887521cd0da..a79f3b1e1ca 100644 --- a/compiler/testData/codegen/boxWasmJsInterop/kotlinToJsAdapters.kt +++ b/compiler/testData/codegen/boxWasmJsInterop/kotlinToJsAdapters.kt @@ -114,6 +114,33 @@ fun testFloat() { null2Float(null) } +@JsFun("(x) => { if (x !== 123.5) throw 'error' }") +external fun notNullNumber(x: Number) + +@JsFun("(x) => { if (x !== 123.5) throw 'error' }") +external fun nullNumber(x: Number?) + +@JsFun("(x) => { if (x !== null) throw 'error' }") +external fun null2Number(x: Number?) + +@JsFun("(x) => { if (x !== 123) throw 'error' }") +external fun byte2Number(x: Number) + +@JsFun("(x) => { if (x !== 123) throw 'error' }") +external fun notNullByte2Number(x: Number?) + +@JsFun("(x) => { if (x !== null) throw 'error' }") +external fun nullByte2Number(x: Number?) + +fun testNumber() { + notNullNumber(123.5) + nullNumber(123.5) + null2Number(null) + byte2Number(123) + notNullByte2Number(123) + nullByte2Number(null) +} + fun box(): String { testString() testExterRef() @@ -122,5 +149,6 @@ fun box(): String { testBoolean() testShort() testFloat() + testNumber() return "OK" } \ No newline at end of file diff --git a/libraries/stdlib/wasm/internal/kotlin/wasm/internal/ExternalWrapper.kt b/libraries/stdlib/wasm/internal/kotlin/wasm/internal/ExternalWrapper.kt index 49f2a8ee0c2..48306d7697e 100644 --- a/libraries/stdlib/wasm/internal/kotlin/wasm/internal/ExternalWrapper.kt +++ b/libraries/stdlib/wasm/internal/kotlin/wasm/internal/ExternalWrapper.kt @@ -261,6 +261,9 @@ private val jsEmptyString by lazy(::getJsEmptyString) private val jsTrue by lazy(::getJsTrue) private val jsFalse by lazy(::getJsFalse) +internal fun numberToDoubleAdapter(x: Number): Double = + x.toDouble() + internal fun kotlinToJsAnyAdapter(x: Any?): ExternalInterfaceType? = if (x == null) null else anyToExternRef(x)