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