66#include < type_traits> // std::remove_reference
77#include " cppgc/garbage-collected.h"
88#include " cppgc/name-provider.h"
9- #include " env.h"
109#include " memory_tracker.h"
10+ #include " util.h"
1111#include " v8-cppgc.h"
1212#include " v8-sandbox.h"
1313#include " v8.h"
1414
1515namespace node {
1616
17+ class Environment ;
18+ class Realm ;
19+ class CppgcWrapperList ;
20+
1721/* *
1822 * This is a helper mixin with a BaseObject-like interface to help
1923 * implementing wrapper objects managed by V8's cppgc (Oilpan) library.
@@ -25,20 +29,29 @@ namespace node {
2529 * with V8's GC scheduling.
2630 *
2731 * 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
32+ * that per cppgc rules, CPPGC_MIXIN(MyWrap ) must be at the left-most
2933 * position in the hierarchy (which ensures cppgc::GarbageCollected
3034 * is at the left-most position).
3135 *
32- * class Klass final : CPPGC_MIXIN(Klass ) {
36+ * class MyWrap final : CPPGC_MIXIN(MyWrap ) {
3337 * public:
34- * SET_CPPGC_NAME(Klass ) // Sets the heap snapshot name to "Node / Klass "
38+ * SET_CPPGC_NAME(MyWrap ) // Sets the heap snapshot name to "Node / MyWrap "
3539 * void Trace(cppgc::Visitor* visitor) const final {
3640 * CppgcMixin::Trace(visitor);
3741 * visitor->Trace(...); // Trace any additional owned traceable data
3842 * }
3943 * }
44+ *
45+ * If the wrapper needs to perform cleanups when it's destroyed and that
46+ * cleanup relies on a living Node.js `Realm`, it should implement a
47+ * pattern like this:
48+ *
49+ * ~MyWrap() { this->Destroy(); }
50+ * void Clean(Realm* env) override {
51+ * // Do cleanup that relies on a living Environemnt.
52+ * }
4053 */
41- class CppgcMixin : public cppgc ::GarbageCollectedMixin {
54+ class CppgcMixin : public cppgc ::GarbageCollectedMixin, public MemoryRetainer {
4255 public:
4356 // To help various callbacks access wrapper objects with different memory
4457 // management, cppgc-managed objects share the same layout as BaseObjects.
@@ -48,49 +61,56 @@ class CppgcMixin : public cppgc::GarbageCollectedMixin {
4861 // invoked from the child class constructor, per cppgc::GarbageCollectedMixin
4962 // rules.
5063 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- }
64+ static inline void Wrap (T* ptr, Realm* realm, v8::Local<v8::Object> obj);
65+ template <typename T>
66+ static inline void Wrap (T* ptr, Environment* env, v8::Local<v8::Object> obj);
6267
63- v8::Local<v8::Object> object () const {
64- return traced_reference_.Get (env_->isolate ());
68+ inline v8::Local<v8::Object> object () const ;
69+ inline Environment* env () const ;
70+ inline Realm* realm () const { return realm_; }
71+ inline v8::Local<v8::Object> object (v8::Isolate* isolate) const {
72+ return traced_reference_.Get (isolate);
6573 }
6674
67- Environment* env () const { return env_; }
68-
6975 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- }
76+ static inline T* Unwrap (v8::Local<v8::Object> obj);
8477
8578 // Subclasses are expected to invoke CppgcMixin::Trace() in their own Trace()
8679 // methods.
8780 void Trace (cppgc::Visitor* visitor) const override {
8881 visitor->Trace (traced_reference_);
8982 }
9083
84+ // TODO(joyeecheung): use ObjectSizeTrait;
85+ inline size_t SelfSize () const override { return sizeof (*this ); }
86+ inline bool IsCppgcWrapper () const override { return true ; }
87+
88+ // This is run for all the remaining Cppgc wrappers tracked in the Realm
89+ // during Realm shutdown. The destruction of the wrappers would happen later,
90+ // when the final garbage collection is triggered when CppHeap is torn down as
91+ // part of the Isolate teardown. If subclasses of CppgcMixin wish to perform
92+ // cleanups that depend on the Realm during destruction, they should implment
93+ // it in a Clean() override, and then call this->Finalize() from their
94+ // destructor. Outside of Finalize(), subclasses should avoid calling
95+ // into JavaScript or perform any operation that can trigger garbage
96+ // collection during the destruction.
97+ void Finalize () {
98+ if (realm_ == nullptr ) return ;
99+ this ->Clean (realm_);
100+ realm_ = nullptr ;
101+ }
102+
103+ // The default implementation of Clean() is a no-op. Subclasses
104+ // should override it to perform cleanup that require a living Realm,
105+ // instead of doing these cleanups directly in the destructor.
106+ virtual void Clean (Realm* realm) {}
107+
108+ friend class CppgcWrapperList ;
109+
91110 private:
92- Environment* env_ ;
111+ Realm* realm_ = nullptr ;
93112 v8::TracedReference<v8::Object> traced_reference_;
113+ ListNode<CppgcMixin> wrapper_list_node_;
94114};
95115
96116// If the class doesn't have additional owned traceable data, use this macro to
@@ -105,7 +125,8 @@ class CppgcMixin : public cppgc::GarbageCollectedMixin {
105125#define SET_CPPGC_NAME (Klass ) \
106126 inline const char * GetHumanReadableName () const final { \
107127 return " Node / " #Klass; \
108- }
128+ } \
129+ inline const char * MemoryInfoName () const override { return #Klass; }
109130
110131/* *
111132 * Similar to ASSIGN_OR_RETURN_UNWRAP() but works on cppgc-managed types
0 commit comments