Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Documentation for error handling #259

Closed
wants to merge 7 commits into from
Closed
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
102 changes: 99 additions & 3 deletions doc/error.md
Original file line number Diff line number Diff line change
@@ -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/)
The **Error** class is a representation of the JavaScript Error object that is thrown
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we have a guideline for these sorts of stylistic flairs? It seems like Error and Error are used in different scenarios throughout this file, what is the guideline we're following?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No at moment we haven't a stylistic standard to follow. My opinion is that we need one.

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
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For single line code I think you can just use a single backtick.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I will provide to remove where necessary.

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.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've been wresting with how to describe this, maybe "from an existing JavaScript Error object." The description as it stands sounds more like a copy constructor when it seems like this would be used more for JavaScript objects which have been acquired elsewhere.


```cpp
TypeError(napi_env env, napi_value value);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this is a typo, should be Error instead of TypeError.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Believe this should Error not TypeError

```

- ```[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.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: I would move this above where the description normally goes and then update it to say "Throws"

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

+1


### 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.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: "exception mechanism"

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this file is also missing

static NAPI_NO_RETURN void Fatal(const char* location, const char* message);

139 changes: 136 additions & 3 deletions doc/error_handling.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,138 @@
# 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/)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What is our feeling about this? I've seen some of the documentation updates leave it, but maybe it's the right time to start removing these designations.

/cc @mhdawson

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That was my thought, once we fill in the section lets remove the comment.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Replied above at this point I'd say take it out.

The error handling represents one of the most important thing on implementing a
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: "Error handling represents one of the most important considerations when implementing a Node.js native add-on."

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

+1

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
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: "You can choose return values or exception handling based on the mechanism that works best for your add-on."

or on the C/C++, library that you are integrating.

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.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: avoid "somewhat", we want to be as descriptive as possible. If there are more nuances we should try to enumerate them.


The following sections explain the approach for each case:

- [Handling Errors With C++ Exceptions](#exceptions)

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: I don't think this extra newline is necessary here

- [Handling Errors Without C++ Exceptions](#noexceptions)

<a name="exceptions"></a>
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

? wondering why there is html here

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is to create an anchor with a friendly name that's not affected by changes to the name of the header. The same approach is used in the official CHANGELOG

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ok, just have not see it in other docs.


Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: extra newline

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm using markdownlint and it reports

[markdownlint] MD022/blanks-around-headers: Headers should be surrounded by blank lines

but if you want I can remove the extra space. Maybe in general we need to have a standard to follow.

## 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.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If C++ exceptions are enabled then exceptions are converted and handled as C++ exceptions while in native code and as JavaScript exceptions in JavaScript code. What this means is that:

and then make the following bullet points:

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**.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

On return from a native method, N-API will automatically convert a pending C++ exception to a JavaScript exception.

the N-API wrapper automatically converts and throws it as a JavaScript exception.

Therefore, catching a C++ exception of type **Error** prevents a JavaScript
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

exception from being thrown.

## 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 **Error**, until it is either caught while still in C++, or
else automatically propataged as a JavaScript exception when returns to
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: "propagated"

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: "C++ statements following the throwing statement will not be executed. The exception throw is of type Error and can be handled in C++ as usual. If the exception is not handled in C++ it will automatically propagate to the JavaScript caller.

JavaScript.

### Propagating a N-API C++ exception

```cpp
Napi::Function jsFunctionThatThrows = someObj.As<Napi::Function>();
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 **Error**, until it is either caught while still in C++, or
else automatically propagated as a JavaScript exception when returns to
JavaScript.

### Handling a N-API C++ exception

```cpp
Napi::Function jsFunctionThatThrows = someObj.As<Napi::Function>();
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.

<a name="noexceptions"></a>

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: extra newline

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe an example that shows rethrowing ?

## 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
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: extra space after "Error"

any call to node-addon-api functions does not throw C++ exception.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: "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
returns to JavaScript.

## Examples with C++ exceptions disabled

### 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.

### Propagating a N-API JS exception

```cpp
Napi::Function jsFunctionThatThrows = someObj.As<Napi::Function>();
Napi::Value result = jsFunctionThatThrows({ arg1, arg2 });
if (result.IsEmpty()) return;
```
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it would be better to suggest that they should check using IsExceptionPending as that will work for methods that don't return a value as well.


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.

### Handling a N-API JS exception

```cpp
Napi::Function jsFunctionThatThrows = someObj.As<Napi::Function>();
Napi::Value result = jsFunctionThatThrows({ arg1, arg2 });
if (result.IsEmpty()) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same goes here to check with IsExceptionPending

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.
59 changes: 59 additions & 0 deletions doc/range_error.md
Original file line number Diff line number Diff line change
@@ -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.
2 changes: 1 addition & 1 deletion doc/setup.md
Original file line number Diff line number Diff line change
Expand Up @@ -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",
}
```

Expand Down
59 changes: 59 additions & 0 deletions doc/type_error.md
Original file line number Diff line number Diff line change
@@ -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.