66#include < type_traits> // std::remove_reference
77#include " cppgc/garbage-collected.h"
88#include " cppgc/name-provider.h"
9- #include " env .h"
9+ #include " cppgc/persistent .h"
1010#include " memory_tracker.h"
11+ #include " util.h"
1112#include " v8-cppgc.h"
1213#include " v8-sandbox.h"
1314#include " v8.h"
1415
1516namespace node {
1617
18+ class Environment ;
19+ class Realm ;
20+ class CppgcWrapperListNode ;
21+
1722/* *
1823 * This is a helper mixin with a BaseObject-like interface to help
1924 * implementing wrapper objects managed by V8's cppgc (Oilpan) library.
@@ -25,20 +30,29 @@ namespace node {
2530 * with V8's GC scheduling.
2631 *
2732 * A cppgc-managed native wrapper should look something like this, note
28- * that per cppgc rules, CPPGC_MIXIN(Klass ) must be at the left-most
33+ * that per cppgc rules, CPPGC_MIXIN(MyWrap ) must be at the left-most
2934 * position in the hierarchy (which ensures cppgc::GarbageCollected
3035 * is at the left-most position).
3136 *
32- * class Klass final : CPPGC_MIXIN(Klass ) {
37+ * class MyWrap final : CPPGC_MIXIN(MyWrap ) {
3338 * public:
34- * SET_CPPGC_NAME(Klass ) // Sets the heap snapshot name to "Node / Klass "
39+ * SET_CPPGC_NAME(MyWrap ) // Sets the heap snapshot name to "Node / MyWrap "
3540 * void Trace(cppgc::Visitor* visitor) const final {
3641 * CppgcMixin::Trace(visitor);
3742 * visitor->Trace(...); // Trace any additional owned traceable data
3843 * }
3944 * }
45+ *
46+ * If the wrapper needs to perform cleanups when it's destroyed and that
47+ * cleanup relies on a living Node.js `Realm`, it should implement a
48+ * pattern like this:
49+ *
50+ * ~MyWrap() { this->Destroy(); }
51+ * void Clean(Realm* env) override {
52+ * // Do cleanup that relies on a living Environemnt.
53+ * }
4054 */
41- class CppgcMixin : public cppgc ::GarbageCollectedMixin {
55+ class CppgcMixin : public cppgc ::GarbageCollectedMixin, public MemoryRetainer {
4256 public:
4357 // To help various callbacks access wrapper objects with different memory
4458 // management, cppgc-managed objects share the same layout as BaseObjects.
@@ -48,48 +62,58 @@ class CppgcMixin : public cppgc::GarbageCollectedMixin {
4862 // invoked from the child class constructor, per cppgc::GarbageCollectedMixin
4963 // rules.
5064 template <typename T>
51- static void Wrap (T* ptr, Environment* env, v8::Local<v8::Object> obj) {
52- CHECK_GE (obj->InternalFieldCount (), T::kInternalFieldCount );
53- ptr->env_ = env;
54- v8::Isolate* isolate = env->isolate ();
55- ptr->traced_reference_ = v8::TracedReference<v8::Object>(isolate, obj);
56- v8::Object::Wrap<v8::CppHeapPointerTag::kDefaultTag >(isolate, obj, ptr);
57- // Keep the layout consistent with BaseObjects.
58- obj->SetAlignedPointerInInternalField (
59- kEmbedderType , env->isolate_data ()->embedder_id_for_cppgc ());
60- obj->SetAlignedPointerInInternalField (kSlot , ptr);
61- }
65+ static inline void Wrap (T* ptr, Realm* realm, v8::Local<v8::Object> obj);
66+ template <typename T>
67+ static inline void Wrap (T* ptr, Environment* env, v8::Local<v8::Object> obj);
6268
63- v8::Local<v8::Object> object () const {
64- return traced_reference_.Get (env_->isolate ());
69+ inline v8::Local<v8::Object> object () const ;
70+ inline Environment* env () const ;
71+ inline Realm* realm () const { return realm_; }
72+ inline v8::Local<v8::Object> object (v8::Isolate* isolate) const {
73+ return traced_reference_.Get (isolate);
6574 }
6675
67- Environment* env () const { return env_; }
68-
6976 template <typename T>
70- static T* Unwrap (v8::Local<v8::Object> obj) {
71- // We are not using v8::Object::Unwrap currently because that requires
72- // access to isolate which the ASSIGN_OR_RETURN_UNWRAP macro that we'll shim
73- // with ASSIGN_OR_RETURN_UNWRAP_GC doesn't take, and we also want a
74- // signature consistent with BaseObject::Unwrap() to avoid churn. Since
75- // cppgc-managed objects share the same layout as BaseObjects, just unwrap
76- // from the pointer in the internal field, which should be valid as long as
77- // the object is still alive.
78- if (obj->InternalFieldCount () != T::kInternalFieldCount ) {
79- return nullptr ;
80- }
81- T* ptr = static_cast <T*>(obj->GetAlignedPointerFromInternalField (T::kSlot ));
82- return ptr;
83- }
77+ static inline T* Unwrap (v8::Local<v8::Object> obj);
8478
8579 // Subclasses are expected to invoke CppgcMixin::Trace() in their own Trace()
8680 // methods.
8781 void Trace (cppgc::Visitor* visitor) const override {
8882 visitor->Trace (traced_reference_);
8983 }
9084
85+ // TODO(joyeecheung): use ObjectSizeTrait;
86+ inline size_t SelfSize () const override { return sizeof (*this ); }
87+ inline bool IsCppgcWrapper () const override { return true ; }
88+
89+ // This is run for all the remaining Cppgc wrappers tracked in the Realm
90+ // during Realm shutdown. The destruction of the wrappers would happen later,
91+ // when the final garbage collection is triggered when CppHeap is torn down as
92+ // part of the Isolate teardown. If subclasses of CppgcMixin wish to perform
93+ // cleanups that depend on the Realm during destruction, they should implment
94+ // it in a Clean() override, and then call this->Finalize() from their
95+ // destructor. Outside of Finalize(), subclasses should avoid calling
96+ // into JavaScript or perform any operation that can trigger garbage
97+ // collection during the destruction.
98+ void Finalize () {
99+ if (realm_ == nullptr ) return ;
100+ this ->Clean (realm_);
101+ realm_ = nullptr ;
102+ }
103+
104+ // The default implementation of Clean() is a no-op. If subclasses wish
105+ // to perform cleanup that require a living Realm, they should
106+ // should put the cleanups in a Clean() override, and call this->Finalize()
107+ // in the destructor, instead of doing those cleanups directly in the
108+ // destructor.
109+ virtual void Clean (Realm* realm) {}
110+
111+ inline ~CppgcMixin ();
112+
113+ friend class CppgcWrapperListNode ;
114+
91115 private:
92- Environment* env_ ;
116+ Realm* realm_ = nullptr ;
93117 v8::TracedReference<v8::Object> traced_reference_;
94118};
95119
@@ -105,7 +129,8 @@ class CppgcMixin : public cppgc::GarbageCollectedMixin {
105129#define SET_CPPGC_NAME (Klass ) \
106130 inline const char * GetHumanReadableName () const final { \
107131 return " Node / " #Klass; \
108- }
132+ } \
133+ inline const char * MemoryInfoName () const override { return #Klass; }
109134
110135/* *
111136 * Similar to ASSIGN_OR_RETURN_UNWRAP() but works on cppgc-managed types
0 commit comments