Skip to content

Commit ed4d1c5

Browse files
committed
Added unwrapping logic to handle graceful error handling for primitives
1 parent 4663453 commit ed4d1c5

File tree

6 files changed

+111
-2
lines changed

6 files changed

+111
-2
lines changed

napi-inl.h

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2595,12 +2595,73 @@ inline Error::Error(napi_env env, napi_value value) : ObjectReference(env, nullp
25952595
if (value != nullptr) {
25962596
napi_status status = napi_create_reference(env, value, 1, &_ref);
25972597

2598+
// Creates a wrapper object containg the error value (primitive types) and
2599+
// create a reference to this wrapper
2600+
if (status != napi_ok) {
2601+
napi_value wrappedErrorObj;
2602+
status = napi_create_object(env, &wrappedErrorObj);
2603+
2604+
NAPI_FATAL_IF_FAILED(status, "Error::Error", "napi_create_object");
2605+
2606+
status = napi_set_property(env,
2607+
wrappedErrorObj,
2608+
String::From(env, "errorVal"),
2609+
Value::From(env, value));
2610+
NAPI_FATAL_IF_FAILED(status, "Error::Error", "napi_set_property");
2611+
2612+
status = napi_set_property(env,
2613+
wrappedErrorObj,
2614+
String::From(env, "isWrapObject"),
2615+
Value::From(env, value));
2616+
NAPI_FATAL_IF_FAILED(status, "Error::Error", "napi_set_property");
2617+
2618+
status = napi_create_reference(env, wrappedErrorObj, 1, &_ref);
2619+
}
2620+
25982621
// Avoid infinite recursion in the failure case.
25992622
// Don't try to construct & throw another Error instance.
26002623
NAPI_FATAL_IF_FAILED(status, "Error::Error", "napi_create_reference");
26012624
}
26022625
}
26032626

2627+
inline Object Error::Value() const {
2628+
if (_ref == nullptr) {
2629+
return Object(_env, nullptr);
2630+
}
2631+
// Most likely will mess up thread execution
2632+
2633+
napi_value refValue;
2634+
napi_status status = napi_get_reference_value(_env, _ref, &refValue);
2635+
NAPI_THROW_IF_FAILED(_env, status, Object());
2636+
2637+
// We are wrapping this object
2638+
bool isWrappedObject = false;
2639+
napi_has_property(
2640+
_env, refValue, String::From(_env, "isWrapObject"), &isWrappedObject);
2641+
// Don't care about status
2642+
2643+
if (isWrappedObject == true) {
2644+
napi_value unwrappedValue;
2645+
status = napi_get_property(
2646+
_env, refValue, String::From(_env, "errorVal"), &unwrappedValue);
2647+
NAPI_THROW_IF_FAILED(_env, status, Object());
2648+
return Object(_env, unwrappedValue);
2649+
}
2650+
2651+
return Object(_env, refValue);
2652+
}
2653+
// template<typename T>
2654+
// inline T Error::Value() const {
2655+
// // if (_ref == nullptr) {
2656+
// // return T(_env, nullptr);
2657+
// // }
2658+
2659+
// // napi_value value;
2660+
// // napi_status status = napi_get_reference_value(_env, _ref, &value);
2661+
// // NAPI_THROW_IF_FAILED(_env, status, T());
2662+
// return nullptr;
2663+
// }
2664+
26042665
inline Error::Error(Error&& other) : ObjectReference(std::move(other)) {
26052666
}
26062667

@@ -2651,6 +2712,7 @@ inline const std::string& Error::Message() const NAPI_NOEXCEPT {
26512712
return _message;
26522713
}
26532714

