Skip to main content

fre_rs/
function.rs

1use super::*;
2
3
4/// The function is used to initialize the extension.
5///
6/// Returns [`None`] if no Extension Data is set.
7/// 
8pub type Initializer = fn () -> Option<Box<dyn Any>>;
9
10/// The function receives ownership of the Extension Data,
11/// allowing it to be saved or disposed.
12/// 
13/// The runtime does **NOT** guarantee that this function will be called before process termination.
14/// 
15pub type Finalizer = fn (ext_data: Option<Box<dyn Any>>);
16
17/// The function is used to initialize a [`Context`].
18///
19/// The first return value sets the Context Data.
20/// The second return value sets the methods associated with the [`Context`].
21/// 
22pub type ContextInitializer = fn (ctx: &mut CurrentContext) -> (Option<Box<dyn Any>>, FunctionSet);
23
24/// The function is called before the Context Data is disposed,
25/// allowing final access and saving data to the Extension Data.
26///
27pub type ContextFinalizer = fn (ctx: &mut CurrentContext);
28
29/// The function can be associated with a [`Context`] and treated as its method.
30///
31/// Although the signature returns [`Object<'a>`], implementations may return
32/// any type implementing [`Into<Object> + 'a`], primarily to support types like [`Option<AsObject<'a>>`].
33/// 
34pub type Function <'a> = fn (ctx: &mut CurrentContext<'a>, func_data: Option<&mut dyn Any>, args: &[Object<'a>]) -> Object<'a>;
35
36
37/// Represents a native function implementation that can be registered as a method.
38/// 
39/// Can only be constructed through the [`function!`] macro.
40/// 
41#[derive(Debug)]
42pub struct FunctionImplementation {
43    name: UCStr,
44    func: FREFunction,
45}
46impl FunctionImplementation {
47    pub fn name(&self) -> &UCStr {&self.name}
48    pub fn func(&self) -> FREFunction {self.func}
49    pub(crate) const fn new (name: UCStr, func: FREFunction) -> Self {Self { name, func }}
50}
51
52
53/// A collection of functions associated with a specific context type.
54///
55/// This type is used to construct a set of functions for a [`Context`].
56/// Once registered, these functions are referred to as *methods* within this crate,
57/// and can be invoked from AS3 via `ExtensionContext.call`.
58/// 
59#[derive(Debug)]
60pub struct FunctionSet {
61    list: Vec<FRENamedFunction>,
62    map: HashMap<UCStr, usize>,
63}
64impl FunctionSet {
65    pub fn new () -> Self {
66        Self {
67            list: Vec::new(),
68            map: HashMap::new(),
69        }
70    }
71    pub fn with_capacity (capacity: usize) -> Self{
72        Self {
73            list: Vec::with_capacity(capacity),
74            map: HashMap::with_capacity(capacity),
75        }
76    }
77
78    /// Adds a function that can be registered as a method.
79    /// 
80    /// # Parameters
81    /// 
82    /// - `name`: The method name. If [`None`], the raw name of `func_impl` is used.
83    /// - `func_data`: Data associated with this method. It will be dropped after [`ContextFinalizer`] returns.
84    /// - `func_impl`: The method implementation created by the [`function!`] macro.
85    /// 
86    /// # Panics
87    /// 
88    /// Panics if a function with the same `name` has already been added.
89    /// 
90    /// Callers must ensure that each `name` is unique within this set. 
91    /// 
92    pub fn add (
93        &mut self,
94        name: Option<UCStr>,
95        func_data: Option<Box<dyn Any>>,
96        func_impl: &'static FunctionImplementation,
97    ) {
98        let name = name.unwrap_or(func_impl.name.clone());
99        let index = self.list.len();
100        self.list.push(FRENamedFunction {
101            name: name.as_ptr(),
102            functionData: if let Some(func_data) = func_data {crate::data::into_raw(func_data).as_ptr()} else {FREData::default()},
103            function: func_impl.func,
104        });
105        let r = self.map.insert(name, index);
106        assert!(r.is_none(), "Method name conflict.");
107    }
108}
109impl Drop for FunctionSet {
110    fn drop(&mut self) {
111        self.list.iter()
112            .map(|i| i.functionData)
113            .for_each(|d| {
114                if let Some(d) = NonNullFREData::new(d) {
115                    unsafe {crate::data::drop_from(d)};
116                }
117            });
118    }
119}
120
121
122#[derive(Debug)]
123pub(super) struct MethodSet {
124    registry: Box<[FRENamedFunction]>,
125    dictionary: HashMap<UCStr, usize>,
126}
127impl MethodSet {
128    /// ## Borrow
129    pub(super) fn get(&self, name: &str) -> Option<(FREFunction, FREData)> {
130        self.dictionary.get(name)
131            .map(|index| {
132                let i = &(self.registry[*index]);
133                (i.function, i.functionData)
134            })
135    }
136}
137impl Drop for MethodSet {
138    fn drop(&mut self) {
139        self.registry.iter()
140            .map(|i| i.functionData)
141            .for_each(|d| {
142                if let Some(d) = NonNullFREData::new(d) {
143                    unsafe {crate::data::drop_from(d)};
144                }
145            });
146    }
147}
148impl From<FunctionSet> for MethodSet {
149    fn from(mut value: FunctionSet) -> Self {
150        let registry = std::mem::take(&mut value.list).into_boxed_slice();
151        let dictionary = std::mem::take(&mut value.map);
152        Self { registry, dictionary }
153    }
154}
155impl AsRef<[FRENamedFunction]> for MethodSet {
156    fn as_ref(&self) -> &[FRENamedFunction] {self.registry.as_ref()}
157}
158
159
160/// Sends a message to the debugger output.
161/// 
162/// Delivery is not guaranteed; the `message` may not be presented.
163/// 
164/// # Panics
165///
166/// Panics if no [`CurrentContext`] exists. The call must occur within an
167/// active native function call from the Flash runtime main thread.
168/// 
169/// # Examples
170/// 
171/// ```
172/// use fre_rs::prelude::*;
173/// fn func <'a> (_: &mut CurrentContext<'a>, args: &[Object<'a>]) {
174///     trace("Hello, Flash runtime!");
175///     trace(args);
176///     trace(args[0]);
177/// }
178/// ```
179/// 
180pub fn trace <T: ToUcstrLossy + Sized> (message: T) {crate::context::stack::current_context().trace(message);}
181