1use std::any::TypeId;
10use std::cell::Cell;
11
12use crate::{
13 Context, HostDefined, JsNativeError, JsObject, JsResult, JsString,
14 class::Class,
15 context::{
16 HostHooks,
17 intrinsics::{Intrinsics, StandardConstructor},
18 },
19 environments::DeclarativeEnvironment,
20 module::Module,
21 object::shape::RootShape,
22 string::StaticJsStrings,
23};
24use boa_ast::scope::Scope;
25use boa_engine::JsValue;
26use boa_engine::property::{Attribute, PropertyDescriptor, PropertyKey};
27use boa_gc::{Finalize, Gc, GcRef, GcRefCell, GcRefMut, Trace};
28use rustc_hash::FxHashMap;
29
30#[derive(Clone, Trace, Finalize)]
34pub struct Realm {
35 inner: Gc<Inner>,
36}
37
38impl Eq for Realm {}
39
40impl PartialEq for Realm {
41 fn eq(&self, other: &Self) -> bool {
42 Gc::ptr_eq(&self.inner, &other.inner)
43 }
44}
45
46impl std::fmt::Debug for Realm {
47 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
48 f.debug_struct("Realm")
49 .field("intrinsics", &self.inner.intrinsics)
50 .field("environment", &self.inner.environment)
51 .field("global_object", &self.inner.global_object)
52 .field("global_this", &self.inner.global_this)
53 .finish()
54 }
55}
56
57#[derive(Trace, Finalize)]
58struct Inner {
59 intrinsics: Intrinsics,
60
61 environment: Gc<DeclarativeEnvironment>,
63
64 #[unsafe_ignore_trace]
68 scope: Scope,
69
70 global_object: JsObject,
71 global_this: JsObject,
72 template_map: GcRefCell<FxHashMap<u64, JsObject>>,
73 loaded_modules: GcRefCell<FxHashMap<JsString, Module>>,
74 host_classes: GcRefCell<FxHashMap<TypeId, StandardConstructor>>,
75
76 host_defined: GcRefCell<HostDefined>,
77
78 #[unsafe_ignore_trace]
80 math_pristine: Cell<bool>,
81}
82
83impl Realm {
84 #[inline]
86 pub fn create(hooks: &dyn HostHooks, root_shape: &RootShape) -> JsResult<Self> {
87 let intrinsics = Intrinsics::uninit(root_shape).ok_or_else(|| {
88 JsNativeError::typ().with_message("failed to create the realm intrinsics")
89 })?;
90
91 let global_object = hooks.create_global_object(&intrinsics);
92 let global_this = hooks
93 .create_global_this(&intrinsics)
94 .unwrap_or_else(|| global_object.clone());
95 let environment = Gc::new(DeclarativeEnvironment::global());
96 let scope = Scope::new_global();
97
98 let realm = Self {
99 inner: Gc::new(Inner {
100 intrinsics,
101 environment,
102 scope,
103 global_object,
104 global_this,
105 template_map: GcRefCell::default(),
106 loaded_modules: GcRefCell::default(),
107 host_classes: GcRefCell::default(),
108 host_defined: GcRefCell::default(),
109 math_pristine: Cell::new(false),
110 }),
111 };
112
113 realm.initialize();
114
115 Ok(realm)
116 }
117
118 #[inline]
120 #[must_use]
121 pub fn intrinsics(&self) -> &Intrinsics {
122 &self.inner.intrinsics
123 }
124
125 #[inline]
134 #[must_use]
135 pub fn host_defined(&self) -> GcRef<'_, HostDefined> {
136 self.inner.host_defined.borrow()
137 }
138
139 #[inline]
148 #[must_use]
149 pub fn host_defined_mut(&self) -> GcRefMut<'_, HostDefined> {
150 self.inner.host_defined.borrow_mut()
151 }
152
153 #[must_use]
155 pub fn has_class<C: Class>(&self) -> bool {
156 self.inner
157 .host_classes
158 .borrow()
159 .contains_key(&TypeId::of::<C>())
160 }
161
162 #[must_use]
164 pub fn get_class<C: Class>(&self) -> Option<StandardConstructor> {
165 self.inner
166 .host_classes
167 .borrow()
168 .get(&TypeId::of::<C>())
169 .cloned()
170 }
171
172 pub(crate) fn environment(&self) -> &Gc<DeclarativeEnvironment> {
173 &self.inner.environment
174 }
175
176 #[must_use]
178 pub fn scope(&self) -> &Scope {
179 &self.inner.scope
180 }
181
182 pub(crate) fn global_object(&self) -> &JsObject {
183 &self.inner.global_object
184 }
185
186 pub(crate) fn global_this(&self) -> &JsObject {
187 &self.inner.global_this
188 }
189
190 #[inline]
193 pub(crate) fn math_pristine(&self) -> bool {
194 self.inner.math_pristine.get()
195 }
196
197 #[inline]
199 pub(crate) fn mark_math_pristine(&self) {
200 self.inner.math_pristine.set(true);
201 }
202
203 #[inline]
204 pub(crate) fn clear_math_pristine(&self) {
205 self.inner.math_pristine.set(false);
206 }
207
208 #[inline]
213 pub(crate) fn observe_math_write(&self, object: &JsObject, key: &PropertyKey) {
214 if !self.inner.math_pristine.get() {
215 return;
216 }
217 if self.intrinsics().objects().is_math(object)
218 || (matches!(key, PropertyKey::String(name) if *name == StaticJsStrings::MATH)
219 && JsObject::equals(object, self.global_object()))
220 {
221 self.inner.math_pristine.set(false);
222 }
223 }
224
225 pub(crate) fn loaded_modules(&self) -> &GcRefCell<FxHashMap<JsString, Module>> {
226 &self.inner.loaded_modules
227 }
228
229 pub(crate) fn resize_global_env(&self) {
231 let binding_number = self.scope().num_bindings();
232 let env = self
233 .environment()
234 .kind()
235 .as_global()
236 .expect("Realm should only store global environments");
237 let mut bindings = env.bindings().borrow_mut();
238
239 if bindings.len() < binding_number as usize {
240 bindings.resize(binding_number as usize, None);
241 }
242 }
243
244 pub(crate) fn push_template(&self, site: u64, template: JsObject) {
245 self.inner.template_map.borrow_mut().insert(site, template);
246 }
247
248 pub(crate) fn lookup_template(&self, site: u64) -> Option<JsObject> {
249 self.inner.template_map.borrow().get(&site).cloned()
250 }
251
252 pub fn register_property<K, V>(
256 &self,
257 key: K,
258 value: V,
259 attribute: Attribute,
260 context: &mut Context,
261 ) -> JsResult<()>
262 where
263 K: Into<PropertyKey>,
264 V: Into<JsValue>,
265 {
266 self.global_object().define_property_or_throw(
267 key,
268 PropertyDescriptor::builder()
269 .value(value)
270 .writable(attribute.writable())
271 .enumerable(attribute.enumerable())
272 .configurable(attribute.configurable()),
273 context,
274 )?;
275 Ok(())
276 }
277
278 pub fn register_class<C: Class>(&self, spec: StandardConstructor) {
280 self.inner
281 .host_classes
282 .borrow_mut()
283 .insert(TypeId::of::<C>(), spec);
284 }
285
286 #[must_use]
288 pub fn unregister_class<C: Class>(&self) -> Option<StandardConstructor> {
289 self.inner
290 .host_classes
291 .borrow_mut()
292 .remove(&TypeId::of::<C>())
293 }
294
295 pub(crate) fn addr(&self) -> *const () {
296 let ptr: *const _ = &raw const *self.inner;
297 ptr.cast()
298 }
299}