Skip to content

Commit 925097e

Browse files
author
Gabriel Schulhof
committed
n-api: add napi_get_module()
We make the `napi_env` per-module once more because it no longer holds any state that needs to be shared among add-ons. We then add a Node.js-specific N-API which allows one to obtain the module whose exports were initialized at addon startup. Re: nodejs/node-addon-api#449 Re: nodejs/node-addon-api#482 Re: nodejs/node-addon-api#505
1 parent 3322686 commit 925097e

10 files changed

Lines changed: 75 additions & 95 deletions

File tree

doc/api/n-api.md

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4828,6 +4828,26 @@ idempotent.
48284828

48294829
This API may only be called from the main thread.
48304830

4831+
## Miscellaneous
4832+
4833+
### napi_get_module
4834+
4835+
<!-- YAML
4836+
added: REPLACEME
4837+
-->
4838+
```C
4839+
NAPI_EXTERN napi_status
4840+
napi_get_module(napi_env env, napi_value* result);
4841+
```
4842+
4843+
- `[in] env`: The environment that the API is invoked under.
4844+
- `[out] result`: The JavaScript object corresponding to the addon's module.
4845+
4846+
This API is used to retrieve the Node.js module whose exports are initialized
4847+
in the addon's initialization function. The result can be used to obtain the
4848+
addon's absolute path, as well as the JavaScript `require()` function, which can
4849+
then be used to retrieve other JavaScript modules.
4850+
48314851
[ABI Stability]: https://nodejs.org/en/docs/guides/abi-stability/
48324852
[ECMAScript Language Specification]: https://tc39.github.io/ecma262/
48334853
[Error Handling]: #n_api_error_handling

src/env.h

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -149,7 +149,6 @@ constexpr size_t kFsStatsBufferLength =
149149
V(contextify_context_private_symbol, "node:contextify:context") \
150150
V(contextify_global_private_symbol, "node:contextify:global") \
151151
V(decorated_private_symbol, "node:decorated") \
152-
V(napi_env, "node:napi:env") \
153152
V(napi_wrapper, "node:napi:wrapper") \
154153
V(sab_lifetimepartner_symbol, "node:sharedArrayBufferLifetimePartner") \
155154

src/node_api.cc

Lines changed: 35 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,9 @@
1212
#include <memory>
1313

