diff options
author | Bartek IwaĆczuk <biwanczuk@gmail.com> | 2024-07-23 01:01:31 +0100 |
---|---|---|
committer | GitHub <noreply@github.com> | 2024-07-23 00:01:31 +0000 |
commit | 3f8efe5289d88097ab49e7a8fcda763c2823376b (patch) | |
tree | 736b2f2315a46a81af1ecfec4e5efb4f2a7d3521 /ext/napi | |
parent | 715675565a928a7ac819b89fa40d8b74e7e1c8bc (diff) |
Revert "chore: move all node-api impl to ext (#24662)" (#24680)
This reverts commit d00fbd70258a77a267fe20bdd2c4a028c799b693.
Reverting because, it caused a failure during v1.45.3 publish:
https://github.com/denoland/deno/actions/runs/10048730693/job/27773718095
Diffstat (limited to 'ext/napi')
-rw-r--r-- | ext/napi/Cargo.toml | 3 | ||||
-rw-r--r-- | ext/napi/README.md | 114 | ||||
-rw-r--r-- | ext/napi/generated_symbol_exports_list_linux.def | 1 | ||||
-rw-r--r-- | ext/napi/generated_symbol_exports_list_macos.def | 153 | ||||
-rw-r--r-- | ext/napi/generated_symbol_exports_list_windows.def | 155 | ||||
-rw-r--r-- | ext/napi/js_native_api.rs | 3603 | ||||
-rw-r--r-- | ext/napi/lib.rs | 15 | ||||
-rw-r--r-- | ext/napi/node_api.rs | 999 | ||||
-rw-r--r-- | ext/napi/sym/Cargo.toml | 21 | ||||
-rw-r--r-- | ext/napi/sym/README.md | 37 | ||||
-rw-r--r-- | ext/napi/sym/lib.rs | 31 | ||||
-rw-r--r-- | ext/napi/sym/symbol_exports.json | 157 | ||||
-rw-r--r-- | ext/napi/util.rs | 291 |
13 files changed, 0 insertions, 5580 deletions
diff --git a/ext/napi/Cargo.toml b/ext/napi/Cargo.toml index ef8ab82d5..b00c30889 100644 --- a/ext/napi/Cargo.toml +++ b/ext/napi/Cargo.toml @@ -16,7 +16,4 @@ path = "lib.rs" [dependencies] deno_core.workspace = true deno_permissions.workspace = true -libc.workspace = true libloading = { version = "0.7" } -log.workspace = true -napi_sym.workspace = true diff --git a/ext/napi/README.md b/ext/napi/README.md index 7b359ac6e..e69de29bb 100644 --- a/ext/napi/README.md +++ b/ext/napi/README.md @@ -1,114 +0,0 @@ -# napi - -This directory contains source for Deno's Node-API implementation. It depends on -`napi_sym` and `deno_napi`. - -Files are generally organized the same as in Node.js's implementation to ease in -ensuring compatibility. - -## Adding a new function - -Add the symbol name to -[`cli/napi_sym/symbol_exports.json`](../napi_sym/symbol_exports.json). - -```diff -{ - "symbols": [ - ... - "napi_get_undefined", -- "napi_get_null" -+ "napi_get_null", -+ "napi_get_boolean" - ] -} -``` - -Determine where to place the implementation. `napi_get_boolean` is related to JS -values so we will place it in `js_native_api.rs`. If something is not clear, -just create a new file module. - -See [`napi_sym`](../napi_sym/) for writing the implementation: - -```rust -#[napi_sym::napi_sym] -pub fn napi_get_boolean( - env: *mut Env, - value: bool, - result: *mut napi_value, -) -> Result { - // ... - Ok(()) -} -``` - -Update the generated symbol lists using the script: - -``` -deno run --allow-write tools/napi/generate_symbols_lists.js -``` - -Add a test in [`/tests/napi`](../../tests/napi/). You can also refer to Node.js -test suite for Node-API. - -```js -// tests/napi/boolean_test.js -import { assertEquals, loadTestLibrary } from "./common.js"; -const lib = loadTestLibrary(); -Deno.test("napi get boolean", function () { - assertEquals(lib.test_get_boolean(true), true); - assertEquals(lib.test_get_boolean(false), false); -}); -``` - -```rust -// tests/napi/src/boolean.rs - -use napi_sys::Status::napi_ok; -use napi_sys::ValueType::napi_boolean; -use napi_sys::*; - -extern "C" fn test_boolean( - 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_boolean); - - // Use napi_get_boolean here... - - value -} - -pub fn init(env: napi_env, exports: napi_value) { - let properties = &[crate::new_property!(env, "test_boolean\0", test_boolean)]; - - unsafe { - napi_define_properties(env, exports, properties.len(), properties.as_ptr()) - }; -} -``` - -```diff -// tests/napi/src/lib.rs - -+ mod boolean; - -... - -#[no_mangle] -unsafe extern "C" fn napi_register_module_v1( - env: napi_env, - exports: napi_value, -) -> napi_value { - ... -+ boolean::init(env, exports); - - exports -} -``` - -Run the test using `cargo test -p tests/napi`. diff --git a/ext/napi/generated_symbol_exports_list_linux.def b/ext/napi/generated_symbol_exports_list_linux.def deleted file mode 100644 index 06e94f05b..000000000 --- a/ext/napi/generated_symbol_exports_list_linux.def +++ /dev/null @@ -1 +0,0 @@ -{ "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"; };
\ No newline at end of file diff --git a/ext/napi/generated_symbol_exports_list_macos.def b/ext/napi/generated_symbol_exports_list_macos.def deleted file mode 100644 index cac7100c6..000000000 --- a/ext/napi/generated_symbol_exports_list_macos.def +++ /dev/null @@ -1,153 +0,0 @@ -_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
\ No newline at end of file diff --git a/ext/napi/generated_symbol_exports_list_windows.def b/ext/napi/generated_symbol_exports_list_windows.def deleted file mode 100644 index 5386b46e5..000000000 --- a/ext/napi/generated_symbol_exports_list_windows.def +++ /dev/null @@ -1,155 +0,0 @@ -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 - 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
\ No newline at end of file diff --git a/ext/napi/js_native_api.rs b/ext/napi/js_native_api.rs deleted file mode 100644 index 9301c68ba..000000000 --- a/ext/napi/js_native_api.rs +++ /dev/null @@ -1,3603 +0,0 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. - -#![allow(non_upper_case_globals)] -#![deny(unsafe_op_in_unsafe_fn)] - -const NAPI_VERSION: u32 = 9; - -use crate::*; -use libc::INT_MAX; - -use super::util::check_new_from_utf8; -use super::util::check_new_from_utf8_len; -use super::util::get_array_buffer_ptr; -use super::util::make_external_backing_store; -use super::util::napi_clear_last_error; -use super::util::napi_set_last_error; -use super::util::v8_name_from_property_descriptor; -use crate::check_arg; -use crate::check_env; -use crate::function::create_function; -use crate::function::create_function_template; -use crate::function::CallbackInfo; -use napi_sym::napi_sym; -use std::ptr::NonNull; - -#[derive(Debug, Clone, Copy, PartialEq)] -enum ReferenceOwnership { - Runtime, - Userland, -} - -enum ReferenceState { - Strong(v8::Global<v8::Value>), - Weak(v8::Weak<v8::Value>), -} - -struct Reference { - env: *mut Env, - state: ReferenceState, - ref_count: u32, - ownership: ReferenceOwnership, - finalize_cb: Option<napi_finalize>, - finalize_data: *mut c_void, - finalize_hint: *mut c_void, -} - -impl Reference { - fn new( - env: *mut Env, - value: v8::Local<v8::Value>, - initial_ref_count: u32, - ownership: ReferenceOwnership, - finalize_cb: Option<napi_finalize>, - finalize_data: *mut c_void, - finalize_hint: *mut c_void, - ) -> Box<Self> { - let isolate = unsafe { (*env).isolate() }; - - let mut reference = Box::new(Reference { - env, - state: ReferenceState::Strong(v8::Global::new(isolate, value)), - ref_count: initial_ref_count, - ownership, - finalize_cb, - finalize_data, - finalize_hint, - }); - - if initial_ref_count == 0 { - reference.set_weak(); - } - - reference - } - - fn ref_(&mut self) -> u32 { - self.ref_count += 1; - if self.ref_count == 1 { - self.set_strong(); - } - self.ref_count - } - - fn unref(&mut self) -> u32 { - let old_ref_count = self.ref_count; - if self.ref_count > 0 { - self.ref_count -= 1; - } - if old_ref_count == 1 && self.ref_count == 0 { - self.set_weak(); - } - self.ref_count - } - - fn reset(&mut self) { - self.finalize_cb = None; - self.finalize_data = std::ptr::null_mut(); - self.finalize_hint = std::ptr::null_mut(); - } - - fn set_strong(&mut self) { - if let ReferenceState::Weak(w) = &self.state { - let isolate = unsafe { (*self.env).isolate() }; - if let Some(g) = w.to_global(isolate) { - self.state = ReferenceState::Strong(g); - } - } - } - - fn set_weak(&mut self) { - let reference = self as *mut Reference; - if let ReferenceState::Strong(g) = &self.state { - let cb = Box::new(move |_: &mut v8::Isolate| { - Reference::weak_callback(reference) - }); - let isolate = unsafe { (*self.env).isolate() }; - self.state = - ReferenceState::Weak(v8::Weak::with_finalizer(isolate, g, cb)); - } - } - - fn weak_callback(reference: *mut Reference) { - let reference = unsafe { &mut *reference }; - - let finalize_cb = reference.finalize_cb; - let finalize_data = reference.finalize_data; - let finalize_hint = reference.finalize_hint; - reference.reset(); - - // copy this value before the finalize callback, since - // it might free the reference (which would be a UAF) - let ownership = reference.ownership; - if let Some(finalize_cb) = finalize_cb { - unsafe { - finalize_cb(reference.env as _, finalize_data, finalize_hint); - } - } - - if ownership == ReferenceOwnership::Runtime { - unsafe { drop(Reference::from_raw(reference)) } - } - } - - fn into_raw(r: Box<Reference>) -> *mut Reference { - Box::into_raw(r) - } - - unsafe fn from_raw(r: *mut Reference) -> Box<Reference> { - unsafe { Box::from_raw(r) } - } - - unsafe fn remove(r: *mut Reference) { - let r = unsafe { &mut *r }; - if r.ownership == ReferenceOwnership::Userland { - r.reset(); - } else { - unsafe { drop(Reference::from_raw(r)) } - } - } -} - -#[napi_sym] -fn napi_get_last_error_info( - env: *mut Env, - result: *mut *const napi_extended_error_info, -) -> napi_status { - let env = check_env!(env); - check_arg!(env, result); - - if env.last_error.error_code == napi_ok { - napi_clear_last_error(env); - } else { - env.last_error.error_message = - ERROR_MESSAGES[env.last_error.error_code as usize].as_ptr(); - } - - unsafe { - *result = &env.last_error; - } - - napi_ok -} - -#[napi_sym] -fn napi_create_function<'s>( - env: &'s mut Env, - name: *const c_char, - length: usize, - cb: Option<napi_callback>, - cb_info: napi_callback_info, - result: *mut napi_value<'s>, -) -> napi_status { - let env_ptr = env as *mut Env; - check_arg!(env, result); - check_arg!(env, cb); - - let name = if !name.is_null() { - match unsafe { check_new_from_utf8_len(env, name, length) } { - Ok(s) => Some(s), - Err(status) => return status, - } - } else { - None - }; - - unsafe { - *result = - create_function(&mut env.scope(), env_ptr, name, cb.unwrap(), cb_info) - .into(); - } - - napi_ok -} - -#[napi_sym] -#[allow(clippy::too_many_arguments)] -fn napi_define_class<'s>( - env: &'s mut Env, - utf8name: *const c_char, - length: usize, - constructor: Option<napi_callback>, - callback_data: *mut c_void, - property_count: usize, - properties: *const napi_property_descriptor, - result: *mut napi_value<'s>, -) -> napi_status { - let env_ptr = env as *mut Env; - check_arg!(env, result); - check_arg!(env, constructor); - - if property_count > 0 { - check_arg!(env, properties); - } - - let name = match unsafe { check_new_from_utf8_len(env, utf8name, length) } { - Ok(string) => string, - Err(status) => return status, - }; - - let tpl = create_function_template( - &mut env.scope(), - env_ptr, - Some(name), - constructor.unwrap(), - callback_data, - ); - - let napi_properties: &[napi_property_descriptor] = if property_count > 0 { - unsafe { std::slice::from_raw_parts(properties, property_count) } - } else { - &[] - }; - let mut static_property_count = 0; - - for p in napi_properties { - if p.attributes & napi_static != 0 { - // Will be handled below - static_property_count += 1; - continue; - } - - let name = match unsafe { v8_name_from_property_descriptor(env_ptr, p) } { - Ok(name) => name, - Err(status) => return status, - }; - - if p.getter.is_some() || p.setter.is_some() { - let getter = p.getter.map(|g| { - create_function_template(&mut env.scope(), env_ptr, None, g, p.data) - }); - let setter = p.setter.map(|s| { - create_function_template(&mut env.scope(), env_ptr, None, s, p.data) - }); - - let mut accessor_property = v8::PropertyAttribute::NONE; - if getter.is_some() - && setter.is_some() - && (p.attributes & napi_writable) == 0 - { - accessor_property = - accessor_property | v8::PropertyAttribute::READ_ONLY; - } - if p.attributes & napi_enumerable == 0 { - accessor_property = - accessor_property | v8::PropertyAttribute::DONT_ENUM; - } - if p.attributes & napi_configurable == 0 { - accessor_property = - accessor_property | v8::PropertyAttribute::DONT_DELETE; - } - - let proto = tpl.prototype_template(&mut env.scope()); - proto.set_accessor_property(name, getter, setter, accessor_property); - } else if let Some(method) = p.method { - let function = create_function_template( - &mut env.scope(), - env_ptr, - None, - method, - p.data, - ); - let proto = tpl.prototype_template(&mut env.scope()); - proto.set(name, function.into()); - } else { - let proto = tpl.prototype_template(&mut env.scope()); - proto.set(name, p.value.unwrap().into()); - } - } - - let value: v8::Local<v8::Value> = - tpl.get_function(&mut env.scope()).unwrap().into(); - - unsafe { - *result = value.into(); - } - - if static_property_count > 0 { - let mut static_descriptors = Vec::with_capacity(static_property_count); - - for p in napi_properties { - if p.attributes & napi_static != 0 { - static_descriptors.push(*p); - } - } - - crate::status_call!(unsafe { - napi_define_properties( - env_ptr, - *result, - static_descriptors.len(), - static_descriptors.as_ptr(), - ) - }); - } - - napi_ok -} - -#[napi_sym] -fn napi_get_property_names( - env: *mut Env, - object: napi_value, - result: *mut napi_value, -) -> napi_status { - unsafe { - napi_get_all_property_names( - env, - object, - napi_key_include_prototypes, - napi_key_enumerable | napi_key_skip_symbols, - napi_key_numbers_to_strings, - result, - ) - } -} - -#[napi_sym] -fn napi_get_all_property_names<'s>( - env: &'s mut Env, - object: napi_value, - key_mode: napi_key_collection_mode, - key_filter: napi_key_filter, - key_conversion: napi_key_conversion, - result: *mut napi_value<'s>, -) -> napi_status { - check_arg!(env, result); - - let scope = &mut env.scope(); - - let Some(obj) = object.and_then(|o| o.to_object(scope)) else { - return napi_object_expected; - }; - - let mut filter = v8::PropertyFilter::ALL_PROPERTIES; - - if key_filter & napi_key_writable != 0 { - filter = filter | v8::PropertyFilter::ONLY_WRITABLE; - } - if key_filter & napi_key_enumerable != 0 { - filter = filter | v8::PropertyFilter::ONLY_ENUMERABLE; - } - if key_filter & napi_key_configurable != 0 { - filter = filter | v8::PropertyFilter::ONLY_CONFIGURABLE; - } - if key_filter & napi_key_skip_strings != 0 { - filter = filter | v8::PropertyFilter::SKIP_STRINGS; - } - if key_filter & napi_key_skip_symbols != 0 { - filter = filter | v8::PropertyFilter::SKIP_SYMBOLS; - } - - let key_mode = match key_mode { - napi_key_include_prototypes => v8::KeyCollectionMode::IncludePrototypes, - napi_key_own_only => v8::KeyCollectionMode::OwnOnly, - _ => return napi_invalid_arg, - }; - - let key_conversion = match key_conversion { - napi_key_keep_numbers => v8::KeyConversionMode::KeepNumbers, - napi_key_numbers_to_strings => v8::KeyConversionMode::ConvertToString, - _ => return napi_invalid_arg, - }; - - let filter = v8::GetPropertyNamesArgsBuilder::new() - .mode(key_mode) - .property_filter(filter) - .index_filter(v8::IndexFilter::IncludeIndices) - .key_conversion(key_conversion) - .build(); - - let property_names = match obj.get_property_names(scope, filter) { - Some(n) => n, - None => return napi_generic_failure, - }; - - unsafe { - *result = property_names.into(); - } - - napi_ok -} - -#[napi_sym] -fn napi_set_property( - env: &mut Env, - object: napi_value, - key: napi_value, - value: napi_value, -) -> napi_status { - check_arg!(env, key); - check_arg!(env, value); - - let scope = &mut env.scope(); - - let Some(object) = object.and_then(|o| o.to_object(scope)) else { - return napi_object_expected; - }; - - if object.set(scope, key.unwrap(), value.unwrap()).is_none() { - return napi_generic_failure; - }; - - napi_ok -} - -#[napi_sym] -fn napi_has_property( - env: &mut Env, - object: napi_value, - key: napi_value, - result: *mut bool, -) -> napi_status { - check_arg!(env, key); - check_arg!(env, result); - - let scope = &mut env.scope(); - - let Some(object) = object.and_then(|o| o.to_object(scope)) else { - return napi_object_expected; - }; - - let Some(has) = object.has(scope, key.unwrap()) else { - return napi_generic_failure; - }; - - unsafe { - *result = has; - } - - napi_ok -} - -#[napi_sym] -fn napi_get_property<'s>( - env: &'s mut Env, - object: napi_value, - key: napi_value, - result: *mut napi_value<'s>, -) -> napi_status { - check_arg!(env, key); - check_arg!(env, result); - - let scope = &mut env.scope(); - - let Some(object) = object.and_then(|o| o.to_object(scope)) else { - return napi_object_expected; - }; - - let Some(value) = object.get(scope, key.unwrap()) else { - return napi_generic_failure; - }; - - unsafe { - *result = value.into(); - } - - napi_ok -} - -#[napi_sym] -fn napi_delete_property( - env: &mut Env, - object: napi_value, - key: napi_value, - result: *mut bool, -) -> napi_status { - check_arg!(env, key); - - let scope = &mut env.scope(); - - let Some(object) = object.and_then(|o| o.to_object(scope)) else { - return napi_object_expected; - }; - - let Some(deleted) = object.delete(scope, key.unwrap()) else { - return napi_generic_failure; - }; - - if !result.is_null() { - unsafe { - *result = deleted; - } - } - - napi_ok -} - -#[napi_sym] -fn napi_has_own_property( - env: &mut Env, - object: napi_value, - key: napi_value, - result: *mut bool, -) -> napi_status { - check_arg!(env, key); - check_arg!(env, result); - - let scope = &mut env.scope(); - - let Some(object) = object.and_then(|o| o.to_object(scope)) else { - return napi_object_expected; - }; - - let Ok(key) = v8::Local::<v8::Name>::try_from(key.unwrap()) else { - return napi_name_expected; - }; - - let Some(has_own) = object.has_own_property(scope, key) else { - return napi_generic_failure; - }; - - unsafe { - *result = has_own; - } - - napi_ok -} - -#[napi_sym] -fn napi_has_named_property<'s>( - env: &'s mut Env, - object: napi_value<'s>, - utf8name: *const c_char, - result: *mut bool, -) -> napi_status { - let env_ptr = env as *mut Env; - check_arg!(env, result); - - let Some(object) = object.and_then(|o| o.to_object(&mut env.scope())) else { - return napi_object_expected; - }; - - let key = match unsafe { check_new_from_utf8(env_ptr, utf8name) } { - Ok(key) => key, - Err(status) => return status, - }; - - let Some(has_property) = object.has(&mut env.scope(), key.into()) else { - return napi_generic_failure; - }; - - unsafe { - *result = has_property; - } - - napi_ok -} - -#[napi_sym] -fn napi_set_named_property<'s>( - env: &'s mut Env, - object: napi_value<'s>, - utf8name: *const c_char, - value: napi_value<'s>, -) -> napi_status { - check_arg!(env, value); - let env_ptr = env as *mut Env; - - let Some(object) = object.and_then(|o| o.to_object(&mut env.scope())) else { - return napi_object_expected; - }; - - let key = match unsafe { check_new_from_utf8(env_ptr, utf8name) } { - Ok(key) => key, - Err(status) => return status, - }; - - let value = value.unwrap(); - - if !object - .set(&mut env.scope(), key.into(), value) - .unwrap_or(false) - { - return napi_generic_failure; - } - - napi_ok -} - -#[napi_sym] -fn napi_get_named_property<'s>( - env: &'s mut Env, - object: napi_value<'s>, - utf8name: *const c_char, - result: *mut napi_value<'s>, -) -> napi_status { - check_arg!(env, result); - let env_ptr = env as *mut Env; - - let Some(object) = object.and_then(|o| o.to_object(&mut env.scope())) else { - return napi_object_expected; - }; - - let key = match unsafe { check_new_from_utf8(env_ptr, utf8name) } { - Ok(key) => key, - Err(status) => return status, - }; - - let Some(value) = object.get(&mut env.scope(), key.into()) else { - return napi_generic_failure; - }; - - unsafe { - *result = value.into(); - } - - napi_ok -} - -#[napi_sym] -fn napi_set_element<'s>( - env: &'s mut Env, - object: napi_value<'s>, - index: u32, - value: napi_value<'s>, -) -> napi_status { - check_arg!(env, value); - - let scope = &mut env.scope(); - - let Some(object) = object.and_then(|o| o.to_object(scope)) else { - return napi_object_expected; - }; - - if !object - .set_index(scope, index, value.unwrap()) - .unwrap_or(false) - { - return napi_generic_failure; - } - - napi_ok -} - -#[napi_sym] -fn napi_has_element( - env: &mut Env, - object: napi_value, - index: u32, - result: *mut bool, -) -> napi_status { - check_arg!(env, result); - - let scope = &mut env.scope(); - - let Some(object) = object.and_then(|o| o.to_object(scope)) else { - return napi_object_expected; - }; - - let Some(has) = object.has_index(scope, index) else { - return napi_generic_failure; - }; - - unsafe { - *result = has; - } - - napi_ok -} - -#[napi_sym] -fn napi_get_element<'s>( - env: &'s mut Env, - object: napi_value, - index: u32, - result: *mut napi_value<'s>, -) -> napi_status { - check_arg!(env, result); - - let scope = &mut env.scope(); - - let Some(object) = object.and_then(|o| o.to_object(scope)) else { - return napi_object_expected; - }; - - let Some(value) = object.get_index(scope, index) else { - return napi_generic_failure; - }; - - unsafe { - *result = value.into(); - } - - napi_ok -} - -#[napi_sym] -fn napi_delete_element( - env: &mut Env, - object: napi_value, - index: u32, - result: *mut bool, -) -> napi_status { - let scope = &mut env.scope(); - - let Some(object) = object.and_then(|o| o.to_object(scope)) else { - return napi_object_expected; - }; - - let Some(deleted) = object.delete_index(scope, index) else { - return napi_generic_failure; - }; - - if !result.is_null() { - unsafe { - *result = deleted; - } - } - - napi_ok -} - -#[napi_sym] -fn napi_define_properties( - env: &mut Env, - object: napi_value, - property_count: usize, - properties: *const napi_property_descriptor, -) -> napi_status { - let env_ptr = env as *mut Env; - - if property_count > 0 { - check_arg!(env, properties); - } - - let scope = &mut env.scope(); - - let Some(object) = object.and_then(|o| o.to_object(scope)) else { - return napi_object_expected; - }; - - let properties = if property_count == 0 { - &[] - } else { - unsafe { std::slice::from_raw_parts(properties, property_count) } - }; - for property in properties { - let property_name = - match unsafe { v8_name_from_property_descriptor(env_ptr, property) } { - Ok(name) => name, - Err(status) => return status, - }; - - let writable = property.attributes & napi_writable != 0; - let enumerable = property.attributes & napi_enumerable != 0; - let configurable = property.attributes & napi_configurable != 0; - - if property.getter.is_some() || property.setter.is_some() { - let local_getter: v8::Local<v8::Value> = if let Some(getter) = - property.getter - { - create_function(&mut env.scope(), env_ptr, None, getter, property.data) - .into() - } else { - v8::undefined(scope).into() - }; - let local_setter: v8::Local<v8::Value> = if let Some(setter) = - property.setter - { - create_function(&mut env.scope(), env_ptr, None, setter, property.data) - .into() - } else { - v8::undefined(scope).into() - }; - - let mut desc = - v8::PropertyDescriptor::new_from_get_set(local_getter, local_setter); - desc.set_enumerable(enumerable); - desc.set_configurable(configurable); - - if !object - .define_property(scope, property_name, &desc) - .unwrap_or(false) - { - return napi_invalid_arg; - } - } else if let Some(method) = property.method { - let method: v8::Local<v8::Value> = { - let function = create_function( - &mut env.scope(), - env_ptr, - None, - method, - property.data, - ); - function.into() - }; - - let mut desc = - v8::PropertyDescriptor::new_from_value_writable(method, writable); - desc.set_enumerable(enumerable); - desc.set_configurable(configurable); - - if !object - .define_property(scope, property_name, &desc) - .unwrap_or(false) - { - return napi_generic_failure; - } - } else { - let value = property.value.unwrap(); - - if enumerable & writable & configurable { - if !object - .create_data_property(scope, property_name, value) - .unwrap_or(false) - { - return napi_invalid_arg; - } - } else { - let mut desc = - v8::PropertyDescriptor::new_from_value_writable(value, writable); - desc.set_enumerable(enumerable); - desc.set_configurable(configurable); - - if !object - .define_property(scope, property_name, &desc) - .unwrap_or(false) - { - return napi_invalid_arg; - } - } - } - } - - napi_ok -} - -#[napi_sym] -fn napi_object_freeze(env: &mut Env, object: napi_value) -> napi_status { - let scope = &mut env.scope(); - - let Some(object) = object.and_then(|o| o.to_object(scope)) else { - return napi_object_expected; - }; - - if !object - .set_integrity_level(scope, v8::IntegrityLevel::Frozen) - .unwrap_or(false) - { - return napi_generic_failure; - } - - napi_ok -} - -#[napi_sym] -fn napi_object_seal(env: &mut Env, object: napi_value) -> napi_status { - let scope = &mut env.scope(); - - let Some(object) = object.and_then(|o| o.to_object(scope)) else { - return napi_object_expected; - }; - - if !object - .set_integrity_level(scope, v8::IntegrityLevel::Sealed) - .unwrap_or(false) - { - return napi_generic_failure; - } - - napi_ok -} - -#[napi_sym] -fn napi_is_array( - env: *mut Env, - value: napi_value, - result: *mut bool, -) -> napi_status { - let env = check_env!(env); - check_arg!(env, value); - check_arg!(env, result); - - let value = value.unwrap(); - - unsafe { - *result = value.is_array(); - } - - napi_clear_last_error(env) -} - -#[napi_sym] -fn napi_get_array_length( - env: &mut Env, - value: napi_value, - result: *mut u32, -) -> napi_status { - check_arg!(env, value); - check_arg!(env, result); - - let value = value.unwrap(); - - match v8::Local::<v8::Array>::try_from(value) { - Ok(array) => { - unsafe { - *result = array.length(); - } - napi_ok - } - Err(_) => napi_array_expected, - } -} - -#[napi_sym] -fn napi_strict_equals( - env: &mut Env, - lhs: napi_value, - rhs: napi_value, - result: *mut bool, -) -> napi_status { - check_arg!(env, lhs); - check_arg!(env, rhs); - check_arg!(env, result); - - unsafe { - *result = lhs.unwrap().strict_equals(rhs.unwrap()); - } - - napi_ok -} - -#[napi_sym] -fn napi_get_prototype<'s>( - env: &'s mut Env, - object: napi_value, - result: *mut napi_value<'s>, -) -> napi_status { - check_arg!(env, result); - - let scope = &mut env.scope(); - - let Some(object) = object.and_then(|o| o.to_object(scope)) else { - return napi_object_expected; - }; - - let Some(proto) = object.get_prototype(scope) else { - return napi_generic_failure; - }; - - unsafe { - *result = proto.into(); - } - - napi_ok -} - -#[napi_sym] -fn napi_create_object( - env_ptr: *mut Env, - result: *mut napi_value, -) -> napi_status { - let env = check_env!(env_ptr); - check_arg!(env, result); - - unsafe { - *result = v8::Object::new(&mut env.scope()).into(); - } - - return napi_clear_last_error(env_ptr); -} - -#[napi_sym] -fn napi_create_array( - env_ptr: *mut Env, - result: *mut napi_value, -) -> napi_status { - let env = check_env!(env_ptr); - check_arg!(env, result); - - unsafe { - *result = v8::Array::new(&mut env.scope(), 0).into(); - } - - return napi_clear_last_error(env_ptr); -} - -#[napi_sym] -fn napi_create_array_with_length( - env_ptr: *mut Env, - length: usize, - result: *mut napi_value, -) -> napi_status { - let env = check_env!(env_ptr); - check_arg!(env, result); - - unsafe { - *result = v8::Array::new(&mut env.scope(), length as _).into(); - } - - return napi_clear_last_error(env_ptr); -} - -#[napi_sym] -fn napi_create_string_latin1( - env_ptr: *mut Env, - string: *const c_char, - length: usize, - result: *mut napi_value, -) -> napi_status { - let env = check_env!(env_ptr); - if length > 0 { - check_arg!(env, string); - } - crate::return_status_if_false!( - env, - (length == NAPI_AUTO_LENGTH) || length <= INT_MAX as _, - napi_invalid_arg - ); - - let buffer = if length > 0 { - unsafe { - std::slice::from_raw_parts( - string as _, - if length == NAPI_AUTO_LENGTH { - std::ffi::CStr::from_ptr(string).to_bytes().len() - } else { - length - }, - ) - } - } else { - &[] - }; - - let Some(string) = v8::String::new_from_one_byte( - &mut env.scope(), - buffer, - v8::NewStringType::Normal, - ) else { - return napi_set_last_error(env_ptr, napi_generic_failure); - }; - - unsafe { - *result = string.into(); - } - - return napi_clear_last_error(env_ptr); -} - -#[napi_sym] -fn napi_create_string_utf8( - env_ptr: *mut Env, - string: *const c_char, - length: usize, - result: *mut napi_value, -) -> napi_status { - let env = check_env!(env_ptr); - if length > 0 { - check_arg!(env, string); - } - crate::return_status_if_false!( - env, - (length == NAPI_AUTO_LENGTH) || length <= INT_MAX as _, - napi_invalid_arg - ); - - let buffer = if length > 0 { - unsafe { - std::slice::from_raw_parts( - string as _, - if length == NAPI_AUTO_LENGTH { - std::ffi::CStr::from_ptr(string).to_bytes().len() - } else { - length - }, - ) - } - } else { - &[] - }; - - let Some(string) = v8::String::new_from_utf8( - &mut env.scope(), - buffer, - v8::NewStringType::Normal, - ) else { - return napi_set_last_error(env_ptr, napi_generic_failure); - }; - - unsafe { - *result = string.into(); - } - - return napi_clear_last_error(env_ptr); -} - -#[napi_sym] -fn napi_create_string_utf16( - env_ptr: *mut Env, - string: *const u16, - length: usize, - result: *mut napi_value, -) -> napi_status { - let env = check_env!(env_ptr); - if length > 0 { - check_arg!(env, string); - } - crate::return_status_if_false!( - env, - (length == NAPI_AUTO_LENGTH) || length <= INT_MAX as _, - napi_invalid_arg - ); - - let buffer = if length > 0 { - unsafe { - std::slice::from_raw_parts( - string, - if length == NAPI_AUTO_LENGTH { - let mut length = 0; - while *(string.add(length)) != 0 { - length += 1; - } - length - } else { - length - }, - ) - } - } else { - &[] - }; - - let Some(string) = v8::String::new_from_two_byte( - &mut env.scope(), - buffer, - v8::NewStringType::Normal, - ) else { - return napi_set_last_error(env_ptr, napi_generic_failure); - }; - - unsafe { - *result = string.into(); - } - - return napi_clear_last_error(env_ptr); -} - -#[napi_sym] -fn node_api_create_external_string_latin1( - env_ptr: *mut Env, - string: *const c_char, - length: usize, - nogc_finalize_callback: Option<napi_finalize>, - finalize_hint: *mut c_void, - result: *mut napi_value, - copied: *mut bool, -) -> napi_status { - let status = - unsafe { napi_create_string_latin1(env_ptr, string, length, result) }; - - if status == napi_ok { - unsafe { - *copied = true; - } - - if let Some(finalize) = nogc_finalize_callback { - unsafe { - finalize(env_ptr as napi_env, string as *mut c_void, finalize_hint); - } - } - } - - status -} - -#[napi_sym] -fn node_api_create_external_string_utf16( - env_ptr: *mut Env, - string: *const u16, - length: usize, - nogc_finalize_callback: Option<napi_finalize>, - finalize_hint: *mut c_void, - result: *mut napi_value, - copied: *mut bool, -) -> napi_status { - let status = - unsafe { napi_create_string_utf16(env_ptr, string, length, result) }; - - if status == napi_ok { - unsafe { - *copied = true; - } - - if let Some(finalize) = nogc_finalize_callback { - unsafe { - finalize(env_ptr as napi_env, string as *mut c_void, finalize_hint); - } - } - } - - status -} - -#[napi_sym] -fn node_api_create_property_key_utf16( - env_ptr: *mut Env, - string: *const u16, - length: usize, - result: *mut napi_value, -) -> napi_status { - let env = check_env!(env_ptr); - if length > 0 { - check_arg!(env, string); - } - crate::return_status_if_false!( - env, - (length == NAPI_AUTO_LENGTH) || length <= INT_MAX as _, - napi_invalid_arg - ); - - let buffer = if length > 0 { - unsafe { - std::slice::from_raw_parts( - string, - if length == NAPI_AUTO_LENGTH { - let mut length = 0; - while *(string.add(length)) != 0 { - length += 1; - } - length - } else { - length - }, - ) - } - } else { - &[] - }; - - let Some(string) = v8::String::new_from_two_byte( - &mut env.scope(), - buffer, - v8::NewStringType::Internalized, - ) else { - return napi_set_last_error(env_ptr, napi_generic_failure); - }; - - unsafe { - *result = string.into(); - } - - return napi_clear_last_error(env_ptr); -} - -#[napi_sym] -fn napi_create_double( - env_ptr: *mut Env, - value: f64, - result: *mut napi_value, -) -> napi_status { - let env = check_env!(env_ptr); - check_arg!(env, result); - - unsafe { - *result = v8::Number::new(&mut env.scope(), value).into(); - } - - napi_clear_last_error(env_ptr) -} - -#[napi_sym] -fn napi_create_int32( - env_ptr: *mut Env, - value: i32, - result: *mut napi_value, -) -> napi_status { - let env = check_env!(env_ptr); - check_arg!(env, result); - - unsafe { - *result = v8::Integer::new(&mut env.scope(), value).into(); - } - - napi_clear_last_error(env_ptr) -} - -#[napi_sym] -fn napi_create_uint32( - env_ptr: *mut Env, - value: u32, - result: *mut napi_value, -) -> napi_status { - let env = check_env!(env_ptr); - check_arg!(env, result); - - unsafe { - *result = v8::Integer::new_from_unsigned(&mut env.scope(), value).into(); - } - - napi_clear_last_error(env_ptr) -} - -#[napi_sym] -fn napi_create_int64( - env_ptr: *mut Env, - value: i64, - result: *mut napi_value, -) -> napi_status { - let env = check_env!(env_ptr); - check_arg!(env, result); - - unsafe { - *result = v8::Number::new(&mut env.scope(), value as _).into(); - } - - napi_clear_last_error(env_ptr) -} - -#[napi_sym] -fn napi_create_bigint_int64( - env_ptr: *mut Env, - value: i64, - result: *mut napi_value, -) -> napi_status { - let env = check_env!(env_ptr); - check_arg!(env, result); - - unsafe { - *result = v8::BigInt::new_from_i64(&mut env.scope(), value).into(); - } - - napi_clear_last_error(env_ptr) -} - -#[napi_sym] -fn napi_create_bigint_uint64( - env_ptr: *mut Env, - value: u64, - result: *mut napi_value, -) -> napi_status { - let env = check_env!(env_ptr); - check_arg!(env, result); - - unsafe { - *result = v8::BigInt::new_from_u64(&mut env.scope(), value).into(); - } - - napi_clear_last_error(env_ptr) -} - -#[napi_sym] -fn napi_create_bigint_words<'s>( - env: &'s mut Env, - sign_bit: bool, - word_count: usize, - words: *const u64, - result: *mut napi_value<'s>, -) -> napi_status { - check_arg!(env, words); - check_arg!(env, result); - - if word_count > INT_MAX as _ { - return napi_invalid_arg; - } - - match v8::BigInt::new_from_words(&mut env.scope(), sign_bit, unsafe { - std::slice::from_raw_parts(words, word_count) - }) { - Some(value) => unsafe { - *result = value.into(); - }, - None => { - return napi_generic_failure; - } - } - - napi_ok -} - -#[napi_sym] -fn napi_get_boolean( - env: *mut Env, - value: bool, - result: *mut napi_value, -) -> napi_status { - let env = check_env!(env); - check_arg!(env, result); - - unsafe { - *result = v8::Boolean::new(env.isolate(), value).into(); - } - - return napi_clear_last_error(env); -} - -#[napi_sym] -fn napi_create_symbol( - env_ptr: *mut Env, - description: napi_value, - result: *mut napi_value, -) -> napi_status { - let env = check_env!(env_ptr); - check_arg!(env, result); - - let description = if let Some(d) = *description { - let Some(d) = d.to_string(&mut env.scope()) else { - return napi_set_last_error(env, napi_string_expected); - }; - Some(d) - } else { - None - }; - - unsafe { - *result = v8::Symbol::new(&mut env.scope(), description).into(); - } - - return napi_clear_last_error(env_ptr); -} - -#[napi_sym] -fn node_api_symbol_for( - env: *mut Env, - utf8description: *const c_char, - length: usize, - result: *mut napi_value, -) -> napi_status { - { - let env = check_env!(env); - check_arg!(env, result); - - let description_string = - match unsafe { check_new_from_utf8_len(env, utf8description, length) } { - Ok(s) => s, - Err(status) => return napi_set_last_error(env, status), - }; - - unsafe { - *result = - v8::Symbol::for_key(&mut env.scope(), description_string).into(); - } - } - - napi_clear_last_error(env) -} - -macro_rules! napi_create_error_impl { - ($env_ptr:ident, $code:ident, $msg:ident, $result:ident, $error:ident) => {{ - let env_ptr = $env_ptr; - let code = $code; - let msg = $msg; - let result = $result; - - let env = check_env!(env_ptr); - check_arg!(env, msg); - check_arg!(env, result); - - let Some(message) = - msg.and_then(|v| v8::Local::<v8::String>::try_from(v).ok()) - else { - return napi_set_last_error(env_ptr, napi_string_expected); - }; - - let error = v8::Exception::$error(&mut env.scope(), message); - - if let Some(code) = *code { - let error_obj: v8::Local<v8::Object> = error.try_into().unwrap(); - let code_key = v8::String::new(&mut env.scope(), "code").unwrap(); - if !error_obj - .set(&mut env.scope(), code_key.into(), code) - .unwrap_or(false) - { - return napi_set_last_error(env_ptr, napi_generic_failure); - } - } - - unsafe { - *result = error.into(); - } - - return napi_clear_last_error(env_ptr); - }}; -} - -#[napi_sym] -fn napi_create_error( - env_ptr: *mut Env, - code: napi_value, - msg: napi_value, - result: *mut napi_value, -) -> napi_status { - napi_create_error_impl!(env_ptr, code, msg, result, error) -} - -#[napi_sym] -fn napi_create_type_error( - env_ptr: *mut Env, - code: napi_value, - msg: napi_value, - result: *mut napi_value, -) -> napi_status { - napi_create_error_impl!(env_ptr, code, msg, result, type_error) -} - -#[napi_sym] -fn napi_create_range_error( - env_ptr: *mut Env, - code: napi_value, - msg: napi_value, - result: *mut napi_value, -) -> napi_status { - napi_create_error_impl!(env_ptr, code, msg, result, range_error) -} - -#[napi_sym] -fn node_api_create_syntax_error( - env_ptr: *mut Env, - code: napi_value, - msg: napi_value, - result: *mut napi_value, -) -> napi_status { - napi_create_error_impl!(env_ptr, code, msg, result, syntax_error) -} - -pub fn get_value_type(value: v8::Local<v8::Value>) -> Option<napi_valuetype> { - if value.is_undefined() { - Some(napi_undefined) - } else if value.is_null() { - Some(napi_null) - } else if value.is_external() { - Some(napi_external) - } else if value.is_boolean() { - Some(napi_boolean) - } else if value.is_number() { - Some(napi_number) - } else if value.is_big_int() { - Some(napi_bigint) - } else if value.is_string() { - Some(napi_string) - } else if value.is_symbol() { - Some(napi_symbol) - } else if value.is_function() { - Some(napi_function) - } else if value.is_object() { - Some(napi_object) - } else { - None - } -} - -#[napi_sym] -fn napi_typeof( - env: *mut Env, - value: napi_value, - result: *mut napi_valuetype, -) -> napi_status { - let env = check_env!(env); - check_arg!(env, value); - check_arg!(env, result); - - let Some(ty) = get_value_type(value.unwrap()) else { - return napi_set_last_error(env, napi_invalid_arg); - }; - - unsafe { - *result = ty; - } - - napi_clear_last_error(env) -} - -#[napi_sym] -fn napi_get_undefined(env: *mut Env, result: *mut napi_value) -> napi_status { - let env = check_env!(env); - check_arg!(env, result); - - unsafe { - *result = v8::undefined(&mut env.scope()).into(); - } - - return napi_clear_last_error(env); -} - -#[napi_sym] -fn napi_get_null(env: *mut Env, result: *mut napi_value) -> napi_status { - let env = check_env!(env); - check_arg!(env, result); - - unsafe { - *result = v8::null(&mut env.scope()).into(); - } - - return napi_clear_last_error(env); -} - -#[napi_sym] -fn napi_get_cb_info( - env: *mut Env, - cbinfo: napi_callback_info, - argc: *mut i32, - argv: *mut napi_value, - this_arg: *mut napi_value, - data: *mut *mut c_void, -) -> napi_status { - let env = check_env!(env); - check_arg!(env, cbinfo); - - let cbinfo: &CallbackInfo = unsafe { &*(cbinfo as *const CallbackInfo) }; - let args = unsafe { &*(cbinfo.args as *const v8::FunctionCallbackArguments) }; - - if !argv.is_null() { - check_arg!(env, argc); - let argc = unsafe { *argc as usize }; - for i in 0..argc { - let arg = args.get(i as _); - unsafe { - *argv.add(i) = arg.into(); - } - } - } - - if !argc.is_null() { - unsafe { - *argc = args.length(); - } - } - - if !this_arg.is_null() { - unsafe { - *this_arg = args.this().into(); - } - } - - if !data.is_null() { - unsafe { - *data = cbinfo.cb_info; - } - } - - napi_clear_last_error(env); - napi_ok -} - -#[napi_sym] -fn napi_get_new_target( - env: *mut Env, - cbinfo: napi_callback_info, - result: *mut napi_value, -) -> napi_status { - let env = check_env!(env); - check_arg!(env, cbinfo); - check_arg!(env, result); - - let cbinfo: &CallbackInfo = unsafe { &*(cbinfo as *const CallbackInfo) }; - let args = unsafe { &*(cbinfo.args as *const v8::FunctionCallbackArguments) }; - - unsafe { - *result = args.new_target().into(); - } - - return napi_clear_last_error(env); -} - -#[napi_sym] -fn napi_call_function( - env_ptr: *mut Env, - recv: napi_value, - func: napi_value, - argc: usize, - argv: *const napi_value, - result: *mut napi_value, -) -> napi_status { - let env = check_env!(env_ptr); - check_arg!(env, recv); - let args = if argc > 0 { - check_arg!(env, argv); - unsafe { - std::slice::from_raw_parts(argv as *mut v8::Local<v8::Value>, argc) - } - } else { - &[] - }; - - let Some(func) = - func.and_then(|f| v8::Local::<v8::Function>::try_from(f).ok()) - else { - return napi_set_last_error(env, napi_function_expected); - }; - - let Some(v) = func.call(&mut env.scope(), recv.unwrap(), args) else { - return napi_set_last_error(env_ptr, napi_generic_failure); - }; - - if !result.is_null() { - unsafe { - *result = v.into(); - } - } - - return napi_clear_last_error(env_ptr); -} - -#[napi_sym] -fn napi_get_global(env_ptr: *mut Env, result: *mut napi_value) -> napi_status { - let env = check_env!(env_ptr); - check_arg!(env, result); - - let global = v8::Local::new(&mut env.scope(), &env.global); - unsafe { - *result = global.into(); - } - - return napi_clear_last_error(env_ptr); -} - -#[napi_sym] -fn napi_throw(env: *mut Env, error: napi_value) -> napi_status { - let env = check_env!(env); - check_arg!(env, error); - - if env.last_exception.is_some() { - return napi_pending_exception; - } - - let error = error.unwrap(); - env.scope().throw_exception(error); - let error = v8::Global::new(&mut env.scope(), error); - env.last_exception = Some(error); - - napi_clear_last_error(env) -} - -macro_rules! napi_throw_error_impl { - ($env:ident, $code:ident, $msg:ident, $error:ident) => {{ - let env = check_env!($env); - let env_ptr = env as *mut Env; - let code = $code; - let msg = $msg; - - if env.last_exception.is_some() { - return napi_pending_exception; - } - - let str_ = match unsafe { check_new_from_utf8(env, msg) } { - Ok(s) => s, - Err(status) => return status, - }; - - let error = v8::Exception::$error(&mut env.scope(), str_); - - if !code.is_null() { - let error_obj: v8::Local<v8::Object> = error.try_into().unwrap(); - let code = match unsafe { check_new_from_utf8(env_ptr, code) } { - Ok(s) => s, - Err(status) => return napi_set_last_error(env, status), - }; - let code_key = v8::String::new(&mut env.scope(), "code").unwrap(); - if !error_obj - .set(&mut env.scope(), code_key.into(), code.into()) - .unwrap_or(false) - { - return napi_set_last_error(env, napi_generic_failure); - } - } - - env.scope().throw_exception(error); - let error = v8::Global::new(&mut env.scope(), error); - env.last_exception = Some(error); - - napi_clear_last_error(env) - }}; -} - -#[napi_sym] -fn napi_throw_error( - env: *mut Env, - code: *const c_char, - msg: *const c_char, -) -> napi_status { - napi_throw_error_impl!(env, code, msg, error) -} - -#[napi_sym] -fn napi_throw_type_error( - env: *mut Env, - code: *const c_char, - msg: *const c_char, -) -> napi_status { - napi_throw_error_impl!(env, code, msg, type_error) -} - -#[napi_sym] -fn napi_throw_range_error( - env: *mut Env, - code: *const c_char, - msg: *const c_char, -) -> napi_status { - napi_throw_error_impl!(env, code, msg, range_error) -} - -#[napi_sym] -fn node_api_throw_syntax_error( - env: *mut Env, - code: *const c_char, - msg: *const c_char, -) -> napi_status { - napi_throw_error_impl!(env, code, msg, syntax_error) -} - -#[napi_sym] -fn napi_is_error( - env: *mut Env, - value: napi_value, - result: *mut bool, -) -> napi_status { - let env = check_env!(env); - check_arg!(env, value); - check_arg!(env, result); - - unsafe { - *result = value.unwrap().is_native_error(); - } - - return napi_clear_last_error(env); -} - -#[napi_sym] -fn napi_get_value_double( - env_ptr: *mut Env, - value: napi_value, - result: *mut f64, -) -> napi_status { - let env = check_env!(env_ptr); - check_arg!(env, value); - check_arg!(env, result); - - let Some(number) = - value.and_then(|v| v8::Local::<v8::Number>::try_from(v).ok()) - else { - return napi_set_last_error(env_ptr, napi_number_expected); - }; - - unsafe { - *result = number.value(); - } - - return napi_clear_last_error(env_ptr); -} - -#[napi_sym] -fn napi_get_value_int32( - env_ptr: *mut Env, - value: napi_value, - result: *mut i32, -) -> napi_status { - let env = check_env!(env_ptr); - check_arg!(env, value); - check_arg!(env, result); - - let Some(value) = value.unwrap().int32_value(&mut env.scope()) else { - return napi_set_last_error(env, napi_number_expected); - }; - - unsafe { - *result = value; - } - - return napi_clear_last_error(env_ptr); -} - -#[napi_sym] -fn napi_get_value_uint32( - env_ptr: *mut Env, - value: napi_value, - result: *mut u32, -) -> napi_status { - let env = check_env!(env_ptr); - check_arg!(env, value); - check_arg!(env, result); - - let Some(value) = value.unwrap().uint32_value(&mut env.scope()) else { - return napi_set_last_error(env, napi_number_expected); - }; - - unsafe { - *result = value; - } - - return napi_clear_last_error(env_ptr); -} - -#[napi_sym] -fn napi_get_value_int64( - env_ptr: *mut Env, - value: napi_value, - result: *mut i64, -) -> napi_status { - let env = check_env!(env_ptr); - check_arg!(env, value); - check_arg!(env, result); - - let Some(number) = - value.and_then(|v| v8::Local::<v8::Number>::try_from(v).ok()) - else { - return napi_set_last_error(env_ptr, napi_number_expected); - }; - - let value = number.value(); - - unsafe { - if value.is_finite() { - *result = value as _; - } else { - *result = 0; - } - } - - return napi_clear_last_error(env_ptr); -} - -#[napi_sym] -fn napi_get_value_bigint_int64( - env_ptr: *mut Env, - value: napi_value, - result: *mut i64, - lossless: *mut bool, -) -> napi_status { - let env = check_env!(env_ptr); - check_arg!(env, value); - check_arg!(env, result); - check_arg!(env, lossless); - - let Some(bigint) = - value.and_then(|v| v8::Local::<v8::BigInt>::try_from(v).ok()) - else { - return napi_set_last_error(env_ptr, napi_bigint_expected); - }; - - let (result_, lossless_) = bigint.i64_value(); - - unsafe { - *result = result_; - *lossless = lossless_; - } - - return napi_clear_last_error(env_ptr); -} - -#[napi_sym] -fn napi_get_value_bigint_uint64( - env_ptr: *mut Env, - value: napi_value, - result: *mut u64, - lossless: *mut bool, -) -> napi_status { - let env = check_env!(env_ptr); - check_arg!(env, value); - check_arg!(env, result); - check_arg!(env, lossless); - - let Some(bigint) = - value.and_then(|v| v8::Local::<v8::BigInt>::try_from(v).ok()) - else { - return napi_set_last_error(env_ptr, napi_bigint_expected); - }; - - let (result_, lossless_) = bigint.u64_value(); - - unsafe { - *result = result_; - *lossless = lossless_; - } - - return napi_clear_last_error(env_ptr); -} - -#[napi_sym] -fn napi_get_value_bigint_words( - env_ptr: *mut Env, - value: napi_value, - sign_bit: *mut i32, - word_count: *mut usize, - words: *mut u64, -) -> napi_status { - let env = check_env!(env_ptr); - check_arg!(env, value); - check_arg!(env, word_count); - - let Some(bigint) = - value.and_then(|v| v8::Local::<v8::BigInt>::try_from(v).ok()) - else { - return napi_set_last_error(env_ptr, napi_bigint_expected); - }; - - let word_count_int; - - if sign_bit.is_null() && words.is_null() { - word_count_int = bigint.word_count(); - } else { - check_arg!(env, sign_bit); - check_arg!(env, words); - let out_words = - unsafe { std::slice::from_raw_parts_mut(words, *word_count) }; - let (sign, slice_) = bigint.to_words_array(out_words); - word_count_int = slice_.len(); - unsafe { - *sign_bit = sign as i32; - } - } - - unsafe { - *word_count = word_count_int; - } - - return napi_clear_last_error(env_ptr); -} - -#[napi_sym] -fn napi_get_value_bool( - env_ptr: *mut Env, - value: napi_value, - result: *mut bool, -) -> napi_status { - let env = check_env!(env_ptr); - check_arg!(env, value); - check_arg!(env, result); - - let Some(boolean) = - value.and_then(|v| v8::Local::<v8::Boolean>::try_from(v).ok()) - else { - return napi_set_last_error(env_ptr, napi_boolean_expected); - }; - - unsafe { - *result = boolean.is_true(); - } - - return napi_clear_last_error(env_ptr); -} - -#[napi_sym] -fn napi_get_value_string_latin1( - env_ptr: *mut Env, - value: napi_value, - buf: *mut c_char, - bufsize: usize, - result: *mut usize, -) -> napi_status { - let env = check_env!(env_ptr); - check_arg!(env, value); - - let Some(value) = - value.and_then(|v| v8::Local::<v8::String>::try_from(v).ok()) - else { - return napi_set_last_error(env_ptr, napi_string_expected); - }; - - if buf.is_null() { - check_arg!(env, result); - unsafe { - *result = value.length(); - } - } else if bufsize != 0 { - let buffer = - unsafe { std::slice::from_raw_parts_mut(buf as _, bufsize - 1) }; - let copied = value.write_one_byte( - &mut env.scope(), - buffer, - 0, - v8::WriteOptions::NO_NULL_TERMINATION, - ); - unsafe { - buf.add(copied).write(0); - } - if !result.is_null() { - unsafe { - *result = copied; - } - } - } else if !result.is_null() { - unsafe { - *result = 0; - } - } - - napi_clear_last_error(env_ptr) -} - -#[napi_sym] -fn napi_get_value_string_utf8( - env_ptr: *mut Env, - value: napi_value, - buf: *mut u8, - bufsize: usize, - result: *mut usize, -) -> napi_status { - let env = check_env!(env_ptr); - check_arg!(env, value); - - let Some(value) = - value.and_then(|v| v8::Local::<v8::String>::try_from(v).ok()) - else { - return napi_set_last_error(env_ptr, napi_string_expected); - }; - - if buf.is_null() { - check_arg!(env, result); - unsafe { - *result = value.utf8_length(env.isolate()); - } - } else if bufsize != 0 { - let buffer = - unsafe { std::slice::from_raw_parts_mut(buf as _, bufsize - 1) }; - let copied = value.write_utf8( - &mut env.scope(), - buffer, - None, - v8::WriteOptions::REPLACE_INVALID_UTF8 - | v8::WriteOptions::NO_NULL_TERMINATION, - ); - unsafe { - buf.add(copied).write(0); - } - if !result.is_null() { - unsafe { - *result = copied; - } - } - } else if !result.is_null() { - unsafe { - *result = 0; - } - } - - napi_clear_last_error(env_ptr) -} - -#[napi_sym] -fn napi_get_value_string_utf16( - env_ptr: *mut Env, - value: napi_value, - buf: *mut u16, - bufsize: usize, - result: *mut usize, -) -> napi_status { - let env = check_env!(env_ptr); - check_arg!(env, value); - - let Some(value) = - value.and_then(|v| v8::Local::<v8::String>::try_from(v).ok()) - else { - return napi_set_last_error(env_ptr, napi_string_expected); - }; - - if buf.is_null() { - check_arg!(env, result); - unsafe { - *result = value.length(); - } - } else if bufsize != 0 { - let buffer = - unsafe { std::slice::from_raw_parts_mut(buf as _, bufsize - 1) }; - let copied = value.write( - &mut env.scope(), - buffer, - 0, - v8::WriteOptions::NO_NULL_TERMINATION, - ); - unsafe { - buf.add(copied).write(0); - } - if !result.is_null() { - unsafe { - *result = copied; - } - } - } else if !result.is_null() { - unsafe { - *result = 0; - } - } - - napi_clear_last_error(env_ptr) -} - -#[napi_sym] -fn napi_coerce_to_bool<'s>( - env: &'s mut Env, - value: napi_value, - result: *mut napi_value<'s>, -) -> napi_status { - check_arg!(env, value); - check_arg!(env, result); - - let coerced = value.unwrap().to_boolean(&mut env.scope()); - - unsafe { - *result = coerced.into(); - } - - napi_ok -} - -#[napi_sym] -fn napi_coerce_to_number<'s>( - env: &'s mut Env, - value: napi_value, - result: *mut napi_value<'s>, -) -> napi_status { - check_arg!(env, value); - check_arg!(env, result); - - let Some(coerced) = value.unwrap().to_number(&mut env.scope()) else { - return napi_number_expected; - }; - - unsafe { - *result = coerced.into(); - } - - napi_ok -} - -#[napi_sym] -fn napi_coerce_to_object<'s>( - env: &'s mut Env, - value: napi_value, - result: *mut napi_value<'s>, -) -> napi_status { - check_arg!(env, value); - check_arg!(env, result); - - let Some(coerced) = value.unwrap().to_object(&mut env.scope()) else { - return napi_object_expected; - }; - - unsafe { - *result = coerced.into(); - } - - napi_ok -} - -#[napi_sym] -fn napi_coerce_to_string<'s>( - env: &'s mut Env, - value: napi_value, - result: *mut napi_value<'s>, -) -> napi_status { - check_arg!(env, value); - check_arg!(env, result); - - let Some(coerced) = value.unwrap().to_string(&mut env.scope()) else { - return napi_string_expected; - }; - - unsafe { - *result = coerced.into(); - } - - napi_ok -} - -#[napi_sym] -fn napi_wrap( - env: &mut Env, - js_object: napi_value, - native_object: *mut c_void, - finalize_cb: Option<napi_finalize>, - finalize_hint: *mut c_void, - result: *mut napi_ref, -) -> napi_status { - check_arg!(env, js_object); - let env_ptr = env as *mut Env; - - let Some(obj) = - js_object.and_then(|v| v8::Local::<v8::Object>::try_from(v).ok()) - else { - return napi_invalid_arg; - }; - - let napi_wrap = v8::Local::new(&mut env.scope(), &env.shared().napi_wrap); - - if obj - .has_private(&mut env.scope(), napi_wrap) - .unwrap_or(false) - { - return napi_invalid_arg; - } - - if !result.is_null() { - check_arg!(env, finalize_cb); - } - - let ownership = if result.is_null() { - ReferenceOwnership::Runtime - } else { - ReferenceOwnership::Userland - }; - let reference = Reference::new( - env_ptr, - obj.into(), - 0, - ownership, - finalize_cb, - native_object, - finalize_hint, - ); - - let reference = Reference::into_raw(reference) as *mut c_void; - - if !result.is_null() { - check_arg!(env, finalize_cb); - unsafe { - *result = reference; - } - } - - let external = v8::External::new(&mut env.scope(), reference); - assert!(obj - .set_private(&mut env.scope(), napi_wrap, external.into()) - .unwrap()); - - napi_ok -} - -fn unwrap( - env: &mut Env, - obj: napi_value, - result: *mut *mut c_void, - keep: bool, -) -> napi_status { - check_arg!(env, obj); - if keep { - check_arg!(env, result); - } - - let Some(obj) = obj.and_then(|v| v8::Local::<v8::Object>::try_from(v).ok()) - else { - return napi_invalid_arg; - }; - - let napi_wrap = v8::Local::new(&mut env.scope(), &env.shared().napi_wrap); - let Some(val) = obj.get_private(&mut env.scope(), napi_wrap) else { - return napi_invalid_arg; - }; - - let Ok(external) = v8::Local::<v8::External>::try_from(val) else { - return napi_invalid_arg; - }; - - let reference = external.value() as *mut Reference; - let reference = unsafe { &mut *reference }; - - if !result.is_null() { - unsafe { - *result = reference.finalize_data; - } - } - - if !keep { - assert!(obj - .delete_private(&mut env.scope(), napi_wrap) - .unwrap_or(false)); - unsafe { Reference::remove(reference) }; - } - - napi_ok -} - -#[napi_sym] -fn napi_unwrap( - env: &mut Env, - obj: napi_value, - result: *mut *mut c_void, -) -> napi_status { - unwrap(env, obj, result, true) -} - -#[napi_sym] -fn napi_remove_wrap( - env: &mut Env, - obj: napi_value, - result: *mut *mut c_void, -) -> napi_status { - unwrap(env, obj, result, false) -} - -struct ExternalWrapper { - data: *mut c_void, - type_tag: Option<napi_type_tag>, -} - -#[napi_sym] -fn napi_create_external<'s>( - env: &'s mut Env, - data: *mut c_void, - finalize_cb: Option<napi_finalize>, - finalize_hint: *mut c_void, - result: *mut napi_value<'s>, -) -> napi_status { - let env_ptr = env as *mut Env; - check_arg!(env, result); - - let wrapper = Box::new(ExternalWrapper { - data, - type_tag: None, - }); - - let wrapper = Box::into_raw(wrapper); - let external = v8::External::new(&mut env.scope(), wrapper as _); - - if let Some(finalize_cb) = finalize_cb { - Reference::into_raw(Reference::new( - env_ptr, - external.into(), - 0, - ReferenceOwnership::Runtime, - Some(finalize_cb), - data, - finalize_hint, - )); - } - - unsafe { - *result = external.into(); - } - - napi_ok -} - -#[napi_sym] -fn napi_type_tag_object( - env: &mut Env, - object_or_external: napi_value, - type_tag: *const napi_type_tag, -) -> napi_status { - check_arg!(env, object_or_external); - check_arg!(env, type_tag); - - let val = object_or_external.unwrap(); - - if let Ok(external) = v8::Local::<v8::External>::try_from(val) { - let wrapper_ptr = external.value() as *mut ExternalWrapper; - let wrapper = unsafe { &mut *wrapper_ptr }; - if wrapper.type_tag.is_some() { - return napi_invalid_arg; - } - wrapper.type_tag = Some(unsafe { *type_tag }); - return napi_ok; - } - - let Some(object) = val.to_object(&mut env.scope()) else { - return napi_object_expected; - }; - - let key = v8::Local::new(&mut env.scope(), &env.shared().type_tag); - - if object.has_private(&mut env.scope(), key).unwrap_or(false) { - return napi_invalid_arg; - } - - let slice = unsafe { std::slice::from_raw_parts(type_tag as *const u64, 2) }; - let Some(tag) = v8::BigInt::new_from_words(&mut env.scope(), false, slice) - else { - return napi_generic_failure; - }; - - if !object - .set_private(&mut env.scope(), key, tag.into()) - .unwrap_or(false) - { - return napi_generic_failure; - } - - napi_ok -} - -#[napi_sym] -fn napi_check_object_type_tag( - env: &mut Env, - object_or_external: napi_value, - type_tag: *const napi_type_tag, - result: *mut bool, -) -> napi_status { - check_arg!(env, object_or_external); - check_arg!(env, type_tag); - check_arg!(env, result); - - let type_tag = unsafe { *type_tag }; - - let val = object_or_external.unwrap(); - - if let Ok(external) = v8::Local::<v8::External>::try_from(val) { - let wrapper_ptr = external.value() as *mut ExternalWrapper; - let wrapper = unsafe { &mut *wrapper_ptr }; - unsafe { - *result = match wrapper.type_tag { - Some(t) => t == type_tag, - None => false, - }; - }; - return napi_ok; - } - - let Some(object) = val.to_object(&mut env.scope()) else { - return napi_object_expected; - }; - - let key = v8::Local::new(&mut env.scope(), &env.shared().type_tag); - - let Some(val) = object.get_private(&mut env.scope(), key) else { - return napi_generic_failure; - }; - - unsafe { - *result = false; - } - - if let Ok(bigint) = v8::Local::<v8::BigInt>::try_from(val) { - let mut words = [0u64; 2]; - let (sign, words) = bigint.to_words_array(&mut words); - if !sign { - let pass = if words.len() == 2 { - type_tag.lower == words[0] && type_tag.upper == words[1] - } else if words.len() == 1 { - type_tag.lower == words[0] && type_tag.upper == 0 - } else if words.is_empty() { - type_tag.lower == 0 && type_tag.upper == 0 - } else { - false - }; - unsafe { - *result = pass; - } - } - } - - napi_ok -} - -#[napi_sym] -fn napi_get_value_external( - env: *mut Env, - value: napi_value, - result: *mut *mut c_void, -) -> napi_status { - let env = check_env!(env); - check_arg!(env, value); - check_arg!(env, result); - - let Some(external) = - value.and_then(|v| v8::Local::<v8::External>::try_from(v).ok()) - else { - return napi_set_last_error(env, napi_invalid_arg); - }; - - let wrapper_ptr = external.value() as *const ExternalWrapper; - let wrapper = unsafe { &*wrapper_ptr }; - - unsafe { - *result = wrapper.data; - } - - napi_clear_last_error(env) -} - -#[napi_sym] -fn napi_create_reference( - env: *mut Env, - value: napi_value, - initial_refcount: u32, - result: *mut napi_ref, -) -> napi_status { - let env = check_env!(env); - check_arg!(env, value); - check_arg!(env, result); - - let value = value.unwrap(); - - let reference = Reference::new( - env, - value, - initial_refcount, - ReferenceOwnership::Userland, - None, - std::ptr::null_mut(), - std::ptr::null_mut(), - ); - - let ptr = Reference::into_raw(reference); - - unsafe { - *result = ptr as _; - } - - napi_clear_last_error(env) -} - -#[napi_sym] -fn napi_delete_reference(env: *mut Env, ref_: napi_ref) -> napi_status { - let env = check_env!(env); - check_arg!(env, ref_); - - let reference = unsafe { Reference::from_raw(ref_ as _) }; - - drop(reference); - - napi_clear_last_error(env) -} - -#[napi_sym] -fn napi_reference_ref( - env: *mut Env, - ref_: napi_ref, - result: *mut u32, -) -> napi_status { - let env = check_env!(env); - check_arg!(env, ref_); - - let reference = unsafe { &mut *(ref_ as *mut Reference) }; - - let count = reference.ref_(); - - if !result.is_null() { - unsafe { - *result = count; - } - } - - napi_clear_last_error(env) -} - -#[napi_sym] -fn napi_reference_unref( - env: *mut Env, - ref_: napi_ref, - result: *mut u32, -) -> napi_status { - let env = check_env!(env); - check_arg!(env, ref_); - - let reference = unsafe { &mut *(ref_ as *mut Reference) }; - - if reference.ref_count == 0 { - return napi_set_last_error(env, napi_generic_failure); - } - - let count = reference.unref(); - - if !result.is_null() { - unsafe { - *result = count; - } - } - - napi_clear_last_error(env) -} - -#[napi_sym] -fn napi_get_reference_value( - env_ptr: *mut Env, - ref_: napi_ref, - result: *mut napi_value, -) -> napi_status { - let env = check_env!(env_ptr); - check_arg!(env, ref_); - check_arg!(env, result); - - let reference = unsafe { &mut *(ref_ as *mut Reference) }; - - let value = match &reference.state { - ReferenceState::Strong(g) => Some(v8::Local::new(&mut env.scope(), g)), - ReferenceState::Weak(w) => w.to_local(&mut env.scope()), - }; - - unsafe { - *result = value.into(); - } - - napi_clear_last_error(env_ptr) -} - -#[napi_sym] -fn napi_open_handle_scope( - env: *mut Env, - _result: *mut napi_handle_scope, -) -> napi_status { - let env = check_env!(env); - napi_clear_last_error(env) -} - -#[napi_sym] -fn napi_close_handle_scope( - env: *mut Env, - _scope: napi_handle_scope, -) -> napi_status { - let env = check_env!(env); - napi_clear_last_error(env) -} - -#[napi_sym] -fn napi_open_escapable_handle_scope( - env: *mut Env, - _result: *mut napi_escapable_handle_scope, -) -> napi_status { - let env = check_env!(env); - napi_clear_last_error(env) -} - -#[napi_sym] -fn napi_close_escapable_handle_scope( - env: *mut Env, - _scope: napi_escapable_handle_scope, -) -> napi_status { - let env = check_env!(env); - napi_clear_last_error(env) -} - -#[napi_sym] -fn napi_escape_handle<'s>( - env: *mut Env, - _scope: napi_escapable_handle_scope, - escapee: napi_value<'s>, - result: *mut napi_value<'s>, -) -> napi_status { - let env = check_env!(env); - - unsafe { - *result = escapee; - } - - napi_clear_last_error(env) -} - -#[napi_sym] -fn napi_new_instance<'s>( - env: &'s mut Env, - constructor: napi_value, - argc: usize, - argv: *const napi_value, - result: *mut napi_value<'s>, -) -> napi_status { - check_arg!(env, constructor); - if argc > 0 { - check_arg!(env, argv); - } - check_arg!(env, result); - - let Some(func) = - constructor.and_then(|v| v8::Local::<v8::Function>::try_from(v).ok()) - else { - return napi_invalid_arg; - }; - - let args = if argc > 0 { - unsafe { - std::slice::from_raw_parts(argv as *mut v8::Local<v8::Value>, argc) - } - } else { - &[] - }; - - let Some(value) = func.new_instance(&mut env.scope(), args) else { - return napi_pending_exception; - }; - - unsafe { - *result = value.into(); - } - - napi_ok -} - -#[napi_sym] -fn napi_instanceof( - env: &mut Env, - object: napi_value, - constructor: napi_value, - result: *mut bool, -) -> napi_status { - check_arg!(env, object); - check_arg!(env, result); - - let Some(ctor) = constructor.and_then(|v| v.to_object(&mut env.scope())) - else { - return napi_object_expected; - }; - - if !ctor.is_function() { - unsafe { - napi_throw_type_error( - env, - c"ERR_NAPI_CONS_FUNCTION".as_ptr(), - c"Constructor must be a function".as_ptr(), - ); - } - return napi_function_expected; - } - - let Some(res) = object.unwrap().instance_of(&mut env.scope(), ctor) else { - return napi_generic_failure; - }; - - unsafe { - *result = res; - } - - napi_ok -} - -#[napi_sym] -fn napi_is_exception_pending( - env_ptr: *mut Env, - result: *mut bool, -) -> napi_status { - let env = check_env!(env_ptr); - check_arg!(env, result); - - unsafe { - *result = env.last_exception.is_some(); - } - - napi_clear_last_error(env_ptr) -} - -#[napi_sym] -fn napi_get_and_clear_last_exception( - env_ptr: *mut Env, - result: *mut napi_value, -) -> napi_status { - let env = check_env!(env_ptr); - check_arg!(env, result); - - let ex: v8::Local<v8::Value> = - if let Some(last_exception) = env.last_exception.take() { - v8::Local::new(&mut env.scope(), last_exception) - } else { - v8::undefined(&mut env.scope()).into() - }; - - unsafe { - *result = ex.into(); - } - - napi_clear_last_error(env_ptr) -} - -#[napi_sym] -fn napi_is_arraybuffer( - env: *mut Env, - value: napi_value, - result: *mut bool, -) -> napi_status { - let env = check_env!(env); - check_arg!(env, value); - check_arg!(env, result); - - unsafe { - *result = value.unwrap().is_array_buffer(); - } - - napi_clear_last_error(env) -} - -#[napi_sym] -fn napi_create_arraybuffer<'s>( - env: &'s mut Env, - len: usize, - data: *mut *mut c_void, - result: *mut napi_value<'s>, -) -> napi_status { - check_arg!(env, result); - - let buffer = v8::ArrayBuffer::new(&mut env.scope(), len); - - if !data.is_null() { - unsafe { - *data = get_array_buffer_ptr(buffer); - } - } - - unsafe { - *result = buffer.into(); - } - - napi_ok -} - -#[napi_sym] -fn napi_create_external_arraybuffer<'s>( - env: &'s mut Env, - data: *mut c_void, - byte_length: usize, - finalize_cb: napi_finalize, - finalize_hint: *mut c_void, - result: *mut napi_value<'s>, -) -> napi_status { - check_arg!(env, result); - - let store = make_external_backing_store( - env, - data, - byte_length, - std::ptr::null_mut(), - finalize_cb, - finalize_hint, - ); - - let ab = - v8::ArrayBuffer::with_backing_store(&mut env.scope(), &store.make_shared()); - let value: v8::Local<v8::Value> = ab.into(); - - unsafe { - *result = value.into(); - } - - napi_ok -} - -#[napi_sym] -fn napi_get_arraybuffer_info( - env: *mut Env, - value: napi_value, - data: *mut *mut c_void, - length: *mut usize, -) -> napi_status { - let env = check_env!(env); - check_arg!(env, value); - - let Some(buf) = - value.and_then(|v| v8::Local::<v8::ArrayBuffer>::try_from(v).ok()) - else { - return napi_set_last_error(env, napi_invalid_arg); - }; - - if !data.is_null() { - unsafe { - *data = get_array_buffer_ptr(buf); - } - } - - if !length.is_null() { - unsafe { - *length = buf.byte_length(); - } - } - - napi_ok -} - -#[napi_sym] -fn napi_is_typedarray( - env: *mut Env, - value: napi_value, - result: *mut bool, -) -> napi_status { - let env = check_env!(env); - check_arg!(env, value); - check_arg!(env, result); - - unsafe { - *result = value.unwrap().is_typed_array(); - } - - napi_ok -} - -#[napi_sym] -fn napi_create_typedarray<'s>( - env: &'s mut Env, - ty: napi_typedarray_type, - length: usize, - arraybuffer: napi_value, - byte_offset: usize, - result: *mut napi_value<'s>, -) -> napi_status { - check_arg!(env, arraybuffer); - check_arg!(env, result); - - let Some(ab) = - arraybuffer.and_then(|v| v8::Local::<v8::ArrayBuffer>::try_from(v).ok()) - else { - return napi_arraybuffer_expected; - }; - - macro_rules! create { - ($TypedArray:ident, $size_of_element:expr) => {{ - let soe = $size_of_element; - if soe > 1 && byte_offset % soe != 0 { - let message = v8::String::new( - &mut env.scope(), - format!( - "start offset of {} should be multiple of {}", - stringify!($TypedArray), - soe - ) - .as_str(), - ) - .unwrap(); - let exc = v8::Exception::range_error(&mut env.scope(), message); - env.scope().throw_exception(exc); - return napi_pending_exception; - } - - if length * soe + byte_offset > ab.byte_length() { - let message = - v8::String::new(&mut env.scope(), "Invalid typed array length") - .unwrap(); - let exc = v8::Exception::range_error(&mut env.scope(), message); - env.scope().throw_exception(exc); - return napi_pending_exception; - } - - let Some(ta) = - v8::$TypedArray::new(&mut env.scope(), ab, byte_offset, length) - else { - return napi_generic_failure; - }; - ta.into() - }}; - } - - let typedarray: v8::Local<v8::Value> = match ty { - napi_uint8_array => create!(Uint8Array, 1), - napi_uint8_clamped_array => create!(Uint8ClampedArray, 1), - napi_int8_array => create!(Int8Array, 1), - napi_uint16_array => create!(Uint16Array, 2), - napi_int16_array => create!(Int16Array, 2), - napi_uint32_array => create!(Uint32Array, 4), - napi_int32_array => create!(Int32Array, 4), - napi_float32_array => create!(Float32Array, 4), - napi_float64_array => create!(Float64Array, 8), - napi_bigint64_array => create!(BigInt64Array, 8), - napi_biguint64_array => create!(BigUint64Array, 8), - _ => { - return napi_invalid_arg; - } - }; - - unsafe { - *result = typedarray.into(); - } - - napi_ok -} - -#[napi_sym] -fn napi_get_typedarray_info( - env_ptr: *mut Env, - typedarray: napi_value, - type_: *mut napi_typedarray_type, - length: *mut usize, - data: *mut *mut c_void, - arraybuffer: *mut napi_value, - byte_offset: *mut usize, -) -> napi_status { - let env = check_env!(env_ptr); - check_arg!(env, typedarray); - - let Some(array) = - typedarray.and_then(|v| v8::Local::<v8::TypedArray>::try_from(v).ok()) - else { - return napi_set_last_error(env_ptr, napi_invalid_arg); - }; - - if !type_.is_null() { - let tatype = if array.is_int8_array() { - napi_int8_array - } else if array.is_uint8_array() { - napi_uint8_array - } else if array.is_uint8_clamped_array() { - napi_uint8_clamped_array - } else if array.is_int16_array() { - napi_int16_array - } else if array.is_uint16_array() { - napi_uint16_array - } else if array.is_int32_array() { - napi_int32_array - } else if array.is_uint32_array() { - napi_uint32_array - } else if array.is_float32_array() { - napi_float32_array - } else if array.is_float64_array() { - napi_float64_array - } else if array.is_big_int64_array() { - napi_bigint64_array - } else if array.is_big_uint64_array() { - napi_biguint64_array - } else { - unreachable!(); - }; - - unsafe { - *type_ = tatype; - } - } - - if !length.is_null() { - unsafe { - *length = array.length(); - } - } - - if !data.is_null() { - unsafe { - *data = array.data(); - } - } - - if !arraybuffer.is_null() { - let buf = array.buffer(&mut env.scope()).unwrap(); - unsafe { - *arraybuffer = buf.into(); - } - } - - if !byte_offset.is_null() { - unsafe { - *byte_offset = array.byte_offset(); - } - } - - napi_clear_last_error(env_ptr) -} - -#[napi_sym] -fn napi_create_dataview<'s>( - env: &'s mut Env, - byte_length: usize, - arraybuffer: napi_value<'s>, - byte_offset: usize, - result: *mut napi_value<'s>, -) -> napi_status { - check_arg!(env, arraybuffer); - check_arg!(env, result); - - let Some(buffer) = - arraybuffer.and_then(|v| v8::Local::<v8::ArrayBuffer>::try_from(v).ok()) - else { - return napi_invalid_arg; - }; - - if byte_length + byte_offset > buffer.byte_length() { - unsafe { - return napi_throw_range_error( - env, - c"ERR_NAPI_INVALID_DATAVIEW_ARGS".as_ptr(), - c"byte_offset + byte_length should be less than or equal to the size in bytes of the array passed in".as_ptr(), - ); - } - } - - let dataview = - v8::DataView::new(&mut env.scope(), buffer, byte_offset, byte_length); - - unsafe { - *result = dataview.into(); - } - - napi_ok -} - -#[napi_sym] -fn napi_is_dataview( - env: *mut Env, - value: napi_value, - result: *mut bool, -) -> napi_status { - let env = check_env!(env); - check_arg!(env, value); - check_arg!(env, result); - - unsafe { - *result = value.unwrap().is_data_view(); - } - - napi_clear_last_error(env) -} - -#[napi_sym] -fn napi_get_dataview_info( - env_ptr: *mut Env, - dataview: napi_value, - byte_length: *mut usize, - data: *mut *mut c_void, - arraybuffer: *mut napi_value, - byte_offset: *mut usize, -) -> napi_status { - let env = check_env!(env_ptr); - check_arg!(env, dataview); - - let Some(array) = - dataview.and_then(|v| v8::Local::<v8::DataView>::try_from(v).ok()) - else { - return napi_invalid_arg; - }; - - if !byte_length.is_null() { - unsafe { - *byte_length = array.byte_length(); - } - } - - if !arraybuffer.is_null() { - let Some(buffer) = array.buffer(&mut env.scope()) else { - return napi_generic_failure; - }; - - unsafe { - *arraybuffer = buffer.into(); - } - } - - if !data.is_null() { - unsafe { - *data = array.data(); - } - } - - if !byte_offset.is_null() { - unsafe { - *byte_offset = array.byte_offset(); - } - } - - napi_clear_last_error(env_ptr) -} - -#[napi_sym] -fn napi_get_version(env: *mut Env, result: *mut u32) -> napi_status { - let env = check_env!(env); - check_arg!(env, result); - - unsafe { - *result = NAPI_VERSION; - } - - napi_clear_last_error(env) -} - -#[napi_sym] -fn napi_create_promise<'s>( - env: &'s mut Env, - deferred: *mut napi_deferred, - promise: *mut napi_value<'s>, -) -> napi_status { - check_arg!(env, deferred); - check_arg!(env, promise); - - let resolver = v8::PromiseResolver::new(&mut env.scope()).unwrap(); - - let global = v8::Global::new(&mut env.scope(), resolver); - let global_ptr = global.into_raw().as_ptr() as napi_deferred; - - let p = resolver.get_promise(&mut env.scope()); - - unsafe { - *deferred = global_ptr; - } - - unsafe { - *promise = p.into(); - } - - napi_ok -} - -#[napi_sym] -fn napi_resolve_deferred( - env: &mut Env, - deferred: napi_deferred, - result: napi_value, -) -> napi_status { - check_arg!(env, result); - check_arg!(env, deferred); - - let deferred_ptr = - unsafe { NonNull::new_unchecked(deferred as *mut v8::PromiseResolver) }; - let global = unsafe { v8::Global::from_raw(env.isolate(), deferred_ptr) }; - let resolver = v8::Local::new(&mut env.scope(), global); - - if !resolver - .resolve(&mut env.scope(), result.unwrap()) - .unwrap_or(false) - { - return napi_generic_failure; - } - - napi_ok -} - -#[napi_sym] -fn napi_reject_deferred( - env: &mut Env, - deferred: napi_deferred, - result: napi_value, -) -> napi_status { - check_arg!(env, result); - check_arg!(env, deferred); - - let deferred_ptr = - unsafe { NonNull::new_unchecked(deferred as *mut v8::PromiseResolver) }; - let global = unsafe { v8::Global::from_raw(env.isolate(), deferred_ptr) }; - let resolver = v8::Local::new(&mut env.scope(), global); - - if !resolver - .reject(&mut env.scope(), result.unwrap()) - .unwrap_or(false) - { - return napi_generic_failure; - } - - napi_ok -} - -#[napi_sym] -fn napi_is_promise( - env: *mut Env, - value: napi_value, - is_promise: *mut bool, -) -> napi_status { - let env = check_env!(env); - check_arg!(env, value); - check_arg!(env, is_promise); - - unsafe { - *is_promise = value.unwrap().is_promise(); - } - - napi_clear_last_error(env) -} - -#[napi_sym] -fn napi_create_date<'s>( - env: &'s mut Env, - time: f64, - result: *mut napi_value<'s>, -) -> napi_status { - check_arg!(env, result); - - let Some(date) = v8::Date::new(&mut env.scope(), time) else { - return napi_generic_failure; - }; - - unsafe { - *result = date.into(); - } - - napi_ok -} - -#[napi_sym] -fn napi_is_date( - env: *mut Env, - value: napi_value, - is_date: *mut bool, -) -> napi_status { - let env = check_env!(env); - check_arg!(env, value); - check_arg!(env, is_date); - - unsafe { - *is_date = value.unwrap().is_date(); - } - - napi_clear_last_error(env) -} - -#[napi_sym] -fn napi_get_date_value( - env: &mut Env, - value: napi_value, - result: *mut f64, -) -> napi_status { - check_arg!(env, result); - - let Some(date) = value.and_then(|v| v8::Local::<v8::Date>::try_from(v).ok()) - else { - return napi_date_expected; - }; - - unsafe { - *result = date.value_of(); - } - - napi_ok -} - -#[napi_sym] -fn napi_run_script<'s>( - env: &'s mut Env, - script: napi_value, - result: *mut napi_value<'s>, -) -> napi_status { - check_arg!(env, script); - check_arg!(env, result); - - let Some(script) = - script.and_then(|v| v8::Local::<v8::String>::try_from(v).ok()) - else { - return napi_string_expected; - }; - - let Some(script) = v8::Script::compile(&mut env.scope(), script, None) else { - return napi_generic_failure; - }; - - let Some(rv) = script.run(&mut env.scope()) else { - return napi_generic_failure; - }; - - unsafe { - *result = rv.into(); - } - - napi_ok -} - -#[napi_sym] -fn napi_add_finalizer( - env_ptr: *mut Env, - value: napi_value, - finalize_data: *mut c_void, - finalize_cb: Option<napi_finalize>, - finalize_hint: *mut c_void, - result: *mut napi_ref, -) -> napi_status { - let env = check_env!(env_ptr); - check_arg!(env, value); - check_arg!(env, finalize_cb); - - let Some(value) = - value.and_then(|v| v8::Local::<v8::Object>::try_from(v).ok()) - else { - return napi_set_last_error(env, napi_invalid_arg); - }; - - let ownership = if result.is_null() { - ReferenceOwnership::Runtime - } else { - ReferenceOwnership::Userland - }; - let reference = Reference::new( - env, - value.into(), - 0, - ownership, - finalize_cb, - finalize_data, - finalize_hint, - ); - - if !result.is_null() { - unsafe { - *result = Reference::into_raw(reference) as _; - } - } - - napi_clear_last_error(env_ptr) -} - -#[napi_sym] -fn node_api_post_finalizer( - env: *mut Env, - _finalize_cb: napi_finalize, - _finalize_data: *mut c_void, - _finalize_hint: *mut c_void, -) -> napi_status { - napi_clear_last_error(env) -} - -#[napi_sym] -fn napi_adjust_external_memory( - env: *mut Env, - change_in_bytes: i64, - adjusted_value: *mut i64, -) -> napi_status { - let env = check_env!(env); - check_arg!(env, adjusted_value); - - unsafe { - *adjusted_value = env - .isolate() - .adjust_amount_of_external_allocated_memory(change_in_bytes); - } - - napi_clear_last_error(env) -} - -#[napi_sym] -fn napi_set_instance_data( - env: *mut Env, - data: *mut c_void, - finalize_cb: Option<napi_finalize>, - finalize_hint: *mut c_void, -) -> napi_status { - let env = check_env!(env); - - env.shared_mut().instance_data = Some(InstanceData { - data, - finalize_cb, - finalize_hint, - }); - - napi_clear_last_error(env) -} - -#[napi_sym] -fn napi_get_instance_data( - env: *mut Env, - data: *mut *mut c_void, -) -> napi_status { - let env = check_env!(env); - check_arg!(env, data); - - let instance_data = match &env.shared().instance_data { - Some(v) => v.data, - None => std::ptr::null_mut(), - }; - - unsafe { *data = instance_data }; - - napi_clear_last_error(env) -} - -#[napi_sym] -fn napi_detach_arraybuffer(env: *mut Env, value: napi_value) -> napi_status { - let env = check_env!(env); - check_arg!(env, value); - - let Some(ab) = - value.and_then(|v| v8::Local::<v8::ArrayBuffer>::try_from(v).ok()) - else { - return napi_set_last_error(env, napi_arraybuffer_expected); - }; - - if !ab.is_detachable() { - return napi_set_last_error(env, napi_detachable_arraybuffer_expected); - } - - // Expected to crash for None. - ab.detach(None).unwrap(); - - napi_clear_last_error(env); - napi_ok -} - -#[napi_sym] -fn napi_is_detached_arraybuffer( - env_ptr: *mut Env, - arraybuffer: napi_value, - result: *mut bool, -) -> napi_status { - let env = check_env!(env_ptr); - check_arg!(env, arraybuffer); - check_arg!(env, result); - - let is_detached = match arraybuffer - .and_then(|v| v8::Local::<v8::ArrayBuffer>::try_from(v).ok()) - { - Some(ab) => ab.was_detached(), - None => false, - }; - - unsafe { - *result = is_detached; - } - - napi_clear_last_error(env) -} diff --git a/ext/napi/lib.rs b/ext/napi/lib.rs index 503921634..829839838 100644 --- a/ext/napi/lib.rs +++ b/ext/napi/lib.rs @@ -5,21 +5,6 @@ #![allow(clippy::undocumented_unsafe_blocks)] #![deny(clippy::missing_safety_doc)] -//! Symbols to be exported are now defined in this JSON file. -//! The `#[napi_sym]` macro checks for missing entries and panics. -//! -//! `./tools/napi/generate_symbols_list.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_symbols_list.js` to update `cli/napi/generated_symbol_exports_list_*.def`. - -pub mod js_native_api; -pub mod node_api; -pub mod util; - use core::ptr::NonNull; use deno_core::error::type_error; use deno_core::error::AnyError; diff --git a/ext/napi/node_api.rs b/ext/napi/node_api.rs deleted file mode 100644 index 44e4eae87..000000000 --- a/ext/napi/node_api.rs +++ /dev/null @@ -1,999 +0,0 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. - -#![deny(unsafe_op_in_unsafe_fn)] - -use super::util::get_array_buffer_ptr; -use super::util::make_external_backing_store; -use super::util::napi_clear_last_error; -use super::util::napi_set_last_error; -use super::util::SendPtr; -use crate::check_arg; -use crate::check_env; -use crate::*; -use deno_core::parking_lot::Condvar; -use deno_core::parking_lot::Mutex; -use deno_core::V8CrossThreadTaskSpawner; -use napi_sym::napi_sym; -use std::sync::atomic::AtomicBool; -use std::sync::atomic::AtomicU8; -use std::sync::atomic::AtomicUsize; -use std::sync::atomic::Ordering; -use std::sync::Arc; - -#[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] -fn napi_add_env_cleanup_hook( - env: *mut Env, - fun: Option<napi_cleanup_hook>, - arg: *mut c_void, -) -> napi_status { - let env = check_env!(env); - check_arg!(env, fun); - - let fun = fun.unwrap(); - - env.add_cleanup_hook(fun, arg); - - napi_ok -} - -#[napi_sym] -fn napi_remove_env_cleanup_hook( - env: *mut Env, - fun: Option<napi_cleanup_hook>, - arg: *mut c_void, -) -> napi_status { - let env = check_env!(env); - check_arg!(env, fun); - - let fun = fun.unwrap(); - - env.remove_cleanup_hook(fun, arg); - - napi_ok -} - -struct AsyncCleanupHandle { - env: *mut Env, - hook: napi_async_cleanup_hook, - data: *mut c_void, -} - -unsafe extern "C" fn async_cleanup_handler(arg: *mut c_void) { - unsafe { - let handle = Box::<AsyncCleanupHandle>::from_raw(arg as _); - (handle.hook)(arg, handle.data); - } -} - -#[napi_sym] -fn napi_add_async_cleanup_hook( - env: *mut Env, - hook: Option<napi_async_cleanup_hook>, - arg: *mut c_void, - remove_handle: *mut napi_async_cleanup_hook_handle, -) -> napi_status { - let env = check_env!(env); - check_arg!(env, hook); - - let hook = hook.unwrap(); - - let handle = Box::into_raw(Box::new(AsyncCleanupHandle { - env, - hook, - data: arg, - })) as *mut c_void; - - env.add_cleanup_hook(async_cleanup_handler, handle); - - if !remove_handle.is_null() { - unsafe { - *remove_handle = handle; - } - } - - napi_clear_last_error(env) -} - -#[napi_sym] -fn napi_remove_async_cleanup_hook( - remove_handle: napi_async_cleanup_hook_handle, -) -> napi_status { - if remove_handle.is_null() { - return napi_invalid_arg; - } - - let handle = - unsafe { Box::<AsyncCleanupHandle>::from_raw(remove_handle as _) }; - - let env = unsafe { &mut *handle.env }; - - env.remove_cleanup_hook(async_cleanup_handler, remove_handle); - - napi_ok -} - -#[napi_sym] -fn napi_fatal_exception(env: &mut Env, err: napi_value) -> napi_status { - check_arg!(env, err); - - let report_error = v8::Local::new(&mut env.scope(), &env.report_error); - - let this = v8::undefined(&mut env.scope()); - if report_error - .call(&mut env.scope(), this.into(), &[err.unwrap()]) - .is_none() - { - return napi_generic_failure; - } - - napi_ok -} - -#[napi_sym] -#[allow(clippy::print_stderr)] -fn napi_fatal_error( - location: *const c_char, - location_len: usize, - message: *const c_char, - message_len: usize, -) -> napi_status { - let location = if location.is_null() { - None - } else { - unsafe { - Some(if location_len == NAPI_AUTO_LENGTH { - 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 == NAPI_AUTO_LENGTH { - unsafe { std::ffi::CStr::from_ptr(message).to_str().unwrap() } - } else { - let slice = unsafe { - std::slice::from_raw_parts(message as *const u8, message_len as usize) - }; - std::str::from_utf8(slice).unwrap() - }; - - if let Some(location) = location { - eprintln!("NODE API FATAL ERROR: {} {}", location, message); - } else { - eprintln!("NODE API FATAL ERROR: {}", message); - } - - std::process::abort(); -} - -#[napi_sym] -fn napi_open_callback_scope( - env: *mut Env, - _resource_object: napi_value, - _context: napi_value, - result: *mut napi_callback_scope, -) -> napi_status { - let env = check_env!(env); - check_arg!(env, result); - - // we open scope automatically when it's needed - unsafe { - *result = std::ptr::null_mut(); - } - - napi_clear_last_error(env) -} - -#[napi_sym] -fn napi_close_callback_scope( - env: *mut Env, - scope: napi_callback_scope, -) -> napi_status { - let env = check_env!(env); - // we close scope automatically when it's needed - assert!(scope.is_null()); - napi_clear_last_error(env) -} - -// NOTE: we don't support "async_hooks::AsyncContext" so these APIs are noops. -#[napi_sym] -fn napi_async_init( - env: *mut Env, - _async_resource: napi_value, - _async_resource_name: napi_value, - result: *mut napi_async_context, -) -> napi_status { - let env = check_env!(env); - unsafe { - *result = ptr::null_mut(); - } - napi_clear_last_error(env) -} - -#[napi_sym] -fn napi_async_destroy( - env: *mut Env, - async_context: napi_async_context, -) -> napi_status { - let env = check_env!(env); - assert!(async_context.is_null()); - napi_clear_last_error(env) -} - -#[napi_sym] -fn napi_make_callback<'s>( - env: &'s mut Env, - _async_context: napi_async_context, - recv: napi_value, - func: napi_value, - argc: usize, - argv: *const napi_value<'s>, - result: *mut napi_value<'s>, -) -> napi_status { - check_arg!(env, recv); - if argc > 0 { - check_arg!(env, argv); - } - - let Some(recv) = recv.and_then(|v| v.to_object(&mut env.scope())) else { - return napi_object_expected; - }; - - let Some(func) = - func.and_then(|v| v8::Local::<v8::Function>::try_from(v).ok()) - else { - return napi_function_expected; - }; - - let args = if argc > 0 { - unsafe { - std::slice::from_raw_parts(argv as *mut v8::Local<v8::Value>, argc) - } - } else { - &[] - }; - - // TODO: async_context - - let Some(v) = func.call(&mut env.scope(), recv.into(), args) else { - return napi_generic_failure; - }; - - unsafe { - *result = v.into(); - } - - napi_ok -} - -#[napi_sym] -fn napi_create_buffer<'s>( - env: &'s mut Env, - length: usize, - data: *mut *mut c_void, - result: *mut napi_value<'s>, -) -> napi_status { - check_arg!(env, result); - - let ab = v8::ArrayBuffer::new(&mut env.scope(), length); - - let buffer_constructor = - v8::Local::new(&mut env.scope(), &env.buffer_constructor); - let Some(buffer) = - buffer_constructor.new_instance(&mut env.scope(), &[ab.into()]) - else { - return napi_generic_failure; - }; - - if !data.is_null() { - unsafe { - *data = get_array_buffer_ptr(ab); - } - } - - unsafe { - *result = buffer.into(); - } - - napi_ok -} - -#[napi_sym] -fn napi_create_external_buffer<'s>( - env: &'s mut Env, - length: usize, - data: *mut c_void, - finalize_cb: napi_finalize, - finalize_hint: *mut c_void, - result: *mut napi_value<'s>, -) -> napi_status { - check_arg!(env, result); - - let store = make_external_backing_store( - env, - data, - length, - ptr::null_mut(), - finalize_cb, - finalize_hint, - ); - - let ab = - v8::ArrayBuffer::with_backing_store(&mut env.scope(), &store.make_shared()); - - let buffer_constructor = - v8::Local::new(&mut env.scope(), &env.buffer_constructor); - let Some(buffer) = - buffer_constructor.new_instance(&mut env.scope(), &[ab.into()]) - else { - return napi_generic_failure; - }; - - unsafe { - *result = buffer.into(); - } - - napi_ok -} - -#[napi_sym] -fn napi_create_buffer_copy<'s>( - env: &'s mut Env, - length: usize, - data: *mut c_void, - result_data: *mut *mut c_void, - result: *mut napi_value<'s>, -) -> napi_status { - check_arg!(env, result); - - let ab = v8::ArrayBuffer::new(&mut env.scope(), length); - - let buffer_constructor = - v8::Local::new(&mut env.scope(), &env.buffer_constructor); - let Some(buffer) = - buffer_constructor.new_instance(&mut env.scope(), &[ab.into()]) - else { - return napi_generic_failure; - }; - - let ptr = get_array_buffer_ptr(ab); - unsafe { - std::ptr::copy(data, ptr, length); - } - - if !result_data.is_null() { - unsafe { - *result_data = ptr; - } - } - - unsafe { - *result = buffer.into(); - } - - napi_ok -} - -#[napi_sym] -fn napi_is_buffer( - env: *mut Env, - value: napi_value, - result: *mut bool, -) -> napi_status { - let env = check_env!(env); - check_arg!(env, value); - check_arg!(env, result); - - let buffer_constructor = - v8::Local::new(&mut env.scope(), &env.buffer_constructor); - - let Some(is_buffer) = value - .unwrap() - .instance_of(&mut env.scope(), buffer_constructor.into()) - else { - return napi_set_last_error(env, napi_generic_failure); - }; - - unsafe { - *result = is_buffer; - } - - napi_clear_last_error(env) -} - -#[napi_sym] -fn napi_get_buffer_info( - env: *mut Env, - value: napi_value, - data: *mut *mut c_void, - length: *mut usize, -) -> napi_status { - let env = check_env!(env); - check_arg!(env, value); - - // NB: Any TypedArray instance seems to be accepted by this function - // in Node.js. - let Some(ta) = - value.and_then(|v| v8::Local::<v8::TypedArray>::try_from(v).ok()) - else { - return napi_set_last_error(env, napi_invalid_arg); - }; - - if !data.is_null() { - unsafe { - *data = ta.data(); - } - } - - if !length.is_null() { - unsafe { - *length = ta.byte_length(); - } - } - - napi_clear_last_error(env) -} - -#[napi_sym] -fn napi_get_node_version( - env: *mut Env, - result: *mut *const napi_node_version, -) -> napi_status { - let env = check_env!(env); - check_arg!(env, result); - - const NODE_VERSION: napi_node_version = napi_node_version { - major: 20, - minor: 11, - patch: 1, - release: c"Deno".as_ptr(), - }; - - unsafe { - *result = &NODE_VERSION as *const napi_node_version; - } - - napi_clear_last_error(env) -} - -struct AsyncWork { - state: AtomicU8, - env: *mut Env, - _async_resource: v8::Global<v8::Object>, - _async_resource_name: String, - execute: napi_async_execute_callback, - complete: Option<napi_async_complete_callback>, - data: *mut c_void, -} - -impl AsyncWork { - const IDLE: u8 = 0; - const QUEUED: u8 = 1; - const RUNNING: u8 = 2; -} - -#[napi_sym] -fn napi_create_async_work( - env: *mut Env, - async_resource: napi_value, - async_resource_name: napi_value, - execute: Option<napi_async_execute_callback>, - complete: Option<napi_async_complete_callback>, - data: *mut c_void, - result: *mut napi_async_work, -) -> napi_status { - let env_ptr = env; - let env = check_env!(env); - check_arg!(env, execute); - check_arg!(env, result); - - let resource = if let Some(v) = *async_resource { - let Some(resource) = v.to_object(&mut env.scope()) else { - return napi_set_last_error(env, napi_object_expected); - }; - resource - } else { - v8::Object::new(&mut env.scope()) - }; - - let Some(resource_name) = - async_resource_name.and_then(|v| v.to_string(&mut env.scope())) - else { - return napi_set_last_error(env, napi_string_expected); - }; - - let resource_name = resource_name.to_rust_string_lossy(&mut env.scope()); - - let work = Box::new(AsyncWork { - state: AtomicU8::new(AsyncWork::IDLE), - env: env_ptr, - _async_resource: v8::Global::new(&mut env.scope(), resource), - _async_resource_name: resource_name, - execute: execute.unwrap(), - complete, - data, - }); - - unsafe { - *result = Box::into_raw(work) as _; - } - - napi_clear_last_error(env) -} - -#[napi_sym] -fn napi_delete_async_work(env: *mut Env, work: napi_async_work) -> napi_status { - let env = check_env!(env); - check_arg!(env, work); - - drop(unsafe { Box::<AsyncWork>::from_raw(work as _) }); - - napi_clear_last_error(env) -} - -#[napi_sym] -fn napi_get_uv_event_loop(env: *mut Env, uv_loop: *mut *mut ()) -> napi_status { - let env = check_env!(env); - check_arg!(env, uv_loop); - // There is no uv_loop in Deno - napi_set_last_error(env, napi_generic_failure) -} - -#[napi_sym] -fn napi_queue_async_work(env: *mut Env, work: napi_async_work) -> napi_status { - let env = check_env!(env); - check_arg!(env, work); - - let work = unsafe { &*(work as *mut AsyncWork) }; - - let result = - work - .state - .fetch_update(Ordering::SeqCst, Ordering::SeqCst, |state| { - // allow queue if idle or if running, but not if already queued. - if state == AsyncWork::IDLE || state == AsyncWork::RUNNING { - Some(AsyncWork::QUEUED) - } else { - None - } - }); - - if result.is_err() { - return napi_clear_last_error(env); - } - - let work = SendPtr(work); - - env.add_async_work(move || { - let work = work.take(); - let work = unsafe { &*work }; - - let state = work.state.compare_exchange( - AsyncWork::QUEUED, - AsyncWork::RUNNING, - Ordering::SeqCst, - Ordering::SeqCst, - ); - - if state.is_ok() { - unsafe { - (work.execute)(work.env as _, work.data); - } - - // reset back to idle if its still marked as running - let _ = work.state.compare_exchange( - AsyncWork::RUNNING, - AsyncWork::IDLE, - Ordering::SeqCst, - Ordering::Relaxed, - ); - } - - if let Some(complete) = work.complete { - let status = if state.is_ok() { - napi_ok - } else if state == Err(AsyncWork::IDLE) { - napi_cancelled - } else { - napi_generic_failure - }; - - unsafe { - complete(work.env as _, status, work.data); - } - } - - // `complete` probably deletes this `work`, so don't use it here. - }); - - napi_clear_last_error(env) -} - -#[napi_sym] -fn napi_cancel_async_work(env: *mut Env, work: napi_async_work) -> napi_status { - let env = check_env!(env); - check_arg!(env, work); - - let work = unsafe { &*(work as *mut AsyncWork) }; - - let _ = work.state.compare_exchange( - AsyncWork::QUEUED, - AsyncWork::IDLE, - Ordering::SeqCst, - Ordering::Relaxed, - ); - - napi_clear_last_error(env) -} - -extern "C" fn default_call_js_cb( - env: napi_env, - js_callback: napi_value, - _context: *mut c_void, - _data: *mut c_void, -) { - if let Some(js_callback) = *js_callback { - if let Ok(js_callback) = v8::Local::<v8::Function>::try_from(js_callback) { - let env = unsafe { &mut *(env as *mut Env) }; - let scope = &mut env.scope(); - let recv = v8::undefined(scope); - js_callback.call(scope, recv.into(), &[]); - } - } -} - -struct TsFn { - env: *mut Env, - func: Option<v8::Global<v8::Function>>, - max_queue_size: usize, - queue_size: Mutex<usize>, - queue_cond: Condvar, - thread_count: AtomicUsize, - thread_finalize_data: *mut c_void, - thread_finalize_cb: Option<napi_finalize>, - context: *mut c_void, - call_js_cb: napi_threadsafe_function_call_js, - _resource: v8::Global<v8::Object>, - _resource_name: String, - is_closing: AtomicBool, - is_closed: Arc<AtomicBool>, - sender: V8CrossThreadTaskSpawner, - is_ref: AtomicBool, -} - -impl Drop for TsFn { - fn drop(&mut self) { - assert!(self - .is_closed - .compare_exchange(false, true, Ordering::Relaxed, Ordering::Relaxed) - .is_ok()); - - self.unref(); - - if let Some(finalizer) = self.thread_finalize_cb { - unsafe { - (finalizer)(self.env as _, self.thread_finalize_data, ptr::null_mut()); - } - } - } -} - -impl TsFn { - pub fn acquire(&self) -> napi_status { - if self.is_closing.load(Ordering::SeqCst) { - return napi_closing; - } - self.thread_count.fetch_add(1, Ordering::Relaxed); - napi_ok - } - - pub fn release( - tsfn: *mut TsFn, - mode: napi_threadsafe_function_release_mode, - ) -> napi_status { - let tsfn = unsafe { &mut *tsfn }; - - let result = tsfn.thread_count.fetch_update( - Ordering::Relaxed, - Ordering::Relaxed, - |x| { - if x == 0 { - None - } else { - Some(x - 1) - } - }, - ); - - if result.is_err() { - return napi_invalid_arg; - } - - if (result == Ok(1) || mode == napi_tsfn_abort) - && tsfn - .is_closing - .compare_exchange(false, true, Ordering::SeqCst, Ordering::SeqCst) - .is_ok() - { - tsfn.queue_cond.notify_all(); - let tsfnptr = SendPtr(tsfn); - // drop must be queued in order to preserve ordering consistent - // with Node.js and so that the finalizer runs on the main thread. - tsfn.sender.spawn(move |_| { - let tsfn = unsafe { Box::from_raw(tsfnptr.take() as *mut TsFn) }; - drop(tsfn); - }); - } - - napi_ok - } - - pub fn ref_(&self) -> napi_status { - if self - .is_ref - .compare_exchange(false, true, Ordering::SeqCst, Ordering::SeqCst) - .is_ok() - { - let env = unsafe { &mut *self.env }; - env.threadsafe_function_ref(); - } - napi_ok - } - - pub fn unref(&self) -> napi_status { - if self - .is_ref - .compare_exchange(true, false, Ordering::SeqCst, Ordering::SeqCst) - .is_ok() - { - let env = unsafe { &mut *self.env }; - env.threadsafe_function_unref(); - } - - napi_ok - } - - pub fn call( - &self, - data: *mut c_void, - mode: napi_threadsafe_function_call_mode, - ) -> napi_status { - if self.is_closing.load(Ordering::SeqCst) { - return napi_closing; - } - - if self.max_queue_size > 0 { - let mut queue_size = self.queue_size.lock(); - while *queue_size >= self.max_queue_size { - if mode == napi_tsfn_blocking { - self.queue_cond.wait(&mut queue_size); - - if self.is_closing.load(Ordering::SeqCst) { - return napi_closing; - } - } else { - return napi_queue_full; - } - } - *queue_size += 1; - } - - let is_closed = self.is_closed.clone(); - let tsfn = SendPtr(self); - let data = SendPtr(data); - let context = SendPtr(self.context); - let call_js_cb = self.call_js_cb; - - self.sender.spawn(move |scope: &mut v8::HandleScope| { - let data = data.take(); - - // if is_closed then tsfn is freed, don't read from it. - if is_closed.load(Ordering::Relaxed) { - unsafe { - call_js_cb( - std::ptr::null_mut(), - None::<v8::Local<v8::Value>>.into(), - context.take() as _, - data as _, - ); - } - } else { - let tsfn = tsfn.take(); - - let tsfn = unsafe { &*tsfn }; - - if tsfn.max_queue_size > 0 { - let mut queue_size = tsfn.queue_size.lock(); - let size = *queue_size; - *queue_size -= 1; - if size == tsfn.max_queue_size { - tsfn.queue_cond.notify_one(); - } - } - - let func = tsfn.func.as_ref().map(|f| v8::Local::new(scope, f)); - - unsafe { - (tsfn.call_js_cb)( - tsfn.env as _, - func.into(), - tsfn.context, - data as _, - ); - } - } - }); - - napi_ok - } -} - -#[napi_sym] -#[allow(clippy::too_many_arguments)] -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, - call_js_cb: Option<napi_threadsafe_function_call_js>, - result: *mut napi_threadsafe_function, -) -> napi_status { - let env = check_env!(env); - check_arg!(env, async_resource_name); - if initial_thread_count == 0 { - return napi_set_last_error(env, napi_invalid_arg); - } - check_arg!(env, result); - - let func = if let Some(value) = *func { - let Ok(func) = v8::Local::<v8::Function>::try_from(value) else { - return napi_set_last_error(env, napi_function_expected); - }; - Some(v8::Global::new(&mut env.scope(), func)) - } else { - check_arg!(env, call_js_cb); - None - }; - - let resource = if let Some(v) = *async_resource { - let Some(resource) = v.to_object(&mut env.scope()) else { - return napi_set_last_error(env, napi_object_expected); - }; - resource - } else { - v8::Object::new(&mut env.scope()) - }; - let resource = v8::Global::new(&mut env.scope(), resource); - - let Some(resource_name) = - async_resource_name.and_then(|v| v.to_string(&mut env.scope())) - else { - return napi_set_last_error(env, napi_string_expected); - }; - let resource_name = resource_name.to_rust_string_lossy(&mut env.scope()); - - let tsfn = Box::new(TsFn { - env, - func, - max_queue_size, - queue_size: Mutex::new(0), - queue_cond: Condvar::new(), - thread_count: AtomicUsize::new(initial_thread_count), - thread_finalize_data, - thread_finalize_cb, - context, - call_js_cb: call_js_cb.unwrap_or(default_call_js_cb), - _resource: resource, - _resource_name: resource_name, - is_closing: AtomicBool::new(false), - is_closed: Arc::new(AtomicBool::new(false)), - is_ref: AtomicBool::new(false), - sender: env.async_work_sender.clone(), - }); - - tsfn.ref_(); - - unsafe { - *result = Box::into_raw(tsfn) as _; - } - - napi_clear_last_error(env) -} - -/// Maybe called from any thread. -#[napi_sym] -fn napi_get_threadsafe_function_context( - func: napi_threadsafe_function, - result: *mut *const c_void, -) -> napi_status { - assert!(!func.is_null()); - let tsfn = unsafe { &*(func as *const TsFn) }; - unsafe { - *result = tsfn.context; - } - napi_ok -} - -#[napi_sym] -fn napi_call_threadsafe_function( - func: napi_threadsafe_function, - data: *mut c_void, - is_blocking: napi_threadsafe_function_call_mode, -) -> napi_status { - assert!(!func.is_null()); - let tsfn = unsafe { &*(func as *mut TsFn) }; - tsfn.call(data, is_blocking) -} - -#[napi_sym] -fn napi_acquire_threadsafe_function( - tsfn: napi_threadsafe_function, -) -> napi_status { - assert!(!tsfn.is_null()); - let tsfn = unsafe { &*(tsfn as *mut TsFn) }; - tsfn.acquire() -} - -#[napi_sym] -fn napi_release_threadsafe_function( - tsfn: napi_threadsafe_function, - mode: napi_threadsafe_function_release_mode, -) -> napi_status { - assert!(!tsfn.is_null()); - TsFn::release(tsfn as _, mode) -} - -#[napi_sym] -fn napi_unref_threadsafe_function( - _env: &mut Env, - func: napi_threadsafe_function, -) -> napi_status { - assert!(!func.is_null()); - let tsfn = unsafe { &*(func as *mut TsFn) }; - tsfn.unref() -} - -#[napi_sym] -fn napi_ref_threadsafe_function( - _env: &mut Env, - func: napi_threadsafe_function, -) -> napi_status { - assert!(!func.is_null()); - let tsfn = unsafe { &*(func as *mut TsFn) }; - tsfn.ref_() -} - -#[napi_sym] -fn node_api_get_module_file_name( - env: *mut Env, - result: *mut *const c_char, -) -> napi_status { - let env = check_env!(env); - check_arg!(env, result); - - unsafe { - *result = env.shared().filename.as_ptr() as _; - } - - napi_clear_last_error(env) -} diff --git a/ext/napi/sym/Cargo.toml b/ext/napi/sym/Cargo.toml deleted file mode 100644 index 43f5b7589..000000000 --- a/ext/napi/sym/Cargo.toml +++ /dev/null @@ -1,21 +0,0 @@ -# Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. - -[package] -name = "napi_sym" -version = "0.91.0" -authors.workspace = true -edition.workspace = true -license.workspace = true -readme = "README.md" -repository.workspace = true -description = "proc macro for writing N-API symbols" - -[lib] -path = "./lib.rs" -proc-macro = true - -[dependencies] -quote.workspace = true -serde.workspace = true -serde_json.workspace = true -syn.workspace = true diff --git a/ext/napi/sym/README.md b/ext/napi/sym/README.md deleted file mode 100644 index de08a8e17..000000000 --- a/ext/napi/sym/README.md +++ /dev/null @@ -1,37 +0,0 @@ -# napi_sym - -A proc_macro for Deno's Node-API implementation. It does the following things: - -- Marks the symbol as `#[no_mangle]` and rewrites it as `pub extern "C" $name`. -- Asserts that the function symbol is present in - [`symbol_exports.json`](./symbol_exports.json). -- Maps `deno_napi::Result` to raw `napi_result`. - -```rust -use deno_napi::napi_value; -use deno_napi::Env; -use deno_napi::Error; -use deno_napi::Result; - -#[napi_sym::napi_sym] -fn napi_get_boolean( - env: *mut Env, - value: bool, - result: *mut napi_value, -) -> Result { - let _env: &mut Env = env.as_mut().ok_or(Error::InvalidArg)?; - // *result = ... - Ok(()) -} -``` - -### `symbol_exports.json` - -A file containing the symbols that need to be put into the executable's dynamic -symbol table at link-time. - -This is done using `/DEF:` on Windows, `-exported_symbol,_` on macOS and -`--export-dynamic-symbol=` on Linux. See [`cli/build.rs`](../build.rs). - -On Windows, you need to generate the `.def` file by running -[`tools/napi/generate_symbols_lists.js`](../../tools/napi/generate_symbols_lists.js). diff --git a/ext/napi/sym/lib.rs b/ext/napi/sym/lib.rs deleted file mode 100644 index 21edaa289..000000000 --- a/ext/napi/sym/lib.rs +++ /dev/null @@ -1,31 +0,0 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. - -use proc_macro::TokenStream; -use quote::quote; -use serde::Deserialize; - -static NAPI_EXPORTS: &str = include_str!("./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()), - "ext/napi/sym/symbol_exports.json is out of sync!" - ); - - TokenStream::from(quote! { - crate::napi_wrap! { - #func - } - }) -} diff --git a/ext/napi/sym/symbol_exports.json b/ext/napi/sym/symbol_exports.json deleted file mode 100644 index 64b548d49..000000000 --- a/ext/napi/sym/symbol_exports.json +++ /dev/null @@ -1,157 +0,0 @@ -{ - "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", - "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" - ] -} diff --git a/ext/napi/util.rs b/ext/napi/util.rs deleted file mode 100644 index 227b9f8d5..000000000 --- a/ext/napi/util.rs +++ /dev/null @@ -1,291 +0,0 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. -use crate::*; -use libc::INT_MAX; - -#[repr(transparent)] -pub(crate) struct SendPtr<T>(pub(crate) *const T); - -impl<T> SendPtr<T> { - // silly function to get around `clippy::redundant_locals` - pub(crate) fn take(self) -> *const T { - self.0 - } -} - -unsafe impl<T> Send for SendPtr<T> {} -unsafe impl<T> Sync for SendPtr<T> {} - -pub(crate) fn get_array_buffer_ptr( - ab: v8::Local<v8::ArrayBuffer>, -) -> *mut c_void { - match ab.data() { - Some(p) => p.as_ptr(), - None => std::ptr::null_mut(), - } -} - -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(crate) 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(crate) 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::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::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() - } -} - -#[macro_export] -macro_rules! check_arg { - ($env: expr, $ptr: expr) => { - $crate::return_status_if_false!( - $env, - !$crate::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] - #[allow(clippy::missing_safety_doc)] - 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::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::util::napi_set_last_error(env_ptr, napi_pending_exception); - } - - if result != napi_ok { - return $crate::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] - #[allow(clippy::missing_safety_doc)] - 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 - } - }; -} |