1
0
Fork 0
mirror of https://github.com/denoland/deno.git synced 2024-11-21 15:04:11 -05:00

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>
This commit is contained in:
Divy Srivastava 2022-10-05 07:06:44 -07:00 committed by GitHub
parent 3a3a848406
commit 0b016a7fb8
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
55 changed files with 5299 additions and 2 deletions

3
.gitignore vendored
View file

@ -11,6 +11,9 @@ gclient_config.py_entries
/target/ /target/
/std/hash/_wasm/target /std/hash/_wasm/target
/tools/wpt/manifest.json /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 # Files that help ensure VSCode can work but we don't want checked into the
# repo # repo

45
Cargo.lock generated
View file

@ -854,6 +854,7 @@ dependencies = [
"log 0.4.17", "log 0.4.17",
"mitata", "mitata",
"monch", "monch",
"napi_sym",
"nix", "nix",
"notify", "notify",
"once_cell", "once_cell",
@ -1161,6 +1162,14 @@ dependencies = [
"serde_json", "serde_json",
] ]
[[package]]
name = "deno_napi"
version = "0.1.0"
dependencies = [
"deno_core",
"libloading",
]
[[package]] [[package]]
name = "deno_net" name = "deno_net"
version = "0.63.0" version = "0.63.0"
@ -1214,6 +1223,7 @@ dependencies = [
"deno_ffi", "deno_ffi",
"deno_flash", "deno_flash",
"deno_http", "deno_http",
"deno_napi",
"deno_net", "deno_net",
"deno_node", "deno_node",
"deno_tls", "deno_tls",
@ -2897,6 +2907,32 @@ dependencies = [
"unicode-xid 0.2.4", "unicode-xid 0.2.4",
] ]
[[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]] [[package]]
name = "netif" name = "netif"
version = "0.1.6" version = "0.1.6"
@ -4783,6 +4819,15 @@ dependencies = [
"test_util", "test_util",
] ]
[[package]]
name = "test_napi"
version = "0.1.0"
dependencies = [
"napi-build",
"napi-sys",
"test_util",
]
[[package]] [[package]]
name = "test_util" name = "test_util"
version = "0.1.0" version = "0.1.0"

View file

@ -5,11 +5,13 @@ resolver = "2"
members = [ members = [
"bench_util", "bench_util",
"cli", "cli",
"cli/napi_sym",
"core", "core",
"ops", "ops",
"runtime", "runtime",
"serde_v8", "serde_v8",
"test_ffi", "test_ffi",
"test_napi",
"test_util", "test_util",
"ext/broadcast_channel", "ext/broadcast_channel",
"ext/cache", "ext/cache",
@ -27,6 +29,7 @@ members = [
"ext/webidl", "ext/webidl",
"ext/websocket", "ext/websocket",
"ext/webstorage", "ext/webstorage",
"ext/napi",
] ]
exclude = ["test_util/std/hash/_wasm"] exclude = ["test_util/std/hash/_wasm"]
@ -154,6 +157,8 @@ opt-level = 3
opt-level = 3 opt-level = 3
[profile.release.package.deno_websocket] [profile.release.package.deno_websocket]
opt-level = 3 opt-level = 3
[profile.release.package.deno_napi]
opt-level = 3
[profile.release.package.num-bigint-dig] [profile.release.package.num-bigint-dig]
opt-level = 3 opt-level = 3
[profile.release.package.v8] [profile.release.package.v8]

View file

@ -56,6 +56,7 @@ deno_graph = "0.34.0"
deno_lint = { version = "0.33.0", features = ["docs"] } deno_lint = { version = "0.33.0", features = ["docs"] }
deno_runtime = { version = "0.79.0", path = "../runtime" } deno_runtime = { version = "0.79.0", path = "../runtime" }
deno_task_shell = "0.5.2" deno_task_shell = "0.5.2"
napi_sym = { path = "./napi_sym", version = "0.1.0" }
atty = "=0.2.14" atty = "=0.2.14"
base64 = "=0.13.0" base64 = "=0.13.0"

View file

@ -331,6 +331,21 @@ fn main() {
if target != host { if target != host {
panic!("Cross compiling with snapshot is not supported."); 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: // To debug snapshot issues uncomment:
// op_fetch_asset::trace_serializer(); // op_fetch_asset::trace_serializer();

146
cli/exports.def Normal file
View file

@ -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

View file

@ -22,6 +22,7 @@ mod lockfile;
mod logger; mod logger;
mod lsp; mod lsp;
mod module_loader; mod module_loader;
mod napi;
mod node; mod node;
mod npm; mod npm;
mod ops; mod ops;

78
cli/napi/async.rs Normal file
View file

@ -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!()
}

141
cli/napi/env.rs Normal file
View file

@ -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(())
}

2275
cli/napi/js_native_api.rs Normal file

File diff suppressed because it is too large Load diff

88
cli/napi/mod.rs Normal file
View file

@ -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,
}
}

