Skip to main content

wasm_bindgen/convert/
slices.rs

1use alloc::borrow::Cow;
2use alloc::boxed::Box;
3use alloc::string::String;
4use alloc::vec::Vec;
5use core::mem::{self, MaybeUninit};
6use core::ops::{Deref, DerefMut};
7use core::str;
8
9use crate::__rt::{marker::ErasableGeneric, WasmWord};
10use crate::__wbindgen_copy_to_typed_array;
11use crate::convert::{
12    js_value_vector_from_abi, js_value_vector_into_abi, try_js_value_vector_from_abi, FromWasmAbi,
13    IntoWasmAbi, LongRefFromWasmAbi, MaybeIntoWasmAbi, NullableIntoWasmAbi, OptionFromWasmAbi,
14    OptionIntoWasmAbi, RefFromWasmAbi, RefMutFromWasmAbi, UpcastFrom, VectorFromWasmAbi,
15    VectorIntoWasmAbi, WasmAbi,
16};
17use crate::describe::*;
18use crate::sys::{JsNullable, JsOption};
19use crate::{JsError, JsValue, Nullable};
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)]
32#[derive(Clone, Copy)]
33pub struct WasmSlice {
34    pub ptr: WasmWord,
35    pub len: WasmWord,
36}
37
38impl WasmSlice {
39    #[inline]
40    pub fn from_usize(ptr: usize, len: usize) -> Self {
41        Self {
42            ptr: WasmWord::from_usize(ptr),
43            len: WasmWord::from_usize(len),
44        }
45    }
46}
47
48impl WasmAbi for WasmSlice {
49    /// `self.ptr`
50    type Prim1 = <WasmWord as WasmAbi>::Prim1;
51    /// `self.len`
52    type Prim2 = <WasmWord as WasmAbi>::Prim1;
53    type Prim3 = ();
54    type Prim4 = ();
55
56    #[inline]
57    fn split(self) -> (Self::Prim1, Self::Prim2, (), ()) {
58        (self.ptr.split().0, self.len.split().0, (), ())
59    }
60
61    #[inline]
62    fn join(ptr: Self::Prim1, len: Self::Prim2, _: (), _: ()) -> Self {
63        Self {
64            ptr: WasmWord::join(ptr, (), (), ()),
65            len: WasmWord::join(len, (), (), ()),
66        }
67    }
68}
69
70#[inline]
71fn null_slice() -> WasmSlice {
72    WasmSlice::from_usize(0, 0)
73}
74
75/// A slice with a null pointer carries a JS handle in `len`, as an interned
76/// string does, and `0` is `undefined`; this is the handle of `null`.
77#[inline]
78fn js_null_slice() -> WasmSlice {
79    WasmSlice::from_usize(0, crate::__rt::JSIDX_NULL as usize)
80}
81
82#[inline]
83fn is_js_null_slice(slice: &WasmSlice) -> bool {
84    slice.ptr.is_zero() && slice.len.into_usize() == crate::__rt::JSIDX_NULL as usize
85}
86
87pub struct WasmMutSlice {
88    pub slice: WasmSlice,
89    pub idx: u32,
90}
91
92impl WasmAbi for WasmMutSlice {
93    /// `self.slice.ptr`
94    type Prim1 = <WasmSlice as WasmAbi>::Prim1;
95    /// `self.slice.len`
96    type Prim2 = <WasmSlice as WasmAbi>::Prim2;
97    /// `self.idx`
98    type Prim3 = u32;
99    type Prim4 = ();
100
101    #[inline]
102    fn split(self) -> (Self::Prim1, Self::Prim2, u32, ()) {
103        let (ptr, len, (), ()) = self.slice.split();
104        (ptr, len, self.idx, ())
105    }
106
107    #[inline]
108    fn join(ptr: Self::Prim1, len: Self::Prim2, idx: u32, _: ()) -> Self {
109        Self {
110            slice: WasmSlice::join(ptr, len, (), ()),
111            idx,
112        }
113    }
114}
115
116/// The representation of a mutable slice passed from JS to Rust.
117pub struct MutSlice<T> {
118    /// A copy of the data in the JS typed array.
119    contents: Box<[T]>,
120    /// A reference to the original JS typed array.
121    js: JsValue,
122}
123
124impl<T> Drop for MutSlice<T> {
125    fn drop(&mut self) {
126        let byte_slice = unsafe {
127            core::slice::from_raw_parts(
128                self.contents.as_ptr() as *const u8,
129                self.contents.len() * mem::size_of::<T>(),
130            )
131        };
132        __wbindgen_copy_to_typed_array(byte_slice, &self.js);
133    }
134}
135
136impl<T> Deref for MutSlice<T> {
137    type Target = [T];
138
139    fn deref(&self) -> &[T] {
140        &self.contents
141    }
142}
143
144impl<T> DerefMut for MutSlice<T> {
145    fn deref_mut(&mut self) -> &mut [T] {
146        &mut self.contents
147    }
148}
149
150macro_rules! vectors {
151    ($($t:ty)*) => ($(
152        vectors_internal!($t);
153        vectors_internal!(MaybeUninit<$t>);
154    )*)
155}
156
157macro_rules! vectors_internal {
158    ($t:ty) => {
159        impl WasmDescribeVector for $t {
160            #[cfg_attr(wasm_bindgen_unstable_test_coverage, coverage(off))]
161            fn describe_vector() {
162                inform(VECTOR);
163                <$t>::describe();
164            }
165        }
166
167        impl VectorIntoWasmAbi for $t {
168            type Abi = WasmSlice;
169
170            #[inline]
171            fn vector_into_abi(vector: Box<[$t]>) -> WasmSlice {
172                let ptr = vector.as_ptr();
173                let len = vector.len();
174                mem::forget(vector);
175                WasmSlice::from_usize(ptr as usize, len)
176            }
177        }
178
179        impl VectorFromWasmAbi for $t {
180            type Abi = WasmSlice;
181
182            #[inline]
183            unsafe fn vector_from_abi(js: WasmSlice) -> Box<[$t]> {
184                let ptr = js.ptr.into_usize() as *mut $t;
185                let len = js.len.into_usize();
186                Vec::from_raw_parts(ptr, len, len).into_boxed_slice()
187            }
188        }
189
190        impl VectorRefIntoWasmAbi for $t {
191            #[inline]
192            fn slice_into_abi(slice: &[$t]) -> WasmSlice {
193                WasmSlice::borrow(slice)
194            }
195        }
196
197        impl crate::__rt::marker::VectorRefIntoWasmAbiSealed for $t {}
198
199        impl<'a> IntoWasmAbi for &'a mut [$t] {
200            type Abi = WasmSlice;
201
202            #[inline]
203            fn into_abi(self) -> WasmSlice {
204                (&*self).into_abi()
205            }
206        }
207
208        impl<'a> OptionIntoWasmAbi for &'a mut [$t] {
209            #[inline]
210            fn none() -> WasmSlice {
211                null_slice()
212            }
213        }
214
215        impl_nullable_into_shared_abi!(null = js_null_slice(); ['a] &'a mut [$t]);
216
217        impl RefFromWasmAbi for [$t] {
218            type Abi = WasmSlice;
219            type Anchor = Box<[$t]>;
220
221            #[inline]
222            unsafe fn ref_from_abi(js: WasmSlice) -> Box<[$t]> {
223                <Box<[$t]>>::from_abi(js)
224            }
225        }
226
227        impl RefMutFromWasmAbi for [$t] {
228            type Abi = WasmMutSlice;
229            type Anchor = MutSlice<$t>;
230
231            #[inline]
232            unsafe fn ref_mut_from_abi(js: WasmMutSlice) -> MutSlice<$t> {
233                let contents = <Box<[$t]>>::from_abi(js.slice);
234                let js = JsValue::from_abi(js.idx);
235                MutSlice { contents, js }
236            }
237        }
238
239        impl LongRefFromWasmAbi for [$t] {
240            type Abi = WasmSlice;
241            type Anchor = Box<[$t]>;
242
243            #[inline]
244            unsafe fn long_ref_from_abi(js: WasmSlice) -> Box<[$t]> {
245                Self::ref_from_abi(js)
246            }
247        }
248    };
249}
250
251vectors! {
252    u8 i8 u16 i16 u32 i32 u64 i64 usize isize f32 f64
253}
254
255impl WasmDescribeVector for String {
256    #[cfg_attr(wasm_bindgen_unstable_test_coverage, coverage(off))]
257    fn describe_vector() {
258        inform(VECTOR);
259        inform(NAMED_EXTERNREF);
260        // Trying to use an actual loop for this breaks the Wasm interpreter.
261        inform(6);
262        inform('s' as u32);
263        inform('t' as u32);
264        inform('r' as u32);
265        inform('i' as u32);
266        inform('n' as u32);
267        inform('g' as u32);
268    }
269}
270
271impl VectorIntoWasmAbi for String {
272    type Abi = <Box<[JsValue]> as IntoWasmAbi>::Abi;
273
274    fn vector_into_abi(vector: Box<[Self]>) -> Self::Abi {
275        js_value_vector_into_abi(vector)
276    }
277}
278
279impl VectorFromWasmAbi for String {
280    type Abi = <Box<[JsValue]> as FromWasmAbi>::Abi;
281
282    unsafe fn vector_from_abi(js: Self::Abi) -> Box<[Self]> {
283        js_value_vector_from_abi(js)
284    }
285
286    unsafe fn try_vector_from_abi(js: Self::Abi) -> Result<Box<[Self]>, &'static str> {
287        try_js_value_vector_from_abi(js)
288    }
289}
290
291cfg_if! {
292    if #[cfg(feature = "enable-interning")] {
293        #[inline]
294        fn unsafe_get_cached_str(x: &str) -> Option<WasmSlice> {
295            // This uses 0 for the ptr as an indication that it is a JsValue and not a str.
296            crate::cache::intern::unsafe_get_str(x).map(|x| WasmSlice::from_usize(0, x as usize))
297        }
298
299    } else {
300        #[inline]
301        fn unsafe_get_cached_str(_x: &str) -> Option<WasmSlice> {
302            None
303        }
304    }
305}
306
307impl<T> IntoWasmAbi for Vec<T>
308where
309    Box<[T]>: IntoWasmAbi<Abi = WasmSlice>,
310{
311    type Abi = <Box<[T]> as IntoWasmAbi>::Abi;
312
313    #[inline]
314    fn into_abi(self) -> Self::Abi {
315        self.into_boxed_slice().into_abi()
316    }
317}
318
319impl<T> OptionIntoWasmAbi for Vec<T>
320where
321    Box<[T]>: IntoWasmAbi<Abi = WasmSlice>,
322{
323    #[inline]
324    fn none() -> WasmSlice {
325        null_slice()
326    }
327}
328
329impl_nullable_into_shared_abi!(
330    null = js_null_slice();
331    [T] Vec<T> where Box<[T]>: IntoWasmAbi<Abi = WasmSlice>
332);
333
334/// How a borrowed `&[Self]` crosses to JS.
335///
336/// A sequence crosses as a [`WasmSlice`] of `len` cells. In place, the cell is
337/// the element itself, which is its JS encoding (numbers, and JS handles as
338/// table indices), so the slice is borrowed ([`WasmSlice::borrow`]). Encoded,
339/// the cell is the element's scalar ABI, in a fresh buffer that JS decodes
340/// with the element's own glue and frees ([`WasmSlice::encode`]). The CLI
341/// tells them apart by the element's descriptor alone: numbers and JS handles
342/// are in place, and anything else is encoded. Encoded elements are declared
343/// once, with `__wbindgen_encoded_sequence!`.
344pub trait VectorRefIntoWasmAbi:
345    WasmDescribe + Sized + crate::__rt::marker::VectorRefIntoWasmAbiSealed
346{
347    fn slice_into_abi(slice: &[Self]) -> WasmSlice;
348}
349
350impl<T: VectorRefIntoWasmAbi> IntoWasmAbi for &[T] {
351    type Abi = WasmSlice;
352
353    #[inline]
354    fn into_abi(self) -> WasmSlice {
355        T::slice_into_abi(self)
356    }
357}
358
359impl<T: VectorRefIntoWasmAbi> OptionIntoWasmAbi for &[T] {
360    #[inline]
361    fn none() -> WasmSlice {
362        null_slice()
363    }
364}
365
366impl_nullable_into_shared_abi!(null = js_null_slice(); [T: VectorRefIntoWasmAbi] &[T]);
367
368/// An encoded sequence's cell: the `#[repr(C)]` record of a scalar ABI's
369/// primitives, with unused ones `()`. That is the same split as the wasm
370/// signature, which is how the CLI lays out the cell's words, so both sides
371/// agree on the layout by definition.
372#[repr(C)]
373struct AbiCell<A: WasmAbi>(A::Prim1, A::Prim2, A::Prim3, A::Prim4);
374
375// A `(ptr, len)` cell is two pointer words, which are `f64`s on memory64.
376#[cfg(target_arch = "wasm64")]
377const _: () = assert!(mem::size_of::<AbiCell<WasmSlice>>() == 16);
378#[cfg(target_arch = "wasm64")]
379const _: () = assert!(mem::align_of::<AbiCell<WasmSlice>>() == 8);
380#[cfg(not(target_arch = "wasm64"))]
381const _: () = assert!(mem::size_of::<AbiCell<WasmSlice>>() == 8);
382#[cfg(not(target_arch = "wasm64"))]
383const _: () = assert!(mem::align_of::<AbiCell<WasmSlice>>() == 4);
384
385impl WasmSlice {
386    /// An in-place sequence: `slice`'s own memory, borrowed.
387    #[inline]
388    pub fn borrow<T>(slice: &[T]) -> Self {
389        Self::from_usize(slice.as_ptr() as usize, slice.len())
390    }
391
392    /// An encoded sequence: a fresh buffer of each element's cell, its scalar
393    /// ABI by `f`, which JS decodes and frees.
394    pub fn encode<'a, T: IntoWasmAbi + 'a>(
395        slice: &'a [T],
396        mut f: impl FnMut(&'a T) -> T::Abi,
397    ) -> Self {
398        let cells: Box<[AbiCell<T::Abi>]> = slice
399            .iter()
400            .map(|elem| {
401                let (p1, p2, p3, p4) = f(elem).split();
402                AbiCell(p1, p2, p3, p4)
403            })
404            .collect();
405        let len = cells.len();
406        Self::from_usize(Box::into_raw(cells) as *mut AbiCell<T::Abi> as usize, len)
407    }
408
409    /// Takes an encoded sequence from JS, converting each element from its
410    /// cell's ABI with `FromWasmAbi::try_from_abi`. A rejected element stops
411    /// the decode, dropping the buffer and every element converted so far.
412    ///
413    /// # Safety
414    ///
415    /// `self` must be `len` cells of `T::Abi` that JS allocated at the cell's
416    /// alignment, for Rust to own.
417    pub unsafe fn try_decode<T: FromWasmAbi>(self) -> Result<Box<[T]>, &'static str> {
418        let ptr = self.ptr.into_usize() as *mut AbiCell<T::Abi>;
419        let len = self.len.into_usize();
420        Vec::from_raw_parts(ptr, len, len)
421            .into_iter()
422            .map(|AbiCell(p1, p2, p3, p4)| T::try_from_abi(T::Abi::join(p1, p2, p3, p4)))
423            .collect()
424    }
425}
426
427/// Declares a sequence of `$t` with encoded cells, emitting every impl it
428/// needs together with its descriptor, for each side listed: `ref_into`
429/// (`&[T]` to JS), `into` (`Vec<T>` / `Box<[T]>` to JS) and `from` (`Vec<T>`
430/// / `Box<[T]>` from JS). `$encode` gives a borrowed element's scalar ABI. An
431/// owned vector to JS encodes as its borrowed slice, so `into` is only for
432/// elements that own nothing, which it checks at compile time. `from` takes
433/// its element with `FromWasmAbi` (see [`WasmSlice::try_decode`]).
434#[macro_export]
435#[doc(hidden)]
436macro_rules! __wbindgen_encoded_sequence {
437    ($t:ty, $encode:expr, [$($side:ident),+ $(,)?]) => {
438        $($crate::__wbindgen_encoded_sequence!(@$side $t, $encode);)+
439    };
440    (@ref_into $t:ty, $encode:expr) => {
441        $crate::__wbindgen_coverage! {
442            #[automatically_derived]
443            impl $crate::convert::VectorRefIntoWasmAbi for $t {
444                #[inline]
445                fn slice_into_abi(slice: &[Self]) -> $crate::convert::WasmSlice {
446                    $crate::convert::WasmSlice::encode(slice, $encode)
447                }
448            }
449        }
450
451        #[automatically_derived]
452        impl $crate::__rt::marker::VectorRefIntoWasmAbiSealed for $t {}
453    };
454    (@into $t:ty, $encode:expr) => {
455        const _: () = assert!(!$crate::__rt::core::mem::needs_drop::<$t>());
456
457        $crate::__wbindgen_coverage! {
458            #[automatically_derived]
459            impl $crate::describe::WasmDescribeVector for $t {
460                fn describe_vector() {
461                    $crate::describe::inform($crate::describe::VECTOR);
462                    <Self as $crate::describe::WasmDescribe>::describe();
463                }
464            }
465        }
466
467        #[automatically_derived]
468        impl $crate::convert::VectorIntoWasmAbi for $t {
469            type Abi = $crate::convert::WasmSlice;
470
471            #[inline]
472            fn vector_into_abi(
473                vector: $crate::__rt::alloc::boxed::Box<[Self]>,
474            ) -> $crate::convert::WasmSlice {
475                <Self as $crate::convert::VectorRefIntoWasmAbi>::slice_into_abi(&vector)
476            }
477        }
478    };
479    (@from $t:ty, $encode:expr) => {
480        #[automatically_derived]
481        impl $crate::convert::VectorFromWasmAbi for $t {
482            type Abi = $crate::convert::WasmSlice;
483
484            #[inline]
485            unsafe fn vector_from_abi(
486                js: $crate::convert::WasmSlice,
487            ) -> $crate::__rt::alloc::boxed::Box<[Self]> {
488                // Thrown directly, as a closure's frames would push the
489                // caller past the debug-mode error stack's frame limit.
490                match js.try_decode() {
491                    $crate::__rt::core::result::Result::Ok(decoded) => decoded,
492                    $crate::__rt::core::result::Result::Err(msg) => $crate::throw_str(msg),
493                }
494            }
495
496            #[inline]
497            unsafe fn try_vector_from_abi(
498                js: $crate::convert::WasmSlice,
499            ) -> $crate::__rt::core::result::Result<
500                $crate::__rt::alloc::boxed::Box<[Self]>,
501                &'static str,
502            > {
503                js.try_decode()
504            }
505        }
506    };
507}
508
509crate::__wbindgen_encoded_sequence!(bool, |b| (*b).into_abi(), [ref_into, into, from]);
510crate::__wbindgen_encoded_sequence!(char, |c| (*c).into_abi(), [ref_into, into, from]);
511crate::__wbindgen_encoded_sequence!(&str, |s| (*s).into_abi(), [ref_into, into]);
512crate::__wbindgen_encoded_sequence!(&String, |s| (*s).into_abi(), [ref_into]);
513crate::__wbindgen_encoded_sequence!(&Box<str>, |s| (*s).into_abi(), [ref_into]);
514crate::__wbindgen_encoded_sequence!(&Cow<'_, str>, |s| (*s).into_abi(), [ref_into]);
515// An owned `Vec<String>` keeps its released `JsValue` form.
516crate::__wbindgen_encoded_sequence!(String, |s| s.into_abi(), [ref_into]);
517
518impl crate::__rt::marker::VectorRefIntoWasmAbiSealed for JsValue {}
519
520impl VectorRefIntoWasmAbi for JsValue {
521    #[inline]
522    fn slice_into_abi(slice: &[Self]) -> WasmSlice {
523        WasmSlice::borrow(slice)
524    }
525}
526
527impl crate::__rt::marker::VectorRefIntoWasmAbiSealed for JsError {}
528
529impl VectorRefIntoWasmAbi for JsError {
530    #[inline]
531    fn slice_into_abi(slice: &[Self]) -> WasmSlice {
532        WasmSlice::borrow(slice)
533    }
534}
535
536impl<T> FromWasmAbi for Vec<T>
537where
538    Box<[T]>: FromWasmAbi<Abi = WasmSlice>,
539{
540    type Abi = <Box<[T]> as FromWasmAbi>::Abi;
541
542    #[inline]
543    unsafe fn from_abi(js: Self::Abi) -> Self {
544        <Box<[T]>>::from_abi(js).into()
545    }
546
547    #[inline]
548    unsafe fn try_from_abi(js: Self::Abi) -> Result<Self, &'static str> {
549        <Box<[T]>>::try_from_abi(js).map(Into::into)
550    }
551}
552
553impl<T> OptionFromWasmAbi for Vec<T>
554where
555    Box<[T]>: FromWasmAbi<Abi = WasmSlice>,
556{
557    #[inline]
558    fn is_none(abi: &WasmSlice) -> bool {
559        abi.ptr.is_zero()
560    }
561}
562
563impl_nullable_from_shared_abi!(
564    is_null = |abi| is_js_null_slice(abi);
565    [T] Vec<T> where Box<[T]>: FromWasmAbi<Abi = WasmSlice>
566);
567
568impl IntoWasmAbi for String {
569    type Abi = <Vec<u8> as IntoWasmAbi>::Abi;
570
571    #[inline]
572    fn into_abi(self) -> Self::Abi {
573        // This is safe because the JsValue is immediately looked up in the heap and
574        // then returned, so use-after-free cannot occur.
575        unsafe_get_cached_str(&self).unwrap_or_else(|| self.into_bytes().into_abi())
576    }
577}
578
579impl OptionIntoWasmAbi for String {
580    #[inline]
581    fn none() -> Self::Abi {
582        null_slice()
583    }
584}
585
586impl_nullable_into_shared_abi!(null = js_null_slice(); [] String);
587
588impl FromWasmAbi for String {
589    type Abi = <Vec<u8> as FromWasmAbi>::Abi;
590
591    #[inline]
592    unsafe fn from_abi(js: Self::Abi) -> Self {
593        String::from_utf8_unchecked(<Vec<u8>>::from_abi(js))
594    }
595}
596
597impl OptionFromWasmAbi for String {
598    #[inline]
599    fn is_none(slice: &WasmSlice) -> bool {
600        slice.ptr.is_zero()
601    }
602}
603
604impl_nullable_from_shared_abi!(is_null = |slice| is_js_null_slice(slice); [] String);
605
606impl<'a> IntoWasmAbi for &'a str {
607    type Abi = <&'a [u8] as IntoWasmAbi>::Abi;
608
609    #[inline]
610    fn into_abi(self) -> Self::Abi {
611        // This is safe because the JsValue is immediately looked up in the heap and
612        // then returned, so use-after-free cannot occur.
613        unsafe_get_cached_str(self).unwrap_or_else(|| self.as_bytes().into_abi())
614    }
615}
616
617impl OptionIntoWasmAbi for &str {
618    #[inline]
619    fn none() -> Self::Abi {
620        null_slice()
621    }
622}
623
624impl_nullable_into_shared_abi!(null = js_null_slice(); [] &str);
625
626// Borrowed shapes that deref to `str` cross exactly like `&str`.
627macro_rules! str_ref_into_wasm_abi {
628    ($($t:ty),* $(,)?) => ($(
629        impl IntoWasmAbi for &$t {
630            type Abi = <&'static str as IntoWasmAbi>::Abi;
631
632            #[inline]
633            fn into_abi(self) -> Self::Abi {
634                <&str as IntoWasmAbi>::into_abi(self)
635            }
636        }
637
638        impl OptionIntoWasmAbi for &$t {
639            #[inline]
640            fn none() -> Self::Abi {
641                <&str as OptionIntoWasmAbi>::none()
642            }
643        }
644
645        impl_nullable_into_shared_abi!(null = <&str as NullableIntoWasmAbi>::null(); [] &$t);
646    )*)
647}
648
649str_ref_into_wasm_abi!(String, Box<str>, Cow<'_, str>);
650
651impl RefFromWasmAbi for str {
652    type Abi = <[u8] as RefFromWasmAbi>::Abi;
653    type Anchor = Box<str>;
654
655    #[inline]
656    unsafe fn ref_from_abi(js: Self::Abi) -> Self::Anchor {
657        mem::transmute::<Box<[u8]>, Box<str>>(<Box<[u8]>>::from_abi(js))
658    }
659}
660
661impl LongRefFromWasmAbi for str {
662    type Abi = <[u8] as RefFromWasmAbi>::Abi;
663    type Anchor = Box<str>;
664
665    #[inline]
666    unsafe fn long_ref_from_abi(js: Self::Abi) -> Self::Anchor {
667        Self::ref_from_abi(js)
668    }
669}
670
671unsafe impl<'a> ErasableGeneric for &'a str {
672    type Repr = &'a str;
673}
674
675unsafe impl<T: ErasableGeneric> ErasableGeneric for Box<[T]> {
676    type Repr = Box<[T::Repr]>;
677}
678
679impl<'a> UpcastFrom<&'a str> for &'a str {}
680impl UpcastFrom<String> for String {}
681impl<'a, Target: UpcastFrom<&'a str>> UpcastFrom<&'a str> for Option<Target> {}
682impl<'a, Target: UpcastFrom<&'a str>> UpcastFrom<&'a str> for Nullable<Target> {}
683impl<Target: UpcastFrom<String>> UpcastFrom<String> for Option<Target> {}
684impl<Target: UpcastFrom<String>> UpcastFrom<String> for Nullable<Target> {}
685// `String` and `&str` label the same JS string, a `&str` element's lifetime
686// being phantom, so they upcast both ways, as each does with `JsString`. An
687// inferred `Array<&str>` settles to `Array<String>`, and a `Function<fn(&str)>`
688// slot relates to a `Function<fn(String)>` one.
689impl UpcastFrom<&str> for String {}
690impl UpcastFrom<String> for &str {}
691
692// A Rust string stands for a JS string in a generic position, as `bool` does
693// for a JS boolean, so `Promise<String>` relabels straight to `Promise<JsValue>`.
694impl UpcastFrom<String> for JsValue {}
695impl UpcastFrom<String> for JsOption<JsValue> {}
696impl UpcastFrom<String> for JsNullable<JsValue> {}
697impl UpcastFrom<&str> for JsValue {}
698impl UpcastFrom<&str> for JsOption<JsValue> {}
699impl UpcastFrom<&str> for JsNullable<JsValue> {}
700
701impl<T, Target> UpcastFrom<Box<[T]>> for Box<[Target]> where Target: UpcastFrom<T> {}
702
703unsafe impl<T: ErasableGeneric> ErasableGeneric for Vec<T> {
704    type Repr = Vec<T::Repr>;
705}
706
707impl<T, Target> UpcastFrom<Vec<T>> for Vec<Target> where Target: UpcastFrom<T> {}
708
709impl<T: VectorIntoWasmAbi> IntoWasmAbi for Box<[T]> {
710    type Abi = <T as VectorIntoWasmAbi>::Abi;
711
712    fn into_abi(self) -> Self::Abi {
713        T::vector_into_abi(self)
714    }
715}
716
717impl<T> OptionIntoWasmAbi for Box<[T]>
718where
719    Self: IntoWasmAbi<Abi = WasmSlice>,
720{
721    fn none() -> WasmSlice {
722        null_slice()
723    }
724}
725
726impl_nullable_into_shared_abi!(
727    null = js_null_slice();
728    [T] Box<[T]> where Self: IntoWasmAbi<Abi = WasmSlice>
729);
730
731impl<T: VectorFromWasmAbi> FromWasmAbi for Box<[T]> {
732    type Abi = <T as VectorFromWasmAbi>::Abi;
733
734    unsafe fn from_abi(js: Self::Abi) -> Self {
735        T::vector_from_abi(js)
736    }
737
738    #[inline]
739    unsafe fn try_from_abi(js: Self::Abi) -> Result<Self, &'static str> {
740        T::try_vector_from_abi(js)
741    }
742}
743
744impl<T> OptionFromWasmAbi for Box<[T]>
745where
746    Self: FromWasmAbi<Abi = WasmSlice>,
747{
748    fn is_none(slice: &WasmSlice) -> bool {
749        slice.ptr.is_zero()
750    }
751}
752
753impl_nullable_from_shared_abi!(
754    is_null = |slice| is_js_null_slice(slice);
755    [T] Box<[T]> where Self: FromWasmAbi<Abi = WasmSlice>
756);
757
758impl<T: ErasableGeneric<Repr = JsValue> + WasmDescribe> VectorFromWasmAbi for T {
759    type Abi = WasmSlice;
760
761    #[inline]
762    unsafe fn vector_from_abi(js: WasmSlice) -> Box<[Self]> {
763        let ptr = js.ptr.into_usize() as *mut T;
764        let len = js.len.into_usize();
765        Vec::from_raw_parts(ptr, len, len).into_boxed_slice()
766    }
767}
768
769impl<T: ErasableGeneric<Repr = JsValue> + WasmDescribe> VectorIntoWasmAbi for T {
770    type Abi = WasmSlice;
771
772    #[inline]
773    fn vector_into_abi(vector: Box<[T]>) -> WasmSlice {
774        let ptr = vector.as_ptr();
775        let len = vector.len();
776        mem::forget(vector);
777        WasmSlice::from_usize(ptr as usize, len)
778    }
779}
780
781unsafe impl<'a, T: ErasableGeneric> ErasableGeneric for &'a [T]
782where
783    <T as ErasableGeneric>::Repr: 'a,
784{
785    type Repr = &'a [T::Repr];
786}
787
788impl<'a, T, Target> UpcastFrom<&'a [T]> for &'a [Target] where Target: UpcastFrom<T> {}
789
790// A borrowed number, `bool`, `char` or borrowed string crosses by copy, so
791// `&T`, `Option<&T>` and `Nullable<&T>` have `T`'s ABIs, as generic code
792// borrowing an element (`contains(&T)`) needs for every `T`.
793macro_rules! ref_by_copy {
794    ($($t:ty => $base:ty,)*) => ($(
795        impl IntoWasmAbi for &$t {
796            type Abi = <$base as IntoWasmAbi>::Abi;
797
798            #[inline]
799            fn into_abi(self) -> Self::Abi {
800                (*self).into_abi()
801            }
802        }
803
804        impl MaybeIntoWasmAbi for &$t {
805            type MaybeAbi = <$base as MaybeIntoWasmAbi>::MaybeAbi;
806
807            #[inline]
808            fn into_maybe_abi(self) -> Self::MaybeAbi {
809                (*self).into_maybe_abi()
810            }
811
812            #[inline]
813            fn none() -> Self::MaybeAbi {
814                <$base as MaybeIntoWasmAbi>::none()
815            }
816        }
817
818        impl NullableIntoWasmAbi for &$t {
819            #[inline]
820            fn null() -> Self::MaybeAbi {
821                <$base as NullableIntoWasmAbi>::null()
822            }
823        }
824    )*)
825}
826
827ref_by_copy! {
828    u8 => u8, i8 => i8, u16 => u16, i16 => i16, u32 => u32, i32 => i32,
829    u64 => u64, i64 => i64, u128 => u128, i128 => i128, usize => usize,
830    isize => isize, f32 => f32, f64 => f64, bool => bool, char => char,
831    &str => &'static str, &String => &'static String,
832    &Box<str> => &'static Box<str>, &Cow<'_, str> => &'static Cow<'static, str>,
833}
834
835impl<'a, T: WasmDescribe> IntoWasmAbi for &'a Option<T>
836where
837    Option<&'a T>: IntoWasmAbi,
838{
839    type Abi = <Option<&'a T> as IntoWasmAbi>::Abi;
840
841    #[inline]
842    fn into_abi(self) -> Self::Abi {
843        self.as_ref().into_abi()
844    }
845}
846
847impl<'a, T: WasmDescribe> IntoWasmAbi for &'a Nullable<T>
848where
849    Nullable<&'a T>: IntoWasmAbi,
850{
851    type Abi = <Nullable<&'a T> as IntoWasmAbi>::Abi;
852
853    #[inline]
854    fn into_abi(self) -> Self::Abi {
855        self.as_ref().into_abi()
856    }
857}
858
859impl<'a, T: WasmDescribe> OptionIntoWasmAbi for &'a Nullable<T>
860where
861    Nullable<&'a T>: OptionIntoWasmAbi,
862{
863    #[inline]
864    fn none() -> Self::Abi {
865        <Nullable<&'a T> as OptionIntoWasmAbi>::none()
866    }
867}