Add NonNull<T> as parameter (#3857)

This commit is contained in:
daxpedda
2024-03-02 09:11:13 +01:00
committed by GitHub
parent 0095fa73c9
commit 983ec579a3
9 changed files with 50 additions and 4 deletions

View File

@@ -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)
* Allow overriding the URL used for headless tests by setting `WASM_BINDGEN_TEST_ADDRESS`.
[#3861](https://github.com/rustwasm/wasm-bindgen/pull/3861)

View File

@@ -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();

View File

@@ -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(());

View File

@@ -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(())
}

View File

@@ -311,6 +311,7 @@ pub enum Instruction {
},
I32FromOptionNonNull,
OptionNonNullFromI32,
I32FromNonNull,
}
impl AdapterType {

View File

@@ -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

View File

@@ -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.
NonNull::new_unchecked(js as *mut T)
}
}
impl<T> OptionFromWasmAbi for NonNull<T> {
#[inline]
fn is_none(js: &u32) -> bool {
*js == 0
}
}

View File

@@ -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);

View File

@@ -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()))