Skip to content

Commit 8f34553

Browse files
committed
napi: change napi_callback to return napi_value
Change ```napi_callback``` to return ```napi_value``` directly instead of requiring ```napi_set_return_value```. When we invoke the callback, we will check the return value and call ```SetReturnValue``` ourselves. If the callback returns ```NULL```, we don't set the return value in v8 which would have the same effect as previously if the callback didn't call ```napi_set_return_value```. Seems to be a more natural way to handle return values from callbacks. As a consequence, remove ```napi_set_return_value```. Add a ```napi_value``` to ```napi_property_descriptor``` to support string values which couldn't be passed in the ```utf8name``` parameter or symbols as property names. Class names, however, cannot be symbols so this ```napi_value``` must be a string type in that case. Remove all of the ```napi_callback_info``` helpers except for ```napi_get_cb_info``` and make all the parameters to ```napi_get_cb_info``` optional except for argc. Update all the test collateral according to these changes. Also add ```test/addons-napi/common.h``` to house some common macros for wrapping N-API calls and error handling.
1 parent 971fe67 commit 8f34553

File tree

32 files changed

+938
-1592
lines changed

32 files changed

+938
-1592
lines changed

src/node_api.cc

Lines changed: 122 additions & 155 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,70 @@ class napi_env__ {
3232
napi_extended_error_info last_error;
3333
};
3434

