Skip to content

Commit 28b6b67

Browse files
committed
src: use cppgc to manage ContextifyContext
1 parent 988519d commit 28b6b67

File tree

2 files changed

+104
-46
lines changed

2 files changed

+104
-46
lines changed

src/node_contextify.cc

Lines changed: 40 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -118,8 +118,9 @@ Local<Name> Uint32ToName(Local<Context> context, uint32_t index) {
118118

119119
} // anonymous namespace
120120

121-
BaseObjectPtr<ContextifyContext> ContextifyContext::New(
122-
Environment* env, Local<Object> sandbox_obj, ContextOptions* options) {
121+
ContextifyContext* ContextifyContext::New(Environment* env,
122+
Local<Object> sandbox_obj,
123+
ContextOptions* options) {
123124
Local<ObjectTemplate> object_template;
124125
HandleScope scope(env->isolate());
125126
CHECK_IMPLIES(sandbox_obj.IsEmpty(), options->vanilla);
@@ -140,43 +141,46 @@ BaseObjectPtr<ContextifyContext> ContextifyContext::New(
140141
if (!(CreateV8Context(env->isolate(), object_template, snapshot_data, queue)
141142
.ToLocal(&v8_context))) {
142143
// Allocation failure, maximum call stack size reached, termination, etc.
143-
return BaseObjectPtr<ContextifyContext>();
144+
return nullptr;
144145
}
145146
return New(v8_context, env, sandbox_obj, options);
146147
}
147148

148-
void ContextifyContext::MemoryInfo(MemoryTracker* tracker) const {}
149+
void ContextifyContext::Trace(cppgc::Visitor* visitor) const {
150+
CppgcMixin::Trace(visitor);
151+
visitor->Trace(context_);
152+
}
149153

150154
ContextifyContext::ContextifyContext(Environment* env,
151155
Local<Object> wrapper,
152156
Local<Context> v8_context,
153157
ContextOptions* options)
154-
: BaseObject(env, wrapper),
155-
microtask_queue_(options->own_microtask_queue
158+
: microtask_queue_(options->own_microtask_queue
156159
? options->own_microtask_queue.release()
157160
: nullptr) {
161+
CppgcMixin::Wrap(this, env, wrapper);
162+
158163
context_.Reset(env->isolate(), v8_context);
159164
// This should only be done after the initial initializations of the context
160165
// global object is finished.
161166
DCHECK_NULL(v8_context->GetAlignedPointerFromEmbedderData(
162167
ContextEmbedderIndex::kContextifyContext));
163168
v8_context->SetAlignedPointerInEmbedderData(
164169
ContextEmbedderIndex::kContextifyContext, this);
165-
// It's okay to make this reference weak - V8 would create an internal
166-
// reference to this context via the constructor of the wrapper.
167-
// As long as the wrapper is alive, it's constructor is alive, and so
168-
// is the context.
169-
context_.SetWeak();
170170
}
171171

172-
ContextifyContext::~ContextifyContext() {
173-
Isolate* isolate = env()->isolate();
172+
void ContextifyContext::CleanEnvResource(Environment* env) {
173+
Isolate* isolate = env->isolate();
174174
HandleScope scope(isolate);
175175

176-
env()->UnassignFromContext(PersistentToLocal::Weak(isolate, context_));
176+
env->UntrackContext(context());
177177
context_.Reset();
178178
}
179179

180+
ContextifyContext::~ContextifyContext() {
181+
Clean();
182+
}
183+
180184
void ContextifyContext::InitializeGlobalTemplates(IsolateData* isolate_data) {
181185
DCHECK(isolate_data->contextify_wrapper_template().IsEmpty());
182186
Local<FunctionTemplate> global_func_template =
@@ -251,19 +255,18 @@ MaybeLocal<Context> ContextifyContext::CreateV8Context(
251255
return scope.Escape(ctx);
252256
}
253257

254-
BaseObjectPtr<ContextifyContext> ContextifyContext::New(
255-
Local<Context> v8_context,
256-
Environment* env,
257-
Local<Object> sandbox_obj,
258-
ContextOptions* options) {
258+
ContextifyContext* ContextifyContext::New(Local<Context> v8_context,
259+
Environment* env,
260+
Local<Object> sandbox_obj,
261+
ContextOptions* options) {
259262
HandleScope scope(env->isolate());
260263
CHECK_IMPLIES(sandbox_obj.IsEmpty(), options->vanilla);
261264
// This only initializes part of the context. The primordials are
262265
// only initialized when needed because even deserializing them slows
263266
// things down significantly and they are only needed in rare occasions
264267
// in the vm contexts.
265268
if (InitializeContextRuntime(v8_context).IsNothing()) {
266-
return BaseObjectPtr<ContextifyContext>();
269+
return nullptr;
267270
}
268271

269272
Local<Context> main_context = env->context();
@@ -300,7 +303,7 @@ BaseObjectPtr<ContextifyContext> ContextifyContext::New(
300303
info.origin = *origin_val;
301304
}
302305

303-
BaseObjectPtr<ContextifyContext> result;
306+
ContextifyContext* result;
304307
Local<Object> wrapper;
305308
{
306309
Context::Scope context_scope(v8_context);
@@ -315,7 +318,7 @@ BaseObjectPtr<ContextifyContext> ContextifyContext::New(
315318
ctor_name,
316319
static_cast<v8::PropertyAttribute>(v8::DontEnum))
317320
.IsNothing()) {
318-
return BaseObjectPtr<ContextifyContext>();
321+
return nullptr;
319322
}
320323
}
321324

@@ -328,21 +331,23 @@ BaseObjectPtr<ContextifyContext> ContextifyContext::New(
328331
env->host_defined_option_symbol(),
329332
options->host_defined_options_id)
330333
.IsNothing()) {
331-
return BaseObjectPtr<ContextifyContext>();
334+
return nullptr;
332335
}
333336

334337
env->AssignToContext(v8_context, nullptr, info);
335338

336339
if (!env->contextify_wrapper_template()
337340
->NewInstance(v8_context)
338341
.ToLocal(&wrapper)) {
339-
return BaseObjectPtr<ContextifyContext>();
342+
return nullptr;
340343
}
341344

342-
result =
343-
MakeBaseObject<ContextifyContext>(env, wrapper, v8_context, options);
344-
// The only strong reference to the wrapper will come from the sandbox.
345-
result->MakeWeak();
345+
result = cppgc::MakeGarbageCollected<ContextifyContext>(
346+
env->isolate()->GetCppHeap()->GetAllocationHandle(),
347+
env,
348+
wrapper,
349+
v8_context,
350+
options);
346351
}
347352

348353
Local<Object> wrapper_holder =
@@ -352,7 +357,7 @@ BaseObjectPtr<ContextifyContext> ContextifyContext::New(
352357
->SetPrivate(
353358
v8_context, env->contextify_context_private_symbol(), wrapper)
354359
.IsNothing()) {
355-
return BaseObjectPtr<ContextifyContext>();
360+
return nullptr;
356361
}
357362

358363
// Assign host_defined_options_id to the sandbox object or the global object
@@ -364,7 +369,7 @@ BaseObjectPtr<ContextifyContext> ContextifyContext::New(
364369
env->host_defined_option_symbol(),
365370
options->host_defined_options_id)
366371
.IsNothing()) {
367-
return BaseObjectPtr<ContextifyContext>();
372+
return nullptr;
368373
}
369374
return result;
370375
}
@@ -438,7 +443,7 @@ void ContextifyContext::MakeContext(const FunctionCallbackInfo<Value>& args) {
438443
options.host_defined_options_id = args[6].As<Symbol>();
439444

440445
TryCatchScope try_catch(env);
441-
BaseObjectPtr<ContextifyContext> context_ptr =
446+
ContextifyContext* context_ptr =
442447
ContextifyContext::New(env, sandbox, &options);
443448

444449
if (try_catch.HasCaught()) {
@@ -469,6 +474,10 @@ ContextifyContext* ContextifyContext::ContextFromContextifiedSandbox(
469474

470475
template <typename T>
471476
ContextifyContext* ContextifyContext::Get(const PropertyCallbackInfo<T>& args) {
477+
// TODO(joyeecheung): it should be fine to simply use
478+
// args.GetIsolate()->GetCurrentContext() and take the pointer at
479+
// ContextEmbedderIndex::kContextifyContext, as V8 is supposed to
480+
// push the creation context before invoking these callbacks.
472481
return Get(args.This());
473482
}
474483

src/node_contextify.h

Lines changed: 64 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -23,17 +23,66 @@ struct ContextOptions {
2323
bool vanilla = false;
2424
};
2525

26-
class ContextifyContext : public BaseObject {
26+
/**
27+
* The memory management of a vm context is as follows:
28+
*
29+
* user code
30+
* │
31+
* As global proxy or ▼
32+
* ┌──────────────┐ kSandboxObject embedder data ┌────────────────┐
33+
* ┌─► │ V8 Context │────────────────────────────────►│ Wrapper holder │
34+
* │ └──────────────┘ └───────┬────────┘
35+
* │ ▲ Object constructor/creation context │
36+
* │ │ │
37+
* │ ┌──────┴────────────┐ contextify_context_private_symbol │
38+
* │ │ ContextifyContext │◄────────────────────────────────────┘
39+
* │ │ JS Wrapper │◄──────────► ┌─────────────────────────┐
40+
* │ └───────────────────┘ cppgc │ node::ContextifyContext │
41+
* │ │ C++ Object │
42+
* └──────────────────────────────────► └─────────────────────────┘
43+
* v8::TracedReference / ContextEmbedderIndex::kContextifyContext
44+
*
45+
* There are two possibilities for the "wrapper holder":
46+
*
47+
* 1. When vm.constants.DONT_CONTEXTIFY is used, the wrapper holder is the V8
48+
* context's global proxy object
49+
* 2. Otherwise it's the arbitrary "sandbox object" that users pass into
50+
* vm.createContext() or a new empty object created internally if they pass
51+
* undefined.
52+
*
53+
* In 2, the global object of the new V8 context is created using
54+
* global_object_template with interceptors that perform any requested
55+
* operations on the global object in the context first on the sandbox object
56+
* living outside of the new context, then fall back to the global proxy of the
57+
* new context.
58+
*
59+
* It's critical for the user-accessible wrapper holder to keep the
60+
* ContextifyContext wrapper alive via contextify_context_private_symbol
61+
* so that the V8 context is always available to the user while they still
62+
* hold the vm "context" object alive.
63+
*
64+
* It's also critical for the V8 context to keep the wrapper holder
65+
* (specifically, the "sandbox object") as well as the node::ContextifyContext
66+
* C++ object alive, so that when the code runs inside the object and accesses
67+
* the global object, the interceptors can still access the "sandbox object"
68+
* as well as and perform operations
69+
* on them, even if users already relinquish access to the outer
70+
* "sandbox object".
71+
*
72+
* The v8::TracedReference acts as a shortcut from the node::ContextifyContext
73+
* C++ object to the V8 context.
74+
*/
75+
class ContextifyContext final : CPPGC_MIXIN(ContextifyContext) {
2776
public:
77+
SET_CPPGC_NAME(ContextifyContext)
78+
void Trace(cppgc::Visitor* visitor) const final;
79+
void CleanEnvResource(Environment* env) override;
80+
2881
ContextifyContext(Environment* env,
2982
v8::Local<v8::Object> wrapper,
3083
v8::Local<v8::Context> v8_context,
3184
ContextOptions* options);
32-
~ContextifyContext();
33-
34-
void MemoryInfo(MemoryTracker* tracker) const override;
35-
SET_MEMORY_INFO_NAME(ContextifyContext)
36-
SET_SELF_SIZE(ContextifyContext)
85+
~ContextifyContext() override;
3786

3887
static v8::MaybeLocal<v8::Context> CreateV8Context(
3988
v8::Isolate* isolate,
@@ -48,7 +97,7 @@ class ContextifyContext : public BaseObject {
4897
Environment* env, const v8::Local<v8::Object>& wrapper_holder);
4998

5099
inline v8::Local<v8::Context> context() const {
51-
return PersistentToLocal::Default(env()->isolate(), context_);
100+
return context_.Get(env()->isolate());
52101
}
53102

54103
inline v8::Local<v8::Object> global_proxy() const {
@@ -75,14 +124,14 @@ class ContextifyContext : public BaseObject {
75124
static void InitializeGlobalTemplates(IsolateData* isolate_data);
76125

77126
private:
78-
static BaseObjectPtr<ContextifyContext> New(Environment* env,
79-
v8::Local<v8::Object> sandbox_obj,
80-
ContextOptions* options);
127+
static ContextifyContext* New(Environment* env,
128+
v8::Local<v8::Object> sandbox_obj,
129+
ContextOptions* options);
81130
// Initialize a context created from CreateV8Context()
82-
static BaseObjectPtr<ContextifyContext> New(v8::Local<v8::Context> ctx,
83-
Environment* env,
84-
v8::Local<v8::Object> sandbox_obj,
85-
ContextOptions* options);
131+
static ContextifyContext* New(v8::Local<v8::Context> ctx,
132+
Environment* env,
133+
v8::Local<v8::Object> sandbox_obj,
134+
ContextOptions* options);
86135

87136
static bool IsStillInitializing(const ContextifyContext* ctx);
88137
static void MakeContext(const v8::FunctionCallbackInfo<v8::Value>& args);
@@ -140,7 +189,7 @@ class ContextifyContext : public BaseObject {
140189
static void IndexedPropertyEnumeratorCallback(
141190
const v8::PropertyCallbackInfo<v8::Array>& args);
142191

143-
v8::Global<v8::Context> context_;
192+
v8::TracedReference<v8::Context> context_;
144193
std::unique_ptr<v8::MicrotaskQueue> microtask_queue_;
145194
};
146195

0 commit comments

Comments
 (0)