wasm_bindgen/convert/
slices.rs

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