Skip to content

Commit 3a6b7e6

Browse files
mafintoshMylesBorins
authored andcommitted
n-api: add napi_fatal_exception
Add function to trigger and uncaught exception. Useful if an async callback throws an exception with no way to recover. Backport-PR-URL: #19265 PR-URL: #19337 Reviewed-By: James M Snell <[email protected]> Reviewed-By: Colin Ihrig <[email protected]> Reviewed-By: Anna Henningsen <[email protected]> Reviewed-By: Michael Dawson <[email protected]>
1 parent 9949d55 commit 3a6b7e6

File tree

8 files changed

+105
-4
lines changed

8 files changed

+105
-4
lines changed

‎doc/api/n-api.md‎

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -541,6 +541,20 @@ This API returns true if an exception is pending.
541541

542542
This API can be called even if there is a pending JavaScript exception.
543543

544+
#### napi_fatal_exception
545+
<!-- YAML
546+
added: REPLACEME
547+
-->
548+
```C
549+
napi_status napi_fatal_exception(napi_env env, napi_value err);
550+
```
551+
552+
- `[in] env`: The environment that the API is invoked under.
553+
- `[in] err`: The error you want to pass to `uncaughtException`.
554+
555+
Trigger an `uncaughtException` in JavaScript. Useful if an async
556+
callback throws an exception with no way to recover.
557+
544558
### Fatal Errors
545559

546560
In the event of an unrecoverable error in a native module, a fatal error can be

‎src/node_api.cc‎

Lines changed: 21 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -173,6 +173,7 @@ struct napi_env__{
173173
(out) = v8::type::New((buffer), (byte_offset), (length)); \
174174
} while (0)
175175

176+
176177
namespace{
177178
namespacev8impl{
178179

@@ -295,6 +296,13 @@ v8::Local<v8::Value> V8LocalValueFromJsValue(napi_value v){
295296
return local;
296297
}
297298

299+
staticinlinevoidtrigger_fatal_exception(
300+
napi_env env, v8::Local<v8::Value> local_err){
301+
v8::Local<v8::Message> local_msg =
302+
v8::Exception::CreateMessage(env->isolate, local_err);
303+
node::FatalException(env->isolate, local_err, local_msg);
304+
}
305+
298306
staticinline napi_status V8NameFromPropertyDescriptor(napi_env env,
299307
const napi_property_descriptor* p,
300308
v8::Local<v8::Name>* result){
@@ -971,6 +979,16 @@ napi_status napi_get_last_error_info(napi_env env,
971979
return napi_ok;
972980
}
973981

982+
napi_status napi_fatal_exception(napi_env env, napi_value err){
983+
NAPI_PREAMBLE(env);
984+
CHECK_ARG(env, err);
985+
986+
v8::Local<v8::Value> local_err = v8impl::V8LocalValueFromJsValue(err);
987+
v8impl::trigger_fatal_exception(env, local_err);
988+
989+
returnnapi_clear_last_error(env);
990+
}
991+
974992
NAPI_NO_RETURN voidnapi_fatal_error(constchar* location,
975993
size_t location_len,
976994
constchar* message,
@@ -3375,10 +3393,9 @@ class Work : public node::AsyncResource{
33753393
// report it as a fatal exception. (There is no JavaScript on the
33763394
// callstack that can possibly handle it.)
33773395
if (!env->last_exception.IsEmpty()){
3378-
v8::TryCatch try_catch(env->isolate);
3379-
env->isolate->ThrowException(
3380-
v8::Local<v8::Value>::New(env->isolate, env->last_exception));
3381-
node::FatalException(env->isolate, try_catch);
3396+
v8::Local<v8::Value> local_err = v8::Local<v8::Value>::New(
3397+
env->isolate, env->last_exception);
3398+
v8impl::trigger_fatal_exception(env, local_err);
33823399
}
33833400
}
33843401
}

‎src/node_api.h‎

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -112,6 +112,8 @@ NAPI_EXTERN napi_status
112112
napi_get_last_error_info(napi_envenv,
113113
constnapi_extended_error_info**result);
114114

115+
NAPI_EXTERNnapi_statusnapi_fatal_exception(napi_envenv, napi_valueerr);
116+
115117
NAPI_EXTERNNAPI_NO_RETURNvoidnapi_fatal_error(constchar*location,
116118
size_tlocation_len,
117119
constchar*message,

‎src/node_internals.h‎

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -218,6 +218,11 @@ void GetSockOrPeerName(const v8::FunctionCallbackInfo<v8::Value>& args){
218218
args.GetReturnValue().Set(err);
219219
}
220220

221+
voidFatalException(v8::Isolate* isolate,
222+
v8::Local<v8::Value> error,
223+
v8::Local<v8::Message> message);
224+
225+
221226
voidSignalExit(int signo);
222227
#ifdef __POSIX__
223228
voidRegisterSignalHandler(int signal,
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
'use strict';
2+
constcommon=require('../../common');
3+
constassert=require('assert');
4+
consttest_async=require(`./build/${common.buildType}/test_async`);
5+
6+
process.on('uncaughtException',common.mustCall(function(err){
7+
try{
8+
thrownewError('should not fail');
9+
}catch(err){
10+
assert.strictEqual(err.message,'should not fail');
11+
}
12+
assert.strictEqual(err.message,'uncaught');
13+
}));
14+
15+
// Successful async execution and completion callback.
16+
test_async.Test(5,{},common.mustCall(function(){
17+
thrownewError('uncaught');
18+
}));
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
{
2+
"targets": [
3+
{
4+
"target_name": "test_fatal_exception",
5+
"sources": [ "test_fatal_exception.c" ]
6+
}
7+
]
8+
}
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
'use strict';
2+
constcommon=require('../../common');
3+
constassert=require('assert');
4+
consttest_fatal=require(`./build/${common.buildType}/test_fatal_exception`);
5+
6+
process.on('uncaughtException',common.mustCall(function(err){
7+
assert.strictEqual(err.message,'fatal error');
8+
}));
9+
10+
consterr=newError('fatal error');
11+
test_fatal.Test(err);
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
#include<node_api.h>
2+
#include"../common.h"
3+
4+
napi_valueTest(napi_envenv, napi_callback_infoinfo){
5+
napi_valueerr;
6+
size_targc=1;
7+
8+
NAPI_CALL(env, napi_get_cb_info(env, info, &argc, &err, NULL, NULL));
9+
10+
NAPI_CALL(env, napi_fatal_exception(env, err));
11+
12+
returnNULL;
13+
}
14+
15+
napi_valueInit(napi_envenv, napi_valueexports){
16+
napi_property_descriptorproperties[] ={
17+
DECLARE_NAPI_PROPERTY("Test", Test),
18+
};
19+
20+
NAPI_CALL(env, napi_define_properties(
21+
env, exports, sizeof(properties) / sizeof(*properties), properties));
22+
23+
returnexports;
24+
}
25+
26+
NAPI_MODULE(NODE_GYP_MODULE_NAME, Init)

0 commit comments

Comments
(0)