ic_wasm_bindgen/convert/
impls.rs

1use core::char;
2use core::mem::{self, ManuallyDrop};
3
4use crate::convert::traits::{WasmAbi, WasmPrimitive};
5use crate::convert::{FromWasmAbi, IntoWasmAbi, LongRefFromWasmAbi, RefFromWasmAbi};
6use crate::convert::{OptionFromWasmAbi, OptionIntoWasmAbi, ReturnWasmAbi};
7use crate::{Clamped, JsError, JsValue, UnwrapThrowExt};
8
9if_std! {
10    use std::boxed::Box;
11    use std::convert::{TryFrom, TryInto};
12    use std::fmt::Debug;
13    use std::vec::Vec;
14}
15
16// Primitive types can always be passed over the ABI.
17impl<T: WasmPrimitive> WasmAbi for T {
18    type Prim1 = Self;
19    type Prim2 = ();
20    type Prim3 = ();
21    type Prim4 = ();
22
23    #[inline]
24    fn split(self) -> (Self, (), (), ()) {
25        (self, (), (), ())
26    }
27
28    #[inline]
29    fn join(prim: Self, _: (), _: (), _: ()) -> Self {
30        prim
31    }
32}
33
34impl<T: WasmAbi<Prim4 = ()>> WasmAbi for Option<T> {
35    /// Whether this `Option` is a `Some` value.
36    type Prim1 = u32;
37    type Prim2 = T::Prim1;
38    type Prim3 = T::Prim2;
39    type Prim4 = T::Prim3;
40
41    #[inline]
42    fn split(self) -> (u32, T::Prim1, T::Prim2, T::Prim3) {
43        match self {
44            None => (
45                0,
46                Default::default(),
47                Default::default(),
48                Default::default(),
49            ),
50            Some(value) => {
51                let (prim1, prim2, prim3, ()) = value.split();
52                (1, prim1, prim2, prim3)
53            }
54        }
55    }
56
57    #[inline]
58    fn join(is_some: u32, prim1: T::Prim1, prim2: T::Prim2, prim3: T::Prim3) -> Self {
59        if is_some == 0 {
60            None
61        } else {
62            Some(T::join(prim1, prim2, prim3, ()))
63        }
64    }
65}
66
67macro_rules! type_wasm_native {
68    ($($t:tt as $c:tt)*) => ($(
69        impl IntoWasmAbi for $t {
70            type Abi = $c;
71
72            #[inline]
73            fn into_abi(self) -> $c { self as $c }
74        }
75
76        impl FromWasmAbi for $t {
77            type Abi = $c;
78
79            #[inline]
80            unsafe fn from_abi(js: $c) -> Self { js as $t }
81        }
82
83        impl IntoWasmAbi for Option<$t> {
84            type Abi = Option<$c>;
85
86            #[inline]
87            fn into_abi(self) -> Self::Abi {
88                self.map(|v| v as $c)
89            }
90        }
91
92        impl FromWasmAbi for Option<$t> {
93            type Abi = Option<$c>;
94
95            #[inline]
96            unsafe fn from_abi(js: Self::Abi) -> Self {
97                js.map(|v: $c| v as $t)
98            }
99        }
100    )*)
101}
102
103type_wasm_native!(
104    i32 as i32
105    isize as i32
106    u32 as u32
107    usize as u32
108    i64 as i64
109    u64 as u64
110    f32 as f32
111    f64 as f64
112);
113
114macro_rules! type_abi_as_u32 {
115    ($($t:tt)*) => ($(
116        impl IntoWasmAbi for $t {
117            type Abi = u32;
118
119            #[inline]
120            fn into_abi(self) -> u32 { self as u32 }
121        }
122
123        impl FromWasmAbi for $t {
124            type Abi = u32;
125
126            #[inline]
127            unsafe fn from_abi(js: u32) -> Self { js as $t }
128        }
129
130        impl OptionIntoWasmAbi for $t {
131            #[inline]
132            fn none() -> u32 { 0x00FF_FFFFu32 }
133        }
134
135        impl OptionFromWasmAbi for $t {
136            #[inline]
137            fn is_none(js: &u32) -> bool { *js == 0x00FF_FFFFu32 }
138        }
139    )*)
140}
141
142type_abi_as_u32!(i8 u8 i16 u16);
143
144impl IntoWasmAbi for bool {
145    type Abi = u32;
146
147    #[inline]
148    fn into_abi(self) -> u32 {
149        self as u32
150    }
151}
152
153impl FromWasmAbi for bool {
154    type Abi = u32;
155
156    #[inline]
157    unsafe fn from_abi(js: u32) -> bool {
158        js != 0
159    }
160}
161
162impl OptionIntoWasmAbi for bool {
163    #[inline]
164    fn none() -> u32 {
165        0x00FF_FFFFu32
166    }
167}
168
169impl OptionFromWasmAbi for bool {
170    #[inline]
171    fn is_none(js: &u32) -> bool {
172        *js == 0x00FF_FFFFu32
173    }
174}
175
176impl IntoWasmAbi for char {
177    type Abi = u32;
178
179    #[inline]
180    fn into_abi(self) -> u32 {
181        self as u32
182    }
183}
184
185impl FromWasmAbi for char {
186    type Abi = u32;
187
188    #[inline]
189    unsafe fn from_abi(js: u32) -> char {
190        char::from_u32_unchecked(js)
191    }
192}
193
194impl OptionIntoWasmAbi for char {
195    #[inline]
196    fn none() -> u32 {
197        0x00FF_FFFFu32
198    }
199}
200
201impl OptionFromWasmAbi for char {
202    #[inline]
203    fn is_none(js: &u32) -> bool {
204        *js == 0x00FF_FFFFu32
205    }
206}
207
208impl<T> IntoWasmAbi for *const T {
209    type Abi = u32;
210
211    #[inline]
212    fn into_abi(self) -> u32 {
213        self as u32
214    }
215}
216
217impl<T> FromWasmAbi for *const T {
218    type Abi = u32;
219
220    #[inline]
221    unsafe fn from_abi(js: u32) -> *const T {
222        js as *const T
223    }
224}
225
226impl<T> IntoWasmAbi for *mut T {
227    type Abi = u32;
228
229    #[inline]
230    fn into_abi(self) -> u32 {
231        self as u32
232    }
233}
234
235impl<T> FromWasmAbi for *mut T {
236    type Abi = u32;
237
238    #[inline]
239    unsafe fn from_abi(js: u32) -> *mut T {
240        js as *mut T
241    }
242}
243
244impl IntoWasmAbi for JsValue {
245    type Abi = u32;
246
247    #[inline]
248    fn into_abi(self) -> u32 {
249        let ret = self.idx;
250        mem::forget(self);
251        ret
252    }
253}
254
255impl FromWasmAbi for JsValue {
256    type Abi = u32;
257
258    #[inline]
259    unsafe fn from_abi(js: u32) -> JsValue {
260        JsValue::_new(js)
261    }
262}
263
264impl<'a> IntoWasmAbi for &'a JsValue {
265    type Abi = u32;
266
267    #[inline]
268    fn into_abi(self) -> u32 {
269        self.idx
270    }
271}
272
273impl RefFromWasmAbi for JsValue {
274    type Abi = u32;
275    type Anchor = ManuallyDrop<JsValue>;
276
277    #[inline]
278    unsafe fn ref_from_abi(js: u32) -> Self::Anchor {
279        ManuallyDrop::new(JsValue::_new(js))
280    }
281}
282
283impl LongRefFromWasmAbi for JsValue {
284    type Abi = u32;
285    type Anchor = JsValue;
286
287    #[inline]
288    unsafe fn long_ref_from_abi(js: u32) -> Self::Anchor {
289        Self::from_abi(js)
290    }
291}
292
293impl<T: OptionIntoWasmAbi> IntoWasmAbi for Option<T> {
294    type Abi = T::Abi;
295
296    #[inline]
297    fn into_abi(self) -> T::Abi {
298        match self {
299            None => T::none(),
300            Some(me) => me.into_abi(),
301        }
302    }
303}
304
305impl<T: OptionFromWasmAbi> FromWasmAbi for Option<T> {
306    type Abi = T::Abi;
307
308    #[inline]
309    unsafe fn from_abi(js: T::Abi) -> Self {
310        if T::is_none(&js) {
311            None
312        } else {
313            Some(T::from_abi(js))
314        }
315    }
316}
317
318impl<T: IntoWasmAbi> IntoWasmAbi for Clamped<T> {
319    type Abi = T::Abi;
320
321    #[inline]
322    fn into_abi(self) -> Self::Abi {
323        self.0.into_abi()
324    }
325}
326
327impl<T: FromWasmAbi> FromWasmAbi for Clamped<T> {
328    type Abi = T::Abi;
329
330    #[inline]
331    unsafe fn from_abi(js: T::Abi) -> Self {
332        Clamped(T::from_abi(js))
333    }
334}
335
336impl IntoWasmAbi for () {
337    type Abi = ();
338
339    #[inline]
340    fn into_abi(self) {
341        self
342    }
343}
344
345impl<T: WasmAbi<Prim3 = (), Prim4 = ()>> WasmAbi for Result<T, u32> {
346    type Prim1 = T::Prim1;
347    type Prim2 = T::Prim2;
348    // The order of primitives here is such that we can pop() the possible error
349    // first, deal with it and move on. Later primitives are popped off the
350    // stack first.
351    /// If this `Result` is an `Err`, the error value.
352    type Prim3 = u32;
353    /// Whether this `Result` is an `Err`.
354    type Prim4 = u32;
355
356    #[inline]
357    fn split(self) -> (T::Prim1, T::Prim2, u32, u32) {
358        match self {
359            Ok(value) => {
360                let (prim1, prim2, (), ()) = value.split();
361                (prim1, prim2, 0, 0)
362            }
363            Err(err) => (Default::default(), Default::default(), err, 1),
364        }
365    }
366
367    #[inline]
368    fn join(prim1: T::Prim1, prim2: T::Prim2, err: u32, is_err: u32) -> Self {
369        if is_err == 0 {
370            Ok(T::join(prim1, prim2, (), ()))
371        } else {
372            Err(err)
373        }
374    }
375}
376
377impl<T, E> ReturnWasmAbi for Result<T, E>
378where
379    T: IntoWasmAbi,
380    E: Into<JsValue>,
381    T::Abi: WasmAbi<Prim3 = (), Prim4 = ()>,
382{
383    type Abi = Result<T::Abi, u32>;
384
385    #[inline]
386    fn return_abi(self) -> Self::Abi {
387        match self {
388            Ok(v) => Ok(v.into_abi()),
389            Err(e) => {
390                let jsval = e.into();
391                Err(jsval.into_abi())
392            }
393        }
394    }
395}
396
397impl IntoWasmAbi for JsError {
398    type Abi = <JsValue as IntoWasmAbi>::Abi;
399
400    fn into_abi(self) -> Self::Abi {
401        self.value.into_abi()
402    }
403}
404
405if_std! {
406    // Note: this can't take `&[T]` because the `Into<JsValue>` impl needs
407    // ownership of `T`.
408    pub fn js_value_vector_into_abi<T: Into<JsValue>>(vector: Box<[T]>) -> <Box<[JsValue]> as IntoWasmAbi>::Abi {
409        let js_vals: Box<[JsValue]> = vector
410            .into_vec()
411            .into_iter()
412            .map(|x| x.into())
413            .collect();
414
415        js_vals.into_abi()
416    }
417
418    pub unsafe fn js_value_vector_from_abi<T: TryFrom<JsValue>>(js: <Box<[JsValue]> as FromWasmAbi>::Abi) -> Box<[T]> where T::Error: Debug {
419        let js_vals = <Vec<JsValue> as FromWasmAbi>::from_abi(js);
420
421        let mut result = Vec::with_capacity(js_vals.len());
422        for value in js_vals {
423            // We push elements one-by-one instead of using `collect` in order to improve
424            // error messages. When using `collect`, this `expect_throw` is buried in a
425            // giant chain of internal iterator functions, which results in the actual
426            // function that takes this `Vec` falling off the end of the call stack.
427            // So instead, make sure to call it directly within this function.
428            //
429            // This is only a problem in debug mode. Since this is the browser's error stack
430            // we're talking about, it can only see functions that actually make it to the
431            // final wasm binary (i.e., not inlined functions). All of those internal
432            // iterator functions get inlined in release mode, and so they don't show up.
433            result.push(value.try_into().expect_throw("array contains a value of the wrong type"));
434        }
435        result.into_boxed_slice()
436    }
437}