wasm_bindgen/convert/
slices.rs

1use alloc::boxed::Box;
2use alloc::string::String;
3use alloc::vec::Vec;
4use core::mem::{self, MaybeUninit};
5use core::ops::{Deref, DerefMut};
6use core::str;
7
8use crate::__wbindgen_copy_to_typed_array;
9use crate::cast::JsObject;
10use crate::convert::{js_value_vector_from_abi, js_value_vector_into_abi};
11use crate::convert::{
12    FromWasmAbi, IntoWasmAbi, LongRefFromWasmAbi, OptionFromWasmAbi, OptionIntoWasmAbi,
13    RefFromWasmAbi, RefMutFromWasmAbi, VectorFromWasmAbi, VectorIntoWasmAbi, WasmAbi,
14};
15use crate::describe::*;
16use crate::JsValue;
17
18use cfg_if::cfg_if;
19
20/// # ⚠️ Unstable
21///
22/// This is part of the internal [`convert`](crate::convert) module, **no
23/// stability guarantees** are provided. Use at your own risk. See its
24/// documentation for more details.
25// note: `WasmAbi` types do not need to be FFI-safe themselves, it's just more
26// convenient to directly write `WasmSlice` in some of the manually-written FFI
27// functions in `lib.rs` rather than `WasmRet<WasmSlice>`.
28#[repr(C)]
29pub struct WasmSlice {
30    pub ptr: u32,
31    pub len: u32,
32}
33
34impl WasmAbi for WasmSlice {
35    /// `self.ptr`
36    type Prim1 = u32;
37    /// `self.len`
38    type Prim2 = u32;
39    type Prim3 = ();
40    type Prim4 = ();
41
42    #[inline]
43    fn split(self) -> (u32, u32, (), ()) {
44        (self.ptr, self.len, (), ())
45    }
46
47    #[inline]
48    fn join(ptr: u32, len: u32, _: (), _: ()) -> Self {
49        Self { ptr, len }
50    }
51}
52
53#[inline]
54fn null_slice() -> WasmSlice {
55    WasmSlice { ptr: 0, len: 0 }
56}
57
58pub struct WasmMutSlice {
59    pub slice: WasmSlice,
60    pub idx: u32,
61}
62
63impl WasmAbi for WasmMutSlice {
64    /// `self.slice.ptr`
65    type Prim1 = u32;
66    /// `self.slice.len`
67    type Prim2 = u32;
68    /// `self.idx`
69    type Prim3 = u32;
70    type Prim4 = ();
71
72    #[inline]
73    fn split(self) -> (u32, u32, u32, ()) {
74        (self.slice.ptr, self.slice.len, self.idx, ())
75    }
76
77    #[inline]
78    fn join(ptr: u32, len: u32, idx: u32, _: ()) -> Self {
79        Self {
80            slice: WasmSlice { ptr, len },
81            idx,
82        }
83    }
84}
85
86/// The representation of a mutable slice passed from JS to Rust.
87pub struct MutSlice<T> {
88    /// A copy of the data in the JS typed array.
89    contents: Box<[T]>,
90    /// A reference to the original JS typed array.
91    js: JsValue,
92}
93
94impl<T> Drop for MutSlice<T> {
95    fn drop(&mut self) {
96        unsafe {
97            __wbindgen_copy_to_typed_array(
98                self.contents.as_ptr() as *const u8,
99                self.contents.len() * mem::size_of::<T>(),
100                self.js.idx,
101            );
102        }
103    }
104}
105
106impl<T> Deref for MutSlice<T> {
107    type Target = [T];
108
109    fn deref(&self) -> &[T] {
110        &self.contents
111    }
112}
113
114impl<T> DerefMut for MutSlice<T> {
115    fn deref_mut(&mut self) -> &mut [T] {
116        &mut self.contents
117    }
118}
119
120macro_rules! vectors {
121    ($($t:ty)*) => ($(
122        vectors_internal!($t);
123        vectors_internal!(MaybeUninit<$t>);
124    )*)
125}
126
127macro_rules! vectors_internal {
128    ($t:ty) => {
129        impl WasmDescribeVector for $t {
130            #[cfg_attr(wasm_bindgen_unstable_test_coverage, coverage(off))]
131            fn describe_vector() {
132                inform(VECTOR);
133                <$t>::describe();
134            }
135        }
136
137        impl VectorIntoWasmAbi for $t {
138            type Abi = WasmSlice;
139
140            #[inline]
141            fn vector_into_abi(vector: Box<[$t]>) -> WasmSlice {
142                let ptr = vector.as_ptr();
143                let len = vector.len();
144                mem::forget(vector);
145                WasmSlice {
146                    ptr: ptr.into_abi(),
147                    len: len as u32,
148                }
149            }
150        }
151
152        impl VectorFromWasmAbi for $t {
153            type Abi = WasmSlice;
154
155            #[inline]
156            unsafe fn vector_from_abi(js: WasmSlice) -> Box<[$t]> {
157                let ptr = <*mut $t>::from_abi(js.ptr);
158                let len = js.len as usize;
159                Vec::from_raw_parts(ptr, len, len).into_boxed_slice()
160            }
161        }
162
163        impl<'a> IntoWasmAbi for &'a [$t] {
164            type Abi = WasmSlice;
165
166            #[inline]
167            fn into_abi(self) -> WasmSlice {
168                WasmSlice {
169                    ptr: self.as_ptr().into_abi(),
170                    len: self.len() as u32,
171                }
172            }
173        }
174
175        impl<'a> OptionIntoWasmAbi for &'a [$t] {
176            #[inline]
177            fn none() -> WasmSlice {
178                null_slice()
179            }
180        }
181
182        impl<'a> IntoWasmAbi for &'a mut [$t] {
183            type Abi = WasmSlice;
184
185            #[inline]
186            fn into_abi(self) -> WasmSlice {
187                (&*self).into_abi()
188            }
189        }
190
191        impl<'a> OptionIntoWasmAbi for &'a mut [$t] {
192            #[inline]
193            fn none() -> WasmSlice {
194                null_slice()
195            }
196        }
197
198        impl RefFromWasmAbi for [$t] {
199            type Abi = WasmSlice;
200            type Anchor = Box<[$t]>;
201
202            #[inline]
203            unsafe fn ref_from_abi(js: WasmSlice) -> Box<[$t]> {
204                <Box<[$t]>>::from_abi(js)
205            }
206        }
207
208        impl RefMutFromWasmAbi for [$t] {
209            type Abi = WasmMutSlice;
210            type Anchor = MutSlice<$t>;
211
212            #[inline]
213            unsafe fn ref_mut_from_abi(js: WasmMutSlice) -> MutSlice<$t> {
214                let contents = <Box<[$t]>>::from_abi(js.slice);
215                let js = JsValue::from_abi(js.idx);
216                MutSlice { contents, js }
217            }
218        }
219
220        impl LongRefFromWasmAbi for [$t] {
221            type Abi = WasmSlice;
222            type Anchor = Box<[$t]>;
223
224            #[inline]
225            unsafe fn long_ref_from_abi(js: WasmSlice) -> Box<[$t]> {
226                Self::ref_from_abi(js)
227            }
228        }
229    };
230}
231
232vectors! {
233    u8 i8 u16 i16 u32 i32 u64 i64 usize isize f32 f64
234}
235
236impl WasmDescribeVector for String {
237    #[cfg_attr(wasm_bindgen_unstable_test_coverage, coverage(off))]
238    fn describe_vector() {
239        inform(VECTOR);
240        inform(NAMED_EXTERNREF);
241        // Trying to use an actual loop for this breaks the Wasm interpreter.
242        inform(6);
243        inform('s' as u32);
244        inform('t' as u32);
245        inform('r' as u32);
246        inform('i' as u32);
247        inform('n' as u32);
248        inform('g' as u32);
249    }
250}
251
252impl VectorIntoWasmAbi for String {
253    type Abi = <Box<[JsValue]> as IntoWasmAbi>::Abi;
254
255    fn vector_into_abi(vector: Box<[Self]>) -> Self::Abi {
256        js_value_vector_into_abi(vector)
257    }
258}
259
260impl VectorFromWasmAbi for String {
261    type Abi = <Box<[JsValue]> as FromWasmAbi>::Abi;
262
263    unsafe fn vector_from_abi(js: Self::Abi) -> Box<[Self]> {
264        js_value_vector_from_abi(js)
265    }
266}
267
268cfg_if! {
269    if #[cfg(feature = "enable-interning")] {
270        #[inline]
271        fn unsafe_get_cached_str(x: &str) -> Option<WasmSlice> {
272            // This uses 0 for the ptr as an indication that it is a JsValue and not a str.
273            crate::cache::intern::unsafe_get_str(x).map(|x| WasmSlice { ptr: 0, len: x })
274        }
275
276    } else {
277        #[inline]
278        fn unsafe_get_cached_str(_x: &str) -> Option<WasmSlice> {
279            None
280        }
281    }
282}
283
284impl<T> IntoWasmAbi for Vec<T>
285where
286    Box<[T]>: IntoWasmAbi<Abi = WasmSlice>,
287{
288    type Abi = <Box<[T]> as IntoWasmAbi>::Abi;
289
290    #[inline]
291    fn into_abi(self) -> Self::Abi {
292        self.into_boxed_slice().into_abi()
293    }
294}
295
296impl<T> OptionIntoWasmAbi for Vec<T>
297where
298    Box<[T]>: IntoWasmAbi<Abi = WasmSlice>,
299{
300    #[inline]
301    fn none() -> WasmSlice {
302        null_slice()
303    }
304}
305
306impl<T> FromWasmAbi for Vec<T>
307where
308    Box<[T]>: FromWasmAbi<Abi = WasmSlice>,
309{
310    type Abi = <Box<[T]> as FromWasmAbi>::Abi;
311
312    #[inline]
313    unsafe fn from_abi(js: Self::Abi) -> Self {
314        <Box<[T]>>::from_abi(js).into()
315    }
316}
317
318impl<T> OptionFromWasmAbi for Vec<T>
319where
320    Box<[T]>: FromWasmAbi<Abi = WasmSlice>,
321{
322    #[inline]
323    fn is_none(abi: &WasmSlice) -> bool {
324        abi.ptr == 0
325    }
326}
327
328impl IntoWasmAbi for String {
329    type Abi = <Vec<u8> as IntoWasmAbi>::Abi;
330
331    #[inline]
332    fn into_abi(self) -> Self::Abi {
333        // This is safe because the JsValue is immediately looked up in the heap and
334        // then returned, so use-after-free cannot occur.
335        unsafe_get_cached_str(&self).unwrap_or_else(|| self.into_bytes().into_abi())
336    }
337}
338
339impl OptionIntoWasmAbi for String {
340    #[inline]
341    fn none() -> Self::Abi {
342        null_slice()
343    }
344}
345
346impl FromWasmAbi for String {
347    type Abi = <Vec<u8> as FromWasmAbi>::Abi;
348
349    #[inline]
350    unsafe fn from_abi(js: Self::Abi) -> Self {
351        String::from_utf8_unchecked(<Vec<u8>>::from_abi(js))
352    }
353}
354
355impl OptionFromWasmAbi for String {
356    #[inline]
357    fn is_none(slice: &WasmSlice) -> bool {
358        slice.ptr == 0
359    }
360}
361
362impl<'a> IntoWasmAbi for &'a str {
363    type Abi = <&'a [u8] as IntoWasmAbi>::Abi;
364
365    #[inline]
366    fn into_abi(self) -> Self::Abi {
367        // This is safe because the JsValue is immediately looked up in the heap and
368        // then returned, so use-after-free cannot occur.
369        unsafe_get_cached_str(self).unwrap_or_else(|| self.as_bytes().into_abi())
370    }
371}
372
373impl OptionIntoWasmAbi for &str {
374    #[inline]
375    fn none() -> Self::Abi {
376        null_slice()
377    }
378}
379
380impl RefFromWasmAbi for str {
381    type Abi = <[u8] as RefFromWasmAbi>::Abi;
382    type Anchor = Box<str>;
383
384    #[inline]
385    unsafe fn ref_from_abi(js: Self::Abi) -> Self::Anchor {
386        mem::transmute::<Box<[u8]>, Box<str>>(<Box<[u8]>>::from_abi(js))
387    }
388}
389
390impl LongRefFromWasmAbi for str {
391    type Abi = <[u8] as RefFromWasmAbi>::Abi;
392    type Anchor = Box<str>;
393
394    #[inline]
395    unsafe fn long_ref_from_abi(js: Self::Abi) -> Self::Anchor {
396        Self::ref_from_abi(js)
397    }
398}
399
400impl<T: VectorIntoWasmAbi> IntoWasmAbi for Box<[T]> {
401    type Abi = <T as VectorIntoWasmAbi>::Abi;
402
403    fn into_abi(self) -> Self::Abi {
404        T::vector_into_abi(self)
405    }
406}
407
408impl<T> OptionIntoWasmAbi for Box<[T]>
409where
410    Self: IntoWasmAbi<Abi = WasmSlice>,
411{
412    fn none() -> WasmSlice {
413        null_slice()
414    }
415}
416
417impl<T: VectorFromWasmAbi> FromWasmAbi for Box<[T]> {
418    type Abi = <T as VectorFromWasmAbi>::Abi;
419
420    unsafe fn from_abi(js: Self::Abi) -> Self {
421        T::vector_from_abi(js)
422    }
423}
424
425impl<T> OptionFromWasmAbi for Box<[T]>
426where
427    Self: FromWasmAbi<Abi = WasmSlice>,
428{
429    fn is_none(slice: &WasmSlice) -> bool {
430        slice.ptr == 0
431    }
432}
433
434impl VectorIntoWasmAbi for JsValue {
435    type Abi = WasmSlice;
436
437    #[inline]
438    fn vector_into_abi(vector: Box<[Self]>) -> WasmSlice {
439        let ptr = vector.as_ptr();
440        let len = vector.len();
441        mem::forget(vector);
442        WasmSlice {
443            ptr: ptr.into_abi(),
444            len: len as u32,
445        }
446    }
447}
448
449impl VectorFromWasmAbi for JsValue {
450    type Abi = WasmSlice;
451
452    #[inline]
453    unsafe fn vector_from_abi(js: WasmSlice) -> Box<[Self]> {
454        let ptr = <*mut JsValue>::from_abi(js.ptr);
455        let len = js.len as usize;
456        Vec::from_raw_parts(ptr, len, len).into_boxed_slice()
457    }
458}
459
460impl<T> VectorIntoWasmAbi for T
461where
462    T: JsObject,
463{
464    type Abi = WasmSlice;
465
466    #[inline]
467    fn vector_into_abi(vector: Box<[T]>) -> WasmSlice {
468        let ptr = vector.as_ptr();
469        let len = vector.len();
470        mem::forget(vector);
471        WasmSlice {
472            ptr: ptr.into_abi(),
473            len: len as u32,
474        }
475    }
476}
477
478impl<T> VectorFromWasmAbi for T
479where
480    T: JsObject,
481{
482    type Abi = WasmSlice;
483
484    #[inline]
485    unsafe fn vector_from_abi(js: WasmSlice) -> Box<[T]> {
486        let ptr = <*mut JsValue>::from_abi(js.ptr);
487        let len = js.len as usize;
488        let vec: Vec<T> = Vec::from_raw_parts(ptr, len, len)
489            .drain(..)
490            .map(|js_value| T::unchecked_from_js(js_value))
491            .collect();
492        vec.into_boxed_slice()
493    }
494}