From 9f16c1667ad3f6328c24b79b01a479bf9a609292 Mon Sep 17 00:00:00 2001 From: Kyle Farnung Date: Tue, 27 Jun 2017 17:54:44 -0700 Subject: [PATCH] n-api: add napi_fatal_error API --- doc/api/n-api.md | 17 +++++++++++++++++ src/node_api.cc | 5 +++++ src/node_api.h | 9 +++++++++ test/addons-napi/test_fatal/binding.gyp | 8 ++++++++ test/addons-napi/test_fatal/test.js | 18 ++++++++++++++++++ test/addons-napi/test_fatal/test_fatal.c | 18 ++++++++++++++++++ 6 files changed, 75 insertions(+) create mode 100644 test/addons-napi/test_fatal/binding.gyp create mode 100644 test/addons-napi/test_fatal/test.js create mode 100644 test/addons-napi/test_fatal/test_fatal.c diff --git a/doc/api/n-api.md b/doc/api/n-api.md index aab13420aab35f..23d4b953cf197a 100644 --- a/doc/api/n-api.md +++ b/doc/api/n-api.md @@ -490,6 +490,23 @@ Returns `napi_ok` if the API succeeded. This API returns true if an exception is pending. +### Fatal Errors + +In the event of an unrecoverable error in a native module, a fatal error can be +thrown to immediately terminate the process. + +#### napi_fatal_error + +```C +NAPI_EXTERN NAPI_NO_RETURN void napi_fatal_error(const char* location, const char* message); +``` + +- `[in] location`: Optional location at which the error occurred. +- `[in] message`: The message associated with the error. + +The function call does not return, the process will be terminated. ## Object Lifetime management diff --git a/src/node_api.cc b/src/node_api.cc index becafc4684925c..f5f8690090455a 100644 --- a/src/node_api.cc +++ b/src/node_api.cc @@ -822,6 +822,11 @@ napi_status napi_get_last_error_info(napi_env env, return napi_ok; } +NAPI_NO_RETURN void napi_fatal_error(const char* location, + const char* message) { + node::FatalError(location, message); +} + napi_status napi_create_function(napi_env env, const char* utf8name, napi_callback cb, diff --git a/src/node_api.h b/src/node_api.h index e346b762dd2702..d60ddf7466ef1f 100644 --- a/src/node_api.h +++ b/src/node_api.h @@ -37,6 +37,12 @@ # define NAPI_MODULE_EXPORT __attribute__((visibility("default"))) #endif +#ifdef __GNUC__ +#define NAPI_NO_RETURN __attribute__((noreturn)) +#else +#define NAPI_NO_RETURN +#endif + typedef void (*napi_addon_register_func)(napi_env env, napi_value exports, @@ -104,6 +110,9 @@ NAPI_EXTERN napi_status napi_get_last_error_info(napi_env env, const napi_extended_error_info** result); +NAPI_EXTERN NAPI_NO_RETURN void napi_fatal_error(const char* location, + const char* message); + // Getters for defined singletons NAPI_EXTERN napi_status napi_get_undefined(napi_env env, napi_value* result); NAPI_EXTERN napi_status napi_get_null(napi_env env, napi_value* result); diff --git a/test/addons-napi/test_fatal/binding.gyp b/test/addons-napi/test_fatal/binding.gyp new file mode 100644 index 00000000000000..ad661825f1fa9b --- /dev/null +++ b/test/addons-napi/test_fatal/binding.gyp @@ -0,0 +1,8 @@ +{ + "targets": [ + { + "target_name": "test_fatal", + "sources": [ "test_fatal.c" ] + } + ] +} diff --git a/test/addons-napi/test_fatal/test.js b/test/addons-napi/test_fatal/test.js new file mode 100644 index 00000000000000..aa37dc6803f0d5 --- /dev/null +++ b/test/addons-napi/test_fatal/test.js @@ -0,0 +1,18 @@ +'use strict'; +const common = require('../../common'); +const assert = require('assert'); +const child_process = require('child_process'); +const test_fatal = require(`./build/${common.buildType}/test_fatal`); + +// Test in a child process because the test code will trigger a fatal error +// that crashes the process. +if (process.argv[2] === 'child') { + test_fatal.Test(); + return; +} + +const p = child_process.spawnSync( + process.execPath, [ '--napi-modules', __filename, 'child' ]); +assert.ifError(p.error); +assert.ok(p.stderr.toString().includes( + 'FATAL ERROR: test_fatal::Test fatal message')); diff --git a/test/addons-napi/test_fatal/test_fatal.c b/test/addons-napi/test_fatal/test_fatal.c new file mode 100644 index 00000000000000..1e8a1cf7955d18 --- /dev/null +++ b/test/addons-napi/test_fatal/test_fatal.c @@ -0,0 +1,18 @@ +#include +#include "../common.h" + +napi_value Test(napi_env env, napi_callback_info info) { + napi_fatal_error("test_fatal::Test", "fatal message"); + return NULL; +} + +void Init(napi_env env, napi_value exports, napi_value module, void* priv) { + napi_property_descriptor properties[] = { + DECLARE_NAPI_PROPERTY("Test", Test), + }; + + NAPI_CALL_RETURN_VOID(env, napi_define_properties( + env, exports, sizeof(properties) / sizeof(*properties), properties)); +} + +NAPI_MODULE(addon, Init)