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