From f6ea68fb613914b268d7f438c4fa5a34968b0323 Mon Sep 17 00:00:00 2001 From: daxpedda Date: Fri, 23 Feb 2024 13:40:01 +0100 Subject: [PATCH 1/6] Add `Option<*const|mut T>` and `NonNull` --- CHANGELOG.md | 3 ++ .../non_null.js | 12 +++++ .../src/non_null.rs | 13 +++++ guide/src/SUMMARY.md | 3 +- guide/src/reference/types/non-null.md | 17 ++++++ guide/src/reference/types/pointers.md | 2 +- src/convert/impls.rs | 54 +++++++++++++++++++ src/describe.rs | 8 +++ tests/wasm/simple.rs | 16 ++++++ 9 files changed, 126 insertions(+), 2 deletions(-) create mode 100644 examples/guide-supported-types-examples/non_null.js create mode 100644 examples/guide-supported-types-examples/src/non_null.rs create mode 100644 guide/src/reference/types/non-null.md diff --git a/CHANGELOG.md b/CHANGELOG.md index 16e9e1d3963..7ebdefce452 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -14,6 +14,9 @@ * Add `TryFrom` implementations for `Number`, that allow losslessly converting from 64- and 128-bits numbers. [#3847](https://github.com/rustwasm/wasm-bindgen/pull/3847) +* Add support for `Option<*const T>`, `Option<*mut T>` and `NonNull`. + [#3852](https://github.com/rustwasm/wasm-bindgen/pull/3852) + ### Fixed * Make .wasm output deterministic when using `--reference-types`. diff --git a/examples/guide-supported-types-examples/non_null.js b/examples/guide-supported-types-examples/non_null.js new file mode 100644 index 00000000000..fbbbf42b9e6 --- /dev/null +++ b/examples/guide-supported-types-examples/non_null.js @@ -0,0 +1,12 @@ +import { + take_pointer_by_value, + return_pointer, + } from './guide_supported_types_examples'; + import { memory } from './guide_supported_types_examples_bg'; + + let ptr = return_pointer(); + let buf = new Uint8Array(memory.buffer); + let value = buf[ptr]; + console.log(`The byte at the ${ptr} address is ${value}`); + + take_pointer_by_value(ptr); diff --git a/examples/guide-supported-types-examples/src/non_null.rs b/examples/guide-supported-types-examples/src/non_null.rs new file mode 100644 index 00000000000..81c3efda6dd --- /dev/null +++ b/examples/guide-supported-types-examples/src/non_null.rs @@ -0,0 +1,13 @@ +use std::ptr; +use std::ptr::NonNull; +use wasm_bindgen::prelude::*; + +#[wasm_bindgen] +pub unsafe fn take_pointer_by_value(x: Option>) { + Box::from_raw(x.unwrap().as_ptr()); +} + +#[wasm_bindgen] +pub fn return_pointer() -> Option> { + NonNull::from(Box::new(42).leak()) +} diff --git a/guide/src/SUMMARY.md b/guide/src/SUMMARY.md index 5d9c362902c..970b95d255e 100644 --- a/guide/src/SUMMARY.md +++ b/guide/src/SUMMARY.md @@ -53,7 +53,8 @@ - [Exported Rust Types](./reference/types/exported-rust-types.md) - [`JsValue`](./reference/types/jsvalue.md) - [`Box<[T]>` and `Vec`](./reference/types/boxed-slices.md) - - [`*const T` and `*mut T`](./reference/types/pointers.md) + - [`*const T`, `*mut T`](./reference/types/pointers.md) + - [`NonNull`](./reference/types/non-null.md) - [Numbers](./reference/types/numbers.md) - [`bool`](./reference/types/bool.md) - [`char`](./reference/types/char.md) diff --git a/guide/src/reference/types/non-null.md b/guide/src/reference/types/non-null.md new file mode 100644 index 00000000000..7926265f41e --- /dev/null +++ b/guide/src/reference/types/non-null.md @@ -0,0 +1,17 @@ +# `*const T` and `*mut T` + +| `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 | + +## Example Rust Usage + +```rust +{{#include ../../../../examples/guide-supported-types-examples/src/non_null.rs}} +``` + +## Example JavaScript Usage + +```js +{{#include ../../../../examples/guide-supported-types-examples/non_null.js}} +``` diff --git a/guide/src/reference/types/pointers.md b/guide/src/reference/types/pointers.md index 15ca4eb5c99..98c72194e3d 100644 --- a/guide/src/reference/types/pointers.md +++ b/guide/src/reference/types/pointers.md @@ -2,7 +2,7 @@ | `T` parameter | `&T` parameter | `&mut T` parameter | `T` return value | `Option` parameter | `Option` return value | JavaScript representation | |:---:|:---:|:---:|:---:|:---:|:---:|:---:| -| Yes | No | No | Yes | No | No | 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 8f1e3798055..34b5e9704e9 100644 --- a/src/convert/impls.rs +++ b/src/convert/impls.rs @@ -1,5 +1,6 @@ use core::char; use core::mem::{self, ManuallyDrop}; +use core::ptr::NonNull; use crate::convert::traits::{WasmAbi, WasmPrimitive}; use crate::convert::TryFromJsValue; @@ -223,6 +224,20 @@ impl FromWasmAbi for *const T { } } +impl OptionIntoWasmAbi for *const T { + #[inline] + fn none() -> u32 { + 0 + } +} + +impl OptionFromWasmAbi for *const T { + #[inline] + fn is_none(js: &u32) -> bool { + *js == 0 + } +} + impl IntoWasmAbi for *mut T { type Abi = u32; @@ -241,6 +256,45 @@ impl FromWasmAbi for *mut T { } } +impl OptionIntoWasmAbi for *mut T { + #[inline] + fn none() -> u32 { + 0 + } +} + +impl OptionFromWasmAbi for *mut T { + #[inline] + fn is_none(js: &u32) -> bool { + *js == 0 + } +} + +impl IntoWasmAbi for NonNull { + type Abi = u32; + + #[inline] + fn into_abi(self) -> u32 { + self.as_ptr() as u32 + } +} + +impl OptionIntoWasmAbi for NonNull { + #[inline] + fn none() -> u32 { + 0 + } +} + +impl FromWasmAbi for Option> { + type Abi = u32; + + #[inline] + unsafe fn from_abi(js: Self::Abi) -> Self { + NonNull::new(js as *mut T) + } +} + impl IntoWasmAbi for JsValue { type Abi = u32; diff --git a/src/describe.rs b/src/describe.rs index d3b5fd0b546..f7e282dcb79 100644 --- a/src/describe.rs +++ b/src/describe.rs @@ -3,6 +3,8 @@ #![doc(hidden)] +use core::ptr::NonNull; + use crate::{Clamped, JsError, JsObject, JsValue}; use cfg_if::cfg_if; @@ -114,6 +116,12 @@ impl WasmDescribe for *mut T { } } +impl WasmDescribe for NonNull { + fn describe() { + inform(U32) + } +} + impl WasmDescribe for [T] { fn describe() { inform(SLICE); diff --git a/tests/wasm/simple.rs b/tests/wasm/simple.rs index 94cad579c56..d016c91653b 100644 --- a/tests/wasm/simple.rs +++ b/tests/wasm/simple.rs @@ -1,3 +1,5 @@ +use std::ptr::NonNull; + use wasm_bindgen::prelude::*; use wasm_bindgen::{intern, unintern}; use wasm_bindgen_test::*; @@ -62,6 +64,20 @@ pub unsafe fn simple_raw_pointers_work(a: *mut u32, b: *const u8) -> *const u32 a } +#[wasm_bindgen] +pub unsafe fn simple_option_raw_pointers_work( + a: Option<*mut u32>, + b: Option<*const u8>, +) -> Option<*const u32> { + *a.unwrap() = *b.unwrap() as u32; + a.map(|ptr| ptr as *const _) +} + +#[wasm_bindgen] +pub unsafe fn simple_option_nonnull_work(a: Option>) -> Option> { + a +} + #[wasm_bindgen_test] fn string_arguments() { test_string_arguments(); From b2ca0e6793157c76a4b6429a8ea27e11733dd68d Mon Sep 17 00:00:00 2001 From: daxpedda Date: Sat, 24 Feb 2024 09:11:55 +0100 Subject: [PATCH 2/6] Address review Co-Authored-By: Liam Murphy <43807659+Liamolucko@users.noreply.github.com> --- .../src/non_null.rs | 2 +- guide/src/reference/types/non-null.md | 2 +- src/convert/impls.rs | 32 ++++++++++++------- tests/wasm/simple.rs | 2 +- 4 files changed, 23 insertions(+), 15 deletions(-) diff --git a/examples/guide-supported-types-examples/src/non_null.rs b/examples/guide-supported-types-examples/src/non_null.rs index 81c3efda6dd..0f524e0eda2 100644 --- a/examples/guide-supported-types-examples/src/non_null.rs +++ b/examples/guide-supported-types-examples/src/non_null.rs @@ -9,5 +9,5 @@ pub unsafe fn take_pointer_by_value(x: Option>) { #[wasm_bindgen] pub fn return_pointer() -> Option> { - NonNull::from(Box::new(42).leak()) + Some(NonNull::from(Box::leak(Box::new(42)))) } diff --git a/guide/src/reference/types/non-null.md b/guide/src/reference/types/non-null.md index 7926265f41e..8e1b9d17f25 100644 --- a/guide/src/reference/types/non-null.md +++ b/guide/src/reference/types/non-null.md @@ -1,4 +1,4 @@ -# `*const T` and `*mut T` +# `NonNull` | `T` parameter | `&T` parameter | `&mut T` parameter | `T` return value | `Option` parameter | `Option` return value | JavaScript representation | |:---:|:---:|:---:|:---:|:---:|:---:|:---:| diff --git a/src/convert/impls.rs b/src/convert/impls.rs index 34b5e9704e9..64a40925c84 100644 --- a/src/convert/impls.rs +++ b/src/convert/impls.rs @@ -224,17 +224,21 @@ impl FromWasmAbi for *const T { } } -impl OptionIntoWasmAbi for *const T { +impl IntoWasmAbi for Option<*const T> { + type Abi = Option; + #[inline] - fn none() -> u32 { - 0 + fn into_abi(self) -> Option { + self.map(|ptr| ptr as u32) } } -impl OptionFromWasmAbi for *const T { +impl FromWasmAbi for Option<*const T> { + type Abi = Option; + #[inline] - fn is_none(js: &u32) -> bool { - *js == 0 + unsafe fn from_abi(js: Option) -> Option<*const T> { + js.map(|ptr| ptr as *const T) } } @@ -256,17 +260,21 @@ impl FromWasmAbi for *mut T { } } -impl OptionIntoWasmAbi for *mut T { +impl IntoWasmAbi for Option<*mut T> { + type Abi = Option; + #[inline] - fn none() -> u32 { - 0 + fn into_abi(self) -> Option { + self.map(|ptr| ptr as u32) } } -impl OptionFromWasmAbi for *mut T { +impl FromWasmAbi for Option<*mut T> { + type Abi = Option; + #[inline] - fn is_none(js: &u32) -> bool { - *js == 0 + unsafe fn from_abi(js: Option) -> Option<*mut T> { + js.map(|ptr| ptr as *mut T) } } diff --git a/tests/wasm/simple.rs b/tests/wasm/simple.rs index d016c91653b..efde14f8698 100644 --- a/tests/wasm/simple.rs +++ b/tests/wasm/simple.rs @@ -74,7 +74,7 @@ pub unsafe fn simple_option_raw_pointers_work( } #[wasm_bindgen] -pub unsafe fn simple_option_nonnull_work(a: Option>) -> Option> { +pub fn simple_option_nonnull_work(a: Option>) -> Option> { a } From f304807fc3e9fca0dbb8ea5341e47dd0f3bc43d5 Mon Sep 17 00:00:00 2001 From: daxpedda Date: Sat, 24 Feb 2024 10:15:56 +0100 Subject: [PATCH 3/6] Implement `NonNull` descriptor --- crates/cli-support/src/descriptor.rs | 3 +++ crates/cli-support/src/js/binding.rs | 15 ++++++++++++++- crates/cli-support/src/wit/incoming.rs | 8 ++++++++ crates/cli-support/src/wit/outgoing.rs | 11 ++++++++++- crates/cli-support/src/wit/standard.rs | 3 +++ crates/shared/src/lib.rs | 2 +- crates/shared/src/schema_hash_approval.rs | 2 +- src/describe.rs | 3 ++- tests/wasm/simple.js | 9 +++++++++ tests/wasm/simple.rs | 21 +++++++++++++++++++-- 10 files changed, 70 insertions(+), 7 deletions(-) diff --git a/crates/cli-support/src/descriptor.rs b/crates/cli-support/src/descriptor.rs index e21dae1ed29..62dd766ac7a 100644 --- a/crates/cli-support/src/descriptor.rs +++ b/crates/cli-support/src/descriptor.rs @@ -40,6 +40,7 @@ tys! { RESULT UNIT CLAMPED + NONNULL } #[derive(Debug, Clone, PartialEq, Eq, Hash)] @@ -72,6 +73,7 @@ pub enum Descriptor { Option(Box), Result(Box), Unit, + NonNull, } #[derive(Debug, Clone, PartialEq, Eq, Hash)] @@ -165,6 +167,7 @@ impl Descriptor { CHAR => Descriptor::Char, UNIT => Descriptor::Unit, CLAMPED => Descriptor::_decode(data, true), + NONNULL => Descriptor::NonNull, other => panic!("unknown descriptor: {}", other), } } diff --git a/crates/cli-support/src/js/binding.rs b/crates/cli-support/src/js/binding.rs index 76b61b42f86..a7dfae6249b 100644 --- a/crates/cli-support/src/js/binding.rs +++ b/crates/cli-support/src/js/binding.rs @@ -1217,6 +1217,18 @@ fn instruction( let val = js.pop(); js.push(format!("{0} === {1} ? undefined : {0}", val, hole)); } + + Instruction::InNonNullSentinel => { + let val = js.pop(); + js.cx.expose_is_like_none(); + js.assert_optional_number(&val); + js.push(format!("isLikeNone({0}) ? 0 : {0}", val)); + } + + Instruction::OutNonNullSentinel => { + let val = js.pop(); + js.push(format!("{0} === 0 ? undefined : {0}", val)); + } } Ok(()) } @@ -1324,7 +1336,8 @@ fn adapter2ts(ty: &AdapterType, dst: &mut String) { | AdapterType::U16 | AdapterType::U32 | AdapterType::F32 - | AdapterType::F64 => dst.push_str("number"), + | AdapterType::F64 + | AdapterType::NonNull => dst.push_str("number"), AdapterType::I64 | AdapterType::S64 | AdapterType::U64 => dst.push_str("bigint"), AdapterType::String => dst.push_str("string"), AdapterType::Externref => dst.push_str("any"), diff --git a/crates/cli-support/src/wit/incoming.rs b/crates/cli-support/src/wit/incoming.rs index b42541791eb..6f89e85ef41 100644 --- a/crates/cli-support/src/wit/incoming.rs +++ b/crates/cli-support/src/wit/incoming.rs @@ -155,6 +155,8 @@ impl InstructionBuilder<'_, '_> { // Largely synthetic and can't show up Descriptor::ClampedU8 => unreachable!(), + + Descriptor::NonNull => self.number(AdapterType::NonNull, WasmVT::I32), } Ok(()) } @@ -331,6 +333,12 @@ impl InstructionBuilder<'_, '_> { ); } + Descriptor::NonNull => self.instruction( + &[AdapterType::NonNull.option()], + Instruction::InNonNullSentinel, + &[AdapterType::I32], + ), + _ => bail!( "unsupported optional argument type for calling Rust function from JS: {:?}", arg diff --git a/crates/cli-support/src/wit/outgoing.rs b/crates/cli-support/src/wit/outgoing.rs index 41763bdbe00..0c5ee3aedeb 100644 --- a/crates/cli-support/src/wit/outgoing.rs +++ b/crates/cli-support/src/wit/outgoing.rs @@ -156,6 +156,8 @@ impl InstructionBuilder<'_, '_> { // Largely synthetic and can't show up Descriptor::ClampedU8 => unreachable!(), + + Descriptor::NonNull => self.outgoing_i32(AdapterType::NonNull), } Ok(()) } @@ -319,6 +321,12 @@ impl InstructionBuilder<'_, '_> { ); } + Descriptor::NonNull => self.instruction( + &[AdapterType::I32], + Instruction::OutNonNullSentinel, + &[AdapterType::NonNull.option()], + ), + _ => bail!( "unsupported optional argument type for calling JS function from Rust: {:?}", arg @@ -350,7 +358,8 @@ impl InstructionBuilder<'_, '_> { | Descriptor::CachedString | Descriptor::Option(_) | Descriptor::Vector(_) - | Descriptor::Unit => { + | Descriptor::Unit + | Descriptor::NonNull => { // We must throw before reading the Ok type, if there is an error. However, the // structure of ResultAbi is that the Err value + discriminant come last (for // alignment reasons). So the UnwrapResult instruction must come first, but the diff --git a/crates/cli-support/src/wit/standard.rs b/crates/cli-support/src/wit/standard.rs index 95781a6131e..ea892680922 100644 --- a/crates/cli-support/src/wit/standard.rs +++ b/crates/cli-support/src/wit/standard.rs @@ -88,6 +88,7 @@ pub enum AdapterType { Enum(String), NamedExternref(String), Function, + NonNull, } #[derive(Debug, Clone)] @@ -308,6 +309,8 @@ pub enum Instruction { OptionEnumFromI32 { hole: u32, }, + InNonNullSentinel, + OutNonNullSentinel, } impl AdapterType { diff --git a/crates/shared/src/lib.rs b/crates/shared/src/lib.rs index 991dc0c61c3..f8ad45c7cd3 100644 --- a/crates/shared/src/lib.rs +++ b/crates/shared/src/lib.rs @@ -6,7 +6,7 @@ mod schema_hash_approval; // This gets changed whenever our schema changes. // At this time versions of wasm-bindgen and wasm-bindgen-cli are required to have the exact same // SCHEMA_VERSION in order to work together. -pub const SCHEMA_VERSION: &str = "0.2.88"; +pub const SCHEMA_VERSION: &str = "0.2.92"; #[macro_export] macro_rules! shared_api { diff --git a/crates/shared/src/schema_hash_approval.rs b/crates/shared/src/schema_hash_approval.rs index 702277b74d0..471ccc9beac 100644 --- a/crates/shared/src/schema_hash_approval.rs +++ b/crates/shared/src/schema_hash_approval.rs @@ -8,7 +8,7 @@ // If the schema in this library has changed then: // 1. Bump the version in `crates/shared/Cargo.toml` // 2. Change the `SCHEMA_VERSION` in this library to this new Cargo.toml version -const APPROVED_SCHEMA_FILE_HASH: &str = "2548486983363536439"; +const APPROVED_SCHEMA_FILE_HASH: &str = "11955579329744078753"; #[test] fn schema_version() { diff --git a/src/describe.rs b/src/describe.rs index f7e282dcb79..23190f71987 100644 --- a/src/describe.rs +++ b/src/describe.rs @@ -48,6 +48,7 @@ tys! { RESULT UNIT CLAMPED + NONNULL } #[inline(always)] // see the wasm-interpreter crate @@ -118,7 +119,7 @@ impl WasmDescribe for *mut T { impl WasmDescribe for NonNull { fn describe() { - inform(U32) + inform(NONNULL) } } diff --git a/tests/wasm/simple.js b/tests/wasm/simple.js index bc3edce8c0a..dfcb19da4ad 100644 --- a/tests/wasm/simple.js +++ b/tests/wasm/simple.js @@ -110,3 +110,12 @@ exports.test_string_roundtrip = () => { test('a longer string'); test('a longer 💖 string'); }; + +exports.test_non_null = function() { + 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); + + assert.strictEqual(wasm.simple_option_nonnull_work(wasm.simple_return_non_null()), 42); + assert.strictEqual(wasm.simple_option_nonnull_work(wasm.simple_return_option_non_null(43)), 43); +}; diff --git a/tests/wasm/simple.rs b/tests/wasm/simple.rs index efde14f8698..d81c1067635 100644 --- a/tests/wasm/simple.rs +++ b/tests/wasm/simple.rs @@ -31,6 +31,8 @@ extern "C" { fn new_renamed() -> Renamed; fn test_string_roundtrip(); + + fn test_non_null(); } #[wasm_bindgen_test] @@ -74,8 +76,23 @@ pub unsafe fn simple_option_raw_pointers_work( } #[wasm_bindgen] -pub fn simple_option_nonnull_work(a: Option>) -> Option> { - a +pub fn simple_return_non_null() -> NonNull { + NonNull::from(Box::leak(Box::new(42))) +} + +#[wasm_bindgen] +pub fn simple_return_option_non_null(value: u32) -> Option> { + Some(NonNull::from(Box::leak(Box::new(value)))) +} + +#[wasm_bindgen] +pub unsafe fn simple_option_nonnull_work(a: Option>) -> Option { + a.map(|ptr| *Box::from_raw(ptr.as_ptr())) +} + +#[wasm_bindgen_test] +fn non_null() { + test_non_null(); } #[wasm_bindgen_test] From 8f12035e64d19b142dc7a2f872e650a289489924 Mon Sep 17 00:00:00 2001 From: daxpedda Date: Sun, 25 Feb 2024 12:18:01 +0100 Subject: [PATCH 4/6] Address review Co-Authored-By: Liam Murphy <43807659+Liamolucko@users.noreply.github.com> --- crates/cli-support/src/js/binding.rs | 6 ++--- crates/cli-support/src/wit/incoming.rs | 4 +-- crates/cli-support/src/wit/outgoing.rs | 2 +- crates/cli-support/src/wit/standard.rs | 4 +-- tests/wasm/simple.js | 27 ++++++++++++++++++++ tests/wasm/simple.rs | 34 +++++++++++++++++++++++--- 6 files changed, 66 insertions(+), 11 deletions(-) diff --git a/crates/cli-support/src/js/binding.rs b/crates/cli-support/src/js/binding.rs index a7dfae6249b..4536e5f8689 100644 --- a/crates/cli-support/src/js/binding.rs +++ b/crates/cli-support/src/js/binding.rs @@ -652,7 +652,7 @@ fn instruction( Instruction::WasmToInt { output, .. } => { let val = js.pop(); match output { - AdapterType::U32 => js.push(format!("{} >>> 0", val)), + AdapterType::U32 | AdapterType::NonNull => js.push(format!("{} >>> 0", val)), AdapterType::U64 => js.push(format!("BigInt.asUintN(64, {val})")), _ => js.push(val), } @@ -1218,14 +1218,14 @@ fn instruction( js.push(format!("{0} === {1} ? undefined : {0}", val, hole)); } - Instruction::InNonNullSentinel => { + Instruction::OptionNonNullFromI32 => { let val = js.pop(); js.cx.expose_is_like_none(); js.assert_optional_number(&val); js.push(format!("isLikeNone({0}) ? 0 : {0}", val)); } - Instruction::OutNonNullSentinel => { + Instruction::I32FromOptionNonNull => { let val = js.pop(); js.push(format!("{0} === 0 ? undefined : {0}", val)); } diff --git a/crates/cli-support/src/wit/incoming.rs b/crates/cli-support/src/wit/incoming.rs index 6f89e85ef41..76f79ebeb5a 100644 --- a/crates/cli-support/src/wit/incoming.rs +++ b/crates/cli-support/src/wit/incoming.rs @@ -156,7 +156,7 @@ impl InstructionBuilder<'_, '_> { // Largely synthetic and can't show up Descriptor::ClampedU8 => unreachable!(), - Descriptor::NonNull => self.number(AdapterType::NonNull, WasmVT::I32), + Descriptor::NonNull => unimplemented!("converting `NonNull` from Wasm to Rust is not implemented"), } Ok(()) } @@ -335,7 +335,7 @@ impl InstructionBuilder<'_, '_> { Descriptor::NonNull => self.instruction( &[AdapterType::NonNull.option()], - Instruction::InNonNullSentinel, + Instruction::OptionNonNullFromI32, &[AdapterType::I32], ), diff --git a/crates/cli-support/src/wit/outgoing.rs b/crates/cli-support/src/wit/outgoing.rs index 0c5ee3aedeb..ebde6b0a3b1 100644 --- a/crates/cli-support/src/wit/outgoing.rs +++ b/crates/cli-support/src/wit/outgoing.rs @@ -323,7 +323,7 @@ impl InstructionBuilder<'_, '_> { Descriptor::NonNull => self.instruction( &[AdapterType::I32], - Instruction::OutNonNullSentinel, + Instruction::I32FromOptionNonNull, &[AdapterType::NonNull.option()], ), diff --git a/crates/cli-support/src/wit/standard.rs b/crates/cli-support/src/wit/standard.rs index ea892680922..3a2f3285c57 100644 --- a/crates/cli-support/src/wit/standard.rs +++ b/crates/cli-support/src/wit/standard.rs @@ -309,8 +309,8 @@ pub enum Instruction { OptionEnumFromI32 { hole: u32, }, - InNonNullSentinel, - OutNonNullSentinel, + OptionNonNullFromI32, + I32FromOptionNonNull, } impl AdapterType { diff --git a/tests/wasm/simple.js b/tests/wasm/simple.js index dfcb19da4ad..bcac3262f49 100644 --- a/tests/wasm/simple.js +++ b/tests/wasm/simple.js @@ -111,6 +111,33 @@ exports.test_string_roundtrip = () => { test('a longer 💖 string'); }; +exports.test_raw_pointers = function() { + const memory32 = new Uint32Array(wasm.__wasm.memory.buffer); + const memory8 = new Uint8Array(wasm.__wasm.memory.buffer); + + const ptr1 = wasm.simple_return_raw_pointer_u32(4294967295); + assert.strictEqual(memory32[ptr1 / 4], 4294967295); + const ptr2 = wasm.simple_return_raw_pointer_u8(42); + assert.strictEqual(memory8[ptr2], 42); + + wasm.simple_raw_pointers_work(ptr1, ptr2); + assert.strictEqual(memory32[ptr1 / 4], 42); + + const ptr3 = wasm.simple_return_raw_pointer_u32(4294967295); + wasm.simple_option_raw_pointers_work(ptr3, ptr2); + assert.strictEqual(memory32[ptr3 / 4], 42); + + assert.strictEqual(wasm.simple_option_raw_pointers_work(0, ptr2), undefined); + assert.strictEqual(wasm.simple_option_raw_pointers_work(null, ptr2), undefined); + assert.strictEqual(wasm.simple_option_raw_pointers_work(undefined, ptr2), undefined); + + assert.strictEqual(wasm.simple_option_raw_pointers_work(ptr1, 0), undefined); + assert.strictEqual(wasm.simple_option_raw_pointers_work(ptr1, null), undefined); + assert.strictEqual(wasm.simple_option_raw_pointers_work(ptr1, undefined), undefined); + + assert.strictEqual(wasm.simple_return_option_null_pointer(), 0) +}; + exports.test_non_null = function() { assert.strictEqual(wasm.simple_option_nonnull_work(0), undefined); assert.strictEqual(wasm.simple_option_nonnull_work(null), undefined); diff --git a/tests/wasm/simple.rs b/tests/wasm/simple.rs index d81c1067635..b6bab8c3874 100644 --- a/tests/wasm/simple.rs +++ b/tests/wasm/simple.rs @@ -1,4 +1,4 @@ -use std::ptr::NonNull; +use std::ptr::{self, NonNull}; use wasm_bindgen::prelude::*; use wasm_bindgen::{intern, unintern}; @@ -32,6 +32,7 @@ extern "C" { fn test_string_roundtrip(); + fn test_raw_pointers(); fn test_non_null(); } @@ -60,19 +61,46 @@ pub fn simple_return_and_take_bool(a: bool, b: bool) -> bool { a && b } +#[wasm_bindgen] +pub fn simple_return_raw_pointer_u32(value: u32) -> *mut u32 { + Box::into_raw(Box::new(value)) +} + +#[wasm_bindgen] +pub fn simple_return_raw_pointer_u8(value: u8) -> *const u8 { + Box::into_raw(Box::new(value)) +} + #[wasm_bindgen] pub unsafe fn simple_raw_pointers_work(a: *mut u32, b: *const u8) -> *const u32 { (*a) = (*b) as u32; a } +#[wasm_bindgen] +pub fn simple_return_option_null_pointer() -> Option<*const u32> { + Some(ptr::null()) +} + #[wasm_bindgen] pub unsafe fn simple_option_raw_pointers_work( a: Option<*mut u32>, b: Option<*const u8>, ) -> Option<*const u32> { - *a.unwrap() = *b.unwrap() as u32; - a.map(|ptr| ptr as *const _) + let a = a.and_then(|ptr| ptr.as_mut()); + let b = b.and_then(|ptr| ptr.as_ref()); + + if let (Some(a), Some(b)) = (a, b) { + *a = *b as u32; + Some(a) + } else { + None + } +} + +#[wasm_bindgen_test] +fn raw_pointers() { + test_raw_pointers(); } #[wasm_bindgen] From 761415e84a3aafeccbc1231d9bad742404e3e8c0 Mon Sep 17 00:00:00 2001 From: daxpedda Date: Mon, 26 Feb 2024 09:53:44 +0100 Subject: [PATCH 5/6] Address review Co-Authored-By: Liam Murphy <43807659+Liamolucko@users.noreply.github.com> --- crates/cli-support/src/js/binding.rs | 6 +++--- crates/cli-support/src/wit/incoming.rs | 2 +- crates/cli-support/src/wit/outgoing.rs | 2 +- crates/cli-support/src/wit/standard.rs | 2 +- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/crates/cli-support/src/js/binding.rs b/crates/cli-support/src/js/binding.rs index 4536e5f8689..f186038bde9 100644 --- a/crates/cli-support/src/js/binding.rs +++ b/crates/cli-support/src/js/binding.rs @@ -1218,16 +1218,16 @@ fn instruction( js.push(format!("{0} === {1} ? undefined : {0}", val, hole)); } - Instruction::OptionNonNullFromI32 => { + Instruction::I32FromOptionNonNull => { let val = js.pop(); js.cx.expose_is_like_none(); js.assert_optional_number(&val); js.push(format!("isLikeNone({0}) ? 0 : {0}", val)); } - Instruction::I32FromOptionNonNull => { + Instruction::OptionNonNullFromI32 => { let val = js.pop(); - js.push(format!("{0} === 0 ? undefined : {0}", val)); + js.push(format!("{0} === 0 ? undefined : {0} >>> 0", val)); } } Ok(()) diff --git a/crates/cli-support/src/wit/incoming.rs b/crates/cli-support/src/wit/incoming.rs index 76f79ebeb5a..084e939e624 100644 --- a/crates/cli-support/src/wit/incoming.rs +++ b/crates/cli-support/src/wit/incoming.rs @@ -335,7 +335,7 @@ impl InstructionBuilder<'_, '_> { Descriptor::NonNull => self.instruction( &[AdapterType::NonNull.option()], - Instruction::OptionNonNullFromI32, + Instruction::I32FromOptionNonNull, &[AdapterType::I32], ), diff --git a/crates/cli-support/src/wit/outgoing.rs b/crates/cli-support/src/wit/outgoing.rs index ebde6b0a3b1..2c0fceefbcd 100644 --- a/crates/cli-support/src/wit/outgoing.rs +++ b/crates/cli-support/src/wit/outgoing.rs @@ -323,7 +323,7 @@ impl InstructionBuilder<'_, '_> { Descriptor::NonNull => self.instruction( &[AdapterType::I32], - Instruction::I32FromOptionNonNull, + Instruction::OptionNonNullFromI32, &[AdapterType::NonNull.option()], ), diff --git a/crates/cli-support/src/wit/standard.rs b/crates/cli-support/src/wit/standard.rs index 3a2f3285c57..d9a86b81a9d 100644 --- a/crates/cli-support/src/wit/standard.rs +++ b/crates/cli-support/src/wit/standard.rs @@ -309,8 +309,8 @@ pub enum Instruction { OptionEnumFromI32 { hole: u32, }, - OptionNonNullFromI32, I32FromOptionNonNull, + OptionNonNullFromI32, } impl AdapterType { From 496fc73e7a15bc47fa6eb47740b8039ef8a09489 Mon Sep 17 00:00:00 2001 From: daxpedda Date: Mon, 26 Feb 2024 11:03:59 +0100 Subject: [PATCH 6/6] Address review Co-Authored-By: Liam Murphy <43807659+Liamolucko@users.noreply.github.com> --- .../non_null.js | 22 +++++++++---------- guide/src/SUMMARY.md | 2 +- 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/examples/guide-supported-types-examples/non_null.js b/examples/guide-supported-types-examples/non_null.js index fbbbf42b9e6..59ff64d80d1 100644 --- a/examples/guide-supported-types-examples/non_null.js +++ b/examples/guide-supported-types-examples/non_null.js @@ -1,12 +1,12 @@ import { - take_pointer_by_value, - return_pointer, - } from './guide_supported_types_examples'; - import { memory } from './guide_supported_types_examples_bg'; - - let ptr = return_pointer(); - let buf = new Uint8Array(memory.buffer); - let value = buf[ptr]; - console.log(`The byte at the ${ptr} address is ${value}`); - - take_pointer_by_value(ptr); + take_pointer_by_value, + return_pointer, +} from './guide_supported_types_examples'; +import { memory } from './guide_supported_types_examples_bg'; + +let ptr = return_pointer(); +let buf = new Uint8Array(memory.buffer); +let value = buf[ptr]; +console.log(`The byte at the ${ptr} address is ${value}`); + +take_pointer_by_value(ptr); diff --git a/guide/src/SUMMARY.md b/guide/src/SUMMARY.md index 970b95d255e..7ba50cd5042 100644 --- a/guide/src/SUMMARY.md +++ b/guide/src/SUMMARY.md @@ -53,7 +53,7 @@ - [Exported Rust Types](./reference/types/exported-rust-types.md) - [`JsValue`](./reference/types/jsvalue.md) - [`Box<[T]>` and `Vec`](./reference/types/boxed-slices.md) - - [`*const T`, `*mut T`](./reference/types/pointers.md) + - [`*const T` and `*mut T`](./reference/types/pointers.md) - [`NonNull`](./reference/types/non-null.md) - [Numbers](./reference/types/numbers.md) - [`bool`](./reference/types/bool.md)