From 127a7f523ef7339e344278d16f9ba30c1560bd42 Mon Sep 17 00:00:00 2001 From: Alistair Date: Mon, 9 Oct 2023 11:44:36 +0100 Subject: [PATCH] Refactor the `[[HostDefined]]` implementation. Currently `[[HostDefined]]` doesn't permit you to mutably borrow two objects from the `[[HostDefined]]` field since the `FxHashMap` is wrapped under a `GcRefCell`. This commit instead wraps all `Box` entries with a `GcRefCell`, permitting a more granular borrowing scheme. Additionally, this commit takes the opportunity to provide automatic downcasting on the `insert` and `remove` methods for `[[HostDefined]]`. --- boa_engine/src/host_defined.rs | 91 ++++++++++++++-------------------- boa_engine/src/realm.rs | 6 +-- 2 files changed, 41 insertions(+), 56 deletions(-) diff --git a/boa_engine/src/host_defined.rs b/boa_engine/src/host_defined.rs index ff66267fbf5..5ff22bb56b8 100644 --- a/boa_engine/src/host_defined.rs +++ b/boa_engine/src/host_defined.rs @@ -6,97 +6,82 @@ use rustc_hash::FxHashMap; use crate::object::NativeObject; -/// Map used to store the host defined objects. -#[doc(hidden)] -type HostDefinedMap = FxHashMap>; - /// This represents a `ECMASCript` specification \[`HostDefined`\] field. /// /// This allows storing types which are mapped by their [`TypeId`]. #[derive(Default, Trace, Finalize)] #[allow(missing_debug_implementations)] pub struct HostDefined { - state: GcRefCell, + env: FxHashMap>>, +} + +#[allow(unsafe_op_in_unsafe_fn)] +unsafe fn downcast_boxed_native_object_unchecked( + obj: Box, +) -> Box { + let raw: *mut dyn NativeObject = Box::into_raw(obj); + Box::from_raw(raw as *mut T) } impl HostDefined { /// Insert a type into the [`HostDefined`]. - /// - /// # Panics - /// - /// Panics if [`HostDefined`] field is borrowed. #[track_caller] - pub fn insert_default(&self) -> Option> { - self.state - .borrow_mut() - .insert(TypeId::of::(), Box::::default()) + pub fn insert_default(&mut self) -> Option> { + self.env + .insert(TypeId::of::(), GcRefCell::new(Box::new(T::default()))) + .map(|obj| unsafe { downcast_boxed_native_object_unchecked(obj.into_inner()) }) } /// Insert a type into the [`HostDefined`]. - /// - /// # Panics - /// - /// Panics if [`HostDefined`] field is borrowed. #[track_caller] - pub fn insert(&self, value: T) -> Option> { - self.state - .borrow_mut() - .insert(TypeId::of::(), Box::new(value)) + pub fn insert(&mut self, value: T) -> Option> { + self.env + .insert(TypeId::of::(), GcRefCell::new(Box::new(value))) + .map(|obj| unsafe { downcast_boxed_native_object_unchecked(obj.into_inner()) }) } /// Check if the [`HostDefined`] has type T. - /// - /// # Panics - /// - /// Panics if [`HostDefined`] field is borrowed mutably. #[track_caller] pub fn has(&self) -> bool { - self.state.borrow().contains_key(&TypeId::of::()) + self.env.contains_key(&TypeId::of::()) } /// Remove type T from [`HostDefined`], if it exists. /// /// Returns [`Some`] with the object if it exits, [`None`] otherwise. - /// - /// # Panics - /// - /// Panics if [`HostDefined`] field is borrowed. #[track_caller] - pub fn remove(&self) -> Option> { - self.state.borrow_mut().remove(&TypeId::of::()) + pub fn remove(&mut self) -> Option> { + self.env + .remove(&TypeId::of::()) + .map(|obj| unsafe { downcast_boxed_native_object_unchecked(obj.into_inner()) }) } - /// Get type T from [`HostDefined`], if it exits. + /// Get type T from [`HostDefined`], if it exists. /// /// # Panics /// - /// Panics if [`HostDefined`] field is borrowed. + /// Panics if T's entry in [`HostDefined`] is mutably borrowed. #[track_caller] pub fn get(&self) -> Option> { - GcRef::try_map(self.state.borrow(), |state| { - state - .get(&TypeId::of::()) - .map(Box::as_ref) - .and_then(::downcast_ref::) + let entry = self.env.get(&TypeId::of::())?; + + GcRef::try_map(entry.borrow(), |obj| { + obj.as_ref().downcast_ref::() }) } - /// Get type T from [`HostDefined`], if it exits. + /// Get type T from [`HostDefined`], if it exists. /// /// # Panics /// - /// Panics if [`HostDefined`] field is borrowed. + /// Panics if T's entry in [`HostDefined`] is borrowed. #[track_caller] - pub fn get_mut(&self) -> Option> { - GcRefMut::try_map( - self.state.borrow_mut(), - |state: &mut FxHashMap>| { - state - .get_mut(&TypeId::of::()) - .map(Box::as_mut) - .and_then(::downcast_mut::) - }, - ) + pub fn get_mut(&self) -> Option, T>> { + let entry = self.env.get(&TypeId::of::())?; + + GcRefMut::try_map(entry.borrow_mut(), |obj| { + obj.as_mut().downcast_mut::() + }) } /// Clears all the objects. @@ -105,7 +90,7 @@ impl HostDefined { /// /// Panics if [`HostDefined`] field is borrowed. #[track_caller] - pub fn clear(&self) { - self.state.borrow_mut().clear(); + pub fn clear(&mut self) { + self.env.clear(); } } diff --git a/boa_engine/src/realm.rs b/boa_engine/src/realm.rs index f0ad3c88da5..ed19b767a81 100644 --- a/boa_engine/src/realm.rs +++ b/boa_engine/src/realm.rs @@ -61,7 +61,7 @@ struct Inner { loaded_modules: GcRefCell>, host_classes: GcRefCell>, - host_defined: HostDefined, + host_defined: GcRefCell, } impl Realm { @@ -86,7 +86,7 @@ impl Realm { template_map: GcRefCell::default(), loaded_modules: GcRefCell::default(), host_classes: GcRefCell::default(), - host_defined: HostDefined::default(), + host_defined: GcRefCell::default(), }), }; @@ -107,7 +107,7 @@ impl Realm { /// [spec]: https://tc39.es/ecma262/#table-realm-record-fields #[inline] #[must_use] - pub fn host_defined(&self) -> &HostDefined { + pub fn host_defined(&self) -> &GcRefCell { &self.inner.host_defined }