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',