From 983ec579a3dfca7ccb4f63f1f1adaa48f8bb3bec Mon Sep 17 00:00:00 2001 From: daxpedda Date: Sat, 2 Mar 2024 09:11:13 +0100 Subject: [PATCH] Add `NonNull` as parameter (#3857) --- CHANGELOG.md | 1 + crates/cli-support/src/js/binding.rs | 11 +++++++++++ crates/cli-support/src/js/mod.rs | 13 +++++++++++++ crates/cli-support/src/wit/incoming.rs | 6 +++++- crates/cli-support/src/wit/standard.rs | 1 + guide/src/reference/types/non-null.md | 2 +- src/convert/impls.rs | 12 ++++++++++-- tests/wasm/simple.js | 3 +++ tests/wasm/simple.rs | 5 +++++ 9 files changed, 50 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index ec4b9281313..b4c87dd8df4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -16,6 +16,7 @@ * Add support for `Option<*const T>`, `Option<*mut T>` and `NonNull`. [#3852](https://github.com/rustwasm/wasm-bindgen/pull/3852) + [#3857](https://github.com/rustwasm/wasm-bindgen/pull/3857) * Allow overriding the URL used for headless tests by setting `WASM_BINDGEN_TEST_ADDRESS`. [#3861](https://github.com/rustwasm/wasm-bindgen/pull/3861) diff --git a/crates/cli-support/src/js/binding.rs b/crates/cli-support/src/js/binding.rs index f186038bde9..4c27d7584e4 100644 --- a/crates/cli-support/src/js/binding.rs +++ b/crates/cli-support/src/js/binding.rs @@ -497,6 +497,11 @@ impl<'a, 'b> JsBuilder<'a, 'b> { self.prelude("}"); } + fn assert_non_null(&mut self, arg: &str) { + self.cx.expose_assert_non_null(); + self.prelude(&format!("_assertNonNull({});", arg)); + } + fn assert_optional_bigint(&mut self, arg: &str) { if !self.cx.config.debug { return; @@ -1218,6 +1223,12 @@ fn instruction( js.push(format!("{0} === {1} ? undefined : {0}", val, hole)); } + Instruction::I32FromNonNull => { + let val = js.pop(); + js.assert_non_null(&val); + js.push(val); + } + Instruction::I32FromOptionNonNull => { let val = js.pop(); js.cx.expose_is_like_none(); diff --git a/crates/cli-support/src/js/mod.rs b/crates/cli-support/src/js/mod.rs index 2e7744874b4..9a3899c9266 100644 --- a/crates/cli-support/src/js/mod.rs +++ b/crates/cli-support/src/js/mod.rs @@ -2103,6 +2103,19 @@ impl<'a> Context<'a> { ); } + fn expose_assert_non_null(&mut self) { + if !self.should_write_global("assert_non_null") { + return; + } + self.global( + " + function _assertNonNull(n) { + if (typeof(n) !== 'number' || n === 0) throw new Error(`expected a number argument that is not 0, found ${n}`); + } + ", + ); + } + fn expose_make_mut_closure(&mut self) -> Result<(), Error> { if !self.should_write_global("make_mut_closure") { return Ok(()); diff --git a/crates/cli-support/src/wit/incoming.rs b/crates/cli-support/src/wit/incoming.rs index 084e939e624..83bd3b32b16 100644 --- a/crates/cli-support/src/wit/incoming.rs +++ b/crates/cli-support/src/wit/incoming.rs @@ -156,7 +156,11 @@ impl InstructionBuilder<'_, '_> { // Largely synthetic and can't show up Descriptor::ClampedU8 => unreachable!(), - Descriptor::NonNull => unimplemented!("converting `NonNull` from Wasm to Rust is not implemented"), + Descriptor::NonNull => self.instruction( + &[AdapterType::NonNull], + Instruction::I32FromNonNull, + &[AdapterType::I32], + ), } Ok(()) } diff --git a/crates/cli-support/src/wit/standard.rs b/crates/cli-support/src/wit/standard.rs index d9a86b81a9d..104a297abfa 100644 --- a/crates/cli-support/src/wit/standard.rs +++ b/crates/cli-support/src/wit/standard.rs @@ -311,6 +311,7 @@ pub enum Instruction { }, I32FromOptionNonNull, OptionNonNullFromI32, + I32FromNonNull, } impl AdapterType { diff --git a/guide/src/reference/types/non-null.md b/guide/src/reference/types/non-null.md index 8e1b9d17f25..33c2e9b0f16 100644 --- a/guide/src/reference/types/non-null.md +++ b/guide/src/reference/types/non-null.md @@ -2,7 +2,7 @@ | `T` parameter | `&T` parameter | `&mut T` parameter | `T` return value | `Option` parameter | `Option` return value | JavaScript representation | |:---:|:---:|:---:|:---:|:---:|:---:|:---:| -| No | No | No | Yes | Yes | Yes | A JavaScript number value | +| Yes | No | No | Yes | Yes | Yes | A JavaScript number value | ## Example Rust Usage diff --git a/src/convert/impls.rs b/src/convert/impls.rs index 64a40925c84..8d2bf4baf6a 100644 --- a/src/convert/impls.rs +++ b/src/convert/impls.rs @@ -294,12 +294,20 @@ impl OptionIntoWasmAbi for NonNull { } } -impl FromWasmAbi for Option> { +impl FromWasmAbi for NonNull { type Abi = u32; #[inline] unsafe fn from_abi(js: Self::Abi) -> Self { - NonNull::new(js as *mut T) + // SAFETY: Checked in bindings. + NonNull::new_unchecked(js as *mut T) + } +} + +impl OptionFromWasmAbi for NonNull { + #[inline] + fn is_none(js: &u32) -> bool { + *js == 0 } } diff --git a/tests/wasm/simple.js b/tests/wasm/simple.js index bcac3262f49..f66c77479f7 100644 --- a/tests/wasm/simple.js +++ b/tests/wasm/simple.js @@ -139,6 +139,9 @@ exports.test_raw_pointers = function() { }; exports.test_non_null = function() { + assert.strictEqual(wasm.simple_nonnull_work(wasm.simple_return_non_null()), 42); + assert.throws(() => wasm.simple_nonnull_work(0), /expected a number argument that is not 0/); + assert.strictEqual(wasm.simple_option_nonnull_work(0), undefined); assert.strictEqual(wasm.simple_option_nonnull_work(null), undefined); assert.strictEqual(wasm.simple_option_nonnull_work(undefined), undefined); diff --git a/tests/wasm/simple.rs b/tests/wasm/simple.rs index b6bab8c3874..fdef34c68fd 100644 --- a/tests/wasm/simple.rs +++ b/tests/wasm/simple.rs @@ -113,6 +113,11 @@ pub fn simple_return_option_non_null(value: u32) -> Option> { Some(NonNull::from(Box::leak(Box::new(value)))) } +#[wasm_bindgen] +pub unsafe fn simple_nonnull_work(a: NonNull) -> u32 { + *Box::from_raw(a.as_ptr()) +} + #[wasm_bindgen] pub unsafe fn simple_option_nonnull_work(a: Option>) -> Option { a.map(|ptr| *Box::from_raw(ptr.as_ptr()))