Skip to content

Commit d115a82

Browse files
committed
src: define realm-aware internal bindings
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.
1 parent 5664822 commit d115a82

13 files changed

+236
-129
lines changed

src/env-inl.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -827,6 +827,7 @@ void Environment::set_process_exit_handler(
827827
#undef VY
828828
#undef VP
829829

830+
#define VM(PropertyName) V(PropertyName##_binding, v8::FunctionTemplate)
830831
#define V(PropertyName, TypeName) \
831832
inline v8::Local<TypeName> IsolateData::PropertyName() const { \
832833
return PropertyName##_.Get(isolate_); \
@@ -835,7 +836,9 @@ void Environment::set_process_exit_handler(
835836
PropertyName##_.Set(isolate_, value); \
836837
}
837838
PER_ISOLATE_TEMPLATE_PROPERTIES(V)
839+
NODE_BUILTIN_REALM_AWARE_MODULES(VM)
838840
#undef V
841+
#undef VM
839842

840843
#define VP(PropertyName, StringValue) V(v8::Private, PropertyName)
841844
#define VY(PropertyName, StringValue) V(v8::Symbol, PropertyName)

src/env.cc

Lines changed: 5 additions & 20 deletions
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_BUILTIN_REALM_AWARE_MODULES(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_BUILTIN_REALM_AWARE_MODULES(VM);
391395
#undef V
392396
}
393397

@@ -460,6 +464,7 @@ void IsolateData::CreateProperties() {
460464
BaseObject::kInternalFieldCount);
461465
templ->Inherit(BaseObject::GetConstructorTemplate(this));
462466
set_binding_data_ctor_template(templ);
467+
binding::CreateInternalBindingTemplates(this);
463468

464469
contextify::ContextifyContext::InitializeGlobalTemplates(this);
465470
}
@@ -1581,30 +1586,13 @@ void Environment::PrintInfoForSnapshotIfDebug() {
15811586
if (enabled_debug_list()->enabled(DebugCategory::MKSNAPSHOT)) {
15821587
fprintf(stderr, "At the exit of the Environment:\n");
15831588
principal_realm()->PrintInfoForSnapshot();
1584-
fprintf(stderr, "\nNative modules without cache:\n");
1585-
for (const auto& s : builtins_without_cache) {
1586-
fprintf(stderr, "%s\n", s.c_str());
1587-
}
1588-
fprintf(stderr, "\nNative modules 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-
}
15961589
}
15971590
}
15981591

15991592
EnvSerializeInfo Environment::Serialize(SnapshotCreator* creator) {
16001593
EnvSerializeInfo info;
16011594
Local<Context> ctx = context();
16021595

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-
16081596
info.async_hooks = async_hooks_.Serialize(ctx, creator);
16091597
info.immediate_info = immediate_info_.Serialize(ctx, creator);
16101598
info.tick_info = tick_info_.Serialize(ctx, creator);
@@ -1650,7 +1638,6 @@ void Environment::DeserializeProperties(const EnvSerializeInfo* info) {
16501638

16511639
RunDeserializeRequests();
16521640

1653-
builtins_in_snapshot = info->builtins;
16541641
async_hooks_.Deserialize(ctx);
16551642
immediate_info_.Deserialize(ctx);
16561643
tick_info_.Deserialize(ctx);
@@ -1841,8 +1828,6 @@ void Environment::MemoryInfo(MemoryTracker* tracker) const {
18411828
// Iteratable STLs have their own sizes subtracted from the parent
18421829
// by default.
18431830
tracker->TrackField("isolate_data", isolate_data_);
1844-
tracker->TrackField("builtins_with_cache", builtins_with_cache);
1845-
tracker->TrackField("builtins_without_cache", builtins_without_cache);
18461831
tracker->TrackField("destroy_async_id_list", destroy_async_id_list_);
18471832
tracker->TrackField("exec_argv", exec_argv_);
18481833
tracker->TrackField("exit_info", exit_info_);

src/env.h

Lines changed: 6 additions & 9 deletions
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_BUILTIN_REALM_AWARE_MODULES(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_BUILTIN_REALM_AWARE_MODULES(VM)
189194
#undef V
190-
#undef V
195+
#undef VM
191196
#undef VT
192197
#undef VS
193198
#undef VY
@@ -460,7 +465,6 @@ struct DeserializeRequest {
460465
};
461466

462467
struct EnvSerializeInfo {
463-
std::vector<std::string> builtins;
464468
AsyncHooks::SerializeInfo async_hooks;
465469
TickInfo::SerializeInfo tick_info;
466470
ImmediateInfo::SerializeInfo immediate_info;
@@ -725,13 +729,6 @@ class Environment : public MemoryRetainer {
725729
// List of id's that have been destroyed and need the destroy() cb called.
726730
inline std::vector<double>* destroy_async_id_list();
727731

728-
std::set<struct node_module*> internal_bindings;
729-
std::set<std::string> builtins_with_cache;
730-
std::set<std::string> builtins_without_cache;
731-
// This is only filled during deserialization. We use a vector since
732-
// it's only used for tests.
733-
std::vector<std::string> builtins_in_snapshot;
734-
735732
std::unordered_multimap<int, loader::ModuleWrap*> hash_to_module_map;
736733
std::unordered_map<uint32_t, loader::ModuleWrap*> id_to_module_map;
737734
std::unordered_map<uint32_t, contextify::ContextifyScript*>

src/node_binding.cc

Lines changed: 72 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,6 @@
3737
V(blob) \
3838
V(block_list) \
3939
V(buffer) \
40-
V(builtins) \
4140
V(cares_wrap) \
4241
V(config) \
4342
V(contextify) \
@@ -87,6 +86,7 @@
8786
V(zlib)
8887

8988
#define NODE_BUILTIN_MODULES(V) \
89+
NODE_BUILTIN_REALM_AWARE_MODULES(V) \
9090
NODE_BUILTIN_STANDARD_MODULES(V) \
9191
NODE_BUILTIN_OPENSSL_MODULES(V) \
9292
NODE_BUILTIN_ICU_MODULES(V) \
@@ -102,6 +102,12 @@
102102
NODE_BUILTIN_MODULES(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_BUILTIN_REALM_AWARE_MODULES(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,11 +235,16 @@ static bool libc_may_be_musl() { return false; }
229235
namespace node {
230236

231237
using v8::Context;
238+
using v8::EscapableHandleScope;
232239
using v8::Exception;
233240
using v8::Function;
234241
using v8::FunctionCallbackInfo;
242+
using v8::FunctionTemplate;
243+
using v8::HandleScope;
244+
using v8::Isolate;
235245
using v8::Local;
236246
using v8::Object;
247+
using v8::ObjectTemplate;
237248
using v8::String;
238249
using v8::Value;
239250

@@ -552,50 +563,86 @@ inline struct node_module* FindModule(struct node_module* list,
552563
return mp;
553564
}
554565

555-
static Local<Object> InitModule(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();
566+
void CreateInternalBindingTemplates(IsolateData* isolate_data) {
567+
#define V(modname) \
568+
do { \
569+
Local<FunctionTemplate> templ = \
570+
FunctionTemplate::New(isolate_data->isolate()); \
571+
templ->InstanceTemplate()->SetInternalFieldCount( \
572+
BaseObject::kInternalFieldCount); \
573+
templ->Inherit(BaseObject::GetConstructorTemplate(isolate_data)); \
574+
_register_isolate_##modname(isolate_data, templ); \
575+
isolate_data->set_##modname##_binding(templ); \
576+
} while (0);
577+
NODE_BUILTIN_REALM_AWARE_MODULES(V)
578+
#undef V
579+
}
580+
581+
static Local<Object> GetInternalBindingExportObject(IsolateData* isolate_data,
582+
const char* mod_name,
583+
Local<Context> context) {
584+
Local<FunctionTemplate> ctor;
585+
#define V(name) \
586+
if (strcmp(mod_name, #name) == 0) { \
587+
ctor = isolate_data->name##_binding(); \
588+
} else
589+
NODE_BUILTIN_REALM_AWARE_MODULES(V)
590+
#undef V
591+
{
592+
ctor = isolate_data->binding_data_ctor_template();
593+
}
594+
595+
Local<Object> obj = ctor->GetFunction(context)
596+
.ToLocalChecked()
597+
->NewInstance(context)
598+
.ToLocalChecked();
599+
return obj;
600+
}
601+
602+
static Local<Object> InitInternalModule(Realm* realm, node_module* mod) {
603+
EscapableHandleScope scope(realm->isolate());
604+
Local<Context> context = realm->context();
605+
Local<Object> exports = GetInternalBindingExportObject(
606+
realm->isolate_data(), mod->nm_modname, context);
563607
CHECK_NULL(mod->nm_register_func);
564608
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;
609+
Local<Value> unused = Undefined(realm->isolate());
610+
// Internal bindings don't have a "module" object, only exports.
611+
mod->nm_context_register_func(exports, unused, context, mod->nm_priv);
612+
return scope.Escape(exports);
568613
}
569614

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

573621
CHECK(args[0]->IsString());
574622

575623
Local<String> module = args[0].As<String>();
576-
node::Utf8Value module_v(env->isolate(), module);
624+
node::Utf8Value module_v(isolate, module);
577625
Local<Object> exports;
578626

579627
node_module* mod = FindModule(modlist_internal, *module_v, NM_F_INTERNAL);
580628
if (mod != nullptr) {
581-
exports = InitModule(env, mod, module);
582-
env->internal_bindings.insert(mod);
629+
exports = InitInternalModule(realm, mod);
630+
realm->internal_bindings.insert(mod);
583631
} 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);
632+
exports = Object::New(isolate);
633+
CHECK(exports->SetPrototype(context, Null(isolate)).FromJust());
634+
DefineConstants(isolate, exports);
588635
} else if (!strcmp(*module_v, "natives")) {
589-
exports = builtins::BuiltinLoader::GetSourceObject(env->context());
636+
exports = builtins::BuiltinLoader::GetSourceObject(context);
590637
// Legacy feature: process.binding('natives').config contains stringified
591638
// config.gypi
592639
CHECK(exports
593-
->Set(env->context(),
594-
env->config_string(),
595-
builtins::BuiltinLoader::GetConfigString(env->isolate()))
640+
->Set(context,
641+
realm->isolate_data()->config_string(),
642+
builtins::BuiltinLoader::GetConfigString(isolate))
596643
.FromJust());
597644
} else {
598-
return THROW_ERR_INVALID_MODULE(env, "No such module: %s", *module_v);
645+
return THROW_ERR_INVALID_MODULE(isolate, "No such binding: %s", *module_v);
599646
}
600647

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

src/node_binding.h

Lines changed: 19 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_BUILTIN_REALM_AWARE_MODULES(V) V(builtins)
28+
2729
#define NODE_MODULE_CONTEXT_AWARE_CPP(modname, regfunc, priv, flags) \
2830
static node::node_module _module = { \
2931
NODE_MODULE_VERSION, \
@@ -44,9 +46,24 @@ void napi_module_register_by_symbol(v8::Local<v8::Object> exports,
4446

4547
namespace node {
4648

49+
// Define a node internal binding that can be loaded in distinct contexts of
50+
// node::Environment, of the main thread as well as worker threads.
51+
// If a internal binding needs to be loaded in distinct contexts of
52+
// node::Realm, define with NODE_MODULE_REALM_AWARE_INTERNAL instead.
4753
#define NODE_MODULE_CONTEXT_AWARE_INTERNAL(modname, regfunc) \
4854
NODE_MODULE_CONTEXT_AWARE_CPP(modname, regfunc, nullptr, NM_F_INTERNAL)
4955

56+
// Define a node internal binding that can be loaded in distinct contexts of
57+
// node::Realm in the same node::Environment (and v8::Isolate).
58+
#define NODE_MODULE_REALM_AWARE_INTERNAL( \
59+
modname, per_isolate_func, per_realm_func) \
60+
NODE_MODULE_CONTEXT_AWARE_CPP( \
61+
modname, per_realm_func, nullptr, NM_F_INTERNAL) \
62+
void _register_isolate_##modname(node::IsolateData* isolate_data, \
63+
v8::Local<v8::FunctionTemplate> target) { \
64+
per_isolate_func(isolate_data, target); \
65+
}
66+
5067
// Globals per process
5168
// This is set by node::Init() which is used by embedders
5269
extern bool node_is_initialized;
@@ -87,6 +104,8 @@ class DLib {
87104
// use the __attribute__((constructor)). Need to
88105
// explicitly call the _register* functions.
89106
void RegisterBuiltinModules();
107+
// Create per-isolate templates for the internal bindings that are realm-aware.
108+
void CreateInternalBindingTemplates(IsolateData* isolate_data);
90109
void GetInternalBinding(const v8::FunctionCallbackInfo<v8::Value>& args);
91110
void GetLinkedBinding(const v8::FunctionCallbackInfo<v8::Value>& args);
92111
void DLOpen(const v8::FunctionCallbackInfo<v8::Value>& args);

0 commit comments

Comments
 (0)