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 to be exported as part of the public ABI,
9/// and four functions: [`Initializer`], [`Finalizer`], [`ContextInitializer`], [`ContextFinalizer`].
10/// Some of these arguments are optional.
11/// 
12/// # Full Examples
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: &mut 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: &mut 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: &mut CurrentContext<'a>, data: Option<&mut dyn Any>, args: &[Object<'a>]) -> 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 Examples
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(_: &mut 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///             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::FREData,
93                ctx_type: $crate::c::FREStr,
94                ctx: $crate::c::FREContext,
95                num_funcs_to_set: *mut u32,
96                funcs_to_set: *mut *const $crate::c::FRENamedFunction,
97            ) {
98                let context_initializer: $crate::function::ContextInitializer = $context_initializer;
99                $crate::__private::context::with_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::FREContext) {
103                $crate::__private::context::with(&ctx, |ctx| {
104                    $(
105                        let context_finalizer: $crate::function::ContextFinalizer = $context_finalizer;
106                        context_finalizer(ctx);
107                    )?
108                    let ctx = *(ctx as *mut $crate::context::CurrentContext as *mut $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::FREData,
128            ctx_initializer_to_set: *mut $crate::c::FREContextInitializer,
129            ctx_finalizer_to_set: *mut $crate::c::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 = 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::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::FREData,
165            ctx_initializer_to_set: *mut $crate::c::FREContextInitializer,
166            ctx_finalizer_to_set: *mut $crate::c::FREContextFinalizer,
167        ) {
168            assert!(!ctx_initializer_to_set.is_null());
169            assert!(!ctx_finalizer_to_set.is_null());
170            *ctx_initializer_to_set = ctx_initializer;
171            *ctx_finalizer_to_set = ctx_finalizer;
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 [`FunctionImplementation`],
181/// intended to be added to a [`FunctionSet`].
182/// 
183/// This macro supports two forms:
184/// one that defines a function inline, and another that binds to an existing
185/// [`Function`] using the `use` keyword.
186///
187/// For larger or more complex implementations, the latter can provide better
188/// IDE support. However, it introduces two distinct items with the same
189/// semantics, so naming should be chosen carefully to avoid confusion.
190/// 
191/// # Panics and Debugging
192/// 
193/// If a panic occurs on the call stack, the process will abort.
194/// The runtime may print a backtrace to `stderr`, but it may lack sufficient
195/// symbol information (such as function names and source locations)
196/// for meaningful debugging.
197///
198/// For example, when using the MSVC toolchain on Windows, including
199/// the generated `.pdb` file alongside the `.dll` (in the `.ane` package)
200/// allows debuggers and crash reporting tools to resolve symbols
201/// and produce human-readable stack traces.
202/// 
203/// # Full Examples
204/// ```
205/// mod lib {
206///     use fre_rs::prelude::*;
207///     fre_rs::function! {
208///         method_name (ctx, data, args) -> Object {
209///             return ctx.get_actionscript_data();
210///         }
211///     }
212///     fre_rs::function! (method_name2 use method_implementation);
213///     fn method_implementation <'a> (ctx: &mut CurrentContext<'a>, data: Option<&mut dyn Any>, args: &[Object<'a>]) -> Object<'a> {as3::null}
214/// }
215/// ```
216/// # Minimal Examples
217/// ```
218/// mod lib {
219///     fre_rs::function! {
220///         method_name (_, _, _) {}
221///     }
222/// }
223/// ```
224/// 
225#[macro_export]
226macro_rules! function {
227    {// #0
228        $name:ident ($($arguments:tt)+) $(-> $return_type:ty)? $body:block
229    } => {
230        #[allow(non_upper_case_globals)]
231        pub const $name: &'static $crate::function::FunctionImplementation = & $crate::__private::function::implement(
232            $crate::ucstringify! ($name), {
233            #[allow(unsafe_op_in_unsafe_fn)]
234            unsafe extern "C" fn abi(
235                ctx: $crate::c::FREContext,
236                func_data: $crate::c::FREData,
237                argc: u32,
238                argv: *const $crate::c::FREObject,
239            ) -> $crate::c::FREObject {
240                fn func <'a> (
241                    ctx: &mut $crate::context::CurrentContext<'a>,
242                    func_data: Option<&mut dyn ::std::any::Any>,
243                    args: &[$crate::as3::Object<'a>],
244                ) -> $crate::as3::Object<'a> {
245                    $crate::function! {@Parameters [ctx, func_data, args] $($arguments)+}
246                    (|| -> $crate::function!(@Return $($return_type)?) {
247                        $body
248                    })().into()
249                }
250                $crate::__private::context::with_method(&ctx, func_data, argc, argv, func)
251            }
252            abi},
253        );
254    };
255    (// #1
256        $name:ident use $func:path
257    ) => {
258        #[allow(non_upper_case_globals)]
259        pub const $name: &'static $crate::function::FunctionImplementation = & $crate::__private::function::implement(
260            $crate::ucstringify! ($name), {
261            #[allow(unsafe_op_in_unsafe_fn)]
262            unsafe extern "C" fn abi(
263                ctx: $crate::c::FREContext,
264                func_data: $crate::c::FREData,
265                argc: u32,
266                argv: *const $crate::c::FREObject,
267            ) -> $crate::c::FREObject {
268                $crate::__private::context::with_method(&ctx, func_data, argc, argv, $func)
269            }
270            abi},
271        );
272    };
273    (// #2
274        @Return $return_type:ty
275    ) => ($return_type); 
276    (// #3
277        @Return
278    ) => (());
279    {// #4
280        @Parameters [$c:ident, $d:ident, $a:ident $(,)?]
281        $ctx:ident, $data:ident, $args:ident $(,)?
282    } => {
283        let $ctx: &mut $crate::context::CurrentContext<'a> = $c;
284        let $data: Option<&mut dyn ::std::any::Any> = $d;
285        let $args: &[$crate::as3::Object<'a>] = $a;
286    };
287    {// #5
288        @Parameters [$c:ident, $d:ident, $a:ident $(,)?]
289        $ctx:ident, $data:ident, _ $(,)?
290    } => {
291        $crate::function! {@Parameters [$c, $d, $a]
292            $ctx, $data, _args
293        }
294    };
295    {// #6
296        @Parameters [$c:ident, $d:ident, $a:ident $(,)?]
297        $ctx:ident, _, $args:ident $(,)?
298    } => {
299        $crate::function! {@Parameters [$c, $d, $a]
300            $ctx, _data, $args
301        }
302    };
303    {// #7
304        @Parameters [$c:ident, $d:ident, $a:ident $(,)?]
305        _, $data:ident, $args:ident $(,)?
306    } => {
307        $crate::function! {@Parameters [$c, $d, $a]
308            _ctx, $data, $args
309        }
310    };
311    {// #8
312        @Parameters [$c:ident, $d:ident, $a:ident $(,)?]
313        _, _, $args:ident $(,)?
314    } => {
315        $crate::function! {@Parameters [$c, $d, $a]
316            _ctx, _data, $args
317        }
318    };
319    {// #9
320        @Parameters [$c:ident, $d:ident, $a:ident $(,)?]
321        _, $data:ident, _ $(,)?
322    } => {
323        $crate::function! {@Parameters [$c, $d, $a]
324            _ctx, $data, _args
325        }
326    };
327    {// #10
328        @Parameters [$c:ident, $d:ident, $a:ident $(,)?]
329        $ctx:ident, _, _ $(,)?
330    } => {
331        $crate::function! {@Parameters [$c, $d, $a]
332            $ctx, _data, _args
333        }
334    };
335    {// #11
336        @Parameters [$c:ident, $d:ident, $a:ident $(,)?]
337        _, _, _ $(,)?
338    } => {
339        $crate::function! {@Parameters [$c, $d, $a]
340            _ctx, _data, _args
341        }
342    };
343}
344
345
346/// Defines a new AS3 class.
347/// 
348/// Accepts a unique class name as an argument.
349/// 
350/// By default, the generated class implements [`PartialEq<Self>`] and [`Eq`]
351/// using pointer equality. This can be disabled by adding the `?PartialEq` modifier.
352/// 
353/// # Examples
354/// 
355/// ```
356/// use fre_rs::prelude::*;
357/// fre_rs::class! (EventDispatcher);
358/// impl<'a> EventDispatcher<'a> {
359///     pub fn new (ctx: &mut CurrentContext<'a>) -> Self {
360///        unsafe {ctx.construct(fre_rs::ucstringify!(flash.events.EventDispatcher), None)
361///             .expect("Object construction failed.")
362///             .as_unchecked()}
363///     }
364/// }
365/// fre_rs::class! (XML: ?PartialEq);
366/// impl PartialEq for XML<'_> {
367///     fn eq(&self, other: &Self) -> bool {todo!()}
368/// }
369/// impl Eq for XML<'_> {}
370/// ```
371/// 
372#[macro_export]
373macro_rules! class {
374
375    // #0
376    {
377        $(#[$meta:meta])*
378        $name:ident $($modifier:tt)*
379    } => {
380        $crate::class! {@Define
381            $(#[$meta])*
382            $name $($modifier)*
383        }
384        unsafe impl<'a> $crate::types::object::AsObject<'a> for $name<'a> {
385            const TYPE: $crate::types::Type = $crate::types::Type::Named(stringify!($name));
386        }
387    };
388
389    // #1
390    {@Typeof
391        $(#[$meta:meta])*
392        $name:ident $($modifier:tt)*
393    } => {                                                          const _: () = $crate::__private::SEALED;
394        $crate::class! {@Define
395            $(#[$meta])*
396            $name $($modifier)*
397        }
398        unsafe impl<'a> $crate::types::object::AsObject<'a> for $name<'a> {
399            const TYPE: $crate::types::Type = $crate::types::Type::$name;
400        }
401        impl<'a> TryFrom<$crate::as3::Object<'a>> for $name<'a> {
402            type Error = $crate::types::Type;
403            fn try_from (object: $crate::as3::Object<'a>) -> Result<Self, Self::Error> {
404                let ty = <$crate::as3::Object as $crate::types::object::AsObject>::get_type(object);
405                if ty == <Self as $crate::types::object::AsObject>::TYPE {
406                    Ok(unsafe {<$crate::as3::Object as $crate::types::object::AsObject>::as_unchecked(object)})
407                }else{Err(ty)}
408            }
409        }
410        impl<'a> TryFrom<$crate::types::object::NonNullObject<'a>> for $name<'a> {
411            type Error = $crate::types::Type;
412            fn try_from (object: $crate::types::object::NonNullObject<'a>) -> Result<Self, Self::Error> {
413                <$crate::types::object::NonNullObject as $crate::types::object::AsObject>::as_object(object).try_into()
414            }
415        }
416    };
417
418    // #2
419    {@Define
420        $(#[$meta:meta])*
421        $name:ident
422    } => {
423        $(#[$meta])*
424        #[derive(::std::fmt::Debug, Clone, Copy, PartialEq, Eq)]
425        #[repr(transparent)]
426        pub struct $name <'a> (::std::ptr::NonNull<::std::ffi::c_void>, ::std::marker::PhantomData<&'a ()>);
427        $crate::class!(@Implement $name);
428    };
429
430    // #3
431    {@Define
432        $(#[$meta:meta])*
433        $name:ident: ?PartialEq
434    } => {
435        $(#[$meta])*
436        #[derive(::std::fmt::Debug, Clone, Copy)]
437        #[repr(transparent)]
438        pub struct $name <'a> (::std::ptr::NonNull<::std::ffi::c_void>, ::std::marker::PhantomData<&'a ()>);
439        $crate::class!(@Implement $name);
440    };
441
442    // #4
443    {@Implement
444        $name:ident
445    } => {
446        #[cfg(debug_assertions)]
447        const _: () = {
448            #[used]
449            #[unsafe(export_name = concat!("__class_", stringify!($name)))]
450            pub static CLASS_NAME_MUST_UNIQUE: u8 = 0;
451        };
452
453        unsafe impl $crate::__private::Sealed for $name<'_> {}
454
455        impl<'a> $crate::types::object::AsNonNullObject<'a> for $name<'a> {}
456        impl<'a> From<$name<'a>> for $crate::types::object::NonNullObject<'a> {fn from(object: $name<'a>) -> Self {<$name as $crate::types::object::AsNonNullObject>::as_non_null_object(object)}}
457        
458        impl From<$name<'_>> for $crate::c::FREObject {fn from(object: $name) -> Self {<$name as $crate::types::object::AsObject>::as_ptr(object)}}
459        
460        impl ::std::fmt::Display for $name<'_> {fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result {::std::fmt::Display::fmt(&(<Self as $crate::types::object::AsNonNullObject>::as_non_null_object(*self)), f)}}
461        
462        impl $crate::validated::ToUcstrLossy for $name<'_> {fn to_ucstr_lossy(&self) -> $crate::validated::UCStr {
463            let object = <$name as $crate::types::object::AsObject>::as_object(*self);
464            <$crate::as3::Object as $crate::validated::ToUcstrLossy>::to_ucstr_lossy(&object)
465        }}
466    };
467}
468
469
470/// Interprets the input tokens as a string literal and yields a const [`UCStr`].
471///
472/// Whitespace between tokens is normalized in the same way as [`stringify!`].
473/// 
474/// Note that the expanded results of the input tokens may change in the future.
475/// You should be careful if you rely on the output.
476/// 
477/// # Examples
478/// 
479/// ```
480/// use fre_rs::prelude::*;
481/// const METHOD_NAME: UCStr = fre_rs::ucstringify! (addChild);
482/// const ERROR_MESSAGE: UCStr = fre_rs::ucstringify! {Invalid argument    type.    };
483/// assert_eq! (METHOD_NAME.as_str(), "addChild");
484/// assert_eq! (ERROR_MESSAGE.as_str(), "Invalid argument type.");
485/// ```
486/// 
487#[macro_export]
488macro_rules! ucstringify {
489
490    // #0
491    {$($tokens:tt)*} => {
492        {
493            const STR: &'static str = concat!(stringify!($($tokens)*), "\0");
494            const CSTR: &'static ::std::ffi::CStr = unsafe {::std::ffi::CStr::from_bytes_with_nul_unchecked(STR.as_bytes())};
495            const UCSTR: $crate::validated::UCStr = unsafe {$crate::validated::UCStr::from_literal_unchecked(CSTR)};
496            UCSTR
497        }
498    };
499}
500