Skip to content

Commit

Permalink
n-api: add napi_get_module()
Browse files Browse the repository at this point in the history
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
  • Loading branch information
Gabriel Schulhof committed Jun 28, 2019
1 parent 3322686 commit 925097e
Show file tree
Hide file tree
Showing 10 changed files with 75 additions and 95 deletions.
20 changes: 20 additions & 0 deletions doc/api/n-api.md
Original file line number Diff line number Diff line change
Expand Up @@ -4828,6 +4828,26 @@ idempotent.

This API may only be called from the main thread.

## Miscellaneous

### napi_get_module

<!-- YAML
added: REPLACEME
-->
```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
Expand Down
1 change: 0 additions & 1 deletion src/env.h
Original file line number Diff line number Diff line change
Expand Up @@ -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") \

Expand Down
74 changes: 35 additions & 39 deletions src/node_api.cc
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,9 @@
#include <memory>

struct node_napi_env__ : public napi_env__ {
explicit node_napi_env__(v8::Local<v8::Context> context):
napi_env__(context) {
node_napi_env__(v8::Local<v8::Context> context, v8::Local<v8::Object> module):
napi_env__(context),
persistent_module(context->GetIsolate(), module) {
CHECK_NOT_NULL(node_env());
}

Expand All @@ -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<v8::Object> persistent_module;
};

typedef node_napi_env__* node_napi_env;
Expand Down Expand Up @@ -59,44 +62,24 @@ class BufferFinalizer : private Finalizer {
}
};

static inline napi_env GetEnv(v8::Local<v8::Context> context) {
static inline napi_env NewEnv(v8::Local<v8::Context> context,
v8::Local<v8::Object> 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<node_napi_env>(value.As<v8::External>()->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<napi_env>(arg)->Unref();
},
static_cast<void*>(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<napi_env>(arg)->Unref();
},
static_cast<void*>(result));

return result;
}
Expand Down Expand Up @@ -480,7 +463,7 @@ void napi_module_register_by_symbol(v8::Local<v8::Object> 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<v8::Object>());

napi_value _exports;
NapiCallIntoModuleThrow(env, [&]() {
Expand Down Expand Up @@ -1111,3 +1094,16 @@ napi_ref_threadsafe_function(napi_env env, napi_threadsafe_function func) {
CHECK_NOT_NULL(func);
return reinterpret_cast<v8impl::ThreadSafeFunction*>(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<node_napi_env>(env);

*result = v8impl::JsValueFromV8LocalValue(
v8::Local<v8::Object>::New(env->isolate, node_env->persistent_module));

return napi_clear_last_error(env);
}
7 changes: 7 additions & 0 deletions src/node_api.h
Original file line number Diff line number Diff line change
Expand Up @@ -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_
12 changes: 0 additions & 12 deletions test/node-api/test_env_sharing/binding.gyp

This file was deleted.

23 changes: 0 additions & 23 deletions test/node-api/test_env_sharing/compare_env.c

This file was deleted.

10 changes: 0 additions & 10 deletions test/node-api/test_env_sharing/store_env.c

This file was deleted.

9 changes: 0 additions & 9 deletions test/node-api/test_env_sharing/test.js

This file was deleted.

7 changes: 6 additions & 1 deletion test/node-api/test_general/test.js
Original file line number Diff line number Diff line change
@@ -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());
7 changes: 7 additions & 0 deletions test/node-api/test_general/test_general.c
Original file line number Diff line number Diff line change
Expand Up @@ -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(
Expand Down

0 comments on commit 925097e

Please sign in to comment.