feat(napi): implement external value
This commit is contained in:
parent
d1a5f84aa0
commit
bdfb1506a2
12 changed files with 191 additions and 14 deletions
crates
|
@ -83,6 +83,7 @@ static KNOWN_TYPES: Lazy<HashMap<&'static str, &'static str>> = Lazy::new(|| {
|
|||
("AbortSignal", "AbortSignal"),
|
||||
("JsFunction", "(...args: any[]) => any"),
|
||||
("JsGlobal", "typeof global"),
|
||||
("External", "ExternalObject<{}>"),
|
||||
]);
|
||||
|
||||
map
|
||||
|
|
|
@ -8,6 +8,7 @@ mod bigint;
|
|||
mod boolean;
|
||||
mod buffer;
|
||||
mod either;
|
||||
mod external;
|
||||
mod function;
|
||||
mod map;
|
||||
mod nil;
|
||||
|
@ -26,6 +27,7 @@ pub use array::*;
|
|||
pub use bigint::*;
|
||||
pub use buffer::*;
|
||||
pub use either::*;
|
||||
pub use external::*;
|
||||
#[cfg(feature = "napi4")]
|
||||
pub use function::*;
|
||||
pub use nil::*;
|
||||
|
|
110
crates/napi/src/bindgen_runtime/js_values/external.rs
Normal file
110
crates/napi/src/bindgen_runtime/js_values/external.rs
Normal file
|
@ -0,0 +1,110 @@
|
|||
use std::any::TypeId;
|
||||
|
||||
use crate::{check_status, Error, Status, TaggedObject};
|
||||
|
||||
use super::{FromNapiValue, ToNapiValue};
|
||||
|
||||
pub struct External<T: 'static> {
|
||||
obj: *mut TaggedObject<T>,
|
||||
size_hint: usize,
|
||||
pub adjusted_size: i64,
|
||||
}
|
||||
|
||||
impl<T: 'static> External<T> {
|
||||
pub fn new(value: T) -> Self {
|
||||
Self {
|
||||
obj: Box::into_raw(Box::new(TaggedObject::new(value))),
|
||||
size_hint: 0,
|
||||
adjusted_size: 0,
|
||||
}
|
||||
}
|
||||
|
||||
/// `size_hint` is a value to tell Node.js GC how much memory is used by this `External` object.
|
||||
///
|
||||
/// If getting the exact `size_hint` is difficult, you can provide an approximate value, it's only effect to the GC.
|
||||
///
|
||||
/// If your `External` object is not effect to GC, you can use `External::new` instead.
|
||||
pub fn new_with_size_hint(value: T, size_hint: usize) -> Self {
|
||||
Self {
|
||||
obj: Box::into_raw(Box::new(TaggedObject::new(value))),
|
||||
size_hint,
|
||||
adjusted_size: 0,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: 'static> FromNapiValue for External<T> {
|
||||
unsafe fn from_napi_value(
|
||||
env: napi_sys::napi_env,
|
||||
napi_val: napi_sys::napi_value,
|
||||
) -> crate::Result<Self> {
|
||||
let mut unknown_tagged_object = std::ptr::null_mut();
|
||||
check_status!(
|
||||
napi_sys::napi_get_value_external(env, napi_val, &mut unknown_tagged_object),
|
||||
"Failed to get external value"
|
||||
)?;
|
||||
|
||||
let type_id = unknown_tagged_object as *const TypeId;
|
||||
if *type_id == TypeId::of::<T>() {
|
||||
let tagged_object = unknown_tagged_object as *mut TaggedObject<T>;
|
||||
Ok(Self {
|
||||
obj: tagged_object,
|
||||
size_hint: 0,
|
||||
adjusted_size: 0,
|
||||
})
|
||||
} else {
|
||||
Err(Error::new(
|
||||
Status::InvalidArg,
|
||||
"T on `get_value_external` is not the type of wrapped object".to_owned(),
|
||||
))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: 'static> AsRef<T> for External<T> {
|
||||
fn as_ref(&self) -> &T {
|
||||
unsafe { Box::leak(Box::from_raw(self.obj)).object.as_ref().unwrap() }
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: 'static> AsMut<T> for External<T> {
|
||||
fn as_mut(&mut self) -> &mut T {
|
||||
unsafe { Box::leak(Box::from_raw(self.obj)).object.as_mut().unwrap() }
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: 'static> ToNapiValue for External<T> {
|
||||
unsafe fn to_napi_value(
|
||||
env: napi_sys::napi_env,
|
||||
mut val: Self,
|
||||
) -> crate::Result<napi_sys::napi_value> {
|
||||
let mut napi_value = std::ptr::null_mut();
|
||||
check_status!(
|
||||
napi_sys::napi_create_external(
|
||||
env,
|
||||
val.obj as *mut _,
|
||||
Some(crate::raw_finalize::<T>),
|
||||
Box::into_raw(Box::new(Some(val.size_hint as i64))) as *mut _,
|
||||
&mut napi_value
|
||||
),
|
||||
"Create external value failed"
|
||||
)?;
|
||||
|
||||
let mut adjusted_external_memory_size = std::mem::MaybeUninit::new(0);
|
||||
|
||||
if val.size_hint != 0 {
|
||||
check_status!(
|
||||
napi_sys::napi_adjust_external_memory(
|
||||
env,
|
||||
val.size_hint as i64,
|
||||
adjusted_external_memory_size.as_mut_ptr()
|
||||
),
|
||||
"Adjust external memory failed"
|
||||
)?;
|
||||
};
|
||||
|
||||
val.adjusted_size = adjusted_external_memory_size.assume_init();
|
||||
|
||||
Ok(napi_value)
|
||||
}
|
||||
}
|
|
@ -879,10 +879,12 @@ impl Env {
|
|||
)
|
||||
})?;
|
||||
if let Some(changed) = size_hint {
|
||||
let mut adjusted_value = 0i64;
|
||||
check_status!(unsafe {
|
||||
sys::napi_adjust_external_memory(self.0, changed, &mut adjusted_value)
|
||||
})?;
|
||||
if changed != 0 {
|
||||
let mut adjusted_value = 0i64;
|
||||
check_status!(unsafe {
|
||||
sys::napi_adjust_external_memory(self.0, changed, &mut adjusted_value)
|
||||
})?;
|
||||
}
|
||||
};
|
||||
Ok(unsafe { JsExternal::from_raw_unchecked(self.0, object_value) })
|
||||
}
|
||||
|
@ -1260,12 +1262,14 @@ pub(crate) unsafe extern "C" fn raw_finalize<T>(
|
|||
if !finalize_hint.is_null() {
|
||||
let size_hint = *Box::from_raw(finalize_hint as *mut Option<i64>);
|
||||
if let Some(changed) = size_hint {
|
||||
let mut adjusted = 0i64;
|
||||
let status = sys::napi_adjust_external_memory(env, -changed, &mut adjusted);
|
||||
debug_assert!(
|
||||
status == sys::Status::napi_ok,
|
||||
"Calling napi_adjust_external_memory failed"
|
||||
);
|
||||
if changed != 0 {
|
||||
let mut adjusted = 0i64;
|
||||
let status = sys::napi_adjust_external_memory(env, -changed, &mut adjusted);
|
||||
debug_assert!(
|
||||
status == sys::Status::napi_ok,
|
||||
"Calling napi_adjust_external_memory failed"
|
||||
);
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue