1use std::any::TypeId;
10
11use crate::{
12 Context, HostDefined, JsNativeError, JsObject, JsResult, JsString,
13 class::Class,
14 context::{
15 HostHooks,
16 intrinsics::{Intrinsics, StandardConstructor},
17 },
18 environments::DeclarativeEnvironment,
19 module::Module,
20 object::shape::RootShape,
21};
22use boa_ast::scope::Scope;
23use boa_engine::JsValue;
24use boa_engine::property::{Attribute, PropertyDescriptor, PropertyKey};
25use boa_gc::{Finalize, Gc, GcRef, GcRefCell, GcRefMut, Trace};
26use rustc_hash::FxHashMap;
27
28#[derive(Clone, Trace, Finalize)]
32pub struct Realm {
33 inner: Gc<Inner>,
34}
35
36impl Eq for Realm {}
37
38impl PartialEq for Realm {
39 fn eq(&self, other: &Self) -> bool {
40 Gc::ptr_eq(&self.inner, &other.inner)
41 }
42}
43
44impl std::fmt::Debug for Realm {
45 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
46 f.debug_struct("Realm")
47 .field("intrinsics", &self.inner.intrinsics)
48 .field("environment", &self.inner.environment)
49 .field("global_object", &self.inner.global_object)
50 .field("global_this", &self.inner.global_this)
51 .finish()
52 }
53}
54
55#[derive(Trace, Finalize)]
56struct Inner {
57 intrinsics: Intrinsics,
58
59 environment: Gc<DeclarativeEnvironment>,
61
62 #[unsafe_ignore_trace]
66 scope: Scope,
67
68 global_object: JsObject,
69 global_this: JsObject,
70 template_map: GcRefCell<FxHashMap<u64, JsObject>>,
71 loaded_modules: GcRefCell<FxHashMap<JsString, Module>>,
72 host_classes: GcRefCell<FxHashMap<TypeId, StandardConstructor>>,
73
74 host_defined: GcRefCell<HostDefined>,
75}
76
77impl Realm {
78 #[inline]
80 pub fn create(hooks: &dyn HostHooks, root_shape: &RootShape) -> JsResult<Self> {
81 let intrinsics = Intrinsics::uninit(root_shape).ok_or_else(|| {
82 JsNativeError::typ().with_message("failed to create the realm intrinsics")
83 })?;
84
85 let global_object = hooks.create_global_object(&intrinsics);
86 let global_this = hooks
87 .create_global_this(&intrinsics)
88 .unwrap_or_else(|| global_object.clone());
89 let environment = Gc::new(DeclarativeEnvironment::global());
90 let scope = Scope::new_global();
91
92 let realm = Self {
93 inner: Gc::new(Inner {
94 intrinsics,
95 environment,
96 scope,
97 global_object,
98 global_this,
99 template_map: GcRefCell::default(),
100 loaded_modules: GcRefCell::default(),
101 host_classes: GcRefCell::default(),
102 host_defined: GcRefCell::default(),
103 }),
104 };
105
106 realm.initialize();
107
108 Ok(realm)
109 }
110
111 #[inline]
113 #[must_use]
114 pub fn intrinsics(&self) -> &Intrinsics {
115 &self.inner.intrinsics
116 }
117
118 #[inline]
127 #[must_use]
128 pub fn host_defined(&self) -> GcRef<'_, HostDefined> {
129 self.inner.host_defined.borrow()
130 }
131
132 #[inline]
141 #[must_use]
142 pub fn host_defined_mut(&self) -> GcRefMut<'_, HostDefined> {
143 self.inner.host_defined.borrow_mut()
144 }
145
146 #[must_use]
148 pub fn has_class<C: Class>(&self) -> bool {
149 self.inner
150 .host_classes
151 .borrow()
152 .contains_key(&TypeId::of::<C>())
153 }
154
155 #[must_use]
157 pub fn get_class<C: Class>(&self) -> Option<StandardConstructor> {
158 self.inner
159 .host_classes
160 .borrow()
161 .get(&TypeId::of::<C>())
162 .cloned()
163 }
164
165 pub(crate) fn environment(&self) -> &Gc<DeclarativeEnvironment> {
166 &self.inner.environment
167 }
168
169 #[must_use]
171 pub fn scope(&self) -> &Scope {
172 &self.inner.scope
173 }
174
175 pub(crate) fn global_object(&self) -> &JsObject {
176 &self.inner.global_object
177 }
178
179 pub(crate) fn global_this(&self) -> &JsObject {
180 &self.inner.global_this
181 }
182
183 pub(crate) fn loaded_modules(&self) -> &GcRefCell<FxHashMap<JsString, Module>> {
184 &self.inner.loaded_modules
185 }
186
187 pub(crate) fn resize_global_env(&self) {
189 let binding_number = self.scope().num_bindings();
190 let env = self
191 .environment()
192 .kind()
193 .as_global()
194 .expect("Realm should only store global environments")
195 .poisonable_environment();
196 let mut bindings = env.bindings().borrow_mut();
197
198 if bindings.len() < binding_number as usize {
199 bindings.resize(binding_number as usize, None);
200 }
201 }
202
203 pub(crate) fn push_template(&self, site: u64, template: JsObject) {
204 self.inner.template_map.borrow_mut().insert(site, template);
205 }
206
207 pub(crate) fn lookup_template(&self, site: u64) -> Option<JsObject> {
208 self.inner.template_map.borrow().get(&site).cloned()
209 }
210
211 pub fn register_property<K, V>(
215 &self,
216 key: K,
217 value: V,
218 attribute: Attribute,
219 context: &mut Context,
220 ) -> JsResult<()>
221 where
222 K: Into<PropertyKey>,
223 V: Into<JsValue>,
224 {
225 self.global_object().define_property_or_throw(
226 key,
227 PropertyDescriptor::builder()
228 .value(value)
229 .writable(attribute.writable())
230 .enumerable(attribute.enumerable())
231 .configurable(attribute.configurable()),
232 context,
233 )?;
234 Ok(())
235 }
236
237 pub fn register_class<C: Class>(&self, spec: StandardConstructor) {
239 self.inner
240 .host_classes
241 .borrow_mut()
242 .insert(TypeId::of::<C>(), spec);
243 }
244
245 #[must_use]
247 pub fn unregister_class<C: Class>(&self) -> Option<StandardConstructor> {
248 self.inner
249 .host_classes
250 .borrow_mut()
251 .remove(&TypeId::of::<C>())
252 }
253
254 pub(crate) fn addr(&self) -> *const () {
255 let ptr: *const _ = &raw const *self.inner;
256 ptr.cast()
257 }
258}