| FazBrowse GitHub Viewer | Trending | | Home |
| Tools: [Download Repo ZIP] [Original HTTPS Page] |
1 parent 7555dee commit b0a3a44
3 files changed
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -167,6 +167,23 @@ struct napi_env__ { | |||
| 167 | 167 | (out) = v8::type::New((buffer), (byte_offset), (length)); \ | |
| 168 | 168 | } while (0) | |
| 169 | 169 | ||
| 170 | + #define NAPI_CALL_INTO_MODULE(env, call, handle_exception) \ | ||
| 171 | + do { \ | ||
| 172 | + int open_handle_scopes = (env)->open_handle_scopes; \ | ||
| 173 | + int open_callback_scopes = (env)->open_callback_scopes; \ | ||
| 174 | + napi_clear_last_error((env)); \ | ||
| 175 | + call; \ | ||
| 176 | + CHECK_EQ((env)->open_handle_scopes, open_handle_scopes); \ | ||
| 177 | + CHECK_EQ((env)->open_callback_scopes, open_callback_scopes); \ | ||
| 178 | + if (!(env)->last_exception.IsEmpty()) { \ | ||
| 179 | + handle_exception( \ | ||
| 180 | + v8::Local<v8::Value>::New((env)->isolate, (env)->last_exception)); \ | ||
| 181 | + (env)->last_exception.Reset(); \ | ||
| 182 | + } \ | ||
| 183 | + } while (0) | ||
| 184 | + | ||
| 185 | + #define NAPI_CALL_INTO_MODULE_THROW(env, call) \ | ||
| 186 | + NAPI_CALL_INTO_MODULE((env), call, (env)->isolate->ThrowException) | ||
| 170 | 187 | ||
| 171 | 188 | namespace { | |
| 172 | 189 | namespace v8impl { | |
@@ -346,10 +363,11 @@ class Finalizer { | |||
| 346 | 363 | static void FinalizeBufferCallback(char* data, void* hint) { | |
| 347 | 364 | Finalizer* finalizer = static_cast<Finalizer*>(hint); | |
| 348 | 365 | if (finalizer->_finalize_callback != nullptr) { | |
| 349 | - finalizer->_finalize_callback( | ||
| 350 | - finalizer->_env, | ||
| 351 | - data, | ||
| 352 | - finalizer->_finalize_hint); | ||
| 366 | + NAPI_CALL_INTO_MODULE_THROW(finalizer->_env, | ||
| 367 | + finalizer->_finalize_callback( | ||
| 368 | + finalizer->_env, | ||
| 369 | + data, | ||
| 370 | + finalizer->_finalize_hint)); | ||
| 353 | 371 | } | |
| 354 | 372 | ||
| 355 | 373 | Delete(finalizer); | |
@@ -451,10 +469,11 @@ class Reference : private Finalizer { | |||
| 451 | 469 | bool delete_self = reference->_delete_self; | |
| 452 | 470 | ||
| 453 | 471 | if (reference->_finalize_callback != nullptr) { | |
| 454 | - reference->_finalize_callback( | ||
| 455 | - reference->_env, | ||
| 456 | - reference->_finalize_data, | ||
| 457 | - reference->_finalize_hint); | ||
| 472 | + NAPI_CALL_INTO_MODULE_THROW(reference->_env, | ||
| 473 | + reference->_finalize_callback( | ||
| 474 | + reference->_env, | ||
| 475 | + reference->_finalize_data, | ||
| 476 | + reference->_finalize_hint)); | ||
| 458 | 477 | } | |
| 459 | 478 | ||
| 460 | 479 | if (delete_self) { | |
@@ -539,32 +558,17 @@ class CallbackWrapperBase : public CallbackWrapper { | |||
| 539 | 558 | napi_callback cb = reinterpret_cast<napi_callback>( | |
| 540 | 559 | v8::Local<v8::External>::Cast( | |
| 541 | 560 | _cbdata->GetInternalField(kInternalFieldIndex))->Value()); | |
| 542 | - v8::Isolate* isolate = _cbinfo.GetIsolate(); | ||
| 543 | 561 | ||
| 544 | 562 | napi_env env = static_cast<napi_env>( | |
| 545 | 563 | v8::Local<v8::External>::Cast( | |
| 546 | 564 | _cbdata->GetInternalField(kEnvIndex))->Value()); | |
| 547 | 565 | ||
| 548 | - // Make sure any errors encountered last time we were in N-API are gone. | ||
| 549 | - napi_clear_last_error(env); | ||
| 550 | - | ||
| 551 | - int open_handle_scopes = env->open_handle_scopes; | ||
| 552 | - int open_callback_scopes = env->open_callback_scopes; | ||
| 553 | - | ||
| 554 | - napi_value result = cb(env, cbinfo_wrapper); | ||
| 566 | + napi_value result; | ||
| 567 | + NAPI_CALL_INTO_MODULE_THROW(env, result = cb(env, cbinfo_wrapper)); | ||
| 555 | 568 | ||
| 556 | 569 | if (result != nullptr) { | |
| 557 | 570 | this->SetReturnValue(result); | |
| 558 | 571 | } | |
| 559 | - | ||
| 560 | - CHECK_EQ(env->open_handle_scopes, open_handle_scopes); | ||
| 561 | - CHECK_EQ(env->open_callback_scopes, open_callback_scopes); | ||
| 562 | - | ||
| 563 | - if (!env->last_exception.IsEmpty()) { | ||
| 564 | - isolate->ThrowException( | ||
| 565 | - v8::Local<v8::Value>::New(isolate, env->last_exception)); | ||
| 566 | - env->last_exception.Reset(); | ||
| 567 | - } | ||
| 568 | 572 | } | |
| 569 | 573 | ||
| 570 | 574 | const Info& _cbinfo; | |
@@ -871,8 +875,10 @@ void napi_module_register_cb(v8::Local<v8::Object> exports, | |||
| 871 | 875 | // one is found. | |
| 872 | 876 | napi_env env = v8impl::GetEnv(context); | |
| 873 | 877 | ||
| 874 | - napi_value _exports = | ||
| 875 | - mod->nm_register_func(env, v8impl::JsValueFromV8LocalValue(exports)); | ||
| 878 | + napi_value _exports; | ||
| 879 | + NAPI_CALL_INTO_MODULE_THROW(env, | ||
| 880 | + _exports = mod->nm_register_func(env, | ||
| 881 | + v8impl::JsValueFromV8LocalValue(exports))); | ||
| 876 | 882 | ||
| 877 | 883 | // If register function returned a non-null exports object different from | |
| 878 | 884 | // the exports object we passed it, set that as the "exports" property of | |
@@ -3367,19 +3373,17 @@ class Work : public node::AsyncResource { | |||
| 3367 | 3373 | v8::HandleScope scope(env->isolate); | |
| 3368 | 3374 | CallbackScope callback_scope(work); | |
| 3369 | 3375 | ||
| 3370 | - work->_complete(env, ConvertUVErrorCode(status), work->_data); | ||
| 3376 | + NAPI_CALL_INTO_MODULE(env, | ||
| 3377 | + work->_complete(env, ConvertUVErrorCode(status), work->_data), | ||
| 3378 | + [env] (v8::Local<v8::Value> local_err) { | ||
| 3379 | + // If there was an unhandled exception in the complete callback, | ||
| 3380 | + // report it as a fatal exception. (There is no JavaScript on the | ||
| 3381 | + // callstack that can possibly handle it.) | ||
| 3382 | + v8impl::trigger_fatal_exception(env, local_err); | ||
| 3383 | + }); | ||
| 3371 | 3384 | ||
| 3372 | 3385 | // Note: Don't access `work` after this point because it was | |
| 3373 | 3386 | // likely deleted by the complete callback. | |
| 3374 | - | ||
| 3375 | - // If there was an unhandled exception in the complete callback, | ||
| 3376 | - // report it as a fatal exception. (There is no JavaScript on the | ||
| 3377 | - // callstack that can possibly handle it.) | ||
| 3378 | - if (!env->last_exception.IsEmpty()) { | ||
| 3379 | - v8::Local<v8::Value> local_err = v8::Local<v8::Value>::New( | ||
| 3380 | - env->isolate, env->last_exception); | ||
| 3381 | - v8impl::trigger_fatal_exception(env, local_err); | ||
| 3382 | - } | ||
| 3383 | 3387 | } | |
| 3384 | 3388 | } | |
| 3385 | 3389 | ||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -1,10 +1,26 @@ | |||
| 1 | 1 | 'use strict'; | |
| 2 | + // Flags: --expose-gc | ||
| 2 | 3 | ||
| 3 | 4 | const common = require('../../common'); | |
| 4 | - const test_exception = require(`./build/${common.buildType}/test_exception`); | ||
| 5 | 5 | const assert = require('assert'); | |
| 6 | 6 | const theError = new Error('Some error'); | |
| 7 | 7 | ||
| 8 | + // The test module throws an error during Init, but in order for its exports to | ||
| 9 | + // not be lost, it attaches them to the error's "bindings" property. This way, | ||
| 10 | + // we can make sure that exceptions thrown during the module initialization | ||
| 11 | + // phase are propagated through require() into JavaScript. | ||
| 12 | + // https://github.com/nodejs/node/issues/19437 | ||
| 13 | + const test_exception = (function() { | ||
| 14 | + let resultingException; | ||
| 15 | + try { | ||
| 16 | + require(`./build/${common.buildType}/test_exception`); | ||
| 17 | + } catch (anException) { | ||
| 18 | + resultingException = anException; | ||
| 19 | + } | ||
| 20 | + assert.strictEqual(resultingException.message, 'Error during Init'); | ||
| 21 | + return resultingException.binding; | ||
| 22 | + })(); | ||
| 23 | + | ||
| 8 | 24 | { | |
| 9 | 25 | const throwTheError = () => { throw theError; }; | |
| 10 | 26 | ||
@@ -50,3 +66,15 @@ const theError = new Error('Some error'); | |||
| 50 | 66 | 'Exception state did not remain clear as expected,' + | |
| 51 | 67 | ` .wasPending() returned ${exception_pending}`); | |
| 52 | 68 | } | |
| 69 | + | ||
| 70 | + // Make sure that exceptions that occur during finalization are propagated. | ||
| 71 | + function testFinalize(binding) { | ||
| 72 | + let x = test_exception[binding](); | ||
| 73 | + x = null; | ||
| 74 | + assert.throws(() => { global.gc(); }, /Error during Finalize/); | ||
| 75 | + | ||
| 76 | + // To assuage the linter's concerns. | ||
| 77 | + (function() {})(x); | ||
| 78 | + } | ||
| 79 | + testFinalize('createExternal'); | ||
| 80 | + testFinalize('createExternalBuffer'); | ||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -3,7 +3,7 @@ | |||
| 3 | 3 | ||
| 4 | 4 | static bool exceptionWasPending = false; | |
| 5 | 5 | ||
| 6 | - napi_value returnException(napi_env env, napi_callback_info info) { | ||
| 6 | + static napi_value returnException(napi_env env, napi_callback_info info) { | ||
| 7 | 7 | size_t argc = 1; | |
| 8 | 8 | napi_value args[1]; | |
| 9 | 9 | NAPI_CALL(env, napi_get_cb_info(env, info, &argc, args, NULL, NULL)); | |
@@ -22,7 +22,7 @@ napi_value returnException(napi_env env, napi_callback_info info) { | |||
| 22 | 22 | return NULL; | |
| 23 | 23 | } | |
| 24 | 24 | ||
| 25 | - napi_value allowException(napi_env env, napi_callback_info info) { | ||
| 25 | + static napi_value allowException(napi_env env, napi_callback_info info) { | ||
| 26 | 26 | size_t argc = 1; | |
| 27 | 27 | napi_value args[1]; | |
| 28 | 28 | NAPI_CALL(env, napi_get_cb_info(env, info, &argc, args, NULL, NULL)); | |
@@ -38,23 +38,55 @@ napi_value allowException(napi_env env, napi_callback_info info) { | |||
| 38 | 38 | return NULL; | |
| 39 | 39 | } | |
| 40 | 40 | ||
| 41 | - napi_value wasPending(napi_env env, napi_callback_info info) { | ||
| 41 | + static napi_value wasPending(napi_env env, napi_callback_info info) { | ||
| 42 | 42 | napi_value result; | |
| 43 | 43 | NAPI_CALL(env, napi_get_boolean(env, exceptionWasPending, &result)); | |
| 44 | 44 | ||
| 45 | 45 | return result; | |
| 46 | 46 | } | |
| 47 | 47 | ||
| 48 | - napi_value Init(napi_env env, napi_value exports) { | ||
| 48 | + static void finalizer(napi_env env, void *data, void *hint) { | ||
| 49 | + NAPI_CALL_RETURN_VOID(env, | ||
| 50 | + napi_throw_error(env, NULL, "Error during Finalize")); | ||
| 51 | + } | ||
| 52 | + | ||
| 53 | + static napi_value createExternal(napi_env env, napi_callback_info info) { | ||
| 54 | + napi_value external; | ||
| 55 | + | ||
| 56 | + NAPI_CALL(env, | ||
| 57 | + napi_create_external(env, NULL, finalizer, NULL, &external)); | ||
| 58 | + | ||
| 59 | + return external; | ||
| 60 | + } | ||
| 61 | + | ||
| 62 | + static char buffer_data[12]; | ||
| 63 | + | ||
| 64 | + static napi_value createExternalBuffer(napi_env env, napi_callback_info info) { | ||
| 65 | + napi_value buffer; | ||
| 66 | + NAPI_CALL(env, napi_create_external_buffer(env, sizeof(buffer_data), | ||
| 67 | + buffer_data, finalizer, NULL, &buffer)); | ||
| 68 | + return buffer; | ||
| 69 | + } | ||
| 70 | + | ||
| 71 | + static napi_value Init(napi_env env, napi_value exports) { | ||
| 49 | 72 | napi_property_descriptor descriptors[] = { | |
| 50 | 73 | DECLARE_NAPI_PROPERTY("returnException", returnException), | |
| 51 | 74 | DECLARE_NAPI_PROPERTY("allowException", allowException), | |
| 52 | 75 | DECLARE_NAPI_PROPERTY("wasPending", wasPending), | |
| 76 | + DECLARE_NAPI_PROPERTY("createExternal", createExternal), | ||
| 77 | + DECLARE_NAPI_PROPERTY("createExternalBuffer", createExternalBuffer), | ||
| 53 | 78 | }; | |
| 54 | - | ||
| 55 | 79 | NAPI_CALL(env, napi_define_properties( | |
| 56 | 80 | env, exports, sizeof(descriptors) / sizeof(*descriptors), descriptors)); | |
| 57 | 81 | ||
| 82 | + napi_value error, code, message; | ||
| 83 | + NAPI_CALL(env, napi_create_string_utf8(env, "Error during Init", | ||
| 84 | + NAPI_AUTO_LENGTH, &message)); | ||
| 85 | + NAPI_CALL(env, napi_create_string_utf8(env, "", NAPI_AUTO_LENGTH, &code)); | ||
| 86 | + NAPI_CALL(env, napi_create_error(env, code, message, &error)); | ||
| 87 | + NAPI_CALL(env, napi_set_named_property(env, error, "binding", exports)); | ||
| 88 | + NAPI_CALL(env, napi_throw(env, error)); | ||
| 89 | + | ||
| 58 | 90 | return exports; | |
| 59 | 91 | } | |
| 60 | 92 | ||
| Back | FazBrowse Home | New Git URL |
0 commit comments