diff options
author | snek <snek@deno.com> | 2024-10-24 09:13:54 +0200 |
---|---|---|
committer | GitHub <noreply@github.com> | 2024-10-24 09:13:54 +0200 |
commit | 79a3ad2b950009f560641cea359d7deb6f7a61ac (patch) | |
tree | a775ff101d1513b24a8cc0afc80fdcbd4c6c9074 /cli/napi/js_native_api.rs | |
parent | 27df42f659ae7b77968d31363ade89addb516ea1 (diff) |
feat: support node-api in denort (#26389)
exposes node-api symbols in denort so that `deno compile` can run native
addons.
Diffstat (limited to 'cli/napi/js_native_api.rs')
-rw-r--r-- | cli/napi/js_native_api.rs | 3616 |
1 files changed, 0 insertions, 3616 deletions
diff --git a/cli/napi/js_native_api.rs b/cli/napi/js_native_api.rs deleted file mode 100644 index 35e7690c3..000000000 --- a/cli/napi/js_native_api.rs +++ /dev/null @@ -1,3616 +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 deno_runtime::deno_napi::*; -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 deno_runtime::deno_napi::function::create_function; -use deno_runtime::deno_napi::function::create_function_template; -use deno_runtime::deno_napi::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, - }; - - let mut accessor_property = v8::PropertyAttribute::NONE; - - 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; - } - - 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) - }); - if getter.is_some() - && setter.is_some() - && (p.attributes & napi_writable) == 0 - { - accessor_property = - accessor_property | v8::PropertyAttribute::READ_ONLY; - } - 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_with_attr(name, function.into(), accessor_property); - } else { - let proto = tpl.prototype_template(&mut env.scope()); - if (p.attributes & napi_writable) == 0 { - accessor_property = - accessor_property | v8::PropertyAttribute::READ_ONLY; - } - proto.set_with_attr(name, p.value.unwrap().into(), accessor_property); - } - } - - 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 mut 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<'s>( - env: &'s mut Env, - recv: napi_value<'s>, - func: napi_value<'s>, - argc: usize, - argv: *const napi_value<'s>, - result: *mut napi_value<'s>, -) -> napi_status { - 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_function_expected; - }; - - let Some(v) = func.call(&mut env.scope(), recv.unwrap(), args) else { - return napi_generic_failure; - }; - - if !result.is_null() { - unsafe { - *result = v.into(); - } - } - - napi_ok -} - -#[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); - - // Make sure microtasks don't run and call back into JS - env - .scope() - .set_microtasks_policy(v8::MicrotasksPolicy::Explicit); - - 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); - - let success = resolver - .resolve(&mut env.scope(), result.unwrap()) - .unwrap_or(false); - - // Restore policy - env - .scope() - .set_microtasks_policy(v8::MicrotasksPolicy::Auto); - - if success { - napi_ok - } else { - napi_generic_failure - } -} - -#[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) -} |