diff --git a/src/js_native_api_v8.h b/src/js_native_api_v8.h index 1a62782c1ad24f..f619248a84326a 100644 --- a/src/js_native_api_v8.h +++ b/src/js_native_api_v8.h @@ -122,6 +122,37 @@ struct napi_env__ { void* instance_data = nullptr; }; +// This class is used to keep a napi_env live in a way that +// is exception safe versus calling Ref/Unref directly +class EnvRefHolder { + public: + explicit EnvRefHolder(napi_env env) : _env(env) { + _env->Ref(); + } + + explicit EnvRefHolder(const EnvRefHolder& other): _env(other.env()) { + _env->Ref(); + } + + EnvRefHolder(EnvRefHolder&& other) { + _env = other._env; + other._env = nullptr; + } + + ~EnvRefHolder() { + if (_env != nullptr) { + _env->Unref(); + } + } + + napi_env env(void) const { + return _env; + } + + private: + napi_env _env; +}; + static inline napi_status napi_clear_last_error(napi_env env) { env->last_error.error_code = napi_ok; diff --git a/src/node_api.cc b/src/node_api.cc index f1a5265b6a7234..2a43a406f07f9d 100644 --- a/src/node_api.cc +++ b/src/node_api.cc @@ -37,8 +37,13 @@ struct node_napi_env__ : public napi_env__ { } void CallFinalizer(napi_finalize cb, void* data, void* hint) override { - napi_env env = static_cast(this); - node_env()->SetImmediate([=](node::Environment* node_env) { + // we need to keep the env live until the finalizer has been run + // EnvRefHolder provides an exception safe wrapper to Ref and then + // Unref once the lamba is freed + EnvRefHolder liveEnv(static_cast(this)); + node_env()->SetImmediate([=, liveEnv = std::move(liveEnv)] + (node::Environment* node_env) { + napi_env env = liveEnv.env(); v8::HandleScope handle_scope(env->isolate); v8::Context::Scope context_scope(env->context()); env->CallIntoModule([&](napi_env env) {