diff --git a/src/node.cc b/src/node.cc index 28d66452f87e9c..dbf9314273efeb 100644 --- a/src/node.cc +++ b/src/node.cc @@ -2147,46 +2147,82 @@ node_module* get_linked_module(const char* name) { } struct DLib { - std::string filename_; +#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_; - int flags_; +#ifndef __POSIX__ + uv_lib_t lib_; +#endif + + DISALLOW_COPY_AND_ASSIGN(DLib); +}; + #ifdef __POSIX__ - static const int kDefaultFlags = RTLD_LAZY; +bool DLib::Open() { + handle_ = dlopen(filename_.c_str(), flags_); + if (handle_ != nullptr) + return true; + errmsg_ = dlerror(); + return false; +} - bool 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 Close() { - if (handle_ != nullptr) - dlclose(handle_); - } +void* DLib::GetSymbolAddress(const char* name) { + return dlsym(handle_, name); +} #else // !__POSIX__ - static const int kDefaultFlags = 0; - uv_lib_t lib_; - - bool 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; +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 Close() { - uv_dlclose(&lib_); - } +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)); +} // DLOpen is process.dlopen(module, filename, flags). // Used to load 'module.node' dynamically shared objects. @@ -2196,6 +2232,7 @@ struct DLib { // 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(); CHECK_EQ(modpending, nullptr); @@ -2205,16 +2242,21 @@ static void DLOpen(const FunctionCallbackInfo& args) { } int32_t flags = DLib::kDefaultFlags; - if (args.Length() > 2 && !args[2]->Int32Value(env->context()).To(&flags)) { + if (args.Length() > 2 && !args[2]->Int32Value(context).To(&flags)) { return env->ThrowTypeError("flag argument must be an integer."); } - Local module = - args[0]->ToObject(env->context()).ToLocalChecked(); // Cast + 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; - dlib.filename_ = *filename; - dlib.flags_ = flags; + DLib dlib(*filename, flags); bool is_opened = dlib.Open(); // Objects containing v14 or later modules will have registered themselves @@ -2229,17 +2271,22 @@ static void DLOpen(const FunctionCallbackInfo& args) { #ifdef _WIN32 // Windows needs to add the filename into the error message errmsg = String::Concat(errmsg, - args[1]->ToString(env->context()).ToLocalChecked()); + args[1]->ToString(context).ToLocalChecked()); #endif // _WIN32 env->isolate()->ThrowException(Exception::Error(errmsg)); return; } if (mp == nullptr) { - dlib.Close(); - env->ThrowError("Module did not self-register."); + if (auto callback = GetInitializerCallback(&dlib)) { + callback(exports, module, context); + } else { + dlib.Close(); + env->ThrowError("Module did not self-register."); + } return; } + if (mp->nm_version == -1) { if (env->EmitNapiWarning()) { if (ProcessEmitWarning(env, "N-API is an experimental feature and could " @@ -2276,22 +2323,8 @@ static void DLOpen(const FunctionCallbackInfo& args) { mp->nm_link = modlist_addon; modlist_addon = mp; - Local exports_string = env->exports_string(); - MaybeLocal maybe_exports = - module->Get(env->context(), exports_string); - - if (maybe_exports.IsEmpty() || - maybe_exports.ToLocalChecked()->ToObject(env->context()).IsEmpty()) { - dlib.Close(); - return; - } - - Local exports = - maybe_exports.ToLocalChecked()->ToObject(env->context()) - .FromMaybe(Local()); - if (mp->nm_context_register_func != nullptr) { - mp->nm_context_register_func(exports, module, env->context(), mp->nm_priv); + 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 { diff --git a/src/node.h b/src/node.h index cb4346893014ef..ab5d1c120fa007 100644 --- a/src/node.h +++ b/src/node.h @@ -535,6 +535,9 @@ extern "C" NODE_EXTERN void node_module_register(void* mod); } \ } +// Usage: `NODE_MODULE(NODE_GYP_MODULE_NAME, InitializerFunction)` +// If no NODE_MODULE is declared, Node.js looks for the well-known +// symbol `node_register_module_v${NODE_MODULE_VERSION}`. #define NODE_MODULE(modname, regfunc) \ NODE_MODULE_X(modname, regfunc, NULL, 0) // NOLINT (readability/null_usage) diff --git a/test/addons/hello-world/binding.cc b/test/addons/hello-world/binding.cc index 944f5631956d15..ba6a22d7196d26 100644 --- a/test/addons/hello-world/binding.cc +++ b/test/addons/hello-world/binding.cc @@ -6,8 +6,12 @@ void Method(const v8::FunctionCallbackInfo& args) { args.GetReturnValue().Set(v8::String::NewFromUtf8(isolate, "world")); } -void init(v8::Local exports) { +#define CONCAT(a, b) CONCAT_HELPER(a, b) +#define CONCAT_HELPER(a, b) a##b +#define INITIALIZER CONCAT(node_register_module_v, NODE_MODULE_VERSION) + +extern "C" NODE_MODULE_EXPORT void INITIALIZER(v8::Local exports, + v8::Local module, + v8::Local context) { NODE_SET_METHOD(exports, "hello", Method); } - -NODE_MODULE(NODE_GYP_MODULE_NAME, init)