View file

@ -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(())
}

23
cli/napi/util.rs Normal file
View file

@ -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()
}

18
cli/napi_sym/Cargo.toml Normal file
View file

@ -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"] }

46
cli/napi_sym/lib.rs Normal file
View file

@ -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())
}
})
}

View file

@ -342,6 +342,18 @@ impl JsRuntime {
// V8 takes ownership of external_references. // V8 takes ownership of external_references.
let refs: &'static v8::ExternalReferences = Box::leak(Box::new(refs)); let refs: &'static v8::ExternalReferences = Box::leak(Box::new(refs));
let global_context; 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 { let (mut isolate, maybe_snapshot_creator) = if options.will_snapshot {
// TODO(ry) Support loading snapshots before snapshotting. // TODO(ry) Support loading snapshots before snapshotting.
assert!(options.startup_snapshot.is_none()); assert!(options.startup_snapshot.is_none());
@ -352,6 +364,12 @@ impl JsRuntime {
let isolate = unsafe { creator.get_owned_isolate() }; let isolate = unsafe { creator.get_owned_isolate() };
let mut isolate = JsRuntime::setup_isolate(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 scope = &mut v8::HandleScope::new(&mut isolate);
let context = bindings::initialize_context(scope, &op_ctxs, false); let context = bindings::initialize_context(scope, &op_ctxs, false);
global_context = v8::Global::new(scope, context); global_context = v8::Global::new(scope, context);
@ -383,15 +401,23 @@ impl JsRuntime {
let isolate = v8::Isolate::new(params); let isolate = v8::Isolate::new(params);
let mut isolate = JsRuntime::setup_isolate(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 scope = &mut v8::HandleScope::new(&mut isolate);
let context = let context =
bindings::initialize_context(scope, &op_ctxs, snapshot_loaded); bindings::initialize_context(scope, &op_ctxs, snapshot_loaded);
global_context = v8::Global::new(scope, context); global_context = v8::Global::new(scope, context);
} }
(isolate, None) (isolate, None)
}; };
op_state.borrow_mut().put(isolate_ptr);
let inspector = let inspector =
JsRuntimeInspector::new(&mut isolate, global_context.clone()); JsRuntimeInspector::new(&mut isolate, global_context.clone());
@ -955,7 +981,6 @@ impl JsRuntime {
self.drain_macrotasks()?; self.drain_macrotasks()?;
self.check_promise_exceptions()?; self.check_promise_exceptions()?;
} }
// Dynamic module loading - ie. modules loaded using "import()" // Dynamic module loading - ie. modules loaded using "import()"
{ {
// Run in a loop so that dynamic imports that only depend on another // Run in a loop so that dynamic imports that only depend on another

18
ext/napi/Cargo.toml Normal file
View file

@ -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" }

103
ext/napi/function.rs Normal file
View file

@ -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
}

607
ext/napi/lib.rs Normal file
View file

@ -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
})
}

View file

