Merge pull request #368 from napi-rs/reference
feat(napi): implement create_reference/get_reference_value
This commit is contained in:
commit
7a7013b6de
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]
|
#[inline]
|
||||||
pub fn create_external<T: 'static>(&self, native_object: T) -> Result<JsExternal> {
|
pub fn create_external<T: 'static>(&self, native_object: T) -> Result<JsExternal> {
|
||||||
let mut object_value = ptr::null_mut();
|
let mut object_value = ptr::null_mut();
|
||||||
|
|
|
@ -55,7 +55,7 @@ pub use string::*;
|
||||||
pub(crate) use tagged_object::TaggedObject;
|
pub(crate) use tagged_object::TaggedObject;
|
||||||
pub use undefined::JsUndefined;
|
pub use undefined::JsUndefined;
|
||||||
pub(crate) use value::Value;
|
pub(crate) use value::Value;
|
||||||
pub use value_ref::Ref;
|
pub use value_ref::*;
|
||||||
pub use value_type::ValueType;
|
pub use value_type::ValueType;
|
||||||
|
|
||||||
// Value types
|
// Value types
|
||||||
|
|
|
@ -6,8 +6,8 @@ use crate::{sys, Env, Result};
|
||||||
|
|
||||||
pub struct Ref<T> {
|
pub struct Ref<T> {
|
||||||
pub(crate) raw_ref: sys::napi_ref,
|
pub(crate) raw_ref: sys::napi_ref,
|
||||||
count: u32,
|
pub(crate) count: u32,
|
||||||
inner: T,
|
pub(crate) inner: T,
|
||||||
}
|
}
|
||||||
|
|
||||||
unsafe impl<T> Send for Ref<T> {}
|
unsafe impl<T> Send for Ref<T> {}
|
||||||
|
|
|
@ -12,7 +12,7 @@ test('should get js function called from a thread', async (t) => {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
await new Promise((resolve, reject) => {
|
await new Promise<void>((resolve, reject) => {
|
||||||
bindings.testThreadsafeFunction((...args: any[]) => {
|
bindings.testThreadsafeFunction((...args: any[]) => {
|
||||||
called += 1
|
called += 1
|
||||||
try {
|
try {
|
||||||
|
@ -55,3 +55,20 @@ test('should return Closing while calling aborted tsfn', (t) => {
|
||||||
}
|
}
|
||||||
t.notThrows(() => bindings.testCallAbortedThreadsafeFunction(() => {}))
|
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",
|
"testCallAbortedThreadsafeFunction",
|
||||||
test_call_aborted_threadsafe_function,
|
test_call_aborted_threadsafe_function,
|
||||||
)?;
|
)?;
|
||||||
|
exports.create_named_method("testTsfnWithRef", test_tsfn_with_ref)?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,7 +3,8 @@ use std::thread;
|
||||||
|
|
||||||
use napi::{
|
use napi::{
|
||||||
threadsafe_function::{ThreadSafeCallContext, ThreadsafeFunctionCallMode},
|
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;
|
use tokio;
|
||||||
|
|
||||||
|
@ -148,3 +149,25 @@ pub fn test_tokio_readfile(ctx: CallContext) -> Result<JsUndefined> {
|
||||||
|
|
||||||
ctx.env.get_undefined()
|
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