feat(napi): implement create_reference/get_reference_value
This commit is contained in:
parent
08e5804c3b
commit
bb5e1f4286
6 changed files with 93 additions and 5 deletions
|
@ -637,6 +637,53 @@ impl Env {
|
|||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
/// This API create a new reference with the specified reference count to the Object passed in.
|
||||
pub fn create_reference<T>(&self, value: T) -> Result<Ref<()>>
|
||||
where
|
||||
T: NapiValue,
|
||||
{
|
||||
let mut raw_ref = ptr::null_mut();
|
||||
let initial_ref_count = 1;
|
||||
check_status!(unsafe {
|
||||
sys::napi_create_reference(self.0, value.raw(), initial_ref_count, &mut raw_ref)
|
||||
})?;
|
||||
Ok(Ref {
|
||||
raw_ref,
|
||||
count: 1,
|
||||
inner: (),
|
||||
})
|
||||
}
|
||||
|
||||
#[inline]
|
||||
/// Get reference value from `Ref` with type check
|
||||
/// Return error if the type of `reference` provided is mismatched with `T`
|
||||
pub fn get_reference_value<T>(&self, reference: &Ref<()>) -> Result<T>
|
||||
where
|
||||
T: NapiValue,
|
||||
{
|
||||
let mut js_value = ptr::null_mut();
|
||||
check_status!(unsafe {
|
||||
sys::napi_get_reference_value(self.0, reference.raw_ref, &mut js_value)
|
||||
})?;
|
||||
unsafe { T::from_raw(self.0, js_value) }
|
||||
}
|
||||
|
||||
#[inline]
|
||||
/// Get reference value from `Ref` without type check
|
||||
/// Using this API if you are sure the type of `T` is matched with provided `Ref<()>`.
|
||||
/// If type mismatched, calling `T::method` would return `Err`.
|
||||
pub fn get_reference_value_unchecked<T>(&self, reference: &Ref<()>) -> Result<T>
|
||||
where
|
||||
T: NapiValue,
|
||||
{
|
||||
let mut js_value = ptr::null_mut();
|
||||
check_status!(unsafe {
|
||||
sys::napi_get_reference_value(self.0, reference.raw_ref, &mut js_value)
|
||||
})?;
|
||||
Ok(unsafe { T::from_raw_unchecked(self.0, js_value) })
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn create_external<T: 'static>(&self, native_object: T) -> Result<JsExternal> {
|
||||
let mut object_value = ptr::null_mut();
|
||||
|
|
|
@ -55,7 +55,7 @@ pub use string::*;
|
|||
pub(crate) use tagged_object::TaggedObject;
|
||||
pub use undefined::JsUndefined;
|
||||
pub(crate) use value::Value;
|
||||
pub use value_ref::Ref;
|
||||
pub use value_ref::*;
|
||||
pub use value_type::ValueType;
|
||||
|
||||
// Value types
|
||||
|
|
|
@ -6,8 +6,8 @@ use crate::{sys, Env, Result};
|
|||
|
||||
pub struct Ref<T> {
|
||||
pub(crate) raw_ref: sys::napi_ref,
|
||||
count: u32,
|
||||
inner: T,
|
||||
pub(crate) count: u32,
|
||||
pub(crate) inner: T,
|
||||
}
|
||||
|
||||
unsafe impl<T> Send for Ref<T> {}
|
||||
|
|
|
@ -12,7 +12,7 @@ test('should get js function called from a thread', async (t) => {
|
|||
return
|
||||
}
|
||||
|
||||
await new Promise((resolve, reject) => {
|
||||
await new Promise<void>((resolve, reject) => {
|
||||
bindings.testThreadsafeFunction((...args: any[]) => {
|
||||
called += 1
|
||||
try {
|
||||
|
@ -55,3 +55,20 @@ test('should return Closing while calling aborted tsfn', (t) => {
|
|||
}
|
||||
t.notThrows(() => bindings.testCallAbortedThreadsafeFunction(() => {}))
|
||||
})
|
||||
|
||||
test('should work with napi ref', (t) => {
|
||||
if (napiVersion < 4) {
|
||||
t.is(bindings.testTsfnWithRef, undefined)
|
||||
} else {
|
||||
const obj = {
|
||||
foo: Symbol(),
|
||||
}
|
||||
return new Promise<void>((resolve) => {
|
||||
bindings.testTsfnWithRef((err: Error | null, returnObj: any) => {
|
||||
t.is(err, null)
|
||||
t.is(obj, returnObj)
|
||||
resolve()
|
||||
}, obj)
|
||||
})
|
||||
}
|
||||
})
|
||||
|
|
|
@ -20,5 +20,6 @@ pub fn register_js(exports: &mut JsObject) -> Result<()> {
|
|||
"testCallAbortedThreadsafeFunction",
|
||||
test_call_aborted_threadsafe_function,
|
||||
)?;
|
||||
exports.create_named_method("testTsfnWithRef", test_tsfn_with_ref)?;
|
||||
Ok(())
|
||||
}
|
||||
|
|
|
@ -3,7 +3,8 @@ use std::thread;
|
|||
|
||||
use napi::{
|
||||
threadsafe_function::{ThreadSafeCallContext, ThreadsafeFunctionCallMode},
|
||||
CallContext, Error, JsBoolean, JsFunction, JsNumber, JsString, JsUndefined, Result, Status,
|
||||
CallContext, Error, JsBoolean, JsFunction, JsNumber, JsObject, JsString, JsUndefined, Ref,
|
||||
Result, Status,
|
||||
};
|
||||
use tokio;
|
||||
|
||||
|
@ -148,3 +149,25 @@ pub fn test_tokio_readfile(ctx: CallContext) -> Result<JsUndefined> {
|
|||
|
||||
ctx.env.get_undefined()
|
||||
}
|
||||
|
||||
#[js_function(2)]
|
||||
pub fn test_tsfn_with_ref(ctx: CallContext) -> Result<JsUndefined> {
|
||||
let callback = ctx.get::<JsFunction>(0)?;
|
||||
let options = ctx.get::<JsObject>(1)?;
|
||||
let options_ref = ctx.env.create_reference(options)?;
|
||||
let tsfn =
|
||||
ctx
|
||||
.env
|
||||
.create_threadsafe_function(&callback, 0, |ctx: ThreadSafeCallContext<Ref<()>>| {
|
||||
ctx
|
||||
.env
|
||||
.get_reference_value_unchecked::<JsObject>(&ctx.value)
|
||||
.and_then(|obj| ctx.value.unref(ctx.env).map(|_| vec![obj]))
|
||||
})?;
|
||||
|
||||
thread::spawn(move || {
|
||||
tsfn.call(Ok(options_ref), ThreadsafeFunctionCallMode::Blocking);
|
||||
});
|
||||
|
||||
ctx.env.get_undefined()
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue