1use std::any::{Any, TypeId};
8use std::cell::RefCell;
9use std::collections::HashMap;
10use std::ffi::c_void;
11use std::ptr;
12use std::ptr::NonNull;
13
14use mozjs::gc::Traceable;
15use mozjs::jsapi::{
16 JS_AddExtraGCRootsTracer, JS_GetContextPrivate, JS_RemoveExtraGCRootsTracer, JS_SetContextPrivate, JSContext,
17 JSTracer, Rooted,
18};
19use mozjs::rust::Runtime;
20use private::RootedArena;
21
22use crate::Local;
23use crate::class::ClassInfo;
24use crate::module::ModuleLoader;
25
26#[derive(Clone, Copy, Debug)]
28pub enum GCType {
29 Value,
30 Object,
31 String,
32 Script,
33 PropertyKey,
34 PropertyDescriptor,
35 Function,
36 BigInt,
37 Symbol,
38}
39
40pub trait TraceablePrivate: Traceable + Any {
41 fn as_any(&self) -> &dyn Any;
42
43 fn as_any_mut(&mut self) -> &mut dyn Any;
44}
45
46impl<T: Traceable + Any> TraceablePrivate for T {
47 fn as_any(&self) -> &dyn Any {
48 self
49 }
50
51 fn as_any_mut(&mut self) -> &mut dyn Any {
52 self
53 }
54}
55
56#[derive(Default)]
57pub struct ContextInner {
58 pub class_infos: HashMap<TypeId, ClassInfo>,
59 pub module_loader: Option<Box<dyn ModuleLoader>>,
60 private: Option<Box<dyn TraceablePrivate>>,
61}
62
63impl ContextInner {
64 unsafe fn add_tracer(cx: *mut JSContext, inner: *mut ContextInner) {
65 unsafe {
66 JS_AddExtraGCRootsTracer(cx, Some(ContextInner::trace), inner.cast::<c_void>());
67 }
68 }
69
70 pub unsafe fn remove_tracer(cx: *mut JSContext, inner: *mut ContextInner) {
71 unsafe {
72 JS_RemoveExtraGCRootsTracer(cx, Some(ContextInner::trace), inner.cast::<c_void>());
73 }
74 }
75
76 extern "C" fn trace(trc: *mut JSTracer, data: *mut c_void) {
77 unsafe {
78 let inner = &mut *data.cast::<ContextInner>();
79 inner.class_infos.values().for_each(|info| info.trace(trc));
80 inner.private.trace(trc);
81 }
82 }
83}
84
85pub struct Context {
89 context: NonNull<JSContext>,
90 rooted: RootedArena,
91 order: RefCell<Vec<GCType>>,
92 private: NonNull<ContextInner>,
93}
94
95impl Context {
96 pub fn from_runtime(rt: &Runtime) -> Context {
97 let cx = rt.cx();
98
99 let private = NonNull::new(unsafe { JS_GetContextPrivate(cx).cast::<ContextInner>() }).unwrap_or_else(|| {
100 let private = Box::<ContextInner>::default();
101 let private = Box::into_raw(private);
102 unsafe {
103 JS_SetContextPrivate(cx, private.cast());
104 ContextInner::add_tracer(cx, private);
105 }
106 unsafe { NonNull::new_unchecked(private) }
107 });
108
109 Context {
110 context: unsafe { NonNull::new_unchecked(cx) },
111 rooted: RootedArena::default(),
112 order: RefCell::new(Vec::new()),
113 private,
114 }
115 }
116
117 pub unsafe fn new_unchecked(cx: *mut JSContext) -> Context {
118 Context {
119 context: unsafe { NonNull::new_unchecked(cx) },
120 rooted: RootedArena::default(),
121 order: RefCell::new(Vec::new()),
122 private: unsafe { NonNull::new_unchecked(JS_GetContextPrivate(cx).cast::<ContextInner>()) },
123 }
124 }
125
126 pub fn as_ptr(&self) -> *mut JSContext {
127 self.context.as_ptr()
128 }
129
130 pub fn get_inner_data(&self) -> NonNull<ContextInner> {
131 self.private
132 }
133
134 pub fn get_raw_private(&self) -> *mut dyn TraceablePrivate {
135 let inner = self.get_inner_data();
136 ptr::from_mut(unsafe { (*inner.as_ptr()).private.as_deref_mut().unwrap() })
137 }
138
139 pub fn set_private(&self, private: Box<dyn TraceablePrivate>) {
140 let inner_private = self.get_inner_data();
141 unsafe {
142 (*inner_private.as_ptr()).private = Some(private);
143 }
144 }
145
146 pub fn root<T: Rootable>(&self, value: T) -> Local<'_, T> {
149 let root = T::alloc(&self.rooted, Rooted::new_unrooted(value));
150 self.order.borrow_mut().push(T::GC_TYPE);
151 Local::new(self, root)
152 }
153}
154
155pub trait Rootable: private::Sealed {}
156
157impl<T: private::Sealed> Rootable for T {}
158
159mod private {
160 use mozjs::gc::RootKind;
161 use mozjs::jsapi::{
162 BigInt, JSFunction, JSObject, JSScript, JSString, PropertyDescriptor, PropertyKey, Rooted, Symbol,
163 };
164 use mozjs::jsval::JSVal;
165 use typed_arena::Arena;
166
167 use super::GCType;
168
169 #[derive(Default)]
171 pub struct RootedArena {
172 pub values: Arena<Rooted<JSVal>>,
173 pub objects: Arena<Rooted<*mut JSObject>>,
174 pub strings: Arena<Rooted<*mut JSString>>,
175 pub scripts: Arena<Rooted<*mut JSScript>>,
176 pub property_keys: Arena<Rooted<PropertyKey>>,
177 pub property_descriptors: Arena<Rooted<PropertyDescriptor>>,
178 pub functions: Arena<Rooted<*mut JSFunction>>,
179 pub big_ints: Arena<Rooted<*mut BigInt>>,
180 pub symbols: Arena<Rooted<*mut Symbol>>,
181 }
182
183 #[expect(clippy::mut_from_ref)]
184 pub trait Sealed: RootKind + Copy + Sized {
185 const GC_TYPE: GCType;
186
187 fn alloc(arena: &RootedArena, root: Rooted<Self>) -> &mut Rooted<Self>;
188 }
189
190 macro_rules! impl_rootable {
191 ($(($value:ty, $key:ident, $gc_type:ident)$(,)?)*) => {
192 $(
193 impl Sealed for $value {
194 const GC_TYPE: GCType = GCType::$gc_type;
195
196 fn alloc(arena: &RootedArena, root: Rooted<$value>) -> &mut Rooted<$value> {
197 arena.$key.alloc(root)
198 }
199 }
200 )*
201 };
202 }
203
204 impl_rootable! {
205 (JSVal, values, Value),
206 (*mut JSObject, objects, Object),
207 (*mut JSString, strings, String),
208 (*mut JSScript, scripts, Script),
209 (PropertyKey, property_keys, PropertyKey),
210 (PropertyDescriptor, property_descriptors, PropertyDescriptor),
211 (*mut JSFunction, functions, Function),
212 (*mut BigInt, big_ints, BigInt),
213 (*mut Symbol, symbols, Symbol),
214 }
215}
216
217macro_rules! impl_drop {
218 ([$self:expr], $(($key:ident, $gc_type:ident)$(,)?)*) => {
219 $(let $key: Vec<_> = $self.rooted.$key.iter_mut().collect();)*
220 $(let mut $key = $key.into_iter().rev();)*
221
222 for ty in $self.order.take().into_iter().rev() {
223 match ty {
224 $(
225 GCType::$gc_type => {
226 let root = $key.next().unwrap();
227 unsafe {
228 root.remove_from_root_stack();
229 }
230 }
231 )*
232 }
233 }
234 }
235}
236
237impl Drop for Context {
238 fn drop(&mut self) {
240 impl_drop! {
241 [self],
242 (values, Value),
243 (objects, Object),
244 (strings, String),
245 (scripts, Script),
246 (property_keys, PropertyKey),
247 (property_descriptors, PropertyDescriptor),
248 (functions, Function),
249 (big_ints, BigInt),
250 (symbols, Symbol),
251 }
252 }
253}