From 96874e8fbc5dc3da3c60ee26b598d9a72af46c57 Mon Sep 17 00:00:00 2001 From: Chengzhong Wu <legendecas@gmail.com> Date: Mon, 25 Sep 2023 12:23:58 +0800 Subject: [PATCH] node-api: enable uncaught exceptions policy by default This enables the option `--force-node-api-uncaught-exceptions-policy` for a specific Node-API addon when it is compiled with `NAPI_EXPERIMENTAL` (and this would be the default behavior when `NAPI_VERSION` 10 releases). This would not break existing Node-API addons. PR-URL: https://github.com/nodejs/node/pull/49313 Refs: https://github.com/nodejs/node/pull/36510 Reviewed-By: Michael Dawson <midawson@redhat.com> Reviewed-By: Gabriel Schulhof <gabrielschulhof@gmail.com> --- doc/api/n-api.md | 8 ++ src/node_api.cc | 16 ++-- test/js-native-api/test_reference/binding.gyp | 6 ++ .../test_reference/test_finalizer.c | 71 ++++++++++++++++++ .../test_reference/test_finalizer.js | 4 +- .../test_reference/test_reference.c | 75 +++++-------------- test/node-api/test_buffer/binding.gyp | 4 + test/node-api/test_buffer/test_buffer.c | 50 ++----------- test/node-api/test_buffer/test_finalizer.c | 61 +++++++++++++++ test/node-api/test_buffer/test_finalizer.js | 2 +- .../test_threadsafe_function/binding.gyp | 14 ++++ ...n.js => test_legacy_uncaught_exception.js} | 2 +- .../test_uncaught_exception.c | 62 +++++++++++++++ .../test_uncaught_exception.js | 26 +------ .../test_uncaught_exception_v9.js | 8 ++ .../uncaught_exception.js | 31 ++++++++ 16 files changed, 307 insertions(+), 133 deletions(-) create mode 100644 test/js-native-api/test_reference/test_finalizer.c create mode 100644 test/node-api/test_buffer/test_finalizer.c rename test/node-api/test_threadsafe_function/{test_force_uncaught_exception.js => test_legacy_uncaught_exception.js} (85%) create mode 100644 test/node-api/test_threadsafe_function/test_uncaught_exception.c create mode 100644 test/node-api/test_threadsafe_function/test_uncaught_exception_v9.js create mode 100644 test/node-api/test_threadsafe_function/uncaught_exception.js diff --git a/doc/api/n-api.md b/doc/api/n-api.md index 10b6dac61c4490..feb9fa6061ca4e 100644 --- a/doc/api/n-api.md +++ b/doc/api/n-api.md @@ -6245,6 +6245,13 @@ napi_create_threadsafe_function(napi_env env, [`napi_threadsafe_function_call_js`][] provides more details. * `[out] result`: The asynchronous thread-safe JavaScript function. +**Change History:** + +* Experimental (`NAPI_EXPERIMENTAL` is defined): + + Uncaught exceptions thrown in `call_js_cb` are handled with the + [`'uncaughtException'`][] event, instead of being ignored. + ### `napi_get_threadsafe_function_context` <!-- YAML @@ -6475,6 +6482,7 @@ the add-on's file name during loading. [Visual Studio]: https://visualstudio.microsoft.com [Working with JavaScript properties]: #working-with-javascript-properties [Xcode]: https://developer.apple.com/xcode/ +[`'uncaughtException'`]: process.md#event-uncaughtexception [`Number.MAX_SAFE_INTEGER`]: https://tc39.github.io/ecma262/#sec-number.max_safe_integer [`Number.MIN_SAFE_INTEGER`]: https://tc39.github.io/ecma262/#sec-number.min_safe_integer [`Worker`]: worker_threads.md#class-worker diff --git a/src/node_api.cc b/src/node_api.cc index 7537dc20b2bd82..368f05f3f4a261 100644 --- a/src/node_api.cc +++ b/src/node_api.cc @@ -82,9 +82,8 @@ void node_napi_env__::trigger_fatal_exception(v8::Local<v8::Value> local_err) { node::errors::TriggerUncaughtException(isolate, local_err, local_msg); } -// option enforceUncaughtExceptionPolicy is added for not breaking existing -// running n-api add-ons, and should be deprecated in the next major Node.js -// release. +// The option enforceUncaughtExceptionPolicy is added for not breaking existing +// running Node-API add-ons. template <bool enforceUncaughtExceptionPolicy, typename T> void node_napi_env__::CallbackIntoModule(T&& call) { CallIntoModule(call, [](napi_env env_, v8::Local<v8::Value> local_err) { @@ -93,19 +92,24 @@ void node_napi_env__::CallbackIntoModule(T&& call) { return; } node::Environment* node_env = env->node_env(); - if (!node_env->options()->force_node_api_uncaught_exceptions_policy && + // If the module api version is less than NAPI_VERSION_EXPERIMENTAL, + // and the option --force-node-api-uncaught-exceptions-policy is not + // specified, emit a warning about the uncaught exception instead of + // triggering uncaught exception event. + if (env->module_api_version < NAPI_VERSION_EXPERIMENTAL && + !node_env->options()->force_node_api_uncaught_exceptions_policy && !enforceUncaughtExceptionPolicy) { ProcessEmitDeprecationWarning( node_env, "Uncaught N-API callback exception detected, please run node " - "with option --force-node-api-uncaught-exceptions-policy=true" + "with option --force-node-api-uncaught-exceptions-policy=true " "to handle those exceptions properly.", "DEP0168"); return; } // If there was an unhandled exception in the complete callback, // report it as a fatal exception. (There is no JavaScript on the - // callstack that can possibly handle it.) + // call stack that can possibly handle it.) env->trigger_fatal_exception(local_err); }); } diff --git a/test/js-native-api/test_reference/binding.gyp b/test/js-native-api/test_reference/binding.gyp index d8940028915f15..a9d81ef9d2c05d 100644 --- a/test/js-native-api/test_reference/binding.gyp +++ b/test/js-native-api/test_reference/binding.gyp @@ -5,6 +5,12 @@ "sources": [ "test_reference.c" ] + }, + { + "target_name": "test_finalizer", + "sources": [ + "test_finalizer.c" + ] } ] } diff --git a/test/js-native-api/test_reference/test_finalizer.c b/test/js-native-api/test_reference/test_finalizer.c new file mode 100644 index 00000000000000..51492d9623f69c --- /dev/null +++ b/test/js-native-api/test_reference/test_finalizer.c @@ -0,0 +1,71 @@ +#include <assert.h> +#include <js_native_api.h> +#include <stdlib.h> +#include "../common.h" +#include "../entry_point.h" + +static int test_value = 1; +static int finalize_count = 0; + +static void FinalizeExternalCallJs(napi_env env, void* data, void* hint) { + int* actual_value = data; + NODE_API_ASSERT_RETURN_VOID( + env, + actual_value == &test_value, + "The correct pointer was passed to the finalizer"); + + napi_ref finalizer_ref = (napi_ref)hint; + napi_value js_finalizer; + napi_value recv; + NODE_API_CALL_RETURN_VOID( + env, napi_get_reference_value(env, finalizer_ref, &js_finalizer)); + NODE_API_CALL_RETURN_VOID(env, napi_get_global(env, &recv)); + NODE_API_CALL_RETURN_VOID( + env, napi_call_function(env, recv, js_finalizer, 0, NULL, NULL)); + NODE_API_CALL_RETURN_VOID(env, napi_delete_reference(env, finalizer_ref)); +} + +static napi_value CreateExternalWithJsFinalize(napi_env env, + napi_callback_info info) { + size_t argc = 1; + napi_value args[1]; + NODE_API_CALL(env, napi_get_cb_info(env, info, &argc, args, NULL, NULL)); + NODE_API_ASSERT(env, argc == 1, "Wrong number of arguments"); + napi_value finalizer = args[0]; + napi_valuetype finalizer_valuetype; + NODE_API_CALL(env, napi_typeof(env, finalizer, &finalizer_valuetype)); + NODE_API_ASSERT(env, + finalizer_valuetype == napi_function, + "Wrong type of first argument"); + napi_ref finalizer_ref; + NODE_API_CALL(env, napi_create_reference(env, finalizer, 1, &finalizer_ref)); + + napi_value result; + NODE_API_CALL(env, + napi_create_external(env, + &test_value, + FinalizeExternalCallJs, + finalizer_ref, /* finalize_hint */ + &result)); + + finalize_count = 0; + return result; +} + +EXTERN_C_START +napi_value Init(napi_env env, napi_value exports) { + napi_property_descriptor descriptors[] = { + DECLARE_NODE_API_PROPERTY("createExternalWithJsFinalize", + CreateExternalWithJsFinalize), + }; + + NODE_API_CALL( + env, + napi_define_properties(env, + exports, + sizeof(descriptors) / sizeof(*descriptors), + descriptors)); + + return exports; +} +EXTERN_C_END diff --git a/test/js-native-api/test_reference/test_finalizer.js b/test/js-native-api/test_reference/test_finalizer.js index b70582fd0342fe..a5270512dc87c1 100644 --- a/test/js-native-api/test_reference/test_finalizer.js +++ b/test/js-native-api/test_reference/test_finalizer.js @@ -2,7 +2,7 @@ // Flags: --expose-gc --force-node-api-uncaught-exceptions-policy const common = require('../../common'); -const test_reference = require(`./build/${common.buildType}/test_reference`); +const binding = require(`./build/${common.buildType}/test_finalizer`); const assert = require('assert'); process.on('uncaughtException', common.mustCall((err) => { @@ -11,7 +11,7 @@ process.on('uncaughtException', common.mustCall((err) => { (async function() { { - test_reference.createExternalWithJsFinalize( + binding.createExternalWithJsFinalize( common.mustCall(() => { throw new Error('finalizer error'); })); diff --git a/test/js-native-api/test_reference/test_reference.c b/test/js-native-api/test_reference/test_reference.c index 82c1f17d9dce0e..058be07363588b 100644 --- a/test/js-native-api/test_reference/test_reference.c +++ b/test/js-native-api/test_reference/test_reference.c @@ -22,20 +22,6 @@ static void FinalizeExternal(napi_env env, void* data, void* hint) { finalize_count++; } -static void FinalizeExternalCallJs(napi_env env, void* data, void* hint) { - int *actual_value = data; - NODE_API_ASSERT_RETURN_VOID(env, actual_value == &test_value, - "The correct pointer was passed to the finalizer"); - - napi_ref finalizer_ref = (napi_ref)hint; - napi_value js_finalizer; - napi_value recv; - NODE_API_CALL_RETURN_VOID(env, napi_get_reference_value(env, finalizer_ref, &js_finalizer)); - NODE_API_CALL_RETURN_VOID(env, napi_get_global(env, &recv)); - NODE_API_CALL_RETURN_VOID(env, napi_call_function(env, recv, js_finalizer, 0, NULL, NULL)); - NODE_API_CALL_RETURN_VOID(env, napi_delete_reference(env, finalizer_ref)); -} - static napi_value CreateExternal(napi_env env, napi_callback_info info) { int* data = &test_value; @@ -118,31 +104,6 @@ CreateExternalWithFinalize(napi_env env, napi_callback_info info) { return result; } -static napi_value -CreateExternalWithJsFinalize(napi_env env, napi_callback_info info) { - size_t argc = 1; - napi_value args[1]; - NODE_API_CALL(env, napi_get_cb_info(env, info, &argc, args, NULL, NULL)); - NODE_API_ASSERT(env, argc == 1, "Wrong number of arguments"); - napi_value finalizer = args[0]; - napi_valuetype finalizer_valuetype; - NODE_API_CALL(env, napi_typeof(env, finalizer, &finalizer_valuetype)); - NODE_API_ASSERT(env, finalizer_valuetype == napi_function, "Wrong type of first argument"); - napi_ref finalizer_ref; - NODE_API_CALL(env, napi_create_reference(env, finalizer, 1, &finalizer_ref)); - - napi_value result; - NODE_API_CALL(env, - napi_create_external(env, - &test_value, - FinalizeExternalCallJs, - finalizer_ref, /* finalize_hint */ - &result)); - - finalize_count = 0; - return result; -} - static napi_value CheckExternal(napi_env env, napi_callback_info info) { size_t argc = 1; napi_value arg; @@ -263,24 +224,24 @@ static napi_value ValidateDeleteBeforeFinalize(napi_env env, napi_callback_info EXTERN_C_START napi_value Init(napi_env env, napi_value exports) { napi_property_descriptor descriptors[] = { - DECLARE_NODE_API_GETTER("finalizeCount", GetFinalizeCount), - DECLARE_NODE_API_PROPERTY("createExternal", CreateExternal), - DECLARE_NODE_API_PROPERTY("createExternalWithFinalize", - CreateExternalWithFinalize), - DECLARE_NODE_API_PROPERTY("createExternalWithJsFinalize", - CreateExternalWithJsFinalize), - DECLARE_NODE_API_PROPERTY("checkExternal", CheckExternal), - DECLARE_NODE_API_PROPERTY("createReference", CreateReference), - DECLARE_NODE_API_PROPERTY("createSymbol", CreateSymbol), - DECLARE_NODE_API_PROPERTY("createSymbolFor", CreateSymbolFor), - DECLARE_NODE_API_PROPERTY("createSymbolForEmptyString", CreateSymbolForEmptyString), - DECLARE_NODE_API_PROPERTY("createSymbolForIncorrectLength", CreateSymbolForIncorrectLength), - DECLARE_NODE_API_PROPERTY("deleteReference", DeleteReference), - DECLARE_NODE_API_PROPERTY("incrementRefcount", IncrementRefcount), - DECLARE_NODE_API_PROPERTY("decrementRefcount", DecrementRefcount), - DECLARE_NODE_API_GETTER("referenceValue", GetReferenceValue), - DECLARE_NODE_API_PROPERTY("validateDeleteBeforeFinalize", - ValidateDeleteBeforeFinalize), + DECLARE_NODE_API_GETTER("finalizeCount", GetFinalizeCount), + DECLARE_NODE_API_PROPERTY("createExternal", CreateExternal), + DECLARE_NODE_API_PROPERTY("createExternalWithFinalize", + CreateExternalWithFinalize), + DECLARE_NODE_API_PROPERTY("checkExternal", CheckExternal), + DECLARE_NODE_API_PROPERTY("createReference", CreateReference), + DECLARE_NODE_API_PROPERTY("createSymbol", CreateSymbol), + DECLARE_NODE_API_PROPERTY("createSymbolFor", CreateSymbolFor), + DECLARE_NODE_API_PROPERTY("createSymbolForEmptyString", + CreateSymbolForEmptyString), + DECLARE_NODE_API_PROPERTY("createSymbolForIncorrectLength", + CreateSymbolForIncorrectLength), + DECLARE_NODE_API_PROPERTY("deleteReference", DeleteReference), + DECLARE_NODE_API_PROPERTY("incrementRefcount", IncrementRefcount), + DECLARE_NODE_API_PROPERTY("decrementRefcount", DecrementRefcount), + DECLARE_NODE_API_GETTER("referenceValue", GetReferenceValue), + DECLARE_NODE_API_PROPERTY("validateDeleteBeforeFinalize", + ValidateDeleteBeforeFinalize), }; NODE_API_CALL(env, napi_define_properties( diff --git a/test/node-api/test_buffer/binding.gyp b/test/node-api/test_buffer/binding.gyp index e41a3993cd7c9d..e5d0955ae6308e 100644 --- a/test/node-api/test_buffer/binding.gyp +++ b/test/node-api/test_buffer/binding.gyp @@ -3,6 +3,10 @@ { "target_name": "test_buffer", "sources": [ "test_buffer.c" ] + }, + { + "target_name": "test_finalizer", + "sources": [ "test_finalizer.c" ] } ] } diff --git a/test/node-api/test_buffer/test_buffer.c b/test/node-api/test_buffer/test_buffer.c index bc61cd7a2e9062..013a7e2d417fbe 100644 --- a/test/node-api/test_buffer/test_buffer.c +++ b/test/node-api/test_buffer/test_buffer.c @@ -22,17 +22,6 @@ static void noopDeleter(napi_env env, void* data, void* finalize_hint) { deleterCallCount++; } -static void malignDeleter(napi_env env, void* data, void* finalize_hint) { - NODE_API_ASSERT_RETURN_VOID(env, data != NULL && strcmp(data, theText) == 0, "invalid data"); - napi_ref finalizer_ref = (napi_ref)finalize_hint; - napi_value js_finalizer; - napi_value recv; - NODE_API_CALL_RETURN_VOID(env, napi_get_reference_value(env, finalizer_ref, &js_finalizer)); - NODE_API_CALL_RETURN_VOID(env, napi_get_global(env, &recv)); - NODE_API_CALL_RETURN_VOID(env, napi_call_function(env, recv, js_finalizer, 0, NULL, NULL)); - NODE_API_CALL_RETURN_VOID(env, napi_delete_reference(env, finalizer_ref)); -} - static napi_value newBuffer(napi_env env, napi_callback_info info) { napi_value theBuffer; char* theCopy; @@ -118,30 +107,6 @@ static napi_value staticBuffer(napi_env env, napi_callback_info info) { return theBuffer; } -static napi_value malignFinalizerBuffer(napi_env env, napi_callback_info info) { - size_t argc = 1; - napi_value args[1]; - NODE_API_CALL(env, napi_get_cb_info(env, info, &argc, args, NULL, NULL)); - NODE_API_ASSERT(env, argc == 1, "Wrong number of arguments"); - napi_value finalizer = args[0]; - napi_valuetype finalizer_valuetype; - NODE_API_CALL(env, napi_typeof(env, finalizer, &finalizer_valuetype)); - NODE_API_ASSERT(env, finalizer_valuetype == napi_function, "Wrong type of first argument"); - napi_ref finalizer_ref; - NODE_API_CALL(env, napi_create_reference(env, finalizer, 1, &finalizer_ref)); - - napi_value theBuffer; - NODE_API_CALL( - env, - napi_create_external_buffer(env, - sizeof(theText), - (void*)theText, - malignDeleter, - finalizer_ref, // finalize_hint - &theBuffer)); - return theBuffer; -} - static napi_value Init(napi_env env, napi_value exports) { napi_value theValue; @@ -151,14 +116,13 @@ static napi_value Init(napi_env env, napi_value exports) { napi_set_named_property(env, exports, "theText", theValue)); napi_property_descriptor methods[] = { - DECLARE_NODE_API_PROPERTY("newBuffer", newBuffer), - DECLARE_NODE_API_PROPERTY("newExternalBuffer", newExternalBuffer), - DECLARE_NODE_API_PROPERTY("getDeleterCallCount", getDeleterCallCount), - DECLARE_NODE_API_PROPERTY("copyBuffer", copyBuffer), - DECLARE_NODE_API_PROPERTY("bufferHasInstance", bufferHasInstance), - DECLARE_NODE_API_PROPERTY("bufferInfo", bufferInfo), - DECLARE_NODE_API_PROPERTY("staticBuffer", staticBuffer), - DECLARE_NODE_API_PROPERTY("malignFinalizerBuffer", malignFinalizerBuffer), + DECLARE_NODE_API_PROPERTY("newBuffer", newBuffer), + DECLARE_NODE_API_PROPERTY("newExternalBuffer", newExternalBuffer), + DECLARE_NODE_API_PROPERTY("getDeleterCallCount", getDeleterCallCount), + DECLARE_NODE_API_PROPERTY("copyBuffer", copyBuffer), + DECLARE_NODE_API_PROPERTY("bufferHasInstance", bufferHasInstance), + DECLARE_NODE_API_PROPERTY("bufferInfo", bufferInfo), + DECLARE_NODE_API_PROPERTY("staticBuffer", staticBuffer), }; NODE_API_CALL(env, napi_define_properties( diff --git a/test/node-api/test_buffer/test_finalizer.c b/test/node-api/test_buffer/test_finalizer.c new file mode 100644 index 00000000000000..eb5426d8f29cdf --- /dev/null +++ b/test/node-api/test_buffer/test_finalizer.c @@ -0,0 +1,61 @@ +#include <node_api.h> +#include <stdlib.h> +#include <string.h> +#include "../../js-native-api/common.h" + +static const char theText[] = + "Lorem ipsum dolor sit amet, consectetur adipiscing elit."; + +static void malignDeleter(napi_env env, void* data, void* finalize_hint) { + NODE_API_ASSERT_RETURN_VOID( + env, data != NULL && strcmp(data, theText) == 0, "invalid data"); + napi_ref finalizer_ref = (napi_ref)finalize_hint; + napi_value js_finalizer; + napi_value recv; + NODE_API_CALL_RETURN_VOID( + env, napi_get_reference_value(env, finalizer_ref, &js_finalizer)); + NODE_API_CALL_RETURN_VOID(env, napi_get_global(env, &recv)); + NODE_API_CALL_RETURN_VOID( + env, napi_call_function(env, recv, js_finalizer, 0, NULL, NULL)); + NODE_API_CALL_RETURN_VOID(env, napi_delete_reference(env, finalizer_ref)); +} + +static napi_value malignFinalizerBuffer(napi_env env, napi_callback_info info) { + size_t argc = 1; + napi_value args[1]; + NODE_API_CALL(env, napi_get_cb_info(env, info, &argc, args, NULL, NULL)); + NODE_API_ASSERT(env, argc == 1, "Wrong number of arguments"); + napi_value finalizer = args[0]; + napi_valuetype finalizer_valuetype; + NODE_API_CALL(env, napi_typeof(env, finalizer, &finalizer_valuetype)); + NODE_API_ASSERT(env, + finalizer_valuetype == napi_function, + "Wrong type of first argument"); + napi_ref finalizer_ref; + NODE_API_CALL(env, napi_create_reference(env, finalizer, 1, &finalizer_ref)); + + napi_value theBuffer; + NODE_API_CALL(env, + napi_create_external_buffer(env, + sizeof(theText), + (void*)theText, + malignDeleter, + finalizer_ref, // finalize_hint + &theBuffer)); + return theBuffer; +} + +static napi_value Init(napi_env env, napi_value exports) { + napi_property_descriptor methods[] = { + DECLARE_NODE_API_PROPERTY("malignFinalizerBuffer", malignFinalizerBuffer), + }; + + NODE_API_CALL( + env, + napi_define_properties( + env, exports, sizeof(methods) / sizeof(methods[0]), methods)); + + return exports; +} + +NAPI_MODULE(NODE_GYP_MODULE_NAME, Init) diff --git a/test/node-api/test_buffer/test_finalizer.js b/test/node-api/test_buffer/test_finalizer.js index b706c68c7c3e02..35511fcb016c95 100644 --- a/test/node-api/test_buffer/test_finalizer.js +++ b/test/node-api/test_buffer/test_finalizer.js @@ -2,7 +2,7 @@ // Flags: --expose-gc --force-node-api-uncaught-exceptions-policy const common = require('../../common'); -const binding = require(`./build/${common.buildType}/test_buffer`); +const binding = require(`./build/${common.buildType}/test_finalizer`); const assert = require('assert'); const tick = require('util').promisify(require('../../common/tick')); diff --git a/test/node-api/test_threadsafe_function/binding.gyp b/test/node-api/test_threadsafe_function/binding.gyp index b60352e05af103..58a9d04d4a5619 100644 --- a/test/node-api/test_threadsafe_function/binding.gyp +++ b/test/node-api/test_threadsafe_function/binding.gyp @@ -3,6 +3,20 @@ { 'target_name': 'binding', 'sources': ['binding.c'] + }, + { + 'target_name': 'test_uncaught_exception_v9', + 'defines': [ + 'NAPI_VERSION=9' + ], + 'sources': ['test_uncaught_exception.c'] + }, + { + 'target_name': 'test_uncaught_exception', + 'defines': [ + 'NAPI_EXPERIMENTAL' + ], + 'sources': ['test_uncaught_exception.c'] } ] } diff --git a/test/node-api/test_threadsafe_function/test_force_uncaught_exception.js b/test/node-api/test_threadsafe_function/test_legacy_uncaught_exception.js similarity index 85% rename from test/node-api/test_threadsafe_function/test_force_uncaught_exception.js rename to test/node-api/test_threadsafe_function/test_legacy_uncaught_exception.js index b1f95715eadf60..a8743e00b5b8c5 100644 --- a/test/node-api/test_threadsafe_function/test_force_uncaught_exception.js +++ b/test/node-api/test_threadsafe_function/test_legacy_uncaught_exception.js @@ -2,7 +2,7 @@ // Flags: --no-force-node-api-uncaught-exceptions-policy const common = require('../../common'); -const binding = require(`./build/${common.buildType}/binding`); +const binding = require(`./build/${common.buildType}/test_uncaught_exception_v9`); process.on( 'uncaughtException', diff --git a/test/node-api/test_threadsafe_function/test_uncaught_exception.c b/test/node-api/test_threadsafe_function/test_uncaught_exception.c new file mode 100644 index 00000000000000..f8499d4fe4d680 --- /dev/null +++ b/test/node-api/test_threadsafe_function/test_uncaught_exception.c @@ -0,0 +1,62 @@ +#include <node_api.h> +#include "../../js-native-api/common.h" + +// Testing calling into JavaScript +static void ThreadSafeFunctionFinalize(napi_env env, + void* finalize_data, + void* finalize_hint) { + napi_ref js_func_ref = (napi_ref)finalize_data; + napi_value js_func; + napi_value recv; + NODE_API_CALL_RETURN_VOID( + env, napi_get_reference_value(env, js_func_ref, &js_func)); + NODE_API_CALL_RETURN_VOID(env, napi_get_global(env, &recv)); + NODE_API_CALL_RETURN_VOID( + env, napi_call_function(env, recv, js_func, 0, NULL, NULL)); + NODE_API_CALL_RETURN_VOID(env, napi_delete_reference(env, js_func_ref)); +} + +// Testing calling into JavaScript +static napi_value CallIntoModule(napi_env env, napi_callback_info info) { + size_t argc = 4; + napi_value argv[4]; + NODE_API_CALL(env, napi_get_cb_info(env, info, &argc, argv, NULL, NULL)); + + napi_ref finalize_func; + NODE_API_CALL(env, napi_create_reference(env, argv[3], 1, &finalize_func)); + + napi_threadsafe_function tsfn; + NODE_API_CALL(env, + napi_create_threadsafe_function(env, + argv[0], + argv[1], + argv[2], + 0, + 1, + finalize_func, + ThreadSafeFunctionFinalize, + NULL, + NULL, + &tsfn)); + NODE_API_CALL(env, + napi_call_threadsafe_function(tsfn, NULL, napi_tsfn_blocking)); + NODE_API_CALL(env, napi_release_threadsafe_function(tsfn, napi_tsfn_release)); + return NULL; +} + +// Module init +static napi_value Init(napi_env env, napi_value exports) { + napi_property_descriptor properties[] = { + DECLARE_NODE_API_PROPERTY("CallIntoModule", CallIntoModule), + }; + + NODE_API_CALL( + env, + napi_define_properties(env, + exports, + sizeof(properties) / sizeof(properties[0]), + properties)); + + return exports; +} +NAPI_MODULE(NODE_GYP_MODULE_NAME, Init) diff --git a/test/node-api/test_threadsafe_function/test_uncaught_exception.js b/test/node-api/test_threadsafe_function/test_uncaught_exception.js index 2529757908999b..81b2623d702790 100644 --- a/test/node-api/test_threadsafe_function/test_uncaught_exception.js +++ b/test/node-api/test_threadsafe_function/test_uncaught_exception.js @@ -1,27 +1,7 @@ 'use strict'; -// Flags: --force-node-api-uncaught-exceptions-policy const common = require('../../common'); -const assert = require('assert'); -const binding = require(`./build/${common.buildType}/binding`); +const binding = require(`./build/${common.buildType}/test_uncaught_exception`); +const { testUncaughtException } = require('./uncaught_exception'); -const callbackCheck = common.mustCall((err) => { - assert.throws(() => { throw err; }, /callback error/); - process.removeListener('uncaughtException', callbackCheck); - process.on('uncaughtException', finalizerCheck); -}); -const finalizerCheck = common.mustCall((err) => { - assert.throws(() => { throw err; }, /finalizer error/); -}); -process.on('uncaughtException', callbackCheck); - -binding.CallIntoModule( - common.mustCall(() => { - throw new Error('callback error'); - }), - {}, - 'resource_name', - common.mustCall(function finalizer() { - throw new Error('finalizer error'); - }), -); +testUncaughtException(binding); diff --git a/test/node-api/test_threadsafe_function/test_uncaught_exception_v9.js b/test/node-api/test_threadsafe_function/test_uncaught_exception_v9.js new file mode 100644 index 00000000000000..28e628918fdff2 --- /dev/null +++ b/test/node-api/test_threadsafe_function/test_uncaught_exception_v9.js @@ -0,0 +1,8 @@ +'use strict'; +// Flags: --force-node-api-uncaught-exceptions-policy + +const common = require('../../common'); +const binding = require(`./build/${common.buildType}/test_uncaught_exception_v9`); +const { testUncaughtException } = require('./uncaught_exception'); + +testUncaughtException(binding); diff --git a/test/node-api/test_threadsafe_function/uncaught_exception.js b/test/node-api/test_threadsafe_function/uncaught_exception.js new file mode 100644 index 00000000000000..da2aa2f4efef8a --- /dev/null +++ b/test/node-api/test_threadsafe_function/uncaught_exception.js @@ -0,0 +1,31 @@ +'use strict'; + +const common = require('../../common'); +const assert = require('assert'); + +function testUncaughtException(binding) { + const callbackCheck = common.mustCall((err) => { + assert.throws(() => { throw err; }, /callback error/); + process.removeListener('uncaughtException', callbackCheck); + process.on('uncaughtException', finalizerCheck); + }); + const finalizerCheck = common.mustCall((err) => { + assert.throws(() => { throw err; }, /finalizer error/); + }); + process.on('uncaughtException', callbackCheck); + + binding.CallIntoModule( + common.mustCall(() => { + throw new Error('callback error'); + }), + {}, + 'resource_name', + common.mustCall(function finalizer() { + throw new Error('finalizer error'); + }), + ); +} + +module.exports = { + testUncaughtException, +};