chore: specified dependencies versions

This commit is contained in:
LongYinan 2021-11-02 20:36:34 +08:00
parent 9570899025
commit d36c303dec
No known key found for this signature in database
GPG key ID: C3666B7FC82ADAD7
29 changed files with 543 additions and 85 deletions

View file

@ -1,5 +1,5 @@
use proc_macro2::Ident; use proc_macro2::Ident;
use syn::Attribute; use syn::{Attribute, Type};
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub struct NapiFn { pub struct NapiFn {
@ -76,6 +76,7 @@ pub struct NapiImpl {
pub name: Ident, pub name: Ident,
pub js_name: String, pub js_name: String,
pub items: Vec<NapiFn>, pub items: Vec<NapiFn>,
pub task_output_type: Option<Type>,
} }
#[derive(Debug, Clone)] #[derive(Debug, Clone)]

View file

@ -76,6 +76,13 @@ impl TryToTokens for NapiStruct {
impl NapiStruct { impl NapiStruct {
fn gen_helper_mod(&self) -> TokenStream { fn gen_helper_mod(&self) -> TokenStream {
if crate::typegen::r#struct::TASK_STRUCTS.with(|t| {
println!("{:?}", t);
t.borrow().get(&self.name.to_string()).is_some()
}) {
return quote! {};
}
let mod_name = Ident::new( let mod_name = Ident::new(
&format!("__napi_helper__{}", self.name.to_string()), &format!("__napi_helper__{}", self.name.to_string()),
Span::call_site(), Span::call_site(),

View file

@ -1,6 +1,6 @@
mod r#enum; mod r#enum;
mod r#fn; mod r#fn;
mod r#struct; pub(crate) mod r#struct;
use std::collections::HashMap; use std::collections::HashMap;
@ -35,6 +35,7 @@ static KNOWN_TYPES: Lazy<HashMap<&'static str, &'static str>> = Lazy::new(|| {
("i16", "number"), ("i16", "number"),
("i32", "number"), ("i32", "number"),
("i64", "number"), ("i64", "number"),
("f64", "number"),
("u8", "number"), ("u8", "number"),
("u16", "number"), ("u16", "number"),
("u32", "number"), ("u32", "number"),
@ -54,6 +55,9 @@ static KNOWN_TYPES: Lazy<HashMap<&'static str, &'static str>> = Lazy::new(|| {
("Value", "any"), ("Value", "any"),
("Map", "Record<string, any>"), ("Map", "Record<string, any>"),
("HashMap", "Record<{}, {}>"), ("HashMap", "Record<{}, {}>"),
("ArrayBuffer", "ArrayBuffer"),
("DataView", "DataView"),
("Date", "Date"),
("Buffer", "Buffer"), ("Buffer", "Buffer"),
// TODO: Vec<u8> should be Buffer, now is Array<number> // TODO: Vec<u8> should be Buffer, now is Array<number>
("Vec", "Array<{}>"), ("Vec", "Array<{}>"),
@ -63,6 +67,12 @@ static KNOWN_TYPES: Lazy<HashMap<&'static str, &'static str>> = Lazy::new(|| {
("Either3", "{} | {} | {}"), ("Either3", "{} | {} | {}"),
("Either4", "{} | {} | {} | {}"), ("Either4", "{} | {} | {} | {}"),
("Either5", "{} | {} | {} | {} | {}"), ("Either5", "{} | {} | {} | {} | {}"),
("unknown", "unknown"),
("null", "null"),
("symbol", "symbol"),
("external", "object"),
("AsyncTaskAbortController", "AbortController"),
("Function", "(...args: any[]) => any"),
]); ]);
map map
@ -124,6 +134,15 @@ pub fn ty_to_ts_type(ty: &Type, is_return_ty: bool) -> String {
if rust_ty == "Result" && is_return_ty { if rust_ty == "Result" && is_return_ty {
ts_ty = Some(args.first().unwrap().to_owned()); ts_ty = Some(args.first().unwrap().to_owned());
} else if rust_ty == "AsyncTask" {
ts_ty = r#struct::TASK_STRUCTS.with(|t| {
let output_type = args.first().unwrap().to_owned();
if let Some(o) = t.borrow().get(&output_type) {
Some(format!("Promise<{}>", o))
} else {
Some("Promise<unknown>".to_owned())
}
});
} else if let Some(&known_ty) = KNOWN_TYPES.get(rust_ty.as_str()) { } else if let Some(&known_ty) = KNOWN_TYPES.get(rust_ty.as_str()) {
if known_ty.contains("{}") { if known_ty.contains("{}") {
ts_ty = Some(fill_ty(known_ty, args)); ts_ty = Some(fill_ty(known_ty, args));

View file

@ -1,6 +1,13 @@
use std::cell::RefCell;
use std::collections::HashMap;
use super::{ToTypeDef, TypeDef}; use super::{ToTypeDef, TypeDef};
use crate::{ty_to_ts_type, NapiImpl, NapiStruct, NapiStructKind}; use crate::{ty_to_ts_type, NapiImpl, NapiStruct, NapiStructKind};
thread_local! {
pub(crate) static TASK_STRUCTS: RefCell<HashMap<String, String>> = Default::default();
}
impl ToTypeDef for NapiStruct { impl ToTypeDef for NapiStruct {
fn to_type_def(&self) -> TypeDef { fn to_type_def(&self) -> TypeDef {
TypeDef { TypeDef {
@ -17,6 +24,12 @@ impl ToTypeDef for NapiStruct {
impl ToTypeDef for NapiImpl { impl ToTypeDef for NapiImpl {
fn to_type_def(&self) -> TypeDef { fn to_type_def(&self) -> TypeDef {
if let Some(output_type) = &self.task_output_type {
TASK_STRUCTS.with(|t| {
t.borrow_mut()
.insert(self.js_name.clone(), ty_to_ts_type(output_type, false));
});
}
TypeDef { TypeDef {
kind: "impl".to_owned(), kind: "impl".to_owned(),
name: self.js_name.to_owned(), name: self.js_name.to_owned(),

View file

@ -18,7 +18,7 @@ type-def = ["napi-derive-backend/type-def"]
[dependencies] [dependencies]
convert_case = "0.4" convert_case = "0.4"
napi-derive-backend = {version = "1", path = "../backend"} napi-derive-backend = {version = "1.0.2", path = "../backend"}
proc-macro2 = "1.0" proc-macro2 = "1.0"
quote = "1.0" quote = "1.0"
syn = {version = "1.0", features = ["fold", "full", "extra-traits"]} syn = {version = "1.0", features = ["fold", "full", "extra-traits"]}

View file

@ -15,7 +15,7 @@ use napi_derive_backend::{
use proc_macro2::{Ident, TokenStream, TokenTree}; use proc_macro2::{Ident, TokenStream, TokenTree};
use quote::ToTokens; use quote::ToTokens;
use syn::parse::{Parse, ParseStream, Result as SynResult}; use syn::parse::{Parse, ParseStream, Result as SynResult};
use syn::{Attribute, Signature, Visibility}; use syn::{Attribute, Signature, Type, Visibility};
use crate::parser::attrs::{check_recorded_struct_for_impl, record_struct}; use crate::parser::attrs::{check_recorded_struct_for_impl, record_struct};
@ -755,43 +755,52 @@ impl ConvertToAST for syn::ItemImpl {
let mut struct_js_name = struct_name.to_string(); let mut struct_js_name = struct_name.to_string();
let mut items = vec![]; let mut items = vec![];
let mut task_output_type = None;
for item in self.items.iter_mut() { for item in self.items.iter_mut() {
let method = match item { if let Some(method) = match item {
syn::ImplItem::Method(m) => m, syn::ImplItem::Method(m) => Some(m),
syn::ImplItem::Type(m) => {
if m.ident == *"JsValue" {
if let Type::Path(_) = &m.ty {
task_output_type = Some(m.ty.clone());
}
}
None
}
_ => { _ => {
bail_span!(item, "unsupported impl item in #[napi]") bail_span!(item, "unsupported impl item in #[napi]")
} }
}; } {
let opts = BindgenAttrs::find(&mut method.attrs)?; let opts = BindgenAttrs::find(&mut method.attrs)?;
// it'd better only care methods decorated with `#[napi]` attribute // it'd better only care methods decorated with `#[napi]` attribute
if !opts.exists { if !opts.exists {
continue; continue;
}
if opts.constructor().is_some() {
struct_js_name = check_recorded_struct_for_impl(&struct_name, &opts)?;
}
let vis = method.vis.clone();
match &vis {
Visibility::Public(_) => {}
_ => {
bail_span!(method.sig.ident, "only pub method supported by #[napi].",);
} }
if opts.constructor().is_some() {
struct_js_name = check_recorded_struct_for_impl(&struct_name, &opts)?;
}
let vis = method.vis.clone();
match &vis {
Visibility::Public(_) => {}
_ => {
bail_span!(method.sig.ident, "only pub method supported by #[napi].",);
}
}
let func = napi_fn_from_decl(
method.sig.clone(),
&opts,
method.attrs.clone(),
vis,
Some(&struct_name),
)?;
items.push(func);
} }
let func = napi_fn_from_decl(
method.sig.clone(),
&opts,
method.attrs.clone(),
vis,
Some(&struct_name),
)?;
items.push(func);
} }
Ok(Napi { Ok(Napi {
@ -800,6 +809,7 @@ impl ConvertToAST for syn::ItemImpl {
name: struct_name, name: struct_name,
js_name: struct_js_name, js_name: struct_js_name,
items, items,
task_output_type,
}), }),
}) })
} }

View file

@ -28,7 +28,7 @@ tokio_rt = ["tokio", "once_cell", "napi4"]
[dependencies] [dependencies]
ctor = "0.1" ctor = "0.1"
napi-sys = {version = "1", path = "../sys"} napi-sys = {version = "1.1.2", path = "../sys"}
[target.'cfg(windows)'.dependencies] [target.'cfg(windows)'.dependencies]
winapi = {version = "0.3.9", features = ["winuser", "minwindef", "ntdef", "libloaderapi"]} winapi = {version = "0.3.9", features = ["winuser", "minwindef", "ntdef", "libloaderapi"]}

View file

@ -1,11 +1,12 @@
use std::mem; use std::mem;
use std::os::raw::{c_char, c_void}; use std::os::raw::c_void;
use std::ptr; use std::ptr;
use std::sync::atomic::{AtomicBool, Ordering};
use std::{ffi::CString, rc::Rc};
use crate::{ use crate::{
check_status, bindgen_runtime::ToNapiValue, check_status, js_values::NapiValue, sys, Env, JsError, JsObject,
js_values::{NapiRaw, NapiValue}, Result, Task,
sys, Env, JsError, JsObject, Result, Task,
}; };
struct AsyncWork<T: Task> { struct AsyncWork<T: Task> {
@ -13,46 +14,53 @@ struct AsyncWork<T: Task> {
deferred: sys::napi_deferred, deferred: sys::napi_deferred,
value: Result<mem::MaybeUninit<T::Output>>, value: Result<mem::MaybeUninit<T::Output>>,
napi_async_work: sys::napi_async_work, napi_async_work: sys::napi_async_work,
abort: Rc<AtomicBool>,
} }
pub struct AsyncWorkPromise<'env> { pub struct AsyncWorkPromise {
napi_async_work: sys::napi_async_work, pub(crate) napi_async_work: sys::napi_async_work,
raw_promise: sys::napi_value, raw_promise: sys::napi_value,
env: &'env Env, pub(crate) deferred: sys::napi_deferred,
env: sys::napi_env,
// share with AsyncWork
pub(crate) abort: Rc<AtomicBool>,
} }
impl<'env> AsyncWorkPromise<'env> { impl AsyncWorkPromise {
pub fn promise_object(&self) -> JsObject { pub fn promise_object(&self) -> JsObject {
unsafe { JsObject::from_raw_unchecked(self.env.0, self.raw_promise) } unsafe { JsObject::from_raw_unchecked(self.env, self.raw_promise) }
} }
pub fn cancel(self) -> Result<()> { pub fn cancel(&self) -> Result<()> {
check_status!(unsafe { sys::napi_cancel_async_work(self.env.0, self.napi_async_work) }) // must be happened in the main thread, relaxed is enough
self.abort.store(true, Ordering::Relaxed);
check_status!(unsafe { sys::napi_cancel_async_work(self.env, self.napi_async_work) })
} }
} }
pub fn run<T: Task>(env: &Env, task: T) -> Result<AsyncWorkPromise<'_>> { pub fn run<T: Task>(
env: sys::napi_env,
task: T,
abort_status: Option<Rc<AtomicBool>>,
) -> Result<AsyncWorkPromise> {
let mut raw_resource = ptr::null_mut(); let mut raw_resource = ptr::null_mut();
check_status!(unsafe { sys::napi_create_object(env.0, &mut raw_resource) })?; check_status!(unsafe { sys::napi_create_object(env, &mut raw_resource) })?;
let mut raw_promise = ptr::null_mut(); let mut raw_promise = ptr::null_mut();
let mut deferred = ptr::null_mut(); let mut deferred = ptr::null_mut();
check_status!(unsafe { sys::napi_create_promise(env.0, &mut deferred, &mut raw_promise) })?; check_status!(unsafe { sys::napi_create_promise(env, &mut deferred, &mut raw_promise) })?;
let mut raw_name = ptr::null_mut(); let task_abort = abort_status.unwrap_or_else(|| Rc::new(AtomicBool::new(false)));
let s = "napi_rs_async_work";
check_status!(unsafe {
sys::napi_create_string_utf8(env.0, s.as_ptr() as *const c_char, s.len(), &mut raw_name)
})?;
let result = Box::leak(Box::new(AsyncWork { let result = Box::leak(Box::new(AsyncWork {
inner_task: task, inner_task: task,
deferred, deferred,
value: Ok(mem::MaybeUninit::zeroed()), value: Ok(mem::MaybeUninit::zeroed()),
napi_async_work: ptr::null_mut(), napi_async_work: ptr::null_mut(),
abort: task_abort.clone(),
})); }));
check_status!(unsafe { check_status!(unsafe {
sys::napi_create_async_work( sys::napi_create_async_work(
env.0, env,
raw_resource, raw_resource,
raw_name, CString::new("napi_rs_async_work")?.as_ptr() as *mut _,
Some(execute::<T> as unsafe extern "C" fn(env: sys::napi_env, data: *mut c_void)), Some(execute::<T> as unsafe extern "C" fn(env: sys::napi_env, data: *mut c_void)),
Some( Some(
complete::<T> complete::<T>
@ -62,11 +70,13 @@ pub fn run<T: Task>(env: &Env, task: T) -> Result<AsyncWorkPromise<'_>> {
&mut result.napi_async_work, &mut result.napi_async_work,
) )
})?; })?;
check_status!(unsafe { sys::napi_queue_async_work(env.0, result.napi_async_work) })?; check_status!(unsafe { sys::napi_queue_async_work(env, result.napi_async_work) })?;
Ok(AsyncWorkPromise { Ok(AsyncWorkPromise {
napi_async_work: result.napi_async_work, napi_async_work: result.napi_async_work,
raw_promise, raw_promise,
deferred,
env, env,
abort: task_abort,
}) })
} }
@ -78,11 +88,16 @@ unsafe impl<T: Task> Sync for AsyncWork<T> {}
/// So it actually could do nothing here, because `execute` function is called in the other thread mostly. /// So it actually could do nothing here, because `execute` function is called in the other thread mostly.
unsafe extern "C" fn execute<T: Task>(_env: sys::napi_env, data: *mut c_void) { unsafe extern "C" fn execute<T: Task>(_env: sys::napi_env, data: *mut c_void) {
let mut work = Box::from_raw(data as *mut AsyncWork<T>); let mut work = Box::from_raw(data as *mut AsyncWork<T>);
if work.abort.load(Ordering::Relaxed) {
return;
}
let _ = mem::replace( let _ = mem::replace(
&mut work.value, &mut work.value,
work.inner_task.compute().map(mem::MaybeUninit::new), work.inner_task.compute().map(mem::MaybeUninit::new),
); );
Box::leak(work); if !work.abort.load(Ordering::Relaxed) {
Box::leak(work);
}
} }
unsafe extern "C" fn complete<T: Task>( unsafe extern "C" fn complete<T: Task>(
@ -101,10 +116,13 @@ unsafe extern "C" fn complete<T: Task>(
} }
Err(e) => work.inner_task.reject(Env::from_raw(env), e), Err(e) => work.inner_task.reject(Env::from_raw(env), e),
}; };
match check_status!(status).and_then(move |_| value) { match check_status!(status)
.and_then(move |_| value)
.and_then(|v| ToNapiValue::to_napi_value(env, v))
{
Ok(v) => { Ok(v) => {
let status = sys::napi_resolve_deferred(env, deferred, v.raw()); let status = sys::napi_resolve_deferred(env, deferred, v);
debug_assert!(status == sys::Status::napi_ok, "Reject promise failed"); debug_assert!(status == sys::Status::napi_ok, "Resolve promise failed");
} }
Err(e) => { Err(e) => {
let status = sys::napi_reject_deferred(env, deferred, JsError::from(e).into_value(env)); let status = sys::napi_reject_deferred(env, deferred, JsError::from(e).into_value(env));

View file

@ -1,4 +1,4 @@
use crate::{check_status, sys, Error, JsUnknown, Result, Status, ValueType}; use crate::{check_status, sys, Error, JsUnknown, NapiRaw, NapiValue, Result, Status, ValueType};
use std::ptr; use std::ptr;
mod array; mod array;
@ -12,6 +12,7 @@ mod object;
#[cfg(feature = "serde-json")] #[cfg(feature = "serde-json")]
mod serde; mod serde;
mod string; mod string;
mod task;
pub use array::*; pub use array::*;
pub use buffer::*; pub use buffer::*;
@ -19,6 +20,7 @@ pub use either::*;
pub use nil::*; pub use nil::*;
pub use object::*; pub use object::*;
pub use string::*; pub use string::*;
pub use task::*;
#[cfg(feature = "latin1")] #[cfg(feature = "latin1")]
pub use string::latin1_string::*; pub use string::latin1_string::*;
@ -36,9 +38,25 @@ pub trait ToNapiValue {
unsafe fn to_napi_value(env: sys::napi_env, val: Self) -> Result<sys::napi_value>; unsafe fn to_napi_value(env: sys::napi_env, val: Self) -> Result<sys::napi_value>;
} }
impl ToNapiValue for JsUnknown { impl TypeName for JsUnknown {
fn type_name() -> &'static str {
"unknown"
}
fn value_type() -> ValueType {
ValueType::Unknown
}
}
impl<T: NapiRaw> ToNapiValue for T {
unsafe fn to_napi_value(_env: sys::napi_env, val: Self) -> Result<sys::napi_value> { unsafe fn to_napi_value(_env: sys::napi_env, val: Self) -> Result<sys::napi_value> {
Ok(val.0.value) Ok(NapiRaw::raw(&val))
}
}
impl<T: NapiValue> FromNapiValue for T {
unsafe fn from_napi_value(env: sys::napi_env, napi_val: sys::napi_value) -> Result<Self> {
Ok(T::from_raw_unchecked(env, napi_val))
} }
} }
@ -49,16 +67,6 @@ pub trait FromNapiValue: Sized {
unsafe fn from_napi_value(env: sys::napi_env, napi_val: sys::napi_value) -> Result<Self>; unsafe fn from_napi_value(env: sys::napi_env, napi_val: sys::napi_value) -> Result<Self>;
} }
impl FromNapiValue for JsUnknown {
unsafe fn from_napi_value(env: sys::napi_env, napi_val: sys::napi_value) -> Result<Self> {
Ok(JsUnknown(crate::Value {
env,
value: napi_val,
value_type: crate::ValueType::Unknown,
}))
}
}
pub trait FromNapiRef { pub trait FromNapiRef {
/// # Safety /// # Safety
/// ///

View file

@ -0,0 +1,159 @@
use std::ffi::c_void;
use std::ptr;
use std::rc::Rc;
use std::sync::atomic::{AtomicBool, AtomicPtr, Ordering};
use super::{FromNapiValue, ToNapiValue, TypeName};
use crate::{async_work, check_status, Env, Error, JsError, JsObject, NapiValue, Status, Task};
pub struct AsyncTask<T: Task> {
inner: T,
abort_controller: Option<AsyncTaskAbortController>,
}
impl<T: Task> TypeName for T {
fn type_name() -> &'static str {
"AsyncTask"
}
fn value_type() -> crate::ValueType {
crate::ValueType::Object
}
}
impl<T: Task> AsyncTask<T> {
pub fn new(task: T) -> Self {
Self {
inner: task,
abort_controller: None,
}
}
pub fn with_abort_controller(task: T, abort_controller: AsyncTaskAbortController) -> Self {
Self {
inner: task,
abort_controller: Some(abort_controller),
}
}
}
/// https://developer.mozilla.org/zh-CN/docs/Web/API/AbortController
pub struct AsyncTaskAbortController {
raw_work: Rc<AtomicPtr<napi_sys::napi_async_work__>>,
raw_deferred: Rc<AtomicPtr<napi_sys::napi_deferred__>>,
abort: Rc<AtomicBool>,
}
impl FromNapiValue for AsyncTaskAbortController {
unsafe fn from_napi_value(
env: napi_sys::napi_env,
napi_val: napi_sys::napi_value,
) -> crate::Result<Self> {
let raw_abort_controller = JsObject::from_raw_unchecked(env, napi_val);
let mut signal = raw_abort_controller.get_named_property::<JsObject>("signal")?;
let async_work_inner: Rc<AtomicPtr<napi_sys::napi_async_work__>> =
Rc::new(AtomicPtr::new(ptr::null_mut()));
let raw_promise: Rc<AtomicPtr<napi_sys::napi_deferred__>> =
Rc::new(AtomicPtr::new(ptr::null_mut()));
let abort_status = Rc::new(AtomicBool::new(false));
let abort_controller = AsyncTaskAbortController {
raw_work: async_work_inner.clone(),
raw_deferred: raw_promise.clone(),
abort: abort_status.clone(),
};
let js_env = Env::from_raw(env);
check_status!(napi_sys::napi_wrap(
env,
signal.0.value,
Box::into_raw(Box::new(abort_controller)) as *mut _,
Some(async_task_abort_controller_finalize),
ptr::null_mut(),
ptr::null_mut(),
))?;
signal.set_named_property("onabort", js_env.create_function("onabort", on_abort)?)?;
Ok(AsyncTaskAbortController {
raw_work: async_work_inner,
raw_deferred: raw_promise,
abort: abort_status,
})
}
}
extern "C" fn on_abort(
env: napi_sys::napi_env,
callback_info: napi_sys::napi_callback_info,
) -> napi_sys::napi_value {
let mut this = ptr::null_mut();
unsafe {
let get_cb_info_status = napi_sys::napi_get_cb_info(
env,
callback_info,
&mut 0,
ptr::null_mut(),
&mut this,
ptr::null_mut(),
);
debug_assert_eq!(
get_cb_info_status,
napi_sys::Status::napi_ok,
"{}",
"Get callback info in AbortController abort callback failed"
);
let mut async_task = ptr::null_mut();
let status = napi_sys::napi_unwrap(env, this, &mut async_task);
debug_assert_eq!(
status,
napi_sys::Status::napi_ok,
"{}",
"Unwrap async_task from AbortSignal failed"
);
let abort_controller = Box::leak(Box::from_raw(async_task as *mut AsyncTaskAbortController));
let raw_async_work = abort_controller.raw_work.load(Ordering::Relaxed);
let deferred = abort_controller.raw_deferred.load(Ordering::Relaxed);
// abort function must be called from JavaScript main thread, so Relaxed Ordering is ok.
abort_controller.abort.store(true, Ordering::Relaxed);
napi_sys::napi_cancel_async_work(env, raw_async_work);
// napi_sys::napi_delete_async_work(env, raw_async_work);
let abort_error = Error::new(Status::Cancelled, "AbortError".to_owned());
let reject_status =
napi_sys::napi_reject_deferred(env, deferred, JsError::from(abort_error).into_value(env));
debug_assert_eq!(
reject_status,
napi_sys::Status::napi_ok,
"{}",
"Reject AbortError failed"
);
let mut undefined = ptr::null_mut();
napi_sys::napi_get_undefined(env, &mut undefined);
undefined
}
}
impl<T: Task> ToNapiValue for AsyncTask<T> {
unsafe fn to_napi_value(
env: napi_sys::napi_env,
val: Self,
) -> crate::Result<napi_sys::napi_value> {
if let Some(abort_controller) = val.abort_controller {
let async_promise = async_work::run(env, val.inner, Some(abort_controller.abort.clone()))?;
abort_controller
.raw_work
.store(async_promise.napi_async_work, Ordering::Relaxed);
abort_controller
.raw_deferred
.store(async_promise.deferred, Ordering::Relaxed);
Ok(async_promise.promise_object().0.value)
} else {
let async_promise = async_work::run(env, val.inner, None)?;
Ok(async_promise.promise_object().0.value)
}
}
}
unsafe extern "C" fn async_task_abort_controller_finalize(
_env: napi_sys::napi_env,
finalize_data: *mut c_void,
_finalize_hint: *mut c_void,
) {
Box::from_raw(finalize_data as *mut AsyncTaskAbortController);
}

View file

@ -485,7 +485,7 @@ impl Env {
#[cfg(feature = "napi5")] #[cfg(feature = "napi5")]
pub fn create_function_from_closure<R, F>(&self, name: &str, callback: F) -> Result<JsFunction> pub fn create_function_from_closure<R, F>(&self, name: &str, callback: F) -> Result<JsFunction>
where where
F: 'static + Send + Sync + Fn(crate::CallContext<'_>) -> Result<R>, F: 'static + Fn(crate::CallContext<'_>) -> Result<R>,
R: NapiRaw, R: NapiRaw,
{ {
use crate::CallContext; use crate::CallContext;
@ -925,7 +925,7 @@ impl Env {
/// Run [Task](./trait.Task.html) in libuv thread pool, return [AsyncWorkPromise](./struct.AsyncWorkPromise.html) /// Run [Task](./trait.Task.html) in libuv thread pool, return [AsyncWorkPromise](./struct.AsyncWorkPromise.html)
pub fn spawn<T: 'static + Task>(&self, task: T) -> Result<AsyncWorkPromise> { pub fn spawn<T: 'static + Task>(&self, task: T) -> Result<AsyncWorkPromise> {
async_work::run(self, task) async_work::run(self.0, task, None)
} }
pub fn run_in_scope<T, F>(&self, executor: F) -> Result<T> pub fn run_in_scope<T, F>(&self, executor: F) -> Result<T>
@ -1253,7 +1253,7 @@ unsafe extern "C" fn drop_buffer(
mem::drop(Vec::from_raw_parts(finalize_data as *mut u8, length, cap)); mem::drop(Vec::from_raw_parts(finalize_data as *mut u8, length, cap));
} }
unsafe extern "C" fn raw_finalize<T>( pub(crate) unsafe extern "C" fn raw_finalize<T>(
env: sys::napi_env, env: sys::napi_env,
finalize_data: *mut c_void, finalize_data: *mut c_void,
finalize_hint: *mut c_void, finalize_hint: *mut c_void,

View file

@ -3,10 +3,21 @@ use std::os::raw::c_void;
use std::ptr; use std::ptr;
use std::slice; use std::slice;
use crate::bindgen_runtime::TypeName;
use crate::{check_status, sys, JsUnknown, NapiValue, Ref, Result, Value, ValueType}; use crate::{check_status, sys, JsUnknown, NapiValue, Ref, Result, Value, ValueType};
pub struct JsArrayBuffer(pub(crate) Value); pub struct JsArrayBuffer(pub(crate) Value);
impl TypeName for JsArrayBuffer {
fn type_name() -> &'static str {
"ArrayBuffer"
}
fn value_type() -> ValueType {
ValueType::Object
}
}
pub struct JsArrayBufferValue { pub struct JsArrayBufferValue {
pub(crate) value: JsArrayBuffer, pub(crate) value: JsArrayBuffer,
len: usize, len: usize,
@ -15,6 +26,16 @@ pub struct JsArrayBufferValue {
pub struct JsTypedArray(pub(crate) Value); pub struct JsTypedArray(pub(crate) Value);
impl TypeName for JsTypedArray {
fn type_name() -> &'static str {
"TypedArray"
}
fn value_type() -> ValueType {
ValueType::Object
}
}
pub struct JsTypedArrayValue { pub struct JsTypedArrayValue {
pub arraybuffer: JsArrayBuffer, pub arraybuffer: JsArrayBuffer,
data: *mut c_void, data: *mut c_void,
@ -25,6 +46,16 @@ pub struct JsTypedArrayValue {
pub struct JsDataView(pub(crate) Value); pub struct JsDataView(pub(crate) Value);
impl TypeName for JsDataView {
fn type_name() -> &'static str {
"DataView"
}
fn value_type() -> ValueType {
ValueType::Object
}
}
pub struct JsDataViewValue { pub struct JsDataViewValue {
pub arraybuffer: JsArrayBuffer, pub arraybuffer: JsArrayBuffer,
_data: *mut c_void, _data: *mut c_void,

View file

@ -2,7 +2,7 @@ use std::convert::TryFrom;
use std::ptr; use std::ptr;
use super::*; use super::*;
use crate::{check_status, sys, Result}; use crate::{bindgen_runtime::TypeName, check_status, sys, Result};
#[derive(Clone, Copy)] #[derive(Clone, Copy)]
pub struct JsBigint { pub struct JsBigint {
@ -10,6 +10,16 @@ pub struct JsBigint {
pub word_count: usize, pub word_count: usize,
} }
impl TypeName for JsBigint {
fn type_name() -> &'static str {
"BigInt"
}
fn value_type() -> ValueType {
ValueType::Bigint
}
}
impl JsBigint { impl JsBigint {
pub(crate) fn from_raw_unchecked( pub(crate) fn from_raw_unchecked(
env: sys::napi_env, env: sys::napi_env,

View file

@ -1,12 +1,23 @@
use std::convert::TryFrom; use std::convert::TryFrom;
use super::Value; use super::Value;
use crate::check_status; use crate::bindgen_runtime::TypeName;
use crate::{check_status, ValueType};
use crate::{sys, Error, Result}; use crate::{sys, Error, Result};
#[derive(Clone, Copy)] #[derive(Clone, Copy)]
pub struct JsBoolean(pub(crate) Value); pub struct JsBoolean(pub(crate) Value);
impl TypeName for JsBoolean {
fn type_name() -> &'static str {
"bool"
}
fn value_type() -> crate::ValueType {
ValueType::Boolean
}
}
impl JsBoolean { impl JsBoolean {
pub fn get_value(&self) -> Result<bool> { pub fn get_value(&self) -> Result<bool> {
let mut result = false; let mut result = false;

View file

@ -5,11 +5,22 @@ use std::ptr;
use super::Value; use super::Value;
#[cfg(feature = "serde-json")] #[cfg(feature = "serde-json")]
use super::ValueType; use super::ValueType;
use crate::bindgen_runtime::TypeName;
use crate::check_status; use crate::check_status;
use crate::{sys, JsUnknown, NapiValue, Ref, Result}; use crate::{sys, JsUnknown, NapiValue, Ref, Result};
pub struct JsBuffer(pub(crate) Value); pub struct JsBuffer(pub(crate) Value);
impl TypeName for JsBuffer {
fn type_name() -> &'static str {
"Buffer"
}
fn value_type() -> ValueType {
ValueType::Object
}
}
pub struct JsBufferValue { pub struct JsBufferValue {
pub(crate) value: JsBuffer, pub(crate) value: JsBuffer,
data: mem::ManuallyDrop<Vec<u8>>, data: mem::ManuallyDrop<Vec<u8>>,

View file

@ -1,8 +1,18 @@
use super::check_status; use super::check_status;
use crate::{sys, Result, Value}; use crate::{bindgen_runtime::TypeName, sys, Result, Value, ValueType};
pub struct JsDate(pub(crate) Value); pub struct JsDate(pub(crate) Value);
impl TypeName for JsDate {
fn type_name() -> &'static str {
"Date"
}
fn value_type() -> crate::ValueType {
ValueType::Object
}
}
impl JsDate { impl JsDate {
pub fn value_of(&self) -> Result<f64> { pub fn value_of(&self) -> Result<f64> {
let mut timestamp: f64 = 0.0; let mut timestamp: f64 = 0.0;

View file

@ -1,11 +1,22 @@
use std::ptr; use std::ptr;
use super::Value; use super::Value;
use crate::check_status; use crate::bindgen_runtime::TypeName;
use crate::{check_status, ValueType};
use crate::{sys, Env, Error, JsObject, JsUnknown, NapiRaw, NapiValue, Result, Status}; use crate::{sys, Env, Error, JsObject, JsUnknown, NapiRaw, NapiValue, Result, Status};
pub struct JsFunction(pub(crate) Value); pub struct JsFunction(pub(crate) Value);
impl TypeName for JsFunction {
fn type_name() -> &'static str {
"Function"
}
fn value_type() -> crate::ValueType {
ValueType::Function
}
}
/// See [Working with JavaScript Functions](https://nodejs.org/api/n-api.html#n_api_working_with_javascript_functions). /// See [Working with JavaScript Functions](https://nodejs.org/api/n-api.html#n_api_working_with_javascript_functions).
/// ///
/// Example: /// Example:

View file

@ -2,7 +2,9 @@ use std::convert::TryFrom;
use std::ffi::CString; use std::ffi::CString;
use std::ptr; use std::ptr;
use crate::{check_status, sys, type_of, Callback, Error, Result, Status, ValueType}; use crate::{
bindgen_runtime::TypeName, check_status, sys, type_of, Callback, Error, Result, Status, ValueType,
};
#[cfg(feature = "serde-json")] #[cfg(feature = "serde-json")]
mod de; mod de;
@ -60,11 +62,41 @@ pub struct JsUnknown(pub(crate) Value);
#[derive(Clone, Copy)] #[derive(Clone, Copy)]
pub struct JsNull(pub(crate) Value); pub struct JsNull(pub(crate) Value);
impl TypeName for JsNull {
fn type_name() -> &'static str {
"null"
}
fn value_type() -> ValueType {
ValueType::Null
}
}
#[derive(Clone, Copy)] #[derive(Clone, Copy)]
pub struct JsSymbol(pub(crate) Value); pub struct JsSymbol(pub(crate) Value);
impl TypeName for JsSymbol {
fn type_name() -> &'static str {
"symbol"
}
fn value_type() -> ValueType {
ValueType::Symbol
}
}
pub struct JsExternal(pub(crate) Value); pub struct JsExternal(pub(crate) Value);
impl TypeName for JsExternal {
fn type_name() -> &'static str {
"external"
}
fn value_type() -> ValueType {
ValueType::External
}
}
macro_rules! impl_napi_value_trait { macro_rules! impl_napi_value_trait {
($js_value:ident, $value_type:ident) => { ($js_value:ident, $value_type:ident) => {
impl NapiValue for $js_value { impl NapiValue for $js_value {

View file

@ -1,12 +1,23 @@
use std::convert::TryFrom; use std::convert::TryFrom;
use super::Value; use super::Value;
use crate::check_status; use crate::bindgen_runtime::TypeName;
use crate::{check_status, ValueType};
use crate::{sys, Error, Result}; use crate::{sys, Error, Result};
#[derive(Clone, Copy)] #[derive(Clone, Copy)]
pub struct JsNumber(pub(crate) Value); pub struct JsNumber(pub(crate) Value);
impl TypeName for JsNumber {
fn type_name() -> &'static str {
"f64"
}
fn value_type() -> crate::ValueType {
ValueType::Number
}
}
impl JsNumber { impl JsNumber {
pub fn get_uint32(&self) -> Result<u32> { pub fn get_uint32(&self) -> Result<u32> {
let mut result = 0; let mut result = 0;

View file

@ -8,6 +8,7 @@ use std::ptr;
#[cfg(feature = "napi5")] #[cfg(feature = "napi5")]
use super::check_status; use super::check_status;
use super::Value; use super::Value;
use crate::bindgen_runtime::TypeName;
#[cfg(feature = "napi5")] #[cfg(feature = "napi5")]
use crate::sys; use crate::sys;
#[cfg(feature = "napi5")] #[cfg(feature = "napi5")]
@ -16,9 +17,20 @@ use crate::Env;
use crate::Error; use crate::Error;
#[cfg(feature = "napi5")] #[cfg(feature = "napi5")]
use crate::Result; use crate::Result;
use crate::ValueType;
pub struct JsObject(pub(crate) Value); pub struct JsObject(pub(crate) Value);
impl TypeName for JsObject {
fn type_name() -> &'static str {
"Object"
}
fn value_type() -> crate::ValueType {
ValueType::Object
}
}
#[cfg(feature = "napi5")] #[cfg(feature = "napi5")]
pub struct FinalizeContext<T: 'static, Hint: 'static> { pub struct FinalizeContext<T: 'static, Hint: 'static> {
pub env: Env, pub env: Env,

View file

@ -1,6 +1,8 @@
use std::mem; use std::mem;
use std::ptr; use std::ptr;
use crate::bindgen_runtime::TypeName;
use crate::ValueType;
use crate::{check_status, sys, Result, Value}; use crate::{check_status, sys, Result, Value};
pub use latin1::JsStringLatin1; pub use latin1::JsStringLatin1;
@ -14,6 +16,16 @@ mod utf8;
#[derive(Clone, Copy)] #[derive(Clone, Copy)]
pub struct JsString(pub(crate) Value); pub struct JsString(pub(crate) Value);
impl TypeName for JsString {
fn type_name() -> &'static str {
"String"
}
fn value_type() -> crate::ValueType {
ValueType::String
}
}
impl JsString { impl JsString {
pub fn utf8_len(&self) -> Result<usize> { pub fn utf8_len(&self) -> Result<usize> {
let mut length = 0; let mut length = 0;

View file

@ -1,4 +1,16 @@
use crate::{bindgen_runtime::TypeName, ValueType};
use super::Value; use super::Value;
#[derive(Clone, Copy)] #[derive(Clone, Copy)]
pub struct JsUndefined(pub(crate) Value); pub struct JsUndefined(pub(crate) Value);
impl TypeName for JsUndefined {
fn type_name() -> &'static str {
"undefined"
}
fn value_type() -> crate::ValueType {
ValueType::Undefined
}
}

View file

@ -1,8 +1,11 @@
use crate::{js_values::NapiValue, Env, Error, Result}; use crate::{
bindgen_runtime::{ToNapiValue, TypeName},
Env, Error, Result,
};
pub trait Task: Send + Sized { pub trait Task: Send + Sized {
type Output: Send + Sized + 'static; type Output: Send + Sized + 'static;
type JsValue: NapiValue; type JsValue: ToNapiValue + TypeName;
/// Compute logic in libuv thread /// Compute logic in libuv thread
fn compute(&mut self) -> Result<Self::Output>; fn compute(&mut self) -> Result<Self::Output>;
@ -14,4 +17,7 @@ pub trait Task: Send + Sized {
fn reject(self, _env: Env, err: Error) -> Result<Self::JsValue> { fn reject(self, _env: Env, err: Error) -> Result<Self::JsValue> {
Err(err) Err(err)
} }
// after resolve or reject
fn finally() {}
} }

View file

@ -42,6 +42,8 @@ Generated by [AVA](https://avajs.dev).
export function concatStr(mutS: string): string␊ export function concatStr(mutS: string): string␊
export function concatUtf16(s: string): string␊ export function concatUtf16(s: string): string␊
export function concatLatin1(s: string): string␊ export function concatLatin1(s: string): string␊
export function withoutAbortController(a: number, b: number): Promise<number>
export function withAbortController(a: number, b: number, ctrl: AbortController): Promise<number>
export function getBuffer(): Buffer␊ export function getBuffer(): Buffer␊
export class Animal {␊ export class Animal {␊
readonly kind: Kind␊ readonly kind: Kind␊

View file

@ -30,6 +30,8 @@ import {
returnEither, returnEither,
either3, either3,
either4, either4,
withoutAbortController,
withAbortController,
} from '../' } from '../'
test('number', (t) => { test('number', (t) => {
@ -160,3 +162,19 @@ test('either4', (t) => {
t.is(either4({ v: 1 }), 1) t.is(either4({ v: 1 }), 1)
t.is(either4({ v: 'world' }), 'world'.length) t.is(either4({ v: 'world' }), 'world'.length)
}) })
test('async task without abort controller', async (t) => {
t.is(await withoutAbortController(1, 2), 3)
})
test('async task with abort controller', async (t) => {
const ctrl = new AbortController()
const promise = withAbortController(1, 2, ctrl)
try {
ctrl.abort()
await promise
t.fail('Should throw AbortError')
} catch (err: unknown) {
t.is((err as Error).message, 'AbortError')
}
})

View file

@ -32,6 +32,8 @@ export function contains(source: string, target: string): boolean
export function concatStr(mutS: string): string export function concatStr(mutS: string): string
export function concatUtf16(s: string): string export function concatUtf16(s: string): string
export function concatLatin1(s: string): string export function concatLatin1(s: string): string
export function withoutAbortController(a: number, b: number): Promise<number>
export function withAbortController(a: number, b: number, ctrl: AbortController): Promise<number>
export function getBuffer(): Buffer export function getBuffer(): Buffer
export class Animal { export class Animal {
readonly kind: Kind readonly kind: Kind

View file

@ -15,4 +15,5 @@ mod number;
mod object; mod object;
mod serde; mod serde;
mod string; mod string;
mod task;
mod typed_array; mod typed_array;

31
examples/napi/src/task.rs Normal file
View file

@ -0,0 +1,31 @@
use std::thread::sleep;
use napi::bindgen_prelude::*;
use napi::Task;
struct DelaySum(u32, u32);
#[napi]
impl Task for DelaySum {
type Output = u32;
type JsValue = u32;
fn compute(&mut self) -> Result<Self::Output> {
sleep(std::time::Duration::from_secs(1));
Ok(self.0 + self.1)
}
fn resolve(self, _env: napi::Env, output: Self::Output) -> Result<Self::JsValue> {
Ok(output)
}
}
#[napi]
fn without_abort_controller(a: u32, b: u32) -> AsyncTask<DelaySum> {
AsyncTask::new(DelaySum(a, b))
}
#[napi]
fn with_abort_controller(a: u32, b: u32, ctrl: AsyncTaskAbortController) -> AsyncTask<DelaySum> {
AsyncTask::with_abort_controller(DelaySum(a, b), ctrl)
}