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
|
For more details on setting properties on objects, see the section on
|
||||||
[Working with JavaScript Properties][].
|
[Working with JavaScript Properties][].
|
||||||
|
|
||||||
|
@ -2229,6 +2229,13 @@ inline InitializerCallback GetInitializerCallback(DLib* dlib) {
|
|||||||
return reinterpret_cast<InitializerCallback>(dlib->GetSymbolAddress(name));
|
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).
|
// DLOpen is process.dlopen(module, filename, flags).
|
||||||
// Used to load 'module.node' dynamically shared objects.
|
// Used to load 'module.node' dynamically shared objects.
|
||||||
//
|
//
|
||||||
@ -2285,6 +2292,8 @@ static void DLOpen(const FunctionCallbackInfo<Value>& args) {
|
|||||||
if (mp == nullptr) {
|
if (mp == nullptr) {
|
||||||
if (auto callback = GetInitializerCallback(&dlib)) {
|
if (auto callback = GetInitializerCallback(&dlib)) {
|
||||||
callback(exports, module, context);
|
callback(exports, module, context);
|
||||||
|
} else if (auto napi_callback = GetNapiInitializerCallback(&dlib)) {
|
||||||
|
napi_module_register_by_symbol(exports, module, context, napi_callback);
|
||||||
} else {
|
} else {
|
||||||
dlib.Close();
|
dlib.Close();
|
||||||
env->ThrowError("Module did not self-register.");
|
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::Value> module,
|
||||||
v8::Local<v8::Context> context,
|
v8::Local<v8::Context> context,
|
||||||
void* priv) {
|
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
|
// Create a new napi_env for this module or reference one if a pre-existing
|
||||||
// one is found.
|
// one is found.
|
||||||
napi_env env = v8impl::GetEnv(context);
|
napi_env env = v8impl::GetEnv(context);
|
||||||
|
|
||||||
napi_value _exports;
|
napi_value _exports;
|
||||||
NAPI_CALL_INTO_MODULE_THROW(env,
|
NAPI_CALL_INTO_MODULE_THROW(env,
|
||||||
_exports = mod->nm_register_func(env,
|
_exports = init(env, v8impl::JsValueFromV8LocalValue(exports)));
|
||||||
v8impl::JsValueFromV8LocalValue(exports)));
|
|
||||||
|
|
||||||
// If register function returned a non-null exports object different from
|
// If register function returned a non-null exports object different from
|
||||||
// the exports object we passed it, set that as the "exports" property of
|
// 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.
|
// Registers a NAPI module.
|
||||||
void napi_module_register(napi_module* mod) {
|
void napi_module_register(napi_module* mod) {
|
||||||
node::node_module* nm = new node::node_module {
|
node::node_module* nm = new node::node_module {
|
||||||
|
@ -93,6 +93,25 @@ typedef struct {
|
|||||||
#define NAPI_MODULE(modname, regfunc) \
|
#define NAPI_MODULE(modname, regfunc) \
|
||||||
NAPI_MODULE_X(modname, regfunc, NULL, 0) // NOLINT (readability/null_usage)
|
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
|
#define NAPI_AUTO_LENGTH SIZE_MAX
|
||||||
|
|
||||||
EXTERN_C_START
|
EXTERN_C_START
|
||||||
|
@ -33,6 +33,7 @@
|
|||||||
#include "tracing/trace_event.h"
|
#include "tracing/trace_event.h"
|
||||||
#include "node_perf_common.h"
|
#include "node_perf_common.h"
|
||||||
#include "node_debug_options.h"
|
#include "node_debug_options.h"
|
||||||
|
#include "node_api.h"
|
||||||
|
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
@ -840,6 +841,10 @@ static inline const char *errno_string(int errorno) {
|
|||||||
|
|
||||||
} // namespace node
|
} // 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
|
#endif // defined(NODE_WANT_INTERNALS) && NODE_WANT_INTERNALS
|
||||||
|
|
||||||
|
@ -10,10 +10,8 @@ napi_value Method(napi_env env, napi_callback_info info) {
|
|||||||
return world;
|
return world;
|
||||||
}
|
}
|
||||||
|
|
||||||
napi_value Init(napi_env env, napi_value exports) {
|
NAPI_MODULE_INIT() {
|
||||||
napi_property_descriptor desc = DECLARE_NAPI_PROPERTY("hello", Method);
|
napi_property_descriptor desc = DECLARE_NAPI_PROPERTY("hello", Method);
|
||||||
NAPI_CALL(env, napi_define_properties(env, exports, 1, &desc));
|
NAPI_CALL(env, napi_define_properties(env, exports, 1, &desc));
|
||||||
return exports;
|
return exports;
|
||||||
}
|
}
|
||||||
|
|
||||||
NAPI_MODULE(NODE_GYP_MODULE_NAME, Init)
|
|
||||||
|
@ -1,6 +1,13 @@
|
|||||||
'use strict';
|
'use strict';
|
||||||
const common = require('../../common');
|
const common = require('../../common');
|
||||||
const assert = require('assert');
|
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