/* file sqlmath-node_api.h */ /*jslint-disable*/ /* shRawLibFetch { "fetchList": [ { "url": "https://github.com/nodejs/node/blob/v14.17.5/src/node_api.h" }, { "url": "https://github.com/nodejs/node/blob/v14.17.5/src/js_native_api.h" }, { "url": "https://github.com/nodejs/node/blob/v14.17.5/src/node_api_types.h" }, { "url": "https://github.com/nodejs/node/blob/v14.17.5/src/js_native_api_types.h" }, { "url": "https://github.com/nodejs/node/blob/v14.17.5/src/node_api.cc" } ] } */ /* repo https://github.com/nodejs/node/tree/v14.17.5 committed 2021-08-11T01:09:31Z */ /* file https://github.com/nodejs/node/blob/v14.17.5/src/node_api.h */ #ifndef SRC_NODE_API_H_ #define SRC_NODE_API_H_ #ifdef BUILDING_NODE_EXTENSION #ifdef _WIN32 // Building native module against node #define NAPI_EXTERN __declspec(dllimport) #elif defined(__wasm32__) #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 # define NAPI_MODULE_EXPORT __attribute__((visibility("default"))) #endif #if defined(__GNUC__) # define NAPI_NO_RETURN __attribute__((noreturn)) #elif defined(_WIN32) # define NAPI_NO_RETURN __declspec(noreturn) #else # define NAPI_NO_RETURN #endif typedef napi_value (*napi_addon_register_func)(napi_env env, napi_value exports); 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 #if defined(_MSC_VER) #pragma section(".CRT$XCU", read) #define NAPI_C_CTOR(fn) \ static void __cdecl fn(void); \ __declspec(dllexport, allocate(".CRT$XCU")) void(__cdecl * fn##_)(void) = \ fn; \ static void __cdecl fn(void) #else #define NAPI_C_CTOR(fn) \ static void fn(void) __attribute__((constructor)); \ static void fn(void) #endif #define NAPI_MODULE_X(modname, regfunc, priv, flags) \ EXTERN_C_START \ static napi_module _module = \ { \ NAPI_MODULE_VERSION, \ flags, \ __FILE__, \ regfunc, \ #modname, \ priv, \ {0}, \ }; \ NAPI_C_CTOR(_register_ ## modname) { \ napi_module_register(&_module); \ } \ EXTERN_C_END #define NAPI_MODULE_INITIALIZER_X(base, version) \ NAPI_MODULE_INITIALIZER_X_HELPER(base, version) #define NAPI_MODULE_INITIALIZER_X_HELPER(base, version) base##version #ifdef __wasm32__ #define NAPI_WASM_INITIALIZER \ NAPI_MODULE_INITIALIZER_X(napi_register_wasm_v, NAPI_MODULE_VERSION) #define NAPI_MODULE(modname, regfunc) \ EXTERN_C_START \ NAPI_MODULE_EXPORT napi_value NAPI_WASM_INITIALIZER(napi_env env, \ napi_value exports) { \ return regfunc(env, exports); \ } \ EXTERN_C_END #else #define NAPI_MODULE(modname, regfunc) \ NAPI_MODULE_X(modname, regfunc, NULL, 0) // NOLINT (readability/null_usage) #endif #define NAPI_MODULE_INITIALIZER_BASE napi_register_module_v #define NAPI_MODULE_INITIALIZER \ NAPI_MODULE_INITIALIZER_X(NAPI_MODULE_INITIALIZER_BASE, \ NAPI_MODULE_VERSION) #define NAPI_MODULE_INIT() \ EXTERN_C_START \ NAPI_MODULE_EXPORT napi_value \ NAPI_MODULE_INITIALIZER(napi_env env, napi_value exports); \ EXTERN_C_END \ NAPI_MODULE(NODE_GYP_MODULE_NAME, NAPI_MODULE_INITIALIZER) \ napi_value NAPI_MODULE_INITIALIZER(napi_env env, \ napi_value exports) EXTERN_C_START NAPI_EXTERN void napi_module_register(napi_module* mod); NAPI_EXTERN NAPI_NO_RETURN void napi_fatal_error(const char* location, size_t location_len, const char* message, size_t message_len); // Methods for custom handling of async operations NAPI_EXTERN napi_status napi_async_init(napi_env env, napi_value async_resource, napi_value async_resource_name, napi_async_context* result); NAPI_EXTERN napi_status napi_async_destroy(napi_env env, napi_async_context async_context); NAPI_EXTERN napi_status 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); // Methods to provide node::Buffer functionality with napi types NAPI_EXTERN napi_status napi_create_buffer(napi_env env, size_t length, void** data, napi_value* result); NAPI_EXTERN napi_status napi_create_external_buffer(napi_env env, size_t length, void* data, napi_finalize finalize_cb, void* finalize_hint, napi_value* result); NAPI_EXTERN napi_status napi_create_buffer_copy(napi_env env, size_t length, const void* data, void** result_data, napi_value* result); NAPI_EXTERN napi_status napi_is_buffer(napi_env env, napi_value value, bool* result); NAPI_EXTERN napi_status napi_get_buffer_info(napi_env env, napi_value value, void** data, size_t* length); // Methods to manage simple async operations NAPI_EXTERN napi_status 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_delete_async_work(napi_env env, napi_async_work work); NAPI_EXTERN napi_status napi_queue_async_work(napi_env env, napi_async_work work); NAPI_EXTERN napi_status napi_cancel_async_work(napi_env env, napi_async_work work); // version management NAPI_EXTERN napi_status napi_get_node_version(napi_env env, const napi_node_version** version); #if NAPI_VERSION >= 2 // Return the current libuv event loop for a given environment NAPI_EXTERN napi_status napi_get_uv_event_loop(napi_env env, struct uv_loop_s** loop); #endif // NAPI_VERSION >= 2 #if NAPI_VERSION >= 3 NAPI_EXTERN napi_status napi_fatal_exception(napi_env env, napi_value err); NAPI_EXTERN napi_status napi_add_env_cleanup_hook(napi_env env, void (*fun)(void* arg), void* arg); NAPI_EXTERN napi_status napi_remove_env_cleanup_hook(napi_env env, void (*fun)(void* arg), void* arg); NAPI_EXTERN napi_status napi_open_callback_scope(napi_env env, napi_value resource_object, napi_async_context context, napi_callback_scope* result); NAPI_EXTERN napi_status napi_close_callback_scope(napi_env env, napi_callback_scope scope); #endif // NAPI_VERSION >= 3 #if NAPI_VERSION >= 4 #ifndef __wasm32__ // Calling into JS from other threads NAPI_EXTERN napi_status napi_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_EXTERN napi_status napi_get_threadsafe_function_context(napi_threadsafe_function func, void** result); NAPI_EXTERN napi_status napi_call_threadsafe_function(napi_threadsafe_function func, void* data, napi_threadsafe_function_call_mode is_blocking); NAPI_EXTERN napi_status napi_acquire_threadsafe_function(napi_threadsafe_function func); NAPI_EXTERN napi_status napi_release_threadsafe_function(napi_threadsafe_function func, napi_threadsafe_function_release_mode mode); NAPI_EXTERN napi_status napi_unref_threadsafe_function(napi_env env, napi_threadsafe_function func); NAPI_EXTERN napi_status napi_ref_threadsafe_function(napi_env env, napi_threadsafe_function func); #endif // __wasm32__ #endif // NAPI_VERSION >= 4 #if NAPI_VERSION >= 8 NAPI_EXTERN napi_status napi_add_async_cleanup_hook( napi_env env, napi_async_cleanup_hook hook, void* arg, napi_async_cleanup_hook_handle* remove_handle); NAPI_EXTERN napi_status napi_remove_async_cleanup_hook( napi_async_cleanup_hook_handle remove_handle); #endif // NAPI_VERSION >= 8 EXTERN_C_END #endif // SRC_NODE_API_H_ /* file https://github.com/nodejs/node/blob/v14.17.5/src/js_native_api.h */ #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) // Use INT_MAX, this should only be consumed by the pre-processor anyway. #define NAPI_VERSION_EXPERIMENTAL 2147483647 #ifndef NAPI_VERSION #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. #define NAPI_VERSION 8 #endif #endif #include "js_native_api_types.h" // If you need __declspec(dllimport), either include instead, or // define NAPI_EXTERN as __declspec(dllimport) on the compiler's command line. #ifndef NAPI_EXTERN #ifdef _WIN32 #define NAPI_EXTERN __declspec(dllexport) #elif defined(__wasm32__) #define NAPI_EXTERN __attribute__((visibility("default"))) \ __attribute__((__import_module__("napi"))) #else #define NAPI_EXTERN __attribute__((visibility("default"))) #endif #endif #define NAPI_AUTO_LENGTH SIZE_MAX #ifdef __cplusplus #define EXTERN_C_START extern "C" { #define EXTERN_C_END } #else #define EXTERN_C_START #define EXTERN_C_END #endif EXTERN_C_START NAPI_EXTERN napi_status napi_get_last_error_info(napi_env env, const napi_extended_error_info** result); // Getters for defined singletons NAPI_EXTERN napi_status napi_get_undefined(napi_env env, napi_value* result); NAPI_EXTERN napi_status napi_get_null(napi_env env, napi_value* result); NAPI_EXTERN napi_status napi_get_global(napi_env env, napi_value* result); NAPI_EXTERN napi_status napi_get_boolean(napi_env env, bool value, napi_value* result); // Methods to create Primitive types/Objects NAPI_EXTERN napi_status napi_create_object(napi_env env, napi_value* result); NAPI_EXTERN napi_status napi_create_array(napi_env env, napi_value* result); NAPI_EXTERN napi_status napi_create_array_with_length(napi_env env, size_t length, napi_value* result); NAPI_EXTERN napi_status napi_create_double(napi_env env, double value, napi_value* result); NAPI_EXTERN napi_status napi_create_int32(napi_env env, int32_t value, napi_value* result); NAPI_EXTERN napi_status napi_create_uint32(napi_env env, uint32_t value, napi_value* result); NAPI_EXTERN napi_status napi_create_int64(napi_env env, int64_t value, napi_value* result); NAPI_EXTERN napi_status napi_create_string_latin1(napi_env env, const char* str, size_t length, napi_value* result); NAPI_EXTERN napi_status napi_create_string_utf8(napi_env env, const char* str, size_t length, napi_value* result); NAPI_EXTERN napi_status napi_create_string_utf16(napi_env env, const char16_t* str, size_t length, napi_value* result); NAPI_EXTERN napi_status napi_create_symbol(napi_env env, napi_value description, napi_value* result); NAPI_EXTERN napi_status napi_create_function(napi_env env, const char* utf8name, size_t length, napi_callback cb, void* data, napi_value* result); NAPI_EXTERN napi_status napi_create_error(napi_env env, napi_value code, napi_value msg, napi_value* result); NAPI_EXTERN napi_status napi_create_type_error(napi_env env, napi_value code, napi_value msg, napi_value* result); NAPI_EXTERN napi_status napi_create_range_error(napi_env env, napi_value code, napi_value msg, napi_value* result); // Methods to get the native napi_value from Primitive type NAPI_EXTERN napi_status napi_typeof(napi_env env, napi_value value, napi_valuetype* result); NAPI_EXTERN napi_status napi_get_value_double(napi_env env, napi_value value, double* result); NAPI_EXTERN napi_status napi_get_value_int32(napi_env env, napi_value value, int32_t* result); NAPI_EXTERN napi_status napi_get_value_uint32(napi_env env, napi_value value, uint32_t* result); NAPI_EXTERN napi_status napi_get_value_int64(napi_env env, napi_value value, int64_t* result); NAPI_EXTERN napi_status napi_get_value_bool(napi_env env, napi_value value, bool* result); // Copies LATIN-1 encoded bytes from a string into a buffer. NAPI_EXTERN napi_status napi_get_value_string_latin1(napi_env env, napi_value value, char* buf, size_t bufsize, size_t* result); // Copies UTF-8 encoded bytes from a string into a buffer. NAPI_EXTERN napi_status napi_get_value_string_utf8(napi_env env, napi_value value, char* buf, size_t bufsize, size_t* result); // Copies UTF-16 encoded bytes from a string into a buffer. NAPI_EXTERN napi_status napi_get_value_string_utf16(napi_env env, napi_value value, char16_t* buf, size_t bufsize, size_t* result); // Methods to coerce values // These APIs may execute user scripts NAPI_EXTERN napi_status napi_coerce_to_bool(napi_env env, napi_value value, napi_value* result); NAPI_EXTERN napi_status napi_coerce_to_number(napi_env env, napi_value value, napi_value* result); NAPI_EXTERN napi_status napi_coerce_to_object(napi_env env, napi_value value, napi_value* result); NAPI_EXTERN napi_status napi_coerce_to_string(napi_env env, napi_value value, napi_value* result); // Methods to work with Objects NAPI_EXTERN napi_status napi_get_prototype(napi_env env, napi_value object, napi_value* result); NAPI_EXTERN napi_status napi_get_property_names(napi_env env, napi_value object, napi_value* result); NAPI_EXTERN napi_status napi_set_property(napi_env env, napi_value object, napi_value key, napi_value value); NAPI_EXTERN napi_status napi_has_property(napi_env env, napi_value object, napi_value key, bool* result); NAPI_EXTERN napi_status napi_get_property(napi_env env, napi_value object, napi_value key, napi_value* result); NAPI_EXTERN napi_status napi_delete_property(napi_env env, napi_value object, napi_value key, bool* result); NAPI_EXTERN napi_status napi_has_own_property(napi_env env, napi_value object, napi_value key, bool* result); NAPI_EXTERN napi_status napi_set_named_property(napi_env env, napi_value object, const char* utf8name, napi_value value); NAPI_EXTERN napi_status napi_has_named_property(napi_env env, napi_value object, const char* utf8name, bool* result); NAPI_EXTERN napi_status napi_get_named_property(napi_env env, napi_value object, const char* utf8name, napi_value* result); NAPI_EXTERN napi_status napi_set_element(napi_env env, napi_value object, uint32_t index, napi_value value); NAPI_EXTERN napi_status napi_has_element(napi_env env, napi_value object, uint32_t index, bool* result); NAPI_EXTERN napi_status napi_get_element(napi_env env, napi_value object, uint32_t index, napi_value* result); NAPI_EXTERN napi_status napi_delete_element(napi_env env, napi_value object, uint32_t index, bool* result); NAPI_EXTERN napi_status napi_define_properties(napi_env env, napi_value object, size_t property_count, const napi_property_descriptor* properties); // Methods to work with Arrays NAPI_EXTERN napi_status napi_is_array(napi_env env, napi_value value, bool* result); NAPI_EXTERN napi_status napi_get_array_length(napi_env env, napi_value value, uint32_t* result); // Methods to compare values NAPI_EXTERN napi_status napi_strict_equals(napi_env env, napi_value lhs, napi_value rhs, bool* result); // Methods to work with Functions NAPI_EXTERN napi_status napi_call_function(napi_env env, napi_value recv, napi_value func, size_t argc, const napi_value* argv, napi_value* result); NAPI_EXTERN napi_status napi_new_instance(napi_env env, napi_value constructor, size_t argc, const napi_value* argv, napi_value* result); NAPI_EXTERN napi_status napi_instanceof(napi_env env, napi_value object, napi_value constructor, bool* result); // Methods to work with napi_callbacks // Gets all callback info in a single call. (Ugly, but faster.) NAPI_EXTERN napi_status napi_get_cb_info( 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. NAPI_EXTERN napi_status napi_get_new_target(napi_env env, napi_callback_info cbinfo, napi_value* result); NAPI_EXTERN napi_status napi_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); // Methods to work with external data objects NAPI_EXTERN napi_status napi_wrap(napi_env env, napi_value js_object, void* native_object, napi_finalize finalize_cb, void* finalize_hint, napi_ref* result); NAPI_EXTERN napi_status napi_unwrap(napi_env env, napi_value js_object, void** result); NAPI_EXTERN napi_status napi_remove_wrap(napi_env env, napi_value js_object, void** result); NAPI_EXTERN napi_status napi_create_external(napi_env env, void* data, napi_finalize finalize_cb, void* finalize_hint, napi_value* result); NAPI_EXTERN napi_status napi_get_value_external(napi_env env, napi_value value, void** 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_create_reference(napi_env env, napi_value value, uint32_t initial_refcount, napi_ref* 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_delete_reference(napi_env env, napi_ref ref); // Increments the reference count, optionally returning the resulting count. // After this call the reference will be a strong reference because its // 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_EXTERN napi_status napi_reference_ref(napi_env env, napi_ref ref, uint32_t* 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 // at any time if there are no other references. Calling this when the // refcount is already 0 results in an error. NAPI_EXTERN napi_status napi_reference_unref(napi_env env, napi_ref ref, uint32_t* 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_get_reference_value(napi_env env, napi_ref ref, napi_value* result); NAPI_EXTERN napi_status napi_open_handle_scope(napi_env env, napi_handle_scope* result); NAPI_EXTERN napi_status napi_close_handle_scope(napi_env env, napi_handle_scope scope); NAPI_EXTERN napi_status napi_open_escapable_handle_scope(napi_env env, napi_escapable_handle_scope* result); NAPI_EXTERN napi_status napi_close_escapable_handle_scope(napi_env env, napi_escapable_handle_scope scope); NAPI_EXTERN napi_status napi_escape_handle(napi_env env, napi_escapable_handle_scope scope, napi_value escapee, napi_value* result); // Methods to support error handling NAPI_EXTERN napi_status napi_throw(napi_env env, napi_value error); NAPI_EXTERN napi_status napi_throw_error(napi_env env, const char* code, const char* msg); NAPI_EXTERN napi_status napi_throw_type_error(napi_env env, const char* code, const char* msg); NAPI_EXTERN napi_status napi_throw_range_error(napi_env env, const char* code, const char* msg); NAPI_EXTERN napi_status napi_is_error(napi_env env, napi_value value, bool* result); // Methods to support catching exceptions NAPI_EXTERN napi_status napi_is_exception_pending(napi_env env, bool* result); NAPI_EXTERN napi_status napi_get_and_clear_last_exception(napi_env env, napi_value* result); // Methods to work with array buffers and typed arrays NAPI_EXTERN napi_status napi_is_arraybuffer(napi_env env, napi_value value, bool* result); NAPI_EXTERN napi_status napi_create_arraybuffer(napi_env env, size_t byte_length, void** data, napi_value* result); NAPI_EXTERN napi_status napi_create_external_arraybuffer(napi_env env, void* external_data, size_t byte_length, napi_finalize finalize_cb, void* finalize_hint, napi_value* result); NAPI_EXTERN napi_status napi_get_arraybuffer_info(napi_env env, napi_value arraybuffer, void** data, size_t* byte_length); NAPI_EXTERN napi_status napi_is_typedarray(napi_env env, napi_value value, bool* result); NAPI_EXTERN napi_status 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_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_EXTERN napi_status napi_create_dataview(napi_env env, size_t length, napi_value arraybuffer, size_t byte_offset, napi_value* result); NAPI_EXTERN napi_status napi_is_dataview(napi_env env, napi_value value, bool* result); NAPI_EXTERN napi_status napi_get_dataview_info(napi_env env, napi_value dataview, size_t* bytelength, void** data, napi_value* arraybuffer, size_t* byte_offset); // version management NAPI_EXTERN napi_status napi_get_version(napi_env env, uint32_t* result); // Promises NAPI_EXTERN napi_status napi_create_promise(napi_env env, napi_deferred* deferred, napi_value* promise); NAPI_EXTERN napi_status napi_resolve_deferred(napi_env env, napi_deferred deferred, napi_value resolution); NAPI_EXTERN napi_status napi_reject_deferred(napi_env env, napi_deferred deferred, napi_value rejection); NAPI_EXTERN napi_status napi_is_promise(napi_env env, napi_value value, bool* is_promise); // Running a script NAPI_EXTERN napi_status napi_run_script(napi_env env, napi_value script, napi_value* result); // Memory management NAPI_EXTERN napi_status napi_adjust_external_memory(napi_env env, int64_t change_in_bytes, int64_t* adjusted_value); #if NAPI_VERSION >= 5 // Dates NAPI_EXTERN napi_status napi_create_date(napi_env env, double time, napi_value* result); NAPI_EXTERN napi_status napi_is_date(napi_env env, napi_value value, bool* is_date); NAPI_EXTERN napi_status napi_get_date_value(napi_env env, napi_value value, double* result); // Add finalizer for pointer NAPI_EXTERN napi_status napi_add_finalizer(napi_env env, napi_value js_object, void* native_object, napi_finalize finalize_cb, void* finalize_hint, napi_ref* result); #endif // NAPI_VERSION >= 5 #if NAPI_VERSION >= 6 // BigInt NAPI_EXTERN napi_status napi_create_bigint_int64(napi_env env, int64_t value, napi_value* result); NAPI_EXTERN napi_status napi_create_bigint_uint64(napi_env env, uint64_t value, napi_value* result); NAPI_EXTERN napi_status napi_create_bigint_words(napi_env env, int sign_bit, size_t word_count, const uint64_t* words, napi_value* result); NAPI_EXTERN napi_status napi_get_value_bigint_int64(napi_env env, napi_value value, int64_t* result, bool* lossless); NAPI_EXTERN napi_status napi_get_value_bigint_uint64(napi_env env, napi_value value, uint64_t* result, bool* lossless); NAPI_EXTERN napi_status napi_get_value_bigint_words(napi_env env, napi_value value, int* sign_bit, size_t* word_count, uint64_t* words); // Object NAPI_EXTERN napi_status 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); // Instance data NAPI_EXTERN napi_status napi_set_instance_data(napi_env env, void* data, napi_finalize finalize_cb, void* finalize_hint); NAPI_EXTERN napi_status napi_get_instance_data(napi_env env, void** data); #endif // NAPI_VERSION >= 6 #if NAPI_VERSION >= 7 // ArrayBuffer detaching NAPI_EXTERN napi_status napi_detach_arraybuffer(napi_env env, napi_value arraybuffer); NAPI_EXTERN napi_status napi_is_detached_arraybuffer(napi_env env, napi_value value, bool* result); #endif // NAPI_VERSION >= 7 #if NAPI_VERSION >= 8 // Type tagging NAPI_EXTERN napi_status napi_type_tag_object(napi_env env, napi_value value, const napi_type_tag* type_tag); NAPI_EXTERN napi_status napi_check_object_type_tag(napi_env env, napi_value value, const napi_type_tag* type_tag, bool* result); NAPI_EXTERN napi_status napi_object_freeze(napi_env env, napi_value object); NAPI_EXTERN napi_status napi_object_seal(napi_env env, napi_value object); #endif // NAPI_VERSION >= 8 EXTERN_C_END #endif // SRC_JS_NATIVE_API_H_ /* file https://github.com/nodejs/node/blob/v14.17.5/src/node_api_types.h */ #ifndef SRC_NODE_API_TYPES_H_ #define SRC_NODE_API_TYPES_H_ #include "js_native_api_types.h" typedef struct napi_callback_scope__* napi_callback_scope; typedef struct napi_async_context__* napi_async_context; typedef struct napi_async_work__* napi_async_work; #if NAPI_VERSION >= 4 typedef struct napi_threadsafe_function__* napi_threadsafe_function; #endif // NAPI_VERSION >= 4 #if NAPI_VERSION >= 4 typedef enum { napi_tsfn_release, napi_tsfn_abort } napi_threadsafe_function_release_mode; typedef enum { napi_tsfn_nonblocking, napi_tsfn_blocking } napi_threadsafe_function_call_mode; #endif // NAPI_VERSION >= 4 typedef void (*napi_async_execute_callback)(napi_env env, void* data); typedef void (*napi_async_complete_callback)(napi_env env, napi_status status, void* data); #if NAPI_VERSION >= 4 typedef void (*napi_threadsafe_function_call_js)(napi_env env, napi_value js_callback, void* context, void* data); #endif // NAPI_VERSION >= 4 typedef struct { uint32_t major; uint32_t minor; uint32_t patch; const char* release; } napi_node_version; #if NAPI_VERSION >= 8 typedef struct napi_async_cleanup_hook_handle__* napi_async_cleanup_hook_handle; typedef void (*napi_async_cleanup_hook)(napi_async_cleanup_hook_handle handle, void* data); #endif // NAPI_VERSION >= 8 #endif // SRC_NODE_API_TYPES_H_ /* file https://github.com/nodejs/node/blob/v14.17.5/src/js_native_api_types.h */ #ifndef SRC_JS_NATIVE_API_TYPES_H_ #define SRC_JS_NATIVE_API_TYPES_H_ // 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) #if !defined __cplusplus || (defined(_MSC_VER) && _MSC_VER < 1900) typedef uint16_t char16_t; #endif // JSVM API types are all opaque pointers for ABI stability // typedef undefined structs instead of void* for compile time type safety typedef struct napi_env__* napi_env; typedef struct napi_value__* napi_value; typedef struct napi_ref__* napi_ref; typedef struct napi_handle_scope__* napi_handle_scope; typedef struct napi_escapable_handle_scope__* napi_escapable_handle_scope; typedef struct napi_callback_info__* napi_callback_info; typedef struct napi_deferred__* napi_deferred; typedef enum { napi_default = 0, napi_writable = 1 << 0, napi_enumerable = 1 << 1, napi_configurable = 1 << 2, // Used with napi_define_class to distinguish static properties // from instance properties. Ignored by napi_define_properties. napi_static = 1 << 10, #if NAPI_VERSION >= 8 // Default for class methods. napi_default_method = napi_writable | napi_configurable, // Default for object properties, like in JS obj[prop]. napi_default_jsproperty = napi_writable | napi_enumerable | napi_configurable, #endif // NAPI_VERSION >= 8 } napi_property_attributes; typedef enum { // ES6 types (corresponds to typeof) napi_undefined, napi_null, napi_boolean, napi_number, napi_string, napi_symbol, napi_object, napi_function, napi_external, napi_bigint, } napi_valuetype; typedef enum { napi_int8_array, napi_uint8_array, napi_uint8_clamped_array, napi_int16_array, napi_uint16_array, napi_int32_array, napi_uint32_array, napi_float32_array, napi_float64_array, napi_bigint64_array, napi_biguint64_array, } napi_typedarray_type; typedef enum { napi_ok, napi_invalid_arg, napi_object_expected, napi_string_expected, napi_name_expected, napi_function_expected, napi_number_expected, napi_boolean_expected, napi_array_expected, napi_generic_failure, napi_pending_exception, napi_cancelled, napi_escape_called_twice, napi_handle_scope_mismatch, napi_callback_scope_mismatch, napi_queue_full, napi_closing, napi_bigint_expected, napi_date_expected, napi_arraybuffer_expected, napi_detachable_arraybuffer_expected, napi_would_deadlock // unused } napi_status; // Note: when adding a new enum value to `napi_status`, please also update // * `const int last_status` in the definition of `napi_get_last_error_info()' // in file js_native_api_v8.cc. // * `const char* error_messages[]` in file js_native_api_v8.cc with a brief // message explaining the error. // * the definition of `napi_status` in doc/api/n-api.md to reflect the newly // added value(s). typedef napi_value (*napi_callback)(napi_env env, napi_callback_info info); typedef void (*napi_finalize)(napi_env env, void* finalize_data, void* finalize_hint); typedef struct { // One of utf8name or name should be NULL. const char* utf8name; napi_value name; napi_callback method; napi_callback getter; napi_callback setter; napi_value value; napi_property_attributes attributes; void* data; } napi_property_descriptor; typedef struct { const char* error_message; void* engine_reserved; uint32_t engine_error_code; napi_status error_code; } napi_extended_error_info; #if NAPI_VERSION >= 6 typedef enum { napi_key_include_prototypes, napi_key_own_only } napi_key_collection_mode; typedef enum { napi_key_all_properties = 0, napi_key_writable = 1, napi_key_enumerable = 1 << 1, napi_key_configurable = 1 << 2, napi_key_skip_strings = 1 << 3, napi_key_skip_symbols = 1 << 4 } napi_key_filter; typedef enum { napi_key_keep_numbers, napi_key_numbers_to_strings } napi_key_conversion; #endif // NAPI_VERSION >= 6 #if NAPI_VERSION >= 8 typedef struct { uint64_t lower; uint64_t upper; } napi_type_tag; #endif // NAPI_VERSION >= 8 #endif // SRC_JS_NATIVE_API_TYPES_H_ /* file https://github.com/nodejs/node/blob/v14.17.5/src/node_api.cc */ #include "async_wrap-inl.h" #include "env-inl.h" #define NAPI_EXPERIMENTAL #include "js_native_api_v8.h" #include "memory_tracker-inl.h" #include "node_api.h" #include "node_binding.h" #include "node_buffer.h" #include "node_errors.h" #include "node_internals.h" #include "threadpoolwork-inl.h" #include "tracing/traced_value.h" #include "util-inl.h" #include #include struct node_napi_env__ : public napi_env__ { explicit node_napi_env__(v8::Local context): napi_env__(context) { CHECK_NOT_NULL(node_env()); } inline node::Environment* node_env() const { return node::Environment::GetCurrent(context()); } bool can_call_into_js() const override { return node_env()->can_call_into_js(); } v8::Maybe mark_arraybuffer_as_untransferable( v8::Local ab) const override { return ab->SetPrivate( context(), node_env()->untransferable_object_private_symbol(), v8::True(isolate)); } void CallFinalizer(napi_finalize cb, void* data, void* hint) override { // we need to keep the env live until the finalizer has been run // EnvRefHolder provides an exception safe wrapper to Ref and then // Unref once the lamba is freed EnvRefHolder liveEnv(static_cast(this)); node_env()->SetImmediate([=, liveEnv = std::move(liveEnv)] (node::Environment* node_env) { napi_env env = liveEnv.env(); v8::HandleScope handle_scope(env->isolate); v8::Context::Scope context_scope(env->context()); env->CallIntoModule([&](napi_env env) { cb(env, data, hint); }); }); } }; typedef node_napi_env__* node_napi_env; namespace v8impl { namespace { class BufferFinalizer : private Finalizer { public: // node::Buffer::FreeCallback static void FinalizeBufferCallback(char* data, void* hint) { std::unique_ptr finalizer{ static_cast(hint)}; finalizer->_finalize_data = data; node::Environment* node_env = static_cast(finalizer->_env)->node_env(); node_env->SetImmediate( [finalizer = std::move(finalizer)](node::Environment* env) { if (finalizer->_finalize_callback == nullptr) return; v8::HandleScope handle_scope(finalizer->_env->isolate); v8::Context::Scope context_scope(finalizer->_env->context()); finalizer->_env->CallIntoModule([&](napi_env env) { finalizer->_finalize_callback( env, finalizer->_finalize_data, finalizer->_finalize_hint); }); }); } struct Deleter { void operator()(BufferFinalizer* finalizer) { Finalizer::Delete(finalizer); } }; }; static inline napi_env NewEnv(v8::Local context) { node_napi_env result; result = new node_napi_env__(context); // 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, // 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. // For now, a per-Environment cleanup hook is the best we can do. result->node_env()->AddCleanupHook( [](void* arg) { static_cast(arg)->Unref(); }, static_cast(result)); return result; } static inline void trigger_fatal_exception( napi_env env, v8::Local local_err) { v8::Local local_msg = v8::Exception::CreateMessage(env->isolate, local_err); node::errors::TriggerUncaughtException(env->isolate, local_err, local_msg); } class ThreadSafeFunction : public node::AsyncResource { public: ThreadSafeFunction(v8::Local func, v8::Local resource, v8::Local name, size_t thread_count_, void* context_, size_t max_queue_size_, node_napi_env env_, void* finalize_data_, napi_finalize finalize_cb_, napi_threadsafe_function_call_js call_js_cb_): AsyncResource(env_->isolate, resource, *v8::String::Utf8Value(env_->isolate, name)), thread_count(thread_count_), is_closing(false), dispatch_state(kDispatchIdle), context(context_), max_queue_size(max_queue_size_), env(env_), finalize_data(finalize_data_), finalize_cb(finalize_cb_), call_js_cb(call_js_cb_ == nullptr ? CallJs : call_js_cb_), handles_closing(false) { ref.Reset(env->isolate, func); node::AddEnvironmentCleanupHook(env->isolate, Cleanup, this); env->Ref(); } ~ThreadSafeFunction() override { node::RemoveEnvironmentCleanupHook(env->isolate, Cleanup, this); env->Unref(); } // These methods can be called from any thread. napi_status Push(void* data, napi_threadsafe_function_call_mode mode) { node::Mutex::ScopedLock lock(this->mutex); while (queue.size() >= max_queue_size && max_queue_size > 0 && !is_closing) { if (mode == napi_tsfn_nonblocking) { return napi_queue_full; } cond->Wait(lock); } if (is_closing) { if (thread_count == 0) { return napi_invalid_arg; } else { thread_count--; return napi_closing; } } else { queue.push(data); Send(); return napi_ok; } } napi_status Acquire() { node::Mutex::ScopedLock lock(this->mutex); if (is_closing) { return napi_closing; } thread_count++; return napi_ok; } napi_status Release(napi_threadsafe_function_release_mode mode) { node::Mutex::ScopedLock lock(this->mutex); if (thread_count == 0) { return napi_invalid_arg; } thread_count--; if (thread_count == 0 || mode == napi_tsfn_abort) { if (!is_closing) { is_closing = (mode == napi_tsfn_abort); if (is_closing && max_queue_size > 0) { cond->Signal(lock); } Send(); } } return napi_ok; } void EmptyQueueAndDelete() { for (; !queue.empty() ; queue.pop()) { call_js_cb(nullptr, nullptr, context, queue.front()); } delete this; } // These methods must only be called from the loop thread. napi_status Init() { ThreadSafeFunction* ts_fn = this; uv_loop_t* loop = env->node_env()->event_loop(); if (uv_async_init(loop, &async, AsyncCb) == 0) { if (max_queue_size > 0) { cond = std::make_unique(); } if (max_queue_size == 0 || cond) { return napi_ok; } env->node_env()->CloseHandle( reinterpret_cast(&async), [](uv_handle_t* handle) -> void { ThreadSafeFunction* ts_fn = node::ContainerOf(&ThreadSafeFunction::async, reinterpret_cast(handle)); delete ts_fn; }); // Prevent the thread-safe function from being deleted here, because // the callback above will delete it. ts_fn = nullptr; } delete ts_fn; return napi_generic_failure; } napi_status Unref() { uv_unref(reinterpret_cast(&async)); return napi_ok; } napi_status Ref() { uv_ref(reinterpret_cast(&async)); return napi_ok; } inline void* Context() { return context; } protected: void Dispatch() { bool has_more = true; // Limit maximum synchronous iteration count to prevent event loop // starvation. See `src/node_messaging.cc` for an inspiration. unsigned int iterations_left = kMaxIterationCount; while (has_more && --iterations_left != 0) { dispatch_state = kDispatchRunning; has_more = DispatchOne(); // Send() was called while we were executing the JS function if (dispatch_state.exchange(kDispatchIdle) != kDispatchRunning) { has_more = true; } } if (has_more) { Send(); } } bool DispatchOne() { void* data = nullptr; bool popped_value = false; bool has_more = false; { node::Mutex::ScopedLock lock(this->mutex); if (is_closing) { CloseHandlesAndMaybeDelete(); } else { size_t size = queue.size(); if (size > 0) { data = queue.front(); queue.pop(); popped_value = true; if (size == max_queue_size && max_queue_size > 0) { cond->Signal(lock); } size--; } if (size == 0) { if (thread_count == 0) { is_closing = true; if (max_queue_size > 0) { cond->Signal(lock); } CloseHandlesAndMaybeDelete(); } } else { has_more = true; } } } if (popped_value) { v8::HandleScope scope(env->isolate); CallbackScope cb_scope(this); napi_value js_callback = nullptr; if (!ref.IsEmpty()) { v8::Local js_cb = v8::Local::New(env->isolate, ref); js_callback = v8impl::JsValueFromV8LocalValue(js_cb); } env->CallIntoModule([&](napi_env env) { call_js_cb(env, js_callback, context, data); }); } return has_more; } void Finalize() { v8::HandleScope scope(env->isolate); if (finalize_cb) { CallbackScope cb_scope(this); env->CallIntoModule([&](napi_env env) { finalize_cb(env, finalize_data, context); }); } EmptyQueueAndDelete(); } void CloseHandlesAndMaybeDelete(bool set_closing = false) { v8::HandleScope scope(env->isolate); if (set_closing) { node::Mutex::ScopedLock lock(this->mutex); is_closing = true; if (max_queue_size > 0) { cond->Signal(lock); } } if (handles_closing) { return; } handles_closing = true; env->node_env()->CloseHandle( reinterpret_cast(&async), [](uv_handle_t* handle) -> void { ThreadSafeFunction* ts_fn = node::ContainerOf(&ThreadSafeFunction::async, reinterpret_cast(handle)); ts_fn->Finalize(); }); } void Send() { // Ask currently running Dispatch() to make one more iteration unsigned char current_state = dispatch_state.fetch_or(kDispatchPending); if ((current_state & kDispatchRunning) == kDispatchRunning) { return; } CHECK_EQ(0, uv_async_send(&async)); } // Default way of calling into JavaScript. Used when ThreadSafeFunction is // without a call_js_cb_. static void CallJs(napi_env env, napi_value cb, void* context, void* data) { if (!(env == nullptr || cb == nullptr)) { napi_value recv; napi_status status; status = napi_get_undefined(env, &recv); if (status != napi_ok) { napi_throw_error(env, "ERR_NAPI_TSFN_GET_UNDEFINED", "Failed to retrieve undefined value"); return; } status = napi_call_function(env, recv, cb, 0, nullptr, nullptr); if (status != napi_ok && status != napi_pending_exception) { napi_throw_error(env, "ERR_NAPI_TSFN_CALL_JS", "Failed to call JS callback"); return; } } } static void AsyncCb(uv_async_t* async) { ThreadSafeFunction* ts_fn = node::ContainerOf(&ThreadSafeFunction::async, async); ts_fn->Dispatch(); } static void Cleanup(void* data) { reinterpret_cast(data) ->CloseHandlesAndMaybeDelete(true); } private: static const unsigned char kDispatchIdle = 0; static const unsigned char kDispatchRunning = 1 << 0; static const unsigned char kDispatchPending = 1 << 1; static const unsigned int kMaxIterationCount = 1000; // These are variables protected by the mutex. node::Mutex mutex; std::unique_ptr cond; std::queue queue; uv_async_t async; size_t thread_count; bool is_closing; std::atomic_uchar dispatch_state; // These are variables set once, upon creation, and then never again, which // means we don't need the mutex to read them. void* context; size_t max_queue_size; // These are variables accessed only from the loop thread. v8impl::Persistent ref; node_napi_env env; void* finalize_data; napi_finalize finalize_cb; napi_threadsafe_function_call_js call_js_cb; bool handles_closing; }; /** * Compared to node::AsyncResource, the resource object in AsyncContext is * gc-able. AsyncContext holds a weak reference to the resource object. * AsyncContext::MakeCallback doesn't implicitly set the receiver of the * callback to the resource object. */ class AsyncContext { public: AsyncContext(node_napi_env env, v8::Local resource_object, const v8::Local resource_name, bool externally_managed_resource) : env_(env) { async_id_ = node_env()->new_async_id(); trigger_async_id_ = node_env()->get_default_trigger_async_id(); resource_.Reset(node_env()->isolate(), resource_object); lost_reference_ = false; if (externally_managed_resource) { resource_.SetWeak( this, AsyncContext::WeakCallback, v8::WeakCallbackType::kParameter); } node::AsyncWrap::EmitAsyncInit(node_env(), resource_object, resource_name, async_id_, trigger_async_id_); } ~AsyncContext() { resource_.Reset(); lost_reference_ = true; node::AsyncWrap::EmitDestroy(node_env(), async_id_); } inline v8::MaybeLocal MakeCallback( v8::Local recv, const v8::Local callback, int argc, v8::Local argv[]) { EnsureReference(); return node::InternalMakeCallback(node_env(), resource(), recv, callback, argc, argv, {async_id_, trigger_async_id_}); } inline napi_callback_scope OpenCallbackScope() { EnsureReference(); napi_callback_scope it = reinterpret_cast(new CallbackScope(this)); env_->open_callback_scopes++; return it; } inline void EnsureReference() { if (lost_reference_) { const v8::HandleScope handle_scope(node_env()->isolate()); resource_.Reset(node_env()->isolate(), v8::Object::New(node_env()->isolate())); lost_reference_ = false; } } inline node::Environment* node_env() { return env_->node_env(); } inline v8::Local resource() { return resource_.Get(node_env()->isolate()); } inline node::async_context async_context() { return {async_id_, trigger_async_id_}; } static inline void CloseCallbackScope(node_napi_env env, napi_callback_scope s) { CallbackScope* callback_scope = reinterpret_cast(s); delete callback_scope; env->open_callback_scopes--; } static void WeakCallback(const v8::WeakCallbackInfo& data) { AsyncContext* async_context = data.GetParameter(); async_context->resource_.Reset(); async_context->lost_reference_ = true; } private: class CallbackScope : public node::CallbackScope { public: explicit CallbackScope(AsyncContext* async_context) : node::CallbackScope(async_context->node_env()->isolate(), async_context->resource_.Get( async_context->node_env()->isolate()), async_context->async_context()) {} }; node_napi_env env_; double async_id_; double trigger_async_id_; v8::Global resource_; bool lost_reference_; }; } // end of anonymous namespace } // end of namespace v8impl // Intercepts the Node-V8 module registration callback. Converts parameters // to NAPI equivalents and then calls the registration callback specified // by the NAPI module. static void napi_module_register_cb(v8::Local exports, v8::Local module, v8::Local context, void* priv) { napi_module_register_by_symbol(exports, module, context, static_cast(priv)->nm_register_func); } void napi_module_register_by_symbol(v8::Local exports, v8::Local module, v8::Local context, napi_addon_register_func init) { if (init == nullptr) { node::Environment* node_env = node::Environment::GetCurrent(context); CHECK_NOT_NULL(node_env); node_env->ThrowError( "Module has no declared entry point."); return; } // Create a new napi_env for this specific module. napi_env env = v8impl::NewEnv(context); napi_value _exports; env->CallIntoModule([&](napi_env env) { _exports = init(env, v8impl::JsValueFromV8LocalValue(exports)); }); // If register function returned a non-null exports object different from // the exports object we passed it, set that as the "exports" property of // the module. if (_exports != nullptr && _exports != v8impl::JsValueFromV8LocalValue(exports)) { napi_value _module = v8impl::JsValueFromV8LocalValue(module); napi_set_named_property(env, _module, "exports", _exports); } } namespace node { node_module napi_module_to_node_module(const napi_module* mod) { return { -1, mod->nm_flags | NM_F_DELETEME, nullptr, mod->nm_filename, nullptr, napi_module_register_cb, mod->nm_modname, const_cast(mod), // priv nullptr, }; } } // namespace node // Registers a NAPI module. void napi_module_register(napi_module* mod) { node::node_module* nm = new node::node_module( node::napi_module_to_node_module(mod)); node::node_module_register(nm); } napi_status napi_add_env_cleanup_hook(napi_env env, void (*fun)(void* arg), void* arg) { CHECK_ENV(env); CHECK_ARG(env, fun); node::AddEnvironmentCleanupHook(env->isolate, fun, arg); return napi_ok; } napi_status napi_remove_env_cleanup_hook(napi_env env, void (*fun)(void* arg), void* arg) { CHECK_ENV(env); CHECK_ARG(env, fun); node::RemoveEnvironmentCleanupHook(env->isolate, fun, arg); 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) { handle_ = node::AddEnvironmentCleanupHook(env->isolate, Hook, this); env->Ref(); } ~napi_async_cleanup_hook_handle__() { 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([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); handle->done_cb_ = done_cb; handle->done_data_ = done_data; handle->user_hook_(handle, handle->user_data_); } node::AsyncCleanupHookHandle handle_; napi_env env_ = nullptr; napi_async_cleanup_hook user_hook_ = nullptr; void* user_data_ = nullptr; void (*done_cb_)(void*) = nullptr; void* done_data_ = nullptr; }; napi_status napi_add_async_cleanup_hook( napi_env env, napi_async_cleanup_hook hook, void* arg, napi_async_cleanup_hook_handle* remove_handle) { CHECK_ENV(env); CHECK_ARG(env, hook); napi_async_cleanup_hook_handle__* handle = new napi_async_cleanup_hook_handle__(env, hook, arg); if (remove_handle != nullptr) *remove_handle = handle; return napi_clear_last_error(env); } napi_status napi_remove_async_cleanup_hook( napi_async_cleanup_hook_handle remove_handle) { if (remove_handle == nullptr) return napi_invalid_arg; delete remove_handle; return napi_ok; } napi_status napi_fatal_exception(napi_env env, napi_value err) { NAPI_PREAMBLE(env); CHECK_ARG(env, err); v8::Local local_err = v8impl::V8LocalValueFromJsValue(err); v8impl::trigger_fatal_exception(env, local_err); return napi_clear_last_error(env); } NAPI_NO_RETURN void napi_fatal_error(const char* location, size_t location_len, const char* message, size_t message_len) { std::string location_string; std::string message_string; if (location_len != NAPI_AUTO_LENGTH) { location_string.assign( const_cast(location), location_len); } else { location_string.assign( const_cast(location), strlen(location)); } if (message_len != NAPI_AUTO_LENGTH) { message_string.assign( const_cast(message), message_len); } else { message_string.assign( const_cast(message), strlen(message)); } node::FatalError(location_string.c_str(), message_string.c_str()); } napi_status 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. CHECK_ENV(env); CHECK_ARG(env, result); v8impl::AsyncContext* node_async_context = reinterpret_cast(async_context_handle); *result = node_async_context->OpenCallbackScope(); return napi_clear_last_error(env); } napi_status 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. 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); return napi_clear_last_error(env); } napi_status napi_async_init(napi_env env, napi_value async_resource, napi_value async_resource_name, napi_async_context* result) { CHECK_ENV(env); CHECK_ARG(env, async_resource_name); CHECK_ARG(env, result); v8::Isolate* isolate = env->isolate; v8::Local context = env->context(); v8::Local v8_resource; bool externally_managed_resource; if (async_resource != nullptr) { CHECK_TO_OBJECT(env, context, v8_resource, async_resource); externally_managed_resource = true; } else { v8_resource = v8::Object::New(isolate); externally_managed_resource = false; } v8::Local v8_resource_name; 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, externally_managed_resource); *result = reinterpret_cast(async_context); return napi_clear_last_error(env); } napi_status napi_async_destroy(napi_env env, napi_async_context async_context) { CHECK_ENV(env); CHECK_ARG(env, async_context); v8impl::AsyncContext* node_async_context = reinterpret_cast(async_context); delete node_async_context; return napi_clear_last_error(env); } napi_status 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_PREAMBLE(env); CHECK_ARG(env, recv); if (argc > 0) { CHECK_ARG(env, argv); } v8::Local context = env->context(); v8::Local v8recv; CHECK_TO_OBJECT(env, context, v8recv, recv); v8::Local v8func; CHECK_TO_FUNCTION(env, v8func, func); v8::MaybeLocal callback_result; if (async_context == nullptr) { callback_result = node::MakeCallback( env->isolate, v8recv, v8func, argc, reinterpret_cast*>(const_cast(argv)), {0, 0}); } else { v8impl::AsyncContext* node_async_context = reinterpret_cast(async_context); callback_result = node_async_context->MakeCallback( v8recv, v8func, argc, reinterpret_cast*>(const_cast(argv))); } if (try_catch.HasCaught()) { return napi_set_last_error(env, napi_pending_exception); } else { CHECK_MAYBE_EMPTY(env, callback_result, napi_generic_failure); if (result != nullptr) { *result = v8impl::JsValueFromV8LocalValue( callback_result.ToLocalChecked()); } } return GET_RETURN_STATUS(env); } napi_status napi_create_buffer(napi_env env, size_t length, void** data, napi_value* result) { NAPI_PREAMBLE(env); CHECK_ARG(env, result); v8::MaybeLocal maybe = node::Buffer::New(env->isolate, length); CHECK_MAYBE_EMPTY(env, maybe, napi_generic_failure); v8::Local buffer = maybe.ToLocalChecked(); *result = v8impl::JsValueFromV8LocalValue(buffer); if (data != nullptr) { *data = node::Buffer::Data(buffer); } return GET_RETURN_STATUS(env); } napi_status napi_create_external_buffer(napi_env env, size_t length, void* data, napi_finalize finalize_cb, void* finalize_hint, napi_value* result) { NAPI_PREAMBLE(env); CHECK_ARG(env, result); v8::Isolate* isolate = env->isolate; // The finalizer object will delete itself after invoking the callback. v8impl::Finalizer* finalizer = v8impl::Finalizer::New( env, finalize_cb, nullptr, finalize_hint, v8impl::Finalizer::kKeepEnvReference); v8::MaybeLocal maybe = node::Buffer::New( isolate, static_cast(data), length, v8impl::BufferFinalizer::FinalizeBufferCallback, finalizer); CHECK_MAYBE_EMPTY(env, maybe, napi_generic_failure); *result = v8impl::JsValueFromV8LocalValue(maybe.ToLocalChecked()); return GET_RETURN_STATUS(env); // Tell coverity that 'finalizer' should not be freed when we return // as it will be deleted when the buffer to which it is associated // is finalized. // coverity[leaked_storage] } napi_status napi_create_buffer_copy(napi_env env, size_t length, const void* data, void** result_data, napi_value* result) { NAPI_PREAMBLE(env); CHECK_ARG(env, result); v8::MaybeLocal maybe = node::Buffer::Copy( env->isolate, static_cast(data), length); CHECK_MAYBE_EMPTY(env, maybe, napi_generic_failure); v8::Local buffer = maybe.ToLocalChecked(); *result = v8impl::JsValueFromV8LocalValue(buffer); if (result_data != nullptr) { *result_data = node::Buffer::Data(buffer); } return GET_RETURN_STATUS(env); } napi_status napi_is_buffer(napi_env env, napi_value value, bool* result) { CHECK_ENV(env); CHECK_ARG(env, value); CHECK_ARG(env, result); *result = node::Buffer::HasInstance(v8impl::V8LocalValueFromJsValue(value)); return napi_clear_last_error(env); } napi_status napi_get_buffer_info(napi_env env, napi_value value, void** data, size_t* length) { CHECK_ENV(env); CHECK_ARG(env, value); v8::Local buffer = v8impl::V8LocalValueFromJsValue(value); if (data != nullptr) { *data = node::Buffer::Data(buffer); } if (length != nullptr) { *length = node::Buffer::Length(buffer); } return napi_clear_last_error(env); } napi_status napi_get_node_version(napi_env env, const napi_node_version** result) { CHECK_ENV(env); CHECK_ARG(env, result); static const napi_node_version version = { NODE_MAJOR_VERSION, NODE_MINOR_VERSION, NODE_PATCH_VERSION, NODE_RELEASE }; *result = &version; return napi_clear_last_error(env); } namespace { namespace uvimpl { static napi_status ConvertUVErrorCode(int code) { switch (code) { case 0: return napi_ok; case UV_EINVAL: return napi_invalid_arg; case UV_ECANCELED: return napi_cancelled; default: return napi_generic_failure; } } // 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, v8::Local async_resource, v8::Local async_resource_name, napi_async_execute_callback execute, napi_async_complete_callback complete = nullptr, void* data = nullptr) : AsyncResource(env->isolate, async_resource, *v8::String::Utf8Value(env->isolate, async_resource_name)), ThreadPoolWork(env->node_env()), _env(env), _data(data), _execute(execute), _complete(complete) { } ~Work() override = default; public: static Work* New(node_napi_env env, v8::Local async_resource, v8::Local async_resource_name, napi_async_execute_callback execute, napi_async_complete_callback complete, void* data) { return new Work(env, async_resource, async_resource_name, execute, complete, data); } static void Delete(Work* work) { delete work; } void DoThreadPoolWork() override { _execute(_env, _data); } void AfterThreadPoolWork(int status) override { if (_complete == nullptr) return; // Establish a handle scope here so that every callback doesn't have to. // Also it is needed for the exception-handling below. v8::HandleScope scope(_env->isolate); CallbackScope callback_scope(this); _env->CallIntoModule([&](napi_env env) { _complete(env, ConvertUVErrorCode(status), _data); }, [](napi_env env, v8::Local local_err) { // If there was an unhandled exception in the complete callback, // report it as a fatal exception. (There is no JavaScript on the // callstack that can possibly handle it.) v8impl::trigger_fatal_exception(env, local_err); }); // Note: Don't access `work` after this point because it was // likely deleted by the complete callback. } private: node_napi_env _env; void* _data; napi_async_execute_callback _execute; napi_async_complete_callback _complete; }; } // end of namespace uvimpl } // end of anonymous namespace #define CALL_UV(env, condition) \ do { \ int result = (condition); \ napi_status status = uvimpl::ConvertUVErrorCode(result); \ if (status != napi_ok) { \ return napi_set_last_error(env, status, result); \ } \ } while (0) napi_status 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) { CHECK_ENV(env); CHECK_ARG(env, execute); CHECK_ARG(env, result); v8::Local context = env->context(); v8::Local resource; if (async_resource != nullptr) { CHECK_TO_OBJECT(env, context, resource, async_resource); } else { resource = v8::Object::New(env->isolate); } v8::Local resource_name; CHECK_TO_STRING(env, context, resource_name, async_resource_name); uvimpl::Work* work = uvimpl::Work::New(reinterpret_cast(env), resource, resource_name, execute, complete, data); *result = reinterpret_cast(work); return napi_clear_last_error(env); } napi_status napi_delete_async_work(napi_env env, napi_async_work work) { CHECK_ENV(env); CHECK_ARG(env, work); uvimpl::Work::Delete(reinterpret_cast(work)); return napi_clear_last_error(env); } napi_status napi_get_uv_event_loop(napi_env env, uv_loop_t** loop) { CHECK_ENV(env); CHECK_ARG(env, loop); *loop = reinterpret_cast(env)->node_env()->event_loop(); return napi_clear_last_error(env); } napi_status napi_queue_async_work(napi_env env, napi_async_work work) { CHECK_ENV(env); CHECK_ARG(env, work); uv_loop_t* event_loop = nullptr; STATUS_CALL(napi_get_uv_event_loop(env, &event_loop)); uvimpl::Work* w = reinterpret_cast(work); w->ScheduleWork(); return napi_clear_last_error(env); } napi_status napi_cancel_async_work(napi_env env, napi_async_work work) { CHECK_ENV(env); CHECK_ARG(env, work); uvimpl::Work* w = reinterpret_cast(work); CALL_UV(env, w->CancelWork()); return napi_clear_last_error(env); } napi_status napi_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) { CHECK_ENV(env); CHECK_ARG(env, async_resource_name); RETURN_STATUS_IF_FALSE(env, initial_thread_count > 0, napi_invalid_arg); CHECK_ARG(env, result); napi_status status = napi_ok; v8::Local v8_func; if (func == nullptr) { CHECK_ARG(env, call_js_cb); } else { CHECK_TO_FUNCTION(env, v8_func, func); } v8::Local v8_context = env->context(); v8::Local v8_resource; if (async_resource == nullptr) { v8_resource = v8::Object::New(env->isolate); } else { CHECK_TO_OBJECT(env, v8_context, v8_resource, async_resource); } v8::Local v8_name; CHECK_TO_STRING(env, v8_context, v8_name, async_resource_name); v8impl::ThreadSafeFunction* ts_fn = new v8impl::ThreadSafeFunction(v8_func, v8_resource, v8_name, initial_thread_count, context, max_queue_size, reinterpret_cast(env), thread_finalize_data, thread_finalize_cb, call_js_cb); if (ts_fn == nullptr) { status = napi_generic_failure; } else { // Init deletes ts_fn upon failure. status = ts_fn->Init(); if (status == napi_ok) { *result = reinterpret_cast(ts_fn); } } return napi_set_last_error(env, status); } napi_status napi_get_threadsafe_function_context(napi_threadsafe_function func, void** result) { CHECK_NOT_NULL(func); CHECK_NOT_NULL(result); *result = reinterpret_cast(func)->Context(); return napi_ok; } napi_status 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); } napi_status napi_acquire_threadsafe_function(napi_threadsafe_function func) { CHECK_NOT_NULL(func); return reinterpret_cast(func)->Acquire(); } napi_status napi_release_threadsafe_function(napi_threadsafe_function func, napi_threadsafe_function_release_mode mode) { CHECK_NOT_NULL(func); return reinterpret_cast(func)->Release(mode); } napi_status napi_unref_threadsafe_function(napi_env env, napi_threadsafe_function func) { CHECK_NOT_NULL(func); return reinterpret_cast(func)->Unref(); } napi_status napi_ref_threadsafe_function(napi_env env, napi_threadsafe_function func) { CHECK_NOT_NULL(func); return reinterpret_cast(func)->Ref(); } /* file none */ /*jslint-enable*/ /* file sqlmath-old.c */ // LINT_C_FILE /* * 2018-04-19 * * The author disclaims copyright to this source code. In place of * a legal notice, here is a blessing: * * May you do good and not evil. * May you find forgiveness for yourself and forgive others. * May you share freely, never taking more than you give. * ************************************************************************ * * This file implements a template virtual-table. * Developers can make a copy of this file as a baseline for writing * new virtual tables and/or table-valued functions. * * Steps for writing a new virtual table implementation: * * (1) Make a copy of this file. Perhaps call it "mynewvtab.c" * * (2) Replace this header comment with something appropriate for * the new virtual table * * (3) Change every occurrence of "sqlmatrix" to some other string * appropriate for the new virtual table. Ideally, the new string * should be the basename of the source file: "mynewvtab". Also * globally change "SQLMATRIX" to "MYNEWVTAB". * * (4) Run a test compilation to make sure the unmodified virtual * table works. * * (5) Begin making incremental changes, testing as you go, to evolve * the new virtual table to do what you want it to do. * * This template is minimal, in the sense that it uses only the required * methods on the sqlite3_module object. As a result, sqlmatrix is * a read-only and eponymous-only table. Those limitation can be removed * by adding new methods. * * This template implements an eponymous-only virtual table with a rowid and * two columns named "a" and "b". The table as 10 rows with fixed integer * values. Usage example: * * SELECT rowid, a, b FROM sqlmatrix; */ //!! amatch.c: amatchDisconnect, /* xDestroy */ //!! closure.c: closureDisconnect, /* xDestroy */ //!! csv.c: csvtabDisconnect, /* xDestroy */ //!! spellfix.c: spellfix1Destroy, /* xDestroy - handle DROP TABLE */ //!! vtablog.c: vtablogDestroy, /* xDestroy */ //!! zipfile.c: zipfileDisconnect, /* xDestroy */ //!! 14390 Aug 9 08:36 vtablog.c //!! 28997 Aug 9 08:36 csv.c //!! 30026 Aug 9 08:36 closure.c //!! 45869 Aug 9 08:36 amatch.c //!! 64873 Aug 9 08:36 zipfile.c //!! 103310 Aug 9 08:36 spellfix.c /* sqlmath.h - start */ /* header */ #include #include #include #include #include #include #include #include #ifdef WIN32 #include #endif #include /* typedef */ typedef struct FuncDef FuncDef; typedef struct sqlite3_stmt Vdbe; typedef struct sqlite3_value Mem; typedef struct sqlmatrix_vtab sqlmatrix_vtab; typedef struct sqlite3_context { Mem *pOut; /* The return value is stored here */ FuncDef *pFunc; /* Pointer to function information */ Mem *pMem; /* Memory cell used to store aggregate ctx */ Vdbe *pVdbe; /* The VM that owns this context */ int iOp; /* Instruction number of OP_Function */ int isError; /* Error code returned by the function. */ u8 skipFlag; /* Skip accumulator loading if true */ u8 argc; /* Number of arguments */ sqlite3_value *argv[1]; /* Argument set */ } sqlite3_context; typedef struct sqlite3_value { /* ** Internally, the vdbe manipulates nearly all SQL values as Mem ** structures. Each Mem struct may cache multiple representations (string, ** integer etc.) of the same value. */ union MemValue { double r; /* Real val used when MEM_Real set in flags */ i64 i; /* Integer val used when MEM_Int set in flags */ int nZero; /* Zero padding for MEM_Zero and MEM_Blob */ const char *zPType; /* Pointer type MEM_Term|MEM_Subtype|MEM_Null */ FuncDef *pDef; /* Used only when flags==MEM_Agg */ } u; u16 flags; /* Combo of MEM_Null, MEM_Str, MEM_Dyn, etc. */ u8 enc; /* SQLITE_UTF8,SQLITE_UTF16BE,SQLITE_UTF16LE */ u8 eSubtype; /* Subtype for this value */ int n; /* Number of char in string, excluding '\0' */ char *z; /* String or BLOB value */ /* ShallowCopy only needs to copy the information above */ char *zMalloc; /* MEM_Str or MEM_Blob if szMalloc>0 */ int szMalloc; /* Size of the zMalloc allocation */ u32 uTemp; /* Trans store serial_type in OP_MakeRecord */ sqlite3 *db; /* The associated database connection */ /* *INDENT-OFF* */ void (*xDel)(void *); /* Mem.z destructor - only valid if MEM_Dyn */ /* *INDENT-ON* */ #ifdef SQLITE_DEBUG Mem *pScopyFrom; /* This Mem is a shallow copy of pScopyFrom */ u16 mScopyFlags; /* flags value immediately after shallow copy */ #endif } sqlite3_value; typedef struct sqlmatrix_cursor { /* sqlmatrix_cursor is a subclass of sqlite3_vtab_cursor which will * serve as the underlying representation of a cursor that scans * over rows of the result */ sqlite3_vtab_cursor base; /* Base class - must be first */ /* Insert new fields here. For this sqlmatrix we only keep track ** of the rowid */ sqlite3_int64 iRowid; /* The rowid */ sqlmatrix_vtab *pXtab; /* Pointer to vtab */ } sqlmatrix_cursor; typedef struct sqlmatrix_vtab { /* sqlmatrix_vtab is a subclass of sqlite3_vtab which is * underlying representation of the virtual table */ sqlite3_vtab base; /* Base class - must be first */ /* Add new fields here, as necessary */ double *pMatrix; /* nRow x nCol matrix */ int nByte; /* number of bytes allocated for matrix */ int nCol; /* number of columns */ int nRow; /* number of rows */ } sqlmatrix_vtab; /* function */ JsonString *sqlite3_exec_get_tables_json( sqlite3 * db, /* The database on which the SQL executes */ const char *zSql, /* The SQL to be executed */ sqlite3_context * pCtx, int *pErrCode ); int sqlmatrixDisconnect( sqlite3_vtab * pVtab ); // static const int64_t JS_MAX_SAFE_INTEGER = 0x1fffffffffffffll; // static const int64_t JS_MIN_SAFE_INTEGER = -0x1fffffffffffffll; /* sqlmath.h - end */ /* sqlmath.c - start */ static unsigned char base64_decode_table[] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, '>', 0, 0, 0, '?', '4', '5', '6', '7', '8', '9', ':', ';', '<', '=', 0, 0, 0, 0, 0, 0, 0, '\x00', '\x01', '\x02', '\x03', '\x04', '\x05', '\x06', '\x07', '\b', '\t', '\n', '\x0b', '\f', '\r', '\x0e', '\x0f', '\x10', '\x11', '\x12', '\x13', '\x14', '\x15', '\x16', '\x17', '\x18', '\x19', 0, 0, 0, 0, 0, 0, '\x1a', '\x1b', '\x1c', '\x1d', '\x1e', '\x1f', ' ', '!', '"', '#', '$', '%', '&', '\'', '(', ')', '*', '+', ',', '-', '.', '/', '0', '1', '2', '3', 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; static unsigned char base64_encode_table[] = { 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '+', '/' }; static int base64_mod_table[] = { 0, 2, 1 }; unsigned char *base64_decode( const unsigned char *input_text, int input_length, int *output_length ) { int ii; int jj; int nn; uint32_t sextet1; uint32_t sextet2; uint32_t sextet3; uint32_t sextet4; uint32_t triplet; unsigned char *output_blob; // Init output_length. *output_length = 0; // Find input_length with '=' stripped. while (1) { input_length -= 1; if (input_length < 0) { return NULL; } if (input_text[input_length] != '=') { break; } } input_length += 1; // Find output_length as multiple of 4. nn = input_length; if (nn % 4 != 0) { nn += 4 - (nn % 4); } // Decrease output_length for each '=' up to 2. switch (nn - input_length) { case 1: nn = nn / 4 * 3 - 1; break; case 2: nn = nn / 4 * 3 - 2; break; default: nn = nn / 4 * 3; } // Init output_blob. output_blob = (unsigned char *) sqlite3_malloc(nn); if (output_blob == NULL) { return NULL; } *output_length = nn; ii = 0; jj = 0; while (ii < input_length) { sextet1 = ii < input_length ? base64_decode_table[input_text[ii]] : 0; ii += 1; sextet2 = ii < input_length ? base64_decode_table[input_text[ii]] : 0; ii += 1; sextet3 = ii < input_length ? base64_decode_table[input_text[ii]] : 0; ii += 1; sextet4 = ii < input_length ? base64_decode_table[input_text[ii]] : 0; ii += 1; triplet = (sextet1 << 3 * 6) + (sextet2 << 2 * 6) + (sextet3 << 1 * 6) + (sextet4 << 0 * 6); if (jj < nn) { output_blob[jj] = (triplet >> 2 * 8) & 0xFF; jj += 1; } if (jj < nn) { output_blob[jj] = (triplet >> 1 * 8) & 0xFF; jj += 1; } if (jj < nn) { output_blob[jj] = (triplet >> 0 * 8) & 0xFF; jj += 1; } } return output_blob; } unsigned char *base64_encode( const unsigned char *input_blob, int input_length, int *output_length ) { int ii; int jj; int nn; uint32_t octet1; uint32_t octet2; uint32_t octet3; uint32_t triplet; unsigned char *output_text; *output_length = 0; nn = 4 * ((input_length + 2) / 3); output_text = sqlite3_malloc(nn); if (output_text == NULL) { return NULL; } *output_length = nn; ii = 0; jj = 0; while (ii < input_length) { octet1 = ii < input_length ? input_blob[ii] : 0; ii += 1; octet2 = ii < input_length ? input_blob[ii] : 0; ii += 1; octet3 = ii < input_length ? input_blob[ii] : 0; ii += 1; triplet = (octet1 << 0x10) + (octet2 << 0x08) + octet3; output_text[jj] = base64_encode_table[(triplet >> 3 * 6) & 0x3F]; jj += 1; output_text[jj] = base64_encode_table[(triplet >> 2 * 6) & 0x3F]; jj += 1; output_text[jj] = base64_encode_table[(triplet >> 1 * 6) & 0x3F]; jj += 1; output_text[jj] = base64_encode_table[(triplet >> 0 * 6) & 0x3F]; jj += 1; } ii = 0; while (ii < base64_mod_table[input_length % 3]) { output_text[nn - 1 - ii] = '='; ii += 1; } return output_text; } int example_sqlite3_exec_get_tables_json( sqlite3 * db ) { JsonString *pJson; pJson = sqlite3_exec_get_tables_json(db, (" CREATE TABLE tt1 AS" " SELECT 101 AS c102, 102 AS c102" " UNION ALL" " VALUES (201, 202)," " (301, NULL);" "" " CREATE TABLE tt2 AS" " SELECT 401 AS c402, 402 AS c402, 403 AS c403" " UNION ALL" " VALUES (501, 502.0123, 5030123456789)," " (601, NULL, 603)," " (701, b64decode('0123456789'), b64decode('8J+YgQ'));" "" " SELECT * FROM tt1;" " SELECT * FROM tt2;"), NULL, NULL); printf("%s\n", pJson->zBuf); sqlite3_free(pJson); return 0; } int example_sqlite3_get_table( sqlite3 * db ) { char **dbResult; // It is char ** type, two * signs char *errmsg = NULL; int ii; int jj; int nColumn; int nRow; int rc; // sqlite3 *db; // rc = sqlite3_open(":memory:", &db); // if (rc != SQLITE_OK) { // return rc; // } // Database operation code // Assume that the MyTable_1 table has been created earlier // Start the query, the incoming dbResult is already char **, there is an & to // get the address character, and the one passed in becomes char *** rc = sqlite3_get_table(db, (" CREATE TABLE tt1 AS" " SELECT 101 AS c102, 102 AS c102" " UNION ALL" " VALUES (201, 202)," " (301, NULL);" "" " CREATE TABLE tt2 AS" " SELECT 401 AS c402, 402 AS c402" " UNION ALL" " VALUES (501, 502)," " (601, NULL);" "" " SELECT * FROM tt1;" " SELECT * FROM tt2;"), &dbResult, &nRow, &nColumn, &errmsg); if (SQLITE_OK == rc) { // search successful // As mentioned earlier, the first line of data in the previous dbResult is the // field name, and the real data starts from the nColumn index printf("% d records found\n", nRow); for (ii = 0; ii < nRow; ii += 1) { printf("The %d record\n", ii); for (jj = 0; jj < nColumn; jj += 1) { printf("%i %i Field name: %s?> Field value: %s\n", ii, jj, dbResult[jj], dbResult[ii * nColumn + jj]); // The field values ??of dbResult are continuous, from the 0th index to the // nColumn-1 index are the field names, starting from the nColumn index, // followed by the field values, it puts a two-dimensional table (traditional // rows and columns Notation) expressed in a flat form } printf("-------\n"); } } // At this point, regardless of whether the database query is successful, the // char ** query results are released, using the function provided by sqlite to // release sqlite3_free_table(dbResult); // sqlite3_close(db); return rc; } int example_sqlite3_step( sqlite3 * db ) { int rc; sqlite3_stmt *pStmt; // sqlite3 *db; // rc = sqlite3_open(":memory:", &db); // if (rc != SQLITE_OK) { // return rc; // } rc = sqlite3_prepare_v2(db, "SELECT SQLITE_VERSION()", -1, &pStmt, 0); if (rc != SQLITE_OK) { fprintf(stderr, "Failed to fetch data: %s\n", sqlite3_errmsg(db)); sqlite3_close(db); return rc; } rc = sqlite3_step(pStmt); if (rc == SQLITE_ROW) { printf("%s\n", sqlite3_column_text(pStmt, 0)); } sqlite3_finalize(pStmt); // sqlite3_close(db); return rc; } void base64DecodeFunc( sqlite3_context * ctx, int argc, sqlite3_value ** argv ) { const unsigned char *zTmp; int nLen; assert(argc == 1); //!! zTmp = base64_decode( //!! (const char *) sqlite3_value_text(argv[0]), //!! sqlite3_value_bytes(argv[0])); zTmp = sqlite3_value_text(argv[0]); nLen = sqlite3_value_bytes(argv[0]); zTmp = base64_decode(zTmp, sqlite3_value_bytes(argv[0]), &nLen); sqlite3_result_blob64(ctx, (const void *) zTmp, nLen, sqlite3_free); //!! SQLITE_API int sqlite3_value_bytes(sqlite3_value *pVal){ //!! return sqlite3ValueBytes(pVal, SQLITE_UTF8); //!! } //!! SQLITE_API void sqlite3_result_blob( //!! sqlite3_context *pCtx, //!! const void *z, //!! int n, //!! void (*xDel)(void *) //!! ){ //!! assert( n>=0 ); //!! assert( sqlite3_mutex_held(pCtx->pOut->db->mutex) ); //!! setResultStrOrError(pCtx, z, n, 0, xDel); //!! } } static void noopfunc2( sqlite3_context * context, int argc, sqlite3_value ** argv ) { /* ** Implementation of the noop() function. ** ** The function returns its argument, unchanged. */ assert(argc == 1); sqlite3_result_value(context, argv[0]); } JsonString *sqlite3_exec_get_tables_json( sqlite3 * db, /* The database on which the SQL executes */ const char *zSql, /* The SQL to be executed */ sqlite3_context * pCtx, int *pErrCode ) { JsonString *pJson; const char *zSql2; const char *zTmp; int ii; int nCol; int nLen; int rc = SQLITE_OK; /* Return code */ sqlite3_context ctx = { 0 }; sqlite3_stmt *pStmt; /* The current SQL statement */ sqlite3_value ctxPout = { 0 }; if (pCtx == NULL) { ctxPout.db = db; ctx.pOut = (Mem *) & ctxPout; pCtx = &ctx; } pJson = (JsonString *) sqlite3_malloc(sizeof(struct JsonString)); jsonInit(pJson, pCtx); // bracket database [ jsonAppendChar(pJson, '['); sqlite3_mutex_enter(sqlite3_db_mutex(db)); // Loop over each table. while (rc == SQLITE_OK && zSql[0]) { pStmt = NULL; rc = sqlite3_prepare_v2(db, zSql, -1, &pStmt, &zSql2); assert(rc == SQLITE_OK || pStmt == NULL); if (rc != SQLITE_OK) { continue; } if (pStmt == NULL) { /* this happens for a comment or white-space */ zSql = zSql2; continue; } nCol = -1; // Loop over each row. while (1) { rc = sqlite3_step(pStmt); if (rc != SQLITE_ROW) { rc = sqlite3_finalize(pStmt); pStmt = NULL; zSql = zSql2; //!! while( sqlite3Isspace(zSql[0]) ) zSql++; break; } // Insert row of column-names. if (nCol == -1) { if (pJson->nUsed > 1) { jsonAppendChar(pJson, ','); jsonAppendChar(pJson, '\n'); jsonAppendChar(pJson, '\n'); } // bracket table [ jsonAppendChar(pJson, '['); nCol = sqlite3_column_count(pStmt); ii = 0; // bracket column [ jsonAppendChar(pJson, '['); // Loop over each column-name. while (ii < nCol) { if (ii > 0) { jsonAppendChar(pJson, ','); } zTmp = sqlite3_column_name(pStmt, ii); jsonAppendString(pJson, zTmp, strlen(zTmp)); ii += 1; } // bracket column ] jsonAppendChar(pJson, ']'); } jsonAppendChar(pJson, ','); jsonAppendChar(pJson, '\n'); // bracket row [ jsonAppendChar(pJson, '['); ii = 0; // Loop over each column-value. while (ii < nCol) { if (ii > 0) { jsonAppendChar(pJson, ','); } switch (sqlite3_column_type(pStmt, ii)) { // encode blob as data-uri application/octet-stream case SQLITE_BLOB: jsonAppendRaw(pJson, "\"data:application/octet-stream;base64,", 38); zTmp = (const char *) base64_encode( (const unsigned char *) sqlite3_column_text(pStmt, ii), sqlite3_column_bytes(pStmt, ii), &nLen); jsonAppendRaw(pJson, zTmp, nLen); sqlite3_free((void *) zTmp); jsonAppendChar(pJson, '"'); break; case SQLITE_INTEGER: case SQLITE_FLOAT: jsonAppendRaw(pJson, (const char *) sqlite3_column_text(pStmt, ii), sqlite3_column_bytes(pStmt, ii)); break; case SQLITE_TEXT: zTmp = (const char *) sqlite3_column_text(pStmt, ii); jsonAppendString(pJson, (const char *) sqlite3_column_text(pStmt, ii), sqlite3_column_bytes(pStmt, ii)); break; default: /* case SQLITE_NULL: */ jsonAppendRaw(pJson, "null", 4); break; } ii += 1; } // bracket row ] jsonAppendChar(pJson, ']'); if (pJson->bErr) { goto exec_out; } } if (nCol != -1) { // bracket table ] jsonAppendChar(pJson, ']'); } if (pJson->bErr) { goto exec_out; } } // bracket database ] jsonAppendChar(pJson, ']'); jsonAppendChar(pJson, 0); exec_out: // sync isError = pErrCode = rc rc = sqlite3_finalize(pStmt) || rc; rc = pJson->pCtx->isError || rc; pJson->pCtx->isError = rc; if (pErrCode != NULL) { *pErrCode = rc; } sqlite3_mutex_leave(sqlite3_db_mutex(db)); // cleanup stack-allocated ctx if (pJson->pCtx == &ctx) { pJson->pCtx = NULL; } return pJson; } SQLITE_API void sqlite3_str_append_jsonescaped( sqlite3_str * p, const char *z, int N ) { /* ** Append N bytes of text from z to the StrAccum object with json-escaping. ** Increase the size of the memory allocation for StrAccum if necessary. */ char *c = (char *) z; char *n = c + N; sqlite3_str_appendchar(p, 1, '"'); while (c < n) { // https://stackoverflow.com/questions/7724448/simple-json-string-escape-for-c switch (*c) { case '\x00': sqlite3_str_append(p, "\\u0000", 6); break; case '\x01': sqlite3_str_append(p, "\\u0001", 6); break; case '\x02': sqlite3_str_append(p, "\\u0002", 6); break; case '\x03': sqlite3_str_append(p, "\\u0003", 6); break; case '\x04': sqlite3_str_append(p, "\\u0004", 6); break; case '\x05': sqlite3_str_append(p, "\\u0005", 6); break; case '\x06': sqlite3_str_append(p, "\\u0006", 6); break; case '\x07': sqlite3_str_append(p, "\\u0007", 6); break; case '\x08': sqlite3_str_append(p, "\\b", 2); break; case '\x09': sqlite3_str_append(p, "\\t", 2); break; case '\x0a': sqlite3_str_append(p, "\\n", 2); break; case '\x0b': sqlite3_str_append(p, "\\u000b", 6); break; case '\x0c': sqlite3_str_append(p, "\\f", 2); break; case '\x0d': sqlite3_str_append(p, "\\r", 2); break; case '\x0e': sqlite3_str_append(p, "\\u000e", 6); break; case '\x0f': sqlite3_str_append(p, "\\u000f", 6); break; case '\x10': sqlite3_str_append(p, "\\u0010", 6); break; case '\x11': sqlite3_str_append(p, "\\u0011", 6); break; case '\x12': sqlite3_str_append(p, "\\u0012", 6); break; case '\x13': sqlite3_str_append(p, "\\u0013", 6); break; case '\x14': sqlite3_str_append(p, "\\u0014", 6); break; case '\x15': sqlite3_str_append(p, "\\u0015", 6); break; case '\x16': sqlite3_str_append(p, "\\u0016", 6); break; case '\x17': sqlite3_str_append(p, "\\u0017", 6); break; case '\x18': sqlite3_str_append(p, "\\u0018", 6); break; case '\x19': sqlite3_str_append(p, "\\u0019", 6); break; case '\x1a': sqlite3_str_append(p, "\\u001a", 6); break; case '\x1b': sqlite3_str_append(p, "\\u001b", 6); break; case '\x1c': sqlite3_str_append(p, "\\u001c", 6); break; case '\x1d': sqlite3_str_append(p, "\\u001d", 6); break; case '\x22': sqlite3_str_append(p, "\\\"", 2); break; case '\x5c': sqlite3_str_append(p, "\\\\", 2); break; default: sqlite3_str_appendchar(p, 1, *c); } c += 1; } sqlite3_str_appendchar(p, 1, '"'); } int sqlmatrixBestIndex( /* * xBestIndex * SQLite will invoke this method one or more times while planning a query * that uses the virtual table. This routine needs to create * a query plan for each invocation and compute an estimated cost for that * plan. */ sqlite3_vtab * tab, sqlite3_index_info * pIdxInfo ) { pIdxInfo->estimatedCost = (double) 10; pIdxInfo->estimatedRows = 10; return SQLITE_OK; } int sqlmatrixClose( sqlite3_vtab_cursor * cur ) { /* * xClose * Destructor for a sqlmatrix_cursor. */ sqlmatrix_cursor *pCur = (sqlmatrix_cursor *) cur; sqlite3_free(pCur); return SQLITE_OK; } int sqlmatrixColumn( /* * xColumn * Return values of columns for the row at which the sqlmatrix_cursor * is currently pointing. */ sqlite3_vtab_cursor * cur, /* The cursor */ sqlite3_context * ctx, /* First argument to sqlite3_result_...() */ int i /* Which column to return */ ) { sqlmatrix_cursor *pCur = (sqlmatrix_cursor *) cur; sqlmatrix_vtab *p = pCur->pXtab; switch (i - p->nCol) { case 0: sqlite3_result_pointer(ctx, p, "sqlmatrix", 0); break; case 1: sqlite3_result_double(ctx, p->nCol); break; case 2: sqlite3_result_double(ctx, p->nRow); break; default: sqlite3_result_double(ctx, p->pMatrix[(pCur->iRowid - 1) * p->nCol + i]); } return SQLITE_OK; } int sqlmatrixConnect( /* * xConnect * The sqlmatrixConnect() method is invoked to create a new * template virtual table. * * Think of this routine as the constructor for sqlmatrix_vtab objects. * * All this routine needs to do is: * * (1) Allocate the sqlmatrix_vtab object and initialize all fields. * * (2) Tell SQLite (via the sqlite3_declare_vtab() interface) what the * result set of queries against the virtual table will look like. */ sqlite3 * db, void *pAux, int argc, const char *const *argv, sqlite3_vtab ** ppVtab, char **pzErr ) { char zCreateTable[15000] = { 0 }; int ii; int jj; int nCol; int rc; sqlmatrix_vtab *pNew; /* * argv[0] -> module name ("approximate_match") * argv[1] -> database name * argv[2] -> table name * argv[3...] -> arguments */ // CREATE VIRTUAL TABLE aa USING SQLMATRIX(2); nCol = atoi(argv[3]); if (!(0 < nCol && nCol <= 2000)) { *pzErr = sqlite3_mprintf ("CREATE TABLE sqlmatrix() is not allowed with column size %i", nCol); return SQLITE_ERROR; } ii = 0; jj = 0; while (ii <= nCol) { jj += (ii == 0 ? snprintf(zCreateTable + jj, 15000ll - jj, "CREATE TABLE %s(", argv[2]) : ii < nCol ? snprintf(&zCreateTable[jj], 15000ll - jj, "c%i,", ii) : snprintf(&zCreateTable[jj], 15000ll - jj, "c%i," "ptr INTEGER HIDDEN," "cols INTEGER HIDDEN," "rows INTEGER HIDDEN)", ii)); if (!(0 <= jj && jj < 15000)) { break; } ii += 1; } if (0 < jj && jj < 15000) { zCreateTable[jj - 1] = ')'; zCreateTable[jj] = 0; } rc = sqlite3_declare_vtab(db, zCreateTable); /* For convenience, define symbolic names for the index to each column. */ if (rc != SQLITE_OK) { *pzErr = sqlite3_mprintf(zCreateTable); return rc; } pNew = sqlite3_malloc(sizeof(*pNew)); if (pNew == NULL) { return SQLITE_NOMEM; } memset(pNew, 0, sizeof(*pNew)); *ppVtab = (sqlite3_vtab *) pNew; pNew->nCol = nCol; pNew->nRow = 0; pNew->nByte = 256 * nCol * sizeof(double); pNew->pMatrix = sqlite3_malloc(pNew->nByte); if (pNew->pMatrix == NULL) { return SQLITE_NOMEM; } return rc; } int sqlmatrixCreate( /* * xCreate * The xConnect and xCreate methods do the same thing, but they must be * different so that the virtual table is not an eponymous virtual table. */ sqlite3 * db, void *pAux, int argc, const char *const *argv, sqlite3_vtab ** ppVtab, char **pzErr ) { return sqlmatrixConnect(db, pAux, argc, argv, ppVtab, pzErr); } int sqlmatrixDisconnect( sqlite3_vtab * pVtab ) { /* * xDisconnect * This method is the destructor for sqlmatrix_vtab objects. */ sqlmatrix_vtab *p = (sqlmatrix_vtab *) pVtab; if (p == NULL) { return SQLITE_OK; } sqlite3_free(p->pMatrix); memset(p, 0, sizeof(*p)); sqlite3_free(p); return SQLITE_OK; } int sqlmatrixEof( sqlite3_vtab_cursor * cur ) { /* * xEof * Return TRUE if the cursor has been moved off of the last * row of output. */ sqlmatrix_cursor *pCur = (sqlmatrix_cursor *) cur; return pCur->iRowid > pCur->pXtab->nRow; } int sqlmatrixFilter( /* * xFilter * This method is called to "rewind" the sqlmatrix_cursor object back * to the first row of output. This method is always called at least * once prior to any call to sqlmatrixColumn() or sqlmatrixRowid() or * sqlmatrixEof(). */ sqlite3_vtab_cursor * pVtabCursor, int idxNum, const char *idxStr, int argc, sqlite3_value ** argv ) { sqlmatrix_cursor *pCur = (sqlmatrix_cursor *) pVtabCursor; pCur->iRowid = 1; pCur->pXtab = (sqlmatrix_vtab *) pVtabCursor->pVtab; return SQLITE_OK; } int sqlmatrixNext( sqlite3_vtab_cursor * cur ) { /* * xNext * Advance a sqlmatrix_cursor to its next row of output. */ sqlmatrix_cursor *pCur = (sqlmatrix_cursor *) cur; pCur->iRowid++; return SQLITE_OK; } int sqlmatrixOpen( sqlite3_vtab * p, sqlite3_vtab_cursor ** ppCursor ) { /* * xOpen * Constructor for a new sqlmatrix_cursor object. */ sqlmatrix_cursor *pCur; pCur = sqlite3_malloc(sizeof(*pCur)); if (pCur == NULL) { return SQLITE_NOMEM; } memset(pCur, 0, sizeof(*pCur)); *ppCursor = &pCur->base; return SQLITE_OK; } int sqlmatrixRowid( sqlite3_vtab_cursor * cur, sqlite_int64 * pRowid ) { /* * xRowid * Return the rowid for the current row. In this implementation, the * rowid is the same as the output value. */ sqlmatrix_cursor *pCur = (sqlmatrix_cursor *) cur; *pRowid = pCur->iRowid; return SQLITE_OK; } int sqlmatrixUpdate( /* * xUpdate * This implementation disallows DELETE and UPDATE. The only thing * allowed is INSERT. */ sqlite3_vtab * pVtab, int argc, sqlite3_value ** argv, sqlite_int64 * pRowid ) { double *pRow; int ii; sqlmatrix_vtab *p = (sqlmatrix_vtab *) pVtab; // https://www.sqlite.org/vtab.html#xupdate /* DELETE row */ if (argc == 1) { pVtab->zErrMsg = sqlite3_mprintf("DELETE row from sqlmatrix() is not allowed"); return SQLITE_ERROR; } /* INSERT row */ if (sqlite3_value_type(argv[0]) == SQLITE_NULL) { p->nRow += 1; *pRowid = p->nRow; while (p->nRow * p->nCol >= p->nByte) { p->nByte *= 2; p->pMatrix = sqlite3_realloc(p->pMatrix, p->nByte); if (p->pMatrix == NULL) { sqlmatrixDisconnect((sqlite3_vtab *) p); return SQLITE_NOMEM; } } ii = 2; pRow = p->pMatrix + (*pRowid - 1) * p->nCol - ii; while (ii < argc) { pRow[ii] = sqlite3_value_double(argv[ii]); ii += 1; } return SQLITE_OK; } /* UPDATE row at rowid argv[0] */ if (argv[0] == argv[1]) { pVtab->zErrMsg = sqlite3_mprintf("UPDATE row in sqlmatrix() is not allowed"); return SQLITE_ERROR; } /* UPDATE row at rowid argv[0] and change rowid to argv[1] */ pVtab->zErrMsg = sqlite3_mprintf ("UPDATE row with rowid change in sqlmatrix() is not allowed"); return SQLITE_ERROR; } sqlite3_module sqlmatrixModule = { /* * This following structure defines all the methods for the * virtual table. */ 0, /* iVersion */ sqlmatrixCreate, /* xCreate */ sqlmatrixConnect, /* xConnect */ sqlmatrixBestIndex, /* xBestIndex */ sqlmatrixDisconnect, /* xDisconnect */ sqlmatrixDisconnect, /* xDestroy */ sqlmatrixOpen, /* xOpen */ sqlmatrixClose, /* xClose */ sqlmatrixFilter, /* xFilter */ sqlmatrixNext, /* xNext */ sqlmatrixEof, /* xEof */ sqlmatrixColumn, /* xColumn */ sqlmatrixRowid, /* xRowid */ sqlmatrixUpdate, /* xUpdate */ 0, /* xBegin */ 0, /* xSync */ 0, /* xCommit */ 0, /* xRollback */ 0, /* xFindMethod */ 0, /* xRename */ 0, /* xSavepoint */ 0, /* xRelease */ 0, /* xRollbackTo */ 0 /* xShadowName */ }; int sqlite3_sqlmath_init( sqlite3 * db, char **pzErrMsg, const sqlite3_api_routines * pApi ) { int rc = SQLITE_OK; // SQLITE_EXTENSION_INIT2(pApi); rc = rc || sqlite3_create_module(db, "sqlmatrix", &sqlmatrixModule, 0); rc = rc || sqlite3_create_function(db, "b64decode", 1, SQLITE_UTF8 | SQLITE_DETERMINISTIC, 0, base64DecodeFunc, 0, 0); rc = rc || sqlite3_create_function(db, "noop2", 1, SQLITE_UTF8 | SQLITE_DETERMINISTIC, 0, noopfunc2, 0, 0); //!! example_sqlite3_get_table(db); //!! example_sqlite3_step(db); example_sqlite3_exec_get_tables_json(db); return rc; } /* sqlmath.c - end */ /* file sqlmath-old.cpp */ /*jslint-disable*/ /* shRawLibFetch { "fetchList": [ { "comment": true, "url": "https://github.com/nodejs/node-addon-api/blob/3.2.1/LICENSE.md" }, { "url": "https://github.com/nodejs/node-addon-api/blob/3.2.1/napi.h" }, { "url": "https://github.com/nodejs/node-addon-api/blob/3.2.1/napi-inl.h" }, { "comment": true, "url": "https://github.com/mapbox/node-sqlite3/blob/v5.0.2/LICENSE" }, { "comment": true, "url": "https://github.com/mapbox/node-sqlite3/blob/v5.0.2/deps/common-sqlite.gypi" }, { "comment": true, "url": "https://github.com/mapbox/node-sqlite3/blob/v5.0.2/deps/sqlite3.gyp" }, { "url": "https://github.com/mapbox/node-sqlite3/blob/v5.0.2/src/threading.h" }, { "url": "https://github.com/mapbox/node-sqlite3/blob/v5.0.2/src/macros.h" }, { "url": "https://github.com/mapbox/node-sqlite3/blob/v5.0.2/src/async.h" }, { "url": "https://github.com/mapbox/node-sqlite3/blob/v5.0.2/src/database.h" }, { "url": "https://github.com/mapbox/node-sqlite3/blob/v5.0.2/src/backup.h" }, { "url": "https://github.com/mapbox/node-sqlite3/blob/v5.0.2/src/statement.h" }, { "url": "https://github.com/mapbox/node-sqlite3/blob/v5.0.2/src/database.cc" }, { "url": "https://github.com/mapbox/node-sqlite3/blob/v5.0.2/src/backup.cc" }, { "url": "https://github.com/mapbox/node-sqlite3/blob/v5.0.2/src/statement.cc" }, { "url": "https://github.com/mapbox/node-sqlite3/blob/v5.0.2/src/node_sqlite3.cc" } ], "replaceList": [ { "aa": "^#include ", "bb": "// $&", "flags": "gm" } ] } -# include "napi-inl.deprecated.h" +// hack-sqlite +// # include "napi-inl.deprecated.h" -#define SRC_NAPI_H_ +// hack-sqlite +#define SRC_NAPI_H_ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include -#endif // SRC_NAPI_H_ +// hack-sqlite +// #endif // SRC_NAPI_H_ -#endif // SRC_NAPI_INL_H_ +// hack-sqlite +#endif // SRC_NAPI_INL_H_ +#endif // SRC_NAPI_H_ */ /* repo https://github.com/nodejs/node-addon-api/tree/3.2.1 committed 2021-05-28T18:09:02Z */ /* file https://github.com/nodejs/node-addon-api/blob/3.2.1/LICENSE.md */ /* The MIT License (MIT) ===================== Copyright (c) 2017 Node.js API collaborators ----------------------------------- *Node.js API collaborators listed at * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /* file https://github.com/nodejs/node-addon-api/blob/3.2.1/napi.h */ #ifndef SRC_NAPI_H_ // hack-sqlite #define SRC_NAPI_H_ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include // #include // #include // #include // #include // #include // #include // #include // VS2015 RTM has bugs with constexpr, so require min of VS2015 Update 3 (known good version) #if !defined(_MSC_VER) || _MSC_FULL_VER >= 190024210 #define NAPI_HAS_CONSTEXPR 1 #endif // VS2013 does not support char16_t literal strings, so we'll work around it using wchar_t strings // and casting them. This is safe as long as the character sizes are the same. #if defined(_MSC_VER) && _MSC_VER <= 1800 static_assert(sizeof(char16_t) == sizeof(wchar_t), "Size mismatch between char16_t and wchar_t"); #define NAPI_WIDE_TEXT(x) reinterpret_cast(L ## x) #else #define NAPI_WIDE_TEXT(x) u ## x #endif // If C++ exceptions are not explicitly enabled or disabled, enable them // if exceptions were enabled in the compiler settings. #if !defined(NAPI_CPP_EXCEPTIONS) && !defined(NAPI_DISABLE_CPP_EXCEPTIONS) #if defined(_CPPUNWIND) || defined (__EXCEPTIONS) #define NAPI_CPP_EXCEPTIONS #else #error Exception support not detected. \ Define either NAPI_CPP_EXCEPTIONS or NAPI_DISABLE_CPP_EXCEPTIONS. #endif #endif #ifdef _NOEXCEPT #define NAPI_NOEXCEPT _NOEXCEPT #else #define NAPI_NOEXCEPT noexcept #endif #ifdef NAPI_CPP_EXCEPTIONS // When C++ exceptions are enabled, Errors are thrown directly. There is no need // to return anything after the throw statements. The variadic parameter is an // optional return value that is ignored. // We need _VOID versions of the macros to avoid warnings resulting from // leaving the NAPI_THROW_* `...` argument empty. #define NAPI_THROW(e, ...) throw e #define NAPI_THROW_VOID(e) throw e #define NAPI_THROW_IF_FAILED(env, status, ...) \ if ((status) != napi_ok) throw Napi::Error::New(env); #define NAPI_THROW_IF_FAILED_VOID(env, status) \ if ((status) != napi_ok) throw Napi::Error::New(env); #else // NAPI_CPP_EXCEPTIONS // When C++ exceptions are disabled, Errors are thrown as JavaScript exceptions, // which are pending until the callback returns to JS. The variadic parameter // is an optional return value; usually it is an empty result. // We need _VOID versions of the macros to avoid warnings resulting from // leaving the NAPI_THROW_* `...` argument empty. #define NAPI_THROW(e, ...) \ do { \ (e).ThrowAsJavaScriptException(); \ return __VA_ARGS__; \ } while (0) #define NAPI_THROW_VOID(e) \ do { \ (e).ThrowAsJavaScriptException(); \ return; \ } while (0) #define NAPI_THROW_IF_FAILED(env, status, ...) \ if ((status) != napi_ok) { \ Napi::Error::New(env).ThrowAsJavaScriptException(); \ return __VA_ARGS__; \ } #define NAPI_THROW_IF_FAILED_VOID(env, status) \ if ((status) != napi_ok) { \ Napi::Error::New(env).ThrowAsJavaScriptException(); \ return; \ } #endif // NAPI_CPP_EXCEPTIONS # define NAPI_DISALLOW_ASSIGN(CLASS) void operator=(const CLASS&) = delete; # define NAPI_DISALLOW_COPY(CLASS) CLASS(const CLASS&) = delete; #define NAPI_DISALLOW_ASSIGN_COPY(CLASS) \ NAPI_DISALLOW_ASSIGN(CLASS) \ NAPI_DISALLOW_COPY(CLASS) #define NAPI_FATAL_IF_FAILED(status, location, message) \ do { \ if ((status) != napi_ok) { \ Napi::Error::Fatal((location), (message)); \ } \ } while (0) //////////////////////////////////////////////////////////////////////////////// /// Node-API C++ Wrapper Classes /// /// These classes wrap the "Node-API" ABI-stable C APIs for Node.js, providing a /// C++ object model and C++ exception-handling semantics with low overhead. /// The wrappers are all header-only so that they do not affect the ABI. //////////////////////////////////////////////////////////////////////////////// namespace Napi { // Forward declarations class Env; class Value; class Boolean; class Number; #if NAPI_VERSION > 5 class BigInt; #endif // NAPI_VERSION > 5 #if (NAPI_VERSION > 4) class Date; #endif class String; class Object; class Array; class ArrayBuffer; class Function; class Error; class PropertyDescriptor; class CallbackInfo; class TypedArray; template class TypedArrayOf; using Int8Array = TypedArrayOf; ///< Typed-array of signed 8-bit integers using Uint8Array = TypedArrayOf; ///< Typed-array of unsigned 8-bit integers using Int16Array = TypedArrayOf; ///< Typed-array of signed 16-bit integers using Uint16Array = TypedArrayOf; ///< Typed-array of unsigned 16-bit integers using Int32Array = TypedArrayOf; ///< Typed-array of signed 32-bit integers using Uint32Array = TypedArrayOf; ///< Typed-array of unsigned 32-bit integers using Float32Array = TypedArrayOf; ///< Typed-array of 32-bit floating-point values using Float64Array = TypedArrayOf; ///< Typed-array of 64-bit floating-point values #if NAPI_VERSION > 5 using BigInt64Array = TypedArrayOf; ///< Typed array of signed 64-bit integers using BigUint64Array = TypedArrayOf; ///< Typed array of unsigned 64-bit integers #endif // NAPI_VERSION > 5 /// Defines the signature of a Node-API C++ module's registration callback /// (init) function. using ModuleRegisterCallback = Object (*)(Env env, Object exports); class MemoryManagement; /// Environment for Node-API values and operations. /// /// All Node-API values and operations must be associated with an environment. /// An environment instance is always provided to callback functions; that /// environment must then be used for any creation of Node-API values or other /// Node-API operations within the callback. (Many methods infer the /// environment from the `this` instance that the method is called on.) /// /// In the future, multiple environments per process may be supported, /// although current implementations only support one environment per process. /// /// In the V8 JavaScript engine, a Node-API environment approximately /// corresponds to an Isolate. class Env { #if NAPI_VERSION > 5 private: template static void DefaultFini(Env, T* data); template static void DefaultFiniWithHint(Env, DataType* data, HintType* hint); #endif // NAPI_VERSION > 5 public: Env(napi_env env); operator napi_env() const; Object Global() const; Value Undefined() const; Value Null() const; bool IsExceptionPending() const; Error GetAndClearPendingException(); Value RunScript(const char* utf8script); Value RunScript(const std::string& utf8script); Value RunScript(String script); #if NAPI_VERSION > 5 template T* GetInstanceData(); template using Finalizer = void (*)(Env, T*); template fini = Env::DefaultFini> void SetInstanceData(T* data); template using FinalizerWithHint = void (*)(Env, DataType*, HintType*); template fini = Env::DefaultFiniWithHint> void SetInstanceData(DataType* data, HintType* hint); #endif // NAPI_VERSION > 5 private: napi_env _env; }; /// A JavaScript value of unknown type. /// /// For type-specific operations, convert to one of the Value subclasses using a `To*` or `As()` /// method. The `To*` methods do type coercion; the `As()` method does not. /// /// Napi::Value value = ... /// if (!value.IsString()) throw Napi::TypeError::New(env, "Invalid arg..."); /// Napi::String str = value.As(); // Cast to a string value /// /// Napi::Value anotherValue = ... /// bool isTruthy = anotherValue.ToBoolean(); // Coerce to a boolean value class Value { public: Value(); ///< Creates a new _empty_ Value instance. Value(napi_env env, napi_value value); ///< Wraps a Node-API value primitive. /// Creates a JS value from a C++ primitive. /// /// `value` may be any of: /// - bool /// - Any integer type /// - Any floating point type /// - const char* (encoded using UTF-8, null-terminated) /// - const char16_t* (encoded using UTF-16-LE, null-terminated) /// - std::string (encoded using UTF-8) /// - std::u16string /// - napi::Value /// - napi_value template static Value From(napi_env env, const T& value); /// Converts to a Node-API value primitive. /// /// If the instance is _empty_, this returns `nullptr`. operator napi_value() const; /// Tests if this value strictly equals another value. bool operator ==(const Value& other) const; /// Tests if this value does not strictly equal another value. bool operator !=(const Value& other) const; /// Tests if this value strictly equals another value. bool StrictEquals(const Value& other) const; /// Gets the environment the value is associated with. Napi::Env Env() const; /// Checks if the value is empty (uninitialized). /// /// An empty value is invalid, and most attempts to perform an operation on an empty value /// will result in an exception. Note an empty value is distinct from JavaScript `null` or /// `undefined`, which are valid values. /// /// When C++ exceptions are disabled at compile time, a method with a `Value` return type may /// return an empty value to indicate a pending exception. So when not using C++ exceptions, /// callers should check whether the value is empty before attempting to use it. bool IsEmpty() const; napi_valuetype Type() const; ///< Gets the type of the value. bool IsUndefined() const; ///< Tests if a value is an undefined JavaScript value. bool IsNull() const; ///< Tests if a value is a null JavaScript value. bool IsBoolean() const; ///< Tests if a value is a JavaScript boolean. bool IsNumber() const; ///< Tests if a value is a JavaScript number. #if NAPI_VERSION > 5 bool IsBigInt() const; ///< Tests if a value is a JavaScript bigint. #endif // NAPI_VERSION > 5 #if (NAPI_VERSION > 4) bool IsDate() const; ///< Tests if a value is a JavaScript date. #endif bool IsString() const; ///< Tests if a value is a JavaScript string. bool IsSymbol() const; ///< Tests if a value is a JavaScript symbol. bool IsArray() const; ///< Tests if a value is a JavaScript array. bool IsArrayBuffer() const; ///< Tests if a value is a JavaScript array buffer. bool IsTypedArray() const; ///< Tests if a value is a JavaScript typed array. bool IsObject() const; ///< Tests if a value is a JavaScript object. bool IsFunction() const; ///< Tests if a value is a JavaScript function. bool IsPromise() const; ///< Tests if a value is a JavaScript promise. bool IsDataView() const; ///< Tests if a value is a JavaScript data view. bool IsBuffer() const; ///< Tests if a value is a Node buffer. bool IsExternal() const; ///< Tests if a value is a pointer to external data. /// Casts to another type of `Napi::Value`, when the actual type is known or assumed. /// /// This conversion does NOT coerce the type. Calling any methods inappropriate for the actual /// value type will throw `Napi::Error`. template T As() const; Boolean ToBoolean() const; ///< Coerces a value to a JavaScript boolean. Number ToNumber() const; ///< Coerces a value to a JavaScript number. String ToString() const; ///< Coerces a value to a JavaScript string. Object ToObject() const; ///< Coerces a value to a JavaScript object. protected: /// !cond INTERNAL napi_env _env; napi_value _value; /// !endcond }; /// A JavaScript boolean value. class Boolean : public Value { public: static Boolean New(napi_env env, ///< Node-API environment bool value ///< Boolean value ); Boolean(); ///< Creates a new _empty_ Boolean instance. Boolean(napi_env env, napi_value value); ///< Wraps a Node-API value primitive. operator bool() const; ///< Converts a Boolean value to a boolean primitive. bool Value() const; ///< Converts a Boolean value to a boolean primitive. }; /// A JavaScript number value. class Number : public Value { public: static Number New(napi_env env, ///< Node-API environment double value ///< Number value ); Number(); ///< Creates a new _empty_ Number instance. Number(napi_env env, napi_value value); ///< Wraps a Node-API value primitive. operator int32_t() const; ///< Converts a Number value to a 32-bit signed integer value. operator uint32_t() const; ///< Converts a Number value to a 32-bit unsigned integer value. operator int64_t() const; ///< Converts a Number value to a 64-bit signed integer value. operator float() const; ///< Converts a Number value to a 32-bit floating-point value. operator double() const; ///< Converts a Number value to a 64-bit floating-point value. int32_t Int32Value() const; ///< Converts a Number value to a 32-bit signed integer value. uint32_t Uint32Value() const; ///< Converts a Number value to a 32-bit unsigned integer value. int64_t Int64Value() const; ///< Converts a Number value to a 64-bit signed integer value. float FloatValue() const; ///< Converts a Number value to a 32-bit floating-point value. double DoubleValue() const; ///< Converts a Number value to a 64-bit floating-point value. }; #if NAPI_VERSION > 5 /// A JavaScript bigint value. class BigInt : public Value { public: static BigInt New(napi_env env, ///< Node-API environment int64_t value ///< Number value ); static BigInt New(napi_env env, ///< Node-API environment uint64_t value ///< Number value ); /// Creates a new BigInt object using a specified sign bit and a /// specified list of digits/words. /// The resulting number is calculated as: /// (-1)^sign_bit * (words[0] * (2^64)^0 + words[1] * (2^64)^1 + ...) static BigInt New(napi_env env, ///< Node-API environment int sign_bit, ///< Sign bit. 1 if negative. size_t word_count, ///< Number of words in array const uint64_t* words ///< Array of words ); BigInt(); ///< Creates a new _empty_ BigInt instance. BigInt(napi_env env, napi_value value); ///< Wraps a Node-API value primitive. int64_t Int64Value(bool* lossless) const; ///< Converts a BigInt value to a 64-bit signed integer value. uint64_t Uint64Value(bool* lossless) const; ///< Converts a BigInt value to a 64-bit unsigned integer value. size_t WordCount() const; ///< The number of 64-bit words needed to store ///< the result of ToWords(). /// Writes the contents of this BigInt to a specified memory location. /// `sign_bit` must be provided and will be set to 1 if this BigInt is /// negative. /// `*word_count` has to be initialized to the length of the `words` array. /// Upon return, it will be set to the actual number of words that would /// be needed to store this BigInt (i.e. the return value of `WordCount()`). void ToWords(int* sign_bit, size_t* word_count, uint64_t* words); }; #endif // NAPI_VERSION > 5 #if (NAPI_VERSION > 4) /// A JavaScript date value. class Date : public Value { public: /// Creates a new Date value from a double primitive. static Date New(napi_env env, ///< Node-API environment double value ///< Number value ); Date(); ///< Creates a new _empty_ Date instance. Date(napi_env env, napi_value value); ///< Wraps a Node-API value primitive. operator double() const; ///< Converts a Date value to double primitive double ValueOf() const; ///< Converts a Date value to a double primitive. }; #endif /// A JavaScript string or symbol value (that can be used as a property name). class Name : public Value { public: Name(); ///< Creates a new _empty_ Name instance. Name(napi_env env, napi_value value); ///< Wraps a Node-API value primitive. }; /// A JavaScript string value. class String : public Name { public: /// Creates a new String value from a UTF-8 encoded C++ string. static String New(napi_env env, ///< Node-API environment const std::string& value ///< UTF-8 encoded C++ string ); /// Creates a new String value from a UTF-16 encoded C++ string. static String New(napi_env env, ///< Node-API environment const std::u16string& value ///< UTF-16 encoded C++ string ); /// Creates a new String value from a UTF-8 encoded C string. static String New( napi_env env, ///< Node-API environment const char* value ///< UTF-8 encoded null-terminated C string ); /// Creates a new String value from a UTF-16 encoded C string. static String New( napi_env env, ///< Node-API environment const char16_t* value ///< UTF-16 encoded null-terminated C string ); /// Creates a new String value from a UTF-8 encoded C string with specified /// length. static String New(napi_env env, ///< Node-API environment const char* value, ///< UTF-8 encoded C string (not ///< necessarily null-terminated) size_t length ///< length of the string in bytes ); /// Creates a new String value from a UTF-16 encoded C string with specified /// length. static String New( napi_env env, ///< Node-API environment const char16_t* value, ///< UTF-16 encoded C string (not necessarily ///< null-terminated) size_t length ///< Length of the string in 2-byte code units ); /// Creates a new String based on the original object's type. /// /// `value` may be any of: /// - const char* (encoded using UTF-8, null-terminated) /// - const char16_t* (encoded using UTF-16-LE, null-terminated) /// - std::string (encoded using UTF-8) /// - std::u16string template static String From(napi_env env, const T& value); String(); ///< Creates a new _empty_ String instance. String(napi_env env, napi_value value); ///< Wraps a Node-API value primitive. operator std::string() const; ///< Converts a String value to a UTF-8 encoded C++ string. operator std::u16string() const; ///< Converts a String value to a UTF-16 encoded C++ string. std::string Utf8Value() const; ///< Converts a String value to a UTF-8 encoded C++ string. std::u16string Utf16Value() const; ///< Converts a String value to a UTF-16 encoded C++ string. }; /// A JavaScript symbol value. class Symbol : public Name { public: /// Creates a new Symbol value with an optional description. static Symbol New( napi_env env, ///< Node-API environment const char* description = nullptr ///< Optional UTF-8 encoded null-terminated C string /// describing the symbol ); /// Creates a new Symbol value with a description. static Symbol New( napi_env env, ///< Node-API environment const std::string& description ///< UTF-8 encoded C++ string describing the symbol ); /// Creates a new Symbol value with a description. static Symbol New(napi_env env, ///< Node-API environment String description ///< String value describing the symbol ); /// Creates a new Symbol value with a description. static Symbol New( napi_env env, ///< Node-API environment napi_value description ///< String value describing the symbol ); /// Get a public Symbol (e.g. Symbol.iterator). static Symbol WellKnown(napi_env, const std::string& name); Symbol(); ///< Creates a new _empty_ Symbol instance. Symbol(napi_env env, napi_value value); ///< Wraps a Node-API value primitive. }; /// A JavaScript object value. class Object : public Value { public: /// Enables property and element assignments using indexing syntax. /// /// Example: /// /// Napi::Value propertyValue = object1['A']; /// object2['A'] = propertyValue; /// Napi::Value elementValue = array[0]; /// array[1] = elementValue; template class PropertyLValue { public: /// Converts an L-value to a value. operator Value() const; /// Assigns a value to the property. The type of value can be /// anything supported by `Object::Set`. template PropertyLValue& operator =(ValueType value); private: PropertyLValue() = delete; PropertyLValue(Object object, Key key); napi_env _env; napi_value _object; Key _key; friend class Napi::Object; }; /// Creates a new Object value. static Object New(napi_env env ///< Node-API environment ); Object(); ///< Creates a new _empty_ Object instance. Object(napi_env env, napi_value value); ///< Wraps a Node-API value primitive. /// Gets or sets a named property. PropertyLValue operator []( const char* utf8name ///< UTF-8 encoded null-terminated property name ); /// Gets or sets a named property. PropertyLValue operator []( const std::string& utf8name ///< UTF-8 encoded property name ); /// Gets or sets an indexed property or array element. PropertyLValue operator []( uint32_t index /// Property / element index ); /// Gets a named property. Value operator []( const char* utf8name ///< UTF-8 encoded null-terminated property name ) const; /// Gets a named property. Value operator []( const std::string& utf8name ///< UTF-8 encoded property name ) const; /// Gets an indexed property or array element. Value operator []( uint32_t index ///< Property / element index ) const; /// Checks whether a property is present. bool Has( napi_value key ///< Property key primitive ) const; /// Checks whether a property is present. bool Has( Value key ///< Property key ) const; /// Checks whether a named property is present. bool Has( const char* utf8name ///< UTF-8 encoded null-terminated property name ) const; /// Checks whether a named property is present. bool Has( const std::string& utf8name ///< UTF-8 encoded property name ) const; /// Checks whether a own property is present. bool HasOwnProperty( napi_value key ///< Property key primitive ) const; /// Checks whether a own property is present. bool HasOwnProperty( Value key ///< Property key ) const; /// Checks whether a own property is present. bool HasOwnProperty( const char* utf8name ///< UTF-8 encoded null-terminated property name ) const; /// Checks whether a own property is present. bool HasOwnProperty( const std::string& utf8name ///< UTF-8 encoded property name ) const; /// Gets a property. Value Get( napi_value key ///< Property key primitive ) const; /// Gets a property. Value Get( Value key ///< Property key ) const; /// Gets a named property. Value Get( const char* utf8name ///< UTF-8 encoded null-terminated property name ) const; /// Gets a named property. Value Get( const std::string& utf8name ///< UTF-8 encoded property name ) const; /// Sets a property. template bool Set(napi_value key, ///< Property key primitive const ValueType& value ///< Property value primitive ); /// Sets a property. template bool Set(Value key, ///< Property key const ValueType& value ///< Property value ); /// Sets a named property. template bool Set( const char* utf8name, ///< UTF-8 encoded null-terminated property name const ValueType& value); /// Sets a named property. template bool Set(const std::string& utf8name, ///< UTF-8 encoded property name const ValueType& value ///< Property value primitive ); /// Delete property. bool Delete( napi_value key ///< Property key primitive ); /// Delete property. bool Delete( Value key ///< Property key ); /// Delete property. bool Delete( const char* utf8name ///< UTF-8 encoded null-terminated property name ); /// Delete property. bool Delete( const std::string& utf8name ///< UTF-8 encoded property name ); /// Checks whether an indexed property is present. bool Has( uint32_t index ///< Property / element index ) const; /// Gets an indexed property or array element. Value Get( uint32_t index ///< Property / element index ) const; /// Sets an indexed property or array element. template bool Set(uint32_t index, ///< Property / element index const ValueType& value ///< Property value primitive ); /// Deletes an indexed property or array element. bool Delete( uint32_t index ///< Property / element index ); Array GetPropertyNames() const; ///< Get all property names /// Defines a property on the object. bool DefineProperty( const PropertyDescriptor& property ///< Descriptor for the property to be defined ); /// Defines properties on the object. bool DefineProperties( const std::initializer_list& properties ///< List of descriptors for the properties to be defined ); /// Defines properties on the object. bool DefineProperties( const std::vector& properties ///< Vector of descriptors for the properties to be defined ); /// Checks if an object is an instance created by a constructor function. /// /// This is equivalent to the JavaScript `instanceof` operator. bool InstanceOf( const Function& constructor ///< Constructor function ) const; template inline void AddFinalizer(Finalizer finalizeCallback, T* data); template inline void AddFinalizer(Finalizer finalizeCallback, T* data, Hint* finalizeHint); #if NAPI_VERSION >= 8 bool Freeze(); bool Seal(); #endif // NAPI_VERSION >= 8 }; template class External : public Value { public: static External New(napi_env env, T* data); // Finalizer must implement `void operator()(Env env, T* data)`. template static External New(napi_env env, T* data, Finalizer finalizeCallback); // Finalizer must implement `void operator()(Env env, T* data, Hint* hint)`. template static External New(napi_env env, T* data, Finalizer finalizeCallback, Hint* finalizeHint); External(); External(napi_env env, napi_value value); T* Data() const; }; class Array : public Object { public: static Array New(napi_env env); static Array New(napi_env env, size_t length); Array(); Array(napi_env env, napi_value value); uint32_t Length() const; }; /// A JavaScript array buffer value. class ArrayBuffer : public Object { public: /// Creates a new ArrayBuffer instance over a new automatically-allocated buffer. static ArrayBuffer New( napi_env env, ///< Node-API environment size_t byteLength ///< Length of the buffer to be allocated, in bytes ); /// Creates a new ArrayBuffer instance, using an external buffer with /// specified byte length. static ArrayBuffer New( napi_env env, ///< Node-API environment void* externalData, ///< Pointer to the external buffer to be used by ///< the array size_t byteLength ///< Length of the external buffer to be used by the ///< array, in bytes ); /// Creates a new ArrayBuffer instance, using an external buffer with /// specified byte length. template static ArrayBuffer New( napi_env env, ///< Node-API environment void* externalData, ///< Pointer to the external buffer to be used by ///< the array size_t byteLength, ///< Length of the external buffer to be used by the ///< array, /// in bytes Finalizer finalizeCallback ///< Function to be called when the array ///< buffer is destroyed; /// must implement `void operator()(Env env, /// void* externalData)` ); /// Creates a new ArrayBuffer instance, using an external buffer with /// specified byte length. template static ArrayBuffer New( napi_env env, ///< Node-API environment void* externalData, ///< Pointer to the external buffer to be used by ///< the array size_t byteLength, ///< Length of the external buffer to be used by the ///< array, /// in bytes Finalizer finalizeCallback, ///< Function to be called when the array ///< buffer is destroyed; /// must implement `void operator()(Env /// env, void* externalData, Hint* hint)` Hint* finalizeHint ///< Hint (second parameter) to be passed to the ///< finalize callback ); ArrayBuffer(); ///< Creates a new _empty_ ArrayBuffer instance. ArrayBuffer(napi_env env, napi_value value); ///< Wraps a Node-API value primitive. void* Data(); ///< Gets a pointer to the data buffer. size_t ByteLength(); ///< Gets the length of the array buffer in bytes. #if NAPI_VERSION >= 7 bool IsDetached() const; void Detach(); #endif // NAPI_VERSION >= 7 }; /// A JavaScript typed-array value with unknown array type. /// /// For type-specific operations, cast to a `TypedArrayOf` instance using the `As()` /// method: /// /// Napi::TypedArray array = ... /// if (t.TypedArrayType() == napi_int32_array) { /// Napi::Int32Array int32Array = t.As(); /// } class TypedArray : public Object { public: TypedArray(); ///< Creates a new _empty_ TypedArray instance. TypedArray(napi_env env, napi_value value); ///< Wraps a Node-API value primitive. napi_typedarray_type TypedArrayType() const; ///< Gets the type of this typed-array. Napi::ArrayBuffer ArrayBuffer() const; ///< Gets the backing array buffer. uint8_t ElementSize() const; ///< Gets the size in bytes of one element in the array. size_t ElementLength() const; ///< Gets the number of elements in the array. size_t ByteOffset() const; ///< Gets the offset into the buffer where the array starts. size_t ByteLength() const; ///< Gets the length of the array in bytes. protected: /// !cond INTERNAL napi_typedarray_type _type; size_t _length; TypedArray(napi_env env, napi_value value, napi_typedarray_type type, size_t length); static const napi_typedarray_type unknown_array_type = static_cast(-1); template static #if defined(NAPI_HAS_CONSTEXPR) constexpr #endif napi_typedarray_type TypedArrayTypeForPrimitiveType() { return std::is_same::value ? napi_int8_array : std::is_same::value ? napi_uint8_array : std::is_same::value ? napi_int16_array : std::is_same::value ? napi_uint16_array : std::is_same::value ? napi_int32_array : std::is_same::value ? napi_uint32_array : std::is_same::value ? napi_float32_array : std::is_same::value ? napi_float64_array #if NAPI_VERSION > 5 : std::is_same::value ? napi_bigint64_array : std::is_same::value ? napi_biguint64_array #endif // NAPI_VERSION > 5 : unknown_array_type; } /// !endcond }; /// A JavaScript typed-array value with known array type. /// /// Note while it is possible to create and access Uint8 "clamped" arrays using this class, /// the _clamping_ behavior is only applied in JavaScript. template class TypedArrayOf : public TypedArray { public: /// Creates a new TypedArray instance over a new automatically-allocated array buffer. /// /// The array type parameter can normally be omitted (because it is inferred from the template /// parameter T), except when creating a "clamped" array: /// /// Uint8Array::New(env, length, napi_uint8_clamped_array) static TypedArrayOf New( napi_env env, ///< Node-API environment size_t elementLength, ///< Length of the created array, as a number of ///< elements #if defined(NAPI_HAS_CONSTEXPR) napi_typedarray_type type = TypedArray::TypedArrayTypeForPrimitiveType() #else napi_typedarray_type type #endif ///< Type of array, if different from the default array type for the ///< template parameter T. ); /// Creates a new TypedArray instance over a provided array buffer. /// /// The array type parameter can normally be omitted (because it is inferred from the template /// parameter T), except when creating a "clamped" array: /// /// Uint8Array::New(env, length, buffer, 0, napi_uint8_clamped_array) static TypedArrayOf New( napi_env env, ///< Node-API environment size_t elementLength, ///< Length of the created array, as a number of ///< elements Napi::ArrayBuffer arrayBuffer, ///< Backing array buffer instance to use size_t bufferOffset, ///< Offset into the array buffer where the ///< typed-array starts #if defined(NAPI_HAS_CONSTEXPR) napi_typedarray_type type = TypedArray::TypedArrayTypeForPrimitiveType() #else napi_typedarray_type type #endif ///< Type of array, if different from the default array type for the ///< template parameter T. ); TypedArrayOf(); ///< Creates a new _empty_ TypedArrayOf instance. TypedArrayOf(napi_env env, napi_value value); ///< Wraps a Node-API value primitive. T& operator [](size_t index); ///< Gets or sets an element in the array. const T& operator [](size_t index) const; ///< Gets an element in the array. /// Gets a pointer to the array's backing buffer. /// /// This is not necessarily the same as the `ArrayBuffer::Data()` pointer, because the /// typed-array may have a non-zero `ByteOffset()` into the `ArrayBuffer`. T* Data(); /// Gets a pointer to the array's backing buffer. /// /// This is not necessarily the same as the `ArrayBuffer::Data()` pointer, because the /// typed-array may have a non-zero `ByteOffset()` into the `ArrayBuffer`. const T* Data() const; private: T* _data; TypedArrayOf(napi_env env, napi_value value, napi_typedarray_type type, size_t length, T* data); }; /// The DataView provides a low-level interface for reading/writing multiple /// number types in an ArrayBuffer irrespective of the platform's endianness. class DataView : public Object { public: static DataView New(napi_env env, Napi::ArrayBuffer arrayBuffer); static DataView New(napi_env env, Napi::ArrayBuffer arrayBuffer, size_t byteOffset); static DataView New(napi_env env, Napi::ArrayBuffer arrayBuffer, size_t byteOffset, size_t byteLength); DataView(); ///< Creates a new _empty_ DataView instance. DataView(napi_env env, napi_value value); ///< Wraps a Node-API value primitive. Napi::ArrayBuffer ArrayBuffer() const; ///< Gets the backing array buffer. size_t ByteOffset() const; ///< Gets the offset into the buffer where the array starts. size_t ByteLength() const; ///< Gets the length of the array in bytes. void* Data() const; float GetFloat32(size_t byteOffset) const; double GetFloat64(size_t byteOffset) const; int8_t GetInt8(size_t byteOffset) const; int16_t GetInt16(size_t byteOffset) const; int32_t GetInt32(size_t byteOffset) const; uint8_t GetUint8(size_t byteOffset) const; uint16_t GetUint16(size_t byteOffset) const; uint32_t GetUint32(size_t byteOffset) const; void SetFloat32(size_t byteOffset, float value) const; void SetFloat64(size_t byteOffset, double value) const; void SetInt8(size_t byteOffset, int8_t value) const; void SetInt16(size_t byteOffset, int16_t value) const; void SetInt32(size_t byteOffset, int32_t value) const; void SetUint8(size_t byteOffset, uint8_t value) const; void SetUint16(size_t byteOffset, uint16_t value) const; void SetUint32(size_t byteOffset, uint32_t value) const; private: template T ReadData(size_t byteOffset) const; template void WriteData(size_t byteOffset, T value) const; void* _data; size_t _length; }; class Function : public Object { public: using VoidCallback = void (*)(const CallbackInfo& info); using Callback = Value (*)(const CallbackInfo& info); template static Function New(napi_env env, const char* utf8name = nullptr, void* data = nullptr); template static Function New(napi_env env, const char* utf8name = nullptr, void* data = nullptr); template static Function New(napi_env env, const std::string& utf8name, void* data = nullptr); template static Function New(napi_env env, const std::string& utf8name, void* data = nullptr); /// Callable must implement operator() accepting a const CallbackInfo& /// and return either void or Value. template static Function New(napi_env env, Callable cb, const char* utf8name = nullptr, void* data = nullptr); /// Callable must implement operator() accepting a const CallbackInfo& /// and return either void or Value. template static Function New(napi_env env, Callable cb, const std::string& utf8name, void* data = nullptr); Function(); Function(napi_env env, napi_value value); Value operator()(const std::initializer_list& args) const; Value Call(const std::initializer_list& args) const; Value Call(const std::vector& args) const; Value Call(size_t argc, const napi_value* args) const; Value Call(napi_value recv, const std::initializer_list& args) const; Value Call(napi_value recv, const std::vector& args) const; Value Call(napi_value recv, size_t argc, const napi_value* args) const; Value MakeCallback(napi_value recv, const std::initializer_list& args, napi_async_context context = nullptr) const; Value MakeCallback(napi_value recv, const std::vector& args, napi_async_context context = nullptr) const; Value MakeCallback(napi_value recv, size_t argc, const napi_value* args, napi_async_context context = nullptr) const; Object New(const std::initializer_list& args) const; Object New(const std::vector& args) const; Object New(size_t argc, const napi_value* args) const; }; class Promise : public Object { public: class Deferred { public: static Deferred New(napi_env env); Deferred(napi_env env); Napi::Promise Promise() const; Napi::Env Env() const; void Resolve(napi_value value) const; void Reject(napi_value value) const; private: napi_env _env; napi_deferred _deferred; napi_value _promise; }; Promise(napi_env env, napi_value value); }; template class Buffer : public Uint8Array { public: static Buffer New(napi_env env, size_t length); static Buffer New(napi_env env, T* data, size_t length); // Finalizer must implement `void operator()(Env env, T* data)`. template static Buffer New(napi_env env, T* data, size_t length, Finalizer finalizeCallback); // Finalizer must implement `void operator()(Env env, T* data, Hint* hint)`. template static Buffer New(napi_env env, T* data, size_t length, Finalizer finalizeCallback, Hint* finalizeHint); static Buffer Copy(napi_env env, const T* data, size_t length); Buffer(); Buffer(napi_env env, napi_value value); size_t Length() const; T* Data() const; private: mutable size_t _length; mutable T* _data; Buffer(napi_env env, napi_value value, size_t length, T* data); void EnsureInfo() const; }; /// Holds a counted reference to a value; initially a weak reference unless otherwise specified, /// may be changed to/from a strong reference by adjusting the refcount. /// /// The referenced value is not immediately destroyed when the reference count is zero; it is /// merely then eligible for garbage-collection if there are no other references to the value. template class Reference { public: static Reference New(const T& value, uint32_t initialRefcount = 0); Reference(); Reference(napi_env env, napi_ref ref); ~Reference(); // A reference can be moved but cannot be copied. Reference(Reference&& other); Reference& operator =(Reference&& other); NAPI_DISALLOW_ASSIGN(Reference) operator napi_ref() const; bool operator ==(const Reference &other) const; bool operator !=(const Reference &other) const; Napi::Env Env() const; bool IsEmpty() const; // Note when getting the value of a Reference it is usually correct to do so // within a HandleScope so that the value handle gets cleaned up efficiently. T Value() const; uint32_t Ref(); uint32_t Unref(); void Reset(); void Reset(const T& value, uint32_t refcount = 0); // Call this on a reference that is declared as static data, to prevent its // destructor from running at program shutdown time, which would attempt to // reset the reference when the environment is no longer valid. Avoid using // this if at all possible. If you do need to use static data, MAKE SURE to // warn your users that your addon is NOT threadsafe. void SuppressDestruct(); protected: Reference(const Reference&); /// !cond INTERNAL napi_env _env; napi_ref _ref; /// !endcond private: bool _suppressDestruct; }; class ObjectReference: public Reference { public: ObjectReference(); ObjectReference(napi_env env, napi_ref ref); // A reference can be moved but cannot be copied. ObjectReference(Reference&& other); ObjectReference& operator =(Reference&& other); ObjectReference(ObjectReference&& other); ObjectReference& operator =(ObjectReference&& other); NAPI_DISALLOW_ASSIGN(ObjectReference) Napi::Value Get(const char* utf8name) const; Napi::Value Get(const std::string& utf8name) const; bool Set(const char* utf8name, napi_value value); bool Set(const char* utf8name, Napi::Value value); bool Set(const char* utf8name, const char* utf8value); bool Set(const char* utf8name, bool boolValue); bool Set(const char* utf8name, double numberValue); bool Set(const std::string& utf8name, napi_value value); bool Set(const std::string& utf8name, Napi::Value value); bool Set(const std::string& utf8name, std::string& utf8value); bool Set(const std::string& utf8name, bool boolValue); bool Set(const std::string& utf8name, double numberValue); Napi::Value Get(uint32_t index) const; bool Set(uint32_t index, const napi_value value); bool Set(uint32_t index, const Napi::Value value); bool Set(uint32_t index, const char* utf8value); bool Set(uint32_t index, const std::string& utf8value); bool Set(uint32_t index, bool boolValue); bool Set(uint32_t index, double numberValue); protected: ObjectReference(const ObjectReference&); }; class FunctionReference: public Reference { public: FunctionReference(); FunctionReference(napi_env env, napi_ref ref); // A reference can be moved but cannot be copied. FunctionReference(Reference&& other); FunctionReference& operator =(Reference&& other); FunctionReference(FunctionReference&& other); FunctionReference& operator =(FunctionReference&& other); NAPI_DISALLOW_ASSIGN_COPY(FunctionReference) Napi::Value operator ()(const std::initializer_list& args) const; Napi::Value Call(const std::initializer_list& args) const; Napi::Value Call(const std::vector& args) const; Napi::Value Call(napi_value recv, const std::initializer_list& args) const; Napi::Value Call(napi_value recv, const std::vector& args) const; Napi::Value Call(napi_value recv, size_t argc, const napi_value* args) const; Napi::Value MakeCallback(napi_value recv, const std::initializer_list& args, napi_async_context context = nullptr) const; Napi::Value MakeCallback(napi_value recv, const std::vector& args, napi_async_context context = nullptr) const; Napi::Value MakeCallback(napi_value recv, size_t argc, const napi_value* args, napi_async_context context = nullptr) const; Object New(const std::initializer_list& args) const; Object New(const std::vector& args) const; }; // Shortcuts to creating a new reference with inferred type and refcount = 0. template Reference Weak(T value); ObjectReference Weak(Object value); FunctionReference Weak(Function value); // Shortcuts to creating a new reference with inferred type and refcount = 1. template Reference Persistent(T value); ObjectReference Persistent(Object value); FunctionReference Persistent(Function value); /// A persistent reference to a JavaScript error object. Use of this class /// depends somewhat on whether C++ exceptions are enabled at compile time. /// /// ### Handling Errors With C++ Exceptions /// /// If C++ exceptions are enabled, then the `Error` class extends /// `std::exception` and enables integrated error-handling for C++ exceptions /// and JavaScript exceptions. /// /// If a Node-API call fails without executing any JavaScript code (for /// example due to an invalid argument), then the Node-API wrapper /// automatically converts and throws the error as a C++ exception of type /// `Napi::Error`. Or if a JavaScript function called by C++ code via Node-API /// throws a JavaScript exception, then the Node-API wrapper automatically /// converts and throws it as a C++ exception of type `Napi::Error`. /// /// If a C++ exception of type `Napi::Error` escapes from a Node-API C++ /// callback, then the Node-API wrapper automatically converts and throws it /// as a JavaScript exception. Therefore, catching a C++ exception of type /// `Napi::Error` prevents a JavaScript exception from being thrown. /// /// #### Example 1A - Throwing a C++ exception: /// /// Napi::Env env = ... /// throw Napi::Error::New(env, "Example exception"); /// /// Following C++ statements will not be executed. The exception will bubble /// up as a C++ exception of type `Napi::Error`, until it is either caught /// while still in C++, or else automatically propataged as a JavaScript /// exception when the callback returns to JavaScript. /// /// #### Example 2A - Propagating a Node-API C++ exception: /// /// Napi::Function jsFunctionThatThrows = someObj.As(); /// Napi::Value result = jsFunctionThatThrows({ arg1, arg2 }); /// /// Following C++ statements will not be executed. The exception will bubble /// up as a C++ exception of type `Napi::Error`, until it is either caught /// while still in C++, or else automatically propagated as a JavaScript /// exception when the callback returns to JavaScript. /// /// #### Example 3A - Handling a Node-API C++ exception: /// /// Napi::Function jsFunctionThatThrows = someObj.As(); /// Napi::Value result; /// try { /// result = jsFunctionThatThrows({ arg1, arg2 }); /// } catch (const Napi::Error& e) { /// cerr << "Caught JavaScript exception: " + e.what(); /// } /// /// Since the exception was caught here, it will not be propagated as a /// JavaScript exception. /// /// ### Handling Errors Without C++ Exceptions /// /// If C++ exceptions are disabled (by defining `NAPI_DISABLE_CPP_EXCEPTIONS`) /// then this class does not extend `std::exception`, and APIs in the `Napi` /// namespace do not throw C++ exceptions when they fail. Instead, they raise /// _pending_ JavaScript exceptions and return _empty_ `Value`s. Calling code /// should check `Value::IsEmpty()` before attempting to use a returned value, /// and may use methods on the `Env` class to check for, get, and clear a /// pending JavaScript exception. If the pending exception is not cleared, it /// will be thrown when the native callback returns to JavaScript. /// /// #### Example 1B - Throwing a JS exception /// /// Napi::Env env = ... /// Napi::Error::New(env, "Example /// exception").ThrowAsJavaScriptException(); return; /// /// After throwing a JS exception, the code should generally return /// immediately from the native callback, after performing any necessary /// cleanup. /// /// #### Example 2B - Propagating a Node-API JS exception: /// /// Napi::Function jsFunctionThatThrows = someObj.As(); /// Napi::Value result = jsFunctionThatThrows({ arg1, arg2 }); /// if (result.IsEmpty()) return; /// /// An empty value result from a Node-API call indicates an error occurred, /// and a JavaScript exception is pending. To let the exception propagate, the /// code should generally return immediately from the native callback, after /// performing any necessary cleanup. /// /// #### Example 3B - Handling a Node-API JS exception: /// /// Napi::Function jsFunctionThatThrows = someObj.As(); /// Napi::Value result = jsFunctionThatThrows({ arg1, arg2 }); /// if (result.IsEmpty()) { /// Napi::Error e = env.GetAndClearPendingException(); /// cerr << "Caught JavaScript exception: " + e.Message(); /// } /// /// Since the exception was cleared here, it will not be propagated as a /// JavaScript exception after the native callback returns. class Error : public ObjectReference #ifdef NAPI_CPP_EXCEPTIONS , public std::exception #endif // NAPI_CPP_EXCEPTIONS { public: static Error New(napi_env env); static Error New(napi_env env, const char* message); static Error New(napi_env env, const std::string& message); static NAPI_NO_RETURN void Fatal(const char* location, const char* message); Error(); Error(napi_env env, napi_value value); // An error can be moved or copied. Error(Error&& other); Error& operator =(Error&& other); Error(const Error&); Error& operator =(const Error&); const std::string& Message() const NAPI_NOEXCEPT; void ThrowAsJavaScriptException() const; #ifdef NAPI_CPP_EXCEPTIONS const char* what() const NAPI_NOEXCEPT override; #endif // NAPI_CPP_EXCEPTIONS protected: /// !cond INTERNAL using create_error_fn = napi_status (*)(napi_env envb, napi_value code, napi_value msg, napi_value* result); template static TError New(napi_env env, const char* message, size_t length, create_error_fn create_error); /// !endcond private: mutable std::string _message; }; class TypeError : public Error { public: static TypeError New(napi_env env, const char* message); static TypeError New(napi_env env, const std::string& message); TypeError(); TypeError(napi_env env, napi_value value); }; class RangeError : public Error { public: static RangeError New(napi_env env, const char* message); static RangeError New(napi_env env, const std::string& message); RangeError(); RangeError(napi_env env, napi_value value); }; class CallbackInfo { public: CallbackInfo(napi_env env, napi_callback_info info); ~CallbackInfo(); // Disallow copying to prevent multiple free of _dynamicArgs NAPI_DISALLOW_ASSIGN_COPY(CallbackInfo) Napi::Env Env() const; Value NewTarget() const; bool IsConstructCall() const; size_t Length() const; const Value operator [](size_t index) const; Value This() const; void* Data() const; void SetData(void* data); private: const size_t _staticArgCount = 6; napi_env _env; napi_callback_info _info; napi_value _this; size_t _argc; napi_value* _argv; napi_value _staticArgs[6]; napi_value* _dynamicArgs; void* _data; }; class PropertyDescriptor { public: using GetterCallback = Napi::Value (*)(const Napi::CallbackInfo& info); using SetterCallback = void (*)(const Napi::CallbackInfo& info); #ifndef NODE_ADDON_API_DISABLE_DEPRECATED template static PropertyDescriptor Accessor(const char* utf8name, Getter getter, napi_property_attributes attributes = napi_default, void* data = nullptr); template static PropertyDescriptor Accessor(const std::string& utf8name, Getter getter, napi_property_attributes attributes = napi_default, void* data = nullptr); template static PropertyDescriptor Accessor(napi_value name, Getter getter, napi_property_attributes attributes = napi_default, void* data = nullptr); template static PropertyDescriptor Accessor(Name name, Getter getter, napi_property_attributes attributes = napi_default, void* data = nullptr); template static PropertyDescriptor Accessor(const char* utf8name, Getter getter, Setter setter, napi_property_attributes attributes = napi_default, void* data = nullptr); template static PropertyDescriptor Accessor(const std::string& utf8name, Getter getter, Setter setter, napi_property_attributes attributes = napi_default, void* data = nullptr); template static PropertyDescriptor Accessor(napi_value name, Getter getter, Setter setter, napi_property_attributes attributes = napi_default, void* data = nullptr); template static PropertyDescriptor Accessor(Name name, Getter getter, Setter setter, napi_property_attributes attributes = napi_default, void* data = nullptr); template static PropertyDescriptor Function(const char* utf8name, Callable cb, napi_property_attributes attributes = napi_default, void* data = nullptr); template static PropertyDescriptor Function(const std::string& utf8name, Callable cb, napi_property_attributes attributes = napi_default, void* data = nullptr); template static PropertyDescriptor Function(napi_value name, Callable cb, napi_property_attributes attributes = napi_default, void* data = nullptr); template static PropertyDescriptor Function(Name name, Callable cb, napi_property_attributes attributes = napi_default, void* data = nullptr); #endif // !NODE_ADDON_API_DISABLE_DEPRECATED template static PropertyDescriptor Accessor(const char* utf8name, napi_property_attributes attributes = napi_default, void* data = nullptr); template static PropertyDescriptor Accessor(const std::string& utf8name, napi_property_attributes attributes = napi_default, void* data = nullptr); template static PropertyDescriptor Accessor(Name name, napi_property_attributes attributes = napi_default, void* data = nullptr); template static PropertyDescriptor Accessor(const char* utf8name, napi_property_attributes attributes = napi_default, void* data = nullptr); template static PropertyDescriptor Accessor(const std::string& utf8name, napi_property_attributes attributes = napi_default, void* data = nullptr); template static PropertyDescriptor Accessor(Name name, napi_property_attributes attributes = napi_default, void* data = nullptr); template static PropertyDescriptor Accessor(Napi::Env env, Napi::Object object, const char* utf8name, Getter getter, napi_property_attributes attributes = napi_default, void* data = nullptr); template static PropertyDescriptor Accessor(Napi::Env env, Napi::Object object, const std::string& utf8name, Getter getter, napi_property_attributes attributes = napi_default, void* data = nullptr); template static PropertyDescriptor Accessor(Napi::Env env, Napi::Object object, Name name, Getter getter, napi_property_attributes attributes = napi_default, void* data = nullptr); template static PropertyDescriptor Accessor(Napi::Env env, Napi::Object object, const char* utf8name, Getter getter, Setter setter, napi_property_attributes attributes = napi_default, void* data = nullptr); template static PropertyDescriptor Accessor(Napi::Env env, Napi::Object object, const std::string& utf8name, Getter getter, Setter setter, napi_property_attributes attributes = napi_default, void* data = nullptr); template static PropertyDescriptor Accessor(Napi::Env env, Napi::Object object, Name name, Getter getter, Setter setter, napi_property_attributes attributes = napi_default, void* data = nullptr); template static PropertyDescriptor Function(Napi::Env env, Napi::Object object, const char* utf8name, Callable cb, napi_property_attributes attributes = napi_default, void* data = nullptr); template static PropertyDescriptor Function(Napi::Env env, Napi::Object object, const std::string& utf8name, Callable cb, napi_property_attributes attributes = napi_default, void* data = nullptr); template static PropertyDescriptor Function(Napi::Env env, Napi::Object object, Name name, Callable cb, napi_property_attributes attributes = napi_default, void* data = nullptr); static PropertyDescriptor Value(const char* utf8name, napi_value value, napi_property_attributes attributes = napi_default); static PropertyDescriptor Value(const std::string& utf8name, napi_value value, napi_property_attributes attributes = napi_default); static PropertyDescriptor Value(napi_value name, napi_value value, napi_property_attributes attributes = napi_default); static PropertyDescriptor Value(Name name, Napi::Value value, napi_property_attributes attributes = napi_default); PropertyDescriptor(napi_property_descriptor desc); operator napi_property_descriptor&(); operator const napi_property_descriptor&() const; private: napi_property_descriptor _desc; }; /// Property descriptor for use with `ObjectWrap::DefineClass()`. /// /// This is different from the standalone `PropertyDescriptor` because it is specific to each /// `ObjectWrap` subclass. This prevents using descriptors from a different class when /// defining a new class (preventing the callbacks from having incorrect `this` pointers). template class ClassPropertyDescriptor { public: ClassPropertyDescriptor(napi_property_descriptor desc) : _desc(desc) {} operator napi_property_descriptor&() { return _desc; } operator const napi_property_descriptor&() const { return _desc; } private: napi_property_descriptor _desc; }; template struct MethodCallbackData { TCallback callback; void* data; }; template struct AccessorCallbackData { TGetterCallback getterCallback; TSetterCallback setterCallback; void* data; }; template class InstanceWrap { public: using InstanceVoidMethodCallback = void (T::*)(const CallbackInfo& info); using InstanceMethodCallback = Napi::Value (T::*)(const CallbackInfo& info); using InstanceGetterCallback = Napi::Value (T::*)(const CallbackInfo& info); using InstanceSetterCallback = void (T::*)(const CallbackInfo& info, const Napi::Value& value); using PropertyDescriptor = ClassPropertyDescriptor; static PropertyDescriptor InstanceMethod(const char* utf8name, InstanceVoidMethodCallback method, napi_property_attributes attributes = napi_default, void* data = nullptr); static PropertyDescriptor InstanceMethod(const char* utf8name, InstanceMethodCallback method, napi_property_attributes attributes = napi_default, void* data = nullptr); static PropertyDescriptor InstanceMethod(Symbol name, InstanceVoidMethodCallback method, napi_property_attributes attributes = napi_default, void* data = nullptr); static PropertyDescriptor InstanceMethod(Symbol name, InstanceMethodCallback method, napi_property_attributes attributes = napi_default, void* data = nullptr); template static PropertyDescriptor InstanceMethod(const char* utf8name, napi_property_attributes attributes = napi_default, void* data = nullptr); template static PropertyDescriptor InstanceMethod(const char* utf8name, napi_property_attributes attributes = napi_default, void* data = nullptr); template static PropertyDescriptor InstanceMethod(Symbol name, napi_property_attributes attributes = napi_default, void* data = nullptr); template static PropertyDescriptor InstanceMethod(Symbol name, napi_property_attributes attributes = napi_default, void* data = nullptr); static PropertyDescriptor InstanceAccessor(const char* utf8name, InstanceGetterCallback getter, InstanceSetterCallback setter, napi_property_attributes attributes = napi_default, void* data = nullptr); static PropertyDescriptor InstanceAccessor(Symbol name, InstanceGetterCallback getter, InstanceSetterCallback setter, napi_property_attributes attributes = napi_default, void* data = nullptr); template static PropertyDescriptor InstanceAccessor(const char* utf8name, napi_property_attributes attributes = napi_default, void* data = nullptr); template static PropertyDescriptor InstanceAccessor(Symbol name, napi_property_attributes attributes = napi_default, void* data = nullptr); static PropertyDescriptor InstanceValue(const char* utf8name, Napi::Value value, napi_property_attributes attributes = napi_default); static PropertyDescriptor InstanceValue(Symbol name, Napi::Value value, napi_property_attributes attributes = napi_default); protected: static void AttachPropData(napi_env env, napi_value value, const napi_property_descriptor* prop); private: using This = InstanceWrap; using InstanceVoidMethodCallbackData = MethodCallbackData; using InstanceMethodCallbackData = MethodCallbackData; using InstanceAccessorCallbackData = AccessorCallbackData; static napi_value InstanceVoidMethodCallbackWrapper(napi_env env, napi_callback_info info); static napi_value InstanceMethodCallbackWrapper(napi_env env, napi_callback_info info); static napi_value InstanceGetterCallbackWrapper(napi_env env, napi_callback_info info); static napi_value InstanceSetterCallbackWrapper(napi_env env, napi_callback_info info); template static napi_value WrappedMethod(napi_env env, napi_callback_info info) NAPI_NOEXCEPT; template struct SetterTag {}; template static napi_callback WrapSetter(SetterTag) NAPI_NOEXCEPT { return &This::WrappedMethod; } static napi_callback WrapSetter(SetterTag) NAPI_NOEXCEPT { return nullptr; } }; /// Base class to be extended by C++ classes exposed to JavaScript; each C++ class instance gets /// "wrapped" by a JavaScript object that is managed by this class. /// /// At initialization time, the `DefineClass()` method must be used to /// hook up the accessor and method callbacks. It takes a list of /// property descriptors, which can be constructed via the various /// static methods on the base class. /// /// #### Example: /// /// class Example: public Napi::ObjectWrap { /// public: /// static void Initialize(Napi::Env& env, Napi::Object& target) { /// Napi::Function constructor = DefineClass(env, "Example", { /// InstanceAccessor<&Example::GetSomething, &Example::SetSomething>("value"), /// InstanceMethod<&Example::DoSomething>("doSomething"), /// }); /// target.Set("Example", constructor); /// } /// /// Example(const Napi::CallbackInfo& info); // Constructor /// Napi::Value GetSomething(const Napi::CallbackInfo& info); /// void SetSomething(const Napi::CallbackInfo& info, const Napi::Value& value); /// Napi::Value DoSomething(const Napi::CallbackInfo& info); /// } template class ObjectWrap : public InstanceWrap, public Reference { public: ObjectWrap(const CallbackInfo& callbackInfo); virtual ~ObjectWrap(); static T* Unwrap(Object wrapper); // Methods exposed to JavaScript must conform to one of these callback signatures. using StaticVoidMethodCallback = void (*)(const CallbackInfo& info); using StaticMethodCallback = Napi::Value (*)(const CallbackInfo& info); using StaticGetterCallback = Napi::Value (*)(const CallbackInfo& info); using StaticSetterCallback = void (*)(const CallbackInfo& info, const Napi::Value& value); using PropertyDescriptor = ClassPropertyDescriptor; static Function DefineClass(Napi::Env env, const char* utf8name, const std::initializer_list& properties, void* data = nullptr); static Function DefineClass(Napi::Env env, const char* utf8name, const std::vector& properties, void* data = nullptr); static PropertyDescriptor StaticMethod(const char* utf8name, StaticVoidMethodCallback method, napi_property_attributes attributes = napi_default, void* data = nullptr); static PropertyDescriptor StaticMethod(const char* utf8name, StaticMethodCallback method, napi_property_attributes attributes = napi_default, void* data = nullptr); static PropertyDescriptor StaticMethod(Symbol name, StaticVoidMethodCallback method, napi_property_attributes attributes = napi_default, void* data = nullptr); static PropertyDescriptor StaticMethod(Symbol name, StaticMethodCallback method, napi_property_attributes attributes = napi_default, void* data = nullptr); template static PropertyDescriptor StaticMethod(const char* utf8name, napi_property_attributes attributes = napi_default, void* data = nullptr); template static PropertyDescriptor StaticMethod(Symbol name, napi_property_attributes attributes = napi_default, void* data = nullptr); template static PropertyDescriptor StaticMethod(const char* utf8name, napi_property_attributes attributes = napi_default, void* data = nullptr); template static PropertyDescriptor StaticMethod(Symbol name, napi_property_attributes attributes = napi_default, void* data = nullptr); static PropertyDescriptor StaticAccessor(const char* utf8name, StaticGetterCallback getter, StaticSetterCallback setter, napi_property_attributes attributes = napi_default, void* data = nullptr); static PropertyDescriptor StaticAccessor(Symbol name, StaticGetterCallback getter, StaticSetterCallback setter, napi_property_attributes attributes = napi_default, void* data = nullptr); template static PropertyDescriptor StaticAccessor(const char* utf8name, napi_property_attributes attributes = napi_default, void* data = nullptr); template static PropertyDescriptor StaticAccessor(Symbol name, napi_property_attributes attributes = napi_default, void* data = nullptr); static PropertyDescriptor StaticValue(const char* utf8name, Napi::Value value, napi_property_attributes attributes = napi_default); static PropertyDescriptor StaticValue(Symbol name, Napi::Value value, napi_property_attributes attributes = napi_default); virtual void Finalize(Napi::Env env); private: using This = ObjectWrap; static napi_value ConstructorCallbackWrapper(napi_env env, napi_callback_info info); static napi_value StaticVoidMethodCallbackWrapper(napi_env env, napi_callback_info info); static napi_value StaticMethodCallbackWrapper(napi_env env, napi_callback_info info); static napi_value StaticGetterCallbackWrapper(napi_env env, napi_callback_info info); static napi_value StaticSetterCallbackWrapper(napi_env env, napi_callback_info info); static void FinalizeCallback(napi_env env, void* data, void* hint); static Function DefineClass(Napi::Env env, const char* utf8name, const size_t props_count, const napi_property_descriptor* props, void* data = nullptr); using StaticVoidMethodCallbackData = MethodCallbackData; using StaticMethodCallbackData = MethodCallbackData; using StaticAccessorCallbackData = AccessorCallbackData; template static napi_value WrappedMethod(napi_env env, napi_callback_info info) NAPI_NOEXCEPT; template struct StaticSetterTag {}; template static napi_callback WrapStaticSetter(StaticSetterTag) NAPI_NOEXCEPT { return &This::WrappedMethod; } static napi_callback WrapStaticSetter(StaticSetterTag) NAPI_NOEXCEPT { return nullptr; } bool _construction_failed = true; }; class HandleScope { public: HandleScope(napi_env env, napi_handle_scope scope); explicit HandleScope(Napi::Env env); ~HandleScope(); // Disallow copying to prevent double close of napi_handle_scope NAPI_DISALLOW_ASSIGN_COPY(HandleScope) operator napi_handle_scope() const; Napi::Env Env() const; private: napi_env _env; napi_handle_scope _scope; }; class EscapableHandleScope { public: EscapableHandleScope(napi_env env, napi_escapable_handle_scope scope); explicit EscapableHandleScope(Napi::Env env); ~EscapableHandleScope(); // Disallow copying to prevent double close of napi_escapable_handle_scope NAPI_DISALLOW_ASSIGN_COPY(EscapableHandleScope) operator napi_escapable_handle_scope() const; Napi::Env Env() const; Value Escape(napi_value escapee); private: napi_env _env; napi_escapable_handle_scope _scope; }; #if (NAPI_VERSION > 2) class CallbackScope { public: CallbackScope(napi_env env, napi_callback_scope scope); CallbackScope(napi_env env, napi_async_context context); virtual ~CallbackScope(); // Disallow copying to prevent double close of napi_callback_scope NAPI_DISALLOW_ASSIGN_COPY(CallbackScope) operator napi_callback_scope() const; Napi::Env Env() const; private: napi_env _env; napi_callback_scope _scope; }; #endif class AsyncContext { public: explicit AsyncContext(napi_env env, const char* resource_name); explicit AsyncContext(napi_env env, const char* resource_name, const Object& resource); virtual ~AsyncContext(); AsyncContext(AsyncContext&& other); AsyncContext& operator =(AsyncContext&& other); NAPI_DISALLOW_ASSIGN_COPY(AsyncContext) operator napi_async_context() const; Napi::Env Env() const; private: napi_env _env; napi_async_context _context; }; class AsyncWorker { public: virtual ~AsyncWorker(); // An async worker can be moved but cannot be copied. AsyncWorker(AsyncWorker&& other); AsyncWorker& operator =(AsyncWorker&& other); NAPI_DISALLOW_ASSIGN_COPY(AsyncWorker) operator napi_async_work() const; Napi::Env Env() const; void Queue(); void Cancel(); void SuppressDestruct(); ObjectReference& Receiver(); FunctionReference& Callback(); virtual void OnExecute(Napi::Env env); virtual void OnWorkComplete(Napi::Env env, napi_status status); protected: explicit AsyncWorker(const Function& callback); explicit AsyncWorker(const Function& callback, const char* resource_name); explicit AsyncWorker(const Function& callback, const char* resource_name, const Object& resource); explicit AsyncWorker(const Object& receiver, const Function& callback); explicit AsyncWorker(const Object& receiver, const Function& callback, const char* resource_name); explicit AsyncWorker(const Object& receiver, const Function& callback, const char* resource_name, const Object& resource); explicit AsyncWorker(Napi::Env env); explicit AsyncWorker(Napi::Env env, const char* resource_name); explicit AsyncWorker(Napi::Env env, const char* resource_name, const Object& resource); virtual void Execute() = 0; virtual void OnOK(); virtual void OnError(const Error& e); virtual void Destroy(); virtual std::vector GetResult(Napi::Env env); void SetError(const std::string& error); private: static inline void OnAsyncWorkExecute(napi_env env, void* asyncworker); static inline void OnAsyncWorkComplete(napi_env env, napi_status status, void* asyncworker); napi_env _env; napi_async_work _work; ObjectReference _receiver; FunctionReference _callback; std::string _error; bool _suppress_destruct; }; #if (NAPI_VERSION > 3 && !defined(__wasm32__)) class ThreadSafeFunction { public: // This API may only be called from the main thread. template static ThreadSafeFunction New(napi_env env, const Function& callback, ResourceString resourceName, size_t maxQueueSize, size_t initialThreadCount); // This API may only be called from the main thread. template static ThreadSafeFunction New(napi_env env, const Function& callback, ResourceString resourceName, size_t maxQueueSize, size_t initialThreadCount, ContextType* context); // This API may only be called from the main thread. template static ThreadSafeFunction New(napi_env env, const Function& callback, ResourceString resourceName, size_t maxQueueSize, size_t initialThreadCount, Finalizer finalizeCallback); // This API may only be called from the main thread. template static ThreadSafeFunction New(napi_env env, const Function& callback, ResourceString resourceName, size_t maxQueueSize, size_t initialThreadCount, Finalizer finalizeCallback, FinalizerDataType* data); // This API may only be called from the main thread. template static ThreadSafeFunction New(napi_env env, const Function& callback, ResourceString resourceName, size_t maxQueueSize, size_t initialThreadCount, ContextType* context, Finalizer finalizeCallback); // This API may only be called from the main thread. template static ThreadSafeFunction New(napi_env env, const Function& callback, ResourceString resourceName, size_t maxQueueSize, size_t initialThreadCount, ContextType* context, Finalizer finalizeCallback, FinalizerDataType* data); // This API may only be called from the main thread. template static ThreadSafeFunction New(napi_env env, const Function& callback, const Object& resource, ResourceString resourceName, size_t maxQueueSize, size_t initialThreadCount); // This API may only be called from the main thread. template static ThreadSafeFunction New(napi_env env, const Function& callback, const Object& resource, ResourceString resourceName, size_t maxQueueSize, size_t initialThreadCount, ContextType* context); // This API may only be called from the main thread. template static ThreadSafeFunction New(napi_env env, const Function& callback, const Object& resource, ResourceString resourceName, size_t maxQueueSize, size_t initialThreadCount, Finalizer finalizeCallback); // This API may only be called from the main thread. template static ThreadSafeFunction New(napi_env env, const Function& callback, const Object& resource, ResourceString resourceName, size_t maxQueueSize, size_t initialThreadCount, Finalizer finalizeCallback, FinalizerDataType* data); // This API may only be called from the main thread. template static ThreadSafeFunction New(napi_env env, const Function& callback, const Object& resource, ResourceString resourceName, size_t maxQueueSize, size_t initialThreadCount, ContextType* context, Finalizer finalizeCallback); // This API may only be called from the main thread. template static ThreadSafeFunction New(napi_env env, const Function& callback, const Object& resource, ResourceString resourceName, size_t maxQueueSize, size_t initialThreadCount, ContextType* context, Finalizer finalizeCallback, FinalizerDataType* data); ThreadSafeFunction(); ThreadSafeFunction(napi_threadsafe_function tsFunctionValue); operator napi_threadsafe_function() const; // This API may be called from any thread. napi_status BlockingCall() const; // This API may be called from any thread. template napi_status BlockingCall(Callback callback) const; // This API may be called from any thread. template napi_status BlockingCall(DataType* data, Callback callback) const; // This API may be called from any thread. napi_status NonBlockingCall() const; // This API may be called from any thread. template napi_status NonBlockingCall(Callback callback) const; // This API may be called from any thread. template napi_status NonBlockingCall(DataType* data, Callback callback) const; // This API may only be called from the main thread. void Ref(napi_env env) const; // This API may only be called from the main thread. void Unref(napi_env env) const; // This API may be called from any thread. napi_status Acquire() const; // This API may be called from any thread. napi_status Release(); // This API may be called from any thread. napi_status Abort(); struct ConvertibleContext { template operator T*() { return static_cast(context); } void* context; }; // This API may be called from any thread. ConvertibleContext GetContext() const; private: using CallbackWrapper = std::function; template static ThreadSafeFunction New(napi_env env, const Function& callback, const Object& resource, ResourceString resourceName, size_t maxQueueSize, size_t initialThreadCount, ContextType* context, Finalizer finalizeCallback, FinalizerDataType* data, napi_finalize wrapper); napi_status CallInternal(CallbackWrapper* callbackWrapper, napi_threadsafe_function_call_mode mode) const; static void CallJS(napi_env env, napi_value jsCallback, void* context, void* data); napi_threadsafe_function _tsfn; }; // A TypedThreadSafeFunction by default has no context (nullptr) and can // accept any type (void) to its CallJs. template class TypedThreadSafeFunction { public: // This API may only be called from the main thread. // Helper function that returns nullptr if running Node-API 5+, otherwise a // non-empty, no-op Function. This provides the ability to specify at // compile-time a callback parameter to `New` that safely does no action // when targeting _any_ Node-API version. #if NAPI_VERSION > 4 static std::nullptr_t EmptyFunctionFactory(Napi::Env env); #else static Napi::Function EmptyFunctionFactory(Napi::Env env); #endif static Napi::Function FunctionOrEmpty(Napi::Env env, Napi::Function& callback); #if NAPI_VERSION > 4 // This API may only be called from the main thread. // Creates a new threadsafe function with: // Callback [missing] Resource [missing] Finalizer [missing] template static TypedThreadSafeFunction New( napi_env env, ResourceString resourceName, size_t maxQueueSize, size_t initialThreadCount, ContextType* context = nullptr); // This API may only be called from the main thread. // Creates a new threadsafe function with: // Callback [missing] Resource [passed] Finalizer [missing] template static TypedThreadSafeFunction New( napi_env env, const Object& resource, ResourceString resourceName, size_t maxQueueSize, size_t initialThreadCount, ContextType* context = nullptr); // This API may only be called from the main thread. // Creates a new threadsafe function with: // Callback [missing] Resource [missing] Finalizer [passed] template static TypedThreadSafeFunction New( napi_env env, ResourceString resourceName, size_t maxQueueSize, size_t initialThreadCount, ContextType* context, Finalizer finalizeCallback, FinalizerDataType* data = nullptr); // This API may only be called from the main thread. // Creates a new threadsafe function with: // Callback [missing] Resource [passed] Finalizer [passed] template static TypedThreadSafeFunction New( napi_env env, const Object& resource, ResourceString resourceName, size_t maxQueueSize, size_t initialThreadCount, ContextType* context, Finalizer finalizeCallback, FinalizerDataType* data = nullptr); #endif // This API may only be called from the main thread. // Creates a new threadsafe function with: // Callback [passed] Resource [missing] Finalizer [missing] template static TypedThreadSafeFunction New( napi_env env, const Function& callback, ResourceString resourceName, size_t maxQueueSize, size_t initialThreadCount, ContextType* context = nullptr); // This API may only be called from the main thread. // Creates a new threadsafe function with: // Callback [passed] Resource [passed] Finalizer [missing] template static TypedThreadSafeFunction New( napi_env env, const Function& callback, const Object& resource, ResourceString resourceName, size_t maxQueueSize, size_t initialThreadCount, ContextType* context = nullptr); // This API may only be called from the main thread. // Creates a new threadsafe function with: // Callback [passed] Resource [missing] Finalizer [passed] template static TypedThreadSafeFunction New( napi_env env, const Function& callback, ResourceString resourceName, size_t maxQueueSize, size_t initialThreadCount, ContextType* context, Finalizer finalizeCallback, FinalizerDataType* data = nullptr); // This API may only be called from the main thread. // Creates a new threadsafe function with: // Callback [passed] Resource [passed] Finalizer [passed] template static TypedThreadSafeFunction New( napi_env env, CallbackType callback, const Object& resource, ResourceString resourceName, size_t maxQueueSize, size_t initialThreadCount, ContextType* context, Finalizer finalizeCallback, FinalizerDataType* data = nullptr); TypedThreadSafeFunction(); TypedThreadSafeFunction( napi_threadsafe_function tsFunctionValue); operator napi_threadsafe_function() const; // This API may be called from any thread. napi_status BlockingCall(DataType* data = nullptr) const; // This API may be called from any thread. napi_status NonBlockingCall(DataType* data = nullptr) const; // This API may only be called from the main thread. void Ref(napi_env env) const; // This API may only be called from the main thread. void Unref(napi_env env) const; // This API may be called from any thread. napi_status Acquire() const; // This API may be called from any thread. napi_status Release(); // This API may be called from any thread. napi_status Abort(); // This API may be called from any thread. ContextType* GetContext() const; private: template static TypedThreadSafeFunction New( napi_env env, const Function& callback, const Object& resource, ResourceString resourceName, size_t maxQueueSize, size_t initialThreadCount, ContextType* context, Finalizer finalizeCallback, FinalizerDataType* data, napi_finalize wrapper); static void CallJsInternal(napi_env env, napi_value jsCallback, void* context, void* data); protected: napi_threadsafe_function _tsfn; }; template class AsyncProgressWorkerBase : public AsyncWorker { public: virtual void OnWorkProgress(DataType* data) = 0; class ThreadSafeData { public: ThreadSafeData(AsyncProgressWorkerBase* asyncprogressworker, DataType* data) : _asyncprogressworker(asyncprogressworker), _data(data) {} AsyncProgressWorkerBase* asyncprogressworker() { return _asyncprogressworker; }; DataType* data() { return _data; }; private: AsyncProgressWorkerBase* _asyncprogressworker; DataType* _data; }; void OnWorkComplete(Napi::Env env, napi_status status) override; protected: explicit AsyncProgressWorkerBase(const Object& receiver, const Function& callback, const char* resource_name, const Object& resource, size_t queue_size = 1); virtual ~AsyncProgressWorkerBase(); // Optional callback of Napi::ThreadSafeFunction only available after NAPI_VERSION 4. // Refs: https://github.com/nodejs/node/pull/27791 #if NAPI_VERSION > 4 explicit AsyncProgressWorkerBase(Napi::Env env, const char* resource_name, const Object& resource, size_t queue_size = 1); #endif static inline void OnAsyncWorkProgress(Napi::Env env, Napi::Function jsCallback, void* data); napi_status NonBlockingCall(DataType* data); private: ThreadSafeFunction _tsfn; bool _work_completed = false; napi_status _complete_status; static inline void OnThreadSafeFunctionFinalize(Napi::Env env, void* data, AsyncProgressWorkerBase* context); }; template class AsyncProgressWorker : public AsyncProgressWorkerBase { public: virtual ~AsyncProgressWorker(); class ExecutionProgress { friend class AsyncProgressWorker; public: void Signal() const; void Send(const T* data, size_t count) const; private: explicit ExecutionProgress(AsyncProgressWorker* worker) : _worker(worker) {} AsyncProgressWorker* const _worker; }; void OnWorkProgress(void*) override; protected: explicit AsyncProgressWorker(const Function& callback); explicit AsyncProgressWorker(const Function& callback, const char* resource_name); explicit AsyncProgressWorker(const Function& callback, const char* resource_name, const Object& resource); explicit AsyncProgressWorker(const Object& receiver, const Function& callback); explicit AsyncProgressWorker(const Object& receiver, const Function& callback, const char* resource_name); explicit AsyncProgressWorker(const Object& receiver, const Function& callback, const char* resource_name, const Object& resource); // Optional callback of Napi::ThreadSafeFunction only available after NAPI_VERSION 4. // Refs: https://github.com/nodejs/node/pull/27791 #if NAPI_VERSION > 4 explicit AsyncProgressWorker(Napi::Env env); explicit AsyncProgressWorker(Napi::Env env, const char* resource_name); explicit AsyncProgressWorker(Napi::Env env, const char* resource_name, const Object& resource); #endif virtual void Execute(const ExecutionProgress& progress) = 0; virtual void OnProgress(const T* data, size_t count) = 0; private: void Execute() override; void Signal() const; void SendProgress_(const T* data, size_t count); std::mutex _mutex; T* _asyncdata; size_t _asyncsize; }; template class AsyncProgressQueueWorker : public AsyncProgressWorkerBase> { public: virtual ~AsyncProgressQueueWorker() {}; class ExecutionProgress { friend class AsyncProgressQueueWorker; public: void Signal() const; void Send(const T* data, size_t count) const; private: explicit ExecutionProgress(AsyncProgressQueueWorker* worker) : _worker(worker) {} AsyncProgressQueueWorker* const _worker; }; void OnWorkComplete(Napi::Env env, napi_status status) override; void OnWorkProgress(std::pair*) override; protected: explicit AsyncProgressQueueWorker(const Function& callback); explicit AsyncProgressQueueWorker(const Function& callback, const char* resource_name); explicit AsyncProgressQueueWorker(const Function& callback, const char* resource_name, const Object& resource); explicit AsyncProgressQueueWorker(const Object& receiver, const Function& callback); explicit AsyncProgressQueueWorker(const Object& receiver, const Function& callback, const char* resource_name); explicit AsyncProgressQueueWorker(const Object& receiver, const Function& callback, const char* resource_name, const Object& resource); // Optional callback of Napi::ThreadSafeFunction only available after NAPI_VERSION 4. // Refs: https://github.com/nodejs/node/pull/27791 #if NAPI_VERSION > 4 explicit AsyncProgressQueueWorker(Napi::Env env); explicit AsyncProgressQueueWorker(Napi::Env env, const char* resource_name); explicit AsyncProgressQueueWorker(Napi::Env env, const char* resource_name, const Object& resource); #endif virtual void Execute(const ExecutionProgress& progress) = 0; virtual void OnProgress(const T* data, size_t count) = 0; private: void Execute() override; void Signal() const; void SendProgress_(const T* data, size_t count); }; #endif // NAPI_VERSION > 3 && !defined(__wasm32__) // Memory management. class MemoryManagement { public: static int64_t AdjustExternalMemory(Env env, int64_t change_in_bytes); }; // Version management class VersionManagement { public: static uint32_t GetNapiVersion(Env env); static const napi_node_version* GetNodeVersion(Env env); }; #if NAPI_VERSION > 5 template class Addon : public InstanceWrap { public: static inline Object Init(Env env, Object exports); static T* Unwrap(Object wrapper); protected: using AddonProp = ClassPropertyDescriptor; void DefineAddon(Object exports, const std::initializer_list& props); Napi::Object DefineProperties(Object object, const std::initializer_list& props); private: Object entry_point_; }; #endif // NAPI_VERSION > 5 } // namespace Napi // Inline implementations of all the above class methods are included here. // #include "napi-inl.h" // hack-sqlite // #endif // SRC_NAPI_H_ /* file https://github.com/nodejs/node-addon-api/blob/3.2.1/napi-inl.h */ #ifndef SRC_NAPI_INL_H_ #define SRC_NAPI_INL_H_ //////////////////////////////////////////////////////////////////////////////// // Node-API C++ Wrapper Classes // // Inline header-only implementations for "Node-API" ABI-stable C APIs for // Node.js. //////////////////////////////////////////////////////////////////////////////// // Note: Do not include this file directly! Include "napi.h" instead. // #include // #include // #include // #include namespace Napi { // Helpers to handle functions exposed from C++. namespace details { // Attach a data item to an object and delete it when the object gets // garbage-collected. // TODO: Replace this code with `napi_add_finalizer()` whenever it becomes // available on all supported versions of Node.js. template static inline napi_status AttachData(napi_env env, napi_value obj, FreeType* data, napi_finalize finalizer = nullptr, void* hint = nullptr) { napi_status status; if (finalizer == nullptr) { finalizer = [](napi_env /*env*/, void* data, void* /*hint*/) { delete static_cast(data); }; } #if (NAPI_VERSION < 5) napi_value symbol, external; status = napi_create_symbol(env, nullptr, &symbol); if (status == napi_ok) { status = napi_create_external(env, data, finalizer, hint, &external); if (status == napi_ok) { napi_property_descriptor desc = { nullptr, symbol, nullptr, nullptr, nullptr, external, napi_default, nullptr }; status = napi_define_properties(env, obj, 1, &desc); } } #else // NAPI_VERSION >= 5 status = napi_add_finalizer(env, obj, data, finalizer, hint, nullptr); #endif return status; } // For use in JS to C++ callback wrappers to catch any Napi::Error exceptions // and rethrow them as JavaScript exceptions before returning from the callback. template inline napi_value WrapCallback(Callable callback) { #ifdef NAPI_CPP_EXCEPTIONS try { return callback(); } catch (const Error& e) { e.ThrowAsJavaScriptException(); return nullptr; } #else // NAPI_CPP_EXCEPTIONS // When C++ exceptions are disabled, errors are immediately thrown as JS // exceptions, so there is no need to catch and rethrow them here. return callback(); #endif // NAPI_CPP_EXCEPTIONS } // For use in JS to C++ void callback wrappers to catch any Napi::Error // exceptions and rethrow them as JavaScript exceptions before returning from the // callback. template inline void WrapVoidCallback(Callable callback) { #ifdef NAPI_CPP_EXCEPTIONS try { callback(); } catch (const Error& e) { e.ThrowAsJavaScriptException(); } #else // NAPI_CPP_EXCEPTIONS // When C++ exceptions are disabled, errors are immediately thrown as JS // exceptions, so there is no need to catch and rethrow them here. callback(); #endif // NAPI_CPP_EXCEPTIONS } template struct CallbackData { static inline napi_value Wrapper(napi_env env, napi_callback_info info) { return details::WrapCallback([&] { CallbackInfo callbackInfo(env, info); CallbackData* callbackData = static_cast(callbackInfo.Data()); callbackInfo.SetData(callbackData->data); return callbackData->callback(callbackInfo); }); } Callable callback; void* data; }; template struct CallbackData { static inline napi_value Wrapper(napi_env env, napi_callback_info info) { return details::WrapCallback([&] { CallbackInfo callbackInfo(env, info); CallbackData* callbackData = static_cast(callbackInfo.Data()); callbackInfo.SetData(callbackData->data); callbackData->callback(callbackInfo); return nullptr; }); } Callable callback; void* data; }; template static napi_value TemplatedVoidCallback(napi_env env, napi_callback_info info) NAPI_NOEXCEPT { return details::WrapCallback([&] { CallbackInfo cbInfo(env, info); Callback(cbInfo); return nullptr; }); } template static napi_value TemplatedCallback(napi_env env, napi_callback_info info) NAPI_NOEXCEPT { return details::WrapCallback([&] { CallbackInfo cbInfo(env, info); return Callback(cbInfo); }); } template static napi_value TemplatedInstanceCallback(napi_env env, napi_callback_info info) NAPI_NOEXCEPT { return details::WrapCallback([&] { CallbackInfo cbInfo(env, info); T* instance = T::Unwrap(cbInfo.This().As()); return (instance->*UnwrapCallback)(cbInfo); }); } template static napi_value TemplatedInstanceVoidCallback(napi_env env, napi_callback_info info) NAPI_NOEXCEPT { return details::WrapCallback([&] { CallbackInfo cbInfo(env, info); T* instance = T::Unwrap(cbInfo.This().As()); (instance->*UnwrapCallback)(cbInfo); return nullptr; }); } template struct FinalizeData { static inline void Wrapper(napi_env env, void* data, void* finalizeHint) NAPI_NOEXCEPT { WrapVoidCallback([&] { FinalizeData* finalizeData = static_cast(finalizeHint); finalizeData->callback(Env(env), static_cast(data)); delete finalizeData; }); } static inline void WrapperWithHint(napi_env env, void* data, void* finalizeHint) NAPI_NOEXCEPT { WrapVoidCallback([&] { FinalizeData* finalizeData = static_cast(finalizeHint); finalizeData->callback(Env(env), static_cast(data), finalizeData->hint); delete finalizeData; }); } Finalizer callback; Hint* hint; }; #if (NAPI_VERSION > 3 && !defined(__wasm32__)) template , typename FinalizerDataType=void> struct ThreadSafeFinalize { static inline void Wrapper(napi_env env, void* rawFinalizeData, void* /* rawContext */) { if (rawFinalizeData == nullptr) return; ThreadSafeFinalize* finalizeData = static_cast(rawFinalizeData); finalizeData->callback(Env(env)); delete finalizeData; } static inline void FinalizeWrapperWithData(napi_env env, void* rawFinalizeData, void* /* rawContext */) { if (rawFinalizeData == nullptr) return; ThreadSafeFinalize* finalizeData = static_cast(rawFinalizeData); finalizeData->callback(Env(env), finalizeData->data); delete finalizeData; } static inline void FinalizeWrapperWithContext(napi_env env, void* rawFinalizeData, void* rawContext) { if (rawFinalizeData == nullptr) return; ThreadSafeFinalize* finalizeData = static_cast(rawFinalizeData); finalizeData->callback(Env(env), static_cast(rawContext)); delete finalizeData; } static inline void FinalizeFinalizeWrapperWithDataAndContext(napi_env env, void* rawFinalizeData, void* rawContext) { if (rawFinalizeData == nullptr) return; ThreadSafeFinalize* finalizeData = static_cast(rawFinalizeData); finalizeData->callback(Env(env), finalizeData->data, static_cast(rawContext)); delete finalizeData; } FinalizerDataType* data; Finalizer callback; }; template typename std::enable_if::type static inline CallJsWrapper( napi_env env, napi_value jsCallback, void* context, void* data) { call(env, Function(env, jsCallback), static_cast(context), static_cast(data)); } template typename std::enable_if::type static inline CallJsWrapper( napi_env env, napi_value jsCallback, void* /*context*/, void* /*data*/) { if (jsCallback != nullptr) { Function(env, jsCallback).Call(0, nullptr); } } #if NAPI_VERSION > 4 template napi_value DefaultCallbackWrapper(napi_env /*env*/, std::nullptr_t /*cb*/) { return nullptr; } template napi_value DefaultCallbackWrapper(napi_env /*env*/, Napi::Function cb) { return cb; } #else template napi_value DefaultCallbackWrapper(napi_env env, Napi::Function cb) { if (cb.IsEmpty()) { return TSFN::EmptyFunctionFactory(env); } return cb; } #endif // NAPI_VERSION > 4 #endif // NAPI_VERSION > 3 && !defined(__wasm32__) template struct AccessorCallbackData { static inline napi_value GetterWrapper(napi_env env, napi_callback_info info) { return details::WrapCallback([&] { CallbackInfo callbackInfo(env, info); AccessorCallbackData* callbackData = static_cast(callbackInfo.Data()); callbackInfo.SetData(callbackData->data); return callbackData->getterCallback(callbackInfo); }); } static inline napi_value SetterWrapper(napi_env env, napi_callback_info info) { return details::WrapCallback([&] { CallbackInfo callbackInfo(env, info); AccessorCallbackData* callbackData = static_cast(callbackInfo.Data()); callbackInfo.SetData(callbackData->data); callbackData->setterCallback(callbackInfo); return nullptr; }); } Getter getterCallback; Setter setterCallback; void* data; }; } // namespace details #ifndef NODE_ADDON_API_DISABLE_DEPRECATED // hack-sqlite // # include "napi-inl.deprecated.h" #endif // !NODE_ADDON_API_DISABLE_DEPRECATED //////////////////////////////////////////////////////////////////////////////// // Module registration //////////////////////////////////////////////////////////////////////////////// // Register an add-on based on an initializer function. #define NODE_API_MODULE(modname, regfunc) \ static napi_value __napi_##regfunc(napi_env env, napi_value exports) { \ return Napi::RegisterModule(env, exports, regfunc); \ } \ NAPI_MODULE(modname, __napi_##regfunc) // Register an add-on based on a subclass of `Addon` with a custom Node.js // module name. #define NODE_API_NAMED_ADDON(modname, classname) \ static napi_value __napi_ ## classname(napi_env env, \ napi_value exports) { \ return Napi::RegisterModule(env, exports, &classname::Init); \ } \ NAPI_MODULE(modname, __napi_ ## classname) // Register an add-on based on a subclass of `Addon` with the Node.js module // name given by node-gyp from the `target_name` in binding.gyp. #define NODE_API_ADDON(classname) \ NODE_API_NAMED_ADDON(NODE_GYP_MODULE_NAME, classname) // Adapt the NAPI_MODULE registration function: // - Wrap the arguments in NAPI wrappers. // - Catch any NAPI errors and rethrow as JS exceptions. inline napi_value RegisterModule(napi_env env, napi_value exports, ModuleRegisterCallback registerCallback) { return details::WrapCallback([&] { return napi_value(registerCallback(Napi::Env(env), Napi::Object(env, exports))); }); } //////////////////////////////////////////////////////////////////////////////// // Env class //////////////////////////////////////////////////////////////////////////////// inline Env::Env(napi_env env) : _env(env) { } inline Env::operator napi_env() const { return _env; } inline Object Env::Global() const { napi_value value; napi_status status = napi_get_global(*this, &value); NAPI_THROW_IF_FAILED(*this, status, Object()); return Object(*this, value); } inline Value Env::Undefined() const { napi_value value; napi_status status = napi_get_undefined(*this, &value); NAPI_THROW_IF_FAILED(*this, status, Value()); return Value(*this, value); } inline Value Env::Null() const { napi_value value; napi_status status = napi_get_null(*this, &value); NAPI_THROW_IF_FAILED(*this, status, Value()); return Value(*this, value); } inline bool Env::IsExceptionPending() const { bool result; napi_status status = napi_is_exception_pending(_env, &result); if (status != napi_ok) result = false; // Checking for a pending exception shouldn't throw. return result; } inline Error Env::GetAndClearPendingException() { napi_value value; napi_status status = napi_get_and_clear_last_exception(_env, &value); if (status != napi_ok) { // Don't throw another exception when failing to get the exception! return Error(); } return Error(_env, value); } inline Value Env::RunScript(const char* utf8script) { String script = String::New(_env, utf8script); return RunScript(script); } inline Value Env::RunScript(const std::string& utf8script) { return RunScript(utf8script.c_str()); } inline Value Env::RunScript(String script) { napi_value result; napi_status status = napi_run_script(_env, script, &result); NAPI_THROW_IF_FAILED(_env, status, Undefined()); return Value(_env, result); } #if NAPI_VERSION > 5 template fini> inline void Env::SetInstanceData(T* data) { napi_status status = napi_set_instance_data(_env, data, [](napi_env env, void* data, void*) { fini(env, static_cast(data)); }, nullptr); NAPI_THROW_IF_FAILED_VOID(_env, status); } template fini> inline void Env::SetInstanceData(DataType* data, HintType* hint) { napi_status status = napi_set_instance_data(_env, data, [](napi_env env, void* data, void* hint) { fini(env, static_cast(data), static_cast(hint)); }, hint); NAPI_THROW_IF_FAILED_VOID(_env, status); } template inline T* Env::GetInstanceData() { void* data = nullptr; napi_status status = napi_get_instance_data(_env, &data); NAPI_THROW_IF_FAILED(_env, status, nullptr); return static_cast(data); } template void Env::DefaultFini(Env, T* data) { delete data; } template void Env::DefaultFiniWithHint(Env, DataType* data, HintType*) { delete data; } #endif // NAPI_VERSION > 5 //////////////////////////////////////////////////////////////////////////////// // Value class //////////////////////////////////////////////////////////////////////////////// inline Value::Value() : _env(nullptr), _value(nullptr) { } inline Value::Value(napi_env env, napi_value value) : _env(env), _value(value) { } inline Value::operator napi_value() const { return _value; } inline bool Value::operator ==(const Value& other) const { return StrictEquals(other); } inline bool Value::operator !=(const Value& other) const { return !this->operator ==(other); } inline bool Value::StrictEquals(const Value& other) const { bool result; napi_status status = napi_strict_equals(_env, *this, other, &result); NAPI_THROW_IF_FAILED(_env, status, false); return result; } inline Napi::Env Value::Env() const { return Napi::Env(_env); } inline bool Value::IsEmpty() const { return _value == nullptr; } inline napi_valuetype Value::Type() const { if (IsEmpty()) { return napi_undefined; } napi_valuetype type; napi_status status = napi_typeof(_env, _value, &type); NAPI_THROW_IF_FAILED(_env, status, napi_undefined); return type; } inline bool Value::IsUndefined() const { return Type() == napi_undefined; } inline bool Value::IsNull() const { return Type() == napi_null; } inline bool Value::IsBoolean() const { return Type() == napi_boolean; } inline bool Value::IsNumber() const { return Type() == napi_number; } #if NAPI_VERSION > 5 inline bool Value::IsBigInt() const { return Type() == napi_bigint; } #endif // NAPI_VERSION > 5 #if (NAPI_VERSION > 4) inline bool Value::IsDate() const { if (IsEmpty()) { return false; } bool result; napi_status status = napi_is_date(_env, _value, &result); NAPI_THROW_IF_FAILED(_env, status, false); return result; } #endif inline bool Value::IsString() const { return Type() == napi_string; } inline bool Value::IsSymbol() const { return Type() == napi_symbol; } inline bool Value::IsArray() const { if (IsEmpty()) { return false; } bool result; napi_status status = napi_is_array(_env, _value, &result); NAPI_THROW_IF_FAILED(_env, status, false); return result; } inline bool Value::IsArrayBuffer() const { if (IsEmpty()) { return false; } bool result; napi_status status = napi_is_arraybuffer(_env, _value, &result); NAPI_THROW_IF_FAILED(_env, status, false); return result; } inline bool Value::IsTypedArray() const { if (IsEmpty()) { return false; } bool result; napi_status status = napi_is_typedarray(_env, _value, &result); NAPI_THROW_IF_FAILED(_env, status, false); return result; } inline bool Value::IsObject() const { return Type() == napi_object || IsFunction(); } inline bool Value::IsFunction() const { return Type() == napi_function; } inline bool Value::IsPromise() const { if (IsEmpty()) { return false; } bool result; napi_status status = napi_is_promise(_env, _value, &result); NAPI_THROW_IF_FAILED(_env, status, false); return result; } inline bool Value::IsDataView() const { if (IsEmpty()) { return false; } bool result; napi_status status = napi_is_dataview(_env, _value, &result); NAPI_THROW_IF_FAILED(_env, status, false); return result; } inline bool Value::IsBuffer() const { if (IsEmpty()) { return false; } bool result; napi_status status = napi_is_buffer(_env, _value, &result); NAPI_THROW_IF_FAILED(_env, status, false); return result; } inline bool Value::IsExternal() const { return Type() == napi_external; } template inline T Value::As() const { return T(_env, _value); } inline Boolean Value::ToBoolean() const { napi_value result; napi_status status = napi_coerce_to_bool(_env, _value, &result); NAPI_THROW_IF_FAILED(_env, status, Boolean()); return Boolean(_env, result); } inline Number Value::ToNumber() const { napi_value result; napi_status status = napi_coerce_to_number(_env, _value, &result); NAPI_THROW_IF_FAILED(_env, status, Number()); return Number(_env, result); } inline String Value::ToString() const { napi_value result; napi_status status = napi_coerce_to_string(_env, _value, &result); NAPI_THROW_IF_FAILED(_env, status, String()); return String(_env, result); } inline Object Value::ToObject() const { napi_value result; napi_status status = napi_coerce_to_object(_env, _value, &result); NAPI_THROW_IF_FAILED(_env, status, Object()); return Object(_env, result); } //////////////////////////////////////////////////////////////////////////////// // Boolean class //////////////////////////////////////////////////////////////////////////////// inline Boolean Boolean::New(napi_env env, bool val) { napi_value value; napi_status status = napi_get_boolean(env, val, &value); NAPI_THROW_IF_FAILED(env, status, Boolean()); return Boolean(env, value); } inline Boolean::Boolean() : Napi::Value() { } inline Boolean::Boolean(napi_env env, napi_value value) : Napi::Value(env, value) { } inline Boolean::operator bool() const { return Value(); } inline bool Boolean::Value() const { bool result; napi_status status = napi_get_value_bool(_env, _value, &result); NAPI_THROW_IF_FAILED(_env, status, false); return result; } //////////////////////////////////////////////////////////////////////////////// // Number class //////////////////////////////////////////////////////////////////////////////// inline Number Number::New(napi_env env, double val) { napi_value value; napi_status status = napi_create_double(env, val, &value); NAPI_THROW_IF_FAILED(env, status, Number()); return Number(env, value); } inline Number::Number() : Value() { } inline Number::Number(napi_env env, napi_value value) : Value(env, value) { } inline Number::operator int32_t() const { return Int32Value(); } inline Number::operator uint32_t() const { return Uint32Value(); } inline Number::operator int64_t() const { return Int64Value(); } inline Number::operator float() const { return FloatValue(); } inline Number::operator double() const { return DoubleValue(); } inline int32_t Number::Int32Value() const { int32_t result; napi_status status = napi_get_value_int32(_env, _value, &result); NAPI_THROW_IF_FAILED(_env, status, 0); return result; } inline uint32_t Number::Uint32Value() const { uint32_t result; napi_status status = napi_get_value_uint32(_env, _value, &result); NAPI_THROW_IF_FAILED(_env, status, 0); return result; } inline int64_t Number::Int64Value() const { int64_t result; napi_status status = napi_get_value_int64(_env, _value, &result); NAPI_THROW_IF_FAILED(_env, status, 0); return result; } inline float Number::FloatValue() const { return static_cast(DoubleValue()); } inline double Number::DoubleValue() const { double result; napi_status status = napi_get_value_double(_env, _value, &result); NAPI_THROW_IF_FAILED(_env, status, 0); return result; } #if NAPI_VERSION > 5 //////////////////////////////////////////////////////////////////////////////// // BigInt Class //////////////////////////////////////////////////////////////////////////////// inline BigInt BigInt::New(napi_env env, int64_t val) { napi_value value; napi_status status = napi_create_bigint_int64(env, val, &value); NAPI_THROW_IF_FAILED(env, status, BigInt()); return BigInt(env, value); } inline BigInt BigInt::New(napi_env env, uint64_t val) { napi_value value; napi_status status = napi_create_bigint_uint64(env, val, &value); NAPI_THROW_IF_FAILED(env, status, BigInt()); return BigInt(env, value); } inline BigInt BigInt::New(napi_env env, int sign_bit, size_t word_count, const uint64_t* words) { napi_value value; napi_status status = napi_create_bigint_words(env, sign_bit, word_count, words, &value); NAPI_THROW_IF_FAILED(env, status, BigInt()); return BigInt(env, value); } inline BigInt::BigInt() : Value() { } inline BigInt::BigInt(napi_env env, napi_value value) : Value(env, value) { } inline int64_t BigInt::Int64Value(bool* lossless) const { int64_t result; napi_status status = napi_get_value_bigint_int64( _env, _value, &result, lossless); NAPI_THROW_IF_FAILED(_env, status, 0); return result; } inline uint64_t BigInt::Uint64Value(bool* lossless) const { uint64_t result; napi_status status = napi_get_value_bigint_uint64( _env, _value, &result, lossless); NAPI_THROW_IF_FAILED(_env, status, 0); return result; } inline size_t BigInt::WordCount() const { size_t word_count; napi_status status = napi_get_value_bigint_words( _env, _value, nullptr, &word_count, nullptr); NAPI_THROW_IF_FAILED(_env, status, 0); return word_count; } inline void BigInt::ToWords(int* sign_bit, size_t* word_count, uint64_t* words) { napi_status status = napi_get_value_bigint_words( _env, _value, sign_bit, word_count, words); NAPI_THROW_IF_FAILED_VOID(_env, status); } #endif // NAPI_VERSION > 5 #if (NAPI_VERSION > 4) //////////////////////////////////////////////////////////////////////////////// // Date Class //////////////////////////////////////////////////////////////////////////////// inline Date Date::New(napi_env env, double val) { napi_value value; napi_status status = napi_create_date(env, val, &value); NAPI_THROW_IF_FAILED(env, status, Date()); return Date(env, value); } inline Date::Date() : Value() { } inline Date::Date(napi_env env, napi_value value) : Value(env, value) { } inline Date::operator double() const { return ValueOf(); } inline double Date::ValueOf() const { double result; napi_status status = napi_get_date_value( _env, _value, &result); NAPI_THROW_IF_FAILED(_env, status, 0); return result; } #endif //////////////////////////////////////////////////////////////////////////////// // Name class //////////////////////////////////////////////////////////////////////////////// inline Name::Name() : Value() { } inline Name::Name(napi_env env, napi_value value) : Value(env, value) { } //////////////////////////////////////////////////////////////////////////////// // String class //////////////////////////////////////////////////////////////////////////////// inline String String::New(napi_env env, const std::string& val) { return String::New(env, val.c_str(), val.size()); } inline String String::New(napi_env env, const std::u16string& val) { return String::New(env, val.c_str(), val.size()); } inline String String::New(napi_env env, const char* val) { napi_value value; napi_status status = napi_create_string_utf8(env, val, std::strlen(val), &value); NAPI_THROW_IF_FAILED(env, status, String()); return String(env, value); } inline String String::New(napi_env env, const char16_t* val) { napi_value value; napi_status status = napi_create_string_utf16(env, val, std::u16string(val).size(), &value); NAPI_THROW_IF_FAILED(env, status, String()); return String(env, value); } inline String String::New(napi_env env, const char* val, size_t length) { napi_value value; napi_status status = napi_create_string_utf8(env, val, length, &value); NAPI_THROW_IF_FAILED(env, status, String()); return String(env, value); } inline String String::New(napi_env env, const char16_t* val, size_t length) { napi_value value; napi_status status = napi_create_string_utf16(env, val, length, &value); NAPI_THROW_IF_FAILED(env, status, String()); return String(env, value); } inline String::String() : Name() { } inline String::String(napi_env env, napi_value value) : Name(env, value) { } inline String::operator std::string() const { return Utf8Value(); } inline String::operator std::u16string() const { return Utf16Value(); } inline std::string String::Utf8Value() const { size_t length; napi_status status = napi_get_value_string_utf8(_env, _value, nullptr, 0, &length); NAPI_THROW_IF_FAILED(_env, status, ""); std::string value; value.reserve(length + 1); value.resize(length); status = napi_get_value_string_utf8(_env, _value, &value[0], value.capacity(), nullptr); NAPI_THROW_IF_FAILED(_env, status, ""); return value; } inline std::u16string String::Utf16Value() const { size_t length; napi_status status = napi_get_value_string_utf16(_env, _value, nullptr, 0, &length); NAPI_THROW_IF_FAILED(_env, status, NAPI_WIDE_TEXT("")); std::u16string value; value.reserve(length + 1); value.resize(length); status = napi_get_value_string_utf16(_env, _value, &value[0], value.capacity(), nullptr); NAPI_THROW_IF_FAILED(_env, status, NAPI_WIDE_TEXT("")); return value; } //////////////////////////////////////////////////////////////////////////////// // Symbol class //////////////////////////////////////////////////////////////////////////////// inline Symbol Symbol::New(napi_env env, const char* description) { napi_value descriptionValue = description != nullptr ? String::New(env, description) : static_cast(nullptr); return Symbol::New(env, descriptionValue); } inline Symbol Symbol::New(napi_env env, const std::string& description) { napi_value descriptionValue = String::New(env, description); return Symbol::New(env, descriptionValue); } inline Symbol Symbol::New(napi_env env, String description) { napi_value descriptionValue = description; return Symbol::New(env, descriptionValue); } inline Symbol Symbol::New(napi_env env, napi_value description) { napi_value value; napi_status status = napi_create_symbol(env, description, &value); NAPI_THROW_IF_FAILED(env, status, Symbol()); return Symbol(env, value); } inline Symbol Symbol::WellKnown(napi_env env, const std::string& name) { return Napi::Env(env).Global().Get("Symbol").As().Get(name).As(); } inline Symbol::Symbol() : Name() { } inline Symbol::Symbol(napi_env env, napi_value value) : Name(env, value) { } //////////////////////////////////////////////////////////////////////////////// // Automagic value creation //////////////////////////////////////////////////////////////////////////////// namespace details { template struct vf_number { static Number From(napi_env env, T value) { return Number::New(env, static_cast(value)); } }; template<> struct vf_number { static Boolean From(napi_env env, bool value) { return Boolean::New(env, value); } }; struct vf_utf8_charp { static String From(napi_env env, const char* value) { return String::New(env, value); } }; struct vf_utf16_charp { static String From(napi_env env, const char16_t* value) { return String::New(env, value); } }; struct vf_utf8_string { static String From(napi_env env, const std::string& value) { return String::New(env, value); } }; struct vf_utf16_string { static String From(napi_env env, const std::u16string& value) { return String::New(env, value); } }; template struct vf_fallback { static Value From(napi_env env, const T& value) { return Value(env, value); } }; template struct disjunction : std::false_type {}; template struct disjunction : B {}; template struct disjunction : std::conditional>::type {}; template struct can_make_string : disjunction::type, typename std::is_convertible::type, typename std::is_convertible::type, typename std::is_convertible::type> {}; } template Value Value::From(napi_env env, const T& value) { using Helper = typename std::conditional< std::is_integral::value || std::is_floating_point::value, details::vf_number, typename std::conditional< details::can_make_string::value, String, details::vf_fallback >::type >::type; return Helper::From(env, value); } template String String::From(napi_env env, const T& value) { struct Dummy {}; using Helper = typename std::conditional< std::is_convertible::value, details::vf_utf8_charp, typename std::conditional< std::is_convertible::value, details::vf_utf16_charp, typename std::conditional< std::is_convertible::value, details::vf_utf8_string, typename std::conditional< std::is_convertible::value, details::vf_utf16_string, Dummy >::type >::type >::type >::type; return Helper::From(env, value); } //////////////////////////////////////////////////////////////////////////////// // Object class //////////////////////////////////////////////////////////////////////////////// template inline Object::PropertyLValue::operator Value() const { return Object(_env, _object).Get(_key); } template template inline Object::PropertyLValue& Object::PropertyLValue::operator =(ValueType value) { Object(_env, _object).Set(_key, value); return *this; } template inline Object::PropertyLValue::PropertyLValue(Object object, Key key) : _env(object.Env()), _object(object), _key(key) {} inline Object Object::New(napi_env env) { napi_value value; napi_status status = napi_create_object(env, &value); NAPI_THROW_IF_FAILED(env, status, Object()); return Object(env, value); } inline Object::Object() : Value() { } inline Object::Object(napi_env env, napi_value value) : Value(env, value) { } inline Object::PropertyLValue Object::operator [](const char* utf8name) { return PropertyLValue(*this, utf8name); } inline Object::PropertyLValue Object::operator [](const std::string& utf8name) { return PropertyLValue(*this, utf8name); } inline Object::PropertyLValue Object::operator [](uint32_t index) { return PropertyLValue(*this, index); } inline Value Object::operator [](const char* utf8name) const { return Get(utf8name); } inline Value Object::operator [](const std::string& utf8name) const { return Get(utf8name); } inline Value Object::operator [](uint32_t index) const { return Get(index); } inline bool Object::Has(napi_value key) const { bool result; napi_status status = napi_has_property(_env, _value, key, &result); NAPI_THROW_IF_FAILED(_env, status, false); return result; } inline bool Object::Has(Value key) const { bool result; napi_status status = napi_has_property(_env, _value, key, &result); NAPI_THROW_IF_FAILED(_env, status, false); return result; } inline bool Object::Has(const char* utf8name) const { bool result; napi_status status = napi_has_named_property(_env, _value, utf8name, &result); NAPI_THROW_IF_FAILED(_env, status, false); return result; } inline bool Object::Has(const std::string& utf8name) const { return Has(utf8name.c_str()); } inline bool Object::HasOwnProperty(napi_value key) const { bool result; napi_status status = napi_has_own_property(_env, _value, key, &result); NAPI_THROW_IF_FAILED(_env, status, false); return result; } inline bool Object::HasOwnProperty(Value key) const { bool result; napi_status status = napi_has_own_property(_env, _value, key, &result); NAPI_THROW_IF_FAILED(_env, status, false); return result; } inline bool Object::HasOwnProperty(const char* utf8name) const { napi_value key; napi_status status = napi_create_string_utf8(_env, utf8name, std::strlen(utf8name), &key); NAPI_THROW_IF_FAILED(_env, status, false); return HasOwnProperty(key); } inline bool Object::HasOwnProperty(const std::string& utf8name) const { return HasOwnProperty(utf8name.c_str()); } inline Value Object::Get(napi_value key) const { napi_value result; napi_status status = napi_get_property(_env, _value, key, &result); NAPI_THROW_IF_FAILED(_env, status, Value()); return Value(_env, result); } inline Value Object::Get(Value key) const { napi_value result; napi_status status = napi_get_property(_env, _value, key, &result); NAPI_THROW_IF_FAILED(_env, status, Value()); return Value(_env, result); } inline Value Object::Get(const char* utf8name) const { napi_value result; napi_status status = napi_get_named_property(_env, _value, utf8name, &result); NAPI_THROW_IF_FAILED(_env, status, Value()); return Value(_env, result); } inline Value Object::Get(const std::string& utf8name) const { return Get(utf8name.c_str()); } template inline bool Object::Set(napi_value key, const ValueType& value) { napi_status status = napi_set_property(_env, _value, key, Value::From(_env, value)); NAPI_THROW_IF_FAILED(_env, status, false); return true; } template inline bool Object::Set(Value key, const ValueType& value) { napi_status status = napi_set_property(_env, _value, key, Value::From(_env, value)); NAPI_THROW_IF_FAILED(_env, status, false); return true; } template inline bool Object::Set(const char* utf8name, const ValueType& value) { napi_status status = napi_set_named_property(_env, _value, utf8name, Value::From(_env, value)); NAPI_THROW_IF_FAILED(_env, status, false); return true; } template inline bool Object::Set(const std::string& utf8name, const ValueType& value) { return Set(utf8name.c_str(), value); } inline bool Object::Delete(napi_value key) { bool result; napi_status status = napi_delete_property(_env, _value, key, &result); NAPI_THROW_IF_FAILED(_env, status, false); return result; } inline bool Object::Delete(Value key) { bool result; napi_status status = napi_delete_property(_env, _value, key, &result); NAPI_THROW_IF_FAILED(_env, status, false); return result; } inline bool Object::Delete(const char* utf8name) { return Delete(String::New(_env, utf8name)); } inline bool Object::Delete(const std::string& utf8name) { return Delete(String::New(_env, utf8name)); } inline bool Object::Has(uint32_t index) const { bool result; napi_status status = napi_has_element(_env, _value, index, &result); NAPI_THROW_IF_FAILED(_env, status, false); return result; } inline Value Object::Get(uint32_t index) const { napi_value value; napi_status status = napi_get_element(_env, _value, index, &value); NAPI_THROW_IF_FAILED(_env, status, Value()); return Value(_env, value); } template inline bool Object::Set(uint32_t index, const ValueType& value) { napi_status status = napi_set_element(_env, _value, index, Value::From(_env, value)); NAPI_THROW_IF_FAILED(_env, status, false); return true; } inline bool Object::Delete(uint32_t index) { bool result; napi_status status = napi_delete_element(_env, _value, index, &result); NAPI_THROW_IF_FAILED(_env, status, false); return result; } inline Array Object::GetPropertyNames() const { napi_value result; napi_status status = napi_get_property_names(_env, _value, &result); NAPI_THROW_IF_FAILED(_env, status, Array()); return Array(_env, result); } inline bool Object::DefineProperty(const PropertyDescriptor& property) { napi_status status = napi_define_properties(_env, _value, 1, reinterpret_cast(&property)); NAPI_THROW_IF_FAILED(_env, status, false); return true; } inline bool Object::DefineProperties( const std::initializer_list& properties) { napi_status status = napi_define_properties(_env, _value, properties.size(), reinterpret_cast(properties.begin())); NAPI_THROW_IF_FAILED(_env, status, false); return true; } inline bool Object::DefineProperties( const std::vector& properties) { napi_status status = napi_define_properties(_env, _value, properties.size(), reinterpret_cast(properties.data())); NAPI_THROW_IF_FAILED(_env, status, false); return true; } inline bool Object::InstanceOf(const Function& constructor) const { bool result; napi_status status = napi_instanceof(_env, _value, constructor, &result); NAPI_THROW_IF_FAILED(_env, status, false); return result; } template inline void Object::AddFinalizer(Finalizer finalizeCallback, T* data) { details::FinalizeData* finalizeData = new details::FinalizeData( {std::move(finalizeCallback), nullptr}); napi_status status = details::AttachData(_env, *this, data, details::FinalizeData::Wrapper, finalizeData); if (status != napi_ok) { delete finalizeData; NAPI_THROW_IF_FAILED_VOID(_env, status); } } template inline void Object::AddFinalizer(Finalizer finalizeCallback, T* data, Hint* finalizeHint) { details::FinalizeData* finalizeData = new details::FinalizeData( {std::move(finalizeCallback), finalizeHint}); napi_status status = details::AttachData(_env, *this, data, details::FinalizeData::WrapperWithHint, finalizeData); if (status != napi_ok) { delete finalizeData; NAPI_THROW_IF_FAILED_VOID(_env, status); } } #if NAPI_VERSION >= 8 inline bool Object::Freeze() { napi_status status = napi_object_freeze(_env, _value); NAPI_THROW_IF_FAILED(_env, status, false); return true; } inline bool Object::Seal() { napi_status status = napi_object_seal(_env, _value); NAPI_THROW_IF_FAILED(_env, status, false); return true; } #endif // NAPI_VERSION >= 8 //////////////////////////////////////////////////////////////////////////////// // External class //////////////////////////////////////////////////////////////////////////////// template inline External External::New(napi_env env, T* data) { napi_value value; napi_status status = napi_create_external(env, data, nullptr, nullptr, &value); NAPI_THROW_IF_FAILED(env, status, External()); return External(env, value); } template template inline External External::New(napi_env env, T* data, Finalizer finalizeCallback) { napi_value value; details::FinalizeData* finalizeData = new details::FinalizeData( {std::move(finalizeCallback), nullptr}); napi_status status = napi_create_external( env, data, details::FinalizeData::Wrapper, finalizeData, &value); if (status != napi_ok) { delete finalizeData; NAPI_THROW_IF_FAILED(env, status, External()); } return External(env, value); } template template inline External External::New(napi_env env, T* data, Finalizer finalizeCallback, Hint* finalizeHint) { napi_value value; details::FinalizeData* finalizeData = new details::FinalizeData( {std::move(finalizeCallback), finalizeHint}); napi_status status = napi_create_external( env, data, details::FinalizeData::WrapperWithHint, finalizeData, &value); if (status != napi_ok) { delete finalizeData; NAPI_THROW_IF_FAILED(env, status, External()); } return External(env, value); } template inline External::External() : Value() { } template inline External::External(napi_env env, napi_value value) : Value(env, value) { } template inline T* External::Data() const { void* data; napi_status status = napi_get_value_external(_env, _value, &data); NAPI_THROW_IF_FAILED(_env, status, nullptr); return reinterpret_cast(data); } //////////////////////////////////////////////////////////////////////////////// // Array class //////////////////////////////////////////////////////////////////////////////// inline Array Array::New(napi_env env) { napi_value value; napi_status status = napi_create_array(env, &value); NAPI_THROW_IF_FAILED(env, status, Array()); return Array(env, value); } inline Array Array::New(napi_env env, size_t length) { napi_value value; napi_status status = napi_create_array_with_length(env, length, &value); NAPI_THROW_IF_FAILED(env, status, Array()); return Array(env, value); } inline Array::Array() : Object() { } inline Array::Array(napi_env env, napi_value value) : Object(env, value) { } inline uint32_t Array::Length() const { uint32_t result; napi_status status = napi_get_array_length(_env, _value, &result); NAPI_THROW_IF_FAILED(_env, status, 0); return result; } //////////////////////////////////////////////////////////////////////////////// // ArrayBuffer class //////////////////////////////////////////////////////////////////////////////// inline ArrayBuffer ArrayBuffer::New(napi_env env, size_t byteLength) { napi_value value; void* data; napi_status status = napi_create_arraybuffer(env, byteLength, &data, &value); NAPI_THROW_IF_FAILED(env, status, ArrayBuffer()); return ArrayBuffer(env, value); } inline ArrayBuffer ArrayBuffer::New(napi_env env, void* externalData, size_t byteLength) { napi_value value; napi_status status = napi_create_external_arraybuffer( env, externalData, byteLength, nullptr, nullptr, &value); NAPI_THROW_IF_FAILED(env, status, ArrayBuffer()); return ArrayBuffer(env, value); } template inline ArrayBuffer ArrayBuffer::New(napi_env env, void* externalData, size_t byteLength, Finalizer finalizeCallback) { napi_value value; details::FinalizeData* finalizeData = new details::FinalizeData( {std::move(finalizeCallback), nullptr}); napi_status status = napi_create_external_arraybuffer( env, externalData, byteLength, details::FinalizeData::Wrapper, finalizeData, &value); if (status != napi_ok) { delete finalizeData; NAPI_THROW_IF_FAILED(env, status, ArrayBuffer()); } return ArrayBuffer(env, value); } template inline ArrayBuffer ArrayBuffer::New(napi_env env, void* externalData, size_t byteLength, Finalizer finalizeCallback, Hint* finalizeHint) { napi_value value; details::FinalizeData* finalizeData = new details::FinalizeData( {std::move(finalizeCallback), finalizeHint}); napi_status status = napi_create_external_arraybuffer( env, externalData, byteLength, details::FinalizeData::WrapperWithHint, finalizeData, &value); if (status != napi_ok) { delete finalizeData; NAPI_THROW_IF_FAILED(env, status, ArrayBuffer()); } return ArrayBuffer(env, value); } inline ArrayBuffer::ArrayBuffer() : Object() { } inline ArrayBuffer::ArrayBuffer(napi_env env, napi_value value) : Object(env, value) { } inline void* ArrayBuffer::Data() { void* data; napi_status status = napi_get_arraybuffer_info(_env, _value, &data, nullptr); NAPI_THROW_IF_FAILED(_env, status, nullptr); return data; } inline size_t ArrayBuffer::ByteLength() { size_t length; napi_status status = napi_get_arraybuffer_info(_env, _value, nullptr, &length); NAPI_THROW_IF_FAILED(_env, status, 0); return length; } #if NAPI_VERSION >= 7 inline bool ArrayBuffer::IsDetached() const { bool detached; napi_status status = napi_is_detached_arraybuffer(_env, _value, &detached); NAPI_THROW_IF_FAILED(_env, status, false); return detached; } inline void ArrayBuffer::Detach() { napi_status status = napi_detach_arraybuffer(_env, _value); NAPI_THROW_IF_FAILED_VOID(_env, status); } #endif // NAPI_VERSION >= 7 //////////////////////////////////////////////////////////////////////////////// // DataView class //////////////////////////////////////////////////////////////////////////////// inline DataView DataView::New(napi_env env, Napi::ArrayBuffer arrayBuffer) { return New(env, arrayBuffer, 0, arrayBuffer.ByteLength()); } inline DataView DataView::New(napi_env env, Napi::ArrayBuffer arrayBuffer, size_t byteOffset) { if (byteOffset > arrayBuffer.ByteLength()) { NAPI_THROW(RangeError::New(env, "Start offset is outside the bounds of the buffer"), DataView()); } return New(env, arrayBuffer, byteOffset, arrayBuffer.ByteLength() - byteOffset); } inline DataView DataView::New(napi_env env, Napi::ArrayBuffer arrayBuffer, size_t byteOffset, size_t byteLength) { if (byteOffset + byteLength > arrayBuffer.ByteLength()) { NAPI_THROW(RangeError::New(env, "Invalid DataView length"), DataView()); } napi_value value; napi_status status = napi_create_dataview( env, byteLength, arrayBuffer, byteOffset, &value); NAPI_THROW_IF_FAILED(env, status, DataView()); return DataView(env, value); } inline DataView::DataView() : Object() { } inline DataView::DataView(napi_env env, napi_value value) : Object(env, value) { napi_status status = napi_get_dataview_info( _env, _value /* dataView */, &_length /* byteLength */, &_data /* data */, nullptr /* arrayBuffer */, nullptr /* byteOffset */); NAPI_THROW_IF_FAILED_VOID(_env, status); } inline Napi::ArrayBuffer DataView::ArrayBuffer() const { napi_value arrayBuffer; napi_status status = napi_get_dataview_info( _env, _value /* dataView */, nullptr /* byteLength */, nullptr /* data */, &arrayBuffer /* arrayBuffer */, nullptr /* byteOffset */); NAPI_THROW_IF_FAILED(_env, status, Napi::ArrayBuffer()); return Napi::ArrayBuffer(_env, arrayBuffer); } inline size_t DataView::ByteOffset() const { size_t byteOffset; napi_status status = napi_get_dataview_info( _env, _value /* dataView */, nullptr /* byteLength */, nullptr /* data */, nullptr /* arrayBuffer */, &byteOffset /* byteOffset */); NAPI_THROW_IF_FAILED(_env, status, 0); return byteOffset; } inline size_t DataView::ByteLength() const { return _length; } inline void* DataView::Data() const { return _data; } inline float DataView::GetFloat32(size_t byteOffset) const { return ReadData(byteOffset); } inline double DataView::GetFloat64(size_t byteOffset) const { return ReadData(byteOffset); } inline int8_t DataView::GetInt8(size_t byteOffset) const { return ReadData(byteOffset); } inline int16_t DataView::GetInt16(size_t byteOffset) const { return ReadData(byteOffset); } inline int32_t DataView::GetInt32(size_t byteOffset) const { return ReadData(byteOffset); } inline uint8_t DataView::GetUint8(size_t byteOffset) const { return ReadData(byteOffset); } inline uint16_t DataView::GetUint16(size_t byteOffset) const { return ReadData(byteOffset); } inline uint32_t DataView::GetUint32(size_t byteOffset) const { return ReadData(byteOffset); } inline void DataView::SetFloat32(size_t byteOffset, float value) const { WriteData(byteOffset, value); } inline void DataView::SetFloat64(size_t byteOffset, double value) const { WriteData(byteOffset, value); } inline void DataView::SetInt8(size_t byteOffset, int8_t value) const { WriteData(byteOffset, value); } inline void DataView::SetInt16(size_t byteOffset, int16_t value) const { WriteData(byteOffset, value); } inline void DataView::SetInt32(size_t byteOffset, int32_t value) const { WriteData(byteOffset, value); } inline void DataView::SetUint8(size_t byteOffset, uint8_t value) const { WriteData(byteOffset, value); } inline void DataView::SetUint16(size_t byteOffset, uint16_t value) const { WriteData(byteOffset, value); } inline void DataView::SetUint32(size_t byteOffset, uint32_t value) const { WriteData(byteOffset, value); } template inline T DataView::ReadData(size_t byteOffset) const { if (byteOffset + sizeof(T) > _length || byteOffset + sizeof(T) < byteOffset) { // overflow NAPI_THROW(RangeError::New(_env, "Offset is outside the bounds of the DataView"), 0); } return *reinterpret_cast(static_cast(_data) + byteOffset); } template inline void DataView::WriteData(size_t byteOffset, T value) const { if (byteOffset + sizeof(T) > _length || byteOffset + sizeof(T) < byteOffset) { // overflow NAPI_THROW_VOID(RangeError::New(_env, "Offset is outside the bounds of the DataView")); } *reinterpret_cast(static_cast(_data) + byteOffset) = value; } //////////////////////////////////////////////////////////////////////////////// // TypedArray class //////////////////////////////////////////////////////////////////////////////// inline TypedArray::TypedArray() : Object(), _type(TypedArray::unknown_array_type), _length(0) { } inline TypedArray::TypedArray(napi_env env, napi_value value) : Object(env, value), _type(TypedArray::unknown_array_type), _length(0) { } inline TypedArray::TypedArray(napi_env env, napi_value value, napi_typedarray_type type, size_t length) : Object(env, value), _type(type), _length(length) { } inline napi_typedarray_type TypedArray::TypedArrayType() const { if (_type == TypedArray::unknown_array_type) { napi_status status = napi_get_typedarray_info(_env, _value, &const_cast(this)->_type, &const_cast(this)->_length, nullptr, nullptr, nullptr); NAPI_THROW_IF_FAILED(_env, status, napi_int8_array); } return _type; } inline uint8_t TypedArray::ElementSize() const { switch (TypedArrayType()) { case napi_int8_array: case napi_uint8_array: case napi_uint8_clamped_array: return 1; case napi_int16_array: case napi_uint16_array: return 2; case napi_int32_array: case napi_uint32_array: case napi_float32_array: return 4; case napi_float64_array: #if (NAPI_VERSION > 5) case napi_bigint64_array: case napi_biguint64_array: #endif // (NAPI_VERSION > 5) return 8; default: return 0; } } inline size_t TypedArray::ElementLength() const { if (_type == TypedArray::unknown_array_type) { napi_status status = napi_get_typedarray_info(_env, _value, &const_cast(this)->_type, &const_cast(this)->_length, nullptr, nullptr, nullptr); NAPI_THROW_IF_FAILED(_env, status, 0); } return _length; } inline size_t TypedArray::ByteOffset() const { size_t byteOffset; napi_status status = napi_get_typedarray_info( _env, _value, nullptr, nullptr, nullptr, nullptr, &byteOffset); NAPI_THROW_IF_FAILED(_env, status, 0); return byteOffset; } inline size_t TypedArray::ByteLength() const { return ElementSize() * ElementLength(); } inline Napi::ArrayBuffer TypedArray::ArrayBuffer() const { napi_value arrayBuffer; napi_status status = napi_get_typedarray_info( _env, _value, nullptr, nullptr, nullptr, &arrayBuffer, nullptr); NAPI_THROW_IF_FAILED(_env, status, Napi::ArrayBuffer()); return Napi::ArrayBuffer(_env, arrayBuffer); } //////////////////////////////////////////////////////////////////////////////// // TypedArrayOf class //////////////////////////////////////////////////////////////////////////////// template inline TypedArrayOf TypedArrayOf::New(napi_env env, size_t elementLength, napi_typedarray_type type) { Napi::ArrayBuffer arrayBuffer = Napi::ArrayBuffer::New(env, elementLength * sizeof (T)); return New(env, elementLength, arrayBuffer, 0, type); } template inline TypedArrayOf TypedArrayOf::New(napi_env env, size_t elementLength, Napi::ArrayBuffer arrayBuffer, size_t bufferOffset, napi_typedarray_type type) { napi_value value; napi_status status = napi_create_typedarray( env, type, elementLength, arrayBuffer, bufferOffset, &value); NAPI_THROW_IF_FAILED(env, status, TypedArrayOf()); return TypedArrayOf( env, value, type, elementLength, reinterpret_cast(reinterpret_cast(arrayBuffer.Data()) + bufferOffset)); } template inline TypedArrayOf::TypedArrayOf() : TypedArray(), _data(nullptr) { } template inline TypedArrayOf::TypedArrayOf(napi_env env, napi_value value) : TypedArray(env, value), _data(nullptr) { napi_status status = napi_ok; if (value != nullptr) { status = napi_get_typedarray_info( _env, _value, &_type, &_length, reinterpret_cast(&_data), nullptr, nullptr); } else { _type = TypedArrayTypeForPrimitiveType(); _length = 0; } NAPI_THROW_IF_FAILED_VOID(_env, status); } template inline TypedArrayOf::TypedArrayOf(napi_env env, napi_value value, napi_typedarray_type type, size_t length, T* data) : TypedArray(env, value, type, length), _data(data) { if (!(type == TypedArrayTypeForPrimitiveType() || (type == napi_uint8_clamped_array && std::is_same::value))) { NAPI_THROW_VOID(TypeError::New(env, "Array type must match the template parameter. " "(Uint8 arrays may optionally have the \"clamped\" array type.)")); } } template inline T& TypedArrayOf::operator [](size_t index) { return _data[index]; } template inline const T& TypedArrayOf::operator [](size_t index) const { return _data[index]; } template inline T* TypedArrayOf::Data() { return _data; } template inline const T* TypedArrayOf::Data() const { return _data; } //////////////////////////////////////////////////////////////////////////////// // Function class //////////////////////////////////////////////////////////////////////////////// template static inline napi_status CreateFunction(napi_env env, const char* utf8name, napi_callback cb, CbData* data, napi_value* result) { napi_status status = napi_create_function(env, utf8name, NAPI_AUTO_LENGTH, cb, data, result); if (status == napi_ok) { status = Napi::details::AttachData(env, *result, data); } return status; } template inline Function Function::New(napi_env env, const char* utf8name, void* data) { napi_value result = nullptr; napi_status status = napi_create_function(env, utf8name, NAPI_AUTO_LENGTH, details::TemplatedVoidCallback, data, &result); NAPI_THROW_IF_FAILED(env, status, Function()); return Function(env, result); } template inline Function Function::New(napi_env env, const char* utf8name, void* data) { napi_value result = nullptr; napi_status status = napi_create_function(env, utf8name, NAPI_AUTO_LENGTH, details::TemplatedCallback, data, &result); NAPI_THROW_IF_FAILED(env, status, Function()); return Function(env, result); } template inline Function Function::New(napi_env env, const std::string& utf8name, void* data) { return Function::New(env, utf8name.c_str(), data); } template inline Function Function::New(napi_env env, const std::string& utf8name, void* data) { return Function::New(env, utf8name.c_str(), data); } template inline Function Function::New(napi_env env, Callable cb, const char* utf8name, void* data) { using ReturnType = decltype(cb(CallbackInfo(nullptr, nullptr))); using CbData = details::CallbackData; auto callbackData = new CbData({ cb, data }); napi_value value; napi_status status = CreateFunction(env, utf8name, CbData::Wrapper, callbackData, &value); if (status != napi_ok) { delete callbackData; NAPI_THROW_IF_FAILED(env, status, Function()); } return Function(env, value); } template inline Function Function::New(napi_env env, Callable cb, const std::string& utf8name, void* data) { return New(env, cb, utf8name.c_str(), data); } inline Function::Function() : Object() { } inline Function::Function(napi_env env, napi_value value) : Object(env, value) { } inline Value Function::operator ()(const std::initializer_list& args) const { return Call(Env().Undefined(), args); } inline Value Function::Call(const std::initializer_list& args) const { return Call(Env().Undefined(), args); } inline Value Function::Call(const std::vector& args) const { return Call(Env().Undefined(), args); } inline Value Function::Call(size_t argc, const napi_value* args) const { return Call(Env().Undefined(), argc, args); } inline Value Function::Call(napi_value recv, const std::initializer_list& args) const { return Call(recv, args.size(), args.begin()); } inline Value Function::Call(napi_value recv, const std::vector& args) const { return Call(recv, args.size(), args.data()); } inline Value Function::Call(napi_value recv, size_t argc, const napi_value* args) const { napi_value result; napi_status status = napi_call_function( _env, recv, _value, argc, args, &result); NAPI_THROW_IF_FAILED(_env, status, Value()); return Value(_env, result); } inline Value Function::MakeCallback( napi_value recv, const std::initializer_list& args, napi_async_context context) const { return MakeCallback(recv, args.size(), args.begin(), context); } inline Value Function::MakeCallback( napi_value recv, const std::vector& args, napi_async_context context) const { return MakeCallback(recv, args.size(), args.data(), context); } inline Value Function::MakeCallback( napi_value recv, size_t argc, const napi_value* args, napi_async_context context) const { napi_value result; napi_status status = napi_make_callback( _env, context, recv, _value, argc, args, &result); NAPI_THROW_IF_FAILED(_env, status, Value()); return Value(_env, result); } inline Object Function::New(const std::initializer_list& args) const { return New(args.size(), args.begin()); } inline Object Function::New(const std::vector& args) const { return New(args.size(), args.data()); } inline Object Function::New(size_t argc, const napi_value* args) const { napi_value result; napi_status status = napi_new_instance( _env, _value, argc, args, &result); NAPI_THROW_IF_FAILED(_env, status, Object()); return Object(_env, result); } //////////////////////////////////////////////////////////////////////////////// // Promise class //////////////////////////////////////////////////////////////////////////////// inline Promise::Deferred Promise::Deferred::New(napi_env env) { return Promise::Deferred(env); } inline Promise::Deferred::Deferred(napi_env env) : _env(env) { napi_status status = napi_create_promise(_env, &_deferred, &_promise); NAPI_THROW_IF_FAILED_VOID(_env, status); } inline Promise Promise::Deferred::Promise() const { return Napi::Promise(_env, _promise); } inline Napi::Env Promise::Deferred::Env() const { return Napi::Env(_env); } inline void Promise::Deferred::Resolve(napi_value value) const { napi_status status = napi_resolve_deferred(_env, _deferred, value); NAPI_THROW_IF_FAILED_VOID(_env, status); } inline void Promise::Deferred::Reject(napi_value value) const { napi_status status = napi_reject_deferred(_env, _deferred, value); NAPI_THROW_IF_FAILED_VOID(_env, status); } inline Promise::Promise(napi_env env, napi_value value) : Object(env, value) { } //////////////////////////////////////////////////////////////////////////////// // Buffer class //////////////////////////////////////////////////////////////////////////////// template inline Buffer Buffer::New(napi_env env, size_t length) { napi_value value; void* data; napi_status status = napi_create_buffer(env, length * sizeof (T), &data, &value); NAPI_THROW_IF_FAILED(env, status, Buffer()); return Buffer(env, value, length, static_cast(data)); } template inline Buffer Buffer::New(napi_env env, T* data, size_t length) { napi_value value; napi_status status = napi_create_external_buffer( env, length * sizeof (T), data, nullptr, nullptr, &value); NAPI_THROW_IF_FAILED(env, status, Buffer()); return Buffer(env, value, length, data); } template template inline Buffer Buffer::New(napi_env env, T* data, size_t length, Finalizer finalizeCallback) { napi_value value; details::FinalizeData* finalizeData = new details::FinalizeData( {std::move(finalizeCallback), nullptr}); napi_status status = napi_create_external_buffer( env, length * sizeof (T), data, details::FinalizeData::Wrapper, finalizeData, &value); if (status != napi_ok) { delete finalizeData; NAPI_THROW_IF_FAILED(env, status, Buffer()); } return Buffer(env, value, length, data); } template template inline Buffer Buffer::New(napi_env env, T* data, size_t length, Finalizer finalizeCallback, Hint* finalizeHint) { napi_value value; details::FinalizeData* finalizeData = new details::FinalizeData( {std::move(finalizeCallback), finalizeHint}); napi_status status = napi_create_external_buffer( env, length * sizeof (T), data, details::FinalizeData::WrapperWithHint, finalizeData, &value); if (status != napi_ok) { delete finalizeData; NAPI_THROW_IF_FAILED(env, status, Buffer()); } return Buffer(env, value, length, data); } template inline Buffer Buffer::Copy(napi_env env, const T* data, size_t length) { napi_value value; napi_status status = napi_create_buffer_copy( env, length * sizeof (T), data, nullptr, &value); NAPI_THROW_IF_FAILED(env, status, Buffer()); return Buffer(env, value); } template inline Buffer::Buffer() : Uint8Array(), _length(0), _data(nullptr) { } template inline Buffer::Buffer(napi_env env, napi_value value) : Uint8Array(env, value), _length(0), _data(nullptr) { } template inline Buffer::Buffer(napi_env env, napi_value value, size_t length, T* data) : Uint8Array(env, value), _length(length), _data(data) { } template inline size_t Buffer::Length() const { EnsureInfo(); return _length; } template inline T* Buffer::Data() const { EnsureInfo(); return _data; } template inline void Buffer::EnsureInfo() const { // The Buffer instance may have been constructed from a napi_value whose // length/data are not yet known. Fetch and cache these values just once, // since they can never change during the lifetime of the Buffer. if (_data == nullptr) { size_t byteLength; void* voidData; napi_status status = napi_get_buffer_info(_env, _value, &voidData, &byteLength); NAPI_THROW_IF_FAILED_VOID(_env, status); _length = byteLength / sizeof (T); _data = static_cast(voidData); } } //////////////////////////////////////////////////////////////////////////////// // Error class //////////////////////////////////////////////////////////////////////////////// inline Error Error::New(napi_env env) { napi_status status; napi_value error = nullptr; bool is_exception_pending; const napi_extended_error_info* info; // We must retrieve the last error info before doing anything else, because // doing anything else will replace the last error info. status = napi_get_last_error_info(env, &info); NAPI_FATAL_IF_FAILED(status, "Error::New", "napi_get_last_error_info"); status = napi_is_exception_pending(env, &is_exception_pending); NAPI_FATAL_IF_FAILED(status, "Error::New", "napi_is_exception_pending"); // A pending exception takes precedence over any internal error status. if (is_exception_pending) { status = napi_get_and_clear_last_exception(env, &error); NAPI_FATAL_IF_FAILED(status, "Error::New", "napi_get_and_clear_last_exception"); } else { const char* error_message = info->error_message != nullptr ? info->error_message : "Error in native callback"; napi_value message; status = napi_create_string_utf8( env, error_message, std::strlen(error_message), &message); NAPI_FATAL_IF_FAILED(status, "Error::New", "napi_create_string_utf8"); switch (info->error_code) { case napi_object_expected: case napi_string_expected: case napi_boolean_expected: case napi_number_expected: status = napi_create_type_error(env, nullptr, message, &error); break; default: status = napi_create_error(env, nullptr, message, &error); break; } NAPI_FATAL_IF_FAILED(status, "Error::New", "napi_create_error"); } return Error(env, error); } inline Error Error::New(napi_env env, const char* message) { return Error::New(env, message, std::strlen(message), napi_create_error); } inline Error Error::New(napi_env env, const std::string& message) { return Error::New(env, message.c_str(), message.size(), napi_create_error); } inline NAPI_NO_RETURN void Error::Fatal(const char* location, const char* message) { napi_fatal_error(location, NAPI_AUTO_LENGTH, message, NAPI_AUTO_LENGTH); } inline Error::Error() : ObjectReference() { } inline Error::Error(napi_env env, napi_value value) : ObjectReference(env, nullptr) { if (value != nullptr) { napi_status status = napi_create_reference(env, value, 1, &_ref); // Avoid infinite recursion in the failure case. // Don't try to construct & throw another Error instance. NAPI_FATAL_IF_FAILED(status, "Error::Error", "napi_create_reference"); } } inline Error::Error(Error&& other) : ObjectReference(std::move(other)) { } inline Error& Error::operator =(Error&& other) { static_cast*>(this)->operator=(std::move(other)); return *this; } inline Error::Error(const Error& other) : ObjectReference(other) { } inline Error& Error::operator =(const Error& other) { Reset(); _env = other.Env(); HandleScope scope(_env); napi_value value = other.Value(); if (value != nullptr) { napi_status status = napi_create_reference(_env, value, 1, &_ref); NAPI_THROW_IF_FAILED(_env, status, *this); } return *this; } inline const std::string& Error::Message() const NAPI_NOEXCEPT { if (_message.size() == 0 && _env != nullptr) { #ifdef NAPI_CPP_EXCEPTIONS try { _message = Get("message").As(); } catch (...) { // Catch all errors here, to include e.g. a std::bad_alloc from // the std::string::operator=, because this method may not throw. } #else // NAPI_CPP_EXCEPTIONS _message = Get("message").As(); #endif // NAPI_CPP_EXCEPTIONS } return _message; } inline void Error::ThrowAsJavaScriptException() const { HandleScope scope(_env); if (!IsEmpty()) { // We intentionally don't use `NAPI_THROW_*` macros here to ensure // that there is no possible recursion as `ThrowAsJavaScriptException` // is part of `NAPI_THROW_*` macro definition for noexcept. napi_status status = napi_throw(_env, Value()); #ifdef NAPI_CPP_EXCEPTIONS if (status != napi_ok) { throw Error::New(_env); } #else // NAPI_CPP_EXCEPTIONS NAPI_FATAL_IF_FAILED(status, "Error::ThrowAsJavaScriptException", "napi_throw"); #endif // NAPI_CPP_EXCEPTIONS } } #ifdef NAPI_CPP_EXCEPTIONS inline const char* Error::what() const NAPI_NOEXCEPT { return Message().c_str(); } #endif // NAPI_CPP_EXCEPTIONS template inline TError Error::New(napi_env env, const char* message, size_t length, create_error_fn create_error) { napi_value str; napi_status status = napi_create_string_utf8(env, message, length, &str); NAPI_THROW_IF_FAILED(env, status, TError()); napi_value error; status = create_error(env, nullptr, str, &error); NAPI_THROW_IF_FAILED(env, status, TError()); return TError(env, error); } inline TypeError TypeError::New(napi_env env, const char* message) { return Error::New(env, message, std::strlen(message), napi_create_type_error); } inline TypeError TypeError::New(napi_env env, const std::string& message) { return Error::New(env, message.c_str(), message.size(), napi_create_type_error); } inline TypeError::TypeError() : Error() { } inline TypeError::TypeError(napi_env env, napi_value value) : Error(env, value) { } inline RangeError RangeError::New(napi_env env, const char* message) { return Error::New(env, message, std::strlen(message), napi_create_range_error); } inline RangeError RangeError::New(napi_env env, const std::string& message) { return Error::New(env, message.c_str(), message.size(), napi_create_range_error); } inline RangeError::RangeError() : Error() { } inline RangeError::RangeError(napi_env env, napi_value value) : Error(env, value) { } //////////////////////////////////////////////////////////////////////////////// // Reference class //////////////////////////////////////////////////////////////////////////////// template inline Reference Reference::New(const T& value, uint32_t initialRefcount) { napi_env env = value.Env(); napi_value val = value; if (val == nullptr) { return Reference(env, nullptr); } napi_ref ref; napi_status status = napi_create_reference(env, value, initialRefcount, &ref); NAPI_THROW_IF_FAILED(env, status, Reference()); return Reference(env, ref); } template inline Reference::Reference() : _env(nullptr), _ref(nullptr), _suppressDestruct(false) { } template inline Reference::Reference(napi_env env, napi_ref ref) : _env(env), _ref(ref), _suppressDestruct(false) { } template inline Reference::~Reference() { if (_ref != nullptr) { if (!_suppressDestruct) { napi_delete_reference(_env, _ref); } _ref = nullptr; } } template inline Reference::Reference(Reference&& other) : _env(other._env), _ref(other._ref), _suppressDestruct(other._suppressDestruct) { other._env = nullptr; other._ref = nullptr; other._suppressDestruct = false; } template inline Reference& Reference::operator =(Reference&& other) { Reset(); _env = other._env; _ref = other._ref; _suppressDestruct = other._suppressDestruct; other._env = nullptr; other._ref = nullptr; other._suppressDestruct = false; return *this; } template inline Reference::Reference(const Reference& other) : _env(other._env), _ref(nullptr), _suppressDestruct(false) { HandleScope scope(_env); napi_value value = other.Value(); if (value != nullptr) { // Copying is a limited scenario (currently only used for Error object) and always creates a // strong reference to the given value even if the incoming reference is weak. napi_status status = napi_create_reference(_env, value, 1, &_ref); NAPI_FATAL_IF_FAILED(status, "Reference::Reference", "napi_create_reference"); } } template inline Reference::operator napi_ref() const { return _ref; } template inline bool Reference::operator ==(const Reference &other) const { HandleScope scope(_env); return this->Value().StrictEquals(other.Value()); } template inline bool Reference::operator !=(const Reference &other) const { return !this->operator ==(other); } template inline Napi::Env Reference::Env() const { return Napi::Env(_env); } template inline bool Reference::IsEmpty() const { return _ref == nullptr; } template inline T Reference::Value() const { if (_ref == nullptr) { return T(_env, nullptr); } napi_value value; napi_status status = napi_get_reference_value(_env, _ref, &value); NAPI_THROW_IF_FAILED(_env, status, T()); return T(_env, value); } template inline uint32_t Reference::Ref() { uint32_t result; napi_status status = napi_reference_ref(_env, _ref, &result); NAPI_THROW_IF_FAILED(_env, status, 1); return result; } template inline uint32_t Reference::Unref() { uint32_t result; napi_status status = napi_reference_unref(_env, _ref, &result); NAPI_THROW_IF_FAILED(_env, status, 1); return result; } template inline void Reference::Reset() { if (_ref != nullptr) { napi_status status = napi_delete_reference(_env, _ref); NAPI_THROW_IF_FAILED_VOID(_env, status); _ref = nullptr; } } template inline void Reference::Reset(const T& value, uint32_t refcount) { Reset(); _env = value.Env(); napi_value val = value; if (val != nullptr) { napi_status status = napi_create_reference(_env, value, refcount, &_ref); NAPI_THROW_IF_FAILED_VOID(_env, status); } } template inline void Reference::SuppressDestruct() { _suppressDestruct = true; } template inline Reference Weak(T value) { return Reference::New(value, 0); } inline ObjectReference Weak(Object value) { return Reference::New(value, 0); } inline FunctionReference Weak(Function value) { return Reference::New(value, 0); } template inline Reference Persistent(T value) { return Reference::New(value, 1); } inline ObjectReference Persistent(Object value) { return Reference::New(value, 1); } inline FunctionReference Persistent(Function value) { return Reference::New(value, 1); } //////////////////////////////////////////////////////////////////////////////// // ObjectReference class //////////////////////////////////////////////////////////////////////////////// inline ObjectReference::ObjectReference(): Reference() { } inline ObjectReference::ObjectReference(napi_env env, napi_ref ref): Reference(env, ref) { } inline ObjectReference::ObjectReference(Reference&& other) : Reference(std::move(other)) { } inline ObjectReference& ObjectReference::operator =(Reference&& other) { static_cast*>(this)->operator=(std::move(other)); return *this; } inline ObjectReference::ObjectReference(ObjectReference&& other) : Reference(std::move(other)) { } inline ObjectReference& ObjectReference::operator =(ObjectReference&& other) { static_cast*>(this)->operator=(std::move(other)); return *this; } inline ObjectReference::ObjectReference(const ObjectReference& other) : Reference(other) { } inline Napi::Value ObjectReference::Get(const char* utf8name) const { EscapableHandleScope scope(_env); return scope.Escape(Value().Get(utf8name)); } inline Napi::Value ObjectReference::Get(const std::string& utf8name) const { EscapableHandleScope scope(_env); return scope.Escape(Value().Get(utf8name)); } inline bool ObjectReference::Set(const char* utf8name, napi_value value) { HandleScope scope(_env); return Value().Set(utf8name, value); } inline bool ObjectReference::Set(const char* utf8name, Napi::Value value) { HandleScope scope(_env); return Value().Set(utf8name, value); } inline bool ObjectReference::Set(const char* utf8name, const char* utf8value) { HandleScope scope(_env); return Value().Set(utf8name, utf8value); } inline bool ObjectReference::Set(const char* utf8name, bool boolValue) { HandleScope scope(_env); return Value().Set(utf8name, boolValue); } inline bool ObjectReference::Set(const char* utf8name, double numberValue) { HandleScope scope(_env); return Value().Set(utf8name, numberValue); } inline bool ObjectReference::Set(const std::string& utf8name, napi_value value) { HandleScope scope(_env); return Value().Set(utf8name, value); } inline bool ObjectReference::Set(const std::string& utf8name, Napi::Value value) { HandleScope scope(_env); return Value().Set(utf8name, value); } inline bool ObjectReference::Set(const std::string& utf8name, std::string& utf8value) { HandleScope scope(_env); return Value().Set(utf8name, utf8value); } inline bool ObjectReference::Set(const std::string& utf8name, bool boolValue) { HandleScope scope(_env); return Value().Set(utf8name, boolValue); } inline bool ObjectReference::Set(const std::string& utf8name, double numberValue) { HandleScope scope(_env); return Value().Set(utf8name, numberValue); } inline Napi::Value ObjectReference::Get(uint32_t index) const { EscapableHandleScope scope(_env); return scope.Escape(Value().Get(index)); } inline bool ObjectReference::Set(uint32_t index, napi_value value) { HandleScope scope(_env); return Value().Set(index, value); } inline bool ObjectReference::Set(uint32_t index, Napi::Value value) { HandleScope scope(_env); return Value().Set(index, value); } inline bool ObjectReference::Set(uint32_t index, const char* utf8value) { HandleScope scope(_env); return Value().Set(index, utf8value); } inline bool ObjectReference::Set(uint32_t index, const std::string& utf8value) { HandleScope scope(_env); return Value().Set(index, utf8value); } inline bool ObjectReference::Set(uint32_t index, bool boolValue) { HandleScope scope(_env); return Value().Set(index, boolValue); } inline bool ObjectReference::Set(uint32_t index, double numberValue) { HandleScope scope(_env); return Value().Set(index, numberValue); } //////////////////////////////////////////////////////////////////////////////// // FunctionReference class //////////////////////////////////////////////////////////////////////////////// inline FunctionReference::FunctionReference(): Reference() { } inline FunctionReference::FunctionReference(napi_env env, napi_ref ref) : Reference(env, ref) { } inline FunctionReference::FunctionReference(Reference&& other) : Reference(std::move(other)) { } inline FunctionReference& FunctionReference::operator =(Reference&& other) { static_cast*>(this)->operator=(std::move(other)); return *this; } inline FunctionReference::FunctionReference(FunctionReference&& other) : Reference(std::move(other)) { } inline FunctionReference& FunctionReference::operator =(FunctionReference&& other) { static_cast*>(this)->operator=(std::move(other)); return *this; } inline Napi::Value FunctionReference::operator ()( const std::initializer_list& args) const { EscapableHandleScope scope(_env); return scope.Escape(Value()(args)); } inline Napi::Value FunctionReference::Call(const std::initializer_list& args) const { EscapableHandleScope scope(_env); Napi::Value result = Value().Call(args); if (scope.Env().IsExceptionPending()) { return Value(); } return scope.Escape(result); } inline Napi::Value FunctionReference::Call(const std::vector& args) const { EscapableHandleScope scope(_env); Napi::Value result = Value().Call(args); if (scope.Env().IsExceptionPending()) { return Value(); } return scope.Escape(result); } inline Napi::Value FunctionReference::Call( napi_value recv, const std::initializer_list& args) const { EscapableHandleScope scope(_env); Napi::Value result = Value().Call(recv, args); if (scope.Env().IsExceptionPending()) { return Value(); } return scope.Escape(result); } inline Napi::Value FunctionReference::Call( napi_value recv, const std::vector& args) const { EscapableHandleScope scope(_env); Napi::Value result = Value().Call(recv, args); if (scope.Env().IsExceptionPending()) { return Value(); } return scope.Escape(result); } inline Napi::Value FunctionReference::Call( napi_value recv, size_t argc, const napi_value* args) const { EscapableHandleScope scope(_env); Napi::Value result = Value().Call(recv, argc, args); if (scope.Env().IsExceptionPending()) { return Value(); } return scope.Escape(result); } inline Napi::Value FunctionReference::MakeCallback( napi_value recv, const std::initializer_list& args, napi_async_context context) const { EscapableHandleScope scope(_env); Napi::Value result = Value().MakeCallback(recv, args, context); if (scope.Env().IsExceptionPending()) { return Value(); } return scope.Escape(result); } inline Napi::Value FunctionReference::MakeCallback( napi_value recv, const std::vector& args, napi_async_context context) const { EscapableHandleScope scope(_env); Napi::Value result = Value().MakeCallback(recv, args, context); if (scope.Env().IsExceptionPending()) { return Value(); } return scope.Escape(result); } inline Napi::Value FunctionReference::MakeCallback( napi_value recv, size_t argc, const napi_value* args, napi_async_context context) const { EscapableHandleScope scope(_env); Napi::Value result = Value().MakeCallback(recv, argc, args, context); if (scope.Env().IsExceptionPending()) { return Value(); } return scope.Escape(result); } inline Object FunctionReference::New(const std::initializer_list& args) const { EscapableHandleScope scope(_env); return scope.Escape(Value().New(args)).As(); } inline Object FunctionReference::New(const std::vector& args) const { EscapableHandleScope scope(_env); return scope.Escape(Value().New(args)).As(); } //////////////////////////////////////////////////////////////////////////////// // CallbackInfo class //////////////////////////////////////////////////////////////////////////////// inline CallbackInfo::CallbackInfo(napi_env env, napi_callback_info info) : _env(env), _info(info), _this(nullptr), _dynamicArgs(nullptr), _data(nullptr) { _argc = _staticArgCount; _argv = _staticArgs; napi_status status = napi_get_cb_info(env, info, &_argc, _argv, &_this, &_data); NAPI_THROW_IF_FAILED_VOID(_env, status); if (_argc > _staticArgCount) { // Use either a fixed-size array (on the stack) or a dynamically-allocated // array (on the heap) depending on the number of args. _dynamicArgs = new napi_value[_argc]; _argv = _dynamicArgs; status = napi_get_cb_info(env, info, &_argc, _argv, nullptr, nullptr); NAPI_THROW_IF_FAILED_VOID(_env, status); } } inline CallbackInfo::~CallbackInfo() { if (_dynamicArgs != nullptr) { delete[] _dynamicArgs; } } inline Value CallbackInfo::NewTarget() const { napi_value newTarget; napi_status status = napi_get_new_target(_env, _info, &newTarget); NAPI_THROW_IF_FAILED(_env, status, Value()); return Value(_env, newTarget); } inline bool CallbackInfo::IsConstructCall() const { return !NewTarget().IsEmpty(); } inline Napi::Env CallbackInfo::Env() const { return Napi::Env(_env); } inline size_t CallbackInfo::Length() const { return _argc; } inline const Value CallbackInfo::operator [](size_t index) const { return index < _argc ? Value(_env, _argv[index]) : Env().Undefined(); } inline Value CallbackInfo::This() const { if (_this == nullptr) { return Env().Undefined(); } return Object(_env, _this); } inline void* CallbackInfo::Data() const { return _data; } inline void CallbackInfo::SetData(void* data) { _data = data; } //////////////////////////////////////////////////////////////////////////////// // PropertyDescriptor class //////////////////////////////////////////////////////////////////////////////// template PropertyDescriptor PropertyDescriptor::Accessor(const char* utf8name, napi_property_attributes attributes, void* data) { napi_property_descriptor desc = napi_property_descriptor(); desc.utf8name = utf8name; desc.getter = details::TemplatedCallback; desc.attributes = attributes; desc.data = data; return desc; } template PropertyDescriptor PropertyDescriptor::Accessor(const std::string& utf8name, napi_property_attributes attributes, void* data) { return Accessor(utf8name.c_str(), attributes, data); } template PropertyDescriptor PropertyDescriptor::Accessor(Name name, napi_property_attributes attributes, void* data) { napi_property_descriptor desc = napi_property_descriptor(); desc.name = name; desc.getter = details::TemplatedCallback; desc.attributes = attributes; desc.data = data; return desc; } template < typename PropertyDescriptor::GetterCallback Getter, typename PropertyDescriptor::SetterCallback Setter> PropertyDescriptor PropertyDescriptor::Accessor(const char* utf8name, napi_property_attributes attributes, void* data) { napi_property_descriptor desc = napi_property_descriptor(); desc.utf8name = utf8name; desc.getter = details::TemplatedCallback; desc.setter = details::TemplatedVoidCallback; desc.attributes = attributes; desc.data = data; return desc; } template < typename PropertyDescriptor::GetterCallback Getter, typename PropertyDescriptor::SetterCallback Setter> PropertyDescriptor PropertyDescriptor::Accessor(const std::string& utf8name, napi_property_attributes attributes, void* data) { return Accessor(utf8name.c_str(), attributes, data); } template < typename PropertyDescriptor::GetterCallback Getter, typename PropertyDescriptor::SetterCallback Setter> PropertyDescriptor PropertyDescriptor::Accessor(Name name, napi_property_attributes attributes, void* data) { napi_property_descriptor desc = napi_property_descriptor(); desc.name = name; desc.getter = details::TemplatedCallback; desc.setter = details::TemplatedVoidCallback; desc.attributes = attributes; desc.data = data; return desc; } template inline PropertyDescriptor PropertyDescriptor::Accessor(Napi::Env env, Napi::Object object, const char* utf8name, Getter getter, napi_property_attributes attributes, void* data) { using CbData = details::CallbackData; auto callbackData = new CbData({ getter, data }); napi_status status = AttachData(env, object, callbackData); if (status != napi_ok) { delete callbackData; NAPI_THROW_IF_FAILED(env, status, napi_property_descriptor()); } return PropertyDescriptor({ utf8name, nullptr, nullptr, CbData::Wrapper, nullptr, nullptr, attributes, callbackData }); } template inline PropertyDescriptor PropertyDescriptor::Accessor(Napi::Env env, Napi::Object object, const std::string& utf8name, Getter getter, napi_property_attributes attributes, void* data) { return Accessor(env, object, utf8name.c_str(), getter, attributes, data); } template inline PropertyDescriptor PropertyDescriptor::Accessor(Napi::Env env, Napi::Object object, Name name, Getter getter, napi_property_attributes attributes, void* data) { using CbData = details::CallbackData; auto callbackData = new CbData({ getter, data }); napi_status status = AttachData(env, object, callbackData); if (status != napi_ok) { delete callbackData; NAPI_THROW_IF_FAILED(env, status, napi_property_descriptor()); } return PropertyDescriptor({ nullptr, name, nullptr, CbData::Wrapper, nullptr, nullptr, attributes, callbackData }); } template inline PropertyDescriptor PropertyDescriptor::Accessor(Napi::Env env, Napi::Object object, const char* utf8name, Getter getter, Setter setter, napi_property_attributes attributes, void* data) { using CbData = details::AccessorCallbackData; auto callbackData = new CbData({ getter, setter, data }); napi_status status = AttachData(env, object, callbackData); if (status != napi_ok) { delete callbackData; NAPI_THROW_IF_FAILED(env, status, napi_property_descriptor()); } return PropertyDescriptor({ utf8name, nullptr, nullptr, CbData::GetterWrapper, CbData::SetterWrapper, nullptr, attributes, callbackData }); } template inline PropertyDescriptor PropertyDescriptor::Accessor(Napi::Env env, Napi::Object object, const std::string& utf8name, Getter getter, Setter setter, napi_property_attributes attributes, void* data) { return Accessor(env, object, utf8name.c_str(), getter, setter, attributes, data); } template inline PropertyDescriptor PropertyDescriptor::Accessor(Napi::Env env, Napi::Object object, Name name, Getter getter, Setter setter, napi_property_attributes attributes, void* data) { using CbData = details::AccessorCallbackData; auto callbackData = new CbData({ getter, setter, data }); napi_status status = AttachData(env, object, callbackData); if (status != napi_ok) { delete callbackData; NAPI_THROW_IF_FAILED(env, status, napi_property_descriptor()); } return PropertyDescriptor({ nullptr, name, nullptr, CbData::GetterWrapper, CbData::SetterWrapper, nullptr, attributes, callbackData }); } template inline PropertyDescriptor PropertyDescriptor::Function(Napi::Env env, Napi::Object /*object*/, const char* utf8name, Callable cb, napi_property_attributes attributes, void* data) { return PropertyDescriptor({ utf8name, nullptr, nullptr, nullptr, nullptr, Napi::Function::New(env, cb, utf8name, data), attributes, nullptr }); } template inline PropertyDescriptor PropertyDescriptor::Function(Napi::Env env, Napi::Object object, const std::string& utf8name, Callable cb, napi_property_attributes attributes, void* data) { return Function(env, object, utf8name.c_str(), cb, attributes, data); } template inline PropertyDescriptor PropertyDescriptor::Function(Napi::Env env, Napi::Object /*object*/, Name name, Callable cb, napi_property_attributes attributes, void* data) { return PropertyDescriptor({ nullptr, name, nullptr, nullptr, nullptr, Napi::Function::New(env, cb, nullptr, data), attributes, nullptr }); } inline PropertyDescriptor PropertyDescriptor::Value(const char* utf8name, napi_value value, napi_property_attributes attributes) { return PropertyDescriptor({ utf8name, nullptr, nullptr, nullptr, nullptr, value, attributes, nullptr }); } inline PropertyDescriptor PropertyDescriptor::Value(const std::string& utf8name, napi_value value, napi_property_attributes attributes) { return Value(utf8name.c_str(), value, attributes); } inline PropertyDescriptor PropertyDescriptor::Value(napi_value name, napi_value value, napi_property_attributes attributes) { return PropertyDescriptor({ nullptr, name, nullptr, nullptr, nullptr, value, attributes, nullptr }); } inline PropertyDescriptor PropertyDescriptor::Value(Name name, Napi::Value value, napi_property_attributes attributes) { napi_value nameValue = name; napi_value valueValue = value; return PropertyDescriptor::Value(nameValue, valueValue, attributes); } inline PropertyDescriptor::PropertyDescriptor(napi_property_descriptor desc) : _desc(desc) { } inline PropertyDescriptor::operator napi_property_descriptor&() { return _desc; } inline PropertyDescriptor::operator const napi_property_descriptor&() const { return _desc; } //////////////////////////////////////////////////////////////////////////////// // InstanceWrap class //////////////////////////////////////////////////////////////////////////////// template inline void InstanceWrap::AttachPropData(napi_env env, napi_value value, const napi_property_descriptor* prop) { napi_status status; if (!(prop->attributes & napi_static)) { if (prop->method == T::InstanceVoidMethodCallbackWrapper) { status = Napi::details::AttachData(env, value, static_cast(prop->data)); NAPI_THROW_IF_FAILED_VOID(env, status); } else if (prop->method == T::InstanceMethodCallbackWrapper) { status = Napi::details::AttachData(env, value, static_cast(prop->data)); NAPI_THROW_IF_FAILED_VOID(env, status); } else if (prop->getter == T::InstanceGetterCallbackWrapper || prop->setter == T::InstanceSetterCallbackWrapper) { status = Napi::details::AttachData(env, value, static_cast(prop->data)); NAPI_THROW_IF_FAILED_VOID(env, status); } } } template inline ClassPropertyDescriptor InstanceWrap::InstanceMethod( const char* utf8name, InstanceVoidMethodCallback method, napi_property_attributes attributes, void* data) { InstanceVoidMethodCallbackData* callbackData = new InstanceVoidMethodCallbackData({ method, data}); napi_property_descriptor desc = napi_property_descriptor(); desc.utf8name = utf8name; desc.method = T::InstanceVoidMethodCallbackWrapper; desc.data = callbackData; desc.attributes = attributes; return desc; } template inline ClassPropertyDescriptor InstanceWrap::InstanceMethod( const char* utf8name, InstanceMethodCallback method, napi_property_attributes attributes, void* data) { InstanceMethodCallbackData* callbackData = new InstanceMethodCallbackData({ method, data }); napi_property_descriptor desc = napi_property_descriptor(); desc.utf8name = utf8name; desc.method = T::InstanceMethodCallbackWrapper; desc.data = callbackData; desc.attributes = attributes; return desc; } template inline ClassPropertyDescriptor InstanceWrap::InstanceMethod( Symbol name, InstanceVoidMethodCallback method, napi_property_attributes attributes, void* data) { InstanceVoidMethodCallbackData* callbackData = new InstanceVoidMethodCallbackData({ method, data}); napi_property_descriptor desc = napi_property_descriptor(); desc.name = name; desc.method = T::InstanceVoidMethodCallbackWrapper; desc.data = callbackData; desc.attributes = attributes; return desc; } template inline ClassPropertyDescriptor InstanceWrap::InstanceMethod( Symbol name, InstanceMethodCallback method, napi_property_attributes attributes, void* data) { InstanceMethodCallbackData* callbackData = new InstanceMethodCallbackData({ method, data }); napi_property_descriptor desc = napi_property_descriptor(); desc.name = name; desc.method = T::InstanceMethodCallbackWrapper; desc.data = callbackData; desc.attributes = attributes; return desc; } template template ::InstanceVoidMethodCallback method> inline ClassPropertyDescriptor InstanceWrap::InstanceMethod( const char* utf8name, napi_property_attributes attributes, void* data) { napi_property_descriptor desc = napi_property_descriptor(); desc.utf8name = utf8name; desc.method = details::TemplatedInstanceVoidCallback; desc.data = data; desc.attributes = attributes; return desc; } template template ::InstanceMethodCallback method> inline ClassPropertyDescriptor InstanceWrap::InstanceMethod( const char* utf8name, napi_property_attributes attributes, void* data) { napi_property_descriptor desc = napi_property_descriptor(); desc.utf8name = utf8name; desc.method = details::TemplatedInstanceCallback; desc.data = data; desc.attributes = attributes; return desc; } template template ::InstanceVoidMethodCallback method> inline ClassPropertyDescriptor InstanceWrap::InstanceMethod( Symbol name, napi_property_attributes attributes, void* data) { napi_property_descriptor desc = napi_property_descriptor(); desc.name = name; desc.method = details::TemplatedInstanceVoidCallback; desc.data = data; desc.attributes = attributes; return desc; } template template ::InstanceMethodCallback method> inline ClassPropertyDescriptor InstanceWrap::InstanceMethod( Symbol name, napi_property_attributes attributes, void* data) { napi_property_descriptor desc = napi_property_descriptor(); desc.name = name; desc.method = details::TemplatedInstanceCallback; desc.data = data; desc.attributes = attributes; return desc; } template inline ClassPropertyDescriptor InstanceWrap::InstanceAccessor( const char* utf8name, InstanceGetterCallback getter, InstanceSetterCallback setter, napi_property_attributes attributes, void* data) { InstanceAccessorCallbackData* callbackData = new InstanceAccessorCallbackData({ getter, setter, data }); napi_property_descriptor desc = napi_property_descriptor(); desc.utf8name = utf8name; desc.getter = getter != nullptr ? T::InstanceGetterCallbackWrapper : nullptr; desc.setter = setter != nullptr ? T::InstanceSetterCallbackWrapper : nullptr; desc.data = callbackData; desc.attributes = attributes; return desc; } template inline ClassPropertyDescriptor InstanceWrap::InstanceAccessor( Symbol name, InstanceGetterCallback getter, InstanceSetterCallback setter, napi_property_attributes attributes, void* data) { InstanceAccessorCallbackData* callbackData = new InstanceAccessorCallbackData({ getter, setter, data }); napi_property_descriptor desc = napi_property_descriptor(); desc.name = name; desc.getter = getter != nullptr ? T::InstanceGetterCallbackWrapper : nullptr; desc.setter = setter != nullptr ? T::InstanceSetterCallbackWrapper : nullptr; desc.data = callbackData; desc.attributes = attributes; return desc; } template template ::InstanceGetterCallback getter, typename InstanceWrap::InstanceSetterCallback setter> inline ClassPropertyDescriptor InstanceWrap::InstanceAccessor( const char* utf8name, napi_property_attributes attributes, void* data) { napi_property_descriptor desc = napi_property_descriptor(); desc.utf8name = utf8name; desc.getter = details::TemplatedInstanceCallback; desc.setter = This::WrapSetter(This::SetterTag()); desc.data = data; desc.attributes = attributes; return desc; } template template ::InstanceGetterCallback getter, typename InstanceWrap::InstanceSetterCallback setter> inline ClassPropertyDescriptor InstanceWrap::InstanceAccessor( Symbol name, napi_property_attributes attributes, void* data) { napi_property_descriptor desc = napi_property_descriptor(); desc.name = name; desc.getter = details::TemplatedInstanceCallback; desc.setter = This::WrapSetter(This::SetterTag()); desc.data = data; desc.attributes = attributes; return desc; } template inline ClassPropertyDescriptor InstanceWrap::InstanceValue( const char* utf8name, Napi::Value value, napi_property_attributes attributes) { napi_property_descriptor desc = napi_property_descriptor(); desc.utf8name = utf8name; desc.value = value; desc.attributes = attributes; return desc; } template inline ClassPropertyDescriptor InstanceWrap::InstanceValue( Symbol name, Napi::Value value, napi_property_attributes attributes) { napi_property_descriptor desc = napi_property_descriptor(); desc.name = name; desc.value = value; desc.attributes = attributes; return desc; } template inline napi_value InstanceWrap::InstanceVoidMethodCallbackWrapper( napi_env env, napi_callback_info info) { return details::WrapCallback([&] { CallbackInfo callbackInfo(env, info); InstanceVoidMethodCallbackData* callbackData = reinterpret_cast(callbackInfo.Data()); callbackInfo.SetData(callbackData->data); T* instance = T::Unwrap(callbackInfo.This().As()); auto cb = callbackData->callback; (instance->*cb)(callbackInfo); return nullptr; }); } template inline napi_value InstanceWrap::InstanceMethodCallbackWrapper( napi_env env, napi_callback_info info) { return details::WrapCallback([&] { CallbackInfo callbackInfo(env, info); InstanceMethodCallbackData* callbackData = reinterpret_cast(callbackInfo.Data()); callbackInfo.SetData(callbackData->data); T* instance = T::Unwrap(callbackInfo.This().As()); auto cb = callbackData->callback; return (instance->*cb)(callbackInfo); }); } template inline napi_value InstanceWrap::InstanceGetterCallbackWrapper( napi_env env, napi_callback_info info) { return details::WrapCallback([&] { CallbackInfo callbackInfo(env, info); InstanceAccessorCallbackData* callbackData = reinterpret_cast(callbackInfo.Data()); callbackInfo.SetData(callbackData->data); T* instance = T::Unwrap(callbackInfo.This().As()); auto cb = callbackData->getterCallback; return (instance->*cb)(callbackInfo); }); } template inline napi_value InstanceWrap::InstanceSetterCallbackWrapper( napi_env env, napi_callback_info info) { return details::WrapCallback([&] { CallbackInfo callbackInfo(env, info); InstanceAccessorCallbackData* callbackData = reinterpret_cast(callbackInfo.Data()); callbackInfo.SetData(callbackData->data); T* instance = T::Unwrap(callbackInfo.This().As()); auto cb = callbackData->setterCallback; (instance->*cb)(callbackInfo, callbackInfo[0]); return nullptr; }); } template template ::InstanceSetterCallback method> inline napi_value InstanceWrap::WrappedMethod( napi_env env, napi_callback_info info) NAPI_NOEXCEPT { return details::WrapCallback([&] { const CallbackInfo cbInfo(env, info); T* instance = T::Unwrap(cbInfo.This().As()); (instance->*method)(cbInfo, cbInfo[0]); return nullptr; }); } //////////////////////////////////////////////////////////////////////////////// // ObjectWrap class //////////////////////////////////////////////////////////////////////////////// template inline ObjectWrap::ObjectWrap(const Napi::CallbackInfo& callbackInfo) { napi_env env = callbackInfo.Env(); napi_value wrapper = callbackInfo.This(); napi_status status; napi_ref ref; T* instance = static_cast(this); status = napi_wrap(env, wrapper, instance, FinalizeCallback, nullptr, &ref); NAPI_THROW_IF_FAILED_VOID(env, status); Reference* instanceRef = instance; *instanceRef = Reference(env, ref); } template inline ObjectWrap::~ObjectWrap() { // If the JS object still exists at this point, remove the finalizer added // through `napi_wrap()`. if (!IsEmpty()) { Object object = Value(); // It is not valid to call `napi_remove_wrap()` with an empty `object`. // This happens e.g. during garbage collection. if (!object.IsEmpty() && _construction_failed) { napi_remove_wrap(Env(), object, nullptr); } } } template inline T* ObjectWrap::Unwrap(Object wrapper) { T* unwrapped; napi_status status = napi_unwrap(wrapper.Env(), wrapper, reinterpret_cast(&unwrapped)); NAPI_THROW_IF_FAILED(wrapper.Env(), status, nullptr); return unwrapped; } template inline Function ObjectWrap::DefineClass(Napi::Env env, const char* utf8name, const size_t props_count, const napi_property_descriptor* descriptors, void* data) { napi_status status; std::vector props(props_count); // We copy the descriptors to a local array because before defining the class // we must replace static method property descriptors with value property // descriptors such that the value is a function-valued `napi_value` created // with `CreateFunction()`. // // This replacement could be made for instance methods as well, but V8 aborts // if we do that, because it expects methods defined on the prototype template // to have `FunctionTemplate`s. for (size_t index = 0; index < props_count; index++) { props[index] = descriptors[index]; napi_property_descriptor* prop = &props[index]; if (prop->method == T::StaticMethodCallbackWrapper) { status = CreateFunction(env, utf8name, prop->method, static_cast(prop->data), &(prop->value)); NAPI_THROW_IF_FAILED(env, status, Function()); prop->method = nullptr; prop->data = nullptr; } else if (prop->method == T::StaticVoidMethodCallbackWrapper) { status = CreateFunction(env, utf8name, prop->method, static_cast(prop->data), &(prop->value)); NAPI_THROW_IF_FAILED(env, status, Function()); prop->method = nullptr; prop->data = nullptr; } } napi_value value; status = napi_define_class(env, utf8name, NAPI_AUTO_LENGTH, T::ConstructorCallbackWrapper, data, props_count, props.data(), &value); NAPI_THROW_IF_FAILED(env, status, Function()); // After defining the class we iterate once more over the property descriptors // and attach the data associated with accessors and instance methods to the // newly created JavaScript class. for (size_t idx = 0; idx < props_count; idx++) { const napi_property_descriptor* prop = &props[idx]; if (prop->getter == T::StaticGetterCallbackWrapper || prop->setter == T::StaticSetterCallbackWrapper) { status = Napi::details::AttachData(env, value, static_cast(prop->data)); NAPI_THROW_IF_FAILED(env, status, Function()); } else { // InstanceWrap::AttachPropData is responsible for attaching the data // of instance methods and accessors. T::AttachPropData(env, value, prop); } } return Function(env, value); } template inline Function ObjectWrap::DefineClass( Napi::Env env, const char* utf8name, const std::initializer_list>& properties, void* data) { return DefineClass(env, utf8name, properties.size(), reinterpret_cast(properties.begin()), data); } template inline Function ObjectWrap::DefineClass( Napi::Env env, const char* utf8name, const std::vector>& properties, void* data) { return DefineClass(env, utf8name, properties.size(), reinterpret_cast(properties.data()), data); } template inline ClassPropertyDescriptor ObjectWrap::StaticMethod( const char* utf8name, StaticVoidMethodCallback method, napi_property_attributes attributes, void* data) { StaticVoidMethodCallbackData* callbackData = new StaticVoidMethodCallbackData({ method, data }); napi_property_descriptor desc = napi_property_descriptor(); desc.utf8name = utf8name; desc.method = T::StaticVoidMethodCallbackWrapper; desc.data = callbackData; desc.attributes = static_cast(attributes | napi_static); return desc; } template inline ClassPropertyDescriptor ObjectWrap::StaticMethod( const char* utf8name, StaticMethodCallback method, napi_property_attributes attributes, void* data) { StaticMethodCallbackData* callbackData = new StaticMethodCallbackData({ method, data }); napi_property_descriptor desc = napi_property_descriptor(); desc.utf8name = utf8name; desc.method = T::StaticMethodCallbackWrapper; desc.data = callbackData; desc.attributes = static_cast(attributes | napi_static); return desc; } template inline ClassPropertyDescriptor ObjectWrap::StaticMethod( Symbol name, StaticVoidMethodCallback method, napi_property_attributes attributes, void* data) { StaticVoidMethodCallbackData* callbackData = new StaticVoidMethodCallbackData({ method, data }); napi_property_descriptor desc = napi_property_descriptor(); desc.name = name; desc.method = T::StaticVoidMethodCallbackWrapper; desc.data = callbackData; desc.attributes = static_cast(attributes | napi_static); return desc; } template inline ClassPropertyDescriptor ObjectWrap::StaticMethod( Symbol name, StaticMethodCallback method, napi_property_attributes attributes, void* data) { StaticMethodCallbackData* callbackData = new StaticMethodCallbackData({ method, data }); napi_property_descriptor desc = napi_property_descriptor(); desc.name = name; desc.method = T::StaticMethodCallbackWrapper; desc.data = callbackData; desc.attributes = static_cast(attributes | napi_static); return desc; } template template ::StaticVoidMethodCallback method> inline ClassPropertyDescriptor ObjectWrap::StaticMethod( const char* utf8name, napi_property_attributes attributes, void* data) { napi_property_descriptor desc = napi_property_descriptor(); desc.utf8name = utf8name; desc.method = details::TemplatedVoidCallback; desc.data = data; desc.attributes = static_cast(attributes | napi_static); return desc; } template template ::StaticVoidMethodCallback method> inline ClassPropertyDescriptor ObjectWrap::StaticMethod( Symbol name, napi_property_attributes attributes, void* data) { napi_property_descriptor desc = napi_property_descriptor(); desc.name = name; desc.method = details::TemplatedVoidCallback; desc.data = data; desc.attributes = static_cast(attributes | napi_static); return desc; } template template ::StaticMethodCallback method> inline ClassPropertyDescriptor ObjectWrap::StaticMethod( const char* utf8name, napi_property_attributes attributes, void* data) { napi_property_descriptor desc = napi_property_descriptor(); desc.utf8name = utf8name; desc.method = details::TemplatedCallback; desc.data = data; desc.attributes = static_cast(attributes | napi_static); return desc; } template template ::StaticMethodCallback method> inline ClassPropertyDescriptor ObjectWrap::StaticMethod( Symbol name, napi_property_attributes attributes, void* data) { napi_property_descriptor desc = napi_property_descriptor(); desc.name = name; desc.method = details::TemplatedCallback; desc.data = data; desc.attributes = static_cast(attributes | napi_static); return desc; } template inline ClassPropertyDescriptor ObjectWrap::StaticAccessor( const char* utf8name, StaticGetterCallback getter, StaticSetterCallback setter, napi_property_attributes attributes, void* data) { StaticAccessorCallbackData* callbackData = new StaticAccessorCallbackData({ getter, setter, data }); napi_property_descriptor desc = napi_property_descriptor(); desc.utf8name = utf8name; desc.getter = getter != nullptr ? T::StaticGetterCallbackWrapper : nullptr; desc.setter = setter != nullptr ? T::StaticSetterCallbackWrapper : nullptr; desc.data = callbackData; desc.attributes = static_cast(attributes | napi_static); return desc; } template inline ClassPropertyDescriptor ObjectWrap::StaticAccessor( Symbol name, StaticGetterCallback getter, StaticSetterCallback setter, napi_property_attributes attributes, void* data) { StaticAccessorCallbackData* callbackData = new StaticAccessorCallbackData({ getter, setter, data }); napi_property_descriptor desc = napi_property_descriptor(); desc.name = name; desc.getter = getter != nullptr ? T::StaticGetterCallbackWrapper : nullptr; desc.setter = setter != nullptr ? T::StaticSetterCallbackWrapper : nullptr; desc.data = callbackData; desc.attributes = static_cast(attributes | napi_static); return desc; } template template ::StaticGetterCallback getter, typename ObjectWrap::StaticSetterCallback setter> inline ClassPropertyDescriptor ObjectWrap::StaticAccessor( const char* utf8name, napi_property_attributes attributes, void* data) { napi_property_descriptor desc = napi_property_descriptor(); desc.utf8name = utf8name; desc.getter = details::TemplatedCallback; desc.setter = This::WrapStaticSetter(This::StaticSetterTag()); desc.data = data; desc.attributes = static_cast(attributes | napi_static); return desc; } template template ::StaticGetterCallback getter, typename ObjectWrap::StaticSetterCallback setter> inline ClassPropertyDescriptor ObjectWrap::StaticAccessor( Symbol name, napi_property_attributes attributes, void* data) { napi_property_descriptor desc = napi_property_descriptor(); desc.name = name; desc.getter = details::TemplatedCallback; desc.setter = This::WrapStaticSetter(This::StaticSetterTag()); desc.data = data; desc.attributes = static_cast(attributes | napi_static); return desc; } template inline ClassPropertyDescriptor ObjectWrap::StaticValue(const char* utf8name, Napi::Value value, napi_property_attributes attributes) { napi_property_descriptor desc = napi_property_descriptor(); desc.utf8name = utf8name; desc.value = value; desc.attributes = static_cast(attributes | napi_static); return desc; } template inline ClassPropertyDescriptor ObjectWrap::StaticValue(Symbol name, Napi::Value value, napi_property_attributes attributes) { napi_property_descriptor desc = napi_property_descriptor(); desc.name = name; desc.value = value; desc.attributes = static_cast(attributes | napi_static); return desc; } template inline void ObjectWrap::Finalize(Napi::Env /*env*/) {} template inline napi_value ObjectWrap::ConstructorCallbackWrapper( napi_env env, napi_callback_info info) { napi_value new_target; napi_status status = napi_get_new_target(env, info, &new_target); if (status != napi_ok) return nullptr; bool isConstructCall = (new_target != nullptr); if (!isConstructCall) { napi_throw_type_error(env, nullptr, "Class constructors cannot be invoked without 'new'"); return nullptr; } napi_value wrapper = details::WrapCallback([&] { CallbackInfo callbackInfo(env, info); T* instance = new T(callbackInfo); #ifdef NAPI_CPP_EXCEPTIONS instance->_construction_failed = false; #else if (callbackInfo.Env().IsExceptionPending()) { // We need to clear the exception so that removing the wrap might work. Error e = callbackInfo.Env().GetAndClearPendingException(); delete instance; e.ThrowAsJavaScriptException(); } else { instance->_construction_failed = false; } # endif // NAPI_CPP_EXCEPTIONS return callbackInfo.This(); }); return wrapper; } template inline napi_value ObjectWrap::StaticVoidMethodCallbackWrapper( napi_env env, napi_callback_info info) { return details::WrapCallback([&] { CallbackInfo callbackInfo(env, info); StaticVoidMethodCallbackData* callbackData = reinterpret_cast(callbackInfo.Data()); callbackInfo.SetData(callbackData->data); callbackData->callback(callbackInfo); return nullptr; }); } template inline napi_value ObjectWrap::StaticMethodCallbackWrapper( napi_env env, napi_callback_info info) { return details::WrapCallback([&] { CallbackInfo callbackInfo(env, info); StaticMethodCallbackData* callbackData = reinterpret_cast(callbackInfo.Data()); callbackInfo.SetData(callbackData->data); return callbackData->callback(callbackInfo); }); } template inline napi_value ObjectWrap::StaticGetterCallbackWrapper( napi_env env, napi_callback_info info) { return details::WrapCallback([&] { CallbackInfo callbackInfo(env, info); StaticAccessorCallbackData* callbackData = reinterpret_cast(callbackInfo.Data()); callbackInfo.SetData(callbackData->data); return callbackData->getterCallback(callbackInfo); }); } template inline napi_value ObjectWrap::StaticSetterCallbackWrapper( napi_env env, napi_callback_info info) { return details::WrapCallback([&] { CallbackInfo callbackInfo(env, info); StaticAccessorCallbackData* callbackData = reinterpret_cast(callbackInfo.Data()); callbackInfo.SetData(callbackData->data); callbackData->setterCallback(callbackInfo, callbackInfo[0]); return nullptr; }); } template inline void ObjectWrap::FinalizeCallback(napi_env env, void* data, void* /*hint*/) { HandleScope scope(env); T* instance = static_cast(data); instance->Finalize(Napi::Env(env)); delete instance; } template template ::StaticSetterCallback method> inline napi_value ObjectWrap::WrappedMethod( napi_env env, napi_callback_info info) NAPI_NOEXCEPT { return details::WrapCallback([&] { const CallbackInfo cbInfo(env, info); method(cbInfo, cbInfo[0]); return nullptr; }); } //////////////////////////////////////////////////////////////////////////////// // HandleScope class //////////////////////////////////////////////////////////////////////////////// inline HandleScope::HandleScope(napi_env env, napi_handle_scope scope) : _env(env), _scope(scope) { } inline HandleScope::HandleScope(Napi::Env env) : _env(env) { napi_status status = napi_open_handle_scope(_env, &_scope); NAPI_THROW_IF_FAILED_VOID(_env, status); } inline HandleScope::~HandleScope() { napi_status status = napi_close_handle_scope(_env, _scope); NAPI_FATAL_IF_FAILED(status, "HandleScope::~HandleScope", "napi_close_handle_scope"); } inline HandleScope::operator napi_handle_scope() const { return _scope; } inline Napi::Env HandleScope::Env() const { return Napi::Env(_env); } //////////////////////////////////////////////////////////////////////////////// // EscapableHandleScope class //////////////////////////////////////////////////////////////////////////////// inline EscapableHandleScope::EscapableHandleScope( napi_env env, napi_escapable_handle_scope scope) : _env(env), _scope(scope) { } inline EscapableHandleScope::EscapableHandleScope(Napi::Env env) : _env(env) { napi_status status = napi_open_escapable_handle_scope(_env, &_scope); NAPI_THROW_IF_FAILED_VOID(_env, status); } inline EscapableHandleScope::~EscapableHandleScope() { napi_status status = napi_close_escapable_handle_scope(_env, _scope); NAPI_FATAL_IF_FAILED(status, "EscapableHandleScope::~EscapableHandleScope", "napi_close_escapable_handle_scope"); } inline EscapableHandleScope::operator napi_escapable_handle_scope() const { return _scope; } inline Napi::Env EscapableHandleScope::Env() const { return Napi::Env(_env); } inline Value EscapableHandleScope::Escape(napi_value escapee) { napi_value result; napi_status status = napi_escape_handle(_env, _scope, escapee, &result); NAPI_THROW_IF_FAILED(_env, status, Value()); return Value(_env, result); } #if (NAPI_VERSION > 2) //////////////////////////////////////////////////////////////////////////////// // CallbackScope class //////////////////////////////////////////////////////////////////////////////// inline CallbackScope::CallbackScope( napi_env env, napi_callback_scope scope) : _env(env), _scope(scope) { } inline CallbackScope::CallbackScope(napi_env env, napi_async_context context) : _env(env) { napi_status status = napi_open_callback_scope( _env, Object::New(env), context, &_scope); NAPI_THROW_IF_FAILED_VOID(_env, status); } inline CallbackScope::~CallbackScope() { napi_status status = napi_close_callback_scope(_env, _scope); NAPI_FATAL_IF_FAILED(status, "CallbackScope::~CallbackScope", "napi_close_callback_scope"); } inline CallbackScope::operator napi_callback_scope() const { return _scope; } inline Napi::Env CallbackScope::Env() const { return Napi::Env(_env); } #endif //////////////////////////////////////////////////////////////////////////////// // AsyncContext class //////////////////////////////////////////////////////////////////////////////// inline AsyncContext::AsyncContext(napi_env env, const char* resource_name) : AsyncContext(env, resource_name, Object::New(env)) { } inline AsyncContext::AsyncContext(napi_env env, const char* resource_name, const Object& resource) : _env(env), _context(nullptr) { napi_value resource_id; napi_status status = napi_create_string_utf8( _env, resource_name, NAPI_AUTO_LENGTH, &resource_id); NAPI_THROW_IF_FAILED_VOID(_env, status); status = napi_async_init(_env, resource, resource_id, &_context); NAPI_THROW_IF_FAILED_VOID(_env, status); } inline AsyncContext::~AsyncContext() { if (_context != nullptr) { napi_async_destroy(_env, _context); _context = nullptr; } } inline AsyncContext::AsyncContext(AsyncContext&& other) { _env = other._env; other._env = nullptr; _context = other._context; other._context = nullptr; } inline AsyncContext& AsyncContext::operator =(AsyncContext&& other) { _env = other._env; other._env = nullptr; _context = other._context; other._context = nullptr; return *this; } inline AsyncContext::operator napi_async_context() const { return _context; } inline Napi::Env AsyncContext::Env() const { return Napi::Env(_env); } //////////////////////////////////////////////////////////////////////////////// // AsyncWorker class //////////////////////////////////////////////////////////////////////////////// inline AsyncWorker::AsyncWorker(const Function& callback) : AsyncWorker(callback, "generic") { } inline AsyncWorker::AsyncWorker(const Function& callback, const char* resource_name) : AsyncWorker(callback, resource_name, Object::New(callback.Env())) { } inline AsyncWorker::AsyncWorker(const Function& callback, const char* resource_name, const Object& resource) : AsyncWorker(Object::New(callback.Env()), callback, resource_name, resource) { } inline AsyncWorker::AsyncWorker(const Object& receiver, const Function& callback) : AsyncWorker(receiver, callback, "generic") { } inline AsyncWorker::AsyncWorker(const Object& receiver, const Function& callback, const char* resource_name) : AsyncWorker(receiver, callback, resource_name, Object::New(callback.Env())) { } inline AsyncWorker::AsyncWorker(const Object& receiver, const Function& callback, const char* resource_name, const Object& resource) : _env(callback.Env()), _receiver(Napi::Persistent(receiver)), _callback(Napi::Persistent(callback)), _suppress_destruct(false) { napi_value resource_id; napi_status status = napi_create_string_latin1( _env, resource_name, NAPI_AUTO_LENGTH, &resource_id); NAPI_THROW_IF_FAILED_VOID(_env, status); status = napi_create_async_work(_env, resource, resource_id, OnAsyncWorkExecute, OnAsyncWorkComplete, this, &_work); NAPI_THROW_IF_FAILED_VOID(_env, status); } inline AsyncWorker::AsyncWorker(Napi::Env env) : AsyncWorker(env, "generic") { } inline AsyncWorker::AsyncWorker(Napi::Env env, const char* resource_name) : AsyncWorker(env, resource_name, Object::New(env)) { } inline AsyncWorker::AsyncWorker(Napi::Env env, const char* resource_name, const Object& resource) : _env(env), _receiver(), _callback(), _suppress_destruct(false) { napi_value resource_id; napi_status status = napi_create_string_latin1( _env, resource_name, NAPI_AUTO_LENGTH, &resource_id); NAPI_THROW_IF_FAILED_VOID(_env, status); status = napi_create_async_work(_env, resource, resource_id, OnAsyncWorkExecute, OnAsyncWorkComplete, this, &_work); NAPI_THROW_IF_FAILED_VOID(_env, status); } inline AsyncWorker::~AsyncWorker() { if (_work != nullptr) { napi_delete_async_work(_env, _work); _work = nullptr; } } inline void AsyncWorker::Destroy() { delete this; } inline AsyncWorker::AsyncWorker(AsyncWorker&& other) { _env = other._env; other._env = nullptr; _work = other._work; other._work = nullptr; _receiver = std::move(other._receiver); _callback = std::move(other._callback); _error = std::move(other._error); _suppress_destruct = other._suppress_destruct; } inline AsyncWorker& AsyncWorker::operator =(AsyncWorker&& other) { _env = other._env; other._env = nullptr; _work = other._work; other._work = nullptr; _receiver = std::move(other._receiver); _callback = std::move(other._callback); _error = std::move(other._error); _suppress_destruct = other._suppress_destruct; return *this; } inline AsyncWorker::operator napi_async_work() const { return _work; } inline Napi::Env AsyncWorker::Env() const { return Napi::Env(_env); } inline void AsyncWorker::Queue() { napi_status status = napi_queue_async_work(_env, _work); NAPI_THROW_IF_FAILED_VOID(_env, status); } inline void AsyncWorker::Cancel() { napi_status status = napi_cancel_async_work(_env, _work); NAPI_THROW_IF_FAILED_VOID(_env, status); } inline ObjectReference& AsyncWorker::Receiver() { return _receiver; } inline FunctionReference& AsyncWorker::Callback() { return _callback; } inline void AsyncWorker::SuppressDestruct() { _suppress_destruct = true; } inline void AsyncWorker::OnOK() { if (!_callback.IsEmpty()) { _callback.Call(_receiver.Value(), GetResult(_callback.Env())); } } inline void AsyncWorker::OnError(const Error& e) { if (!_callback.IsEmpty()) { _callback.Call(_receiver.Value(), std::initializer_list{ e.Value() }); } } inline void AsyncWorker::SetError(const std::string& error) { _error = error; } inline std::vector AsyncWorker::GetResult(Napi::Env /*env*/) { return {}; } // The OnAsyncWorkExecute method receives an napi_env argument. However, do NOT // use it within this method, as it does not run on the JavaScript thread and // must not run any method that would cause JavaScript to run. In practice, // this means that almost any use of napi_env will be incorrect. inline void AsyncWorker::OnAsyncWorkExecute(napi_env env, void* asyncworker) { AsyncWorker* self = static_cast(asyncworker); self->OnExecute(env); } // The OnExecute method receives an napi_env argument. However, do NOT // use it within this method, as it does not run on the JavaScript thread and // must not run any method that would cause JavaScript to run. In practice, // this means that almost any use of napi_env will be incorrect. inline void AsyncWorker::OnExecute(Napi::Env /*DO_NOT_USE*/) { #ifdef NAPI_CPP_EXCEPTIONS try { Execute(); } catch (const std::exception& e) { SetError(e.what()); } #else // NAPI_CPP_EXCEPTIONS Execute(); #endif // NAPI_CPP_EXCEPTIONS } inline void AsyncWorker::OnAsyncWorkComplete(napi_env env, napi_status status, void* asyncworker) { AsyncWorker* self = static_cast(asyncworker); self->OnWorkComplete(env, status); } inline void AsyncWorker::OnWorkComplete(Napi::Env /*env*/, napi_status status) { if (status != napi_cancelled) { HandleScope scope(_env); details::WrapCallback([&] { if (_error.size() == 0) { OnOK(); } else { OnError(Error::New(_env, _error)); } return nullptr; }); } if (!_suppress_destruct) { Destroy(); } } #if (NAPI_VERSION > 3 && !defined(__wasm32__)) //////////////////////////////////////////////////////////////////////////////// // TypedThreadSafeFunction class //////////////////////////////////////////////////////////////////////////////// // Starting with NAPI 5, the JavaScript function `func` parameter of // `napi_create_threadsafe_function` is optional. #if NAPI_VERSION > 4 // static, with Callback [missing] Resource [missing] Finalizer [missing] template template inline TypedThreadSafeFunction TypedThreadSafeFunction::New( napi_env env, ResourceString resourceName, size_t maxQueueSize, size_t initialThreadCount, ContextType* context) { TypedThreadSafeFunction tsfn; napi_status status = napi_create_threadsafe_function(env, nullptr, nullptr, String::From(env, resourceName), maxQueueSize, initialThreadCount, nullptr, nullptr, context, CallJsInternal, &tsfn._tsfn); if (status != napi_ok) { NAPI_THROW_IF_FAILED( env, status, TypedThreadSafeFunction()); } return tsfn; } // static, with Callback [missing] Resource [passed] Finalizer [missing] template template inline TypedThreadSafeFunction TypedThreadSafeFunction::New( napi_env env, const Object& resource, ResourceString resourceName, size_t maxQueueSize, size_t initialThreadCount, ContextType* context) { TypedThreadSafeFunction tsfn; napi_status status = napi_create_threadsafe_function(env, nullptr, resource, String::From(env, resourceName), maxQueueSize, initialThreadCount, nullptr, nullptr, context, CallJsInternal, &tsfn._tsfn); if (status != napi_ok) { NAPI_THROW_IF_FAILED( env, status, TypedThreadSafeFunction()); } return tsfn; } // static, with Callback [missing] Resource [missing] Finalizer [passed] template template inline TypedThreadSafeFunction TypedThreadSafeFunction::New( napi_env env, ResourceString resourceName, size_t maxQueueSize, size_t initialThreadCount, ContextType* context, Finalizer finalizeCallback, FinalizerDataType* data) { TypedThreadSafeFunction tsfn; auto* finalizeData = new details:: ThreadSafeFinalize( {data, finalizeCallback}); napi_status status = napi_create_threadsafe_function( env, nullptr, nullptr, String::From(env, resourceName), maxQueueSize, initialThreadCount, finalizeData, details::ThreadSafeFinalize:: FinalizeFinalizeWrapperWithDataAndContext, context, CallJsInternal, &tsfn._tsfn); if (status != napi_ok) { delete finalizeData; NAPI_THROW_IF_FAILED( env, status, TypedThreadSafeFunction()); } return tsfn; } // static, with Callback [missing] Resource [passed] Finalizer [passed] template template inline TypedThreadSafeFunction TypedThreadSafeFunction::New( napi_env env, const Object& resource, ResourceString resourceName, size_t maxQueueSize, size_t initialThreadCount, ContextType* context, Finalizer finalizeCallback, FinalizerDataType* data) { TypedThreadSafeFunction tsfn; auto* finalizeData = new details:: ThreadSafeFinalize( {data, finalizeCallback}); napi_status status = napi_create_threadsafe_function( env, nullptr, resource, String::From(env, resourceName), maxQueueSize, initialThreadCount, finalizeData, details::ThreadSafeFinalize:: FinalizeFinalizeWrapperWithDataAndContext, context, CallJsInternal, &tsfn._tsfn); if (status != napi_ok) { delete finalizeData; NAPI_THROW_IF_FAILED( env, status, TypedThreadSafeFunction()); } return tsfn; } #endif // static, with Callback [passed] Resource [missing] Finalizer [missing] template template inline TypedThreadSafeFunction TypedThreadSafeFunction::New( napi_env env, const Function& callback, ResourceString resourceName, size_t maxQueueSize, size_t initialThreadCount, ContextType* context) { TypedThreadSafeFunction tsfn; napi_status status = napi_create_threadsafe_function(env, callback, nullptr, String::From(env, resourceName), maxQueueSize, initialThreadCount, nullptr, nullptr, context, CallJsInternal, &tsfn._tsfn); if (status != napi_ok) { NAPI_THROW_IF_FAILED( env, status, TypedThreadSafeFunction()); } return tsfn; } // static, with Callback [passed] Resource [passed] Finalizer [missing] template template inline TypedThreadSafeFunction TypedThreadSafeFunction::New( napi_env env, const Function& callback, const Object& resource, ResourceString resourceName, size_t maxQueueSize, size_t initialThreadCount, ContextType* context) { TypedThreadSafeFunction tsfn; napi_status status = napi_create_threadsafe_function(env, callback, resource, String::From(env, resourceName), maxQueueSize, initialThreadCount, nullptr, nullptr, context, CallJsInternal, &tsfn._tsfn); if (status != napi_ok) { NAPI_THROW_IF_FAILED( env, status, TypedThreadSafeFunction()); } return tsfn; } // static, with Callback [passed] Resource [missing] Finalizer [passed] template template inline TypedThreadSafeFunction TypedThreadSafeFunction::New( napi_env env, const Function& callback, ResourceString resourceName, size_t maxQueueSize, size_t initialThreadCount, ContextType* context, Finalizer finalizeCallback, FinalizerDataType* data) { TypedThreadSafeFunction tsfn; auto* finalizeData = new details:: ThreadSafeFinalize( {data, finalizeCallback}); napi_status status = napi_create_threadsafe_function( env, callback, nullptr, String::From(env, resourceName), maxQueueSize, initialThreadCount, finalizeData, details::ThreadSafeFinalize:: FinalizeFinalizeWrapperWithDataAndContext, context, CallJsInternal, &tsfn._tsfn); if (status != napi_ok) { delete finalizeData; NAPI_THROW_IF_FAILED( env, status, TypedThreadSafeFunction()); } return tsfn; } // static, with: Callback [passed] Resource [passed] Finalizer [passed] template template inline TypedThreadSafeFunction TypedThreadSafeFunction::New( napi_env env, CallbackType callback, const Object& resource, ResourceString resourceName, size_t maxQueueSize, size_t initialThreadCount, ContextType* context, Finalizer finalizeCallback, FinalizerDataType* data) { TypedThreadSafeFunction tsfn; auto* finalizeData = new details:: ThreadSafeFinalize( {data, finalizeCallback}); napi_status status = napi_create_threadsafe_function( env, details::DefaultCallbackWrapper< CallbackType, TypedThreadSafeFunction>(env, callback), resource, String::From(env, resourceName), maxQueueSize, initialThreadCount, finalizeData, details::ThreadSafeFinalize:: FinalizeFinalizeWrapperWithDataAndContext, context, CallJsInternal, &tsfn._tsfn); if (status != napi_ok) { delete finalizeData; NAPI_THROW_IF_FAILED( env, status, TypedThreadSafeFunction()); } return tsfn; } template inline TypedThreadSafeFunction:: TypedThreadSafeFunction() : _tsfn() {} template inline TypedThreadSafeFunction:: TypedThreadSafeFunction(napi_threadsafe_function tsfn) : _tsfn(tsfn) {} template inline TypedThreadSafeFunction:: operator napi_threadsafe_function() const { return _tsfn; } template inline napi_status TypedThreadSafeFunction::BlockingCall( DataType* data) const { return napi_call_threadsafe_function(_tsfn, data, napi_tsfn_blocking); } template inline napi_status TypedThreadSafeFunction::NonBlockingCall( DataType* data) const { return napi_call_threadsafe_function(_tsfn, data, napi_tsfn_nonblocking); } template inline void TypedThreadSafeFunction::Ref( napi_env env) const { if (_tsfn != nullptr) { napi_status status = napi_ref_threadsafe_function(env, _tsfn); NAPI_THROW_IF_FAILED_VOID(env, status); } } template inline void TypedThreadSafeFunction::Unref( napi_env env) const { if (_tsfn != nullptr) { napi_status status = napi_unref_threadsafe_function(env, _tsfn); NAPI_THROW_IF_FAILED_VOID(env, status); } } template inline napi_status TypedThreadSafeFunction::Acquire() const { return napi_acquire_threadsafe_function(_tsfn); } template inline napi_status TypedThreadSafeFunction::Release() { return napi_release_threadsafe_function(_tsfn, napi_tsfn_release); } template inline napi_status TypedThreadSafeFunction::Abort() { return napi_release_threadsafe_function(_tsfn, napi_tsfn_abort); } template inline ContextType* TypedThreadSafeFunction::GetContext() const { void* context; napi_status status = napi_get_threadsafe_function_context(_tsfn, &context); NAPI_FATAL_IF_FAILED(status, "TypedThreadSafeFunction::GetContext", "napi_get_threadsafe_function_context"); return static_cast(context); } // static template void TypedThreadSafeFunction::CallJsInternal( napi_env env, napi_value jsCallback, void* context, void* data) { details::CallJsWrapper( env, jsCallback, context, data); } #if NAPI_VERSION == 4 // static template Napi::Function TypedThreadSafeFunction::EmptyFunctionFactory( Napi::Env env) { return Napi::Function::New(env, [](const CallbackInfo& cb) {}); } // static template Napi::Function TypedThreadSafeFunction::FunctionOrEmpty( Napi::Env env, Napi::Function& callback) { if (callback.IsEmpty()) { return EmptyFunctionFactory(env); } return callback; } #else // static template std::nullptr_t TypedThreadSafeFunction::EmptyFunctionFactory( Napi::Env /*env*/) { return nullptr; } // static template Napi::Function TypedThreadSafeFunction::FunctionOrEmpty( Napi::Env /*env*/, Napi::Function& callback) { return callback; } #endif //////////////////////////////////////////////////////////////////////////////// // ThreadSafeFunction class //////////////////////////////////////////////////////////////////////////////// // static template inline ThreadSafeFunction ThreadSafeFunction::New(napi_env env, const Function& callback, ResourceString resourceName, size_t maxQueueSize, size_t initialThreadCount) { return New(env, callback, Object(), resourceName, maxQueueSize, initialThreadCount); } // static template inline ThreadSafeFunction ThreadSafeFunction::New(napi_env env, const Function& callback, ResourceString resourceName, size_t maxQueueSize, size_t initialThreadCount, ContextType* context) { return New(env, callback, Object(), resourceName, maxQueueSize, initialThreadCount, context); } // static template inline ThreadSafeFunction ThreadSafeFunction::New(napi_env env, const Function& callback, ResourceString resourceName, size_t maxQueueSize, size_t initialThreadCount, Finalizer finalizeCallback) { return New(env, callback, Object(), resourceName, maxQueueSize, initialThreadCount, finalizeCallback); } // static template inline ThreadSafeFunction ThreadSafeFunction::New(napi_env env, const Function& callback, ResourceString resourceName, size_t maxQueueSize, size_t initialThreadCount, Finalizer finalizeCallback, FinalizerDataType* data) { return New(env, callback, Object(), resourceName, maxQueueSize, initialThreadCount, finalizeCallback, data); } // static template inline ThreadSafeFunction ThreadSafeFunction::New(napi_env env, const Function& callback, ResourceString resourceName, size_t maxQueueSize, size_t initialThreadCount, ContextType* context, Finalizer finalizeCallback) { return New(env, callback, Object(), resourceName, maxQueueSize, initialThreadCount, context, finalizeCallback); } // static template inline ThreadSafeFunction ThreadSafeFunction::New(napi_env env, const Function& callback, ResourceString resourceName, size_t maxQueueSize, size_t initialThreadCount, ContextType* context, Finalizer finalizeCallback, FinalizerDataType* data) { return New(env, callback, Object(), resourceName, maxQueueSize, initialThreadCount, context, finalizeCallback, data); } // static template inline ThreadSafeFunction ThreadSafeFunction::New(napi_env env, const Function& callback, const Object& resource, ResourceString resourceName, size_t maxQueueSize, size_t initialThreadCount) { return New(env, callback, resource, resourceName, maxQueueSize, initialThreadCount, static_cast(nullptr) /* context */); } // static template inline ThreadSafeFunction ThreadSafeFunction::New(napi_env env, const Function& callback, const Object& resource, ResourceString resourceName, size_t maxQueueSize, size_t initialThreadCount, ContextType* context) { return New(env, callback, resource, resourceName, maxQueueSize, initialThreadCount, context, [](Env, ContextType*) {} /* empty finalizer */); } // static template inline ThreadSafeFunction ThreadSafeFunction::New(napi_env env, const Function& callback, const Object& resource, ResourceString resourceName, size_t maxQueueSize, size_t initialThreadCount, Finalizer finalizeCallback) { return New(env, callback, resource, resourceName, maxQueueSize, initialThreadCount, static_cast(nullptr) /* context */, finalizeCallback, static_cast(nullptr) /* data */, details::ThreadSafeFinalize::Wrapper); } // static template inline ThreadSafeFunction ThreadSafeFunction::New(napi_env env, const Function& callback, const Object& resource, ResourceString resourceName, size_t maxQueueSize, size_t initialThreadCount, Finalizer finalizeCallback, FinalizerDataType* data) { return New(env, callback, resource, resourceName, maxQueueSize, initialThreadCount, static_cast(nullptr) /* context */, finalizeCallback, data, details::ThreadSafeFinalize< void, Finalizer, FinalizerDataType>::FinalizeWrapperWithData); } // static template inline ThreadSafeFunction ThreadSafeFunction::New(napi_env env, const Function& callback, const Object& resource, ResourceString resourceName, size_t maxQueueSize, size_t initialThreadCount, ContextType* context, Finalizer finalizeCallback) { return New(env, callback, resource, resourceName, maxQueueSize, initialThreadCount, context, finalizeCallback, static_cast(nullptr) /* data */, details::ThreadSafeFinalize< ContextType, Finalizer>::FinalizeWrapperWithContext); } // static template inline ThreadSafeFunction ThreadSafeFunction::New(napi_env env, const Function& callback, const Object& resource, ResourceString resourceName, size_t maxQueueSize, size_t initialThreadCount, ContextType* context, Finalizer finalizeCallback, FinalizerDataType* data) { return New(env, callback, resource, resourceName, maxQueueSize, initialThreadCount, context, finalizeCallback, data, details::ThreadSafeFinalize::FinalizeFinalizeWrapperWithDataAndContext); } inline ThreadSafeFunction::ThreadSafeFunction() : _tsfn() { } inline ThreadSafeFunction::ThreadSafeFunction( napi_threadsafe_function tsfn) : _tsfn(tsfn) { } inline ThreadSafeFunction::operator napi_threadsafe_function() const { return _tsfn; } inline napi_status ThreadSafeFunction::BlockingCall() const { return CallInternal(nullptr, napi_tsfn_blocking); } template <> inline napi_status ThreadSafeFunction::BlockingCall( void* data) const { return napi_call_threadsafe_function(_tsfn, data, napi_tsfn_blocking); } template inline napi_status ThreadSafeFunction::BlockingCall( Callback callback) const { return CallInternal(new CallbackWrapper(callback), napi_tsfn_blocking); } template inline napi_status ThreadSafeFunction::BlockingCall( DataType* data, Callback callback) const { auto wrapper = [data, callback](Env env, Function jsCallback) { callback(env, jsCallback, data); }; return CallInternal(new CallbackWrapper(wrapper), napi_tsfn_blocking); } inline napi_status ThreadSafeFunction::NonBlockingCall() const { return CallInternal(nullptr, napi_tsfn_nonblocking); } template <> inline napi_status ThreadSafeFunction::NonBlockingCall( void* data) const { return napi_call_threadsafe_function(_tsfn, data, napi_tsfn_nonblocking); } template inline napi_status ThreadSafeFunction::NonBlockingCall( Callback callback) const { return CallInternal(new CallbackWrapper(callback), napi_tsfn_nonblocking); } template inline napi_status ThreadSafeFunction::NonBlockingCall( DataType* data, Callback callback) const { auto wrapper = [data, callback](Env env, Function jsCallback) { callback(env, jsCallback, data); }; return CallInternal(new CallbackWrapper(wrapper), napi_tsfn_nonblocking); } inline void ThreadSafeFunction::Ref(napi_env env) const { if (_tsfn != nullptr) { napi_status status = napi_ref_threadsafe_function(env, _tsfn); NAPI_THROW_IF_FAILED_VOID(env, status); } } inline void ThreadSafeFunction::Unref(napi_env env) const { if (_tsfn != nullptr) { napi_status status = napi_unref_threadsafe_function(env, _tsfn); NAPI_THROW_IF_FAILED_VOID(env, status); } } inline napi_status ThreadSafeFunction::Acquire() const { return napi_acquire_threadsafe_function(_tsfn); } inline napi_status ThreadSafeFunction::Release() { return napi_release_threadsafe_function(_tsfn, napi_tsfn_release); } inline napi_status ThreadSafeFunction::Abort() { return napi_release_threadsafe_function(_tsfn, napi_tsfn_abort); } inline ThreadSafeFunction::ConvertibleContext ThreadSafeFunction::GetContext() const { void* context; napi_status status = napi_get_threadsafe_function_context(_tsfn, &context); NAPI_FATAL_IF_FAILED(status, "ThreadSafeFunction::GetContext", "napi_get_threadsafe_function_context"); return ConvertibleContext({ context }); } // static template inline ThreadSafeFunction ThreadSafeFunction::New(napi_env env, const Function& callback, const Object& resource, ResourceString resourceName, size_t maxQueueSize, size_t initialThreadCount, ContextType* context, Finalizer finalizeCallback, FinalizerDataType* data, napi_finalize wrapper) { static_assert(details::can_make_string::value || std::is_convertible::value, "Resource name should be convertible to the string type"); ThreadSafeFunction tsfn; auto* finalizeData = new details::ThreadSafeFinalize({ data, finalizeCallback }); napi_status status = napi_create_threadsafe_function(env, callback, resource, Value::From(env, resourceName), maxQueueSize, initialThreadCount, finalizeData, wrapper, context, CallJS, &tsfn._tsfn); if (status != napi_ok) { delete finalizeData; NAPI_THROW_IF_FAILED(env, status, ThreadSafeFunction()); } return tsfn; } inline napi_status ThreadSafeFunction::CallInternal( CallbackWrapper* callbackWrapper, napi_threadsafe_function_call_mode mode) const { napi_status status = napi_call_threadsafe_function( _tsfn, callbackWrapper, mode); if (status != napi_ok && callbackWrapper != nullptr) { delete callbackWrapper; } return status; } // static inline void ThreadSafeFunction::CallJS(napi_env env, napi_value jsCallback, void* /* context */, void* data) { if (env == nullptr && jsCallback == nullptr) { return; } if (data != nullptr) { auto* callbackWrapper = static_cast(data); (*callbackWrapper)(env, Function(env, jsCallback)); delete callbackWrapper; } else if (jsCallback != nullptr) { Function(env, jsCallback).Call({}); } } //////////////////////////////////////////////////////////////////////////////// // Async Progress Worker Base class //////////////////////////////////////////////////////////////////////////////// template inline AsyncProgressWorkerBase::AsyncProgressWorkerBase(const Object& receiver, const Function& callback, const char* resource_name, const Object& resource, size_t queue_size) : AsyncWorker(receiver, callback, resource_name, resource) { // Fill all possible arguments to work around ambiguous ThreadSafeFunction::New signatures. _tsfn = ThreadSafeFunction::New(callback.Env(), callback, resource, resource_name, queue_size, /** initialThreadCount */ 1, /** context */ this, OnThreadSafeFunctionFinalize, /** finalizeData */ this); } #if NAPI_VERSION > 4 template inline AsyncProgressWorkerBase::AsyncProgressWorkerBase(Napi::Env env, const char* resource_name, const Object& resource, size_t queue_size) : AsyncWorker(env, resource_name, resource) { // TODO: Once the changes to make the callback optional for threadsafe // functions are available on all versions we can remove the dummy Function here. Function callback; // Fill all possible arguments to work around ambiguous ThreadSafeFunction::New signatures. _tsfn = ThreadSafeFunction::New(env, callback, resource, resource_name, queue_size, /** initialThreadCount */ 1, /** context */ this, OnThreadSafeFunctionFinalize, /** finalizeData */ this); } #endif template inline AsyncProgressWorkerBase::~AsyncProgressWorkerBase() { // Abort pending tsfn call. // Don't send progress events after we've already completed. // It's ok to call ThreadSafeFunction::Abort and ThreadSafeFunction::Release duplicated. _tsfn.Abort(); } template inline void AsyncProgressWorkerBase::OnAsyncWorkProgress(Napi::Env /* env */, Napi::Function /* jsCallback */, void* data) { ThreadSafeData* tsd = static_cast(data); tsd->asyncprogressworker()->OnWorkProgress(tsd->data()); delete tsd; } template inline napi_status AsyncProgressWorkerBase::NonBlockingCall(DataType* data) { auto tsd = new AsyncProgressWorkerBase::ThreadSafeData(this, data); return _tsfn.NonBlockingCall(tsd, OnAsyncWorkProgress); } template inline void AsyncProgressWorkerBase::OnWorkComplete(Napi::Env /* env */, napi_status status) { _work_completed = true; _complete_status = status; _tsfn.Release(); } template inline void AsyncProgressWorkerBase::OnThreadSafeFunctionFinalize(Napi::Env env, void* /* data */, AsyncProgressWorkerBase* context) { if (context->_work_completed) { context->AsyncWorker::OnWorkComplete(env, context->_complete_status); } } //////////////////////////////////////////////////////////////////////////////// // Async Progress Worker class //////////////////////////////////////////////////////////////////////////////// template inline AsyncProgressWorker::AsyncProgressWorker(const Function& callback) : AsyncProgressWorker(callback, "generic") { } template inline AsyncProgressWorker::AsyncProgressWorker(const Function& callback, const char* resource_name) : AsyncProgressWorker(callback, resource_name, Object::New(callback.Env())) { } template inline AsyncProgressWorker::AsyncProgressWorker(const Function& callback, const char* resource_name, const Object& resource) : AsyncProgressWorker(Object::New(callback.Env()), callback, resource_name, resource) { } template inline AsyncProgressWorker::AsyncProgressWorker(const Object& receiver, const Function& callback) : AsyncProgressWorker(receiver, callback, "generic") { } template inline AsyncProgressWorker::AsyncProgressWorker(const Object& receiver, const Function& callback, const char* resource_name) : AsyncProgressWorker(receiver, callback, resource_name, Object::New(callback.Env())) { } template inline AsyncProgressWorker::AsyncProgressWorker(const Object& receiver, const Function& callback, const char* resource_name, const Object& resource) : AsyncProgressWorkerBase(receiver, callback, resource_name, resource), _asyncdata(nullptr), _asyncsize(0) { } #if NAPI_VERSION > 4 template inline AsyncProgressWorker::AsyncProgressWorker(Napi::Env env) : AsyncProgressWorker(env, "generic") { } template inline AsyncProgressWorker::AsyncProgressWorker(Napi::Env env, const char* resource_name) : AsyncProgressWorker(env, resource_name, Object::New(env)) { } template inline AsyncProgressWorker::AsyncProgressWorker(Napi::Env env, const char* resource_name, const Object& resource) : AsyncProgressWorkerBase(env, resource_name, resource), _asyncdata(nullptr), _asyncsize(0) { } #endif template inline AsyncProgressWorker::~AsyncProgressWorker() { { std::lock_guard lock(this->_mutex); _asyncdata = nullptr; _asyncsize = 0; } } template inline void AsyncProgressWorker::Execute() { ExecutionProgress progress(this); Execute(progress); } template inline void AsyncProgressWorker::OnWorkProgress(void*) { T* data; size_t size; { std::lock_guard lock(this->_mutex); data = this->_asyncdata; size = this->_asyncsize; this->_asyncdata = nullptr; this->_asyncsize = 0; } /** * The callback of ThreadSafeFunction is not been invoked immediately on the * callback of uv_async_t (uv io poll), rather the callback of TSFN is * invoked on the right next uv idle callback. There are chances that during * the deferring the signal of uv_async_t is been sent again, i.e. potential * not coalesced two calls of the TSFN callback. */ if (data == nullptr) { return; } this->OnProgress(data, size); delete[] data; } template inline void AsyncProgressWorker::SendProgress_(const T* data, size_t count) { T* new_data = new T[count]; std::copy(data, data + count, new_data); T* old_data; { std::lock_guard lock(this->_mutex); old_data = _asyncdata; _asyncdata = new_data; _asyncsize = count; } this->NonBlockingCall(nullptr); delete[] old_data; } template inline void AsyncProgressWorker::Signal() const { this->NonBlockingCall(static_cast(nullptr)); } template inline void AsyncProgressWorker::ExecutionProgress::Signal() const { _worker->Signal(); } template inline void AsyncProgressWorker::ExecutionProgress::Send(const T* data, size_t count) const { _worker->SendProgress_(data, count); } //////////////////////////////////////////////////////////////////////////////// // Async Progress Queue Worker class //////////////////////////////////////////////////////////////////////////////// template inline AsyncProgressQueueWorker::AsyncProgressQueueWorker(const Function& callback) : AsyncProgressQueueWorker(callback, "generic") { } template inline AsyncProgressQueueWorker::AsyncProgressQueueWorker(const Function& callback, const char* resource_name) : AsyncProgressQueueWorker(callback, resource_name, Object::New(callback.Env())) { } template inline AsyncProgressQueueWorker::AsyncProgressQueueWorker(const Function& callback, const char* resource_name, const Object& resource) : AsyncProgressQueueWorker(Object::New(callback.Env()), callback, resource_name, resource) { } template inline AsyncProgressQueueWorker::AsyncProgressQueueWorker(const Object& receiver, const Function& callback) : AsyncProgressQueueWorker(receiver, callback, "generic") { } template inline AsyncProgressQueueWorker::AsyncProgressQueueWorker(const Object& receiver, const Function& callback, const char* resource_name) : AsyncProgressQueueWorker(receiver, callback, resource_name, Object::New(callback.Env())) { } template inline AsyncProgressQueueWorker::AsyncProgressQueueWorker(const Object& receiver, const Function& callback, const char* resource_name, const Object& resource) : AsyncProgressWorkerBase>(receiver, callback, resource_name, resource, /** unlimited queue size */0) { } #if NAPI_VERSION > 4 template inline AsyncProgressQueueWorker::AsyncProgressQueueWorker(Napi::Env env) : AsyncProgressQueueWorker(env, "generic") { } template inline AsyncProgressQueueWorker::AsyncProgressQueueWorker(Napi::Env env, const char* resource_name) : AsyncProgressQueueWorker(env, resource_name, Object::New(env)) { } template inline AsyncProgressQueueWorker::AsyncProgressQueueWorker(Napi::Env env, const char* resource_name, const Object& resource) : AsyncProgressWorkerBase>(env, resource_name, resource, /** unlimited queue size */0) { } #endif template inline void AsyncProgressQueueWorker::Execute() { ExecutionProgress progress(this); Execute(progress); } template inline void AsyncProgressQueueWorker::OnWorkProgress(std::pair* datapair) { if (datapair == nullptr) { return; } T *data = datapair->first; size_t size = datapair->second; this->OnProgress(data, size); delete datapair; delete[] data; } template inline void AsyncProgressQueueWorker::SendProgress_(const T* data, size_t count) { T* new_data = new T[count]; std::copy(data, data + count, new_data); auto pair = new std::pair(new_data, count); this->NonBlockingCall(pair); } template inline void AsyncProgressQueueWorker::Signal() const { this->NonBlockingCall(nullptr); } template inline void AsyncProgressQueueWorker::OnWorkComplete(Napi::Env env, napi_status status) { // Draining queued items in TSFN. AsyncProgressWorkerBase>::OnWorkComplete(env, status); } template inline void AsyncProgressQueueWorker::ExecutionProgress::Signal() const { _worker->Signal(); } template inline void AsyncProgressQueueWorker::ExecutionProgress::Send(const T* data, size_t count) const { _worker->SendProgress_(data, count); } #endif // NAPI_VERSION > 3 && !defined(__wasm32__) //////////////////////////////////////////////////////////////////////////////// // Memory Management class //////////////////////////////////////////////////////////////////////////////// inline int64_t MemoryManagement::AdjustExternalMemory(Env env, int64_t change_in_bytes) { int64_t result; napi_status status = napi_adjust_external_memory(env, change_in_bytes, &result); NAPI_THROW_IF_FAILED(env, status, 0); return result; } //////////////////////////////////////////////////////////////////////////////// // Version Management class //////////////////////////////////////////////////////////////////////////////// inline uint32_t VersionManagement::GetNapiVersion(Env env) { uint32_t result; napi_status status = napi_get_version(env, &result); NAPI_THROW_IF_FAILED(env, status, 0); return result; } inline const napi_node_version* VersionManagement::GetNodeVersion(Env env) { const napi_node_version* result; napi_status status = napi_get_node_version(env, &result); NAPI_THROW_IF_FAILED(env, status, 0); return result; } #if NAPI_VERSION > 5 //////////////////////////////////////////////////////////////////////////////// // Addon class //////////////////////////////////////////////////////////////////////////////// template inline Object Addon::Init(Env env, Object exports) { T* addon = new T(env, exports); env.SetInstanceData(addon); return addon->entry_point_; } template inline T* Addon::Unwrap(Object wrapper) { return wrapper.Env().GetInstanceData(); } template inline void Addon::DefineAddon(Object exports, const std::initializer_list& props) { DefineProperties(exports, props); entry_point_ = exports; } template inline Napi::Object Addon::DefineProperties(Object object, const std::initializer_list& props) { const napi_property_descriptor* properties = reinterpret_cast(props.begin()); size_t size = props.size(); napi_status status = napi_define_properties(object.Env(), object, size, properties); NAPI_THROW_IF_FAILED(object.Env(), status, object); for (size_t idx = 0; idx < size; idx++) T::AttachPropData(object.Env(), object, &properties[idx]); return object; } #endif // NAPI_VERSION > 5 } // namespace Napi // hack-sqlite #endif // SRC_NAPI_INL_H_ #endif // SRC_NAPI_H_ /* repo https://github.com/mapbox/node-sqlite3/tree/v5.0.2 committed 2021-02-15T14:42:52Z */ /* file https://github.com/mapbox/node-sqlite3/blob/v5.0.2/LICENSE */ /* Copyright (c) MapBox All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. - Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. - Neither the name "MapBox" nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 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. */ /* file https://github.com/mapbox/node-sqlite3/blob/v5.0.2/deps/common-sqlite.gypi */ /* { 'variables': { 'sqlite_version%':'3340000', "toolset%":'', }, 'target_defaults': { 'default_configuration': 'Release', 'conditions': [ [ 'toolset!=""', { 'msbuild_toolset':'<(toolset)' }] ], 'configurations': { 'Debug': { 'defines!': [ 'NDEBUG' ], 'cflags_cc!': [ '-O3', '-Os', '-DNDEBUG' ], 'xcode_settings': { 'OTHER_CPLUSPLUSFLAGS!': [ '-O3', '-Os', '-DDEBUG' ], 'GCC_OPTIMIZATION_LEVEL': '0', 'GCC_GENERATE_DEBUGGING_SYMBOLS': 'YES' }, 'msvs_settings': { 'VCCLCompilerTool': { 'ExceptionHandling': 1, # /EHsc } } }, 'Release': { 'defines': [ 'NDEBUG' ], 'xcode_settings': { 'OTHER_CPLUSPLUSFLAGS!': [ '-Os', '-O2' ], 'GCC_OPTIMIZATION_LEVEL': '3', 'GCC_GENERATE_DEBUGGING_SYMBOLS': 'NO', 'DEAD_CODE_STRIPPING': 'YES', 'GCC_INLINES_ARE_PRIVATE_EXTERN': 'YES' }, 'msvs_settings': { 'VCCLCompilerTool': { 'ExceptionHandling': 1, # /EHsc } } } } } } */ /* file https://github.com/mapbox/node-sqlite3/blob/v5.0.2/deps/sqlite3.gyp */ /* { 'includes': [ 'common-sqlite.gypi' ], 'variables': { 'sqlite_magic%': '', }, 'target_defaults': { 'default_configuration': 'Release', 'cflags':[ '-std=c99' ], 'configurations': { 'Debug': { 'defines': [ 'DEBUG', '_DEBUG' ], 'msvs_settings': { 'VCCLCompilerTool': { 'RuntimeLibrary': 1, # static debug }, }, }, 'Release': { 'defines': [ 'NDEBUG' ], 'msvs_settings': { 'VCCLCompilerTool': { 'RuntimeLibrary': 0, # static release }, }, } }, 'msvs_settings': { 'VCCLCompilerTool': { }, 'VCLibrarianTool': { }, 'VCLinkerTool': { 'GenerateDebugInformation': 'true', }, }, 'conditions': [ ['OS == "win"', { 'defines': [ 'WIN32' ], }] ], }, 'targets': [ { 'target_name': 'action_before_build', 'type': 'none', 'hard_dependency': 1, 'actions': [ { 'action_name': 'unpack_sqlite_dep', 'inputs': [ './sqlite-autoconf-<@(sqlite_version).tar.gz' ], 'outputs': [ '<(SHARED_INTERMEDIATE_DIR)/sqlite-autoconf-<@(sqlite_version)/sqlite3.c' ], 'action': [' #define NODE_SQLITE3_MUTEX_t HANDLE mutex; #define NODE_SQLITE3_MUTEX_INIT mutex = CreateMutex(NULL, FALSE, NULL); #define NODE_SQLITE3_MUTEX_LOCK(m) WaitForSingleObject(*m, INFINITE); #define NODE_SQLITE3_MUTEX_UNLOCK(m) ReleaseMutex(*m); #define NODE_SQLITE3_MUTEX_DESTROY CloseHandle(mutex); #elif defined(NODE_SQLITE3_BOOST_THREADING) // #include #define NODE_SQLITE3_MUTEX_t boost::mutex mutex; #define NODE_SQLITE3_MUTEX_INIT #define NODE_SQLITE3_MUTEX_LOCK(m) (*m).lock(); #define NODE_SQLITE3_MUTEX_UNLOCK(m) (*m).unlock(); #define NODE_SQLITE3_MUTEX_DESTROY mutex.unlock(); #else #define NODE_SQLITE3_MUTEX_t pthread_mutex_t mutex; #define NODE_SQLITE3_MUTEX_INIT pthread_mutex_init(&mutex,NULL); #define NODE_SQLITE3_MUTEX_LOCK(m) pthread_mutex_lock(m); #define NODE_SQLITE3_MUTEX_UNLOCK(m) pthread_mutex_unlock(m); #define NODE_SQLITE3_MUTEX_DESTROY pthread_mutex_destroy(&mutex); #endif #endif // NODE_SQLITE3_SRC_THREADING_H /* file https://github.com/mapbox/node-sqlite3/blob/v5.0.2/src/macros.h */ #ifndef NODE_SQLITE3_SRC_MACROS_H #define NODE_SQLITE3_SRC_MACROS_H const char* sqlite_code_string(int code); const char* sqlite_authorizer_string(int type); // #include // TODO: better way to work around StringConcat? // #include inline Napi::String StringConcat(Napi::Value str1, Napi::Value str2) { return Napi::String::New(str1.Env(), str1.As().Utf8Value() + str2.As().Utf8Value() ); } // A Napi substitute IsInt32() inline bool OtherIsInt(Napi::Number source) { double orig_val = source.DoubleValue(); double int_val = (double)source.Int32Value(); if (orig_val == int_val) { return true; } else { return false; } } #define REQUIRE_ARGUMENTS(n) \ if (info.Length() < (n)) { \ Napi::TypeError::New(env, "Expected " #n "arguments").ThrowAsJavaScriptException(); \ return env.Null(); \ } #define REQUIRE_ARGUMENT_EXTERNAL(i, var) \ if (info.Length() <= (i) || !info[i].IsExternal()) { \ Napi::TypeError::New(env, "Argument " #i " invalid").ThrowAsJavaScriptException(); \ return env.Null(); \ } \ Napi::External var = info[i].As(); #define REQUIRE_ARGUMENT_FUNCTION(i, var) \ if (info.Length() <= (i) || !info[i].IsFunction()) { \ Napi::TypeError::New(env, "Argument " #i " must be a function").ThrowAsJavaScriptException(); \ return env.Null(); \ } \ Napi::Function var = info[i].As(); #define REQUIRE_ARGUMENT_STRING(i, var) \ if (info.Length() <= (i) || !info[i].IsString()) { \ Napi::TypeError::New(env, "Argument " #i " must be a string").ThrowAsJavaScriptException(); \ return env.Null(); \ } \ std::string var = info[i].As(); #define REQUIRE_ARGUMENT_INTEGER(i, var) \ if (info.Length() <= (i) || !info[i].IsNumber()) { \ Napi::TypeError::New(env, "Argument " #i " must be an integer").ThrowAsJavaScriptException(); \ return env.Null(); \ } \ int var(info[i].As().Int32Value()); #define OPTIONAL_ARGUMENT_FUNCTION(i, var) \ Napi::Function var; \ if (info.Length() > i && !info[i].IsUndefined()) { \ if (!info[i].IsFunction()) { \ Napi::TypeError::New(env, "Argument " #i " must be a function").ThrowAsJavaScriptException(); \ return env.Null(); \ } \ var = info[i].As(); \ } #define OPTIONAL_ARGUMENT_INTEGER(i, var, default) \ int var; \ if (info.Length() <= (i)) { \ var = (default); \ } \ else if (info[i].IsNumber()) { \ if (OtherIsInt(info[i].As())) { \ var = info[i].As().Int32Value(); \ } \ } \ else { \ Napi::TypeError::New(env, "Argument " #i " must be an integer").ThrowAsJavaScriptException(); \ return env.Null(); \ } #define DEFINE_CONSTANT_INTEGER(target, constant, name) \ Napi::PropertyDescriptor::Value(#name, Napi::Number::New(env, constant), \ static_cast(napi_enumerable | napi_configurable)), #define DEFINE_CONSTANT_STRING(target, constant, name) \ Napi::PropertyDescriptor::Value(#name, Napi::String::New(env, constant), \ static_cast(napi_enumerable | napi_configurable)), #define EXCEPTION(msg, errno, name) \ Napi::Value name = Napi::Error::New(env, \ StringConcat( \ StringConcat( \ Napi::String::New(env, sqlite_code_string(errno)), \ Napi::String::New(env, ": ") \ ), \ (msg) \ ).Utf8Value() \ ).Value(); \ Napi::Object name ##_obj = name.As(); \ (name ##_obj).Set( Napi::String::New(env, "errno"), Napi::Number::New(env, errno)); \ (name ##_obj).Set( Napi::String::New(env, "code"), \ Napi::String::New(env, sqlite_code_string(errno))); #define EMIT_EVENT(obj, argc, argv) \ TRY_CATCH_CALL((obj), \ (obj).Get("emit").As(),\ argc, argv \ ); // The Mac OS compiler complains when argv is NULL unless we // first assign it to a locally defined variable. #define TRY_CATCH_CALL(context, callback, argc, argv, ...) \ Napi::Value* passed_argv = argv;\ std::vector args;\ if ((argc != 0) && (passed_argv != NULL)) {\ args.assign(passed_argv, passed_argv + argc);\ }\ Napi::Value res = (callback).MakeCallback(Napi::Value(context), args); \ if (res.IsEmpty()) return __VA_ARGS__; #define WORK_DEFINITION(name) \ Napi::Value name(const Napi::CallbackInfo& info); \ static void Work_Begin##name(Baton* baton); \ static void Work_##name(napi_env env, void* data); \ static void Work_After##name(napi_env env, napi_status status, void* data); #define STATEMENT_BEGIN(type) \ assert(baton); \ assert(baton->stmt); \ assert(!baton->stmt->locked); \ assert(!baton->stmt->finalized); \ assert(baton->stmt->prepared); \ baton->stmt->locked = true; \ baton->stmt->db->pending++; \ Napi::Env env = baton->stmt->Env(); \ int status = napi_create_async_work( \ env, NULL, Napi::String::New(env, "sqlite3.Statement."#type), \ Work_##type, Work_After##type, baton, &baton->request \ ); \ assert(status == 0); \ napi_queue_async_work(env, baton->request); #define STATEMENT_INIT(type) \ type* baton = static_cast(data); \ Statement* stmt = baton->stmt; #define STATEMENT_MUTEX(name) \ if (!stmt->db->_handle) { \ stmt->status = SQLITE_MISUSE; \ stmt->message = "Database handle is closed"; \ return; \ } \ sqlite3_mutex* name = sqlite3_db_mutex(stmt->db->_handle); #define STATEMENT_END() \ assert(stmt->locked); \ assert(stmt->db->pending); \ stmt->locked = false; \ stmt->db->pending--; \ stmt->Process(); \ stmt->db->Process(); #define BACKUP_BEGIN(type) \ assert(baton); \ assert(baton->backup); \ assert(!baton->backup->locked); \ assert(!baton->backup->finished); \ assert(baton->backup->inited); \ baton->backup->locked = true; \ baton->backup->db->pending++; \ Napi::Env env = baton->backup->Env(); \ int status = napi_create_async_work( \ env, NULL, Napi::String::New(env, "sqlite3.Backup."#type), \ Work_##type, Work_After##type, baton, &baton->request \ ); \ assert(status == 0); \ napi_queue_async_work(env, baton->request); #define BACKUP_INIT(type) \ type* baton = static_cast(data); \ Backup* backup = baton->backup; #define BACKUP_END() \ assert(backup->locked); \ assert(backup->db->pending); \ backup->locked = false; \ backup->db->pending--; \ backup->Process(); \ backup->db->Process(); #define DELETE_FIELD(field) \ if (field != NULL) { \ switch ((field)->type) { \ case SQLITE_INTEGER: delete (Values::Integer*)(field); break; \ case SQLITE_FLOAT: delete (Values::Float*)(field); break; \ case SQLITE_TEXT: delete (Values::Text*)(field); break; \ case SQLITE_BLOB: delete (Values::Blob*)(field); break; \ case SQLITE_NULL: delete (Values::Null*)(field); break; \ } \ } #endif /* file https://github.com/mapbox/node-sqlite3/blob/v5.0.2/src/async.h */ #ifndef NODE_SQLITE3_SRC_ASYNC_H #define NODE_SQLITE3_SRC_ASYNC_H // #include // #include // #include "threading.h" #if defined(NODE_SQLITE3_BOOST_THREADING) // #include #endif // Generic uv_async handler. template class Async { typedef void (*Callback)(Parent* parent, Item* item); protected: uv_async_t watcher; NODE_SQLITE3_MUTEX_t std::vector data; Callback callback; public: Parent* parent; public: Async(Parent* parent_, Callback cb_) : callback(cb_), parent(parent_) { watcher.data = this; NODE_SQLITE3_MUTEX_INIT uv_loop_t *loop; napi_get_uv_event_loop(parent_->Env(), &loop); uv_async_init(loop, &watcher, reinterpret_cast(listener)); } static void listener(uv_async_t* handle) { Async* async = static_cast(handle->data); std::vector rows; NODE_SQLITE3_MUTEX_LOCK(&async->mutex) rows.swap(async->data); NODE_SQLITE3_MUTEX_UNLOCK(&async->mutex) for (unsigned int i = 0, size = rows.size(); i < size; i++) { async->callback(async->parent, rows[i]); } } static void close(uv_handle_t* handle) { assert(handle != NULL); assert(handle->data != NULL); Async* async = static_cast(handle->data); delete async; } void finish() { // Need to call the listener again to ensure all items have been // processed. Is this a bug in uv_async? Feels like uv_close // should handle that. listener(&watcher); uv_close((uv_handle_t*)&watcher, close); } void add(Item* item) { NODE_SQLITE3_MUTEX_LOCK(&mutex); data.push_back(item); NODE_SQLITE3_MUTEX_UNLOCK(&mutex) } void send() { uv_async_send(&watcher); } void send(Item* item) { add(item); send(); } ~Async() { NODE_SQLITE3_MUTEX_DESTROY } }; #endif /* file https://github.com/mapbox/node-sqlite3/blob/v5.0.2/src/database.h */ #ifndef NODE_SQLITE3_SRC_DATABASE_H #define NODE_SQLITE3_SRC_DATABASE_H // #include // #include // #include // #include // #include // #include "async.h" using namespace Napi; namespace node_sqlite3 { class Database; class Database : public Napi::ObjectWrap { public: #if NAPI_VERSION < 6 static Napi::FunctionReference constructor; #endif static Napi::Object Init(Napi::Env env, Napi::Object exports); static inline bool HasInstance(Napi::Value val) { Napi::Env env = val.Env(); Napi::HandleScope scope(env); if (!val.IsObject()) return false; Napi::Object obj = val.As(); #if NAPI_VERSION < 6 return obj.InstanceOf(constructor.Value()); #else Napi::FunctionReference* constructor = env.GetInstanceData(); return obj.InstanceOf(constructor->Value()); #endif } struct Baton { napi_async_work request = NULL; Database* db; Napi::FunctionReference callback; int status; std::string message; Baton(Database* db_, Napi::Function cb_) : db(db_), status(SQLITE_OK) { db->Ref(); if (!cb_.IsUndefined() && cb_.IsFunction()) { callback.Reset(cb_, 1); } } virtual ~Baton() { if (request) napi_delete_async_work(db->Env(), request); db->Unref(); callback.Reset(); } }; struct OpenBaton : Baton { std::string filename; int mode; OpenBaton(Database* db_, Napi::Function cb_, const char* filename_, int mode_) : Baton(db_, cb_), filename(filename_), mode(mode_) {} }; struct ExecBaton : Baton { std::string sql; ExecBaton(Database* db_, Napi::Function cb_, const char* sql_) : Baton(db_, cb_), sql(sql_) {} }; struct LoadExtensionBaton : Baton { std::string filename; LoadExtensionBaton(Database* db_, Napi::Function cb_, const char* filename_) : Baton(db_, cb_), filename(filename_) {} }; typedef void (*Work_Callback)(Baton* baton); struct Call { Call(Work_Callback cb_, Baton* baton_, bool exclusive_ = false) : callback(cb_), exclusive(exclusive_), baton(baton_) {}; Work_Callback callback; bool exclusive; Baton* baton; }; struct ProfileInfo { std::string sql; sqlite3_int64 nsecs; }; struct UpdateInfo { int type; std::string database; std::string table; sqlite3_int64 rowid; }; bool IsOpen() { return open; } bool IsLocked() { return locked; } typedef Async AsyncTrace; typedef Async AsyncProfile; typedef Async AsyncUpdate; friend class Statement; friend class Backup; void init() { _handle = NULL; open = false; closing = false; locked = false; pending = 0; serialize = false; debug_trace = NULL; debug_profile = NULL; update_event = NULL; } Database(const Napi::CallbackInfo& info); ~Database() { RemoveCallbacks(); sqlite3_close(_handle); _handle = NULL; open = false; } protected: static void Work_BeginOpen(Baton* baton); static void Work_Open(napi_env env, void* data); static void Work_AfterOpen(napi_env env, napi_status status, void* data); Napi::Value OpenGetter(const Napi::CallbackInfo& info); void Schedule(Work_Callback callback, Baton* baton, bool exclusive = false); void Process(); Napi::Value Exec(const Napi::CallbackInfo& info); static void Work_BeginExec(Baton* baton); static void Work_Exec(napi_env env, void* data); static void Work_AfterExec(napi_env env, napi_status status, void* data); Napi::Value Wait(const Napi::CallbackInfo& info); static void Work_Wait(Baton* baton); Napi::Value Close(const Napi::CallbackInfo& info); static void Work_BeginClose(Baton* baton); static void Work_Close(napi_env env, void* data); static void Work_AfterClose(napi_env env, napi_status status, void* data); Napi::Value LoadExtension(const Napi::CallbackInfo& info); static void Work_BeginLoadExtension(Baton* baton); static void Work_LoadExtension(napi_env env, void* data); static void Work_AfterLoadExtension(napi_env env, napi_status status, void* data); Napi::Value Serialize(const Napi::CallbackInfo& info); Napi::Value Parallelize(const Napi::CallbackInfo& info); Napi::Value Configure(const Napi::CallbackInfo& info); Napi::Value Interrupt(const Napi::CallbackInfo& info); static void SetBusyTimeout(Baton* baton); static void RegisterTraceCallback(Baton* baton); static void TraceCallback(void* db, const char* sql); static void TraceCallback(Database* db, std::string* sql); static void RegisterProfileCallback(Baton* baton); static void ProfileCallback(void* db, const char* sql, sqlite3_uint64 nsecs); static void ProfileCallback(Database* db, ProfileInfo* info); static void RegisterUpdateCallback(Baton* baton); static void UpdateCallback(void* db, int type, const char* database, const char* table, sqlite3_int64 rowid); static void UpdateCallback(Database* db, UpdateInfo* info); void RemoveCallbacks(); protected: sqlite3* _handle; bool open; bool closing; bool locked; unsigned int pending; bool serialize; std::queue queue; AsyncTrace* debug_trace; AsyncProfile* debug_profile; AsyncUpdate* update_event; }; } #endif /* file https://github.com/mapbox/node-sqlite3/blob/v5.0.2/src/backup.h */ #ifndef NODE_SQLITE3_SRC_BACKUP_H #define NODE_SQLITE3_SRC_BACKUP_H // #include "database.h" // #include // #include // #include // #include // #include using namespace Napi; namespace node_sqlite3 { /** * * A class for managing an sqlite3_backup object. For consistency * with other node-sqlite3 classes, it maintains an internal queue * of calls. * * Intended usage from node: * * var db = new sqlite3.Database('live.db'); * var backup = db.backup('backup.db'); * ... * // in event loop, move backup forward when we have time. * if (backup.idle) { backup.step(NPAGES); } * if (backup.completed) { ... success ... } * if (backup.failed) { ... sadness ... } * // do other work in event loop - fine to modify live.db * ... * * Here is how sqlite's backup api is exposed: * * - `sqlite3_backup_init`: This is implemented as * `db.backup(filename, [callback])` or * `db.backup(filename, destDbName, sourceDbName, filenameIsDest, [callback])`. * - `sqlite3_backup_step`: `backup.step(pages, [callback])`. * - `sqlite3_backup_finish`: `backup.finish([callback])`. * - `sqlite3_backup_remaining`: `backup.remaining`. * - `sqlite3_backup_pagecount`: `backup.pageCount`. * * There are the following read-only properties: * * - `backup.completed` is set to `true` when the backup * succeeeds. * - `backup.failed` is set to `true` when the backup * has a fatal error. * - `backup.idle` is set to `true` when no operation * is currently in progress or queued for the backup. * - `backup.remaining` is an integer with the remaining * number of pages after the last call to `backup.step` * (-1 if `step` not yet called). * - `backup.pageCount` is an integer with the total number * of pages measured during the last call to `backup.step` * (-1 if `step` not yet called). * * There is the following writable property: * * - `backup.retryErrors`: an array of sqlite3 error codes * that are treated as non-fatal - meaning, if they occur, * backup.failed is not set, and the backup may continue. * By default, this is `[sqlite3.BUSY, sqlite3.LOCKED]`. * * The `db.backup(filename, [callback])` shorthand is sufficient * for making a backup of a database opened by node-sqlite3. If * using attached or temporary databases, or moving data in the * opposite direction, the more complete (but daunting) * `db.backup(filename, destDbName, sourceDbName, filenameIsDest, [callback])` * signature is provided. * * A backup will finish automatically when it succeeds or a fatal * error occurs, meaning it is not necessary to call `db.finish()`. * By default, SQLITE_LOCKED and SQLITE_BUSY errors are not * treated as failures, and the backup will continue if they * occur. The set of errors that are tolerated can be controlled * by setting `backup.retryErrors`. To disable automatic * finishing and stick strictly to sqlite's raw api, set * `backup.retryErrors` to `[]`. In that case, it is necessary * to call `backup.finish()`. * * In the same way as node-sqlite3 databases and statements, * backup methods can be called safely without callbacks, due * to an internal call queue. So for example this naive code * will correctly back up a db, if there are no errors: * * var backup = db.backup('backup.db'); * backup.step(-1); * backup.finish(); * */ class Backup : public Napi::ObjectWrap { public: static Napi::Object Init(Napi::Env env, Napi::Object exports); struct Baton { napi_async_work request = NULL; Backup* backup; Napi::FunctionReference callback; Baton(Backup* backup_, Napi::Function cb_) : backup(backup_) { backup->Ref(); callback.Reset(cb_, 1); } virtual ~Baton() { if (request) napi_delete_async_work(backup->Env(), request); backup->Unref(); callback.Reset(); } }; struct InitializeBaton : Database::Baton { Backup* backup; std::string filename; std::string sourceName; std::string destName; bool filenameIsDest; InitializeBaton(Database* db_, Napi::Function cb_, Backup* backup_) : Baton(db_, cb_), backup(backup_), filenameIsDest(true) { backup->Ref(); } virtual ~InitializeBaton() { backup->Unref(); if (!db->IsOpen() && db->IsLocked()) { // The database handle was closed before the backup could be opened. backup->FinishAll(); } } }; struct StepBaton : Baton { int pages; std::set retryErrorsSet; StepBaton(Backup* backup_, Napi::Function cb_, int pages_) : Baton(backup_, cb_), pages(pages_) {} }; typedef void (*Work_Callback)(Baton* baton); struct Call { Call(Work_Callback cb_, Baton* baton_) : callback(cb_), baton(baton_) {}; Work_Callback callback; Baton* baton; }; void init(Database* db_) { db = db_; _handle = NULL; _otherDb = NULL; _destDb = NULL; inited = false; locked = true; completed = false; failed = false; remaining = -1; pageCount = -1; finished = false; db->Ref(); } Backup(const Napi::CallbackInfo& info); ~Backup() { if (!finished) { FinishAll(); } retryErrors.Reset(); } WORK_DEFINITION(Step); WORK_DEFINITION(Finish); Napi::Value IdleGetter(const Napi::CallbackInfo& info); Napi::Value CompletedGetter(const Napi::CallbackInfo& info); Napi::Value FailedGetter(const Napi::CallbackInfo& info); Napi::Value PageCountGetter(const Napi::CallbackInfo& info); Napi::Value RemainingGetter(const Napi::CallbackInfo& info); Napi::Value FatalErrorGetter(const Napi::CallbackInfo& info); Napi::Value RetryErrorGetter(const Napi::CallbackInfo& info); void FatalErrorSetter(const Napi::CallbackInfo& info, const Napi::Value& value); void RetryErrorSetter(const Napi::CallbackInfo& info, const Napi::Value& value); protected: static void Work_BeginInitialize(Database::Baton* baton); static void Work_Initialize(napi_env env, void* data); static void Work_AfterInitialize(napi_env env, napi_status status, void* data); void Schedule(Work_Callback callback, Baton* baton); void Process(); void CleanQueue(); template static void Error(T* baton); void FinishAll(); void FinishSqlite(); void GetRetryErrors(std::set& retryErrorsSet); Database* db; sqlite3_backup* _handle; sqlite3* _otherDb; sqlite3* _destDb; int status; std::string message; bool inited; bool locked; bool completed; bool failed; int remaining; int pageCount; bool finished; std::queue queue; Napi::Reference retryErrors; }; } #endif /* file https://github.com/mapbox/node-sqlite3/blob/v5.0.2/src/statement.h */ #ifndef NODE_SQLITE3_SRC_STATEMENT_H #define NODE_SQLITE3_SRC_STATEMENT_H // #include // #include // #include // #include // #include // #include // #include // #include // #include "database.h" // #include "threading.h" using namespace Napi; namespace node_sqlite3 { namespace Values { struct Field { inline Field(unsigned short _index, unsigned short _type = SQLITE_NULL) : type(_type), index(_index) {} inline Field(const char* _name, unsigned short _type = SQLITE_NULL) : type(_type), index(0), name(_name) {} unsigned short type; unsigned short index; std::string name; }; struct Integer : Field { template inline Integer(T _name, int64_t val) : Field(_name, SQLITE_INTEGER), value(val) {} int64_t value; }; struct Float : Field { template inline Float(T _name, double val) : Field(_name, SQLITE_FLOAT), value(val) {} double value; }; struct Text : Field { template inline Text(T _name, size_t len, const char* val) : Field(_name, SQLITE_TEXT), value(val, len) {} std::string value; }; struct Blob : Field { template inline Blob(T _name, size_t len, const void* val) : Field(_name, SQLITE_BLOB), length(len) { value = (char*)malloc(len); memcpy(value, val, len); } inline ~Blob() { free(value); } int length; char* value; }; typedef Field Null; } typedef std::vector Row; typedef std::vector Rows; typedef Row Parameters; class Statement : public Napi::ObjectWrap { public: static Napi::Object Init(Napi::Env env, Napi::Object exports); static Napi::Value New(const Napi::CallbackInfo& info); struct Baton { napi_async_work request = NULL; Statement* stmt; Napi::FunctionReference callback; Parameters parameters; Baton(Statement* stmt_, Napi::Function cb_) : stmt(stmt_) { stmt->Ref(); callback.Reset(cb_, 1); } virtual ~Baton() { for (unsigned int i = 0; i < parameters.size(); i++) { Values::Field* field = parameters[i]; DELETE_FIELD(field); } if (request) napi_delete_async_work(stmt->Env(), request); stmt->Unref(); callback.Reset(); } }; struct RowBaton : Baton { RowBaton(Statement* stmt_, Napi::Function cb_) : Baton(stmt_, cb_) {} Row row; }; struct RunBaton : Baton { RunBaton(Statement* stmt_, Napi::Function cb_) : Baton(stmt_, cb_), inserted_id(0), changes(0) {} sqlite3_int64 inserted_id; int changes; }; struct RowsBaton : Baton { RowsBaton(Statement* stmt_, Napi::Function cb_) : Baton(stmt_, cb_) {} Rows rows; }; struct Async; struct EachBaton : Baton { Napi::FunctionReference completed; Async* async; // Isn't deleted when the baton is deleted. EachBaton(Statement* stmt_, Napi::Function cb_) : Baton(stmt_, cb_) {} virtual ~EachBaton() { completed.Reset(); } }; struct PrepareBaton : Database::Baton { Statement* stmt; std::string sql; PrepareBaton(Database* db_, Napi::Function cb_, Statement* stmt_) : Baton(db_, cb_), stmt(stmt_) { stmt->Ref(); } virtual ~PrepareBaton() { stmt->Unref(); if (!db->IsOpen() && db->IsLocked()) { // The database handle was closed before the statement could be // prepared. stmt->Finalize_(); } } }; typedef void (*Work_Callback)(Baton* baton); struct Call { Call(Work_Callback cb_, Baton* baton_) : callback(cb_), baton(baton_) {}; Work_Callback callback; Baton* baton; }; struct Async { uv_async_t watcher; Statement* stmt; Rows data; NODE_SQLITE3_MUTEX_t; bool completed; int retrieved; // Store the callbacks here because we don't have // access to the baton in the async callback. Napi::FunctionReference item_cb; Napi::FunctionReference completed_cb; Async(Statement* st, uv_async_cb async_cb) : stmt(st), completed(false), retrieved(0) { watcher.data = this; NODE_SQLITE3_MUTEX_INIT stmt->Ref(); uv_loop_t *loop; napi_get_uv_event_loop(stmt->Env(), &loop); uv_async_init(loop, &watcher, async_cb); } ~Async() { stmt->Unref(); item_cb.Reset(); completed_cb.Reset(); NODE_SQLITE3_MUTEX_DESTROY } }; void init(Database* db_) { db = db_; _handle = NULL; status = SQLITE_OK; prepared = false; locked = true; finalized = false; db->Ref(); } Statement(const Napi::CallbackInfo& info); ~Statement() { if (!finalized) Finalize_(); } WORK_DEFINITION(Bind); WORK_DEFINITION(Get); WORK_DEFINITION(Run); WORK_DEFINITION(All); WORK_DEFINITION(Each); WORK_DEFINITION(Reset); Napi::Value Finalize_(const Napi::CallbackInfo& info); protected: static void Work_BeginPrepare(Database::Baton* baton); static void Work_Prepare(napi_env env, void* data); static void Work_AfterPrepare(napi_env env, napi_status status, void* data); static void AsyncEach(uv_async_t* handle); static void CloseCallback(uv_handle_t* handle); static void Finalize_(Baton* baton); void Finalize_(); template inline Values::Field* BindParameter(const Napi::Value source, T pos); template T* Bind(const Napi::CallbackInfo& info, int start = 0, int end = -1); bool Bind(const Parameters ¶meters); static void GetRow(Row* row, sqlite3_stmt* stmt); static Napi::Value RowToJS(Napi::Env env, Row* row); void Schedule(Work_Callback callback, Baton* baton); void Process(); void CleanQueue(); template static void Error(T* baton); protected: Database* db; sqlite3_stmt* _handle; int status; std::string message; bool prepared; bool locked; bool finalized; std::queue queue; }; } #endif /* file https://github.com/mapbox/node-sqlite3/blob/v5.0.2/src/database.cc */ // #include // #include // #include "macros.h" // #include "database.h" // #include "statement.h" using namespace node_sqlite3; #if NAPI_VERSION < 6 Napi::FunctionReference Database::constructor; #endif Napi::Object Database::Init(Napi::Env env, Napi::Object exports) { Napi::HandleScope scope(env); Napi::Function t = DefineClass(env, "Database", { InstanceMethod("close", &Database::Close), InstanceMethod("exec", &Database::Exec), InstanceMethod("wait", &Database::Wait), InstanceMethod("loadExtension", &Database::LoadExtension), InstanceMethod("serialize", &Database::Serialize), InstanceMethod("parallelize", &Database::Parallelize), InstanceMethod("configure", &Database::Configure), InstanceMethod("interrupt", &Database::Interrupt), InstanceAccessor("open", &Database::OpenGetter, nullptr) }); #if NAPI_VERSION < 6 constructor = Napi::Persistent(t); constructor.SuppressDestruct(); #else Napi::FunctionReference* constructor = new Napi::FunctionReference(); *constructor = Napi::Persistent(t); env.SetInstanceData(constructor); #endif exports.Set("Database", t); return exports; } void Database::Process() { Napi::Env env = this->Env(); Napi::HandleScope scope(env); if (!open && locked && !queue.empty()) { EXCEPTION(Napi::String::New(env, "Database handle is closed"), SQLITE_MISUSE, exception); Napi::Value argv[] = { exception }; bool called = false; // Call all callbacks with the error object. while (!queue.empty()) { std::unique_ptr call(queue.front()); queue.pop(); std::unique_ptr baton(call->baton); Napi::Function cb = baton->callback.Value(); if (!cb.IsUndefined() && cb.IsFunction()) { TRY_CATCH_CALL(this->Value(), cb, 1, argv); called = true; } } // When we couldn't call a callback function, emit an error on the // Database object. if (!called) { Napi::Value info[] = { Napi::String::New(env, "error"), exception }; EMIT_EVENT(Value(), 2, info); } return; } while (open && (!locked || pending == 0) && !queue.empty()) { Call *c = queue.front(); if (c->exclusive && pending > 0) { break; } queue.pop(); std::unique_ptr call(c); locked = call->exclusive; call->callback(call->baton); if (locked) break; } } void Database::Schedule(Work_Callback callback, Baton* baton, bool exclusive) { Napi::Env env = this->Env(); Napi::HandleScope scope(env); if (!open && locked) { EXCEPTION(Napi::String::New(env, "Database is closed"), SQLITE_MISUSE, exception); Napi::Function cb = baton->callback.Value(); // We don't call the actual callback, so we have to make sure that // the baton gets destroyed. delete baton; if (!cb.IsUndefined() && cb.IsFunction()) { Napi::Value argv[] = { exception }; TRY_CATCH_CALL(Value(), cb, 1, argv); } else { Napi::Value argv[] = { Napi::String::New(env, "error"), exception }; EMIT_EVENT(Value(), 2, argv); } return; } if (!open || ((locked || exclusive || serialize) && pending > 0)) { queue.push(new Call(callback, baton, exclusive || serialize)); } else { locked = exclusive; callback(baton); } } Database::Database(const Napi::CallbackInfo& info) : Napi::ObjectWrap(info) { init(); Napi::Env env = info.Env(); if (info.Length() <= 0 || !info[0].IsString()) { Napi::TypeError::New(env, "String expected").ThrowAsJavaScriptException(); return; } std::string filename = info[0].As(); unsigned int pos = 1; int mode; if (info.Length() >= pos && info[pos].IsNumber() && OtherIsInt(info[pos].As())) { mode = info[pos++].As().Int32Value(); } else { mode = SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE | SQLITE_OPEN_FULLMUTEX; } Napi::Function callback; if (info.Length() >= pos && info[pos].IsFunction()) { callback = info[pos++].As(); } info.This().As().DefineProperty(Napi::PropertyDescriptor::Value("filename", info[0].As(), napi_default)); info.This().As().DefineProperty(Napi::PropertyDescriptor::Value("mode", Napi::Number::New(env, mode), napi_default)); // Start opening the database. OpenBaton* baton = new OpenBaton(this, callback, filename.c_str(), mode); Work_BeginOpen(baton); } void Database::Work_BeginOpen(Baton* baton) { Napi::Env env = baton->db->Env(); int status = napi_create_async_work( env, NULL, Napi::String::New(env, "sqlite3.Database.Open"), Work_Open, Work_AfterOpen, baton, &baton->request ); assert(status == 0); napi_queue_async_work(env, baton->request); } void Database::Work_Open(napi_env e, void* data) { OpenBaton* baton = static_cast(data); Database* db = baton->db; baton->status = sqlite3_open_v2( baton->filename.c_str(), &db->_handle, baton->mode, NULL ); if (baton->status != SQLITE_OK) { baton->message = std::string(sqlite3_errmsg(db->_handle)); sqlite3_close(db->_handle); db->_handle = NULL; } else { // Set default database handle values. sqlite3_busy_timeout(db->_handle, 1000); } } void Database::Work_AfterOpen(napi_env e, napi_status status, void* data) { std::unique_ptr baton(static_cast(data)); Database* db = baton->db; Napi::Env env = db->Env(); Napi::HandleScope scope(env); Napi::Value argv[1]; if (baton->status != SQLITE_OK) { EXCEPTION(Napi::String::New(env, baton->message.c_str()), baton->status, exception); argv[0] = exception; } else { db->open = true; argv[0] = env.Null(); } Napi::Function cb = baton->callback.Value(); if (!cb.IsUndefined() && cb.IsFunction()) { TRY_CATCH_CALL(db->Value(), cb, 1, argv); } else if (!db->open) { Napi::Value info[] = { Napi::String::New(env, "error"), argv[0] }; EMIT_EVENT(db->Value(), 2, info); } if (db->open) { Napi::Value info[] = { Napi::String::New(env, "open") }; EMIT_EVENT(db->Value(), 1, info); db->Process(); } } Napi::Value Database::OpenGetter(const Napi::CallbackInfo& info) { Napi::Env env = this->Env(); Database* db = this; return Napi::Boolean::New(env, db->open); } Napi::Value Database::Close(const Napi::CallbackInfo& info) { Napi::Env env = info.Env(); Database* db = this; OPTIONAL_ARGUMENT_FUNCTION(0, callback); Baton* baton = new Baton(db, callback); db->Schedule(Work_BeginClose, baton, true); return info.This(); } void Database::Work_BeginClose(Baton* baton) { assert(baton->db->locked); assert(baton->db->open); assert(baton->db->_handle); assert(baton->db->pending == 0); baton->db->pending++; baton->db->RemoveCallbacks(); baton->db->closing = true; Napi::Env env = baton->db->Env(); int status = napi_create_async_work( env, NULL, Napi::String::New(env, "sqlite3.Database.Close"), Work_Close, Work_AfterClose, baton, &baton->request ); assert(status == 0); napi_queue_async_work(env, baton->request); } void Database::Work_Close(napi_env e, void* data) { Baton* baton = static_cast(data); Database* db = baton->db; baton->status = sqlite3_close(db->_handle); if (baton->status != SQLITE_OK) { baton->message = std::string(sqlite3_errmsg(db->_handle)); } else { db->_handle = NULL; } } void Database::Work_AfterClose(napi_env e, napi_status status, void* data) { std::unique_ptr baton(static_cast(data)); Database* db = baton->db; Napi::Env env = db->Env(); Napi::HandleScope scope(env); db->pending--; db->closing = false; Napi::Value argv[1]; if (baton->status != SQLITE_OK) { EXCEPTION(Napi::String::New(env, baton->message.c_str()), baton->status, exception); argv[0] = exception; } else { db->open = false; // Leave db->locked to indicate that this db object has reached // the end of its life. argv[0] = env.Null(); } Napi::Function cb = baton->callback.Value(); // Fire callbacks. if (!cb.IsUndefined() && cb.IsFunction()) { TRY_CATCH_CALL(db->Value(), cb, 1, argv); } else if (db->open) { Napi::Value info[] = { Napi::String::New(env, "error"), argv[0] }; EMIT_EVENT(db->Value(), 2, info); } if (!db->open) { Napi::Value info[] = { Napi::String::New(env, "close"), argv[0] }; EMIT_EVENT(db->Value(), 1, info); db->Process(); } } Napi::Value Database::Serialize(const Napi::CallbackInfo& info) { Napi::Env env = this->Env(); Database* db = this; OPTIONAL_ARGUMENT_FUNCTION(0, callback); bool before = db->serialize; db->serialize = true; if (!callback.IsEmpty() && callback.IsFunction()) { TRY_CATCH_CALL(info.This(), callback, 0, NULL, info.This()); db->serialize = before; } db->Process(); return info.This(); } Napi::Value Database::Parallelize(const Napi::CallbackInfo& info) { Napi::Env env = this->Env(); Database* db = this; OPTIONAL_ARGUMENT_FUNCTION(0, callback); bool before = db->serialize; db->serialize = false; if (!callback.IsEmpty() && callback.IsFunction()) { TRY_CATCH_CALL(info.This(), callback, 0, NULL, info.This()); db->serialize = before; } db->Process(); return info.This(); } Napi::Value Database::Configure(const Napi::CallbackInfo& info) { Napi::Env env = this->Env(); Database* db = this; REQUIRE_ARGUMENTS(2); if (info[0].StrictEquals( Napi::String::New(env, "trace"))) { Napi::Function handle; Baton* baton = new Baton(db, handle); db->Schedule(RegisterTraceCallback, baton); } else if (info[0].StrictEquals( Napi::String::New(env, "profile"))) { Napi::Function handle; Baton* baton = new Baton(db, handle); db->Schedule(RegisterProfileCallback, baton); } else if (info[0].StrictEquals( Napi::String::New(env, "busyTimeout"))) { if (!info[1].IsNumber()) { Napi::TypeError::New(env, "Value must be an integer").ThrowAsJavaScriptException(); return env.Null(); } Napi::Function handle; Baton* baton = new Baton(db, handle); baton->status = info[1].As().Int32Value(); db->Schedule(SetBusyTimeout, baton); } else { Napi::TypeError::New(env, (StringConcat( #if V8_MAJOR_VERSION > 6 info.GetIsolate(), #endif info[0].As(), Napi::String::New(env, " is not a valid configuration option") )).Utf8Value().c_str()).ThrowAsJavaScriptException(); return env.Null(); } db->Process(); return info.This(); } Napi::Value Database::Interrupt(const Napi::CallbackInfo& info) { Napi::Env env = this->Env(); Database* db = this; if (!db->open) { Napi::Error::New(env, "Database is not open").ThrowAsJavaScriptException(); return env.Null(); } if (db->closing) { Napi::Error::New(env, "Database is closing").ThrowAsJavaScriptException(); return env.Null(); } sqlite3_interrupt(db->_handle); return info.This(); } void Database::SetBusyTimeout(Baton* b) { std::unique_ptr baton(b); assert(baton->db->open); assert(baton->db->_handle); // Abuse the status field for passing the timeout. sqlite3_busy_timeout(baton->db->_handle, baton->status); } void Database::RegisterTraceCallback(Baton* b) { std::unique_ptr baton(b); assert(baton->db->open); assert(baton->db->_handle); Database* db = baton->db; if (db->debug_trace == NULL) { // Add it. db->debug_trace = new AsyncTrace(db, TraceCallback); sqlite3_trace(db->_handle, TraceCallback, db); } else { // Remove it. sqlite3_trace(db->_handle, NULL, NULL); db->debug_trace->finish(); db->debug_trace = NULL; } } void Database::TraceCallback(void* db, const char* sql) { // Note: This function is called in the thread pool. // Note: Some queries, such as "EXPLAIN" queries, are not sent through this. static_cast(db)->debug_trace->send(new std::string(sql)); } void Database::TraceCallback(Database* db, std::string* s) { std::unique_ptr sql(s); // Note: This function is called in the main V8 thread. Napi::Env env = db->Env(); Napi::HandleScope scope(env); Napi::Value argv[] = { Napi::String::New(env, "trace"), Napi::String::New(env, sql->c_str()) }; EMIT_EVENT(db->Value(), 2, argv); } void Database::RegisterProfileCallback(Baton* b) { std::unique_ptr baton(b); assert(baton->db->open); assert(baton->db->_handle); Database* db = baton->db; if (db->debug_profile == NULL) { // Add it. db->debug_profile = new AsyncProfile(db, ProfileCallback); sqlite3_profile(db->_handle, ProfileCallback, db); } else { // Remove it. sqlite3_profile(db->_handle, NULL, NULL); db->debug_profile->finish(); db->debug_profile = NULL; } } void Database::ProfileCallback(void* db, const char* sql, sqlite3_uint64 nsecs) { // Note: This function is called in the thread pool. // Note: Some queries, such as "EXPLAIN" queries, are not sent through this. ProfileInfo* info = new ProfileInfo(); info->sql = std::string(sql); info->nsecs = nsecs; static_cast(db)->debug_profile->send(info); } void Database::ProfileCallback(Database *db, ProfileInfo* i) { std::unique_ptr info(i); Napi::Env env = db->Env(); Napi::HandleScope scope(env); Napi::Value argv[] = { Napi::String::New(env, "profile"), Napi::String::New(env, info->sql.c_str()), Napi::Number::New(env, (double)info->nsecs / 1000000.0) }; EMIT_EVENT(db->Value(), 3, argv); } void Database::RegisterUpdateCallback(Baton* b) { std::unique_ptr baton(b); assert(baton->db->open); assert(baton->db->_handle); Database* db = baton->db; if (db->update_event == NULL) { // Add it. db->update_event = new AsyncUpdate(db, UpdateCallback); sqlite3_update_hook(db->_handle, UpdateCallback, db); } else { // Remove it. sqlite3_update_hook(db->_handle, NULL, NULL); db->update_event->finish(); db->update_event = NULL; } } void Database::UpdateCallback(void* db, int type, const char* database, const char* table, sqlite3_int64 rowid) { // Note: This function is called in the thread pool. // Note: Some queries, such as "EXPLAIN" queries, are not sent through this. UpdateInfo* info = new UpdateInfo(); info->type = type; info->database = std::string(database); info->table = std::string(table); info->rowid = rowid; static_cast(db)->update_event->send(info); } void Database::UpdateCallback(Database *db, UpdateInfo* i) { std::unique_ptr info(i); Napi::Env env = db->Env(); Napi::HandleScope scope(env); Napi::Value argv[] = { Napi::String::New(env, sqlite_authorizer_string(info->type)), Napi::String::New(env, info->database.c_str()), Napi::String::New(env, info->table.c_str()), Napi::Number::New(env, info->rowid), }; EMIT_EVENT(db->Value(), 4, argv); } Napi::Value Database::Exec(const Napi::CallbackInfo& info) { Napi::Env env = this->Env(); Database* db = this; REQUIRE_ARGUMENT_STRING(0, sql); OPTIONAL_ARGUMENT_FUNCTION(1, callback); Baton* baton = new ExecBaton(db, callback, sql.c_str()); db->Schedule(Work_BeginExec, baton, true); return info.This(); } void Database::Work_BeginExec(Baton* baton) { assert(baton->db->locked); assert(baton->db->open); assert(baton->db->_handle); assert(baton->db->pending == 0); baton->db->pending++; Napi::Env env = baton->db->Env(); //!! napi_status 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); int status = napi_create_async_work( env, NULL, //!! napi_value async_resource, Napi::String::New(env, "sqlite3.Database.Exec"), //!! napi_value async_resource_name, Work_Exec, //!! napi_async_execute_callback execute, Work_AfterExec, //!! napi_async_complete_callback complete, baton, //!! void* data, &baton->request //!! napi_async_work* result); ); assert(status == 0); napi_queue_async_work(env, baton->request); } void Database::Work_Exec(napi_env e, void* data) { ExecBaton* baton = static_cast(data); char* message = NULL; baton->status = sqlite3_exec( baton->db->_handle, baton->sql.c_str(), NULL, NULL, &message ); if (baton->status != SQLITE_OK && message != NULL) { baton->message = std::string(message); sqlite3_free(message); } } void Database::Work_AfterExec(napi_env e, napi_status status, void* data) { std::unique_ptr baton(static_cast(data)); Database* db = baton->db; db->pending--; Napi::Env env = db->Env(); Napi::HandleScope scope(env); Napi::Function cb = baton->callback.Value(); if (baton->status != SQLITE_OK) { EXCEPTION(Napi::String::New(env, baton->message.c_str()), baton->status, exception); if (!cb.IsUndefined() && cb.IsFunction()) { Napi::Value argv[] = { exception }; TRY_CATCH_CALL(db->Value(), cb, 1, argv); } else { Napi::Value info[] = { Napi::String::New(env, "error"), exception }; EMIT_EVENT(db->Value(), 2, info); } } else if (!cb.IsUndefined() && cb.IsFunction()) { Napi::Value argv[] = { env.Null() }; TRY_CATCH_CALL(db->Value(), cb, 1, argv); } db->Process(); } Napi::Value Database::Wait(const Napi::CallbackInfo& info) { Napi::Env env = info.Env(); Database* db = this; OPTIONAL_ARGUMENT_FUNCTION(0, callback); Baton* baton = new Baton(db, callback); db->Schedule(Work_Wait, baton, true); return info.This(); } void Database::Work_Wait(Baton* b) { std::unique_ptr baton(b); Napi::Env env = baton->db->Env(); Napi::HandleScope scope(env); assert(baton->db->locked); assert(baton->db->open); assert(baton->db->_handle); assert(baton->db->pending == 0); Napi::Function cb = baton->callback.Value(); if (!cb.IsUndefined() && cb.IsFunction()) { Napi::Value argv[] = { env.Null() }; TRY_CATCH_CALL(baton->db->Value(), cb, 1, argv); } baton->db->Process(); } Napi::Value Database::LoadExtension(const Napi::CallbackInfo& info) { Napi::Env env = this->Env(); Database* db = this; REQUIRE_ARGUMENT_STRING(0, filename); OPTIONAL_ARGUMENT_FUNCTION(1, callback); Baton* baton = new LoadExtensionBaton(db, callback, filename.c_str()); db->Schedule(Work_BeginLoadExtension, baton, true); return info.This(); } void Database::Work_BeginLoadExtension(Baton* baton) { assert(baton->db->locked); assert(baton->db->open); assert(baton->db->_handle); assert(baton->db->pending == 0); baton->db->pending++; Napi::Env env = baton->db->Env(); int status = napi_create_async_work( env, NULL, Napi::String::New(env, "sqlite3.Database.LoadExtension"), Work_LoadExtension, Work_AfterLoadExtension, baton, &baton->request ); assert(status == 0); napi_queue_async_work(env, baton->request); } void Database::Work_LoadExtension(napi_env e, void* data) { LoadExtensionBaton* baton = static_cast(data); sqlite3_enable_load_extension(baton->db->_handle, 1); char* message = NULL; baton->status = sqlite3_load_extension( baton->db->_handle, baton->filename.c_str(), 0, &message ); sqlite3_enable_load_extension(baton->db->_handle, 0); if (baton->status != SQLITE_OK && message != NULL) { baton->message = std::string(message); sqlite3_free(message); } } void Database::Work_AfterLoadExtension(napi_env e, napi_status status, void* data) { std::unique_ptr baton(static_cast(data)); Database* db = baton->db; db->pending--; Napi::Env env = db->Env(); Napi::HandleScope scope(env); Napi::Function cb = baton->callback.Value(); if (baton->status != SQLITE_OK) { EXCEPTION(Napi::String::New(env, baton->message.c_str()), baton->status, exception); if (!cb.IsUndefined() && cb.IsFunction()) { Napi::Value argv[] = { exception }; TRY_CATCH_CALL(db->Value(), cb, 1, argv); } else { Napi::Value info[] = { Napi::String::New(env, "error"), exception }; EMIT_EVENT(db->Value(), 2, info); } } else if (!cb.IsUndefined() && cb.IsFunction()) { Napi::Value argv[] = { env.Null() }; TRY_CATCH_CALL(db->Value(), cb, 1, argv); } db->Process(); } void Database::RemoveCallbacks() { if (debug_trace) { debug_trace->finish(); debug_trace = NULL; } if (debug_profile) { debug_profile->finish(); debug_profile = NULL; } } /* file https://github.com/mapbox/node-sqlite3/blob/v5.0.2/src/backup.cc */ // #include // #include // #include "macros.h" // #include "database.h" // #include "backup.h" using namespace node_sqlite3; Napi::Object Backup::Init(Napi::Env env, Napi::Object exports) { Napi::HandleScope scope(env); Napi::Function t = DefineClass(env, "Backup", { InstanceMethod("step", &Backup::Step), InstanceMethod("finish", &Backup::Finish), InstanceAccessor("idle", &Backup::IdleGetter, nullptr), InstanceAccessor("completed", &Backup::CompletedGetter, nullptr), InstanceAccessor("failed", &Backup::FailedGetter, nullptr), InstanceAccessor("remaining", &Backup::RemainingGetter, nullptr), InstanceAccessor("pageCount", &Backup::PageCountGetter, nullptr), InstanceAccessor("retryErrors", &Backup::RetryErrorGetter, &Backup::RetryErrorSetter), }); exports.Set("Backup", t); return exports; } void Backup::Process() { if (finished && !queue.empty()) { return CleanQueue(); } while (inited && !locked && !queue.empty()) { std::unique_ptr call(queue.front()); queue.pop(); call->callback(call->baton); } } void Backup::Schedule(Work_Callback callback, Baton* baton) { if (finished) { queue.push(new Call(callback, baton)); CleanQueue(); } else if (!inited || locked || !queue.empty()) { queue.push(new Call(callback, baton)); } else { callback(baton); } } template void Backup::Error(T* baton) { Napi::Env env = baton->backup->Env(); Napi::HandleScope scope(env); Backup* backup = baton->backup; // Fail hard on logic errors. assert(backup->status != 0); EXCEPTION(Napi::String::New(env, backup->message), backup->status, exception); Napi::Function cb = baton->callback.Value(); if (!cb.IsEmpty() && cb.IsFunction()) { Napi::Value argv[] = { exception }; TRY_CATCH_CALL(backup->Value(), cb, 1, argv); } else { Napi::Value argv[] = { Napi::String::New(env, "error"), exception }; EMIT_EVENT(backup->Value(), 2, argv); } } void Backup::CleanQueue() { Napi::Env env = this->Env(); Napi::HandleScope scope(env); if (inited && !queue.empty()) { // This backup has already been initialized and is now finished. // Fire error for all remaining items in the queue. EXCEPTION(Napi::String::New(env, "Backup is already finished"), SQLITE_MISUSE, exception); Napi::Value argv[] = { exception }; bool called = false; // Clear out the queue so that this object can get GC'ed. while (!queue.empty()) { std::unique_ptr call(queue.front()); queue.pop(); std::unique_ptr baton(call->baton); Napi::Function cb = baton->callback.Value(); if (inited && !cb.IsEmpty() && cb.IsFunction()) { TRY_CATCH_CALL(Value(), cb, 1, argv); called = true; } } // When we couldn't call a callback function, emit an error on the // Backup object. if (!called) { Napi::Value info[] = { Napi::String::New(env, "error"), exception }; EMIT_EVENT(Value(), 2, info); } } else while (!queue.empty()) { // Just delete all items in the queue; we already fired an event when // initializing the backup failed. std::unique_ptr call(queue.front()); queue.pop(); // We don't call the actual callback, so we have to make sure that // the baton gets destroyed. delete call->baton; } } Backup::Backup(const Napi::CallbackInfo& info) : Napi::ObjectWrap(info) { Napi::Env env = info.Env(); if (!info.IsConstructCall()) { Napi::TypeError::New(env, "Use the new operator to create new Backup objects").ThrowAsJavaScriptException(); return; } int length = info.Length(); if (length <= 0 || !Database::HasInstance(info[0])) { Napi::TypeError::New(env, "Database object expected").ThrowAsJavaScriptException(); return; } else if (length <= 1 || !info[1].IsString()) { Napi::TypeError::New(env, "Filename expected").ThrowAsJavaScriptException(); return; } else if (length <= 2 || !info[2].IsString()) { Napi::TypeError::New(env, "Source database name expected").ThrowAsJavaScriptException(); return; } else if (length <= 3 || !info[3].IsString()) { Napi::TypeError::New(env, "Destination database name expected").ThrowAsJavaScriptException(); return; } else if (length <= 4 || !info[4].IsBoolean()) { Napi::TypeError::New(env, "Direction flag expected").ThrowAsJavaScriptException(); return; } else if (length > 5 && !info[5].IsUndefined() && !info[5].IsFunction()) { Napi::TypeError::New(env, "Callback expected").ThrowAsJavaScriptException(); return; } Database* db = Napi::ObjectWrap::Unwrap(info[0].As()); Napi::String filename = info[1].As(); Napi::String sourceName = info[2].As(); Napi::String destName = info[3].As(); Napi::Boolean filenameIsDest = info[4].As(); info.This().As().DefineProperty(Napi::PropertyDescriptor::Value("filename", filename)); info.This().As().DefineProperty(Napi::PropertyDescriptor::Value("sourceName", sourceName)); info.This().As().DefineProperty(Napi::PropertyDescriptor::Value("destName", destName)); info.This().As().DefineProperty(Napi::PropertyDescriptor::Value("filenameIsDest", filenameIsDest)); init(db); InitializeBaton* baton = new InitializeBaton(db, info[5].As(), this); baton->filename = filename.Utf8Value(); baton->sourceName = sourceName.Utf8Value(); baton->destName = destName.Utf8Value(); baton->filenameIsDest = filenameIsDest.Value(); db->Schedule(Work_BeginInitialize, baton); } void Backup::Work_BeginInitialize(Database::Baton* baton) { assert(baton->db->open); baton->db->pending++; Napi::Env env = baton->db->Env(); int status = napi_create_async_work( env, NULL, Napi::String::New(env, "sqlite3.Backup.Initialize"), Work_Initialize, Work_AfterInitialize, baton, &baton->request ); assert(status == 0); napi_queue_async_work(env, baton->request); } void Backup::Work_Initialize(napi_env e, void* data) { BACKUP_INIT(InitializeBaton); // In case stepping fails, we use a mutex to make sure we get the associated // error message. sqlite3_mutex* mtx = sqlite3_db_mutex(baton->db->_handle); sqlite3_mutex_enter(mtx); backup->status = sqlite3_open(baton->filename.c_str(), &backup->_otherDb); if (backup->status == SQLITE_OK) { backup->_handle = sqlite3_backup_init( baton->filenameIsDest ? backup->_otherDb : backup->db->_handle, baton->destName.c_str(), baton->filenameIsDest ? backup->db->_handle : backup->_otherDb, baton->sourceName.c_str()); } backup->_destDb = baton->filenameIsDest ? backup->_otherDb : backup->db->_handle; if (backup->status != SQLITE_OK) { backup->message = std::string(sqlite3_errmsg(backup->_destDb)); sqlite3_close(backup->_otherDb); backup->_otherDb = NULL; backup->_destDb = NULL; } sqlite3_mutex_leave(mtx); } void Backup::Work_AfterInitialize(napi_env e, napi_status status, void* data) { std::unique_ptr baton(static_cast(data)); Backup* backup = baton->backup; Napi::Env env = backup->Env(); Napi::HandleScope scope(env); if (backup->status != SQLITE_OK) { Error(baton.get()); backup->FinishAll(); } else { backup->inited = true; Napi::Function cb = baton->callback.Value(); if (!cb.IsEmpty() && cb.IsFunction()) { Napi::Value argv[] = { env.Null() }; TRY_CATCH_CALL(backup->Value(), cb, 1, argv); } } BACKUP_END(); } Napi::Value Backup::Step(const Napi::CallbackInfo& info) { Backup* backup = this; Napi::Env env = backup->Env(); REQUIRE_ARGUMENT_INTEGER(0, pages); OPTIONAL_ARGUMENT_FUNCTION(1, callback); StepBaton* baton = new StepBaton(backup, callback, pages); backup->GetRetryErrors(baton->retryErrorsSet); backup->Schedule(Work_BeginStep, baton); return info.This(); } void Backup::Work_BeginStep(Baton* baton) { BACKUP_BEGIN(Step); } void Backup::Work_Step(napi_env e, void* data) { BACKUP_INIT(StepBaton); if (backup->_handle) { backup->status = sqlite3_backup_step(backup->_handle, baton->pages); backup->remaining = sqlite3_backup_remaining(backup->_handle); backup->pageCount = sqlite3_backup_pagecount(backup->_handle); } if (backup->status != SQLITE_OK) { // Text of message is a little awkward to get, since the error is not associated // with a db connection. #if SQLITE_VERSION_NUMBER >= 3007015 // sqlite3_errstr is a relatively new method backup->message = std::string(sqlite3_errstr(backup->status)); #else backup->message = "Sqlite error"; #endif if (baton->retryErrorsSet.size() > 0) { if (baton->retryErrorsSet.find(backup->status) == baton->retryErrorsSet.end()) { backup->FinishSqlite(); } } } } void Backup::Work_AfterStep(napi_env e, napi_status status, void* data) { std::unique_ptr baton(static_cast(data)); Backup* backup = baton->backup; Napi::Env env = backup->Env(); Napi::HandleScope scope(env); if (backup->status == SQLITE_DONE) { backup->completed = true; } else if (!backup->_handle) { backup->failed = true; } if (backup->status != SQLITE_OK && backup->status != SQLITE_DONE) { Error(baton.get()); } else { // Fire callbacks. Napi::Function cb = baton->callback.Value(); if (!cb.IsEmpty() && cb.IsFunction()) { Napi::Value argv[] = { env.Null(), Napi::Boolean::New(env, backup->status == SQLITE_DONE) }; TRY_CATCH_CALL(backup->Value(), cb, 2, argv); } } BACKUP_END(); } Napi::Value Backup::Finish(const Napi::CallbackInfo& info) { Backup* backup = this; Napi::Env env = backup->Env(); OPTIONAL_ARGUMENT_FUNCTION(0, callback); Baton* baton = new Baton(backup, callback); backup->Schedule(Work_BeginFinish, baton); return info.This(); } void Backup::Work_BeginFinish(Baton* baton) { BACKUP_BEGIN(Finish); } void Backup::Work_Finish(napi_env e, void* data) { BACKUP_INIT(Baton); backup->FinishSqlite(); } void Backup::Work_AfterFinish(napi_env e, napi_status status, void* data) { std::unique_ptr baton(static_cast(data)); Backup* backup = baton->backup; Napi::Env env = backup->Env(); Napi::HandleScope scope(env); backup->FinishAll(); // Fire callback in case there was one. Napi::Function cb = baton->callback.Value(); if (!cb.IsEmpty() && cb.IsFunction()) { TRY_CATCH_CALL(backup->Value(), cb, 0, NULL); } BACKUP_END(); } void Backup::FinishAll() { assert(!finished); if (!completed && !failed) { failed = true; } finished = true; CleanQueue(); FinishSqlite(); db->Unref(); } void Backup::FinishSqlite() { if (_handle) { sqlite3_backup_finish(_handle); _handle = NULL; } if (_otherDb) { sqlite3_close(_otherDb); _otherDb = NULL; } _destDb = NULL; } Napi::Value Backup::IdleGetter(const Napi::CallbackInfo& info) { Backup* backup = this; bool idle = backup->inited && !backup->locked && backup->queue.empty(); return Napi::Boolean::New(this->Env(), idle); } Napi::Value Backup::CompletedGetter(const Napi::CallbackInfo& info) { Backup* backup = this; return Napi::Boolean::New(this->Env(), backup->completed); } Napi::Value Backup::FailedGetter(const Napi::CallbackInfo& info) { Backup* backup = this; return Napi::Boolean::New(this->Env(), backup->failed); } Napi::Value Backup::RemainingGetter(const Napi::CallbackInfo& info) { Backup* backup = this; return Napi::Number::New(this->Env(), backup->remaining); } Napi::Value Backup::PageCountGetter(const Napi::CallbackInfo& info) { Backup* backup = this; return Napi::Number::New(this->Env(), backup->pageCount); } Napi::Value Backup::RetryErrorGetter(const Napi::CallbackInfo& info) { Backup* backup = this; return backup->retryErrors.Value(); } void Backup::RetryErrorSetter(const Napi::CallbackInfo& info, const Napi::Value& value) { Backup* backup = this; Napi::Env env = backup->Env(); if (!value.IsArray()) { Napi::Error::New(env, "retryErrors must be an array").ThrowAsJavaScriptException(); return; } Napi::Array array = value.As(); backup->retryErrors.Reset(array, 1); } void Backup::GetRetryErrors(std::set& retryErrorsSet) { retryErrorsSet.clear(); Napi::Array array = retryErrors.Value(); int length = array.Length(); for (int i = 0; i < length; i++) { Napi::Value code = (array).Get(i); if (code.IsNumber()) { retryErrorsSet.insert(code.As().Int32Value()); } } } /* file https://github.com/mapbox/node-sqlite3/blob/v5.0.2/src/statement.cc */ // #include // #include // #include // #include "macros.h" // #include "database.h" // #include "statement.h" using namespace node_sqlite3; Napi::Object Statement::Init(Napi::Env env, Napi::Object exports) { Napi::HandleScope scope(env); Napi::Function t = DefineClass(env, "Statement", { InstanceMethod("bind", &Statement::Bind), InstanceMethod("get", &Statement::Get), InstanceMethod("run", &Statement::Run), InstanceMethod("all", &Statement::All), InstanceMethod("each", &Statement::Each), InstanceMethod("reset", &Statement::Reset), InstanceMethod("finalize", &Statement::Finalize_), }); exports.Set("Statement", t); return exports; } // A Napi InstanceOf for Javascript Objects "Date" and "RegExp" bool OtherInstanceOf(Napi::Object source, const char* object_type) { if (strncmp(object_type, "Date", 4) == 0) { return source.InstanceOf(source.Env().Global().Get("Date").As()); } else if (strncmp(object_type, "RegExp", 6) == 0) { return source.InstanceOf(source.Env().Global().Get("RegExp").As()); } return false; } void Statement::Process() { if (finalized && !queue.empty()) { return CleanQueue(); } while (prepared && !locked && !queue.empty()) { std::unique_ptr call(queue.front()); queue.pop(); call->callback(call->baton); } } void Statement::Schedule(Work_Callback callback, Baton* baton) { if (finalized) { queue.push(new Call(callback, baton)); CleanQueue(); } else if (!prepared || locked) { queue.push(new Call(callback, baton)); } else { callback(baton); } } template void Statement::Error(T* baton) { Statement* stmt = baton->stmt; Napi::Env env = stmt->Env(); Napi::HandleScope scope(env); // Fail hard on logic errors. assert(stmt->status != 0); EXCEPTION(Napi::String::New(env, stmt->message.c_str()), stmt->status, exception); Napi::Function cb = baton->callback.Value(); if (!cb.IsUndefined() && cb.IsFunction()) { Napi::Value argv[] = { exception }; TRY_CATCH_CALL(stmt->Value(), cb, 1, argv); } else { Napi::Value argv[] = { Napi::String::New(env, "error"), exception }; EMIT_EVENT(stmt->Value(), 2, argv); } } // { Database db, String sql, Array params, Function callback } Statement::Statement(const Napi::CallbackInfo& info) : Napi::ObjectWrap(info) { Napi::Env env = info.Env(); int length = info.Length(); if (length <= 0 || !Database::HasInstance(info[0])) { Napi::TypeError::New(env, "Database object expected").ThrowAsJavaScriptException(); return; } else if (length <= 1 || !info[1].IsString()) { Napi::TypeError::New(env, "SQL query expected").ThrowAsJavaScriptException(); return; } else if (length > 2 && !info[2].IsUndefined() && !info[2].IsFunction()) { Napi::TypeError::New(env, "Callback expected").ThrowAsJavaScriptException(); return; } Database* db = Napi::ObjectWrap::Unwrap(info[0].As()); Napi::String sql = info[1].As(); info.This().As().DefineProperty(Napi::PropertyDescriptor::Value("sql", sql, napi_default)); init(db); Statement* stmt = this; PrepareBaton* baton = new PrepareBaton(db, info[2].As(), stmt); baton->sql = std::string(sql.As().Utf8Value().c_str()); db->Schedule(Work_BeginPrepare, baton); } void Statement::Work_BeginPrepare(Database::Baton* baton) { assert(baton->db->open); baton->db->pending++; Napi::Env env = baton->db->Env(); int status = napi_create_async_work( env, NULL, Napi::String::New(env, "sqlite3.Statement.Prepare"), Work_Prepare, Work_AfterPrepare, baton, &baton->request ); assert(status == 0); napi_queue_async_work(env, baton->request); } void Statement::Work_Prepare(napi_env e, void* data) { STATEMENT_INIT(PrepareBaton); // In case preparing fails, we use a mutex to make sure we get the associated // error message. STATEMENT_MUTEX(mtx); sqlite3_mutex_enter(mtx); stmt->status = sqlite3_prepare_v2( baton->db->_handle, baton->sql.c_str(), baton->sql.size(), &stmt->_handle, NULL ); if (stmt->status != SQLITE_OK) { stmt->message = std::string(sqlite3_errmsg(baton->db->_handle)); stmt->_handle = NULL; } sqlite3_mutex_leave(mtx); } void Statement::Work_AfterPrepare(napi_env e, napi_status status, void* data) { std::unique_ptr baton(static_cast(data)); Statement* stmt = baton->stmt; Napi::Env env = stmt->Env(); Napi::HandleScope scope(env); if (stmt->status != SQLITE_OK) { Error(baton.get()); stmt->Finalize_(); } else { stmt->prepared = true; if (!baton->callback.IsEmpty() && baton->callback.Value().IsFunction()) { Napi::Function cb = baton->callback.Value(); Napi::Value argv[] = { env.Null() }; TRY_CATCH_CALL(stmt->Value(), cb, 1, argv); } } STATEMENT_END(); } template Values::Field* Statement::BindParameter(const Napi::Value source, T pos) { if (source.IsString()) { std::string val = source.As().Utf8Value(); return new Values::Text(pos, val.length(), val.c_str()); } else if (OtherInstanceOf(source.As(), "RegExp")) { std::string val = source.ToString().Utf8Value(); return new Values::Text(pos, val.length(), val.c_str()); } else if (source.IsNumber()) { if (OtherIsInt(source.As())) { return new Values::Integer(pos, source.As().Int32Value()); } else { return new Values::Float(pos, source.As().DoubleValue()); } } else if (source.IsBoolean()) { return new Values::Integer(pos, source.As().Value() ? 1 : 0); } else if (source.IsNull()) { return new Values::Null(pos); } else if (source.IsBuffer()) { Napi::Buffer buffer = source.As>(); return new Values::Blob(pos, buffer.Length(), buffer.Data()); } else if (OtherInstanceOf(source.As(), "Date")) { return new Values::Float(pos, source.ToNumber().DoubleValue()); } else if (source.IsObject()) { std::string val = source.ToString().Utf8Value(); return new Values::Text(pos, val.length(), val.c_str()); } else { return NULL; } } template T* Statement::Bind(const Napi::CallbackInfo& info, int start, int last) { Napi::Env env = info.Env(); Napi::HandleScope scope(env); if (last < 0) last = info.Length(); Napi::Function callback; if (last > start && info[last - 1].IsFunction()) { callback = info[last - 1].As(); last--; } T* baton = new T(this, callback); if (start < last) { if (info[start].IsArray()) { Napi::Array array = info[start].As(); int length = array.Length(); // Note: bind parameters start with 1. for (int i = 0, pos = 1; i < length; i++, pos++) { baton->parameters.push_back(BindParameter((array).Get(i), pos)); } } else if (!info[start].IsObject() || OtherInstanceOf(info[start].As(), "RegExp") || OtherInstanceOf(info[start].As(), "Date") || info[start].IsBuffer()) { // Parameters directly in array. // Note: bind parameters start with 1. for (int i = start, pos = 1; i < last; i++, pos++) { baton->parameters.push_back(BindParameter(info[i], pos)); } } else if (info[start].IsObject()) { Napi::Object object = info[start].As(); Napi::Array array = object.GetPropertyNames(); int length = array.Length(); for (int i = 0; i < length; i++) { Napi::Value name = (array).Get(i); Napi::Number num = name.ToNumber(); if (num.Int32Value() == num.DoubleValue()) { baton->parameters.push_back( BindParameter((object).Get(name), num.Int32Value())); } else { baton->parameters.push_back(BindParameter((object).Get(name), name.As().Utf8Value().c_str())); } } } else { return NULL; } } return baton; } bool Statement::Bind(const Parameters & parameters) { if (parameters.size() == 0) { return true; } sqlite3_reset(_handle); sqlite3_clear_bindings(_handle); Parameters::const_iterator it = parameters.begin(); Parameters::const_iterator end = parameters.end(); for (; it < end; ++it) { Values::Field* field = *it; if (field != NULL) { unsigned int pos; if (field->index > 0) { pos = field->index; } else { pos = sqlite3_bind_parameter_index(_handle, field->name.c_str()); } switch (field->type) { case SQLITE_INTEGER: { status = sqlite3_bind_int(_handle, pos, ((Values::Integer*)field)->value); } break; case SQLITE_FLOAT: { status = sqlite3_bind_double(_handle, pos, ((Values::Float*)field)->value); } break; case SQLITE_TEXT: { status = sqlite3_bind_text(_handle, pos, ((Values::Text*)field)->value.c_str(), ((Values::Text*)field)->value.size(), SQLITE_TRANSIENT); } break; case SQLITE_BLOB: { status = sqlite3_bind_blob(_handle, pos, ((Values::Blob*)field)->value, ((Values::Blob*)field)->length, SQLITE_TRANSIENT); } break; case SQLITE_NULL: { status = sqlite3_bind_null(_handle, pos); } break; } if (status != SQLITE_OK) { message = std::string(sqlite3_errmsg(db->_handle)); return false; } } } return true; } Napi::Value Statement::Bind(const Napi::CallbackInfo& info) { Napi::Env env = info.Env(); Statement* stmt = this; Baton* baton = stmt->Bind(info); if (baton == NULL) { Napi::TypeError::New(env, "Data type is not supported").ThrowAsJavaScriptException(); return env.Null(); } else { stmt->Schedule(Work_BeginBind, baton); return info.This(); } } void Statement::Work_BeginBind(Baton* baton) { STATEMENT_BEGIN(Bind); } void Statement::Work_Bind(napi_env e, void* data) { STATEMENT_INIT(Baton); STATEMENT_MUTEX(mtx); sqlite3_mutex_enter(mtx); stmt->Bind(baton->parameters); sqlite3_mutex_leave(mtx); } void Statement::Work_AfterBind(napi_env e, napi_status status, void* data) { std::unique_ptr baton(static_cast(data)); Statement* stmt = baton->stmt; Napi::Env env = stmt->Env(); Napi::HandleScope scope(env); if (stmt->status != SQLITE_OK) { Error(baton.get()); } else { // Fire callbacks. Napi::Function cb = baton->callback.Value(); if (!cb.IsUndefined() && cb.IsFunction()) { Napi::Value argv[] = { env.Null() }; TRY_CATCH_CALL(stmt->Value(), cb, 1, argv); } } STATEMENT_END(); } Napi::Value Statement::Get(const Napi::CallbackInfo& info) { Napi::Env env = info.Env(); Statement* stmt = this; Baton* baton = stmt->Bind(info); if (baton == NULL) { Napi::Error::New(env, "Data type is not supported").ThrowAsJavaScriptException(); return env.Null(); } else { stmt->Schedule(Work_BeginGet, baton); return info.This(); } } void Statement::Work_BeginGet(Baton* baton) { STATEMENT_BEGIN(Get); } void Statement::Work_Get(napi_env e, void* data) { STATEMENT_INIT(RowBaton); if (stmt->status != SQLITE_DONE || baton->parameters.size()) { STATEMENT_MUTEX(mtx); sqlite3_mutex_enter(mtx); if (stmt->Bind(baton->parameters)) { stmt->status = sqlite3_step(stmt->_handle); if (!(stmt->status == SQLITE_ROW || stmt->status == SQLITE_DONE)) { stmt->message = std::string(sqlite3_errmsg(stmt->db->_handle)); } } sqlite3_mutex_leave(mtx); if (stmt->status == SQLITE_ROW) { // Acquire one result row before returning. GetRow(&baton->row, stmt->_handle); } } } void Statement::Work_AfterGet(napi_env e, napi_status status, void* data) { std::unique_ptr baton(static_cast(data)); Statement* stmt = baton->stmt; Napi::Env env = stmt->Env(); Napi::HandleScope scope(env); if (stmt->status != SQLITE_ROW && stmt->status != SQLITE_DONE) { Error(baton.get()); } else { // Fire callbacks. Napi::Function cb = baton->callback.Value(); if (!cb.IsUndefined() && cb.IsFunction()) { if (stmt->status == SQLITE_ROW) { // Create the result array from the data we acquired. Napi::Value argv[] = { env.Null(), RowToJS(env, &baton->row) }; TRY_CATCH_CALL(stmt->Value(), cb, 2, argv); } else { Napi::Value argv[] = { env.Null() }; TRY_CATCH_CALL(stmt->Value(), cb, 1, argv); } } } STATEMENT_END(); } Napi::Value Statement::Run(const Napi::CallbackInfo& info) { Napi::Env env = info.Env(); Statement* stmt = this; Baton* baton = stmt->Bind(info); if (baton == NULL) { Napi::Error::New(env, "Data type is not supported").ThrowAsJavaScriptException(); return env.Null(); } else { stmt->Schedule(Work_BeginRun, baton); return info.This(); } } void Statement::Work_BeginRun(Baton* baton) { STATEMENT_BEGIN(Run); } void Statement::Work_Run(napi_env e, void* data) { STATEMENT_INIT(RunBaton); STATEMENT_MUTEX(mtx); sqlite3_mutex_enter(mtx); // Make sure that we also reset when there are no parameters. if (!baton->parameters.size()) { sqlite3_reset(stmt->_handle); } if (stmt->Bind(baton->parameters)) { stmt->status = sqlite3_step(stmt->_handle); if (!(stmt->status == SQLITE_ROW || stmt->status == SQLITE_DONE)) { stmt->message = std::string(sqlite3_errmsg(stmt->db->_handle)); } else { baton->inserted_id = sqlite3_last_insert_rowid(stmt->db->_handle); baton->changes = sqlite3_changes(stmt->db->_handle); } } sqlite3_mutex_leave(mtx); } void Statement::Work_AfterRun(napi_env e, napi_status status, void* data) { std::unique_ptr baton(static_cast(data)); Statement* stmt = baton->stmt; Napi::Env env = stmt->Env(); Napi::HandleScope scope(env); if (stmt->status != SQLITE_ROW && stmt->status != SQLITE_DONE) { Error(baton.get()); } else { // Fire callbacks. Napi::Function cb = baton->callback.Value(); if (!cb.IsUndefined() && cb.IsFunction()) { (stmt->Value()).Set(Napi::String::New(env, "lastID"), Napi::Number::New(env, baton->inserted_id)); (stmt->Value()).Set( Napi::String::New(env, "changes"), Napi::Number::New(env, baton->changes)); Napi::Value argv[] = { env.Null() }; TRY_CATCH_CALL(stmt->Value(), cb, 1, argv); } } STATEMENT_END(); } Napi::Value Statement::All(const Napi::CallbackInfo& info) { Napi::Env env = info.Env(); Statement* stmt = this; Baton* baton = stmt->Bind(info); if (baton == NULL) { Napi::Error::New(env, "Data type is not supported").ThrowAsJavaScriptException(); return env.Null(); } else { stmt->Schedule(Work_BeginAll, baton); return info.This(); } } void Statement::Work_BeginAll(Baton* baton) { STATEMENT_BEGIN(All); } void Statement::Work_All(napi_env e, void* data) { STATEMENT_INIT(RowsBaton); STATEMENT_MUTEX(mtx); sqlite3_mutex_enter(mtx); // Make sure that we also reset when there are no parameters. if (!baton->parameters.size()) { sqlite3_reset(stmt->_handle); } if (stmt->Bind(baton->parameters)) { while ((stmt->status = sqlite3_step(stmt->_handle)) == SQLITE_ROW) { Row* row = new Row(); GetRow(row, stmt->_handle); baton->rows.push_back(row); } if (stmt->status != SQLITE_DONE) { stmt->message = std::string(sqlite3_errmsg(stmt->db->_handle)); } } sqlite3_mutex_leave(mtx); } void Statement::Work_AfterAll(napi_env e, napi_status status, void* data) { std::unique_ptr baton(static_cast(data)); Statement* stmt = baton->stmt; Napi::Env env = stmt->Env(); Napi::HandleScope scope(env); if (stmt->status != SQLITE_DONE) { Error(baton.get()); } else { // Fire callbacks. Napi::Function cb = baton->callback.Value(); if (!cb.IsUndefined() && cb.IsFunction()) { if (baton->rows.size()) { // Create the result array from the data we acquired. Napi::Array result(Napi::Array::New(env, baton->rows.size())); Rows::const_iterator it = baton->rows.begin(); Rows::const_iterator end = baton->rows.end(); for (int i = 0; it < end; ++it, i++) { std::unique_ptr row(*it); (result).Set(i, RowToJS(env,row.get())); } Napi::Value argv[] = { env.Null(), result }; TRY_CATCH_CALL(stmt->Value(), cb, 2, argv); } else { // There were no result rows. Napi::Value argv[] = { env.Null(), Napi::Array::New(env, 0) }; TRY_CATCH_CALL(stmt->Value(), cb, 2, argv); } } } STATEMENT_END(); } Napi::Value Statement::Each(const Napi::CallbackInfo& info) { Napi::Env env = info.Env(); Statement* stmt = this; int last = info.Length(); Napi::Function completed; if (last >= 2 && info[last - 1].IsFunction() && info[last - 2].IsFunction()) { completed = info[--last].As(); } EachBaton* baton = stmt->Bind(info, 0, last); if (baton == NULL) { Napi::Error::New(env, "Data type is not supported").ThrowAsJavaScriptException(); return env.Null(); } else { baton->completed.Reset(completed, 1); stmt->Schedule(Work_BeginEach, baton); return info.This(); } } void Statement::Work_BeginEach(Baton* baton) { // Only create the Async object when we're actually going into // the event loop. This prevents dangling events. EachBaton* each_baton = static_cast(baton); each_baton->async = new Async(each_baton->stmt, reinterpret_cast(AsyncEach)); each_baton->async->item_cb.Reset(each_baton->callback.Value(), 1); each_baton->async->completed_cb.Reset(each_baton->completed.Value(), 1); STATEMENT_BEGIN(Each); } void Statement::Work_Each(napi_env e, void* data) { STATEMENT_INIT(EachBaton); Async* async = baton->async; STATEMENT_MUTEX(mtx); int retrieved = 0; // Make sure that we also reset when there are no parameters. if (!baton->parameters.size()) { sqlite3_reset(stmt->_handle); } if (stmt->Bind(baton->parameters)) { while (true) { sqlite3_mutex_enter(mtx); stmt->status = sqlite3_step(stmt->_handle); if (stmt->status == SQLITE_ROW) { sqlite3_mutex_leave(mtx); Row* row = new Row(); GetRow(row, stmt->_handle); NODE_SQLITE3_MUTEX_LOCK(&async->mutex) async->data.push_back(row); retrieved++; NODE_SQLITE3_MUTEX_UNLOCK(&async->mutex) uv_async_send(&async->watcher); } else { if (stmt->status != SQLITE_DONE) { stmt->message = std::string(sqlite3_errmsg(stmt->db->_handle)); } sqlite3_mutex_leave(mtx); break; } } } async->completed = true; uv_async_send(&async->watcher); } void Statement::CloseCallback(uv_handle_t* handle) { assert(handle != NULL); assert(handle->data != NULL); Async* async = static_cast(handle->data); delete async; } void Statement::AsyncEach(uv_async_t* handle) { Async* async = static_cast(handle->data); Napi::Env env = async->stmt->Env(); Napi::HandleScope scope(env); while (true) { // Get the contents out of the data cache for us to process in the JS callback. Rows rows; NODE_SQLITE3_MUTEX_LOCK(&async->mutex) rows.swap(async->data); NODE_SQLITE3_MUTEX_UNLOCK(&async->mutex) if (rows.empty()) { break; } Napi::Function cb = async->item_cb.Value(); if (!cb.IsUndefined() && cb.IsFunction()) { Napi::Value argv[2]; argv[0] = env.Null(); Rows::const_iterator it = rows.begin(); Rows::const_iterator end = rows.end(); for (int i = 0; it < end; ++it, i++) { std::unique_ptr row(*it); argv[1] = RowToJS(env,row.get()); async->retrieved++; TRY_CATCH_CALL(async->stmt->Value(), cb, 2, argv); } } } Napi::Function cb = async->completed_cb.Value(); if (async->completed) { if (!cb.IsEmpty() && cb.IsFunction()) { Napi::Value argv[] = { env.Null(), Napi::Number::New(env, async->retrieved) }; TRY_CATCH_CALL(async->stmt->Value(), cb, 2, argv); } uv_close(reinterpret_cast(handle), CloseCallback); } } void Statement::Work_AfterEach(napi_env e, napi_status status, void* data) { std::unique_ptr baton(static_cast(data)); Statement* stmt = baton->stmt; Napi::Env env = stmt->Env(); Napi::HandleScope scope(env); if (stmt->status != SQLITE_DONE) { Error(baton.get()); } STATEMENT_END(); } Napi::Value Statement::Reset(const Napi::CallbackInfo& info) { Napi::Env env = info.Env(); Statement* stmt = this; OPTIONAL_ARGUMENT_FUNCTION(0, callback); Baton* baton = new Baton(stmt, callback); stmt->Schedule(Work_BeginReset, baton); return info.This(); } void Statement::Work_BeginReset(Baton* baton) { STATEMENT_BEGIN(Reset); } void Statement::Work_Reset(napi_env e, void* data) { STATEMENT_INIT(Baton); sqlite3_reset(stmt->_handle); stmt->status = SQLITE_OK; } void Statement::Work_AfterReset(napi_env e, napi_status status, void* data) { std::unique_ptr baton(static_cast(data)); Statement* stmt = baton->stmt; Napi::Env env = stmt->Env(); Napi::HandleScope scope(env); // Fire callbacks. Napi::Function cb = baton->callback.Value(); if (!cb.IsUndefined() && cb.IsFunction()) { Napi::Value argv[] = { env.Null() }; TRY_CATCH_CALL(stmt->Value(), cb, 1, argv); } STATEMENT_END(); } Napi::Value Statement::RowToJS(Napi::Env env, Row* row) { Napi::EscapableHandleScope scope(env); Napi::Object result = Napi::Object::New(env); Row::const_iterator it = row->begin(); Row::const_iterator end = row->end(); for (int i = 0; it < end; ++it, i++) { Values::Field* field = *it; Napi::Value value; switch (field->type) { case SQLITE_INTEGER: { value = Napi::Number::New(env, ((Values::Integer*)field)->value); } break; case SQLITE_FLOAT: { value = Napi::Number::New(env, ((Values::Float*)field)->value); } break; case SQLITE_TEXT: { value = Napi::String::New(env, ((Values::Text*)field)->value.c_str(), ((Values::Text*)field)->value.size()); } break; case SQLITE_BLOB: { value = Napi::Buffer::Copy(env, ((Values::Blob*)field)->value, ((Values::Blob*)field)->length); } break; case SQLITE_NULL: { value = env.Null(); } break; } (result).Set(Napi::String::New(env, field->name.c_str()), value); DELETE_FIELD(field); } return scope.Escape(result); } void Statement::GetRow(Row* row, sqlite3_stmt* stmt) { int rows = sqlite3_column_count(stmt); for (int i = 0; i < rows; i++) { int type = sqlite3_column_type(stmt, i); const char* name = sqlite3_column_name(stmt, i); switch (type) { case SQLITE_INTEGER: { row->push_back(new Values::Integer(name, sqlite3_column_int64(stmt, i))); } break; case SQLITE_FLOAT: { row->push_back(new Values::Float(name, sqlite3_column_double(stmt, i))); } break; case SQLITE_TEXT: { const char* text = (const char*)sqlite3_column_text(stmt, i); int length = sqlite3_column_bytes(stmt, i); row->push_back(new Values::Text(name, length, text)); } break; case SQLITE_BLOB: { const void* blob = sqlite3_column_blob(stmt, i); int length = sqlite3_column_bytes(stmt, i); row->push_back(new Values::Blob(name, length, blob)); } break; case SQLITE_NULL: { row->push_back(new Values::Null(name)); } break; default: assert(false); } } } Napi::Value Statement::Finalize_(const Napi::CallbackInfo& info) { Napi::Env env = info.Env(); Statement* stmt = this; OPTIONAL_ARGUMENT_FUNCTION(0, callback); Baton* baton = new Baton(stmt, callback); stmt->Schedule(Finalize_, baton); return stmt->db->Value(); } void Statement::Finalize_(Baton* b) { std::unique_ptr baton(b); Napi::Env env = baton->stmt->Env(); Napi::HandleScope scope(env); baton->stmt->Finalize_(); // Fire callback in case there was one. Napi::Function cb = baton->callback.Value(); if (!cb.IsUndefined() && cb.IsFunction()) { TRY_CATCH_CALL(baton->stmt->Value(), cb, 0, NULL); } } void Statement::Finalize_() { assert(!finalized); finalized = true; CleanQueue(); // Finalize returns the status code of the last operation. We already fired // error events in case those failed. sqlite3_finalize(_handle); _handle = NULL; db->Unref(); } void Statement::CleanQueue() { Napi::Env env = this->Env(); Napi::HandleScope scope(env); if (prepared && !queue.empty()) { // This statement has already been prepared and is now finalized. // Fire error for all remaining items in the queue. EXCEPTION(Napi::String::New(env, "Statement is already finalized"), SQLITE_MISUSE, exception); Napi::Value argv[] = { exception }; bool called = false; // Clear out the queue so that this object can get GC'ed. while (!queue.empty()) { std::unique_ptr call(queue.front()); queue.pop(); std::unique_ptr baton(call->baton); Napi::Function cb = baton->callback.Value(); if (prepared && !cb.IsEmpty() && cb.IsFunction()) { TRY_CATCH_CALL(Value(), cb, 1, argv); called = true; } } // When we couldn't call a callback function, emit an error on the // Statement object. if (!called) { Napi::Value info[] = { Napi::String::New(env, "error"), exception }; EMIT_EVENT(Value(), 2, info); } } else while (!queue.empty()) { // Just delete all items in the queue; we already fired an event when // preparing the statement failed. std::unique_ptr call(queue.front()); queue.pop(); // We don't call the actual callback, so we have to make sure that // the baton gets destroyed. delete call->baton; } } /* file https://github.com/mapbox/node-sqlite3/blob/v5.0.2/src/node_sqlite3.cc */ // #include // #include // #include // #include // #include // #include "macros.h" // #include "database.h" // #include "statement.h" // #include "backup.h" using namespace node_sqlite3; namespace { Napi::Object RegisterModule(Napi::Env env, Napi::Object exports) { Napi::HandleScope scope(env); Database::Init(env, exports); Statement::Init(env, exports); Backup::Init(env, exports); exports.DefineProperties({ DEFINE_CONSTANT_INTEGER(exports, SQLITE_OPEN_READONLY, OPEN_READONLY) DEFINE_CONSTANT_INTEGER(exports, SQLITE_OPEN_READWRITE, OPEN_READWRITE) DEFINE_CONSTANT_INTEGER(exports, SQLITE_OPEN_CREATE, OPEN_CREATE) DEFINE_CONSTANT_INTEGER(exports, SQLITE_OPEN_FULLMUTEX, OPEN_FULLMUTEX) DEFINE_CONSTANT_INTEGER(exports, SQLITE_OPEN_URI, OPEN_URI) DEFINE_CONSTANT_INTEGER(exports, SQLITE_OPEN_SHAREDCACHE, OPEN_SHAREDCACHE) DEFINE_CONSTANT_INTEGER(exports, SQLITE_OPEN_PRIVATECACHE, OPEN_PRIVATECACHE) DEFINE_CONSTANT_STRING(exports, SQLITE_VERSION, VERSION) #ifdef SQLITE_SOURCE_ID DEFINE_CONSTANT_STRING(exports, SQLITE_SOURCE_ID, SOURCE_ID) #endif DEFINE_CONSTANT_INTEGER(exports, SQLITE_VERSION_NUMBER, VERSION_NUMBER) DEFINE_CONSTANT_INTEGER(exports, SQLITE_OK, OK) DEFINE_CONSTANT_INTEGER(exports, SQLITE_ERROR, ERROR) DEFINE_CONSTANT_INTEGER(exports, SQLITE_INTERNAL, INTERNAL) DEFINE_CONSTANT_INTEGER(exports, SQLITE_PERM, PERM) DEFINE_CONSTANT_INTEGER(exports, SQLITE_ABORT, ABORT) DEFINE_CONSTANT_INTEGER(exports, SQLITE_BUSY, BUSY) DEFINE_CONSTANT_INTEGER(exports, SQLITE_LOCKED, LOCKED) DEFINE_CONSTANT_INTEGER(exports, SQLITE_NOMEM, NOMEM) DEFINE_CONSTANT_INTEGER(exports, SQLITE_READONLY, READONLY) DEFINE_CONSTANT_INTEGER(exports, SQLITE_INTERRUPT, INTERRUPT) DEFINE_CONSTANT_INTEGER(exports, SQLITE_IOERR, IOERR) DEFINE_CONSTANT_INTEGER(exports, SQLITE_CORRUPT, CORRUPT) DEFINE_CONSTANT_INTEGER(exports, SQLITE_NOTFOUND, NOTFOUND) DEFINE_CONSTANT_INTEGER(exports, SQLITE_FULL, FULL) DEFINE_CONSTANT_INTEGER(exports, SQLITE_CANTOPEN, CANTOPEN) DEFINE_CONSTANT_INTEGER(exports, SQLITE_PROTOCOL, PROTOCOL) DEFINE_CONSTANT_INTEGER(exports, SQLITE_EMPTY, EMPTY) DEFINE_CONSTANT_INTEGER(exports, SQLITE_SCHEMA, SCHEMA) DEFINE_CONSTANT_INTEGER(exports, SQLITE_TOOBIG, TOOBIG) DEFINE_CONSTANT_INTEGER(exports, SQLITE_CONSTRAINT, CONSTRAINT) DEFINE_CONSTANT_INTEGER(exports, SQLITE_MISMATCH, MISMATCH) DEFINE_CONSTANT_INTEGER(exports, SQLITE_MISUSE, MISUSE) DEFINE_CONSTANT_INTEGER(exports, SQLITE_NOLFS, NOLFS) DEFINE_CONSTANT_INTEGER(exports, SQLITE_AUTH, AUTH) DEFINE_CONSTANT_INTEGER(exports, SQLITE_FORMAT, FORMAT) DEFINE_CONSTANT_INTEGER(exports, SQLITE_RANGE, RANGE) DEFINE_CONSTANT_INTEGER(exports, SQLITE_NOTADB, NOTADB) }); return exports; } } const char* sqlite_code_string(int code) { switch (code) { case SQLITE_OK: return "SQLITE_OK"; case SQLITE_ERROR: return "SQLITE_ERROR"; case SQLITE_INTERNAL: return "SQLITE_INTERNAL"; case SQLITE_PERM: return "SQLITE_PERM"; case SQLITE_ABORT: return "SQLITE_ABORT"; case SQLITE_BUSY: return "SQLITE_BUSY"; case SQLITE_LOCKED: return "SQLITE_LOCKED"; case SQLITE_NOMEM: return "SQLITE_NOMEM"; case SQLITE_READONLY: return "SQLITE_READONLY"; case SQLITE_INTERRUPT: return "SQLITE_INTERRUPT"; case SQLITE_IOERR: return "SQLITE_IOERR"; case SQLITE_CORRUPT: return "SQLITE_CORRUPT"; case SQLITE_NOTFOUND: return "SQLITE_NOTFOUND"; case SQLITE_FULL: return "SQLITE_FULL"; case SQLITE_CANTOPEN: return "SQLITE_CANTOPEN"; case SQLITE_PROTOCOL: return "SQLITE_PROTOCOL"; case SQLITE_EMPTY: return "SQLITE_EMPTY"; case SQLITE_SCHEMA: return "SQLITE_SCHEMA"; case SQLITE_TOOBIG: return "SQLITE_TOOBIG"; case SQLITE_CONSTRAINT: return "SQLITE_CONSTRAINT"; case SQLITE_MISMATCH: return "SQLITE_MISMATCH"; case SQLITE_MISUSE: return "SQLITE_MISUSE"; case SQLITE_NOLFS: return "SQLITE_NOLFS"; case SQLITE_AUTH: return "SQLITE_AUTH"; case SQLITE_FORMAT: return "SQLITE_FORMAT"; case SQLITE_RANGE: return "SQLITE_RANGE"; case SQLITE_NOTADB: return "SQLITE_NOTADB"; case SQLITE_ROW: return "SQLITE_ROW"; case SQLITE_DONE: return "SQLITE_DONE"; default: return "UNKNOWN"; } } const char* sqlite_authorizer_string(int type) { switch (type) { case SQLITE_INSERT: return "insert"; case SQLITE_UPDATE: return "update"; case SQLITE_DELETE: return "delete"; default: return ""; } } NODE_API_MODULE(node_sqlite3, RegisterModule) /* file none */ /*jslint-enable*/ /* file sqlmath-old2.cpp */ // copyright nobody #include #include extern "C" { typedef struct Jsonresult Jsonresult; void jsExec(sqlite3 *, const char, Jsonresult *); } Napi::Value Foo(const Napi::CallbackInfo& info) { Napi::Env env = info.Env(); sqlite3 *db; int rc = sqlite3_open_v2( (info[0].As().Utf8Value()).c_str(), //!! ":memory:", &db, 1, NULL ); printf("%s %i\n", (info[0].As().Utf8Value()).c_str(), rc ); Napi::Number num = Napi::Number::New(env, rc); return num; } Napi::Object Init(Napi::Env env, Napi::Object exports) { // export foo exports.Set( Napi::String::New(env, "foo"), Napi::Function::New(env, Foo) ); return exports; } NODE_API_MODULE(hello, Init) /* file sqlmath-old.h */ /*jslint-disable*/ /* shRawLibFetch { "fetchList": [ { "comment": true, "url": "https://github.com/nodejs/node-addon-api/blob/4.0.0/LICENSE.md" }, { "url": "https://github.com/nodejs/node-addon-api/blob/4.0.0/napi.h" }, { "url": "https://github.com/nodejs/node-addon-api/blob/4.0.0/napi-inl.h" } ], "replaceList": [ { "aa": "^#include ", "bb": "// $&", "flags": "gm" } ] } -# include "napi-inl.deprecated.h" +// hack-sqlite +// # include "napi-inl.deprecated.h" -#define SRC_NAPI_H_ +// hack-sqlite +#define SRC_NAPI_H_ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include -#endif // SRC_NAPI_H_ +// hack-sqlite +// #endif // SRC_NAPI_H_ -#endif // SRC_NAPI_INL_H_ +// hack-sqlite +#endif // SRC_NAPI_INL_H_ +#endif // SRC_NAPI_H_ */ /* repo https://github.com/nodejs/node-addon-api/tree/4.0.0 committed 2021-06-15T11:15:35Z */ /* file https://github.com/nodejs/node-addon-api/blob/4.0.0/LICENSE.md */ /* The MIT License (MIT) ===================== Copyright (c) 2017 Node.js API collaborators ----------------------------------- *Node.js API collaborators listed at * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /* file https://github.com/nodejs/node-addon-api/blob/4.0.0/napi.h */ #ifndef SRC_NAPI_H_ // hack-sqlite #define SRC_NAPI_H_ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include // #include // #include // #include // #include // #include // #include // #include // VS2015 RTM has bugs with constexpr, so require min of VS2015 Update 3 (known good version) #if !defined(_MSC_VER) || _MSC_FULL_VER >= 190024210 #define NAPI_HAS_CONSTEXPR 1 #endif // VS2013 does not support char16_t literal strings, so we'll work around it using wchar_t strings // and casting them. This is safe as long as the character sizes are the same. #if defined(_MSC_VER) && _MSC_VER <= 1800 static_assert(sizeof(char16_t) == sizeof(wchar_t), "Size mismatch between char16_t and wchar_t"); #define NAPI_WIDE_TEXT(x) reinterpret_cast(L ## x) #else #define NAPI_WIDE_TEXT(x) u ## x #endif // If C++ exceptions are not explicitly enabled or disabled, enable them // if exceptions were enabled in the compiler settings. #if !defined(NAPI_CPP_EXCEPTIONS) && !defined(NAPI_DISABLE_CPP_EXCEPTIONS) #if defined(_CPPUNWIND) || defined (__EXCEPTIONS) #define NAPI_CPP_EXCEPTIONS #else #error Exception support not detected. \ Define either NAPI_CPP_EXCEPTIONS or NAPI_DISABLE_CPP_EXCEPTIONS. #endif #endif #ifdef _NOEXCEPT #define NAPI_NOEXCEPT _NOEXCEPT #else #define NAPI_NOEXCEPT noexcept #endif #ifdef NAPI_CPP_EXCEPTIONS // When C++ exceptions are enabled, Errors are thrown directly. There is no need // to return anything after the throw statements. The variadic parameter is an // optional return value that is ignored. // We need _VOID versions of the macros to avoid warnings resulting from // leaving the NAPI_THROW_* `...` argument empty. #define NAPI_THROW(e, ...) throw e #define NAPI_THROW_VOID(e) throw e #define NAPI_THROW_IF_FAILED(env, status, ...) \ if ((status) != napi_ok) throw Napi::Error::New(env); #define NAPI_THROW_IF_FAILED_VOID(env, status) \ if ((status) != napi_ok) throw Napi::Error::New(env); #else // NAPI_CPP_EXCEPTIONS // When C++ exceptions are disabled, Errors are thrown as JavaScript exceptions, // which are pending until the callback returns to JS. The variadic parameter // is an optional return value; usually it is an empty result. // We need _VOID versions of the macros to avoid warnings resulting from // leaving the NAPI_THROW_* `...` argument empty. #define NAPI_THROW(e, ...) \ do { \ (e).ThrowAsJavaScriptException(); \ return __VA_ARGS__; \ } while (0) #define NAPI_THROW_VOID(e) \ do { \ (e).ThrowAsJavaScriptException(); \ return; \ } while (0) #define NAPI_THROW_IF_FAILED(env, status, ...) \ if ((status) != napi_ok) { \ Napi::Error::New(env).ThrowAsJavaScriptException(); \ return __VA_ARGS__; \ } #define NAPI_THROW_IF_FAILED_VOID(env, status) \ if ((status) != napi_ok) { \ Napi::Error::New(env).ThrowAsJavaScriptException(); \ return; \ } #endif // NAPI_CPP_EXCEPTIONS # define NAPI_DISALLOW_ASSIGN(CLASS) void operator=(const CLASS&) = delete; # define NAPI_DISALLOW_COPY(CLASS) CLASS(const CLASS&) = delete; #define NAPI_DISALLOW_ASSIGN_COPY(CLASS) \ NAPI_DISALLOW_ASSIGN(CLASS) \ NAPI_DISALLOW_COPY(CLASS) #define NAPI_FATAL_IF_FAILED(status, location, message) \ do { \ if ((status) != napi_ok) { \ Napi::Error::Fatal((location), (message)); \ } \ } while (0) //////////////////////////////////////////////////////////////////////////////// /// Node-API C++ Wrapper Classes /// /// These classes wrap the "Node-API" ABI-stable C APIs for Node.js, providing a /// C++ object model and C++ exception-handling semantics with low overhead. /// The wrappers are all header-only so that they do not affect the ABI. //////////////////////////////////////////////////////////////////////////////// namespace Napi { // Forward declarations class Env; class Value; class Boolean; class Number; #if NAPI_VERSION > 5 class BigInt; #endif // NAPI_VERSION > 5 #if (NAPI_VERSION > 4) class Date; #endif class String; class Object; class Array; class ArrayBuffer; class Function; class Error; class PropertyDescriptor; class CallbackInfo; class TypedArray; template class TypedArrayOf; using Int8Array = TypedArrayOf; ///< Typed-array of signed 8-bit integers using Uint8Array = TypedArrayOf; ///< Typed-array of unsigned 8-bit integers using Int16Array = TypedArrayOf; ///< Typed-array of signed 16-bit integers using Uint16Array = TypedArrayOf; ///< Typed-array of unsigned 16-bit integers using Int32Array = TypedArrayOf; ///< Typed-array of signed 32-bit integers using Uint32Array = TypedArrayOf; ///< Typed-array of unsigned 32-bit integers using Float32Array = TypedArrayOf; ///< Typed-array of 32-bit floating-point values using Float64Array = TypedArrayOf; ///< Typed-array of 64-bit floating-point values #if NAPI_VERSION > 5 using BigInt64Array = TypedArrayOf; ///< Typed array of signed 64-bit integers using BigUint64Array = TypedArrayOf; ///< Typed array of unsigned 64-bit integers #endif // NAPI_VERSION > 5 /// Defines the signature of a Node-API C++ module's registration callback /// (init) function. using ModuleRegisterCallback = Object (*)(Env env, Object exports); class MemoryManagement; /// Environment for Node-API values and operations. /// /// All Node-API values and operations must be associated with an environment. /// An environment instance is always provided to callback functions; that /// environment must then be used for any creation of Node-API values or other /// Node-API operations within the callback. (Many methods infer the /// environment from the `this` instance that the method is called on.) /// /// In the future, multiple environments per process may be supported, /// although current implementations only support one environment per process. /// /// In the V8 JavaScript engine, a Node-API environment approximately /// corresponds to an Isolate. class Env { #if NAPI_VERSION > 5 private: template static void DefaultFini(Env, T* data); template static void DefaultFiniWithHint(Env, DataType* data, HintType* hint); #endif // NAPI_VERSION > 5 public: Env(napi_env env); operator napi_env() const; Object Global() const; Value Undefined() const; Value Null() const; bool IsExceptionPending() const; Error GetAndClearPendingException(); Value RunScript(const char* utf8script); Value RunScript(const std::string& utf8script); Value RunScript(String script); #if NAPI_VERSION > 5 template T* GetInstanceData(); template using Finalizer = void (*)(Env, T*); template fini = Env::DefaultFini> void SetInstanceData(T* data); template using FinalizerWithHint = void (*)(Env, DataType*, HintType*); template fini = Env::DefaultFiniWithHint> void SetInstanceData(DataType* data, HintType* hint); #endif // NAPI_VERSION > 5 private: napi_env _env; }; /// A JavaScript value of unknown type. /// /// For type-specific operations, convert to one of the Value subclasses using a `To*` or `As()` /// method. The `To*` methods do type coercion; the `As()` method does not. /// /// Napi::Value value = ... /// if (!value.IsString()) throw Napi::TypeError::New(env, "Invalid arg..."); /// Napi::String str = value.As(); // Cast to a string value /// /// Napi::Value anotherValue = ... /// bool isTruthy = anotherValue.ToBoolean(); // Coerce to a boolean value class Value { public: Value(); ///< Creates a new _empty_ Value instance. Value(napi_env env, napi_value value); ///< Wraps a Node-API value primitive. /// Creates a JS value from a C++ primitive. /// /// `value` may be any of: /// - bool /// - Any integer type /// - Any floating point type /// - const char* (encoded using UTF-8, null-terminated) /// - const char16_t* (encoded using UTF-16-LE, null-terminated) /// - std::string (encoded using UTF-8) /// - std::u16string /// - napi::Value /// - napi_value template static Value From(napi_env env, const T& value); /// Converts to a Node-API value primitive. /// /// If the instance is _empty_, this returns `nullptr`. operator napi_value() const; /// Tests if this value strictly equals another value. bool operator ==(const Value& other) const; /// Tests if this value does not strictly equal another value. bool operator !=(const Value& other) const; /// Tests if this value strictly equals another value. bool StrictEquals(const Value& other) const; /// Gets the environment the value is associated with. Napi::Env Env() const; /// Checks if the value is empty (uninitialized). /// /// An empty value is invalid, and most attempts to perform an operation on an empty value /// will result in an exception. Note an empty value is distinct from JavaScript `null` or /// `undefined`, which are valid values. /// /// When C++ exceptions are disabled at compile time, a method with a `Value` return type may /// return an empty value to indicate a pending exception. So when not using C++ exceptions, /// callers should check whether the value is empty before attempting to use it. bool IsEmpty() const; napi_valuetype Type() const; ///< Gets the type of the value. bool IsUndefined() const; ///< Tests if a value is an undefined JavaScript value. bool IsNull() const; ///< Tests if a value is a null JavaScript value. bool IsBoolean() const; ///< Tests if a value is a JavaScript boolean. bool IsNumber() const; ///< Tests if a value is a JavaScript number. #if NAPI_VERSION > 5 bool IsBigInt() const; ///< Tests if a value is a JavaScript bigint. #endif // NAPI_VERSION > 5 #if (NAPI_VERSION > 4) bool IsDate() const; ///< Tests if a value is a JavaScript date. #endif bool IsString() const; ///< Tests if a value is a JavaScript string. bool IsSymbol() const; ///< Tests if a value is a JavaScript symbol. bool IsArray() const; ///< Tests if a value is a JavaScript array. bool IsArrayBuffer() const; ///< Tests if a value is a JavaScript array buffer. bool IsTypedArray() const; ///< Tests if a value is a JavaScript typed array. bool IsObject() const; ///< Tests if a value is a JavaScript object. bool IsFunction() const; ///< Tests if a value is a JavaScript function. bool IsPromise() const; ///< Tests if a value is a JavaScript promise. bool IsDataView() const; ///< Tests if a value is a JavaScript data view. bool IsBuffer() const; ///< Tests if a value is a Node buffer. bool IsExternal() const; ///< Tests if a value is a pointer to external data. /// Casts to another type of `Napi::Value`, when the actual type is known or assumed. /// /// This conversion does NOT coerce the type. Calling any methods inappropriate for the actual /// value type will throw `Napi::Error`. template T As() const; Boolean ToBoolean() const; ///< Coerces a value to a JavaScript boolean. Number ToNumber() const; ///< Coerces a value to a JavaScript number. String ToString() const; ///< Coerces a value to a JavaScript string. Object ToObject() const; ///< Coerces a value to a JavaScript object. protected: /// !cond INTERNAL napi_env _env; napi_value _value; /// !endcond }; /// A JavaScript boolean value. class Boolean : public Value { public: static Boolean New(napi_env env, ///< Node-API environment bool value ///< Boolean value ); Boolean(); ///< Creates a new _empty_ Boolean instance. Boolean(napi_env env, napi_value value); ///< Wraps a Node-API value primitive. operator bool() const; ///< Converts a Boolean value to a boolean primitive. bool Value() const; ///< Converts a Boolean value to a boolean primitive. }; /// A JavaScript number value. class Number : public Value { public: static Number New(napi_env env, ///< Node-API environment double value ///< Number value ); Number(); ///< Creates a new _empty_ Number instance. Number(napi_env env, napi_value value); ///< Wraps a Node-API value primitive. operator int32_t() const; ///< Converts a Number value to a 32-bit signed integer value. operator uint32_t() const; ///< Converts a Number value to a 32-bit unsigned integer value. operator int64_t() const; ///< Converts a Number value to a 64-bit signed integer value. operator float() const; ///< Converts a Number value to a 32-bit floating-point value. operator double() const; ///< Converts a Number value to a 64-bit floating-point value. int32_t Int32Value() const; ///< Converts a Number value to a 32-bit signed integer value. uint32_t Uint32Value() const; ///< Converts a Number value to a 32-bit unsigned integer value. int64_t Int64Value() const; ///< Converts a Number value to a 64-bit signed integer value. float FloatValue() const; ///< Converts a Number value to a 32-bit floating-point value. double DoubleValue() const; ///< Converts a Number value to a 64-bit floating-point value. }; #if NAPI_VERSION > 5 /// A JavaScript bigint value. class BigInt : public Value { public: static BigInt New(napi_env env, ///< Node-API environment int64_t value ///< Number value ); static BigInt New(napi_env env, ///< Node-API environment uint64_t value ///< Number value ); /// Creates a new BigInt object using a specified sign bit and a /// specified list of digits/words. /// The resulting number is calculated as: /// (-1)^sign_bit * (words[0] * (2^64)^0 + words[1] * (2^64)^1 + ...) static BigInt New(napi_env env, ///< Node-API environment int sign_bit, ///< Sign bit. 1 if negative. size_t word_count, ///< Number of words in array const uint64_t* words ///< Array of words ); BigInt(); ///< Creates a new _empty_ BigInt instance. BigInt(napi_env env, napi_value value); ///< Wraps a Node-API value primitive. int64_t Int64Value(bool* lossless) const; ///< Converts a BigInt value to a 64-bit signed integer value. uint64_t Uint64Value(bool* lossless) const; ///< Converts a BigInt value to a 64-bit unsigned integer value. size_t WordCount() const; ///< The number of 64-bit words needed to store ///< the result of ToWords(). /// Writes the contents of this BigInt to a specified memory location. /// `sign_bit` must be provided and will be set to 1 if this BigInt is /// negative. /// `*word_count` has to be initialized to the length of the `words` array. /// Upon return, it will be set to the actual number of words that would /// be needed to store this BigInt (i.e. the return value of `WordCount()`). void ToWords(int* sign_bit, size_t* word_count, uint64_t* words); }; #endif // NAPI_VERSION > 5 #if (NAPI_VERSION > 4) /// A JavaScript date value. class Date : public Value { public: /// Creates a new Date value from a double primitive. static Date New(napi_env env, ///< Node-API environment double value ///< Number value ); Date(); ///< Creates a new _empty_ Date instance. Date(napi_env env, napi_value value); ///< Wraps a Node-API value primitive. operator double() const; ///< Converts a Date value to double primitive double ValueOf() const; ///< Converts a Date value to a double primitive. }; #endif /// A JavaScript string or symbol value (that can be used as a property name). class Name : public Value { public: Name(); ///< Creates a new _empty_ Name instance. Name(napi_env env, napi_value value); ///< Wraps a Node-API value primitive. }; /// A JavaScript string value. class String : public Name { public: /// Creates a new String value from a UTF-8 encoded C++ string. static String New(napi_env env, ///< Node-API environment const std::string& value ///< UTF-8 encoded C++ string ); /// Creates a new String value from a UTF-16 encoded C++ string. static String New(napi_env env, ///< Node-API environment const std::u16string& value ///< UTF-16 encoded C++ string ); /// Creates a new String value from a UTF-8 encoded C string. static String New( napi_env env, ///< Node-API environment const char* value ///< UTF-8 encoded null-terminated C string ); /// Creates a new String value from a UTF-16 encoded C string. static String New( napi_env env, ///< Node-API environment const char16_t* value ///< UTF-16 encoded null-terminated C string ); /// Creates a new String value from a UTF-8 encoded C string with specified /// length. static String New(napi_env env, ///< Node-API environment const char* value, ///< UTF-8 encoded C string (not ///< necessarily null-terminated) size_t length ///< length of the string in bytes ); /// Creates a new String value from a UTF-16 encoded C string with specified /// length. static String New( napi_env env, ///< Node-API environment const char16_t* value, ///< UTF-16 encoded C string (not necessarily ///< null-terminated) size_t length ///< Length of the string in 2-byte code units ); /// Creates a new String based on the original object's type. /// /// `value` may be any of: /// - const char* (encoded using UTF-8, null-terminated) /// - const char16_t* (encoded using UTF-16-LE, null-terminated) /// - std::string (encoded using UTF-8) /// - std::u16string template static String From(napi_env env, const T& value); String(); ///< Creates a new _empty_ String instance. String(napi_env env, napi_value value); ///< Wraps a Node-API value primitive. operator std::string() const; ///< Converts a String value to a UTF-8 encoded C++ string. operator std::u16string() const; ///< Converts a String value to a UTF-16 encoded C++ string. std::string Utf8Value() const; ///< Converts a String value to a UTF-8 encoded C++ string. std::u16string Utf16Value() const; ///< Converts a String value to a UTF-16 encoded C++ string. }; /// A JavaScript symbol value. class Symbol : public Name { public: /// Creates a new Symbol value with an optional description. static Symbol New( napi_env env, ///< Node-API environment const char* description = nullptr ///< Optional UTF-8 encoded null-terminated C string /// describing the symbol ); /// Creates a new Symbol value with a description. static Symbol New( napi_env env, ///< Node-API environment const std::string& description ///< UTF-8 encoded C++ string describing the symbol ); /// Creates a new Symbol value with a description. static Symbol New(napi_env env, ///< Node-API environment String description ///< String value describing the symbol ); /// Creates a new Symbol value with a description. static Symbol New( napi_env env, ///< Node-API environment napi_value description ///< String value describing the symbol ); /// Get a public Symbol (e.g. Symbol.iterator). static Symbol WellKnown(napi_env, const std::string& name); Symbol(); ///< Creates a new _empty_ Symbol instance. Symbol(napi_env env, napi_value value); ///< Wraps a Node-API value primitive. }; /// A JavaScript object value. class Object : public Value { public: /// Enables property and element assignments using indexing syntax. /// /// Example: /// /// Napi::Value propertyValue = object1['A']; /// object2['A'] = propertyValue; /// Napi::Value elementValue = array[0]; /// array[1] = elementValue; template class PropertyLValue { public: /// Converts an L-value to a value. operator Value() const; /// Assigns a value to the property. The type of value can be /// anything supported by `Object::Set`. template PropertyLValue& operator =(ValueType value); private: PropertyLValue() = delete; PropertyLValue(Object object, Key key); napi_env _env; napi_value _object; Key _key; friend class Napi::Object; }; /// Creates a new Object value. static Object New(napi_env env ///< Node-API environment ); Object(); ///< Creates a new _empty_ Object instance. Object(napi_env env, napi_value value); ///< Wraps a Node-API value primitive. /// Gets or sets a named property. PropertyLValue operator []( const char* utf8name ///< UTF-8 encoded null-terminated property name ); /// Gets or sets a named property. PropertyLValue operator []( const std::string& utf8name ///< UTF-8 encoded property name ); /// Gets or sets an indexed property or array element. PropertyLValue operator []( uint32_t index /// Property / element index ); /// Gets a named property. Value operator []( const char* utf8name ///< UTF-8 encoded null-terminated property name ) const; /// Gets a named property. Value operator []( const std::string& utf8name ///< UTF-8 encoded property name ) const; /// Gets an indexed property or array element. Value operator []( uint32_t index ///< Property / element index ) const; /// Checks whether a property is present. bool Has( napi_value key ///< Property key primitive ) const; /// Checks whether a property is present. bool Has( Value key ///< Property key ) const; /// Checks whether a named property is present. bool Has( const char* utf8name ///< UTF-8 encoded null-terminated property name ) const; /// Checks whether a named property is present. bool Has( const std::string& utf8name ///< UTF-8 encoded property name ) const; /// Checks whether a own property is present. bool HasOwnProperty( napi_value key ///< Property key primitive ) const; /// Checks whether a own property is present. bool HasOwnProperty( Value key ///< Property key ) const; /// Checks whether a own property is present. bool HasOwnProperty( const char* utf8name ///< UTF-8 encoded null-terminated property name ) const; /// Checks whether a own property is present. bool HasOwnProperty( const std::string& utf8name ///< UTF-8 encoded property name ) const; /// Gets a property. Value Get( napi_value key ///< Property key primitive ) const; /// Gets a property. Value Get( Value key ///< Property key ) const; /// Gets a named property. Value Get( const char* utf8name ///< UTF-8 encoded null-terminated property name ) const; /// Gets a named property. Value Get( const std::string& utf8name ///< UTF-8 encoded property name ) const; /// Sets a property. template bool Set(napi_value key, ///< Property key primitive const ValueType& value ///< Property value primitive ); /// Sets a property. template bool Set(Value key, ///< Property key const ValueType& value ///< Property value ); /// Sets a named property. template bool Set( const char* utf8name, ///< UTF-8 encoded null-terminated property name const ValueType& value); /// Sets a named property. template bool Set(const std::string& utf8name, ///< UTF-8 encoded property name const ValueType& value ///< Property value primitive ); /// Delete property. bool Delete( napi_value key ///< Property key primitive ); /// Delete property. bool Delete( Value key ///< Property key ); /// Delete property. bool Delete( const char* utf8name ///< UTF-8 encoded null-terminated property name ); /// Delete property. bool Delete( const std::string& utf8name ///< UTF-8 encoded property name ); /// Checks whether an indexed property is present. bool Has( uint32_t index ///< Property / element index ) const; /// Gets an indexed property or array element. Value Get( uint32_t index ///< Property / element index ) const; /// Sets an indexed property or array element. template bool Set(uint32_t index, ///< Property / element index const ValueType& value ///< Property value primitive ); /// Deletes an indexed property or array element. bool Delete( uint32_t index ///< Property / element index ); Array GetPropertyNames() const; ///< Get all property names /// Defines a property on the object. bool DefineProperty( const PropertyDescriptor& property ///< Descriptor for the property to be defined ); /// Defines properties on the object. bool DefineProperties( const std::initializer_list& properties ///< List of descriptors for the properties to be defined ); /// Defines properties on the object. bool DefineProperties( const std::vector& properties ///< Vector of descriptors for the properties to be defined ); /// Checks if an object is an instance created by a constructor function. /// /// This is equivalent to the JavaScript `instanceof` operator. bool InstanceOf( const Function& constructor ///< Constructor function ) const; template inline void AddFinalizer(Finalizer finalizeCallback, T* data); template inline void AddFinalizer(Finalizer finalizeCallback, T* data, Hint* finalizeHint); #if NAPI_VERSION >= 8 bool Freeze(); bool Seal(); #endif // NAPI_VERSION >= 8 }; template class External : public Value { public: static External New(napi_env env, T* data); // Finalizer must implement `void operator()(Env env, T* data)`. template static External New(napi_env env, T* data, Finalizer finalizeCallback); // Finalizer must implement `void operator()(Env env, T* data, Hint* hint)`. template static External New(napi_env env, T* data, Finalizer finalizeCallback, Hint* finalizeHint); External(); External(napi_env env, napi_value value); T* Data() const; }; class Array : public Object { public: static Array New(napi_env env); static Array New(napi_env env, size_t length); Array(); Array(napi_env env, napi_value value); uint32_t Length() const; }; /// A JavaScript array buffer value. class ArrayBuffer : public Object { public: /// Creates a new ArrayBuffer instance over a new automatically-allocated buffer. static ArrayBuffer New( napi_env env, ///< Node-API environment size_t byteLength ///< Length of the buffer to be allocated, in bytes ); /// Creates a new ArrayBuffer instance, using an external buffer with /// specified byte length. static ArrayBuffer New( napi_env env, ///< Node-API environment void* externalData, ///< Pointer to the external buffer to be used by ///< the array size_t byteLength ///< Length of the external buffer to be used by the ///< array, in bytes ); /// Creates a new ArrayBuffer instance, using an external buffer with /// specified byte length. template static ArrayBuffer New( napi_env env, ///< Node-API environment void* externalData, ///< Pointer to the external buffer to be used by ///< the array size_t byteLength, ///< Length of the external buffer to be used by the ///< array, /// in bytes Finalizer finalizeCallback ///< Function to be called when the array ///< buffer is destroyed; /// must implement `void operator()(Env env, /// void* externalData)` ); /// Creates a new ArrayBuffer instance, using an external buffer with /// specified byte length. template static ArrayBuffer New( napi_env env, ///< Node-API environment void* externalData, ///< Pointer to the external buffer to be used by ///< the array size_t byteLength, ///< Length of the external buffer to be used by the ///< array, /// in bytes Finalizer finalizeCallback, ///< Function to be called when the array ///< buffer is destroyed; /// must implement `void operator()(Env /// env, void* externalData, Hint* hint)` Hint* finalizeHint ///< Hint (second parameter) to be passed to the ///< finalize callback ); ArrayBuffer(); ///< Creates a new _empty_ ArrayBuffer instance. ArrayBuffer(napi_env env, napi_value value); ///< Wraps a Node-API value primitive. void* Data(); ///< Gets a pointer to the data buffer. size_t ByteLength(); ///< Gets the length of the array buffer in bytes. #if NAPI_VERSION >= 7 bool IsDetached() const; void Detach(); #endif // NAPI_VERSION >= 7 }; /// A JavaScript typed-array value with unknown array type. /// /// For type-specific operations, cast to a `TypedArrayOf` instance using the `As()` /// method: /// /// Napi::TypedArray array = ... /// if (t.TypedArrayType() == napi_int32_array) { /// Napi::Int32Array int32Array = t.As(); /// } class TypedArray : public Object { public: TypedArray(); ///< Creates a new _empty_ TypedArray instance. TypedArray(napi_env env, napi_value value); ///< Wraps a Node-API value primitive. napi_typedarray_type TypedArrayType() const; ///< Gets the type of this typed-array. Napi::ArrayBuffer ArrayBuffer() const; ///< Gets the backing array buffer. uint8_t ElementSize() const; ///< Gets the size in bytes of one element in the array. size_t ElementLength() const; ///< Gets the number of elements in the array. size_t ByteOffset() const; ///< Gets the offset into the buffer where the array starts. size_t ByteLength() const; ///< Gets the length of the array in bytes. protected: /// !cond INTERNAL napi_typedarray_type _type; size_t _length; TypedArray(napi_env env, napi_value value, napi_typedarray_type type, size_t length); static const napi_typedarray_type unknown_array_type = static_cast(-1); template static #if defined(NAPI_HAS_CONSTEXPR) constexpr #endif napi_typedarray_type TypedArrayTypeForPrimitiveType() { return std::is_same::value ? napi_int8_array : std::is_same::value ? napi_uint8_array : std::is_same::value ? napi_int16_array : std::is_same::value ? napi_uint16_array : std::is_same::value ? napi_int32_array : std::is_same::value ? napi_uint32_array : std::is_same::value ? napi_float32_array : std::is_same::value ? napi_float64_array #if NAPI_VERSION > 5 : std::is_same::value ? napi_bigint64_array : std::is_same::value ? napi_biguint64_array #endif // NAPI_VERSION > 5 : unknown_array_type; } /// !endcond }; /// A JavaScript typed-array value with known array type. /// /// Note while it is possible to create and access Uint8 "clamped" arrays using this class, /// the _clamping_ behavior is only applied in JavaScript. template class TypedArrayOf : public TypedArray { public: /// Creates a new TypedArray instance over a new automatically-allocated array buffer. /// /// The array type parameter can normally be omitted (because it is inferred from the template /// parameter T), except when creating a "clamped" array: /// /// Uint8Array::New(env, length, napi_uint8_clamped_array) static TypedArrayOf New( napi_env env, ///< Node-API environment size_t elementLength, ///< Length of the created array, as a number of ///< elements #if defined(NAPI_HAS_CONSTEXPR) napi_typedarray_type type = TypedArray::TypedArrayTypeForPrimitiveType() #else napi_typedarray_type type #endif ///< Type of array, if different from the default array type for the ///< template parameter T. ); /// Creates a new TypedArray instance over a provided array buffer. /// /// The array type parameter can normally be omitted (because it is inferred from the template /// parameter T), except when creating a "clamped" array: /// /// Uint8Array::New(env, length, buffer, 0, napi_uint8_clamped_array) static TypedArrayOf New( napi_env env, ///< Node-API environment size_t elementLength, ///< Length of the created array, as a number of ///< elements Napi::ArrayBuffer arrayBuffer, ///< Backing array buffer instance to use size_t bufferOffset, ///< Offset into the array buffer where the ///< typed-array starts #if defined(NAPI_HAS_CONSTEXPR) napi_typedarray_type type = TypedArray::TypedArrayTypeForPrimitiveType() #else napi_typedarray_type type #endif ///< Type of array, if different from the default array type for the ///< template parameter T. ); TypedArrayOf(); ///< Creates a new _empty_ TypedArrayOf instance. TypedArrayOf(napi_env env, napi_value value); ///< Wraps a Node-API value primitive. T& operator [](size_t index); ///< Gets or sets an element in the array. const T& operator [](size_t index) const; ///< Gets an element in the array. /// Gets a pointer to the array's backing buffer. /// /// This is not necessarily the same as the `ArrayBuffer::Data()` pointer, because the /// typed-array may have a non-zero `ByteOffset()` into the `ArrayBuffer`. T* Data(); /// Gets a pointer to the array's backing buffer. /// /// This is not necessarily the same as the `ArrayBuffer::Data()` pointer, because the /// typed-array may have a non-zero `ByteOffset()` into the `ArrayBuffer`. const T* Data() const; private: T* _data; TypedArrayOf(napi_env env, napi_value value, napi_typedarray_type type, size_t length, T* data); }; /// The DataView provides a low-level interface for reading/writing multiple /// number types in an ArrayBuffer irrespective of the platform's endianness. class DataView : public Object { public: static DataView New(napi_env env, Napi::ArrayBuffer arrayBuffer); static DataView New(napi_env env, Napi::ArrayBuffer arrayBuffer, size_t byteOffset); static DataView New(napi_env env, Napi::ArrayBuffer arrayBuffer, size_t byteOffset, size_t byteLength); DataView(); ///< Creates a new _empty_ DataView instance. DataView(napi_env env, napi_value value); ///< Wraps a Node-API value primitive. Napi::ArrayBuffer ArrayBuffer() const; ///< Gets the backing array buffer. size_t ByteOffset() const; ///< Gets the offset into the buffer where the array starts. size_t ByteLength() const; ///< Gets the length of the array in bytes. void* Data() const; float GetFloat32(size_t byteOffset) const; double GetFloat64(size_t byteOffset) const; int8_t GetInt8(size_t byteOffset) const; int16_t GetInt16(size_t byteOffset) const; int32_t GetInt32(size_t byteOffset) const; uint8_t GetUint8(size_t byteOffset) const; uint16_t GetUint16(size_t byteOffset) const; uint32_t GetUint32(size_t byteOffset) const; void SetFloat32(size_t byteOffset, float value) const; void SetFloat64(size_t byteOffset, double value) const; void SetInt8(size_t byteOffset, int8_t value) const; void SetInt16(size_t byteOffset, int16_t value) const; void SetInt32(size_t byteOffset, int32_t value) const; void SetUint8(size_t byteOffset, uint8_t value) const; void SetUint16(size_t byteOffset, uint16_t value) const; void SetUint32(size_t byteOffset, uint32_t value) const; private: template T ReadData(size_t byteOffset) const; template void WriteData(size_t byteOffset, T value) const; void* _data; size_t _length; }; class Function : public Object { public: using VoidCallback = void (*)(const CallbackInfo& info); using Callback = Value (*)(const CallbackInfo& info); template static Function New(napi_env env, const char* utf8name = nullptr, void* data = nullptr); template static Function New(napi_env env, const char* utf8name = nullptr, void* data = nullptr); template static Function New(napi_env env, const std::string& utf8name, void* data = nullptr); template static Function New(napi_env env, const std::string& utf8name, void* data = nullptr); /// Callable must implement operator() accepting a const CallbackInfo& /// and return either void or Value. template static Function New(napi_env env, Callable cb, const char* utf8name = nullptr, void* data = nullptr); /// Callable must implement operator() accepting a const CallbackInfo& /// and return either void or Value. template static Function New(napi_env env, Callable cb, const std::string& utf8name, void* data = nullptr); Function(); Function(napi_env env, napi_value value); Value operator()(const std::initializer_list& args) const; Value Call(const std::initializer_list& args) const; Value Call(const std::vector& args) const; Value Call(size_t argc, const napi_value* args) const; Value Call(napi_value recv, const std::initializer_list& args) const; Value Call(napi_value recv, const std::vector& args) const; Value Call(napi_value recv, size_t argc, const napi_value* args) const; Value MakeCallback(napi_value recv, const std::initializer_list& args, napi_async_context context = nullptr) const; Value MakeCallback(napi_value recv, const std::vector& args, napi_async_context context = nullptr) const; Value MakeCallback(napi_value recv, size_t argc, const napi_value* args, napi_async_context context = nullptr) const; Object New(const std::initializer_list& args) const; Object New(const std::vector& args) const; Object New(size_t argc, const napi_value* args) const; }; class Promise : public Object { public: class Deferred { public: static Deferred New(napi_env env); Deferred(napi_env env); Napi::Promise Promise() const; Napi::Env Env() const; void Resolve(napi_value value) const; void Reject(napi_value value) const; private: napi_env _env; napi_deferred _deferred; napi_value _promise; }; Promise(napi_env env, napi_value value); }; template class Buffer : public Uint8Array { public: static Buffer New(napi_env env, size_t length); static Buffer New(napi_env env, T* data, size_t length); // Finalizer must implement `void operator()(Env env, T* data)`. template static Buffer New(napi_env env, T* data, size_t length, Finalizer finalizeCallback); // Finalizer must implement `void operator()(Env env, T* data, Hint* hint)`. template static Buffer New(napi_env env, T* data, size_t length, Finalizer finalizeCallback, Hint* finalizeHint); static Buffer Copy(napi_env env, const T* data, size_t length); Buffer(); Buffer(napi_env env, napi_value value); size_t Length() const; T* Data() const; private: mutable size_t _length; mutable T* _data; Buffer(napi_env env, napi_value value, size_t length, T* data); void EnsureInfo() const; }; /// Holds a counted reference to a value; initially a weak reference unless otherwise specified, /// may be changed to/from a strong reference by adjusting the refcount. /// /// The referenced value is not immediately destroyed when the reference count is zero; it is /// merely then eligible for garbage-collection if there are no other references to the value. template class Reference { public: static Reference New(const T& value, uint32_t initialRefcount = 0); Reference(); Reference(napi_env env, napi_ref ref); ~Reference(); // A reference can be moved but cannot be copied. Reference(Reference&& other); Reference& operator =(Reference&& other); NAPI_DISALLOW_ASSIGN(Reference) operator napi_ref() const; bool operator ==(const Reference &other) const; bool operator !=(const Reference &other) const; Napi::Env Env() const; bool IsEmpty() const; // Note when getting the value of a Reference it is usually correct to do so // within a HandleScope so that the value handle gets cleaned up efficiently. T Value() const; uint32_t Ref(); uint32_t Unref(); void Reset(); void Reset(const T& value, uint32_t refcount = 0); // Call this on a reference that is declared as static data, to prevent its // destructor from running at program shutdown time, which would attempt to // reset the reference when the environment is no longer valid. Avoid using // this if at all possible. If you do need to use static data, MAKE SURE to // warn your users that your addon is NOT threadsafe. void SuppressDestruct(); protected: Reference(const Reference&); /// !cond INTERNAL napi_env _env; napi_ref _ref; /// !endcond private: bool _suppressDestruct; }; class ObjectReference: public Reference { public: ObjectReference(); ObjectReference(napi_env env, napi_ref ref); // A reference can be moved but cannot be copied. ObjectReference(Reference&& other); ObjectReference& operator =(Reference&& other); ObjectReference(ObjectReference&& other); ObjectReference& operator =(ObjectReference&& other); NAPI_DISALLOW_ASSIGN(ObjectReference) Napi::Value Get(const char* utf8name) const; Napi::Value Get(const std::string& utf8name) const; bool Set(const char* utf8name, napi_value value); bool Set(const char* utf8name, Napi::Value value); bool Set(const char* utf8name, const char* utf8value); bool Set(const char* utf8name, bool boolValue); bool Set(const char* utf8name, double numberValue); bool Set(const std::string& utf8name, napi_value value); bool Set(const std::string& utf8name, Napi::Value value); bool Set(const std::string& utf8name, std::string& utf8value); bool Set(const std::string& utf8name, bool boolValue); bool Set(const std::string& utf8name, double numberValue); Napi::Value Get(uint32_t index) const; bool Set(uint32_t index, const napi_value value); bool Set(uint32_t index, const Napi::Value value); bool Set(uint32_t index, const char* utf8value); bool Set(uint32_t index, const std::string& utf8value); bool Set(uint32_t index, bool boolValue); bool Set(uint32_t index, double numberValue); protected: ObjectReference(const ObjectReference&); }; class FunctionReference: public Reference { public: FunctionReference(); FunctionReference(napi_env env, napi_ref ref); // A reference can be moved but cannot be copied. FunctionReference(Reference&& other); FunctionReference& operator =(Reference&& other); FunctionReference(FunctionReference&& other); FunctionReference& operator =(FunctionReference&& other); NAPI_DISALLOW_ASSIGN_COPY(FunctionReference) Napi::Value operator ()(const std::initializer_list& args) const; Napi::Value Call(const std::initializer_list& args) const; Napi::Value Call(const std::vector& args) const; Napi::Value Call(napi_value recv, const std::initializer_list& args) const; Napi::Value Call(napi_value recv, const std::vector& args) const; Napi::Value Call(napi_value recv, size_t argc, const napi_value* args) const; Napi::Value MakeCallback(napi_value recv, const std::initializer_list& args, napi_async_context context = nullptr) const; Napi::Value MakeCallback(napi_value recv, const std::vector& args, napi_async_context context = nullptr) const; Napi::Value MakeCallback(napi_value recv, size_t argc, const napi_value* args, napi_async_context context = nullptr) const; Object New(const std::initializer_list& args) const; Object New(const std::vector& args) const; }; // Shortcuts to creating a new reference with inferred type and refcount = 0. template Reference Weak(T value); ObjectReference Weak(Object value); FunctionReference Weak(Function value); // Shortcuts to creating a new reference with inferred type and refcount = 1. template Reference Persistent(T value); ObjectReference Persistent(Object value); FunctionReference Persistent(Function value); /// A persistent reference to a JavaScript error object. Use of this class /// depends somewhat on whether C++ exceptions are enabled at compile time. /// /// ### Handling Errors With C++ Exceptions /// /// If C++ exceptions are enabled, then the `Error` class extends /// `std::exception` and enables integrated error-handling for C++ exceptions /// and JavaScript exceptions. /// /// If a Node-API call fails without executing any JavaScript code (for /// example due to an invalid argument), then the Node-API wrapper /// automatically converts and throws the error as a C++ exception of type /// `Napi::Error`. Or if a JavaScript function called by C++ code via Node-API /// throws a JavaScript exception, then the Node-API wrapper automatically /// converts and throws it as a C++ exception of type `Napi::Error`. /// /// If a C++ exception of type `Napi::Error` escapes from a Node-API C++ /// callback, then the Node-API wrapper automatically converts and throws it /// as a JavaScript exception. Therefore, catching a C++ exception of type /// `Napi::Error` prevents a JavaScript exception from being thrown. /// /// #### Example 1A - Throwing a C++ exception: /// /// Napi::Env env = ... /// throw Napi::Error::New(env, "Example exception"); /// /// Following C++ statements will not be executed. The exception will bubble /// up as a C++ exception of type `Napi::Error`, until it is either caught /// while still in C++, or else automatically propataged as a JavaScript /// exception when the callback returns to JavaScript. /// /// #### Example 2A - Propagating a Node-API C++ exception: /// /// Napi::Function jsFunctionThatThrows = someObj.As(); /// Napi::Value result = jsFunctionThatThrows({ arg1, arg2 }); /// /// Following C++ statements will not be executed. The exception will bubble /// up as a C++ exception of type `Napi::Error`, until it is either caught /// while still in C++, or else automatically propagated as a JavaScript /// exception when the callback returns to JavaScript. /// /// #### Example 3A - Handling a Node-API C++ exception: /// /// Napi::Function jsFunctionThatThrows = someObj.As(); /// Napi::Value result; /// try { /// result = jsFunctionThatThrows({ arg1, arg2 }); /// } catch (const Napi::Error& e) { /// cerr << "Caught JavaScript exception: " + e.what(); /// } /// /// Since the exception was caught here, it will not be propagated as a /// JavaScript exception. /// /// ### Handling Errors Without C++ Exceptions /// /// If C++ exceptions are disabled (by defining `NAPI_DISABLE_CPP_EXCEPTIONS`) /// then this class does not extend `std::exception`, and APIs in the `Napi` /// namespace do not throw C++ exceptions when they fail. Instead, they raise /// _pending_ JavaScript exceptions and return _empty_ `Value`s. Calling code /// should check `Value::IsEmpty()` before attempting to use a returned value, /// and may use methods on the `Env` class to check for, get, and clear a /// pending JavaScript exception. If the pending exception is not cleared, it /// will be thrown when the native callback returns to JavaScript. /// /// #### Example 1B - Throwing a JS exception /// /// Napi::Env env = ... /// Napi::Error::New(env, "Example /// exception").ThrowAsJavaScriptException(); return; /// /// After throwing a JS exception, the code should generally return /// immediately from the native callback, after performing any necessary /// cleanup. /// /// #### Example 2B - Propagating a Node-API JS exception: /// /// Napi::Function jsFunctionThatThrows = someObj.As(); /// Napi::Value result = jsFunctionThatThrows({ arg1, arg2 }); /// if (result.IsEmpty()) return; /// /// An empty value result from a Node-API call indicates an error occurred, /// and a JavaScript exception is pending. To let the exception propagate, the /// code should generally return immediately from the native callback, after /// performing any necessary cleanup. /// /// #### Example 3B - Handling a Node-API JS exception: /// /// Napi::Function jsFunctionThatThrows = someObj.As(); /// Napi::Value result = jsFunctionThatThrows({ arg1, arg2 }); /// if (result.IsEmpty()) { /// Napi::Error e = env.GetAndClearPendingException(); /// cerr << "Caught JavaScript exception: " + e.Message(); /// } /// /// Since the exception was cleared here, it will not be propagated as a /// JavaScript exception after the native callback returns. class Error : public ObjectReference #ifdef NAPI_CPP_EXCEPTIONS , public std::exception #endif // NAPI_CPP_EXCEPTIONS { public: static Error New(napi_env env); static Error New(napi_env env, const char* message); static Error New(napi_env env, const std::string& message); static NAPI_NO_RETURN void Fatal(const char* location, const char* message); Error(); Error(napi_env env, napi_value value); // An error can be moved or copied. Error(Error&& other); Error& operator =(Error&& other); Error(const Error&); Error& operator =(const Error&); const std::string& Message() const NAPI_NOEXCEPT; void ThrowAsJavaScriptException() const; #ifdef NAPI_CPP_EXCEPTIONS const char* what() const NAPI_NOEXCEPT override; #endif // NAPI_CPP_EXCEPTIONS protected: /// !cond INTERNAL using create_error_fn = napi_status (*)(napi_env envb, napi_value code, napi_value msg, napi_value* result); template static TError New(napi_env env, const char* message, size_t length, create_error_fn create_error); /// !endcond private: mutable std::string _message; }; class TypeError : public Error { public: static TypeError New(napi_env env, const char* message); static TypeError New(napi_env env, const std::string& message); TypeError(); TypeError(napi_env env, napi_value value); }; class RangeError : public Error { public: static RangeError New(napi_env env, const char* message); static RangeError New(napi_env env, const std::string& message); RangeError(); RangeError(napi_env env, napi_value value); }; class CallbackInfo { public: CallbackInfo(napi_env env, napi_callback_info info); ~CallbackInfo(); // Disallow copying to prevent multiple free of _dynamicArgs NAPI_DISALLOW_ASSIGN_COPY(CallbackInfo) Napi::Env Env() const; Value NewTarget() const; bool IsConstructCall() const; size_t Length() const; const Value operator [](size_t index) const; Value This() const; void* Data() const; void SetData(void* data); private: const size_t _staticArgCount = 6; napi_env _env; napi_callback_info _info; napi_value _this; size_t _argc; napi_value* _argv; napi_value _staticArgs[6]; napi_value* _dynamicArgs; void* _data; }; class PropertyDescriptor { public: using GetterCallback = Napi::Value (*)(const Napi::CallbackInfo& info); using SetterCallback = void (*)(const Napi::CallbackInfo& info); #ifndef NODE_ADDON_API_DISABLE_DEPRECATED template static PropertyDescriptor Accessor(const char* utf8name, Getter getter, napi_property_attributes attributes = napi_default, void* data = nullptr); template static PropertyDescriptor Accessor(const std::string& utf8name, Getter getter, napi_property_attributes attributes = napi_default, void* data = nullptr); template static PropertyDescriptor Accessor(napi_value name, Getter getter, napi_property_attributes attributes = napi_default, void* data = nullptr); template static PropertyDescriptor Accessor(Name name, Getter getter, napi_property_attributes attributes = napi_default, void* data = nullptr); template static PropertyDescriptor Accessor(const char* utf8name, Getter getter, Setter setter, napi_property_attributes attributes = napi_default, void* data = nullptr); template static PropertyDescriptor Accessor(const std::string& utf8name, Getter getter, Setter setter, napi_property_attributes attributes = napi_default, void* data = nullptr); template static PropertyDescriptor Accessor(napi_value name, Getter getter, Setter setter, napi_property_attributes attributes = napi_default, void* data = nullptr); template static PropertyDescriptor Accessor(Name name, Getter getter, Setter setter, napi_property_attributes attributes = napi_default, void* data = nullptr); template static PropertyDescriptor Function(const char* utf8name, Callable cb, napi_property_attributes attributes = napi_default, void* data = nullptr); template static PropertyDescriptor Function(const std::string& utf8name, Callable cb, napi_property_attributes attributes = napi_default, void* data = nullptr); template static PropertyDescriptor Function(napi_value name, Callable cb, napi_property_attributes attributes = napi_default, void* data = nullptr); template static PropertyDescriptor Function(Name name, Callable cb, napi_property_attributes attributes = napi_default, void* data = nullptr); #endif // !NODE_ADDON_API_DISABLE_DEPRECATED template static PropertyDescriptor Accessor(const char* utf8name, napi_property_attributes attributes = napi_default, void* data = nullptr); template static PropertyDescriptor Accessor(const std::string& utf8name, napi_property_attributes attributes = napi_default, void* data = nullptr); template static PropertyDescriptor Accessor(Name name, napi_property_attributes attributes = napi_default, void* data = nullptr); template static PropertyDescriptor Accessor(const char* utf8name, napi_property_attributes attributes = napi_default, void* data = nullptr); template static PropertyDescriptor Accessor(const std::string& utf8name, napi_property_attributes attributes = napi_default, void* data = nullptr); template static PropertyDescriptor Accessor(Name name, napi_property_attributes attributes = napi_default, void* data = nullptr); template static PropertyDescriptor Accessor(Napi::Env env, Napi::Object object, const char* utf8name, Getter getter, napi_property_attributes attributes = napi_default, void* data = nullptr); template static PropertyDescriptor Accessor(Napi::Env env, Napi::Object object, const std::string& utf8name, Getter getter, napi_property_attributes attributes = napi_default, void* data = nullptr); template static PropertyDescriptor Accessor(Napi::Env env, Napi::Object object, Name name, Getter getter, napi_property_attributes attributes = napi_default, void* data = nullptr); template static PropertyDescriptor Accessor(Napi::Env env, Napi::Object object, const char* utf8name, Getter getter, Setter setter, napi_property_attributes attributes = napi_default, void* data = nullptr); template static PropertyDescriptor Accessor(Napi::Env env, Napi::Object object, const std::string& utf8name, Getter getter, Setter setter, napi_property_attributes attributes = napi_default, void* data = nullptr); template static PropertyDescriptor Accessor(Napi::Env env, Napi::Object object, Name name, Getter getter, Setter setter, napi_property_attributes attributes = napi_default, void* data = nullptr); template static PropertyDescriptor Function(Napi::Env env, Napi::Object object, const char* utf8name, Callable cb, napi_property_attributes attributes = napi_default, void* data = nullptr); template static PropertyDescriptor Function(Napi::Env env, Napi::Object object, const std::string& utf8name, Callable cb, napi_property_attributes attributes = napi_default, void* data = nullptr); template static PropertyDescriptor Function(Napi::Env env, Napi::Object object, Name name, Callable cb, napi_property_attributes attributes = napi_default, void* data = nullptr); static PropertyDescriptor Value(const char* utf8name, napi_value value, napi_property_attributes attributes = napi_default); static PropertyDescriptor Value(const std::string& utf8name, napi_value value, napi_property_attributes attributes = napi_default); static PropertyDescriptor Value(napi_value name, napi_value value, napi_property_attributes attributes = napi_default); static PropertyDescriptor Value(Name name, Napi::Value value, napi_property_attributes attributes = napi_default); PropertyDescriptor(napi_property_descriptor desc); operator napi_property_descriptor&(); operator const napi_property_descriptor&() const; private: napi_property_descriptor _desc; }; /// Property descriptor for use with `ObjectWrap::DefineClass()`. /// /// This is different from the standalone `PropertyDescriptor` because it is specific to each /// `ObjectWrap` subclass. This prevents using descriptors from a different class when /// defining a new class (preventing the callbacks from having incorrect `this` pointers). template class ClassPropertyDescriptor { public: ClassPropertyDescriptor(napi_property_descriptor desc) : _desc(desc) {} operator napi_property_descriptor&() { return _desc; } operator const napi_property_descriptor&() const { return _desc; } private: napi_property_descriptor _desc; }; template struct MethodCallbackData { TCallback callback; void* data; }; template struct AccessorCallbackData { TGetterCallback getterCallback; TSetterCallback setterCallback; void* data; }; template class InstanceWrap { public: using InstanceVoidMethodCallback = void (T::*)(const CallbackInfo& info); using InstanceMethodCallback = Napi::Value (T::*)(const CallbackInfo& info); using InstanceGetterCallback = Napi::Value (T::*)(const CallbackInfo& info); using InstanceSetterCallback = void (T::*)(const CallbackInfo& info, const Napi::Value& value); using PropertyDescriptor = ClassPropertyDescriptor; static PropertyDescriptor InstanceMethod(const char* utf8name, InstanceVoidMethodCallback method, napi_property_attributes attributes = napi_default, void* data = nullptr); static PropertyDescriptor InstanceMethod(const char* utf8name, InstanceMethodCallback method, napi_property_attributes attributes = napi_default, void* data = nullptr); static PropertyDescriptor InstanceMethod(Symbol name, InstanceVoidMethodCallback method, napi_property_attributes attributes = napi_default, void* data = nullptr); static PropertyDescriptor InstanceMethod(Symbol name, InstanceMethodCallback method, napi_property_attributes attributes = napi_default, void* data = nullptr); template static PropertyDescriptor InstanceMethod(const char* utf8name, napi_property_attributes attributes = napi_default, void* data = nullptr); template static PropertyDescriptor InstanceMethod(const char* utf8name, napi_property_attributes attributes = napi_default, void* data = nullptr); template static PropertyDescriptor InstanceMethod(Symbol name, napi_property_attributes attributes = napi_default, void* data = nullptr); template static PropertyDescriptor InstanceMethod(Symbol name, napi_property_attributes attributes = napi_default, void* data = nullptr); static PropertyDescriptor InstanceAccessor(const char* utf8name, InstanceGetterCallback getter, InstanceSetterCallback setter, napi_property_attributes attributes = napi_default, void* data = nullptr); static PropertyDescriptor InstanceAccessor(Symbol name, InstanceGetterCallback getter, InstanceSetterCallback setter, napi_property_attributes attributes = napi_default, void* data = nullptr); template static PropertyDescriptor InstanceAccessor(const char* utf8name, napi_property_attributes attributes = napi_default, void* data = nullptr); template static PropertyDescriptor InstanceAccessor(Symbol name, napi_property_attributes attributes = napi_default, void* data = nullptr); static PropertyDescriptor InstanceValue(const char* utf8name, Napi::Value value, napi_property_attributes attributes = napi_default); static PropertyDescriptor InstanceValue(Symbol name, Napi::Value value, napi_property_attributes attributes = napi_default); protected: static void AttachPropData(napi_env env, napi_value value, const napi_property_descriptor* prop); private: using This = InstanceWrap; using InstanceVoidMethodCallbackData = MethodCallbackData; using InstanceMethodCallbackData = MethodCallbackData; using InstanceAccessorCallbackData = AccessorCallbackData; static napi_value InstanceVoidMethodCallbackWrapper(napi_env env, napi_callback_info info); static napi_value InstanceMethodCallbackWrapper(napi_env env, napi_callback_info info); static napi_value InstanceGetterCallbackWrapper(napi_env env, napi_callback_info info); static napi_value InstanceSetterCallbackWrapper(napi_env env, napi_callback_info info); template static napi_value WrappedMethod(napi_env env, napi_callback_info info) NAPI_NOEXCEPT; template struct SetterTag {}; template static napi_callback WrapSetter(SetterTag) NAPI_NOEXCEPT { return &This::WrappedMethod; } static napi_callback WrapSetter(SetterTag) NAPI_NOEXCEPT { return nullptr; } }; /// Base class to be extended by C++ classes exposed to JavaScript; each C++ class instance gets /// "wrapped" by a JavaScript object that is managed by this class. /// /// At initialization time, the `DefineClass()` method must be used to /// hook up the accessor and method callbacks. It takes a list of /// property descriptors, which can be constructed via the various /// static methods on the base class. /// /// #### Example: /// /// class Example: public Napi::ObjectWrap { /// public: /// static void Initialize(Napi::Env& env, Napi::Object& target) { /// Napi::Function constructor = DefineClass(env, "Example", { /// InstanceAccessor<&Example::GetSomething, &Example::SetSomething>("value"), /// InstanceMethod<&Example::DoSomething>("doSomething"), /// }); /// target.Set("Example", constructor); /// } /// /// Example(const Napi::CallbackInfo& info); // Constructor /// Napi::Value GetSomething(const Napi::CallbackInfo& info); /// void SetSomething(const Napi::CallbackInfo& info, const Napi::Value& value); /// Napi::Value DoSomething(const Napi::CallbackInfo& info); /// } template class ObjectWrap : public InstanceWrap, public Reference { public: ObjectWrap(const CallbackInfo& callbackInfo); virtual ~ObjectWrap(); static T* Unwrap(Object wrapper); // Methods exposed to JavaScript must conform to one of these callback signatures. using StaticVoidMethodCallback = void (*)(const CallbackInfo& info); using StaticMethodCallback = Napi::Value (*)(const CallbackInfo& info); using StaticGetterCallback = Napi::Value (*)(const CallbackInfo& info); using StaticSetterCallback = void (*)(const CallbackInfo& info, const Napi::Value& value); using PropertyDescriptor = ClassPropertyDescriptor; static Function DefineClass(Napi::Env env, const char* utf8name, const std::initializer_list& properties, void* data = nullptr); static Function DefineClass(Napi::Env env, const char* utf8name, const std::vector& properties, void* data = nullptr); static PropertyDescriptor StaticMethod(const char* utf8name, StaticVoidMethodCallback method, napi_property_attributes attributes = napi_default, void* data = nullptr); static PropertyDescriptor StaticMethod(const char* utf8name, StaticMethodCallback method, napi_property_attributes attributes = napi_default, void* data = nullptr); static PropertyDescriptor StaticMethod(Symbol name, StaticVoidMethodCallback method, napi_property_attributes attributes = napi_default, void* data = nullptr); static PropertyDescriptor StaticMethod(Symbol name, StaticMethodCallback method, napi_property_attributes attributes = napi_default, void* data = nullptr); template static PropertyDescriptor StaticMethod(const char* utf8name, napi_property_attributes attributes = napi_default, void* data = nullptr); template static PropertyDescriptor StaticMethod(Symbol name, napi_property_attributes attributes = napi_default, void* data = nullptr); template static PropertyDescriptor StaticMethod(const char* utf8name, napi_property_attributes attributes = napi_default, void* data = nullptr); template static PropertyDescriptor StaticMethod(Symbol name, napi_property_attributes attributes = napi_default, void* data = nullptr); static PropertyDescriptor StaticAccessor(const char* utf8name, StaticGetterCallback getter, StaticSetterCallback setter, napi_property_attributes attributes = napi_default, void* data = nullptr); static PropertyDescriptor StaticAccessor(Symbol name, StaticGetterCallback getter, StaticSetterCallback setter, napi_property_attributes attributes = napi_default, void* data = nullptr); template static PropertyDescriptor StaticAccessor(const char* utf8name, napi_property_attributes attributes = napi_default, void* data = nullptr); template static PropertyDescriptor StaticAccessor(Symbol name, napi_property_attributes attributes = napi_default, void* data = nullptr); static PropertyDescriptor StaticValue(const char* utf8name, Napi::Value value, napi_property_attributes attributes = napi_default); static PropertyDescriptor StaticValue(Symbol name, Napi::Value value, napi_property_attributes attributes = napi_default); virtual void Finalize(Napi::Env env); private: using This = ObjectWrap; static napi_value ConstructorCallbackWrapper(napi_env env, napi_callback_info info); static napi_value StaticVoidMethodCallbackWrapper(napi_env env, napi_callback_info info); static napi_value StaticMethodCallbackWrapper(napi_env env, napi_callback_info info); static napi_value StaticGetterCallbackWrapper(napi_env env, napi_callback_info info); static napi_value StaticSetterCallbackWrapper(napi_env env, napi_callback_info info); static void FinalizeCallback(napi_env env, void* data, void* hint); static Function DefineClass(Napi::Env env, const char* utf8name, const size_t props_count, const napi_property_descriptor* props, void* data = nullptr); using StaticVoidMethodCallbackData = MethodCallbackData; using StaticMethodCallbackData = MethodCallbackData; using StaticAccessorCallbackData = AccessorCallbackData; template static napi_value WrappedMethod(napi_env env, napi_callback_info info) NAPI_NOEXCEPT; template struct StaticSetterTag {}; template static napi_callback WrapStaticSetter(StaticSetterTag) NAPI_NOEXCEPT { return &This::WrappedMethod; } static napi_callback WrapStaticSetter(StaticSetterTag) NAPI_NOEXCEPT { return nullptr; } bool _construction_failed = true; }; class HandleScope { public: HandleScope(napi_env env, napi_handle_scope scope); explicit HandleScope(Napi::Env env); ~HandleScope(); // Disallow copying to prevent double close of napi_handle_scope NAPI_DISALLOW_ASSIGN_COPY(HandleScope) operator napi_handle_scope() const; Napi::Env Env() const; private: napi_env _env; napi_handle_scope _scope; }; class EscapableHandleScope { public: EscapableHandleScope(napi_env env, napi_escapable_handle_scope scope); explicit EscapableHandleScope(Napi::Env env); ~EscapableHandleScope(); // Disallow copying to prevent double close of napi_escapable_handle_scope NAPI_DISALLOW_ASSIGN_COPY(EscapableHandleScope) operator napi_escapable_handle_scope() const; Napi::Env Env() const; Value Escape(napi_value escapee); private: napi_env _env; napi_escapable_handle_scope _scope; }; #if (NAPI_VERSION > 2) class CallbackScope { public: CallbackScope(napi_env env, napi_callback_scope scope); CallbackScope(napi_env env, napi_async_context context); virtual ~CallbackScope(); // Disallow copying to prevent double close of napi_callback_scope NAPI_DISALLOW_ASSIGN_COPY(CallbackScope) operator napi_callback_scope() const; Napi::Env Env() const; private: napi_env _env; napi_callback_scope _scope; }; #endif class AsyncContext { public: explicit AsyncContext(napi_env env, const char* resource_name); explicit AsyncContext(napi_env env, const char* resource_name, const Object& resource); virtual ~AsyncContext(); AsyncContext(AsyncContext&& other); AsyncContext& operator =(AsyncContext&& other); NAPI_DISALLOW_ASSIGN_COPY(AsyncContext) operator napi_async_context() const; Napi::Env Env() const; private: napi_env _env; napi_async_context _context; }; class AsyncWorker { public: virtual ~AsyncWorker(); // An async worker can be moved but cannot be copied. AsyncWorker(AsyncWorker&& other); AsyncWorker& operator =(AsyncWorker&& other); NAPI_DISALLOW_ASSIGN_COPY(AsyncWorker) operator napi_async_work() const; Napi::Env Env() const; void Queue(); void Cancel(); void SuppressDestruct(); ObjectReference& Receiver(); FunctionReference& Callback(); virtual void OnExecute(Napi::Env env); virtual void OnWorkComplete(Napi::Env env, napi_status status); protected: explicit AsyncWorker(const Function& callback); explicit AsyncWorker(const Function& callback, const char* resource_name); explicit AsyncWorker(const Function& callback, const char* resource_name, const Object& resource); explicit AsyncWorker(const Object& receiver, const Function& callback); explicit AsyncWorker(const Object& receiver, const Function& callback, const char* resource_name); explicit AsyncWorker(const Object& receiver, const Function& callback, const char* resource_name, const Object& resource); explicit AsyncWorker(Napi::Env env); explicit AsyncWorker(Napi::Env env, const char* resource_name); explicit AsyncWorker(Napi::Env env, const char* resource_name, const Object& resource); virtual void Execute() = 0; virtual void OnOK(); virtual void OnError(const Error& e); virtual void Destroy(); virtual std::vector GetResult(Napi::Env env); void SetError(const std::string& error); private: static inline void OnAsyncWorkExecute(napi_env env, void* asyncworker); static inline void OnAsyncWorkComplete(napi_env env, napi_status status, void* asyncworker); napi_env _env; napi_async_work _work; ObjectReference _receiver; FunctionReference _callback; std::string _error; bool _suppress_destruct; }; #if (NAPI_VERSION > 3 && !defined(__wasm32__)) class ThreadSafeFunction { public: // This API may only be called from the main thread. template static ThreadSafeFunction New(napi_env env, const Function& callback, ResourceString resourceName, size_t maxQueueSize, size_t initialThreadCount); // This API may only be called from the main thread. template static ThreadSafeFunction New(napi_env env, const Function& callback, ResourceString resourceName, size_t maxQueueSize, size_t initialThreadCount, ContextType* context); // This API may only be called from the main thread. template static ThreadSafeFunction New(napi_env env, const Function& callback, ResourceString resourceName, size_t maxQueueSize, size_t initialThreadCount, Finalizer finalizeCallback); // This API may only be called from the main thread. template static ThreadSafeFunction New(napi_env env, const Function& callback, ResourceString resourceName, size_t maxQueueSize, size_t initialThreadCount, Finalizer finalizeCallback, FinalizerDataType* data); // This API may only be called from the main thread. template static ThreadSafeFunction New(napi_env env, const Function& callback, ResourceString resourceName, size_t maxQueueSize, size_t initialThreadCount, ContextType* context, Finalizer finalizeCallback); // This API may only be called from the main thread. template static ThreadSafeFunction New(napi_env env, const Function& callback, ResourceString resourceName, size_t maxQueueSize, size_t initialThreadCount, ContextType* context, Finalizer finalizeCallback, FinalizerDataType* data); // This API may only be called from the main thread. template static ThreadSafeFunction New(napi_env env, const Function& callback, const Object& resource, ResourceString resourceName, size_t maxQueueSize, size_t initialThreadCount); // This API may only be called from the main thread. template static ThreadSafeFunction New(napi_env env, const Function& callback, const Object& resource, ResourceString resourceName, size_t maxQueueSize, size_t initialThreadCount, ContextType* context); // This API may only be called from the main thread. template static ThreadSafeFunction New(napi_env env, const Function& callback, const Object& resource, ResourceString resourceName, size_t maxQueueSize, size_t initialThreadCount, Finalizer finalizeCallback); // This API may only be called from the main thread. template static ThreadSafeFunction New(napi_env env, const Function& callback, const Object& resource, ResourceString resourceName, size_t maxQueueSize, size_t initialThreadCount, Finalizer finalizeCallback, FinalizerDataType* data); // This API may only be called from the main thread. template static ThreadSafeFunction New(napi_env env, const Function& callback, const Object& resource, ResourceString resourceName, size_t maxQueueSize, size_t initialThreadCount, ContextType* context, Finalizer finalizeCallback); // This API may only be called from the main thread. template static ThreadSafeFunction New(napi_env env, const Function& callback, const Object& resource, ResourceString resourceName, size_t maxQueueSize, size_t initialThreadCount, ContextType* context, Finalizer finalizeCallback, FinalizerDataType* data); ThreadSafeFunction(); ThreadSafeFunction(napi_threadsafe_function tsFunctionValue); operator napi_threadsafe_function() const; // This API may be called from any thread. napi_status BlockingCall() const; // This API may be called from any thread. template napi_status BlockingCall(Callback callback) const; // This API may be called from any thread. template napi_status BlockingCall(DataType* data, Callback callback) const; // This API may be called from any thread. napi_status NonBlockingCall() const; // This API may be called from any thread. template napi_status NonBlockingCall(Callback callback) const; // This API may be called from any thread. template napi_status NonBlockingCall(DataType* data, Callback callback) const; // This API may only be called from the main thread. void Ref(napi_env env) const; // This API may only be called from the main thread. void Unref(napi_env env) const; // This API may be called from any thread. napi_status Acquire() const; // This API may be called from any thread. napi_status Release(); // This API may be called from any thread. napi_status Abort(); struct ConvertibleContext { template operator T*() { return static_cast(context); } void* context; }; // This API may be called from any thread. ConvertibleContext GetContext() const; private: using CallbackWrapper = std::function; template static ThreadSafeFunction New(napi_env env, const Function& callback, const Object& resource, ResourceString resourceName, size_t maxQueueSize, size_t initialThreadCount, ContextType* context, Finalizer finalizeCallback, FinalizerDataType* data, napi_finalize wrapper); napi_status CallInternal(CallbackWrapper* callbackWrapper, napi_threadsafe_function_call_mode mode) const; static void CallJS(napi_env env, napi_value jsCallback, void* context, void* data); napi_threadsafe_function _tsfn; }; // A TypedThreadSafeFunction by default has no context (nullptr) and can // accept any type (void) to its CallJs. template class TypedThreadSafeFunction { public: // This API may only be called from the main thread. // Helper function that returns nullptr if running Node-API 5+, otherwise a // non-empty, no-op Function. This provides the ability to specify at // compile-time a callback parameter to `New` that safely does no action // when targeting _any_ Node-API version. #if NAPI_VERSION > 4 static std::nullptr_t EmptyFunctionFactory(Napi::Env env); #else static Napi::Function EmptyFunctionFactory(Napi::Env env); #endif static Napi::Function FunctionOrEmpty(Napi::Env env, Napi::Function& callback); #if NAPI_VERSION > 4 // This API may only be called from the main thread. // Creates a new threadsafe function with: // Callback [missing] Resource [missing] Finalizer [missing] template static TypedThreadSafeFunction New( napi_env env, ResourceString resourceName, size_t maxQueueSize, size_t initialThreadCount, ContextType* context = nullptr); // This API may only be called from the main thread. // Creates a new threadsafe function with: // Callback [missing] Resource [passed] Finalizer [missing] template static TypedThreadSafeFunction New( napi_env env, const Object& resource, ResourceString resourceName, size_t maxQueueSize, size_t initialThreadCount, ContextType* context = nullptr); // This API may only be called from the main thread. // Creates a new threadsafe function with: // Callback [missing] Resource [missing] Finalizer [passed] template static TypedThreadSafeFunction New( napi_env env, ResourceString resourceName, size_t maxQueueSize, size_t initialThreadCount, ContextType* context, Finalizer finalizeCallback, FinalizerDataType* data = nullptr); // This API may only be called from the main thread. // Creates a new threadsafe function with: // Callback [missing] Resource [passed] Finalizer [passed] template static TypedThreadSafeFunction New( napi_env env, const Object& resource, ResourceString resourceName, size_t maxQueueSize, size_t initialThreadCount, ContextType* context, Finalizer finalizeCallback, FinalizerDataType* data = nullptr); #endif // This API may only be called from the main thread. // Creates a new threadsafe function with: // Callback [passed] Resource [missing] Finalizer [missing] template static TypedThreadSafeFunction New( napi_env env, const Function& callback, ResourceString resourceName, size_t maxQueueSize, size_t initialThreadCount, ContextType* context = nullptr); // This API may only be called from the main thread. // Creates a new threadsafe function with: // Callback [passed] Resource [passed] Finalizer [missing] template static TypedThreadSafeFunction New( napi_env env, const Function& callback, const Object& resource, ResourceString resourceName, size_t maxQueueSize, size_t initialThreadCount, ContextType* context = nullptr); // This API may only be called from the main thread. // Creates a new threadsafe function with: // Callback [passed] Resource [missing] Finalizer [passed] template static TypedThreadSafeFunction New( napi_env env, const Function& callback, ResourceString resourceName, size_t maxQueueSize, size_t initialThreadCount, ContextType* context, Finalizer finalizeCallback, FinalizerDataType* data = nullptr); // This API may only be called from the main thread. // Creates a new threadsafe function with: // Callback [passed] Resource [passed] Finalizer [passed] template static TypedThreadSafeFunction New( napi_env env, CallbackType callback, const Object& resource, ResourceString resourceName, size_t maxQueueSize, size_t initialThreadCount, ContextType* context, Finalizer finalizeCallback, FinalizerDataType* data = nullptr); TypedThreadSafeFunction(); TypedThreadSafeFunction(napi_threadsafe_function tsFunctionValue); operator napi_threadsafe_function() const; // This API may be called from any thread. napi_status BlockingCall(DataType* data = nullptr) const; // This API may be called from any thread. napi_status NonBlockingCall(DataType* data = nullptr) const; // This API may only be called from the main thread. void Ref(napi_env env) const; // This API may only be called from the main thread. void Unref(napi_env env) const; // This API may be called from any thread. napi_status Acquire() const; // This API may be called from any thread. napi_status Release(); // This API may be called from any thread. napi_status Abort(); // This API may be called from any thread. ContextType* GetContext() const; private: template static TypedThreadSafeFunction New( napi_env env, const Function& callback, const Object& resource, ResourceString resourceName, size_t maxQueueSize, size_t initialThreadCount, ContextType* context, Finalizer finalizeCallback, FinalizerDataType* data, napi_finalize wrapper); static void CallJsInternal(napi_env env, napi_value jsCallback, void* context, void* data); protected: napi_threadsafe_function _tsfn; }; template class AsyncProgressWorkerBase : public AsyncWorker { public: virtual void OnWorkProgress(DataType* data) = 0; class ThreadSafeData { public: ThreadSafeData(AsyncProgressWorkerBase* asyncprogressworker, DataType* data) : _asyncprogressworker(asyncprogressworker), _data(data) {} AsyncProgressWorkerBase* asyncprogressworker() { return _asyncprogressworker; }; DataType* data() { return _data; }; private: AsyncProgressWorkerBase* _asyncprogressworker; DataType* _data; }; void OnWorkComplete(Napi::Env env, napi_status status) override; protected: explicit AsyncProgressWorkerBase(const Object& receiver, const Function& callback, const char* resource_name, const Object& resource, size_t queue_size = 1); virtual ~AsyncProgressWorkerBase(); // Optional callback of Napi::ThreadSafeFunction only available after NAPI_VERSION 4. // Refs: https://github.com/nodejs/node/pull/27791 #if NAPI_VERSION > 4 explicit AsyncProgressWorkerBase(Napi::Env env, const char* resource_name, const Object& resource, size_t queue_size = 1); #endif static inline void OnAsyncWorkProgress(Napi::Env env, Napi::Function jsCallback, void* data); napi_status NonBlockingCall(DataType* data); private: ThreadSafeFunction _tsfn; bool _work_completed = false; napi_status _complete_status; static inline void OnThreadSafeFunctionFinalize(Napi::Env env, void* data, AsyncProgressWorkerBase* context); }; template class AsyncProgressWorker : public AsyncProgressWorkerBase { public: virtual ~AsyncProgressWorker(); class ExecutionProgress { friend class AsyncProgressWorker; public: void Signal() const; void Send(const T* data, size_t count) const; private: explicit ExecutionProgress(AsyncProgressWorker* worker) : _worker(worker) {} AsyncProgressWorker* const _worker; }; void OnWorkProgress(void*) override; protected: explicit AsyncProgressWorker(const Function& callback); explicit AsyncProgressWorker(const Function& callback, const char* resource_name); explicit AsyncProgressWorker(const Function& callback, const char* resource_name, const Object& resource); explicit AsyncProgressWorker(const Object& receiver, const Function& callback); explicit AsyncProgressWorker(const Object& receiver, const Function& callback, const char* resource_name); explicit AsyncProgressWorker(const Object& receiver, const Function& callback, const char* resource_name, const Object& resource); // Optional callback of Napi::ThreadSafeFunction only available after NAPI_VERSION 4. // Refs: https://github.com/nodejs/node/pull/27791 #if NAPI_VERSION > 4 explicit AsyncProgressWorker(Napi::Env env); explicit AsyncProgressWorker(Napi::Env env, const char* resource_name); explicit AsyncProgressWorker(Napi::Env env, const char* resource_name, const Object& resource); #endif virtual void Execute(const ExecutionProgress& progress) = 0; virtual void OnProgress(const T* data, size_t count) = 0; private: void Execute() override; void Signal() const; void SendProgress_(const T* data, size_t count); std::mutex _mutex; T* _asyncdata; size_t _asyncsize; }; template class AsyncProgressQueueWorker : public AsyncProgressWorkerBase> { public: virtual ~AsyncProgressQueueWorker() {}; class ExecutionProgress { friend class AsyncProgressQueueWorker; public: void Signal() const; void Send(const T* data, size_t count) const; private: explicit ExecutionProgress(AsyncProgressQueueWorker* worker) : _worker(worker) {} AsyncProgressQueueWorker* const _worker; }; void OnWorkComplete(Napi::Env env, napi_status status) override; void OnWorkProgress(std::pair*) override; protected: explicit AsyncProgressQueueWorker(const Function& callback); explicit AsyncProgressQueueWorker(const Function& callback, const char* resource_name); explicit AsyncProgressQueueWorker(const Function& callback, const char* resource_name, const Object& resource); explicit AsyncProgressQueueWorker(const Object& receiver, const Function& callback); explicit AsyncProgressQueueWorker(const Object& receiver, const Function& callback, const char* resource_name); explicit AsyncProgressQueueWorker(const Object& receiver, const Function& callback, const char* resource_name, const Object& resource); // Optional callback of Napi::ThreadSafeFunction only available after NAPI_VERSION 4. // Refs: https://github.com/nodejs/node/pull/27791 #if NAPI_VERSION > 4 explicit AsyncProgressQueueWorker(Napi::Env env); explicit AsyncProgressQueueWorker(Napi::Env env, const char* resource_name); explicit AsyncProgressQueueWorker(Napi::Env env, const char* resource_name, const Object& resource); #endif virtual void Execute(const ExecutionProgress& progress) = 0; virtual void OnProgress(const T* data, size_t count) = 0; private: void Execute() override; void Signal() const; void SendProgress_(const T* data, size_t count); }; #endif // NAPI_VERSION > 3 && !defined(__wasm32__) // Memory management. class MemoryManagement { public: static int64_t AdjustExternalMemory(Env env, int64_t change_in_bytes); }; // Version management class VersionManagement { public: static uint32_t GetNapiVersion(Env env); static const napi_node_version* GetNodeVersion(Env env); }; #if NAPI_VERSION > 5 template class Addon : public InstanceWrap { public: static inline Object Init(Env env, Object exports); static T* Unwrap(Object wrapper); protected: using AddonProp = ClassPropertyDescriptor; void DefineAddon(Object exports, const std::initializer_list& props); Napi::Object DefineProperties(Object object, const std::initializer_list& props); private: Object entry_point_; }; #endif // NAPI_VERSION > 5 } // namespace Napi // Inline implementations of all the above class methods are included here. // #include "napi-inl.h" // hack-sqlite // #endif // SRC_NAPI_H_ /* file https://github.com/nodejs/node-addon-api/blob/4.0.0/napi-inl.h */ #ifndef SRC_NAPI_INL_H_ #define SRC_NAPI_INL_H_ //////////////////////////////////////////////////////////////////////////////// // Node-API C++ Wrapper Classes // // Inline header-only implementations for "Node-API" ABI-stable C APIs for // Node.js. //////////////////////////////////////////////////////////////////////////////// // Note: Do not include this file directly! Include "napi.h" instead. // #include // #include // #include // #include namespace Napi { // Helpers to handle functions exposed from C++. namespace details { // Attach a data item to an object and delete it when the object gets // garbage-collected. // TODO: Replace this code with `napi_add_finalizer()` whenever it becomes // available on all supported versions of Node.js. template static inline napi_status AttachData(napi_env env, napi_value obj, FreeType* data, napi_finalize finalizer = nullptr, void* hint = nullptr) { napi_status status; if (finalizer == nullptr) { finalizer = [](napi_env /*env*/, void* data, void* /*hint*/) { delete static_cast(data); }; } #if (NAPI_VERSION < 5) napi_value symbol, external; status = napi_create_symbol(env, nullptr, &symbol); if (status == napi_ok) { status = napi_create_external(env, data, finalizer, hint, &external); if (status == napi_ok) { napi_property_descriptor desc = { nullptr, symbol, nullptr, nullptr, nullptr, external, napi_default, nullptr }; status = napi_define_properties(env, obj, 1, &desc); } } #else // NAPI_VERSION >= 5 status = napi_add_finalizer(env, obj, data, finalizer, hint, nullptr); #endif return status; } // For use in JS to C++ callback wrappers to catch any Napi::Error exceptions // and rethrow them as JavaScript exceptions before returning from the callback. template inline napi_value WrapCallback(Callable callback) { #ifdef NAPI_CPP_EXCEPTIONS try { return callback(); } catch (const Error& e) { e.ThrowAsJavaScriptException(); return nullptr; } #else // NAPI_CPP_EXCEPTIONS // When C++ exceptions are disabled, errors are immediately thrown as JS // exceptions, so there is no need to catch and rethrow them here. return callback(); #endif // NAPI_CPP_EXCEPTIONS } // For use in JS to C++ void callback wrappers to catch any Napi::Error // exceptions and rethrow them as JavaScript exceptions before returning from the // callback. template inline void WrapVoidCallback(Callable callback) { #ifdef NAPI_CPP_EXCEPTIONS try { callback(); } catch (const Error& e) { e.ThrowAsJavaScriptException(); } #else // NAPI_CPP_EXCEPTIONS // When C++ exceptions are disabled, errors are immediately thrown as JS // exceptions, so there is no need to catch and rethrow them here. callback(); #endif // NAPI_CPP_EXCEPTIONS } template struct CallbackData { static inline napi_value Wrapper(napi_env env, napi_callback_info info) { return details::WrapCallback([&] { CallbackInfo callbackInfo(env, info); CallbackData* callbackData = static_cast(callbackInfo.Data()); callbackInfo.SetData(callbackData->data); return callbackData->callback(callbackInfo); }); } Callable callback; void* data; }; template struct CallbackData { static inline napi_value Wrapper(napi_env env, napi_callback_info info) { return details::WrapCallback([&] { CallbackInfo callbackInfo(env, info); CallbackData* callbackData = static_cast(callbackInfo.Data()); callbackInfo.SetData(callbackData->data); callbackData->callback(callbackInfo); return nullptr; }); } Callable callback; void* data; }; template static napi_value TemplatedVoidCallback(napi_env env, napi_callback_info info) NAPI_NOEXCEPT { return details::WrapCallback([&] { CallbackInfo cbInfo(env, info); Callback(cbInfo); return nullptr; }); } template static napi_value TemplatedCallback(napi_env env, napi_callback_info info) NAPI_NOEXCEPT { return details::WrapCallback([&] { CallbackInfo cbInfo(env, info); return Callback(cbInfo); }); } template static napi_value TemplatedInstanceCallback(napi_env env, napi_callback_info info) NAPI_NOEXCEPT { return details::WrapCallback([&] { CallbackInfo cbInfo(env, info); T* instance = T::Unwrap(cbInfo.This().As()); return (instance->*UnwrapCallback)(cbInfo); }); } template static napi_value TemplatedInstanceVoidCallback(napi_env env, napi_callback_info info) NAPI_NOEXCEPT { return details::WrapCallback([&] { CallbackInfo cbInfo(env, info); T* instance = T::Unwrap(cbInfo.This().As()); (instance->*UnwrapCallback)(cbInfo); return nullptr; }); } template struct FinalizeData { static inline void Wrapper(napi_env env, void* data, void* finalizeHint) NAPI_NOEXCEPT { WrapVoidCallback([&] { FinalizeData* finalizeData = static_cast(finalizeHint); finalizeData->callback(Env(env), static_cast(data)); delete finalizeData; }); } static inline void WrapperWithHint(napi_env env, void* data, void* finalizeHint) NAPI_NOEXCEPT { WrapVoidCallback([&] { FinalizeData* finalizeData = static_cast(finalizeHint); finalizeData->callback(Env(env), static_cast(data), finalizeData->hint); delete finalizeData; }); } Finalizer callback; Hint* hint; }; #if (NAPI_VERSION > 3 && !defined(__wasm32__)) template , typename FinalizerDataType=void> struct ThreadSafeFinalize { static inline void Wrapper(napi_env env, void* rawFinalizeData, void* /* rawContext */) { if (rawFinalizeData == nullptr) return; ThreadSafeFinalize* finalizeData = static_cast(rawFinalizeData); finalizeData->callback(Env(env)); delete finalizeData; } static inline void FinalizeWrapperWithData(napi_env env, void* rawFinalizeData, void* /* rawContext */) { if (rawFinalizeData == nullptr) return; ThreadSafeFinalize* finalizeData = static_cast(rawFinalizeData); finalizeData->callback(Env(env), finalizeData->data); delete finalizeData; } static inline void FinalizeWrapperWithContext(napi_env env, void* rawFinalizeData, void* rawContext) { if (rawFinalizeData == nullptr) return; ThreadSafeFinalize* finalizeData = static_cast(rawFinalizeData); finalizeData->callback(Env(env), static_cast(rawContext)); delete finalizeData; } static inline void FinalizeFinalizeWrapperWithDataAndContext(napi_env env, void* rawFinalizeData, void* rawContext) { if (rawFinalizeData == nullptr) return; ThreadSafeFinalize* finalizeData = static_cast(rawFinalizeData); finalizeData->callback(Env(env), finalizeData->data, static_cast(rawContext)); delete finalizeData; } FinalizerDataType* data; Finalizer callback; }; template typename std::enable_if::type static inline CallJsWrapper( napi_env env, napi_value jsCallback, void* context, void* data) { call(env, Function(env, jsCallback), static_cast(context), static_cast(data)); } template typename std::enable_if::type static inline CallJsWrapper( napi_env env, napi_value jsCallback, void* /*context*/, void* /*data*/) { if (jsCallback != nullptr) { Function(env, jsCallback).Call(0, nullptr); } } #if NAPI_VERSION > 4 template napi_value DefaultCallbackWrapper(napi_env /*env*/, std::nullptr_t /*cb*/) { return nullptr; } template napi_value DefaultCallbackWrapper(napi_env /*env*/, Napi::Function cb) { return cb; } #else template napi_value DefaultCallbackWrapper(napi_env env, Napi::Function cb) { if (cb.IsEmpty()) { return TSFN::EmptyFunctionFactory(env); } return cb; } #endif // NAPI_VERSION > 4 #endif // NAPI_VERSION > 3 && !defined(__wasm32__) template struct AccessorCallbackData { static inline napi_value GetterWrapper(napi_env env, napi_callback_info info) { return details::WrapCallback([&] { CallbackInfo callbackInfo(env, info); AccessorCallbackData* callbackData = static_cast(callbackInfo.Data()); callbackInfo.SetData(callbackData->data); return callbackData->getterCallback(callbackInfo); }); } static inline napi_value SetterWrapper(napi_env env, napi_callback_info info) { return details::WrapCallback([&] { CallbackInfo callbackInfo(env, info); AccessorCallbackData* callbackData = static_cast(callbackInfo.Data()); callbackInfo.SetData(callbackData->data); callbackData->setterCallback(callbackInfo); return nullptr; }); } Getter getterCallback; Setter setterCallback; void* data; }; } // namespace details #ifndef NODE_ADDON_API_DISABLE_DEPRECATED // hack-sqlite // # include "napi-inl.deprecated.h" #endif // !NODE_ADDON_API_DISABLE_DEPRECATED //////////////////////////////////////////////////////////////////////////////// // Module registration //////////////////////////////////////////////////////////////////////////////// // Register an add-on based on an initializer function. #define NODE_API_MODULE(modname, regfunc) \ static napi_value __napi_##regfunc(napi_env env, napi_value exports) { \ return Napi::RegisterModule(env, exports, regfunc); \ } \ NAPI_MODULE(modname, __napi_##regfunc) // Register an add-on based on a subclass of `Addon` with a custom Node.js // module name. #define NODE_API_NAMED_ADDON(modname, classname) \ static napi_value __napi_ ## classname(napi_env env, \ napi_value exports) { \ return Napi::RegisterModule(env, exports, &classname::Init); \ } \ NAPI_MODULE(modname, __napi_ ## classname) // Register an add-on based on a subclass of `Addon` with the Node.js module // name given by node-gyp from the `target_name` in binding.gyp. #define NODE_API_ADDON(classname) \ NODE_API_NAMED_ADDON(NODE_GYP_MODULE_NAME, classname) // Adapt the NAPI_MODULE registration function: // - Wrap the arguments in NAPI wrappers. // - Catch any NAPI errors and rethrow as JS exceptions. inline napi_value RegisterModule(napi_env env, napi_value exports, ModuleRegisterCallback registerCallback) { return details::WrapCallback([&] { return napi_value(registerCallback(Napi::Env(env), Napi::Object(env, exports))); }); } //////////////////////////////////////////////////////////////////////////////// // Env class //////////////////////////////////////////////////////////////////////////////// inline Env::Env(napi_env env) : _env(env) { } inline Env::operator napi_env() const { return _env; } inline Object Env::Global() const { napi_value value; napi_status status = napi_get_global(*this, &value); NAPI_THROW_IF_FAILED(*this, status, Object()); return Object(*this, value); } inline Value Env::Undefined() const { napi_value value; napi_status status = napi_get_undefined(*this, &value); NAPI_THROW_IF_FAILED(*this, status, Value()); return Value(*this, value); } inline Value Env::Null() const { napi_value value; napi_status status = napi_get_null(*this, &value); NAPI_THROW_IF_FAILED(*this, status, Value()); return Value(*this, value); } inline bool Env::IsExceptionPending() const { bool result; napi_status status = napi_is_exception_pending(_env, &result); if (status != napi_ok) result = false; // Checking for a pending exception shouldn't throw. return result; } inline Error Env::GetAndClearPendingException() { napi_value value; napi_status status = napi_get_and_clear_last_exception(_env, &value); if (status != napi_ok) { // Don't throw another exception when failing to get the exception! return Error(); } return Error(_env, value); } inline Value Env::RunScript(const char* utf8script) { String script = String::New(_env, utf8script); return RunScript(script); } inline Value Env::RunScript(const std::string& utf8script) { return RunScript(utf8script.c_str()); } inline Value Env::RunScript(String script) { napi_value result; napi_status status = napi_run_script(_env, script, &result); NAPI_THROW_IF_FAILED(_env, status, Undefined()); return Value(_env, result); } #if NAPI_VERSION > 5 template fini> inline void Env::SetInstanceData(T* data) { napi_status status = napi_set_instance_data(_env, data, [](napi_env env, void* data, void*) { fini(env, static_cast(data)); }, nullptr); NAPI_THROW_IF_FAILED_VOID(_env, status); } template fini> inline void Env::SetInstanceData(DataType* data, HintType* hint) { napi_status status = napi_set_instance_data(_env, data, [](napi_env env, void* data, void* hint) { fini(env, static_cast(data), static_cast(hint)); }, hint); NAPI_THROW_IF_FAILED_VOID(_env, status); } template inline T* Env::GetInstanceData() { void* data = nullptr; napi_status status = napi_get_instance_data(_env, &data); NAPI_THROW_IF_FAILED(_env, status, nullptr); return static_cast(data); } template void Env::DefaultFini(Env, T* data) { delete data; } template void Env::DefaultFiniWithHint(Env, DataType* data, HintType*) { delete data; } #endif // NAPI_VERSION > 5 //////////////////////////////////////////////////////////////////////////////// // Value class //////////////////////////////////////////////////////////////////////////////// inline Value::Value() : _env(nullptr), _value(nullptr) { } inline Value::Value(napi_env env, napi_value value) : _env(env), _value(value) { } inline Value::operator napi_value() const { return _value; } inline bool Value::operator ==(const Value& other) const { return StrictEquals(other); } inline bool Value::operator !=(const Value& other) const { return !this->operator ==(other); } inline bool Value::StrictEquals(const Value& other) const { bool result; napi_status status = napi_strict_equals(_env, *this, other, &result); NAPI_THROW_IF_FAILED(_env, status, false); return result; } inline Napi::Env Value::Env() const { return Napi::Env(_env); } inline bool Value::IsEmpty() const { return _value == nullptr; } inline napi_valuetype Value::Type() const { if (IsEmpty()) { return napi_undefined; } napi_valuetype type; napi_status status = napi_typeof(_env, _value, &type); NAPI_THROW_IF_FAILED(_env, status, napi_undefined); return type; } inline bool Value::IsUndefined() const { return Type() == napi_undefined; } inline bool Value::IsNull() const { return Type() == napi_null; } inline bool Value::IsBoolean() const { return Type() == napi_boolean; } inline bool Value::IsNumber() const { return Type() == napi_number; } #if NAPI_VERSION > 5 inline bool Value::IsBigInt() const { return Type() == napi_bigint; } #endif // NAPI_VERSION > 5 #if (NAPI_VERSION > 4) inline bool Value::IsDate() const { if (IsEmpty()) { return false; } bool result; napi_status status = napi_is_date(_env, _value, &result); NAPI_THROW_IF_FAILED(_env, status, false); return result; } #endif inline bool Value::IsString() const { return Type() == napi_string; } inline bool Value::IsSymbol() const { return Type() == napi_symbol; } inline bool Value::IsArray() const { if (IsEmpty()) { return false; } bool result; napi_status status = napi_is_array(_env, _value, &result); NAPI_THROW_IF_FAILED(_env, status, false); return result; } inline bool Value::IsArrayBuffer() const { if (IsEmpty()) { return false; } bool result; napi_status status = napi_is_arraybuffer(_env, _value, &result); NAPI_THROW_IF_FAILED(_env, status, false); return result; } inline bool Value::IsTypedArray() const { if (IsEmpty()) { return false; } bool result; napi_status status = napi_is_typedarray(_env, _value, &result); NAPI_THROW_IF_FAILED(_env, status, false); return result; } inline bool Value::IsObject() const { return Type() == napi_object || IsFunction(); } inline bool Value::IsFunction() const { return Type() == napi_function; } inline bool Value::IsPromise() const { if (IsEmpty()) { return false; } bool result; napi_status status = napi_is_promise(_env, _value, &result); NAPI_THROW_IF_FAILED(_env, status, false); return result; } inline bool Value::IsDataView() const { if (IsEmpty()) { return false; } bool result; napi_status status = napi_is_dataview(_env, _value, &result); NAPI_THROW_IF_FAILED(_env, status, false); return result; } inline bool Value::IsBuffer() const { if (IsEmpty()) { return false; } bool result; napi_status status = napi_is_buffer(_env, _value, &result); NAPI_THROW_IF_FAILED(_env, status, false); return result; } inline bool Value::IsExternal() const { return Type() == napi_external; } template inline T Value::As() const { return T(_env, _value); } inline Boolean Value::ToBoolean() const { napi_value result; napi_status status = napi_coerce_to_bool(_env, _value, &result); NAPI_THROW_IF_FAILED(_env, status, Boolean()); return Boolean(_env, result); } inline Number Value::ToNumber() const { napi_value result; napi_status status = napi_coerce_to_number(_env, _value, &result); NAPI_THROW_IF_FAILED(_env, status, Number()); return Number(_env, result); } inline String Value::ToString() const { napi_value result; napi_status status = napi_coerce_to_string(_env, _value, &result); NAPI_THROW_IF_FAILED(_env, status, String()); return String(_env, result); } inline Object Value::ToObject() const { napi_value result; napi_status status = napi_coerce_to_object(_env, _value, &result); NAPI_THROW_IF_FAILED(_env, status, Object()); return Object(_env, result); } //////////////////////////////////////////////////////////////////////////////// // Boolean class //////////////////////////////////////////////////////////////////////////////// inline Boolean Boolean::New(napi_env env, bool val) { napi_value value; napi_status status = napi_get_boolean(env, val, &value); NAPI_THROW_IF_FAILED(env, status, Boolean()); return Boolean(env, value); } inline Boolean::Boolean() : Napi::Value() { } inline Boolean::Boolean(napi_env env, napi_value value) : Napi::Value(env, value) { } inline Boolean::operator bool() const { return Value(); } inline bool Boolean::Value() const { bool result; napi_status status = napi_get_value_bool(_env, _value, &result); NAPI_THROW_IF_FAILED(_env, status, false); return result; } //////////////////////////////////////////////////////////////////////////////// // Number class //////////////////////////////////////////////////////////////////////////////// inline Number Number::New(napi_env env, double val) { napi_value value; napi_status status = napi_create_double(env, val, &value); NAPI_THROW_IF_FAILED(env, status, Number()); return Number(env, value); } inline Number::Number() : Value() { } inline Number::Number(napi_env env, napi_value value) : Value(env, value) { } inline Number::operator int32_t() const { return Int32Value(); } inline Number::operator uint32_t() const { return Uint32Value(); } inline Number::operator int64_t() const { return Int64Value(); } inline Number::operator float() const { return FloatValue(); } inline Number::operator double() const { return DoubleValue(); } inline int32_t Number::Int32Value() const { int32_t result; napi_status status = napi_get_value_int32(_env, _value, &result); NAPI_THROW_IF_FAILED(_env, status, 0); return result; } inline uint32_t Number::Uint32Value() const { uint32_t result; napi_status status = napi_get_value_uint32(_env, _value, &result); NAPI_THROW_IF_FAILED(_env, status, 0); return result; } inline int64_t Number::Int64Value() const { int64_t result; napi_status status = napi_get_value_int64(_env, _value, &result); NAPI_THROW_IF_FAILED(_env, status, 0); return result; } inline float Number::FloatValue() const { return static_cast(DoubleValue()); } inline double Number::DoubleValue() const { double result; napi_status status = napi_get_value_double(_env, _value, &result); NAPI_THROW_IF_FAILED(_env, status, 0); return result; } #if NAPI_VERSION > 5 //////////////////////////////////////////////////////////////////////////////// // BigInt Class //////////////////////////////////////////////////////////////////////////////// inline BigInt BigInt::New(napi_env env, int64_t val) { napi_value value; napi_status status = napi_create_bigint_int64(env, val, &value); NAPI_THROW_IF_FAILED(env, status, BigInt()); return BigInt(env, value); } inline BigInt BigInt::New(napi_env env, uint64_t val) { napi_value value; napi_status status = napi_create_bigint_uint64(env, val, &value); NAPI_THROW_IF_FAILED(env, status, BigInt()); return BigInt(env, value); } inline BigInt BigInt::New(napi_env env, int sign_bit, size_t word_count, const uint64_t* words) { napi_value value; napi_status status = napi_create_bigint_words(env, sign_bit, word_count, words, &value); NAPI_THROW_IF_FAILED(env, status, BigInt()); return BigInt(env, value); } inline BigInt::BigInt() : Value() { } inline BigInt::BigInt(napi_env env, napi_value value) : Value(env, value) { } inline int64_t BigInt::Int64Value(bool* lossless) const { int64_t result; napi_status status = napi_get_value_bigint_int64( _env, _value, &result, lossless); NAPI_THROW_IF_FAILED(_env, status, 0); return result; } inline uint64_t BigInt::Uint64Value(bool* lossless) const { uint64_t result; napi_status status = napi_get_value_bigint_uint64( _env, _value, &result, lossless); NAPI_THROW_IF_FAILED(_env, status, 0); return result; } inline size_t BigInt::WordCount() const { size_t word_count; napi_status status = napi_get_value_bigint_words( _env, _value, nullptr, &word_count, nullptr); NAPI_THROW_IF_FAILED(_env, status, 0); return word_count; } inline void BigInt::ToWords(int* sign_bit, size_t* word_count, uint64_t* words) { napi_status status = napi_get_value_bigint_words( _env, _value, sign_bit, word_count, words); NAPI_THROW_IF_FAILED_VOID(_env, status); } #endif // NAPI_VERSION > 5 #if (NAPI_VERSION > 4) //////////////////////////////////////////////////////////////////////////////// // Date Class //////////////////////////////////////////////////////////////////////////////// inline Date Date::New(napi_env env, double val) { napi_value value; napi_status status = napi_create_date(env, val, &value); NAPI_THROW_IF_FAILED(env, status, Date()); return Date(env, value); } inline Date::Date() : Value() { } inline Date::Date(napi_env env, napi_value value) : Value(env, value) { } inline Date::operator double() const { return ValueOf(); } inline double Date::ValueOf() const { double result; napi_status status = napi_get_date_value( _env, _value, &result); NAPI_THROW_IF_FAILED(_env, status, 0); return result; } #endif //////////////////////////////////////////////////////////////////////////////// // Name class //////////////////////////////////////////////////////////////////////////////// inline Name::Name() : Value() { } inline Name::Name(napi_env env, napi_value value) : Value(env, value) { } //////////////////////////////////////////////////////////////////////////////// // String class //////////////////////////////////////////////////////////////////////////////// inline String String::New(napi_env env, const std::string& val) { return String::New(env, val.c_str(), val.size()); } inline String String::New(napi_env env, const std::u16string& val) { return String::New(env, val.c_str(), val.size()); } inline String String::New(napi_env env, const char* val) { napi_value value; napi_status status = napi_create_string_utf8(env, val, std::strlen(val), &value); NAPI_THROW_IF_FAILED(env, status, String()); return String(env, value); } inline String String::New(napi_env env, const char16_t* val) { napi_value value; napi_status status = napi_create_string_utf16(env, val, std::u16string(val).size(), &value); NAPI_THROW_IF_FAILED(env, status, String()); return String(env, value); } inline String String::New(napi_env env, const char* val, size_t length) { napi_value value; napi_status status = napi_create_string_utf8(env, val, length, &value); NAPI_THROW_IF_FAILED(env, status, String()); return String(env, value); } inline String String::New(napi_env env, const char16_t* val, size_t length) { napi_value value; napi_status status = napi_create_string_utf16(env, val, length, &value); NAPI_THROW_IF_FAILED(env, status, String()); return String(env, value); } inline String::String() : Name() { } inline String::String(napi_env env, napi_value value) : Name(env, value) { } inline String::operator std::string() const { return Utf8Value(); } inline String::operator std::u16string() const { return Utf16Value(); } inline std::string String::Utf8Value() const { size_t length; napi_status status = napi_get_value_string_utf8(_env, _value, nullptr, 0, &length); NAPI_THROW_IF_FAILED(_env, status, ""); std::string value; value.reserve(length + 1); value.resize(length); status = napi_get_value_string_utf8(_env, _value, &value[0], value.capacity(), nullptr); NAPI_THROW_IF_FAILED(_env, status, ""); return value; } inline std::u16string String::Utf16Value() const { size_t length; napi_status status = napi_get_value_string_utf16(_env, _value, nullptr, 0, &length); NAPI_THROW_IF_FAILED(_env, status, NAPI_WIDE_TEXT("")); std::u16string value; value.reserve(length + 1); value.resize(length); status = napi_get_value_string_utf16(_env, _value, &value[0], value.capacity(), nullptr); NAPI_THROW_IF_FAILED(_env, status, NAPI_WIDE_TEXT("")); return value; } //////////////////////////////////////////////////////////////////////////////// // Symbol class //////////////////////////////////////////////////////////////////////////////// inline Symbol Symbol::New(napi_env env, const char* description) { napi_value descriptionValue = description != nullptr ? String::New(env, description) : static_cast(nullptr); return Symbol::New(env, descriptionValue); } inline Symbol Symbol::New(napi_env env, const std::string& description) { napi_value descriptionValue = String::New(env, description); return Symbol::New(env, descriptionValue); } inline Symbol Symbol::New(napi_env env, String description) { napi_value descriptionValue = description; return Symbol::New(env, descriptionValue); } inline Symbol Symbol::New(napi_env env, napi_value description) { napi_value value; napi_status status = napi_create_symbol(env, description, &value); NAPI_THROW_IF_FAILED(env, status, Symbol()); return Symbol(env, value); } inline Symbol Symbol::WellKnown(napi_env env, const std::string& name) { return Napi::Env(env).Global().Get("Symbol").As().Get(name).As(); } inline Symbol::Symbol() : Name() { } inline Symbol::Symbol(napi_env env, napi_value value) : Name(env, value) { } //////////////////////////////////////////////////////////////////////////////// // Automagic value creation //////////////////////////////////////////////////////////////////////////////// namespace details { template struct vf_number { static Number From(napi_env env, T value) { return Number::New(env, static_cast(value)); } }; template<> struct vf_number { static Boolean From(napi_env env, bool value) { return Boolean::New(env, value); } }; struct vf_utf8_charp { static String From(napi_env env, const char* value) { return String::New(env, value); } }; struct vf_utf16_charp { static String From(napi_env env, const char16_t* value) { return String::New(env, value); } }; struct vf_utf8_string { static String From(napi_env env, const std::string& value) { return String::New(env, value); } }; struct vf_utf16_string { static String From(napi_env env, const std::u16string& value) { return String::New(env, value); } }; template struct vf_fallback { static Value From(napi_env env, const T& value) { return Value(env, value); } }; template struct disjunction : std::false_type {}; template struct disjunction : B {}; template struct disjunction : std::conditional>::type {}; template struct can_make_string : disjunction::type, typename std::is_convertible::type, typename std::is_convertible::type, typename std::is_convertible::type> {}; } template Value Value::From(napi_env env, const T& value) { using Helper = typename std::conditional< std::is_integral::value || std::is_floating_point::value, details::vf_number, typename std::conditional< details::can_make_string::value, String, details::vf_fallback >::type >::type; return Helper::From(env, value); } template String String::From(napi_env env, const T& value) { struct Dummy {}; using Helper = typename std::conditional< std::is_convertible::value, details::vf_utf8_charp, typename std::conditional< std::is_convertible::value, details::vf_utf16_charp, typename std::conditional< std::is_convertible::value, details::vf_utf8_string, typename std::conditional< std::is_convertible::value, details::vf_utf16_string, Dummy >::type >::type >::type >::type; return Helper::From(env, value); } //////////////////////////////////////////////////////////////////////////////// // Object class //////////////////////////////////////////////////////////////////////////////// template inline Object::PropertyLValue::operator Value() const { return Object(_env, _object).Get(_key); } template template inline Object::PropertyLValue& Object::PropertyLValue::operator =(ValueType value) { Object(_env, _object).Set(_key, value); return *this; } template inline Object::PropertyLValue::PropertyLValue(Object object, Key key) : _env(object.Env()), _object(object), _key(key) {} inline Object Object::New(napi_env env) { napi_value value; napi_status status = napi_create_object(env, &value); NAPI_THROW_IF_FAILED(env, status, Object()); return Object(env, value); } inline Object::Object() : Value() { } inline Object::Object(napi_env env, napi_value value) : Value(env, value) { } inline Object::PropertyLValue Object::operator [](const char* utf8name) { return PropertyLValue(*this, utf8name); } inline Object::PropertyLValue Object::operator [](const std::string& utf8name) { return PropertyLValue(*this, utf8name); } inline Object::PropertyLValue Object::operator [](uint32_t index) { return PropertyLValue(*this, index); } inline Value Object::operator [](const char* utf8name) const { return Get(utf8name); } inline Value Object::operator [](const std::string& utf8name) const { return Get(utf8name); } inline Value Object::operator [](uint32_t index) const { return Get(index); } inline bool Object::Has(napi_value key) const { bool result; napi_status status = napi_has_property(_env, _value, key, &result); NAPI_THROW_IF_FAILED(_env, status, false); return result; } inline bool Object::Has(Value key) const { bool result; napi_status status = napi_has_property(_env, _value, key, &result); NAPI_THROW_IF_FAILED(_env, status, false); return result; } inline bool Object::Has(const char* utf8name) const { bool result; napi_status status = napi_has_named_property(_env, _value, utf8name, &result); NAPI_THROW_IF_FAILED(_env, status, false); return result; } inline bool Object::Has(const std::string& utf8name) const { return Has(utf8name.c_str()); } inline bool Object::HasOwnProperty(napi_value key) const { bool result; napi_status status = napi_has_own_property(_env, _value, key, &result); NAPI_THROW_IF_FAILED(_env, status, false); return result; } inline bool Object::HasOwnProperty(Value key) const { bool result; napi_status status = napi_has_own_property(_env, _value, key, &result); NAPI_THROW_IF_FAILED(_env, status, false); return result; } inline bool Object::HasOwnProperty(const char* utf8name) const { napi_value key; napi_status status = napi_create_string_utf8(_env, utf8name, std::strlen(utf8name), &key); NAPI_THROW_IF_FAILED(_env, status, false); return HasOwnProperty(key); } inline bool Object::HasOwnProperty(const std::string& utf8name) const { return HasOwnProperty(utf8name.c_str()); } inline Value Object::Get(napi_value key) const { napi_value result; napi_status status = napi_get_property(_env, _value, key, &result); NAPI_THROW_IF_FAILED(_env, status, Value()); return Value(_env, result); } inline Value Object::Get(Value key) const { napi_value result; napi_status status = napi_get_property(_env, _value, key, &result); NAPI_THROW_IF_FAILED(_env, status, Value()); return Value(_env, result); } inline Value Object::Get(const char* utf8name) const { napi_value result; napi_status status = napi_get_named_property(_env, _value, utf8name, &result); NAPI_THROW_IF_FAILED(_env, status, Value()); return Value(_env, result); } inline Value Object::Get(const std::string& utf8name) const { return Get(utf8name.c_str()); } template inline bool Object::Set(napi_value key, const ValueType& value) { napi_status status = napi_set_property(_env, _value, key, Value::From(_env, value)); NAPI_THROW_IF_FAILED(_env, status, false); return true; } template inline bool Object::Set(Value key, const ValueType& value) { napi_status status = napi_set_property(_env, _value, key, Value::From(_env, value)); NAPI_THROW_IF_FAILED(_env, status, false); return true; } template inline bool Object::Set(const char* utf8name, const ValueType& value) { napi_status status = napi_set_named_property(_env, _value, utf8name, Value::From(_env, value)); NAPI_THROW_IF_FAILED(_env, status, false); return true; } template inline bool Object::Set(const std::string& utf8name, const ValueType& value) { return Set(utf8name.c_str(), value); } inline bool Object::Delete(napi_value key) { bool result; napi_status status = napi_delete_property(_env, _value, key, &result); NAPI_THROW_IF_FAILED(_env, status, false); return result; } inline bool Object::Delete(Value key) { bool result; napi_status status = napi_delete_property(_env, _value, key, &result); NAPI_THROW_IF_FAILED(_env, status, false); return result; } inline bool Object::Delete(const char* utf8name) { return Delete(String::New(_env, utf8name)); } inline bool Object::Delete(const std::string& utf8name) { return Delete(String::New(_env, utf8name)); } inline bool Object::Has(uint32_t index) const { bool result; napi_status status = napi_has_element(_env, _value, index, &result); NAPI_THROW_IF_FAILED(_env, status, false); return result; } inline Value Object::Get(uint32_t index) const { napi_value value; napi_status status = napi_get_element(_env, _value, index, &value); NAPI_THROW_IF_FAILED(_env, status, Value()); return Value(_env, value); } template inline bool Object::Set(uint32_t index, const ValueType& value) { napi_status status = napi_set_element(_env, _value, index, Value::From(_env, value)); NAPI_THROW_IF_FAILED(_env, status, false); return true; } inline bool Object::Delete(uint32_t index) { bool result; napi_status status = napi_delete_element(_env, _value, index, &result); NAPI_THROW_IF_FAILED(_env, status, false); return result; } inline Array Object::GetPropertyNames() const { napi_value result; napi_status status = napi_get_property_names(_env, _value, &result); NAPI_THROW_IF_FAILED(_env, status, Array()); return Array(_env, result); } inline bool Object::DefineProperty(const PropertyDescriptor& property) { napi_status status = napi_define_properties(_env, _value, 1, reinterpret_cast(&property)); NAPI_THROW_IF_FAILED(_env, status, false); return true; } inline bool Object::DefineProperties( const std::initializer_list& properties) { napi_status status = napi_define_properties(_env, _value, properties.size(), reinterpret_cast(properties.begin())); NAPI_THROW_IF_FAILED(_env, status, false); return true; } inline bool Object::DefineProperties( const std::vector& properties) { napi_status status = napi_define_properties(_env, _value, properties.size(), reinterpret_cast(properties.data())); NAPI_THROW_IF_FAILED(_env, status, false); return true; } inline bool Object::InstanceOf(const Function& constructor) const { bool result; napi_status status = napi_instanceof(_env, _value, constructor, &result); NAPI_THROW_IF_FAILED(_env, status, false); return result; } template inline void Object::AddFinalizer(Finalizer finalizeCallback, T* data) { details::FinalizeData* finalizeData = new details::FinalizeData( {std::move(finalizeCallback), nullptr}); napi_status status = details::AttachData(_env, *this, data, details::FinalizeData::Wrapper, finalizeData); if (status != napi_ok) { delete finalizeData; NAPI_THROW_IF_FAILED_VOID(_env, status); } } template inline void Object::AddFinalizer(Finalizer finalizeCallback, T* data, Hint* finalizeHint) { details::FinalizeData* finalizeData = new details::FinalizeData( {std::move(finalizeCallback), finalizeHint}); napi_status status = details::AttachData(_env, *this, data, details::FinalizeData::WrapperWithHint, finalizeData); if (status != napi_ok) { delete finalizeData; NAPI_THROW_IF_FAILED_VOID(_env, status); } } #if NAPI_VERSION >= 8 inline bool Object::Freeze() { napi_status status = napi_object_freeze(_env, _value); NAPI_THROW_IF_FAILED(_env, status, false); return true; } inline bool Object::Seal() { napi_status status = napi_object_seal(_env, _value); NAPI_THROW_IF_FAILED(_env, status, false); return true; } #endif // NAPI_VERSION >= 8 //////////////////////////////////////////////////////////////////////////////// // External class //////////////////////////////////////////////////////////////////////////////// template inline External External::New(napi_env env, T* data) { napi_value value; napi_status status = napi_create_external(env, data, nullptr, nullptr, &value); NAPI_THROW_IF_FAILED(env, status, External()); return External(env, value); } template template inline External External::New(napi_env env, T* data, Finalizer finalizeCallback) { napi_value value; details::FinalizeData* finalizeData = new details::FinalizeData( {std::move(finalizeCallback), nullptr}); napi_status status = napi_create_external( env, data, details::FinalizeData::Wrapper, finalizeData, &value); if (status != napi_ok) { delete finalizeData; NAPI_THROW_IF_FAILED(env, status, External()); } return External(env, value); } template template inline External External::New(napi_env env, T* data, Finalizer finalizeCallback, Hint* finalizeHint) { napi_value value; details::FinalizeData* finalizeData = new details::FinalizeData( {std::move(finalizeCallback), finalizeHint}); napi_status status = napi_create_external( env, data, details::FinalizeData::WrapperWithHint, finalizeData, &value); if (status != napi_ok) { delete finalizeData; NAPI_THROW_IF_FAILED(env, status, External()); } return External(env, value); } template inline External::External() : Value() { } template inline External::External(napi_env env, napi_value value) : Value(env, value) { } template inline T* External::Data() const { void* data; napi_status status = napi_get_value_external(_env, _value, &data); NAPI_THROW_IF_FAILED(_env, status, nullptr); return reinterpret_cast(data); } //////////////////////////////////////////////////////////////////////////////// // Array class //////////////////////////////////////////////////////////////////////////////// inline Array Array::New(napi_env env) { napi_value value; napi_status status = napi_create_array(env, &value); NAPI_THROW_IF_FAILED(env, status, Array()); return Array(env, value); } inline Array Array::New(napi_env env, size_t length) { napi_value value; napi_status status = napi_create_array_with_length(env, length, &value); NAPI_THROW_IF_FAILED(env, status, Array()); return Array(env, value); } inline Array::Array() : Object() { } inline Array::Array(napi_env env, napi_value value) : Object(env, value) { } inline uint32_t Array::Length() const { uint32_t result; napi_status status = napi_get_array_length(_env, _value, &result); NAPI_THROW_IF_FAILED(_env, status, 0); return result; } //////////////////////////////////////////////////////////////////////////////// // ArrayBuffer class //////////////////////////////////////////////////////////////////////////////// inline ArrayBuffer ArrayBuffer::New(napi_env env, size_t byteLength) { napi_value value; void* data; napi_status status = napi_create_arraybuffer(env, byteLength, &data, &value); NAPI_THROW_IF_FAILED(env, status, ArrayBuffer()); return ArrayBuffer(env, value); } inline ArrayBuffer ArrayBuffer::New(napi_env env, void* externalData, size_t byteLength) { napi_value value; napi_status status = napi_create_external_arraybuffer( env, externalData, byteLength, nullptr, nullptr, &value); NAPI_THROW_IF_FAILED(env, status, ArrayBuffer()); return ArrayBuffer(env, value); } template inline ArrayBuffer ArrayBuffer::New(napi_env env, void* externalData, size_t byteLength, Finalizer finalizeCallback) { napi_value value; details::FinalizeData* finalizeData = new details::FinalizeData( {std::move(finalizeCallback), nullptr}); napi_status status = napi_create_external_arraybuffer( env, externalData, byteLength, details::FinalizeData::Wrapper, finalizeData, &value); if (status != napi_ok) { delete finalizeData; NAPI_THROW_IF_FAILED(env, status, ArrayBuffer()); } return ArrayBuffer(env, value); } template inline ArrayBuffer ArrayBuffer::New(napi_env env, void* externalData, size_t byteLength, Finalizer finalizeCallback, Hint* finalizeHint) { napi_value value; details::FinalizeData* finalizeData = new details::FinalizeData( {std::move(finalizeCallback), finalizeHint}); napi_status status = napi_create_external_arraybuffer( env, externalData, byteLength, details::FinalizeData::WrapperWithHint, finalizeData, &value); if (status != napi_ok) { delete finalizeData; NAPI_THROW_IF_FAILED(env, status, ArrayBuffer()); } return ArrayBuffer(env, value); } inline ArrayBuffer::ArrayBuffer() : Object() { } inline ArrayBuffer::ArrayBuffer(napi_env env, napi_value value) : Object(env, value) { } inline void* ArrayBuffer::Data() { void* data; napi_status status = napi_get_arraybuffer_info(_env, _value, &data, nullptr); NAPI_THROW_IF_FAILED(_env, status, nullptr); return data; } inline size_t ArrayBuffer::ByteLength() { size_t length; napi_status status = napi_get_arraybuffer_info(_env, _value, nullptr, &length); NAPI_THROW_IF_FAILED(_env, status, 0); return length; } #if NAPI_VERSION >= 7 inline bool ArrayBuffer::IsDetached() const { bool detached; napi_status status = napi_is_detached_arraybuffer(_env, _value, &detached); NAPI_THROW_IF_FAILED(_env, status, false); return detached; } inline void ArrayBuffer::Detach() { napi_status status = napi_detach_arraybuffer(_env, _value); NAPI_THROW_IF_FAILED_VOID(_env, status); } #endif // NAPI_VERSION >= 7 //////////////////////////////////////////////////////////////////////////////// // DataView class //////////////////////////////////////////////////////////////////////////////// inline DataView DataView::New(napi_env env, Napi::ArrayBuffer arrayBuffer) { return New(env, arrayBuffer, 0, arrayBuffer.ByteLength()); } inline DataView DataView::New(napi_env env, Napi::ArrayBuffer arrayBuffer, size_t byteOffset) { if (byteOffset > arrayBuffer.ByteLength()) { NAPI_THROW(RangeError::New(env, "Start offset is outside the bounds of the buffer"), DataView()); } return New(env, arrayBuffer, byteOffset, arrayBuffer.ByteLength() - byteOffset); } inline DataView DataView::New(napi_env env, Napi::ArrayBuffer arrayBuffer, size_t byteOffset, size_t byteLength) { if (byteOffset + byteLength > arrayBuffer.ByteLength()) { NAPI_THROW(RangeError::New(env, "Invalid DataView length"), DataView()); } napi_value value; napi_status status = napi_create_dataview( env, byteLength, arrayBuffer, byteOffset, &value); NAPI_THROW_IF_FAILED(env, status, DataView()); return DataView(env, value); } inline DataView::DataView() : Object() { } inline DataView::DataView(napi_env env, napi_value value) : Object(env, value) { napi_status status = napi_get_dataview_info( _env, _value /* dataView */, &_length /* byteLength */, &_data /* data */, nullptr /* arrayBuffer */, nullptr /* byteOffset */); NAPI_THROW_IF_FAILED_VOID(_env, status); } inline Napi::ArrayBuffer DataView::ArrayBuffer() const { napi_value arrayBuffer; napi_status status = napi_get_dataview_info( _env, _value /* dataView */, nullptr /* byteLength */, nullptr /* data */, &arrayBuffer /* arrayBuffer */, nullptr /* byteOffset */); NAPI_THROW_IF_FAILED(_env, status, Napi::ArrayBuffer()); return Napi::ArrayBuffer(_env, arrayBuffer); } inline size_t DataView::ByteOffset() const { size_t byteOffset; napi_status status = napi_get_dataview_info( _env, _value /* dataView */, nullptr /* byteLength */, nullptr /* data */, nullptr /* arrayBuffer */, &byteOffset /* byteOffset */); NAPI_THROW_IF_FAILED(_env, status, 0); return byteOffset; } inline size_t DataView::ByteLength() const { return _length; } inline void* DataView::Data() const { return _data; } inline float DataView::GetFloat32(size_t byteOffset) const { return ReadData(byteOffset); } inline double DataView::GetFloat64(size_t byteOffset) const { return ReadData(byteOffset); } inline int8_t DataView::GetInt8(size_t byteOffset) const { return ReadData(byteOffset); } inline int16_t DataView::GetInt16(size_t byteOffset) const { return ReadData(byteOffset); } inline int32_t DataView::GetInt32(size_t byteOffset) const { return ReadData(byteOffset); } inline uint8_t DataView::GetUint8(size_t byteOffset) const { return ReadData(byteOffset); } inline uint16_t DataView::GetUint16(size_t byteOffset) const { return ReadData(byteOffset); } inline uint32_t DataView::GetUint32(size_t byteOffset) const { return ReadData(byteOffset); } inline void DataView::SetFloat32(size_t byteOffset, float value) const { WriteData(byteOffset, value); } inline void DataView::SetFloat64(size_t byteOffset, double value) const { WriteData(byteOffset, value); } inline void DataView::SetInt8(size_t byteOffset, int8_t value) const { WriteData(byteOffset, value); } inline void DataView::SetInt16(size_t byteOffset, int16_t value) const { WriteData(byteOffset, value); } inline void DataView::SetInt32(size_t byteOffset, int32_t value) const { WriteData(byteOffset, value); } inline void DataView::SetUint8(size_t byteOffset, uint8_t value) const { WriteData(byteOffset, value); } inline void DataView::SetUint16(size_t byteOffset, uint16_t value) const { WriteData(byteOffset, value); } inline void DataView::SetUint32(size_t byteOffset, uint32_t value) const { WriteData(byteOffset, value); } template inline T DataView::ReadData(size_t byteOffset) const { if (byteOffset + sizeof(T) > _length || byteOffset + sizeof(T) < byteOffset) { // overflow NAPI_THROW(RangeError::New(_env, "Offset is outside the bounds of the DataView"), 0); } return *reinterpret_cast(static_cast(_data) + byteOffset); } template inline void DataView::WriteData(size_t byteOffset, T value) const { if (byteOffset + sizeof(T) > _length || byteOffset + sizeof(T) < byteOffset) { // overflow NAPI_THROW_VOID(RangeError::New(_env, "Offset is outside the bounds of the DataView")); } *reinterpret_cast(static_cast(_data) + byteOffset) = value; } //////////////////////////////////////////////////////////////////////////////// // TypedArray class //////////////////////////////////////////////////////////////////////////////// inline TypedArray::TypedArray() : Object(), _type(TypedArray::unknown_array_type), _length(0) { } inline TypedArray::TypedArray(napi_env env, napi_value value) : Object(env, value), _type(TypedArray::unknown_array_type), _length(0) { } inline TypedArray::TypedArray(napi_env env, napi_value value, napi_typedarray_type type, size_t length) : Object(env, value), _type(type), _length(length) { } inline napi_typedarray_type TypedArray::TypedArrayType() const { if (_type == TypedArray::unknown_array_type) { napi_status status = napi_get_typedarray_info(_env, _value, &const_cast(this)->_type, &const_cast(this)->_length, nullptr, nullptr, nullptr); NAPI_THROW_IF_FAILED(_env, status, napi_int8_array); } return _type; } inline uint8_t TypedArray::ElementSize() const { switch (TypedArrayType()) { case napi_int8_array: case napi_uint8_array: case napi_uint8_clamped_array: return 1; case napi_int16_array: case napi_uint16_array: return 2; case napi_int32_array: case napi_uint32_array: case napi_float32_array: return 4; case napi_float64_array: #if (NAPI_VERSION > 5) case napi_bigint64_array: case napi_biguint64_array: #endif // (NAPI_VERSION > 5) return 8; default: return 0; } } inline size_t TypedArray::ElementLength() const { if (_type == TypedArray::unknown_array_type) { napi_status status = napi_get_typedarray_info(_env, _value, &const_cast(this)->_type, &const_cast(this)->_length, nullptr, nullptr, nullptr); NAPI_THROW_IF_FAILED(_env, status, 0); } return _length; } inline size_t TypedArray::ByteOffset() const { size_t byteOffset; napi_status status = napi_get_typedarray_info( _env, _value, nullptr, nullptr, nullptr, nullptr, &byteOffset); NAPI_THROW_IF_FAILED(_env, status, 0); return byteOffset; } inline size_t TypedArray::ByteLength() const { return ElementSize() * ElementLength(); } inline Napi::ArrayBuffer TypedArray::ArrayBuffer() const { napi_value arrayBuffer; napi_status status = napi_get_typedarray_info( _env, _value, nullptr, nullptr, nullptr, &arrayBuffer, nullptr); NAPI_THROW_IF_FAILED(_env, status, Napi::ArrayBuffer()); return Napi::ArrayBuffer(_env, arrayBuffer); } //////////////////////////////////////////////////////////////////////////////// // TypedArrayOf class //////////////////////////////////////////////////////////////////////////////// template inline TypedArrayOf TypedArrayOf::New(napi_env env, size_t elementLength, napi_typedarray_type type) { Napi::ArrayBuffer arrayBuffer = Napi::ArrayBuffer::New(env, elementLength * sizeof (T)); return New(env, elementLength, arrayBuffer, 0, type); } template inline TypedArrayOf TypedArrayOf::New(napi_env env, size_t elementLength, Napi::ArrayBuffer arrayBuffer, size_t bufferOffset, napi_typedarray_type type) { napi_value value; napi_status status = napi_create_typedarray( env, type, elementLength, arrayBuffer, bufferOffset, &value); NAPI_THROW_IF_FAILED(env, status, TypedArrayOf()); return TypedArrayOf( env, value, type, elementLength, reinterpret_cast(reinterpret_cast(arrayBuffer.Data()) + bufferOffset)); } template inline TypedArrayOf::TypedArrayOf() : TypedArray(), _data(nullptr) { } template inline TypedArrayOf::TypedArrayOf(napi_env env, napi_value value) : TypedArray(env, value), _data(nullptr) { napi_status status = napi_ok; if (value != nullptr) { status = napi_get_typedarray_info( _env, _value, &_type, &_length, reinterpret_cast(&_data), nullptr, nullptr); } else { _type = TypedArrayTypeForPrimitiveType(); _length = 0; } NAPI_THROW_IF_FAILED_VOID(_env, status); } template inline TypedArrayOf::TypedArrayOf(napi_env env, napi_value value, napi_typedarray_type type, size_t length, T* data) : TypedArray(env, value, type, length), _data(data) { if (!(type == TypedArrayTypeForPrimitiveType() || (type == napi_uint8_clamped_array && std::is_same::value))) { NAPI_THROW_VOID(TypeError::New(env, "Array type must match the template parameter. " "(Uint8 arrays may optionally have the \"clamped\" array type.)")); } } template inline T& TypedArrayOf::operator [](size_t index) { return _data[index]; } template inline const T& TypedArrayOf::operator [](size_t index) const { return _data[index]; } template inline T* TypedArrayOf::Data() { return _data; } template inline const T* TypedArrayOf::Data() const { return _data; } //////////////////////////////////////////////////////////////////////////////// // Function class //////////////////////////////////////////////////////////////////////////////// template static inline napi_status CreateFunction(napi_env env, const char* utf8name, napi_callback cb, CbData* data, napi_value* result) { napi_status status = napi_create_function(env, utf8name, NAPI_AUTO_LENGTH, cb, data, result); if (status == napi_ok) { status = Napi::details::AttachData(env, *result, data); } return status; } template inline Function Function::New(napi_env env, const char* utf8name, void* data) { napi_value result = nullptr; napi_status status = napi_create_function(env, utf8name, NAPI_AUTO_LENGTH, details::TemplatedVoidCallback, data, &result); NAPI_THROW_IF_FAILED(env, status, Function()); return Function(env, result); } template inline Function Function::New(napi_env env, const char* utf8name, void* data) { napi_value result = nullptr; napi_status status = napi_create_function(env, utf8name, NAPI_AUTO_LENGTH, details::TemplatedCallback, data, &result); NAPI_THROW_IF_FAILED(env, status, Function()); return Function(env, result); } template inline Function Function::New(napi_env env, const std::string& utf8name, void* data) { return Function::New(env, utf8name.c_str(), data); } template inline Function Function::New(napi_env env, const std::string& utf8name, void* data) { return Function::New(env, utf8name.c_str(), data); } template inline Function Function::New(napi_env env, Callable cb, const char* utf8name, void* data) { using ReturnType = decltype(cb(CallbackInfo(nullptr, nullptr))); using CbData = details::CallbackData; auto callbackData = new CbData({ cb, data }); napi_value value; napi_status status = CreateFunction(env, utf8name, CbData::Wrapper, callbackData, &value); if (status != napi_ok) { delete callbackData; NAPI_THROW_IF_FAILED(env, status, Function()); } return Function(env, value); } template inline Function Function::New(napi_env env, Callable cb, const std::string& utf8name, void* data) { return New(env, cb, utf8name.c_str(), data); } inline Function::Function() : Object() { } inline Function::Function(napi_env env, napi_value value) : Object(env, value) { } inline Value Function::operator ()(const std::initializer_list& args) const { return Call(Env().Undefined(), args); } inline Value Function::Call(const std::initializer_list& args) const { return Call(Env().Undefined(), args); } inline Value Function::Call(const std::vector& args) const { return Call(Env().Undefined(), args); } inline Value Function::Call(size_t argc, const napi_value* args) const { return Call(Env().Undefined(), argc, args); } inline Value Function::Call(napi_value recv, const std::initializer_list& args) const { return Call(recv, args.size(), args.begin()); } inline Value Function::Call(napi_value recv, const std::vector& args) const { return Call(recv, args.size(), args.data()); } inline Value Function::Call(napi_value recv, size_t argc, const napi_value* args) const { napi_value result; napi_status status = napi_call_function( _env, recv, _value, argc, args, &result); NAPI_THROW_IF_FAILED(_env, status, Value()); return Value(_env, result); } inline Value Function::MakeCallback( napi_value recv, const std::initializer_list& args, napi_async_context context) const { return MakeCallback(recv, args.size(), args.begin(), context); } inline Value Function::MakeCallback( napi_value recv, const std::vector& args, napi_async_context context) const { return MakeCallback(recv, args.size(), args.data(), context); } inline Value Function::MakeCallback( napi_value recv, size_t argc, const napi_value* args, napi_async_context context) const { napi_value result; napi_status status = napi_make_callback( _env, context, recv, _value, argc, args, &result); NAPI_THROW_IF_FAILED(_env, status, Value()); return Value(_env, result); } inline Object Function::New(const std::initializer_list& args) const { return New(args.size(), args.begin()); } inline Object Function::New(const std::vector& args) const { return New(args.size(), args.data()); } inline Object Function::New(size_t argc, const napi_value* args) const { napi_value result; napi_status status = napi_new_instance( _env, _value, argc, args, &result); NAPI_THROW_IF_FAILED(_env, status, Object()); return Object(_env, result); } //////////////////////////////////////////////////////////////////////////////// // Promise class //////////////////////////////////////////////////////////////////////////////// inline Promise::Deferred Promise::Deferred::New(napi_env env) { return Promise::Deferred(env); } inline Promise::Deferred::Deferred(napi_env env) : _env(env) { napi_status status = napi_create_promise(_env, &_deferred, &_promise); NAPI_THROW_IF_FAILED_VOID(_env, status); } inline Promise Promise::Deferred::Promise() const { return Napi::Promise(_env, _promise); } inline Napi::Env Promise::Deferred::Env() const { return Napi::Env(_env); } inline void Promise::Deferred::Resolve(napi_value value) const { napi_status status = napi_resolve_deferred(_env, _deferred, value); NAPI_THROW_IF_FAILED_VOID(_env, status); } inline void Promise::Deferred::Reject(napi_value value) const { napi_status status = napi_reject_deferred(_env, _deferred, value); NAPI_THROW_IF_FAILED_VOID(_env, status); } inline Promise::Promise(napi_env env, napi_value value) : Object(env, value) { } //////////////////////////////////////////////////////////////////////////////// // Buffer class //////////////////////////////////////////////////////////////////////////////// template inline Buffer Buffer::New(napi_env env, size_t length) { napi_value value; void* data; napi_status status = napi_create_buffer(env, length * sizeof (T), &data, &value); NAPI_THROW_IF_FAILED(env, status, Buffer()); return Buffer(env, value, length, static_cast(data)); } template inline Buffer Buffer::New(napi_env env, T* data, size_t length) { napi_value value; napi_status status = napi_create_external_buffer( env, length * sizeof (T), data, nullptr, nullptr, &value); NAPI_THROW_IF_FAILED(env, status, Buffer()); return Buffer(env, value, length, data); } template template inline Buffer Buffer::New(napi_env env, T* data, size_t length, Finalizer finalizeCallback) { napi_value value; details::FinalizeData* finalizeData = new details::FinalizeData( {std::move(finalizeCallback), nullptr}); napi_status status = napi_create_external_buffer( env, length * sizeof (T), data, details::FinalizeData::Wrapper, finalizeData, &value); if (status != napi_ok) { delete finalizeData; NAPI_THROW_IF_FAILED(env, status, Buffer()); } return Buffer(env, value, length, data); } template template inline Buffer Buffer::New(napi_env env, T* data, size_t length, Finalizer finalizeCallback, Hint* finalizeHint) { napi_value value; details::FinalizeData* finalizeData = new details::FinalizeData( {std::move(finalizeCallback), finalizeHint}); napi_status status = napi_create_external_buffer( env, length * sizeof (T), data, details::FinalizeData::WrapperWithHint, finalizeData, &value); if (status != napi_ok) { delete finalizeData; NAPI_THROW_IF_FAILED(env, status, Buffer()); } return Buffer(env, value, length, data); } template inline Buffer Buffer::Copy(napi_env env, const T* data, size_t length) { napi_value value; napi_status status = napi_create_buffer_copy( env, length * sizeof (T), data, nullptr, &value); NAPI_THROW_IF_FAILED(env, status, Buffer()); return Buffer(env, value); } template inline Buffer::Buffer() : Uint8Array(), _length(0), _data(nullptr) { } template inline Buffer::Buffer(napi_env env, napi_value value) : Uint8Array(env, value), _length(0), _data(nullptr) { } template inline Buffer::Buffer(napi_env env, napi_value value, size_t length, T* data) : Uint8Array(env, value), _length(length), _data(data) { } template inline size_t Buffer::Length() const { EnsureInfo(); return _length; } template inline T* Buffer::Data() const { EnsureInfo(); return _data; } template inline void Buffer::EnsureInfo() const { // The Buffer instance may have been constructed from a napi_value whose // length/data are not yet known. Fetch and cache these values just once, // since they can never change during the lifetime of the Buffer. if (_data == nullptr) { size_t byteLength; void* voidData; napi_status status = napi_get_buffer_info(_env, _value, &voidData, &byteLength); NAPI_THROW_IF_FAILED_VOID(_env, status); _length = byteLength / sizeof (T); _data = static_cast(voidData); } } //////////////////////////////////////////////////////////////////////////////// // Error class //////////////////////////////////////////////////////////////////////////////// inline Error Error::New(napi_env env) { napi_status status; napi_value error = nullptr; bool is_exception_pending; const napi_extended_error_info* info; // We must retrieve the last error info before doing anything else, because // doing anything else will replace the last error info. status = napi_get_last_error_info(env, &info); NAPI_FATAL_IF_FAILED(status, "Error::New", "napi_get_last_error_info"); status = napi_is_exception_pending(env, &is_exception_pending); NAPI_FATAL_IF_FAILED(status, "Error::New", "napi_is_exception_pending"); // A pending exception takes precedence over any internal error status. if (is_exception_pending) { status = napi_get_and_clear_last_exception(env, &error); NAPI_FATAL_IF_FAILED(status, "Error::New", "napi_get_and_clear_last_exception"); } else { const char* error_message = info->error_message != nullptr ? info->error_message : "Error in native callback"; napi_value message; status = napi_create_string_utf8( env, error_message, std::strlen(error_message), &message); NAPI_FATAL_IF_FAILED(status, "Error::New", "napi_create_string_utf8"); switch (info->error_code) { case napi_object_expected: case napi_string_expected: case napi_boolean_expected: case napi_number_expected: status = napi_create_type_error(env, nullptr, message, &error); break; default: status = napi_create_error(env, nullptr, message, &error); break; } NAPI_FATAL_IF_FAILED(status, "Error::New", "napi_create_error"); } return Error(env, error); } inline Error Error::New(napi_env env, const char* message) { return Error::New(env, message, std::strlen(message), napi_create_error); } inline Error Error::New(napi_env env, const std::string& message) { return Error::New(env, message.c_str(), message.size(), napi_create_error); } inline NAPI_NO_RETURN void Error::Fatal(const char* location, const char* message) { napi_fatal_error(location, NAPI_AUTO_LENGTH, message, NAPI_AUTO_LENGTH); } inline Error::Error() : ObjectReference() { } inline Error::Error(napi_env env, napi_value value) : ObjectReference(env, nullptr) { if (value != nullptr) { napi_status status = napi_create_reference(env, value, 1, &_ref); // Avoid infinite recursion in the failure case. // Don't try to construct & throw another Error instance. NAPI_FATAL_IF_FAILED(status, "Error::Error", "napi_create_reference"); } } inline Error::Error(Error&& other) : ObjectReference(std::move(other)) { } inline Error& Error::operator =(Error&& other) { static_cast*>(this)->operator=(std::move(other)); return *this; } inline Error::Error(const Error& other) : ObjectReference(other) { } inline Error& Error::operator =(const Error& other) { Reset(); _env = other.Env(); HandleScope scope(_env); napi_value value = other.Value(); if (value != nullptr) { napi_status status = napi_create_reference(_env, value, 1, &_ref); NAPI_THROW_IF_FAILED(_env, status, *this); } return *this; } inline const std::string& Error::Message() const NAPI_NOEXCEPT { if (_message.size() == 0 && _env != nullptr) { #ifdef NAPI_CPP_EXCEPTIONS try { _message = Get("message").As(); } catch (...) { // Catch all errors here, to include e.g. a std::bad_alloc from // the std::string::operator=, because this method may not throw. } #else // NAPI_CPP_EXCEPTIONS _message = Get("message").As(); #endif // NAPI_CPP_EXCEPTIONS } return _message; } inline void Error::ThrowAsJavaScriptException() const { HandleScope scope(_env); if (!IsEmpty()) { #ifdef NODE_API_SWALLOW_UNTHROWABLE_EXCEPTIONS bool pendingException = false; // check if there is already a pending exception. If so don't try to throw a // new one as that is not allowed/possible napi_status status = napi_is_exception_pending(_env, &pendingException); if ((status != napi_ok) || ((status == napi_ok) && (pendingException == false))) { // We intentionally don't use `NAPI_THROW_*` macros here to ensure // that there is no possible recursion as `ThrowAsJavaScriptException` // is part of `NAPI_THROW_*` macro definition for noexcept. status = napi_throw(_env, Value()); if (status == napi_pending_exception) { // The environment must be terminating as we checked earlier and there // was no pending exception. In this case continuing will result // in a fatal error and there is nothing the author has done incorrectly // in their code that is worth flagging through a fatal error return; } } else { status = napi_pending_exception; } #else // We intentionally don't use `NAPI_THROW_*` macros here to ensure // that there is no possible recursion as `ThrowAsJavaScriptException` // is part of `NAPI_THROW_*` macro definition for noexcept. napi_status status = napi_throw(_env, Value()); #endif #ifdef NAPI_CPP_EXCEPTIONS if (status != napi_ok) { throw Error::New(_env); } #else // NAPI_CPP_EXCEPTIONS NAPI_FATAL_IF_FAILED(status, "Error::ThrowAsJavaScriptException", "napi_throw"); #endif // NAPI_CPP_EXCEPTIONS } } #ifdef NAPI_CPP_EXCEPTIONS inline const char* Error::what() const NAPI_NOEXCEPT { return Message().c_str(); } #endif // NAPI_CPP_EXCEPTIONS template inline TError Error::New(napi_env env, const char* message, size_t length, create_error_fn create_error) { napi_value str; napi_status status = napi_create_string_utf8(env, message, length, &str); NAPI_THROW_IF_FAILED(env, status, TError()); napi_value error; status = create_error(env, nullptr, str, &error); NAPI_THROW_IF_FAILED(env, status, TError()); return TError(env, error); } inline TypeError TypeError::New(napi_env env, const char* message) { return Error::New(env, message, std::strlen(message), napi_create_type_error); } inline TypeError TypeError::New(napi_env env, const std::string& message) { return Error::New(env, message.c_str(), message.size(), napi_create_type_error); } inline TypeError::TypeError() : Error() { } inline TypeError::TypeError(napi_env env, napi_value value) : Error(env, value) { } inline RangeError RangeError::New(napi_env env, const char* message) { return Error::New(env, message, std::strlen(message), napi_create_range_error); } inline RangeError RangeError::New(napi_env env, const std::string& message) { return Error::New(env, message.c_str(), message.size(), napi_create_range_error); } inline RangeError::RangeError() : Error() { } inline RangeError::RangeError(napi_env env, napi_value value) : Error(env, value) { } //////////////////////////////////////////////////////////////////////////////// // Reference class //////////////////////////////////////////////////////////////////////////////// template inline Reference Reference::New(const T& value, uint32_t initialRefcount) { napi_env env = value.Env(); napi_value val = value; if (val == nullptr) { return Reference(env, nullptr); } napi_ref ref; napi_status status = napi_create_reference(env, value, initialRefcount, &ref); NAPI_THROW_IF_FAILED(env, status, Reference()); return Reference(env, ref); } template inline Reference::Reference() : _env(nullptr), _ref(nullptr), _suppressDestruct(false) { } template inline Reference::Reference(napi_env env, napi_ref ref) : _env(env), _ref(ref), _suppressDestruct(false) { } template inline Reference::~Reference() { if (_ref != nullptr) { if (!_suppressDestruct) { napi_delete_reference(_env, _ref); } _ref = nullptr; } } template inline Reference::Reference(Reference&& other) : _env(other._env), _ref(other._ref), _suppressDestruct(other._suppressDestruct) { other._env = nullptr; other._ref = nullptr; other._suppressDestruct = false; } template inline Reference& Reference::operator =(Reference&& other) { Reset(); _env = other._env; _ref = other._ref; _suppressDestruct = other._suppressDestruct; other._env = nullptr; other._ref = nullptr; other._suppressDestruct = false; return *this; } template inline Reference::Reference(const Reference& other) : _env(other._env), _ref(nullptr), _suppressDestruct(false) { HandleScope scope(_env); napi_value value = other.Value(); if (value != nullptr) { // Copying is a limited scenario (currently only used for Error object) and always creates a // strong reference to the given value even if the incoming reference is weak. napi_status status = napi_create_reference(_env, value, 1, &_ref); NAPI_FATAL_IF_FAILED(status, "Reference::Reference", "napi_create_reference"); } } template inline Reference::operator napi_ref() const { return _ref; } template inline bool Reference::operator ==(const Reference &other) const { HandleScope scope(_env); return this->Value().StrictEquals(other.Value()); } template inline bool Reference::operator !=(const Reference &other) const { return !this->operator ==(other); } template inline Napi::Env Reference::Env() const { return Napi::Env(_env); } template inline bool Reference::IsEmpty() const { return _ref == nullptr; } template inline T Reference::Value() const { if (_ref == nullptr) { return T(_env, nullptr); } napi_value value; napi_status status = napi_get_reference_value(_env, _ref, &value); NAPI_THROW_IF_FAILED(_env, status, T()); return T(_env, value); } template inline uint32_t Reference::Ref() { uint32_t result; napi_status status = napi_reference_ref(_env, _ref, &result); NAPI_THROW_IF_FAILED(_env, status, 1); return result; } template inline uint32_t Reference::Unref() { uint32_t result; napi_status status = napi_reference_unref(_env, _ref, &result); NAPI_THROW_IF_FAILED(_env, status, 1); return result; } template inline void Reference::Reset() { if (_ref != nullptr) { napi_status status = napi_delete_reference(_env, _ref); NAPI_THROW_IF_FAILED_VOID(_env, status); _ref = nullptr; } } template inline void Reference::Reset(const T& value, uint32_t refcount) { Reset(); _env = value.Env(); napi_value val = value; if (val != nullptr) { napi_status status = napi_create_reference(_env, value, refcount, &_ref); NAPI_THROW_IF_FAILED_VOID(_env, status); } } template inline void Reference::SuppressDestruct() { _suppressDestruct = true; } template inline Reference Weak(T value) { return Reference::New(value, 0); } inline ObjectReference Weak(Object value) { return Reference::New(value, 0); } inline FunctionReference Weak(Function value) { return Reference::New(value, 0); } template inline Reference Persistent(T value) { return Reference::New(value, 1); } inline ObjectReference Persistent(Object value) { return Reference::New(value, 1); } inline FunctionReference Persistent(Function value) { return Reference::New(value, 1); } //////////////////////////////////////////////////////////////////////////////// // ObjectReference class //////////////////////////////////////////////////////////////////////////////// inline ObjectReference::ObjectReference(): Reference() { } inline ObjectReference::ObjectReference(napi_env env, napi_ref ref): Reference(env, ref) { } inline ObjectReference::ObjectReference(Reference&& other) : Reference(std::move(other)) { } inline ObjectReference& ObjectReference::operator =(Reference&& other) { static_cast*>(this)->operator=(std::move(other)); return *this; } inline ObjectReference::ObjectReference(ObjectReference&& other) : Reference(std::move(other)) { } inline ObjectReference& ObjectReference::operator =(ObjectReference&& other) { static_cast*>(this)->operator=(std::move(other)); return *this; } inline ObjectReference::ObjectReference(const ObjectReference& other) : Reference(other) { } inline Napi::Value ObjectReference::Get(const char* utf8name) const { EscapableHandleScope scope(_env); return scope.Escape(Value().Get(utf8name)); } inline Napi::Value ObjectReference::Get(const std::string& utf8name) const { EscapableHandleScope scope(_env); return scope.Escape(Value().Get(utf8name)); } inline bool ObjectReference::Set(const char* utf8name, napi_value value) { HandleScope scope(_env); return Value().Set(utf8name, value); } inline bool ObjectReference::Set(const char* utf8name, Napi::Value value) { HandleScope scope(_env); return Value().Set(utf8name, value); } inline bool ObjectReference::Set(const char* utf8name, const char* utf8value) { HandleScope scope(_env); return Value().Set(utf8name, utf8value); } inline bool ObjectReference::Set(const char* utf8name, bool boolValue) { HandleScope scope(_env); return Value().Set(utf8name, boolValue); } inline bool ObjectReference::Set(const char* utf8name, double numberValue) { HandleScope scope(_env); return Value().Set(utf8name, numberValue); } inline bool ObjectReference::Set(const std::string& utf8name, napi_value value) { HandleScope scope(_env); return Value().Set(utf8name, value); } inline bool ObjectReference::Set(const std::string& utf8name, Napi::Value value) { HandleScope scope(_env); return Value().Set(utf8name, value); } inline bool ObjectReference::Set(const std::string& utf8name, std::string& utf8value) { HandleScope scope(_env); return Value().Set(utf8name, utf8value); } inline bool ObjectReference::Set(const std::string& utf8name, bool boolValue) { HandleScope scope(_env); return Value().Set(utf8name, boolValue); } inline bool ObjectReference::Set(const std::string& utf8name, double numberValue) { HandleScope scope(_env); return Value().Set(utf8name, numberValue); } inline Napi::Value ObjectReference::Get(uint32_t index) const { EscapableHandleScope scope(_env); return scope.Escape(Value().Get(index)); } inline bool ObjectReference::Set(uint32_t index, napi_value value) { HandleScope scope(_env); return Value().Set(index, value); } inline bool ObjectReference::Set(uint32_t index, Napi::Value value) { HandleScope scope(_env); return Value().Set(index, value); } inline bool ObjectReference::Set(uint32_t index, const char* utf8value) { HandleScope scope(_env); return Value().Set(index, utf8value); } inline bool ObjectReference::Set(uint32_t index, const std::string& utf8value) { HandleScope scope(_env); return Value().Set(index, utf8value); } inline bool ObjectReference::Set(uint32_t index, bool boolValue) { HandleScope scope(_env); return Value().Set(index, boolValue); } inline bool ObjectReference::Set(uint32_t index, double numberValue) { HandleScope scope(_env); return Value().Set(index, numberValue); } //////////////////////////////////////////////////////////////////////////////// // FunctionReference class //////////////////////////////////////////////////////////////////////////////// inline FunctionReference::FunctionReference(): Reference() { } inline FunctionReference::FunctionReference(napi_env env, napi_ref ref) : Reference(env, ref) { } inline FunctionReference::FunctionReference(Reference&& other) : Reference(std::move(other)) { } inline FunctionReference& FunctionReference::operator =(Reference&& other) { static_cast*>(this)->operator=(std::move(other)); return *this; } inline FunctionReference::FunctionReference(FunctionReference&& other) : Reference(std::move(other)) { } inline FunctionReference& FunctionReference::operator =(FunctionReference&& other) { static_cast*>(this)->operator=(std::move(other)); return *this; } inline Napi::Value FunctionReference::operator ()( const std::initializer_list& args) const { EscapableHandleScope scope(_env); return scope.Escape(Value()(args)); } inline Napi::Value FunctionReference::Call(const std::initializer_list& args) const { EscapableHandleScope scope(_env); Napi::Value result = Value().Call(args); if (scope.Env().IsExceptionPending()) { return Value(); } return scope.Escape(result); } inline Napi::Value FunctionReference::Call(const std::vector& args) const { EscapableHandleScope scope(_env); Napi::Value result = Value().Call(args); if (scope.Env().IsExceptionPending()) { return Value(); } return scope.Escape(result); } inline Napi::Value FunctionReference::Call( napi_value recv, const std::initializer_list& args) const { EscapableHandleScope scope(_env); Napi::Value result = Value().Call(recv, args); if (scope.Env().IsExceptionPending()) { return Value(); } return scope.Escape(result); } inline Napi::Value FunctionReference::Call( napi_value recv, const std::vector& args) const { EscapableHandleScope scope(_env); Napi::Value result = Value().Call(recv, args); if (scope.Env().IsExceptionPending()) { return Value(); } return scope.Escape(result); } inline Napi::Value FunctionReference::Call( napi_value recv, size_t argc, const napi_value* args) const { EscapableHandleScope scope(_env); Napi::Value result = Value().Call(recv, argc, args); if (scope.Env().IsExceptionPending()) { return Value(); } return scope.Escape(result); } inline Napi::Value FunctionReference::MakeCallback( napi_value recv, const std::initializer_list& args, napi_async_context context) const { EscapableHandleScope scope(_env); Napi::Value result = Value().MakeCallback(recv, args, context); if (scope.Env().IsExceptionPending()) { return Value(); } return scope.Escape(result); } inline Napi::Value FunctionReference::MakeCallback( napi_value recv, const std::vector& args, napi_async_context context) const { EscapableHandleScope scope(_env); Napi::Value result = Value().MakeCallback(recv, args, context); if (scope.Env().IsExceptionPending()) { return Value(); } return scope.Escape(result); } inline Napi::Value FunctionReference::MakeCallback( napi_value recv, size_t argc, const napi_value* args, napi_async_context context) const { EscapableHandleScope scope(_env); Napi::Value result = Value().MakeCallback(recv, argc, args, context); if (scope.Env().IsExceptionPending()) { return Value(); } return scope.Escape(result); } inline Object FunctionReference::New(const std::initializer_list& args) const { EscapableHandleScope scope(_env); return scope.Escape(Value().New(args)).As(); } inline Object FunctionReference::New(const std::vector& args) const { EscapableHandleScope scope(_env); return scope.Escape(Value().New(args)).As(); } //////////////////////////////////////////////////////////////////////////////// // CallbackInfo class //////////////////////////////////////////////////////////////////////////////// inline CallbackInfo::CallbackInfo(napi_env env, napi_callback_info info) : _env(env), _info(info), _this(nullptr), _dynamicArgs(nullptr), _data(nullptr) { _argc = _staticArgCount; _argv = _staticArgs; napi_status status = napi_get_cb_info(env, info, &_argc, _argv, &_this, &_data); NAPI_THROW_IF_FAILED_VOID(_env, status); if (_argc > _staticArgCount) { // Use either a fixed-size array (on the stack) or a dynamically-allocated // array (on the heap) depending on the number of args. _dynamicArgs = new napi_value[_argc]; _argv = _dynamicArgs; status = napi_get_cb_info(env, info, &_argc, _argv, nullptr, nullptr); NAPI_THROW_IF_FAILED_VOID(_env, status); } } inline CallbackInfo::~CallbackInfo() { if (_dynamicArgs != nullptr) { delete[] _dynamicArgs; } } inline Value CallbackInfo::NewTarget() const { napi_value newTarget; napi_status status = napi_get_new_target(_env, _info, &newTarget); NAPI_THROW_IF_FAILED(_env, status, Value()); return Value(_env, newTarget); } inline bool CallbackInfo::IsConstructCall() const { return !NewTarget().IsEmpty(); } inline Napi::Env CallbackInfo::Env() const { return Napi::Env(_env); } inline size_t CallbackInfo::Length() const { return _argc; } inline const Value CallbackInfo::operator [](size_t index) const { return index < _argc ? Value(_env, _argv[index]) : Env().Undefined(); } inline Value CallbackInfo::This() const { if (_this == nullptr) { return Env().Undefined(); } return Object(_env, _this); } inline void* CallbackInfo::Data() const { return _data; } inline void CallbackInfo::SetData(void* data) { _data = data; } //////////////////////////////////////////////////////////////////////////////// // PropertyDescriptor class //////////////////////////////////////////////////////////////////////////////// template PropertyDescriptor PropertyDescriptor::Accessor(const char* utf8name, napi_property_attributes attributes, void* data) { napi_property_descriptor desc = napi_property_descriptor(); desc.utf8name = utf8name; desc.getter = details::TemplatedCallback; desc.attributes = attributes; desc.data = data; return desc; } template PropertyDescriptor PropertyDescriptor::Accessor(const std::string& utf8name, napi_property_attributes attributes, void* data) { return Accessor(utf8name.c_str(), attributes, data); } template PropertyDescriptor PropertyDescriptor::Accessor(Name name, napi_property_attributes attributes, void* data) { napi_property_descriptor desc = napi_property_descriptor(); desc.name = name; desc.getter = details::TemplatedCallback; desc.attributes = attributes; desc.data = data; return desc; } template < typename PropertyDescriptor::GetterCallback Getter, typename PropertyDescriptor::SetterCallback Setter> PropertyDescriptor PropertyDescriptor::Accessor(const char* utf8name, napi_property_attributes attributes, void* data) { napi_property_descriptor desc = napi_property_descriptor(); desc.utf8name = utf8name; desc.getter = details::TemplatedCallback; desc.setter = details::TemplatedVoidCallback; desc.attributes = attributes; desc.data = data; return desc; } template < typename PropertyDescriptor::GetterCallback Getter, typename PropertyDescriptor::SetterCallback Setter> PropertyDescriptor PropertyDescriptor::Accessor(const std::string& utf8name, napi_property_attributes attributes, void* data) { return Accessor(utf8name.c_str(), attributes, data); } template < typename PropertyDescriptor::GetterCallback Getter, typename PropertyDescriptor::SetterCallback Setter> PropertyDescriptor PropertyDescriptor::Accessor(Name name, napi_property_attributes attributes, void* data) { napi_property_descriptor desc = napi_property_descriptor(); desc.name = name; desc.getter = details::TemplatedCallback; desc.setter = details::TemplatedVoidCallback; desc.attributes = attributes; desc.data = data; return desc; } template inline PropertyDescriptor PropertyDescriptor::Accessor(Napi::Env env, Napi::Object object, const char* utf8name, Getter getter, napi_property_attributes attributes, void* data) { using CbData = details::CallbackData; auto callbackData = new CbData({ getter, data }); napi_status status = AttachData(env, object, callbackData); if (status != napi_ok) { delete callbackData; NAPI_THROW_IF_FAILED(env, status, napi_property_descriptor()); } return PropertyDescriptor({ utf8name, nullptr, nullptr, CbData::Wrapper, nullptr, nullptr, attributes, callbackData }); } template inline PropertyDescriptor PropertyDescriptor::Accessor(Napi::Env env, Napi::Object object, const std::string& utf8name, Getter getter, napi_property_attributes attributes, void* data) { return Accessor(env, object, utf8name.c_str(), getter, attributes, data); } template inline PropertyDescriptor PropertyDescriptor::Accessor(Napi::Env env, Napi::Object object, Name name, Getter getter, napi_property_attributes attributes, void* data) { using CbData = details::CallbackData; auto callbackData = new CbData({ getter, data }); napi_status status = AttachData(env, object, callbackData); if (status != napi_ok) { delete callbackData; NAPI_THROW_IF_FAILED(env, status, napi_property_descriptor()); } return PropertyDescriptor({ nullptr, name, nullptr, CbData::Wrapper, nullptr, nullptr, attributes, callbackData }); } template inline PropertyDescriptor PropertyDescriptor::Accessor(Napi::Env env, Napi::Object object, const char* utf8name, Getter getter, Setter setter, napi_property_attributes attributes, void* data) { using CbData = details::AccessorCallbackData; auto callbackData = new CbData({ getter, setter, data }); napi_status status = AttachData(env, object, callbackData); if (status != napi_ok) { delete callbackData; NAPI_THROW_IF_FAILED(env, status, napi_property_descriptor()); } return PropertyDescriptor({ utf8name, nullptr, nullptr, CbData::GetterWrapper, CbData::SetterWrapper, nullptr, attributes, callbackData }); } template inline PropertyDescriptor PropertyDescriptor::Accessor(Napi::Env env, Napi::Object object, const std::string& utf8name, Getter getter, Setter setter, napi_property_attributes attributes, void* data) { return Accessor(env, object, utf8name.c_str(), getter, setter, attributes, data); } template inline PropertyDescriptor PropertyDescriptor::Accessor(Napi::Env env, Napi::Object object, Name name, Getter getter, Setter setter, napi_property_attributes attributes, void* data) { using CbData = details::AccessorCallbackData; auto callbackData = new CbData({ getter, setter, data }); napi_status status = AttachData(env, object, callbackData); if (status != napi_ok) { delete callbackData; NAPI_THROW_IF_FAILED(env, status, napi_property_descriptor()); } return PropertyDescriptor({ nullptr, name, nullptr, CbData::GetterWrapper, CbData::SetterWrapper, nullptr, attributes, callbackData }); } template inline PropertyDescriptor PropertyDescriptor::Function(Napi::Env env, Napi::Object /*object*/, const char* utf8name, Callable cb, napi_property_attributes attributes, void* data) { return PropertyDescriptor({ utf8name, nullptr, nullptr, nullptr, nullptr, Napi::Function::New(env, cb, utf8name, data), attributes, nullptr }); } template inline PropertyDescriptor PropertyDescriptor::Function(Napi::Env env, Napi::Object object, const std::string& utf8name, Callable cb, napi_property_attributes attributes, void* data) { return Function(env, object, utf8name.c_str(), cb, attributes, data); } template inline PropertyDescriptor PropertyDescriptor::Function(Napi::Env env, Napi::Object /*object*/, Name name, Callable cb, napi_property_attributes attributes, void* data) { return PropertyDescriptor({ nullptr, name, nullptr, nullptr, nullptr, Napi::Function::New(env, cb, nullptr, data), attributes, nullptr }); } inline PropertyDescriptor PropertyDescriptor::Value(const char* utf8name, napi_value value, napi_property_attributes attributes) { return PropertyDescriptor({ utf8name, nullptr, nullptr, nullptr, nullptr, value, attributes, nullptr }); } inline PropertyDescriptor PropertyDescriptor::Value(const std::string& utf8name, napi_value value, napi_property_attributes attributes) { return Value(utf8name.c_str(), value, attributes); } inline PropertyDescriptor PropertyDescriptor::Value(napi_value name, napi_value value, napi_property_attributes attributes) { return PropertyDescriptor({ nullptr, name, nullptr, nullptr, nullptr, value, attributes, nullptr }); } inline PropertyDescriptor PropertyDescriptor::Value(Name name, Napi::Value value, napi_property_attributes attributes) { napi_value nameValue = name; napi_value valueValue = value; return PropertyDescriptor::Value(nameValue, valueValue, attributes); } inline PropertyDescriptor::PropertyDescriptor(napi_property_descriptor desc) : _desc(desc) { } inline PropertyDescriptor::operator napi_property_descriptor&() { return _desc; } inline PropertyDescriptor::operator const napi_property_descriptor&() const { return _desc; } //////////////////////////////////////////////////////////////////////////////// // InstanceWrap class //////////////////////////////////////////////////////////////////////////////// template inline void InstanceWrap::AttachPropData(napi_env env, napi_value value, const napi_property_descriptor* prop) { napi_status status; if (!(prop->attributes & napi_static)) { if (prop->method == T::InstanceVoidMethodCallbackWrapper) { status = Napi::details::AttachData(env, value, static_cast(prop->data)); NAPI_THROW_IF_FAILED_VOID(env, status); } else if (prop->method == T::InstanceMethodCallbackWrapper) { status = Napi::details::AttachData(env, value, static_cast(prop->data)); NAPI_THROW_IF_FAILED_VOID(env, status); } else if (prop->getter == T::InstanceGetterCallbackWrapper || prop->setter == T::InstanceSetterCallbackWrapper) { status = Napi::details::AttachData(env, value, static_cast(prop->data)); NAPI_THROW_IF_FAILED_VOID(env, status); } } } template inline ClassPropertyDescriptor InstanceWrap::InstanceMethod( const char* utf8name, InstanceVoidMethodCallback method, napi_property_attributes attributes, void* data) { InstanceVoidMethodCallbackData* callbackData = new InstanceVoidMethodCallbackData({ method, data}); napi_property_descriptor desc = napi_property_descriptor(); desc.utf8name = utf8name; desc.method = T::InstanceVoidMethodCallbackWrapper; desc.data = callbackData; desc.attributes = attributes; return desc; } template inline ClassPropertyDescriptor InstanceWrap::InstanceMethod( const char* utf8name, InstanceMethodCallback method, napi_property_attributes attributes, void* data) { InstanceMethodCallbackData* callbackData = new InstanceMethodCallbackData({ method, data }); napi_property_descriptor desc = napi_property_descriptor(); desc.utf8name = utf8name; desc.method = T::InstanceMethodCallbackWrapper; desc.data = callbackData; desc.attributes = attributes; return desc; } template inline ClassPropertyDescriptor InstanceWrap::InstanceMethod( Symbol name, InstanceVoidMethodCallback method, napi_property_attributes attributes, void* data) { InstanceVoidMethodCallbackData* callbackData = new InstanceVoidMethodCallbackData({ method, data}); napi_property_descriptor desc = napi_property_descriptor(); desc.name = name; desc.method = T::InstanceVoidMethodCallbackWrapper; desc.data = callbackData; desc.attributes = attributes; return desc; } template inline ClassPropertyDescriptor InstanceWrap::InstanceMethod( Symbol name, InstanceMethodCallback method, napi_property_attributes attributes, void* data) { InstanceMethodCallbackData* callbackData = new InstanceMethodCallbackData({ method, data }); napi_property_descriptor desc = napi_property_descriptor(); desc.name = name; desc.method = T::InstanceMethodCallbackWrapper; desc.data = callbackData; desc.attributes = attributes; return desc; } template template ::InstanceVoidMethodCallback method> inline ClassPropertyDescriptor InstanceWrap::InstanceMethod( const char* utf8name, napi_property_attributes attributes, void* data) { napi_property_descriptor desc = napi_property_descriptor(); desc.utf8name = utf8name; desc.method = details::TemplatedInstanceVoidCallback; desc.data = data; desc.attributes = attributes; return desc; } template template ::InstanceMethodCallback method> inline ClassPropertyDescriptor InstanceWrap::InstanceMethod( const char* utf8name, napi_property_attributes attributes, void* data) { napi_property_descriptor desc = napi_property_descriptor(); desc.utf8name = utf8name; desc.method = details::TemplatedInstanceCallback; desc.data = data; desc.attributes = attributes; return desc; } template template ::InstanceVoidMethodCallback method> inline ClassPropertyDescriptor InstanceWrap::InstanceMethod( Symbol name, napi_property_attributes attributes, void* data) { napi_property_descriptor desc = napi_property_descriptor(); desc.name = name; desc.method = details::TemplatedInstanceVoidCallback; desc.data = data; desc.attributes = attributes; return desc; } template template ::InstanceMethodCallback method> inline ClassPropertyDescriptor InstanceWrap::InstanceMethod( Symbol name, napi_property_attributes attributes, void* data) { napi_property_descriptor desc = napi_property_descriptor(); desc.name = name; desc.method = details::TemplatedInstanceCallback; desc.data = data; desc.attributes = attributes; return desc; } template inline ClassPropertyDescriptor InstanceWrap::InstanceAccessor( const char* utf8name, InstanceGetterCallback getter, InstanceSetterCallback setter, napi_property_attributes attributes, void* data) { InstanceAccessorCallbackData* callbackData = new InstanceAccessorCallbackData({ getter, setter, data }); napi_property_descriptor desc = napi_property_descriptor(); desc.utf8name = utf8name; desc.getter = getter != nullptr ? T::InstanceGetterCallbackWrapper : nullptr; desc.setter = setter != nullptr ? T::InstanceSetterCallbackWrapper : nullptr; desc.data = callbackData; desc.attributes = attributes; return desc; } template inline ClassPropertyDescriptor InstanceWrap::InstanceAccessor( Symbol name, InstanceGetterCallback getter, InstanceSetterCallback setter, napi_property_attributes attributes, void* data) { InstanceAccessorCallbackData* callbackData = new InstanceAccessorCallbackData({ getter, setter, data }); napi_property_descriptor desc = napi_property_descriptor(); desc.name = name; desc.getter = getter != nullptr ? T::InstanceGetterCallbackWrapper : nullptr; desc.setter = setter != nullptr ? T::InstanceSetterCallbackWrapper : nullptr; desc.data = callbackData; desc.attributes = attributes; return desc; } template template ::InstanceGetterCallback getter, typename InstanceWrap::InstanceSetterCallback setter> inline ClassPropertyDescriptor InstanceWrap::InstanceAccessor( const char* utf8name, napi_property_attributes attributes, void* data) { napi_property_descriptor desc = napi_property_descriptor(); desc.utf8name = utf8name; desc.getter = details::TemplatedInstanceCallback; desc.setter = This::WrapSetter(This::SetterTag()); desc.data = data; desc.attributes = attributes; return desc; } template template ::InstanceGetterCallback getter, typename InstanceWrap::InstanceSetterCallback setter> inline ClassPropertyDescriptor InstanceWrap::InstanceAccessor( Symbol name, napi_property_attributes attributes, void* data) { napi_property_descriptor desc = napi_property_descriptor(); desc.name = name; desc.getter = details::TemplatedInstanceCallback; desc.setter = This::WrapSetter(This::SetterTag()); desc.data = data; desc.attributes = attributes; return desc; } template inline ClassPropertyDescriptor InstanceWrap::InstanceValue( const char* utf8name, Napi::Value value, napi_property_attributes attributes) { napi_property_descriptor desc = napi_property_descriptor(); desc.utf8name = utf8name; desc.value = value; desc.attributes = attributes; return desc; } template inline ClassPropertyDescriptor InstanceWrap::InstanceValue( Symbol name, Napi::Value value, napi_property_attributes attributes) { napi_property_descriptor desc = napi_property_descriptor(); desc.name = name; desc.value = value; desc.attributes = attributes; return desc; } template inline napi_value InstanceWrap::InstanceVoidMethodCallbackWrapper( napi_env env, napi_callback_info info) { return details::WrapCallback([&] { CallbackInfo callbackInfo(env, info); InstanceVoidMethodCallbackData* callbackData = reinterpret_cast(callbackInfo.Data()); callbackInfo.SetData(callbackData->data); T* instance = T::Unwrap(callbackInfo.This().As()); auto cb = callbackData->callback; (instance->*cb)(callbackInfo); return nullptr; }); } template inline napi_value InstanceWrap::InstanceMethodCallbackWrapper( napi_env env, napi_callback_info info) { return details::WrapCallback([&] { CallbackInfo callbackInfo(env, info); InstanceMethodCallbackData* callbackData = reinterpret_cast(callbackInfo.Data()); callbackInfo.SetData(callbackData->data); T* instance = T::Unwrap(callbackInfo.This().As()); auto cb = callbackData->callback; return (instance->*cb)(callbackInfo); }); } template inline napi_value InstanceWrap::InstanceGetterCallbackWrapper( napi_env env, napi_callback_info info) { return details::WrapCallback([&] { CallbackInfo callbackInfo(env, info); InstanceAccessorCallbackData* callbackData = reinterpret_cast(callbackInfo.Data()); callbackInfo.SetData(callbackData->data); T* instance = T::Unwrap(callbackInfo.This().As()); auto cb = callbackData->getterCallback; return (instance->*cb)(callbackInfo); }); } template inline napi_value InstanceWrap::InstanceSetterCallbackWrapper( napi_env env, napi_callback_info info) { return details::WrapCallback([&] { CallbackInfo callbackInfo(env, info); InstanceAccessorCallbackData* callbackData = reinterpret_cast(callbackInfo.Data()); callbackInfo.SetData(callbackData->data); T* instance = T::Unwrap(callbackInfo.This().As()); auto cb = callbackData->setterCallback; (instance->*cb)(callbackInfo, callbackInfo[0]); return nullptr; }); } template template ::InstanceSetterCallback method> inline napi_value InstanceWrap::WrappedMethod( napi_env env, napi_callback_info info) NAPI_NOEXCEPT { return details::WrapCallback([&] { const CallbackInfo cbInfo(env, info); T* instance = T::Unwrap(cbInfo.This().As()); (instance->*method)(cbInfo, cbInfo[0]); return nullptr; }); } //////////////////////////////////////////////////////////////////////////////// // ObjectWrap class //////////////////////////////////////////////////////////////////////////////// template inline ObjectWrap::ObjectWrap(const Napi::CallbackInfo& callbackInfo) { napi_env env = callbackInfo.Env(); napi_value wrapper = callbackInfo.This(); napi_status status; napi_ref ref; T* instance = static_cast(this); status = napi_wrap(env, wrapper, instance, FinalizeCallback, nullptr, &ref); NAPI_THROW_IF_FAILED_VOID(env, status); Reference* instanceRef = instance; *instanceRef = Reference(env, ref); } template inline ObjectWrap::~ObjectWrap() { // If the JS object still exists at this point, remove the finalizer added // through `napi_wrap()`. if (!IsEmpty()) { Object object = Value(); // It is not valid to call `napi_remove_wrap()` with an empty `object`. // This happens e.g. during garbage collection. if (!object.IsEmpty() && _construction_failed) { napi_remove_wrap(Env(), object, nullptr); } } } template inline T* ObjectWrap::Unwrap(Object wrapper) { T* unwrapped; napi_status status = napi_unwrap(wrapper.Env(), wrapper, reinterpret_cast(&unwrapped)); NAPI_THROW_IF_FAILED(wrapper.Env(), status, nullptr); return unwrapped; } template inline Function ObjectWrap::DefineClass(Napi::Env env, const char* utf8name, const size_t props_count, const napi_property_descriptor* descriptors, void* data) { napi_status status; std::vector props(props_count); // We copy the descriptors to a local array because before defining the class // we must replace static method property descriptors with value property // descriptors such that the value is a function-valued `napi_value` created // with `CreateFunction()`. // // This replacement could be made for instance methods as well, but V8 aborts // if we do that, because it expects methods defined on the prototype template // to have `FunctionTemplate`s. for (size_t index = 0; index < props_count; index++) { props[index] = descriptors[index]; napi_property_descriptor* prop = &props[index]; if (prop->method == T::StaticMethodCallbackWrapper) { status = CreateFunction(env, utf8name, prop->method, static_cast(prop->data), &(prop->value)); NAPI_THROW_IF_FAILED(env, status, Function()); prop->method = nullptr; prop->data = nullptr; } else if (prop->method == T::StaticVoidMethodCallbackWrapper) { status = CreateFunction(env, utf8name, prop->method, static_cast(prop->data), &(prop->value)); NAPI_THROW_IF_FAILED(env, status, Function()); prop->method = nullptr; prop->data = nullptr; } } napi_value value; status = napi_define_class(env, utf8name, NAPI_AUTO_LENGTH, T::ConstructorCallbackWrapper, data, props_count, props.data(), &value); NAPI_THROW_IF_FAILED(env, status, Function()); // After defining the class we iterate once more over the property descriptors // and attach the data associated with accessors and instance methods to the // newly created JavaScript class. for (size_t idx = 0; idx < props_count; idx++) { const napi_property_descriptor* prop = &props[idx]; if (prop->getter == T::StaticGetterCallbackWrapper || prop->setter == T::StaticSetterCallbackWrapper) { status = Napi::details::AttachData(env, value, static_cast(prop->data)); NAPI_THROW_IF_FAILED(env, status, Function()); } else { // InstanceWrap::AttachPropData is responsible for attaching the data // of instance methods and accessors. T::AttachPropData(env, value, prop); } } return Function(env, value); } template inline Function ObjectWrap::DefineClass( Napi::Env env, const char* utf8name, const std::initializer_list>& properties, void* data) { return DefineClass(env, utf8name, properties.size(), reinterpret_cast(properties.begin()), data); } template inline Function ObjectWrap::DefineClass( Napi::Env env, const char* utf8name, const std::vector>& properties, void* data) { return DefineClass(env, utf8name, properties.size(), reinterpret_cast(properties.data()), data); } template inline ClassPropertyDescriptor ObjectWrap::StaticMethod( const char* utf8name, StaticVoidMethodCallback method, napi_property_attributes attributes, void* data) { StaticVoidMethodCallbackData* callbackData = new StaticVoidMethodCallbackData({ method, data }); napi_property_descriptor desc = napi_property_descriptor(); desc.utf8name = utf8name; desc.method = T::StaticVoidMethodCallbackWrapper; desc.data = callbackData; desc.attributes = static_cast(attributes | napi_static); return desc; } template inline ClassPropertyDescriptor ObjectWrap::StaticMethod( const char* utf8name, StaticMethodCallback method, napi_property_attributes attributes, void* data) { StaticMethodCallbackData* callbackData = new StaticMethodCallbackData({ method, data }); napi_property_descriptor desc = napi_property_descriptor(); desc.utf8name = utf8name; desc.method = T::StaticMethodCallbackWrapper; desc.data = callbackData; desc.attributes = static_cast(attributes | napi_static); return desc; } template inline ClassPropertyDescriptor ObjectWrap::StaticMethod( Symbol name, StaticVoidMethodCallback method, napi_property_attributes attributes, void* data) { StaticVoidMethodCallbackData* callbackData = new StaticVoidMethodCallbackData({ method, data }); napi_property_descriptor desc = napi_property_descriptor(); desc.name = name; desc.method = T::StaticVoidMethodCallbackWrapper; desc.data = callbackData; desc.attributes = static_cast(attributes | napi_static); return desc; } template inline ClassPropertyDescriptor ObjectWrap::StaticMethod( Symbol name, StaticMethodCallback method, napi_property_attributes attributes, void* data) { StaticMethodCallbackData* callbackData = new StaticMethodCallbackData({ method, data }); napi_property_descriptor desc = napi_property_descriptor(); desc.name = name; desc.method = T::StaticMethodCallbackWrapper; desc.data = callbackData; desc.attributes = static_cast(attributes | napi_static); return desc; } template template ::StaticVoidMethodCallback method> inline ClassPropertyDescriptor ObjectWrap::StaticMethod( const char* utf8name, napi_property_attributes attributes, void* data) { napi_property_descriptor desc = napi_property_descriptor(); desc.utf8name = utf8name; desc.method = details::TemplatedVoidCallback; desc.data = data; desc.attributes = static_cast(attributes | napi_static); return desc; } template template ::StaticVoidMethodCallback method> inline ClassPropertyDescriptor ObjectWrap::StaticMethod( Symbol name, napi_property_attributes attributes, void* data) { napi_property_descriptor desc = napi_property_descriptor(); desc.name = name; desc.method = details::TemplatedVoidCallback; desc.data = data; desc.attributes = static_cast(attributes | napi_static); return desc; } template template ::StaticMethodCallback method> inline ClassPropertyDescriptor ObjectWrap::StaticMethod( const char* utf8name, napi_property_attributes attributes, void* data) { napi_property_descriptor desc = napi_property_descriptor(); desc.utf8name = utf8name; desc.method = details::TemplatedCallback; desc.data = data; desc.attributes = static_cast(attributes | napi_static); return desc; } template template ::StaticMethodCallback method> inline ClassPropertyDescriptor ObjectWrap::StaticMethod( Symbol name, napi_property_attributes attributes, void* data) { napi_property_descriptor desc = napi_property_descriptor(); desc.name = name; desc.method = details::TemplatedCallback; desc.data = data; desc.attributes = static_cast(attributes | napi_static); return desc; } template inline ClassPropertyDescriptor ObjectWrap::StaticAccessor( const char* utf8name, StaticGetterCallback getter, StaticSetterCallback setter, napi_property_attributes attributes, void* data) { StaticAccessorCallbackData* callbackData = new StaticAccessorCallbackData({ getter, setter, data }); napi_property_descriptor desc = napi_property_descriptor(); desc.utf8name = utf8name; desc.getter = getter != nullptr ? T::StaticGetterCallbackWrapper : nullptr; desc.setter = setter != nullptr ? T::StaticSetterCallbackWrapper : nullptr; desc.data = callbackData; desc.attributes = static_cast(attributes | napi_static); return desc; } template inline ClassPropertyDescriptor ObjectWrap::StaticAccessor( Symbol name, StaticGetterCallback getter, StaticSetterCallback setter, napi_property_attributes attributes, void* data) { StaticAccessorCallbackData* callbackData = new StaticAccessorCallbackData({ getter, setter, data }); napi_property_descriptor desc = napi_property_descriptor(); desc.name = name; desc.getter = getter != nullptr ? T::StaticGetterCallbackWrapper : nullptr; desc.setter = setter != nullptr ? T::StaticSetterCallbackWrapper : nullptr; desc.data = callbackData; desc.attributes = static_cast(attributes | napi_static); return desc; } template template ::StaticGetterCallback getter, typename ObjectWrap::StaticSetterCallback setter> inline ClassPropertyDescriptor ObjectWrap::StaticAccessor( const char* utf8name, napi_property_attributes attributes, void* data) { napi_property_descriptor desc = napi_property_descriptor(); desc.utf8name = utf8name; desc.getter = details::TemplatedCallback; desc.setter = This::WrapStaticSetter(This::StaticSetterTag()); desc.data = data; desc.attributes = static_cast(attributes | napi_static); return desc; } template template ::StaticGetterCallback getter, typename ObjectWrap::StaticSetterCallback setter> inline ClassPropertyDescriptor ObjectWrap::StaticAccessor( Symbol name, napi_property_attributes attributes, void* data) { napi_property_descriptor desc = napi_property_descriptor(); desc.name = name; desc.getter = details::TemplatedCallback; desc.setter = This::WrapStaticSetter(This::StaticSetterTag()); desc.data = data; desc.attributes = static_cast(attributes | napi_static); return desc; } template inline ClassPropertyDescriptor ObjectWrap::StaticValue(const char* utf8name, Napi::Value value, napi_property_attributes attributes) { napi_property_descriptor desc = napi_property_descriptor(); desc.utf8name = utf8name; desc.value = value; desc.attributes = static_cast(attributes | napi_static); return desc; } template inline ClassPropertyDescriptor ObjectWrap::StaticValue(Symbol name, Napi::Value value, napi_property_attributes attributes) { napi_property_descriptor desc = napi_property_descriptor(); desc.name = name; desc.value = value; desc.attributes = static_cast(attributes | napi_static); return desc; } template inline void ObjectWrap::Finalize(Napi::Env /*env*/) {} template inline napi_value ObjectWrap::ConstructorCallbackWrapper( napi_env env, napi_callback_info info) { napi_value new_target; napi_status status = napi_get_new_target(env, info, &new_target); if (status != napi_ok) return nullptr; bool isConstructCall = (new_target != nullptr); if (!isConstructCall) { napi_throw_type_error(env, nullptr, "Class constructors cannot be invoked without 'new'"); return nullptr; } napi_value wrapper = details::WrapCallback([&] { CallbackInfo callbackInfo(env, info); T* instance = new T(callbackInfo); #ifdef NAPI_CPP_EXCEPTIONS instance->_construction_failed = false; #else if (callbackInfo.Env().IsExceptionPending()) { // We need to clear the exception so that removing the wrap might work. Error e = callbackInfo.Env().GetAndClearPendingException(); delete instance; e.ThrowAsJavaScriptException(); } else { instance->_construction_failed = false; } # endif // NAPI_CPP_EXCEPTIONS return callbackInfo.This(); }); return wrapper; } template inline napi_value ObjectWrap::StaticVoidMethodCallbackWrapper( napi_env env, napi_callback_info info) { return details::WrapCallback([&] { CallbackInfo callbackInfo(env, info); StaticVoidMethodCallbackData* callbackData = reinterpret_cast(callbackInfo.Data()); callbackInfo.SetData(callbackData->data); callbackData->callback(callbackInfo); return nullptr; }); } template inline napi_value ObjectWrap::StaticMethodCallbackWrapper( napi_env env, napi_callback_info info) { return details::WrapCallback([&] { CallbackInfo callbackInfo(env, info); StaticMethodCallbackData* callbackData = reinterpret_cast(callbackInfo.Data()); callbackInfo.SetData(callbackData->data); return callbackData->callback(callbackInfo); }); } template inline napi_value ObjectWrap::StaticGetterCallbackWrapper( napi_env env, napi_callback_info info) { return details::WrapCallback([&] { CallbackInfo callbackInfo(env, info); StaticAccessorCallbackData* callbackData = reinterpret_cast(callbackInfo.Data()); callbackInfo.SetData(callbackData->data); return callbackData->getterCallback(callbackInfo); }); } template inline napi_value ObjectWrap::StaticSetterCallbackWrapper( napi_env env, napi_callback_info info) { return details::WrapCallback([&] { CallbackInfo callbackInfo(env, info); StaticAccessorCallbackData* callbackData = reinterpret_cast(callbackInfo.Data()); callbackInfo.SetData(callbackData->data); callbackData->setterCallback(callbackInfo, callbackInfo[0]); return nullptr; }); } template inline void ObjectWrap::FinalizeCallback(napi_env env, void* data, void* /*hint*/) { HandleScope scope(env); T* instance = static_cast(data); instance->Finalize(Napi::Env(env)); delete instance; } template template ::StaticSetterCallback method> inline napi_value ObjectWrap::WrappedMethod( napi_env env, napi_callback_info info) NAPI_NOEXCEPT { return details::WrapCallback([&] { const CallbackInfo cbInfo(env, info); method(cbInfo, cbInfo[0]); return nullptr; }); } //////////////////////////////////////////////////////////////////////////////// // HandleScope class //////////////////////////////////////////////////////////////////////////////// inline HandleScope::HandleScope(napi_env env, napi_handle_scope scope) : _env(env), _scope(scope) { } inline HandleScope::HandleScope(Napi::Env env) : _env(env) { napi_status status = napi_open_handle_scope(_env, &_scope); NAPI_THROW_IF_FAILED_VOID(_env, status); } inline HandleScope::~HandleScope() { napi_status status = napi_close_handle_scope(_env, _scope); NAPI_FATAL_IF_FAILED(status, "HandleScope::~HandleScope", "napi_close_handle_scope"); } inline HandleScope::operator napi_handle_scope() const { return _scope; } inline Napi::Env HandleScope::Env() const { return Napi::Env(_env); } //////////////////////////////////////////////////////////////////////////////// // EscapableHandleScope class //////////////////////////////////////////////////////////////////////////////// inline EscapableHandleScope::EscapableHandleScope( napi_env env, napi_escapable_handle_scope scope) : _env(env), _scope(scope) { } inline EscapableHandleScope::EscapableHandleScope(Napi::Env env) : _env(env) { napi_status status = napi_open_escapable_handle_scope(_env, &_scope); NAPI_THROW_IF_FAILED_VOID(_env, status); } inline EscapableHandleScope::~EscapableHandleScope() { napi_status status = napi_close_escapable_handle_scope(_env, _scope); NAPI_FATAL_IF_FAILED(status, "EscapableHandleScope::~EscapableHandleScope", "napi_close_escapable_handle_scope"); } inline EscapableHandleScope::operator napi_escapable_handle_scope() const { return _scope; } inline Napi::Env EscapableHandleScope::Env() const { return Napi::Env(_env); } inline Value EscapableHandleScope::Escape(napi_value escapee) { napi_value result; napi_status status = napi_escape_handle(_env, _scope, escapee, &result); NAPI_THROW_IF_FAILED(_env, status, Value()); return Value(_env, result); } #if (NAPI_VERSION > 2) //////////////////////////////////////////////////////////////////////////////// // CallbackScope class //////////////////////////////////////////////////////////////////////////////// inline CallbackScope::CallbackScope( napi_env env, napi_callback_scope scope) : _env(env), _scope(scope) { } inline CallbackScope::CallbackScope(napi_env env, napi_async_context context) : _env(env) { napi_status status = napi_open_callback_scope( _env, Object::New(env), context, &_scope); NAPI_THROW_IF_FAILED_VOID(_env, status); } inline CallbackScope::~CallbackScope() { napi_status status = napi_close_callback_scope(_env, _scope); NAPI_FATAL_IF_FAILED(status, "CallbackScope::~CallbackScope", "napi_close_callback_scope"); } inline CallbackScope::operator napi_callback_scope() const { return _scope; } inline Napi::Env CallbackScope::Env() const { return Napi::Env(_env); } #endif //////////////////////////////////////////////////////////////////////////////// // AsyncContext class //////////////////////////////////////////////////////////////////////////////// inline AsyncContext::AsyncContext(napi_env env, const char* resource_name) : AsyncContext(env, resource_name, Object::New(env)) { } inline AsyncContext::AsyncContext(napi_env env, const char* resource_name, const Object& resource) : _env(env), _context(nullptr) { napi_value resource_id; napi_status status = napi_create_string_utf8( _env, resource_name, NAPI_AUTO_LENGTH, &resource_id); NAPI_THROW_IF_FAILED_VOID(_env, status); status = napi_async_init(_env, resource, resource_id, &_context); NAPI_THROW_IF_FAILED_VOID(_env, status); } inline AsyncContext::~AsyncContext() { if (_context != nullptr) { napi_async_destroy(_env, _context); _context = nullptr; } } inline AsyncContext::AsyncContext(AsyncContext&& other) { _env = other._env; other._env = nullptr; _context = other._context; other._context = nullptr; } inline AsyncContext& AsyncContext::operator =(AsyncContext&& other) { _env = other._env; other._env = nullptr; _context = other._context; other._context = nullptr; return *this; } inline AsyncContext::operator napi_async_context() const { return _context; } inline Napi::Env AsyncContext::Env() const { return Napi::Env(_env); } //////////////////////////////////////////////////////////////////////////////// // AsyncWorker class //////////////////////////////////////////////////////////////////////////////// inline AsyncWorker::AsyncWorker(const Function& callback) : AsyncWorker(callback, "generic") { } inline AsyncWorker::AsyncWorker(const Function& callback, const char* resource_name) : AsyncWorker(callback, resource_name, Object::New(callback.Env())) { } inline AsyncWorker::AsyncWorker(const Function& callback, const char* resource_name, const Object& resource) : AsyncWorker(Object::New(callback.Env()), callback, resource_name, resource) { } inline AsyncWorker::AsyncWorker(const Object& receiver, const Function& callback) : AsyncWorker(receiver, callback, "generic") { } inline AsyncWorker::AsyncWorker(const Object& receiver, const Function& callback, const char* resource_name) : AsyncWorker(receiver, callback, resource_name, Object::New(callback.Env())) { } inline AsyncWorker::AsyncWorker(const Object& receiver, const Function& callback, const char* resource_name, const Object& resource) : _env(callback.Env()), _receiver(Napi::Persistent(receiver)), _callback(Napi::Persistent(callback)), _suppress_destruct(false) { napi_value resource_id; napi_status status = napi_create_string_latin1( _env, resource_name, NAPI_AUTO_LENGTH, &resource_id); NAPI_THROW_IF_FAILED_VOID(_env, status); status = napi_create_async_work(_env, resource, resource_id, OnAsyncWorkExecute, OnAsyncWorkComplete, this, &_work); NAPI_THROW_IF_FAILED_VOID(_env, status); } inline AsyncWorker::AsyncWorker(Napi::Env env) : AsyncWorker(env, "generic") { } inline AsyncWorker::AsyncWorker(Napi::Env env, const char* resource_name) : AsyncWorker(env, resource_name, Object::New(env)) { } inline AsyncWorker::AsyncWorker(Napi::Env env, const char* resource_name, const Object& resource) : _env(env), _receiver(), _callback(), _suppress_destruct(false) { napi_value resource_id; napi_status status = napi_create_string_latin1( _env, resource_name, NAPI_AUTO_LENGTH, &resource_id); NAPI_THROW_IF_FAILED_VOID(_env, status); status = napi_create_async_work(_env, resource, resource_id, OnAsyncWorkExecute, OnAsyncWorkComplete, this, &_work); NAPI_THROW_IF_FAILED_VOID(_env, status); } inline AsyncWorker::~AsyncWorker() { if (_work != nullptr) { napi_delete_async_work(_env, _work); _work = nullptr; } } inline void AsyncWorker::Destroy() { delete this; } inline AsyncWorker::AsyncWorker(AsyncWorker&& other) { _env = other._env; other._env = nullptr; _work = other._work; other._work = nullptr; _receiver = std::move(other._receiver); _callback = std::move(other._callback); _error = std::move(other._error); _suppress_destruct = other._suppress_destruct; } inline AsyncWorker& AsyncWorker::operator =(AsyncWorker&& other) { _env = other._env; other._env = nullptr; _work = other._work; other._work = nullptr; _receiver = std::move(other._receiver); _callback = std::move(other._callback); _error = std::move(other._error); _suppress_destruct = other._suppress_destruct; return *this; } inline AsyncWorker::operator napi_async_work() const { return _work; } inline Napi::Env AsyncWorker::Env() const { return Napi::Env(_env); } inline void AsyncWorker::Queue() { napi_status status = napi_queue_async_work(_env, _work); NAPI_THROW_IF_FAILED_VOID(_env, status); } inline void AsyncWorker::Cancel() { napi_status status = napi_cancel_async_work(_env, _work); NAPI_THROW_IF_FAILED_VOID(_env, status); } inline ObjectReference& AsyncWorker::Receiver() { return _receiver; } inline FunctionReference& AsyncWorker::Callback() { return _callback; } inline void AsyncWorker::SuppressDestruct() { _suppress_destruct = true; } inline void AsyncWorker::OnOK() { if (!_callback.IsEmpty()) { _callback.Call(_receiver.Value(), GetResult(_callback.Env())); } } inline void AsyncWorker::OnError(const Error& e) { if (!_callback.IsEmpty()) { _callback.Call(_receiver.Value(), std::initializer_list{ e.Value() }); } } inline void AsyncWorker::SetError(const std::string& error) { _error = error; } inline std::vector AsyncWorker::GetResult(Napi::Env /*env*/) { return {}; } // The OnAsyncWorkExecute method receives an napi_env argument. However, do NOT // use it within this method, as it does not run on the JavaScript thread and // must not run any method that would cause JavaScript to run. In practice, // this means that almost any use of napi_env will be incorrect. inline void AsyncWorker::OnAsyncWorkExecute(napi_env env, void* asyncworker) { AsyncWorker* self = static_cast(asyncworker); self->OnExecute(env); } // The OnExecute method receives an napi_env argument. However, do NOT // use it within this method, as it does not run on the JavaScript thread and // must not run any method that would cause JavaScript to run. In practice, // this means that almost any use of napi_env will be incorrect. inline void AsyncWorker::OnExecute(Napi::Env /*DO_NOT_USE*/) { #ifdef NAPI_CPP_EXCEPTIONS try { Execute(); } catch (const std::exception& e) { SetError(e.what()); } #else // NAPI_CPP_EXCEPTIONS Execute(); #endif // NAPI_CPP_EXCEPTIONS } inline void AsyncWorker::OnAsyncWorkComplete(napi_env env, napi_status status, void* asyncworker) { AsyncWorker* self = static_cast(asyncworker); self->OnWorkComplete(env, status); } inline void AsyncWorker::OnWorkComplete(Napi::Env /*env*/, napi_status status) { if (status != napi_cancelled) { HandleScope scope(_env); details::WrapCallback([&] { if (_error.size() == 0) { OnOK(); } else { OnError(Error::New(_env, _error)); } return nullptr; }); } if (!_suppress_destruct) { Destroy(); } } #if (NAPI_VERSION > 3 && !defined(__wasm32__)) //////////////////////////////////////////////////////////////////////////////// // TypedThreadSafeFunction class //////////////////////////////////////////////////////////////////////////////// // Starting with NAPI 5, the JavaScript function `func` parameter of // `napi_create_threadsafe_function` is optional. #if NAPI_VERSION > 4 // static, with Callback [missing] Resource [missing] Finalizer [missing] template template inline TypedThreadSafeFunction TypedThreadSafeFunction::New( napi_env env, ResourceString resourceName, size_t maxQueueSize, size_t initialThreadCount, ContextType* context) { TypedThreadSafeFunction tsfn; napi_status status = napi_create_threadsafe_function(env, nullptr, nullptr, String::From(env, resourceName), maxQueueSize, initialThreadCount, nullptr, nullptr, context, CallJsInternal, &tsfn._tsfn); if (status != napi_ok) { NAPI_THROW_IF_FAILED( env, status, TypedThreadSafeFunction()); } return tsfn; } // static, with Callback [missing] Resource [passed] Finalizer [missing] template template inline TypedThreadSafeFunction TypedThreadSafeFunction::New( napi_env env, const Object& resource, ResourceString resourceName, size_t maxQueueSize, size_t initialThreadCount, ContextType* context) { TypedThreadSafeFunction tsfn; napi_status status = napi_create_threadsafe_function(env, nullptr, resource, String::From(env, resourceName), maxQueueSize, initialThreadCount, nullptr, nullptr, context, CallJsInternal, &tsfn._tsfn); if (status != napi_ok) { NAPI_THROW_IF_FAILED( env, status, TypedThreadSafeFunction()); } return tsfn; } // static, with Callback [missing] Resource [missing] Finalizer [passed] template template inline TypedThreadSafeFunction TypedThreadSafeFunction::New( napi_env env, ResourceString resourceName, size_t maxQueueSize, size_t initialThreadCount, ContextType* context, Finalizer finalizeCallback, FinalizerDataType* data) { TypedThreadSafeFunction tsfn; auto* finalizeData = new details:: ThreadSafeFinalize( {data, finalizeCallback}); napi_status status = napi_create_threadsafe_function( env, nullptr, nullptr, String::From(env, resourceName), maxQueueSize, initialThreadCount, finalizeData, details::ThreadSafeFinalize:: FinalizeFinalizeWrapperWithDataAndContext, context, CallJsInternal, &tsfn._tsfn); if (status != napi_ok) { delete finalizeData; NAPI_THROW_IF_FAILED( env, status, TypedThreadSafeFunction()); } return tsfn; } // static, with Callback [missing] Resource [passed] Finalizer [passed] template template inline TypedThreadSafeFunction TypedThreadSafeFunction::New( napi_env env, const Object& resource, ResourceString resourceName, size_t maxQueueSize, size_t initialThreadCount, ContextType* context, Finalizer finalizeCallback, FinalizerDataType* data) { TypedThreadSafeFunction tsfn; auto* finalizeData = new details:: ThreadSafeFinalize( {data, finalizeCallback}); napi_status status = napi_create_threadsafe_function( env, nullptr, resource, String::From(env, resourceName), maxQueueSize, initialThreadCount, finalizeData, details::ThreadSafeFinalize:: FinalizeFinalizeWrapperWithDataAndContext, context, CallJsInternal, &tsfn._tsfn); if (status != napi_ok) { delete finalizeData; NAPI_THROW_IF_FAILED( env, status, TypedThreadSafeFunction()); } return tsfn; } #endif // static, with Callback [passed] Resource [missing] Finalizer [missing] template template inline TypedThreadSafeFunction TypedThreadSafeFunction::New( napi_env env, const Function& callback, ResourceString resourceName, size_t maxQueueSize, size_t initialThreadCount, ContextType* context) { TypedThreadSafeFunction tsfn; napi_status status = napi_create_threadsafe_function(env, callback, nullptr, String::From(env, resourceName), maxQueueSize, initialThreadCount, nullptr, nullptr, context, CallJsInternal, &tsfn._tsfn); if (status != napi_ok) { NAPI_THROW_IF_FAILED( env, status, TypedThreadSafeFunction()); } return tsfn; } // static, with Callback [passed] Resource [passed] Finalizer [missing] template template inline TypedThreadSafeFunction TypedThreadSafeFunction::New( napi_env env, const Function& callback, const Object& resource, ResourceString resourceName, size_t maxQueueSize, size_t initialThreadCount, ContextType* context) { TypedThreadSafeFunction tsfn; napi_status status = napi_create_threadsafe_function(env, callback, resource, String::From(env, resourceName), maxQueueSize, initialThreadCount, nullptr, nullptr, context, CallJsInternal, &tsfn._tsfn); if (status != napi_ok) { NAPI_THROW_IF_FAILED( env, status, TypedThreadSafeFunction()); } return tsfn; } // static, with Callback [passed] Resource [missing] Finalizer [passed] template template inline TypedThreadSafeFunction TypedThreadSafeFunction::New( napi_env env, const Function& callback, ResourceString resourceName, size_t maxQueueSize, size_t initialThreadCount, ContextType* context, Finalizer finalizeCallback, FinalizerDataType* data) { TypedThreadSafeFunction tsfn; auto* finalizeData = new details:: ThreadSafeFinalize( {data, finalizeCallback}); napi_status status = napi_create_threadsafe_function( env, callback, nullptr, String::From(env, resourceName), maxQueueSize, initialThreadCount, finalizeData, details::ThreadSafeFinalize:: FinalizeFinalizeWrapperWithDataAndContext, context, CallJsInternal, &tsfn._tsfn); if (status != napi_ok) { delete finalizeData; NAPI_THROW_IF_FAILED( env, status, TypedThreadSafeFunction()); } return tsfn; } // static, with: Callback [passed] Resource [passed] Finalizer [passed] template template inline TypedThreadSafeFunction TypedThreadSafeFunction::New( napi_env env, CallbackType callback, const Object& resource, ResourceString resourceName, size_t maxQueueSize, size_t initialThreadCount, ContextType* context, Finalizer finalizeCallback, FinalizerDataType* data) { TypedThreadSafeFunction tsfn; auto* finalizeData = new details:: ThreadSafeFinalize( {data, finalizeCallback}); napi_status status = napi_create_threadsafe_function( env, details::DefaultCallbackWrapper< CallbackType, TypedThreadSafeFunction>(env, callback), resource, String::From(env, resourceName), maxQueueSize, initialThreadCount, finalizeData, details::ThreadSafeFinalize:: FinalizeFinalizeWrapperWithDataAndContext, context, CallJsInternal, &tsfn._tsfn); if (status != napi_ok) { delete finalizeData; NAPI_THROW_IF_FAILED( env, status, TypedThreadSafeFunction()); } return tsfn; } template inline TypedThreadSafeFunction:: TypedThreadSafeFunction() : _tsfn() {} template inline TypedThreadSafeFunction:: TypedThreadSafeFunction(napi_threadsafe_function tsfn) : _tsfn(tsfn) {} template inline TypedThreadSafeFunction:: operator napi_threadsafe_function() const { return _tsfn; } template inline napi_status TypedThreadSafeFunction::BlockingCall( DataType* data) const { return napi_call_threadsafe_function(_tsfn, data, napi_tsfn_blocking); } template inline napi_status TypedThreadSafeFunction::NonBlockingCall( DataType* data) const { return napi_call_threadsafe_function(_tsfn, data, napi_tsfn_nonblocking); } template inline void TypedThreadSafeFunction::Ref( napi_env env) const { if (_tsfn != nullptr) { napi_status status = napi_ref_threadsafe_function(env, _tsfn); NAPI_THROW_IF_FAILED_VOID(env, status); } } template inline void TypedThreadSafeFunction::Unref( napi_env env) const { if (_tsfn != nullptr) { napi_status status = napi_unref_threadsafe_function(env, _tsfn); NAPI_THROW_IF_FAILED_VOID(env, status); } } template inline napi_status TypedThreadSafeFunction::Acquire() const { return napi_acquire_threadsafe_function(_tsfn); } template inline napi_status TypedThreadSafeFunction::Release() { return napi_release_threadsafe_function(_tsfn, napi_tsfn_release); } template inline napi_status TypedThreadSafeFunction::Abort() { return napi_release_threadsafe_function(_tsfn, napi_tsfn_abort); } template inline ContextType* TypedThreadSafeFunction::GetContext() const { void* context; napi_status status = napi_get_threadsafe_function_context(_tsfn, &context); NAPI_FATAL_IF_FAILED(status, "TypedThreadSafeFunction::GetContext", "napi_get_threadsafe_function_context"); return static_cast(context); } // static template void TypedThreadSafeFunction::CallJsInternal( napi_env env, napi_value jsCallback, void* context, void* data) { details::CallJsWrapper( env, jsCallback, context, data); } #if NAPI_VERSION == 4 // static template Napi::Function TypedThreadSafeFunction::EmptyFunctionFactory( Napi::Env env) { return Napi::Function::New(env, [](const CallbackInfo& cb) {}); } // static template Napi::Function TypedThreadSafeFunction::FunctionOrEmpty( Napi::Env env, Napi::Function& callback) { if (callback.IsEmpty()) { return EmptyFunctionFactory(env); } return callback; } #else // static template std::nullptr_t TypedThreadSafeFunction::EmptyFunctionFactory( Napi::Env /*env*/) { return nullptr; } // static template Napi::Function TypedThreadSafeFunction::FunctionOrEmpty( Napi::Env /*env*/, Napi::Function& callback) { return callback; } #endif //////////////////////////////////////////////////////////////////////////////// // ThreadSafeFunction class //////////////////////////////////////////////////////////////////////////////// // static template inline ThreadSafeFunction ThreadSafeFunction::New(napi_env env, const Function& callback, ResourceString resourceName, size_t maxQueueSize, size_t initialThreadCount) { return New(env, callback, Object(), resourceName, maxQueueSize, initialThreadCount); } // static template inline ThreadSafeFunction ThreadSafeFunction::New(napi_env env, const Function& callback, ResourceString resourceName, size_t maxQueueSize, size_t initialThreadCount, ContextType* context) { return New(env, callback, Object(), resourceName, maxQueueSize, initialThreadCount, context); } // static template inline ThreadSafeFunction ThreadSafeFunction::New(napi_env env, const Function& callback, ResourceString resourceName, size_t maxQueueSize, size_t initialThreadCount, Finalizer finalizeCallback) { return New(env, callback, Object(), resourceName, maxQueueSize, initialThreadCount, finalizeCallback); } // static template inline ThreadSafeFunction ThreadSafeFunction::New(napi_env env, const Function& callback, ResourceString resourceName, size_t maxQueueSize, size_t initialThreadCount, Finalizer finalizeCallback, FinalizerDataType* data) { return New(env, callback, Object(), resourceName, maxQueueSize, initialThreadCount, finalizeCallback, data); } // static template inline ThreadSafeFunction ThreadSafeFunction::New(napi_env env, const Function& callback, ResourceString resourceName, size_t maxQueueSize, size_t initialThreadCount, ContextType* context, Finalizer finalizeCallback) { return New(env, callback, Object(), resourceName, maxQueueSize, initialThreadCount, context, finalizeCallback); } // static template inline ThreadSafeFunction ThreadSafeFunction::New(napi_env env, const Function& callback, ResourceString resourceName, size_t maxQueueSize, size_t initialThreadCount, ContextType* context, Finalizer finalizeCallback, FinalizerDataType* data) { return New(env, callback, Object(), resourceName, maxQueueSize, initialThreadCount, context, finalizeCallback, data); } // static template inline ThreadSafeFunction ThreadSafeFunction::New(napi_env env, const Function& callback, const Object& resource, ResourceString resourceName, size_t maxQueueSize, size_t initialThreadCount) { return New(env, callback, resource, resourceName, maxQueueSize, initialThreadCount, static_cast(nullptr) /* context */); } // static template inline ThreadSafeFunction ThreadSafeFunction::New(napi_env env, const Function& callback, const Object& resource, ResourceString resourceName, size_t maxQueueSize, size_t initialThreadCount, ContextType* context) { return New(env, callback, resource, resourceName, maxQueueSize, initialThreadCount, context, [](Env, ContextType*) {} /* empty finalizer */); } // static template inline ThreadSafeFunction ThreadSafeFunction::New(napi_env env, const Function& callback, const Object& resource, ResourceString resourceName, size_t maxQueueSize, size_t initialThreadCount, Finalizer finalizeCallback) { return New(env, callback, resource, resourceName, maxQueueSize, initialThreadCount, static_cast(nullptr) /* context */, finalizeCallback, static_cast(nullptr) /* data */, details::ThreadSafeFinalize::Wrapper); } // static template inline ThreadSafeFunction ThreadSafeFunction::New(napi_env env, const Function& callback, const Object& resource, ResourceString resourceName, size_t maxQueueSize, size_t initialThreadCount, Finalizer finalizeCallback, FinalizerDataType* data) { return New(env, callback, resource, resourceName, maxQueueSize, initialThreadCount, static_cast(nullptr) /* context */, finalizeCallback, data, details::ThreadSafeFinalize< void, Finalizer, FinalizerDataType>::FinalizeWrapperWithData); } // static template inline ThreadSafeFunction ThreadSafeFunction::New(napi_env env, const Function& callback, const Object& resource, ResourceString resourceName, size_t maxQueueSize, size_t initialThreadCount, ContextType* context, Finalizer finalizeCallback) { return New(env, callback, resource, resourceName, maxQueueSize, initialThreadCount, context, finalizeCallback, static_cast(nullptr) /* data */, details::ThreadSafeFinalize< ContextType, Finalizer>::FinalizeWrapperWithContext); } // static template inline ThreadSafeFunction ThreadSafeFunction::New(napi_env env, const Function& callback, const Object& resource, ResourceString resourceName, size_t maxQueueSize, size_t initialThreadCount, ContextType* context, Finalizer finalizeCallback, FinalizerDataType* data) { return New(env, callback, resource, resourceName, maxQueueSize, initialThreadCount, context, finalizeCallback, data, details::ThreadSafeFinalize::FinalizeFinalizeWrapperWithDataAndContext); } inline ThreadSafeFunction::ThreadSafeFunction() : _tsfn() { } inline ThreadSafeFunction::ThreadSafeFunction( napi_threadsafe_function tsfn) : _tsfn(tsfn) { } inline ThreadSafeFunction::operator napi_threadsafe_function() const { return _tsfn; } inline napi_status ThreadSafeFunction::BlockingCall() const { return CallInternal(nullptr, napi_tsfn_blocking); } template <> inline napi_status ThreadSafeFunction::BlockingCall( void* data) const { return napi_call_threadsafe_function(_tsfn, data, napi_tsfn_blocking); } template inline napi_status ThreadSafeFunction::BlockingCall( Callback callback) const { return CallInternal(new CallbackWrapper(callback), napi_tsfn_blocking); } template inline napi_status ThreadSafeFunction::BlockingCall( DataType* data, Callback callback) const { auto wrapper = [data, callback](Env env, Function jsCallback) { callback(env, jsCallback, data); }; return CallInternal(new CallbackWrapper(wrapper), napi_tsfn_blocking); } inline napi_status ThreadSafeFunction::NonBlockingCall() const { return CallInternal(nullptr, napi_tsfn_nonblocking); } template <> inline napi_status ThreadSafeFunction::NonBlockingCall( void* data) const { return napi_call_threadsafe_function(_tsfn, data, napi_tsfn_nonblocking); } template inline napi_status ThreadSafeFunction::NonBlockingCall( Callback callback) const { return CallInternal(new CallbackWrapper(callback), napi_tsfn_nonblocking); } template inline napi_status ThreadSafeFunction::NonBlockingCall( DataType* data, Callback callback) const { auto wrapper = [data, callback](Env env, Function jsCallback) { callback(env, jsCallback, data); }; return CallInternal(new CallbackWrapper(wrapper), napi_tsfn_nonblocking); } inline void ThreadSafeFunction::Ref(napi_env env) const { if (_tsfn != nullptr) { napi_status status = napi_ref_threadsafe_function(env, _tsfn); NAPI_THROW_IF_FAILED_VOID(env, status); } } inline void ThreadSafeFunction::Unref(napi_env env) const { if (_tsfn != nullptr) { napi_status status = napi_unref_threadsafe_function(env, _tsfn); NAPI_THROW_IF_FAILED_VOID(env, status); } } inline napi_status ThreadSafeFunction::Acquire() const { return napi_acquire_threadsafe_function(_tsfn); } inline napi_status ThreadSafeFunction::Release() { return napi_release_threadsafe_function(_tsfn, napi_tsfn_release); } inline napi_status ThreadSafeFunction::Abort() { return napi_release_threadsafe_function(_tsfn, napi_tsfn_abort); } inline ThreadSafeFunction::ConvertibleContext ThreadSafeFunction::GetContext() const { void* context; napi_status status = napi_get_threadsafe_function_context(_tsfn, &context); NAPI_FATAL_IF_FAILED(status, "ThreadSafeFunction::GetContext", "napi_get_threadsafe_function_context"); return ConvertibleContext({ context }); } // static template inline ThreadSafeFunction ThreadSafeFunction::New(napi_env env, const Function& callback, const Object& resource, ResourceString resourceName, size_t maxQueueSize, size_t initialThreadCount, ContextType* context, Finalizer finalizeCallback, FinalizerDataType* data, napi_finalize wrapper) { static_assert(details::can_make_string::value || std::is_convertible::value, "Resource name should be convertible to the string type"); ThreadSafeFunction tsfn; auto* finalizeData = new details::ThreadSafeFinalize({ data, finalizeCallback }); napi_status status = napi_create_threadsafe_function(env, callback, resource, Value::From(env, resourceName), maxQueueSize, initialThreadCount, finalizeData, wrapper, context, CallJS, &tsfn._tsfn); if (status != napi_ok) { delete finalizeData; NAPI_THROW_IF_FAILED(env, status, ThreadSafeFunction()); } return tsfn; } inline napi_status ThreadSafeFunction::CallInternal( CallbackWrapper* callbackWrapper, napi_threadsafe_function_call_mode mode) const { napi_status status = napi_call_threadsafe_function( _tsfn, callbackWrapper, mode); if (status != napi_ok && callbackWrapper != nullptr) { delete callbackWrapper; } return status; } // static inline void ThreadSafeFunction::CallJS(napi_env env, napi_value jsCallback, void* /* context */, void* data) { if (env == nullptr && jsCallback == nullptr) { return; } if (data != nullptr) { auto* callbackWrapper = static_cast(data); (*callbackWrapper)(env, Function(env, jsCallback)); delete callbackWrapper; } else if (jsCallback != nullptr) { Function(env, jsCallback).Call({}); } } //////////////////////////////////////////////////////////////////////////////// // Async Progress Worker Base class //////////////////////////////////////////////////////////////////////////////// template inline AsyncProgressWorkerBase::AsyncProgressWorkerBase(const Object& receiver, const Function& callback, const char* resource_name, const Object& resource, size_t queue_size) : AsyncWorker(receiver, callback, resource_name, resource) { // Fill all possible arguments to work around ambiguous ThreadSafeFunction::New signatures. _tsfn = ThreadSafeFunction::New(callback.Env(), callback, resource, resource_name, queue_size, /** initialThreadCount */ 1, /** context */ this, OnThreadSafeFunctionFinalize, /** finalizeData */ this); } #if NAPI_VERSION > 4 template inline AsyncProgressWorkerBase::AsyncProgressWorkerBase(Napi::Env env, const char* resource_name, const Object& resource, size_t queue_size) : AsyncWorker(env, resource_name, resource) { // TODO: Once the changes to make the callback optional for threadsafe // functions are available on all versions we can remove the dummy Function here. Function callback; // Fill all possible arguments to work around ambiguous ThreadSafeFunction::New signatures. _tsfn = ThreadSafeFunction::New(env, callback, resource, resource_name, queue_size, /** initialThreadCount */ 1, /** context */ this, OnThreadSafeFunctionFinalize, /** finalizeData */ this); } #endif template inline AsyncProgressWorkerBase::~AsyncProgressWorkerBase() { // Abort pending tsfn call. // Don't send progress events after we've already completed. // It's ok to call ThreadSafeFunction::Abort and ThreadSafeFunction::Release duplicated. _tsfn.Abort(); } template inline void AsyncProgressWorkerBase::OnAsyncWorkProgress(Napi::Env /* env */, Napi::Function /* jsCallback */, void* data) { ThreadSafeData* tsd = static_cast(data); tsd->asyncprogressworker()->OnWorkProgress(tsd->data()); delete tsd; } template inline napi_status AsyncProgressWorkerBase::NonBlockingCall(DataType* data) { auto tsd = new AsyncProgressWorkerBase::ThreadSafeData(this, data); return _tsfn.NonBlockingCall(tsd, OnAsyncWorkProgress); } template inline void AsyncProgressWorkerBase::OnWorkComplete(Napi::Env /* env */, napi_status status) { _work_completed = true; _complete_status = status; _tsfn.Release(); } template inline void AsyncProgressWorkerBase::OnThreadSafeFunctionFinalize(Napi::Env env, void* /* data */, AsyncProgressWorkerBase* context) { if (context->_work_completed) { context->AsyncWorker::OnWorkComplete(env, context->_complete_status); } } //////////////////////////////////////////////////////////////////////////////// // Async Progress Worker class //////////////////////////////////////////////////////////////////////////////// template inline AsyncProgressWorker::AsyncProgressWorker(const Function& callback) : AsyncProgressWorker(callback, "generic") { } template inline AsyncProgressWorker::AsyncProgressWorker(const Function& callback, const char* resource_name) : AsyncProgressWorker(callback, resource_name, Object::New(callback.Env())) { } template inline AsyncProgressWorker::AsyncProgressWorker(const Function& callback, const char* resource_name, const Object& resource) : AsyncProgressWorker(Object::New(callback.Env()), callback, resource_name, resource) { } template inline AsyncProgressWorker::AsyncProgressWorker(const Object& receiver, const Function& callback) : AsyncProgressWorker(receiver, callback, "generic") { } template inline AsyncProgressWorker::AsyncProgressWorker(const Object& receiver, const Function& callback, const char* resource_name) : AsyncProgressWorker(receiver, callback, resource_name, Object::New(callback.Env())) { } template inline AsyncProgressWorker::AsyncProgressWorker(const Object& receiver, const Function& callback, const char* resource_name, const Object& resource) : AsyncProgressWorkerBase(receiver, callback, resource_name, resource), _asyncdata(nullptr), _asyncsize(0) { } #if NAPI_VERSION > 4 template inline AsyncProgressWorker::AsyncProgressWorker(Napi::Env env) : AsyncProgressWorker(env, "generic") { } template inline AsyncProgressWorker::AsyncProgressWorker(Napi::Env env, const char* resource_name) : AsyncProgressWorker(env, resource_name, Object::New(env)) { } template inline AsyncProgressWorker::AsyncProgressWorker(Napi::Env env, const char* resource_name, const Object& resource) : AsyncProgressWorkerBase(env, resource_name, resource), _asyncdata(nullptr), _asyncsize(0) { } #endif template inline AsyncProgressWorker::~AsyncProgressWorker() { { std::lock_guard lock(this->_mutex); _asyncdata = nullptr; _asyncsize = 0; } } template inline void AsyncProgressWorker::Execute() { ExecutionProgress progress(this); Execute(progress); } template inline void AsyncProgressWorker::OnWorkProgress(void*) { T* data; size_t size; { std::lock_guard lock(this->_mutex); data = this->_asyncdata; size = this->_asyncsize; this->_asyncdata = nullptr; this->_asyncsize = 0; } /** * The callback of ThreadSafeFunction is not been invoked immediately on the * callback of uv_async_t (uv io poll), rather the callback of TSFN is * invoked on the right next uv idle callback. There are chances that during * the deferring the signal of uv_async_t is been sent again, i.e. potential * not coalesced two calls of the TSFN callback. */ if (data == nullptr) { return; } this->OnProgress(data, size); delete[] data; } template inline void AsyncProgressWorker::SendProgress_(const T* data, size_t count) { T* new_data = new T[count]; std::copy(data, data + count, new_data); T* old_data; { std::lock_guard lock(this->_mutex); old_data = _asyncdata; _asyncdata = new_data; _asyncsize = count; } this->NonBlockingCall(nullptr); delete[] old_data; } template inline void AsyncProgressWorker::Signal() const { this->NonBlockingCall(static_cast(nullptr)); } template inline void AsyncProgressWorker::ExecutionProgress::Signal() const { _worker->Signal(); } template inline void AsyncProgressWorker::ExecutionProgress::Send(const T* data, size_t count) const { _worker->SendProgress_(data, count); } //////////////////////////////////////////////////////////////////////////////// // Async Progress Queue Worker class //////////////////////////////////////////////////////////////////////////////// template inline AsyncProgressQueueWorker::AsyncProgressQueueWorker(const Function& callback) : AsyncProgressQueueWorker(callback, "generic") { } template inline AsyncProgressQueueWorker::AsyncProgressQueueWorker(const Function& callback, const char* resource_name) : AsyncProgressQueueWorker(callback, resource_name, Object::New(callback.Env())) { } template inline AsyncProgressQueueWorker::AsyncProgressQueueWorker(const Function& callback, const char* resource_name, const Object& resource) : AsyncProgressQueueWorker(Object::New(callback.Env()), callback, resource_name, resource) { } template inline AsyncProgressQueueWorker::AsyncProgressQueueWorker(const Object& receiver, const Function& callback) : AsyncProgressQueueWorker(receiver, callback, "generic") { } template inline AsyncProgressQueueWorker::AsyncProgressQueueWorker(const Object& receiver, const Function& callback, const char* resource_name) : AsyncProgressQueueWorker(receiver, callback, resource_name, Object::New(callback.Env())) { } template inline AsyncProgressQueueWorker::AsyncProgressQueueWorker(const Object& receiver, const Function& callback, const char* resource_name, const Object& resource) : AsyncProgressWorkerBase>(receiver, callback, resource_name, resource, /** unlimited queue size */0) { } #if NAPI_VERSION > 4 template inline AsyncProgressQueueWorker::AsyncProgressQueueWorker(Napi::Env env) : AsyncProgressQueueWorker(env, "generic") { } template inline AsyncProgressQueueWorker::AsyncProgressQueueWorker(Napi::Env env, const char* resource_name) : AsyncProgressQueueWorker(env, resource_name, Object::New(env)) { } template inline AsyncProgressQueueWorker::AsyncProgressQueueWorker(Napi::Env env, const char* resource_name, const Object& resource) : AsyncProgressWorkerBase>(env, resource_name, resource, /** unlimited queue size */0) { } #endif template inline void AsyncProgressQueueWorker::Execute() { ExecutionProgress progress(this); Execute(progress); } template inline void AsyncProgressQueueWorker::OnWorkProgress(std::pair* datapair) { if (datapair == nullptr) { return; } T *data = datapair->first; size_t size = datapair->second; this->OnProgress(data, size); delete datapair; delete[] data; } template inline void AsyncProgressQueueWorker::SendProgress_(const T* data, size_t count) { T* new_data = new T[count]; std::copy(data, data + count, new_data); auto pair = new std::pair(new_data, count); this->NonBlockingCall(pair); } template inline void AsyncProgressQueueWorker::Signal() const { this->NonBlockingCall(nullptr); } template inline void AsyncProgressQueueWorker::OnWorkComplete(Napi::Env env, napi_status status) { // Draining queued items in TSFN. AsyncProgressWorkerBase>::OnWorkComplete(env, status); } template inline void AsyncProgressQueueWorker::ExecutionProgress::Signal() const { _worker->Signal(); } template inline void AsyncProgressQueueWorker::ExecutionProgress::Send(const T* data, size_t count) const { _worker->SendProgress_(data, count); } #endif // NAPI_VERSION > 3 && !defined(__wasm32__) //////////////////////////////////////////////////////////////////////////////// // Memory Management class //////////////////////////////////////////////////////////////////////////////// inline int64_t MemoryManagement::AdjustExternalMemory(Env env, int64_t change_in_bytes) { int64_t result; napi_status status = napi_adjust_external_memory(env, change_in_bytes, &result); NAPI_THROW_IF_FAILED(env, status, 0); return result; } //////////////////////////////////////////////////////////////////////////////// // Version Management class //////////////////////////////////////////////////////////////////////////////// inline uint32_t VersionManagement::GetNapiVersion(Env env) { uint32_t result; napi_status status = napi_get_version(env, &result); NAPI_THROW_IF_FAILED(env, status, 0); return result; } inline const napi_node_version* VersionManagement::GetNodeVersion(Env env) { const napi_node_version* result; napi_status status = napi_get_node_version(env, &result); NAPI_THROW_IF_FAILED(env, status, 0); return result; } #if NAPI_VERSION > 5 //////////////////////////////////////////////////////////////////////////////// // Addon class //////////////////////////////////////////////////////////////////////////////// template inline Object Addon::Init(Env env, Object exports) { T* addon = new T(env, exports); env.SetInstanceData(addon); return addon->entry_point_; } template inline T* Addon::Unwrap(Object wrapper) { return wrapper.Env().GetInstanceData(); } template inline void Addon::DefineAddon(Object exports, const std::initializer_list& props) { DefineProperties(exports, props); entry_point_ = exports; } template inline Napi::Object Addon::DefineProperties(Object object, const std::initializer_list& props) { const napi_property_descriptor* properties = reinterpret_cast(props.begin()); size_t size = props.size(); napi_status status = napi_define_properties(object.Env(), object, size, properties); NAPI_THROW_IF_FAILED(object.Env(), status, object); for (size_t idx = 0; idx < size; idx++) T::AttachPropData(object.Env(), object, &properties[idx]); return object; } #endif // NAPI_VERSION > 5 } // namespace Napi // hack-sqlite #endif // SRC_NAPI_INL_H_ #endif // SRC_NAPI_H_ /* file none */ /*jslint-enable*/ /* file sqlmath-old.js */ /*jslint beta, name, node, this*/ let Backup; let Database; let EventEmitter = require("events").EventEmitter; let Statement; let isVerbose = false; let sqlite3 = require( "./.binary-sqlmath-node" + "-" + "napi" + process.versions.napi + "-" + process.platform + "-" + process.arch + ".node" ); let supportedEvents = [ "trace", "profile", "insert", "update", "delete" ]; function databaseAddListener(type, ...argList) { let val = EventEmitter.prototype.addListener.call(this, type, ...argList); if (supportedEvents.indexOf(type) >= 0) { this.configure(type, true); } return val; } function databaseAll(statement, params) { // Database#all(sql, [bind1, bind2, ...], [callback]) statement.all.apply(statement, params).finalize(); return this; } function databaseBackup(...argList) { // Database#backup(filename, [callback]) // Database#backup(filename, destName, sourceName, filenameIsDest, [callback]) let backup; if (argList.length <= 2) { // By default, we write the main database out to the main database of the named // file. // This is the most likely use of the backup api. backup = new Backup(this, argList[0], "main", "main", true, argList[1]); } else { // Otherwise, give the user full control over the sqlite3_backup_init arguments. backup = new Backup(this, ...argList); } // Per the sqlite docs, exclude the following errors as non-fatal by default. backup.retryErrors = [ sqlite3.BUSY, sqlite3.LOCKED ]; return backup; } function databaseEach(statement, params) { // Database#each(sql, [bind1, bind2, ...], [callback], [complete]) statement.each.apply(statement, params).finalize(); return this; } function databaseGet(statement, params) { // Database#get(sql, [bind1, bind2, ...], [callback]) statement.get.apply(statement, params).finalize(); return this; } function databaseMap(statement, params) { statement.map.apply(statement, params).finalize(); return this; } function databasePrepare(statement, params) { // Database#prepare(sql, [bind1, bind2, ...], [callback]) return ( params.length ? statement.bind.apply(statement, params) : statement ); } function databaseRemoveAllListeners(type, ...argList) { let val = EventEmitter.prototype.removeAllListeners.call( this, type, ...argList ); if (supportedEvents.indexOf(type) >= 0) { this.configure(type, false); } return val; } function databaseRemoveListener(type, ...argList) { let val = EventEmitter.prototype.removeListener.call( this, type, ...argList ); if (supportedEvents.indexOf(type) >= 0 && !this._events[type]) { this.configure(type, false); } return val; } function databaseRun(statement, params) { // Database#run(sql, [bind1, bind2, ...], [callback]) statement.run.apply(statement, params).finalize(); return this; } function normalizeMethod(fnc) { return function (sql, ...argList) { let onError; if (typeof argList[argList.length - 1] === "function") { onError = function (err) { if (err) { argList[argList.length - 1](err); } }; } return fnc.call(this, new Statement(this, sql, onError), argList); }; } function sqliteCachedDatabase(file, aa, bb) { let callback; let db; if (file === "" || file === ":memory:") { // Don't cache special databases. return new Database(file, aa, bb); } file = require("path").resolve(file); db = sqlite3.cached.objects[file]; if (!db) { db = new Database(file, aa, bb); sqlite3.cached.objects[file] = db; return db; } // Make sure the callback is called. callback = ( (typeof aa === "function") ? aa : bb ); if (typeof callback === "function" && db.open) { process.nextTick(callback.bind(db)); return db; } if (typeof callback === "function") { db.once("open", callback.bind(db)); return db; } return db; } function sqliteVerbose() { if (!isVerbose) { [ "prepare", "get", "run", "all", "each", "map", "close", "exec" ].forEach(function (name) { sqliteVerboseTrace(Database.prototype, name); }); [ "bind", "get", "run", "all", "each", "map", "reset", "finalize" ].forEach(function (name) { sqliteVerboseTrace(Statement.prototype, name); }); isVerbose = true; } return this; } function sqliteVerboseTrace(object, property, pos) { // Save the stack trace over EIO callbacks. // Inspired by https://github.com/tlrobinson/long-stack-traces let old = object[property]; object[property] = function (...argList) { let cb; let error = new Error(); let name = ( object.constructor.name + "#" + property + "(" + argList.map(function (elem) { return require("util").inspect(elem, false, 0); }).join(", ") + ")" ); if (pos === undefined) { pos = -1; } if (pos < 0) { pos += argList.length; } cb = argList[pos]; if (typeof argList[pos] === "function") { argList[pos] = function replacement(err, ...argList) { if (err && err.stack && !err.__augmented) { err.stack = err.stack.split("\n").filter(function (line) { return line.indexOf(__filename) < 0; }).join("\n"); err.stack += "\n--> in " + name; err.stack += "\n" + error.stack.split( "\n" ).filter(function (line) { return line.indexOf(__filename) < 0; }).slice(1).join("\n"); err.__augmented = true; } return cb.call(this, err, ...argList); }; } return old.apply(this, argList); }; } function statementMap(...params) { let callback = params.pop(); params.push(function (err, rowList) { let ii; let key; let keyList; let result; let val; if (err) { return callback(err); } result = {}; if (rowList.length) { keyList = Object.keys(rowList[0]); key = keyList[0]; if (keyList.length > 2) { // val is an object. ii = 0; while (ii < rowList.length) { result[rowList[ii][key]] = rowList[ii]; ii += 1; } } else { val = keyList[1]; // val is a plain value. ii = 0; while (ii < rowList.length) { result[rowList[ii][key]] = rowList[ii][val]; ii += 1; } } } callback(err, result); }); return this.all.apply(this, params); } Backup = sqlite3.Backup; Database = sqlite3.Database; Statement = sqlite3.Statement; Object.assign(Backup.prototype, EventEmitter.prototype); Object.assign(Database.prototype, EventEmitter.prototype, { addListener: databaseAddListener, all: normalizeMethod(databaseAll), backup: databaseBackup, each: normalizeMethod(databaseEach), get: normalizeMethod(databaseGet), map: normalizeMethod(databaseMap), on: databaseAddListener, prepare: normalizeMethod(databasePrepare), removeAllListeners: databaseRemoveAllListeners, removeListener: databaseRemoveListener, run: normalizeMethod(databaseRun) }); Object.assign(Statement.prototype, EventEmitter.prototype, { map: statementMap }); Object.assign(sqlite3, { cached: { Database: sqliteCachedDatabase, objects: {} }, verbose: sqliteVerbose }); module.exports = sqlite3; /* file sqlmath-sqljs.api.js */ /*jslint bitwise, for, this, unordered*/ /*global ALLOC_NORMAL FS HEAP8 Module _malloc _free addFunction allocate allocateUTF8OnStack getValue intArrayFromString removeFunction setValue stackAlloc stackRestore stackSave UTF8ToString stringToUTF8 lengthBytesUTF8 */ "use strict"; function assertOrThrow(passed, err) { if (!passed) { throw err; } } /** * @typedef {{Database:Database, Statement:Statement}} SqlJs * @property {Database} Database A class that represents an SQLite database * @property {Statement} Statement The prepared statement class */ /** * @typedef {{locateFile:function(string):string}} SqlJsConfig * @property {function(string):string} locateFile * a function that returns the full path to a resource given its file name * @see https://emscripten.org/docs/api_reference/module.html */ /** * Asynchronously initializes sql.js * @function initSqlJs * @param {SqlJsConfig} config module inititialization parameters * @returns {SqlJs} * @example * initSqlJs({ * locateFile: name => '/path/to/assets/' + name * }).then(SQL => { * const db = new SQL.Database(); * const result = db.exec("select 'hello world'"); * console.log(result); * }) */ /** * @module SqlJs */ // Wait for preRun to run, and then finish our initialization Module.onRuntimeInitialized = function onRuntimeInitialized() { // Declare toplevel variables // register, used for temporary stack values var apiTemp = stackAlloc(4); var cwrap = Module.cwrap; // Null pointer var NULL = 0; // SQLite enum var SQLITE_OK = 0; var SQLITE_ROW = 100; var SQLITE_DONE = 101; var SQLITE_INTEGER = 1; var SQLITE_FLOAT = 2; var SQLITE_TEXT = 3; var SQLITE_BLOB = 4; // var - Encodings, used for registering functions. var SQLITE_UTF8 = 1; // var - cwrap function var sqlite3_open = cwrap("sqlite3_open", "number", ["string", "number"]); var sqlite3_close_v2 = cwrap("sqlite3_close_v2", "number", ["number"]); var sqlite3_exec = cwrap( "sqlite3_exec", "number", ["number", "string", "number", "number", "number"] ); var sqlite3_changes = cwrap("sqlite3_changes", "number", ["number"]); var sqlite3_prepare_v2 = cwrap( "sqlite3_prepare_v2", "number", ["number", "string", "number", "number", "number"] ); var sqlite3_sql = cwrap("sqlite3_sql", "string", ["number"]); var sqlite3_normalized_sql = cwrap( "sqlite3_normalized_sql", "string", ["number"] ); var sqlite3_prepare_v2_sqlptr = cwrap( "sqlite3_prepare_v2", "number", ["number", "number", "number", "number", "number"] ); var sqlite3_bind_text = cwrap( "sqlite3_bind_text", "number", ["number", "number", "number", "number", "number"] ); var sqlite3_bind_blob = cwrap( "sqlite3_bind_blob", "number", ["number", "number", "number", "number", "number"] ); var sqlite3_bind_double = cwrap( "sqlite3_bind_double", "number", ["number", "number", "number"] ); var sqlite3_bind_int = cwrap( "sqlite3_bind_int", "number", ["number", "number", "number"] ); var sqlite3_bind_parameter_index = cwrap( "sqlite3_bind_parameter_index", "number", ["number", "string"] ); var sqlite3_step = cwrap("sqlite3_step", "number", ["number"]); var sqlite3_errmsg = cwrap("sqlite3_errmsg", "string", ["number"]); var sqlite3_column_count = cwrap( "sqlite3_column_count", "number", ["number"] ); var sqlite3_data_count = cwrap("sqlite3_data_count", "number", ["number"]); var sqlite3_column_double = cwrap( "sqlite3_column_double", "number", ["number", "number"] ); var sqlite3_column_text = cwrap( "sqlite3_column_text", "string", ["number", "number"] ); var sqlite3_column_blob = cwrap( "sqlite3_column_blob", "number", ["number", "number"] ); var sqlite3_column_bytes = cwrap( "sqlite3_column_bytes", "number", ["number", "number"] ); var sqlite3_column_type = cwrap( "sqlite3_column_type", "number", ["number", "number"] ); var sqlite3_column_name = cwrap( "sqlite3_column_name", "string", ["number", "number"] ); var sqlite3_reset = cwrap("sqlite3_reset", "number", ["number"]); var sqlite3_clear_bindings = cwrap( "sqlite3_clear_bindings", "number", ["number"] ); var sqlite3_finalize = cwrap("sqlite3_finalize", "number", ["number"]); var sqlite3_create_function_v2 = cwrap( "sqlite3_create_function_v2", "number", [ "number", "string", "number", "number", "number", "number", "number", "number", "number" ] ); var sqlite3_value_type = cwrap("sqlite3_value_type", "number", ["number"]); var sqlite3_value_bytes = cwrap( "sqlite3_value_bytes", "number", ["number"] ); var sqlite3_value_text = cwrap("sqlite3_value_text", "string", ["number"]); var sqlite3_value_blob = cwrap("sqlite3_value_blob", "number", ["number"]); var sqlite3_value_double = cwrap( "sqlite3_value_double", "number", ["number"] ); var sqlite3_result_double = cwrap( "sqlite3_result_double", "", ["number", "number"] ); var sqlite3_result_null = cwrap( "sqlite3_result_null", "", ["number"] ); var sqlite3_result_text = cwrap( "sqlite3_result_text", "", ["number", "string", "number", "number"] ); var sqlite3_result_blob = cwrap( "sqlite3_result_blob", "", ["number", "number", "number", "number"] ); var sqlite3_result_int = cwrap( "sqlite3_result_int", "", ["number", "number"] ); var sqlite3_result_error = cwrap( "sqlite3_result_error", "", ["number", "string", "number"] ); var registerExtensionFunctions = cwrap( "RegisterExtensionFunctions", "number", ["number"] ); /** * @classdesc * Represents a prepared statement. * Prepared statements allow you to have a template sql string, * that you can execute multiple times with different parameters. * * You can't instantiate this class directly, you have to use a * {@link Database} object in order to create a statement. * * **Warnings** * 1. When you close a database (using db.close()), all * its statements are closed too and become unusable. * 1. After calling db.prepare() you must manually free the assigned memory * by calling Statement.free(). Failure to do this will cause subsequent * 'DROP TABLE ...' statements to fail with 'Uncaught Error: database table * is locked'. * * Statements can't be created by the API user directly, only by * Database::prepare * * @see Database.html#prepare-dynamic * @see https://en.wikipedia.org/wiki/Prepared_statement * * @constructs Statement * @memberof module:SqlJs * @param {number} stmt1 The SQLite statement reference * @param {Database} db The database from which this statement was created */ function Statement(stmt1, db) { this.stmt = stmt1; this.db = db; // Index of the leftmost parameter is 1 this.pos = 1; // Pointers to allocated memory, that need to be freed // when the statemend is destroyed this.allocatedmem = []; } /** @typedef {string|number|null|Uint8Array} Database.SqlValue */ /** @typedef { Database.SqlValue[]|Object|null } Statement.BindParams */ /** Bind values to the parameters, after having reseted the statement. * If values is null, do nothing and return true. * * SQL statements can have parameters, * named *'?', '?NNN', ':VVV', '@VVV', '$VVV'*, * where NNN is a number and VVV a string. * This function binds these parameters to the given values. * * *Warning*: ':', '@', and '$' are included in the parameters names * * ## Value types * Javascript type | SQLite type * -----------------| ----------- * number | REAL, INTEGER * boolean | INTEGER * string | TEXT * Array, Uint8Array| BLOB * null | NULL * * @example Bind values to named parameters * var stmt = db.prepare( * "UPDATE test SET a=@newval WHERE id BETWEEN $mini AND $maxi" * ); * stmt.bind({$mini:10, $maxi:20, '@newval':5}); * * @example Bind values to anonymous parameters * // Create a statement that contains parameters like '?', '?NNN' * var stmt = db.prepare("UPDATE test SET a=? WHERE id BETWEEN ? AND ?"); * // Call Statement.bind with an array as parameter * stmt.bind([5, 10, 20]); * * @see http://www.sqlite.org/datatype3.html * @see http://www.sqlite.org/lang_expr.html#varparam * @param {Statement.BindParams} values The values to bind * @return {boolean} true if it worked * @throws {String} SQLite Error */ Statement.prototype.bind = function bind(values) { if (!this.stmt) { throw "Statement closed"; } this.reset(); if (Array.isArray(values)) { return this.bindFromArray(values); } if ( (values !== null && values !== undefined) && typeof values === "object" ) { return this.bindFromObject(values); } return true; }; /** Execute the statement, fetching the the next line of result, that can be retrieved with {@link Statement.get}. @return {boolean} true if a row of result available @throws {String} SQLite Error */ Statement.prototype.step = function step() { var ret; if (!this.stmt) { throw "Statement closed"; } this.pos = 1; ret = sqlite3_step(this.stmt); switch (ret) { case SQLITE_ROW: return true; case SQLITE_DONE: return false; default: throw this.db.handleError(ret); } }; /* Internal methods to retrieve data from the results of a statement that has been executed */ Statement.prototype.getNumber = function getNumber(pos) { if (pos === null || pos === undefined) { pos = this.pos; this.pos += 1; } return sqlite3_column_double(this.stmt, pos); }; Statement.prototype.getBigInt = function getBigInt(pos) { var text; if (pos === null || pos === undefined) { pos = this.pos; this.pos += 1; } text = sqlite3_column_text(this.stmt, pos); if (typeof BigInt !== "function") { throw new Error("BigInt is not supported"); } /* global BigInt */ return BigInt(text); //jslint-quiet }; Statement.prototype.getString = function getString(pos) { if (pos === null || pos === undefined) { pos = this.pos; this.pos += 1; } return sqlite3_column_text(this.stmt, pos); }; Statement.prototype.getBlob = function getBlob(pos) { var ii; var ptr; var result; var size; if (pos === null || pos === undefined) { pos = this.pos; this.pos += 1; } size = sqlite3_column_bytes(this.stmt, pos); ptr = sqlite3_column_blob(this.stmt, pos); result = new Uint8Array(size); for (ii = 0; ii < size; ii += 1) { result[ii] = HEAP8[ptr + ii]; } return result; }; /** Get one row of results of a statement. If the first parameter is not provided, step must have been called before. @param {Statement.BindParams} [params] If set, the values will be bound to the statement before it is executed @return {Database.SqlValue[]} One row of result @example Print all the rows of the table test to the console var stmt = db.prepare("SELECT * FROM test"); while (stmt.step()) console.log(stmt.get()); Enable BigInt support var stmt = db.prepare("SELECT * FROM test"); while (stmt.step()) console.log(stmt.get(null, {useBigInt: true})); */ Statement.prototype.get = function get(params, config) { var results1; var ref; var field; var getfunc; config = config || {}; if ((params !== null && params !== undefined) && this.bind(params)) { this.step(); } results1 = []; ref = sqlite3_data_count(this.stmt); for (field = 0; field < ref; field += 1) { switch (sqlite3_column_type(this.stmt, field)) { case SQLITE_INTEGER: getfunc = ( config.useBigInt ? this.getBigInt(field) : this.getNumber(field) ); results1.push(getfunc); break; case SQLITE_FLOAT: results1.push(this.getNumber(field)); break; case SQLITE_TEXT: results1.push(this.getString(field)); break; case SQLITE_BLOB: results1.push(this.getBlob(field)); break; default: results1.push(null); } } return results1; }; /** Get the list of column names of a row of result of a statement. @return {string[]} The names of the columns @example var stmt = db.prepare( "SELECT 5 AS nbr, x'616200' AS data, NULL AS null_value;" ); stmt.step(); // Execute the statement console.log(stmt.getColumnNames()); // Will print ['nbr','data','null_value'] */ Statement.prototype.getColumnNames = function getColumnNames() { var results1 = []; var ref = sqlite3_column_count(this.stmt); var ii; for (ii = 0; ii < ref; ii += 1) { results1.push(sqlite3_column_name(this.stmt, ii)); } return results1; }; /** Get one row of result as a javascript object, associating column names with their value in the current row. @param {Statement.BindParams} [params] If set, the values will be bound to the statement, and it will be executed @return {Object} The row of result @see {@link Statement.get} @example var stmt = db.prepare( "SELECT 5 AS nbr, x'010203' AS data, NULL AS null_value;" ); stmt.step(); // Execute the statement console.log(stmt.getAsObject()); // Will print {nbr:5, data: Uint8Array([1,2,3]), null_value:null} */ Statement.prototype.getAsObject = function getAsObject(params, config) { var values = this.get(params, config); var names = this.getColumnNames(); var rowObject = {}; var ii; var name; for (ii = 0; ii < names.length; ii += 1) { name = names[ii]; rowObject[name] = values[ii]; } return rowObject; }; /** Get the SQL string used in preparing this statement. @return {string} The SQL string */ Statement.prototype.getSQL = function getSQL() { return sqlite3_sql(this.stmt); }; /** Get the SQLite's normalized version of the SQL string used in preparing this statement. The meaning of "normalized" is not well-defined: see {@link https://sqlite.org/c3ref/expanded_sql.html the SQLite documentation}. @example db.run("create table test (x integer);"); stmt = db.prepare("select * from test where x = 42"); // returns "SELECT*FROM test WHERE x=?;" @return {string} The normalized SQL string */ Statement.prototype.getNormalizedSQL = function getNormalizedSQL() { return sqlite3_normalized_sql(this.stmt); }; /** Shorthand for bind + step + reset Bind the values, execute the statement, ignoring the rows it returns, and resets it @param {Statement.BindParams} [values] Value to bind to the statement */ Statement.prototype.run = function (values) { if (values !== null && values !== undefined) { this.bind(values); } this.step(); return this.reset(); }; Statement.prototype.bindString = function bindString(string, pos) { var bytes; var strptr; if (pos === null || pos === undefined) { pos = this.pos; this.pos += 1; } bytes = intArrayFromString(string); strptr = allocate(bytes, ALLOC_NORMAL); this.allocatedmem.push(strptr); this.db.handleError(sqlite3_bind_text( this.stmt, pos, strptr, bytes.length - 1, 0 )); return true; }; Statement.prototype.bindBlob = function bindBlob(array, pos) { var blobptr; if (pos === null || pos === undefined) { pos = this.pos; this.pos += 1; } blobptr = allocate(array, ALLOC_NORMAL); this.allocatedmem.push(blobptr); this.db.handleError(sqlite3_bind_blob( this.stmt, pos, blobptr, array.length, 0 )); return true; }; Statement.prototype.bindNumber = function bindNumber(num, pos) { var bindfunc; if (pos === null || pos === undefined) { pos = this.pos; this.pos += 1; } bindfunc = ( num === (num | 0) ? sqlite3_bind_int : sqlite3_bind_double ); this.db.handleError(bindfunc(this.stmt, pos, num)); return true; }; Statement.prototype.bindNull = function bindNull(pos) { if (pos === null || pos === undefined) { pos = this.pos; this.pos += 1; } return sqlite3_bind_blob(this.stmt, pos, 0, 0, 0) === SQLITE_OK; }; Statement.prototype.bindValue = function bindValue(val, pos) { if (pos === null || pos === undefined) { pos = this.pos; this.pos += 1; } switch (typeof val) { case "string": return this.bindString(val, pos); case "number": return this.bindNumber(val + 0, pos); case "bigint": // BigInt is not fully supported yet at WASM level. return this.bindString(val.toString(), pos); case "boolean": return this.bindNumber(val + 0, pos); case "object": if (val === null) { return this.bindNull(pos); } if (val.length !== undefined) { return this.bindBlob(val, pos); } break; } throw ( "Wrong API use : tried to bind a value of an unknown type (" + val + ")." ); }; /** Bind names and values of an object to the named parameters of the statement @param {Object} valuesObj @private @nodoc */ Statement.prototype.bindFromObject = function bindFromObject(valuesObj) { var num; var that = this; Object.keys(valuesObj).forEach(function each(name) { num = sqlite3_bind_parameter_index(that.stmt, name); if (num !== 0) { that.bindValue(valuesObj[name], num); } }); return true; }; /** Bind values to numbered parameters @param {Database.SqlValue[]} values @private @nodoc */ Statement.prototype.bindFromArray = function bindFromArray(values) { var num; for (num = 0; num < values.length; num += 1) { this.bindValue(values[num], num + 1); } return true; }; /** Reset a statement, so that it's parameters can be bound to new values It also clears all previous bindings, freeing the memory used by bound parameters. */ Statement.prototype.reset = function reset() { this.freemem(); return ( sqlite3_clear_bindings(this.stmt) === SQLITE_OK && sqlite3_reset(this.stmt) === SQLITE_OK ); }; /** Free the memory allocated during parameter binding */ Statement.prototype.freemem = function freemem() { var mem; while (true) { mem = this.allocatedmem.pop(); if (mem === undefined) { break; } _free(mem); } }; /** Free the memory used by the statement @return {boolean} true in case of success */ Statement.prototype.free = function free() { var res; this.freemem(); res = sqlite3_finalize(this.stmt) === SQLITE_OK; delete this.db.statements[this.stmt]; this.stmt = NULL; return res; }; /** * @classdesc * An iterator over multiple SQL statements in a string, * preparing and returning a Statement object for the next SQL * statement on each iteration. * * You can't instantiate this class directly, you have to use a * {@link Database} object in order to create a statement iterator * * {@see Database#iterateStatements} * * @example * // loop over and execute statements in string sql * for (let statement of db.iterateStatements(sql) { * statement.step(); * // get results, etc. * // do not call statement.free() manually, each statement is freed * // before the next one is parsed * } * * // capture any bad query exceptions with feedback * // on the bad sql * let it = db.iterateStatements(sql); * try { * for (let statement of it) { * statement.step(); * } * } catch(err) { * console.log( * `The SQL string "${it.getRemainingSQL()}" ` + * `contains the following error: ${err}` * ); * } * * @implements {Iterator} * @implements {Iterable} * @constructs StatementIterator * @memberof module:SqlJs * @param {string} sql A string containing multiple SQL statements * @param {Database} db The database from which this iterator was created */ function StatementIterator(sql, db) { var sz; this.db = db; sz = lengthBytesUTF8(sql) + 1; this.sqlPtr = _malloc(sz); if (this.sqlPtr === null) { throw new Error("Unable to allocate memory for the SQL string"); } stringToUTF8(sql, this.sqlPtr, sz); this.nextSqlPtr = this.sqlPtr; this.nextSqlString = null; this.activeStatement = null; } /** * @typedef {{ done:true, value:undefined } | * { done:false, value:Statement}} * StatementIterator.StatementIteratorResult * @property {Statement} value the next available Statement * (as returned by {@link Database.prepare}) * @property {boolean} done true if there are no more available statements */ /** Prepare the next available SQL statement @return {StatementIterator.StatementIteratorResult} @throws {String} SQLite error or invalid iterator error */ StatementIterator.prototype.next = function next() { var pStmt; var pzTail; var stack; if (this.sqlPtr === null) { return {done: true}; } if (this.activeStatement !== null) { this.activeStatement.free(); this.activeStatement = null; } if (!this.db.db) { this.finalize(); throw new Error("Database closed"); } stack = stackSave(); pzTail = stackAlloc(4); setValue(apiTemp, 0, "i32"); setValue(pzTail, 0, "i32"); try { this.db.handleError(sqlite3_prepare_v2_sqlptr( this.db.db, this.nextSqlPtr, -1, apiTemp, pzTail )); this.nextSqlPtr = getValue(pzTail, "i32"); pStmt = getValue(apiTemp, "i32"); if (pStmt === NULL) { this.finalize(); return {done: true}; } this.activeStatement = new Statement(pStmt, this.db); this.db.statements[pStmt] = this.activeStatement; return {value: this.activeStatement, done: false}; } catch (err) { this.nextSqlString = UTF8ToString(this.nextSqlPtr); //jslint-quiet this.finalize(); assertOrThrow(!err, err); } finally { stackRestore(stack); } }; StatementIterator.prototype.finalize = function finalize() { _free(this.sqlPtr); this.sqlPtr = null; }; /** Get any un-executed portions remaining of the original SQL string @return {String} */ StatementIterator.prototype.getRemainingSQL = function getRemainder() { // iff an exception occurred, we set the nextSqlString if (this.nextSqlString !== null) { return this.nextSqlString; } // otherwise, convert from nextSqlPtr return UTF8ToString(this.nextSqlPtr); //jslint-quiet }; /* implement Iterable interface */ if (typeof Symbol === "function" && typeof Symbol.iterator === "symbol") { StatementIterator.prototype[Symbol.iterator] = function iterator() { return this; }; } /** @classdesc * Represents an SQLite database * @constructs Database * @memberof module:SqlJs * Open a new database either by creating a new one or opening an existing * one stored in the byte array passed in first argument * @param {number[]} data An array of bytes representing * an SQLite database file */ function Database(data) { this.filename = "dbfile_" + (0xffffffff * Math.random() >>> 0); if (data !== null && data !== undefined) { FS.createDataFile("/", this.filename, data, true, true); } this.handleError(sqlite3_open(this.filename, apiTemp)); this.db = getValue(apiTemp, "i32"); registerExtensionFunctions(this.db); // A list of all prepared statements of the database this.statements = {}; // A list of all user function of the database // (created by create_function call) this.functions = {}; } /** Execute an SQL query, ignoring the rows it returns. @param {string} sql a string containing some SQL text to execute @param {Statement.BindParams} [params] When the SQL statement contains placeholders, you can pass them in here. They will be bound to the statement before it is executed. If you use the params argument, you **cannot** provide an sql string that contains several statements (separated by `;`) @example // Insert values in a table db.run( "INSERT INTO test VALUES (:age, :name)", {':age' : 18, ':name' : 'John'} ); @return {Database} The database object (useful for method chaining) */ Database.prototype.run = function (sql, params) { var stmt; if (!this.db) { throw "Database closed"; } if (params) { stmt = this.prepare(sql, params); try { stmt.step(); } catch (err) { stmt.free(); assertOrThrow(!err, err); } stmt.free(); } else { this.handleError(sqlite3_exec(this.db, sql, 0, 0, apiTemp)); } return this; }; /** * @typedef {{ columns:string[], values:Database.SqlValue[][] }} Database.QueryExecResult * @property {string[]} columns the name of the columns of the result * (as returned by {@link Statement.getColumnNames}) * @property {Database.SqlValue[][]} values one array per row, containing * the column values */ /** Execute an SQL query, and returns the result. * * This is a wrapper against * {@link Database.prepare}, * {@link Statement.bind}, * {@link Statement.step}, * {@link Statement.get}, * and {@link Statement.free}. * * The result is an array of result elements. There are as many result * elements as the number of statements in your sql string (statements are * separated by a semicolon) * * ## Example use * We will create the following table, named *test* and query it with a * multi-line statement using params: * * | id | age | name | * |:--:|:---:|:------:| * | 1 | 1 | Ling | * | 2 | 18 | Paul | * * We query it like that: * ```javascript * var db = new SQL.Database(); * var res = db.exec( * "DROP TABLE IF EXISTS test;\n" * + "CREATE TABLE test (id INTEGER, age INTEGER, name TEXT);" * + "INSERT INTO test VALUES ($id1, :age1, @name1);" * + "INSERT INTO test VALUES ($id2, :age2, @name2);" * + "SELECT id FROM test;" * + "SELECT age,name FROM test WHERE id=$id1", * { * "$id1": 1, ":age1": 1, "@name1": "Ling", * "$id2": 2, ":age2": 18, "@name2": "Paul" * } * ); * ``` * * `res` is now : * ```javascript * [ * {"columns":["id"],"values":[[1],[2]]}, * {"columns":["age","name"],"values":[[1,"Ling"]]} * ] * ``` * @param {string} sql a string containing some SQL text to execute @param {Statement.BindParams} [params] When the SQL statement contains placeholders, you can pass them in here. They will be bound to the statement before it is executed. If you use the params argument as an array, you **cannot** provide an sql string that contains several statements (separated by `;`). This limitation does not apply to params as an object. * @return {Database.QueryExecResult[]} The results of each statement */ Database.prototype.exec = function exec(sql, params, config) { var curresult; var nextSqlPtr; var pStmt; var pzTail; var results; var stack; var stmt; if (!this.db) { throw "Database closed"; } stack = stackSave(); stmt = null; try { nextSqlPtr = allocateUTF8OnStack(sql); pzTail = stackAlloc(4); results = []; while (getValue(nextSqlPtr, "i8") !== NULL) { setValue(apiTemp, 0, "i32"); setValue(pzTail, 0, "i32"); this.handleError(sqlite3_prepare_v2_sqlptr( this.db, nextSqlPtr, -1, apiTemp, pzTail )); // pointer to a statement, or null pStmt = getValue(apiTemp, "i32"); nextSqlPtr = getValue(pzTail, "i32"); // Empty statement if (pStmt !== NULL) { curresult = null; stmt = new Statement(pStmt, this); if (params !== null && params !== undefined) { stmt.bind(params); } while (stmt.step()) { if (curresult === null) { curresult = { columns: stmt.getColumnNames(), values: [] }; results.push(curresult); } curresult.values.push(stmt.get(null, config)); } stmt.free(); } } return results; } catch (err) { if (stmt) { stmt.free(); } assertOrThrow(!err, err); } finally { stackRestore(stack); } }; /** Execute an sql statement, and call a callback for each row of result. Currently this method is synchronous, it will not return until the callback has been called on every row of the result. But this might change. @param {string} sql A string of SQL text. Can contain placeholders that will be bound to the parameters given as the second argument @param {Statement.BindParams} [params=[]] Parameters to bind to the query @param {function(Object):void} callback Function to call on each row of result @param {function():void} done A function that will be called when all rows have been retrieved @return {Database} The database object. Useful for method chaining @example Read values from a table db.each("SELECT name,age FROM users WHERE age >= $majority", {$majority:18}, function (row){console.log(row.name + " is a grown-up.")} ); */ Database.prototype.each = function each( sql, params, callback, done, config ) { var stmt; if (typeof params === "function") { done = callback; callback = params; params = undefined; } stmt = this.prepare(sql, params); try { while (stmt.step()) { callback(stmt.getAsObject(null, config)); } } catch (err) { stmt.free(); assertOrThrow(!err, err); } stmt.free(); if (typeof done === "function") { return done(); } return undefined; }; /** Prepare an SQL statement @param {string} sql a string of SQL, that can contain placeholders (`?`, `:VVV`, `:AAA`, `@AAA`) @param {Statement.BindParams} [params] values to bind to placeholders @return {Statement} the resulting statement @throws {String} SQLite error */ Database.prototype.prepare = function prepare(sql, params) { var pStmt; var stmt; setValue(apiTemp, 0, "i32"); this.handleError(sqlite3_prepare_v2(this.db, sql, -1, apiTemp, NULL)); // pointer to a statement, or null pStmt = getValue(apiTemp, "i32"); if (pStmt === NULL) { throw "Nothing to prepare"; } stmt = new Statement(pStmt, this); if (params !== null && params !== undefined) { stmt.bind(params); } this.statements[pStmt] = stmt; return stmt; }; /** Iterate over multiple SQL statements in a SQL string. * This function returns an iterator over {@link Statement} objects. * You can use a for..of loop to execute the returned statements one by one. * @param {string} sql a string of SQL that can contain multiple statements * @return {StatementIterator} the resulting statement iterator * @example Get the results of multiple SQL queries * const sql_queries = "SELECT 1 AS x; SELECT '2' as y"; * for (const statement of db.iterateStatements(sql_queries)) { * const sql = statement.getSQL(); // Get the SQL source * const result = statement.getAsObject({}); // Get the row of data * console.log(sql, result); * } * // This will print: * // 'SELECT 1 AS x;' {x: 1} * // " SELECT '2' as y" {y: '2'} */ Database.prototype.iterateStatements = function iterateStatements(sql) { return new StatementIterator(sql, this); }; /** Exports the contents of the database to a binary array @return {Uint8Array} An array of bytes of the SQLite3 database file */ Database.prototype.export = function exportDatabase() { var binaryDb; Object.values(this.statements).forEach(function each(stmt) { stmt.free(); }); Object.values(this.functions).forEach(removeFunction); this.functions = {}; this.handleError(sqlite3_close_v2(this.db)); binaryDb = FS.readFile(this.filename, {encoding: "binary"}); this.handleError(sqlite3_open(this.filename, apiTemp)); this.db = getValue(apiTemp, "i32"); return binaryDb; }; /** Close the database, and all associated prepared statements. * The memory associated to the database and all associated statements * will be freed. * * **Warning**: A statement belonging to a database that has been closed * cannot be used anymore. * * Databases **must** be closed when you're finished with them, or the * memory consumption will grow forever */ Database.prototype.close = function close() { // do nothing if db is null or already closed if (this.db === null) { return; } Object.values(this.statements).forEach(function each(stmt) { stmt.free(); }); Object.values(this.functions).forEach(removeFunction); this.functions = {}; this.handleError(sqlite3_close_v2(this.db)); FS.unlink("/" + this.filename); this.db = null; }; /** Analyze a result code, return null if no error occured, and throw an error with a descriptive message otherwise @nodoc */ Database.prototype.handleError = function handleError(returnCode) { var errmsg; if (returnCode === SQLITE_OK) { return null; } errmsg = sqlite3_errmsg(this.db); throw new Error(errmsg); }; /** Returns the number of changed rows (modified, inserted or deleted) by the latest completed INSERT, UPDATE or DELETE statement on the database. Executing any other type of SQL statement does not modify the value returned by this function. @return {number} the number of rows modified */ Database.prototype.getRowsModified = function getRowsModified() { return sqlite3_changes(this.db); }; /** Register a custom function with SQLite @example Register a simple function db.create_function("addOne", function (x) {return x+1;}) db.exec("SELECT addOne(1)") // = 2 @param {string} name the name of the function as referenced in SQL statements. @param {function} func the actual function to be executed. @return {Database} The database object. Useful for method chaining */ Database.prototype.create_function = function create_function( name, func ) { var func_ptr; function wrapped_func(cx, argc, argv) { var arg; var args; var blobptr; var ii; var result; var value_ptr; var value_type; function extract_blob(ptr) { var size = sqlite3_value_bytes(ptr); var blob_ptr = sqlite3_value_blob(ptr); var blob_arg = new Uint8Array(size); var jj; for (jj = 0; jj < size; jj += 1) { blob_arg[jj] = HEAP8[blob_ptr + jj]; } return blob_arg; } args = []; for (ii = 0; ii < argc; ii += 1) { value_ptr = getValue(argv + (4 * ii), "i32"); value_type = sqlite3_value_type(value_ptr); if ( value_type === SQLITE_INTEGER || value_type === SQLITE_FLOAT ) { arg = sqlite3_value_double(value_ptr); } else if (value_type === SQLITE_TEXT) { arg = sqlite3_value_text(value_ptr); } else if (value_type === SQLITE_BLOB) { arg = extract_blob(value_ptr); } else { arg = null; } args.push(arg); } try { result = func.apply(null, args); } catch (error) { sqlite3_result_error(cx, error, -1); return; } switch (typeof result) { case "boolean": sqlite3_result_int(cx, ( result ? 1 : 0 )); break; case "number": sqlite3_result_double(cx, result); break; case "string": sqlite3_result_text(cx, result, -1, -1); break; case "object": if (result === null) { sqlite3_result_null(cx); } else if (result.length !== undefined) { blobptr = allocate(result, ALLOC_NORMAL); sqlite3_result_blob(cx, blobptr, result.length, -1); _free(blobptr); } else { sqlite3_result_error(cx, ( "Wrong API use : tried to return a value " + "of an unknown type (" + result + ")." ), -1); } break; default: sqlite3_result_null(cx); } } if (Object.prototype.hasOwnProperty.call(this.functions, name)) { removeFunction(this.functions[name]); delete this.functions[name]; } // The signature of the wrapped function is : // void wrapped(sqlite3_context *db, int argc, sqlite3_value **argv) func_ptr = addFunction(wrapped_func, "viii"); this.functions[name] = func_ptr; this.handleError(sqlite3_create_function_v2( this.db, name, func.length, SQLITE_UTF8, 0, func_ptr, 0, 0, 0 )); return this; }; // export Database to Module Module.Database = Database; }; /* file sqlmath-sqljs.api.no.this.js */ /*jslint bitwise, for, unordered*/ /*global ALLOC_NORMAL FS HEAP8 Module _malloc _free addFunction allocate allocateUTF8OnStack getValue intArrayFromString removeFunction setValue stackAlloc stackRestore stackSave UTF8ToString stringToUTF8 lengthBytesUTF8 */ "use strict"; function assertOrThrow(passed, err) { if (!passed) { throw err; } } function bindThis(fnc) { return function () { return fnc.bind(undefined, this).apply(undefined, arguments); //jslint-quiet }; } /** * @typedef {{Database:Database, Statement:Statement}} SqlJs * @property {Database} Database A class that represents an SQLite database * @property {Statement} Statement The prepared statement class */ /** * @typedef {{locateFile:function(string):string}} SqlJsConfig * @property {function(string):string} locateFile * a function that returns the full path to a resource given its file name * @see https://emscripten.org/docs/api_reference/module.html */ /** * Asynchronously initializes sql.js * @function initSqlJs * @param {SqlJsConfig} config module inititialization parameters * @returns {SqlJs} * @example * initSqlJs({ * locateFile: name => '/path/to/assets/' + name * }).then(SQL => { * const db = new SQL.Database(); * const result = db.exec("select 'hello world'"); * console.log(result); * }) */ /** * @module SqlJs */ // Wait for preRun to run, and then finish our initialization Module["onRuntimeInitialized"] = function onRuntimeInitialized() { //jslint-quiet // Declare toplevel variables // register, used for temporary stack values var apiTemp = stackAlloc(4); var cwrap = Module["cwrap"]; //jslint-quiet // Null pointer var NULL = 0; // SQLite enum var SQLITE_OK = 0; var SQLITE_ROW = 100; var SQLITE_DONE = 101; var SQLITE_INTEGER = 1; var SQLITE_FLOAT = 2; var SQLITE_TEXT = 3; var SQLITE_BLOB = 4; // var - Encodings, used for registering functions. var SQLITE_UTF8 = 1; // var - cwrap function var sqlite3_open = cwrap("sqlite3_open", "number", ["string", "number"]); var sqlite3_close_v2 = cwrap("sqlite3_close_v2", "number", ["number"]); var sqlite3_exec = cwrap( "sqlite3_exec", "number", ["number", "string", "number", "number", "number"] ); var sqlite3_changes = cwrap("sqlite3_changes", "number", ["number"]); var sqlite3_prepare_v2 = cwrap( "sqlite3_prepare_v2", "number", ["number", "string", "number", "number", "number"] ); var sqlite3_sql = cwrap("sqlite3_sql", "string", ["number"]); var sqlite3_normalized_sql = cwrap( "sqlite3_normalized_sql", "string", ["number"] ); var sqlite3_prepare_v2_sqlptr = cwrap( "sqlite3_prepare_v2", "number", ["number", "number", "number", "number", "number"] ); var sqlite3_bind_text = cwrap( "sqlite3_bind_text", "number", ["number", "number", "number", "number", "number"] ); var sqlite3_bind_blob = cwrap( "sqlite3_bind_blob", "number", ["number", "number", "number", "number", "number"] ); var sqlite3_bind_double = cwrap( "sqlite3_bind_double", "number", ["number", "number", "number"] ); var sqlite3_bind_int = cwrap( "sqlite3_bind_int", "number", ["number", "number", "number"] ); var sqlite3_bind_parameter_index = cwrap( "sqlite3_bind_parameter_index", "number", ["number", "string"] ); var sqlite3_step = cwrap("sqlite3_step", "number", ["number"]); var sqlite3_errmsg = cwrap("sqlite3_errmsg", "string", ["number"]); var sqlite3_column_count = cwrap( "sqlite3_column_count", "number", ["number"] ); var sqlite3_data_count = cwrap("sqlite3_data_count", "number", ["number"]); var sqlite3_column_double = cwrap( "sqlite3_column_double", "number", ["number", "number"] ); var sqlite3_column_text = cwrap( "sqlite3_column_text", "string", ["number", "number"] ); var sqlite3_column_blob = cwrap( "sqlite3_column_blob", "number", ["number", "number"] ); var sqlite3_column_bytes = cwrap( "sqlite3_column_bytes", "number", ["number", "number"] ); var sqlite3_column_type = cwrap( "sqlite3_column_type", "number", ["number", "number"] ); var sqlite3_column_name = cwrap( "sqlite3_column_name", "string", ["number", "number"] ); var sqlite3_reset = cwrap("sqlite3_reset", "number", ["number"]); var sqlite3_clear_bindings = cwrap( "sqlite3_clear_bindings", "number", ["number"] ); var sqlite3_finalize = cwrap("sqlite3_finalize", "number", ["number"]); var sqlite3_create_function_v2 = cwrap( "sqlite3_create_function_v2", "number", [ "number", "string", "number", "number", "number", "number", "number", "number", "number" ] ); var sqlite3_value_type = cwrap("sqlite3_value_type", "number", ["number"]); var sqlite3_value_bytes = cwrap( "sqlite3_value_bytes", "number", ["number"] ); var sqlite3_value_text = cwrap("sqlite3_value_text", "string", ["number"]); var sqlite3_value_blob = cwrap("sqlite3_value_blob", "number", ["number"]); var sqlite3_value_double = cwrap( "sqlite3_value_double", "number", ["number"] ); var sqlite3_result_double = cwrap( "sqlite3_result_double", "", ["number", "number"] ); var sqlite3_result_null = cwrap( "sqlite3_result_null", "", ["number"] ); var sqlite3_result_text = cwrap( "sqlite3_result_text", "", ["number", "string", "number", "number"] ); var sqlite3_result_blob = cwrap( "sqlite3_result_blob", "", ["number", "number", "number", "number"] ); var sqlite3_result_int = cwrap( "sqlite3_result_int", "", ["number", "number"] ); var sqlite3_result_error = cwrap( "sqlite3_result_error", "", ["number", "string", "number"] ); var registerExtensionFunctions = cwrap( "RegisterExtensionFunctions", "number", ["number"] ); /** * @classdesc * Represents a prepared statement. * Prepared statements allow you to have a template sql string, * that you can execute multiple times with different parameters. * * You can't instantiate this class directly, you have to use a * {@link Database} object in order to create a statement. * * **Warnings** * 1. When you close a database (using db.close()), all * its statements are closed too and become unusable. * 1. After calling db.prepare() you must manually free the assigned memory * by calling Statement.free(). Failure to do this will cause subsequent * 'DROP TABLE ...' statements to fail with 'Uncaught Error: database table * is locked'. * * Statements can't be created by the API user directly, only by * Database::prepare * * @see Database.html#prepare-dynamic * @see https://en.wikipedia.org/wiki/Prepared_statement * * @constructs Statement * @memberof module:SqlJs * @param {number} stmt1 The SQLite statement reference * @param {Database} db The database from which this statement was created */ function Statement(stmt1, db) { var that = this; //jslint-quiet that.stmt = stmt1; that.db = db; // Index of the leftmost parameter is 1 that.pos = 1; // Pointers to allocated memory, that need to be freed // when the statemend is destroyed that.allocatedmem = []; } /** @typedef {string|number|null|Uint8Array} Database.SqlValue */ /** @typedef { Database.SqlValue[]|Object|null } Statement.BindParams */ /** Bind values to the parameters, after having reseted the statement. * If values is null, do nothing and return true. * * SQL statements can have parameters, * named *'?', '?NNN', ':VVV', '@VVV', '$VVV'*, * where NNN is a number and VVV a string. * This function binds these parameters to the given values. * * *Warning*: ':', '@', and '$' are included in the parameters names * * ## Value types * Javascript type | SQLite type * -----------------| ----------- * number | REAL, INTEGER * boolean | INTEGER * string | TEXT * Array, Uint8Array| BLOB * null | NULL * * @example Bind values to named parameters * var stmt = db.prepare( * "UPDATE test SET a=@newval WHERE id BETWEEN $mini AND $maxi" * ); * stmt.bind({$mini:10, $maxi:20, '@newval':5}); * * @example Bind values to anonymous parameters * // Create a statement that contains parameters like '?', '?NNN' * var stmt = db.prepare("UPDATE test SET a=? WHERE id BETWEEN ? AND ?"); * // Call Statement.bind with an array as parameter * stmt.bind([5, 10, 20]); * * @see http://www.sqlite.org/datatype3.html * @see http://www.sqlite.org/lang_expr.html#varparam * @param {Statement.BindParams} values The values to bind * @return {boolean} true if it worked * @throws {String} SQLite Error */ function statementBind(that, values) { if (!that.stmt) { throw "Statement closed"; } statementReset(that); if (Array.isArray(values)) { return statementBindFromArray(that, values); } if ( (values !== null && values !== undefined) && typeof values === "object" ) { return statementBindFromObject(that, values); } return true; } /** Execute the statement, fetching the the next line of result, that can be retrieved with {@link Statement.get}. @return {boolean} true if a row of result available @throws {String} SQLite Error */ function statementStep(that) { var ret; if (!that.stmt) { throw "Statement closed"; } that.pos = 1; ret = sqlite3_step(that.stmt); switch (ret) { case SQLITE_ROW: return true; case SQLITE_DONE: return false; default: throw databaseHandleError(that.db, ret); } } /* Internal methods to retrieve data from the results of a statement that has been executed */ function statementGetNumber(that, pos) { if (pos === null || pos === undefined) { pos = that.pos; that.pos += 1; } return sqlite3_column_double(that.stmt, pos); } function statementGetBigInt(that, pos) { var text; if (pos === null || pos === undefined) { pos = that.pos; that.pos += 1; } text = sqlite3_column_text(that.stmt, pos); if (typeof BigInt !== "function") { throw new Error("BigInt is not supported"); } /* global BigInt */ return BigInt(text); //jslint-quiet } function statementGetString(that, pos) { if (pos === null || pos === undefined) { pos = that.pos; that.pos += 1; } return sqlite3_column_text(that.stmt, pos); } function statementGetBlob(that, pos) { var ii; var ptr; var result; var size; if (pos === null || pos === undefined) { pos = that.pos; that.pos += 1; } size = sqlite3_column_bytes(that.stmt, pos); ptr = sqlite3_column_blob(that.stmt, pos); result = new Uint8Array(size); for (ii = 0; ii < size; ii += 1) { result[ii] = HEAP8[ptr + ii]; } return result; } /** Get one row of results of a statement. If the first parameter is not provided, step must have been called before. @param {Statement.BindParams} [params] If set, the values will be bound to the statement before it is executed @return {Database.SqlValue[]} One row of result @example Print all the rows of the table test to the console var stmt = db.prepare("SELECT * FROM test"); while (stmt.step()) console.log(stmt.get()); Enable BigInt support var stmt = db.prepare("SELECT * FROM test"); while (stmt.step()) console.log(stmt.get(null, {useBigInt: true})); */ function statementGet(that, params, config) { var results1; var ref; var field; config = config || {}; if ( (params !== null && params !== undefined) && statementBind(that, params) ) { statementStep(that); } results1 = []; ref = sqlite3_data_count(that.stmt); for (field = 0; field < ref; field += 1) { switch (sqlite3_column_type(that.stmt, field)) { case SQLITE_INTEGER: results1.push( config["useBigInt"] //jslint-quiet ? statementGetBigInt(that, field) : statementGetNumber(that, field) ); break; case SQLITE_FLOAT: results1.push(statementGetNumber(that, field)); break; case SQLITE_TEXT: results1.push(statementGetString(that, field)); break; case SQLITE_BLOB: results1.push(statementGetBlob(that, field)); break; default: results1.push(null); } } return results1; } /** Get the list of column names of a row of result of a statement. @return {string[]} The names of the columns @example var stmt = db.prepare( "SELECT 5 AS nbr, x'616200' AS data, NULL AS null_value;" ); stmt.step(); // Execute the statement console.log(stmt.getColumnNames()); // Will print ['nbr','data','null_value'] */ function statementGetColumnNames(that) { var results1 = []; var ref = sqlite3_column_count(that.stmt); var ii; for (ii = 0; ii < ref; ii += 1) { results1.push(sqlite3_column_name(that.stmt, ii)); } return results1; } /** Get one row of result as a javascript object, associating column names with their value in the current row. @param {Statement.BindParams} [params] If set, the values will be bound to the statement, and it will be executed @return {Object} The row of result @see {@link Statement.get} @example var stmt = db.prepare( "SELECT 5 AS nbr, x'010203' AS data, NULL AS null_value;" ); stmt.step(); // Execute the statement console.log(stmt.getAsObject()); // Will print {nbr:5, data: Uint8Array([1,2,3]), null_value:null} */ function statementGetAsObject(that, params, config) { var values = statementGet(that, params, config); var names = statementGetColumnNames(that); var rowObject = {}; var ii; var name; for (ii = 0; ii < names.length; ii += 1) { name = names[ii]; rowObject[name] = values[ii]; } return rowObject; } /** Get the SQL string used in preparing this statement. @return {string} The SQL string */ function statementGetSQL(that) { return sqlite3_sql(that.stmt); } /** Get the SQLite's normalized version of the SQL string used in preparing this statement. The meaning of "normalized" is not well-defined: see {@link https://sqlite.org/c3ref/expanded_sql.html the SQLite documentation}. @example db.run("create table test (x integer);"); stmt = db.prepare("select * from test where x = 42"); // returns "SELECT*FROM test WHERE x=?;" @return {string} The normalized SQL string */ function statementGetNormalizedSQL(that) { return sqlite3_normalized_sql(that.stmt); } /** Shorthand for bind + step + reset Bind the values, execute the statement, ignoring the rows it returns, and resets it @param {Statement.BindParams} [values] Value to bind to the statement */ function statementRun(that, values) { if (values !== null && values !== undefined) { statementBind(that, values); } statementStep(that); return statementReset(that); } function statementBindString(that, string, pos) { var bytes; var strptr; if (pos === null || pos === undefined) { pos = that.pos; that.pos += 1; } bytes = intArrayFromString(string); strptr = allocate(bytes, ALLOC_NORMAL); that.allocatedmem.push(strptr); databaseHandleError(that.db, sqlite3_bind_text( that.stmt, pos, strptr, bytes.length - 1, 0 )); return true; } function statementBindBlob(that, array, pos) { var blobptr; if (pos === null || pos === undefined) { pos = that.pos; that.pos += 1; } blobptr = allocate(array, ALLOC_NORMAL); that.allocatedmem.push(blobptr); databaseHandleError(that.db, sqlite3_bind_blob( that.stmt, pos, blobptr, array.length, 0 )); return true; } function statementBindNumber(that, num, pos) { var bindfunc; if (pos === null || pos === undefined) { pos = that.pos; that.pos += 1; } bindfunc = ( num === (num | 0) ? sqlite3_bind_int : sqlite3_bind_double ); databaseHandleError(that.db, bindfunc(that.stmt, pos, num)); return true; } function statementBindNull(that, pos) { if (pos === null || pos === undefined) { pos = that.pos; that.pos += 1; } return sqlite3_bind_blob(that.stmt, pos, 0, 0, 0) === SQLITE_OK; } function statementBindValue(that, val, pos) { if (pos === null || pos === undefined) { pos = that.pos; that.pos += 1; } switch (typeof val) { case "string": return statementBindString(that, val, pos); case "number": return statementBindNumber(that, val + 0, pos); case "bigint": // BigInt is not fully supported yet at WASM level. return statementBindString(that, val.toString(), pos); case "boolean": return statementBindNumber(that, val + 0, pos); case "object": if (val === null) { return statementBindNull(that, pos); } if (val.length !== undefined) { return statementBindBlob(that, val, pos); } break; } throw ( "Wrong API use : tried to bind a value of an unknown type (" + val + ")." ); } /** Bind names and values of an object to the named parameters of the statement @param {Object} valuesObj @private @nodoc */ function statementBindFromObject(that, valuesObj) { var num; Object.keys(valuesObj).forEach(function each(name) { num = sqlite3_bind_parameter_index(that.stmt, name); if (num !== 0) { statementBindValue(that, valuesObj[name], num); } }); return true; } /** Bind values to numbered parameters @param {Database.SqlValue[]} values @private @nodoc */ function statementBindFromArray(that, values) { var num; for (num = 0; num < values.length; num += 1) { statementBindValue(that, values[num], num + 1); } return true; } /** Reset a statement, so that it's parameters can be bound to new values It also clears all previous bindings, freeing the memory used by bound parameters. */ function statementReset(that) { while (that.allocatedmem.length > 0) { that.allocatedmem.pop(); _free(that.allocatedmem.pop()); } return ( sqlite3_clear_bindings(that.stmt) === SQLITE_OK && sqlite3_reset(that.stmt) === SQLITE_OK ); } /** Free the memory allocated during parameter binding */ function statementFreemem(that) { while (that.allocatedmem.length > 0) { that.allocatedmem.pop(); _free(that.allocatedmem.pop()); } } /** Free the memory used by the statement @return {boolean} true in case of success */ function statementFree(that) { var res; while (that.allocatedmem.length > 0) { that.allocatedmem.pop(); _free(that.allocatedmem.pop()); } res = sqlite3_finalize(that.stmt) === SQLITE_OK; delete that.db.statements[that.stmt]; that.stmt = NULL; return res; } /** * @classdesc * An iterator over multiple SQL statements in a string, * preparing and returning a Statement object for the next SQL * statement on each iteration. * * You can't instantiate this class directly, you have to use a * {@link Database} object in order to create a statement iterator * * {@see Database#iterateStatements} * * @example * // loop over and execute statements in string sql * for (let statement of db.iterateStatements(sql) { * statement.step(); * // get results, etc. * // do not call statement.free() manually, each statement is freed * // before the next one is parsed * } * * // capture any bad query exceptions with feedback * // on the bad sql * let it = db.iterateStatements(sql); * try { * for (let statement of it) { * statement.step(); * } * } catch(err) { * console.log( * `The SQL string "${it.getRemainingSQL()}" ` + * `contains the following error: ${err}` * ); * } * * @implements {Iterator} * @implements {Iterable} * @constructs StatementIterator * @memberof module:SqlJs * @param {string} sql A string containing multiple SQL statements * @param {Database} db The database from which this iterator was created */ function StatementIterator(sql, db) { var sz; var that = this; //jslint-quiet that.db = db; sz = lengthBytesUTF8(sql) + 1; that.sqlPtr = _malloc(sz); if (that.sqlPtr === null) { throw new Error("Unable to allocate memory for the SQL string"); } stringToUTF8(sql, that.sqlPtr, sz); that.nextSqlPtr = that.sqlPtr; that.nextSqlString = null; that.activeStatement = null; } /** * @typedef {{ done:true, value:undefined } | * { done:false, value:Statement}} * StatementIterator.StatementIteratorResult * @property {Statement} value the next available Statement * (as returned by {@link Database.prepare}) * @property {boolean} done true if there are no more available statements */ /** Prepare the next available SQL statement @return {StatementIterator.StatementIteratorResult} @throws {String} SQLite error or invalid iterator error */ function statementIteratorNext(that) { var pStmt; var pzTail; var stack; if (that.sqlPtr === null) { return {done: true}; } if (that.activeStatement !== null) { statementFree(that.activeStatement); that.activeStatement = null; } if (!that.db.db) { statementIteratorFinalize(that); throw new Error("Database closed"); } stack = stackSave(); pzTail = stackAlloc(4); setValue(apiTemp, 0, "i32"); setValue(pzTail, 0, "i32"); try { databaseHandleError(that.db, sqlite3_prepare_v2_sqlptr( that.db.db, that.nextSqlPtr, -1, apiTemp, pzTail )); that.nextSqlPtr = getValue(pzTail, "i32"); pStmt = getValue(apiTemp, "i32"); if (pStmt === NULL) { statementIteratorFinalize(that); return {done: true}; } that.activeStatement = new Statement(pStmt, that.db); that.db.statements[pStmt] = that.activeStatement; return {value: that.activeStatement, done: false}; } catch (err) { that.nextSqlString = UTF8ToString(that.nextSqlPtr); //jslint-quiet statementIteratorFinalize(that); assertOrThrow(!err, err); } finally { stackRestore(stack); } } function statementIteratorFinalize(that) { _free(that.sqlPtr); that.sqlPtr = null; } /** Get any un-executed portions remaining of the original SQL string @return {String} */ function statementIteratorGetRemainingSQL(that) { // iff an exception occurred, we set the nextSqlString if (that.nextSqlString !== null) { return that.nextSqlString; } // otherwise, convert from nextSqlPtr return UTF8ToString(that.nextSqlPtr); //jslint-quiet } /* implement Iterable interface */ if (typeof Symbol === "function" && typeof Symbol.iterator === "symbol") { StatementIterator.prototype[Symbol.iterator] = function iterator() { return this; //jslint-quiet }; } /** @classdesc * Represents an SQLite database * @constructs Database * @memberof module:SqlJs * Open a new database either by creating a new one or opening an existing * one stored in the byte array passed in first argument * @param {number[]} data An array of bytes representing * an SQLite database file */ function Database(data) { var that = this; //jslint-quiet that.filename = "dbfile_" + (0xffffffff * Math.random() >>> 0); if (data !== null && data !== undefined) { FS.createDataFile("/", that.filename, data, true, true); } databaseHandleError(that, sqlite3_open(that.filename, apiTemp)); that.db = getValue(apiTemp, "i32"); registerExtensionFunctions(that.db); // A list of all prepared statements of the database that.statements = {}; // A list of all user function of the database // (created by create_function call) that.functions = {}; } /** Execute an SQL query, ignoring the rows it returns. @param {string} sql a string containing some SQL text to execute @param {Statement.BindParams} [params] When the SQL statement contains placeholders, you can pass them in here. They will be bound to the statement before it is executed. If you use the params argument, you **cannot** provide an sql string that contains several statements (separated by `;`) @example // Insert values in a table db.run( "INSERT INTO test VALUES (:age, :name)", {':age' : 18, ':name' : 'John'} ); @return {Database} The database object (useful for method chaining) */ function databaseRun(that, sql, params) { var stmt; if (!that.db) { throw "Database closed"; } if (params) { stmt = databasePrepare(that, sql, params); try { statementStep(stmt); } catch (err) { statementFree(stmt); assertOrThrow(!err, err); } statementFree(stmt); } else { databaseHandleError( that, sqlite3_exec(that.db, sql, 0, 0, apiTemp) ); } return that; } /** * @typedef {{ columns:string[], values:Database.SqlValue[][] }} Database.QueryExecResult * @property {string[]} columns the name of the columns of the result * (as returned by {@link Statement.getColumnNames}) * @property {Database.SqlValue[][]} values one array per row, containing * the column values */ /** Execute an SQL query, and returns the result. * * This is a wrapper against * {@link Database.prepare}, * {@link Statement.bind}, * {@link Statement.step}, * {@link Statement.get}, * and {@link Statement.free}. * * The result is an array of result elements. There are as many result * elements as the number of statements in your sql string (statements are * separated by a semicolon) * * ## Example use * We will create the following table, named *test* and query it with a * multi-line statement using params: * * | id | age | name | * |:--:|:---:|:------:| * | 1 | 1 | Ling | * | 2 | 18 | Paul | * * We query it like that: * ```javascript * var db = new SQL.Database(); * var res = db.exec( * "DROP TABLE IF EXISTS test;\n" * + "CREATE TABLE test (id INTEGER, age INTEGER, name TEXT);" * + "INSERT INTO test VALUES ($id1, :age1, @name1);" * + "INSERT INTO test VALUES ($id2, :age2, @name2);" * + "SELECT id FROM test;" * + "SELECT age,name FROM test WHERE id=$id1", * { * "$id1": 1, ":age1": 1, "@name1": "Ling", * "$id2": 2, ":age2": 18, "@name2": "Paul" * } * ); * ``` * * `res` is now : * ```javascript * [ * {"columns":["id"],"values":[[1],[2]]}, * {"columns":["age","name"],"values":[[1,"Ling"]]} * ] * ``` * @param {string} sql a string containing some SQL text to execute @param {Statement.BindParams} [params] When the SQL statement contains placeholders, you can pass them in here. They will be bound to the statement before it is executed. If you use the params argument as an array, you **cannot** provide an sql string that contains several statements (separated by `;`). This limitation does not apply to params as an object. * @return {Database.QueryExecResult[]} The results of each statement */ function databaseExec(that, sql, params, config) { var db = that.db; var mallocList = []; var modeStep; var pNext; var pStmt; var pzTail; var rowList; var stack; var statementDict = that.statements; function statementBind2(params) { var ii; var nn; var row2; setValue(apiTemp, 0, "i32"); setValue(pzTail, 0, "i32"); throwIfNotSqliteOk(sqlite3_prepare_v2_sqlptr( db, pNext, -1, apiTemp, pzTail )); // Pointer to a statement or null. pStmt = getValue(apiTemp, "i32"); pNext = getValue(pzTail, "i32"); // Empty statement. if (pStmt === NULL) { return; } row2 = undefined; if (params) { // Reset pStmt. while (mallocList.length > 0) { _free(mallocList.pop()); } sqlite3_clear_bindings(pStmt); sqlite3_reset(pStmt); // Bind list of params. if (Array.isArray(params)) { params.forEach(function (val, ii) { statementBindValue2(val, ii + 1); }); // Bind dict of params. } else if (params) { Object.entries(params).forEach(function (elem) { ii = sqlite3_bind_parameter_index(pStmt, elem[0]); if (ii !== 0) { statementBindValue2(elem[1], ii); } }); } } while (true) { assertOrThrow(pStmt, "Statement closed"); // Step through one sql-statement. modeStep = sqlite3_step(pStmt); if (modeStep === SQLITE_DONE) { break; } throwIfNotSqliteOk(Number(modeStep !== SQLITE_ROW)); if (row2 === undefined) { row2 = { columns: [], values: [] }; nn = sqlite3_column_count(pStmt); ii = 0; while (ii < nn) { row2.columns.push( sqlite3_column_name(pStmt, ii) ); ii += 1; } rowList.push(row2); } row2.values.push( statementGetValList2(config) ); } statementFree2(); } function statementBindValue2(val, pos) { var errCode; var pVal; switch (typeof val) { // BigInt is not fully supported yet at WASM level. case "bigint": case "string": if (typeof val === "bigint") { //jslint-quiet val = val.toString(); } val = intArrayFromString(val); pVal = allocate(val, ALLOC_NORMAL); mallocList.push(pVal); errCode = sqlite3_bind_text( pStmt, pos, pVal, val.length - 1, 0 ); break; case "boolean": case "number": if (val === (val | 0)) { errCode = sqlite3_bind_int(pStmt, pos, val); break; } errCode = sqlite3_bind_double(pStmt, pos, val); break; case "object": if (val === null) { errCode = sqlite3_bind_blob(pStmt, pos, 0, 0, 0); break; } if (val.length !== undefined) { val = allocate(val, ALLOC_NORMAL); mallocList.push(val); errCode = sqlite3_bind_blob( pStmt, pos, val, val.length, 0 ); break; } errCode = undefined; break; } assertOrThrow(errCode !== undefined, ( "Wrong API use : tried to bind a value of an unknown type (" + val + ")." )); throwIfNotSqliteOk(errCode); } function statementFree2() { while (mallocList.length > 0) { _free(mallocList.pop()); } sqlite3_finalize(pStmt); delete statementDict[pStmt]; pStmt = NULL; } function statementGetValList2(config) { var col2; var ii; var nn; var nnBlob; var pBlob; var valBlob; var valList; config = config || {}; col2 = 0; nn = sqlite3_data_count(pStmt); valList = []; while (col2 < nn) { switch (sqlite3_column_type(pStmt, col2)) { case SQLITE_BLOB: nnBlob = sqlite3_column_bytes(pStmt, col2); pBlob = sqlite3_column_blob(pStmt, col2); valBlob = new Uint8Array(nnBlob); ii = 0; while (ii < nnBlob) { valBlob[ii] = HEAP8[pBlob + ii]; ii += 1; } valList.push(valBlob); break; case SQLITE_FLOAT: valList.push(sqlite3_column_double(pStmt, col2)); break; case SQLITE_INTEGER: valList.push( config["useBigInt"] //jslint-quiet ? BigInt(sqlite3_column_text(pStmt, col2)) //jslint-quiet : sqlite3_column_double(pStmt, col2) ); break; case SQLITE_TEXT: valList.push(sqlite3_column_text(pStmt, col2)); break; default: valList.push(null); } col2 += 1; } return valList; } assertOrThrow(db, "Database closed"); stack = stackSave(); try { pNext = allocateUTF8OnStack(sql); pzTail = stackAlloc(4); rowList = []; while (getValue(pNext, "i8") !== NULL) { statementBind2(params); } return rowList; } finally { //jslint-quiet statementFree2(); stackRestore(stack); } function throwIfNotSqliteOk(returnCode) { if (returnCode !== SQLITE_OK) { throw new Error(sqlite3_errmsg(db)); } } } /** Execute an sql statement, and call a callback for each row of result. Currently this method is synchronous, it will not return until the callback has been called on every row of the result. But this might change. @param {string} sql A string of SQL text. Can contain placeholders that will be bound to the parameters given as the second argument @param {Statement.BindParams} [params=[]] Parameters to bind to the query @param {function(Object):void} callback Function to call on each row of result @param {function():void} done A function that will be called when all rows have been retrieved @return {Database} The database object. Useful for method chaining @example Read values from a table db.each("SELECT name,age FROM users WHERE age >= $majority", {$majority:18}, function (row){console.log(row.name + " is a grown-up.")} ); */ function databaseEach(that, sql, params, callback, done, config) { var stmt; if (typeof params === "function") { done = callback; callback = params; params = undefined; } stmt = databasePrepare(that, sql, params); try { while (statementStep(stmt)) { callback(statementGetAsObject(stmt, null, config)); } } catch (err) { statementFree(stmt); assertOrThrow(!err, err); } statementFree(stmt); if (typeof done === "function") { return done(); } return undefined; } /** Prepare an SQL statement @param {string} sql a string of SQL, that can contain placeholders (`?`, `:VVV`, `:AAA`, `@AAA`) @param {Statement.BindParams} [params] values to bind to placeholders @return {Statement} the resulting statement @throws {String} SQLite error */ function databasePrepare(that, sql, params) { var pStmt; var stmt; setValue(apiTemp, 0, "i32"); databaseHandleError( that, sqlite3_prepare_v2(that.db, sql, -1, apiTemp, NULL) ); // pointer to a statement, or null pStmt = getValue(apiTemp, "i32"); if (pStmt === NULL) { throw "Nothing to prepare"; } stmt = new Statement(pStmt, that); if (params !== null && params !== undefined) { statementBind(stmt, params); } that.statements[pStmt] = stmt; return stmt; } /** Iterate over multiple SQL statements in a SQL string. * This function returns an iterator over {@link Statement} objects. * You can use a for..of loop to execute the returned statements one by one. * @param {string} sql a string of SQL that can contain multiple statements * @return {StatementIterator} the resulting statement iterator * @example Get the results of multiple SQL queries * const sql_queries = "SELECT 1 AS x; SELECT '2' as y"; * for (const statement of db.iterateStatements(sql_queries)) { * const sql = statement.getSQL(); // Get the SQL source * const result = statement.getAsObject({}); // Get the row of data * console.log(sql, result); * } * // This will print: * // 'SELECT 1 AS x;' {x: 1} * // " SELECT '2' as y" {y: '2'} */ function databaseIterateStatements(that, sql) { return new StatementIterator(sql, that); } /** Exports the contents of the database to a binary array @return {Uint8Array} An array of bytes of the SQLite3 database file */ function databaseExport(that) { var binaryDb; Object.values(that.statements).forEach(function each(stmt) { statementFree(stmt); }); Object.values(that.functions).forEach(removeFunction); that.functions = {}; databaseHandleError(that, sqlite3_close_v2(that.db)); binaryDb = FS.readFile(that.filename, {encoding: "binary"}); databaseHandleError(that, sqlite3_open(that.filename, apiTemp)); that.db = getValue(apiTemp, "i32"); return binaryDb; } /** Close the database, and all associated prepared statements. * The memory associated to the database and all associated statements * will be freed. * * **Warning**: A statement belonging to a database that has been closed * cannot be used anymore. * * Databases **must** be closed when you're finished with them, or the * memory consumption will grow forever */ function databaseClose(that) { // do nothing if db is null or already closed if (that.db === null) { return; } Object.values(that.statements).forEach(function each(stmt) { statementFree(stmt); }); Object.values(that.functions).forEach(removeFunction); that.functions = {}; databaseHandleError(that, sqlite3_close_v2(that.db)); FS.unlink("/" + that.filename); that.db = null; } /** Analyze a result code, return null if no error occured, and throw an error with a descriptive message otherwise @nodoc */ function databaseHandleError(that, returnCode) { var errmsg; if (returnCode === SQLITE_OK) { return null; } errmsg = sqlite3_errmsg(that.db); throw new Error(errmsg); } /** Returns the number of changed rows (modified, inserted or deleted) by the latest completed INSERT, UPDATE or DELETE statement on the database. Executing any other type of SQL statement does not modify the value returned by this function. @return {number} the number of rows modified */ function databaseGetRowsModified(that) { return sqlite3_changes(that.db); } /** Register a custom function with SQLite @example Register a simple function db.create_function("addOne", function (x) {return x+1;}) db.exec("SELECT addOne(1)") // = 2 @param {string} name the name of the function as referenced in SQL statements. @param {function} func the actual function to be executed. @return {Database} The database object. Useful for method chaining */ function databaseCreate_function(that, name, func) { var func_ptr; function wrapped_func(cx, argc, argv) { var arg; var args; var blobptr; var ii; var result; var value_ptr; var value_type; function extract_blob(ptr) { var size = sqlite3_value_bytes(ptr); var blob_ptr = sqlite3_value_blob(ptr); var blob_arg = new Uint8Array(size); var jj; for (jj = 0; jj < size; jj += 1) { blob_arg[jj] = HEAP8[blob_ptr + jj]; } return blob_arg; } args = []; for (ii = 0; ii < argc; ii += 1) { value_ptr = getValue(argv + (4 * ii), "i32"); value_type = sqlite3_value_type(value_ptr); if ( value_type === SQLITE_INTEGER || value_type === SQLITE_FLOAT ) { arg = sqlite3_value_double(value_ptr); } else if (value_type === SQLITE_TEXT) { arg = sqlite3_value_text(value_ptr); } else if (value_type === SQLITE_BLOB) { arg = extract_blob(value_ptr); } else { arg = null; } args.push(arg); } try { result = func.apply(null, args); } catch (error) { sqlite3_result_error(cx, error, -1); return; } switch (typeof result) { case "boolean": sqlite3_result_int(cx, ( result ? 1 : 0 )); break; case "number": sqlite3_result_double(cx, result); break; case "string": sqlite3_result_text(cx, result, -1, -1); break; case "object": if (result === null) { sqlite3_result_null(cx); } else if (result.length !== undefined) { blobptr = allocate(result, ALLOC_NORMAL); sqlite3_result_blob(cx, blobptr, result.length, -1); _free(blobptr); } else { sqlite3_result_error(cx, ( "Wrong API use : tried to return a value " + "of an unknown type (" + result + ")." ), -1); } break; default: sqlite3_result_null(cx); } } if (Object.prototype.hasOwnProperty.call(that.functions, name)) { removeFunction(that.functions[name]); delete that.functions[name]; } // The signature of the wrapped function is : // void wrapped(sqlite3_context *db, int argc, sqlite3_value **argv) func_ptr = addFunction(wrapped_func, "viii"); that.functions[name] = func_ptr; databaseHandleError(that, sqlite3_create_function_v2( that.db, name, func.length, SQLITE_UTF8, 0, func_ptr, 0, 0, 0 )); return that; } Object.assign(Database.prototype, { "close": bindThis(databaseClose), "create_function": bindThis(databaseCreate_function), "each": bindThis(databaseEach), "exec": bindThis(databaseExec), "export": bindThis(databaseExport), "getRowsModified": bindThis(databaseGetRowsModified), "handleError": bindThis(databaseHandleError), "iterateStatements": bindThis(databaseIterateStatements), "prepare": bindThis(databasePrepare), "run": bindThis(databaseRun) }); Object.assign(Statement.prototype, { "bind": bindThis(statementBind), "bindBlob": bindThis(statementBindBlob), "bindFromArray": bindThis(statementBindFromArray), "bindFromObject": bindThis(statementBindFromObject), "bindNull": bindThis(statementBindNull), "bindNumber": bindThis(statementBindNumber), "bindString": bindThis(statementBindString), "bindValue": bindThis(statementBindValue), "free": bindThis(statementFree), "freemem": bindThis(statementFreemem), "get": bindThis(statementGet), "getAsObject": bindThis(statementGetAsObject), "getBigInt": bindThis(statementGetBigInt), "getBlob": bindThis(statementGetBlob), "getColumnNames": bindThis(statementGetColumnNames), "getNormalizedSQL": bindThis(statementGetNormalizedSQL), "getNumber": bindThis(statementGetNumber), "getSQL": bindThis(statementGetSQL), "getString": bindThis(statementGetString), "reset": bindThis(statementReset), "run": bindThis(statementRun), "step": bindThis(statementStep) }); Object.assign(StatementIterator.prototype, { "finalize": bindThis(statementIteratorFinalize), "getRemainingSQL": bindThis(statementIteratorGetRemainingSQL), "next": bindThis(statementIteratorNext) }); // export Database to Module Module["Database"] = Database; //jslint-quiet }; /* file sqlmath-test-old.js */ /*mode-coverage-ignore-file*/ /*jslint-disable*/ /* shRawLibFetch { "fetchList": [ { "comment": true, "url": "https://github.com/mapbox/node-sqlite3/blob/v5.0.2/LICENSE" }, { "urlIgnore": "https://github.com/mapbox/node-sqlite3/blob/v5.0.2/test/support/createdb-electron.js" }, { "url": "https://github.com/mapbox/node-sqlite3/blob/v5.0.2/test/support/createdb.js" }, { "url": "https://github.com/mapbox/node-sqlite3/blob/v5.0.2/test/support/helper.js" }, { "url": "https://github.com/mapbox/node-sqlite3/blob/v5.0.2/test/support/script.sql" }, { "url": "https://github.com/mapbox/node-sqlite3/blob/v5.0.2/test/affected.test.js" }, { "url": "https://github.com/mapbox/node-sqlite3/blob/v5.0.2/test/backup.test.js" }, { "url": "https://github.com/mapbox/node-sqlite3/blob/v5.0.2/test/blob.test.js" }, { "url": "https://github.com/mapbox/node-sqlite3/blob/v5.0.2/test/cache.test.js" }, { "url": "https://github.com/mapbox/node-sqlite3/blob/v5.0.2/test/constants.test.js" }, { "url": "https://github.com/mapbox/node-sqlite3/blob/v5.0.2/test/database_fail.test.js" }, { "url": "https://github.com/mapbox/node-sqlite3/blob/v5.0.2/test/each.test.js" }, { "url": "https://github.com/mapbox/node-sqlite3/blob/v5.0.2/test/exec.test.js" }, { "url": "https://github.com/mapbox/node-sqlite3/blob/v5.0.2/test/extension.test.js" }, { "urlIgnore": "https://github.com/mapbox/node-sqlite3/blob/v5.0.2/test/extension_functions.test.js" }, { "url": "https://github.com/mapbox/node-sqlite3/blob/v5.0.2/test/fts-content.test.js" }, { "url": "https://github.com/mapbox/node-sqlite3/blob/v5.0.2/test/interrupt.test.js" }, { "url": "https://github.com/mapbox/node-sqlite3/blob/v5.0.2/test/issue-108.test.js" }, { "url": "https://github.com/mapbox/node-sqlite3/blob/v5.0.2/test/json.test.js" }, { "url": "https://github.com/mapbox/node-sqlite3/blob/v5.0.2/test/map.test.js" }, { "url": "https://github.com/mapbox/node-sqlite3/blob/v5.0.2/test/named_columns.test.js" }, { "url": "https://github.com/mapbox/node-sqlite3/blob/v5.0.2/test/named_params.test.js" }, { "url": "https://github.com/mapbox/node-sqlite3/blob/v5.0.2/test/null_error.test.js" }, { "url": "https://github.com/mapbox/node-sqlite3/blob/v5.0.2/test/open_close.test.js" }, { "url": "https://github.com/mapbox/node-sqlite3/blob/v5.0.2/test/other_objects.test.js" }, { "url": "https://github.com/mapbox/node-sqlite3/blob/v5.0.2/test/parallel_insert.test.js" }, { "url": "https://github.com/mapbox/node-sqlite3/blob/v5.0.2/test/prepare.test.js" }, { "url": "https://github.com/mapbox/node-sqlite3/blob/v5.0.2/test/profile.test.js" }, { "url": "https://github.com/mapbox/node-sqlite3/blob/v5.0.2/test/rerun.test.js" }, { "url": "https://github.com/mapbox/node-sqlite3/blob/v5.0.2/test/scheduling.test.js" }, { "url": "https://github.com/mapbox/node-sqlite3/blob/v5.0.2/test/serialization.test.js" }, { "url": "https://github.com/mapbox/node-sqlite3/blob/v5.0.2/test/trace.test.js" }, { "url": "https://github.com/mapbox/node-sqlite3/blob/v5.0.2/test/unicode.test.js" }, { "url": "https://github.com/mapbox/node-sqlite3/blob/v5.0.2/test/upsert.test.js" }, { "url": "https://github.com/mapbox/node-sqlite3/blob/v5.0.2/test/verbose.test.js" } ], "replaceList": [ { "aa": "\n/\\*\nfile .*?/test/.*?([^/]*?)\n\\*\\/\n", "bb": "\n}());$&await (async function() {\nconsole.error(\"\\ntest-old.js - $1\");\n", "flags": "g" }, { "aa": "\\(sqlite3.VERSION_NUMBER\\b.*?\\bit\\)", "bb": "it", "flags": "g" }, { "aa": "\\b(before|it) *?\\(", "bb": "await $&", "flags": "g" }, { "aa": "\\bdescribe *?\\([\\S\\s]*?,", "bb": "await $& async", "flags": "g" }, { "aa": "\\brequire\\('\\.\\.'\\)", "bb": "require('.')", "flags": "g" }, { "aa": "var helper = require\\('./support/helper'\\);", "bb": "// $&", "flags": "g" }, { "aa": "\\btest/tmp\\b", "bb": ".tmp/test", "flags": "g" }, { "aa": "'test/support/prepare.db'", "bb": "'.tmp/test/prepare.db'", "flags": "g" } ] } +// hack-test + assert.equal(Math.floor(row.flt * 10**10), Math.floor(10 * Math.PI * 10**10)); - assert.equal(rows[i].flt, i * Math.PI); +// hack-test + assert.equal(Math.floor(rows[i].flt * 10**10), Math.floor(i * Math.PI * 10**10)); - assert.equal(row.flt, 10 * Math.PI); +// hack-test + assert.equal(Math.floor(row.flt * 10**10), Math.floor(10 * Math.PI * 10**10)); - assert.equal(row.flt, val * Math.PI); +// hack-test + assert.equal(Math.floor(row.flt * 10**10), Math.floor(val * Math.PI * 10**10)); - assert.equal(row.integer, integer); - done(); - }); - }); - }); - }); + assert.equal(row.integer, integer); + done(); + }); + }); + }); +// hack-test + } - assert.equal(row.flt, 10 * Math.PI); +// hack-test + assert.equal(Math.floor(row.flt * 10**10), Math.floor(10 * Math.PI * 10**10)); - var buff = new Buffer(2); +// hack-test + var buff = Buffer.alloc(2); - db = new sqlite3.Database('test/support/big.db', sqlite3.OPEN_READONLY, done); +// hack-test + db = new sqlite3.Database('.tmp/test/big.db', sqlite3.OPEN_READONLY, done); - var sql = fs.readFileSync('test/support/script.sql', 'utf8'); +// hack-test + var sql = assetDict['test/support/script.sql']; - (sqlite3.VERSION_NUMBER < 3008000 ? describe.skip : describe)('open and close shared memory database', function() { +// hack-test + describe('open and close shared memory database', async function() { - [ - 4294967296.249, +// hack-test + var list = [ + 4294967296.249, - ].forEach(function(flt) { +// hack-test + ]; + while (true) { + var flt = list.shift(); + if (!flt) { + break; + } - ].forEach(function(integer) { +// hack-test + ]; + while (true) { + var integer = list.shift(); + if (!integer) { + break; + } - var count = 1000000; - var db_path = path.join(__dirname,'big.db'); +// hack-test + var count = 100000; + var db_path = path.join(__dirname,'.tmp/test/big.db'); - var sqlite3 = require('../../lib/sqlite3'); +// hack-test + var sqlite3 = require('.'); - }); - - [ - 4294967299, +// hack-test + } + + var list = [ + 4294967299, -/\\* -file none -*\\/ +// hack-test +}()); +}()); +/\\* +file none +*\\/ -console.error("\ntest-old.js - script.sql"); +console.error("\ntest-old.js - script.sql"); +// hack-test +assetDict["test/support/script.sql"] = ` -exports.deleteFile = function(name) { +// hack-test +helper.deleteFile = function(name) { -exports.ensureExists = function(name,cb) { +// hack-test +helper.ensureExists = function(name,cb) { -if (require.main === module) { - createdb(); -} +// hack-test +if (true || require.main === module) { + await new Promise(function (resolve) {createdb(resolve);}); +} +/\\*jslint beta, node*\\/ +new Promise(function (resolve) { + let db; + let sqlite3 = require("."); + db = new sqlite3.Database(".tmp/test/prepare.db"); + db.serialize(function () { + db.exec(( + "CREATE TABLE foo (txt text, num int, flt float, blb blob);\n" + + "BEGIN TRANSACTION;\n" + + Array.from(new Array(5000), function (ignore, ii) { + return ( + "INSERT INTO foo VALUES(" + + "'String " + ii + "'," + + ii + "," + + ii * Math.PI + "," + + "NULL" // null (SQLite sets this implicitly) + + ");\n" + ); + }).join("") + + "COMMIT TRANSACTION;\n" + ), function (err) { + if (err) { + throw err; + } + resolve(); + }); + }); +}); -var elmo = fs.readFileSync(__dirname + '/support/elmo.png'); +// hack-test +var elmo = Buffer.from([1, 2, 3, 4]); -}()); +// hack-test +(async function() { +/\\*jslint beta, name*\\/ +/\\*global console, require, process*\\/ +let after; +let afterEach2; +let assetDict = {}; +let before; +let beforeEach2; +let helper = {}; +function afterEach(fnc) { + afterEach2 = fnc; +} +function beforeEach(fnc) { + beforeEach2 = fnc; +} +async function describe(testDescribe, fnc) { + console.error(" " + lineno() + " - describe - " + testDescribe); + afterEach2 = noop; + beforeEach2 = noop; + await fnc(); +} +async function it(testShould, fnc) { + let timeStart = Date.now(); + process.stderr.write( + " " + lineno() + + " - it" + + " - " + testShould + + " ... " + ); + await promisify(beforeEach2); + await promisify(fnc); + await promisify(afterEach2); + console.error( + (Date.now() - timeStart) + " ms" + + " \u001b[32m\u2713\u001b[39m " + ); +} +function lineno() { + return new Error().stack.match( + /(?:\n.*?){3}(\d+?):\d+?\)?$/m + )[1]; +} +function noop() { + return; +} +async function promisify(fnc) { + if (fnc.length === 0) { + return fnc(); + } + await new Promise(function (resolve) { + fnc(resolve); + }); +} +after = promisify; +before = promisify; +require("fs").rmdirSync(".tmp/test", {recursive: true}); +require("fs").mkdirSync(".tmp/test", {recursive: true}); -}()); -/\\* -file https://github.com/mapbox/node-sqlite3/blob/v5.0.2/test/affected.test.js -*\\/ +// hack-test +`; +}()); +/\\* +file https://github.com/mapbox/node-sqlite3/blob/v5.0.2/test/affected.test.js +*\\/ */ /* repo https://github.com/mapbox/node-sqlite3/tree/v5.0.2 committed 2021-02-15T14:42:52Z */ /* file https://github.com/mapbox/node-sqlite3/blob/v5.0.2/LICENSE */ /* Copyright (c) MapBox All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. - Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. - Neither the name "MapBox" nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 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. */ // hack-test (async function() { /*jslint beta, name*/ /*global console, require, process*/ let after; let afterEach2; let assetDict = {}; let before; let beforeEach2; let helper = {}; function afterEach(fnc) { afterEach2 = fnc; } function beforeEach(fnc) { beforeEach2 = fnc; } async function describe(testDescribe, fnc) { console.error(" " + lineno() + " - describe - " + testDescribe); afterEach2 = noop; beforeEach2 = noop; await fnc(); } async function it(testShould, fnc) { let timeStart = Date.now(); process.stderr.write( " " + lineno() + " - it" + " - " + testShould + " ... " ); await promisify(beforeEach2); await promisify(fnc); await promisify(afterEach2); console.error( (Date.now() - timeStart) + " ms" + " \u001b[32m\u2713\u001b[39m " ); } function lineno() { return new Error().stack.match( /(?:\n.*?){3}(\d+?):\d+?\)?$/m )[1]; } function noop() { return; } async function promisify(fnc) { if (fnc.length === 0) { return fnc(); } await new Promise(function (resolve) { fnc(resolve); }); } after = promisify; before = promisify; require("fs").rmdirSync(".tmp/test", {recursive: true}); require("fs").mkdirSync(".tmp/test", {recursive: true}); /* file https://github.com/mapbox/node-sqlite3/blob/v5.0.2/test/support/createdb.js */ await (async function() { console.error("\ntest-old.js - createdb.js"); // #!/usr/bin/env node function createdb(callback) { var existsSync = require('fs').existsSync || require('path').existsSync; var path = require('path'); // hack-test var sqlite3 = require('.'); // hack-test var count = 100000; var db_path = path.join(__dirname,'.tmp/test/big.db'); function randomString() { var str = ''; var chars = 'abcdefghijklmnopqrstuvwxzyABCDEFGHIJKLMNOPQRSTUVWXZY0123456789 '; for (var i = Math.random() * 100; i > 0; i--) { str += chars[Math.floor(Math.random() * chars.length)]; } return str; }; if (existsSync(db_path)) { console.log('okay: database already created (' + db_path + ')'); if (callback) callback(); } else { console.log("Creating test database... This may take several minutes."); var db = new sqlite3.Database(db_path); db.serialize(function() { db.run("CREATE TABLE foo (id INT, txt TEXT)"); db.run("BEGIN TRANSACTION"); var stmt = db.prepare("INSERT INTO foo VALUES(?, ?)"); for (var i = 0; i < count; i++) { stmt.run(i, randomString()); } stmt.finalize(); db.run("COMMIT TRANSACTION", [], function () { db.close(callback); }); }); } }; // hack-test if (true || require.main === module) { await new Promise(function (resolve) {createdb(resolve);}); } /*jslint beta, node*/ new Promise(function (resolve) { let db; let sqlite3 = require("."); db = new sqlite3.Database(".tmp/test/prepare.db"); db.serialize(function () { db.exec(( "CREATE TABLE foo (txt text, num int, flt float, blb blob);\n" + "BEGIN TRANSACTION;\n" + Array.from(new Array(5000), function (ignore, ii) { return ( "INSERT INTO foo VALUES(" + "'String " + ii + "'," + ii + "," + ii * Math.PI + "," + "NULL" // null (SQLite sets this implicitly) + ");\n" ); }).join("") + "COMMIT TRANSACTION;\n" ), function (err) { if (err) { throw err; } resolve(); }); }); }); module.exports = createdb; }()); /* file https://github.com/mapbox/node-sqlite3/blob/v5.0.2/test/support/helper.js */ await (async function() { console.error("\ntest-old.js - helper.js"); var assert = require('assert'); var fs = require('fs'); var pathExists = require('fs').existsSync || require('path').existsSync; // hack-test helper.deleteFile = function(name) { try { fs.unlinkSync(name); } catch(err) { if (err.errno !== process.ENOENT && err.code !== 'ENOENT' && err.syscall !== 'unlink') { throw err; } } }; // hack-test helper.ensureExists = function(name,cb) { if (!pathExists(name)) { fs.mkdirSync(name); }; } assert.fileDoesNotExist = function(name) { try { fs.statSync(name); } catch(err) { if (err.errno !== process.ENOENT && err.code !== 'ENOENT' && err.syscall !== 'unlink') { throw err; } } }; assert.fileExists = function(name) { try { fs.statSync(name); } catch(err) { throw err; } }; }()); /* file https://github.com/mapbox/node-sqlite3/blob/v5.0.2/test/support/script.sql */ await (async function() { console.error("\ntest-old.js - script.sql"); // hack-test assetDict["test/support/script.sql"] = ` CREATE TABLE IF NOT EXISTS map ( zoom_level INTEGER, tile_column INTEGER, tile_row INTEGER, tile_id TEXT, grid_id TEXT ); CREATE TABLE IF NOT EXISTS grid_key ( grid_id TEXT, key_name TEXT ); CREATE TABLE IF NOT EXISTS keymap ( key_name TEXT, key_json TEXT ); CREATE TABLE IF NOT EXISTS grid_utfgrid ( grid_id TEXT, grid_utfgrid TEXT ); CREATE TABLE IF NOT EXISTS images ( tile_data blob, tile_id text ); CREATE TABLE IF NOT EXISTS metadata ( name text, value text ); CREATE UNIQUE INDEX IF NOT EXISTS map_index ON map (zoom_level, tile_column, tile_row); CREATE UNIQUE INDEX IF NOT EXISTS grid_key_lookup ON grid_key (grid_id, key_name); CREATE UNIQUE INDEX IF NOT EXISTS keymap_lookup ON keymap (key_name); CREATE UNIQUE INDEX IF NOT EXISTS grid_utfgrid_lookup ON grid_utfgrid (grid_id); CREATE UNIQUE INDEX IF NOT EXISTS images_id ON images (tile_id); CREATE UNIQUE INDEX IF NOT EXISTS name ON metadata (name); CREATE VIEW IF NOT EXISTS tiles AS SELECT map.zoom_level AS zoom_level, map.tile_column AS tile_column, map.tile_row AS tile_row, images.tile_data AS tile_data FROM map JOIN images ON images.tile_id = map.tile_id; CREATE VIEW IF NOT EXISTS grids AS SELECT map.zoom_level AS zoom_level, map.tile_column AS tile_column, map.tile_row AS tile_row, grid_utfgrid.grid_utfgrid AS grid FROM map JOIN grid_utfgrid ON grid_utfgrid.grid_id = map.grid_id; CREATE VIEW IF NOT EXISTS grid_data AS SELECT map.zoom_level AS zoom_level, map.tile_column AS tile_column, map.tile_row AS tile_row, keymap.key_name AS key_name, keymap.key_json AS key_json FROM map JOIN grid_key ON map.grid_id = grid_key.grid_id JOIN keymap ON grid_key.key_name = keymap.key_name; // hack-test `; }()); /* file https://github.com/mapbox/node-sqlite3/blob/v5.0.2/test/affected.test.js */ await (async function() { console.error("\ntest-old.js - affected.test.js"); var sqlite3 = require('.'); var assert = require('assert'); await describe('query properties', async function() { var db; await before(function(done) { db = new sqlite3.Database(':memory:'); db.run("CREATE TABLE foo (id INT, txt TEXT)", done); }); await it('should return the correct lastID', function(done) { var stmt = db.prepare("INSERT INTO foo VALUES(?, ?)"); var j = 1; for (var i = 0; i < 5000; i++) { stmt.run(i, "demo", function(err) { if (err) throw err; // Relies on SQLite's row numbering to be gapless and starting // from 1. assert.equal(j++, this.lastID); }); } db.wait(done); }); await it('should return the correct changes count', function(done) { db.run("UPDATE foo SET id = id + 1 WHERE id % 2 = 0", function(err) { if (err) throw err; assert.equal(2500, this.changes); done(); }); }); }); }()); /* file https://github.com/mapbox/node-sqlite3/blob/v5.0.2/test/backup.test.js */ await (async function() { console.error("\ntest-old.js - backup.test.js"); var sqlite3 = require('.'); var assert = require('assert'); var fs = require('fs'); // var helper = require('./support/helper'); // Check that the number of rows in two tables matches. function assertRowsMatchDb(db1, table1, db2, table2, done) { db1.get("SELECT COUNT(*) as count FROM " + table1, function(err, row) { if (err) throw err; db2.get("SELECT COUNT(*) as count FROM " + table2, function(err, row2) { if (err) throw err; assert.equal(row.count, row2.count); done(); }); }); } // Check that the number of rows in the table "foo" is preserved in a backup. function assertRowsMatchFile(db, backupName, done) { var db2 = new sqlite3.Database(backupName, sqlite3.OPEN_READONLY, function(err) { if (err) throw err; assertRowsMatchDb(db, 'foo', db2, 'foo', function() { db2.close(done); }); }); } await describe('backup', async function() { await before(function() { helper.ensureExists('.tmp/test'); }); var db; beforeEach(function(done) { helper.deleteFile('.tmp/test/backup.db'); helper.deleteFile('.tmp/test/backup2.db'); db = new sqlite3.Database('.tmp/test/prepare.db', sqlite3.OPEN_READONLY, done); }); afterEach(function(done) { if (!db) { done(); } db.close(done); }); await it ('output db created once step is called', function(done) { var backup = db.backup('.tmp/test/backup.db', function(err) { if (err) throw err; backup.step(1, function(err) { if (err) throw err; assert.fileExists('.tmp/test/backup.db'); backup.finish(done); }); }); }); await it ('copies source fully with step(-1)', function(done) { var backup = db.backup('.tmp/test/backup.db'); backup.step(-1, function(err) { if (err) throw err; assert.fileExists('.tmp/test/backup.db'); backup.finish(function(err) { if (err) throw err; assertRowsMatchFile(db, '.tmp/test/backup.db', done); }); }); }); await it ('backup db not created if finished immediately', function(done) { var backup = db.backup('.tmp/test/backup.db'); backup.finish(function(err) { if (err) throw err; assert.fileDoesNotExist('.tmp/test/backup.db'); done(); }); }); await it ('error closing db if backup not finished', function(done) { var backup = db.backup('.tmp/test/backup.db'); db.close(function(err) { db = null; if (!err) throw new Error('should have an error'); if (err.errno == sqlite3.BUSY) { done(); } else throw err; }); }); await it ('using the backup after finished is an error', function(done) { var backup = db.backup('.tmp/test/backup.db'); backup.finish(function(err) { if (err) throw err; backup.step(1, function(err) { if (!err) throw new Error('should have an error'); if (err.errno == sqlite3.MISUSE && err.message === 'SQLITE_MISUSE: Backup is already finished') { done(); } else throw err; }); }); }); await it ('remaining/pageCount are available after call to step', function(done) { var backup = db.backup('.tmp/test/backup.db'); backup.step(0, function(err) { if (err) throw err; assert.equal(typeof this.pageCount, 'number'); assert.equal(typeof this.remaining, 'number'); assert.equal(this.remaining, this.pageCount); var prevRemaining = this.remaining; var prevPageCount = this.pageCount; backup.step(1, function(err) { if (err) throw err; assert.notEqual(this.remaining, prevRemaining); assert.equal(this.pageCount, prevPageCount); backup.finish(done); }); }); }); await it ('backup works if database is modified half-way through', function(done) { var backup = db.backup('.tmp/test/backup.db'); backup.step(-1, function(err) { if (err) throw err; backup.finish(function(err) { if (err) throw err; var db2 = new sqlite3.Database('.tmp/test/backup.db', function(err) { if (err) throw err; var backup2 = db2.backup('.tmp/test/backup2.db'); backup2.step(1, function(err, completed) { if (err) throw err; assert.equal(completed, false); // Page size for the test db // should not be raised to high. db2.exec("insert into foo(txt) values('hello')", function(err) { if (err) throw err; backup2.step(-1, function(err, completed) { if (err) throw err; assert.equal(completed, true); assertRowsMatchFile(db2, '.tmp/test/backup2.db', function() { backup2.finish(function(err) { if (err) throw err; db2.close(done); }); }); }); }); }); }); }); }); }); await it ('can backup from temp to main', function(done) { db.exec("CREATE TEMP TABLE space (txt TEXT)", function(err) { if (err) throw err; db.exec("INSERT INTO space(txt) VALUES('monkey')", function(err) { if (err) throw err; var backup = db.backup('.tmp/test/backup.db', 'temp', 'main', true, function(err) { if (err) throw err; backup.step(-1, function(err) { if (err) throw err; backup.finish(function(err) { if (err) throw err; var db2 = new sqlite3.Database('.tmp/test/backup.db', function(err) { if (err) throw err; db2.get("SELECT * FROM space", function(err, row) { if (err) throw err; assert.equal(row.txt, 'monkey'); db2.close(done); }); }); }); }); }); }); }); }); await it ('can backup from main to temp', function(done) { var backup = db.backup('.tmp/test/prepare.db', 'main', 'temp', false, function(err) { if (err) throw err; backup.step(-1, function(err) { if (err) throw err; backup.finish(function(err) { if (err) throw err; assertRowsMatchDb(db, 'temp.foo', db, 'main.foo', done); }); }); }); }); await it ('cannot backup to a locked db', function(done) { var db2 = new sqlite3.Database('.tmp/test/backup.db', function(err) { db2.exec("PRAGMA locking_mode = EXCLUSIVE"); db2.exec("BEGIN EXCLUSIVE", function(err) { if (err) throw err; var backup = db.backup('.tmp/test/backup.db'); backup.step(-1, function(stepErr) { db2.close(function(err) { if (err) throw err; if (stepErr.errno == sqlite3.BUSY) { backup.finish(done); } else throw stepErr; }); }); }); }); }); await it ('fuss-free incremental backups work', function(done) { var backup = db.backup('.tmp/test/backup.db'); var timer; function makeProgress() { if (backup.idle) { backup.step(1); } if (backup.completed || backup.failed) { clearInterval(timer); assert.equal(backup.completed, true); assert.equal(backup.failed, false); done(); } } timer = setInterval(makeProgress, 2); }); await it ('setting retryErrors to empty disables automatic finishing', function(done) { var backup = db.backup('.tmp/test/backup.db'); backup.retryErrors = []; backup.step(-1, function(err) { if (err) throw err; db.close(function(err) { db = null; if (!err) throw new Error('should have an error'); assert.equal(err.errno, sqlite3.BUSY); done(); }); }); }); await it ('setting retryErrors enables automatic finishing', function(done) { var backup = db.backup('.tmp/test/backup.db'); backup.retryErrors = [sqlite3.OK]; backup.step(-1, function(err) { if (err) throw err; db.close(function(err) { if (err) throw err; db = null; done(); }); }); }); await it ('default retryErrors will retry on a locked/busy db', function(done) { var db2 = new sqlite3.Database('.tmp/test/backup.db', function(err) { db2.exec("PRAGMA locking_mode = EXCLUSIVE"); db2.exec("BEGIN EXCLUSIVE", function(err) { if (err) throw err; var backup = db.backup('.tmp/test/backup.db'); backup.step(-1, function(stepErr) { db2.close(function(err) { if (err) throw err; assert.equal(stepErr.errno, sqlite3.BUSY); assert.equal(backup.completed, false); assert.equal(backup.failed, false); backup.step(-1, function(err) { if (err) throw err; assert.equal(backup.completed, true); assert.equal(backup.failed, false); done(); }); }); }); }); }); }); }); }()); /* file https://github.com/mapbox/node-sqlite3/blob/v5.0.2/test/blob.test.js */ await (async function() { console.error("\ntest-old.js - blob.test.js"); var sqlite3 = require('.'), fs = require('fs'), assert = require('assert'), Buffer = require('buffer').Buffer; // lots of elmo // hack-test var elmo = Buffer.from([1, 2, 3, 4]); await describe('blob', async function() { var db; await before(function(done) { db = new sqlite3.Database(':memory:'); db.run("CREATE TABLE elmos (id INT, image BLOB)", done); }); var total = 10; var inserted = 0; var retrieved = 0; await it('should insert blobs', function(done) { for (var i = 0; i < total; i++) { db.run('INSERT INTO elmos (id, image) VALUES (?, ?)', i, elmo, function(err) { if (err) throw err; inserted++; }); } db.wait(function() { assert.equal(inserted, total); done(); }); }); await it('should retrieve the blobs', function(done) { db.all('SELECT id, image FROM elmos ORDER BY id', function(err, rows) { if (err) throw err; for (var i = 0; i < rows.length; i++) { assert.ok(Buffer.isBuffer(rows[i].image)); assert.ok(elmo.length, rows[i].image); for (var j = 0; j < elmo.length; j++) { if (elmo[j] !== rows[i].image[j]) { assert.ok(false, "Wrong byte"); } } retrieved++; } assert.equal(retrieved, total); done(); }); }); }); }()); /* file https://github.com/mapbox/node-sqlite3/blob/v5.0.2/test/cache.test.js */ await (async function() { console.error("\ntest-old.js - cache.test.js"); var sqlite3 = require('.'); var assert = require('assert'); // var helper = require('./support/helper'); await describe('cache', async function() { await before(function() { helper.ensureExists('.tmp/test'); }); await it('should cache Database objects while opening', function(done) { var filename = '.tmp/test/test_cache.db'; helper.deleteFile(filename); var opened1 = false, opened2 = false; var db1 = new sqlite3.cached.Database(filename, function(err) { if (err) throw err; opened1 = true; if (opened1 && opened2) done(); }); var db2 = new sqlite3.cached.Database(filename, function(err) { if (err) throw err; opened2 = true; if (opened1 && opened2) done(); }); assert.equal(db1, db2); }); await it('should cache Database objects after they are open', function(done) { var filename = '.tmp/test/test_cache2.db'; helper.deleteFile(filename); var db1, db2; db1 = new sqlite3.cached.Database(filename, function(err) { if (err) throw err; process.nextTick(function() { db2 = new sqlite3.cached.Database(filename, function(err) { done(); }); assert.equal(db1, db2); }); }); }); }); }()); /* file https://github.com/mapbox/node-sqlite3/blob/v5.0.2/test/constants.test.js */ await (async function() { console.error("\ntest-old.js - constants.test.js"); var sqlite3 = require('.'); var assert = require('assert'); await describe('constants', async function() { await it('should have the right OPEN_* flags', function() { assert.ok(sqlite3.OPEN_READONLY === 1); assert.ok(sqlite3.OPEN_READWRITE === 2); assert.ok(sqlite3.OPEN_CREATE === 4); assert.ok(sqlite3.OPEN_URI === 0x00000040); assert.ok(sqlite3.OPEN_FULLMUTEX === 0x00010000); assert.ok(sqlite3.OPEN_SHAREDCACHE === 0x00020000); assert.ok(sqlite3.OPEN_PRIVATECACHE === 0x00040000); }); await it('should have the right error flags', function() { assert.ok(sqlite3.OK === 0); assert.ok(sqlite3.ERROR === 1); assert.ok(sqlite3.INTERNAL === 2); assert.ok(sqlite3.PERM === 3); assert.ok(sqlite3.ABORT === 4); assert.ok(sqlite3.BUSY === 5); assert.ok(sqlite3.LOCKED === 6); assert.ok(sqlite3.NOMEM === 7); assert.ok(sqlite3.READONLY === 8); assert.ok(sqlite3.INTERRUPT === 9); assert.ok(sqlite3.IOERR === 10); assert.ok(sqlite3.CORRUPT === 11); assert.ok(sqlite3.NOTFOUND === 12); assert.ok(sqlite3.FULL === 13); assert.ok(sqlite3.CANTOPEN === 14); assert.ok(sqlite3.PROTOCOL === 15); assert.ok(sqlite3.EMPTY === 16); assert.ok(sqlite3.SCHEMA === 17); assert.ok(sqlite3.TOOBIG === 18); assert.ok(sqlite3.CONSTRAINT === 19); assert.ok(sqlite3.MISMATCH === 20); assert.ok(sqlite3.MISUSE === 21); assert.ok(sqlite3.NOLFS === 22); assert.ok(sqlite3.AUTH === 23); assert.ok(sqlite3.FORMAT === 24); assert.ok(sqlite3.RANGE === 25); assert.ok(sqlite3.NOTADB === 26); }); }); }()); /* file https://github.com/mapbox/node-sqlite3/blob/v5.0.2/test/database_fail.test.js */ await (async function() { console.error("\ntest-old.js - database_fail.test.js"); var sqlite3 = require('.'); var assert = require('assert'); await describe('error handling', async function() { var db; await before(function(done) { db = new sqlite3.Database(':memory:', done); }); await it('throw when calling Database() without new', function() { assert.throws(function() { sqlite3.Database(':memory:'); }, (/Class constructors cannot be invoked without 'new'/)); assert.throws(function() { sqlite3.Statement(); }, (/Class constructors cannot be invoked without 'new'/)); }); await it('should error when calling Database#get on a missing table', function(done) { db.get('SELECT id, txt FROM foo', function(err, row) { if (err) { assert.equal(err.message, 'SQLITE_ERROR: no such table: foo'); assert.equal(err.errno, sqlite3.ERROR); assert.equal(err.code, 'SQLITE_ERROR'); done(); } else { done(new Error('Completed query without error, but expected error')); } }); }); await it('Database#all prepare fail', function(done) { db.all('SELECT id, txt FROM foo', function(err, row) { if (err) { assert.equal(err.message, 'SQLITE_ERROR: no such table: foo'); assert.equal(err.errno, sqlite3.ERROR); assert.equal(err.code, 'SQLITE_ERROR'); done(); } else { done(new Error('Completed query without error, but expected error')); } }); }); await it('Database#run prepare fail', function(done) { db.run('SELECT id, txt FROM foo', function(err, row) { if (err) { assert.equal(err.message, 'SQLITE_ERROR: no such table: foo'); assert.equal(err.errno, sqlite3.ERROR); assert.equal(err.code, 'SQLITE_ERROR'); done(); } else { done(new Error('Completed query without error, but expected error')); } }); }); await it('Database#each prepare fail', function(done) { db.each('SELECT id, txt FROM foo', function(err, row) { assert.ok(false, "this should not be called"); }, function(err, num) { if (err) { assert.equal(err.message, 'SQLITE_ERROR: no such table: foo'); assert.equal(err.errno, sqlite3.ERROR); assert.equal(err.code, 'SQLITE_ERROR'); done(); } else { done(new Error('Completed query without error, but expected error')); } }); }); await it('Database#each prepare fail without completion handler', function(done) { db.each('SELECT id, txt FROM foo', function(err, row) { if (err) { assert.equal(err.message, 'SQLITE_ERROR: no such table: foo'); assert.equal(err.errno, sqlite3.ERROR); assert.equal(err.code, 'SQLITE_ERROR'); done(); } else { done(new Error('Completed query without error, but expected error')); } }); }); await it('Database#get prepare fail with param binding', function(done) { db.get('SELECT id, txt FROM foo WHERE id = ?', 1, function(err, row) { if (err) { assert.equal(err.message, 'SQLITE_ERROR: no such table: foo'); assert.equal(err.errno, sqlite3.ERROR); assert.equal(err.code, 'SQLITE_ERROR'); done(); } else { done(new Error('Completed query without error, but expected error')); } }); }); await it('Database#all prepare fail with param binding', function(done) { db.all('SELECT id, txt FROM foo WHERE id = ?', 1, function(err, row) { if (err) { assert.equal(err.message, 'SQLITE_ERROR: no such table: foo'); assert.equal(err.errno, sqlite3.ERROR); assert.equal(err.code, 'SQLITE_ERROR'); done(); } else { done(new Error('Completed query without error, but expected error')); } }); }); await it('Database#run prepare fail with param binding', function(done) { db.run('SELECT id, txt FROM foo WHERE id = ?', 1, function(err, row) { if (err) { assert.equal(err.message, 'SQLITE_ERROR: no such table: foo'); assert.equal(err.errno, sqlite3.ERROR); assert.equal(err.code, 'SQLITE_ERROR'); done(); } else { done(new Error('Completed query without error, but expected error')); } }); }); await it('Database#each prepare fail with param binding', function(done) { db.each('SELECT id, txt FROM foo WHERE id = ?', 1, function(err, row) { assert.ok(false, "this should not be called"); }, function(err, num) { if (err) { assert.equal(err.message, 'SQLITE_ERROR: no such table: foo'); assert.equal(err.errno, sqlite3.ERROR); assert.equal(err.code, 'SQLITE_ERROR'); done(); } else { done(new Error('Completed query without error, but expected error')); } }); }); await it('Database#each prepare fail with param binding without completion handler', function(done) { db.each('SELECT id, txt FROM foo WHERE id = ?', 1, function(err, row) { if (err) { assert.equal(err.message, 'SQLITE_ERROR: no such table: foo'); assert.equal(err.errno, sqlite3.ERROR); assert.equal(err.code, 'SQLITE_ERROR'); done(); } else { done(new Error('Completed query without error, but expected error')); } }); }); }); }()); /* file https://github.com/mapbox/node-sqlite3/blob/v5.0.2/test/each.test.js */ await (async function() { console.error("\ntest-old.js - each.test.js"); var sqlite3 = require('.'); var assert = require('assert'); await describe('each', async function() { var db; await before(function(done) { // hack-test db = new sqlite3.Database('.tmp/test/big.db', sqlite3.OPEN_READONLY, done); }); await it('retrieve 100,000 rows with Statement#each', function(done) { var total = 100000; var retrieved = 0; db.each('SELECT id, txt FROM foo LIMIT 0, ?', total, function(err, row) { if (err) throw err; retrieved++; if(retrieved === total) { assert.equal(retrieved, total, "Only retrieved " + retrieved + " out of " + total + " rows."); done(); } }); }); await it('Statement#each with complete callback', function(done) { var total = 10000; var retrieved = 0; db.each('SELECT id, txt FROM foo LIMIT 0, ?', total, function(err, row) { if (err) throw err; retrieved++; }, function(err, num) { assert.equal(retrieved, num); assert.equal(retrieved, total, "Only retrieved " + retrieved + " out of " + total + " rows."); done(); }); }); }); }()); /* file https://github.com/mapbox/node-sqlite3/blob/v5.0.2/test/exec.test.js */ await (async function() { console.error("\ntest-old.js - exec.test.js"); var sqlite3 = require('.'); var assert = require('assert'); var fs = require('fs'); await describe('exec', async function() { var db; await before(function(done) { db = new sqlite3.Database(':memory:', done); }); await it('Database#exec', function(done) { // hack-test var sql = assetDict['test/support/script.sql']; db.exec(sql, done); }); await it('retrieve database structure', function(done) { db.all("SELECT type, name FROM sqlite_master ORDER BY type, name", function(err, rows) { if (err) throw err; assert.deepEqual(rows, [ { type: 'index', name: 'grid_key_lookup' }, { type: 'index', name: 'grid_utfgrid_lookup' }, { type: 'index', name: 'images_id' }, { type: 'index', name: 'keymap_lookup' }, { type: 'index', name: 'map_index' }, { type: 'index', name: 'name' }, { type: 'table', name: 'grid_key' }, { type: 'table', name: 'grid_utfgrid' }, { type: 'table', name: 'images' }, { type: 'table', name: 'keymap' }, { type: 'table', name: 'map' }, { type: 'table', name: 'metadata' }, { type: 'view', name: 'grid_data' }, { type: 'view', name: 'grids' }, { type: 'view', name: 'tiles' } ]); done(); }); }); }); }()); /* file https://github.com/mapbox/node-sqlite3/blob/v5.0.2/test/extension.test.js */ await (async function() { console.error("\ntest-old.js - extension.test.js"); var sqlite3 = require('.'); var assert = require('assert'); var exists = require('fs').existsSync || require('path').existsSync; /* // disabled because this is not a generically safe test to run on all systems var spatialite_ext = '/usr/local/lib/libspatialite.dylib'; await describe('loadExtension', async function(done) { var db; await before(function(done) { db = new sqlite3.Database(':memory:', done); }); if (exists(spatialite_ext)) { await it('libspatialite', function(done) { db.loadExtension(spatialite_ext, done); }); } else { await it('libspatialite'); } }); */ }()); /* file https://github.com/mapbox/node-sqlite3/blob/v5.0.2/test/fts-content.test.js */ await (async function() { console.error("\ntest-old.js - fts-content.test.js"); var sqlite3 = require('.'); var assert = require('assert'); await describe('fts', async function() { var db; await before(function(done) { db = new sqlite3.Database(':memory:', done); }); await it('should create a new fts4 table', function(done) { db.exec('CREATE VIRTUAL TABLE t1 USING fts4(content="", a, b, c);', done); }); }); }()); /* file https://github.com/mapbox/node-sqlite3/blob/v5.0.2/test/interrupt.test.js */ await (async function() { console.error("\ntest-old.js - interrupt.test.js"); var sqlite3 = require('.'); var assert = require('assert'); await describe('interrupt', async function() { await it('should interrupt queries', function(done) { var interrupted = false; var saved = null; var db = new sqlite3.Database(':memory:', function() { db.serialize(); var setup = 'create table t (n int);'; for (var i = 0; i < 8; i += 1) { setup += 'insert into t values (' + i + ');'; } db.exec(setup, function(err) { if (err) { return done(err); } var query = 'select last.n ' + 'from t,t,t,t,t,t,t,t,t,t,t,t,t,t,t,t,t,t,t,t,t,t,t,t,t,t as last'; db.each(query, function(err) { if (err) { saved = err; } else if (!interrupted) { interrupted = true; db.interrupt(); } }); db.close(function() { if (saved) { assert.equal(saved.message, 'SQLITE_INTERRUPT: interrupted'); assert.equal(saved.errno, sqlite3.INTERRUPT); assert.equal(saved.code, 'SQLITE_INTERRUPT'); done(); } else { done(new Error('Completed query without error, but expected error')); } }); }); }); }); await it('should throw if interrupt is called before open', function(done) { var db = new sqlite3.Database(':memory:'); assert.throws(function() { db.interrupt(); }, (/Database is not open/)); db.close(); done(); }); await it('should throw if interrupt is called after close', function(done) { var db = new sqlite3.Database(':memory:'); db.close(function() { assert.throws(function() { db.interrupt(); }, (/Database is not open/)); done(); }); }); await it('should throw if interrupt is called during close', function(done) { var db = new sqlite3.Database(':memory:', function() { db.close(); assert.throws(function() { db.interrupt(); }, (/Database is closing/)); done(); }); }); }); }()); /* file https://github.com/mapbox/node-sqlite3/blob/v5.0.2/test/issue-108.test.js */ await (async function() { console.error("\ntest-old.js - issue-108.test.js"); var sqlite3 = require('.'), assert = require('assert'); await describe('buffer', async function() { var db; // await before(function() { // }); await it('should insert blobs', function(done) { db = new sqlite3.Database(':memory:'); db.serialize(function () { db.run("CREATE TABLE lorem (info BLOB)"); var stmt = db.prepare("INSERT INTO lorem VALUES (?)"); stmt.on('error', function (err) { throw err; }); // hack-test var buff = Buffer.alloc(2); stmt.run(buff); stmt.finalize(); }); db.close(done); }); }); }()); /* file https://github.com/mapbox/node-sqlite3/blob/v5.0.2/test/json.test.js */ await (async function() { console.error("\ntest-old.js - json.test.js"); var sqlite3 = require('.'); if( process.env.NODE_SQLITE3_JSON1 === 'no' ){ await describe('json', async function() { await it( 'skips JSON tests when --sqlite=/usr (or similar) is tested', function(){} ); }); } else { await describe('json', async function() { var db; await before(function(done) { db = new sqlite3.Database(':memory:', done); }); await it('should select JSON', function(done) { db.run('SELECT json(?)', JSON.stringify({ok:true}), done); }); }); } }()); /* file https://github.com/mapbox/node-sqlite3/blob/v5.0.2/test/map.test.js */ await (async function() { console.error("\ntest-old.js - map.test.js"); var sqlite3 = require('.'); var assert = require('assert'); await describe('map', async function() { await it('test Database#map() with two columns', function(done) { var count = 10; var inserted = 0; var db = new sqlite3.Database(':memory:'); db.serialize(function() { db.run("CREATE TABLE foo (id INT, value TEXT)"); var stmt = db.prepare("INSERT INTO foo VALUES(?, ?)"); for (var i = 5; i < count; i++) { stmt.run(i, 'Value for ' + i, function(err) { if (err) throw err; inserted++; }); } stmt.finalize(); db.map("SELECT * FROM foo", function(err, map) { if (err) throw err; assert.deepEqual(map, { 5: 'Value for 5', 6: 'Value for 6', 7: 'Value for 7', 8: 'Value for 8', 9: 'Value for 9' }); assert.equal(inserted, 5); done(); }); }); }); await it('test Database#map() with three columns', function(done) { var db = new sqlite3.Database(':memory:'); var count = 10; var inserted = 0; db.serialize(function() { db.run("CREATE TABLE foo (id INT, value TEXT, other TEXT)"); var stmt = db.prepare("INSERT INTO foo VALUES(?, ?, ?)"); for (var i = 5; i < count; i++) { stmt.run(i, 'Value for ' + i, null, function(err) { if (err) throw err; inserted++; }); } stmt.finalize(); db.map("SELECT * FROM foo", function(err, map) { if (err) throw err; assert.deepEqual(map, { 5: { id: 5, value: 'Value for 5', other: null }, 6: { id: 6, value: 'Value for 6', other: null }, 7: { id: 7, value: 'Value for 7', other: null }, 8: { id: 8, value: 'Value for 8', other: null }, 9: { id: 9, value: 'Value for 9', other: null } }); assert.equal(inserted, 5); done(); }); }); }); }); }()); /* file https://github.com/mapbox/node-sqlite3/blob/v5.0.2/test/named_columns.test.js */ await (async function() { console.error("\ntest-old.js - named_columns.test.js"); var sqlite3 = require('.'); var assert = require('assert'); await describe('named columns', async function() { var db; await before(function(done) { db = new sqlite3.Database(':memory:', done); }); await it('should create the table', function(done) { db.run("CREATE TABLE foo (txt TEXT, num INT)", done); }); await it('should insert a value', function(done) { db.run("INSERT INTO foo VALUES($text, $id)", { $id: 1, $text: "Lorem Ipsum" }, done); }); await it('should retrieve the values', function(done) { db.get("SELECT txt, num FROM foo ORDER BY num", function(err, row) { if (err) throw err; assert.equal(row.txt, "Lorem Ipsum"); assert.equal(row.num, 1); done(); }); }); await it('should be able to retrieve rowid of last inserted value', function(done) { db.get("SELECT last_insert_rowid() as last_id FROM foo", function(err, row) { if (err) throw err; assert.equal(row.last_id, 1); done(); }); }); }); }()); /* file https://github.com/mapbox/node-sqlite3/blob/v5.0.2/test/named_params.test.js */ await (async function() { console.error("\ntest-old.js - named_params.test.js"); var sqlite3 = require('.'); var assert = require('assert'); await describe('named parameters', async function() { var db; await before(function(done) { db = new sqlite3.Database(':memory:', done); }); await it('should create the table', function(done) { db.run("CREATE TABLE foo (txt TEXT, num INT)", done); }); await it('should insert a value with $ placeholders', function(done) { db.run("INSERT INTO foo VALUES($text, $id)", { $id: 1, $text: "Lorem Ipsum" }, done); }); await it('should insert a value with : placeholders', function(done) { db.run("INSERT INTO foo VALUES(:text, :id)", { ':id': 2, ':text': "Dolor Sit Amet" }, done); }); await it('should insert a value with @ placeholders', function(done) { db.run("INSERT INTO foo VALUES(@txt, @id)", { "@id": 3, "@txt": "Consectetur Adipiscing Elit" }, done); }); await it('should insert a value with @ placeholders using an array', function(done) { db.run("INSERT INTO foo VALUES(@txt, @id)", [ 'Sed Do Eiusmod', 4 ], done); }); await it('should insert a value with indexed placeholders', function(done) { db.run("INSERT INTO foo VALUES(?2, ?4)", [ null, 'Tempor Incididunt', null, 5 ], done); }); await it('should insert a value with autoindexed placeholders', function(done) { db.run("INSERT INTO foo VALUES(?, ?)", { 2: 6, 1: "Ut Labore Et Dolore" }, done); }); await it('should retrieve all inserted values', function(done) { db.all("SELECT txt, num FROM foo ORDER BY num", function(err, rows) { if (err) throw err; assert.equal(rows[0].txt, "Lorem Ipsum"); assert.equal(rows[0].num, 1); assert.equal(rows[1].txt, "Dolor Sit Amet"); assert.equal(rows[1].num, 2); assert.equal(rows[2].txt, "Consectetur Adipiscing Elit"); assert.equal(rows[2].num, 3); assert.equal(rows[3].txt, "Sed Do Eiusmod"); assert.equal(rows[3].num, 4); assert.equal(rows[4].txt, "Tempor Incididunt"); assert.equal(rows[4].num, 5); assert.equal(rows[5].txt, "Ut Labore Et Dolore"); assert.equal(rows[5].num, 6); done(); }); }); }); }()); /* file https://github.com/mapbox/node-sqlite3/blob/v5.0.2/test/null_error.test.js */ await (async function() { console.error("\ntest-old.js - null_error.test.js"); var sqlite3 = require('.'); var assert = require('assert'); // var helper = require('./support/helper'); await describe('null error', async function() { var filename = '.tmp/test/test_sqlite_ok_error.db'; var db; await before(function(done) { helper.ensureExists('.tmp/test'); helper.deleteFile(filename); db = new sqlite3.Database(filename, done); }); await it('should create a table', function(done) { db.run("CREATE TABLE febp_data (leacode TEXT, leaname TEXT, state TEXT, postcode TEXT, fips TEXT, titleistim TEXT, ideastim TEXT, ideapool TEXT, ideapoolname TEXT, localebasis TEXT, localetype2 TEXT, version TEXT, leacount_2006 TEXT, ppexpend_2005 TEXT, ppexpend_2006 TEXT, ppexpend_2007 TEXT, ppexpend_2008 TEXT, ppexpendrank_2006 TEXT, ppexpendrank_2007 TEXT, ppexpendrank_2008 TEXT, rankppexpend_2005 TEXT, opbud_2004 TEXT, opbud_2006 TEXT, opbud_2007 TEXT, opbud_2008 TEXT, titlei_2004 TEXT, titlei_2006 TEXT, titlei_2007 TEXT, titlei_2008 TEXT, titlei_2009 TEXT, titlei_2010 TEXT, idea_2004 TEXT, idea_2005 TEXT, idea_2006 TEXT, idea_2007 TEXT, idea_2008 TEXT, idea_2009 TEXT, ideaest_2010 TEXT, impact_2007 TEXT, impact_2008 TEXT, impact_2009 TEXT, impact_2010 TEXT, fedrev_2006 TEXT, fedrev_2007 TEXT, fedrev_2008 TEXT, schonut_2006 TEXT, schonut_2007 TEXT, schomeal_2006 TEXT, schomeal_2007 TEXT, schoco_2006 TEXT, schocom_2007 TEXT, medicaid_2006 TEXT, medicaid_2007 TEXT, medicaid_2008 TEXT, cenpov_2004 TEXT, cenpov_2007 TEXT, cenpov_2008 TEXT, rankcenpov_2004 TEXT, rankcenpov_2007 TEXT, rankcenpov_2008 TEXT, enroll_2006 TEXT, enroll_2007 TEXT, enroll_2008 TEXT, white_2006 TEXT, white_2007 TEXT, white_2008 TEXT, afam_2006 TEXT, afam_2007 TEXT, afam_2008 TEXT, amin_2006 TEXT, amin_2007 TEXT, amin_2008 TEXT, asian_2006 TEXT, asian_2007 TEXT, asian_2008 TEXT, hisp_2006 TEXT, hisp_2007 TEXT, hisp_2008 TEXT, frpl_2006 TEXT, frpl_2007 TEXT, frpl_2008 TEXT, ell_2006 TEXT, ell_2007 TEXT, ell_2008 TEXT, sped_2006 TEXT, sped_2007 TEXT, sped_2008 TEXT, state4read_2005 TEXT, state4read_2006 TEXT, state4read_2007 TEXT, state4read_2008 TEXT, state4read_2009 TEXT, state4math_2005 TEXT, state4math_2006 TEXT, state4math_2007 TEXT, state4math_2008 TEXT, state4math_2009 TEXT, minor_2007 TEXT, minor_2008 TEXT, state8math_2006 TEXT, state8math_2007 TEXT, state8math_2008 TEXT, state8math_2009 TEXT, state8read_2006 TEXT, state8read_2007 TEXT, state8read_2008 TEXT, state8read_2009 TEXT, statehsmath_2006 TEXT, statehsmath_2007 TEXT, statehsmath_2008 TEXT, statehsmath_2009 TEXT, statehsread_2006 TEXT, statehsread_2007 TEXT, statehsread_2008 TEXT, statehsread_2009 TEXT)", done); }); await it('should insert rows with lots of null values', function(done) { var stmt = db.prepare('INSERT INTO febp_data VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)', function(err) { if (err) throw err; for (var i = 0; i < 100; i++) { stmt.run([ '100005', 'Albertville City School District', 'ALABAMA', 'AL', '1', '856031', '753000', 'NULL', 'NULL', '6-Small Town', 'Town', 21, '130', '6624', '7140', '8731', '8520', '102', '88', '100', '94', '23352000', '27280000', '30106000', '33028000', '768478', '845886', '782696', '1096819', '1279663', '1168521', '561522', '657649', '684366', '687531', '710543', '727276', '726647', 'N/A', 'N/A', 'N/A', 'N/A', '986', '977', '1006', '1080250', '1202325', '1009962', '1109310', '70287', '93015', '14693.56', '13634.58', 'N/A', '0.230', '0.301', '0.268882175', '73', '26', '29', '3718', '3747', '3790', '2663', '2615', '2575', '75', '82', '89', '3', '2', '6', '11', '9', '8', '955', '1028', '1102', '1991', '2061', '2146', '649', '729', '770', '443', '278', '267', '0.860', '0.86', '0.8474', '0.84', '0.8235', '0.810', '0.84', '0.7729', '0.75', '0.7843', '1121', '1205', '0.74', '0.6862', '0.72', '0.7317', '0.78', '0.7766', '0.79', '0.7387', '0.84', '0.9255', '0.86', '0.9302', '0.88', '0.9308', '0.84', '0.8605' ]); } stmt.finalize(function(err) { if (err) throw err; done(); }); }); }); await it('should have created the database', function() { assert.fileExists(filename); }); after(function() { helper.deleteFile(filename); }); }); }()); /* file https://github.com/mapbox/node-sqlite3/blob/v5.0.2/test/open_close.test.js */ await (async function() { console.error("\ntest-old.js - open_close.test.js"); var sqlite3 = require('.'); var assert = require('assert'); var fs = require('fs'); // var helper = require('./support/helper'); await describe('open/close', async function() { await before(function() { helper.ensureExists('.tmp/test'); }); await describe('open and close non-existant database', async function() { await before(function() { helper.deleteFile('.tmp/test/test_create.db'); }); var db; await it('should open the database', function(done) { db = new sqlite3.Database('.tmp/test/test_create.db', done); }); await it('should close the database', function(done) { db.close(done); }); await it('should have created the file', function() { assert.fileExists('.tmp/test/test_create.db'); }); after(function() { helper.deleteFile('.tmp/test/test_create.db'); }); }); await describe('open and close non-existant shared database', async function() { await before(function() { helper.deleteFile('.tmp/test/test_create_shared.db'); }); var db; await it('should open the database', function(done) { db = new sqlite3.Database('file:./.tmp/test/test_create_shared.db', sqlite3.OPEN_URI | sqlite3.OPEN_SHAREDCACHE | sqlite3.OPEN_READWRITE | sqlite3.OPEN_CREATE, done); }); await it('should close the database', function(done) { db.close(done); }); await it('should have created the file', function() { assert.fileExists('.tmp/test/test_create_shared.db'); }); after(function() { helper.deleteFile('.tmp/test/test_create_shared.db'); }); }); // hack-test describe('open and close shared memory database', async function() { var db1; var db2; await it('should open the first database', function(done) { db1 = new sqlite3.Database('file:./.tmp/test/test_memory.db?mode=memory', sqlite3.OPEN_URI | sqlite3.OPEN_SHAREDCACHE | sqlite3.OPEN_READWRITE | sqlite3.OPEN_CREATE, done); }); await it('should open the second database', function(done) { db2 = new sqlite3.Database('file:./.tmp/test/test_memory.db?mode=memory', sqlite3.OPEN_URI | sqlite3.OPEN_SHAREDCACHE | sqlite3.OPEN_READWRITE | sqlite3.OPEN_CREATE, done); }); await it('first database should set the user_version', function(done) { db1.exec('PRAGMA user_version=42', done); }); await it('second database should get the user_version', function(done) { db2.get('PRAGMA user_version', function(err, row) { if (err) throw err; assert.equal(row.user_version, 42); done(); }); }); await it('should close the first database', function(done) { db1.close(done); }); await it('should close the second database', function(done) { db2.close(done); }); }); await it('should not be unable to open an inaccessible database', function(done) { // NOTE: test assumes that the user is not allowed to create new files // in /usr/bin. var db = new sqlite3.Database('/.tmp/test/directory-does-not-exist/test.db', function(err) { if (err && err.errno === sqlite3.CANTOPEN) { done(); } else if (err) { done(err); } else { done('Opened database that should be inaccessible'); } }); }); await describe('creating database without create flag', async function() { await before(function() { helper.deleteFile('.tmp/test/test_readonly.db'); }); await it('should fail to open the database', function(done) { new sqlite3.Database('tmp/test_readonly.db', sqlite3.OPEN_READONLY, function(err) { if (err && err.errno === sqlite3.CANTOPEN) { done(); } else if (err) { done(err); } else { done('Created database without create flag'); } }); }); await it('should not have created the file', function() { assert.fileDoesNotExist('.tmp/test/test_readonly.db'); }); after(function() { helper.deleteFile('.tmp/test/test_readonly.db'); }); }); await describe('open and close memory database queuing', async function() { var db; await it('should open the database', function(done) { db = new sqlite3.Database(':memory:', done); }); await it('should close the database', function(done) { db.close(done); }); await it('shouldn\'t close the database again', function(done) { db.close(function(err) { assert.ok(err, 'No error object received on second close'); assert.ok(err.errno === sqlite3.MISUSE); done(); }); }); }); await describe('closing with unfinalized statements', async function(done) { var completed = false; var completedSecond = false; var closed = false; var db; await before(function() { db = new sqlite3.Database(':memory:', done); }); await it('should create a table', function(done) { db.run("CREATE TABLE foo (id INT, num INT)", done); }); var stmt; await it('should prepare/run a statement', function(done) { stmt = db.prepare('INSERT INTO foo VALUES (?, ?)'); stmt.run(1, 2, done); }); await it('should fail to close the database', function(done) { db.close(function(err) { assert.ok(err.message, "SQLITE_BUSY: unable to close due to unfinalised statements"); done(); }); }); await it('should succeed to close the database after finalizing', function(done) { stmt.run(3, 4, function() { stmt.finalize(); db.close(done); }); }); }); }); }()); /* file https://github.com/mapbox/node-sqlite3/blob/v5.0.2/test/other_objects.test.js */ await (async function() { console.error("\ntest-old.js - other_objects.test.js"); var sqlite3 = require('.'); var assert = require('assert'); await describe('data types', async function() { var db; await before(function(done) { db = new sqlite3.Database(':memory:'); db.run("CREATE TABLE txt_table (txt TEXT)"); db.run("CREATE TABLE int_table (int INTEGER)"); db.run("CREATE TABLE flt_table (flt FLOAT)"); db.wait(done); }); beforeEach(function(done) { db.exec('DELETE FROM txt_table; DELETE FROM int_table; DELETE FROM flt_table;', done); }); await it('should serialize Date()', function(done) { var date = new Date(); db.run("INSERT INTO int_table VALUES(?)", date, function (err) { if (err) throw err; db.get("SELECT int FROM int_table", function(err, row) { if (err) throw err; assert.equal(row.int, +date); done(); }); }); }); await it('should serialize RegExp()', function(done) { var regexp = /^f\noo/; db.run("INSERT INTO txt_table VALUES(?)", regexp, function (err) { if (err) throw err; db.get("SELECT txt FROM txt_table", function(err, row) { if (err) throw err; assert.equal(row.txt, String(regexp)); done(); }); }); }); // hack-test var list = [ 4294967296.249, Math.PI, 3924729304762836.5, new Date().valueOf(), 912667.394828365, 2.3948728634826374e+83, 9.293476892934982e+300, Infinity, -9.293476892934982e+300, -2.3948728634826374e+83, -Infinity // hack-test ]; while (true) { var flt = list.shift(); if (!flt) { break; } await it('should serialize float ' + flt, function(done) { db.run("INSERT INTO flt_table VALUES(?)", flt, function (err) { if (err) throw err; db.get("SELECT flt FROM flt_table", function(err, row) { if (err) throw err; assert.equal(row.flt, flt); done(); }); }); }); // hack-test } var list = [ 4294967299, 3924729304762836, new Date().valueOf(), 2.3948728634826374e+83, 9.293476892934982e+300, Infinity, -9.293476892934982e+300, -2.3948728634826374e+83, -Infinity // hack-test ]; while (true) { var integer = list.shift(); if (!integer) { break; } await it('should serialize integer ' + integer, function(done) { db.run("INSERT INTO int_table VALUES(?)", integer, function (err) { if (err) throw err; db.get("SELECT int AS integer FROM int_table", function(err, row) { if (err) throw err; assert.equal(row.integer, integer); done(); }); }); }); // hack-test } }); }()); /* file https://github.com/mapbox/node-sqlite3/blob/v5.0.2/test/parallel_insert.test.js */ await (async function() { console.error("\ntest-old.js - parallel_insert.test.js"); var sqlite3 = require('.'); var assert = require('assert'); // var helper = require('./support/helper'); await describe('parallel', async function() { var db; await before(function(done) { helper.deleteFile('.tmp/test/test_parallel_inserts.db'); helper.ensureExists('.tmp/test'); db = new sqlite3.Database('.tmp/test/test_parallel_inserts.db', done); }); var columns = []; for (var i = 0; i < 128; i++) { columns.push('id' + i); } await it('should create the table', function(done) { db.run("CREATE TABLE foo (" + columns + ")", done); }); await it('should insert in parallel', function(done) { for (var i = 0; i < 1000; i++) { for (var values = [], j = 0; j < columns.length; j++) { values.push(i * j); } db.run("INSERT INTO foo VALUES (" + values + ")"); } db.wait(done); }); await it('should close the database', function(done) { db.close(done); }); await it('should verify that the database exists', function() { assert.fileExists('.tmp/test/test_parallel_inserts.db'); }); after(function() { helper.deleteFile('.tmp/test/test_parallel_inserts.db'); }); }); }()); /* file https://github.com/mapbox/node-sqlite3/blob/v5.0.2/test/prepare.test.js */ await (async function() { console.error("\ntest-old.js - prepare.test.js"); var sqlite3 = require('.'); var assert = require('assert'); await describe('prepare', async function() { await describe('invalid SQL', async function() { var db; await before(function(done) { db = new sqlite3.Database(':memory:', done); }); var stmt; await it('should fail preparing a statement with invalid SQL', function(done) { stmt = db.prepare('CRATE TALE foo text bar)', function(err, statement) { if (err && err.errno == sqlite3.ERROR && err.message === 'SQLITE_ERROR: near "CRATE": syntax error') { done(); } else throw err; }); }); after(function(done) { db.close(done); }); }); await describe('simple prepared statement', async function() { var db; await before(function(done) { db = new sqlite3.Database(':memory:', done); }); await it('should prepare, run and finalize the statement', function(done) { db.prepare("CREATE TABLE foo (text bar)") .run() .finalize(done); }); after(function(done) { db.close(done); }); }); await describe('inserting and retrieving rows', async function() { var db; await before(function(done) { db = new sqlite3.Database(':memory:', done); }); var inserted = 0; var retrieved = 0; // We insert and retrieve that many rows. var count = 1000; await it('should create the table', function(done) { db.prepare("CREATE TABLE foo (txt text, num int, flt float, blb blob)").run().finalize(done); }); await it('should insert ' + count + ' rows', function(done) { for (var i = 0; i < count; i++) { db.prepare("INSERT INTO foo VALUES(?, ?, ?, ?)").run( 'String ' + i, i, i * Math.PI, // null (SQLite sets this implicitly) function(err) { if (err) throw err; inserted++; } ).finalize(function(err) { if (err) throw err; if (inserted == count) done(); }); } }); await it('should prepare a statement and run it ' + (count + 5) + ' times', function(done) { var stmt = db.prepare("SELECT txt, num, flt, blb FROM foo ORDER BY num", function(err) { if (err) throw err; assert.equal(stmt.sql, 'SELECT txt, num, flt, blb FROM foo ORDER BY num'); }); for (var i = 0; i < count + 5; i++) (function(i) { stmt.get(function(err, row) { if (err) throw err; if (retrieved >= 1000) { assert.equal(row, undefined); } else { assert.equal(row.txt, 'String ' + i); assert.equal(row.num, i); assert.equal(row.flt, i * Math.PI); assert.equal(row.blb, null); } retrieved++; }); })(i); stmt.finalize(done); }); await it('should have retrieved ' + (count + 5) + ' rows', function() { assert.equal(count + 5, retrieved, "Didn't retrieve all rows"); }); after(function(done) { db.close(done); }); }); await describe('inserting with accidental undefined', async function() { var db; await before(function(done) { db = new sqlite3.Database(':memory:', done); }); var inserted = 0; var retrieved = 0; await it('should create the table', function(done) { db.prepare("CREATE TABLE foo (num int)").run().finalize(done); }); await it('should insert two rows', function(done) { db.prepare('INSERT INTO foo VALUES(4)').run(function(err) { if (err) throw err; inserted++; }).run(undefined, function (err) { // The second time we pass undefined as a parameter. This is // a mistake, but it should either throw an error or be ignored, // not silently fail to run the statement. if (err) throw err; inserted++; }).finalize(function(err) { if (err) throw err; if (inserted == 2) done(); }); }); await it('should retrieve the data', function(done) { var stmt = db.prepare("SELECT num FROM foo", function(err) { if (err) throw err; }); for (var i = 0; i < 2; i++) (function(i) { stmt.get(function(err, row) { if (err) throw err; assert(row); assert.equal(row.num, 4); retrieved++; }); })(i); stmt.finalize(done); }); await it('should have retrieved two rows', function() { assert.equal(2, retrieved, "Didn't retrieve all rows"); }); after(function(done) { db.close(done); }); }); await describe('retrieving reset() function', async function() { var db; await before(function(done) { db = new sqlite3.Database('.tmp/test/prepare.db', sqlite3.OPEN_READONLY, done); }); var retrieved = 0; await it('should retrieve the same row over and over again', function(done) { var stmt = db.prepare("SELECT txt, num, flt, blb FROM foo ORDER BY num"); for (var i = 0; i < 10; i++) { stmt.reset(); stmt.get(function(err, row) { if (err) throw err; assert.equal(row.txt, 'String 0'); assert.equal(row.num, 0); assert.equal(row.flt, 0.0); assert.equal(row.blb, null); retrieved++; }); } stmt.finalize(done); }); await it('should have retrieved 10 rows', function() { assert.equal(10, retrieved, "Didn't retrieve all rows"); }); after(function(done) { db.close(done); }); }); await describe('multiple get() parameter binding', async function() { var db; await before(function(done) { db = new sqlite3.Database('.tmp/test/prepare.db', sqlite3.OPEN_READONLY, done); }); var retrieved = 0; await it('should retrieve particular rows', function(done) { var stmt = db.prepare("SELECT txt, num, flt, blb FROM foo WHERE num = ?"); for (var i = 0; i < 10; i++) (function(i) { stmt.get(i * 10 + 1, function(err, row) { if (err) throw err; var val = i * 10 + 1; assert.equal(row.txt, 'String ' + val); assert.equal(row.num, val); // hack-test assert.equal(Math.floor(row.flt * 10**10), Math.floor(val * Math.PI * 10**10)); assert.equal(row.blb, null); retrieved++; }); })(i); stmt.finalize(done); }); await it('should have retrieved 10 rows', function() { assert.equal(10, retrieved, "Didn't retrieve all rows"); }); after(function(done) { db.close(done); }); }); await describe('prepare() parameter binding', async function() { var db; await before(function(done) { db = new sqlite3.Database('.tmp/test/prepare.db', sqlite3.OPEN_READONLY, done); }); var retrieved = 0; await it('should retrieve particular rows', function(done) { db.prepare("SELECT txt, num, flt, blb FROM foo WHERE num = ? AND txt = ?", 10, 'String 10') .get(function(err, row) { if (err) throw err; assert.equal(row.txt, 'String 10'); assert.equal(row.num, 10); // hack-test assert.equal(Math.floor(row.flt * 10**10), Math.floor(10 * Math.PI * 10**10)); assert.equal(row.blb, null); retrieved++; }) .finalize(done); }); await it('should have retrieved 1 row', function() { assert.equal(1, retrieved, "Didn't retrieve all rows"); }); after(function(done) { db.close(done); }); }); await describe('all()', async function() { var db; await before(function(done) { db = new sqlite3.Database('.tmp/test/prepare.db', sqlite3.OPEN_READONLY, done); }); var retrieved = 0; var count = 1000; await it('should retrieve particular rows', function(done) { db.prepare("SELECT txt, num, flt, blb FROM foo WHERE num < ? ORDER BY num", count) .all(function(err, rows) { if (err) throw err; for (var i = 0; i < rows.length; i++) { assert.equal(rows[i].txt, 'String ' + i); assert.equal(rows[i].num, i); // hack-test assert.equal(Math.floor(rows[i].flt * 10**10), Math.floor(i * Math.PI * 10**10)); assert.equal(rows[i].blb, null); retrieved++; } }) .finalize(done); }); await it('should have retrieved all rows', function() { assert.equal(count, retrieved, "Didn't retrieve all rows"); }); after(function(done) { db.close(done); }); }); await describe('all()', async function() { var db; await before(function(done) { db = new sqlite3.Database('.tmp/test/prepare.db', sqlite3.OPEN_READONLY, done); }); await it('should retrieve particular rows', function(done) { db.prepare("SELECT txt, num, flt, blb FROM foo WHERE num > 5000") .all(function(err, rows) { if (err) throw err; assert.ok(rows.length === 0); }) .finalize(done); }); after(function(done) { db.close(done); }); }); await describe('high concurrency', async function() { var db; await before(function(done) { db = new sqlite3.Database(':memory:', done); }); function randomString() { var str = ''; for (var i = Math.random() * 300; i > 0; i--) { str += String.fromCharCode(Math.floor(Math.random() * 256)); } return str; } // Generate random data. var data = []; var length = Math.floor(Math.random() * 1000) + 200; for (var i = 0; i < length; i++) { data.push([ randomString(), i, i * Math.random(), null ]); } var inserted = 0; var retrieved = 0; await it('should create the table', function(done) { db.prepare("CREATE TABLE foo (txt text, num int, flt float, blb blob)").run().finalize(done); }); await it('should insert all values', function(done) { for (var i = 0; i < data.length; i++) { var stmt = db.prepare("INSERT INTO foo VALUES(?, ?, ?, ?)"); stmt.run(data[i][0], data[i][1], data[i][2], data[i][3], function(err) { if (err) throw err; inserted++; }).finalize(function(err) { if (err) throw err; if (inserted == data.length) done(); }); } }); await it('should retrieve all values', function(done) { db.prepare("SELECT txt, num, flt, blb FROM foo") .all(function(err, rows) { if (err) throw err; for (var i = 0; i < rows.length; i++) { assert.ok(data[rows[i].num] !== true); assert.equal(rows[i].txt, data[rows[i].num][0]); assert.equal(rows[i].num, data[rows[i].num][1]); assert.equal(rows[i].flt, data[rows[i].num][2]); assert.equal(rows[i].blb, data[rows[i].num][3]); // Mark the data row as already retrieved. data[rows[i].num] = true; retrieved++; } assert.equal(retrieved, data.length); assert.equal(retrieved, inserted); }) .finalize(done); }); after(function(done) { db.close(done); }); }); await describe('test Database#get()', async function() { var db; await before(function(done) { db = new sqlite3.Database('.tmp/test/prepare.db', sqlite3.OPEN_READONLY, done); }); var retrieved = 0; await it('should get a row', function(done) { db.get("SELECT txt, num, flt, blb FROM foo WHERE num = ? AND txt = ?", 10, 'String 10', function(err, row) { if (err) throw err; assert.equal(row.txt, 'String 10'); assert.equal(row.num, 10); // hack-test assert.equal(Math.floor(row.flt * 10**10), Math.floor(10 * Math.PI * 10**10)); assert.equal(row.blb, null); retrieved++; done(); }); }); await it('should have retrieved all rows', function() { assert.equal(1, retrieved, "Didn't retrieve all rows"); }); after(function(done) { db.close(done); }); }); await describe('Database#run() and Database#all()', async function() { var db; await before(function(done) { db = new sqlite3.Database(':memory:', done); }); var inserted = 0; var retrieved = 0; // We insert and retrieve that many rows. var count = 1000; await it('should create the table', function(done) { db.run("CREATE TABLE foo (txt text, num int, flt float, blb blob)", done); }); await it('should insert ' + count + ' rows', function(done) { for (var i = 0; i < count; i++) { db.run("INSERT INTO foo VALUES(?, ?, ?, ?)", 'String ' + i, i, i * Math.PI, // null (SQLite sets this implicitly) function(err) { if (err) throw err; inserted++; if (inserted == count) done(); } ); } }); await it('should retrieve all rows', function(done) { db.all("SELECT txt, num, flt, blb FROM foo ORDER BY num", function(err, rows) { if (err) throw err; for (var i = 0; i < rows.length; i++) { assert.equal(rows[i].txt, 'String ' + i); assert.equal(rows[i].num, i); assert.equal(rows[i].flt, i * Math.PI); assert.equal(rows[i].blb, null); retrieved++; } assert.equal(retrieved, count); assert.equal(retrieved, inserted); done(); }); }); after(function(done) { db.close(done); }); }); }); }()); /* file https://github.com/mapbox/node-sqlite3/blob/v5.0.2/test/profile.test.js */ await (async function() { console.error("\ntest-old.js - profile.test.js"); var sqlite3 = require('.'); var assert = require('assert'); await describe('profiling', async function() { var create = false; var select = false; var db; await before(function(done) { db = new sqlite3.Database(':memory:', done); db.on('profile', function(sql, nsecs) { assert.ok(typeof nsecs === "number"); if (sql.match(/^SELECT/)) { assert.ok(!select); assert.equal(sql, "SELECT * FROM foo"); console.log('profile select'); select = true; } else if (sql.match(/^CREATE/)) { assert.ok(!create); assert.equal(sql, "CREATE TABLE foo (id int)"); create = true; } else { assert.ok(false); } }); }); await it('should profile a create table', function(done) { assert.ok(!create); db.run("CREATE TABLE foo (id int)", function(err) { if (err) throw err; setImmediate(function() { assert.ok(create); done(); }); }); }); await it('should profile a select', function(done) { assert.ok(!select); db.run("SELECT * FROM foo", function(err) { if (err) throw err; setImmediate(function() { assert.ok(select); done(); }, 0); }); }); after(function(done) { db.close(done); }); }); }()); /* file https://github.com/mapbox/node-sqlite3/blob/v5.0.2/test/rerun.test.js */ await (async function() { console.error("\ntest-old.js - rerun.test.js"); var sqlite3 = require('.'); var assert = require('assert'); await describe('rerunning statements', async function() { var db; await before(function(done) { db = new sqlite3.Database(':memory:', done); }); var count = 10; var inserted = 0; var retrieved = 0; await it('should create the table', function(done) { db.run("CREATE TABLE foo (id int)", done); }); await it('should insert repeatedly, reusing the same statement', function(done) { var stmt = db.prepare("INSERT INTO foo VALUES(?)"); for (var i = 5; i < count; i++) { stmt.run(i, function(err) { if (err) throw err; inserted++; }); } stmt.finalize(done); }); await it('should retrieve repeatedly, resuing the same statement', function(done) { var collected = []; var stmt = db.prepare("SELECT id FROM foo WHERE id = ?"); for (var i = 0; i < count; i++) { stmt.get(i, function(err, row) { if (err) throw err; if (row) collected.push(row); }); } stmt.finalize(function(err) { if (err) throw err; retrieved += collected.length; assert.deepEqual(collected, [ { id: 5 }, { id: 6 }, { id: 7 }, { id: 8 }, { id: 9 } ]); done(); }); }); await it('should have inserted and retrieved the right amount', function() { assert.equal(inserted, 5); assert.equal(retrieved, 5); }); after(function(done) { db.close(done); }); }); }()); /* file https://github.com/mapbox/node-sqlite3/blob/v5.0.2/test/scheduling.test.js */ await (async function() { console.error("\ntest-old.js - scheduling.test.js"); var sqlite3 = require('.'); var assert = require('assert'); await describe('scheduling', async function() { await it('scheduling after the database was closed', function(done) { var db = new sqlite3.Database(':memory:'); db.on('error', function(err) { assert.ok(err.message && err.message.indexOf("SQLITE_MISUSE: Database handle is closed") > -1); done(); }); db.close(); db.run("CREATE TABLE foo (id int)"); }); await it('scheduling a query with callback after the database was closed', function(done) { var db = new sqlite3.Database(':memory:'); db.on('error', function(err) { assert.ok(false, 'Event was accidentally triggered'); }); db.close(); db.run("CREATE TABLE foo (id int)", function(err) { assert.ok(err.message && err.message.indexOf("SQLITE_MISUSE: Database handle is closed") > -1); done(); }); }); await it('running a query after the database was closed', function(done) { var db = new sqlite3.Database(':memory:'); var stmt = db.prepare("SELECT * FROM sqlite_master", function(err) { if (err) throw err; db.close(function(err) { assert.ok(err); assert.ok(err.message && err.message.indexOf("SQLITE_BUSY: unable to close due to") > -1); // Running a statement now should not fail. stmt.run(done); }); }); }); }); }()); /* file https://github.com/mapbox/node-sqlite3/blob/v5.0.2/test/serialization.test.js */ await (async function() { console.error("\ntest-old.js - serialization.test.js"); var sqlite3 = require('.'); var assert = require('assert'); await describe('serialize() and parallelize()', async function() { var db; await before(function(done) { db = new sqlite3.Database(':memory:', done); }); var inserted1 = 0; var inserted2 = 0; var retrieved = 0; var count = 1000; await it('should toggle', function(done) { db.serialize(); db.run("CREATE TABLE foo (txt text, num int, flt float, blb blob)"); db.parallelize(done); }); await it('should insert rows', function() { var stmt1 = db.prepare("INSERT INTO foo VALUES(?, ?, ?, ?)"); var stmt2 = db.prepare("INSERT INTO foo VALUES(?, ?, ?, ?)"); for (var i = 0; i < count; i++) { // Interleaved inserts with two statements. stmt1.run('String ' + i, i, i * Math.PI, function(err) { if (err) throw err; inserted1++; }); i++; stmt2.run('String ' + i, i, i * Math.PI, function(err) { if (err) throw err; inserted2++; }); } stmt1.finalize(); stmt2.finalize(); }); await it('should have inserted all the rows after synchronizing with serialize()', function(done) { db.serialize(); db.all("SELECT txt, num, flt, blb FROM foo ORDER BY num", function(err, rows) { if (err) throw err; for (var i = 0; i < rows.length; i++) { assert.equal(rows[i].txt, 'String ' + i); assert.equal(rows[i].num, i); assert.equal(rows[i].flt, i * Math.PI); assert.equal(rows[i].blb, null); retrieved++; } assert.equal(count, inserted1 + inserted2, "Didn't insert all rows"); assert.equal(count, retrieved, "Didn't retrieve all rows"); done(); }); }); after(function(done) { db.close(done); }); }); await describe('serialize(fn)', async function() { var db; await before(function(done) { db = new sqlite3.Database(':memory:', done); }); var inserted = 0; var retrieved = 0; var count = 1000; await it('should call the callback', function(done) { db.serialize(function() { db.run("CREATE TABLE foo (txt text, num int, flt float, blb blob)"); var stmt = db.prepare("INSERT INTO foo VALUES(?, ?, ?, ?)"); for (var i = 0; i < count; i++) { stmt.run('String ' + i, i, i * Math.PI, function(err) { if (err) throw err; inserted++; }); } stmt.finalize(); db.all("SELECT txt, num, flt, blb FROM foo ORDER BY num", function(err, rows) { if (err) throw err; for (var i = 0; i < rows.length; i++) { assert.equal(rows[i].txt, 'String ' + i); assert.equal(rows[i].num, i); assert.equal(rows[i].flt, i * Math.PI); assert.equal(rows[i].blb, null); retrieved++; } done(); }); }); }); await it('should have inserted and retrieved all rows', function() { assert.equal(count, inserted, "Didn't insert all rows"); assert.equal(count, retrieved, "Didn't retrieve all rows"); }); after(function(done) { db.close(done); }); }); }()); /* file https://github.com/mapbox/node-sqlite3/blob/v5.0.2/test/trace.test.js */ await (async function() { console.error("\ntest-old.js - trace.test.js"); var sqlite3 = require('.'); var assert = require('assert'); await describe('tracing', async function() { await it('Database tracing', function(done) { var db = new sqlite3.Database(':memory:'); var create = false; var select = false; db.on('trace', function(sql) { if (sql.match(/^SELECT/)) { assert.ok(!select); assert.equal(sql, "SELECT * FROM foo"); select = true; } else if (sql.match(/^CREATE/)) { assert.ok(!create); assert.equal(sql, "CREATE TABLE foo (id int)"); create = true; } else { assert.ok(false); } }); db.serialize(function() { db.run("CREATE TABLE foo (id int)"); db.run("SELECT * FROM foo"); }); db.close(function(err) { if (err) throw err; assert.ok(create); assert.ok(select); done(); }); }); await it('test disabling tracing #1', function(done) { var db = new sqlite3.Database(':memory:'); db.on('trace', function(sql) {}); db.removeAllListeners('trace'); db._events['trace'] = function(sql) { assert.ok(false); }; db.run("CREATE TABLE foo (id int)"); db.close(done); }); await it('test disabling tracing #2', function(done) { var db = new sqlite3.Database(':memory:'); var trace = function(sql) {}; db.on('trace', trace); db.removeListener('trace', trace); db._events['trace'] = function(sql) { assert.ok(false); }; db.run("CREATE TABLE foo (id int)"); db.close(done); }); }); }()); /* file https://github.com/mapbox/node-sqlite3/blob/v5.0.2/test/unicode.test.js */ await (async function() { console.error("\ntest-old.js - unicode.test.js"); var sqlite3 = require('.'); var assert = require('assert'); await describe('unicode', async function() { var first_values = [], trailing_values = [], chars = [], subranges = new Array(2), len = subranges.length, db, i; await before(function(done) { db = new sqlite3.Database(':memory:', done); }); for (i = 0x20; i < 0x80; i++) { first_values.push(i); } for (i = 0xc2; i < 0xf0; i++) { first_values.push(i); } for (i = 0x80; i < 0xc0; i++) { trailing_values.push(i); } for (i = 0; i < len; i++) { subranges[i] = []; } for (i = 0xa0; i < 0xc0; i++) { subranges[0].push(i); } for (i = 0x80; i < 0xa0; i++) { subranges[1].push(i); } function random_choice(arr) { return arr[Math.random() * arr.length | 0]; } function random_utf8() { var first = random_choice(first_values); if (first < 0x80) { return String.fromCharCode(first); } else if (first < 0xe0) { return String.fromCharCode((first & 0x1f) << 0x6 | random_choice(trailing_values) & 0x3f); } else if (first == 0xe0) { return String.fromCharCode(((first & 0xf) << 0xc) | ((random_choice(subranges[0]) & 0x3f) << 6) | random_choice(trailing_values) & 0x3f); } else if (first == 0xed) { return String.fromCharCode(((first & 0xf) << 0xc) | ((random_choice(subranges[1]) & 0x3f) << 6) | random_choice(trailing_values) & 0x3f); } else if (first < 0xf0) { return String.fromCharCode(((first & 0xf) << 0xc) | ((random_choice(trailing_values) & 0x3f) << 6) | random_choice(trailing_values) & 0x3f); } } function randomString() { var str = '', i; for (i = Math.random() * 300; i > 0; i--) { str += random_utf8(); } return str; } // Generate random data. var data = []; var length = Math.floor(Math.random() * 1000) + 200; for (var i = 0; i < length; i++) { data.push(randomString()); } var inserted = 0; var retrieved = 0; await it('should create the table', function(done) { db.run("CREATE TABLE foo (id int, txt text)", done); }); await it('should insert all values', function(done) { var stmt = db.prepare("INSERT INTO foo VALUES(?, ?)"); for (var i = 0; i < data.length; i++) { stmt.run(i, data[i], function(err) { if (err) throw err; inserted++; }); } stmt.finalize(done); }); await it('should retrieve all values', function(done) { db.all("SELECT txt FROM foo ORDER BY id", function(err, rows) { if (err) throw err; for (var i = 0; i < rows.length; i++) { assert.equal(rows[i].txt, data[i]); retrieved++; } done(); }); }); await it('should have inserted and retrieved the correct amount', function() { assert.equal(inserted, length); assert.equal(retrieved, length); }); after(function(done) { db.close(done); }); }); }()); /* file https://github.com/mapbox/node-sqlite3/blob/v5.0.2/test/upsert.test.js */ await (async function() { console.error("\ntest-old.js - upsert.test.js"); var sqlite3 = require('.'); var assert = require('assert'); await describe('query properties', async function() { var db; await before(function(done) { db = new sqlite3.Database(':memory:'); db.run("CREATE TABLE foo (id INT PRIMARY KEY, count INT)", done); }); await it('should upsert', function(done) { var stmt = db.prepare("INSERT INTO foo VALUES(?, ?)"); stmt.run(1, 1, function(err) { // insert 1 if (err) throw err; var upsert_stmt = db.prepare("INSERT INTO foo VALUES(?, ?) ON CONFLICT (id) DO UPDATE SET count = count + excluded.count"); upsert_stmt.run(1, 2, function(err) { // add 2 if (err) throw err; var select_stmt = db.prepare("SELECT count FROM foo WHERE id = ?"); select_stmt.get(1, function(err, row) { if (err) throw err; assert.equal(row.count, 3); // equals 3 }); }) }); db.wait(done); }); }); }()); /* file https://github.com/mapbox/node-sqlite3/blob/v5.0.2/test/verbose.test.js */ await (async function() { console.error("\ntest-old.js - verbose.test.js"); var sqlite3 = require('.'); var assert = require('assert'); var invalid_sql = 'update non_existent_table set id=1'; var originalMethods = { Database: {}, Statement: {}, }; function backupOriginalMethods() { for (var obj in originalMethods) { for (var attr in sqlite3[obj].prototype) { originalMethods[obj][attr] = sqlite3[obj].prototype[attr]; } } } function resetVerbose() { for (var obj in originalMethods) { for (var attr in originalMethods[obj]) { sqlite3[obj].prototype[attr] = originalMethods[obj][attr]; } } } await describe('verbose', async function() { await it('Shoud add trace info to error when verbose is called', function(done) { var db = new sqlite3.Database(':memory:'); backupOriginalMethods(); sqlite3.verbose(); db.run(invalid_sql, function(err) { assert(err instanceof Error); assert( err.stack.indexOf(`Database#run('${invalid_sql}'`) > -1, `Stack shoud contain trace info, stack = ${err.stack}` ); done(); resetVerbose(); }); }); await it('Shoud not add trace info to error when verbose is not called', function(done) { var db = new sqlite3.Database(':memory:'); db.run(invalid_sql, function(err) { assert(err instanceof Error); assert( err.stack.indexOf(invalid_sql) === -1, `Stack shoud not contain trace info, stack = ${err.stack}` ); done(); }); }); }); // hack-test }()); }()); /* file none */ /*jslint-enable*/ /* file sqlmath-test-old2.js */ /*jslint beta, node*/ let sqlite3 = require("."); function assertJsonEqual(aa, bb) { // This function will assert JSON.stringify() === JSON.stringify(). aa = JSON.stringify(objectDeepCopyWithKeysSorted(aa)); bb = JSON.stringify(objectDeepCopyWithKeysSorted(bb)); if (aa !== bb) { throw new Error(JSON.stringify(aa) + " !== " + JSON.stringify(bb)); } } function assertOrThrow(passed, msg) { // This function will throw if is falsy. if (!passed) { throw ( ( msg && typeof msg.message === "string" && typeof msg.stack === "string" ) // if msg is err, then leave as is ? msg : new Error( typeof msg === "string" // if msg is string, then leave as is ? msg // else JSON.stringify(msg) : JSON.stringify(msg, undefined, 4) ) ); } } function lineno() { return new Error().stack.match( /(?:\n.*?){3}(\d+?):\d+?\)?$/m )[1]; } function objectDeepCopyWithKeysSorted(obj) { // This function will recursively deep-copy with keys sorted. let sorted; if (typeof obj !== "object" || !obj) { return obj; } // recursively deep-copy list with child-keys sorted if (Array.isArray(obj)) { return obj.map(objectDeepCopyWithKeysSorted); } // recursively deep-copy obj with keys sorted sorted = {}; Object.keys(obj).sort().forEach(function (key) { sorted[key] = objectDeepCopyWithKeysSorted(obj[key]); }); return sorted; } async function promiseDescribe(testDescribe, fnc) { console.error(" " + lineno() + " - describe - " + testDescribe); await fnc(); } async function promiseIt(testShould, fnc) { let timeStart = Date.now(); process.stderr.write( " " + lineno() + " - it" + " - " + testShould + " ... " ); await promisify(fnc); console.error( (Date.now() - timeStart) + " ms" + " \u001b[32m\u2713\u001b[39m " ); } async function promisify(fnc) { if (fnc.length === 0) { return fnc(); } await new Promise(function (resolve) { fnc(resolve); }); } (async function () { console.error(require("path").basename(__filename)); await promiseDescribe("test sqlmath.noop", async function () { let db = new sqlite3.Database(":memory:"); await promiseIt("test sqlmath.noop", function (done) { db.all("SELECT noop('hello world') AS val", function (err, rows) { assertOrThrow(!err, err); assertJsonEqual(rows[0].val, "hello world"); done(); }); }); }); await promiseDescribe("test extension-functions", async function () { let db = new sqlite3.Database(":memory:"); await promiseIt("test atn2", function (done) { db.all("SELECT atn2(0, 0) AS val", function (err, rows) { assertOrThrow(!err, err); assertJsonEqual(rows[0].val, 0); done(); }); }); }); (function () { let db = new sqlite3.Database(":memory:"); db.exec(` CREATE VIRTUAL TABLE aa USING sqlmatrix(2); INSERT INTO aa VALUES (NULL,2),(3,4),(5,6); `); db.all("SELECT * FROM aa;", function (err, rows) { assertOrThrow(!err, err); console.error(rows); }); db.all("pragma table_info(aa);", function (err, rows) { assertOrThrow(!err, err); console.error(rows); }); /* db.all("SELECT noop2(ptr), cols, rows FROM aa;", function (err, rows) { assertOrThrow(!err, err); console.error(rows); }); */ db.all((` CREATE TABLE tt1 AS SELECT 101 AS c102, 102 AS c102 UNION ALL VALUES (201, 202), (301, NULL); CREATE TABLE tt2 AS SELECT 401 AS c402, 402 AS c402, 403 AS c403 UNION ALL VALUES (501, 502.0123, 5030123456789), (601, NULL, 603), (701, b64decode('0123456789'), b64decode('8J+YgQ')); SELECT * FROM tt1; SELECT * FROM tt2; `), function (err, rows) { assertOrThrow(!err, err); console.error(rows); }); }()); // run legacy-test. if (!process.env.npm_config_mode_fast) { require("./test-old.js"); } }()); /* file sqlmath-test-slr.js */ /*jslint beta, devel*/ (function () { function assertOrThrow(passed, msg) { // This function will throw if is falsy. if (!passed) { throw ( (msg && msg.stack) ? msg : new Error(msg) ); } } function simpleLinearRegressionCreate(size = 8192) { // This function will create an object for calculating simple-linear-regression // statistics. // https://en.wikipedia.org/wiki/Simple_linear_regression let nn = 0; let sxx = 0; let sxy = 0; let syy = 0; let xavg = 0; let xx2 = 0; let xyList = new Float64Array(size * 2); let yavg = 0; function pass1(xx, yy) { // This function will calculate simple-linear-regression in one pass using // Wellford's algorithm. // For a new value xx, compute the new nn, new xavg, the new sxx. // xavg accumulates the xavg of the entire dataset // sxx aggregates the squared distance from the xavg // nn aggregates the number of samples seen so far let dx; let dy; xyList[2 * nn] = xx; xyList[2 * nn + 1] = yy; nn += 1; xx2 += xx * xx; // calculate sxx dx = xx - xavg; xavg += dx / nn; sxx += dx * (xx - xavg); // calculate syy dy = yy - yavg; yavg += dy / nn; syy += dy * (yy - yavg); // calculate sxy sxy += dx * (yy - yavg); } function pass2(xyList2) { // This function will calculate simple-linear-regression in two passes. let ii; let nn2; let xx; let yy; nn = xyList2.length / 2; nn2 = xyList2.length; // pass #1 ii = 0; while (ii < nn2) { xx = xyList2[ii]; yy = xyList2[ii + 1]; xyList[ii] = xx; xyList[ii + 1] = yy; xavg += xx; yavg += yy; xx2 += xx * xx; ii += 2; } xavg = xavg / nn; yavg = yavg / nn; // pass #2 ii = 0; while (ii < nn2) { xx = xyList[ii] - xavg; yy = xyList[ii + 1] - yavg; sxx += xx * xx; syy += yy * yy; sxy += xx * yy; ii += 2; } } function result() { let alpha; let beta; let ii = 0; let nn2 = nn * 2; let salpha2; let sbeta2; let see = 0; beta = sxy / sxx; alpha = yavg - beta * xavg; while (ii < nn2) { see += (alpha + beta * xyList[ii] - xyList[ii + 1]) ** 2; ii += 2; } see = see / (nn - 2); sbeta2 = see / sxx; salpha2 = sbeta2 * xx2 / nn; return { alpha, beta, correlation: sxy / Math.sqrt(sxx * syy), nn, salpha2, sbeta2, see, sxx, sxy, syy, xavg, yavg }; } return { pass1, pass2, result }; } // test (function () { let slr1; let xxList; let yyList; function round(num, decimal) { return Math.round(num / decimal) * decimal; } function validate() { let state = slr1.result(); console.error(state); assertOrThrow(round(state.beta, 0.001) === 61.272, state.beta); assertOrThrow(round(state.alpha, 0.001) === -39.062, state.alpha); assertOrThrow(round(state.see, 0.0001) === 0.5762, state.see); assertOrThrow(round(state.sbeta2, 0.0001) === 3.1539, state.sbeta2); assertOrThrow( round(state.salpha2, 0.00001) === 8.63185, state.salpha2 ); assertOrThrow( round(state.correlation, 0.0001) === 0.9946, state.correlation ); } xxList = [ 1.47, 1.50, 1.52, 1.55, 1.57, 1.60, 1.63, 1.65, 1.68, 1.70, 1.73, 1.75, 1.78, 1.80, 1.83 ]; yyList = [ 52.21, 53.12, 54.48, 55.84, 57.20, 58.57, 59.93, 61.29, 63.11, 64.47, 66.28, 68.10, 69.92, 72.19, 74.46 ]; // validate pass1 slr1 = simpleLinearRegressionCreate(xxList.length); xxList.forEach(function (xx, ii) { slr1.pass1(xx, yyList[ii]); }); validate(); // validate pass2 slr1 = simpleLinearRegressionCreate(xxList.length); slr1.pass2(xxList.flatMap(function (xx, ii) { return [ xx, yyList[ii] ]; })); validate(); // validate pass1 and pass2 slr1 = simpleLinearRegressionCreate(xxList.length); slr1.pass2(xxList.slice(2).flatMap(function (xx, ii) { return [ xx, yyList[ii + 2] ]; })); xxList.slice(0, 2).forEach(function (xx, ii) { slr1.pass1(xx, yyList[ii]); }); validate(); }()); }());