Skip to content

Commit db535b6

Browse files
legendecasRafaelGSS
authored andcommitted
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: #45547 Refs: #42528 Reviewed-By: Joyee Cheung <[email protected]> Reviewed-By: Colin Ihrig <[email protected]> Reviewed-By: James M Snell <[email protected]>
1 parent 0552b13 commit db535b6

13 files changed

+239
-141
lines changed

src/env-inl.h

+3
Original file line numberDiff line numberDiff line change
@@ -821,6 +821,7 @@ void Environment::set_process_exit_handler(
821821
#undef VY
822822
#undef VP
823823

824+
#define VM(PropertyName) V(PropertyName##_binding, v8::FunctionTemplate)
824825
#define V(PropertyName, TypeName) \
825826
inline v8::Local<TypeName> IsolateData::PropertyName() const { \
826827
return PropertyName##_.Get(isolate_); \
@@ -829,7 +830,9 @@ void Environment::set_process_exit_handler(
829830
PropertyName##_.Set(isolate_, value); \
830831
}
831832
PER_ISOLATE_TEMPLATE_PROPERTIES(V)
833+
NODE_BINDINGS_WITH_PER_ISOLATE_INIT(VM)
832834
#undef V
835+
#undef VM
833836

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

src/env.cc

+5-21
Original file line numberDiff line numberDiff line change
@@ -314,6 +314,7 @@ IsolateDataSerializeInfo IsolateData::Serialize(SnapshotCreator* creator) {
314314
info.primitive_values.push_back(creator->AddData(async_wrap_provider(i)));
315315

316316
uint32_t id = 0;
317+
#define VM(PropertyName) V(PropertyName##_binding, FunctionTemplate)
317318
#define V(PropertyName, TypeName) \
318319
do { \
319320
Local<TypeName> field = PropertyName(); \
@@ -324,6 +325,7 @@ IsolateDataSerializeInfo IsolateData::Serialize(SnapshotCreator* creator) {
324325
id++; \
325326
} while (0);
326327
PER_ISOLATE_TEMPLATE_PROPERTIES(V)
328+
NODE_BINDINGS_WITH_PER_ISOLATE_INIT(VM)
327329
#undef V
328330

329331
return info;
@@ -368,6 +370,7 @@ void IsolateData::DeserializeProperties(const IsolateDataSerializeInfo* info) {
368370
const std::vector<PropInfo>& values = info->template_values;
369371
i = 0; // index to the array
370372
uint32_t id = 0;
373+
#define VM(PropertyName) V(PropertyName##_binding, FunctionTemplate)
371374
#define V(PropertyName, TypeName) \
372375
do { \
373376
if (values.size() > i && id == values[i].id) { \
@@ -388,6 +391,7 @@ void IsolateData::DeserializeProperties(const IsolateDataSerializeInfo* info) {
388391
} while (0);
389392

390393
PER_ISOLATE_TEMPLATE_PROPERTIES(V);
394+
NODE_BINDINGS_WITH_PER_ISOLATE_INIT(VM);
391395
#undef V
392396
}
393397

@@ -454,12 +458,12 @@ void IsolateData::CreateProperties() {
454458
NODE_ASYNC_PROVIDER_TYPES(V)
455459
#undef V
456460

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

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

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

1602-
// Currently all modules are compiled without cache in builtin snapshot
1603-
// builder.
1604-
info.builtins = std::vector<std::string>(builtins_without_cache.begin(),
1605-
builtins_without_cache.end());
1606-
16071594
info.async_hooks = async_hooks_.Serialize(ctx, creator);
16081595
info.immediate_info = immediate_info_.Serialize(ctx, creator);
16091596
info.tick_info = tick_info_.Serialize(ctx, creator);
@@ -1649,7 +1636,6 @@ void Environment::DeserializeProperties(const EnvSerializeInfo* info) {
16491636

16501637
RunDeserializeRequests();
16511638

1652-
builtins_in_snapshot = info->builtins;
16531639
async_hooks_.Deserialize(ctx);
16541640
immediate_info_.Deserialize(ctx);
16551641
tick_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

+6-9
Original file line numberDiff line numberDiff line change
@@ -155,11 +155,14 @@ class NODE_EXTERN_PRIVATE IsolateData : public MemoryRetainer {
155155
#undef VS
156156
#undef VP
157157

158+
#define VM(PropertyName) V(PropertyName##_binding, v8::FunctionTemplate)
158159
#define V(PropertyName, TypeName) \
159160
inline v8::Local<TypeName> PropertyName() const; \
160161
inline void set_##PropertyName(v8::Local<TypeName> value);
161162
PER_ISOLATE_TEMPLATE_PROPERTIES(V)
163+
NODE_BINDINGS_WITH_PER_ISOLATE_INIT(VM)
162164
#undef V
165+
#undef VM
163166

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

@@ -179,15 +182,17 @@ class NODE_EXTERN_PRIVATE IsolateData : public MemoryRetainer {
179182
#define VP(PropertyName, StringValue) V(v8::Private, PropertyName)
180183
#define VY(PropertyName, StringValue) V(v8::Symbol, PropertyName)
181184
#define VS(PropertyName, StringValue) V(v8::String, PropertyName)
185+
#define VM(PropertyName) V(v8::FunctionTemplate, PropertyName##_binding)
182186
#define VT(PropertyName, TypeName) V(TypeName, PropertyName)
183187
#define V(TypeName, PropertyName) \
184188
v8::Eternal<TypeName> PropertyName ## _;
185189
PER_ISOLATE_PRIVATE_SYMBOL_PROPERTIES(VP)
186190
PER_ISOLATE_SYMBOL_PROPERTIES(VY)
187191
PER_ISOLATE_STRING_PROPERTIES(VS)
188192
PER_ISOLATE_TEMPLATE_PROPERTIES(VT)
193+
NODE_BINDINGS_WITH_PER_ISOLATE_INIT(VM)
189194
#undef V
190-
#undef V
195+
#undef VM
191196
#undef VT
192197
#undef VS
193198
#undef VY
@@ -457,7 +462,6 @@ struct DeserializeRequest {
457462
};
458463

459464
struct EnvSerializeInfo {
460-
std::vector<std::string> builtins;
461465
AsyncHooks::SerializeInfo async_hooks;
462466
TickInfo::SerializeInfo tick_info;
463467
ImmediateInfo::SerializeInfo immediate_info;
@@ -713,13 +717,6 @@ class Environment : public MemoryRetainer {
713717
// List of id's that have been destroyed and need the destroy() cb called.
714718
inline std::vector<double>* destroy_async_id_list();
715719

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

src/node_binding.cc

+70-25
Original file line numberDiff line numberDiff line change
@@ -102,6 +102,12 @@
102102
NODE_BUILTIN_BINDINGS(V)
103103
#undef V
104104

105+
#define V(modname) \
106+
void _register_isolate_##modname(node::IsolateData* isolate_data, \
107+
v8::Local<v8::FunctionTemplate> target);
108+
NODE_BINDINGS_WITH_PER_ISOLATE_INIT(V)
109+
#undef V
110+
105111
#ifdef _AIX
106112
// On AIX, dlopen() behaves differently from other operating systems, in that
107113
// it returns unique values from each call, rather than identical values, when
@@ -229,9 +235,12 @@ static bool libc_may_be_musl() { return false; }
229235
namespace node {
230236

231237
using v8::Context;
238+
using v8::EscapableHandleScope;
232239
using v8::Exception;
233-
using v8::Function;
234240
using v8::FunctionCallbackInfo;
241+
using v8::FunctionTemplate;
242+
using v8::HandleScope;
243+
using v8::Isolate;
235244
using v8::Local;
236245
using v8::Object;
237246
using v8::String;
@@ -552,50 +561,86 @@ inline struct node_module* FindModule(struct node_module* list,
552561
return mp;
553562
}
554563

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

570613
void GetInternalBinding(const FunctionCallbackInfo<Value>& args) {
571-
Environment* env = Environment::GetCurrent(args);
614+
Realm* realm = Realm::GetCurrent(args);
615+
Isolate* isolate = realm->isolate();
616+
HandleScope scope(isolate);
617+
Local<Context> context = realm->context();
572618

573619
CHECK(args[0]->IsString());
574620

575621
Local<String> module = args[0].As<String>();
576-
node::Utf8Value module_v(env->isolate(), module);
622+
node::Utf8Value module_v(isolate, module);
577623
Local<Object> exports;
578624

579625
node_module* mod = FindModule(modlist_internal, *module_v, NM_F_INTERNAL);
580626
if (mod != nullptr) {
581-
exports = InitInternalBinding(env, mod, module);
582-
env->internal_bindings.insert(mod);
627+
exports = InitInternalBinding(realm, mod);
628+
realm->internal_bindings.insert(mod);
583629
} else if (!strcmp(*module_v, "constants")) {
584-
exports = Object::New(env->isolate());
585-
CHECK(
586-
exports->SetPrototype(env->context(), Null(env->isolate())).FromJust());
587-
DefineConstants(env->isolate(), exports);
630+
exports = Object::New(isolate);
631+
CHECK(exports->SetPrototype(context, Null(isolate)).FromJust());
632+
DefineConstants(isolate, exports);
588633
} else if (!strcmp(*module_v, "natives")) {
589-
exports = builtins::BuiltinLoader::GetSourceObject(env->context());
634+
exports = builtins::BuiltinLoader::GetSourceObject(context);
590635
// Legacy feature: process.binding('natives').config contains stringified
591636
// config.gypi
592637
CHECK(exports
593-
->Set(env->context(),
594-
env->config_string(),
595-
builtins::BuiltinLoader::GetConfigString(env->isolate()))
638+
->Set(context,
639+
realm->isolate_data()->config_string(),
640+
builtins::BuiltinLoader::GetConfigString(isolate))
596641
.FromJust());
597642
} else {
598-
return THROW_ERR_INVALID_MODULE(env, "No such binding: %s", *module_v);
643+
return THROW_ERR_INVALID_MODULE(isolate, "No such binding: %s", *module_v);
599644
}
600645

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

src/node_binding.h

+15
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)