diff --git a/README.md b/README.md index 5bb89a966..c7c16a162 100644 --- a/README.md +++ b/README.md @@ -132,6 +132,7 @@ The following is the documentation for node-addon-api. - [Error](doc/error.md) - [TypeError](doc/type_error.md) - [RangeError](doc/range_error.md) + - [SyntaxError](doc/syntax_error.md) - [Object Lifetime Management](doc/object_lifetime_management.md) - [HandleScope](doc/handle_scope.md) - [EscapableHandleScope](doc/escapable_handle_scope.md) diff --git a/doc/hierarchy.md b/doc/hierarchy.md index 42b1ff99d..440f7a6c6 100644 --- a/doc/hierarchy.md +++ b/doc/hierarchy.md @@ -37,6 +37,7 @@ | [`Napi::Reference`] | | | [`Napi::String`][] | [`Napi::Name`][] | | [`Napi::Symbol`][] | [`Napi::Name`][] | +| [`Napi::SyntaxError`][] | [`Napi::Error`][] | | [`Napi::ThreadSafeFunction`][] | | | [`Napi::TypeTaggable`][] | [`Napi::Value][] | | [`Napi::TypeError`][] | [`Napi::Error`][] | @@ -82,6 +83,7 @@ [`Napi::Reference`]: ./reference.md [`Napi::String`]: ./string.md [`Napi::Symbol`]: ./symbol.md +[`Napi::SyntaxError`]: ./syntax_error.md [`Napi::ThreadSafeFunction`]: ./threadsafe_function.md [`Napi::TypeError`]: ./type_error.md [`Napi::TypeTaggable`]: ./type_taggable.md diff --git a/doc/syntax_error.md b/doc/syntax_error.md new file mode 100644 index 000000000..53b09c4a6 --- /dev/null +++ b/doc/syntax_error.md @@ -0,0 +1,66 @@ +# SyntaxError + +The `Napi::SyntaxError` class is a representation of the JavaScript +`SyntaxError` that is thrown when the engine encounters tokens or token order +that does not conform to the syntax of the language when parsing code. + +The `Napi::SyntaxError` class inherits its behaviors from the `Napi::Error` +class (for more info see: [`Napi::Error`](error.md)). + +For more details about error handling refer to the section titled [Error +handling](error_handling.md). + +## Methods + +### New + +Creates a new instance of a `Napi::SyntaxError` object. + +```cpp +Napi::SyntaxError::New(Napi::Env env, const char* message); +``` + +- `[in] Env`: The environment in which to construct the `Napi::SyntaxError` + object. +- `[in] message`: Null-terminated string to be used as the message for the + `Napi::SyntaxError`. + +Returns an instance of a `Napi::SyntaxError` object. + +### New + +Creates a new instance of a `Napi::SyntaxError` object. + +```cpp +Napi::SyntaxError::New(Napi::Env env, const std::string& message); +``` + +- `[in] Env`: The environment in which to construct the `Napi::SyntaxError` + object. +- `[in] message`: Reference string to be used as the message for the + `Napi::SyntaxError`. + +Returns an instance of a `Napi::SyntaxError` object. + +### Constructor + +Creates a new empty instance of a `Napi::SyntaxError`. + +```cpp +Napi::SyntaxError::SyntaxError(); +``` + +### Constructor + +Initializes a `Napi::SyntaxError` instance from an existing Javascript error +object. + +```cpp +Napi::SyntaxError::SyntaxError(napi_env env, napi_value value); +``` + +- `[in] Env`: The environment in which to construct the `Napi::SyntaxError` + object. +- `[in] value`: The `Napi::Error` reference to wrap. + +Returns an instance of a `Napi::SyntaxError` object. diff --git a/napi-inl.h b/napi-inl.h index b1657d5fe..441fa53ed 100644 --- a/napi-inl.h +++ b/napi-inl.h @@ -3157,6 +3157,23 @@ inline RangeError::RangeError() : Error() {} inline RangeError::RangeError(napi_env env, napi_value value) : Error(env, value) {} +#if NAPI_VERSION > 8 +inline SyntaxError SyntaxError::New(napi_env env, const char* message) { + return Error::New( + env, message, std::strlen(message), node_api_create_syntax_error); +} + +inline SyntaxError SyntaxError::New(napi_env env, const std::string& message) { + return Error::New( + env, message.c_str(), message.size(), node_api_create_syntax_error); +} + +inline SyntaxError::SyntaxError() : Error() {} + +inline SyntaxError::SyntaxError(napi_env env, napi_value value) + : Error(env, value) {} +#endif // NAPI_VERSION > 8 + //////////////////////////////////////////////////////////////////////////////// // Reference class //////////////////////////////////////////////////////////////////////////////// diff --git a/napi.h b/napi.h index f3119d7d1..96af168bc 100644 --- a/napi.h +++ b/napi.h @@ -1853,6 +1853,17 @@ class RangeError : public Error { RangeError(napi_env env, napi_value value); }; +#if NAPI_VERSION > 8 +class SyntaxError : public Error { + public: + static SyntaxError New(napi_env env, const char* message); + static SyntaxError New(napi_env env, const std::string& message); + + SyntaxError(); + SyntaxError(napi_env env, napi_value value); +}; +#endif // NAPI_VERSION > 8 + class CallbackInfo { public: CallbackInfo(napi_env env, napi_callback_info info); diff --git a/test/error.cc b/test/error.cc index f2da8dd71..6c716351b 100644 --- a/test/error.cc +++ b/test/error.cc @@ -153,6 +153,27 @@ void ThrowRangeError(const CallbackInfo& info) { throw RangeError::New(info.Env(), message); } +#if NAPI_VERSION > 8 +void ThrowSyntaxErrorCStr(const CallbackInfo& info) { + std::string message = info[0].As().Utf8Value(); + ReleaseAndWaitForChildProcess(info, 1); + throw SyntaxError::New(info.Env(), message.c_str()); +} + +void ThrowSyntaxErrorCtor(const CallbackInfo& info) { + Napi::Value js_range_err = info[0]; + ReleaseAndWaitForChildProcess(info, 1); + throw Napi::SyntaxError(info.Env(), js_range_err); +} + +void ThrowSyntaxError(const CallbackInfo& info) { + std::string message = info[0].As().Utf8Value(); + + ReleaseAndWaitForChildProcess(info, 1); + throw SyntaxError::New(info.Env(), message); +} +#endif // NAPI_VERSION > 8 + Value CatchError(const CallbackInfo& info) { Function thrower = info[0].As(); try { @@ -255,6 +276,27 @@ void ThrowEmptyRangeError(const CallbackInfo& info) { RangeError().ThrowAsJavaScriptException(); } +#if NAPI_VERSION > 8 +void ThrowSyntaxError(const CallbackInfo& info) { + std::string message = info[0].As().Utf8Value(); + + ReleaseAndWaitForChildProcess(info, 1); + SyntaxError::New(info.Env(), message).ThrowAsJavaScriptException(); +} + +void ThrowSyntaxErrorCtor(const CallbackInfo& info) { + Napi::Value js_range_err = info[0]; + ReleaseAndWaitForChildProcess(info, 1); + SyntaxError(info.Env(), js_range_err).ThrowAsJavaScriptException(); +} + +void ThrowSyntaxErrorCStr(const CallbackInfo& info) { + std::string message = info[0].As().Utf8Value(); + ReleaseAndWaitForChildProcess(info, 1); + SyntaxError::New(info.Env(), message.c_str()).ThrowAsJavaScriptException(); +} +#endif // NAPI_VERSION > 8 + Value CatchError(const CallbackInfo& info) { Function thrower = info[0].As(); thrower({}); @@ -372,6 +414,11 @@ Object InitError(Env env) { exports["throwRangeErrorCtor"] = Function::New(env, ThrowRangeErrorCtor); exports["throwRangeErrorCStr"] = Function::New(env, ThrowRangeErrorCStr); exports["throwEmptyRangeError"] = Function::New(env, ThrowEmptyRangeError); +#if NAPI_VERSION > 8 + exports["throwSyntaxError"] = Function::New(env, ThrowSyntaxError); + exports["throwSyntaxErrorCtor"] = Function::New(env, ThrowSyntaxErrorCtor); + exports["throwSyntaxErrorCStr"] = Function::New(env, ThrowSyntaxErrorCStr); +#endif // NAPI_VERSION > 8 exports["catchError"] = Function::New(env, CatchError); exports["catchErrorMessage"] = Function::New(env, CatchErrorMessage); exports["doNotCatch"] = Function::New(env, DoNotCatch); diff --git a/test/error.js b/test/error.js index ae3496af5..c2a3ad367 100644 --- a/test/error.js +++ b/test/error.js @@ -9,6 +9,8 @@ if (process.argv[2] === 'fatal') { module.exports = require('./common').runTestWithBindingPath(test); +const napiVersion = Number(process.env.NAPI_VERSION ?? process.versions.napi); + function test (bindingPath) { const binding = require(bindingPath); binding.error.testErrorCopySemantics(); @@ -46,6 +48,20 @@ function test (bindingPath) { return err instanceof RangeError && err.message === 'rangeTypeError'; }); + if (napiVersion > 8) { + assert.throws(() => binding.error.throwSyntaxErrorCStr('test'), function (err) { + return err instanceof SyntaxError && err.message === 'test'; + }); + + assert.throws(() => binding.error.throwSyntaxError('test'), function (err) { + return err instanceof SyntaxError && err.message === 'test'; + }); + + assert.throws(() => binding.error.throwSyntaxErrorCtor(new SyntaxError('syntaxTypeError')), function (err) { + return err instanceof SyntaxError && err.message === 'syntaxTypeError'; + }); + } + assert.throws( () => binding.error.doNotCatch( () => {