Skip to content

Commit 7c9eef8

Browse files
committed
Improve the design of ephemerons in our GC (#2530)
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.
1 parent f19467a commit 7c9eef8

File tree

28 files changed

+838
-577
lines changed

28 files changed

+838
-577
lines changed

boa_engine/src/builtins/async_generator/mod.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ use crate::{
1919
vm::GeneratorResumeKind,
2020
Context, JsError, JsResult,
2121
};
22-
use boa_gc::{Finalize, Gc, GcCell, Trace};
22+
use boa_gc::{Finalize, Gc, GcRefCell, Trace};
2323
use boa_profiler::Profiler;
2424
use std::collections::VecDeque;
2525

@@ -57,7 +57,7 @@ pub struct AsyncGenerator {
5757
pub(crate) state: AsyncGeneratorState,
5858

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

6262
/// The `[[AsyncGeneratorQueue]]` internal slot.
6363
pub(crate) queue: VecDeque<AsyncGeneratorRequest>,
@@ -512,7 +512,7 @@ impl AsyncGenerator {
512512
pub(crate) fn resume(
513513
generator: &JsObject,
514514
state: AsyncGeneratorState,
515-
generator_context: &Gc<GcCell<GeneratorContext>>,
515+
generator_context: &Gc<GcRefCell<GeneratorContext>>,
516516
completion: (JsResult<JsValue>, bool),
517517
context: &mut Context<'_>,
518518
) {

boa_engine/src/builtins/generator/mod.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ use crate::{
2020
vm::{CallFrame, GeneratorResumeKind, ReturnType},
2121
Context, JsError, JsResult,
2222
};
23-
use boa_gc::{Finalize, Gc, GcCell, Trace};
23+
use boa_gc::{Finalize, Gc, GcRefCell, Trace};
2424
use boa_profiler::Profiler;
2525

2626
/// Indicates the state of a generator.
@@ -52,7 +52,7 @@ pub struct Generator {
5252
pub(crate) state: GeneratorState,
5353

5454
/// The `[[GeneratorContext]]` internal slot.
55-
pub(crate) context: Option<Gc<GcCell<GeneratorContext>>>,
55+
pub(crate) context: Option<Gc<GcRefCell<GeneratorContext>>>,
5656
}
5757

5858
impl BuiltIn for Generator {

boa_engine/src/builtins/promise/mod.rs

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ use crate::{
1919
value::JsValue,
2020
Context, JsError, JsResult,
2121
};
22-
use boa_gc::{Finalize, Gc, GcCell, Trace};
22+
use boa_gc::{Finalize, Gc, GcRefCell, Trace};
2323
use boa_profiler::Profiler;
2424
use std::{cell::Cell, rc::Rc};
2525
use tap::{Conv, Pipe};
@@ -161,7 +161,7 @@ impl PromiseCapability {
161161

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

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

213213
let promise_capability = promise_capability.borrow();
214214

@@ -470,14 +470,14 @@ impl Promise {
470470
#[unsafe_ignore_trace]
471471
already_called: Rc<Cell<bool>>,
472472
index: usize,
473-
values: Gc<GcCell<Vec<JsValue>>>,
473+
values: Gc<GcRefCell<Vec<JsValue>>>,
474474
capability_resolve: JsFunction,
475475
#[unsafe_ignore_trace]
476476
remaining_elements_count: Rc<Cell<i32>>,
477477
}
478478

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

482482
// 2. Let remainingElementsCount be the Record { [[Value]]: 1 }.
483483
let remaining_elements_count = Rc::new(Cell::new(1));
@@ -714,14 +714,14 @@ impl Promise {
714714
#[unsafe_ignore_trace]
715715
already_called: Rc<Cell<bool>>,
716716
index: usize,
717-
values: Gc<GcCell<Vec<JsValue>>>,
717+
values: Gc<GcRefCell<Vec<JsValue>>>,
718718
capability: JsFunction,
719719
#[unsafe_ignore_trace]
720720
remaining_elements: Rc<Cell<i32>>,
721721
}
722722

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

726726
// 2. Let remainingElementsCount be the Record { [[Value]]: 1 }.
727727
let remaining_elements_count = Rc::new(Cell::new(1));
@@ -1057,14 +1057,14 @@ impl Promise {
10571057
#[unsafe_ignore_trace]
10581058
already_called: Rc<Cell<bool>>,
10591059
index: usize,
1060-
errors: Gc<GcCell<Vec<JsValue>>>,
1060+
errors: Gc<GcRefCell<Vec<JsValue>>>,
10611061
capability_reject: JsFunction,
10621062
#[unsafe_ignore_trace]
10631063
remaining_elements_count: Rc<Cell<i32>>,
10641064
}
10651065

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

10691069
// 2. Let remainingElementsCount be the Record { [[Value]]: 1 }.
10701070
let remaining_elements_count = Rc::new(Cell::new(1));

boa_engine/src/bytecompiler/mod.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ use boa_ast::{
2727
pattern::Pattern,
2828
Declaration, Expression, Statement, StatementList, StatementListItem,
2929
};
30-
use boa_gc::{Gc, GcCell};
30+
use boa_gc::{Gc, GcRefCell};
3131
use boa_interner::{Interner, Sym};
3232
use rustc_hash::FxHashMap;
3333

@@ -246,7 +246,7 @@ impl<'b, 'host> ByteCompiler<'b, 'host> {
246246
/// Push a compile time environment to the current `CodeBlock` and return it's index.
247247
fn push_compile_environment(
248248
&mut self,
249-
environment: Gc<GcCell<CompileTimeEnvironment>>,
249+
environment: Gc<GcRefCell<CompileTimeEnvironment>>,
250250
) -> usize {
251251
let index = self.code_block.compile_environments.len();
252252
self.code_block.compile_environments.push(environment);

boa_engine/src/environments/compile.rs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ use crate::{
22
environments::runtime::BindingLocator, property::PropertyDescriptor, Context, JsString, JsValue,
33
};
44
use boa_ast::expression::Identifier;
5-
use boa_gc::{Finalize, Gc, GcCell, Trace};
5+
use boa_gc::{Finalize, Gc, GcRefCell, Trace};
66

77
use rustc_hash::FxHashMap;
88

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

211-
self.realm.compile_env = Gc::new(GcCell::new(CompileTimeEnvironment {
211+
self.realm.compile_env = Gc::new(GcRefCell::new(CompileTimeEnvironment {
212212
outer: Some(outer),
213213
environment_index,
214214
bindings: FxHashMap::default(),
@@ -225,7 +225,7 @@ impl Context<'_> {
225225
/// Panics if there are no more environments that can be pop'ed.
226226
pub(crate) fn pop_compile_time_environment(
227227
&mut self,
228-
) -> (usize, Gc<GcCell<CompileTimeEnvironment>>) {
228+
) -> (usize, Gc<GcRefCell<CompileTimeEnvironment>>) {
229229
let current_env_borrow = self.realm.compile_env.borrow();
230230
if let Some(outer) = &current_env_borrow.outer {
231231
let outer_clone = outer.clone();

boa_engine/src/environments/runtime.rs

Lines changed: 15 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ use crate::{
22
environments::CompileTimeEnvironment, error::JsNativeError, object::JsObject, Context, JsValue,
33
};
44
use boa_ast::expression::Identifier;
5-
use boa_gc::{Finalize, Gc, GcCell, Trace};
5+
use boa_gc::{Finalize, Gc, GcRefCell, Trace};
66
use rustc_hash::FxHashSet;
77
use std::cell::Cell;
88

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

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

226226
impl DeclarativeEnvironmentStack {
227227
/// Create a new environment stack with the most outer declarative environment.
228-
pub(crate) fn new(global_compile_environment: Gc<GcCell<CompileTimeEnvironment>>) -> Self {
228+
pub(crate) fn new(global_compile_environment: Gc<GcRefCell<CompileTimeEnvironment>>) -> Self {
229229
Self {
230230
stack: vec![Gc::new(DeclarativeEnvironment {
231-
bindings: GcCell::new(Vec::new()),
231+
bindings: GcRefCell::new(Vec::new()),
232232
compile: global_compile_environment,
233233
poisoned: Cell::new(false),
234234
slots: Some(EnvironmentSlots::Global),
@@ -349,7 +349,7 @@ impl DeclarativeEnvironmentStack {
349349
pub(crate) fn push_declarative(
350350
&mut self,
351351
num_bindings: usize,
352-
compile_environment: Gc<GcCell<CompileTimeEnvironment>>,
352+
compile_environment: Gc<GcRefCell<CompileTimeEnvironment>>,
353353
) -> usize {
354354
let poisoned = self
355355
.stack
@@ -361,7 +361,7 @@ impl DeclarativeEnvironmentStack {
361361
let index = self.stack.len();
362362

363363
self.stack.push(Gc::new(DeclarativeEnvironment {
364-
bindings: GcCell::new(vec![None; num_bindings]),
364+
bindings: GcRefCell::new(vec![None; num_bindings]),
365365
compile: compile_environment,
366366
poisoned: Cell::new(poisoned),
367367
slots: None,
@@ -378,7 +378,7 @@ impl DeclarativeEnvironmentStack {
378378
pub(crate) fn push_function(
379379
&mut self,
380380
num_bindings: usize,
381-
compile_environment: Gc<GcCell<CompileTimeEnvironment>>,
381+
compile_environment: Gc<GcRefCell<CompileTimeEnvironment>>,
382382
this: Option<JsValue>,
383383
function_object: JsObject,
384384
new_target: Option<JsObject>,
@@ -402,10 +402,10 @@ impl DeclarativeEnvironmentStack {
402402
let this = this.unwrap_or(JsValue::Null);
403403

404404
self.stack.push(Gc::new(DeclarativeEnvironment {
405-
bindings: GcCell::new(vec![None; num_bindings]),
405+
bindings: GcRefCell::new(vec![None; num_bindings]),
406406
compile: compile_environment,
407407
poisoned: Cell::new(poisoned),
408-
slots: Some(EnvironmentSlots::Function(GcCell::new(FunctionSlots {
408+
slots: Some(EnvironmentSlots::Function(GcRefCell::new(FunctionSlots {
409409
this,
410410
this_binding_status,
411411
function_object,
@@ -422,7 +422,7 @@ impl DeclarativeEnvironmentStack {
422422
pub(crate) fn push_function_inherit(
423423
&mut self,
424424
num_bindings: usize,
425-
compile_environment: Gc<GcCell<CompileTimeEnvironment>>,
425+
compile_environment: Gc<GcRefCell<CompileTimeEnvironment>>,
426426
) {
427427
let outer = self
428428
.stack
@@ -433,7 +433,7 @@ impl DeclarativeEnvironmentStack {
433433
let slots = outer.slots.clone();
434434

435435
self.stack.push(Gc::new(DeclarativeEnvironment {
436-
bindings: GcCell::new(vec![None; num_bindings]),
436+
bindings: GcRefCell::new(vec![None; num_bindings]),
437437
compile: compile_environment,
438438
poisoned: Cell::new(poisoned),
439439
slots,
@@ -480,7 +480,7 @@ impl DeclarativeEnvironmentStack {
480480
/// # Panics
481481
///
482482
/// Panics if no environment exists on the stack.
483-
pub(crate) fn current_compile_environment(&self) -> Gc<GcCell<CompileTimeEnvironment>> {
483+
pub(crate) fn current_compile_environment(&self) -> Gc<GcRefCell<CompileTimeEnvironment>> {
484484
self.stack
485485
.last()
486486
.expect("global environment must always exist")

boa_engine/src/object/jsobject.rs

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ use crate::{
1010
value::PreferredType,
1111
Context, JsResult, JsValue,
1212
};
13-
use boa_gc::{self, Finalize, Gc, GcCell, Trace};
13+
use boa_gc::{self, Finalize, Gc, GcRefCell, Trace};
1414
use std::{
1515
cell::RefCell,
1616
collections::HashMap,
@@ -20,15 +20,15 @@ use std::{
2020
};
2121

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

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

2828
/// Garbage collected `Object`.
2929
#[derive(Trace, Finalize, Clone, Default)]
3030
pub struct JsObject {
31-
inner: Gc<GcCell<Object>>,
31+
inner: Gc<GcRefCell<Object>>,
3232
}
3333

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

757-
pub(crate) const fn inner(&self) -> &Gc<GcCell<Object>> {
757+
pub(crate) const fn inner(&self) -> &Gc<GcRefCell<Object>> {
758758
&self.inner
759759
}
760760
}
761761

762-
impl AsRef<GcCell<Object>> for JsObject {
762+
impl AsRef<GcRefCell<Object>> for JsObject {
763763
#[inline]
764-
fn as_ref(&self) -> &GcCell<Object> {
764+
fn as_ref(&self) -> &GcRefCell<Object> {
765765
&self.inner
766766
}
767767
}
768768

769-
impl From<Gc<GcCell<Object>>> for JsObject {
769+
impl From<Gc<GcRefCell<Object>>> for JsObject {
770770
#[inline]
771-
fn from(inner: Gc<GcCell<Object>>) -> Self {
771+
fn from(inner: Gc<GcRefCell<Object>>) -> Self {
772772
Self { inner }
773773
}
774774
}

boa_engine/src/object/mod.rs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,7 @@ use crate::{
5555
Context, JsBigInt, JsString, JsSymbol, JsValue,
5656
};
5757

58-
use boa_gc::{custom_trace, Finalize, GcCell, Trace, WeakGc};
58+
use boa_gc::{custom_trace, Finalize, GcRefCell, Trace, WeakGc};
5959
use std::{
6060
any::Any,
6161
fmt::{self, Debug},
@@ -266,7 +266,7 @@ pub enum ObjectKind {
266266
Promise(Promise),
267267

268268
/// The `WeakRef` object kind.
269-
WeakRef(WeakGc<GcCell<Object>>),
269+
WeakRef(WeakGc<GcRefCell<Object>>),
270270

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

620620
/// Creates the `WeakRef` object data
621-
pub fn weak_ref(weak_ref: WeakGc<GcCell<Object>>) -> Self {
621+
pub fn weak_ref(weak_ref: WeakGc<GcRefCell<Object>>) -> Self {
622622
Self {
623623
kind: ObjectKind::WeakRef(weak_ref),
624624
internal_methods: &ORDINARY_INTERNAL_METHODS,
@@ -1623,7 +1623,7 @@ impl Object {
16231623

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

0 commit comments

Comments
 (0)