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