Skip to content

Commit 0c7e2a5

Browse files
committed
src: add utilities for cppgc classes
1 parent 9e83853 commit 0c7e2a5

File tree

4 files changed

+131
-9
lines changed

4 files changed

+131
-9
lines changed

node.gyp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -202,6 +202,7 @@
202202
'src/compile_cache.h',
203203
'src/connect_wrap.h',
204204
'src/connection_wrap.h',
205+
'src/cppgc_helpers.h',
205206
'src/dataqueue/queue.h',
206207
'src/debug_utils.h',
207208
'src/debug_utils-inl.h',

src/cppgc_helpers.h

Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
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 "env.h"
10+
#include "memory_tracker.h"
11+
#include "v8-cppgc.h"
12+
#include "v8.h"
13+
14+
namespace node {
15+
16+
#define ASSIGN_OR_RETURN_UNWRAP_CPPGC(ptr, obj, ...) \
17+
do { \
18+
*ptr = CppgcMixin::Unwrap< \
19+
typename std::remove_reference<decltype(**ptr)>::type>(obj); \
20+
if (*ptr == nullptr) return __VA_ARGS__; \
21+
} while (0)
22+
23+
// TODO(joyeecheung): make it a class template?
24+
class CppgcMixin : public cppgc::GarbageCollectedMixin {
25+
public:
26+
enum InternalFields { kEmbedderType = 0, kSlot, kInternalFieldCount };
27+
28+
// This must not be a constructor but called in the child class constructor,
29+
// per cppgc::GarbageCollectedMixin rules.
30+
template <typename T>
31+
void InitializeCppgc(T* ptr, Environment* env, v8::Local<v8::Object> obj) {
32+
env_ = env;
33+
traced_reference_ = v8::TracedReference<v8::Object>(env->isolate(), obj);
34+
SetCppgcReference(env->isolate(), obj, ptr);
35+
// We are adding this additional slot to that BaseObject and cppgc-managed
36+
// objects share the same layout.
37+
CHECK_GE(obj->InternalFieldCount(), T::kInternalFieldCount);
38+
obj->SetAlignedPointerInInternalField(T::kSlot, ptr);
39+
}
40+
41+
v8::Local<v8::Object> object() const {
42+
return traced_reference_.Get(env_->isolate());
43+
}
44+
45+
Environment* env() const { return env_; }
46+
47+
template <typename T>
48+
static T* Unwrap(v8::Local<v8::Object> obj) {
49+
// We are not using v8::Object::Unwrap currently because that requires
50+
// access to isolate which the ASSIGN_OR_RETURN_UNWRAP macro that we'll shim
51+
// with cppgc-allocated types doesn't take.
52+
if (obj->InternalFieldCount() != T::kInternalFieldCount) {
53+
return nullptr;
54+
}
55+
T* ptr = static_cast<T*>(obj->GetAlignedPointerFromInternalField(T::kSlot));
56+
return ptr;
57+
}
58+
59+
void Trace(cppgc::Visitor* visitor) const override {
60+
visitor->Trace(traced_reference_);
61+
}
62+
63+
private:
64+
Environment* env_;
65+
v8::TracedReference<v8::Object> traced_reference_;
66+
};
67+
68+
#define DEFAULT_CPPGC_TRACE() \
69+
void Trace(cppgc::Visitor* visitor) const final { \
70+
CppgcMixin::Trace(visitor); \
71+
}
72+
73+
#define SET_CPPGC_NAME(Klass) \
74+
inline const char* GetHumanReadableName() const final { \
75+
return "Node / " #Klass; \
76+
}
77+
} // namespace node
78+
79+
#endif // defined(NODE_WANT_INTERNALS) && NODE_WANT_INTERNALS
80+
81+
#endif // SRC_BASE_OBJECT_H_

src/heap_utils.cc

