diff --git a/crates/backend/src/codegen.rs b/crates/backend/src/codegen.rs index 72e32398734..8eebc0315f3 100644 --- a/crates/backend/src/codegen.rs +++ b/crates/backend/src/codegen.rs @@ -242,6 +242,16 @@ impl ToTokens for ast::Struct { } } + #[automatically_derived] + impl wasm_bindgen::convert::LongRefFromWasmAbi for #name { + type Abi = u32; + type Anchor = wasm_bindgen::__rt::Ref<'static, #name>; + + unsafe fn long_ref_from_abi(js: Self::Abi) -> Self::Anchor { + ::ref_from_abi(js) + } + } + #[automatically_derived] impl wasm_bindgen::convert::OptionIntoWasmAbi for #name { #[inline] @@ -404,7 +414,7 @@ impl TryToTokens for ast::Export { let mut argtys = Vec::new(); for (i, arg) in self.function.arguments.iter().enumerate() { - argtys.push(&arg.ty); + argtys.push(&*arg.ty); let i = i + offset; let ident = Ident::new(&format!("arg{}", i), Span::call_site()); let ty = &arg.ty; @@ -426,16 +436,31 @@ impl TryToTokens for ast::Export { }); } syn::Type::Reference(syn::TypeReference { elem, .. }) => { - args.push(quote! { - #ident: <#elem as wasm_bindgen::convert::RefFromWasmAbi>::Abi - }); - arg_conversions.push(quote! { - let #ident = unsafe { - <#elem as wasm_bindgen::convert::RefFromWasmAbi> - ::ref_from_abi(#ident) - }; - let #ident = &*#ident; - }); + if self.function.r#async { + args.push(quote! { + #ident: <#elem as wasm_bindgen::convert::LongRefFromWasmAbi>::Abi + }); + arg_conversions.push(quote! { + let #ident = unsafe { + <#elem as wasm_bindgen::convert::LongRefFromWasmAbi> + ::long_ref_from_abi(#ident) + }; + let #ident = <<#elem as wasm_bindgen::convert::LongRefFromWasmAbi> + ::Anchor as core::borrow::Borrow<#elem>> + ::borrow(&#ident); + }); + } else { + args.push(quote! { + #ident: <#elem as wasm_bindgen::convert::RefFromWasmAbi>::Abi + }); + arg_conversions.push(quote! { + let #ident = unsafe { + <#elem as wasm_bindgen::convert::RefFromWasmAbi> + ::ref_from_abi(#ident) + }; + let #ident = &*#ident; + }); + } } _ => { args.push(quote! { @@ -550,6 +575,22 @@ impl TryToTokens for ast::Export { }) .to_tokens(into); + let describe_args: TokenStream = argtys + .iter() + .map(|ty| match ty { + syn::Type::Reference(reference) + if self.function.r#async && reference.mutability.is_none() => + { + let inner = &reference.elem; + quote! { + inform(LONGREF); + <#inner as WasmDescribe>::describe(); + } + } + _ => quote! { <#ty as WasmDescribe>::describe(); }, + }) + .collect(); + // In addition to generating the shim function above which is what // our generated JS will invoke, we *also* generate a "descriptor" // shim. This descriptor shim uses the `WasmDescribe` trait to @@ -573,7 +614,7 @@ impl TryToTokens for ast::Export { inform(FUNCTION); inform(0); inform(#nargs); - #(<#argtys as WasmDescribe>::describe();)* + #describe_args #describe_ret }, attrs: attrs.clone(), @@ -659,7 +700,7 @@ impl ToTokens for ast::ImportType { const #const_name: () = { use wasm_bindgen::convert::{IntoWasmAbi, FromWasmAbi}; use wasm_bindgen::convert::{OptionIntoWasmAbi, OptionFromWasmAbi}; - use wasm_bindgen::convert::RefFromWasmAbi; + use wasm_bindgen::convert::{RefFromWasmAbi, LongRefFromWasmAbi}; use wasm_bindgen::describe::WasmDescribe; use wasm_bindgen::{JsValue, JsCast, JsObject}; use wasm_bindgen::__rt::core; @@ -731,6 +772,17 @@ impl ToTokens for ast::ImportType { } } + impl LongRefFromWasmAbi for #rust_name { + type Abi = ::Abi; + type Anchor = #rust_name; + + #[inline] + unsafe fn long_ref_from_abi(js: Self::Abi) -> Self::Anchor { + let tmp = ::long_ref_from_abi(js); + #rust_name { obj: tmp.into() } + } + } + // TODO: remove this on the next major version impl From for #rust_name { #[inline] diff --git a/crates/cli-support/src/descriptor.rs b/crates/cli-support/src/descriptor.rs index a3b73a2221a..6f7a6ceef42 100644 --- a/crates/cli-support/src/descriptor.rs +++ b/crates/cli-support/src/descriptor.rs @@ -28,6 +28,7 @@ tys! { STRING REF REFMUT + LONGREF SLICE VECTOR EXTERNREF @@ -89,7 +90,7 @@ pub struct Closure { pub mutable: bool, } -#[derive(Clone, Debug)] +#[derive(Clone, Debug, PartialEq, Eq, Hash)] pub enum VectorKind { I8, U8, @@ -132,6 +133,15 @@ impl Descriptor { CLOSURE => Descriptor::Closure(Box::new(Closure::decode(data))), REF => Descriptor::Ref(Box::new(Descriptor::_decode(data, clamped))), REFMUT => Descriptor::RefMut(Box::new(Descriptor::_decode(data, clamped))), + LONGREF => { + // This descriptor basically just serves as a macro, where most things + // become normal `Ref`s, but long refs to externrefs become owned. + let contents = Descriptor::_decode(data, clamped); + match contents { + Descriptor::Externref | Descriptor::NamedExternref(_) => contents, + _ => Descriptor::Ref(Box::new(contents)), + } + } SLICE => Descriptor::Slice(Box::new(Descriptor::_decode(data, clamped))), VECTOR => Descriptor::Vector(Box::new(Descriptor::_decode(data, clamped))), OPTIONAL => Descriptor::Option(Box::new(Descriptor::_decode(data, clamped))), diff --git a/crates/cli-support/src/intrinsic.rs b/crates/cli-support/src/intrinsic.rs index 5e5c70be698..e8874d5e055 100644 --- a/crates/cli-support/src/intrinsic.rs +++ b/crates/cli-support/src/intrinsic.rs @@ -84,6 +84,10 @@ fn opt_i64() -> Descriptor { Descriptor::Option(Box::new(Descriptor::I64)) } +fn slice(contents: Descriptor) -> Descriptor { + Descriptor::Ref(Box::new(Descriptor::Slice(Box::new(contents)))) +} + intrinsics! { pub enum Intrinsic { #[symbol = "__wbindgen_jsval_eq"] @@ -263,6 +267,9 @@ intrinsics! { #[symbol = "__wbindgen_json_serialize"] #[signature = fn(ref_externref()) -> String] JsonSerialize, + #[symbol = "__wbindgen_copy_to_typed_array"] + #[signature = fn(slice(U8), ref_externref()) -> Unit] + CopyToTypedArray, #[symbol = "__wbindgen_externref_heap_live_count"] #[signature = fn() -> I32] ExternrefHeapLiveCount, diff --git a/crates/cli-support/src/js/binding.rs b/crates/cli-support/src/js/binding.rs index f2a0a721d9d..0723928ee9f 100644 --- a/crates/cli-support/src/js/binding.rs +++ b/crates/cli-support/src/js/binding.rs @@ -929,15 +929,8 @@ fn instruction(js: &mut JsBuilder, instr: &Instruction, log_error: &mut bool) -> js.push(format!("len{}", i)); } - Instruction::MutableSliceToMemory { - kind, - malloc, - mem, - free, - } => { - // First up, pass the JS value into wasm, getting out a pointer and - // a length. These two pointer/length values get pushed onto the - // value stack. + Instruction::MutableSliceToMemory { kind, malloc, mem } => { + // Copy the contents of the typed array into wasm. let val = js.pop(); let func = js.cx.pass_to_wasm_function(kind.clone(), *mem)?; let malloc = js.cx.export_name_of(*malloc); @@ -950,25 +943,12 @@ fn instruction(js: &mut JsBuilder, instr: &Instruction, log_error: &mut bool) -> malloc = malloc, )); js.prelude(&format!("var len{} = WASM_VECTOR_LEN;", i)); + // Then pass it the pointer and the length of where we copied it. js.push(format!("ptr{}", i)); js.push(format!("len{}", i)); - - // Next we set up a `finally` clause which will both update the - // original mutable slice with any modifications, and then free the - // Rust-backed memory. - let free = js.cx.export_name_of(*free); - let get = js.cx.memview_function(kind.clone(), *mem); - js.finally(&format!( - " - {val}.set({get}().subarray(ptr{i} / {size}, ptr{i} / {size} + len{i})); - wasm.{free}(ptr{i}, len{i} * {size}); - ", - val = val, - get = get, - free = free, - size = kind.size(), - i = i, - )); + // Then we give wasm a reference to the original typed array, so that it can + // update it with modifications made on the wasm side before returning. + js.push(val); } Instruction::BoolFromI32 => { diff --git a/crates/cli-support/src/js/mod.rs b/crates/cli-support/src/js/mod.rs index c697a52ba05..12f62f7fbee 100644 --- a/crates/cli-support/src/js/mod.rs +++ b/crates/cli-support/src/js/mod.rs @@ -3498,6 +3498,15 @@ impl<'a> Context<'a> { "JSON.stringify(obj === undefined ? null : obj)".to_string() } + Intrinsic::CopyToTypedArray => { + assert_eq!(args.len(), 2); + format!( + "new Uint8Array({dst}.buffer, {dst}.byteOffset, {dst}.byteLength).set({src})", + src = args[0], + dst = args[1] + ) + } + Intrinsic::ExternrefHeapLiveCount => { assert_eq!(args.len(), 0); self.expose_global_heap(); diff --git a/crates/cli-support/src/wit/incoming.rs b/crates/cli-support/src/wit/incoming.rs index 1c8e2cb169c..a940f884dd5 100644 --- a/crates/cli-support/src/wit/incoming.rs +++ b/crates/cli-support/src/wit/incoming.rs @@ -204,9 +204,13 @@ impl InstructionBuilder<'_, '_> { kind, malloc: self.cx.malloc()?, mem: self.cx.memory()?, - free: self.cx.free()?, }, - &[AdapterType::I32, AdapterType::I32], + &[AdapterType::I32, AdapterType::I32, AdapterType::Externref], + ); + self.late_instruction( + &[AdapterType::Externref], + Instruction::I32FromExternrefOwned, + &[AdapterType::I32], ); } else { self.instruction( @@ -373,6 +377,27 @@ impl InstructionBuilder<'_, '_> { self.output.extend_from_slice(outputs); } + /// Add an instruction whose inputs are the results of previous instructions + /// instead of the parameters from JS / results from Rust. + pub fn late_instruction( + &mut self, + inputs: &[AdapterType], + instr: Instruction, + outputs: &[AdapterType], + ) { + for input in inputs { + assert_eq!(self.output.pop().unwrap(), *input); + } + self.instructions.push(InstructionData { + instr, + stack_change: StackChange::Modified { + popped: inputs.len(), + pushed: outputs.len(), + }, + }); + self.output.extend_from_slice(outputs); + } + fn number(&mut self, input: wit_walrus::ValType, output: walrus::ValType) { let std = wit_walrus::Instruction::IntToWasm { input, diff --git a/crates/cli-support/src/wit/standard.rs b/crates/cli-support/src/wit/standard.rs index d07c593491a..7dc98a42beb 100644 --- a/crates/cli-support/src/wit/standard.rs +++ b/crates/cli-support/src/wit/standard.rs @@ -65,7 +65,7 @@ pub enum AdapterJsImportKind { Normal, } -#[derive(Debug, Clone)] +#[derive(Debug, Clone, PartialEq, Eq, Hash)] pub enum AdapterType { S8, S16, @@ -185,7 +185,6 @@ pub enum Instruction { MutableSliceToMemory { kind: VectorKind, malloc: walrus::FunctionId, - free: walrus::FunctionId, mem: walrus::MemoryId, }, @@ -469,12 +468,9 @@ impl walrus::CustomSection for NonstandardWitSection { roots.push_memory(mem); roots.push_func(malloc); } - MutableSliceToMemory { - free, malloc, mem, .. - } => { + MutableSliceToMemory { malloc, mem, .. } => { roots.push_memory(mem); roots.push_func(malloc); - roots.push_func(free); } VectorLoad { free, mem, .. } | OptionVectorLoad { free, mem, .. } diff --git a/crates/macro/ui-tests/missing-catch.stderr b/crates/macro/ui-tests/missing-catch.stderr index 4c20dbe76f7..d08553b1b37 100644 --- a/crates/macro/ui-tests/missing-catch.stderr +++ b/crates/macro/ui-tests/missing-catch.stderr @@ -1,17 +1,17 @@ error[E0277]: the trait bound `Result: FromWasmAbi` is not satisfied - --> $DIR/missing-catch.rs:6:9 - | -6 | pub fn foo() -> Result; - | ^^^ the trait `FromWasmAbi` is not implemented for `Result` - | + --> ui-tests/missing-catch.rs:6:9 + | +6 | pub fn foo() -> Result; + | ^^^ the trait `FromWasmAbi` is not implemented for `Result` + | note: required by a bound in `FromWasmAbi` - --> $DIR/traits.rs:23:1 - | -23 | / pub trait FromWasmAbi: WasmDescribe { -24 | | /// The wasm ABI type that this converts from when coming back out from the -25 | | /// ABI boundary. -26 | | type Abi: WasmAbi; -... | -35 | | unsafe fn from_abi(js: Self::Abi) -> Self; -36 | | } - | |_^ required by this bound in `FromWasmAbi` + --> $WORKSPACE/src/convert/traits.rs + | + | / pub trait FromWasmAbi: WasmDescribe { + | | /// The wasm ABI type that this converts from when coming back out from the + | | /// ABI boundary. + | | type Abi: WasmAbi; +... | + | | unsafe fn from_abi(js: Self::Abi) -> Self; + | | } + | |_^ required by this bound in `FromWasmAbi` diff --git a/crates/macro/ui-tests/traits-not-implemented.stderr b/crates/macro/ui-tests/traits-not-implemented.stderr index 7b0f90f574c..aaad988af58 100644 --- a/crates/macro/ui-tests/traits-not-implemented.stderr +++ b/crates/macro/ui-tests/traits-not-implemented.stderr @@ -1,18 +1,18 @@ error[E0277]: the trait bound `A: IntoWasmAbi` is not satisfied - --> $DIR/traits-not-implemented.rs:5:1 - | -5 | #[wasm_bindgen] - | ^^^^^^^^^^^^^^^ the trait `IntoWasmAbi` is not implemented for `A` - | + --> ui-tests/traits-not-implemented.rs:5:1 + | +5 | #[wasm_bindgen] + | ^^^^^^^^^^^^^^^ the trait `IntoWasmAbi` is not implemented for `A` + | note: required by a bound in `IntoWasmAbi` - --> $DIR/traits.rs:9:1 - | -9 | / pub trait IntoWasmAbi: WasmDescribe { -10 | | /// The wasm ABI type that this converts into when crossing the ABI -11 | | /// boundary. -12 | | type Abi: WasmAbi; -... | -16 | | fn into_abi(self) -> Self::Abi; -17 | | } - | |_^ required by this bound in `IntoWasmAbi` - = note: this error originates in the attribute macro `wasm_bindgen` (in Nightly builds, run with -Z macro-backtrace for more info) + --> $WORKSPACE/src/convert/traits.rs + | + | / pub trait IntoWasmAbi: WasmDescribe { + | | /// The wasm ABI type that this converts into when crossing the ABI + | | /// boundary. + | | type Abi: WasmAbi; +... | + | | fn into_abi(self) -> Self::Abi; + | | } + | |_^ required by this bound in `IntoWasmAbi` + = note: this error originates in the attribute macro `wasm_bindgen` (in Nightly builds, run with -Z macro-backtrace for more info) diff --git a/crates/shared/src/lib.rs b/crates/shared/src/lib.rs index f69d8f4a855..bb20598ddce 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.83"; +pub const SCHEMA_VERSION: &str = "0.2.84"; #[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 4692a28c81e..25084e90566 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: &'static str = "17656911631008664055"; +const APPROVED_SCHEMA_FILE_HASH: &'static str = "1473650450341157828"; #[test] fn schema_version() { diff --git a/src/convert/impls.rs b/src/convert/impls.rs index 79ccd67af97..e8636fe9e66 100644 --- a/src/convert/impls.rs +++ b/src/convert/impls.rs @@ -2,7 +2,7 @@ use core::char; use core::mem::{self, ManuallyDrop}; use crate::convert::traits::WasmAbi; -use crate::convert::{FromWasmAbi, IntoWasmAbi, RefFromWasmAbi}; +use crate::convert::{FromWasmAbi, IntoWasmAbi, LongRefFromWasmAbi, RefFromWasmAbi}; use crate::convert::{OptionFromWasmAbi, OptionIntoWasmAbi, ReturnWasmAbi}; use crate::{Clamped, JsError, JsValue}; @@ -248,6 +248,16 @@ impl RefFromWasmAbi for JsValue { } } +impl LongRefFromWasmAbi for JsValue { + type Abi = u32; + type Anchor = JsValue; + + #[inline] + unsafe fn long_ref_from_abi(js: u32) -> Self::Anchor { + Self::from_abi(js) + } +} + impl IntoWasmAbi for Option { type Abi = T::Abi; diff --git a/src/convert/slices.rs b/src/convert/slices.rs index 9d0970f4e6a..58608b8c520 100644 --- a/src/convert/slices.rs +++ b/src/convert/slices.rs @@ -1,12 +1,15 @@ #[cfg(feature = "std")] use std::prelude::v1::*; -use core::slice; +use core::ops::{Deref, DerefMut}; use core::str; +use crate::__wbindgen_copy_to_typed_array; use crate::cast::JsObject; use crate::convert::OptionIntoWasmAbi; -use crate::convert::{FromWasmAbi, IntoWasmAbi, RefFromWasmAbi, RefMutFromWasmAbi, WasmAbi}; +use crate::convert::{ + FromWasmAbi, IntoWasmAbi, LongRefFromWasmAbi, RefFromWasmAbi, RefMutFromWasmAbi, WasmAbi, +}; use cfg_if::cfg_if; if_std! { @@ -27,6 +30,50 @@ fn null_slice() -> WasmSlice { WasmSlice { ptr: 0, len: 0 } } +if_std! { + #[repr(C)] + pub struct WasmMutSlice { + pub slice: WasmSlice, + pub idx: u32, + } + + unsafe impl WasmAbi for WasmMutSlice {} + + /// The representation of a mutable slice passed from JS to Rust. + pub struct MutSlice { + /// A copy of the data in the JS typed array. + contents: Box<[T]>, + /// A reference to the original JS typed array. + js: JsValue, + } + + impl Drop for MutSlice { + fn drop(&mut self) { + unsafe { + __wbindgen_copy_to_typed_array( + self.contents.as_ptr() as *const u8, + self.contents.len() * mem::size_of::(), + self.js.idx + ); + } + } + } + + impl Deref for MutSlice { + type Target = [T]; + + fn deref(&self) -> &[T] { + &self.contents + } + } + + impl DerefMut for MutSlice { + fn deref_mut(&mut self) -> &mut [T] { + &mut self.contents + } + } +} + macro_rules! vectors { ($($t:ident)*) => ($( if_std! { @@ -109,17 +156,24 @@ macro_rules! vectors { } impl RefMutFromWasmAbi for [$t] { + type Abi = WasmMutSlice; + type Anchor = MutSlice<$t>; + + #[inline] + unsafe fn ref_mut_from_abi(js: WasmMutSlice) -> MutSlice<$t> { + let contents = >::from_abi(js.slice); + let js = JsValue::from_abi(js.idx); + MutSlice { contents, js } + } + } + + impl LongRefFromWasmAbi for [$t] { type Abi = WasmSlice; - type Anchor = &'static mut [$t]; + type Anchor = Box<[$t]>; #[inline] - unsafe fn ref_mut_from_abi(js: WasmSlice) - -> &'static mut [$t] - { - slice::from_raw_parts_mut( - <*mut $t>::from_abi(js.ptr), - js.len as usize, - ) + unsafe fn long_ref_from_abi(js: WasmSlice) -> Box<[$t]> { + Self::ref_from_abi(js) } } )*) @@ -233,6 +287,16 @@ impl RefFromWasmAbi for str { } } +impl LongRefFromWasmAbi for str { + type Abi = <[u8] as RefFromWasmAbi>::Abi; + type Anchor = Box; + + #[inline] + unsafe fn long_ref_from_abi(js: Self::Abi) -> Self::Anchor { + Self::ref_from_abi(js) + } +} + if_std! { use crate::JsValue; diff --git a/src/convert/traits.rs b/src/convert/traits.rs index b9d12b4c891..17b72a837c2 100644 --- a/src/convert/traits.rs +++ b/src/convert/traits.rs @@ -1,3 +1,4 @@ +use core::borrow::Borrow; use core::ops::{Deref, DerefMut}; use crate::describe::*; @@ -58,6 +59,32 @@ pub trait RefFromWasmAbi: WasmDescribe { unsafe fn ref_from_abi(js: Self::Abi) -> Self::Anchor; } +/// A version of the `RefFromWasmAbi` trait with the additional requirement +/// that the reference must remain valid as long as the anchor isn't dropped. +/// +/// This isn't the case for `JsValue`'s `RefFromWasmAbi` implementation. To +/// avoid having to allocate a spot for the `JsValue` on the `JsValue` heap, +/// the `JsValue` is instead pushed onto the `JsValue` stack, and popped off +/// again after the function that the reference was passed to returns. So, +/// `JsValue` has a different `LongRefFromWasmAbi` implementation that behaves +/// the same as `FromWasmAbi`, putting the value on the heap. +/// +/// This is needed for async functions, where the reference needs to be valid +/// for the whole length of the `Future`, rather than the initial synchronous +/// call. +/// +/// 'long ref' is short for 'long-lived reference'. +pub trait LongRefFromWasmAbi: WasmDescribe { + /// Same as `RefFromWasmAbi::Abi` + type Abi: WasmAbi; + + /// Same as `RefFromWasmAbi::Anchor` + type Anchor: Borrow; + + /// Same as `RefFromWasmAbi::ref_from_abi` + unsafe fn long_ref_from_abi(js: Self::Abi) -> Self::Anchor; +} + /// Dual of the `RefFromWasmAbi` trait, except for mutable references. pub trait RefMutFromWasmAbi: WasmDescribe { /// Same as `RefFromWasmAbi::Abi` diff --git a/src/describe.rs b/src/describe.rs index 2b7c4b628c0..be149d7a90b 100644 --- a/src/describe.rs +++ b/src/describe.rs @@ -34,6 +34,7 @@ tys! { STRING REF REFMUT + LONGREF SLICE VECTOR EXTERNREF diff --git a/src/lib.rs b/src/lib.rs index 19a7fe74e6b..ad9b06301b8 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1075,6 +1075,8 @@ externs! { fn __wbindgen_jsval_eq(a: u32, b: u32) -> u32; fn __wbindgen_jsval_loose_eq(a: u32, b: u32) -> u32; + fn __wbindgen_copy_to_typed_array(ptr: *const u8, len: usize, idx: u32) -> (); + fn __wbindgen_not(idx: u32) -> u32; fn __wbindgen_memory() -> u32; @@ -1367,6 +1369,7 @@ pub fn function_table() -> JsValue { #[doc(hidden)] pub mod __rt { use crate::JsValue; + use core::borrow::{Borrow, BorrowMut}; use core::cell::{Cell, UnsafeCell}; use core::ops::{Deref, DerefMut}; @@ -1486,6 +1489,13 @@ pub mod __rt { } } + impl<'b, T: ?Sized> Borrow for Ref<'b, T> { + #[inline] + fn borrow(&self) -> &T { + self.value + } + } + impl<'b, T: ?Sized> Drop for Ref<'b, T> { fn drop(&mut self) { self.borrow.set(self.borrow.get() - 1); @@ -1513,6 +1523,20 @@ pub mod __rt { } } + impl<'b, T: ?Sized> Borrow for RefMut<'b, T> { + #[inline] + fn borrow(&self) -> &T { + self.value + } + } + + impl<'b, T: ?Sized> BorrowMut for RefMut<'b, T> { + #[inline] + fn borrow_mut(&mut self) -> &mut T { + self.value + } + } + impl<'b, T: ?Sized> Drop for RefMut<'b, T> { fn drop(&mut self) { self.borrow.set(0); diff --git a/tests/wasm/futures.js b/tests/wasm/futures.js index 5f4564baca3..b9560866071 100644 --- a/tests/wasm/futures.js +++ b/tests/wasm/futures.js @@ -18,6 +18,10 @@ exports.call_exports = async function() { assert.strictEqual("Hi, Jim!", await wasm.async_take_reference("Jim")); const foo = await new wasm.AsyncStruct(); assert.strictEqual(42, await foo.method()); + await wasm.async_take_js_reference(42); + const buffer = new Int32Array([1, 2, 3, 4]); + await wasm.async_take_mut_slice(buffer); + assert.deepStrictEqual(buffer, new Int32Array([42, 42, 42, 42])); }; exports.call_promise = async function() { diff --git a/tests/wasm/futures.rs b/tests/wasm/futures.rs index ad2be0f71d9..7dfddf2092e 100644 --- a/tests/wasm/futures.rs +++ b/tests/wasm/futures.rs @@ -126,6 +126,16 @@ impl AsyncStruct { } } +#[wasm_bindgen] +pub async fn async_take_js_reference(x: &JsValue) { + assert_eq!(*x, 42); +} + +#[wasm_bindgen] +pub async fn async_take_mut_slice(x: &mut [i32]) { + x.fill(42); +} + #[wasm_bindgen_test] async fn test_promise() { assert_eq!(call_promise().await.as_string(), Some(String::from("ok")))