6
6
#include < type_traits> // std::remove_reference
7
7
#include " cppgc/garbage-collected.h"
8
8
#include " cppgc/name-provider.h"
9
- #include " env .h"
9
+ #include " cppgc/persistent .h"
10
10
#include " memory_tracker.h"
11
+ #include " util.h"
11
12
#include " v8-cppgc.h"
12
13
#include " v8-sandbox.h"
13
14
#include " v8.h"
14
15
15
16
namespace node {
16
17
18
+ class Environment ;
19
+ class Realm ;
20
+ class CppgcWrapperListNode ;
21
+
17
22
/* *
18
23
* This is a helper mixin with a BaseObject-like interface to help
19
24
* implementing wrapper objects managed by V8's cppgc (Oilpan) library.
@@ -25,20 +30,29 @@ namespace node {
25
30
* with V8's GC scheduling.
26
31
*
27
32
* 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
29
34
* position in the hierarchy (which ensures cppgc::GarbageCollected
30
35
* is at the left-most position).
31
36
*
32
- * class Klass final : CPPGC_MIXIN(Klass ) {
37
+ * class MyWrap final : CPPGC_MIXIN(MyWrap ) {
33
38
* 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 "
35
40
* void Trace(cppgc::Visitor* visitor) const final {
36
41
* CppgcMixin::Trace(visitor);
37
42
* visitor->Trace(...); // Trace any additional owned traceable data
38
43
* }
39
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
+ * }
40
54
*/
41
- class CppgcMixin : public cppgc ::GarbageCollectedMixin {
55
+ class CppgcMixin : public cppgc ::GarbageCollectedMixin, public MemoryRetainer {
42
56
public:
43
57
// To help various callbacks access wrapper objects with different memory
44
58
// management, cppgc-managed objects share the same layout as BaseObjects.
@@ -48,48 +62,58 @@ class CppgcMixin : public cppgc::GarbageCollectedMixin {
48
62
// invoked from the child class constructor, per cppgc::GarbageCollectedMixin
49
63
// rules.
50
64
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);
62
68
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);
65
74
}
66
75
67
- Environment* env () const { return env_; }
68
-
69
76
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);
84
78
85
79
// Subclasses are expected to invoke CppgcMixin::Trace() in their own Trace()
86
80
// methods.
87
81
void Trace (cppgc::Visitor* visitor) const override {
88
82
visitor->Trace (traced_reference_);
89
83
}
90
84
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
+
91
115
private:
92
- Environment* env_ ;
116
+ Realm* realm_ = nullptr ;
93
117
v8::TracedReference<v8::Object> traced_reference_;
94
118
};
95
119
@@ -105,7 +129,8 @@ class CppgcMixin : public cppgc::GarbageCollectedMixin {
105
129
#define SET_CPPGC_NAME (Klass ) \
106
130
inline const char * GetHumanReadableName () const final { \
107
131
return " Node / " #Klass; \
108
- }
132
+ } \
133
+ inline const char * MemoryInfoName () const override { return #Klass; }
109
134
110
135
/* *
111
136
* Similar to ASSIGN_OR_RETURN_UNWRAP() but works on cppgc-managed types
0 commit comments