Skip to content

Commit 0b40275

Browse files
DaAitchmhdawson
authored andcommitted
src: fix noexcept control flow issues
- adapt `NAPI_THROW`, create `NAPI_THROW_VOID`: either throw or return, never continue - fix `Error::ThrowAsJavaScriptException`: if `napi_throw` fails, either explicitly throw or create fatal error Fixes: #419 PR-URL: #420 Reviewed-By: Gabriel Schulhof <gabriel.schulhof@intel.com> Reviewed-By: Michael Dawson <michael_dawson@ca.ibm.com>
1 parent c1ff293 commit 0b40275

File tree

1 file changed

+42
-17
lines changed

1 file changed

+42
-17
lines changed

napi-inl.h

Lines changed: 42 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -19,11 +19,15 @@ namespace details {
1919

2020
#ifdef NAPI_CPP_EXCEPTIONS
2121

22-
#define NAPI_THROW(e) throw e
23-
2422
// When C++ exceptions are enabled, Errors are thrown directly. There is no need
25-
// to return anything after the throw statement. The variadic parameter is an
23+
// to return anything after the throw statements. The variadic parameter is an
2624
// optional return value that is ignored.
25+
// We need _VOID versions of the macros to avoid warnings resulting from
26+
// leaving the NAPI_THROW_* `...` argument empty.
27+
28+
#define NAPI_THROW(e, ...) throw e
29+
#define NAPI_THROW_VOID(e) throw e
30+
2731
#define NAPI_THROW_IF_FAILED(env, status, ...) \
2832
if ((status) != napi_ok) throw Error::New(env);
2933

@@ -32,19 +36,30 @@ namespace details {
3236

3337
#else // NAPI_CPP_EXCEPTIONS
3438

35-
#define NAPI_THROW(e) (e).ThrowAsJavaScriptException();
36-
3739
// When C++ exceptions are disabled, Errors are thrown as JavaScript exceptions,
3840
// which are pending until the callback returns to JS. The variadic parameter
3941
// is an optional return value; usually it is an empty result.
42+
// We need _VOID versions of the macros to avoid warnings resulting from
43+
// leaving the NAPI_THROW_* `...` argument empty.
44+
45+
#define NAPI_THROW(e, ...) \
46+
do { \
47+
(e).ThrowAsJavaScriptException(); \
48+
return __VA_ARGS__; \
49+
} while (0)
50+
51+
#define NAPI_THROW_VOID(e) \
52+
do { \
53+
(e).ThrowAsJavaScriptException(); \
54+
return; \
55+
} while (0)
56+
4057
#define NAPI_THROW_IF_FAILED(env, status, ...) \
4158
if ((status) != napi_ok) { \
4259
Error::New(env).ThrowAsJavaScriptException(); \
4360
return __VA_ARGS__; \
4461
}
4562

46-
// We need a _VOID version of this macro to avoid warnings resulting from
47-
// leaving the NAPI_THROW_IF_FAILED `...` argument empty.
4863
#define NAPI_THROW_IF_FAILED_VOID(env, status) \
4964
if ((status) != napi_ok) { \
5065
Error::New(env).ThrowAsJavaScriptException(); \
@@ -1312,8 +1327,8 @@ inline DataView DataView::New(napi_env env,
13121327
size_t byteOffset) {
13131328
if (byteOffset > arrayBuffer.ByteLength()) {
13141329
NAPI_THROW(RangeError::New(env,
1315-
"Start offset is outside the bounds of the buffer"));
1316-
return DataView();
1330+
"Start offset is outside the bounds of the buffer"),
1331+
DataView());
13171332
}
13181333
return New(env, arrayBuffer, byteOffset,
13191334
arrayBuffer.ByteLength() - byteOffset);
@@ -1324,8 +1339,8 @@ inline DataView DataView::New(napi_env env,
13241339
size_t byteOffset,
13251340
size_t byteLength) {
13261341
if (byteOffset + byteLength > arrayBuffer.ByteLength()) {
1327-
NAPI_THROW(RangeError::New(env, "Invalid DataView length"));
1328-
return DataView();
1342+
NAPI_THROW(RangeError::New(env, "Invalid DataView length"),
1343+
DataView());
13291344
}
13301345
napi_value value;
13311346
napi_status status = napi_create_dataview(
@@ -1451,8 +1466,7 @@ inline T DataView::ReadData(size_t byteOffset) const {
14511466
if (byteOffset + sizeof(T) > _length ||
14521467
byteOffset + sizeof(T) < byteOffset) { // overflow
14531468
NAPI_THROW(RangeError::New(_env,
1454-
"Offset is outside the bounds of the DataView"));
1455-
return 0;
1469+
"Offset is outside the bounds of the DataView"), 0);
14561470
}
14571471

14581472
return *reinterpret_cast<T*>(static_cast<uint8_t*>(_data) + byteOffset);
@@ -1462,9 +1476,8 @@ template <typename T>
14621476
inline void DataView::WriteData(size_t byteOffset, T value) const {
14631477
if (byteOffset + sizeof(T) > _length ||
14641478
byteOffset + sizeof(T) < byteOffset) { // overflow
1465-
NAPI_THROW(RangeError::New(_env,
1479+
NAPI_THROW_VOID(RangeError::New(_env,
14661480
"Offset is outside the bounds of the DataView"));
1467-
return;
14681481
}
14691482

14701483
*reinterpret_cast<T*>(static_cast<uint8_t*>(_data) + byteOffset) = value;
@@ -1600,7 +1613,7 @@ inline TypedArrayOf<T>::TypedArrayOf(napi_env env,
16001613
: TypedArray(env, value, type, length), _data(data) {
16011614
if (!(type == TypedArrayTypeForPrimitiveType<T>() ||
16021615
(type == napi_uint8_clamped_array && std::is_same<T, uint8_t>::value))) {
1603-
NAPI_THROW(TypeError::New(env, "Array type must match the template parameter. "
1616+
NAPI_THROW_VOID(TypeError::New(env, "Array type must match the template parameter. "
16041617
"(Uint8 arrays may optionally have the \"clamped\" array type.)"));
16051618
}
16061619
}
@@ -2034,8 +2047,20 @@ inline const std::string& Error::Message() const NAPI_NOEXCEPT {
20342047
inline void Error::ThrowAsJavaScriptException() const {
20352048
HandleScope scope(_env);
20362049
if (!IsEmpty()) {
2050+
2051+
// We intentionally don't use `NAPI_THROW_*` macros here to ensure
2052+
// that there is no possible recursion as `ThrowAsJavaScriptException`
2053+
// is part of `NAPI_THROW_*` macro definition for noexcept.
2054+
20372055
napi_status status = napi_throw(_env, Value());
2038-
NAPI_THROW_IF_FAILED_VOID(_env, status);
2056+
2057+
#ifdef NAPI_CPP_EXCEPTIONS
2058+
if (status != napi_ok) {
2059+
throw Error::New(_env);
2060+
}
2061+
#else // NAPI_CPP_EXCEPTIONS
2062+
NAPI_FATAL_IF_FAILED(status, "Error::ThrowAsJavaScriptException", "napi_throw");
2063+
#endif // NAPI_CPP_EXCEPTIONS
20392064
}
20402065
}
20412066

0 commit comments

Comments
 (0)