-
Notifications
You must be signed in to change notification settings - Fork 31.2k
/
Copy pathcppgc_helpers.h
137 lines (121 loc) Β· 5.53 KB
/
cppgc_helpers.h
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
#ifndef SRC_CPPGC_HELPERS_H_
#define SRC_CPPGC_HELPERS_H_
#if defined(NODE_WANT_INTERNALS) && NODE_WANT_INTERNALS
#include <type_traits> // std::remove_reference
#include "cppgc/garbage-collected.h"
#include "cppgc/name-provider.h"
#include "env.h"
#include "memory_tracker.h"
#include "v8-cppgc.h"
#include "v8-sandbox.h"
#include "v8.h"
namespace node {
/**
* This is a helper mixin with a BaseObject-like interface to help
* implementing wrapper objects managed by V8's cppgc (Oilpan) library.
* cppgc-manged objects in Node.js internals should extend this mixin,
* while non-cppgc-managed objects typically extend BaseObject - the
* latter are being migrated to be cppgc-managed wherever it's beneficial
* and practical. Typically cppgc-managed objects are more efficient to
* keep track of (which lowers initialization cost) and work better
* with V8's GC scheduling.
*
* A cppgc-managed native wrapper should look something like this, note
* that per cppgc rules, CPPGC_MIXIN(Klass) must be at the left-most
* position in the hierarchy (which ensures cppgc::GarbageCollected
* is at the left-most position).
*
* class Klass final : CPPGC_MIXIN(Klass) {
* public:
* SET_CPPGC_NAME(Klass) // Sets the heap snapshot name to "Node / Klass"
* void Trace(cppgc::Visitor* visitor) const final {
* CppgcMixin::Trace(visitor);
* visitor->Trace(...); // Trace any additional owned traceable data
* }
* }
*/
class CppgcMixin : public cppgc::GarbageCollectedMixin {
public:
// To help various callbacks access wrapper objects with different memory
// management, cppgc-managed objects share the same layout as BaseObjects.
enum InternalFields { kEmbedderType = 0, kSlot, kInternalFieldCount };
// The initialization cannot be done in the mixin constructor but has to be
// invoked from the child class constructor, per cppgc::GarbageCollectedMixin
// rules.
template <typename T>
static void Wrap(T* ptr, Environment* env, v8::Local<v8::Object> obj) {
CHECK_GE(obj->InternalFieldCount(), T::kInternalFieldCount);
ptr->env_ = env;
v8::Isolate* isolate = env->isolate();
ptr->traced_reference_ = v8::TracedReference<v8::Object>(isolate, obj);
v8::Object::Wrap<v8::CppHeapPointerTag::kDefaultTag>(isolate, obj, ptr);
// Keep the layout consistent with BaseObjects.
obj->SetAlignedPointerInInternalField(
kEmbedderType, env->isolate_data()->embedder_id_for_cppgc());
obj->SetAlignedPointerInInternalField(kSlot, ptr);
}
v8::Local<v8::Object> object() const {
return traced_reference_.Get(env_->isolate());
}
Environment* env() const { return env_; }
template <typename T>
static T* Unwrap(v8::Local<v8::Object> obj) {
// We are not using v8::Object::Unwrap currently because that requires
// access to isolate which the ASSIGN_OR_RETURN_UNWRAP macro that we'll shim
// with ASSIGN_OR_RETURN_UNWRAP_GC doesn't take, and we also want a
// signature consistent with BaseObject::Unwrap() to avoid churn. Since
// cppgc-managed objects share the same layout as BaseObjects, just unwrap
// from the pointer in the internal field, which should be valid as long as
// the object is still alive.
if (obj->InternalFieldCount() != T::kInternalFieldCount) {
return nullptr;
}
T* ptr = static_cast<T*>(obj->GetAlignedPointerFromInternalField(T::kSlot));
return ptr;
}
// Subclasses are expected to invoke CppgcMixin::Trace() in their own Trace()
// methods.
void Trace(cppgc::Visitor* visitor) const override {
visitor->Trace(traced_reference_);
}
private:
Environment* env_;
v8::TracedReference<v8::Object> traced_reference_;
};
// If the class doesn't have additional owned traceable data, use this macro to
// save the implementation of a custom Trace() method.
#define DEFAULT_CPPGC_TRACE() \
void Trace(cppgc::Visitor* visitor) const final { \
CppgcMixin::Trace(visitor); \
}
// This macro sets the node name in the heap snapshot with a "Node /" prefix.
// Classes that use this macro must extend cppgc::NameProvider.
#define SET_CPPGC_NAME(Klass) \
inline const char* GetHumanReadableName() const final { \
return "Node / " #Klass; \
}
/**
* Similar to ASSIGN_OR_RETURN_UNWRAP() but works on cppgc-managed types
* inheriting CppgcMixin.
*/
#define ASSIGN_OR_RETURN_UNWRAP_CPPGC(ptr, obj, ...) \
do { \
*ptr = CppgcMixin::Unwrap< \
typename std::remove_reference<decltype(**ptr)>::type>(obj); \
if (*ptr == nullptr) return __VA_ARGS__; \
} while (0)
} // namespace node
/**
* Helper macro the manage the cppgc-based wrapper hierarchy. This must
* be used at the left-most position - right after `:` in the class inheritance,
* like this:
* class Klass : CPPGC_MIXIN(Klass) ... {}
*
* This needs to disable linters because it will be at odds with
* clang-format.
*/
#define CPPGC_MIXIN(Klass) \
public /* NOLINT(whitespace/indent) */ \
cppgc::GarbageCollected<Klass>, public cppgc::NameProvider, public CppgcMixin
#endif // defined(NODE_WANT_INTERNALS) && NODE_WANT_INTERNALS
#endif // SRC_CPPGC_HELPERS_H_