Skip to content

Commit

Permalink
rebase and post rebase changes
Browse files Browse the repository at this point in the history
  • Loading branch information
nekevss committed Nov 5, 2022
1 parent 8da3ab9 commit 3b70b83
Show file tree
Hide file tree
Showing 4 changed files with 286 additions and 2 deletions.
2 changes: 1 addition & 1 deletion boa_gc/derive_macros/cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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]
Expand Down
281 changes: 281 additions & 0 deletions boa_gc/src/internals/cell_ref.rs
Original file line number Diff line number Diff line change
@@ -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<T>`.
pub struct GcCellRef<'a, T: ?Sized + 'static> {
pub(crate) flags: &'a Cell<BorrowFlag>,
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<U, F>(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<U, V, F>(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<T>`.
pub struct GcCellRefMut<'a, T: Trace + ?Sized + 'static, U: ?Sized = T> {
pub(crate) gc_cell: &'a GcCell<T>,
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<V, F>(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<T: ?Sized + Send> Send for GcCell<T> {}

impl<T: Trace + Clone> Clone for GcCell<T> {
#[inline]
fn clone(&self) -> Self {
Self::new(self.borrow().clone())
}
}

impl<T: Trace + Default> Default for GcCell<T> {
#[inline]
fn default() -> Self {
Self::new(Default::default())
}
}

impl<T: Trace + ?Sized + PartialEq> PartialEq for GcCell<T> {
#[inline(always)]
fn eq(&self, other: &Self) -> bool {
*self.borrow() == *other.borrow()
}
}

impl<T: Trace + ?Sized + Eq> Eq for GcCell<T> {}

impl<T: Trace + ?Sized + PartialOrd> PartialOrd for GcCell<T> {
#[inline(always)]
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
(*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<T: Trace + ?Sized + Ord> Ord for GcCell<T> {
#[inline]
fn cmp(&self, other: &GcCell<T>) -> Ordering {
(*self.borrow()).cmp(&*other.borrow())
}
}

impl<T: Trace + ?Sized + Debug> Debug for GcCell<T> {
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", &"<borrowed>")
.finish(),
}
}
}
3 changes: 3 additions & 0 deletions boa_gc/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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)
}
}
}
Expand Down
2 changes: 1 addition & 1 deletion boa_gc/tests/promotions.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}
Expand Down

0 comments on commit 3b70b83

Please sign in to comment.