Lines changed: 25 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
using v8::Array;
2222
using v8::Boolean;
2323
using v8::Context;
24+
using v8::Data;
2425
using v8::EmbedderGraph;
2526
using v8::EscapableHandleScope;
2627
using v8::FunctionCallbackInfo;
@@ -50,18 +51,20 @@ class JSGraphJSNode : public EmbedderGraph::Node {
5051
const char* Name() override { return "<JS Node>"; }
5152
size_t SizeInBytes() override { return 0; }
5253
bool IsEmbedderNode() override { return false; }
53-
Local<Value> JSValue() { return PersistentToLocal::Strong(persistent_); }
54+
Local<Data> V8Value() { return PersistentToLocal::Strong(persistent_); }
5455

5556
int IdentityHash() {
56-
Local<Value> v = JSValue();
57+
Local<Data> d = V8Value();
58+
// TODO(joyeecheung): return something better?
59+
if (!d->IsValue()) return reinterpret_cast<std::uintptr_t>(this);
60+
Local<Value> v = d.As<Value>();
5761
if (v->IsObject()) return v.As<Object>()->GetIdentityHash();
5862
if (v->IsName()) return v.As<v8::Name>()->GetIdentityHash();
5963
if (v->IsInt32()) return v.As<v8::Int32>()->Value();
6064
return 0;
6165
}
6266

63-
JSGraphJSNode(Isolate* isolate, Local<Value> val)
64-
: persistent_(isolate, val) {
67+
JSGraphJSNode(Isolate* isolate, Local<Data> val) : persistent_(isolate, val) {
6568
CHECK(!val.IsEmpty());
6669
}
6770

@@ -73,19 +76,27 @@ class JSGraphJSNode : public EmbedderGraph::Node {
7376

7477
struct Equal {
7578
inline bool operator()(JSGraphJSNode* a, JSGraphJSNode* b) const {
76-
return a->JSValue()->SameValue(b->JSValue());
79+
Local<Data> data_a = a->V8Value();
80+
Local<Data> data_b = a->V8Value();
81+
if (data_a->IsValue()) {
82+
if (!data_b->IsValue()) {
83+
return false;
84+
}
85+
return data_a.As<Value>()->SameValue(data_b.As<Value>());
86+
}
87+
return data_a == data_b;
7788
}
7889
};
7990

8091
private:
81-
Global<Value> persistent_;
92+
Global<Data> persistent_;
8293
};
8394

8495
class JSGraph : public EmbedderGraph {
8596
public:
8697
explicit JSGraph(Isolate* isolate) : isolate_(isolate) {}
8798

88-
Node* V8Node(const Local<Value>& value) override {
99+
Node* V8Node(const Local<v8::Data>& value) override {
89100
std::unique_ptr<JSGraphJSNode> n { new JSGraphJSNode(isolate_, value) };
90101
auto it = engine_nodes_.find(n.get());
91102
if (it != engine_nodes_.end())
@@ -94,6 +105,10 @@ class JSGraph : public EmbedderGraph {
94105
return AddNode(std::unique_ptr<Node>(n.release()));
95106
}
96107

108+
Node* V8Node(const Local<v8::Value>& value) override {
109+
return V8Node(value.As<v8::Data>());
110+
}
111+
97112
Node* AddNode(std::unique_ptr<Node> node) override {
98113
Node* n = node.get();
99114
nodes_.emplace(std::move(node));
@@ -154,8 +169,9 @@ class JSGraph : public EmbedderGraph {
154169
if (nodes->Set(context, i++, obj).IsNothing())
155170
return MaybeLocal<Array>();
156171
if (!n->IsEmbedderNode()) {
157-
value = static_cast<JSGraphJSNode*>(n.get())->JSValue();
158-
if (obj->Set(context, value_string, value).IsNothing())
172+
Local<Data> data = static_cast<JSGraphJSNode*>(n.get())->V8Value();
173+
if (data->IsValue() &&
174+
obj->Set(context, value_string, data.As<Value>()).IsNothing())
159175
return MaybeLocal<Array>();
160176
}
161177
}

src/util.h

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -970,6 +970,30 @@ void SetConstructorFunction(v8::Isolate* isolate,
970970
SetConstructorFunctionFlag flag =
971971
SetConstructorFunctionFlag::SET_CLASS_NAME);
972972

973+
#define GET_OR_SET_CONSTRUCTOR_TEMPLATE(tmpl, isolate_data, id, name) \
974+
do { \
975+
v8::Isolate* isolate = isolate_data->isolate(); \
976+
tmpl = isolate_data->id##_constructor_template(); \
977+
if (tmpl.IsEmpty()) { \
978+
tmpl = v8::FunctionTemplate::New(isolate, New); \
979+
tmpl->InstanceTemplate()->SetInternalFieldCount(kInternalFieldCount); \
980+
tmpl->SetClassName(FIXED_ONE_BYTE_STRING(isolate, #name)); \
981+
isolate_data->set_##id##_constructor_template(tmpl); \
982+
} \
983+
} while (0);
984+
985+
#define CONSTRUCTOR_TEMPLATE_GENERATOR(id, name) \
986+
static v8::Local<v8::FunctionTemplate> GetConstructorTemplate( \
987+
IsolateData* isolate_data) { \
988+
v8::Local<v8::FunctionTemplate> tmpl; \
989+
GET_OR_SET_CONSTRUCTOR_TEMPLATE(tmpl, isolate_data, id, name); \
990+
return tmpl; \
991+
}
992+
993+
#define DECL_CONSTRUCTOR_TEMPLATE_GENERATOR() \
994+
static v8::Local<v8::FunctionTemplate> GetConstructorTemplate( \
995+
IsolateData* isolate_data);
996+
973997
// Like RAIIIsolate, except doesn't enter the isolate while it's in scope.
974998
class RAIIIsolateWithoutEntering {
975999
public:

0 commit comments

Comments
 (0)