@@ -25,6 +25,14 @@ struct napi_env__ {
2525 CHECK_NOT_NULL (node_env ());
2626 }
2727
28+ virtual ~napi_env__ () {
29+ if (instance_data.finalize_cb != nullptr ) {
30+ CallIntoModuleThrow ([&](napi_env env) {
31+ instance_data.finalize_cb (env, instance_data.data , instance_data.hint );
32+ });
33+ }
34+ }
35+
2836 v8::Isolate* const isolate; // Shortcut for context()->GetIsolate()
2937 node::Persistent<v8::Context> context_persistent;
3038
@@ -39,11 +47,37 @@ struct napi_env__ {
3947 inline void Ref () { refs++; }
4048 inline void Unref () { if (--refs == 0 ) delete this ; }
4149
50+ template <typename T, typename U>
51+ void CallIntoModule (T&& call, U&& handle_exception) {
52+ int open_handle_scopes_before = open_handle_scopes;
53+ int open_callback_scopes_before = open_callback_scopes;
54+ napi_clear_last_error (this );
55+ call (this );
56+ CHECK_EQ (open_handle_scopes, open_handle_scopes_before);
57+ CHECK_EQ (open_callback_scopes, open_callback_scopes_before);
58+ if (!last_exception.IsEmpty ()) {
59+ handle_exception (this , last_exception.Get (this ->isolate ));
60+ last_exception.Reset ();
61+ }
62+ }
63+
64+ template <typename T>
65+ void CallIntoModuleThrow (T&& call) {
66+ CallIntoModule (call, [&](napi_env env, v8::Local<v8::Value> value) {
67+ env->isolate ->ThrowException (value);
68+ });
69+ }
70+
4271 node::Persistent<v8::Value> last_exception;
4372 napi_extended_error_info last_error;
4473 int open_handle_scopes = 0 ;
4574 int open_callback_scopes = 0 ;
4675 int refs = 1 ;
76+ struct {
77+ void * data = nullptr ;
78+ void * hint = nullptr ;
79+ napi_finalize finalize_cb = nullptr ;
80+ } instance_data;
4781};
4882
4983#define NAPI_PRIVATE_KEY (context, suffix ) \
@@ -158,27 +192,6 @@ struct napi_env__ {
158192 (out) = v8::type::New ((buffer), (byte_offset), (length)); \
159193 } while (0 )
160194
161- template <typename T, typename U>
162- void NapiCallIntoModule (napi_env env, T&& call, U&& handle_exception) {
163- int open_handle_scopes = env->open_handle_scopes ;
164- int open_callback_scopes = env->open_callback_scopes ;
165- napi_clear_last_error (env);
166- call ();
167- CHECK_EQ (env->open_handle_scopes , open_handle_scopes);
168- CHECK_EQ (env->open_callback_scopes , open_callback_scopes);
169- if (!env->last_exception .IsEmpty ()) {
170- handle_exception (env->last_exception .Get (env->isolate ));
171- env->last_exception .Reset ();
172- }
173- }
174-
175- template <typename T>
176- void NapiCallIntoModuleThrow (napi_env env, T&& call) {
177- NapiCallIntoModule (env, call, [&](v8::Local<v8::Value> value) {
178- env->isolate ->ThrowException (value);
179- });
180- }
181-
182195namespace {
183196namespace v8impl {
184197
@@ -357,11 +370,8 @@ class Finalizer {
357370 static void FinalizeBufferCallback (char * data, void * hint) {
358371 Finalizer* finalizer = static_cast <Finalizer*>(hint);
359372 if (finalizer->_finalize_callback != nullptr ) {
360- NapiCallIntoModuleThrow (finalizer->_env , [&]() {
361- finalizer->_finalize_callback (
362- finalizer->_env ,
363- data,
364- finalizer->_finalize_hint );
373+ finalizer->_env ->CallIntoModuleThrow ([&](napi_env env) {
374+ finalizer->_finalize_callback (env, data, finalizer->_finalize_hint );
365375 });
366376 }
367377
@@ -494,12 +504,10 @@ class Reference : private Finalizer {
494504 static void SecondPassCallback (const v8::WeakCallbackInfo<Reference>& data) {
495505 Reference* reference = data.GetParameter ();
496506
497- napi_env env = reference->_env ;
498-
499507 if (reference->_finalize_callback != nullptr ) {
500- NapiCallIntoModuleThrow (env, [&]() {
508+ reference-> _env -> CallIntoModuleThrow ( [&](napi_env env ) {
501509 reference->_finalize_callback (
502- reference-> _env ,
510+ env ,
503511 reference->_finalize_data ,
504512 reference->_finalize_hint );
505513 });
@@ -617,7 +625,9 @@ class CallbackWrapperBase : public CallbackWrapper {
617625 napi_callback cb = _bundle->*FunctionField;
618626
619627 napi_value result;
620- NapiCallIntoModuleThrow (env, [&]() { result = cb (env, cbinfo_wrapper); });
628+ env->CallIntoModuleThrow ([&](napi_env env) {
629+ result = cb (env, cbinfo_wrapper);
630+ });
621631
622632 if (result != nullptr ) {
623633 this ->SetReturnValue (result);
@@ -781,44 +791,22 @@ v8::Local<v8::Value> CreateAccessorCallbackData(napi_env env,
781791}
782792
783793static
784- napi_env GetEnv (v8::Local<v8::Context> context) {
794+ napi_env NewEnv (v8::Local<v8::Context> context) {
785795 napi_env result;
786796
787- auto isolate = context->GetIsolate ();
788- auto global = context->Global ();
789-
790- // In the case of the string for which we grab the private and the value of
791- // the private on the global object we can call .ToLocalChecked() directly
792- // because we need to stop hard if either of them is empty.
793- //
794- // Re https://github.com/nodejs/node/pull/14217#discussion_r128775149
795- auto value = global->GetPrivate (context, NAPI_PRIVATE_KEY (context, env))
796- .ToLocalChecked ();
797-
798- if (value->IsExternal ()) {
799- result = static_cast <napi_env>(value.As <v8::External>()->Value ());
800- } else {
801- result = new napi_env__ (context);
802- auto external = v8::External::New (isolate, result);
803-
804- // We must also stop hard if the result of assigning the env to the global
805- // is either nothing or false.
806- CHECK (global->SetPrivate (context, NAPI_PRIVATE_KEY (context, env), external)
807- .FromJust ());
808-
809- // TODO(addaleax): There was previously code that tried to delete the
810- // napi_env when its v8::Context was garbage collected;
811- // However, as long as N-API addons using this napi_env are in place,
812- // the Context needs to be accessible and alive.
813- // Ideally, weβd want an on-addon-unload hook that takes care of this
814- // once all N-API addons using this napi_env are unloaded.
815- // For now, a per-Environment cleanup hook is the best we can do.
816- result->node_env ()->AddCleanupHook (
817- [](void * arg) {
818- static_cast <napi_env>(arg)->Unref ();
819- },
820- static_cast <void *>(result));
821- }
797+ result = new napi_env__ (context);
798+ // TODO(addaleax): There was previously code that tried to delete the
799+ // napi_env when its v8::Context was garbage collected;
800+ // However, as long as N-API addons using this napi_env are in place,
801+ // the Context needs to be accessible and alive.
802+ // Ideally, we'd want an on-addon-unload hook that takes care of this
803+ // once all N-API addons using this napi_env are unloaded.
804+ // For now, a per-Environment cleanup hook is the best we can do.
805+ result->node_env ()->AddCleanupHook (
806+ [](void * arg) {
807+ static_cast <napi_env>(arg)->Unref ();
808+ },
809+ static_cast <void *>(result));
822810
823811 return result;
824812}
@@ -1311,10 +1299,10 @@ void napi_module_register_by_symbol(v8::Local<v8::Object> exports,
13111299
13121300 // Create a new napi_env for this module or reference one if a pre-existing
13131301 // one is found.
1314- napi_env env = v8impl::GetEnv (context);
1302+ napi_env env = v8impl::NewEnv (context);
13151303
13161304 napi_value _exports;
1317- NapiCallIntoModuleThrow ( env, [&]() {
1305+ env-> CallIntoModuleThrow ( [&](napi_env env ) {
13181306 _exports = init (env, v8impl::JsValueFromV8LocalValue (exports));
13191307 });
13201308
@@ -3941,15 +3929,9 @@ class Work : public node::AsyncResource, public node::ThreadPoolWork {
39413929
39423930 CallbackScope callback_scope (this );
39433931
3944- // We have to back up the env here because the `NAPI_CALL_INTO_MODULE` macro
3945- // makes use of it after the call into the module completes, but the module
3946- // may have deallocated **this**, and along with it the place where _env is
3947- // stored.
3948- napi_env env = _env;
3949-
3950- NapiCallIntoModule (env, [&]() {
3951- _complete (_env, ConvertUVErrorCode (status), _data);
3952- }, [env](v8::Local<v8::Value> local_err) {
3932+ _env->CallIntoModule ([&](napi_env env) {
3933+ _complete (env, ConvertUVErrorCode (status), _data);
3934+ }, [](napi_env env, v8::Local<v8::Value> local_err) {
39533935 // If there was an unhandled exception in the complete callback,
39543936 // report it as a fatal exception. (There is no JavaScript on the
39553937 // callstack that can possibly handle it.)
@@ -4287,3 +4269,26 @@ napi_status napi_add_finalizer(napi_env env,
42874269 finalize_hint,
42884270 result);
42894271}
4272+
4273+ napi_status napi_set_instance_data (napi_env env,
4274+ void * data,
4275+ napi_finalize finalize_cb,
4276+ void * finalize_hint) {
4277+ CHECK_ENV (env);
4278+
4279+ env->instance_data .data = data;
4280+ env->instance_data .finalize_cb = finalize_cb;
4281+ env->instance_data .hint = finalize_hint;
4282+
4283+ return napi_clear_last_error (env);
4284+ }
4285+
4286+ napi_status napi_get_instance_data (napi_env env,
4287+ void ** data) {
4288+ CHECK_ENV (env);
4289+ CHECK_ARG (env, data);
4290+
4291+ *data = env->instance_data .data ;
4292+
4293+ return napi_clear_last_error (env);
4294+ }
0 commit comments