fix: remove use of deprecated apis (#1488)

This commit is contained in:
snek 2024-06-27 08:29:46 -07:00 committed by GitHub
parent 0b440db772
commit e747f405a4
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
16 changed files with 842 additions and 300 deletions

7
Cargo.lock generated
View File

@ -1020,6 +1020,12 @@ dependencies = [
"winapi",
]
[[package]]
name = "paste"
version = "1.0.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a"
[[package]]
name = "percent-encoding"
version = "2.3.1"
@ -1457,6 +1463,7 @@ dependencies = [
"home",
"miniz_oxide",
"once_cell",
"paste",
"rustversion",
"trybuild",
"which 6.0.1",

View File

@ -89,6 +89,7 @@ use_custom_libcxx = []
[dependencies]
bitflags = "2.5"
once_cell = "1.19"
paste = "1.0"
[build-dependencies]
miniz_oxide = "0.7.2"

View File

@ -19,9 +19,7 @@ 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;
const TAG: u16 = 1;
fn main() {
let platform = v8::new_default_platform(0, false).make_shared();
@ -32,18 +30,10 @@ fn main() {
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 heap =
v8::cppgc::Heap::create(platform, v8::cppgc::HeapCreateParams::default());
let isolate =
&mut v8::Isolate::new(v8::CreateParams::default().cpp_heap(heap));
let handle_scope = &mut v8::HandleScope::new(isolate);
let context = v8::Context::new(handle_scope);
@ -57,24 +47,31 @@ fn main() {
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);
fn empty(
_scope: &mut v8::HandleScope,
_args: v8::FunctionCallbackArguments,
_rv: v8::ReturnValue,
) {
}
let templ = v8::FunctionTemplate::new(scope, empty);
let func = templ.get_function(scope).unwrap();
let obj = func.new_instance(scope, &[]).unwrap();
let obj = templ.new_instance(scope).unwrap();
assert!(obj.is_api_wrapper());
let member = v8::cppgc::make_garbage_collected(
scope.get_cpp_heap().unwrap(),
Box::new(Wrappable {
trace_count: Cell::new(0),
id,
}),
);
let member = unsafe {
v8::cppgc::make_garbage_collected(
scope.get_cpp_heap().unwrap(),
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 _);
unsafe {
v8::Object::wrap::<TAG, Wrappable>(scope, obj, &member);
}
rv.set(obj.into());
},

View File

@ -1,47 +1,43 @@
// 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>>,
next: 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())?;
if let Some(next) = self.next.borrow() {
write!(f, "{}", next)?;
}
Ok(())
}
}
impl Rope {
pub fn new(part: String, next: Option<v8::cppgc::Member<Rope>>) -> Box<Rope> {
Box::new(Self { part, next })
pub fn new(part: String, next: v8::cppgc::Member<Rope>) -> Rope {
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);
}
visitor.trace(&self.next);
}
}
impl Drop for Rope {
fn drop(&mut self) {
println!("Dropping {}", self.part);
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());
@ -50,36 +46,46 @@ fn main() {
{
// 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,
)),
);
let heap =
v8::cppgc::Heap::create(platform, v8::cppgc::HeapCreateParams::default());
// 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),
)),
),
);
let rope = unsafe {
v8::cppgc::make_garbage_collected(
&heap,
Rope::new(
String::from("Hello "),
v8::cppgc::make_garbage_collected(
&heap,
Rope::new(String::from("World!"), v8::cppgc::Member::empty()),
),
),
)
};
println!("{}", rope.borrow().unwrap());
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,
);
println!("Collect: MayContainHeapPointers");
unsafe {
heap.collect_garbage_for_testing(
v8::cppgc::EmbedderStackState::MayContainHeapPointers,
);
}
// Should still be live here:
println!("{}", rope.borrow().unwrap());
println!("Collect: NoHeapPointers");
unsafe {
heap.collect_garbage_for_testing(
v8::cppgc::EmbedderStackState::NoHeapPointers,
);
}
// Should be dead now.
}
// Gracefully shutdown the process.

View File

