Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

src: allow adding linked bindings to Environment #30274

Closed
wants to merge 2 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 28 additions & 0 deletions src/api/environment.cc
Original file line number Diff line number Diff line change
Expand Up @@ -498,4 +498,32 @@ uv_loop_t* GetCurrentEventLoop(Isolate* isolate) {
return env->event_loop();
}

void AddLinkedBinding(Environment* env, const node_module& mod) {
devsnek marked this conversation as resolved.
Show resolved Hide resolved
CHECK_NOT_NULL(env);
Mutex::ScopedLock lock(env->extra_linked_bindings_mutex());

node_module* prev_head = env->extra_linked_bindings_head();
env->extra_linked_bindings()->push_back(mod);
if (prev_head != nullptr)
prev_head->nm_link = &env->extra_linked_bindings()->back();
}

void AddLinkedBinding(Environment* env,
const char* name,
addon_context_register_func fn,
void* priv) {
node_module mod = {
NODE_MODULE_VERSION,
NM_F_LINKED,
nullptr, // nm_dso_handle
nullptr, // nm_filename
nullptr, // nm_register_func
fn,
name,
priv,
nullptr // nm_link
};
AddLinkedBinding(env, mod);
}

} // namespace node
13 changes: 13 additions & 0 deletions src/env-inl.h
Original file line number Diff line number Diff line change
Expand Up @@ -876,6 +876,19 @@ inline bool Environment::is_stopping() const {
return thread_stopper_.is_stopped();
}

inline std::list<node_module>* Environment::extra_linked_bindings() {
return &extra_linked_bindings_;
}

inline node_module* Environment::extra_linked_bindings_head() {
return extra_linked_bindings_.size() > 0 ?
&extra_linked_bindings_.front() : nullptr;
}

inline const Mutex& Environment::extra_linked_bindings_mutex() const {
return extra_linked_bindings_mutex_;
}

inline performance::performance_state* Environment::performance_state() {
return performance_state_.get();
}
Expand Down
7 changes: 4 additions & 3 deletions src/env.cc
Original file line number Diff line number Diff line change
Expand Up @@ -935,9 +935,10 @@ void Environment::stop_sub_worker_contexts() {
}
}

#if HAVE_INSPECTOR

#endif // HAVE_INSPECTOR
Environment* Environment::worker_parent_env() const {
if (worker_context_ == nullptr) return nullptr;
return worker_context_->env();
}

