Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

5 changes: 3 additions & 2 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,8 @@ members = [

exclude = [
"tests/fuzz", # Does weird things on Windows tests
"tests/src", # Just a hack to have fuzz inside tests
"tests/wpt", # Should not run WPT by default.
"tests/src", # Just a hack to have fuzz inside tests
"tests/wpt", # Should not run WPT by default.
]

[workspace.package]
Expand All @@ -40,6 +40,7 @@ boa_gc = { version = "~0.20.0", path = "core/gc" }
boa_icu_provider = { version = "~0.20.0", path = "core/icu_provider" }
boa_interner = { version = "~0.20.0", path = "core/interner" }
boa_macros = { version = "~0.20.0", path = "core/macros" }
boa_mempool = { version = "~0.20.0", path = "core/mempool" }
boa_parser = { version = "~0.20.0", path = "core/parser" }
boa_runtime = { version = "~0.20.0", path = "core/runtime" }
boa_string = { version = "~0.20.0", path = "core/string" }
Expand Down
2 changes: 2 additions & 0 deletions core/gc/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -22,13 +22,15 @@ either = ["dep:either"]

[dependencies]
boa_macros.workspace = true
boa_mempool.workspace = true
hashbrown.workspace = true

boa_string = { workspace = true, optional = true }
either = { workspace = true, optional = true }
thin-vec = { workspace = true, optional = true }
icu_locale_core = { workspace = true, optional = true }


[lints]
workspace = true

Expand Down
25 changes: 19 additions & 6 deletions core/gc/src/internals/vtable.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
use crate::{GcBox, GcErasedPointer, MEM_POOL_ELEMENT_SIZE_THRESHOLD, Trace, Tracer};
use boa_mempool::MemPoolAllocator;
use std::any::TypeId;

use crate::{GcBox, GcErasedPointer, Trace, Tracer};

// Workaround: https://users.rust-lang.org/t/custom-vtables-with-integers/78508
pub(crate) const fn vtable_of<T: Trace + 'static>() -> &'static VTable {
trait HasVTable: Trace + Sized + 'static {
Expand Down Expand Up @@ -35,12 +35,22 @@ pub(crate) const fn vtable_of<T: Trace + 'static>() -> &'static VTable {
}

// SAFETY: The caller must ensure that the passed erased pointer is `GcBox<Self>`.
unsafe fn drop_fn(this: GcErasedPointer) {
unsafe fn drop_fn(
this: GcErasedPointer,
pool: &MemPoolAllocator<[u8; MEM_POOL_ELEMENT_SIZE_THRESHOLD]>,
) {
// SAFETY: The caller must ensure that the passed erased pointer is `GcBox<Self>`.
let this = this.cast::<GcBox<Self>>();

// SAFETY: The caller must ensure the erased pointer is not droped or deallocated.
let _value = unsafe { Box::from_raw(this.as_ptr()) };
if pool.contains(this.cast()) {
// SAFETY: The caller must ensure the erased pointer is not dropped or deallocated.
unsafe {
drop(this.read());
pool.dealloc_no_drop(this.cast());
}
} else {
drop(unsafe { Box::from_raw(this.as_ptr()) });
}
}

fn type_id_fn() -> TypeId {
Expand All @@ -67,7 +77,10 @@ pub(crate) const fn vtable_of<T: Trace + 'static>() -> &'static VTable {
pub(crate) type TraceFn = unsafe fn(this: GcErasedPointer, tracer: &mut Tracer);
pub(crate) type TraceNonRootsFn = unsafe fn(this: GcErasedPointer);
pub(crate) type RunFinalizerFn = unsafe fn(this: GcErasedPointer);
pub(crate) type DropFn = unsafe fn(this: GcErasedPointer);
pub(crate) type DropFn = unsafe fn(
this: GcErasedPointer,
pool: &MemPoolAllocator<[u8; MEM_POOL_ELEMENT_SIZE_THRESHOLD]>,
);
pub(crate) type TypeIdFn = fn() -> TypeId;

#[derive(Debug)]
Expand Down
52 changes: 41 additions & 11 deletions core/gc/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,31 +23,36 @@ mod trace;

pub(crate) mod internals;

pub use crate::trace::{Finalize, Trace, Tracer};
pub use boa_macros::{Finalize, Trace};
use boa_mempool::MemPoolAllocator;
pub use cell::{GcRef, GcRefCell, GcRefMut};
pub use internals::GcBox;
use internals::{EphemeronBox, ErasedEphemeronBox, ErasedWeakMapBox, WeakMapBox};
pub use pointers::{Ephemeron, Gc, GcErased, WeakGc, WeakMap};
use pointers::{NonTraceable, RawWeakMap};
use std::collections::BTreeMap;
use std::{
cell::{Cell, RefCell},
mem,
ptr::NonNull,
};

pub use crate::trace::{Finalize, Trace, Tracer};
pub use boa_macros::{Finalize, Trace};
pub use cell::{GcRef, GcRefCell, GcRefMut};
pub use internals::GcBox;
pub use pointers::{Ephemeron, Gc, GcErased, WeakGc, WeakMap};
const MEM_POOL_ELEMENT_SIZE_THRESHOLD: usize = 256;

type GcErasedPointer = NonNull<GcBox<NonTraceable>>;
type EphemeronPointer = NonNull<dyn ErasedEphemeronBox>;
type ErasedWeakMapBoxPointer = NonNull<dyn ErasedWeakMapBox>;

thread_local!(static GC_DROPPING: Cell<bool> = const { Cell::new(false) });
thread_local!(static BOA_GC: RefCell<BoaGc> = RefCell::new( BoaGc {
thread_local!(static BOA_GC: RefCell<BoaGc> = RefCell::new(BoaGc {
config: GcConfig::default(),
runtime: GcRuntimeData::default(),
strongs: Vec::default(),
weaks: Vec::default(),
weak_maps: Vec::default(),
pool: MemPoolAllocator::with_capacity(102_400),
stats: Default::default(),
}));

#[derive(Debug, Clone, Copy)]
Expand Down Expand Up @@ -84,6 +89,8 @@ struct BoaGc {
strongs: Vec<GcErasedPointer>,
weaks: Vec<EphemeronPointer>,
weak_maps: Vec<ErasedWeakMapBoxPointer>,
pool: MemPoolAllocator<[u8; MEM_POOL_ELEMENT_SIZE_THRESHOLD]>,
stats: BTreeMap<usize, usize>,
}

impl Drop for BoaGc {
Expand Down Expand Up @@ -134,8 +141,21 @@ impl Allocator {
let mut gc = st.borrow_mut();

Self::manage_state(&mut gc);
// Safety: value cannot be a null pointer, since `Box` cannot return null pointers.
let ptr = unsafe { NonNull::new_unchecked(Box::into_raw(Box::new(value))) };
// Safety: value cannot be a null pointer, since `MemPool` cannot return null pointers.
let ptr = unsafe {
gc.stats
.entry(element_size)
.and_modify(|v| *v += 1)
.or_insert(1);

if size_of::<GcBox<T>>() <= MEM_POOL_ELEMENT_SIZE_THRESHOLD {
let ptr = gc.pool.alloc_unitialized().cast();
ptr.write(value);
ptr
} else {
NonNull::new_unchecked(Box::into_raw(Box::new(value)))
}
};
let erased: NonNull<GcBox<NonTraceable>> = ptr.cast();

gc.strongs.push(erased);
Expand Down Expand Up @@ -250,6 +270,7 @@ impl Collector {
&mut gc.strongs,
&mut gc.weaks,
&mut gc.runtime.bytes_allocated,
&gc.pool,
);
}

Expand All @@ -266,7 +287,11 @@ impl Collector {
// SAFETY:
// The `Allocator` must always ensure its start node is a valid, non-null pointer that
// was allocated by `Box::from_raw(Box::new(..))`.
let _unmarked_node = unsafe { Box::from_raw(w.as_ptr()) };
unsafe {
if !gc.pool.dealloc(NonNull::new_unchecked(w.as_ptr().cast())) {
drop(Box::from_raw(w.as_ptr()));
}
}

false
}
Expand Down Expand Up @@ -447,6 +472,7 @@ impl Collector {
strong: &mut Vec<GcErasedPointer>,
weak: &mut Vec<EphemeronPointer>,
total_allocated: &mut usize,
pool: &MemPoolAllocator<[u8; MEM_POOL_ELEMENT_SIZE_THRESHOLD]>,
) {
let _guard = DropGuard::new();

Expand All @@ -467,7 +493,7 @@ impl Collector {

// SAFETY: The function pointer is appropriate for this node type because we extract it from it's VTable.
unsafe {
drop_fn(*node);
drop_fn(*node, pool);
}

false
Expand Down Expand Up @@ -497,6 +523,10 @@ impl Collector {

// Clean up the heap when BoaGc is dropped
fn dump(gc: &mut BoaGc) {
eprintln!("GC Stats:");
eprintln!("{:#?}", gc.stats);
eprintln!("------------------------------------------------------------\n");

// Weak maps have to be dropped first, since the process dereferences GcBoxes.
// This can be done without initializing a dropguard since no GcBox's are being dropped.
for node in mem::take(&mut gc.weak_maps) {
Expand All @@ -517,7 +547,7 @@ impl Collector {

// SAFETY: The function pointer is appropriate for this node type because we extract it from it's VTable.
unsafe {
drop_fn(node);
drop_fn(node, &gc.pool);
}
}

Expand Down
40 changes: 40 additions & 0 deletions core/mempool/ABOUT.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
# About Boa

Boa is an open-source, experimental ECMAScript Engine written in Rust for
lexing, parsing and executing ECMAScript/JavaScript. Currently, Boa supports some
of the [language][boa-conformance]. More information can be viewed at [Boa's
website][boa-web].

Try out the most recent release with Boa's live demo
[playground][boa-playground].

## Boa Crates

- [**`boa_cli`**][cli] - Boa's CLI && REPL implementation
- [**`boa_ast`**][ast] - Boa's ECMAScript Abstract Syntax Tree.
- [**`boa_engine`**][engine] - Boa's implementation of ECMAScript builtin objects and execution.
- [**`boa_gc`**][gc] - Boa's garbage collector.
- [**`boa_icu_provider`**][icu] - Boa's ICU4X data provider.
- [**`boa_interner`**][interner] - Boa's string interner.
- [**`boa_macros`**][macros] - Boa's macros.
- [**`boa_parser`**][parser] - Boa's lexer and parser.
- [**`boa_runtime`**][runtime] - Boa's `WebAPI` features.
- [**`boa_string`**][string] - Boa's ECMAScript string implementation.
- [**`tag_ptr`**][tag_ptr] - Utility library that enables a pointer to be associated with a tag of type `usize`.
- [**`small_btree`**][small_btree] - Utility library that adds the `SmallBTreeMap` data structure.

[boa-conformance]: https://boajs.dev/conformance
[boa-web]: https://boajs.dev/
[boa-playground]: https://boajs.dev/playground
[ast]: https://docs.rs/boa_ast/latest/boa_ast/index.html
[engine]: https://docs.rs/boa_engine/latest/boa_engine/index.html
[gc]: https://docs.rs/boa_gc/latest/boa_gc/index.html
[interner]: https://docs.rs/boa_interner/latest/boa_interner/index.html
[parser]: https://docs.rs/boa_parser/latest/boa_parser/index.html
[icu]: https://docs.rs/boa_icu_provider/latest/boa_icu_provider/index.html
[runtime]: https://docs.rs/boa_runtime/latest/boa_runtime/index.html
[string]: https://docs.rs/boa_string/latest/boa_string/index.html
[tag_ptr]: https://docs.rs/tag_ptr/latest/tag_ptr/index.html
[small_btree]: https://docs.rs/small_btree/latest/small_btree/index.html
[macros]: https://docs.rs/boa_macros/latest/boa_macros/index.html
[cli]: https://crates.io/crates/boa_cli
21 changes: 21 additions & 0 deletions core/mempool/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
[package]
name = "boa_mempool"
description = "Pool Allocator for the Boa JavaScript engine."
keywords = ["javascript", "js", "alloc", "memory"]
categories = ["memory-management"]
version.workspace = true
edition.workspace = true
authors.workspace = true
license.workspace = true
repository.workspace = true
rust-version.workspace = true

[features]

[dependencies]

[lints]
workspace = true

[package.metadata.docs.rs]
all-features = true
Loading
Loading