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