Skip to main content

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