void MemoryTracker::TrackField(const char* edge_name,
const CleanupHookCallback& value,
Expand Down
7 changes: 7 additions & 0 deletions src/env.h
Original file line number Diff line number Diff line change
Expand Up @@ -1082,11 +1082,15 @@ class Environment : public MemoryRetainer {
inline bool owns_inspector() const;
inline uint64_t thread_id() const;
inline worker::Worker* worker_context() const;
Environment* worker_parent_env() const;
inline void set_worker_context(worker::Worker* context);
inline void add_sub_worker_context(worker::Worker* context);
inline void remove_sub_worker_context(worker::Worker* context);
void stop_sub_worker_contexts();
inline bool is_stopping() const;
inline std::list<node_module>* extra_linked_bindings();
inline node_module* extra_linked_bindings_head();
inline const Mutex& extra_linked_bindings_mutex() const;

inline void ThrowError(const char* errmsg);
inline void ThrowTypeError(const char* errmsg);
Expand Down Expand Up @@ -1382,6 +1386,9 @@ class Environment : public MemoryRetainer {

worker::Worker* worker_context_ = nullptr;

std::list<node_module> extra_linked_bindings_;
Mutex extra_linked_bindings_mutex_;

static void RunTimers(uv_timer_t* handle);

struct ExitCallback {
Expand Down
11 changes: 11 additions & 0 deletions src/node.h
Original file line number Diff line number Diff line change
Expand Up @@ -668,6 +668,17 @@ extern "C" NODE_EXTERN void node_module_register(void* mod);
v8::Local<v8::Value> module, \
v8::Local<v8::Context> context)

// Allows embedders to add a binding to the current Environment* that can be
// accessed through process._linkedBinding() in the target Environment and all
// Worker threads that it creates.
// In each variant, the registration function needs to be usable at least for
// the time during which the Environment exists.
NODE_EXTERN void AddLinkedBinding(Environment* env, const node_module& mod);
NODE_EXTERN void AddLinkedBinding(Environment* env,
const char* name,
addon_context_register_func fn,
void* priv);

/* Called after the event loop exits but before the VM is disposed.
* Callbacks are run in reverse order of registration, i.e. newest first.
*/
Expand Down
24 changes: 15 additions & 9 deletions src/node_binding.cc
Original file line number Diff line number Diff line change
Expand Up @@ -552,13 +552,6 @@ inline struct node_module* FindModule(struct node_module* list,
return mp;
}

node_module* get_internal_module(const char* name) {
return FindModule(modlist_internal, name, NM_F_INTERNAL);
}
node_module* get_linked_module(const char* name) {
return FindModule(modlist_linked, name, NM_F_LINKED);
}

static Local<Object> InitModule(Environment* env,
node_module* mod,
Local<String> module) {
Expand Down Expand Up @@ -586,7 +579,7 @@ void GetInternalBinding(const FunctionCallbackInfo<Value>& args) {
node::Utf8Value module_v(env->isolate(), module);
Local<Object> exports;

node_module* mod = get_internal_module(*module_v);
node_module* mod = FindModule(modlist_internal, *module_v, NM_F_INTERNAL);
if (mod != nullptr) {
exports = InitModule(env, mod, module);
} else if (!strcmp(*module_v, "constants")) {
Expand Down Expand Up @@ -619,7 +612,20 @@ void GetLinkedBinding(const FunctionCallbackInfo<Value>& args) {
Local<String> module_name = args[0].As<String>();

node::Utf8Value module_name_v(env->isolate(), module_name);
node_module* mod = get_linked_module(*module_name_v);
const char* name = *module_name_v;
node_module* mod = nullptr;

// Iterate from here to the nearest non-Worker Environment to see if there's
// a linked binding defined locally rather than through the global list.
Environment* cur_env = env;
while (mod == nullptr && cur_env != nullptr) {
Mutex::ScopedLock lock(cur_env->extra_linked_bindings_mutex());
mod = FindModule(cur_env->extra_linked_bindings_head(), name, NM_F_LINKED);
cur_env = cur_env->worker_parent_env();
}
addaleax marked this conversation as resolved.
Show resolved Hide resolved

if (mod == nullptr)
mod = FindModule(modlist_linked, name, NM_F_LINKED);

if (mod == nullptr) {
char errmsg[1024];
Expand Down
43 changes: 43 additions & 0 deletions test/cctest/test_linked_binding.cc
Original file line number Diff line number Diff line change
Expand Up @@ -41,3 +41,46 @@ TEST_F(LinkedBindingTest, SimpleTest) {
CHECK_NOT_NULL(*utf8val);
CHECK_EQ(strcmp(*utf8val, "value"), 0);
}

void InitializeLocalBinding(v8::Local<v8::Object> exports,
v8::Local<v8::Value> module,
v8::Local<v8::Context> context,
void* priv) {
++*static_cast<int*>(priv);
v8::Isolate* isolate = context->GetIsolate();
exports->Set(
context,
v8::String::NewFromOneByte(isolate,
reinterpret_cast<const uint8_t*>("key"),
v8::NewStringType::kNormal).ToLocalChecked(),
v8::String::NewFromOneByte(isolate,
reinterpret_cast<const uint8_t*>("value"),
v8::NewStringType::kNormal).ToLocalChecked())
.FromJust();
}

TEST_F(LinkedBindingTest, LocallyDefinedLinkedBindingTest) {
const v8::HandleScope handle_scope(isolate_);
const Argv argv;
Env test_env {handle_scope, argv};

int calls = 0;
AddLinkedBinding(*test_env, "local_linked", InitializeLocalBinding, &calls);

v8::Local<v8::Context> context = isolate_->GetCurrentContext();

const char* run_script =
"process._linkedBinding('local_linked').key";
v8::Local<v8::Script> script = v8::Script::Compile(
context,
v8::String::NewFromOneByte(isolate_,
reinterpret_cast<const uint8_t*>(run_script),
v8::NewStringType::kNormal).ToLocalChecked())
.ToLocalChecked();
v8::Local<v8::Value> completion_value = script->Run(context).ToLocalChecked();
v8::String::Utf8Value utf8val(isolate_, completion_value);
CHECK_NOT_NULL(*utf8val);
CHECK_EQ(strcmp(*utf8val, "value"), 0);
CHECK_EQ(calls, 1);
}