Skip to content

Commit

Permalink
Improve the design of ephemerons in our GC (#2530)
Browse files Browse the repository at this point in the history
This PR changes the following:
- Modifies `EphemeronBox` to be more akin to `GcBox`, with its own header, roots and markers. This also makes it more similar to [Racket's](https://docs.racket-lang.org/reference/ephemerons.html) implementation.
- Removes `EPHEMERON_QUEUE`.
- Ephemerons are now tracked on a special `weak_start` linked list, instead of `strong_start` which is where all other GC boxes live.
- Documents all unsafe blocks.
- Documents our current garbage collection algorithm. I hope this'll clarify a bit what exactly are we doing on every garbage collection.
- Renames/removes some functions.
  • Loading branch information
jedel1043 committed Jan 23, 2023
1 parent f19467a commit 7c9eef8
Show file tree
Hide file tree
Showing 28 changed files with 838 additions and 577 deletions.
6 changes: 3 additions & 3 deletions boa_engine/src/builtins/async_generator/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ use crate::{
vm::GeneratorResumeKind,
Context, JsError, JsResult,
};
use boa_gc::{Finalize, Gc, GcCell, Trace};
use boa_gc::{Finalize, Gc, GcRefCell, Trace};
use boa_profiler::Profiler;
use std::collections::VecDeque;

Expand Down Expand Up @@ -57,7 +57,7 @@ pub struct AsyncGenerator {
pub(crate) state: AsyncGeneratorState,

/// The `[[AsyncGeneratorContext]]` internal slot.
pub(crate) context: Option<Gc<GcCell<GeneratorContext>>>,
pub(crate) context: Option<Gc<GcRefCell<GeneratorContext>>>,

/// The `[[AsyncGeneratorQueue]]` internal slot.
pub(crate) queue: VecDeque<AsyncGeneratorRequest>,
Expand Down Expand Up @@ -512,7 +512,7 @@ impl AsyncGenerator {
pub(crate) fn resume(
generator: &JsObject,
state: AsyncGeneratorState,
generator_context: &Gc<GcCell<GeneratorContext>>,
generator_context: &Gc<GcRefCell<GeneratorContext>>,
completion: (JsResult<JsValue>, bool),
context: &mut Context<'_>,
) {
Expand Down
4 changes: 2 additions & 2 deletions boa_engine/src/builtins/generator/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ use crate::{
vm::{CallFrame, GeneratorResumeKind, ReturnType},
Context, JsError, JsResult,
};
use boa_gc::{Finalize, Gc, GcCell, Trace};
use boa_gc::{Finalize, Gc, GcRefCell, Trace};
use boa_profiler::Profiler;

/// Indicates the state of a generator.
Expand Down Expand Up @@ -52,7 +52,7 @@ pub struct Generator {
pub(crate) state: GeneratorState,

/// The `[[GeneratorContext]]` internal slot.
pub(crate) context: Option<Gc<GcCell<GeneratorContext>>>,
pub(crate) context: Option<Gc<GcRefCell<GeneratorContext>>>,
}

impl BuiltIn for Generator {
Expand Down
18 changes: 9 additions & 9 deletions boa_engine/src/builtins/promise/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ use crate::{
value::JsValue,
Context, JsError, JsResult,
};
use boa_gc::{Finalize, Gc, GcCell, Trace};
use boa_gc::{Finalize, Gc, GcRefCell, Trace};
use boa_profiler::Profiler;
use std::{cell::Cell, rc::Rc};
use tap::{Conv, Pipe};
Expand Down Expand Up @@ -161,7 +161,7 @@ impl PromiseCapability {

// 2. NOTE: C is assumed to be a constructor function that supports the parameter conventions of the Promise constructor (see 27.2.3.1).
// 3. Let promiseCapability be the PromiseCapability Record { [[Promise]]: undefined, [[Resolve]]: undefined, [[Reject]]: undefined }.
let promise_capability = Gc::new(GcCell::new(RejectResolve {
let promise_capability = Gc::new(GcRefCell::new(RejectResolve {
reject: JsValue::undefined(),
resolve: JsValue::undefined(),
}));
Expand Down Expand Up @@ -208,7 +208,7 @@ impl PromiseCapability {
.into();

// 6. Let promise be ? Construct(C, « executor »).
let promise = c.construct(&[executor], Some(&c), context)?;
let promise = c.construct(&[executor], None, context)?;

let promise_capability = promise_capability.borrow();

Expand Down Expand Up @@ -470,14 +470,14 @@ impl Promise {
#[unsafe_ignore_trace]
already_called: Rc<Cell<bool>>,
index: usize,
values: Gc<GcCell<Vec<JsValue>>>,
values: Gc<GcRefCell<Vec<JsValue>>>,
capability_resolve: JsFunction,
#[unsafe_ignore_trace]
remaining_elements_count: Rc<Cell<i32>>,
}

// 1. Let values be a new empty List.
let values = Gc::new(GcCell::new(Vec::new()));
let values = Gc::new(GcRefCell::new(Vec::new()));

// 2. Let remainingElementsCount be the Record { [[Value]]: 1 }.
let remaining_elements_count = Rc::new(Cell::new(1));
Expand Down Expand Up @@ -714,14 +714,14 @@ impl Promise {
#[unsafe_ignore_trace]
already_called: Rc<Cell<bool>>,
index: usize,
values: Gc<GcCell<Vec<JsValue>>>,
values: Gc<GcRefCell<Vec<JsValue>>>,
capability: JsFunction,
#[unsafe_ignore_trace]
remaining_elements: Rc<Cell<i32>>,
}

// 1. Let values be a new empty List.
let values = Gc::new(GcCell::new(Vec::new()));
let values = Gc::new(GcRefCell::new(Vec::new()));

// 2. Let remainingElementsCount be the Record { [[Value]]: 1 }.
let remaining_elements_count = Rc::new(Cell::new(1));
Expand Down Expand Up @@ -1057,14 +1057,14 @@ impl Promise {
#[unsafe_ignore_trace]
already_called: Rc<Cell<bool>>,
index: usize,
errors: Gc<GcCell<Vec<JsValue>>>,
errors: Gc<GcRefCell<Vec<JsValue>>>,
capability_reject: JsFunction,
#[unsafe_ignore_trace]
remaining_elements_count: Rc<Cell<i32>>,
}

// 1. Let errors be a new empty List.
let errors = Gc::new(GcCell::new(Vec::new()));
let errors = Gc::new(GcRefCell::new(Vec::new()));

// 2. Let remainingElementsCount be the Record { [[Value]]: 1 }.
let remaining_elements_count = Rc::new(Cell::new(1));
Expand Down
4 changes: 2 additions & 2 deletions boa_engine/src/bytecompiler/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ use boa_ast::{
pattern::Pattern,
Declaration, Expression, Statement, StatementList, StatementListItem,
};
use boa_gc::{Gc, GcCell};
use boa_gc::{Gc, GcRefCell};
use boa_interner::{Interner, Sym};
use rustc_hash::FxHashMap;

Expand Down Expand Up @@ -246,7 +246,7 @@ impl<'b, 'host> ByteCompiler<'b, 'host> {
/// Push a compile time environment to the current `CodeBlock` and return it's index.
fn push_compile_environment(
&mut self,
environment: Gc<GcCell<CompileTimeEnvironment>>,
environment: Gc<GcRefCell<CompileTimeEnvironment>>,
) -> usize {
let index = self.code_block.compile_environments.len();
self.code_block.compile_environments.push(environment);
Expand Down
8 changes: 4 additions & 4 deletions boa_engine/src/environments/compile.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ use crate::{
environments::runtime::BindingLocator, property::PropertyDescriptor, Context, JsString, JsValue,
};
use boa_ast::expression::Identifier;
use boa_gc::{Finalize, Gc, GcCell, Trace};
use boa_gc::{Finalize, Gc, GcRefCell, Trace};

use rustc_hash::FxHashMap;

Expand All @@ -22,7 +22,7 @@ struct CompileTimeBinding {
/// A compile time environment also indicates, if it is a function environment.
#[derive(Debug, Finalize, Trace)]
pub(crate) struct CompileTimeEnvironment {
outer: Option<Gc<GcCell<Self>>>,
outer: Option<Gc<GcRefCell<Self>>>,
environment_index: usize,
#[unsafe_ignore_trace]
bindings: FxHashMap<Identifier, CompileTimeBinding>,
Expand Down Expand Up @@ -208,7 +208,7 @@ impl Context<'_> {
let environment_index = self.realm.compile_env.borrow().environment_index + 1;
let outer = self.realm.compile_env.clone();

self.realm.compile_env = Gc::new(GcCell::new(CompileTimeEnvironment {
self.realm.compile_env = Gc::new(GcRefCell::new(CompileTimeEnvironment {
outer: Some(outer),
environment_index,
bindings: FxHashMap::default(),
Expand All @@ -225,7 +225,7 @@ impl Context<'_> {
/// Panics if there are no more environments that can be pop'ed.
pub(crate) fn pop_compile_time_environment(
&mut self,
) -> (usize, Gc<GcCell<CompileTimeEnvironment>>) {
) -> (usize, Gc<GcRefCell<CompileTimeEnvironment>>) {
let current_env_borrow = self.realm.compile_env.borrow();
if let Some(outer) = &current_env_borrow.outer {
let outer_clone = outer.clone();
Expand Down
30 changes: 15 additions & 15 deletions boa_engine/src/environments/runtime.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ use crate::{
environments::CompileTimeEnvironment, error::JsNativeError, object::JsObject, Context, JsValue,
};
use boa_ast::expression::Identifier;
use boa_gc::{Finalize, Gc, GcCell, Trace};
use boa_gc::{Finalize, Gc, GcRefCell, Trace};
use rustc_hash::FxHashSet;
use std::cell::Cell;

Expand All @@ -28,8 +28,8 @@ use std::cell::Cell;
/// All poisoned environments have to be checked for added bindings.
#[derive(Debug, Trace, Finalize)]
pub(crate) struct DeclarativeEnvironment {
bindings: GcCell<Vec<Option<JsValue>>>,
compile: Gc<GcCell<CompileTimeEnvironment>>,
bindings: GcRefCell<Vec<Option<JsValue>>>,
compile: Gc<GcRefCell<CompileTimeEnvironment>>,
#[unsafe_ignore_trace]
poisoned: Cell<bool>,
slots: Option<EnvironmentSlots>,
Expand All @@ -38,13 +38,13 @@ pub(crate) struct DeclarativeEnvironment {
/// Describes the different types of internal slot data that an environment can hold.
#[derive(Clone, Debug, Trace, Finalize)]
pub(crate) enum EnvironmentSlots {
Function(GcCell<FunctionSlots>),
Function(GcRefCell<FunctionSlots>),
Global,
}

impl EnvironmentSlots {
/// Return the slots if they are part of a function environment.
pub(crate) const fn as_function_slots(&self) -> Option<&GcCell<FunctionSlots>> {
pub(crate) const fn as_function_slots(&self) -> Option<&GcRefCell<FunctionSlots>> {
if let Self::Function(env) = &self {
Some(env)
} else {
Expand Down Expand Up @@ -225,10 +225,10 @@ pub struct DeclarativeEnvironmentStack {

impl DeclarativeEnvironmentStack {
/// Create a new environment stack with the most outer declarative environment.
pub(crate) fn new(global_compile_environment: Gc<GcCell<CompileTimeEnvironment>>) -> Self {
pub(crate) fn new(global_compile_environment: Gc<GcRefCell<CompileTimeEnvironment>>) -> Self {
Self {
stack: vec![Gc::new(DeclarativeEnvironment {
bindings: GcCell::new(Vec::new()),
bindings: GcRefCell::new(Vec::new()),
compile: global_compile_environment,
poisoned: Cell::new(false),
slots: Some(EnvironmentSlots::Global),
Expand Down Expand Up @@ -349,7 +349,7 @@ impl DeclarativeEnvironmentStack {
pub(crate) fn push_declarative(
&mut self,
num_bindings: usize,
compile_environment: Gc<GcCell<CompileTimeEnvironment>>,
compile_environment: Gc<GcRefCell<CompileTimeEnvironment>>,
) -> usize {
let poisoned = self
.stack
Expand All @@ -361,7 +361,7 @@ impl DeclarativeEnvironmentStack {
let index = self.stack.len();

self.stack.push(Gc::new(DeclarativeEnvironment {
bindings: GcCell::new(vec![None; num_bindings]),
bindings: GcRefCell::new(vec![None; num_bindings]),
compile: compile_environment,
poisoned: Cell::new(poisoned),
slots: None,
Expand All @@ -378,7 +378,7 @@ impl DeclarativeEnvironmentStack {
pub(crate) fn push_function(
&mut self,
num_bindings: usize,
compile_environment: Gc<GcCell<CompileTimeEnvironment>>,
compile_environment: Gc<GcRefCell<CompileTimeEnvironment>>,
this: Option<JsValue>,
function_object: JsObject,
new_target: Option<JsObject>,
Expand All @@ -402,10 +402,10 @@ impl DeclarativeEnvironmentStack {
let this = this.unwrap_or(JsValue::Null);

self.stack.push(Gc::new(DeclarativeEnvironment {
bindings: GcCell::new(vec![None; num_bindings]),
bindings: GcRefCell::new(vec![None; num_bindings]),
compile: compile_environment,
poisoned: Cell::new(poisoned),
slots: Some(EnvironmentSlots::Function(GcCell::new(FunctionSlots {
slots: Some(EnvironmentSlots::Function(GcRefCell::new(FunctionSlots {
this,
this_binding_status,
function_object,
Expand All @@ -422,7 +422,7 @@ impl DeclarativeEnvironmentStack {
pub(crate) fn push_function_inherit(
&mut self,
num_bindings: usize,
compile_environment: Gc<GcCell<CompileTimeEnvironment>>,
compile_environment: Gc<GcRefCell<CompileTimeEnvironment>>,
) {
let outer = self
.stack
Expand All @@ -433,7 +433,7 @@ impl DeclarativeEnvironmentStack {
let slots = outer.slots.clone();

self.stack.push(Gc::new(DeclarativeEnvironment {
bindings: GcCell::new(vec![None; num_bindings]),
bindings: GcRefCell::new(vec![None; num_bindings]),
compile: compile_environment,
poisoned: Cell::new(poisoned),
slots,
Expand Down Expand Up @@ -480,7 +480,7 @@ impl DeclarativeEnvironmentStack {
/// # Panics
///
/// Panics if no environment exists on the stack.
pub(crate) fn current_compile_environment(&self) -> Gc<GcCell<CompileTimeEnvironment>> {
pub(crate) fn current_compile_environment(&self) -> Gc<GcRefCell<CompileTimeEnvironment>> {
self.stack
.last()
.expect("global environment must always exist")
Expand Down
20 changes: 10 additions & 10 deletions boa_engine/src/object/jsobject.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ use crate::{
value::PreferredType,
Context, JsResult, JsValue,
};
use boa_gc::{self, Finalize, Gc, GcCell, Trace};
use boa_gc::{self, Finalize, Gc, GcRefCell, Trace};
use std::{
cell::RefCell,
collections::HashMap,
Expand All @@ -20,15 +20,15 @@ use std::{
};

/// A wrapper type for an immutably borrowed type T.
pub type Ref<'a, T> = boa_gc::GcCellRef<'a, T>;
pub type Ref<'a, T> = boa_gc::GcRef<'a, T>;

/// A wrapper type for a mutably borrowed type T.
pub type RefMut<'a, T, U> = boa_gc::GcCellRefMut<'a, T, U>;
pub type RefMut<'a, T, U> = boa_gc::GcRefMut<'a, T, U>;

/// Garbage collected `Object`.
#[derive(Trace, Finalize, Clone, Default)]
pub struct JsObject {
inner: Gc<GcCell<Object>>,
inner: Gc<GcRefCell<Object>>,
}

impl JsObject {
Expand Down Expand Up @@ -68,7 +68,7 @@ impl JsObject {
/// [`OrdinaryObjectCreate`]: https://tc39.es/ecma262/#sec-ordinaryobjectcreate
pub fn from_proto_and_data<O: Into<Option<Self>>>(prototype: O, data: ObjectData) -> Self {
Self {
inner: Gc::new(GcCell::new(Object {
inner: Gc::new(GcRefCell::new(Object {
data,
prototype: prototype.into(),
extensible: true,
Expand Down Expand Up @@ -754,21 +754,21 @@ Cannot both specify accessors and a value or writable attribute",
)
}

pub(crate) const fn inner(&self) -> &Gc<GcCell<Object>> {
pub(crate) const fn inner(&self) -> &Gc<GcRefCell<Object>> {
&self.inner
}
}

impl AsRef<GcCell<Object>> for JsObject {
impl AsRef<GcRefCell<Object>> for JsObject {
#[inline]
fn as_ref(&self) -> &GcCell<Object> {
fn as_ref(&self) -> &GcRefCell<Object> {
&self.inner
}
}

impl From<Gc<GcCell<Object>>> for JsObject {
impl From<Gc<GcRefCell<Object>>> for JsObject {
#[inline]
fn from(inner: Gc<GcCell<Object>>) -> Self {
fn from(inner: Gc<GcRefCell<Object>>) -> Self {
Self { inner }
}
}
Expand Down
8 changes: 4 additions & 4 deletions boa_engine/src/object/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ use crate::{
Context, JsBigInt, JsString, JsSymbol, JsValue,
};

use boa_gc::{custom_trace, Finalize, GcCell, Trace, WeakGc};
use boa_gc::{custom_trace, Finalize, GcRefCell, Trace, WeakGc};
use std::{
any::Any,
fmt::{self, Debug},
Expand Down Expand Up @@ -266,7 +266,7 @@ pub enum ObjectKind {
Promise(Promise),

/// The `WeakRef` object kind.
WeakRef(WeakGc<GcCell<Object>>),
WeakRef(WeakGc<GcRefCell<Object>>),

/// The `Intl.Collator` object kind.
#[cfg(feature = "intl")]
Expand Down Expand Up @@ -618,7 +618,7 @@ impl ObjectData {
}

/// Creates the `WeakRef` object data
pub fn weak_ref(weak_ref: WeakGc<GcCell<Object>>) -> Self {
pub fn weak_ref(weak_ref: WeakGc<GcRefCell<Object>>) -> Self {
Self {
kind: ObjectKind::WeakRef(weak_ref),
internal_methods: &ORDINARY_INTERNAL_METHODS,
Expand Down Expand Up @@ -1623,7 +1623,7 @@ impl Object {

/// Gets the `WeakRef` data if the object is a `WeakRef`.
#[inline]
pub const fn as_weak_ref(&self) -> Option<&WeakGc<GcCell<Self>>> {
pub const fn as_weak_ref(&self) -> Option<&WeakGc<GcRefCell<Self>>> {
match self.data {
ObjectData {
kind: ObjectKind::WeakRef(ref weak_ref),
Expand Down
Loading

0 comments on commit 7c9eef8

Please sign in to comment.