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 #ifdef _AIX
114 // On AIX, dlopen() behaves differently from other operating systems, in that
115 // it returns unique values from each call, rather than identical values, when
116 // loading the same handle.
117 // We try to work around that by providing wrappers for the dlopen() family of
118 // functions, and using st_dev and st_ino for the file that is to be loaded
119 // as keys for a cache.
120
121 namespace node {
122 namespace dlwrapper {
123
124 struct dl_wrap {
125 uint64_t st_dev;
126 uint64_t st_ino;
127 uint64_t refcount;
128 void* real_handle;
129
130 struct hash {
operator ()node::dlwrapper::dl_wrap::hash131 size_t operator()(const dl_wrap* wrap) const {
132 return std::hash<uint64_t>()(wrap->st_dev) ^
133 std::hash<uint64_t>()(wrap->st_ino);
134 }
135 };
136
137 struct equal {
operator ()node::dlwrapper::dl_wrap::equal138 bool operator()(const dl_wrap* a,
139 const dl_wrap* b) const {
140 return a->st_dev == b->st_dev && a->st_ino == b->st_ino;
141 }
142 };
143 };
144
145 static Mutex dlhandles_mutex;
146 static std::unordered_set<dl_wrap*, dl_wrap::hash, dl_wrap::equal>
147 dlhandles;
148 static thread_local std::string dlerror_storage;
149
wrapped_dlerror()150 char* wrapped_dlerror() {
151 return &dlerror_storage[0];
152 }
153
wrapped_dlopen(const char * filename,int flags)154 void* wrapped_dlopen(const char* filename, int flags) {
155 CHECK_NOT_NULL(filename); // This deviates from the 'real' dlopen().
156 Mutex::ScopedLock lock(dlhandles_mutex);
157
158 uv_fs_t req;
159 auto cleanup = OnScopeLeave([&]() { uv_fs_req_cleanup(&req); });
160 int rc = uv_fs_stat(nullptr, &req, filename, nullptr);
161
162 if (rc != 0) {
163 dlerror_storage = uv_strerror(rc);
164 return nullptr;
165 }
166
167 dl_wrap search = {
168 req.statbuf.st_dev,
169 req.statbuf.st_ino,
170 0, nullptr
171 };
172
173 auto it = dlhandles.find(&search);
174 if (it != dlhandles.end()) {
175 (*it)->refcount++;
176 return *it;
177 }
178
179 void* real_handle = dlopen(filename, flags);
180 if (real_handle == nullptr) {
181 dlerror_storage = dlerror();
182 return nullptr;
183 }
184 dl_wrap* wrap = new dl_wrap();
185 wrap->st_dev = req.statbuf.st_dev;
186 wrap->st_ino = req.statbuf.st_ino;
187 wrap->refcount = 1;
188 wrap->real_handle = real_handle;
189 dlhandles.insert(wrap);
190 return wrap;
191 }
192
wrapped_dlclose(void * handle)193 int wrapped_dlclose(void* handle) {
194 Mutex::ScopedLock lock(dlhandles_mutex);
195 dl_wrap* wrap = static_cast<dl_wrap*>(handle);
196 int ret = 0;
197 CHECK_GE(wrap->refcount, 1);
198 if (--wrap->refcount == 0) {
199 ret = dlclose(wrap->real_handle);
200 if (ret != 0) dlerror_storage = dlerror();
201 dlhandles.erase(wrap);
202 delete wrap;
203 }
204 return ret;
205 }
206
wrapped_dlsym(void * handle,const char * symbol)207 void* wrapped_dlsym(void* handle, const char* symbol) {
208 if (handle == RTLD_DEFAULT || handle == RTLD_NEXT)
209 return dlsym(handle, symbol);
210 dl_wrap* wrap = static_cast<dl_wrap*>(handle);
211 return dlsym(wrap->real_handle, symbol);
212 }
213
214 #define dlopen node::dlwrapper::wrapped_dlopen
215 #define dlerror node::dlwrapper::wrapped_dlerror
216 #define dlclose node::dlwrapper::wrapped_dlclose
217 #define dlsym node::dlwrapper::wrapped_dlsym
218
219 } // namespace dlwrapper
220 } // namespace node
221
222 #endif // _AIX
223
224 #ifdef __linux__
libc_may_be_musl()225 static bool libc_may_be_musl() {
226 static std::atomic_bool retval; // Cache the return value.
227 static std::atomic_bool has_cached_retval { false };
228 if (has_cached_retval) return retval;
229 retval = dlsym(RTLD_DEFAULT, "gnu_get_libc_version") == nullptr;
230 has_cached_retval = true;
231 return retval;
232 }
233 #elif defined(__POSIX__)
libc_may_be_musl()234 static bool libc_may_be_musl() { return false; }
235 #endif // __linux__
236
237 namespace node {
238
239 using v8::Context;
240 using v8::Exception;
241 using v8::Function;
242 using v8::FunctionCallbackInfo;
243 using v8::Local;
244 using v8::Object;
245 using v8::String;
246 using v8::Value;
247
248 // Globals per process
249 static node_module* modlist_internal;
250 static node_module* modlist_linked;
251 static thread_local node_module* thread_local_modpending;
252
253 // This is set by node::Init() which is used by embedders
254 bool node_is_initialized = false;
255
node_module_register(void * m)256 extern "C" void node_module_register(void* m) {
257 struct node_module* mp = reinterpret_cast<struct node_module*>(m);
258
259 if (mp->nm_flags & NM_F_INTERNAL) {
260 mp->nm_link = modlist_internal;
261 modlist_internal = mp;
262 } else if (!node_is_initialized) {
263 // "Linked" modules are included as part of the node project.
264 // Like builtins they are registered *before* node::Init runs.
265 mp->nm_flags = NM_F_LINKED;
266 mp->nm_link = modlist_linked;
267 modlist_linked = mp;
268 } else {
269 thread_local_modpending = mp;
270 }
271 }
272
273 namespace binding {
274
275 static struct global_handle_map_t {
276 public:
setnode::binding::global_handle_map_t277 void set(void* handle, node_module* mod) {
278 CHECK_NE(handle, nullptr);
279 Mutex::ScopedLock lock(mutex_);
280
281 map_[handle].module = mod;
282 // We need to store this flag internally to avoid a chicken-and-egg problem
283 // during cleanup. By the time we actually use the flag's value,
284 // the shared object has been unloaded, and its memory would be gone,
285 // making it impossible to access fields of `mod` --
286 // unless `mod` *is* dynamically allocated, but we cannot know that
287 // without checking the flag.
288 map_[handle].wants_delete_module = mod->nm_flags & NM_F_DELETEME;
289 map_[handle].refcount++;
290 }
291
get_and_increase_refcountnode::binding::global_handle_map_t292 node_module* get_and_increase_refcount(void* handle) {
293 CHECK_NE(handle, nullptr);
294 Mutex::ScopedLock lock(mutex_);
295
296 auto it = map_.find(handle);
297 if (it == map_.end()) return nullptr;
298 it->second.refcount++;
299 return it->second.module;
300 }
301
erasenode::binding::global_handle_map_t302 void erase(void* handle) {
303 CHECK_NE(handle, nullptr);
304 Mutex::ScopedLock lock(mutex_);
305
306 auto it = map_.find(handle);
307 if (it == map_.end()) return;
308 CHECK_GE(it->second.refcount, 1);
309 if (--it->second.refcount == 0) {
310 if (it->second.wants_delete_module)
311 delete it->second.module;
312 map_.erase(handle);
313 }
314 }
315
316 private:
317 Mutex mutex_;
318 struct Entry {
319 unsigned int refcount;
320 bool wants_delete_module;
321 node_module* module;
322 };
323 std::unordered_map<void*, Entry> map_;
324 } global_handle_map;
325
DLib(const char * filename,int flags)326 DLib::DLib(const char* filename, int flags)
327 : filename_(filename), flags_(flags), handle_(nullptr) {}
328
329 #ifdef __POSIX__
Open()330 bool DLib::Open() {
331 handle_ = dlopen(filename_.c_str(), flags_);
332 if (handle_ != nullptr) return true;
333 errmsg_ = dlerror();
334 return false;
335 }
336
Close()337 void DLib::Close() {
338 if (handle_ == nullptr) return;
339
340 if (libc_may_be_musl()) {
341 // musl libc implements dlclose() as a no-op which returns 0.
342 // As a consequence, trying to re-load a previously closed addon at a later
343 // point will not call its static constructors, which Node.js uses.
344 // Therefore, when we may be using musl libc, we assume that the shared
345 // object exists indefinitely and keep it in our handle map.
346 return;
347 }
348
349 int err = dlclose(handle_);
350 if (err == 0) {
351 if (has_entry_in_global_handle_map_)
352 global_handle_map.erase(handle_);
353 }
354 handle_ = nullptr;
355 }
356
GetSymbolAddress(const char * name)357 void* DLib::GetSymbolAddress(const char* name) {
358 return dlsym(handle_, name);
359 }
360 #else // !__POSIX__
Open()361 bool DLib::Open() {
362 int ret = uv_dlopen(filename_.c_str(), &lib_);
363 if (ret == 0) {
364 handle_ = static_cast<void*>(lib_.handle);
365 return true;
366 }
367 errmsg_ = uv_dlerror(&lib_);
368 uv_dlclose(&lib_);
369 return false;
370 }
371
Close()372 void DLib::Close() {
373 if (handle_ == nullptr) return;
374 if (has_entry_in_global_handle_map_)
375 global_handle_map.erase(handle_);
376 uv_dlclose(&lib_);
377 handle_ = nullptr;
378 }
379
GetSymbolAddress(const char * name)380 void* DLib::GetSymbolAddress(const char* name) {
381 void* address;
382 if (0 == uv_dlsym(&lib_, name, &address)) return address;
383 return nullptr;
384 }
385 #endif // !__POSIX__
386
SaveInGlobalHandleMap(node_module * mp)387 void DLib::SaveInGlobalHandleMap(node_module* mp) {
388 has_entry_in_global_handle_map_ = true;
389 global_handle_map.set(handle_, mp);
390 }
391
GetSavedModuleFromGlobalHandleMap()392 node_module* DLib::GetSavedModuleFromGlobalHandleMap() {
393 has_entry_in_global_handle_map_ = true;
394 return global_handle_map.get_and_increase_refcount(handle_);
395 }
396
397 using InitializerCallback = void (*)(Local<Object> exports,
398 Local<Value> module,
399 Local<Context> context);
400
GetInitializerCallback(DLib * dlib)401 inline InitializerCallback GetInitializerCallback(DLib* dlib) {
402 const char* name = "node_register_module_v" STRINGIFY(NODE_MODULE_VERSION);
403 return reinterpret_cast<InitializerCallback>(dlib->GetSymbolAddress(name));
404 }
405
GetNapiInitializerCallback(DLib * dlib)406 inline napi_addon_register_func GetNapiInitializerCallback(DLib* dlib) {
407 const char* name =
408 STRINGIFY(NAPI_MODULE_INITIALIZER_BASE) STRINGIFY(NAPI_MODULE_VERSION);
409 return reinterpret_cast<napi_addon_register_func>(
410 dlib->GetSymbolAddress(name));
411 }
412
GetNapiAddonGetApiVersionCallback(DLib * dlib)413 inline node_api_addon_get_api_version_func GetNapiAddonGetApiVersionCallback(
414 DLib* dlib) {
415 return reinterpret_cast<node_api_addon_get_api_version_func>(
416 dlib->GetSymbolAddress(STRINGIFY(NODE_API_MODULE_GET_API_VERSION)));
417 }
418
419 // DLOpen is process.dlopen(module, filename, flags).
420 // Used to load 'module.node' dynamically shared objects.
421 //
422 // FIXME(bnoordhuis) Not multi-context ready. TBD how to resolve the conflict
423 // when two contexts try to load the same shared object. Maybe have a shadow
424 // cache that's a plain C list or hash table that's shared across contexts?
DLOpen(const FunctionCallbackInfo<Value> & args)425 void DLOpen(const FunctionCallbackInfo<Value>& args) {
426 Environment* env = Environment::GetCurrent(args);
427
428 if (env->no_native_addons()) {
429 return THROW_ERR_DLOPEN_DISABLED(
430 env, "Cannot load native addon because loading addons is disabled.");
431 }
432
433 auto context = env->context();
434
435 CHECK_NULL(thread_local_modpending);
436
437 if (args.Length() < 2) {
438 return THROW_ERR_MISSING_ARGS(
439 env, "process.dlopen needs at least 2 arguments");
440 }
441
442 int32_t flags = DLib::kDefaultFlags;
443 if (args.Length() > 2 && !args[2]->Int32Value(context).To(&flags)) {
444 return THROW_ERR_INVALID_ARG_TYPE(env, "flag argument must be an integer.");
445 }
446
447 Local<Object> module;
448 Local<Object> exports;
449 Local<Value> exports_v;
450 if (!args[0]->ToObject(context).ToLocal(&module) ||
451 !module->Get(context, env->exports_string()).ToLocal(&exports_v) ||
452 !exports_v->ToObject(context).ToLocal(&exports)) {
453 return; // Exception pending.
454 }
455
456 node::Utf8Value filename(env->isolate(), args[1]); // Cast
457 env->TryLoadAddon(*filename, flags, [&](DLib* dlib) {
458 static Mutex dlib_load_mutex;
459 Mutex::ScopedLock lock(dlib_load_mutex);
460
461 const bool is_opened = dlib->Open();
462
463 // Objects containing v14 or later modules will have registered themselves
464 // on the pending list. Activate all of them now. At present, only one
465 // module per object is supported.
466 node_module* mp = thread_local_modpending;
467 thread_local_modpending = nullptr;
468
469 if (!is_opened) {
470 std::string errmsg = dlib->errmsg_.c_str();
471 dlib->Close();
472 #ifdef _WIN32
473 // Windows needs to add the filename into the error message
474 errmsg += *filename;
475 #endif // _WIN32
476 THROW_ERR_DLOPEN_FAILED(env, "%s", errmsg.c_str());
477 return false;
478 }
479
480 if (mp != nullptr) {
481 if (mp->nm_context_register_func == nullptr) {
482 if (env->force_context_aware()) {
483 dlib->Close();
484 THROW_ERR_NON_CONTEXT_AWARE_DISABLED(env);
485 return false;
486 }
487 }
488 mp->nm_dso_handle = dlib->handle_;
489 dlib->SaveInGlobalHandleMap(mp);
490 } else {
491 if (auto callback = GetInitializerCallback(dlib)) {
492 callback(exports, module, context);
493 return true;
494 } else if (auto napi_callback = GetNapiInitializerCallback(dlib)) {
495 int32_t module_api_version = NODE_API_DEFAULT_MODULE_API_VERSION;
496 if (auto get_version = GetNapiAddonGetApiVersionCallback(dlib)) {
497 module_api_version = get_version();
498 }
499 napi_module_register_by_symbol(
500 exports, module, context, napi_callback, module_api_version);
501 return true;
502 } else {
503 mp = dlib->GetSavedModuleFromGlobalHandleMap();
504 if (mp == nullptr || mp->nm_context_register_func == nullptr) {
505 dlib->Close();
506 THROW_ERR_DLOPEN_FAILED(
507 env, "Module did not self-register: '%s'.", *filename);
508 return false;
509 }
510 }
511 }
512
513 // -1 is used for N-API modules
514 if ((mp->nm_version != -1) && (mp->nm_version != NODE_MODULE_VERSION)) {
515 // Even if the module did self-register, it may have done so with the
516 // wrong version. We must only give up after having checked to see if it
517 // has an appropriate initializer callback.
518 if (auto callback = GetInitializerCallback(dlib)) {
519 callback(exports, module, context);
520 return true;
521 }
522
523 const int actual_nm_version = mp->nm_version;
524 // NOTE: `mp` is allocated inside of the shared library's memory, calling
525 // `dlclose` will deallocate it
526 dlib->Close();
527 THROW_ERR_DLOPEN_FAILED(
528 env,
529 "The module '%s'"
530 "\nwas compiled against a different Node.js version using"
531 "\nNODE_MODULE_VERSION %d. This version of Node.js requires"
532 "\nNODE_MODULE_VERSION %d. Please try re-compiling or "
533 "re-installing\nthe module (for instance, using `npm rebuild` "
534 "or `npm install`).",
535 *filename,
536 actual_nm_version,
537 NODE_MODULE_VERSION);
538 return false;
539 }
540 CHECK_EQ(mp->nm_flags & NM_F_BUILTIN, 0);
541
542 // Do not keep the lock while running userland addon loading code.
543 Mutex::ScopedUnlock unlock(lock);
544 if (mp->nm_context_register_func != nullptr) {
545 mp->nm_context_register_func(exports, module, context, mp->nm_priv);
546 } else if (mp->nm_register_func != nullptr) {
547 mp->nm_register_func(exports, module, mp->nm_priv);
548 } else {
549 dlib->Close();
550 THROW_ERR_DLOPEN_FAILED(env, "Module has no declared entry point.");
551 return false;
552 }
553
554 return true;
555 });
556
557 // Tell coverity that 'handle' should not be freed when we return.
558 // coverity[leaked_storage]
559 }
560
FindModule(struct node_module * list,const char * name,int flag)561 inline struct node_module* FindModule(struct node_module* list,
562 const char* name,
563 int flag) {
564 struct node_module* mp;
565
566 for (mp = list; mp != nullptr; mp = mp->nm_link) {
567 if (strcmp(mp->nm_modname, name) == 0) break;
568 }
569
570 CHECK(mp == nullptr || (mp->nm_flags & flag) != 0);
571 return mp;
572 }
573
InitInternalBinding(Environment * env,node_module * mod,Local<String> module)574 static Local<Object> InitInternalBinding(Environment* env,
575 node_module* mod,
576 Local<String> module) {
577 // Internal bindings don't have a "module" object, only exports.
578 Local<Function> ctor = env->binding_data_ctor_template()
579 ->GetFunction(env->context())
580 .ToLocalChecked();
581 Local<Object> exports = ctor->NewInstance(env->context()).ToLocalChecked();
582 CHECK_NULL(mod->nm_register_func);
583 CHECK_NOT_NULL(mod->nm_context_register_func);
584 Local<Value> unused = Undefined(env->isolate());
585 mod->nm_context_register_func(exports, unused, env->context(), mod->nm_priv);
586 return exports;
587 }
588
GetInternalBinding(const FunctionCallbackInfo<Value> & args)589 void GetInternalBinding(const FunctionCallbackInfo<Value>& args) {
590 Environment* env = Environment::GetCurrent(args);
591
592 CHECK(args[0]->IsString());
593
594 Local<String> module = args[0].As<String>();
595 node::Utf8Value module_v(env->isolate(), module);
596 Local<Object> exports;
597
598 node_module* mod = FindModule(modlist_internal, *module_v, NM_F_INTERNAL);
599 if (mod != nullptr) {
600 exports = InitInternalBinding(env, mod, module);
601 env->internal_bindings.insert(mod);
602 } else if (!strcmp(*module_v, "constants")) {
603 exports = Object::New(env->isolate());
604 CHECK(
605 exports->SetPrototype(env->context(), Null(env->isolate())).FromJust());
606 DefineConstants(env->isolate(), exports);
607 } else if (!strcmp(*module_v, "natives")) {
608 exports = builtins::BuiltinLoader::GetSourceObject(env->context());
609 // Legacy feature: process.binding('natives').config contains stringified
610 // config.gypi
611 CHECK(exports
612 ->Set(env->context(),
613 env->config_string(),
614 builtins::BuiltinLoader::GetConfigString(env->isolate()))
615 .FromJust());
616 } else {
617 return THROW_ERR_INVALID_MODULE(env, "No such binding: %s", *module_v);
618 }
619
620 args.GetReturnValue().Set(exports);
621 }
622
GetLinkedBinding(const FunctionCallbackInfo<Value> & args)623 void GetLinkedBinding(const FunctionCallbackInfo<Value>& args) {
624 Environment* env = Environment::GetCurrent(args);
625
626 CHECK(args[0]->IsString());
627
628 Local<String> module_name = args[0].As<String>();
629
630 node::Utf8Value module_name_v(env->isolate(), module_name);
631 const char* name = *module_name_v;
632 node_module* mod = nullptr;
633
634 // Iterate from here to the nearest non-Worker Environment to see if there's
635 // a linked binding defined locally rather than through the global list.
636 Environment* cur_env = env;
637 while (mod == nullptr && cur_env != nullptr) {
638 Mutex::ScopedLock lock(cur_env->extra_linked_bindings_mutex());
639 mod = FindModule(cur_env->extra_linked_bindings_head(), name, NM_F_LINKED);
640 cur_env = cur_env->worker_parent_env();
641 }
642
643 if (mod == nullptr)
644 mod = FindModule(modlist_linked, name, NM_F_LINKED);
645
646 if (mod == nullptr) {
647 return THROW_ERR_INVALID_MODULE(
648 env, "No such binding was linked: %s", *module_name_v);
649 }
650
651 Local<Object> module = Object::New(env->isolate());
652 Local<Object> exports = Object::New(env->isolate());
653 Local<String> exports_prop =
654 String::NewFromUtf8Literal(env->isolate(), "exports");
655 module->Set(env->context(), exports_prop, exports).Check();
656
657 if (mod->nm_context_register_func != nullptr) {
658 mod->nm_context_register_func(
659 exports, module, env->context(), mod->nm_priv);
660 } else if (mod->nm_register_func != nullptr) {
661 mod->nm_register_func(exports, module, mod->nm_priv);
662 } else {
663 return THROW_ERR_INVALID_MODULE(
664 env, "Linked binding has no declared entry point.");
665 }
666
667 auto effective_exports =
668 module->Get(env->context(), exports_prop).ToLocalChecked();
669
670 args.GetReturnValue().Set(effective_exports);
671 }
672
673 // Call built-in bindings' _register_<module name> function to
674 // do binding registration explicitly.
RegisterBuiltinBindings()675 void RegisterBuiltinBindings() {
676 #define V(modname) _register_##modname();
677 NODE_BUILTIN_BINDINGS(V)
678 #undef V
679 }
680
RegisterExternalReferences(ExternalReferenceRegistry * registry)681 void RegisterExternalReferences(ExternalReferenceRegistry* registry) {
682 registry->Register(GetLinkedBinding);
683 registry->Register(GetInternalBinding);
684 }
685
686 } // namespace binding
687 } // namespace node
688
689 NODE_BINDING_EXTERNAL_REFERENCE(binding,
690 node::binding::RegisterExternalReferences)
691