From 3d668262a09ef031003babc14eff9e5d4ac81698 Mon Sep 17 00:00:00 2001 From: Joyee Cheung Date: Thu, 29 Nov 2018 00:46:45 +0800 Subject: [PATCH] src: move C++ binding/addon related code into node_binding{.h, .cc} This patch: - Moves the C++ binding/addon related code out of node_internals.h/node.cc and into dedicated files node_binding.h/node_binding.cc, and only puts the code resued by other files into the header. - Introduce a node::binding namespace so that code exposed to other files can be easily recognized. PR-URL: https://github.com/nodejs/node/pull/24701 Reviewed-By: Ben Noordhuis Reviewed-By: Richard Lau --- node.gyp | 2 + src/node.cc | 409 +------------------------------------ src/node_api.cc | 3 +- src/node_binding.cc | 471 +++++++++++++++++++++++++++++++++++++++++++ src/node_binding.h | 64 ++++++ src/node_internals.h | 112 +--------- 6 files changed, 552 insertions(+), 509 deletions(-) create mode 100644 src/node_binding.cc create mode 100644 src/node_binding.h diff --git a/node.gyp b/node.gyp index 3cbbe61e575ca3..4ad5ef3a9f8706 100644 --- a/node.gyp +++ b/node.gyp @@ -343,6 +343,7 @@ 'src/module_wrap.cc', 'src/node.cc', 'src/node_api.cc', + 'src/node_binding.cc', 'src/node_buffer.cc', 'src/node_config.cc', 'src/node_constants.cc', @@ -414,6 +415,7 @@ 'src/node.h', 'src/node_api.h', 'src/node_api_types.h', + 'src/node_binding.h', 'src/node_buffer.h', 'src/node_constants.h', 'src/node_context_data.h', diff --git a/src/node.cc b/src/node.cc index e01baf9726a45e..01690a1e6c94da 100644 --- a/src/node.cc +++ b/src/node.cc @@ -19,6 +19,7 @@ // OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE // USE OR OTHER DEALINGS IN THE SOFTWARE. +#include "node_binding.h" #include "node_buffer.h" #include "node_constants.h" #include "node_context_data.h" @@ -113,20 +114,6 @@ typedef int mode_t; #include // getgrnam() #endif -#if defined(__POSIX__) -#include -#endif - -// This is used to load built-in modules. Instead of using -// __attribute__((constructor)), we call the _register_ -// function for each built-in modules explicitly in -// node::RegisterBuiltinModules(). This is only forward declaration. -// The definitions are in each module's implementation when calling -// the NODE_BUILTIN_MODULE_CONTEXT_AWARE. -#define V(modname) void _register_##modname(); - NODE_BUILTIN_MODULES(V) -#undef V - namespace node { using native_module::NativeModuleLoader; @@ -173,13 +160,6 @@ using v8::V8; using v8::Value; static bool v8_is_profiling = false; -static bool node_is_initialized = false; -static uv_once_t init_modpending_once = UV_ONCE_INIT; -static uv_key_t thread_local_modpending; -static node_module* modlist_builtin; -static node_module* modlist_internal; -static node_module* modlist_linked; -static node_module* modlist_addon; #ifdef NODE_EXPERIMENTAL_HTTP static const char llhttp_version[] = @@ -825,257 +805,6 @@ static void Exit(const FunctionCallbackInfo& args) { env->Exit(code); } -extern "C" void node_module_register(void* m) { - struct node_module* mp = reinterpret_cast(m); - - if (mp->nm_flags & NM_F_BUILTIN) { - mp->nm_link = modlist_builtin; - modlist_builtin = mp; - } else if (mp->nm_flags & NM_F_INTERNAL) { - mp->nm_link = modlist_internal; - modlist_internal = mp; - } else if (!node_is_initialized) { - // "Linked" modules are included as part of the node project. - // Like builtins they are registered *before* node::Init runs. - mp->nm_flags = NM_F_LINKED; - mp->nm_link = modlist_linked; - modlist_linked = mp; - } else { - uv_key_set(&thread_local_modpending, mp); - } -} - -inline struct node_module* FindModule(struct node_module* list, - const char* name, - int flag) { - struct node_module* mp; - - for (mp = list; mp != nullptr; mp = mp->nm_link) { - if (strcmp(mp->nm_modname, name) == 0) - break; - } - - CHECK(mp == nullptr || (mp->nm_flags & flag) != 0); - return mp; -} - -node_module* get_builtin_module(const char* name) { - return FindModule(modlist_builtin, name, NM_F_BUILTIN); -} -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); -} - -class DLib { - public: -#ifdef __POSIX__ - static const int kDefaultFlags = RTLD_LAZY; -#else - static const int kDefaultFlags = 0; -#endif - - inline DLib(const char* filename, int flags) - : filename_(filename), flags_(flags), handle_(nullptr) {} - - inline bool Open(); - inline void Close(); - inline void* GetSymbolAddress(const char* name); - - const std::string filename_; - const int flags_; - std::string errmsg_; - void* handle_; -#ifndef __POSIX__ - uv_lib_t lib_; -#endif - private: - DISALLOW_COPY_AND_ASSIGN(DLib); -}; - - -#ifdef __POSIX__ -bool DLib::Open() { - handle_ = dlopen(filename_.c_str(), flags_); - if (handle_ != nullptr) - return true; - errmsg_ = dlerror(); - return false; -} - -void DLib::Close() { - if (handle_ == nullptr) return; - dlclose(handle_); - handle_ = nullptr; -} - -void* DLib::GetSymbolAddress(const char* name) { - return dlsym(handle_, name); -} -#else // !__POSIX__ -bool DLib::Open() { - int ret = uv_dlopen(filename_.c_str(), &lib_); - if (ret == 0) { - handle_ = static_cast(lib_.handle); - return true; - } - errmsg_ = uv_dlerror(&lib_); - uv_dlclose(&lib_); - return false; -} - -void DLib::Close() { - if (handle_ == nullptr) return; - uv_dlclose(&lib_); - handle_ = nullptr; -} - -void* DLib::GetSymbolAddress(const char* name) { - void* address; - if (0 == uv_dlsym(&lib_, name, &address)) return address; - return nullptr; -} -#endif // !__POSIX__ - -using InitializerCallback = void (*)(Local exports, - Local module, - Local context); - -inline InitializerCallback GetInitializerCallback(DLib* dlib) { - const char* name = "node_register_module_v" STRINGIFY(NODE_MODULE_VERSION); - return reinterpret_cast(dlib->GetSymbolAddress(name)); -} - -inline napi_addon_register_func GetNapiInitializerCallback(DLib* dlib) { - const char* name = - STRINGIFY(NAPI_MODULE_INITIALIZER_BASE) STRINGIFY(NAPI_MODULE_VERSION); - return - reinterpret_cast(dlib->GetSymbolAddress(name)); -} - -void InitModpendingOnce() { - CHECK_EQ(0, uv_key_create(&thread_local_modpending)); -} - -// DLOpen is process.dlopen(module, filename, flags). -// Used to load 'module.node' dynamically shared objects. -// -// FIXME(bnoordhuis) Not multi-context ready. TBD how to resolve the conflict -// when two contexts try to load the same shared object. Maybe have a shadow -// cache that's a plain C list or hash table that's shared across contexts? -static void DLOpen(const FunctionCallbackInfo& args) { - Environment* env = Environment::GetCurrent(args); - auto context = env->context(); - - uv_once(&init_modpending_once, InitModpendingOnce); - CHECK_NULL(uv_key_get(&thread_local_modpending)); - - if (args.Length() < 2) { - env->ThrowError("process.dlopen needs at least 2 arguments."); - return; - } - - int32_t flags = DLib::kDefaultFlags; - if (args.Length() > 2 && !args[2]->Int32Value(context).To(&flags)) { - return env->ThrowTypeError("flag argument must be an integer."); - } - - Local module; - Local exports; - Local exports_v; - if (!args[0]->ToObject(context).ToLocal(&module) || - !module->Get(context, env->exports_string()).ToLocal(&exports_v) || - !exports_v->ToObject(context).ToLocal(&exports)) { - return; // Exception pending. - } - - node::Utf8Value filename(env->isolate(), args[1]); // Cast - DLib dlib(*filename, flags); - bool is_opened = dlib.Open(); - - // Objects containing v14 or later modules will have registered themselves - // on the pending list. Activate all of them now. At present, only one - // module per object is supported. - node_module* const mp = static_cast( - uv_key_get(&thread_local_modpending)); - uv_key_set(&thread_local_modpending, nullptr); - - if (!is_opened) { - Local errmsg = OneByteString(env->isolate(), dlib.errmsg_.c_str()); - dlib.Close(); -#ifdef _WIN32 - // Windows needs to add the filename into the error message - errmsg = String::Concat( - env->isolate(), errmsg, args[1]->ToString(context).ToLocalChecked()); -#endif // _WIN32 - env->isolate()->ThrowException(Exception::Error(errmsg)); - return; - } - - if (mp == nullptr) { - if (auto callback = GetInitializerCallback(&dlib)) { - callback(exports, module, context); - } else if (auto napi_callback = GetNapiInitializerCallback(&dlib)) { - napi_module_register_by_symbol(exports, module, context, napi_callback); - } else { - dlib.Close(); - env->ThrowError("Module did not self-register."); - } - return; - } - - // -1 is used for N-API modules - if ((mp->nm_version != -1) && (mp->nm_version != NODE_MODULE_VERSION)) { - // Even if the module did self-register, it may have done so with the wrong - // version. We must only give up after having checked to see if it has an - // appropriate initializer callback. - if (auto callback = GetInitializerCallback(&dlib)) { - callback(exports, module, context); - return; - } - char errmsg[1024]; - snprintf(errmsg, - sizeof(errmsg), - "The module '%s'" - "\nwas compiled against a different Node.js version using" - "\nNODE_MODULE_VERSION %d. This version of Node.js requires" - "\nNODE_MODULE_VERSION %d. Please try re-compiling or " - "re-installing\nthe module (for instance, using `npm rebuild` " - "or `npm install`).", - *filename, mp->nm_version, NODE_MODULE_VERSION); - - // NOTE: `mp` is allocated inside of the shared library's memory, calling - // `dlclose` will deallocate it - dlib.Close(); - env->ThrowError(errmsg); - return; - } - if (mp->nm_flags & NM_F_BUILTIN) { - dlib.Close(); - env->ThrowError("Built-in module self-registered."); - return; - } - - mp->nm_dso_handle = dlib.handle_; - mp->nm_link = modlist_addon; - modlist_addon = mp; - - if (mp->nm_context_register_func != nullptr) { - mp->nm_context_register_func(exports, module, context, mp->nm_priv); - } else if (mp->nm_register_func != nullptr) { - mp->nm_register_func(exports, module, mp->nm_priv); - } else { - dlib.Close(); - env->ThrowError("Module has no declared entry point."); - return; - } - - // Tell coverity that 'handle' should not be freed when we return. - // coverity[leaked_storage] -} - static Maybe ProcessEmitWarningGeneric(Environment* env, const char* warning, const char* type = nullptr, @@ -1179,118 +908,6 @@ static void OnMessage(Local message, Local error) { } } - -static Local InitModule(Environment* env, - node_module* mod, - Local module) { - Local exports = Object::New(env->isolate()); - // Internal bindings don't have a "module" object, only exports. - CHECK_NULL(mod->nm_register_func); - CHECK_NOT_NULL(mod->nm_context_register_func); - Local unused = Undefined(env->isolate()); - mod->nm_context_register_func(exports, - unused, - env->context(), - mod->nm_priv); - return exports; -} - -static void ThrowIfNoSuchModule(Environment* env, const char* module_v) { - char errmsg[1024]; - snprintf(errmsg, - sizeof(errmsg), - "No such module: %s", - module_v); - env->ThrowError(errmsg); -} - -static void GetBinding(const FunctionCallbackInfo& args) { - Environment* env = Environment::GetCurrent(args); - - CHECK(args[0]->IsString()); - - Local module = args[0].As(); - node::Utf8Value module_v(env->isolate(), module); - - node_module* mod = get_builtin_module(*module_v); - Local exports; - if (mod != nullptr) { - exports = InitModule(env, mod, module); - } else { - return ThrowIfNoSuchModule(env, *module_v); - } - - args.GetReturnValue().Set(exports); -} - -static void GetInternalBinding(const FunctionCallbackInfo& args) { - Environment* env = Environment::GetCurrent(args); - - CHECK(args[0]->IsString()); - - Local module = args[0].As(); - node::Utf8Value module_v(env->isolate(), module); - Local exports; - - node_module* mod = get_internal_module(*module_v); - if (mod != nullptr) { - exports = InitModule(env, mod, module); - } else if (!strcmp(*module_v, "constants")) { - exports = Object::New(env->isolate()); - CHECK(exports->SetPrototype(env->context(), - Null(env->isolate())).FromJust()); - DefineConstants(env->isolate(), exports); - } else if (!strcmp(*module_v, "natives")) { - exports = per_process_loader.GetSourceObject(env->context()); - } else { - return ThrowIfNoSuchModule(env, *module_v); - } - - args.GetReturnValue().Set(exports); -} - -static void GetLinkedBinding(const FunctionCallbackInfo& args) { - Environment* env = Environment::GetCurrent(args); - - CHECK(args[0]->IsString()); - - Local module_name = args[0].As(); - - node::Utf8Value module_name_v(env->isolate(), module_name); - node_module* mod = get_linked_module(*module_name_v); - - if (mod == nullptr) { - char errmsg[1024]; - snprintf(errmsg, - sizeof(errmsg), - "No such module was linked: %s", - *module_name_v); - return env->ThrowError(errmsg); - } - - Local module = Object::New(env->isolate()); - Local exports = Object::New(env->isolate()); - Local exports_prop = String::NewFromUtf8(env->isolate(), "exports", - NewStringType::kNormal).ToLocalChecked(); - module->Set(env->context(), exports_prop, exports).FromJust(); - - if (mod->nm_context_register_func != nullptr) { - mod->nm_context_register_func(exports, - module, - env->context(), - mod->nm_priv); - } else if (mod->nm_register_func != nullptr) { - mod->nm_register_func(exports, module, mod->nm_priv); - } else { - return env->ThrowError("Linked module has no declared entry point."); - } - - auto effective_exports = module->Get(env->context(), - exports_prop).ToLocalChecked(); - - args.GetReturnValue().Set(effective_exports); -} - static Local GetFeatures(Environment* env) { EscapableHandleScope scope(env->isolate()); @@ -1688,7 +1305,7 @@ void SetupProcessObject(Environment* env, env->SetMethod(process, "_kill", Kill); env->SetMethodNoSideEffect(process, "cwd", Cwd); - env->SetMethod(process, "dlopen", DLOpen); + env->SetMethod(process, "dlopen", binding::DLOpen); env->SetMethod(process, "reallyExit", Exit); env->SetMethodNoSideEffect(process, "uptime", Uptime); @@ -1825,16 +1442,18 @@ void LoadEnvironment(Environment* env) { global).FromJust(); // Create binding loaders - Local get_binding_fn = - env->NewFunctionTemplate(GetBinding)->GetFunction(env->context()) - .ToLocalChecked(); + Local get_binding_fn = env->NewFunctionTemplate(binding::GetBinding) + ->GetFunction(env->context()) + .ToLocalChecked(); Local get_linked_binding_fn = - env->NewFunctionTemplate(GetLinkedBinding)->GetFunction(env->context()) + env->NewFunctionTemplate(binding::GetLinkedBinding) + ->GetFunction(env->context()) .ToLocalChecked(); Local get_internal_binding_fn = - env->NewFunctionTemplate(GetInternalBinding)->GetFunction(env->context()) + env->NewFunctionTemplate(binding::GetInternalBinding) + ->GetFunction(env->context()) .ToLocalChecked(); Local loaders_bootstrapper_args[] = { @@ -2206,7 +1825,7 @@ void Init(std::vector* argv, prog_start_time = static_cast(uv_now(uv_default_loop())); // Register built-in modules - RegisterBuiltinModules(); + binding::RegisterBuiltinModules(); // Make inherited handles noninheritable. uv_disable_stdio_inheritance(); @@ -2727,14 +2346,6 @@ int Start(int argc, char** argv) { return exit_code; } -// Call built-in modules' _register_ function to -// do module registration explicitly. -void RegisterBuiltinModules() { -#define V(modname) _register_##modname(); - NODE_BUILTIN_MODULES(V) -#undef V -} - } // namespace node #if !HAVE_INSPECTOR diff --git a/src/node_api.cc b/src/node_api.cc index 20428d40fadbdf..7d843c08f5a69d 100644 --- a/src/node_api.cc +++ b/src/node_api.cc @@ -1,10 +1,11 @@ #include #include "env.h" #define NAPI_EXPERIMENTAL +#include "js_native_api_v8.h" #include "node_api.h" +#include "node_binding.h" #include "node_errors.h" #include "node_internals.h" -#include "js_native_api_v8.h" struct node_napi_env__ : public napi_env__ { explicit node_napi_env__(v8::Local context): diff --git a/src/node_binding.cc b/src/node_binding.cc new file mode 100644 index 00000000000000..a2e5134bfa1188 --- /dev/null +++ b/src/node_binding.cc @@ -0,0 +1,471 @@ +#include "node_binding.h" +#include "node_internals.h" +#include "node_native_module.h" + +#if defined(__POSIX__) +#include +#endif + +#if HAVE_OPENSSL +#define NODE_BUILTIN_OPENSSL_MODULES(V) V(crypto) V(tls_wrap) +#else +#define NODE_BUILTIN_OPENSSL_MODULES(V) +#endif + +#if NODE_HAVE_I18N_SUPPORT +#define NODE_BUILTIN_ICU_MODULES(V) V(icu) +#else +#define NODE_BUILTIN_ICU_MODULES(V) +#endif + +// A list of built-in modules. In order to do module registration +// in node::Init(), need to add built-in modules in the following list. +// Then in binding::RegisterBuiltinModules(), it calls modules' registration +// function. This helps the built-in modules are loaded properly when +// node is built as static library. No need to depend on the +// __attribute__((constructor)) like mechanism in GCC. +#define NODE_BUILTIN_STANDARD_MODULES(V) \ + V(async_wrap) \ + V(buffer) \ + V(cares_wrap) \ + V(config) \ + V(contextify) \ + V(domain) \ + V(fs) \ + V(fs_event_wrap) \ + V(heap_utils) \ + V(http2) \ + V(http_parser) \ + V(inspector) \ + V(js_stream) \ + V(messaging) \ + V(module_wrap) \ + V(native_module) \ + V(options) \ + V(os) \ + V(performance) \ + V(pipe_wrap) \ + V(process_wrap) \ + V(serdes) \ + V(signal_wrap) \ + V(spawn_sync) \ + V(stream_pipe) \ + V(stream_wrap) \ + V(string_decoder) \ + V(symbols) \ + V(tcp_wrap) \ + V(timers) \ + V(trace_events) \ + V(tty_wrap) \ + V(types) \ + V(udp_wrap) \ + V(url) \ + V(util) \ + V(uv) \ + V(v8) \ + V(worker) \ + V(zlib) + +#define NODE_BUILTIN_MODULES(V) \ + NODE_BUILTIN_STANDARD_MODULES(V) \ + NODE_BUILTIN_OPENSSL_MODULES(V) \ + NODE_BUILTIN_ICU_MODULES(V) + +// This is used to load built-in modules. Instead of using +// __attribute__((constructor)), we call the _register_ +// function for each built-in modules explicitly in +// binding::RegisterBuiltinModules(). This is only forward declaration. +// The definitions are in each module's implementation when calling +// the NODE_BUILTIN_MODULE_CONTEXT_AWARE. +#define V(modname) void _register_##modname(); +NODE_BUILTIN_MODULES(V) +#undef V + +namespace node { + +using v8::Context; +using v8::Exception; +using v8::FunctionCallbackInfo; +using v8::Local; +using v8::NewStringType; +using v8::Object; +using v8::String; +using v8::Value; + +// Globals per process +static node_module* modlist_builtin; +static node_module* modlist_internal; +static node_module* modlist_linked; +static node_module* modlist_addon; +static uv_once_t init_modpending_once = UV_ONCE_INIT; +static uv_key_t thread_local_modpending; + +// This is set by node::Init() which is used by embedders +bool node_is_initialized = false; + +extern "C" void node_module_register(void* m) { + struct node_module* mp = reinterpret_cast(m); + + if (mp->nm_flags & NM_F_BUILTIN) { + mp->nm_link = modlist_builtin; + modlist_builtin = mp; + } else if (mp->nm_flags & NM_F_INTERNAL) { + mp->nm_link = modlist_internal; + modlist_internal = mp; + } else if (!node_is_initialized) { + // "Linked" modules are included as part of the node project. + // Like builtins they are registered *before* node::Init runs. + mp->nm_flags = NM_F_LINKED; + mp->nm_link = modlist_linked; + modlist_linked = mp; + } else { + uv_key_set(&thread_local_modpending, mp); + } +} + +namespace binding { + +class DLib { + public: +#ifdef __POSIX__ + static const int kDefaultFlags = RTLD_LAZY; +#else + static const int kDefaultFlags = 0; +#endif + + inline DLib(const char* filename, int flags) + : filename_(filename), flags_(flags), handle_(nullptr) {} + + inline bool Open(); + inline void Close(); + inline void* GetSymbolAddress(const char* name); + + const std::string filename_; + const int flags_; + std::string errmsg_; + void* handle_; +#ifndef __POSIX__ + uv_lib_t lib_; +#endif + private: + DISALLOW_COPY_AND_ASSIGN(DLib); +}; + +#ifdef __POSIX__ +bool DLib::Open() { + handle_ = dlopen(filename_.c_str(), flags_); + if (handle_ != nullptr) return true; + errmsg_ = dlerror(); + return false; +} + +void DLib::Close() { + if (handle_ == nullptr) return; + dlclose(handle_); + handle_ = nullptr; +} + +void* DLib::GetSymbolAddress(const char* name) { + return dlsym(handle_, name); +} +#else // !__POSIX__ +bool DLib::Open() { + int ret = uv_dlopen(filename_.c_str(), &lib_); + if (ret == 0) { + handle_ = static_cast(lib_.handle); + return true; + } + errmsg_ = uv_dlerror(&lib_); + uv_dlclose(&lib_); + return false; +} + +void DLib::Close() { + if (handle_ == nullptr) return; + uv_dlclose(&lib_); + handle_ = nullptr; +} + +void* DLib::GetSymbolAddress(const char* name) { + void* address; + if (0 == uv_dlsym(&lib_, name, &address)) return address; + return nullptr; +} +#endif // !__POSIX__ + +using InitializerCallback = void (*)(Local exports, + Local module, + Local context); + +inline InitializerCallback GetInitializerCallback(DLib* dlib) { + const char* name = "node_register_module_v" STRINGIFY(NODE_MODULE_VERSION); + return reinterpret_cast(dlib->GetSymbolAddress(name)); +} + +inline napi_addon_register_func GetNapiInitializerCallback(DLib* dlib) { + const char* name = + STRINGIFY(NAPI_MODULE_INITIALIZER_BASE) STRINGIFY(NAPI_MODULE_VERSION); + return reinterpret_cast( + dlib->GetSymbolAddress(name)); +} + +void InitModpendingOnce() { + CHECK_EQ(0, uv_key_create(&thread_local_modpending)); +} + +// DLOpen is process.dlopen(module, filename, flags). +// Used to load 'module.node' dynamically shared objects. +// +// FIXME(bnoordhuis) Not multi-context ready. TBD how to resolve the conflict +// when two contexts try to load the same shared object. Maybe have a shadow +// cache that's a plain C list or hash table that's shared across contexts? +void DLOpen(const FunctionCallbackInfo& args) { + Environment* env = Environment::GetCurrent(args); + auto context = env->context(); + + uv_once(&init_modpending_once, InitModpendingOnce); + CHECK_NULL(uv_key_get(&thread_local_modpending)); + + if (args.Length() < 2) { + env->ThrowError("process.dlopen needs at least 2 arguments."); + return; + } + + int32_t flags = DLib::kDefaultFlags; + if (args.Length() > 2 && !args[2]->Int32Value(context).To(&flags)) { + return env->ThrowTypeError("flag argument must be an integer."); + } + + Local module; + Local exports; + Local exports_v; + if (!args[0]->ToObject(context).ToLocal(&module) || + !module->Get(context, env->exports_string()).ToLocal(&exports_v) || + !exports_v->ToObject(context).ToLocal(&exports)) { + return; // Exception pending. + } + + node::Utf8Value filename(env->isolate(), args[1]); // Cast + DLib dlib(*filename, flags); + bool is_opened = dlib.Open(); + + // Objects containing v14 or later modules will have registered themselves + // on the pending list. Activate all of them now. At present, only one + // module per object is supported. + node_module* const mp = + static_cast(uv_key_get(&thread_local_modpending)); + uv_key_set(&thread_local_modpending, nullptr); + + if (!is_opened) { + Local errmsg = OneByteString(env->isolate(), dlib.errmsg_.c_str()); + dlib.Close(); +#ifdef _WIN32 + // Windows needs to add the filename into the error message + errmsg = String::Concat( + env->isolate(), errmsg, args[1]->ToString(context).ToLocalChecked()); +#endif // _WIN32 + env->isolate()->ThrowException(Exception::Error(errmsg)); + return; + } + + if (mp == nullptr) { + if (auto callback = GetInitializerCallback(&dlib)) { + callback(exports, module, context); + } else if (auto napi_callback = GetNapiInitializerCallback(&dlib)) { + napi_module_register_by_symbol(exports, module, context, napi_callback); + } else { + dlib.Close(); + env->ThrowError("Module did not self-register."); + } + return; + } + + // -1 is used for N-API modules + if ((mp->nm_version != -1) && (mp->nm_version != NODE_MODULE_VERSION)) { + // Even if the module did self-register, it may have done so with the wrong + // version. We must only give up after having checked to see if it has an + // appropriate initializer callback. + if (auto callback = GetInitializerCallback(&dlib)) { + callback(exports, module, context); + return; + } + char errmsg[1024]; + snprintf(errmsg, + sizeof(errmsg), + "The module '%s'" + "\nwas compiled against a different Node.js version using" + "\nNODE_MODULE_VERSION %d. This version of Node.js requires" + "\nNODE_MODULE_VERSION %d. Please try re-compiling or " + "re-installing\nthe module (for instance, using `npm rebuild` " + "or `npm install`).", + *filename, + mp->nm_version, + NODE_MODULE_VERSION); + + // NOTE: `mp` is allocated inside of the shared library's memory, calling + // `dlclose` will deallocate it + dlib.Close(); + env->ThrowError(errmsg); + return; + } + if (mp->nm_flags & NM_F_BUILTIN) { + dlib.Close(); + env->ThrowError("Built-in module self-registered."); + return; + } + + mp->nm_dso_handle = dlib.handle_; + mp->nm_link = modlist_addon; + modlist_addon = mp; + + if (mp->nm_context_register_func != nullptr) { + mp->nm_context_register_func(exports, module, context, mp->nm_priv); + } else if (mp->nm_register_func != nullptr) { + mp->nm_register_func(exports, module, mp->nm_priv); + } else { + dlib.Close(); + env->ThrowError("Module has no declared entry point."); + return; + } + + // Tell coverity that 'handle' should not be freed when we return. + // coverity[leaked_storage] +} + +inline struct node_module* FindModule(struct node_module* list, + const char* name, + int flag) { + struct node_module* mp; + + for (mp = list; mp != nullptr; mp = mp->nm_link) { + if (strcmp(mp->nm_modname, name) == 0) break; + } + + CHECK(mp == nullptr || (mp->nm_flags & flag) != 0); + return mp; +} + +node_module* get_builtin_module(const char* name) { + return FindModule(modlist_builtin, name, NM_F_BUILTIN); +} +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 InitModule(Environment* env, + node_module* mod, + Local module) { + Local exports = Object::New(env->isolate()); + // Internal bindings don't have a "module" object, only exports. + CHECK_NULL(mod->nm_register_func); + CHECK_NOT_NULL(mod->nm_context_register_func); + Local unused = Undefined(env->isolate()); + mod->nm_context_register_func(exports, unused, env->context(), mod->nm_priv); + return exports; +} + +static void ThrowIfNoSuchModule(Environment* env, const char* module_v) { + char errmsg[1024]; + snprintf(errmsg, sizeof(errmsg), "No such module: %s", module_v); + env->ThrowError(errmsg); +} + +void GetBinding(const FunctionCallbackInfo& args) { + Environment* env = Environment::GetCurrent(args); + + CHECK(args[0]->IsString()); + + Local module = args[0].As(); + node::Utf8Value module_v(env->isolate(), module); + + node_module* mod = get_builtin_module(*module_v); + Local exports; + if (mod != nullptr) { + exports = InitModule(env, mod, module); + } else { + return ThrowIfNoSuchModule(env, *module_v); + } + + args.GetReturnValue().Set(exports); +} + +void GetInternalBinding(const FunctionCallbackInfo& args) { + Environment* env = Environment::GetCurrent(args); + + CHECK(args[0]->IsString()); + + Local module = args[0].As(); + node::Utf8Value module_v(env->isolate(), module); + Local exports; + + node_module* mod = get_internal_module(*module_v); + if (mod != nullptr) { + exports = InitModule(env, mod, module); + } else if (!strcmp(*module_v, "constants")) { + exports = Object::New(env->isolate()); + CHECK( + exports->SetPrototype(env->context(), Null(env->isolate())).FromJust()); + DefineConstants(env->isolate(), exports); + } else if (!strcmp(*module_v, "natives")) { + exports = per_process_loader.GetSourceObject(env->context()); + } else { + return ThrowIfNoSuchModule(env, *module_v); + } + + args.GetReturnValue().Set(exports); +} + +void GetLinkedBinding(const FunctionCallbackInfo& args) { + Environment* env = Environment::GetCurrent(args); + + CHECK(args[0]->IsString()); + + Local module_name = args[0].As(); + + node::Utf8Value module_name_v(env->isolate(), module_name); + node_module* mod = get_linked_module(*module_name_v); + + if (mod == nullptr) { + char errmsg[1024]; + snprintf(errmsg, + sizeof(errmsg), + "No such module was linked: %s", + *module_name_v); + return env->ThrowError(errmsg); + } + + Local module = Object::New(env->isolate()); + Local exports = Object::New(env->isolate()); + Local exports_prop = + String::NewFromUtf8(env->isolate(), "exports", NewStringType::kNormal) + .ToLocalChecked(); + module->Set(env->context(), exports_prop, exports).FromJust(); + + if (mod->nm_context_register_func != nullptr) { + mod->nm_context_register_func( + exports, module, env->context(), mod->nm_priv); + } else if (mod->nm_register_func != nullptr) { + mod->nm_register_func(exports, module, mod->nm_priv); + } else { + return env->ThrowError("Linked module has no declared entry point."); + } + + auto effective_exports = + module->Get(env->context(), exports_prop).ToLocalChecked(); + + args.GetReturnValue().Set(effective_exports); +} + +// Call built-in modules' _register_ function to +// do module registration explicitly. +void RegisterBuiltinModules() { +#define V(modname) _register_##modname(); + NODE_BUILTIN_MODULES(V) +#undef V +} + +} // namespace binding +} // namespace node diff --git a/src/node_binding.h b/src/node_binding.h new file mode 100644 index 00000000000000..743c82accd09b8 --- /dev/null +++ b/src/node_binding.h @@ -0,0 +1,64 @@ +#ifndef SRC_NODE_BINDING_H_ +#define SRC_NODE_BINDING_H_ + +#if defined(NODE_WANT_INTERNALS) && NODE_WANT_INTERNALS + +#include "node.h" +#include "node_api.h" +#include "uv.h" +#include "v8.h" + +enum { + NM_F_BUILTIN = 1 << 0, + NM_F_LINKED = 1 << 1, + NM_F_INTERNAL = 1 << 2, +}; + +#define NODE_MODULE_CONTEXT_AWARE_CPP(modname, regfunc, priv, flags) \ + static node::node_module _module = { \ + NODE_MODULE_VERSION, \ + flags, \ + nullptr, \ + __FILE__, \ + nullptr, \ + (node::addon_context_register_func)(regfunc), \ + NODE_STRINGIFY(modname), \ + priv, \ + nullptr}; \ + void _register_##modname() { node_module_register(&_module); } + +#define NODE_BUILTIN_MODULE_CONTEXT_AWARE(modname, regfunc) \ + NODE_MODULE_CONTEXT_AWARE_CPP(modname, regfunc, nullptr, NM_F_BUILTIN) + +void napi_module_register_by_symbol(v8::Local exports, + v8::Local module, + v8::Local context, + napi_addon_register_func init); + +namespace node { + +#define NODE_MODULE_CONTEXT_AWARE_INTERNAL(modname, regfunc) \ + NODE_MODULE_CONTEXT_AWARE_CPP(modname, regfunc, nullptr, NM_F_INTERNAL) + +// Globals per process +// This is set by node::Init() which is used by embedders +extern bool node_is_initialized; + +namespace binding { + +// Call _register functions for all of +// the built-in modules. Because built-in modules don't +// use the __attribute__((constructor)). Need to +// explicitly call the _register* functions. +void RegisterBuiltinModules(); +void GetBinding(const v8::FunctionCallbackInfo& args); +void GetInternalBinding(const v8::FunctionCallbackInfo& args); +void GetLinkedBinding(const v8::FunctionCallbackInfo& args); +void DLOpen(const v8::FunctionCallbackInfo& args); + +} // namespace binding + +} // namespace node + +#endif // defined(NODE_WANT_INTERNALS) && NODE_WANT_INTERNALS +#endif // SRC_NODE_BINDING_H_ diff --git a/src/node_internals.h b/src/node_internals.h index 68c0b1f753588c..6cb40c9070e6a0 100644 --- a/src/node_internals.h +++ b/src/node_internals.h @@ -24,15 +24,15 @@ #if defined(NODE_WANT_INTERNALS) && NODE_WANT_INTERNALS +#include "env-inl.h" #include "node.h" +#include "node_binding.h" #include "node_mutex.h" #include "node_persistent.h" +#include "tracing/trace_event.h" #include "util-inl.h" -#include "env-inl.h" #include "uv.h" #include "v8.h" -#include "tracing/trace_event.h" -#include "node_api.h" #include #include @@ -57,12 +57,6 @@ #define Z_MAX_LEVEL 9 #define Z_DEFAULT_LEVEL Z_DEFAULT_COMPRESSION -enum { - NM_F_BUILTIN = 1 << 0, - NM_F_LINKED = 1 << 1, - NM_F_INTERNAL = 1 << 2, -}; - struct sockaddr; // Variation on NODE_DEFINE_CONSTANT that sets a String value. @@ -83,92 +77,6 @@ struct sockaddr; constant_attributes).FromJust(); \ } while (0) - -#if HAVE_OPENSSL -#define NODE_BUILTIN_OPENSSL_MODULES(V) V(crypto) V(tls_wrap) -#else -#define NODE_BUILTIN_OPENSSL_MODULES(V) -#endif - -#if NODE_HAVE_I18N_SUPPORT -#define NODE_BUILTIN_ICU_MODULES(V) V(icu) -#else -#define NODE_BUILTIN_ICU_MODULES(V) -#endif - -// A list of built-in modules. In order to do module registration -// in node::Init(), need to add built-in modules in the following list. -// Then in node::RegisterBuiltinModules(), it calls modules' registration -// function. This helps the built-in modules are loaded properly when -// node is built as static library. No need to depend on the -// __attribute__((constructor)) like mechanism in GCC. -#define NODE_BUILTIN_STANDARD_MODULES(V) \ - V(async_wrap) \ - V(buffer) \ - V(cares_wrap) \ - V(config) \ - V(contextify) \ - V(domain) \ - V(fs) \ - V(fs_event_wrap) \ - V(heap_utils) \ - V(http2) \ - V(http_parser) \ - V(inspector) \ - V(js_stream) \ - V(messaging) \ - V(module_wrap) \ - V(native_module) \ - V(options) \ - V(os) \ - V(performance) \ - V(pipe_wrap) \ - V(process_wrap) \ - V(serdes) \ - V(signal_wrap) \ - V(spawn_sync) \ - V(stream_pipe) \ - V(stream_wrap) \ - V(string_decoder) \ - V(symbols) \ - V(tcp_wrap) \ - V(timers) \ - V(trace_events) \ - V(tty_wrap) \ - V(types) \ - V(udp_wrap) \ - V(url) \ - V(util) \ - V(uv) \ - V(v8) \ - V(worker) \ - V(zlib) - -#define NODE_BUILTIN_MODULES(V) \ - NODE_BUILTIN_STANDARD_MODULES(V) \ - NODE_BUILTIN_OPENSSL_MODULES(V) \ - NODE_BUILTIN_ICU_MODULES(V) - -#define NODE_MODULE_CONTEXT_AWARE_CPP(modname, regfunc, priv, flags) \ - static node::node_module _module = { \ - NODE_MODULE_VERSION, \ - flags, \ - nullptr, \ - __FILE__, \ - nullptr, \ - (node::addon_context_register_func) (regfunc), \ - NODE_STRINGIFY(modname), \ - priv, \ - nullptr \ - }; \ - void _register_ ## modname() { \ - node_module_register(&_module); \ - } - - -#define NODE_BUILTIN_MODULE_CONTEXT_AWARE(modname, regfunc) \ - NODE_MODULE_CONTEXT_AWARE_CPP(modname, regfunc, nullptr, NM_F_BUILTIN) - namespace node { namespace native_module { @@ -281,12 +189,6 @@ void SetupProcessObject(Environment* env, const std::vector& args, const std::vector& exec_args); -// Call _register functions for all of -// the built-in modules. Because built-in modules don't -// use the __attribute__((constructor)). Need to -// explicitly call the _register* functions. -void RegisterBuiltinModules(); - enum Endianness { kLittleEndian, // _Not_ LITTLE_ENDIAN, clashes with endian.h. kBigEndian @@ -782,9 +684,6 @@ static inline const char* errno_string(int errorno) { } } -#define NODE_MODULE_CONTEXT_AWARE_INTERNAL(modname, regfunc) \ - NODE_MODULE_CONTEXT_AWARE_CPP(modname, regfunc, nullptr, NM_F_INTERNAL) - #define TRACING_CATEGORY_NODE "node" #define TRACING_CATEGORY_NODE1(one) \ TRACING_CATEGORY_NODE "," \ @@ -857,11 +756,6 @@ void DefineZlibConstants(v8::Local target); } // namespace node -void napi_module_register_by_symbol(v8::Local exports, - v8::Local module, - v8::Local context, - napi_addon_register_func init); - #endif // defined(NODE_WANT_INTERNALS) && NODE_WANT_INTERNALS #endif // SRC_NODE_INTERNALS_H_