diff --git a/napi-inl.h b/napi-inl.h index 178807815..477098577 100644 --- a/napi-inl.h +++ b/napi-inl.h @@ -376,6 +376,74 @@ 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 +T Maybe::ToChecked() const { + return FromJust(); +} + +template +void Maybe::Check() const { + NAPI_CHECK(IsJust(), "Napi::Maybe::Check", "Maybe value is Nothing."); +} + +template +bool Maybe::To(T* out) const { + if (IsJust()) *out = _value; + return false; +} + +template +T Maybe::FromJust() const { + NAPI_CHECK(IsJust(), "Napi::Maybe::FromJust", "Maybe value is Nothing."); + return _value; +} + +template +T Maybe::FromMaybe(const T& default_value) const { + return _has_value ? _value : default_value; +} + +template +bool Maybe::operator==(const Maybe& other) const { + return (IsJust() == other.IsJust()) && + (!IsJust() || FromJust() == other.FromJust()); +} + +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 //////////////////////////////////////////////////////////////////////////////// @@ -425,20 +493,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_MAYBE_RETURN_OR_THROW_IF_FAILED( + _env, status, Napi::Value(_env, result), Napi::Value); } #if NAPI_VERSION > 5 @@ -657,32 +725,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_MAYBE_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_MAYBE_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_MAYBE_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_MAYBE_RETURN_OR_THROW_IF_FAILED( + _env, status, Napi::Object(_env, result), Napi::Object); } //////////////////////////////////////////////////////////////////////////////// @@ -731,55 +799,74 @@ inline Number::Number(napi_env env, napi_value value) : Value(env, value) { } inline Number::operator int32_t() const { +#ifdef NODE_ADDON_API_ENABLE_MAYBE + return Int32Value().FromMaybe(0); +#else return Int32Value(); +#endif } inline Number::operator uint32_t() const { +#ifdef NODE_ADDON_API_ENABLE_MAYBE + return Uint32Value().FromMaybe(0); +#else return Uint32Value(); +#endif } inline Number::operator int64_t() const { +#ifdef NODE_ADDON_API_ENABLE_MAYBE + return Int64Value().FromMaybe(0); +#else return Int64Value(); +#endif } inline Number::operator float() const { +#ifdef NODE_ADDON_API_ENABLE_MAYBE + return FloatValue().FromMaybe(0); +#else return FloatValue(); +#endif } inline Number::operator double() const { +#ifdef NODE_ADDON_API_ENABLE_MAYBE + return DoubleValue().FromMaybe(0); +#else return DoubleValue(); +#endif } -inline int32_t Number::Int32Value() const { +inline MaybeOrValue Number::Int32Value() const { int32_t result; napi_status status = napi_get_value_int32(_env, _value, &result); - NAPI_THROW_IF_FAILED(_env, status, 0); - return result; + NAPI_MAYBE_RETURN_OR_THROW_IF_FAILED(_env, status, result, int32_t); } -inline uint32_t Number::Uint32Value() const { +inline MaybeOrValue Number::Uint32Value() const { uint32_t result; napi_status status = napi_get_value_uint32(_env, _value, &result); - NAPI_THROW_IF_FAILED(_env, status, 0); - return result; + NAPI_MAYBE_RETURN_OR_THROW_IF_FAILED(_env, status, result, uint32_t); } -inline int64_t Number::Int64Value() const { +inline MaybeOrValue Number::Int64Value() const { int64_t result; napi_status status = napi_get_value_int64(_env, _value, &result); - NAPI_THROW_IF_FAILED(_env, status, 0); - return result; + NAPI_MAYBE_RETURN_OR_THROW_IF_FAILED(_env, status, result, int64_t); } -inline float Number::FloatValue() const { - return static_cast(DoubleValue()); +inline MaybeOrValue Number::FloatValue() const { + double result; + napi_status status = napi_get_value_double(_env, _value, &result); + NAPI_MAYBE_RETURN_OR_THROW_IF_FAILED( + _env, status, static_cast(result), float); } -inline double Number::DoubleValue() const { +inline MaybeOrValue Number::DoubleValue() const { double result; napi_status status = napi_get_value_double(_env, _value, &result); - NAPI_THROW_IF_FAILED(_env, status, 0); - return result; + NAPI_MAYBE_RETURN_OR_THROW_IF_FAILED(_env, status, result, double); } #if NAPI_VERSION > 5 @@ -2021,53 +2108,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_MAYBE_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, @@ -2075,24 +2170,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_MAYBE_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_MAYBE_RETURN_OR_THROW_IF_FAILED( + _env, status, Napi::Object(_env, result), Napi::Object); } //////////////////////////////////////////////////////////////////////////////// @@ -2779,105 +2877,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.ToChecked())); + } + 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.ToChecked())); + } + 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.ToChecked())); + } + 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.ToChecked())); + } + 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.ToChecked())); + } + 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.ToChecked())); + } + 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.ToChecked())); + } + + 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.ToChecked())); + } + 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.ToChecked())); + } + 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.ToChecked()).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.ToChecked()).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 f95da7402..153f83d22 100644 --- a/napi.h +++ b/napi.h @@ -91,6 +91,22 @@ static_assert(sizeof(char16_t) == sizeof(wchar_t), "Size mismatch between char16 #endif // NAPI_CPP_EXCEPTIONS +#define NAPI_MAYBE_THROW_IF_FAILED(env, status, ...) \ + if ((status) == napi_pending_exception) { \ + return __VA_ARGS__; \ + } \ + NAPI_THROW_IF_FAILED(env, status, __VA_ARGS__) + +#ifdef NODE_ADDON_API_ENABLE_MAYBE +#define NAPI_MAYBE_RETURN_OR_THROW_IF_FAILED(env, status, result, type) \ + NAPI_MAYBE_THROW_IF_FAILED(env, status, Napi::Nothing()); \ + return Napi::Just(result); +#else +#define NAPI_MAYBE_RETURN_OR_THROW_IF_FAILED(env, status, result, type) \ + NAPI_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 +114,26 @@ 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) + +// Annotate a function indicating the caller must examine the return value. +// Use like: +// NAPI_WARN_UNUSED_RESULT int foo(); +// TODO: +#if NAPI_HAS_ATTRIBUTE_WARN_UNUSED_RESULT +#define NAPI_WARN_UNUSED_RESULT __attribute__((warn_unused_result)) +#else +#define NAPI_WARN_UNUSED_RESULT /* NOT SUPPORTED */ +#endif + //////////////////////////////////////////////////////////////////////////////// /// N-API C++ Wrapper Classes /// @@ -164,6 +193,68 @@ namespace Napi { class MemoryManagement; + /// A simple Maybe type, representing an object which may or may not have a + /// value, see https://hackage.haskell.org/package/base/docs/Data-Maybe.html. + /// + /// 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; + + /// An alias for |FromJust|. Will crash if the Maybe<> is nothing. + T ToChecked() const; + + /// Short-hand for ToChecked(), which doesn't return a value. To be used, + /// where the actual value of the Maybe is not needed like Object::Set. + void Check() const; + + /// Converts this Maybe<> to a value of type T. If this Maybe<> is + /// nothing (empty), |false| is returned and |out| is left untouched. + inline bool To(T* out) const; + + /// Converts this Maybe<> to a value of type T. If this Maybe<> is + /// nothing (empty), node-addon-api will crash the process. + T FromJust() const; + + /// Converts this Maybe<> to a value of type T, using a default value if + /// this Maybe<> is nothing (empty). + T FromMaybe(const T& default_value) 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 N-API values and operations. /// /// All N-API values and operations must be associated with an environment. An environment @@ -194,9 +285,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 > 5 template T* GetInstanceData(); @@ -307,12 +398,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; @@ -351,11 +446,16 @@ namespace Napi { operator float() const; ///< Converts a Number value to a 32-bit floating-point value. operator double() const; ///< Converts a Number value to a 64-bit floating-point value. - int32_t Int32Value() const; ///< Converts a Number value to a 32-bit signed integer value. - uint32_t Uint32Value() const; ///< Converts a Number value to a 32-bit unsigned integer value. - int64_t Int64Value() const; ///< Converts a Number value to a 64-bit signed integer value. - float FloatValue() const; ///< Converts a Number value to a 32-bit floating-point value. - double DoubleValue() const; ///< Converts a Number value to a 64-bit floating-point value. + MaybeOrValue Int32Value() + const; ///< Converts a Number value to a 32-bit signed integer value. + MaybeOrValue Uint32Value() + const; ///< Converts a Number value to a 32-bit unsigned integer value. + MaybeOrValue Int64Value() + const; ///< Converts a Number value to a 64-bit signed integer value. + MaybeOrValue FloatValue() + const; ///< Converts a Number value to a 32-bit floating-point value. + MaybeOrValue DoubleValue() + const; ///< Converts a Number value to a 64-bit floating-point value. }; #if NAPI_VERSION > 5 @@ -1059,30 +1159,37 @@ namespace Napi { Function(); Function(napi_env env, napi_value value); - Value operator()(const std::initializer_list& args) const; - - Value Call(const std::initializer_list& args) const; - Value Call(const std::vector& args) const; - Value Call(size_t argc, const napi_value* args) const; - Value Call(napi_value recv, - const std::initializer_list& args) const; - Value Call(napi_value recv, const std::vector& args) const; - Value Call(napi_value recv, size_t argc, const napi_value* args) const; - - Value MakeCallback(napi_value recv, - const std::initializer_list& args, - napi_async_context context = nullptr) const; - Value MakeCallback(napi_value recv, - const std::vector& args, - napi_async_context context = nullptr) const; - Value MakeCallback(napi_value recv, - size_t argc, - const napi_value* args, - napi_async_context context = nullptr) const; - - Object New(const std::initializer_list& args) const; - Object New(const std::vector& args) const; - Object New(size_t argc, const napi_value* args) const; + MaybeOrValue operator()( + const std::initializer_list& args) const; + + MaybeOrValue Call( + const std::initializer_list& args) const; + MaybeOrValue Call(const std::vector& args) const; + MaybeOrValue Call(size_t argc, const napi_value* args) const; + MaybeOrValue Call( + napi_value recv, const std::initializer_list& args) const; + MaybeOrValue Call(napi_value recv, + const std::vector& args) const; + MaybeOrValue Call(napi_value recv, + size_t argc, + const napi_value* args) const; + + MaybeOrValue MakeCallback( + napi_value recv, + const std::initializer_list& args, + napi_async_context context = nullptr) const; + MaybeOrValue MakeCallback(napi_value recv, + const std::vector& args, + napi_async_context context = nullptr) const; + MaybeOrValue MakeCallback(napi_value recv, + size_t argc, + const napi_value* args, + napi_async_context context = nullptr) const; + + MaybeOrValue New( + const std::initializer_list& args) const; + MaybeOrValue New(const std::vector& args) const; + MaybeOrValue New(size_t argc, const napi_value* args) const; }; class Promise : public Object { @@ -1243,27 +1350,37 @@ namespace Napi { FunctionReference& operator =(FunctionReference&& other); NAPI_DISALLOW_ASSIGN_COPY(FunctionReference) - Napi::Value operator ()(const std::initializer_list& args) const; - - Napi::Value Call(const std::initializer_list& args) const; - Napi::Value Call(const std::vector& args) const; - Napi::Value Call(napi_value recv, const std::initializer_list& args) const; - Napi::Value Call(napi_value recv, const std::vector& args) const; - Napi::Value Call(napi_value recv, size_t argc, const napi_value* args) const; - - Napi::Value MakeCallback(napi_value recv, - const std::initializer_list& args, - napi_async_context context = nullptr) const; - Napi::Value MakeCallback(napi_value recv, - const std::vector& args, - napi_async_context context = nullptr) const; - Napi::Value MakeCallback(napi_value recv, - size_t argc, - const napi_value* args, - napi_async_context context = nullptr) const; - - Object New(const std::initializer_list& args) const; - Object New(const std::vector& args) const; + MaybeOrValue operator()( + const std::initializer_list& args) const; + + MaybeOrValue Call( + const std::initializer_list& args) const; + MaybeOrValue Call(const std::vector& args) const; + MaybeOrValue Call( + napi_value recv, const std::initializer_list& args) const; + MaybeOrValue Call(napi_value recv, + const std::vector& args) const; + MaybeOrValue Call(napi_value recv, + size_t argc, + const napi_value* args) const; + + MaybeOrValue MakeCallback( + napi_value recv, + const std::initializer_list& args, + napi_async_context context = nullptr) const; + MaybeOrValue MakeCallback( + napi_value recv, + const std::vector& args, + napi_async_context context = nullptr) const; + MaybeOrValue MakeCallback( + napi_value recv, + size_t argc, + const napi_value* args, + napi_async_context context = nullptr) const; + + MaybeOrValue New( + const std::initializer_list& args) const; + MaybeOrValue New(const std::vector& args) const; }; // Shortcuts to creating a new reference with inferred type and refcount = 0. diff --git a/test/binding.gyp b/test/binding.gyp index c9f82ee20..d6e476ddf 100644 --- a/test/binding.gyp +++ b/test/binding.gyp @@ -75,5 +75,13 @@ 'target_name': 'binding_noexcept', 'includes': ['../noexcept.gypi'] }, + { + 'target_name': 'binding_noexcept_maybe', + 'includes': ['../noexcept.gypi'], + 'defines': ['NODE_ADDON_API_ENABLE_MAYBE'], + 'sources=': [ + 'maybe/function.cc', + ], + }, ], } diff --git a/test/maybe/function.cc b/test/maybe/function.cc new file mode 100644 index 000000000..11a114a95 --- /dev/null +++ b/test/maybe/function.cc @@ -0,0 +1,22 @@ +#include "napi.h" + +using namespace Napi; + +namespace { + +void VoidCallback(const CallbackInfo& info) { + Function fn = info[0].As(); + + Maybe it = fn.Call({}); + + it.Check(); +} + +} // end anonymous namespace + +Object Init(Env env, Object exports) { + exports.Set("voidCallback", Function::New(env, VoidCallback)); + return exports; +} + +NODE_API_MODULE(addon, Init) diff --git a/test/maybe/index.js b/test/maybe/index.js new file mode 100644 index 000000000..b659bcbc4 --- /dev/null +++ b/test/maybe/index.js @@ -0,0 +1,35 @@ +'use strict'; + +const buildType = process.config.target_defaults.default_configuration; +const assert = require('assert'); +const os = require('os'); + +const napiChild = require('../napi_child'); + +test(require(`../build/${buildType}/binding_noexcept_maybe.node`)); + +function test(binding) { + if (process.argv.includes('child')) { + child(binding); + return; + } + const cp = napiChild.spawn(process.execPath, [__filename, 'child'], { + stdio: ['ignore', 'inherit', 'pipe'], + }); + cp.stderr.setEncoding('utf8'); + let stderr = ''; + cp.stderr.on('data', chunk => { + stderr += chunk; + }); + cp.on('exit', (code, signal) => { + assert.ok(code == null); + assert.strictEqual(signal, 'SIGABRT'); + assert.ok(stderr.match(/FATAL ERROR: Napi::Maybe::Check Maybe value is Nothing./)); + }); +} + +function child(binding) { + binding.voidCallback(() => { + throw new Error('foobar'); + }) +}