From 3b70b830eb102416d5e159d278b348533c12e948 Mon Sep 17 00:00:00 2001 From: nekevss Date: Sat, 5 Nov 2022 09:07:36 -0400 Subject: [PATCH] rebase and post rebase changes --- boa_gc/derive_macros/cargo.toml | 2 +- boa_gc/src/internals/cell_ref.rs | 281 +++++++++++++++++++++++++++++++ boa_gc/src/lib.rs | 3 + boa_gc/tests/promotions.rs | 2 +- 4 files changed, 286 insertions(+), 2 deletions(-) create mode 100644 boa_gc/src/internals/cell_ref.rs diff --git a/boa_gc/derive_macros/cargo.toml b/boa_gc/derive_macros/cargo.toml index e38539ab398..e7f90833608 100644 --- a/boa_gc/derive_macros/cargo.toml +++ b/boa_gc/derive_macros/cargo.toml @@ -2,7 +2,7 @@ name = "boa_gc_macros" version = "0.16.0" description = "Garbage collector for the Boa JavaScript engine." -keywords = ["javascript", "js", "garbage", "memory"] +keywords = ["javascript", "js", "garbage", "memory", "derive"] edition = "2021" [lib] diff --git a/boa_gc/src/internals/cell_ref.rs b/boa_gc/src/internals/cell_ref.rs new file mode 100644 index 00000000000..3c9de15a52d --- /dev/null +++ b/boa_gc/src/internals/cell_ref.rs @@ -0,0 +1,281 @@ +//! Implementation of a garbage collected cell reference +use std::cell::Cell; +use std::cmp::Ordering; +use std::fmt::{self, Debug, Display}; +use std::ops::{Deref, DerefMut}; + +use crate::{ + internals::{ + borrow_flag::{BorrowFlag, BorrowState}, + GcCell, + }, + trace::Trace, +}; + +/// A wrapper type for an immutably borrowed value from a `GcCell`. +pub struct GcCellRef<'a, T: ?Sized + 'static> { + pub(crate) flags: &'a Cell, + pub(crate) value: &'a T, +} + +impl<'a, T: ?Sized> GcCellRef<'a, T> { + /// Copies a `GcCellRef`. + /// + /// The `GcCell` is already immutably borrowed, so this cannot fail. + /// + /// This is an associated function that needs to be used as + /// `GcCellRef::clone(...)`. A `Clone` implementation or a method + /// would interfere with the use of `c.borrow().clone()` to clone + /// the contents of a `GcCell`. + #[inline] + pub fn clone(orig: &GcCellRef<'a, T>) -> GcCellRef<'a, T> { + orig.flags.set(orig.flags.get().add_reading()); + GcCellRef { + flags: orig.flags, + value: orig.value, + } + } + + /// Makes a new `GcCellRef` from a component of the borrowed data. + /// + /// The `GcCell` is already immutably borrowed, so this cannot fail. + /// + /// This is an associated function that needs to be used as `GcCellRef::map(...)`. + /// A method would interfere with methods of the same name on the contents + /// of a `GcCellRef` used through `Deref`. + #[inline] + pub fn map(orig: Self, f: F) -> GcCellRef<'a, U> + where + U: ?Sized, + F: FnOnce(&T) -> &U, + { + let ret = GcCellRef { + flags: orig.flags, + value: f(orig.value), + }; + + // We have to tell the compiler not to call the destructor of GcCellRef, + // because it will update the borrow flags. + std::mem::forget(orig); + + ret + } + + /// Splits a `GcCellRef` into multiple `GcCellRef`s for different components of the borrowed data. + /// + /// The `GcCell` is already immutably borrowed, so this cannot fail. + /// + /// This is an associated function that needs to be used as GcCellRef::map_split(...). + /// A method would interfere with methods of the same name on the contents of a `GcCellRef` used through `Deref`. + #[inline] + pub fn map_split(orig: Self, f: F) -> (GcCellRef<'a, U>, GcCellRef<'a, V>) + where + U: ?Sized, + V: ?Sized, + F: FnOnce(&T) -> (&U, &V), + { + let (a, b) = f(orig.value); + + orig.flags.set(orig.flags.get().add_reading()); + + let ret = ( + GcCellRef { + flags: orig.flags, + value: a, + }, + GcCellRef { + flags: orig.flags, + value: b, + }, + ); + + // We have to tell the compiler not to call the destructor of GcCellRef, + // because it will update the borrow flags. + std::mem::forget(orig); + + ret + } +} + +impl<'a, T: ?Sized> Deref for GcCellRef<'a, T> { + type Target = T; + + #[inline] + fn deref(&self) -> &T { + self.value + } +} + +impl<'a, T: ?Sized> Drop for GcCellRef<'a, T> { + fn drop(&mut self) { + debug_assert!(self.flags.get().borrowed() == BorrowState::Reading); + self.flags.set(self.flags.get().sub_reading()); + } +} + +impl<'a, T: ?Sized + Debug> Debug for GcCellRef<'a, T> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + Debug::fmt(&**self, f) + } +} + +impl<'a, T: ?Sized + Display> Display for GcCellRef<'a, T> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + Display::fmt(&**self, f) + } +} + +/// A wrapper type for a mutably borrowed value from a `GcCell`. +pub struct GcCellRefMut<'a, T: Trace + ?Sized + 'static, U: ?Sized = T> { + pub(crate) gc_cell: &'a GcCell, + pub(crate) value: &'a mut U, +} + +impl<'a, T: Trace + ?Sized, U: ?Sized> GcCellRefMut<'a, T, U> { + /// Makes a new `GcCellRefMut` for a component of the borrowed data, e.g., an enum + /// variant. + /// + /// The `GcCellRefMut` is already mutably borrowed, so this cannot fail. + /// + /// This is an associated function that needs to be used as + /// `GcCellRefMut::map(...)`. A method would interfere with methods of the same + /// name on the contents of a `GcCell` used through `Deref`. + #[inline] + pub fn map(orig: Self, f: F) -> GcCellRefMut<'a, T, V> + where + V: ?Sized, + F: FnOnce(&mut U) -> &mut V, + { + let value = unsafe { &mut *(orig.value as *mut U) }; + + let ret = GcCellRefMut { + gc_cell: orig.gc_cell, + value: f(value), + }; + + // We have to tell the compiler not to call the destructor of GcCellRefMut, + // because it will update the borrow flags. + std::mem::forget(orig); + + ret + } +} + +impl<'a, T: Trace + ?Sized, U: ?Sized> Deref for GcCellRefMut<'a, T, U> { + type Target = U; + + #[inline] + fn deref(&self) -> &U { + self.value + } +} + +impl<'a, T: Trace + ?Sized, U: ?Sized> DerefMut for GcCellRefMut<'a, T, U> { + #[inline] + fn deref_mut(&mut self) -> &mut U { + self.value + } +} + +impl<'a, T: Trace + ?Sized, U: ?Sized> Drop for GcCellRefMut<'a, T, U> { + #[inline] + fn drop(&mut self) { + debug_assert!(self.gc_cell.flags.get().borrowed() == BorrowState::Writing); + // Restore the rooted state of the GcCell's contents to the state of the GcCell. + // During the lifetime of the GcCellRefMut, the GcCell's contents are rooted. + if !self.gc_cell.flags.get().rooted() { + unsafe { + (*self.gc_cell.cell.get()).unroot(); + } + } + self.gc_cell + .flags + .set(self.gc_cell.flags.get().set_unused()); + } +} + +impl<'a, T: Trace + ?Sized, U: Debug + ?Sized> Debug for GcCellRefMut<'a, T, U> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + Debug::fmt(&*(self.deref()), f) + } +} + +impl<'a, T: Trace + ?Sized, U: Display + ?Sized> Display for GcCellRefMut<'a, T, U> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + Display::fmt(&**self, f) + } +} + +unsafe impl Send for GcCell {} + +impl Clone for GcCell { + #[inline] + fn clone(&self) -> Self { + Self::new(self.borrow().clone()) + } +} + +impl Default for GcCell { + #[inline] + fn default() -> Self { + Self::new(Default::default()) + } +} + +impl PartialEq for GcCell { + #[inline(always)] + fn eq(&self, other: &Self) -> bool { + *self.borrow() == *other.borrow() + } +} + +impl Eq for GcCell {} + +impl PartialOrd for GcCell { + #[inline(always)] + fn partial_cmp(&self, other: &Self) -> Option { + (*self.borrow()).partial_cmp(&*other.borrow()) + } + + #[inline(always)] + fn lt(&self, other: &Self) -> bool { + *self.borrow() < *other.borrow() + } + + #[inline(always)] + fn le(&self, other: &Self) -> bool { + *self.borrow() <= *other.borrow() + } + + #[inline(always)] + fn gt(&self, other: &Self) -> bool { + *self.borrow() > *other.borrow() + } + + #[inline(always)] + fn ge(&self, other: &Self) -> bool { + *self.borrow() >= *other.borrow() + } +} + +impl Ord for GcCell { + #[inline] + fn cmp(&self, other: &GcCell) -> Ordering { + (*self.borrow()).cmp(&*other.borrow()) + } +} + +impl Debug for GcCell { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self.flags.get().borrowed() { + BorrowState::Unused | BorrowState::Reading => f + .debug_struct("GcCell") + .field("value", &self.borrow()) + .finish(), + BorrowState::Writing => f + .debug_struct("GcCell") + .field("value", &"") + .finish(), + } + } +} diff --git a/boa_gc/src/lib.rs b/boa_gc/src/lib.rs index c7252fdbd9a..0fc9088f0b4 100644 --- a/boa_gc/src/lib.rs +++ b/boa_gc/src/lib.rs @@ -506,6 +506,9 @@ impl Collector { // Drops every node let unmarked_node = Box::from_raw(node.as_ptr()); sweep_head.set(unmarked_node.header.next.take()); + + // Need to stay consistent when it comes to `mem::forget` approach vs. Drop in place + mem::forget(unmarked_node) } } } diff --git a/boa_gc/tests/promotions.rs b/boa_gc/tests/promotions.rs index a75023baa13..04cfa1ccacd 100644 --- a/boa_gc/tests/promotions.rs +++ b/boa_gc/tests/promotions.rs @@ -7,7 +7,7 @@ fn generational_promo_one() { let mut storage = Vec::new(); // Super basic loop that loads bytes and force collections - for i in 0..200 as usize { + for i in 0..15000 as usize { let gc = BoaAlloc::new(i); storage.push(gc); }