Skip to content

Commit cd83df3

Browse files
Gabriel SchulhofMylesBorins
Gabriel Schulhof
authored andcommitted
n-api: initialize a module via a special symbol
Much like regular modules, N-API modules can also benefit from having a special symbol which they can expose. Fixes: #19845 PR-URL: #20161 Reviewed-By: Ben Noordhuis <[email protected]>
1 parent 4289402 commit cd83df3

File tree

7 files changed

+80
-11
lines changed

7 files changed

+80
-11
lines changed

doc/api/n-api.md

+26
Original file line numberDiff line numberDiff line change
@@ -982,6 +982,32 @@ napi_value Init(napi_env env, napi_value exports) {
982982
}
983983
```
984984

985+
If you expect that your module will be loaded multiple times during the lifetime
986+
of the Node.js process, you can use the `NAPI_MODULE_INIT` macro to initialize
987+
your module:
988+
989+
```C
990+
NAPI_MODULE_INIT() {
991+
napi_value answer;
992+
napi_status result;
993+
994+
status = napi_create_int64(env, 42, &answer);
995+
if (status != napi_ok) return NULL;
996+
997+
status = napi_set_named_property(env, exports, "answer", answer);
998+
if (status != napi_ok) return NULL;
999+
1000+
return exports;
1001+
}
1002+
```
1003+
1004+
This macro includes `NAPI_MODULE`, and declares an `Init` function with a
1005+
special name and with visibility beyond the addon. This will allow Node.js to
1006+
initialize the module even if it is loaded multiple times.
1007+
1008+
The variables `env` and `exports` will be available inside the function body
1009+
following the macro invocation.
1010+
9851011
For more details on setting properties on objects, see the section on
9861012
[Working with JavaScript Properties][].
9871013

src/node.cc

+9
Original file line numberDiff line numberDiff line change
@@ -2227,6 +2227,13 @@ inline InitializerCallback GetInitializerCallback(DLib* dlib) {
22272227
return reinterpret_cast<InitializerCallback>(dlib->GetSymbolAddress(name));
22282228
}
22292229

2230+
inline napi_addon_register_func GetNapiInitializerCallback(DLib* dlib) {
2231+
const char* name =
2232+
STRINGIFY(NAPI_MODULE_INITIALIZER_BASE) STRINGIFY(NAPI_MODULE_VERSION);
2233+
return
2234+
reinterpret_cast<napi_addon_register_func>(dlib->GetSymbolAddress(name));
2235+
}
2236+
22302237
// DLOpen is process.dlopen(module, filename, flags).
22312238
// Used to load 'module.node' dynamically shared objects.
22322239
//
@@ -2283,6 +2290,8 @@ static void DLOpen(const FunctionCallbackInfo<Value>& args) {
22832290
if (mp == nullptr) {
22842291
if (auto callback = GetInitializerCallback(&dlib)) {
22852292
callback(exports, module, context);
2293+
} else if (auto napi_callback = GetNapiInitializerCallback(&dlib)) {
2294+
napi_module_register_by_symbol(exports, module, context, napi_callback);
22862295
} else {
22872296
dlib.Close();
22882297
env->ThrowError("Module did not self-register.");

src/node_api.cc

+10-5
Original file line numberDiff line numberDiff line change
@@ -858,16 +858,23 @@ void napi_module_register_cb(v8::Local<v8::Object> exports,
858858
v8::Local<v8::Value> module,
859859
v8::Local<v8::Context> context,
860860
void* priv) {
861-
napi_module* mod = static_cast<napi_module*>(priv);
861+
napi_module_register_by_symbol(exports, module, context,
862+
static_cast<napi_module*>(priv)->nm_register_func);
863+
}
864+
865+
} // end of anonymous namespace
862866

867+
void napi_module_register_by_symbol(v8::Local<v8::Object> exports,
868+
v8::Local<v8::Value> module,
869+
v8::Local<v8::Context> context,
870+
napi_addon_register_func init) {
863871
// Create a new napi_env for this module or reference one if a pre-existing
864872
// one is found.
865873
napi_env env = v8impl::GetEnv(context);
866874

867875
napi_value _exports;
868876
NAPI_CALL_INTO_MODULE_THROW(env,
869-
_exports = mod->nm_register_func(env,
870-
v8impl::JsValueFromV8LocalValue(exports)));
877+
_exports = init(env, v8impl::JsValueFromV8LocalValue(exports)));
871878

872879
// If register function returned a non-null exports object different from
873880
// the exports object we passed it, set that as the "exports" property of
@@ -879,8 +886,6 @@ void napi_module_register_cb(v8::Local<v8::Object> exports,
879886
}
880887
}
881888

882-
} // end of anonymous namespace
883-
884889
// Registers a NAPI module.
885890
void napi_module_register(napi_module* mod) {
886891
node::node_module* nm = new node::node_module {

src/node_api.h

+20-1
Original file line numberDiff line numberDiff line change
@@ -90,9 +90,28 @@ typedef struct {
9090
} \
9191
EXTERN_C_END
9292

93-
#define NAPI_MODULE(modname, regfunc) \
93+
#define NAPI_MODULE(modname, regfunc) \
9494
NAPI_MODULE_X(modname, regfunc, NULL, 0) // NOLINT (readability/null_usage)
9595

96+
#define NAPI_MODULE_INITIALIZER_BASE napi_register_module_v
97+
98+
#define NAPI_MODULE_INITIALIZER_X(base, version) \
99+
NAPI_MODULE_INITIALIZER_X_HELPER(base, version)
100+
#define NAPI_MODULE_INITIALIZER_X_HELPER(base, version) base##version
101+
102+
#define NAPI_MODULE_INITIALIZER \
103+
NAPI_MODULE_INITIALIZER_X(NAPI_MODULE_INITIALIZER_BASE, \
104+
NAPI_MODULE_VERSION)
105+
106+
#define NAPI_MODULE_INIT() \
107+
EXTERN_C_START \
108+
NAPI_MODULE_EXPORT napi_value \
109+
NAPI_MODULE_INITIALIZER(napi_env env, napi_value exports); \
110+
EXTERN_C_END \
111+
NAPI_MODULE(NODE_GYP_MODULE_NAME, NAPI_MODULE_INITIALIZER) \
112+
napi_value NAPI_MODULE_INITIALIZER(napi_env env, \
113+
napi_value exports)
114+
96115
#define NAPI_AUTO_LENGTH SIZE_MAX
97116

98117
EXTERN_C_START

src/node_internals.h

+5
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@
3333
#include "tracing/trace_event.h"
3434
#include "node_perf_common.h"
3535
#include "node_debug_options.h"
36+
#include "node_api.h"
3637

3738
#include <stdint.h>
3839
#include <stdlib.h>
@@ -840,6 +841,10 @@ static inline const char *errno_string(int errorno) {
840841

841842
} // namespace node
842843

844+
void napi_module_register_by_symbol(v8::Local<v8::Object> exports,
845+
v8::Local<v8::Value> module,
846+
v8::Local<v8::Context> context,
847+
napi_addon_register_func init);
843848

844849
#endif // defined(NODE_WANT_INTERNALS) && NODE_WANT_INTERNALS
845850

test/addons-napi/1_hello_world/binding.c

+1-3
Original file line numberDiff line numberDiff line change
@@ -10,10 +10,8 @@ napi_value Method(napi_env env, napi_callback_info info) {
1010
return world;
1111
}
1212

13-
napi_value Init(napi_env env, napi_value exports) {
13+
NAPI_MODULE_INIT() {
1414
napi_property_descriptor desc = DECLARE_NAPI_PROPERTY("hello", Method);
1515
NAPI_CALL(env, napi_define_properties(env, exports, 1, &desc));
1616
return exports;
1717
}
18-
19-
NAPI_MODULE(NODE_GYP_MODULE_NAME, Init)
+9-2
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,13 @@
11
'use strict';
22
const common = require('../../common');
33
const assert = require('assert');
4-
const addon = require(`./build/${common.buildType}/binding`);
4+
const bindingPath = require.resolve(`./build/${common.buildType}/binding`);
5+
const binding = require(bindingPath);
6+
assert.strictEqual(binding.hello(), 'world');
7+
console.log('binding.hello() =', binding.hello());
58

6-
assert.strictEqual(addon.hello(), 'world');
9+
// Test multiple loading of the same module.
10+
delete require.cache[bindingPath];
11+
const rebinding = require(bindingPath);
12+
assert.strictEqual(rebinding.hello(), 'world');
13+
assert.notStrictEqual(binding.hello, rebinding.hello);

0 commit comments

Comments
 (0)