diff --git a/crates/cli-support/src/descriptor.rs b/crates/cli-support/src/descriptor.rs index e21dae1ed290..62dd766ac7a5 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 76b61b42f861..a7dfae6249bd 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 b42541791eb1..6f89e85ef416 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 41763bdbe00e..0c5ee3aedebc 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 95781a6131e4..ea8926809220 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/src/describe.rs b/src/describe.rs index f7e282dcb792..23190f719875 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 bc3edce8c0ad..dfcb19da4add 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 efde14f86980..d81c10676356 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]