Skip to main content

fre_rs/types/
object.rs

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