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/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..ccacd381131 100644 --- a/src/node.cc +++ b/src/node.cc @@ -38,6 +38,13 @@ #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 +#include "../wasm_node_api/wasm_node_api.h" +#endif + #if HAVE_OPENSSL #include "node_crypto.h" #endif @@ -421,6 +428,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/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/test_wasm/test_wasm_napi.node.wasm b/test_wasm/test_wasm_napi.node.wasm new file mode 100755 index 00000000000..d383b41cbd8 Binary files /dev/null and b/test_wasm/test_wasm_napi.node.wasm differ 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..5eb4795e243 --- /dev/null +++ b/wasm_node_api/wasm_napi_lib/js_native_api.h @@ -0,0 +1,498 @@ +// Taken from src/js_native_api.h +#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..24f7c8e8ad8 --- /dev/null +++ b/wasm_node_api/wasm_napi_lib/js_native_api_types.h @@ -0,0 +1,105 @@ +// 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_ + +// 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..5c8d0aba9c5 --- /dev/null +++ b/wasm_node_api/wasm_napi_lib/node_api.h @@ -0,0 +1,184 @@ +// Taken from src/node_api.h +#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..f742a513bd2 --- /dev/null +++ b/wasm_node_api/wasm_napi_lib/node_api_types.h @@ -0,0 +1,38 @@ +// Taken from src/node_api_types.h +#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..02f57c822fc --- /dev/null +++ b/wasm_node_api/wasm_node_api.cc @@ -0,0 +1,949 @@ +#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; + +/* 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: + 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; + +/* 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) { + 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; + } +} + +/* 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 createVoidFn(callback cb) -> w::Func { + return w::Func( + w::FuncType({Args...}, {}), + cb + ); +}; + +template +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 createFn(callback cb) -> w::Func { + if constexpr(std::is_void>::value) { + return createVoidFn()...>(cb); + } else { + 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 wasmCast(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(); + } +} + +/* 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 wasmUnwrap(const w::Context* ctx, const w::Val& v) { + if constexpr(is_wasm_v) { + if constexpr(is_function_pointer_v>) { + return (kind) v.i32(); + } else { + 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 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 wasmWrap(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)); + } +} + +/* 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 applyFn { + 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(wasmUnwrap(ctx, args[Indices])...); + } else { + 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 callFn { +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) { + applyFn::apply(Fn, ctx, args, + std::index_sequence_for{}); + } else { + 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 = createFn( + (callback) callFn::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 init) { + v8::Isolate* isolate = context->GetIsolate(); + v8::MaybeLocal initFunc = + wasmContext->table->get((size_t) init); + 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 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) v8impl::JsValueFromV8LocalValue(exports)); + + v8::MaybeLocal result = + initFunc.ToLocalChecked()->Call(context, context->Global(), 2, argv); + if (result.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); + + 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 + // 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; +}*/ + +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( + 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) { + // 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 +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 register_fn = createFn, Native>( + (callback) _napi_module_register); + w::RegisterEmbedderBuiltin(isolate, "napi_unstable", "_napi_module_register", + 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"); + + w::Func set_method_fn = + createFn, 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> + ::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