summaryrefslogtreecommitdiff
path: root/cli/napi/js_native_api.rs
diff options
context:
space:
mode:
Diffstat (limited to 'cli/napi/js_native_api.rs')
-rw-r--r--cli/napi/js_native_api.rs4933
1 files changed, 2842 insertions, 2091 deletions
diff --git a/cli/napi/js_native_api.rs b/cli/napi/js_native_api.rs
index 428c4a04a..cbce113dc 100644
--- a/cli/napi/js_native_api.rs
+++ b/cli/napi/js_native_api.rs
@@ -1,2016 +1,3046 @@
// 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 v8::BackingStore;
-use v8::UniqueRef;
+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;
-#[macro_export]
-macro_rules! check_env {
- ($env: expr) => {
- if $env.is_null() {
- return napi_invalid_arg;
- }
- };
+#[derive(Debug, Clone, Copy, PartialEq)]
+enum ReferenceOwnership {
+ Runtime,
+ Userland,
}
-#[inline]
-unsafe fn napi_value_unchecked(val: napi_value) -> v8::Local<v8::Value> {
- transmute::<napi_value, v8::Local<v8::Value>>(val)
+enum ReferenceState {
+ Strong(v8::Global<v8::Value>),
+ Weak(v8::Weak<v8::Value>),
}
-#[macro_export]
-macro_rules! return_error_status_if_false {
- ($env: expr, $condition: expr, $status: ident) => {
- if !$condition {
- return Err(
- $crate::napi::js_native_api::napi_set_last_error(
- $env,
- $status,
- 0,
- std::ptr::null_mut(),
- )
- .into(),
- );
- }
- };
+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,
}
-#[macro_export]
-macro_rules! return_status_if_false {
- ($env: expr, $condition: expr, $status: ident) => {
- if !$condition {
- return $crate::napi::js_native_api::napi_set_last_error(
- $env,
- $status,
- 0,
- std::ptr::null_mut(),
- );
+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 { &mut *(*env).isolate_ptr };
+
+ 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();
}
- };
-}
-fn check_new_from_utf8_len<'s>(
- env: *mut Env,
- str_: *const c_char,
- len: usize,
-) -> Result<v8::Local<'s, v8::String>, napi_status> {
- 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 };
- v8::String::new(&mut env.scope(), string)
- };
- return_error_status_if_false!(env, result.is_some(), napi_generic_failure);
- Ok(result.unwrap())
-}
+ reference
+ }
-#[inline]
-fn check_new_from_utf8<'s>(
- env: *mut Env,
- str_: *const c_char,
-) -> Result<v8::Local<'s, v8::String>, napi_status> {
- check_new_from_utf8_len(env, str_, NAPI_AUTO_LENGTH)
-}
+ fn ref_(&mut self) -> u32 {
+ self.ref_count += 1;
+ if self.ref_count == 1 {
+ self.set_strong();
+ }
+ self.ref_count
+ }
-#[macro_export]
-macro_rules! status_call {
- ($call: expr) => {
- let status = $call;
- if status != napi_ok {
- return status;
+ 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
+ }
-// Macro to check napi arguments.
-// If nullptr, return napi_invalid_arg.
-#[macro_export]
-macro_rules! check_arg {
- ($env: expr, $ptr: expr) => {
- $crate::return_status_if_false!($env, !$ptr.is_null(), napi_invalid_arg);
- };
-}
+ fn reset(&mut self) {
+ self.finalize_cb = None;
+ self.finalize_data = std::ptr::null_mut();
+ self.finalize_hint = std::ptr::null_mut();
+ }
-macro_rules! check_arg_option {
- ($env: expr, $opt: expr) => {
- $crate::return_status_if_false!($env, $opt.is_some(), napi_invalid_arg);
- };
-}
+ fn set_strong(&mut self) {
+ if let ReferenceState::Weak(w) = &self.state {
+ let isolate = unsafe { &mut *(*self.env).isolate_ptr };
+ if let Some(g) = w.to_global(isolate) {
+ self.state = ReferenceState::Strong(g);
+ }
+ }
+ }
-fn napi_clear_last_error(env: *mut Env) {
- 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();
-}
+ 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 { &mut *(*self.env).isolate_ptr };
+ self.state =
+ ReferenceState::Weak(v8::Weak::with_finalizer(isolate, g, cb));
+ }
+ }
-pub(crate) fn napi_set_last_error(
- env: *mut Env,
- error_code: napi_status,
- engine_error_code: i32,
- engine_reserved: *mut c_void,
-) -> napi_status {
- let env = unsafe { &mut *env };
- env.last_error.error_code = error_code;
- env.last_error.engine_error_code = engine_error_code;
- env.last_error.engine_reserved = engine_reserved;
- error_code
-}
+ fn weak_callback(reference: *mut Reference) {
+ let reference = unsafe { &mut *reference };
-/// Returns napi_value that represents a new JavaScript Array.
-#[napi_sym::napi_sym]
-fn napi_create_array(env: *mut Env, result: *mut napi_value) -> napi_status {
- check_env!(env);
- check_arg!(env, result);
- let env = unsafe { &mut *env };
- *result = v8::Array::new(&mut env.scope(), 0).into();
- napi_ok
-}
+ let finalize_cb = reference.finalize_cb;
+ let finalize_data = reference.finalize_data;
+ let finalize_hint = reference.finalize_hint;
+ reference.reset();
-#[napi_sym::napi_sym]
-fn napi_create_array_with_length(
- env: *mut Env,
- len: i32,
- result: *mut napi_value,
-) -> napi_status {
- check_env!(env);
- check_arg!(env, result);
- let env = unsafe { &mut *env };
- *result = v8::Array::new(&mut env.scope(), len).into();
- napi_ok
-}
+ if let Some(finalize_cb) = finalize_cb {
+ unsafe {
+ finalize_cb(reference.env as _, finalize_data, finalize_hint);
+ }
+ }
-#[napi_sym::napi_sym]
-fn napi_create_arraybuffer(
- env: *mut Env,
- len: usize,
- data: *mut *mut u8,
- result: *mut napi_value,
-) -> napi_status {
- check_env!(env);
- check_arg!(env, result);
- let env = unsafe { &mut *env };
+ if reference.ownership == ReferenceOwnership::Runtime {
+ unsafe { drop(Reference::from_raw(reference)) }
+ }
+ }
- let value = v8::ArrayBuffer::new(&mut env.scope(), len);
- if !data.is_null() {
- *data = get_array_buffer_ptr(value);
+ fn into_raw(r: Box<Reference>) -> *mut Reference {
+ Box::into_raw(r)
}
- *result = value.into();
- napi_ok
+ 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::napi_sym]
-fn napi_create_bigint_int64(
+#[napi_sym]
+fn napi_get_last_error_info(
env: *mut Env,
- value: i64,
- result: *mut napi_value,
+ result: *mut *const napi_extended_error_info,
) -> napi_status {
- check_env!(env);
+ let env = check_env!(env);
check_arg!(env, result);
- let env = unsafe { &mut *env };
- *result = v8::BigInt::new_from_i64(&mut env.scope(), value).into();
+
+ 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::napi_sym]
-fn napi_create_bigint_uint64(
- env: *mut Env,
- value: u64,
+#[napi_sym]
+fn napi_create_function(
+ env: &mut Env,
+ name: *const c_char,
+ length: usize,
+ cb: napi_callback,
+ cb_info: napi_callback_info,
result: *mut napi_value,
) -> napi_status {
- check_env!(env);
check_arg!(env, result);
- let env = unsafe { &mut *env };
- *result = v8::BigInt::new_from_u64(&mut env.scope(), value).into();
+ 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(env, name, cb, cb_info).into();
+ }
+
napi_ok
}
-#[napi_sym::napi_sym]
-fn napi_create_bigint_words(
- env: *mut Env,
- sign_bit: bool,
- word_count: usize,
- words: *const u64,
- result: *mut napi_value,
+#[napi_sym]
+#[allow(clippy::too_many_arguments)]
+fn napi_define_class<'s>(
+ env: &'s mut Env,
+ utf8name: *const c_char,
+ length: usize,
+ constructor: napi_callback,
+ callback_data: *mut c_void,
+ property_count: usize,
+ properties: *const napi_property_descriptor,
+ result: *mut napi_value<'s>,
) -> napi_status {
- check_env!(env);
- check_arg!(env, words);
- let env = unsafe { &mut *env };
check_arg!(env, result);
+ check_arg!(env, constructor);
- if word_count > INT_MAX as _ {
- return napi_invalid_arg;
+ if property_count > 0 {
+ check_arg!(env, properties);
}
- match v8::BigInt::new_from_words(
- &mut env.scope(),
- sign_bit,
- std::slice::from_raw_parts(words, word_count),
- ) {
- Some(value) => {
- *result = value.into();
+ let name = match unsafe { check_new_from_utf8_len(env, utf8name, length) } {
+ Ok(string) => string,
+ Err(status) => return status,
+ };
+
+ let tpl = unsafe {
+ create_function_template(env, Some(name), constructor, 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;
}
- None => {
- return napi_invalid_arg;
+
+ let name = match unsafe { v8_name_from_property_descriptor(env, p) } {
+ Ok(name) => name,
+ Err(status) => return status,
+ };
+
+ let method = p.method;
+ let getter = p.getter;
+ let setter = p.setter;
+
+ if getter.is_some() || setter.is_some() {
+ let getter: Option<v8::Local<v8::FunctionTemplate>> = if getter.is_some()
+ {
+ Some(unsafe { create_function_template(env, None, p.getter, p.data) })
+ } else {
+ None
+ };
+ let setter: Option<v8::Local<v8::FunctionTemplate>> = if setter.is_some()
+ {
+ Some(unsafe { create_function_template(env, None, p.setter, p.data) })
+ } else {
+ None
+ };
+
+ 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 method.is_some() {
+ let function =
+ unsafe { create_function_template(env, None, p.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 env_ptr = env as *mut Env;
+ 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::napi_sym]
-fn napi_create_buffer(
+#[napi_sym]
+fn napi_get_property_names(
env: *mut Env,
- len: usize,
- data: *mut *mut u8,
+ object: napi_value,
result: *mut napi_value,
) -> napi_status {
- check_env!(env);
- let env = unsafe { &mut *env };
- let value = v8::ArrayBuffer::new(&mut env.scope(), len);
- if !data.is_null() {
- *data = get_array_buffer_ptr(value);
+ 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,
+ )
}
- let value = v8::Uint8Array::new(&mut env.scope(), value, 0, len).unwrap();
- let value: v8::Local<v8::Value> = value.into();
- *result = value.into();
- napi_ok
}
-#[napi_sym::napi_sym]
-fn napi_create_buffer_copy(
- env: *mut Env,
- len: usize,
- data: *mut u8,
- result_data: *mut *mut u8,
- result: *mut napi_value,
+#[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_env!(env);
- let env = unsafe { &mut *env };
- let value = v8::ArrayBuffer::new(&mut env.scope(), len);
- let ptr = get_array_buffer_ptr(value);
- std::ptr::copy(data, ptr, len);
- if !result_data.is_null() {
- *result_data = ptr;
- }
- let value = v8::Uint8Array::new(&mut env.scope(), value, 0, len).unwrap();
- let value: v8::Local<v8::Value> = value.into();
- *result = value.into();
+ 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::napi_sym]
-fn napi_coerce_to_bool(
- env: *mut Env,
+#[napi_sym]
+fn napi_set_property(
+ env: &mut Env,
+ object: napi_value,
+ key: napi_value,
value: napi_value,
- result: *mut napi_value,
) -> napi_status {
- check_env!(env);
- let env = unsafe { &mut *env };
- let value = napi_value_unchecked(value);
- let coerced = value.to_boolean(&mut env.scope());
- let value: v8::Local<v8::Value> = coerced.into();
- *result = value.into();
+ 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::napi_sym]
-fn napi_coerce_to_number(
- env: *mut Env,
- value: napi_value,
- result: *mut napi_value,
+#[napi_sym]
+fn napi_has_property(
+ env: &mut Env,
+ object: napi_value,
+ key: napi_value,
+ result: *mut bool,
) -> napi_status {
- check_env!(env);
- let env = unsafe { &mut *env };
- let value = napi_value_unchecked(value);
- let Some(coerced) = value.to_number(&mut env.scope()) else {
- return napi_number_expected;
+ 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 value: v8::Local<v8::Value> = coerced.into();
- *result = value.into();
+
+ let Some(has) = object.has(scope, key.unwrap()) else {
+ return napi_generic_failure;
+ };
+
+ unsafe {
+ *result = has;
+ }
+
napi_ok
}
-#[napi_sym::napi_sym]
-fn napi_coerce_to_object(
- env: *mut Env,
- value: napi_value,
- result: *mut napi_value,
+#[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_env!(env);
- let env = unsafe { &mut *env };
- let value = napi_value_unchecked(value);
- let coerced = value.to_object(&mut env.scope()).unwrap();
- let value: v8::Local<v8::Value> = coerced.into();
- *result = value.into();
+ 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::napi_sym]
-fn napi_coerce_to_string(
- env: *mut Env,
- value: napi_value,
- result: *mut napi_value,
+#[napi_sym]
+fn napi_delete_property(
+ env: &mut Env,
+ object: napi_value,
+ key: napi_value,
+ result: *mut bool,
) -> napi_status {
- check_env!(env);
- let env = unsafe { &mut *env };
- let value = napi_value_unchecked(value);
- let coerced = value.to_string(&mut env.scope()).unwrap();
- let value: v8::Local<v8::Value> = coerced.into();
- *result = value.into();
+ 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::napi_sym]
-fn napi_create_dataview(
- env: *mut Env,
- len: usize,
- data: *mut *mut u8,
- byte_offset: usize,
- result: *mut napi_value,
+#[napi_sym]
+fn napi_has_own_property(
+ env: &mut Env,
+ object: napi_value,
+ key: napi_value,
+ result: *mut bool,
) -> napi_status {
- check_env!(env);
- check_arg!(env, data);
- let env = unsafe { &mut *env };
+ check_arg!(env, key);
check_arg!(env, result);
- let value = v8::ArrayBuffer::new(&mut env.scope(), len);
- if !data.is_null() {
- *data = get_array_buffer_ptr(value);
- }
- let context = &mut env.scope().get_current_context();
- let global = context.global(&mut env.scope());
- let data_view_name = v8::String::new(&mut env.scope(), "DataView").unwrap();
- let data_view = global.get(&mut env.scope(), data_view_name.into()).unwrap();
- let Ok(data_view) = v8::Local::<v8::Function>::try_from(data_view) else {
- return napi_function_expected;
+
+ let scope = &mut env.scope();
+
+ let Some(object) = object.and_then(|o| o.to_object(scope)) else {
+ return napi_object_expected;
};
- let byte_offset = v8::Number::new(&mut env.scope(), byte_offset as f64);
- let byte_length = v8::Number::new(&mut env.scope(), len as f64);
- let value = data_view
- .new_instance(
- &mut env.scope(),
- &[value.into(), byte_offset.into(), byte_length.into()],
- )
- .unwrap();
- let value: v8::Local<v8::Value> = value.into();
- *result = value.into();
- napi_ok
-}
-#[napi_sym::napi_sym]
-fn napi_create_date(
- env: *mut Env,
- time: f64,
- result: *mut napi_value,
-) -> napi_status {
- check_env!(env);
- let env = unsafe { &mut *env };
- let value: v8::Local<v8::Value> =
- v8::Date::new(&mut env.scope(), time).unwrap().into();
- *result = value.into();
+ 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::napi_sym]
-fn napi_create_double(
- env: *mut Env,
- value: f64,
- result: *mut napi_value,
+#[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 {
- check_env!(env);
+ let env_ptr = env as *mut Env;
check_arg!(env, result);
- let env = unsafe { &mut *env };
- *result = v8::Number::new(&mut env.scope(), value).into();
+
+ 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
}
-fn set_error_code(
- env: *mut Env,
- error: v8::Local<v8::Value>,
- code: napi_value,
- code_cstring: *const c_char,
+#[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 {
- if code.is_some() || !code_cstring.is_null() {
- let err_object: v8::Local<v8::Object> = error.try_into().unwrap();
+ check_arg!(env, value);
+ let env_ptr = env as *mut Env;
- let code_value: v8::Local<v8::Value> = if code.is_some() {
- let mut code_value = unsafe { napi_value_unchecked(code) };
- return_status_if_false!(
- env,
- code_value.is_string(),
- napi_string_expected
- );
- code_value
- } else {
- let name = match check_new_from_utf8(env, code_cstring) {
- Ok(s) => s,
- Err(status) => return status,
- };
- name.into()
- };
+ 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 mut scope = unsafe { &mut *env }.scope();
- let code_key = v8::String::new(&mut scope, "code").unwrap();
+ let value = value.unwrap();
- if err_object
- .set(&mut scope, code_key.into(), code_value)
- .is_none()
- {
- return napi_generic_failure;
- }
+ if !object
+ .set(&mut env.scope(), key.into(), value)
+ .unwrap_or(false)
+ {
+ return napi_generic_failure;
}
napi_ok
}
-#[napi_sym::napi_sym]
-fn napi_create_error(
- env: *mut Env,
- code: napi_value,
- msg: napi_value,
- result: *mut napi_value,
+#[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_env!(env);
- check_arg_option!(env, msg);
check_arg!(env, result);
- let mut message_value = napi_value_unchecked(msg);
- return_status_if_false!(env, message_value.is_string(), napi_string_expected);
- let error_obj = v8::Exception::error(
- &mut unsafe { &mut *env }.scope(),
- message_value.try_into().unwrap(),
- );
- status_call!(set_error_code(env, error_obj, code, std::ptr::null()));
- *result = error_obj.into();
- napi_clear_last_error(env);
+ 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::napi_sym]
-fn napi_create_type_error(
- env: *mut Env,
- code: napi_value,
- msg: napi_value,
- result: *mut napi_value,
+#[napi_sym]
+fn napi_set_element<'s>(
+ env: &'s mut Env,
+ object: napi_value<'s>,
+ index: u32,
+ value: napi_value<'s>,
) -> napi_status {
- check_env!(env);
- check_arg_option!(env, msg);
- check_arg!(env, result);
- let mut message_value = napi_value_unchecked(msg);
- return_status_if_false!(env, message_value.is_string(), napi_string_expected);
- let error_obj = v8::Exception::type_error(
- &mut unsafe { &mut *env }.scope(),
- message_value.try_into().unwrap(),
- );
- status_call!(set_error_code(env, error_obj, code, std::ptr::null()));
- *result = error_obj.into();
- napi_clear_last_error(env);
+ 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::napi_sym]
-fn napi_create_range_error(
- env: *mut Env,
- code: napi_value,
- msg: napi_value,
- result: *mut napi_value,
+#[napi_sym]
+fn napi_has_element(
+ env: &mut Env,
+ object: napi_value,
+ index: u32,
+ result: *mut bool,
) -> napi_status {
- check_env!(env);
- check_arg_option!(env, msg);
check_arg!(env, result);
- let mut message_value = napi_value_unchecked(msg);
- return_status_if_false!(env, message_value.is_string(), napi_string_expected);
- let error_obj = v8::Exception::range_error(
- &mut unsafe { &mut *env }.scope(),
- message_value.try_into().unwrap(),
- );
- status_call!(set_error_code(env, error_obj, code, std::ptr::null()));
- *result = error_obj.into();
- napi_clear_last_error(env);
- napi_ok
-}
-#[napi_sym::napi_sym]
-fn napi_create_external(
- env_ptr: *mut Env,
- value: *mut c_void,
- finalize_cb: napi_finalize,
- finalize_hint: *mut c_void,
- result: *mut napi_value,
-) -> napi_status {
- check_env!(env_ptr);
- let env = unsafe { &mut *env_ptr };
- let external: v8::Local<v8::Value> =
- v8::External::new(&mut env.scope(), value).into();
+ 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;
+ };
- let value = weak_local(env_ptr, external, value, finalize_cb, finalize_hint);
+ unsafe {
+ *result = has;
+ }
- *result = transmute(value);
napi_ok
}
-pub type BackingStoreDeleterCallback = unsafe extern "C" fn(
- data: *mut c_void,
- byte_length: usize,
- deleter_data: *mut c_void,
-);
+#[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);
-extern "C" {
- fn v8__ArrayBuffer__NewBackingStore__with_data(
- data: *mut c_void,
- byte_length: usize,
- deleter: BackingStoreDeleterCallback,
- deleter_data: *mut c_void,
- ) -> *mut BackingStore;
-}
+ let scope = &mut env.scope();
-struct BufferFinalizer {
- env: *mut Env,
- finalize_cb: napi_finalize,
- finalize_data: *mut c_void,
- finalize_hint: *mut c_void,
-}
+ let Some(object) = object.and_then(|o| o.to_object(scope)) else {
+ return napi_object_expected;
+ };
-impl BufferFinalizer {
- fn into_raw(self) -> *mut BufferFinalizer {
- Box::into_raw(Box::new(self))
+ let Some(value) = object.get_index(scope, index) else {
+ return napi_generic_failure;
+ };
+
+ unsafe {
+ *result = value.into();
}
+
+ napi_ok
}
-impl Drop for BufferFinalizer {
- fn drop(&mut self) {
+#[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 {
- (self.finalize_cb)(self.env as _, self.finalize_data, self.finalize_hint);
+ *result = deleted;
}
}
-}
-pub extern "C" fn backing_store_deleter_callback(
- data: *mut c_void,
- _byte_length: usize,
- deleter_data: *mut c_void,
-) {
- let mut finalizer =
- unsafe { Box::from_raw(deleter_data as *mut BufferFinalizer) };
-
- finalizer.finalize_data = data;
+ napi_ok
}
-#[napi_sym::napi_sym]
-fn napi_create_external_arraybuffer(
- env_ptr: *mut Env,
- data: *mut c_void,
- byte_length: usize,
- finalize_cb: napi_finalize,
- finalize_hint: *mut c_void,
- result: *mut napi_value,
+#[napi_sym]
+fn napi_define_properties(
+ env: &mut Env,
+ object: napi_value,
+ property_count: usize,
+ properties: *const napi_property_descriptor,
) -> napi_status {
- check_env!(env_ptr);
- let env = unsafe { &mut *env_ptr };
+ let env_ptr = env as *mut Env;
- let finalizer = BufferFinalizer {
- env: env_ptr,
- finalize_data: ptr::null_mut(),
- finalize_cb,
- finalize_hint,
+ 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 store: UniqueRef<BackingStore> =
- transmute(v8__ArrayBuffer__NewBackingStore__with_data(
- data,
- byte_length,
- backing_store_deleter_callback,
- finalizer.into_raw() as _,
- ));
+ 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 ab =
- v8::ArrayBuffer::with_backing_store(&mut env.scope(), &store.make_shared());
- let value: v8::Local<v8::Value> = ab.into();
+ 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 property.getter.is_some() {
+ unsafe {
+ create_function(env_ptr, None, property.getter, property.data).into()
+ }
+ } else {
+ v8::undefined(scope).into()
+ };
+ let local_setter: v8::Local<v8::Value> = if property.setter.is_some() {
+ unsafe {
+ create_function(env_ptr, None, property.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 property.method.is_some() {
+ let method: v8::Local<v8::Value> = {
+ let function = unsafe {
+ create_function(env_ptr, None, property.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;
+ }
+ }
+ }
+ }
- *result = value.into();
napi_ok
}
-#[napi_sym::napi_sym]
-fn napi_create_external_buffer(
- env_ptr: *mut Env,
- byte_length: usize,
- data: *mut c_void,
- finalize_cb: napi_finalize,
- finalize_hint: *mut c_void,
- result: *mut napi_value,
-) -> napi_status {
- check_env!(env_ptr);
- let env = unsafe { &mut *env_ptr };
- let finalizer = BufferFinalizer {
- env: env_ptr,
- finalize_data: ptr::null_mut(),
- finalize_cb,
- finalize_hint,
+#[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;
};
- let store: UniqueRef<BackingStore> =
- transmute(v8__ArrayBuffer__NewBackingStore__with_data(
- data,
- byte_length,
- backing_store_deleter_callback,
- finalizer.into_raw() as _,
- ));
+ if !object
+ .set_integrity_level(scope, v8::IntegrityLevel::Frozen)
+ .unwrap_or(false)
+ {
+ return napi_generic_failure;
+ }
- let ab =
- v8::ArrayBuffer::with_backing_store(&mut env.scope(), &store.make_shared());
- let value =
- v8::Uint8Array::new(&mut env.scope(), ab, 0, byte_length).unwrap();
- let value: v8::Local<v8::Value> = value.into();
- *result = value.into();
napi_ok
}
-#[napi_sym::napi_sym]
-fn napi_create_function(
+#[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,
- name: *const c_char,
- length: usize,
- cb: napi_callback,
- cb_info: napi_callback_info,
- result: *mut napi_value,
+ value: napi_value,
+ result: *mut bool,
) -> napi_status {
- check_env!(env);
+ let env = check_env!(env);
+ check_arg!(env, value);
check_arg!(env, result);
- check_arg_option!(env, cb);
- let name = if let Some(name) = name.as_ref() {
- match check_new_from_utf8_len(env, name, length) {
- Ok(s) => Some(s),
- Err(status) => return status,
+ 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
}
- } else {
- None
- };
+ 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());
+ }
- *result = create_function(env, name, cb, cb_info).into();
napi_ok
}
-#[napi_sym::napi_sym]
-fn napi_create_int32(
- env: *mut Env,
- value: i32,
- result: *mut napi_value,
+#[napi_sym]
+fn napi_get_prototype<'s>(
+ env: &'s mut Env,
+ object: napi_value,
+ result: *mut napi_value<'s>,
) -> napi_status {
- check_env!(env);
check_arg!(env, result);
- let env = unsafe { &mut *env };
- *result = v8::Integer::new(&mut env.scope(), value).into();
+
+ 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::napi_sym]
-fn napi_create_uint32(
- env: *mut Env,
- value: u32,
+#[napi_sym]
+fn napi_create_object(
+ env_ptr: *mut Env,
result: *mut napi_value,
) -> napi_status {
- check_env!(env);
+ let env = check_env!(env_ptr);
check_arg!(env, result);
- let env = unsafe { &mut *env };
- *result = v8::Integer::new_from_unsigned(&mut env.scope(), value).into();
- napi_ok
+
+ unsafe {
+ *result = v8::Object::new(&mut env.scope()).into();
+ }
+
+ return napi_clear_last_error(env_ptr);
}
-#[napi_sym::napi_sym]
-fn napi_create_int64(
- env: *mut Env,
- value: i64,
+#[napi_sym]
+fn napi_create_array(
+ env_ptr: *mut Env,
result: *mut napi_value,
) -> napi_status {
- check_env!(env);
+ let env = check_env!(env_ptr);
check_arg!(env, result);
- let env = unsafe { &mut *env };
- *result = v8::Number::new(&mut env.scope(), value as f64).into();
- napi_ok
-}
-#[napi_sym::napi_sym]
-fn napi_create_object(env: *mut Env, result: *mut napi_value) -> napi_status {
- check_env!(env);
- let env = unsafe { &mut *env };
- let object = v8::Object::new(&mut env.scope());
- *result = object.into();
- napi_ok
+ unsafe {
+ *result = v8::Array::new(&mut env.scope(), 0).into();
+ }
+
+ return napi_clear_last_error(env_ptr);
}
-#[napi_sym::napi_sym]
-fn napi_create_promise(
- env: *mut Env,
- deferred: *mut napi_deferred,
- promise_out: *mut napi_value,
+#[napi_sym]
+fn napi_create_array_with_length(
+ env_ptr: *mut Env,
+ length: usize,
+ result: *mut napi_value,
) -> napi_status {
- check_env!(env);
- let env = unsafe { &mut *env };
- let resolver = v8::PromiseResolver::new(&mut env.scope()).unwrap();
- let mut global = v8::Global::new(&mut env.scope(), resolver);
- let mut global_ptr = global.into_raw();
- let promise = resolver.get_promise(&mut env.scope());
- *deferred = global_ptr.as_mut() as *mut _ as napi_deferred;
- *promise_out = promise.into();
+ let env = check_env!(env_ptr);
+ check_arg!(env, result);
- napi_ok
+ unsafe {
+ *result = v8::Array::new(&mut env.scope(), length as _).into();
+ }
+
+ return napi_clear_last_error(env_ptr);
}
-#[napi_sym::napi_sym]
-fn napi_create_reference(
- env: *mut Env,
- value: napi_value,
- _initial_refcount: u32,
- result: *mut napi_ref,
+#[napi_sym]
+fn napi_create_string_latin1(
+ env_ptr: *mut Env,
+ string: *const c_char,
+ length: usize,
+ result: *mut napi_value,
) -> napi_status {
- check_env!(env);
- let env = unsafe { &mut *env };
+ 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 value = napi_value_unchecked(value);
- let global = v8::Global::new(&mut env.scope(), value);
- let mut global_ptr = global.into_raw();
- *result = transmute::<NonNull<v8::Value>, napi_ref>(global_ptr);
- napi_ok
+ 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::napi_sym]
-fn napi_create_string_latin1(
- env: *mut Env,
- string: *const u8,
+#[napi_sym]
+fn napi_create_string_utf8(
+ env_ptr: *mut Env,
+ string: *const c_char,
length: usize,
result: *mut napi_value,
) -> napi_status {
- check_env!(env);
- let env = unsafe { &mut *env };
+ let env = check_env!(env_ptr);
if length > 0 {
check_arg!(env, string);
}
- check_arg!(env, result);
- return_status_if_false!(
+ crate::return_status_if_false!(
env,
(length == NAPI_AUTO_LENGTH) || length <= INT_MAX as _,
napi_invalid_arg
);
- let string = if length == NAPI_AUTO_LENGTH {
- std::ffi::CStr::from_ptr(string as *const _)
- .to_str()
- .unwrap()
- .as_bytes()
+ 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 {
- std::slice::from_raw_parts(string, length)
+ &[]
};
- let Some(v8str) = v8::String::new_from_one_byte(
+
+ let Some(string) = v8::String::new_from_utf8(
&mut env.scope(),
- string,
+ buffer,
v8::NewStringType::Normal,
) else {
- return napi_generic_failure;
+ return napi_set_last_error(env_ptr, napi_generic_failure);
};
- *result = v8str.into();
- napi_ok
+ unsafe {
+ *result = string.into();
+ }
+
+ return napi_clear_last_error(env_ptr);
}
-#[napi_sym::napi_sym]
+#[napi_sym]
fn napi_create_string_utf16(
- env: *mut Env,
+ env_ptr: *mut Env,
string: *const u16,
length: usize,
result: *mut napi_value,
) -> napi_status {
- check_env!(env);
- let env = unsafe { &mut *env };
+ let env = check_env!(env_ptr);
if length > 0 {
check_arg!(env, string);
}
- check_arg!(env, result);
- return_status_if_false!(
+ crate::return_status_if_false!(
env,
(length == NAPI_AUTO_LENGTH) || length <= INT_MAX as _,
napi_invalid_arg
);
- let string = if length == NAPI_AUTO_LENGTH {
- let s = std::ffi::CStr::from_ptr(string as *const _)
- .to_str()
- .unwrap();
- std::slice::from_raw_parts(s.as_ptr() as *const u16, s.len())
+ 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 {
- std::slice::from_raw_parts(string, length)
+ &[]
};
- match v8::String::new_from_two_byte(
+ let Some(string) = v8::String::new_from_two_byte(
&mut env.scope(),
- string,
+ buffer,
v8::NewStringType::Normal,
- ) {
- Some(v8str) => {
- *result = v8str.into();
- }
- None => return napi_generic_failure,
+ ) else {
+ return napi_set_last_error(env_ptr, napi_generic_failure);
+ };
+
+ unsafe {
+ *result = string.into();
}
- napi_ok
+
+ return napi_clear_last_error(env_ptr);
}
-#[napi_sym::napi_sym]
-fn napi_create_string_utf8(
- env: *mut Env,
- string: *const u8,
+#[napi_sym]
+fn node_api_create_external_string_latin1(
+ env_ptr: *mut Env,
+ _string: *const c_char,
+ _length: usize,
+ _nogc_finalize_callback: napi_finalize,
+ _finalize_hint: *mut c_void,
+ _result: *mut napi_value,
+ _copied: *mut bool,
+) -> napi_status {
+ return napi_set_last_error(env_ptr, napi_generic_failure);
+}
+
+#[napi_sym]
+fn node_api_create_external_string_utf16(
+ env_ptr: *mut Env,
+ _string: *const u16,
+ _length: usize,
+ _nogc_finalize_callback: napi_finalize,
+ _finalize_hint: *mut c_void,
+ _result: *mut napi_value,
+ _copied: *mut bool,
+) -> napi_status {
+ return napi_set_last_error(env_ptr, napi_generic_failure);
+}
+
+#[napi_sym]
+fn node_api_create_property_key_utf16(
+ env_ptr: *mut Env,
+ string: *const u16,
length: usize,
result: *mut napi_value,
) -> napi_status {
- check_env!(env);
- let env = unsafe { &mut *env };
+ let env = check_env!(env_ptr);
if length > 0 {
check_arg!(env, string);
}
- check_arg!(env, result);
- return_status_if_false!(
+ crate::return_status_if_false!(
env,
(length == NAPI_AUTO_LENGTH) || length <= INT_MAX as _,
napi_invalid_arg
);
- let string = if length == NAPI_AUTO_LENGTH {
- std::ffi::CStr::from_ptr(string as *const _)
- .to_str()
- .unwrap()
+ 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 string = std::slice::from_raw_parts(string, length);
- std::str::from_utf8(string).unwrap()
+ &[]
};
- let v8str = v8::String::new(&mut env.scope(), string).unwrap();
- *result = v8str.into();
+
+ 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::napi_sym]
-fn napi_create_symbol(
+#[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 {
- check_env!(env);
+ let env = check_env!(env_ptr);
check_arg!(env, result);
- let env = unsafe { &mut *env };
- let scope = &mut env.scope();
let description = if let Some(d) = *description {
- let Some(d) = d.to_string(scope) else {
- return napi_string_expected;
+ let Some(d) = d.to_string(&mut env.scope()) else {
+ return napi_set_last_error(env, napi_string_expected);
};
Some(d)
} else {
None
};
- *result = v8::Symbol::new(scope, description).into();
- napi_ok
+
+ unsafe {
+ *result = v8::Symbol::new(&mut env.scope(), description).into();
+ }
+
+ return napi_clear_last_error(env_ptr);
}
-#[napi_sym::napi_sym]
-fn napi_create_typedarray(
+#[napi_sym]
+fn node_api_symbol_for(
env: *mut Env,
- ty: napi_typedarray_type,
+ utf8description: *const c_char,
length: usize,
- arraybuffer: napi_value,
- byte_offset: usize,
result: *mut napi_value,
) -> napi_status {
- check_env!(env);
- let env = unsafe { &mut *env };
- let ab = napi_value_unchecked(arraybuffer);
- let Ok(ab) = v8::Local::<v8::ArrayBuffer>::try_from(ab) else {
- return napi_arraybuffer_expected;
- };
- let typedarray: v8::Local<v8::Value> = match ty {
- napi_uint8_array => {
- v8::Uint8Array::new(&mut env.scope(), ab, byte_offset, length)
- .unwrap()
- .into()
- }
- napi_uint8_clamped_array => {
- v8::Uint8ClampedArray::new(&mut env.scope(), ab, byte_offset, length)
- .unwrap()
- .into()
- }
- napi_int8_array => {
- v8::Int8Array::new(&mut env.scope(), ab, byte_offset, length)
- .unwrap()
- .into()
- }
- napi_uint16_array => {
- v8::Uint16Array::new(&mut env.scope(), ab, byte_offset, length)
- .unwrap()
- .into()
- }
- napi_int16_array => {
- v8::Int16Array::new(&mut env.scope(), ab, byte_offset, length)
- .unwrap()
- .into()
- }
- napi_uint32_array => {
- v8::Uint32Array::new(&mut env.scope(), ab, byte_offset, length)
- .unwrap()
- .into()
- }
- napi_int32_array => {
- v8::Int32Array::new(&mut env.scope(), ab, byte_offset, length)
- .unwrap()
- .into()
- }
- napi_float32_array => {
- v8::Float32Array::new(&mut env.scope(), ab, byte_offset, length)
- .unwrap()
- .into()
- }
- napi_float64_array => {
- v8::Float64Array::new(&mut env.scope(), ab, byte_offset, length)
- .unwrap()
- .into()
- }
- napi_bigint64_array => {
- v8::BigInt64Array::new(&mut env.scope(), ab, byte_offset, length)
- .unwrap()
- .into()
- }
- napi_biguint64_array => {
- v8::BigUint64Array::new(&mut env.scope(), ab, byte_offset, length)
- .unwrap()
- .into()
+ {
+ 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();
}
- _ => {
- return napi_invalid_arg;
+ }
+
+ 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);
};
- *result = typedarray.into();
+
+ 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::napi_sym]
-fn napi_make_callback(
+#[napi_sym]
+fn napi_get_new_target(
env: *mut Env,
- async_context: *mut c_void,
+ 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: isize,
+ argc: usize,
argv: *const napi_value,
result: *mut napi_value,
) -> napi_status {
- check_env!(env);
- let env = unsafe { &mut *env };
- check_arg_option!(env, recv);
- if argc > 0 {
+ 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();
+ }
}
- if !async_context.is_null() {
- log::info!("napi_make_callback: async_context is not supported");
+ return napi_clear_last_error(env_ptr);
+}
+
+#[napi_sym]
+fn napi_get_global(env: *mut Env, result: *mut napi_value) -> napi_status {
+ let env = check_env!(env);
+ check_arg!(env, result);
+
+ unsafe {
+ *result = std::mem::transmute::<NonNull<v8::Value>, v8::Local<v8::Value>>(
+ env.global,
+ )
+ .into();
}
- let recv = napi_value_unchecked(recv);
- let func = napi_value_unchecked(func);
+ return napi_clear_last_error(env);
+}
- let Ok(func) = v8::Local::<v8::Function>::try_from(func) else {
- return napi_function_expected;
+#[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);
};
- let argv: &[v8::Local<v8::Value>] =
- transmute(std::slice::from_raw_parts(argv, argc as usize));
- let ret = func.call(&mut env.scope(), recv, argv);
- *result = transmute::<Option<v8::Local<v8::Value>>, napi_value>(ret);
- napi_ok
+
+ 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::napi_sym]
+#[napi_sym]
fn napi_get_value_bigint_int64(
- env: *mut Env,
+ env_ptr: *mut Env,
value: napi_value,
result: *mut i64,
lossless: *mut bool,
) -> napi_status {
- check_env!(env);
- let env = unsafe { &mut *env };
- let value = napi_value_unchecked(value);
- let bigint = value.to_big_int(&mut env.scope()).unwrap();
+ 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();
- *result = result_;
- *lossless = lossless_;
- // TODO(bartlomieju):
- // napi_clear_last_error()
- napi_ok
+
+ unsafe {
+ *result = result_;
+ *lossless = lossless_;
+ }
+
+ return napi_clear_last_error(env_ptr);
}
-#[napi_sym::napi_sym]
+#[napi_sym]
fn napi_get_value_bigint_uint64(
- env: *mut Env,
+ env_ptr: *mut Env,
value: napi_value,
result: *mut u64,
lossless: *mut bool,
) -> napi_status {
- check_env!(env);
- let env = unsafe { &mut *env };
- let value = napi_value_unchecked(value);
- let bigint = value.to_big_int(&mut env.scope()).unwrap();
+ 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();
- *result = result_;
- *lossless = lossless_;
- // TODO(bartlomieju):
- // napi_clear_last_error()
- napi_ok
+
+ unsafe {
+ *result = result_;
+ *lossless = lossless_;
+ }
+
+ return napi_clear_last_error(env_ptr);
}
-#[napi_sym::napi_sym]
+#[napi_sym]
fn napi_get_value_bigint_words(
- env: *mut Env,
+ env_ptr: *mut Env,
value: napi_value,
sign_bit: *mut i32,
word_count: *mut usize,
words: *mut u64,
) -> napi_status {
- check_env!(env);
- // TODO(bartlomieju):
- // check_arg!(env, value);
+ let env = check_env!(env_ptr);
+ check_arg!(env, value);
check_arg!(env, word_count);
- let env = unsafe { &mut *env };
- let value = napi_value_unchecked(value);
- let big = match value.to_big_int(&mut env.scope()) {
- Some(b) => b,
- None => return napi_bigint_expected,
+ 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 = big.word_count();
+ word_count_int = bigint.word_count();
} else {
check_arg!(env, sign_bit);
check_arg!(env, words);
- let out_words = std::slice::from_raw_parts_mut(words, *word_count);
- let (sign, slice_) = big.to_words_array(out_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();
- *sign_bit = sign as i32;
+ unsafe {
+ *sign_bit = sign as i32;
+ }
}
- *word_count = word_count_int;
- // TODO(bartlomieju):
- // napi_clear_last_error()
- napi_ok
+ unsafe {
+ *word_count = word_count_int;
+ }
+
+ return napi_clear_last_error(env_ptr);
}
-#[napi_sym::napi_sym]
+#[napi_sym]
fn napi_get_value_bool(
- env: *mut Env,
+ env_ptr: *mut Env,
value: napi_value,
result: *mut bool,
) -> napi_status {
- check_env!(env);
- let env = unsafe { &mut *env };
- let value = napi_value_unchecked(value);
- *result = value.boolean_value(&mut env.scope());
- napi_ok
-}
+ let env = check_env!(env_ptr);
+ check_arg!(env, value);
+ check_arg!(env, result);
-#[napi_sym::napi_sym]
-fn napi_get_value_double(
- env: *mut Env,
- value: napi_value,
- result: *mut f64,
-) -> napi_status {
- check_env!(env);
- let env = unsafe { &mut *env };
- let value = napi_value_unchecked(value);
- return_status_if_false!(env, value.is_number(), napi_number_expected);
- *result = value.number_value(&mut env.scope()).unwrap();
- napi_ok
-}
+ 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);
+ };
-#[napi_sym::napi_sym]
-fn napi_get_value_external(
- _env: *mut Env,
- value: napi_value,
- result: *mut *mut c_void,
-) -> napi_status {
- let value = napi_value_unchecked(value);
- let ext = v8::Local::<v8::External>::try_from(value).unwrap();
- *result = ext.value();
- napi_ok
-}
+ unsafe {
+ *result = boolean.is_true();
+ }
-#[napi_sym::napi_sym]
-fn napi_get_value_int32(
- env: *mut Env,
- value: napi_value,
- result: *mut i32,
-) -> napi_status {
- check_env!(env);
- let env = unsafe { &mut *env };
- let value = napi_value_unchecked(value);
- *result = value.int32_value(&mut env.scope()).unwrap();
- napi_ok
+ return napi_clear_last_error(env_ptr);
}
-#[napi_sym::napi_sym]
-fn napi_get_value_int64(
- env: *mut Env,
- value: napi_value,
- result: *mut i64,
-) -> napi_status {
- check_env!(env);
- let env = unsafe { &mut *env };
- let value = napi_value_unchecked(value);
- *result = value.integer_value(&mut env.scope()).unwrap();
- napi_ok
-}
-
-#[napi_sym::napi_sym]
+#[napi_sym]
fn napi_get_value_string_latin1(
- env: *mut Env,
+ env_ptr: *mut Env,
value: napi_value,
- buf: *mut u8,
+ buf: *mut c_char,
bufsize: usize,
result: *mut usize,
) -> napi_status {
- check_env!(env);
- let env = unsafe { &mut *env };
-
- let value = napi_value_unchecked(value);
-
- if !value.is_string() && !value.is_string_object() {
- return napi_string_expected;
- }
+ let env = check_env!(env_ptr);
+ check_arg!(env, value);
- let v8str = value.to_string(&mut env.scope()).unwrap();
- let string_len = v8str.utf8_length(&mut env.scope());
+ 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() {
- *result = string_len;
+ check_arg!(env, result);
+ unsafe {
+ *result = value.length();
+ }
} else if bufsize != 0 {
- let buffer = std::slice::from_raw_parts_mut(buf, bufsize - 1);
- let copied = v8str.write_one_byte(
+ 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,
);
- buf.add(copied).write(0);
+ unsafe {
+ buf.add(copied).write(0);
+ }
if !result.is_null() {
- *result = copied;
+ unsafe {
+ *result = copied;
+ }
}
} else if !result.is_null() {
- *result = string_len;
+ unsafe {
+ *result = 0;
+ }
}
- napi_ok
+ napi_clear_last_error(env_ptr)
}
-#[napi_sym::napi_sym]
+#[napi_sym]
fn napi_get_value_string_utf8(
- env: *mut Env,
+ env_ptr: *mut Env,
value: napi_value,
buf: *mut u8,
bufsize: usize,
result: *mut usize,
) -> napi_status {
- check_env!(env);
- let env = unsafe { &mut *env };
-
- let value = napi_value_unchecked(value);
+ let env = check_env!(env_ptr);
+ check_arg!(env, value);
- if !value.is_string() && !value.is_string_object() {
- return napi_string_expected;
- }
-
- let v8str = value.to_string(&mut env.scope()).unwrap();
- let string_len = v8str.utf8_length(&mut env.scope());
+ 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() {
- *result = string_len;
+ check_arg!(env, result);
+ unsafe {
+ *result = value.length();
+ }
} else if bufsize != 0 {
- let buffer = std::slice::from_raw_parts_mut(buf, bufsize - 1);
- let copied = v8str.write_utf8(
+ 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::NO_NULL_TERMINATION
- | v8::WriteOptions::REPLACE_INVALID_UTF8,
+ v8::WriteOptions::REPLACE_INVALID_UTF8
+ | v8::WriteOptions::NO_NULL_TERMINATION,
);
- buf.add(copied).write(0);
+ unsafe {
+ buf.add(copied).write(0);
+ }
if !result.is_null() {
- *result = copied;
+ unsafe {
+ *result = copied;
+ }
}
} else if !result.is_null() {
- *result = string_len;
+ unsafe {
+ *result = 0;
+ }
}
- napi_ok
+ napi_clear_last_error(env_ptr)
}
-#[napi_sym::napi_sym]
+#[napi_sym]
fn napi_get_value_string_utf16(
- env: *mut Env,
+ env_ptr: *mut Env,
value: napi_value,
buf: *mut u16,
bufsize: usize,
result: *mut usize,
) -> napi_status {
- check_env!(env);
- let env = unsafe { &mut *env };
-
- let value = napi_value_unchecked(value);
-
- if !value.is_string() && !value.is_string_object() {
- return napi_string_expected;
- }
+ let env = check_env!(env_ptr);
+ check_arg!(env, value);
- let v8str = value.to_string(&mut env.scope()).unwrap();
- let string_len = v8str.length();
+ 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() {
- *result = string_len;
+ check_arg!(env, result);
+ unsafe {
+ *result = value.length();
+ }
} else if bufsize != 0 {
- let buffer = std::slice::from_raw_parts_mut(buf, bufsize - 1);
- let copied = v8str.write(
+ 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,
);
- buf.add(copied).write(0);
+ unsafe {
+ buf.add(copied).write(0);
+ }
if !result.is_null() {
- *result = copied;
+ unsafe {
+ *result = copied;
+ }
}
} else if !result.is_null() {
- *result = string_len;
+ unsafe {
+ *result = 0;
+ }
}
- napi_ok
+ napi_clear_last_error(env_ptr)
}
-#[napi_sym::napi_sym]
-fn napi_get_value_uint32(
- env: *mut Env,
+#[napi_sym]
+fn napi_coerce_to_bool<'s>(
+ env: &'s mut Env,
value: napi_value,
- result: *mut u32,
+ result: *mut napi_value<'s>,
) -> napi_status {
- check_env!(env);
- let env = unsafe { &mut *env };
- let value = napi_value_unchecked(value);
- *result = value.uint32_value(&mut env.scope()).unwrap();
+ 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::napi_sym]
-fn napi_add_finalizer(
- env_ptr: *mut Env,
- js_object: napi_value,
- native_object: *mut c_void,
- finalize_cb: napi_finalize,
- finalize_hint: *mut c_void,
- result: *mut napi_ref,
+#[napi_sym]
+fn napi_coerce_to_number<'s>(
+ env: &'s mut Env,
+ value: napi_value,
+ result: *mut napi_value<'s>,
) -> napi_status {
- check_env!(env_ptr);
+ check_arg!(env, value);
+ check_arg!(env, result);
- let value = napi_value_unchecked(js_object);
- let value =
- weak_local(env_ptr, value, native_object, finalize_cb, finalize_hint);
+ let Some(coerced) = value.unwrap().to_number(&mut env.scope()) else {
+ return napi_number_expected;
+ };
- if !result.is_null() {
- *result = transmute(value);
+ unsafe {
+ *result = coerced.into();
}
napi_ok
}
-#[napi_sym::napi_sym]
-fn napi_adjust_external_memory(
- env: *mut Env,
- change_in_bytes: i64,
- adjusted_value: *mut i64,
+#[napi_sym]
+fn napi_coerce_to_object<'s>(
+ env: &'s mut Env,
+ value: napi_value,
+ result: *mut napi_value<'s>,
) -> napi_status {
- check_env!(env);
- check_arg!(env, adjusted_value);
+ check_arg!(env, value);
+ check_arg!(env, result);
- let env = unsafe { &mut *env };
- let isolate = &mut *env.isolate_ptr;
- *adjusted_value =
- isolate.adjust_amount_of_external_allocated_memory(change_in_bytes);
+ let Some(coerced) = value.unwrap().to_object(&mut env.scope()) else {
+ return napi_object_expected;
+ };
+
+ unsafe {
+ *result = coerced.into();
+ }
- napi_clear_last_error(env);
napi_ok
}
-#[napi_sym::napi_sym]
-fn napi_call_function(
- env: *mut Env,
- recv: napi_value,
- func: napi_value,
- argc: usize,
- argv: *const napi_value,
- result: *mut napi_value,
+#[napi_sym]
+fn napi_coerce_to_string<'s>(
+ env: &'s mut Env,
+ value: napi_value,
+ result: *mut napi_value<'s>,
) -> napi_status {
- check_env!(env);
- let env = unsafe { &mut *env };
- let recv = napi_value_unchecked(recv);
- let func = napi_value_unchecked(func);
- let Ok(func) = v8::Local::<v8::Function>::try_from(func) else {
- return napi_function_expected;
+ check_arg!(env, value);
+ check_arg!(env, result);
+
+ let Some(coerced) = value.unwrap().to_string(&mut env.scope()) else {
+ return napi_string_expected;
};
- let argv: &[v8::Local<v8::Value>] =
- transmute(std::slice::from_raw_parts(argv, argc));
- let ret = func.call(&mut env.scope(), recv, argv);
- if !result.is_null() {
- *result = transmute::<Option<v8::Local<v8::Value>>, napi_value>(ret);
+ unsafe {
+ *result = coerced.into();
}
napi_ok
}
-#[napi_sym::napi_sym]
-fn napi_close_escapable_handle_scope(
- _env: *mut Env,
- _scope: napi_escapable_handle_scope,
+#[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 {
- // TODO: do this properly
- napi_ok
-}
+ check_arg!(env, js_object);
+ let env_ptr = env as *mut Env;
-#[napi_sym::napi_sym]
-fn napi_close_handle_scope(
- env: *mut Env,
- _scope: napi_handle_scope,
-) -> napi_status {
- let env = &mut *env;
- if env.open_handle_scopes == 0 {
- return napi_handle_scope_mismatch;
+ 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);
}
- // TODO: We are not opening a handle scope, therefore we cannot close it
- // TODO: this is also not implemented in napi_open_handle_scope
- // let _scope = &mut *(scope as *mut v8::HandleScope);
- env.open_handle_scopes -= 1;
+
+ 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
}
-#[napi_sym::napi_sym]
-fn napi_define_class(
- env_ptr: *mut Env,
- name: *const c_char,
- length: isize,
- constructor: napi_callback,
- callback_data: *mut c_void,
- property_count: usize,
- properties: *const napi_property_descriptor,
- result: *mut napi_value,
+fn unwrap(
+ env: &mut Env,
+ obj: napi_value,
+ result: *mut *mut c_void,
+ keep: bool,
) -> napi_status {
- check_env!(env_ptr);
- let env = unsafe { &mut *env_ptr };
- check_arg!(env, result);
- check_arg_option!(env, constructor);
-
- if property_count > 0 {
- check_arg!(env, properties);
+ check_arg!(env, obj);
+ if keep {
+ check_arg!(env, result);
}
- let name = if length == -1 {
- let Ok(s) = std::ffi::CStr::from_ptr(name).to_str() else {
- return napi_invalid_arg;
- };
- s
- } else {
- let slice = std::slice::from_raw_parts(name as *const u8, length as usize);
- std::str::from_utf8(slice).unwrap()
+ 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 tpl =
- create_function_template(env_ptr, Some(name), constructor, callback_data);
+ let Ok(external) = v8::Local::<v8::External>::try_from(val) else {
+ return napi_invalid_arg;
+ };
- let scope = &mut env.scope();
- let napi_properties: &[napi_property_descriptor] =
- std::slice::from_raw_parts(properties, property_count);
- let mut static_property_count = 0;
+ let reference = external.value() as *mut Reference;
+ let reference = unsafe { &mut *reference };
- for p in napi_properties {
- if p.attributes & napi_static != 0 {
- // Will be handled below
- static_property_count += 1;
- continue;
+ if !result.is_null() {
+ unsafe {
+ *result = reference.finalize_data;
}
+ }
- let name = if !p.utf8name.is_null() {
- let name_str = CStr::from_ptr(p.utf8name).to_str().unwrap();
- v8::String::new(scope, name_str).unwrap()
- } else {
- transmute::<napi_value, v8::Local<v8::String>>(p.name)
- };
+ if !keep {
+ assert!(obj
+ .delete_private(&mut env.scope(), napi_wrap)
+ .unwrap_or(false));
+ unsafe { Reference::remove(reference) };
+ }
- let method = p.method;
- let getter = p.getter;
- let setter = p.setter;
+ napi_ok
+}
- if getter.is_some() || setter.is_some() {
- let getter: Option<v8::Local<v8::FunctionTemplate>> = if getter.is_some()
- {
- Some(create_function_template(env_ptr, None, p.getter, p.data))
- } else {
- None
- };
- let setter: Option<v8::Local<v8::FunctionTemplate>> = if setter.is_some()
- {
- Some(create_function_template(env_ptr, None, p.setter, p.data))
- } else {
- None
- };
+#[napi_sym]
+fn napi_unwrap(
+ env: &mut Env,
+ obj: napi_value,
+ result: *mut *mut c_void,
+) -> napi_status {
+ unwrap(env, obj, result, true)
+}
- 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;
- }
+#[napi_sym]
+fn napi_remove_wrap(
+ env: &mut Env,
+ obj: napi_value,
+ result: *mut *mut c_void,
+) -> napi_status {
+ unwrap(env, obj, result, false)
+}
- let proto = tpl.prototype_template(scope);
- proto.set_accessor_property(
- name.into(),
- getter,
- setter,
- accessor_property,
- );
- } else if method.is_some() {
- let function = create_function_template(env_ptr, None, p.method, p.data);
- let proto = tpl.prototype_template(scope);
- proto.set(name.into(), function.into());
- } else {
- let proto = tpl.prototype_template(scope);
- proto.set(
- name.into(),
- transmute::<napi_value, v8::Local<v8::Data>>(p.value),
- );
- }
- }
+struct ExternalWrapper {
+ data: *mut c_void,
+ type_tag: Option<napi_type_tag>,
+}
- let value: v8::Local<v8::Value> = tpl.get_function(scope).unwrap().into();
- *result = value.into();
+#[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);
- if static_property_count > 0 {
- let mut static_descriptors = Vec::with_capacity(static_property_count);
+ let wrapper = Box::new(ExternalWrapper {
+ data,
+ type_tag: None,
+ });
- for p in napi_properties {
- if p.attributes & napi_static != 0 {
- static_descriptors.push(*p);
- }
- }
+ let wrapper = Box::into_raw(wrapper);
+ let external = v8::External::new(&mut env.scope(), wrapper as _);
- status_call!(napi_define_properties(
+ if let Some(finalize_cb) = finalize_cb {
+ Reference::into_raw(Reference::new(
env_ptr,
- *result,
- static_descriptors.len(),
- static_descriptors.as_ptr(),
+ external.into(),
+ 0,
+ ReferenceOwnership::Runtime,
+ Some(finalize_cb),
+ data,
+ finalize_hint,
));
}
+ unsafe {
+ *result = external.into();
+ }
+
napi_ok
}
-#[napi_sym::napi_sym]
-fn napi_define_properties(
- env_ptr: *mut Env,
- obj: napi_value,
- property_count: usize,
- properties: *const napi_property_descriptor,
+#[napi_sym]
+fn napi_type_tag_object(
+ env: &mut Env,
+ object_or_external: napi_value,
+ type_tag: *const napi_type_tag,
) -> napi_status {
- check_env!(env_ptr);
- let env = unsafe { &mut *env_ptr };
- if property_count > 0 {
- check_arg!(env, properties);
+ 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 scope = &mut env.scope();
+ 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 Ok(object) = v8::Local::<v8::Object>::try_from(napi_value_unchecked(obj))
+ 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_object_expected;
+ return napi_generic_failure;
};
- let properties = std::slice::from_raw_parts(properties, property_count);
- for property in properties {
- let name = if !property.utf8name.is_null() {
- let name_str = CStr::from_ptr(property.utf8name).to_str().unwrap();
- let Some(name_v8_str) = v8::String::new(scope, name_str) else {
- return napi_generic_failure;
- };
- name_v8_str.into()
- } else {
- let property_value = napi_value_unchecked(property.name);
- let Ok(prop) = v8::Local::<v8::Name>::try_from(property_value) else {
- return napi_name_expected;
+ 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,
};
- prop
};
+ return napi_ok;
+ }
- if property.getter.is_some() || property.setter.is_some() {
- let local_getter: v8::Local<v8::Value> = if property.getter.is_some() {
- create_function(env_ptr, None, property.getter, property.data).into()
- } else {
- v8::undefined(scope).into()
- };
- let local_setter: v8::Local<v8::Value> = if property.setter.is_some() {
- create_function(env_ptr, None, property.setter, property.data).into()
- } else {
- v8::undefined(scope).into()
- };
+ let Some(object) = val.to_object(&mut env.scope()) else {
+ return napi_object_expected;
+ };
- let mut desc =
- v8::PropertyDescriptor::new_from_get_set(local_getter, local_setter);
- desc.set_enumerable(property.attributes & napi_enumerable != 0);
- desc.set_configurable(property.attributes & napi_configurable != 0);
+ let key = v8::Local::new(&mut env.scope(), &env.shared().type_tag);
- let define_maybe = object.define_property(scope, name, &desc);
- return_status_if_false!(
- env_ptr,
- define_maybe.is_some(),
- napi_invalid_arg
- );
- } else if property.method.is_some() {
- let value: v8::Local<v8::Value> = {
- let function =
- create_function(env_ptr, None, property.method, property.data);
- function.into()
+ 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
};
- return_status_if_false!(
- env_ptr,
- object.set(scope, name.into(), value).is_some(),
- napi_invalid_arg
- );
- } else {
- let value = napi_value_unchecked(property.value);
- return_status_if_false!(
- env_ptr,
- object.set(scope, name.into(), value).is_some(),
- napi_invalid_arg
- );
+ unsafe {
+ *result = pass;
+ }
}
}
napi_ok
}
-#[napi_sym::napi_sym]
-fn napi_delete_element(
+#[napi_sym]
+fn napi_get_value_external(
env: *mut Env,
value: napi_value,
- index: u32,
- result: *mut bool,
+ result: *mut *mut c_void,
) -> napi_status {
- check_env!(env);
- let env = unsafe { &mut *env };
- let value = napi_value_unchecked(value);
- let obj = value.to_object(&mut env.scope()).unwrap();
- *result = obj.delete_index(&mut env.scope(), index).unwrap_or(false);
- napi_ok
+ 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::napi_sym]
-fn napi_delete_property(
+#[napi_sym]
+fn napi_create_reference(
env: *mut Env,
- object: napi_value,
- key: napi_value,
- result: *mut bool,
+ value: napi_value,
+ initial_refcount: u32,
+ result: *mut napi_ref,
) -> napi_status {
- check_env!(env);
- let env = unsafe { &mut *env };
- check_arg_option!(env, key);
+ let env = check_env!(env);
+ check_arg!(env, value);
check_arg!(env, result);
- let scope = &mut env.scope();
- let Some(object) = object.map(|o| o.to_object(scope)).flatten() else {
- return napi_invalid_arg;
- };
+ let value = value.unwrap();
- let Some(deleted) = object.delete(scope, key.unwrap_unchecked()) else {
- return napi_generic_failure;
- };
+ let reference = Reference::new(
+ env,
+ value,
+ initial_refcount,
+ ReferenceOwnership::Userland,
+ None,
+ std::ptr::null_mut(),
+ std::ptr::null_mut(),
+ );
- *result = deleted;
- napi_ok
+ let ptr = Reference::into_raw(reference);
+
+ unsafe {
+ *result = ptr as _;
+ }
+
+ napi_clear_last_error(env)
}
-// TODO: properly implement ref counting stuff
-#[napi_sym::napi_sym]
-fn napi_delete_reference(_env: *mut Env, _nref: napi_ref) -> napi_status {
- napi_ok
+#[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::napi_sym]
-fn napi_detach_arraybuffer(env: *mut Env, value: napi_value) -> napi_status {
- check_env!(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 value = napi_value_unchecked(value);
- let Ok(ab) = v8::Local::<v8::ArrayBuffer>::try_from(value) else {
- return napi_arraybuffer_expected;
- };
+ let reference = unsafe { &mut *(ref_ as *mut Reference) };
- if !ab.is_detachable() {
- return napi_detachable_arraybuffer_expected;
- }
+ let count = reference.ref_();
- // Expected to crash for None.
- ab.detach(None).unwrap();
+ if !result.is_null() {
+ unsafe {
+ *result = count;
+ }
+ }
- napi_clear_last_error(env);
- napi_ok
+ napi_clear_last_error(env)
}
-#[napi_sym::napi_sym]
-fn napi_escape_handle<'s>(
- _env: *mut Env,
- _handle_scope: napi_escapable_handle_scope,
- escapee: napi_value<'s>,
- result: *mut napi_value<'s>,
+#[napi_sym]
+fn napi_reference_unref(
+ env: *mut Env,
+ ref_: napi_ref,
+ result: *mut u32,
) -> napi_status {
- // TODO
- *result = escapee;
- napi_ok
-}
+ let env = check_env!(env);
+ check_arg!(env, ref_);
-#[napi_sym::napi_sym]
-fn napi_get_all_property_names(_env: *mut Env) -> napi_status {
- // TODO
- napi_ok
+ 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::napi_sym]
-fn napi_get_and_clear_last_exception(
- env: *mut Env,
+#[napi_sym]
+fn napi_get_reference_value(
+ env_ptr: *mut Env,
+ ref_: napi_ref,
result: *mut napi_value,
) -> napi_status {
- check_env!(env);
- let env = unsafe { &mut *env };
- // TODO: just return undefined for now we don't cache
- // exceptions in env.
- let value: v8::Local<v8::Value> = v8::undefined(&mut env.scope()).into();
- *result = value.into();
- napi_ok
+ 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::napi_sym]
-fn napi_get_array_length(
- _env: *mut Env,
- value: napi_value,
- result: *mut u32,
+#[napi_sym]
+fn napi_open_handle_scope(
+ env: *mut Env,
+ _result: *mut napi_handle_scope,
) -> napi_status {
- let value = napi_value_unchecked(value);
- *result = v8::Local::<v8::Array>::try_from(value).unwrap().length();
- napi_ok
+ let env = check_env!(env);
+ napi_clear_last_error(env)
}
-#[napi_sym::napi_sym]
-fn napi_get_arraybuffer_info(
- _env: *mut Env,
- value: napi_value,
- data: *mut *mut u8,
- length: *mut usize,
+#[napi_sym]
+fn napi_close_handle_scope(
+ env: *mut Env,
+ _scope: napi_handle_scope,
) -> napi_status {
- let value = napi_value_unchecked(value);
- let buf = v8::Local::<v8::ArrayBuffer>::try_from(value).unwrap();
- if !data.is_null() {
- *data = get_array_buffer_ptr(buf);
- }
- if !length.is_null() {
- *length = buf.byte_length();
- }
- napi_ok
+ let env = check_env!(env);
+ napi_clear_last_error(env)
}
-#[napi_sym::napi_sym]
-fn napi_get_boolean(
+#[napi_sym]
+fn napi_open_escapable_handle_scope(
env: *mut Env,
- value: bool,
- result: *mut napi_value,
+ _result: *mut napi_escapable_handle_scope,
) -> napi_status {
- check_env!(env);
- check_arg!(env, result);
- let env = unsafe { &mut *env };
- *result = v8::Boolean::new(env.isolate(), value).into();
- napi_ok
+ let env = check_env!(env);
+ napi_clear_last_error(env)
}
-#[napi_sym::napi_sym]
-fn napi_get_buffer_info(
+#[napi_sym]
+fn napi_close_escapable_handle_scope(
env: *mut Env,
- value: napi_value,
- data: *mut *mut u8,
- length: *mut usize,
+ _scope: napi_escapable_handle_scope,
) -> napi_status {
- check_env!(env);
- let env = unsafe { &mut *env };
- let value = napi_value_unchecked(value);
- let Ok(buf) = v8::Local::<v8::ArrayBufferView>::try_from(value) else {
- return napi_arraybuffer_expected;
- };
- let buffer_name = v8::String::new(&mut env.scope(), "buffer").unwrap();
- let Ok(abuf) = v8::Local::<v8::ArrayBuffer>::try_from(
- buf.get(&mut env.scope(), buffer_name.into()).unwrap(),
- ) else {
- return napi_arraybuffer_expected;
- };
- if !data.is_null() {
- *data = get_array_buffer_ptr(abuf);
- }
- if !length.is_null() {
- *length = abuf.byte_length();
- }
- napi_ok
+ let env = check_env!(env);
+ napi_clear_last_error(env)
}
-#[napi_sym::napi_sym]
-fn napi_get_cb_info(
+#[napi_sym]
+fn napi_escape_handle<'s>(
env: *mut Env,
- cbinfo: napi_callback_info,
- argc: *mut i32,
- argv: *mut napi_value,
- this_arg: *mut napi_value,
- data: *mut *mut c_void,
+ _scope: napi_escapable_handle_scope,
+ escapee: napi_value<'s>,
+ result: *mut napi_value<'s>,
) -> napi_status {
- check_env!(env);
- let env = unsafe { &mut *env };
- check_arg!(env, cbinfo);
+ let env = check_env!(env);
- let cbinfo: &CallbackInfo = &*(cbinfo as *const CallbackInfo);
- let args = &*(cbinfo.args as *const v8::FunctionCallbackArguments);
-
- if !argv.is_null() {
- check_arg!(env, argc);
- let mut v_argv = std::slice::from_raw_parts_mut(argv, argc as usize);
- for i in 0..*argc {
- let mut arg = args.get(i);
- v_argv[i as usize] = arg.into();
- }
+ unsafe {
+ *result = escapee;
}
- if !argc.is_null() {
- *argc = args.length();
- }
+ napi_clear_last_error(env)
+}
- if !this_arg.is_null() {
- let mut this = args.this();
- *this_arg = this.into();
+#[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);
- if !data.is_null() {
- *data = cbinfo.cb_info;
+ 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_clear_last_error(env);
napi_ok
}
-#[napi_sym::napi_sym]
-fn napi_get_dataview_info(
- env: *mut Env,
- value: napi_value,
- data: *mut *mut u8,
- length: *mut usize,
+#[napi_sym]
+fn napi_instanceof(
+ env: &mut Env,
+ object: napi_value,
+ constructor: napi_value,
+ result: *mut bool,
) -> napi_status {
- check_env!(env);
- let env = unsafe { &mut *env };
- let value = napi_value_unchecked(value);
- let Ok(buf) = v8::Local::<v8::DataView>::try_from(value) else {
- return napi_invalid_arg;
+ 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;
};
- let buffer_name = v8::String::new(&mut env.scope(), "buffer").unwrap();
- let Ok(abuf) = v8::Local::<v8::ArrayBuffer>::try_from(
- buf.get(&mut env.scope(), buffer_name.into()).unwrap(),
- ) else {
- return napi_invalid_arg;
+
+ if !ctor.is_function() {
+ unsafe {
+ napi_throw_type_error(
+ env,
+ "ERR_NAPI_CONS_FUNCTION\0".as_ptr() as _,
+ "Constructor must be a function\0".as_ptr() as _,
+ );
+ }
+ return napi_function_expected;
+ }
+
+ let Some(res) = object.unwrap().instance_of(&mut env.scope(), ctor) else {
+ return napi_generic_failure;
};
- if !data.is_null() {
- *data = get_array_buffer_ptr(abuf);
+
+ unsafe {
+ *result = res;
}
- *length = abuf.byte_length();
+
napi_ok
}
-#[napi_sym::napi_sym]
-fn napi_get_date_value(
- env: *mut Env,
- value: napi_value,
- result: *mut f64,
+#[napi_sym]
+fn napi_is_exception_pending(
+ env_ptr: *mut Env,
+ result: *mut bool,
) -> napi_status {
- check_env!(env);
- let value = napi_value_unchecked(value);
- return_status_if_false!(env, value.is_date(), napi_date_expected);
- let env = unsafe { &mut *env };
- let Ok(date) = v8::Local::<v8::Date>::try_from(value) else {
- return napi_date_expected;
- };
- // TODO: should be value of
- *result = date.number_value(&mut env.scope()).unwrap();
- napi_ok
+ 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::napi_sym]
-fn napi_get_element(
- env: *mut Env,
- object: napi_value,
- index: u32,
+#[napi_sym]
+fn napi_get_and_clear_last_exception(
+ env_ptr: *mut Env,
result: *mut napi_value,
) -> napi_status {
- check_env!(env);
- let env = unsafe { &mut *env };
- let object = napi_value_unchecked(object);
- let Ok(object) = v8::Local::<v8::Object>::try_from(object) else {
- return napi_invalid_arg;
- };
- let value: v8::Local<v8::Value> =
- object.get_index(&mut env.scope(), index).unwrap();
- *result = value.into();
- napi_ok
-}
-
-#[napi_sym::napi_sym]
-fn napi_get_global(env: *mut Env, result: *mut napi_value) -> napi_status {
- check_env!(env);
+ let env = check_env!(env_ptr);
check_arg!(env, result);
- let value: v8::Local<v8::Value> =
- transmute::<NonNull<v8::Value>, v8::Local<v8::Value>>((*env).global);
- *result = value.into();
- napi_clear_last_error(env);
- napi_ok
+ 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::napi_sym]
-fn napi_get_instance_data(
+#[napi_sym]
+fn napi_is_arraybuffer(
env: *mut Env,
- result: *mut *mut c_void,
+ value: napi_value,
+ result: *mut bool,
) -> napi_status {
- let env = &mut *env;
- let shared = env.shared();
- *result = shared.instance_data;
- napi_ok
-}
+ let env = check_env!(env);
+ check_arg!(env, value);
+ check_arg!(env, result);
-// TODO(bartlomieju): this function is broken
-#[napi_sym::napi_sym]
-fn napi_get_last_error_info(
- _env: *mut Env,
- error_code: *mut *const napi_extended_error_info,
-) -> napi_status {
- let err_info = Box::new(napi_extended_error_info {
- error_message: std::ptr::null(),
- engine_reserved: std::ptr::null_mut(),
- engine_error_code: 0,
- error_code: napi_ok,
- });
+ unsafe {
+ *result = value.unwrap().is_array_buffer();
+ }
- *error_code = Box::into_raw(err_info);
- napi_ok
+ napi_clear_last_error(env)
}
-#[napi_sym::napi_sym]
-fn napi_get_named_property(
- env: *mut Env,
- object: napi_value,
- utf8_name: *const c_char,
- result: *mut napi_value,
+#[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_env!(env);
- let env = unsafe { &mut *env };
- let object = napi_value_unchecked(object);
- let utf8_name = std::ffi::CStr::from_ptr(utf8_name);
- let name =
- v8::String::new(&mut env.scope(), &utf8_name.to_string_lossy()).unwrap();
- let value: v8::Local<v8::Value> = object
- .to_object(&mut env.scope())
- .unwrap()
- .get(&mut env.scope(), name.into())
- .unwrap();
- *result = value.into();
- napi_ok
-}
+ check_arg!(env, result);
-#[napi_sym::napi_sym]
-fn napi_get_new_target(
- _env: &mut Env,
- cbinfo: &CallbackInfo,
- result: &mut v8::Local<v8::Value>,
-) -> napi_status {
- let info = &*(cbinfo.args as *const v8::FunctionCallbackArguments);
- *result = info.new_target();
- napi_ok
-}
+ let buffer = v8::ArrayBuffer::new(&mut env.scope(), len);
+
+ if !data.is_null() {
+ unsafe {
+ *data = get_array_buffer_ptr(buffer) as _;
+ }
+ }
+
+ unsafe {
+ *result = buffer.into();
+ }
-#[napi_sym::napi_sym]
-fn napi_get_null(env: *mut Env, result: *mut napi_value) -> napi_status {
- check_env!(env);
- check_arg!(env, result);
- let env = unsafe { &mut *env };
- *result = v8::null(env.isolate()).into();
napi_ok
}
-#[napi_sym::napi_sym]
-fn napi_get_property(
- env: *mut Env,
- object: napi_value,
- key: napi_value,
- result: *mut napi_value,
+#[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_env!(env);
- let env = unsafe { &mut *env };
- let object = transmute::<napi_value, v8::Local<v8::Object>>(object);
- let key = napi_value_unchecked(key);
- let value: v8::Local<v8::Value> = object.get(&mut env.scope(), key).unwrap();
- *result = value.into();
+ 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::napi_sym]
-fn napi_get_property_names(
+#[napi_sym]
+fn napi_get_arraybuffer_info(
env: *mut Env,
- object: napi_value,
- result: *mut napi_value,
+ value: napi_value,
+ data: *mut *mut u8,
+ length: *mut usize,
) -> napi_status {
- check_env!(env);
- let env = unsafe { &mut *env };
- let object = napi_value_unchecked(object);
- let array: v8::Local<v8::Array> = object
- .to_object(&mut env.scope())
- .unwrap()
- .get_property_names(&mut env.scope(), Default::default())
- .unwrap();
- let value: v8::Local<v8::Value> = array.into();
- *result = value.into();
+ 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::napi_sym]
-fn napi_get_prototype(
+#[napi_sym]
+fn napi_is_typedarray(
env: *mut Env,
value: napi_value,
- result: *mut napi_value,
+ result: *mut bool,
) -> napi_status {
- check_env!(env);
- let env = unsafe { &mut *env };
- let value = napi_value_unchecked(value);
- let obj = value.to_object(&mut env.scope()).unwrap();
- let proto = obj.get_prototype(&mut env.scope()).unwrap();
- *result = proto.into();
+ let env = check_env!(env);
+ check_arg!(env, value);
+ check_arg!(env, result);
+
+ unsafe {
+ *result = value.unwrap().is_typed_array();
+ }
+
napi_ok
}
-#[napi_sym::napi_sym]
-fn napi_get_reference_value(
- env: *mut Env,
- reference: napi_ref,
- result: *mut napi_value,
+#[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_env!(env);
- let value = transmute::<napi_ref, v8::Local<v8::Value>>(reference);
- *result = value.into();
+ 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::napi_sym]
+#[napi_sym]
fn napi_get_typedarray_info(
- env: *mut Env,
+ env_ptr: *mut Env,
typedarray: napi_value,
type_: *mut napi_typedarray_type,
length: *mut usize,
@@ -2018,364 +3048,278 @@ fn napi_get_typedarray_info(
arraybuffer: *mut napi_value,
byte_offset: *mut usize,
) -> napi_status {
- check_env!(env);
- let env = unsafe { &mut *env };
- let value = napi_value_unchecked(typedarray);
- let Ok(array) = v8::Local::<v8::TypedArray>::try_from(value) else {
- return napi_invalid_arg;
+ 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() {
- if value.is_int8_array() {
- *type_ = napi_int8_array;
- } else if value.is_uint8_array() {
- *type_ = napi_uint8_array;
- } else if value.is_uint8_clamped_array() {
- *type_ = napi_uint8_clamped_array;
- } else if value.is_int16_array() {
- *type_ = napi_int16_array;
- } else if value.is_uint16_array() {
- *type_ = napi_uint16_array;
- } else if value.is_int32_array() {
- *type_ = napi_int32_array;
- } else if value.is_uint32_array() {
- *type_ = napi_uint32_array;
- } else if value.is_float32_array() {
- *type_ = napi_float32_array;
- } else if value.is_float64_array() {
- *type_ = napi_float64_array;
+ 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() {
- *length = array.length();
+ unsafe {
+ *length = array.length();
+ }
}
- if !data.is_null() || !arraybuffer.is_null() {
- let buf = array.buffer(&mut env.scope()).unwrap();
- if !data.is_null() {
- *data = get_array_buffer_ptr(buf) as *mut c_void;
+ if !data.is_null() {
+ unsafe {
+ *data = array.data();
}
- if !arraybuffer.is_null() {
+ }
+
+ if !arraybuffer.is_null() {
+ let buf = array.buffer(&mut env.scope()).unwrap();
+ unsafe {
*arraybuffer = buf.into();
}
}
if !byte_offset.is_null() {
- *byte_offset = array.byte_offset();
+ unsafe {
+ *byte_offset = array.byte_offset();
+ }
}
- napi_ok
-}
-
-#[napi_sym::napi_sym]
-fn napi_get_undefined(env: *mut Env, result: *mut napi_value) -> napi_status {
- check_env!(env);
- check_arg!(env, result);
- let env = unsafe { &mut *env };
- *result = v8::undefined(env.isolate()).into();
- napi_ok
-}
-
-pub const NAPI_VERSION: u32 = 8;
-
-#[napi_sym::napi_sym]
-fn napi_get_version(_: napi_env, version: *mut u32) -> napi_status {
- *version = NAPI_VERSION;
- napi_ok
-}
-
-#[napi_sym::napi_sym]
-fn napi_has_element(
- env: *mut Env,
- value: napi_value,
- index: u32,
- result: *mut bool,
-) -> napi_status {
- check_env!(env);
- let env = unsafe { &mut *env };
- let value = napi_value_unchecked(value);
- let obj = value.to_object(&mut env.scope()).unwrap();
- *result = obj.has_index(&mut env.scope(), index).unwrap_or(false);
- napi_ok
-}
-
-#[napi_sym::napi_sym]
-fn napi_has_named_property(
- env: *mut Env,
- value: napi_value,
- key: *const c_char,
- result: *mut bool,
-) -> napi_status {
- check_env!(env);
- let env = unsafe { &mut *env };
- let value = napi_value_unchecked(value);
- let obj = value.to_object(&mut env.scope()).unwrap();
- let key = CStr::from_ptr(key).to_str().unwrap();
- let key = v8::String::new(&mut env.scope(), key).unwrap();
- *result = obj.has(&mut env.scope(), key.into()).unwrap_or(false);
- napi_ok
+ napi_clear_last_error(env_ptr)
}
-#[napi_sym::napi_sym]
-fn napi_has_own_property(
- env: *mut Env,
- object: napi_value,
- key: napi_value,
- result: *mut bool,
+#[napi_sym]
+fn napi_create_dataview<'s>(
+ env: &'s mut Env,
+ byte_length: usize,
+ arraybuffer: napi_value,
+ byte_offset: usize,
+ result: *mut napi_value<'s>,
) -> napi_status {
- check_env!(env);
- let env = unsafe { &mut *env };
- check_arg_option!(env, key);
+ check_arg!(env, arraybuffer);
check_arg!(env, result);
- let scope = &mut env.scope();
- let Some(object) = object.map(|o| o.to_object(scope)).flatten() else {
+ let Some(buffer) =
+ arraybuffer.and_then(|v| v8::Local::<v8::ArrayBuffer>::try_from(v).ok())
+ else {
return napi_invalid_arg;
};
- if key.is_none() {
- return napi_invalid_arg;
+ if byte_length + byte_offset > buffer.byte_length() {
+ unsafe {
+ return napi_throw_range_error(
+ env,
+ "ERR_NAPI_INVALID_DATAVIEW_ARGS\0".as_ptr() as _,
+ "byte_offset + byte_length should be less than or equal to the size in bytes of the array passed in\0".as_ptr() as _,
+ );
+ }
}
- 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;
+ // let dataview = v8::DataView::new(&mut env, buffer, byte_offset, byte_length);
+ let dataview = {
+ let context = &mut env.scope().get_current_context();
+ let global = context.global(&mut env.scope());
+ let data_view_name = v8::String::new(&mut env.scope(), "DataView").unwrap();
+ let data_view =
+ global.get(&mut env.scope(), data_view_name.into()).unwrap();
+ let Ok(data_view) = v8::Local::<v8::Function>::try_from(data_view) else {
+ return napi_function_expected;
+ };
+ let byte_offset = v8::Number::new(&mut env.scope(), byte_offset as f64);
+ let byte_length = v8::Number::new(&mut env.scope(), byte_length as f64);
+ let Some(dv) = data_view.new_instance(
+ &mut env.scope(),
+ &[buffer.into(), byte_offset.into(), byte_length.into()],
+ ) else {
+ return napi_generic_failure;
+ };
+ dv
};
- *result = has_own;
+ unsafe {
+ *result = dataview.into();
+ }
napi_ok
}
-#[napi_sym::napi_sym]
-fn napi_has_property(
+#[napi_sym]
+fn napi_is_dataview(
env: *mut Env,
- object: napi_value,
- key: napi_value,
+ value: napi_value,
result: *mut bool,
) -> napi_status {
- check_env!(env);
- let env = unsafe { &mut *env };
- check_arg_option!(env, key);
+ let env = check_env!(env);
+ check_arg!(env, value);
check_arg!(env, result);
- let scope = &mut env.scope();
- let Some(object) = object.map(|o| o.to_object(scope)).flatten() else {
- return napi_invalid_arg;
- };
+ unsafe {
+ *result = value.unwrap().is_data_view();
+ }
- let Some(has) = object.has(scope, key.unwrap_unchecked()) else {
- return napi_generic_failure;
- };
- *result = has;
- napi_ok
+ napi_clear_last_error(env)
}
-#[napi_sym::napi_sym]
-fn napi_instanceof(
- env: *mut Env,
- value: napi_value,
- constructor: napi_value,
- result: *mut bool,
+#[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 {
- check_env!(env);
- let env = unsafe { &mut *env };
- check_arg_option!(env, constructor);
- check_arg_option!(env, value);
+ let env = check_env!(env_ptr);
+ check_arg!(env, dataview);
- let value = napi_value_unchecked(value);
- let constructor = napi_value_unchecked(constructor);
- let Some(ctor) = constructor.to_object(&mut env.scope()) else {
- return napi_object_expected;
- };
- if !ctor.is_function() {
- return napi_function_expected;
- }
- let Some(res) = value.instance_of(&mut env.scope(), ctor) else {
- return napi_generic_failure;
+ let Some(array) =
+ dataview.and_then(|v| v8::Local::<v8::DataView>::try_from(v).ok())
+ else {
+ return napi_invalid_arg;
};
- *result = res;
- napi_ok
-}
+ if !byte_length.is_null() {
+ unsafe {
+ *byte_length = array.byte_length();
+ }
+ }
-#[napi_sym::napi_sym]
-fn napi_is_array(
- _env: *mut Env,
- value: napi_value,
- result: *mut bool,
-) -> napi_status {
- let value = napi_value_unchecked(value);
- *result = value.is_array();
- napi_ok
-}
+ if !arraybuffer.is_null() {
+ let Some(buffer) = array.buffer(&mut env.scope()) else {
+ return napi_generic_failure;
+ };
-#[napi_sym::napi_sym]
-fn napi_is_arraybuffer(
- _env: *mut Env,
- value: napi_value,
- result: *mut bool,
-) -> napi_status {
- let value = napi_value_unchecked(value);
- *result = value.is_array_buffer();
- napi_ok
-}
+ unsafe {
+ *arraybuffer = buffer.into();
+ }
+ }
-#[napi_sym::napi_sym]
-fn napi_is_buffer(
- _env: *mut Env,
- value: napi_value,
- result: *mut bool,
-) -> napi_status {
- let value = napi_value_unchecked(value);
- // TODO: should we assume Buffer as Uint8Array in Deno?
- // or use std/node polyfill?
- *result = value.is_typed_array();
- napi_ok
-}
+ if !data.is_null() {
+ unsafe {
+ *data = array.data();
+ }
+ }
-#[napi_sym::napi_sym]
-fn napi_is_dataview(
- _env: *mut Env,
- value: napi_value,
- result: *mut bool,
-) -> napi_status {
- let value = napi_value_unchecked(value);
- *result = value.is_data_view();
- napi_ok
-}
+ if !byte_offset.is_null() {
+ unsafe {
+ *byte_offset = array.byte_offset();
+ }
+ }
-#[napi_sym::napi_sym]
-fn napi_is_date(
- _env: *mut Env,
- value: napi_value,
- result: *mut bool,
-) -> napi_status {
- let value = napi_value_unchecked(value);
- *result = value.is_date();
- napi_ok
+ napi_clear_last_error(env_ptr)
}
-#[napi_sym::napi_sym]
-fn napi_is_detached_arraybuffer(
- env: *mut Env,
- value: napi_value,
- result: *mut bool,
-) -> napi_status {
- check_env!(env);
+#[napi_sym]
+fn napi_get_version(env: *mut Env, result: *mut u32) -> napi_status {
+ let env = check_env!(env);
check_arg!(env, result);
- let value = napi_value_unchecked(value);
-
- *result = match v8::Local::<v8::ArrayBuffer>::try_from(value) {
- Ok(array_buffer) => array_buffer.was_detached(),
- Err(_) => false,
- };
-
- napi_clear_last_error(env);
+ unsafe {
+ *result = NAPI_VERSION;
+ }
- napi_ok
+ napi_clear_last_error(env)
}
-#[napi_sym::napi_sym]
-fn napi_is_error(
- env: *mut Env,
- value: napi_value,
- result: *mut bool,
+#[napi_sym]
+fn napi_create_promise<'s>(
+ env: &'s mut Env,
+ deferred: *mut napi_deferred,
+ promise: *mut napi_value<'s>,
) -> napi_status {
- {
- check_env!(env);
- if value.is_none() {
- return napi_invalid_arg;
- }
- check_arg!(env, result);
+ check_arg!(env, deferred);
+ check_arg!(env, promise);
- let value = napi_value_unchecked(value);
- *result = value.is_native_error();
- }
- napi_clear_last_error(env);
- napi_ok
-}
+ let resolver = v8::PromiseResolver::new(&mut env.scope()).unwrap();
-#[napi_sym::napi_sym]
-fn napi_is_exception_pending(_env: *mut Env, result: *mut bool) -> napi_status {
- // TODO
- *result = false;
- napi_ok
-}
+ let global = v8::Global::new(&mut env.scope(), resolver);
+ let global_ptr = global.into_raw().as_ptr() as napi_deferred;
-#[napi_sym::napi_sym]
-fn napi_is_promise(
- _env: *mut Env,
- value: napi_value,
- result: *mut bool,
-) -> napi_status {
- let value = napi_value_unchecked(value);
- *result = value.is_promise();
- napi_ok
-}
+ let p = resolver.get_promise(&mut env.scope());
-#[napi_sym::napi_sym]
-fn napi_is_typedarray(
- _env: *mut Env,
- value: napi_value,
- result: *mut bool,
-) -> napi_status {
- let value = napi_value_unchecked(value);
- *result = value.is_typed_array();
- napi_ok
-}
+ unsafe {
+ *deferred = global_ptr;
+ }
+
+ unsafe {
+ *promise = p.into();
+ }
-#[napi_sym::napi_sym]
-fn napi_new_instance(
- env: *mut Env,
- constructor: napi_value,
- argc: usize,
- argv: *const napi_value,
- result: *mut napi_value,
-) -> napi_status {
- check_env!(env);
- let env = unsafe { &mut *env };
- let constructor = napi_value_unchecked(constructor);
- let Ok(constructor) = v8::Local::<v8::Function>::try_from(constructor) else {
- return napi_function_expected;
- };
- let args: &[v8::Local<v8::Value>] =
- transmute(std::slice::from_raw_parts(argv, argc));
- let inst = constructor.new_instance(&mut env.scope(), args).unwrap();
- let value: v8::Local<v8::Value> = inst.into();
- *result = value.into();
napi_ok
}
-#[napi_sym::napi_sym]
-fn napi_object_freeze(
+#[napi_sym]
+fn napi_resolve_deferred(
env: &mut Env,
- object: v8::Local<v8::Value>,
+ deferred: napi_deferred,
+ result: napi_value,
) -> napi_status {
- let object = object.to_object(&mut env.scope()).unwrap();
- if object
- .set_integrity_level(&mut env.scope(), v8::IntegrityLevel::Frozen)
- .is_none()
+ check_arg!(env, result);
+ check_arg!(env, deferred);
+
+ let isolate = unsafe { &mut *env.isolate_ptr };
+ let deferred_ptr =
+ unsafe { NonNull::new_unchecked(deferred as *mut v8::PromiseResolver) };
+ let global = unsafe { v8::Global::from_raw(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::napi_sym]
-fn napi_object_seal(
+#[napi_sym]
+fn napi_reject_deferred(
env: &mut Env,
- object: v8::Local<v8::Value>,
+ deferred: napi_deferred,
+ result: napi_value,
) -> napi_status {
- let object = object.to_object(&mut env.scope()).unwrap();
- if object
- .set_integrity_level(&mut env.scope(), v8::IntegrityLevel::Sealed)
- .is_none()
+ check_arg!(env, result);
+ check_arg!(env, deferred);
+
+ let isolate = unsafe { &mut *env.isolate_ptr };
+ let deferred_ptr =
+ unsafe { NonNull::new_unchecked(deferred as *mut v8::PromiseResolver) };
+ let global = unsafe { v8::Global::from_raw(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;
}
@@ -2383,453 +3327,260 @@ fn napi_object_seal(
napi_ok
}
-#[napi_sym::napi_sym]
-fn napi_open_escapable_handle_scope(
- _env: *mut Env,
- _result: *mut napi_escapable_handle_scope,
+#[napi_sym]
+fn napi_is_promise(
+ env: *mut Env,
+ value: napi_value,
+ is_promise: *mut bool,
) -> napi_status {
- // TODO: do this properly
- napi_ok
+ 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::napi_sym]
-fn napi_open_handle_scope(
- env: *mut Env,
- _result: *mut napi_handle_scope,
+#[napi_sym]
+fn napi_create_date<'s>(
+ env: &'s mut Env,
+ time: f64,
+ result: *mut napi_value<'s>,
) -> napi_status {
- let env = &mut *env;
+ check_arg!(env, result);
- // TODO: this is also not implemented in napi_close_handle_scope
- // *result = &mut env.scope() as *mut _ as napi_handle_scope;
- env.open_handle_scopes += 1;
- napi_ok
-}
+ let Some(date) = v8::Date::new(&mut env.scope(), time) else {
+ return napi_generic_failure;
+ };
-#[napi_sym::napi_sym]
-fn napi_reference_ref() -> napi_status {
- // TODO
- napi_ok
-}
+ unsafe {
+ *result = date.into();
+ }
-#[napi_sym::napi_sym]
-fn napi_reference_unref() -> napi_status {
- // TODO
napi_ok
}
-#[napi_sym::napi_sym]
-fn napi_reject_deferred(
+#[napi_sym]
+fn napi_is_date(
env: *mut Env,
- deferred: napi_deferred,
- error: napi_value,
+ value: napi_value,
+ is_date: *mut bool,
) -> napi_status {
- check_env!(env);
- let env = unsafe { &mut *env };
+ let env = check_env!(env);
+ check_arg!(env, value);
+ check_arg!(env, is_date);
- let deferred_ptr =
- NonNull::new_unchecked(deferred as *mut v8::PromiseResolver);
- // TODO(@littledivy): Use Global::from_raw instead casting to local.
- // SAFETY: Isolate is still alive unless the module is doing something weird,
- // global data is the size of a pointer.
- // Global pointer is obtained from napi_create_promise
- let resolver = transmute::<
- NonNull<v8::PromiseResolver>,
- v8::Local<v8::PromiseResolver>,
- >(deferred_ptr);
- resolver
- .reject(&mut env.scope(), napi_value_unchecked(error))
- .unwrap();
- napi_ok
-}
+ unsafe {
+ *is_date = value.unwrap().is_date();
+ }
-#[napi_sym::napi_sym]
-fn napi_remove_wrap(env: *mut Env, value: napi_value) -> napi_status {
- check_env!(env);
- let env = unsafe { &mut *env };
- let value = napi_value_unchecked(value);
- let obj = value.to_object(&mut env.scope()).unwrap();
- let shared = &*(env.shared as *const EnvShared);
- let napi_wrap = v8::Local::new(&mut env.scope(), &shared.napi_wrap);
- obj.delete_private(&mut env.scope(), napi_wrap).unwrap();
- napi_ok
+ napi_clear_last_error(env)
}
-#[napi_sym::napi_sym]
-fn napi_resolve_deferred(
- env: *mut Env,
- deferred: napi_deferred,
- result: napi_value,
+#[napi_sym]
+fn napi_get_date_value(
+ env: &mut Env,
+ value: napi_value,
+ result: *mut f64,
) -> napi_status {
- check_env!(env);
- let env = unsafe { &mut *env };
- let deferred_ptr =
- NonNull::new_unchecked(deferred as *mut v8::PromiseResolver);
- // TODO(@littledivy): Use Global::from_raw instead casting to local.
- // SAFETY: Isolate is still alive unless the module is doing something weird,
- // global data is the size of a pointer.
- // Global pointer is obtained from napi_create_promise
- let resolver = transmute::<
- NonNull<v8::PromiseResolver>,
- v8::Local<v8::PromiseResolver>,
- >(deferred_ptr);
- resolver
- .resolve(&mut env.scope(), napi_value_unchecked(result))
- .unwrap();
+ 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::napi_sym]
-fn napi_run_script(
- env: *mut Env,
+#[napi_sym]
+fn napi_run_script<'s>(
+ env: &'s mut Env,
script: napi_value,
- result: *mut napi_value,
+ result: *mut napi_value<'s>,
) -> napi_status {
- check_env!(env);
- let env = unsafe { &mut *env };
+ check_arg!(env, script);
+ check_arg!(env, result);
- let script = napi_value_unchecked(script);
- if !script.is_string() {
- // TODO:
- // napi_set_last_error
+ let Some(script) =
+ script.and_then(|v| v8::Local::<v8::String>::try_from(v).ok())
+ else {
return napi_string_expected;
- }
- let script = script.to_string(&mut env.scope()).unwrap();
+ };
- let script = v8::Script::compile(&mut env.scope(), script, None);
- if script.is_none() {
+ let Some(script) = v8::Script::compile(&mut env.scope(), script, None) else {
return napi_generic_failure;
- }
- let script = script.unwrap();
- let rv = script.run(&mut env.scope());
+ };
- if let Some(rv) = rv {
- *result = rv.into();
- } else {
+ let Some(rv) = script.run(&mut env.scope()) else {
return napi_generic_failure;
+ };
+
+ unsafe {
+ *result = rv.into();
}
napi_ok
}
-#[napi_sym::napi_sym]
-fn napi_set_element(
- env: *mut Env,
- object: napi_value,
- index: u32,
+#[napi_sym]
+fn napi_add_finalizer(
+ env_ptr: *mut Env,
value: napi_value,
-) -> napi_status {
- check_env!(env);
- let env = unsafe { &mut *env };
- let object = napi_value_unchecked(object);
- let Ok(object) = v8::Local::<v8::Object>::try_from(object) else {
- return napi_invalid_arg;
- };
- let value = napi_value_unchecked(value);
- object.set_index(&mut env.scope(), index, value).unwrap();
- napi_ok
-}
-
-#[napi_sym::napi_sym]
-fn napi_set_instance_data(
- env: *mut Env,
- data: *mut c_void,
+ finalize_data: *mut c_void,
finalize_cb: Option<napi_finalize>,
finalize_hint: *mut c_void,
+ result: *mut napi_ref,
) -> napi_status {
- let env = &mut *env;
- let shared = env.shared_mut();
- shared.instance_data = data;
- shared.data_finalize = if finalize_cb.is_some() {
- finalize_cb
- } else {
- None
- };
- shared.data_finalize_hint = finalize_hint;
- napi_ok
-}
-
-#[napi_sym::napi_sym]
-fn napi_set_named_property(
- env: *mut Env,
- object: napi_value,
- name: *const c_char,
- value: napi_value,
-) -> napi_status {
- check_env!(env);
- let env = unsafe { &mut *env };
- let name = CStr::from_ptr(name).to_str().unwrap();
- let object = transmute::<napi_value, v8::Local<v8::Object>>(object);
- let value = napi_value_unchecked(value);
- let name = v8::String::new(&mut env.scope(), name).unwrap();
- object.set(&mut env.scope(), name.into(), value).unwrap();
- napi_ok
-}
-
-#[napi_sym::napi_sym]
-fn napi_set_property(
- env: *mut Env,
- object: napi_value,
- key: napi_value,
- value: napi_value,
-) -> napi_status {
- check_env!(env);
- let env = unsafe { &mut *env };
- check_arg_option!(env, key);
- check_arg_option!(env, value);
+ let env = check_env!(env_ptr);
+ check_arg!(env, value);
+ check_arg!(env, finalize_cb);
- let scope = &mut env.scope();
- let Some(object) = object.map(|o| o.to_object(scope)).flatten() else {
- return napi_invalid_arg;
+ 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);
};
- if object
- .set(scope, key.unwrap_unchecked(), value.unwrap_unchecked())
- .is_none()
- {
- return napi_generic_failure;
+ let ownership = if result.is_null() {
+ ReferenceOwnership::Runtime
+ } else {
+ ReferenceOwnership::Userland
};
- napi_ok
-}
-
-#[napi_sym::napi_sym]
-fn napi_strict_equals(
- env: *mut Env,
- lhs: napi_value,
- rhs: napi_value,
- result: *mut bool,
-) -> napi_status {
- check_env!(env);
- let env = unsafe { &mut *env };
- check_arg_option!(env, lhs);
- check_arg_option!(env, rhs);
+ let reference = Reference::new(
+ env,
+ value.into(),
+ 0,
+ ownership,
+ finalize_cb,
+ finalize_data,
+ finalize_hint,
+ );
- *result = lhs.unwrap_unchecked().strict_equals(rhs.unwrap_unchecked());
- napi_ok
-}
+ if !result.is_null() {
+ unsafe {
+ *result = Reference::into_raw(reference) as _;
+ }
+ }
-#[napi_sym::napi_sym]
-fn napi_throw(env: *mut Env, error: napi_value) -> napi_status {
- check_env!(env);
- let env = unsafe { &mut *env };
- let error = napi_value_unchecked(error);
- env.scope().throw_exception(error);
- napi_ok
+ napi_clear_last_error(env_ptr)
}
-#[napi_sym::napi_sym]
-fn napi_throw_error(
+#[napi_sym]
+fn node_api_post_finalizer(
env: *mut Env,
- code: *const c_char,
- msg: *const c_char,
+ _finalize_cb: napi_finalize,
+ _finalize_data: *mut c_void,
+ _finalize_hint: *mut c_void,
) -> napi_status {
- // TODO: add preamble here
-
- {
- check_env!(env);
- let str_ = match check_new_from_utf8(env, msg) {
- Ok(s) => s,
- Err(status) => return status,
- };
-
- let error = {
- let env = unsafe { &mut *env };
- let scope = &mut env.scope();
- v8::Exception::error(scope, str_)
- };
- status_call!(set_error_code(
- env,
- error,
- transmute::<*mut (), napi_value>(std::ptr::null_mut()),
- code,
- ));
-
- unsafe { &mut *env }.scope().throw_exception(error);
- }
- napi_clear_last_error(env);
- napi_ok
+ napi_clear_last_error(env)
}
-#[napi_sym::napi_sym]
-fn napi_throw_range_error(
+#[napi_sym]
+fn napi_adjust_external_memory(
env: *mut Env,
- code: *const c_char,
- msg: *const c_char,
+ change_in_bytes: i64,
+ adjusted_value: *mut i64,
) -> napi_status {
- // TODO: add preamble here
+ let env = check_env!(env);
+ check_arg!(env, adjusted_value);
- {
- check_env!(env);
- let str_ = match check_new_from_utf8(env, msg) {
- Ok(s) => s,
- Err(status) => return status,
- };
- let error = {
- let env = unsafe { &mut *env };
- let scope = &mut env.scope();
- v8::Exception::range_error(scope, str_)
- };
- status_call!(set_error_code(
- env,
- error,
- transmute::<*mut (), napi_value>(std::ptr::null_mut()),
- code,
- ));
- unsafe { &mut *env }.scope().throw_exception(error);
+ let isolate = unsafe { &mut *env.isolate_ptr };
+
+ unsafe {
+ *adjusted_value =
+ isolate.adjust_amount_of_external_allocated_memory(change_in_bytes);
}
- napi_clear_last_error(env);
- napi_ok
+
+ napi_clear_last_error(env)
}
-#[napi_sym::napi_sym]
-fn napi_throw_type_error(
+#[napi_sym]
+fn napi_set_instance_data(
env: *mut Env,
- code: *const c_char,
- msg: *const c_char,
+ data: *mut c_void,
+ finalize_cb: Option<napi_finalize>,
+ finalize_hint: *mut c_void,
) -> napi_status {
- // TODO: add preamble here
+ let env = check_env!(env);
- {
- check_env!(env);
- let str_ = match check_new_from_utf8(env, msg) {
- Ok(s) => s,
- Err(status) => return status,
- };
- let error = {
- let env = unsafe { &mut *env };
- let scope = &mut env.scope();
- v8::Exception::type_error(scope, str_)
- };
- status_call!(set_error_code(
- env,
- error,
- transmute::<*mut (), napi_value>(std::ptr::null_mut()),
- code,
- ));
- unsafe { &mut *env }.scope().throw_exception(error);
- }
- napi_clear_last_error(env);
- napi_ok
-}
+ env.shared_mut().instance_data = Some(InstanceData {
+ data,
+ finalize_cb,
+ finalize_hint,
+ });
-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_clear_last_error(env)
}
-#[napi_sym::napi_sym]
-fn napi_typeof(
+#[napi_sym]
+fn napi_get_instance_data(
env: *mut Env,
- value: napi_value,
- result: *mut napi_valuetype,
+ data: *mut *mut c_void,
) -> napi_status {
- check_env!(env);
- check_arg_option!(env, value);
- check_arg!(env, result);
+ let env = check_env!(env);
+ check_arg!(env, data);
- let Some(ty) = get_value_type(value.unwrap()) else {
- return napi_invalid_arg;
+ let instance_data = match &env.shared().instance_data {
+ Some(v) => v.data,
+ None => std::ptr::null_mut(),
};
- *result = ty;
- napi_ok
-}
-#[napi_sym::napi_sym]
-fn napi_unwrap(
- env: *mut Env,
- value: napi_value,
- result: *mut *mut c_void,
-) -> napi_status {
- check_env!(env);
- let env = unsafe { &mut *env };
- let value = napi_value_unchecked(value);
- let obj = value.to_object(&mut env.scope()).unwrap();
- let shared = &*(env.shared as *const EnvShared);
- let napi_wrap = v8::Local::new(&mut env.scope(), &shared.napi_wrap);
- let ext = obj.get_private(&mut env.scope(), napi_wrap).unwrap();
- let Some(ext) = v8::Local::<v8::External>::try_from(ext).ok() else {
- return napi_invalid_arg;
- };
- *result = ext.value();
- napi_ok
-}
+ unsafe { *data = instance_data };
-#[napi_sym::napi_sym]
-fn napi_wrap(
- env: *mut Env,
- value: napi_value,
- native_object: *mut c_void,
-) -> napi_status {
- check_env!(env);
- let env = unsafe { &mut *env };
- let value = napi_value_unchecked(value);
- let obj = value.to_object(&mut env.scope()).unwrap();
- let shared = &*(env.shared as *const EnvShared);
- let napi_wrap = v8::Local::new(&mut env.scope(), &shared.napi_wrap);
- let ext = v8::External::new(&mut env.scope(), native_object);
- obj.set_private(&mut env.scope(), napi_wrap, ext.into());
- napi_ok
+ napi_clear_last_error(env)
}
-#[napi_sym::napi_sym]
-fn node_api_throw_syntax_error(
- env: *mut Env,
- _code: *const c_char,
- msg: *const c_char,
-) -> napi_status {
- check_env!(env);
- let env = unsafe { &mut *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 code = CStr::from_ptr(code).to_str().unwrap();
- let msg = CStr::from_ptr(msg).to_str().unwrap();
+ 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);
+ };
- // let code = v8::String::new(&mut env.scope(), code).unwrap();
- let msg = v8::String::new(&mut env.scope(), msg).unwrap();
+ if !ab.is_detachable() {
+ return napi_set_last_error(env, napi_detachable_arraybuffer_expected);
+ }
- let error = v8::Exception::syntax_error(&mut env.scope(), msg);
- env.scope().throw_exception(error);
+ // Expected to crash for None.
+ ab.detach(None).unwrap();
+ napi_clear_last_error(env);
napi_ok
}
-#[napi_sym::napi_sym]
-fn node_api_create_syntax_error(
- env: *mut Env,
- _code: napi_value,
- msg: napi_value,
- result: *mut napi_value,
+#[napi_sym]
+fn napi_is_detached_arraybuffer(
+ env_ptr: *mut Env,
+ arraybuffer: napi_value,
+ result: *mut bool,
) -> napi_status {
- check_env!(env);
- let env = unsafe { &mut *env };
-
- // let code = napi_value_unchecked(code);
- let msg = napi_value_unchecked(msg);
+ let env = check_env!(env_ptr);
+ check_arg!(env, arraybuffer);
+ check_arg!(env, result);
- let msg = msg.to_string(&mut env.scope()).unwrap();
+ let is_detached = match arraybuffer
+ .and_then(|v| v8::Local::<v8::ArrayBuffer>::try_from(v).ok())
+ {
+ Some(ab) => ab.was_detached(),
+ None => false,
+ };
- let error = v8::Exception::syntax_error(&mut env.scope(), msg);
- *result = error.into();
+ unsafe {
+ *result = is_detached;
+ }
- napi_ok
+ napi_clear_last_error(env)
}