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:
parent
3a3a848406
commit
0b016a7fb8
55 changed files with 5299 additions and 2 deletions
3
.gitignore
vendored
3
.gitignore
vendored
|
@ -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
45
Cargo.lock
generated
|
@ -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"
|
||||||
|
|
|
@ -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]
|
||||||
|
|
|
@ -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"
|
||||||
|
|
15
cli/build.rs
15
cli/build.rs
|
@ -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
146
cli/exports.def
Normal 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
|
|
@ -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
78
cli/napi/async.rs
Normal 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
141
cli/napi/env.rs
Normal 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
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
88
cli/napi/mod.rs
Normal 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,
|
||||||
|
}
|
||||||
|
}
|
199
cli/napi/threadsafe_functions.rs
Normal file
199
cli/napi/threadsafe_functions.rs
Normal 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
23
cli/napi/util.rs
Normal 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
18
cli/napi_sym/Cargo.toml
Normal 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
46
cli/napi_sym/lib.rs
Normal 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())
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
|
@ -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
18
ext/napi/Cargo.toml
Normal 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
103
ext/napi/function.rs
Normal 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
607
ext/napi/lib.rs
Normal 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
|
||||||
|
})
|
||||||
|
}
|
|
@ -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) {
|
||||||
|
|
|
@ -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" }
|
||||||
|
|
|
@ -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
|
||||||
];
|
];
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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(),
|
||||||
|
|
|
@ -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
4
test_napi/.gitignore
vendored
Normal file
|
@ -0,0 +1,4 @@
|
||||||
|
package-lock.json
|
||||||
|
|
||||||
|
# Test generated artifacts
|
||||||
|
.swc
|
20
test_napi/Cargo.toml
Normal file
20
test_napi/Cargo.toml
Normal 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
19
test_napi/array_test.js
Normal 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
16
test_napi/async_test.js
Normal 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
7
test_napi/build.rs
Normal 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();
|
||||||
|
}
|
38
test_napi/callback_test.js
Normal file
38
test_napi/callback_test.js
Normal 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
74
test_napi/coerce_test.js
Normal 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
20
test_napi/common.js
Normal 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
18
test_napi/numbers_test.js
Normal 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,
|
||||||
|
);
|
||||||
|
});
|
17
test_napi/object_wrap_test.js
Normal file
17
test_napi/object_wrap_test.js
Normal 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
34
test_napi/promise_test.js
Normal 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);
|
||||||
|
});
|
15
test_napi/properties_test.js
Normal file
15
test_napi/properties_test.js
Normal 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
73
test_napi/src/array.rs
Normal 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
112
test_napi/src/async.rs
Normal 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
113
test_napi/src/callback.rs
Normal 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
71
test_napi/src/coerce.rs
Normal 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
78
test_napi/src/lib.rs
Normal 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
55
test_napi/src/numbers.rs
Normal 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())
|
||||||
|
};
|
||||||
|
}
|
154
test_napi/src/object_wrap.rs
Normal file
154
test_napi/src/object_wrap.rs
Normal 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
76
test_napi/src/promise.rs
Normal 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())
|
||||||
|
};
|
||||||
|
}
|
89
test_napi/src/properties.rs
Normal file
89
test_napi/src/properties.rs
Normal 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
45
test_napi/src/strings.rs
Normal 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())
|
||||||
|
};
|
||||||
|
}
|
53
test_napi/src/typedarray.rs
Normal file
53
test_napi/src/typedarray.rs
Normal 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
15
test_napi/strings_test.js
Normal 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("🦕"), "🦕");
|
||||||
|
});
|
45
test_napi/tests/napi_tests.rs
Normal file
45
test_napi/tests/napi_tests.rs
Normal 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());
|
||||||
|
}
|
12
test_napi/typedarray_test.js
Normal file
12
test_napi/typedarray_test.js
Normal 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]),
|
||||||
|
);
|
||||||
|
});
|
|
@ -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
12
tools/napi/generate_link_win.js
Executable 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 });
|
148
tools/napi/symbol_exports.json
Normal file
148
tools/napi/symbol_exports.json
Normal 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"
|
||||||
|
]
|
||||||
|
}
|
Loading…
Reference in a new issue