mirror of
https://github.com/denoland/deno.git
synced 2024-11-27 16:10:57 -05:00
79a3ad2b95
exposes node-api symbols in denort so that `deno compile` can run native addons.
3616 lines
74 KiB
Rust
3616 lines
74 KiB
Rust
// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license.
|
|
|
|
#![allow(non_upper_case_globals)]
|
|
#![deny(unsafe_op_in_unsafe_fn)]
|
|
|
|
const NAPI_VERSION: u32 = 9;
|
|
|
|
use crate::*;
|
|
use libc::INT_MAX;
|
|
|
|
use super::util::check_new_from_utf8;
|
|
use super::util::check_new_from_utf8_len;
|
|
use super::util::get_array_buffer_ptr;
|
|
use super::util::make_external_backing_store;
|
|
use super::util::napi_clear_last_error;
|
|
use super::util::napi_set_last_error;
|
|
use super::util::v8_name_from_property_descriptor;
|
|
use crate::check_arg;
|
|
use crate::check_env;
|
|
use crate::function::create_function;
|
|
use crate::function::create_function_template;
|
|
use crate::function::CallbackInfo;
|
|
use napi_sym::napi_sym;
|
|
use std::ptr::NonNull;
|
|
|
|
#[derive(Debug, Clone, Copy, PartialEq)]
|
|
enum ReferenceOwnership {
|
|
Runtime,
|
|
Userland,
|
|
}
|
|
|
|
enum ReferenceState {
|
|
Strong(v8::Global<v8::Value>),
|
|
Weak(v8::Weak<v8::Value>),
|
|
}
|
|
|
|
struct Reference {
|
|
env: *mut Env,
|
|
state: ReferenceState,
|
|
ref_count: u32,
|
|
ownership: ReferenceOwnership,
|
|
finalize_cb: Option<napi_finalize>,
|
|
finalize_data: *mut c_void,
|
|
finalize_hint: *mut c_void,
|
|
}
|
|
|
|
impl Reference {
|
|
fn new(
|
|
env: *mut Env,
|
|
value: v8::Local<v8::Value>,
|
|
initial_ref_count: u32,
|
|
ownership: ReferenceOwnership,
|
|
finalize_cb: Option<napi_finalize>,
|
|
finalize_data: *mut c_void,
|
|
finalize_hint: *mut c_void,
|
|
) -> Box<Self> {
|
|
let isolate = unsafe { (*env).isolate() };
|
|
|
|
let mut reference = Box::new(Reference {
|
|
env,
|
|
state: ReferenceState::Strong(v8::Global::new(isolate, value)),
|
|
ref_count: initial_ref_count,
|
|
ownership,
|
|
finalize_cb,
|
|
finalize_data,
|
|
finalize_hint,
|
|
});
|
|
|
|
if initial_ref_count == 0 {
|
|
reference.set_weak();
|
|
}
|
|
|
|
reference
|
|
}
|
|
|
|
fn ref_(&mut self) -> u32 {
|
|
self.ref_count += 1;
|
|
if self.ref_count == 1 {
|
|
self.set_strong();
|
|
}
|
|
self.ref_count
|
|
}
|
|
|
|
fn unref(&mut self) -> u32 {
|
|
let old_ref_count = self.ref_count;
|
|
if self.ref_count > 0 {
|
|
self.ref_count -= 1;
|
|
}
|
|
if old_ref_count == 1 && self.ref_count == 0 {
|
|
self.set_weak();
|
|
}
|
|
self.ref_count
|
|
}
|
|
|
|
fn reset(&mut self) {
|
|
self.finalize_cb = None;
|
|
self.finalize_data = std::ptr::null_mut();
|
|
self.finalize_hint = std::ptr::null_mut();
|
|
}
|
|
|
|
fn set_strong(&mut self) {
|
|
if let ReferenceState::Weak(w) = &self.state {
|
|
let isolate = unsafe { (*self.env).isolate() };
|
|
if let Some(g) = w.to_global(isolate) {
|
|
self.state = ReferenceState::Strong(g);
|
|
}
|
|
}
|
|
}
|
|
|
|
fn set_weak(&mut self) {
|
|
let reference = self as *mut Reference;
|
|
if let ReferenceState::Strong(g) = &self.state {
|
|
let cb = Box::new(move |_: &mut v8::Isolate| {
|
|
Reference::weak_callback(reference)
|
|
});
|
|
let isolate = unsafe { (*self.env).isolate() };
|
|
self.state =
|
|
ReferenceState::Weak(v8::Weak::with_finalizer(isolate, g, cb));
|
|
}
|
|
}
|
|
|
|
fn weak_callback(reference: *mut Reference) {
|
|
let reference = unsafe { &mut *reference };
|
|
|
|
let finalize_cb = reference.finalize_cb;
|
|
let finalize_data = reference.finalize_data;
|
|
let finalize_hint = reference.finalize_hint;
|
|
reference.reset();
|
|
|
|
// copy this value before the finalize callback, since
|
|
// it might free the reference (which would be a UAF)
|
|
let ownership = reference.ownership;
|
|
if let Some(finalize_cb) = finalize_cb {
|
|
unsafe {
|
|
finalize_cb(reference.env as _, finalize_data, finalize_hint);
|
|
}
|
|
}
|
|
|
|
if ownership == ReferenceOwnership::Runtime {
|
|
unsafe { drop(Reference::from_raw(reference)) }
|
|
}
|
|
}
|
|
|
|
fn into_raw(r: Box<Reference>) -> *mut Reference {
|
|
Box::into_raw(r)
|
|
}
|
|
|
|
unsafe fn from_raw(r: *mut Reference) -> Box<Reference> {
|
|
unsafe { Box::from_raw(r) }
|
|
}
|
|
|
|
unsafe fn remove(r: *mut Reference) {
|
|
let r = unsafe { &mut *r };
|
|
if r.ownership == ReferenceOwnership::Userland {
|
|
r.reset();
|
|
} else {
|
|
unsafe { drop(Reference::from_raw(r)) }
|
|
}
|
|
}
|
|
}
|
|
|
|
#[napi_sym]
|
|
fn napi_get_last_error_info(
|
|
env: *mut Env,
|
|
result: *mut *const napi_extended_error_info,
|
|
) -> napi_status {
|
|
let env = check_env!(env);
|
|
check_arg!(env, result);
|
|
|
|
if env.last_error.error_code == napi_ok {
|
|
napi_clear_last_error(env);
|
|
} else {
|
|
env.last_error.error_message =
|
|
ERROR_MESSAGES[env.last_error.error_code as usize].as_ptr();
|
|
}
|
|
|
|
unsafe {
|
|
*result = &env.last_error;
|
|
}
|
|
|
|
napi_ok
|
|
}
|
|
|
|
#[napi_sym]
|
|
fn napi_create_function<'s>(
|
|
env: &'s mut Env,
|
|
name: *const c_char,
|
|
length: usize,
|
|
cb: Option<napi_callback>,
|
|
cb_info: napi_callback_info,
|
|
result: *mut napi_value<'s>,
|
|
) -> napi_status {
|
|
let env_ptr = env as *mut Env;
|
|
check_arg!(env, result);
|
|
check_arg!(env, cb);
|
|
|
|
let name = if !name.is_null() {
|
|
match unsafe { check_new_from_utf8_len(env, name, length) } {
|
|
Ok(s) => Some(s),
|
|
Err(status) => return status,
|
|
}
|
|
} else {
|
|
None
|
|
};
|
|
|
|
unsafe {
|
|
*result =
|
|
create_function(&mut env.scope(), env_ptr, name, cb.unwrap(), cb_info)
|
|
.into();
|
|
}
|
|
|
|
napi_ok
|
|
}
|
|
|
|
#[napi_sym]
|
|
#[allow(clippy::too_many_arguments)]
|
|
fn napi_define_class<'s>(
|
|
env: &'s mut Env,
|
|
utf8name: *const c_char,
|
|
length: usize,
|
|
constructor: Option<napi_callback>,
|
|
callback_data: *mut c_void,
|
|
property_count: usize,
|
|
properties: *const napi_property_descriptor,
|
|
result: *mut napi_value<'s>,
|
|
) -> napi_status {
|
|
let env_ptr = env as *mut Env;
|
|
check_arg!(env, result);
|
|
check_arg!(env, constructor);
|
|
|
|
if property_count > 0 {
|
|
check_arg!(env, properties);
|
|
}
|
|
|
|
let name = match unsafe { check_new_from_utf8_len(env, utf8name, length) } {
|
|
Ok(string) => string,
|
|
Err(status) => return status,
|
|
};
|
|
|
|
let tpl = create_function_template(
|
|
&mut env.scope(),
|
|
env_ptr,
|
|
Some(name),
|
|
constructor.unwrap(),
|
|
callback_data,
|
|
);
|
|
|
|
let napi_properties: &[napi_property_descriptor] = if property_count > 0 {
|
|
unsafe { std::slice::from_raw_parts(properties, property_count) }
|
|
} else {
|
|
&[]
|
|
};
|
|
let mut static_property_count = 0;
|
|
|
|
for p in napi_properties {
|
|
if p.attributes & napi_static != 0 {
|
|
// Will be handled below
|
|
static_property_count += 1;
|
|
continue;
|
|
}
|
|
|
|
let name = match unsafe { v8_name_from_property_descriptor(env_ptr, p) } {
|
|
Ok(name) => name,
|
|
Err(status) => return status,
|
|
};
|
|
|
|
let mut accessor_property = v8::PropertyAttribute::NONE;
|
|
|
|
if p.attributes & napi_enumerable == 0 {
|
|
accessor_property = accessor_property | v8::PropertyAttribute::DONT_ENUM;
|
|
}
|
|
if p.attributes & napi_configurable == 0 {
|
|
accessor_property =
|
|
accessor_property | v8::PropertyAttribute::DONT_DELETE;
|
|
}
|
|
|
|
if p.getter.is_some() || p.setter.is_some() {
|
|
let getter = p.getter.map(|g| {
|
|
create_function_template(&mut env.scope(), env_ptr, None, g, p.data)
|
|
});
|
|
let setter = p.setter.map(|s| {
|
|
create_function_template(&mut env.scope(), env_ptr, None, s, p.data)
|
|
});
|
|
if getter.is_some()
|
|
&& setter.is_some()
|
|
&& (p.attributes & napi_writable) == 0
|
|
{
|
|
accessor_property =
|
|
accessor_property | v8::PropertyAttribute::READ_ONLY;
|
|
}
|
|
let proto = tpl.prototype_template(&mut env.scope());
|
|
proto.set_accessor_property(name, getter, setter, accessor_property);
|
|
} else if let Some(method) = p.method {
|
|
let function = create_function_template(
|
|
&mut env.scope(),
|
|
env_ptr,
|
|
None,
|
|
method,
|
|
p.data,
|
|
);
|
|
let proto = tpl.prototype_template(&mut env.scope());
|
|
proto.set_with_attr(name, function.into(), accessor_property);
|
|
} else {
|
|
let proto = tpl.prototype_template(&mut env.scope());
|
|
if (p.attributes & napi_writable) == 0 {
|
|
accessor_property =
|
|
accessor_property | v8::PropertyAttribute::READ_ONLY;
|
|
}
|
|
proto.set_with_attr(name, p.value.unwrap().into(), accessor_property);
|
|
}
|
|
}
|
|
|
|
let value: v8::Local<v8::Value> =
|
|
tpl.get_function(&mut env.scope()).unwrap().into();
|
|
|
|
unsafe {
|
|
*result = value.into();
|
|
}
|
|
|
|
if static_property_count > 0 {
|
|
let mut static_descriptors = Vec::with_capacity(static_property_count);
|
|
|
|
for p in napi_properties {
|
|
if p.attributes & napi_static != 0 {
|
|
static_descriptors.push(*p);
|
|
}
|
|
}
|
|
|
|
crate::status_call!(unsafe {
|
|
napi_define_properties(
|
|
env_ptr,
|
|
*result,
|
|
static_descriptors.len(),
|
|
static_descriptors.as_ptr(),
|
|
)
|
|
});
|
|
}
|
|
|
|
napi_ok
|
|
}
|
|
|
|
#[napi_sym]
|
|
fn napi_get_property_names(
|
|
env: *mut Env,
|
|
object: napi_value,
|
|
result: *mut napi_value,
|
|
) -> napi_status {
|
|
unsafe {
|
|
napi_get_all_property_names(
|
|
env,
|
|
object,
|
|
napi_key_include_prototypes,
|
|
napi_key_enumerable | napi_key_skip_symbols,
|
|
napi_key_numbers_to_strings,
|
|
result,
|
|
)
|
|
}
|
|
}
|
|
|
|
#[napi_sym]
|
|
fn napi_get_all_property_names<'s>(
|
|
env: &'s mut Env,
|
|
object: napi_value,
|
|
key_mode: napi_key_collection_mode,
|
|
key_filter: napi_key_filter,
|
|
key_conversion: napi_key_conversion,
|
|
result: *mut napi_value<'s>,
|
|
) -> napi_status {
|
|
check_arg!(env, result);
|
|
|
|
let scope = &mut env.scope();
|
|
|
|
let Some(obj) = object.and_then(|o| o.to_object(scope)) else {
|
|
return napi_object_expected;
|
|
};
|
|
|
|
let mut filter = v8::PropertyFilter::ALL_PROPERTIES;
|
|
|
|
if key_filter & napi_key_writable != 0 {
|
|
filter = filter | v8::PropertyFilter::ONLY_WRITABLE;
|
|
}
|
|
if key_filter & napi_key_enumerable != 0 {
|
|
filter = filter | v8::PropertyFilter::ONLY_ENUMERABLE;
|
|
}
|
|
if key_filter & napi_key_configurable != 0 {
|
|
filter = filter | v8::PropertyFilter::ONLY_CONFIGURABLE;
|
|
}
|
|
if key_filter & napi_key_skip_strings != 0 {
|
|
filter = filter | v8::PropertyFilter::SKIP_STRINGS;
|
|
}
|
|
if key_filter & napi_key_skip_symbols != 0 {
|
|
filter = filter | v8::PropertyFilter::SKIP_SYMBOLS;
|
|
}
|
|
|
|
let key_mode = match key_mode {
|
|
napi_key_include_prototypes => v8::KeyCollectionMode::IncludePrototypes,
|
|
napi_key_own_only => v8::KeyCollectionMode::OwnOnly,
|
|
_ => return napi_invalid_arg,
|
|
};
|
|
|
|
let key_conversion = match key_conversion {
|
|
napi_key_keep_numbers => v8::KeyConversionMode::KeepNumbers,
|
|
napi_key_numbers_to_strings => v8::KeyConversionMode::ConvertToString,
|
|
_ => return napi_invalid_arg,
|
|
};
|
|
|
|
let filter = v8::GetPropertyNamesArgsBuilder::new()
|
|
.mode(key_mode)
|
|
.property_filter(filter)
|
|
.index_filter(v8::IndexFilter::IncludeIndices)
|
|
.key_conversion(key_conversion)
|
|
.build();
|
|
|
|
let property_names = match obj.get_property_names(scope, filter) {
|
|
Some(n) => n,
|
|
None => return napi_generic_failure,
|
|
};
|
|
|
|
unsafe {
|
|
*result = property_names.into();
|
|
}
|
|
|
|
napi_ok
|
|
}
|
|
|
|
#[napi_sym]
|
|
fn napi_set_property(
|
|
env: &mut Env,
|
|
object: napi_value,
|
|
key: napi_value,
|
|
value: napi_value,
|
|
) -> napi_status {
|
|
check_arg!(env, key);
|
|
check_arg!(env, value);
|
|
|
|
let scope = &mut env.scope();
|
|
|
|
let Some(object) = object.and_then(|o| o.to_object(scope)) else {
|
|
return napi_object_expected;
|
|
};
|
|
|
|
if object.set(scope, key.unwrap(), value.unwrap()).is_none() {
|
|
return napi_generic_failure;
|
|
};
|
|
|
|
napi_ok
|
|
}
|
|
|
|
#[napi_sym]
|
|
fn napi_has_property(
|
|
env: &mut Env,
|
|
object: napi_value,
|
|
key: napi_value,
|
|
result: *mut bool,
|
|
) -> napi_status {
|
|
check_arg!(env, key);
|
|
check_arg!(env, result);
|
|
|
|
let scope = &mut env.scope();
|
|
|
|
let Some(object) = object.and_then(|o| o.to_object(scope)) else {
|
|
return napi_object_expected;
|
|
};
|
|
|
|
let Some(has) = object.has(scope, key.unwrap()) else {
|
|
return napi_generic_failure;
|
|
};
|
|
|
|
unsafe {
|
|
*result = has;
|
|
}
|
|
|
|
napi_ok
|
|
}
|
|
|
|
#[napi_sym]
|
|
fn napi_get_property<'s>(
|
|
env: &'s mut Env,
|
|
object: napi_value,
|
|
key: napi_value,
|
|
result: *mut napi_value<'s>,
|
|
) -> napi_status {
|
|
check_arg!(env, key);
|
|
check_arg!(env, result);
|
|
|
|
let scope = &mut env.scope();
|
|
|
|
let Some(object) = object.and_then(|o| o.to_object(scope)) else {
|
|
return napi_object_expected;
|
|
};
|
|
|
|
let Some(value) = object.get(scope, key.unwrap()) else {
|
|
return napi_generic_failure;
|
|
};
|
|
|
|
unsafe {
|
|
*result = value.into();
|
|
}
|
|
|
|
napi_ok
|
|
}
|
|
|
|
#[napi_sym]
|
|
fn napi_delete_property(
|
|
env: &mut Env,
|
|
object: napi_value,
|
|
key: napi_value,
|
|
result: *mut bool,
|
|
) -> napi_status {
|
|
check_arg!(env, key);
|
|
|
|
let scope = &mut env.scope();
|
|
|
|
let Some(object) = object.and_then(|o| o.to_object(scope)) else {
|
|
return napi_object_expected;
|
|
};
|
|
|
|
let Some(deleted) = object.delete(scope, key.unwrap()) else {
|
|
return napi_generic_failure;
|
|
};
|
|
|
|
if !result.is_null() {
|
|
unsafe {
|
|
*result = deleted;
|
|
}
|
|
}
|
|
|
|
napi_ok
|
|
}
|
|
|
|
#[napi_sym]
|
|
fn napi_has_own_property(
|
|
env: &mut Env,
|
|
object: napi_value,
|
|
key: napi_value,
|
|
result: *mut bool,
|
|
) -> napi_status {
|
|
check_arg!(env, key);
|
|
check_arg!(env, result);
|
|
|
|
let scope = &mut env.scope();
|
|
|
|
let Some(object) = object.and_then(|o| o.to_object(scope)) else {
|
|
return napi_object_expected;
|
|
};
|
|
|
|
let Ok(key) = v8::Local::<v8::Name>::try_from(key.unwrap()) else {
|
|
return napi_name_expected;
|
|
};
|
|
|
|
let Some(has_own) = object.has_own_property(scope, key) else {
|
|
return napi_generic_failure;
|
|
};
|
|
|
|
unsafe {
|
|
*result = has_own;
|
|
}
|
|
|
|
napi_ok
|
|
}
|
|
|
|
#[napi_sym]
|
|
fn napi_has_named_property<'s>(
|
|
env: &'s mut Env,
|
|
object: napi_value<'s>,
|
|
utf8name: *const c_char,
|
|
result: *mut bool,
|
|
) -> napi_status {
|
|
let env_ptr = env as *mut Env;
|
|
check_arg!(env, result);
|
|
|
|
let Some(object) = object.and_then(|o| o.to_object(&mut env.scope())) else {
|
|
return napi_object_expected;
|
|
};
|
|
|
|
let key = match unsafe { check_new_from_utf8(env_ptr, utf8name) } {
|
|
Ok(key) => key,
|
|
Err(status) => return status,
|
|
};
|
|
|
|
let Some(has_property) = object.has(&mut env.scope(), key.into()) else {
|
|
return napi_generic_failure;
|
|
};
|
|
|
|
unsafe {
|
|
*result = has_property;
|
|
}
|
|
|
|
napi_ok
|
|
}
|
|
|
|
#[napi_sym]
|
|
fn napi_set_named_property<'s>(
|
|
env: &'s mut Env,
|
|
object: napi_value<'s>,
|
|
utf8name: *const c_char,
|
|
value: napi_value<'s>,
|
|
) -> napi_status {
|
|
check_arg!(env, value);
|
|
let env_ptr = env as *mut Env;
|
|
|
|
let Some(object) = object.and_then(|o| o.to_object(&mut env.scope())) else {
|
|
return napi_object_expected;
|
|
};
|
|
|
|
let key = match unsafe { check_new_from_utf8(env_ptr, utf8name) } {
|
|
Ok(key) => key,
|
|
Err(status) => return status,
|
|
};
|
|
|
|
let value = value.unwrap();
|
|
|
|
if !object
|
|
.set(&mut env.scope(), key.into(), value)
|
|
.unwrap_or(false)
|
|
{
|
|
return napi_generic_failure;
|
|
}
|
|
|
|
napi_ok
|
|
}
|
|
|
|
#[napi_sym]
|
|
fn napi_get_named_property<'s>(
|
|
env: &'s mut Env,
|
|
object: napi_value<'s>,
|
|
utf8name: *const c_char,
|
|
result: *mut napi_value<'s>,
|
|
) -> napi_status {
|
|
check_arg!(env, result);
|
|
let env_ptr = env as *mut Env;
|
|
|
|
let Some(object) = object.and_then(|o| o.to_object(&mut env.scope())) else {
|
|
return napi_object_expected;
|
|
};
|
|
|
|
let key = match unsafe { check_new_from_utf8(env_ptr, utf8name) } {
|
|
Ok(key) => key,
|
|
Err(status) => return status,
|
|
};
|
|
|
|
let Some(value) = object.get(&mut env.scope(), key.into()) else {
|
|
return napi_generic_failure;
|
|
};
|
|
|
|
unsafe {
|
|
*result = value.into();
|
|
}
|
|
|
|
napi_ok
|
|
}
|
|
|
|
#[napi_sym]
|
|
fn napi_set_element<'s>(
|
|
env: &'s mut Env,
|
|
object: napi_value<'s>,
|
|
index: u32,
|
|
value: napi_value<'s>,
|
|
) -> napi_status {
|
|
check_arg!(env, value);
|
|
|
|
let scope = &mut env.scope();
|
|
|
|
let Some(object) = object.and_then(|o| o.to_object(scope)) else {
|
|
return napi_object_expected;
|
|
};
|
|
|
|
if !object
|
|
.set_index(scope, index, value.unwrap())
|
|
.unwrap_or(false)
|
|
{
|
|
return napi_generic_failure;
|
|
}
|
|
|
|
napi_ok
|
|
}
|
|
|
|
#[napi_sym]
|
|
fn napi_has_element(
|
|
env: &mut Env,
|
|
object: napi_value,
|
|
index: u32,
|
|
result: *mut bool,
|
|
) -> napi_status {
|
|
check_arg!(env, result);
|
|
|
|
let scope = &mut env.scope();
|
|
|
|
let Some(object) = object.and_then(|o| o.to_object(scope)) else {
|
|
return napi_object_expected;
|
|
};
|
|
|
|
let Some(has) = object.has_index(scope, index) else {
|
|
return napi_generic_failure;
|
|
};
|
|
|
|
unsafe {
|
|
*result = has;
|
|
}
|
|
|
|
napi_ok
|
|
}
|
|
|
|
#[napi_sym]
|
|
fn napi_get_element<'s>(
|
|
env: &'s mut Env,
|
|
object: napi_value,
|
|
index: u32,
|
|
result: *mut napi_value<'s>,
|
|
) -> napi_status {
|
|
check_arg!(env, result);
|
|
|
|
let scope = &mut env.scope();
|
|
|
|
let Some(object) = object.and_then(|o| o.to_object(scope)) else {
|
|
return napi_object_expected;
|
|
};
|
|
|
|
let Some(value) = object.get_index(scope, index) else {
|
|
return napi_generic_failure;
|
|
};
|
|
|
|
unsafe {
|
|
*result = value.into();
|
|
}
|
|
|
|
napi_ok
|
|
}
|
|
|
|
#[napi_sym]
|
|
fn napi_delete_element(
|
|
env: &mut Env,
|
|
object: napi_value,
|
|
index: u32,
|
|
result: *mut bool,
|
|
) -> napi_status {
|
|
let scope = &mut env.scope();
|
|
|
|
let Some(object) = object.and_then(|o| o.to_object(scope)) else {
|
|
return napi_object_expected;
|
|
};
|
|
|
|
let Some(deleted) = object.delete_index(scope, index) else {
|
|
return napi_generic_failure;
|
|
};
|
|
|
|
if !result.is_null() {
|
|
unsafe {
|
|
*result = deleted;
|
|
}
|
|
}
|
|
|
|
napi_ok
|
|
}
|
|
|
|
#[napi_sym]
|
|
fn napi_define_properties(
|
|
env: &mut Env,
|
|
object: napi_value,
|
|
property_count: usize,
|
|
properties: *const napi_property_descriptor,
|
|
) -> napi_status {
|
|
let env_ptr = env as *mut Env;
|
|
|
|
if property_count > 0 {
|
|
check_arg!(env, properties);
|
|
}
|
|
|
|
let scope = &mut env.scope();
|
|
|
|
let Some(object) = object.and_then(|o| o.to_object(scope)) else {
|
|
return napi_object_expected;
|
|
};
|
|
|
|
let properties = if property_count == 0 {
|
|
&[]
|
|
} else {
|
|
unsafe { std::slice::from_raw_parts(properties, property_count) }
|
|
};
|
|
for property in properties {
|
|
let property_name =
|
|
match unsafe { v8_name_from_property_descriptor(env_ptr, property) } {
|
|
Ok(name) => name,
|
|
Err(status) => return status,
|
|
};
|
|
|
|
let writable = property.attributes & napi_writable != 0;
|
|
let enumerable = property.attributes & napi_enumerable != 0;
|
|
let configurable = property.attributes & napi_configurable != 0;
|
|
|
|
if property.getter.is_some() || property.setter.is_some() {
|
|
let local_getter: v8::Local<v8::Value> = if let Some(getter) =
|
|
property.getter
|
|
{
|
|
create_function(&mut env.scope(), env_ptr, None, getter, property.data)
|
|
.into()
|
|
} else {
|
|
v8::undefined(scope).into()
|
|
};
|
|
let local_setter: v8::Local<v8::Value> = if let Some(setter) =
|
|
property.setter
|
|
{
|
|
create_function(&mut env.scope(), env_ptr, None, setter, property.data)
|
|
.into()
|
|
} else {
|
|
v8::undefined(scope).into()
|
|
};
|
|
|
|
let mut desc =
|
|
v8::PropertyDescriptor::new_from_get_set(local_getter, local_setter);
|
|
desc.set_enumerable(enumerable);
|
|
desc.set_configurable(configurable);
|
|
|
|
if !object
|
|
.define_property(scope, property_name, &desc)
|
|
.unwrap_or(false)
|
|
{
|
|
return napi_invalid_arg;
|
|
}
|
|
} else if let Some(method) = property.method {
|
|
let method: v8::Local<v8::Value> = {
|
|
let function = create_function(
|
|
&mut env.scope(),
|
|
env_ptr,
|
|
None,
|
|
method,
|
|
property.data,
|
|
);
|
|
function.into()
|
|
};
|
|
|
|
let mut desc =
|
|
v8::PropertyDescriptor::new_from_value_writable(method, writable);
|
|
desc.set_enumerable(enumerable);
|
|
desc.set_configurable(configurable);
|
|
|
|
if !object
|
|
.define_property(scope, property_name, &desc)
|
|
.unwrap_or(false)
|
|
{
|
|
return napi_generic_failure;
|
|
}
|
|
} else {
|
|
let value = property.value.unwrap();
|
|
|
|
if enumerable & writable & configurable {
|
|
if !object
|
|
.create_data_property(scope, property_name, value)
|
|
.unwrap_or(false)
|
|
{
|
|
return napi_invalid_arg;
|
|
}
|
|
} else {
|
|
let mut desc =
|
|
v8::PropertyDescriptor::new_from_value_writable(value, writable);
|
|
desc.set_enumerable(enumerable);
|
|
desc.set_configurable(configurable);
|
|
|
|
if !object
|
|
.define_property(scope, property_name, &desc)
|
|
.unwrap_or(false)
|
|
{
|
|
return napi_invalid_arg;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
napi_ok
|
|
}
|
|
|
|
#[napi_sym]
|
|
fn napi_object_freeze(env: &mut Env, object: napi_value) -> napi_status {
|
|
let scope = &mut env.scope();
|
|
|
|
let Some(object) = object.and_then(|o| o.to_object(scope)) else {
|
|
return napi_object_expected;
|
|
};
|
|
|
|
if !object
|
|
.set_integrity_level(scope, v8::IntegrityLevel::Frozen)
|
|
.unwrap_or(false)
|
|
{
|
|
return napi_generic_failure;
|
|
}
|
|
|
|
napi_ok
|
|
}
|
|
|
|
#[napi_sym]
|
|
fn napi_object_seal(env: &mut Env, object: napi_value) -> napi_status {
|
|
let scope = &mut env.scope();
|
|
|
|
let Some(object) = object.and_then(|o| o.to_object(scope)) else {
|
|
return napi_object_expected;
|
|
};
|
|
|
|
if !object
|
|
.set_integrity_level(scope, v8::IntegrityLevel::Sealed)
|
|
.unwrap_or(false)
|
|
{
|
|
return napi_generic_failure;
|
|
}
|
|
|
|
napi_ok
|
|
}
|
|
|
|
#[napi_sym]
|
|
fn napi_is_array(
|
|
env: *mut Env,
|
|
value: napi_value,
|
|
result: *mut bool,
|
|
) -> napi_status {
|
|
let env = check_env!(env);
|
|
check_arg!(env, value);
|
|
check_arg!(env, result);
|
|
|
|
let value = value.unwrap();
|
|
|
|
unsafe {
|
|
*result = value.is_array();
|
|
}
|
|
|
|
napi_clear_last_error(env)
|
|
}
|
|
|
|
#[napi_sym]
|
|
fn napi_get_array_length(
|
|
env: &mut Env,
|
|
value: napi_value,
|
|
result: *mut u32,
|
|
) -> napi_status {
|
|
check_arg!(env, value);
|
|
check_arg!(env, result);
|
|
|
|
let value = value.unwrap();
|
|
|
|
match v8::Local::<v8::Array>::try_from(value) {
|
|
Ok(array) => {
|
|
unsafe {
|
|
*result = array.length();
|
|
}
|
|
napi_ok
|
|
}
|
|
Err(_) => napi_array_expected,
|
|
}
|
|
}
|
|
|
|
#[napi_sym]
|
|
fn napi_strict_equals(
|
|
env: &mut Env,
|
|
lhs: napi_value,
|
|
rhs: napi_value,
|
|
result: *mut bool,
|
|
) -> napi_status {
|
|
check_arg!(env, lhs);
|
|
check_arg!(env, rhs);
|
|
check_arg!(env, result);
|
|
|
|
unsafe {
|
|
*result = lhs.unwrap().strict_equals(rhs.unwrap());
|
|
}
|
|
|
|
napi_ok
|
|
}
|
|
|
|
#[napi_sym]
|
|
fn napi_get_prototype<'s>(
|
|
env: &'s mut Env,
|
|
object: napi_value,
|
|
result: *mut napi_value<'s>,
|
|
) -> napi_status {
|
|
check_arg!(env, result);
|
|
|
|
let scope = &mut env.scope();
|
|
|
|
let Some(object) = object.and_then(|o| o.to_object(scope)) else {
|
|
return napi_object_expected;
|
|
};
|
|
|
|
let Some(proto) = object.get_prototype(scope) else {
|
|
return napi_generic_failure;
|
|
};
|
|
|
|
unsafe {
|
|
*result = proto.into();
|
|
}
|
|
|
|
napi_ok
|
|
}
|
|
|
|
#[napi_sym]
|
|
fn napi_create_object(
|
|
env_ptr: *mut Env,
|
|
result: *mut napi_value,
|
|
) -> napi_status {
|
|
let env = check_env!(env_ptr);
|
|
check_arg!(env, result);
|
|
|
|
unsafe {
|
|
*result = v8::Object::new(&mut env.scope()).into();
|
|
}
|
|
|
|
return napi_clear_last_error(env_ptr);
|
|
}
|
|
|
|
#[napi_sym]
|
|
fn napi_create_array(
|
|
env_ptr: *mut Env,
|
|
result: *mut napi_value,
|
|
) -> napi_status {
|
|
let env = check_env!(env_ptr);
|
|
check_arg!(env, result);
|
|
|
|
unsafe {
|
|
*result = v8::Array::new(&mut env.scope(), 0).into();
|
|
}
|
|
|
|
return napi_clear_last_error(env_ptr);
|
|
}
|
|
|
|
#[napi_sym]
|
|
fn napi_create_array_with_length(
|
|
env_ptr: *mut Env,
|
|
length: usize,
|
|
result: *mut napi_value,
|
|
) -> napi_status {
|
|
let env = check_env!(env_ptr);
|
|
check_arg!(env, result);
|
|
|
|
unsafe {
|
|
*result = v8::Array::new(&mut env.scope(), length as _).into();
|
|
}
|
|
|
|
return napi_clear_last_error(env_ptr);
|
|
}
|
|
|
|
#[napi_sym]
|
|
fn napi_create_string_latin1(
|
|
env_ptr: *mut Env,
|
|
string: *const c_char,
|
|
length: usize,
|
|
result: *mut napi_value,
|
|
) -> napi_status {
|
|
let env = check_env!(env_ptr);
|
|
if length > 0 {
|
|
check_arg!(env, string);
|
|
}
|
|
crate::return_status_if_false!(
|
|
env,
|
|
(length == NAPI_AUTO_LENGTH) || length <= INT_MAX as _,
|
|
napi_invalid_arg
|
|
);
|
|
|
|
let buffer = if length > 0 {
|
|
unsafe {
|
|
std::slice::from_raw_parts(
|
|
string as _,
|
|
if length == NAPI_AUTO_LENGTH {
|
|
std::ffi::CStr::from_ptr(string).to_bytes().len()
|
|
} else {
|
|
length
|
|
},
|
|
)
|
|
}
|
|
} else {
|
|
&[]
|
|
};
|
|
|
|
let Some(string) = v8::String::new_from_one_byte(
|
|
&mut env.scope(),
|
|
buffer,
|
|
v8::NewStringType::Normal,
|
|
) else {
|
|
return napi_set_last_error(env_ptr, napi_generic_failure);
|
|
};
|
|
|
|
unsafe {
|
|
*result = string.into();
|
|
}
|
|
|
|
return napi_clear_last_error(env_ptr);
|
|
}
|
|
|
|
#[napi_sym]
|
|
pub(crate) fn napi_create_string_utf8(
|
|
env_ptr: *mut Env,
|
|
string: *const c_char,
|
|
length: usize,
|
|
result: *mut napi_value,
|
|
) -> napi_status {
|
|
let env = check_env!(env_ptr);
|
|
if length > 0 {
|
|
check_arg!(env, string);
|
|
}
|
|
crate::return_status_if_false!(
|
|
env,
|
|
(length == NAPI_AUTO_LENGTH) || length <= INT_MAX as _,
|
|
napi_invalid_arg
|
|
);
|
|
|
|
let buffer = if length > 0 {
|
|
unsafe {
|
|
std::slice::from_raw_parts(
|
|
string as _,
|
|
if length == NAPI_AUTO_LENGTH {
|
|
std::ffi::CStr::from_ptr(string).to_bytes().len()
|
|
} else {
|
|
length
|
|
},
|
|
)
|
|
}
|
|
} else {
|
|
&[]
|
|
};
|
|
|
|
let Some(string) = v8::String::new_from_utf8(
|
|
&mut env.scope(),
|
|
buffer,
|
|
v8::NewStringType::Normal,
|
|
) else {
|
|
return napi_set_last_error(env_ptr, napi_generic_failure);
|
|
};
|
|
|
|
unsafe {
|
|
*result = string.into();
|
|
}
|
|
|
|
return napi_clear_last_error(env_ptr);
|
|
}
|
|
|
|
#[napi_sym]
|
|
fn napi_create_string_utf16(
|
|
env_ptr: *mut Env,
|
|
string: *const u16,
|
|
length: usize,
|
|
result: *mut napi_value,
|
|
) -> napi_status {
|
|
let env = check_env!(env_ptr);
|
|
if length > 0 {
|
|
check_arg!(env, string);
|
|
}
|
|
crate::return_status_if_false!(
|
|
env,
|
|
(length == NAPI_AUTO_LENGTH) || length <= INT_MAX as _,
|
|
napi_invalid_arg
|
|
);
|
|
|
|
let buffer = if length > 0 {
|
|
unsafe {
|
|
std::slice::from_raw_parts(
|
|
string,
|
|
if length == NAPI_AUTO_LENGTH {
|
|
let mut length = 0;
|
|
while *(string.add(length)) != 0 {
|
|
length += 1;
|
|
}
|
|
length
|
|
} else {
|
|
length
|
|
},
|
|
)
|
|
}
|
|
} else {
|
|
&[]
|
|
};
|
|
|
|
let Some(string) = v8::String::new_from_two_byte(
|
|
&mut env.scope(),
|
|
buffer,
|
|
v8::NewStringType::Normal,
|
|
) else {
|
|
return napi_set_last_error(env_ptr, napi_generic_failure);
|
|
};
|
|
|
|
unsafe {
|
|
*result = string.into();
|
|
}
|
|
|
|
return napi_clear_last_error(env_ptr);
|
|
}
|
|
|
|
#[napi_sym]
|
|
fn node_api_create_external_string_latin1(
|
|
env_ptr: *mut Env,
|
|
string: *const c_char,
|
|
length: usize,
|
|
nogc_finalize_callback: Option<napi_finalize>,
|
|
finalize_hint: *mut c_void,
|
|
result: *mut napi_value,
|
|
copied: *mut bool,
|
|
) -> napi_status {
|
|
let status =
|
|
unsafe { napi_create_string_latin1(env_ptr, string, length, result) };
|
|
|
|
if status == napi_ok {
|
|
unsafe {
|
|
*copied = true;
|
|
}
|
|
|
|
if let Some(finalize) = nogc_finalize_callback {
|
|
unsafe {
|
|
finalize(env_ptr as napi_env, string as *mut c_void, finalize_hint);
|
|
}
|
|
}
|
|
}
|
|
|
|
status
|
|
}
|
|
|
|
#[napi_sym]
|
|
fn node_api_create_external_string_utf16(
|
|
env_ptr: *mut Env,
|
|
string: *const u16,
|
|
length: usize,
|
|
nogc_finalize_callback: Option<napi_finalize>,
|
|
finalize_hint: *mut c_void,
|
|
result: *mut napi_value,
|
|
copied: *mut bool,
|
|
) -> napi_status {
|
|
let status =
|
|
unsafe { napi_create_string_utf16(env_ptr, string, length, result) };
|
|
|
|
if status == napi_ok {
|
|
unsafe {
|
|
*copied = true;
|
|
}
|
|
|
|
if let Some(finalize) = nogc_finalize_callback {
|
|
unsafe {
|
|
finalize(env_ptr as napi_env, string as *mut c_void, finalize_hint);
|
|
}
|
|
}
|
|
}
|
|
|
|
status
|
|
}
|
|
|
|
#[napi_sym]
|
|
fn node_api_create_property_key_utf16(
|
|
env_ptr: *mut Env,
|
|
string: *const u16,
|
|
length: usize,
|
|
result: *mut napi_value,
|
|
) -> napi_status {
|
|
let env = check_env!(env_ptr);
|
|
if length > 0 {
|
|
check_arg!(env, string);
|
|
}
|
|
crate::return_status_if_false!(
|
|
env,
|
|
(length == NAPI_AUTO_LENGTH) || length <= INT_MAX as _,
|
|
napi_invalid_arg
|
|
);
|
|
|
|
let buffer = if length > 0 {
|
|
unsafe {
|
|
std::slice::from_raw_parts(
|
|
string,
|
|
if length == NAPI_AUTO_LENGTH {
|
|
let mut length = 0;
|
|
while *(string.add(length)) != 0 {
|
|
length += 1;
|
|
}
|
|
length
|
|
} else {
|
|
length
|
|
},
|
|
)
|
|
}
|
|
} else {
|
|
&[]
|
|
};
|
|
|
|
let Some(string) = v8::String::new_from_two_byte(
|
|
&mut env.scope(),
|
|
buffer,
|
|
v8::NewStringType::Internalized,
|
|
) else {
|
|
return napi_set_last_error(env_ptr, napi_generic_failure);
|
|
};
|
|
|
|
unsafe {
|
|
*result = string.into();
|
|
}
|
|
|
|
return napi_clear_last_error(env_ptr);
|
|
}
|
|
|
|
#[napi_sym]
|
|
fn napi_create_double(
|
|
env_ptr: *mut Env,
|
|
value: f64,
|
|
result: *mut napi_value,
|
|
) -> napi_status {
|
|
let env = check_env!(env_ptr);
|
|
check_arg!(env, result);
|
|
|
|
unsafe {
|
|
*result = v8::Number::new(&mut env.scope(), value).into();
|
|
}
|
|
|
|
napi_clear_last_error(env_ptr)
|
|
}
|
|
|
|
#[napi_sym]
|
|
fn napi_create_int32(
|
|
env_ptr: *mut Env,
|
|
value: i32,
|
|
result: *mut napi_value,
|
|
) -> napi_status {
|
|
let env = check_env!(env_ptr);
|
|
check_arg!(env, result);
|
|
|
|
unsafe {
|
|
*result = v8::Integer::new(&mut env.scope(), value).into();
|
|
}
|
|
|
|
napi_clear_last_error(env_ptr)
|
|
}
|
|
|
|
#[napi_sym]
|
|
fn napi_create_uint32(
|
|
env_ptr: *mut Env,
|
|
value: u32,
|
|
result: *mut napi_value,
|
|
) -> napi_status {
|
|
let env = check_env!(env_ptr);
|
|
check_arg!(env, result);
|
|
|
|
unsafe {
|
|
*result = v8::Integer::new_from_unsigned(&mut env.scope(), value).into();
|
|
}
|
|
|
|
napi_clear_last_error(env_ptr)
|
|
}
|
|
|
|
#[napi_sym]
|
|
fn napi_create_int64(
|
|
env_ptr: *mut Env,
|
|
value: i64,
|
|
result: *mut napi_value,
|
|
) -> napi_status {
|
|
let env = check_env!(env_ptr);
|
|
check_arg!(env, result);
|
|
|
|
unsafe {
|
|
*result = v8::Number::new(&mut env.scope(), value as _).into();
|
|
}
|
|
|
|
napi_clear_last_error(env_ptr)
|
|
}
|
|
|
|
#[napi_sym]
|
|
fn napi_create_bigint_int64(
|
|
env_ptr: *mut Env,
|
|
value: i64,
|
|
result: *mut napi_value,
|
|
) -> napi_status {
|
|
let env = check_env!(env_ptr);
|
|
check_arg!(env, result);
|
|
|
|
unsafe {
|
|
*result = v8::BigInt::new_from_i64(&mut env.scope(), value).into();
|
|
}
|
|
|
|
napi_clear_last_error(env_ptr)
|
|
}
|
|
|
|
#[napi_sym]
|
|
fn napi_create_bigint_uint64(
|
|
env_ptr: *mut Env,
|
|
value: u64,
|
|
result: *mut napi_value,
|
|
) -> napi_status {
|
|
let env = check_env!(env_ptr);
|
|
check_arg!(env, result);
|
|
|
|
unsafe {
|
|
*result = v8::BigInt::new_from_u64(&mut env.scope(), value).into();
|
|
}
|
|
|
|
napi_clear_last_error(env_ptr)
|
|
}
|
|
|
|
#[napi_sym]
|
|
fn napi_create_bigint_words<'s>(
|
|
env: &'s mut Env,
|
|
sign_bit: bool,
|
|
word_count: usize,
|
|
words: *const u64,
|
|
result: *mut napi_value<'s>,
|
|
) -> napi_status {
|
|
check_arg!(env, words);
|
|
check_arg!(env, result);
|
|
|
|
if word_count > INT_MAX as _ {
|
|
return napi_invalid_arg;
|
|
}
|
|
|
|
match v8::BigInt::new_from_words(&mut env.scope(), sign_bit, unsafe {
|
|
std::slice::from_raw_parts(words, word_count)
|
|
}) {
|
|
Some(value) => unsafe {
|
|
*result = value.into();
|
|
},
|
|
None => {
|
|
return napi_generic_failure;
|
|
}
|
|
}
|
|
|
|
napi_ok
|
|
}
|
|
|
|
#[napi_sym]
|
|
fn napi_get_boolean(
|
|
env: *mut Env,
|
|
value: bool,
|
|
result: *mut napi_value,
|
|
) -> napi_status {
|
|
let env = check_env!(env);
|
|
check_arg!(env, result);
|
|
|
|
unsafe {
|
|
*result = v8::Boolean::new(env.isolate(), value).into();
|
|
}
|
|
|
|
return napi_clear_last_error(env);
|
|
}
|
|
|
|
#[napi_sym]
|
|
fn napi_create_symbol(
|
|
env_ptr: *mut Env,
|
|
description: napi_value,
|
|
result: *mut napi_value,
|
|
) -> napi_status {
|
|
let env = check_env!(env_ptr);
|
|
check_arg!(env, result);
|
|
|
|
let description = if let Some(d) = *description {
|
|
let Some(d) = d.to_string(&mut env.scope()) else {
|
|
return napi_set_last_error(env, napi_string_expected);
|
|
};
|
|
Some(d)
|
|
} else {
|
|
None
|
|
};
|
|
|
|
unsafe {
|
|
*result = v8::Symbol::new(&mut env.scope(), description).into();
|
|
}
|
|
|
|
return napi_clear_last_error(env_ptr);
|
|
}
|
|
|
|
#[napi_sym]
|
|
fn node_api_symbol_for(
|
|
env: *mut Env,
|
|
utf8description: *const c_char,
|
|
length: usize,
|
|
result: *mut napi_value,
|
|
) -> napi_status {
|
|
{
|
|
let env = check_env!(env);
|
|
check_arg!(env, result);
|
|
|
|
let description_string =
|
|
match unsafe { check_new_from_utf8_len(env, utf8description, length) } {
|
|
Ok(s) => s,
|
|
Err(status) => return napi_set_last_error(env, status),
|
|
};
|
|
|
|
unsafe {
|
|
*result =
|
|
v8::Symbol::for_key(&mut env.scope(), description_string).into();
|
|
}
|
|
}
|
|
|
|
napi_clear_last_error(env)
|
|
}
|
|
|
|
macro_rules! napi_create_error_impl {
|
|
($env_ptr:ident, $code:ident, $msg:ident, $result:ident, $error:ident) => {{
|
|
let env_ptr = $env_ptr;
|
|
let code = $code;
|
|
let msg = $msg;
|
|
let result = $result;
|
|
|
|
let env = check_env!(env_ptr);
|
|
check_arg!(env, msg);
|
|
check_arg!(env, result);
|
|
|
|
let Some(message) =
|
|
msg.and_then(|v| v8::Local::<v8::String>::try_from(v).ok())
|
|
else {
|
|
return napi_set_last_error(env_ptr, napi_string_expected);
|
|
};
|
|
|
|
let error = v8::Exception::$error(&mut env.scope(), message);
|
|
|
|
if let Some(code) = *code {
|
|
let error_obj: v8::Local<v8::Object> = error.try_into().unwrap();
|
|
let code_key = v8::String::new(&mut env.scope(), "code").unwrap();
|
|
if !error_obj
|
|
.set(&mut env.scope(), code_key.into(), code)
|
|
.unwrap_or(false)
|
|
{
|
|
return napi_set_last_error(env_ptr, napi_generic_failure);
|
|
}
|
|
}
|
|
|
|
unsafe {
|
|
*result = error.into();
|
|
}
|
|
|
|
return napi_clear_last_error(env_ptr);
|
|
}};
|
|
}
|
|
|
|
#[napi_sym]
|
|
fn napi_create_error(
|
|
env_ptr: *mut Env,
|
|
code: napi_value,
|
|
msg: napi_value,
|
|
result: *mut napi_value,
|
|
) -> napi_status {
|
|
napi_create_error_impl!(env_ptr, code, msg, result, error)
|
|
}
|
|
|
|
#[napi_sym]
|
|
fn napi_create_type_error(
|
|
env_ptr: *mut Env,
|
|
code: napi_value,
|
|
msg: napi_value,
|
|
result: *mut napi_value,
|
|
) -> napi_status {
|
|
napi_create_error_impl!(env_ptr, code, msg, result, type_error)
|
|
}
|
|
|
|
#[napi_sym]
|
|
fn napi_create_range_error(
|
|
env_ptr: *mut Env,
|
|
code: napi_value,
|
|
msg: napi_value,
|
|
result: *mut napi_value,
|
|
) -> napi_status {
|
|
napi_create_error_impl!(env_ptr, code, msg, result, range_error)
|
|
}
|
|
|
|
#[napi_sym]
|
|
fn node_api_create_syntax_error(
|
|
env_ptr: *mut Env,
|
|
code: napi_value,
|
|
msg: napi_value,
|
|
result: *mut napi_value,
|
|
) -> napi_status {
|
|
napi_create_error_impl!(env_ptr, code, msg, result, syntax_error)
|
|
}
|
|
|
|
pub fn get_value_type(value: v8::Local<v8::Value>) -> Option<napi_valuetype> {
|
|
if value.is_undefined() {
|
|
Some(napi_undefined)
|
|
} else if value.is_null() {
|
|
Some(napi_null)
|
|
} else if value.is_external() {
|
|
Some(napi_external)
|
|
} else if value.is_boolean() {
|
|
Some(napi_boolean)
|
|
} else if value.is_number() {
|
|
Some(napi_number)
|
|
} else if value.is_big_int() {
|
|
Some(napi_bigint)
|
|
} else if value.is_string() {
|
|
Some(napi_string)
|
|
} else if value.is_symbol() {
|
|
Some(napi_symbol)
|
|
} else if value.is_function() {
|
|
Some(napi_function)
|
|
} else if value.is_object() {
|
|
Some(napi_object)
|
|
} else {
|
|
None
|
|
}
|
|
}
|
|
|
|
#[napi_sym]
|
|
fn napi_typeof(
|
|
env: *mut Env,
|
|
value: napi_value,
|
|
result: *mut napi_valuetype,
|
|
) -> napi_status {
|
|
let env = check_env!(env);
|
|
check_arg!(env, value);
|
|
check_arg!(env, result);
|
|
|
|
let Some(ty) = get_value_type(value.unwrap()) else {
|
|
return napi_set_last_error(env, napi_invalid_arg);
|
|
};
|
|
|
|
unsafe {
|
|
*result = ty;
|
|
}
|
|
|
|
napi_clear_last_error(env)
|
|
}
|
|
|
|
#[napi_sym]
|
|
fn napi_get_undefined(env: *mut Env, result: *mut napi_value) -> napi_status {
|
|
let env = check_env!(env);
|
|
check_arg!(env, result);
|
|
|
|
unsafe {
|
|
*result = v8::undefined(&mut env.scope()).into();
|
|
}
|
|
|
|
return napi_clear_last_error(env);
|
|
}
|
|
|
|
#[napi_sym]
|
|
fn napi_get_null(env: *mut Env, result: *mut napi_value) -> napi_status {
|
|
let env = check_env!(env);
|
|
check_arg!(env, result);
|
|
|
|
unsafe {
|
|
*result = v8::null(&mut env.scope()).into();
|
|
}
|
|
|
|
return napi_clear_last_error(env);
|
|
}
|
|
|
|
#[napi_sym]
|
|
fn napi_get_cb_info(
|
|
env: *mut Env,
|
|
cbinfo: napi_callback_info,
|
|
argc: *mut i32,
|
|
argv: *mut napi_value,
|
|
this_arg: *mut napi_value,
|
|
data: *mut *mut c_void,
|
|
) -> napi_status {
|
|
let env = check_env!(env);
|
|
check_arg!(env, cbinfo);
|
|
|
|
let cbinfo: &CallbackInfo = unsafe { &*(cbinfo as *const CallbackInfo) };
|
|
let args = unsafe { &*(cbinfo.args as *const v8::FunctionCallbackArguments) };
|
|
|
|
if !argv.is_null() {
|
|
check_arg!(env, argc);
|
|
let argc = unsafe { *argc as usize };
|
|
for i in 0..argc {
|
|
let arg = args.get(i as _);
|
|
unsafe {
|
|
*argv.add(i) = arg.into();
|
|
}
|
|
}
|
|
}
|
|
|
|
if !argc.is_null() {
|
|
unsafe {
|
|
*argc = args.length();
|
|
}
|
|
}
|
|
|
|
if !this_arg.is_null() {
|
|
unsafe {
|
|
*this_arg = args.this().into();
|
|
}
|
|
}
|
|
|
|
if !data.is_null() {
|
|
unsafe {
|
|
*data = cbinfo.cb_info;
|
|
}
|
|
}
|
|
|
|
napi_clear_last_error(env);
|
|
napi_ok
|
|
}
|
|
|
|
#[napi_sym]
|
|
fn napi_get_new_target(
|
|
env: *mut Env,
|
|
cbinfo: napi_callback_info,
|
|
result: *mut napi_value,
|
|
) -> napi_status {
|
|
let env = check_env!(env);
|
|
check_arg!(env, cbinfo);
|
|
check_arg!(env, result);
|
|
|
|
let cbinfo: &CallbackInfo = unsafe { &*(cbinfo as *const CallbackInfo) };
|
|
let args = unsafe { &*(cbinfo.args as *const v8::FunctionCallbackArguments) };
|
|
|
|
unsafe {
|
|
*result = args.new_target().into();
|
|
}
|
|
|
|
return napi_clear_last_error(env);
|
|
}
|
|
|
|
#[napi_sym]
|
|
fn napi_call_function<'s>(
|
|
env: &'s mut Env,
|
|
recv: napi_value<'s>,
|
|
func: napi_value<'s>,
|
|
argc: usize,
|
|
argv: *const napi_value<'s>,
|
|
result: *mut napi_value<'s>,
|
|
) -> napi_status {
|
|
check_arg!(env, recv);
|
|
let args = if argc > 0 {
|
|
check_arg!(env, argv);
|
|
unsafe {
|
|
std::slice::from_raw_parts(argv as *mut v8::Local<v8::Value>, argc)
|
|
}
|
|
} else {
|
|
&[]
|
|
};
|
|
|
|
let Some(func) =
|
|
func.and_then(|f| v8::Local::<v8::Function>::try_from(f).ok())
|
|
else {
|
|
return napi_function_expected;
|
|
};
|
|
|
|
let Some(v) = func.call(&mut env.scope(), recv.unwrap(), args) else {
|
|
return napi_generic_failure;
|
|
};
|
|
|
|
if !result.is_null() {
|
|
unsafe {
|
|
*result = v.into();
|
|
}
|
|
}
|
|
|
|
napi_ok
|
|
}
|
|
|
|
#[napi_sym]
|
|
fn napi_get_global(env_ptr: *mut Env, result: *mut napi_value) -> napi_status {
|
|
let env = check_env!(env_ptr);
|
|
check_arg!(env, result);
|
|
|
|
let global = v8::Local::new(&mut env.scope(), &env.global);
|
|
unsafe {
|
|
*result = global.into();
|
|
}
|
|
|
|
return napi_clear_last_error(env_ptr);
|
|
}
|
|
|
|
#[napi_sym]
|
|
fn napi_throw(env: *mut Env, error: napi_value) -> napi_status {
|
|
let env = check_env!(env);
|
|
check_arg!(env, error);
|
|
|
|
if env.last_exception.is_some() {
|
|
return napi_pending_exception;
|
|
}
|
|
|
|
let error = error.unwrap();
|
|
env.scope().throw_exception(error);
|
|
let error = v8::Global::new(&mut env.scope(), error);
|
|
env.last_exception = Some(error);
|
|
|
|
napi_clear_last_error(env)
|
|
}
|
|
|
|
macro_rules! napi_throw_error_impl {
|
|
($env:ident, $code:ident, $msg:ident, $error:ident) => {{
|
|
let env = check_env!($env);
|
|
let env_ptr = env as *mut Env;
|
|
let code = $code;
|
|
let msg = $msg;
|
|
|
|
if env.last_exception.is_some() {
|
|
return napi_pending_exception;
|
|
}
|
|
|
|
let str_ = match unsafe { check_new_from_utf8(env, msg) } {
|
|
Ok(s) => s,
|
|
Err(status) => return status,
|
|
};
|
|
|
|
let error = v8::Exception::$error(&mut env.scope(), str_);
|
|
|
|
if !code.is_null() {
|
|
let error_obj: v8::Local<v8::Object> = error.try_into().unwrap();
|
|
let code = match unsafe { check_new_from_utf8(env_ptr, code) } {
|
|
Ok(s) => s,
|
|
Err(status) => return napi_set_last_error(env, status),
|
|
};
|
|
let code_key = v8::String::new(&mut env.scope(), "code").unwrap();
|
|
if !error_obj
|
|
.set(&mut env.scope(), code_key.into(), code.into())
|
|
.unwrap_or(false)
|
|
{
|
|
return napi_set_last_error(env, napi_generic_failure);
|
|
}
|
|
}
|
|
|
|
env.scope().throw_exception(error);
|
|
let error = v8::Global::new(&mut env.scope(), error);
|
|
env.last_exception = Some(error);
|
|
|
|
napi_clear_last_error(env)
|
|
}};
|
|
}
|
|
|
|
#[napi_sym]
|
|
fn napi_throw_error(
|
|
env: *mut Env,
|
|
code: *const c_char,
|
|
msg: *const c_char,
|
|
) -> napi_status {
|
|
napi_throw_error_impl!(env, code, msg, error)
|
|
}
|
|
|
|
#[napi_sym]
|
|
fn napi_throw_type_error(
|
|
env: *mut Env,
|
|
code: *const c_char,
|
|
msg: *const c_char,
|
|
) -> napi_status {
|
|
napi_throw_error_impl!(env, code, msg, type_error)
|
|
}
|
|
|
|
#[napi_sym]
|
|
fn napi_throw_range_error(
|
|
env: *mut Env,
|
|
code: *const c_char,
|
|
msg: *const c_char,
|
|
) -> napi_status {
|
|
napi_throw_error_impl!(env, code, msg, range_error)
|
|
}
|
|
|
|
#[napi_sym]
|
|
fn node_api_throw_syntax_error(
|
|
env: *mut Env,
|
|
code: *const c_char,
|
|
msg: *const c_char,
|
|
) -> napi_status {
|
|
napi_throw_error_impl!(env, code, msg, syntax_error)
|
|
}
|
|
|
|
#[napi_sym]
|
|
fn napi_is_error(
|
|
env: *mut Env,
|
|
value: napi_value,
|
|
result: *mut bool,
|
|
) -> napi_status {
|
|
let env = check_env!(env);
|
|
check_arg!(env, value);
|
|
check_arg!(env, result);
|
|
|
|
unsafe {
|
|
*result = value.unwrap().is_native_error();
|
|
}
|
|
|
|
return napi_clear_last_error(env);
|
|
}
|
|
|
|
#[napi_sym]
|
|
fn napi_get_value_double(
|
|
env_ptr: *mut Env,
|
|
value: napi_value,
|
|
result: *mut f64,
|
|
) -> napi_status {
|
|
let env = check_env!(env_ptr);
|
|
check_arg!(env, value);
|
|
check_arg!(env, result);
|
|
|
|
let Some(number) =
|
|
value.and_then(|v| v8::Local::<v8::Number>::try_from(v).ok())
|
|
else {
|
|
return napi_set_last_error(env_ptr, napi_number_expected);
|
|
};
|
|
|
|
unsafe {
|
|
*result = number.value();
|
|
}
|
|
|
|
return napi_clear_last_error(env_ptr);
|
|
}
|
|
|
|
#[napi_sym]
|
|
fn napi_get_value_int32(
|
|
env_ptr: *mut Env,
|
|
value: napi_value,
|
|
result: *mut i32,
|
|
) -> napi_status {
|
|
let env = check_env!(env_ptr);
|
|
check_arg!(env, value);
|
|
check_arg!(env, result);
|
|
|
|
let Some(value) = value.unwrap().int32_value(&mut env.scope()) else {
|
|
return napi_set_last_error(env, napi_number_expected);
|
|
};
|
|
|
|
unsafe {
|
|
*result = value;
|
|
}
|
|
|
|
return napi_clear_last_error(env_ptr);
|
|
}
|
|
|
|
#[napi_sym]
|
|
fn napi_get_value_uint32(
|
|
env_ptr: *mut Env,
|
|
value: napi_value,
|
|
result: *mut u32,
|
|
) -> napi_status {
|
|
let env = check_env!(env_ptr);
|
|
check_arg!(env, value);
|
|
check_arg!(env, result);
|
|
|
|
let Some(value) = value.unwrap().uint32_value(&mut env.scope()) else {
|
|
return napi_set_last_error(env, napi_number_expected);
|
|
};
|
|
|
|
unsafe {
|
|
*result = value;
|
|
}
|
|
|
|
return napi_clear_last_error(env_ptr);
|
|
}
|
|
|
|
#[napi_sym]
|
|
fn napi_get_value_int64(
|
|
env_ptr: *mut Env,
|
|
value: napi_value,
|
|
result: *mut i64,
|
|
) -> napi_status {
|
|
let env = check_env!(env_ptr);
|
|
check_arg!(env, value);
|
|
check_arg!(env, result);
|
|
|
|
let Some(number) =
|
|
value.and_then(|v| v8::Local::<v8::Number>::try_from(v).ok())
|
|
else {
|
|
return napi_set_last_error(env_ptr, napi_number_expected);
|
|
};
|
|
|
|
let value = number.value();
|
|
|
|
unsafe {
|
|
if value.is_finite() {
|
|
*result = value as _;
|
|
} else {
|
|
*result = 0;
|
|
}
|
|
}
|
|
|
|
return napi_clear_last_error(env_ptr);
|
|
}
|
|
|
|
#[napi_sym]
|
|
fn napi_get_value_bigint_int64(
|
|
env_ptr: *mut Env,
|
|
value: napi_value,
|
|
result: *mut i64,
|
|
lossless: *mut bool,
|
|
) -> napi_status {
|
|
let env = check_env!(env_ptr);
|
|
check_arg!(env, value);
|
|
check_arg!(env, result);
|
|
check_arg!(env, lossless);
|
|
|
|
let Some(bigint) =
|
|
value.and_then(|v| v8::Local::<v8::BigInt>::try_from(v).ok())
|
|
else {
|
|
return napi_set_last_error(env_ptr, napi_bigint_expected);
|
|
};
|
|
|
|
let (result_, lossless_) = bigint.i64_value();
|
|
|
|
unsafe {
|
|
*result = result_;
|
|
*lossless = lossless_;
|
|
}
|
|
|
|
return napi_clear_last_error(env_ptr);
|
|
}
|
|
|
|
#[napi_sym]
|
|
fn napi_get_value_bigint_uint64(
|
|
env_ptr: *mut Env,
|
|
value: napi_value,
|
|
result: *mut u64,
|
|
lossless: *mut bool,
|
|
) -> napi_status {
|
|
let env = check_env!(env_ptr);
|
|
check_arg!(env, value);
|
|
check_arg!(env, result);
|
|
check_arg!(env, lossless);
|
|
|
|
let Some(bigint) =
|
|
value.and_then(|v| v8::Local::<v8::BigInt>::try_from(v).ok())
|
|
else {
|
|
return napi_set_last_error(env_ptr, napi_bigint_expected);
|
|
};
|
|
|
|
let (result_, lossless_) = bigint.u64_value();
|
|
|
|
unsafe {
|
|
*result = result_;
|
|
*lossless = lossless_;
|
|
}
|
|
|
|
return napi_clear_last_error(env_ptr);
|
|
}
|
|
|
|
#[napi_sym]
|
|
fn napi_get_value_bigint_words(
|
|
env_ptr: *mut Env,
|
|
value: napi_value,
|
|
sign_bit: *mut i32,
|
|
word_count: *mut usize,
|
|
words: *mut u64,
|
|
) -> napi_status {
|
|
let env = check_env!(env_ptr);
|
|
check_arg!(env, value);
|
|
check_arg!(env, word_count);
|
|
|
|
let Some(bigint) =
|
|
value.and_then(|v| v8::Local::<v8::BigInt>::try_from(v).ok())
|
|
else {
|
|
return napi_set_last_error(env_ptr, napi_bigint_expected);
|
|
};
|
|
|
|
let word_count_int;
|
|
|
|
if sign_bit.is_null() && words.is_null() {
|
|
word_count_int = bigint.word_count();
|
|
} else {
|
|
check_arg!(env, sign_bit);
|
|
check_arg!(env, words);
|
|
let out_words =
|
|
unsafe { std::slice::from_raw_parts_mut(words, *word_count) };
|
|
let (sign, slice_) = bigint.to_words_array(out_words);
|
|
word_count_int = slice_.len();
|
|
unsafe {
|
|
*sign_bit = sign as i32;
|
|
}
|
|
}
|
|
|
|
unsafe {
|
|
*word_count = word_count_int;
|
|
}
|
|
|
|
return napi_clear_last_error(env_ptr);
|
|
}
|
|
|
|
#[napi_sym]
|
|
fn napi_get_value_bool(
|
|
env_ptr: *mut Env,
|
|
value: napi_value,
|
|
result: *mut bool,
|
|
) -> napi_status {
|
|
let env = check_env!(env_ptr);
|
|
check_arg!(env, value);
|
|
check_arg!(env, result);
|
|
|
|
let Some(boolean) =
|
|
value.and_then(|v| v8::Local::<v8::Boolean>::try_from(v).ok())
|
|
else {
|
|
return napi_set_last_error(env_ptr, napi_boolean_expected);
|
|
};
|
|
|
|
unsafe {
|
|
*result = boolean.is_true();
|
|
}
|
|
|
|
return napi_clear_last_error(env_ptr);
|
|
}
|
|
|
|
#[napi_sym]
|
|
fn napi_get_value_string_latin1(
|
|
env_ptr: *mut Env,
|
|
value: napi_value,
|
|
buf: *mut c_char,
|
|
bufsize: usize,
|
|
result: *mut usize,
|
|
) -> napi_status {
|
|
let env = check_env!(env_ptr);
|
|
check_arg!(env, value);
|
|
|
|
let Some(value) =
|
|
value.and_then(|v| v8::Local::<v8::String>::try_from(v).ok())
|
|
else {
|
|
return napi_set_last_error(env_ptr, napi_string_expected);
|
|
};
|
|
|
|
if buf.is_null() {
|
|
check_arg!(env, result);
|
|
unsafe {
|
|
*result = value.length();
|
|
}
|
|
} else if bufsize != 0 {
|
|
let buffer =
|
|
unsafe { std::slice::from_raw_parts_mut(buf as _, bufsize - 1) };
|
|
let copied = value.write_one_byte(
|
|
&mut env.scope(),
|
|
buffer,
|
|
0,
|
|
v8::WriteOptions::NO_NULL_TERMINATION,
|
|
);
|
|
unsafe {
|
|
buf.add(copied).write(0);
|
|
}
|
|
if !result.is_null() {
|
|
unsafe {
|
|
*result = copied;
|
|
}
|
|
}
|
|
} else if !result.is_null() {
|
|
unsafe {
|
|
*result = 0;
|
|
}
|
|
}
|
|
|
|
napi_clear_last_error(env_ptr)
|
|
}
|
|
|
|
#[napi_sym]
|
|
fn napi_get_value_string_utf8(
|
|
env_ptr: *mut Env,
|
|
value: napi_value,
|
|
buf: *mut u8,
|
|
bufsize: usize,
|
|
result: *mut usize,
|
|
) -> napi_status {
|
|
let env = check_env!(env_ptr);
|
|
check_arg!(env, value);
|
|
|
|
let Some(value) =
|
|
value.and_then(|v| v8::Local::<v8::String>::try_from(v).ok())
|
|
else {
|
|
return napi_set_last_error(env_ptr, napi_string_expected);
|
|
};
|
|
|
|
if buf.is_null() {
|
|
check_arg!(env, result);
|
|
unsafe {
|
|
*result = value.utf8_length(env.isolate());
|
|
}
|
|
} else if bufsize != 0 {
|
|
let buffer =
|
|
unsafe { std::slice::from_raw_parts_mut(buf as _, bufsize - 1) };
|
|
let copied = value.write_utf8(
|
|
&mut env.scope(),
|
|
buffer,
|
|
None,
|
|
v8::WriteOptions::REPLACE_INVALID_UTF8
|
|
| v8::WriteOptions::NO_NULL_TERMINATION,
|
|
);
|
|
unsafe {
|
|
buf.add(copied).write(0);
|
|
}
|
|
if !result.is_null() {
|
|
unsafe {
|
|
*result = copied;
|
|
}
|
|
}
|
|
} else if !result.is_null() {
|
|
unsafe {
|
|
*result = 0;
|
|
}
|
|
}
|
|
|
|
napi_clear_last_error(env_ptr)
|
|
}
|
|
|
|
#[napi_sym]
|
|
fn napi_get_value_string_utf16(
|
|
env_ptr: *mut Env,
|
|
value: napi_value,
|
|
buf: *mut u16,
|
|
bufsize: usize,
|
|
result: *mut usize,
|
|
) -> napi_status {
|
|
let env = check_env!(env_ptr);
|
|
check_arg!(env, value);
|
|
|
|
let Some(value) =
|
|
value.and_then(|v| v8::Local::<v8::String>::try_from(v).ok())
|
|
else {
|
|
return napi_set_last_error(env_ptr, napi_string_expected);
|
|
};
|
|
|
|
if buf.is_null() {
|
|
check_arg!(env, result);
|
|
unsafe {
|
|
*result = value.length();
|
|
}
|
|
} else if bufsize != 0 {
|
|
let buffer =
|
|
unsafe { std::slice::from_raw_parts_mut(buf as _, bufsize - 1) };
|
|
let copied = value.write(
|
|
&mut env.scope(),
|
|
buffer,
|
|
0,
|
|
v8::WriteOptions::NO_NULL_TERMINATION,
|
|
);
|
|
unsafe {
|
|
buf.add(copied).write(0);
|
|
}
|
|
if !result.is_null() {
|
|
unsafe {
|
|
*result = copied;
|
|
}
|
|
}
|
|
} else if !result.is_null() {
|
|
unsafe {
|
|
*result = 0;
|
|
}
|
|
}
|
|
|
|
napi_clear_last_error(env_ptr)
|
|
}
|
|
|
|
#[napi_sym]
|
|
fn napi_coerce_to_bool<'s>(
|
|
env: &'s mut Env,
|
|
value: napi_value,
|
|
result: *mut napi_value<'s>,
|
|
) -> napi_status {
|
|
check_arg!(env, value);
|
|
check_arg!(env, result);
|
|
|
|
let coerced = value.unwrap().to_boolean(&mut env.scope());
|
|
|
|
unsafe {
|
|
*result = coerced.into();
|
|
}
|
|
|
|
napi_ok
|
|
}
|
|
|
|
#[napi_sym]
|
|
fn napi_coerce_to_number<'s>(
|
|
env: &'s mut Env,
|
|
value: napi_value,
|
|
result: *mut napi_value<'s>,
|
|
) -> napi_status {
|
|
check_arg!(env, value);
|
|
check_arg!(env, result);
|
|
|
|
let Some(coerced) = value.unwrap().to_number(&mut env.scope()) else {
|
|
return napi_number_expected;
|
|
};
|
|
|
|
unsafe {
|
|
*result = coerced.into();
|
|
}
|
|
|
|
napi_ok
|
|
}
|
|
|
|
#[napi_sym]
|
|
fn napi_coerce_to_object<'s>(
|
|
env: &'s mut Env,
|
|
value: napi_value,
|
|
result: *mut napi_value<'s>,
|
|
) -> napi_status {
|
|
check_arg!(env, value);
|
|
check_arg!(env, result);
|
|
|
|
let Some(coerced) = value.unwrap().to_object(&mut env.scope()) else {
|
|
return napi_object_expected;
|
|
};
|
|
|
|
unsafe {
|
|
*result = coerced.into();
|
|
}
|
|
|
|
napi_ok
|
|
}
|
|
|
|
#[napi_sym]
|
|
fn napi_coerce_to_string<'s>(
|
|
env: &'s mut Env,
|
|
value: napi_value,
|
|
result: *mut napi_value<'s>,
|
|
) -> napi_status {
|
|
check_arg!(env, value);
|
|
check_arg!(env, result);
|
|
|
|
let Some(coerced) = value.unwrap().to_string(&mut env.scope()) else {
|
|
return napi_string_expected;
|
|
};
|
|
|
|
unsafe {
|
|
*result = coerced.into();
|
|
}
|
|
|
|
napi_ok
|
|
}
|
|
|
|
#[napi_sym]
|
|
fn napi_wrap(
|
|
env: &mut Env,
|
|
js_object: napi_value,
|
|
native_object: *mut c_void,
|
|
finalize_cb: Option<napi_finalize>,
|
|
finalize_hint: *mut c_void,
|
|
result: *mut napi_ref,
|
|
) -> napi_status {
|
|
check_arg!(env, js_object);
|
|
let env_ptr = env as *mut Env;
|
|
|
|
let Some(obj) =
|
|
js_object.and_then(|v| v8::Local::<v8::Object>::try_from(v).ok())
|
|
else {
|
|
return napi_invalid_arg;
|
|
};
|
|
|
|
let napi_wrap = v8::Local::new(&mut env.scope(), &env.shared().napi_wrap);
|
|
|
|
if obj
|
|
.has_private(&mut env.scope(), napi_wrap)
|
|
.unwrap_or(false)
|
|
{
|
|
return napi_invalid_arg;
|
|
}
|
|
|
|
if !result.is_null() {
|
|
check_arg!(env, finalize_cb);
|
|
}
|
|
|
|
let ownership = if result.is_null() {
|
|
ReferenceOwnership::Runtime
|
|
} else {
|
|
ReferenceOwnership::Userland
|
|
};
|
|
let reference = Reference::new(
|
|
env_ptr,
|
|
obj.into(),
|
|
0,
|
|
ownership,
|
|
finalize_cb,
|
|
native_object,
|
|
finalize_hint,
|
|
);
|
|
|
|
let reference = Reference::into_raw(reference) as *mut c_void;
|
|
|
|
if !result.is_null() {
|
|
check_arg!(env, finalize_cb);
|
|
unsafe {
|
|
*result = reference;
|
|
}
|
|
}
|
|
|
|
let external = v8::External::new(&mut env.scope(), reference);
|
|
assert!(obj
|
|
.set_private(&mut env.scope(), napi_wrap, external.into())
|
|
.unwrap());
|
|
|
|
napi_ok
|
|
}
|
|
|
|
fn unwrap(
|
|
env: &mut Env,
|
|
obj: napi_value,
|
|
result: *mut *mut c_void,
|
|
keep: bool,
|
|
) -> napi_status {
|
|
check_arg!(env, obj);
|
|
if keep {
|
|
check_arg!(env, result);
|
|
}
|
|
|
|
let Some(obj) = obj.and_then(|v| v8::Local::<v8::Object>::try_from(v).ok())
|
|
else {
|
|
return napi_invalid_arg;
|
|
};
|
|
|
|
let napi_wrap = v8::Local::new(&mut env.scope(), &env.shared().napi_wrap);
|
|
let Some(val) = obj.get_private(&mut env.scope(), napi_wrap) else {
|
|
return napi_invalid_arg;
|
|
};
|
|
|
|
let Ok(external) = v8::Local::<v8::External>::try_from(val) else {
|
|
return napi_invalid_arg;
|
|
};
|
|
|
|
let reference = external.value() as *mut Reference;
|
|
let reference = unsafe { &mut *reference };
|
|
|
|
if !result.is_null() {
|
|
unsafe {
|
|
*result = reference.finalize_data;
|
|
}
|
|
}
|
|
|
|
if !keep {
|
|
assert!(obj
|
|
.delete_private(&mut env.scope(), napi_wrap)
|
|
.unwrap_or(false));
|
|
unsafe { Reference::remove(reference) };
|
|
}
|
|
|
|
napi_ok
|
|
}
|
|
|
|
#[napi_sym]
|
|
fn napi_unwrap(
|
|
env: &mut Env,
|
|
obj: napi_value,
|
|
result: *mut *mut c_void,
|
|
) -> napi_status {
|
|
unwrap(env, obj, result, true)
|
|
}
|
|
|
|
#[napi_sym]
|
|
fn napi_remove_wrap(
|
|
env: &mut Env,
|
|
obj: napi_value,
|
|
result: *mut *mut c_void,
|
|
) -> napi_status {
|
|
unwrap(env, obj, result, false)
|
|
}
|
|
|
|
struct ExternalWrapper {
|
|
data: *mut c_void,
|
|
type_tag: Option<napi_type_tag>,
|
|
}
|
|
|
|
#[napi_sym]
|
|
fn napi_create_external<'s>(
|
|
env: &'s mut Env,
|
|
data: *mut c_void,
|
|
finalize_cb: Option<napi_finalize>,
|
|
finalize_hint: *mut c_void,
|
|
result: *mut napi_value<'s>,
|
|
) -> napi_status {
|
|
let env_ptr = env as *mut Env;
|
|
check_arg!(env, result);
|
|
|
|
let wrapper = Box::new(ExternalWrapper {
|
|
data,
|
|
type_tag: None,
|
|
});
|
|
|
|
let wrapper = Box::into_raw(wrapper);
|
|
let external = v8::External::new(&mut env.scope(), wrapper as _);
|
|
|
|
if let Some(finalize_cb) = finalize_cb {
|
|
Reference::into_raw(Reference::new(
|
|
env_ptr,
|
|
external.into(),
|
|
0,
|
|
ReferenceOwnership::Runtime,
|
|
Some(finalize_cb),
|
|
data,
|
|
finalize_hint,
|
|
));
|
|
}
|
|
|
|
unsafe {
|
|
*result = external.into();
|
|
}
|
|
|
|
napi_ok
|
|
}
|
|
|
|
#[napi_sym]
|
|
fn napi_type_tag_object(
|
|
env: &mut Env,
|
|
object_or_external: napi_value,
|
|
type_tag: *const napi_type_tag,
|
|
) -> napi_status {
|
|
check_arg!(env, object_or_external);
|
|
check_arg!(env, type_tag);
|
|
|
|
let val = object_or_external.unwrap();
|
|
|
|
if let Ok(external) = v8::Local::<v8::External>::try_from(val) {
|
|
let wrapper_ptr = external.value() as *mut ExternalWrapper;
|
|
let wrapper = unsafe { &mut *wrapper_ptr };
|
|
if wrapper.type_tag.is_some() {
|
|
return napi_invalid_arg;
|
|
}
|
|
wrapper.type_tag = Some(unsafe { *type_tag });
|
|
return napi_ok;
|
|
}
|
|
|
|
let Some(object) = val.to_object(&mut env.scope()) else {
|
|
return napi_object_expected;
|
|
};
|
|
|
|
let key = v8::Local::new(&mut env.scope(), &env.shared().type_tag);
|
|
|
|
if object.has_private(&mut env.scope(), key).unwrap_or(false) {
|
|
return napi_invalid_arg;
|
|
}
|
|
|
|
let slice = unsafe { std::slice::from_raw_parts(type_tag as *const u64, 2) };
|
|
let Some(tag) = v8::BigInt::new_from_words(&mut env.scope(), false, slice)
|
|
else {
|
|
return napi_generic_failure;
|
|
};
|
|
|
|
if !object
|
|
.set_private(&mut env.scope(), key, tag.into())
|
|
.unwrap_or(false)
|
|
{
|
|
return napi_generic_failure;
|
|
}
|
|
|
|
napi_ok
|
|
}
|
|
|
|
#[napi_sym]
|
|
fn napi_check_object_type_tag(
|
|
env: &mut Env,
|
|
object_or_external: napi_value,
|
|
type_tag: *const napi_type_tag,
|
|
result: *mut bool,
|
|
) -> napi_status {
|
|
check_arg!(env, object_or_external);
|
|
check_arg!(env, type_tag);
|
|
check_arg!(env, result);
|
|
|
|
let type_tag = unsafe { *type_tag };
|
|
|
|
let val = object_or_external.unwrap();
|
|
|
|
if let Ok(external) = v8::Local::<v8::External>::try_from(val) {
|
|
let wrapper_ptr = external.value() as *mut ExternalWrapper;
|
|
let wrapper = unsafe { &mut *wrapper_ptr };
|
|
unsafe {
|
|
*result = match wrapper.type_tag {
|
|
Some(t) => t == type_tag,
|
|
None => false,
|
|
};
|
|
};
|
|
return napi_ok;
|
|
}
|
|
|
|
let Some(object) = val.to_object(&mut env.scope()) else {
|
|
return napi_object_expected;
|
|
};
|
|
|
|
let key = v8::Local::new(&mut env.scope(), &env.shared().type_tag);
|
|
|
|
let Some(val) = object.get_private(&mut env.scope(), key) else {
|
|
return napi_generic_failure;
|
|
};
|
|
|
|
unsafe {
|
|
*result = false;
|
|
}
|
|
|
|
if let Ok(bigint) = v8::Local::<v8::BigInt>::try_from(val) {
|
|
let mut words = [0u64; 2];
|
|
let (sign, words) = bigint.to_words_array(&mut words);
|
|
if !sign {
|
|
let pass = if words.len() == 2 {
|
|
type_tag.lower == words[0] && type_tag.upper == words[1]
|
|
} else if words.len() == 1 {
|
|
type_tag.lower == words[0] && type_tag.upper == 0
|
|
} else if words.is_empty() {
|
|
type_tag.lower == 0 && type_tag.upper == 0
|
|
} else {
|
|
false
|
|
};
|
|
unsafe {
|
|
*result = pass;
|
|
}
|
|
}
|
|
}
|
|
|
|
napi_ok
|
|
}
|
|
|
|
#[napi_sym]
|
|
fn napi_get_value_external(
|
|
env: *mut Env,
|
|
value: napi_value,
|
|
result: *mut *mut c_void,
|
|
) -> napi_status {
|
|
let env = check_env!(env);
|
|
check_arg!(env, value);
|
|
check_arg!(env, result);
|
|
|
|
let Some(external) =
|
|
value.and_then(|v| v8::Local::<v8::External>::try_from(v).ok())
|
|
else {
|
|
return napi_set_last_error(env, napi_invalid_arg);
|
|
};
|
|
|
|
let wrapper_ptr = external.value() as *const ExternalWrapper;
|
|
let wrapper = unsafe { &*wrapper_ptr };
|
|
|
|
unsafe {
|
|
*result = wrapper.data;
|
|
}
|
|
|
|
napi_clear_last_error(env)
|
|
}
|
|
|
|
#[napi_sym]
|
|
fn napi_create_reference(
|
|
env: *mut Env,
|
|
value: napi_value,
|
|
initial_refcount: u32,
|
|
result: *mut napi_ref,
|
|
) -> napi_status {
|
|
let env = check_env!(env);
|
|
check_arg!(env, value);
|
|
check_arg!(env, result);
|
|
|
|
let value = value.unwrap();
|
|
|
|
let reference = Reference::new(
|
|
env,
|
|
value,
|
|
initial_refcount,
|
|
ReferenceOwnership::Userland,
|
|
None,
|
|
std::ptr::null_mut(),
|
|
std::ptr::null_mut(),
|
|
);
|
|
|
|
let ptr = Reference::into_raw(reference);
|
|
|
|
unsafe {
|
|
*result = ptr as _;
|
|
}
|
|
|
|
napi_clear_last_error(env)
|
|
}
|
|
|
|
#[napi_sym]
|
|
fn napi_delete_reference(env: *mut Env, ref_: napi_ref) -> napi_status {
|
|
let env = check_env!(env);
|
|
check_arg!(env, ref_);
|
|
|
|
let reference = unsafe { Reference::from_raw(ref_ as _) };
|
|
|
|
drop(reference);
|
|
|
|
napi_clear_last_error(env)
|
|
}
|
|
|
|
#[napi_sym]
|
|
fn napi_reference_ref(
|
|
env: *mut Env,
|
|
ref_: napi_ref,
|
|
result: *mut u32,
|
|
) -> napi_status {
|
|
let env = check_env!(env);
|
|
check_arg!(env, ref_);
|
|
|
|
let reference = unsafe { &mut *(ref_ as *mut Reference) };
|
|
|
|
let count = reference.ref_();
|
|
|
|
if !result.is_null() {
|
|
unsafe {
|
|
*result = count;
|
|
}
|
|
}
|
|
|
|
napi_clear_last_error(env)
|
|
}
|
|
|
|
#[napi_sym]
|
|
fn napi_reference_unref(
|
|
env: *mut Env,
|
|
ref_: napi_ref,
|
|
result: *mut u32,
|
|
) -> napi_status {
|
|
let env = check_env!(env);
|
|
check_arg!(env, ref_);
|
|
|
|
let reference = unsafe { &mut *(ref_ as *mut Reference) };
|
|
|
|
if reference.ref_count == 0 {
|
|
return napi_set_last_error(env, napi_generic_failure);
|
|
}
|
|
|
|
let count = reference.unref();
|
|
|
|
if !result.is_null() {
|
|
unsafe {
|
|
*result = count;
|
|
}
|
|
}
|
|
|
|
napi_clear_last_error(env)
|
|
}
|
|
|
|
#[napi_sym]
|
|
fn napi_get_reference_value(
|
|
env_ptr: *mut Env,
|
|
ref_: napi_ref,
|
|
result: *mut napi_value,
|
|
) -> napi_status {
|
|
let env = check_env!(env_ptr);
|
|
check_arg!(env, ref_);
|
|
check_arg!(env, result);
|
|
|
|
let reference = unsafe { &mut *(ref_ as *mut Reference) };
|
|
|
|
let value = match &reference.state {
|
|
ReferenceState::Strong(g) => Some(v8::Local::new(&mut env.scope(), g)),
|
|
ReferenceState::Weak(w) => w.to_local(&mut env.scope()),
|
|
};
|
|
|
|
unsafe {
|
|
*result = value.into();
|
|
}
|
|
|
|
napi_clear_last_error(env_ptr)
|
|
}
|
|
|
|
#[napi_sym]
|
|
fn napi_open_handle_scope(
|
|
env: *mut Env,
|
|
_result: *mut napi_handle_scope,
|
|
) -> napi_status {
|
|
let env = check_env!(env);
|
|
napi_clear_last_error(env)
|
|
}
|
|
|
|
#[napi_sym]
|
|
fn napi_close_handle_scope(
|
|
env: *mut Env,
|
|
_scope: napi_handle_scope,
|
|
) -> napi_status {
|
|
let env = check_env!(env);
|
|
napi_clear_last_error(env)
|
|
}
|
|
|
|
#[napi_sym]
|
|
fn napi_open_escapable_handle_scope(
|
|
env: *mut Env,
|
|
_result: *mut napi_escapable_handle_scope,
|
|
) -> napi_status {
|
|
let env = check_env!(env);
|
|
napi_clear_last_error(env)
|
|
}
|
|
|
|
#[napi_sym]
|
|
fn napi_close_escapable_handle_scope(
|
|
env: *mut Env,
|
|
_scope: napi_escapable_handle_scope,
|
|
) -> napi_status {
|
|
let env = check_env!(env);
|
|
napi_clear_last_error(env)
|
|
}
|
|
|
|
#[napi_sym]
|
|
fn napi_escape_handle<'s>(
|
|
env: *mut Env,
|
|
_scope: napi_escapable_handle_scope,
|
|
escapee: napi_value<'s>,
|
|
result: *mut napi_value<'s>,
|
|
) -> napi_status {
|
|
let env = check_env!(env);
|
|
|
|
unsafe {
|
|
*result = escapee;
|
|
}
|
|
|
|
napi_clear_last_error(env)
|
|
}
|
|
|
|
#[napi_sym]
|
|
fn napi_new_instance<'s>(
|
|
env: &'s mut Env,
|
|
constructor: napi_value,
|
|
argc: usize,
|
|
argv: *const napi_value,
|
|
result: *mut napi_value<'s>,
|
|
) -> napi_status {
|
|
check_arg!(env, constructor);
|
|
if argc > 0 {
|
|
check_arg!(env, argv);
|
|
}
|
|
check_arg!(env, result);
|
|
|
|
let Some(func) =
|
|
constructor.and_then(|v| v8::Local::<v8::Function>::try_from(v).ok())
|
|
else {
|
|
return napi_invalid_arg;
|
|
};
|
|
|
|
let args = if argc > 0 {
|
|
unsafe {
|
|
std::slice::from_raw_parts(argv as *mut v8::Local<v8::Value>, argc)
|
|
}
|
|
} else {
|
|
&[]
|
|
};
|
|
|
|
let Some(value) = func.new_instance(&mut env.scope(), args) else {
|
|
return napi_pending_exception;
|
|
};
|
|
|
|
unsafe {
|
|
*result = value.into();
|
|
}
|
|
|
|
napi_ok
|
|
}
|
|
|
|
#[napi_sym]
|
|
fn napi_instanceof(
|
|
env: &mut Env,
|
|
object: napi_value,
|
|
constructor: napi_value,
|
|
result: *mut bool,
|
|
) -> napi_status {
|
|
check_arg!(env, object);
|
|
check_arg!(env, result);
|
|
|
|
let Some(ctor) = constructor.and_then(|v| v.to_object(&mut env.scope()))
|
|
else {
|
|
return napi_object_expected;
|
|
};
|
|
|
|
if !ctor.is_function() {
|
|
unsafe {
|
|
napi_throw_type_error(
|
|
env,
|
|
c"ERR_NAPI_CONS_FUNCTION".as_ptr(),
|
|
c"Constructor must be a function".as_ptr(),
|
|
);
|
|
}
|
|
return napi_function_expected;
|
|
}
|
|
|
|
let Some(res) = object.unwrap().instance_of(&mut env.scope(), ctor) else {
|
|
return napi_generic_failure;
|
|
};
|
|
|
|
unsafe {
|
|
*result = res;
|
|
}
|
|
|
|
napi_ok
|
|
}
|
|
|
|
#[napi_sym]
|
|
fn napi_is_exception_pending(
|
|
env_ptr: *mut Env,
|
|
result: *mut bool,
|
|
) -> napi_status {
|
|
let env = check_env!(env_ptr);
|
|
check_arg!(env, result);
|
|
|
|
unsafe {
|
|
*result = env.last_exception.is_some();
|
|
}
|
|
|
|
napi_clear_last_error(env_ptr)
|
|
}
|
|
|
|
#[napi_sym]
|
|
fn napi_get_and_clear_last_exception(
|
|
env_ptr: *mut Env,
|
|
result: *mut napi_value,
|
|
) -> napi_status {
|
|
let env = check_env!(env_ptr);
|
|
check_arg!(env, result);
|
|
|
|
let ex: v8::Local<v8::Value> =
|
|
if let Some(last_exception) = env.last_exception.take() {
|
|
v8::Local::new(&mut env.scope(), last_exception)
|
|
} else {
|
|
v8::undefined(&mut env.scope()).into()
|
|
};
|
|
|
|
unsafe {
|
|
*result = ex.into();
|
|
}
|
|
|
|
napi_clear_last_error(env_ptr)
|
|
}
|
|
|
|
#[napi_sym]
|
|
fn napi_is_arraybuffer(
|
|
env: *mut Env,
|
|
value: napi_value,
|
|
result: *mut bool,
|
|
) -> napi_status {
|
|
let env = check_env!(env);
|
|
check_arg!(env, value);
|
|
check_arg!(env, result);
|
|
|
|
unsafe {
|
|
*result = value.unwrap().is_array_buffer();
|
|
}
|
|
|
|
napi_clear_last_error(env)
|
|
}
|
|
|
|
#[napi_sym]
|
|
fn napi_create_arraybuffer<'s>(
|
|
env: &'s mut Env,
|
|
len: usize,
|
|
data: *mut *mut c_void,
|
|
result: *mut napi_value<'s>,
|
|
) -> napi_status {
|
|
check_arg!(env, result);
|
|
|
|
let buffer = v8::ArrayBuffer::new(&mut env.scope(), len);
|
|
|
|
if !data.is_null() {
|
|
unsafe {
|
|
*data = get_array_buffer_ptr(buffer);
|
|
}
|
|
}
|
|
|
|
unsafe {
|
|
*result = buffer.into();
|
|
}
|
|
|
|
napi_ok
|
|
}
|
|
|
|
#[napi_sym]
|
|
fn napi_create_external_arraybuffer<'s>(
|
|
env: &'s mut Env,
|
|
data: *mut c_void,
|
|
byte_length: usize,
|
|
finalize_cb: napi_finalize,
|
|
finalize_hint: *mut c_void,
|
|
result: *mut napi_value<'s>,
|
|
) -> napi_status {
|
|
check_arg!(env, result);
|
|
|
|
let store = make_external_backing_store(
|
|
env,
|
|
data,
|
|
byte_length,
|
|
std::ptr::null_mut(),
|
|
finalize_cb,
|
|
finalize_hint,
|
|
);
|
|
|
|
let ab =
|
|
v8::ArrayBuffer::with_backing_store(&mut env.scope(), &store.make_shared());
|
|
let value: v8::Local<v8::Value> = ab.into();
|
|
|
|
unsafe {
|
|
*result = value.into();
|
|
}
|
|
|
|
napi_ok
|
|
}
|
|
|
|
#[napi_sym]
|
|
fn napi_get_arraybuffer_info(
|
|
env: *mut Env,
|
|
value: napi_value,
|
|
data: *mut *mut c_void,
|
|
length: *mut usize,
|
|
) -> napi_status {
|
|
let env = check_env!(env);
|
|
check_arg!(env, value);
|
|
|
|
let Some(buf) =
|
|
value.and_then(|v| v8::Local::<v8::ArrayBuffer>::try_from(v).ok())
|
|
else {
|
|
return napi_set_last_error(env, napi_invalid_arg);
|
|
};
|
|
|
|
if !data.is_null() {
|
|
unsafe {
|
|
*data = get_array_buffer_ptr(buf);
|
|
}
|
|
}
|
|
|
|
if !length.is_null() {
|
|
unsafe {
|
|
*length = buf.byte_length();
|
|
}
|
|
}
|
|
|
|
napi_ok
|
|
}
|
|
|
|
#[napi_sym]
|
|
fn napi_is_typedarray(
|
|
env: *mut Env,
|
|
value: napi_value,
|
|
result: *mut bool,
|
|
) -> napi_status {
|
|
let env = check_env!(env);
|
|
check_arg!(env, value);
|
|
check_arg!(env, result);
|
|
|
|
unsafe {
|
|
*result = value.unwrap().is_typed_array();
|
|
}
|
|
|
|
napi_ok
|
|
}
|
|
|
|
#[napi_sym]
|
|
fn napi_create_typedarray<'s>(
|
|
env: &'s mut Env,
|
|
ty: napi_typedarray_type,
|
|
length: usize,
|
|
arraybuffer: napi_value,
|
|
byte_offset: usize,
|
|
result: *mut napi_value<'s>,
|
|
) -> napi_status {
|
|
check_arg!(env, arraybuffer);
|
|
check_arg!(env, result);
|
|
|
|
let Some(ab) =
|
|
arraybuffer.and_then(|v| v8::Local::<v8::ArrayBuffer>::try_from(v).ok())
|
|
else {
|
|
return napi_arraybuffer_expected;
|
|
};
|
|
|
|
macro_rules! create {
|
|
($TypedArray:ident, $size_of_element:expr) => {{
|
|
let soe = $size_of_element;
|
|
if soe > 1 && byte_offset % soe != 0 {
|
|
let message = v8::String::new(
|
|
&mut env.scope(),
|
|
format!(
|
|
"start offset of {} should be multiple of {}",
|
|
stringify!($TypedArray),
|
|
soe
|
|
)
|
|
.as_str(),
|
|
)
|
|
.unwrap();
|
|
let exc = v8::Exception::range_error(&mut env.scope(), message);
|
|
env.scope().throw_exception(exc);
|
|
return napi_pending_exception;
|
|
}
|
|
|
|
if length * soe + byte_offset > ab.byte_length() {
|
|
let message =
|
|
v8::String::new(&mut env.scope(), "Invalid typed array length")
|
|
.unwrap();
|
|
let exc = v8::Exception::range_error(&mut env.scope(), message);
|
|
env.scope().throw_exception(exc);
|
|
return napi_pending_exception;
|
|
}
|
|
|
|
let Some(ta) =
|
|
v8::$TypedArray::new(&mut env.scope(), ab, byte_offset, length)
|
|
else {
|
|
return napi_generic_failure;
|
|
};
|
|
ta.into()
|
|
}};
|
|
}
|
|
|
|
let typedarray: v8::Local<v8::Value> = match ty {
|
|
napi_uint8_array => create!(Uint8Array, 1),
|
|
napi_uint8_clamped_array => create!(Uint8ClampedArray, 1),
|
|
napi_int8_array => create!(Int8Array, 1),
|
|
napi_uint16_array => create!(Uint16Array, 2),
|
|
napi_int16_array => create!(Int16Array, 2),
|
|
napi_uint32_array => create!(Uint32Array, 4),
|
|
napi_int32_array => create!(Int32Array, 4),
|
|
napi_float32_array => create!(Float32Array, 4),
|
|
napi_float64_array => create!(Float64Array, 8),
|
|
napi_bigint64_array => create!(BigInt64Array, 8),
|
|
napi_biguint64_array => create!(BigUint64Array, 8),
|
|
_ => {
|
|
return napi_invalid_arg;
|
|
}
|
|
};
|
|
|
|
unsafe {
|
|
*result = typedarray.into();
|
|
}
|
|
|
|
napi_ok
|
|
}
|
|
|
|
#[napi_sym]
|
|
fn napi_get_typedarray_info(
|
|
env_ptr: *mut Env,
|
|
typedarray: napi_value,
|
|
type_: *mut napi_typedarray_type,
|
|
length: *mut usize,
|
|
data: *mut *mut c_void,
|
|
arraybuffer: *mut napi_value,
|
|
byte_offset: *mut usize,
|
|
) -> napi_status {
|
|
let env = check_env!(env_ptr);
|
|
check_arg!(env, typedarray);
|
|
|
|
let Some(array) =
|
|
typedarray.and_then(|v| v8::Local::<v8::TypedArray>::try_from(v).ok())
|
|
else {
|
|
return napi_set_last_error(env_ptr, napi_invalid_arg);
|
|
};
|
|
|
|
if !type_.is_null() {
|
|
let tatype = if array.is_int8_array() {
|
|
napi_int8_array
|
|
} else if array.is_uint8_array() {
|
|
napi_uint8_array
|
|
} else if array.is_uint8_clamped_array() {
|
|
napi_uint8_clamped_array
|
|
} else if array.is_int16_array() {
|
|
napi_int16_array
|
|
} else if array.is_uint16_array() {
|
|
napi_uint16_array
|
|
} else if array.is_int32_array() {
|
|
napi_int32_array
|
|
} else if array.is_uint32_array() {
|
|
napi_uint32_array
|
|
} else if array.is_float32_array() {
|
|
napi_float32_array
|
|
} else if array.is_float64_array() {
|
|
napi_float64_array
|
|
} else if array.is_big_int64_array() {
|
|
napi_bigint64_array
|
|
} else if array.is_big_uint64_array() {
|
|
napi_biguint64_array
|
|
} else {
|
|
unreachable!();
|
|
};
|
|
|
|
unsafe {
|
|
*type_ = tatype;
|
|
}
|
|
}
|
|
|
|
if !length.is_null() {
|
|
unsafe {
|
|
*length = array.length();
|
|
}
|
|
}
|
|
|
|
if !data.is_null() {
|
|
unsafe {
|
|
*data = array.data();
|
|
}
|
|
}
|
|
|
|
if !arraybuffer.is_null() {
|
|
let buf = array.buffer(&mut env.scope()).unwrap();
|
|
unsafe {
|
|
*arraybuffer = buf.into();
|
|
}
|
|
}
|
|
|
|
if !byte_offset.is_null() {
|
|
unsafe {
|
|
*byte_offset = array.byte_offset();
|
|
}
|
|
}
|
|
|
|
napi_clear_last_error(env_ptr)
|
|
}
|
|
|
|
#[napi_sym]
|
|
fn napi_create_dataview<'s>(
|
|
env: &'s mut Env,
|
|
byte_length: usize,
|
|
arraybuffer: napi_value<'s>,
|
|
byte_offset: usize,
|
|
result: *mut napi_value<'s>,
|
|
) -> napi_status {
|
|
check_arg!(env, arraybuffer);
|
|
check_arg!(env, result);
|
|
|
|
let Some(buffer) =
|
|
arraybuffer.and_then(|v| v8::Local::<v8::ArrayBuffer>::try_from(v).ok())
|
|
else {
|
|
return napi_invalid_arg;
|
|
};
|
|
|
|
if byte_length + byte_offset > buffer.byte_length() {
|
|
unsafe {
|
|
return napi_throw_range_error(
|
|
env,
|
|
c"ERR_NAPI_INVALID_DATAVIEW_ARGS".as_ptr(),
|
|
c"byte_offset + byte_length should be less than or equal to the size in bytes of the array passed in".as_ptr(),
|
|
);
|
|
}
|
|
}
|
|
|
|
let dataview =
|
|
v8::DataView::new(&mut env.scope(), buffer, byte_offset, byte_length);
|
|
|
|
unsafe {
|
|
*result = dataview.into();
|
|
}
|
|
|
|
napi_ok
|
|
}
|
|
|
|
#[napi_sym]
|
|
fn napi_is_dataview(
|
|
env: *mut Env,
|
|
value: napi_value,
|
|
result: *mut bool,
|
|
) -> napi_status {
|
|
let env = check_env!(env);
|
|
check_arg!(env, value);
|
|
check_arg!(env, result);
|
|
|
|
unsafe {
|
|
*result = value.unwrap().is_data_view();
|
|
}
|
|
|
|
napi_clear_last_error(env)
|
|
}
|
|
|
|
#[napi_sym]
|
|
fn napi_get_dataview_info(
|
|
env_ptr: *mut Env,
|
|
dataview: napi_value,
|
|
byte_length: *mut usize,
|
|
data: *mut *mut c_void,
|
|
arraybuffer: *mut napi_value,
|
|
byte_offset: *mut usize,
|
|
) -> napi_status {
|
|
let env = check_env!(env_ptr);
|
|
check_arg!(env, dataview);
|
|
|
|
let Some(array) =
|
|
dataview.and_then(|v| v8::Local::<v8::DataView>::try_from(v).ok())
|
|
else {
|
|
return napi_invalid_arg;
|
|
};
|
|
|
|
if !byte_length.is_null() {
|
|
unsafe {
|
|
*byte_length = array.byte_length();
|
|
}
|
|
}
|
|
|
|
if !arraybuffer.is_null() {
|
|
let Some(buffer) = array.buffer(&mut env.scope()) else {
|
|
return napi_generic_failure;
|
|
};
|
|
|
|
unsafe {
|
|
*arraybuffer = buffer.into();
|
|
}
|
|
}
|
|
|
|
if !data.is_null() {
|
|
unsafe {
|
|
*data = array.data();
|
|
}
|
|
}
|
|
|
|
if !byte_offset.is_null() {
|
|
unsafe {
|
|
*byte_offset = array.byte_offset();
|
|
}
|
|
}
|
|
|
|
napi_clear_last_error(env_ptr)
|
|
}
|
|
|
|
#[napi_sym]
|
|
fn napi_get_version(env: *mut Env, result: *mut u32) -> napi_status {
|
|
let env = check_env!(env);
|
|
check_arg!(env, result);
|
|
|
|
unsafe {
|
|
*result = NAPI_VERSION;
|
|
}
|
|
|
|
napi_clear_last_error(env)
|
|
}
|
|
|
|
#[napi_sym]
|
|
fn napi_create_promise<'s>(
|
|
env: &'s mut Env,
|
|
deferred: *mut napi_deferred,
|
|
promise: *mut napi_value<'s>,
|
|
) -> napi_status {
|
|
check_arg!(env, deferred);
|
|
check_arg!(env, promise);
|
|
|
|
let resolver = v8::PromiseResolver::new(&mut env.scope()).unwrap();
|
|
|
|
let global = v8::Global::new(&mut env.scope(), resolver);
|
|
let global_ptr = global.into_raw().as_ptr() as napi_deferred;
|
|
|
|
let p = resolver.get_promise(&mut env.scope());
|
|
|
|
unsafe {
|
|
*deferred = global_ptr;
|
|
}
|
|
|
|
unsafe {
|
|
*promise = p.into();
|
|
}
|
|
|
|
napi_ok
|
|
}
|
|
|
|
#[napi_sym]
|
|
fn napi_resolve_deferred(
|
|
env: &mut Env,
|
|
deferred: napi_deferred,
|
|
result: napi_value,
|
|
) -> napi_status {
|
|
check_arg!(env, result);
|
|
check_arg!(env, deferred);
|
|
|
|
// Make sure microtasks don't run and call back into JS
|
|
env
|
|
.scope()
|
|
.set_microtasks_policy(v8::MicrotasksPolicy::Explicit);
|
|
|
|
let deferred_ptr =
|
|
unsafe { NonNull::new_unchecked(deferred as *mut v8::PromiseResolver) };
|
|
let global = unsafe { v8::Global::from_raw(env.isolate(), deferred_ptr) };
|
|
let resolver = v8::Local::new(&mut env.scope(), global);
|
|
|
|
let success = resolver
|
|
.resolve(&mut env.scope(), result.unwrap())
|
|
.unwrap_or(false);
|
|
|
|
// Restore policy
|
|
env
|
|
.scope()
|
|
.set_microtasks_policy(v8::MicrotasksPolicy::Auto);
|
|
|
|
if success {
|
|
napi_ok
|
|
} else {
|
|
napi_generic_failure
|
|
}
|
|
}
|
|
|
|
#[napi_sym]
|
|
fn napi_reject_deferred(
|
|
env: &mut Env,
|
|
deferred: napi_deferred,
|
|
result: napi_value,
|
|
) -> napi_status {
|
|
check_arg!(env, result);
|
|
check_arg!(env, deferred);
|
|
|
|
let deferred_ptr =
|
|
unsafe { NonNull::new_unchecked(deferred as *mut v8::PromiseResolver) };
|
|
let global = unsafe { v8::Global::from_raw(env.isolate(), deferred_ptr) };
|
|
let resolver = v8::Local::new(&mut env.scope(), global);
|
|
|
|
if !resolver
|
|
.reject(&mut env.scope(), result.unwrap())
|
|
.unwrap_or(false)
|
|
{
|
|
return napi_generic_failure;
|
|
}
|
|
|
|
napi_ok
|
|
}
|
|
|
|
#[napi_sym]
|
|
fn napi_is_promise(
|
|
env: *mut Env,
|
|
value: napi_value,
|
|
is_promise: *mut bool,
|
|
) -> napi_status {
|
|
let env = check_env!(env);
|
|
check_arg!(env, value);
|
|
check_arg!(env, is_promise);
|
|
|
|
unsafe {
|
|
*is_promise = value.unwrap().is_promise();
|
|
}
|
|
|
|
napi_clear_last_error(env)
|
|
}
|
|
|
|
#[napi_sym]
|
|
fn napi_create_date<'s>(
|
|
env: &'s mut Env,
|
|
time: f64,
|
|
result: *mut napi_value<'s>,
|
|
) -> napi_status {
|
|
check_arg!(env, result);
|
|
|
|
let Some(date) = v8::Date::new(&mut env.scope(), time) else {
|
|
return napi_generic_failure;
|
|
};
|
|
|
|
unsafe {
|
|
*result = date.into();
|
|
}
|
|
|
|
napi_ok
|
|
}
|
|
|
|
#[napi_sym]
|
|
fn napi_is_date(
|
|
env: *mut Env,
|
|
value: napi_value,
|
|
is_date: *mut bool,
|
|
) -> napi_status {
|
|
let env = check_env!(env);
|
|
check_arg!(env, value);
|
|
check_arg!(env, is_date);
|
|
|
|
unsafe {
|
|
*is_date = value.unwrap().is_date();
|
|
}
|
|
|
|
napi_clear_last_error(env)
|
|
}
|
|
|
|
#[napi_sym]
|
|
fn napi_get_date_value(
|
|
env: &mut Env,
|
|
value: napi_value,
|
|
result: *mut f64,
|
|
) -> napi_status {
|
|
check_arg!(env, result);
|
|
|
|
let Some(date) = value.and_then(|v| v8::Local::<v8::Date>::try_from(v).ok())
|
|
else {
|
|
return napi_date_expected;
|
|
};
|
|
|
|
unsafe {
|
|
*result = date.value_of();
|
|
}
|
|
|
|
napi_ok
|
|
}
|
|
|
|
#[napi_sym]
|
|
fn napi_run_script<'s>(
|
|
env: &'s mut Env,
|
|
script: napi_value,
|
|
result: *mut napi_value<'s>,
|
|
) -> napi_status {
|
|
check_arg!(env, script);
|
|
check_arg!(env, result);
|
|
|
|
let Some(script) =
|
|
script.and_then(|v| v8::Local::<v8::String>::try_from(v).ok())
|
|
else {
|
|
return napi_string_expected;
|
|
};
|
|
|
|
let Some(script) = v8::Script::compile(&mut env.scope(), script, None) else {
|
|
return napi_generic_failure;
|
|
};
|
|
|
|
let Some(rv) = script.run(&mut env.scope()) else {
|
|
return napi_generic_failure;
|
|
};
|
|
|
|
unsafe {
|
|
*result = rv.into();
|
|
}
|
|
|
|
napi_ok
|
|
}
|
|
|
|
#[napi_sym]
|
|
fn napi_add_finalizer(
|
|
env_ptr: *mut Env,
|
|
value: napi_value,
|
|
finalize_data: *mut c_void,
|
|
finalize_cb: Option<napi_finalize>,
|
|
finalize_hint: *mut c_void,
|
|
result: *mut napi_ref,
|
|
) -> napi_status {
|
|
let env = check_env!(env_ptr);
|
|
check_arg!(env, value);
|
|
check_arg!(env, finalize_cb);
|
|
|
|
let Some(value) =
|
|
value.and_then(|v| v8::Local::<v8::Object>::try_from(v).ok())
|
|
else {
|
|
return napi_set_last_error(env, napi_invalid_arg);
|
|
};
|
|
|
|
let ownership = if result.is_null() {
|
|
ReferenceOwnership::Runtime
|
|
} else {
|
|
ReferenceOwnership::Userland
|
|
};
|
|
let reference = Reference::new(
|
|
env,
|
|
value.into(),
|
|
0,
|
|
ownership,
|
|
finalize_cb,
|
|
finalize_data,
|
|
finalize_hint,
|
|
);
|
|
|
|
if !result.is_null() {
|
|
unsafe {
|
|
*result = Reference::into_raw(reference) as _;
|
|
}
|
|
}
|
|
|
|
napi_clear_last_error(env_ptr)
|
|
}
|
|
|
|
#[napi_sym]
|
|
fn node_api_post_finalizer(
|
|
env: *mut Env,
|
|
_finalize_cb: napi_finalize,
|
|
_finalize_data: *mut c_void,
|
|
_finalize_hint: *mut c_void,
|
|
) -> napi_status {
|
|
napi_clear_last_error(env)
|
|
}
|
|
|
|
#[napi_sym]
|
|
fn napi_adjust_external_memory(
|
|
env: *mut Env,
|
|
change_in_bytes: i64,
|
|
adjusted_value: *mut i64,
|
|
) -> napi_status {
|
|
let env = check_env!(env);
|
|
check_arg!(env, adjusted_value);
|
|
|
|
unsafe {
|
|
*adjusted_value = env
|
|
.isolate()
|
|
.adjust_amount_of_external_allocated_memory(change_in_bytes);
|
|
}
|
|
|
|
napi_clear_last_error(env)
|
|
}
|
|
|
|
#[napi_sym]
|
|
fn napi_set_instance_data(
|
|
env: *mut Env,
|
|
data: *mut c_void,
|
|
finalize_cb: Option<napi_finalize>,
|
|
finalize_hint: *mut c_void,
|
|
) -> napi_status {
|
|
let env = check_env!(env);
|
|
|
|
env.shared_mut().instance_data = Some(InstanceData {
|
|
data,
|
|
finalize_cb,
|
|
finalize_hint,
|
|
});
|
|
|
|
napi_clear_last_error(env)
|
|
}
|
|
|
|
#[napi_sym]
|
|
fn napi_get_instance_data(
|
|
env: *mut Env,
|
|
data: *mut *mut c_void,
|
|
) -> napi_status {
|
|
let env = check_env!(env);
|
|
check_arg!(env, data);
|
|
|
|
let instance_data = match &env.shared().instance_data {
|
|
Some(v) => v.data,
|
|
None => std::ptr::null_mut(),
|
|
};
|
|
|
|
unsafe { *data = instance_data };
|
|
|
|
napi_clear_last_error(env)
|
|
}
|
|
|
|
#[napi_sym]
|
|
fn napi_detach_arraybuffer(env: *mut Env, value: napi_value) -> napi_status {
|
|
let env = check_env!(env);
|
|
check_arg!(env, value);
|
|
|
|
let Some(ab) =
|
|
value.and_then(|v| v8::Local::<v8::ArrayBuffer>::try_from(v).ok())
|
|
else {
|
|
return napi_set_last_error(env, napi_arraybuffer_expected);
|
|
};
|
|
|
|
if !ab.is_detachable() {
|
|
return napi_set_last_error(env, napi_detachable_arraybuffer_expected);
|
|
}
|
|
|
|
// Expected to crash for None.
|
|
ab.detach(None).unwrap();
|
|
|
|
napi_clear_last_error(env);
|
|
napi_ok
|
|
}
|
|
|
|
#[napi_sym]
|
|
fn napi_is_detached_arraybuffer(
|
|
env_ptr: *mut Env,
|
|
arraybuffer: napi_value,
|
|
result: *mut bool,
|
|
) -> napi_status {
|
|
let env = check_env!(env_ptr);
|
|
check_arg!(env, arraybuffer);
|
|
check_arg!(env, result);
|
|
|
|
let is_detached = match arraybuffer
|
|
.and_then(|v| v8::Local::<v8::ArrayBuffer>::try_from(v).ok())
|
|
{
|
|
Some(ab) => ab.was_detached(),
|
|
None => false,
|
|
};
|
|
|
|
unsafe {
|
|
*result = is_detached;
|
|
}
|
|
|
|
napi_clear_last_error(env)
|
|
}
|