Skip to content

Commit c560db9

Browse files
jasonginMylesBorins
authored andcommittedApr 16, 2018
n-api: Enable scope and ref APIs during exception
N-API is somewhat strict about blocking calls to many APIs while there is a pending exception. The NAPI_PREAMBLE macro at the beginning of many API implementations checks for a pending exception. However, a subset of the APIs (which don't call back into JavaScript) still need to work while in a pending-exception state. This changes the reference APIs (equivalent to v8::Persistent) and handle scope APIs so that they can be used for cleanup up while an exception is pending. We may decide to similarly enable a few other APIs later, (which would be a non-breaking change) but we know at least these are needed now to unblock some specific scenarios. Fixes: nodejs/abi-stable-node#122 Fixes: nodejs/abi-stable-node#228 Backport-PR-URL: #19447 PR-URL: #12524 Reviewed-By: Anna Henningsen <anna@addaleax.net> Reviewed-By: Michael Dawson <michael_dawson@ca.ibm.com> Reviewed-By: Gabriel Schulhof <gabriel.schulhof@intel.com>
1 parent 8287e76 commit c560db9

File tree

3 files changed

+72
-20
lines changed

3 files changed

+72
-20
lines changed
 

‎src/node_api.cc

+40-20
Original file line numberDiff line numberDiff line change
@@ -2044,15 +2044,17 @@ napi_status napi_create_reference(napi_env env,
20442044
napi_value value,
20452045
uint32_t initial_refcount,
20462046
napi_ref* result) {
2047-
NAPI_PREAMBLE(env);
2047+
// Omit NAPI_PREAMBLE and GET_RETURN_STATUS because V8 calls here cannot throw
2048+
// JS exceptions.
2049+
CHECK_ENV(env);
20482050
CHECK_ARG(env, value);
20492051
CHECK_ARG(env, result);
20502052

20512053
v8impl::Reference* reference = v8impl::Reference::New(
20522054
env, v8impl::V8LocalValueFromJsValue(value), initial_refcount, false);
20532055

20542056
*result = reinterpret_cast<napi_ref>(reference);
2055-
return GET_RETURN_STATUS(env);
2057+
return napi_clear_last_error(env);
20562058
}
20572059

20582060
// Deletes a reference. The referenced value is released, and may be GC'd unless
@@ -2074,7 +2076,9 @@ napi_status napi_delete_reference(napi_env env, napi_ref ref) {
20742076
// Calling this when the refcount is 0 and the object is unavailable
20752077
// results in an error.
20762078
napi_status napi_reference_ref(napi_env env, napi_ref ref, uint32_t* result) {
2077-
NAPI_PREAMBLE(env);
2079+
// Omit NAPI_PREAMBLE and GET_RETURN_STATUS because V8 calls here cannot throw
2080+
// JS exceptions.
2081+
CHECK_ENV(env);
20782082
CHECK_ARG(env, ref);
20792083

20802084
v8impl::Reference* reference = reinterpret_cast<v8impl::Reference*>(ref);
@@ -2084,15 +2088,17 @@ napi_status napi_reference_ref(napi_env env, napi_ref ref, uint32_t* result) {
20842088
*result = count;
20852089
}
20862090

2087-
return GET_RETURN_STATUS(env);
2091+
return napi_clear_last_error(env);
20882092
}
20892093

20902094
// Decrements the reference count, optionally returning the resulting count. If
20912095
// the result is 0 the reference is now weak and the object may be GC'd at any
20922096
// time if there are no other references. Calling this when the refcount is
20932097
// already 0 results in an error.
20942098
napi_status napi_reference_unref(napi_env env, napi_ref ref, uint32_t* result) {
2095-
NAPI_PREAMBLE(env);
2099+
// Omit NAPI_PREAMBLE and GET_RETURN_STATUS because V8 calls here cannot throw
2100+
// JS exceptions.
2101+
CHECK_ENV(env);
20962102
CHECK_ARG(env, ref);
20972103

20982104
v8impl::Reference* reference = reinterpret_cast<v8impl::Reference*>(ref);
@@ -2107,7 +2113,7 @@ napi_status napi_reference_unref(napi_env env, napi_ref ref, uint32_t* result) {
21072113
*result = count;
21082114
}
21092115

2110-
return GET_RETURN_STATUS(env);
2116+
return napi_clear_last_error(env);
21112117
}
21122118

21132119
// Attempts to get a referenced value. If the reference is weak, the value might
@@ -2116,59 +2122,71 @@ napi_status napi_reference_unref(napi_env env, napi_ref ref, uint32_t* result) {
21162122
napi_status napi_get_reference_value(napi_env env,
21172123
napi_ref ref,
21182124
napi_value* result) {
2119-
NAPI_PREAMBLE(env);
2125+
// Omit NAPI_PREAMBLE and GET_RETURN_STATUS because V8 calls here cannot throw
2126+
// JS exceptions.
2127+
CHECK_ENV(env);
21202128
CHECK_ARG(env, ref);
21212129
CHECK_ARG(env, result);
21222130

21232131
v8impl::Reference* reference = reinterpret_cast<v8impl::Reference*>(ref);
21242132
*result = v8impl::JsValueFromV8LocalValue(reference->Get());
21252133

2126-
return GET_RETURN_STATUS(env);
2134+
return napi_clear_last_error(env);
21272135
}
21282136

21292137
napi_status napi_open_handle_scope(napi_env env, napi_handle_scope* result) {
2130-
NAPI_PREAMBLE(env);
2138+
// Omit NAPI_PREAMBLE and GET_RETURN_STATUS because V8 calls here cannot throw
2139+
// JS exceptions.
2140+
CHECK_ENV(env);
21312141
CHECK_ARG(env, result);
21322142

21332143
*result = v8impl::JsHandleScopeFromV8HandleScope(
21342144
new v8impl::HandleScopeWrapper(env->isolate));
2135-
return GET_RETURN_STATUS(env);
2145+
return napi_clear_last_error(env);
21362146
}
21372147

21382148
napi_status napi_close_handle_scope(napi_env env, napi_handle_scope scope) {
2139-
NAPI_PREAMBLE(env);
2149+
// Omit NAPI_PREAMBLE and GET_RETURN_STATUS because V8 calls here cannot throw
2150+
// JS exceptions.
2151+
CHECK_ENV(env);
21402152
CHECK_ARG(env, scope);
21412153

21422154
delete v8impl::V8HandleScopeFromJsHandleScope(scope);
2143-
return GET_RETURN_STATUS(env);
2155+
return napi_clear_last_error(env);
21442156
}
21452157

21462158
napi_status napi_open_escapable_handle_scope(
21472159
napi_env env,
21482160
napi_escapable_handle_scope* result) {
2149-
NAPI_PREAMBLE(env);
2161+
// Omit NAPI_PREAMBLE and GET_RETURN_STATUS because V8 calls here cannot throw
2162+
// JS exceptions.
2163+
CHECK_ENV(env);
21502164
CHECK_ARG(env, result);
21512165

21522166
*result = v8impl::JsEscapableHandleScopeFromV8EscapableHandleScope(
21532167
new v8impl::EscapableHandleScopeWrapper(env->isolate));
2154-
return GET_RETURN_STATUS(env);
2168+
return napi_clear_last_error(env);
21552169
}
21562170

21572171
napi_status napi_close_escapable_handle_scope(
21582172
napi_env env,
21592173
napi_escapable_handle_scope scope) {
2160-
NAPI_PREAMBLE(env);
2174+
// Omit NAPI_PREAMBLE and GET_RETURN_STATUS because V8 calls here cannot throw
2175+
// JS exceptions.
2176+
CHECK_ENV(env);
21612177
CHECK_ARG(env, scope);
21622178

21632179
delete v8impl::V8EscapableHandleScopeFromJsEscapableHandleScope(scope);
2164-
return GET_RETURN_STATUS(env);
2180+
return napi_clear_last_error(env);
21652181
}
21662182

21672183
napi_status napi_escape_handle(napi_env env,
21682184
napi_escapable_handle_scope scope,
21692185
napi_value escapee,
21702186
napi_value* result) {
2171-
NAPI_PREAMBLE(env);
2187+
// Omit NAPI_PREAMBLE and GET_RETURN_STATUS because V8 calls here cannot throw
2188+
// JS exceptions.
2189+
CHECK_ENV(env);
21722190
CHECK_ARG(env, scope);
21732191
CHECK_ARG(env, escapee);
21742192
CHECK_ARG(env, result);
@@ -2177,7 +2195,7 @@ napi_status napi_escape_handle(napi_env env,
21772195
v8impl::V8EscapableHandleScopeFromJsEscapableHandleScope(scope);
21782196
*result = v8impl::JsValueFromV8LocalValue(
21792197
s->Escape(v8impl::V8LocalValueFromJsValue(escapee)));
2180-
return GET_RETURN_STATUS(env);
2198+
return napi_clear_last_error(env);
21812199
}
21822200

21832201
napi_status napi_new_instance(napi_env env,
@@ -2387,7 +2405,6 @@ napi_status napi_create_buffer(napi_env env,
23872405
void** data,
23882406
napi_value* result) {
23892407
NAPI_PREAMBLE(env);
2390-
CHECK_ARG(env, data);
23912408
CHECK_ARG(env, result);
23922409

23932410
auto maybe = node::Buffer::New(env->isolate, length);
@@ -2397,7 +2414,10 @@ napi_status napi_create_buffer(napi_env env,
23972414
v8::Local<v8::Object> buffer = maybe.ToLocalChecked();
23982415

23992416
*result = v8impl::JsValueFromV8LocalValue(buffer);
2400-
*data = node::Buffer::Data(buffer);
2417+
2418+
if (data != nullptr) {
2419+
*data = node::Buffer::Data(buffer);
2420+
}
24012421

24022422
return GET_RETURN_STATUS(env);
24032423
}

‎test/addons-napi/test_handle_scope/test.js

+7
Original file line numberDiff line numberDiff line change
@@ -7,4 +7,11 @@ const testHandleScope =
77
require(`./build/${common.buildType}/test_handle_scope`);
88

99
testHandleScope.NewScope();
10+
1011
assert.ok(testHandleScope.NewScopeEscape() instanceof Object);
12+
13+
assert.throws(
14+
() => {
15+
testHandleScope.NewScopeWithException(() => { throw new RangeError(); });
16+
},
17+
RangeError);

‎test/addons-napi/test_handle_scope/test_handle_scope.c

+25
Original file line numberDiff line numberDiff line change
@@ -29,10 +29,35 @@ napi_value NewScopeEscape(napi_env env, napi_callback_info info) {
2929
return escapee;
3030
}
3131

32+
napi_value NewScopeWithException(napi_env env, napi_callback_info info) {
33+
napi_handle_scope scope;
34+
size_t argc;
35+
napi_value exception_function;
36+
napi_status status;
37+
napi_value output = NULL;
38+
39+
NAPI_CALL(env, napi_open_handle_scope(env, &scope));
40+
NAPI_CALL(env, napi_create_object(env, &output));
41+
42+
argc = 1;
43+
NAPI_CALL(env, napi_get_cb_info(
44+
env, info, &argc, &exception_function, NULL, NULL));
45+
46+
status = napi_call_function(
47+
env, output, exception_function, 0, NULL, NULL);
48+
NAPI_ASSERT(env, status == napi_pending_exception,
49+
"Function should have thrown.");
50+
51+
// Closing a handle scope should still work while an exception is pending.
52+
NAPI_CALL(env, napi_close_handle_scope(env, scope));
53+
return NULL;
54+
}
55+
3256
void Init(napi_env env, napi_value exports, napi_value module, void* priv) {
3357
napi_property_descriptor properties[] = {
3458
DECLARE_NAPI_PROPERTY("NewScope", NewScope),
3559
DECLARE_NAPI_PROPERTY("NewScopeEscape", NewScopeEscape),
60+
DECLARE_NAPI_PROPERTY("NewScopeWithException", NewScopeWithException),
3661
};
3762

3863
NAPI_CALL_RETURN_VOID(env, napi_define_properties(

0 commit comments

Comments
 (0)
Please sign in to comment.