chore: specified dependencies versions
This commit is contained in:
parent
9570899025
commit
d36c303dec
29 changed files with 543 additions and 85 deletions
|
@ -1,5 +1,5 @@
|
|||
use proc_macro2::Ident;
|
||||
use syn::Attribute;
|
||||
use syn::{Attribute, Type};
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct NapiFn {
|
||||
|
@ -76,6 +76,7 @@ pub struct NapiImpl {
|
|||
pub name: Ident,
|
||||
pub js_name: String,
|
||||
pub items: Vec<NapiFn>,
|
||||
pub task_output_type: Option<Type>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
|
|
|
@ -76,6 +76,13 @@ impl TryToTokens for NapiStruct {
|
|||
|
||||
impl NapiStruct {
|
||||
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(
|
||||
&format!("__napi_helper__{}", self.name.to_string()),
|
||||
Span::call_site(),
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
mod r#enum;
|
||||
mod r#fn;
|
||||
mod r#struct;
|
||||
pub(crate) mod r#struct;
|
||||
|
||||
use std::collections::HashMap;
|
||||
|
||||
|
@ -35,6 +35,7 @@ static KNOWN_TYPES: Lazy<HashMap<&'static str, &'static str>> = Lazy::new(|| {
|
|||
("i16", "number"),
|
||||
("i32", "number"),
|
||||
("i64", "number"),
|
||||
("f64", "number"),
|
||||
("u8", "number"),
|
||||
("u16", "number"),
|
||||
("u32", "number"),
|
||||
|
@ -54,6 +55,9 @@ static KNOWN_TYPES: Lazy<HashMap<&'static str, &'static str>> = Lazy::new(|| {
|
|||
("Value", "any"),
|
||||
("Map", "Record<string, any>"),
|
||||
("HashMap", "Record<{}, {}>"),
|
||||
("ArrayBuffer", "ArrayBuffer"),
|
||||
("DataView", "DataView"),
|
||||
("Date", "Date"),
|
||||
("Buffer", "Buffer"),
|
||||
// TODO: Vec<u8> should be Buffer, now is Array<number>
|
||||
("Vec", "Array<{}>"),
|
||||
|
@ -63,6 +67,12 @@ static KNOWN_TYPES: Lazy<HashMap<&'static str, &'static str>> = Lazy::new(|| {
|
|||
("Either3", "{} | {} | {}"),
|
||||
("Either4", "{} | {} | {} | {}"),
|
||||
("Either5", "{} | {} | {} | {} | {}"),
|
||||
("unknown", "unknown"),
|
||||
("null", "null"),
|
||||
("symbol", "symbol"),
|
||||
("external", "object"),
|
||||
("AsyncTaskAbortController", "AbortController"),
|
||||
("Function", "(...args: any[]) => any"),
|
||||
]);
|
||||
|
||||
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 {
|
||||
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()) {
|
||||
if known_ty.contains("{}") {
|
||||
ts_ty = Some(fill_ty(known_ty, args));
|
||||
|
|
|
@ -1,6 +1,13 @@
|
|||
use std::cell::RefCell;
|
||||
use std::collections::HashMap;
|
||||
|
||||
use super::{ToTypeDef, TypeDef};
|
||||
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 {
|
||||
fn to_type_def(&self) -> TypeDef {
|
||||
TypeDef {
|
||||
|
@ -17,6 +24,12 @@ impl ToTypeDef for NapiStruct {
|
|||
|
||||
impl ToTypeDef for NapiImpl {
|
||||
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 {
|
||||
kind: "impl".to_owned(),
|
||||
name: self.js_name.to_owned(),
|
||||
|
|
|
@ -18,7 +18,7 @@ type-def = ["napi-derive-backend/type-def"]
|
|||
|
||||
[dependencies]
|
||||
convert_case = "0.4"
|
||||
napi-derive-backend = {version = "1", path = "../backend"}
|
||||
napi-derive-backend = {version = "1.0.2", path = "../backend"}
|
||||
proc-macro2 = "1.0"
|
||||
quote = "1.0"
|
||||
syn = {version = "1.0", features = ["fold", "full", "extra-traits"]}
|
||||
|
|
|
@ -15,7 +15,7 @@ use napi_derive_backend::{
|
|||
use proc_macro2::{Ident, TokenStream, TokenTree};
|
||||
use quote::ToTokens;
|
||||
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};
|
||||
|
||||
|
@ -755,43 +755,52 @@ impl ConvertToAST for syn::ItemImpl {
|
|||
|
||||
let mut struct_js_name = struct_name.to_string();
|
||||
let mut items = vec![];
|
||||
|
||||
let mut task_output_type = None;
|
||||
for item in self.items.iter_mut() {
|
||||
let method = match item {
|
||||
syn::ImplItem::Method(m) => m,
|
||||
if let Some(method) = match item {
|
||||
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]")
|
||||
}
|
||||
};
|
||||
let opts = BindgenAttrs::find(&mut method.attrs)?;
|
||||
} {
|
||||
let opts = BindgenAttrs::find(&mut method.attrs)?;
|
||||
|
||||
// it'd better only care methods decorated with `#[napi]` attribute
|
||||
if !opts.exists {
|
||||
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].",);
|
||||
// it'd better only care methods decorated with `#[napi]` attribute
|
||||
if !opts.exists {
|
||||
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].",);
|
||||
}
|
||||
}
|
||||
|
||||
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 {
|
||||
|
@ -800,6 +809,7 @@ impl ConvertToAST for syn::ItemImpl {
|
|||
name: struct_name,
|
||||
js_name: struct_js_name,
|
||||
items,
|
||||
task_output_type,
|
||||
}),
|
||||
})
|
||||
}
|
||||
|
|
|
@ -28,7 +28,7 @@ tokio_rt = ["tokio", "once_cell", "napi4"]
|
|||
|
||||
[dependencies]
|
||||
ctor = "0.1"
|
||||
napi-sys = {version = "1", path = "../sys"}
|
||||
napi-sys = {version = "1.1.2", path = "../sys"}
|
||||
|
||||
[target.'cfg(windows)'.dependencies]
|
||||
winapi = {version = "0.3.9", features = ["winuser", "minwindef", "ntdef", "libloaderapi"]}
|
||||
|
|
|
@ -1,11 +1,12 @@
|
|||
use std::mem;
|
||||
use std::os::raw::{c_char, c_void};
|
||||
use std::os::raw::c_void;
|
||||
use std::ptr;
|
||||
use std::sync::atomic::{AtomicBool, Ordering};
|
||||
use std::{ffi::CString, rc::Rc};
|
||||
|
||||
use crate::{
|
||||
check_status,
|
||||
js_values::{NapiRaw, NapiValue},
|
||||
sys, Env, JsError, JsObject, Result, Task,
|
||||
bindgen_runtime::ToNapiValue, check_status, js_values::NapiValue, sys, Env, JsError, JsObject,
|
||||
Result, Task,
|
||||
};
|
||||
|
||||
struct AsyncWork<T: Task> {
|
||||
|
@ -13,46 +14,53 @@ struct AsyncWork<T: Task> {
|
|||
deferred: sys::napi_deferred,
|
||||
value: Result<mem::MaybeUninit<T::Output>>,
|
||||
napi_async_work: sys::napi_async_work,
|
||||
abort: Rc<AtomicBool>,
|
||||
}
|
||||
|
||||
pub struct AsyncWorkPromise<'env> {
|
||||
napi_async_work: sys::napi_async_work,
|
||||
pub struct AsyncWorkPromise {
|
||||
pub(crate) napi_async_work: sys::napi_async_work,
|
||||
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 {
|
||||
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<()> {
|
||||
check_status!(unsafe { sys::napi_cancel_async_work(self.env.0, self.napi_async_work) })
|
||||
pub fn cancel(&self) -> Result<()> {
|
||||
// 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();
|
||||
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 deferred = ptr::null_mut();
|
||||
check_status!(unsafe { sys::napi_create_promise(env.0, &mut deferred, &mut raw_promise) })?;
|
||||
let mut raw_name = ptr::null_mut();
|
||||
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)
|
||||
})?;
|
||||
check_status!(unsafe { sys::napi_create_promise(env, &mut deferred, &mut raw_promise) })?;
|
||||
let task_abort = abort_status.unwrap_or_else(|| Rc::new(AtomicBool::new(false)));
|
||||
let result = Box::leak(Box::new(AsyncWork {
|
||||
inner_task: task,
|
||||
deferred,
|
||||
value: Ok(mem::MaybeUninit::zeroed()),
|
||||
napi_async_work: ptr::null_mut(),
|
||||
abort: task_abort.clone(),
|
||||
}));
|
||||
check_status!(unsafe {
|
||||
sys::napi_create_async_work(
|
||||
env.0,
|
||||
env,
|
||||
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(
|
||||
complete::<T>
|
||||
|
@ -62,11 +70,13 @@ pub fn run<T: Task>(env: &Env, task: T) -> Result<AsyncWorkPromise<'_>> {
|
|||
&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 {
|
||||
napi_async_work: result.napi_async_work,
|
||||
raw_promise,
|
||||
deferred,
|
||||
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.
|
||||
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>);
|
||||
if work.abort.load(Ordering::Relaxed) {
|
||||
return;
|
||||
}
|
||||
let _ = mem::replace(
|
||||
&mut work.value,
|
||||
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>(
|
||||
|
@ -101,10 +116,13 @@ unsafe extern "C" fn complete<T: Task>(
|
|||
}
|
||||
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) => {
|
||||
let status = sys::napi_resolve_deferred(env, deferred, v.raw());
|
||||
debug_assert!(status == sys::Status::napi_ok, "Reject promise failed");
|
||||
let status = sys::napi_resolve_deferred(env, deferred, v);
|
||||
debug_assert!(status == sys::Status::napi_ok, "Resolve promise failed");
|
||||
}
|
||||
Err(e) => {
|
||||
let status = sys::napi_reject_deferred(env, deferred, JsError::from(e).into_value(env));
|
||||
|
|
|
@ -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;
|
||||
|
||||
mod array;
|
||||
|
@ -12,6 +12,7 @@ mod object;
|
|||
#[cfg(feature = "serde-json")]
|
||||
mod serde;
|
||||
mod string;
|
||||
mod task;
|
||||
|
||||
pub use array::*;
|
||||
pub use buffer::*;
|
||||
|
@ -19,6 +20,7 @@ pub use either::*;
|
|||
pub use nil::*;
|
||||
pub use object::*;
|
||||
pub use string::*;
|
||||
pub use task::*;
|
||||
|
||||
#[cfg(feature = "latin1")]
|
||||
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>;
|
||||
}
|
||||
|
||||
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> {
|
||||
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>;
|
||||
}
|
||||
|
||||
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 {
|
||||
/// # Safety
|
||||
///
|
||||
|
|
159
crates/napi/src/bindgen_runtime/js_values/task.rs
Normal file
159
crates/napi/src/bindgen_runtime/js_values/task.rs
Normal 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);
|
||||
}
|
|
@ -485,7 +485,7 @@ impl Env {
|
|||
#[cfg(feature = "napi5")]
|
||||
pub fn create_function_from_closure<R, F>(&self, name: &str, callback: F) -> Result<JsFunction>
|
||||
where
|
||||
F: 'static + Send + Sync + Fn(crate::CallContext<'_>) -> Result<R>,
|
||||
F: 'static + Fn(crate::CallContext<'_>) -> Result<R>,
|
||||
R: NapiRaw,
|
||||
{
|
||||
use crate::CallContext;
|
||||
|
@ -925,7 +925,7 @@ impl Env {
|
|||
|
||||
/// 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> {
|
||||
async_work::run(self, task)
|
||||
async_work::run(self.0, task, None)
|
||||
}
|
||||
|
||||
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));
|
||||
}
|
||||
|
||||
unsafe extern "C" fn raw_finalize<T>(
|
||||
pub(crate) unsafe extern "C" fn raw_finalize<T>(
|
||||
env: sys::napi_env,
|
||||
finalize_data: *mut c_void,
|
||||
finalize_hint: *mut c_void,
|
||||
|
|
|
@ -3,10 +3,21 @@ use std::os::raw::c_void;
|
|||
use std::ptr;
|
||||
use std::slice;
|
||||
|
||||
use crate::bindgen_runtime::TypeName;
|
||||
use crate::{check_status, sys, JsUnknown, NapiValue, Ref, Result, Value, ValueType};
|
||||
|
||||
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(crate) value: JsArrayBuffer,
|
||||
len: usize,
|
||||
|
@ -15,6 +26,16 @@ pub struct JsArrayBufferValue {
|
|||
|
||||
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 arraybuffer: JsArrayBuffer,
|
||||
data: *mut c_void,
|
||||
|
@ -25,6 +46,16 @@ pub struct JsTypedArrayValue {
|
|||
|
||||
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 arraybuffer: JsArrayBuffer,
|
||||
_data: *mut c_void,
|
||||
|
|
|
@ -2,7 +2,7 @@ use std::convert::TryFrom;
|
|||
use std::ptr;
|
||||
|
||||
use super::*;
|
||||
use crate::{check_status, sys, Result};
|
||||
use crate::{bindgen_runtime::TypeName, check_status, sys, Result};
|
||||
|
||||
#[derive(Clone, Copy)]
|
||||
pub struct JsBigint {
|
||||
|
@ -10,6 +10,16 @@ pub struct JsBigint {
|
|||
pub word_count: usize,
|
||||
}
|
||||
|
||||
impl TypeName for JsBigint {
|
||||
fn type_name() -> &'static str {
|
||||
"BigInt"
|
||||
}
|
||||
|
||||
fn value_type() -> ValueType {
|
||||
ValueType::Bigint
|
||||
}
|
||||
}
|
||||
|
||||
impl JsBigint {
|
||||
pub(crate) fn from_raw_unchecked(
|
||||
env: sys::napi_env,
|
||||
|
|
|
@ -1,12 +1,23 @@
|
|||
use std::convert::TryFrom;
|
||||
|
||||
use super::Value;
|
||||
use crate::check_status;
|
||||
use crate::bindgen_runtime::TypeName;
|
||||
use crate::{check_status, ValueType};
|
||||
use crate::{sys, Error, Result};
|
||||
|
||||
#[derive(Clone, Copy)]
|
||||
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 {
|
||||
pub fn get_value(&self) -> Result<bool> {
|
||||
let mut result = false;
|
||||
|
|
|
@ -5,11 +5,22 @@ use std::ptr;
|
|||
use super::Value;
|
||||
#[cfg(feature = "serde-json")]
|
||||
use super::ValueType;
|
||||
use crate::bindgen_runtime::TypeName;
|
||||
use crate::check_status;
|
||||
use crate::{sys, JsUnknown, NapiValue, Ref, Result};
|
||||
|
||||
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(crate) value: JsBuffer,
|
||||
data: mem::ManuallyDrop<Vec<u8>>,
|
||||
|
|
|
@ -1,8 +1,18 @@
|
|||
use super::check_status;
|
||||
use crate::{sys, Result, Value};
|
||||
use crate::{bindgen_runtime::TypeName, sys, Result, Value, ValueType};
|
||||
|
||||
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 {
|
||||
pub fn value_of(&self) -> Result<f64> {
|
||||
let mut timestamp: f64 = 0.0;
|
||||
|
|
|
@ -1,11 +1,22 @@
|
|||
use std::ptr;
|
||||
|
||||
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};
|
||||
|
||||
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).
|
||||
///
|
||||
/// Example:
|
||||
|
|
|
@ -2,7 +2,9 @@ use std::convert::TryFrom;
|
|||
use std::ffi::CString;
|
||||
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")]
|
||||
mod de;
|
||||
|
@ -60,11 +62,41 @@ pub struct JsUnknown(pub(crate) Value);
|
|||
#[derive(Clone, Copy)]
|
||||
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)]
|
||||
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);
|
||||
|
||||
impl TypeName for JsExternal {
|
||||
fn type_name() -> &'static str {
|
||||
"external"
|
||||
}
|
||||
|
||||
fn value_type() -> ValueType {
|
||||
ValueType::External
|
||||
}
|
||||
}
|
||||
|
||||
macro_rules! impl_napi_value_trait {
|
||||
($js_value:ident, $value_type:ident) => {
|
||||
impl NapiValue for $js_value {
|
||||
|
|
|
@ -1,12 +1,23 @@
|
|||
use std::convert::TryFrom;
|
||||
|
||||
use super::Value;
|
||||
use crate::check_status;
|
||||
use crate::bindgen_runtime::TypeName;
|
||||
use crate::{check_status, ValueType};
|
||||
use crate::{sys, Error, Result};
|
||||
|
||||
#[derive(Clone, Copy)]
|
||||
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 {
|
||||
pub fn get_uint32(&self) -> Result<u32> {
|
||||
let mut result = 0;
|
||||
|
|
|
@ -8,6 +8,7 @@ use std::ptr;
|
|||
#[cfg(feature = "napi5")]
|
||||
use super::check_status;
|
||||
use super::Value;
|
||||
use crate::bindgen_runtime::TypeName;
|
||||
#[cfg(feature = "napi5")]
|
||||
use crate::sys;
|
||||
#[cfg(feature = "napi5")]
|
||||
|
@ -16,9 +17,20 @@ use crate::Env;
|
|||
use crate::Error;
|
||||
#[cfg(feature = "napi5")]
|
||||
use crate::Result;
|
||||
use crate::ValueType;
|
||||
|
||||
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")]
|
||||
pub struct FinalizeContext<T: 'static, Hint: 'static> {
|
||||
pub env: Env,
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
use std::mem;
|
||||
use std::ptr;
|
||||
|
||||
use crate::bindgen_runtime::TypeName;
|
||||
use crate::ValueType;
|
||||
use crate::{check_status, sys, Result, Value};
|
||||
|
||||
pub use latin1::JsStringLatin1;
|
||||
|
@ -14,6 +16,16 @@ mod utf8;
|
|||
#[derive(Clone, Copy)]
|
||||
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 {
|
||||
pub fn utf8_len(&self) -> Result<usize> {
|
||||
let mut length = 0;
|
||||
|
|
|
@ -1,4 +1,16 @@
|
|||
use crate::{bindgen_runtime::TypeName, ValueType};
|
||||
|
||||
use super::Value;
|
||||
|
||||
#[derive(Clone, Copy)]
|
||||
pub struct JsUndefined(pub(crate) Value);
|
||||
|
||||
impl TypeName for JsUndefined {
|
||||
fn type_name() -> &'static str {
|
||||
"undefined"
|
||||
}
|
||||
|
||||
fn value_type() -> crate::ValueType {
|
||||
ValueType::Undefined
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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 {
|
||||
type Output: Send + Sized + 'static;
|
||||
type JsValue: NapiValue;
|
||||
type JsValue: ToNapiValue + TypeName;
|
||||
|
||||
/// Compute logic in libuv thread
|
||||
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> {
|
||||
Err(err)
|
||||
}
|
||||
|
||||
// after resolve or reject
|
||||
fn finally() {}
|
||||
}
|
||||
|
|
|
@ -42,6 +42,8 @@ Generated by [AVA](https://avajs.dev).
|
|||
export function concatStr(mutS: string): string␊
|
||||
export function concatUtf16(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 class Animal {␊
|
||||
readonly kind: Kind␊
|
||||
|
|
Binary file not shown.
|
@ -30,6 +30,8 @@ import {
|
|||
returnEither,
|
||||
either3,
|
||||
either4,
|
||||
withoutAbortController,
|
||||
withAbortController,
|
||||
} from '../'
|
||||
|
||||
test('number', (t) => {
|
||||
|
@ -160,3 +162,19 @@ test('either4', (t) => {
|
|||
t.is(either4({ v: 1 }), 1)
|
||||
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')
|
||||
}
|
||||
})
|
||||
|
|
2
examples/napi/index.d.ts
vendored
2
examples/napi/index.d.ts
vendored
|
@ -32,6 +32,8 @@ export function contains(source: string, target: string): boolean
|
|||
export function concatStr(mutS: string): string
|
||||
export function concatUtf16(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 class Animal {
|
||||
readonly kind: Kind
|
||||
|
|
|
@ -15,4 +15,5 @@ mod number;
|
|||
mod object;
|
||||
mod serde;
|
||||
mod string;
|
||||
mod task;
|
||||
mod typed_array;
|
||||
|
|
31
examples/napi/src/task.rs
Normal file
31
examples/napi/src/task.rs
Normal 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)
|
||||
}
|
Loading…
Reference in a new issue