From 596bd5f1bb64cebc8d9b72c114f78ef9f074863b Mon Sep 17 00:00:00 2001 From: Gabriel Schulhof Date: Fri, 19 Oct 2018 15:10:59 -0400 Subject: [PATCH] src: factor out Node.js-agnostic N-APIs Split the Node.js ECMAScript API (N-EAPI?) into its own header and implementation files. The motivation is that the ECMAScript API stand on its own so it might be embedded separately, implementation and all. Portions of the implementation used by both files are stored in `node_api_impl.h`. The checked boxes below indicate that the given API remains in `node_api.h`, whereas the lack of a checkbox indicates that the API was moved to `node_ecma_api.h`. * [x] NAPI_MODULE * [x] NAPI_MODULE_INIT * [x] napi_acquire_threadsafe_function * [x] napi_add_env_cleanup_hook * [x] napi_async_destroy * [x] napi_async_init * [x] napi_call_threadsafe_function * [x] napi_cancel_async_work * [x] napi_close_callback_scope * [x] napi_create_async_work * [x] napi_create_buffer * [x] napi_create_buffer_copy * [x] napi_create_external_buffer * [x] napi_create_threadsafe_function * [x] napi_delete_async_work * [x] napi_fatal_error * [x] napi_fatal_exception * [x] napi_get_buffer_info * [x] napi_get_node_version * [x] napi_get_threadsafe_function_context * [x] napi_get_uv_event_loop * [x] napi_is_buffer * [x] napi_make_callback * [x] napi_module_register * [x] napi_open_callback_scope * [x] napi_queue_async_work * [x] napi_ref_threadsafe_function * [x] napi_release_threadsafe_function * [x] napi_remove_env_cleanup_hook * [x] napi_unref_threadsafe_function * [ ] napi_add_finalizer * [ ] napi_adjust_external_memory * [ ] napi_call_function * [ ] napi_close_escapable_handle_scope * [ ] napi_close_handle_scope * [ ] napi_coerce_to_bool * [ ] napi_coerce_to_number * [ ] napi_coerce_to_object * [ ] napi_coerce_to_string * [ ] napi_create_array * [ ] napi_create_arraybuffer * [ ] napi_create_array_with_length * [ ] napi_create_bigint_int64 * [ ] napi_create_bigint_uint64 * [ ] napi_create_bigint_words * [ ] napi_create_dataview * [ ] napi_create_double * [ ] napi_create_error * [ ] napi_create_external * [ ] napi_create_external_arraybuffer * [ ] napi_create_function * [ ] napi_create_int32 * [ ] napi_create_int64 * [ ] napi_create_object * [ ] napi_create_promise * [ ] napi_create_range_error * [ ] napi_create_reference * [ ] napi_create_string_latin1 * [ ] napi_create_string_utf16 * [ ] napi_create_string_utf8 * [ ] napi_create_symbol * [ ] napi_create_typedarray * [ ] napi_create_type_error * [ ] napi_create_uint32 * [ ] napi_define_class * [ ] napi_define_properties * [ ] napi_delete_element * [ ] napi_delete_property * [ ] napi_delete_reference * [ ] napi_escape_handle * [ ] napi_get_and_clear_last_exception * [ ] napi_get_arraybuffer_info * [ ] napi_get_array_length * [ ] napi_get_boolean * [ ] napi_get_cb_info * [ ] napi_get_dataview_info * [ ] napi_get_element * [ ] napi_get_global * [ ] napi_get_last_error_info * [ ] napi_get_named_property * [ ] napi_get_new_target * [ ] napi_get_null * [ ] napi_get_property * [ ] napi_get_property_names * [ ] napi_get_prototype * [ ] napi_get_reference_value * [ ] napi_get_typedarray_info * [ ] napi_get_undefined * [ ] napi_get_value_bigint_int64 * [ ] napi_get_value_bigint_uint64 * [ ] napi_get_value_bigint_words * [ ] napi_get_value_bool * [ ] napi_get_value_double * [ ] napi_get_value_external * [ ] napi_get_value_int32 * [ ] napi_get_value_int64 * [ ] napi_get_value_string_latin1 * [ ] napi_get_value_string_utf16 * [ ] napi_get_value_string_utf8 * [ ] napi_get_value_uint32 * [ ] napi_get_version * [ ] napi_has_element * [ ] napi_has_named_property * [ ] napi_has_own_property * [ ] napi_has_property * [ ] napi_instanceof * [ ] napi_is_array * [ ] napi_is_arraybuffer * [ ] napi_is_dataview * [ ] napi_is_error * [ ] napi_is_exception_pending * [ ] napi_is_promise * [ ] napi_is_typedarray * [ ] napi_new_instance * [ ] napi_open_escapable_handle_scope * [ ] napi_open_handle_scope * [ ] napi_reference_ref * [ ] napi_reference_unref * [ ] napi_reject_deferred * [ ] napi_remove_wrap * [ ] napi_resolve_deferred * [ ] napi_run_script * [ ] napi_set_element * [ ] napi_set_named_property * [ ] napi_set_property * [ ] napi_strict_equals * [ ] napi_throw * [ ] napi_throw_error * [ ] napi_throw_range_error * [ ] napi_throw_type_error * [ ] napi_typeof * [ ] napi_unwrap * [ ] napi_wrap PR-URL: https://github.com/nodejs/node/pull/23786 Reviewed-By: Yazhong Liu Reviewed-By: Michael Dawson --- Makefile | 3 +- doc/api/n-api.md | 59 + node.gyp | 5 + src/js_native_api.h | 485 +++ src/js_native_api_types.h | 108 + src/js_native_api_v8.cc | 2907 +++++++++++++++ src/js_native_api_v8.h | 202 + src/js_native_api_v8_internals.h | 35 + src/node_api.cc | 3276 +---------------- src/node_api.h | 521 +-- src/node_api_types.h | 103 +- test/addons-napi/6_object_wrap/binding.cc | 1 + test/addons-napi/6_object_wrap/myobject.h | 2 +- test/addons-napi/7_factory_wrap/binding.cc | 1 + test/addons-napi/7_factory_wrap/myobject.h | 2 +- test/addons-napi/8_passing_wrapped/binding.cc | 1 + test/addons-napi/8_passing_wrapped/myobject.h | 2 +- tools/install.py | 2 + 18 files changed, 3927 insertions(+), 3788 deletions(-) create mode 100644 src/js_native_api.h create mode 100644 src/js_native_api_types.h create mode 100644 src/js_native_api_v8.cc create mode 100644 src/js_native_api_v8.h create mode 100644 src/js_native_api_v8_internals.h diff --git a/Makefile b/Makefile index 3d828f71f80bb6..3dc34995cce213 100644 --- a/Makefile +++ b/Makefile @@ -393,7 +393,8 @@ ADDONS_NAPI_BINDING_SOURCES := \ # Implicitly depends on $(NODE_EXE), see the build-addons-napi rule for rationale. test/addons-napi/.buildstamp: $(ADDONS_PREREQS) \ $(ADDONS_NAPI_BINDING_GYPS) $(ADDONS_NAPI_BINDING_SOURCES) \ - src/node_api.h src/node_api_types.h + src/node_api.h src/node_api_types.h src/js_native_api.h \ + src/js_native_api_types.h src/js_native_api_v8.h src/js_native_api_v8_internals.h @$(call run_build_addons,"$$PWD/test/addons-napi",$@) .PHONY: build-addons-napi diff --git a/doc/api/n-api.md b/doc/api/n-api.md index 27ebefb4b56b3e..33d9f561bec313 100644 --- a/doc/api/n-api.md +++ b/doc/api/n-api.md @@ -155,6 +155,65 @@ available to the module code. \* Indicates that the N-API version was released as experimental +The N-APIs associated strictly with accessing ECMAScript features from native +code can be found separately in `js_native_api.h` and `js_native_api_types.h`. +The APIs defined in these headers are included in `node_api.h` and +`node_api_types.h`. The headers are structured in this way in order to allow +implementations of N-API outside of Node.js. For those implementations the +Node.js specific APIs may not be applicable. + +The Node.js-specific parts of an addon can be separated from the code that +exposes the actual functionality to the JavaScript environment so that the +latter may be used with multiple implementations of N-API. In the example below, +`addon.c` and `addon.h` refer only to `js_native_api.h`. This ensures that +`addon.c` can be reused to compile against either the Node.js implementation of +N-API or any implementation of N-API outside of Node.js. + +`addon_node.c` is a separate file that contains the Node.js specific entry point +to the addon and which instantiates the addon by calling into `addon.c` when the +addon is loaded into a Node.js environment. + +```C +// addon.h +#ifndef _ADDON_H_ +#define _ADDON_H_ +#include +napi_value create_addon(napi_env env); +#endif // _ADDON_H_ +``` + +```C +// addon.c +#include "addon.h" +napi_value create_addon(napi_env env) { + napi_value result; + assert(napi_create_object(env, &result) == napi_ok); + napi_value exported_function; + assert(napi_create_function(env, + "doSomethingUseful", + NAPI_AUTO_LENGTH, + DoSomethingUseful, + NULL, + &exported_function) == napi_ok); + assert(napi_set_named_property(env, + result, + "doSomethingUseful", + exported_function) == napi_ok); + return result; +} +``` + +```C +// addon_node.c +#include + +static napi_value Init(napi_env env, napi_value exports) { + return create_addon(env); +} + +NAPI_MODULE(NODE_GYP_MODULE_NAME, Init) +``` + ## Basic N-API Data Types N-API exposes the following fundamental datatypes as abstractions that are diff --git a/node.gyp b/node.gyp index 5b68281de45790..b40933a0ebc0c4 100644 --- a/node.gyp +++ b/node.gyp @@ -334,6 +334,11 @@ 'src/fs_event_wrap.cc', 'src/handle_wrap.cc', 'src/heap_utils.cc', + 'src/js_native_api.h', + 'src/js_native_api_types.h', + 'src/js_native_api_v8.cc', + 'src/js_native_api_v8.h', + 'src/js_native_api_v8_internals.h', 'src/js_stream.cc', 'src/module_wrap.cc', 'src/node.cc', diff --git a/src/js_native_api.h b/src/js_native_api.h new file mode 100644 index 00000000000000..01707d78ccaa46 --- /dev/null +++ b/src/js_native_api.h @@ -0,0 +1,485 @@ +#ifndef SRC_JS_NATIVE_API_H_ +#define SRC_JS_NATIVE_API_H_ + +#include +#include +#include "js_native_api_types.h" + +#ifndef NAPI_VERSION +#ifdef NAPI_EXPERIMENTAL +// Use INT_MAX, this should only be consumed by the pre-processor anyway. +#define NAPI_VERSION 2147483647 +#else +// The baseline version for N-API +#define NAPI_VERSION 3 +#endif +#endif + +// 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) + #else + #define NAPI_EXTERN /* nothing */ + #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 promise, + 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); + +#ifdef NAPI_EXPERIMENTAL + +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); +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_EXPERIMENTAL + +EXTERN_C_END + +#endif // SRC_JS_NATIVE_API_H_ diff --git a/src/js_native_api_types.h b/src/js_native_api_types.h new file mode 100644 index 00000000000000..a4739957991e3f --- /dev/null +++ b/src/js_native_api_types.h @@ -0,0 +1,108 @@ +#ifndef SRC_JS_NATIVE_API_TYPES_H_ +#define SRC_JS_NATIVE_API_TYPES_H_ + +#include +#include + +#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, +} 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_status; + +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; + +#endif // SRC_JS_NATIVE_API_TYPES_H_ diff --git a/src/js_native_api_v8.cc b/src/js_native_api_v8.cc new file mode 100644 index 00000000000000..b28376afb7cedd --- /dev/null +++ b/src/js_native_api_v8.cc @@ -0,0 +1,2907 @@ +#include // INT_MAX +#include +#define NAPI_EXPERIMENTAL +#include "js_native_api_v8.h" +#include "js_native_api.h" + +#define CHECK_MAYBE_NOTHING(env, maybe, status) \ + RETURN_STATUS_IF_FALSE((env), !((maybe).IsNothing()), (status)) + +#define CHECK_TO_NUMBER(env, context, result, src) \ + CHECK_TO_TYPE((env), Number, (context), (result), (src), napi_number_expected) + +#define CHECK_TO_BOOL(env, context, result, src) \ + CHECK_TO_TYPE((env), Boolean, (context), (result), (src), \ + napi_boolean_expected) + +// n-api defines NAPI_AUTO_LENGHTH as the indicator that a string +// is null terminated. For V8 the equivalent is -1. The assert +// validates that our cast of NAPI_AUTO_LENGTH results in -1 as +// needed by V8. +#define CHECK_NEW_FROM_UTF8_LEN(env, result, str, len) \ + do { \ + static_assert(static_cast(NAPI_AUTO_LENGTH) == -1, \ + "Casting NAPI_AUTO_LENGTH to int must result in -1"); \ + RETURN_STATUS_IF_FALSE((env), \ + (len == NAPI_AUTO_LENGTH) || len <= INT_MAX, \ + napi_invalid_arg); \ + auto str_maybe = v8::String::NewFromUtf8( \ + (env)->isolate, (str), v8::NewStringType::kInternalized, \ + static_cast(len)); \ + CHECK_MAYBE_EMPTY((env), str_maybe, napi_generic_failure); \ + (result) = str_maybe.ToLocalChecked(); \ + } while (0) + +#define CHECK_NEW_FROM_UTF8(env, result, str) \ + CHECK_NEW_FROM_UTF8_LEN((env), (result), (str), NAPI_AUTO_LENGTH) + +#define CREATE_TYPED_ARRAY( \ + env, type, size_of_element, buffer, byte_offset, length, out) \ + do { \ + if ((size_of_element) > 1) { \ + THROW_RANGE_ERROR_IF_FALSE( \ + (env), (byte_offset) % (size_of_element) == 0, \ + "ERR_NAPI_INVALID_TYPEDARRAY_ALIGNMENT", \ + "start offset of "#type" should be a multiple of "#size_of_element); \ + } \ + THROW_RANGE_ERROR_IF_FALSE((env), (length) * (size_of_element) + \ + (byte_offset) <= buffer->ByteLength(), \ + "ERR_NAPI_INVALID_TYPEDARRAY_LENGTH", \ + "Invalid typed array length"); \ + (out) = v8::type::New((buffer), (byte_offset), (length)); \ + } while (0) + +namespace v8impl { + +namespace { + +inline static napi_status +V8NameFromPropertyDescriptor(napi_env env, + const napi_property_descriptor* p, + v8::Local* result) { + if (p->utf8name != nullptr) { + CHECK_NEW_FROM_UTF8(env, *result, p->utf8name); + } else { + v8::Local property_value = + v8impl::V8LocalValueFromJsValue(p->name); + + RETURN_STATUS_IF_FALSE(env, property_value->IsName(), napi_name_expected); + *result = property_value.As(); + } + + return napi_ok; +} + +// convert from n-api property attributes to v8::PropertyAttribute +inline static v8::PropertyAttribute V8PropertyAttributesFromDescriptor( + const napi_property_descriptor* descriptor) { + unsigned int attribute_flags = v8::PropertyAttribute::None; + + if (descriptor->getter != nullptr || descriptor->setter != nullptr) { + // The napi_writable attribute is ignored for accessor descriptors, but + // V8 requires the ReadOnly attribute to match nonexistence of a setter. + attribute_flags |= (descriptor->setter == nullptr ? + v8::PropertyAttribute::ReadOnly : v8::PropertyAttribute::None); + } else if ((descriptor->attributes & napi_writable) == 0) { + attribute_flags |= v8::PropertyAttribute::ReadOnly; + } + + if ((descriptor->attributes & napi_enumerable) == 0) { + attribute_flags |= v8::PropertyAttribute::DontEnum; + } + if ((descriptor->attributes & napi_configurable) == 0) { + attribute_flags |= v8::PropertyAttribute::DontDelete; + } + + return static_cast(attribute_flags); +} + +inline static napi_deferred +JsDeferredFromNodePersistent(v8impl::Persistent* local) { + return reinterpret_cast(local); +} + +inline static v8impl::Persistent* +NodePersistentFromJsDeferred(napi_deferred local) { + return reinterpret_cast*>(local); +} + +class HandleScopeWrapper { + public: + explicit HandleScopeWrapper(v8::Isolate* isolate) : scope(isolate) {} + + private: + v8::HandleScope scope; +}; + +// In node v0.10 version of v8, there is no EscapableHandleScope and the +// node v0.10 port use HandleScope::Close(Local v) to mimic the behavior +// of a EscapableHandleScope::Escape(Local v), but it is not the same +// semantics. This is an example of where the api abstraction fail to work +// across different versions. +class EscapableHandleScopeWrapper { + public: + explicit EscapableHandleScopeWrapper(v8::Isolate* isolate) + : scope(isolate), escape_called_(false) {} + bool escape_called() const { + return escape_called_; + } + template + v8::Local Escape(v8::Local handle) { + escape_called_ = true; + return scope.Escape(handle); + } + + private: + v8::EscapableHandleScope scope; + bool escape_called_; +}; + +inline static napi_handle_scope +JsHandleScopeFromV8HandleScope(HandleScopeWrapper* s) { + return reinterpret_cast(s); +} + +inline static HandleScopeWrapper* +V8HandleScopeFromJsHandleScope(napi_handle_scope s) { + return reinterpret_cast(s); +} + +inline static napi_escapable_handle_scope +JsEscapableHandleScopeFromV8EscapableHandleScope( + EscapableHandleScopeWrapper* s) { + return reinterpret_cast(s); +} + +inline static EscapableHandleScopeWrapper* +V8EscapableHandleScopeFromJsEscapableHandleScope( + napi_escapable_handle_scope s) { + return reinterpret_cast(s); +} + +inline static napi_status ConcludeDeferred(napi_env env, + napi_deferred deferred, + napi_value result, + bool is_resolved) { + NAPI_PREAMBLE(env); + CHECK_ARG(env, result); + + v8::Local context = env->isolate->GetCurrentContext(); + v8impl::Persistent* deferred_ref = + NodePersistentFromJsDeferred(deferred); + v8::Local v8_deferred = + v8::Local::New(env->isolate, *deferred_ref); + + auto v8_resolver = v8::Local::Cast(v8_deferred); + + v8::Maybe success = is_resolved ? + v8_resolver->Resolve(context, v8impl::V8LocalValueFromJsValue(result)) : + v8_resolver->Reject(context, v8impl::V8LocalValueFromJsValue(result)); + + delete deferred_ref; + + RETURN_STATUS_IF_FALSE(env, success.FromMaybe(false), napi_generic_failure); + + return GET_RETURN_STATUS(env); +} + +// Wrapper around v8impl::Persistent that implements reference counting. +class Reference : private Finalizer { + private: + Reference(napi_env env, + v8::Local value, + uint32_t initial_refcount, + bool delete_self, + napi_finalize finalize_callback, + void* finalize_data, + void* finalize_hint) + : Finalizer(env, finalize_callback, finalize_data, finalize_hint), + _persistent(env->isolate, value), + _refcount(initial_refcount), + _delete_self(delete_self) { + if (initial_refcount == 0) { + _persistent.SetWeak( + this, FinalizeCallback, v8::WeakCallbackType::kParameter); + } + } + + public: + void* Data() { + return _finalize_data; + } + + static Reference* New(napi_env env, + v8::Local value, + uint32_t initial_refcount, + bool delete_self, + napi_finalize finalize_callback = nullptr, + void* finalize_data = nullptr, + void* finalize_hint = nullptr) { + return new Reference(env, + value, + initial_refcount, + delete_self, + finalize_callback, + finalize_data, + finalize_hint); + } + + static void Delete(Reference* reference) { + delete reference; + } + + uint32_t Ref() { + if (++_refcount == 1) { + _persistent.ClearWeak(); + } + + return _refcount; + } + + uint32_t Unref() { + if (_refcount == 0) { + return 0; + } + if (--_refcount == 0) { + _persistent.SetWeak( + this, FinalizeCallback, v8::WeakCallbackType::kParameter); + } + + return _refcount; + } + + uint32_t RefCount() { + return _refcount; + } + + v8::Local Get() { + if (_persistent.IsEmpty()) { + return v8::Local(); + } else { + return v8::Local::New(_env->isolate, _persistent); + } + } + + private: + static void FinalizeCallback(const v8::WeakCallbackInfo& data) { + Reference* reference = data.GetParameter(); + reference->_persistent.Reset(); + + // Check before calling the finalize callback, because the callback might + // delete it. + bool delete_self = reference->_delete_self; + napi_env env = reference->_env; + + if (reference->_finalize_callback != nullptr) { + NAPI_CALL_INTO_MODULE_THROW(env, + reference->_finalize_callback( + reference->_env, + reference->_finalize_data, + reference->_finalize_hint)); + } + + if (delete_self) { + Delete(reference); + } + } + + v8impl::Persistent _persistent; + uint32_t _refcount; + bool _delete_self; +}; + +enum UnwrapAction { + KeepWrap, + RemoveWrap +}; + +inline static napi_status Unwrap(napi_env env, + napi_value js_object, + void** result, + UnwrapAction action) { + NAPI_PREAMBLE(env); + CHECK_ARG(env, js_object); + if (action == KeepWrap) { + CHECK_ARG(env, result); + } + + v8::Isolate* isolate = env->isolate; + v8::Local context = isolate->GetCurrentContext(); + + v8::Local value = v8impl::V8LocalValueFromJsValue(js_object); + RETURN_STATUS_IF_FALSE(env, value->IsObject(), napi_invalid_arg); + v8::Local obj = value.As(); + + auto val = obj->GetPrivate(context, NAPI_PRIVATE_KEY(context, wrapper)) + .ToLocalChecked(); + RETURN_STATUS_IF_FALSE(env, val->IsExternal(), napi_invalid_arg); + Reference* reference = + static_cast(val.As()->Value()); + + if (result) { + *result = reference->Data(); + } + + if (action == RemoveWrap) { + CHECK(obj->DeletePrivate(context, NAPI_PRIVATE_KEY(context, wrapper)) + .FromJust()); + Reference::Delete(reference); + } + + return GET_RETURN_STATUS(env); +} + +//=== Function napi_callback wrapper ================================= + +// Use this data structure to associate callback data with each N-API function +// exposed to JavaScript. The structure is stored in a v8::External which gets +// passed into our callback wrapper. This reduces the performance impact of +// calling through N-API. +// Ref: benchmark/misc/function_call +// Discussion (incl. perf. data): https://github.com/nodejs/node/pull/21072 +struct CallbackBundle { + // Bind the lifecycle of `this` C++ object to a JavaScript object. + // We never delete a CallbackBundle C++ object directly. + void BindLifecycleTo(v8::Isolate* isolate, v8::Local target) { + handle.Reset(isolate, target); + handle.SetWeak(this, WeakCallback, v8::WeakCallbackType::kParameter); + } + + napi_env env; // Necessary to invoke C++ NAPI callback + void* cb_data; // The user provided callback data + napi_callback function_or_getter; + napi_callback setter; + v8impl::Persistent handle; // Die with this JavaScript object + + private: + static void WeakCallback(v8::WeakCallbackInfo const& info) { + // Use the "WeakCallback mechanism" to delete the C++ `bundle` object. + // This will be called when the v8::External containing `this` pointer + // is being GC-ed. + CallbackBundle* bundle = info.GetParameter(); + if (bundle != nullptr) { + delete bundle; + } + } +}; + +// Base class extended by classes that wrap V8 function and property callback +// info. +class CallbackWrapper { + public: + CallbackWrapper(napi_value this_arg, size_t args_length, void* data) + : _this(this_arg), _args_length(args_length), _data(data) {} + + virtual napi_value GetNewTarget() = 0; + virtual void Args(napi_value* buffer, size_t bufferlength) = 0; + virtual void SetReturnValue(napi_value value) = 0; + + napi_value This() { return _this; } + + size_t ArgsLength() { return _args_length; } + + void* Data() { return _data; } + + protected: + const napi_value _this; + const size_t _args_length; + void* _data; +}; + +template +class CallbackWrapperBase : public CallbackWrapper { + public: + CallbackWrapperBase(const Info& cbinfo, const size_t args_length) + : CallbackWrapper(JsValueFromV8LocalValue(cbinfo.This()), + args_length, + nullptr), + _cbinfo(cbinfo) { + _bundle = reinterpret_cast( + v8::Local::Cast(cbinfo.Data())->Value()); + _data = _bundle->cb_data; + } + + napi_value GetNewTarget() override { return nullptr; } + + protected: + void InvokeCallback() { + napi_callback_info cbinfo_wrapper = reinterpret_cast( + static_cast(this)); + + // All other pointers we need are stored in `_bundle` + napi_env env = _bundle->env; + napi_callback cb = _bundle->*FunctionField; + + napi_value result; + NAPI_CALL_INTO_MODULE_THROW(env, result = cb(env, cbinfo_wrapper)); + + if (result != nullptr) { + this->SetReturnValue(result); + } + } + + const Info& _cbinfo; + CallbackBundle* _bundle; +}; + +class FunctionCallbackWrapper + : public CallbackWrapperBase, + &CallbackBundle::function_or_getter> { + public: + static void Invoke(const v8::FunctionCallbackInfo& info) { + FunctionCallbackWrapper cbwrapper(info); + cbwrapper.InvokeCallback(); + } + + explicit FunctionCallbackWrapper( + const v8::FunctionCallbackInfo& cbinfo) + : CallbackWrapperBase(cbinfo, cbinfo.Length()) {} + + napi_value GetNewTarget() override { + if (_cbinfo.IsConstructCall()) { + return v8impl::JsValueFromV8LocalValue(_cbinfo.NewTarget()); + } else { + return nullptr; + } + } + + /*virtual*/ + void Args(napi_value* buffer, size_t buffer_length) override { + size_t i = 0; + size_t min = std::min(buffer_length, _args_length); + + for (; i < min; i += 1) { + buffer[i] = v8impl::JsValueFromV8LocalValue(_cbinfo[i]); + } + + if (i < buffer_length) { + napi_value undefined = + v8impl::JsValueFromV8LocalValue(v8::Undefined(_cbinfo.GetIsolate())); + for (; i < buffer_length; i += 1) { + buffer[i] = undefined; + } + } + } + + /*virtual*/ + void SetReturnValue(napi_value value) override { + v8::Local val = v8impl::V8LocalValueFromJsValue(value); + _cbinfo.GetReturnValue().Set(val); + } +}; + +class GetterCallbackWrapper + : public CallbackWrapperBase, + &CallbackBundle::function_or_getter> { + public: + static void Invoke(v8::Local property, + const v8::PropertyCallbackInfo& info) { + GetterCallbackWrapper cbwrapper(info); + cbwrapper.InvokeCallback(); + } + + explicit GetterCallbackWrapper( + const v8::PropertyCallbackInfo& cbinfo) + : CallbackWrapperBase(cbinfo, 0) {} + + /*virtual*/ + void Args(napi_value* buffer, size_t buffer_length) override { + if (buffer_length > 0) { + napi_value undefined = + v8impl::JsValueFromV8LocalValue(v8::Undefined(_cbinfo.GetIsolate())); + for (size_t i = 0; i < buffer_length; i += 1) { + buffer[i] = undefined; + } + } + } + + /*virtual*/ + void SetReturnValue(napi_value value) override { + v8::Local val = v8impl::V8LocalValueFromJsValue(value); + _cbinfo.GetReturnValue().Set(val); + } +}; + +class SetterCallbackWrapper + : public CallbackWrapperBase, + &CallbackBundle::setter> { + public: + static void Invoke(v8::Local property, + v8::Local value, + const v8::PropertyCallbackInfo& info) { + SetterCallbackWrapper cbwrapper(info, value); + cbwrapper.InvokeCallback(); + } + + SetterCallbackWrapper(const v8::PropertyCallbackInfo& cbinfo, + const v8::Local& value) + : CallbackWrapperBase(cbinfo, 1), _value(value) {} + + /*virtual*/ + void Args(napi_value* buffer, size_t buffer_length) override { + if (buffer_length > 0) { + buffer[0] = v8impl::JsValueFromV8LocalValue(_value); + + if (buffer_length > 1) { + napi_value undefined = v8impl::JsValueFromV8LocalValue( + v8::Undefined(_cbinfo.GetIsolate())); + for (size_t i = 1; i < buffer_length; i += 1) { + buffer[i] = undefined; + } + } + } + } + + /*virtual*/ + void SetReturnValue(napi_value value) override { + // Ignore any value returned from a setter callback. + } + + private: + const v8::Local& _value; +}; + +// Creates an object to be made available to the static function callback +// wrapper, used to retrieve the native callback function and data pointer. +static +v8::Local CreateFunctionCallbackData(napi_env env, + napi_callback cb, + void* data) { + CallbackBundle* bundle = new CallbackBundle(); + bundle->function_or_getter = cb; + bundle->cb_data = data; + bundle->env = env; + v8::Local cbdata = v8::External::New(env->isolate, bundle); + bundle->BindLifecycleTo(env->isolate, cbdata); + + return cbdata; +} + +// Creates an object to be made available to the static getter/setter +// callback wrapper, used to retrieve the native getter/setter callback +// function and data pointer. +inline v8::Local CreateAccessorCallbackData(napi_env env, + napi_callback getter, + napi_callback setter, + void* data) { + CallbackBundle* bundle = new CallbackBundle(); + bundle->function_or_getter = getter; + bundle->setter = setter; + bundle->cb_data = data; + bundle->env = env; + v8::Local cbdata = v8::External::New(env->isolate, bundle); + bundle->BindLifecycleTo(env->isolate, cbdata); + + return cbdata; +} + +enum WrapType { + retrievable, + anonymous +}; + +template +inline napi_status Wrap(napi_env env, + napi_value js_object, + void* native_object, + napi_finalize finalize_cb, + void* finalize_hint, + napi_ref* result) { + NAPI_PREAMBLE(env); + CHECK_ARG(env, js_object); + + v8::Local context = env->context(); + + v8::Local value = v8impl::V8LocalValueFromJsValue(js_object); + RETURN_STATUS_IF_FALSE(env, value->IsObject(), napi_invalid_arg); + v8::Local obj = value.As(); + + if (wrap_type == retrievable) { + // If we've already wrapped this object, we error out. + RETURN_STATUS_IF_FALSE(env, + !obj->HasPrivate(context, NAPI_PRIVATE_KEY(context, wrapper)) + .FromJust(), + napi_invalid_arg); + } else if (wrap_type == anonymous) { + // If no finalize callback is provided, we error out. + CHECK_ARG(env, finalize_cb); + } + + v8impl::Reference* reference = nullptr; + if (result != nullptr) { + // The returned reference should be deleted via napi_delete_reference() + // ONLY in response to the finalize callback invocation. (If it is deleted + // before then, then the finalize callback will never be invoked.) + // Therefore a finalize callback is required when returning a reference. + CHECK_ARG(env, finalize_cb); + reference = v8impl::Reference::New( + env, obj, 0, false, finalize_cb, native_object, finalize_hint); + *result = reinterpret_cast(reference); + } else { + // Create a self-deleting reference. + reference = v8impl::Reference::New(env, obj, 0, true, finalize_cb, + native_object, finalize_cb == nullptr ? nullptr : finalize_hint); + } + + if (wrap_type == retrievable) { + CHECK(obj->SetPrivate(context, NAPI_PRIVATE_KEY(context, wrapper), + v8::External::New(env->isolate, reference)).FromJust()); + } + + return GET_RETURN_STATUS(env); +} + +} // end of anonymous namespace + +} // end of namespace v8impl + +// Warning: Keep in-sync with napi_status enum +static +const char* error_messages[] = {nullptr, + "Invalid argument", + "An object was expected", + "A string was expected", + "A string or symbol was expected", + "A function was expected", + "A number was expected", + "A boolean was expected", + "An array was expected", + "Unknown failure", + "An exception is pending", + "The async work item was cancelled", + "napi_escape_handle already called on scope", + "Invalid handle scope usage", + "Invalid callback scope usage", + "Thread-safe function queue is full", + "Thread-safe function handle is closing", + "A bigint was expected", +}; + +napi_status napi_get_last_error_info(napi_env env, + const napi_extended_error_info** result) { + CHECK_ENV(env); + CHECK_ARG(env, result); + + // you must update this assert to reference the last message + // in the napi_status enum each time a new error message is added. + // We don't have a napi_status_last as this would result in an ABI + // change each time a message was added. + static_assert( + NAPI_ARRAYSIZE(error_messages) == napi_bigint_expected + 1, + "Count of error messages must match count of error values"); + CHECK_LE(env->last_error.error_code, napi_callback_scope_mismatch); + + // Wait until someone requests the last error information to fetch the error + // message string + env->last_error.error_message = + error_messages[env->last_error.error_code]; + + *result = &(env->last_error); + return napi_ok; +} + +napi_status napi_create_function(napi_env env, + const char* utf8name, + size_t length, + napi_callback cb, + void* callback_data, + napi_value* result) { + NAPI_PREAMBLE(env); + CHECK_ARG(env, result); + CHECK_ARG(env, cb); + + v8::Isolate* isolate = env->isolate; + v8::Local return_value; + v8::EscapableHandleScope scope(isolate); + v8::Local cbdata = + v8impl::CreateFunctionCallbackData(env, cb, callback_data); + + RETURN_STATUS_IF_FALSE(env, !cbdata.IsEmpty(), napi_generic_failure); + + v8::Local context = env->context(); + v8::MaybeLocal maybe_function = + v8::Function::New(context, + v8impl::FunctionCallbackWrapper::Invoke, + cbdata); + CHECK_MAYBE_EMPTY(env, maybe_function, napi_generic_failure); + + return_value = scope.Escape(maybe_function.ToLocalChecked()); + + if (utf8name != nullptr) { + v8::Local name_string; + CHECK_NEW_FROM_UTF8_LEN(env, name_string, utf8name, length); + return_value->SetName(name_string); + } + + *result = v8impl::JsValueFromV8LocalValue(return_value); + + return GET_RETURN_STATUS(env); +} + +napi_status napi_define_class(napi_env env, + const char* utf8name, + size_t length, + napi_callback constructor, + void* callback_data, + size_t property_count, + const napi_property_descriptor* properties, + napi_value* result) { + NAPI_PREAMBLE(env); + CHECK_ARG(env, result); + CHECK_ARG(env, constructor); + + v8::Isolate* isolate = env->isolate; + + v8::EscapableHandleScope scope(isolate); + v8::Local cbdata = + v8impl::CreateFunctionCallbackData(env, constructor, callback_data); + + RETURN_STATUS_IF_FALSE(env, !cbdata.IsEmpty(), napi_generic_failure); + + v8::Local tpl = v8::FunctionTemplate::New( + isolate, v8impl::FunctionCallbackWrapper::Invoke, cbdata); + + v8::Local name_string; + CHECK_NEW_FROM_UTF8_LEN(env, name_string, utf8name, length); + tpl->SetClassName(name_string); + + size_t static_property_count = 0; + for (size_t i = 0; i < property_count; i++) { + const napi_property_descriptor* p = properties + i; + + if ((p->attributes & napi_static) != 0) { + // Static properties are handled separately below. + static_property_count++; + continue; + } + + v8::Local property_name; + napi_status status = + v8impl::V8NameFromPropertyDescriptor(env, p, &property_name); + + if (status != napi_ok) { + return napi_set_last_error(env, status); + } + + v8::PropertyAttribute attributes = + v8impl::V8PropertyAttributesFromDescriptor(p); + + // This code is similar to that in napi_define_properties(); the + // difference is it applies to a template instead of an object. + if (p->getter != nullptr || p->setter != nullptr) { + v8::Local cbdata = v8impl::CreateAccessorCallbackData( + env, p->getter, p->setter, p->data); + + tpl->PrototypeTemplate()->SetAccessor( + property_name, + p->getter ? v8impl::GetterCallbackWrapper::Invoke : nullptr, + p->setter ? v8impl::SetterCallbackWrapper::Invoke : nullptr, + cbdata, + v8::AccessControl::DEFAULT, + attributes); + } else if (p->method != nullptr) { + v8::Local cbdata = + v8impl::CreateFunctionCallbackData(env, p->method, p->data); + + RETURN_STATUS_IF_FALSE(env, !cbdata.IsEmpty(), napi_generic_failure); + + v8::Local t = + v8::FunctionTemplate::New(isolate, + v8impl::FunctionCallbackWrapper::Invoke, + cbdata, + v8::Signature::New(isolate, tpl)); + + tpl->PrototypeTemplate()->Set(property_name, t, attributes); + } else { + v8::Local value = v8impl::V8LocalValueFromJsValue(p->value); + tpl->PrototypeTemplate()->Set(property_name, value, attributes); + } + } + + v8::Local context = env->context(); + *result = v8impl::JsValueFromV8LocalValue( + scope.Escape(tpl->GetFunction(context).ToLocalChecked())); + + if (static_property_count > 0) { + std::vector static_descriptors; + static_descriptors.reserve(static_property_count); + + for (size_t i = 0; i < property_count; i++) { + const napi_property_descriptor* p = properties + i; + if ((p->attributes & napi_static) != 0) { + static_descriptors.push_back(*p); + } + } + + napi_status status = + napi_define_properties(env, + *result, + static_descriptors.size(), + static_descriptors.data()); + if (status != napi_ok) return status; + } + + return GET_RETURN_STATUS(env); +} + +napi_status napi_get_property_names(napi_env env, + napi_value object, + napi_value* result) { + NAPI_PREAMBLE(env); + CHECK_ARG(env, result); + + v8::Local context = env->context(); + v8::Local obj; + CHECK_TO_OBJECT(env, context, obj, object); + + auto maybe_propertynames = obj->GetPropertyNames(context); + + CHECK_MAYBE_EMPTY(env, maybe_propertynames, napi_generic_failure); + + *result = v8impl::JsValueFromV8LocalValue( + maybe_propertynames.ToLocalChecked()); + return GET_RETURN_STATUS(env); +} + +napi_status napi_set_property(napi_env env, + napi_value object, + napi_value key, + napi_value value) { + NAPI_PREAMBLE(env); + CHECK_ARG(env, key); + CHECK_ARG(env, value); + + v8::Local context = env->context(); + v8::Local obj; + + CHECK_TO_OBJECT(env, context, obj, object); + + v8::Local k = v8impl::V8LocalValueFromJsValue(key); + v8::Local val = v8impl::V8LocalValueFromJsValue(value); + + v8::Maybe set_maybe = obj->Set(context, k, val); + + RETURN_STATUS_IF_FALSE(env, set_maybe.FromMaybe(false), napi_generic_failure); + return GET_RETURN_STATUS(env); +} + +napi_status napi_has_property(napi_env env, + napi_value object, + napi_value key, + bool* result) { + NAPI_PREAMBLE(env); + CHECK_ARG(env, result); + CHECK_ARG(env, key); + + v8::Local context = env->context(); + v8::Local obj; + + CHECK_TO_OBJECT(env, context, obj, object); + + v8::Local k = v8impl::V8LocalValueFromJsValue(key); + v8::Maybe has_maybe = obj->Has(context, k); + + CHECK_MAYBE_NOTHING(env, has_maybe, napi_generic_failure); + + *result = has_maybe.FromMaybe(false); + return GET_RETURN_STATUS(env); +} + +napi_status napi_get_property(napi_env env, + napi_value object, + napi_value key, + napi_value* result) { + NAPI_PREAMBLE(env); + CHECK_ARG(env, key); + CHECK_ARG(env, result); + + v8::Local context = env->context(); + v8::Local k = v8impl::V8LocalValueFromJsValue(key); + v8::Local obj; + + CHECK_TO_OBJECT(env, context, obj, object); + + auto get_maybe = obj->Get(context, k); + + CHECK_MAYBE_EMPTY(env, get_maybe, napi_generic_failure); + + v8::Local val = get_maybe.ToLocalChecked(); + *result = v8impl::JsValueFromV8LocalValue(val); + return GET_RETURN_STATUS(env); +} + +napi_status napi_delete_property(napi_env env, + napi_value object, + napi_value key, + bool* result) { + NAPI_PREAMBLE(env); + CHECK_ARG(env, key); + + v8::Local context = env->context(); + v8::Local k = v8impl::V8LocalValueFromJsValue(key); + v8::Local obj; + + CHECK_TO_OBJECT(env, context, obj, object); + v8::Maybe delete_maybe = obj->Delete(context, k); + CHECK_MAYBE_NOTHING(env, delete_maybe, napi_generic_failure); + + if (result != nullptr) + *result = delete_maybe.FromMaybe(false); + + return GET_RETURN_STATUS(env); +} + +napi_status napi_has_own_property(napi_env env, + napi_value object, + napi_value key, + bool* result) { + NAPI_PREAMBLE(env); + CHECK_ARG(env, key); + + v8::Local context = env->context(); + v8::Local obj; + + CHECK_TO_OBJECT(env, context, obj, object); + v8::Local k = v8impl::V8LocalValueFromJsValue(key); + RETURN_STATUS_IF_FALSE(env, k->IsName(), napi_name_expected); + v8::Maybe has_maybe = obj->HasOwnProperty(context, k.As()); + CHECK_MAYBE_NOTHING(env, has_maybe, napi_generic_failure); + *result = has_maybe.FromMaybe(false); + + return GET_RETURN_STATUS(env); +} + +napi_status napi_set_named_property(napi_env env, + napi_value object, + const char* utf8name, + napi_value value) { + NAPI_PREAMBLE(env); + CHECK_ARG(env, value); + + v8::Local context = env->context(); + v8::Local obj; + + CHECK_TO_OBJECT(env, context, obj, object); + + v8::Local key; + CHECK_NEW_FROM_UTF8(env, key, utf8name); + + v8::Local val = v8impl::V8LocalValueFromJsValue(value); + + v8::Maybe set_maybe = obj->Set(context, key, val); + + RETURN_STATUS_IF_FALSE(env, set_maybe.FromMaybe(false), napi_generic_failure); + return GET_RETURN_STATUS(env); +} + +napi_status napi_has_named_property(napi_env env, + napi_value object, + const char* utf8name, + bool* result) { + NAPI_PREAMBLE(env); + CHECK_ARG(env, result); + + v8::Local context = env->context(); + v8::Local obj; + + CHECK_TO_OBJECT(env, context, obj, object); + + v8::Local key; + CHECK_NEW_FROM_UTF8(env, key, utf8name); + + v8::Maybe has_maybe = obj->Has(context, key); + + CHECK_MAYBE_NOTHING(env, has_maybe, napi_generic_failure); + + *result = has_maybe.FromMaybe(false); + return GET_RETURN_STATUS(env); +} + +napi_status napi_get_named_property(napi_env env, + napi_value object, + const char* utf8name, + napi_value* result) { + NAPI_PREAMBLE(env); + CHECK_ARG(env, result); + + v8::Local context = env->context(); + + v8::Local key; + CHECK_NEW_FROM_UTF8(env, key, utf8name); + + v8::Local obj; + + CHECK_TO_OBJECT(env, context, obj, object); + + auto get_maybe = obj->Get(context, key); + + CHECK_MAYBE_EMPTY(env, get_maybe, napi_generic_failure); + + v8::Local val = get_maybe.ToLocalChecked(); + *result = v8impl::JsValueFromV8LocalValue(val); + return GET_RETURN_STATUS(env); +} + +napi_status napi_set_element(napi_env env, + napi_value object, + uint32_t index, + napi_value value) { + NAPI_PREAMBLE(env); + CHECK_ARG(env, value); + + v8::Local context = env->context(); + v8::Local obj; + + CHECK_TO_OBJECT(env, context, obj, object); + + v8::Local val = v8impl::V8LocalValueFromJsValue(value); + auto set_maybe = obj->Set(context, index, val); + + RETURN_STATUS_IF_FALSE(env, set_maybe.FromMaybe(false), napi_generic_failure); + + return GET_RETURN_STATUS(env); +} + +napi_status napi_has_element(napi_env env, + napi_value object, + uint32_t index, + bool* result) { + NAPI_PREAMBLE(env); + CHECK_ARG(env, result); + + v8::Local context = env->context(); + v8::Local obj; + + CHECK_TO_OBJECT(env, context, obj, object); + + v8::Maybe has_maybe = obj->Has(context, index); + + CHECK_MAYBE_NOTHING(env, has_maybe, napi_generic_failure); + + *result = has_maybe.FromMaybe(false); + return GET_RETURN_STATUS(env); +} + +napi_status napi_get_element(napi_env env, + napi_value object, + uint32_t index, + napi_value* result) { + NAPI_PREAMBLE(env); + CHECK_ARG(env, result); + + v8::Local context = env->context(); + v8::Local obj; + + CHECK_TO_OBJECT(env, context, obj, object); + + auto get_maybe = obj->Get(context, index); + + CHECK_MAYBE_EMPTY(env, get_maybe, napi_generic_failure); + + *result = v8impl::JsValueFromV8LocalValue(get_maybe.ToLocalChecked()); + return GET_RETURN_STATUS(env); +} + +napi_status napi_delete_element(napi_env env, + napi_value object, + uint32_t index, + bool* result) { + NAPI_PREAMBLE(env); + + v8::Local context = env->context(); + v8::Local obj; + + CHECK_TO_OBJECT(env, context, obj, object); + v8::Maybe delete_maybe = obj->Delete(context, index); + CHECK_MAYBE_NOTHING(env, delete_maybe, napi_generic_failure); + + if (result != nullptr) + *result = delete_maybe.FromMaybe(false); + + return GET_RETURN_STATUS(env); +} + +napi_status napi_define_properties(napi_env env, + napi_value object, + size_t property_count, + const napi_property_descriptor* properties) { + NAPI_PREAMBLE(env); + if (property_count > 0) { + CHECK_ARG(env, properties); + } + + v8::Local context = env->context(); + + v8::Local obj; + CHECK_TO_OBJECT(env, context, obj, object); + + for (size_t i = 0; i < property_count; i++) { + const napi_property_descriptor* p = &properties[i]; + + v8::Local property_name; + napi_status status = + v8impl::V8NameFromPropertyDescriptor(env, p, &property_name); + + if (status != napi_ok) { + return napi_set_last_error(env, status); + } + + v8::PropertyAttribute attributes = + v8impl::V8PropertyAttributesFromDescriptor(p); + + if (p->getter != nullptr || p->setter != nullptr) { + v8::Local cbdata = v8impl::CreateAccessorCallbackData( + env, + p->getter, + p->setter, + p->data); + + auto set_maybe = obj->SetAccessor( + context, + property_name, + p->getter ? v8impl::GetterCallbackWrapper::Invoke : nullptr, + p->setter ? v8impl::SetterCallbackWrapper::Invoke : nullptr, + cbdata, + v8::AccessControl::DEFAULT, + attributes); + + if (!set_maybe.FromMaybe(false)) { + return napi_set_last_error(env, napi_invalid_arg); + } + } else if (p->method != nullptr) { + v8::Local cbdata = + v8impl::CreateFunctionCallbackData(env, p->method, p->data); + + CHECK_MAYBE_EMPTY(env, cbdata, napi_generic_failure); + + v8::MaybeLocal maybe_fn = + v8::Function::New(context, + v8impl::FunctionCallbackWrapper::Invoke, + cbdata); + + CHECK_MAYBE_EMPTY(env, maybe_fn, napi_generic_failure); + + auto define_maybe = obj->DefineOwnProperty( + context, property_name, maybe_fn.ToLocalChecked(), attributes); + + if (!define_maybe.FromMaybe(false)) { + return napi_set_last_error(env, napi_generic_failure); + } + } else { + v8::Local value = v8impl::V8LocalValueFromJsValue(p->value); + + auto define_maybe = + obj->DefineOwnProperty(context, property_name, value, attributes); + + if (!define_maybe.FromMaybe(false)) { + return napi_set_last_error(env, napi_invalid_arg); + } + } + } + + return GET_RETURN_STATUS(env); +} + +napi_status napi_is_array(napi_env env, napi_value value, bool* result) { + CHECK_ENV(env); + CHECK_ARG(env, value); + CHECK_ARG(env, result); + + v8::Local val = v8impl::V8LocalValueFromJsValue(value); + + *result = val->IsArray(); + return napi_clear_last_error(env); +} + +napi_status napi_get_array_length(napi_env env, + napi_value value, + uint32_t* result) { + NAPI_PREAMBLE(env); + CHECK_ARG(env, value); + CHECK_ARG(env, result); + + v8::Local val = v8impl::V8LocalValueFromJsValue(value); + RETURN_STATUS_IF_FALSE(env, val->IsArray(), napi_array_expected); + + v8::Local arr = val.As(); + *result = arr->Length(); + + return GET_RETURN_STATUS(env); +} + +napi_status napi_strict_equals(napi_env env, + napi_value lhs, + napi_value rhs, + bool* result) { + NAPI_PREAMBLE(env); + CHECK_ARG(env, lhs); + CHECK_ARG(env, rhs); + CHECK_ARG(env, result); + + v8::Local a = v8impl::V8LocalValueFromJsValue(lhs); + v8::Local b = v8impl::V8LocalValueFromJsValue(rhs); + + *result = a->StrictEquals(b); + return GET_RETURN_STATUS(env); +} + +napi_status napi_get_prototype(napi_env env, + napi_value object, + napi_value* result) { + NAPI_PREAMBLE(env); + CHECK_ARG(env, result); + + v8::Local context = env->context(); + + v8::Local obj; + CHECK_TO_OBJECT(env, context, obj, object); + + v8::Local val = obj->GetPrototype(); + *result = v8impl::JsValueFromV8LocalValue(val); + return GET_RETURN_STATUS(env); +} + +napi_status napi_create_object(napi_env env, napi_value* result) { + CHECK_ENV(env); + CHECK_ARG(env, result); + + *result = v8impl::JsValueFromV8LocalValue( + v8::Object::New(env->isolate)); + + return napi_clear_last_error(env); +} + +napi_status napi_create_array(napi_env env, napi_value* result) { + CHECK_ENV(env); + CHECK_ARG(env, result); + + *result = v8impl::JsValueFromV8LocalValue( + v8::Array::New(env->isolate)); + + return napi_clear_last_error(env); +} + +napi_status napi_create_array_with_length(napi_env env, + size_t length, + napi_value* result) { + CHECK_ENV(env); + CHECK_ARG(env, result); + + *result = v8impl::JsValueFromV8LocalValue( + v8::Array::New(env->isolate, length)); + + return napi_clear_last_error(env); +} + +napi_status napi_create_string_latin1(napi_env env, + const char* str, + size_t length, + napi_value* result) { + CHECK_ENV(env); + CHECK_ARG(env, result); + + auto isolate = env->isolate; + auto str_maybe = + v8::String::NewFromOneByte(isolate, + reinterpret_cast(str), + v8::NewStringType::kInternalized, + length); + CHECK_MAYBE_EMPTY(env, str_maybe, napi_generic_failure); + + *result = v8impl::JsValueFromV8LocalValue(str_maybe.ToLocalChecked()); + return napi_clear_last_error(env); +} + +napi_status napi_create_string_utf8(napi_env env, + const char* str, + size_t length, + napi_value* result) { + CHECK_ENV(env); + CHECK_ARG(env, result); + + v8::Local s; + CHECK_NEW_FROM_UTF8_LEN(env, s, str, length); + + *result = v8impl::JsValueFromV8LocalValue(s); + return napi_clear_last_error(env); +} + +napi_status napi_create_string_utf16(napi_env env, + const char16_t* str, + size_t length, + napi_value* result) { + CHECK_ENV(env); + CHECK_ARG(env, result); + + auto isolate = env->isolate; + auto str_maybe = + v8::String::NewFromTwoByte(isolate, + reinterpret_cast(str), + v8::NewStringType::kInternalized, + length); + CHECK_MAYBE_EMPTY(env, str_maybe, napi_generic_failure); + + *result = v8impl::JsValueFromV8LocalValue(str_maybe.ToLocalChecked()); + return napi_clear_last_error(env); +} + +napi_status napi_create_double(napi_env env, + double value, + napi_value* result) { + CHECK_ENV(env); + CHECK_ARG(env, result); + + *result = v8impl::JsValueFromV8LocalValue( + v8::Number::New(env->isolate, value)); + + return napi_clear_last_error(env); +} + +napi_status napi_create_int32(napi_env env, + int32_t value, + napi_value* result) { + CHECK_ENV(env); + CHECK_ARG(env, result); + + *result = v8impl::JsValueFromV8LocalValue( + v8::Integer::New(env->isolate, value)); + + return napi_clear_last_error(env); +} + +napi_status napi_create_uint32(napi_env env, + uint32_t value, + napi_value* result) { + CHECK_ENV(env); + CHECK_ARG(env, result); + + *result = v8impl::JsValueFromV8LocalValue( + v8::Integer::NewFromUnsigned(env->isolate, value)); + + return napi_clear_last_error(env); +} + +napi_status napi_create_int64(napi_env env, + int64_t value, + napi_value* result) { + CHECK_ENV(env); + CHECK_ARG(env, result); + + *result = v8impl::JsValueFromV8LocalValue( + v8::Number::New(env->isolate, static_cast(value))); + + return napi_clear_last_error(env); +} + +napi_status napi_create_bigint_int64(napi_env env, + int64_t value, + napi_value* result) { + CHECK_ENV(env); + CHECK_ARG(env, result); + + *result = v8impl::JsValueFromV8LocalValue( + v8::BigInt::New(env->isolate, value)); + + return napi_clear_last_error(env); +} + +napi_status napi_create_bigint_uint64(napi_env env, + uint64_t value, + napi_value* result) { + CHECK_ENV(env); + CHECK_ARG(env, result); + + *result = v8impl::JsValueFromV8LocalValue( + v8::BigInt::NewFromUnsigned(env->isolate, value)); + + return napi_clear_last_error(env); +} + +napi_status napi_create_bigint_words(napi_env env, + int sign_bit, + size_t word_count, + const uint64_t* words, + napi_value* result) { + NAPI_PREAMBLE(env); + CHECK_ARG(env, words); + CHECK_ARG(env, result); + + v8::Local context = env->context(); + + if (word_count > INT_MAX) { + napi_throw_range_error(env, nullptr, "Maximum BigInt size exceeded"); + return napi_set_last_error(env, napi_pending_exception); + } + + v8::MaybeLocal b = v8::BigInt::NewFromWords( + context, sign_bit, word_count, words); + + if (try_catch.HasCaught()) { + return napi_set_last_error(env, napi_pending_exception); + } else { + CHECK_MAYBE_EMPTY(env, b, napi_generic_failure); + *result = v8impl::JsValueFromV8LocalValue(b.ToLocalChecked()); + return napi_clear_last_error(env); + } +} + +napi_status napi_get_boolean(napi_env env, bool value, napi_value* result) { + CHECK_ENV(env); + CHECK_ARG(env, result); + + v8::Isolate* isolate = env->isolate; + + if (value) { + *result = v8impl::JsValueFromV8LocalValue(v8::True(isolate)); + } else { + *result = v8impl::JsValueFromV8LocalValue(v8::False(isolate)); + } + + return napi_clear_last_error(env); +} + +napi_status napi_create_symbol(napi_env env, + napi_value description, + napi_value* result) { + CHECK_ENV(env); + CHECK_ARG(env, result); + + v8::Isolate* isolate = env->isolate; + + if (description == nullptr) { + *result = v8impl::JsValueFromV8LocalValue(v8::Symbol::New(isolate)); + } else { + v8::Local desc = v8impl::V8LocalValueFromJsValue(description); + RETURN_STATUS_IF_FALSE(env, desc->IsString(), napi_string_expected); + + *result = v8impl::JsValueFromV8LocalValue( + v8::Symbol::New(isolate, desc.As())); + } + + return napi_clear_last_error(env); +} + +static inline napi_status set_error_code(napi_env env, + v8::Local error, + napi_value code, + const char* code_cstring) { + if ((code != nullptr) || (code_cstring != nullptr)) { + v8::Isolate* isolate = env->isolate; + v8::Local context = env->context(); + v8::Local err_object = error.As(); + + v8::Local code_value = v8impl::V8LocalValueFromJsValue(code); + if (code != nullptr) { + code_value = v8impl::V8LocalValueFromJsValue(code); + RETURN_STATUS_IF_FALSE(env, code_value->IsString(), napi_string_expected); + } else { + CHECK_NEW_FROM_UTF8(env, code_value, code_cstring); + } + + v8::Local code_key; + CHECK_NEW_FROM_UTF8(env, code_key, "code"); + + v8::Maybe set_maybe = err_object->Set(context, code_key, code_value); + RETURN_STATUS_IF_FALSE(env, + set_maybe.FromMaybe(false), + napi_generic_failure); + + // now update the name to be "name [code]" where name is the + // original name and code is the code associated with the Error + v8::Local name_string; + CHECK_NEW_FROM_UTF8(env, name_string, ""); + v8::Local name_key; + CHECK_NEW_FROM_UTF8(env, name_key, "name"); + + auto maybe_name = err_object->Get(context, name_key); + if (!maybe_name.IsEmpty()) { + v8::Local name = maybe_name.ToLocalChecked(); + if (name->IsString()) { + name_string = + v8::String::Concat(isolate, name_string, name.As()); + } + } + name_string = v8::String::Concat( + isolate, name_string, NAPI_FIXED_ONE_BYTE_STRING(isolate, " [")); + name_string = + v8::String::Concat(isolate, name_string, code_value.As()); + name_string = v8::String::Concat( + isolate, name_string, NAPI_FIXED_ONE_BYTE_STRING(isolate, "]")); + + set_maybe = err_object->Set(context, name_key, name_string); + RETURN_STATUS_IF_FALSE(env, + set_maybe.FromMaybe(false), + napi_generic_failure); + } + return napi_ok; +} + +napi_status napi_create_error(napi_env env, + napi_value code, + napi_value msg, + napi_value* result) { + CHECK_ENV(env); + CHECK_ARG(env, msg); + CHECK_ARG(env, result); + + v8::Local message_value = v8impl::V8LocalValueFromJsValue(msg); + RETURN_STATUS_IF_FALSE(env, message_value->IsString(), napi_string_expected); + + v8::Local error_obj = + v8::Exception::Error(message_value.As()); + napi_status status = set_error_code(env, error_obj, code, nullptr); + if (status != napi_ok) return status; + + *result = v8impl::JsValueFromV8LocalValue(error_obj); + + return napi_clear_last_error(env); +} + +napi_status napi_create_type_error(napi_env env, + napi_value code, + napi_value msg, + napi_value* result) { + CHECK_ENV(env); + CHECK_ARG(env, msg); + CHECK_ARG(env, result); + + v8::Local message_value = v8impl::V8LocalValueFromJsValue(msg); + RETURN_STATUS_IF_FALSE(env, message_value->IsString(), napi_string_expected); + + v8::Local error_obj = + v8::Exception::TypeError(message_value.As()); + napi_status status = set_error_code(env, error_obj, code, nullptr); + if (status != napi_ok) return status; + + *result = v8impl::JsValueFromV8LocalValue(error_obj); + + return napi_clear_last_error(env); +} + +napi_status napi_create_range_error(napi_env env, + napi_value code, + napi_value msg, + napi_value* result) { + CHECK_ENV(env); + CHECK_ARG(env, msg); + CHECK_ARG(env, result); + + v8::Local message_value = v8impl::V8LocalValueFromJsValue(msg); + RETURN_STATUS_IF_FALSE(env, message_value->IsString(), napi_string_expected); + + v8::Local error_obj = + v8::Exception::RangeError(message_value.As()); + napi_status status = set_error_code(env, error_obj, code, nullptr); + if (status != napi_ok) return status; + + *result = v8impl::JsValueFromV8LocalValue(error_obj); + + return napi_clear_last_error(env); +} + +napi_status napi_typeof(napi_env env, + napi_value value, + napi_valuetype* result) { + // Omit NAPI_PREAMBLE and GET_RETURN_STATUS because V8 calls here cannot throw + // JS exceptions. + CHECK_ENV(env); + CHECK_ARG(env, value); + CHECK_ARG(env, result); + + v8::Local v = v8impl::V8LocalValueFromJsValue(value); + + if (v->IsNumber()) { + *result = napi_number; + } else if (v->IsBigInt()) { + *result = napi_bigint; + } else if (v->IsString()) { + *result = napi_string; + } else if (v->IsFunction()) { + // This test has to come before IsObject because IsFunction + // implies IsObject + *result = napi_function; + } else if (v->IsExternal()) { + // This test has to come before IsObject because IsExternal + // implies IsObject + *result = napi_external; + } else if (v->IsObject()) { + *result = napi_object; + } else if (v->IsBoolean()) { + *result = napi_boolean; + } else if (v->IsUndefined()) { + *result = napi_undefined; + } else if (v->IsSymbol()) { + *result = napi_symbol; + } else if (v->IsNull()) { + *result = napi_null; + } else { + // Should not get here unless V8 has added some new kind of value. + return napi_set_last_error(env, napi_invalid_arg); + } + + return napi_clear_last_error(env); +} + +napi_status napi_get_undefined(napi_env env, napi_value* result) { + CHECK_ENV(env); + CHECK_ARG(env, result); + + *result = v8impl::JsValueFromV8LocalValue( + v8::Undefined(env->isolate)); + + return napi_clear_last_error(env); +} + +napi_status napi_get_null(napi_env env, napi_value* result) { + CHECK_ENV(env); + CHECK_ARG(env, result); + + *result = v8impl::JsValueFromV8LocalValue( + v8::Null(env->isolate)); + + return napi_clear_last_error(env); +} + +// Gets all callback info in a single call. (Ugly, but faster.) +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. + CHECK_ENV(env); + CHECK_ARG(env, cbinfo); + + v8impl::CallbackWrapper* info = + reinterpret_cast(cbinfo); + + if (argv != nullptr) { + CHECK_ARG(env, argc); + info->Args(argv, *argc); + } + if (argc != nullptr) { + *argc = info->ArgsLength(); + } + if (this_arg != nullptr) { + *this_arg = info->This(); + } + if (data != nullptr) { + *data = info->Data(); + } + + return napi_clear_last_error(env); +} + +napi_status napi_get_new_target(napi_env env, + napi_callback_info cbinfo, + napi_value* result) { + CHECK_ENV(env); + CHECK_ARG(env, cbinfo); + CHECK_ARG(env, result); + + v8impl::CallbackWrapper* info = + reinterpret_cast(cbinfo); + + *result = info->GetNewTarget(); + return napi_clear_last_error(env); +} + +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_PREAMBLE(env); + CHECK_ARG(env, recv); + if (argc > 0) { + CHECK_ARG(env, argv); + } + + v8::Local context = env->context(); + + v8::Local v8recv = v8impl::V8LocalValueFromJsValue(recv); + + v8::Local v8func; + CHECK_TO_FUNCTION(env, v8func, func); + + auto maybe = v8func->Call(context, v8recv, argc, + reinterpret_cast*>(const_cast(argv))); + + if (try_catch.HasCaught()) { + return napi_set_last_error(env, napi_pending_exception); + } else { + if (result != nullptr) { + CHECK_MAYBE_EMPTY(env, maybe, napi_generic_failure); + *result = v8impl::JsValueFromV8LocalValue(maybe.ToLocalChecked()); + } + return napi_clear_last_error(env); + } +} + +napi_status napi_get_global(napi_env env, napi_value* result) { + CHECK_ENV(env); + CHECK_ARG(env, result); + + *result = v8impl::JsValueFromV8LocalValue(env->context()->Global()); + + return napi_clear_last_error(env); +} + +napi_status napi_throw(napi_env env, napi_value error) { + NAPI_PREAMBLE(env); + CHECK_ARG(env, error); + + v8::Isolate* isolate = env->isolate; + + isolate->ThrowException(v8impl::V8LocalValueFromJsValue(error)); + // any VM calls after this point and before returning + // to the javascript invoker will fail + return napi_clear_last_error(env); +} + +napi_status napi_throw_error(napi_env env, + const char* code, + const char* msg) { + NAPI_PREAMBLE(env); + + v8::Isolate* isolate = env->isolate; + v8::Local str; + CHECK_NEW_FROM_UTF8(env, str, msg); + + v8::Local error_obj = v8::Exception::Error(str); + napi_status status = set_error_code(env, error_obj, nullptr, code); + if (status != napi_ok) return status; + + isolate->ThrowException(error_obj); + // any VM calls after this point and before returning + // to the javascript invoker will fail + return napi_clear_last_error(env); +} + +napi_status napi_throw_type_error(napi_env env, + const char* code, + const char* msg) { + NAPI_PREAMBLE(env); + + v8::Isolate* isolate = env->isolate; + v8::Local str; + CHECK_NEW_FROM_UTF8(env, str, msg); + + v8::Local error_obj = v8::Exception::TypeError(str); + napi_status status = set_error_code(env, error_obj, nullptr, code); + if (status != napi_ok) return status; + + isolate->ThrowException(error_obj); + // any VM calls after this point and before returning + // to the javascript invoker will fail + return napi_clear_last_error(env); +} + +napi_status napi_throw_range_error(napi_env env, + const char* code, + const char* msg) { + NAPI_PREAMBLE(env); + + v8::Isolate* isolate = env->isolate; + v8::Local str; + CHECK_NEW_FROM_UTF8(env, str, msg); + + v8::Local error_obj = v8::Exception::RangeError(str); + napi_status status = set_error_code(env, error_obj, nullptr, code); + if (status != napi_ok) return status; + + isolate->ThrowException(error_obj); + // any VM calls after this point and before returning + // to the javascript invoker will fail + return napi_clear_last_error(env); +} + +napi_status napi_is_error(napi_env env, napi_value value, bool* result) { + // Omit NAPI_PREAMBLE and GET_RETURN_STATUS because V8 calls here cannot + // throw JS exceptions. + CHECK_ENV(env); + CHECK_ARG(env, value); + CHECK_ARG(env, result); + + v8::Local val = v8impl::V8LocalValueFromJsValue(value); + *result = val->IsNativeError(); + + return napi_clear_last_error(env); +} + +napi_status napi_get_value_double(napi_env env, + napi_value value, + double* result) { + // Omit NAPI_PREAMBLE and GET_RETURN_STATUS because V8 calls here cannot throw + // JS exceptions. + CHECK_ENV(env); + CHECK_ARG(env, value); + CHECK_ARG(env, result); + + v8::Local val = v8impl::V8LocalValueFromJsValue(value); + RETURN_STATUS_IF_FALSE(env, val->IsNumber(), napi_number_expected); + + *result = val.As()->Value(); + + return napi_clear_last_error(env); +} + +napi_status napi_get_value_int32(napi_env env, + napi_value value, + int32_t* result) { + // Omit NAPI_PREAMBLE and GET_RETURN_STATUS because V8 calls here cannot throw + // JS exceptions. + CHECK_ENV(env); + CHECK_ARG(env, value); + CHECK_ARG(env, result); + + v8::Local val = v8impl::V8LocalValueFromJsValue(value); + + if (val->IsInt32()) { + *result = val.As()->Value(); + } else { + RETURN_STATUS_IF_FALSE(env, val->IsNumber(), napi_number_expected); + + // Empty context: https://github.com/nodejs/node/issues/14379 + v8::Local context; + *result = val->Int32Value(context).FromJust(); + } + + return napi_clear_last_error(env); +} + +napi_status napi_get_value_uint32(napi_env env, + napi_value value, + uint32_t* result) { + // Omit NAPI_PREAMBLE and GET_RETURN_STATUS because V8 calls here cannot throw + // JS exceptions. + CHECK_ENV(env); + CHECK_ARG(env, value); + CHECK_ARG(env, result); + + v8::Local val = v8impl::V8LocalValueFromJsValue(value); + + if (val->IsUint32()) { + *result = val.As()->Value(); + } else { + RETURN_STATUS_IF_FALSE(env, val->IsNumber(), napi_number_expected); + + // Empty context: https://github.com/nodejs/node/issues/14379 + v8::Local context; + *result = val->Uint32Value(context).FromJust(); + } + + return napi_clear_last_error(env); +} + +napi_status napi_get_value_int64(napi_env env, + napi_value value, + int64_t* result) { + // Omit NAPI_PREAMBLE and GET_RETURN_STATUS because V8 calls here cannot throw + // JS exceptions. + CHECK_ENV(env); + CHECK_ARG(env, value); + CHECK_ARG(env, result); + + v8::Local val = v8impl::V8LocalValueFromJsValue(value); + + // This is still a fast path very likely to be taken. + if (val->IsInt32()) { + *result = val.As()->Value(); + return napi_clear_last_error(env); + } + + RETURN_STATUS_IF_FALSE(env, val->IsNumber(), napi_number_expected); + + // v8::Value::IntegerValue() converts NaN, +Inf, and -Inf to INT64_MIN, + // inconsistent with v8::Value::Int32Value() which converts those values to 0. + // Special-case all non-finite values to match that behavior. + double doubleValue = val.As()->Value(); + if (std::isfinite(doubleValue)) { + // Empty context: https://github.com/nodejs/node/issues/14379 + v8::Local context; + *result = val->IntegerValue(context).FromJust(); + } else { + *result = 0; + } + + return napi_clear_last_error(env); +} + +napi_status napi_get_value_bigint_int64(napi_env env, + napi_value value, + int64_t* result, + bool* lossless) { + CHECK_ENV(env); + CHECK_ARG(env, value); + CHECK_ARG(env, result); + CHECK_ARG(env, lossless); + + v8::Local val = v8impl::V8LocalValueFromJsValue(value); + + RETURN_STATUS_IF_FALSE(env, val->IsBigInt(), napi_bigint_expected); + + *result = val.As()->Int64Value(lossless); + + return napi_clear_last_error(env); +} + +napi_status napi_get_value_bigint_uint64(napi_env env, + napi_value value, + uint64_t* result, + bool* lossless) { + CHECK_ENV(env); + CHECK_ARG(env, value); + CHECK_ARG(env, result); + CHECK_ARG(env, lossless); + + v8::Local val = v8impl::V8LocalValueFromJsValue(value); + + RETURN_STATUS_IF_FALSE(env, val->IsBigInt(), napi_bigint_expected); + + *result = val.As()->Uint64Value(lossless); + + return napi_clear_last_error(env); +} + +napi_status napi_get_value_bigint_words(napi_env env, + napi_value value, + int* sign_bit, + size_t* word_count, + uint64_t* words) { + CHECK_ENV(env); + CHECK_ARG(env, value); + CHECK_ARG(env, word_count); + + v8::Local val = v8impl::V8LocalValueFromJsValue(value); + + RETURN_STATUS_IF_FALSE(env, val->IsBigInt(), napi_bigint_expected); + + v8::Local big = val.As(); + + int word_count_int = *word_count; + + if (sign_bit == nullptr && words == nullptr) { + word_count_int = big->WordCount(); + } else { + CHECK_ARG(env, sign_bit); + CHECK_ARG(env, words); + big->ToWordsArray(sign_bit, &word_count_int, words); + } + + *word_count = word_count_int; + + return napi_clear_last_error(env); +} + +napi_status napi_get_value_bool(napi_env env, napi_value value, bool* result) { + // Omit NAPI_PREAMBLE and GET_RETURN_STATUS because V8 calls here cannot throw + // JS exceptions. + CHECK_ENV(env); + CHECK_ARG(env, value); + CHECK_ARG(env, result); + + v8::Local val = v8impl::V8LocalValueFromJsValue(value); + RETURN_STATUS_IF_FALSE(env, val->IsBoolean(), napi_boolean_expected); + + *result = val.As()->Value(); + + return napi_clear_last_error(env); +} + +// Copies a JavaScript string into a LATIN-1 string buffer. The result is the +// number of bytes (excluding the null terminator) copied into buf. +// A sufficient buffer size should be greater than the length of string, +// reserving space for null terminator. +// If bufsize is insufficient, the string will be truncated and null terminated. +// If buf is NULL, this method returns the length of the string (in bytes) +// via the result parameter. +// The result argument is optional unless buf is NULL. +napi_status napi_get_value_string_latin1(napi_env env, + napi_value value, + char* buf, + size_t bufsize, + size_t* result) { + CHECK_ENV(env); + CHECK_ARG(env, value); + + v8::Local val = v8impl::V8LocalValueFromJsValue(value); + RETURN_STATUS_IF_FALSE(env, val->IsString(), napi_string_expected); + + if (!buf) { + CHECK_ARG(env, result); + *result = val.As()->Length(); + } else { + int copied = + val.As()->WriteOneByte(env->isolate, + reinterpret_cast(buf), + 0, + bufsize - 1, + v8::String::NO_NULL_TERMINATION); + + buf[copied] = '\0'; + if (result != nullptr) { + *result = copied; + } + } + + return napi_clear_last_error(env); +} + +// Copies a JavaScript string into a UTF-8 string buffer. The result is the +// number of bytes (excluding the null terminator) copied into buf. +// A sufficient buffer size should be greater than the length of string, +// reserving space for null terminator. +// If bufsize is insufficient, the string will be truncated and null terminated. +// If buf is NULL, this method returns the length of the string (in bytes) +// via the result parameter. +// The result argument is optional unless buf is NULL. +napi_status napi_get_value_string_utf8(napi_env env, + napi_value value, + char* buf, + size_t bufsize, + size_t* result) { + CHECK_ENV(env); + CHECK_ARG(env, value); + + v8::Local val = v8impl::V8LocalValueFromJsValue(value); + RETURN_STATUS_IF_FALSE(env, val->IsString(), napi_string_expected); + + if (!buf) { + CHECK_ARG(env, result); + *result = val.As()->Utf8Length(env->isolate); + } else { + int copied = val.As()->WriteUtf8( + env->isolate, + buf, + bufsize - 1, + nullptr, + v8::String::REPLACE_INVALID_UTF8 | v8::String::NO_NULL_TERMINATION); + + buf[copied] = '\0'; + if (result != nullptr) { + *result = copied; + } + } + + return napi_clear_last_error(env); +} + +// Copies a JavaScript string into a UTF-16 string buffer. The result is the +// number of 2-byte code units (excluding the null terminator) copied into buf. +// A sufficient buffer size should be greater than the length of string, +// reserving space for null terminator. +// If bufsize is insufficient, the string will be truncated and null terminated. +// If buf is NULL, this method returns the length of the string (in 2-byte +// code units) via the result parameter. +// The result argument is optional unless buf is NULL. +napi_status napi_get_value_string_utf16(napi_env env, + napi_value value, + char16_t* buf, + size_t bufsize, + size_t* result) { + CHECK_ENV(env); + CHECK_ARG(env, value); + + v8::Local val = v8impl::V8LocalValueFromJsValue(value); + RETURN_STATUS_IF_FALSE(env, val->IsString(), napi_string_expected); + + if (!buf) { + CHECK_ARG(env, result); + // V8 assumes UTF-16 length is the same as the number of characters. + *result = val.As()->Length(); + } else { + int copied = val.As()->Write(env->isolate, + reinterpret_cast(buf), + 0, + bufsize - 1, + v8::String::NO_NULL_TERMINATION); + + buf[copied] = '\0'; + if (result != nullptr) { + *result = copied; + } + } + + return napi_clear_last_error(env); +} + +napi_status napi_coerce_to_object(napi_env env, + napi_value value, + napi_value* result) { + NAPI_PREAMBLE(env); + CHECK_ARG(env, value); + CHECK_ARG(env, result); + + v8::Local context = env->context(); + v8::Local obj; + CHECK_TO_OBJECT(env, context, obj, value); + + *result = v8impl::JsValueFromV8LocalValue(obj); + return GET_RETURN_STATUS(env); +} + +napi_status napi_coerce_to_bool(napi_env env, + napi_value value, + napi_value* result) { + NAPI_PREAMBLE(env); + CHECK_ARG(env, value); + CHECK_ARG(env, result); + + v8::Local context = env->context(); + v8::Local b; + + CHECK_TO_BOOL(env, context, b, value); + + *result = v8impl::JsValueFromV8LocalValue(b); + return GET_RETURN_STATUS(env); +} + +napi_status napi_coerce_to_number(napi_env env, + napi_value value, + napi_value* result) { + NAPI_PREAMBLE(env); + CHECK_ARG(env, value); + CHECK_ARG(env, result); + + v8::Local context = env->context(); + v8::Local num; + + CHECK_TO_NUMBER(env, context, num, value); + + *result = v8impl::JsValueFromV8LocalValue(num); + return GET_RETURN_STATUS(env); +} + +napi_status napi_coerce_to_string(napi_env env, + napi_value value, + napi_value* result) { + NAPI_PREAMBLE(env); + CHECK_ARG(env, value); + CHECK_ARG(env, result); + + v8::Local context = env->context(); + v8::Local str; + + CHECK_TO_STRING(env, context, str, value); + + *result = v8impl::JsValueFromV8LocalValue(str); + return GET_RETURN_STATUS(env); +} + +napi_status napi_wrap(napi_env env, + napi_value js_object, + void* native_object, + napi_finalize finalize_cb, + void* finalize_hint, + napi_ref* result) { + return v8impl::Wrap(env, + js_object, + native_object, + finalize_cb, + finalize_hint, + result); +} + +napi_status napi_unwrap(napi_env env, napi_value obj, void** result) { + return v8impl::Unwrap(env, obj, result, v8impl::KeepWrap); +} + +napi_status napi_remove_wrap(napi_env env, napi_value obj, void** result) { + return v8impl::Unwrap(env, obj, result, v8impl::RemoveWrap); +} + +napi_status napi_create_external(napi_env env, + void* data, + napi_finalize finalize_cb, + void* finalize_hint, + napi_value* result) { + NAPI_PREAMBLE(env); + CHECK_ARG(env, result); + + v8::Isolate* isolate = env->isolate; + + v8::Local external_value = v8::External::New(isolate, data); + + // The Reference object will delete itself after invoking the finalizer + // callback. + v8impl::Reference::New(env, + external_value, + 0, + true, + finalize_cb, + data, + finalize_hint); + + *result = v8impl::JsValueFromV8LocalValue(external_value); + + return napi_clear_last_error(env); +} + +napi_status napi_get_value_external(napi_env env, + napi_value value, + void** result) { + CHECK_ENV(env); + CHECK_ARG(env, value); + CHECK_ARG(env, result); + + v8::Local val = v8impl::V8LocalValueFromJsValue(value); + RETURN_STATUS_IF_FALSE(env, val->IsExternal(), napi_invalid_arg); + + v8::Local external_value = val.As(); + *result = external_value->Value(); + + return napi_clear_last_error(env); +} + +// Set initial_refcount to 0 for a weak reference, >0 for a strong reference. +napi_status napi_create_reference(napi_env env, + napi_value value, + uint32_t initial_refcount, + napi_ref* result) { + // Omit NAPI_PREAMBLE and GET_RETURN_STATUS because V8 calls here cannot throw + // JS exceptions. + CHECK_ENV(env); + CHECK_ARG(env, value); + CHECK_ARG(env, result); + + v8::Local v8_value = v8impl::V8LocalValueFromJsValue(value); + + if (!(v8_value->IsObject() || v8_value->IsFunction())) { + return napi_set_last_error(env, napi_object_expected); + } + + v8impl::Reference* reference = + v8impl::Reference::New(env, v8_value, initial_refcount, false); + + *result = reinterpret_cast(reference); + return napi_clear_last_error(env); +} + +// Deletes a reference. The referenced value is released, and may be GC'd unless +// there are other references to it. +napi_status napi_delete_reference(napi_env env, napi_ref ref) { + // Omit NAPI_PREAMBLE and GET_RETURN_STATUS because V8 calls here cannot throw + // JS exceptions. + CHECK_ENV(env); + CHECK_ARG(env, ref); + + v8impl::Reference::Delete(reinterpret_cast(ref)); + + return napi_clear_last_error(env); +} + +// 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_status napi_reference_ref(napi_env env, napi_ref ref, uint32_t* result) { + // Omit NAPI_PREAMBLE and GET_RETURN_STATUS because V8 calls here cannot throw + // JS exceptions. + CHECK_ENV(env); + CHECK_ARG(env, ref); + + v8impl::Reference* reference = reinterpret_cast(ref); + uint32_t count = reference->Ref(); + + if (result != nullptr) { + *result = count; + } + + return napi_clear_last_error(env); +} + +// 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_status napi_reference_unref(napi_env env, napi_ref ref, uint32_t* result) { + // Omit NAPI_PREAMBLE and GET_RETURN_STATUS because V8 calls here cannot throw + // JS exceptions. + CHECK_ENV(env); + CHECK_ARG(env, ref); + + v8impl::Reference* reference = reinterpret_cast(ref); + + if (reference->RefCount() == 0) { + return napi_set_last_error(env, napi_generic_failure); + } + + uint32_t count = reference->Unref(); + + if (result != nullptr) { + *result = count; + } + + return napi_clear_last_error(env); +} + +// Attempts to get a referenced value. If the reference is weak, the value might +// no longer be available, in that case the call is still successful but the +// result is NULL. +napi_status napi_get_reference_value(napi_env env, + napi_ref ref, + napi_value* result) { + // Omit NAPI_PREAMBLE and GET_RETURN_STATUS because V8 calls here cannot throw + // JS exceptions. + CHECK_ENV(env); + CHECK_ARG(env, ref); + CHECK_ARG(env, result); + + v8impl::Reference* reference = reinterpret_cast(ref); + *result = v8impl::JsValueFromV8LocalValue(reference->Get()); + + return napi_clear_last_error(env); +} + +napi_status napi_open_handle_scope(napi_env env, napi_handle_scope* result) { + // Omit NAPI_PREAMBLE and GET_RETURN_STATUS because V8 calls here cannot throw + // JS exceptions. + CHECK_ENV(env); + CHECK_ARG(env, result); + + *result = v8impl::JsHandleScopeFromV8HandleScope( + new v8impl::HandleScopeWrapper(env->isolate)); + env->open_handle_scopes++; + return napi_clear_last_error(env); +} + +napi_status napi_close_handle_scope(napi_env env, napi_handle_scope scope) { + // Omit NAPI_PREAMBLE and GET_RETURN_STATUS because V8 calls here cannot throw + // JS exceptions. + CHECK_ENV(env); + CHECK_ARG(env, scope); + if (env->open_handle_scopes == 0) { + return napi_handle_scope_mismatch; + } + + env->open_handle_scopes--; + delete v8impl::V8HandleScopeFromJsHandleScope(scope); + return napi_clear_last_error(env); +} + +napi_status napi_open_escapable_handle_scope( + napi_env env, + napi_escapable_handle_scope* result) { + // Omit NAPI_PREAMBLE and GET_RETURN_STATUS because V8 calls here cannot throw + // JS exceptions. + CHECK_ENV(env); + CHECK_ARG(env, result); + + *result = v8impl::JsEscapableHandleScopeFromV8EscapableHandleScope( + new v8impl::EscapableHandleScopeWrapper(env->isolate)); + env->open_handle_scopes++; + return napi_clear_last_error(env); +} + +napi_status napi_close_escapable_handle_scope( + napi_env env, + napi_escapable_handle_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_handle_scopes == 0) { + return napi_handle_scope_mismatch; + } + + delete v8impl::V8EscapableHandleScopeFromJsEscapableHandleScope(scope); + env->open_handle_scopes--; + return napi_clear_last_error(env); +} + +napi_status napi_escape_handle(napi_env env, + napi_escapable_handle_scope scope, + napi_value escapee, + napi_value* result) { + // Omit NAPI_PREAMBLE and GET_RETURN_STATUS because V8 calls here cannot throw + // JS exceptions. + CHECK_ENV(env); + CHECK_ARG(env, scope); + CHECK_ARG(env, escapee); + CHECK_ARG(env, result); + + v8impl::EscapableHandleScopeWrapper* s = + v8impl::V8EscapableHandleScopeFromJsEscapableHandleScope(scope); + if (!s->escape_called()) { + *result = v8impl::JsValueFromV8LocalValue( + s->Escape(v8impl::V8LocalValueFromJsValue(escapee))); + return napi_clear_last_error(env); + } + return napi_set_last_error(env, napi_escape_called_twice); +} + +napi_status napi_new_instance(napi_env env, + napi_value constructor, + size_t argc, + const napi_value* argv, + napi_value* result) { + NAPI_PREAMBLE(env); + CHECK_ARG(env, constructor); + if (argc > 0) { + CHECK_ARG(env, argv); + } + CHECK_ARG(env, result); + + v8::Local context = env->context(); + + v8::Local ctor; + CHECK_TO_FUNCTION(env, ctor, constructor); + + auto maybe = ctor->NewInstance(context, argc, + reinterpret_cast*>(const_cast(argv))); + + CHECK_MAYBE_EMPTY(env, maybe, napi_generic_failure); + + *result = v8impl::JsValueFromV8LocalValue(maybe.ToLocalChecked()); + return GET_RETURN_STATUS(env); +} + +napi_status napi_instanceof(napi_env env, + napi_value object, + napi_value constructor, + bool* result) { + NAPI_PREAMBLE(env); + CHECK_ARG(env, object); + CHECK_ARG(env, result); + + *result = false; + + v8::Local ctor; + v8::Local context = env->context(); + + CHECK_TO_OBJECT(env, context, ctor, constructor); + + if (!ctor->IsFunction()) { + napi_throw_type_error(env, + "ERR_NAPI_CONS_FUNCTION", + "Constructor must be a function"); + + return napi_set_last_error(env, napi_function_expected); + } + + napi_status status = napi_generic_failure; + + v8::Local val = v8impl::V8LocalValueFromJsValue(object); + auto maybe_result = val->InstanceOf(context, ctor); + CHECK_MAYBE_NOTHING(env, maybe_result, status); + *result = maybe_result.FromJust(); + return GET_RETURN_STATUS(env); +} + +// Methods to support catching exceptions +napi_status napi_is_exception_pending(napi_env env, bool* result) { + // NAPI_PREAMBLE is not used here: this function must execute when there is a + // pending exception. + CHECK_ENV(env); + CHECK_ARG(env, result); + + *result = !env->last_exception.IsEmpty(); + return napi_clear_last_error(env); +} + +napi_status napi_get_and_clear_last_exception(napi_env env, + napi_value* result) { + // NAPI_PREAMBLE is not used here: this function must execute when there is a + // pending exception. + CHECK_ENV(env); + CHECK_ARG(env, result); + + if (env->last_exception.IsEmpty()) { + return napi_get_undefined(env, result); + } else { + *result = v8impl::JsValueFromV8LocalValue( + v8::Local::New(env->isolate, env->last_exception)); + env->last_exception.Reset(); + } + + return napi_clear_last_error(env); +} + +napi_status napi_is_arraybuffer(napi_env env, napi_value value, bool* result) { + CHECK_ENV(env); + CHECK_ARG(env, value); + CHECK_ARG(env, result); + + v8::Local val = v8impl::V8LocalValueFromJsValue(value); + *result = val->IsArrayBuffer(); + + return napi_clear_last_error(env); +} + +napi_status napi_create_arraybuffer(napi_env env, + size_t byte_length, + void** data, + napi_value* result) { + NAPI_PREAMBLE(env); + CHECK_ARG(env, result); + + v8::Isolate* isolate = env->isolate; + v8::Local buffer = + v8::ArrayBuffer::New(isolate, byte_length); + + // Optionally return a pointer to the buffer's data, to avoid another call to + // retrieve it. + if (data != nullptr) { + *data = buffer->GetContents().Data(); + } + + *result = v8impl::JsValueFromV8LocalValue(buffer); + return GET_RETURN_STATUS(env); +} + +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_PREAMBLE(env); + CHECK_ARG(env, result); + + v8::Isolate* isolate = env->isolate; + v8::Local buffer = + v8::ArrayBuffer::New(isolate, external_data, byte_length); + + if (finalize_cb != nullptr) { + // Create a self-deleting weak reference that invokes the finalizer + // callback. + v8impl::Reference::New(env, + buffer, + 0, + true, + finalize_cb, + external_data, + finalize_hint); + } + + *result = v8impl::JsValueFromV8LocalValue(buffer); + return GET_RETURN_STATUS(env); +} + +napi_status napi_get_arraybuffer_info(napi_env env, + napi_value arraybuffer, + void** data, + size_t* byte_length) { + CHECK_ENV(env); + CHECK_ARG(env, arraybuffer); + + v8::Local value = v8impl::V8LocalValueFromJsValue(arraybuffer); + RETURN_STATUS_IF_FALSE(env, value->IsArrayBuffer(), napi_invalid_arg); + + v8::ArrayBuffer::Contents contents = + value.As()->GetContents(); + + if (data != nullptr) { + *data = contents.Data(); + } + + if (byte_length != nullptr) { + *byte_length = contents.ByteLength(); + } + + return napi_clear_last_error(env); +} + +napi_status napi_is_typedarray(napi_env env, napi_value value, bool* result) { + CHECK_ENV(env); + CHECK_ARG(env, value); + CHECK_ARG(env, result); + + v8::Local val = v8impl::V8LocalValueFromJsValue(value); + *result = val->IsTypedArray(); + + return napi_clear_last_error(env); +} + +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_PREAMBLE(env); + CHECK_ARG(env, arraybuffer); + CHECK_ARG(env, result); + + v8::Local value = v8impl::V8LocalValueFromJsValue(arraybuffer); + RETURN_STATUS_IF_FALSE(env, value->IsArrayBuffer(), napi_invalid_arg); + + v8::Local buffer = value.As(); + v8::Local typedArray; + + switch (type) { + case napi_int8_array: + CREATE_TYPED_ARRAY( + env, Int8Array, 1, buffer, byte_offset, length, typedArray); + break; + case napi_uint8_array: + CREATE_TYPED_ARRAY( + env, Uint8Array, 1, buffer, byte_offset, length, typedArray); + break; + case napi_uint8_clamped_array: + CREATE_TYPED_ARRAY( + env, Uint8ClampedArray, 1, buffer, byte_offset, length, typedArray); + break; + case napi_int16_array: + CREATE_TYPED_ARRAY( + env, Int16Array, 2, buffer, byte_offset, length, typedArray); + break; + case napi_uint16_array: + CREATE_TYPED_ARRAY( + env, Uint16Array, 2, buffer, byte_offset, length, typedArray); + break; + case napi_int32_array: + CREATE_TYPED_ARRAY( + env, Int32Array, 4, buffer, byte_offset, length, typedArray); + break; + case napi_uint32_array: + CREATE_TYPED_ARRAY( + env, Uint32Array, 4, buffer, byte_offset, length, typedArray); + break; + case napi_float32_array: + CREATE_TYPED_ARRAY( + env, Float32Array, 4, buffer, byte_offset, length, typedArray); + break; + case napi_float64_array: + CREATE_TYPED_ARRAY( + env, Float64Array, 8, buffer, byte_offset, length, typedArray); + break; + case napi_bigint64_array: + CREATE_TYPED_ARRAY( + env, BigInt64Array, 8, buffer, byte_offset, length, typedArray); + break; + case napi_biguint64_array: + CREATE_TYPED_ARRAY( + env, BigUint64Array, 8, buffer, byte_offset, length, typedArray); + break; + default: + return napi_set_last_error(env, napi_invalid_arg); + } + + *result = v8impl::JsValueFromV8LocalValue(typedArray); + return GET_RETURN_STATUS(env); +} + +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) { + CHECK_ENV(env); + CHECK_ARG(env, typedarray); + + v8::Local value = v8impl::V8LocalValueFromJsValue(typedarray); + RETURN_STATUS_IF_FALSE(env, value->IsTypedArray(), napi_invalid_arg); + + v8::Local array = value.As(); + + if (type != nullptr) { + if (value->IsInt8Array()) { + *type = napi_int8_array; + } else if (value->IsUint8Array()) { + *type = napi_uint8_array; + } else if (value->IsUint8ClampedArray()) { + *type = napi_uint8_clamped_array; + } else if (value->IsInt16Array()) { + *type = napi_int16_array; + } else if (value->IsUint16Array()) { + *type = napi_uint16_array; + } else if (value->IsInt32Array()) { + *type = napi_int32_array; + } else if (value->IsUint32Array()) { + *type = napi_uint32_array; + } else if (value->IsFloat32Array()) { + *type = napi_float32_array; + } else if (value->IsFloat64Array()) { + *type = napi_float64_array; + } else if (value->IsBigInt64Array()) { + *type = napi_bigint64_array; + } else if (value->IsBigUint64Array()) { + *type = napi_biguint64_array; + } + } + + if (length != nullptr) { + *length = array->Length(); + } + + v8::Local buffer = array->Buffer(); + if (data != nullptr) { + *data = static_cast(buffer->GetContents().Data()) + + array->ByteOffset(); + } + + if (arraybuffer != nullptr) { + *arraybuffer = v8impl::JsValueFromV8LocalValue(buffer); + } + + if (byte_offset != nullptr) { + *byte_offset = array->ByteOffset(); + } + + return napi_clear_last_error(env); +} + +napi_status napi_create_dataview(napi_env env, + size_t byte_length, + napi_value arraybuffer, + size_t byte_offset, + napi_value* result) { + NAPI_PREAMBLE(env); + CHECK_ARG(env, arraybuffer); + CHECK_ARG(env, result); + + v8::Local value = v8impl::V8LocalValueFromJsValue(arraybuffer); + RETURN_STATUS_IF_FALSE(env, value->IsArrayBuffer(), napi_invalid_arg); + + v8::Local buffer = value.As(); + if (byte_length + byte_offset > buffer->ByteLength()) { + napi_throw_range_error( + env, + "ERR_NAPI_INVALID_DATAVIEW_ARGS", + "byte_offset + byte_length should be less than or " + "equal to the size in bytes of the array passed in"); + return napi_set_last_error(env, napi_pending_exception); + } + v8::Local DataView = v8::DataView::New(buffer, byte_offset, + byte_length); + + *result = v8impl::JsValueFromV8LocalValue(DataView); + return GET_RETURN_STATUS(env); +} + +napi_status napi_is_dataview(napi_env env, napi_value value, bool* result) { + CHECK_ENV(env); + CHECK_ARG(env, value); + CHECK_ARG(env, result); + + v8::Local val = v8impl::V8LocalValueFromJsValue(value); + *result = val->IsDataView(); + + return napi_clear_last_error(env); +} + +napi_status napi_get_dataview_info(napi_env env, + napi_value dataview, + size_t* byte_length, + void** data, + napi_value* arraybuffer, + size_t* byte_offset) { + CHECK_ENV(env); + CHECK_ARG(env, dataview); + + v8::Local value = v8impl::V8LocalValueFromJsValue(dataview); + RETURN_STATUS_IF_FALSE(env, value->IsDataView(), napi_invalid_arg); + + v8::Local array = value.As(); + + if (byte_length != nullptr) { + *byte_length = array->ByteLength(); + } + + v8::Local buffer = array->Buffer(); + if (data != nullptr) { + *data = static_cast(buffer->GetContents().Data()) + + array->ByteOffset(); + } + + if (arraybuffer != nullptr) { + *arraybuffer = v8impl::JsValueFromV8LocalValue(buffer); + } + + if (byte_offset != nullptr) { + *byte_offset = array->ByteOffset(); + } + + return napi_clear_last_error(env); +} + +napi_status napi_get_version(napi_env env, uint32_t* result) { + CHECK_ENV(env); + CHECK_ARG(env, result); + *result = NAPI_VERSION; + return napi_clear_last_error(env); +} + +napi_status napi_create_promise(napi_env env, + napi_deferred* deferred, + napi_value* promise) { + NAPI_PREAMBLE(env); + CHECK_ARG(env, deferred); + CHECK_ARG(env, promise); + + auto maybe = v8::Promise::Resolver::New(env->context()); + CHECK_MAYBE_EMPTY(env, maybe, napi_generic_failure); + + auto v8_resolver = maybe.ToLocalChecked(); + auto v8_deferred = new v8impl::Persistent(); + v8_deferred->Reset(env->isolate, v8_resolver); + + *deferred = v8impl::JsDeferredFromNodePersistent(v8_deferred); + *promise = v8impl::JsValueFromV8LocalValue(v8_resolver->GetPromise()); + return GET_RETURN_STATUS(env); +} + +napi_status napi_resolve_deferred(napi_env env, + napi_deferred deferred, + napi_value resolution) { + return v8impl::ConcludeDeferred(env, deferred, resolution, true); +} + +napi_status napi_reject_deferred(napi_env env, + napi_deferred deferred, + napi_value resolution) { + return v8impl::ConcludeDeferred(env, deferred, resolution, false); +} + +napi_status napi_is_promise(napi_env env, + napi_value promise, + bool* is_promise) { + CHECK_ENV(env); + CHECK_ARG(env, promise); + CHECK_ARG(env, is_promise); + + *is_promise = v8impl::V8LocalValueFromJsValue(promise)->IsPromise(); + + return napi_clear_last_error(env); +} + +napi_status napi_run_script(napi_env env, + napi_value script, + napi_value* result) { + NAPI_PREAMBLE(env); + CHECK_ARG(env, script); + CHECK_ARG(env, result); + + v8::Local v8_script = v8impl::V8LocalValueFromJsValue(script); + + if (!v8_script->IsString()) { + return napi_set_last_error(env, napi_string_expected); + } + + v8::Local context = env->context(); + + auto maybe_script = v8::Script::Compile(context, + v8::Local::Cast(v8_script)); + CHECK_MAYBE_EMPTY(env, maybe_script, napi_generic_failure); + + auto script_result = + maybe_script.ToLocalChecked()->Run(context); + CHECK_MAYBE_EMPTY(env, script_result, napi_generic_failure); + + *result = v8impl::JsValueFromV8LocalValue(script_result.ToLocalChecked()); + return GET_RETURN_STATUS(env); +} + +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) { + return v8impl::Wrap(env, + js_object, + native_object, + finalize_cb, + finalize_hint, + result); +} + +napi_status napi_adjust_external_memory(napi_env env, + int64_t change_in_bytes, + int64_t* adjusted_value) { + CHECK_ENV(env); + CHECK_ARG(env, adjusted_value); + + *adjusted_value = env->isolate->AdjustAmountOfExternalAllocatedMemory( + change_in_bytes); + + return napi_clear_last_error(env); +} diff --git a/src/js_native_api_v8.h b/src/js_native_api_v8.h new file mode 100644 index 00000000000000..d5402845dc6af6 --- /dev/null +++ b/src/js_native_api_v8.h @@ -0,0 +1,202 @@ +#ifndef SRC_JS_NATIVE_API_V8_H_ +#define SRC_JS_NATIVE_API_V8_H_ + +#include +#include "js_native_api_types.h" +#include "js_native_api_v8_internals.h" + +struct napi_env__ { + explicit napi_env__(v8::Local context) + : isolate(context->GetIsolate()), + context_persistent(isolate, context) { + CHECK_EQ(isolate, context->GetIsolate()); + } + v8::Isolate* const isolate; // Shortcut for context()->GetIsolate() + v8impl::Persistent context_persistent; + + inline v8::Local context() const { + return v8impl::PersistentToLocal::Strong(context_persistent); + } + + inline void Ref() { refs++; } + inline void Unref() { if ( --refs == 0) delete this; } + + v8impl::Persistent last_exception; + napi_extended_error_info last_error; + int open_handle_scopes = 0; + int open_callback_scopes = 0; + int refs = 1; +}; + +static inline napi_status napi_clear_last_error(napi_env env) { + env->last_error.error_code = napi_ok; + + // TODO(boingoing): Should this be a callback? + env->last_error.engine_error_code = 0; + env->last_error.engine_reserved = nullptr; + return napi_ok; +} + +static inline +napi_status napi_set_last_error(napi_env env, napi_status error_code, + uint32_t engine_error_code = 0, + void* engine_reserved = nullptr) { + env->last_error.error_code = error_code; + env->last_error.engine_error_code = engine_error_code; + env->last_error.engine_reserved = engine_reserved; + return error_code; +} + +#define RETURN_STATUS_IF_FALSE(env, condition, status) \ + do { \ + if (!(condition)) { \ + return napi_set_last_error((env), (status)); \ + } \ + } while (0) + +#define CHECK_ENV(env) \ + do { \ + if ((env) == nullptr) { \ + return napi_invalid_arg; \ + } \ + } while (0) + +#define CHECK_ARG(env, arg) \ + RETURN_STATUS_IF_FALSE((env), ((arg) != nullptr), napi_invalid_arg) + +#define CHECK_MAYBE_EMPTY(env, maybe, status) \ + RETURN_STATUS_IF_FALSE((env), !((maybe).IsEmpty()), (status)) + +// NAPI_PREAMBLE is not wrapped in do..while: try_catch must have function scope +#define NAPI_PREAMBLE(env) \ + CHECK_ENV((env)); \ + RETURN_STATUS_IF_FALSE((env), (env)->last_exception.IsEmpty(), \ + napi_pending_exception); \ + napi_clear_last_error((env)); \ + v8impl::TryCatch try_catch((env)) + +#define CHECK_TO_TYPE(env, type, context, result, src, status) \ + do { \ + CHECK_ARG((env), (src)); \ + auto maybe = v8impl::V8LocalValueFromJsValue((src))->To##type((context)); \ + CHECK_MAYBE_EMPTY((env), maybe, (status)); \ + (result) = maybe.ToLocalChecked(); \ + } while (0) + +#define CHECK_TO_FUNCTION(env, result, src) \ + do { \ + CHECK_ARG((env), (src)); \ + v8::Local v8value = v8impl::V8LocalValueFromJsValue((src)); \ + RETURN_STATUS_IF_FALSE((env), v8value->IsFunction(), napi_invalid_arg); \ + (result) = v8value.As(); \ + } while (0) + +#define CHECK_TO_OBJECT(env, context, result, src) \ + CHECK_TO_TYPE((env), Object, (context), (result), (src), napi_object_expected) + +#define CHECK_TO_STRING(env, context, result, src) \ + CHECK_TO_TYPE((env), String, (context), (result), (src), napi_string_expected) + +#define GET_RETURN_STATUS(env) \ + (!try_catch.HasCaught() ? napi_ok \ + : napi_set_last_error((env), napi_pending_exception)) + +#define THROW_RANGE_ERROR_IF_FALSE(env, condition, error, message) \ + do { \ + if (!(condition)) { \ + napi_throw_range_error((env), (error), (message)); \ + return napi_set_last_error((env), napi_generic_failure); \ + } \ + } while (0) + +#define NAPI_CALL_INTO_MODULE(env, call, handle_exception) \ + do { \ + int open_handle_scopes = (env)->open_handle_scopes; \ + int open_callback_scopes = (env)->open_callback_scopes; \ + napi_clear_last_error((env)); \ + call; \ + CHECK_EQ((env)->open_handle_scopes, open_handle_scopes); \ + CHECK_EQ((env)->open_callback_scopes, open_callback_scopes); \ + if (!(env)->last_exception.IsEmpty()) { \ + handle_exception( \ + v8::Local::New((env)->isolate, (env)->last_exception)); \ + (env)->last_exception.Reset(); \ + } \ + } while (0) + +#define NAPI_CALL_INTO_MODULE_THROW(env, call) \ + NAPI_CALL_INTO_MODULE((env), call, (env)->isolate->ThrowException) + +namespace v8impl { + +//=== Conversion between V8 Handles and napi_value ======================== + +// This asserts v8::Local<> will always be implemented with a single +// pointer field so that we can pass it around as a void*. +static_assert(sizeof(v8::Local) == sizeof(napi_value), + "Cannot convert between v8::Local and napi_value"); + +inline napi_value JsValueFromV8LocalValue(v8::Local local) { + return reinterpret_cast(*local); +} + +inline v8::Local V8LocalValueFromJsValue(napi_value v) { + v8::Local local; + memcpy(&local, &v, sizeof(v)); + return local; +} + +// Adapter for napi_finalize callbacks. +class Finalizer { + protected: + Finalizer(napi_env env, + napi_finalize finalize_callback, + void* finalize_data, + void* finalize_hint) + : _env(env), + _finalize_callback(finalize_callback), + _finalize_data(finalize_data), + _finalize_hint(finalize_hint) { + } + + ~Finalizer() { + } + + public: + static Finalizer* New(napi_env env, + napi_finalize finalize_callback = nullptr, + void* finalize_data = nullptr, + void* finalize_hint = nullptr) { + return new Finalizer( + env, finalize_callback, finalize_data, finalize_hint); + } + + static void Delete(Finalizer* finalizer) { + delete finalizer; + } + + protected: + napi_env _env; + napi_finalize _finalize_callback; + void* _finalize_data; + void* _finalize_hint; +}; + +class TryCatch : public v8::TryCatch { + public: + explicit TryCatch(napi_env env) + : v8::TryCatch(env->isolate), _env(env) {} + + ~TryCatch() { + if (HasCaught()) { + _env->last_exception.Reset(_env->isolate, Exception()); + } + } + + private: + napi_env _env; +}; + +} // end of namespace v8impl + +#endif // SRC_JS_NATIVE_API_V8_H_ diff --git a/src/js_native_api_v8_internals.h b/src/js_native_api_v8_internals.h new file mode 100644 index 00000000000000..91baae6a3b8201 --- /dev/null +++ b/src/js_native_api_v8_internals.h @@ -0,0 +1,35 @@ +#ifndef SRC_JS_NATIVE_API_V8_INTERNALS_H_ +#define SRC_JS_NATIVE_API_V8_INTERNALS_H_ + +// The V8 implementation of N-API, including `js_native_api_v8.h` uses certain +// idioms which require definition here. For example, it uses a variant of +// persistent references which need not be reset in the constructor. It is the +// responsibility of this file to define these idioms. + +// In the case of the Node.js implementation of N-API some of the idioms are +// imported directly from Node.js by including `node_internals.h` below. Others +// are bridged to remove references to the `node` namespace. + +#include "node_version.h" +#include "env.h" +#include "node_internals.h" + +#define NAPI_ARRAYSIZE(array) \ + node::arraysize((array)) + +#define NAPI_FIXED_ONE_BYTE_STRING(isolate, string) \ + node::FIXED_ONE_BYTE_STRING((isolate), (string)) + +#define NAPI_PRIVATE_KEY(context, suffix) \ + (node::Environment::GetCurrent((context))->napi_ ## suffix()) + +namespace v8impl { + +template +using Persistent = node::Persistent; + +using PersistentToLocal = node::PersistentToLocal; + +} // end of namespace v8impl + +#endif // SRC_JS_NATIVE_API_V8_INTERNALS_H_ diff --git a/src/node_api.cc b/src/node_api.cc index 500312750974ef..20428d40fadbdf 100644 --- a/src/node_api.cc +++ b/src/node_api.cc @@ -1,359 +1,32 @@ #include -#include -#include // INT_MAX -#include -#include -#include -#include -#define NAPI_EXPERIMENTAL #include "env.h" +#define NAPI_EXPERIMENTAL #include "node_api.h" #include "node_errors.h" #include "node_internals.h" +#include "js_native_api_v8.h" -static -napi_status napi_set_last_error(napi_env env, napi_status error_code, - uint32_t engine_error_code = 0, - void* engine_reserved = nullptr); -static -napi_status napi_clear_last_error(napi_env env); - -struct napi_env__ { - explicit napi_env__(v8::Local context) - : isolate(context->GetIsolate()), - context_persistent(isolate, context) { - CHECK_EQ(isolate, context->GetIsolate()); +struct node_napi_env__ : public napi_env__ { + explicit node_napi_env__(v8::Local context): + napi_env__(context) { CHECK_NOT_NULL(node_env()); } - - v8::Isolate* const isolate; // Shortcut for context()->GetIsolate() - node::Persistent context_persistent; - - inline v8::Local context() const { - return node::PersistentToLocal::Strong(context_persistent); - } - inline node::Environment* node_env() const { return node::Environment::GetCurrent(context()); } - - inline void Ref() { refs++; } - inline void Unref() { if (--refs == 0) delete this; } - - node::Persistent last_exception; - napi_extended_error_info last_error; - int open_handle_scopes = 0; - int open_callback_scopes = 0; - int refs = 1; }; -#define NAPI_PRIVATE_KEY(context, suffix) \ - (node::Environment::GetCurrent((context))->napi_ ## suffix()) - -#define RETURN_STATUS_IF_FALSE(env, condition, status) \ - do { \ - if (!(condition)) { \ - return napi_set_last_error((env), (status)); \ - } \ - } while (0) - -#define CHECK_ENV(env) \ - do { \ - if ((env) == nullptr) { \ - return napi_invalid_arg; \ - } \ - } while (0) - -#define CHECK_ARG(env, arg) \ - RETURN_STATUS_IF_FALSE((env), ((arg) != nullptr), napi_invalid_arg) - -#define CHECK_MAYBE_EMPTY(env, maybe, status) \ - RETURN_STATUS_IF_FALSE((env), !((maybe).IsEmpty()), (status)) - -#define CHECK_MAYBE_NOTHING(env, maybe, status) \ - RETURN_STATUS_IF_FALSE((env), !((maybe).IsNothing()), (status)) - -// NAPI_PREAMBLE is not wrapped in do..while: try_catch must have function scope -#define NAPI_PREAMBLE(env) \ - CHECK_ENV((env)); \ - RETURN_STATUS_IF_FALSE((env), (env)->last_exception.IsEmpty(), \ - napi_pending_exception); \ - napi_clear_last_error((env)); \ - v8impl::TryCatch try_catch((env)) - -#define CHECK_TO_TYPE(env, type, context, result, src, status) \ - do { \ - CHECK_ARG((env), (src)); \ - auto maybe = v8impl::V8LocalValueFromJsValue((src))->To##type((context)); \ - CHECK_MAYBE_EMPTY((env), maybe, (status)); \ - (result) = maybe.ToLocalChecked(); \ - } while (0) - -#define CHECK_TO_FUNCTION(env, result, src) \ - do { \ - CHECK_ARG((env), (src)); \ - v8::Local v8value = v8impl::V8LocalValueFromJsValue((src)); \ - RETURN_STATUS_IF_FALSE((env), v8value->IsFunction(), napi_invalid_arg); \ - (result) = v8value.As(); \ - } while (0) - -#define CHECK_TO_OBJECT(env, context, result, src) \ - CHECK_TO_TYPE((env), Object, (context), (result), (src), napi_object_expected) - -#define CHECK_TO_STRING(env, context, result, src) \ - CHECK_TO_TYPE((env), String, (context), (result), (src), napi_string_expected) - -#define CHECK_TO_NUMBER(env, context, result, src) \ - CHECK_TO_TYPE((env), Number, (context), (result), (src), napi_number_expected) - -#define CHECK_TO_BOOL(env, context, result, src) \ - CHECK_TO_TYPE((env), Boolean, (context), (result), (src), \ - napi_boolean_expected) - -// n-api defines NAPI_AUTO_LENGHTH as the indicator that a string -// is null terminated. For V8 the equivalent is -1. The assert -// validates that our cast of NAPI_AUTO_LENGTH results in -1 as -// needed by V8. -#define CHECK_NEW_FROM_UTF8_LEN(env, result, str, len) \ - do { \ - static_assert(static_cast(NAPI_AUTO_LENGTH) == -1, \ - "Casting NAPI_AUTO_LENGTH to int must result in -1"); \ - RETURN_STATUS_IF_FALSE((env), \ - (len == NAPI_AUTO_LENGTH) || len <= INT_MAX, \ - napi_invalid_arg); \ - auto str_maybe = v8::String::NewFromUtf8( \ - (env)->isolate, (str), v8::NewStringType::kInternalized, \ - static_cast(len)); \ - CHECK_MAYBE_EMPTY((env), str_maybe, napi_generic_failure); \ - (result) = str_maybe.ToLocalChecked(); \ - } while (0) - -#define CHECK_NEW_FROM_UTF8(env, result, str) \ - CHECK_NEW_FROM_UTF8_LEN((env), (result), (str), NAPI_AUTO_LENGTH) - -#define GET_RETURN_STATUS(env) \ - (!try_catch.HasCaught() ? napi_ok \ - : napi_set_last_error((env), napi_pending_exception)) - -#define THROW_RANGE_ERROR_IF_FALSE(env, condition, error, message) \ - do { \ - if (!(condition)) { \ - napi_throw_range_error((env), (error), (message)); \ - return napi_set_last_error((env), napi_generic_failure); \ - } \ - } while (0) - -#define CREATE_TYPED_ARRAY( \ - env, type, size_of_element, buffer, byte_offset, length, out) \ - do { \ - if ((size_of_element) > 1) { \ - THROW_RANGE_ERROR_IF_FALSE( \ - (env), (byte_offset) % (size_of_element) == 0, \ - "ERR_NAPI_INVALID_TYPEDARRAY_ALIGNMENT", \ - "start offset of "#type" should be a multiple of "#size_of_element); \ - } \ - THROW_RANGE_ERROR_IF_FALSE((env), (length) * (size_of_element) + \ - (byte_offset) <= buffer->ByteLength(), \ - "ERR_NAPI_INVALID_TYPEDARRAY_LENGTH", \ - "Invalid typed array length"); \ - (out) = v8::type::New((buffer), (byte_offset), (length)); \ - } while (0) - -#define NAPI_CALL_INTO_MODULE(env, call, handle_exception) \ - do { \ - int open_handle_scopes = (env)->open_handle_scopes; \ - int open_callback_scopes = (env)->open_callback_scopes; \ - napi_clear_last_error((env)); \ - call; \ - CHECK_EQ((env)->open_handle_scopes, open_handle_scopes); \ - CHECK_EQ((env)->open_callback_scopes, open_callback_scopes); \ - if (!(env)->last_exception.IsEmpty()) { \ - handle_exception( \ - v8::Local::New((env)->isolate, (env)->last_exception)); \ - (env)->last_exception.Reset(); \ - } \ - } while (0) - -#define NAPI_CALL_INTO_MODULE_THROW(env, call) \ - NAPI_CALL_INTO_MODULE((env), call, (env)->isolate->ThrowException) +typedef node_napi_env__* node_napi_env; -namespace { namespace v8impl { -// convert from n-api property attributes to v8::PropertyAttribute -static inline v8::PropertyAttribute V8PropertyAttributesFromDescriptor( - const napi_property_descriptor* descriptor) { - unsigned int attribute_flags = v8::PropertyAttribute::None; - - if (descriptor->getter != nullptr || descriptor->setter != nullptr) { - // The napi_writable attribute is ignored for accessor descriptors, but - // V8 requires the ReadOnly attribute to match nonexistence of a setter. - attribute_flags |= (descriptor->setter == nullptr ? - v8::PropertyAttribute::ReadOnly : v8::PropertyAttribute::None); - } else if ((descriptor->attributes & napi_writable) == 0) { - attribute_flags |= v8::PropertyAttribute::ReadOnly; - } - - if ((descriptor->attributes & napi_enumerable) == 0) { - attribute_flags |= v8::PropertyAttribute::DontEnum; - } - if ((descriptor->attributes & napi_configurable) == 0) { - attribute_flags |= v8::PropertyAttribute::DontDelete; - } - - return static_cast(attribute_flags); -} - -class HandleScopeWrapper { - public: - explicit HandleScopeWrapper(v8::Isolate* isolate) : scope(isolate) {} - - private: - v8::HandleScope scope; -}; - -// In node v0.10 version of v8, there is no EscapableHandleScope and the -// node v0.10 port use HandleScope::Close(Local v) to mimic the behavior -// of a EscapableHandleScope::Escape(Local v), but it is not the same -// semantics. This is an example of where the api abstraction fail to work -// across different versions. -class EscapableHandleScopeWrapper { - public: - explicit EscapableHandleScopeWrapper(v8::Isolate* isolate) - : scope(isolate), escape_called_(false) {} - bool escape_called() const { - return escape_called_; - } - template - v8::Local Escape(v8::Local handle) { - escape_called_ = true; - return scope.Escape(handle); - } - - private: - v8::EscapableHandleScope scope; - bool escape_called_; -}; - -static -napi_handle_scope JsHandleScopeFromV8HandleScope(HandleScopeWrapper* s) { - return reinterpret_cast(s); -} - -static -HandleScopeWrapper* V8HandleScopeFromJsHandleScope(napi_handle_scope s) { - return reinterpret_cast(s); -} - -static -napi_escapable_handle_scope JsEscapableHandleScopeFromV8EscapableHandleScope( - EscapableHandleScopeWrapper* s) { - return reinterpret_cast(s); -} - -static -EscapableHandleScopeWrapper* -V8EscapableHandleScopeFromJsEscapableHandleScope( - napi_escapable_handle_scope s) { - return reinterpret_cast(s); -} - -static -napi_callback_scope JsCallbackScopeFromV8CallbackScope( - node::CallbackScope* s) { - return reinterpret_cast(s); -} - -static -node::CallbackScope* V8CallbackScopeFromJsCallbackScope( - napi_callback_scope s) { - return reinterpret_cast(s); -} - -//=== Conversion between V8 Handles and napi_value ======================== - -// This asserts v8::Local<> will always be implemented with a single -// pointer field so that we can pass it around as a void*. -static_assert(sizeof(v8::Local) == sizeof(napi_value), - "Cannot convert between v8::Local and napi_value"); - -static -napi_deferred JsDeferredFromNodePersistent(node::Persistent* local) { - return reinterpret_cast(local); -} - -static -node::Persistent* NodePersistentFromJsDeferred(napi_deferred local) { - return reinterpret_cast*>(local); -} - -static -napi_value JsValueFromV8LocalValue(v8::Local local) { - return reinterpret_cast(*local); -} - -static -v8::Local V8LocalValueFromJsValue(napi_value v) { - v8::Local local; - memcpy(&local, &v, sizeof(v)); - return local; -} - -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::FatalException(env->isolate, local_err, local_msg); -} - -static inline napi_status V8NameFromPropertyDescriptor(napi_env env, - const napi_property_descriptor* p, - v8::Local* result) { - if (p->utf8name != nullptr) { - CHECK_NEW_FROM_UTF8(env, *result, p->utf8name); - } else { - v8::Local property_value = - v8impl::V8LocalValueFromJsValue(p->name); - - RETURN_STATUS_IF_FALSE(env, property_value->IsName(), napi_name_expected); - *result = property_value.As(); - } - - return napi_ok; -} - -// Adapter for napi_finalize callbacks. -class Finalizer { - protected: - Finalizer(napi_env env, - napi_finalize finalize_callback, - void* finalize_data, - void* finalize_hint) - : _env(env), - _finalize_callback(finalize_callback), - _finalize_data(finalize_data), - _finalize_hint(finalize_hint) { - } - - ~Finalizer() { - } +namespace { +class BufferFinalizer: private Finalizer { public: - static Finalizer* New(napi_env env, - napi_finalize finalize_callback = nullptr, - void* finalize_data = nullptr, - void* finalize_hint = nullptr) { - return new Finalizer( - env, finalize_callback, finalize_data, finalize_hint); - } - - static void Delete(Finalizer* finalizer) { - delete finalizer; - } - // node::Buffer::FreeCallback static void FinalizeBufferCallback(char* data, void* hint) { - Finalizer* finalizer = static_cast(hint); + BufferFinalizer* finalizer = static_cast(hint); if (finalizer->_finalize_callback != nullptr) { NAPI_CALL_INTO_MODULE_THROW(finalizer->_env, finalizer->_finalize_callback( @@ -364,382 +37,10 @@ class Finalizer { Delete(finalizer); } - - protected: - napi_env _env; - napi_finalize _finalize_callback; - void* _finalize_data; - void* _finalize_hint; -}; - -// Wrapper around node::Persistent that implements reference counting. -class Reference : private Finalizer { - private: - Reference(napi_env env, - v8::Local value, - uint32_t initial_refcount, - bool delete_self, - napi_finalize finalize_callback, - void* finalize_data, - void* finalize_hint) - : Finalizer(env, finalize_callback, finalize_data, finalize_hint), - _persistent(env->isolate, value), - _refcount(initial_refcount), - _delete_self(delete_self) { - if (initial_refcount == 0) { - _persistent.SetWeak( - this, FinalizeCallback, v8::WeakCallbackType::kParameter); - } - } - - public: - void* Data() { - return _finalize_data; - } - - static Reference* New(napi_env env, - v8::Local value, - uint32_t initial_refcount, - bool delete_self, - napi_finalize finalize_callback = nullptr, - void* finalize_data = nullptr, - void* finalize_hint = nullptr) { - return new Reference(env, - value, - initial_refcount, - delete_self, - finalize_callback, - finalize_data, - finalize_hint); - } - - static void Delete(Reference* reference) { - delete reference; - } - - uint32_t Ref() { - if (++_refcount == 1) { - _persistent.ClearWeak(); - } - - return _refcount; - } - - uint32_t Unref() { - if (_refcount == 0) { - return 0; - } - if (--_refcount == 0) { - _persistent.SetWeak( - this, FinalizeCallback, v8::WeakCallbackType::kParameter); - } - - return _refcount; - } - - uint32_t RefCount() { - return _refcount; - } - - v8::Local Get() { - if (_persistent.IsEmpty()) { - return v8::Local(); - } else { - return v8::Local::New(_env->isolate, _persistent); - } - } - - private: - static void FinalizeCallback(const v8::WeakCallbackInfo& data) { - Reference* reference = data.GetParameter(); - reference->_persistent.Reset(); - - // Check before calling the finalize callback, because the callback might - // delete it. - bool delete_self = reference->_delete_self; - napi_env env = reference->_env; - - if (reference->_finalize_callback != nullptr) { - NAPI_CALL_INTO_MODULE_THROW(env, - reference->_finalize_callback( - reference->_env, - reference->_finalize_data, - reference->_finalize_hint)); - } - - if (delete_self) { - Delete(reference); - } - } - - node::Persistent _persistent; - uint32_t _refcount; - bool _delete_self; -}; - -class TryCatch : public v8::TryCatch { - public: - explicit TryCatch(napi_env env) - : v8::TryCatch(env->isolate), _env(env) {} - - ~TryCatch() { - if (HasCaught()) { - _env->last_exception.Reset(_env->isolate, Exception()); - } - } - - private: - napi_env _env; -}; - -//=== Function napi_callback wrapper ================================= - -// Use this data structure to associate callback data with each N-API function -// exposed to JavaScript. The structure is stored in a v8::External which gets -// passed into our callback wrapper. This reduces the performance impact of -// calling through N-API. -// Ref: benchmark/misc/function_call -// Discussion (incl. perf. data): https://github.com/nodejs/node/pull/21072 -struct CallbackBundle { - // Bind the lifecycle of `this` C++ object to a JavaScript object. - // We never delete a CallbackBundle C++ object directly. - void BindLifecycleTo(v8::Isolate* isolate, v8::Local target) { - handle.Reset(isolate, target); - handle.SetWeak(this, WeakCallback, v8::WeakCallbackType::kParameter); - } - - napi_env env; // Necessary to invoke C++ NAPI callback - void* cb_data; // The user provided callback data - napi_callback function_or_getter; - napi_callback setter; - node::Persistent handle; // Die with this JavaScript object - - private: - static void WeakCallback(v8::WeakCallbackInfo const& info) { - // Use the "WeakCallback mechanism" to delete the C++ `bundle` object. - // This will be called when the v8::External containing `this` pointer - // is being GC-ed. - CallbackBundle* bundle = info.GetParameter(); - if (bundle != nullptr) { - delete bundle; - } - } -}; - -// Base class extended by classes that wrap V8 function and property callback -// info. -class CallbackWrapper { - public: - CallbackWrapper(napi_value this_arg, size_t args_length, void* data) - : _this(this_arg), _args_length(args_length), _data(data) {} - - virtual napi_value GetNewTarget() = 0; - virtual void Args(napi_value* buffer, size_t bufferlength) = 0; - virtual void SetReturnValue(napi_value value) = 0; - - napi_value This() { return _this; } - - size_t ArgsLength() { return _args_length; } - - void* Data() { return _data; } - - protected: - const napi_value _this; - const size_t _args_length; - void* _data; -}; - -template -class CallbackWrapperBase : public CallbackWrapper { - public: - CallbackWrapperBase(const Info& cbinfo, const size_t args_length) - : CallbackWrapper(JsValueFromV8LocalValue(cbinfo.This()), - args_length, - nullptr), - _cbinfo(cbinfo) { - _bundle = reinterpret_cast( - v8::Local::Cast(cbinfo.Data())->Value()); - _data = _bundle->cb_data; - } - - napi_value GetNewTarget() override { return nullptr; } - - protected: - void InvokeCallback() { - napi_callback_info cbinfo_wrapper = reinterpret_cast( - static_cast(this)); - - // All other pointers we need are stored in `_bundle` - napi_env env = _bundle->env; - napi_callback cb = _bundle->*FunctionField; - - napi_value result; - NAPI_CALL_INTO_MODULE_THROW(env, result = cb(env, cbinfo_wrapper)); - - if (result != nullptr) { - this->SetReturnValue(result); - } - } - - const Info& _cbinfo; - CallbackBundle* _bundle; -}; - -class FunctionCallbackWrapper - : public CallbackWrapperBase, - &CallbackBundle::function_or_getter> { - public: - static void Invoke(const v8::FunctionCallbackInfo& info) { - FunctionCallbackWrapper cbwrapper(info); - cbwrapper.InvokeCallback(); - } - - explicit FunctionCallbackWrapper( - const v8::FunctionCallbackInfo& cbinfo) - : CallbackWrapperBase(cbinfo, cbinfo.Length()) {} - - napi_value GetNewTarget() override { - if (_cbinfo.IsConstructCall()) { - return v8impl::JsValueFromV8LocalValue(_cbinfo.NewTarget()); - } else { - return nullptr; - } - } - - /*virtual*/ - void Args(napi_value* buffer, size_t buffer_length) override { - size_t i = 0; - size_t min = std::min(buffer_length, _args_length); - - for (; i < min; i += 1) { - buffer[i] = v8impl::JsValueFromV8LocalValue(_cbinfo[i]); - } - - if (i < buffer_length) { - napi_value undefined = - v8impl::JsValueFromV8LocalValue(v8::Undefined(_cbinfo.GetIsolate())); - for (; i < buffer_length; i += 1) { - buffer[i] = undefined; - } - } - } - - /*virtual*/ - void SetReturnValue(napi_value value) override { - v8::Local val = v8impl::V8LocalValueFromJsValue(value); - _cbinfo.GetReturnValue().Set(val); - } -}; - -class GetterCallbackWrapper - : public CallbackWrapperBase, - &CallbackBundle::function_or_getter> { - public: - static void Invoke(v8::Local property, - const v8::PropertyCallbackInfo& info) { - GetterCallbackWrapper cbwrapper(info); - cbwrapper.InvokeCallback(); - } - - explicit GetterCallbackWrapper( - const v8::PropertyCallbackInfo& cbinfo) - : CallbackWrapperBase(cbinfo, 0) {} - - /*virtual*/ - void Args(napi_value* buffer, size_t buffer_length) override { - if (buffer_length > 0) { - napi_value undefined = - v8impl::JsValueFromV8LocalValue(v8::Undefined(_cbinfo.GetIsolate())); - for (size_t i = 0; i < buffer_length; i += 1) { - buffer[i] = undefined; - } - } - } - - /*virtual*/ - void SetReturnValue(napi_value value) override { - v8::Local val = v8impl::V8LocalValueFromJsValue(value); - _cbinfo.GetReturnValue().Set(val); - } -}; - -class SetterCallbackWrapper - : public CallbackWrapperBase, - &CallbackBundle::setter> { - public: - static void Invoke(v8::Local property, - v8::Local value, - const v8::PropertyCallbackInfo& info) { - SetterCallbackWrapper cbwrapper(info, value); - cbwrapper.InvokeCallback(); - } - - SetterCallbackWrapper(const v8::PropertyCallbackInfo& cbinfo, - const v8::Local& value) - : CallbackWrapperBase(cbinfo, 1), _value(value) {} - - /*virtual*/ - void Args(napi_value* buffer, size_t buffer_length) override { - if (buffer_length > 0) { - buffer[0] = v8impl::JsValueFromV8LocalValue(_value); - - if (buffer_length > 1) { - napi_value undefined = v8impl::JsValueFromV8LocalValue( - v8::Undefined(_cbinfo.GetIsolate())); - for (size_t i = 1; i < buffer_length; i += 1) { - buffer[i] = undefined; - } - } - } - } - - /*virtual*/ - void SetReturnValue(napi_value value) override { - // Ignore any value returned from a setter callback. - } - - private: - const v8::Local& _value; }; -// Creates an object to be made available to the static function callback -// wrapper, used to retrieve the native callback function and data pointer. -static -v8::Local CreateFunctionCallbackData(napi_env env, - napi_callback cb, - void* data) { - CallbackBundle* bundle = new CallbackBundle(); - bundle->function_or_getter = cb; - bundle->cb_data = data; - bundle->env = env; - v8::Local cbdata = v8::External::New(env->isolate, bundle); - bundle->BindLifecycleTo(env->isolate, cbdata); - - return cbdata; -} - -// Creates an object to be made available to the static getter/setter -// callback wrapper, used to retrieve the native getter/setter callback -// function and data pointer. -static -v8::Local CreateAccessorCallbackData(napi_env env, - napi_callback getter, - napi_callback setter, - void* data) { - CallbackBundle* bundle = new CallbackBundle(); - bundle->function_or_getter = getter; - bundle->setter = setter; - bundle->cb_data = data; - bundle->env = env; - v8::Local cbdata = v8::External::New(env->isolate, bundle); - bundle->BindLifecycleTo(env->isolate, cbdata); - - return cbdata; -} - -static -napi_env GetEnv(v8::Local context) { - napi_env result; +static inline napi_env GetEnv(v8::Local context) { + node_napi_env result; auto isolate = context->GetIsolate(); auto global = context->Global(); @@ -753,9 +54,9 @@ napi_env GetEnv(v8::Local context) { .ToLocalChecked(); if (value->IsExternal()) { - result = static_cast(value.As()->Value()); + result = static_cast(value.As()->Value()); } else { - result = new napi_env__(context); + result = new node_napi_env__(context); auto external = v8::External::New(isolate, result); // We must also stop hard if the result of assigning the env to the global @@ -767,7 +68,7 @@ napi_env GetEnv(v8::Local context) { // 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 + // 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( @@ -780,72 +81,21 @@ napi_env GetEnv(v8::Local context) { return result; } -enum UnwrapAction { - KeepWrap, - RemoveWrap -}; - -static -napi_status Unwrap(napi_env env, - napi_value js_object, - void** result, - UnwrapAction action) { - NAPI_PREAMBLE(env); - CHECK_ARG(env, js_object); - if (action == KeepWrap) { - CHECK_ARG(env, result); - } - - v8::Local context = env->context(); - - v8::Local value = v8impl::V8LocalValueFromJsValue(js_object); - RETURN_STATUS_IF_FALSE(env, value->IsObject(), napi_invalid_arg); - v8::Local obj = value.As(); - - auto val = obj->GetPrivate(context, NAPI_PRIVATE_KEY(context, wrapper)) - .ToLocalChecked(); - RETURN_STATUS_IF_FALSE(env, val->IsExternal(), napi_invalid_arg); - Reference* reference = - static_cast(val.As()->Value()); - - if (result) { - *result = reference->Data(); - } - - if (action == RemoveWrap) { - CHECK(obj->DeletePrivate(context, NAPI_PRIVATE_KEY(context, wrapper)) - .FromJust()); - Reference::Delete(reference); - } - - return GET_RETURN_STATUS(env); +static inline napi_callback_scope +JsCallbackScopeFromV8CallbackScope(node::CallbackScope* s) { + return reinterpret_cast(s); } -static -napi_status ConcludeDeferred(napi_env env, - napi_deferred deferred, - napi_value result, - bool is_resolved) { - NAPI_PREAMBLE(env); - CHECK_ARG(env, result); - - v8::Local context = env->context(); - node::Persistent* deferred_ref = - NodePersistentFromJsDeferred(deferred); - v8::Local v8_deferred = - v8::Local::New(env->isolate, *deferred_ref); - - auto v8_resolver = v8::Local::Cast(v8_deferred); - - v8::Maybe success = is_resolved ? - v8_resolver->Resolve(context, v8impl::V8LocalValueFromJsValue(result)) : - v8_resolver->Reject(context, v8impl::V8LocalValueFromJsValue(result)); - - delete deferred_ref; - - RETURN_STATUS_IF_FALSE(env, success.FromMaybe(false), napi_generic_failure); +static inline node::CallbackScope* +V8CallbackScopeFromJsCallbackScope(napi_callback_scope s) { + return reinterpret_cast(s); +} - return GET_RETURN_STATUS(env); +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::FatalException(env->isolate, local_err, local_msg); } class ThreadSafeFunction : public node::AsyncResource { @@ -856,7 +106,7 @@ class ThreadSafeFunction : public node::AsyncResource { size_t thread_count_, void* context_, size_t max_queue_size_, - napi_env env_, + node_napi_env env_, void* finalize_data_, napi_finalize finalize_cb_, napi_threadsafe_function_call_js call_js_cb_): @@ -958,10 +208,8 @@ class ThreadSafeFunction : public node::AsyncResource { // These methods must only be called from the loop thread. napi_status Init() { - uv_loop_t* loop = nullptr; - CHECK_EQ(napi_get_uv_event_loop(env, &loop), napi_ok); - 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) { @@ -1171,85 +419,29 @@ class ThreadSafeFunction : public node::AsyncResource { size_t max_queue_size; // These are variables accessed only from the loop thread. - node::Persistent ref; - napi_env env; + 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; }; -enum WrapType { - retrievable, - anonymous -}; - -template static inline -napi_status Wrap(napi_env env, - napi_value js_object, - void* native_object, - napi_finalize finalize_cb, - void* finalize_hint, - napi_ref* result) { - NAPI_PREAMBLE(env); - CHECK_ARG(env, js_object); +} // end of anonymous namespace - v8::Local context = env->context(); - - v8::Local value = v8impl::V8LocalValueFromJsValue(js_object); - RETURN_STATUS_IF_FALSE(env, value->IsObject(), napi_invalid_arg); - v8::Local obj = value.As(); - - if (wrap_type == retrievable) { - // If we've already wrapped this object, we error out. - RETURN_STATUS_IF_FALSE(env, - !obj->HasPrivate(context, NAPI_PRIVATE_KEY(context, wrapper)) - .FromJust(), - napi_invalid_arg); - } else if (wrap_type == anonymous) { - // If no finalize callback is provided, we error out. - CHECK_ARG(env, finalize_cb); - } - - v8impl::Reference* reference = nullptr; - if (result != nullptr) { - // The returned reference should be deleted via napi_delete_reference() - // ONLY in response to the finalize callback invocation. (If it is deleted - // before then, then the finalize callback will never be invoked.) - // Therefore a finalize callback is required when returning a reference. - CHECK_ARG(env, finalize_cb); - reference = v8impl::Reference::New( - env, obj, 0, false, finalize_cb, native_object, finalize_hint); - *result = reinterpret_cast(reference); - } else { - // Create a self-deleting reference. - reference = v8impl::Reference::New(env, obj, 0, true, finalize_cb, - native_object, finalize_cb == nullptr ? nullptr : finalize_hint); - } - - if (wrap_type == retrievable) { - CHECK(obj->SetPrivate(context, NAPI_PRIVATE_KEY(context, wrapper), - v8::External::New(env->isolate, reference)).FromJust()); - } - - return GET_RETURN_STATUS(env); -} - -} // end of namespace v8impl +} // 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. -void napi_module_register_cb(v8::Local exports, - v8::Local module, - v8::Local context, - void* priv) { +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); } -} // end of anonymous namespace - void napi_module_register_by_symbol(v8::Local exports, v8::Local module, v8::Local context, @@ -1300,1858 +492,58 @@ 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; -} - -// Warning: Keep in-sync with napi_status enum -static -const char* error_messages[] = {nullptr, - "Invalid argument", - "An object was expected", - "A string was expected", - "A string or symbol was expected", - "A function was expected", - "A number was expected", - "A boolean was expected", - "An array was expected", - "Unknown failure", - "An exception is pending", - "The async work item was cancelled", - "napi_escape_handle already called on scope", - "Invalid handle scope usage", - "Invalid callback scope usage", - "Thread-safe function queue is full", - "Thread-safe function handle is closing", - "A bigint was expected", -}; - -static inline napi_status napi_clear_last_error(napi_env env) { - env->last_error.error_code = napi_ok; - - // TODO(boingoing): Should this be a callback? - env->last_error.engine_error_code = 0; - env->last_error.engine_reserved = nullptr; - return napi_ok; -} - -static inline -napi_status napi_set_last_error(napi_env env, napi_status error_code, - uint32_t engine_error_code, - void* engine_reserved) { - env->last_error.error_code = error_code; - env->last_error.engine_error_code = engine_error_code; - env->last_error.engine_reserved = engine_reserved; - return error_code; -} - -napi_status napi_get_last_error_info(napi_env env, - const napi_extended_error_info** result) { - CHECK_ENV(env); - CHECK_ARG(env, result); - - // you must update this assert to reference the last message - // in the napi_status enum each time a new error message is added. - // We don't have a napi_status_last as this would result in an ABI - // change each time a message was added. - static_assert( - node::arraysize(error_messages) == napi_bigint_expected + 1, - "Count of error messages must match count of error values"); - CHECK_LE(env->last_error.error_code, napi_callback_scope_mismatch); - - // Wait until someone requests the last error information to fetch the error - // message string - env->last_error.error_message = - error_messages[env->last_error.error_code]; - - *result = &(env->last_error); - 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_create_function(napi_env env, - const char* utf8name, - size_t length, - napi_callback cb, - void* callback_data, - napi_value* result) { - NAPI_PREAMBLE(env); - CHECK_ARG(env, result); - CHECK_ARG(env, cb); - - v8::Isolate* isolate = env->isolate; - v8::Local return_value; - v8::EscapableHandleScope scope(isolate); - v8::Local cbdata = - v8impl::CreateFunctionCallbackData(env, cb, callback_data); - - RETURN_STATUS_IF_FALSE(env, !cbdata.IsEmpty(), napi_generic_failure); - - v8::Local context = env->context(); - v8::MaybeLocal maybe_function = - v8::Function::New(context, - v8impl::FunctionCallbackWrapper::Invoke, - cbdata); - CHECK_MAYBE_EMPTY(env, maybe_function, napi_generic_failure); - - return_value = scope.Escape(maybe_function.ToLocalChecked()); - - if (utf8name != nullptr) { - v8::Local name_string; - CHECK_NEW_FROM_UTF8_LEN(env, name_string, utf8name, length); - return_value->SetName(name_string); - } - - *result = v8impl::JsValueFromV8LocalValue(return_value); - - return GET_RETURN_STATUS(env); -} - -napi_status napi_define_class(napi_env env, - const char* utf8name, - size_t length, - napi_callback constructor, - void* callback_data, - size_t property_count, - const napi_property_descriptor* properties, - napi_value* result) { - NAPI_PREAMBLE(env); - CHECK_ARG(env, result); - CHECK_ARG(env, constructor); - - v8::Isolate* isolate = env->isolate; - - v8::EscapableHandleScope scope(isolate); - v8::Local cbdata = - v8impl::CreateFunctionCallbackData(env, constructor, callback_data); - - RETURN_STATUS_IF_FALSE(env, !cbdata.IsEmpty(), napi_generic_failure); - - v8::Local tpl = v8::FunctionTemplate::New( - isolate, v8impl::FunctionCallbackWrapper::Invoke, cbdata); - - v8::Local name_string; - CHECK_NEW_FROM_UTF8_LEN(env, name_string, utf8name, length); - tpl->SetClassName(name_string); - - size_t static_property_count = 0; - for (size_t i = 0; i < property_count; i++) { - const napi_property_descriptor* p = properties + i; - - if ((p->attributes & napi_static) != 0) { - // Static properties are handled separately below. - static_property_count++; - continue; - } - - v8::Local property_name; - napi_status status = - v8impl::V8NameFromPropertyDescriptor(env, p, &property_name); - - if (status != napi_ok) { - return napi_set_last_error(env, status); - } - - v8::PropertyAttribute attributes = - v8impl::V8PropertyAttributesFromDescriptor(p); - - // This code is similar to that in napi_define_properties(); the - // difference is it applies to a template instead of an object. - if (p->getter != nullptr || p->setter != nullptr) { - v8::Local cbdata = v8impl::CreateAccessorCallbackData( - env, p->getter, p->setter, p->data); - - tpl->PrototypeTemplate()->SetAccessor( - property_name, - p->getter ? v8impl::GetterCallbackWrapper::Invoke : nullptr, - p->setter ? v8impl::SetterCallbackWrapper::Invoke : nullptr, - cbdata, - v8::AccessControl::DEFAULT, - attributes); - } else if (p->method != nullptr) { - v8::Local cbdata = - v8impl::CreateFunctionCallbackData(env, p->method, p->data); - - RETURN_STATUS_IF_FALSE(env, !cbdata.IsEmpty(), napi_generic_failure); - - v8::Local t = - v8::FunctionTemplate::New(isolate, - v8impl::FunctionCallbackWrapper::Invoke, - cbdata, - v8::Signature::New(isolate, tpl)); - - tpl->PrototypeTemplate()->Set(property_name, t, attributes); - } else { - v8::Local value = v8impl::V8LocalValueFromJsValue(p->value); - tpl->PrototypeTemplate()->Set(property_name, value, attributes); - } - } - - v8::Local context = env->context(); - *result = v8impl::JsValueFromV8LocalValue( - scope.Escape(tpl->GetFunction(context).ToLocalChecked())); - - if (static_property_count > 0) { - std::vector static_descriptors; - static_descriptors.reserve(static_property_count); - - for (size_t i = 0; i < property_count; i++) { - const napi_property_descriptor* p = properties + i; - if ((p->attributes & napi_static) != 0) { - static_descriptors.push_back(*p); - } - } - - napi_status status = - napi_define_properties(env, - *result, - static_descriptors.size(), - static_descriptors.data()); - if (status != napi_ok) return status; - } - - return GET_RETURN_STATUS(env); -} - -napi_status napi_get_property_names(napi_env env, - napi_value object, - napi_value* result) { - NAPI_PREAMBLE(env); - CHECK_ARG(env, result); - - v8::Local context = env->context(); - v8::Local obj; - CHECK_TO_OBJECT(env, context, obj, object); - - auto maybe_propertynames = obj->GetPropertyNames(context); - - CHECK_MAYBE_EMPTY(env, maybe_propertynames, napi_generic_failure); - - *result = v8impl::JsValueFromV8LocalValue( - maybe_propertynames.ToLocalChecked()); - return GET_RETURN_STATUS(env); -} - -napi_status napi_set_property(napi_env env, - napi_value object, - napi_value key, - napi_value value) { - NAPI_PREAMBLE(env); - CHECK_ARG(env, key); - CHECK_ARG(env, value); - - v8::Local context = env->context(); - v8::Local obj; - - CHECK_TO_OBJECT(env, context, obj, object); - - v8::Local k = v8impl::V8LocalValueFromJsValue(key); - v8::Local val = v8impl::V8LocalValueFromJsValue(value); - - v8::Maybe set_maybe = obj->Set(context, k, val); - - RETURN_STATUS_IF_FALSE(env, set_maybe.FromMaybe(false), napi_generic_failure); - return GET_RETURN_STATUS(env); -} - -napi_status napi_has_property(napi_env env, - napi_value object, - napi_value key, - bool* result) { - NAPI_PREAMBLE(env); - CHECK_ARG(env, result); - CHECK_ARG(env, key); - - v8::Local context = env->context(); - v8::Local obj; - - CHECK_TO_OBJECT(env, context, obj, object); - - v8::Local k = v8impl::V8LocalValueFromJsValue(key); - v8::Maybe has_maybe = obj->Has(context, k); - - CHECK_MAYBE_NOTHING(env, has_maybe, napi_generic_failure); - - *result = has_maybe.FromMaybe(false); - return GET_RETURN_STATUS(env); -} - -napi_status napi_get_property(napi_env env, - napi_value object, - napi_value key, - napi_value* result) { - NAPI_PREAMBLE(env); - CHECK_ARG(env, key); - CHECK_ARG(env, result); - - v8::Local context = env->context(); - v8::Local k = v8impl::V8LocalValueFromJsValue(key); - v8::Local obj; - - CHECK_TO_OBJECT(env, context, obj, object); - - auto get_maybe = obj->Get(context, k); - - CHECK_MAYBE_EMPTY(env, get_maybe, napi_generic_failure); - - v8::Local val = get_maybe.ToLocalChecked(); - *result = v8impl::JsValueFromV8LocalValue(val); - return GET_RETURN_STATUS(env); -} - -napi_status napi_delete_property(napi_env env, - napi_value object, - napi_value key, - bool* result) { - NAPI_PREAMBLE(env); - CHECK_ARG(env, key); - - v8::Local context = env->context(); - v8::Local k = v8impl::V8LocalValueFromJsValue(key); - v8::Local obj; - - CHECK_TO_OBJECT(env, context, obj, object); - v8::Maybe delete_maybe = obj->Delete(context, k); - CHECK_MAYBE_NOTHING(env, delete_maybe, napi_generic_failure); - - if (result != nullptr) - *result = delete_maybe.FromMaybe(false); - - return GET_RETURN_STATUS(env); -} - -napi_status napi_has_own_property(napi_env env, - napi_value object, - napi_value key, - bool* result) { - NAPI_PREAMBLE(env); - CHECK_ARG(env, key); - - v8::Local context = env->context(); - v8::Local obj; - - CHECK_TO_OBJECT(env, context, obj, object); - v8::Local k = v8impl::V8LocalValueFromJsValue(key); - RETURN_STATUS_IF_FALSE(env, k->IsName(), napi_name_expected); - v8::Maybe has_maybe = obj->HasOwnProperty(context, k.As()); - CHECK_MAYBE_NOTHING(env, has_maybe, napi_generic_failure); - *result = has_maybe.FromMaybe(false); - - return GET_RETURN_STATUS(env); -} - -napi_status napi_set_named_property(napi_env env, - napi_value object, - const char* utf8name, - napi_value value) { - NAPI_PREAMBLE(env); - CHECK_ARG(env, value); - - v8::Local context = env->context(); - v8::Local obj; - - CHECK_TO_OBJECT(env, context, obj, object); - - v8::Local key; - CHECK_NEW_FROM_UTF8(env, key, utf8name); - - v8::Local val = v8impl::V8LocalValueFromJsValue(value); - - v8::Maybe set_maybe = obj->Set(context, key, val); - - RETURN_STATUS_IF_FALSE(env, set_maybe.FromMaybe(false), napi_generic_failure); - return GET_RETURN_STATUS(env); -} - -napi_status napi_has_named_property(napi_env env, - napi_value object, - const char* utf8name, - bool* result) { - NAPI_PREAMBLE(env); - CHECK_ARG(env, result); - - v8::Local context = env->context(); - v8::Local obj; - - CHECK_TO_OBJECT(env, context, obj, object); - - v8::Local key; - CHECK_NEW_FROM_UTF8(env, key, utf8name); - - v8::Maybe has_maybe = obj->Has(context, key); - - CHECK_MAYBE_NOTHING(env, has_maybe, napi_generic_failure); - - *result = has_maybe.FromMaybe(false); - return GET_RETURN_STATUS(env); -} - -napi_status napi_get_named_property(napi_env env, - napi_value object, - const char* utf8name, - napi_value* result) { - NAPI_PREAMBLE(env); - CHECK_ARG(env, result); - - v8::Local context = env->context(); - - v8::Local key; - CHECK_NEW_FROM_UTF8(env, key, utf8name); - - v8::Local obj; - - CHECK_TO_OBJECT(env, context, obj, object); - - auto get_maybe = obj->Get(context, key); - - CHECK_MAYBE_EMPTY(env, get_maybe, napi_generic_failure); - - v8::Local val = get_maybe.ToLocalChecked(); - *result = v8impl::JsValueFromV8LocalValue(val); - return GET_RETURN_STATUS(env); -} - -napi_status napi_set_element(napi_env env, - napi_value object, - uint32_t index, - napi_value value) { - NAPI_PREAMBLE(env); - CHECK_ARG(env, value); - - v8::Local context = env->context(); - v8::Local obj; - - CHECK_TO_OBJECT(env, context, obj, object); - - v8::Local val = v8impl::V8LocalValueFromJsValue(value); - auto set_maybe = obj->Set(context, index, val); - - RETURN_STATUS_IF_FALSE(env, set_maybe.FromMaybe(false), napi_generic_failure); - - return GET_RETURN_STATUS(env); -} - -napi_status napi_has_element(napi_env env, - napi_value object, - uint32_t index, - bool* result) { - NAPI_PREAMBLE(env); - CHECK_ARG(env, result); - - v8::Local context = env->context(); - v8::Local obj; - - CHECK_TO_OBJECT(env, context, obj, object); - - v8::Maybe has_maybe = obj->Has(context, index); - - CHECK_MAYBE_NOTHING(env, has_maybe, napi_generic_failure); - - *result = has_maybe.FromMaybe(false); - return GET_RETURN_STATUS(env); -} - -napi_status napi_get_element(napi_env env, - napi_value object, - uint32_t index, - napi_value* result) { - NAPI_PREAMBLE(env); - CHECK_ARG(env, result); - - v8::Local context = env->context(); - v8::Local obj; - - CHECK_TO_OBJECT(env, context, obj, object); - - auto get_maybe = obj->Get(context, index); - - CHECK_MAYBE_EMPTY(env, get_maybe, napi_generic_failure); - - *result = v8impl::JsValueFromV8LocalValue(get_maybe.ToLocalChecked()); - return GET_RETURN_STATUS(env); -} - -napi_status napi_delete_element(napi_env env, - napi_value object, - uint32_t index, - bool* result) { - NAPI_PREAMBLE(env); - - v8::Local context = env->context(); - v8::Local obj; - - CHECK_TO_OBJECT(env, context, obj, object); - v8::Maybe delete_maybe = obj->Delete(context, index); - CHECK_MAYBE_NOTHING(env, delete_maybe, napi_generic_failure); - - if (result != nullptr) - *result = delete_maybe.FromMaybe(false); - - return GET_RETURN_STATUS(env); -} - -napi_status napi_define_properties(napi_env env, - napi_value object, - size_t property_count, - const napi_property_descriptor* properties) { - NAPI_PREAMBLE(env); - if (property_count > 0) { - CHECK_ARG(env, properties); - } - - v8::Local context = env->context(); - - v8::Local obj; - CHECK_TO_OBJECT(env, context, obj, object); - - for (size_t i = 0; i < property_count; i++) { - const napi_property_descriptor* p = &properties[i]; - - v8::Local property_name; - napi_status status = - v8impl::V8NameFromPropertyDescriptor(env, p, &property_name); - - if (status != napi_ok) { - return napi_set_last_error(env, status); - } - - v8::PropertyAttribute attributes = - v8impl::V8PropertyAttributesFromDescriptor(p); - - if (p->getter != nullptr || p->setter != nullptr) { - v8::Local cbdata = v8impl::CreateAccessorCallbackData( - env, - p->getter, - p->setter, - p->data); - - auto set_maybe = obj->SetAccessor( - context, - property_name, - p->getter ? v8impl::GetterCallbackWrapper::Invoke : nullptr, - p->setter ? v8impl::SetterCallbackWrapper::Invoke : nullptr, - cbdata, - v8::AccessControl::DEFAULT, - attributes); - - if (!set_maybe.FromMaybe(false)) { - return napi_set_last_error(env, napi_invalid_arg); - } - } else if (p->method != nullptr) { - v8::Local cbdata = - v8impl::CreateFunctionCallbackData(env, p->method, p->data); - - CHECK_MAYBE_EMPTY(env, cbdata, napi_generic_failure); - - v8::MaybeLocal maybe_fn = - v8::Function::New(context, - v8impl::FunctionCallbackWrapper::Invoke, - cbdata); - - CHECK_MAYBE_EMPTY(env, maybe_fn, napi_generic_failure); - - auto define_maybe = obj->DefineOwnProperty( - context, property_name, maybe_fn.ToLocalChecked(), attributes); - - if (!define_maybe.FromMaybe(false)) { - return napi_set_last_error(env, napi_generic_failure); - } - } else { - v8::Local value = v8impl::V8LocalValueFromJsValue(p->value); - - auto define_maybe = - obj->DefineOwnProperty(context, property_name, value, attributes); - - if (!define_maybe.FromMaybe(false)) { - return napi_set_last_error(env, napi_invalid_arg); - } - } - } - - return GET_RETURN_STATUS(env); -} - -napi_status napi_is_array(napi_env env, napi_value value, bool* result) { - CHECK_ENV(env); - CHECK_ARG(env, value); - CHECK_ARG(env, result); - - v8::Local val = v8impl::V8LocalValueFromJsValue(value); - - *result = val->IsArray(); - return napi_clear_last_error(env); -} - -napi_status napi_get_array_length(napi_env env, - napi_value value, - uint32_t* result) { - NAPI_PREAMBLE(env); - CHECK_ARG(env, value); - CHECK_ARG(env, result); - - v8::Local val = v8impl::V8LocalValueFromJsValue(value); - RETURN_STATUS_IF_FALSE(env, val->IsArray(), napi_array_expected); - - v8::Local arr = val.As(); - *result = arr->Length(); - - return GET_RETURN_STATUS(env); -} - -napi_status napi_strict_equals(napi_env env, - napi_value lhs, - napi_value rhs, - bool* result) { - NAPI_PREAMBLE(env); - CHECK_ARG(env, lhs); - CHECK_ARG(env, rhs); - CHECK_ARG(env, result); - - v8::Local a = v8impl::V8LocalValueFromJsValue(lhs); - v8::Local b = v8impl::V8LocalValueFromJsValue(rhs); - - *result = a->StrictEquals(b); - return GET_RETURN_STATUS(env); -} - -napi_status napi_get_prototype(napi_env env, - napi_value object, - napi_value* result) { - NAPI_PREAMBLE(env); - CHECK_ARG(env, result); - - v8::Local context = env->context(); - - v8::Local obj; - CHECK_TO_OBJECT(env, context, obj, object); - - v8::Local val = obj->GetPrototype(); - *result = v8impl::JsValueFromV8LocalValue(val); - return GET_RETURN_STATUS(env); -} - -napi_status napi_create_object(napi_env env, napi_value* result) { - CHECK_ENV(env); - CHECK_ARG(env, result); - - *result = v8impl::JsValueFromV8LocalValue( - v8::Object::New(env->isolate)); - - return napi_clear_last_error(env); -} - -napi_status napi_create_array(napi_env env, napi_value* result) { - CHECK_ENV(env); - CHECK_ARG(env, result); - - *result = v8impl::JsValueFromV8LocalValue( - v8::Array::New(env->isolate)); - - return napi_clear_last_error(env); -} - -napi_status napi_create_array_with_length(napi_env env, - size_t length, - napi_value* result) { - CHECK_ENV(env); - CHECK_ARG(env, result); - - *result = v8impl::JsValueFromV8LocalValue( - v8::Array::New(env->isolate, length)); - - return napi_clear_last_error(env); -} - -napi_status napi_create_string_latin1(napi_env env, - const char* str, - size_t length, - napi_value* result) { - CHECK_ENV(env); - CHECK_ARG(env, result); - - auto isolate = env->isolate; - auto str_maybe = - v8::String::NewFromOneByte(isolate, - reinterpret_cast(str), - v8::NewStringType::kInternalized, - length); - CHECK_MAYBE_EMPTY(env, str_maybe, napi_generic_failure); - - *result = v8impl::JsValueFromV8LocalValue(str_maybe.ToLocalChecked()); - return napi_clear_last_error(env); -} - -napi_status napi_create_string_utf8(napi_env env, - const char* str, - size_t length, - napi_value* result) { - CHECK_ENV(env); - CHECK_ARG(env, result); - - v8::Local s; - CHECK_NEW_FROM_UTF8_LEN(env, s, str, length); - - *result = v8impl::JsValueFromV8LocalValue(s); - return napi_clear_last_error(env); -} - -napi_status napi_create_string_utf16(napi_env env, - const char16_t* str, - size_t length, - napi_value* result) { - CHECK_ENV(env); - CHECK_ARG(env, result); - - auto isolate = env->isolate; - auto str_maybe = - v8::String::NewFromTwoByte(isolate, - reinterpret_cast(str), - v8::NewStringType::kInternalized, - length); - CHECK_MAYBE_EMPTY(env, str_maybe, napi_generic_failure); - - *result = v8impl::JsValueFromV8LocalValue(str_maybe.ToLocalChecked()); - return napi_clear_last_error(env); -} - -napi_status napi_create_double(napi_env env, - double value, - napi_value* result) { - CHECK_ENV(env); - CHECK_ARG(env, result); - - *result = v8impl::JsValueFromV8LocalValue( - v8::Number::New(env->isolate, value)); - - return napi_clear_last_error(env); -} - -napi_status napi_create_int32(napi_env env, - int32_t value, - napi_value* result) { - CHECK_ENV(env); - CHECK_ARG(env, result); - - *result = v8impl::JsValueFromV8LocalValue( - v8::Integer::New(env->isolate, value)); - - return napi_clear_last_error(env); -} - -napi_status napi_create_uint32(napi_env env, - uint32_t value, - napi_value* result) { - CHECK_ENV(env); - CHECK_ARG(env, result); - - *result = v8impl::JsValueFromV8LocalValue( - v8::Integer::NewFromUnsigned(env->isolate, value)); - - return napi_clear_last_error(env); -} - -napi_status napi_create_int64(napi_env env, - int64_t value, - napi_value* result) { - CHECK_ENV(env); - CHECK_ARG(env, result); - - *result = v8impl::JsValueFromV8LocalValue( - v8::Number::New(env->isolate, static_cast(value))); - - return napi_clear_last_error(env); -} - -napi_status napi_create_bigint_int64(napi_env env, - int64_t value, - napi_value* result) { - CHECK_ENV(env); - CHECK_ARG(env, result); - - *result = v8impl::JsValueFromV8LocalValue( - v8::BigInt::New(env->isolate, value)); - - return napi_clear_last_error(env); -} - -napi_status napi_create_bigint_uint64(napi_env env, - uint64_t value, - napi_value* result) { - CHECK_ENV(env); - CHECK_ARG(env, result); - - *result = v8impl::JsValueFromV8LocalValue( - v8::BigInt::NewFromUnsigned(env->isolate, value)); - - return napi_clear_last_error(env); -} - -napi_status napi_create_bigint_words(napi_env env, - int sign_bit, - size_t word_count, - const uint64_t* words, - napi_value* result) { - NAPI_PREAMBLE(env); - CHECK_ARG(env, words); - CHECK_ARG(env, result); - - v8::Local context = env->context(); - - if (word_count > INT_MAX) { - napi_throw_range_error(env, nullptr, "Maximum BigInt size exceeded"); - return napi_set_last_error(env, napi_pending_exception); - } - - v8::MaybeLocal b = v8::BigInt::NewFromWords( - context, sign_bit, word_count, words); - - if (try_catch.HasCaught()) { - return napi_set_last_error(env, napi_pending_exception); - } else { - CHECK_MAYBE_EMPTY(env, b, napi_generic_failure); - *result = v8impl::JsValueFromV8LocalValue(b.ToLocalChecked()); - return napi_clear_last_error(env); - } -} - -napi_status napi_get_boolean(napi_env env, bool value, napi_value* result) { - CHECK_ENV(env); - CHECK_ARG(env, result); - - v8::Isolate* isolate = env->isolate; - - if (value) { - *result = v8impl::JsValueFromV8LocalValue(v8::True(isolate)); - } else { - *result = v8impl::JsValueFromV8LocalValue(v8::False(isolate)); - } - - return napi_clear_last_error(env); -} - -napi_status napi_create_symbol(napi_env env, - napi_value description, - napi_value* result) { - CHECK_ENV(env); - CHECK_ARG(env, result); - - v8::Isolate* isolate = env->isolate; - - if (description == nullptr) { - *result = v8impl::JsValueFromV8LocalValue(v8::Symbol::New(isolate)); - } else { - v8::Local desc = v8impl::V8LocalValueFromJsValue(description); - RETURN_STATUS_IF_FALSE(env, desc->IsString(), napi_string_expected); - - *result = v8impl::JsValueFromV8LocalValue( - v8::Symbol::New(isolate, desc.As())); - } - - return napi_clear_last_error(env); -} - -static napi_status set_error_code(napi_env env, - v8::Local error, - napi_value code, - const char* code_cstring) { - if ((code != nullptr) || (code_cstring != nullptr)) { - v8::Isolate* isolate = env->isolate; - v8::Local context = env->context(); - v8::Local err_object = error.As(); - - v8::Local code_value = v8impl::V8LocalValueFromJsValue(code); - if (code != nullptr) { - code_value = v8impl::V8LocalValueFromJsValue(code); - RETURN_STATUS_IF_FALSE(env, code_value->IsString(), napi_string_expected); - } else { - CHECK_NEW_FROM_UTF8(env, code_value, code_cstring); - } - - v8::Local code_key; - CHECK_NEW_FROM_UTF8(env, code_key, "code"); - - v8::Maybe set_maybe = err_object->Set(context, code_key, code_value); - RETURN_STATUS_IF_FALSE(env, - set_maybe.FromMaybe(false), - napi_generic_failure); - - // now update the name to be "name [code]" where name is the - // original name and code is the code associated with the Error - v8::Local name_string; - CHECK_NEW_FROM_UTF8(env, name_string, ""); - v8::Local name_key; - CHECK_NEW_FROM_UTF8(env, name_key, "name"); - - auto maybe_name = err_object->Get(context, name_key); - if (!maybe_name.IsEmpty()) { - v8::Local name = maybe_name.ToLocalChecked(); - if (name->IsString()) { - name_string = - v8::String::Concat(isolate, name_string, name.As()); - } - } - name_string = v8::String::Concat( - isolate, name_string, node::FIXED_ONE_BYTE_STRING(isolate, " [")); - name_string = - v8::String::Concat(isolate, name_string, code_value.As()); - name_string = v8::String::Concat( - isolate, name_string, node::FIXED_ONE_BYTE_STRING(isolate, "]")); - - set_maybe = err_object->Set(context, name_key, name_string); - RETURN_STATUS_IF_FALSE(env, - set_maybe.FromMaybe(false), - napi_generic_failure); - } - return napi_ok; -} - -napi_status napi_create_error(napi_env env, - napi_value code, - napi_value msg, - napi_value* result) { - CHECK_ENV(env); - CHECK_ARG(env, msg); - CHECK_ARG(env, result); - - v8::Local message_value = v8impl::V8LocalValueFromJsValue(msg); - RETURN_STATUS_IF_FALSE(env, message_value->IsString(), napi_string_expected); - - v8::Local error_obj = - v8::Exception::Error(message_value.As()); - napi_status status = set_error_code(env, error_obj, code, nullptr); - if (status != napi_ok) return status; - - *result = v8impl::JsValueFromV8LocalValue(error_obj); - - return napi_clear_last_error(env); -} - -napi_status napi_create_type_error(napi_env env, - napi_value code, - napi_value msg, - napi_value* result) { - CHECK_ENV(env); - CHECK_ARG(env, msg); - CHECK_ARG(env, result); - - v8::Local message_value = v8impl::V8LocalValueFromJsValue(msg); - RETURN_STATUS_IF_FALSE(env, message_value->IsString(), napi_string_expected); - - v8::Local error_obj = - v8::Exception::TypeError(message_value.As()); - napi_status status = set_error_code(env, error_obj, code, nullptr); - if (status != napi_ok) return status; - - *result = v8impl::JsValueFromV8LocalValue(error_obj); - - return napi_clear_last_error(env); -} - -napi_status napi_create_range_error(napi_env env, - napi_value code, - napi_value msg, - napi_value* result) { - CHECK_ENV(env); - CHECK_ARG(env, msg); - CHECK_ARG(env, result); - - v8::Local message_value = v8impl::V8LocalValueFromJsValue(msg); - RETURN_STATUS_IF_FALSE(env, message_value->IsString(), napi_string_expected); - - v8::Local error_obj = - v8::Exception::RangeError(message_value.As()); - napi_status status = set_error_code(env, error_obj, code, nullptr); - if (status != napi_ok) return status; - - *result = v8impl::JsValueFromV8LocalValue(error_obj); - - return napi_clear_last_error(env); -} - -napi_status napi_typeof(napi_env env, - napi_value value, - napi_valuetype* result) { - // Omit NAPI_PREAMBLE and GET_RETURN_STATUS because V8 calls here cannot throw - // JS exceptions. - CHECK_ENV(env); - CHECK_ARG(env, value); - CHECK_ARG(env, result); - - v8::Local v = v8impl::V8LocalValueFromJsValue(value); - - if (v->IsNumber()) { - *result = napi_number; - } else if (v->IsBigInt()) { - *result = napi_bigint; - } else if (v->IsString()) { - *result = napi_string; - } else if (v->IsFunction()) { - // This test has to come before IsObject because IsFunction - // implies IsObject - *result = napi_function; - } else if (v->IsExternal()) { - // This test has to come before IsObject because IsExternal - // implies IsObject - *result = napi_external; - } else if (v->IsObject()) { - *result = napi_object; - } else if (v->IsBoolean()) { - *result = napi_boolean; - } else if (v->IsUndefined()) { - *result = napi_undefined; - } else if (v->IsSymbol()) { - *result = napi_symbol; - } else if (v->IsNull()) { - *result = napi_null; - } else { - // Should not get here unless V8 has added some new kind of value. - return napi_set_last_error(env, napi_invalid_arg); - } - - return napi_clear_last_error(env); -} - -napi_status napi_get_undefined(napi_env env, napi_value* result) { - CHECK_ENV(env); - CHECK_ARG(env, result); - - *result = v8impl::JsValueFromV8LocalValue( - v8::Undefined(env->isolate)); - - return napi_clear_last_error(env); -} - -napi_status napi_get_null(napi_env env, napi_value* result) { - CHECK_ENV(env); - CHECK_ARG(env, result); - - *result = v8impl::JsValueFromV8LocalValue( - v8::Null(env->isolate)); - - return napi_clear_last_error(env); -} - -// Gets all callback info in a single call. (Ugly, but faster.) -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. - CHECK_ENV(env); - CHECK_ARG(env, cbinfo); - - v8impl::CallbackWrapper* info = - reinterpret_cast(cbinfo); - - if (argv != nullptr) { - CHECK_ARG(env, argc); - info->Args(argv, *argc); - } - if (argc != nullptr) { - *argc = info->ArgsLength(); - } - if (this_arg != nullptr) { - *this_arg = info->This(); - } - if (data != nullptr) { - *data = info->Data(); - } - - return napi_clear_last_error(env); -} - -napi_status napi_get_new_target(napi_env env, - napi_callback_info cbinfo, - napi_value* result) { - CHECK_ENV(env); - CHECK_ARG(env, cbinfo); - CHECK_ARG(env, result); - - v8impl::CallbackWrapper* info = - reinterpret_cast(cbinfo); - - *result = info->GetNewTarget(); - return napi_clear_last_error(env); -} - -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_PREAMBLE(env); - CHECK_ARG(env, recv); - if (argc > 0) { - CHECK_ARG(env, argv); - } - - v8::Local context = env->context(); - - v8::Local v8recv = v8impl::V8LocalValueFromJsValue(recv); - - v8::Local v8func; - CHECK_TO_FUNCTION(env, v8func, func); - - auto maybe = v8func->Call(context, v8recv, argc, - reinterpret_cast*>(const_cast(argv))); - - if (try_catch.HasCaught()) { - return napi_set_last_error(env, napi_pending_exception); - } else { - if (result != nullptr) { - CHECK_MAYBE_EMPTY(env, maybe, napi_generic_failure); - *result = v8impl::JsValueFromV8LocalValue(maybe.ToLocalChecked()); - } - return napi_clear_last_error(env); - } -} - -napi_status napi_get_global(napi_env env, napi_value* result) { - CHECK_ENV(env); - CHECK_ARG(env, result); - - v8::Local context = env->context(); - *result = v8impl::JsValueFromV8LocalValue(context->Global()); - - return napi_clear_last_error(env); -} - -napi_status napi_throw(napi_env env, napi_value error) { - NAPI_PREAMBLE(env); - CHECK_ARG(env, error); - - v8::Isolate* isolate = env->isolate; - - isolate->ThrowException(v8impl::V8LocalValueFromJsValue(error)); - // any VM calls after this point and before returning - // to the javascript invoker will fail - return napi_clear_last_error(env); -} - -napi_status napi_throw_error(napi_env env, - const char* code, - const char* msg) { - NAPI_PREAMBLE(env); - - v8::Isolate* isolate = env->isolate; - v8::Local str; - CHECK_NEW_FROM_UTF8(env, str, msg); - - v8::Local error_obj = v8::Exception::Error(str); - napi_status status = set_error_code(env, error_obj, nullptr, code); - if (status != napi_ok) return status; - - isolate->ThrowException(error_obj); - // any VM calls after this point and before returning - // to the javascript invoker will fail - return napi_clear_last_error(env); -} - -napi_status napi_throw_type_error(napi_env env, - const char* code, - const char* msg) { - NAPI_PREAMBLE(env); - - v8::Isolate* isolate = env->isolate; - v8::Local str; - CHECK_NEW_FROM_UTF8(env, str, msg); - - v8::Local error_obj = v8::Exception::TypeError(str); - napi_status status = set_error_code(env, error_obj, nullptr, code); - if (status != napi_ok) return status; - - isolate->ThrowException(error_obj); - // any VM calls after this point and before returning - // to the javascript invoker will fail - return napi_clear_last_error(env); -} - -napi_status napi_throw_range_error(napi_env env, - const char* code, - const char* msg) { - NAPI_PREAMBLE(env); - - v8::Isolate* isolate = env->isolate; - v8::Local str; - CHECK_NEW_FROM_UTF8(env, str, msg); - - v8::Local error_obj = v8::Exception::RangeError(str); - napi_status status = set_error_code(env, error_obj, nullptr, code); - if (status != napi_ok) return status; - - isolate->ThrowException(error_obj); - // any VM calls after this point and before returning - // to the javascript invoker will fail - return napi_clear_last_error(env); -} - -napi_status napi_is_error(napi_env env, napi_value value, bool* result) { - // Omit NAPI_PREAMBLE and GET_RETURN_STATUS because V8 calls here cannot - // throw JS exceptions. - CHECK_ENV(env); - CHECK_ARG(env, value); - CHECK_ARG(env, result); - - v8::Local val = v8impl::V8LocalValueFromJsValue(value); - *result = val->IsNativeError(); - - return napi_clear_last_error(env); -} - -napi_status napi_get_value_double(napi_env env, - napi_value value, - double* result) { - // Omit NAPI_PREAMBLE and GET_RETURN_STATUS because V8 calls here cannot throw - // JS exceptions. - CHECK_ENV(env); - CHECK_ARG(env, value); - CHECK_ARG(env, result); - - v8::Local val = v8impl::V8LocalValueFromJsValue(value); - RETURN_STATUS_IF_FALSE(env, val->IsNumber(), napi_number_expected); - - *result = val.As()->Value(); - - return napi_clear_last_error(env); -} - -napi_status napi_get_value_int32(napi_env env, - napi_value value, - int32_t* result) { - // Omit NAPI_PREAMBLE and GET_RETURN_STATUS because V8 calls here cannot throw - // JS exceptions. - CHECK_ENV(env); - CHECK_ARG(env, value); - CHECK_ARG(env, result); - - v8::Local val = v8impl::V8LocalValueFromJsValue(value); - - if (val->IsInt32()) { - *result = val.As()->Value(); - } else { - RETURN_STATUS_IF_FALSE(env, val->IsNumber(), napi_number_expected); - - // Empty context: https://github.com/nodejs/node/issues/14379 - v8::Local context; - *result = val->Int32Value(context).FromJust(); - } - - return napi_clear_last_error(env); -} - -napi_status napi_get_value_uint32(napi_env env, - napi_value value, - uint32_t* result) { - // Omit NAPI_PREAMBLE and GET_RETURN_STATUS because V8 calls here cannot throw - // JS exceptions. - CHECK_ENV(env); - CHECK_ARG(env, value); - CHECK_ARG(env, result); - - v8::Local val = v8impl::V8LocalValueFromJsValue(value); - - if (val->IsUint32()) { - *result = val.As()->Value(); - } else { - RETURN_STATUS_IF_FALSE(env, val->IsNumber(), napi_number_expected); - - // Empty context: https://github.com/nodejs/node/issues/14379 - v8::Local context; - *result = val->Uint32Value(context).FromJust(); - } - - return napi_clear_last_error(env); -} - -napi_status napi_get_value_int64(napi_env env, - napi_value value, - int64_t* result) { - // Omit NAPI_PREAMBLE and GET_RETURN_STATUS because V8 calls here cannot throw - // JS exceptions. - CHECK_ENV(env); - CHECK_ARG(env, value); - CHECK_ARG(env, result); - - v8::Local val = v8impl::V8LocalValueFromJsValue(value); - - // This is still a fast path very likely to be taken. - if (val->IsInt32()) { - *result = val.As()->Value(); - return napi_clear_last_error(env); - } - - RETURN_STATUS_IF_FALSE(env, val->IsNumber(), napi_number_expected); - - // v8::Value::IntegerValue() converts NaN, +Inf, and -Inf to INT64_MIN, - // inconsistent with v8::Value::Int32Value() which converts those values to 0. - // Special-case all non-finite values to match that behavior. - double doubleValue = val.As()->Value(); - if (std::isfinite(doubleValue)) { - // Empty context: https://github.com/nodejs/node/issues/14379 - v8::Local context; - *result = val->IntegerValue(context).FromJust(); - } else { - *result = 0; - } - - return napi_clear_last_error(env); -} - -napi_status napi_get_value_bigint_int64(napi_env env, - napi_value value, - int64_t* result, - bool* lossless) { - CHECK_ENV(env); - CHECK_ARG(env, value); - CHECK_ARG(env, result); - CHECK_ARG(env, lossless); - - v8::Local val = v8impl::V8LocalValueFromJsValue(value); - - RETURN_STATUS_IF_FALSE(env, val->IsBigInt(), napi_bigint_expected); - - *result = val.As()->Int64Value(lossless); - - return napi_clear_last_error(env); -} - -napi_status napi_get_value_bigint_uint64(napi_env env, - napi_value value, - uint64_t* result, - bool* lossless) { - CHECK_ENV(env); - CHECK_ARG(env, value); - CHECK_ARG(env, result); - CHECK_ARG(env, lossless); - - v8::Local val = v8impl::V8LocalValueFromJsValue(value); - - RETURN_STATUS_IF_FALSE(env, val->IsBigInt(), napi_bigint_expected); - - *result = val.As()->Uint64Value(lossless); - - return napi_clear_last_error(env); -} - -napi_status napi_get_value_bigint_words(napi_env env, - napi_value value, - int* sign_bit, - size_t* word_count, - uint64_t* words) { - CHECK_ENV(env); - CHECK_ARG(env, value); - CHECK_ARG(env, word_count); - - v8::Local val = v8impl::V8LocalValueFromJsValue(value); - - RETURN_STATUS_IF_FALSE(env, val->IsBigInt(), napi_bigint_expected); - - v8::Local big = val.As(); - - int word_count_int = *word_count; - - if (sign_bit == nullptr && words == nullptr) { - word_count_int = big->WordCount(); - } else { - CHECK_ARG(env, sign_bit); - CHECK_ARG(env, words); - big->ToWordsArray(sign_bit, &word_count_int, words); - } - - *word_count = word_count_int; - - return napi_clear_last_error(env); -} - -napi_status napi_get_value_bool(napi_env env, napi_value value, bool* result) { - // Omit NAPI_PREAMBLE and GET_RETURN_STATUS because V8 calls here cannot throw - // JS exceptions. - CHECK_ENV(env); - CHECK_ARG(env, value); - CHECK_ARG(env, result); - - v8::Local val = v8impl::V8LocalValueFromJsValue(value); - RETURN_STATUS_IF_FALSE(env, val->IsBoolean(), napi_boolean_expected); - - *result = val.As()->Value(); - - return napi_clear_last_error(env); -} - -// Copies a JavaScript string into a LATIN-1 string buffer. The result is the -// number of bytes (excluding the null terminator) copied into buf. -// A sufficient buffer size should be greater than the length of string, -// reserving space for null terminator. -// If bufsize is insufficient, the string will be truncated and null terminated. -// If buf is NULL, this method returns the length of the string (in bytes) -// via the result parameter. -// The result argument is optional unless buf is NULL. -napi_status napi_get_value_string_latin1(napi_env env, - napi_value value, - char* buf, - size_t bufsize, - size_t* result) { - CHECK_ENV(env); - CHECK_ARG(env, value); - - v8::Local val = v8impl::V8LocalValueFromJsValue(value); - RETURN_STATUS_IF_FALSE(env, val->IsString(), napi_string_expected); - - if (!buf) { - CHECK_ARG(env, result); - *result = val.As()->Length(); - } else { - int copied = - val.As()->WriteOneByte(env->isolate, - reinterpret_cast(buf), - 0, - bufsize - 1, - v8::String::NO_NULL_TERMINATION); - - buf[copied] = '\0'; - if (result != nullptr) { - *result = copied; - } - } - - return napi_clear_last_error(env); -} - -// Copies a JavaScript string into a UTF-8 string buffer. The result is the -// number of bytes (excluding the null terminator) copied into buf. -// A sufficient buffer size should be greater than the length of string, -// reserving space for null terminator. -// If bufsize is insufficient, the string will be truncated and null terminated. -// If buf is NULL, this method returns the length of the string (in bytes) -// via the result parameter. -// The result argument is optional unless buf is NULL. -napi_status napi_get_value_string_utf8(napi_env env, - napi_value value, - char* buf, - size_t bufsize, - size_t* result) { - CHECK_ENV(env); - CHECK_ARG(env, value); - - v8::Local val = v8impl::V8LocalValueFromJsValue(value); - RETURN_STATUS_IF_FALSE(env, val->IsString(), napi_string_expected); - - if (!buf) { - CHECK_ARG(env, result); - *result = val.As()->Utf8Length(env->isolate); - } else { - int copied = val.As()->WriteUtf8( - env->isolate, - buf, - bufsize - 1, - nullptr, - v8::String::REPLACE_INVALID_UTF8 | v8::String::NO_NULL_TERMINATION); - - buf[copied] = '\0'; - if (result != nullptr) { - *result = copied; - } - } - - return napi_clear_last_error(env); -} - -// Copies a JavaScript string into a UTF-16 string buffer. The result is the -// number of 2-byte code units (excluding the null terminator) copied into buf. -// A sufficient buffer size should be greater than the length of string, -// reserving space for null terminator. -// If bufsize is insufficient, the string will be truncated and null terminated. -// If buf is NULL, this method returns the length of the string (in 2-byte -// code units) via the result parameter. -// The result argument is optional unless buf is NULL. -napi_status napi_get_value_string_utf16(napi_env env, - napi_value value, - char16_t* buf, - size_t bufsize, - size_t* result) { - CHECK_ENV(env); - CHECK_ARG(env, value); - - v8::Local val = v8impl::V8LocalValueFromJsValue(value); - RETURN_STATUS_IF_FALSE(env, val->IsString(), napi_string_expected); - - if (!buf) { - CHECK_ARG(env, result); - // V8 assumes UTF-16 length is the same as the number of characters. - *result = val.As()->Length(); - } else { - int copied = val.As()->Write(env->isolate, - reinterpret_cast(buf), - 0, - bufsize - 1, - v8::String::NO_NULL_TERMINATION); - - buf[copied] = '\0'; - if (result != nullptr) { - *result = copied; - } - } - - return napi_clear_last_error(env); -} - -napi_status napi_coerce_to_object(napi_env env, - napi_value value, - napi_value* result) { - NAPI_PREAMBLE(env); - CHECK_ARG(env, value); - CHECK_ARG(env, result); - - v8::Local context = env->context(); - v8::Local obj; - CHECK_TO_OBJECT(env, context, obj, value); - - *result = v8impl::JsValueFromV8LocalValue(obj); - return GET_RETURN_STATUS(env); -} - -napi_status napi_coerce_to_bool(napi_env env, - napi_value value, - napi_value* result) { - NAPI_PREAMBLE(env); - CHECK_ARG(env, value); - CHECK_ARG(env, result); - - v8::Local context = env->context(); - v8::Local b; - - CHECK_TO_BOOL(env, context, b, value); - - *result = v8impl::JsValueFromV8LocalValue(b); - return GET_RETURN_STATUS(env); -} - -napi_status napi_coerce_to_number(napi_env env, - napi_value value, - napi_value* result) { - NAPI_PREAMBLE(env); - CHECK_ARG(env, value); - CHECK_ARG(env, result); - - v8::Local context = env->context(); - v8::Local num; - - CHECK_TO_NUMBER(env, context, num, value); - - *result = v8impl::JsValueFromV8LocalValue(num); - return GET_RETURN_STATUS(env); -} - -napi_status napi_coerce_to_string(napi_env env, - napi_value value, - napi_value* result) { - NAPI_PREAMBLE(env); - CHECK_ARG(env, value); - CHECK_ARG(env, result); - - v8::Local context = env->context(); - v8::Local str; - - CHECK_TO_STRING(env, context, str, value); - - *result = v8impl::JsValueFromV8LocalValue(str); - return GET_RETURN_STATUS(env); -} - -napi_status napi_wrap(napi_env env, - napi_value js_object, - void* native_object, - napi_finalize finalize_cb, - void* finalize_hint, - napi_ref* result) { - return v8impl::Wrap(env, - js_object, - native_object, - finalize_cb, - finalize_hint, - result); -} - -napi_status napi_unwrap(napi_env env, napi_value obj, void** result) { - return v8impl::Unwrap(env, obj, result, v8impl::KeepWrap); -} - -napi_status napi_remove_wrap(napi_env env, napi_value obj, void** result) { - return v8impl::Unwrap(env, obj, result, v8impl::RemoveWrap); -} - -napi_status napi_create_external(napi_env env, - void* data, - napi_finalize finalize_cb, - void* finalize_hint, - napi_value* result) { - NAPI_PREAMBLE(env); - CHECK_ARG(env, result); - - v8::Isolate* isolate = env->isolate; - - v8::Local external_value = v8::External::New(isolate, data); - - // The Reference object will delete itself after invoking the finalizer - // callback. - v8impl::Reference::New(env, - external_value, - 0, - true, - finalize_cb, - data, - finalize_hint); - - *result = v8impl::JsValueFromV8LocalValue(external_value); - - return napi_clear_last_error(env); -} - -napi_status napi_get_value_external(napi_env env, - napi_value value, - void** result) { - CHECK_ENV(env); - CHECK_ARG(env, value); - CHECK_ARG(env, result); - - v8::Local val = v8impl::V8LocalValueFromJsValue(value); - RETURN_STATUS_IF_FALSE(env, val->IsExternal(), napi_invalid_arg); - - v8::Local external_value = val.As(); - *result = external_value->Value(); - - return napi_clear_last_error(env); -} - -// Set initial_refcount to 0 for a weak reference, >0 for a strong reference. -napi_status napi_create_reference(napi_env env, - napi_value value, - uint32_t initial_refcount, - napi_ref* result) { - // Omit NAPI_PREAMBLE and GET_RETURN_STATUS because V8 calls here cannot throw - // JS exceptions. - CHECK_ENV(env); - CHECK_ARG(env, value); - CHECK_ARG(env, result); - - v8::Local v8_value = v8impl::V8LocalValueFromJsValue(value); - - if (!(v8_value->IsObject() || v8_value->IsFunction())) { - return napi_set_last_error(env, napi_object_expected); - } - - v8impl::Reference* reference = - v8impl::Reference::New(env, v8_value, initial_refcount, false); - - *result = reinterpret_cast(reference); - return napi_clear_last_error(env); -} - -// Deletes a reference. The referenced value is released, and may be GC'd unless -// there are other references to it. -napi_status napi_delete_reference(napi_env env, napi_ref ref) { - // Omit NAPI_PREAMBLE and GET_RETURN_STATUS because V8 calls here cannot throw - // JS exceptions. - CHECK_ENV(env); - CHECK_ARG(env, ref); - - v8impl::Reference::Delete(reinterpret_cast(ref)); - - return napi_clear_last_error(env); -} - -// 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_status napi_reference_ref(napi_env env, napi_ref ref, uint32_t* result) { - // Omit NAPI_PREAMBLE and GET_RETURN_STATUS because V8 calls here cannot throw - // JS exceptions. - CHECK_ENV(env); - CHECK_ARG(env, ref); - - v8impl::Reference* reference = reinterpret_cast(ref); - uint32_t count = reference->Ref(); - - if (result != nullptr) { - *result = count; - } - - return napi_clear_last_error(env); -} - -// 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_status napi_reference_unref(napi_env env, napi_ref ref, uint32_t* result) { - // Omit NAPI_PREAMBLE and GET_RETURN_STATUS because V8 calls here cannot throw - // JS exceptions. - CHECK_ENV(env); - CHECK_ARG(env, ref); - - v8impl::Reference* reference = reinterpret_cast(ref); - - if (reference->RefCount() == 0) { - return napi_set_last_error(env, napi_generic_failure); - } - - uint32_t count = reference->Unref(); - - if (result != nullptr) { - *result = count; - } - - return napi_clear_last_error(env); -} - -// Attempts to get a referenced value. If the reference is weak, the value might -// no longer be available, in that case the call is still successful but the -// result is NULL. -napi_status napi_get_reference_value(napi_env env, - napi_ref ref, - napi_value* result) { - // Omit NAPI_PREAMBLE and GET_RETURN_STATUS because V8 calls here cannot throw - // JS exceptions. - CHECK_ENV(env); - CHECK_ARG(env, ref); - CHECK_ARG(env, result); - - v8impl::Reference* reference = reinterpret_cast(ref); - *result = v8impl::JsValueFromV8LocalValue(reference->Get()); - - return napi_clear_last_error(env); -} - -napi_status napi_open_handle_scope(napi_env env, napi_handle_scope* result) { - // Omit NAPI_PREAMBLE and GET_RETURN_STATUS because V8 calls here cannot throw - // JS exceptions. - CHECK_ENV(env); - CHECK_ARG(env, result); - - *result = v8impl::JsHandleScopeFromV8HandleScope( - new v8impl::HandleScopeWrapper(env->isolate)); - env->open_handle_scopes++; - return napi_clear_last_error(env); -} - -napi_status napi_close_handle_scope(napi_env env, napi_handle_scope scope) { - // Omit NAPI_PREAMBLE and GET_RETURN_STATUS because V8 calls here cannot throw - // JS exceptions. - CHECK_ENV(env); - CHECK_ARG(env, scope); - if (env->open_handle_scopes == 0) { - return napi_handle_scope_mismatch; - } + CHECK_ARG(env, fun); - env->open_handle_scopes--; - delete v8impl::V8HandleScopeFromJsHandleScope(scope); - return napi_clear_last_error(env); + node::AddEnvironmentCleanupHook(env->isolate, fun, arg); + + return napi_ok; } -napi_status napi_open_escapable_handle_scope( - napi_env env, - napi_escapable_handle_scope* result) { - // Omit NAPI_PREAMBLE and GET_RETURN_STATUS because V8 calls here cannot throw - // JS exceptions. +napi_status napi_remove_env_cleanup_hook(napi_env env, + void (*fun)(void* arg), + void* arg) { CHECK_ENV(env); - CHECK_ARG(env, result); + CHECK_ARG(env, fun); - *result = v8impl::JsEscapableHandleScopeFromV8EscapableHandleScope( - new v8impl::EscapableHandleScopeWrapper(env->isolate)); - env->open_handle_scopes++; - return napi_clear_last_error(env); + node::RemoveEnvironmentCleanupHook(env->isolate, fun, arg); + + return napi_ok; } -napi_status napi_close_escapable_handle_scope( - napi_env env, - napi_escapable_handle_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_handle_scopes == 0) { - return napi_handle_scope_mismatch; - } +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); - delete v8impl::V8EscapableHandleScopeFromJsEscapableHandleScope(scope); - env->open_handle_scopes--; return napi_clear_last_error(env); } -napi_status napi_escape_handle(napi_env env, - napi_escapable_handle_scope scope, - napi_value escapee, - napi_value* result) { - // Omit NAPI_PREAMBLE and GET_RETURN_STATUS because V8 calls here cannot throw - // JS exceptions. - CHECK_ENV(env); - CHECK_ARG(env, scope); - CHECK_ARG(env, escapee); - CHECK_ARG(env, result); +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)); + } - v8impl::EscapableHandleScopeWrapper* s = - v8impl::V8EscapableHandleScopeFromJsEscapableHandleScope(scope); - if (!s->escape_called()) { - *result = v8impl::JsValueFromV8LocalValue( - s->Escape(v8impl::V8LocalValueFromJsValue(escapee))); - return napi_clear_last_error(env); + if (message_len != NAPI_AUTO_LENGTH) { + message_string.assign( + const_cast(message), message_len); + } else { + message_string.assign( + const_cast(message), strlen(message)); } - return napi_set_last_error(env, napi_escape_called_twice); + + node::FatalError(location_string.c_str(), message_string.c_str()); } napi_status napi_open_callback_scope(napi_env env, @@ -3194,64 +586,6 @@ napi_status napi_close_callback_scope(napi_env env, napi_callback_scope scope) { return napi_clear_last_error(env); } -napi_status napi_new_instance(napi_env env, - napi_value constructor, - size_t argc, - const napi_value* argv, - napi_value* result) { - NAPI_PREAMBLE(env); - CHECK_ARG(env, constructor); - if (argc > 0) { - CHECK_ARG(env, argv); - } - CHECK_ARG(env, result); - - v8::Local context = env->context(); - - v8::Local ctor; - CHECK_TO_FUNCTION(env, ctor, constructor); - - auto maybe = ctor->NewInstance(context, argc, - reinterpret_cast*>(const_cast(argv))); - - CHECK_MAYBE_EMPTY(env, maybe, napi_generic_failure); - - *result = v8impl::JsValueFromV8LocalValue(maybe.ToLocalChecked()); - return GET_RETURN_STATUS(env); -} - -napi_status napi_instanceof(napi_env env, - napi_value object, - napi_value constructor, - bool* result) { - NAPI_PREAMBLE(env); - CHECK_ARG(env, object); - CHECK_ARG(env, result); - - *result = false; - - v8::Local ctor; - v8::Local context = env->context(); - - CHECK_TO_OBJECT(env, context, ctor, constructor); - - if (!ctor->IsFunction()) { - napi_throw_type_error(env, - "ERR_NAPI_CONS_FUNCTION", - "Constructor must be a function"); - - return napi_set_last_error(env, napi_function_expected); - } - - napi_status status = napi_generic_failure; - - v8::Local val = v8impl::V8LocalValueFromJsValue(object); - auto maybe_result = val->InstanceOf(context, ctor); - CHECK_MAYBE_NOTHING(env, maybe_result, status); - *result = maybe_result.FromJust(); - return GET_RETURN_STATUS(env); -} - napi_status napi_async_init(napi_env env, napi_value async_resource, napi_value async_resource_name, @@ -3344,35 +678,6 @@ napi_status napi_make_callback(napi_env env, return GET_RETURN_STATUS(env); } -// Methods to support catching exceptions -napi_status napi_is_exception_pending(napi_env env, bool* result) { - // NAPI_PREAMBLE is not used here: this function must execute when there is a - // pending exception. - CHECK_ENV(env); - CHECK_ARG(env, result); - - *result = !env->last_exception.IsEmpty(); - return napi_clear_last_error(env); -} - -napi_status napi_get_and_clear_last_exception(napi_env env, - napi_value* result) { - // NAPI_PREAMBLE is not used here: this function must execute when there is a - // pending exception. - CHECK_ENV(env); - CHECK_ARG(env, result); - - if (env->last_exception.IsEmpty()) { - return napi_get_undefined(env, result); - } else { - *result = v8impl::JsValueFromV8LocalValue( - v8::Local::New(env->isolate, env->last_exception)); - env->last_exception.Reset(); - } - - return napi_clear_last_error(env); -} - napi_status napi_create_buffer(napi_env env, size_t length, void** data, @@ -3411,10 +716,10 @@ napi_status napi_create_external_buffer(napi_env env, env, finalize_cb, nullptr, finalize_hint); auto maybe = node::Buffer::New(isolate, - static_cast(data), - length, - v8impl::Finalizer::FinalizeBufferCallback, - finalizer); + static_cast(data), + length, + v8impl::BufferFinalizer::FinalizeBufferCallback, + finalizer); CHECK_MAYBE_EMPTY(env, maybe, napi_generic_failure); @@ -3477,314 +782,6 @@ napi_status napi_get_buffer_info(napi_env env, return napi_clear_last_error(env); } -napi_status napi_is_arraybuffer(napi_env env, napi_value value, bool* result) { - CHECK_ENV(env); - CHECK_ARG(env, value); - CHECK_ARG(env, result); - - v8::Local val = v8impl::V8LocalValueFromJsValue(value); - *result = val->IsArrayBuffer(); - - return napi_clear_last_error(env); -} - -napi_status napi_create_arraybuffer(napi_env env, - size_t byte_length, - void** data, - napi_value* result) { - NAPI_PREAMBLE(env); - CHECK_ARG(env, result); - - v8::Isolate* isolate = env->isolate; - v8::Local buffer = - v8::ArrayBuffer::New(isolate, byte_length); - - // Optionally return a pointer to the buffer's data, to avoid another call to - // retrieve it. - if (data != nullptr) { - *data = buffer->GetContents().Data(); - } - - *result = v8impl::JsValueFromV8LocalValue(buffer); - return GET_RETURN_STATUS(env); -} - -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_PREAMBLE(env); - CHECK_ARG(env, result); - - v8::Isolate* isolate = env->isolate; - v8::Local buffer = - v8::ArrayBuffer::New(isolate, external_data, byte_length); - - if (finalize_cb != nullptr) { - // Create a self-deleting weak reference that invokes the finalizer - // callback. - v8impl::Reference::New(env, - buffer, - 0, - true, - finalize_cb, - external_data, - finalize_hint); - } - - *result = v8impl::JsValueFromV8LocalValue(buffer); - return GET_RETURN_STATUS(env); -} - -napi_status napi_get_arraybuffer_info(napi_env env, - napi_value arraybuffer, - void** data, - size_t* byte_length) { - CHECK_ENV(env); - CHECK_ARG(env, arraybuffer); - - v8::Local value = v8impl::V8LocalValueFromJsValue(arraybuffer); - RETURN_STATUS_IF_FALSE(env, value->IsArrayBuffer(), napi_invalid_arg); - - v8::ArrayBuffer::Contents contents = - value.As()->GetContents(); - - if (data != nullptr) { - *data = contents.Data(); - } - - if (byte_length != nullptr) { - *byte_length = contents.ByteLength(); - } - - return napi_clear_last_error(env); -} - -napi_status napi_is_typedarray(napi_env env, napi_value value, bool* result) { - CHECK_ENV(env); - CHECK_ARG(env, value); - CHECK_ARG(env, result); - - v8::Local val = v8impl::V8LocalValueFromJsValue(value); - *result = val->IsTypedArray(); - - return napi_clear_last_error(env); -} - -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_PREAMBLE(env); - CHECK_ARG(env, arraybuffer); - CHECK_ARG(env, result); - - v8::Local value = v8impl::V8LocalValueFromJsValue(arraybuffer); - RETURN_STATUS_IF_FALSE(env, value->IsArrayBuffer(), napi_invalid_arg); - - v8::Local buffer = value.As(); - v8::Local typedArray; - - switch (type) { - case napi_int8_array: - CREATE_TYPED_ARRAY( - env, Int8Array, 1, buffer, byte_offset, length, typedArray); - break; - case napi_uint8_array: - CREATE_TYPED_ARRAY( - env, Uint8Array, 1, buffer, byte_offset, length, typedArray); - break; - case napi_uint8_clamped_array: - CREATE_TYPED_ARRAY( - env, Uint8ClampedArray, 1, buffer, byte_offset, length, typedArray); - break; - case napi_int16_array: - CREATE_TYPED_ARRAY( - env, Int16Array, 2, buffer, byte_offset, length, typedArray); - break; - case napi_uint16_array: - CREATE_TYPED_ARRAY( - env, Uint16Array, 2, buffer, byte_offset, length, typedArray); - break; - case napi_int32_array: - CREATE_TYPED_ARRAY( - env, Int32Array, 4, buffer, byte_offset, length, typedArray); - break; - case napi_uint32_array: - CREATE_TYPED_ARRAY( - env, Uint32Array, 4, buffer, byte_offset, length, typedArray); - break; - case napi_float32_array: - CREATE_TYPED_ARRAY( - env, Float32Array, 4, buffer, byte_offset, length, typedArray); - break; - case napi_float64_array: - CREATE_TYPED_ARRAY( - env, Float64Array, 8, buffer, byte_offset, length, typedArray); - break; - case napi_bigint64_array: - CREATE_TYPED_ARRAY( - env, BigInt64Array, 8, buffer, byte_offset, length, typedArray); - break; - case napi_biguint64_array: - CREATE_TYPED_ARRAY( - env, BigUint64Array, 8, buffer, byte_offset, length, typedArray); - break; - default: - return napi_set_last_error(env, napi_invalid_arg); - } - - *result = v8impl::JsValueFromV8LocalValue(typedArray); - return GET_RETURN_STATUS(env); -} - -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) { - CHECK_ENV(env); - CHECK_ARG(env, typedarray); - - v8::Local value = v8impl::V8LocalValueFromJsValue(typedarray); - RETURN_STATUS_IF_FALSE(env, value->IsTypedArray(), napi_invalid_arg); - - v8::Local array = value.As(); - - if (type != nullptr) { - if (value->IsInt8Array()) { - *type = napi_int8_array; - } else if (value->IsUint8Array()) { - *type = napi_uint8_array; - } else if (value->IsUint8ClampedArray()) { - *type = napi_uint8_clamped_array; - } else if (value->IsInt16Array()) { - *type = napi_int16_array; - } else if (value->IsUint16Array()) { - *type = napi_uint16_array; - } else if (value->IsInt32Array()) { - *type = napi_int32_array; - } else if (value->IsUint32Array()) { - *type = napi_uint32_array; - } else if (value->IsFloat32Array()) { - *type = napi_float32_array; - } else if (value->IsFloat64Array()) { - *type = napi_float64_array; - } else if (value->IsBigInt64Array()) { - *type = napi_bigint64_array; - } else if (value->IsBigUint64Array()) { - *type = napi_biguint64_array; - } - } - - if (length != nullptr) { - *length = array->Length(); - } - - v8::Local buffer = array->Buffer(); - if (data != nullptr) { - *data = static_cast(buffer->GetContents().Data()) + - array->ByteOffset(); - } - - if (arraybuffer != nullptr) { - *arraybuffer = v8impl::JsValueFromV8LocalValue(buffer); - } - - if (byte_offset != nullptr) { - *byte_offset = array->ByteOffset(); - } - - return napi_clear_last_error(env); -} - -napi_status napi_create_dataview(napi_env env, - size_t byte_length, - napi_value arraybuffer, - size_t byte_offset, - napi_value* result) { - NAPI_PREAMBLE(env); - CHECK_ARG(env, arraybuffer); - CHECK_ARG(env, result); - - v8::Local value = v8impl::V8LocalValueFromJsValue(arraybuffer); - RETURN_STATUS_IF_FALSE(env, value->IsArrayBuffer(), napi_invalid_arg); - - v8::Local buffer = value.As(); - if (byte_length + byte_offset > buffer->ByteLength()) { - napi_throw_range_error( - env, - "ERR_NAPI_INVALID_DATAVIEW_ARGS", - "byte_offset + byte_length should be less than or " - "equal to the size in bytes of the array passed in"); - return napi_set_last_error(env, napi_pending_exception); - } - v8::Local DataView = v8::DataView::New(buffer, byte_offset, - byte_length); - - *result = v8impl::JsValueFromV8LocalValue(DataView); - return GET_RETURN_STATUS(env); -} - -napi_status napi_is_dataview(napi_env env, napi_value value, bool* result) { - CHECK_ENV(env); - CHECK_ARG(env, value); - CHECK_ARG(env, result); - - v8::Local val = v8impl::V8LocalValueFromJsValue(value); - *result = val->IsDataView(); - - return napi_clear_last_error(env); -} - -napi_status napi_get_dataview_info(napi_env env, - napi_value dataview, - size_t* byte_length, - void** data, - napi_value* arraybuffer, - size_t* byte_offset) { - CHECK_ENV(env); - CHECK_ARG(env, dataview); - - v8::Local value = v8impl::V8LocalValueFromJsValue(dataview); - RETURN_STATUS_IF_FALSE(env, value->IsDataView(), napi_invalid_arg); - - v8::Local array = value.As(); - - if (byte_length != nullptr) { - *byte_length = array->ByteLength(); - } - - v8::Local buffer = array->Buffer(); - if (data != nullptr) { - *data = static_cast(buffer->GetContents().Data()) + - array->ByteOffset(); - } - - if (arraybuffer != nullptr) { - *arraybuffer = v8impl::JsValueFromV8LocalValue(buffer); - } - - if (byte_offset != nullptr) { - *byte_offset = array->ByteOffset(); - } - - return napi_clear_last_error(env); -} - -napi_status napi_get_version(napi_env env, uint32_t* result) { - CHECK_ENV(env); - CHECK_ARG(env, result); - *result = NAPI_VERSION; - return napi_clear_last_error(env); -} - napi_status napi_get_node_version(napi_env env, const napi_node_version** result) { CHECK_ENV(env); @@ -3799,18 +796,6 @@ napi_status napi_get_node_version(napi_env env, return napi_clear_last_error(env); } -napi_status napi_adjust_external_memory(napi_env env, - int64_t change_in_bytes, - int64_t* adjusted_value) { - CHECK_ENV(env); - CHECK_ARG(env, adjusted_value); - - *adjusted_value = env->isolate->AdjustAmountOfExternalAllocatedMemory( - change_in_bytes); - - return napi_clear_last_error(env); -} - namespace { namespace uvimpl { @@ -3830,7 +815,7 @@ static napi_status ConvertUVErrorCode(int code) { // Wrapper around uv_work_t which calls user-provided callbacks. class Work : public node::AsyncResource, public node::ThreadPoolWork { private: - explicit Work(napi_env env, + explicit Work(node_napi_env env, v8::Local async_resource, v8::Local async_resource_name, napi_async_execute_callback execute, @@ -3849,7 +834,7 @@ class Work : public node::AsyncResource, public node::ThreadPoolWork { virtual ~Work() { } public: - static Work* New(napi_env env, + static Work* New(node_napi_env env, v8::Local async_resource, v8::Local async_resource_name, napi_async_execute_callback execute, @@ -3897,7 +882,7 @@ class Work : public node::AsyncResource, public node::ThreadPoolWork { } private: - napi_env _env; + node_napi_env _env; void* _data; napi_async_execute_callback _execute; napi_async_complete_callback _complete; @@ -3938,9 +923,12 @@ napi_status napi_create_async_work(napi_env env, v8::Local resource_name; CHECK_TO_STRING(env, context, resource_name, async_resource_name); - uvimpl::Work* work = - uvimpl::Work::New(env, resource, resource_name, - execute, complete, data); + uvimpl::Work* work = uvimpl::Work::New(reinterpret_cast(env), + resource, + resource_name, + execute, + complete, + data); *result = reinterpret_cast(work); @@ -3959,7 +947,7 @@ napi_status napi_delete_async_work(napi_env env, napi_async_work work) { napi_status napi_get_uv_event_loop(napi_env env, uv_loop_t** loop) { CHECK_ENV(env); CHECK_ARG(env, loop); - *loop = env->node_env()->event_loop(); + *loop = reinterpret_cast(env)->node_env()->event_loop(); return napi_clear_last_error(env); } @@ -3991,76 +979,6 @@ napi_status napi_cancel_async_work(napi_env env, napi_async_work work) { return napi_clear_last_error(env); } -napi_status napi_create_promise(napi_env env, - napi_deferred* deferred, - napi_value* promise) { - NAPI_PREAMBLE(env); - CHECK_ARG(env, deferred); - CHECK_ARG(env, promise); - - auto maybe = v8::Promise::Resolver::New(env->context()); - CHECK_MAYBE_EMPTY(env, maybe, napi_generic_failure); - - auto v8_resolver = maybe.ToLocalChecked(); - auto v8_deferred = new node::Persistent(); - v8_deferred->Reset(env->isolate, v8_resolver); - - *deferred = v8impl::JsDeferredFromNodePersistent(v8_deferred); - *promise = v8impl::JsValueFromV8LocalValue(v8_resolver->GetPromise()); - return GET_RETURN_STATUS(env); -} - -napi_status napi_resolve_deferred(napi_env env, - napi_deferred deferred, - napi_value resolution) { - return v8impl::ConcludeDeferred(env, deferred, resolution, true); -} - -napi_status napi_reject_deferred(napi_env env, - napi_deferred deferred, - napi_value resolution) { - return v8impl::ConcludeDeferred(env, deferred, resolution, false); -} - -napi_status napi_is_promise(napi_env env, - napi_value promise, - bool* is_promise) { - CHECK_ENV(env); - CHECK_ARG(env, promise); - CHECK_ARG(env, is_promise); - - *is_promise = v8impl::V8LocalValueFromJsValue(promise)->IsPromise(); - - return napi_clear_last_error(env); -} - -napi_status napi_run_script(napi_env env, - napi_value script, - napi_value* result) { - NAPI_PREAMBLE(env); - CHECK_ARG(env, script); - CHECK_ARG(env, result); - - v8::Local v8_script = v8impl::V8LocalValueFromJsValue(script); - - if (!v8_script->IsString()) { - return napi_set_last_error(env, napi_string_expected); - } - - v8::Local context = env->context(); - - auto maybe_script = v8::Script::Compile(context, - v8::Local::Cast(v8_script)); - CHECK_MAYBE_EMPTY(env, maybe_script, napi_generic_failure); - - auto script_result = - maybe_script.ToLocalChecked()->Run(context); - CHECK_MAYBE_EMPTY(env, script_result, napi_generic_failure); - - *result = v8impl::JsValueFromV8LocalValue(script_result.ToLocalChecked()); - return GET_RETURN_STATUS(env); -} - napi_status napi_create_threadsafe_function(napi_env env, napi_value func, @@ -4103,7 +1021,7 @@ napi_create_threadsafe_function(napi_env env, initial_thread_count, context, max_queue_size, - env, + reinterpret_cast(env), thread_finalize_data, thread_finalize_cb, call_js_cb); @@ -4164,17 +1082,3 @@ napi_ref_threadsafe_function(napi_env env, napi_threadsafe_function func) { CHECK(func != nullptr); return reinterpret_cast(func)->Ref(); } - -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) { - return v8impl::Wrap(env, - js_object, - native_object, - finalize_cb, - finalize_hint, - result); -} diff --git a/src/node_api.h b/src/node_api.h index e8c1f79de7cd91..b36f6d4b7a4b9e 100644 --- a/src/node_api.h +++ b/src/node_api.h @@ -1,39 +1,17 @@ #ifndef SRC_NODE_API_H_ #define SRC_NODE_API_H_ -#include -#include +#ifdef BUILDING_NODE_EXTENSION + #ifdef _WIN32 + // Building native module against node + #define NAPI_EXTERN __declspec(dllimport) + #endif +#endif +#include "js_native_api.h" #include "node_api_types.h" struct uv_loop_s; // Forward declaration. -#ifndef NAPI_VERSION -#ifdef NAPI_EXPERIMENTAL -// Use INT_MAX, this should only be consumed by the pre-processor anyway. -#define NAPI_VERSION 2147483647 -#else -// The baseline version for N-API -#define NAPI_VERSION 3 -#endif -#endif - -#ifdef _WIN32 - #ifdef BUILDING_NODE_EXTENSION - #ifdef EXTERNAL_NAPI - // Building external N-API, or native module against external N-API - #define NAPI_EXTERN /* nothing */ - #else - // Building native module against node with built-in N-API - #define NAPI_EXTERN __declspec(dllimport) - #endif - #else - // Building node with built-in N-API - #define NAPI_EXTERN __declspec(dllexport) - #endif -#else - #define NAPI_EXTERN /* nothing */ -#endif - #ifdef _WIN32 # define NAPI_MODULE_EXPORT __declspec(dllexport) #else @@ -46,7 +24,6 @@ struct uv_loop_s; // Forward declaration. #define NAPI_NO_RETURN #endif - typedef napi_value (*napi_addon_register_func)(napi_env env, napi_value exports); @@ -75,14 +52,6 @@ typedef struct { static void fn(void) #endif -#ifdef __cplusplus -#define EXTERN_C_START extern "C" { -#define EXTERN_C_END } -#else -#define EXTERN_C_START -#define EXTERN_C_END -#endif - #define NAPI_MODULE_X(modname, regfunc, priv, flags) \ EXTERN_C_START \ static napi_module _module = \ @@ -122,347 +91,31 @@ typedef struct { napi_value NAPI_MODULE_INITIALIZER(napi_env env, \ napi_value exports) -#define NAPI_AUTO_LENGTH SIZE_MAX - EXTERN_C_START NAPI_EXTERN void napi_module_register(napi_module* mod); -NAPI_EXTERN napi_status -napi_get_last_error_info(napi_env env, - const napi_extended_error_info** result); - NAPI_EXTERN NAPI_NO_RETURN void napi_fatal_error(const char* location, size_t location_len, const char* message, size_t message_len); -// 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 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); -// 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_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); -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 provide node::Buffer functionality with napi types NAPI_EXTERN napi_status napi_create_buffer(napi_env env, @@ -488,57 +141,6 @@ NAPI_EXTERN napi_status napi_get_buffer_info(napi_env env, void** data, size_t* length); -// 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); - // Methods to manage simple async operations NAPI_EXTERN napi_status napi_create_async_work(napi_env env, @@ -555,54 +157,11 @@ NAPI_EXTERN napi_status napi_queue_async_work(napi_env env, NAPI_EXTERN napi_status napi_cancel_async_work(napi_env env, napi_async_work work); -// 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); - // version management -NAPI_EXTERN napi_status napi_get_version(napi_env env, uint32_t* result); - NAPI_EXTERN napi_status napi_get_node_version(napi_env env, const napi_node_version** version); -// 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 promise, - bool* is_promise); - -// Memory management -NAPI_EXTERN napi_status napi_adjust_external_memory(napi_env env, - int64_t change_in_bytes, - int64_t* adjusted_value); - -// Running a script -NAPI_EXTERN napi_status napi_run_script(napi_env env, - napi_value script, - napi_value* result); - #if NAPI_VERSION >= 2 // Return the current libuv event loop for a given environment @@ -613,14 +172,6 @@ NAPI_EXTERN napi_status napi_get_uv_event_loop(napi_env env, #if NAPI_VERSION >= 3 -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); - 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, @@ -631,6 +182,14 @@ 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 #ifdef NAPI_EXPERIMENTAL @@ -671,36 +230,6 @@ 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); -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); -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_EXPERIMENTAL EXTERN_C_END diff --git a/src/node_api_types.h b/src/node_api_types.h index 10215d9aa3a0ff..ab4f7ac58cb3dd 100644 --- a/src/node_api_types.h +++ b/src/node_api_types.h @@ -1,89 +1,15 @@ #ifndef SRC_NODE_API_TYPES_H_ #define SRC_NODE_API_TYPES_H_ -#include -#include +#include "js_native_api_types.h" -#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_scope__* napi_callback_scope; -typedef struct napi_callback_info__* napi_callback_info; typedef struct napi_async_context__* napi_async_context; typedef struct napi_async_work__* napi_async_work; -typedef struct napi_deferred__* napi_deferred; #ifdef NAPI_EXPERIMENTAL typedef struct napi_threadsafe_function__* napi_threadsafe_function; #endif // NAPI_EXPERIMENTAL -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, -} 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_status; - #ifdef NAPI_EXPERIMENTAL typedef enum { napi_tsfn_release, @@ -96,17 +22,11 @@ typedef enum { } napi_threadsafe_function_call_mode; #endif // NAPI_EXPERIMENTAL -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 void (*napi_async_execute_callback)(napi_env env, void* data); typedef void (*napi_async_complete_callback)(napi_env env, napi_status status, void* data); - #ifdef NAPI_EXPERIMENTAL typedef void (*napi_threadsafe_function_call_js)(napi_env env, napi_value js_callback, @@ -114,27 +34,6 @@ typedef void (*napi_threadsafe_function_call_js)(napi_env env, void* data); #endif // NAPI_EXPERIMENTAL -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; - typedef struct { uint32_t major; uint32_t minor; diff --git a/test/addons-napi/6_object_wrap/binding.cc b/test/addons-napi/6_object_wrap/binding.cc index ec4a4f347afc88..380ca6b27bb306 100644 --- a/test/addons-napi/6_object_wrap/binding.cc +++ b/test/addons-napi/6_object_wrap/binding.cc @@ -1,3 +1,4 @@ +#include #include "myobject.h" #include "../common.h" diff --git a/test/addons-napi/6_object_wrap/myobject.h b/test/addons-napi/6_object_wrap/myobject.h index b7f425951dda7f..f67dddf406299f 100644 --- a/test/addons-napi/6_object_wrap/myobject.h +++ b/test/addons-napi/6_object_wrap/myobject.h @@ -1,7 +1,7 @@ #ifndef TEST_ADDONS_NAPI_6_OBJECT_WRAP_MYOBJECT_H_ #define TEST_ADDONS_NAPI_6_OBJECT_WRAP_MYOBJECT_H_ -#include +#include class MyObject { public: diff --git a/test/addons-napi/7_factory_wrap/binding.cc b/test/addons-napi/7_factory_wrap/binding.cc index e937516c894a90..a5df612393ccec 100644 --- a/test/addons-napi/7_factory_wrap/binding.cc +++ b/test/addons-napi/7_factory_wrap/binding.cc @@ -1,3 +1,4 @@ +#include #include "myobject.h" #include "../common.h" diff --git a/test/addons-napi/7_factory_wrap/myobject.h b/test/addons-napi/7_factory_wrap/myobject.h index 172fcd3ca49295..24883dfa3a0c29 100644 --- a/test/addons-napi/7_factory_wrap/myobject.h +++ b/test/addons-napi/7_factory_wrap/myobject.h @@ -1,7 +1,7 @@ #ifndef TEST_ADDONS_NAPI_7_FACTORY_WRAP_MYOBJECT_H_ #define TEST_ADDONS_NAPI_7_FACTORY_WRAP_MYOBJECT_H_ -#include +#include class MyObject { public: diff --git a/test/addons-napi/8_passing_wrapped/binding.cc b/test/addons-napi/8_passing_wrapped/binding.cc index 48e94f10ec4838..f978fe151954ae 100644 --- a/test/addons-napi/8_passing_wrapped/binding.cc +++ b/test/addons-napi/8_passing_wrapped/binding.cc @@ -1,3 +1,4 @@ +#include #include "myobject.h" #include "../common.h" diff --git a/test/addons-napi/8_passing_wrapped/myobject.h b/test/addons-napi/8_passing_wrapped/myobject.h index 7c6a35aa853cc5..f1637b8dfee34e 100644 --- a/test/addons-napi/8_passing_wrapped/myobject.h +++ b/test/addons-napi/8_passing_wrapped/myobject.h @@ -1,7 +1,7 @@ #ifndef TEST_ADDONS_NAPI_8_PASSING_WRAPPED_MYOBJECT_H_ #define TEST_ADDONS_NAPI_8_PASSING_WRAPPED_MYOBJECT_H_ -#include +#include class MyObject { public: diff --git a/tools/install.py b/tools/install.py index c97518d4220788..af8e9e27086d96 100755 --- a/tools/install.py +++ b/tools/install.py @@ -169,6 +169,8 @@ def ignore_inspector_headers(files, dest): 'config.gypi', 'src/node.h', 'src/node_api.h', + 'src/js_native_api.h', + 'src/js_native_api_types.h', 'src/node_api_types.h', 'src/node_buffer.h', 'src/node_object_wrap.h',