mirror of https://github.com/boa-dev/boa.git
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
257 lines
6.9 KiB
257 lines
6.9 KiB
use crate::{ |
|
finalizer_safe, |
|
internals::GcBox, |
|
trace::{Finalize, Trace}, |
|
Allocator, |
|
}; |
|
use std::{ |
|
cell::Cell, |
|
cmp::Ordering, |
|
fmt::{self, Debug, Display}, |
|
hash::{Hash, Hasher}, |
|
marker::PhantomData, |
|
ops::Deref, |
|
ptr::NonNull, |
|
rc::Rc, |
|
}; |
|
|
|
use super::rootable::Rootable; |
|
|
|
/// A garbage-collected pointer type over an immutable value. |
|
pub struct Gc<T: Trace + ?Sized + 'static> { |
|
pub(crate) inner_ptr: Cell<Rootable<GcBox<T>>>, |
|
pub(crate) marker: PhantomData<Rc<T>>, |
|
} |
|
|
|
impl<T: Trace> Gc<T> { |
|
/// Constructs a new `Gc<T>` with the given value. |
|
pub fn new(value: T) -> Self { |
|
// Create GcBox and allocate it to heap. |
|
// |
|
// Note: Allocator can cause Collector to run |
|
let inner_ptr = Allocator::alloc_gc(GcBox::new(value)); |
|
// SAFETY: inner_ptr was just allocated, so it must be a valid value that implements [`Trace`] |
|
unsafe { (*inner_ptr.as_ptr()).value().unroot() } |
|
|
|
// SAFETY: inner_ptr is 2-byte aligned. |
|
let inner_ptr = unsafe { Rootable::new_unchecked(inner_ptr) }; |
|
|
|
Self { |
|
inner_ptr: Cell::new(inner_ptr.rooted()), |
|
marker: PhantomData, |
|
} |
|
} |
|
|
|
/// Consumes the `Gc`, returning a wrapped raw pointer. |
|
/// |
|
/// To avoid a memory leak, the pointer must be converted back to a `Gc` using [`Gc::from_raw`]. |
|
#[allow(clippy::use_self)] |
|
pub fn into_raw(this: Gc<T>) -> NonNull<GcBox<T>> { |
|
let ptr = this.inner_ptr(); |
|
std::mem::forget(this); |
|
ptr |
|
} |
|
} |
|
|
|
impl<T: Trace + ?Sized> Gc<T> { |
|
/// Returns `true` if the two `Gc`s point to the same allocation. |
|
pub fn ptr_eq(this: &Self, other: &Self) -> bool { |
|
GcBox::ptr_eq(this.inner(), other.inner()) |
|
} |
|
|
|
/// Constructs a `Gc<T>` from a raw pointer. |
|
/// |
|
/// The raw pointer must have been returned by a previous call to [`Gc<U>::into_raw`][Gc::into_raw] |
|
/// where `U` must have the same size and alignment as `T`. |
|
/// |
|
/// # Safety |
|
/// |
|
/// This function is unsafe because improper use may lead to memory corruption, double-free, |
|
/// or misbehaviour of the garbage collector. |
|
#[must_use] |
|
pub unsafe fn from_raw(ptr: NonNull<GcBox<T>>) -> Self { |
|
// SAFETY: it is the caller's job to ensure the safety of this operation. |
|
unsafe { |
|
Self { |
|
inner_ptr: Cell::new(Rootable::new_unchecked(ptr).rooted()), |
|
marker: PhantomData, |
|
} |
|
} |
|
} |
|
} |
|
|
|
impl<T: Trace + ?Sized> Gc<T> { |
|
fn is_rooted(&self) -> bool { |
|
self.inner_ptr.get().is_rooted() |
|
} |
|
|
|
fn root_ptr(&self) { |
|
self.inner_ptr.set(self.inner_ptr.get().rooted()); |
|
} |
|
|
|
fn unroot_ptr(&self) { |
|
self.inner_ptr.set(self.inner_ptr.get().unrooted()); |
|
} |
|
|
|
pub(crate) fn inner_ptr(&self) -> NonNull<GcBox<T>> { |
|
assert!(finalizer_safe()); |
|
self.inner_ptr.get().as_ptr() |
|
} |
|
|
|
fn inner(&self) -> &GcBox<T> { |
|
// SAFETY: Please see Gc::inner_ptr() |
|
unsafe { self.inner_ptr().as_ref() } |
|
} |
|
} |
|
|
|
impl<T: Trace + ?Sized> Finalize for Gc<T> {} |
|
|
|
// SAFETY: `Gc` maintains it's own rootedness and implements all methods of |
|
// Trace. It is not possible to root an already rooted `Gc` and vice versa. |
|
unsafe impl<T: Trace + ?Sized> Trace for Gc<T> { |
|
unsafe fn trace(&self) { |
|
// SAFETY: Inner must be live and allocated GcBox. |
|
unsafe { |
|
self.inner().mark_and_trace(); |
|
} |
|
} |
|
|
|
unsafe fn root(&self) { |
|
assert!(!self.is_rooted(), "Can't double-root a Gc<T>"); |
|
// Try to get inner before modifying our state. Inner may be |
|
// inaccessible due to this method being invoked during the sweeping |
|
// phase, and we don't want to modify our state before panicking. |
|
self.inner().root(); |
|
self.root_ptr(); |
|
} |
|
|
|
unsafe fn unroot(&self) { |
|
assert!(self.is_rooted(), "Can't double-unroot a Gc<T>"); |
|
// Try to get inner before modifying our state. Inner may be |
|
// inaccessible due to this method being invoked during the sweeping |
|
// phase, and we don't want to modify our state before panicking. |
|
self.inner().unroot(); |
|
self.unroot_ptr(); |
|
} |
|
|
|
fn run_finalizer(&self) { |
|
Finalize::finalize(self); |
|
} |
|
} |
|
|
|
impl<T: Trace + ?Sized> Clone for Gc<T> { |
|
fn clone(&self) -> Self { |
|
let ptr = self.inner_ptr(); |
|
// SAFETY: since a `Gc` is always valid, its `inner_ptr` must also be always a valid pointer. |
|
unsafe { |
|
ptr.as_ref().root(); |
|
} |
|
// SAFETY: though `ptr` doesn't come from a `into_raw` call, it essentially does the same, |
|
// but it skips the call to `std::mem::forget` since we have a reference instead of an owned |
|
// value. |
|
unsafe { Self::from_raw(ptr) } |
|
} |
|
} |
|
|
|
impl<T: Trace + ?Sized> Deref for Gc<T> { |
|
type Target = T; |
|
|
|
fn deref(&self) -> &T { |
|
self.inner().value() |
|
} |
|
} |
|
|
|
impl<T: Trace + ?Sized> Drop for Gc<T> { |
|
fn drop(&mut self) { |
|
// If this pointer was a root, we should unroot it. |
|
if self.is_rooted() { |
|
self.inner().unroot(); |
|
} |
|
} |
|
} |
|
|
|
impl<T: Trace + Default> Default for Gc<T> { |
|
fn default() -> Self { |
|
Self::new(Default::default()) |
|
} |
|
} |
|
|
|
#[allow(clippy::inline_always)] |
|
impl<T: Trace + ?Sized + PartialEq> PartialEq for Gc<T> { |
|
#[inline(always)] |
|
fn eq(&self, other: &Self) -> bool { |
|
**self == **other |
|
} |
|
} |
|
|
|
impl<T: Trace + ?Sized + Eq> Eq for Gc<T> {} |
|
|
|
#[allow(clippy::inline_always)] |
|
impl<T: Trace + ?Sized + PartialOrd> PartialOrd for Gc<T> { |
|
#[inline(always)] |
|
fn partial_cmp(&self, other: &Self) -> Option<Ordering> { |
|
(**self).partial_cmp(&**other) |
|
} |
|
|
|
#[inline(always)] |
|
fn lt(&self, other: &Self) -> bool { |
|
**self < **other |
|
} |
|
|
|
#[inline(always)] |
|
fn le(&self, other: &Self) -> bool { |
|
**self <= **other |
|
} |
|
|
|
#[inline(always)] |
|
fn gt(&self, other: &Self) -> bool { |
|
**self > **other |
|
} |
|
|
|
#[inline(always)] |
|
fn ge(&self, other: &Self) -> bool { |
|
**self >= **other |
|
} |
|
} |
|
|
|
impl<T: Trace + ?Sized + Ord> Ord for Gc<T> { |
|
fn cmp(&self, other: &Self) -> Ordering { |
|
(**self).cmp(&**other) |
|
} |
|
} |
|
|
|
impl<T: Trace + ?Sized + Hash> Hash for Gc<T> { |
|
fn hash<H: Hasher>(&self, state: &mut H) { |
|
(**self).hash(state); |
|
} |
|
} |
|
|
|
impl<T: Trace + ?Sized + Display> Display for Gc<T> { |
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { |
|
Display::fmt(&**self, f) |
|
} |
|
} |
|
|
|
impl<T: Trace + ?Sized + Debug> Debug for Gc<T> { |
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { |
|
Debug::fmt(&**self, f) |
|
} |
|
} |
|
|
|
impl<T: Trace + ?Sized> fmt::Pointer for Gc<T> { |
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { |
|
fmt::Pointer::fmt(&self.inner(), f) |
|
} |
|
} |
|
|
|
impl<T: Trace + ?Sized> std::borrow::Borrow<T> for Gc<T> { |
|
fn borrow(&self) -> &T { |
|
self |
|
} |
|
} |
|
|
|
impl<T: Trace + ?Sized> AsRef<T> for Gc<T> { |
|
fn as_ref(&self) -> &T { |
|
self |
|
} |
|
}
|
|
|