wasm_bindgen/convert/
impls.rs

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