Skip to content

Commit a292630

Browse files
joyeecheungcodebytere
authored andcommitted
src: retrieve binding data from the context
Instead of passing them through the data bound to function templates, store references to them in a list embedded inside the context. This makes the function templates more context-independent, and makes it possible to embed binding data in non-main contexts. Co-authored-by: Anna Henningsen <anna@addaleax.net> PR-URL: #33139 Reviewed-By: James M Snell <jasnell@gmail.com> Reviewed-By: Joyee Cheung <joyeec9h3@gmail.com>
1 parent 07372e9 commit a292630

26 files changed

+233
-161
lines changed

src/README.md

+15-9
Original file line numberDiff line numberDiff line change
@@ -400,16 +400,23 @@ NODE_MODULE_CONTEXT_AWARE_INTERNAL(cares_wrap, Initialize)
400400
401401
Some internal bindings, such as the HTTP parser, maintain internal state that
402402
only affects that particular binding. In that case, one common way to store
403-
that state is through the use of `Environment::BindingScope`, which gives all
404-
new functions created within it access to an object for storing such state.
403+
that state is through the use of `Environment::AddBindingData`, which gives
404+
binding functions access to an object for storing such state.
405405
That object is always a [`BaseObject`][].
406406
407+
Its class needs to have a static `binding_data_name` field that based on a
408+
constant string, in order to disambiguate it from other classes of this type,
409+
and which could e.g. match the binding’s name (in the example above, that would
410+
be `cares_wrap`).
411+
407412
```c++
408413
// In the HTTP parser source code file:
409414
class BindingData : public BaseObject {
410415
public:
411416
BindingData(Environment* env, Local<Object> obj) : BaseObject(env, obj) {}
412417
418+
static constexpr FastStringKey binding_data_name { "http_parser" };
419+
413420
std::vector<char> parser_buffer;
414421
bool parser_buffer_in_use = false;
415422
@@ -418,22 +425,21 @@ class BindingData : public BaseObject {
418425
419426
// Available for binding functions, e.g. the HTTP Parser constructor:
420427
static void New(const FunctionCallbackInfo<Value>& args) {
421-
BindingData* binding_data = Unwrap<BindingData>(args.Data());
428+
BindingData* binding_data = Environment::GetBindingData<BindingData>(args);
422429
new Parser(binding_data, args.This());
423430
}
424431
425-
// ... because the initialization function told the Environment to use this
426-
// BindingData class for all functions created by it:
432+
// ... because the initialization function told the Environment to store the
433+
// BindingData object:
427434
void InitializeHttpParser(Local<Object> target,
428435
Local<Value> unused,
429436
Local<Context> context,
430437
void* priv) {
431438
Environment* env = Environment::GetCurrent(context);
432-
Environment::BindingScope<BindingData> binding_scope(env);
433-
if (!binding_scope) return;
434-
BindingData* binding_data = binding_scope.data;
439+
BindingData* const binding_data =
440+
env->AddBindingData<BindingData>(context, target);
441+
if (binding_data == nullptr) return;
435442
436-
// Created within the Environment::BindingScope
437443
Local<FunctionTemplate> t = env->NewFunctionTemplate(Parser::New);
438444
...
439445
}

src/env-inl.h

+41-37
Original file line numberDiff line numberDiff line change
@@ -287,6 +287,10 @@ inline void Environment::AssignToContext(v8::Local<v8::Context> context,
287287
// Used by Environment::GetCurrent to know that we are on a node context.
288288
context->SetAlignedPointerInEmbedderData(
289289
ContextEmbedderIndex::kContextTag, Environment::kNodeContextTagPtr);
290+
// Used to retrieve bindings
291+
context->SetAlignedPointerInEmbedderData(
292+
ContextEmbedderIndex::kBindingListIndex, &(this->bindings_));
293+
290294
#if HAVE_INSPECTOR
291295
inspector_agent()->ContextCreated(context, info);
292296
#endif // HAVE_INSPECTOR
@@ -318,55 +322,55 @@ inline Environment* Environment::GetCurrent(v8::Local<v8::Context> context) {
318322

319323
inline Environment* Environment::GetCurrent(
320324
const v8::FunctionCallbackInfo<v8::Value>& info) {
321-
return GetFromCallbackData(info.Data());
325+
return GetCurrent(info.GetIsolate()->GetCurrentContext());
322326
}
323327

324328
template <typename T>
325329
inline Environment* Environment::GetCurrent(
326330
const v8::PropertyCallbackInfo<T>& info) {
327-
return GetFromCallbackData(info.Data());
331+
return GetCurrent(info.GetIsolate()->GetCurrentContext());
328332
}
329333

330-
Environment* Environment::GetFromCallbackData(v8::Local<v8::Value> val) {
331-
DCHECK(val->IsObject());
332-
v8::Local<v8::Object> obj = val.As<v8::Object>();
333-
DCHECK_GE(obj->InternalFieldCount(),
334-
BaseObject::kInternalFieldCount);
335-
Environment* env = Unwrap<BaseObject>(obj)->env();
336-
DCHECK(env->as_callback_data_template()->HasInstance(obj));
337-
return env;
334+
template <typename T, typename U>
335+
inline T* Environment::GetBindingData(const v8::PropertyCallbackInfo<U>& info) {
336+
return GetBindingData<T>(info.GetIsolate()->GetCurrentContext());
338337
}
339338

340339
template <typename T>
341-
Environment::BindingScope<T>::BindingScope(Environment* env) : env(env) {
342-
v8::Local<v8::Object> callback_data;
343-
if (!env->MakeBindingCallbackData<T>().ToLocal(&callback_data))
344-
return;
345-
data = Unwrap<T>(callback_data);
346-
347-
// No nesting allowed currently.
348-
CHECK_EQ(env->current_callback_data(), env->as_callback_data());
349-
env->set_current_callback_data(callback_data);
340+
inline T* Environment::GetBindingData(
341+
const v8::FunctionCallbackInfo<v8::Value>& info) {
342+
return GetBindingData<T>(info.GetIsolate()->GetCurrentContext());
350343
}
351344

352345
template <typename T>
353-
Environment::BindingScope<T>::~BindingScope() {
354-
env->set_current_callback_data(env->as_callback_data());
346+
inline T* Environment::GetBindingData(v8::Local<v8::Context> context) {
347+
BindingDataStore* map = static_cast<BindingDataStore*>(
348+
context->GetAlignedPointerFromEmbedderData(
349+
ContextEmbedderIndex::kBindingListIndex));
350+
DCHECK_NOT_NULL(map);
351+
auto it = map->find(T::binding_data_name);
352+
if (UNLIKELY(it == map->end())) return nullptr;
353+
T* result = static_cast<T*>(it->second.get());
354+
DCHECK_NOT_NULL(result);
355+
DCHECK_EQ(result->env(), GetCurrent(context));
356+
return result;
355357
}
356358

357359
template <typename T>
358-
v8::MaybeLocal<v8::Object> Environment::MakeBindingCallbackData() {
359-
v8::Local<v8::Function> ctor;
360-
v8::Local<v8::Object> obj;
361-
if (!as_callback_data_template()->GetFunction(context()).ToLocal(&ctor) ||
362-
!ctor->NewInstance(context()).ToLocal(&obj)) {
363-
return v8::MaybeLocal<v8::Object>();
364-
}
365-
T* data = new T(this, obj);
360+
inline T* Environment::AddBindingData(
361+
v8::Local<v8::Context> context,
362+
v8::Local<v8::Object> target) {
363+
DCHECK_EQ(GetCurrent(context), this);
366364
// This won't compile if T is not a BaseObject subclass.
367-
CHECK_EQ(data, static_cast<BaseObject*>(data));
368-
data->MakeWeak();
369-
return obj;
365+
BaseObjectPtr<T> item = MakeDetachedBaseObject<T>(this, target);
366+
BindingDataStore* map = static_cast<BindingDataStore*>(
367+
context->GetAlignedPointerFromEmbedderData(
368+
ContextEmbedderIndex::kBindingListIndex));
369+
DCHECK_NOT_NULL(map);
370+
auto result = map->emplace(T::binding_data_name, item);
371+
CHECK(result.second);
372+
DCHECK_EQ(GetBindingData<T>(context), item.get());
373+
return item.get();
370374
}
371375

372376
inline Environment* Environment::GetThreadLocalEnv() {
@@ -1077,8 +1081,7 @@ inline v8::Local<v8::FunctionTemplate>
10771081
v8::Local<v8::Signature> signature,
10781082
v8::ConstructorBehavior behavior,
10791083
v8::SideEffectType side_effect_type) {
1080-
v8::Local<v8::Object> external = current_callback_data();
1081-
return v8::FunctionTemplate::New(isolate(), callback, external,
1084+
return v8::FunctionTemplate::New(isolate(), callback, v8::Local<v8::Value>(),
10821085
signature, 0, behavior, side_effect_type);
10831086
}
10841087

@@ -1270,9 +1273,10 @@ void Environment::set_process_exit_handler(
12701273
ENVIRONMENT_STRONG_PERSISTENT_VALUES(V)
12711274
#undef V
12721275

1273-
inline v8::Local<v8::Context> Environment::context() const {
1274-
return PersistentToLocal::Strong(context_);
1275-
}
1276+
v8::Local<v8::Context> Environment::context() const {
1277+
return PersistentToLocal::Strong(context_);
1278+
}
1279+
12761280
} // namespace node
12771281

12781282
// These two files depend on each other. Including base_object-inl.h after this

src/env.cc

+4-14
Original file line numberDiff line numberDiff line change
@@ -261,29 +261,17 @@ void TrackingTraceStateObserver::UpdateTraceCategoryState() {
261261
USE(cb->Call(env_->context(), Undefined(isolate), arraysize(args), args));
262262
}
263263

264-
class NoBindingData : public BaseObject {
265-
public:
266-
NoBindingData(Environment* env, Local<Object> obj) : BaseObject(env, obj) {}
267-
268-
SET_NO_MEMORY_INFO()
269-
SET_MEMORY_INFO_NAME(NoBindingData)
270-
SET_SELF_SIZE(NoBindingData)
271-
};
272-
273264
void Environment::CreateProperties() {
274265
HandleScope handle_scope(isolate_);
275266
Local<Context> ctx = context();
267+
276268
{
277269
Context::Scope context_scope(ctx);
278270
Local<FunctionTemplate> templ = FunctionTemplate::New(isolate());
279271
templ->InstanceTemplate()->SetInternalFieldCount(
280272
BaseObject::kInternalFieldCount);
281-
set_as_callback_data_template(templ);
282273

283-
Local<Object> obj = MakeBindingCallbackData<NoBindingData>()
284-
.ToLocalChecked();
285-
set_as_callback_data(obj);
286-
set_current_callback_data(obj);
274+
set_binding_data_ctor_template(templ);
287275
}
288276

289277
// Store primordials setup by the per-context script in the environment.
@@ -674,6 +662,8 @@ void Environment::RunCleanup() {
674662
started_cleanup_ = true;
675663
TraceEventScope trace_scope(TRACING_CATEGORY_NODE1(environment),
676664
"RunCleanup", this);
665+
bindings_.clear();
666+
initial_base_object_count_ = 0;
677667
CleanupHandles();
678668

679669
while (!cleanup_hooks_.empty()) {

src/env.h

+17-18
Original file line numberDiff line numberDiff line change
@@ -390,9 +390,9 @@ constexpr size_t kFsStatsBufferLength =
390390
V(zero_return_string, "ZERO_RETURN")
391391

392392
#define ENVIRONMENT_STRONG_PERSISTENT_TEMPLATES(V) \
393-
V(as_callback_data_template, v8::FunctionTemplate) \
394393
V(async_wrap_ctor_template, v8::FunctionTemplate) \
395394
V(async_wrap_object_ctor_template, v8::FunctionTemplate) \
395+
V(binding_data_ctor_template, v8::FunctionTemplate) \
396396
V(compiled_fn_entry_template, v8::ObjectTemplate) \
397397
V(dir_instance_template, v8::ObjectTemplate) \
398398
V(fd_constructor_template, v8::ObjectTemplate) \
@@ -420,7 +420,6 @@ constexpr size_t kFsStatsBufferLength =
420420
V(worker_heap_snapshot_taker_template, v8::ObjectTemplate)
421421

422422
#define ENVIRONMENT_STRONG_PERSISTENT_VALUES(V) \
423-
V(as_callback_data, v8::Object) \
424423
V(async_hooks_after_function, v8::Function) \
425424
V(async_hooks_before_function, v8::Function) \
426425
V(async_hooks_binding, v8::Object) \
@@ -429,7 +428,6 @@ constexpr size_t kFsStatsBufferLength =
429428
V(async_hooks_promise_resolve_function, v8::Function) \
430429
V(buffer_prototype_object, v8::Object) \
431430
V(crypto_key_object_constructor, v8::Function) \
432-
V(current_callback_data, v8::Object) \
433431
V(domain_callback, v8::Function) \
434432
V(domexception_function, v8::Function) \
435433
V(enhance_fatal_stack_after_inspector, v8::Function) \
@@ -864,25 +862,24 @@ class Environment : public MemoryRetainer {
864862
static inline Environment* GetCurrent(
865863
const v8::PropertyCallbackInfo<T>& info);
866864

867-
static inline Environment* GetFromCallbackData(v8::Local<v8::Value> val);
868-
869865
// Methods created using SetMethod(), SetPrototypeMethod(), etc. inside
870866
// this scope can access the created T* object using
871-
// Unwrap<T>(args.Data()) later.
867+
// GetBindingData<T>(args) later.
872868
template <typename T>
873-
struct BindingScope {
874-
explicit inline BindingScope(Environment* env);
875-
inline ~BindingScope();
876-
877-
T* data = nullptr;
878-
Environment* env;
879-
880-
inline operator bool() const { return data != nullptr; }
881-
inline bool operator !() const { return data == nullptr; }
882-
};
883-
869+
T* AddBindingData(v8::Local<v8::Context> context,
870+
v8::Local<v8::Object> target);
871+
template <typename T, typename U>
872+
static inline T* GetBindingData(const v8::PropertyCallbackInfo<U>& info);
884873
template <typename T>
885-
inline v8::MaybeLocal<v8::Object> MakeBindingCallbackData();
874+
static inline T* GetBindingData(
875+
const v8::FunctionCallbackInfo<v8::Value>& info);
876+
template <typename T>
877+
static inline T* GetBindingData(v8::Local<v8::Context> context);
878+
879+
typedef std::unordered_map<
880+
FastStringKey,
881+
BaseObjectPtr<BaseObject>,
882+
FastStringKey::Hash> BindingDataStore;
886883

887884
static uv_key_t thread_local_env;
888885
static inline Environment* GetThreadLocalEnv();
@@ -1425,6 +1422,8 @@ class Environment : public MemoryRetainer {
14251422
void RequestInterruptFromV8();
14261423
static void CheckImmediate(uv_check_t* handle);
14271424

1425+
BindingDataStore bindings_;
1426+
14281427
// Use an unordered_set, so that we have efficient insertion and removal.
14291428
std::unordered_set<CleanupHookCallback,
14301429
CleanupHookCallback::Hash,

src/fs_event_wrap.cc

+1-1
Original file line numberDiff line numberDiff line change
@@ -108,7 +108,7 @@ void FSEventWrap::Initialize(Local<Object> target,
108108
Local<FunctionTemplate> get_initialized_templ =
109109
FunctionTemplate::New(env->isolate(),
110110
GetInitialized,
111-
env->current_callback_data(),
111+
Local<Value>(),
112112
Signature::New(env->isolate(), t));
113113

114114
t->PrototypeTemplate()->SetAccessorProperty(

src/node.cc

+1-2
Original file line numberDiff line numberDiff line change
@@ -331,8 +331,7 @@ MaybeLocal<Value> Environment::BootstrapNode() {
331331

332332
Local<String> env_string = FIXED_ONE_BYTE_STRING(isolate_, "env");
333333
Local<Object> env_var_proxy;
334-
if (!CreateEnvVarProxy(context(), isolate_, current_callback_data())
335-
.ToLocal(&env_var_proxy) ||
334+
if (!CreateEnvVarProxy(context(), isolate_).ToLocal(&env_var_proxy) ||
336335
process_object()->Set(context(), env_string, env_var_proxy).IsNothing()) {
337336
return MaybeLocal<Value>();
338337
}

src/node_binding.cc

+5-1
Original file line numberDiff line numberDiff line change
@@ -230,6 +230,7 @@ namespace node {
230230

231231
using v8::Context;
232232
using v8::Exception;
233+
using v8::Function;
233234
using v8::FunctionCallbackInfo;
234235
using v8::Local;
235236
using v8::NewStringType;
@@ -556,8 +557,11 @@ inline struct node_module* FindModule(struct node_module* list,
556557
static Local<Object> InitModule(Environment* env,
557558
node_module* mod,
558559
Local<String> module) {
559-
Local<Object> exports = Object::New(env->isolate());
560560
// Internal bindings don't have a "module" object, only exports.
561+
Local<Function> ctor = env->binding_data_ctor_template()
562+
->GetFunction(env->context())
563+
.ToLocalChecked();
564+
Local<Object> exports = ctor->NewInstance(env->context()).ToLocalChecked();
561565
CHECK_NULL(mod->nm_register_func);
562566
CHECK_NOT_NULL(mod->nm_context_register_func);
563567
Local<Value> unused = Undefined(env->isolate());

src/node_context_data.h

+5
Original file line numberDiff line numberDiff line change
@@ -25,11 +25,16 @@ namespace node {
2525
#define NODE_CONTEXT_TAG 35
2626
#endif
2727

28+
#ifndef NODE_BINDING_LIST
29+
#define NODE_BINDING_LIST_INDEX 36
30+
#endif
31+
2832
enum ContextEmbedderIndex {
2933
kEnvironment = NODE_CONTEXT_EMBEDDER_DATA_INDEX,
3034
kSandboxObject = NODE_CONTEXT_SANDBOX_OBJECT_INDEX,
3135
kAllowWasmCodeGeneration = NODE_CONTEXT_ALLOW_WASM_CODE_GENERATION_INDEX,
3236
kContextTag = NODE_CONTEXT_TAG,
37+
kBindingListIndex = NODE_BINDING_LIST_INDEX
3338
};
3439

3540
} // namespace node

src/node_crypto.cc

+2-2
Original file line numberDiff line numberDiff line change
@@ -505,7 +505,7 @@ void SecureContext::Initialize(Environment* env, Local<Object> target) {
505505
Local<FunctionTemplate> ctx_getter_templ =
506506
FunctionTemplate::New(env->isolate(),
507507
CtxGetter,
508-
env->current_callback_data(),
508+
Local<Value>(),
509509
Signature::New(env->isolate(), t));
510510

511511

@@ -5103,7 +5103,7 @@ void DiffieHellman::Initialize(Environment* env, Local<Object> target) {
51035103
Local<FunctionTemplate> verify_error_getter_templ =
51045104
FunctionTemplate::New(env->isolate(),
51055105
DiffieHellman::VerifyErrorGetter,
5106-
env->current_callback_data(),
5106+
Local<Value>(),
51075107
Signature::New(env->isolate(), t),
51085108
/* length */ 0,
51095109
ConstructorBehavior::kThrow,

src/node_env_var.cc

+2-4
Original file line numberDiff line numberDiff line change
@@ -377,13 +377,11 @@ static void EnvEnumerator(const PropertyCallbackInfo<Array>& info) {
377377
env->env_vars()->Enumerate(env->isolate()));
378378
}
379379

380-
MaybeLocal<Object> CreateEnvVarProxy(Local<Context> context,
381-
Isolate* isolate,
382-
Local<Object> data) {
380+
MaybeLocal<Object> CreateEnvVarProxy(Local<Context> context, Isolate* isolate) {
383381
EscapableHandleScope scope(isolate);
384382
Local<ObjectTemplate> env_proxy_template = ObjectTemplate::New(isolate);
385383
env_proxy_template->SetHandler(NamedPropertyHandlerConfiguration(
386-
EnvGetter, EnvSetter, EnvQuery, EnvDeleter, EnvEnumerator, data,
384+
EnvGetter, EnvSetter, EnvQuery, EnvDeleter, EnvEnumerator, Local<Value>(),
387385
PropertyHandlerFlags::kHasNoSideEffect));
388386
return scope.EscapeMaybe(env_proxy_template->NewInstance(context));
389387
}

src/node_file-inl.h

+1-1
Original file line numberDiff line numberDiff line change
@@ -227,7 +227,7 @@ FSReqBase* GetReqWrap(const v8::FunctionCallbackInfo<v8::Value>& args,
227227
return Unwrap<FSReqBase>(value.As<v8::Object>());
228228
}
229229

230-
BindingData* binding_data = Unwrap<BindingData>(args.Data());
230+
BindingData* binding_data = Environment::GetBindingData<BindingData>(args);
231231
Environment* env = binding_data->env();
232232
if (value->StrictEquals(env->fs_use_promises_symbol())) {
233233
if (use_bigint) {

0 commit comments

Comments
 (0)