@ -773,7 +773,7 @@
// Native extension for .node // Native extension for .node
Module._extensions[".node"] = function (module, filename) { Module._extensions[".node"] = function (module, filename) {
throw new Error("not implemented loading .node files"); module.exports = ops.op_napi_open(filename);
}; };
function createRequireFromPath(filename) { function createRequireFromPath(filename) {

View file

@ -40,6 +40,7 @@ deno_webgpu = { version = "0.72.0", path = "../ext/webgpu" }
deno_webidl = { version = "0.71.0", path = "../ext/webidl" } deno_webidl = { version = "0.71.0", path = "../ext/webidl" }
deno_websocket = { version = "0.76.0", path = "../ext/websocket" } deno_websocket = { version = "0.76.0", path = "../ext/websocket" }
deno_webstorage = { version = "0.66.0", path = "../ext/webstorage" } deno_webstorage = { version = "0.66.0", path = "../ext/webstorage" }
deno_napi = { version = "0.1.0", path = "../ext/napi" }
lzzzz = '1.0' 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_ffi = { version = "0.58.0", path = "../ext/ffi" }
deno_flash = { version = "0.7.0", path = "../ext/flash" } deno_flash = { version = "0.7.0", path = "../ext/flash" }
deno_http = { version = "0.65.0", path = "../ext/http" } 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_net = { version = "0.63.0", path = "../ext/net" }
deno_node = { version = "0.8.0", path = "../ext/node" } deno_node = { version = "0.8.0", path = "../ext/node" }
deno_tls = { version = "0.58.0", path = "../ext/tls" } deno_tls = { version = "0.58.0", path = "../ext/tls" }

View file

@ -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 { impl deno_flash::FlashPermissions for Permissions {
fn check_net<T: AsRef<str>>( fn check_net<T: AsRef<str>>(
&mut self, &mut self,
@ -191,6 +200,7 @@ mod not_docs {
None, false, // No --unstable. None, false, // No --unstable.
None, None,
), ),
deno_napi::init::<Permissions>(false),
deno_http::init(), deno_http::init(),
deno_flash::init::<Permissions>(false), // No --unstable deno_flash::init::<Permissions>(false), // No --unstable
]; ];

View file

@ -8,6 +8,7 @@ pub use deno_crypto;
pub use deno_fetch; pub use deno_fetch;
pub use deno_ffi; pub use deno_ffi;
pub use deno_http; pub use deno_http;
pub use deno_napi;
pub use deno_net; pub use deno_net;
pub use deno_node; pub use deno_node;
pub use deno_tls; pub use deno_tls;

View file

@ -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 { impl deno_ffi::FfiPermissions for Permissions {
fn check(&mut self, path: Option<&Path>) -> Result<(), AnyError> { fn check(&mut self, path: Option<&Path>) -> Result<(), AnyError> {
self.ffi.check(path) self.ffi.check(path)

View file

@ -431,6 +431,7 @@ impl WebWorker {
unstable, unstable,
options.unsafely_ignore_certificate_errors.clone(), options.unsafely_ignore_certificate_errors.clone(),
), ),
deno_napi::init::<Permissions>(unstable),
deno_node::init::<Permissions>(unstable, options.npm_resolver), deno_node::init::<Permissions>(unstable, options.npm_resolver),
ops::os::init_for_worker(), ops::os::init_for_worker(),
ops::permissions::init(), ops::permissions::init(),

View file

@ -189,6 +189,7 @@ impl MainWorker {
unstable, unstable,
options.unsafely_ignore_certificate_errors.clone(), options.unsafely_ignore_certificate_errors.clone(),
), ),
deno_napi::init::<Permissions>(unstable),
deno_node::init::<Permissions>(unstable, options.npm_resolver), deno_node::init::<Permissions>(unstable, options.npm_resolver),
ops::os::init(exit_code.clone()), ops::os::init(exit_code.clone()),
ops::permissions::init(), ops::permissions::init(),

4
test_napi/.gitignore vendored Normal file
View file

@ -0,0 +1,4 @@
package-lock.json
# Test generated artifacts
.swc

20
test_napi/Cargo.toml Normal file
View file

@ -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"

19
test_napi/array_test.js Normal file
View file

@ -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);
});

16
test_napi/async_test.js Normal file
View file

@ -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);
});

7
test_napi/build.rs Normal file
View file

@ -0,0 +1,7 @@
// Copyright 2018-2022 the Deno authors. All rights reserved. MIT license.
extern crate napi_build;
fn main() {
napi_build::setup();
}

View file

@ -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);
});

74
test_napi/coerce_test.js Normal file
View file

@ -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({}), {});
});

20
test_napi/common.js Normal file
View file

@ -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
}

18
test_napi/numbers_test.js Normal file
View file

@ -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,
);
});

View file

@ -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);
});

34
test_napi/promise_test.js Normal file
View file

@ -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);
});

View file

@ -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);
});

73
test_napi/src/array.rs Normal file
View file

@ -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())
};
}

112
test_napi/src/async.rs Normal file
View file

@ -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())
};
}

113
test_napi/src/callback.rs Normal file
View file

@ -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())
};
}

71
test_napi/src/coerce.rs Normal file
View file

@ -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())
};
}

78
test_napi/src/lib.rs Normal file
View file

@ -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
}

55
test_napi/src/numbers.rs Normal file
View file

@ -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())
};
}

View file

@ -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
);
}

76
test_napi/src/promise.rs Normal file
View file

@ -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())
};
}

View file

@ -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())
};
}

45
test_napi/src/strings.rs Normal file
View file

@ -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())
};
}

View file

@ -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())
};
}

15
test_napi/strings_test.js Normal file
View file

@ -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("🦕"), "🦕");
});

View file

@ -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());
}

View file

@ -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]),
);
});

View file

@ -116,6 +116,10 @@ pub fn third_party_path() -> PathBuf {
root_path().join("third_party") root_path().join("third_party")
} }
pub fn napi_tests_path() -> PathBuf {
root_path().join("test_napi")
}
pub fn std_path() -> PathBuf { pub fn std_path() -> PathBuf {
root_path().join("test_util").join("std") root_path().join("test_util").join("std")
} }

12
tools/napi/generate_link_win.js Executable file
View file

@ -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 });

View file

@ -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"
]
}