mirror of
https://github.com/denoland/deno.git
synced 2024-11-21 15:04:11 -05:00
parent
44a4e2bcca
commit
21736392dc
21 changed files with 4797 additions and 3317 deletions
|
@ -3,11 +3,8 @@
|
|||
This directory contains source for Deno's Node-API implementation. It depends on
|
||||
`napi_sym` and `deno_napi`.
|
||||
|
||||
- [`async.rs`](./async.rs) - Asynchronous work related functions.
|
||||
- [`env.rs`](./env.rs) - Environment related functions.
|
||||
- [`js_native_api.rs`](./js_native_api.rs) - V8/JS related functions.
|
||||
- [`thread_safe_function.rs`](./threadsafe_functions.rs) - Thread safe function
|
||||
related functions.
|
||||
Files are generally organized the same as in Node.js's implementation to ease in
|
||||
ensuring compatibility.
|
||||
|
||||
## Adding a new function
|
||||
|
||||
|
|
|
@ -1,102 +0,0 @@
|
|||
// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license.
|
||||
|
||||
use deno_runtime::deno_napi::*;
|
||||
|
||||
use crate::check_env;
|
||||
use crate::napi::threadsafe_functions::SendPtr;
|
||||
|
||||
#[repr(C)]
|
||||
pub struct AsyncWork {
|
||||
pub data: *mut c_void,
|
||||
pub execute: napi_async_execute_callback,
|
||||
pub complete: napi_async_complete_callback,
|
||||
}
|
||||
|
||||
unsafe impl Send for AsyncWork {}
|
||||
unsafe impl Sync for AsyncWork {}
|
||||
|
||||
#[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,
|
||||
) -> napi_status {
|
||||
let mut work = AsyncWork {
|
||||
data,
|
||||
execute,
|
||||
complete,
|
||||
};
|
||||
let work_box = Box::new(work);
|
||||
*result = transmute::<*mut AsyncWork, _>(Box::into_raw(work_box));
|
||||
napi_ok
|
||||
}
|
||||
|
||||
#[napi_sym::napi_sym]
|
||||
fn napi_cancel_async_work(
|
||||
_env: &mut Env,
|
||||
_async_work: napi_async_work,
|
||||
) -> napi_status {
|
||||
napi_ok
|
||||
}
|
||||
|
||||
/// Frees a previously allocated work object.
|
||||
#[napi_sym::napi_sym]
|
||||
fn napi_delete_async_work(
|
||||
_env: &mut Env,
|
||||
work: napi_async_work,
|
||||
) -> napi_status {
|
||||
let work = Box::from_raw(work as *mut AsyncWork);
|
||||
drop(work);
|
||||
|
||||
napi_ok
|
||||
}
|
||||
|
||||
#[napi_sym::napi_sym]
|
||||
fn napi_queue_async_work(
|
||||
env_ptr: *mut Env,
|
||||
work: napi_async_work,
|
||||
) -> napi_status {
|
||||
let work: &AsyncWork = &*(work as *const AsyncWork);
|
||||
let Some(env) = env_ptr.as_mut() else {
|
||||
return napi_invalid_arg;
|
||||
};
|
||||
|
||||
let send_env = SendPtr(env_ptr);
|
||||
|
||||
#[inline(always)]
|
||||
fn do_work(ptr: SendPtr<Env>, work: &AsyncWork) {
|
||||
// SAFETY: This is a valid async work queue call and it runs on the event loop thread
|
||||
unsafe {
|
||||
(work.execute)(ptr.0 as napi_env, work.data);
|
||||
(work.complete)(ptr.0 as napi_env, napi_ok, work.data);
|
||||
}
|
||||
}
|
||||
|
||||
env.add_async_work(move || do_work(send_env, work));
|
||||
|
||||
napi_ok
|
||||
}
|
||||
|
||||
// NOTE: we don't support "async_hooks::AsyncContext" so these APIs are noops.
|
||||
#[napi_sym::napi_sym]
|
||||
fn napi_async_init(
|
||||
env: *mut Env,
|
||||
_async_resource: napi_value,
|
||||
_async_resource_name: napi_value,
|
||||
result: *mut *mut (),
|
||||
) -> napi_status {
|
||||
check_env!(env);
|
||||
*result = ptr::null_mut();
|
||||
napi_ok
|
||||
}
|
||||
|
||||
#[napi_sym::napi_sym]
|
||||
fn napi_async_destroy(env: *mut Env, async_context: *mut ()) -> napi_status {
|
||||
check_env!(env);
|
||||
assert!(async_context.is_null());
|
||||
napi_ok
|
||||
}
|
174
cli/napi/env.rs
174
cli/napi/env.rs
|
@ -1,174 +0,0 @@
|
|||
// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license.
|
||||
|
||||
use deno_runtime::deno_napi::*;
|
||||
use std::os::raw::c_char;
|
||||
|
||||
/// # 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: {location:?}\n{message}"
|
||||
);
|
||||
}
|
||||
|
||||
// napi-3
|
||||
|
||||
#[napi_sym::napi_sym]
|
||||
fn napi_fatal_exception(env: *mut Env, value: napi_value) -> napi_status {
|
||||
let Some(env) = env.as_mut() else {
|
||||
return napi_invalid_arg;
|
||||
};
|
||||
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}");
|
||||
}
|
||||
|
||||
#[napi_sym::napi_sym]
|
||||
fn napi_add_env_cleanup_hook(
|
||||
env: *mut Env,
|
||||
hook: extern "C" fn(*const c_void),
|
||||
data: *const c_void,
|
||||
) -> napi_status {
|
||||
let Some(env) = env.as_mut() else {
|
||||
return napi_invalid_arg;
|
||||
};
|
||||
|
||||
{
|
||||
let mut env_cleanup_hooks = env.cleanup_hooks.borrow_mut();
|
||||
if env_cleanup_hooks
|
||||
.iter()
|
||||
.any(|pair| pair.0 == hook && pair.1 == data)
|
||||
{
|
||||
panic!("Cleanup hook with this data already registered");
|
||||
}
|
||||
env_cleanup_hooks.push((hook, data));
|
||||
}
|
||||
napi_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,
|
||||
) -> napi_status {
|
||||
let Some(env) = env.as_mut() else {
|
||||
return napi_invalid_arg;
|
||||
};
|
||||
|
||||
{
|
||||
let mut env_cleanup_hooks = env.cleanup_hooks.borrow_mut();
|
||||
// Hooks are supposed to be removed in LIFO order
|
||||
let maybe_index = env_cleanup_hooks
|
||||
.iter()
|
||||
.rposition(|&pair| pair.0 == hook && pair.1 == data);
|
||||
|
||||
if let Some(index) = maybe_index {
|
||||
env_cleanup_hooks.remove(index);
|
||||
} else {
|
||||
panic!("Cleanup hook with this data not found");
|
||||
}
|
||||
}
|
||||
|
||||
napi_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,
|
||||
) -> napi_status {
|
||||
// we open scope automatically when it's needed
|
||||
napi_ok
|
||||
}
|
||||
|
||||
#[napi_sym::napi_sym]
|
||||
fn napi_close_callback_scope(
|
||||
_env: *mut Env,
|
||||
_scope: napi_callback_scope,
|
||||
) -> napi_status {
|
||||
// we close scope automatically when it's needed
|
||||
napi_ok
|
||||
}
|
||||
|
||||
#[napi_sym::napi_sym]
|
||||
fn node_api_get_module_file_name(
|
||||
env: *mut Env,
|
||||
result: *mut *const c_char,
|
||||
) -> napi_status {
|
||||
let Some(env) = env.as_mut() else {
|
||||
return napi_invalid_arg;
|
||||
};
|
||||
|
||||
let shared = env.shared();
|
||||
*result = shared.filename;
|
||||
napi_ok
|
||||
}
|
||||
|
||||
#[napi_sym::napi_sym]
|
||||
fn napi_module_register(module: *const NapiModule) -> napi_status {
|
||||
MODULE_TO_REGISTER.with(|cell| {
|
||||
let mut slot = cell.borrow_mut();
|
||||
let prev = slot.replace(module);
|
||||
assert!(prev.is_none());
|
||||
});
|
||||
napi_ok
|
||||
}
|
||||
|
||||
#[napi_sym::napi_sym]
|
||||
fn napi_get_uv_event_loop(
|
||||
_env: *mut Env,
|
||||
uv_loop: *mut *mut (),
|
||||
) -> napi_status {
|
||||
// There is no uv_loop in Deno
|
||||
*uv_loop = std::ptr::null_mut();
|
||||
napi_ok
|
||||
}
|
||||
|
||||
const NODE_VERSION: napi_node_version = napi_node_version {
|
||||
major: 18,
|
||||
minor: 13,
|
||||
patch: 0,
|
||||
release: "Deno\0".as_ptr() as *const c_char,
|
||||
};
|
||||
|
||||
#[napi_sym::napi_sym]
|
||||
fn napi_get_node_version(
|
||||
env: *mut Env,
|
||||
result: *mut *const napi_node_version,
|
||||
) -> napi_status {
|
||||
crate::check_env!(env);
|
||||
crate::check_arg!(env, result);
|
||||
|
||||
*result = &NODE_VERSION as *const napi_node_version;
|
||||
napi_ok
|
||||
}
|
|
@ -1 +1 @@
|
|||
{ "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"; };
|
||||
{ "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"; "node_api_create_external_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"; "node_api_create_external_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"; "node_api_symbol_for"; "napi_coerce_to_string"; "napi_create_type_error"; "napi_fatal_exception"; "napi_create_async_work"; "napi_async_init"; "node_api_create_property_key_utf16"; "napi_type_tag_object"; "napi_check_object_type_tag"; "node_api_post_finalizer"; "napi_add_async_cleanup_hook"; "napi_remove_async_cleanup_hook"; };
|
|
@ -34,6 +34,7 @@ _napi_create_external
|
|||
_napi_get_threadsafe_function_context
|
||||
_napi_get_null
|
||||
_napi_create_string_utf16
|
||||
_node_api_create_external_string_utf16
|
||||
_napi_get_value_bigint_uint64
|
||||
_napi_module_register
|
||||
_napi_is_typedarray
|
||||
|
@ -74,6 +75,7 @@ _napi_adjust_external_memory
|
|||
_napi_release_threadsafe_function
|
||||
_napi_delete_async_work
|
||||
_napi_create_string_latin1
|
||||
_node_api_create_external_string_latin1
|
||||
_napi_is_array
|
||||
_napi_unref_threadsafe_function
|
||||
_napi_throw_error
|
||||
|
@ -137,8 +139,15 @@ _napi_create_uint32
|
|||
_napi_has_element
|
||||
_napi_create_external_arraybuffer
|
||||
_napi_create_symbol
|
||||
_node_api_symbol_for
|
||||
_napi_coerce_to_string
|
||||
_napi_create_type_error
|
||||
_napi_fatal_exception
|
||||
_napi_create_async_work
|
||||
_napi_async_init
|
||||
_napi_async_init
|
||||
_node_api_create_property_key_utf16
|
||||
_napi_type_tag_object
|
||||
_napi_check_object_type_tag
|
||||
_node_api_post_finalizer
|
||||
_napi_add_async_cleanup_hook
|
||||
_napi_remove_async_cleanup_hook
|
|
@ -36,6 +36,7 @@ EXPORTS
|
|||
napi_get_threadsafe_function_context
|
||||
napi_get_null
|
||||
napi_create_string_utf16
|
||||
node_api_create_external_string_utf16
|
||||
napi_get_value_bigint_uint64
|
||||
napi_module_register
|
||||
napi_is_typedarray
|
||||
|
@ -76,6 +77,7 @@ EXPORTS
|
|||
napi_release_threadsafe_function
|
||||
napi_delete_async_work
|
||||
napi_create_string_latin1
|
||||
node_api_create_external_string_latin1
|
||||
napi_is_array
|
||||
napi_unref_threadsafe_function
|
||||
napi_throw_error
|
||||
|
@ -139,8 +141,15 @@ EXPORTS
|
|||
napi_has_element
|
||||
napi_create_external_arraybuffer
|
||||
napi_create_symbol
|
||||
node_api_symbol_for
|
||||
napi_coerce_to_string
|
||||
napi_create_type_error
|
||||
napi_fatal_exception
|
||||
napi_create_async_work
|
||||
napi_async_init
|
||||
napi_async_init
|
||||
node_api_create_property_key_utf16
|
||||
napi_type_tag_object
|
||||
napi_check_object_type_tag
|
||||
node_api_post_finalizer
|
||||
napi_add_async_cleanup_hook
|
||||
napi_remove_async_cleanup_hook
|
File diff suppressed because it is too large
Load diff
|
@ -15,8 +15,6 @@
|
|||
//! 2. Add the function's identifier to this JSON list.
|
||||
//! 3. Finally, run `tools/napi/generate_symbols_list.js` to update `cli/napi/generated_symbol_exports_list_*.def`.
|
||||
|
||||
pub mod r#async;
|
||||
pub mod env;
|
||||
pub mod js_native_api;
|
||||
pub mod threadsafe_functions;
|
||||
pub mod node_api;
|
||||
pub mod util;
|
||||
|
|
1020
cli/napi/node_api.rs
Normal file
1020
cli/napi/node_api.rs
Normal file
File diff suppressed because it is too large
Load diff
|
@ -20,17 +20,12 @@ pub fn napi_sym(_attr: TokenStream, item: TokenStream) -> TokenStream {
|
|||
let name = &func.sig.ident;
|
||||
assert!(
|
||||
exports.symbols.contains(&name.to_string()),
|
||||
"tools/napi/sym/symbol_exports.json is out of sync!"
|
||||
"cli/napi/sym/symbol_exports.json is out of sync!"
|
||||
);
|
||||
|
||||
let block = &func.block;
|
||||
let inputs = &func.sig.inputs;
|
||||
let generics = &func.sig.generics;
|
||||
TokenStream::from(quote! {
|
||||
// SAFETY: it's an NAPI function.
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn #name #generics (#inputs) -> napi_status {
|
||||
#block
|
||||
}
|
||||
crate::napi_wrap! {
|
||||
#func
|
||||
}
|
||||
})
|
||||
}
|
||||
|
|
|
@ -36,6 +36,7 @@
|
|||
"napi_get_threadsafe_function_context",
|
||||
"napi_get_null",
|
||||
"napi_create_string_utf16",
|
||||
"node_api_create_external_string_utf16",
|
||||
"napi_get_value_bigint_uint64",
|
||||
"napi_module_register",
|
||||
"napi_is_typedarray",
|
||||
|
@ -76,6 +77,7 @@
|
|||
"napi_release_threadsafe_function",
|
||||
"napi_delete_async_work",
|
||||
"napi_create_string_latin1",
|
||||
"node_api_create_external_string_latin1",
|
||||
"napi_is_array",
|
||||
"napi_unref_threadsafe_function",
|
||||
"napi_throw_error",
|
||||
|
@ -139,10 +141,17 @@
|
|||
"napi_has_element",
|
||||
"napi_create_external_arraybuffer",
|
||||
"napi_create_symbol",
|
||||
"node_api_symbol_for",
|
||||
"napi_coerce_to_string",
|
||||
"napi_create_type_error",
|
||||
"napi_fatal_exception",
|
||||
"napi_create_async_work",
|
||||
"napi_async_init"
|
||||
"napi_async_init",
|
||||
"node_api_create_property_key_utf16",
|
||||
"napi_type_tag_object",
|
||||
"napi_check_object_type_tag",
|
||||
"node_api_post_finalizer",
|
||||
"napi_add_async_cleanup_hook",
|
||||
"napi_remove_async_cleanup_hook"
|
||||
]
|
||||
}
|
||||
|
|
|
@ -1,290 +0,0 @@
|
|||
// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license.
|
||||
|
||||
use deno_core::futures::channel::mpsc;
|
||||
use deno_core::V8CrossThreadTaskSpawner;
|
||||
use deno_runtime::deno_napi::*;
|
||||
use once_cell::sync::Lazy;
|
||||
use std::mem::forget;
|
||||
use std::ptr::NonNull;
|
||||
use std::sync::atomic::AtomicUsize;
|
||||
use std::sync::Arc;
|
||||
|
||||
#[repr(transparent)]
|
||||
pub struct SendPtr<T>(pub *const T);
|
||||
|
||||
unsafe impl<T> Send for SendPtr<T> {}
|
||||
unsafe impl<T> Sync for SendPtr<T> {}
|
||||
|
||||
static TS_FN_ID_COUNTER: Lazy<AtomicUsize> = Lazy::new(|| AtomicUsize::new(0));
|
||||
|
||||
pub struct TsFn {
|
||||
pub id: usize,
|
||||
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,
|
||||
pub ref_counter: Arc<AtomicUsize>,
|
||||
finalizer: Option<napi_finalize>,
|
||||
finalizer_data: *mut c_void,
|
||||
sender: V8CrossThreadTaskSpawner,
|
||||
tsfn_sender: mpsc::UnboundedSender<ThreadSafeFunctionStatus>,
|
||||
}
|
||||
|
||||
impl Drop for TsFn {
|
||||
fn drop(&mut self) {
|
||||
let env = unsafe { self.env.as_mut().unwrap() };
|
||||
env.remove_threadsafe_function_ref_counter(self.id);
|
||||
if let Some(finalizer) = self.finalizer {
|
||||
unsafe {
|
||||
(finalizer)(self.env as _, self.finalizer_data, ptr::null_mut());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl TsFn {
|
||||
pub fn acquire(&mut self) -> napi_status {
|
||||
self.thread_counter += 1;
|
||||
napi_ok
|
||||
}
|
||||
|
||||
pub fn release(mut self) -> napi_status {
|
||||
self.thread_counter -= 1;
|
||||
if self.thread_counter == 0 {
|
||||
if self
|
||||
.tsfn_sender
|
||||
.unbounded_send(ThreadSafeFunctionStatus::Dead)
|
||||
.is_err()
|
||||
{
|
||||
return napi_generic_failure;
|
||||
}
|
||||
drop(self);
|
||||
} else {
|
||||
forget(self);
|
||||
}
|
||||
napi_ok
|
||||
}
|
||||
|
||||
pub fn ref_(&mut self) -> napi_status {
|
||||
self
|
||||
.ref_counter
|
||||
.fetch_add(1, std::sync::atomic::Ordering::SeqCst);
|
||||
napi_ok
|
||||
}
|
||||
|
||||
pub fn unref(&mut self) -> napi_status {
|
||||
let _ = self.ref_counter.fetch_update(
|
||||
std::sync::atomic::Ordering::SeqCst,
|
||||
std::sync::atomic::Ordering::SeqCst,
|
||||
|x| {
|
||||
if x == 0 {
|
||||
None
|
||||
} else {
|
||||
Some(x - 1)
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
napi_ok
|
||||
}
|
||||
|
||||
pub fn call(&self, data: *mut c_void, is_blocking: bool) {
|
||||
let js_func = self.maybe_func.clone();
|
||||
|
||||
let env = SendPtr(self.env);
|
||||
let context = SendPtr(self.context);
|
||||
let data = SendPtr(data);
|
||||
|
||||
#[inline(always)]
|
||||
fn spawn(
|
||||
sender: &V8CrossThreadTaskSpawner,
|
||||
is_blocking: bool,
|
||||
f: impl FnOnce(&mut v8::HandleScope) + Send + 'static,
|
||||
) {
|
||||
if is_blocking {
|
||||
sender.spawn_blocking(f);
|
||||
} else {
|
||||
sender.spawn(f);
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(call_js_cb) = self.maybe_call_js_cb {
|
||||
if let Some(func) = js_func {
|
||||
let func = SendPtr(func.into_raw().as_ptr());
|
||||
#[inline(always)]
|
||||
fn call(
|
||||
scope: &mut v8::HandleScope,
|
||||
call_js_cb: napi_threadsafe_function_call_js,
|
||||
func: SendPtr<v8::Function>,
|
||||
env: SendPtr<Env>,
|
||||
context: SendPtr<c_void>,
|
||||
data: SendPtr<c_void>,
|
||||
) {
|
||||
// SAFETY: This is a valid global from above
|
||||
let func: v8::Global<v8::Function> = unsafe {
|
||||
v8::Global::<v8::Function>::from_raw(
|
||||
scope,
|
||||
NonNull::new_unchecked(func.0 as _),
|
||||
)
|
||||
};
|
||||
let func: v8::Local<v8::Value> =
|
||||
func.open(scope).to_object(scope).unwrap().into();
|
||||
// SAFETY: env is valid for the duration of the callback.
|
||||
// data lifetime is users responsibility.
|
||||
unsafe {
|
||||
call_js_cb(env.0 as _, func.into(), context.0 as _, data.0 as _)
|
||||
}
|
||||
}
|
||||
spawn(&self.sender, is_blocking, move |scope| {
|
||||
call(scope, call_js_cb, func, env, context, data);
|
||||
});
|
||||
} else {
|
||||
#[inline(always)]
|
||||
fn call(
|
||||
call_js_cb: napi_threadsafe_function_call_js,
|
||||
env: SendPtr<Env>,
|
||||
context: SendPtr<c_void>,
|
||||
data: SendPtr<c_void>,
|
||||
) {
|
||||
// SAFETY: env is valid for the duration of the callback.
|
||||
// data lifetime is users responsibility.
|
||||
unsafe {
|
||||
call_js_cb(
|
||||
env.0 as _,
|
||||
std::mem::zeroed(),
|
||||
context.0 as _,
|
||||
data.0 as _,
|
||||
)
|
||||
}
|
||||
}
|
||||
spawn(&self.sender, is_blocking, move |_| {
|
||||
call(call_js_cb, env, context, data);
|
||||
});
|
||||
}
|
||||
} else {
|
||||
spawn(&self.sender, is_blocking, |_| {
|
||||
// TODO: func.call
|
||||
});
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
#[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_finalize_data: *mut c_void,
|
||||
thread_finalize_cb: Option<napi_finalize>,
|
||||
context: *mut c_void,
|
||||
maybe_call_js_cb: Option<napi_threadsafe_function_call_js>,
|
||||
result: *mut napi_threadsafe_function,
|
||||
) -> napi_status {
|
||||
let Some(env_ref) = env.as_mut() else {
|
||||
return napi_generic_failure;
|
||||
};
|
||||
if initial_thread_count == 0 {
|
||||
return napi_invalid_arg;
|
||||
}
|
||||
|
||||
let mut maybe_func = None;
|
||||
|
||||
if let Some(value) = *func {
|
||||
let Ok(func) = v8::Local::<v8::Function>::try_from(value) else {
|
||||
return napi_function_expected;
|
||||
};
|
||||
maybe_func = Some(v8::Global::new(&mut env_ref.scope(), func));
|
||||
}
|
||||
|
||||
let id = TS_FN_ID_COUNTER.fetch_add(1, std::sync::atomic::Ordering::SeqCst);
|
||||
|
||||
let tsfn = TsFn {
|
||||
id,
|
||||
maybe_func,
|
||||
maybe_call_js_cb,
|
||||
context,
|
||||
thread_counter: initial_thread_count,
|
||||
sender: env_ref.async_work_sender.clone(),
|
||||
finalizer: thread_finalize_cb,
|
||||
finalizer_data: thread_finalize_data,
|
||||
tsfn_sender: env_ref.threadsafe_function_sender.clone(),
|
||||
ref_counter: Arc::new(AtomicUsize::new(1)),
|
||||
env,
|
||||
};
|
||||
|
||||
env_ref
|
||||
.add_threadsafe_function_ref_counter(tsfn.id, tsfn.ref_counter.clone());
|
||||
|
||||
if env_ref
|
||||
.threadsafe_function_sender
|
||||
.unbounded_send(ThreadSafeFunctionStatus::Alive)
|
||||
.is_err()
|
||||
{
|
||||
return napi_generic_failure;
|
||||
}
|
||||
*result = transmute::<Box<TsFn>, _>(Box::new(tsfn));
|
||||
|
||||
napi_ok
|
||||
}
|
||||
|
||||
#[napi_sym::napi_sym]
|
||||
fn napi_acquire_threadsafe_function(
|
||||
tsfn: napi_threadsafe_function,
|
||||
_mode: napi_threadsafe_function_release_mode,
|
||||
) -> napi_status {
|
||||
let tsfn: &mut TsFn = &mut *(tsfn as *mut TsFn);
|
||||
tsfn.acquire()
|
||||
}
|
||||
|
||||
#[napi_sym::napi_sym]
|
||||
fn napi_unref_threadsafe_function(
|
||||
_env: &mut Env,
|
||||
tsfn: napi_threadsafe_function,
|
||||
) -> napi_status {
|
||||
let tsfn: &mut TsFn = &mut *(tsfn as *mut TsFn);
|
||||
tsfn.unref()
|
||||
}
|
||||
|
||||
/// 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,
|
||||
) -> napi_status {
|
||||
let tsfn: &TsFn = &*(func as *const TsFn);
|
||||
*result = tsfn.context;
|
||||
napi_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,
|
||||
) -> napi_status {
|
||||
let tsfn: &TsFn = &*(func as *const TsFn);
|
||||
tsfn.call(data, is_blocking != 0);
|
||||
napi_ok
|
||||
}
|
||||
|
||||
#[napi_sym::napi_sym]
|
||||
fn napi_ref_threadsafe_function(
|
||||
_env: &mut Env,
|
||||
func: napi_threadsafe_function,
|
||||
) -> napi_status {
|
||||
let tsfn: &mut TsFn = &mut *(func as *mut TsFn);
|
||||
tsfn.ref_()
|
||||
}
|
||||
|
||||
#[napi_sym::napi_sym]
|
||||
fn napi_release_threadsafe_function(
|
||||
tsfn: napi_threadsafe_function,
|
||||
_mode: napi_threadsafe_function_release_mode,
|
||||
) -> napi_status {
|
||||
let tsfn: Box<TsFn> = Box::from_raw(tsfn as *mut TsFn);
|
||||
tsfn.release()
|
||||
}
|
284
cli/napi/util.rs
284
cli/napi/util.rs
|
@ -1,8 +1,292 @@
|
|||
// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license.
|
||||
use deno_runtime::deno_napi::*;
|
||||
use libc::INT_MAX;
|
||||
|
||||
#[repr(transparent)]
|
||||
pub struct SendPtr<T>(pub *const T);
|
||||
|
||||
impl<T> SendPtr<T> {
|
||||
// silly function to get around `clippy::redundant_locals`
|
||||
pub fn take(self) -> *const T {
|
||||
self.0
|
||||
}
|
||||
}
|
||||
|
||||
unsafe impl<T> Send for SendPtr<T> {}
|
||||
unsafe impl<T> Sync for SendPtr<T> {}
|
||||
|
||||
pub fn get_array_buffer_ptr(ab: v8::Local<v8::ArrayBuffer>) -> *mut u8 {
|
||||
// SAFETY: Thanks to the null pointer optimization, NonNull<T> and Option<NonNull<T>> are guaranteed
|
||||
// to have the same size and alignment.
|
||||
unsafe { std::mem::transmute(ab.data()) }
|
||||
}
|
||||
|
||||
struct BufferFinalizer {
|
||||
env: *mut Env,
|
||||
finalize_cb: napi_finalize,
|
||||
finalize_data: *mut c_void,
|
||||
finalize_hint: *mut c_void,
|
||||
}
|
||||
|
||||
impl Drop for BufferFinalizer {
|
||||
fn drop(&mut self) {
|
||||
unsafe {
|
||||
(self.finalize_cb)(self.env as _, self.finalize_data, self.finalize_hint);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub extern "C" fn backing_store_deleter_callback(
|
||||
data: *mut c_void,
|
||||
_byte_length: usize,
|
||||
deleter_data: *mut c_void,
|
||||
) {
|
||||
let mut finalizer =
|
||||
unsafe { Box::<BufferFinalizer>::from_raw(deleter_data as _) };
|
||||
|
||||
finalizer.finalize_data = data;
|
||||
|
||||
drop(finalizer);
|
||||
}
|
||||
|
||||
pub fn make_external_backing_store(
|
||||
env: *mut Env,
|
||||
data: *mut c_void,
|
||||
byte_length: usize,
|
||||
finalize_data: *mut c_void,
|
||||
finalize_cb: napi_finalize,
|
||||
finalize_hint: *mut c_void,
|
||||
) -> v8::UniqueRef<v8::BackingStore> {
|
||||
let finalizer = Box::new(BufferFinalizer {
|
||||
env,
|
||||
finalize_data,
|
||||
finalize_cb,
|
||||
finalize_hint,
|
||||
});
|
||||
|
||||
unsafe {
|
||||
v8::ArrayBuffer::new_backing_store_from_ptr(
|
||||
data,
|
||||
byte_length,
|
||||
backing_store_deleter_callback,
|
||||
Box::into_raw(finalizer) as _,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! check_env {
|
||||
($env: expr) => {{
|
||||
let env = $env;
|
||||
if env.is_null() {
|
||||
return napi_invalid_arg;
|
||||
}
|
||||
unsafe { &mut *env }
|
||||
}};
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! return_error_status_if_false {
|
||||
($env: expr, $condition: expr, $status: ident) => {
|
||||
if !$condition {
|
||||
return Err(
|
||||
$crate::napi::util::napi_set_last_error($env, $status).into(),
|
||||
);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! return_status_if_false {
|
||||
($env: expr, $condition: expr, $status: ident) => {
|
||||
if !$condition {
|
||||
return $crate::napi::util::napi_set_last_error($env, $status);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
pub(crate) unsafe fn check_new_from_utf8_len<'s>(
|
||||
env: *mut Env,
|
||||
str_: *const c_char,
|
||||
len: usize,
|
||||
) -> Result<v8::Local<'s, v8::String>, napi_status> {
|
||||
let env = unsafe { &mut *env };
|
||||
return_error_status_if_false!(
|
||||
env,
|
||||
(len == NAPI_AUTO_LENGTH) || len <= INT_MAX as _,
|
||||
napi_invalid_arg
|
||||
);
|
||||
return_error_status_if_false!(env, !str_.is_null(), napi_invalid_arg);
|
||||
let string = if len == NAPI_AUTO_LENGTH {
|
||||
let result = unsafe { std::ffi::CStr::from_ptr(str_ as *const _) }.to_str();
|
||||
return_error_status_if_false!(env, result.is_ok(), napi_generic_failure);
|
||||
result.unwrap()
|
||||
} else {
|
||||
let string = unsafe { std::slice::from_raw_parts(str_ as *const u8, len) };
|
||||
let result = std::str::from_utf8(string);
|
||||
return_error_status_if_false!(env, result.is_ok(), napi_generic_failure);
|
||||
result.unwrap()
|
||||
};
|
||||
let result = {
|
||||
let env = unsafe { &mut *(env as *mut Env) };
|
||||
v8::String::new(&mut env.scope(), string)
|
||||
};
|
||||
return_error_status_if_false!(env, result.is_some(), napi_generic_failure);
|
||||
Ok(result.unwrap())
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub(crate) unsafe fn check_new_from_utf8<'s>(
|
||||
env: *mut Env,
|
||||
str_: *const c_char,
|
||||
) -> Result<v8::Local<'s, v8::String>, napi_status> {
|
||||
unsafe { check_new_from_utf8_len(env, str_, NAPI_AUTO_LENGTH) }
|
||||
}
|
||||
|
||||
pub(crate) unsafe fn v8_name_from_property_descriptor<'s>(
|
||||
env: *mut Env,
|
||||
p: &'s napi_property_descriptor,
|
||||
) -> Result<v8::Local<'s, v8::Name>, napi_status> {
|
||||
if !p.utf8name.is_null() {
|
||||
unsafe { check_new_from_utf8(env, p.utf8name).map(|v| v.into()) }
|
||||
} else {
|
||||
match *p.name {
|
||||
Some(v) => match v.try_into() {
|
||||
Ok(name) => Ok(name),
|
||||
Err(_) => Err(napi_name_expected),
|
||||
},
|
||||
None => Err(napi_name_expected),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn napi_clear_last_error(env: *mut Env) -> napi_status {
|
||||
let env = unsafe { &mut *env };
|
||||
env.last_error.error_code = napi_ok;
|
||||
env.last_error.engine_error_code = 0;
|
||||
env.last_error.engine_reserved = std::ptr::null_mut();
|
||||
env.last_error.error_message = std::ptr::null_mut();
|
||||
napi_ok
|
||||
}
|
||||
|
||||
pub(crate) fn napi_set_last_error(
|
||||
env: *mut Env,
|
||||
error_code: napi_status,
|
||||
) -> napi_status {
|
||||
let env = unsafe { &mut *env };
|
||||
env.last_error.error_code = error_code;
|
||||
error_code
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! status_call {
|
||||
($call: expr) => {
|
||||
let status = $call;
|
||||
if status != napi_ok {
|
||||
return status;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
pub trait Nullable {
|
||||
fn is_null(&self) -> bool;
|
||||
}
|
||||
|
||||
impl<T> Nullable for *mut T {
|
||||
fn is_null(&self) -> bool {
|
||||
(*self).is_null()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Nullable for *const T {
|
||||
fn is_null(&self) -> bool {
|
||||
(*self).is_null()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Nullable for Option<T> {
|
||||
fn is_null(&self) -> bool {
|
||||
self.is_none()
|
||||
}
|
||||
}
|
||||
|
||||
impl<'s> Nullable for napi_value<'s> {
|
||||
fn is_null(&self) -> bool {
|
||||
self.is_none()
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: replace Nullable with some sort of "CheckedUnwrap" trait
|
||||
// *mut T -> &mut MaybeUninit<T>
|
||||
// Option<T> -> T
|
||||
// napi_value -> Local<Value>
|
||||
#[macro_export]
|
||||
macro_rules! check_arg {
|
||||
($env: expr, $ptr: expr) => {
|
||||
$crate::return_status_if_false!(
|
||||
$env,
|
||||
!$crate::napi::util::Nullable::is_null(&$ptr),
|
||||
napi_invalid_arg
|
||||
);
|
||||
};
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! napi_wrap {
|
||||
( $( # $attr:tt )* fn $name:ident $( < $( $x:lifetime ),* > )? ( $env:ident : & $( $lt:lifetime )? mut Env $( , $ident:ident : $ty:ty )* $(,)? ) -> napi_status $body:block ) => {
|
||||
$( # $attr )*
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn $name $( < $( $x ),* > )? ( env_ptr : *mut Env , $( $ident : $ty ),* ) -> napi_status {
|
||||
let env: & $( $lt )? mut Env = $crate::check_env!(env_ptr);
|
||||
|
||||
if env.last_exception.is_some() {
|
||||
return napi_pending_exception;
|
||||
}
|
||||
|
||||
$crate::napi::util::napi_clear_last_error(env);
|
||||
|
||||
let scope_env = unsafe { &mut *env_ptr };
|
||||
let scope = &mut scope_env.scope();
|
||||
let try_catch = &mut v8::TryCatch::new(scope);
|
||||
|
||||
#[inline(always)]
|
||||
fn inner $( < $( $x ),* > )? ( $env: & $( $lt )? mut Env , $( $ident : $ty ),* ) -> napi_status $body
|
||||
|
||||
log::trace!("NAPI ENTER: {}", stringify!($name));
|
||||
|
||||
let result = inner( env, $( $ident ),* );
|
||||
|
||||
log::trace!("NAPI EXIT: {} {}", stringify!($name), result);
|
||||
|
||||
if let Some(exception) = try_catch.exception() {
|
||||
let env = unsafe { &mut *env_ptr };
|
||||
let global = v8::Global::new(env.isolate(), exception);
|
||||
env.last_exception = Some(global);
|
||||
return $crate::napi::util::napi_set_last_error(env_ptr, napi_pending_exception);
|
||||
}
|
||||
|
||||
if result != napi_ok {
|
||||
return $crate::napi::util::napi_set_last_error(env_ptr, result);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
};
|
||||
|
||||
( $( # $attr:tt )* fn $name:ident $( < $( $x:lifetime ),* > )? ( $( $ident:ident : $ty:ty ),* $(,)? ) -> napi_status $body:block ) => {
|
||||
$( # $attr )*
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn $name $( < $( $x ),* > )? ( $( $ident : $ty ),* ) -> napi_status {
|
||||
#[inline(always)]
|
||||
fn inner $( < $( $x ),* > )? ( $( $ident : $ty ),* ) -> napi_status $body
|
||||
|
||||
log::trace!("NAPI ENTER: {}", stringify!($name));
|
||||
|
||||
let result = inner( $( $ident ),* );
|
||||
|
||||
log::trace!("NAPI EXIT: {} {}", stringify!($name), result);
|
||||
|
||||
result
|
||||
}
|
||||
};
|
||||
}
|
||||
|
|
|
@ -27,9 +27,10 @@ impl CallbackInfo {
|
|||
}
|
||||
|
||||
extern "C" fn call_fn(info: *const v8::FunctionCallbackInfo) {
|
||||
let info = unsafe { &*info };
|
||||
let args = v8::FunctionCallbackArguments::from_function_callback_info(info);
|
||||
let mut rv = v8::ReturnValue::from_function_callback_info(info);
|
||||
let callback_info = unsafe { &*info };
|
||||
let args =
|
||||
v8::FunctionCallbackArguments::from_function_callback_info(callback_info);
|
||||
let mut rv = v8::ReturnValue::from_function_callback_info(callback_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());
|
||||
|
@ -43,19 +44,29 @@ extern "C" fn call_fn(info: *const v8::FunctionCallbackInfo) {
|
|||
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 represented as v8::Local<v8::Value> internally.
|
||||
rv.set(unsafe { transmute::<napi_value, v8::Local<v8::Value>>(value) });
|
||||
if let Some(exc) = unsafe { &mut *(info.env as *mut Env) }
|
||||
.last_exception
|
||||
.take()
|
||||
{
|
||||
let scope = unsafe { &mut v8::CallbackScope::new(callback_info) };
|
||||
let exc = v8::Local::new(scope, exc);
|
||||
scope.throw_exception(exc);
|
||||
}
|
||||
if let Some(value) = *value {
|
||||
rv.set(value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(clippy::not_unsafe_ptr_arg_deref)]
|
||||
pub fn create_function<'a>(
|
||||
/// # Safety
|
||||
/// env_ptr must be valid
|
||||
pub unsafe fn create_function<'a>(
|
||||
env_ptr: *mut Env,
|
||||
name: Option<v8::Local<v8::String>>,
|
||||
cb: napi_callback,
|
||||
cb_info: napi_callback_info,
|
||||
) -> v8::Local<'a, v8::Function> {
|
||||
let env: &mut Env = unsafe { &mut *env_ptr };
|
||||
let env = unsafe { &mut *env_ptr };
|
||||
let scope = &mut env.scope();
|
||||
|
||||
let external = v8::External::new(
|
||||
|
@ -74,14 +85,15 @@ pub fn create_function<'a>(
|
|||
function
|
||||
}
|
||||
|
||||
#[allow(clippy::not_unsafe_ptr_arg_deref)]
|
||||
pub fn create_function_template<'a>(
|
||||
/// # Safety
|
||||
/// env_ptr must be valid
|
||||
pub unsafe fn create_function_template<'a>(
|
||||
env_ptr: *mut Env,
|
||||
name: Option<&str>,
|
||||
name: Option<v8::Local<v8::String>>,
|
||||
cb: napi_callback,
|
||||
cb_info: napi_callback_info,
|
||||
) -> v8::Local<'a, v8::FunctionTemplate> {
|
||||
let env: &mut Env = unsafe { &mut *env_ptr };
|
||||
let env = unsafe { &mut *env_ptr };
|
||||
let scope = &mut env.scope();
|
||||
|
||||
let external = v8::External::new(
|
||||
|
@ -92,8 +104,7 @@ pub fn create_function_template<'a>(
|
|||
.data(external.into())
|
||||
.build(scope);
|
||||
|
||||
if let Some(name) = name {
|
||||
let v8str = v8::String::new(scope, name).unwrap();
|
||||
if let Some(v8str) = name {
|
||||
function.set_class_name(v8str);
|
||||
}
|
||||
|
||||
|
|
284
ext/napi/lib.rs
284
ext/napi/lib.rs
|
@ -8,18 +8,14 @@
|
|||
use core::ptr::NonNull;
|
||||
use deno_core::error::type_error;
|
||||
use deno_core::error::AnyError;
|
||||
use deno_core::futures::channel::mpsc;
|
||||
use deno_core::op2;
|
||||
use deno_core::parking_lot::Mutex;
|
||||
use deno_core::ExternalOpsTracker;
|
||||
use deno_core::OpState;
|
||||
use deno_core::V8CrossThreadTaskSpawner;
|
||||
use std::cell::RefCell;
|
||||
use std::ffi::CString;
|
||||
use std::path::Path;
|
||||
use std::path::PathBuf;
|
||||
use std::rc::Rc;
|
||||
use std::sync::atomic::AtomicUsize;
|
||||
use std::sync::Arc;
|
||||
use std::thread_local;
|
||||
|
||||
#[cfg(unix)]
|
||||
|
@ -32,7 +28,6 @@ use libloading::os::windows::*;
|
|||
// `use deno_napi::*`
|
||||
pub use deno_core::v8;
|
||||
pub use std::ffi::CStr;
|
||||
pub use std::mem::transmute;
|
||||
pub use std::os::raw::c_char;
|
||||
pub use std::os::raw::c_void;
|
||||
pub use std::ptr;
|
||||
|
@ -52,6 +47,7 @@ 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 type napi_async_context = *mut c_void;
|
||||
|
||||
pub const napi_ok: napi_status = 0;
|
||||
pub const napi_invalid_arg: napi_status = 1;
|
||||
|
@ -75,6 +71,35 @@ 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;
|
||||
pub const napi_no_external_buffers_allowed: napi_status = 22;
|
||||
pub const napi_cannot_run_js: napi_status = 23;
|
||||
|
||||
pub static ERROR_MESSAGES: &[&CStr] = &[
|
||||
c"",
|
||||
c"Invalid argument",
|
||||
c"An object was expected",
|
||||
c"A string was expected",
|
||||
c"A string or symbol was expected",
|
||||
c"A function was expected",
|
||||
c"A number was expected",
|
||||
c"A boolean was expected",
|
||||
c"An array was expected",
|
||||
c"Unknown failure",
|
||||
c"An exception is pending",
|
||||
c"The async work item was cancelled",
|
||||
c"napi_escape_handle already called on scope",
|
||||
c"Invalid handle scope usage",
|
||||
c"Invalid callback scope usage",
|
||||
c"Thread-safe function queue is full",
|
||||
c"Thread-safe function handle is closing",
|
||||
c"A bigint was expected",
|
||||
c"A date was expected",
|
||||
c"An arraybuffer was expected",
|
||||
c"A detachable arraybuffer was expected",
|
||||
c"Main thread would deadlock",
|
||||
c"External buffers are not allowed",
|
||||
c"Cannot run JavaScript",
|
||||
];
|
||||
|
||||
pub const NAPI_AUTO_LENGTH: usize = usize::MAX;
|
||||
|
||||
|
@ -83,7 +108,9 @@ thread_local! {
|
|||
}
|
||||
|
||||
type napi_addon_register_func =
|
||||
extern "C" fn(env: napi_env, exports: napi_value) -> napi_value;
|
||||
unsafe extern "C" fn(env: napi_env, exports: napi_value) -> napi_value;
|
||||
type napi_register_module_v1 =
|
||||
unsafe extern "C" fn(env: napi_env, exports: napi_value) -> napi_value;
|
||||
|
||||
#[repr(C)]
|
||||
#[derive(Clone)]
|
||||
|
@ -113,7 +140,7 @@ 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 const napi_tsfn_abort: napi_threadsafe_function_release_mode = 1;
|
||||
|
||||
pub type napi_threadsafe_function_call_mode = i32;
|
||||
|
||||
|
@ -153,6 +180,8 @@ 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;
|
||||
|
||||
#[repr(C)]
|
||||
#[derive(Clone, Copy, PartialEq)]
|
||||
pub struct napi_type_tag {
|
||||
pub lower: u64,
|
||||
pub upper: u64,
|
||||
|
@ -187,6 +216,8 @@ pub type napi_threadsafe_function_call_js = unsafe extern "C" fn(
|
|||
pub type napi_async_cleanup_hook =
|
||||
unsafe extern "C" fn(env: napi_env, data: *mut c_void);
|
||||
|
||||
pub type napi_cleanup_hook = unsafe extern "C" fn(data: *mut c_void);
|
||||
|
||||
pub type napi_property_attributes = i32;
|
||||
|
||||
pub const napi_default: napi_property_attributes = 0;
|
||||
|
@ -233,17 +264,9 @@ pub struct napi_node_version {
|
|||
pub trait PendingNapiAsyncWork: FnOnce() + Send + 'static {}
|
||||
impl<T> PendingNapiAsyncWork for T where T: FnOnce() + Send + 'static {}
|
||||
|
||||
pub type ThreadsafeFunctionRefCounters = Vec<(usize, Arc<AtomicUsize>)>;
|
||||
pub struct NapiState {
|
||||
// Thread safe functions.
|
||||
pub active_threadsafe_functions: usize,
|
||||
pub threadsafe_function_receiver:
|
||||
mpsc::UnboundedReceiver<ThreadSafeFunctionStatus>,
|
||||
pub threadsafe_function_sender:
|
||||
mpsc::UnboundedSender<ThreadSafeFunctionStatus>,
|
||||
pub env_cleanup_hooks:
|
||||
Rc<RefCell<Vec<(extern "C" fn(*const c_void), *const c_void)>>>,
|
||||
pub tsfn_ref_counters: Arc<Mutex<ThreadsafeFunctionRefCounters>>,
|
||||
pub env_cleanup_hooks: Rc<RefCell<Vec<(napi_cleanup_hook, *mut c_void)>>>,
|
||||
}
|
||||
|
||||
impl Drop for NapiState {
|
||||
|
@ -267,7 +290,10 @@ impl Drop for NapiState {
|
|||
continue;
|
||||
}
|
||||
|
||||
(hook.0)(hook.1);
|
||||
unsafe {
|
||||
(hook.0)(hook.1);
|
||||
}
|
||||
|
||||
{
|
||||
self
|
||||
.env_cleanup_hooks
|
||||
|
@ -277,38 +303,44 @@ impl Drop for NapiState {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
#[derive(Debug)]
|
||||
pub struct InstanceData {
|
||||
pub data: *mut c_void,
|
||||
pub finalize_cb: Option<napi_finalize>,
|
||||
pub finalize_hint: *mut c_void,
|
||||
}
|
||||
|
||||
#[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 instance_data: Option<InstanceData>,
|
||||
pub napi_wrap: v8::Global<v8::Private>,
|
||||
pub type_tag: v8::Global<v8::Private>,
|
||||
pub finalize: Option<napi_finalize>,
|
||||
pub finalize_hint: *mut c_void,
|
||||
pub filename: *const c_char,
|
||||
pub filename: String,
|
||||
}
|
||||
|
||||
impl EnvShared {
|
||||
pub fn new(napi_wrap: v8::Global<v8::Private>) -> Self {
|
||||
pub fn new(
|
||||
napi_wrap: v8::Global<v8::Private>,
|
||||
type_tag: v8::Global<v8::Private>,
|
||||
filename: String,
|
||||
) -> Self {
|
||||
Self {
|
||||
instance_data: std::ptr::null_mut(),
|
||||
data_finalize: None,
|
||||
data_finalize_hint: std::ptr::null_mut(),
|
||||
instance_data: None,
|
||||
napi_wrap,
|
||||
type_tag,
|
||||
finalize: None,
|
||||
finalize_hint: std::ptr::null_mut(),
|
||||
filename: std::ptr::null(),
|
||||
filename,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub enum ThreadSafeFunctionStatus {
|
||||
Alive,
|
||||
Dead,
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
pub struct Env {
|
||||
context: NonNull<v8::Context>,
|
||||
|
@ -316,46 +348,48 @@ pub struct Env {
|
|||
pub open_handle_scopes: usize,
|
||||
pub shared: *mut EnvShared,
|
||||
pub async_work_sender: V8CrossThreadTaskSpawner,
|
||||
pub threadsafe_function_sender:
|
||||
mpsc::UnboundedSender<ThreadSafeFunctionStatus>,
|
||||
pub cleanup_hooks:
|
||||
Rc<RefCell<Vec<(extern "C" fn(*const c_void), *const c_void)>>>,
|
||||
pub tsfn_ref_counters: Arc<Mutex<ThreadsafeFunctionRefCounters>>,
|
||||
pub cleanup_hooks: Rc<RefCell<Vec<(napi_cleanup_hook, *mut c_void)>>>,
|
||||
pub external_ops_tracker: ExternalOpsTracker,
|
||||
pub last_error: napi_extended_error_info,
|
||||
pub last_exception: Option<v8::Global<v8::Value>>,
|
||||
pub global: NonNull<v8::Value>,
|
||||
pub buffer_constructor: NonNull<v8::Function>,
|
||||
pub report_error: NonNull<v8::Function>,
|
||||
}
|
||||
|
||||
unsafe impl Send for Env {}
|
||||
unsafe impl Sync for Env {}
|
||||
|
||||
impl Env {
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
pub fn new(
|
||||
isolate_ptr: *mut v8::OwnedIsolate,
|
||||
context: v8::Global<v8::Context>,
|
||||
global: v8::Global<v8::Value>,
|
||||
buffer_constructor: v8::Global<v8::Function>,
|
||||
report_error: v8::Global<v8::Function>,
|
||||
sender: V8CrossThreadTaskSpawner,
|
||||
threadsafe_function_sender: mpsc::UnboundedSender<ThreadSafeFunctionStatus>,
|
||||
cleanup_hooks: Rc<
|
||||
RefCell<Vec<(extern "C" fn(*const c_void), *const c_void)>>,
|
||||
>,
|
||||
tsfn_ref_counters: Arc<Mutex<ThreadsafeFunctionRefCounters>>,
|
||||
cleanup_hooks: Rc<RefCell<Vec<(napi_cleanup_hook, *mut c_void)>>>,
|
||||
external_ops_tracker: ExternalOpsTracker,
|
||||
) -> Self {
|
||||
Self {
|
||||
isolate_ptr,
|
||||
context: context.into_raw(),
|
||||
global: global.into_raw(),
|
||||
buffer_constructor: buffer_constructor.into_raw(),
|
||||
report_error: report_error.into_raw(),
|
||||
shared: std::ptr::null_mut(),
|
||||
open_handle_scopes: 0,
|
||||
async_work_sender: sender,
|
||||
threadsafe_function_sender,
|
||||
cleanup_hooks,
|
||||
tsfn_ref_counters,
|
||||
external_ops_tracker,
|
||||
last_error: napi_extended_error_info {
|
||||
error_message: std::ptr::null(),
|
||||
engine_reserved: std::ptr::null_mut(),
|
||||
engine_error_code: 0,
|
||||
error_code: napi_ok,
|
||||
},
|
||||
last_exception: None,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -384,7 +418,9 @@ impl Env {
|
|||
// 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)
|
||||
std::mem::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`
|
||||
|
@ -392,20 +428,12 @@ impl Env {
|
|||
unsafe { v8::CallbackScope::new(context) }
|
||||
}
|
||||
|
||||
pub fn add_threadsafe_function_ref_counter(
|
||||
&mut self,
|
||||
id: usize,
|
||||
counter: Arc<AtomicUsize>,
|
||||
) {
|
||||
let mut counters = self.tsfn_ref_counters.lock();
|
||||
assert!(!counters.iter().any(|(i, _)| *i == id));
|
||||
counters.push((id, counter));
|
||||
pub fn threadsafe_function_ref(&mut self) {
|
||||
self.external_ops_tracker.ref_op();
|
||||
}
|
||||
|
||||
pub fn remove_threadsafe_function_ref_counter(&mut self, id: usize) {
|
||||
let mut counters = self.tsfn_ref_counters.lock();
|
||||
let index = counters.iter().position(|(i, _)| *i == id).unwrap();
|
||||
counters.remove(index);
|
||||
pub fn threadsafe_function_unref(&mut self) {
|
||||
self.external_ops_tracker.unref_op();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -415,14 +443,8 @@ deno_core::extension!(deno_napi,
|
|||
op_napi_open<P>
|
||||
],
|
||||
state = |state| {
|
||||
let (threadsafe_function_sender, threadsafe_function_receiver) =
|
||||
mpsc::unbounded::<ThreadSafeFunctionStatus>();
|
||||
state.put(NapiState {
|
||||
threadsafe_function_sender,
|
||||
threadsafe_function_receiver,
|
||||
active_threadsafe_functions: 0,
|
||||
env_cleanup_hooks: Rc::new(RefCell::new(vec![])),
|
||||
tsfn_ref_counters: Arc::new(Mutex::new(vec![])),
|
||||
});
|
||||
},
|
||||
);
|
||||
|
@ -441,69 +463,21 @@ impl NapiPermissions for deno_permissions::PermissionsContainer {
|
|||
}
|
||||
}
|
||||
|
||||
/// # Safety
|
||||
///
|
||||
/// This function is unsafe because it dereferences raw pointer Env.
|
||||
/// - The caller must ensure that the pointer is valid.
|
||||
/// - The caller must ensure that the pointer is not freed.
|
||||
pub unsafe fn weak_local(
|
||||
env_ptr: *mut Env,
|
||||
value: v8::Local<v8::Value>,
|
||||
data: *mut c_void,
|
||||
finalize_cb: napi_finalize,
|
||||
finalize_hint: *mut c_void,
|
||||
) -> Option<v8::Local<v8::Value>> {
|
||||
use std::cell::Cell;
|
||||
|
||||
let env = &mut *env_ptr;
|
||||
|
||||
let weak_ptr = Rc::new(Cell::new(None));
|
||||
let scope = &mut env.scope();
|
||||
|
||||
let weak = v8::Weak::with_finalizer(
|
||||
scope,
|
||||
value,
|
||||
Box::new({
|
||||
let weak_ptr = weak_ptr.clone();
|
||||
move |isolate| {
|
||||
finalize_cb(env_ptr as _, data as _, finalize_hint as _);
|
||||
|
||||
// Self-deleting weak.
|
||||
if let Some(weak_ptr) = weak_ptr.get() {
|
||||
let weak: v8::Weak<v8::Value> =
|
||||
unsafe { v8::Weak::from_raw(isolate, Some(weak_ptr)) };
|
||||
drop(weak);
|
||||
}
|
||||
}
|
||||
}),
|
||||
);
|
||||
|
||||
let value = weak.to_local(scope);
|
||||
let raw = weak.into_raw();
|
||||
weak_ptr.set(raw);
|
||||
|
||||
value
|
||||
}
|
||||
|
||||
#[op2]
|
||||
#[op2(reentrant)]
|
||||
fn op_napi_open<NP, 'scope>(
|
||||
scope: &mut v8::HandleScope<'scope>,
|
||||
op_state: Rc<RefCell<OpState>>,
|
||||
#[string] path: String,
|
||||
global: v8::Local<'scope, v8::Value>,
|
||||
buffer_constructor: v8::Local<'scope, v8::Function>,
|
||||
report_error: v8::Local<'scope, v8::Function>,
|
||||
) -> std::result::Result<v8::Local<'scope, v8::Value>, AnyError>
|
||||
where
|
||||
NP: NapiPermissions + 'static,
|
||||
{
|
||||
// We must limit the OpState borrow because this function can trigger a
|
||||
// re-borrow through the NAPI module.
|
||||
let (
|
||||
async_work_sender,
|
||||
tsfn_sender,
|
||||
isolate_ptr,
|
||||
cleanup_hooks,
|
||||
tsfn_ref_counters,
|
||||
) = {
|
||||
let (async_work_sender, isolate_ptr, cleanup_hooks, external_ops_tracker) = {
|
||||
let mut op_state = op_state.borrow_mut();
|
||||
let permissions = op_state.borrow_mut::<NP>();
|
||||
permissions.check(Some(&PathBuf::from(&path)))?;
|
||||
|
@ -511,10 +485,9 @@ where
|
|||
let isolate_ptr = op_state.borrow::<*mut v8::OwnedIsolate>();
|
||||
(
|
||||
op_state.borrow::<V8CrossThreadTaskSpawner>().clone(),
|
||||
napi_state.threadsafe_function_sender.clone(),
|
||||
*isolate_ptr,
|
||||
napi_state.env_cleanup_hooks.clone(),
|
||||
napi_state.tsfn_ref_counters.clone(),
|
||||
op_state.external_ops_tracker.clone(),
|
||||
)
|
||||
};
|
||||
|
||||
|
@ -522,23 +495,25 @@ where
|
|||
let napi_wrap = v8::Private::new(scope, Some(napi_wrap_name));
|
||||
let napi_wrap = v8::Global::new(scope, napi_wrap);
|
||||
|
||||
let type_tag_name = v8::String::new(scope, "type_tag").unwrap();
|
||||
let type_tag = v8::Private::new(scope, Some(type_tag_name));
|
||||
let type_tag = v8::Global::new(scope, type_tag);
|
||||
|
||||
// The `module.exports` object.
|
||||
let exports = v8::Object::new(scope);
|
||||
|
||||
let mut env_shared = EnvShared::new(napi_wrap);
|
||||
let cstr = CString::new(&*path).unwrap();
|
||||
env_shared.filename = cstr.as_ptr();
|
||||
std::mem::forget(cstr);
|
||||
let env_shared = EnvShared::new(napi_wrap, type_tag, path.clone());
|
||||
|
||||
let ctx = scope.get_current_context();
|
||||
let mut env = Env::new(
|
||||
isolate_ptr,
|
||||
v8::Global::new(scope, ctx),
|
||||
v8::Global::new(scope, global),
|
||||
v8::Global::new(scope, buffer_constructor),
|
||||
v8::Global::new(scope, report_error),
|
||||
async_work_sender,
|
||||
tsfn_sender,
|
||||
cleanup_hooks,
|
||||
tsfn_ref_counters,
|
||||
external_ops_tracker,
|
||||
);
|
||||
env.shared = Box::into_raw(Box::new(env_shared));
|
||||
let env_ptr = Box::into_raw(Box::new(env)) as _;
|
||||
|
@ -567,63 +542,30 @@ where
|
|||
slot.take()
|
||||
});
|
||||
|
||||
if let Some(module_to_register) = maybe_module {
|
||||
let maybe_exports = if let Some(module_to_register) = maybe_module {
|
||||
// SAFETY: napi_register_module guarantees that `module_to_register` is valid.
|
||||
let nm = unsafe { &*module_to_register };
|
||||
assert_eq!(nm.nm_version, 1);
|
||||
// SAFETY: we are going blind, calling the register function on the other side.
|
||||
let maybe_exports = unsafe {
|
||||
(nm.nm_register_func)(
|
||||
env_ptr,
|
||||
std::mem::transmute::<v8::Local<v8::Value>, napi_value>(exports.into()),
|
||||
)
|
||||
};
|
||||
|
||||
let exports = if maybe_exports.is_some() {
|
||||
// SAFETY: v8::Local is a pointer to a value and napi_value is also a pointer
|
||||
// to a value, they have the same layout
|
||||
unsafe {
|
||||
std::mem::transmute::<napi_value, v8::Local<v8::Value>>(maybe_exports)
|
||||
}
|
||||
} else {
|
||||
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);
|
||||
return Ok(exports);
|
||||
}
|
||||
|
||||
// Initializer callback.
|
||||
// SAFETY: we are going blind, calling the register function on the other side.
|
||||
|
||||
let maybe_exports = unsafe {
|
||||
let Ok(init) = library
|
||||
.get::<unsafe extern "C" fn(
|
||||
env: napi_env,
|
||||
exports: napi_value,
|
||||
) -> napi_value>(b"napi_register_module_v1") else {
|
||||
return Err(type_error(format!("Unable to find napi_register_module_v1 symbol in {}", path)));
|
||||
};
|
||||
init(
|
||||
env_ptr,
|
||||
std::mem::transmute::<v8::Local<v8::Value>, napi_value>(exports.into()),
|
||||
)
|
||||
};
|
||||
|
||||
let exports = if maybe_exports.is_some() {
|
||||
// SAFETY: v8::Local is a pointer to a value and napi_value is also a pointer
|
||||
// to a value, they have the same layout
|
||||
unsafe {
|
||||
std::mem::transmute::<napi_value, v8::Local<v8::Value>>(maybe_exports)
|
||||
}
|
||||
unsafe { (nm.nm_register_func)(env_ptr, exports.into()) }
|
||||
} else if let Ok(init) = unsafe {
|
||||
library.get::<napi_register_module_v1>(b"napi_register_module_v1")
|
||||
} {
|
||||
// Initializer callback.
|
||||
// SAFETY: we are going blind, calling the register function on the other side.
|
||||
unsafe { init(env_ptr, exports.into()) }
|
||||
} else {
|
||||
exports.into()
|
||||
return Err(type_error(format!(
|
||||
"Unable to find register Node-API module at {}",
|
||||
path
|
||||
)));
|
||||
};
|
||||
|
||||
let exports = maybe_exports.unwrap_or(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);
|
||||
|
||||
Ok(exports)
|
||||
}
|
||||
|
|
|
@ -37,6 +37,19 @@ where
|
|||
}
|
||||
}
|
||||
|
||||
impl<'s, T> From<Option<v8::Local<'s, T>>> for napi_value<'s>
|
||||
where
|
||||
v8::Local<'s, T>: Into<v8::Local<'s, v8::Value>>,
|
||||
{
|
||||
fn from(v: Option<v8::Local<'s, T>>) -> Self {
|
||||
if let Some(v) = v {
|
||||
NapiValue::from(v)
|
||||
} else {
|
||||
Self(None, std::marker::PhantomData)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const _: () = {
|
||||
assert!(
|
||||
std::mem::size_of::<napi_value>() == std::mem::size_of::<*mut c_void>()
|
||||
|
|
|
@ -1103,7 +1103,12 @@ Module._extensions[".node"] = function (module, filename) {
|
|||
if (filename.endsWith("fsevents.node")) {
|
||||
throw new Error("Using fsevents module is currently not supported");
|
||||
}
|
||||
module.exports = op_napi_open(filename, globalThis);
|
||||
module.exports = op_napi_open(
|
||||
filename,
|
||||
globalThis,
|
||||
nodeGlobals.Buffer,
|
||||
reportError,
|
||||
);
|
||||
};
|
||||
|
||||
function createRequireFromPath(filename) {
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license.
|
||||
|
||||
import { Buffer } from "node:buffer";
|
||||
import { assert, libSuffix } from "./common.js";
|
||||
|
||||
const ops = Deno[Deno.internal].core.ops;
|
||||
|
@ -8,7 +9,7 @@ Deno.test("ctr initialization (napi_module_register)", {
|
|||
ignore: Deno.build.os == "windows",
|
||||
}, function () {
|
||||
const path = new URL(`./module.${libSuffix}`, import.meta.url).pathname;
|
||||
const obj = ops.op_napi_open(path, {});
|
||||
const obj = ops.op_napi_open(path, {}, Buffer, reportError);
|
||||
assert(obj != null);
|
||||
assert(typeof obj === "object");
|
||||
});
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license.
|
||||
|
||||
import { Buffer } from "node:buffer";
|
||||
import { assert, assertEquals, loadTestLibrary } from "./common.js";
|
||||
|
||||
const objectWrap = loadTestLibrary();
|
||||
|
@ -30,7 +31,7 @@ Deno.test("napi external finalizer", function () {
|
|||
|
||||
Deno.test("napi external buffer", function () {
|
||||
let buf = objectWrap.test_external_buffer();
|
||||
assertEquals(buf, new Uint8Array([1, 2, 3]));
|
||||
assertEquals(buf, new Buffer([1, 2, 3]));
|
||||
buf = null;
|
||||
});
|
||||
|
||||
|
|
|
@ -11,7 +11,6 @@ use std::ptr;
|
|||
|
||||
pub struct NapiObject {
|
||||
counter: i32,
|
||||
_wrapper: napi_ref,
|
||||
}
|
||||
|
||||
impl NapiObject {
|
||||
|
@ -33,18 +32,14 @@ impl NapiObject {
|
|||
|
||||
assert_napi_ok!(napi_get_value_int32(env, args[0], &mut value));
|
||||
|
||||
let mut wrapper: napi_ref = ptr::null_mut();
|
||||
let obj = Box::new(Self {
|
||||
counter: value,
|
||||
_wrapper: wrapper,
|
||||
});
|
||||
let obj = Box::new(Self { counter: value });
|
||||
assert_napi_ok!(napi_wrap(
|
||||
env,
|
||||
this,
|
||||
Box::into_raw(obj) as *mut c_void,
|
||||
None,
|
||||
ptr::null_mut(),
|
||||
&mut wrapper,
|
||||
ptr::null_mut(),
|
||||
));
|
||||
|
||||
return this;
|
||||
|
|
|
@ -31,11 +31,17 @@ async function getFilesFromGit(baseDir, args) {
|
|||
throw new Error("gitLsFiles failed");
|
||||
}
|
||||
|
||||
const files = output.split("\0").filter((line) => line.length > 0).map(
|
||||
(filePath) => {
|
||||
return Deno.realPathSync(join(baseDir, filePath));
|
||||
},
|
||||
);
|
||||
const files = output
|
||||
.split("\0")
|
||||
.filter((line) => line.length > 0)
|
||||
.map((filePath) => {
|
||||
try {
|
||||
return Deno.realPathSync(join(baseDir, filePath));
|
||||
} catch {
|
||||
return null;
|
||||
}
|
||||
})
|
||||
.filter((filePath) => filePath !== null);
|
||||
|
||||
return files;
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue