diff --git a/doc/api/n-api.md b/doc/api/n-api.md index 64d1f9aa322457..16b237a51275c9 100644 --- a/doc/api/n-api.md +++ b/doc/api/n-api.md @@ -4828,6 +4828,26 @@ idempotent. This API may only be called from the main thread. +## Miscellaneous + +### napi_get_module + + +```C +NAPI_EXTERN napi_status +napi_get_module(napi_env env, napi_value* result); +``` + +- `[in] env`: The environment that the API is invoked under. +- `[out] result`: The JavaScript object corresponding to the addon's module. + +This API is used to retrieve the Node.js module whose exports are initialized +in the addon's initialization function. The result can be used to obtain the +addon's absolute path, as well as the JavaScript `require()` function, which can +then be used to retrieve other JavaScript modules. + [ABI Stability]: https://nodejs.org/en/docs/guides/abi-stability/ [ECMAScript Language Specification]: https://tc39.github.io/ecma262/ [Error Handling]: #n_api_error_handling diff --git a/src/env.h b/src/env.h index 419b22b246c390..a937b6a9bde8d3 100644 --- a/src/env.h +++ b/src/env.h @@ -149,7 +149,6 @@ constexpr size_t kFsStatsBufferLength = V(contextify_context_private_symbol, "node:contextify:context") \ V(contextify_global_private_symbol, "node:contextify:global") \ V(decorated_private_symbol, "node:decorated") \ - V(napi_env, "node:napi:env") \ V(napi_wrapper, "node:napi:wrapper") \ V(sab_lifetimepartner_symbol, "node:sharedArrayBufferLifetimePartner") \ diff --git a/src/node_api.cc b/src/node_api.cc index a10664d3e344fe..dca8d259190fd5 100644 --- a/src/node_api.cc +++ b/src/node_api.cc @@ -12,8 +12,9 @@ #include struct node_napi_env__ : public napi_env__ { - explicit node_napi_env__(v8::Local context): - napi_env__(context) { + node_napi_env__(v8::Local context, v8::Local module): + napi_env__(context), + persistent_module(context->GetIsolate(), module) { CHECK_NOT_NULL(node_env()); } @@ -24,6 +25,8 @@ struct node_napi_env__ : public napi_env__ { bool can_call_into_js() const override { return node_env()->can_call_into_js(); } + + v8impl::Persistent persistent_module; }; typedef node_napi_env__* node_napi_env; @@ -59,44 +62,24 @@ class BufferFinalizer : private Finalizer { } }; -static inline napi_env GetEnv(v8::Local context) { +static inline napi_env NewEnv(v8::Local context, + v8::Local module) { node_napi_env result; - auto isolate = context->GetIsolate(); - auto global = context->Global(); - - // In the case of the string for which we grab the private and the value of - // the private on the global object we can call .ToLocalChecked() directly - // because we need to stop hard if either of them is empty. - // - // Re https://github.com/nodejs/node/pull/14217#discussion_r128775149 - auto value = global->GetPrivate(context, NAPI_PRIVATE_KEY(context, env)) - .ToLocalChecked(); - - if (value->IsExternal()) { - result = static_cast(value.As()->Value()); - } else { - result = new node_napi_env__(context); - auto external = v8::External::New(isolate, result); - - // We must also stop hard if the result of assigning the env to the global - // is either nothing or false. - CHECK(global->SetPrivate(context, NAPI_PRIVATE_KEY(context, env), external) - .FromJust()); - - // TODO(addaleax): There was previously code that tried to delete the - // napi_env when its v8::Context was garbage collected; - // However, as long as N-API addons using this napi_env are in place, - // the Context needs to be accessible and alive. - // Ideally, we'd want an on-addon-unload hook that takes care of this - // once all N-API addons using this napi_env are unloaded. - // For now, a per-Environment cleanup hook is the best we can do. - result->node_env()->AddCleanupHook( - [](void* arg) { - static_cast(arg)->Unref(); - }, - static_cast(result)); - } + result = new node_napi_env__(context, module); + + // TODO(addaleax): There was previously code that tried to delete the + // napi_env when its v8::Context was garbage collected; + // However, as long as N-API addons using this napi_env are in place, + // the Context needs to be accessible and alive. + // Ideally, we'd want an on-addon-unload hook that takes care of this + // once all N-API addons using this napi_env are unloaded. + // For now, a per-Environment cleanup hook is the best we can do. + result->node_env()->AddCleanupHook( + [](void* arg) { + static_cast(arg)->Unref(); + }, + static_cast(result)); return result; } @@ -480,7 +463,7 @@ void napi_module_register_by_symbol(v8::Local exports, // 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_env env = v8impl::NewEnv(context, module.As()); napi_value _exports; NapiCallIntoModuleThrow(env, [&]() { @@ -1111,3 +1094,16 @@ napi_ref_threadsafe_function(napi_env env, napi_threadsafe_function func) { CHECK_NOT_NULL(func); return reinterpret_cast(func)->Ref(); } + +napi_status +napi_get_module(napi_env env, napi_value* result) { + CHECK_ENV(env); + CHECK_ARG(env, result); + + node_napi_env node_env = static_cast(env); + + *result = v8impl::JsValueFromV8LocalValue( + v8::Local::New(env->isolate, node_env->persistent_module)); + + return napi_clear_last_error(env); +} diff --git a/src/node_api.h b/src/node_api.h index a37ee06ce16a70..14d6fbaa5dabd8 100644 --- a/src/node_api.h +++ b/src/node_api.h @@ -232,6 +232,13 @@ napi_ref_threadsafe_function(napi_env env, napi_threadsafe_function func); #endif // NAPI_VERSION >= 4 +#ifdef NAPI_EXPERIMENTAL + +NAPI_EXTERN napi_status +napi_get_module(napi_env env, napi_value* result); + +#endif // NAPI_EXPERIMENTAL + EXTERN_C_END #endif // SRC_NODE_API_H_ diff --git a/test/node-api/test_env_sharing/binding.gyp b/test/node-api/test_env_sharing/binding.gyp deleted file mode 100644 index 5699a8391dd347..00000000000000 --- a/test/node-api/test_env_sharing/binding.gyp +++ /dev/null @@ -1,12 +0,0 @@ -{ - "targets": [ - { - "target_name": "store_env", - "sources": [ "store_env.c" ] - }, - { - "target_name": "compare_env", - "sources": [ "compare_env.c" ] - } - ] -} diff --git a/test/node-api/test_env_sharing/compare_env.c b/test/node-api/test_env_sharing/compare_env.c deleted file mode 100644 index 0c365f7e343dca..00000000000000 --- a/test/node-api/test_env_sharing/compare_env.c +++ /dev/null @@ -1,23 +0,0 @@ -#include -#include "../../js-native-api/common.h" - -static napi_value compare(napi_env env, napi_callback_info info) { - napi_value external; - size_t argc = 1; - void* data; - napi_value return_value; - - NAPI_CALL(env, napi_get_cb_info(env, info, &argc, &external, NULL, NULL)); - NAPI_CALL(env, napi_get_value_external(env, external, &data)); - NAPI_CALL(env, napi_get_boolean(env, ((napi_env)data) == env, &return_value)); - - return return_value; -} - -static napi_value Init(napi_env env, napi_value exports) { - NAPI_CALL(env, napi_create_function( - env, "exports", NAPI_AUTO_LENGTH, compare, NULL, &exports)); - return exports; -} - -NAPI_MODULE(NODE_GYP_MODULE_NAME, Init) diff --git a/test/node-api/test_env_sharing/store_env.c b/test/node-api/test_env_sharing/store_env.c deleted file mode 100644 index 6f59cf1fc0f2d9..00000000000000 --- a/test/node-api/test_env_sharing/store_env.c +++ /dev/null @@ -1,10 +0,0 @@ -#include -#include "../../js-native-api/common.h" - -static napi_value Init(napi_env env, napi_value exports) { - napi_value external; - NAPI_CALL(env, napi_create_external(env, env, NULL, NULL, &external)); - return external; -} - -NAPI_MODULE(NODE_GYP_MODULE_NAME, Init) diff --git a/test/node-api/test_env_sharing/test.js b/test/node-api/test_env_sharing/test.js deleted file mode 100644 index 0a3507177d56fa..00000000000000 --- a/test/node-api/test_env_sharing/test.js +++ /dev/null @@ -1,9 +0,0 @@ -'use strict'; - -const common = require('../../common'); -const storeEnv = require(`./build/${common.buildType}/store_env`); -const compareEnv = require(`./build/${common.buildType}/compare_env`); -const assert = require('assert'); - -// N-API environment pointers in two different modules have the same value -assert.strictEqual(compareEnv(storeEnv), true); diff --git a/test/node-api/test_general/test.js b/test/node-api/test_general/test.js index 108d51a1e11ac9..67850c0141cce5 100644 --- a/test/node-api/test_general/test.js +++ b/test/node-api/test_general/test.js @@ -1,10 +1,15 @@ 'use strict'; const common = require('../../common'); -const test_general = require(`./build/${common.buildType}/test_general`); +const addonPath = `./build/${common.buildType}/test_general`; +const resolvedPath = require.resolve(addonPath); +const test_general = require(addonPath); +const test_general_module = require.cache[resolvedPath]; const assert = require('assert'); const [ major, minor, patch, release ] = test_general.testGetNodeVersion(); assert.strictEqual(process.version.split('-')[0], `v${major}.${minor}.${patch}`); assert.strictEqual(release, process.release.name); + +assert.strictEqual(test_general_module, test_general.testGetModule()); diff --git a/test/node-api/test_general/test_general.c b/test/node-api/test_general/test_general.c index 05bccaf5c2cf4f..7840d36c840831 100644 --- a/test/node-api/test_general/test_general.c +++ b/test/node-api/test_general/test_general.c @@ -22,9 +22,16 @@ static napi_value testGetNodeVersion(napi_env env, napi_callback_info info) { return result; } +static napi_value testGetModule(napi_env env, napi_callback_info info) { + napi_value module; + NAPI_CALL(env, napi_get_module(env, &module)); + return module; +} + static napi_value Init(napi_env env, napi_value exports) { napi_property_descriptor descriptors[] = { DECLARE_NAPI_PROPERTY("testGetNodeVersion", testGetNodeVersion), + DECLARE_NAPI_PROPERTY("testGetModule", testGetModule), }; NAPI_CALL(env, napi_define_properties(