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