diff --git a/napi-derive/src/lib.rs b/napi-derive/src/lib.rs index cda575d5..997c646a 100644 --- a/napi-derive/src/lib.rs +++ b/napi-derive/src/lib.rs @@ -93,8 +93,7 @@ pub fn js_function(attr: TokenStream, input: TokenStream) -> TokenStream { use std::ffi::CString; use napi::{JsUnknown, Env, Error, Status, NapiValue, CallContext}; let mut argc = #arg_len_span as usize; - let mut raw_args = - unsafe { mem::MaybeUninit::<[napi::sys::napi_value; #arg_len_span as usize]>::uninit().assume_init() }; + let mut raw_args: [napi::sys::napi_value; #arg_len_span] = [ptr::null_mut(); #arg_len_span]; let mut raw_this = ptr::null_mut(); let mut has_error = false; @@ -112,19 +111,19 @@ pub fn js_function(attr: TokenStream, input: TokenStream) -> TokenStream { } let mut env = Env::from_raw(raw_env); - match CallContext::new(&mut env, cb_info, raw_this, &raw_args, #arg_len_span, argc as usize) - .and_then(|ctx| panic::catch_unwind(AssertUnwindSafe(move || #new_fn_name(ctx))).map_err(|e| { - let message = { - if let Some(string) = e.downcast_ref::() { - string.clone() - } else if let Some(string) = e.downcast_ref::<&str>() { - string.to_string() - } else { - format!("panic from Rust code: {:?}", e) - } - }; - Error::from_reason(message) - }).and_then(|v| v)) + let ctx = CallContext::new(&mut env, cb_info, raw_this, &raw_args, #arg_len_span, argc as usize); + match panic::catch_unwind(AssertUnwindSafe(move || #new_fn_name(ctx))).map_err(|e| { + let message = { + if let Some(string) = e.downcast_ref::() { + string.clone() + } else if let Some(string) = e.downcast_ref::<&str>() { + string.to_string() + } else { + format!("panic from Rust code: {:?}", e) + } + }; + Error::from_reason(message) + }).and_then(|v| v) { Ok(v) => v.raw_value(), Err(e) => { diff --git a/napi/src/async_work.rs b/napi/src/async_work.rs index d65fdf7f..b0164e4b 100644 --- a/napi/src/async_work.rs +++ b/napi/src/async_work.rs @@ -4,54 +4,76 @@ use std::ptr; use crate::error::check_status; use crate::js_values::NapiValue; -use crate::{sys, Env, Result, Task}; +use crate::{sys, Env, JsObject, Result, Task}; -pub struct AsyncWork { +struct AsyncWork { inner_task: T, deferred: sys::napi_deferred, - value: Result<*mut T::Output>, + value: Result>, + napi_async_work: sys::napi_async_work, } -impl AsyncWork { - pub fn run(env: sys::napi_env, task: T, deferred: sys::napi_deferred) -> Result<()> { - let mut raw_resource = ptr::null_mut(); - check_status(unsafe { sys::napi_create_object(env, &mut raw_resource) })?; - let mut raw_name = ptr::null_mut(); - let s = "napi_rs_async"; - check_status(unsafe { - sys::napi_create_string_utf8( - env, - s.as_ptr() as *const c_char, - s.len() as u64, - &mut raw_name, - ) - })?; - let result = AsyncWork { - inner_task: task, - deferred, - value: Ok(ptr::null_mut()), - }; - let mut async_work = ptr::null_mut(); - check_status(unsafe { - sys::napi_create_async_work( - env, - raw_resource, - raw_name, - Some(execute:: as unsafe extern "C" fn(env: sys::napi_env, data: *mut c_void)), - Some( - complete:: - as unsafe extern "C" fn( - env: sys::napi_env, - status: sys::napi_status, - data: *mut c_void, - ), - ), - Box::leak(Box::new(result)) as *mut _ as *mut c_void, - &mut async_work, - ) - })?; - check_status(unsafe { sys::napi_queue_async_work(env, async_work) }) +#[derive(Debug)] +pub struct AsyncWorkPromise<'env> { + napi_async_work: sys::napi_async_work, + raw_promise: sys::napi_value, + env: &'env Env, +} + +impl<'env> AsyncWorkPromise<'env> { + #[inline(always)] + pub fn promise_object(&self) -> JsObject { + JsObject::from_raw_unchecked(self.env.0, self.raw_promise) } + + pub fn cancel(self) -> Result<()> { + check_status(unsafe { sys::napi_cancel_async_work(self.env.0, self.napi_async_work) }) + } +} + +#[inline(always)] +pub fn run<'env, T: Task>(env: &'env Env, task: T) -> Result> { + let mut raw_resource = ptr::null_mut(); + check_status(unsafe { sys::napi_create_object(env.0, &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() as u64, + &mut raw_name, + ) + })?; + let result = Box::leak(Box::new(AsyncWork { + inner_task: task, + deferred, + value: Ok(mem::MaybeUninit::zeroed()), + napi_async_work: ptr::null_mut(), + })); + check_status(unsafe { + sys::napi_create_async_work( + env.0, + raw_resource, + raw_name, + Some(execute:: as unsafe extern "C" fn(env: sys::napi_env, data: *mut c_void)), + Some( + complete:: + as unsafe extern "C" fn(env: sys::napi_env, status: sys::napi_status, data: *mut c_void), + ), + result as *mut _ as *mut c_void, + &mut result.napi_async_work, + ) + })?; + check_status(unsafe { sys::napi_queue_async_work(env.0, result.napi_async_work) })?; + Ok(AsyncWorkPromise { + napi_async_work: result.napi_async_work, + raw_promise, + env, + }) } unsafe impl Send for AsyncWork {} @@ -60,10 +82,10 @@ unsafe impl Sync for AsyncWork {} unsafe extern "C" fn execute(_env: sys::napi_env, data: *mut c_void) { let mut work = Box::from_raw(data as *mut AsyncWork); - work.value = work - .inner_task - .compute() - .map(|v| Box::into_raw(Box::from(v))); + let _ = mem::replace( + &mut work.value, + work.inner_task.compute().map(|v| mem::MaybeUninit::new(v)), + ); Box::leak(work); } @@ -73,37 +95,26 @@ unsafe extern "C" fn complete( data: *mut c_void, ) { let mut work = Box::from_raw(data as *mut AsyncWork); - let value_ptr = mem::replace(&mut work.value, Ok(ptr::null_mut())); + let value_ptr = mem::replace(&mut work.value, Ok(mem::MaybeUninit::zeroed())); let deferred = mem::replace(&mut work.deferred, ptr::null_mut()); + let napi_async_work = mem::replace(&mut work.napi_async_work, ptr::null_mut()); let value = value_ptr.and_then(move |v| { - let mut env = Env::from_raw(env); - let output = ptr::read(v as *const _); - work.inner_task.resolve(&mut env, output) + let output = v.assume_init(); + work.inner_task.resolve(&mut Env::from_raw(env), output) }); - let mut handle_scope = ptr::null_mut(); match check_status(status).and_then(move |_| value) { Ok(v) => { - let open_handle_status = sys::napi_open_handle_scope(env, &mut handle_scope); - debug_assert!( - open_handle_status == sys::napi_status::napi_ok, - "OpenHandleScope failed" - ); let status = sys::napi_resolve_deferred(env, deferred, v.raw_value()); debug_assert!(status == sys::napi_status::napi_ok, "Reject promise failed"); } Err(e) => { - let open_handle_status = sys::napi_open_handle_scope(env, &mut handle_scope); - debug_assert!( - open_handle_status == sys::napi_status::napi_ok, - "OpenHandleScope failed" - ); let status = sys::napi_reject_deferred(env, deferred, e.into_raw(env)); debug_assert!(status == sys::napi_status::napi_ok, "Reject promise failed"); } }; - let close_handle_scope_status = sys::napi_close_handle_scope(env, handle_scope); + let delete_status = sys::napi_delete_async_work(env, napi_async_work); debug_assert!( - close_handle_scope_status == sys::napi_status::napi_ok, - "Close handle scope failed" + delete_status == sys::napi_status::napi_ok, + "Delete async work failed" ); } diff --git a/napi/src/call_context.rs b/napi/src/call_context.rs index d75a7164..2f2fa095 100644 --- a/napi/src/call_context.rs +++ b/napi/src/call_context.rs @@ -1,34 +1,34 @@ use std::ptr; use crate::error::check_status; -use crate::{sys, Either, Env, Error, JsUndefined, JsUnknown, NapiValue, Result, Status}; +use crate::{sys, Either, Env, Error, JsUndefined, NapiValue, Result, Status}; -pub struct CallContext<'env, T: NapiValue = JsUnknown> { +pub struct CallContext<'env> { pub env: &'env Env, - pub this: T, + raw_this: sys::napi_value, callback_info: sys::napi_callback_info, args: &'env [sys::napi_value], arg_len: usize, actual_arg_length: usize, } -impl<'env, T: NapiValue> CallContext<'env, T> { +impl<'env> CallContext<'env> { pub fn new( env: &'env Env, callback_info: sys::napi_callback_info, - this: sys::napi_value, + raw_this: sys::napi_value, args: &'env [sys::napi_value], arg_len: usize, actual_arg_length: usize, - ) -> Result { - Ok(Self { + ) -> Self { + Self { env, callback_info, - this: T::from_raw(env.0, this)?, + raw_this, args, arg_len, actual_arg_length, - }) + } } pub fn get(&self, index: usize) -> Result { @@ -38,10 +38,7 @@ impl<'env, T: NapiValue> CallContext<'env, T> { reason: "Arguments index out of range".to_owned(), }) } else { - Ok(ArgType::from_raw_without_typecheck( - self.env.0, - self.args[index], - )) + Ok(ArgType::from_raw_unchecked(self.env.0, self.args[index])) } } @@ -68,4 +65,14 @@ impl<'env, T: NapiValue> CallContext<'env, T> { check_status(unsafe { sys::napi_get_new_target(self.env.0, self.callback_info, &mut value) })?; V::from_raw(self.env.0, value) } + + #[inline(always)] + pub fn this(&self) -> Result { + T::from_raw(self.env.0, self.raw_this) + } + + #[inline(always)] + pub fn this_unchecked(&self) -> T { + T::from_raw_unchecked(self.env.0, self.raw_this) + } } diff --git a/napi/src/env.rs b/napi/src/env.rs index c470bb68..f90186a3 100644 --- a/napi/src/env.rs +++ b/napi/src/env.rs @@ -5,7 +5,7 @@ use std::mem; use std::os::raw::{c_char, c_void}; use std::ptr; -use crate::async_work::AsyncWork; +use crate::async_work::{self, AsyncWorkPromise}; use crate::error::check_status; use crate::js_values::*; use crate::task::Task; @@ -40,8 +40,7 @@ impl Env { pub fn get_undefined(&self) -> Result { let mut raw_value = ptr::null_mut(); - let status = unsafe { sys::napi_get_undefined(self.0, &mut raw_value) }; - check_status(status)?; + check_status(unsafe { sys::napi_get_undefined(self.0, &mut raw_value) })?; Ok(JsUndefined::from_raw_unchecked(self.0, raw_value)) } @@ -220,7 +219,7 @@ impl Env { Ok(JsObject::from_raw_unchecked(self.0, raw_value)) } - pub fn create_buffer(&self, length: u64) -> Result { + pub fn create_buffer<'env, 'buffer>(&'env self, length: u64) -> Result> { let mut raw_value = ptr::null_mut(); let mut data = Vec::with_capacity(length as usize); let mut data_ptr = data.as_mut_ptr(); @@ -229,7 +228,7 @@ impl Env { })?; mem::forget(data); - Ok(JsBuffer::from_raw_unchecked( + Ok(JsBuffer::new( self.0, raw_value, data_ptr as *mut u8, @@ -237,7 +236,10 @@ impl Env { )) } - pub fn create_buffer_with_data(&self, mut data: Vec) -> Result { + pub fn create_buffer_with_data<'env, 'buffer>( + &'env self, + mut data: Vec, + ) -> Result> { let length = data.len() as u64; let mut raw_value = ptr::null_mut(); let data_ptr = data.as_mut_ptr(); @@ -254,12 +256,7 @@ impl Env { let mut changed = 0; check_status(unsafe { sys::napi_adjust_external_memory(self.0, length as i64, &mut changed) })?; mem::forget(data); - Ok(JsBuffer::from_raw_unchecked( - self.0, - raw_value, - data_ptr, - length as usize, - )) + Ok(JsBuffer::new(self.0, raw_value, data_ptr, length as usize)) } pub fn create_arraybuffer(&self, length: u64) -> Result { @@ -490,13 +487,8 @@ impl Env { Ok(JsObject::from_raw_unchecked(self.0, result)) } - pub fn spawn(&self, task: T) -> Result { - let mut raw_promise = ptr::null_mut(); - let mut raw_deferred = ptr::null_mut(); - - check_status(unsafe { sys::napi_create_promise(self.0, &mut raw_deferred, &mut raw_promise) })?; - AsyncWork::run(self.0, task, raw_deferred)?; - Ok(JsObject::from_raw_unchecked(self.0, raw_promise)) + pub fn spawn(&self, task: T) -> Result { + async_work::run(self, task) } pub fn get_global(&self) -> Result { diff --git a/napi/src/js_values/arraybuffer.rs b/napi/src/js_values/arraybuffer.rs index eca15128..6a179089 100644 --- a/napi/src/js_values/arraybuffer.rs +++ b/napi/src/js_values/arraybuffer.rs @@ -154,7 +154,7 @@ impl NapiValue for JsArrayBuffer { }) } - fn from_raw_without_typecheck(env: sys::napi_env, value: sys::napi_value) -> Self { + fn from_raw_unchecked(env: sys::napi_env, value: sys::napi_value) -> Self { let mut data = ptr::null_mut(); let mut len: u64 = 0; let status = unsafe { sys::napi_get_arraybuffer_info(env, value, &mut data, &mut len) }; diff --git a/napi/src/js_values/bigint.rs b/napi/src/js_values/bigint.rs index 3242953d..31221519 100644 --- a/napi/src/js_values/bigint.rs +++ b/napi/src/js_values/bigint.rs @@ -154,7 +154,7 @@ impl NapiValue for JsBigint { }) } - fn from_raw_without_typecheck(env: sys::napi_env, value: sys::napi_value) -> Self { + fn from_raw_unchecked(env: sys::napi_env, value: sys::napi_value) -> Self { let mut word_count: u64 = 0; let status = unsafe { sys::napi_get_value_bigint_words( diff --git a/napi/src/js_values/buffer.rs b/napi/src/js_values/buffer.rs index 15e54c49..37d10c4f 100644 --- a/napi/src/js_values/buffer.rs +++ b/napi/src/js_values/buffer.rs @@ -8,18 +8,13 @@ use crate::error::check_status; use crate::{sys, Error, Result}; #[derive(Debug)] -pub struct JsBuffer { +pub struct JsBuffer<'buffer> { pub value: JsObject, - pub data: &'static [u8], + pub data: &'buffer [u8], } -impl JsBuffer { - pub(crate) fn from_raw_unchecked( - env: sys::napi_env, - value: sys::napi_value, - data: *mut u8, - len: usize, - ) -> Self { +impl<'buffer> JsBuffer<'buffer> { + pub(crate) fn new(env: sys::napi_env, value: sys::napi_value, data: *mut u8, len: usize) -> Self { Self { value: JsObject(Value { env, @@ -139,7 +134,7 @@ impl JsBuffer { } } -impl NapiValue for JsBuffer { +impl<'buffer> NapiValue for JsBuffer<'buffer> { fn raw_value(&self) -> sys::napi_value { self.value.0.value } @@ -158,7 +153,7 @@ impl NapiValue for JsBuffer { }) } - fn from_raw_without_typecheck(env: sys::napi_env, value: sys::napi_value) -> Self { + fn from_raw_unchecked(env: sys::napi_env, value: sys::napi_value) -> Self { let mut data = ptr::null_mut(); let mut len: u64 = 0; let status = unsafe { sys::napi_get_buffer_info(env, value, &mut data, &mut len) }; @@ -177,13 +172,13 @@ impl NapiValue for JsBuffer { } } -impl AsRef<[u8]> for JsBuffer { +impl<'buffer> AsRef<[u8]> for JsBuffer<'buffer> { fn as_ref(&self) -> &[u8] { self.data } } -impl Deref for JsBuffer { +impl<'buffer> Deref for JsBuffer<'buffer> { type Target = [u8]; fn deref(&self) -> &[u8] { @@ -191,9 +186,9 @@ impl Deref for JsBuffer { } } -impl TryFrom for JsBuffer { +impl<'buffer> TryFrom for JsBuffer<'buffer> { type Error = Error; - fn try_from(value: JsUnknown) -> Result { + fn try_from(value: JsUnknown) -> Result> { JsBuffer::from_raw(value.0.env, value.0.value) } } diff --git a/napi/src/js_values/either.rs b/napi/src/js_values/either.rs index abb7b248..31cfa35a 100644 --- a/napi/src/js_values/either.rs +++ b/napi/src/js_values/either.rs @@ -22,7 +22,7 @@ impl NapiValue for Either { .or_else(|_| B::from_raw(env, value).map(Self::B)) } - fn from_raw_without_typecheck(env: sys::napi_env, value: sys::napi_value) -> Either { + fn from_raw_unchecked(env: sys::napi_env, value: sys::napi_value) -> Either { Self::from_raw(env, value).unwrap() } diff --git a/napi/src/js_values/mod.rs b/napi/src/js_values/mod.rs index cc6b211f..3c26a78c 100644 --- a/napi/src/js_values/mod.rs +++ b/napi/src/js_values/mod.rs @@ -92,7 +92,7 @@ macro_rules! impl_napi_value_trait { self.0.value } - fn from_raw_without_typecheck(env: sys::napi_env, value: sys::napi_value) -> $js_value { + fn from_raw_unchecked(env: sys::napi_env, value: sys::napi_value) -> $js_value { $js_value(Value { env, value, @@ -101,17 +101,6 @@ macro_rules! impl_napi_value_trait { } } - impl $js_value { - #[inline] - pub fn from_raw_unchecked(env: sys::napi_env, value: sys::napi_value) -> Self { - Self(Value { - env, - value, - value_type: $value_type, - }) - } - } - impl TryFrom for $js_value { type Error = Error; fn try_from(value: JsUnknown) -> Result<$js_value> { @@ -231,7 +220,7 @@ macro_rules! impl_js_value_methods { pub trait NapiValue: Sized { fn from_raw(env: sys::napi_env, value: sys::napi_value) -> Result; - fn from_raw_without_typecheck(env: sys::napi_env, value: sys::napi_value) -> Self; + fn from_raw_unchecked(env: sys::napi_env, value: sys::napi_value) -> Self; fn raw_value(&self) -> sys::napi_value; } @@ -268,7 +257,7 @@ impl NapiValue for JsUnknown { })) } - fn from_raw_without_typecheck(env: sys::napi_env, value: sys::napi_value) -> Self { + fn from_raw_unchecked(env: sys::napi_env, value: sys::napi_value) -> Self { JsUnknown(Value { env, value, diff --git a/test_module/src/class.rs b/test_module/src/class.rs index ae62c156..8d5fa5ef 100644 --- a/test_module/src/class.rs +++ b/test_module/src/class.rs @@ -12,24 +12,19 @@ fn create_test_class(ctx: CallContext) -> Result { } #[js_function(1)] -fn test_class_constructor(mut ctx: CallContext) -> Result { +fn test_class_constructor(ctx: CallContext) -> Result { let count = ctx.get::(0)?; - ctx - .this - .set_named_property("count", ctx.env.create_int32(count.try_into()?)?)?; + let mut this: JsObject = ctx.this_unchecked(); + this.set_named_property("count", ctx.env.create_int32(count.try_into()?)?)?; ctx.env.get_undefined() } #[js_function(1)] -fn add_count(mut ctx: CallContext) -> Result { +fn add_count(ctx: CallContext) -> Result { let add: i32 = ctx.get::(0)?.try_into()?; - let count: i32 = ctx - .this - .get_named_property::("count")? - .try_into()?; - ctx - .this - .set_named_property("count", ctx.env.create_int32(count + add)?)?; + let mut this: JsObject = ctx.this_unchecked(); + let count: i32 = this.get_named_property::("count")?.try_into()?; + this.set_named_property("count", ctx.env.create_int32(count + add)?)?; ctx.env.get_undefined() } diff --git a/test_module/src/function.rs b/test_module/src/function.rs index f9e33845..4307c609 100644 --- a/test_module/src/function.rs +++ b/test_module/src/function.rs @@ -12,11 +12,11 @@ pub fn call_function(ctx: CallContext) -> Result { } #[js_function(1)] -pub fn call_function_with_this(ctx: CallContext) -> Result { - let js_this = &ctx.this; +pub fn call_function_with_this(ctx: CallContext) -> Result { + let js_this: JsObject = ctx.this_unchecked(); let js_func = ctx.get::(0)?; - js_func.call(Some(js_this), &[])?; + js_func.call(Some(&js_this), &[])?; ctx.env.get_null() } diff --git a/test_module/src/object.rs b/test_module/src/object.rs index afa131ab..710787ed 100644 --- a/test_module/src/object.rs +++ b/test_module/src/object.rs @@ -139,15 +139,11 @@ fn test_define_properties(ctx: CallContext) -> Result { } #[js_function(1)] -fn add(mut ctx: CallContext) -> Result { - let count: i32 = ctx - .this - .get_named_property::("count")? - .try_into()?; +fn add(ctx: CallContext) -> Result { + let mut this: JsObject = ctx.this_unchecked(); + let count: i32 = this.get_named_property::("count")?.try_into()?; let value_to_add: i32 = ctx.get::(0)?.try_into()?; - ctx - .this - .set_named_property("count", ctx.env.create_int32(count + value_to_add)?)?; + this.set_named_property("count", ctx.env.create_int32(count + value_to_add)?)?; ctx.env.get_undefined() } diff --git a/test_module/src/task.rs b/test_module/src/task.rs index c2ad5f66..ebe32450 100644 --- a/test_module/src/task.rs +++ b/test_module/src/task.rs @@ -37,7 +37,8 @@ fn fibonacci_native(n: u32) -> u32 { fn test_spawn_thread(ctx: CallContext) -> Result { let n = ctx.get::(0)?; let task = ComputeFib::new(n.try_into()?); - ctx.env.spawn(task) + let async_promise = ctx.env.spawn(task)?; + Ok(async_promise.promise_object()) } pub fn register_js(module: &mut Module) -> Result<()> {