Skip to main content

fre_rs/types/
object.rs

1use super::*;
2use crate::__private::Sealed;
3
4
5/// An abstraction over all AS3 objects.
6/// 
7/// This trait is **`Sealed`** and should not be implemented manually.
8/// 
9/// To define a new AS3 class, use the [`class!`] macro.
10/// 
11/// # Safety
12/// 
13/// Implementing this trait requires strict guarantees about memory layout.
14/// The implementing type must be layout-compatible with [`FREObject`].
15/// In practice, this means it must be annotated with `#[repr(transparent)]`
16/// and wrap the underlying [`FREObject`] without altering its representation.
17/// 
18/// This requirement exists because the methods provided by this trait may
19/// perform reinterpretation of the underlying memory. Failure to uphold
20/// these guarantees will result in undefined behavior.
21/// 
22pub unsafe trait AsObject<'a>: Sealed + Sized + Debug + Display + Copy + Into<Object<'a>> + Into<FREObject> {
23
24    /// The [`Type`] associated with the class.
25    ///
26    /// This does not represent the actual runtime type of the object.
27    /// 
28    const TYPE: Type;
29
30    #[inline]
31    fn as_object (self) -> Object<'a> {unsafe {transmute_unchecked(self)}}
32
33    /// Casts this object to `T` without checks.
34    /// 
35    /// Prefer to use [`TryAs::try_as`] instead whenever possible.
36    ///
37    /// # Safety
38    ///
39    /// The concrete object type must be fully determined, must be type-checked
40    /// and validated in AS3, and the conversion must strictly follow the
41    /// semantics of the AS3 `as` operator.
42    ///
43    /// Violating these requirements may cause related APIs to perform illegal FFI
44    /// calls, which may panic even if those APIs do not explicitly document panics.
45    /// Violations may also result in undefined behavior.
46    /// 
47    #[allow(unsafe_op_in_unsafe_fn)]
48    #[inline]
49    unsafe fn as_unchecked <T: AsObject<'a>> (self) -> T {T::from_unchecked(self)}
50
51    /// Casts an object to `Self` without checks.
52    /// 
53    /// Prefer to use [`TryAs::try_as`] instead whenever possible.
54    /// 
55    /// # Safety
56    /// 
57    /// The concrete object type must be fully determined, must be type-checked
58    /// and validated in AS3, and the conversion must strictly follow the
59    /// semantics of the AS3 `as` operator.
60    /// 
61    /// Violating these requirements may cause related APIs to perform illegal FFI
62    /// calls, which may panic even if those APIs do not explicitly document panics.
63    /// Violations may also result in undefined behavior.
64    /// 
65    #[inline]
66    unsafe fn from_unchecked <O: AsObject<'a>> (object: O) -> Self {
67        #[cfg(debug_assertions)]
68        if object.is_null() && Self::TYPE != Object::TYPE && Self::TYPE != Type::null {
69            panic!("Cannot cast `null` to `{}`.", Self::TYPE);
70        }
71        unsafe {transmute_unchecked(object)}
72    }
73
74    #[inline]
75    fn as_ptr (self) -> FREObject {unsafe {transmute_unchecked(self)}}
76
77    #[inline]
78    fn is_null(self) -> bool {false}
79
80    /// Returns the runtime type of the object.
81    /// 
82    /// The result depends on the underlying [`FREGetObjectType`] implementation.
83    /// Most unsupported types return [`Type::NonNullObject`].
84    /// 
85    fn get_type(self) -> Type {
86        let mut ty = MaybeUninit::<FREObjectType>::uninit();
87        let r = unsafe {FREGetObjectType(self.as_ptr(), ty.as_mut_ptr())};
88        if r.is_ok() {
89            let ty = unsafe {ty.assume_init()};
90            ty.into()
91        } else if r == FREResult::FRE_WRONG_THREAD {
92
93            // Assumes that only `as3::null` can cause `FREResult::FRE_WRONG_THREAD`.
94            Type::null
95        } else {unreachable!()}
96    }
97    
98    fn get_property (self, name: UCStr) -> Result<Object<'a>, ExternalError<'a>> {
99        let mut object = MaybeUninit::<FREObject>::uninit();
100        let mut thrown = MaybeUninit::<FREObject>::uninit();
101        let r = unsafe {FREGetObjectProperty(self.as_ptr(), name.as_ptr(), object.as_mut_ptr(), thrown.as_mut_ptr())};
102        if let Some(e) = ExternalError::try_from(r, Some(unsafe {transmute(thrown)})) {
103            Err(e)
104        }else{
105            Ok(unsafe {transmute(object)})
106        }
107    }
108
109    fn set_property <O: AsObject<'a>> (self, name: UCStr, value: O) -> Result<(), ExternalError<'a>> {
110        let mut thrown = MaybeUninit::<FREObject>::uninit();
111        let r = unsafe {FRESetObjectProperty(self.as_ptr(), name.as_ptr(), value.as_ptr(), thrown.as_mut_ptr())};
112        if let Some(e) = ExternalError::try_from(r, Some(unsafe {transmute(thrown)})) {
113            Err(e)
114        }else{
115            Ok(())
116        }
117    }
118
119    fn call_method (self, name: UCStr, args: Option<&[Object]>) -> Result<Object<'a>, ExternalError<'a>> {
120        let args = args.unwrap_or_default();
121        debug_assert!(args.len() <= u32::MAX as usize);
122        let mut object = MaybeUninit::<FREObject>::uninit();
123        let mut thrown = MaybeUninit::<FREObject>::uninit();
124        let r = unsafe {FRECallObjectMethod(self.as_ptr(), name.as_ptr(), args.len() as u32, transmute(args.as_ptr()), object.as_mut_ptr(), thrown.as_mut_ptr())};
125        if let Some(e) = ExternalError::try_from(r, Some(unsafe {transmute(thrown)})) {
126            Err(e)
127        }else{
128            Ok(unsafe {transmute(object)})
129        }
130    }
131
132    /// Returns [`Err`] when this is `null` or `undefined`.
133    /// 
134    /// Returns [`Err`] when an AS3 error occurs.
135    /// 
136    #[allow(non_snake_case)]
137    fn toString (self) -> Result<as3::String<'a>, ExternalError<'a>> {
138        self.call_method(crate::ucstringify!(toString), None).map(|r|r.try_as().unwrap())
139    }
140}
141impl<'a, O> From<O> for Object<'a>
142where O: AsNonNullObject<'a> {
143    fn from(object: O) -> Self {object.as_object()}
144}
145// crate::class!(...) => impl AsObject for ...
146
147
148/// Implemented for all classes except [`Object`], providing casting to [`NonNullObject`].
149/// 
150pub trait AsNonNullObject<'a>: AsObject<'a> {
151    #[inline]
152    fn as_non_null_object(self) -> NonNullObject<'a> {unsafe {self.as_unchecked()}}
153}
154// crate::class!(...) => impl AsNonNullObject for ...
155// crate::class!(...) => impl From<...> for NonNullObject
156
157
158/// An abstraction for fallible casting between object types.
159///
160/// Implement [`TryFrom<Self, Error = Type>`] instead of implementing this trait directly.
161/// 
162pub trait TryAs<'a, T>: AsObject<'a> + TryInto<T, Error = Type>
163where T: AsObject<'a> + TryFrom<Self, Error = Type> {
164
165    /// This function must follow the semantics of the AS3 `as` operator.
166    /// 
167    fn try_as (self) -> Result<T, Type>;
168}
169impl<'a, O, T> TryAs<'a, T> for O
170where
171    O: AsObject<'a> + TryInto<T, Error = Type>,
172    T: AsObject<'a> + TryFrom<O, Error = Type>,
173{
174    fn try_as (self) -> Result<T, Type> {T::try_from(self)}
175}
176// crate::class!(...) => impl TryFrom<...> for Object
177// crate::class!(...) => impl TryFrom<...> for NonNullObject
178
179
180/// A reference to an AS3 object.
181///
182/// This type assumes that the underlying handle is always valid.
183/// The runtime is responsible for ensuring the correctness and lifetime
184/// of the handle.
185///
186/// The handle may still be [`as3::null`], depending on the API behavior.
187/// In such cases, [`as3::null`] is treated as a valid value at the ABI level,
188/// but may represent the absence of an object.
189/// 
190#[derive(Debug, Clone, Copy)]
191#[repr(transparent)]
192pub struct Object<'a> (FREObject, PhantomData<&'a ()>);
193
194/// A reference to a non-null AS3 object.
195/// 
196#[derive(Debug, Clone, Copy)]
197#[repr(transparent)]
198pub struct NonNullObject<'a> (NonNull<c_void>, PhantomData<&'a ()>);
199
200impl<'a> Object<'a> {
201    pub fn new (ctx: &CurrentContext<'a>) -> NonNullObject<'a> {NonNullObject::new(ctx)}
202    pub fn non_null (self) -> Option<NonNullObject<'a>> {NonNullObject::from_object(self)}
203}
204impl<'a> NonNullObject<'a> {
205    pub fn new (ctx: &CurrentContext<'a>) -> Self {ctx.construct(crate::ucstringify!(Object), None).unwrap()}
206    pub fn from_object (object: Object<'a>) -> Option<NonNullObject<'a>> {NonNull::new(object.0).map(|object|unsafe {transmute(object)})}
207}
208
209impl<'a> TryFrom<Object<'a>> for NonNullObject<'a> {
210    type Error = ();
211    fn try_from(object: Object<'a>) -> Result<Self, Self::Error> {Self::from_object(object).ok_or(())}
212}
213
214impl TryFrom<Object<'_>> for i32 {
215    type Error = FfiError;
216    fn try_from(object: Object) -> Result<Self, Self::Error> {
217        let mut value = MaybeUninit::<i32>::uninit();
218        let r = unsafe {FREGetObjectAsInt32(object.0, value.as_mut_ptr())};
219        if let Ok(e) = r.try_into() {return Err(e);}
220        let value = unsafe {value.assume_init()};
221        Ok(value)
222    }
223}
224impl TryFrom<&Object<'_>> for i32 {type Error = FfiError; fn try_from(object: &Object) -> Result<Self, Self::Error> {Self::try_from(object.as_object())}}
225impl TryFrom<&mut Object<'_>> for i32 {type Error = FfiError; fn try_from(object: &mut Object) -> Result<Self, Self::Error> {Self::try_from(object.as_object())}}
226impl TryFrom<NonNullObject<'_>> for i32 {type Error = FfiError; fn try_from(object: NonNullObject) -> Result<Self, Self::Error> {Self::try_from(object.as_object())}}
227impl TryFrom<&NonNullObject<'_>> for i32 {type Error = FfiError; fn try_from(object: &NonNullObject) -> Result<Self, Self::Error> {Self::try_from(object.as_object())}}
228impl TryFrom<&mut NonNullObject<'_>> for i32 {type Error = FfiError; fn try_from(object: &mut NonNullObject) -> Result<Self, Self::Error> {Self::try_from(object.as_object())}}
229
230impl TryFrom<Object<'_>> for u32 {
231    type Error = FfiError;
232    fn try_from(object: Object) -> Result<Self, Self::Error> {
233        let mut value = MaybeUninit::<u32>::uninit();
234        let r = unsafe {FREGetObjectAsUint32(object.0, value.as_mut_ptr())};
235        if let Ok(e) = r.try_into() {return Err(e);}
236        let value = unsafe {value.assume_init()};
237        Ok(value)
238    }
239}
240impl TryFrom<&Object<'_>> for u32 {type Error = FfiError; fn try_from(object: &Object) -> Result<Self, Self::Error> {Self::try_from(object.as_object())}}
241impl TryFrom<&mut Object<'_>> for u32 {type Error = FfiError; fn try_from(object: &mut Object) -> Result<Self, Self::Error> {Self::try_from(object.as_object())}}
242impl TryFrom<NonNullObject<'_>> for u32 {type Error = FfiError; fn try_from(object: NonNullObject) -> Result<Self, Self::Error> {Self::try_from(object.as_object())}}
243impl TryFrom<&NonNullObject<'_>> for u32 {type Error = FfiError; fn try_from(object: &NonNullObject) -> Result<Self, Self::Error> {Self::try_from(object.as_object())}}
244impl TryFrom<&mut NonNullObject<'_>> for u32 {type Error = FfiError; fn try_from(object: &mut NonNullObject) -> Result<Self, Self::Error> {Self::try_from(object.as_object())}}
245
246impl TryFrom<Object<'_>> for f64 {
247    type Error = FfiError;
248    fn try_from(object: Object) -> Result<Self, Self::Error> {
249        let mut value = MaybeUninit::<f64>::uninit();
250        let r = unsafe {FREGetObjectAsDouble(object.0, value.as_mut_ptr())};
251        if let Ok(e) = r.try_into() {return Err(e);}
252        let value = unsafe {value.assume_init()};
253        Ok(value)
254    }
255}
256impl TryFrom<&Object<'_>> for f64 {type Error = FfiError; fn try_from(object: &Object) -> Result<Self, Self::Error> {Self::try_from(object.as_object())}}
257impl TryFrom<&mut Object<'_>> for f64 {type Error = FfiError; fn try_from(object: &mut Object) -> Result<Self, Self::Error> {Self::try_from(object.as_object())}}
258impl TryFrom<NonNullObject<'_>> for f64 {type Error = FfiError; fn try_from(object: NonNullObject) -> Result<Self, Self::Error> {Self::try_from(object.as_object())}}
259impl TryFrom<&NonNullObject<'_>> for f64 {type Error = FfiError; fn try_from(object: &NonNullObject) -> Result<Self, Self::Error> {Self::try_from(object.as_object())}}
260impl TryFrom<&mut NonNullObject<'_>> for f64 {type Error = FfiError; fn try_from(object: &mut NonNullObject) -> Result<Self, Self::Error> {Self::try_from(object.as_object())}}
261
262impl TryFrom<Object<'_>> for bool {
263    type Error = FfiError;
264    fn try_from(object: Object) -> Result<Self, Self::Error> {
265        let mut value = MaybeUninit::<u32>::uninit();
266        let r = unsafe {FREGetObjectAsBool(object.0, value.as_mut_ptr())};
267        if let Ok(e) = r.try_into() {return Err(e);}
268        let value = unsafe {value.assume_init()};
269        Ok(value != 0)
270    }
271}
272impl TryFrom<&Object<'_>> for bool {type Error = FfiError; fn try_from(object: &Object) -> Result<Self, Self::Error> {Self::try_from(object.as_object())}}
273impl TryFrom<&mut Object<'_>> for bool {type Error = FfiError; fn try_from(object: &mut Object) -> Result<Self, Self::Error> {Self::try_from(object.as_object())}}
274impl TryFrom<NonNullObject<'_>> for bool {type Error = FfiError; fn try_from(object: NonNullObject) -> Result<Self, Self::Error> {Self::try_from(object.as_object())}}
275impl TryFrom<&NonNullObject<'_>> for bool {type Error = FfiError; fn try_from(object: &NonNullObject) -> Result<Self, Self::Error> {Self::try_from(object.as_object())}}
276impl TryFrom<&mut NonNullObject<'_>> for bool {type Error = FfiError; fn try_from(object: &mut NonNullObject) -> Result<Self, Self::Error> {Self::try_from(object.as_object())}}
277
278impl<'a> TryFrom<Object<'a>> for &'a str {
279    type Error = FfiError;
280    fn try_from(object: Object<'a>) -> Result<Self, Self::Error> {
281        let mut len = MaybeUninit::<u32>::uninit();
282        let mut ptr = MaybeUninit::<FREStr>::uninit();
283        let r = unsafe {FREGetObjectAsUTF8(object.0, len.as_mut_ptr(), ptr.as_mut_ptr())};
284        if let Ok(e) = r.try_into() {return Err(e);}
285        let len = unsafe {len.assume_init()};
286        let ptr = unsafe {ptr.assume_init()};
287        let bytes = unsafe {std::slice::from_raw_parts(ptr, len as usize)};
288        let s = unsafe {str::from_utf8_unchecked(bytes)};
289        Ok(s)
290    }
291}
292impl<'a> TryFrom<&Object<'a>> for &'a str {type Error = FfiError; fn try_from(object: &Object<'a>) -> Result<Self, Self::Error> {Self::try_from(object.as_object())}}
293impl<'a> TryFrom<&mut Object<'a>> for &'a str {type Error = FfiError; fn try_from(object: &mut Object<'a>) -> Result<Self, Self::Error> {Self::try_from(object.as_object())}}
294impl<'a> TryFrom<NonNullObject<'a>> for &'a str {type Error = FfiError; fn try_from(object: NonNullObject<'a>) -> Result<Self, Self::Error> {Self::try_from(object.as_object())}}
295impl<'a> TryFrom<&NonNullObject<'a>> for &'a str {type Error = FfiError; fn try_from(object: &NonNullObject<'a>) -> Result<Self, Self::Error> {Self::try_from(object.as_object())}}
296impl<'a> TryFrom<&mut NonNullObject<'a>> for &'a str {type Error = FfiError; fn try_from(object: &mut NonNullObject<'a>) -> Result<Self, Self::Error> {Self::try_from(object.as_object())}}
297
298impl Display for Object<'_> {
299    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
300        if let Some(obj) = NonNullObject::from_object(*self) {
301            Display::fmt(&obj, f)
302        } else {write!(f, "null")}
303    }
304}
305impl Display for NonNullObject<'_> {
306    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
307        match self.toString() {
308            Ok(s) => Display::fmt(s.value(), f),
309
310            // May mutually call with `<ExternalError as Display>::fmt`.
311            // Assumes thrown object's `toString` does not re-enter and cause an infinite call loop.
312            Err(ref e) => Display::fmt(e, f),
313        }
314    }
315}
316
317impl Default for Object<'_> {
318    fn default() -> Self {as3::null}
319}
320
321unsafe impl Sealed for Object<'_> {}
322unsafe impl Sealed for NonNullObject<'_> {}
323unsafe impl<'a> AsObject<'a> for Object<'a> {const TYPE: Type = Type::Named("Object");
324    #[inline]
325    fn is_null(self) -> bool {self.0.is_null() || self.get_type() == Type::null}
326}
327unsafe impl<'a> AsObject<'a> for NonNullObject<'a> {const TYPE: Type = Type::NonNullObject;}
328impl<'a> AsNonNullObject<'a> for NonNullObject<'a> {fn as_non_null_object(self) -> NonNullObject<'a> {self}}
329
330impl From<()> for Object<'_> {fn from(_: ()) -> Self {Self::default()}}
331impl<'a, O: AsObject<'a>> From<Option<O>> for Object<'a> {
332    fn from(value: Option<O>) -> Self {
333        if let Some(obj) = value {
334            obj.as_object()
335        } else {as3::null}
336    }
337}
338
339impl From<Object<'_>> for FREObject {fn from(object: Object) -> Self {object.as_ptr()}}
340impl From<NonNullObject<'_>> for FREObject {fn from(object: NonNullObject) -> Self {object.as_ptr()}}
341