fre_rs/context.rs
1//!
2//! Core implementation of the extension entry point abstraction.
3//!
4
5
6use super::*;
7
8
9/// The primary entry point of this crate, providing access to its public APIs.
10#[derive(Debug)]
11#[repr(transparent)]
12pub struct FlashRuntime<'a> (FREContext, PhantomData<&'a()>);
13impl<'a> FlashRuntime<'a> {
14 pub fn current_context(&self) -> Context<'a> {Context(self.0, PhantomData)}
15 pub fn event_dispatcher(&self) -> EventDispatcher {self.current_context().event_dispatcher()}
16
17 /// A wrapper used by [`FREContextInitializer`], [`FREContextFinalizer`], and
18 /// [`FREFunction`] that provides a safe stack-level execution environment.
19 ///
20 /// **In typical usage of this crate, this function should not be called directly.**
21 ///
22 /// # Safety
23 /// While all operations performed within this function are safe at the stack level,
24 /// calling this function itself is unsafe and requires that all input arguments
25 /// are valid. In particular, this function assumes it is invoked directly with
26 /// arguments provided by the Flash runtime.
27 ///
28 /// Violating these assumptions may lead to undefined behavior.
29 #[allow(unsafe_op_in_unsafe_fn)]
30 pub unsafe fn with <F, R> (ctx: &'a FREContext, f: F) -> R
31 where
32 F: FnOnce (&FlashRuntime<'a>) -> R,
33 R: 'a,
34 {
35 assert!(!ctx.is_null());
36 assert!(!IS_FRT_BORROWED.get());
37 IS_FRT_BORROWED.set(true);
38 let frt: FlashRuntime<'a> = Self(*ctx, PhantomData);
39 let r = f(&frt);
40 IS_FRT_BORROWED.set(false);
41 r
42 }
43
44 /// A wrapper around [`FREContextInitializer`] that provides a safe stack-level
45 /// execution environment for context initialization.
46 ///
47 /// **In typical usage of this crate, this function should not be called directly.**
48 ///
49 /// # Safety
50 /// While all operations performed within this function are safe at the stack level,
51 /// calling this function itself is unsafe and requires the following conditions:
52 ///
53 /// - The native data associated with [`Context`] must not be accessed or managed
54 /// by external code.
55 /// - This function will construct a [`ContextRegistry`] and assign it as the native data.
56 /// The constructed [`ContextRegistry`] must be properly disposed in
57 /// [`FREContextFinalizer`] to ensure its lifecycle is correctly terminated.
58 /// - This function assumes it is invoked directly with arguments provided by the
59 /// Flash runtime, meaning all arguments must be valid and consistent.
60 ///
61 /// Violating these assumptions may lead to undefined behavior.
62 #[allow(unsafe_op_in_unsafe_fn)]
63 pub unsafe fn with_context_initializer <F> (
64 ext_data: FREData,// &'extension mut
65 ctx_type: FREStr,// &'function
66 ctx: &'a FREContext,// &'function
67 num_funcs_to_set: *mut u32,// return
68 funcs_to_set: *mut *const FRENamedFunction,// return &'context mut
69 f: F
70 )
71 where F: FnOnce (&FlashRuntime<'a>) -> FunctionSet
72 {
73 assert!(!num_funcs_to_set.is_null());
74 assert!(!funcs_to_set.is_null());
75 Self::with(ctx, |frt|{
76 let ctx_type = if ctx_type.is_null() {None} else {
77 let ctx_type = CStr::from_ptr(ctx_type as *const c_char);
78 let ctx_type = UCStr::try_from(ctx_type).expect("Input string is not valid UTF-8.");
79 Some(ctx_type)
80 };
81 let ctx_data = ContextRegistry::new(ext_data, ctx_type).into_raw();
82 let r= frt.current_context().set_native_data(ctx_data);// <'context> move
83 assert!(r.is_ok());
84 let r = f(frt);
85 let methods = MethodSet::from(r);
86 let r = methods.as_ref();
87 assert!(r.len() <= u32::MAX as usize);
88 *num_funcs_to_set = r.len() as u32;
89 *funcs_to_set = r.as_ptr();
90 let ctx_data = ContextRegistry::mut_from(ctx_data).unwrap();
91 ctx_data.methods = Some(methods);
92 })
93 }
94
95 /// A wrapper around [`FREFunction`] that provides a safe stack-level execution
96 /// environment for the given closure.
97 ///
98 /// **In typical usage of this crate, this function should not be called directly.**
99 ///
100 /// # Safety
101 /// While all operations performed within this function are safe at the stack level,
102 /// calling this function itself is unsafe and requires the following conditions:
103 ///
104 /// - `func_data` must either be constructed via [`Data::into_raw`] before
105 /// [`ContextInitializer`] returns, or be a null pointer.
106 /// - This function assumes it is invoked directly with arguments provided by the
107 /// Flash runtime, meaning all arguments must be valid and consistent.
108 ///
109 /// Violating these assumptions may lead to undefined behavior.
110 #[allow(unsafe_op_in_unsafe_fn)]
111 pub unsafe fn with_method <F> (
112 ctx: &'a FREContext,// &'function
113 func_data: FREData,// &'function mut
114 argc: u32,
115 argv: *const FREObject,// &'function
116 f: F
117 ) -> FREObject
118 where F: FnOnce (&FlashRuntime<'a>, Option<&mut dyn Any>, &[Object<'a>]) -> Object<'a>
119 {
120 assert!(!argv.is_null() || argc==0);
121 Self::with(ctx, |frt| {
122 let func_data = NonNullFREData::new(func_data)
123 .map(|raw| crate::data::mut_from(raw));
124 let args = std::slice::from_raw_parts(argv as *const Object, argc as usize);
125 f(frt, func_data, args).as_ptr()
126 })
127 }
128
129 pub fn trace (&self, msg: impl Into<UCStr>) {_ = self.current_context().trace(msg);}
130
131}
132impl Drop for FlashRuntime<'_> {
133 fn drop(&mut self) {IS_FRT_BORROWED.set(false);}
134}
135thread_local! {static IS_FRT_BORROWED: Cell<bool> = Cell::new(false)}
136
137
138/// A handle to a context that may become invalid under specific conditions.
139///
140/// Invalidity only occurs after the associated `ExtensionContext` AS3 object
141/// has been disposed. Therefore, callers should be prepared for operations
142/// on [`Context`] to fail at appropriate points.
143///
144/// This crate leverages [`FREGetFREContextFromExtensionContext`] to enable
145/// more advanced use cases, but doing so also increases overall complexity.
146#[derive(Debug, Clone, Copy, PartialEq, Eq)]
147#[repr(transparent)]
148pub struct Context <'a> (FREContext, PhantomData<&'a()>);
149impl<'a> Context<'a> {
150 pub fn is_valid(self) -> bool {self.get_actionscript_data().is_ok()}
151 pub fn event_dispatcher(self) -> EventDispatcher {EventDispatcher(self.0)}
152
153 /// Provides exclusive access to the [`ContextRegistry`] in a constrained manner.
154 ///
155 /// # Safety
156 /// This method assumes that the native data associated with the [`Context`]
157 /// was created and is exclusively managed by [`crate::extension!`] as a [`ContextRegistry`].
158 ///
159 /// To call this method safely, you must guarantee that the native data
160 /// attached to the [`Context`] has NOT been manually managed, replaced,
161 /// or altered by external code. Violating this assumption may lead to
162 /// undefined behavior.
163 ///
164 /// The use of a closure and the [`Sync`] bound is intentional: it constrains
165 /// the ordering of FFI interactions, while helping prevent data races,
166 /// preserving memory safety, and avoiding uncontrolled complexity growth
167 /// in cross-boundary usage.
168 ///
169 /// [`Err`]=> [`ContextError::InvalidContext`], [`ContextError::NullData`], [`ContextError::UnexpectedData`], [`ContextError::FfiCallFailed`];
170 #[allow(unsafe_op_in_unsafe_fn)]
171 pub unsafe fn with <F, R> (self, f: F) -> Result<R, ContextError>
172 where F: FnOnce (&mut ContextRegistry) -> R + Sync {
173 if !self.is_valid() {return Err(ContextError::InvalidContext)}
174 let raw = NonNullFREData::new(self.get_native_data()?)
175 .ok_or(ContextError::NullData)?;
176 let cr = ContextRegistry::mut_from(raw)
177 .map_err(|_| ContextError::UnexpectedData)?;
178 let r = f(cr);
179 Ok(r)
180 }
181
182 /// **In typical usage of this crate, this function should not be called directly.**
183 ///
184 /// This is because the native data is reserved for [`ContextRegistry`] and is
185 /// automatically managed by [`crate::extension!`].
186 pub fn get_native_data (self) -> Result<FREData, FfiError> {
187 let mut data = FREData::default();
188 let r = unsafe {FREGetContextNativeData(self.0, &mut data)};
189 if let Ok(e) = FfiError::try_from(r) {Err(e)} else {Ok(data)}
190 }
191
192 /// **In typical usage of this crate, this function should not be called directly.**
193 ///
194 /// This is because the native data is reserved for [`ContextRegistry`] and is
195 /// automatically managed by [`crate::extension!`].
196 ///
197 /// # Safety
198 ///
199 /// This function sets the native data pointer associated with the underlying [`FREContext`].
200 /// Calling this function is unsafe because the caller must uphold the following invariants:
201 ///
202 /// 1. **The native data must not have been previously set**
203 /// - The context must currently be in an uninitialized state with respect to native data.
204 /// - In other words, [`Self::get_native_data`] must return null pointer.
205 /// - Violating this will overwrite an existing pointer, causing a memory leak or double-free.
206 ///
207 /// 2. **The ownership and lifetime of `data` must be correctly managed**
208 /// - If `data` represents a moved (owned) allocation, the caller is responsible for ensuring
209 /// that it is eventually freed.
210 /// - The memory must remain valid for the entire lifetime of the [`FREContext`].
211 /// - The allocation must be released no later than in the [`FREContextFinalizer`] callback,
212 /// where the native data is expected to be cleaned up manually.
213 ///
214 /// Failure to uphold these guarantees may result in undefined behavior, including memory leaks,
215 /// use-after-free, or double-free errors.
216 #[allow(unsafe_op_in_unsafe_fn)]
217 pub unsafe fn set_native_data (self, data: NonNullFREData) -> Result<(), FfiError> {
218 debug_assert!(self.get_native_data()?.is_null());
219 let r = FRESetContextNativeData(self.0, data.as_ptr());
220 if let Ok(e) = FfiError::try_from(r) {Err(e)} else {Ok(())}
221 }
222 /// `flash.external.ExtensionContext.actionScriptData`
223 pub fn get_actionscript_data (self) -> Result<Object<'a>, FfiError> {
224 let mut obj = FREObject::default();
225 let r = unsafe {FREGetContextActionScriptData(self.0, &mut obj)};
226 if let Ok(e) = FfiError::try_from(r) {Err(e)} else {Ok(unsafe {transmute(obj)})}
227 }
228 /// `flash.external.ExtensionContext.actionScriptData`
229 pub fn set_actionscript_data (self, object: Object<'_>) -> Result<(), FfiError> {
230 let r = unsafe {FRESetContextActionScriptData(self.0, object.as_ptr())};
231 if let Ok(e) = FfiError::try_from(r) {Err(e)} else {Ok(())}
232 }
233
234 /// Calls a registered function by name through this crate's internal API.
235 ///
236 /// # Safety
237 /// To call this method safely, all safety requirements of [`Self::with`]
238 /// must be upheld. In particular, the native data associated with the
239 /// [`Context`] must be a valid [`ContextRegistry`] created and exclusively
240 /// managed by [`crate::extension!`], and must not be manually modified or
241 /// replaced by external code.
242 ///
243 /// Violating these conditions may lead to undefined behavior.
244 #[allow(unsafe_op_in_unsafe_fn)]
245 pub unsafe fn call_method (self, name: &str, args: &[Object<'a>] ) -> Result<Object<'a>, ContextError> {
246 self.with(|registry| {
247 registry.methods.as_mut()
248 .ok_or(ContextError::MethodsNotRegistered)
249 .map(|ms| ms.get(name))
250 })??
251 .ok_or(ContextError::MethodNotFound)
252 .map(|(func, data)| {
253 let r = func(self.0, data, args.len() as u32, args.as_ptr() as *mut FREObject);
254 transmute(r)
255 })
256 }
257
258 pub fn trace (self, msg: impl Into<UCStr>) -> Result<(), FfiError> {
259 let r = unsafe {FRETrace(self.0, msg.into().as_ptr())};
260 if let Ok(e) = FfiError::try_from(r) {Err(e)} else {Ok(())}
261 }
262
263 /// Return [`Err`] if `stage` is non-null but not a `Stage` object
264 ///
265 /// This is a minimal safety wrapper around the underlying FFI. Its current placement,
266 /// shape, and usage are not ideal, and it is hoped that it can be refactored
267 /// if a more flexible C API becomes available in the AIR SDK.
268 pub fn get_render_mode (self, stage: Option<Object<'a>>) -> Result<crate::misc::RenderMode, FfiError> {
269 let stage = stage.unwrap_or_default();
270 let mut rm = u8::default();
271 let r = unsafe {FREGetRenderMode(self.0, stage.as_ptr(), &mut rm)};
272 if let Ok(e) = FfiError::try_from(r) {
273 Err(e)
274 } else {
275 let rm: FRERenderMode = FRERenderMode(rm as i32);
276 Ok(crate::misc::RenderMode::from(rm))
277 }
278 }
279
280 /// `air.media.MediaBuffer` (AIR SDK 51)
281 ///
282 /// `AIR-5963: Add ANE capabilities to render a Sprite using a MediaBuffer - initial support via BitmapData`
283 ///
284 /// This is a minimal safety wrapper around the underlying FFI. Its current placement,
285 /// shape, and usage are not ideal, and it is hoped that it can be refactored
286 /// if a more flexible C API becomes available in the AIR SDK.
287 pub fn set_render_source (self, media_buffer: Object<'a>, sprite: Object<'a>) -> Result<(), FfiError> {
288 let r = unsafe {FRESetRenderSource(self.0, media_buffer.as_ptr(), sprite.as_ptr())};
289 if let Ok(e) = FfiError::try_from(r) {Err(e)} else {Ok(())}
290 }
291
292 /// This is a minimal safety wrapper around the underlying FFI. Its current placement,
293 /// shape, and usage are not ideal, and it is hoped that it can be refactored
294 /// if a more flexible C API becomes available in the AIR SDK.
295 pub fn with_media_buffer <F, R> (self, media_buffer: Object<'a>, f: F) -> Result<R, FfiError>
296 where F: FnOnce (MediaBufferDataAdapter) -> R {
297 let mut bytes = FREBytes::default();
298 let mut width = u32::default();
299 let mut height = u32::default();
300 let mut stride = u32::default();
301 let mut format = u32::default();
302 let result = unsafe {FREMediaBufferLock(self.0, media_buffer.as_ptr(), &mut bytes, &mut width, &mut height, &mut stride, &mut format)};
303 if let Ok(e) = FfiError::try_from(result) {return Err(e);}
304 let adapter = unsafe {MediaBufferDataAdapter::new(bytes, width, height, stride, format)};
305 let r = f(adapter);
306 let result = unsafe {FREMediaBufferUnlock(self.0, media_buffer.as_ptr(), u32::default())};
307 debug_assert!(result.is_ok());
308 Ok(r)
309 }
310
311}
312
313
314/// The extension-side concrete representation of a [`Context`].
315///
316/// This can be considered the actual context instance, while [`Context`]
317/// serves as an abstract handle or outer wrapper around it.
318///
319/// This struct is only constructed via [`FlashRuntime::with_context_initializer`].
320#[derive(Debug)]
321pub struct ContextRegistry {
322 ctx_type: Option<UCStr>,
323 ctx_data: Option<Box<dyn Any>>,
324 ext_data: Option<NonNullFREData>,// &'extension mut
325 methods: Option<MethodSet>,
326}
327impl ContextRegistry {
328 pub fn context_type(&self) -> Option<UCStr> {(self.ctx_type).clone()}
329 pub fn context_data(&self) -> Option<&dyn Any> {self.ctx_data.as_ref().map(|b| b.as_ref())}
330 pub fn context_data_mut(&mut self) -> &mut Option<Box<dyn Any>> {&mut self.ctx_data}
331
332 /// Returns a reference to the extension data.
333 ///
334 /// # Safety
335 /// This method assumes that `ext_data` was either constructed via
336 /// [`Data::into_raw`] or is a null pointer.
337 ///
338 /// Violating these assumptions may lead to undefined behavior.
339 #[allow(unsafe_op_in_unsafe_fn)]
340 pub unsafe fn extension_data(&self) -> Option<&dyn Any> {
341 self.ext_data
342 .as_ref()
343 .map(|raw| crate::data::ref_from(*raw))
344 }
345 /// Returns a mutable reference to the extension data.
346 ///
347 /// # Safety
348 /// This method assumes that `ext_data` was either constructed via
349 /// [`Data::into_raw`] or is a null pointer.
350 ///
351 /// Violating these assumptions may lead to undefined behavior.
352 #[allow(unsafe_op_in_unsafe_fn)]
353 pub unsafe fn extension_data_mut(&mut self) -> Option<&mut dyn Any> {
354 self.ext_data
355 .as_ref()
356 .map(|raw| crate::data::mut_from(*raw))
357 }
358 fn new (ext_data: FREData, ctx_type: Option<UCStr>) -> Self {
359 let ext_data = NonNullFREData::new(ext_data);
360 Self {
361 ctx_type,
362 ctx_data: None,
363 ext_data,
364 methods: None,
365 }
366 }
367}
368impl Data for ContextRegistry {}
369