From e5bf4aa12219224534f811259056c80b25b656d7 Mon Sep 17 00:00:00 2001 From: Divy Srivastava Date: Thu, 28 Sep 2023 17:58:46 +0530 Subject: [PATCH 01/17] Add `cppgc` bindings --- examples/cppgc.rs | 45 +++++++++++++++++++ src/binding.cc | 62 ++++++++++++++++++++++++++ src/cppgc.rs | 110 ++++++++++++++++++++++++++++++++++++++++++++++ src/lib.rs | 1 + 4 files changed, 218 insertions(+) create mode 100644 examples/cppgc.rs create mode 100644 src/cppgc.rs diff --git a/examples/cppgc.rs b/examples/cppgc.rs new file mode 100644 index 0000000000..e55238a99f --- /dev/null +++ b/examples/cppgc.rs @@ -0,0 +1,45 @@ +struct Resource { + name: String, +} + +impl Drop for Resource { + fn drop(&mut self) { + println!("Dropping {}", self.name); + } +} + +fn main() { + let platform = v8::new_default_platform(0, false).make_shared(); + v8::V8::initialize_platform(platform.clone()); + v8::V8::initialize(); + + v8::cppgc::initalize_process(platform.clone()); + { + let heap = v8::cppgc::Heap::create(platform); + + let obj = make_object(&*heap, "hello"); + + heap.enable_detached_garbage_collections_for_testing(); + + heap.force_garbage_collection_slow( + v8::cppgc::EmbedderStackState::MayContainHeapPointers, + ); + heap.force_garbage_collection_slow( + v8::cppgc::EmbedderStackState::NoHeapPointers, + ); + } + unsafe { v8::cppgc::shutdown_process() }; +} + +fn make_object(heap: &v8::cppgc::Heap, name: &str) -> *mut Resource { + extern "C" fn trace(visitor: *mut v8::cppgc::Visitor, obj: *mut ()) { + let obj = unsafe { &*(obj as *const Resource) }; + println!("Trace {}", obj.name); + } + + let val = Box::new(Resource { + name: name.to_string(), + }); + let obj = v8::cppgc::make_garbage_collected(heap, val, trace); + return obj; +} diff --git a/src/binding.cc b/src/binding.cc index 6dab984650..ab5cecf953 100644 --- a/src/binding.cc +++ b/src/binding.cc @@ -11,6 +11,7 @@ #include "unicode/locid.h" #include "v8-callbacks.h" #include "v8/include/libplatform/libplatform.h" +#include "v8/include/v8-cppgc.h" #include "v8/include/v8-fast-api-calls.h" #include "v8/include/v8-inspector.h" #include "v8/include/v8-internal.h" @@ -29,6 +30,8 @@ #include "v8/src/objects/objects.h" #include "v8/src/objects/smi.h" +#include "cppgc/platform.h" + using namespace support; template @@ -3571,3 +3574,62 @@ void v8__PropertyDescriptor__set_configurable(v8::PropertyDescriptor* self, } } // extern "C" + +// cppgc + +extern "C" { + +using RustTraceFn = void (*)(cppgc::Visitor*, void* obj); +using RustDestroyFn = void (*)(void* obj); + +class RustObj final: public cppgc::GarbageCollected { + public: + explicit RustObj(void* obj, RustTraceFn trace, RustDestroyFn destroy): trace_(trace), destroy_(destroy), obj_(obj) {} + + ~RustObj() { + destroy_(obj_); + } + + void Trace(cppgc::Visitor* visitor) const { + trace_(visitor, obj_); + } + + private: + RustTraceFn trace_; + RustDestroyFn destroy_; + void* obj_; +}; + +enum InternalFields { kEmbedderType, kSlot, kInternalFieldCount }; + +constexpr uint16_t kDefaultCppGCEmebdderID = 0x90de; + +void cppgc__initialize_process(v8::Platform* platform) { + cppgc::InitializeProcess(platform->GetPageAllocator()); +} + +void cppgc__shutdown_process() { + cppgc::ShutdownProcess(); +} + +v8::CppHeap* cppgc__heap__create(v8::Platform* platform) { + std::unique_ptr heap = v8::CppHeap::Create(platform, v8::CppHeapCreateParams { + {}, + v8::WrapperDescriptor(InternalFields::kEmbedderType, InternalFields::kSlot, kDefaultCppGCEmebdderID), + }); + return heap.release(); +} + +void cppgc__heap__enable_detached_garbage_collections_for_testing(v8::CppHeap* heap) { + heap->EnableDetachedGarbageCollectionsForTesting(); +} + +void cppgc__heap__force_garbage_collection_slow(v8::CppHeap* heap, cppgc::EmbedderStackState stack_state) { + heap->CollectGarbageForTesting(stack_state); +} + +RustObj* cppgc__make_garbage_collectable(v8::CppHeap* heap, void* obj, RustTraceFn trace, RustDestroyFn destroy) { + return cppgc::MakeGarbageCollected(heap->GetAllocationHandle(), obj, trace, destroy); +} + +} // extern "C" \ No newline at end of file diff --git a/src/cppgc.rs b/src/cppgc.rs new file mode 100644 index 0000000000..8f06706399 --- /dev/null +++ b/src/cppgc.rs @@ -0,0 +1,110 @@ +// Copyright 2019-2021 the Deno authors. All rights reserved. MIT license + +use crate::platform::Platform; +use crate::support::Opaque; +use crate::support::SharedRef; +use crate::support::UniqueRef; + +extern "C" { + fn cppgc__initialize_process(platform: *mut Platform); + fn cppgc__shutdown_process(); + + fn cppgc__heap__create(platform: *mut Platform) -> *mut Heap; + fn cppgc__make_garbage_collectable( + heap: *mut Heap, + obj: *mut (), + trace: TraceFn, + destroy: DestroyFn, + ) -> *mut (); + + fn cppgc__heap__enable_detached_garbage_collections_for_testing( + heap: *mut Heap, + ); + fn cppgc__heap__force_garbage_collection_slow( + heap: *mut Heap, + stack_state: EmbedderStackState, + ); +} + +pub fn initalize_process(platform: SharedRef) { + unsafe { + cppgc__initialize_process(&*platform as *const Platform as *mut _); + } +} + +pub unsafe fn shutdown_process() { + cppgc__shutdown_process(); +} + +#[repr(C)] +#[derive(Debug)] +pub struct RustObj(Opaque); + +#[repr(C)] +#[derive(Debug)] +pub struct Visitor(Opaque); + +#[repr(C)] +pub enum EmbedderStackState { + /// Stack may contain interesting heap pointers. + MayContainHeapPointers, + /// Stack does not contain any interesting heap pointers. + NoHeapPointers, +} + +type TraceFn = extern "C" fn(*mut Visitor, *mut ()); +type DestroyFn = extern "C" fn(*mut ()); + +#[repr(C)] +#[derive(Debug)] +pub struct Heap(Opaque); + +impl Drop for Heap { + fn drop(&mut self) {} +} + +impl Heap { + pub fn create(platform: SharedRef) -> UniqueRef { + unsafe { + UniqueRef::from_raw(cppgc__heap__create( + &*platform as *const Platform as *mut _, + )) + } + } + + pub fn force_garbage_collection_slow(&self, stack_state: EmbedderStackState) { + unsafe { + cppgc__heap__force_garbage_collection_slow( + self as *const Heap as *mut _, + stack_state, + ); + } + } + + pub fn enable_detached_garbage_collections_for_testing(&self) { + unsafe { + cppgc__heap__enable_detached_garbage_collections_for_testing( + self as *const Heap as *mut _, + ); + } + } +} + +pub fn make_garbage_collected( + heap: &Heap, + obj: Box, + trace: TraceFn, +) -> *mut T { + extern "C" fn destroy(obj: *mut ()) { + let _ = unsafe { Box::from_raw(obj as *mut T) }; + } + + unsafe { + cppgc__make_garbage_collectable( + heap as *const Heap as *mut _, + Box::into_raw(obj) as _, + trace, + destroy::, + ) as *mut T + } +} diff --git a/src/lib.rs b/src/lib.rs index a6c2a69c45..e17b398b3b 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -31,6 +31,7 @@ mod array_buffer; mod array_buffer_view; mod bigint; mod context; +pub mod cppgc; mod data; mod date; mod exception; From c649fff62a10cdd71fa770bdf20fe02bf97a2ee4 Mon Sep 17 00:00:00 2001 From: Divy Srivastava Date: Thu, 28 Sep 2023 18:50:51 +0530 Subject: [PATCH 02/17] trait obj --- examples/cppgc.rs | 19 +++++++++++-------- src/binding.cc | 2 ++ src/cppgc.rs | 22 ++++++++++++++++++---- 3 files changed, 31 insertions(+), 12 deletions(-) diff --git a/examples/cppgc.rs b/examples/cppgc.rs index e55238a99f..87d235fe2b 100644 --- a/examples/cppgc.rs +++ b/examples/cppgc.rs @@ -2,6 +2,12 @@ struct Resource { name: String, } +impl v8::cppgc::GarbageCollected for Resource { + fn trace(&self, _visitor: *mut v8::cppgc::Visitor) { + println!("Trace {}", self.name); + } +} + impl Drop for Resource { fn drop(&mut self) { println!("Dropping {}", self.name); @@ -17,10 +23,12 @@ fn main() { { let heap = v8::cppgc::Heap::create(platform); - let obj = make_object(&*heap, "hello"); + // No trace. Sweep. + make_object(&*heap, "hello"); + // Trace and sweep. + let _obj = make_object(&*heap, "hello 2"); heap.enable_detached_garbage_collections_for_testing(); - heap.force_garbage_collection_slow( v8::cppgc::EmbedderStackState::MayContainHeapPointers, ); @@ -32,14 +40,9 @@ fn main() { } fn make_object(heap: &v8::cppgc::Heap, name: &str) -> *mut Resource { - extern "C" fn trace(visitor: *mut v8::cppgc::Visitor, obj: *mut ()) { - let obj = unsafe { &*(obj as *const Resource) }; - println!("Trace {}", obj.name); - } - let val = Box::new(Resource { name: name.to_string(), }); - let obj = v8::cppgc::make_garbage_collected(heap, val, trace); + let obj = v8::cppgc::make_garbage_collected(heap, val); return obj; } diff --git a/src/binding.cc b/src/binding.cc index ab5cecf953..7eb9a4a734 100644 --- a/src/binding.cc +++ b/src/binding.cc @@ -3620,6 +3620,8 @@ v8::CppHeap* cppgc__heap__create(v8::Platform* platform) { return heap.release(); } +void cppgc__heap__DELETE(v8::CppHeap* self) { delete self; } + void cppgc__heap__enable_detached_garbage_collections_for_testing(v8::CppHeap* heap) { heap->EnableDetachedGarbageCollectionsForTesting(); } diff --git a/src/cppgc.rs b/src/cppgc.rs index 8f06706399..19e351b827 100644 --- a/src/cppgc.rs +++ b/src/cppgc.rs @@ -10,6 +10,7 @@ extern "C" { fn cppgc__shutdown_process(); fn cppgc__heap__create(platform: *mut Platform) -> *mut Heap; + fn cppgc__heap__DELETE(heap: *mut Heap); fn cppgc__make_garbage_collectable( heap: *mut Heap, obj: *mut (), @@ -60,7 +61,9 @@ type DestroyFn = extern "C" fn(*mut ()); pub struct Heap(Opaque); impl Drop for Heap { - fn drop(&mut self) {} + fn drop(&mut self) { + unsafe { cppgc__heap__DELETE(self as *mut Heap) } + } } impl Heap { @@ -90,20 +93,31 @@ impl Heap { } } -pub fn make_garbage_collected( +pub trait GarbageCollected { + fn trace(&self, visitor: *mut Visitor) {} +} + +pub fn make_garbage_collected( heap: &Heap, obj: Box, - trace: TraceFn, ) -> *mut T { extern "C" fn destroy(obj: *mut ()) { let _ = unsafe { Box::from_raw(obj as *mut T) }; } + extern "C" fn trace( + visitor: *mut Visitor, + obj: *mut (), + ) { + let obj = unsafe { &*(obj as *const T) }; + obj.trace(visitor); + } + unsafe { cppgc__make_garbage_collectable( heap as *const Heap as *mut _, Box::into_raw(obj) as _, - trace, + trace::, destroy::, ) as *mut T } From d5e35b66ad046a628a5e8614a834d04c93c6e8c7 Mon Sep 17 00:00:00 2001 From: Divy Srivastava Date: Thu, 28 Sep 2023 19:03:42 +0530 Subject: [PATCH 03/17] cppgc::Member --- examples/cppgc.rs | 7 +++++-- src/cppgc.rs | 26 +++++++++++++++++++++----- 2 files changed, 26 insertions(+), 7 deletions(-) diff --git a/examples/cppgc.rs b/examples/cppgc.rs index 87d235fe2b..b9812ee1c5 100644 --- a/examples/cppgc.rs +++ b/examples/cppgc.rs @@ -1,3 +1,4 @@ +#[derive(Debug)] struct Resource { name: String, } @@ -18,7 +19,6 @@ fn main() { let platform = v8::new_default_platform(0, false).make_shared(); v8::V8::initialize_platform(platform.clone()); v8::V8::initialize(); - v8::cppgc::initalize_process(platform.clone()); { let heap = v8::cppgc::Heap::create(platform); @@ -39,7 +39,10 @@ fn main() { unsafe { v8::cppgc::shutdown_process() }; } -fn make_object(heap: &v8::cppgc::Heap, name: &str) -> *mut Resource { +fn make_object( + heap: &v8::cppgc::Heap, + name: &str, +) -> v8::cppgc::Member { let val = Box::new(Resource { name: name.to_string(), }); diff --git a/src/cppgc.rs b/src/cppgc.rs index 19e351b827..0425444699 100644 --- a/src/cppgc.rs +++ b/src/cppgc.rs @@ -97,10 +97,23 @@ pub trait GarbageCollected { fn trace(&self, visitor: *mut Visitor) {} } +pub struct Member { + handle: *mut (), + ptr: *mut T, +} + +impl std::ops::Deref for Member { + type Target = T; + + fn deref(&self) -> &Self::Target { + unsafe { &*self.ptr } + } +} + pub fn make_garbage_collected( heap: &Heap, obj: Box, -) -> *mut T { +) -> Member { extern "C" fn destroy(obj: *mut ()) { let _ = unsafe { Box::from_raw(obj as *mut T) }; } @@ -113,12 +126,15 @@ pub fn make_garbage_collected( obj.trace(visitor); } - unsafe { + let ptr = Box::into_raw(obj); + let handle = unsafe { cppgc__make_garbage_collectable( heap as *const Heap as *mut _, - Box::into_raw(obj) as _, + ptr as _, trace::, destroy::, - ) as *mut T - } + ) + }; + + Member { handle, ptr } } From c8261ec7e9dad91a1474e7d3d45f4fa59c11bef5 Mon Sep 17 00:00:00 2001 From: Divy Srivastava Date: Thu, 28 Sep 2023 22:01:56 +0530 Subject: [PATCH 04/17] Improve API --- examples/cppgc.rs | 2 +- src/binding.cc | 4 ++++ src/cppgc.rs | 20 ++++++++++++-------- 3 files changed, 17 insertions(+), 9 deletions(-) diff --git a/examples/cppgc.rs b/examples/cppgc.rs index b9812ee1c5..d8613e4df3 100644 --- a/examples/cppgc.rs +++ b/examples/cppgc.rs @@ -4,7 +4,7 @@ struct Resource { } impl v8::cppgc::GarbageCollected for Resource { - fn trace(&self, _visitor: *mut v8::cppgc::Visitor) { + fn trace(&self, _visitor: &v8::cppgc::Visitor) { println!("Trace {}", self.name); } } diff --git a/src/binding.cc b/src/binding.cc index 7eb9a4a734..80f8d79e84 100644 --- a/src/binding.cc +++ b/src/binding.cc @@ -3634,4 +3634,8 @@ RustObj* cppgc__make_garbage_collectable(v8::CppHeap* heap, void* obj, RustTrace return cppgc::MakeGarbageCollected(heap->GetAllocationHandle(), obj, trace, destroy); } +void cppgc__visitor__trace(cppgc::Visitor* visitor, RustObj* member) { + visitor->Trace(*member); +} + } // extern "C" \ No newline at end of file diff --git a/src/cppgc.rs b/src/cppgc.rs index 0425444699..983f544d9d 100644 --- a/src/cppgc.rs +++ b/src/cppgc.rs @@ -25,6 +25,8 @@ extern "C" { heap: *mut Heap, stack_state: EmbedderStackState, ); + + fn cppgc__visitor__trace(visitor: *const Visitor, member: *const ()); } pub fn initalize_process(platform: SharedRef) { @@ -37,14 +39,16 @@ pub unsafe fn shutdown_process() { cppgc__shutdown_process(); } -#[repr(C)] -#[derive(Debug)] -pub struct RustObj(Opaque); - #[repr(C)] #[derive(Debug)] pub struct Visitor(Opaque); +impl Visitor { + pub fn trace(&self, member: &Member) { + unsafe { cppgc__visitor__trace(self, member.handle) } + } +} + #[repr(C)] pub enum EmbedderStackState { /// Stack may contain interesting heap pointers. @@ -94,15 +98,15 @@ impl Heap { } pub trait GarbageCollected { - fn trace(&self, visitor: *mut Visitor) {} + fn trace(&self, _visitor: &Visitor) {} } -pub struct Member { +pub struct Member { handle: *mut (), ptr: *mut T, } -impl std::ops::Deref for Member { +impl std::ops::Deref for Member { type Target = T; fn deref(&self) -> &Self::Target { @@ -123,7 +127,7 @@ pub fn make_garbage_collected( obj: *mut (), ) { let obj = unsafe { &*(obj as *const T) }; - obj.trace(visitor); + obj.trace(unsafe { &*visitor }); } let ptr = Box::into_raw(obj); From 9923d28632727e4087de2b82ce7781b8382a58c9 Mon Sep 17 00:00:00 2001 From: Divy Srivastava Date: Thu, 28 Sep 2023 22:42:04 +0530 Subject: [PATCH 05/17] Rope String example --- examples/cppgc.rs | 77 +++++++++++++++++++++++++++++++++-------------- 1 file changed, 54 insertions(+), 23 deletions(-) diff --git a/examples/cppgc.rs b/examples/cppgc.rs index d8613e4df3..75abb6835c 100644 --- a/examples/cppgc.rs +++ b/examples/cppgc.rs @@ -1,17 +1,42 @@ -#[derive(Debug)] -struct Resource { - name: String, +// Copyright 2019-2021 the Deno authors. All rights reserved. MIT license. +// +// This sample program shows how to set up a stand-alone cppgc heap. +use std::ops::Deref; + +// Simple string rope to illustrate allocation and garbage collection below. +// The rope keeps the next parts alive via regular managed reference. +struct Rope { + part: String, + next: Option>, +} + +impl std::fmt::Display for Rope { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "{}", self.part)?; + if let Some(next) = &self.next { + write!(f, "{}", next.deref())?; + } + Ok(()) + } } -impl v8::cppgc::GarbageCollected for Resource { - fn trace(&self, _visitor: &v8::cppgc::Visitor) { - println!("Trace {}", self.name); +impl Rope { + pub fn new(part: String, next: Option>) -> Box { + Box::new(Self { part, next }) } } -impl Drop for Resource { +impl v8::cppgc::GarbageCollected for Rope { + fn trace(&self, visitor: &v8::cppgc::Visitor) { + if let Some(member) = &self.next { + visitor.trace(&member); + } + } +} + +impl Drop for Rope { fn drop(&mut self) { - println!("Dropping {}", self.name); + println!("Dropping {}", self.part); } } @@ -20,14 +45,25 @@ fn main() { v8::V8::initialize_platform(platform.clone()); v8::V8::initialize(); v8::cppgc::initalize_process(platform.clone()); + { + // Create a managed heap. let heap = v8::cppgc::Heap::create(platform); - // No trace. Sweep. - make_object(&*heap, "hello"); - // Trace and sweep. - let _obj = make_object(&*heap, "hello 2"); + // Allocate a string rope on the managed heap. + let rope = v8::cppgc::make_garbage_collected( + &heap, + Rope::new( + String::from("Hello "), + Some(v8::cppgc::make_garbage_collected( + &heap, + Rope::new(String::from("World!"), None), + )), + ), + ); + println!("{}", &*rope); + // Manually trigger garbage collection. heap.enable_detached_garbage_collections_for_testing(); heap.force_garbage_collection_slow( v8::cppgc::EmbedderStackState::MayContainHeapPointers, @@ -36,16 +72,11 @@ fn main() { v8::cppgc::EmbedderStackState::NoHeapPointers, ); } - unsafe { v8::cppgc::shutdown_process() }; -} -fn make_object( - heap: &v8::cppgc::Heap, - name: &str, -) -> v8::cppgc::Member { - let val = Box::new(Resource { - name: name.to_string(), - }); - let obj = v8::cppgc::make_garbage_collected(heap, val); - return obj; + // Gracefully shutdown the process. + unsafe { + v8::cppgc::shutdown_process(); + v8::V8::dispose(); + } + v8::V8::dispose_platform(); } From 593cdaff7d1163e41e453ac86e5f3aaca75fb7d0 Mon Sep 17 00:00:00 2001 From: Divy Srivastava Date: Thu, 28 Sep 2023 22:57:54 +0530 Subject: [PATCH 06/17] Some docs --- examples/cppgc.rs | 4 ++-- src/binding.cc | 2 +- src/cppgc.rs | 36 +++++++++++++++++++++++++++++++++--- 3 files changed, 36 insertions(+), 6 deletions(-) diff --git a/examples/cppgc.rs b/examples/cppgc.rs index 75abb6835c..c4eb55a929 100644 --- a/examples/cppgc.rs +++ b/examples/cppgc.rs @@ -65,10 +65,10 @@ fn main() { println!("{}", &*rope); // Manually trigger garbage collection. heap.enable_detached_garbage_collections_for_testing(); - heap.force_garbage_collection_slow( + heap.collect_garbage_for_testing( v8::cppgc::EmbedderStackState::MayContainHeapPointers, ); - heap.force_garbage_collection_slow( + heap.collect_garbage_for_testing( v8::cppgc::EmbedderStackState::NoHeapPointers, ); } diff --git a/src/binding.cc b/src/binding.cc index 80f8d79e84..5642bea854 100644 --- a/src/binding.cc +++ b/src/binding.cc @@ -3626,7 +3626,7 @@ void cppgc__heap__enable_detached_garbage_collections_for_testing(v8::CppHeap* h heap->EnableDetachedGarbageCollectionsForTesting(); } -void cppgc__heap__force_garbage_collection_slow(v8::CppHeap* heap, cppgc::EmbedderStackState stack_state) { +void cppgc__heap__collect_garbage_for_testing(v8::CppHeap* heap, cppgc::EmbedderStackState stack_state) { heap->CollectGarbageForTesting(stack_state); } diff --git a/src/cppgc.rs b/src/cppgc.rs index 983f544d9d..72616e03d1 100644 --- a/src/cppgc.rs +++ b/src/cppgc.rs @@ -21,7 +21,7 @@ extern "C" { fn cppgc__heap__enable_detached_garbage_collections_for_testing( heap: *mut Heap, ); - fn cppgc__heap__force_garbage_collection_slow( + fn cppgc__heap__collect_garbage_for_testing( heap: *mut Heap, stack_state: EmbedderStackState, ); @@ -29,16 +29,37 @@ extern "C" { fn cppgc__visitor__trace(visitor: *const Visitor, member: *const ()); } +/// Process-global initialization of the garbage collector. Must be called before +/// creating a Heap. +/// +/// Can be called multiple times when paired with `ShutdownProcess()`. pub fn initalize_process(platform: SharedRef) { unsafe { cppgc__initialize_process(&*platform as *const Platform as *mut _); } } +/// # SAFETY +/// +/// Must be called after destroying the last used heap. Some process-global +/// metadata may not be returned and reused upon a subsequent +/// `initalize_process()` call. pub unsafe fn shutdown_process() { cppgc__shutdown_process(); } +/// Visitor passed to trace methods. All managed pointers must have called the +/// Visitor's trace method on them. +/// +/// ```no_run +/// struct Foo { foo: Member } +/// +/// impl GarbageCollected for Foo { +/// fn trace(&self, visitor: &Visitor) { +/// visitor.trace(&self.foo); +/// } +/// } +/// ``` #[repr(C)] #[derive(Debug)] pub struct Visitor(Opaque); @@ -60,6 +81,10 @@ pub enum EmbedderStackState { type TraceFn = extern "C" fn(*mut Visitor, *mut ()); type DestroyFn = extern "C" fn(*mut ()); +/// A heap for allocating managed C++ objects. +/// +/// Similar to v8::Isolate, the heap may only be accessed from one thread at a +/// time. #[repr(C)] #[derive(Debug)] pub struct Heap(Opaque); @@ -79,9 +104,9 @@ impl Heap { } } - pub fn force_garbage_collection_slow(&self, stack_state: EmbedderStackState) { + pub fn collect_garbage_for_testing(&self, stack_state: EmbedderStackState) { unsafe { - cppgc__heap__force_garbage_collection_slow( + cppgc__heap__collect_garbage_for_testing( self as *const Heap as *mut _, stack_state, ); @@ -97,10 +122,14 @@ impl Heap { } } +/// Base trait for managed objects. pub trait GarbageCollected { fn trace(&self, _visitor: &Visitor) {} } +/// Members are used to contain strong pointers to other garbage +/// collected objects. All members fields on garbage collected objects +/// must be trace in the `trace` method. pub struct Member { handle: *mut (), ptr: *mut T, @@ -114,6 +143,7 @@ impl std::ops::Deref for Member { } } +/// Constructs an instance of T, which is a garbage collected type. pub fn make_garbage_collected( heap: &Heap, obj: Box, From 1004ee601ea46294f2d779448833f3359d827702 Mon Sep 17 00:00:00 2001 From: Divy Srivastava Date: Thu, 28 Sep 2023 23:04:33 +0530 Subject: [PATCH 07/17] Fix lint --- examples/cppgc.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/cppgc.rs b/examples/cppgc.rs index c4eb55a929..b322b20839 100644 --- a/examples/cppgc.rs +++ b/examples/cppgc.rs @@ -29,7 +29,7 @@ impl Rope { impl v8::cppgc::GarbageCollected for Rope { fn trace(&self, visitor: &v8::cppgc::Visitor) { if let Some(member) = &self.next { - visitor.trace(&member); + visitor.trace(member); } } } From 8ed6ea2a7196946a78d9abc7faf99a88dbf6b90c Mon Sep 17 00:00:00 2001 From: Divy Srivastava Date: Fri, 29 Sep 2023 09:04:05 +0530 Subject: [PATCH 08/17] HeapCreateParams --- examples/cppgc.rs | 11 +++++- src/binding.cc | 9 ++--- src/cppgc.rs | 91 +++++++++++++++++++++++++++++++++++++++++++++-- 3 files changed, 102 insertions(+), 9 deletions(-) diff --git a/examples/cppgc.rs b/examples/cppgc.rs index b322b20839..8538e07c14 100644 --- a/examples/cppgc.rs +++ b/examples/cppgc.rs @@ -40,6 +40,8 @@ impl Drop for Rope { } } +const DEFAULT_CPP_GC_EMBEDDER_ID: u16 = 0x90de; + fn main() { let platform = v8::new_default_platform(0, false).make_shared(); v8::V8::initialize_platform(platform.clone()); @@ -48,7 +50,14 @@ fn main() { { // Create a managed heap. - let heap = v8::cppgc::Heap::create(platform); + let heap = v8::cppgc::Heap::create( + platform, + v8::cppgc::HeapCreateParams::new(v8::cppgc::WrapperDescriptor::new( + 0, + 1, + DEFAULT_CPP_GC_EMBEDDER_ID, + )), + ); // Allocate a string rope on the managed heap. let rope = v8::cppgc::make_garbage_collected( diff --git a/src/binding.cc b/src/binding.cc index 5642bea854..aab7b4c60b 100644 --- a/src/binding.cc +++ b/src/binding.cc @@ -3600,10 +3600,6 @@ class RustObj final: public cppgc::GarbageCollected { void* obj_; }; -enum InternalFields { kEmbedderType, kSlot, kInternalFieldCount }; - -constexpr uint16_t kDefaultCppGCEmebdderID = 0x90de; - void cppgc__initialize_process(v8::Platform* platform) { cppgc::InitializeProcess(platform->GetPageAllocator()); } @@ -3612,10 +3608,11 @@ void cppgc__shutdown_process() { cppgc::ShutdownProcess(); } -v8::CppHeap* cppgc__heap__create(v8::Platform* platform) { +v8::CppHeap* cppgc__heap__create(v8::Platform* platform, int wrappable_type_index, + int wrappable_instance_index, uint16_t embedder_id) { std::unique_ptr heap = v8::CppHeap::Create(platform, v8::CppHeapCreateParams { {}, - v8::WrapperDescriptor(InternalFields::kEmbedderType, InternalFields::kSlot, kDefaultCppGCEmebdderID), + v8::WrapperDescriptor(wrappable_type_index, wrappable_instance_index, embedder_id), }); return heap.release(); } diff --git a/src/cppgc.rs b/src/cppgc.rs index 72616e03d1..8f12a35348 100644 --- a/src/cppgc.rs +++ b/src/cppgc.rs @@ -1,6 +1,7 @@ // Copyright 2019-2021 the Deno authors. All rights reserved. MIT license use crate::platform::Platform; +use crate::support::int; use crate::support::Opaque; use crate::support::SharedRef; use crate::support::UniqueRef; @@ -9,7 +10,12 @@ extern "C" { fn cppgc__initialize_process(platform: *mut Platform); fn cppgc__shutdown_process(); - fn cppgc__heap__create(platform: *mut Platform) -> *mut Heap; + fn cppgc__heap__create( + platform: *mut Platform, + wrappable_type_index: int, + wrappable_instance_index: int, + embedder_id_for_garbage_collected: u16, + ) -> *mut Heap; fn cppgc__heap__DELETE(heap: *mut Heap); fn cppgc__make_garbage_collectable( heap: *mut Heap, @@ -78,6 +84,75 @@ pub enum EmbedderStackState { NoHeapPointers, } +/// Specifies supported marking types. +#[repr(u8)] +pub enum MarkingType { + /// Atomic stop-the-world marking. This option does not require any write barriers but is the most intrusive in terms of jank. + Atomic, + /// Incremental marking interleaves marking with the rest of the application workload on the same thread. + Incremental, + /// Incremental and concurrent marking. + IncrementalAndConcurrent, +} + +/// Specifies supported sweeping types. +#[repr(u8)] +pub enum SweepingType { + /// Atomic stop-the-world sweeping. All of sweeping is performed at once. + Atomic, + /// Incremental sweeping interleaves sweeping with the rest of the application workload on the same thread. + Incremental, + /// Incremental and concurrent sweeping. Sweeping is split and interleaved with the rest of the application. + IncrementalAndConcurrent, +} + +pub type InternalFieldIndex = int; + +/// Describes how V8 wrapper objects maintain references to garbage-collected C++ objects. +pub struct WrapperDescriptor { + /// Index of the wrappable type. + pub wrappable_type_index: InternalFieldIndex, + /// Index of the wrappable instance. + pub wrappable_instance_index: InternalFieldIndex, + /// Embedder id identifying instances of garbage-collected objects. It is expected that + /// the first field of the wrappable type is a uint16_t holding the id. Only references + /// to instances of wrappables types with an id of embedder_id_for_garbage_collected will + /// be considered by Heap. + pub embedder_id_for_garbage_collected: u16, +} + +impl WrapperDescriptor { + pub fn new( + wrappable_type_index: InternalFieldIndex, + wrappable_instance_index: InternalFieldIndex, + embedder_id_for_garbage_collected: u16, + ) -> Self { + Self { + wrappable_type_index, + wrappable_instance_index, + embedder_id_for_garbage_collected, + } + } +} + +pub struct HeapCreateParams { + wrapper_descriptor: WrapperDescriptor, + /// Specifies which kind of marking are supported by the heap. + pub marking_support: MarkingType, + /// Specifies which kind of sweeping are supported by the heap. + pub sweeping_support: SweepingType, +} + +impl HeapCreateParams { + pub fn new(wrapper_descriptor: WrapperDescriptor) -> Self { + Self { + wrapper_descriptor, + marking_support: MarkingType::IncrementalAndConcurrent, + sweeping_support: SweepingType::IncrementalAndConcurrent, + } + } +} + type TraceFn = extern "C" fn(*mut Visitor, *mut ()); type DestroyFn = extern "C" fn(*mut ()); @@ -96,10 +171,22 @@ impl Drop for Heap { } impl Heap { - pub fn create(platform: SharedRef) -> UniqueRef { + pub fn create( + platform: SharedRef, + params: HeapCreateParams, + ) -> UniqueRef { + let WrapperDescriptor { + wrappable_type_index, + wrappable_instance_index, + embedder_id_for_garbage_collected, + } = params.wrapper_descriptor; + unsafe { UniqueRef::from_raw(cppgc__heap__create( &*platform as *const Platform as *mut _, + wrappable_type_index, + wrappable_instance_index, + embedder_id_for_garbage_collected, )) } } From 648e42ef8c5c0555e2f28ee19047975ca9528161 Mon Sep 17 00:00:00 2001 From: Divy Srivastava Date: Fri, 29 Sep 2023 09:10:22 +0530 Subject: [PATCH 09/17] `0xde90` --- examples/cppgc.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/cppgc.rs b/examples/cppgc.rs index 8538e07c14..3c629023ff 100644 --- a/examples/cppgc.rs +++ b/examples/cppgc.rs @@ -40,7 +40,7 @@ impl Drop for Rope { } } -const DEFAULT_CPP_GC_EMBEDDER_ID: u16 = 0x90de; +const DEFAULT_CPP_GC_EMBEDDER_ID: u16 = 0xde90; fn main() { let platform = v8::new_default_platform(0, false).make_shared(); From fda93a4de492ab36405d3454e5c5025f3c9da36f Mon Sep 17 00:00:00 2001 From: Divy Srivastava Date: Fri, 29 Sep 2023 09:25:16 +0530 Subject: [PATCH 10/17] AttachCppHeap --- src/binding.cc | 4 ++++ src/isolate.rs | 19 +++++++++++++++++++ 2 files changed, 23 insertions(+) diff --git a/src/binding.cc b/src/binding.cc index aab7b4c60b..a1c080c751 100644 --- a/src/binding.cc +++ b/src/binding.cc @@ -3617,6 +3617,10 @@ v8::CppHeap* cppgc__heap__create(v8::Platform* platform, int wrappable_type_inde return heap.release(); } +void v8__Isolate__AttachCppHeap(v8::Isolate* isolate, v8::CppHeap* cpp_heap) { + isolate->AttachCppHeap(cpp_heap); +} + void cppgc__heap__DELETE(v8::CppHeap* self) { delete self; } void cppgc__heap__enable_detached_garbage_collections_for_testing(v8::CppHeap* heap) { diff --git a/src/isolate.rs b/src/isolate.rs index ade12644b8..019ae5d6f7 100644 --- a/src/isolate.rs +++ b/src/isolate.rs @@ -1,5 +1,6 @@ // Copyright 2019-2021 the Deno authors. All rights reserved. MIT license. use crate::function::FunctionCallbackInfo; +use crate::cppgc::Heap; use crate::gc::GCCallbackFlags; use crate::gc::GCType; use crate::handle::FinalizerCallback; @@ -420,6 +421,10 @@ extern "C" { isolate: *mut Isolate, change_in_bytes: i64, ) -> i64; + fn v8__Isolate__AttachCppHeap( + isolate: *mut Isolate, + heap: *mut Heap, + ); fn v8__Isolate__SetPrepareStackTraceCallback( isolate: *mut Isolate, callback: PrepareStackTraceCallback, @@ -1084,6 +1089,20 @@ impl Isolate { } } + /// Attaches a managed C++ heap as an extension to the JavaScript heap. + /// + /// The embedder maintains ownership of the CppHeap. At most one C++ heap + /// can be attached to V8. + #[inline(always)] + pub fn attach_cpp_heap( + &mut self, + heap: &Heap, + ) { + unsafe { + v8__Isolate__AttachCppHeap(self, heap as *const Heap as *mut _); + } + } + #[inline(always)] pub fn set_oom_error_handler(&mut self, callback: OomErrorCallback) { unsafe { v8__Isolate__SetOOMErrorHandler(self, callback) }; From 5d135fdfbaaa69ae821fdfb256e5b5f50815bc7b Mon Sep 17 00:00:00 2001 From: Divy Srivastava Date: Fri, 29 Sep 2023 10:21:28 +0530 Subject: [PATCH 11/17] Add cppgc object example --- examples/cppgc-object.rs | 127 +++++++++++++++++++++++++++++++++++++++ src/binding.cc | 4 ++ src/cppgc.rs | 2 +- src/isolate.rs | 19 +++--- 4 files changed, 141 insertions(+), 11 deletions(-) create mode 100644 examples/cppgc-object.rs diff --git a/examples/cppgc-object.rs b/examples/cppgc-object.rs new file mode 100644 index 0000000000..d40bf7f08c --- /dev/null +++ b/examples/cppgc-object.rs @@ -0,0 +1,127 @@ +// Copyright 2019-2021 the Deno authors. All rights reserved. MIT license. +use std::cell::Cell; + +struct Wrappable { + id: String, + trace_count: Cell, +} + +impl v8::cppgc::GarbageCollected for Wrappable { + fn trace(&self, _visitor: &v8::cppgc::Visitor) { + println!("Wrappable::trace() {}", self.id); + self.trace_count.set(self.trace_count.get() + 1); + } +} + +impl Drop for Wrappable { + fn drop(&mut self) { + println!("Wrappable::drop() {}", self.id); + } +} + +const DEFAULT_CPP_GC_EMBEDDER_ID: u16 = 0xde90; + +fn main() { + let platform = v8::new_default_platform(0, false).make_shared(); + v8::V8::set_flags_from_string("--no_freeze_flags_after_init --expose-gc"); + v8::V8::initialize_platform(platform.clone()); + v8::V8::initialize(); + + v8::cppgc::initalize_process(platform.clone()); + + { + let isolate = &mut v8::Isolate::new(v8::CreateParams::default()); + + // Create a managed heap. + let heap = v8::cppgc::Heap::create( + platform, + v8::cppgc::HeapCreateParams::new(v8::cppgc::WrapperDescriptor::new( + 0, + 1, + DEFAULT_CPP_GC_EMBEDDER_ID, + )), + ); + + isolate.attach_cpp_heap(&heap); + + let handle_scope = &mut v8::HandleScope::new(isolate); + let context = v8::Context::new(handle_scope); + let scope = &mut v8::ContextScope::new(handle_scope, context); + let global = context.global(scope); + { + let func = v8::Function::new( + scope, + |scope: &mut v8::HandleScope, + args: v8::FunctionCallbackArguments, + mut rv: v8::ReturnValue| { + let id = args.get(0).to_rust_string_lossy(scope); + + let templ = v8::ObjectTemplate::new(scope); + templ.set_internal_field_count(2); + + let obj = templ.new_instance(scope).unwrap(); + + let member = v8::cppgc::make_garbage_collected( + scope.get_cpp_heap(), + Box::new(Wrappable { + trace_count: Cell::new(0), + id, + }), + ); + + obj.set_aligned_pointer_in_internal_field( + 0, + &DEFAULT_CPP_GC_EMBEDDER_ID as *const u16 as _, + ); + obj.set_aligned_pointer_in_internal_field(1, member.handle as _); + + rv.set(obj.into()); + }, + ) + .unwrap(); + let name = v8::String::new(scope, "make_wrap").unwrap(); + global.set(scope, name.into(), func.into()).unwrap(); + } + + let source = v8::String::new( + scope, + r#" + make_wrap('gc me pls'); // Inaccessible after scope. + globalThis.wrap = make_wrap('dont gc me'); // Accessible after scope. + "#, + ) + .unwrap(); + execute_script(scope, source); + + scope + .request_garbage_collection_for_testing(v8::GarbageCollectionType::Full); + } + + // Gracefully shutdown the process. + unsafe { + v8::cppgc::shutdown_process(); + v8::V8::dispose(); + } + v8::V8::dispose_platform(); +} + +fn execute_script( + context_scope: &mut v8::ContextScope, + script: v8::Local, +) { + let scope = &mut v8::HandleScope::new(context_scope); + let try_catch = &mut v8::TryCatch::new(scope); + + let script = v8::Script::compile(try_catch, script, None) + .expect("failed to compile script"); + + if script.run(try_catch).is_none() { + let exception_string = try_catch + .stack_trace() + .or_else(|| try_catch.exception()) + .map(|value| value.to_rust_string_lossy(try_catch)) + .unwrap_or_else(|| "no stack trace".into()); + + panic!("{}", exception_string); + } +} diff --git a/src/binding.cc b/src/binding.cc index a1c080c751..8ab6a82546 100644 --- a/src/binding.cc +++ b/src/binding.cc @@ -3621,6 +3621,10 @@ void v8__Isolate__AttachCppHeap(v8::Isolate* isolate, v8::CppHeap* cpp_heap) { isolate->AttachCppHeap(cpp_heap); } +v8::CppHeap* v8__Isolate__GetCppHeap(v8::Isolate* isolate) { + return isolate->GetCppHeap(); +} + void cppgc__heap__DELETE(v8::CppHeap* self) { delete self; } void cppgc__heap__enable_detached_garbage_collections_for_testing(v8::CppHeap* heap) { diff --git a/src/cppgc.rs b/src/cppgc.rs index 8f12a35348..83f8d9baeb 100644 --- a/src/cppgc.rs +++ b/src/cppgc.rs @@ -218,7 +218,7 @@ pub trait GarbageCollected { /// collected objects. All members fields on garbage collected objects /// must be trace in the `trace` method. pub struct Member { - handle: *mut (), + pub handle: *mut (), ptr: *mut T, } diff --git a/src/isolate.rs b/src/isolate.rs index 019ae5d6f7..76e481a5be 100644 --- a/src/isolate.rs +++ b/src/isolate.rs @@ -1,6 +1,6 @@ // Copyright 2019-2021 the Deno authors. All rights reserved. MIT license. -use crate::function::FunctionCallbackInfo; use crate::cppgc::Heap; +use crate::function::FunctionCallbackInfo; use crate::gc::GCCallbackFlags; use crate::gc::GCType; use crate::handle::FinalizerCallback; @@ -421,10 +421,8 @@ extern "C" { isolate: *mut Isolate, change_in_bytes: i64, ) -> i64; - fn v8__Isolate__AttachCppHeap( - isolate: *mut Isolate, - heap: *mut Heap, - ); + fn v8__Isolate__GetCppHeap(isolate: *mut Isolate) -> *mut Heap; + fn v8__Isolate__AttachCppHeap(isolate: *mut Isolate, heap: *mut Heap); fn v8__Isolate__SetPrepareStackTraceCallback( isolate: *mut Isolate, callback: PrepareStackTraceCallback, @@ -1090,19 +1088,20 @@ impl Isolate { } /// Attaches a managed C++ heap as an extension to the JavaScript heap. - /// + /// /// The embedder maintains ownership of the CppHeap. At most one C++ heap /// can be attached to V8. #[inline(always)] - pub fn attach_cpp_heap( - &mut self, - heap: &Heap, - ) { + pub fn attach_cpp_heap(&mut self, heap: &Heap) { unsafe { v8__Isolate__AttachCppHeap(self, heap as *const Heap as *mut _); } } + pub fn get_cpp_heap(&mut self) -> &Heap { + unsafe { &*v8__Isolate__GetCppHeap(self) } + } + #[inline(always)] pub fn set_oom_error_handler(&mut self, callback: OomErrorCallback) { unsafe { v8__Isolate__SetOOMErrorHandler(self, callback) }; From 1e3559698891279dfeb738974079994f0b499c93 Mon Sep 17 00:00:00 2001 From: Divy Srivastava Date: Sat, 30 Sep 2023 09:32:27 +0530 Subject: [PATCH 12/17] Change trace_ arg order --- src/binding.cc | 4 ++-- src/cppgc.rs | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/binding.cc b/src/binding.cc index 8ab6a82546..c6a0a4733f 100644 --- a/src/binding.cc +++ b/src/binding.cc @@ -3579,7 +3579,7 @@ void v8__PropertyDescriptor__set_configurable(v8::PropertyDescriptor* self, extern "C" { -using RustTraceFn = void (*)(cppgc::Visitor*, void* obj); +using RustTraceFn = void (*)(void* obj, cppgc::Visitor*); using RustDestroyFn = void (*)(void* obj); class RustObj final: public cppgc::GarbageCollected { @@ -3591,7 +3591,7 @@ class RustObj final: public cppgc::GarbageCollected { } void Trace(cppgc::Visitor* visitor) const { - trace_(visitor, obj_); + trace_(obj_, visitor); } private: diff --git a/src/cppgc.rs b/src/cppgc.rs index 83f8d9baeb..0dfaff310e 100644 --- a/src/cppgc.rs +++ b/src/cppgc.rs @@ -153,7 +153,7 @@ impl HeapCreateParams { } } -type TraceFn = extern "C" fn(*mut Visitor, *mut ()); +type TraceFn = extern "C" fn(*mut (), *mut Visitor); type DestroyFn = extern "C" fn(*mut ()); /// A heap for allocating managed C++ objects. @@ -240,8 +240,8 @@ pub fn make_garbage_collected( } extern "C" fn trace( - visitor: *mut Visitor, obj: *mut (), + visitor: *mut Visitor, ) { let obj = unsafe { &*(obj as *const T) }; obj.trace(unsafe { &*visitor }); From 62a3bd048ab7650e745a71d062210054ac3e1338 Mon Sep 17 00:00:00 2001 From: Divy Srivastava Date: Sat, 30 Sep 2023 10:48:28 +0530 Subject: [PATCH 13/17] Add `make_garbage_collected_raw` --- src/cppgc.rs | 45 ++++++++++++++++++++++++++++----------------- 1 file changed, 28 insertions(+), 17 deletions(-) diff --git a/src/cppgc.rs b/src/cppgc.rs index 0dfaff310e..a4291304c3 100644 --- a/src/cppgc.rs +++ b/src/cppgc.rs @@ -45,7 +45,7 @@ pub fn initalize_process(platform: SharedRef) { } } -/// # SAFETY +/// # Safety /// /// Must be called after destroying the last used heap. Some process-global /// metadata may not be returned and reused upon a subsequent @@ -153,8 +153,8 @@ impl HeapCreateParams { } } -type TraceFn = extern "C" fn(*mut (), *mut Visitor); -type DestroyFn = extern "C" fn(*mut ()); +type TraceFn = unsafe extern "C" fn(*mut (), *mut Visitor); +type DestroyFn = unsafe extern "C" fn(*mut ()); /// A heap for allocating managed C++ objects. /// @@ -235,11 +235,25 @@ pub fn make_garbage_collected( heap: &Heap, obj: Box, ) -> Member { - extern "C" fn destroy(obj: *mut ()) { - let _ = unsafe { Box::from_raw(obj as *mut T) }; + unsafe extern "C" fn destroy(obj: *mut ()) { + let _ = Box::from_raw(obj as *mut T); } - extern "C" fn trace( + unsafe { make_garbage_collected_raw(heap, Box::into_raw(obj), destroy::) } +} + +/// # Safety +/// +/// `obj` must be a pointer to a valid instance of T allocated on the heap. +/// +/// `drop_fn` must be a function that drops the instance of T. This function +/// will be called when the object is garbage collected. +pub unsafe fn make_garbage_collected_raw( + heap: &Heap, + obj: *mut T, + destroy: DestroyFn, +) -> Member { + unsafe extern "C" fn trace( obj: *mut (), visitor: *mut Visitor, ) { @@ -247,15 +261,12 @@ pub fn make_garbage_collected( obj.trace(unsafe { &*visitor }); } - let ptr = Box::into_raw(obj); - let handle = unsafe { - cppgc__make_garbage_collectable( - heap as *const Heap as *mut _, - ptr as _, - trace::, - destroy::, - ) - }; - - Member { handle, ptr } + let handle = cppgc__make_garbage_collectable( + heap as *const Heap as *mut _, + obj as _, + trace::, + destroy, + ); + + Member { handle, ptr: obj } } From d0fcef5494e08b84b3217ba03757d6eb2d7cc4af Mon Sep 17 00:00:00 2001 From: Divy Srivastava Date: Sat, 30 Sep 2023 10:56:12 +0530 Subject: [PATCH 14/17] Explain a bit of lifetime of T --- src/cppgc.rs | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/cppgc.rs b/src/cppgc.rs index a4291304c3..1907edbd1a 100644 --- a/src/cppgc.rs +++ b/src/cppgc.rs @@ -231,6 +231,12 @@ impl std::ops::Deref for Member { } /// Constructs an instance of T, which is a garbage collected type. +/// +/// The object will be allocated on the heap and managed by cppgc. During +/// marking, the object will be traced by calling the `trace` method on it. +/// +/// During sweeping, the destructor will be called and the memory will be +/// freed using `Box::from_raw`. pub fn make_garbage_collected( heap: &Heap, obj: Box, @@ -244,6 +250,9 @@ pub fn make_garbage_collected( /// # Safety /// +/// By calling this function, you are giving up ownership of `T` to the +/// garbage collector. +/// /// `obj` must be a pointer to a valid instance of T allocated on the heap. /// /// `drop_fn` must be a function that drops the instance of T. This function From c9c8f063240c3ffb03c12f84310ff006e69a7092 Mon Sep 17 00:00:00 2001 From: Divy Srivastava Date: Sat, 30 Sep 2023 11:18:01 +0530 Subject: [PATCH 15/17] Add tests --- src/cppgc.rs | 4 +- tests/test_cppgc.rs | 137 ++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 140 insertions(+), 1 deletion(-) create mode 100644 tests/test_cppgc.rs diff --git a/src/cppgc.rs b/src/cppgc.rs index 1907edbd1a..0f7e597b99 100644 --- a/src/cppgc.rs +++ b/src/cppgc.rs @@ -57,7 +57,9 @@ pub unsafe fn shutdown_process() { /// Visitor passed to trace methods. All managed pointers must have called the /// Visitor's trace method on them. /// -/// ```no_run +/// ``` +/// use v8::cppgc::{Member, Visitor, GarbageCollected}; +/// /// struct Foo { foo: Member } /// /// impl GarbageCollected for Foo { diff --git a/tests/test_cppgc.rs b/tests/test_cppgc.rs new file mode 100644 index 0000000000..fc5feb5a21 --- /dev/null +++ b/tests/test_cppgc.rs @@ -0,0 +1,137 @@ +// Copyright 2019-2021 the Deno authors. All rights reserved. MIT license. +use std::sync::atomic::{AtomicUsize, Ordering}; +use v8::cppgc::{GarbageCollected, Visitor}; + +struct CppGCGuard { + pub platform: v8::SharedRef, +} + +fn initalize_test() -> CppGCGuard { + v8::V8::set_flags_from_string("--no_freeze_flags_after_init --expose-gc"); + let platform = v8::new_unprotected_default_platform(0, false).make_shared(); + v8::V8::initialize_platform(platform.clone()); + v8::V8::initialize(); + v8::cppgc::initalize_process(platform.clone()); + + CppGCGuard { platform } +} + +impl Drop for CppGCGuard { + fn drop(&mut self) { + unsafe { + v8::cppgc::shutdown_process(); + v8::V8::dispose(); + } + v8::V8::dispose_platform(); + } +} + +const DEFAULT_CPP_GC_EMBEDDER_ID: u16 = 0xde90; + +#[test] +fn cppgc_object_wrap() { + let guard = initalize_test(); + + static TRACE_COUNT: AtomicUsize = AtomicUsize::new(0); + static DROP_COUNT: AtomicUsize = AtomicUsize::new(0); + + struct Wrap; + + impl GarbageCollected for Wrap { + fn trace(&self, _: &Visitor) { + TRACE_COUNT.fetch_add(1, Ordering::SeqCst); + } + } + + impl Drop for Wrap { + fn drop(&mut self) { + DROP_COUNT.fetch_add(1, Ordering::SeqCst); + } + } + + fn op_make_wrap( + scope: &mut v8::HandleScope, + _: v8::FunctionCallbackArguments, + mut rv: v8::ReturnValue, + ) { + let templ = v8::ObjectTemplate::new(scope); + templ.set_internal_field_count(2); + + let obj = templ.new_instance(scope).unwrap(); + + let member = + v8::cppgc::make_garbage_collected(scope.get_cpp_heap(), Box::new(Wrap)); + + obj.set_aligned_pointer_in_internal_field( + 0, + &DEFAULT_CPP_GC_EMBEDDER_ID as *const u16 as _, + ); + obj.set_aligned_pointer_in_internal_field(1, member.handle as _); + + rv.set(obj.into()); + } + + { + let isolate = &mut v8::Isolate::new(Default::default()); + // Create a managed heap. + let heap = v8::cppgc::Heap::create( + guard.platform.clone(), + v8::cppgc::HeapCreateParams::new(v8::cppgc::WrapperDescriptor::new( + 0, + 1, + DEFAULT_CPP_GC_EMBEDDER_ID, + )), + ); + + isolate.attach_cpp_heap(&heap); + + let handle_scope = &mut v8::HandleScope::new(isolate); + let context = v8::Context::new(handle_scope); + let scope = &mut v8::ContextScope::new(handle_scope, context); + let global = context.global(scope); + { + let func = v8::Function::new(scope, op_make_wrap).unwrap(); + let name = v8::String::new(scope, "make_wrap").unwrap(); + global.set(scope, name.into(), func.into()).unwrap(); + } + + let source = v8::String::new( + scope, + r#" + make_wrap(); // Inaccessible after scope. + globalThis.wrap = make_wrap(); // Accessible after scope. + "#, + ) + .unwrap(); + execute_script(scope, source); + + assert_eq!(DROP_COUNT.load(Ordering::SeqCst), 0); + + scope + .request_garbage_collection_for_testing(v8::GarbageCollectionType::Full); + + assert!(TRACE_COUNT.load(Ordering::SeqCst) > 0); + assert_eq!(DROP_COUNT.load(Ordering::SeqCst), 1); + } +} + +fn execute_script( + context_scope: &mut v8::ContextScope, + script: v8::Local, +) { + let scope = &mut v8::HandleScope::new(context_scope); + let try_catch = &mut v8::TryCatch::new(scope); + + let script = v8::Script::compile(try_catch, script, None) + .expect("failed to compile script"); + + if script.run(try_catch).is_none() { + let exception_string = try_catch + .stack_trace() + .or_else(|| try_catch.exception()) + .map(|value| value.to_rust_string_lossy(try_catch)) + .unwrap_or_else(|| "no stack trace".into()); + + panic!("{}", exception_string); + } +} From 4477b0003cf1404bc17e97b50bec19fe8d581090 Mon Sep 17 00:00:00 2001 From: Divy Srivastava Date: Sat, 30 Sep 2023 11:53:46 +0530 Subject: [PATCH 16/17] Remove deref on Member --- examples/cppgc.rs | 2 +- src/cppgc.rs | 11 +++++++++++ 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/examples/cppgc.rs b/examples/cppgc.rs index 3c629023ff..abcf5b6eac 100644 --- a/examples/cppgc.rs +++ b/examples/cppgc.rs @@ -71,7 +71,7 @@ fn main() { ), ); - println!("{}", &*rope); + println!("{}", unsafe { rope.get() }); // Manually trigger garbage collection. heap.enable_detached_garbage_collections_for_testing(); heap.collect_garbage_for_testing( diff --git a/src/cppgc.rs b/src/cppgc.rs index 0f7e597b99..f2fce22b93 100644 --- a/src/cppgc.rs +++ b/src/cppgc.rs @@ -224,6 +224,17 @@ pub struct Member { ptr: *mut T, } +impl Member { + /// Returns a raw pointer to the object. + /// + /// # Safety + /// + /// There are no guarantees that the object is alive and not garbage collected. + pub unsafe fn get(&self) -> &T { + unsafe { &*self.ptr } + } +} + impl std::ops::Deref for Member { type Target = T; From 1ad60ea056b848d878ef781e468307bdf9c8f4fd Mon Sep 17 00:00:00 2001 From: Divy Srivastava Date: Sat, 30 Sep 2023 15:54:30 +0530 Subject: [PATCH 17/17] Add comment --- examples/cppgc-object.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/examples/cppgc-object.rs b/examples/cppgc-object.rs index d40bf7f08c..577a917603 100644 --- a/examples/cppgc-object.rs +++ b/examples/cppgc-object.rs @@ -19,6 +19,8 @@ impl Drop for Wrappable { } } +// Set a custom embedder ID for the garbage collector. cppgc will use this ID to +// identify the object that it manages. const DEFAULT_CPP_GC_EMBEDDER_ID: u16 = 0xde90; fn main() {