Skip to content

Commit b34433f

Browse files
committed
src: define per-isolate internal bindings registration
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/node#45547 Refs: nodejs/node#42528 Reviewed-By: Joyee Cheung <[email protected]> Reviewed-By: Colin Ihrig <[email protected]> Reviewed-By: James M Snell <[email protected]>
1 parent 3b8eb99 commit b34433f

13 files changed

+242
-143
lines changed

Diff for: graal-nodejs/src/env-inl.h

+3
Original file line numberDiff line numberDiff line change
@@ -778,6 +778,7 @@ void Environment::set_process_exit_handler(
778778
#undef VY
779779
#undef VP
780780

781+
#define VM(PropertyName) V(PropertyName##_binding, v8::FunctionTemplate)
781782
#define V(PropertyName, TypeName) \
782783
inline v8::Local<TypeName> IsolateData::PropertyName() const { \
783784
return PropertyName##_.Get(isolate_); \
@@ -786,7 +787,9 @@ void Environment::set_process_exit_handler(
786787
PropertyName##_.Set(isolate_, value); \
787788
}
788789
PER_ISOLATE_TEMPLATE_PROPERTIES(V)
790+
NODE_BINDINGS_WITH_PER_ISOLATE_INIT(VM)
789791
#undef V
792+
#undef VM
790793

791794
#define VP(PropertyName, StringValue) V(v8::Private, PropertyName)
792795
#define VY(PropertyName, StringValue) V(v8::Symbol, PropertyName)

Diff for: graal-nodejs/src/env.cc

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

313313
uint32_t id = 0;
314+
#define VM(PropertyName) V(PropertyName##_binding, FunctionTemplate)
314315
#define V(PropertyName, TypeName) \
315316
do { \
316317
Local<TypeName> field = PropertyName(); \
@@ -321,6 +322,7 @@ IsolateDataSerializeInfo IsolateData::Serialize(SnapshotCreator* creator) {
321322
id++; \
322323
} while (0);
323324
PER_ISOLATE_TEMPLATE_PROPERTIES(V)
325+
NODE_BINDINGS_WITH_PER_ISOLATE_INIT(VM)
324326
#undef V
325327

326328
return info;
@@ -370,6 +372,7 @@ void IsolateData::DeserializeProperties(const IsolateDataSerializeInfo* info) {
370372
const std::vector<PropInfo>& values = info->template_values;
371373
i = 0; // index to the array
372374
uint32_t id = 0;
375+
#define VM(PropertyName) V(PropertyName##_binding, FunctionTemplate)
373376
#define V(PropertyName, TypeName) \
374377
do { \
375378
if (values.size() > i && id == values[i].id) { \
@@ -390,6 +393,7 @@ void IsolateData::DeserializeProperties(const IsolateDataSerializeInfo* info) {
390393
} while (0);
391394

392395
PER_ISOLATE_TEMPLATE_PROPERTIES(V);
396+
NODE_BINDINGS_WITH_PER_ISOLATE_INIT(VM);
393397
#undef V
394398
}
395399

@@ -456,12 +460,12 @@ void IsolateData::CreateProperties() {
456460
NODE_ASYNC_PROVIDER_TYPES(V)
457461
#undef V
458462

459-
// TODO(legendecas): eagerly create per isolate templates.
460463
Local<FunctionTemplate> templ = FunctionTemplate::New(isolate());
461464
templ->InstanceTemplate()->SetInternalFieldCount(
462465
BaseObject::kInternalFieldCount);
463466
templ->Inherit(BaseObject::GetConstructorTemplate(this));
464467
set_binding_data_ctor_template(templ);
468+
binding::CreateInternalBindingTemplates(this);
465469

466470
contextify::ContextifyContext::InitializeGlobalTemplates(this);
467471
}
@@ -1585,30 +1589,13 @@ void Environment::PrintInfoForSnapshotIfDebug() {
15851589
if (enabled_debug_list()->enabled(DebugCategory::MKSNAPSHOT)) {
15861590
fprintf(stderr, "At the exit of the Environment:\n");
15871591
principal_realm()->PrintInfoForSnapshot();
1588-
fprintf(stderr, "\nBuiltins without cache:\n");
1589-
for (const auto& s : builtins_without_cache) {
1590-
fprintf(stderr, "%s\n", s.c_str());
1591-
}
1592-
fprintf(stderr, "\nBuiltins with cache:\n");
1593-
for (const auto& s : builtins_with_cache) {
1594-
fprintf(stderr, "%s\n", s.c_str());
1595-
}
1596-
fprintf(stderr, "\nStatic bindings (need to be registered):\n");
1597-
for (const auto mod : internal_bindings) {
1598-
fprintf(stderr, "%s:%s\n", mod->nm_filename, mod->nm_modname);
1599-
}
16001592
}
16011593
}
16021594

16031595
EnvSerializeInfo Environment::Serialize(SnapshotCreator* creator) {
16041596
EnvSerializeInfo info;
16051597
Local<Context> ctx = context();
16061598

1607-
// Currently all modules are compiled without cache in builtin snapshot
1608-
// builder.
1609-
info.builtins = std::vector<std::string>(builtins_without_cache.begin(),
1610-
builtins_without_cache.end());
1611-
16121599
info.async_hooks = async_hooks_.Serialize(ctx, creator);
16131600
info.immediate_info = immediate_info_.Serialize(ctx, creator);
16141601
info.timeout_info = timeout_info_.Serialize(ctx, creator);
@@ -1660,7 +1647,6 @@ void Environment::DeserializeProperties(const EnvSerializeInfo* info) {
16601647

16611648
RunDeserializeRequests();
16621649

1663-
builtins_in_snapshot = info->builtins;
16641650
async_hooks_.Deserialize(ctx);
16651651
immediate_info_.Deserialize(ctx);
16661652
timeout_info_.Deserialize(ctx);
@@ -1844,8 +1830,6 @@ void Environment::MemoryInfo(MemoryTracker* tracker) const {
18441830
// Iteratable STLs have their own sizes subtracted from the parent
18451831
// by default.
18461832
tracker->TrackField("isolate_data", isolate_data_);
1847-
tracker->TrackField("builtins_with_cache", builtins_with_cache);
1848-
tracker->TrackField("builtins_without_cache", builtins_without_cache);
18491833
tracker->TrackField("destroy_async_id_list", destroy_async_id_list_);
18501834
tracker->TrackField("exec_argv", exec_argv_);
18511835
tracker->TrackField("exiting", exiting_);

Diff for: graal-nodejs/src/env.h

+6-9
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;
@@ -691,13 +695,6 @@ class Environment : public MemoryRetainer {
691695
// List of id's that have been destroyed and need the destroy() cb called.
692696
inline std::vector<double>* destroy_async_id_list();
693697

694-
std::set<struct node_module*> internal_bindings;
695-
std::set<std::string> builtins_with_cache;
696-
std::set<std::string> builtins_without_cache;
697-
// This is only filled during deserialization. We use a vector since
698-
// it's only used for tests.
699-
std::vector<std::string> builtins_in_snapshot;
700-
701698
std::unordered_multimap<int, loader::ModuleWrap*> hash_to_module_map;
702699
std::unordered_map<uint32_t, loader::ModuleWrap*> id_to_module_map;
703700
std::unordered_map<uint32_t, contextify::ContextifyScript*>

Diff for: graal-nodejs/src/node_binding.cc

+70-25
Original file line numberDiff line numberDiff line change
@@ -110,6 +110,12 @@
110110
NODE_BUILTIN_BINDINGS(V)
111111
#undef V
112112

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

239245
using v8::Context;
246+
using v8::EscapableHandleScope;
240247
using v8::Exception;
241-
using v8::Function;
242248
using v8::FunctionCallbackInfo;
249+
using v8::FunctionTemplate;
250+
using v8::HandleScope;
251+
using v8::Isolate;
243252
using v8::Local;
244253
using v8::Object;
245254
using v8::String;
@@ -584,50 +593,86 @@ inline struct node_module* FindModule(struct node_module* list,
584593
return mp;
585594
}
586595

587-
static Local<Object> InitInternalBinding(Environment* env,
588-
node_module* mod,
589-
Local<String> module) {
590-
// Internal bindings don't have a "module" object, only exports.
591-
Local<Function> ctor = env->binding_data_ctor_template()
592-
->GetFunction(env->context())
593-
.ToLocalChecked();
594-
Local<Object> exports = ctor->NewInstance(env->context()).ToLocalChecked();
596+
void CreateInternalBindingTemplates(IsolateData* isolate_data) {
597+
#define V(modname) \
598+
do { \
599+
Local<FunctionTemplate> templ = \
600+
FunctionTemplate::New(isolate_data->isolate()); \
601+
templ->InstanceTemplate()->SetInternalFieldCount( \
602+
BaseObject::kInternalFieldCount); \
603+
templ->Inherit(BaseObject::GetConstructorTemplate(isolate_data)); \
604+
_register_isolate_##modname(isolate_data, templ); \
605+
isolate_data->set_##modname##_binding(templ); \
606+
} while (0);
607+
NODE_BINDINGS_WITH_PER_ISOLATE_INIT(V)
608+
#undef V
609+
}
610+
611+
static Local<Object> GetInternalBindingExportObject(IsolateData* isolate_data,
612+
const char* mod_name,
613+
Local<Context> context) {
614+
Local<FunctionTemplate> ctor;
615+
#define V(name) \
616+
if (strcmp(mod_name, #name) == 0) { \
617+
ctor = isolate_data->name##_binding(); \
618+
} else // NOLINT(readability/braces)
619+
NODE_BINDINGS_WITH_PER_ISOLATE_INIT(V)
620+
#undef V
621+
{
622+
ctor = isolate_data->binding_data_ctor_template();
623+
}
624+
625+
Local<Object> obj = ctor->GetFunction(context)
626+
.ToLocalChecked()
627+
->NewInstance(context)
628+
.ToLocalChecked();
629+
return obj;
630+
}
631+
632+
static Local<Object> InitInternalBinding(Realm* realm, node_module* mod) {
633+
EscapableHandleScope scope(realm->isolate());
634+
Local<Context> context = realm->context();
635+
Local<Object> exports = GetInternalBindingExportObject(
636+
realm->isolate_data(), mod->nm_modname, context);
595637
CHECK_NULL(mod->nm_register_func);
596638
CHECK_NOT_NULL(mod->nm_context_register_func);
597-
Local<Value> unused = Undefined(env->isolate());
598-
mod->nm_context_register_func(exports, unused, env->context(), mod->nm_priv);
599-
return exports;
639+
Local<Value> unused = Undefined(realm->isolate());
640+
// Internal bindings don't have a "module" object, only exports.
641+
mod->nm_context_register_func(exports, unused, context, mod->nm_priv);
642+
return scope.Escape(exports);
600643
}
601644

602645
void GetInternalBinding(const FunctionCallbackInfo<Value>& args) {
603-
Environment* env = Environment::GetCurrent(args);
646+
Realm* realm = Realm::GetCurrent(args);
647+
Isolate* isolate = realm->isolate();
648+
HandleScope scope(isolate);
649+
Local<Context> context = realm->context();
604650

605651
CHECK(args[0]->IsString());
606652

607653
Local<String> module = args[0].As<String>();
608-
node::Utf8Value module_v(env->isolate(), module);
654+
node::Utf8Value module_v(isolate, module);
609655
Local<Object> exports;
610656

611657
node_module* mod = FindModule(modlist_internal, *module_v, NM_F_INTERNAL);
612658
if (mod != nullptr) {
613-
exports = InitInternalBinding(env, mod, module);
614-
env->internal_bindings.insert(mod);
659+
exports = InitInternalBinding(realm, mod);
660+
realm->internal_bindings.insert(mod);
615661
} else if (!strcmp(*module_v, "constants")) {
616-
exports = Object::New(env->isolate());
617-
CHECK(
618-
exports->SetPrototype(env->context(), Null(env->isolate())).FromJust());
619-
DefineConstants(env->isolate(), exports);
662+
exports = Object::New(isolate);
663+
CHECK(exports->SetPrototype(context, Null(isolate)).FromJust());
664+
DefineConstants(isolate, exports);
620665
} else if (!strcmp(*module_v, "natives")) {
621-
exports = builtins::BuiltinLoader::GetSourceObject(env->context());
666+
exports = builtins::BuiltinLoader::GetSourceObject(context);
622667
// Legacy feature: process.binding('natives').config contains stringified
623668
// config.gypi
624669
CHECK(exports
625-
->Set(env->context(),
626-
env->config_string(),
627-
builtins::BuiltinLoader::GetConfigString(env->isolate()))
670+
->Set(context,
671+
realm->isolate_data()->config_string(),
672+
builtins::BuiltinLoader::GetConfigString(isolate))
628673
.FromJust());
629674
} else {
630-
return THROW_ERR_INVALID_MODULE(env, "No such binding: %s", *module_v);
675+
return THROW_ERR_INVALID_MODULE(isolate, "No such binding: %s", *module_v);
631676
}
632677

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

Diff for: graal-nodejs/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, \
@@ -51,9 +53,20 @@ node::addon_context_register_func get_node_api_context_register_func(
5153

5254
namespace node {
5355

56+
// Define a node internal binding that may be loaded in a context of
57+
// a node::Environment.
58+
// If an internal binding needs initializing per-isolate templates, define
59+
// with NODE_BINDING_PER_ISOLATE_INIT too.
5460
#define NODE_BINDING_CONTEXT_AWARE_INTERNAL(modname, regfunc) \
5561
NODE_BINDING_CONTEXT_AWARE_CPP(modname, regfunc, nullptr, NM_F_INTERNAL)
5662

63+
// Define a per-isolate initialization function for a node internal binding.
64+
#define NODE_BINDING_PER_ISOLATE_INIT(modname, per_isolate_func) \
65+
void _register_isolate_##modname(node::IsolateData* isolate_data, \
66+
v8::Local<v8::FunctionTemplate> target) { \
67+
per_isolate_func(isolate_data, target); \
68+
}
69+
5770
// Globals per process
5871
// This is set by node::Init() which is used by embedders
5972
extern bool node_is_initialized;
@@ -94,6 +107,8 @@ class DLib {
94107
// use the __attribute__((constructor)). Need to
95108
// explicitly call the _register* functions.
96109
void RegisterBuiltinBindings();
110+
// Create per-isolate templates for the internal bindings.
111+
void CreateInternalBindingTemplates(IsolateData* isolate_data);
97112
void GetInternalBinding(const v8::FunctionCallbackInfo<v8::Value>& args);
98113
void GetLinkedBinding(const v8::FunctionCallbackInfo<v8::Value>& args);
99114
void DLOpen(const v8::FunctionCallbackInfo<v8::Value>& args);

0 commit comments

Comments
 (0)