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