Skip to content

Commit 396b766

Browse files
committed
src: define per-isolate internal bindings registration callback
Bindings that need to be loaded in distinct contexts of node::Realm in the same node::Environment instance needs to share the per-isolate templates. Add a new binding registration callback, which is called with per-IsolateData. This allows bindings to define templates and share them across realms eagerly, and avoid accidental modification on the templates when the per-context callback is called multiple times. This also tracks loaded bindings and built-in modules with node::Realm. PR-URL: nodejs#45547 Refs: nodejs#42528 Reviewed-By: Joyee Cheung <[email protected]> Reviewed-By: Colin Ihrig <[email protected]> Reviewed-By: James M Snell <[email protected]>
1 parent d60d4c1 commit 396b766

13 files changed

+238
-137
lines changed

src/env-inl.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -820,6 +820,7 @@ void Environment::set_process_exit_handler(
820820
#undef VY
821821
#undef VP
822822

823+
#define VM(PropertyName) V(PropertyName##_binding, v8::FunctionTemplate)
823824
#define V(PropertyName, TypeName) \
824825
inline v8::Local<TypeName> IsolateData::PropertyName() const { \
825826
return PropertyName##_.Get(isolate_); \
@@ -828,7 +829,9 @@ void Environment::set_process_exit_handler(
828829
PropertyName##_.Set(isolate_, value); \
829830
}
830831
PER_ISOLATE_TEMPLATE_PROPERTIES(V)
832+
NODE_BINDINGS_WITH_PER_ISOLATE_INIT(VM)
831833
#undef V
834+
#undef VM
832835

833836
#define VP(PropertyName, StringValue) V(v8::Private, PropertyName)
834837
#define VY(PropertyName, StringValue) V(v8::Symbol, PropertyName)

src/env.cc

Lines changed: 5 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -313,6 +313,7 @@ IsolateDataSerializeInfo IsolateData::Serialize(SnapshotCreator* creator) {
313313
info.primitive_values.push_back(creator->AddData(async_wrap_provider(i)));
314314

315315
uint32_t id = 0;
316+
#define VM(PropertyName) V(PropertyName##_binding, FunctionTemplate)
316317
#define V(PropertyName, TypeName) \
317318
do { \
318319
Local<TypeName> field = PropertyName(); \
@@ -323,6 +324,7 @@ IsolateDataSerializeInfo IsolateData::Serialize(SnapshotCreator* creator) {
323324
id++; \
324325
} while (0);
325326
PER_ISOLATE_TEMPLATE_PROPERTIES(V)
327+
NODE_BINDINGS_WITH_PER_ISOLATE_INIT(VM)
326328
#undef V
327329

328330
return info;
@@ -367,6 +369,7 @@ void IsolateData::DeserializeProperties(const IsolateDataSerializeInfo* info) {
367369
const std::vector<PropInfo>& values = info->template_values;
368370
i = 0; // index to the array
369371
uint32_t id = 0;
372+
#define VM(PropertyName) V(PropertyName##_binding, FunctionTemplate)
370373
#define V(PropertyName, TypeName) \
371374
do { \
372375
if (values.size() > i && id == values[i].id) { \
@@ -387,6 +390,7 @@ void IsolateData::DeserializeProperties(const IsolateDataSerializeInfo* info) {
387390
} while (0);
388391

389392
PER_ISOLATE_TEMPLATE_PROPERTIES(V);
393+
NODE_BINDINGS_WITH_PER_ISOLATE_INIT(VM);
390394
#undef V
391395
}
392396

@@ -453,12 +457,12 @@ void IsolateData::CreateProperties() {
453457
NODE_ASYNC_PROVIDER_TYPES(V)
454458
#undef V
455459

456-
// TODO(legendecas): eagerly create per isolate templates.
457460
Local<FunctionTemplate> templ = FunctionTemplate::New(isolate());
458461
templ->InstanceTemplate()->SetInternalFieldCount(
459462
BaseObject::kInternalFieldCount);
460463
templ->Inherit(BaseObject::GetConstructorTemplate(this));
461464
set_binding_data_ctor_template(templ);
465+
binding::CreateInternalBindingTemplates(this);
462466

463467
contextify::ContextifyContext::InitializeGlobalTemplates(this);
464468
}
@@ -1581,30 +1585,13 @@ void Environment::PrintInfoForSnapshotIfDebug() {
15811585
if (enabled_debug_list()->enabled(DebugCategory::MKSNAPSHOT)) {
15821586
fprintf(stderr, "At the exit of the Environment:\n");
15831587
principal_realm()->PrintInfoForSnapshot();
1584-
fprintf(stderr, "\nBuiltins without cache:\n");
1585-
for (const auto& s : builtins_without_cache) {
1586-
fprintf(stderr, "%s\n", s.c_str());
1587-
}
1588-
fprintf(stderr, "\nBuiltins with cache:\n");
1589-
for (const auto& s : builtins_with_cache) {
1590-
fprintf(stderr, "%s\n", s.c_str());
1591-
}
1592-
fprintf(stderr, "\nStatic bindings (need to be registered):\n");
1593-
for (const auto mod : internal_bindings) {
1594-
fprintf(stderr, "%s:%s\n", mod->nm_filename, mod->nm_modname);
1595-
}
15961588
}
15971589
}
15981590

15991591
EnvSerializeInfo Environment::Serialize(SnapshotCreator* creator) {
16001592
EnvSerializeInfo info;
16011593
Local<Context> ctx = context();
16021594

1603-
// Currently all modules are compiled without cache in builtin snapshot
1604-
// builder.
1605-
info.builtins = std::vector<std::string>(builtins_without_cache.begin(),
1606-
builtins_without_cache.end());
1607-
16081595
info.async_hooks = async_hooks_.Serialize(ctx, creator);
16091596
info.immediate_info = immediate_info_.Serialize(ctx, creator);
16101597
info.timeout_info = timeout_info_.Serialize(ctx, creator);
@@ -1651,7 +1638,6 @@ void Environment::DeserializeProperties(const EnvSerializeInfo* info) {
16511638

16521639
RunDeserializeRequests();
16531640

1654-
builtins_in_snapshot = info->builtins;
16551641
async_hooks_.Deserialize(ctx);
16561642
immediate_info_.Deserialize(ctx);
16571643
timeout_info_.Deserialize(ctx);
@@ -1840,8 +1826,6 @@ void Environment::MemoryInfo(MemoryTracker* tracker) const {
18401826
// Iteratable STLs have their own sizes subtracted from the parent
18411827
// by default.
18421828
tracker->TrackField("isolate_data", isolate_data_);
1843-
tracker->TrackField("builtins_with_cache", builtins_with_cache);
1844-
tracker->TrackField("builtins_without_cache", builtins_without_cache);
18451829
tracker->TrackField("destroy_async_id_list", destroy_async_id_list_);
18461830
tracker->TrackField("exec_argv", exec_argv_);
18471831
tracker->TrackField("exiting", exiting_);

src/env.h

Lines changed: 6 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -154,11 +154,14 @@ class NODE_EXTERN_PRIVATE IsolateData : public MemoryRetainer {
154154
#undef VS
155155
#undef VP
156156

157+
#define VM(PropertyName) V(PropertyName##_binding, v8::FunctionTemplate)
157158
#define V(PropertyName, TypeName) \
158159
inline v8::Local<TypeName> PropertyName() const; \
159160
inline void set_##PropertyName(v8::Local<TypeName> value);
160161
PER_ISOLATE_TEMPLATE_PROPERTIES(V)
162+
NODE_BINDINGS_WITH_PER_ISOLATE_INIT(VM)
161163
#undef V
164+
#undef VM
162165

163166
inline v8::Local<v8::String> async_wrap_provider(int index) const;
164167

@@ -178,15 +181,17 @@ class NODE_EXTERN_PRIVATE IsolateData : public MemoryRetainer {
178181
#define VP(PropertyName, StringValue) V(v8::Private, PropertyName)
179182
#define VY(PropertyName, StringValue) V(v8::Symbol, PropertyName)
180183
#define VS(PropertyName, StringValue) V(v8::String, PropertyName)
184+
#define VM(PropertyName) V(v8::FunctionTemplate, PropertyName##_binding)
181185
#define VT(PropertyName, TypeName) V(TypeName, PropertyName)
182186
#define V(TypeName, PropertyName) \
183187
v8::Eternal<TypeName> PropertyName ## _;
184188
PER_ISOLATE_PRIVATE_SYMBOL_PROPERTIES(VP)
185189
PER_ISOLATE_SYMBOL_PROPERTIES(VY)
186190
PER_ISOLATE_STRING_PROPERTIES(VS)
187191
PER_ISOLATE_TEMPLATE_PROPERTIES(VT)
192+
NODE_BINDINGS_WITH_PER_ISOLATE_INIT(VM)
188193
#undef V
189-
#undef V
194+
#undef VM
190195
#undef VT
191196
#undef VS
192197
#undef VY
@@ -456,7 +461,6 @@ struct DeserializeRequest {
456461
};
457462

458463
struct EnvSerializeInfo {
459-
std::vector<std::string> builtins;
460464
AsyncHooks::SerializeInfo async_hooks;
461465
TickInfo::SerializeInfo tick_info;
462466
ImmediateInfo::SerializeInfo immediate_info;
@@ -710,13 +714,6 @@ class Environment : public MemoryRetainer {
710714
// List of id's that have been destroyed and need the destroy() cb called.
711715
inline std::vector<double>* destroy_async_id_list();
712716

713-
std::set<struct node_module*> internal_bindings;
714-
std::set<std::string> builtins_with_cache;
715-
std::set<std::string> builtins_without_cache;
716-
// This is only filled during deserialization. We use a vector since
717-
// it's only used for tests.
718-
std::vector<std::string> builtins_in_snapshot;
719-
720717
std::unordered_multimap<int, loader::ModuleWrap*> hash_to_module_map;
721718
std::unordered_map<uint32_t, loader::ModuleWrap*> id_to_module_map;
722719
std::unordered_map<uint32_t, contextify::ContextifyScript*>

src/node_binding.cc

Lines changed: 70 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -109,6 +109,12 @@
109109
NODE_BUILTIN_BINDINGS(V)
110110
#undef V
111111

112+
#define V(modname) \
113+
void _register_isolate_##modname(node::IsolateData* isolate_data, \
114+
v8::Local<v8::FunctionTemplate> target);
115+
NODE_BINDINGS_WITH_PER_ISOLATE_INIT(V)
116+
#undef V
117+
112118
#ifdef _AIX
113119
// On AIX, dlopen() behaves differently from other operating systems, in that
114120
// it returns unique values from each call, rather than identical values, when
@@ -236,9 +242,12 @@ static bool libc_may_be_musl() { return false; }
236242
namespace node {
237243

238244
using v8::Context;
245+
using v8::EscapableHandleScope;
239246
using v8::Exception;
240-
using v8::Function;
241247
using v8::FunctionCallbackInfo;
248+
using v8::FunctionTemplate;
249+
using v8::HandleScope;
250+
using v8::Isolate;
242251
using v8::Local;
243252
using v8::Object;
244253
using v8::String;
@@ -559,50 +568,86 @@ inline struct node_module* FindModule(struct node_module* list,
559568
return mp;
560569
}
561570

562-
static Local<Object> InitInternalBinding(Environment* env,
563-
node_module* mod,
564-
Local<String> module) {
565-
// Internal bindings don't have a "module" object, only exports.
566-
Local<Function> ctor = env->binding_data_ctor_template()
567-
->GetFunction(env->context())
568-
.ToLocalChecked();
569-
Local<Object> exports = ctor->NewInstance(env->context()).ToLocalChecked();
571+
void CreateInternalBindingTemplates(IsolateData* isolate_data) {
572+
#define V(modname) \
573+
do { \
574+
Local<FunctionTemplate> templ = \
575+
FunctionTemplate::New(isolate_data->isolate()); \
576+
templ->InstanceTemplate()->SetInternalFieldCount( \
577+
BaseObject::kInternalFieldCount); \
578+
templ->Inherit(BaseObject::GetConstructorTemplate(isolate_data)); \
579+
_register_isolate_##modname(isolate_data, templ); \
580+
isolate_data->set_##modname##_binding(templ); \
581+
} while (0);
582+
NODE_BINDINGS_WITH_PER_ISOLATE_INIT(V)
583+
#undef V
584+
}
585+
586+
static Local<Object> GetInternalBindingExportObject(IsolateData* isolate_data,
587+
const char* mod_name,
588+
Local<Context> context) {
589+
Local<FunctionTemplate> ctor;
590+
#define V(name) \
591+
if (strcmp(mod_name, #name) == 0) { \
592+
ctor = isolate_data->name##_binding(); \
593+
} else // NOLINT(readability/braces)
594+
NODE_BINDINGS_WITH_PER_ISOLATE_INIT(V)
595+
#undef V
596+
{
597+
ctor = isolate_data->binding_data_ctor_template();
598+
}
599+
600+
Local<Object> obj = ctor->GetFunction(context)
601+
.ToLocalChecked()
602+
->NewInstance(context)
603+
.ToLocalChecked();
604+
return obj;
605+
}
606+
607+
static Local<Object> InitInternalBinding(Realm* realm, node_module* mod) {
608+
EscapableHandleScope scope(realm->isolate());
609+
Local<Context> context = realm->context();
610+
Local<Object> exports = GetInternalBindingExportObject(
611+
realm->isolate_data(), mod->nm_modname, context);
570612
CHECK_NULL(mod->nm_register_func);
571613
CHECK_NOT_NULL(mod->nm_context_register_func);
572-
Local<Value> unused = Undefined(env->isolate());
573-
mod->nm_context_register_func(exports, unused, env->context(), mod->nm_priv);
574-
return exports;
614+
Local<Value> unused = Undefined(realm->isolate());
615+
// Internal bindings don't have a "module" object, only exports.
616+
mod->nm_context_register_func(exports, unused, context, mod->nm_priv);
617+
return scope.Escape(exports);
575618
}
576619

577620
void GetInternalBinding(const FunctionCallbackInfo<Value>& args) {
578-
Environment* env = Environment::GetCurrent(args);
621+
Realm* realm = Realm::GetCurrent(args);
622+
Isolate* isolate = realm->isolate();
623+
HandleScope scope(isolate);
624+
Local<Context> context = realm->context();
579625

580626
CHECK(args[0]->IsString());
581627

582628
Local<String> module = args[0].As<String>();
583-
node::Utf8Value module_v(env->isolate(), module);
629+
node::Utf8Value module_v(isolate, module);
584630
Local<Object> exports;
585631

586632
node_module* mod = FindModule(modlist_internal, *module_v, NM_F_INTERNAL);
587633
if (mod != nullptr) {
588-
exports = InitInternalBinding(env, mod, module);
589-
env->internal_bindings.insert(mod);
634+
exports = InitInternalBinding(realm, mod);
635+
realm->internal_bindings.insert(mod);
590636
} else if (!strcmp(*module_v, "constants")) {
591-
exports = Object::New(env->isolate());
592-
CHECK(
593-
exports->SetPrototype(env->context(), Null(env->isolate())).FromJust());
594-
DefineConstants(env->isolate(), exports);
637+
exports = Object::New(isolate);
638+
CHECK(exports->SetPrototype(context, Null(isolate)).FromJust());
639+
DefineConstants(isolate, exports);
595640
} else if (!strcmp(*module_v, "natives")) {
596-
exports = builtins::BuiltinLoader::GetSourceObject(env->context());
641+
exports = builtins::BuiltinLoader::GetSourceObject(context);
597642
// Legacy feature: process.binding('natives').config contains stringified
598643
// config.gypi
599644
CHECK(exports
600-
->Set(env->context(),
601-
env->config_string(),
602-
builtins::BuiltinLoader::GetConfigString(env->isolate()))
645+
->Set(context,
646+
realm->isolate_data()->config_string(),
647+
builtins::BuiltinLoader::GetConfigString(isolate))
603648
.FromJust());
604649
} else {
605-
return THROW_ERR_INVALID_MODULE(env, "No such binding: %s", *module_v);
650+
return THROW_ERR_INVALID_MODULE(isolate, "No such binding: %s", *module_v);
606651
}
607652

608653
args.GetReturnValue().Set(exports);

src/node_binding.h

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,8 @@ static_assert(static_cast<int>(NM_F_LINKED) ==
2424
static_cast<int>(node::ModuleFlags::kLinked),
2525
"NM_F_LINKED != node::ModuleFlags::kLinked");
2626

27+
#define NODE_BINDINGS_WITH_PER_ISOLATE_INIT(V) V(builtins)
28+
2729
#define NODE_BINDING_CONTEXT_AWARE_CPP(modname, regfunc, priv, flags) \
2830
static node::node_module _module = { \
2931
NODE_MODULE_VERSION, \
@@ -44,9 +46,20 @@ void napi_module_register_by_symbol(v8::Local<v8::Object> exports,
4446

4547
namespace node {
4648

49+
// Define a node internal binding that may be loaded in a context of
50+
// a node::Environment.
51+
// If an internal binding needs initializing per-isolate templates, define
52+
// with NODE_BINDING_PER_ISOLATE_INIT too.
4753
#define NODE_BINDING_CONTEXT_AWARE_INTERNAL(modname, regfunc) \
4854
NODE_BINDING_CONTEXT_AWARE_CPP(modname, regfunc, nullptr, NM_F_INTERNAL)
4955

56+
// Define a per-isolate initialization function for a node internal binding.
57+
#define NODE_BINDING_PER_ISOLATE_INIT(modname, per_isolate_func) \
58+
void _register_isolate_##modname(node::IsolateData* isolate_data, \
59+
v8::Local<v8::FunctionTemplate> target) { \
60+
per_isolate_func(isolate_data, target); \
61+
}
62+
5063
// Globals per process
5164
// This is set by node::Init() which is used by embedders
5265
extern bool node_is_initialized;
@@ -87,6 +100,8 @@ class DLib {
87100
// use the __attribute__((constructor)). Need to
88101
// explicitly call the _register* functions.
89102
void RegisterBuiltinBindings();
103+
// Create per-isolate templates for the internal bindings.
104+
void CreateInternalBindingTemplates(IsolateData* isolate_data);
90105
void GetInternalBinding(const v8::FunctionCallbackInfo<v8::Value>& args);
91106
void GetLinkedBinding(const v8::FunctionCallbackInfo<v8::Value>& args);
92107
void DLOpen(const v8::FunctionCallbackInfo<v8::Value>& args);

0 commit comments

Comments
 (0)