Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

Commit cd83df3

Browse filesBrowse files
Gabriel SchulhofMylesBorins
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 <info@bnoordhuis.nl>
1 parent 4289402 commit cd83df3
Copy full SHA for cd83df3

File tree

Expand file treeCollapse file tree

7 files changed

+80
-11
lines changed
Open diff view settings
Filter options
Expand file treeCollapse file tree

7 files changed

+80
-11
lines changed
Open diff view settings
Collapse file

‎doc/api/n-api.md‎

Copy file name to clipboardExpand all lines: doc/api/n-api.md
+26Lines changed: 26 additions & 0 deletions
  • Display the source diff
  • Display the rich diff
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

Collapse file

‎src/node.cc‎

Copy file name to clipboardExpand all lines: src/node.cc
+9Lines changed: 9 additions & 0 deletions
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.");
Collapse file

‎src/node_api.cc‎

Copy file name to clipboardExpand all lines: src/node_api.cc
+10-5Lines changed: 10 additions & 5 deletions
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 {
Collapse file

‎src/node_api.h‎

Copy file name to clipboardExpand all lines: src/node_api.h
+20-1Lines changed: 20 additions & 1 deletion
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
Collapse file

‎src/node_internals.h‎

Copy file name to clipboardExpand all lines: src/node_internals.h
+5Lines changed: 5 additions & 0 deletions
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

Collapse file

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

Copy file name to clipboardExpand all lines: test/addons-napi/1_hello_world/binding.c
+1-3Lines changed: 1 addition & 3 deletions
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)
Collapse file
+9-2Lines changed: 9 additions & 2 deletions
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 (0)
Morty Proxy This is a proxified and sanitized view of the page, visit original site.