Skip to content

Commit 3117ea6

Browse files
addaleaxMylesBorins
authored andcommitted
src: allow adding linked bindings to Environment
This allows manually adding linked bindings to an `Environment` instance, without having to register modules at program load in a global namespace. PR-URL: #30274 Reviewed-By: Gireesh Punathil <[email protected]> Reviewed-By: Ben Noordhuis <[email protected]>
1 parent 0beb8a6 commit 3117ea6

File tree

7 files changed

+120
-12
lines changed

7 files changed

+120
-12
lines changed

src/api/environment.cc

+28
Original file line numberDiff line numberDiff line change
@@ -498,4 +498,32 @@ uv_loop_t* GetCurrentEventLoop(Isolate* isolate) {
498498
return env->event_loop();
499499
}
500500

501+
void AddLinkedBinding(Environment* env, const node_module& mod) {
502+
CHECK_NOT_NULL(env);
503+
Mutex::ScopedLock lock(env->extra_linked_bindings_mutex());
504+
505+
node_module* prev_head = env->extra_linked_bindings_head();
506+
env->extra_linked_bindings()->push_back(mod);
507+
if (prev_head != nullptr)
508+
prev_head->nm_link = &env->extra_linked_bindings()->back();
509+
}
510+
511+
void AddLinkedBinding(Environment* env,
512+
const char* name,
513+
addon_context_register_func fn,
514+
void* priv) {
515+
node_module mod = {
516+
NODE_MODULE_VERSION,
517+
NM_F_LINKED,
518+
nullptr, // nm_dso_handle
519+
nullptr, // nm_filename
520+
nullptr, // nm_register_func
521+
fn,
522+
name,
523+
priv,
524+
nullptr // nm_link
525+
};
526+
AddLinkedBinding(env, mod);
527+
}
528+
501529
} // namespace node

src/env-inl.h

+13
Original file line numberDiff line numberDiff line change
@@ -868,6 +868,19 @@ inline bool Environment::is_stopping() const {
868868
return thread_stopper_.is_stopped();
869869
}
870870

871+
inline std::list<node_module>* Environment::extra_linked_bindings() {
872+
return &extra_linked_bindings_;
873+
}
874+
875+
inline node_module* Environment::extra_linked_bindings_head() {
876+
return extra_linked_bindings_.size() > 0 ?
877+
&extra_linked_bindings_.front() : nullptr;
878+
}
879+
880+
inline const Mutex& Environment::extra_linked_bindings_mutex() const {
881+
return extra_linked_bindings_mutex_;
882+
}
883+
871884
inline performance::performance_state* Environment::performance_state() {
872885
return performance_state_.get();
873886
}

src/env.cc

+4-3
Original file line numberDiff line numberDiff line change
@@ -934,9 +934,10 @@ void Environment::stop_sub_worker_contexts() {
934934
}
935935
}
936936

937-
#if HAVE_INSPECTOR
938-
939-
#endif // HAVE_INSPECTOR
937+
Environment* Environment::worker_parent_env() const {
938+
if (worker_context_ == nullptr) return nullptr;
939+
return worker_context_->env();
940+
}
940941

