9
9
#include " node_buffer.h"
10
10
#include " node_errors.h"
11
11
#include " node_internals.h"
12
+ #include " node_process.h"
12
13
#include " node_url.h"
13
14
#include " threadpoolwork-inl.h"
14
15
#include " tracing/traced_value.h"
@@ -23,6 +24,11 @@ node_napi_env__::node_napi_env__(v8::Local<v8::Context> context,
23
24
CHECK_NOT_NULL (node_env ());
24
25
}
25
26
27
+ node_napi_env__::~node_napi_env__ () {
28
+ destructing = true ;
29
+ FinalizeAll ();
30
+ }
31
+
26
32
bool node_napi_env__::can_call_into_js () const {
27
33
return node_env ()->can_call_into_js ();
28
34
}
@@ -35,19 +41,64 @@ v8::Maybe<bool> node_napi_env__::mark_arraybuffer_as_untransferable(
35
41
}
36
42
37
43
void 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
+ }
38
57
// we need to keep the env live until the finalizer has been run
39
58
// EnvRefHolder provides an exception safe wrapper to Ref and then
40
59
// Unref once the lambda is freed
41
60
EnvRefHolder liveEnv (static_cast <napi_env>(this ));
42
61
node_env ()->SetImmediate (
43
62
[=, 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 () );
45
64
v8::HandleScope handle_scope (env->isolate );
46
65
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); });
48
68
});
49
69
}
50
70
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
+
51
102
namespace v8impl {
52
103
53
104
namespace {
@@ -60,20 +111,10 @@ class BufferFinalizer : private Finalizer {
60
111
static_cast <BufferFinalizer*>(hint)};
61
112
finalizer->_finalize_data = data;
62
113
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 );
77
118
}
78
119
79
120
struct Deleter {
@@ -102,13 +143,6 @@ static inline napi_env NewEnv(v8::Local<v8::Context> context,
102
143
return result;
103
144
}
104
145
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
-
112
146
class ThreadSafeFunction : public node ::AsyncResource {
113
147
public:
114
148
ThreadSafeFunction (v8::Local<v8::Function> func,
@@ -325,7 +359,7 @@ class ThreadSafeFunction : public node::AsyncResource {
325
359
v8::Local<v8::Function>::New (env->isolate , ref);
326
360
js_callback = v8impl::JsValueFromV8LocalValue (js_cb);
327
361
}
328
- env->CallIntoModule (
362
+ env->CallbackIntoModule < false > (
329
363
[&](napi_env env) { call_js_cb (env, js_callback, context, data); });
330
364
}
331
365
@@ -336,7 +370,9 @@ class ThreadSafeFunction : public node::AsyncResource {
336
370
v8::HandleScope scope (env->isolate );
337
371
if (finalize_cb) {
338
372
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 >(
340
376
[&](napi_env env) { finalize_cb (env, finalize_data, context); });
341
377
}
342
378
EmptyQueueAndDelete ();
@@ -719,7 +755,7 @@ napi_status NAPI_CDECL napi_fatal_exception(napi_env env, napi_value err) {
719
755
CHECK_ARG (env, err);
720
756
721
757
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);
723
759
724
760
return napi_clear_last_error (env);
725
761
}
@@ -1064,16 +1100,9 @@ class Work : public node::AsyncResource, public node::ThreadPoolWork {
1064
1100
1065
1101
CallbackScope callback_scope (this );
1066
1102
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
+ });
1077
1106
1078
1107
// Note: Don't access `work` after this point because it was
1079
1108
// likely deleted by the complete callback.
0 commit comments