diff --git a/doc/error_handling.md b/doc/error_handling.md
index 50c04a0fe..57de85c9e 100644
--- a/doc/error_handling.md
+++ b/doc/error_handling.md
@@ -17,6 +17,7 @@ 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 With Maybe Type and C++ Exceptions Disabled](#noexceptions-maybe)
- [Handling Errors Without C++ Exceptions](#noexceptions)
@@ -70,7 +71,7 @@ when returning to JavaScript.
### Propagating a Node-API C++ exception
```cpp
-Napi::Function jsFunctionThatThrows = someObj.As();
+Napi::Function jsFunctionThatThrows = someValue.As();
Napi::Value result = jsFunctionThatThrows({ arg1, arg2 });
// other C++ statements
// ...
@@ -84,7 +85,7 @@ a JavaScript exception when returning to JavaScript.
### Handling a Node-API C++ exception
```cpp
-Napi::Function jsFunctionThatThrows = someObj.As();
+Napi::Function jsFunctionThatThrows = someValue.As();
Napi::Value result;
try {
result = jsFunctionThatThrows({ arg1, arg2 });
@@ -96,6 +97,70 @@ try {
Since the exception was caught here, it will not be propagated as a JavaScript
exception.
+
+
+## Handling Errors With Maybe Type and C++ Exceptions Disabled
+
+If C++ exceptions are disabled (for more info see: [Setup](setup.md)), then the
+`Napi::Error` class does not extend `std::exception`. This means that any calls to
+node-addon-api functions do not throw a C++ exceptions. Instead, these node-api
+functions that call into JavaScript are returning with `Maybe` boxed values.
+In that case, the calling side should convert the `Maybe` boxed values with
+checks to ensure that the call did succeed and therefore no exception is pending.
+If the check fails, that is to say, the returning value is _empty_, the calling
+side should determine what to do with `env.GetAndClearPendingException()` before
+attempting to call another node-api (for more info see: [Env](env.md)).
+
+The conversion from the `Maybe` boxed value to the actual return value is
+enforced by compilers so that the exceptions must be properly handled before
+continuing.
+
+## Examples with Maybe Type and C++ exceptions disabled
+
+### Throwing a JS exception
+
+```cpp
+Napi::Env env = ...
+Napi::Error::New(env, "Example exception").ThrowAsJavaScriptException();
+return;
+```
+
+After throwing a JavaScript exception, the code should generally return
+immediately from the native callback, after performing any necessary cleanup.
+
+### Propagating a Node-API JS exception
+
+```cpp
+Napi::Env env = ...
+Napi::Function jsFunctionThatThrows = someValue.As();
+Maybe maybeResult = jsFunctionThatThrows({ arg1, arg2 });
+Napi::Value result;
+if (!maybeResult.To(&result)) {
+ // The Maybe is empty, calling into js failed, cleaning up...
+ // It is recommended to return an empty Maybe if the procedure failed.
+ return result;
+}
+```
+
+If `maybeResult.To(&result)` returns false 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 Node-API JS exception
+
+```cpp
+Napi::Env env = ...
+Napi::Function jsFunctionThatThrows = someValue.As();
+Maybe maybeResult = jsFunctionThatThrows({ arg1, arg2 });
+if (maybeResult.IsNothing()) {
+ 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.
+
## Handling Errors Without C++ Exceptions
@@ -127,7 +192,7 @@ immediately from the native callback, after performing any necessary cleanup.
```cpp
Napi::Env env = ...
-Napi::Function jsFunctionThatThrows = someObj.As();
+Napi::Function jsFunctionThatThrows = someValue.As();
Napi::Value result = jsFunctionThatThrows({ arg1, arg2 });
if (env.IsExceptionPending()) {
Error e = env.GetAndClearPendingException();
@@ -143,7 +208,7 @@ the native callback, after performing any necessary cleanup.
```cpp
Napi::Env env = ...
-Napi::Function jsFunctionThatThrows = someObj.As();
+Napi::Function jsFunctionThatThrows = someValue.As();
Napi::Value result = jsFunctionThatThrows({ arg1, arg2 });
if (env.IsExceptionPending()) {
Napi::Error e = env.GetAndClearPendingException();
diff --git a/doc/maybe.md b/doc/maybe.md
new file mode 100644
index 000000000..dc71c0750
--- /dev/null
+++ b/doc/maybe.md
@@ -0,0 +1,76 @@
+# Maybe (template)
+
+Class `Napi::Maybe` represents a value that may be empty: every `Maybe` is
+either `Just` and contains a value, or `Nothing`, and does not. `Maybe` types
+are very common in node-addon-api code, as they represent that the function may
+throw a JavaScript exception and cause the program to be unable to evaluate any
+JavaScript code until the exception has been handled.
+
+Typically, the value wrapped in `Napi::Maybe` is [`Napi::Value`] and its
+subclasses.
+
+## Methods
+
+### IsNothing
+
+```cpp
+template
+bool Napi::Maybe::IsNothing() const;
+```
+
+Returns `true` if the `Maybe` is `Nothing` and does not contain a value, and
+`false` otherwise.
+
+### IsJust
+
+```cpp
+template
+bool Napi::Maybe::IsJust() const;
+```
+
+Returns `true` if the `Maybe` is `Just` and contains a value, and `false`
+otherwise.
+
+### Check
+
+```cpp
+template
+void Napi::Maybe::Check() const;
+```
+
+Short-hand for `Maybe::Unwrap()`, which doesn't return a value. Could be used
+where the actual value of the Maybe is not needed like `Object::Set`.
+If this Maybe is nothing (empty), node-addon-api will crash the
+process.
+
+### Unwrap
+
+```cpp
+template
+T Napi::Maybe::Unwrap() const;
+```
+
+Return the value of type `T` contained in the Maybe. If this Maybe is
+nothing (empty), node-addon-api will crash the process.
+
+### UnwrapOr
+
+```cpp
+template
+T Napi::Maybe::UnwrapOr(const T& default_value) const;
+```
+
+Return the value of type T contained in the Maybe, or use a default
+value if this Maybe is nothing (empty).
+
+### UnwrapTo
+
+```cpp
+template
+bool Napi::Maybe::UnwrapTo(T* result) const;
+```
+
+Converts this Maybe to a value of type `T` in the `out`. If this Maybe is
+nothing (empty), `false` is returned and `out` is left untouched.
+
+[`Napi::Value`]: ./value.md
diff --git a/doc/setup.md b/doc/setup.md
index aec397e61..5db3452ad 100644
--- a/doc/setup.md
+++ b/doc/setup.md
@@ -54,6 +54,15 @@ To use **Node-API** in a native module:
```gyp
'defines': [ 'NAPI_DISABLE_CPP_EXCEPTIONS' ],
```
+
+ If you decide to use node-addon-api without C++ exceptions enabled, please
+ consider enabling node-addon-api safe API type guards to ensure the proper
+ exception handling pattern:
+
+```gyp
+ 'defines': [ 'NODE_ADDON_API_ENABLE_MAYBE' ],
+```
+
4. If you would like your native addon to support OSX, please also add the
following settings in the `binding.gyp` file:
diff --git a/napi-inl.h b/napi-inl.h
index cc02af3ac..a1d67ac17 100644
--- a/napi-inl.h
+++ b/napi-inl.h
@@ -377,6 +377,72 @@ inline napi_value RegisterModule(napi_env env,
});
}
+////////////////////////////////////////////////////////////////////////////////
+// Maybe class
+////////////////////////////////////////////////////////////////////////////////
+
+template
+bool Maybe::IsNothing() const {
+ return !_has_value;
+}
+
+template
+bool Maybe::IsJust() const {
+ return _has_value;
+}
+
+template
+void Maybe::Check() const {
+ NAPI_CHECK(IsJust(), "Napi::Maybe::Check", "Maybe value is Nothing.");
+}
+
+template
+T Maybe::Unwrap() const {
+ NAPI_CHECK(IsJust(), "Napi::Maybe::Unwrap", "Maybe value is Nothing.");
+ return _value;
+}
+
+template
+T Maybe::UnwrapOr(const T& default_value) const {
+ return _has_value ? _value : default_value;
+}
+
+template
+bool Maybe::UnwrapTo(T* out) const {
+ if (IsJust()) {
+ *out = _value;
+ return true;
+ };
+ return false;
+}
+
+template
+bool Maybe::operator==(const Maybe& other) const {
+ return (IsJust() == other.IsJust()) &&
+ (!IsJust() || Unwrap() == other.Unwrap());
+}
+
+template
+bool Maybe::operator!=(const Maybe& other) const {
+ return !operator==(other);
+}
+
+template
+Maybe::Maybe() : _has_value(false) {}
+
+template
+Maybe::Maybe(const T& t) : _has_value(true), _value(t) {}
+
+template
+inline Maybe Nothing() {
+ return Maybe();
+}
+
+template
+inline Maybe Just(const T& t) {
+ return Maybe(t);
+}
+
////////////////////////////////////////////////////////////////////////////////
// Env class
////////////////////////////////////////////////////////////////////////////////
@@ -426,20 +492,20 @@ inline Error Env::GetAndClearPendingException() {
return Error(_env, value);
}
-inline Value Env::RunScript(const char* utf8script) {
+inline MaybeOrValue Env::RunScript(const char* utf8script) {
String script = String::New(_env, utf8script);
return RunScript(script);
}
-inline Value Env::RunScript(const std::string& utf8script) {
+inline MaybeOrValue Env::RunScript(const std::string& utf8script) {
return RunScript(utf8script.c_str());
}
-inline Value Env::RunScript(String script) {
+inline MaybeOrValue Env::RunScript(String script) {
napi_value result;
napi_status status = napi_run_script(_env, script, &result);
- NAPI_THROW_IF_FAILED(_env, status, Undefined());
- return Value(_env, result);
+ NAPI_RETURN_OR_THROW_IF_FAILED(
+ _env, status, Napi::Value(_env, result), Napi::Value);
}
#if NAPI_VERSION > 2
@@ -678,32 +744,32 @@ inline T Value::As() const {
return T(_env, _value);
}
-inline Boolean Value::ToBoolean() const {
+inline MaybeOrValue Value::ToBoolean() const {
napi_value result;
napi_status status = napi_coerce_to_bool(_env, _value, &result);
- NAPI_THROW_IF_FAILED(_env, status, Boolean());
- return Boolean(_env, result);
+ NAPI_RETURN_OR_THROW_IF_FAILED(
+ _env, status, Napi::Boolean(_env, result), Napi::Boolean);
}
-inline Number Value::ToNumber() const {
+inline MaybeOrValue Value::ToNumber() const {
napi_value result;
napi_status status = napi_coerce_to_number(_env, _value, &result);
- NAPI_THROW_IF_FAILED(_env, status, Number());
- return Number(_env, result);
+ NAPI_RETURN_OR_THROW_IF_FAILED(
+ _env, status, Napi::Number(_env, result), Napi::Number);
}
-inline String Value::ToString() const {
+inline MaybeOrValue Value::ToString() const {
napi_value result;
napi_status status = napi_coerce_to_string(_env, _value, &result);
- NAPI_THROW_IF_FAILED(_env, status, String());
- return String(_env, result);
+ NAPI_RETURN_OR_THROW_IF_FAILED(
+ _env, status, Napi::String(_env, result), Napi::String);
}
-inline Object Value::ToObject() const {
+inline MaybeOrValue Value::ToObject() const {
napi_value result;
napi_status status = napi_coerce_to_object(_env, _value, &result);
- NAPI_THROW_IF_FAILED(_env, status, Object());
- return Object(_env, result);
+ NAPI_RETURN_OR_THROW_IF_FAILED(
+ _env, status, Napi::Object(_env, result), Napi::Object);
}
////////////////////////////////////////////////////////////////////////////////
@@ -1026,29 +1092,56 @@ inline Symbol Symbol::New(napi_env env, napi_value description) {
return Symbol(env, value);
}
-inline Symbol Symbol::WellKnown(napi_env env, const std::string& name) {
+inline MaybeOrValue Symbol::WellKnown(napi_env env,
+ const std::string& name) {
+#if defined(NODE_ADDON_API_ENABLE_MAYBE)
+ Value symbol_obj;
+ Value symbol_value;
+ if (Napi::Env(env).Global().Get("Symbol").UnwrapTo(&symbol_obj) &&
+ symbol_obj.As().Get(name).UnwrapTo(&symbol_value)) {
+ return Just(symbol_value.As());
+ }
+ return Nothing();
+#else
return Napi::Env(env).Global().Get("Symbol").As().Get(name).As();
+#endif
}
-inline Symbol Symbol::For(napi_env env, const std::string& description) {
+inline MaybeOrValue Symbol::For(napi_env env,
+ const std::string& description) {
napi_value descriptionValue = String::New(env, description);
return Symbol::For(env, descriptionValue);
}
-inline Symbol Symbol::For(napi_env env, const char* description) {
+inline MaybeOrValue Symbol::For(napi_env env, const char* description) {
napi_value descriptionValue = String::New(env, description);
return Symbol::For(env, descriptionValue);
}
-inline Symbol Symbol::For(napi_env env, String description) {
+inline MaybeOrValue Symbol::For(napi_env env, String description) {
return Symbol::For(env, static_cast(description));
}
-inline Symbol Symbol::For(napi_env env, napi_value description) {
- Object symbObject = Napi::Env(env).Global().Get("Symbol").As();
- auto forSymb =
- symbObject.Get("for").As().Call(symbObject, {description});
- return forSymb.As();
+inline MaybeOrValue Symbol::For(napi_env env, napi_value description) {
+#if defined(NODE_ADDON_API_ENABLE_MAYBE)
+ Value symbol_obj;
+ Value symbol_for_value;
+ Value symbol_value;
+ if (Napi::Env(env).Global().Get("Symbol").UnwrapTo(&symbol_obj) &&
+ symbol_obj.As().Get("for").UnwrapTo(&symbol_for_value) &&
+ symbol_for_value.As()
+ .Call(symbol_obj, {description})
+ .UnwrapTo(&symbol_value)) {
+ return Just(symbol_value.As());
+ }
+ return Nothing();
+#else
+ Object symbol_obj = Napi::Env(env).Global().Get("Symbol").As();
+ return symbol_obj.Get("for")
+ .As()
+ .Call(symbol_obj, {description})
+ .As();
+#endif
}
inline Symbol::Symbol() : Name() {
@@ -1163,12 +1256,23 @@ String String::From(napi_env env, const T& value) {
template
inline Object::PropertyLValue::operator Value() const {
- return Object(_env, _object).Get(_key);
+ MaybeOrValue val = Object(_env, _object).Get(_key);
+#ifdef NODE_ADDON_API_ENABLE_MAYBE
+ return val.Unwrap();
+#else
+ return val;
+#endif
}
template template
inline Object::PropertyLValue& Object::PropertyLValue::operator =(ValueType value) {
- Object(_env, _object).Set(_key, value);
+#ifdef NODE_ADDON_API_ENABLE_MAYBE
+ MaybeOrValue result =
+#endif
+ Object(_env, _object).Set(_key, value);
+#ifdef NODE_ADDON_API_ENABLE_MAYBE
+ result.Unwrap();
+#endif
return *this;
}
@@ -1201,208 +1305,192 @@ inline Object::PropertyLValue Object::operator [](uint32_t index) {
return PropertyLValue(*this, index);
}
-inline Value Object::operator [](const char* utf8name) const {
+inline MaybeOrValue Object::operator[](const char* utf8name) const {
return Get(utf8name);
}
-inline Value Object::operator [](const std::string& utf8name) const {
+inline MaybeOrValue Object::operator[](
+ const std::string& utf8name) const {
return Get(utf8name);
}
-inline Value Object::operator [](uint32_t index) const {
+inline MaybeOrValue Object::operator[](uint32_t index) const {
return Get(index);
}
-inline bool Object::Has(napi_value key) const {
+inline MaybeOrValue Object::Has(napi_value key) const {
bool result;
napi_status status = napi_has_property(_env, _value, key, &result);
- NAPI_THROW_IF_FAILED(_env, status, false);
- return result;
+ NAPI_RETURN_OR_THROW_IF_FAILED(_env, status, result, bool);
}
-inline bool Object::Has(Value key) const {
+inline MaybeOrValue Object::Has(Value key) const {
bool result;
napi_status status = napi_has_property(_env, _value, key, &result);
- NAPI_THROW_IF_FAILED(_env, status, false);
- return result;
+ NAPI_RETURN_OR_THROW_IF_FAILED(_env, status, result, bool);
}
-inline bool Object::Has(const char* utf8name) const {
+inline MaybeOrValue Object::Has(const char* utf8name) const {
bool result;
napi_status status = napi_has_named_property(_env, _value, utf8name, &result);
- NAPI_THROW_IF_FAILED(_env, status, false);
- return result;
+ NAPI_RETURN_OR_THROW_IF_FAILED(_env, status, result, bool);
}
-inline bool Object::Has(const std::string& utf8name) const {
+inline MaybeOrValue Object::Has(const std::string& utf8name) const {
return Has(utf8name.c_str());
}
-inline bool Object::HasOwnProperty(napi_value key) const {
+inline MaybeOrValue Object::HasOwnProperty(napi_value key) const {
bool result;
napi_status status = napi_has_own_property(_env, _value, key, &result);
- NAPI_THROW_IF_FAILED(_env, status, false);
- return result;
+ NAPI_RETURN_OR_THROW_IF_FAILED(_env, status, result, bool);
}
-inline bool Object::HasOwnProperty(Value key) const {
+inline MaybeOrValue Object::HasOwnProperty(Value key) const {
bool result;
napi_status status = napi_has_own_property(_env, _value, key, &result);
- NAPI_THROW_IF_FAILED(_env, status, false);
- return result;
+ NAPI_RETURN_OR_THROW_IF_FAILED(_env, status, result, bool);
}
-inline bool Object::HasOwnProperty(const char* utf8name) const {
+inline MaybeOrValue Object::HasOwnProperty(const char* utf8name) const {
napi_value key;
napi_status status = napi_create_string_utf8(_env, utf8name, std::strlen(utf8name), &key);
- NAPI_THROW_IF_FAILED(_env, status, false);
+ NAPI_MAYBE_THROW_IF_FAILED(_env, status, bool);
return HasOwnProperty(key);
}
-inline bool Object::HasOwnProperty(const std::string& utf8name) const {
+inline MaybeOrValue Object::HasOwnProperty(
+ const std::string& utf8name) const {
return HasOwnProperty(utf8name.c_str());
}
-inline Value Object::Get(napi_value key) const {
+inline MaybeOrValue Object::Get(napi_value key) const {
napi_value result;
napi_status status = napi_get_property(_env, _value, key, &result);
- NAPI_THROW_IF_FAILED(_env, status, Value());
- return Value(_env, result);
+ NAPI_RETURN_OR_THROW_IF_FAILED(_env, status, Value(_env, result), Value);
}
-inline Value Object::Get(Value key) const {
+inline MaybeOrValue Object::Get(Value key) const {
napi_value result;
napi_status status = napi_get_property(_env, _value, key, &result);
- NAPI_THROW_IF_FAILED(_env, status, Value());
- return Value(_env, result);
+ NAPI_RETURN_OR_THROW_IF_FAILED(_env, status, Value(_env, result), Value);
}
-inline Value Object::Get(const char* utf8name) const {
+inline MaybeOrValue Object::Get(const char* utf8name) const {
napi_value result;
napi_status status = napi_get_named_property(_env, _value, utf8name, &result);
- NAPI_THROW_IF_FAILED(_env, status, Value());
- return Value(_env, result);
+ NAPI_RETURN_OR_THROW_IF_FAILED(_env, status, Value(_env, result), Value);
}
-inline Value Object::Get(const std::string& utf8name) const {
+inline MaybeOrValue Object::Get(const std::string& utf8name) const {
return Get(utf8name.c_str());
}
template
-inline bool Object::Set(napi_value key, const ValueType& value) {
+inline MaybeOrValue Object::Set(napi_value key, const ValueType& value) {
napi_status status =
napi_set_property(_env, _value, key, Value::From(_env, value));
- NAPI_THROW_IF_FAILED(_env, status, false);
- return true;
+ NAPI_RETURN_OR_THROW_IF_FAILED(_env, status, status == napi_ok, bool);
}
template
-inline bool Object::Set(Value key, const ValueType& value) {
+inline MaybeOrValue Object::Set(Value key, const ValueType& value) {
napi_status status =
napi_set_property(_env, _value, key, Value::From(_env, value));
- NAPI_THROW_IF_FAILED(_env, status, false);
- return true;
+ NAPI_RETURN_OR_THROW_IF_FAILED(_env, status, status == napi_ok, bool);
}
template
-inline bool Object::Set(const char* utf8name, const ValueType& value) {
+inline MaybeOrValue Object::Set(const char* utf8name,
+ const ValueType& value) {
napi_status status =
napi_set_named_property(_env, _value, utf8name, Value::From(_env, value));
- NAPI_THROW_IF_FAILED(_env, status, false);
- return true;
+ NAPI_RETURN_OR_THROW_IF_FAILED(_env, status, status == napi_ok, bool);
}
template
-inline bool Object::Set(const std::string& utf8name, const ValueType& value) {
+inline MaybeOrValue Object::Set(const std::string& utf8name,
+ const ValueType& value) {
return Set(utf8name.c_str(), value);
}
-inline bool Object::Delete(napi_value key) {
+inline MaybeOrValue Object::Delete(napi_value key) {
bool result;
napi_status status = napi_delete_property(_env, _value, key, &result);
- NAPI_THROW_IF_FAILED(_env, status, false);
- return result;
+ NAPI_RETURN_OR_THROW_IF_FAILED(_env, status, result, bool);
}
-inline bool Object::Delete(Value key) {
+inline MaybeOrValue Object::Delete(Value key) {
bool result;
napi_status status = napi_delete_property(_env, _value, key, &result);
- NAPI_THROW_IF_FAILED(_env, status, false);
- return result;
+ NAPI_RETURN_OR_THROW_IF_FAILED(_env, status, result, bool);
}
-inline bool Object::Delete(const char* utf8name) {
+inline MaybeOrValue Object::Delete(const char* utf8name) {
return Delete(String::New(_env, utf8name));
}
-inline bool Object::Delete(const std::string& utf8name) {
+inline MaybeOrValue Object::Delete(const std::string& utf8name) {
return Delete(String::New(_env, utf8name));
}
-inline bool Object::Has(uint32_t index) const {
+inline MaybeOrValue Object::Has(uint32_t index) const {
bool result;
napi_status status = napi_has_element(_env, _value, index, &result);
- NAPI_THROW_IF_FAILED(_env, status, false);
- return result;
+ NAPI_RETURN_OR_THROW_IF_FAILED(_env, status, result, bool);
}
-inline Value Object::Get(uint32_t index) const {
+inline MaybeOrValue Object::Get(uint32_t index) const {
napi_value value;
napi_status status = napi_get_element(_env, _value, index, &value);
- NAPI_THROW_IF_FAILED(_env, status, Value());
- return Value(_env, value);
+ NAPI_RETURN_OR_THROW_IF_FAILED(_env, status, Value(_env, value), Value);
}
template
-inline bool Object::Set(uint32_t index, const ValueType& value) {
+inline MaybeOrValue Object::Set(uint32_t index, const ValueType& value) {
napi_status status =
napi_set_element(_env, _value, index, Value::From(_env, value));
- NAPI_THROW_IF_FAILED(_env, status, false);
- return true;
+ NAPI_RETURN_OR_THROW_IF_FAILED(_env, status, status == napi_ok, bool);
}
-inline bool Object::Delete(uint32_t index) {
+inline MaybeOrValue Object::Delete(uint32_t index) {
bool result;
napi_status status = napi_delete_element(_env, _value, index, &result);
- NAPI_THROW_IF_FAILED(_env, status, false);
- return result;
+ NAPI_RETURN_OR_THROW_IF_FAILED(_env, status, result, bool);
}
-inline Array Object::GetPropertyNames() const {
+inline MaybeOrValue Object::GetPropertyNames() const {
napi_value result;
napi_status status = napi_get_property_names(_env, _value, &result);
- NAPI_THROW_IF_FAILED(_env, status, Array());
- return Array(_env, result);
+ NAPI_RETURN_OR_THROW_IF_FAILED(_env, status, Array(_env, result), Array);
}
-inline bool Object::DefineProperty(const PropertyDescriptor& property) {
+inline MaybeOrValue Object::DefineProperty(
+ const PropertyDescriptor& property) {
napi_status status = napi_define_properties(_env, _value, 1,
reinterpret_cast(&property));
- NAPI_THROW_IF_FAILED(_env, status, false);
- return true;
+ NAPI_RETURN_OR_THROW_IF_FAILED(_env, status, status == napi_ok, bool);
}
-inline bool Object::DefineProperties(
+inline MaybeOrValue Object::DefineProperties(
const std::initializer_list& properties) {
napi_status status = napi_define_properties(_env, _value, properties.size(),
reinterpret_cast(properties.begin()));
- NAPI_THROW_IF_FAILED(_env, status, false);
- return true;
+ NAPI_RETURN_OR_THROW_IF_FAILED(_env, status, status == napi_ok, bool);
}
-inline bool Object::DefineProperties(
+inline MaybeOrValue Object::DefineProperties(
const std::vector& properties) {
napi_status status = napi_define_properties(_env, _value, properties.size(),
reinterpret_cast(properties.data()));
- NAPI_THROW_IF_FAILED(_env, status, false);
- return true;
+ NAPI_RETURN_OR_THROW_IF_FAILED(_env, status, status == napi_ok, bool);
}
-inline bool Object::InstanceOf(const Function& constructor) const {
+inline MaybeOrValue Object::InstanceOf(
+ const Function& constructor) const {
bool result;
napi_status status = napi_instanceof(_env, _value, constructor, &result);
- NAPI_THROW_IF_FAILED(_env, status, false);
- return result;
+ NAPI_RETURN_OR_THROW_IF_FAILED(_env, status, result, bool);
}
template
@@ -1442,16 +1530,14 @@ inline void Object::AddFinalizer(Finalizer finalizeCallback,
}
#if NAPI_VERSION >= 8
-inline bool Object::Freeze() {
+inline MaybeOrValue Object::Freeze() {
napi_status status = napi_object_freeze(_env, _value);
- NAPI_THROW_IF_FAILED(_env, status, false);
- return true;
+ NAPI_RETURN_OR_THROW_IF_FAILED(_env, status, status == napi_ok, bool);
}
-inline bool Object::Seal() {
+inline MaybeOrValue Object::Seal() {
napi_status status = napi_object_seal(_env, _value);
- NAPI_THROW_IF_FAILED(_env, status, false);
- return true;
+ NAPI_RETURN_OR_THROW_IF_FAILED(_env, status, status == napi_ok, bool);
}
#endif // NAPI_VERSION >= 8
@@ -2098,53 +2184,61 @@ inline Function::Function() : Object() {
inline Function::Function(napi_env env, napi_value value) : Object(env, value) {
}
-inline Value Function::operator ()(const std::initializer_list& args) const {
+inline MaybeOrValue Function::operator()(
+ const std::initializer_list& args) const {
return Call(Env().Undefined(), args);
}
-inline Value Function::Call(const std::initializer_list& args) const {
+inline MaybeOrValue Function::Call(
+ const std::initializer_list& args) const {
return Call(Env().Undefined(), args);
}
-inline Value Function::Call(const std::vector& args) const {
+inline MaybeOrValue Function::Call(
+ const std::vector& args) const {
return Call(Env().Undefined(), args);
}
-inline Value Function::Call(size_t argc, const napi_value* args) const {
+inline MaybeOrValue Function::Call(size_t argc,
+ const napi_value* args) const {
return Call(Env().Undefined(), argc, args);
}
-inline Value Function::Call(napi_value recv, const std::initializer_list& args) const {
+inline MaybeOrValue Function::Call(
+ napi_value recv, const std::initializer_list& args) const {
return Call(recv, args.size(), args.begin());
}
-inline Value Function::Call(napi_value recv, const std::vector& args) const {
+inline MaybeOrValue Function::Call(
+ napi_value recv, const std::vector& args) const {
return Call(recv, args.size(), args.data());
}
-inline Value Function::Call(napi_value recv, size_t argc, const napi_value* args) const {
+inline MaybeOrValue Function::Call(napi_value recv,
+ size_t argc,
+ const napi_value* args) const {
napi_value result;
napi_status status = napi_call_function(
_env, recv, _value, argc, args, &result);
- NAPI_THROW_IF_FAILED(_env, status, Value());
- return Value(_env, result);
+ NAPI_RETURN_OR_THROW_IF_FAILED(
+ _env, status, Napi::Value(_env, result), Napi::Value);
}
-inline Value Function::MakeCallback(
+inline MaybeOrValue Function::MakeCallback(
napi_value recv,
const std::initializer_list& args,
napi_async_context context) const {
return MakeCallback(recv, args.size(), args.begin(), context);
}
-inline Value Function::MakeCallback(
+inline MaybeOrValue Function::MakeCallback(
napi_value recv,
const std::vector& args,
napi_async_context context) const {
return MakeCallback(recv, args.size(), args.data(), context);
}
-inline Value Function::MakeCallback(
+inline MaybeOrValue Function::MakeCallback(
napi_value recv,
size_t argc,
const napi_value* args,
@@ -2152,24 +2246,27 @@ inline Value Function::MakeCallback(
napi_value result;
napi_status status = napi_make_callback(
_env, context, recv, _value, argc, args, &result);
- NAPI_THROW_IF_FAILED(_env, status, Value());
- return Value(_env, result);
+ NAPI_RETURN_OR_THROW_IF_FAILED(
+ _env, status, Napi::Value(_env, result), Napi::Value);
}
-inline Object Function::New(const std::initializer_list& args) const {
+inline MaybeOrValue Function::New(
+ const std::initializer_list& args) const {
return New(args.size(), args.begin());
}
-inline Object Function::New(const std::vector& args) const {
+inline MaybeOrValue Function::New(
+ const std::vector& args) const {
return New(args.size(), args.data());
}
-inline Object Function::New(size_t argc, const napi_value* args) const {
+inline MaybeOrValue Function::New(size_t argc,
+ const napi_value* args) const {
napi_value result;
napi_status status = napi_new_instance(
_env, _value, argc, args, &result);
- NAPI_THROW_IF_FAILED(_env, status, Object());
- return Object(_env, result);
+ NAPI_RETURN_OR_THROW_IF_FAILED(
+ _env, status, Napi::Object(_env, result), Napi::Object);
}
////////////////////////////////////////////////////////////////////////////////
@@ -2441,7 +2538,14 @@ inline const std::string& Error::Message() const NAPI_NOEXCEPT {
// the std::string::operator=, because this method may not throw.
}
#else // NAPI_CPP_EXCEPTIONS
+#if defined(NODE_ADDON_API_ENABLE_MAYBE)
+ Napi::Value message_val;
+ if (Get("message").UnwrapTo(&message_val)) {
+ _message = message_val.As();
+ }
+#else
_message = Get("message").As();
+#endif
#endif // NAPI_CPP_EXCEPTIONS
}
return _message;
@@ -2758,101 +2862,147 @@ inline ObjectReference::ObjectReference(const ObjectReference& other)
: Reference(other) {
}
-inline Napi::Value ObjectReference::Get(const char* utf8name) const {
+inline MaybeOrValue ObjectReference::Get(
+ const char* utf8name) const {
EscapableHandleScope scope(_env);
- return scope.Escape(Value().Get(utf8name));
+ MaybeOrValue result = Value().Get(utf8name);
+#ifdef NODE_ADDON_API_ENABLE_MAYBE
+ if (result.IsJust()) {
+ return Just(scope.Escape(result.Unwrap()));
+ }
+ return result;
+#else
+ if (scope.Env().IsExceptionPending()) {
+ return Value();
+ }
+ return scope.Escape(result);
+#endif
}
-inline Napi::Value ObjectReference::Get(const std::string& utf8name) const {
+inline MaybeOrValue ObjectReference::Get(
+ const std::string& utf8name) const {
EscapableHandleScope scope(_env);
- return scope.Escape(Value().Get(utf8name));
+ MaybeOrValue result = Value().Get(utf8name);
+#ifdef NODE_ADDON_API_ENABLE_MAYBE
+ if (result.IsJust()) {
+ return Just(scope.Escape(result.Unwrap()));
+ }
+ return result;
+#else
+ if (scope.Env().IsExceptionPending()) {
+ return Value();
+ }
+ return scope.Escape(result);
+#endif
}
-inline bool ObjectReference::Set(const char* utf8name, napi_value value) {
+inline MaybeOrValue ObjectReference::Set(const char* utf8name,
+ napi_value value) {
HandleScope scope(_env);
return Value().Set(utf8name, value);
}
-inline bool ObjectReference::Set(const char* utf8name, Napi::Value value) {
+inline MaybeOrValue ObjectReference::Set(const char* utf8name,
+ Napi::Value value) {
HandleScope scope(_env);
return Value().Set(utf8name, value);
}
-inline bool ObjectReference::Set(const char* utf8name, const char* utf8value) {
+inline MaybeOrValue ObjectReference::Set(const char* utf8name,
+ const char* utf8value) {
HandleScope scope(_env);
return Value().Set(utf8name, utf8value);
}
-inline bool ObjectReference::Set(const char* utf8name, bool boolValue) {
+inline MaybeOrValue ObjectReference::Set(const char* utf8name,
+ bool boolValue) {
HandleScope scope(_env);
return Value().Set(utf8name, boolValue);
}
-inline bool ObjectReference::Set(const char* utf8name, double numberValue) {
+inline MaybeOrValue ObjectReference::Set(const char* utf8name,
+ double numberValue) {
HandleScope scope(_env);
return Value().Set(utf8name, numberValue);
}
-inline bool ObjectReference::Set(const std::string& utf8name,
- napi_value value) {
+inline MaybeOrValue ObjectReference::Set(const std::string& utf8name,
+ napi_value value) {
HandleScope scope(_env);
return Value().Set(utf8name, value);
}
-inline bool ObjectReference::Set(const std::string& utf8name,
- Napi::Value value) {
+inline MaybeOrValue ObjectReference::Set(const std::string& utf8name,
+ Napi::Value value) {
HandleScope scope(_env);
return Value().Set(utf8name, value);
}
-inline bool ObjectReference::Set(const std::string& utf8name,
- std::string& utf8value) {
+inline MaybeOrValue ObjectReference::Set(const std::string& utf8name,
+ std::string& utf8value) {
HandleScope scope(_env);
return Value().Set(utf8name, utf8value);
}
-inline bool ObjectReference::Set(const std::string& utf8name, bool boolValue) {
+inline MaybeOrValue ObjectReference::Set(const std::string& utf8name,
+ bool boolValue) {
HandleScope scope(_env);
return Value().Set(utf8name, boolValue);
}
-inline bool ObjectReference::Set(const std::string& utf8name,
- double numberValue) {
+inline MaybeOrValue ObjectReference::Set(const std::string& utf8name,
+ double numberValue) {
HandleScope scope(_env);
return Value().Set(utf8name, numberValue);
}
-inline Napi::Value ObjectReference::Get(uint32_t index) const {
+inline MaybeOrValue ObjectReference::Get(uint32_t index) const {
EscapableHandleScope scope(_env);
- return scope.Escape(Value().Get(index));
+ MaybeOrValue result = Value().Get(index);
+#ifdef NODE_ADDON_API_ENABLE_MAYBE
+ if (result.IsJust()) {
+ return Just(scope.Escape(result.Unwrap()));
+ }
+ return result;
+#else
+ if (scope.Env().IsExceptionPending()) {
+ return Value();
+ }
+ return scope.Escape(result);
+#endif
}
-inline bool ObjectReference::Set(uint32_t index, napi_value value) {
+inline MaybeOrValue ObjectReference::Set(uint32_t index,
+ napi_value value) {
HandleScope scope(_env);
return Value().Set(index, value);
}
-inline bool ObjectReference::Set(uint32_t index, Napi::Value value) {
+inline MaybeOrValue ObjectReference::Set(uint32_t index,
+ Napi::Value value) {
HandleScope scope(_env);
return Value().Set(index, value);
}
-inline bool ObjectReference::Set(uint32_t index, const char* utf8value) {
+inline MaybeOrValue ObjectReference::Set(uint32_t index,
+ const char* utf8value) {
HandleScope scope(_env);
return Value().Set(index, utf8value);
}
-inline bool ObjectReference::Set(uint32_t index, const std::string& utf8value) {
+inline MaybeOrValue ObjectReference::Set(uint32_t index,
+ const std::string& utf8value) {
HandleScope scope(_env);
return Value().Set(index, utf8value);
}
-inline bool ObjectReference::Set(uint32_t index, bool boolValue) {
+inline MaybeOrValue ObjectReference::Set(uint32_t index, bool boolValue) {
HandleScope scope(_env);
return Value().Set(index, boolValue);
}
-inline bool ObjectReference::Set(uint32_t index, double numberValue) {
+inline MaybeOrValue ObjectReference::Set(uint32_t index,
+ double numberValue) {
HandleScope scope(_env);
return Value().Set(index, numberValue);
}
@@ -2886,105 +3036,200 @@ inline FunctionReference& FunctionReference::operator =(FunctionReference&& othe
return *this;
}
-inline Napi::Value FunctionReference::operator ()(
+inline MaybeOrValue FunctionReference::operator()(
const std::initializer_list& args) const {
EscapableHandleScope scope(_env);
- return scope.Escape(Value()(args));
+ MaybeOrValue result = Value()(args);
+#ifdef NODE_ADDON_API_ENABLE_MAYBE
+ if (result.IsJust()) {
+ return Just(scope.Escape(result.Unwrap()));
+ }
+ return result;
+#else
+ if (scope.Env().IsExceptionPending()) {
+ return Value();
+ }
+ return scope.Escape(result);
+#endif
}
-inline Napi::Value FunctionReference::Call(const std::initializer_list& args) const {
+inline MaybeOrValue FunctionReference::Call(
+ const std::initializer_list& args) const {
EscapableHandleScope scope(_env);
- Napi::Value result = Value().Call(args);
+ MaybeOrValue result = Value().Call(args);
+#ifdef NODE_ADDON_API_ENABLE_MAYBE
+ if (result.IsJust()) {
+ return Just(scope.Escape(result.Unwrap()));
+ }
+ return result;
+#else
if (scope.Env().IsExceptionPending()) {
return Value();
}
return scope.Escape(result);
+#endif
}
-inline Napi::Value FunctionReference::Call(const std::vector& args) const {
+inline MaybeOrValue FunctionReference::Call(
+ const std::vector& args) const {
EscapableHandleScope scope(_env);
- Napi::Value result = Value().Call(args);
+ MaybeOrValue result = Value().Call(args);
+#ifdef NODE_ADDON_API_ENABLE_MAYBE
+ if (result.IsJust()) {
+ return Just(scope.Escape(result.Unwrap()));
+ }
+ return result;
+#else
if (scope.Env().IsExceptionPending()) {
return Value();
}
return scope.Escape(result);
+#endif
}
-inline Napi::Value FunctionReference::Call(
+inline MaybeOrValue FunctionReference::Call(
napi_value recv, const std::initializer_list& args) const {
EscapableHandleScope scope(_env);
- Napi::Value result = Value().Call(recv, args);
+ MaybeOrValue result = Value().Call(recv, args);
+#ifdef NODE_ADDON_API_ENABLE_MAYBE
+ if (result.IsJust()) {
+ return Just(scope.Escape(result.Unwrap()));
+ }
+ return result;
+#else
if (scope.Env().IsExceptionPending()) {
return Value();
}
return scope.Escape(result);
+#endif
}
-inline Napi::Value FunctionReference::Call(
+inline MaybeOrValue FunctionReference::Call(
napi_value recv, const std::vector& args) const {
EscapableHandleScope scope(_env);
- Napi::Value result = Value().Call(recv, args);
+ MaybeOrValue result = Value().Call(recv, args);
+#ifdef NODE_ADDON_API_ENABLE_MAYBE
+ if (result.IsJust()) {
+ return Just(scope.Escape(result.Unwrap()));
+ }
+ return result;
+#else
if (scope.Env().IsExceptionPending()) {
return Value();
}
return scope.Escape(result);
+#endif
}
-inline Napi::Value FunctionReference::Call(
+inline MaybeOrValue FunctionReference::Call(
napi_value recv, size_t argc, const napi_value* args) const {
EscapableHandleScope scope(_env);
- Napi::Value result = Value().Call(recv, argc, args);
+ MaybeOrValue result = Value().Call(recv, argc, args);
+#ifdef NODE_ADDON_API_ENABLE_MAYBE
+ if (result.IsJust()) {
+ return Just(scope.Escape(result.Unwrap()));
+ }
+ return result;
+#else
if (scope.Env().IsExceptionPending()) {
return Value();
}
return scope.Escape(result);
+#endif
}
-inline Napi::Value FunctionReference::MakeCallback(
+inline MaybeOrValue FunctionReference::MakeCallback(
napi_value recv,
const std::initializer_list& args,
napi_async_context context) const {
EscapableHandleScope scope(_env);
- Napi::Value result = Value().MakeCallback(recv, args, context);
+ MaybeOrValue result = Value().MakeCallback(recv, args, context);
+#ifdef NODE_ADDON_API_ENABLE_MAYBE
+ if (result.IsJust()) {
+ return Just(scope.Escape(result.Unwrap()));
+ }
+
+ return result;
+#else
if (scope.Env().IsExceptionPending()) {
return Value();
}
return scope.Escape(result);
+#endif
}
-inline Napi::Value FunctionReference::MakeCallback(
+inline MaybeOrValue FunctionReference::MakeCallback(
napi_value recv,
const std::vector& args,
napi_async_context context) const {
EscapableHandleScope scope(_env);
- Napi::Value result = Value().MakeCallback(recv, args, context);
+ MaybeOrValue result = Value().MakeCallback(recv, args, context);
+#ifdef NODE_ADDON_API_ENABLE_MAYBE
+ if (result.IsJust()) {
+ return Just(scope.Escape(result.Unwrap()));
+ }
+ return result;
+#else
if (scope.Env().IsExceptionPending()) {
return Value();
}
return scope.Escape(result);
+#endif
}
-inline Napi::Value FunctionReference::MakeCallback(
+inline MaybeOrValue FunctionReference::MakeCallback(
napi_value recv,
size_t argc,
const napi_value* args,
napi_async_context context) const {
EscapableHandleScope scope(_env);
- Napi::Value result = Value().MakeCallback(recv, argc, args, context);
+ MaybeOrValue result =
+ Value().MakeCallback(recv, argc, args, context);
+#ifdef NODE_ADDON_API_ENABLE_MAYBE
+ if (result.IsJust()) {
+ return Just(scope.Escape(result.Unwrap()));
+ }
+ return result;
+#else
if (scope.Env().IsExceptionPending()) {
return Value();
}
return scope.Escape(result);
+#endif
}
-inline Object FunctionReference::New(const std::initializer_list& args) const {
+inline MaybeOrValue FunctionReference::New(
+ const std::initializer_list& args) const {
EscapableHandleScope scope(_env);
- return scope.Escape(Value().New(args)).As();
+ MaybeOrValue result = Value().New(args);
+#ifdef NODE_ADDON_API_ENABLE_MAYBE
+ if (result.IsJust()) {
+ return Just(scope.Escape(result.Unwrap()).As());
+ }
+ return result;
+#else
+ if (scope.Env().IsExceptionPending()) {
+ return Object();
+ }
+ return scope.Escape(result).As();
+#endif
}
-inline Object FunctionReference::New(const std::vector& args) const {
+inline MaybeOrValue FunctionReference::New(
+ const std::vector& args) const {
EscapableHandleScope scope(_env);
- return scope.Escape(Value().New(args)).As();
+ MaybeOrValue result = Value().New(args);
+#ifdef NODE_ADDON_API_ENABLE_MAYBE
+ if (result.IsJust()) {
+ return Just(scope.Escape(result.Unwrap()).As());
+ }
+ return result;
+#else
+ if (scope.Env().IsExceptionPending()) {
+ return Object();
+ }
+ return scope.Escape(result).As();
+#endif
}
////////////////////////////////////////////////////////////////////////////////
diff --git a/napi.h b/napi.h
index 9f12a76bf..8475bfc96 100644
--- a/napi.h
+++ b/napi.h
@@ -34,6 +34,13 @@ static_assert(sizeof(char16_t) == sizeof(wchar_t), "Size mismatch between char16
#endif
#endif
+// If C++ NAPI_CPP_EXCEPTIONS are enabled, NODE_ADDON_API_ENABLE_MAYBE should
+// not be set
+#if defined(NAPI_CPP_EXCEPTIONS) && defined(NODE_ADDON_API_ENABLE_MAYBE)
+#error NODE_ADDON_API_ENABLE_MAYBE should not be set when \
+ NAPI_CPP_EXCEPTIONS is defined.
+#endif
+
#ifdef _NOEXCEPT
#define NAPI_NOEXCEPT _NOEXCEPT
#else
@@ -77,20 +84,36 @@ static_assert(sizeof(char16_t) == sizeof(wchar_t), "Size mismatch between char16
return; \
} while (0)
-#define NAPI_THROW_IF_FAILED(env, status, ...) \
- if ((status) != napi_ok) { \
- Napi::Error::New(env).ThrowAsJavaScriptException(); \
- return __VA_ARGS__; \
+#define NAPI_THROW_IF_FAILED(env, status, ...) \
+ if ((status) != napi_ok) { \
+ Napi::Error::New(env).ThrowAsJavaScriptException(); \
+ return __VA_ARGS__; \
}
-#define NAPI_THROW_IF_FAILED_VOID(env, status) \
- if ((status) != napi_ok) { \
- Napi::Error::New(env).ThrowAsJavaScriptException(); \
- return; \
+#define NAPI_THROW_IF_FAILED_VOID(env, status) \
+ if ((status) != napi_ok) { \
+ Napi::Error::New(env).ThrowAsJavaScriptException(); \
+ return; \
}
#endif // NAPI_CPP_EXCEPTIONS
+#ifdef NODE_ADDON_API_ENABLE_MAYBE
+#define NAPI_MAYBE_THROW_IF_FAILED(env, status, type) \
+ NAPI_THROW_IF_FAILED(env, status, Napi::Nothing())
+
+#define NAPI_RETURN_OR_THROW_IF_FAILED(env, status, result, type) \
+ NAPI_MAYBE_THROW_IF_FAILED(env, status, type); \
+ return Napi::Just(result);
+#else
+#define NAPI_MAYBE_THROW_IF_FAILED(env, status, type) \
+ NAPI_THROW_IF_FAILED(env, status, type())
+
+#define NAPI_RETURN_OR_THROW_IF_FAILED(env, status, result, type) \
+ NAPI_MAYBE_THROW_IF_FAILED(env, status, type); \
+ return result;
+#endif
+
# define NAPI_DISALLOW_ASSIGN(CLASS) void operator=(const CLASS&) = delete;
# define NAPI_DISALLOW_COPY(CLASS) CLASS(const CLASS&) = delete;
@@ -98,13 +121,16 @@ static_assert(sizeof(char16_t) == sizeof(wchar_t), "Size mismatch between char16
NAPI_DISALLOW_ASSIGN(CLASS) \
NAPI_DISALLOW_COPY(CLASS)
-#define NAPI_FATAL_IF_FAILED(status, location, message) \
- do { \
- if ((status) != napi_ok) { \
- Napi::Error::Fatal((location), (message)); \
- } \
+#define NAPI_CHECK(condition, location, message) \
+ do { \
+ if (!(condition)) { \
+ Napi::Error::Fatal((location), (message)); \
+ } \
} while (0)
+#define NAPI_FATAL_IF_FAILED(status, location, message) \
+ NAPI_CHECK((status) == napi_ok, location, message)
+
////////////////////////////////////////////////////////////////////////////////
/// Node-API C++ Wrapper Classes
///
@@ -165,6 +191,67 @@ namespace Napi {
class MemoryManagement;
+ /// A simple Maybe type, representing an object which may or may not have a
+ /// value.
+ ///
+ /// If an API method returns a Maybe<>, the API method can potentially fail
+ /// either because an exception is thrown, or because an exception is pending,
+ /// e.g. because a previous API call threw an exception that hasn't been
+ /// caught yet. In that case, a "Nothing" value is returned.
+ template
+ class Maybe {
+ public:
+ bool IsNothing() const;
+ bool IsJust() const;
+
+ /// Short-hand for Unwrap(), which doesn't return a value. Could be used
+ /// where the actual value of the Maybe is not needed like Object::Set.
+ /// If this Maybe is nothing (empty), node-addon-api will crash the
+ /// process.
+ void Check() const;
+
+ /// Return the value of type T contained in the Maybe. If this Maybe is
+ /// nothing (empty), node-addon-api will crash the process.
+ T Unwrap() const;
+
+ /// Return the value of type T contained in the Maybe, or using a default
+ /// value if this Maybe is nothing (empty).
+ T UnwrapOr(const T& default_value) const;
+
+ /// Converts this Maybe to a value of type T in the out. If this Maybe is
+ /// nothing (empty), `false` is returned and `out` is left untouched.
+ bool UnwrapTo(T* out) const;
+
+ bool operator==(const Maybe& other) const;
+ bool operator!=(const Maybe& other) const;
+
+ private:
+ Maybe();
+ explicit Maybe(const T& t);
+
+ bool _has_value;
+ T _value;
+
+ template
+ friend Maybe Nothing();
+ template
+ friend Maybe Just(const U& u);
+ };
+
+ template
+ inline Maybe Nothing();
+
+ template
+ inline Maybe Just(const T& t);
+
+#if defined(NODE_ADDON_API_ENABLE_MAYBE)
+ template
+ using MaybeOrValue = Maybe;
+#else
+ template
+ using MaybeOrValue = T;
+#endif
+
/// Environment for Node-API values and operations.
///
/// All Node-API values and operations must be associated with an environment.
@@ -201,9 +288,9 @@ namespace Napi {
bool IsExceptionPending() const;
Error GetAndClearPendingException();
- Value RunScript(const char* utf8script);
- Value RunScript(const std::string& utf8script);
- Value RunScript(String script);
+ MaybeOrValue RunScript(const char* utf8script);
+ MaybeOrValue RunScript(const std::string& utf8script);
+ MaybeOrValue RunScript(String script);
#if NAPI_VERSION > 2
template
@@ -344,12 +431,16 @@ namespace Napi {
/// value type will throw `Napi::Error`.
template T As() const;
- Boolean ToBoolean() const; ///< Coerces a value to a JavaScript boolean.
- Number ToNumber() const; ///< Coerces a value to a JavaScript number.
- String ToString() const; ///< Coerces a value to a JavaScript string.
- Object ToObject() const; ///< Coerces a value to a JavaScript object.
+ MaybeOrValue ToBoolean()
+ const; ///< Coerces a value to a JavaScript boolean.
+ MaybeOrValue ToNumber()
+ const; ///< Coerces a value to a JavaScript number.
+ MaybeOrValue ToString()
+ const; ///< Coerces a value to a JavaScript string.
+ MaybeOrValue ToObject()
+ const; ///< Coerces a value to a JavaScript object.
- protected:
+ protected:
/// !cond INTERNAL
napi_env _env;
napi_value _value;
@@ -569,19 +660,20 @@ namespace Napi {
);
/// Get a public Symbol (e.g. Symbol.iterator).
- static Symbol WellKnown(napi_env, const std::string& name);
+ static MaybeOrValue WellKnown(napi_env, const std::string& name);
// Create a symbol in the global registry, UTF-8 Encoded cpp string
- static Symbol For(napi_env env, const std::string& description);
+ static MaybeOrValue For(napi_env env,
+ const std::string& description);
// Create a symbol in the global registry, C style string (null terminated)
- static Symbol For(napi_env env, const char* description);
+ static MaybeOrValue For(napi_env env, const char* description);
// Create a symbol in the global registry, String value describing the symbol
- static Symbol For(napi_env env, String description);
+ static MaybeOrValue For(napi_env env, String description);
// Create a symbol in the global registry, napi_value describing the symbol
- static Symbol For(napi_env env, napi_value description);
+ static MaybeOrValue For(napi_env env, napi_value description);
Symbol(); ///< Creates a new _empty_ Symbol instance.
Symbol(napi_env env,
@@ -591,16 +683,22 @@ namespace Napi {
/// A JavaScript object value.
class Object : public Value {
public:
- /// Enables property and element assignments using indexing syntax.
- ///
- /// Example:
- ///
- /// Napi::Value propertyValue = object1['A'];
- /// object2['A'] = propertyValue;
- /// Napi::Value elementValue = array[0];
- /// array[1] = elementValue;
- template