Skip to main content

wasm_bindgen/convert/
slices.rs

1use alloc::borrow::Cow;
2use alloc::boxed::Box;
3use alloc::string::String;
4use alloc::vec::Vec;
5use core::mem::{self, MaybeUninit};
6use core::ops::{Deref, DerefMut};
7use core::str;
8
9use crate::__rt::{marker::ErasableGeneric, WasmWord};
10use crate::__wbindgen_copy_to_typed_array;
11use crate::convert::{
12    js_value_vector_from_abi, js_value_vector_into_abi, FromWasmAbi, IntoWasmAbi,
13    LongRefFromWasmAbi, OptionFromWasmAbi, OptionIntoWasmAbi, RefFromWasmAbi, RefMutFromWasmAbi,
14    UpcastFrom, VectorFromWasmAbi, VectorIntoWasmAbi, WasmAbi,
15};
16use crate::describe::*;
17use crate::JsValue;
18
19use cfg_if::cfg_if;
20
21/// # ⚠️ Unstable
22///
23/// This is part of the internal [`convert`](crate::convert) module, **no
24/// stability guarantees** are provided. Use at your own risk. See its
25/// documentation for more details.
26// note: `WasmAbi` types do not need to be FFI-safe themselves, it's just more
27// convenient to directly write `WasmSlice` in some of the manually-written FFI
28// functions in `lib.rs` rather than `WasmRet<WasmSlice>`.
29#[repr(C)]
30#[derive(Clone, Copy)]
31pub struct WasmSlice {
32    pub ptr: WasmWord,
33    pub len: WasmWord,
34}
35
36impl WasmSlice {
37    #[inline]
38    pub fn from_usize(ptr: usize, len: usize) -> Self {
39        Self {
40            ptr: WasmWord::from_usize(ptr),
41            len: WasmWord::from_usize(len),
42        }
43    }
44}
45
46impl WasmAbi for WasmSlice {
47    /// `self.ptr`
48    type Prim1 = <WasmWord as WasmAbi>::Prim1;
49    /// `self.len`
50    type Prim2 = <WasmWord as WasmAbi>::Prim1;
51    type Prim3 = ();
52    type Prim4 = ();
53
54    #[inline]
55    fn split(self) -> (Self::Prim1, Self::Prim2, (), ()) {
56        (self.ptr.split().0, self.len.split().0, (), ())
57    }
58
59    #[inline]
60    fn join(ptr: Self::Prim1, len: Self::Prim2, _: (), _: ()) -> Self {
61        Self {
62            ptr: WasmWord::join(ptr, (), (), ()),
63            len: WasmWord::join(len, (), (), ()),
64        }
65    }
66}
67
68#[inline]
69fn null_slice() -> WasmSlice {
70    WasmSlice::from_usize(0, 0)
71}
72
73pub struct WasmMutSlice {
74    pub slice: WasmSlice,
75    pub idx: u32,
76}
77
78impl WasmAbi for WasmMutSlice {
79    /// `self.slice.ptr`
80    type Prim1 = <WasmSlice as WasmAbi>::Prim1;
81    /// `self.slice.len`
82    type Prim2 = <WasmSlice as WasmAbi>::Prim2;
83    /// `self.idx`
84    type Prim3 = u32;
85    type Prim4 = ();
86
87    #[inline]
88    fn split(self) -> (Self::Prim1, Self::Prim2, u32, ()) {
89        let (ptr, len, (), ()) = self.slice.split();
90        (ptr, len, self.idx, ())
91    }
92
93    #[inline]
94    fn join(ptr: Self::Prim1, len: Self::Prim2, idx: u32, _: ()) -> Self {
95        Self {
96            slice: WasmSlice::join(ptr, len, (), ()),
97            idx,
98        }
99    }
100}
101
102/// The representation of a mutable slice passed from JS to Rust.
103pub struct MutSlice<T> {
104    /// A copy of the data in the JS typed array.
105    contents: Box<[T]>,
106    /// A reference to the original JS typed array.
107    js: JsValue,
108}
109
110impl<T> Drop for MutSlice<T> {
111    fn drop(&mut self) {
112        let byte_slice = unsafe {
113            core::slice::from_raw_parts(
114                self.contents.as_ptr() as *const u8,
115                self.contents.len() * mem::size_of::<T>(),
116            )
117        };
118        __wbindgen_copy_to_typed_array(byte_slice, &self.js);
119    }
120}
121
122impl<T> Deref for MutSlice<T> {
123    type Target = [T];
124
125    fn deref(&self) -> &[T] {
126        &self.contents
127    }
128}
129
130impl<T> DerefMut for MutSlice<T> {
131    fn deref_mut(&mut self) -> &mut [T] {
132        &mut self.contents
133    }
134}
135
136macro_rules! vectors {
137    ($($t:ty)*) => ($(
138        vectors_internal!($t);
139        vectors_internal!(MaybeUninit<$t>);
140    )*)
141}
142
143macro_rules! vectors_internal {
144    ($t:ty) => {
145        impl WasmDescribeVector for $t {
146            #[cfg_attr(wasm_bindgen_unstable_test_coverage, coverage(off))]
147            fn describe_vector() {
148                inform(VECTOR);
149                <$t>::describe();
150            }
151        }
152
153        impl VectorIntoWasmAbi for $t {
154            type Abi = WasmSlice;
155
156            #[inline]
157            fn vector_into_abi(vector: Box<[$t]>) -> WasmSlice {
158                let ptr = vector.as_ptr();
159                let len = vector.len();
160                mem::forget(vector);
161                WasmSlice::from_usize(ptr as usize, len)
162            }
163        }
164
165        impl VectorFromWasmAbi for $t {
166            type Abi = WasmSlice;
167
168            #[inline]
169            unsafe fn vector_from_abi(js: WasmSlice) -> Box<[$t]> {
170                let ptr = js.ptr.into_usize() as *mut $t;
171                let len = js.len.into_usize();
172                Vec::from_raw_parts(ptr, len, len).into_boxed_slice()
173            }
174        }
175
176        impl<'a> IntoWasmAbi for &'a [$t] {
177            type Abi = WasmSlice;
178
179            #[inline]
180            fn into_abi(self) -> WasmSlice {
181                WasmSlice::from_usize(self.as_ptr() as usize, self.len())
182            }
183        }
184
185        impl<'a> OptionIntoWasmAbi for &'a [$t] {
186            #[inline]
187            fn none() -> WasmSlice {
188                null_slice()
189            }
190        }
191
192        impl<'a> IntoWasmAbi for &'a mut [$t] {
193            type Abi = WasmSlice;
194
195            #[inline]
196            fn into_abi(self) -> WasmSlice {
197                (&*self).into_abi()
198            }
199        }
200
201        impl<'a> OptionIntoWasmAbi for &'a mut [$t] {
202            #[inline]
203            fn none() -> WasmSlice {
204                null_slice()
205            }
206        }
207
208        impl RefFromWasmAbi for [$t] {
209            type Abi = WasmSlice;
210            type Anchor = Box<[$t]>;
211
212            #[inline]
213            unsafe fn ref_from_abi(js: WasmSlice) -> Box<[$t]> {
214                <Box<[$t]>>::from_abi(js)
215            }
216        }
217
218        impl RefMutFromWasmAbi for [$t] {
219            type Abi = WasmMutSlice;
220            type Anchor = MutSlice<$t>;
221
222            #[inline]
223            unsafe fn ref_mut_from_abi(js: WasmMutSlice) -> MutSlice<$t> {
224                let contents = <Box<[$t]>>::from_abi(js.slice);
225                let js = JsValue::from_abi(js.idx);
226                MutSlice { contents, js }
227            }
228        }
229
230        impl LongRefFromWasmAbi for [$t] {
231            type Abi = WasmSlice;
232            type Anchor = Box<[$t]>;
233
234            #[inline]
235            unsafe fn long_ref_from_abi(js: WasmSlice) -> Box<[$t]> {
236                Self::ref_from_abi(js)
237            }
238        }
239    };
240}
241
242vectors! {
243    u8 i8 u16 i16 u32 i32 u64 i64 usize isize f32 f64
244}
245
246impl WasmDescribeVector for String {
247    #[cfg_attr(wasm_bindgen_unstable_test_coverage, coverage(off))]
248    fn describe_vector() {
249        inform(VECTOR);
250        inform(NAMED_EXTERNREF);
251        // Trying to use an actual loop for this breaks the Wasm interpreter.
252        inform(6);
253        inform('s' as u32);
254        inform('t' as u32);
255        inform('r' as u32);
256        inform('i' as u32);
257        inform('n' as u32);
258        inform('g' as u32);
259    }
260}
261
262impl VectorIntoWasmAbi for String {
263    type Abi = <Box<[JsValue]> as IntoWasmAbi>::Abi;
264
265    fn vector_into_abi(vector: Box<[Self]>) -> Self::Abi {
266        js_value_vector_into_abi(vector)
267    }
268}
269
270impl VectorFromWasmAbi for String {
271    type Abi = <Box<[JsValue]> as FromWasmAbi>::Abi;
272
273    unsafe fn vector_from_abi(js: Self::Abi) -> Box<[Self]> {
274        js_value_vector_from_abi(js)
275    }
276}
277
278cfg_if! {
279    if #[cfg(feature = "enable-interning")] {
280        #[inline]
281        fn unsafe_get_cached_str(x: &str) -> Option<WasmSlice> {
282            // This uses 0 for the ptr as an indication that it is a JsValue and not a str.
283            crate::cache::intern::unsafe_get_str(x).map(|x| WasmSlice::from_usize(0, x as usize))
284        }
285
286    } else {
287        #[inline]
288        fn unsafe_get_cached_str(_x: &str) -> Option<WasmSlice> {
289            None
290        }
291    }
292}
293
294impl<T> IntoWasmAbi for Vec<T>
295where
296    Box<[T]>: IntoWasmAbi<Abi = WasmSlice>,
297{
298    type Abi = <Box<[T]> as IntoWasmAbi>::Abi;
299
300    #[inline]
301    fn into_abi(self) -> Self::Abi {
302        self.into_boxed_slice().into_abi()
303    }
304}
305
306impl<T> OptionIntoWasmAbi for Vec<T>
307where
308    Box<[T]>: IntoWasmAbi<Abi = WasmSlice>,
309{
310    #[inline]
311    fn none() -> WasmSlice {
312        null_slice()
313    }
314}
315
316/// Internal trait used by the `slice_to_array` macro codegen.
317///
318/// Produces the wire representation JS observes when an outgoing `&[T]`
319/// argument is rendered as a plain `Array`. There are two impl shapes,
320/// neither of which requires `T: Clone`:
321///
322/// * For primitive numeric `T` (`u8`, `i32`, `f64`, ...) the wire is a
323///   borrow of the slice memory directly — no allocation, no copy. The
324///   JS-side shim performs `Array.from(typedArrayView)` to materialise
325///   the JS `Array` and never frees the buffer.
326/// * For everything else (`String`, `JsValue`, imported types, exported
327///   types) the wire is a freshly allocated `Box<[u32]>` of externref
328///   indices — one per element, constructed via `&T -> JsValue` (which
329///   for handle-shaped types is a refcount bump on the existing JS
330///   slot, and for `String` / value-shaped types creates a fresh JS
331///   value). The JS-side shim reads the indices into a JS `Array` and
332///   frees the index buffer.
333///
334/// Both shapes carry the same `WasmSlice` (ptr + len) on the wire. The
335/// cli-support side picks the right JS shim based on the element
336/// `VectorKind` recovered from the descriptor.
337///
338/// Not user-facing: users opt in via `#[wasm_bindgen(slice_to_array)]`
339/// on an imported function or `extern "C"` block.
340pub trait VectorRefIntoWasmAbi {
341    /// Construct the wire representation for `Some(slice)`. The returned
342    /// `WasmSlice` is either a borrow of the input slice (primitive
343    /// case) or a buffer JS owns and frees (handle-shaped case).
344    fn slice_into_abi(slice: &[Self]) -> WasmSlice
345    where
346        Self: Sized;
347
348    /// Wire representation for `None` (used by `Option<&[T]>`). A null
349    /// `WasmSlice` (`ptr == 0`) is the convention shared with every
350    /// other vector-like ABI in the crate.
351    #[inline]
352    fn slice_none() -> WasmSlice
353    where
354        Self: Sized,
355    {
356        null_slice()
357    }
358}
359
360macro_rules! vector_ref_into_wasm_abi_primitive {
361    ($($t:ty)*) => ($(
362        impl VectorRefIntoWasmAbi for $t {
363            #[inline]
364            fn slice_into_abi(slice: &[Self]) -> WasmSlice {
365                // Borrow of the slice memory; the JS shim does
366                // `Array.from(view)` and never frees.
367                WasmSlice::from_usize(slice.as_ptr() as usize, slice.len())
368            }
369        }
370    )*);
371}
372
373vector_ref_into_wasm_abi_primitive!(u8 i8 u16 i16 u32 i32 u64 i64 usize isize f32 f64);
374
375impl<T> VectorRefIntoWasmAbi for T
376where
377    for<'a> &'a T: Into<JsValue>,
378{
379    #[inline]
380    fn slice_into_abi(slice: &[Self]) -> WasmSlice {
381        // Build a fresh `[JsValue]` buffer one element at a time. The
382        // existing `Vec<JsValue>` ABI hands off the buffer to JS; the
383        // JS shim drops each externref slot it reads and frees the
384        // buffer.
385        let js_vals: Box<[JsValue]> = slice.iter().map(Into::into).collect();
386        js_vals.into_abi()
387    }
388}
389
390impl<T> FromWasmAbi for Vec<T>
391where
392    Box<[T]>: FromWasmAbi<Abi = WasmSlice>,
393{
394    type Abi = <Box<[T]> as FromWasmAbi>::Abi;
395
396    #[inline]
397    unsafe fn from_abi(js: Self::Abi) -> Self {
398        <Box<[T]>>::from_abi(js).into()
399    }
400}
401
402impl<T> OptionFromWasmAbi for Vec<T>
403where
404    Box<[T]>: FromWasmAbi<Abi = WasmSlice>,
405{
406    #[inline]
407    fn is_none(abi: &WasmSlice) -> bool {
408        abi.ptr.is_zero()
409    }
410}
411
412impl IntoWasmAbi for String {
413    type Abi = <Vec<u8> as IntoWasmAbi>::Abi;
414
415    #[inline]
416    fn into_abi(self) -> Self::Abi {
417        // This is safe because the JsValue is immediately looked up in the heap and
418        // then returned, so use-after-free cannot occur.
419        unsafe_get_cached_str(&self).unwrap_or_else(|| self.into_bytes().into_abi())
420    }
421}
422
423impl OptionIntoWasmAbi for String {
424    #[inline]
425    fn none() -> Self::Abi {
426        null_slice()
427    }
428}
429
430impl FromWasmAbi for String {
431    type Abi = <Vec<u8> as FromWasmAbi>::Abi;
432
433    #[inline]
434    unsafe fn from_abi(js: Self::Abi) -> Self {
435        String::from_utf8_unchecked(<Vec<u8>>::from_abi(js))
436    }
437}
438
439impl OptionFromWasmAbi for String {
440    #[inline]
441    fn is_none(slice: &WasmSlice) -> bool {
442        slice.ptr.is_zero()
443    }
444}
445
446impl<'a> IntoWasmAbi for &'a str {
447    type Abi = <&'a [u8] as IntoWasmAbi>::Abi;
448
449    #[inline]
450    fn into_abi(self) -> Self::Abi {
451        // This is safe because the JsValue is immediately looked up in the heap and
452        // then returned, so use-after-free cannot occur.
453        unsafe_get_cached_str(self).unwrap_or_else(|| self.as_bytes().into_abi())
454    }
455}
456
457impl OptionIntoWasmAbi for &str {
458    #[inline]
459    fn none() -> Self::Abi {
460        null_slice()
461    }
462}
463
464// Borrowed shapes that deref to `str` cross exactly like `&str`. `&&str` is
465// omitted: the blanket `&T` describe gives `REF REF STRING`, which the CLI
466// can't bind.
467macro_rules! str_ref_into_wasm_abi {
468    ($($t:ty),* $(,)?) => ($(
469        impl IntoWasmAbi for &$t {
470            type Abi = <&'static str as IntoWasmAbi>::Abi;
471
472            #[inline]
473            fn into_abi(self) -> Self::Abi {
474                <&str as IntoWasmAbi>::into_abi(self)
475            }
476        }
477
478        impl OptionIntoWasmAbi for &$t {
479            #[inline]
480            fn none() -> Self::Abi {
481                <&str as OptionIntoWasmAbi>::none()
482            }
483        }
484    )*)
485}
486
487str_ref_into_wasm_abi!(String, Box<str>, Cow<'_, str>);
488
489impl RefFromWasmAbi for str {
490    type Abi = <[u8] as RefFromWasmAbi>::Abi;
491    type Anchor = Box<str>;
492
493    #[inline]
494    unsafe fn ref_from_abi(js: Self::Abi) -> Self::Anchor {
495        mem::transmute::<Box<[u8]>, Box<str>>(<Box<[u8]>>::from_abi(js))
496    }
497}
498
499impl LongRefFromWasmAbi for str {
500    type Abi = <[u8] as RefFromWasmAbi>::Abi;
501    type Anchor = Box<str>;
502
503    #[inline]
504    unsafe fn long_ref_from_abi(js: Self::Abi) -> Self::Anchor {
505        Self::ref_from_abi(js)
506    }
507}
508
509unsafe impl ErasableGeneric for &str {
510    type Repr = &'static str;
511}
512
513unsafe impl<T: ErasableGeneric> ErasableGeneric for Box<[T]> {
514    type Repr = Box<[T::Repr]>;
515}
516
517impl UpcastFrom<&str> for &str {}
518
519impl<T, Target> UpcastFrom<Box<[T]>> for Box<[Target]> where Target: UpcastFrom<T> {}
520
521unsafe impl<T: ErasableGeneric> ErasableGeneric for Vec<T> {
522    type Repr = Vec<T::Repr>;
523}
524
525impl<T, Target> UpcastFrom<Vec<T>> for Vec<Target> where Target: UpcastFrom<T> {}
526
527impl<T: VectorIntoWasmAbi> IntoWasmAbi for Box<[T]> {
528    type Abi = <T as VectorIntoWasmAbi>::Abi;
529
530    fn into_abi(self) -> Self::Abi {
531        T::vector_into_abi(self)
532    }
533}
534
535impl<T> OptionIntoWasmAbi for Box<[T]>
536where
537    Self: IntoWasmAbi<Abi = WasmSlice>,
538{
539    fn none() -> WasmSlice {
540        null_slice()
541    }
542}
543
544impl<T: VectorFromWasmAbi> FromWasmAbi for Box<[T]> {
545    type Abi = <T as VectorFromWasmAbi>::Abi;
546
547    unsafe fn from_abi(js: Self::Abi) -> Self {
548        T::vector_from_abi(js)
549    }
550}
551
552impl<T> OptionFromWasmAbi for Box<[T]>
553where
554    Self: FromWasmAbi<Abi = WasmSlice>,
555{
556    fn is_none(slice: &WasmSlice) -> bool {
557        slice.ptr.is_zero()
558    }
559}
560
561impl<T: ErasableGeneric<Repr = JsValue> + WasmDescribe> VectorFromWasmAbi for T {
562    type Abi = WasmSlice;
563
564    #[inline]
565    unsafe fn vector_from_abi(js: WasmSlice) -> Box<[Self]> {
566        let ptr = js.ptr.into_usize() as *mut T;
567        let len = js.len.into_usize();
568        Vec::from_raw_parts(ptr, len, len).into_boxed_slice()
569    }
570}
571
572impl<T: ErasableGeneric<Repr = JsValue> + WasmDescribe> VectorIntoWasmAbi for T {
573    type Abi = WasmSlice;
574
575    #[inline]
576    fn vector_into_abi(vector: Box<[T]>) -> WasmSlice {
577        let ptr = vector.as_ptr();
578        let len = vector.len();
579        mem::forget(vector);
580        WasmSlice::from_usize(ptr as usize, len)
581    }
582}
583
584// JsValue-like slice support (Rust-to-JS only)
585// JsValue-like are repr(transparent) over u32, so &[JsValue] is a contiguous array of heap indices
586
587unsafe impl<T: ErasableGeneric> ErasableGeneric for &[T] {
588    type Repr = &'static [T::Repr];
589}
590
591impl<'a, T, Target> UpcastFrom<&'a [T]> for &'a [Target] where Target: UpcastFrom<T> {}
592
593impl<T: ErasableGeneric<Repr = JsValue> + WasmDescribe> IntoWasmAbi for &[T] {
594    type Abi = WasmSlice;
595
596    #[inline]
597    fn into_abi(self) -> WasmSlice {
598        WasmSlice::from_usize(self.as_ptr() as usize, self.len())
599    }
600}
601
602impl<T: ErasableGeneric<Repr = JsValue> + WasmDescribe> OptionIntoWasmAbi for &[T] {
603    #[inline]
604    fn none() -> WasmSlice {
605        null_slice()
606    }
607}