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