Skip to content

Commit 9684123

Browse files
committed
src: return Maybe<> on pending exception when cpp exception disabled
1 parent 60348d1 commit 9684123

32 files changed

+1237
-498
lines changed

doc/error_handling.md

Lines changed: 68 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,69 @@ 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 function do not throw a C++ exceptions. Instead, these node-api
107+
functions that calling 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 calling into another node-api (for more info see: [Env](env.md)).
113+
114+
The conversion from `Maybe` boxed values to actual return value is enforced by
115+
compilers so that the exceptions must be properly handled before continuing.
116+
117+
## Examples with Maybe Type and C++ exceptions disabled
118+
119+
### Throwing a JS exception
120+
121+
```cpp
122+
Napi::Env env = ...
123+
Napi::Error::New(env, "Example exception").ThrowAsJavaScriptException();
124+
return;
125+
```
126+
127+
After throwing a JavaScript exception, the code should generally return
128+
immediately from the native callback, after performing any necessary cleanup.
129+
130+
### Propagating a Node-API JS exception
131+
132+
```cpp
133+
Napi::Env env = ...
134+
Napi::Function jsFunctionThatThrows = someValue.As<Napi::Function>();
135+
Maybe<Napi::Value> maybeResult = jsFunctionThatThrows({ arg1, arg2 });
136+
Napi::Value result;
137+
if (!maybeResult.To(&result)) {
138+
// The Maybe is empty, calling into js failed, cleaning up...
139+
// It is recommended to return an empty Maybe if the procedure failed.
140+
return result;
141+
}
142+
```
143+
144+
If `maybeResult.To(&result)` returns false a JavaScript exception is pending.
145+
To let the exception propagate, the code should generally return immediately
146+
from the native callback, after performing any necessary cleanup.
147+
148+
### Handling a Node-API JS exception
149+
150+
```cpp
151+
Napi::Env env = ...
152+
Napi::Function jsFunctionThatThrows = someValue.As<Napi::Function>();
153+
Maybe<Napi::Value> maybeResult = jsFunctionThatThrows({ arg1, arg2 });
154+
if (maybeResult.IsNothing()) {
155+
Napi::Error e = env.GetAndClearPendingException();
156+
cerr << "Caught JavaScript exception: " + e.Message();
157+
}
158+
```
159+
160+
Since the exception was cleared here, it will not be propagated as a JavaScript
161+
exception after the native callback returns.
162+
99163
<a name="noexceptions"></a>
100164

101165
## Handling Errors Without C++ Exceptions
@@ -127,7 +191,7 @@ immediately from the native callback, after performing any necessary cleanup.
127191
128192
```cpp
129193
Napi::Env env = ...
130-
Napi::Function jsFunctionThatThrows = someObj.As<Napi::Function>();
194+
Napi::Function jsFunctionThatThrows = someValue.As<Napi::Function>();
131195
Napi::Value result = jsFunctionThatThrows({ arg1, arg2 });
132196
if (env.IsExceptionPending()) {
133197
Error e = env.GetAndClearPendingException();
@@ -143,7 +207,7 @@ the native callback, after performing any necessary cleanup.
143207

144208
```cpp
145209
Napi::Env env = ...
146-
Napi::Function jsFunctionThatThrows = someObj.As<Napi::Function>();
210+
Napi::Function jsFunctionThatThrows = someValue.As<Napi::Function>();
147211
Napi::Value result = jsFunctionThatThrows({ arg1, arg2 });
148212
if (env.IsExceptionPending()) {
149213
Napi::Error e = env.GetAndClearPendingException();

doc/maybe.md

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