summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMathias Buus <mathiasbuus@gmail.com>2018-03-14 03:14:08 +0100
committerMichael Dawson <mdawson@devrus.com>2018-03-20 15:48:14 -0400
commitc1695d8bad09fc61922ec91101736debb2d165db (patch)
treec3214883da34cd76fc358535df0365df5f37828a
parent1203b1372592a5067888987080f30b6b9047d150 (diff)
downloadandroid-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.md14
-rw-r--r--src/node_api.cc25
-rw-r--r--src/node_api.h2
-rw-r--r--src/node_internals.h5
-rw-r--r--test/addons-napi/test_async/test-uncaught.js18
-rw-r--r--test/addons-napi/test_fatal_exception/binding.gyp8
-rw-r--r--test/addons-napi/test_fatal_exception/test.js11
-rw-r--r--test/addons-napi/test_fatal_exception/test_fatal_exception.c26
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)