From 592db48858c541dce7f0e783db8f6d8729622cb5 Mon Sep 17 00:00:00 2001 From: Gabriel Schulhof Date: Thu, 14 Feb 2019 19:14:57 -0800 Subject: [PATCH 1/2] src: expose macros that throw errors Macros `NAPI_THROW`, `NAPI_THROW_IF_FAILED`, and `NAPI_FATAL_IF_FAILED` have so far been used only in the implementation of node-addon-api. Nevertheless, they are useful in cases where direct N-API calls must be interspersed between normal node-addon-api usage. The greatest value they provide is that they convert non-`napi_ok` `napi_status` values into errors that can be thrown, and that they throw the errors respecting whether C++ exceptions are enabled or not. --- doc/error_handling.md | 29 ++++++++++++++++++++ napi-inl.h | 62 ------------------------------------------- napi.h | 58 ++++++++++++++++++++++++++++++++++++++++ 3 files changed, 87 insertions(+), 62 deletions(-) diff --git a/doc/error_handling.md b/doc/error_handling.md index d5df55450..995702318 100644 --- a/doc/error_handling.md +++ b/doc/error_handling.md @@ -153,3 +153,32 @@ if (env.IsExceptionPending()) { Since the exception was cleared here, it will not be propagated as a JavaScript exception after the native callback returns. + +## Calling N-API directly from a **node-addon-api** addon + +**node-addon-api** provides macros for throwing errors in response to non-OK +`napi_status` results when calling [N-API](https://nodejs.org/docs/latest/api/n-api.html) +functions from within a native addon. These macros are defined differently +depending on whether C++ exceptions are enabled or not, but are available for +use in either case. + +### `NAPI_THROW(e, ...)` + +This macro accepts a `Napi::Error`, throws it, and returns the value given as +the last parameter. + +### `NAPI_THROW_IF_FAILED(env, status, ...)` + +This macro accepts a `Napi::Env` and a `napi_status`. It constructs an error +from the `napi_status`, throws it, and returns the value given as the last +parameter. + +### `NAPI_THROW_IF_FAILED_VOID(env, status)` + +This macro accepts a `Napi::Env` and a `napi_status`. It constructs an error +from the `napi_status`, throws it, and returns. + +### `NAPI_FATAL_IF_FAILED(status, location, message)` + +This macro accepts a `napi_status`, a C string indicating the location where the +error occurred, and a second C string for the message to display. diff --git a/napi-inl.h b/napi-inl.h index 4f532c77b..d5fbb1bf7 100644 --- a/napi-inl.h +++ b/napi-inl.h @@ -17,64 +17,6 @@ namespace Napi { // Helpers to handle functions exposed from C++. namespace details { -#ifdef NAPI_CPP_EXCEPTIONS - -// When C++ exceptions are enabled, Errors are thrown directly. There is no need -// to return anything after the throw statements. The variadic parameter is an -// optional return value that is ignored. -// We need _VOID versions of the macros to avoid warnings resulting from -// leaving the NAPI_THROW_* `...` argument empty. - -#define NAPI_THROW(e, ...) throw e -#define NAPI_THROW_VOID(e) throw e - -#define NAPI_THROW_IF_FAILED(env, status, ...) \ - if ((status) != napi_ok) throw Error::New(env); - -#define NAPI_THROW_IF_FAILED_VOID(env, status) \ - if ((status) != napi_ok) throw Error::New(env); - -#else // NAPI_CPP_EXCEPTIONS - -// When C++ exceptions are disabled, Errors are thrown as JavaScript exceptions, -// which are pending until the callback returns to JS. The variadic parameter -// is an optional return value; usually it is an empty result. -// We need _VOID versions of the macros to avoid warnings resulting from -// leaving the NAPI_THROW_* `...` argument empty. - -#define NAPI_THROW(e, ...) \ - do { \ - (e).ThrowAsJavaScriptException(); \ - return __VA_ARGS__; \ - } while (0) - -#define NAPI_THROW_VOID(e) \ - do { \ - (e).ThrowAsJavaScriptException(); \ - return; \ - } while (0) - -#define NAPI_THROW_IF_FAILED(env, status, ...) \ - if ((status) != napi_ok) { \ - Error::New(env).ThrowAsJavaScriptException(); \ - return __VA_ARGS__; \ - } - -#define NAPI_THROW_IF_FAILED_VOID(env, status) \ - if ((status) != napi_ok) { \ - Error::New(env).ThrowAsJavaScriptException(); \ - return; \ - } - -#endif // NAPI_CPP_EXCEPTIONS - -#define NAPI_FATAL_IF_FAILED(status, location, message) \ - do { \ - if ((status) != napi_ok) { \ - Error::Fatal((location), (message)); \ - } \ - } while (0) - // Attach a data item to an object and delete it when the object gets // garbage-collected. // TODO: Replace this code with `napi_add_finalizer()` whenever it becomes @@ -3705,10 +3647,6 @@ inline const napi_node_version* VersionManagement::GetNodeVersion(Env env) { return result; } -// These macros shouldn't be useful in user code. -#undef NAPI_THROW -#undef NAPI_THROW_IF_FAILED - } // namespace Napi #endif // SRC_NAPI_INL_H_ diff --git a/napi.h b/napi.h index bff387adb..13ac4853f 100644 --- a/napi.h +++ b/napi.h @@ -38,6 +38,64 @@ static_assert(sizeof(char16_t) == sizeof(wchar_t), "Size mismatch between char16 #define NAPI_NOEXCEPT noexcept #endif +#ifdef NAPI_CPP_EXCEPTIONS + +// When C++ exceptions are enabled, Errors are thrown directly. There is no need +// to return anything after the throw statements. The variadic parameter is an +// optional return value that is ignored. +// We need _VOID versions of the macros to avoid warnings resulting from +// leaving the NAPI_THROW_* `...` argument empty. + +#define NAPI_THROW(e, ...) throw e +#define NAPI_THROW_VOID(e) throw e + +#define NAPI_THROW_IF_FAILED(env, status, ...) \ + if ((status) != napi_ok) throw Error::New(env); + +#define NAPI_THROW_IF_FAILED_VOID(env, status) \ + if ((status) != napi_ok) throw Error::New(env); + +#else // NAPI_CPP_EXCEPTIONS + +// When C++ exceptions are disabled, Errors are thrown as JavaScript exceptions, +// which are pending until the callback returns to JS. The variadic parameter +// is an optional return value; usually it is an empty result. +// We need _VOID versions of the macros to avoid warnings resulting from +// leaving the NAPI_THROW_* `...` argument empty. + +#define NAPI_THROW(e, ...) \ + do { \ + (e).ThrowAsJavaScriptException(); \ + return __VA_ARGS__; \ + } while (0) + +#define NAPI_THROW_VOID(e) \ + do { \ + (e).ThrowAsJavaScriptException(); \ + return; \ + } while (0) + +#define NAPI_THROW_IF_FAILED(env, status, ...) \ + if ((status) != napi_ok) { \ + Error::New(env).ThrowAsJavaScriptException(); \ + return __VA_ARGS__; \ + } + +#define NAPI_THROW_IF_FAILED_VOID(env, status) \ + if ((status) != napi_ok) { \ + Error::New(env).ThrowAsJavaScriptException(); \ + return; \ + } + +#endif // NAPI_CPP_EXCEPTIONS + +#define NAPI_FATAL_IF_FAILED(status, location, message) \ + do { \ + if ((status) != napi_ok) { \ + Error::Fatal((location), (message)); \ + } \ + } while (0) + //////////////////////////////////////////////////////////////////////////////// /// N-API C++ Wrapper Classes /// From 5b4bbd9c5ee03aaf708bd2a811ee0570e5760ae5 Mon Sep 17 00:00:00 2001 From: Gabriel Schulhof Date: Tue, 19 Feb 2019 19:59:35 -0800 Subject: [PATCH 2/2] squash: mention that return value gets ignored when C++ exceptions are on --- doc/error_handling.md | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/doc/error_handling.md b/doc/error_handling.md index 995702318..9a0ef349e 100644 --- a/doc/error_handling.md +++ b/doc/error_handling.md @@ -165,13 +165,15 @@ use in either case. ### `NAPI_THROW(e, ...)` This macro accepts a `Napi::Error`, throws it, and returns the value given as -the last parameter. +the last parameter. If C++ exceptions are enabled (by defining +`NAPI_CPP_EXCEPTIONS` during the build), the return value will be ignored. ### `NAPI_THROW_IF_FAILED(env, status, ...)` This macro accepts a `Napi::Env` and a `napi_status`. It constructs an error from the `napi_status`, throws it, and returns the value given as the last -parameter. +parameter. If C++ exceptions are enabled (by defining `NAPI_CPP_EXCEPTIONS` +during the build), the return value will be ignored. ### `NAPI_THROW_IF_FAILED_VOID(env, status)`