1 #include "node_binding.h"
2 #include <atomic>
3 #include "env-inl.h"
4 #include "node_builtins.h"
5 #include "node_errors.h"
6 #include "node_external_reference.h"
7 #include "util.h"
8
9 #include <string>
10
11 #if HAVE_OPENSSL
12 #define NODE_BUILTIN_OPENSSL_BINDINGS(V) V(crypto) V(tls_wrap)
13 #else
14 #define NODE_BUILTIN_OPENSSL_BINDINGS(V)
15 #endif
16
17 #if NODE_HAVE_I18N_SUPPORT
18 #define NODE_BUILTIN_ICU_BINDINGS(V) V(icu)
19 #else
20 #define NODE_BUILTIN_ICU_BINDINGS(V)
21 #endif
22
23 #if HAVE_INSPECTOR
24 #define NODE_BUILTIN_PROFILER_BINDINGS(V) V(profiler)
25 #else
26 #define NODE_BUILTIN_PROFILER_BINDINGS(V)
27 #endif
28
29 #if HAVE_DTRACE || HAVE_ETW
30 #define NODE_BUILTIN_DTRACE_BINDINGS(V) V(dtrace)
31 #else
32 #define NODE_BUILTIN_DTRACE_BINDINGS(V)
33 #endif
34
35 // A list of built-in bindings. In order to do binding registration
36 // in node::Init(), need to add built-in bindings in the following list.
37 // Then in binding::RegisterBuiltinBindings(), it calls bindings' registration
38 // function. This helps the built-in bindings are loaded properly when
39 // node is built as static library. No need to depend on the
40 // __attribute__((constructor)) like mechanism in GCC.
41 #define NODE_BUILTIN_STANDARD_BINDINGS(V) \
42 V(async_wrap) \
43 V(blob) \
44 V(block_list) \
45 V(buffer) \
46 V(builtins) \
47 V(cares_wrap) \
48 V(config) \
49 V(contextify) \
50 V(credentials) \
51 V(errors) \
52 V(fs) \
53 V(fs_dir) \
54 V(fs_event_wrap) \
55 V(heap_utils) \
56 V(http2) \
57 V(http_parser) \
58 V(inspector) \
59 V(js_stream) \
60 V(js_udp_wrap) \
61 V(messaging) \
62 V(module_wrap) \
63 V(mksnapshot) \
64 V(options) \
65 V(os) \
66 V(performance) \
67 V(pipe_wrap) \
68 V(process_wrap) \
69 V(process_methods) \
70 V(report) \
71 V(sea) \
72 V(serdes) \
73 V(signal_wrap) \
74 V(spawn_sync) \
75 V(stream_pipe) \
76 V(stream_wrap) \
77 V(string_decoder) \
78 V(symbols) \
79 V(task_queue) \
80 V(tcp_wrap) \
81 V(timers) \
82 V(trace_events) \
83 V(tty_wrap) \
84 V(types) \
85 V(udp_wrap) \
86 V(url) \
87 V(util) \
88 V(uv) \
89 V(v8) \
90 V(wasi) \
91 V(wasm_web_api) \
92 V(watchdog) \
93 V(worker) \
94 V(zlib)
95
96 #define NODE_BUILTIN_BINDINGS(V) \
97 NODE_BUILTIN_STANDARD_BINDINGS(V) \
98 NODE_BUILTIN_OPENSSL_BINDINGS(V) \
99 NODE_BUILTIN_ICU_BINDINGS(V) \
100 NODE_BUILTIN_PROFILER_BINDINGS(V) \
101 NODE_BUILTIN_DTRACE_BINDINGS(V)
102
103 // This is used to load built-in bindings. Instead of using
104 // __attribute__((constructor)), we call the _register_<modname>
105 // function for each built-in bindings explicitly in
106 // binding::RegisterBuiltinBindings(). This is only forward declaration.
107 // The definitions are in each binding's implementation when calling
108 // the NODE_BINDING_CONTEXT_AWARE_INTERNAL.
109 #define V(modname) void _register_##modname();
110 NODE_BUILTIN_BINDINGS(V)
111 #undef V
112
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
119 #ifdef _AIX
120 // On AIX, dlopen() behaves differently from other operating systems, in that
121 // it returns unique values from each call, rather than identical values, when
122 // loading the same handle.
123 // We try to work around that by providing wrappers for the dlopen() family of
124 // functions, and using st_dev and st_ino for the file that is to be loaded
125 // as keys for a cache.
126
127 namespace node {
128 namespace dlwrapper {
129
130 struct dl_wrap {
131 uint64_t st_dev;
132 uint64_t st_ino;
133 uint64_t refcount;
134 void* real_handle;
135
136 struct hash {
operator ()node::dlwrapper::dl_wrap::hash137 size_t operator()(const dl_wrap* wrap) const {
138 return std::hash<uint64_t>()(wrap->st_dev) ^
139 std::hash<uint64_t>()(wrap->st_ino);
140 }
141 };
142
143 struct equal {
operator ()node::dlwrapper::dl_wrap::equal144 bool operator()(const dl_wrap* a,
145 const dl_wrap* b) const {
146 return a->st_dev == b->st_dev && a->st_ino == b->st_ino;
147 }
148 };
149 };
150
151 static Mutex dlhandles_mutex;
152 static std::unordered_set<dl_wrap*, dl_wrap::hash, dl_wrap::equal>
153 dlhandles;
154 static thread_local std::string dlerror_storage;
155
wrapped_dlerror()156 char* wrapped_dlerror() {
157 return &dlerror_storage[0];
158 }
159
wrapped_dlopen(const char * filename,int flags)160 void* wrapped_dlopen(const char* filename, int flags) {
161 CHECK_NOT_NULL(filename); // This deviates from the 'real' dlopen().
162 Mutex::ScopedLock lock(dlhandles_mutex);
163
164 uv_fs_t req;
165 auto cleanup = OnScopeLeave([&]() { uv_fs_req_cleanup(&req); });
166 int rc = uv_fs_stat(nullptr, &req, filename, nullptr);
167
168 if (rc != 0) {
169 dlerror_storage = uv_strerror(rc);
170 return nullptr;
171 }
172
173 dl_wrap search = {
174 req.statbuf.st_dev,
175 req.statbuf.st_ino,
176 0, nullptr
177 };
178
179 auto it = dlhandles.find(&search);
180 if (it != dlhandles.end()) {
181 (*it)->refcount++;
182 return *it;
183 }
184
185 void* real_handle = dlopen(filename, flags);
186 if (real_handle == nullptr) {
187 dlerror_storage = dlerror();
188 return nullptr;
189 }
190 dl_wrap* wrap = new dl_wrap();
191 wrap->st_dev = req.statbuf.st_dev;
192 wrap->st_ino = req.statbuf.st_ino;
193 wrap->refcount = 1;
194 wrap->real_handle = real_handle;
195 dlhandles.insert(wrap);
196 return wrap;
197 }
198
wrapped_dlclose(void * handle)199 int wrapped_dlclose(void* handle) {
200 Mutex::ScopedLock lock(dlhandles_mutex);
201 dl_wrap* wrap = static_cast<dl_wrap*>(handle);
202 int ret = 0;
203 CHECK_GE(wrap->refcount, 1);
204 if (--wrap->refcount == 0) {
205 ret = dlclose(wrap->real_handle);
206 if (ret != 0) dlerror_storage = dlerror();
207 dlhandles.erase(wrap);
208 delete wrap;
209 }
210 return ret;
211 }
212
wrapped_dlsym(void * handle,const char * symbol)213 void* wrapped_dlsym(void* handle, const char* symbol) {
214 if (handle == RTLD_DEFAULT || handle == RTLD_NEXT)
215 return dlsym(handle, symbol);
216 dl_wrap* wrap = static_cast<dl_wrap*>(handle);
217 return dlsym(wrap->real_handle, symbol);
218 }
219
220 #define dlopen node::dlwrapper::wrapped_dlopen
221 #define dlerror node::dlwrapper::wrapped_dlerror
222 #define dlclose node::dlwrapper::wrapped_dlclose
223 #define dlsym node::dlwrapper::wrapped_dlsym
224
225 } // namespace dlwrapper
226 } // namespace node
227
228 #endif // _AIX
229
230 #ifdef __linux__
libc_may_be_musl()231 static bool libc_may_be_musl() {
232 static std::atomic_bool retval; // Cache the return value.
233 static std::atomic_bool has_cached_retval { false };
234 if (has_cached_retval) return retval;
235 retval = dlsym(RTLD_DEFAULT, "gnu_get_libc_version") == nullptr;
236 has_cached_retval = true;
237 return retval;
238 }
239 #elif defined(__POSIX__)
libc_may_be_musl()240 static bool libc_may_be_musl() { return false; }
241 #endif // __linux__
242
243 namespace node {
244
245 using v8::Context;
246 using v8::EscapableHandleScope;
247 using v8::Exception;
248 using v8::FunctionCallbackInfo;
249 using v8::FunctionTemplate;
250 using v8::HandleScope;
251 using v8::Isolate;
252 using v8::Local;
253 using v8::Object;
254 using v8::String;
255 using v8::Value;
256
257 // Globals per process
258 static node_module* modlist_internal;
259 static node_module* modlist_linked;
260 static thread_local node_module* thread_local_modpending;
261
262 // This is set by node::Init() which is used by embedders
263 bool node_is_initialized = false;
264
node_module_register(void * m)265 extern "C" void node_module_register(void* m) {
266 struct node_module* mp = reinterpret_cast<struct node_module*>(m);
267
268 if (mp->nm_flags & NM_F_INTERNAL) {
269 mp->nm_link = modlist_internal;
270 modlist_internal = mp;
271 } else if (!node_is_initialized) {
272 // "Linked" modules are included as part of the node project.
273 // Like builtins they are registered *before* node::Init runs.
274 mp->nm_flags = NM_F_LINKED;
275 mp->nm_link = modlist_linked;
276 modlist_linked = mp;
277 } else {
278 thread_local_modpending = mp;
279 }
280 }
281
282 namespace binding {
283
284 static struct global_handle_map_t {
285 public:
setnode::binding::global_handle_map_t286 void set(void* handle, node_module* mod) {
287 CHECK_NE(handle, nullptr);
288 Mutex::ScopedLock lock(mutex_);
289
290 map_[handle].module = mod;
291 // We need to store this flag internally to avoid a chicken-and-egg problem
292 // during cleanup. By the time we actually use the flag's value,
293 // the shared object has been unloaded, and its memory would be gone,
294 // making it impossible to access fields of `mod` --
295 // unless `mod` *is* dynamically allocated, but we cannot know that
296 // without checking the flag.
297 map_[handle].wants_delete_module = mod->nm_flags & NM_F_DELETEME;
298 map_[handle].refcount++;
299 }
300
get_and_increase_refcountnode::binding::global_handle_map_t301 node_module* get_and_increase_refcount(void* handle) {
302 CHECK_NE(handle, nullptr);
303 Mutex::ScopedLock lock(mutex_);
304
305 auto it = map_.find(handle);
306 if (it == map_.end()) return nullptr;
307 it->second.refcount++;
308 return it->second.module;
309 }
310
erasenode::binding::global_handle_map_t311 void erase(void* handle) {
312 CHECK_NE(handle, nullptr);
313 Mutex::ScopedLock lock(mutex_);
314
315 auto it = map_.find(handle);
316 if (it == map_.end()) return;
317 CHECK_GE(it->second.refcount, 1);
318 if (--it->second.refcount == 0) {
319 if (it->second.wants_delete_module)
320 delete it->second.module;
321 map_.erase(handle);
322 }
323 }
324
325 private:
326 Mutex mutex_;
327 struct Entry {
328 unsigned int refcount;
329 bool wants_delete_module;
330 node_module* module;
331 };
332 std::unordered_map<void*, Entry> map_;
333 } global_handle_map;
334
DLib(const char * filename,int flags)335 DLib::DLib(const char* filename, int flags)
336 : filename_(filename), flags_(flags), handle_(nullptr) {}
337
338 #ifdef __POSIX__
Open()339 bool DLib::Open() {
340 handle_ = dlopen(filename_.c_str(), flags_);
341 if (handle_ != nullptr) return true;
342 errmsg_ = dlerror();
343 return false;
344 }
345
Close()346 void DLib::Close() {
347 if (handle_ == nullptr) return;
348
349 if (libc_may_be_musl()) {
350 // musl libc implements dlclose() as a no-op which returns 0.
351 // As a consequence, trying to re-load a previously closed addon at a later
352 // point will not call its static constructors, which Node.js uses.
353 // Therefore, when we may be using musl libc, we assume that the shared
354 // object exists indefinitely and keep it in our handle map.
355 return;
356 }
357
358 int err = dlclose(handle_);
359 if (err == 0) {
360 if (has_entry_in_global_handle_map_)
361 global_handle_map.erase(handle_);
362 }
363 handle_ = nullptr;
364 }
365
GetSymbolAddress(const char * name)366 void* DLib::GetSymbolAddress(const char* name) {
367 return dlsym(handle_, name);
368 }
369 #else // !__POSIX__
Open()370 bool DLib::Open() {
371 int ret = uv_dlopen(filename_.c_str(), &lib_);
372 if (ret == 0) {
373 handle_ = static_cast<void*>(lib_.handle);
374 return true;
375 }
376 errmsg_ = uv_dlerror(&lib_);
377 uv_dlclose(&lib_);
378 return false;
379 }
380
Close()381 void DLib::Close() {
382 if (handle_ == nullptr) return;
383 if (has_entry_in_global_handle_map_)
384 global_handle_map.erase(handle_);
385 uv_dlclose(&lib_);
386 handle_ = nullptr;
387 }
388
GetSymbolAddress(const char * name)389 void* DLib::GetSymbolAddress(const char* name) {
390 void* address;
391 if (0 == uv_dlsym(&lib_, name, &address)) return address;
392 return nullptr;
393 }
394 #endif // !__POSIX__
395
SaveInGlobalHandleMap(node_module * mp)396 void DLib::SaveInGlobalHandleMap(node_module* mp) {
397 has_entry_in_global_handle_map_ = true;
398 global_handle_map.set(handle_, mp);
399 }
400
GetSavedModuleFromGlobalHandleMap()401 node_module* DLib::GetSavedModuleFromGlobalHandleMap() {
402 has_entry_in_global_handle_map_ = true;
403 return global_handle_map.get_and_increase_refcount(handle_);
404 }
405
406 using InitializerCallback = void (*)(Local<Object> exports,
407 Local<Value> module,
408 Local<Context> context);
409
GetInitializerCallback(DLib * dlib)410 inline InitializerCallback GetInitializerCallback(DLib* dlib) {
411 const char* name = "node_register_module_v" STRINGIFY(NODE_MODULE_VERSION);
412 return reinterpret_cast<InitializerCallback>(dlib->GetSymbolAddress(name));
413 }
414
GetNapiInitializerCallback(DLib * dlib)415 inline jsvm_addon_register_func GetNapiInitializerCallback(DLib* dlib) {
416 const char* name =
417 STRINGIFY(JSVM_MODULE_INITIALIZER_BASE) STRINGIFY(JSVM_MODULE_VERSION);
418 return reinterpret_cast<jsvm_addon_register_func>(
419 dlib->GetSymbolAddress(name));
420 }
421
GetNapiAddonGetApiVersionCallback(DLib * dlib)422 inline node_api_addon_get_api_version_func GetNapiAddonGetApiVersionCallback(
423 DLib* dlib) {
424 return reinterpret_cast<node_api_addon_get_api_version_func>(
425 dlib->GetSymbolAddress(STRINGIFY(NODE_API_MODULE_GET_API_VERSION)));
426 }
427
428 // DLOpen is process.dlopen(module, filename, flags).
429 // Used to load 'module.node' dynamically shared objects.
430 //
431 // FIXME(bnoordhuis) Not multi-context ready. TBD how to resolve the conflict
432 // when two contexts try to load the same shared object. Maybe have a shadow
433 // cache that's a plain C list or hash table that's shared across contexts?
DLOpen(const FunctionCallbackInfo<Value> & args)434 void DLOpen(const FunctionCallbackInfo<Value>& args) {
435 Environment* env = Environment::GetCurrent(args);
436
437 if (env->no_native_addons()) {
438 return THROW_ERR_DLOPEN_DISABLED(
439 env, "Cannot load native addon because loading addons is disabled.");
440 }
441
442 auto context = env->context();
443
444 CHECK_NULL(thread_local_modpending);
445
446 if (args.Length() < 2) {
447 return THROW_ERR_MISSING_ARGS(
448 env, "process.dlopen needs at least 2 arguments");
449 }
450
451 int32_t flags = DLib::kDefaultFlags;
452 if (args.Length() > 2 && !args[2]->Int32Value(context).To(&flags)) {
453 return THROW_ERR_INVALID_ARG_TYPE(env, "flag argument must be an integer.");
454 }
455
456 Local<Object> module;
457 Local<Object> exports;
458 Local<Value> exports_v;
459 if (!args[0]->ToObject(context).ToLocal(&module) ||
460 !module->Get(context, env->exports_string()).ToLocal(&exports_v) ||
461 !exports_v->ToObject(context).ToLocal(&exports)) {
462 return; // Exception pending.
463 }
464
465 node::Utf8Value filename(env->isolate(), args[1]); // Cast
466 env->TryLoadAddon(*filename, flags, [&](DLib* dlib) {
467 static Mutex dlib_load_mutex;
468 Mutex::ScopedLock lock(dlib_load_mutex);
469
470 const bool is_opened = dlib->Open();
471
472 // Objects containing v14 or later modules will have registered themselves
473 // on the pending list. Activate all of them now. At present, only one
474 // module per object is supported.
475 node_module* mp = thread_local_modpending;
476 thread_local_modpending = nullptr;
477
478 if (!is_opened) {
479 std::string errmsg = dlib->errmsg_.c_str();
480 dlib->Close();
481 #ifdef _WIN32
482 // Windows needs to add the filename into the error message
483 errmsg += *filename;
484 #endif // _WIN32
485 THROW_ERR_DLOPEN_FAILED(env, "%s", errmsg.c_str());
486 return false;
487 }
488
489 if (mp != nullptr) {
490 if (mp->nm_context_register_func == nullptr) {
491 if (env->force_context_aware()) {
492 dlib->Close();
493 THROW_ERR_NON_CONTEXT_AWARE_DISABLED(env);
494 return false;
495 }
496 }
497 mp->nm_dso_handle = dlib->handle_;
498 dlib->SaveInGlobalHandleMap(mp);
499 } else {
500 if (auto callback = GetInitializerCallback(dlib)) {
501 callback(exports, module, context);
502 return true;
503 } else if (auto JSVM_Callback = GetNapiInitializerCallback(dlib)) {
504 int32_t module_api_version = NODE_API_DEFAULT_MODULE_API_VERSION;
505 if (auto get_version = GetNapiAddonGetApiVersionCallback(dlib)) {
506 module_api_version = get_version();
507 }
508 jsvm_module_register_by_symbol(
509 exports, module, context, JSVM_Callback, module_api_version);
510 return true;
511 } else {
512 mp = dlib->GetSavedModuleFromGlobalHandleMap();
513 if (mp == nullptr || mp->nm_context_register_func == nullptr) {
514 dlib->Close();
515 THROW_ERR_DLOPEN_FAILED(
516 env, "Module did not self-register: '%s'.", *filename);
517 return false;
518 }
519 }
520 }
521
522 // -1 is used for N-API modules
523 if ((mp->nm_version != -1) && (mp->nm_version != NODE_MODULE_VERSION)) {
524 // Even if the module did self-register, it may have done so with the
525 // wrong version. We must only give up after having checked to see if it
526 // has an appropriate initializer callback.
527 if (auto callback = GetInitializerCallback(dlib)) {
528 callback(exports, module, context);
529 return true;
530 }
531
532 const int actual_nm_version = mp->nm_version;
533 // NOTE: `mp` is allocated inside of the shared library's memory, calling
534 // `dlclose` will deallocate it
535 dlib->Close();
536 THROW_ERR_DLOPEN_FAILED(
537 env,
538 "The module '%s'"
539 "\nwas compiled against a different Node.js version using"
540 "\nNODE_MODULE_VERSION %d. This version of Node.js requires"
541 "\nNODE_MODULE_VERSION %d. Please try re-compiling or "
542 "re-installing\nthe module (for instance, using `npm rebuild` "
543 "or `npm install`).",
544 *filename,
545 actual_nm_version,
546 NODE_MODULE_VERSION);
547 return false;
548 }
549 CHECK_EQ(mp->nm_flags & NM_F_BUILTIN, 0);
550
551 // Do not keep the lock while running userland addon loading code.
552 Mutex::ScopedUnlock unlock(lock);
553 if (mp->nm_context_register_func != nullptr) {
554 mp->nm_context_register_func(exports, module, context, mp->nm_priv);
555 } else if (mp->nm_register_func != nullptr) {
556 mp->nm_register_func(exports, module, mp->nm_priv);
557 } else {
558 dlib->Close();
559 THROW_ERR_DLOPEN_FAILED(env, "Module has no declared entry point.");
560 return false;
561 }
562
563 return true;
564 });
565
566 // Tell coverity that 'handle' should not be freed when we return.
567 // coverity[leaked_storage]
568 }
569
FindModule(struct node_module * list,const char * name,int flag)570 inline struct node_module* FindModule(struct node_module* list,
571 const char* name,
572 int flag) {
573 struct node_module* mp;
574
575 for (mp = list; mp != nullptr; mp = mp->nm_link) {
576 if (strcmp(mp->nm_modname, name) == 0) break;
577 }
578
579 CHECK(mp == nullptr || (mp->nm_flags & flag) != 0);
580 return mp;
581 }
582
CreateInternalBindingTemplates(IsolateData * isolate_data)583 void CreateInternalBindingTemplates(IsolateData* isolate_data) {
584 #define V(modname) \
585 do { \
586 Local<FunctionTemplate> templ = \
587 FunctionTemplate::New(isolate_data->isolate()); \
588 templ->InstanceTemplate()->SetInternalFieldCount( \
589 BaseObject::kInternalFieldCount); \
590 templ->Inherit(BaseObject::GetConstructorTemplate(isolate_data)); \
591 _register_isolate_##modname(isolate_data, templ); \
592 isolate_data->set_##modname##_binding(templ); \
593 } while (0);
594 NODE_BINDINGS_WITH_PER_ISOLATE_INIT(V)
595 #undef V
596 }
597
GetInternalBindingExportObject(IsolateData * isolate_data,const char * mod_name,Local<Context> context)598 static Local<Object> GetInternalBindingExportObject(IsolateData* isolate_data,
599 const char* mod_name,
600 Local<Context> context) {
601 Local<FunctionTemplate> ctor;
602 #define V(name) \
603 if (strcmp(mod_name, #name) == 0) { \
604 ctor = isolate_data->name##_binding(); \
605 } else // NOLINT(readability/braces)
606 NODE_BINDINGS_WITH_PER_ISOLATE_INIT(V)
607 #undef V
608 {
609 ctor = isolate_data->binding_data_ctor_template();
610 }
611
612 Local<Object> obj = ctor->GetFunction(context)
613 .ToLocalChecked()
614 ->NewInstance(context)
615 .ToLocalChecked();
616 return obj;
617 }
618
InitInternalBinding(Realm * realm,node_module * mod)619 static Local<Object> InitInternalBinding(Realm* realm, node_module* mod) {
620 EscapableHandleScope scope(realm->isolate());
621 Local<Context> context = realm->context();
622 Local<Object> exports = GetInternalBindingExportObject(
623 realm->isolate_data(), mod->nm_modname, context);
624 CHECK_NULL(mod->nm_register_func);
625 CHECK_NOT_NULL(mod->nm_context_register_func);
626 Local<Value> unused = Undefined(realm->isolate());
627 // Internal bindings don't have a "module" object, only exports.
628 mod->nm_context_register_func(exports, unused, context, mod->nm_priv);
629 return scope.Escape(exports);
630 }
631
GetInternalBinding(const FunctionCallbackInfo<Value> & args)632 void GetInternalBinding(const FunctionCallbackInfo<Value>& args) {
633 Realm* realm = Realm::GetCurrent(args);
634 Isolate* isolate = realm->isolate();
635 HandleScope scope(isolate);
636 Local<Context> context = realm->context();
637
638 CHECK(args[0]->IsString());
639
640 Local<String> module = args[0].As<String>();
641 node::Utf8Value module_v(isolate, module);
642 Local<Object> exports;
643
644 node_module* mod = FindModule(modlist_internal, *module_v, NM_F_INTERNAL);
645 if (mod != nullptr) {
646 exports = InitInternalBinding(realm, mod);
647 realm->internal_bindings.insert(mod);
648 } else if (!strcmp(*module_v, "constants")) {
649 exports = Object::New(isolate);
650 CHECK(exports->SetPrototype(context, Null(isolate)).FromJust());
651 DefineConstants(isolate, exports);
652 } else if (!strcmp(*module_v, "natives")) {
653 exports = realm->env()->builtin_loader()->GetSourceObject(context);
654 // Legacy feature: process.binding('natives').config contains stringified
655 // config.gypi
656 CHECK(exports
657 ->Set(context,
658 realm->isolate_data()->config_string(),
659 realm->env()->builtin_loader()->GetConfigString(isolate))
660 .FromJust());
661 } else {
662 return THROW_ERR_INVALID_MODULE(isolate, "No such binding: %s", *module_v);
663 }
664
665 args.GetReturnValue().Set(exports);
666 }
667
GetLinkedBinding(const FunctionCallbackInfo<Value> & args)668 void GetLinkedBinding(const FunctionCallbackInfo<Value>& args) {
669 Environment* env = Environment::GetCurrent(args);
670
671 CHECK(args[0]->IsString());
672
673 Local<String> module_name = args[0].As<String>();
674
675 node::Utf8Value module_name_v(env->isolate(), module_name);
676 const char* name = *module_name_v;
677 node_module* mod = nullptr;
678
679 // Iterate from here to the nearest non-Worker Environment to see if there's
680 // a linked binding defined locally rather than through the global list.
681 Environment* cur_env = env;
682 while (mod == nullptr && cur_env != nullptr) {
683 Mutex::ScopedLock lock(cur_env->extra_linked_bindings_mutex());
684 mod = FindModule(cur_env->extra_linked_bindings_head(), name, NM_F_LINKED);
685 cur_env = cur_env->worker_parent_env();
686 }
687
688 if (mod == nullptr)
689 mod = FindModule(modlist_linked, name, NM_F_LINKED);
690
691 if (mod == nullptr) {
692 return THROW_ERR_INVALID_MODULE(
693 env, "No such binding was linked: %s", *module_name_v);
694 }
695
696 Local<Object> module = Object::New(env->isolate());
697 Local<Object> exports = Object::New(env->isolate());
698 Local<String> exports_prop =
699 String::NewFromUtf8Literal(env->isolate(), "exports");
700 module->Set(env->context(), exports_prop, exports).Check();
701
702 if (mod->nm_context_register_func != nullptr) {
703 mod->nm_context_register_func(
704 exports, module, env->context(), mod->nm_priv);
705 } else if (mod->nm_register_func != nullptr) {
706 mod->nm_register_func(exports, module, mod->nm_priv);
707 } else {
708 return THROW_ERR_INVALID_MODULE(
709 env, "Linked binding has no declared entry point.");
710 }
711
712 auto effective_exports =
713 module->Get(env->context(), exports_prop).ToLocalChecked();
714
715 args.GetReturnValue().Set(effective_exports);
716 }
717
718 // Call built-in bindings' _register_<module name> function to
719 // do binding registration explicitly.
RegisterBuiltinBindings()720 void RegisterBuiltinBindings() {
721 #define V(modname) _register_##modname();
722 NODE_BUILTIN_BINDINGS(V)
723 #undef V
724 }
725
RegisterExternalReferences(ExternalReferenceRegistry * registry)726 void RegisterExternalReferences(ExternalReferenceRegistry* registry) {
727 registry->Register(GetLinkedBinding);
728 registry->Register(GetInternalBinding);
729 }
730
731 } // namespace binding
732 } // namespace node
733
734 NODE_BINDING_EXTERNAL_REFERENCE(binding,
735 node::binding::RegisterExternalReferences)
736