summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDivy Srivastava <dj.srivastava23@gmail.com>2022-10-05 07:06:44 -0700
committerGitHub <noreply@github.com>2022-10-05 19:36:44 +0530
commit0b016a7fb8639ce49603c8c339539174b191a4b1 (patch)
treec511060d701db60ede0214b7280e89c5749bbe62
parent3a3a8484069c9c6955fcb83ea761f9f74638175a (diff)
feat(npm): implement Node API (#13633)
This PR implements the NAPI for loading native modules into Deno. Co-authored-by: Bartek Iwańczuk <biwanczuk@gmail.com> Co-authored-by: DjDeveloper <43033058+DjDeveloperr@users.noreply.github.com> Co-authored-by: Ryan Dahl <ry@tinyclouds.org>
-rw-r--r--.gitignore3
-rw-r--r--Cargo.lock45
-rw-r--r--Cargo.toml5
-rw-r--r--cli/Cargo.toml1
-rw-r--r--cli/build.rs15
-rw-r--r--cli/exports.def146
-rw-r--r--cli/main.rs1
-rw-r--r--cli/napi/async.rs78
-rw-r--r--cli/napi/env.rs141
-rw-r--r--cli/napi/js_native_api.rs2275
-rw-r--r--cli/napi/mod.rs88
-rw-r--r--cli/napi/threadsafe_functions.rs199
-rw-r--r--cli/napi/util.rs23
-rw-r--r--cli/napi_sym/Cargo.toml18
-rw-r--r--cli/napi_sym/lib.rs46
-rw-r--r--core/runtime.rs27
-rw-r--r--ext/napi/Cargo.toml18
-rw-r--r--ext/napi/function.rs103
-rw-r--r--ext/napi/lib.rs607
-rw-r--r--ext/node/02_require.js2
-rw-r--r--runtime/Cargo.toml2
-rw-r--r--runtime/build.rs10
-rw-r--r--runtime/lib.rs1
-rw-r--r--runtime/permissions.rs8
-rw-r--r--runtime/web_worker.rs1
-rw-r--r--runtime/worker.rs1
-rw-r--r--test_napi/.gitignore4
-rw-r--r--test_napi/Cargo.toml20
-rw-r--r--test_napi/array_test.js19
-rw-r--r--test_napi/async_test.js16
-rw-r--r--test_napi/build.rs7
-rw-r--r--test_napi/callback_test.js38
-rw-r--r--test_napi/coerce_test.js74
-rw-r--r--test_napi/common.js20
-rw-r--r--test_napi/numbers_test.js18
-rw-r--r--test_napi/object_wrap_test.js17
-rw-r--r--test_napi/promise_test.js34
-rw-r--r--test_napi/properties_test.js15
-rw-r--r--test_napi/src/array.rs73
-rw-r--r--test_napi/src/async.rs112
-rw-r--r--test_napi/src/callback.rs113
-rw-r--r--test_napi/src/coerce.rs71
-rw-r--r--test_napi/src/lib.rs78
-rw-r--r--test_napi/src/numbers.rs55
-rw-r--r--test_napi/src/object_wrap.rs154
-rw-r--r--test_napi/src/promise.rs76
-rw-r--r--test_napi/src/properties.rs89
-rw-r--r--test_napi/src/strings.rs45
-rw-r--r--test_napi/src/typedarray.rs53
-rw-r--r--test_napi/strings_test.js15
-rw-r--r--test_napi/tests/napi_tests.rs45
-rw-r--r--test_napi/typedarray_test.js12
-rw-r--r--test_util/src/lib.rs4
-rwxr-xr-xtools/napi/generate_link_win.js12
-rw-r--r--tools/napi/symbol_exports.json148
55 files changed, 5299 insertions, 2 deletions
diff --git a/.gitignore b/.gitignore
index 7c4314508..19e60deba 100644
--- a/.gitignore
+++ b/.gitignore
@@ -11,6 +11,9 @@ gclient_config.py_entries
/target/
/std/hash/_wasm/target
/tools/wpt/manifest.json
+/test_napi/node_modules
+/test_napi/build
+/test_napi/third_party_tests/node_modules
# Files that help ensure VSCode can work but we don't want checked into the
# repo
diff --git a/Cargo.lock b/Cargo.lock
index c125f02a8..071665eb5 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -854,6 +854,7 @@ dependencies = [
"log 0.4.17",
"mitata",
"monch",
+ "napi_sym",
"nix",
"notify",
"once_cell",
@@ -1162,6 +1163,14 @@ dependencies = [
]
[[package]]
+name = "deno_napi"
+version = "0.1.0"
+dependencies = [
+ "deno_core",
+ "libloading",
+]
+
+[[package]]
name = "deno_net"
version = "0.63.0"
dependencies = [
@@ -1214,6 +1223,7 @@ dependencies = [
"deno_ffi",
"deno_flash",
"deno_http",
+ "deno_napi",
"deno_net",
"deno_node",
"deno_tls",
@@ -2898,6 +2908,32 @@ dependencies = [
]
[[package]]
+name = "napi-build"
+version = "1.2.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ebd4419172727423cf30351406c54f6cc1b354a2cfb4f1dba3e6cd07f6d5522b"
+
+[[package]]
+name = "napi-sys"
+version = "2.2.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "529671ebfae679f2ce9630b62dd53c72c56b3eb8b2c852e7e2fa91704ff93d67"
+dependencies = [
+ "libloading",
+]
+
+[[package]]
+name = "napi_sym"
+version = "0.1.0"
+dependencies = [
+ "proc-macro2 1.0.43",
+ "quote 1.0.21",
+ "serde",
+ "serde_json",
+ "syn 1.0.99",
+]
+
+[[package]]
name = "netif"
version = "0.1.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -4784,6 +4820,15 @@ dependencies = [
]
[[package]]
+name = "test_napi"
+version = "0.1.0"
+dependencies = [
+ "napi-build",
+ "napi-sys",
+ "test_util",
+]
+
+[[package]]
name = "test_util"
version = "0.1.0"
dependencies = [
diff --git a/Cargo.toml b/Cargo.toml
index f6aaf6cdf..e1cc40cd6 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -5,11 +5,13 @@ resolver = "2"
members = [
"bench_util",
"cli",
+ "cli/napi_sym",
"core",
"ops",
"runtime",
"serde_v8",
"test_ffi",
+ "test_napi",
"test_util",
"ext/broadcast_channel",
"ext/cache",
@@ -27,6 +29,7 @@ members = [
"ext/webidl",
"ext/websocket",
"ext/webstorage",
+ "ext/napi",
]
exclude = ["test_util/std/hash/_wasm"]
@@ -154,6 +157,8 @@ opt-level = 3
opt-level = 3
[profile.release.package.deno_websocket]
opt-level = 3
+[profile.release.package.deno_napi]
+opt-level = 3
[profile.release.package.num-bigint-dig]
opt-level = 3
[profile.release.package.v8]
diff --git a/cli/Cargo.toml b/cli/Cargo.toml
index 12b605aa9..9d668f8a5 100644
--- a/cli/Cargo.toml
+++ b/cli/Cargo.toml
@@ -56,6 +56,7 @@ deno_graph = "0.34.0"
deno_lint = { version = "0.33.0", features = ["docs"] }
deno_runtime = { version = "0.79.0", path = "../runtime" }
deno_task_shell = "0.5.2"
+napi_sym = { path = "./napi_sym", version = "0.1.0" }
atty = "=0.2.14"
base64 = "=0.13.0"
diff --git a/cli/build.rs b/cli/build.rs
index df4cd5917..aaa29a6d0 100644
--- a/cli/build.rs
+++ b/cli/build.rs
@@ -331,6 +331,21 @@ fn main() {
if target != host {
panic!("Cross compiling with snapshot is not supported.");
}
+
+ #[cfg(target_os = "windows")]
+ println!(
+ "cargo:rustc-link-arg-bin=deno=/DEF:{}",
+ std::path::Path::new("exports.def")
+ .canonicalize()
+ .expect(
+ "Missing exports.def! Generate using tools/napi/generate_link_win.js"
+ )
+ .display(),
+ );
+
+ #[cfg(not(target_os = "windows"))]
+ println!("cargo:rustc-link-arg-bin=deno=-rdynamic");
+
// To debug snapshot issues uncomment:
// op_fetch_asset::trace_serializer();
diff --git a/cli/exports.def b/cli/exports.def
new file mode 100644
index 000000000..0b6cb946a
--- /dev/null
+++ b/cli/exports.def
@@ -0,0 +1,146 @@
+LIBRARY
+EXPORTS
+ node_api_create_syntax_error
+ napi_make_callback
+ napi_has_named_property
+ napi_async_destroy
+ napi_coerce_to_object
+ napi_get_arraybuffer_info
+ napi_detach_arraybuffer
+ napi_get_undefined
+ napi_reference_unref
+ napi_fatal_error
+ napi_open_callback_scope
+ napi_close_callback_scope
+ napi_get_value_uint32
+ napi_create_function
+ napi_create_arraybuffer
+ napi_get_value_int64
+ napi_get_all_property_names
+ napi_resolve_deferred
+ napi_is_detached_arraybuffer
+ napi_create_string_utf8
+ napi_create_threadsafe_function
+ node_api_throw_syntax_error
+ napi_create_bigint_int64
+ napi_wrap
+ napi_set_property
+ napi_get_value_bigint_int64
+ napi_open_handle_scope
+ napi_create_error
+ napi_create_buffer
+ napi_cancel_async_work
+ napi_is_exception_pending
+ napi_acquire_threadsafe_function
+ napi_create_external
+ napi_get_threadsafe_function_context
+ napi_get_null
+ napi_create_string_utf16
+ napi_get_value_bigint_uint64
+ napi_module_register
+ napi_is_typedarray
+ napi_create_external_buffer
+ napi_get_new_target
+ napi_get_instance_data
+ napi_close_handle_scope
+ napi_get_value_string_utf16
+ napi_get_property_names
+ napi_is_arraybuffer
+ napi_get_cb_info
+ napi_define_properties
+ napi_add_env_cleanup_hook
+ node_api_get_module_file_name
+ napi_get_node_version
+ napi_create_int64
+ napi_create_double
+ napi_get_and_clear_last_exception
+ napi_create_reference
+ napi_get_typedarray_info
+ napi_call_threadsafe_function
+ napi_get_last_error_info
+ napi_create_array_with_length
+ napi_coerce_to_number
+ napi_get_global
+ napi_is_error
+ napi_set_instance_data
+ napi_create_typedarray
+ napi_throw_type_error
+ napi_has_property
+ napi_get_value_external
+ napi_create_range_error
+ napi_typeof
+ napi_ref_threadsafe_function
+ napi_create_bigint_uint64
+ napi_get_prototype
+ napi_adjust_external_memory
+ napi_release_threadsafe_function
+ napi_delete_async_work
+ napi_create_string_latin1
+ napi_is_array
+ napi_unref_threadsafe_function
+ napi_throw_error
+ napi_has_own_property
+ napi_get_reference_value
+ napi_remove_env_cleanup_hook
+ napi_get_value_string_utf8
+ napi_is_promise
+ napi_get_boolean
+ napi_run_script
+ napi_get_element
+ napi_get_named_property
+ napi_get_buffer_info
+ napi_get_value_bool
+ napi_reference_ref
+ napi_create_object
+ napi_create_promise
+ napi_create_int32
+ napi_escape_handle
+ napi_open_escapable_handle_scope
+ napi_throw
+ napi_get_value_double
+ napi_set_named_property
+ napi_call_function
+ napi_create_date
+ napi_object_freeze
+ napi_get_uv_event_loop
+ napi_get_value_string_latin1
+ napi_reject_deferred
+ napi_add_finalizer
+ napi_create_array
+ napi_delete_reference
+ napi_get_date_value
+ napi_create_dataview
+ napi_get_version
+ napi_define_class
+ napi_is_date
+ napi_remove_wrap
+ napi_delete_property
+ napi_instanceof
+ napi_create_buffer_copy
+ napi_delete_element
+ napi_object_seal
+ napi_queue_async_work
+ napi_get_value_bigint_words
+ napi_is_buffer
+ napi_get_array_length
+ napi_get_property
+ napi_new_instance
+ napi_set_element
+ napi_create_bigint_words
+ napi_strict_equals
+ napi_is_dataview
+ napi_close_escapable_handle_scope
+ napi_get_dataview_info
+ napi_get_value_int32
+ napi_unwrap
+ napi_throw_range_error
+ napi_coerce_to_bool
+ napi_create_uint32
+ napi_has_element
+ napi_create_external_arraybuffer
+ napi_create_symbol
+ napi_coerce_to_string
+ napi_create_type_error
+ napi_fatal_exception
+ napi_create_async_work
+ napi_async_init
diff --git a/cli/main.rs b/cli/main.rs
index 358f12b17..f0cac4f38 100644
--- a/cli/main.rs
+++ b/cli/main.rs
@@ -22,6 +22,7 @@ mod lockfile;
mod logger;
mod lsp;
mod module_loader;
+mod napi;
mod node;
mod npm;
mod ops;
diff --git a/cli/napi/async.rs b/cli/napi/async.rs
new file mode 100644
index 000000000..b84d22272
--- /dev/null
+++ b/cli/napi/async.rs
@@ -0,0 +1,78 @@
+// Copyright 2018-2022 the Deno authors. All rights reserved. MIT license.
+
+use deno_runtime::deno_napi::*;
+
+#[repr(C)]
+pub struct AsyncWork {
+ pub data: *mut c_void,
+ pub execute: napi_async_execute_callback,
+ pub complete: napi_async_complete_callback,
+}
+
+#[napi_sym::napi_sym]
+fn napi_create_async_work(
+ _env: *mut Env,
+ _async_resource: napi_value,
+ _async_resource_name: napi_value,
+ execute: napi_async_execute_callback,
+ complete: napi_async_complete_callback,
+ data: *mut c_void,
+ result: *mut napi_async_work,
+) -> Result {
+ let mut work = AsyncWork {
+ data,
+ execute,
+ complete,
+ };
+ *result = transmute::<Box<AsyncWork>, _>(Box::new(work));
+ Ok(())
+}
+
+#[napi_sym::napi_sym]
+fn napi_cancel_async_work(
+ _env: &mut Env,
+ _async_work: napi_async_work,
+) -> Result {
+ Ok(())
+}
+
+/// Frees a previously allocated work object.
+#[napi_sym::napi_sym]
+fn napi_delete_async_work(_env: &mut Env, work: napi_async_work) -> Result {
+ let work = Box::from_raw(work);
+ drop(work);
+
+ Ok(())
+}
+
+#[napi_sym::napi_sym]
+fn napi_queue_async_work(env_ptr: *mut Env, work: napi_async_work) -> Result {
+ let work: &AsyncWork = &*(work as *const AsyncWork);
+ let env: &mut Env = env_ptr.as_mut().ok_or(Error::InvalidArg)?;
+
+ let fut = Box::new(move || {
+ (work.execute)(env_ptr as napi_env, work.data);
+ // Note: Must be called from the loop thread.
+ (work.complete)(env_ptr as napi_env, napi_ok, work.data);
+ });
+ env.add_async_work(fut);
+
+ Ok(())
+}
+
+// TODO: Custom async operations.
+
+#[napi_sym::napi_sym]
+fn napi_async_init(
+ _env: *mut Env,
+ _async_resource: napi_value,
+ _async_resource_name: napi_value,
+ _result: *mut *mut (),
+) -> Result {
+ todo!()
+}
+
+#[napi_sym::napi_sym]
+fn napi_async_destroy(_env: *mut Env, _async_context: *mut ()) -> Result {
+ todo!()
+}
diff --git a/cli/napi/env.rs b/cli/napi/env.rs
new file mode 100644
index 000000000..24dc8af81
--- /dev/null
+++ b/cli/napi/env.rs
@@ -0,0 +1,141 @@
+// Copyright 2018-2022 the Deno authors. All rights reserved. MIT license.
+
+use deno_runtime::deno_napi::*;
+
+/// # Safety
+///
+/// It's an N-API symbol
+#[no_mangle]
+pub unsafe extern "C" fn napi_fatal_error(
+ location: *const c_char,
+ location_len: isize,
+ message: *const c_char,
+ message_len: isize,
+) -> ! {
+ let location = if location.is_null() {
+ None
+ } else {
+ Some(if location_len < 0 {
+ std::ffi::CStr::from_ptr(location).to_str().unwrap()
+ } else {
+ let slice = std::slice::from_raw_parts(
+ location as *const u8,
+ location_len as usize,
+ );
+ std::str::from_utf8(slice).unwrap()
+ })
+ };
+ let message = if message_len < 0 {
+ std::ffi::CStr::from_ptr(message).to_str().unwrap()
+ } else {
+ let slice =
+ std::slice::from_raw_parts(message as *const u8, message_len as usize);
+ std::str::from_utf8(slice).unwrap()
+ };
+ panic!(
+ "Fatal exception triggered by napi_fatal_error!\nLocation: {:?}\n{}",
+ location, message
+ );
+}
+
+// napi-3
+
+#[napi_sym::napi_sym]
+fn napi_fatal_exception(env: *mut Env, value: napi_value) -> Result {
+ let env: &mut Env = env.as_mut().ok_or(Error::InvalidArg)?;
+ let value = transmute::<napi_value, v8::Local<v8::Value>>(value);
+ let error = value.to_rust_string_lossy(&mut env.scope());
+ panic!(
+ "Fatal exception triggered by napi_fatal_exception!\n{}",
+ error
+ );
+}
+
+// TODO: properly implement
+#[napi_sym::napi_sym]
+fn napi_add_env_cleanup_hook(
+ _env: *mut Env,
+ _hook: extern "C" fn(*const c_void),
+ _data: *const c_void,
+) -> Result {
+ eprintln!("napi_add_env_cleanup_hook is currently not supported");
+ Ok(())
+}
+
+#[napi_sym::napi_sym]
+fn napi_remove_env_cleanup_hook(
+ _env: *mut Env,
+ _hook: extern "C" fn(*const c_void),
+ _data: *const c_void,
+) -> Result {
+ eprintln!("napi_remove_env_cleanup_hook is currently not supported");
+ Ok(())
+}
+
+#[napi_sym::napi_sym]
+fn napi_open_callback_scope(
+ _env: *mut Env,
+ _resource_object: napi_value,
+ _context: napi_value,
+ _result: *mut napi_callback_scope,
+) -> Result {
+ // we open scope automatically when it's needed
+ Ok(())
+}
+
+#[napi_sym::napi_sym]
+fn napi_close_callback_scope(
+ _env: *mut Env,
+ _scope: napi_callback_scope,
+) -> Result {
+ // we close scope automatically when it's needed
+ Ok(())
+}
+
+#[napi_sym::napi_sym]
+fn node_api_get_module_file_name(
+ env: *mut Env,
+ result: *mut *const c_char,
+) -> Result {
+ let env: &mut Env = env.as_mut().ok_or(Error::InvalidArg)?;
+
+ let shared = env.shared();
+ *result = shared.filename;
+ Ok(())
+}
+
+#[napi_sym::napi_sym]
+fn napi_module_register(module: *const NapiModule) -> Result {
+ MODULE.with(|cell| {
+ let mut slot = cell.borrow_mut();
+ slot.replace(module);
+ });
+ Ok(())
+}
+
+#[napi_sym::napi_sym]
+fn napi_get_uv_event_loop(_env: *mut Env, uv_loop: *mut *mut ()) -> Result {
+ // Don't error out because addons may pass this to
+ // our libuv _polyfills_.
+ *uv_loop = std::ptr::null_mut();
+ Ok(())
+}
+
+const NODE_VERSION: napi_node_version = napi_node_version {
+ major: 17,
+ minor: 4,
+ patch: 0,
+ release: "Deno\0".as_ptr() as *const i8,
+};
+
+#[napi_sym::napi_sym]
+fn napi_get_node_version(
+ env: *mut Env,
+ result: *mut *const napi_node_version,
+) -> Result {
+ let _: &mut Env = env.as_mut().ok_or(Error::InvalidArg)?;
+ crate::check_arg!(result);
+
+ *result = &NODE_VERSION as *const napi_node_version;
+ Ok(())
+}
diff --git a/cli/napi/js_native_api.rs b/cli/napi/js_native_api.rs
new file mode 100644
index 000000000..6270dbe5c
--- /dev/null
+++ b/cli/napi/js_native_api.rs
@@ -0,0 +1,2275 @@
+// Copyright 2018-2022 the Deno authors. All rights reserved. MIT license.
+
+#![allow(non_upper_case_globals)]
+
+use deno_runtime::deno_napi::*;
+use v8::BackingStore;
+use v8::UniqueRef;
+
+use super::util::get_array_buffer_ptr;
+use deno_runtime::deno_napi::function::create_function;
+use deno_runtime::deno_napi::function::create_function_template;
+use deno_runtime::deno_napi::function::CallbackInfo;
+use std::ptr::NonNull;
+
+// Macro to check napi arguments.
+// If nullptr, return Err(Error::InvalidArg).
+#[macro_export]
+macro_rules! check_arg {
+ ($ptr: expr) => {
+ if $ptr.is_null() {
+ return Err(Error::InvalidArg);
+ }
+ };
+}
+
+/// Returns napi_value that represents a new JavaScript Array.
+#[napi_sym::napi_sym]
+fn napi_create_array(env: *mut Env, result: *mut napi_value) -> Result {
+ let env: &mut Env = env.as_mut().ok_or(Error::InvalidArg)?;
+ check_arg!(result);
+
+ let value = v8::Array::new(&mut env.scope(), 0);
+ *result = transmute::<v8::Local<v8::Value>, napi_value>(value.into());
+ Ok(())
+}
+
+#[napi_sym::napi_sym]
+fn napi_create_array_with_length(
+ env: *mut Env,
+ len: i32,
+ result: *mut napi_value,
+) -> Result {
+ let env: &mut Env = env.as_mut().ok_or(Error::InvalidArg)?;
+ check_arg!(result);
+
+ let value = v8::Array::new(&mut env.scope(), len);
+ *result = transmute::<v8::Local<v8::Value>, napi_value>(value.into());
+ Ok(())
+}
+
+#[napi_sym::napi_sym]
+fn napi_create_arraybuffer(
+ env: *mut Env,
+ len: usize,
+ data: *mut *mut u8,
+ result: *mut napi_value,
+) -> Result {
+ let env: &mut Env = env.as_mut().ok_or(Error::InvalidArg)?;
+ check_arg!(result);
+
+ let value = v8::ArrayBuffer::new(&mut env.scope(), len);
+ if !data.is_null() {
+ *data = get_array_buffer_ptr(value);
+ }
+
+ *result = transmute::<v8::Local<v8::Value>, napi_value>(value.into());
+ Ok(())
+}
+
+#[napi_sym::napi_sym]
+fn napi_create_bigint_int64(
+ env: *mut Env,
+ value: i64,
+ result: *mut napi_value,
+) -> Result {
+ let env: &mut Env = env.as_mut().ok_or(Error::InvalidArg)?;
+ check_arg!(result);
+
+ let value = v8::BigInt::new_from_i64(&mut env.scope(), value);
+ *result = transmute::<v8::Local<v8::Value>, napi_value>(value.into());
+ Ok(())
+}
+
+#[napi_sym::napi_sym]
+fn napi_create_bigint_uint64(
+ env: *mut Env,
+ value: u64,
+ result: *mut napi_value,
+) -> Result {
+ let env: &mut Env = env.as_mut().ok_or(Error::InvalidArg)?;
+ let value: v8::Local<v8::Value> =
+ v8::BigInt::new_from_u64(&mut env.scope(), value).into();
+ *result = transmute::<v8::Local<v8::Value>, napi_value>(value);
+ Ok(())
+}
+
+#[napi_sym::napi_sym]
+fn napi_create_bigint_words(
+ env: *mut Env,
+ sign_bit: bool,
+ words: *const u64,
+ word_count: usize,
+ result: *mut napi_value,
+) -> Result {
+ let env: &mut Env = env.as_mut().ok_or(Error::InvalidArg)?;
+ let value: v8::Local<v8::Value> = v8::BigInt::new_from_words(
+ &mut env.scope(),
+ sign_bit,
+ std::slice::from_raw_parts(words, word_count),
+ )
+ .unwrap()
+ .into();
+ *result = transmute::<v8::Local<v8::Value>, napi_value>(value);
+ Ok(())
+}
+
+#[napi_sym::napi_sym]
+fn napi_create_buffer(
+ env: *mut Env,
+ len: usize,
+ data: *mut *mut u8,
+ result: *mut napi_value,
+) -> Result {
+ let env: &mut Env = env.as_mut().ok_or(Error::InvalidArg)?;
+ let value = v8::ArrayBuffer::new(&mut env.scope(), len);
+ if !data.is_null() {
+ *data = get_array_buffer_ptr(value);
+ }
+ let value = v8::Uint8Array::new(&mut env.scope(), value, 0, len).unwrap();
+ let value: v8::Local<v8::Value> = value.into();
+ *result = transmute::<v8::Local<v8::Value>, napi_value>(value);
+ Ok(())
+}
+
+#[napi_sym::napi_sym]
+fn napi_create_buffer_copy(
+ env: *mut Env,
+ len: usize,
+ data: *mut u8,
+ result_data: *mut *mut u8,
+ result: *mut napi_value,
+) -> Result {
+ let env: &mut Env = env.as_mut().ok_or(Error::InvalidArg)?;
+ let value = v8::ArrayBuffer::new(&mut env.scope(), len);
+ let ptr = get_array_buffer_ptr(value);
+ std::ptr::copy(data, ptr, len);
+ if !result_data.is_null() {
+ *result_data = ptr;
+ }
+ let value = v8::Uint8Array::new(&mut env.scope(), value, 0, len).unwrap();
+ let value: v8::Local<v8::Value> = value.into();
+ *result = transmute::<v8::Local<v8::Value>, napi_value>(value);
+ Ok(())
+}
+
+#[napi_sym::napi_sym]
+fn napi_coerce_to_bool(
+ env: *mut Env,
+ value: napi_value,
+ result: *mut napi_value,
+) -> Result {
+ let env: &mut Env = env.as_mut().ok_or(Error::InvalidArg)?;
+ let value = transmute::<napi_value, v8::Local<v8::Value>>(value);
+ let coerced = value.to_boolean(&mut env.scope());
+ let value: v8::Local<v8::Value> = coerced.into();
+ *result = transmute::<v8::Local<v8::Value>, napi_value>(value);
+ Ok(())
+}
+
+#[napi_sym::napi_sym]
+fn napi_coerce_to_number(
+ env: *mut Env,
+ value: napi_value,
+ result: *mut napi_value,
+) -> Result {
+ let env: &mut Env = env.as_mut().ok_or(Error::InvalidArg)?;
+ let value = transmute::<napi_value, v8::Local<v8::Value>>(value);
+ let coerced = value
+ .to_number(&mut env.scope())
+ .ok_or(Error::NumberExpected)?;
+ let value: v8::Local<v8::Value> = coerced.into();
+ *result = transmute::<v8::Local<v8::Value>, napi_value>(value);
+ Ok(())
+}
+
+#[napi_sym::napi_sym]
+fn napi_coerce_to_object(
+ env: *mut Env,
+ value: napi_value,
+ result: *mut napi_value,
+) -> Result {
+ let env: &mut Env = env.as_mut().ok_or(Error::InvalidArg)?;
+ let value = transmute::<napi_value, v8::Local<v8::Value>>(value);
+ let coerced = value.to_object(&mut env.scope()).unwrap();
+ let value: v8::Local<v8::Value> = coerced.into();
+ *result = transmute::<v8::Local<v8::Value>, napi_value>(value);
+ Ok(())
+}
+
+#[napi_sym::napi_sym]
+fn napi_coerce_to_string(
+ env: *mut Env,
+ value: napi_value,
+ result: *mut napi_value,
+) -> Result {
+ let env: &mut Env = env.as_mut().ok_or(Error::InvalidArg)?;
+ let value = transmute::<napi_value, v8::Local<v8::Value>>(value);
+ let coerced = value.to_string(&mut env.scope()).unwrap();
+ let value: v8::Local<v8::Value> = coerced.into();
+ *result = transmute::<v8::Local<v8::Value>, napi_value>(value);
+ Ok(())
+}
+
+#[napi_sym::napi_sym]
+fn napi_create_dataview(
+ env: *mut Env,
+ len: usize,
+ data: *mut *mut u8,
+ byte_offset: usize,
+ result: *mut napi_value,
+) -> Result {
+ let env: &mut Env = env.as_mut().ok_or(Error::InvalidArg)?;
+ check_arg!(data);
+ check_arg!(result);
+ let value = v8::ArrayBuffer::new(&mut env.scope(), len);
+ if !data.is_null() {
+ *data = get_array_buffer_ptr(value);
+ }
+ let context = &mut env.scope().get_current_context();
+ let global = context.global(&mut env.scope());
+ let data_view_name = v8::String::new(&mut env.scope(), "DataView").unwrap();
+ let data_view = global.get(&mut env.scope(), data_view_name.into()).unwrap();
+ let data_view = v8::Local::<v8::Function>::try_from(data_view).unwrap();
+ let byte_offset = v8::Number::new(&mut env.scope(), byte_offset as f64);
+ let byte_length = v8::Number::new(&mut env.scope(), len as f64);
+ let value = data_view
+ .new_instance(
+ &mut env.scope(),
+ &[value.into(), byte_offset.into(), byte_length.into()],
+ )
+ .unwrap();
+ let value: v8::Local<v8::Value> = value.into();
+ *result = transmute::<v8::Local<v8::Value>, napi_value>(value);
+ Ok(())
+}
+
+#[napi_sym::napi_sym]
+fn napi_create_date(
+ env: *mut Env,
+ time: f64,
+ result: *mut napi_value,
+) -> Result {
+ let env: &mut Env = env.as_mut().ok_or(Error::InvalidArg)?;
+ let value: v8::Local<v8::Value> =
+ v8::Date::new(&mut env.scope(), time).unwrap().into();
+ *result = transmute::<v8::Local<v8::Value>, napi_value>(value);
+ Ok(())
+}
+
+#[napi_sym::napi_sym]
+fn napi_create_double(
+ env: *mut Env,
+ value: f64,
+ result: *mut napi_value,
+) -> Result {
+ let env: &mut Env = env.as_mut().ok_or(Error::InvalidArg)?;
+ let value: v8::Local<v8::Value> =
+ v8::Number::new(&mut env.scope(), value).into();
+ *result = transmute::<v8::Local<v8::Value>, napi_value>(value);
+ Ok(())
+}
+
+#[napi_sym::napi_sym]
+fn napi_create_error(
+ env: *mut Env,
+ code: napi_value,
+ msg: napi_value,
+ result: *mut napi_value,
+) -> Result {
+ let env: &mut Env = env.as_mut().ok_or(Error::InvalidArg)?;
+
+ let _code = transmute::<napi_value, v8::Local<v8::Value>>(code);
+ let msg = transmute::<napi_value, v8::Local<v8::Value>>(msg);
+
+ let msg = msg.to_string(&mut env.scope()).unwrap();
+
+ let error = v8::Exception::error(&mut env.scope(), msg);
+ *result = transmute::<v8::Local<v8::Value>, napi_value>(error);
+
+ Ok(())
+}
+
+#[napi_sym::napi_sym]
+fn napi_create_external(
+ env: *mut Env,
+ value: *mut c_void,
+ _finalize_cb: napi_finalize,
+ _finalize_hint: *mut c_void,
+ result: *mut napi_value,
+) -> Result {
+ let env: &mut Env = env.as_mut().ok_or(Error::InvalidArg)?;
+ let value: v8::Local<v8::Value> =
+ v8::External::new(&mut env.scope(), value).into();
+ // TODO: finalization
+ *result = transmute::<v8::Local<v8::Value>, napi_value>(value);
+ Ok(())
+}
+
+pub type BackingStoreDeleterCallback = unsafe extern "C" fn(
+ data: *mut c_void,
+ byte_length: usize,
+ deleter_data: *mut c_void,
+);
+extern "C" {
+ fn v8__ArrayBuffer__NewBackingStore__with_data(
+ data: *mut c_void,
+ byte_length: usize,
+ deleter: BackingStoreDeleterCallback,
+ deleter_data: *mut c_void,
+ ) -> *mut BackingStore;
+}
+
+pub extern "C" fn backing_store_deleter_callback(
+ data: *mut c_void,
+ byte_length: usize,
+ _deleter_data: *mut c_void,
+) {
+ let slice_ptr = ptr::slice_from_raw_parts_mut(data as *mut u8, byte_length);
+ let b = unsafe { Box::from_raw(slice_ptr) };
+ drop(b);
+}
+
+#[napi_sym::napi_sym]
+fn napi_create_external_arraybuffer(
+ env: *mut Env,
+ data: *mut c_void,
+ byte_length: usize,
+ _finalize_cb: napi_finalize,
+ finalize_hint: *mut c_void,
+ result: *mut napi_value,
+) -> Result {
+ let env: &mut Env = env.as_mut().ok_or(Error::InvalidArg)?;
+ let _slice = std::slice::from_raw_parts(data as *mut u8, byte_length);
+ // TODO: finalization
+ let store: UniqueRef<BackingStore> =
+ transmute(v8__ArrayBuffer__NewBackingStore__with_data(
+ data,
+ byte_length,
+ backing_store_deleter_callback,
+ finalize_hint,
+ ));
+
+ let ab =
+ v8::ArrayBuffer::with_backing_store(&mut env.scope(), &store.make_shared());
+ let value: v8::Local<v8::Value> = ab.into();
+ *result = transmute::<v8::Local<v8::Value>, napi_value>(value);
+ Ok(())
+}
+
+#[napi_sym::napi_sym]
+fn napi_create_external_buffer(
+ env: *mut Env,
+ byte_length: isize,
+ data: *mut c_void,
+ _finalize_cb: napi_finalize,
+ _finalize_hint: *mut c_void,
+ result: *mut napi_value,
+) -> Result {
+ let env: &mut Env = env.as_mut().ok_or(Error::InvalidArg)?;
+ let slice = if byte_length == -1 {
+ std::ffi::CStr::from_ptr(data as *const _).to_bytes()
+ } else {
+ std::slice::from_raw_parts(data as *mut u8, byte_length as usize)
+ };
+ // TODO: make this not copy the slice
+ // TODO: finalization
+ let store = v8::ArrayBuffer::new_backing_store_from_boxed_slice(
+ slice.to_vec().into_boxed_slice(),
+ );
+ let ab =
+ v8::ArrayBuffer::with_backing_store(&mut env.scope(), &store.make_shared());
+ let value =
+ v8::Uint8Array::new(&mut env.scope(), ab, 0, slice.len()).unwrap();
+ let value: v8::Local<v8::Value> = value.into();
+ *result = transmute::<v8::Local<v8::Value>, napi_value>(value);
+ Ok(())
+}
+
+#[napi_sym::napi_sym]
+fn napi_create_function(
+ env_ptr: *mut Env,
+ name: *const u8,
+ length: isize,
+ cb: napi_callback,
+ cb_info: napi_callback_info,
+ result: *mut napi_value,
+) -> Result {
+ let _: &mut Env = env_ptr.as_mut().ok_or(Error::InvalidArg)?;
+ let name = match name.is_null() {
+ true => None,
+ false => Some(name),
+ };
+ let name = name.map(|name| {
+ if length == -1 {
+ std::ffi::CStr::from_ptr(name as *const _).to_str().unwrap()
+ } else {
+ let name = std::slice::from_raw_parts(name, length as usize);
+ // If ends with NULL
+ if name[name.len() - 1] == 0 {
+ std::str::from_utf8(&name[0..name.len() - 1]).unwrap()
+ } else {
+ std::str::from_utf8(name).unwrap()
+ }
+ }
+ });
+
+ let function = create_function(env_ptr, name, cb, cb_info);
+ let value: v8::Local<v8::Value> = function.into();
+ *result = transmute::<v8::Local<v8::Value>, napi_value>(value);
+ Ok(())
+}
+
+#[napi_sym::napi_sym]
+fn napi_create_int32(
+ env: *mut Env,
+ value: i32,
+ result: *mut napi_value,
+) -> Result {
+ let env: &mut Env = env.as_mut().ok_or(Error::InvalidArg)?;
+ let value: v8::Local<v8::Value> =
+ v8::Number::new(&mut env.scope(), value as f64).into();
+ *result = transmute::<v8::Local<v8::Value>, napi_value>(value);
+ Ok(())
+}
+
+#[napi_sym::napi_sym]
+fn napi_create_int64(
+ env: *mut Env,
+ value: i64,
+ result: *mut napi_value,
+) -> Result {
+ let env: &mut Env = env.as_mut().ok_or(Error::InvalidArg)?;
+ let value: v8::Local<v8::Value> =
+ v8::Number::new(&mut env.scope(), value as f64).into();
+ *result = transmute::<v8::Local<v8::Value>, napi_value>(value);
+ Ok(())
+}
+
+#[napi_sym::napi_sym]
+fn napi_create_object(env: *mut Env, result: *mut napi_value) -> Result {
+ let env: &mut Env = env.as_mut().ok_or(Error::InvalidArg)?;
+ let object = v8::Object::new(&mut env.scope());
+ *result = transmute::<v8::Local<v8::Value>, napi_value>(object.into());
+ Ok(())
+}
+
+#[napi_sym::napi_sym]
+fn napi_create_promise(
+ env: *mut Env,
+ deferred: *mut napi_deferred,
+ promise_out: *mut napi_value,
+) -> Result {
+ let env: &mut Env = env.as_mut().ok_or(Error::InvalidArg)?;
+ let resolver = v8::PromiseResolver::new(&mut env.scope()).unwrap();
+ let mut global = v8::Global::new(&mut env.scope(), resolver);
+ let mut global_ptr = global.into_raw();
+ let promise = resolver.get_promise(&mut env.scope());
+ *deferred = global_ptr.as_mut() as *mut _ as napi_deferred;
+ *promise_out = transmute::<v8::Local<v8::Value>, napi_value>(promise.into());
+
+ Ok(())
+}
+
+#[napi_sym::napi_sym]
+fn napi_create_range_error(
+ env: *mut Env,
+ _code: napi_value,
+ msg: napi_value,
+ result: *mut napi_value,
+) -> Result {
+ let env: &mut Env = env.as_mut().ok_or(Error::InvalidArg)?;
+
+ // let code = transmute::<napi_value, v8::Local<v8::Value>>(code);
+ let msg = transmute::<napi_value, v8::Local<v8::Value>>(msg);
+
+ let msg = msg.to_string(&mut env.scope()).unwrap();
+
+ let error = v8::Exception::range_error(&mut env.scope(), msg);
+ *result = transmute::<v8::Local<v8::Value>, napi_value>(error);
+
+ Ok(())
+}
+
+#[napi_sym::napi_sym]
+fn napi_create_reference(
+ env: *mut Env,
+ value: napi_value,
+ _initial_refcount: u32,
+ result: *mut napi_ref,
+) -> Result {
+ let env: &mut Env = env.as_mut().ok_or(Error::InvalidArg)?;
+
+ let value = transmute::<napi_value, v8::Local<v8::Value>>(value);
+ let global = v8::Global::new(&mut env.scope(), value);
+ let mut global_ptr = global.into_raw();
+ *result = transmute::<NonNull<v8::Value>, napi_ref>(global_ptr);
+ Ok(())
+}
+
+#[napi_sym::napi_sym]
+fn napi_create_string_latin1(
+ env: *mut Env,
+ string: *const u8,
+ length: isize,
+ result: *mut napi_value,
+) -> Result {
+ let env: &mut Env = env.as_mut().ok_or(Error::InvalidArg)?;
+
+ let string = if length == -1 {
+ std::ffi::CStr::from_ptr(string as *const _)
+ .to_str()
+ .unwrap()
+ .as_bytes()
+ } else {
+ std::slice::from_raw_parts(string, length as usize)
+ };
+ match v8::String::new_from_one_byte(
+ &mut env.scope(),
+ string,
+ v8::NewStringType::Normal,
+ ) {
+ Some(v8str) => {
+ let value: v8::Local<v8::Value> = v8str.into();
+ *result = transmute::<v8::Local<v8::Value>, napi_value>(value);
+ }
+ None => return Err(Error::GenericFailure),
+ }
+
+ Ok(())
+}
+
+#[napi_sym::napi_sym]
+fn napi_create_string_utf16(
+ env: *mut Env,
+ string: *const u16,
+ length: usize,
+ result: *mut napi_value,
+) -> Result {
+ let env: &mut Env = env.as_mut().ok_or(Error::InvalidArg)?;
+ let string = std::slice::from_raw_parts(string, length);
+ let v8str = v8::String::new_from_two_byte(
+ &mut env.scope(),
+ string,
+ v8::NewStringType::Normal,
+ )
+ .unwrap();
+ let value: v8::Local<v8::Value> = v8str.into();
+ *result = transmute::<v8::Local<v8::Value>, napi_value>(value);
+ Ok(())
+}
+
+#[napi_sym::napi_sym]
+fn napi_create_string_utf8(
+ env: *mut Env,
+ string: *const u8,
+ length: isize,
+ result: *mut napi_value,
+) -> Result {
+ let env: &mut Env = env.as_mut().ok_or(Error::InvalidArg)?;
+
+ let string = if length == -1 {
+ std::ffi::CStr::from_ptr(string as *const _)
+ .to_str()
+ .unwrap()
+ } else {
+ let string = std::slice::from_raw_parts(string, length as usize);
+ std::str::from_utf8(string).unwrap()
+ };
+ let v8str = v8::String::new(&mut env.scope(), string).unwrap();
+ let value: v8::Local<v8::Value> = v8str.into();
+ *result = transmute::<v8::Local<v8::Value>, napi_value>(value);
+
+ Ok(())
+}
+
+#[napi_sym::napi_sym]
+fn napi_create_symbol(
+ env: *mut Env,
+ description: napi_value,
+ result: *mut napi_value,
+) -> Result {
+ let env: &mut Env = env.as_mut().ok_or(Error::InvalidArg)?;
+ let description = match description.is_null() {
+ true => None,
+ false => Some(
+ transmute::<napi_value, v8::Local<v8::Value>>(description)
+ .to_string(&mut env.scope())
+ .unwrap(),
+ ),
+ };
+ let sym = v8::Symbol::new(&mut env.scope(), description);
+ *result = transmute::<v8::Local<v8::Value>, napi_value>(sym.into());
+ Ok(())
+}
+
+#[napi_sym::napi_sym]
+fn napi_create_type_error(
+ env: *mut Env,
+ _code: napi_value,
+ msg: napi_value,
+ result: *mut napi_value,
+) -> Result {
+ let env: &mut Env = env.as_mut().ok_or(Error::InvalidArg)?;
+
+ // let code = transmute::<napi_value, v8::Local<v8::Value>>(code);
+ let msg = transmute::<napi_value, v8::Local<v8::Value>>(msg);
+
+ let msg = msg.to_string(&mut env.scope()).unwrap();
+
+ let error = v8::Exception::type_error(&mut env.scope(), msg);
+ *result = transmute::<v8::Local<v8::Value>, napi_value>(error);
+
+ Ok(())
+}
+
+#[napi_sym::napi_sym]
+fn napi_create_typedarray(
+ env: *mut Env,
+ ty: napi_typedarray_type,
+ length: usize,
+ arraybuffer: napi_value,
+ byte_offset: usize,
+ result: *mut napi_value,
+) -> Result {
+ let env: &mut Env = env.as_mut().ok_or(Error::InvalidArg)?;
+ let ab = transmute::<napi_value, v8::Local<v8::Value>>(arraybuffer);
+ let ab = v8::Local::<v8::ArrayBuffer>::try_from(ab).unwrap();
+ let typedarray: v8::Local<v8::Value> = match ty {
+ napi_uint8_array => {
+ v8::Uint8Array::new(&mut env.scope(), ab, byte_offset, length)
+ .unwrap()
+ .into()
+ }
+ napi_uint8_clamped_array => {
+ v8::Uint8ClampedArray::new(&mut env.scope(), ab, byte_offset, length)
+ .unwrap()
+ .into()
+ }
+ napi_int8_array => {
+ v8::Int8Array::new(&mut env.scope(), ab, byte_offset, length)
+ .unwrap()
+ .into()
+ }
+ napi_uint16_array => {
+ v8::Uint16Array::new(&mut env.scope(), ab, byte_offset, length)
+ .unwrap()
+ .into()
+ }
+ napi_int16_array => {
+ v8::Int16Array::new(&mut env.scope(), ab, byte_offset, length)
+ .unwrap()
+ .into()
+ }
+ napi_uint32_array => {
+ v8::Uint32Array::new(&mut env.scope(), ab, byte_offset, length)
+ .unwrap()
+ .into()
+ }
+ napi_int32_array => {
+ v8::Int32Array::new(&mut env.scope(), ab, byte_offset, length)
+ .unwrap()
+ .into()
+ }
+ napi_float32_array => {
+ v8::Float32Array::new(&mut env.scope(), ab, byte_offset, length)
+ .unwrap()
+ .into()
+ }
+ napi_float64_array => {
+ v8::Float64Array::new(&mut env.scope(), ab, byte_offset, length)
+ .unwrap()
+ .into()
+ }
+ napi_bigint64_array => {
+ v8::BigInt64Array::new(&mut env.scope(), ab, byte_offset, length)
+ .unwrap()
+ .into()
+ }
+ napi_biguint64_array => {
+ v8::BigUint64Array::new(&mut env.scope(), ab, byte_offset, length)
+ .unwrap()
+ .into()
+ }
+ _ => {
+ return Err(Error::InvalidArg);
+ }
+ };
+ *result = transmute::<v8::Local<v8::Value>, napi_value>(typedarray);
+ Ok(())
+}
+
+#[napi_sym::napi_sym]
+fn napi_create_uint32(
+ env: *mut Env,
+ value: u32,
+ result: *mut napi_value,
+) -> Result {
+ let env: &mut Env = env.as_mut().ok_or(Error::InvalidArg)?;
+ let value: v8::Local<v8::Value> =
+ v8::Number::new(&mut env.scope(), value as f64).into();
+ *result = transmute::<v8::Local<v8::Value>, napi_value>(value);
+ Ok(())
+}
+
+#[napi_sym::napi_sym]
+fn napi_make_callback(
+ env: *mut Env,
+ async_context: *mut c_void,
+ recv: napi_value,
+ func: napi_value,
+ argc: isize,
+ argv: *const napi_value,
+ result: *mut napi_value,
+) -> Result {
+ let env: &mut Env = env.as_mut().ok_or(Error::InvalidArg)?;
+ check_arg!(recv);
+ if argc > 0 {
+ check_arg!(argv);
+ }
+
+ if !async_context.is_null() {
+ eprintln!("napi_make_callback: async_context is not supported");
+ }
+
+ let recv = transmute::<napi_value, v8::Local<v8::Value>>(recv);
+ let func = transmute::<napi_value, v8::Local<v8::Value>>(func);
+
+ let func = v8::Local::<v8::Function>::try_from(func)
+ .map_err(|_| Error::FunctionExpected)?;
+ let argv: &[v8::Local<v8::Value>] =
+ transmute(std::slice::from_raw_parts(argv, argc as usize));
+ let ret = func.call(&mut env.scope(), recv, argv);
+ *result = transmute::<Option<v8::Local<v8::Value>>, napi_value>(ret);
+ Ok(())
+}
+
+#[napi_sym::napi_sym]
+fn napi_get_value_bigint_int64(
+ env: *mut Env,
+ value: napi_value,
+ result: *mut i64,
+) -> Result {
+ let env: &mut Env = env.as_mut().ok_or(Error::InvalidArg)?;
+ let value = transmute::<napi_value, v8::Local<v8::Value>>(value);
+ let bigint = value.to_big_int(&mut env.scope()).unwrap();
+ *result = bigint.i64_value().0;
+ Ok(())
+}
+
+#[napi_sym::napi_sym]
+fn napi_get_value_bigint_uint64(
+ env: *mut Env,
+ value: napi_value,
+ result: *mut u64,
+) -> Result {
+ let env: &mut Env = env.as_mut().ok_or(Error::InvalidArg)?;
+ let value = transmute::<napi_value, v8::Local<v8::Value>>(value);
+ let bigint = value.to_big_int(&mut env.scope()).unwrap();
+ *result = bigint.u64_value().0;
+ Ok(())
+}
+
+#[napi_sym::napi_sym]
+fn napi_get_value_bigint_words(
+ env: *mut Env,
+ value: napi_value,
+ sign_bit: *mut i32,
+ size: *mut usize,
+ out_words: *mut u64,
+) -> Result {
+ let env: &mut Env = env.as_mut().ok_or(Error::InvalidArg)?;
+
+ let value = transmute::<napi_value, v8::Local<v8::Value>>(value);
+ let bigint = value.to_big_int(&mut env.scope()).unwrap();
+
+ let out_words = std::slice::from_raw_parts_mut(out_words, *size);
+ let mut words = Vec::with_capacity(bigint.word_count());
+ let (sign, _) = bigint.to_words_array(words.as_mut_slice());
+ *sign_bit = sign as i32;
+
+ for (i, word) in out_words.iter_mut().enumerate() {
+ *word = words[i];
+ }
+
+ Ok(())
+}
+
+#[napi_sym::napi_sym]
+fn napi_get_value_bool(
+ env: *mut Env,
+ value: napi_value,
+ result: *mut bool,
+) -> Result {
+ let env: &mut Env = env.as_mut().ok_or(Error::InvalidArg)?;
+ let value = transmute::<napi_value, v8::Local<v8::Value>>(value);
+ *result = value.boolean_value(&mut env.scope());
+ Ok(())
+}
+
+#[napi_sym::napi_sym]
+fn napi_get_value_double(
+ env: *mut Env,
+ value: napi_value,
+ result: *mut f64,
+) -> Result {
+ let env: &mut Env = env.as_mut().ok_or(Error::InvalidArg)?;
+ let value = transmute::<napi_value, v8::Local<v8::Value>>(value);
+ *result = value.number_value(&mut env.scope()).unwrap();
+ Ok(())
+}
+
+#[napi_sym::napi_sym]
+fn napi_get_value_external(
+ _env: *mut Env,
+ value: napi_value,
+ result: *mut *mut c_void,
+) -> Result {
+ let value = transmute::<napi_value, v8::Local<v8::Value>>(value);
+ let ext = v8::Local::<v8::External>::try_from(value).unwrap();
+ *result = ext.value();
+ Ok(())
+}
+
+#[napi_sym::napi_sym]
+fn napi_get_value_int32(
+ env: *mut Env,
+ value: napi_value,
+ result: *mut i32,
+) -> Result {
+ let env: &mut Env = env.as_mut().ok_or(Error::InvalidArg)?;
+ let value = transmute::<napi_value, v8::Local<v8::Value>>(value);
+ *result = value.int32_value(&mut env.scope()).unwrap();
+ Ok(())
+}
+
+#[napi_sym::napi_sym]
+fn napi_get_value_int64(
+ env: *mut Env,
+ value: napi_value,
+ result: *mut i64,
+) -> Result {
+ let env: &mut Env = env.as_mut().ok_or(Error::InvalidArg)?;
+ let value = transmute::<napi_value, v8::Local<v8::Value>>(value);
+ *result = value.integer_value(&mut env.scope()).unwrap();
+ Ok(())
+}
+
+#[napi_sym::napi_sym]
+fn napi_get_value_string_latin1(
+ env: *mut Env,
+ value: napi_value,
+ buf: *mut u8,
+ bufsize: usize,
+ result: *mut usize,
+) -> Result {
+ let env: &mut Env = env.as_mut().ok_or(Error::InvalidArg)?;
+
+ let value = transmute::<napi_value, v8::Local<v8::Value>>(value);
+
+ if !value.is_string() && !value.is_string_object() {
+ return Err(Error::StringExpected);
+ }
+
+ let v8str = value.to_string(&mut env.scope()).unwrap();
+ let string_len = v8str.utf8_length(&mut env.scope());
+
+ if buf.is_null() {
+ *result = string_len;
+ } else if bufsize != 0 {
+ let buffer = std::slice::from_raw_parts_mut(buf, bufsize - 1);
+ let copied = v8str.write_one_byte(
+ &mut env.scope(),
+ buffer,
+ 0,
+ v8::WriteOptions::NO_NULL_TERMINATION,
+ );
+ buf.add(copied).write(0);
+ if !result.is_null() {
+ *result = copied;
+ }
+ } else if !result.is_null() {
+ *result = string_len;
+ }
+
+ Ok(())
+}
+
+#[napi_sym::napi_sym]
+fn napi_get_value_string_utf8(
+ env: *mut Env,
+ value: napi_value,
+ buf: *mut u8,
+ bufsize: usize,
+ result: *mut usize,
+) -> Result {
+ let env: &mut Env = env.as_mut().ok_or(Error::InvalidArg)?;
+
+ let value = transmute::<napi_value, v8::Local<v8::Value>>(value);
+
+ if !value.is_string() && !value.is_string_object() {
+ return Err(Error::StringExpected);
+ }
+
+ let v8str = value.to_string(&mut env.scope()).unwrap();
+ let string_len = v8str.utf8_length(&mut env.scope());
+
+ if buf.is_null() {
+ *result = string_len;
+ } else if bufsize != 0 {
+ let buffer = std::slice::from_raw_parts_mut(buf, bufsize - 1);
+ let copied = v8str.write_utf8(
+ &mut env.scope(),
+ buffer,
+ None,
+ v8::WriteOptions::NO_NULL_TERMINATION
+ | v8::WriteOptions::REPLACE_INVALID_UTF8,
+ );
+ buf.add(copied).write(0);
+ if !result.is_null() {
+ *result = copied;
+ }
+ } else if !result.is_null() {
+ *result = string_len;
+ }
+
+ Ok(())
+}
+
+#[napi_sym::napi_sym]
+fn napi_get_value_string_utf16(
+ env: *mut Env,
+ value: napi_value,
+ buf: *mut u16,
+ bufsize: usize,
+ result: *mut usize,
+) -> Result {
+ let env: &mut Env = env.as_mut().ok_or(Error::InvalidArg)?;
+
+ let value = transmute::<napi_value, v8::Local<v8::Value>>(value);
+
+ if !value.is_string() && !value.is_string_object() {
+ return Err(Error::StringExpected);
+ }
+
+ let v8str = value.to_string(&mut env.scope()).unwrap();
+ let string_len = v8str.length();
+
+ if buf.is_null() {
+ *result = string_len;
+ } else if bufsize != 0 {
+ let buffer = std::slice::from_raw_parts_mut(buf, bufsize - 1);
+ let copied = v8str.write(
+ &mut env.scope(),
+ buffer,
+ 0,
+ v8::WriteOptions::NO_NULL_TERMINATION,
+ );
+ buf.add(copied).write(0);
+ if !result.is_null() {
+ *result = copied;
+ }
+ } else if !result.is_null() {
+ *result = string_len;
+ }
+
+ Ok(())
+}
+
+#[napi_sym::napi_sym]
+fn napi_get_value_uint32(
+ env: *mut Env,
+ value: napi_value,
+ result: *mut u32,
+) -> Result {
+ let env: &mut Env = env.as_mut().ok_or(Error::InvalidArg)?;
+ let value = transmute::<napi_value, v8::Local<v8::Value>>(value);
+ *result = value.uint32_value(&mut env.scope()).unwrap();
+ Ok(())
+}
+
+// TODO
+#[napi_sym::napi_sym]
+fn napi_add_finalizer(
+ _env: *mut Env,
+ _js_object: napi_value,
+ _native_object: *const c_void,
+ _finalize_cb: napi_finalize,
+ _finalize_hint: *const c_void,
+ _result: *mut napi_ref,
+) -> Result {
+ eprintln!("napi_add_finalizer is not yet supported.");
+ Ok(())
+}
+
+#[napi_sym::napi_sym]
+fn napi_adjust_external_memory(
+ env: *mut Env,
+ change_in_bytes: i64,
+ adjusted_value: &mut i64,
+) -> Result {
+ let env: &mut Env = env.as_mut().ok_or(Error::InvalidArg)?;
+ let isolate = &mut *env.isolate_ptr;
+ *adjusted_value =
+ isolate.adjust_amount_of_external_allocated_memory(change_in_bytes);
+ Ok(())
+}
+
+#[napi_sym::napi_sym]
+fn napi_call_function(
+ env: *mut Env,
+ recv: napi_value,
+ func: napi_value,
+ argc: usize,
+ argv: *const napi_value,
+ result: *mut napi_value,
+) -> Result {
+ let env: &mut Env = env.as_mut().ok_or(Error::InvalidArg)?;
+ let recv = transmute::<napi_value, v8::Local<v8::Value>>(recv);
+ let func = transmute::<napi_value, v8::Local<v8::Value>>(func);
+ let func = v8::Local::<v8::Function>::try_from(func)
+ .map_err(|_| Error::FunctionExpected)?;
+
+ let argv: &[v8::Local<v8::Value>] =
+ transmute(std::slice::from_raw_parts(argv, argc as usize));
+ let ret = func.call(&mut env.scope(), recv, argv);
+ if !result.is_null() {
+ *result = transmute::<Option<v8::Local<v8::Value>>, napi_value>(ret);
+ }
+
+ Ok(())
+}
+
+#[napi_sym::napi_sym]
+fn napi_close_escapable_handle_scope(
+ env: *mut Env,
+ _scope: napi_escapable_handle_scope,
+) -> Result {
+ let mut _env = &mut *(env as *mut Env);
+ // TODO: do this properly
+ Ok(())
+}
+
+#[napi_sym::napi_sym]
+fn napi_close_handle_scope(env: *mut Env, scope: napi_handle_scope) -> Result {
+ let env = &mut *(env as *mut Env);
+ if env.open_handle_scopes == 0 {
+ return Err(Error::HandleScopeMismatch);
+ }
+ let _scope = &mut *(scope as *mut v8::HandleScope);
+ env.open_handle_scopes -= 1;
+ Ok(())
+}
+
+#[napi_sym::napi_sym]
+fn napi_define_class(
+ env_ptr: *mut Env,
+ name: *const c_char,
+ length: isize,
+ constructor: napi_callback,
+ callback_data: *mut c_void,
+ property_count: usize,
+ properties: *const napi_property_descriptor,
+ result: *mut napi_value,
+) -> Result {
+ let env: &mut Env = env_ptr.as_mut().ok_or(Error::InvalidArg)?;
+ check_arg!(result);
+ // check_arg!(constructor as *const c_void);
+
+ if property_count > 0 {
+ check_arg!(properties);
+ }
+
+ let name = if length == -1 {
+ std::ffi::CStr::from_ptr(name)
+ .to_str()
+ .map_err(|_| Error::InvalidArg)?
+ } else {
+ let slice = std::slice::from_raw_parts(name as *const u8, length as usize);
+ std::str::from_utf8(slice).unwrap()
+ };
+
+ let tpl =
+ create_function_template(env_ptr, Some(name), constructor, callback_data);
+
+ let scope = &mut env.scope();
+ let napi_properties: &[napi_property_descriptor] =
+ std::slice::from_raw_parts(properties, property_count);
+
+ for p in napi_properties {
+ let name = if !p.utf8name.is_null() {
+ let name_str = CStr::from_ptr(p.utf8name).to_str().unwrap();
+ v8::String::new(scope, name_str).unwrap()
+ } else {
+ transmute::<napi_value, v8::Local<v8::String>>(p.name)
+ };
+
+ let method = p.method;
+ let getter = p.getter;
+ let setter = p.setter;
+
+ if getter.is_some() || setter.is_some() {
+ let getter: Option<v8::Local<v8::FunctionTemplate>> = if getter.is_some()
+ {
+ Some(create_function_template(env_ptr, None, p.getter, p.data))
+ } else {
+ None
+ };
+ let setter: Option<v8::Local<v8::FunctionTemplate>> = if setter.is_some()
+ {
+ Some(create_function_template(env_ptr, None, p.setter, p.data))
+ } else {
+ None
+ };
+
+ let mut accessor_property = v8::NONE;
+ if getter.is_some()
+ && setter.is_some()
+ && (p.attributes & napi_writable) == 0
+ {
+ accessor_property = accessor_property | v8::READ_ONLY;
+ }
+ if p.attributes & napi_enumerable == 0 {
+ accessor_property = accessor_property | v8::DONT_ENUM;
+ }
+ if p.attributes & napi_configurable == 0 {
+ accessor_property = accessor_property | v8::DONT_DELETE;
+ }
+
+ let proto = tpl.prototype_template(scope);
+ proto.set_accessor_property(
+ name.into(),
+ getter,
+ setter,
+ accessor_property,
+ );
+
+ // // TODO: use set_accessor & set_accessor_with_setter
+ // match (getter, setter) {
+ // (Some(getter), None) => {
+ // proto.set(name.into(), getter.into());
+ // }
+ // (Some(getter), Some(setter)) => {
+ // proto.set(name.into(), getter.into());
+ // proto.set(name.into(), setter.into());
+ // }
+ // (None, Some(setter)) => {
+ // proto.set(name.into(), setter.into());
+ // }
+ // (None, None) => unreachable!(),
+ // }
+ } else if method.is_some() {
+ let function = create_function_template(env_ptr, None, p.method, p.data);
+ let proto = tpl.prototype_template(scope);
+ proto.set(name.into(), function.into());
+ } else {
+ let proto = tpl.prototype_template(scope);
+ proto.set(
+ name.into(),
+ transmute::<napi_value, v8::Local<v8::Data>>(p.value),
+ );
+ }
+ }
+
+ let value: v8::Local<v8::Value> = tpl.get_function(scope).unwrap().into();
+ *result = transmute::<v8::Local<v8::Value>, napi_value>(value);
+ Ok(())
+}
+
+#[napi_sym::napi_sym]
+fn napi_define_properties(
+ env_ptr: *mut Env,
+ obj: napi_value,
+ property_count: usize,
+ properties: *const napi_property_descriptor,
+) -> Result {
+ let env: &mut Env = env_ptr.as_mut().ok_or(Error::InvalidArg)?;
+ let scope = &mut env.scope();
+ let object = transmute::<napi_value, v8::Local<v8::Object>>(obj);
+ let properties = std::slice::from_raw_parts(properties, property_count);
+
+ for property in properties {
+ let name = if !property.utf8name.is_null() {
+ let name_str = CStr::from_ptr(property.utf8name).to_str().unwrap();
+ v8::String::new(scope, name_str).unwrap()
+ } else {
+ transmute::<napi_value, v8::Local<v8::String>>(property.name)
+ };
+
+ let method_ptr = property.method;
+
+ if method_ptr.is_some() {
+ let function: v8::Local<v8::Value> = {
+ let function =
+ create_function(env_ptr, None, property.method, property.data);
+ function.into()
+ };
+ object.set(scope, name.into(), function).unwrap();
+ }
+ }
+
+ Ok(())
+}
+
+#[napi_sym::napi_sym]
+fn napi_delete_element(
+ env: *mut Env,
+ value: napi_value,
+ index: u32,
+ result: *mut bool,
+) -> Result {
+ let env: &mut Env = env.as_mut().ok_or(Error::InvalidArg)?;
+ let value = transmute::<napi_value, v8::Local<v8::Value>>(value);
+ let obj = value.to_object(&mut env.scope()).unwrap();
+ *result = obj.delete_index(&mut env.scope(), index).unwrap_or(false);
+ Ok(())
+}
+
+#[napi_sym::napi_sym]
+fn napi_delete_property(
+ env: *mut Env,
+ value: napi_value,
+ key: napi_value,
+ result: *mut bool,
+) -> Result {
+ let env: &mut Env = env.as_mut().ok_or(Error::InvalidArg)?;
+ let value = transmute::<napi_value, v8::Local<v8::Value>>(value);
+ let obj = value.to_object(&mut env.scope()).unwrap();
+ *result = obj
+ .delete(
+ &mut env.scope(),
+ transmute::<napi_value, v8::Local<v8::Value>>(key),
+ )
+ .unwrap_or(false);
+ Ok(())
+}
+
+// TODO: properly implement ref counting stuff
+#[napi_sym::napi_sym]
+fn napi_delete_reference(env: *mut Env, _nref: napi_ref) -> Result {
+ let mut _env = &mut *(env as *mut Env);
+ Ok(())
+}
+
+#[napi_sym::napi_sym]
+fn napi_detach_arraybuffer(_env: *mut Env, value: napi_value) -> Result {
+ let value = transmute::<napi_value, v8::Local<v8::Value>>(value);
+ let ab = v8::Local::<v8::ArrayBuffer>::try_from(value).unwrap();
+ ab.detach();
+ Ok(())
+}
+
+#[napi_sym::napi_sym]
+fn napi_escape_handle(
+ _env: *mut Env,
+ _handle_scope: napi_escapable_handle_scope,
+ escapee: napi_value,
+ result: *mut napi_value,
+) -> Result {
+ // TODO
+ *result = escapee;
+ Ok(())
+}
+
+#[napi_sym::napi_sym]
+fn napi_get_all_property_names(_env: *mut Env) -> Result {
+ // TODO
+ Ok(())
+}
+
+#[napi_sym::napi_sym]
+fn napi_get_and_clear_last_exception(
+ env: *mut Env,
+ result: *mut napi_value,
+) -> Result {
+ let env: &mut Env = env.as_mut().ok_or(Error::InvalidArg)?;
+ // TODO: just return undefined for now we don't cache
+ // exceptions in env.
+ let value: v8::Local<v8::Value> = v8::undefined(&mut env.scope()).into();
+ *result = transmute::<v8::Local<v8::Value>, napi_value>(value);
+ Ok(())
+}
+
+#[napi_sym::napi_sym]
+fn napi_get_array_length(
+ _env: *mut Env,
+ value: napi_value,
+ result: *mut u32,
+) -> Result {
+ let value = transmute::<napi_value, v8::Local<v8::Value>>(value);
+ *result = v8::Local::<v8::Array>::try_from(value).unwrap().length();
+ Ok(())
+}
+
+#[napi_sym::napi_sym]
+fn napi_get_arraybuffer_info(
+ _env: *mut Env,
+ value: napi_value,
+ data: *mut *mut u8,
+ length: *mut usize,
+) -> Result {
+ let value = transmute::<napi_value, v8::Local<v8::Value>>(value);
+ let buf = v8::Local::<v8::ArrayBuffer>::try_from(value).unwrap();
+ if !data.is_null() {
+ *data = get_array_buffer_ptr(buf);
+ }
+ *length = buf.byte_length();
+ Ok(())
+}
+
+#[napi_sym::napi_sym]
+fn napi_get_boolean(
+ env: *mut Env,
+ value: bool,
+ result: *mut napi_value,
+) -> Result {
+ let env: &mut Env = env.as_mut().ok_or(Error::InvalidArg)?;
+ let value: v8::Local<v8::Value> =
+ v8::Boolean::new(&mut env.scope(), value).into();
+ *result = transmute::<v8::Local<v8::Value>, napi_value>(value);
+ Ok(())
+}
+
+#[napi_sym::napi_sym]
+fn napi_get_buffer_info(
+ env: *mut Env,
+ value: napi_value,
+ data: *mut *mut u8,
+ length: *mut usize,
+) -> Result {
+ let env: &mut Env = env.as_mut().ok_or(Error::InvalidArg)?;
+ let value = transmute::<napi_value, v8::Local<v8::Value>>(value);
+ let buf = v8::Local::<v8::Uint8Array>::try_from(value).unwrap();
+ let buffer_name = v8::String::new(&mut env.scope(), "buffer").unwrap();
+ let abuf = v8::Local::<v8::ArrayBuffer>::try_from(
+ buf.get(&mut env.scope(), buffer_name.into()).unwrap(),
+ )
+ .unwrap();
+ if !data.is_null() {
+ *data = get_array_buffer_ptr(abuf);
+ }
+ *length = abuf.byte_length();
+ Ok(())
+}
+
+#[napi_sym::napi_sym]
+fn napi_get_cb_info(
+ _env: *mut Env,
+ cbinfo: napi_callback_info,
+ argc: *mut i32,
+ argv: *mut napi_value,
+ this_arg: *mut napi_value,
+ cb_data: *mut *mut c_void,
+) -> Result {
+ let cbinfo: &CallbackInfo = &*(cbinfo as *const CallbackInfo);
+ let args = &*(cbinfo.args as *const v8::FunctionCallbackArguments);
+
+ if !cb_data.is_null() {
+ *cb_data = cbinfo.cb_info;
+ }
+
+ if !this_arg.is_null() {
+ let mut this = args.this();
+ *this_arg = transmute::<v8::Local<v8::Value>, napi_value>(this.into());
+ }
+
+ let len = args.length();
+ let mut v_argc = len;
+ if !argc.is_null() {
+ *argc = len;
+ }
+
+ if !argv.is_null() {
+ let mut v_argv = std::slice::from_raw_parts_mut(argv, v_argc as usize);
+ for i in 0..v_argc {
+ let mut arg = args.get(i);
+ v_argv[i as usize] = transmute::<v8::Local<v8::Value>, napi_value>(arg);
+ }
+ }
+
+ Ok(())
+}
+
+#[napi_sym::napi_sym]
+fn napi_get_dataview_info(
+ env: *mut Env,
+ value: napi_value,
+ data: *mut *mut u8,
+ length: *mut usize,
+) -> Result {
+ let env: &mut Env = env.as_mut().ok_or(Error::InvalidArg)?;
+ let value = transmute::<napi_value, v8::Local<v8::Value>>(value);
+ let buf = v8::Local::<v8::DataView>::try_from(value).unwrap();
+ let buffer_name = v8::String::new(&mut env.scope(), "buffer").unwrap();
+ let abuf = v8::Local::<v8::ArrayBuffer>::try_from(
+ buf.get(&mut env.scope(), buffer_name.into()).unwrap(),
+ )
+ .unwrap();
+ if !data.is_null() {
+ *data = get_array_buffer_ptr(abuf);
+ }
+ *length = abuf.byte_length();
+ Ok(())
+}
+
+#[napi_sym::napi_sym]
+fn napi_get_date_value(
+ env: *mut Env,
+ value: napi_value,
+ result: *mut f64,
+) -> Result {
+ let env: &mut Env = env.as_mut().ok_or(Error::InvalidArg)?;
+ let value = transmute::<napi_value, v8::Local<v8::Value>>(value);
+ let date = v8::Local::<v8::Date>::try_from(value).unwrap();
+ *result = date.number_value(&mut env.scope()).unwrap();
+ Ok(())
+}
+
+#[napi_sym::napi_sym]
+fn napi_get_element(
+ env: *mut Env,
+ object: napi_value,
+ index: u32,
+ result: *mut napi_value,
+) -> Result {
+ let env: &mut Env = env.as_mut().ok_or(Error::InvalidArg)?;
+ let object = transmute::<napi_value, v8::Local<v8::Value>>(object);
+ let array = v8::Local::<v8::Array>::try_from(object).unwrap();
+ let value: v8::Local<v8::Value> =
+ array.get_index(&mut env.scope(), index).unwrap();
+ *result = transmute::<v8::Local<v8::Value>, napi_value>(value);
+ Ok(())
+}
+
+#[napi_sym::napi_sym]
+fn napi_get_global(env: *mut Env, result: *mut napi_value) -> Result {
+ let env: &mut Env = env.as_mut().ok_or(Error::InvalidArg)?;
+
+ let context = &mut env.scope().get_current_context();
+ let global = context.global(&mut env.scope());
+ let value: v8::Local<v8::Value> = global.into();
+ *result = transmute::<v8::Local<v8::Value>, napi_value>(value);
+ Ok(())
+}
+
+#[napi_sym::napi_sym]
+fn napi_get_instance_data(env: *mut Env, result: *mut *mut c_void) -> Result {
+ let env = &mut *(env as *mut Env);
+ let shared = env.shared();
+ *result = shared.instance_data;
+ Ok(())
+}
+
+#[napi_sym::napi_sym]
+fn napi_get_last_error_info(
+ _env: *mut Env,
+ error_code: *mut *const napi_extended_error_info,
+) -> Result {
+ let err_info = Box::new(napi_extended_error_info {
+ error_message: std::ptr::null(),
+ engine_reserved: std::ptr::null_mut(),
+ engine_error_code: 0,
+ status_code: napi_ok,
+ });
+
+ *error_code = Box::into_raw(err_info);
+ Ok(())
+}
+
+#[napi_sym::napi_sym]
+fn napi_get_named_property(
+ env: *mut Env,
+ object: napi_value,
+ utf8_name: *const c_char,
+ result: *mut napi_value,
+) -> Result {
+ let env: &mut Env = env.as_mut().ok_or(Error::InvalidArg)?;
+ let object = transmute::<napi_value, v8::Local<v8::Value>>(object);
+ let utf8_name = std::ffi::CStr::from_ptr(utf8_name);
+ let name =
+ v8::String::new(&mut env.scope(), &utf8_name.to_string_lossy()).unwrap();
+ let value: v8::Local<v8::Value> = object
+ .to_object(&mut env.scope())
+ .unwrap()
+ .get(&mut env.scope(), name.into())
+ .unwrap();
+ *result = transmute::<v8::Local<v8::Value>, napi_value>(value);
+ Ok(())
+}
+
+#[napi_sym::napi_sym]
+fn napi_get_new_target(
+ _env: &mut Env,
+ cbinfo: &CallbackInfo,
+ result: &mut v8::Local<v8::Value>,
+) -> Result {
+ let info = &*(cbinfo.args as *const v8::FunctionCallbackArguments);
+ *result = info.new_target();
+ Ok(())
+}
+
+#[napi_sym::napi_sym]
+fn napi_get_null(env: *mut Env, result: *mut napi_value) -> Result {
+ let env: &mut Env = env.as_mut().ok_or(Error::InvalidArg)?;
+
+ let value: v8::Local<v8::Value> = v8::null(&mut env.scope()).into();
+ *result = transmute::<v8::Local<v8::Value>, napi_value>(value);
+ Ok(())
+}
+
+#[napi_sym::napi_sym]
+fn napi_get_property(
+ env: *mut Env,
+ object: napi_value,
+ key: napi_value,
+ result: *mut napi_value,
+) -> Result {
+ let env: &mut Env = env.as_mut().ok_or(Error::InvalidArg)?;
+ let object = transmute::<napi_value, v8::Local<v8::Object>>(object);
+ let key = transmute::<napi_value, v8::Local<v8::Value>>(key);
+ let value: v8::Local<v8::Value> = object.get(&mut env.scope(), key).unwrap();
+ *result = transmute::<v8::Local<v8::Value>, napi_value>(value);
+ Ok(())
+}
+
+#[napi_sym::napi_sym]
+fn napi_get_property_names(
+ env: *mut Env,
+ object: napi_value,
+ result: *mut napi_value,
+) -> Result {
+ let env: &mut Env = env.as_mut().ok_or(Error::InvalidArg)?;
+ let object = transmute::<napi_value, v8::Local<v8::Value>>(object);
+ let array: v8::Local<v8::Array> = object
+ .to_object(&mut env.scope())
+ .unwrap()
+ .get_property_names(&mut env.scope(), Default::default())
+ .unwrap();
+ let value: v8::Local<v8::Value> = array.into();
+ *result = transmute::<v8::Local<v8::Value>, napi_value>(value);
+ Ok(())
+}
+
+#[napi_sym::napi_sym]
+fn napi_get_prototype(
+ env: *mut Env,
+ value: napi_value,
+ result: *mut napi_value,
+) -> Result {
+ let env: &mut Env = env.as_mut().ok_or(Error::InvalidArg)?;
+ let value = transmute::<napi_value, v8::Local<v8::Value>>(value);
+ let obj = value.to_object(&mut env.scope()).unwrap();
+ let proto = obj.get_prototype(&mut env.scope()).unwrap();
+ *result = transmute::<v8::Local<v8::Value>, napi_value>(proto);
+ Ok(())
+}
+
+#[napi_sym::napi_sym]
+fn napi_get_reference_value(
+ env: *mut Env,
+ reference: napi_ref,
+ result: *mut napi_value,
+) -> Result {
+ // TODO
+ let _env: &mut Env = env.as_mut().ok_or(Error::InvalidArg)?;
+
+ let value = transmute::<napi_ref, v8::Local<v8::Value>>(reference);
+ *result = transmute::<v8::Local<v8::Value>, napi_value>(value);
+ Ok(())
+}
+
+#[napi_sym::napi_sym]
+fn napi_get_typedarray_info(
+ env: *mut Env,
+ value: napi_value,
+ data: *mut *mut u8,
+ length: *mut usize,
+) -> Result {
+ let env: &mut Env = env.as_mut().ok_or(Error::InvalidArg)?;
+ let value = transmute::<napi_value, v8::Local<v8::Value>>(value);
+ let buf = v8::Local::<v8::TypedArray>::try_from(value).unwrap();
+ let buffer_name = v8::String::new(&mut env.scope(), "buffer").unwrap();
+ let abuf = v8::Local::<v8::ArrayBuffer>::try_from(
+ buf.get(&mut env.scope(), buffer_name.into()).unwrap(),
+ )
+ .unwrap();
+ if !data.is_null() {
+ *data = get_array_buffer_ptr(abuf);
+ }
+ *length = abuf.byte_length();
+ Ok(())
+}
+
+#[napi_sym::napi_sym]
+fn napi_get_undefined(env: *mut Env, result: *mut napi_value) -> Result {
+ let env: &mut Env = env.as_mut().ok_or(Error::InvalidArg)?;
+ let value: v8::Local<v8::Value> = v8::undefined(&mut env.scope()).into();
+ *result = transmute::<v8::Local<v8::Value>, napi_value>(value);
+ Ok(())
+}
+
+pub const NAPI_VERSION: u32 = 8;
+
+#[napi_sym::napi_sym]
+fn napi_get_version(_: napi_env, version: *mut u32) -> Result {
+ *version = NAPI_VERSION;
+ Ok(())
+}
+
+#[napi_sym::napi_sym]
+fn napi_has_element(
+ env: *mut Env,
+ value: napi_value,
+ index: u32,
+ result: *mut bool,
+) -> Result {
+ let env: &mut Env = env.as_mut().ok_or(Error::InvalidArg)?;
+ let value = transmute::<napi_value, v8::Local<v8::Value>>(value);
+ let obj = value.to_object(&mut env.scope()).unwrap();
+ *result = obj.has_index(&mut env.scope(), index).unwrap_or(false);
+ Ok(())
+}
+
+#[napi_sym::napi_sym]
+fn napi_has_named_property(
+ env: *mut Env,
+ value: napi_value,
+ key: *const c_char,
+ result: *mut bool,
+) -> Result {
+ let env: &mut Env = env.as_mut().ok_or(Error::InvalidArg)?;
+ let value = transmute::<napi_value, v8::Local<v8::Value>>(value);
+ let obj = value.to_object(&mut env.scope()).unwrap();
+ let key = CStr::from_ptr(key).to_str().unwrap();
+ let key = v8::String::new(&mut env.scope(), key).unwrap();
+ *result = obj.has(&mut env.scope(), key.into()).unwrap_or(false);
+ Ok(())
+}
+
+#[napi_sym::napi_sym]
+fn napi_has_own_property(
+ env: *mut Env,
+ object: napi_value,
+ key: napi_value,
+ result: *mut bool,
+) -> Result {
+ let env: &mut Env = env.as_mut().ok_or(Error::InvalidArg)?;
+ let value = transmute::<napi_value, v8::Local<v8::Value>>(object);
+ let object = value.to_object(&mut env.scope()).unwrap();
+
+ let key = transmute::<napi_value, v8::Local<v8::Value>>(key);
+ if !key.is_name() {
+ return Err(Error::NameExpected);
+ }
+
+ let maybe = object
+ .has_own_property(
+ &mut env.scope(),
+ v8::Local::<v8::Name>::try_from(key).unwrap(),
+ )
+ .unwrap_or(false);
+
+ *result = maybe;
+ if !maybe {
+ return Err(Error::GenericFailure);
+ }
+
+ Ok(())
+}
+
+#[napi_sym::napi_sym]
+fn napi_has_property(
+ env: *mut Env,
+ value: napi_value,
+ key: napi_value,
+ result: *mut bool,
+) -> Result {
+ let env: &mut Env = env.as_mut().ok_or(Error::InvalidArg)?;
+ let value = transmute::<napi_value, v8::Local<v8::Value>>(value);
+ let obj = value.to_object(&mut env.scope()).unwrap();
+ *result = obj
+ .has(
+ &mut env.scope(),
+ transmute::<napi_value, v8::Local<v8::Value>>(key),
+ )
+ .unwrap_or(false);
+ Ok(())
+}
+
+#[napi_sym::napi_sym]
+fn napi_instanceof(
+ env: *mut Env,
+ value: napi_value,
+ constructor: napi_value,
+ result: *mut bool,
+) -> Result {
+ let env: &mut Env = env.as_mut().ok_or(Error::InvalidArg)?;
+ check_arg!(constructor);
+ check_arg!(value);
+
+ let value = transmute::<napi_value, v8::Local<v8::Value>>(value);
+ let constructor = transmute::<napi_value, v8::Local<v8::Value>>(constructor);
+ let ctor = constructor
+ .to_object(&mut env.scope())
+ .ok_or(Error::ObjectExpected)?;
+ if !ctor.is_function() {
+ return Err(Error::FunctionExpected);
+ }
+ let maybe = value.instance_of(&mut env.scope(), ctor);
+ match maybe {
+ Some(res) => {
+ *result = res;
+ Ok(())
+ }
+ None => Err(Error::GenericFailure),
+ }
+}
+
+#[napi_sym::napi_sym]
+fn napi_is_array(
+ _env: *mut Env,
+ value: napi_value,
+ result: *mut bool,
+) -> Result {
+ let value = transmute::<napi_value, v8::Local<v8::Value>>(value);
+ *result = value.is_array();
+ Ok(())
+}
+
+#[napi_sym::napi_sym]
+fn napi_is_arraybuffer(
+ _env: *mut Env,
+ value: napi_value,
+ result: *mut bool,
+) -> Result {
+ let value = transmute::<napi_value, v8::Local<v8::Value>>(value);
+ *result = value.is_array_buffer();
+ Ok(())
+}
+
+#[napi_sym::napi_sym]
+fn napi_is_buffer(
+ _env: *mut Env,
+ value: napi_value,
+ result: *mut bool,
+) -> Result {
+ let value = transmute::<napi_value, v8::Local<v8::Value>>(value);
+ // TODO: should we assume Buffer as Uint8Array in Deno?
+ // or use std/node polyfill?
+ *result = value.is_typed_array();
+ Ok(())
+}
+
+#[napi_sym::napi_sym]
+fn napi_is_dataview(
+ _env: *mut Env,
+ value: napi_value,
+ result: *mut bool,
+) -> Result {
+ let value = transmute::<napi_value, v8::Local<v8::Value>>(value);
+ *result = value.is_data_view();
+ Ok(())
+}
+
+#[napi_sym::napi_sym]
+fn napi_is_date(
+ _env: *mut Env,
+ value: napi_value,
+ result: *mut bool,
+) -> Result {
+ let value = transmute::<napi_value, v8::Local<v8::Value>>(value);
+ *result = value.is_date();
+ Ok(())
+}
+
+#[napi_sym::napi_sym]
+fn napi_is_detached_arraybuffer(
+ _env: *mut Env,
+ value: napi_value,
+ result: *mut bool,
+) -> Result {
+ let value = transmute::<napi_value, v8::Local<v8::Value>>(value);
+ let _ab = v8::Local::<v8::ArrayBuffer>::try_from(value).unwrap();
+ // TODO: what is API for checking if ArrayBuffer is detached?
+ // there's only is_detachable I could find.
+ *result = false;
+ Ok(())
+}
+
+#[napi_sym::napi_sym]
+fn napi_is_error(
+ _env: *mut Env,
+ value: napi_value,
+ result: *mut bool,
+) -> Result {
+ let value = transmute::<napi_value, v8::Local<v8::Value>>(value);
+ // TODO
+ *result = value.is_object();
+ Ok(())
+}
+
+#[napi_sym::napi_sym]
+fn napi_is_exception_pending(env: *mut Env, result: *mut bool) -> Result {
+ let mut _env = &mut *(env as *mut Env);
+ // TODO
+ *result = false;
+ Ok(())
+}
+
+#[napi_sym::napi_sym]
+fn napi_is_promise(
+ _env: *mut Env,
+ value: napi_value,
+ result: *mut bool,
+) -> Result {
+ let value = transmute::<napi_value, v8::Local<v8::Value>>(value);
+ *result = value.is_promise();
+ Ok(())
+}
+
+#[napi_sym::napi_sym]
+fn napi_is_typedarray(
+ _env: *mut Env,
+ value: napi_value,
+ result: *mut bool,
+) -> Result {
+ let value = transmute::<napi_value, v8::Local<v8::Value>>(value);
+ *result = value.is_typed_array();
+ Ok(())
+}
+
+#[napi_sym::napi_sym]
+fn napi_new_instance(
+ env: *mut Env,
+ constructor: napi_value,
+ argc: usize,
+ argv: *const napi_value,
+ result: *mut napi_value,
+) -> Result {
+ let env: &mut Env = env.as_mut().ok_or(Error::InvalidArg)?;
+ let constructor = transmute::<napi_value, v8::Local<v8::Value>>(constructor);
+ let constructor = v8::Local::<v8::Function>::try_from(constructor).unwrap();
+ let args: &[v8::Local<v8::Value>] =
+ transmute(std::slice::from_raw_parts(argv, argc));
+ let inst = constructor.new_instance(&mut env.scope(), args).unwrap();
+ let value: v8::Local<v8::Value> = inst.into();
+ *result = transmute::<v8::Local<v8::Value>, napi_value>(value);
+ Ok(())
+}
+
+#[napi_sym::napi_sym]
+fn napi_object_freeze(env: &mut Env, object: v8::Local<v8::Value>) -> Result {
+ let object = object.to_object(&mut env.scope()).unwrap();
+ let maybe =
+ object.set_integrity_level(&mut env.scope(), v8::IntegrityLevel::Frozen);
+
+ match maybe {
+ Some(_) => Ok(()),
+ None => Err(Error::GenericFailure),
+ }
+}
+
+#[napi_sym::napi_sym]
+fn napi_object_seal(env: &mut Env, object: v8::Local<v8::Value>) -> Result {
+ let object = object.to_object(&mut env.scope()).unwrap();
+ let maybe =
+ object.set_integrity_level(&mut env.scope(), v8::IntegrityLevel::Sealed);
+
+ match maybe {
+ Some(_) => Ok(()),
+ None => Err(Error::GenericFailure),
+ }
+}
+
+#[napi_sym::napi_sym]
+fn napi_open_escapable_handle_scope(
+ _env: *mut Env,
+ _result: *mut napi_escapable_handle_scope,
+) -> Result {
+ // TODO: do this properly
+ Ok(())
+}
+
+#[napi_sym::napi_sym]
+fn napi_open_handle_scope(
+ env: *mut Env,
+ _result: *mut napi_handle_scope,
+) -> Result {
+ let env = &mut *(env as *mut Env);
+
+ // *result = &mut env.scope() as *mut _ as napi_handle_scope;
+ env.open_handle_scopes += 1;
+ Ok(())
+}
+
+#[napi_sym::napi_sym]
+fn napi_reference_ref() -> Result {
+ // TODO
+ Ok(())
+}
+
+#[napi_sym::napi_sym]
+fn napi_reference_unref() -> Result {
+ // TODO
+ Ok(())
+}
+
+#[napi_sym::napi_sym]
+fn napi_reject_deferred(
+ env: *mut Env,
+ deferred: napi_deferred,
+ error: napi_value,
+) -> Result {
+ let env: &mut Env = env.as_mut().ok_or(Error::InvalidArg)?;
+
+ let deferred_ptr =
+ NonNull::new_unchecked(deferred as *mut v8::PromiseResolver);
+ // TODO(@littledivy): Use Global::from_raw instead casting to local.
+ // SAFETY: Isolate is still alive unless the module is doing something weird,
+ // global data is the size of a pointer.
+ // Global pointer is obtained from napi_create_promise
+ let resolver = transmute::<
+ NonNull<v8::PromiseResolver>,
+ v8::Local<v8::PromiseResolver>,
+ >(deferred_ptr);
+ resolver
+ .reject(
+ &mut env.scope(),
+ transmute::<napi_value, v8::Local<v8::Value>>(error),
+ )
+ .unwrap();
+ Ok(())
+}
+
+#[napi_sym::napi_sym]
+fn napi_remove_wrap(env: *mut Env, value: napi_value) -> Result {
+ let env: &mut Env = env.as_mut().ok_or(Error::InvalidArg)?;
+ let value = transmute::<napi_value, v8::Local<v8::Value>>(value);
+ let obj = value.to_object(&mut env.scope()).unwrap();
+ let shared = &*(env.shared as *const EnvShared);
+ let napi_wrap = v8::Local::new(&mut env.scope(), &shared.napi_wrap);
+ obj.delete_private(&mut env.scope(), napi_wrap).unwrap();
+ Ok(())
+}
+
+#[napi_sym::napi_sym]
+fn napi_resolve_deferred(
+ env: *mut Env,
+ deferred: napi_deferred,
+ result: napi_value,
+) -> Result {
+ let env: &mut Env = env.as_mut().ok_or(Error::InvalidArg)?;
+ let deferred_ptr =
+ NonNull::new_unchecked(deferred as *mut v8::PromiseResolver);
+ // TODO(@littledivy): Use Global::from_raw instead casting to local.
+ // SAFETY: Isolate is still alive unless the module is doing something weird,
+ // global data is the size of a pointer.
+ // Global pointer is obtained from napi_create_promise
+ let resolver = transmute::<
+ NonNull<v8::PromiseResolver>,
+ v8::Local<v8::PromiseResolver>,
+ >(deferred_ptr);
+ resolver
+ .resolve(
+ &mut env.scope(),
+ transmute::<napi_value, v8::Local<v8::Value>>(result),
+ )
+ .unwrap();
+ Ok(())
+}
+
+#[napi_sym::napi_sym]
+fn napi_run_script(
+ env: *mut Env,
+ script: napi_value,
+ result: *mut napi_value,
+) -> Result {
+ let env: &mut Env = env.as_mut().ok_or(Error::InvalidArg)?;
+
+ let script = transmute::<napi_value, v8::Local<v8::Value>>(script);
+ if !script.is_string() {
+ return Err(Error::StringExpected);
+ }
+ let script = script.to_string(&mut env.scope()).unwrap();
+
+ let script = v8::Script::compile(&mut env.scope(), script, None);
+ if script.is_none() {
+ return Err(Error::GenericFailure);
+ }
+ let script = script.unwrap();
+ let rv = script.run(&mut env.scope());
+
+ if let Some(rv) = rv {
+ *result = transmute::<v8::Local<v8::Value>, napi_value>(rv);
+ } else {
+ return Err(Error::GenericFailure);
+ }
+
+ Ok(())
+}
+
+#[napi_sym::napi_sym]
+fn napi_set_element(
+ env: *mut Env,
+ object: napi_value,
+ index: u32,
+ value: napi_value,
+) -> Result {
+ let env: &mut Env = env.as_mut().ok_or(Error::InvalidArg)?;
+ let object = transmute::<napi_value, v8::Local<v8::Value>>(object);
+ let array = v8::Local::<v8::Array>::try_from(object).unwrap();
+ let value = transmute::<napi_value, v8::Local<v8::Value>>(value);
+ array.set_index(&mut env.scope(), index, value).unwrap();
+ Ok(())
+}
+
+#[napi_sym::napi_sym]
+fn napi_set_instance_data(
+ env: *mut Env,
+ data: *mut c_void,
+ finalize_cb: napi_finalize,
+ finalize_hint: *mut c_void,
+) -> Result {
+ let env = &mut *(env as *mut Env);
+ let shared = env.shared_mut();
+ shared.instance_data = data;
+ shared.data_finalize = if !(finalize_cb as *const c_void).is_null() {
+ Some(finalize_cb)
+ } else {
+ None
+ };
+ shared.data_finalize_hint = finalize_hint;
+ Ok(())
+}
+
+#[napi_sym::napi_sym]
+fn napi_set_named_property(
+ env: *mut Env,
+ object: napi_value,
+ name: *const c_char,
+ value: napi_value,
+) -> Result {
+ let env: &mut Env = env.as_mut().ok_or(Error::InvalidArg)?;
+ let name = CStr::from_ptr(name).to_str().unwrap();
+ let object = transmute::<napi_value, v8::Local<v8::Object>>(object);
+ let value = transmute::<napi_value, v8::Local<v8::Value>>(value);
+ let name = v8::String::new(&mut env.scope(), name).unwrap();
+ object.set(&mut env.scope(), name.into(), value).unwrap();
+ Ok(())
+}
+
+#[napi_sym::napi_sym]
+fn napi_set_property(
+ env: *mut Env,
+ object: napi_value,
+ property: napi_value,
+ value: napi_value,
+) -> Result {
+ let env: &mut Env = env.as_mut().ok_or(Error::InvalidArg)?;
+ let object = transmute::<napi_value, v8::Local<v8::Value>>(object);
+ let object = object.to_object(&mut env.scope()).unwrap();
+ let property = transmute::<napi_value, v8::Local<v8::Value>>(property);
+ let value = transmute::<napi_value, v8::Local<v8::Value>>(value);
+ object.set(&mut env.scope(), property, value).unwrap();
+ Ok(())
+}
+
+#[napi_sym::napi_sym]
+fn napi_strict_equals(
+ _env: *mut Env,
+ lhs: napi_value,
+ rhs: napi_value,
+ result: *mut bool,
+) -> Result {
+ let lhs = transmute::<napi_value, v8::Local<v8::Value>>(lhs);
+ let rhs = transmute::<napi_value, v8::Local<v8::Value>>(rhs);
+ *result = lhs.strict_equals(rhs);
+ Ok(())
+}
+
+#[napi_sym::napi_sym]
+fn napi_throw(env: *mut Env, error: napi_value) -> Result {
+ let env: &mut Env = env.as_mut().ok_or(Error::InvalidArg)?;
+ let error = transmute::<napi_value, v8::Local<v8::Value>>(error);
+ env.scope().throw_exception(error);
+ Ok(())
+}
+
+#[napi_sym::napi_sym]
+fn napi_throw_error(
+ env: *mut Env,
+ _code: *const c_char,
+ msg: *const c_char,
+) -> Result {
+ let env: &mut Env = env.as_mut().ok_or(Error::InvalidArg)?;
+
+ // let code = CStr::from_ptr(code).to_str().unwrap();
+ let msg = CStr::from_ptr(msg).to_str().unwrap();
+
+ // let code = v8::String::new(&mut env.scope(), code).unwrap();
+ let msg = v8::String::new(&mut env.scope(), msg).unwrap();
+
+ let error = v8::Exception::error(&mut env.scope(), msg);
+ env.scope().throw_exception(error);
+
+ Ok(())
+}
+
+#[napi_sym::napi_sym]
+fn napi_throw_range_error(
+ env: *mut Env,
+ _code: *const c_char,
+ msg: *const c_char,
+) -> Result {
+ let env: &mut Env = env.as_mut().ok_or(Error::InvalidArg)?;
+
+ // let code = CStr::from_ptr(code).to_str().unwrap();
+ let msg = CStr::from_ptr(msg).to_str().unwrap();
+
+ // let code = v8::String::new(&mut env.scope(), code).unwrap();
+ let msg = v8::String::new(&mut env.scope(), msg).unwrap();
+
+ let error = v8::Exception::range_error(&mut env.scope(), msg);
+ env.scope().throw_exception(error);
+
+ Ok(())
+}
+
+#[napi_sym::napi_sym]
+fn napi_throw_type_error(
+ env: *mut Env,
+ _code: *const c_char,
+ msg: *const c_char,
+) -> Result {
+ let env: &mut Env = env.as_mut().ok_or(Error::InvalidArg)?;
+
+ // let code = CStr::from_ptr(code).to_str().unwrap();
+ let msg = CStr::from_ptr(msg).to_str().unwrap();
+
+ // let code = v8::String::new(&mut env.scope(), code).unwrap();
+ let msg = v8::String::new(&mut env.scope(), msg).unwrap();
+
+ let error = v8::Exception::type_error(&mut env.scope(), msg);
+ env.scope().throw_exception(error);
+
+ Ok(())
+}
+
+pub fn get_value_type(value: v8::Local<v8::Value>) -> Option<napi_valuetype> {
+ if value.is_undefined() {
+ Some(napi_undefined)
+ } else if value.is_null() {
+ Some(napi_null)
+ } else if value.is_external() {
+ Some(napi_external)
+ } else if value.is_boolean() {
+ Some(napi_boolean)
+ } else if value.is_number() {
+ Some(napi_number)
+ } else if value.is_big_int() {
+ Some(napi_bigint)
+ } else if value.is_string() {
+ Some(napi_string)
+ } else if value.is_symbol() {
+ Some(napi_symbol)
+ } else if value.is_function() {
+ Some(napi_function)
+ } else if value.is_object() {
+ Some(napi_object)
+ } else {
+ None
+ }
+}
+
+#[napi_sym::napi_sym]
+fn napi_typeof(
+ _env: *mut Env,
+ value: napi_value,
+ result: *mut napi_valuetype,
+) -> Result {
+ if value.is_null() {
+ *result = napi_undefined;
+ return Ok(());
+ }
+ let value = transmute::<napi_value, v8::Local<v8::Value>>(value);
+ let ty = get_value_type(value);
+ if let Some(ty) = ty {
+ *result = ty;
+ Ok(())
+ } else {
+ Err(Error::InvalidArg)
+ }
+}
+
+#[napi_sym::napi_sym]
+fn napi_unwrap(
+ env: *mut Env,
+ value: napi_value,
+ result: *mut *mut c_void,
+) -> Result {
+ let env: &mut Env = env.as_mut().ok_or(Error::InvalidArg)?;
+ let value = transmute::<napi_value, v8::Local<v8::Value>>(value);
+ let obj = value.to_object(&mut env.scope()).unwrap();
+ let shared = &*(env.shared as *const EnvShared);
+ let napi_wrap = v8::Local::new(&mut env.scope(), &shared.napi_wrap);
+ let ext = obj.get_private(&mut env.scope(), napi_wrap).unwrap();
+ let ext = v8::Local::<v8::External>::try_from(ext).unwrap();
+ *result = ext.value();
+ Ok(())
+}
+
+#[napi_sym::napi_sym]
+fn napi_wrap(
+ env: *mut Env,
+ value: napi_value,
+ native_object: *mut c_void,
+) -> Result {
+ let env: &mut Env = env.as_mut().ok_or(Error::InvalidArg)?;
+ let value = transmute::<napi_value, v8::Local<v8::Value>>(value);
+ let obj = value.to_object(&mut env.scope()).unwrap();
+ let shared = &*(env.shared as *const EnvShared);
+ let napi_wrap = v8::Local::new(&mut env.scope(), &shared.napi_wrap);
+ let ext = v8::External::new(&mut env.scope(), native_object);
+ obj.set_private(&mut env.scope(), napi_wrap, ext.into());
+ Ok(())
+}
+
+#[napi_sym::napi_sym]
+fn node_api_throw_syntax_error(
+ env: *mut Env,
+ _code: *const c_char,
+ msg: *const c_char,
+) -> Result {
+ let env: &mut Env = env.as_mut().ok_or(Error::InvalidArg)?;
+
+ // let code = CStr::from_ptr(code).to_str().unwrap();
+ let msg = CStr::from_ptr(msg).to_str().unwrap();
+
+ // let code = v8::String::new(&mut env.scope(), code).unwrap();
+ let msg = v8::String::new(&mut env.scope(), msg).unwrap();
+
+ let error = v8::Exception::syntax_error(&mut env.scope(), msg);
+ env.scope().throw_exception(error);
+
+ Ok(())
+}
+
+#[napi_sym::napi_sym]
+fn node_api_create_syntax_error(
+ env: *mut Env,
+ _code: napi_value,
+ msg: napi_value,
+ result: *mut napi_value,
+) -> Result {
+ let env: &mut Env = env.as_mut().ok_or(Error::InvalidArg)?;
+
+ // let code = transmute::<napi_value, v8::Local<v8::Value>>(code);
+ let msg = transmute::<napi_value, v8::Local<v8::Value>>(msg);
+
+ let msg = msg.to_string(&mut env.scope()).unwrap();
+
+ let error = v8::Exception::syntax_error(&mut env.scope(), msg);
+ *result = transmute::<v8::Local<v8::Value>, napi_value>(error);
+
+ Ok(())
+}
diff --git a/cli/napi/mod.rs b/cli/napi/mod.rs
new file mode 100644
index 000000000..8982a732a
--- /dev/null
+++ b/cli/napi/mod.rs
@@ -0,0 +1,88 @@
+// Copyright 2018-2022 the Deno authors. All rights reserved. MIT license.
+
+#![allow(unused_mut)]
+#![allow(non_camel_case_types)]
+#![allow(clippy::undocumented_unsafe_blocks)]
+
+//! Symbols to be exported are now defined in this JSON file.
+//! The `#[napi_sym]` macro checks for missing entries and panics.
+//!
+//! `./tools/napi/generate_link_win.js` is used to generate the LINK `cli/exports.def` on Windows,
+//! which is also checked into git.
+//!
+//! To add a new napi function:
+//! 1. Place `#[napi_sym]` on top of your implementation.
+//! 2. Add the function's identifier to this JSON list.
+//! 3. Finally, run `./tools/napi/generate_link_win.js` to update `cli/exports.def`.
+
+pub mod r#async;
+pub mod env;
+pub mod js_native_api;
+pub mod threadsafe_functions;
+pub mod util;
+
+use std::os::raw::c_int;
+use std::os::raw::c_void;
+
+pub type uv_async_t = *mut uv_async;
+pub type uv_loop_t = *mut c_void;
+pub type uv_async_cb = extern "C" fn(handle: uv_async_t);
+
+use deno_core::futures::channel::mpsc;
+#[repr(C)]
+pub struct uv_async {
+ pub data: Option<*mut c_void>,
+ callback: uv_async_cb,
+ sender: Option<
+ mpsc::UnboundedSender<deno_runtime::deno_napi::PendingNapiAsyncWork>,
+ >,
+ ref_sender: Option<
+ mpsc::UnboundedSender<deno_runtime::deno_napi::ThreadSafeFunctionStatus>,
+ >,
+}
+
+#[no_mangle]
+pub extern "C" fn uv_default_loop() -> uv_loop_t {
+ std::ptr::null_mut()
+}
+
+/// # Safety
+/// libuv APIs
+#[no_mangle]
+pub unsafe extern "C" fn uv_async_init(
+ _loop: uv_loop_t,
+ async_: uv_async_t,
+ cb: uv_async_cb,
+) -> c_int {
+ (*async_).callback = cb;
+ deno_runtime::deno_napi::ASYNC_WORK_SENDER.with(|sender| {
+ (*async_).sender = Some(sender.borrow().clone().unwrap());
+ });
+
+ deno_runtime::deno_napi::THREAD_SAFE_FN_SENDER.with(|sender| {
+ sender
+ .borrow()
+ .clone()
+ .unwrap()
+ .unbounded_send(deno_runtime::deno_napi::ThreadSafeFunctionStatus::Alive)
+ .unwrap();
+ (*async_).ref_sender = Some(sender.borrow().clone().unwrap());
+ });
+
+ 0
+}
+
+/// # Safety
+/// libuv APIs
+#[no_mangle]
+pub unsafe extern "C" fn uv_async_send(async_: uv_async_t) -> c_int {
+ let sender = (*async_).sender.as_ref().unwrap();
+ let fut = Box::new(move || {
+ ((*async_).callback)(async_);
+ });
+
+ match sender.unbounded_send(fut) {
+ Ok(_) => 0,
+ Err(_) => 1,
+ }
+}
diff --git a/cli/napi/threadsafe_functions.rs b/cli/napi/threadsafe_functions.rs
new file mode 100644
index 000000000..5374b6159
--- /dev/null
+++ b/cli/napi/threadsafe_functions.rs
@@ -0,0 +1,199 @@
+// Copyright 2018-2022 the Deno authors. All rights reserved. MIT license.
+
+use deno_core::futures::channel::mpsc;
+use deno_runtime::deno_napi::*;
+use std::mem::forget;
+use std::sync::mpsc::channel;
+
+pub struct TsFn {
+ pub env: *mut Env,
+ pub maybe_func: Option<v8::Global<v8::Function>>,
+ pub maybe_call_js_cb: Option<napi_threadsafe_function_call_js>,
+ pub context: *mut c_void,
+ pub thread_counter: usize,
+ sender: mpsc::UnboundedSender<PendingNapiAsyncWork>,
+ tsfn_sender: mpsc::UnboundedSender<ThreadSafeFunctionStatus>,
+}
+
+impl TsFn {
+ pub fn acquire(&mut self) -> Result {
+ self.thread_counter += 1;
+ Ok(())
+ }
+
+ pub fn release(mut self) -> Result {
+ self.thread_counter -= 1;
+ if self.thread_counter == 0 {
+ self
+ .tsfn_sender
+ .unbounded_send(ThreadSafeFunctionStatus::Dead)
+ .map_err(|_| Error::GenericFailure)?;
+ drop(self);
+ } else {
+ forget(self);
+ }
+ Ok(())
+ }
+
+ pub fn call(&self, data: *mut c_void, is_blocking: bool) {
+ let js_func = self.maybe_func.clone();
+ let (tx, rx) = channel();
+
+ if let Some(call_js_cb) = self.maybe_call_js_cb {
+ let context = self.context;
+ let env = self.env;
+ let call = Box::new(move || {
+ let scope = &mut unsafe { (*env).scope() };
+ match js_func {
+ Some(func) => {
+ let func: v8::Local<v8::Value> =
+ func.open(scope).to_object(scope).unwrap().into();
+ unsafe {
+ call_js_cb(
+ env as *mut c_void,
+ transmute::<v8::Local<v8::Value>, napi_value>(func),
+ context,
+ data,
+ )
+ };
+ }
+ None => {
+ unsafe {
+ call_js_cb(
+ env as *mut c_void,
+ std::ptr::null_mut(),
+ context,
+ data,
+ )
+ };
+ }
+ }
+
+ // Receiver might have been already dropped
+ let _ = tx.send(());
+ });
+ // This call should never fail
+ self.sender.unbounded_send(call).unwrap();
+ } else if let Some(_js_func) = js_func {
+ let call = Box::new(move || {
+ // TODO: func.call
+ // let func = js_func.open(scope);
+ // Receiver might have been already dropped
+ let _ = tx.send(());
+ });
+ // This call should never fail
+ self.sender.unbounded_send(call).unwrap();
+ }
+
+ if is_blocking {
+ rx.recv().unwrap();
+ }
+ }
+}
+
+#[napi_sym::napi_sym]
+fn napi_create_threadsafe_function(
+ env: *mut Env,
+ func: napi_value,
+ _async_resource: napi_value,
+ _async_resource_name: napi_value,
+ _max_queue_size: usize,
+ initial_thread_count: usize,
+ _thread_finialize_data: *mut c_void,
+ _thread_finalize_cb: napi_finalize,
+ context: *mut c_void,
+ maybe_call_js_cb: Option<napi_threadsafe_function_call_js>,
+ result: *mut napi_threadsafe_function,
+) -> Result {
+ let env_ref = env.as_mut().ok_or(Error::GenericFailure)?;
+ if initial_thread_count == 0 {
+ return Err(Error::InvalidArg);
+ }
+ let maybe_func = func
+ .as_mut()
+ .map(|func| {
+ let value = transmute::<napi_value, v8::Local<v8::Value>>(func);
+ let func = v8::Local::<v8::Function>::try_from(value)
+ .map_err(|_| Error::FunctionExpected)?;
+ Ok(v8::Global::new(&mut env_ref.scope(), func))
+ })
+ .transpose()?;
+
+ let tsfn = TsFn {
+ maybe_func,
+ maybe_call_js_cb,
+ context,
+ thread_counter: initial_thread_count,
+ sender: env_ref.async_work_sender.clone(),
+ tsfn_sender: env_ref.threadsafe_function_sender.clone(),
+ env,
+ };
+
+ env_ref
+ .threadsafe_function_sender
+ .unbounded_send(ThreadSafeFunctionStatus::Alive)
+ .map_err(|_| Error::GenericFailure)?;
+ *result = transmute::<Box<TsFn>, _>(Box::new(tsfn));
+
+ Ok(())
+}
+
+#[napi_sym::napi_sym]
+fn napi_acquire_threadsafe_function(
+ tsfn: napi_threadsafe_function,
+ _mode: napi_threadsafe_function_release_mode,
+) -> Result {
+ let tsfn: &mut TsFn = &mut *(tsfn as *mut TsFn);
+ tsfn.acquire()?;
+
+ Ok(())
+}
+
+#[napi_sym::napi_sym]
+fn napi_unref_threadsafe_function(
+ _env: &mut Env,
+ tsfn: napi_threadsafe_function,
+) -> Result {
+ let _tsfn: &TsFn = &*(tsfn as *const TsFn);
+
+ Ok(())
+}
+
+/// Maybe called from any thread.
+#[napi_sym::napi_sym]
+pub fn napi_get_threadsafe_function_context(
+ func: napi_threadsafe_function,
+ result: *mut *const c_void,
+) -> Result {
+ let tsfn: &TsFn = &*(func as *const TsFn);
+ *result = tsfn.context;
+ Ok(())
+}
+
+#[napi_sym::napi_sym]
+fn napi_call_threadsafe_function(
+ func: napi_threadsafe_function,
+ data: *mut c_void,
+ is_blocking: napi_threadsafe_function_call_mode,
+) -> Result {
+ let tsfn: &TsFn = &*(func as *const TsFn);
+ tsfn.call(data, is_blocking != 0);
+ Ok(())
+}
+
+#[napi_sym::napi_sym]
+fn napi_ref_threadsafe_function() -> Result {
+ // TODO
+ Ok(())
+}
+
+#[napi_sym::napi_sym]
+fn napi_release_threadsafe_function(
+ tsfn: napi_threadsafe_function,
+ _mode: napi_threadsafe_function_release_mode,
+) -> Result {
+ let tsfn: Box<TsFn> = Box::from_raw(tsfn as *mut TsFn);
+ tsfn.release()?;
+
+ Ok(())
+}
diff --git a/cli/napi/util.rs b/cli/napi/util.rs
new file mode 100644
index 000000000..2dea03554
--- /dev/null
+++ b/cli/napi/util.rs
@@ -0,0 +1,23 @@
+// Copyright 2018-2022 the Deno authors. All rights reserved. MIT license.
+
+use deno_runtime::deno_napi::*;
+use std::cell::Cell;
+
+unsafe fn get_backing_store_slice(
+ backing_store: &mut v8::SharedRef<v8::BackingStore>,
+ byte_offset: usize,
+ byte_length: usize,
+) -> &mut [u8] {
+ let cells: *const [Cell<u8>] =
+ &backing_store[byte_offset..byte_offset + byte_length];
+ let mut bytes = cells as *mut [u8];
+ &mut *bytes
+}
+
+pub fn get_array_buffer_ptr(ab: v8::Local<v8::ArrayBuffer>) -> *mut u8 {
+ let mut backing_store = ab.get_backing_store();
+ let byte_length = ab.byte_length();
+ let mut slice =
+ unsafe { get_backing_store_slice(&mut backing_store, 0, byte_length) };
+ slice.as_mut_ptr()
+}
diff --git a/cli/napi_sym/Cargo.toml b/cli/napi_sym/Cargo.toml
new file mode 100644
index 000000000..797ddf0bf
--- /dev/null
+++ b/cli/napi_sym/Cargo.toml
@@ -0,0 +1,18 @@
+[package]
+name = "napi_sym"
+version = "0.1.0"
+edition = "2021"
+license = "MIT"
+readme = "../README.md"
+description = "proc macro for writing N-API symbols"
+
+[lib]
+path = "./lib.rs"
+proc-macro = true
+
+[dependencies]
+proc-macro2 = "1"
+quote = "1"
+serde = { version = "1", features = ["derive"] }
+serde_json = "1"
+syn = { version = "1", features = ["full", "extra-traits"] }
diff --git a/cli/napi_sym/lib.rs b/cli/napi_sym/lib.rs
new file mode 100644
index 000000000..caef3da65
--- /dev/null
+++ b/cli/napi_sym/lib.rs
@@ -0,0 +1,46 @@
+// Copyright 2018-2022 the Deno authors. All rights reserved. MIT license.
+
+use proc_macro::TokenStream;
+use quote::quote;
+use serde::Deserialize;
+
+static NAPI_EXPORTS: &str =
+ include_str!("../../tools/napi/symbol_exports.json");
+
+#[derive(Deserialize)]
+struct SymbolExports {
+ pub symbols: Vec<String>,
+}
+
+#[proc_macro_attribute]
+pub fn napi_sym(_attr: TokenStream, item: TokenStream) -> TokenStream {
+ let func = syn::parse::<syn::ItemFn>(item).expect("expected a function");
+
+ let exports: SymbolExports =
+ serde_json::from_str(NAPI_EXPORTS).expect("failed to parse exports");
+ let name = &func.sig.ident;
+ assert!(
+ exports.symbols.contains(&name.to_string()),
+ "tools/napi/symbol_exports.json is out of sync!"
+ );
+
+ let block = &func.block;
+ let inputs = &func.sig.inputs;
+ let output = &func.sig.output;
+ let ret_ty = match output {
+ syn::ReturnType::Default => panic!("expected a return type"),
+ syn::ReturnType::Type(_, ty) => quote! { #ty },
+ };
+ TokenStream::from(quote! {
+ // SAFETY: it's an NAPI function.
+ #[no_mangle]
+ pub unsafe extern "C" fn #name(#inputs) -> napi_status {
+ let mut inner = || -> #ret_ty {
+ #block
+ };
+ inner()
+ .map(|_| napi_ok)
+ .unwrap_or_else(|e| e.into())
+ }
+ })
+}
diff --git a/core/runtime.rs b/core/runtime.rs
index df2cd9a97..7c70d4366 100644
--- a/core/runtime.rs
+++ b/core/runtime.rs
@@ -342,6 +342,18 @@ impl JsRuntime {
// V8 takes ownership of external_references.
let refs: &'static v8::ExternalReferences = Box::leak(Box::new(refs));
let global_context;
+
+ let align = std::mem::align_of::<usize>();
+ let layout = std::alloc::Layout::from_size_align(
+ std::mem::size_of::<*mut v8::OwnedIsolate>(),
+ align,
+ )
+ .unwrap();
+ assert!(layout.size() > 0);
+ let isolate_ptr: *mut v8::OwnedIsolate =
+ // SAFETY: we just asserted that layout has non-0 size.
+ unsafe { std::alloc::alloc(layout) as *mut _ };
+
let (mut isolate, maybe_snapshot_creator) = if options.will_snapshot {
// TODO(ry) Support loading snapshots before snapshotting.
assert!(options.startup_snapshot.is_none());
@@ -352,6 +364,12 @@ impl JsRuntime {
let isolate = unsafe { creator.get_owned_isolate() };
let mut isolate = JsRuntime::setup_isolate(isolate);
{
+ // SAFETY: this is first use of `isolate_ptr` so we are sure we're
+ // not overwriting an existing pointer.
+ isolate = unsafe {
+ isolate_ptr.write(isolate);
+ isolate_ptr.read()
+ };
let scope = &mut v8::HandleScope::new(&mut isolate);
let context = bindings::initialize_context(scope, &op_ctxs, false);
global_context = v8::Global::new(scope, context);
@@ -383,15 +401,23 @@ impl JsRuntime {
let isolate = v8::Isolate::new(params);
let mut isolate = JsRuntime::setup_isolate(isolate);
{
+ // SAFETY: this is first use of `isolate_ptr` so we are sure we're
+ // not overwriting an existing pointer.
+ isolate = unsafe {
+ isolate_ptr.write(isolate);
+ isolate_ptr.read()
+ };
let scope = &mut v8::HandleScope::new(&mut isolate);
let context =
bindings::initialize_context(scope, &op_ctxs, snapshot_loaded);
global_context = v8::Global::new(scope, context);
}
+
(isolate, None)
};
+ op_state.borrow_mut().put(isolate_ptr);
let inspector =
JsRuntimeInspector::new(&mut isolate, global_context.clone());
@@ -955,7 +981,6 @@ impl JsRuntime {
self.drain_macrotasks()?;
self.check_promise_exceptions()?;
}
-
// Dynamic module loading - ie. modules loaded using "import()"
{
// Run in a loop so that dynamic imports that only depend on another
diff --git a/ext/napi/Cargo.toml b/ext/napi/Cargo.toml
new file mode 100644
index 000000000..53d235c72
--- /dev/null
+++ b/ext/napi/Cargo.toml
@@ -0,0 +1,18 @@
+# Copyright 2018-2022 the Deno authors. All rights reserved. MIT license.
+
+[package]
+name = "deno_napi"
+version = "0.1.0"
+authors = ["the Deno authors"]
+edition = "2021"
+license = "MIT"
+readme = "README.md"
+repository = "https://github.com/denoland/deno"
+description = "NAPI implementation for Deno"
+
+[lib]
+path = "lib.rs"
+
+[dependencies]
+deno_core = { version = "0.153.0", path = "../../core" }
+libloading = { version = "0.7" }
diff --git a/ext/napi/function.rs b/ext/napi/function.rs
new file mode 100644
index 000000000..853283b08
--- /dev/null
+++ b/ext/napi/function.rs
@@ -0,0 +1,103 @@
+// Copyright 2018-2022 the Deno authors. All rights reserved. MIT license.
+use crate::*;
+
+#[repr(C)]
+#[derive(Debug)]
+pub struct CallbackInfo {
+ pub env: napi_env,
+ pub cb: napi_callback,
+ pub cb_info: napi_callback_info,
+ pub args: *const c_void,
+}
+
+impl CallbackInfo {
+ #[inline]
+ pub fn new_raw(
+ env: napi_env,
+ cb: napi_callback,
+ cb_info: napi_callback_info,
+ ) -> *mut Self {
+ Box::into_raw(Box::new(Self {
+ env,
+ cb,
+ cb_info,
+ args: std::ptr::null(),
+ }))
+ }
+}
+
+extern "C" fn call_fn(info: *const v8::FunctionCallbackInfo) {
+ let args =
+ unsafe { v8::FunctionCallbackArguments::from_function_callback_info(info) };
+ let mut rv = unsafe { v8::ReturnValue::from_function_callback_info(info) };
+ // SAFETY: create_function guarantees that the data is a CallbackInfo external.
+ let info_ptr: *mut CallbackInfo = unsafe {
+ let external_value =
+ v8::Local::<v8::External>::cast(args.data().unwrap_unchecked());
+ external_value.value() as _
+ };
+
+ // SAFETY: pointer from Box::into_raw.
+ let mut info = unsafe { &mut *info_ptr };
+ info.args = &args as *const _ as *const c_void;
+
+ if let Some(f) = info.cb {
+ // SAFETY: calling user provided function pointer.
+ let value = unsafe { f(info.env, info_ptr as *mut _) };
+ // SAFETY: napi_value is reprsented as v8::Local<v8::Value> internally.
+ rv.set(unsafe { transmute::<napi_value, v8::Local<v8::Value>>(value) });
+ }
+}
+
+#[allow(clippy::not_unsafe_ptr_arg_deref)]
+pub fn create_function<'a>(
+ env_ptr: *mut Env,
+ name: Option<&str>,
+ cb: napi_callback,
+ cb_info: napi_callback_info,
+) -> v8::Local<'a, v8::Function> {
+ let env: &mut Env = unsafe { &mut *env_ptr };
+ let scope = &mut env.scope();
+
+ let external = v8::External::new(
+ scope,
+ CallbackInfo::new_raw(env_ptr as _, cb, cb_info) as *mut _,
+ );
+ let function = v8::Function::builder_raw(call_fn)
+ .data(external.into())
+ .build(scope)
+ .unwrap();
+
+ if let Some(name) = name {
+ let v8str = v8::String::new(scope, name).unwrap();
+ function.set_name(v8str);
+ }
+
+ function
+}
+
+#[allow(clippy::not_unsafe_ptr_arg_deref)]
+pub fn create_function_template<'a>(
+ env_ptr: *mut Env,
+ name: Option<&str>,
+ cb: napi_callback,
+ cb_info: napi_callback_info,
+) -> v8::Local<'a, v8::FunctionTemplate> {
+ let env: &mut Env = unsafe { &mut *env_ptr };
+ let scope = &mut env.scope();
+
+ let external = v8::External::new(
+ scope,
+ CallbackInfo::new_raw(env_ptr as _, cb, cb_info) as *mut _,
+ );
+ let function = v8::FunctionTemplate::builder_raw(call_fn)
+ .data(external.into())
+ .build(scope);
+
+ if let Some(name) = name {
+ let v8str = v8::String::new(scope, name).unwrap();
+ function.set_class_name(v8str);
+ }
+
+ function
+}
diff --git a/ext/napi/lib.rs b/ext/napi/lib.rs
new file mode 100644
index 000000000..5bcb29643
--- /dev/null
+++ b/ext/napi/lib.rs
@@ -0,0 +1,607 @@
+// Copyright 2018-2022 the Deno authors. All rights reserved. MIT license.
+
+#![allow(non_camel_case_types)]
+#![allow(non_upper_case_globals)]
+#![allow(clippy::undocumented_unsafe_blocks)]
+#![deny(clippy::missing_safety_doc)]
+
+use core::ptr::NonNull;
+use deno_core::error::type_error;
+use deno_core::error::AnyError;
+use deno_core::futures::channel::mpsc;
+use deno_core::futures::StreamExt;
+use deno_core::op;
+use deno_core::serde_v8;
+pub use deno_core::v8;
+use deno_core::Extension;
+use deno_core::OpState;
+use std::cell::RefCell;
+pub use std::ffi::CStr;
+use std::ffi::CString;
+pub use std::mem::transmute;
+pub use std::os::raw::c_char;
+pub use std::os::raw::c_void;
+use std::path::Path;
+use std::path::PathBuf;
+pub use std::ptr;
+use std::task::Poll;
+use std::thread_local;
+
+#[cfg(unix)]
+use libloading::os::unix::*;
+
+#[cfg(windows)]
+use libloading::os::windows::*;
+
+pub mod function;
+
+pub type napi_status = i32;
+pub type napi_env = *mut c_void;
+pub type napi_value = *mut c_void;
+pub type napi_callback_info = *mut c_void;
+pub type napi_deferred = *mut c_void;
+pub type napi_ref = *mut c_void;
+pub type napi_threadsafe_function = *mut c_void;
+pub type napi_handle_scope = *mut c_void;
+pub type napi_callback_scope = *mut c_void;
+pub type napi_escapable_handle_scope = *mut c_void;
+pub type napi_async_cleanup_hook_handle = *mut c_void;
+pub type napi_async_work = *mut c_void;
+
+pub const napi_ok: napi_status = 0;
+pub const napi_invalid_arg: napi_status = 1;
+pub const napi_object_expected: napi_status = 2;
+pub const napi_string_expected: napi_status = 3;
+pub const napi_name_expected: napi_status = 4;
+pub const napi_function_expected: napi_status = 5;
+pub const napi_number_expected: napi_status = 6;
+pub const napi_boolean_expected: napi_status = 7;
+pub const napi_array_expected: napi_status = 8;
+pub const napi_generic_failure: napi_status = 9;
+pub const napi_pending_exception: napi_status = 10;
+pub const napi_cancelled: napi_status = 11;
+pub const napi_escape_called_twice: napi_status = 12;
+pub const napi_handle_scope_mismatch: napi_status = 13;
+pub const napi_callback_scope_mismatch: napi_status = 14;
+pub const napi_queue_full: napi_status = 15;
+pub const napi_closing: napi_status = 16;
+pub const napi_bigint_expected: napi_status = 17;
+pub const napi_date_expected: napi_status = 18;
+pub const napi_arraybuffer_expected: napi_status = 19;
+pub const napi_detachable_arraybuffer_expected: napi_status = 20;
+pub const napi_would_deadlock: napi_status = 21;
+
+thread_local! {
+ pub static MODULE: RefCell<Option<*const NapiModule>> = RefCell::new(None);
+ pub static ASYNC_WORK_SENDER: RefCell<Option<mpsc::UnboundedSender<PendingNapiAsyncWork>>> = RefCell::new(None);
+ pub static THREAD_SAFE_FN_SENDER: RefCell<Option<mpsc::UnboundedSender<ThreadSafeFunctionStatus>>> = RefCell::new(None);
+}
+
+type napi_addon_register_func =
+ extern "C" fn(env: napi_env, exports: napi_value) -> napi_value;
+
+#[repr(C)]
+#[derive(Debug, Clone)]
+pub struct NapiModule {
+ pub nm_version: i32,
+ pub nm_flags: u32,
+ nm_filename: *const c_char,
+ pub nm_register_func: napi_addon_register_func,
+ nm_modname: *const c_char,
+ nm_priv: *mut c_void,
+ reserved: [*mut c_void; 4],
+}
+
+#[derive(Debug, Clone, PartialEq, Eq)]
+pub enum Error {
+ InvalidArg,
+ ObjectExpected,
+ StringExpected,
+ NameExpected,
+ FunctionExpected,
+ NumberExpected,
+ BooleanExpected,
+ ArrayExpected,
+ GenericFailure,
+ PendingException,
+ Cancelled,
+ EscapeCalledTwice,
+ HandleScopeMismatch,
+ CallbackScopeMismatch,
+ QueueFull,
+ Closing,
+ BigIntExpected,
+ DateExpected,
+ ArrayBufferExpected,
+ DetachableArraybufferExpected,
+ WouldDeadlock,
+}
+
+pub type Result = std::result::Result<(), Error>;
+
+impl From<Error> for napi_status {
+ fn from(error: Error) -> Self {
+ match error {
+ Error::InvalidArg => napi_invalid_arg,
+ Error::ObjectExpected => napi_object_expected,
+ Error::StringExpected => napi_string_expected,
+ Error::NameExpected => napi_name_expected,
+ Error::FunctionExpected => napi_function_expected,
+ Error::NumberExpected => napi_number_expected,
+ Error::BooleanExpected => napi_boolean_expected,
+ Error::ArrayExpected => napi_array_expected,
+ Error::GenericFailure => napi_generic_failure,
+ Error::PendingException => napi_pending_exception,
+ Error::Cancelled => napi_cancelled,
+ Error::EscapeCalledTwice => napi_escape_called_twice,
+ Error::HandleScopeMismatch => napi_handle_scope_mismatch,
+ Error::CallbackScopeMismatch => napi_callback_scope_mismatch,
+ Error::QueueFull => napi_queue_full,
+ Error::Closing => napi_closing,
+ Error::BigIntExpected => napi_bigint_expected,
+ Error::DateExpected => napi_date_expected,
+ Error::ArrayBufferExpected => napi_arraybuffer_expected,
+ Error::DetachableArraybufferExpected => {
+ napi_detachable_arraybuffer_expected
+ }
+ Error::WouldDeadlock => napi_would_deadlock,
+ }
+ }
+}
+
+pub type napi_valuetype = i32;
+
+pub const napi_undefined: napi_valuetype = 0;
+pub const napi_null: napi_valuetype = 1;
+pub const napi_boolean: napi_valuetype = 2;
+pub const napi_number: napi_valuetype = 3;
+pub const napi_string: napi_valuetype = 4;
+pub const napi_symbol: napi_valuetype = 5;
+pub const napi_object: napi_valuetype = 6;
+pub const napi_function: napi_valuetype = 7;
+pub const napi_external: napi_valuetype = 8;
+pub const napi_bigint: napi_valuetype = 9;
+
+pub type napi_threadsafe_function_release_mode = i32;
+
+pub const napi_tsfn_release: napi_threadsafe_function_release_mode = 0;
+pub const napi_tsfn_abortext: napi_threadsafe_function_release_mode = 1;
+
+pub type napi_threadsafe_function_call_mode = i32;
+
+pub const napi_tsfn_nonblocking: napi_threadsafe_function_call_mode = 0;
+pub const napi_tsfn_blocking: napi_threadsafe_function_call_mode = 1;
+
+pub type napi_key_collection_mode = i32;
+
+pub const napi_key_include_prototypes: napi_key_collection_mode = 0;
+pub const napi_key_own_only: napi_key_collection_mode = 1;
+
+pub type napi_key_filter = i32;
+
+pub const napi_key_all_properties: napi_key_filter = 0;
+pub const napi_key_writable: napi_key_filter = 1;
+pub const napi_key_enumerable: napi_key_filter = 1 << 1;
+pub const napi_key_configurable: napi_key_filter = 1 << 2;
+pub const napi_key_skip_strings: napi_key_filter = 1 << 3;
+pub const napi_key_skip_symbols: napi_key_filter = 1 << 4;
+
+pub type napi_key_conversion = i32;
+
+pub const napi_key_keep_numbers: napi_key_conversion = 0;
+pub const napi_key_numbers_to_strings: napi_key_conversion = 1;
+
+pub type napi_typedarray_type = i32;
+
+pub const napi_int8_array: napi_typedarray_type = 0;
+pub const napi_uint8_array: napi_typedarray_type = 1;
+pub const napi_uint8_clamped_array: napi_typedarray_type = 2;
+pub const napi_int16_array: napi_typedarray_type = 3;
+pub const napi_uint16_array: napi_typedarray_type = 4;
+pub const napi_int32_array: napi_typedarray_type = 5;
+pub const napi_uint32_array: napi_typedarray_type = 6;
+pub const napi_float32_array: napi_typedarray_type = 7;
+pub const napi_float64_array: napi_typedarray_type = 8;
+pub const napi_bigint64_array: napi_typedarray_type = 9;
+pub const napi_biguint64_array: napi_typedarray_type = 10;
+
+pub struct napi_type_tag {
+ pub lower: u64,
+ pub upper: u64,
+}
+
+pub type napi_callback = Option<
+ unsafe extern "C" fn(env: napi_env, info: napi_callback_info) -> napi_value,
+>;
+
+pub type napi_finalize = unsafe extern "C" fn(
+ env: napi_env,
+ data: *mut c_void,
+ finalize_hint: *mut c_void,
+);
+
+pub type napi_async_execute_callback =
+ unsafe extern "C" fn(env: napi_env, data: *mut c_void);
+
+pub type napi_async_complete_callback =
+ unsafe extern "C" fn(env: napi_env, status: napi_status, data: *mut c_void);
+
+pub type napi_threadsafe_function_call_js = unsafe extern "C" fn(
+ env: napi_env,
+ js_callback: napi_value,
+ context: *mut c_void,
+ data: *mut c_void,
+);
+
+pub type napi_async_cleanup_hook =
+ unsafe extern "C" fn(env: napi_env, data: *mut c_void);
+
+pub type napi_property_attributes = i32;
+
+pub const napi_default: napi_property_attributes = 0;
+pub const napi_writable: napi_property_attributes = 1 << 0;
+pub const napi_enumerable: napi_property_attributes = 1 << 1;
+pub const napi_configurable: napi_property_attributes = 1 << 2;
+pub const napi_static: napi_property_attributes = 1 << 10;
+pub const napi_default_method: napi_property_attributes =
+ napi_writable | napi_configurable;
+pub const napi_default_jsproperty: napi_property_attributes =
+ napi_enumerable | napi_configurable | napi_writable;
+
+#[repr(C)]
+#[derive(Copy, Clone, Debug)]
+pub struct napi_property_descriptor {
+ pub utf8name: *const c_char,
+ pub name: napi_value,
+ pub method: napi_callback,
+ pub getter: napi_callback,
+ pub setter: napi_callback,
+ pub value: napi_value,
+ pub attributes: napi_property_attributes,
+ pub data: *mut c_void,
+}
+
+#[repr(C)]
+#[derive(Debug)]
+pub struct napi_extended_error_info {
+ pub error_message: *const c_char,
+ pub engine_reserved: *mut c_void,
+ pub engine_error_code: i32,
+ pub status_code: napi_status,
+}
+
+#[repr(C)]
+#[derive(Debug)]
+pub struct napi_node_version {
+ pub major: u32,
+ pub minor: u32,
+ pub patch: u32,
+ pub release: *const c_char,
+}
+
+pub type PendingNapiAsyncWork = Box<dyn FnOnce()>;
+
+pub struct NapiState {
+ // Async tasks.
+ pub pending_async_work: Vec<PendingNapiAsyncWork>,
+ pub async_work_sender: mpsc::UnboundedSender<PendingNapiAsyncWork>,
+ pub async_work_receiver: mpsc::UnboundedReceiver<PendingNapiAsyncWork>,
+ // Thread safe functions.
+ pub active_threadsafe_functions: usize,
+ pub threadsafe_function_receiver:
+ mpsc::UnboundedReceiver<ThreadSafeFunctionStatus>,
+ pub threadsafe_function_sender:
+ mpsc::UnboundedSender<ThreadSafeFunctionStatus>,
+}
+
+#[repr(C)]
+#[derive(Debug)]
+/// Env that is shared between all contexts in same native module.
+pub struct EnvShared {
+ pub instance_data: *mut c_void,
+ pub data_finalize: Option<napi_finalize>,
+ pub data_finalize_hint: *mut c_void,
+ pub napi_wrap: v8::Global<v8::Private>,
+ pub finalize: Option<napi_finalize>,
+ pub finalize_hint: *mut c_void,
+ pub filename: *const c_char,
+}
+
+impl EnvShared {
+ pub fn new(napi_wrap: v8::Global<v8::Private>) -> Self {
+ Self {
+ instance_data: std::ptr::null_mut(),
+ data_finalize: None,
+ data_finalize_hint: std::ptr::null_mut(),
+ napi_wrap,
+ finalize: None,
+ finalize_hint: std::ptr::null_mut(),
+ filename: std::ptr::null(),
+ }
+ }
+}
+
+pub enum ThreadSafeFunctionStatus {
+ Alive,
+ Dead,
+}
+
+#[repr(C)]
+pub struct Env {
+ context: NonNull<v8::Context>,
+ pub isolate_ptr: *mut v8::OwnedIsolate,
+ pub open_handle_scopes: usize,
+ pub shared: *mut EnvShared,
+ pub async_work_sender: mpsc::UnboundedSender<PendingNapiAsyncWork>,
+ pub threadsafe_function_sender:
+ mpsc::UnboundedSender<ThreadSafeFunctionStatus>,
+}
+
+unsafe impl Send for Env {}
+unsafe impl Sync for Env {}
+
+impl Env {
+ pub fn new(
+ isolate_ptr: *mut v8::OwnedIsolate,
+ context: v8::Global<v8::Context>,
+ sender: mpsc::UnboundedSender<PendingNapiAsyncWork>,
+ threadsafe_function_sender: mpsc::UnboundedSender<ThreadSafeFunctionStatus>,
+ ) -> Self {
+ let sc = sender.clone();
+ ASYNC_WORK_SENDER.with(|s| {
+ s.replace(Some(sc));
+ });
+ let ts = threadsafe_function_sender.clone();
+ THREAD_SAFE_FN_SENDER.with(|s| {
+ s.replace(Some(ts));
+ });
+
+ Self {
+ isolate_ptr,
+ context: context.into_raw(),
+ shared: std::ptr::null_mut(),
+ open_handle_scopes: 0,
+ async_work_sender: sender,
+ threadsafe_function_sender,
+ }
+ }
+
+ pub fn shared(&self) -> &EnvShared {
+ // SAFETY: the lifetime of `EnvShared` always exceeds the lifetime of `Env`.
+ unsafe { &*self.shared }
+ }
+
+ pub fn shared_mut(&mut self) -> &mut EnvShared {
+ // SAFETY: the lifetime of `EnvShared` always exceeds the lifetime of `Env`.
+ unsafe { &mut *self.shared }
+ }
+
+ pub fn add_async_work(&mut self, async_work: PendingNapiAsyncWork) {
+ self.async_work_sender.unbounded_send(async_work).unwrap();
+ }
+
+ #[inline]
+ pub fn isolate(&mut self) -> &mut v8::OwnedIsolate {
+ // SAFETY: Lifetime of `OwnedIsolate` is longer than `Env`.
+ unsafe { &mut *self.isolate_ptr }
+ }
+
+ #[inline]
+ pub fn scope(&self) -> v8::CallbackScope {
+ // SAFETY: `v8::Local` is always non-null pointer; the `HandleScope` is
+ // already on the stack, but we don't have access to it.
+ let context = unsafe {
+ transmute::<NonNull<v8::Context>, v8::Local<v8::Context>>(self.context)
+ };
+ // SAFETY: there must be a `HandleScope` on the stack, this is ensured because
+ // we are in a V8 callback or the module has already opened a `HandleScope`
+ // using `napi_open_handle_scope`.
+ unsafe { v8::CallbackScope::new(context) }
+ }
+}
+
+pub fn init<P: NapiPermissions + 'static>(unstable: bool) -> Extension {
+ Extension::builder()
+ .ops(vec![op_napi_open::decl::<P>()])
+ .event_loop_middleware(|op_state_rc, cx| {
+ // `work` can call back into the runtime. It can also schedule an async task
+ // but we don't know that now. We need to make the runtime re-poll to make
+ // sure no pending NAPI tasks exist.
+ let mut maybe_scheduling = false;
+
+ {
+ let mut op_state = op_state_rc.borrow_mut();
+ let napi_state = op_state.borrow_mut::<NapiState>();
+
+ while let Poll::Ready(Some(async_work_fut)) =
+ napi_state.async_work_receiver.poll_next_unpin(cx)
+ {
+ napi_state.pending_async_work.push(async_work_fut);
+ }
+
+ while let Poll::Ready(Some(tsfn_status)) =
+ napi_state.threadsafe_function_receiver.poll_next_unpin(cx)
+ {
+ match tsfn_status {
+ ThreadSafeFunctionStatus::Alive => {
+ napi_state.active_threadsafe_functions += 1
+ }
+ ThreadSafeFunctionStatus::Dead => {
+ napi_state.active_threadsafe_functions -= 1
+ }
+ };
+ }
+
+ if napi_state.active_threadsafe_functions > 0 {
+ maybe_scheduling = true;
+ }
+ }
+
+ loop {
+ let maybe_work = {
+ let mut op_state = op_state_rc.borrow_mut();
+ let napi_state = op_state.borrow_mut::<NapiState>();
+ napi_state.pending_async_work.pop()
+ };
+
+ if let Some(work) = maybe_work {
+ work();
+ maybe_scheduling = true;
+ } else {
+ break;
+ }
+ }
+
+ maybe_scheduling
+ })
+ .state(move |state| {
+ let (async_work_sender, async_work_receiver) =
+ mpsc::unbounded::<PendingNapiAsyncWork>();
+ let (threadsafe_function_sender, threadsafe_function_receiver) =
+ mpsc::unbounded::<ThreadSafeFunctionStatus>();
+ state.put(NapiState {
+ pending_async_work: Vec::new(),
+ async_work_sender,
+ async_work_receiver,
+ threadsafe_function_sender,
+ threadsafe_function_receiver,
+ active_threadsafe_functions: 0,
+ });
+ state.put(Unstable(unstable));
+ Ok(())
+ })
+ .build()
+}
+
+pub trait NapiPermissions {
+ fn check(&mut self, path: Option<&Path>)
+ -> std::result::Result<(), AnyError>;
+}
+
+pub struct Unstable(pub bool);
+
+fn check_unstable(state: &OpState) {
+ let unstable = state.borrow::<Unstable>();
+
+ if !unstable.0 {
+ eprintln!("Unstable API 'node-api'. The --unstable flag must be provided.");
+ std::process::exit(70);
+ }
+}
+
+#[op(v8)]
+fn op_napi_open<NP, 'scope>(
+ scope: &mut v8::HandleScope<'scope>,
+ op_state: &mut OpState,
+ path: String,
+) -> std::result::Result<serde_v8::Value<'scope>, AnyError>
+where
+ NP: NapiPermissions + 'static,
+{
+ check_unstable(op_state);
+ let permissions = op_state.borrow_mut::<NP>();
+ permissions.check(Some(&PathBuf::from(&path)))?;
+
+ let (async_work_sender, tsfn_sender, isolate_ptr) = {
+ let napi_state = op_state.borrow::<NapiState>();
+ let isolate_ptr = op_state.borrow::<*mut v8::OwnedIsolate>();
+ (
+ napi_state.async_work_sender.clone(),
+ napi_state.threadsafe_function_sender.clone(),
+ *isolate_ptr,
+ )
+ };
+
+ let napi_wrap_name = v8::String::new(scope, "napi_wrap").unwrap();
+ let napi_wrap = v8::Private::new(scope, Some(napi_wrap_name));
+ let napi_wrap = v8::Global::new(scope, napi_wrap);
+
+ // The `module.exports` object.
+ let exports = v8::Object::new(scope);
+
+ let mut env_shared = EnvShared::new(napi_wrap);
+ let cstr = CString::new(path.clone()).unwrap();
+ env_shared.filename = cstr.as_ptr();
+ std::mem::forget(cstr);
+
+ let ctx = scope.get_current_context();
+ let mut env = Env::new(
+ isolate_ptr,
+ v8::Global::new(scope, ctx),
+ async_work_sender,
+ tsfn_sender,
+ );
+ env.shared = Box::into_raw(Box::new(env_shared));
+ let env_ptr = Box::into_raw(Box::new(env)) as _;
+
+ #[cfg(unix)]
+ let flags = RTLD_LAZY;
+ #[cfg(not(unix))]
+ let flags = 0x00000008;
+
+ // SAFETY: opening a DLL calls dlopen
+ #[cfg(unix)]
+ let library = match unsafe { Library::open(Some(&path), flags) } {
+ Ok(lib) => lib,
+ Err(e) => return Err(type_error(e.to_string())),
+ };
+
+ // SAFETY: opening a DLL calls dlopen
+ #[cfg(not(unix))]
+ let library = match unsafe { Library::load_with_flags(&path, flags) } {
+ Ok(lib) => lib,
+ Err(e) => return Err(type_error(e.to_string())),
+ };
+
+ MODULE.with(|cell| {
+ let slot = *cell.borrow();
+ let obj = match slot {
+ Some(nm) => {
+ // SAFETY: napi_register_module guarantees that `nm` is valid.
+ let nm = unsafe { &*nm };
+ assert_eq!(nm.nm_version, 1);
+ // SAFETY: we are going blind, calling the register function on the other side.
+ let exports = unsafe {
+ (nm.nm_register_func)(
+ env_ptr,
+ std::mem::transmute::<v8::Local<v8::Value>, napi_value>(
+ exports.into(),
+ ),
+ )
+ };
+
+ // SAFETY: v8::Local is a pointer to a value and napi_value is also a pointer
+ // to a value, they have the same layout
+ let exports = unsafe {
+ std::mem::transmute::<napi_value, v8::Local<v8::Value>>(exports)
+ };
+ Ok(serde_v8::Value { v8_value: exports })
+ }
+ None => {
+ // Initializer callback.
+ // SAFETY: we are going blind, calling the register function on the other side.
+ unsafe {
+ let init = library
+ .get::<unsafe extern "C" fn(
+ env: napi_env,
+ exports: napi_value,
+ ) -> napi_value>(b"napi_register_module_v1")
+ .expect("napi_register_module_v1 not found");
+ init(
+ env_ptr,
+ std::mem::transmute::<v8::Local<v8::Value>, napi_value>(
+ exports.into(),
+ ),
+ )
+ };
+ Ok(serde_v8::Value {
+ v8_value: exports.into(),
+ })
+ }
+ };
+ // NAPI addons can't be unloaded, so we're going to "forget" the library
+ // object so it lives till the program exit.
+ std::mem::forget(library);
+ obj
+ })
+}
diff --git a/ext/node/02_require.js b/ext/node/02_require.js
index 04cc5ebb3..e55b788e9 100644
--- a/ext/node/02_require.js
+++ b/ext/node/02_require.js
@@ -773,7 +773,7 @@
// Native extension for .node
Module._extensions[".node"] = function (module, filename) {
- throw new Error("not implemented loading .node files");
+ module.exports = ops.op_napi_open(filename);
};
function createRequireFromPath(filename) {
diff --git a/runtime/Cargo.toml b/runtime/Cargo.toml
index 467623933..a37f344ac 100644
--- a/runtime/Cargo.toml
+++ b/runtime/Cargo.toml
@@ -40,6 +40,7 @@ deno_webgpu = { version = "0.72.0", path = "../ext/webgpu" }
deno_webidl = { version = "0.71.0", path = "../ext/webidl" }
deno_websocket = { version = "0.76.0", path = "../ext/websocket" }
deno_webstorage = { version = "0.66.0", path = "../ext/webstorage" }
+deno_napi = { version = "0.1.0", path = "../ext/napi" }
lzzzz = '1.0'
@@ -57,6 +58,7 @@ deno_fetch = { version = "0.94.0", path = "../ext/fetch" }
deno_ffi = { version = "0.58.0", path = "../ext/ffi" }
deno_flash = { version = "0.7.0", path = "../ext/flash" }
deno_http = { version = "0.65.0", path = "../ext/http" }
+deno_napi = { version = "0.1.0", path = "../ext/napi" }
deno_net = { version = "0.63.0", path = "../ext/net" }
deno_node = { version = "0.8.0", path = "../ext/node" }
deno_tls = { version = "0.58.0", path = "../ext/tls" }
diff --git a/runtime/build.rs b/runtime/build.rs
index 55a89fb8b..23411fc77 100644
--- a/runtime/build.rs
+++ b/runtime/build.rs
@@ -120,6 +120,15 @@ mod not_docs {
}
}
+ impl deno_napi::NapiPermissions for Permissions {
+ fn check(
+ &mut self,
+ _path: Option<&Path>,
+ ) -> Result<(), deno_core::error::AnyError> {
+ unreachable!("snapshotting!")
+ }
+ }
+
impl deno_flash::FlashPermissions for Permissions {
fn check_net<T: AsRef<str>>(
&mut self,
@@ -191,6 +200,7 @@ mod not_docs {
None, false, // No --unstable.
None,
),
+ deno_napi::init::<Permissions>(false),
deno_http::init(),
deno_flash::init::<Permissions>(false), // No --unstable
];
diff --git a/runtime/lib.rs b/runtime/lib.rs
index 99813e3d8..d0d27b71c 100644
--- a/runtime/lib.rs
+++ b/runtime/lib.rs
@@ -8,6 +8,7 @@ pub use deno_crypto;
pub use deno_fetch;
pub use deno_ffi;
pub use deno_http;
+pub use deno_napi;
pub use deno_net;
pub use deno_node;
pub use deno_tls;
diff --git a/runtime/permissions.rs b/runtime/permissions.rs
index 1568410b3..ec2f146ae 100644
--- a/runtime/permissions.rs
+++ b/runtime/permissions.rs
@@ -1656,6 +1656,14 @@ impl deno_websocket::WebSocketPermissions for Permissions {
}
}
+// NOTE(bartlomieju): for now, NAPI uses `--allow-ffi` flag, but that might
+// change in the future.
+impl deno_napi::NapiPermissions for Permissions {
+ fn check(&mut self, path: Option<&Path>) -> Result<(), AnyError> {
+ self.ffi.check(path)
+ }
+}
+
impl deno_ffi::FfiPermissions for Permissions {
fn check(&mut self, path: Option<&Path>) -> Result<(), AnyError> {
self.ffi.check(path)
diff --git a/runtime/web_worker.rs b/runtime/web_worker.rs
index 09a631916..f08127b22 100644
--- a/runtime/web_worker.rs
+++ b/runtime/web_worker.rs
@@ -431,6 +431,7 @@ impl WebWorker {
unstable,
options.unsafely_ignore_certificate_errors.clone(),
),
+ deno_napi::init::<Permissions>(unstable),
deno_node::init::<Permissions>(unstable, options.npm_resolver),
ops::os::init_for_worker(),
ops::permissions::init(),
diff --git a/runtime/worker.rs b/runtime/worker.rs
index 3ac3654e2..9b2755939 100644
--- a/runtime/worker.rs
+++ b/runtime/worker.rs
@@ -189,6 +189,7 @@ impl MainWorker {
unstable,
options.unsafely_ignore_certificate_errors.clone(),
),
+ deno_napi::init::<Permissions>(unstable),
deno_node::init::<Permissions>(unstable, options.npm_resolver),
ops::os::init(exit_code.clone()),
ops::permissions::init(),
diff --git a/test_napi/.gitignore b/test_napi/.gitignore
new file mode 100644
index 000000000..6fdcc4a66
--- /dev/null
+++ b/test_napi/.gitignore
@@ -0,0 +1,4 @@
+package-lock.json
+
+# Test generated artifacts
+.swc \ No newline at end of file
diff --git a/test_napi/Cargo.toml b/test_napi/Cargo.toml
new file mode 100644
index 000000000..09faa4175
--- /dev/null
+++ b/test_napi/Cargo.toml
@@ -0,0 +1,20 @@
+# Copyright 2018-2022 the Deno authors. All rights reserved. MIT license.
+
+[package]
+name = "test_napi"
+version = "0.1.0"
+authors = ["the deno authors"]
+edition = "2021"
+publish = false
+
+[lib]
+crate-type = ["cdylib"]
+
+[dependencies]
+napi-sys = { version = "2.2.2", default-features = false, features = ["napi4"] }
+
+[dev-dependencies]
+test_util = { path = "../test_util" }
+
+[build-dependencies]
+napi-build = "1"
diff --git a/test_napi/array_test.js b/test_napi/array_test.js
new file mode 100644
index 000000000..314ea5e50
--- /dev/null
+++ b/test_napi/array_test.js
@@ -0,0 +1,19 @@
+// Copyright 2018-2022 the Deno authors. All rights reserved. MIT license.
+
+import { assertEquals, loadTestLibrary } from "./common.js";
+
+const array = loadTestLibrary();
+
+Deno.test("napi array new", function () {
+ const e = [0, "Hello", {}];
+ const r = array.test_array_new(e);
+ assertEquals(typeof r, "object");
+ assertEquals(r.length, 3);
+ assertEquals(e, r);
+});
+
+Deno.test("napi array new with length", function () {
+ const r = array.test_array_new_with_length(100);
+ assertEquals(typeof r, "object");
+ assertEquals(r.length, 100);
+});
diff --git a/test_napi/async_test.js b/test_napi/async_test.js
new file mode 100644
index 000000000..ea1b714bd
--- /dev/null
+++ b/test_napi/async_test.js
@@ -0,0 +1,16 @@
+// Copyright 2018-2022 the Deno authors. All rights reserved. MIT license.
+
+import { assertEquals, loadTestLibrary } from "./common.js";
+
+const asyncTask = loadTestLibrary();
+
+Deno.test("napi async task schedule", async () => {
+ let called = false;
+ await new Promise((resolve) => {
+ asyncTask.test_async_work(() => {
+ called = true;
+ resolve();
+ });
+ });
+ assertEquals(called, true);
+});
diff --git a/test_napi/build.rs b/test_napi/build.rs
new file mode 100644
index 000000000..9d3021a0e
--- /dev/null
+++ b/test_napi/build.rs
@@ -0,0 +1,7 @@
+// Copyright 2018-2022 the Deno authors. All rights reserved. MIT license.
+
+extern crate napi_build;
+
+fn main() {
+ napi_build::setup();
+}
diff --git a/test_napi/callback_test.js b/test_napi/callback_test.js
new file mode 100644
index 000000000..debb1d7ba
--- /dev/null
+++ b/test_napi/callback_test.js
@@ -0,0 +1,38 @@
+// Copyright 2018-2022 the Deno authors. All rights reserved. MIT license.
+
+import { assertEquals, loadTestLibrary } from "./common.js";
+
+const callback = loadTestLibrary();
+
+Deno.test("napi callback run with args", function () {
+ const result = callback.test_callback_run((a, b) => a + b, [1, 2]);
+ assertEquals(result, 3);
+});
+
+Deno.test("napi callback run with args (no return)", function () {
+ const result = callback.test_callback_run(() => {}, []);
+ assertEquals(result, undefined);
+});
+
+Deno.test("napi callback run with args (extra arguments)", function () {
+ const result = callback.test_callback_run((a, b) => a + b, [
+ "Hello,",
+ " Deno!",
+ 1,
+ 2,
+ 3,
+ ]);
+ assertEquals(result, "Hello, Deno!");
+});
+
+Deno.test("napi callback run with args & recv", function () {
+ const result = callback.test_callback_run_with_recv(
+ function () {
+ assertEquals(this, 69);
+ return this;
+ },
+ [],
+ 69,
+ );
+ assertEquals(result, 69);
+});
diff --git a/test_napi/coerce_test.js b/test_napi/coerce_test.js
new file mode 100644
index 000000000..be0ee03e7
--- /dev/null
+++ b/test_napi/coerce_test.js
@@ -0,0 +1,74 @@
+// Copyright 2018-2022 the Deno authors. All rights reserved. MIT license.
+
+import { assertEquals, loadTestLibrary } from "./common.js";
+
+const coerce = loadTestLibrary();
+
+Deno.test("napi coerce bool", function () {
+ assertEquals(coerce.test_coerce_bool(true), true);
+ assertEquals(coerce.test_coerce_bool(false), false);
+ assertEquals(coerce.test_coerce_bool(0), false);
+ assertEquals(coerce.test_coerce_bool(69), true);
+ assertEquals(coerce.test_coerce_bool(Number.MAX_SAFE_INTEGER), true);
+ assertEquals(coerce.test_coerce_bool(new Array(10)), true);
+ assertEquals(coerce.test_coerce_bool("Hello, Deno!"), true);
+ assertEquals(coerce.test_coerce_bool(Symbol("[[test]]")), true);
+ assertEquals(coerce.test_coerce_bool({}), true);
+ assertEquals(coerce.test_coerce_bool(() => false), true);
+ assertEquals(coerce.test_coerce_bool(undefined), false);
+ assertEquals(coerce.test_coerce_bool(null), false);
+});
+
+Deno.test("napi coerce number", function () {
+ assertEquals(coerce.test_coerce_number(true), 1);
+ assertEquals(coerce.test_coerce_number(false), 0);
+ assertEquals(coerce.test_coerce_number(0), 0);
+ assertEquals(coerce.test_coerce_number(69), 69);
+ assertEquals(coerce.test_coerce_number(""), 0);
+ assertEquals(
+ coerce.test_coerce_number(Number.MAX_SAFE_INTEGER),
+ Number.MAX_SAFE_INTEGER,
+ );
+ assertEquals(coerce.test_coerce_number(new Array(10)), NaN);
+ assertEquals(coerce.test_coerce_number("Hello, Deno!"), NaN);
+ assertEquals(coerce.test_coerce_number({}), NaN);
+ assertEquals(coerce.test_coerce_number(() => false), NaN);
+ assertEquals(coerce.test_coerce_number(undefined), NaN);
+ assertEquals(coerce.test_coerce_number(null), 0);
+});
+
+Deno.test("napi coerce string", function () {
+ assertEquals(coerce.test_coerce_string(true), "true");
+ assertEquals(coerce.test_coerce_string(false), "false");
+ assertEquals(coerce.test_coerce_string(0), "0");
+ assertEquals(coerce.test_coerce_string(69), "69");
+ assertEquals(coerce.test_coerce_string(""), "");
+ assertEquals(
+ coerce.test_coerce_string(Number.MAX_SAFE_INTEGER),
+ "9007199254740991",
+ );
+ assertEquals(coerce.test_coerce_string(new Array(10)), ",,,,,,,,,");
+ assertEquals(coerce.test_coerce_string("Hello, Deno!"), "Hello, Deno!");
+ assertEquals(coerce.test_coerce_string({}), "[object Object]");
+ assertEquals(coerce.test_coerce_string(() => false), "() => false");
+ assertEquals(coerce.test_coerce_string(undefined), "undefined");
+ assertEquals(coerce.test_coerce_string(null), "null");
+});
+
+Deno.test("napi coerce object", function () {
+ assertEquals(coerce.test_coerce_object(true), new Boolean(true));
+ assertEquals(coerce.test_coerce_object(false), new Boolean(false));
+ assertEquals(coerce.test_coerce_object(0), new Number(0));
+ assertEquals(coerce.test_coerce_object(69), new Number(0));
+ assertEquals(coerce.test_coerce_object(""), new String(""));
+ assertEquals(
+ coerce.test_coerce_object(Number.MAX_SAFE_INTEGER),
+ new Number(Number.MAX_SAFE_INTEGER),
+ );
+ assertEquals(coerce.test_coerce_object(new Array(10)), new Array(10));
+ assertEquals(
+ coerce.test_coerce_object("Hello, Deno!"),
+ new String("Hello, Deno!"),
+ );
+ assertEquals(coerce.test_coerce_object({}), {});
+});
diff --git a/test_napi/common.js b/test_napi/common.js
new file mode 100644
index 000000000..13dad6cfc
--- /dev/null
+++ b/test_napi/common.js
@@ -0,0 +1,20 @@
+// Copyright 2018-2022 the Deno authors. All rights reserved. MIT license.
+
+export {
+ assert,
+ assertEquals,
+ assertRejects,
+} from "../test_util/std/testing/asserts.ts";
+export { fromFileUrl } from "../test_util/std/path/mod.ts";
+
+const targetDir = Deno.execPath().replace(/[^\/\\]+$/, "");
+const [libPrefix, libSuffix] = {
+ darwin: ["lib", "dylib"],
+ linux: ["lib", "so"],
+ windows: ["", "dll"],
+}[Deno.build.os];
+
+export function loadTestLibrary() {
+ const specifier = `${targetDir}/${libPrefix}test_napi.${libSuffix}`;
+ return Deno.core.ops.op_napi_open(specifier); // Internal, used in ext/node
+}
diff --git a/test_napi/numbers_test.js b/test_napi/numbers_test.js
new file mode 100644
index 000000000..2f778285a
--- /dev/null
+++ b/test_napi/numbers_test.js
@@ -0,0 +1,18 @@
+// Copyright 2018-2022 the Deno authors. All rights reserved. MIT license.
+
+import { assertEquals, loadTestLibrary } from "./common.js";
+
+const numbers = loadTestLibrary();
+
+Deno.test("napi int32", function () {
+ assertEquals(numbers.test_int32(69), 69);
+ assertEquals(numbers.test_int32(Number.MAX_SAFE_INTEGER), -1);
+});
+
+Deno.test("napi int64", function () {
+ assertEquals(numbers.test_int64(69), 69);
+ assertEquals(
+ numbers.test_int64(Number.MAX_SAFE_INTEGER),
+ Number.MAX_SAFE_INTEGER,
+ );
+});
diff --git a/test_napi/object_wrap_test.js b/test_napi/object_wrap_test.js
new file mode 100644
index 000000000..2263894cd
--- /dev/null
+++ b/test_napi/object_wrap_test.js
@@ -0,0 +1,17 @@
+// Copyright 2018-2022 the Deno authors. All rights reserved. MIT license.
+
+import { assertEquals, loadTestLibrary } from "./common.js";
+
+const objectWrap = loadTestLibrary();
+
+Deno.test("napi object wrap new", function () {
+ const obj = new objectWrap.NapiObject(0);
+ assertEquals(obj.get_value(), 0);
+ obj.set_value(10);
+ assertEquals(obj.get_value(), 10);
+ obj.increment();
+ assertEquals(obj.get_value(), 11);
+ obj.increment();
+ obj.set_value(10);
+ assertEquals(obj.get_value(), 10);
+});
diff --git a/test_napi/promise_test.js b/test_napi/promise_test.js
new file mode 100644
index 000000000..86a3f134b
--- /dev/null
+++ b/test_napi/promise_test.js
@@ -0,0 +1,34 @@
+// Copyright 2018-2022 the Deno authors. All rights reserved. MIT license.
+
+import { assertEquals, assertRejects, loadTestLibrary } from "./common.js";
+
+const promise = loadTestLibrary();
+
+Deno.test("napi new promise and resolve", async () => {
+ const p = promise.test_promise_new();
+ promise.test_promise_resolve(69);
+
+ assertEquals(await p, 69);
+});
+
+Deno.test("napi new promise and reject", () => {
+ const p = promise.test_promise_new();
+
+ assertRejects(async () => {
+ promise.test_promise_reject(new TypeError("pikaboo"));
+ await p;
+ }, TypeError);
+});
+
+Deno.test("napi new promise and reject", async () => {
+ const p = promise.test_promise_new();
+ const is = promise.test_promise_is(p);
+ assertEquals(typeof is, "boolean");
+ assertEquals(is, true);
+
+ assertEquals(promise.test_promise_is(undefined), false);
+ assertEquals(promise.test_promise_is({}), false);
+ promise.test_promise_resolve(69);
+
+ assertEquals(await p, 69);
+});
diff --git a/test_napi/properties_test.js b/test_napi/properties_test.js
new file mode 100644
index 000000000..b24c5649e
--- /dev/null
+++ b/test_napi/properties_test.js
@@ -0,0 +1,15 @@
+// Copyright 2018-2022 the Deno authors. All rights reserved. MIT license.
+
+import { assertEquals, loadTestLibrary } from "./common.js";
+
+const properties = loadTestLibrary();
+
+Deno.test("napi properties", () => {
+ properties.test_property_rw = 1;
+ assertEquals(properties.test_property_rw, 1);
+ properties.test_property_rw = 2;
+ assertEquals(properties.test_property_rw, 2);
+
+ // assertEquals(properties.test_property_r, 2);
+ // assertRejects(() => properties.test_property_r = 3);
+});
diff --git a/test_napi/src/array.rs b/test_napi/src/array.rs
new file mode 100644
index 000000000..767aa08b1
--- /dev/null
+++ b/test_napi/src/array.rs
@@ -0,0 +1,73 @@
+// Copyright 2018-2022 the Deno authors. All rights reserved. MIT license.
+
+use napi_sys::Status::napi_ok;
+use napi_sys::ValueType::napi_number;
+use napi_sys::ValueType::napi_object;
+use napi_sys::*;
+use std::ptr;
+
+extern "C" fn test_array_new(
+ env: napi_env,
+ info: napi_callback_info,
+) -> napi_value {
+ let (args, argc, _) = crate::get_callback_info!(env, info, 1);
+ assert_eq!(argc, 1);
+
+ let mut ty = -1;
+ assert!(unsafe { napi_typeof(env, args[0], &mut ty) } == napi_ok);
+ assert_eq!(ty, napi_object);
+
+ let mut value: napi_value = ptr::null_mut();
+ assert!(unsafe { napi_create_array(env, &mut value) } == napi_ok);
+
+ let mut length: u32 = 0;
+ assert!(
+ unsafe { napi_get_array_length(env, args[0], &mut length) } == napi_ok
+ );
+
+ for i in 0..length {
+ let mut e: napi_value = ptr::null_mut();
+ assert!(unsafe { napi_get_element(env, args[0], i, &mut e) } == napi_ok);
+ assert!(unsafe { napi_set_element(env, value, i, e) } == napi_ok);
+ }
+
+ value
+}
+
+extern "C" fn test_array_new_with_length(
+ env: napi_env,
+ info: napi_callback_info,
+) -> napi_value {
+ let (args, argc, _) = crate::get_callback_info!(env, info, 1);
+ assert_eq!(argc, 1);
+
+ let mut ty = -1;
+ assert!(unsafe { napi_typeof(env, args[0], &mut ty) } == napi_ok);
+ assert_eq!(ty, napi_number);
+
+ let mut len: u32 = 0;
+ assert!(unsafe { napi_get_value_uint32(env, args[0], &mut len) } == napi_ok);
+
+ let mut value: napi_value = ptr::null_mut();
+ assert!(
+ unsafe { napi_create_array_with_length(env, len as usize, &mut value) }
+ == napi_ok
+ );
+
+ value
+}
+
+pub fn init(env: napi_env, exports: napi_value) {
+ let properties = &[
+ crate::new_property!(env, "test_array_new\0", test_array_new),
+ crate::new_property!(
+ env,
+ "test_array_new_with_length\0",
+ test_array_new_with_length
+ ),
+ ];
+
+ unsafe {
+ napi_define_properties(env, exports, properties.len(), properties.as_ptr())
+ };
+}
diff --git a/test_napi/src/async.rs b/test_napi/src/async.rs
new file mode 100644
index 000000000..d14871a7c
--- /dev/null
+++ b/test_napi/src/async.rs
@@ -0,0 +1,112 @@
+// Copyright 2018-2022 the Deno authors. All rights reserved. MIT license.
+
+use napi_sys::Status::napi_ok;
+use napi_sys::ValueType::napi_function;
+use napi_sys::*;
+use std::os::raw::c_void;
+use std::ptr;
+
+pub struct Baton {
+ called: bool,
+ func: napi_ref,
+ task: napi_async_work,
+}
+
+unsafe extern "C" fn execute(_env: napi_env, data: *mut c_void) {
+ let baton: &mut Baton = &mut *(data as *mut Baton);
+ assert!(!baton.called);
+ assert!(!baton.func.is_null());
+
+ baton.called = true;
+}
+
+unsafe extern "C" fn complete(
+ env: napi_env,
+ status: napi_status,
+ data: *mut c_void,
+) {
+ assert!(status == napi_ok);
+ let baton: Box<Baton> = Box::from_raw(data as *mut Baton);
+ assert!(baton.called);
+ assert!(!baton.func.is_null());
+
+ let mut global: napi_value = ptr::null_mut();
+ assert!(napi_get_global(env, &mut global) == napi_ok);
+
+ let mut callback: napi_value = ptr::null_mut();
+ assert!(napi_get_reference_value(env, baton.func, &mut callback) == napi_ok);
+
+ let mut _result: napi_value = ptr::null_mut();
+ assert!(
+ napi_call_function(env, global, callback, 0, ptr::null(), &mut _result)
+ == napi_ok
+ );
+
+ assert!(napi_delete_reference(env, baton.func) == napi_ok);
+ assert!(napi_delete_async_work(env, baton.task) == napi_ok);
+}
+
+extern "C" fn test_async_work(
+ env: napi_env,
+ info: napi_callback_info,
+) -> napi_value {
+ let (args, argc, _) = crate::get_callback_info!(env, info, 1);
+ assert_eq!(argc, 1);
+
+ let mut ty = -1;
+ assert!(unsafe { napi_typeof(env, args[0], &mut ty) } == napi_ok);
+ assert_eq!(ty, napi_function);
+
+ let mut resource_name: napi_value = ptr::null_mut();
+ assert!(
+ unsafe {
+ napi_create_string_utf8(
+ env,
+ "test_async_resource\0".as_ptr() as *const i8,
+ usize::MAX,
+ &mut resource_name,
+ )
+ } == napi_ok
+ );
+
+ let mut async_work: napi_async_work = ptr::null_mut();
+
+ let mut func: napi_ref = ptr::null_mut();
+ assert!(
+ unsafe { napi_create_reference(env, args[0], 1, &mut func) } == napi_ok
+ );
+ let baton = Box::new(Baton {
+ called: false,
+ func,
+ task: async_work,
+ });
+
+ assert!(
+ unsafe {
+ napi_create_async_work(
+ env,
+ ptr::null_mut(),
+ resource_name,
+ Some(execute),
+ Some(complete),
+ Box::into_raw(baton) as *mut c_void,
+ &mut async_work,
+ )
+ } == napi_ok
+ );
+ assert!(unsafe { napi_queue_async_work(env, async_work) } == napi_ok);
+
+ ptr::null_mut()
+}
+
+pub fn init(env: napi_env, exports: napi_value) {
+ let properties = &[crate::new_property!(
+ env,
+ "test_async_work\0",
+ test_async_work
+ )];
+
+ unsafe {
+ napi_define_properties(env, exports, properties.len(), properties.as_ptr())
+ };
+}
diff --git a/test_napi/src/callback.rs b/test_napi/src/callback.rs
new file mode 100644
index 000000000..bf6062913
--- /dev/null
+++ b/test_napi/src/callback.rs
@@ -0,0 +1,113 @@
+// Copyright 2018-2022 the Deno authors. All rights reserved. MIT license.
+
+use napi_sys::Status::napi_ok;
+use napi_sys::ValueType::napi_function;
+use napi_sys::ValueType::napi_object;
+use napi_sys::*;
+use std::ptr;
+
+/// `test_callback_run((a, b) => a + b, [1, 2])` => 3
+extern "C" fn test_callback_run(
+ env: napi_env,
+ info: napi_callback_info,
+) -> napi_value {
+ let (args, argc, _) = crate::get_callback_info!(env, info, 2);
+ assert_eq!(argc, 2);
+
+ let mut ty = -1;
+ assert!(unsafe { napi_typeof(env, args[0], &mut ty) } == napi_ok);
+ assert_eq!(ty, napi_function);
+
+ let mut ty = -1;
+ assert!(unsafe { napi_typeof(env, args[1], &mut ty) } == napi_ok);
+ assert_eq!(ty, napi_object);
+
+ let mut len = 0;
+ assert!(unsafe { napi_get_array_length(env, args[1], &mut len) } == napi_ok);
+
+ let mut argv = Vec::with_capacity(len as usize);
+ for index in 0..len {
+ let mut value: napi_value = ptr::null_mut();
+ assert!(
+ unsafe { napi_get_element(env, args[1], index, &mut value) } == napi_ok
+ );
+ argv.push(value);
+ }
+ let mut global: napi_value = ptr::null_mut();
+ assert!(unsafe { napi_get_global(env, &mut global) } == napi_ok);
+
+ let mut result: napi_value = ptr::null_mut();
+ assert!(
+ unsafe {
+ napi_call_function(
+ env,
+ global,
+ args[0],
+ argv.len(),
+ argv.as_mut_ptr(),
+ &mut result,
+ )
+ } == napi_ok
+ );
+
+ result
+}
+
+extern "C" fn test_callback_run_with_recv(
+ env: napi_env,
+ info: napi_callback_info,
+) -> napi_value {
+ let (args, argc, _) = crate::get_callback_info!(env, info, 3);
+ assert_eq!(argc, 3);
+
+ let mut ty = -1;
+ assert!(unsafe { napi_typeof(env, args[0], &mut ty) } == napi_ok);
+ assert_eq!(ty, napi_function);
+
+ let mut ty = -1;
+ assert!(unsafe { napi_typeof(env, args[1], &mut ty) } == napi_ok);
+ assert_eq!(ty, napi_object);
+
+ let mut len = 0;
+ assert!(unsafe { napi_get_array_length(env, args[1], &mut len) } == napi_ok);
+
+ let mut argv = Vec::with_capacity(len as usize);
+ for index in 0..len {
+ let mut value: napi_value = ptr::null_mut();
+ assert!(
+ unsafe { napi_get_element(env, args[1], index, &mut value) } == napi_ok
+ );
+ argv.push(value);
+ }
+
+ let mut result: napi_value = ptr::null_mut();
+ assert!(
+ unsafe {
+ napi_call_function(
+ env,
+ args[2], // recv
+ args[0], // cb
+ argv.len(),
+ argv.as_mut_ptr(),
+ &mut result,
+ )
+ } == napi_ok
+ );
+
+ result
+}
+
+pub fn init(env: napi_env, exports: napi_value) {
+ let properties = &[
+ crate::new_property!(env, "test_callback_run\0", test_callback_run),
+ crate::new_property!(
+ env,
+ "test_callback_run_with_recv\0",
+ test_callback_run_with_recv
+ ),
+ ];
+
+ unsafe {
+ napi_define_properties(env, exports, properties.len(), properties.as_ptr())
+ };
+}
diff --git a/test_napi/src/coerce.rs b/test_napi/src/coerce.rs
new file mode 100644
index 000000000..611540fae
--- /dev/null
+++ b/test_napi/src/coerce.rs
@@ -0,0 +1,71 @@
+// Copyright 2018-2022 the Deno authors. All rights reserved. MIT license.
+
+use napi_sys::Status::napi_ok;
+use napi_sys::*;
+use std::ptr;
+
+extern "C" fn test_coerce_bool(
+ env: napi_env,
+ info: napi_callback_info,
+) -> napi_value {
+ let (args, argc, _) = crate::get_callback_info!(env, info, 1);
+ assert_eq!(argc, 1);
+
+ let mut value: napi_value = ptr::null_mut();
+ assert!(unsafe { napi_coerce_to_bool(env, args[0], &mut value) } == napi_ok);
+ value
+}
+
+extern "C" fn test_coerce_number(
+ env: napi_env,
+ info: napi_callback_info,
+) -> napi_value {
+ let (args, argc, _) = crate::get_callback_info!(env, info, 1);
+ assert_eq!(argc, 1);
+
+ let mut value: napi_value = ptr::null_mut();
+ assert!(
+ unsafe { napi_coerce_to_number(env, args[0], &mut value) } == napi_ok
+ );
+ value
+}
+
+extern "C" fn test_coerce_object(
+ env: napi_env,
+ info: napi_callback_info,
+) -> napi_value {
+ let (args, argc, _) = crate::get_callback_info!(env, info, 1);
+ assert_eq!(argc, 1);
+
+ let mut value: napi_value = ptr::null_mut();
+ assert!(
+ unsafe { napi_coerce_to_object(env, args[0], &mut value) } == napi_ok
+ );
+ value
+}
+
+extern "C" fn test_coerce_string(
+ env: napi_env,
+ info: napi_callback_info,
+) -> napi_value {
+ let (args, argc, _) = crate::get_callback_info!(env, info, 1);
+ assert_eq!(argc, 1);
+
+ let mut value: napi_value = ptr::null_mut();
+ assert!(
+ unsafe { napi_coerce_to_string(env, args[0], &mut value) } == napi_ok
+ );
+ value
+}
+pub fn init(env: napi_env, exports: napi_value) {
+ let properties = &[
+ crate::new_property!(env, "test_coerce_bool\0", test_coerce_bool),
+ crate::new_property!(env, "test_coerce_number\0", test_coerce_number),
+ crate::new_property!(env, "test_coerce_object\0", test_coerce_object),
+ crate::new_property!(env, "test_coerce_string\0", test_coerce_string),
+ ];
+
+ unsafe {
+ napi_define_properties(env, exports, properties.len(), properties.as_ptr())
+ };
+}
diff --git a/test_napi/src/lib.rs b/test_napi/src/lib.rs
new file mode 100644
index 000000000..e058686c5
--- /dev/null
+++ b/test_napi/src/lib.rs
@@ -0,0 +1,78 @@
+// Copyright 2018-2022 the Deno authors. All rights reserved. MIT license.
+#![allow(clippy::all)]
+#![allow(clippy::undocumented_unsafe_blocks)]
+
+use napi_sys::*;
+
+pub mod array;
+pub mod r#async;
+pub mod callback;
+pub mod coerce;
+pub mod numbers;
+pub mod object_wrap;
+pub mod promise;
+pub mod properties;
+pub mod strings;
+pub mod typedarray;
+
+#[macro_export]
+macro_rules! get_callback_info {
+ ($env: expr, $callback_info: expr, $size: literal) => {{
+ let mut args = [ptr::null_mut(); $size];
+ let mut argc = $size;
+ let mut this = ptr::null_mut();
+ unsafe {
+ assert!(
+ napi_get_cb_info(
+ $env,
+ $callback_info,
+ &mut argc,
+ args.as_mut_ptr(),
+ &mut this,
+ ptr::null_mut(),
+ ) == napi_ok,
+ )
+ };
+ (args, argc, this)
+ }};
+}
+
+#[macro_export]
+macro_rules! new_property {
+ ($env: expr, $name: expr, $value: expr) => {
+ napi_property_descriptor {
+ utf8name: $name.as_ptr() as *const i8,
+ name: ptr::null_mut(),
+ method: Some($value),
+ getter: None,
+ setter: None,
+ data: ptr::null_mut(),
+ attributes: 0,
+ value: ptr::null_mut(),
+ }
+ };
+}
+
+#[no_mangle]
+unsafe extern "C" fn napi_register_module_v1(
+ env: napi_env,
+ exports: napi_value,
+) -> napi_value {
+ #[cfg(windows)]
+ {
+ napi_sys::setup();
+ }
+
+ strings::init(env, exports);
+ numbers::init(env, exports);
+ typedarray::init(env, exports);
+ array::init(env, exports);
+ properties::init(env, exports);
+ promise::init(env, exports);
+ coerce::init(env, exports);
+ object_wrap::init(env, exports);
+ callback::init(env, exports);
+ r#async::init(env, exports);
+
+ exports
+}
diff --git a/test_napi/src/numbers.rs b/test_napi/src/numbers.rs
new file mode 100644
index 000000000..a6628af13
--- /dev/null
+++ b/test_napi/src/numbers.rs
@@ -0,0 +1,55 @@
+// Copyright 2018-2022 the Deno authors. All rights reserved. MIT license.
+
+use napi_sys::Status::napi_ok;
+use napi_sys::ValueType::napi_number;
+use napi_sys::*;
+use std::ptr;
+
+extern "C" fn test_int32(
+ env: napi_env,
+ info: napi_callback_info,
+) -> napi_value {
+ let (args, argc, _) = crate::get_callback_info!(env, info, 1);
+ assert_eq!(argc, 1);
+
+ let mut ty = -1;
+ assert!(unsafe { napi_typeof(env, args[0], &mut ty) } == napi_ok);
+ assert_eq!(ty, napi_number);
+
+ let mut int32 = -1;
+ assert!(unsafe { napi_get_value_int32(env, args[0], &mut int32) } == napi_ok);
+
+ let mut value: napi_value = ptr::null_mut();
+ assert!(unsafe { napi_create_int32(env, int32, &mut value) } == napi_ok);
+ value
+}
+
+extern "C" fn test_int64(
+ env: napi_env,
+ info: napi_callback_info,
+) -> napi_value {
+ let (args, argc, _) = crate::get_callback_info!(env, info, 1);
+ assert_eq!(argc, 1);
+
+ let mut ty = -1;
+ assert!(unsafe { napi_typeof(env, args[0], &mut ty) } == napi_ok);
+ assert_eq!(ty, napi_number);
+
+ let mut int64 = -1;
+ assert!(unsafe { napi_get_value_int64(env, args[0], &mut int64) } == napi_ok);
+
+ let mut value: napi_value = ptr::null_mut();
+ assert!(unsafe { napi_create_int64(env, int64, &mut value) } == napi_ok);
+ value
+}
+
+pub fn init(env: napi_env, exports: napi_value) {
+ let properties = &[
+ crate::new_property!(env, "test_int32\0", test_int32),
+ crate::new_property!(env, "test_int64\0", test_int64),
+ ];
+
+ unsafe {
+ napi_define_properties(env, exports, properties.len(), properties.as_ptr())
+ };
+}
diff --git a/test_napi/src/object_wrap.rs b/test_napi/src/object_wrap.rs
new file mode 100644
index 000000000..3b849b2dc
--- /dev/null
+++ b/test_napi/src/object_wrap.rs
@@ -0,0 +1,154 @@
+// Copyright 2018-2022 the Deno authors. All rights reserved. MIT license.
+
+use napi_sys::Status::napi_ok;
+use napi_sys::ValueType::napi_number;
+use napi_sys::*;
+use std::os::raw::c_void;
+use std::ptr;
+
+pub struct NapiObject {
+ counter: i32,
+ _wrapper: napi_ref,
+}
+
+impl NapiObject {
+ #[allow(clippy::new_ret_no_self)]
+ pub extern "C" fn new(env: napi_env, info: napi_callback_info) -> napi_value {
+ let mut new_target: napi_value = ptr::null_mut();
+ assert!(
+ unsafe { napi_get_new_target(env, info, &mut new_target) } == napi_ok
+ );
+ let is_constructor = !new_target.is_null();
+
+ let (args, argc, this) = crate::get_callback_info!(env, info, 1);
+ assert_eq!(argc, 1);
+
+ if is_constructor {
+ let mut value = 0;
+
+ let mut ty = -1;
+ assert!(unsafe { napi_typeof(env, args[0], &mut ty) } == napi_ok);
+ assert_eq!(ty, napi_number);
+
+ assert!(
+ unsafe { napi_get_value_int32(env, args[0], &mut value) } == napi_ok
+ );
+
+ let mut wrapper: napi_ref = ptr::null_mut();
+ let obj = Box::new(Self {
+ counter: value,
+ _wrapper: wrapper,
+ });
+ assert!(
+ unsafe {
+ napi_wrap(
+ env,
+ this,
+ Box::into_raw(obj) as *mut c_void,
+ None,
+ ptr::null_mut(),
+ &mut wrapper,
+ )
+ } == napi_ok
+ );
+
+ return this;
+ }
+
+ unreachable!();
+ }
+
+ pub extern "C" fn set_value(
+ env: napi_env,
+ info: napi_callback_info,
+ ) -> napi_value {
+ let (args, argc, this) = crate::get_callback_info!(env, info, 1);
+ assert_eq!(argc, 1);
+ let mut obj: *mut Self = ptr::null_mut();
+ assert!(
+ unsafe { napi_unwrap(env, this, &mut obj as *mut _ as *mut *mut c_void) }
+ == napi_ok
+ );
+
+ assert!(
+ unsafe { napi_get_value_int32(env, args[0], &mut (*obj).counter) }
+ == napi_ok
+ );
+
+ ptr::null_mut()
+ }
+
+ pub extern "C" fn get_value(
+ env: napi_env,
+ info: napi_callback_info,
+ ) -> napi_value {
+ let (_args, argc, this) = crate::get_callback_info!(env, info, 0);
+ assert_eq!(argc, 0);
+ let mut obj: *mut Self = ptr::null_mut();
+ assert!(
+ unsafe { napi_unwrap(env, this, &mut obj as *mut _ as *mut *mut c_void) }
+ == napi_ok
+ );
+
+ let mut num: napi_value = ptr::null_mut();
+ assert!(
+ unsafe { napi_create_int32(env, (*obj).counter, &mut num) } == napi_ok
+ );
+
+ num
+ }
+
+ pub extern "C" fn increment(
+ env: napi_env,
+ info: napi_callback_info,
+ ) -> napi_value {
+ let (_args, argc, this) = crate::get_callback_info!(env, info, 0);
+ assert_eq!(argc, 0);
+ let mut obj: *mut Self = ptr::null_mut();
+ assert!(
+ unsafe { napi_unwrap(env, this, &mut obj as *mut _ as *mut *mut c_void) }
+ == napi_ok
+ );
+
+ unsafe {
+ (*obj).counter += 1;
+ }
+
+ ptr::null_mut()
+ }
+}
+
+pub fn init(env: napi_env, exports: napi_value) {
+ let properties = &[
+ crate::new_property!(env, "set_value\0", NapiObject::set_value),
+ crate::new_property!(env, "get_value\0", NapiObject::get_value),
+ crate::new_property!(env, "increment\0", NapiObject::increment),
+ ];
+
+ let mut cons: napi_value = ptr::null_mut();
+ assert!(
+ unsafe {
+ napi_define_class(
+ env,
+ "NapiObject\0".as_ptr() as *mut i8,
+ usize::MAX,
+ Some(NapiObject::new),
+ ptr::null_mut(),
+ properties.len(),
+ properties.as_ptr(),
+ &mut cons,
+ )
+ } == napi_ok
+ );
+
+ assert!(
+ unsafe {
+ napi_set_named_property(
+ env,
+ exports,
+ "NapiObject\0".as_ptr() as *const i8,
+ cons,
+ )
+ } == napi_ok
+ );
+}
diff --git a/test_napi/src/promise.rs b/test_napi/src/promise.rs
new file mode 100644
index 000000000..ebb9dedab
--- /dev/null
+++ b/test_napi/src/promise.rs
@@ -0,0 +1,76 @@
+// Copyright 2018-2022 the Deno authors. All rights reserved. MIT license.
+
+use napi_sys::Status::napi_ok;
+use napi_sys::*;
+use std::ptr;
+
+static mut CURRENT_DEFERRED: napi_deferred = ptr::null_mut();
+
+extern "C" fn test_promise_new(
+ env: napi_env,
+ _info: napi_callback_info,
+) -> napi_value {
+ let mut value: napi_value = ptr::null_mut();
+ assert!(
+ unsafe { napi_create_promise(env, &mut CURRENT_DEFERRED, &mut value) }
+ == napi_ok
+ );
+ value
+}
+
+extern "C" fn test_promise_resolve(
+ env: napi_env,
+ info: napi_callback_info,
+) -> napi_value {
+ let (args, argc, _) = crate::get_callback_info!(env, info, 1);
+ assert_eq!(argc, 1);
+
+ assert!(
+ unsafe { napi_resolve_deferred(env, CURRENT_DEFERRED, args[0]) } == napi_ok
+ );
+ unsafe { CURRENT_DEFERRED = ptr::null_mut() };
+ ptr::null_mut()
+}
+
+extern "C" fn test_promise_reject(
+ env: napi_env,
+ info: napi_callback_info,
+) -> napi_value {
+ let (args, argc, _) = crate::get_callback_info!(env, info, 1);
+ assert_eq!(argc, 1);
+
+ assert!(
+ unsafe { napi_reject_deferred(env, CURRENT_DEFERRED, args[0]) } == napi_ok
+ );
+ unsafe { CURRENT_DEFERRED = ptr::null_mut() };
+ ptr::null_mut()
+}
+
+extern "C" fn test_promise_is(
+ env: napi_env,
+ info: napi_callback_info,
+) -> napi_value {
+ let (args, argc, _) = crate::get_callback_info!(env, info, 1);
+ assert_eq!(argc, 1);
+
+ let mut is_promise: bool = false;
+ assert!(unsafe { napi_is_promise(env, args[0], &mut is_promise) } == napi_ok);
+
+ let mut result: napi_value = ptr::null_mut();
+ assert!(unsafe { napi_get_boolean(env, is_promise, &mut result) } == napi_ok);
+
+ result
+}
+
+pub fn init(env: napi_env, exports: napi_value) {
+ let properties = &[
+ crate::new_property!(env, "test_promise_new\0", test_promise_new),
+ crate::new_property!(env, "test_promise_resolve\0", test_promise_resolve),
+ crate::new_property!(env, "test_promise_reject\0", test_promise_reject),
+ crate::new_property!(env, "test_promise_is\0", test_promise_is),
+ ];
+
+ unsafe {
+ napi_define_properties(env, exports, properties.len(), properties.as_ptr())
+ };
+}
diff --git a/test_napi/src/properties.rs b/test_napi/src/properties.rs
new file mode 100644
index 000000000..89d95d6c6
--- /dev/null
+++ b/test_napi/src/properties.rs
@@ -0,0 +1,89 @@
+// Copyright 2018-2022 the Deno authors. All rights reserved. MIT license.
+
+use napi_sys::PropertyAttributes::*;
+use napi_sys::Status::napi_ok;
+use napi_sys::*;
+use std::ptr;
+
+pub fn init(env: napi_env, exports: napi_value) {
+ let mut number: napi_value = ptr::null_mut();
+ assert!(unsafe { napi_create_double(env, 1.0, &mut number) } == napi_ok);
+
+ // Key name as napi_value representing `v8::String`
+ let mut name_value: napi_value = ptr::null_mut();
+ assert!(
+ unsafe {
+ napi_create_string_utf8(
+ env,
+ "key_v8_string".as_ptr() as *const i8,
+ usize::MAX,
+ &mut name_value,
+ )
+ } == napi_ok
+ );
+
+ // Key symbol
+ let mut symbol_description: napi_value = ptr::null_mut();
+ let mut name_symbol: napi_value = ptr::null_mut();
+ assert!(
+ unsafe {
+ napi_create_string_utf8(
+ env,
+ "key_v8_symbol".as_ptr() as *const i8,
+ usize::MAX,
+ &mut symbol_description,
+ )
+ } == napi_ok
+ );
+ assert!(
+ unsafe { napi_create_symbol(env, symbol_description, &mut name_symbol) }
+ == napi_ok
+ );
+
+ let properties = &[
+ napi_property_descriptor {
+ utf8name: "test_property_rw\0".as_ptr() as *const i8,
+ name: ptr::null_mut(),
+ method: None,
+ getter: None,
+ setter: None,
+ data: ptr::null_mut(),
+ attributes: enumerable | writable,
+ value: number,
+ },
+ napi_property_descriptor {
+ utf8name: "test_property_r\0".as_ptr() as *const i8,
+ name: ptr::null_mut(),
+ method: None,
+ getter: None,
+ setter: None,
+ data: ptr::null_mut(),
+ attributes: enumerable,
+ value: number,
+ },
+ napi_property_descriptor {
+ utf8name: ptr::null(),
+ name: name_value,
+ method: None,
+ getter: None,
+ setter: None,
+ data: ptr::null_mut(),
+ attributes: enumerable,
+ value: number,
+ },
+ napi_property_descriptor {
+ utf8name: ptr::null(),
+ name: name_symbol,
+ method: None,
+ getter: None,
+ setter: None,
+ data: ptr::null_mut(),
+ attributes: enumerable,
+ value: number,
+ },
+ ];
+
+ unsafe {
+ napi_define_properties(env, exports, properties.len(), properties.as_ptr())
+ };
+}
diff --git a/test_napi/src/strings.rs b/test_napi/src/strings.rs
new file mode 100644
index 000000000..f4139c85a
--- /dev/null
+++ b/test_napi/src/strings.rs
@@ -0,0 +1,45 @@
+// Copyright 2018-2022 the Deno authors. All rights reserved. MIT license.
+
+use napi_sys::Status::napi_ok;
+use napi_sys::ValueType::napi_string;
+use napi_sys::*;
+use std::ptr;
+
+extern "C" fn test_utf8(env: napi_env, info: napi_callback_info) -> napi_value {
+ let (args, argc, _) = crate::get_callback_info!(env, info, 1);
+ assert_eq!(argc, 1);
+
+ let mut ty = -1;
+ assert!(unsafe { napi_typeof(env, args[0], &mut ty) } == napi_ok);
+ assert_eq!(ty, napi_string);
+
+ args[0]
+}
+
+extern "C" fn test_utf16(
+ env: napi_env,
+ info: napi_callback_info,
+) -> napi_value {
+ let (args, argc, _) = crate::get_callback_info!(env, info, 1);
+ assert_eq!(argc, 1);
+
+ let mut ty = -1;
+ assert!(unsafe { napi_typeof(env, args[0], &mut ty) } == napi_ok);
+ assert_eq!(ty, napi_string);
+
+ args[0]
+}
+
+pub fn init(env: napi_env, exports: napi_value) {
+ let properties = &[
+ // utf8
+ crate::new_property!(env, "test_utf8\0", test_utf8),
+ // utf16
+ crate::new_property!(env, "test_utf16\0", test_utf16),
+ // latin1
+ ];
+
+ unsafe {
+ napi_define_properties(env, exports, properties.len(), properties.as_ptr())
+ };
+}
diff --git a/test_napi/src/typedarray.rs b/test_napi/src/typedarray.rs
new file mode 100644
index 000000000..f7d2e2f24
--- /dev/null
+++ b/test_napi/src/typedarray.rs
@@ -0,0 +1,53 @@
+// Copyright 2018-2022 the Deno authors. All rights reserved. MIT license.
+
+use core::ffi::c_void;
+use napi_sys::Status::napi_ok;
+use napi_sys::TypedarrayType::uint8_array;
+use napi_sys::*;
+use std::ptr;
+
+extern "C" fn test_external(
+ env: napi_env,
+ _info: napi_callback_info,
+) -> napi_value {
+ let mut arraybuffer: napi_value = ptr::null_mut();
+ let mut external: Box<[u8; 4]> = Box::new([0, 1, 2, 3]);
+ assert!(
+ unsafe {
+ napi_create_external_arraybuffer(
+ env,
+ external.as_mut_ptr() as *mut c_void,
+ external.len(),
+ None,
+ ptr::null_mut(),
+ &mut arraybuffer,
+ )
+ } == napi_ok
+ );
+
+ let mut typedarray: napi_value = ptr::null_mut();
+ assert!(
+ unsafe {
+ napi_create_typedarray(
+ env,
+ uint8_array,
+ external.len(),
+ arraybuffer,
+ 0,
+ &mut typedarray,
+ )
+ } == napi_ok
+ );
+
+ std::mem::forget(external); // Leak into JS land
+ typedarray
+}
+
+pub fn init(env: napi_env, exports: napi_value) {
+ let properties =
+ &[crate::new_property!(env, "test_external\0", test_external)];
+
+ unsafe {
+ napi_define_properties(env, exports, properties.len(), properties.as_ptr())
+ };
+}
diff --git a/test_napi/strings_test.js b/test_napi/strings_test.js
new file mode 100644
index 000000000..20e95ba61
--- /dev/null
+++ b/test_napi/strings_test.js
@@ -0,0 +1,15 @@
+// Copyright 2018-2022 the Deno authors. All rights reserved. MIT license.
+
+import { assertEquals, loadTestLibrary } from "./common.js";
+
+const strings = loadTestLibrary();
+
+Deno.test("napi string utf8", function () {
+ assertEquals(strings.test_utf8(""), "");
+ assertEquals(strings.test_utf8("🦕"), "🦕");
+});
+
+Deno.test("napi string", function () {
+ assertEquals(strings.test_utf16(""), "");
+ assertEquals(strings.test_utf16("🦕"), "🦕");
+});
diff --git a/test_napi/tests/napi_tests.rs b/test_napi/tests/napi_tests.rs
new file mode 100644
index 000000000..41ec5c851
--- /dev/null
+++ b/test_napi/tests/napi_tests.rs
@@ -0,0 +1,45 @@
+// Copyright 2018-2022 the Deno authors. All rights reserved. MIT license.
+
+use std::process::Command;
+use test_util::deno_cmd;
+
+#[cfg(debug_assertions)]
+const BUILD_VARIANT: &str = "debug";
+
+#[cfg(not(debug_assertions))]
+const BUILD_VARIANT: &str = "release";
+
+fn build() {
+ let mut build_plugin_base = Command::new("cargo");
+ let mut build_plugin =
+ build_plugin_base.arg("build").arg("-p").arg("test_napi");
+ if BUILD_VARIANT == "release" {
+ build_plugin = build_plugin.arg("--release");
+ }
+ let build_plugin_output = build_plugin.output().unwrap();
+ assert!(build_plugin_output.status.success());
+}
+
+#[test]
+fn napi_tests() {
+ build();
+
+ let output = deno_cmd()
+ .current_dir(test_util::napi_tests_path())
+ .arg("test")
+ .arg("--allow-read")
+ .arg("--allow-env")
+ .arg("--allow-ffi")
+ .arg("--unstable")
+ .spawn()
+ .unwrap()
+ .wait_with_output()
+ .unwrap();
+ let stdout = std::str::from_utf8(&output.stdout).unwrap();
+ let stderr = std::str::from_utf8(&output.stderr).unwrap();
+ if !output.status.success() {
+ println!("stdout {}", stdout);
+ println!("stderr {}", stderr);
+ }
+ assert!(output.status.success());
+}
diff --git a/test_napi/typedarray_test.js b/test_napi/typedarray_test.js
new file mode 100644
index 000000000..a3c7322f7
--- /dev/null
+++ b/test_napi/typedarray_test.js
@@ -0,0 +1,12 @@
+// Copyright 2018-2022 the Deno authors. All rights reserved. MIT license.
+
+import { assertEquals, loadTestLibrary } from "./common.js";
+
+const typedarray = loadTestLibrary();
+
+Deno.test("napi typedarray external", function () {
+ assertEquals(
+ new Uint8Array(typedarray.test_external()),
+ new Uint8Array([0, 1, 2, 3]),
+ );
+});
diff --git a/test_util/src/lib.rs b/test_util/src/lib.rs
index 73fe3ff9b..363fe5d3f 100644
--- a/test_util/src/lib.rs
+++ b/test_util/src/lib.rs
@@ -116,6 +116,10 @@ pub fn third_party_path() -> PathBuf {
root_path().join("third_party")
}
+pub fn napi_tests_path() -> PathBuf {
+ root_path().join("test_napi")
+}
+
pub fn std_path() -> PathBuf {
root_path().join("test_util").join("std")
}
diff --git a/tools/napi/generate_link_win.js b/tools/napi/generate_link_win.js
new file mode 100755
index 000000000..a0bda6a42
--- /dev/null
+++ b/tools/napi/generate_link_win.js
@@ -0,0 +1,12 @@
+#!/usr/bin/env -S deno run --unstable --allow-read --allow-write
+// Copyright 2018-2022 the Deno authors. All rights reserved. MIT license.
+
+import exports from "./symbol_exports.json" assert { type: "json" };
+
+let def = "LIBRARY\nEXPORTS\n";
+for (const symbol of exports.symbols) {
+ def += ` ${symbol}\n`;
+}
+
+const defUrl = new URL("../../cli/exports.def", import.meta.url);
+await Deno.writeTextFile(defUrl.pathname, def, { create: true });
diff --git a/tools/napi/symbol_exports.json b/tools/napi/symbol_exports.json
new file mode 100644
index 000000000..ba1bba67a
--- /dev/null
+++ b/tools/napi/symbol_exports.json
@@ -0,0 +1,148 @@
+{
+ "symbols": [
+ "node_api_create_syntax_error",
+ "napi_make_callback",
+ "napi_has_named_property",
+ "napi_async_destroy",
+ "napi_coerce_to_object",
+ "napi_get_arraybuffer_info",
+ "napi_detach_arraybuffer",
+ "napi_get_undefined",
+ "napi_reference_unref",
+ "napi_fatal_error",
+ "napi_open_callback_scope",
+ "napi_close_callback_scope",
+ "napi_get_value_uint32",
+ "napi_create_function",
+ "napi_create_arraybuffer",
+ "napi_get_value_int64",
+ "napi_get_all_property_names",
+ "napi_resolve_deferred",
+ "napi_is_detached_arraybuffer",
+ "napi_create_string_utf8",
+ "napi_create_threadsafe_function",
+ "node_api_throw_syntax_error",
+ "napi_create_bigint_int64",
+ "napi_wrap",
+ "napi_set_property",
+ "napi_get_value_bigint_int64",
+ "napi_open_handle_scope",
+ "napi_create_error",
+ "napi_create_buffer",
+ "napi_cancel_async_work",
+ "napi_is_exception_pending",
+ "napi_acquire_threadsafe_function",
+ "napi_create_external",
+ "napi_get_threadsafe_function_context",
+ "napi_get_null",
+ "napi_create_string_utf16",
+ "napi_get_value_bigint_uint64",
+ "napi_module_register",
+ "napi_is_typedarray",
+ "napi_create_external_buffer",
+ "napi_get_new_target",
+ "napi_get_instance_data",
+ "napi_close_handle_scope",
+ "napi_get_value_string_utf16",
+ "napi_get_property_names",
+ "napi_is_arraybuffer",
+ "napi_get_cb_info",
+ "napi_define_properties",
+ "napi_add_env_cleanup_hook",
+ "node_api_get_module_file_name",
+ "napi_get_node_version",
+ "napi_create_int64",
+ "napi_create_double",
+ "napi_get_and_clear_last_exception",
+ "napi_create_reference",
+ "napi_get_typedarray_info",
+ "napi_call_threadsafe_function",
+ "napi_get_last_error_info",
+ "napi_create_array_with_length",
+ "napi_coerce_to_number",
+ "napi_get_global",
+ "napi_is_error",
+ "napi_set_instance_data",
+ "napi_create_typedarray",
+ "napi_throw_type_error",
+ "napi_has_property",
+ "napi_get_value_external",
+ "napi_create_range_error",
+ "napi_typeof",
+ "napi_ref_threadsafe_function",
+ "napi_create_bigint_uint64",
+ "napi_get_prototype",
+ "napi_adjust_external_memory",
+ "napi_release_threadsafe_function",
+ "napi_delete_async_work",
+ "napi_create_string_latin1",
+ "napi_is_array",
+ "napi_unref_threadsafe_function",
+ "napi_throw_error",
+ "napi_has_own_property",
+ "napi_get_reference_value",
+ "napi_remove_env_cleanup_hook",
+ "napi_get_value_string_utf8",
+ "napi_is_promise",
+ "napi_get_boolean",
+ "napi_run_script",
+ "napi_get_element",
+ "napi_get_named_property",
+ "napi_get_buffer_info",
+ "napi_get_value_bool",
+ "napi_reference_ref",
+ "napi_create_object",
+ "napi_create_promise",
+ "napi_create_int32",
+ "napi_escape_handle",
+ "napi_open_escapable_handle_scope",
+ "napi_throw",
+ "napi_get_value_double",
+ "napi_set_named_property",
+ "napi_call_function",
+ "napi_create_date",
+ "napi_object_freeze",
+ "napi_get_uv_event_loop",
+ "napi_get_value_string_latin1",
+ "napi_reject_deferred",
+ "napi_add_finalizer",
+ "napi_create_array",
+ "napi_delete_reference",
+ "napi_get_date_value",
+ "napi_create_dataview",
+ "napi_get_version",
+ "napi_define_class",
+ "napi_is_date",
+ "napi_remove_wrap",
+ "napi_delete_property",
+ "napi_instanceof",
+ "napi_create_buffer_copy",
+ "napi_delete_element",
+ "napi_object_seal",
+ "napi_queue_async_work",
+ "napi_get_value_bigint_words",
+ "napi_is_buffer",
+ "napi_get_array_length",
+ "napi_get_property",
+ "napi_new_instance",
+ "napi_set_element",
+ "napi_create_bigint_words",
+ "napi_strict_equals",
+ "napi_is_dataview",
+ "napi_close_escapable_handle_scope",
+ "napi_get_dataview_info",
+ "napi_get_value_int32",
+ "napi_unwrap",
+ "napi_throw_range_error",
+ "napi_coerce_to_bool",
+ "napi_create_uint32",
+ "napi_has_element",
+ "napi_create_external_arraybuffer",
+ "napi_create_symbol",
+ "napi_coerce_to_string",
+ "napi_create_type_error",
+ "napi_fatal_exception",
+ "napi_create_async_work",
+ "napi_async_init"
+ ]
+}