Skip to content

Commit 3615041

Browse files
authored
src: return Maybe on pending exception when cpp exception disabled
PR-URL: #927 Reviewed-By: Michael Dawson <[email protected]>
1 parent 10564a4 commit 3615041

35 files changed

+1391
-509
lines changed

doc/error_handling.md

Lines changed: 69 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ error-handling for C++ exceptions and JavaScript exceptions.
1717
The following sections explain the approach for each case:
1818

1919
- [Handling Errors With C++ Exceptions](#exceptions)
20+
- [Handling Errors With Maybe Type and C++ Exceptions Disabled](#noexceptions-maybe)
2021
- [Handling Errors Without C++ Exceptions](#noexceptions)
2122

2223
<a name="exceptions"></a>
@@ -70,7 +71,7 @@ when returning to JavaScript.
7071
### Propagating a Node-API C++ exception
7172
7273
```cpp
73-
Napi::Function jsFunctionThatThrows = someObj.As<Napi::Function>();
74+
Napi::Function jsFunctionThatThrows = someValue.As<Napi::Function>();
7475
Napi::Value result = jsFunctionThatThrows({ arg1, arg2 });
7576
// other C++ statements
7677
// ...
@@ -84,7 +85,7 @@ a JavaScript exception when returning to JavaScript.
8485
### Handling a Node-API C++ exception
8586

8687
```cpp
87-
Napi::Function jsFunctionThatThrows = someObj.As<Napi::Function>();
88+
Napi::Function jsFunctionThatThrows = someValue.As<Napi::Function>();
8889
Napi::Value result;
8990
try {
9091
result = jsFunctionThatThrows({ arg1, arg2 });
@@ -96,6 +97,70 @@ try {
9697
Since the exception was caught here, it will not be propagated as a JavaScript
9798
exception.
9899

100+
<a name="noexceptions-maybe"></a>
101+
102+
## Handling Errors With Maybe Type and C++ Exceptions Disabled
103+
104+
If C++ exceptions are disabled (for more info see: [Setup](setup.md)), then the
105+
`Napi::Error` class does not extend `std::exception`. This means that any calls to
106+
node-addon-api functions do not throw a C++ exceptions. Instead, these node-api
107+
functions that call into JavaScript are returning with `Maybe` boxed values.
108+
In that case, the calling side should convert the `Maybe` boxed values with
109+
checks to ensure that the call did succeed and therefore no exception is pending.
110+
If the check fails, that is to say, the returning value is _empty_, the calling
111+
side should determine what to do with `env.GetAndClearPendingException()` before
112+
attempting to call another node-api (for more info see: [Env](env.md)).
113+
114+
The conversion from the `Maybe` boxed value to the actual return value is
115+
enforced by compilers so that the exceptions must be properly handled before
116+
continuing.
117+
118+
## Examples with Maybe Type and C++ exceptions disabled
119+
120+
### Throwing a JS exception
121+
122+
```cpp
123+
Napi::Env env = ...
124+
Napi::Error::New(env, "Example exception").ThrowAsJavaScriptException();
125+
return;
126+
```
127+
128+
After throwing a JavaScript exception, the code should generally return
129+
immediately from the native callback, after performing any necessary cleanup.
130+
131+
### Propagating a Node-API JS exception
132+
133+
```cpp
134+
Napi::Env env = ...
135+
Napi::Function jsFunctionThatThrows = someValue.As<Napi::Function>();
136+
Maybe<Napi::Value> maybeResult = jsFunctionThatThrows({ arg1, arg2 });
137+
Napi::Value result;
138+
if (!maybeResult.To(&result)) {
139+
// The Maybe is empty, calling into js failed, cleaning up...
140+
// It is recommended to return an empty Maybe if the procedure failed.
141+
return result;
142+
}
143+
```
144+
145+
If `maybeResult.To(&result)` returns false a JavaScript exception is pending.
146+
To let the exception propagate, the code should generally return immediately
147+
from the native callback, after performing any necessary cleanup.
148+
149+
### Handling a Node-API JS exception
150+
151+
```cpp
152+
Napi::Env env = ...
153+
Napi::Function jsFunctionThatThrows = someValue.As<Napi::Function>();
154+
Maybe<Napi::Value> maybeResult = jsFunctionThatThrows({ arg1, arg2 });
155+
if (maybeResult.IsNothing()) {
156+
Napi::Error e = env.GetAndClearPendingException();
157+
cerr << "Caught JavaScript exception: " + e.Message();
158+
}
159+
```
160+
161+
Since the exception was cleared here, it will not be propagated as a JavaScript
162+
exception after the native callback returns.
163+
99164
<a name="noexceptions"></a>
100165

101166
## Handling Errors Without C++ Exceptions
@@ -127,7 +192,7 @@ immediately from the native callback, after performing any necessary cleanup.
127192
128193
```cpp
129194
Napi::Env env = ...
130-
Napi::Function jsFunctionThatThrows = someObj.As<Napi::Function>();
195+
Napi::Function jsFunctionThatThrows = someValue.As<Napi::Function>();
131196
Napi::Value result = jsFunctionThatThrows({ arg1, arg2 });
132197
if (env.IsExceptionPending()) {
133198
Error e = env.GetAndClearPendingException();
@@ -143,7 +208,7 @@ the native callback, after performing any necessary cleanup.
143208

144209
```cpp
145210
Napi::Env env = ...
146-
Napi::Function jsFunctionThatThrows = someObj.As<Napi::Function>();
211+
Napi::Function jsFunctionThatThrows = someValue.As<Napi::Function>();
147212
Napi::Value result = jsFunctionThatThrows({ arg1, arg2 });
148213
if (env.IsExceptionPending()) {
149214
Napi::Error e = env.GetAndClearPendingException();

doc/maybe.md

Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
# Maybe (template)
2+
3+
Class `Napi::Maybe<T>` represents a value that may be empty: every `Maybe` is
4+
either `Just` and contains a value, or `Nothing`, and does not. `Maybe` types
5+
are very common in node-addon-api code, as they represent that the function may
6+
throw a JavaScript exception and cause the program to be unable to evaluate any
7+
JavaScript code until the exception has been handled.
8+
9+
Typically, the value wrapped in `Napi::Maybe<T>` is [`Napi::Value`] and its
10+
subclasses.
11+
12+
## Methods
13+
14+
### IsNothing
15+
16+
```cpp
17+
template <typename T>
18+
bool Napi::Maybe::IsNothing() const;
19+
```
20+
21+
Returns `true` if the `Maybe` is `Nothing` and does not contain a value, and
22+
`false` otherwise.
23+
24+
### IsJust
25+
26+
```cpp
27+
template <typename T>
28+
bool Napi::Maybe::IsJust() const;
29+
```
30+
31+
Returns `true` if the `Maybe` is `Just` and contains a value, and `false`
32+
otherwise.
33+
34+
### Check
35+
36+
```cpp
37+
template <typename T>
38+
void Napi::Maybe::Check() const;
39+
```
40+
41+
Short-hand for `Maybe::Unwrap()`, which doesn't return a value. Could be used
42+
where the actual value of the Maybe is not needed like `Object::Set`.
43+
If this Maybe is nothing (empty), node-addon-api will crash the
44+
process.
45+
46+
### Unwrap
47+
48+
```cpp
49+
template <typename T>
50+
T Napi::Maybe::Unwrap() const;
51+
```
52+
53+
Return the value of type `T` contained in the Maybe. If this Maybe is
54+
nothing (empty), node-addon-api will crash the process.
55+
56+
### UnwrapOr
57+
58+
```cpp
59+
template <typename T>
60+
T Napi::Maybe::UnwrapOr(const T& default_value) const;
61+
```
62+
63+
Return the value of type T contained in the Maybe, or use a default
64+
value if this Maybe is nothing (empty).
65+
66+
### UnwrapTo
67+
68+
```cpp
69+
template <typename T>
70+
bool Napi::Maybe::UnwrapTo(T* result) const;
71+
```
72+
73+
Converts this Maybe to a value of type `T` in the `out`. If this Maybe is
74+
nothing (empty), `false` is returned and `out` is left untouched.
75+
76+
[`Napi::Value`]: ./value.md

doc/setup.md

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,15 @@ To use **Node-API** in a native module:
5454
```gyp
5555
'defines': [ 'NAPI_DISABLE_CPP_EXCEPTIONS' ],
5656
```
57+
58+
If you decide to use node-addon-api without C++ exceptions enabled, please
59+
consider enabling node-addon-api safe API type guards to ensure the proper
60+
exception handling pattern:
61+
62+
```gyp
63+
'defines': [ 'NODE_ADDON_API_ENABLE_MAYBE' ],
64+
```
65+
5766
4. If you would like your native addon to support OSX, please also add the
5867
following settings in the `binding.gyp` file:
5968

0 commit comments

Comments
 (0)