Skip to content

Commit

Permalink
make Handle and Persistent the size of a machine pointer
Browse files Browse the repository at this point in the history
  • Loading branch information
y21 committed Dec 29, 2023
1 parent 0144323 commit 46445ac
Show file tree
Hide file tree
Showing 37 changed files with 578 additions and 552 deletions.
2 changes: 1 addition & 1 deletion crates/dash_node_impl/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -154,7 +154,7 @@ impl Object for RequireFunction {
fn apply(
&self,
scope: &mut LocalScope,
_callee: dash_vm::gc::handle::Handle<dyn Object>,
_callee: dash_vm::gc::handle::Handle,
_this: Value,
args: Vec<Value>,
) -> Result<Unrooted, Unrooted> {
Expand Down
1 change: 1 addition & 0 deletions crates/dash_rt/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ use dash_vm::frame::{Exports, Frame};
use dash_vm::gc::persistent::Persistent;
use dash_vm::localscope::LocalScope;
use dash_vm::value::function::native::CallContext;
use dash_vm::value::object::Object;
use dash_vm::value::ops::conversions::ValueConversion;
use dash_vm::value::promise::Promise;
use dash_vm::value::{Root, Value};
Expand Down
9 changes: 4 additions & 5 deletions crates/dash_rt/src/state.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ use std::rc::Rc;
use std::sync::atomic::{AtomicU64, Ordering};

use dash_vm::gc::persistent::Persistent;
use dash_vm::value::object::Object;
use dash_vm::Vm;
use rustc_hash::FxHashMap;

Expand All @@ -16,7 +15,7 @@ pub struct State {
tx: EventSender,
root_module: Rc<RefCell<Option<Box<dyn ModuleLoader>>>>,
tasks: TaskIds,
promises: RefCell<FxHashMap<u64, Persistent<dyn Object>>>,
promises: RefCell<FxHashMap<u64, Persistent>>,
}

impl State {
Expand Down Expand Up @@ -65,19 +64,19 @@ impl State {
self.rt.clone()
}

pub fn add_pending_promise(&self, promise: Persistent<dyn Object>) -> u64 {
pub fn add_pending_promise(&self, promise: Persistent) -> u64 {
static NEXT_PROMISE_ID: AtomicU64 = AtomicU64::new(0);
let id = NEXT_PROMISE_ID.fetch_add(1, Ordering::Relaxed);
self.promises.borrow_mut().insert(id, promise);
id
}

pub fn take_promise(&self, id: u64) -> Persistent<dyn Object> {
pub fn take_promise(&self, id: u64) -> Persistent {
self.try_take_promise(id)
.expect("Attempted to take a promise that was already taken")
}

pub fn try_take_promise(&self, id: u64) -> Option<Persistent<dyn Object>> {
pub fn try_take_promise(&self, id: u64) -> Option<Persistent> {
self.promises.borrow_mut().remove(&id)
}
}
4 changes: 2 additions & 2 deletions crates/dash_rt_net/src/listener/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ impl Object for TcpListenerConstructor {
fn apply(
&self,
scope: &mut dash_vm::localscope::LocalScope,
_callee: dash_vm::gc::handle::Handle<dyn Object>,
_callee: dash_vm::gc::handle::Handle,
_this: dash_vm::value::Value,
_args: Vec<dash_vm::value::Value>,
) -> Result<dash_vm::value::Unrooted, dash_vm::value::Unrooted> {
Expand All @@ -76,7 +76,7 @@ impl Object for TcpListenerConstructor {
fn construct(
&self,
scope: &mut dash_vm::localscope::LocalScope,
_callee: dash_vm::gc::handle::Handle<dyn Object>,
_callee: dash_vm::gc::handle::Handle,
_this: Value,
args: Vec<Value>,
) -> Result<Unrooted, Unrooted> {
Expand Down
9 changes: 4 additions & 5 deletions crates/dash_vm/src/external.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,9 @@ use crate::gc::handle::Handle;
use crate::gc::trace::{Trace, TraceCtxt};

use super::localscope::LocalScope;
use super::value::object::Object;

#[derive(Debug, Default)]
pub struct Externals(FxHashMap<*const (), Vec<Handle<dyn Object>>>);
pub struct Externals(FxHashMap<*const (), Vec<Handle>>);

unsafe impl Trace for Externals {
fn trace(&self, cx: &mut TraceCtxt<'_>) {
Expand All @@ -22,15 +21,15 @@ impl Externals {
Self::default()
}

pub fn extend_from_scope(&mut self, sc: *const LocalScope, mut refs: Vec<Handle<dyn Object>>) {
pub fn extend_from_scope(&mut self, sc: *const LocalScope, mut refs: Vec<Handle>) {
self.0.entry(sc.cast()).or_default().append(&mut refs);
}

pub fn add_single(&mut self, sc: *const LocalScope, re: Handle<dyn Object>) {
pub fn add_single(&mut self, sc: *const LocalScope, re: Handle) {
self.0.entry(sc.cast()).or_default().push(re)
}

pub fn remove(&mut self, sc: *const LocalScope) -> Option<Vec<Handle<dyn Object>>> {
pub fn remove(&mut self, sc: *const LocalScope) -> Option<Vec<Handle>> {
self.0.remove(&sc.cast())
}
}
131 changes: 82 additions & 49 deletions crates/dash_vm/src/gc/handle.rs
Original file line number Diff line number Diff line change
@@ -1,17 +1,21 @@
use std::any::Any;
use std::cell::Cell;
use std::fmt::Debug;
use std::hash::Hash;
use std::ops::Deref;
use std::ptr::NonNull;
use std::ptr::{addr_of, NonNull};

use bitflags::bitflags;

use crate::value::object::Object;
use crate::localscope::LocalScope;
use crate::value::object::{PropertyKey, PropertyValue};
use crate::value::primitive::PrimitiveCapabilities;
use crate::value::{Typeof, Unrooted, Value};

use super::trace::{Trace, TraceCtxt};

bitflags! {
#[derive(Default)]
struct HandleFlagsInner: u8 {
pub struct HandleFlagsInner: u8 {
/// Whether the node has been visited in the last mark phase or not.
const MARKED_VISITED = 1 << 0;
const VM_DETACHED = 1 << 1;
Expand Down Expand Up @@ -50,83 +54,112 @@ impl HandleFlags {
}
}

pub struct GcNode<T: ?Sized> {
#[repr(C)]
pub struct ObjectVTable {
pub(crate) drop_boxed_gcnode: unsafe fn(*mut GcNode<()>),
pub(crate) trace: unsafe fn(*const (), &mut TraceCtxt<'_>),
pub(crate) debug_fmt: unsafe fn(*const (), &mut core::fmt::Formatter<'_>) -> core::fmt::Result,
pub(crate) js_get_own_property:
unsafe fn(*const (), &mut LocalScope<'_>, Value, PropertyKey) -> Result<Unrooted, Unrooted>,
pub(crate) js_get_own_property_descriptor:
unsafe fn(*const (), &mut LocalScope<'_>, PropertyKey) -> Result<Option<PropertyValue>, Unrooted>,
pub(crate) js_get_property: unsafe fn(*const (), &mut LocalScope, Value, PropertyKey) -> Result<Unrooted, Unrooted>,
pub(crate) js_get_property_descriptor:
unsafe fn(*const (), &mut LocalScope<'_>, PropertyKey) -> Result<Option<PropertyValue>, Unrooted>,
pub(crate) js_set_property:
unsafe fn(*const (), &mut LocalScope<'_>, PropertyKey, PropertyValue) -> Result<(), Value>,
pub(crate) js_delete_property: unsafe fn(*const (), &mut LocalScope<'_>, PropertyKey) -> Result<Unrooted, Value>,
pub(crate) js_set_prototype: unsafe fn(*const (), &mut LocalScope<'_>, Value) -> Result<(), Value>,
pub(crate) js_get_prototype: unsafe fn(*const (), &mut LocalScope<'_>) -> Result<Value, Value>,
pub(crate) js_apply:
unsafe fn(*const (), &mut LocalScope<'_>, Handle, Value, Vec<Value>) -> Result<Unrooted, Unrooted>,
pub(crate) js_construct:
unsafe fn(*const (), &mut LocalScope<'_>, Handle, Value, Vec<Value>) -> Result<Unrooted, Unrooted>,
pub(crate) js_as_any: unsafe fn(*const ()) -> *const dyn Any,
pub(crate) js_as_primitive_capable: unsafe fn(*const ()) -> Option<*const dyn PrimitiveCapabilities>,
pub(crate) js_own_keys: unsafe fn(*const (), sc: &mut LocalScope<'_>) -> Result<Vec<Value>, Value>,
pub(crate) js_type_of: unsafe fn(*const ()) -> Typeof,
}

#[repr(C)]
pub struct GcNode<T> {
pub(crate) vtable: &'static ObjectVTable,
pub(crate) flags: HandleFlags,
/// Persistent<T> reference count
pub(crate) refcount: Cell<u64>,
/// The next pointer in the linked list of nodes
pub(crate) next: Option<NonNull<GcNode<dyn Object>>>,
pub(crate) next: Option<NonNull<GcNode<()>>>,
pub(crate) value: T,
}

#[derive(Debug)]
pub struct Handle<T: ?Sized>(NonNull<GcNode<T>>);
#[repr(C)]
#[derive(Eq, PartialEq, Clone, Hash)]
pub struct Handle(NonNull<GcNode<()>>);

impl Debug for Handle {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
unsafe { (self.vtable().debug_fmt)(addr_of!((*self.0.as_ptr()).value), f) }
}
}

impl<T: ?Sized> Handle<T> {
impl Handle {
/// # Safety
/// The given [`NonNull`] pointer must point to a valid [`InnerHandle`]
pub unsafe fn from_raw(ptr: NonNull<GcNode<T>>) -> Self {
/// The given [`NonNull`] pointer must point to a valid [`GcNode`]
pub unsafe fn from_raw(ptr: NonNull<GcNode<()>>) -> Self {
Self(ptr)
}
}

pub fn into_raw(ptr: Self) -> NonNull<GcNode<T>> {
ptr.0
impl Handle {
pub fn as_ptr<U>(&self) -> *mut GcNode<U> {
self.0.as_ptr().cast()
}
}
impl<T: Object + 'static> Handle<T> {
pub fn into_dyn(self) -> Handle<dyn Object> {
let ptr = Handle::into_raw(self);
// SAFETY: `T: Object` bound makes this cast safe. once CoerceUnsized is stable, we can use that instead.
unsafe { Handle::<dyn Object>::from_raw(ptr) }

pub fn as_erased_ptr(&self) -> *mut GcNode<()> {
self.0.as_ptr()
}
}

impl<T: ?Sized> Eq for Handle<T> {}
pub fn erased_value(&self) -> *const () {
unsafe { addr_of!((*self.0.as_ptr()).value) }
}

impl<T: ?Sized> PartialEq for Handle<T> {
fn eq(&self, other: &Self) -> bool {
self.0 == other.0
pub fn vtable(&self) -> &'static ObjectVTable {
unsafe { &(*self.0.as_ptr()).vtable }
}
}

impl<T: ?Sized> Clone for Handle<T> {
fn clone(&self) -> Self {
Self(self.0)
/// Returns the `Persistent<T>` refcount.
pub fn refcount(&self) -> u64 {
unsafe { (*self.0.as_ptr()).refcount.get() }
}
}

// FIXME: this is severly unsound and hard to fix: this Handle can be smuggled because it's not tied to any reference
// and later, when its GC goes out of scope, it will be freed, even though it's still alive
impl<T: ?Sized> Deref for Handle<T> {
type Target = T;
fn deref(&self) -> &T {
unsafe { &(*self.as_ptr()).value }
pub unsafe fn set_refcount(&self, refcount: u64) {
(*self.0.as_ptr()).refcount.set(refcount);
}
}

impl<T: ?Sized> Handle<T> {
pub fn as_ptr(&self) -> *mut GcNode<T> {
self.0.as_ptr()
pub fn next(&self) -> Option<NonNull<GcNode<()>>> {
unsafe { (*self.0.as_ptr()).next }
}

pub fn flags(&self) -> HandleFlagsInner {
unsafe { (*self.0.as_ptr()).flags.flags.get() }
}
}

impl<T: ?Sized> Hash for Handle<T> {
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
self.as_ptr().hash(state);
pub fn interior_flags(&self) -> &HandleFlags {
unsafe { &(*self.0.as_ptr()).flags }
}
}

unsafe impl<T: ?Sized + Trace> Trace for Handle<T> {
unsafe impl Trace for Handle {
fn trace(&self, cx: &mut TraceCtxt<'_>) {
unsafe {
let this = self.0.as_ref();
if this.flags.is_marked() {
let flags = &(*self.0.as_ptr()).flags;
if flags.is_marked() {
// If already marked, do nothing to avoid getting stucked in an infinite loop
return;
}
this.flags.mark();
};
flags.mark();

T::trace(self, cx);
(self.vtable().trace)(self.erased_value(), cx)
};
}
}
Loading

0 comments on commit 46445ac

Please sign in to comment.