From 2f3c1c2b28c696be90f417d187efc45dd03234a3 Mon Sep 17 00:00:00 2001 From: Ohad Rau Date: Thu, 1 Aug 2019 10:59:11 -0700 Subject: [PATCH 1/3] Add WASM port of NAPI --- node.gyp | 2 + src/node.cc | 11 + test_wasm/test_wasm_napi.node.wasm | Bin 0 -> 22373 bytes wasm_node_api/node_api_overrides.c | 127 +++ wasm_node_api/node_api_overrides.h | 69 ++ wasm_node_api/wasm_napi_lib/js_native_api.h | 497 +++++++++++ .../wasm_napi_lib/js_native_api_types.h | 104 +++ wasm_node_api/wasm_napi_lib/node_api.h | 183 ++++ .../wasm_napi_lib/node_api_overrides.c | 138 +++ .../wasm_napi_lib/node_api_overrides.h | 67 ++ wasm_node_api/wasm_napi_lib/node_api_types.h | 37 + wasm_node_api/wasm_node_api.cc | 801 ++++++++++++++++++ wasm_node_api/wasm_node_api.h | 14 + 13 files changed, 2050 insertions(+) create mode 100755 test_wasm/test_wasm_napi.node.wasm create mode 100644 wasm_node_api/node_api_overrides.c create mode 100644 wasm_node_api/node_api_overrides.h create mode 100644 wasm_node_api/wasm_napi_lib/js_native_api.h create mode 100644 wasm_node_api/wasm_napi_lib/js_native_api_types.h create mode 100644 wasm_node_api/wasm_napi_lib/node_api.h create mode 100644 wasm_node_api/wasm_napi_lib/node_api_overrides.c create mode 100644 wasm_node_api/wasm_napi_lib/node_api_overrides.h create mode 100644 wasm_node_api/wasm_napi_lib/node_api_types.h create mode 100644 wasm_node_api/wasm_node_api.cc create mode 100644 wasm_node_api/wasm_node_api.h diff --git a/node.gyp b/node.gyp index 9a5556c2eff..7c39378912d 100644 --- a/node.gyp +++ b/node.gyp @@ -580,6 +580,8 @@ 'src/udp_wrap.cc', 'src/util.cc', 'src/uv.cc', + # wasm napi sources + 'wasm_node_api/wasm_node_api.cc', # headers to make for a more pleasant IDE experience 'src/aliased_buffer.h', 'src/async_wrap.h', diff --git a/src/node.cc b/src/node.cc index 9c788e64532..b9b655af546 100644 --- a/src/node.cc +++ b/src/node.cc @@ -38,6 +38,12 @@ #include "node_v8_platform-inl.h" #include "node_version.h" +#define WASM_BUILTIN_NODE_API 1 + +#if WASM_BUILTIN_NODE_API +#include "../wasm_node_api/wasm_node_api.h" +#endif + #if HAVE_OPENSSL #include "node_crypto.h" #endif @@ -421,6 +427,11 @@ MaybeLocal StartMainThreadExecution(Environment* env) { // To allow people to extend Node in different ways, this hook allows // one to drop a file lib/_third_party_main.js into the build // directory which will be executed instead of Node's normal loading. +#if WASM_BUILTIN_NODE_API + v8::Isolate* isolate = env->isolate(); + wasm_napi::RegisterNapiBuiltins(isolate); +#endif + if (NativeModuleEnv::Exists("_third_party_main")) { return StartExecution(env, "internal/main/run_third_party_main"); } diff --git a/test_wasm/test_wasm_napi.node.wasm b/test_wasm/test_wasm_napi.node.wasm new file mode 100755 index 0000000000000000000000000000000000000000..61878db634e516a3fcfb84a85f4f03353b4e99e9 GIT binary patch literal 22373 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 literal 0 HcmV?d00001 diff --git a/wasm_node_api/node_api_overrides.c b/wasm_node_api/node_api_overrides.c new file mode 100644 index 00000000000..3804ef5b75e --- /dev/null +++ b/wasm_node_api/node_api_overrides.c @@ -0,0 +1,127 @@ +#include +#include "node_api_overrides.h" + +void finalizeBuffer(napi_env env, void *finalize_data, void *finalize_hint) { + free(finalize_data); +} + +napi_status napi_create_arraybuffer(napi_env env, size_t byte_length, + void **data, napi_value *result) { + void *buffer = malloc(byte_length); + if (!buffer) { + return napi_generic_failure; + } + napi_status status = napi_create_external_arraybuffer(env, buffer, byte_length, + finalizeBuffer, NULL, + result); + if (status == napi_ok) { + *data = buffer; + } + return status; +} + +napi_status napi_create_buffer(napi_env env, size_t size, + void **data, napi_value *result) { + void *buffer = malloc(size); + if (!buffer) { + return napi_generic_failure; + } + napi_status status = napi_create_external_buffer(env, size, buffer, + finalizeBuffer, NULL, + result); + if (status == napi_ok) { + *data = buffer; + } + return status; +} + +napi_status napi_create_buffer_copy(napi_env env, size_t length, + const void *data, + void **result_data, + napi_value *result) { + void *buffer = malloc(length); + if (!buffer) { + return napi_generic_failure; + } + napi_status status = napi_create_external_buffer(env, length, buffer, + finalizeBuffer, + NULL, result); + if (status == napi_ok) { + memcpy(buffer, data, length); + *result_data = buffer; + } +} + +napi_property_descriptor* copy_properties( + size_t property_count, const napi_property_descriptor* properties) { + napi_property_descriptor* properties_ = + create_property_descriptors(property_count); + for (int i = 0; i < property_count; i++) { + set_napi_property_descriptor_utf8name( + &properties_[i], properties[i].utf8name); + set_napi_property_descriptor_name( + &properties_[i], properties[i].name); + set_napi_property_descriptor_method( + &properties_[i], properties[i].method); + set_napi_property_descriptor_getter( + &properties_[i], properties[i].getter); + set_napi_property_descriptor_setter( + &properties_[i], properties[i].setter); + set_napi_property_descriptor_value( + &properties_[i], properties[i].value); + set_napi_property_descriptor_attributes( + &properties_[i], properties[i].attributes); + set_napi_property_descriptor_data( + &properties_[i], properties[i].data); + } + return properties_; +} + +napi_status napi_define_properties( + napi_env env, napi_value object, size_t property_count, + const napi_property_descriptor* properties +) { + napi_property_descriptor* properties_ = + copy_properties(property_count, properties); + return _napi_define_properties(env, object, property_count, properties_); +} + +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 +) { + napi_property_descriptor* properties_ = + copy_properties(property_count, properties); + return _napi_define_class(env, utf8name, length, constructor, + data, property_count, properties_, + result); +} + +char* get_c_string(const char* cString) { + char* wasmString = (char*) malloc(get_string_length(cString)); + copy_string_to_wasm(cString, wasmString); + return wasmString; +} + +napi_status napi_get_last_error_info( + napi_env env, const napi_extended_error_info** result +) { + napi_extended_error_info* extended_error_info = + create_napi_extended_error_info(); + napi_status status = + _napi_get_last_error_info(env, &extended_error_info); + + napi_extended_error_info* result_ = *result; + result_->engine_error_code = + napi_extended_error_info_engine_error_code(extended_error_info); + result_->engine_reserved = + napi_extended_error_info_engine_reserved(extended_error_info); + result_->error_code = + napi_extended_error_info_error_code(extended_error_info); + char* error_msg = napi_extended_error_info_error_message(extended_error_info); + result_->error_message = + get_c_string(error_msg); + + return status; +} diff --git a/wasm_node_api/node_api_overrides.h b/wasm_node_api/node_api_overrides.h new file mode 100644 index 00000000000..837ba6c8388 --- /dev/null +++ b/wasm_node_api/node_api_overrides.h @@ -0,0 +1,69 @@ +#ifndef NODE_API_OVERRIDES_H_ +#define NODE_API_OVERRIDES_H_ + +#include +#include +#include + +/* // UNUSED: b/c we define our own versions w/out the builtin implementations +napi_status _napi_create_arraybuffer(napi_env env, size_t byte_length, + void **data, napi_value *result); + +napi_status _napi_create_buffer(napi_env env, size_t size, + void **data, napi_value *result); + +napi_status _napi_create_buffer_copy(napi_env env, size_t length, + const void *data, + void **result_data, + napi_value *result); */ + +napi_property_descriptor* create_property_descriptors( + size_t property_count); +void set_napi_property_descriptor_utf8name( + napi_property_descriptor* pd, const char* utf8name); +void set_napi_property_descriptor_name( + napi_property_descriptor* pd, napi_value name); +void set_napi_property_descriptor_method( + napi_property_descriptor* pd, napi_callback method); +void set_napi_property_descriptor_getter( + napi_property_descriptor* pd, napi_callback getter); +void set_napi_property_descriptor_setter( + napi_property_descriptor* pd, napi_callback setter); +void set_napi_property_descriptor_value( + napi_property_descriptor* pd, napi_value value); +void set_napi_property_descriptor_attributes( + napi_property_descriptor* pd, napi_property_attributes attributes); +void set_napi_property_descriptor_data( + napi_property_descriptor* pd, void* data); + +napi_status _napi_define_properties(napi_env env, + napi_value object, + size_t property_count, + const napi_property_descriptor* properties); + +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); + +size_t get_string_length(const char* cString); +void copy_string_to_wasm(const char* cString, char* wasmString); + +const char* napi_extended_error_info_error_message( + napi_extended_error_info* eei); +void* napi_extended_error_info_engine_reserved( + napi_extended_error_info* eei); +uint32_t napi_extended_error_info_engine_error_code( + napi_extended_error_info* eei); +napi_status napi_extended_error_info_error_code( + napi_extended_error_info* eei); + +napi_extended_error_info* create_napi_extended_error_info(); +napi_status _napi_get_last_error_info(napi_env env, + const napi_extended_error_info** result); + +#endif // NODE_API_OVERRIDES_H_ \ No newline at end of file diff --git a/wasm_node_api/wasm_napi_lib/js_native_api.h b/wasm_node_api/wasm_napi_lib/js_native_api.h new file mode 100644 index 00000000000..42a54c7a633 --- /dev/null +++ b/wasm_node_api/wasm_napi_lib/js_native_api.h @@ -0,0 +1,497 @@ +#ifndef WASM_NODE_API_JS_NATIVE_API_H_ +#define WASM_NODE_API_JS_NATIVE_API_H_ + +// This file needs to be compatible with C compilers. +#include // NOLINT(modernize-deprecated-headers) +#include // NOLINT(modernize-deprecated-headers) +#include "js_native_api_types.h" + +// Use INT_MAX, this should only be consumed by the pre-processor anyway. +#define NAPI_VERSION_EXPERIMENTAL 2147483647 +#ifndef NAPI_VERSION +#ifdef NAPI_EXPERIMENTAL +#define NAPI_VERSION NAPI_VERSION_EXPERIMENTAL +#else +// The baseline version for N-API +#define NAPI_VERSION 4 +#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 + #define NAPI_EXTERN __attribute__((visibility("default"), __import_module__("napi_unstable"))) +#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(opaque_ptr env, + const napi_extended_error_info** result); + +// Getters for defined singletons +NAPI_EXTERN napi_status napi_get_undefined(opaque_ptr env, opaque_ptr* result); +NAPI_EXTERN napi_status napi_get_null(opaque_ptr env, opaque_ptr* result); +NAPI_EXTERN napi_status napi_get_global(opaque_ptr env, opaque_ptr* result); +NAPI_EXTERN napi_status napi_get_boolean(opaque_ptr env, + bool value, + opaque_ptr* result); + +// Methods to create Primitive types/Objects +NAPI_EXTERN napi_status napi_create_object(opaque_ptr env, opaque_ptr* result); +NAPI_EXTERN napi_status napi_create_array(opaque_ptr env, opaque_ptr* result); +NAPI_EXTERN napi_status napi_create_array_with_length(opaque_ptr env, + size_t length, + opaque_ptr* result); +NAPI_EXTERN napi_status napi_create_double(opaque_ptr env, + double value, + opaque_ptr* result); +NAPI_EXTERN napi_status napi_create_int32(opaque_ptr env, + int32_t value, + opaque_ptr* result); +NAPI_EXTERN napi_status napi_create_uint32(opaque_ptr env, + uint32_t value, + opaque_ptr* result); +NAPI_EXTERN napi_status napi_create_int64(opaque_ptr env, + int64_t value, + opaque_ptr* result); +NAPI_EXTERN napi_status napi_create_string_latin1(opaque_ptr env, + const char* str, + size_t length, + opaque_ptr* result); +NAPI_EXTERN napi_status napi_create_string_utf8(opaque_ptr env, + const char* str, + size_t length, + opaque_ptr* result); +NAPI_EXTERN napi_status napi_create_string_utf16(opaque_ptr env, + const char16_t* str, + size_t length, + opaque_ptr* result); +NAPI_EXTERN napi_status napi_create_symbol(opaque_ptr env, + opaque_ptr description, + opaque_ptr* result); +NAPI_EXTERN napi_status napi_create_function(opaque_ptr env, + const char* utf8name, + size_t length, + napi_callback cb, + void* data, + opaque_ptr* result); +NAPI_EXTERN napi_status napi_create_error(opaque_ptr env, + opaque_ptr code, + opaque_ptr msg, + opaque_ptr* result); +NAPI_EXTERN napi_status napi_create_type_error(opaque_ptr env, + opaque_ptr code, + opaque_ptr msg, + opaque_ptr* result); +NAPI_EXTERN napi_status napi_create_range_error(opaque_ptr env, + opaque_ptr code, + opaque_ptr msg, + opaque_ptr* result); + +// Methods to get the native napi_value from Primitive type +NAPI_EXTERN napi_status napi_typeof(opaque_ptr env, + opaque_ptr value, + napi_valuetype* result); +NAPI_EXTERN napi_status napi_get_value_double(opaque_ptr env, + opaque_ptr value, + double* result); +NAPI_EXTERN napi_status napi_get_value_int32(opaque_ptr env, + opaque_ptr value, + int32_t* result); +NAPI_EXTERN napi_status napi_get_value_uint32(opaque_ptr env, + opaque_ptr value, + uint32_t* result); +NAPI_EXTERN napi_status napi_get_value_int64(opaque_ptr env, + opaque_ptr value, + int64_t* result); +NAPI_EXTERN napi_status napi_get_value_bool(opaque_ptr env, + opaque_ptr value, + bool* result); + +// Copies LATIN-1 encoded bytes from a string into a buffer. +NAPI_EXTERN napi_status napi_get_value_string_latin1(opaque_ptr env, + opaque_ptr 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(opaque_ptr env, + opaque_ptr 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(opaque_ptr env, + opaque_ptr 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(opaque_ptr env, + opaque_ptr value, + opaque_ptr* result); +NAPI_EXTERN napi_status napi_coerce_to_number(opaque_ptr env, + opaque_ptr value, + opaque_ptr* result); +NAPI_EXTERN napi_status napi_coerce_to_object(opaque_ptr env, + opaque_ptr value, + opaque_ptr* result); +NAPI_EXTERN napi_status napi_coerce_to_string(opaque_ptr env, + opaque_ptr value, + opaque_ptr* result); + +// Methods to work with Objects +NAPI_EXTERN napi_status napi_get_prototype(opaque_ptr env, + opaque_ptr object, + opaque_ptr* result); +NAPI_EXTERN napi_status napi_get_property_names(opaque_ptr env, + opaque_ptr object, + opaque_ptr* result); +NAPI_EXTERN napi_status napi_set_property(opaque_ptr env, + opaque_ptr object, + opaque_ptr key, + opaque_ptr value); +NAPI_EXTERN napi_status napi_has_property(opaque_ptr env, + opaque_ptr object, + opaque_ptr key, + bool* result); +NAPI_EXTERN napi_status napi_get_property(opaque_ptr env, + opaque_ptr object, + opaque_ptr key, + opaque_ptr* result); +NAPI_EXTERN napi_status napi_delete_property(opaque_ptr env, + opaque_ptr object, + opaque_ptr key, + bool* result); +NAPI_EXTERN napi_status napi_has_own_property(opaque_ptr env, + opaque_ptr object, + opaque_ptr key, + bool* result); +NAPI_EXTERN napi_status napi_set_named_property(opaque_ptr env, + opaque_ptr object, + const char* utf8name, + opaque_ptr value); +NAPI_EXTERN napi_status napi_has_named_property(opaque_ptr env, + opaque_ptr object, + const char* utf8name, + bool* result); +NAPI_EXTERN napi_status napi_get_named_property(opaque_ptr env, + opaque_ptr object, + const char* utf8name, + opaque_ptr* result); +NAPI_EXTERN napi_status napi_set_element(opaque_ptr env, + opaque_ptr object, + uint32_t index, + opaque_ptr value); +NAPI_EXTERN napi_status napi_has_element(opaque_ptr env, + opaque_ptr object, + uint32_t index, + bool* result); +NAPI_EXTERN napi_status napi_get_element(opaque_ptr env, + opaque_ptr object, + uint32_t index, + opaque_ptr* result); +NAPI_EXTERN napi_status napi_delete_element(opaque_ptr env, + opaque_ptr object, + uint32_t index, + bool* result); +NAPI_EXTERN napi_status +napi_define_properties(opaque_ptr env, + opaque_ptr object, + size_t property_count, + const napi_property_descriptor* properties); + +// Methods to work with Arrays +NAPI_EXTERN napi_status napi_is_array(opaque_ptr env, + opaque_ptr value, + bool* result); +NAPI_EXTERN napi_status napi_get_array_length(opaque_ptr env, + opaque_ptr value, + uint32_t* result); + +// Methods to compare values +NAPI_EXTERN napi_status napi_strict_equals(opaque_ptr env, + opaque_ptr lhs, + opaque_ptr rhs, + bool* result); + +// Methods to work with Functions +NAPI_EXTERN napi_status napi_call_function(opaque_ptr env, + opaque_ptr recv, + opaque_ptr func, + size_t argc, + const opaque_ptr* argv, + opaque_ptr* result); +NAPI_EXTERN napi_status napi_new_instance(opaque_ptr env, + opaque_ptr constructor, + opaque_ptr argc, + const opaque_ptr* argv, + opaque_ptr* result); +NAPI_EXTERN napi_status napi_instanceof(opaque_ptr env, + opaque_ptr object, + opaque_ptr 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( + opaque_ptr env, // [in] NAPI environment handle + opaque_ptr 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. + opaque_ptr* argv, // [out] Array of values + opaque_ptr* 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(opaque_ptr env, + opaque_ptr cbinfo, + opaque_ptr* result); +NAPI_EXTERN napi_status +napi_define_class(opaque_ptr env, + const char* utf8name, + size_t length, + napi_callback constructor, + void* data, + size_t property_count, + const napi_property_descriptor* properties, + opaque_ptr* result); + +// Methods to work with external data objects +NAPI_EXTERN napi_status napi_wrap(opaque_ptr env, + opaque_ptr js_object, + void* native_object, + opaque_ptr finalize_cb, + void* finalize_hint, + opaque_ptr* result); +NAPI_EXTERN napi_status napi_unwrap(opaque_ptr env, + opaque_ptr js_object, + void** result); +NAPI_EXTERN napi_status napi_remove_wrap(opaque_ptr env, + opaque_ptr js_object, + void** result); +NAPI_EXTERN napi_status napi_create_external(opaque_ptr env, + void* data, + napi_finalize finalize_cb, + void* finalize_hint, + opaque_ptr* result); +NAPI_EXTERN napi_status napi_get_value_external(opaque_ptr env, + opaque_ptr 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(opaque_ptr env, + opaque_ptr value, + uint32_t initial_refcount, + opaque_ptr* 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(opaque_ptr env, opaque_ptr 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(opaque_ptr env, + opaque_ptr 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(opaque_ptr env, + opaque_ptr 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(opaque_ptr env, + opaque_ptr ref, + opaque_ptr* result); + +NAPI_EXTERN napi_status napi_open_handle_scope(opaque_ptr env, + opaque_ptr* result); +NAPI_EXTERN napi_status napi_close_handle_scope(opaque_ptr env, + opaque_ptr scope); +NAPI_EXTERN napi_status +napi_open_escapable_handle_scope(opaque_ptr env, + opaque_ptr* result); +NAPI_EXTERN napi_status +napi_close_escapable_handle_scope(opaque_ptr env, + opaque_ptr scope); + +NAPI_EXTERN napi_status napi_escape_handle(opaque_ptr env, + opaque_ptr scope, + opaque_ptr escapee, + opaque_ptr* result); + +// Methods to support error handling +NAPI_EXTERN napi_status napi_throw(opaque_ptr env, opaque_ptr error); +NAPI_EXTERN napi_status napi_throw_error(opaque_ptr env, + const char* code, + const char* msg); +NAPI_EXTERN napi_status napi_throw_type_error(opaque_ptr env, + const char* code, + const char* msg); +NAPI_EXTERN napi_status napi_throw_range_error(opaque_ptr env, + const char* code, + const char* msg); +NAPI_EXTERN napi_status napi_is_error(opaque_ptr env, + opaque_ptr value, + bool* result); + +// Methods to support catching exceptions +NAPI_EXTERN napi_status napi_is_exception_pending(opaque_ptr env, bool* result); +NAPI_EXTERN napi_status napi_get_and_clear_last_exception(opaque_ptr env, + opaque_ptr* result); + +// Methods to work with array buffers and typed arrays +NAPI_EXTERN napi_status napi_is_arraybuffer(opaque_ptr env, + opaque_ptr value, + bool* result); +NAPI_EXTERN napi_status napi_create_arraybuffer(opaque_ptr env, + size_t byte_length, + void** data, + opaque_ptr* result); +NAPI_EXTERN napi_status +napi_create_external_arraybuffer(opaque_ptr env, + void* external_data, + size_t byte_length, + napi_finalize finalize_cb, + void* finalize_hint, + opaque_ptr* result); +NAPI_EXTERN napi_status napi_get_arraybuffer_info(opaque_ptr env, + opaque_ptr arraybuffer, + void** data, + size_t* byte_length); +NAPI_EXTERN napi_status napi_is_typedarray(opaque_ptr env, + opaque_ptr value, + bool* result); +NAPI_EXTERN napi_status napi_create_typedarray(opaque_ptr env, + napi_typedarray_type type, + size_t length, + opaque_ptr arraybuffer, + size_t byte_offset, + opaque_ptr* result); +NAPI_EXTERN napi_status napi_get_typedarray_info(opaque_ptr env, + opaque_ptr typedarray, + napi_typedarray_type* type, + size_t* length, + void** data, + opaque_ptr* arraybuffer, + size_t* byte_offset); + +NAPI_EXTERN napi_status napi_create_dataview(opaque_ptr env, + size_t length, + opaque_ptr arraybuffer, + size_t byte_offset, + opaque_ptr* result); +NAPI_EXTERN napi_status napi_is_dataview(opaque_ptr env, + opaque_ptr value, + bool* result); +NAPI_EXTERN napi_status napi_get_dataview_info(opaque_ptr env, + opaque_ptr dataview, + size_t* bytelength, + void** data, + opaque_ptr* arraybuffer, + size_t* byte_offset); + +// version management +NAPI_EXTERN napi_status napi_get_version(opaque_ptr env, uint32_t* result); + +// Promises +NAPI_EXTERN napi_status napi_create_promise(opaque_ptr env, + opaque_ptr* deferred, + opaque_ptr* promise); +NAPI_EXTERN napi_status napi_resolve_deferred(opaque_ptr env, + opaque_ptr deferred, + opaque_ptr resolution); +NAPI_EXTERN napi_status napi_reject_deferred(opaque_ptr env, + opaque_ptr deferred, + opaque_ptr rejection); +NAPI_EXTERN napi_status napi_is_promise(opaque_ptr env, + opaque_ptr promise, + bool* is_promise); + +// Running a script +NAPI_EXTERN napi_status napi_run_script(opaque_ptr env, + opaque_ptr script, + opaque_ptr* result); + +// Memory management +NAPI_EXTERN napi_status napi_adjust_external_memory(opaque_ptr env, + int64_t change_in_bytes, + int64_t* adjusted_value); + +#ifdef NAPI_EXPERIMENTAL + +// Dates +NAPI_EXTERN napi_status napi_create_date(opaque_ptr env, + double time, + opaque_ptr* result); + +NAPI_EXTERN napi_status napi_is_date(opaque_ptr env, + opaque_ptr value, + bool* is_date); + +NAPI_EXTERN napi_status napi_get_date_value(opaque_ptr env, + opaque_ptr value, + double* result); + +// BigInt +NAPI_EXTERN napi_status napi_create_bigint_int64(opaque_ptr env, + int64_t value, + opaque_ptr* result); +NAPI_EXTERN napi_status napi_create_bigint_uint64(opaque_ptr env, + uint64_t value, + opaque_ptr* result); +NAPI_EXTERN napi_status napi_create_bigint_words(opaque_ptr env, + int sign_bit, + size_t word_count, + const uint64_t* words, + opaque_ptr* result); +NAPI_EXTERN napi_status napi_get_value_bigint_int64(opaque_ptr env, + opaque_ptr value, + int64_t* result, + bool* lossless); +NAPI_EXTERN napi_status napi_get_value_bigint_uint64(opaque_ptr env, + opaque_ptr value, + uint64_t* result, + bool* lossless); +NAPI_EXTERN napi_status napi_get_value_bigint_words(opaque_ptr env, + opaque_ptr value, + int* sign_bit, + size_t* word_count, + uint64_t* words); +NAPI_EXTERN napi_status napi_add_finalizer(opaque_ptr env, + opaque_ptr js_object, + void* native_object, + napi_finalize finalize_cb, + void* finalize_hint, + opaque_ptr* result); +#endif // NAPI_EXPERIMENTAL + +EXTERN_C_END + +#endif // WASM_NODE_API_JS_NATIVE_API_H_ diff --git a/wasm_node_api/wasm_napi_lib/js_native_api_types.h b/wasm_node_api/wasm_napi_lib/js_native_api_types.h new file mode 100644 index 00000000000..8dd207420d1 --- /dev/null +++ b/wasm_node_api/wasm_napi_lib/js_native_api_types.h @@ -0,0 +1,104 @@ +#ifndef WASM_NODE_API_JS_NATIVE_API_TYPES_H_ +#define WASM_NODE_API_JS_NATIVE_API_TYPES_H_ + +// This file needs to be compatible with C compilers. +// This is a public include file, and these includes have essentially +// became part of it's API. +#include // NOLINT(modernize-deprecated-headers) +#include // NOLINT(modernize-deprecated-headers) + +#if !defined __cplusplus || (defined(_MSC_VER) && _MSC_VER < 1900) + typedef uint16_t char16_t; +#endif + +typedef uint64_t opaque_ptr; + +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_date_expected, +} napi_status; + +typedef opaque_ptr (*napi_callback)(opaque_ptr env, + opaque_ptr info); +typedef void (*napi_finalize)(opaque_ptr env, + void* finalize_data, + void* finalize_hint); + +typedef struct { + // One of utf8name or name should be NULL. + const char* utf8name; + opaque_ptr name; + + napi_callback method; + napi_callback getter; + napi_callback setter; + opaque_ptr 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 // WASM_NODE_API_JS_NATIVE_API_TYPES_H_ diff --git a/wasm_node_api/wasm_napi_lib/node_api.h b/wasm_node_api/wasm_napi_lib/node_api.h new file mode 100644 index 00000000000..ace0303bcf7 --- /dev/null +++ b/wasm_node_api/wasm_napi_lib/node_api.h @@ -0,0 +1,183 @@ +#ifndef WASM_NODE_API_NODE_API_H_ +#define WASM_NODE_API_NODE_API_H_ + +#include "js_native_api.h" +#include "node_api_types.h" + +struct uv_loop_s; // Forward declaration. + +#define NAPI_MODULE_EXPORT __attribute__((visibility("default"))) + +#ifdef __GNUC__ +#define NAPI_NO_RETURN __attribute__((noreturn)) +#else +#define NAPI_NO_RETURN +#endif + +typedef opaque_ptr (*napi_addon_register_func)(opaque_ptr env, + opaque_ptr exports); + +typedef struct { + int nm_version; + unsigned int nm_flags; + const char* nm_filename; + napi_addon_register_func nm_register_func; + const char* nm_modname; + void* nm_priv; + void* reserved[4]; +} napi_module; + +#define NAPI_MODULE_VERSION 1 + +#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 void napi_module_register(napi_module* mod); + +NAPI_EXTERN NAPI_NO_RETURN void napi_fatal_error(const char* location, + size_t location_len, + const char* message, + size_t message_len); + +// Methods for custom handling of async operations +NAPI_EXTERN napi_status napi_async_init(opaque_ptr env, + opaque_ptr async_resource, + opaque_ptr async_resource_name, + opaque_ptr* result); + +NAPI_EXTERN napi_status napi_async_destroy(opaque_ptr env, + opaque_ptr async_context); + +NAPI_EXTERN napi_status napi_make_callback(opaque_ptr env, + opaque_ptr async_context, + opaque_ptr recv, + opaque_ptr func, + size_t argc, + opaque_ptr* argv, + opaque_ptr* result); + +// Methods to provide node::Buffer functionality with napi types +NAPI_EXTERN napi_status napi_create_buffer(opaque_ptr env, + size_t length, + void** data, + opaque_ptr* result); +NAPI_EXTERN napi_status napi_create_external_buffer(opaque_ptr env, + size_t length, + void* data, + napi_finalize finalize_cb, + void* finalize_hint, + opaque_ptr* result); +NAPI_EXTERN napi_status napi_create_buffer_copy(opaque_ptr env, + size_t length, + const void* data, + void** result_data, + opaque_ptr* result); +NAPI_EXTERN napi_status napi_is_buffer(opaque_ptr env, + opaque_ptr value, + bool* result); +NAPI_EXTERN napi_status napi_get_buffer_info(opaque_ptr env, + opaque_ptr value, + void** data, + size_t* length); + +// Methods to manage simple async operations +NAPI_EXTERN +napi_status napi_create_async_work(opaque_ptr env, + opaque_ptr async_resource, + opaque_ptr async_resource_name, + napi_async_execute_callback execute, + napi_async_complete_callback complete, + void* data, + opaque_ptr* result); +NAPI_EXTERN napi_status napi_delete_async_work(opaque_ptr env, + opaque_ptr work); +NAPI_EXTERN napi_status napi_queue_async_work(opaque_ptr env, + opaque_ptr work); +NAPI_EXTERN napi_status napi_cancel_async_work(opaque_ptr env, + opaque_ptr work); + +// version management +NAPI_EXTERN +napi_status napi_get_node_version(opaque_ptr env, + const napi_node_version** version); + +#if NAPI_VERSION >= 2 + +// Return the current libuv event loop for a given environment +NAPI_EXTERN napi_status napi_get_uv_event_loop(opaque_ptr env, + opaque_ptr** loop); + +#endif // NAPI_VERSION >= 2 + +#if NAPI_VERSION >= 3 + +NAPI_EXTERN napi_status napi_fatal_exception(opaque_ptr env, opaque_ptr err); + +NAPI_EXTERN napi_status napi_add_env_cleanup_hook(opaque_ptr env, + void (*fun)(void* arg), + void* arg); + +NAPI_EXTERN napi_status napi_remove_env_cleanup_hook(opaque_ptr env, + void (*fun)(void* arg), + void* arg); + +NAPI_EXTERN napi_status napi_open_callback_scope(opaque_ptr env, + opaque_ptr resource_object, + opaque_ptr context, + opaque_ptr* result); + +NAPI_EXTERN napi_status napi_close_callback_scope(opaque_ptr env, + opaque_ptr scope); + +#endif // NAPI_VERSION >= 3 + +#if NAPI_VERSION >= 4 + +// Calling into JS from other threads +NAPI_EXTERN napi_status +napi_create_threadsafe_function(opaque_ptr env, + opaque_ptr func, + opaque_ptr async_resource, + opaque_ptr async_resource_name, + size_t max_queue_size, + size_t initial_thread_count, + void* thread_finalize_data, + napi_finalize thread_finalize_cb, + void* context, + napi_threadsafe_function_call_js call_js_cb, + opaque_ptr* result); + +NAPI_EXTERN napi_status +napi_get_threadsafe_function_context(opaque_ptr func, + void** result); + +NAPI_EXTERN napi_status +napi_call_threadsafe_function(opaque_ptr func, + void* data, + napi_threadsafe_function_call_mode is_blocking); + +NAPI_EXTERN napi_status +napi_acquire_threadsafe_function(opaque_ptr func); + +NAPI_EXTERN napi_status +napi_release_threadsafe_function(opaque_ptr func, + napi_threadsafe_function_release_mode mode); + +NAPI_EXTERN napi_status +napi_unref_threadsafe_function(opaque_ptr env, opaque_ptr func); + +NAPI_EXTERN napi_status +napi_ref_threadsafe_function(opaque_ptr env, opaque_ptr func); + +EXTERN_C_END + +#endif // NAPI_VERSION >= 4 + +#endif // WASM_NODE_API_NODE_API_H_ diff --git a/wasm_node_api/wasm_napi_lib/node_api_overrides.c b/wasm_node_api/wasm_napi_lib/node_api_overrides.c new file mode 100644 index 00000000000..d60c974fe01 --- /dev/null +++ b/wasm_node_api/wasm_napi_lib/node_api_overrides.c @@ -0,0 +1,138 @@ +#include +#include "node_api_overrides.h" + +void napi_module_register(napi_module* mod) { + opaque_ptr real_mod = create_napi_module(); + set_napi_module_nm_version(real_mod, mod->nm_version); + set_napi_module_nm_flags(real_mod, mod->nm_flags); + set_napi_module_nm_filename(real_mod, mod->nm_filename); + set_napi_module_nm_register_func(real_mod, mod->nm_register_func); + set_napi_module_nm_modname(real_mod, mod->nm_modname); + set_napi_module_nm_priv(real_mod, mod->nm_priv); + _napi_module_register(real_mod); +} + +void finalizeBuffer(opaque_ptr env, void *finalize_data, void *finalize_hint) { + free(finalize_data); +} + +napi_status napi_create_arraybuffer(opaque_ptr env, size_t byte_length, + void **data, opaque_ptr *result) { + void *buffer = malloc(byte_length); + if (!buffer) { + return napi_generic_failure; + } + napi_status status = napi_create_external_arraybuffer(env, buffer, byte_length, + finalizeBuffer, NULL, + result); + if (status == napi_ok) { + *data = buffer; + } + return status; +} + +napi_status napi_create_buffer(opaque_ptr env, size_t size, + void **data, opaque_ptr *result) { + void *buffer = malloc(size); + if (!buffer) { + return napi_generic_failure; + } + napi_status status = napi_create_external_buffer(env, size, buffer, + finalizeBuffer, NULL, + result); + if (status == napi_ok) { + *data = buffer; + } + return status; +} + +napi_status napi_create_buffer_copy(opaque_ptr env, size_t length, + const void *data, + void **result_data, + opaque_ptr *result) { + void *buffer = malloc(length); + if (!buffer) { + return napi_generic_failure; + } + napi_status status = napi_create_external_buffer(env, length, buffer, + finalizeBuffer, + NULL, result); + if (status == napi_ok) { + memcpy(buffer, data, length); + *result_data = buffer; + } +} + +opaque_ptr copy_properties( + size_t property_count, const napi_property_descriptor* properties) { + opaque_ptr properties_ = + create_property_descriptors(property_count); + for (int i = 0; i < property_count; i++) { + set_napi_property_descriptor_utf8name( + properties_, i, properties[i].utf8name); + set_napi_property_descriptor_name( + properties_, i, properties[i].name); + set_napi_property_descriptor_method( + properties_, i, properties[i].method); + set_napi_property_descriptor_getter( + properties_, i, properties[i].getter); + set_napi_property_descriptor_setter( + properties_, i, properties[i].setter); + set_napi_property_descriptor_value( + properties_, i, properties[i].value); + set_napi_property_descriptor_attributes( + properties_, i, properties[i].attributes); + set_napi_property_descriptor_data( + properties_, i, properties[i].data); + } + return properties_; +} + +napi_status napi_define_properties( + opaque_ptr env, opaque_ptr object, size_t property_count, + const napi_property_descriptor* properties +) { + opaque_ptr properties_ = + copy_properties(property_count, properties); + return _napi_define_properties(env, object, property_count, properties_); +} + +napi_status napi_define_class( + opaque_ptr env, const char* utf8name, size_t length, + napi_callback constructor, void* data, size_t property_count, + const napi_property_descriptor* properties, opaque_ptr* result +) { + napi_property_descriptor* properties_ = + copy_properties(property_count, properties); + return _napi_define_class(env, utf8name, length, constructor, + data, property_count, properties_, + result); +} + +char* get_c_string(const char* cString) { + char* wasmString = (char*) malloc(get_string_length(cString)); + copy_string_to_wasm(cString, wasmString); + return wasmString; +} + +napi_status napi_get_last_error_info( + opaque_ptr env, const napi_extended_error_info** result +) { + napi_extended_error_info* extended_error_info = + create_napi_extended_error_info(); + napi_status status = + _napi_get_last_error_info(env, &extended_error_info); + + napi_extended_error_info* result_ = *result; + result_->engine_error_code = + napi_extended_error_info_engine_error_code(extended_error_info); + result_->engine_reserved = + napi_extended_error_info_engine_reserved(extended_error_info); + result_->error_code = + napi_extended_error_info_error_code(extended_error_info); + char* error_msg = napi_extended_error_info_error_message(extended_error_info); + result_->error_message = + get_c_string(error_msg); + + return status; +} diff --git a/wasm_node_api/wasm_napi_lib/node_api_overrides.h b/wasm_node_api/wasm_napi_lib/node_api_overrides.h new file mode 100644 index 00000000000..52c699c49c7 --- /dev/null +++ b/wasm_node_api/wasm_napi_lib/node_api_overrides.h @@ -0,0 +1,67 @@ +#ifndef NODE_API_OVERRIDES_H_ +#define NODE_API_OVERRIDES_H_ + +#include +#include + +#include "node_api.h" + +NAPI_EXTERN void _napi_module_register(opaque_ptr mod); +NAPI_EXTERN opaque_ptr create_napi_module(); +NAPI_EXTERN void NAPI_EXTERN set_napi_module_nm_version(opaque_ptr mod, int nm_version); +NAPI_EXTERN void set_napi_module_nm_flags(opaque_ptr mod, unsigned int nm_flags); +NAPI_EXTERN void set_napi_module_nm_filename(opaque_ptr mod, const char* nm_filename); +NAPI_EXTERN void set_napi_module_nm_register_func(opaque_ptr mod, napi_addon_register_func nm_register_func); +NAPI_EXTERN void set_napi_module_nm_modname(opaque_ptr mod, const char* nm_modname); +NAPI_EXTERN void set_napi_module_nm_priv(opaque_ptr mod, void* nm_priv); + +NAPI_EXTERN opaque_ptr create_property_descriptors( + size_t property_count); +NAPI_EXTERN void set_napi_property_descriptor_utf8name( + opaque_ptr pd, int offset, const char* utf8name); +NAPI_EXTERN void set_napi_property_descriptor_name( + opaque_ptr pd, int offset, opaque_ptr name); +NAPI_EXTERN void set_napi_property_descriptor_method( + opaque_ptr pd, int offset, napi_callback method); +NAPI_EXTERN void set_napi_property_descriptor_getter( + opaque_ptr pd, int offset, napi_callback getter); +NAPI_EXTERN void set_napi_property_descriptor_setter( + opaque_ptr pd, int offset, napi_callback setter); +NAPI_EXTERN void set_napi_property_descriptor_value( + opaque_ptr pd, int offset, opaque_ptr value); +NAPI_EXTERN void set_napi_property_descriptor_attributes( + opaque_ptr pd, int offset, napi_property_attributes attributes); +NAPI_EXTERN void set_napi_property_descriptor_data( + opaque_ptr pd, int offset, void* data); + +NAPI_EXTERN napi_status _napi_define_properties(opaque_ptr env, + opaque_ptr object, + size_t property_count, + opaque_ptr properties); + +NAPI_EXTERN napi_status _napi_define_class(opaque_ptr env, + const char* utf8name, + size_t length, + napi_callback constructor, + void* data, + size_t property_count, + opaque_ptr properties, + opaque_ptr* result); + +NAPI_EXTERN size_t get_string_length(opaque_ptr cString); +NAPI_EXTERN void copy_string_to_wasm(opaque_ptr cString, char* wasmString); + +NAPI_EXTERN const char* napi_extended_error_info_error_message( + opaque_ptr eei); +NAPI_EXTERN void* napi_extended_error_info_engine_reserved( + opaque_ptr eei); +NAPI_EXTERN uint32_t napi_extended_error_info_engine_error_code( + opaque_ptr eei); +NAPI_EXTERN napi_status napi_extended_error_info_error_code( + opaque_ptr eei); + +NAPI_EXTERN opaque_ptr create_napi_extended_error_info(); +NAPI_EXTERN napi_status _napi_get_last_error_info(opaque_ptr env, + opaque_ptr* result); + +#endif // NODE_API_OVERRIDES_H_ \ No newline at end of file diff --git a/wasm_node_api/wasm_napi_lib/node_api_types.h b/wasm_node_api/wasm_napi_lib/node_api_types.h new file mode 100644 index 00000000000..a8090f6d014 --- /dev/null +++ b/wasm_node_api/wasm_napi_lib/node_api_types.h @@ -0,0 +1,37 @@ +#ifndef WASM_NODE_API_NODE_API_TYPES_H_ +#define WASM_NODE_API_NODE_API_TYPES_H_ + +#include "js_native_api_types.h" + +#if NAPI_VERSION >= 4 +typedef enum { + napi_tsfn_release, + napi_tsfn_abort +} napi_threadsafe_function_release_mode; + +typedef enum { + napi_tsfn_nonblocking, + napi_tsfn_blocking +} napi_threadsafe_function_call_mode; +#endif // NAPI_VERSION >= 4 + +typedef void (*napi_async_execute_callback)(opaque_ptr env, + void* data); +typedef void (*napi_async_complete_callback)(opaque_ptr env, + napi_status status, + void* data); +#if NAPI_VERSION >= 4 +typedef void (*napi_threadsafe_function_call_js)(opaque_ptr env, + opaque_ptr js_callback, + void* context, + void* data); +#endif // NAPI_VERSION >= 4 + +typedef struct { + uint32_t major; + uint32_t minor; + uint32_t patch; + const char* release; +} napi_node_version; + +#endif // WASM_NODE_API_NODE_API_TYPES_H_ diff --git a/wasm_node_api/wasm_node_api.cc b/wasm_node_api/wasm_node_api.cc new file mode 100644 index 00000000000..cd37c7b94a7 --- /dev/null +++ b/wasm_node_api/wasm_node_api.cc @@ -0,0 +1,801 @@ +#include +#include +#include + +#define NODE_WANT_INTERNALS 1 +#include "js_native_api_v8.h" +#include "wasm_node_api.h" +#include "node_api.h" +#include "v8-wasm.h" +#include "env.h" +#undef NODE_WANT_INTERNALS + +namespace node { +namespace wasm_napi { + +namespace w = v8::wasm; + +using callback = w::Func::callbackType; + +template +struct is_function_pointer { + static const bool value = + std::is_pointer::value ? + std::is_function::type>::value : + false; +}; + +template +constexpr auto is_function_pointer_v = is_function_pointer::value; + +template +class Native { +public: + using type = T; +}; + +template +class Wasm { +public: + using type = T; +}; + +template +struct is_native : public std::false_type {}; + +template +struct is_native> : public std::true_type {}; + +template +constexpr auto is_native_v = is_native::value; + +template +struct is_wasm : public std::false_type {}; + +template +struct is_wasm> : public std::true_type {}; + +template +constexpr auto is_wasm_v = is_wasm::value; + +template +using kind = typename K::type; + +template +constexpr w::ValKind wasmType() { + if constexpr (std::is_same, float>::value) { + return w::ValKind::F32; + } else if constexpr (std::is_same, double>::value) { + return w::ValKind::F64; + #if __WORDSIZE == 64 + } else if constexpr (is_native_v && std::is_pointer>::value) { + return w::ValKind::I64; + #endif + } else { + return w::ValKind::I32; + } +} + +template +constexpr auto exportVoid(callback cb) -> w::Func { + return w::Func( + w::FuncType({Args...}, {}), + cb + ); +}; + +template +constexpr auto exportFn(callback cb) -> w::Func { + return w::Func( + w::FuncType({Args...}, {Return}), + cb + ); +}; + +template +constexpr auto exportNative(callback cb) -> w::Func { + if constexpr(std::is_void>::value) { + return exportVoid()...>(cb); + } else { + return exportFn(), wasmType()...>(cb); + } +} + +template +constexpr Arg napiCast(const w::Val& v) { + if constexpr(std::is_same::value) { + return (Arg) v.f32(); + } else if constexpr(std::is_same::value) { + return (Arg) v.f64(); + } else { + return (Arg) v.i32(); + } +} + +template +constexpr kind napiUnwrap(const w::Context* ctx, const w::Val& v) { + if constexpr(is_wasm_v) { + if constexpr(/*std::is_same, + v8::MaybeLocal>::value*/ + is_function_pointer_v>) { + return (kind) v.i32();/*(ctx->table->get(v.i32()));*/ + } else { + return (kind) (&ctx->memory->data()[(uint32_t) v.i32()]); + } + #if __WORDSIZE == 64 + } else if constexpr(std::is_pointer>::value) { // Native pointer + return (kind) v.i64(); + #endif + } else { + return napiCast>(v); + } +}; + +template +constexpr w::Val napiWrap(kind v) { + if constexpr(std::is_same, float>::value) { + return w::Val((float) v); + } else if constexpr (std::is_same, double>::value) { + return w::Val((double) v); + #if __WORDSIZE == 64 + } else if constexpr(std::is_pointer>::value) { // Native pointer + return w::Val(static_cast((size_t) v)); + #endif + } else { + return w::Val(static_cast((size_t) v)); + } +} + +template +struct napiApply { + template + static constexpr auto apply( + kind (*fn)(kind...), + const w::Context* ctx, + const w::Val args[], + std::index_sequence + ) -> kind { + if constexpr(std::is_void>::value) { + fn(napiUnwrap(ctx, args[Indices])...); + } else { + return fn(napiUnwrap(ctx, args[Indices])...); + } + } +}; + +template +struct napiCall { +private: + using fp = kind (*)(kind...); +public: + template + static void const call( + const w::Context* ctx, const w::Val args[], w::Val results[] + ) { + if constexpr(std::is_void>::value) { + napiApply::apply(Fn, ctx, args, + std::index_sequence_for{}); + } else { + results[0] = napiWrap( + napiApply::apply(Fn, ctx, args, + std::index_sequence_for{})); + } + } +}; + +template +struct napiBind { + template (*const Fn)(kind...)> + static void bind(v8::Isolate* isolate, const char *name) { + w::Func fn = exportNative( + (callback) napiCall::template call); + w::RegisterEmbedderBuiltin(isolate, "napi_unstable", name, fn); + } +}; + +// Functions to deal with copying strings +size_t get_string_length(const char* cString) { + return strlen(cString); +} + +void copy_string_to_wasm(const char* cString, char* wasmString) { + strcpy(wasmString, cString); +} + +// Functions for dealing with napi modules +napi_module* create_napi_module() { + return (napi_module*) malloc(sizeof(napi_module)); +} + +void set_napi_module_nm_version(napi_module* mod, int nm_version) { + mod->nm_version = nm_version; +} + +void set_napi_module_nm_flags(napi_module* mod, unsigned int nm_flags) { + mod->nm_flags = nm_flags; +} + +void set_napi_module_nm_filename(napi_module* mod, const char* nm_filename) { + mod->nm_filename = nm_filename; +} + +// TODO(ohadrau): Is this a safe operation? +void set_napi_module_nm_register_func( + napi_module* mod, + napi_addon_register_func nm_register_func) { + mod->nm_register_func = nm_register_func; +} + +void set_napi_module_nm_modname(napi_module* mod, const char* nm_modname) { + mod->nm_modname = nm_modname; +} + +void set_napi_module_nm_priv(napi_module* mod, void* nm_priv) { + mod->nm_priv = nm_priv; +} + +// Copied from node_api.cc +struct node_napi_env__ : public napi_env__ { + explicit node_napi_env__(v8::Local context): + napi_env__(context) { + CHECK_NOT_NULL(node_env()); + } + + inline node::Environment* node_env() const { + return node::Environment::GetCurrent(context()); + } + + bool can_call_into_js() const override { + return node_env()->can_call_into_js(); + } +}; + +typedef node_napi_env__* node_napi_env; + +static inline napi_env GetEnv(v8::Local context) { + node_napi_env result; + + auto isolate = context->GetIsolate(); + auto global = context->Global(); + + // In the case of the string for which we grab the private and the value of + // the private on the global object we can call .ToLocalChecked() directly + // because we need to stop hard if either of them is empty. + // + // Re https://github.com/nodejs/node/pull/14217#discussion_r128775149 + auto value = global->GetPrivate(context, NAPI_PRIVATE_KEY(context, env)) + .ToLocalChecked(); + + if (value->IsExternal()) { + result = static_cast(value.As()->Value()); + } else { + 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 + // is either nothing or false. + CHECK(global->SetPrivate(context, NAPI_PRIVATE_KEY(context, env), external) + .FromJust()); + + // TODO(addaleax): There was previously code that tried to delete the + // napi_env when its v8::Context was garbage collected; + // However, as long as N-API addons using this napi_env are in place, + // the Context needs to be accessible and alive. + // Ideally, we'd want an on-addon-unload hook that takes care of this + // once all N-API addons using this napi_env are unloaded. + // For now, a per-Environment cleanup hook is the best we can do. + result->node_env()->AddCleanupHook( + [](void* arg) { + static_cast(arg)->Unref(); + }, + static_cast(result)); + } + + return result; +} + +struct wasm_napi_module_data { + napi_module* mod; + w::Context* ctx; +}; + +void _napi_module_register_by_symbol(v8::Local exports, + v8::Local module, + v8::Local context, + w::Context* wasmContext, + napi_addon_register_func fn) { + v8::Isolate* isolate = context->GetIsolate(); + v8::MaybeLocal initFunc = + wasmContext->table->get((size_t) fn); + delete wasmContext; + if (initFunc.IsEmpty()) { + node::Environment* node_env = node::Environment::GetCurrent(context); + CHECK_NOT_NULL(node_env); + node_env->ThrowError( + "Module has no declared entry point."); + return; + } + + // Create a new napi_env for this module or reference one if a pre-existing + // one is found. + napi_env env = GetEnv(context); + + napi_value _exports; + NapiCallIntoModuleThrow(env, [&]() { + v8::Local* exportsPtr = + new v8::Local(exports); + + v8::Local argv[2]; + // TODO(ohadrau): Can we use v8::External here? What would that translate to? + argv[0] = v8::BigInt::New(isolate, (size_t) env); + argv[1] = v8::BigInt::New(isolate, (size_t) exportsPtr); + + v8::MaybeLocal result = + initFunc.ToLocalChecked()->Call(context, context->Global(), 2, argv); + if (initFunc.IsEmpty()) { + node::Environment* node_env = node::Environment::GetCurrent(context); + CHECK_NOT_NULL(node_env); + node_env->ThrowError( + "Module initializer returned void."); + return; + } + v8::Local resultLocal; + result.ToLocal(&resultLocal); + exportsPtr = + (v8::Local*) (size_t) resultLocal + ->IntegerValue(context) + .ToChecked(); + _exports = v8impl::JsValueFromV8LocalValue(*exportsPtr); + + delete exportsPtr; + }); + + // If register function returned a non-null exports object different from + // the exports object we passed it, set that as the "exports" property of + // the module. + if (_exports != nullptr && + _exports != v8impl::JsValueFromV8LocalValue(exports)) { + napi_value _module = v8impl::JsValueFromV8LocalValue(module); + napi_set_named_property(env, _module, "exports", _exports); + } +} + +// Intercepts the Node-V8 module registration callback. Converts parameters +// to NAPI equivalents and then calls the registration callback specified +// by the NAPI module. +static void _napi_module_register_cb(v8::Local exports, + v8::Local module, + v8::Local context, + void* priv) { + wasm_napi_module_data* data = + reinterpret_cast(priv); + _napi_module_register_by_symbol(exports, module, context, + data->ctx, data->mod->nm_register_func); +} + +// Registers a NAPI module. +void _napi_module_register(w::Context* ctx, const w::Val args[], + w::Val results[]) { + napi_module* mod = (napi_module*) (size_t) args[0].i64(); + w::Context* ctxCopy = new w::Context(*ctx); + node::node_module* nm = new node::node_module { + -1, + mod->nm_flags | NM_F_DELETEME, + nullptr, + mod->nm_filename, + nullptr, + _napi_module_register_cb, + mod->nm_modname, + new wasm_napi_module_data { mod, ctxCopy }, // priv + nullptr, + }; + // TODO(ohadrau): Calling this will pass our v8::Context + // into the module rather than a v8::wasm::Context. This + // means we won't be able to access WASM memory and function + // tables within the module register function! (i.e. the + // whole point of writing this alternate register function) + node::node_module_register(nm); +} + +// Functions for dealing with property descriptors +napi_property_descriptor* create_property_descriptors( + size_t property_count) { + size_t total_size = property_count * sizeof(napi_property_descriptor); + return (napi_property_descriptor*) malloc(total_size); +} + +void set_napi_property_descriptor_utf8name( + napi_property_descriptor* pd, int offset, const char* utf8name) { + pd[offset].utf8name = utf8name; +} + +void set_napi_property_descriptor_name( + napi_property_descriptor* pd, int offset, napi_value name) { + pd[offset].name = name; +} + +void set_napi_property_descriptor_method( + napi_property_descriptor* pd, int offset, napi_callback method) { + pd[offset].method = method; +} + +void set_napi_property_descriptor_getter( + napi_property_descriptor* pd, int offset, napi_callback getter) { + pd[offset].getter = getter; +} + +void set_napi_property_descriptor_setter( + napi_property_descriptor* pd, int offset, napi_callback setter) { + pd[offset].setter = setter; +} + +void set_napi_property_descriptor_value( + napi_property_descriptor* pd, int offset, napi_value value) { + pd[offset].value = value; +} + +void set_napi_property_descriptor_attributes( + napi_property_descriptor* pd, int offset, napi_property_attributes attributes) { + pd[offset].attributes = attributes; +} + +void set_napi_property_descriptor_data( + napi_property_descriptor* pd, int offset, void* data) { + pd[offset].data = data; +} + +// Functions for dealing with extended error infos +napi_extended_error_info* create_napi_extended_error_info() { + return (napi_extended_error_info*) malloc(sizeof(napi_extended_error_info)); +} + +const char* napi_extended_error_info_error_message( + napi_extended_error_info* eei) { + return eei->error_message; +} + +void* napi_extended_error_info_engine_reserved(napi_extended_error_info* eei) { + return eei->engine_reserved; +} + +uint32_t napi_extended_error_info_engine_error_code( + napi_extended_error_info* eei) { + return eei->engine_error_code; +} + +napi_status napi_extended_error_info_error_code(napi_extended_error_info* eei) { + return eei->error_code; +} + +void RegisterNapiBuiltins(v8::Isolate* isolate) { + w::Func fn = exportNative, Native>( + (callback) _napi_module_register); + w::RegisterEmbedderBuiltin(isolate, "napi_unstable", "_napi_module_register", fn); + napiBind> + ::bind(isolate, "create_napi_module"); + napiBind, Native, Native> + ::bind(isolate, "set_napi_module_nm_version"); + napiBind, Native, Native> + ::bind(isolate, "set_napi_module_nm_flags"); + napiBind, Native, Wasm> + ::bind(isolate, "set_napi_module_nm_filename"); + napiBind, Native, Wasm> + ::bind(isolate, "set_napi_module_nm_register_func"); + napiBind, Native, Wasm> + ::bind(isolate, "set_napi_module_nm_modname"); + napiBind, Native, Wasm> + ::bind(isolate, "set_napi_module_nm_priv"); + + napiBind, Native, Wasm> + ::bind(isolate, "_napi_get_last_error_info"); + napiBind, Native, Native> + ::bind(isolate, "napi_throw"); + napiBind, Native, Wasm, Wasm> + ::bind(isolate, "napi_throw_error"); + napiBind, Native, Wasm, Wasm> + ::bind(isolate, "napi_throw_type_error"); + napiBind, Native, Wasm, Wasm> + ::bind(isolate, "napi_throw_range_error"); + napiBind, Native, Native, Wasm> + ::bind(isolate, "napi_is_error"); + napiBind, Native, Native, Native, Wasm> + ::bind(isolate, "napi_create_error"); + napiBind, Native, Native, Native, Wasm> + ::bind(isolate, "napi_create_type_error"); + napiBind, Native, Native, Native, Wasm> + ::bind(isolate, "napi_create_range_error"); + napiBind, Native, Wasm> + ::bind(isolate, "napi_get_and_clear_last_exception"); + napiBind, Native, Wasm> + ::bind(isolate, "napi_is_exception_pending"); + napiBind, Native, Native> + ::bind(isolate, "napi_fatal_exception"); + napiBind, Wasm, Native, Wasm, Native> + ::bind(isolate, "napi_fatal_error"); + napiBind, Native, Wasm> + ::bind(isolate, "napi_open_handle_scope"); + napiBind, Native, Native> + ::bind(isolate, "napi_close_handle_scope"); + napiBind, Native, Wasm> + ::bind(isolate, "napi_open_escapable_handle_scope"); + napiBind, Native, Native> + ::bind(isolate, "napi_close_escapable_handle_scope"); + napiBind, Native, Native, Native, Wasm> + ::bind(isolate, "napi_escape_handle"); + napiBind, Native, Native, Native, Wasm> + ::bind(isolate, "napi_create_reference"); + napiBind, Native, Native> + ::bind(isolate, "napi_delete_reference"); + napiBind, Native, Native, Wasm> + ::bind(isolate, "napi_reference_ref"); + napiBind, Native, Native, Wasm> + ::bind(isolate, "napi_reference_ref"); + napiBind, Native, Native, Wasm> + ::bind(isolate, "napi_get_reference_value"); + napiBind, Native, Wasm, Wasm> + ::bind(isolate, "napi_add_env_cleanup_hook"); + napiBind, Native, Wasm, Wasm> + ::bind(isolate, "napi_remove_env_cleanup_hook"); + napiBind, Native, Wasm> + ::bind(isolate, "napi_create_array"); + napiBind, Native, Native, Wasm> + ::bind(isolate, "napi_create_array_with_length"); + #if NAPI_VERSION >= 4 && defined(NAPI_EXPERIMENTAL) + napiBind, Native, Native, Wasm> + ::bind(isolate, "napi_create_date"); + #endif + napiBind, Native, Wasm, Wasm, Wasm, Wasm> + ::bind(isolate, "napi_create_external"); + napiBind, Native, Wasm, Native, Wasm, Wasm, Wasm> + ::bind(isolate, "napi_create_external_arraybuffer"); + napiBind, Native, Native, Wasm, Wasm, Wasm, Wasm> + ::bind(isolate, "napi_create_external_buffer"); + napiBind, Native, Wasm> + ::bind(isolate, "napi_create_object"); + napiBind, Native, Native, Wasm> + ::bind(isolate, "napi_create_symbol"); + napiBind, Native, Native, Native, Native, Native, Wasm> + ::bind(isolate, "napi_create_typedarray"); + napiBind, Native, Native, Native, Native, Wasm> + ::bind(isolate, "napi_create_dataview"); + napiBind, Native, Native, Wasm> + ::bind(isolate, "napi_create_int32"); + napiBind, Native, Native, Wasm> + ::bind(isolate, "napi_create_uint32"); + napiBind, Native, Native, Wasm> + ::bind(isolate, "napi_create_int64"); + napiBind, Native, Native, Wasm> + ::bind(isolate, "napi_create_double"); + #if NAPI_VERSION >= 4 && defined(NAPI_EXPERIMENTAL) + napiBind, Native, Native, Wasm> + ::bind(isolate, "napi_create_bigint_int64"); + napiBind, Native, Native, Wasm> + ::bind(isolate, "napi_create_bigint_uint64"); + napiBind, Native, Native, Native, Wasm, Wasm> + ::bind(isolate, "napi_create_bigint_words"); + #endif + napiBind, Native, Wasm, Native, Wasm> + ::bind(isolate, "napi_create_string_latin1"); + napiBind, Native, Wasm, Native, Wasm> + ::bind(isolate, "napi_create_string_utf16"); + napiBind, Native, Wasm, Native, Wasm> + ::bind(isolate, "napi_create_string_utf8"); + napiBind, Native, Native, Wasm> + ::bind(isolate, "napi_get_array_length"); + napiBind, Native, Native, Wasm> + ::bind(isolate, "napi_get_prototype"); + napiBind, Native, Native, Wasm, Wasm, Wasm, Wasm, Wasm> + ::bind(isolate, "napi_get_typedarray_info"); + napiBind, Native, Native, Wasm, Wasm, Wasm, Wasm> + ::bind(isolate, "napi_get_dataview_info"); + #if NAPI_VERSION >= 4 && defined(NAPI_EXPERIMENTAL) + napiBind, Native, Native, Wasm> + ::bind(isolate, "napi_get_date_value"); + #endif + napiBind, Native, Native, Wasm> + ::bind(isolate, "napi_get_value_bool"); + napiBind, Native, Native, Wasm> + ::bind(isolate, "napi_get_value_double"); + #if NAPI_VERSION >= 4 && defined(NAPI_EXPERIMENTAL) + napiBind, Native, Native, Wasm, Wasm> + ::bind(isolate, "napi_get_value_bigint_int64"); + napiBind, Native, Native, Wasm, Wasm> + ::bind(isolate, "napi_get_value_bigint_uint64"); + napiBind, Native, Native, Wasm, Wasm, Wasm> + ::bind(isolate, "napi_get_value_bigint_words"); + #endif + napiBind, Native, Native, Wasm> + ::bind(isolate, "napi_get_value_external"); + napiBind, Native, Native, Wasm> + ::bind(isolate, "napi_get_value_int32"); + napiBind, Native, Native, Wasm> + ::bind(isolate, "napi_get_value_uint32"); + napiBind, Native, Native, Wasm> + ::bind(isolate, "napi_get_value_int64"); + napiBind, Native, Native, Wasm, Native, Wasm> + ::bind(isolate, "napi_get_value_string_latin1"); + napiBind, Native, Native, Wasm, Native, Wasm> + ::bind(isolate, "napi_get_value_string_utf8"); + napiBind, Native, Native, Wasm, Native, Wasm> + ::bind(isolate, "napi_get_value_string_utf16"); + napiBind, Native, Native, Wasm> + ::bind(isolate, "napi_get_boolean"); + napiBind, Native, Wasm> + ::bind(isolate, "napi_get_global"); + napiBind, Native, Wasm> + ::bind(isolate, "napi_get_null"); + napiBind, Native, Wasm> + ::bind(isolate, "napi_get_undefined"); + napiBind, Native, Native, Wasm> + ::bind(isolate, "napi_coerce_to_bool"); + napiBind, Native, Native, Wasm> + ::bind(isolate, "napi_coerce_to_number"); + napiBind, Native, Native, Wasm> + ::bind(isolate, "napi_coerce_to_object"), + napiBind, Native, Native, Wasm> + ::bind(isolate, "napi_coerce_to_string"); + napiBind, Native, Native, Wasm> + ::bind(isolate, "napi_typeof"); + napiBind, Native, Native, Native, Wasm> + ::bind(isolate, "napi_instanceof"); + napiBind, Native, Native, Wasm> + ::bind(isolate, "napi_is_array"); + napiBind, Native, Native, Wasm> + ::bind(isolate, "napi_is_arraybuffer"); + napiBind, Native, Native, Wasm> + ::bind(isolate, "napi_is_buffer"); + #if NAPI_VERSION >= 4 && defined(NAPI_EXPERIMENTAL) + napiBind, Native, Native, Wasm> + ::bind(isolate, "napi_is_date"); + #endif + napiBind, Native, Native, Wasm> + ::bind(isolate, "napi_is_error"); + napiBind, Native, Native, Wasm> + ::bind(isolate, "napi_is_typedarray"); + napiBind, Native, Native, Wasm> + ::bind(isolate, "napi_is_dataview"); + napiBind, Native, Native, Native, Wasm> + ::bind(isolate, "napi_strict_equals"); + napiBind, Native, Native, Wasm> + ::bind(isolate, "napi_get_property_names"); + napiBind, Native, Native, Native, Native> + ::bind(isolate, "napi_set_property"); + napiBind, Native, Native, Native, Wasm> + ::bind(isolate, "napi_get_property"); + napiBind, Native, Native, Native, Wasm> + ::bind(isolate, "napi_has_property"); + napiBind, Native, Native, Native, Wasm> + ::bind(isolate, "napi_delete_property"); + napiBind, Native, Native, Native, Wasm> + ::bind(isolate, "napi_has_own_property"); + napiBind, Native, Native, Wasm, Native> + ::bind(isolate, "napi_set_named_property"); + napiBind, Native, Native, Wasm, Wasm> + ::bind(isolate, "napi_get_named_property"); + napiBind, Native, Native, Wasm, Wasm> + ::bind(isolate, "napi_has_named_property"); + napiBind, Native, Native, Native, Native> + ::bind(isolate, "napi_set_element"); + napiBind, Native, Native, Native, Wasm> + ::bind(isolate, "napi_get_element"); + napiBind, Native, Native, Native, Wasm> + ::bind(isolate, "napi_has_element"); + napiBind, Native, Native, Native, Wasm> + ::bind(isolate, "napi_delete_element"); + napiBind, Native, Native, Native, Native> + ::bind(isolate, "_napi_define_properties"); + napiBind, Native, Native, Native, Native, Wasm, Wasm> + ::bind(isolate, "napi_call_function"); + napiBind, Native, Wasm, Native, Wasm, Wasm, Wasm> + ::bind(isolate, "napi_create_function"); + napiBind, Native, Native, Wasm, Wasm, Wasm, Wasm> + ::bind(isolate, "napi_get_cb_info"); + napiBind, Native, Native, Wasm> + ::bind(isolate, "napi_get_new_target"); + napiBind, Native, Native, Native, Wasm, Wasm> + ::bind(isolate, "napi_new_instance"); + napiBind, Native, Wasm, Native, Wasm, Wasm, Native, Wasm, Wasm> + ::bind(isolate, "_napi_define_class"); + napiBind, Native, Native, Wasm, Wasm, Wasm, Wasm> + ::bind(isolate, "napi_wrap"); + napiBind, Native, Native, Wasm> + ::bind(isolate, "napi_unwrap"); + napiBind, Native, Native, Wasm> + ::bind(isolate, "napi_remove_wrap"); + #if NAPI_VERSION >= 4 && defined(NAPI_EXPERIMENTAL) + napiBind, Native, Native, Wasm, Wasm, Wasm, Wasm> + ::bind(isolate, "napi_add_finalizer"); + #endif + napiBind, Native, Native, Native, Wasm, Wasm, Wasm, Wasm> + ::bind(isolate, "napi_create_async_work"); + napiBind, Native, Native> + ::bind(isolate, "napi_delete_async_work"); + napiBind, Native, Native> + ::bind(isolate, "napi_queue_async_work"); + napiBind, Native, Native> + ::bind(isolate, "napi_cancel_async_work"); + napiBind, Native, Native, Native, Wasm> + ::bind(isolate, "napi_async_init"); + napiBind, Native, Native> + ::bind(isolate, "napi_async_destroy"); + napiBind, Native, Native, Native, Native, Native, Wasm, Wasm> + ::bind(isolate, "napi_make_callback"); + napiBind, Native, Native, Native, Wasm> + ::bind(isolate, "napi_open_callback_scope"); + napiBind, Native, Native> + ::bind(isolate, "napi_close_callback_scope"); + // TODO(ohadrau): Copy the node version struct + napiBind, Native, Wasm> + ::bind(isolate, "napi_get_node_version"); + napiBind, Native, Wasm> + ::bind(isolate, "napi_get_version"); + napiBind, Native, Native, Wasm> + ::bind(isolate, "napi_adjust_external_memory"); + napiBind, Native, Wasm, Wasm> + ::bind(isolate, "napi_create_promise"); + napiBind, Native, Native, Native> + ::bind(isolate, "napi_resolve_deferred"); + napiBind, Native, Native, Native> + ::bind(isolate, "napi_reject_deferred"); + napiBind, Native, Native, Wasm> + ::bind(isolate, "napi_is_promise"); + napiBind, Native, Native, Wasm> + ::bind(isolate, "napi_run_script"); + // TODO(ohadrau): What kind of copying has to be done here? + napiBind, Native, Wasm> + ::bind(isolate, "napi_get_uv_event_loop"); + napiBind, Native, Native, Native, Native, Native, Native, Wasm, Wasm, Wasm, Wasm, Wasm> + ::bind(isolate, "napi_create_threadsafe_function"); + napiBind, Native, Wasm> + ::bind(isolate, "napi_get_threadsafe_function_context"); + napiBind, Native, Wasm, Native> + ::bind(isolate, "napi_call_threadsafe_function"); + napiBind, Native> + ::bind(isolate, "napi_acquire_threadsafe_function"); + napiBind, Native, Native> + ::bind(isolate, "napi_release_threadsafe_function"); + napiBind, Native, Native> + ::bind(isolate, "napi_ref_threadsafe_function"); + napiBind, Native, Native> + ::bind(isolate, "napi_unref_threadsafe_function"); + // Extra functions to help write our overloads: + napiBind, Native> + ::bind(isolate, "get_string_length"); + napiBind, Native, Wasm> + ::bind(isolate, "copy_string_to_wasm"); + + napiBind, Native> + ::bind(isolate, "create_property_descriptors"); + napiBind, Native, Native, Wasm> + ::bind(isolate, "set_napi_property_descriptor_utf8name"); + napiBind, Native, Native, Native> + ::bind(isolate, "set_napi_property_descriptor_name"); + napiBind, Native, Native, Wasm> + ::bind(isolate, "set_napi_property_descriptor_method"); + napiBind, Native, Native, Wasm> + ::bind(isolate, "set_napi_property_descriptor_getter"); + napiBind, Native, Native, Wasm> + ::bind(isolate, "set_napi_property_descriptor_setter"); + napiBind, Native, Native, Native> + ::bind(isolate, "set_napi_property_descriptor_value"); + napiBind, Native, Native, Native> + ::bind(isolate, "set_napi_property_descriptor_attributes"); + // TODO(ohadrau): Is void* really gonna be a WASM pointer? + napiBind, Native, Native, Wasm> + ::bind(isolate, "set_napi_property_descriptor_data"); + + napiBind> + ::bind(isolate, "create_napi_extended_error_info"); + napiBind, Native> + ::bind(isolate, "napi_extended_error_info_error_message"); + napiBind, Native> + ::bind(isolate, "napi_extended_error_info_engine_reserved"); + napiBind, Native> + ::bind(isolate, "napi_extended_error_info_engine_error_code"); + napiBind, Native> + ::bind(isolate, "napi_extended_error_info_error_code"); +} + +} // namespace wasm_napi +} // namespace node \ No newline at end of file diff --git a/wasm_node_api/wasm_node_api.h b/wasm_node_api/wasm_node_api.h new file mode 100644 index 00000000000..9745e06c6c5 --- /dev/null +++ b/wasm_node_api/wasm_node_api.h @@ -0,0 +1,14 @@ +#ifndef WASM_NODE_API_H_ +#define WASM_NODE_API_H_ + +#include "v8.h" + +namespace node { +namespace wasm_napi { + +void RegisterNapiBuiltins(v8::Isolate* isolate); + +} // namespace wasm_napi +} // namespace node + +#endif // WASM_NODE_API_H \ No newline at end of file From b469559263e483de1ff1d0cbaba781df8b588f53 Mon Sep 17 00:00:00 2001 From: Ohad Rau Date: Wed, 31 Jul 2019 15:21:55 -0700 Subject: [PATCH 2/3] Implement WASM callbacks for NAPI properties --- deps/v8/src/api/api-wasm.cc | 9 +-- test_wasm/test_wasm_napi.node.wasm | Bin 22373 -> 22369 bytes wasm_node_api/wasm_node_api.cc | 123 ++++++++++++++++++++++++----- 3 files changed, 107 insertions(+), 25 deletions(-) diff --git a/deps/v8/src/api/api-wasm.cc b/deps/v8/src/api/api-wasm.cc index 729ed2e3d6e..0ef066aeff4 100644 --- a/deps/v8/src/api/api-wasm.cc +++ b/deps/v8/src/api/api-wasm.cc @@ -70,8 +70,7 @@ Context::~Context() { Val::Val(ValKind kind, value value) : kind_(kind), value_(value) {} Val::Val() : kind_(ANYREF) { value_.ref = nullptr; } -Val::Val(Val&& val) : Val(val.kind_, val.value_) { -} +Val::Val(Val&& val) : Val(val.kind_, val.value_) {} Val::Val(int32_t i) : kind_(I32) { value_.i32 = i; } Val::Val(int64_t i) : kind_(I64) { value_.i64 = i; } @@ -170,7 +169,7 @@ i::Address FuncData::v8_callback( ) { FuncData* self = reinterpret_cast(data); i::Isolate* isolate = self->isolate; - HandleScope scope(reinterpret_cast(isolate)); + i::HandleScope scope(isolate); int num_param_types = self->param_count; int num_result_types = self->result_count; @@ -203,9 +202,7 @@ i::Address FuncData::v8_callback( if (raw == i::kNullAddress) { params[i] = Val(nullptr); } else { - i::JSReceiver raw_obj = i::JSReceiver::cast(i::Object(raw)); - i::Handle obj(raw_obj, raw_obj.GetIsolate()); - params[i] = Val(reinterpret_cast(obj->address())); + params[i] = Val(reinterpret_cast(raw)); } break; } diff --git a/test_wasm/test_wasm_napi.node.wasm b/test_wasm/test_wasm_napi.node.wasm index 61878db634e516a3fcfb84a85f4f03353b4e99e9..d383b41cbd88598d029f3f3235c374f2c498c0ea 100755 GIT binary patch delta 171 zcmaF5j`86-MjnR5;#@`s1_s8-j1m)hT3MOu>ly1dPI$)5Bqlz&f~ARZ+2-FYlkFK_ zPwsconta|tZu51QUM6`*1;t)#t)OHx@a*n zZ$99%ib+KwXt`prqk>|H0*gXOmSU(vs7eD6Fen7`bFXG(WvgeZW18IQ9_4Aq#G=5a z$tBLxfR$nn0ORC1#Sf%1wMhPAR`p{f#wP*uqg;+DGDkG NLe1shtnOjO4*-ZKC?Eg; diff --git a/wasm_node_api/wasm_node_api.cc b/wasm_node_api/wasm_node_api.cc index cd37c7b94a7..2af7a3b0476 100644 --- a/wasm_node_api/wasm_node_api.cc +++ b/wasm_node_api/wasm_node_api.cc @@ -304,10 +304,10 @@ void _napi_module_register_by_symbol(v8::Local exports, v8::Local module, v8::Local context, w::Context* wasmContext, - napi_addon_register_func fn) { + napi_addon_register_func init) { v8::Isolate* isolate = context->GetIsolate(); v8::MaybeLocal initFunc = - wasmContext->table->get((size_t) fn); + wasmContext->table->get((size_t) init); delete wasmContext; if (initFunc.IsEmpty()) { node::Environment* node_env = node::Environment::GetCurrent(context); @@ -322,18 +322,17 @@ void _napi_module_register_by_symbol(v8::Local exports, napi_env env = GetEnv(context); napi_value _exports; - NapiCallIntoModuleThrow(env, [&]() { - v8::Local* exportsPtr = - new v8::Local(exports); + NapiCallIntoModuleThrow(env, [&]() { v8::Local argv[2]; // TODO(ohadrau): Can we use v8::External here? What would that translate to? + // WARN: Requires --wasm-experimental-bigint flag argv[0] = v8::BigInt::New(isolate, (size_t) env); - argv[1] = v8::BigInt::New(isolate, (size_t) exportsPtr); + argv[1] = v8::BigInt::New(isolate, (size_t) v8impl::JsValueFromV8LocalValue(exports)); v8::MaybeLocal result = initFunc.ToLocalChecked()->Call(context, context->Global(), 2, argv); - if (initFunc.IsEmpty()) { + if (result.IsEmpty()) { node::Environment* node_env = node::Environment::GetCurrent(context); CHECK_NOT_NULL(node_env); node_env->ThrowError( @@ -342,13 +341,17 @@ void _napi_module_register_by_symbol(v8::Local exports, } v8::Local resultLocal; result.ToLocal(&resultLocal); - exportsPtr = - (v8::Local*) (size_t) resultLocal - ->IntegerValue(context) - .ToChecked(); - _exports = v8impl::JsValueFromV8LocalValue(*exportsPtr); - delete exportsPtr; + v8::MaybeLocal resultBigInt = + resultLocal->ToBigInt(context); + if (resultBigInt.IsEmpty()) { + node::Environment* node_env = node::Environment::GetCurrent(context); + CHECK_NOT_NULL(node_env); + node_env->ThrowError( + "Module initializer returned invalid value."); + return; + } + _exports = (napi_value) resultBigInt.ToLocalChecked()->Uint64Value(); }); // If register function returned a non-null exports object different from @@ -415,9 +418,72 @@ void set_napi_property_descriptor_name( pd[offset].name = name; } -void set_napi_property_descriptor_method( +/*void set_napi_property_descriptor_method( napi_property_descriptor* pd, int offset, napi_callback method) { pd[offset].method = method; +}*/ + +struct wasm_napi_callback_data { + w::Context* ctx; + int fp; + void* data; +}; + +napi_value napi_method_callback(napi_env env, napi_callback_info cb_info) { + void* data; + napi_get_cb_info(env, cb_info, nullptr, nullptr, nullptr, &data); + wasm_napi_callback_data* callback_data = + reinterpret_cast(data); + + v8::Isolate* isolate = callback_data->ctx->isolate; + v8::Local context = isolate->GetCurrentContext(); + v8::Local function = + callback_data->ctx->table->get( + callback_data->fp + ).ToLocalChecked(); + + size_t callback_argc = 2; + v8::Local callback_argv[2]; + callback_argv[0] = v8::BigInt::New(isolate, (size_t) env); + // TODO(ohadrau): Modify the cb_info.data to only pass in callback_data->data + callback_argv[1] = v8::BigInt::New(isolate, (size_t) cb_info); + + v8::MaybeLocal result = + function->Call(context, context->Global(), callback_argc, callback_argv); + + napi_value return_value; + + if (result.IsEmpty()) { + napi_get_null(env, &return_value); + } else { + v8::Local resultLocal; + result.ToLocal(&resultLocal); + + v8::MaybeLocal resultBigInt = + resultLocal->ToBigInt(context); + if (resultBigInt.IsEmpty()) { + napi_get_null(env, &return_value); + } else { + return_value = (napi_value) + resultBigInt.ToLocalChecked()->Uint64Value(); + } + } + + return return_value; +} + +void set_napi_property_descriptor_method( + w::Context* ctx, const w::Val args[], + w::Val results[]) { + napi_property_descriptor* pd = + (napi_property_descriptor*) args[0].i64(); + int offset = args[1].i32(); + int fp = args[2].i32(); + + // TODO(ohadrau): Currently a memory leak! + w::Context* ctxCopy = new w::Context(*ctx); + pd[offset].data = new wasm_napi_callback_data { ctxCopy, fp }; + pd[offset].method = napi_method_callback; } void set_napi_property_descriptor_getter( @@ -442,7 +508,17 @@ void set_napi_property_descriptor_attributes( void set_napi_property_descriptor_data( napi_property_descriptor* pd, int offset, void* data) { - pd[offset].data = data; + // WARN(ohadrau): Due to the way that we store the FP & context + // we can't overwrite the data here. Instead we can store a second + // void* data inside of the napi_method_callback_data struct. + if (pd[offset].data) { + wasm_napi_callback_data* callback_data = + reinterpret_cast(pd[offset].data); + callback_data->data = data; + } else { + // TODO(ohadrau): Currently a memory leak! + pd[offset].data = new wasm_napi_callback_data { nullptr, 0, data }; + } } // Functions for dealing with extended error infos @@ -469,9 +545,10 @@ napi_status napi_extended_error_info_error_code(napi_extended_error_info* eei) { } void RegisterNapiBuiltins(v8::Isolate* isolate) { - w::Func fn = exportNative, Native>( + w::Func register_fn = exportNative, Native>( (callback) _napi_module_register); - w::RegisterEmbedderBuiltin(isolate, "napi_unstable", "_napi_module_register", fn); + w::RegisterEmbedderBuiltin(isolate, "napi_unstable", "_napi_module_register", + register_fn); napiBind> ::bind(isolate, "create_napi_module"); napiBind, Native, Native> @@ -771,8 +848,16 @@ void RegisterNapiBuiltins(v8::Isolate* isolate) { ::bind(isolate, "set_napi_property_descriptor_utf8name"); napiBind, Native, Native, Native> ::bind(isolate, "set_napi_property_descriptor_name"); - napiBind, Native, Native, Wasm> - ::bind(isolate, "set_napi_property_descriptor_method"); + + w::Func set_method_fn = + exportNative, Native, + Native, Wasm>( + (callback) set_napi_property_descriptor_method); + w::RegisterEmbedderBuiltin(isolate, "napi_unstable", + "set_napi_property_descriptor_method", + set_method_fn); + /*napiBind, Native, Native, Wasm> + ::bind(isolate, "set_napi_property_descriptor_method");*/ napiBind, Native, Native, Wasm> ::bind(isolate, "set_napi_property_descriptor_getter"); napiBind, Native, Native, Wasm> From 362776bf6b56c48dd45f63f7dca7bb9e52151d55 Mon Sep 17 00:00:00 2001 From: Ohad Rau Date: Fri, 2 Aug 2019 13:31:43 -0700 Subject: [PATCH 3/3] Cleanup wasm node api --- src/node.cc | 1 + test_wasm/Makefile | 27 ++++ test_wasm/export.syms | 2 + test_wasm/import.syms | 30 +++++ test_wasm/test_napi.c | 40 ++++++ wasm_node_api/node_api_overrides.c | 127 ------------------ wasm_node_api/node_api_overrides.h | 69 ---------- wasm_node_api/wasm_napi_lib/js_native_api.h | 1 + .../wasm_napi_lib/js_native_api_types.h | 1 + wasm_node_api/wasm_napi_lib/node_api.h | 1 + wasm_node_api/wasm_napi_lib/node_api_types.h | 1 + wasm_node_api/wasm_node_api.cc | 113 ++++++++++++---- 12 files changed, 192 insertions(+), 221 deletions(-) create mode 100644 test_wasm/Makefile create mode 100644 test_wasm/export.syms create mode 100644 test_wasm/import.syms create mode 100644 test_wasm/test_napi.c delete mode 100644 wasm_node_api/node_api_overrides.c delete mode 100644 wasm_node_api/node_api_overrides.h diff --git a/src/node.cc b/src/node.cc index b9b655af546..ccacd381131 100644 --- a/src/node.cc +++ b/src/node.cc @@ -38,6 +38,7 @@ #include "node_v8_platform-inl.h" #include "node_version.h" +// TODO(ohadrau): Move this to a configure flag #define WASM_BUILTIN_NODE_API 1 #if WASM_BUILTIN_NODE_API diff --git a/test_wasm/Makefile b/test_wasm/Makefile new file mode 100644 index 00000000000..4070c6fd71d --- /dev/null +++ b/test_wasm/Makefile @@ -0,0 +1,27 @@ +SYSROOT=$$HOME/Projects/wasi-sysroot/sysroot +TARGET=wasm32-wasi + +COMMA=, + +SOURCES=test_napi.c ../wasm_node_api/wasm_napi_lib/node_api_overrides.c + +OBJECTS=$(SOURCES:.c=.o) + +IMPORTS=$(shell cat import.syms) +EXPORTS=$(shell cat export.syms) + +CFLAGS=-I../wasm_node_api/wasm_napi_lib --sysroot=$(SYSROOT) --target=$(TARGET) +IMPORTFLAGS=-allow-undefined-file import.syms +EXPORTFLAGS=$(addprefix --export=, $(EXPORTS)) --export-table +LDFLAGS=--entry=main $(IMPORTFLAGS) $(EXPORTFLAGS) -L$(SYSROOT)/lib/wasm32-wasi -lc + +all: test_wasm_napi.node.wasm + +clean: + rm -f $(OBJECTS) test-napi.wasm + +test_wasm_napi.wasm: $(OBJECTS) + wasm-ld $(LDFLAGS) -o $@ $^ + +%.o: %.c + clang $(CFLAGS) -o $@ -c $< diff --git a/test_wasm/export.syms b/test_wasm/export.syms new file mode 100644 index 00000000000..7388fac0d1c --- /dev/null +++ b/test_wasm/export.syms @@ -0,0 +1,2 @@ +main +Init diff --git a/test_wasm/import.syms b/test_wasm/import.syms new file mode 100644 index 00000000000..71b432cec09 --- /dev/null +++ b/test_wasm/import.syms @@ -0,0 +1,30 @@ +napi_create_string_utf8 +create_napi_module +set_napi_module_nm_version +set_napi_module_nm_flags +set_napi_module_nm_filename +set_napi_module_nm_register_func +set_napi_module_nm_modname +set_napi_module_nm_priv +_napi_module_register +napi_create_external_arraybuffer +napi_create_external_buffer +create_property_descriptors +set_napi_property_descriptor_utf8name +set_napi_property_descriptor_name +set_napi_property_descriptor_method +set_napi_property_descriptor_getter +set_napi_property_descriptor_setter +set_napi_property_descriptor_value +set_napi_property_descriptor_attributes +set_napi_property_descriptor_data +_napi_define_properties +_napi_define_class +get_string_length +copy_string_to_wasm +create_napi_extended_error_info +_napi_get_last_error_info +napi_extended_error_info_engine_error_code +napi_extended_error_info_engine_reserved +napi_extended_error_info_error_code +napi_extended_error_info_error_message \ No newline at end of file diff --git a/test_wasm/test_napi.c b/test_wasm/test_napi.c new file mode 100644 index 00000000000..517e639e73d --- /dev/null +++ b/test_wasm/test_napi.c @@ -0,0 +1,40 @@ +#include +#include + +typedef opaque_ptr napi_env; +typedef opaque_ptr napi_callback_info; +typedef opaque_ptr napi_value; + +napi_value Hello(napi_env env, napi_callback_info info) { + napi_status status; + napi_value world; + status = napi_create_string_utf8(env, "world", 5, &world); + assert(status == napi_ok); + return world; +} + +#define DECLARE_NAPI_METHOD(name, func) \ + { name, 0, func, 0, 0, 0, napi_default, 0 } + +napi_value Init(napi_env env, napi_value exports) { + napi_status status; + napi_property_descriptor desc = DECLARE_NAPI_METHOD("hello", Hello); + status = napi_define_properties(env, exports, 1, &desc); + assert(status == napi_ok); + return exports; +} + +napi_module mod = { + NAPI_MODULE_VERSION, + 0, + __FILE__, + Init, + "test_wasm_napi", + NULL, + {0} +}; + +int main(int argc, char **argv) { + napi_module_register(&mod); + return 0; +} diff --git a/wasm_node_api/node_api_overrides.c b/wasm_node_api/node_api_overrides.c deleted file mode 100644 index 3804ef5b75e..00000000000 --- a/wasm_node_api/node_api_overrides.c +++ /dev/null @@ -1,127 +0,0 @@ -#include -#include "node_api_overrides.h" - -void finalizeBuffer(napi_env env, void *finalize_data, void *finalize_hint) { - free(finalize_data); -} - -napi_status napi_create_arraybuffer(napi_env env, size_t byte_length, - void **data, napi_value *result) { - void *buffer = malloc(byte_length); - if (!buffer) { - return napi_generic_failure; - } - napi_status status = napi_create_external_arraybuffer(env, buffer, byte_length, - finalizeBuffer, NULL, - result); - if (status == napi_ok) { - *data = buffer; - } - return status; -} - -napi_status napi_create_buffer(napi_env env, size_t size, - void **data, napi_value *result) { - void *buffer = malloc(size); - if (!buffer) { - return napi_generic_failure; - } - napi_status status = napi_create_external_buffer(env, size, buffer, - finalizeBuffer, NULL, - result); - if (status == napi_ok) { - *data = buffer; - } - return status; -} - -napi_status napi_create_buffer_copy(napi_env env, size_t length, - const void *data, - void **result_data, - napi_value *result) { - void *buffer = malloc(length); - if (!buffer) { - return napi_generic_failure; - } - napi_status status = napi_create_external_buffer(env, length, buffer, - finalizeBuffer, - NULL, result); - if (status == napi_ok) { - memcpy(buffer, data, length); - *result_data = buffer; - } -} - -napi_property_descriptor* copy_properties( - size_t property_count, const napi_property_descriptor* properties) { - napi_property_descriptor* properties_ = - create_property_descriptors(property_count); - for (int i = 0; i < property_count; i++) { - set_napi_property_descriptor_utf8name( - &properties_[i], properties[i].utf8name); - set_napi_property_descriptor_name( - &properties_[i], properties[i].name); - set_napi_property_descriptor_method( - &properties_[i], properties[i].method); - set_napi_property_descriptor_getter( - &properties_[i], properties[i].getter); - set_napi_property_descriptor_setter( - &properties_[i], properties[i].setter); - set_napi_property_descriptor_value( - &properties_[i], properties[i].value); - set_napi_property_descriptor_attributes( - &properties_[i], properties[i].attributes); - set_napi_property_descriptor_data( - &properties_[i], properties[i].data); - } - return properties_; -} - -napi_status napi_define_properties( - napi_env env, napi_value object, size_t property_count, - const napi_property_descriptor* properties -) { - napi_property_descriptor* properties_ = - copy_properties(property_count, properties); - return _napi_define_properties(env, object, property_count, properties_); -} - -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 -) { - napi_property_descriptor* properties_ = - copy_properties(property_count, properties); - return _napi_define_class(env, utf8name, length, constructor, - data, property_count, properties_, - result); -} - -char* get_c_string(const char* cString) { - char* wasmString = (char*) malloc(get_string_length(cString)); - copy_string_to_wasm(cString, wasmString); - return wasmString; -} - -napi_status napi_get_last_error_info( - napi_env env, const napi_extended_error_info** result -) { - napi_extended_error_info* extended_error_info = - create_napi_extended_error_info(); - napi_status status = - _napi_get_last_error_info(env, &extended_error_info); - - napi_extended_error_info* result_ = *result; - result_->engine_error_code = - napi_extended_error_info_engine_error_code(extended_error_info); - result_->engine_reserved = - napi_extended_error_info_engine_reserved(extended_error_info); - result_->error_code = - napi_extended_error_info_error_code(extended_error_info); - char* error_msg = napi_extended_error_info_error_message(extended_error_info); - result_->error_message = - get_c_string(error_msg); - - return status; -} diff --git a/wasm_node_api/node_api_overrides.h b/wasm_node_api/node_api_overrides.h deleted file mode 100644 index 837ba6c8388..00000000000 --- a/wasm_node_api/node_api_overrides.h +++ /dev/null @@ -1,69 +0,0 @@ -#ifndef NODE_API_OVERRIDES_H_ -#define NODE_API_OVERRIDES_H_ - -#include -#include -#include - -/* // UNUSED: b/c we define our own versions w/out the builtin implementations -napi_status _napi_create_arraybuffer(napi_env env, size_t byte_length, - void **data, napi_value *result); - -napi_status _napi_create_buffer(napi_env env, size_t size, - void **data, napi_value *result); - -napi_status _napi_create_buffer_copy(napi_env env, size_t length, - const void *data, - void **result_data, - napi_value *result); */ - -napi_property_descriptor* create_property_descriptors( - size_t property_count); -void set_napi_property_descriptor_utf8name( - napi_property_descriptor* pd, const char* utf8name); -void set_napi_property_descriptor_name( - napi_property_descriptor* pd, napi_value name); -void set_napi_property_descriptor_method( - napi_property_descriptor* pd, napi_callback method); -void set_napi_property_descriptor_getter( - napi_property_descriptor* pd, napi_callback getter); -void set_napi_property_descriptor_setter( - napi_property_descriptor* pd, napi_callback setter); -void set_napi_property_descriptor_value( - napi_property_descriptor* pd, napi_value value); -void set_napi_property_descriptor_attributes( - napi_property_descriptor* pd, napi_property_attributes attributes); -void set_napi_property_descriptor_data( - napi_property_descriptor* pd, void* data); - -napi_status _napi_define_properties(napi_env env, - napi_value object, - size_t property_count, - const napi_property_descriptor* properties); - -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); - -size_t get_string_length(const char* cString); -void copy_string_to_wasm(const char* cString, char* wasmString); - -const char* napi_extended_error_info_error_message( - napi_extended_error_info* eei); -void* napi_extended_error_info_engine_reserved( - napi_extended_error_info* eei); -uint32_t napi_extended_error_info_engine_error_code( - napi_extended_error_info* eei); -napi_status napi_extended_error_info_error_code( - napi_extended_error_info* eei); - -napi_extended_error_info* create_napi_extended_error_info(); -napi_status _napi_get_last_error_info(napi_env env, - const napi_extended_error_info** result); - -#endif // NODE_API_OVERRIDES_H_ \ No newline at end of file diff --git a/wasm_node_api/wasm_napi_lib/js_native_api.h b/wasm_node_api/wasm_napi_lib/js_native_api.h index 42a54c7a633..5eb4795e243 100644 --- a/wasm_node_api/wasm_napi_lib/js_native_api.h +++ b/wasm_node_api/wasm_napi_lib/js_native_api.h @@ -1,3 +1,4 @@ +// Taken from src/js_native_api.h #ifndef WASM_NODE_API_JS_NATIVE_API_H_ #define WASM_NODE_API_JS_NATIVE_API_H_ diff --git a/wasm_node_api/wasm_napi_lib/js_native_api_types.h b/wasm_node_api/wasm_napi_lib/js_native_api_types.h index 8dd207420d1..24f7c8e8ad8 100644 --- a/wasm_node_api/wasm_napi_lib/js_native_api_types.h +++ b/wasm_node_api/wasm_napi_lib/js_native_api_types.h @@ -1,3 +1,4 @@ +// Taken from src/js_native_api_types.h #ifndef WASM_NODE_API_JS_NATIVE_API_TYPES_H_ #define WASM_NODE_API_JS_NATIVE_API_TYPES_H_ diff --git a/wasm_node_api/wasm_napi_lib/node_api.h b/wasm_node_api/wasm_napi_lib/node_api.h index ace0303bcf7..5c8d0aba9c5 100644 --- a/wasm_node_api/wasm_napi_lib/node_api.h +++ b/wasm_node_api/wasm_napi_lib/node_api.h @@ -1,3 +1,4 @@ +// Taken from src/node_api.h #ifndef WASM_NODE_API_NODE_API_H_ #define WASM_NODE_API_NODE_API_H_ diff --git a/wasm_node_api/wasm_napi_lib/node_api_types.h b/wasm_node_api/wasm_napi_lib/node_api_types.h index a8090f6d014..f742a513bd2 100644 --- a/wasm_node_api/wasm_napi_lib/node_api_types.h +++ b/wasm_node_api/wasm_napi_lib/node_api_types.h @@ -1,3 +1,4 @@ +// Taken from src/node_api_types.h #ifndef WASM_NODE_API_NODE_API_TYPES_H_ #define WASM_NODE_API_NODE_API_TYPES_H_ diff --git a/wasm_node_api/wasm_node_api.cc b/wasm_node_api/wasm_node_api.cc index 2af7a3b0476..02f57c822fc 100644 --- a/wasm_node_api/wasm_node_api.cc +++ b/wasm_node_api/wasm_node_api.cc @@ -28,6 +28,13 @@ struct is_function_pointer { template constexpr auto is_function_pointer_v = is_function_pointer::value; +/* Native and Wasm are essentially type annotations that tell us whether + * a value was created in native code or in WASM code. This is important + * because of the different pointer sizes between systems, different memory + * spaces of native code vs. WASM, and other differences like function pointer + * representations. These types are defined such that `kind> == T` and + * `kind> == T`, allowing us to "unwrap" the types when we actually + * use them. */ template class Native { public: @@ -61,6 +68,9 @@ constexpr auto is_wasm_v = is_wasm::value; template using kind = typename K::type; +/* Converts a given C++ type into its canonical WASM representation. In + * this case, floats become F32, doubles become F64, native pointers + * become I64, and everything else becomes I32. */ template constexpr w::ValKind wasmType() { if constexpr (std::is_same, float>::value) { @@ -76,8 +86,17 @@ constexpr w::ValKind wasmType() { } } +/* These two functions (createVoidFn and createResultFn) allow us to turn + * a given WASM callback into a `v8::wasm::Func` value (that can be shared + * with WASM code). It does this by constructing the appropriate + * `v8::wasm::FuncType` based on the type parameters passed in and creating + * a `v8::wasm::Func` from the callback. The difference between the two + * functions is that `createVoidFn` deals with functions that return void, + * while `createResultFn` deals with functions that return a single value. + * Note that the WASM API allows us to create functions with >1 return value, + * but that functionality isn't necessary to embed N-API. */ template -constexpr auto exportVoid(callback cb) -> w::Func { +constexpr auto createVoidFn(callback cb) -> w::Func { return w::Func( w::FuncType({Args...}, {}), cb @@ -85,24 +104,29 @@ constexpr auto exportVoid(callback cb) -> w::Func { }; template -constexpr auto exportFn(callback cb) -> w::Func { +constexpr auto createResultFn(callback cb) -> w::Func { return w::Func( w::FuncType({Args...}, {Return}), cb ); }; +/* Determines what kind of function type we're dealing with and uses either + * createVoidFn or createResultFn as appropriate. */ template -constexpr auto exportNative(callback cb) -> w::Func { +constexpr auto createFn(callback cb) -> w::Func { if constexpr(std::is_void>::value) { - return exportVoid()...>(cb); + return createVoidFn()...>(cb); } else { - return exportFn(), wasmType()...>(cb); + return createResultFn(), wasmType()...>(cb); } } +/* wasmCast implements the v8::wasm::Val -> C++ type conversion. For types + * other than floating point numbers, this just takes the 32-bit number and + * casts it to the proper type. */ template -constexpr Arg napiCast(const w::Val& v) { +constexpr Arg wasmCast(const w::Val& v) { if constexpr(std::is_same::value) { return (Arg) v.f32(); } else if constexpr(std::is_same::value) { @@ -112,27 +136,32 @@ constexpr Arg napiCast(const w::Val& v) { } } +/* This function builds on top of wasmCast, but allows for more complex + * interpretations of WASM values. Specifically, this automatically + * dereferences pointers from the WASM memory block and discerns between + * different pointer sizes as necessary. */ template -constexpr kind napiUnwrap(const w::Context* ctx, const w::Val& v) { +constexpr kind wasmUnwrap(const w::Context* ctx, const w::Val& v) { if constexpr(is_wasm_v) { - if constexpr(/*std::is_same, - v8::MaybeLocal>::value*/ - is_function_pointer_v>) { - return (kind) v.i32();/*(ctx->table->get(v.i32()));*/ + if constexpr(is_function_pointer_v>) { + return (kind) v.i32(); } else { - return (kind) (&ctx->memory->data()[(uint32_t) v.i32()]); + uint32_t addr = v.i32(); + return (kind) addr == 0 ? nullptr : &ctx->memory->data()[addr]; } #if __WORDSIZE == 64 } else if constexpr(std::is_pointer>::value) { // Native pointer return (kind) v.i64(); #endif } else { - return napiCast>(v); + return wasmCast>(v); } }; +/* This function is the inverse of wasmUnwrap and is able to "re-wrap" the + * C++ value in a v8::wasm::Val. */ template -constexpr w::Val napiWrap(kind v) { +constexpr w::Val wasmWrap(kind v) { if constexpr(std::is_same, float>::value) { return w::Val((float) v); } else if constexpr (std::is_same, double>::value) { @@ -146,8 +175,28 @@ constexpr w::Val napiWrap(kind v) { } } +/* This function is analogous to std::apply in that it allows us to call generic + * functions using an array of arguments but is specifically designed to unwrap + * arguments using wasmUnwrap. This allows us to statically create WASM call + * wrappers for N-API functions (i.e. converting every parameter into a N-API + * value before making the call). + * + * The use of `std::index_sequence` is a bit of a hack that's needed in order to + * implement calls for an arbitrary number of parameters. I use this to iterate + * over the arguments array and "spread" them out as the arguments to the + * function pointer `fn`. + * + * The use of a function inside of a struct enables us to have two templates. + * This is useful for two reasons: + * 1. Because we can infer some of the template parameters (specifically the + * indices, which would be a pain to write manually each time) but not others + * (the type of the function pointer), we can separate them out to two sets + * where only one is inferred. + * 2. Index sequences require us to use a variadic template. Since we can't + * have two sets of variadic parameters, we have to split this into two + * different template invocations to get both the `...Args` and `...Indices` */ template -struct napiApply { +struct applyFn { template static constexpr auto apply( kind (*fn)(kind...), @@ -156,15 +205,21 @@ struct napiApply { std::index_sequence ) -> kind { if constexpr(std::is_void>::value) { - fn(napiUnwrap(ctx, args[Indices])...); + fn(wasmUnwrap(ctx, args[Indices])...); } else { - return fn(napiUnwrap(ctx, args[Indices])...); + return fn(wasmUnwrap(ctx, args[Indices])...); } } }; +/* callFn is an abstraction over applyFn that performs the entire call + * operation into a N-API function. By taking the function pointer as a + * template parameter, this generates a static call wrapper for the N-API + * function. It includes some of the boilerplate for making applyFn calls + * and also handles the return values from applyFn and places them into + * the results array if necessary. */ template -struct napiCall { +struct callFn { private: using fp = kind (*)(kind...); public: @@ -173,22 +228,30 @@ struct napiCall { const w::Context* ctx, const w::Val args[], w::Val results[] ) { if constexpr(std::is_void>::value) { - napiApply::apply(Fn, ctx, args, + applyFn::apply(Fn, ctx, args, std::index_sequence_for{}); } else { - results[0] = napiWrap( - napiApply::apply(Fn, ctx, args, + results[0] = wasmWrap( + applyFn::apply(Fn, ctx, args, std::index_sequence_for{})); } } }; +/* This is the final "step" in code generation for N-API wrappers, and + * performs the registration of the N-API function as a WASM embedder + * builtin. This is done in three parts: + * 1. Generate the callFn wrapper for the function pointer. + * 2. Create a v8::wasm::Func for that call wrapper + * 3. Register that v8::wasm::Func as a builtin using the V8 WASM API. + * This takes an isolate and name, which tells us where to register + * the function. */ template struct napiBind { template (*const Fn)(kind...)> static void bind(v8::Isolate* isolate, const char *name) { - w::Func fn = exportNative( - (callback) napiCall::template call); + w::Func fn = createFn( + (callback) callFn::template call); w::RegisterEmbedderBuiltin(isolate, "napi_unstable", name, fn); } }; @@ -545,7 +608,7 @@ napi_status napi_extended_error_info_error_code(napi_extended_error_info* eei) { } void RegisterNapiBuiltins(v8::Isolate* isolate) { - w::Func register_fn = exportNative, Native>( + w::Func register_fn = createFn, Native>( (callback) _napi_module_register); w::RegisterEmbedderBuiltin(isolate, "napi_unstable", "_napi_module_register", register_fn); @@ -850,7 +913,7 @@ void RegisterNapiBuiltins(v8::Isolate* isolate) { ::bind(isolate, "set_napi_property_descriptor_name"); w::Func set_method_fn = - exportNative, Native, + createFn, Native, Native, Wasm>( (callback) set_napi_property_descriptor_method); w::RegisterEmbedderBuiltin(isolate, "napi_unstable",