Merge pull request #368 from napi-rs/reference

feat(napi): implement create_reference/get_reference_value
This commit is contained in:
LongYinan 2020-12-18 11:03:23 +08:00 committed by GitHub
commit 7a7013b6de
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
6 changed files with 93 additions and 5 deletions

View file

@ -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();

View file

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

View file

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

View file

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

View file

@ -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(())
} }

View file

@ -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()
}