From 22c3f03e49d34a1ae37631625b60373a222a3386 Mon Sep 17 00:00:00 2001 From: Michael Schmidt Date: Mon, 11 Nov 2024 14:34:04 +0100 Subject: [PATCH] Add support for i128 and u128 (#4222) --- CHANGELOG.md | 4 + crates/cli-support/src/descriptor.rs | 6 ++ crates/cli-support/src/js/binding.rs | 55 ++++++++++- crates/cli-support/src/wit/incoming.rs | 28 ++++++ crates/cli-support/src/wit/outgoing.rs | 30 ++++++ crates/cli-support/src/wit/standard.rs | 14 +++ crates/cli/tests/reference/int128.d.ts | 7 ++ crates/cli/tests/reference/int128.js | 74 ++++++++++++++ crates/cli/tests/reference/int128.rs | 28 ++++++ crates/cli/tests/reference/int128.wat | 19 ++++ crates/macro/ui-tests/missing-catch.stderr | 2 +- .../ui-tests/traits-not-implemented.stderr | 2 +- guide/src/reference/types/numbers.md | 55 ++++++++++- src/convert/impls.rs | 97 ++++++++++++++++++- src/describe.rs | 4 + tests/wasm/optional_primitives.js | 18 ++++ tests/wasm/optional_primitives.rs | 67 +++++++++++++ 17 files changed, 502 insertions(+), 8 deletions(-) create mode 100644 crates/cli/tests/reference/int128.d.ts create mode 100644 crates/cli/tests/reference/int128.js create mode 100644 crates/cli/tests/reference/int128.rs create mode 100644 crates/cli/tests/reference/int128.wat diff --git a/CHANGELOG.md b/CHANGELOG.md index e4535f9a886..8fd1d648ac3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,10 @@ # `wasm-bindgen` Change Log -------------------------------------------------------------------------------- +## Unreleased + +Unstable, local branch + ## [0.2.95](https://github.com/rustwasm/wasm-bindgen/compare/0.2.94...0.2.95) Released 2024-10-10 diff --git a/crates/cli-support/src/descriptor.rs b/crates/cli-support/src/descriptor.rs index 5dec8d86d71..d1b129ed77a 100644 --- a/crates/cli-support/src/descriptor.rs +++ b/crates/cli-support/src/descriptor.rs @@ -19,6 +19,8 @@ tys! { U32 I64 U64 + I128 + U128 F32 F64 BOOLEAN @@ -55,6 +57,8 @@ pub enum Descriptor { U32, I64, U64, + I128, + U128, F32, F64, Boolean, @@ -132,11 +136,13 @@ impl Descriptor { I16 => Descriptor::I16, I32 => Descriptor::I32, I64 => Descriptor::I64, + I128 => Descriptor::I128, U8 if clamped => Descriptor::ClampedU8, U8 => Descriptor::U8, U16 => Descriptor::U16, U32 => Descriptor::U32, U64 => Descriptor::U64, + U128 => Descriptor::U128, F32 => Descriptor::F32, F64 => Descriptor::F64, BOOLEAN => Descriptor::Boolean, diff --git a/crates/cli-support/src/js/binding.rs b/crates/cli-support/src/js/binding.rs index 27d58549ba6..807824bf61a 100644 --- a/crates/cli-support/src/js/binding.rs +++ b/crates/cli-support/src/js/binding.rs @@ -595,6 +595,23 @@ fn instruction( ) } + fn int128_to_int64x2(val: &str) -> (String, String) { + // we don't need to perform any conversion here, because the JS + // WebAssembly API will automatically convert the bigints to 64 bits + // for us. This even allows us to ignore signedness. + let low = val.to_owned(); + let high = format!("{val} >> BigInt(64)"); + (low, high) + } + fn int64x2_to_int128(low: String, high: String, signed: bool) -> String { + let low = format!("BigInt.asUintN(64, {low})"); + if signed { + format!("({low} | ({high} << BigInt(64)))") + } else { + format!("({low} | (BigInt.asUintN(64, {high}) << BigInt(64)))") + } + } + match instr { Instruction::ArgGet(n) => { let arg = js.arg(*n).to_string(); @@ -687,6 +704,36 @@ fn instruction( } } + Instruction::Int128ToWasm => { + let val = js.pop(); + js.assert_bigint(&val); + let (low, high) = int128_to_int64x2(&val); + js.push(low); + js.push(high); + } + Instruction::WasmToInt128 { signed } => { + let high = js.pop(); + let low = js.pop(); + js.push(int64x2_to_int128(low, high, *signed)); + } + + Instruction::OptionInt128ToWasm => { + let val = js.pop(); + js.cx.expose_is_like_none(); + js.assert_optional_bigint(&val); + let (low, high) = int128_to_int64x2(&val); + js.push(format!("!isLikeNone({val})")); + js.push(format!("isLikeNone({val}) ? BigInt(0) : {low}")); + js.push(format!("isLikeNone({val}) ? BigInt(0) : {high}")); + } + Instruction::OptionWasmToInt128 { signed } => { + let high = js.pop(); + let low = js.pop(); + let present = js.pop(); + let val = int64x2_to_int128(low, high, *signed); + js.push(format!("{present} === 0 ? undefined : {val}")); + } + Instruction::WasmToStringEnum { name } => { let index = js.pop(); js.push(wasm_to_string_enum(name, &index)) @@ -917,6 +964,8 @@ fn instruction( js.push(format!( "isLikeNone({val}) ? {zero} : {val}", zero = if *ty == ValType::I64 { + // We can't use bigint literals for now. See: + // https://github.com/rustwasm/wasm-bindgen/issues/4246 "BigInt(0)" } else { "0" @@ -1429,7 +1478,11 @@ fn adapter2ts(ty: &AdapterType, dst: &mut String) { | AdapterType::F32 | AdapterType::F64 | AdapterType::NonNull => dst.push_str("number"), - AdapterType::I64 | AdapterType::S64 | AdapterType::U64 => dst.push_str("bigint"), + AdapterType::I64 + | AdapterType::S64 + | AdapterType::U64 + | AdapterType::S128 + | AdapterType::U128 => dst.push_str("bigint"), AdapterType::String => dst.push_str("string"), AdapterType::Externref => dst.push_str("any"), AdapterType::Bool => dst.push_str("boolean"), diff --git a/crates/cli-support/src/wit/incoming.rs b/crates/cli-support/src/wit/incoming.rs index 06c7160cb63..2ec275a7bea 100644 --- a/crates/cli-support/src/wit/incoming.rs +++ b/crates/cli-support/src/wit/incoming.rs @@ -90,6 +90,20 @@ impl InstructionBuilder<'_, '_> { Descriptor::U32 => self.number(AdapterType::U32, WasmVT::I32), Descriptor::I64 => self.number(AdapterType::S64, WasmVT::I64), Descriptor::U64 => self.number(AdapterType::U64, WasmVT::I64), + Descriptor::I128 => { + self.instruction( + &[AdapterType::S128], + Instruction::Int128ToWasm, + &[AdapterType::I64, AdapterType::I64], + ); + } + Descriptor::U128 => { + self.instruction( + &[AdapterType::U128], + Instruction::Int128ToWasm, + &[AdapterType::I64, AdapterType::I64], + ); + } Descriptor::F32 => { self.get(AdapterType::F32); self.output.push(AdapterType::F32); @@ -285,6 +299,20 @@ impl InstructionBuilder<'_, '_> { Descriptor::F32 => self.in_option_native(ValType::F32), Descriptor::F64 => self.in_option_native(ValType::F64), Descriptor::I64 | Descriptor::U64 => self.in_option_native(ValType::I64), + Descriptor::I128 => { + self.instruction( + &[AdapterType::S128.option()], + Instruction::OptionInt128ToWasm, + &[AdapterType::I32, AdapterType::I64, AdapterType::I64], + ); + } + Descriptor::U128 => { + self.instruction( + &[AdapterType::U128.option()], + Instruction::OptionInt128ToWasm, + &[AdapterType::I32, AdapterType::I64, AdapterType::I64], + ); + } Descriptor::Boolean => { self.instruction( &[AdapterType::Bool.option()], diff --git a/crates/cli-support/src/wit/outgoing.rs b/crates/cli-support/src/wit/outgoing.rs index a3563551bb5..bde181b7c12 100644 --- a/crates/cli-support/src/wit/outgoing.rs +++ b/crates/cli-support/src/wit/outgoing.rs @@ -65,6 +65,20 @@ impl InstructionBuilder<'_, '_> { Descriptor::U32 => self.outgoing_i32(AdapterType::U32), Descriptor::I64 => self.outgoing_i64(AdapterType::I64), Descriptor::U64 => self.outgoing_i64(AdapterType::U64), + Descriptor::I128 => { + self.instruction( + &[AdapterType::I64, AdapterType::I64], + Instruction::WasmToInt128 { signed: true }, + &[AdapterType::S128], + ); + } + Descriptor::U128 => { + self.instruction( + &[AdapterType::I64, AdapterType::I64], + Instruction::WasmToInt128 { signed: false }, + &[AdapterType::U128], + ); + } Descriptor::F32 => { self.get(AdapterType::F32); self.output.push(AdapterType::F32); @@ -267,6 +281,20 @@ impl InstructionBuilder<'_, '_> { Descriptor::U64 => self.option_native(false, ValType::I64), Descriptor::F32 => self.option_native(true, ValType::F32), Descriptor::F64 => self.option_native(true, ValType::F64), + Descriptor::I128 => { + self.instruction( + &[AdapterType::I32, AdapterType::I64, AdapterType::I64], + Instruction::OptionWasmToInt128 { signed: true }, + &[AdapterType::S128.option()], + ); + } + Descriptor::U128 => { + self.instruction( + &[AdapterType::I32, AdapterType::I64, AdapterType::I64], + Instruction::OptionWasmToInt128 { signed: false }, + &[AdapterType::U128.option()], + ); + } Descriptor::Boolean => { self.instruction( &[AdapterType::I32], @@ -360,6 +388,8 @@ impl InstructionBuilder<'_, '_> { | Descriptor::F64 | Descriptor::I64 | Descriptor::U64 + | Descriptor::I128 + | Descriptor::U128 | Descriptor::Boolean | Descriptor::Char | Descriptor::Enum { .. } diff --git a/crates/cli-support/src/wit/standard.rs b/crates/cli-support/src/wit/standard.rs index aee0e0845e9..3875b48b9ee 100644 --- a/crates/cli-support/src/wit/standard.rs +++ b/crates/cli-support/src/wit/standard.rs @@ -71,10 +71,12 @@ pub enum AdapterType { S16, S32, S64, + S128, U8, U16, U32, U64, + U128, F32, F64, String, @@ -141,6 +143,18 @@ pub enum Instruction { output: AdapterType, }, + /// Pops a 128-bit integer and pushes 2 Wasm 64-bit ints. + Int128ToWasm, + /// Pops 2 Wasm 64-bit ints and pushes a 128-bit integer. + WasmToInt128 { + signed: bool, + }, + + OptionInt128ToWasm, + OptionWasmToInt128 { + signed: bool, + }, + /// Pops a Wasm `i32` and pushes the enum variant as a string WasmToStringEnum { name: String, diff --git a/crates/cli/tests/reference/int128.d.ts b/crates/cli/tests/reference/int128.d.ts new file mode 100644 index 00000000000..183cef4758a --- /dev/null +++ b/crates/cli/tests/reference/int128.d.ts @@ -0,0 +1,7 @@ +/* tslint:disable */ +/* eslint-disable */ +export function echo_i128(a: bigint): bigint; +export function echo_u128(a: bigint): bigint; +export function echo_option_i128(a?: bigint): bigint | undefined; +export function echo_option_u128(a?: bigint): bigint | undefined; +export function throw_i128(): bigint; diff --git a/crates/cli/tests/reference/int128.js b/crates/cli/tests/reference/int128.js new file mode 100644 index 00000000000..04b865d7fb1 --- /dev/null +++ b/crates/cli/tests/reference/int128.js @@ -0,0 +1,74 @@ +let wasm; +export function __wbg_set_wasm(val) { + wasm = val; +} + +/** + * @param {bigint} a + * @returns {bigint} + */ +export function echo_i128(a) { + const ret = wasm.echo_i128(a, a >> BigInt(64)); + return (BigInt.asUintN(64, ret[0]) | (ret[1] << BigInt(64))); +} + +/** + * @param {bigint} a + * @returns {bigint} + */ +export function echo_u128(a) { + const ret = wasm.echo_u128(a, a >> BigInt(64)); + return (BigInt.asUintN(64, ret[0]) | (BigInt.asUintN(64, ret[1]) << BigInt(64))); +} + +function isLikeNone(x) { + return x === undefined || x === null; +} +/** + * @param {bigint | undefined} [a] + * @returns {bigint | undefined} + */ +export function echo_option_i128(a) { + const ret = wasm.echo_option_i128(!isLikeNone(a), isLikeNone(a) ? BigInt(0) : a, isLikeNone(a) ? BigInt(0) : a >> BigInt(64)); + return ret[0] === 0 ? undefined : (BigInt.asUintN(64, ret[1]) | (ret[2] << BigInt(64))); +} + +/** + * @param {bigint | undefined} [a] + * @returns {bigint | undefined} + */ +export function echo_option_u128(a) { + const ret = wasm.echo_option_u128(!isLikeNone(a), isLikeNone(a) ? BigInt(0) : a, isLikeNone(a) ? BigInt(0) : a >> BigInt(64)); + return ret[0] === 0 ? undefined : (BigInt.asUintN(64, ret[1]) | (BigInt.asUintN(64, ret[2]) << BigInt(64))); +} + +const heap = new Array(128).fill(undefined); + +heap.push(undefined, null, true, false); + +function getObject(idx) { return heap[idx]; } + +let heap_next = heap.length; + +function dropObject(idx) { + if (idx < 132) return; + heap[idx] = heap_next; + heap_next = idx; +} + +function takeObject(idx) { + const ret = getObject(idx); + dropObject(idx); + return ret; +} +/** + * @returns {bigint} + */ +export function throw_i128() { + const ret = wasm.throw_i128(); + if (ret[3]) { + throw takeObject(ret[2]); + } + return (BigInt.asUintN(64, ret[0]) | (ret[1] << BigInt(64))); +} + diff --git a/crates/cli/tests/reference/int128.rs b/crates/cli/tests/reference/int128.rs new file mode 100644 index 00000000000..350faa34a43 --- /dev/null +++ b/crates/cli/tests/reference/int128.rs @@ -0,0 +1,28 @@ +use wasm_bindgen::prelude::*; + +#[wasm_bindgen] +pub fn echo_i128(a: i128) -> i128 { + a +} +#[wasm_bindgen] +pub fn echo_u128(a: u128) -> u128 { + a +} + +#[wasm_bindgen] +pub fn echo_option_i128(a: Option) -> Option { + a +} +#[wasm_bindgen] +pub fn echo_option_u128(a: Option) -> Option { + a +} + +#[wasm_bindgen] +pub fn throw_i128() -> Result { + Ok(0_i128) +} +// #[wasm_bindgen] +// pub fn throw_option_i128() -> Result, JsError> { +// Ok(None) +// } diff --git a/crates/cli/tests/reference/int128.wat b/crates/cli/tests/reference/int128.wat new file mode 100644 index 00000000000..61c7eab3ea3 --- /dev/null +++ b/crates/cli/tests/reference/int128.wat @@ -0,0 +1,19 @@ +(module $reference_test.wasm + (type (;0;) (func (result i64 i64 i32 i32))) + (type (;1;) (func (param i32 i64 i64) (result i32 i64 i64))) + (type (;2;) (func (param i64 i64) (result i64 i64))) + (func $"echo_option_i128 multivalue shim" (;0;) (type 1) (param i32 i64 i64) (result i32 i64 i64)) + (func $"echo_option_u128 multivalue shim" (;1;) (type 1) (param i32 i64 i64) (result i32 i64 i64)) + (func $"throw_i128 multivalue shim" (;2;) (type 0) (result i64 i64 i32 i32)) + (func $"echo_i128 multivalue shim" (;3;) (type 2) (param i64 i64) (result i64 i64)) + (func $"echo_u128 multivalue shim" (;4;) (type 2) (param i64 i64) (result i64 i64)) + (memory (;0;) 17) + (export "memory" (memory 0)) + (export "echo_i128" (func $"echo_i128 multivalue shim")) + (export "echo_u128" (func $"echo_u128 multivalue shim")) + (export "echo_option_i128" (func $"echo_option_i128 multivalue shim")) + (export "echo_option_u128" (func $"echo_option_u128 multivalue shim")) + (export "throw_i128" (func $"throw_i128 multivalue shim")) + (@custom "target_features" (after code) "\04+\0amultivalue+\0fmutable-globals+\0freference-types+\08sign-ext") +) + diff --git a/crates/macro/ui-tests/missing-catch.stderr b/crates/macro/ui-tests/missing-catch.stderr index 02a74e62f00..e21623d69e3 100644 --- a/crates/macro/ui-tests/missing-catch.stderr +++ b/crates/macro/ui-tests/missing-catch.stderr @@ -12,5 +12,5 @@ error[E0277]: the trait bound `Result` parameter | `Option` return value | JavaScript representation | |:---:|:---:|:---:|:---:|:---:|:---:|:---:| -| Yes | No | No | Yes | Yes | Yes | A JavaScript number value | +| Yes | No | No | Yes | Yes | Yes | A JavaScript number or bigint value | + +[JavaScript `Number`s](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number#number_encoding) are 64-bit floating point value under the hood and cannot accurately represent all of Rust's numeric types. `wasm-bindgen` will automatically use either [`BigInt`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/BigInt) or `Number` to accurately represent Rust's numeric types in JavaScript: + +- `u8`, `i8`, `u16`, `i16`, `u32`, `i32`, `isize`, `usize`, `f32`, and `f64` will be represented as `Number` in JavaScript. +- `u64`, `i64`, `u128`, and `i128` will be represented as `BigInt` in JavaScript. + +> **Note**: Wasm is currently a 32-bit architecture, so `isize` and `usize` are 32-bit integers and "fit" into a JavaScript `Number`. + +## Converting from JavaScript to Rust + +`wasm-bindgen` will automatically handle the conversion of JavaScript numbers to Rust numeric types. The conversion rules are as follows: + +### `Number` to `u8`, `i8`, `u16`, `i16`, `u32`, `i32`, `isize`, and `usize` + +If the JavaScript number is `Infinity`, `-Infinity`, or `NaN`, then the Rust value will be 0. Otherwise, the JavaScript number will rounded towards zero (see [`Math.trunc`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/trunc) or [`f64::trunc`](https://doc.rust-lang.org/std/primitive.f64.html#method.trunc)). If the rounded number is too large or too small for the target integer type, it will wrap around. + +For example, if the target type is `i8`, Rust will see the following values for the following inputs: + +| JS input number | Rust value (`i8`) | +| --------------: | :---------------- | +| 42 | 42 | +| -42 | -42 | +| 1.999 | 1 | +| -1.999 | -1 | +| 127 | 127 | +| 128 | -128 | +| 255 | -1 | +| 256 | 0 | +| -0 | 0 | +| `±Infinity` | 0 | +| `NaN` | 0 | + +This is the same behavior as assigning the JavaScript `Number` to a [typed array](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/TypedArray) of the appropriate integer type in JavaScript, i.e. `new Uint8Array([value])[0]`. + +Except for the handling of `Infinity` and `-Infinity`, this is the same behavior as [casting](https://doc.rust-lang.org/reference/expressions/operator-expr.html#numeric-cast) `f64` to the appropriate integer type in Rust, i.e. `value_f64 as u32`. + +### `BigInt` to `u64`, `i64`, `u128`, and `i128` + +If the JavaScript `BigInt` is too large or too small for the target integer type, it will wrap around. + +This is the same behavior as assigning the JavaScript `BigInt` to a [typed array](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/TypedArray) for 64-bit integer types in JavaScript, i.e. `new Int64Array([value])[0]`. + +### `Number` to `f32` + +The JavaScript `Number` is converted to a Rust `f32` using the same rules as [casting](https://doc.rust-lang.org/reference/expressions/operator-expr.html#numeric-cast) `f64` to `f32` in Rust, i.e. `value_f64 as f32`. + +This is the same behavior as [`Math.fround`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/fround) or assigning the JavaScript `Number` to a [`Float32Array`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Float32Array) in JavaScript, i.e. `new Float32Array([value])[0]`. + +### `Number` to `f64` + +Since JavaScript numbers are 64-bit floating point values, converting a JavaScript `Number` to a Rust `f64` is a no-op. ## Example Rust Usage diff --git a/src/convert/impls.rs b/src/convert/impls.rs index e54f0669c5a..87d6e910baf 100644 --- a/src/convert/impls.rs +++ b/src/convert/impls.rs @@ -29,6 +29,43 @@ impl WasmAbi for T { } } +impl WasmAbi for i128 { + type Prim1 = u64; + type Prim2 = u64; + type Prim3 = (); + type Prim4 = (); + + #[inline] + fn split(self) -> (u64, u64, (), ()) { + let low = self as u64; + let high = (self >> 64) as u64; + (low, high, (), ()) + } + + #[inline] + fn join(low: u64, high: u64, _: (), _: ()) -> Self { + ((high as u128) << 64 | low as u128) as i128 + } +} +impl WasmAbi for u128 { + type Prim1 = u64; + type Prim2 = u64; + type Prim3 = (); + type Prim4 = (); + + #[inline] + fn split(self) -> (u64, u64, (), ()) { + let low = self as u64; + let high = (self >> 64) as u64; + (low, high, (), ()) + } + + #[inline] + fn join(low: u64, high: u64, _: (), _: ()) -> Self { + (high as u128) << 64 | low as u128 + } +} + impl> WasmAbi for Option { /// Whether this `Option` is a `Some` value. type Prim1 = u32; @@ -99,14 +136,68 @@ macro_rules! type_wasm_native { } type_wasm_native!( + i64 as i64 + u64 as u64 + i128 as i128 + u128 as u128 + f64 as f64 +); + +/// The sentinel value is 2^32 + 1 for 32-bit primitive types. +/// +/// 2^32 + 1 is used, because it's the smallest positive integer that cannot be +/// represented by any 32-bit primitive. While any value >= 2^32 works as a +/// sentinel value for 32-bit integers, it's a bit more tricky for `f32`. `f32` +/// can represent all powers of 2 up to 2^127 exactly. And between 2^32 and 2^33, +/// `f32` can represent all integers 2^32+512*k exactly. +const F64_ABI_OPTION_SENTINEL: f64 = 4294967297_f64; + +macro_rules! type_wasm_native_f64_option { + ($($t:tt as $c:tt)*) => ($( + impl IntoWasmAbi for $t { + type Abi = $c; + + #[inline] + fn into_abi(self) -> $c { self as $c } + } + + impl FromWasmAbi for $t { + type Abi = $c; + + #[inline] + unsafe fn from_abi(js: $c) -> Self { js as $t } + } + + impl IntoWasmAbi for Option<$t> { + type Abi = f64; + + #[inline] + fn into_abi(self) -> Self::Abi { + self.map(|v| v as $c as f64).unwrap_or(F64_ABI_OPTION_SENTINEL) + } + } + + impl FromWasmAbi for Option<$t> { + type Abi = f64; + + #[inline] + unsafe fn from_abi(js: Self::Abi) -> Self { + if js == F64_ABI_OPTION_SENTINEL { + None + } else { + Some(js as $c as $t) + } + } + } + )*) +} + +type_wasm_native_f64_option!( i32 as i32 isize as i32 u32 as u32 usize as u32 - i64 as i64 - u64 as u64 f32 as f32 - f64 as f64 ); macro_rules! type_abi_as_u32 { diff --git a/src/describe.rs b/src/describe.rs index f7e9e83d2cf..ac78848d222 100644 --- a/src/describe.rs +++ b/src/describe.rs @@ -30,6 +30,8 @@ tys! { U32 I64 U64 + I128 + U128 F32 F64 BOOLEAN @@ -89,6 +91,8 @@ simple! { u32 => U32 i64 => I64 u64 => U64 + i128 => I128 + u128 => U128 isize => I32 usize => U32 f32 => F32 diff --git a/tests/wasm/optional_primitives.js b/tests/wasm/optional_primitives.js index 89bf4df7eab..93a2f5fafdc 100644 --- a/tests/wasm/optional_primitives.js +++ b/tests/wasm/optional_primitives.js @@ -13,6 +13,8 @@ exports.optional_i16_js_identity = a => a; exports.optional_u16_js_identity = a => a; exports.optional_i64_js_identity = a => a; exports.optional_u64_js_identity = a => a; +exports.optional_i128_js_identity = a => a; +exports.optional_u128_js_identity = a => a; exports.optional_bool_js_identity = a => a; exports.optional_char_js_identity = a => a; @@ -92,6 +94,22 @@ exports.js_works = () => { assert.strictEqual(wasm.optional_u64_identity(wasm.optional_u64_min()), BigInt('0')); assert.strictEqual(wasm.optional_u64_identity(wasm.optional_u64_max()), BigInt('18446744073709551615')); + assert.strictEqual(wasm.optional_i128_identity(wasm.optional_i128_none()), undefined); + assert.strictEqual(wasm.optional_i128_identity(wasm.optional_i128_zero()), BigInt('0')); + assert.strictEqual(wasm.optional_i128_identity(wasm.optional_i128_one()), BigInt('1')); + assert.strictEqual(wasm.optional_i128_identity(wasm.optional_i128_neg_one()), BigInt('-1')); + assert.strictEqual(wasm.optional_i128_identity(wasm.optional_i128_min()), -0x8000_0000_0000_0000_0000_0000_0000_0000n); + assert.strictEqual(wasm.optional_i128_identity(wasm.optional_i128_max()), 0x7FFF_FFFF_FFFF_FFFF_FFFF_FFFF_FFFF_FFFFn); + assert.strictEqual(wasm.optional_i128_identity(0x3_1415_9265_3598_7932_3846n), 0x3_1415_9265_3598_7932_3846n); + assert.strictEqual(wasm.optional_i128_identity(-0x3_1415_9265_3598_7932_3846n), -0x3_1415_9265_3598_7932_3846n); + + assert.strictEqual(wasm.optional_u128_identity(wasm.optional_u128_none()), undefined); + assert.strictEqual(wasm.optional_u128_identity(wasm.optional_u128_zero()), BigInt('0')); + assert.strictEqual(wasm.optional_u128_identity(wasm.optional_u128_one()), BigInt('1')); + assert.strictEqual(wasm.optional_u128_identity(wasm.optional_u128_min()), BigInt('0')); + assert.strictEqual(wasm.optional_u128_identity(wasm.optional_u128_max()), 0xFFFF_FFFF_FFFF_FFFF_FFFF_FFFF_FFFF_FFFFn); + assert.strictEqual(wasm.optional_u128_identity(0x3_1415_9265_3598_7932_3846n), 0x3_1415_9265_3598_7932_3846n); + assert.strictEqual(wasm.optional_bool_identity(wasm.optional_bool_none()), undefined); assert.strictEqual(wasm.optional_bool_identity(wasm.optional_bool_false()), false); assert.strictEqual(wasm.optional_bool_identity(wasm.optional_bool_true()), true); diff --git a/tests/wasm/optional_primitives.rs b/tests/wasm/optional_primitives.rs index 027bc18c99f..c3cd1e9fc95 100644 --- a/tests/wasm/optional_primitives.rs +++ b/tests/wasm/optional_primitives.rs @@ -15,6 +15,8 @@ extern "C" { fn optional_u16_js_identity(a: Option) -> Option; fn optional_i64_js_identity(a: Option) -> Option; fn optional_u64_js_identity(a: Option) -> Option; + fn optional_i128_js_identity(a: Option) -> Option; + fn optional_u128_js_identity(a: Option) -> Option; fn optional_bool_js_identity(a: Option) -> Option; fn optional_char_js_identity(a: Option) -> Option; @@ -396,6 +398,71 @@ pub fn optional_u64_identity(a: Option) -> Option { optional_u64_js_identity(a) } +#[wasm_bindgen] +pub fn optional_i128_none() -> Option { + None +} + +#[wasm_bindgen] +pub fn optional_i128_zero() -> Option { + Some(0) +} + +#[wasm_bindgen] +pub fn optional_i128_one() -> Option { + Some(1) +} + +#[wasm_bindgen] +pub fn optional_i128_neg_one() -> Option { + Some(-1) +} + +#[wasm_bindgen] +pub fn optional_i128_min() -> Option { + Some(i128::MIN) +} + +#[wasm_bindgen] +pub fn optional_i128_max() -> Option { + Some(i128::MAX) +} + +#[wasm_bindgen] +pub fn optional_i128_identity(a: Option) -> Option { + optional_i128_js_identity(a) +} + +#[wasm_bindgen] +pub fn optional_u128_none() -> Option { + None +} + +#[wasm_bindgen] +pub fn optional_u128_zero() -> Option { + Some(0) +} + +#[wasm_bindgen] +pub fn optional_u128_one() -> Option { + Some(1) +} + +#[wasm_bindgen] +pub fn optional_u128_min() -> Option { + Some(u128::MIN) +} + +#[wasm_bindgen] +pub fn optional_u128_max() -> Option { + Some(u128::MAX) +} + +#[wasm_bindgen] +pub fn optional_u128_identity(a: Option) -> Option { + optional_u128_js_identity(a) +} + #[wasm_bindgen] pub fn optional_bool_none() -> Option { None