use crate::Data;
use crate::TracedReference;
use crate::binding::RustObj;
use crate::platform::Platform;
use crate::support::Opaque;
use crate::support::SharedRef;
use crate::support::UniqueRef;
use crate::support::int;
use std::cell::UnsafeCell;
use std::ffi::CStr;
use std::ffi::c_char;
use std::marker::PhantomData;
use std::ptr::NonNull;
unsafe extern "C" {
fn cppgc__initialize_process(platform: *mut Platform);
fn cppgc__shutdown_process();
fn v8__CppHeap__Create(
platform: *mut Platform,
marking_support: MarkingType,
sweeping_support: SweepingType,
) -> *mut Heap;
fn v8__CppHeap__Terminate(heap: *mut Heap);
fn v8__CppHeap__DELETE(heap: *mut Heap);
fn cppgc__make_garbage_collectable(
heap: *mut Heap,
size: usize,
alignment: usize,
) -> *mut RustObj;
fn cppgc__heap__enable_detached_garbage_collections_for_testing(
heap: *mut Heap,
);
fn cppgc__heap__collect_garbage_for_testing(
heap: *mut Heap,
stack_state: EmbedderStackState,
);
fn cppgc__Visitor__Trace__Member(
visitor: *mut Visitor,
member: *const MemberInner,
);
fn cppgc__Visitor__Trace__WeakMember(
visitor: *mut Visitor,
member: *const WeakMemberInner,
);
fn cppgc__Visitor__Trace__TracedReference(
visitor: *mut 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(obj: *mut RustObj) -> *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(
obj: *mut RustObj,
) -> *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;
}
#[unsafe(no_mangle)]
unsafe extern "C" fn rusty_v8_RustObj_trace(
obj: *const RustObj,
visitor: *mut Visitor,
) {
unsafe {
let r = get_rust_obj(obj);
r.trace(&mut *visitor);
}
}
#[unsafe(no_mangle)]
unsafe extern "C" fn rusty_v8_RustObj_get_name(
obj: *const RustObj,
) -> *const c_char {
let r = unsafe { get_rust_obj(obj) };
r.get_name().as_ptr()
}
#[unsafe(no_mangle)]
unsafe extern "C" fn rusty_v8_RustObj_drop(obj: *mut RustObj) {
unsafe {
let r = get_rust_obj_mut(obj);
std::ptr::drop_in_place(r);
}
}
pub fn initialize_process(platform: SharedRef<Platform>) {
unsafe {
cppgc__initialize_process(&*platform as *const Platform as *mut _);
}
}
pub unsafe fn shutdown_process() {
unsafe {
cppgc__shutdown_process();
}
}
#[repr(C)]
#[derive(Debug)]
pub struct Visitor(Opaque);
impl Visitor {
#[inline(always)]
pub fn trace(&mut self, member: &impl Traced) {
member.trace(self);
}
}
pub trait Traced {
fn trace(&self, visitor: &mut Visitor);
}
impl<T: Traced> Traced for Option<T> {
fn trace(&self, visitor: &mut Visitor) {
if let Some(value) = self {
visitor.trace(value);
}
}
}
impl<T> Traced for TracedReference<T> {
fn trace(&self, visitor: &mut Visitor) {
unsafe {
cppgc__Visitor__Trace__TracedReference(
visitor,
self as *const TracedReference<T> as *const TracedReference<Data>,
);
}
}
}
#[repr(C)]
pub enum EmbedderStackState {
MayContainHeapPointers,
NoHeapPointers,
}
#[repr(u8)]
pub enum MarkingType {
Atomic,
Incremental,
IncrementalAndConcurrent,
}
#[repr(u8)]
pub enum SweepingType {
Atomic,
Incremental,
IncrementalAndConcurrent,
}
pub type InternalFieldIndex = int;
pub struct HeapCreateParams {
pub marking_support: MarkingType,
pub sweeping_support: SweepingType,
}
impl Default for HeapCreateParams {
fn default() -> Self {
Self {
marking_support: MarkingType::IncrementalAndConcurrent,
sweeping_support: SweepingType::IncrementalAndConcurrent,
}
}
}
#[repr(C)]
#[derive(Debug)]
pub struct Heap(Opaque);
impl Drop for Heap {
fn drop(&mut self) {
unsafe {
v8__CppHeap__DELETE(self);
}
}
}
impl Heap {
pub fn create(
platform: SharedRef<Platform>,
params: HeapCreateParams,
) -> UniqueRef<Heap> {
unsafe {
UniqueRef::from_raw(v8__CppHeap__Create(
&*platform as *const Platform as *mut _,
params.marking_support,
params.sweeping_support,
))
}
}
pub unsafe fn collect_garbage_for_testing(
&self,
stack_state: EmbedderStackState,
) {
unsafe {
cppgc__heap__collect_garbage_for_testing(
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 terminate(&mut self) {
unsafe {
v8__CppHeap__Terminate(self);
}
}
}
pub unsafe trait GarbageCollected {
fn trace(&self, visitor: &mut Visitor);
fn get_name(&self) -> &'static CStr;
}
#[repr(C)]
struct RustObjConcrete<T> {
head: RustObj,
dynamic: *mut dyn GarbageCollected,
value: T,
}
unsafe fn get_rust_obj<'s>(obj: *const RustObj) -> &'s dyn GarbageCollected {
unsafe {
let obj = &*(obj as *const RustObjConcrete<()>);
&*obj.dynamic
}
}
unsafe fn get_rust_obj_mut<'s>(
obj: *mut RustObj,
) -> &'s mut dyn GarbageCollected {
unsafe {
let obj = &mut *(obj as *mut RustObjConcrete<()>);
&mut *obj.dynamic
}
}
pub unsafe fn make_garbage_collected<T: GarbageCollected + 'static>(
heap: &Heap,
obj: T,
) -> UnsafePtr<T> {
const {
assert!(std::mem::align_of::<T>() <= 16);
assert!(
std::mem::offset_of!(RustObjConcrete<T>, dynamic)
== std::mem::offset_of!(RustObjConcrete<()>, dynamic)
);
}
let additional_bytes =
std::mem::size_of::<RustObjConcrete<T>>() - std::mem::size_of::<RustObj>();
let pointer = unsafe {
cppgc__make_garbage_collectable(
heap as *const Heap as *mut _,
additional_bytes,
std::mem::align_of::<RustObjConcrete<T>>(),
)
};
assert!(!pointer.is_null());
unsafe {
let pointer = &mut *(pointer as *mut RustObjConcrete<T>);
let value_ptr = std::ptr::addr_of_mut!(pointer.value);
value_ptr.write(obj);
std::ptr::addr_of_mut!(pointer.dynamic).write(value_ptr as _);
}
UnsafePtr {
pointer: unsafe { NonNull::new_unchecked(pointer) },
_phantom: PhantomData,
}
}
unsafe fn get_value_from_rust_obj<T: GarbageCollected>(
obj: *mut RustObj,
) -> *const T {
unsafe {
let obj = &mut *(obj as *mut RustObjConcrete<T>);
std::ptr::addr_of_mut!(obj.value)
}
}
#[derive(Clone, Copy)]
pub struct UnsafePtr<T: GarbageCollected> {
pointer: NonNull<RustObj>,
_phantom: PhantomData<T>,
}
impl<T: GarbageCollected> UnsafePtr<T> {
pub unsafe fn new(value: &impl GetRustObj<T>) -> Option<UnsafePtr<T>> {
NonNull::new(value.get_rust_obj()).map(|pointer| UnsafePtr {
pointer,
_phantom: PhantomData,
})
}
pub unsafe fn as_ref(&self) -> &T {
unsafe { &*get_value_from_rust_obj(self.pointer.as_ptr()) }
}
}
impl<T: GarbageCollected> GetRustObj<T> for UnsafePtr<T> {
fn get_rust_obj(&self) -> *mut RustObj {
self.pointer.as_ptr()
}
}
#[doc(hidden)]
pub trait GetRustObj<T: GarbageCollected> {
fn get_rust_obj(&self) -> *mut RustObj;
}
impl<T: GarbageCollected> GetRustObj<T> for *mut RustObj {
fn get_rust_obj(&self) -> *mut RustObj {
*self
}
}
macro_rules! member {
($( # $attr:tt )* $name:ident) => {
paste::paste! {
#[repr(transparent)]
struct [< $name Inner >]([u8; crate::binding:: [< 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 {
unsafe { [< cppgc__ $name __Get >](self) }
}
#[inline(always)]
fn assign(&mut self, ptr: *mut RustObj) {
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> {
#[doc = "Create a new empty "]
#[doc = stringify!($name)]
#[doc = " which may be set later."]
pub fn empty() -> Self {
Self {
inner: [< $name Inner >]::new(std::ptr::null_mut()),
_phantom: PhantomData,
}
}
#[doc = "Create a new "]
#[doc = stringify!($name)]
#[doc = " and initialize it with an object."]
pub fn new(other: &impl GetRustObj<T>) -> Self {
Self {
inner: [< $name Inner >]::new(other.get_rust_obj()),
_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.inner.assign(ptr);
}
#[doc = "Get the object pointed to by this "]
#[doc = stringify!($name)]
#[doc = ", returning `None` if the pointer is empty or has been garbage-collected."]
#[doc = ""]
#[doc = "# Safety"]
#[doc = ""]
#[doc = "The caller must ensure that this pointer is being traced correctly by appearing in the [`trace`](GarbageCollected::trace)"]
#[doc = "implementation of the object that owns the pointer. Between initializing the pointer and calling `get()`, the pointer must be reachable by the garbage collector."]
pub unsafe fn get(&self) -> Option<&T> {
let ptr = self.inner.get();
if ptr.is_null() {
None
} else {
Some(unsafe { &*get_value_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> Traced for $name<T> {
fn trace(&self, visitor: &mut Visitor) {
unsafe { [< cppgc__Visitor__Trace__ $name >](visitor, &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()
}
}
}
}
}
member! {
Member
}
member! {
WeakMember
}
macro_rules! persistent {
($( # $attr:tt )* $name:ident) => {
paste::paste! {
#[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 >](std::ptr::null_mut()) };
Self {
inner: this,
_phantom: PhantomData,
}
}
#[doc = "Create a new "]
#[doc = stringify!($name)]
#[doc = " and initialize it with an object."]
pub fn new(other: &impl GetRustObj<T>) -> Self {
let this = unsafe { [< cppgc__ $name __CONSTRUCT >](other.get_rust_obj()) };
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 get(&self) -> Option<&T> {
let ptr = self.get_rust_obj();
if ptr.is_null() {
None
} else {
Some(unsafe { &*get_value_from_rust_obj(ptr) })
}
}
#[inline(always)]
fn assign(&mut self, ptr: *mut RustObj) {
unsafe {
[< cppgc__ $name __Assign >](self.inner, ptr);
}
}
}
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 {
unsafe {
[< cppgc__ $name __Get >](self.inner)
}
}
}
}
};
}
persistent! {
Persistent
}
persistent! {
WeakPersistent
}
pub struct GcCell<T> {
value: UnsafeCell<T>,
}
impl<T> std::fmt::Debug for GcCell<T> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("GcCell").finish()
}
}
unsafe impl<T: Send> Send for GcCell<T> {}
unsafe impl<T: Sync> Sync for GcCell<T> {}
impl<T> GcCell<T> {
pub fn new(value: T) -> Self {
Self {
value: UnsafeCell::new(value),
}
}
pub fn set(&self, isolate: &mut crate::Isolate, value: T) {
_ = isolate;
unsafe {
*self.value.get() = value;
}
}
}
impl<T> GcCell<T> {
pub fn get<'a>(&'a self, isolate: &'a crate::Isolate) -> &'a T {
_ = isolate;
unsafe {
&*self.value.get()
}
}
pub fn get_mut<'a>(&'a self, isolate: &'a mut crate::Isolate) -> &'a mut T {
_ = isolate;
unsafe {
&mut *self.value.get()
}
}
pub fn with<'a, 's, R>(
&'a self,
scope: &'a mut crate::HandleScope<'s>,
f: impl FnOnce(&'a mut crate::HandleScope<'s>, &'a mut T) -> R,
) -> R {
f(scope, unsafe { &mut *self.value.get() })
}
}
impl<T: Traced> Traced for GcCell<T> {
fn trace(&self, visitor: &mut Visitor) {
unsafe {
visitor.trace(&(*self.value.get()));
}
}
}