Skip to main content

fre_rs/
macros.rs

1#[allow(unused_imports)]
2use super::*;
3
4
5/// Generates and links the required Flash Runtime Extension entry points and lifecycle hooks,
6/// bridging the C ABI with safe Rust abstractions.
7/// 
8/// This macro accepts two external symbols as arguments for `extern "C"` functions,
9/// and four functions: [`Initializer`], [`Finalizer`], [`ContextInitializer`], [`ContextFinalizer`].
10/// Some of these arguments are optional.
11/// 
12/// ## Full Example
13/// ```
14/// mod lib {
15///     use fre_rs::prelude::*;
16///     fre_rs::extension! {
17///         extern symbol_initializer, symbol_finalizer;
18///         move initializer, final finalizer;
19///         gen context_initializer, final context_finalizer;
20///     }
21///     struct NativeData (i32);
22///     impl Data for NativeData {}
23///     fn initializer() -> Option<Box<dyn Any>> {
24///         let extension_data = NativeData(-3).into_boxed();
25///         return Some(extension_data);
26///     }
27///     fn finalizer(ext_data: Option<Box<dyn Any>>) {
28///         assert_eq!(NativeData::from_boxed(ext_data.unwrap()).unwrap().0, -3);
29///     }
30///     fn context_initializer(ctx: &CurrentContext) -> (Option<Box<dyn Any>>, FunctionSet) {
31///         let context_data = NativeData(-2).into_boxed();
32///         let mut funcs = FunctionSet::with_capacity(1);
33///         let function_data = NativeData(-1).into_boxed();
34///         if ctx.ty().is_some() {
35///             funcs.add(None, Some(function_data), method_name);
36///         } else {
37///             funcs.add(None, Some(function_data), method_name2);
38///         }
39///         return (Some(context_data), funcs);
40///     }
41///     fn context_finalizer(ctx: &CurrentContext) {
42///         let context_data: &NativeData = ctx.data().unwrap().downcast_ref().unwrap();
43///         assert_eq!(context_data.0, -2);
44///         ctx.set_actionscript_data(as3::null)
45///     }
46///     fn method_implementation <'a> (ctx: &CurrentContext<'a>, data: Option<&mut dyn Any>, args: &[as3::Object<'a>]) -> as3::Object<'a> {as3::null}
47///     fre_rs::function! (method_name use method_implementation);
48///     fre_rs::function! {
49///         method_name2 (ctx, data, args) -> Option<as3::Array> {
50///             let function_data: &mut NativeData = data.unwrap().downcast_mut().unwrap();
51///             assert_eq!(function_data.0, -1);
52///             return None;
53///         }
54///     }
55/// }
56/// ```
57/// ## Minimal Example
58/// ```
59/// mod lib {
60///     use fre_rs::prelude::*;
61///     fre_rs::extension! {
62///         extern symbol_initializer;
63///         gen context_initializer, final;
64///     }
65///     fn context_initializer(_: &CurrentContext) -> (Option<Box<dyn Any>>, FunctionSet) {
66///         let mut funcs = FunctionSet::new();
67///         funcs.add(None, None, method_name);
68///         (None, funcs)
69///     }
70///     fre_rs::function! {
71///         method_name (ctx, _, args) -> as3::String {
72///             ctx.trace(args);
73///             as3::String::new(ctx, "Hello! Flash Runtime.")
74///         }
75///     }
76/// }
77/// ```
78/// 
79#[macro_export]
80macro_rules! extension {
81    {// #0
82        extern $symbol_initializer:ident $(, $symbol_finalizer:ident;
83        move $initializer:path, final $($finalizer:path)?)?;
84        gen $context_initializer:path, final $($context_finalizer:path)?;
85    } => {
86        const _: () = {
87        mod _flash_runtime_extension {
88            use super::*;
89            $crate::extension! {@Extern [$symbol_initializer $(, $symbol_finalizer, $initializer $(, $finalizer)?)?]}
90            #[allow(unsafe_op_in_unsafe_fn)]
91            unsafe extern "C" fn ctx_initializer (
92                ext_data: $crate::c::markers::FREData,
93                ctx_type: $crate::c::markers::FREStr,
94                ctx: $crate::c::markers::FREContext,
95                num_funcs_to_set: *mut u32,
96                funcs_to_set: *mut *const $crate::c::ffi::FRENamedFunction,
97            ) {
98                let context_initializer: $crate::function::ContextInitializer = $context_initializer;
99                $crate::context::CurrentContext::with_context_initializer(ext_data, ctx_type, &ctx, num_funcs_to_set, funcs_to_set, $context_initializer);
100            }
101            #[allow(unsafe_op_in_unsafe_fn)]
102            unsafe extern "C" fn ctx_finalizer (ctx: $crate::c::markers::FREContext) {
103                $crate::context::CurrentContext::with(&ctx, |ctx| {
104                    $(
105                        let context_finalizer: $crate::function::ContextFinalizer = $context_finalizer;
106                        context_finalizer(ctx);
107                    )?
108                    let ctx = *(ctx as *const $crate::context::CurrentContext as *const $crate::context::ForeignContext);
109                    let raw = ctx
110                        .get_native_data()
111                        .expect("Failed to retrieve native data from FFI.")
112                        .expect("`ContextRegistry` is expected in native data but is missing.");
113                    assert!(<::std::cell::RefCell<$crate::context::ContextRegistry> as $crate::data::Data>::ref_from(raw).is_ok());
114                    $crate::data::drop_from(raw);
115                });
116
117            }
118        }
119        };
120    };
121    {// #1
122        @Extern [$symbol_initializer:ident, $symbol_finalizer:ident, $initializer:path $(, $finalizer:path)?]
123    } => {
124        #[allow(unsafe_op_in_unsafe_fn, non_snake_case)]
125        #[unsafe(no_mangle)]
126        pub unsafe extern "C" fn $symbol_initializer (
127            ext_data_to_set: *mut $crate::c::markers::FREData,
128            ctx_initializer_to_set: *mut $crate::c::ffi::FREContextInitializer,
129            ctx_finalizer_to_set: *mut $crate::c::ffi::FREContextFinalizer,
130        ) {
131            assert!(!ext_data_to_set.is_null());
132            assert!(!ctx_initializer_to_set.is_null());
133            assert!(!ctx_finalizer_to_set.is_null());
134            let initializer: $crate::function::Initializer = $initializer;
135            if let Some(ext_data) = initializer() {
136                let ext_data = ::std::sync::Arc::new(::std::sync::Mutex::new(ext_data));
137                *ext_data_to_set = <::std::sync::Arc<::std::sync::Mutex<Box<dyn ::std::any::Any>>> as $crate::data::Data>::into_raw(ext_data).as_ptr();
138            }
139            *ctx_initializer_to_set = ctx_initializer;
140            *ctx_finalizer_to_set = Some(ctx_finalizer);
141        }
142        #[allow(unsafe_op_in_unsafe_fn, non_snake_case)]
143        #[unsafe(no_mangle)]
144        pub unsafe extern "C" fn $symbol_finalizer (ext_data: $crate::c::markers::FREData) {
145            let ext_data = $crate::validated::NonNullFREData::new(ext_data)
146                .map(|raw| {
147                    let arc_mutex_boxed = <::std::sync::Arc<::std::sync::Mutex<Box<dyn ::std::any::Any>>> as $crate::data::Data>::from_raw(raw);
148                    let mutex = ::std::sync::Arc::try_unwrap(arc_mutex_boxed).expect("INVARIANT: No context exists.");
149                    let boxed = mutex.into_inner().expect("The mutex is poisoned.");
150                    boxed
151                });
152            $(
153                let finalizer: $crate::function::Finalizer = $finalizer;
154                finalizer(ext_data);
155            )?
156        }
157    };
158    {// #2
159        @Extern [$symbol_initializer:ident]
160    } => {
161        #[allow(unsafe_op_in_unsafe_fn, non_snake_case)]
162        #[unsafe(no_mangle)]
163        pub unsafe extern "C" fn $symbol_initializer (
164            ext_data_to_set: *mut $crate::c::markers::FREData,
165            ctx_initializer_to_set: *mut $crate::c::ffi::FREContextInitializer,
166            ctx_finalizer_to_set: *mut $crate::c::ffi::FREContextFinalizer,
167        ) {
168            assert!(!ext_data_to_set.is_null());
169            assert!(!ctx_initializer_to_set.is_null());
170            assert!(!ctx_finalizer_to_set.is_null());
171            *ctx_initializer_to_set = ctx_initializer;
172            *ctx_finalizer_to_set = Some(ctx_finalizer);
173        }
174    };
175}
176
177
178/// Defines a function intended for context registration by generating its
179/// ABI-compatible wrapper and binding it to a Rust implementation.
180///
181/// Expands to a `&'static` constant of type [`FunctionImplementation`],
182/// intended to be added to a [`FunctionSet`].
183/// 
184/// This macro supports two forms:
185/// one that defines a function inline, and another that binds to an existing
186/// [`Function`] using the `use` keyword.
187///
188/// For larger or more complex implementations, the latter can provide better
189/// IDE support. However, it introduces two distinct items with the same
190/// semantics, so naming should be chosen carefully to avoid confusion.
191/// 
192/// # Panics and Debugging
193/// 
194/// If a panic occurs on the call stack, the process will abort.
195/// The runtime may print a backtrace to `stderr`, but it may lack sufficient
196/// symbol information (such as function names and source locations)
197/// for meaningful debugging.
198///
199/// For example, when using the MSVC toolchain on Windows, including
200/// the generated `.pdb` file alongside the `.dll` (in the `.ane` package)
201/// allows debuggers and crash reporting tools to resolve symbols
202/// and produce human-readable stack traces.
203/// 
204/// ## Full Example
205/// ```
206/// mod lib {
207///     use fre_rs::prelude::*;
208///     fre_rs::function! {
209///         method_name (ctx, data, args) -> as3::Object {
210///             return ctx.get_actionscript_data();
211///         }
212///     }
213///     fre_rs::function! (method_name2 use method_implementation);
214///     fn method_implementation <'a> (ctx: &CurrentContext<'a>, data: Option<&mut dyn Any>, args: &[as3::Object<'a>]) -> as3::Object<'a> {as3::null}
215/// }
216/// ```
217/// ## Minimal Example
218/// ```
219/// mod lib {
220///     fre_rs::function! {
221///         method_name (_, _, _) {}
222///     }
223/// }
224/// ```
225/// 
226#[macro_export]
227macro_rules! function {
228    {// #0
229        $name:ident ($($arguments:tt)+) $(-> $return_type:ty)? $body:block
230    } => {
231        #[allow(non_upper_case_globals)]
232        pub const $name: &'static $crate::function::FunctionImplementation = & $crate::function::FunctionImplementation::new(
233            $crate::function!(@Name $name), {
234            #[allow(unsafe_op_in_unsafe_fn)]
235            unsafe extern "C" fn abi(
236                ctx: $crate::c::markers::FREContext,
237                func_data: $crate::c::markers::FREData,
238                argc: u32,
239                argv: *const $crate::c::markers::FREObject,
240            ) -> $crate::c::markers::FREObject {
241                fn func <'a> (
242                    ctx: &$crate::context::CurrentContext<'a>,
243                    func_data: Option<&mut dyn ::std::any::Any>,
244                    args: &[$crate::as3::Object<'a>],
245                ) -> $crate::as3::Object<'a> {
246                    $crate::function! {@Parameters [ctx, func_data, args] $($arguments)+}
247                    (|| -> $crate::function!(@Return $($return_type)?) {
248                        $body
249                    })().into()
250                }
251                $crate::context::CurrentContext::with_method(&ctx, func_data, argc, argv, func)
252            }
253            abi},
254        );
255    };
256    (// #1
257        $name:ident use $func:path
258    ) => {
259        #[allow(non_upper_case_globals)]
260        pub const $name: &'static $crate::function::FunctionImplementation = & $crate::function::FunctionImplementation::new(
261            $crate::function!(@Name $name), {
262            #[allow(unsafe_op_in_unsafe_fn)]
263            unsafe extern "C" fn abi(
264                ctx: $crate::c::markers::FREContext,
265                func_data: $crate::c::markers::FREData,
266                argc: u32,
267                argv: *const $crate::c::markers::FREObject,
268            ) -> $crate::c::markers::FREObject {
269                $crate::context::CurrentContext::with_method(&ctx, func_data, argc, argv, $func)
270            }
271            abi},
272        );
273    };
274    (// #2
275        @Name $name:ident
276    ) => {
277        unsafe {
278            let s: &'static str = concat!(stringify!($name), "\0");
279            let s: &'static ::std::ffi::CStr = ::std::ffi::CStr::from_bytes_with_nul_unchecked(s.as_bytes());
280            let s = $crate::validated::UCStr::from_literal_unchecked(s);
281            s
282        }
283    };
284    (// #3
285        @Return $return_type:ty
286    ) => ($return_type); 
287    (// #4
288        @Return
289    ) => (());
290    {// #5
291        @Parameters [$c:ident, $d:ident, $a:ident $(,)?]
292        $ctx:ident, $data:ident, $args:ident $(,)?
293    } => {
294        let $ctx: &$crate::context::CurrentContext<'a> = $c;
295        let $data: Option<&mut dyn ::std::any::Any> = $d;
296        let $args: &[$crate::as3::Object<'a>] = $a;
297    };
298    {// #6
299        @Parameters [$c:ident, $d:ident, $a:ident $(,)?]
300        $ctx:ident, $data:ident, _ $(,)?
301    } => {
302        $crate::function! {@Parameters [$c, $d, $a]
303            $ctx, $data, _args
304        }
305    };
306    {// #7
307        @Parameters [$c:ident, $d:ident, $a:ident $(,)?]
308        $ctx:ident, _, $args:ident $(,)?
309    } => {
310        $crate::function! {@Parameters [$c, $d, $a]
311            $ctx, _data, $args
312        }
313    };
314    {// #8
315        @Parameters [$c:ident, $d:ident, $a:ident $(,)?]
316        _, $data:ident, $args:ident $(,)?
317    } => {
318        $crate::function! {@Parameters [$c, $d, $a]
319            _ctx, $data, $args
320        }
321    };
322    {// #9
323        @Parameters [$c:ident, $d:ident, $a:ident $(,)?]
324        _, _, $args:ident $(,)?
325    } => {
326        $crate::function! {@Parameters [$c, $d, $a]
327            _ctx, _data, $args
328        }
329    };
330    {// #10
331        @Parameters [$c:ident, $d:ident, $a:ident $(,)?]
332        _, $data:ident, _ $(,)?
333    } => {
334        $crate::function! {@Parameters [$c, $d, $a]
335            _ctx, $data, _args
336        }
337    };
338    {// #11
339        @Parameters [$c:ident, $d:ident, $a:ident $(,)?]
340        $ctx:ident, _, _ $(,)?
341    } => {
342        $crate::function! {@Parameters [$c, $d, $a]
343            $ctx, _data, _args
344        }
345    };
346    {// #12
347        @Parameters [$c:ident, $d:ident, $a:ident $(,)?]
348        _, _, _ $(,)?
349    } => {
350        $crate::function! {@Parameters [$c, $d, $a]
351            _ctx, _data, _args
352        }
353    };
354}
355