Skip to main content

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