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: https://github.com/nodejs/node/issues/19845 PR-URL: https://github.com/nodejs/node/pull/20161 Reviewed-By: Ben Noordhuis <info@bnoordhuis.nl>
This commit is contained in:
parent
3bcd8576fc
commit
0f8caf23be
@ -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][].
|
||||
|
||||
|
@ -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.");
|
||||
|
@ -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 {
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
||||
|
@ -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)
|
||||
|
@ -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);
|
||||
|
Loading…
x
Reference in New Issue
Block a user