diff options
author | Mathias Buus <mathiasbuus@gmail.com> | 2018-03-14 03:14:08 +0100 |
---|---|---|
committer | Michael Dawson <mdawson@devrus.com> | 2018-03-20 15:48:14 -0400 |
commit | c1695d8bad09fc61922ec91101736debb2d165db (patch) | |
tree | c3214883da34cd76fc358535df0365df5f37828a | |
parent | 1203b1372592a5067888987080f30b6b9047d150 (diff) | |
download | android-node-v8-c1695d8bad09fc61922ec91101736debb2d165db.tar.gz android-node-v8-c1695d8bad09fc61922ec91101736debb2d165db.tar.bz2 android-node-v8-c1695d8bad09fc61922ec91101736debb2d165db.zip |
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.
PR-URL: https://github.com/nodejs/node/pull/19337
Reviewed-By: James M Snell <jasnell@gmail.com>
Reviewed-By: Colin Ihrig <cjihrig@gmail.com>
Reviewed-By: Anna Henningsen <anna@addaleax.net>
Reviewed-By: Michael Dawson <michael_dawson@ca.ibm.com>
-rw-r--r-- | doc/api/n-api.md | 14 | ||||
-rw-r--r-- | src/node_api.cc | 25 | ||||
-rw-r--r-- | src/node_api.h | 2 | ||||
-rw-r--r-- | src/node_internals.h | 5 | ||||
-rw-r--r-- | test/addons-napi/test_async/test-uncaught.js | 18 | ||||
-rw-r--r-- | test/addons-napi/test_fatal_exception/binding.gyp | 8 | ||||
-rw-r--r-- | test/addons-napi/test_fatal_exception/test.js | 11 | ||||
-rw-r--r-- | test/addons-napi/test_fatal_exception/test_fatal_exception.c | 26 |
8 files changed, 105 insertions, 4 deletions
diff --git a/doc/api/n-api.md b/doc/api/n-api.md index 7178d3a4a8..a625741603 100644 --- a/doc/api/n-api.md +++ b/doc/api/n-api.md @@ -541,6 +541,20 @@ This API returns true if an exception is pending. This API can be called even if there is a pending JavaScript exception. +#### napi_fatal_exception +<!-- YAML +added: REPLACEME +--> +```C +napi_status napi_fatal_exception(napi_env env, napi_value err); +``` + +- `[in] env`: The environment that the API is invoked under. +- `[in] err`: The error you want to pass to `uncaughtException`. + +Trigger an `uncaughtException` in JavaScript. Useful if an async +callback throws an exception with no way to recover. + ### Fatal Errors In the event of an unrecoverable error in a native module, a fatal error can be diff --git a/src/node_api.cc b/src/node_api.cc index 2ee241badf..16e0a3e18b 100644 --- a/src/node_api.cc +++ b/src/node_api.cc @@ -157,6 +157,7 @@ struct napi_env__ { (out) = v8::type::New((buffer), (byte_offset), (length)); \ } while (0) + namespace { namespace v8impl { @@ -279,6 +280,13 @@ v8::Local<v8::Value> V8LocalValueFromJsValue(napi_value v) { return local; } +static inline void trigger_fatal_exception( + napi_env env, v8::Local<v8::Value> local_err) { + v8::Local<v8::Message> local_msg = + v8::Exception::CreateMessage(env->isolate, local_err); + node::FatalException(env->isolate, local_err, local_msg); +} + static inline napi_status V8NameFromPropertyDescriptor(napi_env env, const napi_property_descriptor* p, v8::Local<v8::Name>* result) { @@ -944,6 +952,16 @@ napi_status napi_get_last_error_info(napi_env env, return napi_ok; } +napi_status napi_fatal_exception(napi_env env, napi_value err) { + NAPI_PREAMBLE(env); + CHECK_ARG(env, err); + + v8::Local<v8::Value> local_err = v8impl::V8LocalValueFromJsValue(err); + v8impl::trigger_fatal_exception(env, local_err); + + return napi_clear_last_error(env); +} + NAPI_NO_RETURN void napi_fatal_error(const char* location, size_t location_len, const char* message, @@ -3348,10 +3366,9 @@ class Work : public node::AsyncResource { // report it as a fatal exception. (There is no JavaScript on the // callstack that can possibly handle it.) if (!env->last_exception.IsEmpty()) { - v8::TryCatch try_catch(env->isolate); - env->isolate->ThrowException( - v8::Local<v8::Value>::New(env->isolate, env->last_exception)); - node::FatalException(env->isolate, try_catch); + v8::Local<v8::Value> local_err = v8::Local<v8::Value>::New( + env->isolate, env->last_exception); + v8impl::trigger_fatal_exception(env, local_err); } } } diff --git a/src/node_api.h b/src/node_api.h index 791a8f8725..aaf002b758 100644 --- a/src/node_api.h +++ b/src/node_api.h @@ -103,6 +103,8 @@ NAPI_EXTERN napi_status napi_get_last_error_info(napi_env env, const napi_extended_error_info** result); +NAPI_EXTERN napi_status napi_fatal_exception(napi_env env, napi_value err); + NAPI_EXTERN NAPI_NO_RETURN void napi_fatal_error(const char* location, size_t location_len, const char* message, diff --git a/src/node_internals.h b/src/node_internals.h index 909d5e1790..6439ccc7a2 100644 --- a/src/node_internals.h +++ b/src/node_internals.h @@ -251,6 +251,11 @@ void GetSockOrPeerName(const v8::FunctionCallbackInfo<v8::Value>& args) { args.GetReturnValue().Set(err); } +void FatalException(v8::Isolate* isolate, + v8::Local<v8::Value> error, + v8::Local<v8::Message> message); + + void SignalExit(int signo); #ifdef __POSIX__ void RegisterSignalHandler(int signal, diff --git a/test/addons-napi/test_async/test-uncaught.js b/test/addons-napi/test_async/test-uncaught.js new file mode 100644 index 0000000000..fdcb3203f5 --- /dev/null +++ b/test/addons-napi/test_async/test-uncaught.js @@ -0,0 +1,18 @@ +'use strict'; +const common = require('../../common'); +const assert = require('assert'); +const test_async = require(`./build/${common.buildType}/test_async`); + +process.on('uncaughtException', common.mustCall(function(err) { + try { + throw new Error('should not fail'); + } catch (err) { + assert.strictEqual(err.message, 'should not fail'); + } + assert.strictEqual(err.message, 'uncaught'); +})); + +// Successful async execution and completion callback. +test_async.Test(5, {}, common.mustCall(function() { + throw new Error('uncaught'); +})); diff --git a/test/addons-napi/test_fatal_exception/binding.gyp b/test/addons-napi/test_fatal_exception/binding.gyp new file mode 100644 index 0000000000..f4dc0a71ea --- /dev/null +++ b/test/addons-napi/test_fatal_exception/binding.gyp @@ -0,0 +1,8 @@ +{ + "targets": [ + { + "target_name": "test_fatal_exception", + "sources": [ "test_fatal_exception.c" ] + } + ] +} diff --git a/test/addons-napi/test_fatal_exception/test.js b/test/addons-napi/test_fatal_exception/test.js new file mode 100644 index 0000000000..f02b9bce1e --- /dev/null +++ b/test/addons-napi/test_fatal_exception/test.js @@ -0,0 +1,11 @@ +'use strict'; +const common = require('../../common'); +const assert = require('assert'); +const test_fatal = require(`./build/${common.buildType}/test_fatal_exception`); + +process.on('uncaughtException', common.mustCall(function(err) { + assert.strictEqual(err.message, 'fatal error'); +})); + +const err = new Error('fatal error'); +test_fatal.Test(err); diff --git a/test/addons-napi/test_fatal_exception/test_fatal_exception.c b/test/addons-napi/test_fatal_exception/test_fatal_exception.c new file mode 100644 index 0000000000..fd81c56d85 --- /dev/null +++ b/test/addons-napi/test_fatal_exception/test_fatal_exception.c @@ -0,0 +1,26 @@ +#include <node_api.h> +#include "../common.h" + +napi_value Test(napi_env env, napi_callback_info info) { + napi_value err; + size_t argc = 1; + + NAPI_CALL(env, napi_get_cb_info(env, info, &argc, &err, NULL, NULL)); + + NAPI_CALL(env, napi_fatal_exception(env, err)); + + return NULL; +} + +napi_value Init(napi_env env, napi_value exports) { + napi_property_descriptor properties[] = { + DECLARE_NAPI_PROPERTY("Test", Test), + }; + + NAPI_CALL(env, napi_define_properties( + env, exports, sizeof(properties) / sizeof(*properties), properties)); + + return exports; +} + +NAPI_MODULE(NODE_GYP_MODULE_NAME, Init) |