1use std::any::{Any as _, TypeId};
8use std::ptr;
9
10use mozjs::gc::Traceable;
11use mozjs::jsapi::{Heap, JSObject, JSTracer};
12use mozjs::rust::{Handle, get_object_class};
13
14use crate::class::{NativeClass, PrototypeChain};
15
16pub trait NativeObject: Traceable + Sized + 'static {
17 fn reflector(&self) -> &Reflector;
18}
19
20pub unsafe trait DerivedFrom<T: Castable>: Castable {}
21
22unsafe impl<T: Castable> DerivedFrom<T> for T {}
23
24pub trait Castable: NativeObject {
25 fn is<T>(&self) -> bool
26 where
27 T: NativeObject,
28 {
29 let class = unsafe { get_object_class(self.reflector().get()) };
30 if class.is_null() {
31 return false;
32 }
33
34 unsafe {
35 (*class.cast::<NativeClass>())
36 .prototype_chain
37 .iter()
38 .any(|proto| proto.type_id() == TypeId::of::<T>())
39 }
40 }
41
42 fn upcast<T: Castable>(&self) -> &T
43 where
44 Self: DerivedFrom<T>,
45 {
46 unsafe { &*ptr::from_ref(self).cast::<T>() }
47 }
48
49 fn downcast<T>(&self) -> Option<&T>
50 where
51 T: DerivedFrom<Self> + NativeObject,
52 {
53 self.is::<T>().then(|| unsafe { &*ptr::from_ref(self).cast::<T>() })
54 }
55}
56
57#[derive(Debug, Default)]
58pub struct Reflector(Heap<*mut JSObject>);
59
60impl Reflector {
61 pub fn new() -> Reflector {
62 Reflector::default()
63 }
64
65 pub fn get(&self) -> *mut JSObject {
66 self.0.get()
67 }
68
69 pub fn handle(&self) -> Handle<'_, *mut JSObject> {
70 unsafe { Handle::from_raw(self.0.handle()) }
71 }
72
73 pub(super) fn set(&self, obj: *mut JSObject) {
74 assert!(self.0.get().is_null());
75 assert!(!obj.is_null());
76 self.0.set(obj);
77 }
78
79 #[doc(hidden)]
80 pub const fn __ion_native_prototype_chain() -> PrototypeChain {
81 PrototypeChain::new()
82 }
83}
84
85unsafe impl Traceable for Reflector {
86 unsafe fn trace(&self, trc: *mut JSTracer) {
87 unsafe {
88 self.0.trace(trc);
89 }
90 }
91}
92
93impl NativeObject for Reflector {
94 fn reflector(&self) -> &Reflector {
95 self
96 }
97}
98
99impl Castable for Reflector {}