feat(napi): implement external value

This commit is contained in:
LongYinan 2021-11-21 16:11:15 +08:00
parent d1a5f84aa0
commit bdfb1506a2
No known key found for this signature in database
GPG key ID: C3666B7FC82ADAD7
12 changed files with 191 additions and 14 deletions
crates
backend/src
napi/src
bindgen_runtime
env.rs

View file

@ -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

View file

@ -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::*;

View 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)
}
}

View file

@ -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"
);
}
};
}
}