diff --git a/node.gyp b/node.gyp index e1033654a672fd..71ac1fa139bf9d 100644 --- a/node.gyp +++ b/node.gyp @@ -894,8 +894,11 @@ 'NODE_PLATFORM="<(OS)"', 'NODE_WANT_INTERNALS=1', # Define NAPI_EXPERIMENTAL to enable Node-API experimental function symbols being exposed. - 'NAPI_EXPERIMENTAL=1', - 'NODE_API_EXPERIMENTAL_NO_WARNING=1', + 'NAPI_EXPERIMENTAL', + # Suppress warning about using experimental Node-API features. + 'NODE_API_EXPERIMENTAL_NO_WARNING', + # Enable Node-API headers to support v-table. + 'NODE_API_RUNTIME_USE_VTABLE', # Warn when using deprecated V8 APIs. 'V8_DEPRECATION_WARNINGS=1', 'NODE_OPENSSL_SYSTEM_CERT_PATH="<(openssl_system_ca_path)"', @@ -1226,6 +1229,9 @@ 'NODE_ARCH="<(target_arch)"', 'NODE_PLATFORM="<(OS)"', 'NODE_WANT_INTERNALS=1', + 'NAPI_EXPERIMENTAL', + 'NODE_API_EXPERIMENTAL_NO_WARNING', + 'NODE_API_RUNTIME_USE_VTABLE', ], 'sources': [ '<@(node_cctest_sources)' ], diff --git a/src/js_native_api.h b/src/js_native_api.h index 0bc8f1810d2872..0e760bd08beab0 100644 --- a/src/js_native_api.h +++ b/src/js_native_api.h @@ -1,10 +1,6 @@ #ifndef SRC_JS_NATIVE_API_H_ #define SRC_JS_NATIVE_API_H_ -// This file needs to be compatible with C compilers. -#include // NOLINT(modernize-deprecated-headers) -#include // NOLINT(modernize-deprecated-headers) - #include "js_native_api_types.h" // If you need __declspec(dllimport), either include instead, or @@ -31,25 +27,146 @@ #define EXTERN_C_END #endif +#if defined(NODE_API_MODULE_USE_VTABLE) && \ + !defined(NODE_API_MODULE_NO_VTABLE_IMPL) +#define NODE_API_MODULE_USE_VTABLE_IMPL +#endif + +#ifdef NODE_API_MODULE_USE_VTABLE_IMPL + +// Implement all Node-API functions via vtable indirection. +#undef NAPI_EXTERN +#define NAPI_EXTERN static inline + +#ifndef NODE_API_MODULE_NO_VTABLE_FALLBACK + +extern node_api_js_vtable g_node_api_js_vtable_fallback; + +// Atomic pointer write with release semantics for thread-safe lazy init. +#ifndef NODE_API_WRITE_POINTER_RELEASE +// NOLINTBEGIN (readability/casting) - must be compilable by C compiler +#ifdef _MSC_VER +#include +#define NODE_API_WRITE_POINTER_RELEASE(ptr, val) \ + (void)_InterlockedExchangePointer((void* volatile*)(ptr), (void*)(val)) +#else +#define NODE_API_WRITE_POINTER_RELEASE(ptr, val) \ + __atomic_store_n((void**)(ptr), (void*)(val), __ATOMIC_RELEASE) +#endif +// NOLINTEND (readability/casting) +#endif // NODE_API_WRITE_POINTER_RELEASE + +#ifndef NODE_API_LOAD_SYMBOL +#ifdef _WIN32 +#ifndef WIN32_LEAN_AND_MEAN +#define WIN32_LEAN_AND_MEAN +#endif +#ifndef NOMINMAX +#define NOMINMAX +#endif +#include + +// NOLINTBEGIN (readability/null_usage) - must be compilable by C compiler +static inline HMODULE node_api_get_runtime_module_handle(void) { + static HMODULE module_handle = NULL; + HMODULE handle = module_handle; + if (handle == NULL) { + // Match the code in win_delay_load_hook.cc from node-gyp. + handle = GetModuleHandleA("libnode.dll"); + if (handle == NULL) { + handle = GetModuleHandleA(NULL); + } + NODE_API_WRITE_POINTER_RELEASE(&module_handle, handle); + } + return handle; +} +// NOLINTEND (readability/null_usage) + +#define NODE_API_LOAD_SYMBOL(name) \ + GetProcAddress(node_api_get_runtime_module_handle(), name) +#else +#include +#define NODE_API_LOAD_SYMBOL(name) dlsym(RTLD_DEFAULT, name) +#endif // _WIN32 +#endif // NODE_API_LOAD_SYMBOL + +#define NODE_API_VTABLE_IMPL_FALLBACK( \ + ret, vtable, func_name, method_name, ...) \ + const node_api_##vtable* vtable = &g_node_api_##vtable##_fallback; \ + if (!vtable->method_name) { \ + NODE_API_WRITE_POINTER_RELEASE(&vtable->method_name, \ + NODE_API_LOAD_SYMBOL(#func_name)); \ + } \ + ret vtable->method_name(__VA_ARGS__) + +#else // NODE_API_MODULE_NO_VTABLE_FALLBACK + +#ifndef NODE_API_UNREACHABLE +#ifdef _MSC_VER +#define NODE_API_UNREACHABLE() __fastfail(7 /* FAST_FAIL_FATAL_APP_EXIT */) +#else +#define NODE_API_UNREACHABLE() __builtin_trap() +#endif +#endif // NODE_API_UNREACHABLE + +#define NODE_API_VTABLE_IMPL_FALLBACK(...) NODE_API_UNREACHABLE() + +#endif // NODE_API_MODULE_NO_VTABLE_FALLBACK + +#define NODE_API_VTABLE_IMPL_BASE(vtable, func_name, method_name, obj, ...) \ + { \ + if (!obj) { \ + return napi_invalid_arg; \ + } else if (obj->sentinel == NODE_API_VT_SENTINEL) { \ + return obj->vtable->method_name(__VA_ARGS__); \ + } else { \ + NODE_API_VTABLE_IMPL_FALLBACK( \ + return, vtable, func_name, method_name, __VA_ARGS__); \ + } \ + } + +#define NODE_API_JS_VTABLE_IMPL(func_name, method_name, env, ...) \ + NODE_API_VTABLE_IMPL_BASE( \ + js_vtable, func_name, method_name, env, env, __VA_ARGS__) + +#else // NODE_API_MODULE_USE_VTABLE_IMPL + +#define NODE_API_JS_VTABLE_IMPL(...) + +#endif // NODE_API_MODULE_USE_VTABLE_IMPL + EXTERN_C_START NAPI_EXTERN napi_status NAPI_CDECL napi_get_last_error_info( - node_api_basic_env env, const napi_extended_error_info** result); + node_api_basic_env env, const napi_extended_error_info** result) + NODE_API_JS_VTABLE_IMPL(napi_get_last_error_info, + get_last_error_info, + env, + result); // Getters for defined singletons NAPI_EXTERN napi_status NAPI_CDECL napi_get_undefined(napi_env env, - napi_value* result); + napi_value* result) + NODE_API_JS_VTABLE_IMPL(napi_get_undefined, get_undefined, env, result); + NAPI_EXTERN napi_status NAPI_CDECL napi_get_null(napi_env env, - napi_value* result); + napi_value* result) + NODE_API_JS_VTABLE_IMPL(napi_get_null, get_null, env, result); + NAPI_EXTERN napi_status NAPI_CDECL napi_get_global(napi_env env, - napi_value* result); + napi_value* result) + NODE_API_JS_VTABLE_IMPL(napi_get_global, get_global, env, result); + NAPI_EXTERN napi_status NAPI_CDECL napi_get_boolean(napi_env env, bool value, - napi_value* result); + napi_value* result) + NODE_API_JS_VTABLE_IMPL(napi_get_boolean, get_boolean, env, value, result); // Methods to create Primitive types/Objects NAPI_EXTERN napi_status NAPI_CDECL napi_create_object(napi_env env, - napi_value* result); + napi_value* result) + NODE_API_JS_VTABLE_IMPL(napi_create_object, create_object, env, result); + #ifdef NAPI_EXPERIMENTAL #define NODE_API_EXPERIMENTAL_HAS_CREATE_OBJECT_WITH_PROPERTIES NAPI_EXTERN napi_status NAPI_CDECL @@ -58,35 +175,82 @@ napi_create_object_with_properties(napi_env env, napi_value* property_names, napi_value* property_values, size_t property_count, - napi_value* result); + napi_value* result) + NODE_API_JS_VTABLE_IMPL(napi_create_object_with_properties, + create_object_with_properties, + env, + prototype_or_null, + property_names, + property_values, + property_count, + result); #endif // NAPI_EXPERIMENTAL NAPI_EXTERN napi_status NAPI_CDECL napi_create_array(napi_env env, - napi_value* result); + napi_value* result) + NODE_API_JS_VTABLE_IMPL(napi_create_array, create_array, env, result); + NAPI_EXTERN napi_status NAPI_CDECL -napi_create_array_with_length(napi_env env, size_t length, napi_value* result); +napi_create_array_with_length(napi_env env, size_t length, napi_value* result) + NODE_API_JS_VTABLE_IMPL(napi_create_array_with_length, + create_array_with_length, + env, + length, + result); + NAPI_EXTERN napi_status NAPI_CDECL napi_create_double(napi_env env, double value, - napi_value* result); + napi_value* result) + NODE_API_JS_VTABLE_IMPL( + napi_create_double, create_double, env, value, result); + NAPI_EXTERN napi_status NAPI_CDECL napi_create_int32(napi_env env, int32_t value, - napi_value* result); + napi_value* result) + NODE_API_JS_VTABLE_IMPL( + napi_create_int32, create_int32, env, value, result); + NAPI_EXTERN napi_status NAPI_CDECL napi_create_uint32(napi_env env, uint32_t value, - napi_value* result); + napi_value* result) + NODE_API_JS_VTABLE_IMPL( + napi_create_uint32, create_uint32, env, value, result); + NAPI_EXTERN napi_status NAPI_CDECL napi_create_int64(napi_env env, int64_t value, - napi_value* result); -NAPI_EXTERN napi_status NAPI_CDECL napi_create_string_latin1( - napi_env env, const char* str, size_t length, napi_value* result); + napi_value* result) + NODE_API_JS_VTABLE_IMPL( + napi_create_int64, create_int64, env, value, result); + +NAPI_EXTERN napi_status NAPI_CDECL napi_create_string_latin1(napi_env env, + const char* str, + size_t length, + napi_value* result) + NODE_API_JS_VTABLE_IMPL(napi_create_string_latin1, + create_string_latin1, + env, + str, + length, + result); + NAPI_EXTERN napi_status NAPI_CDECL napi_create_string_utf8(napi_env env, const char* str, size_t length, - napi_value* result); + napi_value* result) + NODE_API_JS_VTABLE_IMPL( + napi_create_string_utf8, create_string_utf8, env, str, length, result); + NAPI_EXTERN napi_status NAPI_CDECL napi_create_string_utf16(napi_env env, const char16_t* str, size_t length, - napi_value* result); + napi_value* result) + NODE_API_JS_VTABLE_IMPL(napi_create_string_utf16, + create_string_utf16, + env, + str, + length, + result); + #if NAPI_VERSION >= 10 NAPI_EXTERN napi_status NAPI_CDECL node_api_create_external_string_latin1( napi_env env, @@ -95,187 +259,365 @@ NAPI_EXTERN napi_status NAPI_CDECL node_api_create_external_string_latin1( node_api_basic_finalize finalize_callback, void* finalize_hint, napi_value* result, - bool* copied); -NAPI_EXTERN napi_status NAPI_CDECL -node_api_create_external_string_utf16(napi_env env, - char16_t* str, - size_t length, - node_api_basic_finalize finalize_callback, - void* finalize_hint, - napi_value* result, - bool* copied); + bool* copied) + NODE_API_JS_VTABLE_IMPL(node_api_create_external_string_latin1, + create_external_string_latin1, + env, + str, + length, + finalize_callback, + finalize_hint, + result, + copied); + +NAPI_EXTERN napi_status NAPI_CDECL node_api_create_external_string_utf16( + napi_env env, + char16_t* str, + size_t length, + node_api_basic_finalize finalize_callback, + void* finalize_hint, + napi_value* result, + bool* copied) NODE_API_JS_VTABLE_IMPL(node_api_create_external_string_utf16, + create_external_string_utf16, + env, + str, + length, + finalize_callback, + finalize_hint, + result, + copied); NAPI_EXTERN napi_status NAPI_CDECL node_api_create_property_key_latin1( - napi_env env, const char* str, size_t length, napi_value* result); + napi_env env, const char* str, size_t length, napi_value* result) + NODE_API_JS_VTABLE_IMPL(node_api_create_property_key_latin1, + create_property_key_latin1, + env, + str, + length, + result); + NAPI_EXTERN napi_status NAPI_CDECL node_api_create_property_key_utf8( - napi_env env, const char* str, size_t length, napi_value* result); + napi_env env, const char* str, size_t length, napi_value* result) + NODE_API_JS_VTABLE_IMPL(node_api_create_property_key_utf8, + create_property_key_utf8, + env, + str, + length, + result); + NAPI_EXTERN napi_status NAPI_CDECL node_api_create_property_key_utf16( - napi_env env, const char16_t* str, size_t length, napi_value* result); + napi_env env, const char16_t* str, size_t length, napi_value* result) + NODE_API_JS_VTABLE_IMPL(node_api_create_property_key_utf16, + create_property_key_utf16, + env, + str, + length, + result); + #endif // NAPI_VERSION >= 10 NAPI_EXTERN napi_status NAPI_CDECL napi_create_symbol(napi_env env, napi_value description, - napi_value* result); + napi_value* result) + NODE_API_JS_VTABLE_IMPL( + napi_create_symbol, create_symbol, env, description, result); + #if NAPI_VERSION >= 9 NAPI_EXTERN napi_status NAPI_CDECL node_api_symbol_for(napi_env env, const char* utf8description, size_t length, - napi_value* result); + napi_value* result) + NODE_API_JS_VTABLE_IMPL( + node_api_symbol_for, symbol_for, env, utf8description, length, result); #endif // NAPI_VERSION >= 9 NAPI_EXTERN napi_status NAPI_CDECL napi_create_function(napi_env env, const char* utf8name, size_t length, napi_callback cb, void* data, - napi_value* result); + napi_value* result) + NODE_API_JS_VTABLE_IMPL(napi_create_function, + create_function, + env, + utf8name, + length, + cb, + data, + result); + NAPI_EXTERN napi_status NAPI_CDECL napi_create_error(napi_env env, napi_value code, napi_value msg, - napi_value* result); + napi_value* result) + NODE_API_JS_VTABLE_IMPL( + napi_create_error, create_error, env, code, msg, result); + NAPI_EXTERN napi_status NAPI_CDECL napi_create_type_error(napi_env env, napi_value code, napi_value msg, - napi_value* result); + napi_value* result) + NODE_API_JS_VTABLE_IMPL( + napi_create_type_error, create_type_error, env, code, msg, result); + NAPI_EXTERN napi_status NAPI_CDECL napi_create_range_error(napi_env env, napi_value code, napi_value msg, - napi_value* result); + napi_value* result) + NODE_API_JS_VTABLE_IMPL( + napi_create_range_error, create_range_error, env, code, msg, result); + #if NAPI_VERSION >= 9 NAPI_EXTERN napi_status NAPI_CDECL node_api_create_syntax_error( - napi_env env, napi_value code, napi_value msg, napi_value* result); + napi_env env, napi_value code, napi_value msg, napi_value* result) + NODE_API_JS_VTABLE_IMPL(node_api_create_syntax_error, + create_syntax_error, + env, + code, + msg, + result); #endif // NAPI_VERSION >= 9 // Methods to get the native napi_value from Primitive type NAPI_EXTERN napi_status NAPI_CDECL napi_typeof(napi_env env, napi_value value, - napi_valuetype* result); + napi_valuetype* result) + NODE_API_JS_VTABLE_IMPL(napi_typeof, type_of, env, value, result); + NAPI_EXTERN napi_status NAPI_CDECL napi_get_value_double(napi_env env, napi_value value, - double* result); + double* result) + NODE_API_JS_VTABLE_IMPL( + napi_get_value_double, get_value_double, env, value, result); + NAPI_EXTERN napi_status NAPI_CDECL napi_get_value_int32(napi_env env, napi_value value, - int32_t* result); + int32_t* result) + NODE_API_JS_VTABLE_IMPL( + napi_get_value_int32, get_value_int32, env, value, result); + NAPI_EXTERN napi_status NAPI_CDECL napi_get_value_uint32(napi_env env, napi_value value, - uint32_t* result); + uint32_t* result) + NODE_API_JS_VTABLE_IMPL( + napi_get_value_uint32, get_value_uint32, env, value, result); + NAPI_EXTERN napi_status NAPI_CDECL napi_get_value_int64(napi_env env, napi_value value, - int64_t* result); + int64_t* result) + NODE_API_JS_VTABLE_IMPL( + napi_get_value_int64, get_value_int64, env, value, result); + NAPI_EXTERN napi_status NAPI_CDECL napi_get_value_bool(napi_env env, napi_value value, - bool* result); + bool* result) + NODE_API_JS_VTABLE_IMPL( + napi_get_value_bool, get_value_bool, env, value, result); // Copies LATIN-1 encoded bytes from a string into a buffer. NAPI_EXTERN napi_status NAPI_CDECL napi_get_value_string_latin1( - napi_env env, napi_value value, char* buf, size_t bufsize, size_t* result); + napi_env env, napi_value value, char* buf, size_t bufsize, size_t* result) + NODE_API_JS_VTABLE_IMPL(napi_get_value_string_latin1, + get_value_string_latin1, + env, + value, + buf, + bufsize, + result); // Copies UTF-8 encoded bytes from a string into a buffer. NAPI_EXTERN napi_status NAPI_CDECL napi_get_value_string_utf8( - napi_env env, napi_value value, char* buf, size_t bufsize, size_t* result); + napi_env env, napi_value value, char* buf, size_t bufsize, size_t* result) + NODE_API_JS_VTABLE_IMPL(napi_get_value_string_utf8, + get_value_string_utf8, + env, + value, + buf, + bufsize, + result); // Copies UTF-16 encoded bytes from a string into a buffer. NAPI_EXTERN napi_status NAPI_CDECL napi_get_value_string_utf16(napi_env env, napi_value value, char16_t* buf, size_t bufsize, - size_t* result); + size_t* result) + NODE_API_JS_VTABLE_IMPL(napi_get_value_string_utf16, + get_value_string_utf16, + env, + value, + buf, + bufsize, + result); // Methods to coerce values // These APIs may execute user scripts NAPI_EXTERN napi_status NAPI_CDECL napi_coerce_to_bool(napi_env env, napi_value value, - napi_value* result); + napi_value* result) + NODE_API_JS_VTABLE_IMPL( + napi_coerce_to_bool, coerce_to_bool, env, value, result); + NAPI_EXTERN napi_status NAPI_CDECL napi_coerce_to_number(napi_env env, napi_value value, - napi_value* result); + napi_value* result) + NODE_API_JS_VTABLE_IMPL( + napi_coerce_to_number, coerce_to_number, env, value, result); + NAPI_EXTERN napi_status NAPI_CDECL napi_coerce_to_object(napi_env env, napi_value value, - napi_value* result); + napi_value* result) + NODE_API_JS_VTABLE_IMPL( + napi_coerce_to_object, coerce_to_object, env, value, result); + NAPI_EXTERN napi_status NAPI_CDECL napi_coerce_to_string(napi_env env, napi_value value, - napi_value* result); + napi_value* result) + NODE_API_JS_VTABLE_IMPL( + napi_coerce_to_string, coerce_to_string, env, value, result); // Methods to work with Objects #ifdef NAPI_EXPERIMENTAL #define NODE_API_EXPERIMENTAL_HAS_SET_PROTOTYPE NAPI_EXTERN napi_status NAPI_CDECL node_api_set_prototype(napi_env env, napi_value object, - napi_value value); + napi_value value) + NODE_API_JS_VTABLE_IMPL( + node_api_set_prototype, set_prototype, env, object, value); #endif NAPI_EXTERN napi_status NAPI_CDECL napi_get_prototype(napi_env env, napi_value object, - napi_value* result); + napi_value* result) + NODE_API_JS_VTABLE_IMPL( + napi_get_prototype, get_prototype, env, object, result); + NAPI_EXTERN napi_status NAPI_CDECL napi_get_property_names(napi_env env, napi_value object, - napi_value* result); + napi_value* result) + NODE_API_JS_VTABLE_IMPL( + napi_get_property_names, get_property_names, env, object, result); + NAPI_EXTERN napi_status NAPI_CDECL napi_set_property(napi_env env, napi_value object, napi_value key, - napi_value value); -NAPI_EXTERN napi_status NAPI_CDECL napi_has_property(napi_env env, - napi_value object, - napi_value key, - bool* result); + napi_value value) + NODE_API_JS_VTABLE_IMPL( + napi_set_property, set_property, env, object, key, value); + +NAPI_EXTERN napi_status NAPI_CDECL +napi_has_property(napi_env env, napi_value object, napi_value key, bool* result) + NODE_API_JS_VTABLE_IMPL( + napi_has_property, has_property, env, object, key, result); + NAPI_EXTERN napi_status NAPI_CDECL napi_get_property(napi_env env, napi_value object, napi_value key, - napi_value* result); + napi_value* result) + NODE_API_JS_VTABLE_IMPL( + napi_get_property, get_property, env, object, key, result); + NAPI_EXTERN napi_status NAPI_CDECL napi_delete_property(napi_env env, napi_value object, napi_value key, - bool* result); + bool* result) + NODE_API_JS_VTABLE_IMPL( + napi_delete_property, delete_property, env, object, key, result); + NAPI_EXTERN napi_status NAPI_CDECL napi_has_own_property(napi_env env, napi_value object, napi_value key, - bool* result); + bool* result) + NODE_API_JS_VTABLE_IMPL( + napi_has_own_property, has_own_property, env, object, key, result); + NAPI_EXTERN napi_status NAPI_CDECL napi_set_named_property(napi_env env, napi_value object, const char* utf8name, - napi_value value); + napi_value value) + NODE_API_JS_VTABLE_IMPL(napi_set_named_property, + set_named_property, + env, + object, + utf8name, + value); + NAPI_EXTERN napi_status NAPI_CDECL napi_has_named_property(napi_env env, napi_value object, const char* utf8name, - bool* result); + bool* result) + NODE_API_JS_VTABLE_IMPL(napi_has_named_property, + has_named_property, + env, + object, + utf8name, + result); + NAPI_EXTERN napi_status NAPI_CDECL napi_get_named_property(napi_env env, napi_value object, const char* utf8name, - napi_value* result); + napi_value* result) + NODE_API_JS_VTABLE_IMPL(napi_get_named_property, + get_named_property, + env, + object, + utf8name, + result); + NAPI_EXTERN napi_status NAPI_CDECL napi_set_element(napi_env env, napi_value object, uint32_t index, - napi_value value); -NAPI_EXTERN napi_status NAPI_CDECL napi_has_element(napi_env env, - napi_value object, - uint32_t index, - bool* result); + napi_value value) + NODE_API_JS_VTABLE_IMPL( + napi_set_element, set_element, env, object, index, value); + +NAPI_EXTERN napi_status NAPI_CDECL +napi_has_element(napi_env env, napi_value object, uint32_t index, bool* result) + NODE_API_JS_VTABLE_IMPL( + napi_has_element, has_element, env, object, index, result); + NAPI_EXTERN napi_status NAPI_CDECL napi_get_element(napi_env env, napi_value object, uint32_t index, - napi_value* result); + napi_value* result) + NODE_API_JS_VTABLE_IMPL( + napi_get_element, get_element, env, object, index, result); + NAPI_EXTERN napi_status NAPI_CDECL napi_delete_element(napi_env env, napi_value object, uint32_t index, - bool* result); + bool* result) + NODE_API_JS_VTABLE_IMPL( + napi_delete_element, delete_element, env, object, index, result); + NAPI_EXTERN napi_status NAPI_CDECL napi_define_properties(napi_env env, napi_value object, size_t property_count, - const napi_property_descriptor* properties); + const napi_property_descriptor* properties) + NODE_API_JS_VTABLE_IMPL(napi_define_properties, + define_properties, + env, + object, + property_count, + properties); // Methods to work with Arrays NAPI_EXTERN napi_status NAPI_CDECL napi_is_array(napi_env env, napi_value value, - bool* result); + bool* result) + NODE_API_JS_VTABLE_IMPL(napi_is_array, is_array, env, value, result); + NAPI_EXTERN napi_status NAPI_CDECL napi_get_array_length(napi_env env, napi_value value, - uint32_t* result); + uint32_t* result) + NODE_API_JS_VTABLE_IMPL( + napi_get_array_length, get_array_length, env, value, result); // Methods to compare values -NAPI_EXTERN napi_status NAPI_CDECL napi_strict_equals(napi_env env, - napi_value lhs, - napi_value rhs, - bool* result); +NAPI_EXTERN napi_status NAPI_CDECL +napi_strict_equals(napi_env env, napi_value lhs, napi_value rhs, bool* result) + NODE_API_JS_VTABLE_IMPL( + napi_strict_equals, strict_equals, env, lhs, rhs, result); // Methods to work with Functions NAPI_EXTERN napi_status NAPI_CDECL napi_call_function(napi_env env, @@ -283,16 +625,25 @@ NAPI_EXTERN napi_status NAPI_CDECL napi_call_function(napi_env env, napi_value func, size_t argc, const napi_value* argv, - napi_value* result); + napi_value* result) + NODE_API_JS_VTABLE_IMPL( + napi_call_function, call_function, env, recv, func, argc, argv, result); + NAPI_EXTERN napi_status NAPI_CDECL napi_new_instance(napi_env env, napi_value constructor, size_t argc, const napi_value* argv, - napi_value* result); + napi_value* result) + NODE_API_JS_VTABLE_IMPL( + napi_new_instance, new_instance, env, constructor, argc, argv, result); + NAPI_EXTERN napi_status NAPI_CDECL napi_instanceof(napi_env env, napi_value object, napi_value constructor, - bool* result); + bool* result) + NODE_API_JS_VTABLE_IMPL(napi_instanceof, + instanceof + , env, object, constructor, result); // Methods to work with napi_callbacks @@ -304,10 +655,15 @@ NAPI_EXTERN napi_status NAPI_CDECL napi_get_cb_info( // and receives the actual count of args. napi_value* argv, // [out] Array of values napi_value* this_arg, // [out] Receives the JS 'this' arg for the call - void** data); // [out] Receives the data pointer for the callback. + void** data) // [out] Receives the data pointer for the callback. + NODE_API_JS_VTABLE_IMPL( + napi_get_cb_info, get_cb_info, env, cbinfo, argc, argv, this_arg, data); + +NAPI_EXTERN napi_status NAPI_CDECL +napi_get_new_target(napi_env env, napi_callback_info cbinfo, napi_value* result) + NODE_API_JS_VTABLE_IMPL( + napi_get_new_target, get_new_target, env, cbinfo, result); -NAPI_EXTERN napi_status NAPI_CDECL napi_get_new_target( - napi_env env, napi_callback_info cbinfo, napi_value* result); NAPI_EXTERN napi_status NAPI_CDECL napi_define_class(napi_env env, const char* utf8name, @@ -316,7 +672,16 @@ napi_define_class(napi_env env, void* data, size_t property_count, const napi_property_descriptor* properties, - napi_value* result); + napi_value* result) NODE_API_JS_VTABLE_IMPL(napi_define_class, + define_class, + env, + utf8name, + length, + constructor, + data, + property_count, + properties, + result); // Methods to work with external data objects NAPI_EXTERN napi_status NAPI_CDECL @@ -325,36 +690,62 @@ napi_wrap(napi_env env, void* native_object, node_api_basic_finalize finalize_cb, void* finalize_hint, - napi_ref* result); + napi_ref* result) NODE_API_JS_VTABLE_IMPL(napi_wrap, + wrap, + env, + js_object, + native_object, + finalize_cb, + finalize_hint, + result); + NAPI_EXTERN napi_status NAPI_CDECL napi_unwrap(napi_env env, napi_value js_object, - void** result); + void** result) + NODE_API_JS_VTABLE_IMPL(napi_unwrap, unwrap, env, js_object, result); + NAPI_EXTERN napi_status NAPI_CDECL napi_remove_wrap(napi_env env, napi_value js_object, - void** result); -NAPI_EXTERN napi_status NAPI_CDECL -napi_create_external(napi_env env, - void* data, - node_api_basic_finalize finalize_cb, - void* finalize_hint, - napi_value* result); + void** result) + NODE_API_JS_VTABLE_IMPL( + napi_remove_wrap, remove_wrap, env, js_object, result); + +NAPI_EXTERN napi_status NAPI_CDECL napi_create_external( + napi_env env, + void* data, + node_api_basic_finalize finalize_cb, + void* finalize_hint, + napi_value* result) NODE_API_JS_VTABLE_IMPL(napi_create_external, + create_external, + env, + data, + finalize_cb, + finalize_hint, + result); + NAPI_EXTERN napi_status NAPI_CDECL napi_get_value_external(napi_env env, napi_value value, - void** result); + void** result) + NODE_API_JS_VTABLE_IMPL( + napi_get_value_external, get_value_external, env, value, result); // Methods to control object lifespan // Set initial_refcount to 0 for a weak reference, >0 for a strong reference. -NAPI_EXTERN napi_status NAPI_CDECL -napi_create_reference(napi_env env, - napi_value value, - uint32_t initial_refcount, - napi_ref* result); +NAPI_EXTERN napi_status NAPI_CDECL napi_create_reference( + napi_env env, napi_value value, uint32_t initial_refcount, napi_ref* result) + NODE_API_JS_VTABLE_IMPL(napi_create_reference, + create_reference, + env, + value, + initial_refcount, + result); // Deletes a reference. The referenced value is released, and may // be GC'd unless there are other references to it. NAPI_EXTERN napi_status NAPI_CDECL napi_delete_reference(node_api_basic_env env, - napi_ref ref); + napi_ref ref) + NODE_API_JS_VTABLE_IMPL(napi_delete_reference, delete_reference, env, ref); // Increments the reference count, optionally returning the resulting count. // After this call the reference will be a strong reference because its @@ -363,7 +754,9 @@ NAPI_EXTERN napi_status NAPI_CDECL napi_delete_reference(node_api_basic_env env, // results in an error. NAPI_EXTERN napi_status NAPI_CDECL napi_reference_ref(napi_env env, napi_ref ref, - uint32_t* result); + uint32_t* result) + NODE_API_JS_VTABLE_IMPL( + napi_reference_ref, reference_ref, env, ref, result); // Decrements the reference count, optionally returning the resulting count. // If the result is 0 the reference is now weak and the object may be GC'd @@ -371,64 +764,122 @@ NAPI_EXTERN napi_status NAPI_CDECL napi_reference_ref(napi_env env, // refcount is already 0 results in an error. NAPI_EXTERN napi_status NAPI_CDECL napi_reference_unref(napi_env env, napi_ref ref, - uint32_t* result); + uint32_t* result) + NODE_API_JS_VTABLE_IMPL( + napi_reference_unref, reference_unref, env, ref, result); // Attempts to get a referenced value. If the reference is weak, // the value might no longer be available, in that case the call // is still successful but the result is NULL. NAPI_EXTERN napi_status NAPI_CDECL napi_get_reference_value(napi_env env, napi_ref ref, - napi_value* result); + napi_value* result) + NODE_API_JS_VTABLE_IMPL( + napi_get_reference_value, get_reference_value, env, ref, result); NAPI_EXTERN napi_status NAPI_CDECL -napi_open_handle_scope(napi_env env, napi_handle_scope* result); +napi_open_handle_scope(napi_env env, napi_handle_scope* result) + NODE_API_JS_VTABLE_IMPL(napi_open_handle_scope, + open_handle_scope, + env, + result); + NAPI_EXTERN napi_status NAPI_CDECL -napi_close_handle_scope(napi_env env, napi_handle_scope scope); +napi_close_handle_scope(napi_env env, napi_handle_scope scope) + NODE_API_JS_VTABLE_IMPL(napi_close_handle_scope, + close_handle_scope, + env, + scope); + NAPI_EXTERN napi_status NAPI_CDECL napi_open_escapable_handle_scope( - napi_env env, napi_escapable_handle_scope* result); + napi_env env, napi_escapable_handle_scope* result) + NODE_API_JS_VTABLE_IMPL(napi_open_escapable_handle_scope, + open_escapable_handle_scope, + env, + result); + NAPI_EXTERN napi_status NAPI_CDECL napi_close_escapable_handle_scope( - napi_env env, napi_escapable_handle_scope scope); + napi_env env, napi_escapable_handle_scope scope) + NODE_API_JS_VTABLE_IMPL(napi_close_escapable_handle_scope, + close_escapable_handle_scope, + env, + scope); NAPI_EXTERN napi_status NAPI_CDECL napi_escape_handle(napi_env env, napi_escapable_handle_scope scope, napi_value escapee, - napi_value* result); + napi_value* result) + NODE_API_JS_VTABLE_IMPL( + napi_escape_handle, escape_handle, env, scope, escapee, result); // Methods to support error handling -NAPI_EXTERN napi_status NAPI_CDECL napi_throw(napi_env env, napi_value error); +NAPI_EXTERN napi_status NAPI_CDECL napi_throw(napi_env env, napi_value error) + NODE_API_JS_VTABLE_IMPL(napi_throw, throw_value, env, error); + NAPI_EXTERN napi_status NAPI_CDECL napi_throw_error(napi_env env, const char* code, - const char* msg); + const char* msg) + NODE_API_JS_VTABLE_IMPL(napi_throw_error, throw_error, env, code, msg); + NAPI_EXTERN napi_status NAPI_CDECL napi_throw_type_error(napi_env env, const char* code, - const char* msg); + const char* msg) + NODE_API_JS_VTABLE_IMPL( + napi_throw_type_error, throw_type_error, env, code, msg); + NAPI_EXTERN napi_status NAPI_CDECL napi_throw_range_error(napi_env env, const char* code, - const char* msg); + const char* msg) + NODE_API_JS_VTABLE_IMPL( + napi_throw_range_error, throw_range_error, env, code, msg); + #if NAPI_VERSION >= 9 NAPI_EXTERN napi_status NAPI_CDECL node_api_throw_syntax_error(napi_env env, const char* code, - const char* msg); + const char* msg) + NODE_API_JS_VTABLE_IMPL( + node_api_throw_syntax_error, throw_syntax_error, env, code, msg); #endif // NAPI_VERSION >= 9 + NAPI_EXTERN napi_status NAPI_CDECL napi_is_error(napi_env env, napi_value value, - bool* result); + bool* result) + NODE_API_JS_VTABLE_IMPL(napi_is_error, is_error, env, value, result); // Methods to support catching exceptions NAPI_EXTERN napi_status NAPI_CDECL napi_is_exception_pending(napi_env env, - bool* result); + bool* result) + NODE_API_JS_VTABLE_IMPL(napi_is_exception_pending, + is_exception_pending, + env, + result); + NAPI_EXTERN napi_status NAPI_CDECL -napi_get_and_clear_last_exception(napi_env env, napi_value* result); +napi_get_and_clear_last_exception(napi_env env, napi_value* result) + NODE_API_JS_VTABLE_IMPL(napi_get_and_clear_last_exception, + get_and_clear_last_exception, + env, + result); // Methods to work with array buffers and typed arrays NAPI_EXTERN napi_status NAPI_CDECL napi_is_arraybuffer(napi_env env, napi_value value, - bool* result); + bool* result) + NODE_API_JS_VTABLE_IMPL( + napi_is_arraybuffer, is_arraybuffer, env, value, result); + NAPI_EXTERN napi_status NAPI_CDECL napi_create_arraybuffer(napi_env env, size_t byte_length, void** data, - napi_value* result); + napi_value* result) + NODE_API_JS_VTABLE_IMPL(napi_create_arraybuffer, + create_arraybuffer, + env, + byte_length, + data, + result); + #ifndef NODE_API_NO_EXTERNAL_BUFFERS_ALLOWED NAPI_EXTERN napi_status NAPI_CDECL napi_create_external_arraybuffer(napi_env env, @@ -436,94 +887,181 @@ napi_create_external_arraybuffer(napi_env env, size_t byte_length, node_api_basic_finalize finalize_cb, void* finalize_hint, - napi_value* result); + napi_value* result) + NODE_API_JS_VTABLE_IMPL(napi_create_external_arraybuffer, + create_external_arraybuffer, + env, + external_data, + byte_length, + finalize_cb, + finalize_hint, + result); + #endif // NODE_API_NO_EXTERNAL_BUFFERS_ALLOWED + NAPI_EXTERN napi_status NAPI_CDECL napi_get_arraybuffer_info( - napi_env env, napi_value arraybuffer, void** data, size_t* byte_length); + napi_env env, napi_value arraybuffer, void** data, size_t* byte_length) + NODE_API_JS_VTABLE_IMPL(napi_get_arraybuffer_info, + get_arraybuffer_info, + env, + arraybuffer, + data, + byte_length); + NAPI_EXTERN napi_status NAPI_CDECL napi_is_typedarray(napi_env env, napi_value value, - bool* result); -NAPI_EXTERN napi_status NAPI_CDECL -napi_create_typedarray(napi_env env, - napi_typedarray_type type, - size_t length, - napi_value arraybuffer, - size_t byte_offset, - napi_value* result); -NAPI_EXTERN napi_status NAPI_CDECL -napi_get_typedarray_info(napi_env env, - napi_value typedarray, - napi_typedarray_type* type, - size_t* length, - void** data, - napi_value* arraybuffer, - size_t* byte_offset); + bool* result) + NODE_API_JS_VTABLE_IMPL( + napi_is_typedarray, is_typedarray, env, value, result); + +NAPI_EXTERN napi_status NAPI_CDECL napi_create_typedarray( + napi_env env, + napi_typedarray_type type, + size_t length, + napi_value arraybuffer, + size_t byte_offset, + napi_value* result) NODE_API_JS_VTABLE_IMPL(napi_create_typedarray, + create_typedarray, + env, + type, + length, + arraybuffer, + byte_offset, + result); + +NAPI_EXTERN napi_status NAPI_CDECL napi_get_typedarray_info( + napi_env env, + napi_value typedarray, + napi_typedarray_type* type, + size_t* length, + void** data, + napi_value* arraybuffer, + size_t* byte_offset) NODE_API_JS_VTABLE_IMPL(napi_get_typedarray_info, + get_typedarray_info, + env, + typedarray, + type, + length, + data, + arraybuffer, + byte_offset); NAPI_EXTERN napi_status NAPI_CDECL napi_create_dataview(napi_env env, size_t length, napi_value arraybuffer, size_t byte_offset, - napi_value* result); + napi_value* result) + NODE_API_JS_VTABLE_IMPL(napi_create_dataview, + create_dataview, + env, + length, + arraybuffer, + byte_offset, + result); + NAPI_EXTERN napi_status NAPI_CDECL napi_is_dataview(napi_env env, napi_value value, - bool* result); -NAPI_EXTERN napi_status NAPI_CDECL -napi_get_dataview_info(napi_env env, - napi_value dataview, - size_t* bytelength, - void** data, - napi_value* arraybuffer, - size_t* byte_offset); + bool* result) + NODE_API_JS_VTABLE_IMPL(napi_is_dataview, is_dataview, env, value, result); + +NAPI_EXTERN napi_status NAPI_CDECL napi_get_dataview_info( + napi_env env, + napi_value dataview, + size_t* bytelength, + void** data, + napi_value* arraybuffer, + size_t* byte_offset) NODE_API_JS_VTABLE_IMPL(napi_get_dataview_info, + get_dataview_info, + env, + dataview, + bytelength, + data, + arraybuffer, + byte_offset); #ifdef NAPI_EXPERIMENTAL #define NODE_API_EXPERIMENTAL_HAS_SHAREDARRAYBUFFER NAPI_EXTERN napi_status NAPI_CDECL -node_api_is_sharedarraybuffer(napi_env env, napi_value value, bool* result); +node_api_is_sharedarraybuffer(napi_env env, napi_value value, bool* result) + NODE_API_JS_VTABLE_IMPL(node_api_is_sharedarraybuffer, + is_sharedarraybuffer, + env, + value, + result); + NAPI_EXTERN napi_status NAPI_CDECL node_api_create_sharedarraybuffer( - napi_env env, size_t byte_length, void** data, napi_value* result); + napi_env env, size_t byte_length, void** data, napi_value* result) + NODE_API_JS_VTABLE_IMPL(node_api_create_sharedarraybuffer, + create_sharedarraybuffer, + env, + byte_length, + data, + result); #endif // NAPI_EXPERIMENTAL // version management NAPI_EXTERN napi_status NAPI_CDECL napi_get_version(node_api_basic_env env, - uint32_t* result); + uint32_t* result) + NODE_API_JS_VTABLE_IMPL(napi_get_version, get_version, env, result); // Promises NAPI_EXTERN napi_status NAPI_CDECL napi_create_promise(napi_env env, napi_deferred* deferred, - napi_value* promise); + napi_value* promise) + NODE_API_JS_VTABLE_IMPL( + napi_create_promise, create_promise, env, deferred, promise); + NAPI_EXTERN napi_status NAPI_CDECL napi_resolve_deferred(napi_env env, napi_deferred deferred, - napi_value resolution); + napi_value resolution) + NODE_API_JS_VTABLE_IMPL( + napi_resolve_deferred, resolve_deferred, env, deferred, resolution); + NAPI_EXTERN napi_status NAPI_CDECL napi_reject_deferred(napi_env env, napi_deferred deferred, - napi_value rejection); + napi_value rejection) + NODE_API_JS_VTABLE_IMPL( + napi_reject_deferred, reject_deferred, env, deferred, rejection); + NAPI_EXTERN napi_status NAPI_CDECL napi_is_promise(napi_env env, napi_value value, - bool* is_promise); + bool* is_promise) + NODE_API_JS_VTABLE_IMPL( + napi_is_promise, is_promise, env, value, is_promise); // Running a script NAPI_EXTERN napi_status NAPI_CDECL napi_run_script(napi_env env, napi_value script, - napi_value* result); + napi_value* result) + NODE_API_JS_VTABLE_IMPL(napi_run_script, run_script, env, script, result); // Memory management NAPI_EXTERN napi_status NAPI_CDECL napi_adjust_external_memory( - node_api_basic_env env, int64_t change_in_bytes, int64_t* adjusted_value); + node_api_basic_env env, int64_t change_in_bytes, int64_t* adjusted_value) + NODE_API_JS_VTABLE_IMPL(napi_adjust_external_memory, + adjust_external_memory, + env, + change_in_bytes, + adjusted_value); #if NAPI_VERSION >= 5 // Dates NAPI_EXTERN napi_status NAPI_CDECL napi_create_date(napi_env env, double time, - napi_value* result); + napi_value* result) + NODE_API_JS_VTABLE_IMPL(napi_create_date, create_date, env, time, result); NAPI_EXTERN napi_status NAPI_CDECL napi_is_date(napi_env env, napi_value value, - bool* is_date); + bool* is_date) + NODE_API_JS_VTABLE_IMPL(napi_is_date, is_date, env, value, is_date); NAPI_EXTERN napi_status NAPI_CDECL napi_get_date_value(napi_env env, napi_value value, - double* result); + double* result) + NODE_API_JS_VTABLE_IMPL( + napi_get_date_value, get_date_value, env, value, result); // Add finalizer for pointer NAPI_EXTERN napi_status NAPI_CDECL @@ -532,18 +1070,30 @@ napi_add_finalizer(napi_env env, void* finalize_data, node_api_basic_finalize finalize_cb, void* finalize_hint, - napi_ref* result); + napi_ref* result) NODE_API_JS_VTABLE_IMPL(napi_add_finalizer, + add_finalizer, + env, + js_object, + finalize_data, + finalize_cb, + finalize_hint, + result); #endif // NAPI_VERSION >= 5 #ifdef NAPI_EXPERIMENTAL #define NODE_API_EXPERIMENTAL_HAS_POST_FINALIZER -NAPI_EXTERN napi_status NAPI_CDECL -node_api_post_finalizer(node_api_basic_env env, - napi_finalize finalize_cb, - void* finalize_data, - void* finalize_hint); +NAPI_EXTERN napi_status NAPI_CDECL node_api_post_finalizer( + node_api_basic_env env, + napi_finalize finalize_cb, + void* finalize_data, + void* finalize_hint) NODE_API_JS_VTABLE_IMPL(node_api_post_finalizer, + post_finalizer, + env, + finalize_cb, + finalize_data, + finalize_hint); #endif // NAPI_EXPERIMENTAL @@ -552,71 +1102,142 @@ node_api_post_finalizer(node_api_basic_env env, // BigInt NAPI_EXTERN napi_status NAPI_CDECL napi_create_bigint_int64(napi_env env, int64_t value, - napi_value* result); -NAPI_EXTERN napi_status NAPI_CDECL -napi_create_bigint_uint64(napi_env env, uint64_t value, napi_value* result); -NAPI_EXTERN napi_status NAPI_CDECL -napi_create_bigint_words(napi_env env, - int sign_bit, - size_t word_count, - const uint64_t* words, - napi_value* result); + napi_value* result) + NODE_API_JS_VTABLE_IMPL( + napi_create_bigint_int64, create_bigint_int64, env, value, result); + +NAPI_EXTERN napi_status NAPI_CDECL napi_create_bigint_uint64(napi_env env, + uint64_t value, + napi_value* result) + NODE_API_JS_VTABLE_IMPL( + napi_create_bigint_uint64, create_bigint_uint64, env, value, result); + +NAPI_EXTERN napi_status NAPI_CDECL napi_create_bigint_words( + napi_env env, + int sign_bit, + size_t word_count, + const uint64_t* words, + napi_value* result) NODE_API_JS_VTABLE_IMPL(napi_create_bigint_words, + create_bigint_words, + env, + sign_bit, + word_count, + words, + result); + NAPI_EXTERN napi_status NAPI_CDECL napi_get_value_bigint_int64(napi_env env, napi_value value, int64_t* result, - bool* lossless); + bool* lossless) + NODE_API_JS_VTABLE_IMPL(napi_get_value_bigint_int64, + get_value_bigint_int64, + env, + value, + result, + lossless); + NAPI_EXTERN napi_status NAPI_CDECL napi_get_value_bigint_uint64( - napi_env env, napi_value value, uint64_t* result, bool* lossless); -NAPI_EXTERN napi_status NAPI_CDECL -napi_get_value_bigint_words(napi_env env, - napi_value value, - int* sign_bit, - size_t* word_count, - uint64_t* words); + napi_env env, napi_value value, uint64_t* result, bool* lossless) + NODE_API_JS_VTABLE_IMPL(napi_get_value_bigint_uint64, + get_value_bigint_uint64, + env, + value, + result, + lossless); + +NAPI_EXTERN napi_status NAPI_CDECL napi_get_value_bigint_words( + napi_env env, + napi_value value, + int* sign_bit, + size_t* word_count, + uint64_t* words) NODE_API_JS_VTABLE_IMPL(napi_get_value_bigint_words, + get_value_bigint_words, + env, + value, + sign_bit, + word_count, + words); // Object -NAPI_EXTERN napi_status NAPI_CDECL -napi_get_all_property_names(napi_env env, - napi_value object, - napi_key_collection_mode key_mode, - napi_key_filter key_filter, - napi_key_conversion key_conversion, - napi_value* result); +NAPI_EXTERN napi_status NAPI_CDECL napi_get_all_property_names( + napi_env env, + napi_value object, + napi_key_collection_mode key_mode, + napi_key_filter key_filter, + napi_key_conversion key_conversion, + napi_value* result) NODE_API_JS_VTABLE_IMPL(napi_get_all_property_names, + get_all_property_names, + env, + object, + key_mode, + key_filter, + key_conversion, + result); // Instance data -NAPI_EXTERN napi_status NAPI_CDECL -napi_set_instance_data(node_api_basic_env env, - void* data, - napi_finalize finalize_cb, - void* finalize_hint); +NAPI_EXTERN napi_status NAPI_CDECL napi_set_instance_data( + node_api_basic_env env, + void* data, + napi_finalize finalize_cb, + void* finalize_hint) NODE_API_JS_VTABLE_IMPL(napi_set_instance_data, + set_instance_data, + env, + data, + finalize_cb, + finalize_hint); NAPI_EXTERN napi_status NAPI_CDECL -napi_get_instance_data(node_api_basic_env env, void** data); +napi_get_instance_data(node_api_basic_env env, void** data) + NODE_API_JS_VTABLE_IMPL(napi_get_instance_data, + get_instance_data, + env, + data); + #endif // NAPI_VERSION >= 6 #if NAPI_VERSION >= 7 // ArrayBuffer detaching NAPI_EXTERN napi_status NAPI_CDECL -napi_detach_arraybuffer(napi_env env, napi_value arraybuffer); +napi_detach_arraybuffer(napi_env env, napi_value arraybuffer) + NODE_API_JS_VTABLE_IMPL(napi_detach_arraybuffer, + detach_arraybuffer, + env, + arraybuffer); NAPI_EXTERN napi_status NAPI_CDECL -napi_is_detached_arraybuffer(napi_env env, napi_value value, bool* result); +napi_is_detached_arraybuffer(napi_env env, napi_value value, bool* result) + NODE_API_JS_VTABLE_IMPL(napi_is_detached_arraybuffer, + is_detached_arraybuffer, + env, + value, + result); + #endif // NAPI_VERSION >= 7 #if NAPI_VERSION >= 8 // Type tagging NAPI_EXTERN napi_status NAPI_CDECL napi_type_tag_object( - napi_env env, napi_value value, const napi_type_tag* type_tag); + napi_env env, napi_value value, const napi_type_tag* type_tag) + NODE_API_JS_VTABLE_IMPL( + napi_type_tag_object, type_tag_object, env, value, type_tag); + +NAPI_EXTERN napi_status NAPI_CDECL napi_check_object_type_tag( + napi_env env, napi_value value, const napi_type_tag* type_tag, bool* result) + NODE_API_JS_VTABLE_IMPL(napi_check_object_type_tag, + check_object_type_tag, + env, + value, + type_tag, + result); -NAPI_EXTERN napi_status NAPI_CDECL -napi_check_object_type_tag(napi_env env, - napi_value value, - const napi_type_tag* type_tag, - bool* result); NAPI_EXTERN napi_status NAPI_CDECL napi_object_freeze(napi_env env, - napi_value object); + napi_value object) + NODE_API_JS_VTABLE_IMPL(napi_object_freeze, object_freeze, env, object); + NAPI_EXTERN napi_status NAPI_CDECL napi_object_seal(napi_env env, - napi_value object); + napi_value object) + NODE_API_JS_VTABLE_IMPL(napi_object_seal, object_seal, env, object); + #endif // NAPI_VERSION >= 8 EXTERN_C_END diff --git a/src/js_native_api_types.h b/src/js_native_api_types.h index 9642db6fba0281..5ad620eaee434c 100644 --- a/src/js_native_api_types.h +++ b/src/js_native_api_types.h @@ -7,12 +7,11 @@ #ifdef NAPI_EXPERIMENTAL #define NAPI_VERSION NAPI_VERSION_EXPERIMENTAL #else -// The baseline version for N-API. -// The NAPI_VERSION controls which version will be used by default when -// compilling a native addon. If the addon developer specifically wants to use -// functions available in a new version of N-API that is not yet ported in all -// LTS versions, they can set NAPI_VERSION knowing that they have specifically -// depended on that version. +// The baseline version for Node-API. +// NAPI_VERSION controls which version is used by default when compiling +// a native addon. If the addon developer wants to use functions from a +// newer Node-API version not yet available in all LTS versions, they can +// set NAPI_VERSION to explicitly depend on that version. #define NAPI_VERSION 8 #endif #endif @@ -29,13 +28,19 @@ #endif #endif +#ifdef __cplusplus +#include +#include +#else // This file needs to be compatible with C compilers. // This is a public include file, and these includes have essentially -// became part of it's API. -#include // NOLINT(modernize-deprecated-headers) -#include // NOLINT(modernize-deprecated-headers) +// become part of its API. +#include +#include +#include +#endif -#if !defined __cplusplus || (defined(_MSC_VER) && _MSC_VER < 1900) +#if !defined(__cplusplus) || (defined(_MSC_VER) && _MSC_VER < 1900) typedef uint16_t char16_t; #endif @@ -238,4 +243,574 @@ typedef struct { } napi_type_tag; #endif // NAPI_VERSION >= 8 +#if defined(NODE_API_MODULE_USE_VTABLE) || defined(NODE_API_RUNTIME_USE_VTABLE) + +// Vtable for JavaScript to native interop functions. +// New functions must be added at the end to maintain backward compatibility. +typedef struct node_api_js_vtable { + napi_status(NAPI_CDECL* get_last_error_info)( + node_api_basic_env env, const napi_extended_error_info** result); + + napi_status(NAPI_CDECL* get_undefined)(napi_env env, napi_value* result); + napi_status(NAPI_CDECL* get_null)(napi_env env, napi_value* result); + napi_status(NAPI_CDECL* get_global)(napi_env env, napi_value* result); + napi_status(NAPI_CDECL* get_boolean)(napi_env env, + bool value, + napi_value* result); + + napi_status(NAPI_CDECL* create_object)(napi_env env, napi_value* result); + + napi_status(NAPI_CDECL* create_array)(napi_env env, napi_value* result); + napi_status(NAPI_CDECL* create_array_with_length)(napi_env env, + size_t length, + napi_value* result); + napi_status(NAPI_CDECL* create_double)(napi_env env, + double value, + napi_value* result); + napi_status(NAPI_CDECL* create_int32)(napi_env env, + int32_t value, + napi_value* result); + napi_status(NAPI_CDECL* create_uint32)(napi_env env, + uint32_t value, + napi_value* result); + napi_status(NAPI_CDECL* create_int64)(napi_env env, + int64_t value, + napi_value* result); + napi_status(NAPI_CDECL* create_string_latin1)(napi_env env, + const char* str, + size_t length, + napi_value* result); + napi_status(NAPI_CDECL* create_string_utf8)(napi_env env, + const char* str, + size_t length, + napi_value* result); + napi_status(NAPI_CDECL* create_string_utf16)(napi_env env, + const char16_t* str, + size_t length, + napi_value* result); + + napi_status(NAPI_CDECL* create_symbol)(napi_env env, + napi_value description, + napi_value* result); + napi_status(NAPI_CDECL* create_function)(napi_env env, + const char* utf8name, + size_t length, + napi_callback cb, + void* data, + napi_value* result); + napi_status(NAPI_CDECL* create_error)(napi_env env, + napi_value code, + napi_value msg, + napi_value* result); + napi_status(NAPI_CDECL* create_type_error)(napi_env env, + napi_value code, + napi_value msg, + napi_value* result); + napi_status(NAPI_CDECL* create_range_error)(napi_env env, + napi_value code, + napi_value msg, + napi_value* result); + + // The name is changed to avoid conflict with the `typeof` keyword in C + napi_status(NAPI_CDECL* type_of)(napi_env env, + napi_value value, + napi_valuetype* result); + napi_status(NAPI_CDECL* get_value_double)(napi_env env, + napi_value value, + double* result); + napi_status(NAPI_CDECL* get_value_int32)(napi_env env, + napi_value value, + int32_t* result); + napi_status(NAPI_CDECL* get_value_uint32)(napi_env env, + napi_value value, + uint32_t* result); + napi_status(NAPI_CDECL* get_value_int64)(napi_env env, + napi_value value, + int64_t* result); + napi_status(NAPI_CDECL* get_value_bool)(napi_env env, + napi_value value, + bool* result); + + napi_status(NAPI_CDECL* get_value_string_latin1)(napi_env env, + napi_value value, + char* buf, + size_t bufsize, + size_t* result); + + napi_status(NAPI_CDECL* get_value_string_utf8)(napi_env env, + napi_value value, + char* buf, + size_t bufsize, + size_t* result); + + napi_status(NAPI_CDECL* get_value_string_utf16)(napi_env env, + napi_value value, + char16_t* buf, + size_t bufsize, + size_t* result); + + napi_status(NAPI_CDECL* coerce_to_bool)(napi_env env, + napi_value value, + napi_value* result); + napi_status(NAPI_CDECL* coerce_to_number)(napi_env env, + napi_value value, + napi_value* result); + napi_status(NAPI_CDECL* coerce_to_object)(napi_env env, + napi_value value, + napi_value* result); + napi_status(NAPI_CDECL* coerce_to_string)(napi_env env, + napi_value value, + napi_value* result); + + napi_status(NAPI_CDECL* get_prototype)(napi_env env, + napi_value object, + napi_value* result); + napi_status(NAPI_CDECL* get_property_names)(napi_env env, + napi_value object, + napi_value* result); + napi_status(NAPI_CDECL* set_property)(napi_env env, + napi_value object, + napi_value key, + napi_value value); + napi_status(NAPI_CDECL* has_property)(napi_env env, + napi_value object, + napi_value key, + bool* result); + napi_status(NAPI_CDECL* get_property)(napi_env env, + napi_value object, + napi_value key, + napi_value* result); + napi_status(NAPI_CDECL* delete_property)(napi_env env, + napi_value object, + napi_value key, + bool* result); + napi_status(NAPI_CDECL* has_own_property)(napi_env env, + napi_value object, + napi_value key, + bool* result); + napi_status(NAPI_CDECL* set_named_property)(napi_env env, + napi_value object, + const char* utf8name, + napi_value value); + napi_status(NAPI_CDECL* has_named_property)(napi_env env, + napi_value object, + const char* utf8name, + bool* result); + napi_status(NAPI_CDECL* get_named_property)(napi_env env, + napi_value object, + const char* utf8name, + napi_value* result); + napi_status(NAPI_CDECL* set_element)(napi_env env, + napi_value object, + uint32_t index, + napi_value value); + napi_status(NAPI_CDECL* has_element)(napi_env env, + napi_value object, + uint32_t index, + bool* result); + napi_status(NAPI_CDECL* get_element)(napi_env env, + napi_value object, + uint32_t index, + napi_value* result); + napi_status(NAPI_CDECL* delete_element)(napi_env env, + napi_value object, + uint32_t index, + bool* result); + napi_status(NAPI_CDECL* define_properties)( + napi_env env, + napi_value object, + size_t property_count, + const napi_property_descriptor* properties); + + napi_status(NAPI_CDECL* is_array)(napi_env env, + napi_value value, + bool* result); + napi_status(NAPI_CDECL* get_array_length)(napi_env env, + napi_value value, + uint32_t* result); + + napi_status(NAPI_CDECL* strict_equals)(napi_env env, + napi_value lhs, + napi_value rhs, + bool* result); + + napi_status(NAPI_CDECL* call_function)(napi_env env, + napi_value recv, + napi_value func, + size_t argc, + const napi_value* argv, + napi_value* result); + napi_status(NAPI_CDECL* new_instance)(napi_env env, + napi_value constructor, + size_t argc, + const napi_value* argv, + napi_value* result); + napi_status(NAPI_CDECL* instanceof)(napi_env env, + napi_value object, + napi_value constructor, + bool* result); + + napi_status(NAPI_CDECL* get_cb_info)(napi_env env, + napi_callback_info cbinfo, + size_t* argc, + napi_value* argv, + napi_value* this_arg, + void** data); + + napi_status(NAPI_CDECL* get_new_target)(napi_env env, + napi_callback_info cbinfo, + napi_value* result); + napi_status(NAPI_CDECL* define_class)( + napi_env env, + const char* utf8name, + size_t length, + napi_callback constructor, + void* data, + size_t property_count, + const napi_property_descriptor* properties, + napi_value* result); + + napi_status(NAPI_CDECL* wrap)(napi_env env, + napi_value js_object, + void* native_object, + node_api_basic_finalize finalize_cb, + void* finalize_hint, + napi_ref* result); + napi_status(NAPI_CDECL* unwrap)(napi_env env, + napi_value js_object, + void** result); + napi_status(NAPI_CDECL* remove_wrap)(napi_env env, + napi_value js_object, + void** result); + napi_status(NAPI_CDECL* create_external)(napi_env env, + void* data, + node_api_basic_finalize finalize_cb, + void* finalize_hint, + napi_value* result); + napi_status(NAPI_CDECL* get_value_external)(napi_env env, + napi_value value, + void** result); + + napi_status(NAPI_CDECL* create_reference)(napi_env env, + napi_value value, + uint32_t initial_refcount, + napi_ref* result); + + napi_status(NAPI_CDECL* delete_reference)(node_api_basic_env env, + napi_ref ref); + + napi_status(NAPI_CDECL* reference_ref)(napi_env env, + napi_ref ref, + uint32_t* result); + + napi_status(NAPI_CDECL* reference_unref)(napi_env env, + napi_ref ref, + uint32_t* result); + + napi_status(NAPI_CDECL* get_reference_value)(napi_env env, + napi_ref ref, + napi_value* result); + + napi_status(NAPI_CDECL* open_handle_scope)(napi_env env, + napi_handle_scope* result); + napi_status(NAPI_CDECL* close_handle_scope)(napi_env env, + napi_handle_scope scope); + napi_status(NAPI_CDECL* open_escapable_handle_scope)( + napi_env env, napi_escapable_handle_scope* result); + napi_status(NAPI_CDECL* close_escapable_handle_scope)( + napi_env env, napi_escapable_handle_scope scope); + + napi_status(NAPI_CDECL* escape_handle)(napi_env env, + napi_escapable_handle_scope scope, + napi_value escapee, + napi_value* result); + + // The name is changed to avoid conflict with the `throw` keyword in C++ + napi_status(NAPI_CDECL* throw_value)(napi_env env, napi_value error); + napi_status(NAPI_CDECL* throw_error)(napi_env env, + const char* code, + const char* msg); + napi_status(NAPI_CDECL* throw_type_error)(napi_env env, + const char* code, + const char* msg); + napi_status(NAPI_CDECL* throw_range_error)(napi_env env, + const char* code, + const char* msg); + napi_status(NAPI_CDECL* is_error)(napi_env env, + napi_value value, + bool* result); + + napi_status(NAPI_CDECL* is_exception_pending)(napi_env env, bool* result); + napi_status(NAPI_CDECL* get_and_clear_last_exception)(napi_env env, + napi_value* result); + + napi_status(NAPI_CDECL* is_arraybuffer)(napi_env env, + napi_value value, + bool* result); + napi_status(NAPI_CDECL* create_arraybuffer)(napi_env env, + size_t byte_length, + void** data, + napi_value* result); + napi_status(NAPI_CDECL* create_external_arraybuffer)( + napi_env env, + void* external_data, + size_t byte_length, + node_api_basic_finalize finalize_cb, + void* finalize_hint, + napi_value* result); + napi_status(NAPI_CDECL* get_arraybuffer_info)(napi_env env, + napi_value arraybuffer, + void** data, + size_t* byte_length); + napi_status(NAPI_CDECL* is_typedarray)(napi_env env, + napi_value value, + bool* result); + napi_status(NAPI_CDECL* create_typedarray)(napi_env env, + napi_typedarray_type type, + size_t length, + napi_value arraybuffer, + size_t byte_offset, + napi_value* result); + napi_status(NAPI_CDECL* get_typedarray_info)(napi_env env, + napi_value typedarray, + napi_typedarray_type* type, + size_t* length, + void** data, + napi_value* arraybuffer, + size_t* byte_offset); + + napi_status(NAPI_CDECL* create_dataview)(napi_env env, + size_t length, + napi_value arraybuffer, + size_t byte_offset, + napi_value* result); + napi_status(NAPI_CDECL* is_dataview)(napi_env env, + napi_value value, + bool* result); + napi_status(NAPI_CDECL* get_dataview_info)(napi_env env, + napi_value dataview, + size_t* bytelength, + void** data, + napi_value* arraybuffer, + size_t* byte_offset); + + napi_status(NAPI_CDECL* get_version)(node_api_basic_env env, + uint32_t* result); + + napi_status(NAPI_CDECL* create_promise)(napi_env env, + napi_deferred* deferred, + napi_value* promise); + napi_status(NAPI_CDECL* resolve_deferred)(napi_env env, + napi_deferred deferred, + napi_value resolution); + napi_status(NAPI_CDECL* reject_deferred)(napi_env env, + napi_deferred deferred, + napi_value rejection); + napi_status(NAPI_CDECL* is_promise)(napi_env env, + napi_value value, + bool* is_promise); + + napi_status(NAPI_CDECL* run_script)(napi_env env, + napi_value script, + napi_value* result); + + napi_status(NAPI_CDECL* adjust_external_memory)(node_api_basic_env env, + int64_t change_in_bytes, + int64_t* adjusted_value); + +#if NAPI_VERSION >= 5 + + napi_status(NAPI_CDECL* create_date)(napi_env env, + double time, + napi_value* result); + + napi_status(NAPI_CDECL* is_date)(napi_env env, + napi_value value, + bool* is_date); + + napi_status(NAPI_CDECL* get_date_value)(napi_env env, + napi_value value, + double* result); + + napi_status(NAPI_CDECL* add_finalizer)(napi_env env, + napi_value js_object, + void* finalize_data, + node_api_basic_finalize finalize_cb, + void* finalize_hint, + napi_ref* result); + +#endif // NAPI_VERSION >= 5 + +#if NAPI_VERSION >= 6 + + napi_status(NAPI_CDECL* create_bigint_int64)(napi_env env, + int64_t value, + napi_value* result); + napi_status(NAPI_CDECL* create_bigint_uint64)(napi_env env, + uint64_t value, + napi_value* result); + napi_status(NAPI_CDECL* create_bigint_words)(napi_env env, + int sign_bit, + size_t word_count, + const uint64_t* words, + napi_value* result); + napi_status(NAPI_CDECL* get_value_bigint_int64)(napi_env env, + napi_value value, + int64_t* result, + bool* lossless); + napi_status(NAPI_CDECL* get_value_bigint_uint64)(napi_env env, + napi_value value, + uint64_t* result, + bool* lossless); + napi_status(NAPI_CDECL* get_value_bigint_words)(napi_env env, + napi_value value, + int* sign_bit, + size_t* word_count, + uint64_t* words); + + napi_status(NAPI_CDECL* get_all_property_names)( + napi_env env, + napi_value object, + napi_key_collection_mode key_mode, + napi_key_filter key_filter, + napi_key_conversion key_conversion, + napi_value* result); + + napi_status(NAPI_CDECL* set_instance_data)(node_api_basic_env env, + void* data, + napi_finalize finalize_cb, + void* finalize_hint); + + napi_status(NAPI_CDECL* get_instance_data)(node_api_basic_env env, + void** data); + +#endif // NAPI_VERSION >= 6 + +#if NAPI_VERSION >= 7 + + napi_status(NAPI_CDECL* detach_arraybuffer)(napi_env env, + napi_value arraybuffer); + + napi_status(NAPI_CDECL* is_detached_arraybuffer)(napi_env env, + napi_value value, + bool* result); +#endif // NAPI_VERSION >= 7 + +#if NAPI_VERSION >= 8 + + napi_status(NAPI_CDECL* type_tag_object)(napi_env env, + napi_value value, + const napi_type_tag* type_tag); + + napi_status(NAPI_CDECL* check_object_type_tag)(napi_env env, + napi_value value, + const napi_type_tag* type_tag, + bool* result); + napi_status(NAPI_CDECL* object_freeze)(napi_env env, napi_value object); + napi_status(NAPI_CDECL* object_seal)(napi_env env, napi_value object); + +#endif // NAPI_VERSION >= 8 + +#if NAPI_VERSION >= 9 + + napi_status(NAPI_CDECL* symbol_for)(napi_env env, + const char* utf8description, + size_t length, + napi_value* result); + + napi_status(NAPI_CDECL* create_syntax_error)(napi_env env, + napi_value code, + napi_value msg, + napi_value* result); + + napi_status(NAPI_CDECL* throw_syntax_error)(napi_env env, + const char* code, + const char* msg); + +#endif // NAPI_VERSION >= 9 + +#if NAPI_VERSION >= 10 + + napi_status(NAPI_CDECL* create_external_string_latin1)( + napi_env env, + char* str, + size_t length, + node_api_basic_finalize finalize_callback, + void* finalize_hint, + napi_value* result, + bool* copied); + napi_status(NAPI_CDECL* create_external_string_utf16)( + napi_env env, + char16_t* str, + size_t length, + node_api_basic_finalize finalize_callback, + void* finalize_hint, + napi_value* result, + bool* copied); + + napi_status(NAPI_CDECL* create_property_key_latin1)(napi_env env, + const char* str, + size_t length, + napi_value* result); + napi_status(NAPI_CDECL* create_property_key_utf8)(napi_env env, + const char* str, + size_t length, + napi_value* result); + napi_status(NAPI_CDECL* create_property_key_utf16)(napi_env env, + const char16_t* str, + size_t length, + napi_value* result); + +#endif // NAPI_VERSION >= 10 + +#ifdef NAPI_EXPERIMENTAL + + napi_status(NAPI_CDECL* post_finalizer)(node_api_basic_env env, + napi_finalize finalize_cb, + void* finalize_data, + void* finalize_hint); + + napi_status(NAPI_CDECL* create_object_with_properties)( + napi_env env, + napi_value prototype_or_null, + napi_value* property_names, + napi_value* property_values, + size_t property_count, + napi_value* result); + + napi_status(NAPI_CDECL* is_sharedarraybuffer)(napi_env env, + napi_value value, + bool* result); + napi_status(NAPI_CDECL* create_sharedarraybuffer)(napi_env env, + size_t byte_length, + void** data, + napi_value* result); + + napi_status(NAPI_CDECL* set_prototype)(napi_env env, + napi_value object, + napi_value value); + +#endif // NAPI_EXPERIMENTAL +} node_api_js_vtable; + +// Sentinel format: "NODE_VT" (7 bytes) + marker byte. +// Marker byte = (version << 1) | 1 +// - Bit 0 is always 1: ensures the sentinel can never match a C++ vtable +// pointer (which is always pointer-aligned, thus bit 0 = 0). +// - Bits 1-7: struct version number (0-127). +#define NODE_API_VT_SENTINEL_VERSION 0 +#define NODE_API_VT_SENTINEL_MAKE(version) \ + (0x4E4F44455F565400ULL | (((version) << 1) | 1)) +#define NODE_API_VT_SENTINEL \ + NODE_API_VT_SENTINEL_MAKE(NODE_API_VT_SENTINEL_VERSION) + +struct napi_env__ { + uint64_t sentinel; // Should be NODE_API_VT_SENTINEL + const struct node_api_js_vtable* js_vtable; + const struct node_api_module_vtable* module_vtable; +}; + +#endif // defined(NODE_API_MODULE_USE_VTABLE) || + // defined(NODE_API_RUNTIME_USE_VTABLE) + #endif // SRC_JS_NATIVE_API_TYPES_H_ diff --git a/src/js_native_api_v8.cc b/src/js_native_api_v8.cc index 42e15e959bb1c9..762d00a85d71af 100644 --- a/src/js_native_api_v8.cc +++ b/src/js_native_api_v8.cc @@ -1,6 +1,7 @@ #include #include // INT_MAX #include + #ifndef NAPI_EXPERIMENTAL #define NAPI_EXPERIMENTAL #endif @@ -18,7 +19,7 @@ #define CHECK_TO_NUMBER(env, context, result, src) \ CHECK_TO_TYPE((env), Number, (context), (result), (src), napi_number_expected) -// n-api defines NAPI_AUTO_LENGTH as the indicator that a string +// Node-API defines NAPI_AUTO_LENGTH as the indicator that a string // is null terminated. For V8 the equivalent is -1. The assert // validates that our cast of NAPI_AUTO_LENGTH results in -1 as // needed by V8. @@ -70,7 +71,9 @@ (out) = v8::type::New((buffer), (byte_offset), (length)); \ } while (0) -void napi_env__::InvokeFinalizerFromGC(v8impl::RefTracker* finalizer) { +namespace v8impl { + +void NodeApiBaseEnv::InvokeFinalizerFromGC(v8impl::RefTracker* finalizer) { if (module_api_version != NAPI_VERSION_EXPERIMENTAL) { EnqueueFinalizer(finalizer); } else { @@ -85,11 +88,10 @@ void napi_env__::InvokeFinalizerFromGC(v8impl::RefTracker* finalizer) { } } -namespace v8impl { namespace { template -napi_status NewString(napi_env env, +napi_status NewString(NodeApiBaseEnv* env, const CCharType* str, size_t length, napi_value* result, @@ -104,7 +106,7 @@ napi_status NewString(napi_env env, } template -napi_status NewExternalString(napi_env env, +napi_status NewExternalString(NodeApiBaseEnv* env, CharType* str, size_t length, napi_finalize finalize_callback, @@ -138,7 +140,7 @@ napi_status NewExternalString(napi_env env, class TrackedStringResource : private RefTracker { public: - TrackedStringResource(napi_env env, + TrackedStringResource(NodeApiBaseEnv* env, napi_finalize finalize_callback, void* data, void* finalize_hint) @@ -171,7 +173,7 @@ class ExternalOneByteStringResource final : public v8::String::ExternalOneByteStringResource, TrackedStringResource { public: - ExternalOneByteStringResource(napi_env env, + ExternalOneByteStringResource(NodeApiBaseEnv* env, char* string, const size_t length, napi_finalize finalize_callback, @@ -191,7 +193,7 @@ class ExternalOneByteStringResource final class ExternalStringResource final : public v8::String::ExternalStringResource, TrackedStringResource { public: - ExternalStringResource(napi_env env, + ExternalStringResource(NodeApiBaseEnv* env, char16_t* string, const size_t length, napi_finalize finalize_callback, @@ -209,7 +211,7 @@ class ExternalStringResource final : public v8::String::ExternalStringResource, }; inline napi_status V8NameFromPropertyDescriptor( - napi_env env, + NodeApiBaseEnv* env, const napi_property_descriptor* p, v8::Local* result) { if (p->utf8name != nullptr) { @@ -225,7 +227,7 @@ inline napi_status V8NameFromPropertyDescriptor( return napi_ok; } -// convert from n-api property attributes to v8::PropertyAttribute +// convert from Node-API property attributes to v8::PropertyAttribute inline v8::PropertyAttribute V8PropertyAttributesFromDescriptor( const napi_property_descriptor* descriptor) { unsigned int attribute_flags = v8::PropertyAttribute::None; @@ -306,7 +308,7 @@ V8EscapableHandleScopeFromJsEscapableHandleScope( return reinterpret_cast(s); } -inline napi_status ConcludeDeferred(napi_env env, +inline napi_status ConcludeDeferred(NodeApiBaseEnv* env, napi_deferred deferred, napi_value result, bool is_resolved) { @@ -336,7 +338,7 @@ inline napi_status ConcludeDeferred(napi_env env, enum UnwrapAction { KeepWrap, RemoveWrap }; -inline napi_status Unwrap(napi_env env, +inline napi_status Unwrap(NodeApiBaseEnv* env, napi_value js_object, void** result, UnwrapAction action) { @@ -378,17 +380,16 @@ inline napi_status Unwrap(napi_env env, //=== Function napi_callback wrapper ================================= -// Use this data structure to associate callback data with each N-API function -// exposed to JavaScript. The structure is stored in a v8::External which gets -// passed into our callback wrapper. This reduces the performance impact of -// calling through N-API. -// Ref: benchmark/misc/function_call +// Use this data structure to associate callback data with each Node-API +// function exposed to JavaScript. The structure is stored in a v8::External +// which gets passed into our callback wrapper. This reduces the performance +// impact of calling through Node-API. Ref: benchmark/misc/function_call // Discussion (incl. perf. data): https://github.com/nodejs/node/pull/21072 class CallbackBundle { public: // Creates an object to be made available to the static function callback // wrapper, used to retrieve the native callback function and data pointer. - static inline v8::Local New(napi_env env, + static inline v8::Local New(NodeApiBaseEnv* env, napi_callback cb, void* data) { CallbackBundle* bundle = new CallbackBundle(); @@ -407,8 +408,8 @@ class CallbackBundle { } public: - napi_env env; // Necessary to invoke C++ NAPI callback - void* cb_data; // The user provided callback data + NodeApiBaseEnv* env; // Necessary to invoke C++ NAPI callback + void* cb_data; // The user provided callback data napi_callback cb; private: @@ -427,7 +428,7 @@ class FunctionCallbackWrapper { cbwrapper.InvokeCallback(); } - static inline napi_status NewFunction(napi_env env, + static inline napi_status NewFunction(NodeApiBaseEnv* env, napi_callback cb, void* cb_data, v8::Local* result) { @@ -443,7 +444,7 @@ class FunctionCallbackWrapper { } static inline napi_status NewTemplate( - napi_env env, + NodeApiBaseEnv* env, napi_callback cb, void* cb_data, v8::Local* result, @@ -497,19 +498,20 @@ class FunctionCallbackWrapper { reinterpret_cast(this); // All other pointers we need are stored in `_bundle` - napi_env env = bundle_->env; + v8impl::NodeApiBaseEnv* env = bundle_->env; napi_callback cb = bundle_->cb; napi_value result = nullptr; bool exceptionOccurred = false; - env->CallIntoModule([&](napi_env env) { result = cb(env, cbinfo_wrapper); }, - [&](napi_env env, v8::Local value) { - exceptionOccurred = true; - if (env->terminatedOrTerminating()) { - return; - } - env->isolate->ThrowException(value); - }); + env->CallIntoModule( + [&](NodeApiBaseEnv* env) { result = cb(env, cbinfo_wrapper); }, + [&](NodeApiBaseEnv* env, v8::Local value) { + exceptionOccurred = true; + if (env->terminatedOrTerminating()) { + return; + } + env->isolate->ThrowException(value); + }); if (!exceptionOccurred && (result != nullptr)) { cbinfo_.GetReturnValue().Set(V8LocalValueFromJsValue(result)); @@ -521,7 +523,7 @@ class FunctionCallbackWrapper { CallbackBundle* bundle_; }; -inline napi_status Wrap(napi_env env, +inline napi_status Wrap(NodeApiBaseEnv* env, napi_value js_object, void* native_object, napi_finalize finalize_cb, @@ -621,14 +623,14 @@ void Finalizer::CallFinalizer() { } } -TrackedFinalizer::TrackedFinalizer(napi_env env, +TrackedFinalizer::TrackedFinalizer(NodeApiBaseEnv* env, napi_finalize finalize_callback, void* finalize_data, void* finalize_hint) : RefTracker(), finalizer_(env, finalize_callback, finalize_data, finalize_hint) {} -TrackedFinalizer* TrackedFinalizer::New(napi_env env, +TrackedFinalizer* TrackedFinalizer::New(NodeApiBaseEnv* env, napi_finalize finalize_callback, void* finalize_data, void* finalize_hint) { @@ -653,7 +655,7 @@ void TrackedFinalizer::Finalize() { delete this; } -Reference::Reference(napi_env env, +Reference::Reference(NodeApiBaseEnv* env, v8::Local value, uint32_t initial_refcount, ReferenceOwnership ownership) @@ -675,7 +677,7 @@ Reference::~Reference() { Unlink(); } -Reference* Reference::New(napi_env env, +Reference* Reference::New(NodeApiBaseEnv* env, v8::Local value, uint32_t initial_refcount, ReferenceOwnership ownership) { @@ -708,7 +710,7 @@ uint32_t Reference::Unref() { return refcount_; } -v8::Local Reference::Get(napi_env env) { +v8::Local Reference::Get(NodeApiBaseEnv* env) { if (persistent_.IsEmpty()) { return v8::Local(); } else { @@ -761,7 +763,7 @@ void Reference::WeakCallback(const v8::WeakCallbackInfo& data) { reference->InvokeFinalizerFromGC(); } -ReferenceWithData* ReferenceWithData::New(napi_env env, +ReferenceWithData* ReferenceWithData::New(NodeApiBaseEnv* env, v8::Local value, uint32_t initial_refcount, ReferenceOwnership ownership, @@ -772,7 +774,7 @@ ReferenceWithData* ReferenceWithData::New(napi_env env, return reference; } -ReferenceWithData::ReferenceWithData(napi_env env, +ReferenceWithData::ReferenceWithData(NodeApiBaseEnv* env, v8::Local value, uint32_t initial_refcount, ReferenceOwnership ownership, @@ -780,7 +782,7 @@ ReferenceWithData::ReferenceWithData(napi_env env, : Reference(env, value, initial_refcount, ownership), data_(data) {} ReferenceWithFinalizer* ReferenceWithFinalizer::New( - napi_env env, + NodeApiBaseEnv* env, v8::Local value, uint32_t initial_refcount, ReferenceOwnership ownership, @@ -799,7 +801,7 @@ ReferenceWithFinalizer* ReferenceWithFinalizer::New( return reference; } -ReferenceWithFinalizer::ReferenceWithFinalizer(napi_env env, +ReferenceWithFinalizer::ReferenceWithFinalizer(NodeApiBaseEnv* env, v8::Local value, uint32_t initial_refcount, ReferenceOwnership ownership, @@ -841,7 +843,7 @@ class ExternalWrapper { } public: - static v8::Local New(napi_env env, void* data) { + static v8::Local New(NodeApiBaseEnv* env, void* data) { ExternalWrapper* wrapper = new ExternalWrapper(data); v8::Local external = v8::External::New(env->isolate, wrapper); wrapper->persistent_.Reset(env->isolate, external); @@ -910,7 +912,7 @@ static const char* error_messages[] = { napi_status NAPI_CDECL napi_get_last_error_info( node_api_basic_env basic_env, const napi_extended_error_info** result) { - napi_env env = const_cast(basic_env); + v8impl::NodeApiBaseEnv* env = v8impl::AsNodeApiBaseEnv(basic_env); CHECK_ENV(env); CHECK_ARG(env, result); @@ -934,12 +936,13 @@ napi_status NAPI_CDECL napi_get_last_error_info( return napi_ok; } -napi_status NAPI_CDECL napi_create_function(napi_env env, +napi_status NAPI_CDECL napi_create_function(napi_env env_, const char* utf8name, size_t length, napi_callback cb, void* callback_data, napi_value* result) { + v8impl::NodeApiBaseEnv* env = v8impl::AsNodeApiBaseEnv(env_); NAPI_PREAMBLE(env); CHECK_ARG(env, result); CHECK_ARG(env, cb); @@ -963,7 +966,7 @@ napi_status NAPI_CDECL napi_create_function(napi_env env, } napi_status NAPI_CDECL -napi_define_class(napi_env env, +napi_define_class(napi_env env_, const char* utf8name, size_t length, napi_callback constructor, @@ -971,6 +974,7 @@ napi_define_class(napi_env env, size_t property_count, const napi_property_descriptor* properties, napi_value* result) { + v8impl::NodeApiBaseEnv* env = v8impl::AsNodeApiBaseEnv(env_); NAPI_PREAMBLE(env); CHECK_ARG(env, result); CHECK_ARG(env, constructor); @@ -1058,11 +1062,11 @@ napi_define_class(napi_env env, return GET_RETURN_STATUS(env); } -napi_status NAPI_CDECL napi_get_property_names(napi_env env, +napi_status NAPI_CDECL napi_get_property_names(napi_env env_, napi_value object, napi_value* result) { return napi_get_all_property_names( - env, + env_, object, napi_key_include_prototypes, static_cast(napi_key_enumerable | napi_key_skip_symbols), @@ -1071,12 +1075,13 @@ napi_status NAPI_CDECL napi_get_property_names(napi_env env, } napi_status NAPI_CDECL -napi_get_all_property_names(napi_env env, +napi_get_all_property_names(napi_env env_, napi_value object, napi_key_collection_mode key_mode, napi_key_filter key_filter, napi_key_conversion key_conversion, napi_value* result) { + v8impl::NodeApiBaseEnv* env = v8impl::AsNodeApiBaseEnv(env_); NAPI_PREAMBLE(env); CHECK_ARG(env, result); @@ -1145,10 +1150,11 @@ napi_get_all_property_names(napi_env env, return GET_RETURN_STATUS(env); } -napi_status NAPI_CDECL napi_set_property(napi_env env, +napi_status NAPI_CDECL napi_set_property(napi_env env_, napi_value object, napi_value key, napi_value value) { + v8impl::NodeApiBaseEnv* env = v8impl::AsNodeApiBaseEnv(env_); NAPI_PREAMBLE(env); CHECK_ARG(env, key); CHECK_ARG(env, value); @@ -1168,10 +1174,11 @@ napi_status NAPI_CDECL napi_set_property(napi_env env, return GET_RETURN_STATUS(env); } -napi_status NAPI_CDECL napi_has_property(napi_env env, +napi_status NAPI_CDECL napi_has_property(napi_env env_, napi_value object, napi_value key, bool* result) { + v8impl::NodeApiBaseEnv* env = v8impl::AsNodeApiBaseEnv(env_); NAPI_PREAMBLE(env); CHECK_ARG(env, result); CHECK_ARG(env, key); @@ -1190,10 +1197,11 @@ napi_status NAPI_CDECL napi_has_property(napi_env env, return GET_RETURN_STATUS(env); } -napi_status NAPI_CDECL napi_get_property(napi_env env, +napi_status NAPI_CDECL napi_get_property(napi_env env_, napi_value object, napi_value key, napi_value* result) { + v8impl::NodeApiBaseEnv* env = v8impl::AsNodeApiBaseEnv(env_); NAPI_PREAMBLE(env); CHECK_ARG(env, key); CHECK_ARG(env, result); @@ -1213,10 +1221,11 @@ napi_status NAPI_CDECL napi_get_property(napi_env env, return GET_RETURN_STATUS(env); } -napi_status NAPI_CDECL napi_delete_property(napi_env env, +napi_status NAPI_CDECL napi_delete_property(napi_env env_, napi_value object, napi_value key, bool* result) { + v8impl::NodeApiBaseEnv* env = v8impl::AsNodeApiBaseEnv(env_); NAPI_PREAMBLE(env); CHECK_ARG(env, key); @@ -1233,10 +1242,11 @@ napi_status NAPI_CDECL napi_delete_property(napi_env env, return GET_RETURN_STATUS(env); } -napi_status NAPI_CDECL napi_has_own_property(napi_env env, +napi_status NAPI_CDECL napi_has_own_property(napi_env env_, napi_value object, napi_value key, bool* result) { + v8impl::NodeApiBaseEnv* env = v8impl::AsNodeApiBaseEnv(env_); NAPI_PREAMBLE(env); CHECK_ARG(env, key); CHECK_ARG(env, result); @@ -1254,10 +1264,11 @@ napi_status NAPI_CDECL napi_has_own_property(napi_env env, return GET_RETURN_STATUS(env); } -napi_status NAPI_CDECL napi_set_named_property(napi_env env, +napi_status NAPI_CDECL napi_set_named_property(napi_env env_, napi_value object, const char* utf8name, napi_value value) { + v8impl::NodeApiBaseEnv* env = v8impl::AsNodeApiBaseEnv(env_); NAPI_PREAMBLE(env); CHECK_ARG(env, value); @@ -1278,10 +1289,11 @@ napi_status NAPI_CDECL napi_set_named_property(napi_env env, return GET_RETURN_STATUS(env); } -napi_status NAPI_CDECL napi_has_named_property(napi_env env, +napi_status NAPI_CDECL napi_has_named_property(napi_env env_, napi_value object, const char* utf8name, bool* result) { + v8impl::NodeApiBaseEnv* env = v8impl::AsNodeApiBaseEnv(env_); NAPI_PREAMBLE(env); CHECK_ARG(env, result); @@ -1301,10 +1313,11 @@ napi_status NAPI_CDECL napi_has_named_property(napi_env env, return GET_RETURN_STATUS(env); } -napi_status NAPI_CDECL napi_get_named_property(napi_env env, +napi_status NAPI_CDECL napi_get_named_property(napi_env env_, napi_value object, const char* utf8name, napi_value* result) { + v8impl::NodeApiBaseEnv* env = v8impl::AsNodeApiBaseEnv(env_); NAPI_PREAMBLE(env); CHECK_ARG(env, result); @@ -1326,10 +1339,11 @@ napi_status NAPI_CDECL napi_get_named_property(napi_env env, return GET_RETURN_STATUS(env); } -napi_status NAPI_CDECL napi_set_element(napi_env env, +napi_status NAPI_CDECL napi_set_element(napi_env env_, napi_value object, uint32_t index, napi_value value) { + v8impl::NodeApiBaseEnv* env = v8impl::AsNodeApiBaseEnv(env_); NAPI_PREAMBLE(env); CHECK_ARG(env, value); @@ -1347,10 +1361,11 @@ napi_status NAPI_CDECL napi_set_element(napi_env env, return GET_RETURN_STATUS(env); } -napi_status NAPI_CDECL napi_has_element(napi_env env, +napi_status NAPI_CDECL napi_has_element(napi_env env_, napi_value object, uint32_t index, bool* result) { + v8impl::NodeApiBaseEnv* env = v8impl::AsNodeApiBaseEnv(env_); NAPI_PREAMBLE(env); CHECK_ARG(env, result); @@ -1367,10 +1382,11 @@ napi_status NAPI_CDECL napi_has_element(napi_env env, return GET_RETURN_STATUS(env); } -napi_status NAPI_CDECL napi_get_element(napi_env env, +napi_status NAPI_CDECL napi_get_element(napi_env env_, napi_value object, uint32_t index, napi_value* result) { + v8impl::NodeApiBaseEnv* env = v8impl::AsNodeApiBaseEnv(env_); NAPI_PREAMBLE(env); CHECK_ARG(env, result); @@ -1387,10 +1403,11 @@ napi_status NAPI_CDECL napi_get_element(napi_env env, return GET_RETURN_STATUS(env); } -napi_status NAPI_CDECL napi_delete_element(napi_env env, +napi_status NAPI_CDECL napi_delete_element(napi_env env_, napi_value object, uint32_t index, bool* result) { + v8impl::NodeApiBaseEnv* env = v8impl::AsNodeApiBaseEnv(env_); NAPI_PREAMBLE(env); v8::Local context = env->context(); @@ -1406,10 +1423,11 @@ napi_status NAPI_CDECL napi_delete_element(napi_env env, } napi_status NAPI_CDECL -napi_define_properties(napi_env env, +napi_define_properties(napi_env env_, napi_value object, size_t property_count, const napi_property_descriptor* properties) { + v8impl::NodeApiBaseEnv* env = v8impl::AsNodeApiBaseEnv(env_); NAPI_PREAMBLE(env); if (property_count > 0) { CHECK_ARG(env, properties); @@ -1488,7 +1506,8 @@ napi_define_properties(napi_env env, return GET_RETURN_STATUS(env); } -napi_status NAPI_CDECL napi_object_freeze(napi_env env, napi_value object) { +napi_status NAPI_CDECL napi_object_freeze(napi_env env_, napi_value object) { + v8impl::NodeApiBaseEnv* env = v8impl::AsNodeApiBaseEnv(env_); NAPI_PREAMBLE(env); v8::Local context = env->context(); @@ -1505,7 +1524,8 @@ napi_status NAPI_CDECL napi_object_freeze(napi_env env, napi_value object) { return GET_RETURN_STATUS(env); } -napi_status NAPI_CDECL napi_object_seal(napi_env env, napi_value object) { +napi_status NAPI_CDECL napi_object_seal(napi_env env_, napi_value object) { + v8impl::NodeApiBaseEnv* env = v8impl::AsNodeApiBaseEnv(env_); NAPI_PREAMBLE(env); v8::Local context = env->context(); @@ -1522,9 +1542,10 @@ napi_status NAPI_CDECL napi_object_seal(napi_env env, napi_value object) { return GET_RETURN_STATUS(env); } -napi_status NAPI_CDECL napi_is_array(napi_env env, +napi_status NAPI_CDECL napi_is_array(napi_env env_, napi_value value, bool* result) { + v8impl::NodeApiBaseEnv* env = v8impl::AsNodeApiBaseEnv(env_); CHECK_ENV_NOT_IN_GC(env); CHECK_ARG(env, value); CHECK_ARG(env, result); @@ -1535,9 +1556,10 @@ napi_status NAPI_CDECL napi_is_array(napi_env env, return napi_clear_last_error(env); } -napi_status NAPI_CDECL napi_get_array_length(napi_env env, +napi_status NAPI_CDECL napi_get_array_length(napi_env env_, napi_value value, uint32_t* result) { + v8impl::NodeApiBaseEnv* env = v8impl::AsNodeApiBaseEnv(env_); NAPI_PREAMBLE(env); CHECK_ARG(env, value); CHECK_ARG(env, result); @@ -1551,10 +1573,11 @@ napi_status NAPI_CDECL napi_get_array_length(napi_env env, return GET_RETURN_STATUS(env); } -napi_status NAPI_CDECL napi_strict_equals(napi_env env, +napi_status NAPI_CDECL napi_strict_equals(napi_env env_, napi_value lhs, napi_value rhs, bool* result) { + v8impl::NodeApiBaseEnv* env = v8impl::AsNodeApiBaseEnv(env_); NAPI_PREAMBLE(env); CHECK_ARG(env, lhs); CHECK_ARG(env, rhs); @@ -1567,9 +1590,10 @@ napi_status NAPI_CDECL napi_strict_equals(napi_env env, return GET_RETURN_STATUS(env); } -napi_status NAPI_CDECL node_api_set_prototype(napi_env env, +napi_status NAPI_CDECL node_api_set_prototype(napi_env env_, napi_value object, napi_value value) { + v8impl::NodeApiBaseEnv* env = v8impl::AsNodeApiBaseEnv(env_); NAPI_PREAMBLE(env); CHECK_ARG(env, value); @@ -1587,9 +1611,10 @@ napi_status NAPI_CDECL node_api_set_prototype(napi_env env, return GET_RETURN_STATUS(env); } -napi_status NAPI_CDECL napi_get_prototype(napi_env env, +napi_status NAPI_CDECL napi_get_prototype(napi_env env_, napi_value object, napi_value* result) { + v8impl::NodeApiBaseEnv* env = v8impl::AsNodeApiBaseEnv(env_); NAPI_PREAMBLE(env); CHECK_ARG(env, result); @@ -1604,7 +1629,8 @@ napi_status NAPI_CDECL napi_get_prototype(napi_env env, return GET_RETURN_STATUS(env); } -napi_status NAPI_CDECL napi_create_object(napi_env env, napi_value* result) { +napi_status NAPI_CDECL napi_create_object(napi_env env_, napi_value* result) { + v8impl::NodeApiBaseEnv* env = v8impl::AsNodeApiBaseEnv(env_); CHECK_ENV_NOT_IN_GC(env); CHECK_ARG(env, result); @@ -1614,12 +1640,13 @@ napi_status NAPI_CDECL napi_create_object(napi_env env, napi_value* result) { } napi_status NAPI_CDECL -napi_create_object_with_properties(napi_env env, +napi_create_object_with_properties(napi_env env_, napi_value prototype_or_null, napi_value* property_names, napi_value* property_values, size_t property_count, napi_value* result) { + v8impl::NodeApiBaseEnv* env = v8impl::AsNodeApiBaseEnv(env_); CHECK_ENV_NOT_IN_GC(env); CHECK_ARG(env, result); @@ -1657,7 +1684,8 @@ napi_create_object_with_properties(napi_env env, return napi_clear_last_error(env); } -napi_status NAPI_CDECL napi_create_array(napi_env env, napi_value* result) { +napi_status NAPI_CDECL napi_create_array(napi_env env_, napi_value* result) { + v8impl::NodeApiBaseEnv* env = v8impl::AsNodeApiBaseEnv(env_); CHECK_ENV_NOT_IN_GC(env); CHECK_ARG(env, result); @@ -1666,9 +1694,10 @@ napi_status NAPI_CDECL napi_create_array(napi_env env, napi_value* result) { return napi_clear_last_error(env); } -napi_status NAPI_CDECL napi_create_array_with_length(napi_env env, +napi_status NAPI_CDECL napi_create_array_with_length(napi_env env_, size_t length, napi_value* result) { + v8impl::NodeApiBaseEnv* env = v8impl::AsNodeApiBaseEnv(env_); CHECK_ENV_NOT_IN_GC(env); CHECK_ARG(env, result); @@ -1678,10 +1707,11 @@ napi_status NAPI_CDECL napi_create_array_with_length(napi_env env, return napi_clear_last_error(env); } -napi_status NAPI_CDECL napi_create_string_latin1(napi_env env, +napi_status NAPI_CDECL napi_create_string_latin1(napi_env env_, const char* str, size_t length, napi_value* result) { + v8impl::NodeApiBaseEnv* env = v8impl::AsNodeApiBaseEnv(env_); return v8impl::NewString(env, str, length, result, [&](v8::Isolate* isolate) { return v8::String::NewFromOneByte(isolate, reinterpret_cast(str), @@ -1690,20 +1720,22 @@ napi_status NAPI_CDECL napi_create_string_latin1(napi_env env, }); } -napi_status NAPI_CDECL napi_create_string_utf8(napi_env env, +napi_status NAPI_CDECL napi_create_string_utf8(napi_env env_, const char* str, size_t length, napi_value* result) { + v8impl::NodeApiBaseEnv* env = v8impl::AsNodeApiBaseEnv(env_); return v8impl::NewString(env, str, length, result, [&](v8::Isolate* isolate) { return v8::String::NewFromUtf8( isolate, str, v8::NewStringType::kNormal, static_cast(length)); }); } -napi_status NAPI_CDECL napi_create_string_utf16(napi_env env, +napi_status NAPI_CDECL napi_create_string_utf16(napi_env env_, const char16_t* str, size_t length, napi_value* result) { + v8impl::NodeApiBaseEnv* env = v8impl::AsNodeApiBaseEnv(env_); return v8impl::NewString(env, str, length, result, [&](v8::Isolate* isolate) { return v8::String::NewFromTwoByte(isolate, reinterpret_cast(str), @@ -1713,13 +1745,14 @@ napi_status NAPI_CDECL napi_create_string_utf16(napi_env env, } napi_status NAPI_CDECL node_api_create_external_string_latin1( - napi_env env, + napi_env env_, char* str, size_t length, node_api_basic_finalize basic_finalize_callback, void* finalize_hint, napi_value* result, bool* copied) { + v8impl::NodeApiBaseEnv* env = v8impl::AsNodeApiBaseEnv(env_); napi_finalize finalize_callback = reinterpret_cast(basic_finalize_callback); return v8impl::NewExternalString( @@ -1742,13 +1775,14 @@ napi_status NAPI_CDECL node_api_create_external_string_latin1( } napi_status NAPI_CDECL node_api_create_external_string_utf16( - napi_env env, + napi_env env_, char16_t* str, size_t length, node_api_basic_finalize basic_finalize_callback, void* finalize_hint, napi_value* result, bool* copied) { + v8impl::NodeApiBaseEnv* env = v8impl::AsNodeApiBaseEnv(env_); napi_finalize finalize_callback = reinterpret_cast(basic_finalize_callback); return v8impl::NewExternalString( @@ -1770,10 +1804,11 @@ napi_status NAPI_CDECL node_api_create_external_string_utf16( }); } -napi_status node_api_create_property_key_latin1(napi_env env, +napi_status node_api_create_property_key_latin1(napi_env env_, const char* str, size_t length, napi_value* result) { + v8impl::NodeApiBaseEnv* env = v8impl::AsNodeApiBaseEnv(env_); return v8impl::NewString(env, str, length, result, [&](v8::Isolate* isolate) { return v8::String::NewFromOneByte(isolate, reinterpret_cast(str), @@ -1782,10 +1817,11 @@ napi_status node_api_create_property_key_latin1(napi_env env, }); } -napi_status node_api_create_property_key_utf8(napi_env env, +napi_status node_api_create_property_key_utf8(napi_env env_, const char* str, size_t length, napi_value* result) { + v8impl::NodeApiBaseEnv* env = v8impl::AsNodeApiBaseEnv(env_); return v8impl::NewString(env, str, length, result, [&](v8::Isolate* isolate) { return v8::String::NewFromUtf8(isolate, str, @@ -1794,10 +1830,11 @@ napi_status node_api_create_property_key_utf8(napi_env env, }); } -napi_status NAPI_CDECL node_api_create_property_key_utf16(napi_env env, +napi_status NAPI_CDECL node_api_create_property_key_utf16(napi_env env_, const char16_t* str, size_t length, napi_value* result) { + v8impl::NodeApiBaseEnv* env = v8impl::AsNodeApiBaseEnv(env_); return v8impl::NewString(env, str, length, result, [&](v8::Isolate* isolate) { return v8::String::NewFromTwoByte(isolate, reinterpret_cast(str), @@ -1806,9 +1843,10 @@ napi_status NAPI_CDECL node_api_create_property_key_utf16(napi_env env, }); } -napi_status NAPI_CDECL napi_create_double(napi_env env, +napi_status NAPI_CDECL napi_create_double(napi_env env_, double value, napi_value* result) { + v8impl::NodeApiBaseEnv* env = v8impl::AsNodeApiBaseEnv(env_); CHECK_ENV_NOT_IN_GC(env); CHECK_ARG(env, result); @@ -1818,9 +1856,10 @@ napi_status NAPI_CDECL napi_create_double(napi_env env, return napi_clear_last_error(env); } -napi_status NAPI_CDECL napi_create_int32(napi_env env, +napi_status NAPI_CDECL napi_create_int32(napi_env env_, int32_t value, napi_value* result) { + v8impl::NodeApiBaseEnv* env = v8impl::AsNodeApiBaseEnv(env_); CHECK_ENV_NOT_IN_GC(env); CHECK_ARG(env, result); @@ -1830,9 +1869,10 @@ napi_status NAPI_CDECL napi_create_int32(napi_env env, return napi_clear_last_error(env); } -napi_status NAPI_CDECL napi_create_uint32(napi_env env, +napi_status NAPI_CDECL napi_create_uint32(napi_env env_, uint32_t value, napi_value* result) { + v8impl::NodeApiBaseEnv* env = v8impl::AsNodeApiBaseEnv(env_); CHECK_ENV_NOT_IN_GC(env); CHECK_ARG(env, result); @@ -1842,9 +1882,10 @@ napi_status NAPI_CDECL napi_create_uint32(napi_env env, return napi_clear_last_error(env); } -napi_status NAPI_CDECL napi_create_int64(napi_env env, +napi_status NAPI_CDECL napi_create_int64(napi_env env_, int64_t value, napi_value* result) { + v8impl::NodeApiBaseEnv* env = v8impl::AsNodeApiBaseEnv(env_); CHECK_ENV_NOT_IN_GC(env); CHECK_ARG(env, result); @@ -1854,9 +1895,10 @@ napi_status NAPI_CDECL napi_create_int64(napi_env env, return napi_clear_last_error(env); } -napi_status NAPI_CDECL napi_create_bigint_int64(napi_env env, +napi_status NAPI_CDECL napi_create_bigint_int64(napi_env env_, int64_t value, napi_value* result) { + v8impl::NodeApiBaseEnv* env = v8impl::AsNodeApiBaseEnv(env_); CHECK_ENV_NOT_IN_GC(env); CHECK_ARG(env, result); @@ -1866,9 +1908,10 @@ napi_status NAPI_CDECL napi_create_bigint_int64(napi_env env, return napi_clear_last_error(env); } -napi_status NAPI_CDECL napi_create_bigint_uint64(napi_env env, +napi_status NAPI_CDECL napi_create_bigint_uint64(napi_env env_, uint64_t value, napi_value* result) { + v8impl::NodeApiBaseEnv* env = v8impl::AsNodeApiBaseEnv(env_); CHECK_ENV_NOT_IN_GC(env); CHECK_ARG(env, result); @@ -1878,11 +1921,12 @@ napi_status NAPI_CDECL napi_create_bigint_uint64(napi_env env, return napi_clear_last_error(env); } -napi_status NAPI_CDECL napi_create_bigint_words(napi_env env, +napi_status NAPI_CDECL napi_create_bigint_words(napi_env env_, int sign_bit, size_t word_count, const uint64_t* words, napi_value* result) { + v8impl::NodeApiBaseEnv* env = v8impl::AsNodeApiBaseEnv(env_); NAPI_PREAMBLE(env); CHECK_ARG(env, words); CHECK_ARG(env, result); @@ -1900,9 +1944,10 @@ napi_status NAPI_CDECL napi_create_bigint_words(napi_env env, return GET_RETURN_STATUS(env); } -napi_status NAPI_CDECL napi_get_boolean(napi_env env, +napi_status NAPI_CDECL napi_get_boolean(napi_env env_, bool value, napi_value* result) { + v8impl::NodeApiBaseEnv* env = v8impl::AsNodeApiBaseEnv(env_); CHECK_ENV_NOT_IN_GC(env); CHECK_ARG(env, result); @@ -1917,9 +1962,10 @@ napi_status NAPI_CDECL napi_get_boolean(napi_env env, return napi_clear_last_error(env); } -napi_status NAPI_CDECL napi_create_symbol(napi_env env, +napi_status NAPI_CDECL napi_create_symbol(napi_env env_, napi_value description, napi_value* result) { + v8impl::NodeApiBaseEnv* env = v8impl::AsNodeApiBaseEnv(env_); CHECK_ENV_NOT_IN_GC(env); CHECK_ARG(env, result); @@ -1938,10 +1984,11 @@ napi_status NAPI_CDECL napi_create_symbol(napi_env env, return napi_clear_last_error(env); } -napi_status NAPI_CDECL node_api_symbol_for(napi_env env, +napi_status NAPI_CDECL node_api_symbol_for(napi_env env_, const char* utf8description, size_t length, napi_value* result) { + v8impl::NodeApiBaseEnv* env = v8impl::AsNodeApiBaseEnv(env_); CHECK_ENV_NOT_IN_GC(env); CHECK_ARG(env, result); @@ -1957,10 +2004,11 @@ napi_status NAPI_CDECL node_api_symbol_for(napi_env env, return napi_clear_last_error(env); } -static inline napi_status set_error_code(napi_env env, +static inline napi_status set_error_code(napi_env env_, v8::Local error, napi_value code, const char* code_cstring) { + v8impl::NodeApiBaseEnv* env = v8impl::AsNodeApiBaseEnv(env_); if ((code != nullptr) || (code_cstring != nullptr)) { v8::Local context = env->context(); v8::Local err_object = error.As(); @@ -1983,10 +2031,11 @@ static inline napi_status set_error_code(napi_env env, return napi_ok; } -napi_status NAPI_CDECL napi_create_error(napi_env env, +napi_status NAPI_CDECL napi_create_error(napi_env env_, napi_value code, napi_value msg, napi_value* result) { + v8impl::NodeApiBaseEnv* env = v8impl::AsNodeApiBaseEnv(env_); CHECK_ENV_NOT_IN_GC(env); CHECK_ARG(env, msg); CHECK_ARG(env, result); @@ -2003,10 +2052,11 @@ napi_status NAPI_CDECL napi_create_error(napi_env env, return napi_clear_last_error(env); } -napi_status NAPI_CDECL napi_create_type_error(napi_env env, +napi_status NAPI_CDECL napi_create_type_error(napi_env env_, napi_value code, napi_value msg, napi_value* result) { + v8impl::NodeApiBaseEnv* env = v8impl::AsNodeApiBaseEnv(env_); CHECK_ENV_NOT_IN_GC(env); CHECK_ARG(env, msg); CHECK_ARG(env, result); @@ -2023,10 +2073,11 @@ napi_status NAPI_CDECL napi_create_type_error(napi_env env, return napi_clear_last_error(env); } -napi_status NAPI_CDECL napi_create_range_error(napi_env env, +napi_status NAPI_CDECL napi_create_range_error(napi_env env_, napi_value code, napi_value msg, napi_value* result) { + v8impl::NodeApiBaseEnv* env = v8impl::AsNodeApiBaseEnv(env_); CHECK_ENV_NOT_IN_GC(env); CHECK_ARG(env, msg); CHECK_ARG(env, result); @@ -2043,10 +2094,11 @@ napi_status NAPI_CDECL napi_create_range_error(napi_env env, return napi_clear_last_error(env); } -napi_status NAPI_CDECL node_api_create_syntax_error(napi_env env, +napi_status NAPI_CDECL node_api_create_syntax_error(napi_env env_, napi_value code, napi_value msg, napi_value* result) { + v8impl::NodeApiBaseEnv* env = v8impl::AsNodeApiBaseEnv(env_); CHECK_ENV_NOT_IN_GC(env); CHECK_ARG(env, msg); CHECK_ARG(env, result); @@ -2063,11 +2115,12 @@ napi_status NAPI_CDECL node_api_create_syntax_error(napi_env env, return napi_clear_last_error(env); } -napi_status NAPI_CDECL napi_typeof(napi_env env, +napi_status NAPI_CDECL napi_typeof(napi_env env_, napi_value value, napi_valuetype* result) { // Omit NAPI_PREAMBLE and GET_RETURN_STATUS because V8 calls here cannot throw // JS exceptions. + v8impl::NodeApiBaseEnv* env = v8impl::AsNodeApiBaseEnv(env_); CHECK_ENV_NOT_IN_GC(env); CHECK_ARG(env, value); CHECK_ARG(env, result); @@ -2106,7 +2159,8 @@ napi_status NAPI_CDECL napi_typeof(napi_env env, return napi_clear_last_error(env); } -napi_status NAPI_CDECL napi_get_undefined(napi_env env, napi_value* result) { +napi_status NAPI_CDECL napi_get_undefined(napi_env env_, napi_value* result) { + v8impl::NodeApiBaseEnv* env = v8impl::AsNodeApiBaseEnv(env_); CHECK_ENV_NOT_IN_GC(env); CHECK_ARG(env, result); @@ -2115,7 +2169,8 @@ napi_status NAPI_CDECL napi_get_undefined(napi_env env, napi_value* result) { return napi_clear_last_error(env); } -napi_status NAPI_CDECL napi_get_null(napi_env env, napi_value* result) { +napi_status NAPI_CDECL napi_get_null(napi_env env_, napi_value* result) { + v8impl::NodeApiBaseEnv* env = v8impl::AsNodeApiBaseEnv(env_); CHECK_ENV_NOT_IN_GC(env); CHECK_ARG(env, result); @@ -2126,13 +2181,14 @@ napi_status NAPI_CDECL napi_get_null(napi_env env, napi_value* result) { // Gets all callback info in a single call. (Ugly, but faster.) napi_status NAPI_CDECL napi_get_cb_info( - napi_env env, // [in] NAPI environment handle + napi_env env_, // [in] NAPI environment handle napi_callback_info cbinfo, // [in] Opaque callback-info handle size_t* argc, // [in-out] Specifies the size of the provided argv array // and receives the actual count of args. napi_value* argv, // [out] Array of values napi_value* this_arg, // [out] Receives the JS 'this' arg for the call void** data) { // [out] Receives the data pointer for the callback. + v8impl::NodeApiBaseEnv* env = v8impl::AsNodeApiBaseEnv(env_); CHECK_ENV(env); CHECK_ARG(env, cbinfo); @@ -2156,9 +2212,10 @@ napi_status NAPI_CDECL napi_get_cb_info( return napi_clear_last_error(env); } -napi_status NAPI_CDECL napi_get_new_target(napi_env env, +napi_status NAPI_CDECL napi_get_new_target(napi_env env_, napi_callback_info cbinfo, napi_value* result) { + v8impl::NodeApiBaseEnv* env = v8impl::AsNodeApiBaseEnv(env_); CHECK_ENV_NOT_IN_GC(env); CHECK_ARG(env, cbinfo); CHECK_ARG(env, result); @@ -2170,12 +2227,13 @@ napi_status NAPI_CDECL napi_get_new_target(napi_env env, return napi_clear_last_error(env); } -napi_status NAPI_CDECL napi_call_function(napi_env env, +napi_status NAPI_CDECL napi_call_function(napi_env env_, napi_value recv, napi_value func, size_t argc, const napi_value* argv, napi_value* result) { + v8impl::NodeApiBaseEnv* env = v8impl::AsNodeApiBaseEnv(env_); NAPI_PREAMBLE(env); CHECK_ARG(env, recv); if (argc > 0) { @@ -2202,7 +2260,8 @@ napi_status NAPI_CDECL napi_call_function(napi_env env, return napi_clear_last_error(env); } -napi_status NAPI_CDECL napi_get_global(napi_env env, napi_value* result) { +napi_status NAPI_CDECL napi_get_global(napi_env env_, napi_value* result) { + v8impl::NodeApiBaseEnv* env = v8impl::AsNodeApiBaseEnv(env_); CHECK_ENV_NOT_IN_GC(env); CHECK_ARG(env, result); @@ -2211,7 +2270,8 @@ napi_status NAPI_CDECL napi_get_global(napi_env env, napi_value* result) { return napi_clear_last_error(env); } -napi_status NAPI_CDECL napi_throw(napi_env env, napi_value error) { +napi_status NAPI_CDECL napi_throw(napi_env env_, napi_value error) { + v8impl::NodeApiBaseEnv* env = v8impl::AsNodeApiBaseEnv(env_); NAPI_PREAMBLE(env); CHECK_ARG(env, error); @@ -2223,9 +2283,10 @@ napi_status NAPI_CDECL napi_throw(napi_env env, napi_value error) { return napi_clear_last_error(env); } -napi_status NAPI_CDECL napi_throw_error(napi_env env, +napi_status NAPI_CDECL napi_throw_error(napi_env env_, const char* code, const char* msg) { + v8impl::NodeApiBaseEnv* env = v8impl::AsNodeApiBaseEnv(env_); NAPI_PREAMBLE(env); v8::Isolate* isolate = env->isolate; @@ -2241,9 +2302,10 @@ napi_status NAPI_CDECL napi_throw_error(napi_env env, return napi_clear_last_error(env); } -napi_status NAPI_CDECL napi_throw_type_error(napi_env env, +napi_status NAPI_CDECL napi_throw_type_error(napi_env env_, const char* code, const char* msg) { + v8impl::NodeApiBaseEnv* env = v8impl::AsNodeApiBaseEnv(env_); NAPI_PREAMBLE(env); v8::Isolate* isolate = env->isolate; @@ -2259,9 +2321,10 @@ napi_status NAPI_CDECL napi_throw_type_error(napi_env env, return napi_clear_last_error(env); } -napi_status NAPI_CDECL napi_throw_range_error(napi_env env, +napi_status NAPI_CDECL napi_throw_range_error(napi_env env_, const char* code, const char* msg) { + v8impl::NodeApiBaseEnv* env = v8impl::AsNodeApiBaseEnv(env_); NAPI_PREAMBLE(env); v8::Isolate* isolate = env->isolate; @@ -2277,9 +2340,10 @@ napi_status NAPI_CDECL napi_throw_range_error(napi_env env, return napi_clear_last_error(env); } -napi_status NAPI_CDECL node_api_throw_syntax_error(napi_env env, +napi_status NAPI_CDECL node_api_throw_syntax_error(napi_env env_, const char* code, const char* msg) { + v8impl::NodeApiBaseEnv* env = v8impl::AsNodeApiBaseEnv(env_); NAPI_PREAMBLE(env); v8::Isolate* isolate = env->isolate; @@ -2295,11 +2359,12 @@ napi_status NAPI_CDECL node_api_throw_syntax_error(napi_env env, return napi_clear_last_error(env); } -napi_status NAPI_CDECL napi_is_error(napi_env env, +napi_status NAPI_CDECL napi_is_error(napi_env env_, napi_value value, bool* result) { // Omit NAPI_PREAMBLE and GET_RETURN_STATUS because V8 calls here cannot // throw JS exceptions. + v8impl::NodeApiBaseEnv* env = v8impl::AsNodeApiBaseEnv(env_); CHECK_ENV_NOT_IN_GC(env); CHECK_ARG(env, value); CHECK_ARG(env, result); @@ -2310,11 +2375,12 @@ napi_status NAPI_CDECL napi_is_error(napi_env env, return napi_clear_last_error(env); } -napi_status NAPI_CDECL napi_get_value_double(napi_env env, +napi_status NAPI_CDECL napi_get_value_double(napi_env env_, napi_value value, double* result) { // Omit NAPI_PREAMBLE and GET_RETURN_STATUS because V8 calls here cannot throw // JS exceptions. + v8impl::NodeApiBaseEnv* env = v8impl::AsNodeApiBaseEnv(env_); CHECK_ENV_NOT_IN_GC(env); CHECK_ARG(env, value); CHECK_ARG(env, result); @@ -2327,11 +2393,12 @@ napi_status NAPI_CDECL napi_get_value_double(napi_env env, return napi_clear_last_error(env); } -napi_status NAPI_CDECL napi_get_value_int32(napi_env env, +napi_status NAPI_CDECL napi_get_value_int32(napi_env env_, napi_value value, int32_t* result) { // Omit NAPI_PREAMBLE and GET_RETURN_STATUS because V8 calls here cannot throw // JS exceptions. + v8impl::NodeApiBaseEnv* env = v8impl::AsNodeApiBaseEnv(env_); CHECK_ENV_NOT_IN_GC(env); CHECK_ARG(env, value); CHECK_ARG(env, result); @@ -2351,11 +2418,12 @@ napi_status NAPI_CDECL napi_get_value_int32(napi_env env, return napi_clear_last_error(env); } -napi_status NAPI_CDECL napi_get_value_uint32(napi_env env, +napi_status NAPI_CDECL napi_get_value_uint32(napi_env env_, napi_value value, uint32_t* result) { // Omit NAPI_PREAMBLE and GET_RETURN_STATUS because V8 calls here cannot throw // JS exceptions. + v8impl::NodeApiBaseEnv* env = v8impl::AsNodeApiBaseEnv(env_); CHECK_ENV_NOT_IN_GC(env); CHECK_ARG(env, value); CHECK_ARG(env, result); @@ -2375,11 +2443,12 @@ napi_status NAPI_CDECL napi_get_value_uint32(napi_env env, return napi_clear_last_error(env); } -napi_status NAPI_CDECL napi_get_value_int64(napi_env env, +napi_status NAPI_CDECL napi_get_value_int64(napi_env env_, napi_value value, int64_t* result) { // Omit NAPI_PREAMBLE and GET_RETURN_STATUS because V8 calls here cannot throw // JS exceptions. + v8impl::NodeApiBaseEnv* env = v8impl::AsNodeApiBaseEnv(env_); CHECK_ENV_NOT_IN_GC(env); CHECK_ARG(env, value); CHECK_ARG(env, result); @@ -2409,10 +2478,11 @@ napi_status NAPI_CDECL napi_get_value_int64(napi_env env, return napi_clear_last_error(env); } -napi_status NAPI_CDECL napi_get_value_bigint_int64(napi_env env, +napi_status NAPI_CDECL napi_get_value_bigint_int64(napi_env env_, napi_value value, int64_t* result, bool* lossless) { + v8impl::NodeApiBaseEnv* env = v8impl::AsNodeApiBaseEnv(env_); CHECK_ENV_NOT_IN_GC(env); CHECK_ARG(env, value); CHECK_ARG(env, result); @@ -2427,10 +2497,11 @@ napi_status NAPI_CDECL napi_get_value_bigint_int64(napi_env env, return napi_clear_last_error(env); } -napi_status NAPI_CDECL napi_get_value_bigint_uint64(napi_env env, +napi_status NAPI_CDECL napi_get_value_bigint_uint64(napi_env env_, napi_value value, uint64_t* result, bool* lossless) { + v8impl::NodeApiBaseEnv* env = v8impl::AsNodeApiBaseEnv(env_); CHECK_ENV_NOT_IN_GC(env); CHECK_ARG(env, value); CHECK_ARG(env, result); @@ -2445,11 +2516,12 @@ napi_status NAPI_CDECL napi_get_value_bigint_uint64(napi_env env, return napi_clear_last_error(env); } -napi_status NAPI_CDECL napi_get_value_bigint_words(napi_env env, +napi_status NAPI_CDECL napi_get_value_bigint_words(napi_env env_, napi_value value, int* sign_bit, size_t* word_count, uint64_t* words) { + v8impl::NodeApiBaseEnv* env = v8impl::AsNodeApiBaseEnv(env_); CHECK_ENV_NOT_IN_GC(env); CHECK_ARG(env, value); CHECK_ARG(env, word_count); @@ -2475,11 +2547,12 @@ napi_status NAPI_CDECL napi_get_value_bigint_words(napi_env env, return napi_clear_last_error(env); } -napi_status NAPI_CDECL napi_get_value_bool(napi_env env, +napi_status NAPI_CDECL napi_get_value_bool(napi_env env_, napi_value value, bool* result) { // Omit NAPI_PREAMBLE and GET_RETURN_STATUS because V8 calls here cannot throw // JS exceptions. + v8impl::NodeApiBaseEnv* env = v8impl::AsNodeApiBaseEnv(env_); CHECK_ENV_NOT_IN_GC(env); CHECK_ARG(env, value); CHECK_ARG(env, result); @@ -2500,8 +2573,12 @@ napi_status NAPI_CDECL napi_get_value_bool(napi_env env, // If buf is NULL, this method returns the length of the string (in bytes) // via the result parameter. // The result argument is optional unless buf is NULL. -napi_status NAPI_CDECL napi_get_value_string_latin1( - napi_env env, napi_value value, char* buf, size_t bufsize, size_t* result) { +napi_status NAPI_CDECL napi_get_value_string_latin1(napi_env env_, + napi_value value, + char* buf, + size_t bufsize, + size_t* result) { + v8impl::NodeApiBaseEnv* env = v8impl::AsNodeApiBaseEnv(env_); CHECK_ENV_NOT_IN_GC(env); CHECK_ARG(env, value); @@ -2538,8 +2615,12 @@ napi_status NAPI_CDECL napi_get_value_string_latin1( // If buf is NULL, this method returns the length of the string (in bytes) // via the result parameter. // The result argument is optional unless buf is NULL. -napi_status NAPI_CDECL napi_get_value_string_utf8( - napi_env env, napi_value value, char* buf, size_t bufsize, size_t* result) { +napi_status NAPI_CDECL napi_get_value_string_utf8(napi_env env_, + napi_value value, + char* buf, + size_t bufsize, + size_t* result) { + v8impl::NodeApiBaseEnv* env = v8impl::AsNodeApiBaseEnv(env_); CHECK_ENV_NOT_IN_GC(env); CHECK_ARG(env, value); @@ -2576,11 +2657,12 @@ napi_status NAPI_CDECL napi_get_value_string_utf8( // If buf is NULL, this method returns the length of the string (in 2-byte // code units) via the result parameter. // The result argument is optional unless buf is NULL. -napi_status NAPI_CDECL napi_get_value_string_utf16(napi_env env, +napi_status NAPI_CDECL napi_get_value_string_utf16(napi_env env_, napi_value value, char16_t* buf, size_t bufsize, size_t* result) { + v8impl::NodeApiBaseEnv* env = v8impl::AsNodeApiBaseEnv(env_); CHECK_ENV_NOT_IN_GC(env); CHECK_ARG(env, value); @@ -2611,9 +2693,10 @@ napi_status NAPI_CDECL napi_get_value_string_utf16(napi_env env, return napi_clear_last_error(env); } -napi_status NAPI_CDECL napi_coerce_to_bool(napi_env env, +napi_status NAPI_CDECL napi_coerce_to_bool(napi_env env_, napi_value value, napi_value* result) { + v8impl::NodeApiBaseEnv* env = v8impl::AsNodeApiBaseEnv(env_); NAPI_PREAMBLE(env); CHECK_ARG(env, value); CHECK_ARG(env, result); @@ -2627,7 +2710,8 @@ napi_status NAPI_CDECL napi_coerce_to_bool(napi_env env, #define GEN_COERCE_FUNCTION(UpperCaseName, MixedCaseName, LowerCaseName) \ napi_status NAPI_CDECL napi_coerce_to_##LowerCaseName( \ - napi_env env, napi_value value, napi_value* result) { \ + napi_env env_, napi_value value, napi_value* result) { \ + v8impl::NodeApiBaseEnv* env = v8impl::AsNodeApiBaseEnv(env_); \ NAPI_PREAMBLE(env); \ CHECK_ARG(env, value); \ CHECK_ARG(env, result); \ @@ -2647,36 +2731,40 @@ GEN_COERCE_FUNCTION(STRING, String, string) #undef GEN_COERCE_FUNCTION -napi_status NAPI_CDECL napi_wrap(napi_env env, +napi_status NAPI_CDECL napi_wrap(napi_env env_, napi_value js_object, void* native_object, node_api_basic_finalize basic_finalize_cb, void* finalize_hint, napi_ref* result) { + v8impl::NodeApiBaseEnv* env = v8impl::AsNodeApiBaseEnv(env_); napi_finalize finalize_cb = reinterpret_cast(basic_finalize_cb); return v8impl::Wrap( env, js_object, native_object, finalize_cb, finalize_hint, result); } -napi_status NAPI_CDECL napi_unwrap(napi_env env, +napi_status NAPI_CDECL napi_unwrap(napi_env env_, napi_value obj, void** result) { + v8impl::NodeApiBaseEnv* env = v8impl::AsNodeApiBaseEnv(env_); return v8impl::Unwrap(env, obj, result, v8impl::KeepWrap); } -napi_status NAPI_CDECL napi_remove_wrap(napi_env env, +napi_status NAPI_CDECL napi_remove_wrap(napi_env env_, napi_value obj, void** result) { + v8impl::NodeApiBaseEnv* env = v8impl::AsNodeApiBaseEnv(env_); return v8impl::Unwrap(env, obj, result, v8impl::RemoveWrap); } napi_status NAPI_CDECL -napi_create_external(napi_env env, +napi_create_external(napi_env env_, void* data, node_api_basic_finalize basic_finalize_cb, void* finalize_hint, napi_value* result) { + v8impl::NodeApiBaseEnv* env = v8impl::AsNodeApiBaseEnv(env_); napi_finalize finalize_cb = reinterpret_cast(basic_finalize_cb); NAPI_PREAMBLE(env); @@ -2702,9 +2790,10 @@ napi_create_external(napi_env env, return napi_clear_last_error(env); } -napi_status NAPI_CDECL napi_type_tag_object(napi_env env, +napi_status NAPI_CDECL napi_type_tag_object(napi_env env_, napi_value object_or_external, const napi_type_tag* type_tag) { + v8impl::NodeApiBaseEnv* env = v8impl::AsNodeApiBaseEnv(env_); NAPI_PREAMBLE(env); v8::Local context = env->context(); @@ -2741,10 +2830,11 @@ napi_status NAPI_CDECL napi_type_tag_object(napi_env env, return GET_RETURN_STATUS(env); } -napi_status NAPI_CDECL napi_check_object_type_tag(napi_env env, +napi_status NAPI_CDECL napi_check_object_type_tag(napi_env env_, napi_value object_or_external, const napi_type_tag* type_tag, bool* result) { + v8impl::NodeApiBaseEnv* env = v8impl::AsNodeApiBaseEnv(env_); NAPI_PREAMBLE(env); v8::Local context = env->context(); @@ -2793,9 +2883,10 @@ napi_status NAPI_CDECL napi_check_object_type_tag(napi_env env, return GET_RETURN_STATUS(env); } -napi_status NAPI_CDECL napi_get_value_external(napi_env env, +napi_status NAPI_CDECL napi_get_value_external(napi_env env_, napi_value value, void** result) { + v8impl::NodeApiBaseEnv* env = v8impl::AsNodeApiBaseEnv(env_); CHECK_ENV_NOT_IN_GC(env); CHECK_ARG(env, value); CHECK_ARG(env, result); @@ -2810,12 +2901,13 @@ napi_status NAPI_CDECL napi_get_value_external(napi_env env, } // Set initial_refcount to 0 for a weak reference, >0 for a strong reference. -napi_status NAPI_CDECL napi_create_reference(napi_env env, +napi_status NAPI_CDECL napi_create_reference(napi_env env_, napi_value value, uint32_t initial_refcount, napi_ref* result) { // Omit NAPI_PREAMBLE and GET_RETURN_STATUS because V8 calls here cannot throw // JS exceptions. + v8impl::NodeApiBaseEnv* env = v8impl::AsNodeApiBaseEnv(env_); CHECK_ENV_NOT_IN_GC(env); CHECK_ARG(env, value); CHECK_ARG(env, result); @@ -2839,10 +2931,11 @@ napi_status NAPI_CDECL napi_create_reference(napi_env env, // there are other references to it. // For a napi_reference returned from `napi_wrap`, this must be called in the // finalizer. -napi_status NAPI_CDECL napi_delete_reference(node_api_basic_env env, +napi_status NAPI_CDECL napi_delete_reference(node_api_basic_env basic_env, napi_ref ref) { // Omit NAPI_PREAMBLE and GET_RETURN_STATUS because V8 calls here cannot throw // JS exceptions. + v8impl::NodeApiBaseEnv* env = v8impl::AsNodeApiBaseEnv(basic_env); CHECK_ENV(env); CHECK_ARG(env, ref); @@ -2856,11 +2949,12 @@ napi_status NAPI_CDECL napi_delete_reference(node_api_basic_env env, // refcount is >0, and the referenced object is effectively "pinned". // Calling this when the refcount is 0 and the object is unavailable // results in an error. -napi_status NAPI_CDECL napi_reference_ref(napi_env env, +napi_status NAPI_CDECL napi_reference_ref(napi_env env_, napi_ref ref, uint32_t* result) { // Omit NAPI_PREAMBLE and GET_RETURN_STATUS because V8 calls here cannot throw // JS exceptions. + v8impl::NodeApiBaseEnv* env = v8impl::AsNodeApiBaseEnv(env_); CHECK_ENV_NOT_IN_GC(env); CHECK_ARG(env, ref); @@ -2878,11 +2972,12 @@ napi_status NAPI_CDECL napi_reference_ref(napi_env env, // the result is 0 the reference is now weak and the object may be GC'd at any // time if there are no other references. Calling this when the refcount is // already 0 results in an error. -napi_status NAPI_CDECL napi_reference_unref(napi_env env, +napi_status NAPI_CDECL napi_reference_unref(napi_env env_, napi_ref ref, uint32_t* result) { // Omit NAPI_PREAMBLE and GET_RETURN_STATUS because V8 calls here cannot throw // JS exceptions. + v8impl::NodeApiBaseEnv* env = v8impl::AsNodeApiBaseEnv(env_); CHECK_ENV_NOT_IN_GC(env); CHECK_ARG(env, ref); @@ -2904,11 +2999,12 @@ napi_status NAPI_CDECL napi_reference_unref(napi_env env, // Attempts to get a referenced value. If the reference is weak, the value might // no longer be available, in that case the call is still successful but the // result is NULL. -napi_status NAPI_CDECL napi_get_reference_value(napi_env env, +napi_status NAPI_CDECL napi_get_reference_value(napi_env env_, napi_ref ref, napi_value* result) { // Omit NAPI_PREAMBLE and GET_RETURN_STATUS because V8 calls here cannot throw // JS exceptions. + v8impl::NodeApiBaseEnv* env = v8impl::AsNodeApiBaseEnv(env_); CHECK_ENV_NOT_IN_GC(env); CHECK_ARG(env, ref); CHECK_ARG(env, result); @@ -2919,10 +3015,11 @@ napi_status NAPI_CDECL napi_get_reference_value(napi_env env, return napi_clear_last_error(env); } -napi_status NAPI_CDECL napi_open_handle_scope(napi_env env, +napi_status NAPI_CDECL napi_open_handle_scope(napi_env env_, napi_handle_scope* result) { // Omit NAPI_PREAMBLE and GET_RETURN_STATUS because V8 calls here cannot throw // JS exceptions. + v8impl::NodeApiBaseEnv* env = v8impl::AsNodeApiBaseEnv(env_); CHECK_ENV_NOT_IN_GC(env); CHECK_ARG(env, result); @@ -2932,10 +3029,11 @@ napi_status NAPI_CDECL napi_open_handle_scope(napi_env env, return napi_clear_last_error(env); } -napi_status NAPI_CDECL napi_close_handle_scope(napi_env env, +napi_status NAPI_CDECL napi_close_handle_scope(napi_env env_, napi_handle_scope scope) { // Omit NAPI_PREAMBLE and GET_RETURN_STATUS because V8 calls here cannot throw // JS exceptions. + v8impl::NodeApiBaseEnv* env = v8impl::AsNodeApiBaseEnv(env_); CHECK_ENV_NOT_IN_GC(env); CHECK_ARG(env, scope); if (env->open_handle_scopes == 0) { @@ -2948,9 +3046,10 @@ napi_status NAPI_CDECL napi_close_handle_scope(napi_env env, } napi_status NAPI_CDECL napi_open_escapable_handle_scope( - napi_env env, napi_escapable_handle_scope* result) { + napi_env env_, napi_escapable_handle_scope* result) { // Omit NAPI_PREAMBLE and GET_RETURN_STATUS because V8 calls here cannot throw // JS exceptions. + v8impl::NodeApiBaseEnv* env = v8impl::AsNodeApiBaseEnv(env_); CHECK_ENV_NOT_IN_GC(env); CHECK_ARG(env, result); @@ -2961,9 +3060,10 @@ napi_status NAPI_CDECL napi_open_escapable_handle_scope( } napi_status NAPI_CDECL napi_close_escapable_handle_scope( - napi_env env, napi_escapable_handle_scope scope) { + napi_env env_, napi_escapable_handle_scope scope) { // Omit NAPI_PREAMBLE and GET_RETURN_STATUS because V8 calls here cannot throw // JS exceptions. + v8impl::NodeApiBaseEnv* env = v8impl::AsNodeApiBaseEnv(env_); CHECK_ENV_NOT_IN_GC(env); CHECK_ARG(env, scope); if (env->open_handle_scopes == 0) { @@ -2975,12 +3075,13 @@ napi_status NAPI_CDECL napi_close_escapable_handle_scope( return napi_clear_last_error(env); } -napi_status NAPI_CDECL napi_escape_handle(napi_env env, +napi_status NAPI_CDECL napi_escape_handle(napi_env env_, napi_escapable_handle_scope scope, napi_value escapee, napi_value* result) { // Omit NAPI_PREAMBLE and GET_RETURN_STATUS because V8 calls here cannot throw // JS exceptions. + v8impl::NodeApiBaseEnv* env = v8impl::AsNodeApiBaseEnv(env_); CHECK_ENV_NOT_IN_GC(env); CHECK_ARG(env, scope); CHECK_ARG(env, escapee); @@ -2996,11 +3097,12 @@ napi_status NAPI_CDECL napi_escape_handle(napi_env env, return napi_set_last_error(env, napi_escape_called_twice); } -napi_status NAPI_CDECL napi_new_instance(napi_env env, +napi_status NAPI_CDECL napi_new_instance(napi_env env_, napi_value constructor, size_t argc, const napi_value* argv, napi_value* result) { + v8impl::NodeApiBaseEnv* env = v8impl::AsNodeApiBaseEnv(env_); NAPI_PREAMBLE(env); CHECK_ARG(env, constructor); if (argc > 0) { @@ -3024,10 +3126,11 @@ napi_status NAPI_CDECL napi_new_instance(napi_env env, return GET_RETURN_STATUS(env); } -napi_status NAPI_CDECL napi_instanceof(napi_env env, +napi_status NAPI_CDECL napi_instanceof(napi_env env_, napi_value object, napi_value constructor, bool* result) { + v8impl::NodeApiBaseEnv* env = v8impl::AsNodeApiBaseEnv(env_); NAPI_PREAMBLE(env); CHECK_ARG(env, object); CHECK_ARG(env, result); @@ -3056,9 +3159,10 @@ napi_status NAPI_CDECL napi_instanceof(napi_env env, } // Methods to support catching exceptions -napi_status NAPI_CDECL napi_is_exception_pending(napi_env env, bool* result) { +napi_status NAPI_CDECL napi_is_exception_pending(napi_env env_, bool* result) { // NAPI_PREAMBLE is not used here: this function must execute when there is a // pending exception. + v8impl::NodeApiBaseEnv* env = v8impl::AsNodeApiBaseEnv(env_); CHECK_ENV_NOT_IN_GC(env); CHECK_ARG(env, result); @@ -3066,10 +3170,11 @@ napi_status NAPI_CDECL napi_is_exception_pending(napi_env env, bool* result) { return napi_clear_last_error(env); } -napi_status NAPI_CDECL napi_get_and_clear_last_exception(napi_env env, +napi_status NAPI_CDECL napi_get_and_clear_last_exception(napi_env env_, napi_value* result) { // NAPI_PREAMBLE is not used here: this function must execute when there is a // pending exception. + v8impl::NodeApiBaseEnv* env = v8impl::AsNodeApiBaseEnv(env_); CHECK_ENV_NOT_IN_GC(env); CHECK_ARG(env, result); @@ -3084,9 +3189,10 @@ napi_status NAPI_CDECL napi_get_and_clear_last_exception(napi_env env, return napi_clear_last_error(env); } -napi_status NAPI_CDECL napi_is_arraybuffer(napi_env env, +napi_status NAPI_CDECL napi_is_arraybuffer(napi_env env_, napi_value value, bool* result) { + v8impl::NodeApiBaseEnv* env = v8impl::AsNodeApiBaseEnv(env_); CHECK_ENV_NOT_IN_GC(env); CHECK_ARG(env, value); CHECK_ARG(env, result); @@ -3097,10 +3203,11 @@ napi_status NAPI_CDECL napi_is_arraybuffer(napi_env env, return napi_clear_last_error(env); } -napi_status NAPI_CDECL napi_create_arraybuffer(napi_env env, +napi_status NAPI_CDECL napi_create_arraybuffer(napi_env env_, size_t byte_length, void** data, napi_value* result) { + v8impl::NodeApiBaseEnv* env = v8impl::AsNodeApiBaseEnv(env_); NAPI_PREAMBLE(env); CHECK_ARG(env, result); @@ -3119,7 +3226,7 @@ napi_status NAPI_CDECL napi_create_arraybuffer(napi_env env, } napi_status NAPI_CDECL -napi_create_external_arraybuffer(napi_env env, +napi_create_external_arraybuffer(napi_env env_, void* external_data, size_t byte_length, node_api_basic_finalize finalize_cb, @@ -3130,15 +3237,16 @@ napi_create_external_arraybuffer(napi_env env, // `Buffer` variant for easier implementation. napi_value buffer; STATUS_CALL(napi_create_external_buffer( - env, byte_length, external_data, finalize_cb, finalize_hint, &buffer)); + env_, byte_length, external_data, finalize_cb, finalize_hint, &buffer)); return napi_get_typedarray_info( - env, buffer, nullptr, nullptr, nullptr, result, nullptr); + env_, buffer, nullptr, nullptr, nullptr, result, nullptr); } -napi_status NAPI_CDECL napi_get_arraybuffer_info(napi_env env, +napi_status NAPI_CDECL napi_get_arraybuffer_info(napi_env env_, napi_value arraybuffer, void** data, size_t* byte_length) { + v8impl::NodeApiBaseEnv* env = v8impl::AsNodeApiBaseEnv(env_); CHECK_ENV_NOT_IN_GC(env); CHECK_ARG(env, arraybuffer); @@ -3171,9 +3279,10 @@ napi_status NAPI_CDECL napi_get_arraybuffer_info(napi_env env, return napi_clear_last_error(env); } -napi_status NAPI_CDECL node_api_is_sharedarraybuffer(napi_env env, +napi_status NAPI_CDECL node_api_is_sharedarraybuffer(napi_env env_, napi_value value, bool* result) { + v8impl::NodeApiBaseEnv* env = v8impl::AsNodeApiBaseEnv(env_); CHECK_ENV_NOT_IN_GC(env); CHECK_ARG(env, value); CHECK_ARG(env, result); @@ -3184,10 +3293,11 @@ napi_status NAPI_CDECL node_api_is_sharedarraybuffer(napi_env env, return napi_clear_last_error(env); } -napi_status NAPI_CDECL node_api_create_sharedarraybuffer(napi_env env, +napi_status NAPI_CDECL node_api_create_sharedarraybuffer(napi_env env_, size_t byte_length, void** data, napi_value* result) { + v8impl::NodeApiBaseEnv* env = v8impl::AsNodeApiBaseEnv(env_); NAPI_PREAMBLE(env); CHECK_ARG(env, result); @@ -3205,9 +3315,10 @@ napi_status NAPI_CDECL node_api_create_sharedarraybuffer(napi_env env, return GET_RETURN_STATUS(env); } -napi_status NAPI_CDECL napi_is_typedarray(napi_env env, +napi_status NAPI_CDECL napi_is_typedarray(napi_env env_, napi_value value, bool* result) { + v8impl::NodeApiBaseEnv* env = v8impl::AsNodeApiBaseEnv(env_); CHECK_ENV_NOT_IN_GC(env); CHECK_ARG(env, value); CHECK_ARG(env, result); @@ -3218,12 +3329,13 @@ napi_status NAPI_CDECL napi_is_typedarray(napi_env env, return napi_clear_last_error(env); } -napi_status NAPI_CDECL napi_create_typedarray(napi_env env, +napi_status NAPI_CDECL napi_create_typedarray(napi_env env_, napi_typedarray_type type, size_t length, napi_value arraybuffer, size_t byte_offset, napi_value* result) { + v8impl::NodeApiBaseEnv* env = v8impl::AsNodeApiBaseEnv(env_); NAPI_PREAMBLE(env); CHECK_ARG(env, arraybuffer); CHECK_ARG(env, result); @@ -3291,13 +3403,14 @@ napi_status NAPI_CDECL napi_create_typedarray(napi_env env, return GET_RETURN_STATUS(env); } -napi_status NAPI_CDECL napi_get_typedarray_info(napi_env env, +napi_status NAPI_CDECL napi_get_typedarray_info(napi_env env_, napi_value typedarray, napi_typedarray_type* type, size_t* length, void** data, napi_value* arraybuffer, size_t* byte_offset) { + v8impl::NodeApiBaseEnv* env = v8impl::AsNodeApiBaseEnv(env_); CHECK_ENV_NOT_IN_GC(env); CHECK_ARG(env, typedarray); @@ -3360,11 +3473,12 @@ napi_status NAPI_CDECL napi_get_typedarray_info(napi_env env, return napi_clear_last_error(env); } -napi_status NAPI_CDECL napi_create_dataview(napi_env env, +napi_status NAPI_CDECL napi_create_dataview(napi_env env_, size_t byte_length, napi_value arraybuffer, size_t byte_offset, napi_value* result) { + v8impl::NodeApiBaseEnv* env = v8impl::AsNodeApiBaseEnv(env_); NAPI_PREAMBLE(env); CHECK_ARG(env, arraybuffer); CHECK_ARG(env, result); @@ -3396,9 +3510,10 @@ napi_status NAPI_CDECL napi_create_dataview(napi_env env, } } -napi_status NAPI_CDECL napi_is_dataview(napi_env env, +napi_status NAPI_CDECL napi_is_dataview(napi_env env_, napi_value value, bool* result) { + v8impl::NodeApiBaseEnv* env = v8impl::AsNodeApiBaseEnv(env_); CHECK_ENV_NOT_IN_GC(env); CHECK_ARG(env, value); CHECK_ARG(env, result); @@ -3409,12 +3524,13 @@ napi_status NAPI_CDECL napi_is_dataview(napi_env env, return napi_clear_last_error(env); } -napi_status NAPI_CDECL napi_get_dataview_info(napi_env env, +napi_status NAPI_CDECL napi_get_dataview_info(napi_env env_, napi_value dataview, size_t* byte_length, void** data, napi_value* arraybuffer, size_t* byte_offset) { + v8impl::NodeApiBaseEnv* env = v8impl::AsNodeApiBaseEnv(env_); CHECK_ENV_NOT_IN_GC(env); CHECK_ARG(env, dataview); @@ -3449,17 +3565,19 @@ napi_status NAPI_CDECL napi_get_dataview_info(napi_env env, return napi_clear_last_error(env); } -napi_status NAPI_CDECL napi_get_version(node_api_basic_env env, +napi_status NAPI_CDECL napi_get_version(node_api_basic_env basic_env, uint32_t* result) { + v8impl::NodeApiBaseEnv* env = v8impl::AsNodeApiBaseEnv(basic_env); CHECK_ENV(env); CHECK_ARG(env, result); *result = NODE_API_SUPPORTED_VERSION_MAX; return napi_clear_last_error(env); } -napi_status NAPI_CDECL napi_create_promise(napi_env env, +napi_status NAPI_CDECL napi_create_promise(napi_env env_, napi_deferred* deferred, napi_value* promise) { + v8impl::NodeApiBaseEnv* env = v8impl::AsNodeApiBaseEnv(env_); NAPI_PREAMBLE(env); CHECK_ARG(env, deferred); CHECK_ARG(env, promise); @@ -3476,21 +3594,24 @@ napi_status NAPI_CDECL napi_create_promise(napi_env env, return GET_RETURN_STATUS(env); } -napi_status NAPI_CDECL napi_resolve_deferred(napi_env env, +napi_status NAPI_CDECL napi_resolve_deferred(napi_env env_, napi_deferred deferred, napi_value resolution) { + v8impl::NodeApiBaseEnv* env = v8impl::AsNodeApiBaseEnv(env_); return v8impl::ConcludeDeferred(env, deferred, resolution, true); } -napi_status NAPI_CDECL napi_reject_deferred(napi_env env, +napi_status NAPI_CDECL napi_reject_deferred(napi_env env_, napi_deferred deferred, napi_value resolution) { + v8impl::NodeApiBaseEnv* env = v8impl::AsNodeApiBaseEnv(env_); return v8impl::ConcludeDeferred(env, deferred, resolution, false); } -napi_status NAPI_CDECL napi_is_promise(napi_env env, +napi_status NAPI_CDECL napi_is_promise(napi_env env_, napi_value value, bool* is_promise) { + v8impl::NodeApiBaseEnv* env = v8impl::AsNodeApiBaseEnv(env_); CHECK_ENV_NOT_IN_GC(env); CHECK_ARG(env, value); CHECK_ARG(env, is_promise); @@ -3500,9 +3621,10 @@ napi_status NAPI_CDECL napi_is_promise(napi_env env, return napi_clear_last_error(env); } -napi_status NAPI_CDECL napi_create_date(napi_env env, +napi_status NAPI_CDECL napi_create_date(napi_env env_, double time, napi_value* result) { + v8impl::NodeApiBaseEnv* env = v8impl::AsNodeApiBaseEnv(env_); NAPI_PREAMBLE(env); CHECK_ARG(env, result); @@ -3514,9 +3636,10 @@ napi_status NAPI_CDECL napi_create_date(napi_env env, return GET_RETURN_STATUS(env); } -napi_status NAPI_CDECL napi_is_date(napi_env env, +napi_status NAPI_CDECL napi_is_date(napi_env env_, napi_value value, bool* is_date) { + v8impl::NodeApiBaseEnv* env = v8impl::AsNodeApiBaseEnv(env_); CHECK_ENV_NOT_IN_GC(env); CHECK_ARG(env, value); CHECK_ARG(env, is_date); @@ -3526,9 +3649,10 @@ napi_status NAPI_CDECL napi_is_date(napi_env env, return napi_clear_last_error(env); } -napi_status NAPI_CDECL napi_get_date_value(napi_env env, +napi_status NAPI_CDECL napi_get_date_value(napi_env env_, napi_value value, double* result) { + v8impl::NodeApiBaseEnv* env = v8impl::AsNodeApiBaseEnv(env_); NAPI_PREAMBLE(env); CHECK_ARG(env, value); CHECK_ARG(env, result); @@ -3542,9 +3666,10 @@ napi_status NAPI_CDECL napi_get_date_value(napi_env env, return GET_RETURN_STATUS(env); } -napi_status NAPI_CDECL napi_run_script(napi_env env, +napi_status NAPI_CDECL napi_run_script(napi_env env_, napi_value script, napi_value* result) { + v8impl::NodeApiBaseEnv* env = v8impl::AsNodeApiBaseEnv(env_); NAPI_PREAMBLE(env); CHECK_ARG(env, script); CHECK_ARG(env, result); @@ -3568,7 +3693,7 @@ napi_status NAPI_CDECL napi_run_script(napi_env env, } napi_status NAPI_CDECL -napi_add_finalizer(napi_env env, +napi_add_finalizer(napi_env env_, napi_value js_object, void* finalize_data, node_api_basic_finalize basic_finalize_cb, @@ -3576,6 +3701,7 @@ napi_add_finalizer(napi_env env, napi_ref* result) { // Omit NAPI_PREAMBLE and GET_RETURN_STATUS because V8 calls here cannot throw // JS exceptions. + v8impl::NodeApiBaseEnv* env = v8impl::AsNodeApiBaseEnv(env_); napi_finalize finalize_cb = reinterpret_cast(basic_finalize_cb); CHECK_ENV_NOT_IN_GC(env); @@ -3605,7 +3731,7 @@ napi_status NAPI_CDECL node_api_post_finalizer(node_api_basic_env basic_env, napi_finalize finalize_cb, void* finalize_data, void* finalize_hint) { - napi_env env = const_cast(basic_env); + v8impl::NodeApiBaseEnv* env = v8impl::AsNodeApiBaseEnv(basic_env); CHECK_ENV(env); env->EnqueueFinalizer(v8impl::TrackedFinalizer::New( env, finalize_cb, finalize_data, finalize_hint)); @@ -3614,9 +3740,10 @@ napi_status NAPI_CDECL node_api_post_finalizer(node_api_basic_env basic_env, #endif -napi_status NAPI_CDECL napi_adjust_external_memory(node_api_basic_env env, +napi_status NAPI_CDECL napi_adjust_external_memory(node_api_basic_env basic_env, int64_t change_in_bytes, int64_t* adjusted_value) { + v8impl::NodeApiBaseEnv* env = v8impl::AsNodeApiBaseEnv(basic_env); CHECK_ENV(env); CHECK_ARG(env, adjusted_value); @@ -3630,7 +3757,7 @@ napi_status NAPI_CDECL napi_set_instance_data(node_api_basic_env basic_env, void* data, napi_finalize finalize_cb, void* finalize_hint) { - napi_env env = const_cast(basic_env); + v8impl::NodeApiBaseEnv* env = v8impl::AsNodeApiBaseEnv(basic_env); CHECK_ENV(env); v8impl::TrackedFinalizer* old_data = @@ -3647,8 +3774,9 @@ napi_status NAPI_CDECL napi_set_instance_data(node_api_basic_env basic_env, return napi_clear_last_error(env); } -napi_status NAPI_CDECL napi_get_instance_data(node_api_basic_env env, +napi_status NAPI_CDECL napi_get_instance_data(node_api_basic_env basic_env, void** data) { + v8impl::NodeApiBaseEnv* env = v8impl::AsNodeApiBaseEnv(basic_env); CHECK_ENV(env); CHECK_ARG(env, data); @@ -3660,8 +3788,9 @@ napi_status NAPI_CDECL napi_get_instance_data(node_api_basic_env env, return napi_clear_last_error(env); } -napi_status NAPI_CDECL napi_detach_arraybuffer(napi_env env, +napi_status NAPI_CDECL napi_detach_arraybuffer(napi_env env_, napi_value arraybuffer) { + v8impl::NodeApiBaseEnv* env = v8impl::AsNodeApiBaseEnv(env_); CHECK_ENV_NOT_IN_GC(env); CHECK_ARG(env, arraybuffer); @@ -3678,9 +3807,10 @@ napi_status NAPI_CDECL napi_detach_arraybuffer(napi_env env, return napi_clear_last_error(env); } -napi_status NAPI_CDECL napi_is_detached_arraybuffer(napi_env env, +napi_status NAPI_CDECL napi_is_detached_arraybuffer(napi_env env_, napi_value arraybuffer, bool* result) { + v8impl::NodeApiBaseEnv* env = v8impl::AsNodeApiBaseEnv(env_); CHECK_ENV_NOT_IN_GC(env); CHECK_ARG(env, arraybuffer); CHECK_ARG(env, result); @@ -3692,3 +3822,142 @@ napi_status NAPI_CDECL napi_is_detached_arraybuffer(napi_env env, return napi_clear_last_error(env); } + +namespace v8impl { + +static const node_api_js_vtable g_js_vtable = { + napi_get_last_error_info, + napi_get_undefined, + napi_get_null, + napi_get_global, + napi_get_boolean, + napi_create_object, + napi_create_array, + napi_create_array_with_length, + napi_create_double, + napi_create_int32, + napi_create_uint32, + napi_create_int64, + napi_create_string_latin1, + napi_create_string_utf8, + napi_create_string_utf16, + napi_create_symbol, + napi_create_function, + napi_create_error, + napi_create_type_error, + napi_create_range_error, + napi_typeof, + napi_get_value_double, + napi_get_value_int32, + napi_get_value_uint32, + napi_get_value_int64, + napi_get_value_bool, + napi_get_value_string_latin1, + napi_get_value_string_utf8, + napi_get_value_string_utf16, + napi_coerce_to_bool, + napi_coerce_to_number, + napi_coerce_to_object, + napi_coerce_to_string, + napi_get_prototype, + napi_get_property_names, + napi_set_property, + napi_has_property, + napi_get_property, + napi_delete_property, + napi_has_own_property, + napi_set_named_property, + napi_has_named_property, + napi_get_named_property, + napi_set_element, + napi_has_element, + napi_get_element, + napi_delete_element, + napi_define_properties, + napi_is_array, + napi_get_array_length, + napi_strict_equals, + napi_call_function, + napi_new_instance, + napi_instanceof, + napi_get_cb_info, + napi_get_new_target, + napi_define_class, + napi_wrap, + napi_unwrap, + napi_remove_wrap, + napi_create_external, + napi_get_value_external, + napi_create_reference, + napi_delete_reference, + napi_reference_ref, + napi_reference_unref, + napi_get_reference_value, + napi_open_handle_scope, + napi_close_handle_scope, + napi_open_escapable_handle_scope, + napi_close_escapable_handle_scope, + napi_escape_handle, + napi_throw, + napi_throw_error, + napi_throw_type_error, + napi_throw_range_error, + napi_is_error, + napi_is_exception_pending, + napi_get_and_clear_last_exception, + napi_is_arraybuffer, + napi_create_arraybuffer, + napi_create_external_arraybuffer, + napi_get_arraybuffer_info, + napi_is_typedarray, + napi_create_typedarray, + napi_get_typedarray_info, + napi_create_dataview, + napi_is_dataview, + napi_get_dataview_info, + napi_get_version, + napi_create_promise, + napi_resolve_deferred, + napi_reject_deferred, + napi_is_promise, + napi_run_script, + napi_adjust_external_memory, + napi_create_date, + napi_is_date, + napi_get_date_value, + napi_add_finalizer, + napi_create_bigint_int64, + napi_create_bigint_uint64, + napi_create_bigint_words, + napi_get_value_bigint_int64, + napi_get_value_bigint_uint64, + napi_get_value_bigint_words, + napi_get_all_property_names, + napi_set_instance_data, + napi_get_instance_data, + napi_detach_arraybuffer, + napi_is_detached_arraybuffer, + napi_type_tag_object, + napi_check_object_type_tag, + napi_object_freeze, + napi_object_seal, + node_api_symbol_for, + node_api_create_syntax_error, + node_api_throw_syntax_error, + node_api_create_external_string_latin1, + node_api_create_external_string_utf16, + node_api_create_property_key_latin1, + node_api_create_property_key_utf8, + node_api_create_property_key_utf16, + node_api_post_finalizer, + napi_create_object_with_properties, + node_api_is_sharedarraybuffer, + node_api_create_sharedarraybuffer, + node_api_set_prototype, +}; + +const node_api_js_vtable* GetNodeApiJsVTable() { + return &g_js_vtable; +} + +} // namespace v8impl diff --git a/src/js_native_api_v8.h b/src/js_native_api_v8.h index 262916c09b5b78..0db3b46703bcc0 100644 --- a/src/js_native_api_v8.h +++ b/src/js_native_api_v8.h @@ -4,7 +4,7 @@ #include "js_native_api_types.h" #include "js_native_api_v8_internals.h" -inline napi_status napi_clear_last_error(node_api_basic_env env); +extern napi_status napi_clear_last_error(node_api_basic_env basic_env); namespace v8impl { @@ -48,12 +48,13 @@ class RefTracker { RefList* prev_ = nullptr; }; -} // end of namespace v8impl - -struct napi_env__ { - explicit napi_env__(v8::Local context, - int32_t module_api_version) - : isolate(v8::Isolate::GetCurrent()), +struct NodeApiBaseEnv : public napi_env__ { + explicit NodeApiBaseEnv(v8::Local context, + int32_t module_api_version) + : napi_env__{NODE_API_VT_SENTINEL, + GetNodeApiJsVTable(), + GetNodeApiModuleVTable()}, + isolate(v8::Isolate::GetCurrent()), context_persistent(isolate, context), module_api_version(module_api_version) { napi_clear_last_error(this); @@ -70,7 +71,8 @@ struct napi_env__ { virtual bool can_call_into_js() const { return true; } - static inline void HandleThrow(napi_env env, v8::Local value) { + static inline void HandleThrow(NodeApiBaseEnv* env, + v8::Local value) { if (env->terminatedOrTerminating()) { return; } @@ -104,7 +106,7 @@ struct napi_env__ { // Invoke finalizer from V8 garbage collector. void InvokeFinalizerFromGC(v8impl::RefTracker* finalizer); - // Enqueue the finalizer to the napi_env's own queue of the second pass + // Enqueue the finalizer to the NodeApiBaseEnv's own queue of the second pass // weak callback. // Implementation should drain the queue at the time it is safe to call // into JavaScript. @@ -149,7 +151,7 @@ struct napi_env__ { // We store references in two different lists, depending on whether they have // `napi_finalizer` callbacks, because we must first finalize the ones that - // have such a callback. See `~napi_env__()` above for details. + // have such a callback. See `~NodeApiBaseEnv()` above for details. v8impl::RefTracker::RefList reflist; v8impl::RefTracker::RefList finalizing_reflist; // The invocation order of the finalizers is not determined. @@ -163,13 +165,23 @@ struct napi_env__ { bool in_gc_finalizer = false; protected: - // Should not be deleted directly. Delete with `napi_env__::DeleteMe()` + // Should not be deleted directly. Delete with `NodeApiBaseEnv::DeleteMe()` // instead. - virtual ~napi_env__() = default; + virtual ~NodeApiBaseEnv() = default; }; +inline NodeApiBaseEnv* AsNodeApiBaseEnv(napi_env env) { + return static_cast(env); +} + +inline NodeApiBaseEnv* AsNodeApiBaseEnv(node_api_basic_env env) { + return static_cast(const_cast(env)); +} + +} // end of namespace v8impl + inline napi_status napi_clear_last_error(node_api_basic_env basic_env) { - napi_env env = const_cast(basic_env); + v8impl::NodeApiBaseEnv* env = v8impl::AsNodeApiBaseEnv(basic_env); env->last_error.error_code = napi_ok; env->last_error.engine_error_code = 0; env->last_error.engine_reserved = nullptr; @@ -181,7 +193,7 @@ inline napi_status napi_set_last_error(node_api_basic_env basic_env, napi_status error_code, uint32_t engine_error_code = 0, void* engine_reserved = nullptr) { - napi_env env = const_cast(basic_env); + v8impl::NodeApiBaseEnv* env = v8impl::AsNodeApiBaseEnv(basic_env); env->last_error.error_code = error_code; env->last_error.engine_error_code = engine_error_code; env->last_error.engine_reserved = engine_reserved; @@ -320,7 +332,7 @@ inline v8::Local V8LocalValueFromJsValue(napi_value v) { // Adapter for napi_finalize callbacks. class Finalizer { public: - Finalizer(napi_env env, + Finalizer(NodeApiBaseEnv* env, napi_finalize finalize_callback, void* finalize_data, void* finalize_hint) @@ -329,7 +341,7 @@ class Finalizer { finalize_data_(finalize_data), finalize_hint_(finalize_hint) {} - napi_env env() { return env_; } + NodeApiBaseEnv* env() { return env_; } void* data() { return finalize_data_; } void ResetEnv(); @@ -337,7 +349,7 @@ class Finalizer { void CallFinalizer(); private: - napi_env env_; + NodeApiBaseEnv* env_; napi_finalize finalize_callback_; void* finalize_data_; void* finalize_hint_; @@ -345,7 +357,8 @@ class Finalizer { class TryCatch : public v8::TryCatch { public: - explicit TryCatch(napi_env env) : v8::TryCatch(env->isolate), _env(env) {} + explicit TryCatch(NodeApiBaseEnv* env) + : v8::TryCatch(env->isolate), _env(env) {} ~TryCatch() { if (HasCaught()) { @@ -354,13 +367,13 @@ class TryCatch : public v8::TryCatch { } private: - napi_env _env; + NodeApiBaseEnv* _env; }; // Wrapper around Finalizer that can be tracked. class TrackedFinalizer final : public RefTracker { public: - static TrackedFinalizer* New(napi_env env, + static TrackedFinalizer* New(NodeApiBaseEnv* env, napi_finalize finalize_callback, void* finalize_data, void* finalize_hint); @@ -369,7 +382,7 @@ class TrackedFinalizer final : public RefTracker { void* data() { return finalizer_.data(); } private: - TrackedFinalizer(napi_env env, + TrackedFinalizer(NodeApiBaseEnv* env, napi_finalize finalize_callback, void* finalize_data, void* finalize_hint); @@ -392,7 +405,7 @@ enum class ReferenceOwnership : uint8_t { // Wrapper around v8impl::Persistent. class Reference : public RefTracker { public: - static Reference* New(napi_env env, + static Reference* New(NodeApiBaseEnv* env, v8::Local value, uint32_t initial_refcount, ReferenceOwnership ownership); @@ -400,7 +413,7 @@ class Reference : public RefTracker { uint32_t Ref(); uint32_t Unref(); - v8::Local Get(napi_env env); + v8::Local Get(NodeApiBaseEnv* env); virtual void ResetFinalizer() {} virtual void* Data() { return nullptr; } @@ -409,7 +422,7 @@ class Reference : public RefTracker { ReferenceOwnership ownership() { return ownership_; } protected: - Reference(napi_env env, + Reference(NodeApiBaseEnv* env, v8::Local value, uint32_t initial_refcount, ReferenceOwnership ownership); @@ -431,7 +444,7 @@ class Reference : public RefTracker { // Reference that can store additional data. class ReferenceWithData final : public Reference { public: - static ReferenceWithData* New(napi_env env, + static ReferenceWithData* New(NodeApiBaseEnv* env, v8::Local value, uint32_t initial_refcount, ReferenceOwnership ownership, @@ -440,7 +453,7 @@ class ReferenceWithData final : public Reference { void* Data() override { return data_; } private: - ReferenceWithData(napi_env env, + ReferenceWithData(NodeApiBaseEnv* env, v8::Local value, uint32_t initial_refcount, ReferenceOwnership ownership, @@ -453,7 +466,7 @@ class ReferenceWithData final : public Reference { // Reference that has a user finalizer callback. class ReferenceWithFinalizer final : public Reference { public: - static ReferenceWithFinalizer* New(napi_env env, + static ReferenceWithFinalizer* New(NodeApiBaseEnv* env, v8::Local value, uint32_t initial_refcount, ReferenceOwnership ownership, @@ -466,7 +479,7 @@ class ReferenceWithFinalizer final : public Reference { void* Data() override { return finalizer_.data(); } private: - ReferenceWithFinalizer(napi_env env, + ReferenceWithFinalizer(NodeApiBaseEnv* env, v8::Local value, uint32_t initial_refcount, ReferenceOwnership ownership, diff --git a/src/js_native_api_v8_internals.h b/src/js_native_api_v8_internals.h index 7bf3bf0fa7e1a2..15013b96c0f795 100644 --- a/src/js_native_api_v8_internals.h +++ b/src/js_native_api_v8_internals.h @@ -1,14 +1,14 @@ #ifndef SRC_JS_NATIVE_API_V8_INTERNALS_H_ #define SRC_JS_NATIVE_API_V8_INTERNALS_H_ -// The V8 implementation of N-API, including `js_native_api_v8.h` uses certain -// idioms which require definition here. For example, it uses a variant of -// persistent references which need not be reset in the constructor. It is the -// responsibility of this file to define these idioms. Optionally, this file -// may also define `NAPI_VERSION` and set it to the version of N-API to be +// The V8 implementation of Node-API, including `js_native_api_v8.h` uses +// certain idioms which require definition here. For example, it uses a variant +// of persistent references which need not be reset in the constructor. It is +// the responsibility of this file to define these idioms. Optionally, this file +// may also define `NAPI_VERSION` and set it to the version of Node-API to be // exposed. -// In the case of the Node.js implementation of N-API some of the idioms are +// In the case of the Node.js implementation of Node-API some of the idioms are // imported directly from Node.js by including `node_internals.h` below. Others // are bridged to remove references to the `node` namespace. `node_version.h`, // included below, defines `NAPI_VERSION`. @@ -40,6 +40,12 @@ using PersistentToLocal = node::PersistentToLocal; node::OnFatalError(location, message); } +// Defined in node_api.cc +extern const struct node_api_module_vtable* GetNodeApiModuleVTable(); + +// Defined in js_native_api_v8.cc +extern const struct node_api_js_vtable* GetNodeApiJsVTable(); + } // end of namespace v8impl #endif // SRC_JS_NATIVE_API_V8_INTERNALS_H_ diff --git a/src/node_api.cc b/src/node_api.cc index cd17f7c199dae5..21c4771a8d9899 100644 --- a/src/node_api.cc +++ b/src/node_api.cc @@ -1,12 +1,10 @@ + +#include "node_api.h" #include "async_context_frame.h" #include "async_wrap-inl.h" #include "env-inl.h" -#ifndef NAPI_EXPERIMENTAL -#define NAPI_EXPERIMENTAL -#endif #include "js_native_api_v8.h" #include "memory_tracker-inl.h" -#include "node_api.h" #include "node_api_internals.h" #include "node_binding.h" #include "node_buffer.h" @@ -34,12 +32,11 @@ static void ThrowNodeApiVersionError(node::Environment* node_env, error_message += NODE_STRINGIFY(NODE_API_SUPPORTED_VERSION_MAX) " add-ons."; node_env->ThrowError(error_message.c_str()); } -} // namespace v8impl -/*static*/ napi_env node_napi_env__::New(v8::Local context, - const std::string& module_filename, - int32_t module_api_version) { - node_napi_env result; +/*static*/ NodeApiBaseEnv* NodeApiEnv::New(v8::Local context, + const std::string& module_filename, + int32_t module_api_version) { + NodeApiEnv* result; // Validate module_api_version. if (module_api_version < NODE_API_DEFAULT_MODULE_API_VERSION) { @@ -53,56 +50,56 @@ static void ThrowNodeApiVersionError(node::Environment* node_env, return nullptr; } - result = new node_napi_env__(context, module_filename, module_api_version); + result = new NodeApiEnv(context, module_filename, module_api_version); // TODO(addaleax): There was previously code that tried to delete the // napi_env when its v8::Context was garbage collected; - // However, as long as N-API addons using this napi_env are in place, + // However, as long as Node-API addons using this napi_env are in place, // the Context needs to be accessible and alive. // Ideally, we'd want an on-addon-unload hook that takes care of this - // once all N-API addons using this napi_env are unloaded. + // once all Node-API addons using this napi_env are unloaded. // For now, a per-Environment cleanup hook is the best we can do. result->node_env()->AddCleanupHook( - [](void* arg) { static_cast(arg)->Unref(); }, + [](void* arg) { static_cast(arg)->Unref(); }, static_cast(result)); return result; } -node_napi_env__::node_napi_env__(v8::Local context, - const std::string& module_filename, - int32_t module_api_version) - : napi_env__(context, module_api_version), filename(module_filename) { +NodeApiEnv::NodeApiEnv(v8::Local context, + const std::string& module_filename, + int32_t module_api_version) + : NodeApiBaseEnv(context, module_api_version), filename(module_filename) { CHECK_NOT_NULL(node_env()); } -void node_napi_env__::DeleteMe() { +void NodeApiEnv::DeleteMe() { destructing = true; DrainFinalizerQueue(); - napi_env__::DeleteMe(); + NodeApiBaseEnv::DeleteMe(); } -bool node_napi_env__::can_call_into_js() const { +bool NodeApiEnv::can_call_into_js() const { return node_env()->can_call_into_js(); } -void node_napi_env__::CallFinalizer(napi_finalize cb, void* data, void* hint) { +void NodeApiEnv::CallFinalizer(napi_finalize cb, void* data, void* hint) { CallFinalizer(cb, data, hint); } template -void node_napi_env__::CallFinalizer(napi_finalize cb, void* data, void* hint) { +void NodeApiEnv::CallFinalizer(napi_finalize cb, void* data, void* hint) { v8::HandleScope handle_scope(isolate); v8::Context::Scope context_scope(context()); CallbackIntoModule( [&](napi_env env) { cb(env, data, hint); }); } -void node_napi_env__::EnqueueFinalizer(v8impl::RefTracker* finalizer) { - napi_env__::EnqueueFinalizer(finalizer); +void NodeApiEnv::EnqueueFinalizer(v8impl::RefTracker* finalizer) { + NodeApiBaseEnv::EnqueueFinalizer(finalizer); // Schedule a second pass only when it has not been scheduled, and not // destructing the env. // When the env is being destructed, queued finalizers are drained in the - // loop of `node_napi_env__::DrainFinalizerQueue`. + // loop of `NodeApiEnv::DrainFinalizerQueue`. if (!finalization_scheduled && !destructing) { finalization_scheduled = true; Ref(); @@ -114,7 +111,7 @@ void node_napi_env__::EnqueueFinalizer(v8impl::RefTracker* finalizer) { } } -void node_napi_env__::DrainFinalizerQueue() { +void NodeApiEnv::DrainFinalizerQueue() { // As userland code can delete additional references in one finalizer, // the list of pending finalizers may be mutated as we execute them, so // we keep iterating it until it is empty. @@ -125,7 +122,7 @@ void node_napi_env__::DrainFinalizerQueue() { } } -void node_napi_env__::trigger_fatal_exception(v8::Local local_err) { +void NodeApiEnv::trigger_fatal_exception(v8::Local local_err) { v8::Local local_msg = v8::Exception::CreateMessage(isolate, local_err); node::errors::TriggerUncaughtException(isolate, local_err, local_msg); @@ -134,9 +131,9 @@ void node_napi_env__::trigger_fatal_exception(v8::Local local_err) { // The option enforceUncaughtExceptionPolicy is added for not breaking existing // running Node-API add-ons. template -void node_napi_env__::CallbackIntoModule(T&& call) { +void NodeApiEnv::CallbackIntoModule(T&& call) { CallIntoModule(call, [](napi_env env_, v8::Local local_err) { - node_napi_env__* env = static_cast(env_); + NodeApiEnv* env = static_cast(env_); if (env->terminatedOrTerminating()) { return; } @@ -150,7 +147,7 @@ void node_napi_env__::CallbackIntoModule(T&& call) { !enforceUncaughtExceptionPolicy) { ProcessEmitDeprecationWarning( node_env, - "Uncaught N-API callback exception detected, please run node " + "Uncaught Node-API callback exception detected, please run node " "with option --force-node-api-uncaught-exceptions-policy=true " "to handle those exceptions properly.", "DEP0168"); @@ -163,13 +160,11 @@ void node_napi_env__::CallbackIntoModule(T&& call) { }); } -namespace v8impl { - namespace { class BufferFinalizer : private Finalizer { public: - static BufferFinalizer* New(napi_env env, + static BufferFinalizer* New(NodeApiBaseEnv* env, napi_finalize finalize_callback, void* finalize_data, void* finalize_hint) { @@ -189,7 +184,7 @@ class BufferFinalizer : private Finalizer { }; private: - BufferFinalizer(napi_env env, + BufferFinalizer(NodeApiBaseEnv* env, napi_finalize finalize_callback, void* finalize_data, void* finalize_hint) @@ -200,7 +195,7 @@ class BufferFinalizer : private Finalizer { ~BufferFinalizer() { env()->Unref(); } }; -class ThreadSafeFunction { +class ThreadSafeFunction : public napi_threadsafe_function__ { public: ThreadSafeFunction(v8::Local func, v8::Local resource, @@ -208,11 +203,13 @@ class ThreadSafeFunction { size_t thread_count_, void* context_, size_t max_queue_size_, - node_napi_env env_, + NodeApiEnv* env_, void* finalize_data_, napi_finalize finalize_cb_, napi_threadsafe_function_call_js call_js_cb_) - : async_resource(std::in_place, + : napi_threadsafe_function__{NODE_API_VT_SENTINEL, + GetNodeApiModuleVTable()}, + async_resource(std::in_place, env_->isolate, resource, node::Utf8Value(env_->isolate, name).ToStringView()), @@ -573,7 +570,7 @@ class ThreadSafeFunction { // These are variables accessed only from the loop thread. v8impl::Persistent ref; - node_napi_env env; + NodeApiEnv* env; void* finalize_data; napi_finalize finalize_cb; napi_threadsafe_function_call_js call_js_cb; @@ -588,7 +585,7 @@ class ThreadSafeFunction { */ class AsyncContext { public: - AsyncContext(node_napi_env env, + AsyncContext(NodeApiEnv* env, v8::Local resource_object, v8::Local resource_name) : env_(env) { @@ -637,7 +634,7 @@ class AsyncContext { return {async_id_, trigger_async_id_}; } - static inline void CloseCallbackScope(node_napi_env env, + static inline void CloseCallbackScope(NodeApiEnv* env, napi_callback_scope s) { delete HeapAllocatedCallbackScope::FromOpaque(s); CHECK_GT(env->open_callback_scopes, 0); @@ -663,7 +660,7 @@ class AsyncContext { node::CallbackScope cs_; }; - node_napi_env env_; + NodeApiEnv* env_; double async_id_; double trigger_async_id_; v8::Global resource_; @@ -671,7 +668,6 @@ class AsyncContext { }; } // end of anonymous namespace - } // end of namespace v8impl // Intercepts the Node-V8 module registration callback. Converts parameters @@ -762,8 +758,8 @@ void napi_module_register_by_symbol(v8::Local exports, } // Create a new napi_env for this specific module. - napi_env env = - node_napi_env__::New(context, module_filename, module_api_version); + v8impl::NodeApiBaseEnv* env = + v8impl::NodeApiEnv::New(context, module_filename, module_api_version); napi_value _exports = nullptr; env->CallIntoModule([&](napi_env env) { @@ -803,9 +799,10 @@ void NAPI_CDECL napi_module_register(napi_module* mod) { node::node_module_register(nm); } -napi_status NAPI_CDECL napi_add_env_cleanup_hook(node_api_basic_env env, +napi_status NAPI_CDECL napi_add_env_cleanup_hook(node_api_basic_env basic_env, napi_cleanup_hook fun, void* arg) { + v8impl::NodeApiBaseEnv* env = v8impl::AsNodeApiBaseEnv(basic_env); CHECK_ENV(env); CHECK_ARG(env, fun); @@ -814,9 +811,9 @@ napi_status NAPI_CDECL napi_add_env_cleanup_hook(node_api_basic_env env, return napi_ok; } -napi_status NAPI_CDECL napi_remove_env_cleanup_hook(node_api_basic_env env, - napi_cleanup_hook fun, - void* arg) { +napi_status NAPI_CDECL napi_remove_env_cleanup_hook( + node_api_basic_env basic_env, napi_cleanup_hook fun, void* arg) { + v8impl::NodeApiBaseEnv* env = v8impl::AsNodeApiBaseEnv(basic_env); CHECK_ENV(env); CHECK_ARG(env, fun); @@ -825,52 +822,62 @@ napi_status NAPI_CDECL napi_remove_env_cleanup_hook(node_api_basic_env env, return napi_ok; } -struct napi_async_cleanup_hook_handle__ { - napi_async_cleanup_hook_handle__(napi_env env, - napi_async_cleanup_hook user_hook, - void* user_data) - : env_(env), user_hook_(user_hook), user_data_(user_data) { +namespace v8impl { +namespace { + +struct NodeApiAsyncCleanupHook : public napi_async_cleanup_hook_handle__ { + NodeApiAsyncCleanupHook(NodeApiBaseEnv* env, + napi_async_cleanup_hook user_hook, + void* user_data) + : napi_async_cleanup_hook_handle__{NODE_API_VT_SENTINEL, + GetNodeApiModuleVTable()}, + env_(env), + user_hook_(user_hook), + user_data_(user_data) { handle_ = node::AddEnvironmentCleanupHook(env->isolate, Hook, this); env->Ref(); } - ~napi_async_cleanup_hook_handle__() { + ~NodeApiAsyncCleanupHook() { node::RemoveEnvironmentCleanupHook(std::move(handle_)); if (done_cb_ != nullptr) done_cb_(done_data_); // Release the `env` handle asynchronously since it would be surprising if - // a call to a N-API function would destroy `env` synchronously. - static_cast(env_)->node_env()->SetImmediate( + // a call to a Node-API function would destroy `env` synchronously. + static_cast(env_)->node_env()->SetImmediate( [env = env_](node::Environment*) { env->Unref(); }); } static void Hook(void* data, void (*done_cb)(void*), void* done_data) { - napi_async_cleanup_hook_handle__* handle = - static_cast(data); + NodeApiAsyncCleanupHook* handle = + static_cast(data); handle->done_cb_ = done_cb; handle->done_data_ = done_data; handle->user_hook_(handle, handle->user_data_); } node::AsyncCleanupHookHandle handle_; - napi_env env_ = nullptr; + NodeApiBaseEnv* env_ = nullptr; napi_async_cleanup_hook user_hook_ = nullptr; void* user_data_ = nullptr; void (*done_cb_)(void*) = nullptr; void* done_data_ = nullptr; }; +} // end of anonymous namespace +} // end of namespace v8impl + napi_status NAPI_CDECL napi_add_async_cleanup_hook(node_api_basic_env basic_env, napi_async_cleanup_hook hook, void* arg, napi_async_cleanup_hook_handle* remove_handle) { - napi_env env = const_cast(basic_env); + v8impl::NodeApiBaseEnv* env = v8impl::AsNodeApiBaseEnv(basic_env); CHECK_ENV(env); CHECK_ARG(env, hook); - napi_async_cleanup_hook_handle__* handle = - new napi_async_cleanup_hook_handle__(env, hook, arg); + v8impl::NodeApiAsyncCleanupHook* handle = + new v8impl::NodeApiAsyncCleanupHook(env, hook, arg); if (remove_handle != nullptr) *remove_handle = handle; @@ -881,17 +888,18 @@ napi_status NAPI_CDECL napi_remove_async_cleanup_hook(napi_async_cleanup_hook_handle remove_handle) { if (remove_handle == nullptr) return napi_invalid_arg; - delete remove_handle; + delete static_cast(remove_handle); return napi_ok; } -napi_status NAPI_CDECL napi_fatal_exception(napi_env env, napi_value err) { +napi_status NAPI_CDECL napi_fatal_exception(napi_env env_, napi_value err) { + v8impl::NodeApiBaseEnv* env = v8impl::AsNodeApiBaseEnv(env_); NAPI_PREAMBLE(env); CHECK_ARG(env, err); v8::Local local_err = v8impl::V8LocalValueFromJsValue(err); - static_cast(env)->trigger_fatal_exception(local_err); + static_cast(env)->trigger_fatal_exception(local_err); return napi_clear_last_error(env); } @@ -919,12 +927,13 @@ NAPI_NO_RETURN void NAPI_CDECL napi_fatal_error(const char* location, } napi_status NAPI_CDECL -napi_open_callback_scope(napi_env env, +napi_open_callback_scope(napi_env env_, napi_value /** ignored */, napi_async_context async_context_handle, napi_callback_scope* result) { // Omit NAPI_PREAMBLE and GET_RETURN_STATUS because V8 calls here cannot throw // JS exceptions. + v8impl::NodeApiBaseEnv* env = v8impl::AsNodeApiBaseEnv(env_); CHECK_ENV(env); CHECK_ARG(env, result); @@ -936,26 +945,28 @@ napi_open_callback_scope(napi_env env, return napi_clear_last_error(env); } -napi_status NAPI_CDECL napi_close_callback_scope(napi_env env, +napi_status NAPI_CDECL napi_close_callback_scope(napi_env env_, napi_callback_scope scope) { // Omit NAPI_PREAMBLE and GET_RETURN_STATUS because V8 calls here cannot throw // JS exceptions. + v8impl::NodeApiBaseEnv* env = v8impl::AsNodeApiBaseEnv(env_); CHECK_ENV(env); CHECK_ARG(env, scope); if (env->open_callback_scopes == 0) { return napi_callback_scope_mismatch; } - v8impl::AsyncContext::CloseCallbackScope(reinterpret_cast(env), - scope); + v8impl::AsyncContext::CloseCallbackScope( + static_cast(env), scope); return napi_clear_last_error(env); } -napi_status NAPI_CDECL napi_async_init(napi_env env, +napi_status NAPI_CDECL napi_async_init(napi_env env_, napi_value async_resource, napi_value async_resource_name, napi_async_context* result) { + v8impl::NodeApiBaseEnv* env = v8impl::AsNodeApiBaseEnv(env_); CHECK_ENV_NOT_IN_GC(env); CHECK_ARG(env, async_resource_name); CHECK_ARG(env, result); @@ -974,15 +985,16 @@ napi_status NAPI_CDECL napi_async_init(napi_env env, CHECK_TO_STRING(env, context, v8_resource_name, async_resource_name); v8impl::AsyncContext* async_context = new v8impl::AsyncContext( - reinterpret_cast(env), v8_resource, v8_resource_name); + static_cast(env), v8_resource, v8_resource_name); *result = reinterpret_cast(async_context); return napi_clear_last_error(env); } -napi_status NAPI_CDECL napi_async_destroy(napi_env env, +napi_status NAPI_CDECL napi_async_destroy(napi_env env_, napi_async_context async_context) { + v8impl::NodeApiBaseEnv* env = v8impl::AsNodeApiBaseEnv(env_); CHECK_ENV_NOT_IN_GC(env); CHECK_ARG(env, async_context); @@ -994,13 +1006,14 @@ napi_status NAPI_CDECL napi_async_destroy(napi_env env, return napi_clear_last_error(env); } -napi_status NAPI_CDECL napi_make_callback(napi_env env, +napi_status NAPI_CDECL napi_make_callback(napi_env env_, napi_async_context async_context, napi_value recv, napi_value func, size_t argc, const napi_value* argv, napi_value* result) { + v8impl::NodeApiBaseEnv* env = v8impl::AsNodeApiBaseEnv(env_); NAPI_PREAMBLE(env); CHECK_ARG(env, recv); if (argc > 0) { @@ -1048,10 +1061,11 @@ napi_status NAPI_CDECL napi_make_callback(napi_env env, return GET_RETURN_STATUS(env); } -napi_status NAPI_CDECL napi_create_buffer(napi_env env, +napi_status NAPI_CDECL napi_create_buffer(napi_env env_, size_t length, void** data, napi_value* result) { + v8impl::NodeApiBaseEnv* env = v8impl::AsNodeApiBaseEnv(env_); NAPI_PREAMBLE(env); CHECK_ARG(env, result); @@ -1071,12 +1085,13 @@ napi_status NAPI_CDECL napi_create_buffer(napi_env env, } napi_status NAPI_CDECL -napi_create_external_buffer(napi_env env, +napi_create_external_buffer(napi_env env_, size_t length, void* data, node_api_basic_finalize basic_finalize_cb, void* finalize_hint, napi_value* result) { + v8impl::NodeApiBaseEnv* env = v8impl::AsNodeApiBaseEnv(env_); napi_finalize finalize_cb = reinterpret_cast(basic_finalize_cb); NAPI_PREAMBLE(env); @@ -1109,11 +1124,12 @@ napi_create_external_buffer(napi_env env, #endif // V8_ENABLE_SANDBOX } -napi_status NAPI_CDECL napi_create_buffer_copy(napi_env env, +napi_status NAPI_CDECL napi_create_buffer_copy(napi_env env_, size_t length, const void* data, void** result_data, napi_value* result) { + v8impl::NodeApiBaseEnv* env = v8impl::AsNodeApiBaseEnv(env_); NAPI_PREAMBLE(env); CHECK_ARG(env, result); @@ -1132,9 +1148,10 @@ napi_status NAPI_CDECL napi_create_buffer_copy(napi_env env, return GET_RETURN_STATUS(env); } -napi_status NAPI_CDECL napi_is_buffer(napi_env env, +napi_status NAPI_CDECL napi_is_buffer(napi_env env_, napi_value value, bool* result) { + v8impl::NodeApiBaseEnv* env = v8impl::AsNodeApiBaseEnv(env_); CHECK_ENV_NOT_IN_GC(env); CHECK_ARG(env, value); CHECK_ARG(env, result); @@ -1143,10 +1160,11 @@ napi_status NAPI_CDECL napi_is_buffer(napi_env env, return napi_clear_last_error(env); } -napi_status NAPI_CDECL napi_get_buffer_info(napi_env env, +napi_status NAPI_CDECL napi_get_buffer_info(napi_env env_, napi_value value, void** data, size_t* length) { + v8impl::NodeApiBaseEnv* env = v8impl::AsNodeApiBaseEnv(env_); CHECK_ENV_NOT_IN_GC(env); CHECK_ARG(env, value); @@ -1164,8 +1182,9 @@ napi_status NAPI_CDECL napi_get_buffer_info(napi_env env, return napi_clear_last_error(env); } -napi_status NAPI_CDECL napi_get_node_version(node_api_basic_env env, +napi_status NAPI_CDECL napi_get_node_version(node_api_basic_env basic_env, const napi_node_version** result) { + v8impl::NodeApiBaseEnv* env = v8impl::AsNodeApiBaseEnv(basic_env); CHECK_ENV(env); CHECK_ARG(env, result); static const napi_node_version version = { @@ -1193,7 +1212,7 @@ static napi_status ConvertUVErrorCode(int code) { // Wrapper around uv_work_t which calls user-provided callbacks. class Work : public node::AsyncResource, public node::ThreadPoolWork { private: - explicit Work(node_napi_env env, + explicit Work(v8impl::NodeApiEnv* env, v8::Local async_resource, v8::Local async_resource_name, napi_async_execute_callback execute, @@ -1212,7 +1231,7 @@ class Work : public node::AsyncResource, public node::ThreadPoolWork { ~Work() override = default; public: - static Work* New(node_napi_env env, + static Work* New(v8impl::NodeApiEnv* env, v8::Local async_resource, v8::Local async_resource_name, napi_async_execute_callback execute, @@ -1244,7 +1263,7 @@ class Work : public node::AsyncResource, public node::ThreadPoolWork { } private: - node_napi_env _env; + v8impl::NodeApiEnv* _env; void* _data; napi_async_execute_callback _execute; napi_async_complete_callback _complete; @@ -1263,13 +1282,14 @@ class Work : public node::AsyncResource, public node::ThreadPoolWork { } while (0) napi_status NAPI_CDECL -napi_create_async_work(napi_env env, +napi_create_async_work(napi_env env_, napi_value async_resource, napi_value async_resource_name, napi_async_execute_callback execute, napi_async_complete_callback complete, void* data, napi_async_work* result) { + v8impl::NodeApiBaseEnv* env = v8impl::AsNodeApiBaseEnv(env_); CHECK_ENV_NOT_IN_GC(env); CHECK_ARG(env, execute); CHECK_ARG(env, result); @@ -1286,7 +1306,7 @@ napi_create_async_work(napi_env env, v8::Local resource_name; CHECK_TO_STRING(env, context, resource_name, async_resource_name); - uvimpl::Work* work = uvimpl::Work::New(reinterpret_cast(env), + uvimpl::Work* work = uvimpl::Work::New(static_cast(env), resource, resource_name, execute, @@ -1298,8 +1318,9 @@ napi_create_async_work(napi_env env, return napi_clear_last_error(env); } -napi_status NAPI_CDECL napi_delete_async_work(napi_env env, +napi_status NAPI_CDECL napi_delete_async_work(napi_env env_, napi_async_work work) { + v8impl::NodeApiBaseEnv* env = v8impl::AsNodeApiBaseEnv(env_); CHECK_ENV_NOT_IN_GC(env); CHECK_ARG(env, work); @@ -1310,15 +1331,16 @@ napi_status NAPI_CDECL napi_delete_async_work(napi_env env, napi_status NAPI_CDECL napi_get_uv_event_loop(node_api_basic_env basic_env, uv_loop_t** loop) { - napi_env env = const_cast(basic_env); + v8impl::NodeApiBaseEnv* env = v8impl::AsNodeApiBaseEnv(basic_env); CHECK_ENV(env); CHECK_ARG(env, loop); - *loop = reinterpret_cast(env)->node_env()->event_loop(); + *loop = static_cast(env)->node_env()->event_loop(); return napi_clear_last_error(env); } -napi_status NAPI_CDECL napi_queue_async_work(node_api_basic_env env, +napi_status NAPI_CDECL napi_queue_async_work(node_api_basic_env basic_env, napi_async_work work) { + v8impl::NodeApiBaseEnv* env = v8impl::AsNodeApiBaseEnv(basic_env); CHECK_ENV(env); CHECK_ARG(env, work); @@ -1332,8 +1354,9 @@ napi_status NAPI_CDECL napi_queue_async_work(node_api_basic_env env, return napi_clear_last_error(env); } -napi_status NAPI_CDECL napi_cancel_async_work(node_api_basic_env env, +napi_status NAPI_CDECL napi_cancel_async_work(node_api_basic_env basic_env, napi_async_work work) { + v8impl::NodeApiBaseEnv* env = v8impl::AsNodeApiBaseEnv(basic_env); CHECK_ENV(env); CHECK_ARG(env, work); @@ -1345,7 +1368,7 @@ napi_status NAPI_CDECL napi_cancel_async_work(node_api_basic_env env, } napi_status NAPI_CDECL -napi_create_threadsafe_function(napi_env env, +napi_create_threadsafe_function(napi_env env_, napi_value func, napi_value async_resource, napi_value async_resource_name, @@ -1356,6 +1379,7 @@ napi_create_threadsafe_function(napi_env env, void* context, napi_threadsafe_function_call_js call_js_cb, napi_threadsafe_function* result) { + v8impl::NodeApiBaseEnv* env = v8impl::AsNodeApiBaseEnv(env_); CHECK_ENV_NOT_IN_GC(env); CHECK_ARG(env, async_resource_name); RETURN_STATUS_IF_FALSE(env, initial_thread_count > 0, napi_invalid_arg); @@ -1389,7 +1413,7 @@ napi_create_threadsafe_function(napi_env env, initial_thread_count, context, max_queue_size, - reinterpret_cast(env), + static_cast(env), thread_finalize_data, thread_finalize_cb, call_js_cb); @@ -1400,7 +1424,7 @@ napi_create_threadsafe_function(napi_env env, // Init deletes ts_fn upon failure. status = ts_fn->Init(); if (status == napi_ok) { - *result = reinterpret_cast(ts_fn); + *result = ts_fn; } } @@ -1412,7 +1436,7 @@ napi_status NAPI_CDECL napi_get_threadsafe_function_context( CHECK_NOT_NULL(func); CHECK_NOT_NULL(result); - *result = reinterpret_cast(func)->Context(); + *result = static_cast(func)->Context(); return napi_ok; } @@ -1421,52 +1445,51 @@ napi_call_threadsafe_function(napi_threadsafe_function func, void* data, napi_threadsafe_function_call_mode is_blocking) { CHECK_NOT_NULL(func); - return reinterpret_cast(func)->Push(data, - is_blocking); + return static_cast(func)->Push(data, + is_blocking); } napi_status NAPI_CDECL napi_acquire_threadsafe_function(napi_threadsafe_function func) { CHECK_NOT_NULL(func); - return reinterpret_cast(func)->Acquire(); + return static_cast(func)->Acquire(); } napi_status NAPI_CDECL napi_release_threadsafe_function( napi_threadsafe_function func, napi_threadsafe_function_release_mode mode) { CHECK_NOT_NULL(func); - return reinterpret_cast(func)->Release(mode); + return static_cast(func)->Release(mode); } napi_status NAPI_CDECL napi_unref_threadsafe_function( node_api_basic_env env, napi_threadsafe_function func) { CHECK_NOT_NULL(func); - return reinterpret_cast(func)->Unref(); + return static_cast(func)->Unref(); } napi_status NAPI_CDECL napi_ref_threadsafe_function( node_api_basic_env env, napi_threadsafe_function func) { CHECK_NOT_NULL(func); - return reinterpret_cast(func)->Ref(); + return static_cast(func)->Ref(); } napi_status NAPI_CDECL node_api_get_module_file_name( node_api_basic_env basic_env, const char** result) { - napi_env env = const_cast(basic_env); + v8impl::NodeApiBaseEnv* env = v8impl::AsNodeApiBaseEnv(basic_env); CHECK_ENV(env); CHECK_ARG(env, result); - *result = static_cast(env)->GetFilename(); + *result = static_cast(env)->GetFilename(); return napi_clear_last_error(env); } -#ifdef NAPI_EXPERIMENTAL - napi_status NAPI_CDECL -node_api_create_buffer_from_arraybuffer(napi_env env, +node_api_create_buffer_from_arraybuffer(napi_env env_, napi_value arraybuffer, size_t byte_offset, size_t byte_length, napi_value* result) { + v8impl::NodeApiBaseEnv* env = v8impl::AsNodeApiBaseEnv(env_); NAPI_PREAMBLE(env); CHECK_ARG(env, arraybuffer); CHECK_ARG(env, result); @@ -1492,4 +1515,45 @@ node_api_create_buffer_from_arraybuffer(napi_env env, return napi_ok; } -#endif +namespace v8impl { + +static const node_api_module_vtable g_module_vtable = { + napi_module_register, + napi_fatal_error, + napi_async_init, + napi_async_destroy, + napi_make_callback, + napi_create_buffer, + napi_create_external_buffer, + napi_create_buffer_copy, + napi_is_buffer, + napi_get_buffer_info, + napi_create_async_work, + napi_delete_async_work, + napi_queue_async_work, + napi_cancel_async_work, + napi_get_node_version, + napi_get_uv_event_loop, + napi_fatal_exception, + napi_add_env_cleanup_hook, + napi_remove_env_cleanup_hook, + napi_open_callback_scope, + napi_close_callback_scope, + napi_create_threadsafe_function, + napi_get_threadsafe_function_context, + napi_call_threadsafe_function, + napi_acquire_threadsafe_function, + napi_release_threadsafe_function, + napi_unref_threadsafe_function, + napi_ref_threadsafe_function, + napi_add_async_cleanup_hook, + napi_remove_async_cleanup_hook, + node_api_get_module_file_name, + node_api_create_buffer_from_arraybuffer, +}; + +const node_api_module_vtable* GetNodeApiModuleVTable() { + return &g_module_vtable; +} + +} // namespace v8impl diff --git a/src/node_api.h b/src/node_api.h index 46dbb02b47d24b..c450ef1c176450 100644 --- a/src/node_api.h +++ b/src/node_api.h @@ -9,11 +9,10 @@ #define NAPI_EXTERN __attribute__((__import_module__("napi"))) #endif #endif + #include "js_native_api.h" #include "node_api_types.h" -struct uv_loop_s; // Forward declaration. - #ifdef _WIN32 #define NAPI_MODULE_EXPORT __declspec(dllexport) #else @@ -33,17 +32,6 @@ struct uv_loop_s; // Forward declaration. #define NAPI_NO_RETURN #endif -// Used by deprecated registration method napi_module_register. -typedef struct napi_module { - int nm_version; - unsigned int nm_flags; - const char* nm_filename; - napi_addon_register_func nm_register_func; - const char* nm_modname; - void* nm_priv; - void* reserved[4]; -} napi_module; - #define NAPI_MODULE_VERSION 1 #define NAPI_MODULE_INITIALIZER_X(base, version) \ @@ -65,6 +53,37 @@ typedef struct napi_module { NAPI_MODULE_INITIALIZER_X(NODE_API_MODULE_GET_API_VERSION_BASE, \ NAPI_MODULE_VERSION) +#ifdef NODE_API_MODULE_USE_VTABLE_IMPL + +#ifndef NODE_API_MODULE_NO_VTABLE_FALLBACK +#define NODE_API_VTABLE_FALLBACK_GLOBALS \ + node_api_js_vtable g_node_api_js_vtable_fallback = {0}; \ + node_api_module_vtable g_node_api_module_vtable_fallback = {0}; +#else +#define NODE_API_VTABLE_FALLBACK_GLOBALS +#endif + +#define NODE_API_MODULE_INITIALIZER_IMPL NAPI_MODULE_INITIALIZER##_impl + +// NOLINTBEGIN (readability/null_usage) - must be compilable by C compiler +#define NODE_API_MODULE_INITIALIZER_IMPL_EX \ + NODE_API_VTABLE_FALLBACK_GLOBALS \ + const node_api_module_vtable* g_node_api_module_vtable = NULL; \ + napi_value NODE_API_MODULE_INITIALIZER_IMPL(napi_env env, \ + napi_value exports); \ + napi_value NAPI_MODULE_INITIALIZER(napi_env env, napi_value exports) { \ + if (env && env->sentinel == NODE_API_VT_SENTINEL) { \ + g_node_api_module_vtable = env->module_vtable; \ + } \ + return NODE_API_MODULE_INITIALIZER_IMPL(env, exports); \ + } +// NOLINTEND (readability/null_usage) + +#else // NODE_API_MODULE_USE_VTABLE_IMPL +#define NODE_API_MODULE_INITIALIZER_IMPL_EX +#define NODE_API_MODULE_INITIALIZER_IMPL NAPI_MODULE_INITIALIZER +#endif // NODE_API_MODULE_USE_VTABLE_IMPL + #define NAPI_MODULE_INIT() \ EXTERN_C_START \ NAPI_MODULE_EXPORT int32_t NODE_API_MODULE_GET_API_VERSION(void) { \ @@ -73,7 +92,8 @@ typedef struct napi_module { NAPI_MODULE_EXPORT napi_value NAPI_MODULE_INITIALIZER(napi_env env, \ napi_value exports); \ EXTERN_C_END \ - napi_value NAPI_MODULE_INITIALIZER(napi_env env, napi_value exports) + NODE_API_MODULE_INITIALIZER_IMPL_EX \ + napi_value NODE_API_MODULE_INITIALIZER_IMPL(napi_env env, napi_value exports) #define NAPI_MODULE(modname, regfunc) \ NAPI_MODULE_INIT() { return regfunc(env, exports); } @@ -84,49 +104,110 @@ typedef struct napi_module { EXTERN_C_START +#ifdef NODE_API_MODULE_USE_VTABLE_IMPL + +extern const node_api_module_vtable* g_node_api_module_vtable; + +#ifndef NODE_API_MODULE_NO_VTABLE_FALLBACK +extern node_api_module_vtable g_node_api_module_vtable_fallback; +#endif // NODE_API_MODULE_NO_VTABLE_FALLBACK + +#define NODE_API_GLOBAL_MODULE_VTABLE_IMPL(func_name, method_name, ...) \ + { \ + NODE_API_VTABLE_IMPL_FALLBACK( \ + , module_vtable, func_name, method_name, __VA_ARGS__); \ + } + +#define NODE_API_MODULE_VTABLE_IMPL(func_name, method_name, obj, ...) \ + NODE_API_VTABLE_IMPL_BASE( \ + module_vtable, func_name, method_name, obj, obj, __VA_ARGS__) + +#define NODE_API_MODULE_VTABLE_IMPL_NOARGS(func_name, method_name, obj) \ + NODE_API_VTABLE_IMPL_BASE(module_vtable, func_name, method_name, obj, obj) + +#else // NODE_API_MODULE_USE_VTABLE_IMPL + +#define NODE_API_GLOBAL_MODULE_VTABLE_IMPL(...) +#define NODE_API_MODULE_VTABLE_IMPL(...) +#define NODE_API_MODULE_VTABLE_IMPL_NOARGS(...) + +#endif // NODE_API_MODULE_USE_VTABLE_IMPL + // Deprecated. Replaced by symbol-based registration defined by NAPI_MODULE // and NAPI_MODULE_INIT macros. -NAPI_EXTERN void NAPI_CDECL -napi_module_register(napi_module* mod); - -NAPI_EXTERN NAPI_NO_RETURN void NAPI_CDECL -napi_fatal_error(const char* location, - size_t location_len, - const char* message, - size_t message_len); +NAPI_EXTERN void NAPI_CDECL napi_module_register(napi_module* mod) + NODE_API_GLOBAL_MODULE_VTABLE_IMPL(napi_module_register, + module_register, + mod); + +NAPI_EXTERN void NAPI_NO_RETURN NAPI_CDECL napi_fatal_error( + const char* location, + size_t location_len, + const char* message, + size_t message_len) NODE_API_GLOBAL_MODULE_VTABLE_IMPL(napi_fatal_error, + fatal_error, + location, + location_len, + message, + message_len); // Methods for custom handling of async operations -NAPI_EXTERN napi_status NAPI_CDECL -napi_async_init(napi_env env, - napi_value async_resource, - napi_value async_resource_name, - napi_async_context* result); +NAPI_EXTERN napi_status NAPI_CDECL napi_async_init( + napi_env env, + napi_value async_resource, + napi_value async_resource_name, + napi_async_context* result) NODE_API_MODULE_VTABLE_IMPL(napi_async_init, + async_init, + env, + async_resource, + async_resource_name, + result); NAPI_EXTERN napi_status NAPI_CDECL -napi_async_destroy(napi_env env, napi_async_context async_context); +napi_async_destroy(napi_env env, napi_async_context async_context) + NODE_API_MODULE_VTABLE_IMPL(napi_async_destroy, + async_destroy, + env, + async_context); + +NAPI_EXTERN napi_status NAPI_CDECL napi_make_callback( + napi_env env, + napi_async_context async_context, + napi_value recv, + napi_value func, + size_t argc, + const napi_value* argv, + napi_value* result) NODE_API_MODULE_VTABLE_IMPL(napi_make_callback, + make_callback, + env, + async_context, + recv, + func, + argc, + argv, + result); +// Methods to provide node::Buffer functionality with napi types NAPI_EXTERN napi_status NAPI_CDECL -napi_make_callback(napi_env env, - napi_async_context async_context, - napi_value recv, - napi_value func, - size_t argc, - const napi_value* argv, - napi_value* result); +napi_create_buffer(napi_env env, size_t length, void** data, napi_value* result) + NODE_API_MODULE_VTABLE_IMPL( + napi_create_buffer, create_buffer, env, length, data, result); -// Methods to provide node::Buffer functionality with napi types -NAPI_EXTERN napi_status NAPI_CDECL napi_create_buffer(napi_env env, - size_t length, - void** data, - napi_value* result); #ifndef NODE_API_NO_EXTERNAL_BUFFERS_ALLOWED -NAPI_EXTERN napi_status NAPI_CDECL -napi_create_external_buffer(napi_env env, - size_t length, - void* data, - node_api_basic_finalize finalize_cb, - void* finalize_hint, - napi_value* result); +NAPI_EXTERN napi_status NAPI_CDECL napi_create_external_buffer( + napi_env env, + size_t length, + void* data, + node_api_basic_finalize finalize_cb, + void* finalize_hint, + napi_value* result) NODE_API_MODULE_VTABLE_IMPL(napi_create_external_buffer, + create_external_buffer, + env, + length, + data, + finalize_cb, + finalize_hint, + result); #endif // NODE_API_NO_EXTERNAL_BUFFERS_ALLOWED #if NAPI_VERSION >= 10 @@ -136,69 +217,137 @@ node_api_create_buffer_from_arraybuffer(napi_env env, napi_value arraybuffer, size_t byte_offset, size_t byte_length, - napi_value* result); + napi_value* result) + NODE_API_MODULE_VTABLE_IMPL(node_api_create_buffer_from_arraybuffer, + create_buffer_from_arraybuffer, + env, + arraybuffer, + byte_offset, + byte_length, + result); #endif // NAPI_VERSION >= 10 NAPI_EXTERN napi_status NAPI_CDECL napi_create_buffer_copy(napi_env env, size_t length, const void* data, void** result_data, - napi_value* result); + napi_value* result) + NODE_API_MODULE_VTABLE_IMPL(napi_create_buffer_copy, + create_buffer_copy, + env, + length, + data, + result_data, + result); + NAPI_EXTERN napi_status NAPI_CDECL napi_is_buffer(napi_env env, napi_value value, - bool* result); + bool* result) + NODE_API_MODULE_VTABLE_IMPL(napi_is_buffer, is_buffer, env, value, result); + NAPI_EXTERN napi_status NAPI_CDECL napi_get_buffer_info(napi_env env, napi_value value, void** data, - size_t* length); + size_t* length) + NODE_API_MODULE_VTABLE_IMPL( + napi_get_buffer_info, get_buffer_info, env, value, data, length); // Methods to manage simple async operations -NAPI_EXTERN napi_status NAPI_CDECL -napi_create_async_work(napi_env env, - napi_value async_resource, - napi_value async_resource_name, - napi_async_execute_callback execute, - napi_async_complete_callback complete, - void* data, - napi_async_work* result); +NAPI_EXTERN napi_status NAPI_CDECL napi_create_async_work( + napi_env env, + napi_value async_resource, + napi_value async_resource_name, + napi_async_execute_callback execute, + napi_async_complete_callback complete, + void* data, + napi_async_work* result) NODE_API_MODULE_VTABLE_IMPL(napi_create_async_work, + create_async_work, + env, + async_resource, + async_resource_name, + execute, + complete, + data, + result); + NAPI_EXTERN napi_status NAPI_CDECL napi_delete_async_work(napi_env env, - napi_async_work work); + napi_async_work work) + NODE_API_MODULE_VTABLE_IMPL(napi_delete_async_work, + delete_async_work, + env, + work); + NAPI_EXTERN napi_status NAPI_CDECL napi_queue_async_work(node_api_basic_env env, - napi_async_work work); + napi_async_work work) + NODE_API_MODULE_VTABLE_IMPL(napi_queue_async_work, + queue_async_work, + env, + work); + NAPI_EXTERN napi_status NAPI_CDECL -napi_cancel_async_work(node_api_basic_env env, napi_async_work work); +napi_cancel_async_work(node_api_basic_env env, napi_async_work work) + NODE_API_MODULE_VTABLE_IMPL(napi_cancel_async_work, + cancel_async_work, + env, + work); // version management -NAPI_EXTERN napi_status NAPI_CDECL napi_get_node_version( - node_api_basic_env env, const napi_node_version** version); +NAPI_EXTERN napi_status NAPI_CDECL +napi_get_node_version(node_api_basic_env env, const napi_node_version** version) + NODE_API_MODULE_VTABLE_IMPL(napi_get_node_version, + get_node_version, + env, + version); #if NAPI_VERSION >= 2 // Return the current libuv event loop for a given environment NAPI_EXTERN napi_status NAPI_CDECL -napi_get_uv_event_loop(node_api_basic_env env, struct uv_loop_s** loop); +napi_get_uv_event_loop(node_api_basic_env env, struct uv_loop_s** loop) + NODE_API_MODULE_VTABLE_IMPL(napi_get_uv_event_loop, + get_uv_event_loop, + env, + loop); #endif // NAPI_VERSION >= 2 #if NAPI_VERSION >= 3 NAPI_EXTERN napi_status NAPI_CDECL napi_fatal_exception(napi_env env, - napi_value err); + napi_value err) + NODE_API_MODULE_VTABLE_IMPL(napi_fatal_exception, + fatal_exception, + env, + err); NAPI_EXTERN napi_status NAPI_CDECL napi_add_env_cleanup_hook( - node_api_basic_env env, napi_cleanup_hook fun, void* arg); + node_api_basic_env env, napi_cleanup_hook fun, void* arg) + NODE_API_MODULE_VTABLE_IMPL( + napi_add_env_cleanup_hook, add_env_cleanup_hook, env, fun, arg); NAPI_EXTERN napi_status NAPI_CDECL napi_remove_env_cleanup_hook( - node_api_basic_env env, napi_cleanup_hook fun, void* arg); + node_api_basic_env env, napi_cleanup_hook fun, void* arg) + NODE_API_MODULE_VTABLE_IMPL( + napi_remove_env_cleanup_hook, remove_env_cleanup_hook, env, fun, arg); NAPI_EXTERN napi_status NAPI_CDECL napi_open_callback_scope(napi_env env, napi_value resource_object, napi_async_context context, - napi_callback_scope* result); + napi_callback_scope* result) + NODE_API_MODULE_VTABLE_IMPL(napi_open_callback_scope, + open_callback_scope, + env, + resource_object, + context, + result); NAPI_EXTERN napi_status NAPI_CDECL -napi_close_callback_scope(napi_env env, napi_callback_scope scope); +napi_close_callback_scope(napi_env env, napi_callback_scope scope) + NODE_API_MODULE_VTABLE_IMPL(napi_close_callback_scope, + close_callback_scope, + env, + scope); #endif // NAPI_VERSION >= 3 @@ -216,27 +365,64 @@ napi_create_threadsafe_function(napi_env env, napi_finalize thread_finalize_cb, void* context, napi_threadsafe_function_call_js call_js_cb, - napi_threadsafe_function* result); + napi_threadsafe_function* result) + NODE_API_MODULE_VTABLE_IMPL(napi_create_threadsafe_function, + create_threadsafe_function, + env, + func, + async_resource, + async_resource_name, + max_queue_size, + initial_thread_count, + thread_finalize_data, + thread_finalize_cb, + context, + call_js_cb, + result); NAPI_EXTERN napi_status NAPI_CDECL napi_get_threadsafe_function_context( - napi_threadsafe_function func, void** result); + napi_threadsafe_function func, void** result) + NODE_API_MODULE_VTABLE_IMPL(napi_get_threadsafe_function_context, + get_threadsafe_function_context, + func, + result); NAPI_EXTERN napi_status NAPI_CDECL napi_call_threadsafe_function(napi_threadsafe_function func, void* data, - napi_threadsafe_function_call_mode is_blocking); + napi_threadsafe_function_call_mode is_blocking) + NODE_API_MODULE_VTABLE_IMPL(napi_call_threadsafe_function, + call_threadsafe_function, + func, + data, + is_blocking); NAPI_EXTERN napi_status NAPI_CDECL -napi_acquire_threadsafe_function(napi_threadsafe_function func); +napi_acquire_threadsafe_function(napi_threadsafe_function func) + NODE_API_MODULE_VTABLE_IMPL_NOARGS(napi_acquire_threadsafe_function, + acquire_threadsafe_function, + func); NAPI_EXTERN napi_status NAPI_CDECL napi_release_threadsafe_function( - napi_threadsafe_function func, napi_threadsafe_function_release_mode mode); + napi_threadsafe_function func, napi_threadsafe_function_release_mode mode) + NODE_API_MODULE_VTABLE_IMPL(napi_release_threadsafe_function, + release_threadsafe_function, + func, + mode); NAPI_EXTERN napi_status NAPI_CDECL napi_unref_threadsafe_function( - node_api_basic_env env, napi_threadsafe_function func); + node_api_basic_env env, napi_threadsafe_function func) + NODE_API_MODULE_VTABLE_IMPL(napi_unref_threadsafe_function, + unref_threadsafe_function, + env, + func); NAPI_EXTERN napi_status NAPI_CDECL napi_ref_threadsafe_function( - node_api_basic_env env, napi_threadsafe_function func); + node_api_basic_env env, napi_threadsafe_function func) + NODE_API_MODULE_VTABLE_IMPL(napi_ref_threadsafe_function, + ref_threadsafe_function, + env, + func); #endif // NAPI_VERSION >= 4 @@ -246,17 +432,30 @@ NAPI_EXTERN napi_status NAPI_CDECL napi_add_async_cleanup_hook(node_api_basic_env env, napi_async_cleanup_hook hook, void* arg, - napi_async_cleanup_hook_handle* remove_handle); + napi_async_cleanup_hook_handle* remove_handle) + NODE_API_MODULE_VTABLE_IMPL(napi_add_async_cleanup_hook, + add_async_cleanup_hook, + env, + hook, + arg, + remove_handle); NAPI_EXTERN napi_status NAPI_CDECL -napi_remove_async_cleanup_hook(napi_async_cleanup_hook_handle remove_handle); +napi_remove_async_cleanup_hook(napi_async_cleanup_hook_handle remove_handle) + NODE_API_MODULE_VTABLE_IMPL_NOARGS(napi_remove_async_cleanup_hook, + remove_async_cleanup_hook, + remove_handle); #endif // NAPI_VERSION >= 8 #if NAPI_VERSION >= 9 NAPI_EXTERN napi_status NAPI_CDECL -node_api_get_module_file_name(node_api_basic_env env, const char** result); +node_api_get_module_file_name(node_api_basic_env env, const char** result) + NODE_API_MODULE_VTABLE_IMPL(node_api_get_module_file_name, + get_module_file_name, + env, + result); #endif // NAPI_VERSION >= 9 diff --git a/src/node_api_internals.h b/src/node_api_internals.h index 43d28211ddb3d4..070522ccf0ab68 100644 --- a/src/node_api_internals.h +++ b/src/node_api_internals.h @@ -10,14 +10,16 @@ #include "node_api.h" #include "util-inl.h" -struct node_napi_env__ : public napi_env__ { - static napi_env New(v8::Local context, - const std::string& module_filename, - int32_t module_api_version); +namespace v8impl { - node_napi_env__(v8::Local context, - const std::string& module_filename, - int32_t module_api_version); +struct NodeApiEnv : public NodeApiBaseEnv { + static NodeApiBaseEnv* New(v8::Local context, + const std::string& module_filename, + int32_t module_api_version); + + NodeApiEnv(v8::Local context, + const std::string& module_filename, + int32_t module_api_version); bool can_call_into_js() const override; void CallFinalizer(napi_finalize cb, void* data, void* hint) override; @@ -43,6 +45,6 @@ struct node_napi_env__ : public napi_env__ { bool finalization_scheduled = false; }; -using node_napi_env = node_napi_env__*; +} // end of namespace v8impl #endif // SRC_NODE_API_INTERNALS_H_ diff --git a/src/node_api_types.h b/src/node_api_types.h index 79123f0423d6dd..dd650e8a12e556 100644 --- a/src/node_api_types.h +++ b/src/node_api_types.h @@ -3,6 +3,8 @@ #include "js_native_api_types.h" +struct uv_loop_s; // Forward declaration. + typedef napi_value(NAPI_CDECL* napi_addon_register_func)(napi_env env, napi_value exports); // False positive: https://github.com/cpplint/cpplint/issues/409 @@ -55,4 +57,206 @@ typedef void(NAPI_CDECL* napi_async_cleanup_hook)( napi_async_cleanup_hook_handle handle, void* data); #endif // NAPI_VERSION >= 8 +// Used by deprecated registration method napi_module_register. +typedef struct napi_module { + int nm_version; + unsigned int nm_flags; + const char* nm_filename; + napi_addon_register_func nm_register_func; + const char* nm_modname; + void* nm_priv; + void* reserved[4]; +} napi_module; + +#if defined(NODE_API_MODULE_USE_VTABLE) || defined(NODE_API_RUNTIME_USE_VTABLE) + +// Vtable for Node.js module-specific functions +// New functions must be added at the end to maintain backward compatibility. +typedef struct node_api_module_vtable { + void(NAPI_CDECL* module_register)(napi_module* mod); + + void(NAPI_CDECL* fatal_error)(const char* location, + size_t location_len, + const char* message, + size_t message_len); + + napi_status(NAPI_CDECL* async_init)(napi_env env, + napi_value async_resource, + napi_value async_resource_name, + napi_async_context* result); + + napi_status(NAPI_CDECL* async_destroy)(napi_env env, + napi_async_context async_context); + + napi_status(NAPI_CDECL* make_callback)(napi_env env, + napi_async_context async_context, + napi_value recv, + napi_value func, + size_t argc, + const napi_value* argv, + napi_value* result); + + napi_status(NAPI_CDECL* create_buffer)(napi_env env, + size_t length, + void** data, + napi_value* result); + napi_status(NAPI_CDECL* create_external_buffer)( + napi_env env, + size_t length, + void* data, + node_api_basic_finalize finalize_cb, + void* finalize_hint, + napi_value* result); + + napi_status(NAPI_CDECL* create_buffer_copy)(napi_env env, + size_t length, + const void* data, + void** result_data, + napi_value* result); + napi_status(NAPI_CDECL* is_buffer)(napi_env env, + napi_value value, + bool* result); + napi_status(NAPI_CDECL* get_buffer_info)(napi_env env, + napi_value value, + void** data, + size_t* length); + + napi_status(NAPI_CDECL* create_async_work)( + napi_env env, + napi_value async_resource, + napi_value async_resource_name, + napi_async_execute_callback execute, + napi_async_complete_callback complete, + void* data, + napi_async_work* result); + napi_status(NAPI_CDECL* delete_async_work)(napi_env env, + napi_async_work work); + napi_status(NAPI_CDECL* queue_async_work)(node_api_basic_env env, + napi_async_work work); + napi_status(NAPI_CDECL* cancel_async_work)(node_api_basic_env env, + napi_async_work work); + + napi_status(NAPI_CDECL* get_node_version)(node_api_basic_env env, + const napi_node_version** version); + +#if NAPI_VERSION >= 2 + + napi_status(NAPI_CDECL* get_uv_event_loop)(node_api_basic_env env, + struct uv_loop_s** loop); + +#endif // NAPI_VERSION >= 2 + +#if NAPI_VERSION >= 3 + + napi_status(NAPI_CDECL* fatal_exception)(napi_env env, napi_value err); + + napi_status(NAPI_CDECL* add_env_cleanup_hook)(node_api_basic_env env, + napi_cleanup_hook fun, + void* arg); + + napi_status(NAPI_CDECL* remove_env_cleanup_hook)(node_api_basic_env env, + napi_cleanup_hook fun, + void* arg); + + napi_status(NAPI_CDECL* open_callback_scope)(napi_env env, + napi_value resource_object, + napi_async_context context, + napi_callback_scope* result); + + napi_status(NAPI_CDECL* close_callback_scope)(napi_env env, + napi_callback_scope scope); + +#endif // NAPI_VERSION >= 3 + +#if NAPI_VERSION >= 4 + + napi_status(NAPI_CDECL* create_threadsafe_function)( + napi_env env, + napi_value func, + napi_value async_resource, + napi_value async_resource_name, + size_t max_queue_size, + size_t initial_thread_count, + void* thread_finalize_data, + napi_finalize thread_finalize_cb, + void* context, + napi_threadsafe_function_call_js call_js_cb, + napi_threadsafe_function* result); + + napi_status(NAPI_CDECL* get_threadsafe_function_context)( + napi_threadsafe_function func, void** result); + + napi_status(NAPI_CDECL* call_threadsafe_function)( + napi_threadsafe_function func, + void* data, + napi_threadsafe_function_call_mode is_blocking); + + napi_status(NAPI_CDECL* acquire_threadsafe_function)( + napi_threadsafe_function func); + + napi_status(NAPI_CDECL* release_threadsafe_function)( + napi_threadsafe_function func, + napi_threadsafe_function_release_mode mode); + + napi_status(NAPI_CDECL* unref_threadsafe_function)( + node_api_basic_env env, napi_threadsafe_function func); + + napi_status(NAPI_CDECL* ref_threadsafe_function)( + node_api_basic_env env, napi_threadsafe_function func); + +#endif // NAPI_VERSION >= 4 + +#if NAPI_VERSION >= 8 + + napi_status(NAPI_CDECL* add_async_cleanup_hook)( + node_api_basic_env env, + napi_async_cleanup_hook hook, + void* arg, + napi_async_cleanup_hook_handle* remove_handle); + + napi_status(NAPI_CDECL* remove_async_cleanup_hook)( + napi_async_cleanup_hook_handle remove_handle); + +#endif // NAPI_VERSION >= 8 + +#if NAPI_VERSION >= 9 + + napi_status(NAPI_CDECL* get_module_file_name)(node_api_basic_env env, + const char** result); + +#endif // NAPI_VERSION >= 9 + +#if NAPI_VERSION >= 10 + + napi_status(NAPI_CDECL* create_buffer_from_arraybuffer)( + napi_env env, + napi_value arraybuffer, + size_t byte_offset, + size_t byte_length, + napi_value* result); + +#endif // NAPI_VERSION >= 10 +} node_api_module_vtable; + +#if NAPI_VERSION >= 4 + +struct napi_threadsafe_function__ { + uint64_t sentinel; // Should be NODE_API_VT_SENTINEL + const struct node_api_module_vtable* module_vtable; +}; + +#endif // NAPI_VERSION >= 4 + +#if NAPI_VERSION >= 8 + +struct napi_async_cleanup_hook_handle__ { + uint64_t sentinel; // Should be NODE_API_VT_SENTINEL + const struct node_api_module_vtable* module_vtable; +}; + +#endif // NAPI_VERSION >= 8 + +#endif // defined(NODE_API_MODULE_USE_VTABLE) || + // defined(NODE_API_RUNTIME_USE_VTABLE) + #endif // SRC_NODE_API_TYPES_H_ diff --git a/src/node_binding.cc b/src/node_binding.cc index 5bd07e5253ae64..3b284583d6ccc9 100644 --- a/src/node_binding.cc +++ b/src/node_binding.cc @@ -526,7 +526,7 @@ void DLOpen(const FunctionCallbackInfo& args) { } } - // -1 is used for N-API modules + // -1 is used for Node-API modules if ((mp->nm_version != -1) && (mp->nm_version != NODE_MODULE_VERSION)) { // Even if the module did self-register, it may have done so with the // wrong version. We must only give up after having checked to see if it diff --git a/test/cctest/test_node_api.cc b/test/cctest/test_node_api.cc index 902776c157e9ed..893c50f728da9d 100644 --- a/test/cctest/test_node_api.cc +++ b/test/cctest/test_node_api.cc @@ -37,6 +37,7 @@ TEST_F(NodeApiTest, CreateNodeApiEnv) { napi_module_register_by_symbol( exports_obj, module_obj, env->context(), init, NAPI_VERSION); ASSERT_NE(addon_env, nullptr); - node_napi_env internal_env = reinterpret_cast(addon_env); + v8impl::NodeApiEnv* internal_env = + static_cast(addon_env); EXPECT_EQ(internal_env->node_env(), env); } diff --git a/test/common/addon-test.js b/test/common/addon-test.js new file mode 100644 index 00000000000000..475d0960721afc --- /dev/null +++ b/test/common/addon-test.js @@ -0,0 +1,58 @@ +'use strict'; + +const common = require('./'); +const { spawnSync } = require('child_process'); +const { Worker } = require('worker_threads'); +const { parseArgs } = require('node:util'); + +const { values: parsedArgs } = parseArgs({ + options: { + script: { type: 'string' }, + addon: { type: 'string' }, + role: { type: 'string' }, // 'child', 'worker', or 'worker-thread' + }, + strict: false, +}); + +if (require.main === module) { + if (!parsedArgs.addon || !parsedArgs.role || !parsedArgs.script) { + throw new Error('addon-test.js requires --addon, --role, and --script parameters'); + } +} else if (!parsedArgs.addon) { + throw new Error('Tests using addon-test.js require --addon= command line argument'); +} + +const isInvokedAsChild = !!parsedArgs.role; +const addonPath = `./build/${common.buildType}/${parsedArgs.addon}`; + +function spawnTestSync(args = [], options = {}) { + const { stdio = ['inherit', 'pipe', 'pipe'], env = {}, useWorkerThread = false } = options; + return spawnSync(process.execPath, [ + ...process.execArgv, + ...args, + __filename, + `--script=${require.main.filename}`, + `--addon=${parsedArgs.addon}`, + `--role=${useWorkerThread ? 'worker' : 'child'}`, + ], { + ...options, + env: { ...process.env, ...env }, + stdio, + }); +} + +module.exports = { + addonPath, + isInvokedAsChild, + spawnTestSync, +}; + +if (require.main === module) { + if (parsedArgs.role === 'worker') { + new Worker(__filename, { + argv: [`--script=${parsedArgs.script}`, `--addon=${parsedArgs.addon}`, '--role=worker-thread'], + }); + } else { + require(parsedArgs.script); + } +} diff --git a/test/eslint.config_partial.mjs b/test/eslint.config_partial.mjs index 6fbdf277044679..80942d7cc933f2 100644 --- a/test/eslint.config_partial.mjs +++ b/test/eslint.config_partial.mjs @@ -210,6 +210,15 @@ export default [ 'node-core/must-call-assert': 'off', }, }, + { + files: [ + 'test/{js-native-api,node-api}/**/*.js', + ], + rules: { + 'node-core/required-modules': 'off', + 'node-core/require-common-first': 'off', + }, + }, { files: [ 'test/es-module/test-esm-example-loader.js', diff --git a/test/js-native-api/2_function_arguments/binding.gyp b/test/js-native-api/2_function_arguments/binding.gyp index 77836418d4736e..4eff61d61505ef 100644 --- a/test/js-native-api/2_function_arguments/binding.gyp +++ b/test/js-native-api/2_function_arguments/binding.gyp @@ -5,6 +5,13 @@ "sources": [ "2_function_arguments.c" ] + }, + { + "target_name": "2_function_arguments_vtable", + "defines": [ "NODE_API_MODULE_USE_VTABLE" ], + "sources": [ + "2_function_arguments.c" + ] } ] } diff --git a/test/js-native-api/2_function_arguments/test.js b/test/js-native-api/2_function_arguments/test.js index bf0ecabac3b7a0..c6eb8e6349147e 100644 --- a/test/js-native-api/2_function_arguments/test.js +++ b/test/js-native-api/2_function_arguments/test.js @@ -1,6 +1,8 @@ 'use strict'; -const common = require('../../common'); +// Addons: 2_function_arguments, 2_function_arguments_vtable + +const { addonPath } = require('../../common/addon-test'); const assert = require('assert'); -const addon = require(`./build/${common.buildType}/2_function_arguments`); +const addon = require(addonPath); assert.strictEqual(addon.add(3, 5), 8); diff --git a/test/js-native-api/3_callbacks/binding.gyp b/test/js-native-api/3_callbacks/binding.gyp index 0b3e2eb96cd903..f604f06722c2fc 100644 --- a/test/js-native-api/3_callbacks/binding.gyp +++ b/test/js-native-api/3_callbacks/binding.gyp @@ -5,6 +5,13 @@ "sources": [ "3_callbacks.c" ] + }, + { + "target_name": "3_callbacks_vtable", + "defines": [ "NODE_API_MODULE_USE_VTABLE" ], + "sources": [ + "3_callbacks.c" + ] } ] } diff --git a/test/js-native-api/3_callbacks/test.js b/test/js-native-api/3_callbacks/test.js index 9c61e18c982b89..67e1f1f40a81b7 100644 --- a/test/js-native-api/3_callbacks/test.js +++ b/test/js-native-api/3_callbacks/test.js @@ -1,7 +1,10 @@ 'use strict'; +// Addons: 3_callbacks, 3_callbacks_vtable + const common = require('../../common'); +const { addonPath } = require('../../common/addon-test'); const assert = require('assert'); -const addon = require(`./build/${common.buildType}/3_callbacks`); +const addon = require(addonPath); addon.RunCallback(common.mustCall((msg) => { assert.strictEqual(msg, 'hello world'); diff --git a/test/js-native-api/4_object_factory/binding.gyp b/test/js-native-api/4_object_factory/binding.gyp index c1f2aca1498346..a4b1142999ddb6 100644 --- a/test/js-native-api/4_object_factory/binding.gyp +++ b/test/js-native-api/4_object_factory/binding.gyp @@ -5,6 +5,13 @@ "sources": [ "4_object_factory.c" ] + }, + { + "target_name": "4_object_factory_vtable", + "defines": [ "NODE_API_MODULE_USE_VTABLE" ], + "sources": [ + "4_object_factory.c" + ] } ] } diff --git a/test/js-native-api/4_object_factory/test.js b/test/js-native-api/4_object_factory/test.js index 16cad91753d40e..041b0d5787e1d2 100644 --- a/test/js-native-api/4_object_factory/test.js +++ b/test/js-native-api/4_object_factory/test.js @@ -1,7 +1,9 @@ 'use strict'; -const common = require('../../common'); +// Addons: 4_object_factory, 4_object_factory_vtable + +const { addonPath } = require('../../common/addon-test'); const assert = require('assert'); -const addon = require(`./build/${common.buildType}/4_object_factory`); +const addon = require(addonPath); const obj1 = addon('hello'); const obj2 = addon('world'); diff --git a/test/js-native-api/5_function_factory/binding.gyp b/test/js-native-api/5_function_factory/binding.gyp index 183332d3441112..9d8438068081fc 100644 --- a/test/js-native-api/5_function_factory/binding.gyp +++ b/test/js-native-api/5_function_factory/binding.gyp @@ -5,6 +5,13 @@ "sources": [ "5_function_factory.c" ] + }, + { + "target_name": "5_function_factory_vtable", + "defines": [ "NODE_API_MODULE_USE_VTABLE" ], + "sources": [ + "5_function_factory.c" + ] } ] } diff --git a/test/js-native-api/5_function_factory/test.js b/test/js-native-api/5_function_factory/test.js index 9d4772985c2a40..87fed072bb7a82 100644 --- a/test/js-native-api/5_function_factory/test.js +++ b/test/js-native-api/5_function_factory/test.js @@ -1,7 +1,9 @@ 'use strict'; -const common = require('../../common'); +// Addons: 5_function_factory, 5_function_factory_vtable + +const { addonPath } = require('../../common/addon-test'); const assert = require('assert'); -const addon = require(`./build/${common.buildType}/5_function_factory`); +const addon = require(addonPath); const fn = addon(); assert.strictEqual(fn(), 'hello world'); // 'hello world' diff --git a/test/js-native-api/6_object_wrap/binding.gyp b/test/js-native-api/6_object_wrap/binding.gyp index 49dd929fc747a0..8305df39a393ba 100644 --- a/test/js-native-api/6_object_wrap/binding.gyp +++ b/test/js-native-api/6_object_wrap/binding.gyp @@ -7,6 +7,14 @@ "myobject.h", ] }, + { + "target_name": "myobject_vtable", + "defines": [ "NODE_API_MODULE_USE_VTABLE" ], + "sources": [ + "myobject.cc", + "myobject.h", + ] + }, { "target_name": "myobject_basic_finalizer", "defines": [ "NAPI_EXPERIMENTAL" ], @@ -15,6 +23,14 @@ "myobject.h", ] }, + { + "target_name": "myobject_basic_finalizer_vtable", + "defines": [ "NAPI_EXPERIMENTAL", "NODE_API_MODULE_USE_VTABLE" ], + "sources": [ + "myobject.cc", + "myobject.h", + ] + }, { "target_name": "nested_wrap", # Test without basic finalizers as it schedules differently. @@ -24,5 +40,14 @@ "nested_wrap.h", ], }, + { + "target_name": "nested_wrap_vtable", + # Test without basic finalizers as it schedules differently. + "defines": [ "NAPI_VERSION=10", "NODE_API_MODULE_USE_VTABLE" ], + "sources": [ + "nested_wrap.cc", + "nested_wrap.h", + ] + } ] } diff --git a/test/js-native-api/6_object_wrap/nested_wrap.js b/test/js-native-api/6_object_wrap/nested_wrap.js index 2d99b54c7d2754..744083ce238b4a 100644 --- a/test/js-native-api/6_object_wrap/nested_wrap.js +++ b/test/js-native-api/6_object_wrap/nested_wrap.js @@ -1,10 +1,11 @@ +'use strict'; // Flags: --expose-gc +// Addons: nested_wrap, nested_wrap_vtable -'use strict'; -const common = require('../../common'); +const { addonPath } = require('../../common/addon-test'); const { gcUntil } = require('../../common/gc'); const assert = require('assert'); -const addon = require(`./build/${common.buildType}/nested_wrap`); +const addon = require(addonPath); // This test verifies that ObjectWrap and napi_ref can be nested and finalized // correctly with a non-basic finalizer. diff --git a/test/js-native-api/6_object_wrap/test-basic-finalizer.js b/test/js-native-api/6_object_wrap/test-basic-finalizer.js index 631c1cb96a50eb..69790d3315ce8e 100644 --- a/test/js-native-api/6_object_wrap/test-basic-finalizer.js +++ b/test/js-native-api/6_object_wrap/test-basic-finalizer.js @@ -1,9 +1,10 @@ +'use strict'; // Flags: --expose-gc +// Addons: myobject_basic_finalizer, myobject_basic_finalizer_vtable -'use strict'; -const common = require('../../common'); +const { addonPath } = require('../../common/addon-test'); const assert = require('assert'); -const addon = require(`./build/${common.buildType}/myobject_basic_finalizer`); +const addon = require(addonPath); // This test verifies that ObjectWrap can be correctly finalized with a node_api_basic_finalizer // in the current JS loop tick diff --git a/test/js-native-api/6_object_wrap/test-object-wrap-ref.js b/test/js-native-api/6_object_wrap/test-object-wrap-ref.js index 874c1304b26867..931048a2f2c795 100644 --- a/test/js-native-api/6_object_wrap/test-object-wrap-ref.js +++ b/test/js-native-api/6_object_wrap/test-object-wrap-ref.js @@ -1,8 +1,9 @@ +'use strict'; // Flags: --expose-gc +// Addons: myobject, myobject_vtable -'use strict'; -const common = require('../../common'); -const addon = require(`./build/${common.buildType}/myobject`); +const { addonPath } = require('../../common/addon-test'); +const addon = require(addonPath); const { gcUntil } = require('../../common/gc'); (function scope() { diff --git a/test/js-native-api/6_object_wrap/test.js b/test/js-native-api/6_object_wrap/test.js index e30289e9bd437b..5c54a18a070d1d 100644 --- a/test/js-native-api/6_object_wrap/test.js +++ b/test/js-native-api/6_object_wrap/test.js @@ -1,7 +1,9 @@ 'use strict'; -const common = require('../../common'); +// Addons: myobject, myobject_vtable + +const { addonPath } = require('../../common/addon-test'); const assert = require('assert'); -const addon = require(`./build/${common.buildType}/myobject`); +const addon = require(addonPath); const getterOnlyErrorRE = /^TypeError: Cannot set property .* of #<.*> which has only a getter$/; diff --git a/test/js-native-api/7_factory_wrap/binding.gyp b/test/js-native-api/7_factory_wrap/binding.gyp index bb7c8aab1826a2..83ad60157a32e6 100644 --- a/test/js-native-api/7_factory_wrap/binding.gyp +++ b/test/js-native-api/7_factory_wrap/binding.gyp @@ -6,6 +6,14 @@ "7_factory_wrap.cc", "myobject.cc" ] + }, + { + "target_name": "7_factory_wrap_vtable", + "defines": [ "NODE_API_MODULE_USE_VTABLE" ], + "sources": [ + "7_factory_wrap.cc", + "myobject.cc" + ] } ] } diff --git a/test/js-native-api/7_factory_wrap/test.js b/test/js-native-api/7_factory_wrap/test.js index f2afc94807c569..023c9b61dfa262 100644 --- a/test/js-native-api/7_factory_wrap/test.js +++ b/test/js-native-api/7_factory_wrap/test.js @@ -1,9 +1,10 @@ 'use strict'; // Flags: --expose-gc +// Addons: 7_factory_wrap, 7_factory_wrap_vtable -const common = require('../../common'); +const { addonPath } = require('../../common/addon-test'); const assert = require('assert'); -const test = require(`./build/${common.buildType}/7_factory_wrap`); +const test = require(addonPath); const { gcUntil } = require('../../common/gc'); assert.strictEqual(test.finalizeCount, 0); diff --git a/test/js-native-api/8_passing_wrapped/binding.gyp b/test/js-native-api/8_passing_wrapped/binding.gyp index 206d106e52cf94..71244935dd49ff 100644 --- a/test/js-native-api/8_passing_wrapped/binding.gyp +++ b/test/js-native-api/8_passing_wrapped/binding.gyp @@ -6,6 +6,14 @@ "8_passing_wrapped.cc", "myobject.cc" ] + }, + { + "target_name": "8_passing_wrapped_vtable", + "defines": [ "NODE_API_MODULE_USE_VTABLE" ], + "sources": [ + "8_passing_wrapped.cc", + "myobject.cc" + ] } ] } diff --git a/test/js-native-api/8_passing_wrapped/test.js b/test/js-native-api/8_passing_wrapped/test.js index a192f6a7588f37..9fff4d68d24041 100644 --- a/test/js-native-api/8_passing_wrapped/test.js +++ b/test/js-native-api/8_passing_wrapped/test.js @@ -1,9 +1,10 @@ 'use strict'; // Flags: --expose-gc +// Addons: 8_passing_wrapped, 8_passing_wrapped_vtable -const common = require('../../common'); +const { addonPath } = require('../../common/addon-test'); const assert = require('assert'); -const addon = require(`./build/${common.buildType}/8_passing_wrapped`); +const addon = require(addonPath); const { gcUntil } = require('../../common/gc'); async function runTest() { diff --git a/test/js-native-api/test_array/binding.gyp b/test/js-native-api/test_array/binding.gyp index ba19b16e397ad8..6a2404e30c9be2 100644 --- a/test/js-native-api/test_array/binding.gyp +++ b/test/js-native-api/test_array/binding.gyp @@ -5,6 +5,13 @@ "sources": [ "test_array.c" ] + }, + { + "target_name": "test_array_vtable", + "defines": [ "NODE_API_MODULE_USE_VTABLE" ], + "sources": [ + "test_array.c" + ] } ] } diff --git a/test/js-native-api/test_array/test.js b/test/js-native-api/test_array/test.js index bec7e657a76d39..f917d446e26126 100644 --- a/test/js-native-api/test_array/test.js +++ b/test/js-native-api/test_array/test.js @@ -1,9 +1,11 @@ 'use strict'; -const common = require('../../common'); +// Addons: test_array, test_array_vtable + +const { addonPath } = require('../../common/addon-test'); const assert = require('assert'); // Testing api calls for arrays -const test_array = require(`./build/${common.buildType}/test_array`); +const test_array = require(addonPath); const array = [ 1, diff --git a/test/js-native-api/test_bigint/binding.gyp b/test/js-native-api/test_bigint/binding.gyp index 6ef04b4394ae8a..a985c3f8adcef7 100644 --- a/test/js-native-api/test_bigint/binding.gyp +++ b/test/js-native-api/test_bigint/binding.gyp @@ -5,6 +5,13 @@ "sources": [ "test_bigint.c" ] + }, + { + "target_name": "test_bigint_vtable", + "defines": [ "NODE_API_MODULE_USE_VTABLE" ], + "sources": [ + "test_bigint.c" + ] } ] } diff --git a/test/js-native-api/test_bigint/test.js b/test/js-native-api/test_bigint/test.js index 75fccc8512717b..0784401cbdc1ff 100644 --- a/test/js-native-api/test_bigint/test.js +++ b/test/js-native-api/test_bigint/test.js @@ -1,5 +1,7 @@ 'use strict'; -const common = require('../../common'); +// Addons: test_bigint, test_bigint_vtable + +const { addonPath } = require('../../common/addon-test'); const assert = require('assert'); const { IsLossless, @@ -8,7 +10,7 @@ const { TestWords, CreateTooBigBigInt, MakeBigIntWordsThrow, -} = require(`./build/${common.buildType}/test_bigint`); +} = require(addonPath); [ 0n, diff --git a/test/js-native-api/test_cannot_run_js/binding.gyp b/test/js-native-api/test_cannot_run_js/binding.gyp index dfaaf408296d1d..7cd0d303a1db9f 100644 --- a/test/js-native-api/test_cannot_run_js/binding.gyp +++ b/test/js-native-api/test_cannot_run_js/binding.gyp @@ -2,17 +2,31 @@ "targets": [ { "target_name": "test_cannot_run_js", + "defines": [ "NAPI_VERSION=10" ], "sources": [ "test_cannot_run_js.c" ], - "defines": [ "NAPI_VERSION=10" ], }, { - "target_name": "test_pending_exception", + "target_name": "test_cannot_run_js_vtable", + "defines": [ "NAPI_VERSION=10", "NODE_API_MODULE_USE_VTABLE" ], "sources": [ "test_cannot_run_js.c" ], + }, + { + "target_name": "test_pending_exception", "defines": [ "NAPI_VERSION=9" ], + "sources": [ + "test_cannot_run_js.c" + ], + }, + { + "target_name": "test_pending_exception_vtable", + "defines": [ "NAPI_VERSION=9", "NODE_API_MODULE_USE_VTABLE" ], + "sources": [ + "test_cannot_run_js.c" + ], } ] } diff --git a/test/js-native-api/test_cannot_run_js/test.js b/test/js-native-api/test_cannot_run_js/test.js index da77d0e0e1cff3..64509dc7ddaf94 100644 --- a/test/js-native-api/test_cannot_run_js/test.js +++ b/test/js-native-api/test_cannot_run_js/test.js @@ -1,4 +1,6 @@ 'use strict'; +// Addons: test_pending_exception, test_pending_exception_vtable +// Addons: test_cannot_run_js, test_cannot_run_js_vtable // Test that `napi_call_function()` returns `napi_cannot_run_js` in experimental // mode and `napi_pending_exception` otherwise. This test calls the add-on's @@ -8,17 +10,8 @@ // a property of the global object and will abort the process if the API doesn't // return the correct status. -const { buildType, mustNotCall } = require('../../common'); -const addon_v8 = require(`./build/${buildType}/test_pending_exception`); -const addon_new = require(`./build/${buildType}/test_cannot_run_js`); +const { mustNotCall } = require('../../common'); +const { addonPath } = require('../../common/addon-test'); +const addon = require(addonPath); -function runTests(addon, isVersion8) { - addon.createRef(mustNotCall()); -} - -function runAllTests() { - runTests(addon_v8, /* isVersion8 */ true); - runTests(addon_new, /* isVersion8 */ false); -} - -runAllTests(); +addon.createRef(mustNotCall()); diff --git a/test/js-native-api/test_constructor/binding.gyp b/test/js-native-api/test_constructor/binding.gyp index d796a9dbf1cf44..a58ec7bf89bbfd 100644 --- a/test/js-native-api/test_constructor/binding.gyp +++ b/test/js-native-api/test_constructor/binding.gyp @@ -6,6 +6,14 @@ "test_constructor.c", "test_null.c", ] + }, + { + "target_name": "test_constructor_vtable", + "defines": [ "NODE_API_MODULE_USE_VTABLE" ], + "sources": [ + "test_constructor.c", + "test_null.c", + ] } ] } diff --git a/test/js-native-api/test_constructor/test.js b/test/js-native-api/test_constructor/test.js index 6fb33d935489b5..d77e6705e7b9b5 100644 --- a/test/js-native-api/test_constructor/test.js +++ b/test/js-native-api/test_constructor/test.js @@ -1,12 +1,14 @@ 'use strict'; -const common = require('../../common'); +// Addons: test_constructor, test_constructor_vtable + +const { addonPath } = require('../../common/addon-test'); const assert = require('assert'); const getterOnlyErrorRE = /^TypeError: Cannot set property .* of #<.*> which has only a getter$/; // Testing api calls for a constructor that defines properties -const TestConstructor = require(`./build/${common.buildType}/test_constructor`); +const TestConstructor = require(addonPath); const test_object = new TestConstructor(); assert.strictEqual(test_object.echo('hello'), 'hello'); diff --git a/test/js-native-api/test_constructor/test2.js b/test/js-native-api/test_constructor/test2.js index 230a2250e20ae7..a6688d17ddaefb 100644 --- a/test/js-native-api/test_constructor/test2.js +++ b/test/js-native-api/test_constructor/test2.js @@ -1,8 +1,10 @@ 'use strict'; -const common = require('../../common'); +// Addons: test_constructor, test_constructor_vtable + +const { addonPath } = require('../../common/addon-test'); const assert = require('assert'); // Testing api calls for a constructor that defines properties const TestConstructor = - require(`./build/${common.buildType}/test_constructor`).constructorName; + require(addonPath).constructorName; assert.strictEqual(TestConstructor.name, 'MyObject'); diff --git a/test/js-native-api/test_constructor/test_null.js b/test/js-native-api/test_constructor/test_null.js index 832d2e6ef4811a..423dac62af4281 100644 --- a/test/js-native-api/test_constructor/test_null.js +++ b/test/js-native-api/test_constructor/test_null.js @@ -1,9 +1,11 @@ 'use strict'; -const common = require('../../common'); +// Addons: test_constructor, test_constructor_vtable + +const { addonPath } = require('../../common/addon-test'); const assert = require('assert'); -// Test passing NULL to object-related N-APIs. -const { testNull } = require(`./build/${common.buildType}/test_constructor`); +// Test passing NULL to object-related Node-APIs. +const { testNull } = require(addonPath); const expectedResult = { envIsNull: 'Invalid argument', nameIsNull: 'Invalid argument', diff --git a/test/js-native-api/test_conversions/binding.gyp b/test/js-native-api/test_conversions/binding.gyp index c286c3fd029203..b4f08e2d0b7035 100644 --- a/test/js-native-api/test_conversions/binding.gyp +++ b/test/js-native-api/test_conversions/binding.gyp @@ -6,6 +6,14 @@ "test_conversions.c", "test_null.c", ] + }, + { + "target_name": "test_conversions_vtable", + "defines": [ "NODE_API_MODULE_USE_VTABLE" ], + "sources": [ + "test_conversions.c", + "test_null.c", + ] } ] } diff --git a/test/js-native-api/test_conversions/test.js b/test/js-native-api/test_conversions/test.js index 02227fd27920d1..2267a3ad2c0f20 100644 --- a/test/js-native-api/test_conversions/test.js +++ b/test/js-native-api/test_conversions/test.js @@ -1,7 +1,9 @@ 'use strict'; -const common = require('../../common'); +// Addons: test_conversions, test_conversions_vtable + +const { addonPath } = require('../../common/addon-test'); const assert = require('assert'); -const test = require(`./build/${common.buildType}/test_conversions`); +const test = require(addonPath); const boolExpected = /boolean was expected/; const numberExpected = /number was expected/; diff --git a/test/js-native-api/test_dataview/binding.gyp b/test/js-native-api/test_dataview/binding.gyp index c6d8a22c035c43..5a22656bc9dec0 100644 --- a/test/js-native-api/test_dataview/binding.gyp +++ b/test/js-native-api/test_dataview/binding.gyp @@ -2,12 +2,18 @@ "targets": [ { "target_name": "test_dataview", + # NAPI_EXPERIMENTAL for node_api_is_sharedarraybuffer + "defines": [ "NAPI_EXPERIMENTAL", "NODE_API_EXPERIMENTAL_NO_WARNING" ], + "sources": [ + "test_dataview.c" + ], + }, + { + "target_name": "test_dataview_vtable", + "defines": [ "NAPI_EXPERIMENTAL", "NODE_API_EXPERIMENTAL_NO_WARNING", "NODE_API_MODULE_USE_VTABLE" ], "sources": [ "test_dataview.c" ], - - # For node_api_is_sharedarraybuffer - 'defines': [ 'NAPI_EXPERIMENTAL', 'NODE_API_EXPERIMENTAL_NO_WARNING' ] } ] } diff --git a/test/js-native-api/test_dataview/test.js b/test/js-native-api/test_dataview/test.js index 218d9446a6b972..7e8c53693e69ce 100644 --- a/test/js-native-api/test_dataview/test.js +++ b/test/js-native-api/test_dataview/test.js @@ -1,9 +1,11 @@ 'use strict'; -const common = require('../../common'); +// Addons: test_dataview, test_dataview_vtable + +const { addonPath } = require('../../common/addon-test'); const assert = require('assert'); // Testing api calls for arrays -const test_dataview = require(`./build/${common.buildType}/test_dataview`); +const test_dataview = require(addonPath); // Test for creating dataview with ArrayBuffer { diff --git a/test/js-native-api/test_date/binding.gyp b/test/js-native-api/test_date/binding.gyp index 6039d122c7649a..97d20a60e25f7a 100644 --- a/test/js-native-api/test_date/binding.gyp +++ b/test/js-native-api/test_date/binding.gyp @@ -5,6 +5,13 @@ "sources": [ "test_date.c" ] + }, + { + "target_name": "test_date_vtable", + "defines": [ "NODE_API_MODULE_USE_VTABLE" ], + "sources": [ + "test_date.c" + ] } ] } diff --git a/test/js-native-api/test_date/test.js b/test/js-native-api/test_date/test.js index e504208520e4d3..de5fb4c52ef76a 100644 --- a/test/js-native-api/test_date/test.js +++ b/test/js-native-api/test_date/test.js @@ -1,11 +1,12 @@ 'use strict'; +// Addons: test_date, test_date_vtable -const common = require('../../common'); +const { addonPath } = require('../../common/addon-test'); -// This tests the date-related n-api calls +// This tests the date-related Node-API calls const assert = require('assert'); -const test_date = require(`./build/${common.buildType}/test_date`); +const test_date = require(addonPath); const dateTypeTestDate = test_date.createDate(1549183351); assert.strictEqual(test_date.isDate(dateTypeTestDate), true); diff --git a/test/js-native-api/test_error/binding.gyp b/test/js-native-api/test_error/binding.gyp index 46382427fe669c..f7e3d17b86452c 100644 --- a/test/js-native-api/test_error/binding.gyp +++ b/test/js-native-api/test_error/binding.gyp @@ -5,6 +5,13 @@ "sources": [ "test_error.c" ] + }, + { + "target_name": "test_error_vtable", + "defines": [ "NODE_API_MODULE_USE_VTABLE" ], + "sources": [ + "test_error.c" + ] } ] } diff --git a/test/js-native-api/test_error/test.js b/test/js-native-api/test_error/test.js index c9725df09dadba..8ed72fc30f6b8c 100644 --- a/test/js-native-api/test_error/test.js +++ b/test/js-native-api/test_error/test.js @@ -1,7 +1,8 @@ 'use strict'; +// Addons: test_error, test_error_vtable -const common = require('../../common'); -const test_error = require(`./build/${common.buildType}/test_error`); +const { addonPath } = require('../../common/addon-test'); +const test_error = require(addonPath); const assert = require('assert'); const theError = new Error('Some error'); const theTypeError = new TypeError('Some type error'); diff --git a/test/js-native-api/test_exception/binding.gyp b/test/js-native-api/test_exception/binding.gyp index e98a564a10feac..6b4502c34e49b9 100644 --- a/test/js-native-api/test_exception/binding.gyp +++ b/test/js-native-api/test_exception/binding.gyp @@ -5,6 +5,13 @@ "sources": [ "test_exception.c" ] + }, + { + "target_name": "test_exception_vtable", + "defines": [ "NODE_API_MODULE_USE_VTABLE" ], + "sources": [ + "test_exception.c" + ] } ] } diff --git a/test/js-native-api/test_exception/test.js b/test/js-native-api/test_exception/test.js index 9e1d0d960628e9..24712e7cdfbe63 100644 --- a/test/js-native-api/test_exception/test.js +++ b/test/js-native-api/test_exception/test.js @@ -1,7 +1,9 @@ 'use strict'; // Flags: --expose-gc +// Addons: test_exception, test_exception_vtable const common = require('../../common'); +const { addonPath } = require('../../common/addon-test'); const assert = require('assert'); const theError = new Error('Some error'); @@ -13,7 +15,7 @@ const theError = new Error('Some error'); const test_exception = (function() { let resultingException; try { - require(`./build/${common.buildType}/test_exception`); + require(addonPath); } catch (anException) { resultingException = anException; } diff --git a/test/js-native-api/test_exception/testFinalizerException.js b/test/js-native-api/test_exception/testFinalizerException.js index 16f8bd5fe76228..805b474aa55734 100644 --- a/test/js-native-api/test_exception/testFinalizerException.js +++ b/test/js-native-api/test_exception/testFinalizerException.js @@ -1,11 +1,17 @@ 'use strict'; -if (process.argv[2] === 'child') { - const common = require('../../common'); +// Addons: test_exception, test_exception_vtable + +const { addonPath, isInvokedAsChild, spawnTestSync } = + require('../../common/addon-test'); +const { gcUntil } = require('../../common/gc'); +const assert = require('assert'); + +if (isInvokedAsChild) { // Trying, catching the exception, and finding the bindings at the `Error`'s // `binding` property is done intentionally, because we're also testing what // happens when the add-on entry point throws. See test.js. try { - require(`./build/${common.buildType}/test_exception`); + require(addonPath); } catch (anException) { anException.binding.createExternal(); } @@ -13,20 +19,9 @@ if (process.argv[2] === 'child') { // Collect garbage 10 times. At least one of those should throw the exception // and cause the whole process to bail with it, its text printed to stderr and // asserted by the parent process to match expectations. - let gcCount = 10; - (function gcLoop() { - global.gc(); - if (--gcCount > 0) { - setImmediate(() => gcLoop()); - } - })(); - return; + gcUntil('finalizer exception', () => false); +} else { + const child = spawnTestSync(['--expose-gc']); + assert.strictEqual(child.signal, null); + assert.match(child.stderr.toString(), /Error during Finalize/); } - -const assert = require('assert'); -const { spawnSync } = require('child_process'); -const child = spawnSync(process.execPath, [ - '--expose-gc', __filename, 'child', -]); -assert.strictEqual(child.signal, null); -assert.match(child.stderr.toString(), /Error during Finalize/); diff --git a/test/js-native-api/test_finalizer/binding.gyp b/test/js-native-api/test_finalizer/binding.gyp index 4c63346f30ce74..b3236c7aba944e 100644 --- a/test/js-native-api/test_finalizer/binding.gyp +++ b/test/js-native-api/test_finalizer/binding.gyp @@ -6,6 +6,13 @@ "sources": [ "test_finalizer.c" ] + }, + { + "target_name": "test_finalizer_vtable", + "defines": [ "NAPI_EXPERIMENTAL", "NODE_API_MODULE_USE_VTABLE" ], + "sources": [ + "test_finalizer.c" + ] } ] } diff --git a/test/js-native-api/test_finalizer/test.js b/test/js-native-api/test_finalizer/test.js index 502db40122e3a8..6d504059b85f49 100644 --- a/test/js-native-api/test_finalizer/test.js +++ b/test/js-native-api/test_finalizer/test.js @@ -1,10 +1,10 @@ 'use strict'; // Flags: --expose-gc +// Addons: test_finalizer, test_finalizer_vtable -const common = require('../../common'); -const test_finalizer = require(`./build/${common.buildType}/test_finalizer`); +const { addonPath } = require('../../common/addon-test'); +const test_finalizer = require(addonPath); const assert = require('assert'); - const { gcUntil } = require('../../common/gc'); // The goal of this test is to show that we can run "pure" finalizers in the diff --git a/test/js-native-api/test_finalizer/test_fatal_finalize.js b/test/js-native-api/test_finalizer/test_fatal_finalize.js index a100eb220024da..7a55182bfe5485 100644 --- a/test/js-native-api/test_finalizer/test_fatal_finalize.js +++ b/test/js-native-api/test_finalizer/test_fatal_finalize.js @@ -1,8 +1,14 @@ 'use strict'; +// Addons: test_finalizer, test_finalizer_vtable + const common = require('../../common'); +const { addonPath, isInvokedAsChild, spawnTestSync } = + require('../../common/addon-test'); +const { gcUntil } = require('../../common/gc'); +const assert = require('assert'); -if (process.argv[2] === 'child') { - const test_finalizer = require(`./build/${common.buildType}/test_finalizer`); +if (isInvokedAsChild) { + const test_finalizer = require(addonPath); (() => { const obj = {}; @@ -12,20 +18,9 @@ if (process.argv[2] === 'child') { // Collect garbage 10 times. At least one of those should throw the exception // and cause the whole process to bail with it, its text printed to stderr and // asserted by the parent process to match expectations. - let gcCount = 10; - (function gcLoop() { - global.gc(); - if (--gcCount > 0) { - setImmediate(() => gcLoop()); - } - })(); - return; + gcUntil('fatal finalize', () => false); +} else { + const child = spawnTestSync(['--expose-gc']); + assert(common.nodeProcessAborted(child.status, child.signal)); + assert.match(child.stderr.toString(), /Finalizer is calling a function that may affect GC state/); } - -const assert = require('assert'); -const { spawnSync } = require('child_process'); -const child = spawnSync(process.execPath, [ - '--expose-gc', __filename, 'child', -]); -assert(common.nodeProcessAborted(child.status, child.signal)); -assert.match(child.stderr.toString(), /Finalizer is calling a function that may affect GC state/); diff --git a/test/js-native-api/test_function/binding.gyp b/test/js-native-api/test_function/binding.gyp index 7ea9400c351b88..f6916475d191f1 100644 --- a/test/js-native-api/test_function/binding.gyp +++ b/test/js-native-api/test_function/binding.gyp @@ -5,6 +5,13 @@ "sources": [ "test_function.c" ] + }, + { + "target_name": "test_function_vtable", + "defines": [ "NODE_API_MODULE_USE_VTABLE" ], + "sources": [ + "test_function.c" + ] } ] } diff --git a/test/js-native-api/test_function/test.js b/test/js-native-api/test_function/test.js index a976540f5d6230..02b3dab70e524d 100644 --- a/test/js-native-api/test_function/test.js +++ b/test/js-native-api/test_function/test.js @@ -1,11 +1,13 @@ 'use strict'; // Flags: --expose-gc +// Addons: test_function, test_function_vtable const common = require('../../common'); +const { addonPath } = require('../../common/addon-test'); const assert = require('assert'); // Testing api calls for function -const test_function = require(`./build/${common.buildType}/test_function`); +const test_function = require(addonPath); function func1() { return 1; diff --git a/test/js-native-api/test_general/binding.gyp b/test/js-native-api/test_general/binding.gyp index cd261547de149f..068ac2403e06ce 100644 --- a/test/js-native-api/test_general/binding.gyp +++ b/test/js-native-api/test_general/binding.gyp @@ -2,12 +2,17 @@ "targets": [ { "target_name": "test_general", + "defines": [ "NAPI_EXPERIMENTAL" ], "sources": [ "test_general.c" ], - "defines": [ - "NAPI_EXPERIMENTAL" - ], + }, + { + "target_name": "test_general_vtable", + "defines": [ "NAPI_EXPERIMENTAL", "NODE_API_MODULE_USE_VTABLE" ], + "sources": [ + "test_general.c" + ] } ] } diff --git a/test/js-native-api/test_general/test.js b/test/js-native-api/test_general/test.js index 7bb40f7ae74eb8..c83262ec9beac9 100644 --- a/test/js-native-api/test_general/test.js +++ b/test/js-native-api/test_general/test.js @@ -1,8 +1,9 @@ 'use strict'; // Flags: --expose-gc +// Addons: test_general, test_general_vtable -const common = require('../../common'); -const test_general = require(`./build/${common.buildType}/test_general`); +const { addonPath } = require('../../common/addon-test'); +const test_general = require(addonPath); const assert = require('assert'); const { gcUntil } = require('../../common/gc'); diff --git a/test/js-native-api/test_general/testEnvCleanup.js b/test/js-native-api/test_general/testEnvCleanup.js index 4aa707d3de03ea..e6eb30cb09ca6a 100644 --- a/test/js-native-api/test_general/testEnvCleanup.js +++ b/test/js-native-api/test_general/testEnvCleanup.js @@ -1,8 +1,11 @@ 'use strict'; +// Addons: test_general, test_general_vtable -if (process.argv[2] === 'child') { - const common = require('../../common'); - const test_general = require(`./build/${common.buildType}/test_general`); +const { addonPath, isInvokedAsChild, spawnTestSync } = require('../../common/addon-test'); +const assert = require('assert'); + +if (isInvokedAsChild) { + const test_general = require(addonPath); // The second argument to `envCleanupWrap()` is an index into the global // static string array named `env_cleanup_finalizer_messages` on the native @@ -35,12 +38,7 @@ if (process.argv[2] === 'child') { test_general.envCleanupWrap(module.exports['first wrap'], finalizerMessages['second wrap']); } else { - const assert = require('assert'); - const { spawnSync } = require('child_process'); - - const child = spawnSync(process.execPath, [__filename, 'child'], { - stdio: [ process.stdin, 'pipe', process.stderr ], - }); + const child = spawnTestSync(); // Grab the child's output and construct an object whose keys are the rows of // the output and whose values are `true`, so we can compare the output while diff --git a/test/js-native-api/test_general/testFinalizer.js b/test/js-native-api/test_general/testFinalizer.js index 975170249262a9..f43d996606b7ec 100644 --- a/test/js-native-api/test_general/testFinalizer.js +++ b/test/js-native-api/test_general/testFinalizer.js @@ -1,8 +1,10 @@ 'use strict'; // Flags: --expose-gc +// Addons: test_general, test_general_vtable const common = require('../../common'); -const test_general = require(`./build/${common.buildType}/test_general`); +const { addonPath } = require('../../common/addon-test'); +const test_general = require(addonPath); const assert = require('assert'); const { gcUntil } = require('../../common/gc'); diff --git a/test/js-native-api/test_general/testGlobals.js b/test/js-native-api/test_general/testGlobals.js index 38cf3f3edbccbc..03a1ef37cdc0d0 100644 --- a/test/js-native-api/test_general/testGlobals.js +++ b/test/js-native-api/test_general/testGlobals.js @@ -1,8 +1,10 @@ 'use strict'; -const common = require('../../common'); +// Addons: test_general, test_general_vtable + +const { addonPath } = require('../../common/addon-test'); const assert = require('assert'); -const test_globals = require(`./build/${common.buildType}/test_general`); +const test_globals = require(addonPath); assert.strictEqual(test_globals.getUndefined(), undefined); assert.strictEqual(test_globals.getNull(), null); diff --git a/test/js-native-api/test_general/testInstanceOf.js b/test/js-native-api/test_general/testInstanceOf.js index 0946dda957d48c..812cd7ca7ce4db 100644 --- a/test/js-native-api/test_general/testInstanceOf.js +++ b/test/js-native-api/test_general/testInstanceOf.js @@ -1,9 +1,11 @@ 'use strict'; -const common = require('../../common'); +// Addons: test_general, test_general_vtable + +const { addonPath } = require('../../common/addon-test'); const assert = require('assert'); // Addon is referenced through the eval expression in testFile -const addon = require(`./build/${common.buildType}/test_general`); +const addon = require(addonPath); // We can only perform this test if we have a working Symbol.hasInstance if (typeof Symbol !== 'undefined' && 'hasInstance' in Symbol && diff --git a/test/js-native-api/test_general/testNapiRun.js b/test/js-native-api/test_general/testNapiRun.js index 38bfa3a516a018..71b3b032cedd06 100644 --- a/test/js-native-api/test_general/testNapiRun.js +++ b/test/js-native-api/test_general/testNapiRun.js @@ -1,10 +1,11 @@ 'use strict'; +// Addons: test_general, test_general_vtable -const common = require('../../common'); +const { addonPath } = require('../../common/addon-test'); const assert = require('assert'); // `addon` is referenced through the eval expression in testFile -const addon = require(`./build/${common.buildType}/test_general`); +const addon = require(addonPath); const testCase = '(41.92 + 0.08);'; const expected = 42; diff --git a/test/js-native-api/test_general/testNapiStatus.js b/test/js-native-api/test_general/testNapiStatus.js index a588862098f68f..8920e62c15ff8e 100644 --- a/test/js-native-api/test_general/testNapiStatus.js +++ b/test/js-native-api/test_general/testNapiStatus.js @@ -1,7 +1,8 @@ 'use strict'; +// Addons: test_general, test_general_vtable -const common = require('../../common'); -const addon = require(`./build/${common.buildType}/test_general`); +const { addonPath } = require('../../common/addon-test'); +const addon = require(addonPath); const assert = require('assert'); addon.createNapiError(); diff --git a/test/js-native-api/test_general/testV8Instanceof.js b/test/js-native-api/test_general/testV8Instanceof.js index d8016c94ce4bd1..ab1857b755a5ed 100644 --- a/test/js-native-api/test_general/testV8Instanceof.js +++ b/test/js-native-api/test_general/testV8Instanceof.js @@ -1,3 +1,6 @@ +'use strict'; +// Addons: test_general, test_general_vtable + // This test is adopted from V8's test suite. // See deps/v8/test/mjsunit/instanceof.js in Node.js source repository. // @@ -27,10 +30,9 @@ // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -'use strict'; -const common = require('../../common'); -const addon = require(`./build/${common.buildType}/test_general`); +const { addonPath } = require('../../common/addon-test'); +const addon = require(addonPath); const assert = require('assert'); assert.ok(addon.doInstanceOf({}, Object)); diff --git a/test/js-native-api/test_general/testV8Instanceof2.js b/test/js-native-api/test_general/testV8Instanceof2.js index f39ec7227cbb9b..86157110fcb2a6 100644 --- a/test/js-native-api/test_general/testV8Instanceof2.js +++ b/test/js-native-api/test_general/testV8Instanceof2.js @@ -1,3 +1,6 @@ +'use strict'; +// Addons: test_general, test_general_vtable + // This test is adopted from V8's test suite. // See deps/v8/test/mjsunit/instanceof-2.js in Node.js source repository. // @@ -27,10 +30,9 @@ // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -'use strict'; -const common = require('../../common'); -const addon = require(`./build/${common.buildType}/test_general`); +const { addonPath } = require('../../common/addon-test'); +const addon = require(addonPath); const assert = require('assert'); const except = 'exception'; diff --git a/test/js-native-api/test_handle_scope/binding.gyp b/test/js-native-api/test_handle_scope/binding.gyp index 7959c47cb9039e..17879a88f692ab 100644 --- a/test/js-native-api/test_handle_scope/binding.gyp +++ b/test/js-native-api/test_handle_scope/binding.gyp @@ -5,6 +5,13 @@ "sources": [ "test_handle_scope.c" ] + }, + { + "target_name": "test_handle_scope_vtable", + "defines": [ "NODE_API_MODULE_USE_VTABLE" ], + "sources": [ + "test_handle_scope.c" + ] } ] } diff --git a/test/js-native-api/test_handle_scope/test.js b/test/js-native-api/test_handle_scope/test.js index 3935e1164092f2..1184aa59c38879 100644 --- a/test/js-native-api/test_handle_scope/test.js +++ b/test/js-native-api/test_handle_scope/test.js @@ -1,10 +1,11 @@ 'use strict'; -const common = require('../../common'); +// Addons: test_handle_scope, test_handle_scope_vtable + +const { addonPath } = require('../../common/addon-test'); const assert = require('assert'); // Testing handle scope api calls -const testHandleScope = - require(`./build/${common.buildType}/test_handle_scope`); +const testHandleScope = require(addonPath); testHandleScope.NewScope(); diff --git a/test/js-native-api/test_instance_data/binding.gyp b/test/js-native-api/test_instance_data/binding.gyp index 0d55905e9e7236..698fdef2a1699f 100644 --- a/test/js-native-api/test_instance_data/binding.gyp +++ b/test/js-native-api/test_instance_data/binding.gyp @@ -5,6 +5,13 @@ "sources": [ "test_instance_data.c" ] + }, + { + "target_name": "test_instance_data_vtable", + "defines": [ "NODE_API_MODULE_USE_VTABLE" ], + "sources": [ + "test_instance_data.c" + ] } ] } diff --git a/test/js-native-api/test_instance_data/test.js b/test/js-native-api/test_instance_data/test.js index 23efb32678eb87..63b35882760a70 100644 --- a/test/js-native-api/test_instance_data/test.js +++ b/test/js-native-api/test_instance_data/test.js @@ -1,13 +1,15 @@ 'use strict'; +// Addons: test_instance_data, test_instance_data_vtable + // Test API calls for instance data. const common = require('../../common'); +const { addonPath, isInvokedAsChild, spawnTestSync } = require('../../common/addon-test'); const assert = require('assert'); -if (module !== require.main) { +if (isInvokedAsChild) { // When required as a module, run the tests. - const test_instance_data = - require(`./build/${common.buildType}/test_instance_data`); + const test_instance_data = require(addonPath); // Print to stdout when the environment deletes the instance data. This output // is checked by the parent process. @@ -22,8 +24,6 @@ if (module !== require.main) { } else { // When launched as a script, run tests in either a child process or in a // worker thread. - const requireAs = require('../../common/require-as'); - const runOptions = { stdio: ['inherit', 'pipe', 'inherit'] }; function checkOutput(child) { assert.strictEqual(child.status, 0); @@ -33,8 +33,8 @@ if (module !== require.main) { } // Run tests in a child process. - checkOutput(requireAs(__filename, ['--expose-gc'], runOptions, 'child')); + checkOutput(spawnTestSync(['--expose-gc'])); // Run tests in a worker thread in a child process. - checkOutput(requireAs(__filename, ['--expose-gc'], runOptions, 'worker')); + checkOutput(spawnTestSync(['--expose-gc'], { useWorkerThread: true })); } diff --git a/test/js-native-api/test_new_target/binding.gyp b/test/js-native-api/test_new_target/binding.gyp index 1afe797d1402b8..b7c2e374d9a241 100644 --- a/test/js-native-api/test_new_target/binding.gyp +++ b/test/js-native-api/test_new_target/binding.gyp @@ -6,6 +6,13 @@ 'sources': [ 'test_new_target.c' ] + }, + { + 'target_name': 'test_new_target_vtable', + 'defines': [ 'V8_DEPRECATION_WARNINGS=1', 'NODE_API_MODULE_USE_VTABLE' ], + 'sources': [ + 'test_new_target.c' + ] } ] } diff --git a/test/js-native-api/test_new_target/test.js b/test/js-native-api/test_new_target/test.js index 02d610e1e09734..9847cbb12cb67a 100644 --- a/test/js-native-api/test_new_target/test.js +++ b/test/js-native-api/test_new_target/test.js @@ -1,8 +1,9 @@ 'use strict'; +// Addons: test_new_target, test_new_target_vtable -const common = require('../../common'); +const { addonPath } = require('../../common/addon-test'); const assert = require('assert'); -const binding = require(`./build/${common.buildType}/test_new_target`); +const binding = require(addonPath); class Class extends binding.BaseClass { constructor() { diff --git a/test/js-native-api/test_number/binding.gyp b/test/js-native-api/test_number/binding.gyp index c0a4cb62d9803e..5803378121abcb 100644 --- a/test/js-native-api/test_number/binding.gyp +++ b/test/js-native-api/test_number/binding.gyp @@ -6,6 +6,14 @@ "test_number.c", "test_null.c", ] + }, + { + "target_name": "test_number_vtable", + "defines": [ "NODE_API_MODULE_USE_VTABLE" ], + "sources": [ + "test_number.c", + "test_null.c", + ] } ] } diff --git a/test/js-native-api/test_number/test.js b/test/js-native-api/test_number/test.js index 33d2699bc563c1..edc85774cc731b 100644 --- a/test/js-native-api/test_number/test.js +++ b/test/js-native-api/test_number/test.js @@ -1,8 +1,9 @@ 'use strict'; -const common = require('../../common'); -const assert = require('assert'); -const test_number = require(`./build/${common.buildType}/test_number`); +// Addons: test_number, test_number_vtable +const { addonPath } = require('../../common/addon-test'); +const assert = require('assert'); +const test_number = require(addonPath); // Testing api calls for number function testNumber(num) { diff --git a/test/js-native-api/test_number/test_null.js b/test/js-native-api/test_number/test_null.js index 046369d199a5d4..c82a13e1e5d762 100644 --- a/test/js-native-api/test_number/test_null.js +++ b/test/js-native-api/test_number/test_null.js @@ -1,7 +1,9 @@ 'use strict'; -const common = require('../../common'); +// Addons: test_number, test_number_vtable + +const { addonPath } = require('../../common/addon-test'); const assert = require('assert'); -const { testNull } = require(`./build/${common.buildType}/test_number`); +const { testNull } = require(addonPath); const expectedCreateResult = { envIsNull: 'Invalid argument', diff --git a/test/js-native-api/test_object/binding.gyp b/test/js-native-api/test_object/binding.gyp index cb1dca11ad65ad..83f061375fcf4f 100644 --- a/test/js-native-api/test_object/binding.gyp +++ b/test/js-native-api/test_object/binding.gyp @@ -10,11 +10,26 @@ "NAPI_EXPERIMENTAL" ], }, + { + "target_name": "test_object_vtable", + "defines": [ "NAPI_EXPERIMENTAL", "NODE_API_MODULE_USE_VTABLE" ], + "sources": [ + "test_null.c", + "test_object.c" + ], + }, { "target_name": "test_exceptions", "sources": [ "test_exceptions.c", ] + }, + { + "target_name": "test_exceptions_vtable", + "defines": [ "NODE_API_MODULE_USE_VTABLE" ], + "sources": [ + "test_exceptions.c", + ] } ] } diff --git a/test/js-native-api/test_object/test.js b/test/js-native-api/test_object/test.js index 603f7d765aa703..e9686dfcff53dd 100644 --- a/test/js-native-api/test_object/test.js +++ b/test/js-native-api/test_object/test.js @@ -1,9 +1,11 @@ 'use strict'; -const common = require('../../common'); +// Addons: test_object, test_object_vtable + +const { addonPath } = require('../../common/addon-test'); const assert = require('assert'); // Testing api calls for objects -const test_object = require(`./build/${common.buildType}/test_object`); +const test_object = require(addonPath); const object = { hello: 'world', diff --git a/test/js-native-api/test_object/test_exceptions.js b/test/js-native-api/test_object/test_exceptions.js index 1e45d64368b01c..fe806fd97378d2 100644 --- a/test/js-native-api/test_object/test_exceptions.js +++ b/test/js-native-api/test_object/test_exceptions.js @@ -1,8 +1,11 @@ 'use strict'; +// Addons: test_exceptions, test_exceptions_vtable + const common = require('../../common'); +const { addonPath } = require('../../common/addon-test'); // Test -const { testExceptions } = require(`./build/${common.buildType}/test_exceptions`); +const { testExceptions } = require(addonPath); function throws() { throw new Error('foobar'); diff --git a/test/js-native-api/test_object/test_null.js b/test/js-native-api/test_object/test_null.js index 399952aaeb0724..231bc1395c78ab 100644 --- a/test/js-native-api/test_object/test_null.js +++ b/test/js-native-api/test_object/test_null.js @@ -1,9 +1,11 @@ 'use strict'; -const common = require('../../common'); +// Addons: test_object, test_object_vtable + +const { addonPath } = require('../../common/addon-test'); const assert = require('assert'); -// Test passing NULL to object-related N-APIs. -const { testNull } = require(`./build/${common.buildType}/test_object`); +// Test passing NULL to object-related Node-APIs. +const { testNull } = require(addonPath); const expectedForProperty = { envIsNull: 'Invalid argument', diff --git a/test/js-native-api/test_promise/binding.gyp b/test/js-native-api/test_promise/binding.gyp index de2802f8607dcf..2d7702d1c2d67e 100644 --- a/test/js-native-api/test_promise/binding.gyp +++ b/test/js-native-api/test_promise/binding.gyp @@ -5,6 +5,13 @@ "sources": [ "test_promise.c" ] + }, + { + "target_name": "test_promise_vtable", + "defines": [ "NODE_API_MODULE_USE_VTABLE" ], + "sources": [ + "test_promise.c" + ] } ] } diff --git a/test/js-native-api/test_promise/test.js b/test/js-native-api/test_promise/test.js index 3b43e6132ed4d9..c931fc05ce2b76 100644 --- a/test/js-native-api/test_promise/test.js +++ b/test/js-native-api/test_promise/test.js @@ -1,11 +1,13 @@ 'use strict'; +// Addons: test_promise, test_promise_vtable const common = require('../../common'); +const { addonPath } = require('../../common/addon-test'); -// This tests the promise-related n-api calls +// This tests the promise-related Node-API calls const assert = require('assert'); -const test_promise = require(`./build/${common.buildType}/test_promise`); +const test_promise = require(addonPath); // A resolution { diff --git a/test/js-native-api/test_properties/binding.gyp b/test/js-native-api/test_properties/binding.gyp index ee38504eea75a6..40cd10fe91162b 100644 --- a/test/js-native-api/test_properties/binding.gyp +++ b/test/js-native-api/test_properties/binding.gyp @@ -5,6 +5,13 @@ "sources": [ "test_properties.c" ] + }, + { + "target_name": "test_properties_vtable", + "defines": [ "NODE_API_MODULE_USE_VTABLE" ], + "sources": [ + "test_properties.c" + ] } ] } diff --git a/test/js-native-api/test_properties/test.js b/test/js-native-api/test_properties/test.js index 7311c615cb815f..afc6549700e5a4 100644 --- a/test/js-native-api/test_properties/test.js +++ b/test/js-native-api/test_properties/test.js @@ -1,5 +1,7 @@ 'use strict'; -const common = require('../../common'); +// Addons: test_properties, test_properties_vtable + +const { addonPath } = require('../../common/addon-test'); const assert = require('assert'); const readonlyErrorRE = /^TypeError: Cannot assign to read only property '.*' of object '#'$/; @@ -7,7 +9,7 @@ const getterOnlyErrorRE = /^TypeError: Cannot set property .* of # which has only a getter$/; // Testing api calls for defining properties -const test_object = require(`./build/${common.buildType}/test_properties`); +const test_object = require(addonPath); assert.strictEqual(test_object.echo('hello'), 'hello'); diff --git a/test/js-native-api/test_reference/binding.gyp b/test/js-native-api/test_reference/binding.gyp index a9d81ef9d2c05d..a86d052a7e47c7 100644 --- a/test/js-native-api/test_reference/binding.gyp +++ b/test/js-native-api/test_reference/binding.gyp @@ -6,11 +6,25 @@ "test_reference.c" ] }, + { + "target_name": "test_reference_vtable", + "defines": [ "NODE_API_MODULE_USE_VTABLE" ], + "sources": [ + "test_reference.c" + ] + }, { "target_name": "test_finalizer", "sources": [ "test_finalizer.c" ] + }, + { + "target_name": "test_finalizer_vtable", + "defines": [ "NODE_API_MODULE_USE_VTABLE" ], + "sources": [ + "test_finalizer.c" + ] } ] } diff --git a/test/js-native-api/test_reference/test.js b/test/js-native-api/test_reference/test.js index 6ef84009ea0333..8174f90ea4e7e3 100644 --- a/test/js-native-api/test_reference/test.js +++ b/test/js-native-api/test_reference/test.js @@ -1,11 +1,12 @@ 'use strict'; // Flags: --expose-gc +// Addons: test_reference, test_reference_vtable -const { buildType } = require('../../common'); +const { addonPath } = require('../../common/addon-test'); const { gcUntil } = require('../../common/gc'); const assert = require('assert'); -const test_reference = require(`./build/${buildType}/test_reference`); +const test_reference = require(addonPath); // This test script uses external values with finalizer callbacks // in order to track when values get garbage-collected. Each invocation diff --git a/test/js-native-api/test_reference/test_finalizer.js b/test/js-native-api/test_reference/test_finalizer.js index a5270512dc87c1..ea5178868cadca 100644 --- a/test/js-native-api/test_reference/test_finalizer.js +++ b/test/js-native-api/test_reference/test_finalizer.js @@ -1,8 +1,10 @@ 'use strict'; // Flags: --expose-gc --force-node-api-uncaught-exceptions-policy +// Addons: test_finalizer, test_finalizer_vtable const common = require('../../common'); -const binding = require(`./build/${common.buildType}/test_finalizer`); +const { addonPath } = require('../../common/addon-test'); +const binding = require(addonPath); const assert = require('assert'); process.on('uncaughtException', common.mustCall((err) => { diff --git a/test/js-native-api/test_reference_double_free/binding.gyp b/test/js-native-api/test_reference_double_free/binding.gyp index 2d906dadae6126..255cfbd79fad46 100644 --- a/test/js-native-api/test_reference_double_free/binding.gyp +++ b/test/js-native-api/test_reference_double_free/binding.gyp @@ -5,6 +5,13 @@ "sources": [ "test_reference_double_free.c" ] + }, + { + "target_name": "test_reference_double_free_vtable", + "defines": [ "NODE_API_MODULE_USE_VTABLE" ], + "sources": [ + "test_reference_double_free.c" + ] } ] } diff --git a/test/js-native-api/test_reference_double_free/test.js b/test/js-native-api/test_reference_double_free/test.js index 242d850ba9f677..c3e53970e58a64 100644 --- a/test/js-native-api/test_reference_double_free/test.js +++ b/test/js-native-api/test_reference_double_free/test.js @@ -1,11 +1,11 @@ 'use strict'; +// Addons: test_reference_double_free, test_reference_double_free_vtable // This test makes no assertions. It tests a fix without which it will crash // with a double free. -const { buildType } = require('../../common'); - -const addon = require(`./build/${buildType}/test_reference_double_free`); +const { addonPath } = require('../../common/addon-test'); +const addon = require(addonPath); { new addon.MyObject(true); } { new addon.MyObject(false); } diff --git a/test/js-native-api/test_reference_double_free/test_wrap.js b/test/js-native-api/test_reference_double_free/test_wrap.js index 4f0092111032ca..89178f8dd25e15 100644 --- a/test/js-native-api/test_reference_double_free/test_wrap.js +++ b/test/js-native-api/test_reference_double_free/test_wrap.js @@ -1,10 +1,11 @@ 'use strict'; +// Addons: test_reference_double_free, test_reference_double_free_vtable // This test makes no assertions. It tests that calling napi_remove_wrap and // napi_delete_reference consecutively doesn't crash the process. -const { buildType } = require('../../common'); +const { addonPath } = require('../../common/addon-test'); -const addon = require(`./build/${buildType}/test_reference_double_free`); +const addon = require(addonPath); addon.deleteImmediately({}); diff --git a/test/js-native-api/test_sharedarraybuffer/binding.gyp b/test/js-native-api/test_sharedarraybuffer/binding.gyp index 0020e6b4a9e917..b0ca983321e5b4 100644 --- a/test/js-native-api/test_sharedarraybuffer/binding.gyp +++ b/test/js-native-api/test_sharedarraybuffer/binding.gyp @@ -3,6 +3,11 @@ { "target_name": "test_sharedarraybuffer", "sources": [ "test_sharedarraybuffer.c" ] + }, + { + "target_name": "test_sharedarraybuffer_vtable", + "defines": [ "NODE_API_MODULE_USE_VTABLE" ], + "sources": [ "test_sharedarraybuffer.c" ] } ] } diff --git a/test/js-native-api/test_sharedarraybuffer/test.js b/test/js-native-api/test_sharedarraybuffer/test.js index 9c3066124b7c7b..b0b222d6c32ede 100644 --- a/test/js-native-api/test_sharedarraybuffer/test.js +++ b/test/js-native-api/test_sharedarraybuffer/test.js @@ -1,8 +1,9 @@ 'use strict'; +// Addons: test_sharedarraybuffer, test_sharedarraybuffer_vtable -const common = require('../../common'); +const { addonPath } = require('../../common/addon-test'); const assert = require('assert'); -const test_sharedarraybuffer = require(`./build/${common.buildType}/test_sharedarraybuffer`); +const test_sharedarraybuffer = require(addonPath); { const sab = new SharedArrayBuffer(16); diff --git a/test/js-native-api/test_string/binding.gyp b/test/js-native-api/test_string/binding.gyp index 82a1185c3d9d76..3b186445ee08bb 100644 --- a/test/js-native-api/test_string/binding.gyp +++ b/test/js-native-api/test_string/binding.gyp @@ -2,12 +2,18 @@ "targets": [ { "target_name": "test_string", + "defines": [ "NAPI_VERSION=10" ], "sources": [ "test_string.c", "test_null.c", ], - "defines": [ - "NAPI_VERSION=10", + }, + { + "target_name": "test_string_vtable", + "defines": [ "NAPI_VERSION=10", "NODE_API_MODULE_USE_VTABLE" ], + "sources": [ + "test_string.c", + "test_null.c", ], }, ], diff --git a/test/js-native-api/test_string/test.js b/test/js-native-api/test_string/test.js index 04515a286ce36a..060cefa13d9e1d 100644 --- a/test/js-native-api/test_string/test.js +++ b/test/js-native-api/test_string/test.js @@ -1,9 +1,11 @@ 'use strict'; -const common = require('../../common'); +// Addons: test_string, test_string_vtable + +const { addonPath } = require('../../common/addon-test'); const assert = require('assert'); // Testing api calls for string -const test_string = require(`./build/${common.buildType}/test_string`); +const test_string = require(addonPath); // The insufficient buffer test case allocates a buffer of size 4, including // the null terminator. const kInsufficientIdx = 3; diff --git a/test/js-native-api/test_string/test_null.js b/test/js-native-api/test_string/test_null.js index ad19b4a82b588b..974cde2b93a235 100644 --- a/test/js-native-api/test_string/test_null.js +++ b/test/js-native-api/test_string/test_null.js @@ -1,9 +1,11 @@ 'use strict'; -const common = require('../../common'); +// Addons: test_string, test_string_vtable + +const { addonPath } = require('../../common/addon-test'); const assert = require('assert'); -// Test passing NULL to object-related N-APIs. -const { testNull } = require(`./build/${common.buildType}/test_string`); +// Test passing NULL to object-related Node-APIs. +const { testNull } = require(addonPath); const expectedResult = { envIsNull: 'Invalid argument', diff --git a/test/js-native-api/test_symbol/binding.gyp b/test/js-native-api/test_symbol/binding.gyp index c44a78d042f57c..9b1c3dc970e62a 100644 --- a/test/js-native-api/test_symbol/binding.gyp +++ b/test/js-native-api/test_symbol/binding.gyp @@ -5,6 +5,13 @@ "sources": [ "test_symbol.c" ] + }, + { + "target_name": "test_symbol_vtable", + "defines": [ "NODE_API_MODULE_USE_VTABLE" ], + "sources": [ + "test_symbol.c" + ] } ] } diff --git a/test/js-native-api/test_symbol/test1.js b/test/js-native-api/test_symbol/test1.js index c0a4634c88e898..2c0329be1ddfde 100644 --- a/test/js-native-api/test_symbol/test1.js +++ b/test/js-native-api/test_symbol/test1.js @@ -1,9 +1,11 @@ 'use strict'; -const common = require('../../common'); +// Addons: test_symbol, test_symbol_vtable + +const { addonPath } = require('../../common/addon-test'); const assert = require('assert'); // Testing api calls for symbol -const test_symbol = require(`./build/${common.buildType}/test_symbol`); +const test_symbol = require(addonPath); const sym = test_symbol.New('test'); assert.strictEqual(sym.toString(), 'Symbol(test)'); diff --git a/test/js-native-api/test_symbol/test2.js b/test/js-native-api/test_symbol/test2.js index 642d3623cc51fc..6e71d9a6789612 100644 --- a/test/js-native-api/test_symbol/test2.js +++ b/test/js-native-api/test_symbol/test2.js @@ -1,9 +1,11 @@ 'use strict'; -const common = require('../../common'); +// Addons: test_symbol, test_symbol_vtable + +const { addonPath } = require('../../common/addon-test'); const assert = require('assert'); // Testing api calls for symbol -const test_symbol = require(`./build/${common.buildType}/test_symbol`); +const test_symbol = require(addonPath); const fooSym = test_symbol.New('foo'); assert.strictEqual(fooSym.toString(), 'Symbol(foo)'); diff --git a/test/js-native-api/test_symbol/test3.js b/test/js-native-api/test_symbol/test3.js index 445fa3f891954c..d9408841aaab86 100644 --- a/test/js-native-api/test_symbol/test3.js +++ b/test/js-native-api/test_symbol/test3.js @@ -1,9 +1,11 @@ 'use strict'; -const common = require('../../common'); +// Addons: test_symbol, test_symbol_vtable + +const { addonPath } = require('../../common/addon-test'); const assert = require('assert'); // Testing api calls for symbol -const test_symbol = require(`./build/${common.buildType}/test_symbol`); +const test_symbol = require(addonPath); assert.notStrictEqual(test_symbol.New(), test_symbol.New()); assert.notStrictEqual(test_symbol.New('foo'), test_symbol.New('foo')); diff --git a/test/js-native-api/test_typedarray/binding.gyp b/test/js-native-api/test_typedarray/binding.gyp index d708d2d2493bf6..01d4d01d052faf 100644 --- a/test/js-native-api/test_typedarray/binding.gyp +++ b/test/js-native-api/test_typedarray/binding.gyp @@ -5,6 +5,13 @@ "sources": [ "test_typedarray.c" ] + }, + { + "target_name": "test_typedarray_vtable", + "defines": [ "NODE_API_MODULE_USE_VTABLE" ], + "sources": [ + "test_typedarray.c" + ] } ] } diff --git a/test/js-native-api/test_typedarray/test.js b/test/js-native-api/test_typedarray/test.js index 02a6a2acba816a..99b6b42e640822 100644 --- a/test/js-native-api/test_typedarray/test.js +++ b/test/js-native-api/test_typedarray/test.js @@ -1,9 +1,11 @@ 'use strict'; -const common = require('../../common'); +// Addons: test_typedarray, test_typedarray_vtable + +const { addonPath } = require('../../common/addon-test'); const assert = require('assert'); // Testing api calls for arrays -const test_typedarray = require(`./build/${common.buildType}/test_typedarray`); +const test_typedarray = require(addonPath); const byteArray = new Uint8Array(3); byteArray[0] = 0; diff --git a/test/node-api/1_hello_world/binding.gyp b/test/node-api/1_hello_world/binding.gyp index 62381d5e54f22b..ac03f1a3e061e6 100644 --- a/test/node-api/1_hello_world/binding.gyp +++ b/test/node-api/1_hello_world/binding.gyp @@ -3,6 +3,21 @@ { "target_name": "binding", "sources": [ "binding.c" ] + }, + { + "target_name": "binding_vtable", + "defines": [ "NODE_API_MODULE_USE_VTABLE" ], + "sources": [ "binding.c" ] + }, + { + "target_name": "binding_vtable_nofb", + "defines": [ "NODE_API_MODULE_USE_VTABLE", "NODE_API_MODULE_NO_VTABLE_FALLBACK" ], + "sources": [ "binding.c" ] + }, + { + "target_name": "binding_vtable_noimpl", + "defines": [ "NODE_API_MODULE_USE_VTABLE", "NODE_API_MODULE_NO_VTABLE_IMPL" ], + "sources": [ "binding_vtable.c" ] } ] } diff --git a/test/node-api/1_hello_world/binding_vtable.c b/test/node-api/1_hello_world/binding_vtable.c new file mode 100644 index 00000000000000..46a7f0fba1d50f --- /dev/null +++ b/test/node-api/1_hello_world/binding_vtable.c @@ -0,0 +1,20 @@ +#include +#include +#include "../../js-native-api/common.h" + +// Use vtable-based Node-API directly. + +static napi_value Method(napi_env env, napi_callback_info info) { + napi_value world; + const char* str = "world"; + size_t str_len = strlen(str); + NODE_API_CALL(env, + env->js_vtable->create_string_utf8(env, str, str_len, &world)); + return world; +} + +NAPI_MODULE_INIT() { + napi_property_descriptor desc = DECLARE_NODE_API_PROPERTY("hello", Method); + NODE_API_CALL(env, env->js_vtable->define_properties(env, exports, 1, &desc)); + return exports; +} diff --git a/test/node-api/1_hello_world/test.js b/test/node-api/1_hello_world/test.js index dd28e26a561295..4fea89770d0313 100644 --- a/test/node-api/1_hello_world/test.js +++ b/test/node-api/1_hello_world/test.js @@ -1,9 +1,12 @@ 'use strict'; +// Addons: binding, binding_vtable, binding_vtable_nofb, binding_vtable_noimpl + const common = require('../../common'); +const { addonPath } = require('../../common/addon-test'); const assert = require('assert'); const { Worker } = require('worker_threads'); -const bindingPath = require.resolve(`./build/${common.buildType}/binding`); +const bindingPath = require.resolve(addonPath); const binding = require(bindingPath); assert.strictEqual(binding.hello(), 'world'); console.log('binding.hello() =', binding.hello()); diff --git a/test/node-api/test_async/binding.gyp b/test/node-api/test_async/binding.gyp index d8436e2b1d189f..a29fbbde54c13e 100644 --- a/test/node-api/test_async/binding.gyp +++ b/test/node-api/test_async/binding.gyp @@ -3,6 +3,11 @@ { "target_name": "test_async", "sources": [ "test_async.c" ] + }, + { + "target_name": "test_async_vtable", + "defines": [ "NODE_API_MODULE_USE_VTABLE" ], + "sources": [ "test_async.c" ] } ] } diff --git a/test/node-api/test_async/test-async-hooks.js b/test/node-api/test_async/test-async-hooks.js index 956d210f3f0832..bbff359af8bab0 100644 --- a/test/node-api/test_async/test-async-hooks.js +++ b/test/node-api/test_async/test-async-hooks.js @@ -1,8 +1,11 @@ 'use strict'; +// Addons: test_async, test_async_vtable + const common = require('../../common'); +const { addonPath } = require('../../common/addon-test'); const assert = require('assert'); const async_hooks = require('async_hooks'); -const test_async = require(`./build/${common.buildType}/test_async`); +const test_async = require(addonPath); const events = []; let testId; diff --git a/test/node-api/test_async/test-loop.js b/test/node-api/test_async/test-loop.js index 1b05cbad4a501b..8c3d7169052976 100644 --- a/test/node-api/test_async/test-loop.js +++ b/test/node-api/test_async/test-loop.js @@ -1,7 +1,10 @@ 'use strict'; +// Addons: test_async, test_async_vtable + const common = require('../../common'); +const { addonPath } = require('../../common/addon-test'); const assert = require('assert'); -const test_async = require(`./build/${common.buildType}/test_async`); +const test_async = require(addonPath); const iterations = 500; let x = 0; diff --git a/test/node-api/test_async/test-uncaught.js b/test/node-api/test_async/test-uncaught.js index fdcb3203f54410..8be2592138bd25 100644 --- a/test/node-api/test_async/test-uncaught.js +++ b/test/node-api/test_async/test-uncaught.js @@ -1,7 +1,10 @@ 'use strict'; +// Addons: test_async, test_async_vtable + const common = require('../../common'); +const { addonPath } = require('../../common/addon-test'); const assert = require('assert'); -const test_async = require(`./build/${common.buildType}/test_async`); +const test_async = require(addonPath); process.on('uncaughtException', common.mustCall(function(err) { try { diff --git a/test/node-api/test_async/test.js b/test/node-api/test_async/test.js index 34ecae08e67f2e..24fc80ad675c1b 100644 --- a/test/node-api/test_async/test.js +++ b/test/node-api/test_async/test.js @@ -1,30 +1,31 @@ 'use strict'; +// Addons: test_async, test_async_vtable + const common = require('../../common'); +const { addonPath, isInvokedAsChild, spawnTestSync } = require('../../common/addon-test'); const assert = require('assert'); -const child_process = require('child_process'); -const test_async = require(`./build/${common.buildType}/test_async`); +const test_async = require(addonPath); const testException = 'test_async_cb_exception'; // Exception thrown from async completion callback. // (Tested in a spawned process because the exception is fatal.) -if (process.argv[2] === 'child') { +if (isInvokedAsChild) { test_async.Test(1, {}, common.mustCall(function() { throw new Error(testException); })); - return; -} -const p = child_process.spawnSync( - process.execPath, [ __filename, 'child' ]); -assert.ifError(p.error); -assert.ok(p.stderr.toString().includes(testException)); +} else { + const p = spawnTestSync(); + assert.ifError(p.error); + assert.ok(p.stderr.toString().includes(testException)); -// Successful async execution and completion callback. -test_async.Test(5, {}, common.mustCall(function(err, val) { - assert.strictEqual(err, null); - assert.strictEqual(val, 10); - process.nextTick(common.mustCall()); -})); + // Successful async execution and completion callback. + test_async.Test(5, {}, common.mustCall(function(err, val) { + assert.strictEqual(err, null); + assert.strictEqual(val, 10); + process.nextTick(common.mustCall()); + })); -// Async work item cancellation with callback. -test_async.TestCancel(common.mustCall()); + // Async work item cancellation with callback. + test_async.TestCancel(common.mustCall()); +} diff --git a/test/node-api/test_async_cleanup_hook/binding.gyp b/test/node-api/test_async_cleanup_hook/binding.gyp index 23daf507916ff6..3daf996fa0c9b4 100644 --- a/test/node-api/test_async_cleanup_hook/binding.gyp +++ b/test/node-api/test_async_cleanup_hook/binding.gyp @@ -4,6 +4,11 @@ 'target_name': 'binding', 'defines': [ 'V8_DEPRECATION_WARNINGS=1' ], 'sources': [ 'binding.c' ] + }, + { + 'target_name': 'binding_vtable', + 'defines': [ 'V8_DEPRECATION_WARNINGS=1', 'NODE_API_MODULE_USE_VTABLE' ], + 'sources': [ 'binding.c' ] } ] } diff --git a/test/node-api/test_async_cleanup_hook/test.js b/test/node-api/test_async_cleanup_hook/test.js index 55cc2517a59bc8..a36f23feea263b 100644 --- a/test/node-api/test_async_cleanup_hook/test.js +++ b/test/node-api/test_async_cleanup_hook/test.js @@ -1,8 +1,11 @@ 'use strict'; +// Addons: binding, binding_vtable + const common = require('../../common'); +const { addonPath } = require('../../common/addon-test'); const path = require('path'); const { Worker } = require('worker_threads'); -const binding = path.resolve(__dirname, `./build/${common.buildType}/binding`); +const binding = path.resolve(__dirname, addonPath); const w = new Worker(`require(${JSON.stringify(binding)})`, { eval: true }); w.on('exit', common.mustCall(() => require(binding))); diff --git a/test/node-api/test_async_context/binding.gyp b/test/node-api/test_async_context/binding.gyp index 23daf507916ff6..3daf996fa0c9b4 100644 --- a/test/node-api/test_async_context/binding.gyp +++ b/test/node-api/test_async_context/binding.gyp @@ -4,6 +4,11 @@ 'target_name': 'binding', 'defines': [ 'V8_DEPRECATION_WARNINGS=1' ], 'sources': [ 'binding.c' ] + }, + { + 'target_name': 'binding_vtable', + 'defines': [ 'V8_DEPRECATION_WARNINGS=1', 'NODE_API_MODULE_USE_VTABLE' ], + 'sources': [ 'binding.c' ] } ] } diff --git a/test/node-api/test_async_context/test-gcable-callback.js b/test/node-api/test_async_context/test-gcable-callback.js index b1e4d29c5f41e5..1ed7958c5c4e6c 100644 --- a/test/node-api/test_async_context/test-gcable-callback.js +++ b/test/node-api/test_async_context/test-gcable-callback.js @@ -1,14 +1,16 @@ 'use strict'; // Flags: --gc-interval=100 --gc-global +// Addons: binding, binding_vtable const common = require('../../common'); +const { addonPath } = require('../../common/addon-test'); const assert = require('assert'); const async_hooks = require('async_hooks'); const { createAsyncResource, destroyAsyncResource, makeCallback, -} = require(`./build/${common.buildType}/binding`); +} = require(addonPath); // Test for https://github.com/nodejs/node/issues/27218: // napi_async_destroy() can be called during a regular garbage collection run. diff --git a/test/node-api/test_async_context/test.js b/test/node-api/test_async_context/test.js index e4e46afbf2eb83..fbc307acb1169a 100644 --- a/test/node-api/test_async_context/test.js +++ b/test/node-api/test_async_context/test.js @@ -1,14 +1,16 @@ 'use strict'; // Flags: --gc-interval=100 --gc-global +// Addons: binding, binding_vtable const common = require('../../common'); +const { addonPath } = require('../../common/addon-test'); const assert = require('assert'); const async_hooks = require('async_hooks'); const { makeCallback, createAsyncResource, destroyAsyncResource, -} = require(`./build/${common.buildType}/binding`); +} = require(addonPath); const hook_result = { id: null, diff --git a/test/node-api/test_buffer/binding.gyp b/test/node-api/test_buffer/binding.gyp index 0a1dc92de7ffb4..19a768de49a663 100644 --- a/test/node-api/test_buffer/binding.gyp +++ b/test/node-api/test_buffer/binding.gyp @@ -7,9 +7,22 @@ ], "sources": [ "test_buffer.c" ] }, + { + "target_name": "test_buffer_vtable", + "defines": [ + 'NAPI_VERSION=10', + 'NODE_API_MODULE_USE_VTABLE' + ], + "sources": [ "test_buffer.c" ] + }, { "target_name": "test_finalizer", "sources": [ "test_finalizer.c" ] + }, + { + "target_name": "test_finalizer_vtable", + "defines": [ 'NODE_API_MODULE_USE_VTABLE' ], + "sources": [ "test_finalizer.c" ] } ] } diff --git a/test/node-api/test_buffer/test-external-buffer.js b/test/node-api/test_buffer/test-external-buffer.js index 4f220f1d21e9ce..c7409bb4508d08 100644 --- a/test/node-api/test_buffer/test-external-buffer.js +++ b/test/node-api/test_buffer/test-external-buffer.js @@ -1,7 +1,8 @@ 'use strict'; +// Addons: test_buffer, test_buffer_vtable -const common = require('../../common'); -const binding = require(`./build/${common.buildType}/test_buffer`); +const { addonPath } = require('../../common/addon-test'); +const binding = require(addonPath); const assert = require('assert'); // Regression test for https://github.com/nodejs/node/issues/31134 diff --git a/test/node-api/test_buffer/test.js b/test/node-api/test_buffer/test.js index c51eddabc183e6..55e5b54a4f2fe7 100644 --- a/test/node-api/test_buffer/test.js +++ b/test/node-api/test_buffer/test.js @@ -1,8 +1,10 @@ 'use strict'; // Flags: --expose-gc --no-concurrent-array-buffer-sweeping +// Addons: test_buffer, test_buffer_vtable const common = require('../../common'); -const binding = require(`./build/${common.buildType}/test_buffer`); +const { addonPath } = require('../../common/addon-test'); +const binding = require(addonPath); const assert = require('assert'); const tick = require('util').promisify(require('../../common/tick')); diff --git a/test/node-api/test_buffer/test_finalizer.js b/test/node-api/test_buffer/test_finalizer.js index 35511fcb016c95..6e34dc433705f9 100644 --- a/test/node-api/test_buffer/test_finalizer.js +++ b/test/node-api/test_buffer/test_finalizer.js @@ -1,8 +1,10 @@ 'use strict'; // Flags: --expose-gc --force-node-api-uncaught-exceptions-policy +// Addons: test_finalizer, test_finalizer_vtable const common = require('../../common'); -const binding = require(`./build/${common.buildType}/test_finalizer`); +const { addonPath } = require('../../common/addon-test'); +const binding = require(addonPath); const assert = require('assert'); const tick = require('util').promisify(require('../../common/tick')); diff --git a/test/node-api/test_callback_scope/binding.gyp b/test/node-api/test_callback_scope/binding.gyp index 23daf507916ff6..3daf996fa0c9b4 100644 --- a/test/node-api/test_callback_scope/binding.gyp +++ b/test/node-api/test_callback_scope/binding.gyp @@ -4,6 +4,11 @@ 'target_name': 'binding', 'defines': [ 'V8_DEPRECATION_WARNINGS=1' ], 'sources': [ 'binding.c' ] + }, + { + 'target_name': 'binding_vtable', + 'defines': [ 'V8_DEPRECATION_WARNINGS=1', 'NODE_API_MODULE_USE_VTABLE' ], + 'sources': [ 'binding.c' ] } ] } diff --git a/test/node-api/test_callback_scope/test-async-hooks.js b/test/node-api/test_callback_scope/test-async-hooks.js index bb5927236f6765..d94059dca6b0ee 100644 --- a/test/node-api/test_callback_scope/test-async-hooks.js +++ b/test/node-api/test_callback_scope/test-async-hooks.js @@ -1,16 +1,18 @@ 'use strict'; +// Addons: binding, binding_vtable const common = require('../../common'); +const { addonPath } = require('../../common/addon-test'); const assert = require('assert'); const async_hooks = require('async_hooks'); // The async_hook that we enable would register the process.emitWarning() -// call from loading the N-API addon as asynchronous activity because +// call from loading the Node-API addon as asynchronous activity because // it contains a process.nextTick() call. Monkey patch it to be a no-op // before we load the addon in order to avoid this. process.emitWarning = () => {}; -const { runInCallbackScope } = require(`./build/${common.buildType}/binding`); +const { runInCallbackScope } = require(addonPath); const expectedResource = {}; const expectedResourceType = 'test-resource'; diff --git a/test/node-api/test_callback_scope/test-resolve-async.js b/test/node-api/test_callback_scope/test-resolve-async.js index 77f25c9dde533f..3fa6e089e40eb3 100644 --- a/test/node-api/test_callback_scope/test-resolve-async.js +++ b/test/node-api/test_callback_scope/test-resolve-async.js @@ -1,6 +1,8 @@ 'use strict'; +// Addons: binding, binding_vtable const common = require('../../common'); -const { testResolveAsync } = require(`./build/${common.buildType}/binding`); +const { addonPath } = require('../../common/addon-test'); +const { testResolveAsync } = require(addonPath); testResolveAsync().then(common.mustCall()); diff --git a/test/node-api/test_callback_scope/test.js b/test/node-api/test_callback_scope/test.js index abf9e41b8f5028..bb846292d384b0 100644 --- a/test/node-api/test_callback_scope/test.js +++ b/test/node-api/test_callback_scope/test.js @@ -1,8 +1,10 @@ 'use strict'; +// Addons: binding, binding_vtable const common = require('../../common'); +const { addonPath } = require('../../common/addon-test'); const assert = require('assert'); -const { runInCallbackScope } = require(`./build/${common.buildType}/binding`); +const { runInCallbackScope } = require(addonPath); assert.strictEqual(runInCallbackScope({}, 'test-resource', () => 42), 42); diff --git a/test/node-api/test_cleanup_hook/binding.gyp b/test/node-api/test_cleanup_hook/binding.gyp index 23daf507916ff6..3daf996fa0c9b4 100644 --- a/test/node-api/test_cleanup_hook/binding.gyp +++ b/test/node-api/test_cleanup_hook/binding.gyp @@ -4,6 +4,11 @@ 'target_name': 'binding', 'defines': [ 'V8_DEPRECATION_WARNINGS=1' ], 'sources': [ 'binding.c' ] + }, + { + 'target_name': 'binding_vtable', + 'defines': [ 'V8_DEPRECATION_WARNINGS=1', 'NODE_API_MODULE_USE_VTABLE' ], + 'sources': [ 'binding.c' ] } ] } diff --git a/test/node-api/test_cleanup_hook/test.js b/test/node-api/test_cleanup_hook/test.js index 300551ce678be1..c39951883885e5 100644 --- a/test/node-api/test_cleanup_hook/test.js +++ b/test/node-api/test_cleanup_hook/test.js @@ -1,13 +1,13 @@ 'use strict'; -const common = require('../../common'); +// Addons: binding, binding_vtable + +const { addonPath, isInvokedAsChild, spawnTestSync } = require('../../common/addon-test'); const assert = require('assert'); -const child_process = require('child_process'); -if (process.argv[2] === 'child') { - require(`./build/${common.buildType}/binding`); +if (isInvokedAsChild) { + require(addonPath); } else { - const { stdout, status, signal } = - child_process.spawnSync(process.execPath, [__filename, 'child']); + const { stdout, status, signal } = spawnTestSync(); assert.strictEqual(status, 0, `process exited with status(${status}) and signal(${signal})`); assert.strictEqual(stdout.toString().trim(), 'cleanup(42)'); } diff --git a/test/node-api/test_env_teardown_gc/binding.gyp b/test/node-api/test_env_teardown_gc/binding.gyp index 413621ade335a1..5d0e870583aad4 100644 --- a/test/node-api/test_env_teardown_gc/binding.gyp +++ b/test/node-api/test_env_teardown_gc/binding.gyp @@ -3,6 +3,11 @@ { 'target_name': 'binding', 'sources': [ 'binding.c' ] + }, + { + 'target_name': 'binding_vtable', + 'defines': [ 'NODE_API_MODULE_USE_VTABLE' ], + 'sources': [ 'binding.c' ] } ] } diff --git a/test/node-api/test_env_teardown_gc/test.js b/test/node-api/test_env_teardown_gc/test.js index 8f1c4f4038fbab..049cf8ff23391c 100644 --- a/test/node-api/test_env_teardown_gc/test.js +++ b/test/node-api/test_env_teardown_gc/test.js @@ -1,10 +1,11 @@ 'use strict'; // Flags: --expose-gc +// Addons: binding, binding_vtable process.env.NODE_TEST_KNOWN_GLOBALS = 0; -const common = require('../../common'); -const binding = require(`./build/${common.buildType}/binding`); +const { addonPath } = require('../../common/addon-test'); +const binding = require(addonPath); global.it = new binding.MyObject(); diff --git a/test/node-api/test_exception/binding.gyp b/test/node-api/test_exception/binding.gyp index d2e4586e46bc1e..1c66ddda20d8af 100644 --- a/test/node-api/test_exception/binding.gyp +++ b/test/node-api/test_exception/binding.gyp @@ -3,6 +3,11 @@ { "target_name": "test_exception", "sources": [ "test_exception.c" ] + }, + { + "target_name": "test_exception_vtable", + "defines": [ "NODE_API_MODULE_USE_VTABLE" ], + "sources": [ "test_exception.c" ] } ] } diff --git a/test/node-api/test_exception/test.js b/test/node-api/test_exception/test.js index 8a2ffdcd27d2d3..40cfed556b50d3 100644 --- a/test/node-api/test_exception/test.js +++ b/test/node-api/test_exception/test.js @@ -1,9 +1,10 @@ 'use strict'; // Flags: --expose-gc +// Addons: test_exception, test_exception_vtable -const common = require('../../common'); const assert = require('assert'); -const test_exception = require(`./build/${common.buildType}/test_exception`); +const { addonPath } = require('../../common/addon-test'); +const test_exception = require(addonPath); // Make sure that exceptions that occur during finalization are propagated. function testFinalize(binding) { diff --git a/test/node-api/test_fatal/binding.gyp b/test/node-api/test_fatal/binding.gyp index ad661825f1fa9b..9563f130eba864 100644 --- a/test/node-api/test_fatal/binding.gyp +++ b/test/node-api/test_fatal/binding.gyp @@ -3,6 +3,11 @@ { "target_name": "test_fatal", "sources": [ "test_fatal.c" ] + }, + { + "target_name": "test_fatal_vtable", + "defines": [ "NODE_API_MODULE_USE_VTABLE" ], + "sources": [ "test_fatal.c" ] } ] } diff --git a/test/node-api/test_fatal/test.js b/test/node-api/test_fatal/test.js index c4af828ff154d1..186583f747907c 100644 --- a/test/node-api/test_fatal/test.js +++ b/test/node-api/test_fatal/test.js @@ -1,19 +1,18 @@ 'use strict'; -const common = require('../../common'); +// Addons: test_fatal, test_fatal_vtable + +const { addonPath, isInvokedAsChild, spawnTestSync } = require('../../common/addon-test'); const assert = require('assert'); -const child_process = require('child_process'); -const test_fatal = require(`./build/${common.buildType}/test_fatal`); +const test_fatal = require(addonPath); // Test in a child process because the test code will trigger a fatal error // that crashes the process. -if (process.argv[2] === 'child') { +if (isInvokedAsChild) { test_fatal.Test(); - return; +} else { + const p = spawnTestSync(); + assert.ifError(p.error); + assert.ok(p.stderr.toString().includes( + 'FATAL ERROR: test_fatal::Test fatal message')); + assert.ok(p.status === 134 || p.signal === 'SIGABRT'); } - -const p = child_process.spawnSync( - process.execPath, [ __filename, 'child' ]); -assert.ifError(p.error); -assert.ok(p.stderr.toString().includes( - 'FATAL ERROR: test_fatal::Test fatal message')); -assert.ok(p.status === 134 || p.signal === 'SIGABRT'); diff --git a/test/node-api/test_fatal/test2.js b/test/node-api/test_fatal/test2.js index 6449f098458d76..1496f41167f4e0 100644 --- a/test/node-api/test_fatal/test2.js +++ b/test/node-api/test_fatal/test2.js @@ -1,19 +1,18 @@ 'use strict'; -const common = require('../../common'); +// Addons: test_fatal, test_fatal_vtable + +const { addonPath, isInvokedAsChild, spawnTestSync } = require('../../common/addon-test'); const assert = require('assert'); -const child_process = require('child_process'); -const test_fatal = require(`./build/${common.buildType}/test_fatal`); +const test_fatal = require(addonPath); // Test in a child process because the test code will trigger a fatal error // that crashes the process. -if (process.argv[2] === 'child') { +if (isInvokedAsChild) { test_fatal.TestStringLength(); - return; +} else { + const p = spawnTestSync(['--napi-modules']); + assert.ifError(p.error); + assert.ok(p.stderr.toString().includes( + 'FATAL ERROR: test_fatal::Test fatal message')); + assert.ok(p.status === 134 || p.signal === 'SIGABRT'); } - -const p = child_process.spawnSync( - process.execPath, [ '--napi-modules', __filename, 'child' ]); -assert.ifError(p.error); -assert.ok(p.stderr.toString().includes( - 'FATAL ERROR: test_fatal::Test fatal message')); -assert.ok(p.status === 134 || p.signal === 'SIGABRT'); diff --git a/test/node-api/test_fatal/test_threads.js b/test/node-api/test_fatal/test_threads.js index 4a4f867613a3b5..55cf59d35efefa 100644 --- a/test/node-api/test_fatal/test_threads.js +++ b/test/node-api/test_fatal/test_threads.js @@ -1,21 +1,22 @@ 'use strict'; +// Addons: test_fatal, test_fatal_vtable + const common = require('../../common'); +const { addonPath, isInvokedAsChild, spawnTestSync } = require('../../common/addon-test'); const assert = require('assert'); -const child_process = require('child_process'); -const test_fatal = require(`./build/${common.buildType}/test_fatal`); +const test_fatal = require(addonPath); // Test in a child process because the test code will trigger a fatal error // that crashes the process. -if (process.argv[2] === 'child') { +if (isInvokedAsChild) { test_fatal.TestThread(); while (true) { // Busy loop to allow the work thread to abort. } +} else { + const p = spawnTestSync(); + assert.ifError(p.error); + assert.ok(p.stderr.toString().includes( + 'FATAL ERROR: work_thread foobar')); + assert(common.nodeProcessAborted(p.status, p.signal)); } - -const p = child_process.spawnSync( - process.execPath, [ __filename, 'child' ]); -assert.ifError(p.error); -assert.ok(p.stderr.toString().includes( - 'FATAL ERROR: work_thread foobar')); -assert(common.nodeProcessAborted(p.status, p.signal)); diff --git a/test/node-api/test_fatal/test_threads_report.js b/test/node-api/test_fatal/test_threads_report.js index c2e635007db750..e8d6344a3582c6 100644 --- a/test/node-api/test_fatal/test_threads_report.js +++ b/test/node-api/test_fatal/test_threads_report.js @@ -1,11 +1,13 @@ 'use strict'; +// Addons: test_fatal, test_fatal_vtable + const common = require('../../common'); +const { addonPath, isInvokedAsChild, spawnTestSync } = require('../../common/addon-test'); const helper = require('../../common/report.js'); const tmpdir = require('../../common/tmpdir'); const assert = require('assert'); -const child_process = require('child_process'); -const test_fatal = require(`./build/${common.buildType}/test_fatal`); +const test_fatal = require(addonPath); if (common.buildType === 'Debug') common.skip('as this will currently fail with a Debug check ' + @@ -13,24 +15,22 @@ if (common.buildType === 'Debug') // Test in a child process because the test code will trigger a fatal error // that crashes the process. -if (process.argv[2] === 'child') { +if (isInvokedAsChild) { test_fatal.TestThread(); - // Busy loop to allow the work thread to abort. - while (true); -} + while (true) { + // Busy loop to allow the work thread to abort. + } +} else { + tmpdir.refresh(); + const p = spawnTestSync(['--report-on-fatalerror'], { cwd: tmpdir.path }); + assert.ifError(p.error); + assert.ok(p.stderr.toString().includes( + 'FATAL ERROR: work_thread foobar')); + assert.ok(p.status === 134 || p.signal === 'SIGABRT'); -tmpdir.refresh(); -const p = child_process.spawnSync( - process.execPath, - [ '--report-on-fatalerror', __filename, 'child' ], - { cwd: tmpdir.path }); -assert.ifError(p.error); -assert.ok(p.stderr.toString().includes( - 'FATAL ERROR: work_thread foobar')); -assert.ok(p.status === 134 || p.signal === 'SIGABRT'); + const reports = helper.findReports(p.pid, tmpdir.path); + assert.strictEqual(reports.length, 1); -const reports = helper.findReports(p.pid, tmpdir.path); -assert.strictEqual(reports.length, 1); - -const report = reports[0]; -helper.validate(report); + const report = reports[0]; + helper.validate(report); +} diff --git a/test/node-api/test_fatal_exception/binding.gyp b/test/node-api/test_fatal_exception/binding.gyp index f4dc0a71ea2817..2a9721e954ef35 100644 --- a/test/node-api/test_fatal_exception/binding.gyp +++ b/test/node-api/test_fatal_exception/binding.gyp @@ -3,6 +3,11 @@ { "target_name": "test_fatal_exception", "sources": [ "test_fatal_exception.c" ] + }, + { + "target_name": "test_fatal_exception_vtable", + "defines": [ "NODE_API_MODULE_USE_VTABLE" ], + "sources": [ "test_fatal_exception.c" ] } ] } diff --git a/test/node-api/test_fatal_exception/test.js b/test/node-api/test_fatal_exception/test.js index f02b9bce1e8169..8358c791cdefa9 100644 --- a/test/node-api/test_fatal_exception/test.js +++ b/test/node-api/test_fatal_exception/test.js @@ -1,7 +1,10 @@ 'use strict'; +// Addons: test_fatal_exception, test_fatal_exception_vtable + const common = require('../../common'); +const { addonPath } = require('../../common/addon-test'); const assert = require('assert'); -const test_fatal = require(`./build/${common.buildType}/test_fatal_exception`); +const test_fatal = require(addonPath); process.on('uncaughtException', common.mustCall(function(err) { assert.strictEqual(err.message, 'fatal error'); diff --git a/test/node-api/test_general/binding.gyp b/test/node-api/test_general/binding.gyp index f8ef9f59613355..85a7bd2c463eda 100644 --- a/test/node-api/test_general/binding.gyp +++ b/test/node-api/test_general/binding.gyp @@ -3,6 +3,11 @@ { "target_name": "test_general", "sources": [ "test_general.c" ] + }, + { + "target_name": "test_general_vtable", + "defines": [ "NODE_API_MODULE_USE_VTABLE" ], + "sources": [ "test_general.c" ] } ] } diff --git a/test/node-api/test_general/test.js b/test/node-api/test_general/test.js index c7dd70f2da5f17..cac9f5300a5475 100644 --- a/test/node-api/test_general/test.js +++ b/test/node-api/test_general/test.js @@ -1,12 +1,13 @@ 'use strict'; +// Addons: test_general, test_general_vtable -const common = require('../../common'); +const { addonPath } = require('../../common/addon-test'); const tmpdir = require('../../common/tmpdir'); const child_process = require('child_process'); const fs = require('fs'); const path = require('path'); const url = require('url'); -const filename = require.resolve(`./build/${common.buildType}/test_general`); +const filename = require.resolve(addonPath); const test_general = require(filename); const assert = require('assert'); diff --git a/test/node-api/test_init_order/binding.gyp b/test/node-api/test_init_order/binding.gyp index 2ffb81838d1189..d685d621bc994f 100644 --- a/test/node-api/test_init_order/binding.gyp +++ b/test/node-api/test_init_order/binding.gyp @@ -3,6 +3,11 @@ { "target_name": "test_init_order", "sources": [ "test_init_order.cc" ] + }, + { + "target_name": "test_init_order_vtable", + "defines": [ "NODE_API_MODULE_USE_VTABLE" ], + "sources": [ "test_init_order.cc" ] } ] } diff --git a/test/node-api/test_init_order/test.js b/test/node-api/test_init_order/test.js index 0bfeb8f94afebb..7957043dc9fb02 100644 --- a/test/node-api/test_init_order/test.js +++ b/test/node-api/test_init_order/test.js @@ -1,9 +1,11 @@ 'use strict'; +// Addons: test_init_order, test_init_order_vtable // This test verifies that C++ static variable dynamic initialization is called // correctly and does not interfere with the module initialization. -const common = require('../../common'); -const test_init_order = require(`./build/${common.buildType}/test_init_order`); + +const { addonPath } = require('../../common/addon-test'); +const test_init_order = require(addonPath); const assert = require('assert'); assert.strictEqual(test_init_order.cppIntValue, 42); diff --git a/test/node-api/test_instance_data/binding.gyp b/test/node-api/test_instance_data/binding.gyp index eb20531269e0f8..30d843e1deb05c 100644 --- a/test/node-api/test_instance_data/binding.gyp +++ b/test/node-api/test_instance_data/binding.gyp @@ -6,6 +6,13 @@ "test_instance_data.c" ] }, + { + "target_name": "test_instance_data_vtable", + "defines": [ "NODE_API_MODULE_USE_VTABLE" ], + "sources": [ + "test_instance_data.c" + ] + }, { "target_name": "test_set_then_ref", "sources": [ @@ -13,6 +20,14 @@ "test_set_then_ref.c", ] }, + { + "target_name": "test_set_then_ref_vtable", + "defines": [ "NODE_API_MODULE_USE_VTABLE" ], + "sources": [ + "addon.c", + "test_set_then_ref.c", + ] + }, { "target_name": "test_ref_then_set", "sources": [ @@ -20,5 +35,13 @@ "test_ref_then_set.c", ] }, + { + "target_name": "test_ref_then_set_vtable", + "defines": [ "NODE_API_MODULE_USE_VTABLE" ], + "sources": [ + "addon.c", + "test_ref_then_set.c", + ] + }, ] } diff --git a/test/node-api/test_instance_data/test.js b/test/node-api/test_instance_data/test.js index 2c78f7fd5540bf..bde11e1d1a2596 100644 --- a/test/node-api/test_instance_data/test.js +++ b/test/node-api/test_instance_data/test.js @@ -1,12 +1,14 @@ 'use strict'; +// Addons: test_instance_data, test_instance_data_vtable + // Test API calls for instance data. const common = require('../../common'); +const { addonPath, isInvokedAsChild, spawnTestSync } = require('../../common/addon-test'); +const assert = require('assert'); -if (module !== require.main) { - // When required as a module, run the tests. - const test_instance_data = - require(`./build/${common.buildType}/test_instance_data`); +if (isInvokedAsChild) { + const test_instance_data = require(addonPath); // Test that instance data can be used in an async work callback. new Promise((resolve) => test_instance_data.asyncWorkCallback(resolve)) @@ -25,27 +27,17 @@ if (module !== require.main) { } else { // When launched as a script, run tests in either a child process or in a // worker thread. - const assert = require('assert'); - const requireAs = require('../../common/require-as'); - const runOptions = { stdio: ['inherit', 'pipe', 'inherit'] }; - const { spawnSync } = require('child_process'); + function checkOutput(child) { + assert.strictEqual(child.status, 0, + `status=${child.status} ` + + `signal=${child.signal} ` + + `stderr=${child.stderr?.toString()} ` + + `stdout=${child.stdout?.toString()}`); + }; // Run tests in a child process. - requireAs(__filename, ['--expose-gc'], runOptions, 'child'); + checkOutput(spawnTestSync(['--expose-gc'])); // Run tests in a worker thread in a child process. - requireAs(__filename, ['--expose-gc'], runOptions, 'worker'); - - function testProcessExit(addonName) { - // Make sure that process exit is clean when the instance data has - // references to JS objects. - const path = require.resolve(`./build/${common.buildType}/${addonName}`); - const child = spawnSync(process.execPath, ['-e', `require(${JSON.stringify(path)});`]); - assert.strictEqual(child.signal, null); - assert.strictEqual(child.status, 0); - assert.strictEqual(child.stderr.toString(), 'addon_free'); - } - - testProcessExit('test_ref_then_set'); - testProcessExit('test_set_then_ref'); + checkOutput(spawnTestSync(['--expose-gc'], { useWorkerThread: true })); } diff --git a/test/node-api/test_instance_data/test_process_exit.js b/test/node-api/test_instance_data/test_process_exit.js new file mode 100644 index 00000000000000..623e8e098fd0e1 --- /dev/null +++ b/test/node-api/test_instance_data/test_process_exit.js @@ -0,0 +1,17 @@ +'use strict'; +// Addons: test_ref_then_set, test_ref_then_set_vtable +// Addons: test_set_then_ref, test_set_then_ref_vtable + +const { addonPath, isInvokedAsChild, spawnTestSync } = require('../../common/addon-test'); +const assert = require('assert'); + +if (isInvokedAsChild) { + require(addonPath); +} else { + // Make sure that process exit is clean when the instance data has + // references to JS objects. + const child = spawnTestSync(); + assert.strictEqual(child.signal, null); + assert.strictEqual(child.status, 0); + assert.strictEqual(child.stderr.toString(), 'addon_free'); +} diff --git a/test/node-api/test_make_callback/binding.gyp b/test/node-api/test_make_callback/binding.gyp index 23daf507916ff6..3daf996fa0c9b4 100644 --- a/test/node-api/test_make_callback/binding.gyp +++ b/test/node-api/test_make_callback/binding.gyp @@ -4,6 +4,11 @@ 'target_name': 'binding', 'defines': [ 'V8_DEPRECATION_WARNINGS=1' ], 'sources': [ 'binding.c' ] + }, + { + 'target_name': 'binding_vtable', + 'defines': [ 'V8_DEPRECATION_WARNINGS=1', 'NODE_API_MODULE_USE_VTABLE' ], + 'sources': [ 'binding.c' ] } ] } diff --git a/test/node-api/test_make_callback/test-async-hooks.js b/test/node-api/test_make_callback/test-async-hooks.js index d6053c5058a1b0..a1a9698c561dbf 100644 --- a/test/node-api/test_make_callback/test-async-hooks.js +++ b/test/node-api/test_make_callback/test-async-hooks.js @@ -1,9 +1,11 @@ 'use strict'; +// Addons: binding, binding_vtable const common = require('../../common'); +const { addonPath } = require('../../common/addon-test'); const assert = require('assert'); const async_hooks = require('async_hooks'); -const binding = require(`./build/${common.buildType}/binding`); +const binding = require(addonPath); const makeCallback = binding.makeCallback; // Check async hooks integration using async context. diff --git a/test/node-api/test_make_callback/test.js b/test/node-api/test_make_callback/test.js index d0b4b22500c9f6..60d7a50cab477a 100644 --- a/test/node-api/test_make_callback/test.js +++ b/test/node-api/test_make_callback/test.js @@ -1,9 +1,11 @@ 'use strict'; +// Addons: binding, binding_vtable const common = require('../../common'); +const { addonPath } = require('../../common/addon-test'); const assert = require('assert'); const vm = require('vm'); -const binding = require(`./build/${common.buildType}/binding`); +const binding = require(addonPath); const makeCallback = binding.makeCallback; function myMultiArgFunc(arg1, arg2, arg3) { @@ -61,19 +63,19 @@ assert.strictEqual(makeCallback(resource, this, // Check that the callback is made in the context of the receiver. const target = vm.runInNewContext(` - (function($Object) { - if (Object === $Object) - throw new Error('bad'); - return Object; - }) + (function($Object) { + if (Object === $Object) + throw new Error('bad'); + return Object; + }) `); assert.notStrictEqual(makeCallback(resource, process, target, Object), Object); // Runs in inner context. const forward = vm.runInNewContext(` - (function(forward) { - return forward(Object); - }) + (function(forward) { + return forward(Object); + }) `); // Runs in outer context. diff --git a/test/node-api/test_make_callback_recurse/binding.gyp b/test/node-api/test_make_callback_recurse/binding.gyp index 23daf507916ff6..b136a7ec31aadf 100644 --- a/test/node-api/test_make_callback_recurse/binding.gyp +++ b/test/node-api/test_make_callback_recurse/binding.gyp @@ -4,6 +4,14 @@ 'target_name': 'binding', 'defines': [ 'V8_DEPRECATION_WARNINGS=1' ], 'sources': [ 'binding.c' ] + }, + { + 'target_name': 'binding_vtable', + 'defines': [ + 'V8_DEPRECATION_WARNINGS=1', + 'NODE_API_MODULE_USE_VTABLE' + ], + 'sources': [ 'binding.c' ] } ] } diff --git a/test/node-api/test_make_callback_recurse/test.js b/test/node-api/test_make_callback_recurse/test.js index 6a35f970e34eae..a18e5a0d1d3033 100644 --- a/test/node-api/test_make_callback_recurse/test.js +++ b/test/node-api/test_make_callback_recurse/test.js @@ -1,9 +1,11 @@ 'use strict'; +// Addons: binding, binding_vtable const common = require('../../common'); +const { addonPath } = require('../../common/addon-test'); const assert = require('assert'); const domain = require('domain'); -const binding = require(`./build/${common.buildType}/binding`); +const binding = require(addonPath); const makeCallback = binding.makeCallback; // Make sure this is run in the future. diff --git a/test/node-api/test_reference_by_node_api_version/binding.gyp b/test/node-api/test_reference_by_node_api_version/binding.gyp index 4987828ffb3d86..216b33ff2ba668 100644 --- a/test/node-api/test_reference_by_node_api_version/binding.gyp +++ b/test/node-api/test_reference_by_node_api_version/binding.gyp @@ -5,10 +5,26 @@ "sources": [ "test_reference_by_node_api_version.c" ], "defines": [ "NAPI_VERSION=10" ], }, + { + "target_name": "test_reference_all_types_vtable", + "sources": [ "test_reference_by_node_api_version.c" ], + "defines": [ + "NAPI_VERSION=10", + "NODE_API_MODULE_USE_VTABLE", + ], + }, { "target_name": "test_reference_obj_only", "sources": [ "test_reference_by_node_api_version.c" ], "defines": [ "NAPI_VERSION=9" ], + }, + { + "target_name": "test_reference_obj_only_vtable", + "sources": [ "test_reference_by_node_api_version.c" ], + "defines": [ + "NAPI_VERSION=9", + "NODE_API_MODULE_USE_VTABLE", + ], } ] } diff --git a/test/node-api/test_reference_by_node_api_version/test.js b/test/node-api/test_reference_by_node_api_version/test.js index a1172b492a73dc..47ba4d0acf9fa9 100644 --- a/test/node-api/test_reference_by_node_api_version/test.js +++ b/test/node-api/test_reference_by_node_api_version/test.js @@ -1,17 +1,19 @@ 'use strict'; // Flags: --expose-gc -// +// Addons: test_reference_all_types, test_reference_all_types_vtable +// Addons: test_reference_obj_only, test_reference_obj_only_vtable + // Testing API calls for Node-API references. // We compare their behavior between Node-API version 8 and later. // In version 8 references can be created only for object, function, // and symbol types, while in newer versions they can be created for // any value type. -// -const { mustCall, buildType } = require('../../common'); + +const { mustCall } = require('../../common'); +const { addonPath } = require('../../common/addon-test'); const { gcUntil } = require('../../common/gc'); const assert = require('assert'); -const addon_v8 = require(`./build/${buildType}/test_reference_obj_only`); -const addon_new = require(`./build/${buildType}/test_reference_all_types`); +const addon = require(addonPath); async function runTests(addon, isVersion8, isLocalSymbol) { let allEntries = []; @@ -116,10 +118,8 @@ async function runTests(addon, isVersion8, isLocalSymbol) { } async function runAllTests() { - await runTests(addon_v8, /* isVersion8 */ true, /* isLocalSymbol */ true); - await runTests(addon_v8, /* isVersion8 */ true, /* isLocalSymbol */ false); - await runTests(addon_new, /* isVersion8 */ false, /* isLocalSymbol */ true); - await runTests(addon_new, /* isVersion8 */ false, /* isLocalSymbol */ false); + await runTests(addon, addon.isVersion8, /* isLocalSymbol */ true); + await runTests(addon, addon.isVersion8, /* isLocalSymbol */ false); } runAllTests().then(mustCall()); diff --git a/test/node-api/test_reference_by_node_api_version/test_reference_by_node_api_version.c b/test/node-api/test_reference_by_node_api_version/test_reference_by_node_api_version.c index d1a871949951b2..78ac5d4208536a 100644 --- a/test/node-api/test_reference_by_node_api_version/test_reference_by_node_api_version.c +++ b/test/node-api/test_reference_by_node_api_version/test_reference_by_node_api_version.c @@ -146,7 +146,12 @@ NAPI_MODULE_INIT() { finalizeCount = 0; NODE_API_CALL(env, InitRefArray(env)); + napi_value isVersion8Value; + NODE_API_CALL(env, + napi_get_boolean(env, NAPI_VERSION <= 9, &isVersion8Value)); + napi_property_descriptor properties[] = { + DECLARE_NODE_API_PROPERTY_VALUE("isVersion8", isVersion8Value), DECLARE_NODE_API_PROPERTY("createExternal", CreateExternal), DECLARE_NODE_API_PROPERTY("createRef", CreateRef), DECLARE_NODE_API_PROPERTY("getRefValue", GetRefValue), diff --git a/test/node-api/test_sea_addon/binding.gyp b/test/node-api/test_sea_addon/binding.gyp index 62381d5e54f22b..fcd94df746cf70 100644 --- a/test/node-api/test_sea_addon/binding.gyp +++ b/test/node-api/test_sea_addon/binding.gyp @@ -3,6 +3,11 @@ { "target_name": "binding", "sources": [ "binding.c" ] + }, + { + "target_name": "binding_vtable", + "defines": [ "NODE_API_MODULE_USE_VTABLE" ], + "sources": [ "binding.c" ] } ] } diff --git a/test/node-api/test_sea_addon/test.js b/test/node-api/test_sea_addon/test.js index 386ece80955c9d..f0ae18d8a98da4 100644 --- a/test/node-api/test_sea_addon/test.js +++ b/test/node-api/test_sea_addon/test.js @@ -1,7 +1,10 @@ 'use strict'; +// Addons: binding, binding_vtable + // This tests that SEA can load addons packaged as assets by writing them to disk // and loading them via process.dlopen(). -const common = require('../../common'); + +const { addonPath } = require('../../common/addon-test'); const { generateSEA, skipIfSingleExecutableIsNotSupported } = require('../../common/sea'); skipIfSingleExecutableIsNotSupported(); @@ -21,10 +24,13 @@ const outputFile = tmpdir.resolve(process.platform === 'win32' ? 'sea.exe' : 'se tmpdir.refresh(); // Copy test fixture to working directory -const addonPath = join(__dirname, 'build', common.buildType, 'binding.node'); +const addonFile = join(__dirname, `${addonPath}.node`); +assert.ok(existsSync(addonFile), `missing addon artifact: ${addonFile}`); const copiedAddonPath = tmpdir.resolve('binding.node'); -copyFileSync(addonPath, copiedAddonPath); -writeFileSync(tmpdir.resolve('sea.js'), ` + +try { + copyFileSync(addonFile, copiedAddonPath); + writeFileSync(tmpdir.resolve('sea.js'), ` const sea = require('node:sea'); const fs = require('fs'); const path = require('path'); @@ -36,7 +42,7 @@ process.dlopen(mod, addonPath); console.log('hello,', mod.exports.hello()); `, 'utf-8'); -writeFileSync(configFile, ` + writeFileSync(configFile, ` { "main": "sea.js", "output": "sea-prep.blob", @@ -47,29 +53,34 @@ writeFileSync(configFile, ` } `, 'utf8'); -spawnSyncAndExitWithoutError( - process.execPath, - ['--experimental-sea-config', 'sea-config.json'], - { cwd: tmpdir.path }, -); -assert(existsSync(seaPrepBlob)); + spawnSyncAndExitWithoutError( + process.execPath, + ['--experimental-sea-config', 'sea-config.json'], + { cwd: tmpdir.path }, + ); + assert(existsSync(seaPrepBlob)); -generateSEA(outputFile, process.execPath, seaPrepBlob); + generateSEA(outputFile, process.execPath, seaPrepBlob); + assert.ok(existsSync(outputFile), 'SEA output was not created'); -// Remove the copied addon after it's been packaged into the SEA blob -rmSync(copiedAddonPath, { force: true }); + // Remove the copied addon after it's been packaged into the SEA blob + rmSync(copiedAddonPath, { force: true }); -spawnSyncAndAssert( - outputFile, - [], - { - env: { - ...process.env, - NODE_DEBUG_NATIVE: 'SEA', + spawnSyncAndAssert( + outputFile, + [], + { + env: { + ...process.env, + NODE_DEBUG_NATIVE: 'SEA', + }, + cwd: tmpdir.path, }, - cwd: tmpdir.path, - }, - { - stdout: /hello, world/, - }, -); + { + stdout: /hello, world/, + }, + ); +} finally { + rmSync(seaPrepBlob, { force: true }); + rmSync(outputFile, { force: true }); +} diff --git a/test/node-api/test_threadsafe_function/binding.gyp b/test/node-api/test_threadsafe_function/binding.gyp index 58a9d04d4a5619..004cb0928f9f9a 100644 --- a/test/node-api/test_threadsafe_function/binding.gyp +++ b/test/node-api/test_threadsafe_function/binding.gyp @@ -4,6 +4,11 @@ 'target_name': 'binding', 'sources': ['binding.c'] }, + { + 'target_name': 'binding_vtable', + 'defines': [ 'NODE_API_MODULE_USE_VTABLE' ], + 'sources': ['binding.c'] + }, { 'target_name': 'test_uncaught_exception_v9', 'defines': [ @@ -11,12 +16,28 @@ ], 'sources': ['test_uncaught_exception.c'] }, + { + 'target_name': 'test_uncaught_exception_v9_vtable', + 'defines': [ + 'NAPI_VERSION=9', + 'NODE_API_MODULE_USE_VTABLE' + ], + 'sources': ['test_uncaught_exception.c'] + }, { 'target_name': 'test_uncaught_exception', 'defines': [ 'NAPI_EXPERIMENTAL' ], 'sources': ['test_uncaught_exception.c'] + }, + { + 'target_name': 'test_uncaught_exception_vtable', + 'defines': [ + 'NAPI_EXPERIMENTAL', + 'NODE_API_MODULE_USE_VTABLE' + ], + 'sources': ['test_uncaught_exception.c'] } ] } diff --git a/test/node-api/test_threadsafe_function/test.js b/test/node-api/test_threadsafe_function/test.js index 69d37ca221e9f9..f49ac14216c090 100644 --- a/test/node-api/test_threadsafe_function/test.js +++ b/test/node-api/test_threadsafe_function/test.js @@ -1,9 +1,10 @@ 'use strict'; +// Addons: binding, binding_vtable const common = require('../../common'); +const { addonPath, isInvokedAsChild, spawnTestSync } = require('../../common/addon-test'); const assert = require('assert'); -const binding = require(`./build/${common.buildType}/binding`); -const { fork } = require('child_process'); +const binding = require(addonPath); const expectedArray = (function(arrayLength) { const result = []; for (let index = 0; index < arrayLength; index++) { @@ -15,7 +16,7 @@ const expectedArray = (function(arrayLength) { // Handle the rapid teardown test case as the child process. We unref the // thread-safe function after we have received two values. This causes the // process to exit and the environment cleanup handler to be invoked. -if (process.argv[2] === 'child') { +if (isInvokedAsChild) { let callCount = 0; binding.StartThread((value) => { callCount++; @@ -23,207 +24,204 @@ if (process.argv[2] === 'child') { if (callCount === 2) { binding.Unref(); } - }, false /* abort */, true /* launchSecondary */, +process.argv[3]); + }, false /* abort */, true /* launchSecondary */, +process.env.QUEUE_SIZE); // Release the thread-safe function from the main thread so that it may be // torn down via the environment cleanup handler. binding.Release(); - return; -} - -function testWithJSMarshaller({ - threadStarter, - quitAfter, - abort, - maxQueueSize, - launchSecondary, -}) { - return new Promise((resolve) => { - const array = []; - binding[threadStarter](function testCallback(value) { - array.push(value); - if (array.length === quitAfter) { - setImmediate(() => { - binding.StopThread(() => { - resolve(array); - }, !!abort); - }); +} else { + + function testWithJSMarshaller({ + threadStarter, + quitAfter, + abort, + maxQueueSize, + launchSecondary, + }) { + return new Promise((resolve) => { + const array = []; + binding[threadStarter](function testCallback(value) { + array.push(value); + if (array.length === quitAfter) { + setImmediate(() => { + binding.StopThread(() => { + resolve(array); + }, !!abort); + }); + } + }, !!abort, !!launchSecondary, maxQueueSize); + if (threadStarter === 'StartThreadNonblocking') { + // Let's make this thread really busy for a short while to ensure that + // the queue fills and the thread receives a napi_queue_full. + const start = Date.now(); + while (Date.now() - start < 200); } - }, !!abort, !!launchSecondary, maxQueueSize); - if (threadStarter === 'StartThreadNonblocking') { - // Let's make this thread really busy for a short while to ensure that - // the queue fills and the thread receives a napi_queue_full. - const start = Date.now(); - while (Date.now() - start < 200); - } - }); -} - -function testUnref(queueSize) { - return new Promise((resolve, reject) => { - let output = ''; - const child = fork(__filename, ['child', queueSize], { - stdio: [process.stdin, 'pipe', process.stderr, 'ipc'], }); - child.on('close', (code) => { - if (code === 0) { + } + + function testUnref(queueSize) { + return new Promise((resolve, reject) => { + const child = spawnTestSync([], { + env: { ...process.env, QUEUE_SIZE: String(queueSize) }, + }); + if (child.status === 0) { + const output = child.stdout ? child.stdout.toString() : ''; resolve(output.match(/\S+/g)); } else { - reject(new Error('Child process died with code ' + code)); + reject(new Error('Child process died with code ' + child.status)); } - }); - child.stdout.on('data', (data) => (output += data.toString())); - }) - .then((result) => assert.strictEqual(result.indexOf(0), -1)); -} + }) + .then((result) => assert.strictEqual(result.indexOf(0), -1)); + } -new Promise(function testWithoutJSMarshaller(resolve) { - let callCount = 0; - binding.StartThreadNoNative(common.mustCallAtLeast(function testCallback() { - callCount++; + new Promise(function testWithoutJSMarshaller(resolve) { + let callCount = 0; + binding.StartThreadNoNative(common.mustCallAtLeast(function testCallback() { + callCount++; - // The default call-into-JS implementation passes no arguments. - assert.strictEqual(arguments.length, 0); - if (callCount === binding.ARRAY_LENGTH) { - setImmediate(() => { - binding.StopThread(() => { - resolve(); - }, false); - }); - } - }), false /* abort */, false /* launchSecondary */, binding.MAX_QUEUE_SIZE); -}) - -// Start the thread in blocking mode, and assert that all values are passed. -// Quit after it's done. -.then(() => testWithJSMarshaller({ - threadStarter: 'StartThread', - maxQueueSize: binding.MAX_QUEUE_SIZE, - quitAfter: binding.ARRAY_LENGTH, -})) -.then((result) => assert.deepStrictEqual(result, expectedArray)) - -// Start the thread in blocking mode, and assert that all values are passed. -// Quit after it's done. -// Doesn't pass the callback js function to napi_create_threadsafe_function. -// Instead, use an alternative reference to get js function called. -.then(() => testWithJSMarshaller({ - threadStarter: 'StartThreadNoJsFunc', - maxQueueSize: binding.MAX_QUEUE_SIZE, - quitAfter: binding.ARRAY_LENGTH, -})) -.then((result) => assert.deepStrictEqual(result, expectedArray)) - -// Start the thread in blocking mode with an infinite queue, and assert that all -// values are passed. Quit after it's done. -.then(() => testWithJSMarshaller({ - threadStarter: 'StartThread', - maxQueueSize: 0, - quitAfter: binding.ARRAY_LENGTH, -})) -.then((result) => assert.deepStrictEqual(result, expectedArray)) - -// Start the thread in non-blocking mode, and assert that all values are passed. -// Quit after it's done. -.then(() => testWithJSMarshaller({ - threadStarter: 'StartThreadNonblocking', - maxQueueSize: binding.MAX_QUEUE_SIZE, - quitAfter: binding.ARRAY_LENGTH, -})) -.then((result) => assert.deepStrictEqual(result, expectedArray)) - -// Start the thread in blocking mode, and assert that all values are passed. -// Quit early, but let the thread finish. -.then(() => testWithJSMarshaller({ - threadStarter: 'StartThread', - maxQueueSize: binding.MAX_QUEUE_SIZE, - quitAfter: 1, -})) -.then((result) => assert.deepStrictEqual(result, expectedArray)) - -// Start the thread in blocking mode with an infinite queue, and assert that all -// values are passed. Quit early, but let the thread finish. -.then(() => testWithJSMarshaller({ - threadStarter: 'StartThread', - maxQueueSize: 0, - quitAfter: 1, -})) -.then((result) => assert.deepStrictEqual(result, expectedArray)) - -// Start the thread in non-blocking mode, and assert that all values are passed. -// Quit early, but let the thread finish. -.then(() => testWithJSMarshaller({ - threadStarter: 'StartThreadNonblocking', - maxQueueSize: binding.MAX_QUEUE_SIZE, - quitAfter: 1, -})) -.then((result) => assert.deepStrictEqual(result, expectedArray)) - -// Start the thread in blocking mode, and assert that all values are passed. -// Quit early, but let the thread finish. Launch a secondary thread to test the -// reference counter incrementing functionality. -.then(() => testWithJSMarshaller({ - threadStarter: 'StartThread', - quitAfter: 1, - maxQueueSize: binding.MAX_QUEUE_SIZE, - launchSecondary: true, -})) -.then((result) => assert.deepStrictEqual(result, expectedArray)) - -// Start the thread in non-blocking mode, and assert that all values are passed. -// Quit early, but let the thread finish. Launch a secondary thread to test the -// reference counter incrementing functionality. -.then(() => testWithJSMarshaller({ - threadStarter: 'StartThreadNonblocking', - quitAfter: 1, - maxQueueSize: binding.MAX_QUEUE_SIZE, - launchSecondary: true, -})) -.then((result) => assert.deepStrictEqual(result, expectedArray)) - -// Start the thread in blocking mode, and assert that it could not finish. -// Quit early by aborting. -.then(() => testWithJSMarshaller({ - threadStarter: 'StartThread', - quitAfter: 1, - maxQueueSize: binding.MAX_QUEUE_SIZE, - abort: true, -})) -.then((result) => assert.strictEqual(result.indexOf(0), -1)) - -// Start the thread in blocking mode with an infinite queue, and assert that it -// could not finish. Quit early by aborting. -.then(() => testWithJSMarshaller({ - threadStarter: 'StartThread', - quitAfter: 1, - maxQueueSize: 0, - abort: true, -})) -.then((result) => assert.strictEqual(result.indexOf(0), -1)) - -// Start the thread in non-blocking mode, and assert that it could not finish. -// Quit early and aborting. -.then(() => testWithJSMarshaller({ - threadStarter: 'StartThreadNonblocking', - quitAfter: 1, - maxQueueSize: binding.MAX_QUEUE_SIZE, - abort: true, -})) -.then((result) => assert.strictEqual(result.indexOf(0), -1)) - -// Make sure that threadsafe function isn't stalled when we hit -// `kMaxIterationCount` in `src/node_api.cc` -.then(() => testWithJSMarshaller({ - threadStarter: 'StartThreadNonblocking', - maxQueueSize: binding.ARRAY_LENGTH >>> 1, - quitAfter: binding.ARRAY_LENGTH, -})) -.then((result) => assert.deepStrictEqual(result, expectedArray)) - -// Start a child process to test rapid teardown -.then(() => testUnref(binding.MAX_QUEUE_SIZE)) - -// Start a child process with an infinite queue to test rapid teardown -.then(() => testUnref(0)) - -.then(common.mustCall()); + // The default call-into-JS implementation passes no arguments. + assert.strictEqual(arguments.length, 0); + if (callCount === binding.ARRAY_LENGTH) { + setImmediate(() => { + binding.StopThread(() => { + resolve(); + }, false); + }); + } + }), false /* abort */, false /* launchSecondary */, binding.MAX_QUEUE_SIZE); + }) + + // Start the thread in blocking mode, and assert that all values are passed. + // Quit after it's done. + .then(() => testWithJSMarshaller({ + threadStarter: 'StartThread', + maxQueueSize: binding.MAX_QUEUE_SIZE, + quitAfter: binding.ARRAY_LENGTH, + })) + .then((result) => assert.deepStrictEqual(result, expectedArray)) + + // Start the thread in blocking mode, and assert that all values are passed. + // Quit after it's done. + // Doesn't pass the callback js function to napi_create_threadsafe_function. + // Instead, use an alternative reference to get js function called. + .then(() => testWithJSMarshaller({ + threadStarter: 'StartThreadNoJsFunc', + maxQueueSize: binding.MAX_QUEUE_SIZE, + quitAfter: binding.ARRAY_LENGTH, + })) + .then((result) => assert.deepStrictEqual(result, expectedArray)) + + // Start the thread in blocking mode with an infinite queue, and assert that all + // values are passed. Quit after it's done. + .then(() => testWithJSMarshaller({ + threadStarter: 'StartThread', + maxQueueSize: 0, + quitAfter: binding.ARRAY_LENGTH, + })) + .then((result) => assert.deepStrictEqual(result, expectedArray)) + + // Start the thread in non-blocking mode, and assert that all values are passed. + // Quit after it's done. + .then(() => testWithJSMarshaller({ + threadStarter: 'StartThreadNonblocking', + maxQueueSize: binding.MAX_QUEUE_SIZE, + quitAfter: binding.ARRAY_LENGTH, + })) + .then((result) => assert.deepStrictEqual(result, expectedArray)) + + // Start the thread in blocking mode, and assert that all values are passed. + // Quit early, but let the thread finish. + .then(() => testWithJSMarshaller({ + threadStarter: 'StartThread', + maxQueueSize: binding.MAX_QUEUE_SIZE, + quitAfter: 1, + })) + .then((result) => assert.deepStrictEqual(result, expectedArray)) + + // Start the thread in blocking mode with an infinite queue, and assert that all + // values are passed. Quit early, but let the thread finish. + .then(() => testWithJSMarshaller({ + threadStarter: 'StartThread', + maxQueueSize: 0, + quitAfter: 1, + })) + .then((result) => assert.deepStrictEqual(result, expectedArray)) + + // Start the thread in non-blocking mode, and assert that all values are passed. + // Quit early, but let the thread finish. + .then(() => testWithJSMarshaller({ + threadStarter: 'StartThreadNonblocking', + maxQueueSize: binding.MAX_QUEUE_SIZE, + quitAfter: 1, + })) + .then((result) => assert.deepStrictEqual(result, expectedArray)) + + // Start the thread in blocking mode, and assert that all values are passed. + // Quit early, but let the thread finish. Launch a secondary thread to test the + // reference counter incrementing functionality. + .then(() => testWithJSMarshaller({ + threadStarter: 'StartThread', + quitAfter: 1, + maxQueueSize: binding.MAX_QUEUE_SIZE, + launchSecondary: true, + })) + .then((result) => assert.deepStrictEqual(result, expectedArray)) + + // Start the thread in non-blocking mode, and assert that all values are passed. + // Quit early, but let the thread finish. Launch a secondary thread to test the + // reference counter incrementing functionality. + .then(() => testWithJSMarshaller({ + threadStarter: 'StartThreadNonblocking', + quitAfter: 1, + maxQueueSize: binding.MAX_QUEUE_SIZE, + launchSecondary: true, + })) + .then((result) => assert.deepStrictEqual(result, expectedArray)) + + // Start the thread in blocking mode, and assert that it could not finish. + // Quit early by aborting. + .then(() => testWithJSMarshaller({ + threadStarter: 'StartThread', + quitAfter: 1, + maxQueueSize: binding.MAX_QUEUE_SIZE, + abort: true, + })) + .then((result) => assert.strictEqual(result.indexOf(0), -1)) + + // Start the thread in blocking mode with an infinite queue, and assert that it + // could not finish. Quit early by aborting. + .then(() => testWithJSMarshaller({ + threadStarter: 'StartThread', + quitAfter: 1, + maxQueueSize: 0, + abort: true, + })) + .then((result) => assert.strictEqual(result.indexOf(0), -1)) + + // Start the thread in non-blocking mode, and assert that it could not finish. + // Quit early and aborting. + .then(() => testWithJSMarshaller({ + threadStarter: 'StartThreadNonblocking', + quitAfter: 1, + maxQueueSize: binding.MAX_QUEUE_SIZE, + abort: true, + })) + .then((result) => assert.strictEqual(result.indexOf(0), -1)) + + // Make sure that threadsafe function isn't stalled when we hit + // `kMaxIterationCount` in `src/node_api.cc` + .then(() => testWithJSMarshaller({ + threadStarter: 'StartThreadNonblocking', + maxQueueSize: binding.ARRAY_LENGTH >>> 1, + quitAfter: binding.ARRAY_LENGTH, + })) + .then((result) => assert.deepStrictEqual(result, expectedArray)) + + // Start a child process to test rapid teardown + .then(() => testUnref(binding.MAX_QUEUE_SIZE)) + + // Start a child process with an infinite queue to test rapid teardown + .then(() => testUnref(0)) + + .then(common.mustCall()); +} diff --git a/test/node-api/test_threadsafe_function/test_legacy_uncaught_exception.js b/test/node-api/test_threadsafe_function/test_legacy_uncaught_exception.js index a8743e00b5b8c5..36575b7381c89b 100644 --- a/test/node-api/test_threadsafe_function/test_legacy_uncaught_exception.js +++ b/test/node-api/test_threadsafe_function/test_legacy_uncaught_exception.js @@ -1,8 +1,10 @@ 'use strict'; // Flags: --no-force-node-api-uncaught-exceptions-policy +// Addons: test_uncaught_exception_v9, test_uncaught_exception_v9_vtable const common = require('../../common'); -const binding = require(`./build/${common.buildType}/test_uncaught_exception_v9`); +const { addonPath } = require('../../common/addon-test'); +const binding = require(addonPath); process.on( 'uncaughtException', 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 81b2623d702790..d0afbdd0eaa7a0 100644 --- a/test/node-api/test_threadsafe_function/test_uncaught_exception.js +++ b/test/node-api/test_threadsafe_function/test_uncaught_exception.js @@ -1,7 +1,8 @@ 'use strict'; +// Addons: test_uncaught_exception, test_uncaught_exception_vtable -const common = require('../../common'); -const binding = require(`./build/${common.buildType}/test_uncaught_exception`); +const { addonPath } = require('../../common/addon-test'); +const binding = require(addonPath); const { testUncaughtException } = require('./uncaught_exception'); 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 index 28e628918fdff2..c4a6901b9acdff 100644 --- a/test/node-api/test_threadsafe_function/test_uncaught_exception_v9.js +++ b/test/node-api/test_threadsafe_function/test_uncaught_exception_v9.js @@ -1,8 +1,9 @@ 'use strict'; // Flags: --force-node-api-uncaught-exceptions-policy +// Addons: test_uncaught_exception_v9, test_uncaught_exception_v9_vtable -const common = require('../../common'); -const binding = require(`./build/${common.buildType}/test_uncaught_exception_v9`); +const { addonPath } = require('../../common/addon-test'); +const binding = require(addonPath); const { testUncaughtException } = require('./uncaught_exception'); testUncaughtException(binding); diff --git a/test/node-api/test_threadsafe_function_shutdown/binding.gyp b/test/node-api/test_threadsafe_function_shutdown/binding.gyp index eb08b447a94a86..a9b014d36a3627 100644 --- a/test/node-api/test_threadsafe_function_shutdown/binding.gyp +++ b/test/node-api/test_threadsafe_function_shutdown/binding.gyp @@ -6,6 +6,14 @@ "cflags_cc": ["--std=c++20"], 'cflags!': [ '-fno-exceptions', '-fno-rtti' ], 'cflags_cc!': [ '-fno-exceptions', '-fno-rtti' ], + }, + { + "target_name": "binding_vtable", + "defines": [ "NODE_API_MODULE_USE_VTABLE" ], + "sources": ["binding.cc"], + "cflags_cc": ["--std=c++20"], + 'cflags!': [ '-fno-exceptions', '-fno-rtti' ], + 'cflags_cc!': [ '-fno-exceptions', '-fno-rtti' ], } ] } diff --git a/test/node-api/test_threadsafe_function_shutdown/test.js b/test/node-api/test_threadsafe_function_shutdown/test.js index 9b2587b5bbdfde..8bd6c78c0c6d13 100644 --- a/test/node-api/test_threadsafe_function_shutdown/test.js +++ b/test/node-api/test_threadsafe_function_shutdown/test.js @@ -1,17 +1,15 @@ 'use strict'; +// Addons: binding, binding_vtable -const common = require('../../common'); -const process = require('process'); +const { addonPath, isInvokedAsChild, spawnTestSync } = require('../../common/addon-test'); const assert = require('assert'); -const { fork } = require('child_process'); -const binding = require(`./build/${common.buildType}/binding`); -if (process.argv[2] === 'child') { +if (isInvokedAsChild) { + const binding = require(addonPath); binding(); setTimeout(() => {}, 100); } else { - const child = fork(__filename, ['child']); - child.on('close', common.mustCall((code) => { - assert.strictEqual(code, 0); - })); + const { status, stderr } = spawnTestSync(); + const stderrText = stderr ? stderr.toString().trim() : ''; + assert.strictEqual(status, 0, stderrText); } diff --git a/test/node-api/test_uv_loop/binding.gyp b/test/node-api/test_uv_loop/binding.gyp index 81fcfdc592a523..51cae3a67015af 100644 --- a/test/node-api/test_uv_loop/binding.gyp +++ b/test/node-api/test_uv_loop/binding.gyp @@ -3,6 +3,11 @@ { "target_name": "test_uv_loop", "sources": [ "test_uv_loop.cc" ] + }, + { + "target_name": "test_uv_loop_vtable", + "defines": [ "NODE_API_MODULE_USE_VTABLE" ], + "sources": [ "test_uv_loop.cc" ] } ] } diff --git a/test/node-api/test_uv_loop/test.js b/test/node-api/test_uv_loop/test.js index 4efc3c6fcd7001..13d3f30ec491ce 100644 --- a/test/node-api/test_uv_loop/test.js +++ b/test/node-api/test_uv_loop/test.js @@ -1,5 +1,8 @@ 'use strict'; +// Addons: test_uv_loop, test_uv_loop_vtable + const common = require('../../common'); -const { SetImmediate } = require(`./build/${common.buildType}/test_uv_loop`); +const { addonPath } = require('../../common/addon-test'); +const { SetImmediate } = require(addonPath); SetImmediate(common.mustCall()); diff --git a/test/node-api/test_uv_threadpool_size/binding.gyp b/test/node-api/test_uv_threadpool_size/binding.gyp index 55eff885e1ca1f..20674505a32ea7 100644 --- a/test/node-api/test_uv_threadpool_size/binding.gyp +++ b/test/node-api/test_uv_threadpool_size/binding.gyp @@ -3,6 +3,11 @@ { "target_name": "test_uv_threadpool_size", "sources": [ "test_uv_threadpool_size.c" ] + }, + { + "target_name": "test_uv_threadpool_size_vtable", + "defines": [ "NODE_API_MODULE_USE_VTABLE" ], + "sources": [ "test_uv_threadpool_size.c" ] } ] } diff --git a/test/node-api/test_uv_threadpool_size/node-options.js b/test/node-api/test_uv_threadpool_size/node-options.js index 68351c6cbee8dd..97789d52e9f5cb 100644 --- a/test/node-api/test_uv_threadpool_size/node-options.js +++ b/test/node-api/test_uv_threadpool_size/node-options.js @@ -1,6 +1,8 @@ 'use strict'; +// Addons: test_uv_threadpool_size, test_uv_threadpool_size_vtable const common = require('../../common'); +const { addonPath } = require('../../common/addon-test'); const assert = require('assert'); const path = require('path'); const { spawnSync } = require('child_process'); @@ -9,20 +11,23 @@ if (process.config.variables.node_without_node_options) { common.skip('missing NODE_OPTIONS support'); } -const uvThreadPoolPath = '../../fixtures/dotenv/uv-threadpool.env'; +const uvThreadPoolPath = path.resolve(__dirname, '../../fixtures/dotenv/uv-threadpool.env'); // Should update UV_THREADPOOL_SIZE -const filePath = path.join(__dirname, `./build/${common.buildType}/test_uv_threadpool_size`); +const filePath = path.join(__dirname, addonPath); const code = ` - const { test } = require(${JSON.stringify(filePath)}); - const size = parseInt(process.env.UV_THREADPOOL_SIZE, 10); - assert.strictEqual(size, 4); - test(size); - `.trim(); + const assert = require('assert'); + const { test } = require(${JSON.stringify(filePath)}); + const size = parseInt(process.env.UV_THREADPOOL_SIZE, 10); + assert.strictEqual(size, 4); + test(size); +`.trim(); + const child = spawnSync( process.execPath, - [ `--env-file=${uvThreadPoolPath}`, '--eval', code ], + [`--env-file=${uvThreadPoolPath}`, '--eval', code], { cwd: __dirname, encoding: 'utf-8' }, ); + assert.strictEqual(child.stderr, ''); assert.strictEqual(child.status, 0); diff --git a/test/node-api/test_uv_threadpool_size/test.js b/test/node-api/test_uv_threadpool_size/test.js index 9f219e2e87aead..2e617bee55c194 100644 --- a/test/node-api/test_uv_threadpool_size/test.js +++ b/test/node-api/test_uv_threadpool_size/test.js @@ -1,6 +1,8 @@ 'use strict'; -const common = require('../../common'); -const { test } = require(`./build/${common.buildType}/test_uv_threadpool_size`); +// Addons: test_uv_threadpool_size, test_uv_threadpool_size_vtable + +const { addonPath } = require('../../common/addon-test'); +const { test } = require(addonPath); const uvThreadpoolSize = parseInt(process.env.EXPECTED_UV_THREADPOOL_SIZE || process.env.UV_THREADPOOL_SIZE, 10) || 4; diff --git a/test/node-api/test_worker_buffer_callback/binding.gyp b/test/node-api/test_worker_buffer_callback/binding.gyp index 7ab6381930da9c..17af4972aca46f 100644 --- a/test/node-api/test_worker_buffer_callback/binding.gyp +++ b/test/node-api/test_worker_buffer_callback/binding.gyp @@ -3,6 +3,11 @@ { 'target_name': 'binding', 'sources': [ 'test_worker_buffer_callback.c' ] + }, + { + 'target_name': 'binding_vtable', + 'defines': [ 'NODE_API_MODULE_USE_VTABLE' ], + 'sources': [ 'test_worker_buffer_callback.c' ] } ] } diff --git a/test/node-api/test_worker_buffer_callback/test-free-called.js b/test/node-api/test_worker_buffer_callback/test-free-called.js index 2a3cc9e47c22ff..cc95d2f8d5597b 100644 --- a/test/node-api/test_worker_buffer_callback/test-free-called.js +++ b/test/node-api/test_worker_buffer_callback/test-free-called.js @@ -1,9 +1,12 @@ 'use strict'; +// Addons: binding, binding_vtable + const common = require('../../common'); +const { addonPath } = require('../../common/addon-test'); const path = require('path'); const assert = require('assert'); const { Worker } = require('worker_threads'); -const binding = path.resolve(__dirname, `./build/${common.buildType}/binding`); +const binding = path.resolve(__dirname, addonPath); const { getFreeCallCount } = require(binding); // Test that buffers allocated with a free callback through our APIs are diff --git a/test/node-api/test_worker_buffer_callback/test.js b/test/node-api/test_worker_buffer_callback/test.js index 04b9249c684eab..283059e90212dc 100644 --- a/test/node-api/test_worker_buffer_callback/test.js +++ b/test/node-api/test_worker_buffer_callback/test.js @@ -1,8 +1,10 @@ 'use strict'; -const common = require('../../common'); +// Addons: binding, binding_vtable + +const { addonPath } = require('../../common/addon-test'); const assert = require('assert'); const { MessageChannel } = require('worker_threads'); -const { buffer } = require(`./build/${common.buildType}/binding`); +const { buffer } = require(addonPath); // Test that buffers allocated with a free callback through our APIs are not // transferred. diff --git a/test/node-api/test_worker_terminate/binding.gyp b/test/node-api/test_worker_terminate/binding.gyp index 3a9465c7454148..b2c9deb47bb42f 100644 --- a/test/node-api/test_worker_terminate/binding.gyp +++ b/test/node-api/test_worker_terminate/binding.gyp @@ -3,6 +3,11 @@ { "target_name": "test_worker_terminate", "sources": [ "test_worker_terminate.c" ] + }, + { + "target_name": "test_worker_terminate_vtable", + "defines": [ "NODE_API_MODULE_USE_VTABLE" ], + "sources": [ "test_worker_terminate.c" ] } ] } diff --git a/test/node-api/test_worker_terminate/test.js b/test/node-api/test_worker_terminate/test.js index eefb974af5a669..dc8d24b6234b5a 100644 --- a/test/node-api/test_worker_terminate/test.js +++ b/test/node-api/test_worker_terminate/test.js @@ -1,22 +1,27 @@ 'use strict'; +// Addons: test_worker_terminate, test_worker_terminate_vtable + const common = require('../../common'); const assert = require('assert'); const { Worker, isMainThread, workerData } = require('worker_threads'); if (isMainThread) { + const { addonPath } = require('../../common/addon-test'); + // Load the addon in the main thread first. - // This checks that N-API addons can be loaded from multiple contexts + // This checks that Node-API addons can be loaded from multiple contexts // when they are not loaded through NAPI_MODULE(). - require(`./build/${common.buildType}/test_worker_terminate`); + require(addonPath); const counter = new Int32Array(new SharedArrayBuffer(4)); - const worker = new Worker(__filename, { workerData: { counter } }); + const worker = new Worker(__filename, { workerData: { counter, addonPath } }); + worker.on('exit', common.mustCall(() => { assert.strictEqual(counter[0], 1); })); worker.on('error', common.mustNotCall()); } else { - const { Test } = require(`./build/${common.buildType}/test_worker_terminate`); + const { Test } = require(workerData.addonPath); const { counter } = workerData; // Test() tries to call a function and asserts it fails because of a diff --git a/test/node-api/test_worker_terminate_finalization/binding.gyp b/test/node-api/test_worker_terminate_finalization/binding.gyp index 800ed54d568e16..ee0aba5d5bc609 100644 --- a/test/node-api/test_worker_terminate_finalization/binding.gyp +++ b/test/node-api/test_worker_terminate_finalization/binding.gyp @@ -3,6 +3,11 @@ { "target_name": "test_worker_terminate_finalization", "sources": [ "test_worker_terminate_finalization.c" ] + }, + { + "target_name": "test_worker_terminate_finalization_vtable", + "defines": [ "NODE_API_MODULE_USE_VTABLE" ], + "sources": [ "test_worker_terminate_finalization.c" ] } ] } diff --git a/test/node-api/test_worker_terminate_finalization/test.js b/test/node-api/test_worker_terminate_finalization/test.js index 937079968f722a..f4a9aee5ea783e 100644 --- a/test/node-api/test_worker_terminate_finalization/test.js +++ b/test/node-api/test_worker_terminate_finalization/test.js @@ -1,21 +1,24 @@ 'use strict'; +// Addons: test_worker_terminate_finalization, test_worker_terminate_finalization_vtable + const common = require('../../common'); // Refs: https://github.com/nodejs/node/issues/34731 // Refs: https://github.com/nodejs/node/pull/35777 // Refs: https://github.com/nodejs/node/issues/35778 -const { Worker, isMainThread } = require('worker_threads'); +const { Worker, isMainThread, workerData } = require('worker_threads'); if (isMainThread) { - const worker = new Worker(__filename); + const { addonPath } = require('../../common/addon-test'); + + const worker = new Worker(__filename, { workerData: { addonPath } }); worker.on('error', common.mustNotCall()); } else { - const { Test } = - require(`./build/${common.buildType}/test_worker_terminate_finalization`); + const { Test } = require(workerData.addonPath); // Spin up thread and call add-on create the right sequence - // of rerences to hit the case reported in + // of references to hit the case reported in // https://github.com/nodejs/node-addon-api/issues/722 // will crash if run under debug and its not possible to // create object in the specific finalizer diff --git a/test/testpy/__init__.py b/test/testpy/__init__.py index 9e7686c450b74b..17b78e67dc4200 100644 --- a/test/testpy/__init__.py +++ b/test/testpy/__init__.py @@ -33,6 +33,7 @@ FLAGS_PATTERN = re.compile(r"//\s+Flags:(.*)") +ADDONS_PATTERN = re.compile(r"//\s+Addons:(.*)") LS_RE = re.compile(r'^test-.*\.m?js$') ENV_PATTERN = re.compile(r"//\s+Env:(.*)") NODE_TEST_PATTERN = re.compile(r"('|`|\")node:test\1") @@ -154,6 +155,22 @@ def ListTests(self, current_path, path, arch, mode): tst.parallel = True return result +class AddonTestCase(SimpleTestCase): + def __init__(self, path, file, arch, mode, context, config, additional=None, addon_name=None): + super(AddonTestCase, self).__init__(path, file, arch, mode, context, config, additional) + self.addon_name = addon_name + + def GetName(self): + # path format: ['node-api', 'folder', 'script', 'addon'] + # Return: folder/script/addon (e.g., 1_hello_world/test/binding) + return '/'.join(self.path[-3:]) + + def GetRunConfiguration(self): + config = super(AddonTestCase, self).GetRunConfiguration() + # Append --addon= after the script path so it's a script argument + config['command'].append(f'--addon={self.addon_name}') + return config + class AddonTestConfiguration(SimpleTestConfiguration): def __init__(self, context, root, section, additional=None): super(AddonTestConfiguration, self).__init__(context, root, section, additional) @@ -164,8 +181,26 @@ def SelectTest(name): result = [] for subpath in os.listdir(path): - if os.path.isdir(os.path.join(path, subpath)): - result.extend([subpath, f[:-3]] for f in os.listdir(os.path.join(path, subpath)) if SelectTest(f)) + subdir = os.path.join(path, subpath) + if not os.path.isdir(subdir): + continue + + for filename in os.listdir(subdir): + if not SelectTest(filename): + continue + filepath = os.path.join(subdir, filename) + try: + with open(filepath, 'r', encoding='utf8') as f: + content = f.read(1024) # Read first 1KB for the comments + addons_matches = ADDONS_PATTERN.findall(content) + if addons_matches: + addons = [a.strip() for match in addons_matches for a in match.split(',') if a.strip()] + result.extend([subpath, filename[:-3], addon] for addon in addons) + else: + result.append([subpath, filename[:-3]]) + except IOError as e: + print(f"Warning: Failed to read {filepath}: {e}") + return result def ListTests(self, current_path, path, arch, mode): @@ -173,9 +208,17 @@ def ListTests(self, current_path, path, arch, mode): result = [] for tst in all_tests: if self.Contains(path, tst): - file_path = os.path.join(self.root, reduce(os.path.join, tst[1:], "") + ".js") - result.append( - SimpleTestCase(tst, file_path, arch, mode, self.context, self, self.additional_flags)) + # Addons test: current_path + [subpath, script_name, addon_name] + # Default test: current_path + [subpath, script_name] + addon_name = tst[-1] if len(tst) == len(current_path) + 3 else None + parts = tst[1:-1] if addon_name else tst[1:] + file_path = os.path.join(self.root, reduce(os.path.join, parts, "") + ".js") + if addon_name: + result.append(AddonTestCase(tst, file_path, arch, mode, self.context, + self, self.additional_flags, addon_name)) + else: + result.append(SimpleTestCase(tst, file_path, arch, mode, self.context, + self, self.additional_flags)) return result class AbortTestConfiguration(SimpleTestConfiguration): diff --git a/tools/build_addons.py b/tools/build_addons.py index b8e36078d236cb..e985d6f67a4625 100755 --- a/tools/build_addons.py +++ b/tools/build_addons.py @@ -58,8 +58,8 @@ def node_gyp_rebuild(test_dir): # We buffer the output and print it out once the process is done in order # to avoid interleaved output from multiple builds running at once. - return_code = process.wait() stdout, stderr = process.communicate() + return_code = process.returncode if return_code != 0: print(f'Failed to build addon in {test_dir}:') if stdout: diff --git a/vcbuild.bat b/vcbuild.bat index 18b0f3aa6fdaed..5cb863e3c1bcc6 100644 --- a/vcbuild.bat +++ b/vcbuild.bat @@ -752,7 +752,7 @@ if errorlevel 1 goto exit goto lint-cpp :lint-cpp -if not defined lint_cpp goto lint-js +if not defined lint_cpp goto lint-js-build if defined NODEJS_MAKE goto run-make-lint where make > NUL 2>&1 && make -v | findstr /C:"GNU Make" 1> NUL if "%ERRORLEVEL%"=="0" set "NODEJS_MAKE=make PYTHON=python" & goto run-make-lint @@ -760,7 +760,7 @@ where wsl > NUL 2>&1 if "%ERRORLEVEL%"=="0" set "NODEJS_MAKE=wsl make" & goto run-make-lint echo Could not find GNU Make, needed for linting C/C++ echo Alternatively, you can use WSL -goto lint-js +goto lint-js-build :run-make-lint %NODEJS_MAKE% lint-cpp