diff --git a/boa_engine/src/class.rs b/boa_engine/src/class.rs index 07285ef28b9..bfac2ed83ed 100644 --- a/boa_engine/src/class.rs +++ b/boa_engine/src/class.rs @@ -79,21 +79,29 @@ use crate::{ /// Native class. pub trait Class: NativeObject + Sized { - /// The binding name of the class. + /// The binding name of this class. const NAME: &'static str; - /// The amount of arguments the constructor of the class takes, default is `0`. + /// The amount of arguments this class' constructor takes. Default is `0`. const LENGTH: usize = 0; - /// The attributes the class will be binded with, default is `writable`, `enumerable`, `configurable`. + /// The property attributes of this class' constructor in the global object. + /// Default is `writable`, `enumerable`, `configurable`. const ATTRIBUTES: Attribute = Attribute::all(); - /// Creates the inner data for an instance of this class. + /// Creates the internal data for an instance of this class. + /// + /// This method can also be called the "native constructor" of this class. fn make_data(this: &JsValue, args: &[JsValue], context: &mut Context<'_>) -> JsResult; - /// Initializes the internals and the methods of this class. + /// Initializes the properties and methods of this class. fn init(class: &mut ClassBuilder<'_, '_>) -> JsResult<()>; /// Creates a new [`JsObject`] with its internal data set to the result of calling `Self::make_data`. /// + /// # Note + /// + /// This will throw an error if this class is not registered in the context's active realm. + /// See [`Context::register_global_class`]. + /// /// # Warning /// /// Overriding this method could be useful for certain usages, but incorrectly implementing this diff --git a/boa_engine/src/context/mod.rs b/boa_engine/src/context/mod.rs index d80da051a68..32dd3008f71 100644 --- a/boa_engine/src/context/mod.rs +++ b/boa_engine/src/context/mod.rs @@ -334,10 +334,7 @@ impl<'host> Context<'host> { /// /// context.register_global_class::()?; /// ``` - pub fn register_global_class(&mut self) -> JsResult<()> - where - C: Class, - { + pub fn register_global_class(&mut self) -> JsResult<()> { if self.realm.has_class::() { return Err(JsNativeError::typ() .with_message("cannot register a class twice") @@ -361,6 +358,35 @@ impl<'host> Context<'host> { Ok(()) } + /// Removes the global class `C` from the currently active realm, returning the constructor + /// and prototype of the class if `C` was registered. + /// + /// # Note + /// + /// This makes the constructor return an error on further calls, but note that this won't protect + /// static properties from being accessed within variables that stored the constructor before being + /// unregistered. If you need that functionality, you can use a static accessor that first checks + /// if the class is registered ([`Context::has_global_class`]) before returning the static value. + /// + /// # Example + /// ```ignore + /// #[derive(Debug, Trace, Finalize)] + /// struct MyClass; + /// + /// impl Class for MyClass { + /// // ... + /// } + /// + /// context.register_global_class::()?; + /// // ... code + /// context.unregister_global_class::()?; + /// ``` + pub fn unregister_global_class(&mut self) -> JsResult> { + self.global_object() + .delete_property_or_throw(js_string!(C::NAME), self)?; + Ok(self.realm.unregister_class::()) + } + /// Checks if the currently active realm has the global class `C` registered. #[must_use] pub fn has_global_class(&self) -> bool { diff --git a/boa_engine/src/realm.rs b/boa_engine/src/realm.rs index afb9a9fb0ab..f0ad3c88da5 100644 --- a/boa_engine/src/realm.rs +++ b/boa_engine/src/realm.rs @@ -177,6 +177,13 @@ impl Realm { .insert(TypeId::of::(), spec); } + pub(crate) fn unregister_class(&self) -> Option { + self.inner + .host_classes + .borrow_mut() + .remove(&TypeId::of::()) + } + pub(crate) fn addr(&self) -> *const () { let ptr: *const _ = &*self.inner; ptr.cast()