summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--doc/api/n-api.md26
-rw-r--r--src/node.cc9
-rw-r--r--src/node_api.cc15
-rw-r--r--src/node_api.h21
-rw-r--r--src/node_internals.h5
-rw-r--r--test/addons-napi/1_hello_world/binding.c4
-rw-r--r--test/addons-napi/1_hello_world/test.js11
7 files changed, 80 insertions, 11 deletions
diff --git a/doc/api/n-api.md b/doc/api/n-api.md
index 826b83e225..26b5e87463 100644
--- a/doc/api/n-api.md
+++ b/doc/api/n-api.md
@@ -982,6 +982,32 @@ napi_value Init(napi_env env, napi_value exports) {
}
```
+If you expect that your module will be loaded multiple times during the lifetime
+of the Node.js process, you can use the `NAPI_MODULE_INIT` macro to initialize
+your module:
+
+```C
+NAPI_MODULE_INIT() {
+ napi_value answer;
+ napi_status result;
+
+ status = napi_create_int64(env, 42, &answer);
+ if (status != napi_ok) return NULL;
+
+ status = napi_set_named_property(env, exports, "answer", answer);
+ if (status != napi_ok) return NULL;
+
+ return exports;
+}
+```
+
+This macro includes `NAPI_MODULE`, and declares an `Init` function with a
+special name and with visibility beyond the addon. This will allow Node.js to
+initialize the module even if it is loaded multiple times.
+
+The variables `env` and `exports` will be available inside the function body
+following the macro invocation.
+
For more details on setting properties on objects, see the section on
[Working with JavaScript Properties][].
diff --git a/src/node.cc b/src/node.cc
index 355b71de67..7374678198 100644
--- a/src/node.cc
+++ b/src/node.cc
@@ -2229,6 +2229,13 @@ inline InitializerCallback GetInitializerCallback(DLib* dlib) {
return reinterpret_cast<InitializerCallback>(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<napi_addon_register_func>(dlib->GetSymbolAddress(name));
+}
+
// DLOpen is process.dlopen(module, filename, flags).
// Used to load 'module.node' dynamically shared objects.
//
@@ -2285,6 +2292,8 @@ static void DLOpen(const FunctionCallbackInfo<Value>& args) {
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.");
diff --git a/src/node_api.cc b/src/node_api.cc
index ea7ddba77d..4878cf241e 100644
--- a/src/node_api.cc
+++ b/src/node_api.cc
@@ -858,16 +858,23 @@ void napi_module_register_cb(v8::Local<v8::Object> exports,
v8::Local<v8::Value> module,
v8::Local<v8::Context> context,
void* priv) {
- napi_module* mod = static_cast<napi_module*>(priv);
+ napi_module_register_by_symbol(exports, module, context,
+ static_cast<napi_module*>(priv)->nm_register_func);
+}
+
+} // end of anonymous namespace
+void napi_module_register_by_symbol(v8::Local<v8::Object> exports,
+ v8::Local<v8::Value> module,
+ v8::Local<v8::Context> context,
+ napi_addon_register_func init) {
// Create a new napi_env for this module or reference one if a pre-existing
// one is found.
napi_env env = v8impl::GetEnv(context);
napi_value _exports;
NAPI_CALL_INTO_MODULE_THROW(env,
- _exports = mod->nm_register_func(env,
- v8impl::JsValueFromV8LocalValue(exports)));
+ _exports = init(env, v8impl::JsValueFromV8LocalValue(exports)));
// If register function returned a non-null exports object different from
// 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,
}
}
-} // end of anonymous namespace
-
// Registers a NAPI module.
void napi_module_register(napi_module* mod) {
node::node_module* nm = new node::node_module {
diff --git a/src/node_api.h b/src/node_api.h
index aaf002b758..b010d32db7 100644
--- a/src/node_api.h
+++ b/src/node_api.h
@@ -90,9 +90,28 @@ typedef struct {
} \
EXTERN_C_END
-#define NAPI_MODULE(modname, regfunc) \
+#define NAPI_MODULE(modname, regfunc) \
NAPI_MODULE_X(modname, regfunc, NULL, 0) // NOLINT (readability/null_usage)
+#define NAPI_MODULE_INITIALIZER_BASE napi_register_module_v
+
+#define NAPI_MODULE_INITIALIZER_X(base, version) \
+ NAPI_MODULE_INITIALIZER_X_HELPER(base, version)
+#define NAPI_MODULE_INITIALIZER_X_HELPER(base, version) base##version
+
+#define NAPI_MODULE_INITIALIZER \
+ NAPI_MODULE_INITIALIZER_X(NAPI_MODULE_INITIALIZER_BASE, \
+ NAPI_MODULE_VERSION)
+
+#define NAPI_MODULE_INIT() \
+ EXTERN_C_START \
+ NAPI_MODULE_EXPORT napi_value \
+ NAPI_MODULE_INITIALIZER(napi_env env, napi_value exports); \
+ EXTERN_C_END \
+ NAPI_MODULE(NODE_GYP_MODULE_NAME, NAPI_MODULE_INITIALIZER) \
+ napi_value NAPI_MODULE_INITIALIZER(napi_env env, \
+ napi_value exports)
+
#define NAPI_AUTO_LENGTH SIZE_MAX
EXTERN_C_START
diff --git a/src/node_internals.h b/src/node_internals.h
index 50047e3307..b8acfa63c2 100644
--- a/src/node_internals.h
+++ b/src/node_internals.h
@@ -33,6 +33,7 @@
#include "tracing/trace_event.h"
#include "node_perf_common.h"
#include "node_debug_options.h"
+#include "node_api.h"
#include <stdint.h>
#include <stdlib.h>
@@ -840,6 +841,10 @@ static inline const char *errno_string(int errorno) {
} // namespace node
+void napi_module_register_by_symbol(v8::Local<v8::Object> exports,
+ v8::Local<v8::Value> module,
+ v8::Local<v8::Context> context,
+ napi_addon_register_func init);
#endif // defined(NODE_WANT_INTERNALS) && NODE_WANT_INTERNALS
diff --git a/test/addons-napi/1_hello_world/binding.c b/test/addons-napi/1_hello_world/binding.c
index 27d4907996..6efc14ee66 100644
--- a/test/addons-napi/1_hello_world/binding.c
+++ b/test/addons-napi/1_hello_world/binding.c
@@ -10,10 +10,8 @@ napi_value Method(napi_env env, napi_callback_info info) {
return world;
}
-napi_value Init(napi_env env, napi_value exports) {
+NAPI_MODULE_INIT() {
napi_property_descriptor desc = DECLARE_NAPI_PROPERTY("hello", Method);
NAPI_CALL(env, napi_define_properties(env, exports, 1, &desc));
return exports;
}
-
-NAPI_MODULE(NODE_GYP_MODULE_NAME, Init)
diff --git a/test/addons-napi/1_hello_world/test.js b/test/addons-napi/1_hello_world/test.js
index c975c48a73..d1d67d34f6 100644
--- a/test/addons-napi/1_hello_world/test.js
+++ b/test/addons-napi/1_hello_world/test.js
@@ -1,6 +1,13 @@
'use strict';
const common = require('../../common');
const assert = require('assert');
-const addon = require(`./build/${common.buildType}/binding`);
+const bindingPath = require.resolve(`./build/${common.buildType}/binding`);
+const binding = require(bindingPath);
+assert.strictEqual(binding.hello(), 'world');
+console.log('binding.hello() =', binding.hello());
-assert.strictEqual(addon.hello(), 'world');
+// Test multiple loading of the same module.
+delete require.cache[bindingPath];
+const rebinding = require(bindingPath);
+assert.strictEqual(rebinding.hello(), 'world');
+assert.notStrictEqual(binding.hello, rebinding.hello);