941942
void MemoryTracker::TrackField(const char* edge_name,
942943
const CleanupHookCallback& value,

src/env.h

+7
Original file line numberDiff line numberDiff line change
@@ -1074,11 +1074,15 @@ class Environment : public MemoryRetainer {
10741074
inline bool owns_inspector() const;
10751075
inline uint64_t thread_id() const;
10761076
inline worker::Worker* worker_context() const;
1077+
Environment* worker_parent_env() const;
10771078
inline void set_worker_context(worker::Worker* context);
10781079
inline void add_sub_worker_context(worker::Worker* context);
10791080
inline void remove_sub_worker_context(worker::Worker* context);
10801081
void stop_sub_worker_contexts();
10811082
inline bool is_stopping() const;
1083+
inline std::list<node_module>* extra_linked_bindings();
1084+
inline node_module* extra_linked_bindings_head();
1085+
inline const Mutex& extra_linked_bindings_mutex() const;
10821086

10831087
inline void ThrowError(const char* errmsg);
10841088
inline void ThrowTypeError(const char* errmsg);
@@ -1374,6 +1378,9 @@ class Environment : public MemoryRetainer {
13741378

13751379
worker::Worker* worker_context_ = nullptr;
13761380

1381+
std::list<node_module> extra_linked_bindings_;
1382+
Mutex extra_linked_bindings_mutex_;
1383+
13771384
static void RunTimers(uv_timer_t* handle);
13781385

13791386
struct ExitCallback {

src/node.h

+11
Original file line numberDiff line numberDiff line change
@@ -662,6 +662,17 @@ extern "C" NODE_EXTERN void node_module_register(void* mod);
662662
v8::Local<v8::Value> module, \
663663
v8::Local<v8::Context> context)
664664

665+
// Allows embedders to add a binding to the current Environment* that can be
666+
// accessed through process._linkedBinding() in the target Environment and all
667+
// Worker threads that it creates.
668+
// In each variant, the registration function needs to be usable at least for
669+
// the time during which the Environment exists.
670+
NODE_EXTERN void AddLinkedBinding(Environment* env, const node_module& mod);
671+
NODE_EXTERN void AddLinkedBinding(Environment* env,
672+
const char* name,
673+
addon_context_register_func fn,
674+
void* priv);
675+
665676
/* Called after the event loop exits but before the VM is disposed.
666677
* Callbacks are run in reverse order of registration, i.e. newest first.
667678
*

src/node_binding.cc

+15-9
Original file line numberDiff line numberDiff line change
@@ -558,13 +558,6 @@ inline struct node_module* FindModule(struct node_module* list,
558558
return mp;
559559
}
560560

561-
node_module* get_internal_module(const char* name) {
562-
return FindModule(modlist_internal, name, NM_F_INTERNAL);
563-
}
564-
node_module* get_linked_module(const char* name) {
565-
return FindModule(modlist_linked, name, NM_F_LINKED);
566-
}
567-
568561
static Local<Object> InitModule(Environment* env,
569562
node_module* mod,
570563
Local<String> module) {
@@ -592,7 +585,7 @@ void GetInternalBinding(const FunctionCallbackInfo<Value>& args) {
592585
node::Utf8Value module_v(env->isolate(), module);
593586
Local<Object> exports;
594587

595-
node_module* mod = get_internal_module(*module_v);
588+
node_module* mod = FindModule(modlist_internal, *module_v, NM_F_INTERNAL);
596589
if (mod != nullptr) {
597590
exports = InitModule(env, mod, module);
598591
} else if (!strcmp(*module_v, "constants")) {
@@ -625,7 +618,20 @@ void GetLinkedBinding(const FunctionCallbackInfo<Value>& args) {
625618
Local<String> module_name = args[0].As<String>();
626619

627620
node::Utf8Value module_name_v(env->isolate(), module_name);
628-
node_module* mod = get_linked_module(*module_name_v);
621+
const char* name = *module_name_v;
622+
node_module* mod = nullptr;
623+
624+
// Iterate from here to the nearest non-Worker Environment to see if there's
625+
// a linked binding defined locally rather than through the global list.
626+
Environment* cur_env = env;
627+
while (mod == nullptr && cur_env != nullptr) {
628+
Mutex::ScopedLock lock(cur_env->extra_linked_bindings_mutex());
629+
mod = FindModule(cur_env->extra_linked_bindings_head(), name, NM_F_LINKED);
630+
cur_env = cur_env->worker_parent_env();
631+
}
632+
633+
if (mod == nullptr)
634+
mod = FindModule(modlist_linked, name, NM_F_LINKED);
629635

630636
if (mod == nullptr) {
631637
char errmsg[1024];

test/cctest/test_linked_binding.cc

+42
Original file line numberDiff line numberDiff line change
@@ -41,3 +41,45 @@ TEST_F(LinkedBindingTest, SimpleTest) {
4141
CHECK_NOT_NULL(*utf8val);
4242
CHECK_EQ(strcmp(*utf8val, "value"), 0);
4343
}
44+
45+
void InitializeLocalBinding(v8::Local<v8::Object> exports,
46+
v8::Local<v8::Value> module,
47+
v8::Local<v8::Context> context,
48+
void* priv) {
49+
++*static_cast<int*>(priv);
50+
v8::Isolate* isolate = context->GetIsolate();
51+
exports->Set(
52+
context,
53+
v8::String::NewFromOneByte(isolate,
54+
reinterpret_cast<const uint8_t*>("key"),
55+
v8::NewStringType::kNormal).ToLocalChecked(),
56+
v8::String::NewFromOneByte(isolate,
57+
reinterpret_cast<const uint8_t*>("value"),
58+
v8::NewStringType::kNormal).ToLocalChecked())
59+
.FromJust();
60+
}
61+
62+
TEST_F(LinkedBindingTest, LocallyDefinedLinkedBindingTest) {
63+
const v8::HandleScope handle_scope(isolate_);
64+
const Argv argv;
65+
Env test_env {handle_scope, argv};
66+
67+
int calls = 0;
68+
AddLinkedBinding(*test_env, "local_linked", InitializeLocalBinding, &calls);
69+
70+
v8::Local<v8::Context> context = isolate_->GetCurrentContext();
71+
72+
const char* run_script =
73+
"process._linkedBinding('local_linked').key";
74+
v8::Local<v8::Script> script = v8::Script::Compile(
75+
context,
76+
v8::String::NewFromOneByte(isolate_,
77+
reinterpret_cast<const uint8_t*>(run_script),
78+
v8::NewStringType::kNormal).ToLocalChecked())
79+
.ToLocalChecked();
80+
v8::Local<v8::Value> completion_value = script->Run(context).ToLocalChecked();
81+
v8::String::Utf8Value utf8val(isolate_, completion_value);
82+
CHECK_NOT_NULL(*utf8val);
83+
CHECK_EQ(strcmp(*utf8val, "value"), 0);
84+
CHECK_EQ(calls, 1);
85+
}

0 commit comments

Comments
 (0)