Skip to main content

fre_rs/
context.rs

1//! 
2//! Core implementation of the extension abstraction.
3//! 
4
5
6use super::*;
7
8
9unsafe trait Sealed {}
10pub(crate) type ContextHandle = NonNull<c_void>;
11
12
13/// The current ANE context, on which most APIs in this crate depend.
14///
15/// The lifetime is strictly tied to the function call stack.
16///
17/// # Invariants
18/// 
19/// During any context-related call, the current context is guaranteed to be
20/// valid, and its associated AS3 `ExtensionContext` object must not call `dispose`.
21///
22/// Violating these invariants may cause subsequent API calls to fail and lead to
23/// rapidly increasing complexity in error handling. This crate treats such
24/// situations as invalid state and does not attempt to recover from them.
25/// 
26#[derive(Debug)]
27#[repr(transparent)]
28pub struct CurrentContext<'a> (ContextHandle, PhantomData<&'a()>);
29impl<'a> CurrentContext<'a> {
30
31    /// Returns the context type associated with the current context.
32    ///
33    /// This corresponds to the `contextType` argument passed to
34    /// `ExtensionContext.createExtensionContext`.
35    ///
36    /// Returns [`None`] if that argument was `null`.
37    /// 
38    pub fn ty(&self) -> Option<UCStr> {self.ctx_reg().context_type()}
39    
40    /// Returns an immutable reference to the Context Data associated with the current context.
41    ///
42    /// Context Data is user-defined data bound to the context, sharing the same
43    /// lifetime as the context itself.
44    ///
45    /// It can only be set via the first return value of [`ContextInitializer`].
46    /// 
47    pub fn data(&self) -> Option<&dyn Any> {self.ctx_reg().context_data()}
48
49    /// Returns a mutable reference to the Context Data associated with the current context.
50    ///
51    /// Context Data is user-defined data bound to the context, sharing the same
52    /// lifetime as the context itself.
53    ///
54    /// It can only be set via the first return value of [`ContextInitializer`].
55    /// 
56    pub fn data_mut(&mut self) -> Option<&mut dyn Any> {self.ctx_reg_mut().context_data_mut()}
57
58    /// Calls the constructor for the specified class.
59    /// 
60    pub fn construct (&self, class: UCStr, args: Option<&[Object]>) -> Result<NonNullObject<'a>, ExternalError<'a>> {
61        let args = args.unwrap_or_default();
62        debug_assert!(args.len() <= u32::MAX as usize);
63        let mut object = MaybeUninit::<FREObject>::uninit();
64        let mut thrown = MaybeUninit::<FREObject>::uninit();
65        let r = unsafe {FRENewObject(class.as_ptr(), args.len() as u32, transmute(args.as_ptr()), object.as_mut_ptr(), thrown.as_mut_ptr())};
66        if let Some(e) = ExternalError::try_from(r, Some(unsafe {transmute(thrown)})) {
67            Err(e)
68        }else{
69            let object = unsafe {object.assume_init()};
70            assert!(!object.is_null());
71            Ok(unsafe {transmute(object)})
72        }
73    }
74
75    /// Calls a method associated with the current context.
76    ///
77    /// Methods can only be set via the second return value of [`ContextInitializer`].
78    /// 
79    /// [`Err`]=> [`ContextError::MethodsNotRegistered`], [`ContextError::MethodNotFound`];
80    /// 
81    pub fn call_method (&mut self, name: &str, args: Option<&[Object]> ) -> Result<Object<'a>, ContextError> {
82        self.ctx_reg_mut().methods.as_ref()
83            .ok_or(ContextError::MethodsNotRegistered)
84            .map(|ms| ms.get(name))?
85            .ok_or(ContextError::MethodNotFound)
86            .map(|(func, data)| {
87                let args = args.unwrap_or_default();
88                let argc = args.len() as u32;
89                let argv = args.as_ptr() as *const FREObject;
90                let r = unsafe {func(self.as_ptr(), data, argc, argv)};
91                unsafe {transmute(r)}
92            })
93    }
94
95    /// Returns the ActionScript-side Context Data associated with the current context.
96    /// 
97    /// `ExtensionContext.actionScriptData`
98    /// 
99    pub fn get_actionscript_data (&self) -> Object<'a>
100    {self.as_cooperative_ctx().get_actionscript_data().expect("INVARIANT: `CurrentContext` is always valid.")}
101
102    /// Sets the ActionScript-side Context Data associated with the current context.
103    /// 
104    /// `ExtensionContext.actionScriptData`
105    /// 
106    pub fn set_actionscript_data (&self, object: Object<'_>)
107    {self.as_cooperative_ctx().set_actionscript_data(object).expect("INVARIANT: `CurrentContext` is always valid.");}
108}
109impl<'a> CurrentContext<'a> {
110    fn new(ctx: &'a FREContext) -> Self {
111        Self(ContextHandle::new(*ctx).expect("INVARIANT: `CurrentContext` is always valid."), PhantomData)
112    }
113    fn as_cooperative_ctx(&self) -> CooperativeContext<'a> {unsafe {transmute(self.0)}}
114    fn ctx_reg(&self) -> &ContextRegistry {
115        let ptr = self.as_cooperative_ctx()
116            .with(|ctx_reg|ctx_reg as *const ContextRegistry)
117            .expect("INVARIANT: `CurrentContext` is unique.");
118        unsafe {&*(ptr)}
119    }
120    fn ctx_reg_mut(&mut self) -> &mut ContextRegistry {
121        let ptr = self.as_cooperative_ctx()
122            .with_mut(|ctx_reg|ctx_reg as *mut ContextRegistry)
123            .expect("INVARIANT: `CurrentContext` is unique.");
124        unsafe {&mut *(ptr)}
125    }
126}
127unsafe impl Sealed for CurrentContext<'_> {}
128impl<'a> Context<'a> for CurrentContext<'a> {
129    fn as_handle (&self) -> ContextHandle {self.0}
130    fn is_valid(&self) -> bool {
131        debug_assert!(self.as_cooperative_ctx().is_valid());
132        true
133    }
134}
135
136
137/// A handle to a context created by the current extension,
138/// which may become invalid under specific conditions.
139///
140/// Invalidity only occurs after the associated `ExtensionContext` object
141/// has been disposed. Therefore, callers should be prepared for operations
142/// on the 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/// 
147/// # Invariants
148/// 
149/// The context must have been constructed by the current extension via
150/// [`extension!`] and [`ContextInitializer`], and it must not be the [`CurrentContext`].
151/// Violating these invariants may results in undefined behavior.
152///
153#[derive(Debug, Clone, Copy, PartialEq, Eq)]
154#[repr(transparent)]
155pub struct CooperativeContext <'a> (ContextHandle, PhantomData<&'a()>);
156impl<'a> CooperativeContext<'a> {
157    
158    /// Returns the associated context from an AS3 `ExtensionContext` object.
159    /// 
160    /// # Safety
161    ///
162    /// `context` must be another context constructed by the current extension
163    /// via [`extension!`] and [`ContextInitializer`]. Otherwise, the invariants
164    /// of [`CooperativeContext`] are violated, and its internal APIs
165    /// for accessing native data will result in undefined behavior.
166    /// 
167    /// # Errors
168    /// 
169    /// - [`ContextError::FfiCallFailed`] If `context` is not an `ExtensionContext` object.
170    /// - [`ContextError::ContextConflict`] If it is associated with the current context.
171    /// 
172    pub unsafe fn from_object(context: NonNullObject<'a>) -> Result<Self, ContextError> {ForeignContext::from_object(context).map(|ctx|Self(ctx.0, PhantomData))}
173
174
175    /// Provides immutable access to the [`ContextRegistry`] within a closure.
176    /// 
177    /// # Errors
178    /// 
179    /// - [`ContextError::InvalidContext`]
180    /// - [`ContextError::NullRegistry`]
181    /// - [`ContextError::InvalidRegistry`]
182    /// - [`ContextError::FfiCallFailed`]
183    /// - [`ContextError::BorrowRegistryConflict`]
184    /// 
185    pub fn with <F, R> (self, f: F) -> Result<R, ContextError>
186    where F: FnOnce (&ContextRegistry) -> R {
187        if !self.is_valid() {return Err(ContextError::InvalidContext)}
188        let handle = self.as_foreign_ctx().get_native_data()?
189            .ok_or(ContextError::NullRegistry)?;
190        let cell = unsafe {RefCell::<ContextRegistry>::ref_from(handle)}
191            .map_err(|_| ContextError::InvalidRegistry)?;
192        let ctx_reg = cell.try_borrow()
193            .map_err(|_| ContextError::BorrowRegistryConflict)?;
194        let r = f(&ctx_reg);
195        Ok(r)
196    }
197
198    /// Provides mutable access to the [`ContextRegistry`] within a closure.
199    /// 
200    /// # Errors
201    /// 
202    /// - [`ContextError::InvalidContext`]
203    /// - [`ContextError::NullRegistry`]
204    /// - [`ContextError::InvalidRegistry`]
205    /// - [`ContextError::FfiCallFailed`]
206    /// - [`ContextError::BorrowRegistryConflict`]
207    /// 
208    pub fn with_mut <F, R> (self, f: F) -> Result<R, ContextError>
209    where F: FnOnce (&mut ContextRegistry) -> R {
210        if !self.is_valid() {return Err(ContextError::InvalidContext)}
211        let handle = self.as_foreign_ctx().get_native_data()?
212            .ok_or(ContextError::NullRegistry)?;
213        let cell = unsafe {RefCell::<ContextRegistry>::ref_from(handle)}
214            .map_err(|_| ContextError::InvalidRegistry)?;
215        let mut ctx_reg = cell.try_borrow_mut()
216            .map_err(|_| ContextError::BorrowRegistryConflict)?;
217        let r = f(&mut ctx_reg);
218        Ok(r)
219    }
220
221    /// Calls a registered function by name through an internal call within the current extension.
222    /// 
223    /// Nested calls increase complexity. Callers must consider borrowing rules and context validity.
224    /// 
225    /// # Errors
226    /// 
227    /// - [`ContextError::InvalidContext`]
228    /// - [`ContextError::NullRegistry`]
229    /// - [`ContextError::InvalidRegistry`]
230    /// - [`ContextError::FfiCallFailed`]
231    /// - [`ContextError::BorrowRegistryConflict`]
232    /// - [`ContextError::MethodNotFound`]
233    /// 
234    pub fn call_method (self, name: &str, args: Option<&[Object<'a>]> ) -> Result<Object<'a>, ContextError> {
235        let (func, data) = self.with(|ctx_reg| {
236            ctx_reg.methods.as_ref()
237            .expect("INVARIANT: `CooperativeContext` is not current context and has been initialized with `MethodSet`.")
238            .get(name)
239            .ok_or(ContextError::MethodNotFound)
240        })??;
241        let args = args.unwrap_or_default();
242        let argc = args.len() as u32;
243        let argv = args.as_ptr() as *const FREObject;
244        let r = unsafe {func(self.as_ptr(), data, argc, argv)};
245        Ok(unsafe {transmute(r)})
246    }
247    
248    /// Returns the ActionScript-side Context Data associated with the context.
249    /// 
250    /// `ExtensionContext.actionScriptData`
251    /// 
252    /// Only fails if the context is invalid.
253    /// 
254    pub fn get_actionscript_data (self) -> Result<Object<'a>, FfiError>
255    {self.as_foreign_ctx().get_actionscript_data()}
256    
257    
258    /// Sets the ActionScript-side Context Data associated with the context.
259    /// 
260    /// `ExtensionContext.actionScriptData`
261    /// 
262    /// Only fails if the context is invalid.
263    /// 
264    pub fn set_actionscript_data (self, object: Object<'_>) -> Result<(), FfiError>
265    {self.as_foreign_ctx().set_actionscript_data(object)}
266
267
268    fn as_foreign_ctx(self) -> ForeignContext<'a> {unsafe {transmute(self)}}
269}
270unsafe impl Sealed for CooperativeContext<'_> {}
271impl<'a> Context<'a> for CooperativeContext<'a> {
272    fn as_handle (&self) -> ContextHandle {self.0}
273    fn is_valid(&self) -> bool {self.get_actionscript_data().is_ok()}
274}
275
276
277/// A handle to a context that may become invalid under specific conditions.
278/// 
279/// Assumes the context was NOT constructed by the current extension.
280/// Accessing its associated native data is therefore unsafe.
281///
282/// Invalidity only occurs after the associated `ExtensionContext` object
283/// has been disposed. Therefore, callers should be prepared for operations
284/// on the context to fail at appropriate points.
285///
286/// This crate leverages [`FREGetFREContextFromExtensionContext`] to enable
287/// more advanced use cases, but doing so also increases overall complexity.
288///
289#[derive(Debug, Clone, Copy, PartialEq, Eq)]
290#[repr(transparent)]
291pub struct ForeignContext <'a> (ContextHandle, PhantomData<&'a()>);
292impl<'a> ForeignContext<'a> {
293    
294    /// Returns the associated context from an AS3 `ExtensionContext` object.
295    /// 
296    /// # Errors
297    /// 
298    /// - [`ContextError::FfiCallFailed`] If `context` is not an `ExtensionContext` object.
299    /// - [`ContextError::ContextConflict`] If it is associated with the current context.
300    /// 
301    pub fn from_object(context: NonNullObject<'a>) -> Result<Self, ContextError> {
302        let mut handle = MaybeUninit::<FREContext>::uninit();
303        let r = unsafe {FREGetFREContextFromExtensionContext(context.as_ptr(), handle.as_mut_ptr())};
304        if let Ok(e) = FfiError::try_from(r) {return Err(e.into());}
305        let handle = ContextHandle::new(unsafe {handle.assume_init()}).expect("Unexpected null pointer.");
306        if handle == stack::current_context().0 {return Err(ContextError::ContextConflict);}
307        Ok(Self(handle, PhantomData))
308    }
309
310    /// Returns a pointer to the native data. Callers must understand its memory
311    /// layout and explicitly treat it as either a borrow or a move.
312    /// 
313    /// Only fails if the context is invalid.
314    /// 
315    pub fn get_native_data (self) -> Result<Option<NonNullFREData>, FfiError> {
316        let mut data = MaybeUninit::<FREData>::uninit();
317        let r = unsafe {FREGetContextNativeData(self.as_ptr(), data.as_mut_ptr())};
318        let data = unsafe {data.assume_init()};
319        if let Ok(e) = FfiError::try_from(r) {Err(e)} else {Ok(NonNullFREData::new(data))}
320    }
321
322    /// Sets the native data pointer for the context.
323    ///
324    /// Only fails if the context is invalid.
325    /// 
326    /// # Safety
327    ///
328    /// Callers must ensure that no valid native data is currently set,
329    /// or that any previously associated native data has become invalid
330    /// and its memory has been properly released.
331    ///
332    /// If `data` is a non-null pointer, it must have well-defined ownership:
333    /// it must be treated explicitly as either a borrow or a move.
334    ///
335    /// If treated as a move, callers must ensure that the memory is properly
336    /// released before [`FREContextFinalizer`] completes.
337    /// 
338    #[allow(unsafe_op_in_unsafe_fn)]
339    pub unsafe fn set_native_data (self, data: Option<NonNullFREData>) -> Result<(), FfiError> {
340        let data: *mut c_void = data.map(|ptr|ptr.as_ptr())
341            .unwrap_or_default();
342        let r = FRESetContextNativeData(self.as_ptr(), data);
343        if let Ok(e) = FfiError::try_from(r) {Err(e)} else {Ok(())}
344    }
345
346    /// Returns the ActionScript-side Context Data associated with the context.
347    /// 
348    /// `ExtensionContext.actionScriptData`
349    /// 
350    /// Only fails if the context is invalid.
351    /// 
352    pub fn get_actionscript_data (self) -> Result<Object<'a>, FfiError> {
353        let mut object = MaybeUninit::<FREObject>::uninit();
354        let r = unsafe {FREGetContextActionScriptData(self.as_ptr(), object.as_mut_ptr())};
355        if let Ok(e) = FfiError::try_from(r) {Err(e)} else {Ok(unsafe {transmute(object)})}
356    }
357    
358    /// Sets the ActionScript-side Context Data associated with the context.
359    /// 
360    /// `ExtensionContext.actionScriptData`
361    /// 
362    /// Only fails if the context is invalid.
363    /// 
364    pub fn set_actionscript_data (self, object: Object<'_>) -> Result<(), FfiError> {
365        let r = unsafe {FRESetContextActionScriptData(self.as_ptr(), object.as_ptr())};
366        if let Ok(e) = FfiError::try_from(r) {Err(e)} else {Ok(())}
367    }
368
369}
370unsafe impl Sealed for ForeignContext<'_> {}
371impl<'a> Context<'a> for ForeignContext<'a> {
372    fn as_handle (&self) -> ContextHandle {self.0}
373    fn is_valid(&self) -> bool {self.get_actionscript_data().is_ok()}
374}
375
376
377#[allow(private_bounds)]
378pub trait Context<'a>: Sealed {
379    fn as_handle (&self) -> ContextHandle;
380    fn as_ptr (&self) -> FREContext {self.as_handle().as_ptr()}
381    
382    /// Returns whether the context is valid.
383    ///
384    /// The context remains valid until [`FREContextFinalizer`] has completed.
385    /// Invalidity only occurs when the associated `ExtensionContext` object
386    /// is destructed or its `dispose` method is explicitly called.
387    /// 
388    fn is_valid(&self) -> bool;
389
390    /// Sends a message to the debugger output.
391    /// 
392    /// Delivery is not guaranteed; the `message` may not be presented.
393    /// 
394    /// # Examples
395    /// 
396    /// ```
397    /// use fre_rs::prelude::*;
398    /// fn func <'a> (ctx: &mut CurrentContext<'a>, args: &[Object<'a>]) {
399    ///     ctx.trace("Hello, Flash runtime!");
400    ///     ctx.trace(args);
401    ///     ctx.trace(args[0]);
402    /// }
403    /// ```
404    /// 
405    fn trace <T: ToUcstrLossy + Sized> (&self, message: T) {
406        let r = unsafe {FRETrace(self.as_ptr(), message.to_ucstr_lossy().as_ptr())};
407        debug_assert!(r.is_ok(), "{}", FfiError::try_from(r).unwrap());
408    }
409
410    /// Returns an [`EventDispatcher`] used to perform asynchronous callbacks
411    /// via the AS3 event system.
412    /// 
413    fn event_dispatcher(&self) -> EventDispatcher {EventDispatcher(self.as_handle())}
414
415    /// Returns the render mode of the stage.
416    /// 
417    /// If `stage` is [`None`], the main/initial stage is used.
418    /// 
419    fn get_render_mode (&self, stage: Option<as3::Stage<'a>>) -> RenderMode {
420        let stage: Object = stage.into();
421        let mut mode = MaybeUninit::<FRERenderMode>::uninit();
422        let r = unsafe {FREGetRenderMode(self.as_ptr(), stage.as_ptr(), mode.as_mut_ptr())};
423        debug_assert!(r.is_ok(), "{}", FfiError::try_from(r).unwrap());
424        let mode = unsafe {mode.assume_init()};
425        RenderMode::from(mode)
426    }
427
428    /// [`FRESetRenderSource`]
429    /// 
430    /// Returns [`Err`] if `display_object` has an incorrect type.
431    /// 
432    fn set_render_source (&self, source: as3::MediaBuffer<'a>, display_object: NonNullObject<'a>) -> Result<(), FfiError> {
433        let r = unsafe {FRESetRenderSource(self.as_ptr(), source.as_ptr(), display_object.as_ptr())};
434        if let Ok(e) = FfiError::try_from(r) {Err(e)} else {Ok(())}
435    }
436
437    /// [`FREMediaBufferLock`] [`FREMediaBufferUnlock`]
438    /// 
439    fn with_media_buffer <F, R> (&self, media_buffer: as3::MediaBuffer<'a>, f: F) -> R 
440    where F: FnOnce (MediaBufferDataAdapter) -> R {
441        let mut bytes = MaybeUninit::<FREBytes>::uninit();
442        let mut width = MaybeUninit::<u32>::uninit();
443        let mut height = MaybeUninit::<u32>::uninit();
444        let mut stride = MaybeUninit::<u32>::uninit();
445        let mut format = MaybeUninit::<u32>::uninit();
446        let result = unsafe {FREMediaBufferLock(self.as_ptr(), media_buffer.as_ptr(), bytes.as_mut_ptr(), width.as_mut_ptr(), height.as_mut_ptr(), stride.as_mut_ptr(), format.as_mut_ptr())};
447        debug_assert!(result.is_ok(), "{}", FfiError::try_from(result).unwrap());
448        let (bytes, width, height, stride, format) = unsafe {(
449            bytes.assume_init(),
450            width.assume_init(),
451            height.assume_init(),
452            stride.assume_init(),
453            format.assume_init(),
454        )};
455        assert!(!bytes.is_null());
456        let adapter = unsafe {MediaBufferDataAdapter::new(bytes, width, height, stride, format)};
457        let r = f(adapter);
458        let result = unsafe {FREMediaBufferUnlock(self.as_ptr(), media_buffer.as_ptr(), u32::default())};
459        debug_assert!(result.is_ok(), "{}", FfiError::try_from(result).unwrap());
460        r
461    }
462}
463
464
465/// The extension-side concrete representation of a [`Context`].
466///
467/// This can be considered the actual context instance, while [`Context`]
468/// serves as an abstract handle or outer wrapper around it.
469/// 
470/// Can only be constructed through [`CurrentContext::with_context_initializer`].
471/// 
472#[derive(Debug)]
473pub struct ContextRegistry {
474    ctx_type: Option<UCStr>,
475    ctx_data: Option<Box<dyn Any>>,
476    ext_data: Option<ExtensionData>,// &'extension mut
477    methods: Option<MethodSet>,
478}
479impl ContextRegistry {
480
481    /// Returns the context type associated with the context.
482    ///
483    /// This corresponds to the `contextType` argument passed to
484    /// `ExtensionContext.createExtensionContext`.
485    ///
486    /// Returns [`None`] if that argument was `null`.
487    /// 
488    pub fn context_type(&self) -> Option<UCStr> {(self.ctx_type).clone()}
489
490    
491    /// Returns an immutable reference to the Context Data associated with the context.
492    ///
493    /// Context Data is user-defined data bound to the context, sharing the same
494    /// lifetime as the context itself.
495    ///
496    /// It can only be set via the first return value of [`ContextInitializer`].
497    /// 
498    pub fn context_data(&self) -> Option<&dyn Any> {self.ctx_data.as_ref().map(|d|d.as_ref())}
499
500    /// Returns a mutable reference to the Context Data associated with the context.
501    ///
502    /// Context Data is user-defined data bound to the context, sharing the same
503    /// lifetime as the context itself.
504    ///
505    /// It can only be set via the first return value of [`ContextInitializer`].
506    /// 
507    pub fn context_data_mut(&mut self) -> Option<&mut dyn Any> {self.ctx_data.as_mut().map(|d|d.as_mut())}
508
509    /// Provides access to the Extension Data.
510    ///
511    /// The Extension Data is set from the return value of [`Initializer`].
512    /// It can be accessed across threads and is synchronized via a [`Mutex`],
513    /// providing exclusive access on each call.
514    ///
515    /// Calling this method within nested [`Function`] invocations can lead
516    /// to deadlocks. It is recommended to avoid accessing it within a
517    /// [`Function`] call stack, and instead perform synchronization between
518    /// Context Data and Extension Data in [`ContextInitializer`] and [`ContextFinalizer`].
519    /// 
520    pub fn with_extension_data <F, R> (&self, f: F) -> Option<R>
521    where F: FnOnce (&mut dyn Any) -> R {
522        let ext_data = self.ext_data.as_ref()?;
523        let mut ext_data = ext_data.lock().expect("Mutex poisoned.");
524        let r = f(ext_data.as_mut());
525        Some(r)
526    }
527    
528    fn new (ext_data: FREData, ctx_type: Option<UCStr>) -> RefCell<Self> {
529        let ext_data = NonNullFREData::new(ext_data)
530            .map(|raw| {
531                Arc::clone(unsafe {
532                    <ExtensionData as Data>::ref_from(raw)
533                }.unwrap())
534            });
535        RefCell::new(Self {
536            ctx_type,
537            ctx_data: None,
538            ext_data,
539            methods: None,
540        })
541    }
542}
543impl Data for RefCell<ContextRegistry> {}
544
545
546pub(crate) mod stack {
547    use super::*;
548    thread_local! {static STACK: RefCell<Vec<ContextHandle>> = RefCell::new(Vec::new());}
549    pub(super) fn push(ctx: ContextHandle) {STACK.with_borrow_mut(|stack|stack.push(ctx));}
550    pub(super) fn pop() -> Option<ContextHandle> {STACK.with_borrow_mut(|stack|stack.pop())}
551    pub(crate) fn current_context<'a> () -> CurrentContext<'a> {
552        let handle = STACK.with_borrow(|stack|stack.last().cloned())
553            .expect("Outside the scope of a Flash runtime function call; no context available.");
554        unsafe {transmute(handle)}
555    }
556    
557    /// A wrapper used by [`FREContextInitializer`], [`FREContextFinalizer`], and
558    /// [`FREFunction`] that provides a safe stack-level execution environment.
559    ///
560    /// **In typical usage of this crate, this function should not be called directly.**
561    /// 
562    /// # Safety
563    /// 
564    /// While all operations performed within this function are safe at the stack level,
565    /// calling this function itself is unsafe and requires that all input arguments
566    /// are valid. In particular, this function assumes it is invoked directly with
567    /// arguments provided by the Flash runtime.
568    ///
569    /// Violating these assumptions may lead to undefined behavior.
570    /// 
571    #[allow(unsafe_op_in_unsafe_fn)]
572    pub unsafe fn with <'a, F, R> (ctx: &'a FREContext, f: F) -> R
573    where
574        F: FnOnce (&mut CurrentContext<'a>) -> R,
575        R: 'a,
576    {
577        let mut ctx = CurrentContext::new(ctx);
578        stack::push(ctx.0);
579        let r = f(&mut ctx);
580        let popped = stack::pop();
581        debug_assert_eq!(ctx.0, popped.expect("Context unexpectedly missing from stack."), "The context pushed at the beginning of this function must be popped at the end.");
582        r
583    }
584    
585    /// A wrapper around [`FREContextInitializer`] that provides a safe stack-level
586    /// execution environment for context initialization.
587    /// 
588    /// **In typical usage of this crate, this function should not be called directly.**
589    ///
590    /// # Safety
591    /// 
592    /// While all operations performed within this function are safe at the stack level,
593    /// calling this function itself is unsafe and requires the following conditions:
594    ///
595    /// - The native data associated with [`Context`] must not be accessed or managed
596    ///   by external code.
597    /// - This function will construct a [`ContextRegistry`] and assign it as the native data.
598    ///   The constructed [`ContextRegistry`] must be properly disposed in
599    ///   [`FREContextFinalizer`] to ensure its lifecycle is correctly terminated.
600    /// - This function assumes it is invoked directly with arguments provided by the
601    ///   Flash runtime, meaning all arguments must be valid and consistent.
602    ///
603    /// Violating these assumptions may lead to undefined behavior.
604    /// 
605    #[allow(unsafe_op_in_unsafe_fn)]
606    pub unsafe fn with_initializer <'a, F> (
607        ext_data: FREData,// &'extension mut
608        ctx_type: FREStr,// &'function
609        ctx: &'a FREContext,// &'function
610        num_funcs_to_set: *mut u32,// return
611        funcs_to_set: *mut *const FRENamedFunction,// return &'context mut
612        f: F
613    )
614    where F: FnOnce (&mut CurrentContext<'a>) -> (Option<Box<dyn Any>>, FunctionSet)
615    {
616        assert!(!num_funcs_to_set.is_null());
617        assert!(!funcs_to_set.is_null());
618        stack::with(ctx, |ctx|{
619            let ctx_type = if ctx_type.is_null() {None} else {
620                let ctx_type = CStr::from_ptr(ctx_type as *const c_char);
621                let ctx_type = UCStr::try_from(ctx_type).expect("Input string is not valid UTF-8.");
622                Some(ctx_type)
623            };
624            let ctx_reg = ContextRegistry::new(ext_data, ctx_type).into_raw();
625            let r = ForeignContext(ctx.0, PhantomData).set_native_data(Some(ctx_reg));// <'context> move
626            assert!(r.is_ok());
627            let (ctx_data, funcs) = f(ctx);
628            let methods = MethodSet::from(funcs);
629            let r = methods.as_ref();
630            debug_assert!(r.len() <= u32::MAX as usize);
631            *num_funcs_to_set = r.len() as u32;
632            *funcs_to_set = r.as_ptr();
633            let ctx_reg = RefCell::<ContextRegistry>::mut_from(ctx_reg).unwrap();
634            let mut ctx_reg_mut = ctx_reg.borrow_mut();
635            ctx_reg_mut.ctx_data = ctx_data;
636            ctx_reg_mut.methods = Some(methods);
637        })
638    }
639
640    /// A wrapper around [`FREFunction`] that provides a safe stack-level execution
641    /// environment for the given closure.
642    /// 
643    /// **In typical usage of this crate, this function should not be called directly.**
644    ///
645    /// # Safety
646    /// 
647    /// While all operations performed within this function are safe at the stack level,
648    /// calling this function itself is unsafe and requires the following conditions:
649    ///
650    /// - `func_data` must either be constructed via [`Data::into_raw`] before
651    ///   [`ContextInitializer`] returns, or be a null pointer.
652    /// - This function assumes it is invoked directly with arguments provided by the
653    ///   Flash runtime, meaning all arguments must be valid and consistent.
654    ///
655    /// Violating these assumptions may lead to undefined behavior.
656    /// 
657    #[allow(unsafe_op_in_unsafe_fn)]
658    pub unsafe fn with_method <'a, F, R> (
659        ctx: &'a FREContext,// &'function
660        func_data: FREData,// &'function mut
661        argc: u32,
662        argv: *const FREObject,// &'function
663        f: F
664    ) -> FREObject
665    where
666        F: FnOnce (&mut CurrentContext<'a>, Option<&mut dyn Any>, &[Object<'a>]) -> R,
667        R: Into<Object<'a>> + 'a
668    {
669        assert!(!argv.is_null() || argc==0);
670        stack::with(ctx, |ctx| {
671            let func_data = NonNullFREData::new(func_data)
672                .map(|raw| crate::data::mut_from(raw));
673            let args = std::slice::from_raw_parts(argv as *const Object, argc as usize);
674            f(ctx, func_data, args).into().as_ptr()
675        })
676    }
677}
678