Skip to main content

v8/
cppgc.rs

1// Copyright 2019-2021 the Deno authors. All rights reserved. MIT license
2
3use crate::Data;
4use crate::TracedReference;
5use crate::binding::RustObj;
6use crate::platform::Platform;
7use crate::support::Opaque;
8use crate::support::SharedRef;
9use crate::support::UniqueRef;
10use crate::support::int;
11use std::cell::UnsafeCell;
12use std::ffi::CStr;
13use std::ffi::c_char;
14use std::marker::PhantomData;
15use std::ptr::NonNull;
16
17unsafe extern "C" {
18  fn cppgc__initialize_process(platform: *mut Platform);
19  fn cppgc__shutdown_process();
20
21  fn v8__CppHeap__Create(
22    platform: *mut Platform,
23    marking_support: MarkingType,
24    sweeping_support: SweepingType,
25  ) -> *mut Heap;
26  fn v8__CppHeap__Terminate(heap: *mut Heap);
27  fn v8__CppHeap__DELETE(heap: *mut Heap);
28  fn cppgc__make_garbage_collectable(
29    heap: *mut Heap,
30    size: usize,
31    alignment: usize,
32  ) -> *mut RustObj;
33
34  fn cppgc__heap__enable_detached_garbage_collections_for_testing(
35    heap: *mut Heap,
36  );
37  fn cppgc__heap__collect_garbage_for_testing(
38    heap: *mut Heap,
39    stack_state: EmbedderStackState,
40  );
41
42  fn cppgc__Visitor__Trace__Member(
43    visitor: *mut Visitor,
44    member: *const MemberInner,
45  );
46  fn cppgc__Visitor__Trace__WeakMember(
47    visitor: *mut Visitor,
48    member: *const WeakMemberInner,
49  );
50  fn cppgc__Visitor__Trace__TracedReference(
51    visitor: *mut Visitor,
52    reference: *const TracedReference<Data>,
53  );
54
55  fn cppgc__Member__CONSTRUCT(member: *mut MemberInner, obj: *mut RustObj);
56  fn cppgc__Member__DESTRUCT(member: *mut MemberInner);
57  fn cppgc__Member__Get(member: *const MemberInner) -> *mut RustObj;
58  fn cppgc__Member__Assign(member: *mut MemberInner, other: *mut RustObj);
59
60  fn cppgc__WeakMember__CONSTRUCT(
61    member: *mut WeakMemberInner,
62    obj: *mut RustObj,
63  );
64  fn cppgc__WeakMember__DESTRUCT(member: *mut WeakMemberInner);
65  fn cppgc__WeakMember__Get(member: *const WeakMemberInner) -> *mut RustObj;
66  fn cppgc__WeakMember__Assign(
67    member: *mut WeakMemberInner,
68    other: *mut RustObj,
69  );
70
71  fn cppgc__Persistent__CONSTRUCT(obj: *mut RustObj) -> *mut PersistentInner;
72  fn cppgc__Persistent__DESTRUCT(this: *mut PersistentInner);
73  fn cppgc__Persistent__Assign(this: *mut PersistentInner, ptr: *mut RustObj);
74  fn cppgc__Persistent__Get(this: *const PersistentInner) -> *mut RustObj;
75
76  fn cppgc__WeakPersistent__CONSTRUCT(
77    obj: *mut RustObj,
78  ) -> *mut WeakPersistentInner;
79  fn cppgc__WeakPersistent__DESTRUCT(this: *mut WeakPersistentInner);
80  fn cppgc__WeakPersistent__Assign(
81    this: *mut WeakPersistentInner,
82    ptr: *mut RustObj,
83  );
84  fn cppgc__WeakPersistent__Get(
85    this: *const WeakPersistentInner,
86  ) -> *mut RustObj;
87}
88
89#[unsafe(no_mangle)]
90unsafe extern "C" fn rusty_v8_RustObj_trace(
91  obj: *const RustObj,
92  visitor: *mut Visitor,
93) {
94  unsafe {
95    let r = get_rust_obj(obj);
96    r.trace(&mut *visitor);
97  }
98}
99
100#[unsafe(no_mangle)]
101unsafe extern "C" fn rusty_v8_RustObj_get_name(
102  obj: *const RustObj,
103) -> *const c_char {
104  let r = unsafe { get_rust_obj(obj) };
105  r.get_name().as_ptr()
106}
107
108#[unsafe(no_mangle)]
109unsafe extern "C" fn rusty_v8_RustObj_drop(obj: *mut RustObj) {
110  unsafe {
111    let r = get_rust_obj_mut(obj);
112    std::ptr::drop_in_place(r);
113  }
114}
115
116/// Process-global initialization of the garbage collector. Must be called before
117/// creating a Heap.
118///
119/// Can be called multiple times when paired with `ShutdownProcess()`.
120pub fn initialize_process(platform: SharedRef<Platform>) {
121  unsafe {
122    cppgc__initialize_process(&*platform as *const Platform as *mut _);
123  }
124}
125
126/// # Safety
127///
128/// Must be called after destroying the last used heap. Some process-global
129/// metadata may not be returned and reused upon a subsequent
130/// `initialize_process()` call.
131pub unsafe fn shutdown_process() {
132  unsafe {
133    cppgc__shutdown_process();
134  }
135}
136
137/// Visitor passed to trace methods. All managed pointers must have called the
138/// Visitor's trace method on them.
139///
140/// ```
141/// use v8::cppgc::{Member, Visitor, GarbageCollected};
142/// use std::ffi::CStr;
143///
144/// struct Foo { foo: Member<Foo> }
145///
146/// unsafe impl GarbageCollected for Foo {
147///   fn trace(&self, visitor: &mut Visitor) {
148///     visitor.trace(&self.foo);
149///   }
150///
151///   fn get_name(&self) -> &'static std::ffi::CStr {
152///     c"Foo"
153///   }
154/// }
155/// ```
156#[repr(C)]
157#[derive(Debug)]
158pub struct Visitor(Opaque);
159
160impl Visitor {
161  /// Trace a managed object.
162  #[inline(always)]
163  pub fn trace(&mut self, member: &impl Traced) {
164    member.trace(self);
165  }
166}
167
168/// Trait for inlined objects that are not allocated
169/// themselves but otherwise follow managed heap layout.
170pub trait Traced {
171  /// Called by the `Visitor` when tracing managed objects.
172  fn trace(&self, visitor: &mut Visitor);
173}
174
175impl<T: Traced> Traced for Option<T> {
176  fn trace(&self, visitor: &mut Visitor) {
177    if let Some(value) = self {
178      visitor.trace(value);
179    }
180  }
181}
182
183impl<T> Traced for TracedReference<T> {
184  fn trace(&self, visitor: &mut Visitor) {
185    unsafe {
186      cppgc__Visitor__Trace__TracedReference(
187        visitor,
188        self as *const TracedReference<T> as *const TracedReference<Data>,
189      );
190    }
191  }
192}
193
194#[repr(C)]
195pub enum EmbedderStackState {
196  /// Stack may contain interesting heap pointers.
197  MayContainHeapPointers,
198  /// Stack does not contain any interesting heap pointers.
199  NoHeapPointers,
200}
201
202/// Specifies supported marking types.
203#[repr(u8)]
204pub enum MarkingType {
205  /// Atomic stop-the-world marking. This option does not require any write barriers but is the most intrusive in terms of jank.
206  Atomic,
207  /// Incremental marking interleaves marking with the rest of the application workload on the same thread.
208  Incremental,
209  /// Incremental and concurrent marking.
210  IncrementalAndConcurrent,
211}
212
213/// Specifies supported sweeping types.
214#[repr(u8)]
215pub enum SweepingType {
216  /// Atomic stop-the-world sweeping. All of sweeping is performed at once.
217  Atomic,
218  /// Incremental sweeping interleaves sweeping with the rest of the application workload on the same thread.
219  Incremental,
220  /// Incremental and concurrent sweeping. Sweeping is split and interleaved with the rest of the application.
221  IncrementalAndConcurrent,
222}
223
224pub type InternalFieldIndex = int;
225
226pub struct HeapCreateParams {
227  /// Specifies which kind of marking are supported by the heap.
228  pub marking_support: MarkingType,
229  /// Specifies which kind of sweeping are supported by the heap.
230  pub sweeping_support: SweepingType,
231}
232
233impl Default for HeapCreateParams {
234  fn default() -> Self {
235    Self {
236      marking_support: MarkingType::IncrementalAndConcurrent,
237      sweeping_support: SweepingType::IncrementalAndConcurrent,
238    }
239  }
240}
241
242/// A heap for allocating managed C++ objects.
243///
244/// Similar to v8::Isolate, the heap may only be accessed from one thread at a
245/// time.
246#[repr(C)]
247#[derive(Debug)]
248pub struct Heap(Opaque);
249
250impl Drop for Heap {
251  fn drop(&mut self) {
252    unsafe {
253      v8__CppHeap__DELETE(self);
254    }
255  }
256}
257
258impl Heap {
259  pub fn create(
260    platform: SharedRef<Platform>,
261    params: HeapCreateParams,
262  ) -> UniqueRef<Heap> {
263    unsafe {
264      UniqueRef::from_raw(v8__CppHeap__Create(
265        &*platform as *const Platform as *mut _,
266        params.marking_support,
267        params.sweeping_support,
268      ))
269    }
270  }
271
272  pub unsafe fn collect_garbage_for_testing(
273    &self,
274    stack_state: EmbedderStackState,
275  ) {
276    unsafe {
277      cppgc__heap__collect_garbage_for_testing(
278        self as *const Heap as *mut _,
279        stack_state,
280      );
281    }
282  }
283
284  pub fn enable_detached_garbage_collections_for_testing(&self) {
285    unsafe {
286      cppgc__heap__enable_detached_garbage_collections_for_testing(
287        self as *const Heap as *mut _,
288      );
289    }
290  }
291
292  pub fn terminate(&mut self) {
293    unsafe {
294      v8__CppHeap__Terminate(self);
295    }
296  }
297}
298
299/// Base trait for objects supporting garbage collection.
300///
301/// # Safety
302///
303/// implementors must guarantee that the `trace()`
304/// method correctly visits all [`Member`], [`WeakMember`], and
305/// [`TraceReference`] pointers held by this object. Failing to do so will leave
306/// dangling pointers in the heap as objects are garbage collected.
307pub unsafe trait GarbageCollected {
308  /// `trace` must call [`Visitor::trace`] for each
309  /// [`Member`], [`WeakMember`], or [`TracedReference`] reachable
310  /// from `self`.
311  fn trace(&self, visitor: &mut Visitor);
312
313  /// Specifies a name for the garbage-collected object. Such names will never
314  /// be hidden, as they are explicitly specified by the user of this API.
315  ///
316  /// V8 may call this function while generating a heap snapshot or at other
317  /// times. If V8 is currently generating a heap snapshot (according to
318  /// HeapProfiler::IsTakingSnapshot), then the returned string must stay alive
319  /// until the snapshot generation has completed. Otherwise, the returned string
320  /// must stay alive forever. If you need a place to store a temporary string
321  /// during snapshot generation, use HeapProfiler::CopyNameForHeapSnapshot.
322  fn get_name(&self) -> &'static CStr;
323}
324
325#[repr(C)]
326struct RustObjConcrete<T> {
327  head: RustObj,
328  dynamic: *mut dyn GarbageCollected,
329  value: T,
330}
331
332unsafe fn get_rust_obj<'s>(obj: *const RustObj) -> &'s dyn GarbageCollected {
333  unsafe {
334    let obj = &*(obj as *const RustObjConcrete<()>);
335    &*obj.dynamic
336  }
337}
338
339unsafe fn get_rust_obj_mut<'s>(
340  obj: *mut RustObj,
341) -> &'s mut dyn GarbageCollected {
342  unsafe {
343    let obj = &mut *(obj as *mut RustObjConcrete<()>);
344    &mut *obj.dynamic
345  }
346}
347
348/// Constructs an instance of T, which is a garbage collected type.
349///
350/// The object will be allocated on the heap and managed by cppgc. During
351/// marking, the object will be traced by calling the `trace` method on it.
352///
353/// During sweeping, the destructor will be called and the memory will be
354/// freed.
355///
356/// # Safety
357///
358/// The caller must ensure that the returned pointer is always stored on
359/// the stack, or is safely moved into one of the other cppgc pointer types.
360pub unsafe fn make_garbage_collected<T: GarbageCollected + 'static>(
361  heap: &Heap,
362  obj: T,
363) -> UnsafePtr<T> {
364  const {
365    // max alignment in cppgc is 16
366    assert!(std::mem::align_of::<T>() <= 16);
367    assert!(
368      std::mem::offset_of!(RustObjConcrete<T>, dynamic)
369        == std::mem::offset_of!(RustObjConcrete<()>, dynamic)
370    );
371  }
372
373  let additional_bytes =
374    std::mem::size_of::<RustObjConcrete<T>>() - std::mem::size_of::<RustObj>();
375
376  let pointer = unsafe {
377    cppgc__make_garbage_collectable(
378      heap as *const Heap as *mut _,
379      additional_bytes,
380      std::mem::align_of::<RustObjConcrete<T>>(),
381    )
382  };
383
384  assert!(!pointer.is_null());
385
386  unsafe {
387    let pointer = &mut *(pointer as *mut RustObjConcrete<T>);
388    let value_ptr = std::ptr::addr_of_mut!(pointer.value);
389    value_ptr.write(obj);
390    std::ptr::addr_of_mut!(pointer.dynamic).write(value_ptr as _);
391  }
392
393  UnsafePtr {
394    pointer: unsafe { NonNull::new_unchecked(pointer) },
395    _phantom: PhantomData,
396  }
397}
398
399/// # Safety
400///
401/// T must be the correct type for this specific RustObj
402unsafe fn get_value_from_rust_obj<T: GarbageCollected>(
403  obj: *mut RustObj,
404) -> *const T {
405  unsafe {
406    let obj = &mut *(obj as *mut RustObjConcrete<T>);
407    std::ptr::addr_of_mut!(obj.value)
408  }
409}
410
411/// UnsafePtr is used to refer to an on-heap object from the stack.
412#[derive(Clone, Copy)]
413pub struct UnsafePtr<T: GarbageCollected> {
414  pointer: NonNull<RustObj>,
415  _phantom: PhantomData<T>,
416}
417
418impl<T: GarbageCollected> UnsafePtr<T> {
419  /// Create a new UnsafePtr.
420  ///
421  /// # Safety
422  ///
423  /// The caller must ensure that the returned pointer is always stored on
424  /// the stack, or is safely moved into one of the other cppgc pointer types.
425  pub unsafe fn new(value: &impl GetRustObj<T>) -> Option<UnsafePtr<T>> {
426    NonNull::new(value.get_rust_obj()).map(|pointer| UnsafePtr {
427      pointer,
428      _phantom: PhantomData,
429    })
430  }
431
432  pub unsafe fn as_ref(&self) -> &T {
433    unsafe { &*get_value_from_rust_obj(self.pointer.as_ptr()) }
434  }
435}
436
437impl<T: GarbageCollected> GetRustObj<T> for UnsafePtr<T> {
438  fn get_rust_obj(&self) -> *mut RustObj {
439    self.pointer.as_ptr()
440  }
441}
442
443#[doc(hidden)]
444pub trait GetRustObj<T: GarbageCollected> {
445  fn get_rust_obj(&self) -> *mut RustObj;
446}
447
448impl<T: GarbageCollected> GetRustObj<T> for *mut RustObj {
449  fn get_rust_obj(&self) -> *mut RustObj {
450    *self
451  }
452}
453
454macro_rules! member {
455  ($( # $attr:tt )* $name:ident) => {
456    paste::paste! {
457      #[repr(transparent)]
458      struct [< $name Inner >]([u8; crate::binding:: [< cppgc__ $name _SIZE >]]);
459
460      impl [< $name Inner >] {
461        fn new(ptr: *mut RustObj) -> Self {
462          let mut this = std::mem::MaybeUninit::uninit();
463          unsafe {
464            [< cppgc__ $name __CONSTRUCT >](this.as_mut_ptr(), ptr);
465            this.assume_init()
466          }
467        }
468
469        #[inline(always)]
470        fn get(&self) -> *mut RustObj {
471          // Member may be a compressed pointer, so just read it from C++
472          unsafe { [< cppgc__ $name __Get >](self) }
473        }
474
475        #[inline(always)]
476        fn assign(&mut self, ptr: *mut RustObj) {
477          // Assignment has write barriers in the GC, so call into C++
478          unsafe {
479            [< cppgc__ $name __Assign >](self, ptr);
480          }
481        }
482      }
483
484      impl Drop for [< $name Inner >] {
485        fn drop(&mut self) {
486          unsafe {
487            [< cppgc__ $name __DESTRUCT >](self);
488          }
489        }
490      }
491
492      $( # $attr )*
493      #[repr(transparent)]
494      pub struct $name<T: GarbageCollected> {
495        inner: [< $name Inner >],
496        _phantom: PhantomData<T>,
497      }
498
499      impl<T: GarbageCollected> $name<T> {
500        #[doc = "Create a new empty "]
501        #[doc = stringify!($name)]
502        #[doc = " which may be set later."]
503        pub fn empty() -> Self {
504          Self {
505            inner: [< $name Inner >]::new(std::ptr::null_mut()),
506            _phantom: PhantomData,
507          }
508        }
509
510        #[doc = "Create a new "]
511        #[doc = stringify!($name)]
512        #[doc = " and initialize it with an object."]
513        pub fn new(other: &impl GetRustObj<T>) -> Self {
514          Self {
515            inner: [< $name Inner >]::new(other.get_rust_obj()),
516            _phantom: PhantomData,
517          }
518        }
519
520        #[doc = "Set the object pointed to by this "]
521        #[doc = stringify!($name)]
522        #[doc = "."]
523        pub fn set(&mut self, other: &impl GetRustObj<T>) {
524          let ptr = other.get_rust_obj();
525          self.inner.assign(ptr);
526        }
527
528        #[doc = "Get the object pointed to by this "]
529        #[doc = stringify!($name)]
530        #[doc = ", returning `None` if the pointer is empty or has been garbage-collected."]
531        #[doc = ""]
532        #[doc = "# Safety"]
533        #[doc = ""]
534        #[doc = "The caller must ensure that this pointer is being traced correctly by appearing in the [`trace`](GarbageCollected::trace)"]
535        #[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."]
536        pub unsafe fn get(&self) -> Option<&T> {
537          let ptr = self.inner.get();
538          if ptr.is_null() {
539            None
540          } else {
541            // SAFETY: Either this is a strong reference and the pointer is valid according
542            // to the safety contract of this method, or this is a weak reference and the
543            // ptr will be null if it was collected.
544            Some(unsafe { &*get_value_from_rust_obj(ptr) })
545          }
546        }
547      }
548
549      impl<T: GarbageCollected> GetRustObj<T> for $name<T> {
550        fn get_rust_obj(&self) -> *mut RustObj {
551          self.inner.get()
552        }
553      }
554
555      impl<T: GarbageCollected> Traced for $name<T> {
556        fn trace(&self, visitor: &mut Visitor) {
557          unsafe { [< cppgc__Visitor__Trace__ $name >](visitor, &self.inner) }
558        }
559      }
560
561      impl<T: GarbageCollected> std::fmt::Debug for $name<T> {
562        fn fmt(&self, fmt: &mut std::fmt::Formatter) -> std::fmt::Result {
563          fmt.debug_struct(stringify!($name)).finish()
564        }
565      }
566    }
567  }
568}
569
570member! {
571  /// Members are used in classes to contain strong pointers to other garbage
572  /// collected objects. All Member fields of a class must be traced in the class'
573  /// trace method.
574  Member
575}
576
577member! {
578  /// WeakMember is similar to Member in that it is used to point to other garbage
579  /// collected objects. However instead of creating a strong pointer to the
580  /// object, the WeakMember creates a weak pointer, which does not keep the
581  /// pointee alive. Hence if all pointers to to a heap allocated object are weak
582  /// the object will be garbage collected. At the time of GC the weak pointers
583  /// will automatically be set to null.
584  WeakMember
585}
586
587macro_rules! persistent {
588  ($( # $attr:tt )* $name:ident) => {
589    paste::paste! {
590      // PersistentBase is extremely particular about move and copy semantics,
591      // so we allocate it on the heap and only interact with it via calls to C++.
592      #[repr(C)]
593      struct [< $name Inner >](Opaque);
594
595      $( # $attr )*
596      pub struct $name<T: GarbageCollected> {
597        inner: *mut [< $name Inner >],
598        _phantom: PhantomData<T>,
599      }
600
601      impl<T: GarbageCollected> $name<T> {
602        #[doc = "Create a new empty "]
603        #[doc = stringify!($name)]
604        #[doc = " which may be set later."]
605        pub fn empty() -> Self {
606          let this = unsafe { [< cppgc__ $name __CONSTRUCT >](std::ptr::null_mut()) };
607          Self {
608            inner: this,
609            _phantom: PhantomData,
610          }
611        }
612
613        #[doc = "Create a new "]
614        #[doc = stringify!($name)]
615        #[doc = " and initialize it with an object."]
616        pub fn new(other: &impl GetRustObj<T>) -> Self {
617          let this = unsafe { [< cppgc__ $name __CONSTRUCT >](other.get_rust_obj()) };
618          Self {
619            inner: this,
620            _phantom: PhantomData,
621          }
622        }
623
624        #[doc = "Set the object pointed to by this "]
625        #[doc = stringify!($name)]
626        #[doc = "."]
627        pub fn set(&mut self, other: &impl GetRustObj<T>) {
628          let ptr = other.get_rust_obj();
629          self.assign(ptr);
630        }
631
632        #[doc = "Borrow the object pointed to by this "]
633        #[doc = stringify!($name)]
634        #[doc = "."]
635        pub fn get(&self) -> Option<&T> {
636          let ptr = self.get_rust_obj();
637          if ptr.is_null() {
638            None
639          } else {
640            // SAFETY: Either this is a strong reference and the pointer is always valid
641            // or this is a weak reference and the ptr will be null if it was collected.
642            Some(unsafe { &*get_value_from_rust_obj(ptr) })
643          }
644        }
645
646        #[inline(always)]
647        fn assign(&mut self, ptr: *mut RustObj) {
648          unsafe {
649            [< cppgc__ $name __Assign >](self.inner, ptr);
650          }
651        }
652      }
653
654      impl<T: GarbageCollected> Drop for $name<T> {
655        fn drop(&mut self) {
656          unsafe {
657            [< cppgc__ $name __DESTRUCT >](self.inner);
658          }
659        }
660      }
661
662      impl<T: GarbageCollected> std::fmt::Debug for $name<T> {
663        fn fmt(&self, fmt: &mut std::fmt::Formatter) -> std::fmt::Result {
664          fmt.debug_struct(stringify!($name)).finish()
665        }
666      }
667
668      impl<T: GarbageCollected> GetRustObj<T> for $name<T> {
669        fn get_rust_obj(&self) -> *mut RustObj {
670          unsafe {
671            [< cppgc__ $name __Get >](self.inner)
672          }
673        }
674      }
675    }
676  };
677}
678
679persistent! {
680  /// Persistent is a way to create a strong pointer from an off-heap object to
681  /// another on-heap object. As long as the Persistent handle is alive the GC will
682  /// keep the object pointed to alive. The Persistent handle is always a GC root
683  /// from the point of view of the GC. Persistent must be constructed and
684  /// destructed in the same thread.
685  Persistent
686}
687
688persistent! {
689  /// WeakPersistent is a way to create a weak pointer from an off-heap object to
690  /// an on-heap object. The pointer is automatically cleared when the pointee gets
691  /// collected. WeakPersistent must be constructed and destructed in the same
692  /// thread.
693  WeakPersistent
694}
695
696/// `GcCell` is a zero-overhead memory cell that provides interior mutability
697/// for garbage collected types.
698///
699/// Mutable access to the value inside the `GcCell` is granted by delivering
700/// proof that the caller has mutable access to the V8 Isolate (such as through
701/// a [`HandleScope`](crate::HandleScope)). This statically guarantees that
702/// access patterns follow the rules of both the garbage collector and the Rust
703/// borrow checker.
704///
705/// `GcCell` also implements [`Traced`], which means that it can hold other
706/// garbage collected references, like [`Member`] or [`WeakMember`]. If the
707/// `GcCell` holds other garbage collected objects, those objects cannot be
708/// accessed while the `GcCell` is borrowed, and the caller must construct
709/// temporary pointers ([`UnsafePtr<T>`]) on the stack in order to access nested
710/// objects in the object graph.
711pub struct GcCell<T> {
712  // Contents guarded by access to the `Isolate`.
713  value: UnsafeCell<T>,
714}
715
716impl<T> std::fmt::Debug for GcCell<T> {
717  fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
718    f.debug_struct("GcCell").finish()
719  }
720}
721
722unsafe impl<T: Send> Send for GcCell<T> {}
723unsafe impl<T: Sync> Sync for GcCell<T> {}
724
725impl<T> GcCell<T> {
726  pub fn new(value: T) -> Self {
727    Self {
728      value: UnsafeCell::new(value),
729    }
730  }
731
732  pub fn set(&self, isolate: &mut crate::Isolate, value: T) {
733    _ = isolate;
734    unsafe {
735      // SAFETY: The `isolate` argument is proof that we have mutable access to
736      // the value.
737      *self.value.get() = value;
738    }
739  }
740}
741
742impl<T> GcCell<T> {
743  pub fn get<'a>(&'a self, isolate: &'a crate::Isolate) -> &'a T {
744    _ = isolate;
745    unsafe {
746      // SAFETY: The `isolate` argument is proof that we have access to the
747      // value, and the returned reference binds the isolate's lifetime.
748      &*self.value.get()
749    }
750  }
751
752  pub fn get_mut<'a>(&'a self, isolate: &'a mut crate::Isolate) -> &'a mut T {
753    _ = isolate;
754    unsafe {
755      // SAFETY: The `isolate` argument is proof that we have mutable access to
756      // the value, and the returned reference binds the isolate's lifetime.
757      &mut *self.value.get()
758    }
759  }
760
761  pub fn with<'a, 's, R>(
762    &'a self,
763    scope: &'a mut crate::HandleScope<'s>,
764    f: impl FnOnce(&'a mut crate::HandleScope<'s>, &'a mut T) -> R,
765  ) -> R {
766    f(scope, unsafe { &mut *self.value.get() })
767  }
768}
769
770impl<T: Traced> Traced for GcCell<T> {
771  fn trace(&self, visitor: &mut Visitor) {
772    unsafe {
773      // SAFETY: This is invoked by the GC, so access is guaranteed to follow
774      // its rules.
775      visitor.trace(&(*self.value.get()));
776    }
777  }
778}