2715+
// we created an object on the &_ref
26542716
inline void Error::ThrowAsJavaScriptException() const {
26552717
HandleScope scope(_env);
26562718
if (!IsEmpty()) {

napi.h

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,11 +4,11 @@
44
#include <node_api.h>
55
#include <functional>
66
#include <initializer_list>
7+
#include <iostream>
78
#include <memory>
89
#include <mutex>
910
#include <string>
1011
#include <vector>
11-
1212
// VS2015 RTM has bugs with constexpr, so require min of VS2015 Update 3 (known good version)
1313
#if !defined(_MSC_VER) || _MSC_FULL_VER >= 190024210
1414
#define NAPI_HAS_CONSTEXPR 1
@@ -1699,6 +1699,8 @@ namespace Napi {
16991699
const std::string& Message() const NAPI_NOEXCEPT;
17001700
void ThrowAsJavaScriptException() const;
17011701

1702+
Object Value() const;
1703+
17021704
#ifdef NAPI_CPP_EXCEPTIONS
17031705
const char* what() const NAPI_NOEXCEPT override;
17041706
#endif // NAPI_CPP_EXCEPTIONS
@@ -1718,7 +1720,7 @@ namespace Napi {
17181720
/// !endcond
17191721

17201722
private:
1721-
mutable std::string _message;
1723+
mutable std::string _message;
17221724
};
17231725

17241726
class TypeError : public Error {

test/binding.cc

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ Object InitDate(Env env);
3131
Object InitDataView(Env env);
3232
Object InitDataViewReadWrite(Env env);
3333
Object InitEnvCleanup(Env env);
34+
Object InitErrorHandlingPrim(Env env);
3435
Object InitError(Env env);
3536
Object InitExternal(Env env);
3637
Object InitFunction(Env env);
@@ -113,6 +114,7 @@ Object Init(Env env, Object exports) {
113114
exports.Set("env_cleanup", InitEnvCleanup(env));
114115
#endif
115116
exports.Set("error", InitError(env));
117+
exports.Set("errorHandlingPrim", InitErrorHandlingPrim(env));
116118
exports.Set("external", InitExternal(env));
117119
exports.Set("function", InitFunction(env));
118120
exports.Set("functionreference", InitFunctionReference(env));

test/binding.gyp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
'dataview/dataview_read_write.cc',
2626
'env_cleanup.cc',
2727
'error.cc',
28+
'errorHandlingForPrimitives.cc',
2829
'external.cc',
2930
'function.cc',
3031
'function_reference.cc',

test/errorHandlingForPrimitives.cc

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
#include <napi.h>
2+
3+
namespace {
4+
void Test(const Napi::CallbackInfo& info) {
5+
info[0].As<Napi::Function>().Call({});
6+
}
7+
8+
} // namespace
9+
Napi::Object InitErrorHandlingPrim(Napi::Env env) {
10+
Napi::Object exports = Napi::Object::New(env);
11+
exports.Set("errorHandlingPrim", Napi::Function::New<Test>(env));
12+
return exports;
13+
}

test/errorHandlingForPrimitives.js

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
'use strict';
2+
3+
const assert = require('assert');
4+
5+
module.exports = require('./common').runTest((binding) => {
6+
test(binding.errorHandlingPrim);
7+
});
8+
9+
function canThrow (binding, errorMessage, errorType) {
10+
try {
11+
binding.errorHandlingPrim(() => {
12+
throw errorMessage;
13+
});
14+
} catch (e) {
15+
// eslint-disable-next-line valid-typeof
16+
assert(typeof e === errorType);
17+
assert(e === errorMessage);
18+
}
19+
}
20+
21+
function test (binding) {
22+
canThrow(binding, '404 server not found!', 'string');
23+
canThrow(binding, 42, 'number');
24+
canThrow(binding, Symbol.for('newSym'), 'symbol');
25+
canThrow(binding, false, 'boolean');
26+
canThrow(binding, BigInt(123), 'bigint');
27+
canThrow(binding, () => { console.log('Logger shutdown incorrectly'); }, 'function');
28+
canThrow(binding, { status: 403, errorMsg: 'Not authenticated' }, 'object');
29+
}

0 commit comments

Comments
 (0)