Skip to main content

fre_rs/types/
object.rs

1use super::*;
2
3
4/// Although `'static`, it must not be used outside the Flash runtime main thread,
5/// or related APIs may return errors or panic due to failed assertions.
6#[allow(non_upper_case_globals)]
7pub const null: Object = Object::NULL;
8
9
10/// An abstraction over objects of all types.
11/// 
12/// **In typical usage of this crate, this trait should not be implemented directly.**
13///
14/// This trait represents a common interface for types that are backed by an [`FREObject`].
15/// It is only intended to be implemented when defining a new object type.
16///
17/// # Safety
18///
19/// Implementing this trait requires strict guarantees about memory layout.
20/// The implementing type must be layout-compatible with [`FREObject`].
21/// In practice, this means it must be annotated with `#[repr(transparent)]`
22/// and wrap the underlying [`FREObject`] without altering its representation.
23///
24/// This requirement exists because the methods provided by this trait may
25/// perform reinterpretation of the underlying memory. Failure to uphold
26/// these guarantees will result in undefined behavior.
27pub unsafe trait AsObject<'a>: Sized + Copy + Eq + Display + Into<FREObject> + Into<Object<'a>> {
28    const TYPE: Type;
29    fn as_object (self) -> Object<'a> {unsafe {transmute_unchecked(self)}}
30    fn as_ptr (self) -> FREObject {unsafe {transmute_unchecked(self)}}
31    fn is_null(self) -> bool {self.as_ptr().is_null()}
32    fn get_type(self) -> Type {
33        if self.is_null() {return Type::Null;}
34        let mut ty = FREObjectType(i32::default());
35        let r = unsafe {FREGetObjectType(self.as_ptr(), &mut ty)};
36        assert!(r.is_ok());
37        ty.into()
38    }
39    fn get_property (self, name: UCStr) -> Result<Object<'a>, ExternalError<'a>> {
40        let mut obj = std::ptr::null_mut();
41        let mut thrown = std::ptr::null_mut();
42        let r = unsafe {FREGetObjectProperty(self.as_ptr(), name.as_ptr(), &mut obj, &mut thrown)};
43        if let Ok(e) = ExternalError::try_from(r, Some(unsafe {transmute(thrown)})) {
44            Err(e)
45        }else{
46            Ok(unsafe {transmute(obj)})
47        }
48    }
49    fn set_property <O: AsObject<'a>> (self, name: UCStr, value: O) -> Result<(), ExternalError<'a>> {
50        let mut thrown = std::ptr::null_mut();
51        let r = unsafe {FRESetObjectProperty(self.as_ptr(), name.as_ptr(), value.as_ptr(), &mut thrown)};
52        if let Ok(e) = ExternalError::try_from(r, Some(unsafe {transmute(thrown)})) {
53            Err(e)
54        }else{
55            Ok(())
56        }
57    }
58    fn call_method (self, name: UCStr, args: Option<&[Object]>) -> Result<Object<'a>, ExternalError<'a>> {
59        let args = args.unwrap_or_default();
60        debug_assert!(args.len() <= u32::MAX as usize);
61        let mut obj = std::ptr::null_mut();
62        let mut thrown = std::ptr::null_mut();
63        let r = unsafe {FRECallObjectMethod(self.as_ptr(), name.as_ptr(), args.len() as u32, transmute(args.as_ptr()), &mut obj, &mut thrown)};
64        if let Ok(e) = ExternalError::try_from(r, Some(unsafe {transmute(thrown)})) {
65            Err(e)
66        }else{
67            Ok(unsafe {transmute(obj)})
68        }
69    }
70    /// Return [`Err`] if this is [`null`] or `undefined`.
71    #[allow(non_snake_case)]
72    fn toString (self) -> Result<StringObject<'a>, ExternalError<'a>> {
73        const NAME: UCStr = unsafe {UCStr::from_literal_unchecked(c"toString")};
74        self.call_method(NAME, None).map(|r|{unsafe {transmute(r)}})
75    }
76}
77impl<'a, O: AsObject<'a>> From<O> for UCStr {
78    fn from(value: O) -> Self {
79        value.to_string()
80            .replace('\0', "\\0")
81            .try_into()
82            .unwrap()
83    }
84}
85impl<'a, O: AsObject<'a>> From<&[O]> for UCStr {
86    fn from(value: &[O]) -> Self {
87        value.iter()
88            .map(|o|{o.to_string()})
89            .collect::<Vec<_>>()
90            .join(", ")
91            .replace('\0', "\\0")
92            .try_into()
93            .unwrap()
94    }
95}
96
97/// An abstraction for fallible casting between object types.
98///
99/// **In typical usage of this crate, this trait should not be implemented directly.**
100/// 
101/// Primarily used for object type casting, and should be implemented when defining a new type.
102pub unsafe trait TryAs<'a, T>: AsObject<'a> + TryInto<T>
103where T: AsObject<'a> {
104    fn try_as (self) -> Result<T, Type> {
105        let ty = self.get_type();
106        if ty == T::TYPE {
107            Ok(unsafe {transmute_unchecked(self)})
108        }else{Err(ty)}
109    }
110}
111unsafe impl<'a, O> TryAs<'a, Object<'a>> for O
112where O: AsObject<'a> {
113    fn try_as (self) -> Result<Object<'a>, Type> {Ok(self.as_object())}
114}
115
116
117/// A wrapper around [`FREObject`].
118///
119/// This type assumes that the underlying handle is always valid.
120/// The runtime is responsible for ensuring the correctness and lifetime
121/// of the handle.
122///
123/// Note that the handle may still be [`null`], depending on the API behavior.
124/// In such cases, [`null`] is treated as a valid value at the ABI level,
125/// but may represent the absence of an object.
126#[derive(Debug, Clone, Copy, PartialEq, Eq)]
127#[repr(transparent)]
128pub struct Object <'a> (FREObject, PhantomData<&'a()>);
129impl<'a> Object<'a> {
130    pub const NULL: Object<'static> = Object(std::ptr::null_mut(), PhantomData);
131    #[allow(unused_variables)]
132    pub fn new (frt: &FlashRuntime<'a>, class: UCStr, args: Option<&[Object]>) -> Result<Object<'a>, ExternalError<'a>> {
133        let args = args.unwrap_or_default();
134        debug_assert!(args.len() <= u32::MAX as usize);
135        let mut obj = std::ptr::null_mut();
136        let mut thrown = std::ptr::null_mut();
137        let r = unsafe {FRENewObject(class.as_ptr(), args.len() as u32, transmute(args.as_ptr()), &mut obj, &mut thrown)};
138        if let Ok(e) = ExternalError::try_from(r, Some(unsafe {transmute(thrown)})) {
139            Err(e)
140        }else{
141            assert!(!obj.is_null());
142            Ok(unsafe {transmute(obj)})
143        }
144    }
145
146    /// [`FRENativeWindow`] is only valid for the duration of this closure call.
147    /// 
148    /// Using [`null`] inside the closure is meaningless and may lead to unintended FFI call ordering.
149    /// 
150    /// This is a minimal safety wrapper around the underlying FFI. Its current placement,
151    /// shape, and usage are not ideal, and it is hoped that it can be refactored
152    /// if a more flexible C API becomes available in the AIR SDK.
153    pub fn with_native_window <F, R> (self, f: F) -> Result<R, FfiError>
154    where F: FnOnce (FRENativeWindow) -> R + Sync
155    {
156        let mut handle = std::ptr::null_mut();
157        let result = unsafe {FREAcquireNativeWindowHandle(self.as_ptr(), &mut handle)};
158        if let Ok(e) = FfiError::try_from(result) {return Err(e)};
159        let r = f(handle);
160        let result = unsafe {FREReleaseNativeWindowHandle(self.as_ptr())};
161        assert!(result.is_ok());
162        Ok(r)
163    }
164    /// Using [`null`] inside the closure is meaningless and may lead to unintended FFI call ordering.
165    /// 
166    /// This is a minimal safety wrapper around the underlying FFI. Its current placement,
167    /// shape, and usage are not ideal, and it is hoped that it can be refactored
168    /// if a more flexible C API becomes available in the AIR SDK.
169    pub fn with_native_window_3d <F, R> (self, f: F) -> Result<R, ExternalError<'a>>
170    where F: FnOnce (FRENativeWindow, &[Option<Context3D<'a>>]) -> R + Sync
171    {
172        const NAME_STAGE: UCStr = unsafe {UCStr::from_literal_unchecked(c"stage")};
173        const NAME_STAGE_3DS: UCStr = unsafe {UCStr::from_literal_unchecked(c"stage3Ds")};
174        const NAME_CONTEXT_3D: UCStr = unsafe {UCStr::from_literal_unchecked(c"context3D")};
175        let stage3ds: Vector = self.get_property(NAME_STAGE)?
176            .get_property(NAME_STAGE_3DS)?
177            .try_as()
178            .map_err(|_|ExternalError::C(FfiError::TypeMismatch))?;
179        let ctx3ds: Box<[Option<Context3D>]>  = stage3ds.iter()
180            .map(|stage3d|{
181                stage3d.get_property(NAME_CONTEXT_3D)
182                    .ok()
183            })
184            .map(|i|{
185                if let Some(ctx3d) = i {
186                    if ctx3d.is_null() {None} else {Some(unsafe {transmute(ctx3d)})}
187                } else {None}
188            })
189            .collect();
190        let mut handle = std::ptr::null_mut();
191        let result = unsafe {FREAcquireNativeWindowHandle(self.as_ptr(), &mut handle)};
192        if let Ok(e) = FfiError::try_from(result) {return Err(e.into())};
193        let r = f(handle, ctx3ds.as_ref());
194        let result = unsafe {FREReleaseNativeWindowHandle(self.as_ptr())};
195        debug_assert!(result.is_ok());
196        Ok(r)
197    }
198}
199impl TryFrom<Object<'_>> for i32 {
200    type Error = FfiError;
201    fn try_from(value: Object) -> Result<Self, Self::Error> {
202        let mut val = i32::default();
203        let r = unsafe {FREGetObjectAsInt32(value.0, &mut val)};
204        if let Ok(e) = r.try_into() {return Err(e);}
205        Ok(val)
206    }
207}
208impl TryFrom<Object<'_>> for u32 {
209    type Error = FfiError;
210    fn try_from(value: Object) -> Result<Self, Self::Error> {
211        let mut val = u32::default();
212        let r = unsafe {FREGetObjectAsUint32(value.0, &mut val)};
213        if let Ok(e) = r.try_into() {return Err(e);}
214        Ok(val)
215    }
216}
217impl TryFrom<Object<'_>> for f64 {
218    type Error = FfiError;
219    fn try_from(value: Object) -> Result<Self, Self::Error> {
220        let mut val = f64::default();
221        let r = unsafe {FREGetObjectAsDouble(value.0, &mut val)};
222        if let Ok(e) = r.try_into() {return Err(e);}
223        Ok(val)
224    }
225}
226impl TryFrom<Object<'_>> for bool {
227    type Error = FfiError;
228    fn try_from(value: Object) -> Result<Self, Self::Error> {
229        let mut val = u32::default();
230        let r = unsafe {FREGetObjectAsBool(value.0, &mut val)};
231        if let Ok(e) = r.try_into() {return Err(e);}
232        Ok(val != 0)
233    }
234}
235impl TryFrom<Object<'_>> for String {
236    type Error = FfiError;
237    fn try_from(value: Object) -> Result<Self, Self::Error> {
238        let mut len = u32::default();
239        let mut ptr = std::ptr::null();
240        let r = unsafe {FREGetObjectAsUTF8(value.0, &mut len, &mut ptr)};
241        if let Ok(e) = r.try_into() {return Err(e);}
242        let bytes = unsafe {std::slice::from_raw_parts(ptr, len as usize)};
243        let s = unsafe {str::from_utf8_unchecked(bytes)};
244        Ok(s.to_owned())
245    }
246}
247/// Attempts to cast an [`Object`] into an `ExtensionContext`, returning [`Err`] if it is not.
248///
249/// The returned [`Context`] must be used with caution. It is derived from external state,
250/// and its associated data may be unknown and not managed by this crate. As a result,
251/// the underlying memory may become invalid or outlive its expected lifetime.
252/// 
253/// This is a minimal safety wrapper around the underlying FFI. Its current placement,
254/// shape, and usage are not ideal, and it is hoped that it can be refactored
255/// if a more flexible C API becomes available in the AIR SDK.
256impl<'a> TryFrom<Object<'a>> for Context<'a> {
257    type Error = FfiError;
258    fn try_from(value: Object) -> Result<Self, Self::Error> {
259        let mut ctx = std::ptr::null_mut();
260        let r = unsafe {FREGetFREContextFromExtensionContext(value.0, &mut ctx)};
261        if let Ok(e) = r.try_into() {
262            Err(e)
263        } else {Ok(unsafe {transmute(ctx)})}
264    }
265}
266impl Display for Object<'_> {
267    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
268        if self.is_null() {return write!(f, "null");}
269        match self.toString() {
270            Ok(s) => {Display::fmt(s.value(), f)},
271            Err(e) => {
272                match e {
273                    ExternalError::C(e) => Display::fmt(&e, f),
274                    ExternalError::ActionScript(e) => {
275                        if let Ok(s) = e.thrown().toString().map(|s|{s.value()}) {
276                            write!(f, "{e} {s}")
277                        } else {Display::fmt(&e, f)}
278                    },
279                }
280            },
281        }
282    }
283}
284impl Default for Object<'_> {
285    fn default() -> Self {null}
286}
287unsafe impl<'a> AsObject<'a> for Object<'a> {const TYPE: Type = Type::Object;}
288impl From<()> for Object<'static> {fn from(_: ()) -> Self {null}}
289impl<'a, O: AsObject<'a>> From<Option<O>> for Object<'a> {
290    fn from(value: Option<O>) -> Self {
291        if let Some(obj) = value {
292            obj.as_object()
293        } else {null}
294    }
295}
296impl From<Object<'_>> for FREObject {fn from(value: Object) -> Self {value.as_ptr()}}