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: &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: &CurrentContext);
28
29/// The function can be associated with a [`Context`] and treated as its method.
30/// 
31/// This signature is intended to match the closure accepted by [`CurrentContext::with_method`].
32///
33/// Although the signature returns `as3::Object<'a>`, implementations may return
34/// any type implementing `Into<as3::Object<'a>> + 'a`, primarily to support
35/// types like `Option<AsObject<'a>>`.
36/// 
37pub type Function <'a> = fn (ctx: &CurrentContext<'a>, func_data: Option<&mut dyn Any>, args: &[as3::Object<'a>]) -> as3::Object<'a>;
38
39
40/// **In typical usage of this crate, instances of this type should not be constructed directly.**
41/// 
42/// The [`crate::function!`] macro should be used to construct this type, as it provides a safer abstraction.
43/// 
44#[derive(Debug)]
45pub struct FunctionImplementation {
46    raw_name: UCStr,
47    raw_func: FREFunction,
48}
49impl FunctionImplementation {
50    pub fn raw_name(&self) -> &UCStr {&self.raw_name}
51    pub fn raw_func(&self) -> FREFunction {self.raw_func}
52    pub const fn new (raw_name: UCStr, raw_func: FREFunction) -> Self {
53        Self { raw_name, raw_func }
54    }
55}
56
57
58/// A collection of functions associated with a specific context type.
59///
60/// This type is used to construct a set of functions for a [`Context`].
61/// Once registered, these functions are referred to as *methods* within this crate,
62/// and can be invoked from ActionScript via `ExtensionContext.call`.
63/// 
64#[derive(Debug)]
65pub struct FunctionSet {
66    list: Vec<FRENamedFunction>,
67    map: HashMap<UCStr, usize>,
68}
69impl FunctionSet {
70    pub fn new () -> Self {
71        Self {
72            list: Vec::new(),
73            map: HashMap::new(),
74        }
75    }
76    pub fn with_capacity (capacity: usize) -> Self{
77        Self {
78            list: Vec::with_capacity(capacity),
79            map: HashMap::with_capacity(capacity),
80        }
81    }
82
83    /// Adds a function that can be registered as a method.
84    ///
85    /// - `name`: The method name. If [`None`], the raw name of `func_impl` is used.
86    /// - `func_data`: Data associated with this method. It will be dropped after [`ContextFinalizer`] returns.
87    /// - `func_impl`: The method implementation created by the [`crate::function!`] macro.
88    /// 
89    /// # Panics
90    ///
91    /// Panics if a function with the same `name` has already been added.
92    ///
93    /// Callers must ensure that each `name` is unique within this set. 
94    /// 
95    pub fn add (
96        &mut self,
97        name: Option<UCStr>,
98        func_data: Option<Box<dyn Any>>,
99        func_impl: &FunctionImplementation,
100    ) {
101        let name = name.unwrap_or(func_impl.raw_name.clone());
102        let index = self.list.len();
103        self.list.push(FRENamedFunction {
104            name: name.as_ptr(),
105            functionData: if let Some(func_data) = func_data {crate::data::into_raw(func_data).as_ptr()} else {FREData::default()},
106            function: func_impl.raw_func,
107        });
108        let r = self.map.insert(name, index);
109        assert!(r.is_none(), "Method name conflict.");
110    }
111}
112impl Drop for FunctionSet {
113    fn drop(&mut self) {
114        self.list.iter()
115            .map(|i| i.functionData)
116            .for_each(|d| {
117                if let Some(d) = NonNullFREData::new(d) {
118                    unsafe {crate::data::drop_from(d)};
119                }
120            });
121    }
122}
123
124
125#[derive(Debug)]
126pub(super) struct MethodSet {
127    registry: Box<[FRENamedFunction]>,
128    dictionary: HashMap<UCStr, usize>,
129}
130impl MethodSet {
131    /// ## Borrow
132    pub(super) fn get(&self, name: &str) -> Option<(FREFunction, FREData)> {
133        self.dictionary.get(name)
134            .map(|index| {
135                let i = &(self.registry[*index]);
136                (i.function, i.functionData)
137            })
138    }
139}
140impl Drop for MethodSet {
141    fn drop(&mut self) {
142        self.registry.iter()
143            .map(|i| i.functionData)
144            .for_each(|d| {
145                if let Some(d) = NonNullFREData::new(d) {
146                    unsafe {crate::data::drop_from(d)};
147                }
148            });
149    }
150}
151impl From<FunctionSet> for MethodSet {
152    fn from(mut value: FunctionSet) -> Self {
153        let registry = std::mem::take(&mut value.list).into_boxed_slice();
154        let dictionary = std::mem::take(&mut value.map);
155        Self { registry, dictionary }
156    }
157}
158impl AsRef<[FRENamedFunction]> for MethodSet {
159    fn as_ref(&self) -> &[FRENamedFunction] {self.registry.as_ref()}
160}