From 825440adcba1e47e79e2e37dc9e17199603ce899 Mon Sep 17 00:00:00 2001 From: Nicola Del Gobbo Date: Thu, 4 Jan 2018 02:07:14 +0100 Subject: [PATCH 1/8] Update package.json example Changed version of node-addon-api on the package.json example see: [issue 206](https://github.com/nodejs/node-addon-api/issues/206#issuecomment-355165254) --- doc/setup.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/doc/setup.md b/doc/setup.md index 84dafd3f7..be95e747e 100644 --- a/doc/setup.md +++ b/doc/setup.md @@ -19,7 +19,7 @@ To use **N-API** in a native module: ```json "dependencies": { - "node-addon-api": "1.0.0", + "node-addon-api": "1.1.0", } ``` @@ -65,4 +65,4 @@ To use **N-API** in a native module: ``` At build time, the N-API back-compat library code will be used only when the -targeted node version *does not* have N-API built-in. \ No newline at end of file +targeted node version *does not* have N-API built-in. From d11cd21bfed1d83a667ff2fb3094ffbdd8e7cb22 Mon Sep 17 00:00:00 2001 From: NickNaso Date: Thu, 8 Mar 2018 20:16:01 +0100 Subject: [PATCH 2/8] First step of error documentation --- doc/error_handling.md | 110 ++++++++++++++++++++++++++++++++++++++++-- doc/setup.md | 2 +- 2 files changed, 108 insertions(+), 4 deletions(-) diff --git a/doc/error_handling.md b/doc/error_handling.md index 14c0c4ac0..5517483eb 100644 --- a/doc/error_handling.md +++ b/doc/error_handling.md @@ -1,5 +1,109 @@ # Error handling -You are reading a draft of the next documentation and it's in continuos update so -if you don't find what you need please refer to: -[C++ wrapper classes for the ABI-stable C APIs for Node.js](https://nodejs.github.io/node-addon-api/) \ No newline at end of file +A persistent reference to a JavaScript error object. Use of this class depends +somewhat on whether C++ exceptions are enabled at compile time. + +### Handling Errors With C++ Exceptions + +If C++ exceptions are enabled, then the `Error` class extends `std::exception` +and enables integrated error-handling for C++ exceptions and JavaScript +exceptions. + +If a N-API call fails without executing any JavaScript code (for example due to +an invalid argument), then the N-API wrapper automatically converts and throws +the error as a C++ exception of type `Napi::Error`. Or if a JavaScript function +called by C++ code via N-API throws a JavaScript exception, then the N-API +wrapper automatically converts and throws it as a C++ exception of type +`Napi::Error`. + +If a C++ exception of type `Napi::Error` escapes from a N-API C++ callback, then +the N-API wrapper automatically converts and throws it as a JavaScript exception. +Therefore, catching a C++ exception of type `Napi::Error` prevents a JavaScript +exception from being thrown. + +#### Example 1A - Throwing a C++ exception: + +```cpp +Napi::Env env = ... +throw Napi::Error::New(env, "Example exception"); +``` + +Following C++ statements will not be executed. The exception will bubble up as a +C++ exception of type `Napi::Error`, until it is either caught while still in +C++, or else automatically propataged as a JavaScript exception when the callback +returns to JavaScript. + +#### Example 2A - Propagating a N-API C++ exception: + +```cpp +Napi::Function jsFunctionThatThrows = someObj.As(); +Napi::Value result = jsFunctionThatThrows({ arg1, arg2 }); +``` + +Following C++ statements will not be executed. The exception will bubble up as a +C++ exception of type `Napi::Error`, until it is either caught while still in +C++, or else automatically propagated as a JavaScript exception when the callback +returns to JavaScript. + +#### Example 3A - Handling a N-API C++ exception: + +```cpp +Napi::Function jsFunctionThatThrows = someObj.As(); +Napi::Value result; +try { + result = jsFunctionThatThrows({ arg1, arg2 }); +} catch (const Napi::Error& e) { + cerr << "Caught JavaScript exception: " + e.what(); +} +``` + +Since the exception was caught here, it will not be propagated as a JavaScript +exception. + +### Handling Errors Without C++ Exceptions + +If C++ exceptions are disabled (by defining `NAPI_DISABLE_CPP_EXCEPTIONS`) then +this class does not extend `std::exception`, and APIs in the `Napi` namespace do +not throw C++ exceptions when they fail. Instead, they raise _pending_ JavaScript +exceptions and return _empty_ `Value`s. Calling code should check +`Value::IsEmpty()` before attempting o use a returned value, and may use methods +on the `Env` class to check for, get, and clear a pending JavaScript exception. +If the pending exception is not cleared, it will be thrown when the native +callback returns to JavaScript. + +#### Example 1B - Throwing a JS exception + +```cpp +Napi::Env env = ... +Napi::Error::New(env, "Example exception").ThrowAsJavaScriptException(); +return; +``` + +After throwing a JS exception, the code should generally return immediately from +the native callback, after performing any necessary cleanup. + +#### Example 2B - Propagating a N-API JS exception: + +```cpp +Napi::Function jsFunctionThatThrows = someObj.As(); +Napi::Value result = jsFunctionThatThrows({ arg1, arg2 }); +if (result.IsEmpty()) return; +``` + +An empty value result from a N-API call indicates an error occurred, and a +JavaScript xception is pending. To let the exception propagate, the code should +generally return immediately from the native callback, after performing any +necessary cleanup. + +#### Example 3B - Handling a N-API JS exception: + +```cpp +Napi::Function jsFunctionThatThrows = someObj.As(); +Napi::Value result = jsFunctionThatThrows({ arg1, arg2 }); +if (result.IsEmpty()) { + Napi::Error e = env.GetAndClearPendingException(); + cerr << "Caught JavaScript exception: " + e.Message(); +} +``` +Since the exception was cleared here, it will not be propagated as a JavaScript +exception after the native callback returns. \ No newline at end of file diff --git a/doc/setup.md b/doc/setup.md index be95e747e..176815a96 100644 --- a/doc/setup.md +++ b/doc/setup.md @@ -19,7 +19,7 @@ To use **N-API** in a native module: ```json "dependencies": { - "node-addon-api": "1.1.0", + "node-addon-api": "1.2.0", } ``` From 794ee4db4b8b64a473caaf50c681c19a273cd928 Mon Sep 17 00:00:00 2001 From: NickNaso Date: Thu, 3 May 2018 04:03:02 +0200 Subject: [PATCH 3/8] Documentation for error handling --- README.md | 2 + doc/error.md | 102 ++++++++++++++++++++++++++++++++++-- doc/error_handling.md | 117 ++++++++++++++++++++++++++---------------- doc/range_error.md | 59 +++++++++++++++++++++ doc/type_error.md | 59 +++++++++++++++++++++ 5 files changed, 292 insertions(+), 47 deletions(-) create mode 100644 doc/range_error.md create mode 100644 doc/type_error.md diff --git a/README.md b/README.md index ebf6fec2d..409fd1d16 100644 --- a/README.md +++ b/README.md @@ -64,6 +64,8 @@ values. Concepts and operations generally map to ideas specified in the - [PropertyDescriptor](doc/property_descriptor.md) - [Error Handling](doc/error_handling.md) - [Error](doc/error.md) + - [TypeError](doc/type_error.md) + - [RangeError](doc/range_error.md) - [Object Lifettime Management](doc/object_lifetime_management.md) - [HandleScope](doc/handle_scope.md) - [EscapableHandleScope](doc/escapable_handle_scope.md) diff --git a/doc/error.md b/doc/error.md index 5ad6befc8..ae03cdfa2 100644 --- a/doc/error.md +++ b/doc/error.md @@ -1,5 +1,101 @@ # Error -You are reading a draft of the next documentation and it's in continuos update so -if you don't find what you need please refer to: -[C++ wrapper classes for the ABI-stable C APIs for Node.js](https://nodejs.github.io/node-addon-api/) \ No newline at end of file +The **Error** class is a representation of the JavaScript Error object that is thrown +when runtime errors occur. The Error object can also be used as a base object for +user defined exceptions. + +The **Error** class is a persistent reference to a JavaScript error object and +inherits its behaviour from ObjectReference class (for more info see: [ObjectReference](object_reference.md) +section). + +If C++ exceptions are enabled (for more info see: [Setup](setup.md) section), +then the **Error** class extends ```std::exception``` and enables integrated +error-handling for C++ exceptions and JavaScript exceptions. + +For more details about error handling refer to the section titled [Error handling](error_handling.md). + +## Methods + +### New + +Creates a new instance empty of ```Error``` object for the specified environment. + +```cpp +Error::New(Napi:Env env); +``` + +- ```[in] Env```: The environment in which to construct the Error object. + +Returns an instance of ```Error``` object. + +### New + +Creates a new instance of ```Error``` object + +```cpp +Error::New(Napi:Env env, const char* message); +``` + +- ```[in] Env```: The environment in which to construct the Error object. +- ```[in] message```: Null-terminated strings to be used as the message for the Error. + +Returns an instance of ```Error``` object. + +### New + +Creates a new instance of ```Error``` object + +```cpp +Error::New(Napi:Env env, const std::string& message); +``` + +- `[in] Env`: The environment in which to construct the Error object. +- `[in] message`: Reference string to be used as the message for the Error. + +Returns an instance of ```Error``` object. + +### Constructor + +Creates a new empty instance of ```Error``` + +```cpp +Error(); +``` + +### Constructor + +Initializes a ```Error``` instance from an existing ```Error``` object. + +```cpp +TypeError(napi_env env, napi_value value); +``` + +- ```[in] Env```: The environment in which to construct the Error object. +- ```[in] value```: The ```Error``` reference to wrap. + +Returns an instance of ```Error``` object. + +### Message + +```cpp +std::string& Message() const NAPI_NOEXCEPT; +``` + +Returns the reference to string that represent the message of the error. + +### ThrowAsJavaScriptException + +```cpp +void ThrowAsJavaScriptException() const; +``` + +Throw the error as JavaScript exception. + +### what + +```cpp +const char* what() const NAPI_NOEXCEPT override; +``` + +Returns a pointer to a null-terminated string that is used to identify the +exception. This method can be used only if the eceptions mechanis is enabled. \ No newline at end of file diff --git a/doc/error_handling.md b/doc/error_handling.md index 5517483eb..d8684de72 100644 --- a/doc/error_handling.md +++ b/doc/error_handling.md @@ -1,51 +1,74 @@ # Error handling -A persistent reference to a JavaScript error object. Use of this class depends -somewhat on whether C++ exceptions are enabled at compile time. +The error handling represents one of the most important thing on implementing a +Node.js native add-on. When an error occurs in your C++ code you have to handle +and dispatch it correctly. **N-API** uses return values and JavaScript exceptions +for error handling. You can choice one method or other based on your preferences +or on the C/C++, library that you are integrating. -### Handling Errors With C++ Exceptions +The **Error** is a persistent reference (for more info see: [Object reference](object_reference.md) +section) to a JavaScript error object. Use of this class depends somewhat on +whether C++ exceptions are enabled at compile time. -If C++ exceptions are enabled, then the `Error` class extends `std::exception` -and enables integrated error-handling for C++ exceptions and JavaScript -exceptions. +The following sections explain the approach for each case: -If a N-API call fails without executing any JavaScript code (for example due to -an invalid argument), then the N-API wrapper automatically converts and throws -the error as a C++ exception of type `Napi::Error`. Or if a JavaScript function -called by C++ code via N-API throws a JavaScript exception, then the N-API -wrapper automatically converts and throws it as a C++ exception of type -`Napi::Error`. +- [Handling Errors With C++ Exceptions](#exceptions) -If a C++ exception of type `Napi::Error` escapes from a N-API C++ callback, then -the N-API wrapper automatically converts and throws it as a JavaScript exception. -Therefore, catching a C++ exception of type `Napi::Error` prevents a JavaScript +- [Handling Errors Without C++ Exceptions](#noexceptions) + + + +## Handling Errors With C++ Exceptions + +If C++ exceptions are enabled (for more info see: [Setup](setup.md) section), +then the **Error** class extends ```std::exception``` and enables integrated +error-handling for C++ exceptions and JavaScript exceptions. + +If a N-API call fails without executing any JavaScript code (for example due to +an invalid argument), then the N-API wrapper automatically converts and throws +the error as a C++ exception of type **Error**. + +If a JavaScript function called by C++ code via N-API throws a JavaScript +exception, then the N-API wrapper automatically converts and throws it as a C++ +exception of type **Error**. + +If a C++ exception of type **Error** escapes from a N-API C++ callback, then +the N-API wrapper automatically converts and throws it as a JavaScript exception. + +Therefore, catching a C++ exception of type **Error** prevents a JavaScript exception from being thrown. -#### Example 1A - Throwing a C++ exception: +## Examples with C++ exceptions enabled + +### Throwing a C++ exception ```cpp Napi::Env env = ... throw Napi::Error::New(env, "Example exception"); +// other C++ statements +// ... ``` -Following C++ statements will not be executed. The exception will bubble up as a -C++ exception of type `Napi::Error`, until it is either caught while still in -C++, or else automatically propataged as a JavaScript exception when the callback -returns to JavaScript. +Following C++ statements will not be executed. The exception will bubble up as a +C++ exception of type **Error**, until it is either caught while still in C++, or +else automatically propataged as a JavaScript exception when returns to +JavaScript. -#### Example 2A - Propagating a N-API C++ exception: +### Propagating a N-API C++ exception ```cpp Napi::Function jsFunctionThatThrows = someObj.As(); Napi::Value result = jsFunctionThatThrows({ arg1, arg2 }); +// other C++ statements +// ... ``` -Following C++ statements will not be executed. The exception will bubble up as a -C++ exception of type `Napi::Error`, until it is either caught while still in -C++, or else automatically propagated as a JavaScript exception when the callback -returns to JavaScript. +Following C++ statements will not be executed. The exception will bubble up as a +C++ exception of type **Error**, until it is either caught while still in C++, or +else automatically propagated as a JavaScript exception when returns to +JavaScript. -#### Example 3A - Handling a N-API C++ exception: +### Handling a N-API C++ exception ```cpp Napi::Function jsFunctionThatThrows = someObj.As(); @@ -57,21 +80,26 @@ try { } ``` -Since the exception was caught here, it will not be propagated as a JavaScript +Since the exception was caught here, it will not be propagated as a JavaScript exception. -### Handling Errors Without C++ Exceptions + -If C++ exceptions are disabled (by defining `NAPI_DISABLE_CPP_EXCEPTIONS`) then -this class does not extend `std::exception`, and APIs in the `Napi` namespace do -not throw C++ exceptions when they fail. Instead, they raise _pending_ JavaScript -exceptions and return _empty_ `Value`s. Calling code should check -`Value::IsEmpty()` before attempting o use a returned value, and may use methods -on the `Env` class to check for, get, and clear a pending JavaScript exception. -If the pending exception is not cleared, it will be thrown when the native -callback returns to JavaScript. +## Handling Errors Without C++ Exceptions -#### Example 1B - Throwing a JS exception +If C++ exceptions are disabled (for more info see: [Setup](setup.md) section), +then the **Error** class does not extend ```std::exception```. This means that +any call to node-addon-api functions does not throw C++ exception. +Instead, it raises _pending_ JavaScript exceptions and return _empty_ **Value**. +The calling code should check ```Value::IsEmpty()``` (for more info see: [Value](value.md)) +before attempting or use a returned value, and may use methods on the **Env** class +to check for, get, and clear a pending JavaScript exception (for more info see: [Env](env.md)). +If the pending exception is not cleared, it will be thrown when the native code +returns to JavaScript. + +## Examples with C++ exceptions disabled + +### Throwing a JS exception ```cpp Napi::Env env = ... @@ -79,10 +107,10 @@ Napi::Error::New(env, "Example exception").ThrowAsJavaScriptException(); return; ``` -After throwing a JS exception, the code should generally return immediately from +After throwing a JS exception, the code should generally return immediately from the native callback, after performing any necessary cleanup. -#### Example 2B - Propagating a N-API JS exception: +### Propagating a N-API JS exception ```cpp Napi::Function jsFunctionThatThrows = someObj.As(); @@ -90,12 +118,12 @@ Napi::Value result = jsFunctionThatThrows({ arg1, arg2 }); if (result.IsEmpty()) return; ``` -An empty value result from a N-API call indicates an error occurred, and a -JavaScript xception is pending. To let the exception propagate, the code should -generally return immediately from the native callback, after performing any +An empty value result from a N-API call indicates that an error occurred, and a +JavaScript exception is pending. To let the exception propagate, the code should +generally return immediately from the native callback, after performing any necessary cleanup. -#### Example 3B - Handling a N-API JS exception: +### Handling a N-API JS exception ```cpp Napi::Function jsFunctionThatThrows = someObj.As(); @@ -105,5 +133,6 @@ if (result.IsEmpty()) { cerr << "Caught JavaScript exception: " + e.Message(); } ``` -Since the exception was cleared here, it will not be propagated as a JavaScript + +Since the exception was cleared here, it will not be propagated as a JavaScript exception after the native callback returns. \ No newline at end of file diff --git a/doc/range_error.md b/doc/range_error.md new file mode 100644 index 000000000..6a27053f3 --- /dev/null +++ b/doc/range_error.md @@ -0,0 +1,59 @@ +# RangeError + +The **RangeError** class is a representation of the JavaScript RangeError that is +thrown when trying to pass a value as an argument to a function that does not allow +a range that includes the value. + +The **RangeError** class inherits its behaviours from **Error** class (for more info +see: [Error](error.md) section). + +For more details about error handling refer to the section titled [Error handling](error_handling.md). + +## Methods + +### New + +Creates a new instance of ```RangeError``` object + +```cpp +RangeError::New(Napi:Env env, const char* message); +``` + +- ```[in] Env```: The environment in which to construct the RangeError object. +- ```[in] message```: Null-terminated strings to be used as the message for the RangeError. + +Returns an instance of ```RangeError``` object. + +### New + +Creates a new instance of ```RangeError``` object + +```cpp +RangeError::New(Napi:Env env, const std::string& message); +``` + +- `[in] Env`: The environment in which to construct the RangeError object. +- `[in] message`: Reference string to be used as the message for the RangeError. + +Returns an instance of ```RangeError``` object. + +### Constructor + +Creates a new empty instance of ```RangeError``` + +```cpp +RangeError(); +``` + +### Constructor + +Initializes a ```RangeError``` instance from an existing ```Error``` object. + +```cpp +RangeError(napi_env env, napi_value value); +``` + +- ```[in] Env```: The environment in which to construct the RangeError object. +- ```[in] value```: The ```Error``` reference to wrap. + +Returns an instance of ```RangeError``` object. \ No newline at end of file diff --git a/doc/type_error.md b/doc/type_error.md new file mode 100644 index 000000000..b9d0e8430 --- /dev/null +++ b/doc/type_error.md @@ -0,0 +1,59 @@ +# TypeError + +The **TypeError** class is a representation of the JavaScript TypeError that is +thrown when an operand or argument passed to a function is incompatible with the +type expected by the operator or function. + +The **TypeError** class inherits its behaviours from **Error** class (for more info +see: [Error](error.md) section). + +For more details about error handling refer to the section titled [Error handling](error_handling.md). + +## Methods + +### New + +Creates a new instance of ```TypeError``` object + +```cpp +TypeError::New(Napi:Env env, const char* message); +``` + +- ```[in] Env```: The environment in which to construct the TypeError object. +- ```[in] message```: Null-terminated strings to be used as the message for the TypeError. + +Returns an instance of ```TypeError``` object. + +### New + +Creates a new instance of ```TypeError``` object + +```cpp +TypeError::New(Napi:Env env, const std::string& message); +``` + +- `[in] Env`: The environment in which to construct the TypeError object. +- `[in] message`: Reference string to be used as the message for the TypeError. + +Returns an instance of ```TypeError``` object. + +### Constructor + +Creates a new empty instance of ```TypeError``` + +```cpp +TypeError(); +``` + +### Constructor + +Initializes a ```TypeError``` instance from an existing ```Error``` object. + +```cpp +TypeError(napi_env env, napi_value value); +``` + +- ```[in] Env```: The environment in which to construct the TypeError object. +- ```[in] value```: The ```Error``` reference to wrap. + +Returns an instance of ```TypeError``` object. \ No newline at end of file From 844b651e2106dd81a59268bd9c233f13b3c4180c Mon Sep 17 00:00:00 2001 From: NickNaso Date: Thu, 10 May 2018 14:47:26 +0200 Subject: [PATCH 4/8] Complete fix proposed on first review --- doc/error.md | 43 ++++++++++++++-------- doc/error_handling.md | 85 +++++++++++++++++++++++++------------------ doc/range_error.md | 26 ++++++------- doc/type_error.md | 26 ++++++------- 4 files changed, 103 insertions(+), 77 deletions(-) diff --git a/doc/error.md b/doc/error.md index ae03cdfa2..c0c71d637 100644 --- a/doc/error.md +++ b/doc/error.md @@ -9,7 +9,7 @@ inherits its behaviour from ObjectReference class (for more info see: [ObjectRef section). If C++ exceptions are enabled (for more info see: [Setup](setup.md) section), -then the **Error** class extends ```std::exception``` and enables integrated +then the **Error** class extends `std::exception` and enables integrated error-handling for C++ exceptions and JavaScript exceptions. For more details about error handling refer to the section titled [Error handling](error_handling.md). @@ -18,32 +18,32 @@ For more details about error handling refer to the section titled [Error handlin ### New -Creates a new instance empty of ```Error``` object for the specified environment. +Creates a new instance empty of `Error` object for the specified environment. ```cpp Error::New(Napi:Env env); ``` -- ```[in] Env```: The environment in which to construct the Error object. +- `[in] Env`: The environment in which to construct the Error object. -Returns an instance of ```Error``` object. +Returns an instance of `Error` object. ### New -Creates a new instance of ```Error``` object +Creates a new instance of `Error` object ```cpp Error::New(Napi:Env env, const char* message); ``` -- ```[in] Env```: The environment in which to construct the Error object. -- ```[in] message```: Null-terminated strings to be used as the message for the Error. +- `[in] Env`: The environment in which to construct the Error object. +- `[in] message`: Null-terminated strings to be used as the message for the Error. -Returns an instance of ```Error``` object. +Returns an instance of `Error` object. ### New -Creates a new instance of ```Error``` object +Creates a new instance of `Error` object ```cpp Error::New(Napi:Env env, const std::string& message); @@ -52,11 +52,22 @@ Error::New(Napi:Env env, const std::string& message); - `[in] Env`: The environment in which to construct the Error object. - `[in] message`: Reference string to be used as the message for the Error. -Returns an instance of ```Error``` object. +Returns an instance of `Error` object. + +### Fatal + +In case of an unrecoverable error in a native module, a fatal error can be thrown +to immediately terminate the process. + +```cpp +static NAPI_NO_RETURN void Fatal(const char* location, const char* message); +``` + +The function call does not return, the process will be terminated. ### Constructor -Creates a new empty instance of ```Error``` +Creates a new empty instance of `Error` ```cpp Error(); @@ -64,10 +75,10 @@ Error(); ### Constructor -Initializes a ```Error``` instance from an existing ```Error``` object. +Initializes a `Error` instance from an existing JavaScript error object. ```cpp -TypeError(napi_env env, napi_value value); +Error(napi_env env, napi_value value); ``` - ```[in] Env```: The environment in which to construct the Error object. @@ -85,11 +96,13 @@ Returns the reference to string that represent the message of the error. ### ThrowAsJavaScriptException +Throw the error as JavaScript exception. + ```cpp void ThrowAsJavaScriptException() const; ``` -Throw the error as JavaScript exception. +Throws the error as JavaScript exception. ### what @@ -98,4 +111,4 @@ const char* what() const NAPI_NOEXCEPT override; ``` Returns a pointer to a null-terminated string that is used to identify the -exception. This method can be used only if the eceptions mechanis is enabled. \ No newline at end of file +exception. This method can be used only if the exception mechanism is enabled. \ No newline at end of file diff --git a/doc/error_handling.md b/doc/error_handling.md index d8684de72..093ba6736 100644 --- a/doc/error_handling.md +++ b/doc/error_handling.md @@ -1,28 +1,31 @@ # Error handling -The error handling represents one of the most important thing on implementing a -Node.js native add-on. When an error occurs in your C++ code you have to handle -and dispatch it correctly. **N-API** uses return values and JavaScript exceptions -for error handling. You can choice one method or other based on your preferences -or on the C/C++, library that you are integrating. +Error handling represents one of the most important considerations when +implementing a Node.js native add-on. When an error occurs in your C++ code you +have to handle and dispatch it correctly. **N-API** uses return values and +JavaScript exceptions for error handling. You can choose return values or +exception handling based on the mechanism that works best for your add-on. The **Error** is a persistent reference (for more info see: [Object reference](object_reference.md) -section) to a JavaScript error object. Use of this class depends somewhat on -whether C++ exceptions are enabled at compile time. +section) to a JavaScript error object. Use of this class depends on whether C++ +exceptions are enabled at compile time. + +If C++ exceptions are enabled (for more info see: [Setup](setup.md) section), +then the **Error** class extends `std::exception` and enables integrated +error-handling for C++ exceptions and JavaScript exceptions. The following sections explain the approach for each case: - [Handling Errors With C++ Exceptions](#exceptions) - - [Handling Errors Without C++ Exceptions](#noexceptions) ## Handling Errors With C++ Exceptions -If C++ exceptions are enabled (for more info see: [Setup](setup.md) section), -then the **Error** class extends ```std::exception``` and enables integrated -error-handling for C++ exceptions and JavaScript exceptions. +When C++ exceptions are enabled try/catch can be used to catch exceptions thrown +from calls to JavaScript and then they can either be handled or rethrown before +returning from a native method. If a N-API call fails without executing any JavaScript code (for example due to an invalid argument), then the N-API wrapper automatically converts and throws @@ -30,52 +33,57 @@ the error as a C++ exception of type **Error**. If a JavaScript function called by C++ code via N-API throws a JavaScript exception, then the N-API wrapper automatically converts and throws it as a C++ -exception of type **Error**. +exception of type **Error** on return from the JavaScript code to the native +method. If a C++ exception of type **Error** escapes from a N-API C++ callback, then the N-API wrapper automatically converts and throws it as a JavaScript exception. -Therefore, catching a C++ exception of type **Error** prevents a JavaScript -exception from being thrown. +On return from a native method, N-API will automatically convert a pending C++ +exception to a JavaScript exception. + +When C++ exceptions are enabled try/catch can be used to catch exceptions thrown +from calls to JavaScript and then they can either be handled or rethrown before +returning from a native method. ## Examples with C++ exceptions enabled ### Throwing a C++ exception ```cpp -Napi::Env env = ... -throw Napi::Error::New(env, "Example exception"); +Env env = ... +throw Error::New(env, "Example exception"); // other C++ statements // ... ``` Following C++ statements will not be executed. The exception will bubble up as a C++ exception of type **Error**, until it is either caught while still in C++, or -else automatically propataged as a JavaScript exception when returns to +else automatically propagated as a JavaScript exception when returns to JavaScript. ### Propagating a N-API C++ exception ```cpp -Napi::Function jsFunctionThatThrows = someObj.As(); -Napi::Value result = jsFunctionThatThrows({ arg1, arg2 }); +Function jsFunctionThatThrows = someObj.As(); +Value result = jsFunctionThatThrows({ arg1, arg2 }); // other C++ statements // ... ``` Following C++ statements will not be executed. The exception will bubble up as a C++ exception of type **Error**, until it is either caught while still in C++, or -else automatically propagated as a JavaScript exception when returns to +else automatically propagated as a JavaScript exception when returns to JavaScript. ### Handling a N-API C++ exception ```cpp -Napi::Function jsFunctionThatThrows = someObj.As(); -Napi::Value result; +Function jsFunctionThatThrows = someObj.As(); +Value result; try { result = jsFunctionThatThrows({ arg1, arg2 }); -} catch (const Napi::Error& e) { +} catch (const Error& e) { cerr << "Caught JavaScript exception: " + e.what(); } ``` @@ -88,10 +96,10 @@ exception. ## Handling Errors Without C++ Exceptions If C++ exceptions are disabled (for more info see: [Setup](setup.md) section), -then the **Error** class does not extend ```std::exception```. This means that -any call to node-addon-api functions does not throw C++ exception. -Instead, it raises _pending_ JavaScript exceptions and return _empty_ **Value**. -The calling code should check ```Value::IsEmpty()``` (for more info see: [Value](value.md)) +then the **Error** class does not extend `std::exception`. This means that any +calls to node-addon-api function do not throw C++ exceptions. Instead, it raises +_pending_ JavaScript exceptions and return _empty_ **Value**. +The calling code should check `Value::IsEmpty()` (for more info see: [Value](value.md)) before attempting or use a returned value, and may use methods on the **Env** class to check for, get, and clear a pending JavaScript exception (for more info see: [Env](env.md)). If the pending exception is not cleared, it will be thrown when the native code @@ -102,8 +110,8 @@ returns to JavaScript. ### Throwing a JS exception ```cpp -Napi::Env env = ... -Napi::Error::New(env, "Example exception").ThrowAsJavaScriptException(); +Env env = ... +Error::New(env, "Example exception").ThrowAsJavaScriptException(); return; ``` @@ -113,9 +121,13 @@ the native callback, after performing any necessary cleanup. ### Propagating a N-API JS exception ```cpp -Napi::Function jsFunctionThatThrows = someObj.As(); -Napi::Value result = jsFunctionThatThrows({ arg1, arg2 }); -if (result.IsEmpty()) return; +Env env = ... +Function jsFunctionThatThrows = someObj.As(); +Value result = jsFunctionThatThrows({ arg1, arg2 }); +if (env.IsExceptionPending()) { + Error e = env.GetAndClearPendingException(); + return e.Value(); +} ``` An empty value result from a N-API call indicates that an error occurred, and a @@ -126,10 +138,11 @@ necessary cleanup. ### Handling a N-API JS exception ```cpp -Napi::Function jsFunctionThatThrows = someObj.As(); -Napi::Value result = jsFunctionThatThrows({ arg1, arg2 }); -if (result.IsEmpty()) { - Napi::Error e = env.GetAndClearPendingException(); +Env env = ... +Function jsFunctionThatThrows = someObj.As(); +Value result = jsFunctionThatThrows({ arg1, arg2 }); +if (env.IsExceptionPending()) { + Error e = env.GetAndClearPendingException(); cerr << "Caught JavaScript exception: " + e.Message(); } ``` diff --git a/doc/range_error.md b/doc/range_error.md index 6a27053f3..776401399 100644 --- a/doc/range_error.md +++ b/doc/range_error.md @@ -13,33 +13,33 @@ For more details about error handling refer to the section titled [Error handlin ### New -Creates a new instance of ```RangeError``` object +Creates a new instance of `RangeError` object ```cpp RangeError::New(Napi:Env env, const char* message); ``` -- ```[in] Env```: The environment in which to construct the RangeError object. -- ```[in] message```: Null-terminated strings to be used as the message for the RangeError. +- `[in] Env`: The environment in which to construct the `RangeError` object. +- `[in] message`: Null-terminated strings to be used as the message for the `RangeError`. -Returns an instance of ```RangeError``` object. +Returns an instance of `RangeError` object. ### New -Creates a new instance of ```RangeError``` object +Creates a new instance of `RangeError` object ```cpp RangeError::New(Napi:Env env, const std::string& message); ``` -- `[in] Env`: The environment in which to construct the RangeError object. -- `[in] message`: Reference string to be used as the message for the RangeError. +- `[in] Env`: The environment in which to construct the `RangeError` object. +- `[in] message`: Reference string to be used as the message for the `RangeError`. -Returns an instance of ```RangeError``` object. +Returns an instance of `RangeError` object. ### Constructor -Creates a new empty instance of ```RangeError``` +Creates a new empty instance of `RangeError` ```cpp RangeError(); @@ -47,13 +47,13 @@ RangeError(); ### Constructor -Initializes a ```RangeError``` instance from an existing ```Error``` object. +Initializes a `RangeError` instance from an existing Javascript error object. ```cpp RangeError(napi_env env, napi_value value); ``` -- ```[in] Env```: The environment in which to construct the RangeError object. -- ```[in] value```: The ```Error``` reference to wrap. +- `[in] Env`: The environment in which to construct the `RangeError` object. +- `[in] value`: The `Error` reference to wrap. -Returns an instance of ```RangeError``` object. \ No newline at end of file +Returns an instance of `RangeError` object. \ No newline at end of file diff --git a/doc/type_error.md b/doc/type_error.md index b9d0e8430..1fd2532ba 100644 --- a/doc/type_error.md +++ b/doc/type_error.md @@ -13,33 +13,33 @@ For more details about error handling refer to the section titled [Error handlin ### New -Creates a new instance of ```TypeError``` object +Creates a new instance of `TypeError` object ```cpp TypeError::New(Napi:Env env, const char* message); ``` -- ```[in] Env```: The environment in which to construct the TypeError object. -- ```[in] message```: Null-terminated strings to be used as the message for the TypeError. +- `[in] Env`: The environment in which to construct the `TypeError` object. +- `[in] message`: Null-terminated strings to be used as the message for the `TypeError`. -Returns an instance of ```TypeError``` object. +Returns an instance of `TypeError` object. ### New -Creates a new instance of ```TypeError``` object +Creates a new instance of `TypeError` object ```cpp TypeError::New(Napi:Env env, const std::string& message); ``` -- `[in] Env`: The environment in which to construct the TypeError object. -- `[in] message`: Reference string to be used as the message for the TypeError. +- `[in] Env`: The environment in which to construct the `TypeError` object. +- `[in] message`: Reference string to be used as the message for the `TypeError`. -Returns an instance of ```TypeError``` object. +Returns an instance of `TypeError` object. ### Constructor -Creates a new empty instance of ```TypeError``` +Creates a new empty instance of `TypeError` ```cpp TypeError(); @@ -47,13 +47,13 @@ TypeError(); ### Constructor -Initializes a ```TypeError``` instance from an existing ```Error``` object. +Initializes a ```TypeError``` instance from an existing JavaScript error object. ```cpp TypeError(napi_env env, napi_value value); ``` -- ```[in] Env```: The environment in which to construct the TypeError object. -- ```[in] value```: The ```Error``` reference to wrap. +- `[in] Env`: The environment in which to construct the `TypeError` object. +- `[in] value`: The `Error` reference to wrap. -Returns an instance of ```TypeError``` object. \ No newline at end of file +Returns an instance of `TypeError` object. \ No newline at end of file From 92e6e899f1d6026d52cf9ff833f70e3656b33115 Mon Sep 17 00:00:00 2001 From: NickNaso Date: Tue, 15 May 2018 01:57:49 +0200 Subject: [PATCH 5/8] First steps to add memory management function with corresponding tests and documentation --- README.md | 1 + doc/memory_management.md | 24 ++++++++++++++++++++++++ napi-inl.h | 11 +++++++++++ napi.h | 3 +++ package.json | 2 +- test/binding.cc | 2 ++ test/binding.gyp | 1 + test/index.js | 1 + test/memory_management.cc | 17 +++++++++++++++++ test/memory_management.js | 10 ++++++++++ 10 files changed, 71 insertions(+), 1 deletion(-) create mode 100644 doc/memory_management.md create mode 100644 test/memory_management.cc create mode 100644 test/memory_management.js diff --git a/README.md b/README.md index 76cf996fb..e651dcccb 100644 --- a/README.md +++ b/README.md @@ -76,6 +76,7 @@ values. Concepts and operations generally map to ideas specified in the - [ArrayBuffer](doc/array_buffer.md) - [TypedArray](doc/typed_array.md) - [TypedArrayOf](doc/typed_array_of.md) + - [Memory Management](doc/memory_management.md) - [Async Operations](doc/async_operations.md) - [AsyncWorker](async_worker.md) - [Promises](doc/promises.md) diff --git a/doc/memory_management.md b/doc/memory_management.md new file mode 100644 index 000000000..a47b07a0d --- /dev/null +++ b/doc/memory_management.md @@ -0,0 +1,24 @@ +# Memory Management + +The function `AdjustExternalMemory` adjusts the amount of registered external +memory. Used to give JavaScript engine an indication of the amount of externally +allocated memory that is kept alive by JavaScript objects. +JavaScript engine uses this to decide when to perform global garbage collections. +Registering externally allocated memory will trigger global garbage collections +more often than it would otherwise in an attempt to garbage collect the JavaScript +objects that keep the externally allocated memory alive. + +## AdjustExternalMemory + +This function gives to JavaScript engine an indication of the amount of externally +allocated memory that is kept alive by JavaScript objects. + +```cpp +int64_t AdjustExternalMemory(napi_env env, int64_t change_in_bytes); +``` + +- `[in] env`: The environment in which the API is inoked under. +- `[in] change_in_bytes`: The change in externally allocated memory that is kept +alive by JavaScript objects expressed in bytes. + +Returns the adjusted memory value. \ No newline at end of file diff --git a/napi-inl.h b/napi-inl.h index 884f8cbbf..2c4b5c626 100644 --- a/napi-inl.h +++ b/napi-inl.h @@ -3074,6 +3074,17 @@ inline Value EscapableHandleScope::Escape(napi_value escapee) { return Value(_env, result); } +//////////////////////////////////////////////////////////////////////////////// +// Memory Management +//////////////////////////////////////////////////////////////////////////////// + +inline int64_t AdjustExternalMemory(napi_env env, int64_t change_in_bytes) { + int64_t result; + napi_status status = napi_adjust_external_memory(env, change_in_bytes, &result); + NAPI_THROW_IF_FAILED(env, status, 0); + return result; +} + //////////////////////////////////////////////////////////////////////////////// // AsyncWorker class //////////////////////////////////////////////////////////////////////////////// diff --git a/napi.h b/napi.h index a2d1f20ec..f03c0b7da 100644 --- a/napi.h +++ b/napi.h @@ -1536,6 +1536,9 @@ namespace Napi { std::string _error; }; + /// Memory management. + int64_t AdjustExternalMemory(napi_env env, int64_t change_in_bytes); + } // namespace Napi // Inline implementations of all the above class methods are included here. diff --git a/package.json b/package.json index 352cfc382..f1fdeb73a 100644 --- a/package.json +++ b/package.json @@ -8,7 +8,7 @@ "Anna Henningsen (https://github.com/addaleax)", "Arunesh Chandra (https://github.com/aruneshchandra)", "Benjamin Byholm (https://github.com/kkoopa)", - "Cory Mickelson (https://github.com/corymickelson)", + "Cory Mickelson (https://github.com/corymickelson)", "David Halls (https://github.com/davedoesdev)", "Eric Bickle (https://github.com/ebickle)", "Gabriel Schulhof (https://github.com/gabrielschulhof)", diff --git a/test/binding.cc b/test/binding.cc index 8e6a1e9ec..085eb8e7b 100644 --- a/test/binding.cc +++ b/test/binding.cc @@ -13,6 +13,7 @@ Object InitError(Env env); Object InitExternal(Env env); Object InitFunction(Env env); Object InitHandleScope(Env env); +Object InitMemoryManagement(Env env); Object InitName(Env env); Object InitObject(Env env); Object InitPromise(Env env); @@ -31,6 +32,7 @@ Object Init(Env env, Object exports) { exports.Set("error", InitError(env)); exports.Set("external", InitExternal(env)); exports.Set("function", InitFunction(env)); + exports.Set("memory_management", InitMemoryManagement(env)); exports.Set("name", InitName(env)); exports.Set("handlescope", InitHandleScope(env)); exports.Set("object", InitObject(env)); diff --git a/test/binding.gyp b/test/binding.gyp index 4e0050480..acbbc0a12 100644 --- a/test/binding.gyp +++ b/test/binding.gyp @@ -13,6 +13,7 @@ 'external.cc', 'function.cc', 'handlescope.cc', + 'memory_management.cc', 'name.cc', 'object/delete_property.cc', 'object/get_property.cc', diff --git a/test/index.js b/test/index.js index 5211bb467..c9d7fb0c2 100644 --- a/test/index.js +++ b/test/index.js @@ -19,6 +19,7 @@ let testModules = [ 'external', 'function', 'handlescope', + 'memory_management', 'name', 'object/delete_property', 'object/get_property', diff --git a/test/memory_management.cc b/test/memory_management.cc new file mode 100644 index 000000000..fe481f2f1 --- /dev/null +++ b/test/memory_management.cc @@ -0,0 +1,17 @@ +#include "napi.h" + +using namespace Napi; + +Value externalAllocatedMemory(const CallbackInfo& info) { + int64_t kSize = 1024 * 1024; + int64_t baseline = AdjustExternalMemory(info.Env(), 0); + int64_t tmp = AdjustExternalMemory(info.Env(), kSize); + tmp = AdjustExternalMemory(info.Env(), -kSize); + return Boolean::New(info.Env(), tmp == baseline); +} + +Object InitMemoryManagement(Env env) { + Object exports = Object::New(env); + exports["externalAllocatedMemory"] = Function::New(env, externalAllocatedMemory); + return exports; +} \ No newline at end of file diff --git a/test/memory_management.js b/test/memory_management.js new file mode 100644 index 000000000..7b0b63a2f --- /dev/null +++ b/test/memory_management.js @@ -0,0 +1,10 @@ +'use strict'; +const buildType = process.config.target_defaults.default_configuration; +const assert = require('assert'); + +test(require(`./build/${buildType}/binding.node`)); +test(require(`./build/${buildType}/binding_noexcept.node`)); + +function test(binding) { + assert.strictEqual(binding.memory_management.externalAllocatedMemory(), true) +} \ No newline at end of file From d617836e2893c191f955a83a8f1869ad71decac6 Mon Sep 17 00:00:00 2001 From: Michael Dawson Date: Tue, 29 May 2018 20:48:42 -0400 Subject: [PATCH 6/8] squash: fix nits --- doc/memory_management.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/doc/memory_management.md b/doc/memory_management.md index a47b07a0d..bd40a95bc 100644 --- a/doc/memory_management.md +++ b/doc/memory_management.md @@ -1,16 +1,16 @@ # Memory Management The function `AdjustExternalMemory` adjusts the amount of registered external -memory. Used to give JavaScript engine an indication of the amount of externally +memory. Used to give the JavaScript engine an indication of the amount of externally allocated memory that is kept alive by JavaScript objects. -JavaScript engine uses this to decide when to perform global garbage collections. +The JavaScript engine uses this to decide when to perform global garbage collections. Registering externally allocated memory will trigger global garbage collections more often than it would otherwise in an attempt to garbage collect the JavaScript objects that keep the externally allocated memory alive. ## AdjustExternalMemory -This function gives to JavaScript engine an indication of the amount of externally +This function gives the JavaScript engine an indication of the amount of externally allocated memory that is kept alive by JavaScript objects. ```cpp @@ -21,4 +21,4 @@ int64_t AdjustExternalMemory(napi_env env, int64_t change_in_bytes); - `[in] change_in_bytes`: The change in externally allocated memory that is kept alive by JavaScript objects expressed in bytes. -Returns the adjusted memory value. \ No newline at end of file +Returns the adjusted memory value. From c61469761c35aa3491ab68de5e8ef777426d40ae Mon Sep 17 00:00:00 2001 From: NickNaso Date: Wed, 6 Jun 2018 10:43:52 +0200 Subject: [PATCH 7/8] Set env parameter as node-addon-api Eenv instead of napi_env in AdjustExternalMemory --- napi-inl.h | 2 +- napi.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/napi-inl.h b/napi-inl.h index 2c4b5c626..b75739a94 100644 --- a/napi-inl.h +++ b/napi-inl.h @@ -3078,7 +3078,7 @@ inline Value EscapableHandleScope::Escape(napi_value escapee) { // Memory Management //////////////////////////////////////////////////////////////////////////////// -inline int64_t AdjustExternalMemory(napi_env env, int64_t change_in_bytes) { +inline int64_t AdjustExternalMemory(Env env, int64_t change_in_bytes) { int64_t result; napi_status status = napi_adjust_external_memory(env, change_in_bytes, &result); NAPI_THROW_IF_FAILED(env, status, 0); diff --git a/napi.h b/napi.h index f03c0b7da..ad00981af 100644 --- a/napi.h +++ b/napi.h @@ -1537,7 +1537,7 @@ namespace Napi { }; /// Memory management. - int64_t AdjustExternalMemory(napi_env env, int64_t change_in_bytes); + int64_t AdjustExternalMemory(Env env, int64_t change_in_bytes); } // namespace Napi From 7f171bdb9792544b67b0927f2242862de8c64f3b Mon Sep 17 00:00:00 2001 From: NickNaso Date: Wed, 6 Jun 2018 10:50:40 +0200 Subject: [PATCH 8/8] Update doc for AdjustExternalMemory function --- doc/memory_management.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/memory_management.md b/doc/memory_management.md index a47b07a0d..7714a8a62 100644 --- a/doc/memory_management.md +++ b/doc/memory_management.md @@ -14,7 +14,7 @@ This function gives to JavaScript engine an indication of the amount of external allocated memory that is kept alive by JavaScript objects. ```cpp -int64_t AdjustExternalMemory(napi_env env, int64_t change_in_bytes); +int64_t AdjustExternalMemory(Env env, int64_t change_in_bytes); ``` - `[in] env`: The environment in which the API is inoked under.