1 | use core::char; |
2 | use core::mem::{self, ManuallyDrop}; |
3 | use core::ptr::NonNull; |
4 | |
5 | use crate::convert::traits::{WasmAbi, WasmPrimitive}; |
6 | use crate::convert::TryFromJsValue; |
7 | use crate::convert::{FromWasmAbi, IntoWasmAbi, LongRefFromWasmAbi, RefFromWasmAbi}; |
8 | use crate::convert::{OptionFromWasmAbi, OptionIntoWasmAbi, ReturnWasmAbi}; |
9 | use crate::{Clamped, JsError, JsValue, UnwrapThrowExt}; |
10 | |
11 | if_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. |
18 | impl<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 | |
35 | impl<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 | |
68 | macro_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 | |
104 | type_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 | |
115 | macro_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 | |
143 | type_abi_as_u32!(i8 u8 i16 u16); |
144 | |
145 | impl IntoWasmAbi for bool { |
146 | type Abi = u32; |
147 | |
148 | #[inline ] |
149 | fn into_abi(self) -> u32 { |
150 | self as u32 |
151 | } |
152 | } |
153 | |
154 | impl FromWasmAbi for bool { |
155 | type Abi = u32; |
156 | |
157 | #[inline ] |
158 | unsafe fn from_abi(js: u32) -> bool { |
159 | js != 0 |
160 | } |
161 | } |
162 | |
163 | impl OptionIntoWasmAbi for bool { |
164 | #[inline ] |
165 | fn none() -> u32 { |
166 | 0x00FF_FFFFu32 |
167 | } |
168 | } |
169 | |
170 | impl OptionFromWasmAbi for bool { |
171 | #[inline ] |
172 | fn is_none(js: &u32) -> bool { |
173 | *js == 0x00FF_FFFFu32 |
174 | } |
175 | } |
176 | |
177 | impl IntoWasmAbi for char { |
178 | type Abi = u32; |
179 | |
180 | #[inline ] |
181 | fn into_abi(self) -> u32 { |
182 | self as u32 |
183 | } |
184 | } |
185 | |
186 | impl 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 | |
196 | impl OptionIntoWasmAbi for char { |
197 | #[inline ] |
198 | fn none() -> u32 { |
199 | 0x00FF_FFFFu32 |
200 | } |
201 | } |
202 | |
203 | impl OptionFromWasmAbi for char { |
204 | #[inline ] |
205 | fn is_none(js: &u32) -> bool { |
206 | *js == 0x00FF_FFFFu32 |
207 | } |
208 | } |
209 | |
210 | impl<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 | |
219 | impl<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 | |
228 | impl<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 | |
237 | impl<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 | |
246 | impl<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 | |
255 | impl<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 | |
264 | impl<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 | |
273 | impl<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 | |
282 | impl<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 | |
291 | impl<T> OptionIntoWasmAbi for NonNull<T> { |
292 | #[inline ] |
293 | fn none() -> u32 { |
294 | 0 |
295 | } |
296 | } |
297 | |
298 | impl<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 | |
308 | impl<T> OptionFromWasmAbi for NonNull<T> { |
309 | #[inline ] |
310 | fn is_none(js: &u32) -> bool { |
311 | *js == 0 |
312 | } |
313 | } |
314 | |
315 | impl 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 | |
326 | impl 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 | |
335 | impl<'a> IntoWasmAbi for &'a JsValue { |
336 | type Abi = u32; |
337 | |
338 | #[inline ] |
339 | fn into_abi(self) -> u32 { |
340 | self.idx |
341 | } |
342 | } |
343 | |
344 | impl 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 | |
354 | impl 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 | |
364 | impl<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 | |
376 | impl<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 | |
389 | impl<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 | |
398 | impl<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 | |
407 | impl IntoWasmAbi for () { |
408 | type Abi = (); |
409 | |
410 | #[inline ] |
411 | fn into_abi(self) { |
412 | self |
413 | } |
414 | } |
415 | |
416 | impl<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 | |
448 | impl<T, E> ReturnWasmAbi for Result<T, E> |
449 | where |
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 | |
468 | impl 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 | |
476 | if_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 | |