1use webcore::try_from::TryInto;
2use webcore::value::Value;
3
4macro_rules! next {
5 (empty) => {};
6
7 ((peel, $callback:tt, ($value:tt))) => {
8 $callback!( empty => );
9 };
10
11 ((peel, $callback:tt, ($value:tt, $($other:tt),+))) => {
12 $callback!( (peel, $callback, ($($other),+)) => $($other),+ );
13 };
14}
15
16macro_rules! foreach {
17 ($callback:tt => $($values:tt),*) => {
18 $callback!( (peel, $callback, ($($values),*)) => $($values),* );
19 };
20}
21
22macro_rules! loop_through_identifiers {
23 ($callback:tt) => {
24 foreach!( $callback => A0, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12 );
25 };
26}
27
28#[doc(hidden)]
29#[macro_export]
30macro_rules! __js_raw_asm {
31 ($code:expr, $($token:expr),*) => {{
32 #[$crate::private::js_raw_attr]
33 fn snippet() {
34 call!( $code, $($token),* );
35 }
36
37 snippet( $($token as *const u8),* )
38 }};
39
40 ($code:expr) => { $crate::__js_raw_asm!( $code, ) };
41}
42
43#[doc(hidden)]
44#[macro_export]
45macro_rules! __js_raw_asm_int {
46 ($code:expr, $($token:expr),*) => {{
47 #[$crate::private::js_raw_attr]
48 fn snippet() -> i32 {
49 call!( $code, $($token),* );
50 }
51
52 snippet( $($token as *const u8),* )
53 }};
54
55 ($code:expr) => { $crate::__js_raw_asm_int!( $code, ) };
56}
57
58#[cfg(not(all(target_arch = "wasm32", target_vendor = "unknown", target_os = "unknown", not(cargo_web))))]
60#[doc(hidden)]
61#[macro_export]
62macro_rules! __js_raw_asm_bool {
63 ($code:expr, $($token:expr),*) => {{
64 #[$crate::private::js_raw_attr]
65 fn snippet() -> i32 {
66 call!( $code, $($token),* );
67 }
68
69 snippet( $($token as *const u8),* )
70 } == 1};
71
72 ($code:expr) => { $crate::__js_raw_asm_bool!( $code, ) };
73}
74
75#[cfg(all(target_arch = "wasm32", target_vendor = "unknown", target_os = "unknown", not(cargo_web)))]
76#[doc(hidden)]
77#[macro_export]
78macro_rules! __js_raw_asm_bool {
79 ($code:expr, $($token:expr),*) => {{
80 #[$crate::private::js_raw_attr]
81 fn snippet() -> bool {
82 call!( $code, $($token),* );
83 }
84
85 snippet( $($token as *const u8),* )
86 }};
87
88 ($code:expr) => { $crate::__js_raw_asm_bool!( $code, ) };
89}
90
91#[doc(hidden)]
98#[macro_export]
99macro_rules! _js_impl {
100 (@if no_return in [no_return $($rest:tt)*] {$($true_case:tt)*} else {$($false_case:tt)*}) => {
101 $($true_case)*
102 };
103
104 (@if $condition:tt in [] {$($true_case:tt)*} else {$($false_case:tt)*}) => {
105 $($false_case)*
106 };
107
108 (@if $condition:tt in [$token:tt $($rest:tt)*] {$($true_case:tt)*} else {$($false_case:tt)*}) => {
109 $crate::_js_impl!( @if $condition in [$($rest)*] {$($true_case)*} else {$($false_case)*} );
110 };
111
112 (@serialize [] [$($names:tt)*]) => {};
113 (@serialize [$arg:tt $($rest_args:tt)*] [$name:tt $($rest_names:tt)*]) => {
114 let $name = $arg;
115 let $name = $crate::private::IntoNewtype::into_newtype( $name );
116 let mut $name = Some( $name );
117 let $name = $crate::private::JsSerializeOwned::into_js_owned( &mut $name );
118 let $name = &$name as *const $crate::private::SerializedValue as *const _;
119 $crate::_js_impl!( @serialize [$($rest_args)*] [$($rest_names)*] );
120 };
121
122 (@call [$($code:tt)*] [$($flags:tt)*] [$($args:tt)*] [$($arg_names:tt)*] [$($unused_arg_names:tt)*] ->) => {
123 {
128 if cfg!( test ) {
129 $crate::initialize();
130 }
131
132 let restore_point = $crate::private::ArenaRestorePoint::new();
133 $crate::_js_impl!( @serialize [$($args)*] [$($arg_names)*] );
134
135 #[allow(unused_unsafe, unused_parens)]
136 let result = unsafe {
137 $crate::_js_impl!(
138 @if no_return in [$($flags)*] {{
139 #[$crate::private::js_no_return_attr]
140 fn snippet() {
141 call!( $($code)* );
142 }
143
144 snippet( $($arg_names),* );
145 }} else {{
146 let mut result: $crate::private::SerializedValue = std::default::Default::default();
147 let result_ptr = &mut result as *mut $crate::private::SerializedValue as *mut _;
148
149 #[$crate::private::js_attr]
150 fn snippet() {
151 call!( $($code)* );
152 }
153
154 snippet( result_ptr, $($arg_names),* );
155
156 result.deserialize()
157 }}
158 )
159 };
160
161 std::mem::drop( restore_point );
162 result
163 }
164 };
165
166 (@call [$($code:tt)*] [$($flags:tt)*] [$($args:tt)*] [$($arg_names:tt)*] [$($unused_arg_names:tt)*] -> { $($inner:tt)* } $($rest:tt)*) => {
167 $crate::_js_impl!( @call [$($code)*] [$($flags)*] [$($args)*] [$($arg_names)*] [$($unused_arg_names)*] -> $($inner)* $($rest)* );
168 };
169
170 (@call [$($code:tt)*] [$($flags:tt)*] [$($args:tt)*] [$($arg_names:tt)*] [$($unused_arg_names:tt)*] -> ( $($inner:tt)* ) $($rest:tt)*) => {
171 $crate::_js_impl!( @call [$($code)*] [$($flags)*] [$($args)*] [$($arg_names)*] [$($unused_arg_names)*] -> $($inner)* $($rest)* );
172 };
173
174 (@call [$($code:tt)*] [$($flags:tt)*] [$($args:tt)*] [$($arg_names:tt)*] [$($unused_arg_names:tt)*] -> [ $($inner:tt)* ] $($rest:tt)*) => {
175 $crate::_js_impl!( @call [$($code)*] [$($flags)*] [$($args)*] [$($arg_names)*] [$($unused_arg_names)*] -> $($inner)* $($rest)* );
176 };
177
178 (@call [$($code:tt)*] [$($flags:tt)*] [$($args:tt)*] [$($arg_names:tt)*] [$arg_name:tt $($unused_arg_names:tt)*] -> @{$arg:expr} $($rest:tt)*) => {
179 $crate::_js_impl!( @call [$($code)*] [$($flags)*] [$($args)* $arg] [$($arg_names)* $arg_name] [$($unused_arg_names)*] -> $($rest)* );
180 };
181
182 (@call [$($code:tt)*] [$($flags:tt)*] [$($args:tt)*] [$($arg_names:tt)*] [$($unused_arg_names:tt)*] -> $token:tt $($rest:tt)*) => {
183 $crate::_js_impl!( @call [$($code)*] [$($flags)*] [$($args)*] [$($arg_names)*] [$($unused_arg_names)*] -> $($rest)* );
184 };
185}
186
187#[macro_export]
230macro_rules! js {
231 (@($($flags:tt),*) $($token:tt)*) => {
232 $crate::_js_impl!( @call [$($token)*] [$($flags)*] [] [] [a0 a1 a2 a3 a4 a5 a6 a7 a8 a9 a10 a11 a12 a13 a14 a15] -> $($token)* )
233 };
234
235 ($($token:tt)*) => {
236 js! { @() $($token)* }
237 };
238}
239
240#[doc(hidden)]
241#[macro_export]
242macro_rules! __js_serializable_boilerplate {
243 ($kind:tt) => {
244 __js_serializable_boilerplate!( () ($kind) () );
245 };
246
247 (impl< $($impl_arg:tt),* > for $kind:ty where $($bounds:tt)*) => {
248 __js_serializable_boilerplate!( ($($impl_arg),*) ($kind) ($($bounds)*) );
249 };
250
251 (impl< $($impl_arg:tt),* > for $kind:ty) => {
252 __js_serializable_boilerplate!( ($($impl_arg),*) ($kind) () );
253 };
254
255 (($($impl_arg:tt)*) ($($kind_arg:tt)*) ($($bounds:tt)*)) => {
256 impl< $($impl_arg)* > $crate::private::JsSerializeOwned for $($kind_arg)* where $($bounds)* {
257 #[inline]
258 fn into_js_owned< '_a >( value: &'_a mut Option< Self > ) -> $crate::private::SerializedValue< '_a > {
259 $crate::private::JsSerialize::_into_js( value.as_ref().unwrap() )
260 }
261 }
262
263 impl< '_r, $($impl_arg)* > $crate::private::JsSerializeOwned for &'_r $($kind_arg)* where $($bounds)* {
264 #[inline]
265 fn into_js_owned< '_a >( value: &'_a mut Option< Self > ) -> $crate::private::SerializedValue< '_a > {
266 $crate::private::JsSerialize::_into_js( value.unwrap() )
267 }
268 }
269 };
270}
271
272macro_rules! error_boilerplate {
273 ($type_name:ident) => {
274 impl std::fmt::Display for $type_name {
275 fn fmt(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
276 write!(formatter, "{}: {}", stringify!($type_name), self.message())
277 }
278 }
279
280 impl std::error::Error for $type_name {
281 fn description(&self) -> &str {
282 stringify!($type_name)
283 }
284 }
285 };
286
287 ($type_name:ident, dom_exception = $error_name:expr) => {
288 impl ::InstanceOf for $type_name {
289 #[inline]
290 fn instance_of( reference: &Reference ) -> bool {
291 $crate::__js_raw_asm_bool!(
292 concat!(
293 "var r = Module.STDWEB_PRIVATE.acquire_js_reference( $0 );",
294 "return (r instanceof DOMException) && (r.name === \"", $error_name, "\");"
295 ),
296 reference.as_raw()
297 )
298 }
299 }
300
301 error_boilerplate!( $type_name );
302 };
303}
304
305macro_rules! instanceof {
306 ($value:expr, $kind:ident) => {{
307 use $crate::unstable::TryInto;
308 let reference: Option< &$crate::Reference > = (&$value).try_into().ok();
309 reference.map( |reference| {
310 $crate::__js_raw_asm_int!(
311 concat!( "return (Module.STDWEB_PRIVATE.acquire_js_reference( $0 ) instanceof ", stringify!( $kind ), ") | 0;" ),
312 reference.as_raw()
313 ) == 1
314 }).unwrap_or( false )
315 }};
316}
317
318macro_rules! newtype_enum {
319 ($name:ident {
320 $(
321 $(#[$attr:meta])*
322 $variant:ident = $value:expr
323 ),* $(,)*
324 }) => {
325 impl $name {
326 $(
327 $(#[$attr])*
328 pub const $variant: $name = $name($value);
329 )*
330 }
331 impl std::fmt::Debug for $name {
332 fn fmt(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
333 match self.0 {
334 $($value => write!(formatter, "{}::{}", stringify!($name), stringify!($variant)),)*
335 other => write!(formatter, "{}({})", stringify!($name), other)
336 }
337 }
338 }
339 }
340}
341
342#[inline]
346pub fn js_try_convert< T, E, P >( value: Value ) -> Result< Result< T, E >, P >
347 where Value: TryInto< T >, <Value as TryInto< T >>::Error: Into< P >
348{
349 match value.try_into() {
350 Ok( value ) => Ok( Ok( value ) ),
351 Err( error ) => Err( error.into() )
352 }
353}
354
355macro_rules! js_try {
370 (@(no_return) $($token:tt)*) => {{
371 let result = js! {
372 try {
373 $($token)*
374 return {
375 success: true
376 };
377 } catch( error ) {
378 return {
379 error: error,
380 success: false
381 };
382 }
383 };
384
385 use ::webcore::try_from::TryInto;
386 if js!( return @{result.as_ref()}.success; ) == true {
387 Ok(Ok(()))
388 } else {
389 match js!( return @{result}.error; ).try_into() {
390 Ok(e) => Ok(Err(e)),
391 Err(e) => Err(e),
392 }
393 }
394 }};
395
396 ($($token:tt)*) => {{
397 let result = js! {
398 try {
399 return {
400 value: function() { $($token)* }(),
401 success: true
402 };
403 } catch( error ) {
404 return {
405 error: error,
406 success: false
407 };
408 }
409 };
410
411 use webcore::try_from::TryInto;
412 if js!( return @{result.as_ref()}.success; ) == true {
413 ::webcore::macros::js_try_convert( js!( return @{result}.value; ) )
414 } else {
415 match js!( return @{result}.error; ).try_into() {
416 Ok(e) => Ok(Err(e)),
417 Err(e) => Err(e),
418 }
419 }
420 }};
421}
422
423macro_rules! comma_join {
424 ($a:ident) => {
425 stringify!( $a )
426 };
427
428 ($a:ident $b:ident) => {
429 concat!(
430 stringify!( $a ),
431 " or ",
432 stringify!( $b ),
433 )
434 };
435
436 ($a:ident $($item:ident)+) => {
437 concat!(
438 stringify!( $a ),
439 ", ",
440 comma_join!( $($item)+ )
441 )
442 };
443}
444
445macro_rules! error_enum_boilerplate {
446 ($( #[ $error_meta:meta ] )* $error_name:ident, $( $( #[ $variant_meta:meta ] )* $variant:ident),*) => {
447 $( #[ $error_meta ] )*
448 #[derive(Debug, Clone)]
449 pub enum $error_name {
450 $(
451 $( #[ $variant_meta ] )*
452 $variant($variant)
453 ),*
454 }
455
456 impl TryFrom<::webcore::value::Value> for $error_name {
457 type Error = ::webcore::value::ConversionError;
458
459 fn try_from(value: ::webcore::value::Value) -> Result<Self, Self::Error> {
460 $(
461 if let Ok(v) = $variant::try_from(value.clone()) {
462 return Ok($error_name::$variant(v));
463 }
464 )*
465
466 let expected = comma_join!( $($variant)+ ).into();
467 Err( ::webcore::value::ConversionError::type_mismatch( &value, expected ) )
468 }
469 }
470
471 impl std::fmt::Display for $error_name {
472 fn fmt(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
473 match *self {
474 $($error_name::$variant( ref r ) => r.fmt(formatter),)*
475 }
476 }
477 }
478
479 impl std::error::Error for $error_name {
480 fn description(&self) -> &str {
481 stringify!($error_name)
482 }
483 }
484
485 impl ::webcore::serialization::JsSerialize for $error_name {
486 #[doc(hidden)]
487 #[inline]
488 fn _into_js< 'a >( &'a self ) -> ::webcore::serialization::SerializedValue< 'a > {
489 let reference: &::webcore::value::Reference = match self {
490 $(
491 &$error_name::$variant( ref variant ) => variant.as_ref(),
492 )+
493 };
494
495 reference._into_js()
496 }
497 }
498 }
499}
500
501#[cfg(test)]
502mod tests {
503 use webcore::value::Value;
504
505 #[test]
506 fn js_try() {
507 let v: Result<Value, Value> = js_try!( return "test"; ).unwrap();
508 assert_eq!( v, Ok(Value::String("test".to_string())) );
509
510 let v: Result<bool, bool> = js_try!( return true; ).unwrap();
511 assert_eq!( v, Ok(true) );
512
513 let v: Result<bool, bool> = js_try!( throw true; ).unwrap();
514 assert_eq!( v, Err(true) );
515
516 let v: Result<i32, String> = js_try!( throw "error"; ).unwrap();
517 assert_eq!( v, Err("error".to_string()) );
518
519 let v: Result<(), String> = js_try!( @(no_return) 2+2; ).unwrap();
520 assert_eq!( v, Ok(()) );
521
522 let v: Result<(), f64> = js_try!( @(no_return) throw 3.3; ).unwrap();
523 assert_eq!( v, Err(3.3) );
524
525 let v: Result< Result<i32, i32>, _ > = js_try!( return "f"; );
526 assert!( v.is_err() );
527
528 let v: Result< Result<i32, i32>, _ > = js_try!( throw "Broken"; );
529 assert!( v.is_err() );
530 }
531
532 #[test]
533 fn js_try_from_value_to_value() {
534 let output: Result< Value, String > = js_try!( return null; ).unwrap();
535 assert_eq!( output, Ok( Value::Null ) );
536 }
537}