35+
#define RETURN_STATUS_IF_FALSE(env, condition, status) \
36+
do { \
37+
if (!(condition)) { \
38+
return napi_set_last_error((env), (status)); \
39+
} \
40+
} while (0)
41+
42+
#define CHECK_ENV(env) \
43+
if ((env) == nullptr) { \
44+
node::FatalError(__func__, "environment(env) must not be null"); \
45+
}
46+
47+
#define CHECK_ARG(env, arg) \
48+
RETURN_STATUS_IF_FALSE((env), ((arg) != nullptr), napi_invalid_arg)
49+
50+
#define CHECK_MAYBE_EMPTY(env, maybe, status) \
51+
RETURN_STATUS_IF_FALSE((env), !((maybe).IsEmpty()), (status))
52+
53+
#define CHECK_MAYBE_NOTHING(env, maybe, status) \
54+
RETURN_STATUS_IF_FALSE((env), !((maybe).IsNothing()), (status))
55+
56+
// NAPI_PREAMBLE is not wrapped in do..while: try_catch must have function scope
57+
#define NAPI_PREAMBLE(env) \
58+
CHECK_ENV((env)); \
59+
RETURN_STATUS_IF_FALSE((env), (env)->last_exception.IsEmpty(), \
60+
napi_pending_exception); \
61+
napi_clear_last_error((env)); \
62+
v8impl::TryCatch try_catch((env))
63+
64+
#define CHECK_TO_TYPE(env, type, context, result, src, status) \
65+
do { \
66+
auto maybe = v8impl::V8LocalValueFromJsValue((src))->To##type((context)); \
67+
CHECK_MAYBE_EMPTY((env), maybe, (status)); \
68+
(result) = maybe.ToLocalChecked(); \
69+
} while (0)
70+
71+
#define CHECK_TO_OBJECT(env, context, result, src) \
72+
CHECK_TO_TYPE((env), Object, (context), (result), (src), napi_object_expected)
73+
74+
#define CHECK_TO_STRING(env, context, result, src) \
75+
CHECK_TO_TYPE((env), String, (context), (result), (src), napi_string_expected)
76+
77+
#define CHECK_TO_NUMBER(env, context, result, src) \
78+
CHECK_TO_TYPE((env), Number, (context), (result), (src), napi_number_expected)
79+
80+
#define CHECK_TO_BOOL(env, context, result, src) \
81+
CHECK_TO_TYPE((env), Boolean, (context), (result), (src), \
82+
napi_boolean_expected)
83+
84+
#define CHECK_NEW_FROM_UTF8_LEN(env, result, str, len) \
85+
do { \
86+
auto str_maybe = v8::String::NewFromUtf8( \
87+
(env)->isolate, (str), v8::NewStringType::kInternalized, (len)); \
88+
CHECK_MAYBE_EMPTY((env), str_maybe, napi_generic_failure); \
89+
result = str_maybe.ToLocalChecked(); \
90+
} while (0)
91+
92+
#define CHECK_NEW_FROM_UTF8(env, result, str) \
93+
CHECK_NEW_FROM_UTF8_LEN((env), (result), (str), -1)
94+
95+
#define GET_RETURN_STATUS(env) \
96+
(!try_catch.HasCaught() ? napi_ok \
97+
: napi_set_last_error((env), napi_pending_exception))
98+
3599
namespace v8impl {
36100

37101
// convert from n-api property attributes to v8::PropertyAttribute
@@ -123,6 +187,22 @@ v8::Local<v8::Value> V8LocalValueFromJsValue(napi_value v) {
123187
return local;
124188
}
125189

190+
napi_status V8NameFromPropertyDescriptor(napi_env env,
191+
const napi_property_descriptor* p,
192+
v8::Local<v8::Name>* result) {
193+
if (p->utf8name != nullptr) {
194+
CHECK_NEW_FROM_UTF8(env, *result, p->utf8name);
195+
} else {
196+
v8::Local<v8::Value> property_value =
197+
v8impl::V8LocalValueFromJsValue(p->name);
198+
199+
RETURN_STATUS_IF_FALSE(env, property_value->IsName(), napi_name_expected);
200+
*result = property_value.As<v8::Name>();
201+
}
202+
203+
return napi_ok;
204+
}
205+
126206
// Adapter for napi_finalize callbacks.
127207
class Finalizer {
128208
protected:
@@ -357,13 +437,19 @@ class CallbackWrapperBase : public CallbackWrapper {
357437
v8::Local<v8::External>::Cast(
358438
_cbdata->GetInternalField(kInternalFieldIndex))->Value());
359439
v8::Isolate* isolate = _cbinfo.GetIsolate();
440+
360441
napi_env env = static_cast<napi_env>(
361442
v8::Local<v8::External>::Cast(
362443
_cbdata->GetInternalField(kEnvIndex))->Value());
363444

364445
// Make sure any errors encountered last time we were in N-API are gone.
365446
napi_clear_last_error(env);
366-
cb(env, cbinfo_wrapper);
447+
448+
napi_value result = cb(env, cbinfo_wrapper);
449+
450+
if (result != nullptr) {
451+
this->SetReturnValue(result);
452+
}
367453

368454
if (!env->last_exception.IsEmpty()) {
369455
isolate->ThrowException(
@@ -604,75 +690,12 @@ void napi_module_register(napi_module* mod) {
604690
node::node_module_register(nm);
605691
}
606692

607-
#define RETURN_STATUS_IF_FALSE(env, condition, status) \
608-
do { \
609-
if (!(condition)) { \
610-
return napi_set_last_error((env), (status)); \
611-
} \
612-
} while (0)
613-
614-
#define CHECK_ENV(env) \
615-
if ((env) == nullptr) { \
616-
node::FatalError(__func__, "environment(env) must not be null"); \
617-
}
618-
619-
#define CHECK_ARG(env, arg) \
620-
RETURN_STATUS_IF_FALSE((env), ((arg) != nullptr), napi_invalid_arg)
621-
622-
#define CHECK_MAYBE_EMPTY(env, maybe, status) \
623-
RETURN_STATUS_IF_FALSE((env), !((maybe).IsEmpty()), (status))
624-
625-
#define CHECK_MAYBE_NOTHING(env, maybe, status) \
626-
RETURN_STATUS_IF_FALSE((env), !((maybe).IsNothing()), (status))
627-
628-
// NAPI_PREAMBLE is not wrapped in do..while: try_catch must have function scope
629-
#define NAPI_PREAMBLE(env) \
630-
CHECK_ENV((env)); \
631-
RETURN_STATUS_IF_FALSE((env), (env)->last_exception.IsEmpty(), \
632-
napi_pending_exception); \
633-
napi_clear_last_error((env)); \
634-
v8impl::TryCatch try_catch((env))
635-
636-
#define CHECK_TO_TYPE(env, type, context, result, src, status) \
637-
do { \
638-
auto maybe = v8impl::V8LocalValueFromJsValue((src))->To##type((context)); \
639-
CHECK_MAYBE_EMPTY((env), maybe, (status)); \
640-
(result) = maybe.ToLocalChecked(); \
641-
} while (0)
642-
643-
#define CHECK_TO_OBJECT(env, context, result, src) \
644-
CHECK_TO_TYPE((env), Object, (context), (result), (src), napi_object_expected)
645-
646-
#define CHECK_TO_STRING(env, context, result, src) \
647-
CHECK_TO_TYPE((env), String, (context), (result), (src), napi_string_expected)
648-
649-
#define CHECK_TO_NUMBER(env, context, result, src) \
650-
CHECK_TO_TYPE((env), Number, (context), (result), (src), napi_number_expected)
651-
652-
#define CHECK_TO_BOOL(env, context, result, src) \
653-
CHECK_TO_TYPE((env), Boolean, (context), (result), (src), \
654-
napi_boolean_expected)
655-
656-
#define CHECK_NEW_FROM_UTF8_LEN(env, result, str, len) \
657-
do { \
658-
auto str_maybe = v8::String::NewFromUtf8( \
659-
(env)->isolate, (str), v8::NewStringType::kInternalized, (len)); \
660-
CHECK_MAYBE_EMPTY((env), str_maybe, napi_generic_failure); \
661-
result = str_maybe.ToLocalChecked(); \
662-
} while (0)
663-
664-
#define CHECK_NEW_FROM_UTF8(env, result, str) \
665-
CHECK_NEW_FROM_UTF8_LEN((env), (result), (str), -1)
666-
667-
#define GET_RETURN_STATUS(env) \
668-
(!try_catch.HasCaught() ? napi_ok \
669-
: napi_set_last_error((env), napi_pending_exception))
670-
671693
// Warning: Keep in-sync with napi_status enum
672694
const char* error_messages[] = {nullptr,
673695
"Invalid pointer passed as argument",
674696
"An object was expected",
675697
"A string was expected",
698+
"A string or symbol was expected",
676699
"A function was expected",
677700
"A number was expected",
678701
"A boolean was expected",
@@ -789,10 +812,16 @@ napi_status napi_define_class(napi_env env,
789812
continue;
790813
}
791814

792-
v8::Local<v8::String> property_name;
793-
CHECK_NEW_FROM_UTF8(env, property_name, p->utf8name);
815+
v8::Local<v8::Name> property_name;
816+
napi_status status =
817+
v8impl::V8NameFromPropertyDescriptor(env, p, &property_name);
818+
819+
if (status != napi_ok) {
820+
return napi_set_last_error(env, status);
821+
}
822+
794823
v8::PropertyAttribute attributes =
795-
v8impl::V8PropertyAttributesFromDescriptor(p);
824+
v8impl::V8PropertyAttributesFromDescriptor(p);
796825

797826
// This code is similar to that in napi_define_properties(); the
798827
// difference is it applies to a template instead of an object.
@@ -809,7 +838,7 @@ napi_status napi_define_class(napi_env env,
809838
attributes);
810839
} else if (p->method != nullptr) {
811840
v8::Local<v8::Object> cbdata =
812-
v8impl::CreateFunctionCallbackData(env, p->method, p->data);
841+
v8impl::CreateFunctionCallbackData(env, p->method, p->data);
813842

814843
RETURN_STATUS_IF_FALSE(env, !cbdata.IsEmpty(), napi_generic_failure);
815844

@@ -818,7 +847,6 @@ napi_status napi_define_class(napi_env env,
818847
v8impl::FunctionCallbackWrapper::Invoke,
819848
cbdata,
820849
v8::Signature::New(isolate, tpl));
821-
t->SetClassName(property_name);
822850

823851
tpl->PrototypeTemplate()->Set(property_name, t, attributes);
824852
} else {
@@ -851,18 +879,6 @@ napi_status napi_define_class(napi_env env,
851879
return GET_RETURN_STATUS(env);
852880
}
853881

854-
napi_status napi_set_return_value(napi_env env,
855-
napi_callback_info cbinfo,
856-
napi_value value) {
857-
NAPI_PREAMBLE(env);
858-
859-
v8impl::CallbackWrapper* info =
860-
reinterpret_cast<v8impl::CallbackWrapper*>(cbinfo);
861-
862-
info->SetReturnValue(value);
863-
return GET_RETURN_STATUS(env);
864-
}
865-
866882
napi_status napi_get_property_names(napi_env env,
867883
napi_value object,
868884
napi_value* result) {
@@ -1098,8 +1114,13 @@ napi_status napi_define_properties(napi_env env,
10981114
for (size_t i = 0; i < property_count; i++) {
10991115
const napi_property_descriptor* p = &properties[i];
11001116

1101-
v8::Local<v8::Name> name;
1102-
CHECK_NEW_FROM_UTF8(env, name, p->utf8name);
1117+
v8::Local<v8::Name> property_name;
1118+
napi_status status =
1119+
v8impl::V8NameFromPropertyDescriptor(env, p, &property_name);
1120+
1121+
if (status != napi_ok) {
1122+
return napi_set_last_error(env, status);
1123+
}
11031124

11041125
v8::PropertyAttribute attributes =
11051126
v8impl::V8PropertyAttributesFromDescriptor(p);
@@ -1113,7 +1134,7 @@ napi_status napi_define_properties(napi_env env,
11131134

11141135
auto set_maybe = obj->SetAccessor(
11151136
context,
1116-
name,
1137+
property_name,
11171138
p->getter ? v8impl::GetterCallbackWrapper::Invoke : nullptr,
11181139
p->setter ? v8impl::SetterCallbackWrapper::Invoke : nullptr,
11191140
cbdata,
@@ -1132,8 +1153,8 @@ napi_status napi_define_properties(napi_env env,
11321153
v8::Local<v8::FunctionTemplate> t = v8::FunctionTemplate::New(
11331154
isolate, v8impl::FunctionCallbackWrapper::Invoke, cbdata);
11341155

1135-
auto define_maybe =
1136-
obj->DefineOwnProperty(context, name, t->GetFunction(), attributes);
1156+
auto define_maybe = obj->DefineOwnProperty(
1157+
context, property_name, t->GetFunction(), attributes);
11371158

11381159
if (!define_maybe.FromMaybe(false)) {
11391160
return napi_set_last_error(env, napi_generic_failure);
@@ -1142,7 +1163,7 @@ napi_status napi_define_properties(napi_env env,
11421163
v8::Local<v8::Value> value = v8impl::V8LocalValueFromJsValue(p->value);
11431164

11441165
auto define_maybe =
1145-
obj->DefineOwnProperty(context, name, value, attributes);
1166+
obj->DefineOwnProperty(context, property_name, value, attributes);
11461167

11471168
if (!define_maybe.FromMaybe(false)) {
11481169
return napi_set_last_error(env, napi_invalid_arg);
@@ -1435,33 +1456,24 @@ napi_status napi_get_cb_info(
14351456
napi_value* this_arg, // [out] Receives the JS 'this' arg for the call
14361457
void** data) { // [out] Receives the data pointer for the callback.
14371458
CHECK_ENV(env);
1438-
CHECK_ARG(env, argc);
1439-
CHECK_ARG(env, argv);
1440-
CHECK_ARG(env, this_arg);
1441-
CHECK_ARG(env, data);
14421459

14431460
v8impl::CallbackWrapper* info =
14441461
reinterpret_cast<v8impl::CallbackWrapper*>(cbinfo);
14451462

1446-
info->Args(argv, std::min(*argc, info->ArgsLength()));
1447-
*argc = info->ArgsLength();
1448-
*this_arg = info->This();
1449-
*data = info->Data();
1450-
1451-
return napi_ok;
1452-
}
1453-
1454-
napi_status napi_get_cb_args_length(napi_env env,
1455-
napi_callback_info cbinfo,
1456-
size_t* result) {
1457-
// Omit NAPI_PREAMBLE and GET_RETURN_STATUS because no V8 APIs are called.
1458-
CHECK_ENV(env);
1459-
CHECK_ARG(env, result);
1460-
1461-
v8impl::CallbackWrapper* info =
1462-
reinterpret_cast<v8impl::CallbackWrapper*>(cbinfo);
1463+
if (argv != nullptr) {
1464+
CHECK_ARG(env, argc);
1465+
info->Args(argv, std::min(*argc, info->ArgsLength()));
1466+
}
1467+
if (argc != nullptr) {
1468+
*argc = info->ArgsLength();
1469+
}
1470+
if (this_arg != nullptr) {
1471+
*this_arg = info->This();
1472+
}
1473+
if (data != nullptr) {
1474+
*data = info->Data();
1475+
}
14631476

1464-
*result = info->ArgsLength();
14651477
return napi_ok;
14661478
}
14671479

@@ -1479,51 +1491,6 @@ napi_status napi_is_construct_call(napi_env env,
14791491
return napi_ok;
14801492
}
14811493

1482-
// copy encoded arguments into provided buffer or return direct pointer to
1483-
// encoded arguments array?
1484-
napi_status napi_get_cb_args(napi_env env,
1485-
napi_callback_info cbinfo,
1486-
napi_value* buf,
1487-
size_t bufsize) {
1488-
// Omit NAPI_PREAMBLE and GET_RETURN_STATUS because no V8 APIs are called.
1489-
CHECK_ENV(env);
1490-
CHECK_ARG(env, buf);
1491-
1492-
v8impl::CallbackWrapper* info =
1493-
reinterpret_cast<v8impl::CallbackWrapper*>(cbinfo);
1494-
1495-
info->Args(buf, bufsize);
1496-
return napi_ok;
1497-
}
1498-
1499-
napi_status napi_get_cb_this(napi_env env,
1500-
napi_callback_info cbinfo,
1501-
napi_value* result) {
1502-
// Omit NAPI_PREAMBLE and GET_RETURN_STATUS because no V8 APIs are called.
1503-
CHECK_ENV(env);
1504-
CHECK_ARG(env, result);
1505-
1506-
v8impl::CallbackWrapper* info =
1507-
reinterpret_cast<v8impl::CallbackWrapper*>(cbinfo);
1508-
1509-
*result = info->This();
1510-
return napi_ok;
1511-
}
1512-
1513-
napi_status napi_get_cb_data(napi_env env,
1514-
napi_callback_info cbinfo,
1515-
void** result) {
1516-
// Omit NAPI_PREAMBLE and GET_RETURN_STATUS because no V8 APIs are called.
1517-
CHECK_ENV(env);
1518-
CHECK_ARG(env, result);
1519-
1520-
v8impl::CallbackWrapper* info =
1521-
reinterpret_cast<v8impl::CallbackWrapper*>(cbinfo);
1522-
1523-
*result = info->Data();
1524-
return napi_ok;
1525-
}
1526-
15271494
napi_status napi_call_function(napi_env env,
15281495
napi_value recv,
15291496
napi_value func,

0 commit comments

Comments
 (0)