99#include " node_buffer.h"
1010#include " node_errors.h"
1111#include " node_internals.h"
12+ #include " node_process.h"
1213#include " node_url.h"
1314#include " threadpoolwork-inl.h"
1415#include " tracing/traced_value.h"
@@ -23,6 +24,11 @@ node_napi_env__::node_napi_env__(v8::Local<v8::Context> context,
2324 CHECK_NOT_NULL (node_env ());
2425}
2526
27+ node_napi_env__::~node_napi_env__ () {
28+ destructing = true ;
29+ FinalizeAll ();
30+ }
31+
2632bool node_napi_env__::can_call_into_js () const {
2733 return node_env ()->can_call_into_js ();
2834}
@@ -35,19 +41,64 @@ v8::Maybe<bool> node_napi_env__::mark_arraybuffer_as_untransferable(
3541}
3642
3743void node_napi_env__::CallFinalizer (napi_finalize cb, void * data, void * hint) {
44+ CallFinalizer<true >(cb, data, hint);
45+ }
46+
47+ template <bool enforceUncaughtExceptionPolicy>
48+ void node_napi_env__::CallFinalizer (napi_finalize cb, void * data, void * hint) {
49+ if (destructing) {
50+ // we can not defer finalizers when the environment is being destructed.
51+ v8::HandleScope handle_scope (isolate);
52+ v8::Context::Scope context_scope (context ());
53+ CallbackIntoModule<enforceUncaughtExceptionPolicy>(
54+ [&](napi_env env) { cb (env, data, hint); });
55+ return ;
56+ }
3857 // we need to keep the env live until the finalizer has been run
3958 // EnvRefHolder provides an exception safe wrapper to Ref and then
4059 // Unref once the lambda is freed
4160 EnvRefHolder liveEnv (static_cast <napi_env>(this ));
4261 node_env ()->SetImmediate (
4362 [=, liveEnv = std::move (liveEnv)](node::Environment* node_env) {
44- napi_env env = liveEnv.env ();
63+ node_napi_env__* env = static_cast <node_napi_env__*>( liveEnv.env () );
4564 v8::HandleScope handle_scope (env->isolate );
4665 v8::Context::Scope context_scope (env->context ());
47- env->CallIntoModule ([&](napi_env env) { cb (env, data, hint); });
66+ env->CallbackIntoModule <enforceUncaughtExceptionPolicy>(
67+ [&](napi_env env) { cb (env, data, hint); });
4868 });
4969}
5070
71+ void node_napi_env__::trigger_fatal_exception (v8::Local<v8::Value> local_err) {
72+ v8::Local<v8::Message> local_msg =
73+ v8::Exception::CreateMessage (isolate, local_err);
74+ node::errors::TriggerUncaughtException (isolate, local_err, local_msg);
75+ }
76+
77+ // option enforceUncaughtExceptionPolicy is added for not breaking existing
78+ // running n-api add-ons, and should be deprecated in the next major Node.js
79+ // release.
80+ template <bool enforceUncaughtExceptionPolicy, typename T>
81+ void node_napi_env__::CallbackIntoModule (T&& call) {
82+ CallIntoModule (call, [](napi_env env_, v8::Local<v8::Value> local_err) {
83+ node_napi_env__* env = static_cast <node_napi_env__*>(env_);
84+ node::Environment* node_env = env->node_env ();
85+ if (!node_env->options ()->force_node_api_uncaught_exceptions_policy &&
86+ !enforceUncaughtExceptionPolicy) {
87+ ProcessEmitDeprecationWarning (
88+ node_env,
89+ " Uncaught N-API callback exception detected, please run node "
90+ " with option --force-node-api-uncaught-exceptions-policy=true"
91+ " to handle those exceptions properly." ,
92+ " DEP0XXX" );
93+ return ;
94+ }
95+ // If there was an unhandled exception in the complete callback,
96+ // report it as a fatal exception. (There is no JavaScript on the
97+ // callstack that can possibly handle it.)
98+ env->trigger_fatal_exception (local_err);
99+ });
100+ }
101+
51102namespace v8impl {
52103
53104namespace {
@@ -60,20 +111,10 @@ class BufferFinalizer : private Finalizer {
60111 static_cast <BufferFinalizer*>(hint)};
61112 finalizer->_finalize_data = data;
62113
63- node::Environment* node_env =
64- static_cast <node_napi_env>(finalizer->_env )->node_env ();
65- node_env->SetImmediate (
66- [finalizer = std::move (finalizer)](node::Environment* env) {
67- if (finalizer->_finalize_callback == nullptr ) return ;
68-
69- v8::HandleScope handle_scope (finalizer->_env ->isolate );
70- v8::Context::Scope context_scope (finalizer->_env ->context ());
71-
72- finalizer->_env ->CallIntoModule ([&](napi_env env) {
73- finalizer->_finalize_callback (
74- env, finalizer->_finalize_data , finalizer->_finalize_hint );
75- });
76- });
114+ if (finalizer->_finalize_callback == nullptr ) return ;
115+ finalizer->_env ->CallFinalizer (finalizer->_finalize_callback ,
116+ finalizer->_finalize_data ,
117+ finalizer->_finalize_hint );
77118 }
78119
79120 struct Deleter {
@@ -102,13 +143,6 @@ static inline napi_env NewEnv(v8::Local<v8::Context> context,
102143 return result;
103144}
104145
105- static inline void trigger_fatal_exception (napi_env env,
106- v8::Local<v8::Value> local_err) {
107- v8::Local<v8::Message> local_msg =
108- v8::Exception::CreateMessage (env->isolate , local_err);
109- node::errors::TriggerUncaughtException (env->isolate , local_err, local_msg);
110- }
111-
112146class ThreadSafeFunction : public node ::AsyncResource {
113147 public:
114148 ThreadSafeFunction (v8::Local<v8::Function> func,
@@ -325,7 +359,7 @@ class ThreadSafeFunction : public node::AsyncResource {
325359 v8::Local<v8::Function>::New (env->isolate , ref);
326360 js_callback = v8impl::JsValueFromV8LocalValue (js_cb);
327361 }
328- env->CallIntoModule (
362+ env->CallbackIntoModule < false > (
329363 [&](napi_env env) { call_js_cb (env, js_callback, context, data); });
330364 }
331365
@@ -336,7 +370,9 @@ class ThreadSafeFunction : public node::AsyncResource {
336370 v8::HandleScope scope (env->isolate );
337371 if (finalize_cb) {
338372 CallbackScope cb_scope (this );
339- env->CallIntoModule (
373+ // Do not use CallFinalizer since it will defer the invocation, which
374+ // would lead to accessing a deleted ThreadSafeFunction.
375+ env->CallbackIntoModule <false >(
340376 [&](napi_env env) { finalize_cb (env, finalize_data, context); });
341377 }
342378 EmptyQueueAndDelete ();
@@ -719,7 +755,7 @@ napi_status NAPI_CDECL napi_fatal_exception(napi_env env, napi_value err) {
719755 CHECK_ARG (env, err);
720756
721757 v8::Local<v8::Value> local_err = v8impl::V8LocalValueFromJsValue (err);
722- v8impl::trigger_fatal_exception (env, local_err);
758+ static_cast <node_napi_env> (env)-> trigger_fatal_exception ( local_err);
723759
724760 return napi_clear_last_error (env);
725761}
@@ -1064,16 +1100,9 @@ class Work : public node::AsyncResource, public node::ThreadPoolWork {
10641100
10651101 CallbackScope callback_scope (this );
10661102
1067- _env->CallIntoModule (
1068- [&](napi_env env) {
1069- _complete (env, ConvertUVErrorCode (status), _data);
1070- },
1071- [](napi_env env, v8::Local<v8::Value> local_err) {
1072- // If there was an unhandled exception in the complete callback,
1073- // report it as a fatal exception. (There is no JavaScript on the
1074- // callstack that can possibly handle it.)
1075- v8impl::trigger_fatal_exception (env, local_err);
1076- });
1103+ _env->CallbackIntoModule <true >([&](napi_env env) {
1104+ _complete (env, ConvertUVErrorCode (status), _data);
1105+ });
10771106
10781107 // Note: Don't access `work` after this point because it was
10791108 // likely deleted by the complete callback.
0 commit comments