Skip to content

Commit cdb7f13

Browse files
author
Gabriel Schulhof
committed
n-api: re-use napi_env between modules
Store the `napi_env` on the global object at a private key. This gives us one `napi_env` per context. Re: #13872 (comment)
1 parent ac81267 commit cdb7f13

File tree

5 files changed

+98
-3
lines changed

5 files changed

+98
-3
lines changed

src/node_api.cc

Lines changed: 42 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -706,6 +706,45 @@ bool FindWrapper(v8::Local<v8::Object> obj,
706706
return true;
707707
}
708708

709+
static void DeleteEnv(napi_env env, void* data, void* hint) {
710+
delete env;
711+
}
712+
713+
napi_env GetEnv(v8::Local<v8::Context> context) {
714+
napi_env result;
715+
716+
auto isolate = context->GetIsolate();
717+
auto global = context->Global();
718+
719+
// In the case of the string for which we grab the private and the value of
720+
// the private on the global object we can call .ToLocalChecked() directly
721+
// because we need to stop hard if either of them is empty.
722+
//
723+
// Re https://github.com/nodejs/node/pull/14217#discussion_r128775149
724+
auto key = v8::Private::ForApi(isolate,
725+
v8::String::NewFromOneByte(isolate,
726+
reinterpret_cast<const uint8_t*>("N-API Environment"),
727+
v8::NewStringType::kInternalized).ToLocalChecked());
728+
auto value = global->GetPrivate(context, key).ToLocalChecked();
729+
730+
if (value->IsExternal()) {
731+
result = static_cast<napi_env>(value.As<v8::External>()->Value());
732+
} else {
733+
result = new napi_env__(isolate);
734+
auto external = v8::External::New(isolate, result);
735+
736+
// We must also stop hard if the result of assigning the env to the global
737+
// is either nothing or false.
738+
CHECK(global->SetPrivate(context, key, external).FromJust());
739+
740+
// Create a self-destructing reference to external that will get rid of the
741+
// napi_env when external goes out of scope.
742+
Reference::New(result, external, 0, true, DeleteEnv, nullptr, nullptr);
743+
}
744+
745+
return result;
746+
}
747+
709748
} // end of namespace v8impl
710749

711750
// Intercepts the Node-V8 module registration callback. Converts parameters
@@ -717,9 +756,9 @@ void napi_module_register_cb(v8::Local<v8::Object> exports,
717756
void* priv) {
718757
napi_module* mod = static_cast<napi_module*>(priv);
719758

720-
// Create a new napi_env for this module. Once module unloading is supported
721-
// we shall have to call delete on this object from there.
722-
napi_env env = new napi_env__(context->GetIsolate());
759+
// Create a new napi_env for this module or reference one if a pre-existing
760+
// one is found.
761+
napi_env env = v8impl::GetEnv(context);
723762

724763
mod->nm_register_func(
725764
env,
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
{
2+
"targets": [
3+
{
4+
"target_name": "store_env",
5+
"sources": [ "store_env.c" ]
6+
},
7+
{
8+
"target_name": "compare_env",
9+
"sources": [ "compare_env.c" ]
10+
}
11+
]
12+
}
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
#include <node_api.h>
2+
#include "../common.h"
3+
4+
napi_value compare(napi_env env, napi_callback_info info) {
5+
napi_value external;
6+
size_t argc = 1;
7+
void* data;
8+
napi_value return_value;
9+
10+
NAPI_CALL(env, napi_get_cb_info(env, info, &argc, &external, NULL, NULL));
11+
NAPI_CALL(env, napi_get_value_external(env, external, &data));
12+
NAPI_CALL(env, napi_get_boolean(env, ((napi_env)data) == env, &return_value));
13+
14+
return return_value;
15+
}
16+
17+
void Init(napi_env env, napi_value exports, napi_value module, void* context) {
18+
napi_property_descriptor prop = DECLARE_NAPI_PROPERTY("exports", compare);
19+
NAPI_CALL_RETURN_VOID(env, napi_define_properties(env, module, 1, &prop));
20+
}
21+
22+
NAPI_MODULE(compare_env, Init)
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
#include <node_api.h>
2+
#include "../common.h"
3+
4+
void Init(napi_env env, napi_value exports, napi_value module, void* context) {
5+
napi_value external;
6+
NAPI_CALL_RETURN_VOID(env,
7+
napi_create_external(env, env, NULL, NULL, &external));
8+
NAPI_CALL_RETURN_VOID(env,
9+
napi_set_named_property(env, module, "exports", external));
10+
}
11+
12+
NAPI_MODULE(store_env, Init)
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
'use strict';
2+
3+
const common = require('../../common');
4+
const storeEnv = require(`./build/${common.buildType}/store_env`);
5+
const compareEnv = require(`./build/${common.buildType}/compare_env`);
6+
const assert = require('assert');
7+
8+
assert.strictEqual(compareEnv(storeEnv), true,
9+
'N-API environment pointers in two different modules have ' +
10+
'the same value');

0 commit comments

Comments
 (0)