Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add cppgc bindings #1336

Merged
merged 17 commits into from
Oct 30, 2023
129 changes: 129 additions & 0 deletions examples/cppgc-object.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,129 @@
// Copyright 2019-2021 the Deno authors. All rights reserved. MIT license.
use std::cell::Cell;

struct Wrappable {
id: String,
trace_count: Cell<u16>,
}

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);
}
}

// 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() {
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<v8::HandleScope>,
script: v8::Local<v8::String>,
) {
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);
}
}
91 changes: 91 additions & 0 deletions examples/cppgc.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
// 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<v8::cppgc::Member<Rope>>,
}

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 Rope {
pub fn new(part: String, next: Option<v8::cppgc::Member<Rope>>) -> Box<Rope> {
Box::new(Self { part, next })
}
}

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.part);
}
}

const DEFAULT_CPP_GC_EMBEDDER_ID: u16 = 0xde90;

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());

{
// 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,
)),
);

// 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!("{}", unsafe { rope.get() });
// Manually trigger garbage collection.
heap.enable_detached_garbage_collections_for_testing();
heap.collect_garbage_for_testing(
v8::cppgc::EmbedderStackState::MayContainHeapPointers,
);
heap.collect_garbage_for_testing(
v8::cppgc::EmbedderStackState::NoHeapPointers,
);
}

// Gracefully shutdown the process.
unsafe {
v8::cppgc::shutdown_process();
v8::V8::dispose();
}
v8::V8::dispose_platform();
}
73 changes: 73 additions & 0 deletions src/binding.cc
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand All @@ -29,6 +30,8 @@
#include "v8/src/objects/objects.h"
#include "v8/src/objects/smi.h"

#include "cppgc/platform.h"

using namespace support;

template <typename T>
Expand Down Expand Up @@ -3571,3 +3574,73 @@ void v8__PropertyDescriptor__set_configurable(v8::PropertyDescriptor* self,
}

} // extern "C"

// cppgc

extern "C" {

using RustTraceFn = void (*)(void* obj, cppgc::Visitor*);
using RustDestroyFn = void (*)(void* obj);

class RustObj final: public cppgc::GarbageCollected<RustObj> {
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_(obj_, visitor);
}

private:
RustTraceFn trace_;
RustDestroyFn destroy_;
void* obj_;
};

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, int wrappable_type_index,
int wrappable_instance_index, uint16_t embedder_id) {
std::unique_ptr<v8::CppHeap> heap = v8::CppHeap::Create(platform, v8::CppHeapCreateParams {
{},
v8::WrapperDescriptor(wrappable_type_index, wrappable_instance_index, embedder_id),
});
return heap.release();
}

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) {
heap->EnableDetachedGarbageCollectionsForTesting();
}

void cppgc__heap__collect_garbage_for_testing(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<RustObj>(heap->GetAllocationHandle(), obj, trace, destroy);
}

void cppgc__visitor__trace(cppgc::Visitor* visitor, RustObj* member) {
visitor->Trace(*member);
}

} // extern "C"
Loading