Skip to main content

boa_engine/
realm.rs

1//! Boa's implementation of ECMAScript's `Realm Records`
2//!
3//! Conceptually, a realm consists of a set of intrinsic objects, an ECMAScript global environment,
4//! all of the ECMAScript code that is loaded within the scope of that global environment,
5//! and other associated state and resources.
6//!
7//! A realm is represented in this implementation as a Realm struct with the fields specified from the spec.
8
9use 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/// Representation of a Realm.
29///
30/// In the specification these are called Realm Records.
31#[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    /// The global declarative environment of this realm.
60    environment: Gc<DeclarativeEnvironment>,
61
62    /// The global scope of this realm.
63    /// This is directly related to the global declarative environment.
64    // Safety: Nothing in `Scope` needs tracing.
65    #[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    /// Create a new [`Realm`].
79    #[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    /// Gets the intrinsics of this `Realm`.
112    #[inline]
113    #[must_use]
114    pub fn intrinsics(&self) -> &Intrinsics {
115        &self.inner.intrinsics
116    }
117
118    /// Returns an immutable reference to the [`ECMAScript specification`][spec] defined
119    /// [`\[\[\HostDefined]\]`][`HostDefined`] field of the [`Realm`].
120    ///
121    /// [spec]: https://tc39.es/ecma262/#table-realm-record-fields
122    ///
123    /// # Panics
124    ///
125    /// Panics if [`HostDefined`] field is mutably borrowed.
126    #[inline]
127    #[must_use]
128    pub fn host_defined(&self) -> GcRef<'_, HostDefined> {
129        self.inner.host_defined.borrow()
130    }
131
132    /// Returns a mutable reference to [`ECMAScript specification`][spec] defined
133    /// [`\[\[\HostDefined]\]`][`HostDefined`] field of the [`Realm`].
134    ///
135    /// [spec]: https://tc39.es/ecma262/#table-realm-record-fields
136    ///
137    /// # Panics
138    ///
139    /// Panics if [`HostDefined`] field is borrowed.
140    #[inline]
141    #[must_use]
142    pub fn host_defined_mut(&self) -> GcRefMut<'_, HostDefined> {
143        self.inner.host_defined.borrow_mut()
144    }
145
146    /// Checks if this `Realm` has the class `C` registered into its class map.
147    #[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    /// Gets the constructor and prototype of the class `C` if it is registered in the class map.
156    #[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    /// Returns the scope of this realm.
170    #[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    /// Resizes the number of bindings on the global environment.
188    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    /// Register a property on the global object of this realm.
212    ///
213    /// It will return an error if the property is already defined.
214    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    /// Register a class `C` in this realm.
238    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    /// Unregister a class `C` in this realm.
246    #[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}