ic_wasm_bindgen/convert/
slices.rs

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