diff --git a/packages/test/CMakeLists.txt b/packages/test/CMakeLists.txt index 0656defc..188641a5 100644 --- a/packages/test/CMakeLists.txt +++ b/packages/test/CMakeLists.txt @@ -267,6 +267,7 @@ add_test("string" "./string/binding.c;./string/test_null.c" OFF) add_test("property" "./property/binding.c" OFF) add_test("promise" "./promise/binding.c" OFF) add_test("object" "./object/test_null.c;./object/test_object.c" OFF) +add_test("object_exception" "./object/test_exceptions.c" OFF) add_test("objwrap" "./objwrap/myobject.cc" OFF) add_test("bigint" "./bigint/binding.c" OFF) add_test("fnwrap" "./fnwrap/myobject.cc;./fnwrap/binding.cc" OFF) diff --git a/packages/test/common-inl.h b/packages/test/common-inl.h index 7ea3a755..08b694fc 100644 --- a/packages/test/common-inl.h +++ b/packages/test/common-inl.h @@ -41,17 +41,34 @@ inline void add_last_status(napi_env env, const char* key, napi_value return_value) { napi_value prop_value; + napi_value exception; const napi_extended_error_info* p_last_error; NODE_API_CALL_RETURN_VOID(env, napi_get_last_error_info(env, &p_last_error)); + // Content of p_last_error can be updated in subsequent node-api calls. + // Retrieve it immediately. + const char* error_message = p_last_error->error_message == NULL + ? "napi_ok" + : p_last_error->error_message; + + bool is_exception_pending; + NODE_API_CALL_RETURN_VOID( + env, napi_is_exception_pending(env, &is_exception_pending)); + if (is_exception_pending) { + NODE_API_CALL_RETURN_VOID( + env, napi_get_and_clear_last_exception(env, &exception)); + char exception_key[50]; +#if !defined(__wasm__) || (defined(__EMSCRIPTEN__) || defined(__wasi__)) + snprintf(exception_key, sizeof(exception_key), "%s%s", key, "Exception"); +#endif + NODE_API_CALL_RETURN_VOID( + env, + napi_set_named_property(env, return_value, exception_key, exception)); + } NODE_API_CALL_RETURN_VOID( env, napi_create_string_utf8( - env, - (p_last_error->error_message == NULL ? "napi_ok" - : p_last_error->error_message), - NAPI_AUTO_LENGTH, - &prop_value)); + env, error_message, NAPI_AUTO_LENGTH, &prop_value)); NODE_API_CALL_RETURN_VOID( env, napi_set_named_property(env, return_value, key, prop_value)); } diff --git a/packages/test/object/object_exceptions.test.js b/packages/test/object/object_exceptions.test.js new file mode 100644 index 00000000..f6803cc2 --- /dev/null +++ b/packages/test/object/object_exceptions.test.js @@ -0,0 +1,22 @@ +/* eslint-disable symbol-description */ +/* eslint-disable camelcase */ +'use strict' +const { load } = require('../util') +const common = require('../common') + +module.exports = load('object_exception').then(test_object => { + const { testExceptions } = test_object + + function throws () { + throw new Error('foobar') + } + testExceptions(new Proxy({}, { + get: common.mustCallAtLeast(throws, 1), + getOwnPropertyDescriptor: common.mustCallAtLeast(throws, 1), + defineProperty: common.mustCallAtLeast(throws, 1), + deleteProperty: common.mustCallAtLeast(throws, 1), + has: common.mustCallAtLeast(throws, 1), + set: common.mustCallAtLeast(throws, 1), + ownKeys: common.mustCallAtLeast(throws, 1) + })) +}) diff --git a/packages/test/object/test_exceptions.c b/packages/test/object/test_exceptions.c new file mode 100644 index 00000000..00fd8939 --- /dev/null +++ b/packages/test/object/test_exceptions.c @@ -0,0 +1,82 @@ +// #include +#include +// #include +#include "../common.h" +#include "../entry_point.h" + +static napi_value TestExceptions(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)); + + napi_value target = args[0]; + napi_value exception, key, value; + napi_status status; + bool is_exception_pending; + bool bool_result; + + NODE_API_CALL(env, + napi_create_string_utf8(env, "key", NAPI_AUTO_LENGTH, &key)); + NODE_API_CALL( + env, napi_create_string_utf8(env, "value", NAPI_AUTO_LENGTH, &value)); + +#define PROCEDURE(call) \ + { \ + status = (call); \ + NODE_API_ASSERT( \ + env, status == napi_pending_exception, "expect exception pending"); \ + NODE_API_CALL(env, napi_is_exception_pending(env, &is_exception_pending)); \ + NODE_API_ASSERT(env, is_exception_pending, "expect exception pending"); \ + NODE_API_CALL(env, napi_get_and_clear_last_exception(env, &exception)); \ + } + // discard the exception values. + + // properties + PROCEDURE(napi_set_property(env, target, key, value)); + PROCEDURE(napi_set_named_property(env, target, "key", value)); + PROCEDURE(napi_has_property(env, target, key, &bool_result)); + PROCEDURE(napi_has_own_property(env, target, key, &bool_result)); + PROCEDURE(napi_has_named_property(env, target, "key", &bool_result)); + PROCEDURE(napi_get_property(env, target, key, &value)); + PROCEDURE(napi_get_named_property(env, target, "key", &value)); + PROCEDURE(napi_delete_property(env, target, key, &bool_result)); + + // elements + PROCEDURE(napi_set_element(env, target, 0, value)); + PROCEDURE(napi_has_element(env, target, 0, &bool_result)); + PROCEDURE(napi_get_element(env, target, 0, &value)); + PROCEDURE(napi_delete_element(env, target, 0, &bool_result)); + + napi_property_descriptor descriptors[] = { + DECLARE_NODE_API_PROPERTY_VALUE("key", value), + }; + PROCEDURE(napi_define_properties( + env, target, sizeof(descriptors) / sizeof(*descriptors), descriptors)); + + PROCEDURE(napi_get_all_property_names(env, + target, + napi_key_own_only, + napi_key_enumerable, + napi_key_keep_numbers, + &value)); + PROCEDURE(napi_get_property_names(env, target, &value)); + + return NULL; +} + +EXTERN_C_START +napi_value Init(napi_env env, napi_value exports) { + napi_property_descriptor descriptors[] = { + DECLARE_NODE_API_PROPERTY("testExceptions", TestExceptions), + }; + + NODE_API_CALL( + env, + napi_define_properties(env, + exports, + sizeof(descriptors) / sizeof(*descriptors), + descriptors)); + + return exports; +} +EXTERN_C_END