diff --git a/build.rs b/build.rs index 86befe0b..4aead477 100644 --- a/build.rs +++ b/build.rs @@ -157,6 +157,7 @@ fn build_binding() { .rustified_enum(".*UseCounterFeature") .allowlist_item("v8__.*") .allowlist_item("cppgc__.*") + .allowlist_item("RustObj") .generate() .expect("Unable to generate bindings"); diff --git a/src/binding.cc b/src/binding.cc index de913d5d..a2161d17 100644 --- a/src/binding.cc +++ b/src/binding.cc @@ -3856,24 +3856,19 @@ void v8__PropertyDescriptor__set_configurable(v8::PropertyDescriptor* self, extern "C" { -class RustObj; +void rusty_v8_RustObj_trace(const RustObj*, cppgc::Visitor*); +const char* rusty_v8_RustObj_get_name(const RustObj*); +void rusty_v8_RustObj_drop(RustObj*); -using RustTraceFn = void (*)(const RustObj* obj, cppgc::Visitor*); -using RustDestroyFn = void (*)(const RustObj* obj); +RustObj::~RustObj() { rusty_v8_RustObj_drop(this); } -class RustObj final : public cppgc::GarbageCollected { - public: - explicit RustObj(RustTraceFn trace, RustDestroyFn destroy) - : trace_(trace), destroy_(destroy) {} +void RustObj::Trace(cppgc::Visitor* visitor) const { + rusty_v8_RustObj_trace(this, visitor); +} - ~RustObj() { destroy_(this); } - - void Trace(cppgc::Visitor* visitor) const { trace_(this, visitor); } - - private: - RustTraceFn trace_; - RustDestroyFn destroy_; -}; +const char* RustObj::GetHumanReadableName() const { + return rusty_v8_RustObj_get_name(this); +} RustObj* v8__Object__Unwrap(v8::Isolate* isolate, const v8::Object& wrapper, v8::CppHeapPointerTag tag) { @@ -3930,12 +3925,9 @@ void cppgc__heap__collect_garbage_for_testing( heap->CollectGarbageForTesting(stack_state); } -RustObj* cppgc__make_garbage_collectable(v8::CppHeap* heap, size_t size, - RustTraceFn trace, - RustDestroyFn destroy) { +RustObj* cppgc__make_garbage_collectable(v8::CppHeap* heap, size_t size) { return cppgc::MakeGarbageCollected(heap->GetAllocationHandle(), - cppgc::AdditionalBytes(size), - trace, destroy); + cppgc::AdditionalBytes(size)); } void cppgc__Visitor__Trace__Member(cppgc::Visitor* visitor, diff --git a/src/binding.hpp b/src/binding.hpp index 8f16ccc3..2f27a6ed 100644 --- a/src/binding.hpp +++ b/src/binding.hpp @@ -11,8 +11,6 @@ * and made available in `crate::binding` in rust. */ -class RustObj; - static size_t v8__ScriptOrigin_SIZE = sizeof(v8::ScriptOrigin); static size_t cppgc__Member_SIZE = sizeof(cppgc::Member); diff --git a/src/cppgc.rs b/src/cppgc.rs index 7ecd6a80..9601b06a 100644 --- a/src/cppgc.rs +++ b/src/cppgc.rs @@ -1,5 +1,6 @@ // Copyright 2019-2021 the Deno authors. All rights reserved. MIT license +use crate::binding::RustObj; use crate::platform::Platform; use crate::support::int; use crate::support::Opaque; @@ -7,6 +8,8 @@ use crate::support::SharedRef; use crate::support::UniqueRef; use crate::Data; use crate::TracedReference; +use std::ffi::c_char; +use std::ffi::CStr; use std::marker::PhantomData; use std::ptr::NonNull; @@ -24,8 +27,6 @@ extern "C" { fn cppgc__make_garbage_collectable( heap: *mut Heap, size: usize, - trace: TraceFn, - destroy: DestroyFn, ) -> *mut RustObj; fn cppgc__heap__enable_detached_garbage_collections_for_testing( @@ -83,14 +84,42 @@ extern "C" { ) -> *mut RustObj; } -type TraceFn = unsafe extern "C" fn(*const RustObj, *mut Visitor); -type DestroyFn = unsafe extern "C" fn(*const RustObj); +unsafe fn get_rust_obj<'s>(obj: *const RustObj) -> &'s dyn GarbageCollected { + &*std::mem::transmute::<[usize; 2], *mut dyn GarbageCollected>((*obj).data) +} -#[doc(hidden)] -#[repr(C)] -pub struct RustObj { - trace: TraceFn, - destroy: DestroyFn, +unsafe fn get_rust_obj_mut<'s>( + obj: *mut RustObj, +) -> &'s mut dyn GarbageCollected { + &mut *std::mem::transmute::<[usize; 2], *mut dyn GarbageCollected>( + (*obj).data, + ) +} + +#[no_mangle] +unsafe extern "C" fn rusty_v8_RustObj_trace( + obj: *const RustObj, + visitor: *mut Visitor, +) { + let r = get_rust_obj(obj); + r.trace(&*visitor); +} + +#[no_mangle] +unsafe extern "C" fn rusty_v8_RustObj_get_name( + obj: *const RustObj, +) -> *const c_char { + let r = get_rust_obj(obj); + match r.get_name() { + Some(s) => s.as_ptr(), + None => std::ptr::null(), + } +} + +#[no_mangle] +unsafe extern "C" fn rusty_v8_RustObj_drop(obj: *mut RustObj) { + let r = get_rust_obj_mut(obj); + std::ptr::drop_in_place(r); } fn object_offset_for_rust_obj() -> usize { @@ -103,7 +132,10 @@ fn object_offset_for_rust_obj() -> usize { std::mem::offset_of!(Calc, data) } -fn get_object_from_rust_obj( +/// # Safety +/// +/// T must be the correct type for this specific RustObj +unsafe fn get_object_from_rust_obj( rust_obj: *const RustObj, ) -> *mut T { unsafe { rust_obj.byte_add(object_offset_for_rust_obj::()) as *mut T } @@ -276,7 +308,23 @@ impl Heap { /// Base trait for managed objects. pub trait GarbageCollected { + /// `trace` should call `Visitor::visit` for each + /// `Member`, `WeakMember`, or `TracedReference` in + /// by the managed object. fn trace(&self, _visitor: &Visitor) {} + + /// Specifies a name for the garbage-collected object. Such names will never + /// be hidden, as they are explicitly specified by the user of this API. + /// + /// V8 may call this function while generating a heap snapshot or at other + /// times. If V8 is currently generating a heap snapshot (according to + /// HeapProfiler::IsTakingSnapshot), then the returned string must stay alive + /// until the snapshot generation has completed. Otherwise, the returned string + /// must stay alive forever. If you need a place to store a temporary string + /// during snapshot generation, use HeapProfiler::CopyNameForHeapSnapshot. + fn get_name(&self) -> Option<&'static CStr> { + None + } } /// Constructs an instance of T, which is a garbage collected type. @@ -291,23 +339,10 @@ pub trait GarbageCollected { /// /// The caller must ensure that the returned pointer is always stored on /// the stack, or is safely moved into one of the other cppgc pointer types. -pub unsafe fn make_garbage_collected( +pub unsafe fn make_garbage_collected( heap: &Heap, obj: T, ) -> Ptr { - unsafe extern "C" fn trace( - obj: *const RustObj, - visitor: *mut Visitor, - ) { - let obj = unsafe { &*get_object_from_rust_obj::(obj) }; - obj.trace(unsafe { &*visitor }); - } - - unsafe extern "C" fn destroy(obj: *const RustObj) { - let obj = get_object_from_rust_obj::(obj); - std::ptr::drop_in_place(obj); - } - let additional_bytes = (object_offset_for_rust_obj::() - std::mem::size_of::()) + std::mem::size_of::(); @@ -316,13 +351,17 @@ pub unsafe fn make_garbage_collected( cppgc__make_garbage_collectable( heap as *const Heap as *mut _, additional_bytes, - trace::, - destroy::, ) }; unsafe { - get_object_from_rust_obj::(pointer).write(obj); + let inner = get_object_from_rust_obj::(pointer); + inner.write(obj); + + let rust_obj = &mut *pointer; + rust_obj.data = std::mem::transmute::<*mut dyn GarbageCollected, [usize; 2]>( + &mut *inner as &mut dyn GarbageCollected as *mut dyn GarbageCollected, + ); } Ptr { diff --git a/src/object.rs b/src/object.rs index bec96f7c..f87141fb 100644 --- a/src/object.rs +++ b/src/object.rs @@ -1,7 +1,7 @@ +use crate::binding::RustObj; use crate::cppgc::GarbageCollected; use crate::cppgc::GetRustObj; use crate::cppgc::Ptr; -use crate::cppgc::RustObj; use crate::isolate::Isolate; use crate::support::int; use crate::support::MapFnTo; diff --git a/src/support.h b/src/support.h index 48d77434..a530343c 100644 --- a/src/support.h +++ b/src/support.h @@ -9,6 +9,8 @@ #include #include +#include "v8/include/cppgc/name-provider.h" +#include "v8/include/v8-cppgc.h" #include "v8/include/v8.h" // Work around a bug in the V8 headers. @@ -190,3 +192,12 @@ struct three_pointers_t { V(BigInt64Array) #endif // SUPPORT_H_ + +class RustObj final : public cppgc::GarbageCollected, + public cppgc::NameProvider { + public: + ~RustObj(); + void Trace(cppgc::Visitor* visitor) const; + const char* GetHumanReadableName() const final; + uintptr_t data[2]; +}; diff --git a/tests/test_cppgc.rs b/tests/test_cppgc.rs index b44a9293..6a84622b 100644 --- a/tests/test_cppgc.rs +++ b/tests/test_cppgc.rs @@ -44,6 +44,10 @@ fn cppgc_object_wrap() { TRACE_COUNT.fetch_add(1, Ordering::SeqCst); visitor.trace(&self.value); } + + fn get_name(&self) -> Option<&'static std::ffi::CStr> { + Some(c"Eyecatcher") + } } impl Drop for Wrap { @@ -134,6 +138,16 @@ fn cppgc_object_wrap() { assert_eq!(DROP_COUNT.load(Ordering::SeqCst), 0); + { + let mut vec = Vec::::new(); + scope.take_heap_snapshot(|chunk| { + vec.extend_from_slice(chunk); + true + }); + let s = std::str::from_utf8(&vec).unwrap(); + assert!(s.contains("Eyecatcher")); + } + scope.request_garbage_collection_for_testing( v8::GarbageCollectionType::Full, );