/src/node/src/cppgc_helpers.h
Line  | Count  | Source  | 
1  |  | #ifndef SRC_CPPGC_HELPERS_H_  | 
2  |  | #define SRC_CPPGC_HELPERS_H_  | 
3  |  |  | 
4  |  | #if defined(NODE_WANT_INTERNALS) && NODE_WANT_INTERNALS  | 
5  |  |  | 
6  |  | #include <type_traits>  // std::remove_reference  | 
7  |  | #include "cppgc/garbage-collected.h"  | 
8  |  | #include "cppgc/name-provider.h"  | 
9  |  | #include "cppgc/persistent.h"  | 
10  |  | #include "memory_tracker.h"  | 
11  |  | #include "util.h"  | 
12  |  | #include "v8-cppgc.h"  | 
13  |  | #include "v8-sandbox.h"  | 
14  |  | #include "v8.h"  | 
15  |  |  | 
16  |  | namespace node { | 
17  |  |  | 
18  |  | class Environment;  | 
19  |  | class Realm;  | 
20  |  | class CppgcWrapperListNode;  | 
21  |  |  | 
22  |  | /**  | 
23  |  |  * This is a helper mixin with a BaseObject-like interface to help  | 
24  |  |  * implementing wrapper objects managed by V8's cppgc (Oilpan) library.  | 
25  |  |  * cppgc-manged objects in Node.js internals should extend this mixin,  | 
26  |  |  * while non-cppgc-managed objects typically extend BaseObject - the  | 
27  |  |  * latter are being migrated to be cppgc-managed wherever it's beneficial  | 
28  |  |  * and practical. Typically cppgc-managed objects are more efficient to  | 
29  |  |  * keep track of (which lowers initialization cost) and work better  | 
30  |  |  * with V8's GC scheduling.  | 
31  |  |  *  | 
32  |  |  * A cppgc-managed native wrapper should look something like this, note  | 
33  |  |  * that per cppgc rules, CPPGC_MIXIN(MyWrap) must be at the left-most  | 
34  |  |  * position in the hierarchy (which ensures cppgc::GarbageCollected  | 
35  |  |  * is at the left-most position).  | 
36  |  |  *  | 
37  |  |  * class MyWrap final : CPPGC_MIXIN(MyWrap) { | 
38  |  |  *  public:  | 
39  |  |  *   SET_CPPGC_NAME(MyWrap)  // Sets the heap snapshot name to "Node / MyWrap"  | 
40  |  |  *   void Trace(cppgc::Visitor* visitor) const final { | 
41  |  |  *     CppgcMixin::Trace(visitor);  | 
42  |  |  *     visitor->Trace(...);  // Trace any additional owned traceable data  | 
43  |  |  *   }  | 
44  |  |  * }  | 
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  |  |  *   }  | 
54  |  |  */  | 
55  |  | class CppgcMixin : public cppgc::GarbageCollectedMixin, public MemoryRetainer { | 
56  |  |  public:  | 
57  |  |   // To help various callbacks access wrapper objects with different memory  | 
58  |  |   // management, cppgc-managed objects share the same layout as BaseObjects.  | 
59  |  |   enum InternalFields { kEmbedderType = 0, kSlot, kInternalFieldCount }; | 
60  |  |  | 
61  |  |   // The initialization cannot be done in the mixin constructor but has to be  | 
62  |  |   // invoked from the child class constructor, per cppgc::GarbageCollectedMixin  | 
63  |  |   // rules.  | 
64  |  |   template <typename T>  | 
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);  | 
68  |  |  | 
69  |  |   inline v8::Local<v8::Object> object() const;  | 
70  |  |   inline Environment* env() const;  | 
71  | 0  |   inline Realm* realm() const { return realm_; } | 
72  | 0  |   inline v8::Local<v8::Object> object(v8::Isolate* isolate) const { | 
73  | 0  |     return traced_reference_.Get(isolate);  | 
74  | 0  |   }  | 
75  |  |  | 
76  |  |   template <typename T>  | 
77  |  |   static inline T* Unwrap(v8::Local<v8::Object> obj);  | 
78  |  |  | 
79  |  |   // Subclasses are expected to invoke CppgcMixin::Trace() in their own Trace()  | 
80  |  |   // methods.  | 
81  | 0  |   void Trace(cppgc::Visitor* visitor) const override { | 
82  | 0  |     visitor->Trace(traced_reference_);  | 
83  | 0  |   }  | 
84  |  |  | 
85  |  |   // TODO(joyeecheung): use ObjectSizeTrait;  | 
86  | 0  |   inline size_t SelfSize() const override { return sizeof(*this); } | 
87  | 0  |   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  | 0  |   void Finalize() { | 
99  | 0  |     if (realm_ == nullptr) return;  | 
100  | 0  |     this->Clean(realm_);  | 
101  | 0  |     realm_ = nullptr;  | 
102  | 0  |   }  | 
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  | 0  |   virtual void Clean(Realm* realm) {} | 
110  |  |  | 
111  |  |   inline ~CppgcMixin();  | 
112  |  |  | 
113  |  |   friend class CppgcWrapperListNode;  | 
114  |  |  | 
115  |  |  private:  | 
116  |  |   Realm* realm_ = nullptr;  | 
117  |  |   v8::TracedReference<v8::Object> traced_reference_;  | 
118  |  | };  | 
119  |  |  | 
120  |  | // If the class doesn't have additional owned traceable data, use this macro to  | 
121  |  | // save the implementation of a custom Trace() method.  | 
122  |  | #define DEFAULT_CPPGC_TRACE()                                                  \  | 
123  |  |   void Trace(cppgc::Visitor* visitor) const final {                            \ | 
124  |  |     CppgcMixin::Trace(visitor);                                                \  | 
125  |  |   }  | 
126  |  |  | 
127  |  | // This macro sets the node name in the heap snapshot with a "Node /" prefix.  | 
128  |  | // Classes that use this macro must extend cppgc::NameProvider.  | 
129  |  | #define SET_CPPGC_NAME(Klass)                                                  \  | 
130  | 0  |   inline const char* GetHumanReadableName() const final {                      \ | 
131  | 0  |     return "Node / " #Klass;                                                   \  | 
132  | 0  |   }                                                                            \ Unexecuted instantiation: node::contextify::ContextifyContext::GetHumanReadableName() const Unexecuted instantiation: node::contextify::ContextifyScript::GetHumanReadableName() const  | 
133  | 0  |   inline const char* MemoryInfoName() const override { return #Klass; }Unexecuted instantiation: node::contextify::ContextifyContext::MemoryInfoName() const Unexecuted instantiation: node::contextify::ContextifyScript::MemoryInfoName() const  | 
134  |  |  | 
135  |  | /**  | 
136  |  |  * Similar to ASSIGN_OR_RETURN_UNWRAP() but works on cppgc-managed types  | 
137  |  |  * inheriting CppgcMixin.  | 
138  |  |  */  | 
139  |  | #define ASSIGN_OR_RETURN_UNWRAP_CPPGC(ptr, obj, ...)                           \  | 
140  | 0  |   do {                                                                         \ | 
141  | 0  |     *ptr = CppgcMixin::Unwrap<                                                 \  | 
142  | 0  |         typename std::remove_reference<decltype(**ptr)>::type>(obj);           \  | 
143  | 0  |     if (*ptr == nullptr) return __VA_ARGS__;                                   \  | 
144  | 0  |   } while (0)  | 
145  |  | }  // namespace node  | 
146  |  |  | 
147  |  | /**  | 
148  |  |  * Helper macro the manage the cppgc-based wrapper hierarchy. This must  | 
149  |  |  * be used at the left-most position - right after `:` in the class inheritance,  | 
150  |  |  * like this:  | 
151  |  |  * class Klass : CPPGC_MIXIN(Klass) ... {} | 
152  |  |  *  | 
153  |  |  * This needs to disable linters because it will be at odds with  | 
154  |  |  * clang-format.  | 
155  |  |  */  | 
156  |  | #define CPPGC_MIXIN(Klass)                                                     \  | 
157  |  |   public /* NOLINT(whitespace/indent) */                                       \  | 
158  |  |   v8::Object::Wrappable, public CppgcMixin  | 
159  |  |  | 
160  |  | #endif  // defined(NODE_WANT_INTERNALS) && NODE_WANT_INTERNALS  | 
161  |  |  | 
162  |  | #endif  // SRC_CPPGC_HELPERS_H_  |