diff --git a/bench/src/async_compute.rs b/bench/src/async_compute.rs index 7b50544d..2404ce87 100644 --- a/bench/src/async_compute.rs +++ b/bench/src/async_compute.rs @@ -1,17 +1,20 @@ -use napi::{CallContext, Env, JsBuffer, JsNumber, JsObject, Module, Result, Task}; +use napi::{ + CallContext, Env, JsBuffer, JsBufferValue, JsNumber, JsObject, Module, Ref, Result, Task, +}; #[repr(transparent)] -struct BufferLength(&'static [u8]); +struct BufferLength(Ref); impl Task for BufferLength { type Output = usize; type JsValue = JsNumber; fn compute(&mut self) -> Result { - Ok(self.0.len()) + Ok((&self.0).len()) } - fn resolve(&self, env: &mut Env, output: Self::Output) -> Result { + fn resolve(self, env: Env, output: Self::Output) -> Result { + self.0.unref(env)?; env.create_uint32(output as u32) } } @@ -19,7 +22,7 @@ impl Task for BufferLength { #[js_function(1)] fn bench_async_task(ctx: CallContext) -> Result { let n = ctx.get::(0)?; - let task = BufferLength(n.data); + let task = BufferLength(n.into_ref()?); let async_promise = ctx.env.spawn(task)?; Ok(async_promise.promise_object()) } diff --git a/napi-derive/src/lib.rs b/napi-derive/src/lib.rs index 9c201d84..a03631a6 100644 --- a/napi-derive/src/lib.rs +++ b/napi-derive/src/lib.rs @@ -176,13 +176,13 @@ fn get_execute_js_code( let return_token_stream = match function_kind { FunctionKind::Contextless => { quote! { - Ok(Some(v)) => v.raw_value(), + Ok(Some(v)) => v.raw(), Ok(None) => ptr::null_mut(), } } FunctionKind::JsFunction => { quote! { - Ok(v) => v.raw_value(), + Ok(v) => v.raw(), } } }; diff --git a/napi/src/async_work.rs b/napi/src/async_work.rs index b0164e4b..b0847735 100644 --- a/napi/src/async_work.rs +++ b/napi/src/async_work.rs @@ -80,6 +80,8 @@ unsafe impl Send for AsyncWork {} unsafe impl Sync for AsyncWork {} +/// env here is the same with the one in `CallContext`. +/// So it actually could do nothing here, because `execute` function is called in the other thread mostly. unsafe extern "C" fn execute(_env: sys::napi_env, data: *mut c_void) { let mut work = Box::from_raw(data as *mut AsyncWork); let _ = mem::replace( @@ -100,11 +102,11 @@ unsafe extern "C" fn complete( let napi_async_work = mem::replace(&mut work.napi_async_work, ptr::null_mut()); let value = value_ptr.and_then(move |v| { let output = v.assume_init(); - work.inner_task.resolve(&mut Env::from_raw(env), output) + work.inner_task.resolve(Env::from_raw(env), output) }); match check_status(status).and_then(move |_| value) { Ok(v) => { - let status = sys::napi_resolve_deferred(env, deferred, v.raw_value()); + let status = sys::napi_resolve_deferred(env, deferred, v.raw()); debug_assert!(status == sys::napi_status::napi_ok, "Reject promise failed"); } Err(e) => { diff --git a/napi/src/env.rs b/napi/src/env.rs index f90186a3..0dc961e7 100644 --- a/napi/src/env.rs +++ b/napi/src/env.rs @@ -30,6 +30,7 @@ use tokio::sync::mpsc::error::TrySendError; pub type Callback = extern "C" fn(sys::napi_env, sys::napi_callback_info) -> sys::napi_value; +#[repr(transparent)] #[derive(Clone, Copy, Debug)] pub struct Env(pub(crate) sys::napi_env); @@ -219,28 +220,26 @@ impl Env { Ok(JsObject::from_raw_unchecked(self.0, raw_value)) } - pub fn create_buffer<'env, 'buffer>(&'env self, length: u64) -> Result> { + pub fn create_buffer(&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(); + let mut data: Vec = Vec::with_capacity(length as usize); + let mut data_ptr = data.as_mut_ptr() as *mut c_void; check_status(unsafe { sys::napi_create_buffer(self.0, length, &mut data_ptr, &mut raw_value) })?; - mem::forget(data); - Ok(JsBuffer::new( - self.0, - raw_value, - data_ptr as *mut u8, - length as usize, + Ok(JsBufferValue::new( + JsBuffer(Value { + env: self.0, + value: raw_value, + value_type: ValueType::Object, + }), + data, )) } - pub fn create_buffer_with_data<'env, 'buffer>( - &'env self, - mut data: Vec, - ) -> Result> { - let length = data.len() as u64; + pub fn create_buffer_with_data(&self, mut data: Vec) -> Result { + let mut length = data.len() as u64; let mut raw_value = ptr::null_mut(); let data_ptr = data.as_mut_ptr(); check_status(unsafe { @@ -249,14 +248,20 @@ impl Env { length, data_ptr as *mut c_void, Some(drop_buffer), - Box::leak(Box::new(length)) as *mut u64 as *mut _, + &mut length as *mut u64 as *mut _, &mut raw_value, ) })?; let mut changed = 0; check_status(unsafe { sys::napi_adjust_external_memory(self.0, length as i64, &mut changed) })?; - mem::forget(data); - Ok(JsBuffer::new(self.0, raw_value, data_ptr, length as usize)) + Ok(JsBufferValue::new( + JsBuffer(Value { + env: self.0, + value: raw_value, + value_type: ValueType::Object, + }), + data, + )) } pub fn create_arraybuffer(&self, length: u64) -> Result { @@ -328,25 +333,6 @@ impl Env { }) } - pub fn create_reference(&self, value: T) -> Result> { - let mut raw_ref = ptr::null_mut(); - let initial_ref_count = 1; - check_status(unsafe { - sys::napi_create_reference(self.0, value.raw_value(), initial_ref_count, &mut raw_ref) - })?; - - Ok(Ref::new(self.0, raw_ref)) - } - - pub fn get_reference_value(&self, reference: &Ref) -> Result { - let mut raw_value = ptr::null_mut(); - check_status(unsafe { - sys::napi_get_reference_value(self.0, reference.ref_value, &mut raw_value) - })?; - - T::from_raw(self.0, raw_value) - } - pub fn define_class( &self, name: &str, @@ -491,6 +477,19 @@ impl Env { async_work::run(self, task) } + pub fn run_in_scope(&self, executor: F) -> Result + where + F: FnOnce() -> Result, + { + let mut handle_scope = ptr::null_mut(); + check_status(unsafe { sys::napi_open_handle_scope(self.0, &mut handle_scope) })?; + + let result = executor(); + + check_status(unsafe { sys::napi_close_handle_scope(self.0, handle_scope) })?; + result + } + pub fn get_global(&self) -> Result { let mut raw_global = ptr::null_mut(); check_status(unsafe { sys::napi_get_global(self.0, &mut raw_global) })?; @@ -503,6 +502,7 @@ impl Env { let versions: JsObject = process.get_named_property("versions")?; let napi_version: JsString = versions.get_named_property("napi")?; napi_version + .into_utf8()? .as_str()? .parse() .map_err(|e| Error::new(Status::InvalidArg, format!("{}", e))) @@ -622,7 +622,7 @@ impl Env { { let value = Value { env: self.0, - value: value.raw_value(), + value: value.raw(), value_type: ValueType::Unknown, }; let mut de = De(&value); @@ -631,9 +631,7 @@ impl Env { pub fn strict_equals(&self, a: A, b: B) -> Result { let mut result = false; - check_status(unsafe { - sys::napi_strict_equals(self.0, a.raw_value(), b.raw_value(), &mut result) - })?; + check_status(unsafe { sys::napi_strict_equals(self.0, a.raw(), b.raw(), &mut result) })?; Ok(result) } @@ -646,7 +644,7 @@ impl Env { } unsafe extern "C" fn drop_buffer(env: sys::napi_env, finalize_data: *mut c_void, len: *mut c_void) { - let length = Box::from_raw(len as *mut u64); + let length = len as *mut u64; let length = *length as usize; let _ = Vec::from_raw_parts(finalize_data as *mut u8, length, length); let mut changed = 0; diff --git a/napi/src/js_values/arraybuffer.rs b/napi/src/js_values/arraybuffer.rs index 6a179089..d290aa1a 100644 --- a/napi/src/js_values/arraybuffer.rs +++ b/napi/src/js_values/arraybuffer.rs @@ -126,7 +126,7 @@ impl JsArrayBuffer { sys::napi_instanceof( self.value.0.env, self.value.0.value, - constructor.raw_value(), + constructor.raw(), &mut result, ) })?; @@ -135,7 +135,7 @@ impl JsArrayBuffer { } impl NapiValue for JsArrayBuffer { - fn raw_value(&self) -> sys::napi_value { + fn raw(&self) -> sys::napi_value { self.value.0.value } diff --git a/napi/src/js_values/bigint.rs b/napi/src/js_values/bigint.rs index 31221519..d094f9d0 100644 --- a/napi/src/js_values/bigint.rs +++ b/napi/src/js_values/bigint.rs @@ -117,19 +117,14 @@ impl JsBigint { pub fn instanceof(&self, constructor: Constructor) -> Result { let mut result = false; check_status(unsafe { - sys::napi_instanceof( - self.raw.env, - self.raw.value, - constructor.raw_value(), - &mut result, - ) + sys::napi_instanceof(self.raw.env, self.raw.value, constructor.raw(), &mut result) })?; Ok(result) } } impl NapiValue for JsBigint { - fn raw_value(&self) -> sys::napi_value { + fn raw(&self) -> sys::napi_value { self.raw.value } diff --git a/napi/src/js_values/boolean.rs b/napi/src/js_values/boolean.rs index d4f43cc2..e0d25bd5 100644 --- a/napi/src/js_values/boolean.rs +++ b/napi/src/js_values/boolean.rs @@ -4,6 +4,7 @@ use super::Value; use crate::error::check_status; use crate::{sys, Error, Result}; +#[repr(transparent)] #[derive(Debug)] pub struct JsBoolean(pub(crate) Value); diff --git a/napi/src/js_values/buffer.rs b/napi/src/js_values/buffer.rs index 37d10c4f..110d6ba6 100644 --- a/napi/src/js_values/buffer.rs +++ b/napi/src/js_values/buffer.rs @@ -1,194 +1,88 @@ -use std::convert::TryFrom; +use std::mem; use std::ops::Deref; use std::ptr; -use std::slice; -use super::{JsNumber, JsObject, JsString, JsUnknown, NapiValue, Status, Value, ValueType}; +use super::Value; +#[cfg(feature = "serde-json")] +use super::ValueType; use crate::error::check_status; -use crate::{sys, Error, Result}; +use crate::{sys, JsUnknown, Ref, Result}; + +#[repr(transparent)] +#[derive(Debug, Clone, Copy)] +pub struct JsBuffer(pub(crate) Value); #[derive(Debug)] -pub struct JsBuffer<'buffer> { - pub value: JsObject, - pub data: &'buffer [u8], +pub struct JsBufferValue { + pub(crate) value: JsBuffer, + data: mem::ManuallyDrop>, } -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, - value, - value_type: ValueType::Object, - }), - data: unsafe { slice::from_raw_parts_mut(data, len) }, - } - } - - #[inline] - pub fn into_unknown(self) -> Result { - JsUnknown::from_raw(self.value.0.env, self.value.0.value) - } - - #[inline] - pub fn coerce_to_number(self) -> Result { - let mut new_raw_value = ptr::null_mut(); - check_status(unsafe { - sys::napi_coerce_to_number(self.value.0.env, self.value.0.value, &mut new_raw_value) - })?; - Ok(JsNumber(Value { - env: self.value.0.env, - value: new_raw_value, - value_type: ValueType::Number, - })) - } - - #[inline] - pub fn coerce_to_string(self) -> Result { - let mut new_raw_value = ptr::null_mut(); - check_status(unsafe { - sys::napi_coerce_to_string(self.value.0.env, self.value.0.value, &mut new_raw_value) - })?; - Ok(JsString(Value { - env: self.value.0.env, - value: new_raw_value, - value_type: ValueType::String, - })) - } - #[inline] - pub fn coerce_to_object(self) -> Result { - let mut new_raw_value = ptr::null_mut(); - check_status(unsafe { - sys::napi_coerce_to_object(self.value.0.env, self.value.0.value, &mut new_raw_value) - })?; - Ok(JsObject(Value { - env: self.value.0.env, - value: new_raw_value, - value_type: ValueType::Object, - })) - } - - #[inline] - #[cfg(napi5)] - pub fn is_date(&self) -> Result { - let mut is_date = true; - check_status(unsafe { sys::napi_is_date(self.value.0.env, self.value.0.value, &mut is_date) })?; - Ok(is_date) - } - - #[inline] - pub fn is_error(&self) -> Result { - let mut result = false; - check_status(unsafe { sys::napi_is_error(self.value.0.env, self.value.0.value, &mut result) })?; - Ok(result) - } - - #[inline] - pub fn is_typedarray(&self) -> Result { - let mut result = false; - check_status(unsafe { - sys::napi_is_typedarray(self.value.0.env, self.value.0.value, &mut result) - })?; - Ok(result) - } - - #[inline] - pub fn is_dataview(&self) -> Result { - let mut result = false; - check_status(unsafe { - sys::napi_is_dataview(self.value.0.env, self.value.0.value, &mut result) - })?; - Ok(result) - } - - #[inline] - pub fn is_array(&self) -> Result { - let mut is_array = false; - check_status(unsafe { - sys::napi_is_array(self.value.0.env, self.value.0.value, &mut is_array) - })?; - Ok(is_array) - } - - #[inline] - pub fn is_buffer(&self) -> Result { - let mut is_buffer = false; - check_status(unsafe { - sys::napi_is_buffer(self.value.0.env, self.value.0.value, &mut is_buffer) - })?; - Ok(is_buffer) - } - - #[inline] - pub fn instanceof(&self, constructor: Constructor) -> Result { - let mut result = false; - check_status(unsafe { - sys::napi_instanceof( - self.value.0.env, - self.value.0.value, - constructor.raw_value(), - &mut result, - ) - })?; - Ok(result) - } -} - -impl<'buffer> NapiValue for JsBuffer<'buffer> { - fn raw_value(&self) -> sys::napi_value { - self.value.0.value - } - - fn from_raw(env: sys::napi_env, value: sys::napi_value) -> Result { +impl JsBuffer { + pub fn into_value(self) -> Result { let mut data = ptr::null_mut(); let mut len: u64 = 0; - check_status(unsafe { sys::napi_get_buffer_info(env, value, &mut data, &mut len) })?; - Ok(JsBuffer { - value: JsObject(Value { - env, - value, - value_type: ValueType::Object, + check_status(unsafe { + sys::napi_get_buffer_info(self.0.env, self.0.value, &mut data, &mut len) + })?; + Ok(JsBufferValue { + data: mem::ManuallyDrop::new(unsafe { + Vec::from_raw_parts(data as *mut _, len as usize, len as usize) }), - data: unsafe { slice::from_raw_parts_mut(data as *mut _, len as usize) }, + value: self, }) } - fn from_raw_unchecked(env: sys::napi_env, value: sys::napi_value) -> Self { + #[inline] + pub fn into_ref(self) -> Result> { + Ref::new(self.0, 1, self.into_value()?) + } +} + +impl JsBufferValue { + #[cfg(feature = "serde-json")] + pub(crate) fn from_raw(env: sys::napi_env, value: sys::napi_value) -> Result { 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) }; - debug_assert!( - Status::from(status) == Status::Ok, - "napi_get_buffer_info failed" - ); - JsBuffer { - value: JsObject(Value { + check_status(unsafe { sys::napi_get_buffer_info(env, value, &mut data, &mut len) })?; + Ok(Self { + value: JsBuffer(Value { env, value, value_type: ValueType::Object, }), - data: unsafe { slice::from_raw_parts_mut(data as *mut _, len as usize) }, + data: mem::ManuallyDrop::new(unsafe { + Vec::from_raw_parts(data as *mut _, len as usize, len as usize) + }), + }) + } + + pub fn new(value: JsBuffer, data: Vec) -> Self { + JsBufferValue { + value, + data: mem::ManuallyDrop::new(data), } } -} -impl<'buffer> AsRef<[u8]> for JsBuffer<'buffer> { - fn as_ref(&self) -> &[u8] { - self.data + pub fn into_raw(self) -> JsBuffer { + self.value + } + + pub fn into_unknown(self) -> Result { + self.value.into_unknown() } } -impl<'buffer> Deref for JsBuffer<'buffer> { +impl AsRef<[u8]> for JsBufferValue { + fn as_ref(&self) -> &[u8] { + self.data.as_slice() + } +} + +impl Deref for JsBufferValue { type Target = [u8]; fn deref(&self) -> &[u8] { - self.data - } -} - -impl<'buffer> TryFrom for JsBuffer<'buffer> { - type Error = Error; - fn try_from(value: JsUnknown) -> Result> { - JsBuffer::from_raw(value.0.env, value.0.value) + self.data.as_slice() } } diff --git a/napi/src/js_values/de.rs b/napi/src/js_values/de.rs index a565448a..f3f5a6c6 100644 --- a/napi/src/js_values/de.rs +++ b/napi/src/js_values/de.rs @@ -6,7 +6,9 @@ use serde::de::{DeserializeSeed, EnumAccess, MapAccess, SeqAccess, Unexpected, V use super::{type_of, NapiValue, Value, ValueType}; #[cfg(napi6)] use crate::JsBigint; -use crate::{Error, JsBoolean, JsBuffer, JsNumber, JsObject, JsString, JsUnknown, Result, Status}; +use crate::{ + Error, JsBoolean, JsBufferValue, JsNumber, JsObject, JsString, JsUnknown, Result, Status, +}; pub(crate) struct De<'env>(pub(crate) &'env Value); @@ -35,7 +37,7 @@ impl<'x, 'de, 'env> serde::de::Deserializer<'x> for &'de mut De<'env> { } ValueType::String => { let js_string = JsString::from_raw_unchecked(self.0.env, self.0.value); - visitor.visit_str(js_string.as_str()?) + visitor.visit_str(js_string.into_utf8()?.as_str()?) } ValueType::Object => { let js_object = JsObject::from_raw_unchecked(self.0.env, self.0.value); @@ -44,7 +46,7 @@ impl<'x, 'de, 'env> serde::de::Deserializer<'x> for &'de mut De<'env> { JsArrayAccess::new(&js_object, js_object.get_array_length_unchecked()?); visitor.visit_seq(&mut deserializer) } else if js_object.is_buffer()? { - visitor.visit_bytes(JsBuffer::from_raw(self.0.env, self.0.value)?.data) + visitor.visit_bytes(&JsBufferValue::from_raw(self.0.env, self.0.value)?) } else { let mut deserializer = JsObjectAccess::new(&js_object)?; visitor.visit_map(&mut deserializer) @@ -72,14 +74,14 @@ impl<'x, 'de, 'env> serde::de::Deserializer<'x> for &'de mut De<'env> { where V: Visitor<'x>, { - visitor.visit_bytes(JsBuffer::from_raw(self.0.env, self.0.value)?.data) + visitor.visit_bytes(&JsBufferValue::from_raw(self.0.env, self.0.value)?) } fn deserialize_byte_buf(self, visitor: V) -> Result where V: Visitor<'x>, { - visitor.visit_bytes(JsBuffer::from_raw(self.0.env, self.0.value)?.data) + visitor.visit_bytes(&JsBufferValue::from_raw(self.0.env, self.0.value)?) } fn deserialize_option(self, visitor: V) -> Result @@ -105,8 +107,8 @@ impl<'x, 'de, 'env> serde::de::Deserializer<'x> for &'de mut De<'env> { match js_value_type { ValueType::String => visitor.visit_enum(JsEnumAccess::new( JsString::from_raw_unchecked(self.0.env, self.0.value) - .as_str()? - .to_owned(), + .into_utf8()? + .to_owned()?, None, )), ValueType::Object => { @@ -124,7 +126,10 @@ impl<'x, 'de, 'env> serde::de::Deserializer<'x> for &'de mut De<'env> { } else { let key = properties.get_element::(0)?; let value: JsUnknown = js_object.get_property(&key)?; - visitor.visit_enum(JsEnumAccess::new(key.as_str()?.to_owned(), Some(&value.0))) + visitor.visit_enum(JsEnumAccess::new( + key.into_utf8()?.to_owned()?, + Some(&value.0), + )) } } _ => Err(Error::new( diff --git a/napi/src/js_values/either.rs b/napi/src/js_values/either.rs index 31cfa35a..015e6e1f 100644 --- a/napi/src/js_values/either.rs +++ b/napi/src/js_values/either.rs @@ -26,10 +26,10 @@ impl NapiValue for Either { Self::from_raw(env, value).unwrap() } - fn raw_value(&self) -> sys::napi_value { + fn raw(&self) -> sys::napi_value { match self { - Either::A(v) => v.raw_value(), - Either::B(v) => v.raw_value(), + Either::A(v) => v.raw(), + Either::B(v) => v.raw(), } } } diff --git a/napi/src/js_values/escapable_handle_scope.rs b/napi/src/js_values/escapable_handle_scope.rs new file mode 100644 index 00000000..d09a4480 --- /dev/null +++ b/napi/src/js_values/escapable_handle_scope.rs @@ -0,0 +1,39 @@ +use std::ops::Deref; +use std::ptr; + +use super::check_status; +use crate::{sys, Env, NapiValue, Result}; + +pub struct EscapableHandleScope { + handle_scope: sys::napi_escapable_handle_scope, + value: T, +} + +impl EscapableHandleScope { + #[inline] + pub fn open(env: sys::napi_env, value: T) -> Result { + let mut handle_scope = ptr::null_mut(); + check_status(unsafe { sys::napi_open_escapable_handle_scope(env, &mut handle_scope) })?; + let mut result = ptr::null_mut(); + check_status(unsafe { + sys::napi_escape_handle(env, handle_scope, NapiValue::raw(&value), &mut result) + })?; + Ok(Self { + value, + handle_scope, + }) + } + + #[must_use] + pub fn close(self, env: Env) -> Result<()> { + check_status(unsafe { sys::napi_close_escapable_handle_scope(env.0, self.handle_scope) }) + } +} + +impl Deref for EscapableHandleScope { + type Target = T; + + fn deref(&self) -> &T { + &self.value + } +} diff --git a/napi/src/js_values/function.rs b/napi/src/js_values/function.rs index a2190a3d..66b35884 100644 --- a/napi/src/js_values/function.rs +++ b/napi/src/js_values/function.rs @@ -5,6 +5,7 @@ use super::Value; use crate::error::check_status; use crate::{sys, Env, Error, JsObject, JsUnknown, NapiValue, Result, Status}; +#[repr(transparent)] #[derive(Debug)] pub struct JsFunction(pub(crate) Value); @@ -26,12 +27,12 @@ impl JsFunction { /// [napi_call_function](https://nodejs.org/api/n-api.html#n_api_napi_call_function) pub fn call(&self, this: Option<&JsObject>, args: &[JsUnknown]) -> Result { let raw_this = this - .map(|v| v.raw_value()) + .map(|v| v.raw()) .or_else(|| { Env::from_raw(self.0.env) .get_undefined() .ok() - .map(|u| u.raw_value()) + .map(|u| u.raw()) }) .ok_or(Error::new( Status::Unknown, diff --git a/napi/src/js_values/mod.rs b/napi/src/js_values/mod.rs index 3c26a78c..9ed42f30 100644 --- a/napi/src/js_values/mod.rs +++ b/napi/src/js_values/mod.rs @@ -15,6 +15,7 @@ mod bigint; mod boolean; mod buffer; mod either; +mod escapable_handle_scope; mod function; mod number; mod object; @@ -30,33 +31,38 @@ pub use arraybuffer::JsArrayBuffer; #[cfg(napi6)] pub use bigint::JsBigint; pub use boolean::JsBoolean; -pub use buffer::JsBuffer; +pub use buffer::*; #[cfg(feature = "serde-json")] pub(crate) use de::De; pub use either::Either; +pub use escapable_handle_scope::EscapableHandleScope; pub use function::JsFunction; pub use number::JsNumber; pub use object::JsObject; pub use object_property::Property; #[cfg(feature = "serde-json")] pub(crate) use ser::Ser; -pub use string::JsString; +pub use string::*; pub(crate) use tagged_object::TaggedObject; pub use undefined::JsUndefined; pub(crate) use value::Value; -pub(crate) use value_ref::Ref; +pub use value_ref::Ref; pub use value_type::ValueType; // Value types +#[repr(transparent)] #[derive(Debug)] pub struct JsUnknown(pub(crate) Value); +#[repr(transparent)] #[derive(Debug)] pub struct JsNull(pub(crate) Value); +#[repr(transparent)] #[derive(Debug)] pub struct JsSymbol(pub(crate) Value); +#[repr(transparent)] #[derive(Debug)] pub struct JsExternal(pub(crate) Value); @@ -88,7 +94,7 @@ macro_rules! impl_napi_value_trait { } } - fn raw_value(&self) -> sys::napi_value { + fn raw(&self) -> sys::napi_value { self.0.value } @@ -130,6 +136,7 @@ macro_rules! impl_js_value_methods { value_type: ValueType::Number, })) } + #[inline] pub fn coerce_to_string(self) -> Result { let mut new_raw_value = ptr::null_mut(); @@ -204,12 +211,7 @@ macro_rules! impl_js_value_methods { pub fn instanceof(&self, constructor: Constructor) -> Result { let mut result = false; check_status(unsafe { - sys::napi_instanceof( - self.0.env, - self.0.value, - constructor.raw_value(), - &mut result, - ) + sys::napi_instanceof(self.0.env, self.0.value, constructor.raw(), &mut result) })?; Ok(result) } @@ -222,13 +224,14 @@ pub trait NapiValue: Sized { fn from_raw_unchecked(env: sys::napi_env, value: sys::napi_value) -> Self; - fn raw_value(&self) -> sys::napi_value; + fn raw(&self) -> sys::napi_value; } impl_js_value_methods!(JsUnknown); impl_js_value_methods!(JsUndefined); impl_js_value_methods!(JsNull); impl_js_value_methods!(JsBoolean); +impl_js_value_methods!(JsBuffer); impl_js_value_methods!(JsNumber); impl_js_value_methods!(JsString); impl_js_value_methods!(JsObject); @@ -241,6 +244,7 @@ use ValueType::*; impl_napi_value_trait!(JsUndefined, Undefined); impl_napi_value_trait!(JsNull, Null); impl_napi_value_trait!(JsBoolean, Boolean); +impl_napi_value_trait!(JsBuffer, Object); impl_napi_value_trait!(JsNumber, Number); impl_napi_value_trait!(JsString, String); impl_napi_value_trait!(JsObject, Object); @@ -265,7 +269,7 @@ impl NapiValue for JsUnknown { }) } - fn raw_value(&self) -> sys::napi_value { + fn raw(&self) -> sys::napi_value { self.0.value } } diff --git a/napi/src/js_values/number.rs b/napi/src/js_values/number.rs index 0561cb7c..4b573025 100644 --- a/napi/src/js_values/number.rs +++ b/napi/src/js_values/number.rs @@ -4,6 +4,7 @@ use super::Value; use crate::error::check_status; use crate::{sys, Error, Result}; +#[repr(transparent)] #[derive(Debug)] pub struct JsNumber(pub(crate) Value); diff --git a/napi/src/js_values/object.rs b/napi/src/js_values/object.rs index bd320253..0a248f0a 100644 --- a/napi/src/js_values/object.rs +++ b/napi/src/js_values/object.rs @@ -5,6 +5,7 @@ use super::Value; use crate::error::check_status; use crate::{sys, Error, JsString, NapiValue, Property, Result, Status}; +#[repr(transparent)] #[derive(Debug)] pub struct JsObject(pub(crate) Value); @@ -14,7 +15,7 @@ impl JsObject { V: NapiValue, { check_status(unsafe { - sys::napi_set_property(self.0.env, self.0.value, key.0.value, value.raw_value()) + sys::napi_set_property(self.0.env, self.0.value, key.0.value, value.raw()) }) } @@ -25,7 +26,7 @@ impl JsObject { { let mut raw_value = ptr::null_mut(); check_status(unsafe { - sys::napi_get_property(self.0.env, self.0.value, key.raw_value(), &mut raw_value) + sys::napi_get_property(self.0.env, self.0.value, key.raw(), &mut raw_value) })?; T::from_raw(self.0.env, raw_value) } @@ -36,7 +37,7 @@ impl JsObject { { let key = CString::new(name)?; check_status(unsafe { - sys::napi_set_named_property(self.0.env, self.0.value, key.as_ptr(), value.raw_value()) + sys::napi_set_named_property(self.0.env, self.0.value, key.as_ptr(), value.raw()) }) } @@ -70,7 +71,7 @@ impl JsObject { { let mut result = false; check_status(unsafe { - sys::napi_delete_property(self.0.env, self.0.value, name.raw_value(), &mut result) + sys::napi_delete_property(self.0.env, self.0.value, name.raw(), &mut result) })?; Ok(result) } @@ -107,7 +108,7 @@ impl JsObject { { let mut result = false; check_status(unsafe { - sys::napi_has_own_property(self.0.env, self.0.value, key.raw_value(), &mut result) + sys::napi_has_own_property(self.0.env, self.0.value, key.raw(), &mut result) })?; Ok(result) } @@ -129,7 +130,7 @@ impl JsObject { { let mut result = false; check_status(unsafe { - sys::napi_has_property(self.0.env, self.0.value, name.raw_value(), &mut result) + sys::napi_has_property(self.0.env, self.0.value, name.raw(), &mut result) })?; Ok(result) } @@ -157,9 +158,7 @@ impl JsObject { where T: NapiValue, { - check_status(unsafe { - sys::napi_set_element(self.0.env, self.0.value, index, value.raw_value()) - }) + check_status(unsafe { sys::napi_set_element(self.0.env, self.0.value, index, value.raw()) }) } pub fn has_element(&self, index: u32) -> Result { @@ -215,7 +214,7 @@ impl JsObject { #[inline] pub fn get_array_length_unchecked(&self) -> Result { let mut length: u32 = 0; - check_status(unsafe { sys::napi_get_array_length(self.0.env, self.raw_value(), &mut length) })?; + check_status(unsafe { sys::napi_get_array_length(self.0.env, self.raw(), &mut length) })?; Ok(length) } } diff --git a/napi/src/js_values/object_property.rs b/napi/src/js_values/object_property.rs index 01d7d647..43a64606 100644 --- a/napi/src/js_values/object_property.rs +++ b/napi/src/js_values/object_property.rs @@ -55,7 +55,7 @@ impl<'env> Property<'env> { } pub fn with_value(mut self, value: T) -> Self { - self.raw_descriptor.value = T::raw_value(&value); + self.raw_descriptor.value = T::raw(&value); self } diff --git a/napi/src/js_values/string.rs b/napi/src/js_values/string.rs deleted file mode 100644 index 9065e5e4..00000000 --- a/napi/src/js_values/string.rs +++ /dev/null @@ -1,165 +0,0 @@ -use std::convert::TryFrom; -use std::mem; -use std::ptr; -use std::slice; -use std::str; - -#[cfg(feature = "latin1")] -use encoding_rs; - -use super::Value; -use crate::error::check_status; -use crate::{sys, Error, Result, Status}; - -#[derive(Debug)] -pub struct JsString(pub(crate) Value); - -impl JsString { - pub fn utf8_len(&self) -> Result { - let mut length = 0; - check_status(unsafe { - sys::napi_get_value_string_utf8(self.0.env, self.0.value, ptr::null_mut(), 0, &mut length) - })?; - Ok(length as usize) - } - - pub fn latin1_len(&self) -> Result { - let mut length = 0; - check_status(unsafe { - sys::napi_get_value_string_latin1(self.0.env, self.0.value, ptr::null_mut(), 0, &mut length) - })?; - Ok(length as usize) - } -} - -impl JsString { - pub fn get_utf8(&self) -> Result<&[u8]> { - let mut written_char_count: u64 = 0; - let len = self.utf8_len()? + 1; - let mut result = Vec::with_capacity(len); - unsafe { - check_status(sys::napi_get_value_string_utf8( - self.0.env, - self.0.value, - result.as_mut_ptr(), - len as u64, - &mut written_char_count, - ))?; - let ptr = result.as_ptr(); - mem::forget(result); - Ok(slice::from_raw_parts( - ptr as *const u8, - written_char_count as usize, - )) - } - } - - pub fn get_latin1(&self) -> Result<(&[u8], usize)> { - let mut written_char_count: u64 = 0; - let len = self.latin1_len()? + 1; - let mut result = Vec::with_capacity(len); - unsafe { - check_status(sys::napi_get_value_string_latin1( - self.0.env, - self.0.value, - result.as_mut_ptr(), - len as u64, - &mut written_char_count, - ))?; - let ptr = result.as_ptr(); - mem::forget(result); - Ok(( - slice::from_raw_parts(ptr as *const u8, written_char_count as usize), - written_char_count as usize, - )) - } - } - - #[inline] - pub fn get_utf8_chars(&self) -> Result<&[char]> { - let mut written_char_count: u64 = 0; - let len = self.utf8_len()? + 1; - let mut result = Vec::with_capacity(len); - unsafe { - check_status(sys::napi_get_value_string_utf8( - self.0.env, - self.0.value, - result.as_mut_ptr(), - len as u64, - &mut written_char_count, - ))?; - - let ptr = result.as_ptr(); - mem::forget(result); - Ok(slice::from_raw_parts( - ptr as *const char, - written_char_count as usize, - )) - } - } - - pub fn as_str(&self) -> Result<&str> { - str::from_utf8(self.get_utf8()?) - .map_err(|e| Error::new(Status::GenericFailure, format!("{:?}", e))) - } - - #[cfg(feature = "latin1")] - pub fn as_latin1_string(&self) -> Result { - let (latin1_bytes, len) = self.get_latin1()?; - let mut dst_str = unsafe { String::from_utf8_unchecked(vec![0; len * 2 + 1]) }; - encoding_rs::mem::convert_latin1_to_str(latin1_bytes, dst_str.as_mut_str()); - Ok(dst_str) - } - - pub fn get_ref_mut(&mut self) -> Result<&mut [u8]> { - let mut written_char_count: u64 = 0; - let len = self.utf8_len()? + 1; - let mut result = Vec::with_capacity(len); - unsafe { - check_status(sys::napi_get_value_string_utf8( - self.0.env, - self.0.value, - result.as_mut_ptr(), - len as u64, - &mut written_char_count, - ))?; - - let ptr = result.as_ptr(); - mem::forget(result); - Ok(slice::from_raw_parts_mut( - ptr as *mut _, - written_char_count as usize, - )) - } - } -} - -impl TryFrom for Vec { - type Error = Error; - - fn try_from(value: JsString) -> Result> { - let mut result = Vec::with_capacity(value.utf8_len()? + 1); // Leave room for trailing null byte - - unsafe { - let mut written_char_count = 0; - check_status(sys::napi_get_value_string_utf16( - value.0.env, - value.0.value, - result.as_mut_ptr(), - result.capacity() as u64, - &mut written_char_count, - ))?; - result.set_len(written_char_count as usize); - } - - Ok(result) - } -} - -impl TryFrom for String { - type Error = Error; - - fn try_from(value: JsString) -> Result { - Ok(value.as_str()?.to_owned()) - } -} diff --git a/napi/src/js_values/string/latin1.rs b/napi/src/js_values/string/latin1.rs new file mode 100644 index 00000000..a5cc564b --- /dev/null +++ b/napi/src/js_values/string/latin1.rs @@ -0,0 +1,45 @@ +use std::mem::ManuallyDrop; + +#[cfg(feature = "latin1")] +use encoding_rs; + +use crate::JsString; + +#[cfg(feature = "latin1")] +use crate::Result; + +pub struct JsStringLatin1 { + pub(crate) inner: JsString, + pub(crate) buf: ManuallyDrop>, +} + +impl JsStringLatin1 { + pub fn as_slice(&self) -> &[u8] { + &self.buf + } + + pub fn len(&self) -> usize { + self.buf.len() + } + + pub fn take(self) -> Vec { + self.as_slice().to_vec() + } + + pub fn into_value(self) -> JsString { + self.inner + } + + #[cfg(feature = "latin1")] + pub fn into_latin1_string(self) -> Result { + let mut dst_str = unsafe { String::from_utf8_unchecked(vec![0; self.len() * 2 + 1]) }; + encoding_rs::mem::convert_latin1_to_str(self.buf.as_slice(), dst_str.as_mut_str()); + Ok(dst_str) + } +} + +impl From for Vec { + fn from(value: JsStringLatin1) -> Self { + value.take() + } +} diff --git a/napi/src/js_values/string/mod.rs b/napi/src/js_values/string/mod.rs new file mode 100644 index 00000000..136ad453 --- /dev/null +++ b/napi/src/js_values/string/mod.rs @@ -0,0 +1,138 @@ +use std::mem; +use std::ptr; + +use super::Value; +use crate::error::check_status; +use crate::{sys, Ref, Result}; + +pub use latin1::JsStringLatin1; +pub use utf16::JsStringUtf16; +pub use utf8::JsStringUtf8; + +mod latin1; +mod utf16; +mod utf8; + +#[repr(transparent)] +#[derive(Debug, Clone, Copy)] +pub struct JsString(pub(crate) Value); + +impl JsString { + pub fn utf8_len(&self) -> Result { + let mut length = 0; + check_status(unsafe { + sys::napi_get_value_string_utf8(self.0.env, self.0.value, ptr::null_mut(), 0, &mut length) + })?; + Ok(length as usize) + } + + pub fn utf16_len(&self) -> Result { + let mut length = 0; + check_status(unsafe { + sys::napi_get_value_string_utf16(self.0.env, self.0.value, ptr::null_mut(), 0, &mut length) + })?; + Ok(length as usize) + } + + pub fn latin1_len(&self) -> Result { + let mut length = 0; + check_status(unsafe { + sys::napi_get_value_string_latin1(self.0.env, self.0.value, ptr::null_mut(), 0, &mut length) + })?; + Ok(length as usize) + } + + pub fn into_utf8(self) -> Result { + let mut written_char_count: u64 = 0; + let len = self.utf8_len()? + 1; + let mut result = Vec::with_capacity(len); + let buf_ptr = result.as_mut_ptr(); + check_status(unsafe { + sys::napi_get_value_string_utf8( + self.0.env, + self.0.value, + buf_ptr, + len as u64, + &mut written_char_count, + ) + })?; + + mem::forget(result); + + Ok(JsStringUtf8 { + inner: self, + buf: mem::ManuallyDrop::new(unsafe { + Vec::from_raw_parts( + buf_ptr as *mut _, + written_char_count as _, + written_char_count as _, + ) + }), + }) + } + + pub fn into_utf8_ref(self) -> Result> { + Ref::new(self.0, 1, self.into_utf8()?) + } + + pub fn into_utf16(self) -> Result { + let mut written_char_count: u64 = 0; + let len = self.utf16_len()? + 1; + let mut result = Vec::with_capacity(len); + let buf_ptr = result.as_mut_ptr(); + check_status(unsafe { + sys::napi_get_value_string_utf16( + self.0.env, + self.0.value, + buf_ptr, + len as u64, + &mut written_char_count, + ) + })?; + mem::forget(result); + + Ok(JsStringUtf16 { + inner: self, + buf: mem::ManuallyDrop::new(unsafe { + Vec::from_raw_parts(buf_ptr, written_char_count as _, written_char_count as _) + }), + }) + } + + pub fn into_utf16_ref(self) -> Result> { + Ref::new(self.0, 1, self.into_utf16()?) + } + + pub fn into_latin1(self) -> Result { + let mut written_char_count: u64 = 0; + let len = self.latin1_len()? + 1; + let mut result = Vec::with_capacity(len); + let buf_ptr = result.as_mut_ptr(); + check_status(unsafe { + sys::napi_get_value_string_latin1( + self.0.env, + self.0.value, + buf_ptr, + len as u64, + &mut written_char_count, + ) + })?; + + mem::forget(result); + + Ok(JsStringLatin1 { + inner: self, + buf: mem::ManuallyDrop::new(unsafe { + Vec::from_raw_parts( + buf_ptr as *mut _, + written_char_count as _, + written_char_count as _, + ) + }), + }) + } + + pub fn into_latin1_ref(self) -> Result> { + Ref::new(self.0, 1, self.into_latin1()?) + } +} diff --git a/napi/src/js_values/string/utf16.rs b/napi/src/js_values/string/utf16.rs new file mode 100644 index 00000000..250593ff --- /dev/null +++ b/napi/src/js_values/string/utf16.rs @@ -0,0 +1,57 @@ +use std::convert::TryFrom; +use std::mem; +use std::ops::Deref; + +use crate::{Error, JsString, Result, Status}; + +pub struct JsStringUtf16 { + pub(crate) inner: JsString, + pub(crate) buf: mem::ManuallyDrop>, +} + +impl JsStringUtf16 { + pub fn as_str(&self) -> Result { + String::from_utf16(self.as_slice()) + .map_err(|e| Error::new(Status::InvalidArg, format!("{}", e))) + } + + pub fn as_slice(&self) -> &[u16] { + self.buf.as_slice() + } + + pub fn len(&self) -> usize { + self.buf.len() + } + + pub fn into_value(self) -> JsString { + self.inner + } +} + +impl TryFrom for String { + type Error = Error; + + fn try_from(value: JsStringUtf16) -> Result { + value.as_str() + } +} + +impl Deref for JsStringUtf16 { + type Target = [u16]; + + fn deref(&self) -> &[u16] { + self.buf.as_slice() + } +} + +impl AsRef> for JsStringUtf16 { + fn as_ref(&self) -> &Vec { + &self.buf + } +} + +impl From for Vec { + fn from(value: JsStringUtf16) -> Self { + value.as_slice().to_vec() + } +} diff --git a/napi/src/js_values/string/utf8.rs b/napi/src/js_values/string/utf8.rs new file mode 100644 index 00000000..6484d646 --- /dev/null +++ b/napi/src/js_values/string/utf8.rs @@ -0,0 +1,51 @@ +use std::convert::TryFrom; +use std::mem::ManuallyDrop; +use std::str; + +use crate::{Error, JsString, Result, Status}; + +pub struct JsStringUtf8 { + pub(crate) inner: JsString, + pub(crate) buf: ManuallyDrop>, +} + +impl JsStringUtf8 { + pub fn as_str(&self) -> Result<&str> { + str::from_utf8(self.buf.as_slice()) + .map_err(|e| Error::new(Status::InvalidArg, format!("{}", e))) + } + + pub fn as_slice(&self) -> &[u8] { + &self.buf + } + + pub fn len(&self) -> usize { + self.buf.len() + } + + pub fn to_owned(self) -> Result { + Ok(self.as_str()?.to_owned()) + } + + pub fn take(self) -> Vec { + self.as_slice().to_vec() + } + + pub fn into_value(self) -> JsString { + self.inner + } +} + +impl TryFrom for String { + type Error = Error; + + fn try_from(value: JsStringUtf8) -> Result { + Ok(value.as_str()?.to_owned()) + } +} + +impl From for Vec { + fn from(value: JsStringUtf8) -> Self { + value.take() + } +} diff --git a/napi/src/js_values/undefined.rs b/napi/src/js_values/undefined.rs index 4f32b746..6fb90619 100644 --- a/napi/src/js_values/undefined.rs +++ b/napi/src/js_values/undefined.rs @@ -1,4 +1,5 @@ use super::Value; +#[repr(transparent)] #[derive(Debug)] pub struct JsUndefined(pub(crate) Value); diff --git a/napi/src/js_values/value_ref.rs b/napi/src/js_values/value_ref.rs index 0e4dd2e2..c130e9d9 100644 --- a/napi/src/js_values/value_ref.rs +++ b/napi/src/js_values/value_ref.rs @@ -1,35 +1,62 @@ -use std::marker::PhantomData; +use std::ops::Deref; +use std::ptr; -use super::NapiValue; -use crate::{sys, Status}; +use super::{check_status, Value}; +use crate::{sys, Env, Result}; -pub struct Ref { - pub(crate) raw_env: sys::napi_env, - pub(crate) ref_value: sys::napi_ref, - _phantom: PhantomData, +pub struct Ref { + pub(crate) raw_ref: sys::napi_ref, + count: u32, + inner: T, } -impl Ref { - pub fn new(raw_env: sys::napi_env, ref_value: sys::napi_ref) -> Ref { - Ref { - raw_env, - ref_value, - _phantom: PhantomData, +unsafe impl Send for Ref {} +unsafe impl Sync for Ref {} + +impl Ref { + pub(crate) fn new(js_value: Value, ref_count: u32, inner: T) -> Result> { + let mut raw_ref = ptr::null_mut(); + let initial_ref_count = 1; + check_status(unsafe { + sys::napi_create_reference( + js_value.env, + js_value.value, + initial_ref_count, + &mut raw_ref, + ) + })?; + Ok(Ref { + raw_ref, + count: ref_count, + inner, + }) + } + + #[must_use] + pub fn unref(mut self, env: Env) -> Result { + check_status(unsafe { sys::napi_reference_unref(env.0, self.raw_ref, &mut self.count) })?; + + if self.count == 0 { + check_status(unsafe { sys::napi_delete_reference(env.0, self.raw_ref) })?; } + Ok(self.count) } } -impl Drop for Ref { +impl Deref for Ref { + type Target = T; + + fn deref(&self) -> &T { + &self.inner + } +} + +#[cfg(debug_assertions)] +impl Drop for Ref { fn drop(&mut self) { - unsafe { - let mut ref_count = 0; - let status = sys::napi_reference_unref(self.raw_env, self.ref_value, &mut ref_count); - debug_assert!(Status::from(status) == Status::Ok); - - if ref_count == 0 { - let status = sys::napi_delete_reference(self.raw_env, self.ref_value); - debug_assert!(Status::from(status) == Status::Ok); - } - } + debug_assert_eq!( + self.count, 0, + "Ref count is not equal to 0 while dropping Ref, potential memory leak" + ); } } diff --git a/napi/src/lib.rs b/napi/src/lib.rs index 586dfae0..8eb69b0a 100644 --- a/napi/src/lib.rs +++ b/napi/src/lib.rs @@ -199,7 +199,7 @@ macro_rules! register_module { let hook_result = Ok(()); match hook_result.and_then(move |_| result) { - Ok(_) => cjs_module.exports.raw_value(), + Ok(_) => cjs_module.exports.raw(), Err(e) => { unsafe { sys::napi_throw_error( diff --git a/napi/src/promise.rs b/napi/src/promise.rs index e222d477..0ea2abb7 100644 --- a/napi/src/promise.rs +++ b/napi/src/promise.rs @@ -106,7 +106,7 @@ unsafe extern "C" fn call_js_cb( let js_value_to_resolve = value.and_then(move |v| (resolver)(&mut env, v)); match js_value_to_resolve { Ok(v) => { - let status = sys::napi_resolve_deferred(raw_env, deferred, v.raw_value()); + let status = sys::napi_resolve_deferred(raw_env, deferred, v.raw()); debug_assert!( status == sys::napi_status::napi_ok, "Resolve promise failed" diff --git a/napi/src/task.rs b/napi/src/task.rs index af36c6f6..428f0d41 100644 --- a/napi/src/task.rs +++ b/napi/src/task.rs @@ -7,5 +7,5 @@ pub trait Task: Send { fn compute(&mut self) -> Result; - fn resolve(&self, env: &mut Env, output: Self::Output) -> Result; + fn resolve(self, env: Env, output: Self::Output) -> Result; } diff --git a/test_module/__test__/spawn.spec.ts b/test_module/__test__/spawn.spec.ts index 41ffd065..70420ba5 100644 --- a/test_module/__test__/spawn.spec.ts +++ b/test_module/__test__/spawn.spec.ts @@ -6,3 +6,9 @@ test('should be able to spawn thread and return promise', async (t) => { const result = await bindings.testSpawnThread(20) t.is(result, 6765) }) + +test('should be able to spawn thread with ref value', async (t) => { + const fixture = 'hello' + const result = await bindings.testSpawnThreadWithRef(Buffer.from(fixture)) + t.is(result, fixture.length) +}) diff --git a/test_module/src/buffer.rs b/test_module/src/buffer.rs index 9b37edf0..9a0d4959 100644 --- a/test_module/src/buffer.rs +++ b/test_module/src/buffer.rs @@ -4,13 +4,13 @@ use napi::{CallContext, Error, JsBuffer, JsNumber, JsString, Module, Result, Sta #[js_function(1)] pub fn get_buffer_length(ctx: CallContext) -> Result { - let buffer = ctx.get::(0)?; + let buffer = ctx.get::(0)?.into_value()?; ctx.env.create_uint32((&buffer).len() as u32) } #[js_function(1)] pub fn buffer_to_string(ctx: CallContext) -> Result { - let buffer = ctx.get::(0)?; + let buffer = ctx.get::(0)?.into_value()?; ctx.env.create_string( str::from_utf8(&buffer).map_err(|e| Error::new(Status::StringExpected, format!("{}", e)))?, ) diff --git a/test_module/src/either.rs b/test_module/src/either.rs index 195b1ab7..c9eb7240 100644 --- a/test_module/src/either.rs +++ b/test_module/src/either.rs @@ -11,7 +11,7 @@ pub fn either_number_string(ctx: CallContext) -> Result { - let content = format!("Either::B({})", s.as_str()?); + let content = format!("Either::B({})", s.into_utf8()?.as_str()?); ctx.env.create_string_from_std(content).map(Either::B) } } diff --git a/test_module/src/error.rs b/test_module/src/error.rs index b833eb65..27b6cd3e 100644 --- a/test_module/src/error.rs +++ b/test_module/src/error.rs @@ -10,7 +10,7 @@ fn test_throw_with_reason(ctx: CallContext) -> Result { let reason = ctx.get::(0)?; Err(Error::new( Status::GenericFailure, - reason.as_str()?.to_owned(), + reason.into_utf8()?.as_str()?.to_owned(), )) } diff --git a/test_module/src/libuv/read_file.rs b/test_module/src/libuv/read_file.rs index cb4b0e75..5f0537f2 100644 --- a/test_module/src/libuv/read_file.rs +++ b/test_module/src/libuv/read_file.rs @@ -9,7 +9,7 @@ use napi::{CallContext, Error, JsObject, JsString, Module, Result, Status}; pub fn uv_read_file(ctx: CallContext) -> Result { let path = ctx.get::(0)?; let (sender, receiver) = oneshot::channel(); - let p = path.as_str()?.to_owned(); + let p = path.into_utf8()?.to_owned()?; thread::spawn(|| { let res = fs::read(p).map_err(|e| Error::new(Status::Unknown, format!("{}", e))); sender.send(res).expect("Send data failed"); @@ -18,7 +18,7 @@ pub fn uv_read_file(ctx: CallContext) -> Result { receiver .map_err(|e| Error::new(Status::Unknown, format!("{}", e))) .map(|x| x.and_then(|x| x)), - |&mut env, data| env.create_buffer_with_data(data), + |&mut env, data| env.create_buffer_with_data(data).map(|v| v.into_raw()), ) } diff --git a/test_module/src/napi4/tsfn.rs b/test_module/src/napi4/tsfn.rs index 777795fa..2281f7ef 100644 --- a/test_module/src/napi4/tsfn.rs +++ b/test_module/src/napi4/tsfn.rs @@ -85,22 +85,21 @@ impl ToJs for HandleBuffer { async fn read_file_content(filepath: &Path) -> Result> { tokio::fs::read(filepath) .await - .map_err(|_| Error::new(Status::Unknown, "failed to read file".to_owned())) + .map_err(|e| Error::new(Status::Unknown, format!("{}", e))) } #[js_function(2)] pub fn test_tokio_readfile(ctx: CallContext) -> Result { let js_filepath = ctx.get::(0)?; let js_func = ctx.get::(1)?; - let path_str = js_filepath.as_str()?; + let path_str = js_filepath.into_utf8()?.to_owned()?; let to_js = HandleBuffer; let tsfn = ThreadsafeFunction::create(ctx.env, js_func, to_js, 0)?; let mut rt = tokio::runtime::Runtime::new().unwrap(); rt.block_on(async move { - let mut filepath = Path::new(path_str); - let ret = read_file_content(&mut filepath).await; + let ret = read_file_content(&Path::new(&path_str)).await; let _ = tsfn.call(ret, ThreadsafeFunctionCallMode::Blocking); tsfn .release(ThreadsafeFunctionReleaseMode::Release) diff --git a/test_module/src/object.rs b/test_module/src/object.rs index 710787ed..63c09e60 100644 --- a/test_module/src/object.rs +++ b/test_module/src/object.rs @@ -31,14 +31,18 @@ fn test_get_named_property(ctx: CallContext) -> Result { fn test_has_named_property(ctx: CallContext) -> Result { let obj = ctx.get::(0)?; let key = ctx.get::(1)?; - ctx.env.get_boolean(obj.has_named_property(key.as_str()?)?) + ctx + .env + .get_boolean(obj.has_named_property(key.into_utf8()?.as_str()?)?) } #[js_function(2)] fn test_has_own_property(ctx: CallContext) -> Result { let obj = ctx.get::(0)?; let key = ctx.get::(1)?; - ctx.env.get_boolean(obj.has_own_property(key.as_str()?)?) + ctx + .env + .get_boolean(obj.has_own_property(key.into_utf8()?.as_str()?)?) } #[js_function(2)] @@ -52,7 +56,9 @@ fn test_has_own_property_js(ctx: CallContext) -> Result { fn test_has_property(ctx: CallContext) -> Result { let obj = ctx.get::(0)?; let key = ctx.get::(1)?; - ctx.env.get_boolean(obj.has_property(key.as_str()?)?) + ctx + .env + .get_boolean(obj.has_property(key.into_utf8()?.as_str()?)?) } #[js_function(2)] @@ -75,7 +81,7 @@ fn test_delete_named_property(ctx: CallContext) -> Result { let key = ctx.get::(1)?; ctx .env - .get_boolean(obj.delete_named_property(key.as_str()?)?) + .get_boolean(obj.delete_named_property(key.into_utf8()?.as_str()?)?) } #[js_function(2)] diff --git a/test_module/src/string.rs b/test_module/src/string.rs index 72197584..02d14190 100644 --- a/test_module/src/string.rs +++ b/test_module/src/string.rs @@ -3,14 +3,17 @@ use napi::{CallContext, JsString, Module, Result}; #[js_function(1)] fn concat_string(ctx: CallContext) -> Result { let in_string = ctx.get::(0)?; - let out_string = format!("{} + Rust 🦀 string!", in_string.as_str()?); + let out_string = format!("{} + Rust 🦀 string!", in_string.into_utf8()?.as_str()?); ctx.env.create_string_from_std(out_string) } #[js_function(1)] fn concat_latin1_string(ctx: CallContext) -> Result { let in_string = ctx.get::(0)?; - let out_string = format!("{} + Rust 🦀 string!", in_string.as_latin1_string()?); + let out_string = format!( + "{} + Rust 🦀 string!", + in_string.into_latin1()?.into_latin1_string()? + ); ctx.env.create_string_from_std(out_string) } diff --git a/test_module/src/task.rs b/test_module/src/task.rs index ebe32450..8566372a 100644 --- a/test_module/src/task.rs +++ b/test_module/src/task.rs @@ -1,6 +1,8 @@ use std::convert::TryInto; -use napi::{CallContext, Env, JsNumber, JsObject, Module, Result, Task}; +use napi::{ + CallContext, Env, JsBuffer, JsBufferValue, JsNumber, JsObject, Module, Ref, Result, Task, +}; struct ComputeFib { n: u32, @@ -20,7 +22,7 @@ impl Task for ComputeFib { Ok(fibonacci_native(self.n)) } - fn resolve(&self, env: &mut Env, output: Self::Output) -> Result { + fn resolve(self, env: Env, output: Self::Output) -> Result { env.create_uint32(output) } } @@ -41,7 +43,40 @@ fn test_spawn_thread(ctx: CallContext) -> Result { Ok(async_promise.promise_object()) } +struct CountBufferLength { + data: Ref, +} + +impl CountBufferLength { + pub fn new(data: Ref) -> Self { + Self { data } + } +} + +impl Task for CountBufferLength { + type Output = usize; + type JsValue = JsNumber; + + fn compute(&mut self) -> Result { + Ok((&self.data).len()) + } + + fn resolve(self, env: Env, output: Self::Output) -> Result { + self.data.unref(env)?; + env.create_uint32(output as _) + } +} + +#[js_function(1)] +fn test_spawn_thread_with_ref(ctx: CallContext) -> Result { + let n = ctx.get::(0)?.into_ref()?; + let task = CountBufferLength::new(n); + let async_work_promise = ctx.env.spawn(task)?; + Ok(async_work_promise.promise_object()) +} + pub fn register_js(module: &mut Module) -> Result<()> { module.create_named_method("testSpawnThread", test_spawn_thread)?; + module.create_named_method("testSpawnThreadWithRef", test_spawn_thread_with_ref)?; Ok(()) } diff --git a/test_module/src/tokio_rt/read_file.rs b/test_module/src/tokio_rt/read_file.rs index de270e3b..bd013f23 100644 --- a/test_module/src/tokio_rt/read_file.rs +++ b/test_module/src/tokio_rt/read_file.rs @@ -5,20 +5,20 @@ use tokio; #[js_function(1)] pub fn test_execute_tokio_readfile(ctx: CallContext) -> Result { let js_filepath = ctx.get::(0)?; - let path_str = js_filepath.as_str()?; + let path_str = js_filepath.into_utf8()?.to_owned()?; ctx.env.execute_tokio_future( - tokio::fs::read(path_str.to_owned()) + tokio::fs::read(path_str) .map(|v| v.map_err(|e| Error::new(Status::Unknown, format!("failed to read file, {}", e)))), - |&mut env, data| env.create_buffer_with_data(data), + |&mut env, data| env.create_buffer_with_data(data).map(|v| v.into_raw()), ) } #[js_function(1)] pub fn error_from_tokio_future(ctx: CallContext) -> Result { let js_filepath = ctx.get::(0)?; - let path_str = js_filepath.as_str()?; + let path_str = js_filepath.into_utf8()?.to_owned()?; ctx.env.execute_tokio_future( - tokio::fs::read(path_str.to_owned()) + tokio::fs::read(path_str) .map_err(Error::from) .and_then(|_| async move { Err::, Error>(Error::new( @@ -26,6 +26,6 @@ pub fn error_from_tokio_future(ctx: CallContext) -> Result { "Error from tokio future".to_owned(), )) }), - |&mut env, data| env.create_buffer_with_data(data), + |&mut env, data| env.create_buffer_with_data(data).map(|v| v.into_raw()), ) }