feat: add dependency
This commit is contained in:
115
javascript-engine/external/boa/boa_gc/src/internals/ephemeron_box.rs
vendored
Normal file
115
javascript-engine/external/boa/boa_gc/src/internals/ephemeron_box.rs
vendored
Normal file
@@ -0,0 +1,115 @@
|
||||
use crate::{finalizer_safe, trace::Trace, Finalize, Gc, GcBox};
|
||||
use std::{cell::Cell, ptr::NonNull};
|
||||
|
||||
/// The inner allocation of an [`Ephemeron`][crate::Ephemeron] pointer.
|
||||
pub(crate) struct EphemeronBox<K: Trace + ?Sized + 'static, V: Trace + ?Sized + 'static> {
|
||||
key: Cell<Option<NonNull<GcBox<K>>>>,
|
||||
value: V,
|
||||
}
|
||||
|
||||
impl<K: Trace + ?Sized, V: Trace> EphemeronBox<K, V> {
|
||||
pub(crate) fn new(key: &Gc<K>, value: V) -> Self {
|
||||
Self {
|
||||
key: Cell::new(Some(key.inner_ptr())),
|
||||
value,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<K: Trace + ?Sized, V: Trace + ?Sized> EphemeronBox<K, V> {
|
||||
/// Checks if the key pointer is marked by Trace
|
||||
pub(crate) fn is_marked(&self) -> bool {
|
||||
self.inner_key().map_or(false, GcBox::is_marked)
|
||||
}
|
||||
|
||||
/// Returns some pointer to the `key`'s `GcBox` or None
|
||||
/// # Panics
|
||||
/// This method will panic if called while the garbage collector is dropping.
|
||||
pub(crate) fn inner_key_ptr(&self) -> Option<*mut GcBox<K>> {
|
||||
assert!(finalizer_safe());
|
||||
self.key.get().map(NonNull::as_ptr)
|
||||
}
|
||||
|
||||
/// Returns some reference to `key`'s `GcBox` or None
|
||||
pub(crate) fn inner_key(&self) -> Option<&GcBox<K>> {
|
||||
// SAFETY: This is safe as `EphemeronBox::inner_key_ptr()` will
|
||||
// fetch either a live `GcBox` or None. The value of `key` is set
|
||||
// to None in the case where `EphemeronBox` and `key`'s `GcBox`
|
||||
// entered into `Collector::sweep()` as unmarked.
|
||||
unsafe { self.inner_key_ptr().map(|inner_key| &*inner_key) }
|
||||
}
|
||||
|
||||
/// Returns a reference to the value of `key`'s `GcBox`
|
||||
pub(crate) fn key(&self) -> Option<&K> {
|
||||
self.inner_key().map(GcBox::value)
|
||||
}
|
||||
|
||||
/// Returns a reference to `value`
|
||||
pub(crate) const fn value(&self) -> &V {
|
||||
&self.value
|
||||
}
|
||||
|
||||
/// Calls [`Trace::weak_trace()`][crate::Trace] on key
|
||||
fn weak_trace_key(&self) {
|
||||
if let Some(key) = self.inner_key() {
|
||||
key.weak_trace_inner();
|
||||
}
|
||||
}
|
||||
|
||||
/// Calls [`Trace::weak_trace()`][crate::Trace] on value
|
||||
fn weak_trace_value(&self) {
|
||||
// SAFETY: Value is a sized element that must implement trace. The
|
||||
// operation is safe as EphemeronBox owns value and `Trace::weak_trace`
|
||||
// must be implemented on it
|
||||
unsafe {
|
||||
self.value().weak_trace();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// `EphemeronBox`'s Finalize is special in that if it is determined to be unreachable
|
||||
// and therefore so has the `GcBox` that `key`stores the pointer to, then we set `key`
|
||||
// to None to guarantee that we do not access freed memory.
|
||||
impl<K: Trace + ?Sized, V: Trace + ?Sized> Finalize for EphemeronBox<K, V> {
|
||||
fn finalize(&self) {
|
||||
self.key.set(None);
|
||||
}
|
||||
}
|
||||
|
||||
// SAFETY: EphemeronBox implements primarly two methods of trace `Trace::is_marked_ephemeron`
|
||||
// to determine whether the key field is stored and `Trace::weak_trace` which continues the `Trace::weak_trace()`
|
||||
// into `key` and `value`.
|
||||
unsafe impl<K: Trace + ?Sized, V: Trace + ?Sized> Trace for EphemeronBox<K, V> {
|
||||
unsafe fn trace(&self) {
|
||||
/* An ephemeron is never traced with Phase One Trace */
|
||||
}
|
||||
|
||||
/// Checks if the `key`'s `GcBox` has been marked by `Trace::trace()` or `Trace::weak_trace`.
|
||||
fn is_marked_ephemeron(&self) -> bool {
|
||||
self.is_marked()
|
||||
}
|
||||
|
||||
/// Checks if this `EphemeronBox` has already been determined reachable. If so, continue to trace
|
||||
/// value in `key` and `value`.
|
||||
unsafe fn weak_trace(&self) {
|
||||
if self.is_marked() {
|
||||
self.weak_trace_key();
|
||||
self.weak_trace_value();
|
||||
}
|
||||
}
|
||||
|
||||
// EphemeronBox does not implement root.
|
||||
unsafe fn root(&self) {}
|
||||
|
||||
// EphemeronBox does not implement unroot
|
||||
unsafe fn unroot(&self) {}
|
||||
|
||||
// An `EphemeronBox`'s key is set to None once it has been finalized.
|
||||
//
|
||||
// NOTE: while it is possible for the `key`'s pointer value to be
|
||||
// resurrected, we should still consider the finalize the ephemeron
|
||||
// box and set the `key` to None.
|
||||
fn run_finalizer(&self) {
|
||||
Finalize::finalize(self);
|
||||
}
|
||||
}
|
||||
185
javascript-engine/external/boa/boa_gc/src/internals/gc_box.rs
vendored
Normal file
185
javascript-engine/external/boa/boa_gc/src/internals/gc_box.rs
vendored
Normal file
@@ -0,0 +1,185 @@
|
||||
use crate::Trace;
|
||||
use std::{
|
||||
cell::Cell,
|
||||
fmt,
|
||||
ptr::{self, NonNull},
|
||||
};
|
||||
|
||||
// Age and Weak Flags
|
||||
const MARK_MASK: usize = 1 << (usize::BITS - 2);
|
||||
const WEAK_MASK: usize = 1 << (usize::BITS - 1);
|
||||
const ROOTS_MASK: usize = !(MARK_MASK | WEAK_MASK);
|
||||
const ROOTS_MAX: usize = ROOTS_MASK;
|
||||
|
||||
/// The `GcBoxheader` contains the `GcBox`'s current state for the `Collector`'s
|
||||
/// Mark/Sweep as well as a pointer to the next node in the heap.
|
||||
///
|
||||
/// These flags include:
|
||||
/// - Root Count
|
||||
/// - Mark Flag Bit
|
||||
/// - Weak Flag Bit
|
||||
///
|
||||
/// The next node is set by the `Allocator` during initialization and by the
|
||||
/// `Collector` during the sweep phase.
|
||||
pub(crate) struct GcBoxHeader {
|
||||
roots: Cell<usize>,
|
||||
pub(crate) next: Cell<Option<NonNull<GcBox<dyn Trace>>>>,
|
||||
}
|
||||
|
||||
impl GcBoxHeader {
|
||||
/// Creates a new `GcBoxHeader` with a root of 1 and next set to None.
|
||||
pub(crate) fn new() -> Self {
|
||||
Self {
|
||||
roots: Cell::new(1),
|
||||
next: Cell::new(None),
|
||||
}
|
||||
}
|
||||
|
||||
/// Creates a new `GcBoxHeader` with the Weak bit at 1 and roots of 1.
|
||||
pub(crate) fn new_weak() -> Self {
|
||||
// Set weak_flag
|
||||
Self {
|
||||
roots: Cell::new(WEAK_MASK | 1),
|
||||
next: Cell::new(None),
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the `GcBoxHeader`'s current root count
|
||||
pub(crate) fn roots(&self) -> usize {
|
||||
self.roots.get() & ROOTS_MASK
|
||||
}
|
||||
|
||||
/// Increments `GcBoxHeader`'s root count.
|
||||
pub(crate) fn inc_roots(&self) {
|
||||
let roots = self.roots.get();
|
||||
|
||||
if (roots & ROOTS_MASK) < ROOTS_MAX {
|
||||
self.roots.set(roots + 1);
|
||||
} else {
|
||||
// TODO: implement a better way to handle root overload.
|
||||
panic!("roots counter overflow");
|
||||
}
|
||||
}
|
||||
|
||||
/// Decreases `GcBoxHeader`'s current root count.
|
||||
pub(crate) fn dec_roots(&self) {
|
||||
// Underflow check as a stop gap for current issue when dropping.
|
||||
if self.roots.get() > 0 {
|
||||
self.roots.set(self.roots.get() - 1);
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns a bool for whether `GcBoxHeader`'s mark bit is 1.
|
||||
pub(crate) fn is_marked(&self) -> bool {
|
||||
self.roots.get() & MARK_MASK != 0
|
||||
}
|
||||
|
||||
/// Sets `GcBoxHeader`'s mark bit to 1.
|
||||
pub(crate) fn mark(&self) {
|
||||
self.roots.set(self.roots.get() | MARK_MASK);
|
||||
}
|
||||
|
||||
/// Sets `GcBoxHeader`'s mark bit to 0.
|
||||
pub(crate) fn unmark(&self) {
|
||||
self.roots.set(self.roots.get() & !MARK_MASK);
|
||||
}
|
||||
|
||||
/// Returns a bool for whether the `GcBoxHeader`'s weak bit is 1.
|
||||
pub(crate) fn is_ephemeron(&self) -> bool {
|
||||
self.roots.get() & WEAK_MASK != 0
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Debug for GcBoxHeader {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
f.debug_struct("GcBoxHeader")
|
||||
.field("Roots", &self.roots())
|
||||
.field("Weak", &self.is_ephemeron())
|
||||
.field("Marked", &self.is_marked())
|
||||
.finish()
|
||||
}
|
||||
}
|
||||
|
||||
/// A garbage collected allocation.
|
||||
#[derive(Debug)]
|
||||
pub struct GcBox<T: Trace + ?Sized + 'static> {
|
||||
pub(crate) header: GcBoxHeader,
|
||||
pub(crate) value: T,
|
||||
}
|
||||
|
||||
impl<T: Trace> GcBox<T> {
|
||||
/// Returns a new `GcBox` with a rooted `GcBoxHeader`.
|
||||
pub(crate) fn new(value: T) -> Self {
|
||||
Self {
|
||||
header: GcBoxHeader::new(),
|
||||
value,
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns a new `GcBox` with a rooted and weak `GcBoxHeader`.
|
||||
pub(crate) fn new_weak(value: T) -> Self {
|
||||
Self {
|
||||
header: GcBoxHeader::new_weak(),
|
||||
value,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Trace + ?Sized> GcBox<T> {
|
||||
/// Returns `true` if the two references refer to the same `GcBox`.
|
||||
pub(crate) fn ptr_eq(this: &Self, other: &Self) -> bool {
|
||||
// Use .header to ignore fat pointer vtables, to work around
|
||||
// https://github.com/rust-lang/rust/issues/46139
|
||||
ptr::eq(&this.header, &other.header)
|
||||
}
|
||||
|
||||
/// Marks this `GcBox` and marks through its data.
|
||||
pub(crate) unsafe fn trace_inner(&self) {
|
||||
if !self.header.is_marked() && !self.header.is_ephemeron() {
|
||||
self.header.mark();
|
||||
// SAFETY: if `GcBox::trace_inner()` has been called, then,
|
||||
// this box must have been deemed as reachable via tracing
|
||||
// from a root, which by extension means that value has not
|
||||
// been dropped either.
|
||||
unsafe {
|
||||
self.value.trace();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Trace inner data and search for ephemerons to add to the ephemeron queue.
|
||||
pub(crate) fn weak_trace_inner(&self) {
|
||||
if !self.header.is_marked() && !self.header.is_ephemeron() {
|
||||
self.header.mark();
|
||||
// SAFETY: if a `GcBox` has `weak_trace_inner` called, then the inner.
|
||||
// value must have been deemed as reachable.
|
||||
unsafe {
|
||||
self.value.weak_trace();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Increases the root count on this `GcBox`.
|
||||
///
|
||||
/// Roots prevent the `GcBox` from being destroyed by the garbage collector.
|
||||
pub(crate) fn root_inner(&self) {
|
||||
self.header.inc_roots();
|
||||
}
|
||||
|
||||
/// Decreases the root count on this `GcBox`.
|
||||
///
|
||||
/// Roots prevent the `GcBox` from being destroyed by the garbage collector.
|
||||
pub(crate) fn unroot_inner(&self) {
|
||||
self.header.dec_roots();
|
||||
}
|
||||
|
||||
/// Returns a reference to the `GcBox`'s value.
|
||||
pub(crate) const fn value(&self) -> &T {
|
||||
&self.value
|
||||
}
|
||||
|
||||
/// Returns a bool for whether the header is marked.
|
||||
pub(crate) fn is_marked(&self) -> bool {
|
||||
self.header.is_marked()
|
||||
}
|
||||
}
|
||||
5
javascript-engine/external/boa/boa_gc/src/internals/mod.rs
vendored
Normal file
5
javascript-engine/external/boa/boa_gc/src/internals/mod.rs
vendored
Normal file
@@ -0,0 +1,5 @@
|
||||
mod ephemeron_box;
|
||||
mod gc_box;
|
||||
|
||||
pub(crate) use self::ephemeron_box::EphemeronBox;
|
||||
pub use self::gc_box::GcBox;
|
||||
Reference in New Issue
Block a user