artichoke_backend/
macros.rs

1//! Macros for use within the Artichoke VM.
2//!
3//! This module contains macros for working with interpreters and Ruby values.
4//! This module should be included first in `lib.rs` with the `#[macro_use]`
5//! attribute, which makes all macros available for all modules in this crate.
6//!
7//! Macros that relate to manipulating the underlying mruby VM are exported.
8
9/// Log a fatal error to stderr and suppress all errors.
10///
11/// This macro accepts a format string and arguments similar to [`write!`].
12macro_rules! emit_fatal_warning {
13    ($($arg:tt)+) => {{
14        use ::std::io::Write;
15
16        // Something bad, terrible, and unexpected has happened.
17        //
18        // Suppress errors from logging to stderr because this function may being
19        // called when there are foreign C frames in the stack and panics are
20        // either undefined behavior or will result in an abort.
21        //
22        // Ensure the returned error is dropped so we don't leave anything on
23        // the stack in the event of a foreign unwind.
24        let maybe_err = ::std::write!(::std::io::stderr(), "fatal[artichoke-backend]: ");
25        drop(maybe_err);
26        let maybe_err = ::std::writeln!(::std::io::stderr(), $($arg)+);
27        drop(maybe_err);
28    }};
29}
30
31/// Extract an [`Artichoke`] instance from the userdata on a [`sys::mrb_state`].
32///
33/// If there is an error when extracting the Rust wrapper around the
34/// interpreter, return `nil` or a user-provided default.
35///
36/// This macro calls `unsafe` functions.
37///
38/// [`Artichoke`]: crate::Artichoke
39/// [`sys::mrb_state`]: crate::sys::mrb_state
40#[macro_export]
41macro_rules! unwrap_interpreter {
42    ($mrb:ident, to => $to:ident, or_else = ()) => {
43        let mrb = $mrb;
44        let mut interp = if let Ok(interp) = unsafe { $crate::ffi::from_user_data(mrb) } {
45            interp
46        } else {
47            return;
48        };
49        let mut arena = if let Ok(arena) =
50            $crate::gc::MrbGarbageCollection::create_arena_savepoint(&mut interp)
51        {
52            arena
53        } else {
54            return;
55        };
56        #[allow(unused_mut)]
57        let mut $to = $crate::Guard::new(arena.interp());
58    };
59    ($mrb:ident, to => $to:ident, or_else = $default:expr) => {
60        let mrb = $mrb;
61        let mut interp = if let Ok(interp) = unsafe { $crate::ffi::from_user_data(mrb) } {
62            interp
63        } else {
64            return $default;
65        };
66        let mut arena = if let Ok(arena) =
67            $crate::gc::MrbGarbageCollection::create_arena_savepoint(&mut interp)
68        {
69            arena
70        } else {
71            return $default;
72        };
73        #[allow(unused_mut)]
74        let mut $to = $crate::Guard::new(arena.interp());
75    };
76    ($mrb:ident, to => $to:ident) => {
77        unwrap_interpreter!($mrb, to => $to, or_else = $crate::sys::mrb_sys_nil_value())
78    };
79}
80
81#[doc(hidden)]
82pub mod argspec {
83    use std::ffi::CStr;
84
85    pub const NONE: &CStr = c"";
86    pub const REQ1: &CStr = c"o";
87    pub const OPT1: &CStr = c"|o";
88    pub const REQ1_OPT1: &CStr = c"o|o";
89    pub const REQ1_OPT2: &CStr = c"o|oo";
90    pub const REQ1_OPT3: &CStr = c"o|ooo";
91    pub const REQ1_REQBLOCK: &CStr = c"o&";
92    pub const REQ1_REQBLOCK_OPT1: &CStr = c"o&|o?";
93    pub const REQ1_REQBLOCK_OPT2: &CStr = c"o&|o?o?";
94    pub const REQ2: &CStr = c"oo";
95    pub const OPT2: &CStr = c"|oo";
96    pub const OPT2_OPTBLOCK: &CStr = c"&|o?o?";
97    pub const REQ2_OPT1: &CStr = c"oo|o";
98    pub const REST: &CStr = c"*";
99}
100
101/// Extract [`sys::mrb_value`]s from a [`sys::mrb_state`] to adapt a C
102/// entry point to a Rust implementation of a Ruby function.
103///
104/// This macro exists because the mruby VM [does not validate argspecs] attached
105/// to native functions.
106///
107/// This macro calls `unsafe` functions.
108///
109/// [`sys::mrb_value`]: crate::sys::mrb_value
110/// [`sys::mrb_state`]: crate::sys::mrb_state
111/// [does not validate argspecs]: https://github.com/mruby/mruby/issues/4688
112#[macro_export]
113macro_rules! mrb_get_args {
114    ($mrb:ident, none) => {{
115        let mrb = $mrb;
116        unsafe {
117            $crate::sys::mrb_get_args(mrb, $crate::macros::argspec::NONE.as_ptr());
118        }
119    }};
120    ($mrb:ident, required = 1) => {{
121        let mrb = $mrb;
122        unsafe {
123            let mut req1 = std::mem::MaybeUninit::<$crate::sys::mrb_value>::uninit();
124            let argc = $crate::sys::mrb_get_args(mrb, $crate::macros::argspec::REQ1.as_ptr(), req1.as_mut_ptr());
125            match argc {
126                1 => req1.assume_init(),
127                _ => unreachable!("mrb_get_args should have raised"),
128            }
129        }
130    }};
131    ($mrb:ident, optional = 1) => {{
132        let mrb = $mrb;
133        unsafe {
134            let mut opt1 = std::mem::MaybeUninit::<$crate::sys::mrb_value>::uninit();
135            let argc = $crate::sys::mrb_get_args(mrb, $crate::macros::argspec::OPT1.as_ptr(), opt1.as_mut_ptr());
136            match argc {
137                1 => {
138                    let opt1 = opt1.assume_init();
139                    Some(opt1)
140                }
141                0 => None,
142                _ => unreachable!("mrb_get_args should have raised"),
143            }
144        }
145    }};
146    ($mrb:ident, required = 1, optional = 1) => {{
147        let mrb = $mrb;
148        unsafe {
149            let mut req1 = std::mem::MaybeUninit::<$crate::sys::mrb_value>::uninit();
150            let mut opt1 = std::mem::MaybeUninit::<$crate::sys::mrb_value>::uninit();
151            let argc = $crate::sys::mrb_get_args(
152                mrb,
153                $crate::macros::argspec::REQ1_OPT1.as_ptr(),
154                req1.as_mut_ptr(),
155                opt1.as_mut_ptr(),
156            );
157            match argc {
158                2 => {
159                    let req1 = req1.assume_init();
160                    let opt1 = opt1.assume_init();
161                    (req1, Some(opt1))
162                }
163                1 => {
164                    let req1 = req1.assume_init();
165                    (req1, None)
166                }
167                _ => unreachable!("mrb_get_args should have raised"),
168            }
169        }
170    }};
171    ($mrb:ident, required = 1, optional = 2) => {{
172        let mrb = $mrb;
173        unsafe {
174            let mut req1 = std::mem::MaybeUninit::<$crate::sys::mrb_value>::uninit();
175            let mut opt1 = std::mem::MaybeUninit::<$crate::sys::mrb_value>::uninit();
176            let mut opt2 = std::mem::MaybeUninit::<$crate::sys::mrb_value>::uninit();
177            let argc = $crate::sys::mrb_get_args(
178                mrb,
179                $crate::macros::argspec::REQ1_OPT2.as_ptr(),
180                req1.as_mut_ptr(),
181                opt1.as_mut_ptr(),
182                opt2.as_mut_ptr(),
183            );
184            match argc {
185                3 => {
186                    let req1 = req1.assume_init();
187                    let opt1 = opt1.assume_init();
188                    let opt2 = opt2.assume_init();
189                    (req1, Some(opt1), Some(opt2))
190                }
191                2 => {
192                    let req1 = req1.assume_init();
193                    let opt1 = opt1.assume_init();
194                    (req1, Some(opt1), None)
195                }
196                1 => {
197                    let req1 = req1.assume_init();
198                    (req1, None, None)
199                }
200                _ => unreachable!("mrb_get_args should have raised"),
201            }
202        }
203    }};
204    ($mrb:ident, required = 1, optional = 3) => {{
205        let mrb = $mrb;
206        unsafe {
207            let mut req1 = std::mem::MaybeUninit::<$crate::sys::mrb_value>::uninit();
208            let mut opt1 = std::mem::MaybeUninit::<$crate::sys::mrb_value>::uninit();
209            let mut opt2 = std::mem::MaybeUninit::<$crate::sys::mrb_value>::uninit();
210            let mut opt3 = std::mem::MaybeUninit::<$crate::sys::mrb_value>::uninit();
211            let argc = $crate::sys::mrb_get_args(
212                mrb,
213                $crate::macros::argspec::REQ1_OPT3.as_ptr(),
214                req1.as_mut_ptr(),
215                opt1.as_mut_ptr(),
216                opt2.as_mut_ptr(),
217                opt3.as_mut_ptr(),
218            );
219            match argc {
220                4 => {
221                    let req1 = req1.assume_init();
222                    let opt1 = opt1.assume_init();
223                    let opt2 = opt2.assume_init();
224                    let opt3 = opt3.assume_init();
225                    (req1, Some(opt1), Some(opt2), Some(opt3))
226                }
227                3 => {
228                    let req1 = req1.assume_init();
229                    let opt1 = opt1.assume_init();
230                    let opt2 = opt2.assume_init();
231                    (req1, Some(opt1), Some(opt2), None)
232                }
233                2 => {
234                    let req1 = req1.assume_init();
235                    let opt1 = opt1.assume_init();
236                    (req1, Some(opt1), None, None)
237                }
238                1 => {
239                    let req1 = req1.assume_init();
240                    (req1, None, None, None)
241                }
242                _ => unreachable!("mrb_get_args should have raised"),
243            }
244        }
245    }};
246    ($mrb:ident, required = 1, &block) => {{
247        let mrb = $mrb;
248        unsafe {
249            let mut req1 = std::mem::MaybeUninit::<$crate::sys::mrb_value>::uninit();
250            let mut block = std::mem::MaybeUninit::<$crate::sys::mrb_value>::uninit();
251            let argc = $crate::sys::mrb_get_args(
252                mrb,
253                $crate::macros::argspec::REQ1_REQBLOCK.as_ptr(),
254                req1.as_mut_ptr(),
255                block.as_mut_ptr(),
256            );
257            match argc {
258                2 | 1 => {
259                    let req1 = req1.assume_init();
260                    let block = block.assume_init();
261                    (req1, $crate::block::Block::new(block))
262                }
263                _ => unreachable!("mrb_get_args should have raised"),
264            }
265        }
266    }};
267    ($mrb:ident, required = 1, optional = 1, &block) => {{
268        let mrb = $mrb;
269        unsafe {
270            let mut req1 = std::mem::MaybeUninit::<$crate::sys::mrb_value>::uninit();
271            let mut opt1 = std::mem::MaybeUninit::<$crate::sys::mrb_value>::uninit();
272            let mut has_opt1 = std::mem::MaybeUninit::<$crate::sys::mrb_bool>::uninit();
273            let mut block = std::mem::MaybeUninit::<$crate::sys::mrb_value>::uninit();
274            let argc = $crate::sys::mrb_get_args(
275                mrb,
276                $crate::macros::argspec::REQ1_REQBLOCK_OPT1.as_ptr(),
277                req1.as_mut_ptr(),
278                block.as_mut_ptr(),
279                opt1.as_mut_ptr(),
280                has_opt1.as_mut_ptr(),
281            );
282            let has_opt1 = has_opt1.assume_init();
283            match argc {
284                3 => {
285                    let req1 = req1.assume_init();
286                    let opt1 = opt1.assume_init();
287                    let block = block.assume_init();
288                    (req1, Some(opt1), $crate::block::Block::new(block))
289                }
290                2 => {
291                    let req1 = req1.assume_init();
292                    let opt1 = if has_opt1 { Some(opt1.assume_init()) } else { None };
293                    let block = block.assume_init();
294                    (req1, opt1, $crate::block::Block::new(block))
295                }
296                1 => {
297                    let req1 = req1.assume_init();
298                    let block = block.assume_init();
299                    (req1, None, $crate::block::Block::new(block))
300                }
301                _ => unreachable!("mrb_get_args should have raised"),
302            }
303        }
304    }};
305    ($mrb:ident, required = 1, optional = 2, &block) => {{
306        let mrb = $mrb;
307        unsafe {
308            let mut req1 = std::mem::MaybeUninit::<$crate::sys::mrb_value>::uninit();
309            let mut opt1 = std::mem::MaybeUninit::<$crate::sys::mrb_value>::uninit();
310            let mut has_opt1 = std::mem::MaybeUninit::<$crate::sys::mrb_bool>::uninit();
311            let mut opt2 = std::mem::MaybeUninit::<$crate::sys::mrb_value>::uninit();
312            let mut has_opt2 = std::mem::MaybeUninit::<$crate::sys::mrb_bool>::uninit();
313            let mut block = std::mem::MaybeUninit::<$crate::sys::mrb_value>::uninit();
314            let argc = $crate::sys::mrb_get_args(
315                mrb,
316                $crate::macros::argspec::REQ1_REQBLOCK_OPT2.as_ptr(),
317                req1.as_mut_ptr(),
318                block.as_mut_ptr(),
319                opt1.as_mut_ptr(),
320                has_opt1.as_mut_ptr(),
321                opt2.as_mut_ptr(),
322                has_opt2.as_mut_ptr(),
323            );
324            let has_opt1 = has_opt1.assume_init();
325            let has_opt2 = has_opt2.assume_init();
326            match argc {
327                4 => {
328                    let req1 = req1.assume_init();
329                    let opt1 = if has_opt1 { Some(opt1.assume_init()) } else { None };
330                    let opt2 = if has_opt2 { Some(opt2.assume_init()) } else { None };
331                    let block = block.assume_init();
332                    (req1, opt1, opt2, $crate::block::Block::new(block))
333                }
334                3 => {
335                    let req1 = req1.assume_init();
336                    let opt1 = if has_opt1 { Some(opt1.assume_init()) } else { None };
337                    let opt2 = if has_opt2 { Some(opt2.assume_init()) } else { None };
338                    let block = block.assume_init();
339                    (req1, opt1, opt2, $crate::block::Block::new(block))
340                }
341                2 => {
342                    let req1 = req1.assume_init();
343                    let opt1 = if has_opt1 { Some(opt1.assume_init()) } else { None };
344                    let block = block.assume_init();
345                    (req1, opt1, None, $crate::block::Block::new(block))
346                }
347                1 => {
348                    let req1 = req1.assume_init();
349                    let block = block.assume_init();
350                    (req1, None, None, $crate::block::Block::new(block))
351                }
352                _ => unreachable!("mrb_get_args should have raised"),
353            }
354        }
355    }};
356    ($mrb:ident, required = 2) => {{
357        let mrb = $mrb;
358        unsafe {
359            let mut req1 = std::mem::MaybeUninit::<$crate::sys::mrb_value>::uninit();
360            let mut req2 = std::mem::MaybeUninit::<$crate::sys::mrb_value>::uninit();
361            let argc = $crate::sys::mrb_get_args(
362                mrb,
363                $crate::macros::argspec::REQ2.as_ptr(),
364                req1.as_mut_ptr(),
365                req2.as_mut_ptr(),
366            );
367            match argc {
368                2 => {
369                    let req1 = req1.assume_init();
370                    let req2 = req2.assume_init();
371                    (req1, req2)
372                }
373                _ => unreachable!("mrb_get_args should have raised"),
374            }
375        }
376    }};
377    ($mrb:ident, optional = 2) => {{
378        let mrb = $mrb;
379        unsafe {
380            let mut opt1 = std::mem::MaybeUninit::<$crate::sys::mrb_value>::uninit();
381            let mut opt2 = std::mem::MaybeUninit::<$crate::sys::mrb_value>::uninit();
382            let argc = $crate::sys::mrb_get_args(
383                mrb,
384                $crate::macros::argspec::OPT2.as_ptr(),
385                opt1.as_mut_ptr(),
386                opt2.as_mut_ptr(),
387            );
388            match argc {
389                2 => {
390                    let opt1 = opt1.assume_init();
391                    let opt2 = opt2.assume_init();
392                    (Some(opt1), Some(opt2))
393                }
394                1 => {
395                    let opt1 = opt1.assume_init();
396                    (Some(opt1), None)
397                }
398                0 => (None, None),
399                _ => unreachable!("mrb_get_args should have raised"),
400            }
401        }
402    }};
403    ($mrb:ident, optional = 2, &block) => {{
404        let mrb = $mrb;
405        unsafe {
406            let mut opt1 = std::mem::MaybeUninit::<$crate::sys::mrb_value>::uninit();
407            let mut has_opt1 = std::mem::MaybeUninit::<$crate::sys::mrb_bool>::uninit();
408            let mut opt2 = std::mem::MaybeUninit::<$crate::sys::mrb_value>::uninit();
409            let mut has_opt2 = std::mem::MaybeUninit::<$crate::sys::mrb_bool>::uninit();
410            let mut block = std::mem::MaybeUninit::<$crate::sys::mrb_value>::uninit();
411            $crate::sys::mrb_get_args(
412                mrb,
413                $crate::macros::argspec::OPT2_OPTBLOCK.as_ptr(),
414                block.as_mut_ptr(),
415                opt1.as_mut_ptr(),
416                has_opt1.as_mut_ptr(),
417                opt2.as_mut_ptr(),
418                has_opt2.as_mut_ptr(),
419            );
420            let has_opt1 = has_opt1.assume_init();
421            let has_opt2 = has_opt2.assume_init();
422            let opt1 = if has_opt1 { Some(opt1.assume_init()) } else { None };
423            let opt2 = if has_opt2 { Some(opt2.assume_init()) } else { None };
424            let block = block.assume_init();
425            (opt1, opt2, $crate::block::Block::new(block))
426        }
427    }};
428    ($mrb:ident, required = 2, optional = 1) => {{
429        let mrb = $mrb;
430        unsafe {
431            let mut req1 = std::mem::MaybeUninit::<$crate::sys::mrb_value>::uninit();
432            let mut req2 = std::mem::MaybeUninit::<$crate::sys::mrb_value>::uninit();
433            let mut opt1 = std::mem::MaybeUninit::<$crate::sys::mrb_value>::uninit();
434            let argc = $crate::sys::mrb_get_args(
435                mrb,
436                $crate::macros::argspec::REQ2_OPT1.as_ptr(),
437                req1.as_mut_ptr(),
438                req2.as_mut_ptr(),
439                opt1.as_mut_ptr(),
440            );
441            match argc {
442                3 => {
443                    let req1 = req1.assume_init();
444                    let req2 = req2.assume_init();
445                    let opt1 = opt1.assume_init();
446                    (req1, req2, Some(opt1))
447                }
448                2 => {
449                    let req1 = req1.assume_init();
450                    let req2 = req2.assume_init();
451                    (req1, req2, None)
452                }
453                _ => unreachable!("mrb_get_args should have raised"),
454            }
455        }
456    }};
457    ($mrb:ident, *args) => {{
458        let mrb = $mrb;
459        unsafe {
460            let mut args = std::mem::MaybeUninit::<*const $crate::sys::mrb_value>::uninit();
461            let mut count = std::mem::MaybeUninit::<usize>::uninit();
462            let _argc = $crate::sys::mrb_get_args(
463                mrb,
464                $crate::macros::argspec::REST.as_ptr(),
465                args.as_mut_ptr(),
466                count.as_mut_ptr(),
467            );
468            let args = args.assume_init();
469            let count = count.assume_init();
470            if args.is_null() || count == 0 {
471                &[]
472            } else {
473                std::slice::from_raw_parts(args, count)
474            }
475        }
476    }};
477}