1414
struct node_napi_env__ : public napi_env__ {
15-
explicit node_napi_env__(v8::Local<v8::Context> context):
16-
napi_env__(context) {
15+
node_napi_env__(v8::Local<v8::Context> context, v8::Local<v8::Object> module):
16+
napi_env__(context),
17+
persistent_module(context->GetIsolate(), module) {
1718
CHECK_NOT_NULL(node_env());
1819
}
1920

@@ -24,6 +25,8 @@ struct node_napi_env__ : public napi_env__ {
2425
bool can_call_into_js() const override {
2526
return node_env()->can_call_into_js();
2627
}
28+
29+
v8impl::Persistent<v8::Object> persistent_module;
2730
};
2831

2932
typedef node_napi_env__* node_napi_env;
@@ -59,44 +62,24 @@ class BufferFinalizer : private Finalizer {
5962
}
6063
};
6164

62-
static inline napi_env GetEnv(v8::Local<v8::Context> context) {
65+
static inline napi_env NewEnv(v8::Local<v8::Context> context,
66+
v8::Local<v8::Object> module) {
6367
node_napi_env result;
6468

65-
auto isolate = context->GetIsolate();
66-
auto global = context->Global();
67-
68-
// In the case of the string for which we grab the private and the value of
69-
// the private on the global object we can call .ToLocalChecked() directly
70-
// because we need to stop hard if either of them is empty.
71-
//
72-
// Re https://github.com/nodejs/node/pull/14217#discussion_r128775149
73-
auto value = global->GetPrivate(context, NAPI_PRIVATE_KEY(context, env))
74-
.ToLocalChecked();
75-
76-
if (value->IsExternal()) {
77-
result = static_cast<node_napi_env>(value.As<v8::External>()->Value());
78-
} else {
79-
result = new node_napi_env__(context);
80-
auto external = v8::External::New(isolate, result);
81-
82-
// We must also stop hard if the result of assigning the env to the global
83-
// is either nothing or false.
84-
CHECK(global->SetPrivate(context, NAPI_PRIVATE_KEY(context, env), external)
85-
.FromJust());
86-
87-
// TODO(addaleax): There was previously code that tried to delete the
88-
// napi_env when its v8::Context was garbage collected;
89-
// However, as long as N-API addons using this napi_env are in place,
90-
// the Context needs to be accessible and alive.
91-
// Ideally, we'd want an on-addon-unload hook that takes care of this
92-
// once all N-API addons using this napi_env are unloaded.
93-
// For now, a per-Environment cleanup hook is the best we can do.
94-
result->node_env()->AddCleanupHook(
95-
[](void* arg) {
96-
static_cast<napi_env>(arg)->Unref();
97-
},
98-
static_cast<void*>(result));
99-
}
69+
result = new node_napi_env__(context, module);
70+
71+
// TODO(addaleax): There was previously code that tried to delete the
72+
// napi_env when its v8::Context was garbage collected;
73+
// However, as long as N-API addons using this napi_env are in place,
74+
// the Context needs to be accessible and alive.
75+
// Ideally, we'd want an on-addon-unload hook that takes care of this
76+
// once all N-API addons using this napi_env are unloaded.
77+
// For now, a per-Environment cleanup hook is the best we can do.
78+
result->node_env()->AddCleanupHook(
79+
[](void* arg) {
80+
static_cast<napi_env>(arg)->Unref();
81+
},
82+
static_cast<void*>(result));
10083

10184
return result;
10285
}
@@ -480,7 +463,7 @@ void napi_module_register_by_symbol(v8::Local<v8::Object> exports,
480463

481464
// Create a new napi_env for this module or reference one if a pre-existing
482465
// one is found.
483-
napi_env env = v8impl::GetEnv(context);
466+
napi_env env = v8impl::NewEnv(context, module.As<v8::Object>());
484467

485468
napi_value _exports;
486469
NapiCallIntoModuleThrow(env, [&]() {
@@ -1111,3 +1094,16 @@ napi_ref_threadsafe_function(napi_env env, napi_threadsafe_function func) {
11111094
CHECK_NOT_NULL(func);
11121095
return reinterpret_cast<v8impl::ThreadSafeFunction*>(func)->Ref();
11131096
}
1097+
1098+
napi_status
1099+
napi_get_module(napi_env env, napi_value* result) {
1100+
CHECK_ENV(env);
1101+
CHECK_ARG(env, result);
1102+
1103+
node_napi_env node_env = static_cast<node_napi_env>(env);
1104+
1105+
*result = v8impl::JsValueFromV8LocalValue(
1106+
v8::Local<v8::Object>::New(env->isolate, node_env->persistent_module));
1107+
1108+
return napi_clear_last_error(env);
1109+
}

src/node_api.h

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -232,6 +232,13 @@ napi_ref_threadsafe_function(napi_env env, napi_threadsafe_function func);
232232

233233
#endif // NAPI_VERSION >= 4
234234

235+
#ifdef NAPI_EXPERIMENTAL
236+
237+
NAPI_EXTERN napi_status
238+
napi_get_module(napi_env env, napi_value* result);
239+
240+
#endif // NAPI_EXPERIMENTAL
241+
235242
EXTERN_C_END
236243

237244
#endif // SRC_NODE_API_H_

test/node-api/test_env_sharing/binding.gyp

Lines changed: 0 additions & 12 deletions
This file was deleted.

test/node-api/test_env_sharing/compare_env.c

Lines changed: 0 additions & 23 deletions
This file was deleted.

test/node-api/test_env_sharing/store_env.c

Lines changed: 0 additions & 10 deletions
This file was deleted.

test/node-api/test_env_sharing/test.js

Lines changed: 0 additions & 9 deletions
This file was deleted.

test/node-api/test_general/test.js

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,15 @@
11
'use strict';
22

33
const common = require('../../common');
4-
const test_general = require(`./build/${common.buildType}/test_general`);
4+
const addonPath = `./build/${common.buildType}/test_general`;
5+
const resolvedPath = require.resolve(addonPath);
6+
const test_general = require(addonPath);
7+
const test_general_module = require.cache[resolvedPath];
58
const assert = require('assert');
69

710
const [ major, minor, patch, release ] = test_general.testGetNodeVersion();
811
assert.strictEqual(process.version.split('-')[0],
912
`v${major}.${minor}.${patch}`);
1013
assert.strictEqual(release, process.release.name);
14+
15+
assert.strictEqual(test_general_module, test_general.testGetModule());

test/node-api/test_general/test_general.c

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,9 +22,16 @@ static napi_value testGetNodeVersion(napi_env env, napi_callback_info info) {
2222
return result;
2323
}
2424

25+
static napi_value testGetModule(napi_env env, napi_callback_info info) {
26+
napi_value module;
27+
NAPI_CALL(env, napi_get_module(env, &module));
28+
return module;
29+
}
30+
2531
static napi_value Init(napi_env env, napi_value exports) {
2632
napi_property_descriptor descriptors[] = {
2733
DECLARE_NAPI_PROPERTY("testGetNodeVersion", testGetNodeVersion),
34+
DECLARE_NAPI_PROPERTY("testGetModule", testGetModule),
2835
};
2936

3037
NAPI_CALL(env, napi_define_properties(

0 commit comments

Comments
 (0)