Skip to content

Commit

Permalink
src: allow adding linked bindings to Environment
Browse files Browse the repository at this point in the history
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 <gpunathi@in.ibm.com>
Reviewed-By: Ben Noordhuis <info@bnoordhuis.nl>
  • Loading branch information
addaleax authored and MylesBorins committed Jan 12, 2020
1 parent 0beb8a6 commit 3117ea6
Show file tree
Hide file tree
Showing 7 changed files with 120 additions and 12 deletions.
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) {
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 @@ -868,6 +868,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 @@ -934,9 +934,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 @@ -1074,11 +1074,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 @@ -1374,6 +1378,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 @@ -662,6 +662,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 @@ -558,13 +558,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 @@ -592,7 +585,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 @@ -625,7 +618,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();
}

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

if (mod == nullptr) {
char errmsg[1024];
Expand Down
42 changes: 42 additions & 0 deletions test/cctest/test_linked_binding.cc
Original file line number Diff line number Diff line change
Expand Up @@ -41,3 +41,45 @@ 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);
}

0 comments on commit 3117ea6

Please sign in to comment.