@ -11,6 +11,7 @@
#include "support.h"
#include "unicode/locid.h"
#include "v8-callbacks.h"
#include "v8/include/cppgc/persistent.h"
#include "v8/include/libplatform/libplatform.h"
#include "v8/include/v8-cppgc.h"
#include "v8/include/v8-fast-api-calls.h"
@ -31,8 +32,6 @@
#include "v8/src/objects/objects.h"
#include "v8/src/objects/smi.h"
#pragma clang diagnostic ignored "-Wdeprecated-declarations"
using namespace support;
template <typename T>
@ -405,6 +404,21 @@ void v8__Global__Reset(const v8::Data* data) {
global.Reset();
}
void v8__TracedReference__CONSTRUCT(
uninit_t<v8::TracedReference<v8::Data>>* buf) {
construct_in_place<v8::TracedReference<v8::Data>>(buf);
}
void v8__TracedReference__Reset(v8::TracedReference<v8::Data>* self,
v8::Isolate* isolate, const v8::Data* other) {
self->Reset(isolate, ptr_to_local(other));
}
const v8::Data* v8__TracedReference__Get(v8::TracedReference<v8::Data>* self,
v8::Isolate* isolate) {
return local_to_ptr(self->Get(isolate));
}
v8::Isolate* v8__WeakCallbackInfo__GetIsolate(
const v8::WeakCallbackInfo<void>* self) {
return self->GetIsolate();
@ -1313,8 +1327,12 @@ void v8__Object__SetAlignedPointerInInternalField(const v8::Object& self,
ptr_to_local(&self)->SetAlignedPointerInInternalField(index, value);
}
bool v8__Object__IsApiWrapper(const v8::Object& self) {
return ptr_to_local(&self)->IsApiWrapper();
}
const v8::Value* v8__Object__GetPrototype(const v8::Object& self) {
return local_to_ptr(ptr_to_local(&self)->GetPrototype());
return local_to_ptr(ptr_to_local(&self)->GetPrototypeV2());
}
MaybeBool v8__Object__Set(const v8::Object& self, const v8::Context& context,
@ -1333,7 +1351,7 @@ MaybeBool v8__Object__SetIndex(const v8::Object& self,
MaybeBool v8__Object__SetPrototype(const v8::Object& self,
const v8::Context& context,
const v8::Value& prototype) {
return maybe_to_maybe_bool(ptr_to_local(&self)->SetPrototype(
return maybe_to_maybe_bool(ptr_to_local(&self)->SetPrototypeV2(
ptr_to_local(&context), ptr_to_local(&prototype)));
}
@ -3729,24 +3747,38 @@ void v8__PropertyDescriptor__set_configurable(v8::PropertyDescriptor* self,
extern "C" {
using RustTraceFn = void (*)(void* obj, cppgc::Visitor*);
using RustDestroyFn = void (*)(void* obj);
class RustObj;
using RustTraceFn = void (*)(const RustObj* obj, cppgc::Visitor*);
using RustDestroyFn = void (*)(const RustObj* obj);
class RustObj final : public cppgc::GarbageCollected<RustObj> {
public:
explicit RustObj(void* obj, RustTraceFn trace, RustDestroyFn destroy)
: trace_(trace), destroy_(destroy), obj_(obj) {}
explicit RustObj(RustTraceFn trace, RustDestroyFn destroy)
: trace_(trace), destroy_(destroy) {}
~RustObj() { destroy_(obj_); }
~RustObj() { destroy_(this); }
void Trace(cppgc::Visitor* visitor) const { trace_(obj_, visitor); }
void Trace(cppgc::Visitor* visitor) const { trace_(this, visitor); }
private:
RustTraceFn trace_;
RustDestroyFn destroy_;
void* obj_;
};
RustObj* v8__Object__Unwrap(v8::Isolate* isolate, const v8::Object& wrapper,
v8::CppHeapPointerTag tag) {
v8::CppHeapPointerTagRange tag_range(tag, tag);
return static_cast<RustObj*>(
v8::Object::Unwrap(isolate, ptr_to_local(&wrapper), tag_range));
}
void v8__Object__Wrap(v8::Isolate* isolate, const v8::Object& wrapper,
RustObj* value, v8::CppHeapPointerTag tag) {
v8::Object::Wrap(isolate, ptr_to_local(&wrapper), static_cast<void*>(value),
tag);
}
void cppgc__initialize_process(v8::Platform* platform) {
cppgc::InitializeProcess(platform->GetPageAllocator());
}
@ -3754,31 +3786,15 @@ void cppgc__initialize_process(v8::Platform* platform) {
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),
});
cppgc::Heap::MarkingType marking_support,
cppgc::Heap::SweepingType sweeping_support) {
v8::CppHeapCreateParams params{{}};
params.marking_support = marking_support;
params.sweeping_support = sweeping_support;
std::unique_ptr<v8::CppHeap> heap = v8::CppHeap::Create(platform, params);
return heap.release();
}
void v8__Isolate__AttachCppHeap(v8::Isolate* isolate, v8::CppHeap* cpp_heap) {
// The AttachCppHeap method is deprecated but the alternative of passing
// heap to the Isolate CreateParams is broken.
//
// TODO(@littledivy): Remove this when the above CL is merged.
// https://chromium-review.googlesource.com/c/chromium/src/+/4992764
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wdeprecated-declarations"
isolate->AttachCppHeap(cpp_heap);
#pragma clang diagnostic pop
}
v8::CppHeap* v8__Isolate__GetCppHeap(v8::Isolate* isolate) {
return isolate->GetCppHeap();
}
@ -3795,15 +3811,95 @@ void cppgc__heap__collect_garbage_for_testing(
heap->CollectGarbageForTesting(stack_state);
}
RustObj* cppgc__make_garbage_collectable(v8::CppHeap* heap, void* obj,
RustObj* cppgc__make_garbage_collectable(v8::CppHeap* heap, size_t size,
RustTraceFn trace,
RustDestroyFn destroy) {
return cppgc::MakeGarbageCollected<RustObj>(heap->GetAllocationHandle(), obj,
return cppgc::MakeGarbageCollected<RustObj>(heap->GetAllocationHandle(),
cppgc::AdditionalBytes(size),
trace, destroy);
}
void cppgc__visitor__trace(cppgc::Visitor* visitor, RustObj* member) {
void cppgc__Visitor__Trace__Member(cppgc::Visitor* visitor,
cppgc::Member<RustObj>* member) {
visitor->Trace(*member);
}
void cppgc__Visitor__Trace__WeakMember(cppgc::Visitor* visitor,
cppgc::WeakMember<RustObj>* member) {
visitor->Trace(*member);
}
void cppgc__Visitor__Trace__TracedReference(
cppgc::Visitor* visitor, v8::TracedReference<v8::Data>* ref) {
visitor->Trace(*ref);
}
void cppgc__Member__CONSTRUCT(uninit_t<cppgc::Member<RustObj>>* buf,
RustObj* other) {
construct_in_place<cppgc::Member<RustObj>>(buf, other);
}
void cppgc__Member__DESTRUCT(cppgc::Member<RustObj>* self) {
self->~BasicMember();
}
RustObj* cppgc__Member__Get(cppgc::Member<RustObj>* member) {
return member->Get();
}
void cppgc__Member__Assign(cppgc::Member<RustObj>* member, RustObj* other) {
member->operator=(other);
}
void cppgc__WeakMember__CONSTRUCT(uninit_t<cppgc::WeakMember<RustObj>>* buf,
RustObj* other) {
construct_in_place<cppgc::WeakMember<RustObj>>(buf, other);
}
void cppgc__WeakMember__DESTRUCT(cppgc::WeakMember<RustObj>* self) {
self->~BasicMember();
}
RustObj* cppgc__WeakMember__Get(cppgc::WeakMember<RustObj>* member) {
return member->Get();
}
void cppgc__WeakMember__Assign(cppgc::WeakMember<RustObj>* member,
RustObj* other) {
member->operator=(other);
}
cppgc::Persistent<RustObj>* cppgc__Persistent__CONSTRUCT() {
return new cppgc::Persistent<RustObj>(nullptr);
}
void cppgc__Persistent__DESTRUCT(cppgc::Persistent<RustObj>* self) {
delete self;
}
void cppgc__Persistent__Assign(cppgc::Persistent<RustObj>* self, RustObj* ptr) {
self->operator=(ptr);
}
RustObj* cppgc__Persistent__Get(cppgc::Persistent<RustObj>* self) {
return self->Get();
}
cppgc::WeakPersistent<RustObj>* cppgc__WeakPersistent__CONSTRUCT() {
return new cppgc::WeakPersistent<RustObj>(nullptr);
}
void cppgc__WeakPersistent__DESTRUCT(cppgc::WeakPersistent<RustObj>* self) {
delete self;
}
void cppgc__WeakPersistent__Assign(cppgc::WeakPersistent<RustObj>* self,
RustObj* ptr) {
self->operator=(ptr);
}
RustObj* cppgc__WeakPersistent__Get(cppgc::WeakPersistent<RustObj>* self) {
return self->Get();
}
} // extern "C"

View File

@ -1,3 +1,4 @@
#include <v8-cppgc.h>
#include <v8-message.h>
/**
@ -5,8 +6,16 @@
* and made available in `crate::binding` in rust.
*/
// TODO: In the immediate term, cppgc definitions will go here.
// In the future we should migrate over the rest of our SIZE definitions,
// and eventually entire structs and functions.
namespace {
class RustObj;
}
static size_t RUST_v8__ScriptOrigin_SIZE = sizeof(v8::ScriptOrigin);
static size_t RUST_cppgc__Member_SIZE = sizeof(cppgc::Member<RustObj>);
static size_t RUST_cppgc__WeakMember_SIZE = sizeof(cppgc::WeakMember<RustObj>);
static size_t RUST_v8__TracedReference_SIZE =
sizeof(v8::TracedReference<v8::Data>);

View File

@ -5,6 +5,9 @@ use crate::support::int;
use crate::support::Opaque;
use crate::support::SharedRef;
use crate::support::UniqueRef;
use crate::Data;
use crate::TracedReference;
use std::marker::PhantomData;
extern "C" {
fn cppgc__initialize_process(platform: *mut Platform);
@ -12,17 +15,16 @@ extern "C" {
fn cppgc__heap__create(
platform: *mut Platform,
wrappable_type_index: int,
wrappable_instance_index: int,
embedder_id_for_garbage_collected: u16,
marking_support: MarkingType,
sweeping_support: SweepingType,
) -> *mut Heap;
fn cppgc__heap__DELETE(heap: *mut Heap);
fn cppgc__make_garbage_collectable(
heap: *mut Heap,
obj: *mut (),
size: usize,
trace: TraceFn,
destroy: DestroyFn,
) -> *mut InnerMember;
) -> *mut RustObj;
fn cppgc__heap__enable_detached_garbage_collections_for_testing(
heap: *mut Heap,
@ -32,7 +34,75 @@ extern "C" {
stack_state: EmbedderStackState,
);
fn cppgc__visitor__trace(visitor: *const Visitor, member: *const InnerMember);
fn cppgc__Visitor__Trace__Member(
visitor: *const Visitor,
member: *const MemberInner,
);
fn cppgc__Visitor__Trace__WeakMember(
visitor: *const Visitor,
member: *const WeakMemberInner,
);
fn cppgc__Visitor__Trace__TracedReference(
visitor: *const Visitor,
reference: *const TracedReference<Data>,
);
fn cppgc__Member__CONSTRUCT(member: *mut MemberInner, obj: *mut RustObj);
fn cppgc__Member__DESTRUCT(member: *mut MemberInner);
fn cppgc__Member__Get(member: *const MemberInner) -> *mut RustObj;
fn cppgc__Member__Assign(member: *mut MemberInner, other: *mut RustObj);
fn cppgc__WeakMember__CONSTRUCT(
member: *mut WeakMemberInner,
obj: *mut RustObj,
);
fn cppgc__WeakMember__DESTRUCT(member: *mut WeakMemberInner);
fn cppgc__WeakMember__Get(member: *const WeakMemberInner) -> *mut RustObj;
fn cppgc__WeakMember__Assign(
member: *mut WeakMemberInner,
other: *mut RustObj,
);
fn cppgc__Persistent__CONSTRUCT() -> *mut PersistentInner;
fn cppgc__Persistent__DESTRUCT(this: *mut PersistentInner);
fn cppgc__Persistent__Assign(this: *mut PersistentInner, ptr: *mut RustObj);
fn cppgc__Persistent__Get(this: *const PersistentInner) -> *mut RustObj;
fn cppgc__WeakPersistent__CONSTRUCT() -> *mut WeakPersistentInner;
fn cppgc__WeakPersistent__DESTRUCT(this: *mut WeakPersistentInner);
fn cppgc__WeakPersistent__Assign(
this: *mut WeakPersistentInner,
ptr: *mut RustObj,
);
fn cppgc__WeakPersistent__Get(
this: *const WeakPersistentInner,
) -> *mut RustObj;
}
type TraceFn = unsafe extern "C" fn(*const RustObj, *mut Visitor);
type DestroyFn = unsafe extern "C" fn(*const RustObj);
#[doc(hidden)]
#[repr(C)]
pub struct RustObj {
trace: TraceFn,
destroy: DestroyFn,
}
fn object_offset_for_rust_obj<T: GarbageCollected>() -> usize {
#[repr(C)]
struct Calc<T> {
header: RustObj,
data: T,
}
std::mem::offset_of!(Calc<T>, data)
}
fn get_object_from_rust_obj<T: GarbageCollected>(
rust_obj: *const RustObj,
) -> *mut T {
unsafe { rust_obj.byte_add(object_offset_for_rust_obj::<T>()) as *mut T }
}
/// Process-global initialization of the garbage collector. Must be called before
@ -73,8 +143,37 @@ pub unsafe fn shutdown_process() {
pub struct Visitor(Opaque);
impl Visitor {
pub fn trace<T: GarbageCollected>(&self, member: &Member<T>) {
unsafe { cppgc__visitor__trace(self, member.handle) }
#[inline(always)]
pub fn trace(&self, member: &impl Traced) {
member.trace(self);
}
}
#[doc(hidden)]
pub trait Traced {
fn trace(&self, visitor: &Visitor);
}
impl<T: GarbageCollected> Traced for Member<T> {
fn trace(&self, visitor: &Visitor) {
unsafe { cppgc__Visitor__Trace__Member(visitor, &self.inner) }
}
}
impl<T: GarbageCollected> Traced for WeakMember<T> {
fn trace(&self, visitor: &Visitor) {
unsafe { cppgc__Visitor__Trace__WeakMember(visitor, &self.inner) }
}
}
impl<T> Traced for TracedReference<T> {
fn trace(&self, visitor: &Visitor) {
unsafe {
cppgc__Visitor__Trace__TracedReference(
visitor,
self as *const TracedReference<T> as *const TracedReference<Data>,
)
}
}
}
@ -110,54 +209,22 @@ pub enum SweepingType {
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 {
impl Default for HeapCreateParams {
fn default() -> Self {
Self {
wrapper_descriptor,
marking_support: MarkingType::IncrementalAndConcurrent,
sweeping_support: SweepingType::IncrementalAndConcurrent,
}
}
}
type TraceFn = unsafe extern "C" fn(*mut (), *mut Visitor);
type DestroyFn = unsafe 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
@ -168,7 +235,7 @@ pub struct Heap(Opaque);
impl Drop for Heap {
fn drop(&mut self) {
unsafe { cppgc__heap__DELETE(self as *mut Heap) }
unsafe { cppgc__heap__DELETE(self) }
}
}
@ -177,23 +244,19 @@ impl Heap {
platform: SharedRef<Platform>,
params: HeapCreateParams,
) -> UniqueRef<Heap> {
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,
params.marking_support,
params.sweeping_support,
))
}
}
pub fn collect_garbage_for_testing(&self, stack_state: EmbedderStackState) {
pub unsafe fn collect_garbage_for_testing(
&self,
stack_state: EmbedderStackState,
) {
unsafe {
cppgc__heap__collect_garbage_for_testing(
self as *const Heap as *mut _,
@ -216,99 +279,275 @@ pub trait GarbageCollected {
fn trace(&self, _visitor: &Visitor) {}
}
#[repr(C)]
pub struct InnerMember {
inner: [usize; 2],
ptr: *mut (),
}
impl InnerMember {
pub unsafe fn get<T: GarbageCollected>(&self) -> &T {
unsafe { self.ptr.cast::<T>().as_ref().unwrap() }
}
pub unsafe fn get_mut<T: GarbageCollected>(&mut self) -> &mut T {
unsafe { self.ptr.cast::<T>().as_mut().unwrap() }
}
}
/// 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.
#[repr(transparent)]
pub struct Member<T: GarbageCollected> {
pub handle: *mut InnerMember,
_phantom: std::marker::PhantomData<T>,
}
impl<T: GarbageCollected> Member<T> {
/// 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.handle).get() }
}
}
impl<T: GarbageCollected> std::ops::Deref for Member<T> {
type Target = T;
fn deref(&self) -> &Self::Target {
unsafe { self.get() }
}
}
/// 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<T: GarbageCollected>(
heap: &Heap,
obj: Box<T>,
) -> Member<T> {
unsafe extern "C" fn destroy<T>(obj: *mut ()) {
let _ = Box::from_raw(obj as *mut T);
}
unsafe { make_garbage_collected_raw(heap, Box::into_raw(obj), destroy::<T>) }
}
/// freed.
///
/// # 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
/// will be called when the object is garbage collected.
pub unsafe fn make_garbage_collected_raw<T: GarbageCollected>(
/// The caller must ensure that the returned pointer is always stored on
/// the stack, or moved into one of the Persistent types.
pub unsafe fn make_garbage_collected<T: GarbageCollected>(
heap: &Heap,
obj: *mut T,
destroy: DestroyFn,
obj: T,
) -> Member<T> {
unsafe extern "C" fn trace<T: GarbageCollected>(
obj: *mut (),
obj: *const RustObj,
visitor: *mut Visitor,
) {
let obj = unsafe { &*(obj as *const T) };
let obj = unsafe { &*get_object_from_rust_obj::<T>(obj) };
obj.trace(unsafe { &*visitor });
}
let handle = cppgc__make_garbage_collectable(
heap as *const Heap as *mut _,
obj as _,
trace::<T>,
destroy,
);
unsafe extern "C" fn destroy<T: GarbageCollected>(obj: *const RustObj) {
let obj = get_object_from_rust_obj::<T>(obj);
std::ptr::drop_in_place(obj);
}
Member {
handle,
_phantom: std::marker::PhantomData,
let additional_bytes = (object_offset_for_rust_obj::<T>()
- std::mem::size_of::<RustObj>())
+ std::mem::size_of::<T>();
let handle = unsafe {
cppgc__make_garbage_collectable(
heap as *const Heap as *mut _,
additional_bytes,
trace::<T>,
destroy::<T>,
)
};
unsafe {
get_object_from_rust_obj::<T>(handle).write(obj);
}
Member::new(handle)
}
#[doc(hidden)]
pub trait GetRustObj<T: GarbageCollected> {
fn get_rust_obj(&self) -> *mut RustObj;
}
macro_rules! member {
($( # $attr:tt )* $name:ident) => {
paste::paste! {
#[repr(transparent)]
struct [< $name Inner >]([u8; crate::binding:: [< RUST_cppgc__ $name _SIZE >]]);
impl [< $name Inner >] {
fn new(ptr: *mut RustObj) -> Self {
let mut this = std::mem::MaybeUninit::uninit();
unsafe {
[< cppgc__ $name __CONSTRUCT >](this.as_mut_ptr(), ptr);
this.assume_init()
}
}
#[inline(always)]
fn get(&self) -> *mut RustObj {
// Member may be a compressed pointer, so just read it from C++
unsafe { [< cppgc__ $name __Get >](self) }
}
#[inline(always)]
fn assign(&mut self, ptr: *mut RustObj) {
// Assignment has write barriers in the GC, so call into C++
unsafe {
[< cppgc__ $name __Assign >](self, ptr);
}
}
}
impl Drop for [< $name Inner >] {
fn drop(&mut self) {
unsafe {
[< cppgc__ $name __DESTRUCT >](self);
}
}
}
$( # $attr )*
#[repr(transparent)]
pub struct $name<T: GarbageCollected> {
inner: [< $name Inner >],
_phantom: PhantomData<T>,
}
impl<T: GarbageCollected> $name<T> {
pub(crate) fn new(obj: *mut RustObj) -> Self {
Self {
inner: [< $name Inner >]::new(obj),
_phantom: PhantomData,
}
}
#[doc = "Create a new empty "]
#[doc = stringify!($name)]
#[doc = " which may be set later."]
pub fn empty() -> Self {
Self::new(std::ptr::null_mut())
}
#[doc = "Set the object pointed to by this "]
#[doc = stringify!($name)]
#[doc = "."]
pub fn set(&mut self, other: &impl GetRustObj<T>) {
let ptr = other.get_rust_obj();
self.inner.assign(ptr);
}
#[doc = "Borrow the object pointed to by this "]
#[doc = stringify!($name)]
#[doc = "."]
pub fn borrow(&self) -> Option<&T> {
let ptr = self.inner.get();
if ptr.is_null() {
None
} else {
// SAFETY: Either this is a strong reference and the pointer is always valid
// or this is a weak reference and the ptr will be null if it was collected.
Some(unsafe { &*get_object_from_rust_obj(ptr) })
}
}
}
impl<T: GarbageCollected> GetRustObj<T> for $name<T> {
fn get_rust_obj(&self) -> *mut RustObj {
self.inner.get()
}
}
impl<T: GarbageCollected> std::fmt::Debug for $name<T> {
fn fmt(&self, fmt: &mut std::fmt::Formatter) -> std::fmt::Result {
fmt.debug_struct(stringify!($name)).finish()
}
}
}
}
}
member! {
/// Members are used in classes to contain strong pointers to other garbage
/// collected objects. All Member fields of a class must be traced in the class'
/// trace method.
Member
}
member! {
/// WeakMember is similar to Member in that it is used to point to other garbage
/// collected objects. However instead of creating a strong pointer to the
/// object, the WeakMember creates a weak pointer, which does not keep the
/// pointee alive. Hence if all pointers to to a heap allocated object are weak
/// the object will be garbage collected. At the time of GC the weak pointers
/// will automatically be set to null.
WeakMember
}
macro_rules! persistent {
($( # $attr:tt )* $name:ident) => {
paste::paste! {
// PersistentBase is extremely particular about move and copy semantics,
// so we allocate it on the heap and only interact with it via calls to C++.
#[repr(C)]
struct [< $name Inner >](Opaque);
$( # $attr )*
pub struct $name<T: GarbageCollected> {
inner: *mut [< $name Inner >],
_phantom: PhantomData<T>,
}
impl<T: GarbageCollected> $name<T> {
#[doc = "Create a new empty "]
#[doc = stringify!($name)]
#[doc = " which may be set later."]
pub fn empty() -> Self {
let this = unsafe { [< cppgc__ $name __CONSTRUCT >]() };
Self {
inner: this,
_phantom: PhantomData,
}
}
#[doc = "Set the object pointed to by this "]
#[doc = stringify!($name)]
#[doc = "."]
pub fn set(&mut self, other: &impl GetRustObj<T>) {
let ptr = other.get_rust_obj();
self.assign(ptr);
}
#[doc = "Borrow the object pointed to by this "]
#[doc = stringify!($name)]
#[doc = "."]
pub fn borrow(&self) -> Option<&T> {
let ptr = self.get();
if ptr.is_null() {
None
} else {
// SAFETY: Either this is a strong reference and the pointer is always valid
// or this is a weak reference and the ptr will be null if it was collected.
Some(unsafe { &*get_object_from_rust_obj(ptr) })
}
}
#[inline(always)]
fn assign(&mut self, ptr: *mut RustObj) {
unsafe {
[< cppgc__ $name __Assign >](self.inner, ptr);
}
}
#[inline(always)]
fn get(&self) -> *mut RustObj {
unsafe {
[< cppgc__ $name __Get >](self.inner)
}
}
}
impl<T: GarbageCollected> Drop for $name<T> {
fn drop(&mut self) {
unsafe {
[< cppgc__ $name __DESTRUCT >](self.inner);
}
}
}
impl<T: GarbageCollected> std::fmt::Debug for $name<T> {
fn fmt(&self, fmt: &mut std::fmt::Formatter) -> std::fmt::Result {
fmt.debug_struct(stringify!($name)).finish()
}
}
impl<T: GarbageCollected> GetRustObj<T> for $name<T> {
fn get_rust_obj(&self) -> *mut RustObj {
self.get()
}
}
}
};
}
persistent! {
/// Persistent is a way to create a strong pointer from an off-heap object to
/// another on-heap object. As long as the Persistent handle is alive the GC will
/// keep the object pointed to alive. The Persistent handle is always a GC root
/// from the point of view of the GC. Persistent must be constructed and
/// destructed in the same thread.
Persistent
}
persistent! {
/// WeakPersistent is a way to create a weak pointer from an off-heap object to
/// an on-heap object. The pointer is automatically cleared when the pointee gets
/// collected. WeakPersistent must be constructed and destructed in the same
/// thread.
WeakPersistent
}

View File

@ -35,6 +35,17 @@ extern "C" {
this: *const WeakCallbackInfo,
callback: extern "C" fn(*const WeakCallbackInfo),
);
fn v8__TracedReference__CONSTRUCT(this: *mut TracedReference<Data>);
fn v8__TracedReference__Reset(
this: *mut TracedReference<Data>,
isolate: *mut Isolate,
data: *mut Data,
);
fn v8__TracedReference__Get(
this: *const TracedReference<Data>,
isolate: *mut Isolate,
) -> *const Data;
}
/// An object reference managed by the v8 garbage collector.
@ -978,3 +989,61 @@ impl FinalizerMap {
self.map.drain().map(|(_, finalizer)| finalizer)
}
}
/// A traced handle without destructor that clears the handle. The embedder needs
/// to ensure that the handle is not accessed once the V8 object has been
/// reclaimed. For more details see BasicTracedReference.
#[repr(C)]
pub struct TracedReference<T> {
data: [u8; crate::binding::RUST_v8__TracedReference_SIZE],
_phantom: PhantomData<T>,
}
impl<T> TracedReference<T> {
/// An empty TracedReference without storage cell.
pub fn empty() -> Self {
let mut this = std::mem::MaybeUninit::uninit();
unsafe {
v8__TracedReference__CONSTRUCT(this.as_mut_ptr() as _);
this.assume_init()
}
}
/// Construct a TracedReference from a Local.
///
/// A new storage cell is created pointing to the same object.
pub fn new(scope: &mut HandleScope<()>, data: Local<T>) -> Self {
let mut this = Self::empty();
this.reset(scope, Some(data));
this
}
pub fn get<'s>(
&self,
scope: &mut HandleScope<'s, ()>,
) -> Option<Local<'s, T>> {
unsafe {
scope.cast_local(|sd| {
v8__TracedReference__Get(
self as *const Self as *const TracedReference<Data>,
sd.get_isolate_ptr(),
) as *const T
})
}
}
/// Always resets the reference. Creates a new reference from `other` if it is
/// non-empty.
pub fn reset(&mut self, scope: &mut HandleScope<()>, data: Option<Local<T>>) {
unsafe {
v8__TracedReference__Reset(
self as *mut Self as *mut TracedReference<Data>,
scope.get_isolate_ptr(),
data
.map(|h| h.as_non_null().as_ptr())
.unwrap_or(std::ptr::null_mut())
.cast(),
);
}
}
}

View File

@ -460,7 +460,6 @@ extern "C" {
change_in_bytes: i64,
) -> i64;
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,
@ -637,18 +636,21 @@ impl Isolate {
#[allow(clippy::new_ret_no_self)]
pub fn snapshot_creator(
external_references: Option<&'static ExternalReferences>,
params: Option<CreateParams>,
) -> OwnedIsolate {
SnapshotCreator::new(external_references)
SnapshotCreator::new(external_references, params)
}
#[allow(clippy::new_ret_no_self)]
pub fn snapshot_creator_from_existing_snapshot(
existing_snapshot_blob: impl Allocated<[u8]>,
external_references: Option<&'static ExternalReferences>,
params: Option<CreateParams>,
) -> OwnedIsolate {
SnapshotCreator::from_existing_snapshot(
existing_snapshot_blob,
external_references,
params,
)
}
@ -1184,17 +1186,6 @@ 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 _);
}
}
pub fn get_cpp_heap(&mut self) -> Option<&Heap> {
unsafe { v8__Isolate__GetCppHeap(self).as_ref() }
}

View File

@ -8,6 +8,7 @@ use crate::support::Allocated;
use crate::support::Allocation;
use crate::support::Opaque;
use crate::support::SharedPtr;
use crate::support::UniqueRef;
use std::any::Any;
use std::convert::TryFrom;
@ -159,6 +160,13 @@ impl CreateParams {
self
}
/// A CppHeap used to construct the Isolate. V8 takes ownership of the
/// CppHeap passed this way.
pub fn cpp_heap(mut self, heap: UniqueRef<Heap>) -> Self {
self.raw.cpp_heap = heap.into_raw();
self
}
pub(crate) fn finalize(mut self) -> (raw::CreateParams, Box<dyn Any>) {
if self.raw.array_buffer_allocator_shared.is_null() {
self = self.array_buffer_allocator(array_buffer::new_default_allocator());

View File

@ -98,6 +98,7 @@ pub use get_property_names_args_builder::*;
pub use handle::Global;
pub use handle::Handle;
pub use handle::Local;
pub use handle::TracedReference;
pub use handle::Weak;
pub use isolate::GarbageCollectionType;
pub use isolate::HeapStatistics;

View File

@ -1,3 +1,7 @@
use crate::cppgc::GarbageCollected;
use crate::cppgc::GetRustObj;
use crate::cppgc::Member;
use crate::cppgc::RustObj;
use crate::isolate::Isolate;
use crate::support::int;
use crate::support::MapFnTo;
@ -209,6 +213,18 @@ extern "C" {
key: *const Name,
out: *mut Maybe<PropertyAttribute>,
);
fn v8__Object__Wrap(
isolate: *const Isolate,
wrapper: *const Object,
value: *const RustObj,
tag: u16,
);
fn v8__Object__Unwrap(
isolate: *const Isolate,
wrapper: *const Object,
tag: u16,
) -> *mut RustObj;
fn v8__Object__IsApiWrapper(this: *const Object) -> bool;
fn v8__Array__New(isolate: *mut Isolate, length: int) -> *const Array;
fn v8__Array__New_with_elements(
@ -263,6 +279,8 @@ extern "C" {
fn v8__Set__As__Array(this: *const Set) -> *const Array;
}
const LAST_TAG: u16 = 0x7fff;
impl Object {
/// Creates an empty object.
#[inline(always)]
@ -680,6 +698,56 @@ impl Object {
unsafe { v8__Object__SetAlignedPointerInInternalField(self, index, value) }
}
/// Wraps a JS wrapper with a C++ instance.
///
/// # Safety
///
/// The `TAG` must be unique to the caller within the heap.
#[allow(clippy::not_unsafe_ptr_arg_deref)]
#[inline(always)]
pub unsafe fn wrap<const TAG: u16, T: GarbageCollected>(
scope: &mut HandleScope,
wrapper: Local<Object>,
value: &impl GetRustObj<T>,
) {
// TODO: use a const assert once const expressions are stable
assert!(TAG < LAST_TAG);
let ptr = value.get_rust_obj();
unsafe { v8__Object__Wrap(scope.get_isolate_ptr(), &*wrapper, ptr, TAG) }
}
/// Unwraps a JS wrapper object.
///
/// # Safety
///
/// The caller must ensure that the returned pointer is always stored on
/// the stack, or moved into one of the Persistent types.
#[inline(always)]
pub unsafe fn unwrap<const TAG: u16, T: GarbageCollected>(
scope: &mut HandleScope,
wrapper: Local<Object>,
) -> Member<T> {
// TODO: use a const assert once const expressions are stable
assert!(TAG < LAST_TAG);
let ptr =
unsafe { v8__Object__Unwrap(scope.get_isolate_ptr(), &*wrapper, TAG) };
Member::new(ptr)
}
/// Returns true if this object can be generally used to wrap object objects.
/// This means that the object either follows the convention of using embedder
/// fields to denote type/instance pointers or is using the Wrap()/Unwrap()
/// APIs for the same purpose. Returns false otherwise.
///
/// Note that there may be other objects that use embedder fields but are not
/// used as API wrapper objects. E.g., v8::Promise may in certain configuration
/// use embedder fields but promises are not generally supported as API
/// wrappers. The method will return false in those cases.
#[inline(always)]
pub fn is_api_wrapper(&self) -> bool {
unsafe { v8__Object__IsApiWrapper(self) }
}
/// Sets the integrity level of the object.
#[inline(always)]
pub fn set_integrity_level(

View File

@ -102,8 +102,9 @@ impl SnapshotCreator {
#[allow(clippy::new_ret_no_self)]
pub(crate) fn new(
external_references: Option<&'static ExternalReferences>,
params: Option<crate::CreateParams>,
) -> OwnedIsolate {
Self::new_impl(external_references, None::<&[u8]>)
Self::new_impl(external_references, None::<&[u8]>, params)
}
/// Create an isolate, and set it up for serialization.
@ -113,8 +114,9 @@ impl SnapshotCreator {
pub(crate) fn from_existing_snapshot(
existing_snapshot_blob: impl Allocated<[u8]>,
external_references: Option<&'static ExternalReferences>,
params: Option<crate::CreateParams>,
) -> OwnedIsolate {
Self::new_impl(external_references, Some(existing_snapshot_blob))
Self::new_impl(external_references, Some(existing_snapshot_blob), params)
}
/// Create and enter an isolate, and set it up for serialization.
@ -124,10 +126,11 @@ impl SnapshotCreator {
fn new_impl(
external_references: Option<&'static ExternalReferences>,
existing_snapshot_blob: Option<impl Allocated<[u8]>>,
params: Option<crate::CreateParams>,
) -> OwnedIsolate {
let mut snapshot_creator: MaybeUninit<Self> = MaybeUninit::uninit();
let mut params = crate::CreateParams::default();
let mut params = params.unwrap_or_default();
if let Some(external_refs) = external_references {
params = params.external_references(&**external_refs);
}

View File

@ -326,7 +326,7 @@ fn dropped_context_slots_on_kept_context() {
fn clear_all_context_slots() {
setup();
let mut snapshot_creator = v8::Isolate::snapshot_creator(None);
let mut snapshot_creator = v8::Isolate::snapshot_creator(None, None);
{
let scope = &mut v8::HandleScope::new(&mut snapshot_creator);

View File

@ -5395,7 +5395,7 @@ fn snapshot_creator() {
let context_data_index;
let context_data_index_2;
let startup_data = {
let mut snapshot_creator = v8::Isolate::snapshot_creator(None);
let mut snapshot_creator = v8::Isolate::snapshot_creator(None, None);
{
let scope = &mut v8::HandleScope::new(&mut snapshot_creator);
let context = v8::Context::new(scope);
@ -5411,7 +5411,11 @@ fn snapshot_creator() {
let startup_data = {
let mut snapshot_creator =
v8::Isolate::snapshot_creator_from_existing_snapshot(startup_data, None);
v8::Isolate::snapshot_creator_from_existing_snapshot(
startup_data,
None,
None,
);
{
// Check that the SnapshotCreator isolate has been set up correctly.
let _ = snapshot_creator.thread_safe_handle();
@ -5482,7 +5486,7 @@ fn snapshot_creator() {
fn snapshot_creator_multiple_contexts() {
let _setup_guard = setup::sequential_test();
let startup_data = {
let mut snapshot_creator = v8::Isolate::snapshot_creator(None);
let mut snapshot_creator = v8::Isolate::snapshot_creator(None, None);
{
let mut scope = v8::HandleScope::new(&mut snapshot_creator);
let context = v8::Context::new(&mut scope);
@ -5517,7 +5521,11 @@ fn snapshot_creator_multiple_contexts() {
let startup_data = {
let mut snapshot_creator =
v8::Isolate::snapshot_creator_from_existing_snapshot(startup_data, None);
v8::Isolate::snapshot_creator_from_existing_snapshot(
startup_data,
None,
None,
);
{
let scope = &mut v8::HandleScope::new(&mut snapshot_creator);
let context = v8::Context::new(scope);
@ -5655,7 +5663,7 @@ fn external_references() {
// First we create the snapshot, there is a single global variable 'a' set to
// the value 3.
let startup_data = {
let mut snapshot_creator = v8::Isolate::snapshot_creator(Some(refs));
let mut snapshot_creator = v8::Isolate::snapshot_creator(Some(refs), None);
{
let scope = &mut v8::HandleScope::new(&mut snapshot_creator);
let context = v8::Context::new(scope);
@ -7477,7 +7485,7 @@ fn module_snapshot() {
let _setup_guard = setup::sequential_test();
let startup_data = {
let mut snapshot_creator = v8::Isolate::snapshot_creator(None);
let mut snapshot_creator = v8::Isolate::snapshot_creator(None, None);
{
let scope = &mut v8::HandleScope::new(&mut snapshot_creator);
let context = v8::Context::new(scope);

View File

@ -26,7 +26,7 @@ impl Drop for CppGCGuard {
}
}
const DEFAULT_CPP_GC_EMBEDDER_ID: u16 = 0xde90;
const TAG: u16 = 1;
#[test]
fn cppgc_object_wrap() {
@ -35,11 +35,14 @@ fn cppgc_object_wrap() {
static TRACE_COUNT: AtomicUsize = AtomicUsize::new(0);
static DROP_COUNT: AtomicUsize = AtomicUsize::new(0);
struct Wrap;
struct Wrap {
value: v8::TracedReference<v8::Value>,
}
impl GarbageCollected for Wrap {
fn trace(&self, _: &Visitor) {
fn trace(&self, visitor: &Visitor) {
TRACE_COUNT.fetch_add(1, Ordering::SeqCst);
visitor.trace(&self.value);
}
}
@ -49,62 +52,84 @@ fn cppgc_object_wrap() {
}
}
fn op_make_wrap(
fn op_wrap(
scope: &mut v8::HandleScope,
_: v8::FunctionCallbackArguments,
args: v8::FunctionCallbackArguments,
mut rv: v8::ReturnValue,
) {
let templ = v8::ObjectTemplate::new(scope);
templ.set_internal_field_count(2);
fn empty(
_scope: &mut v8::HandleScope,
_args: v8::FunctionCallbackArguments,
_rv: v8::ReturnValue,
) {
}
let templ = v8::FunctionTemplate::new(scope, empty);
let func = templ.get_function(scope).unwrap();
let obj = func.new_instance(scope, &[]).unwrap();
assert!(obj.is_api_wrapper());
let obj = templ.new_instance(scope).unwrap();
let wrap = Wrap {
value: v8::TracedReference::new(scope, args.get(0)),
};
let member = unsafe {
v8::cppgc::make_garbage_collected(scope.get_cpp_heap().unwrap(), wrap)
};
let member = v8::cppgc::make_garbage_collected(
scope.get_cpp_heap().unwrap(),
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 _);
unsafe {
v8::Object::wrap::<TAG, Wrap>(scope, obj, &member);
}
rv.set(obj.into());
}
fn op_unwrap(
scope: &mut v8::HandleScope,
args: v8::FunctionCallbackArguments,
mut rv: v8::ReturnValue,
) {
let obj = args.get(0).try_into().unwrap();
let member = unsafe { v8::Object::unwrap::<TAG, Wrap>(scope, obj) };
rv.set(member.borrow().unwrap().value.get(scope).unwrap());
}
{
let isolate = &mut v8::Isolate::new(v8::CreateParams::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,
)),
v8::cppgc::HeapCreateParams::default(),
);
isolate.attach_cpp_heap(&heap);
let isolate =
&mut v8::Isolate::new(v8::CreateParams::default().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();
let func = v8::Function::new(scope, op_wrap).unwrap();
let name = v8::String::new(scope, "wrap").unwrap();
global.set(scope, name.into(), func.into()).unwrap();
}
{
let func = v8::Function::new(scope, op_unwrap).unwrap();
let name = v8::String::new(scope, "unwrap").unwrap();
global.set(scope, name.into(), func.into()).unwrap();
}
let source = v8::String::new(
execute_script(
scope,
r#"
make_wrap(); // Inaccessible after scope.
globalThis.wrap = make_wrap(); // Accessible after scope.
{
const x = {};
const y = unwrap(wrap(x)); // collected
if (x !== y) {
throw new Error('mismatch');
}
}
globalThis.wrapped = wrap(wrap({})); // not collected
"#,
)
.unwrap();
execute_script(scope, source);
);
assert_eq!(DROP_COUNT.load(Ordering::SeqCst), 0);
@ -113,24 +138,38 @@ fn cppgc_object_wrap() {
assert!(TRACE_COUNT.load(Ordering::SeqCst) > 0);
assert_eq!(DROP_COUNT.load(Ordering::SeqCst), 1);
execute_script(
scope,
r#"
globalThis.wrapped = undefined;
"#,
);
scope
.request_garbage_collection_for_testing(v8::GarbageCollectionType::Full);
assert_eq!(DROP_COUNT.load(Ordering::SeqCst), 3);
}
}
fn execute_script(
context_scope: &mut v8::ContextScope<v8::HandleScope>,
script: v8::Local<v8::String>,
source: &str,
) {
let scope = &mut v8::HandleScope::new(context_scope);
let try_catch = &mut v8::TryCatch::new(scope);
let scope = &mut v8::TryCatch::new(scope);
let script = v8::Script::compile(try_catch, script, None)
.expect("failed to compile script");
let source = v8::String::new(scope, source).unwrap();
if script.run(try_catch).is_none() {
let exception_string = try_catch
let script =
v8::Script::compile(scope, source, None).expect("failed to compile script");
if script.run(scope).is_none() {
let exception_string = scope
.stack_trace()
.or_else(|| try_catch.exception())
.map(|value| value.to_rust_string_lossy(try_catch))
.or_else(|| scope.exception())
.map(|value| value.to_rust_string_lossy(scope))
.unwrap_or_else(|| "no stack trace".into());
panic!("{}", exception_string);