Skip to main content

fre_rs/
macros.rs

1#[allow(unused_imports)]
2use super::*;
3
4
5/// 
6/// Generates and links the required Flash Runtime Extension entry points and lifecycle hooks,
7/// bridging the C ABI with safe Rust abstractions.
8/// 
9/// This macro accepts up to four optional function arguments:
10/// [`Initializer`], [`Finalizer`], [`ContextInitializer`], and [`ContextFinalizer`],
11/// along with two external symbols for `extern "C"` functions.
12/// 
13/// ## Full Example
14/// ```
15/// mod lib {
16///     use fre_rs::prelude::*;
17///     fre_rs::extension! {
18///         extern symbol_initializer, symbol_finalizer;
19///         move initializer, final finalizer;
20///         gen context_initializer, final context_finalizer;
21///     }
22///     struct ExtensionData (i32);
23///     impl Data for ExtensionData {}
24///     fn initializer() -> Option<Box<dyn Any>> {Some(ExtensionData(0).into_boxed())}
25///     fn finalizer(ext_data: Option<Box<dyn Any>>) {ExtensionData::from_boxed(ext_data.unwrap()).unwrap().0 = -1;}
26///     fn context_initializer(frt: &FlashRuntime) -> FunctionSet {
27///         let mut funcs = FunctionSet::new();
28///         let ctx_type = unsafe {frt.current_context().with(|ctx_reg| {
29///             ctx_reg.context_type()
30///         })}.unwrap();
31///         if ctx_type.is_some() {
32///             funcs.add(None, None::<()>, method_name);
33///         } else {
34///             funcs.add(None, None::<()>, method_name2);
35///         }
36///         return funcs;
37///     }
38///     fn context_finalizer(frt: &FlashRuntime) {frt.current_context().set_actionscript_data(null).unwrap()}
39///     fn method_implementation <'a> (frt: &FlashRuntime<'a>, data: Option<&mut dyn Any>, args: &[Object<'a>]) -> Object<'a> {null}
40///     fre_rs::function! (method_name use method_implementation);
41///     fre_rs::function! {
42///         method_name2 (_, _, _) -> Option<Array> {None}
43///     }
44/// }
45/// ```
46/// ## Minimal Example
47/// ```
48/// mod lib {
49///     use fre_rs::prelude::*;
50///     fre_rs::extension! {
51///         extern symbol_initializer;
52///         gen context_initializer, final;
53///     }
54///     fn context_initializer(frt: &FlashRuntime) -> FunctionSet {
55///         let mut funcs = FunctionSet::new();
56///         funcs.add(None, None::<()>, method_name);
57///         funcs
58///     }
59///     fre_rs::function! {
60///         method_name (frt, _, args) -> StringObject {
61///             frt.trace(args);
62///             StringObject::new(frt, "Hello! Flash Runtime")
63///         }
64///     }
65/// }
66/// ```
67#[macro_export]
68macro_rules! extension {
69    {// #0
70        extern $symbol_initializer:ident $(, $symbol_finalizer:ident;
71        move $initializer:path, final $($finalizer:path)?)?;
72        gen $context_initializer:path, final $($context_finalizer:path)?;
73    } => {
74        const _: () = {
75        mod _flash_runtime_extension {
76            use super::*;
77            $crate::extension! {@Extern [$symbol_initializer $(, $symbol_finalizer, $initializer $(, $finalizer)?)?]}
78            #[allow(unsafe_op_in_unsafe_fn)]
79            unsafe extern "C" fn ctx_initializer (
80                ext_data: $crate::c::markers::FREData,
81                ctx_type: $crate::c::markers::FREStr,
82                ctx: $crate::c::markers::FREContext,
83                num_funcs_to_set: *mut u32,
84                funcs_to_set: *mut *const $crate::c::ffi::FRENamedFunction,
85            ) {
86                let context_initializer: $crate::function::ContextInitializer = $context_initializer;
87                $crate::context::FlashRuntime::with_context_initializer(ext_data, ctx_type, &ctx, num_funcs_to_set, funcs_to_set, $context_initializer);
88            }
89            #[allow(unsafe_op_in_unsafe_fn)]
90            unsafe extern "C" fn ctx_finalizer (ctx: $crate::c::markers::FREContext) {
91                $crate::context::FlashRuntime::with(&ctx, |frt| {
92                    $(
93                        let context_finalizer: $crate::function::ContextFinalizer = $context_finalizer;
94                        context_finalizer(frt);
95                    )?
96                    let raw = $crate::validated::NonNullFREData::new(frt.current_context().get_native_data().expect("Failed to retrieve native data from FFI.")).expect("`ContextRegistry` is expected in native data but is missing.");
97                    assert!(<$crate::context::ContextRegistry as $crate::data::Data>::ref_from(raw).is_ok());
98                    $crate::data::drop_from(raw);
99                });
100
101            }
102        }
103        };
104    };
105    {// #1``
106        @Extern [$symbol_initializer:ident, $symbol_finalizer:ident, $initializer:path $(, $finalizer:path)?]
107    } => {
108        #[allow(unsafe_op_in_unsafe_fn, non_snake_case)]
109        #[unsafe(no_mangle)]
110        pub unsafe extern "C" fn $symbol_initializer (
111            ext_data_to_set: *mut $crate::c::markers::FREData,
112            ctx_initializer_to_set: *mut $crate::c::ffi::FREContextInitializer,
113            ctx_finalizer_to_set: *mut $crate::c::ffi::FREContextFinalizer,
114        ) {
115            assert!(!ext_data_to_set.is_null());
116            assert!(!ctx_initializer_to_set.is_null());
117            assert!(!ctx_finalizer_to_set.is_null());
118            let initializer: $crate::function::Initializer = $initializer;
119            if let Some(ext_data) = initializer() {
120                *ext_data_to_set = $crate::data::into_raw(ext_data).as_ptr();
121            }
122            *ctx_initializer_to_set = ctx_initializer;
123            *ctx_finalizer_to_set = Some(ctx_finalizer);
124            $crate::extension! (@Hook);
125        }
126        #[allow(unsafe_op_in_unsafe_fn, non_snake_case)]
127        #[unsafe(no_mangle)]
128        pub unsafe extern "C" fn $symbol_finalizer (ext_data: $crate::c::markers::FREData) {
129            let ext_data = $crate::validated::NonNullFREData::new(ext_data)
130                .map(|raw| $crate::data::from_raw(raw));
131            $(
132                let finalizer: $crate::function::Finalizer = $finalizer;
133                finalizer(ext_data);
134            )?
135        }
136    };
137    {// #2
138        @Extern [$symbol_initializer:ident]
139    } => {
140        #[allow(unsafe_op_in_unsafe_fn, non_snake_case)]
141        #[unsafe(no_mangle)]
142        pub unsafe extern "C" fn $symbol_initializer (
143            ext_data_to_set: *mut $crate::c::markers::FREData,
144            ctx_initializer_to_set: *mut $crate::c::ffi::FREContextInitializer,
145            ctx_finalizer_to_set: *mut $crate::c::ffi::FREContextFinalizer,
146        ) {
147            assert!(!ext_data_to_set.is_null());
148            assert!(!ctx_initializer_to_set.is_null());
149            assert!(!ctx_finalizer_to_set.is_null());
150            *ctx_initializer_to_set = ctx_initializer;
151            *ctx_finalizer_to_set = Some(ctx_finalizer);
152            $crate::extension! (@Hook);
153        }
154    };
155    (// #3
156        @Hook
157    ) => {
158        static INIT_HOOK: ::std::sync::Once = ::std::sync::Once::new();
159        INIT_HOOK.call_once(|| {
160            let default_hook = ::std::panic::take_hook();
161            ::std::panic::set_hook(Box::new(move |info| {
162                let payload = info.payload_as_str().unwrap_or_default();
163                let location = info.location()
164                    .map(|l| format!("at {}:{}:{}", l.file(), l.line(), l.column()))
165                    .unwrap_or_default();
166                let backtrace = ::std::backtrace::Backtrace::force_capture();
167                let s = format!("{payload}\n{location}\n{backtrace}");
168                $crate::_internal::LAST_PANIC_INFO.with(|i| {*i.borrow_mut() = Some(s);});
169                default_hook(info);
170            }));
171        });
172    }
173}
174
175
176/// 
177/// Defines a function intended for context registration by generating its
178/// ABI-compatible wrapper and binding it to a Rust implementation.
179///
180/// Expands to a `&'static` constant of type [`FunctionDefinition`],
181/// intended to be added to a [`FunctionSet`].
182/// 
183/// # Panic Handling
184/// Any [`panic`] occurring within the function body is intercepted via
185/// [`std::panic::catch_unwind`]. Instead of unwinding across the FFI boundary,
186/// an [`ErrorObject`] containing the captured panic details is constructed and
187/// returned to the Flash Runtime.
188///
189/// This fallback return value is **NOT** constrained by the return type
190/// declared in the macro invocation. On the ActionScript side, the result may
191/// either be expected and handled as an `Error`, or not. In the latter case,
192/// if an [`ErrorObject`] is returned, casting it to a non-error type yields
193/// `null` and may lead to runtime exceptions.
194///
195/// When the [`ErrorObject`] is properly handled, the Flash Runtime remains
196/// stable. However, care must be taken to avoid leaving the native extension
197/// in an inconsistent state; resources should be managed reliably even in the
198/// presence of panics.
199/// 
200/// ## Full Example
201/// ```
202/// mod lib {
203///     use fre_rs::prelude::*;
204///     fre_rs::function! {
205///         method_name (frt, data, args) -> Option<Object> {
206///             return frt.current_context().get_actionscript_data().ok();
207///         }
208///     }
209///     fre_rs::function! (method_name2 use method_implementation);
210///     fn method_implementation <'a> (frt: &FlashRuntime<'a>, data: Option<&mut dyn Any>, args: &[Object<'a>]) -> Object<'a> {null}
211/// }
212/// ```
213/// ## Minimal Example
214/// ```
215/// mod lib {
216///     fre_rs::function! {
217///         method_name (_, _, _) {}
218///     }
219/// }
220/// ```
221#[macro_export]
222macro_rules! function {
223    {// #0
224        $name:ident ($($arguments:tt)+) $(-> $return_type:ty)? $body:block
225    } => {
226        #[allow(non_upper_case_globals)]
227        pub const $name: &'static $crate::function::FunctionDefinition = & $crate::function::FunctionDefinition::new(
228            $crate::function!(@Name $name), {
229            #[allow(unsafe_op_in_unsafe_fn)]
230            unsafe extern "C" fn abi(
231                ctx: $crate::c::markers::FREContext,
232                func_data: $crate::c::markers::FREData,
233                argc: u32,
234                argv: *const $crate::c::markers::FREObject,
235            ) -> $crate::c::markers::FREObject {
236                fn func <'a> (
237                    frt: &$crate::context::FlashRuntime<'a>,
238                    func_data: Option<&mut dyn ::std::any::Any>,
239                    args: &[$crate::types::Object<'a>],
240                ) -> $crate::types::Object<'a> {
241                    $crate::function! {@Parameters [frt, func_data, args] $($arguments)+}
242                    let r = ::std::panic::catch_unwind(|| -> $crate::function!(@Return $($return_type)?) {
243                        $body
244                    });
245                    $crate::function! (@Unwind [frt, r])
246                }
247                $crate::context::FlashRuntime::with_method(&ctx, func_data, argc, argv, func)
248            }
249            abi},
250        );
251    };
252    (// #1
253        $name:ident use $func:path
254    ) => {
255        #[allow(non_upper_case_globals)]
256        pub const $name: &'static $crate::function::FunctionDefinition = & $crate::function::FunctionDefinition::new(
257            $crate::function!(@Name $name), {
258            #[allow(unsafe_op_in_unsafe_fn)]
259            unsafe extern "C" fn abi(
260                ctx: $crate::c::markers::FREContext,
261                func_data: $crate::c::markers::FREData,
262                argc: u32,
263                argv: *const $crate::c::markers::FREObject,
264            ) -> $crate::c::markers::FREObject {
265                let func: $crate::function::Function = $func;
266                let r = ::std::panic::catch_unwind(|| -> $crate::c::markers::FREObject {
267                    $crate::context::FlashRuntime::with_method(&ctx, func_data, argc, argv, func)
268                });
269                let frt: $crate::context::FlashRuntime = ::std::mem::transmute(ctx);
270                $crate::function! (@Unwind [&frt, r])
271            }
272            abi},
273        );
274    };
275    (// #2
276        @Name $name:ident
277    ) => {
278        unsafe {
279            let s: &'static str = concat!(stringify!($name), "\0");
280            let s: &'static ::std::ffi::CStr = ::std::ffi::CStr::from_bytes_with_nul_unchecked(s.as_bytes());
281            let s = $crate::validated::UCStr::from_literal_unchecked(s);
282            s
283        }
284    };
285    (// #3
286        @Return $return_type:ty
287    ) => ($return_type); 
288    (// #4
289        @Return
290    ) => (());
291    {// #5
292        @Parameters [$f:ident, $d:ident, $a:ident $(,)?]
293        $frt:ident, $data:ident, $args:ident $(,)?
294    } => {
295        let $frt: &$crate::context::FlashRuntime<'a> = $f;
296        let $data: Option<&mut dyn ::std::any::Any> = $d;
297        let $args: &[$crate::types::Object<'a>] = $a;
298    };
299    {// #6
300        @Parameters [$f:ident, $d:ident, $a:ident $(,)?]
301        $frt:ident, $data:ident, _ $(,)?
302    } => {
303        $crate::function! {@Parameters [$f, $d, $a]
304            $frt, $data, _args
305        }
306    };
307    {// #7
308        @Parameters [$f:ident, $d:ident, $a:ident $(,)?]
309        $frt:ident, _, $args:ident $(,)?
310    } => {
311        $crate::function! {@Parameters [$f, $d, $a]
312            $frt, _data, $args
313        }
314    };
315    {// #8
316        @Parameters [$f:ident, $d:ident, $a:ident $(,)?]
317        _, $data:ident, $args:ident $(,)?
318    } => {
319        $crate::function! {@Parameters [$f, $d, $a]
320            _frt, $data, $args
321        }
322    };
323    {// #9
324        @Parameters [$f:ident, $d:ident, $a:ident $(,)?]
325        _, _, $args:ident $(,)?
326    } => {
327        $crate::function! {@Parameters [$f, $d, $a]
328            _frt, _data, $args
329        }
330    };
331    {// #10
332        @Parameters [$f:ident, $d:ident, $a:ident $(,)?]
333        _, $data:ident, _ $(,)?
334    } => {
335        $crate::function! {@Parameters [$f, $d, $a]
336            _frt, $data, _args
337        }
338    };
339    {// #11
340        @Parameters [$f:ident, $d:ident, $a:ident $(,)?]
341        $frt:ident, _, _ $(,)?
342    } => {
343        $crate::function! {@Parameters [$f, $d, $a]
344            $frt, _data, _args
345        }
346    };
347    {// #12
348        @Parameters [$f:ident, $d:ident, $a:ident $(,)?]
349        _, _, _ $(,)?
350    } => {
351        $crate::function! {@Parameters [$f, $d, $a]
352            _frt, _data, _args
353        }
354    };
355    (// #13
356        @Unwind [$frt:expr_2021, $catched:expr_2021]
357    ) => {
358        match $catched {
359            Ok(r) => r.into(),
360            Err(_) => {
361                let info = $crate::_internal::LAST_PANIC_INFO.with(|i| {i.borrow_mut().take()});
362                let msg = info.as_ref()
363                    .map(|s|s.as_str())
364                    .unwrap_or("Panic occurred but no details were captured.");
365                let err = $crate::types::ErrorObject::new($frt, Some(msg), i32::MIN);
366                err.set_name(Some($crate::types::StringObject::new($frt, "Native Extension Panic Error")));
367                err.into()
368            },
369        }
370    }
371}
372