Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add NonNull<T> as parameter #3857

Merged
merged 3 commits into from
Mar 2, 2024
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@

* Add support for `Option<*const T>`, `Option<*mut T>` and `NonNull<T>`.
[#3852](https://github.com/rustwasm/wasm-bindgen/pull/3852)
[#3857](https://github.com/rustwasm/wasm-bindgen/pull/3857)

### Fixed

Expand Down
14 changes: 14 additions & 0 deletions crates/cli-support/src/js/binding.rs
Original file line number Diff line number Diff line change
Expand Up @@ -497,6 +497,14 @@ impl<'a, 'b> JsBuilder<'a, 'b> {
self.prelude("}");
}

fn assert_non_null(&mut self, arg: &str) {
if !self.cx.config.debug {
return;
}
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;
Expand Down Expand Up @@ -1218,6 +1226,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();
Expand Down
13 changes: 13 additions & 0 deletions crates/cli-support/src/js/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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(());
Expand Down
6 changes: 5 additions & 1 deletion crates/cli-support/src/wit/incoming.rs
Original file line number Diff line number Diff line change
Expand Up @@ -156,7 +156,11 @@ impl InstructionBuilder<'_, '_> {
// Largely synthetic and can't show up
Descriptor::ClampedU8 => unreachable!(),

Descriptor::NonNull => unimplemented!("converting `NonNull<T>` from Wasm to Rust is not implemented"),
Descriptor::NonNull => self.instruction(
&[AdapterType::NonNull],
Instruction::I32FromNonNull,
&[AdapterType::I32],
),
}
Ok(())
}
Expand Down
1 change: 1 addition & 0 deletions crates/cli-support/src/wit/standard.rs
Original file line number Diff line number Diff line change
Expand Up @@ -311,6 +311,7 @@ pub enum Instruction {
},
I32FromOptionNonNull,
OptionNonNullFromI32,
I32FromNonNull,
}

impl AdapterType {
Expand Down
2 changes: 1 addition & 1 deletion guide/src/reference/types/non-null.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

| `T` parameter | `&T` parameter | `&mut T` parameter | `T` return value | `Option<T>` parameter | `Option<T>` 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

Expand Down
12 changes: 10 additions & 2 deletions src/convert/impls.rs
Original file line number Diff line number Diff line change
Expand Up @@ -294,12 +294,20 @@ impl<T> OptionIntoWasmAbi for NonNull<T> {
}
}

impl<T> FromWasmAbi for Option<NonNull<T>> {
impl<T> FromWasmAbi for NonNull<T> {
type Abi = u32;

#[inline]
unsafe fn from_abi(js: Self::Abi) -> Self {
NonNull::new(js as *mut T)
// SAFETY: Checked in bindings.
daxpedda marked this conversation as resolved.
Show resolved Hide resolved
NonNull::new(js as *mut T).unwrap_unchecked()
}
}

impl<T> OptionFromWasmAbi for NonNull<T> {
#[inline]
fn is_none(js: &u32) -> bool {
*js == 0
}
}

Expand Down
7 changes: 7 additions & 0 deletions tests/wasm/simple.js
Original file line number Diff line number Diff line change
Expand Up @@ -139,6 +139,13 @@ exports.test_raw_pointers = function() {
};

exports.test_non_null = function() {
assert.strictEqual(wasm.simple_nonnull_work(wasm.simple_return_non_null()), 42);

// this test only works when `--debug` is passed to `wasm-bindgen` (or the
// equivalent thereof)
if (!require('process').env.WASM_BINDGEN_NO_DEBUG)
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);
Expand Down
5 changes: 5 additions & 0 deletions tests/wasm/simple.rs
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,11 @@ pub fn simple_return_option_non_null(value: u32) -> Option<NonNull<u32>> {
Some(NonNull::from(Box::leak(Box::new(value))))
}

#[wasm_bindgen]
pub unsafe fn simple_nonnull_work(a: NonNull<u32>) -> u32 {
*Box::from_raw(a.as_ptr())
}

#[wasm_bindgen]
pub unsafe fn simple_option_nonnull_work(a: Option<NonNull<u32>>) -> Option<u32> {
a.map(|ptr| *Box::from_raw(ptr.as_ptr()))
Expand Down