Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
src: make process.dlopen() load well-known symbol
Look for symbol `node_register_module_v${NODE_MODULE_VERSION}` if the
add-on didn't self-register.  This can be used to create add-ons that
support multiple Node.js versions from a single shared object.

PR-URL: #18934
Reviewed-By: Colin Ihrig <cjihrig@gmail.com>
Reviewed-By: Daniel Bevenius <daniel.bevenius@gmail.com>
Reviewed-By: Franziska Hinkelmann <franziska.hinkelmann@gmail.com>
Reviewed-By: James M Snell <jasnell@gmail.com>
Reviewed-By: Joyee Cheung <joyeec9h3@gmail.com>
Reviewed-By: Matheus Marchini <matheus@sthima.com>
Reviewed-By: Tobias Nießen <tniessen@tnie.de>
  • Loading branch information
bnoordhuis committed Mar 1, 2018
commit 3828fc6289783a632b00270d6b72b5c5bdd6437d
29 changes: 27 additions & 2 deletions src/node.cc
Original file line number Diff line number Diff line change
Expand Up @@ -2158,6 +2158,7 @@ struct DLib {

inline bool Open();
inline void Close();
inline void* GetSymbolAddress(const char* name);

const std::string filename_;
const int flags_;
Expand Down Expand Up @@ -2185,6 +2186,10 @@ void DLib::Close() {
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_);
Expand All @@ -2202,8 +2207,23 @@ void DLib::Close() {
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<Object> exports,
Local<Value> module,
Local<Context> context);

inline InitializerCallback GetInitializerCallback(DLib* dlib) {
const char* name = "node_register_module_v" STRINGIFY(NODE_MODULE_VERSION);
return reinterpret_cast<InitializerCallback>(dlib->GetSymbolAddress(name));
}

// DLOpen is process.dlopen(module, filename, flags).
// Used to load 'module.node' dynamically shared objects.
//
Expand Down Expand Up @@ -2258,10 +2278,15 @@ static void DLOpen(const FunctionCallbackInfo<Value>& args) {
}

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 "
Expand Down
3 changes: 3 additions & 0 deletions src/node.h
Original file line number Diff line number Diff line change
Expand Up @@ -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)

Expand Down
10 changes: 7 additions & 3 deletions test/addons/hello-world/binding.cc
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,12 @@ void Method(const v8::FunctionCallbackInfo<v8::Value>& args) {
args.GetReturnValue().Set(v8::String::NewFromUtf8(isolate, "world"));
}

void init(v8::Local<v8::Object> 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<v8::Object> exports,
v8::Local<v8::Value> module,
v8::Local<v8::Context> context) {
NODE_SET_METHOD(exports, "hello", Method);
}

NODE_MODULE(NODE_GYP_MODULE_NAME, init)