refactor(napi): scope and Ref

This commit is contained in:
LongYinan 2020-09-28 00:27:37 +08:00 committed by LongYinan
parent 4460f43be8
commit 9c921ffaa3
No known key found for this signature in database
GPG key ID: A3FFE134A3E20881
36 changed files with 619 additions and 474 deletions

View file

@ -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<JsBufferValue>);
impl Task for BufferLength {
type Output = usize;
type JsValue = JsNumber;
fn compute(&mut self) -> Result<Self::Output> {
Ok(self.0.len())
Ok((&self.0).len())
}
fn resolve(&self, env: &mut Env, output: Self::Output) -> Result<Self::JsValue> {
fn resolve(self, env: Env, output: Self::Output) -> Result<Self::JsValue> {
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<JsObject> {
let n = ctx.get::<JsBuffer>(0)?;
let task = BufferLength(n.data);
let task = BufferLength(n.into_ref()?);
let async_promise = ctx.env.spawn(task)?;
Ok(async_promise.promise_object())
}

View file

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

View file

@ -80,6 +80,8 @@ unsafe impl<T: Task> Send for AsyncWork<T> {}
unsafe impl<T: Task> Sync for AsyncWork<T> {}
/// 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<T: Task>(_env: sys::napi_env, data: *mut c_void) {
let mut work = Box::from_raw(data as *mut AsyncWork<T>);
let _ = mem::replace(
@ -100,11 +102,11 @@ unsafe extern "C" fn complete<T: Task>(
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) => {

View file

@ -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<JsBuffer<'buffer>> {
pub fn create_buffer(&self, length: u64) -> Result<JsBufferValue> {
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<u8> = 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<u8>,
) -> Result<JsBuffer<'buffer>> {
let length = data.len() as u64;
pub fn create_buffer_with_data(&self, mut data: Vec<u8>) -> Result<JsBufferValue> {
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<JsArrayBuffer> {
@ -328,25 +333,6 @@ impl Env {
})
}
pub fn create_reference<T: NapiValue>(&self, value: T) -> Result<Ref<T>> {
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<T: NapiValue>(&self, reference: &Ref<T>) -> Result<T> {
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<T, F>(&self, executor: F) -> Result<T>
where
F: FnOnce() -> Result<T>,
{
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<JsObject> {
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<A: NapiValue, B: NapiValue>(&self, a: A, b: B) -> Result<bool> {
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;

View file

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

View file

@ -117,19 +117,14 @@ impl JsBigint {
pub fn instanceof<Constructor: NapiValue>(&self, constructor: Constructor) -> Result<bool> {
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
}

View file

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

View file

@ -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<Vec<u8>>,
}
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> {
JsUnknown::from_raw(self.value.0.env, self.value.0.value)
}
#[inline]
pub fn coerce_to_number(self) -> Result<JsNumber> {
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<JsString> {
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<JsObject> {
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<bool> {
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<bool> {
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<bool> {
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<bool> {
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<bool> {
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<bool> {
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<Constructor: NapiValue>(&self, constructor: Constructor) -> Result<bool> {
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<Self> {
impl JsBuffer {
pub fn into_value(self) -> Result<JsBufferValue> {
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<JsBufferValue>> {
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<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) };
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<u8>) -> 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<JsUnknown> {
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<JsUnknown> for JsBuffer<'buffer> {
type Error = Error;
fn try_from(value: JsUnknown) -> Result<JsBuffer<'buffer>> {
JsBuffer::from_raw(value.0.env, value.0.value)
self.data.as_slice()
}
}

View file

@ -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<V>(self, visitor: V) -> Result<V::Value>
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<V>(self, visitor: V) -> Result<V::Value>
@ -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::<JsString>(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(

View file

@ -26,10 +26,10 @@ impl<A: NapiValue, B: NapiValue> NapiValue for Either<A, B> {
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(),
}
}
}

View file

@ -0,0 +1,39 @@
use std::ops::Deref;
use std::ptr;
use super::check_status;
use crate::{sys, Env, NapiValue, Result};
pub struct EscapableHandleScope<T: NapiValue> {
handle_scope: sys::napi_escapable_handle_scope,
value: T,
}
impl<T: NapiValue> EscapableHandleScope<T> {
#[inline]
pub fn open(env: sys::napi_env, value: T) -> Result<Self> {
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<T: NapiValue> Deref for EscapableHandleScope<T> {
type Target = T;
fn deref(&self) -> &T {
&self.value
}
}

View file

@ -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<JsUnknown> {
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,

View file

@ -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<JsString> {
let mut new_raw_value = ptr::null_mut();
@ -204,12 +211,7 @@ macro_rules! impl_js_value_methods {
pub fn instanceof<Constructor: NapiValue>(&self, constructor: Constructor) -> Result<bool> {
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
}
}

View file

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

View file

@ -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<bool> {
@ -215,7 +214,7 @@ impl JsObject {
#[inline]
pub fn get_array_length_unchecked(&self) -> Result<u32> {
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)
}
}

View file

@ -55,7 +55,7 @@ impl<'env> Property<'env> {
}
pub fn with_value<T: NapiValue>(mut self, value: T) -> Self {
self.raw_descriptor.value = T::raw_value(&value);
self.raw_descriptor.value = T::raw(&value);
self
}

View file

@ -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<usize> {
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<usize> {
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<String> {
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<JsString> for Vec<u16> {
type Error = Error;
fn try_from(value: JsString) -> Result<Vec<u16>> {
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<JsString> for String {
type Error = Error;
fn try_from(value: JsString) -> Result<String> {
Ok(value.as_str()?.to_owned())
}
}

View file

@ -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<Vec<u8>>,
}
impl JsStringLatin1 {
pub fn as_slice(&self) -> &[u8] {
&self.buf
}
pub fn len(&self) -> usize {
self.buf.len()
}
pub fn take(self) -> Vec<u8> {
self.as_slice().to_vec()
}
pub fn into_value(self) -> JsString {
self.inner
}
#[cfg(feature = "latin1")]
pub fn into_latin1_string(self) -> Result<String> {
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<JsStringLatin1> for Vec<u8> {
fn from(value: JsStringLatin1) -> Self {
value.take()
}
}

View file

@ -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<usize> {
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<usize> {
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<usize> {
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<JsStringUtf8> {
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<JsStringUtf8>> {
Ref::new(self.0, 1, self.into_utf8()?)
}
pub fn into_utf16(self) -> Result<JsStringUtf16> {
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<JsStringUtf16>> {
Ref::new(self.0, 1, self.into_utf16()?)
}
pub fn into_latin1(self) -> Result<JsStringLatin1> {
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<JsStringLatin1>> {
Ref::new(self.0, 1, self.into_latin1()?)
}
}

View file

@ -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<Vec<u16>>,
}
impl JsStringUtf16 {
pub fn as_str(&self) -> Result<String> {
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<JsStringUtf16> for String {
type Error = Error;
fn try_from(value: JsStringUtf16) -> Result<String> {
value.as_str()
}
}
impl Deref for JsStringUtf16 {
type Target = [u16];
fn deref(&self) -> &[u16] {
self.buf.as_slice()
}
}
impl AsRef<Vec<u16>> for JsStringUtf16 {
fn as_ref(&self) -> &Vec<u16> {
&self.buf
}
}
impl From<JsStringUtf16> for Vec<u16> {
fn from(value: JsStringUtf16) -> Self {
value.as_slice().to_vec()
}
}

View file

@ -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<Vec<u8>>,
}
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<String> {
Ok(self.as_str()?.to_owned())
}
pub fn take(self) -> Vec<u8> {
self.as_slice().to_vec()
}
pub fn into_value(self) -> JsString {
self.inner
}
}
impl TryFrom<JsStringUtf8> for String {
type Error = Error;
fn try_from(value: JsStringUtf8) -> Result<String> {
Ok(value.as_str()?.to_owned())
}
}
impl From<JsStringUtf8> for Vec<u8> {
fn from(value: JsStringUtf8) -> Self {
value.take()
}
}

View file

@ -1,4 +1,5 @@
use super::Value;
#[repr(transparent)]
#[derive(Debug)]
pub struct JsUndefined(pub(crate) Value);

View file

@ -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<T: NapiValue> {
pub(crate) raw_env: sys::napi_env,
pub(crate) ref_value: sys::napi_ref,
_phantom: PhantomData<T>,
pub struct Ref<T> {
pub(crate) raw_ref: sys::napi_ref,
count: u32,
inner: T,
}
impl<T: NapiValue> Ref<T> {
pub fn new(raw_env: sys::napi_env, ref_value: sys::napi_ref) -> Ref<T> {
Ref {
raw_env,
ref_value,
_phantom: PhantomData,
unsafe impl<T> Send for Ref<T> {}
unsafe impl<T> Sync for Ref<T> {}
impl<T> Ref<T> {
pub(crate) fn new(js_value: Value, ref_count: u32, inner: T) -> Result<Ref<T>> {
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<u32> {
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<T: NapiValue> Drop for Ref<T> {
impl<T> Deref for Ref<T> {
type Target = T;
fn deref(&self) -> &T {
&self.inner
}
}
#[cfg(debug_assertions)]
impl<T> Drop for Ref<T> {
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"
);
}
}

View file

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

View file

@ -106,7 +106,7 @@ unsafe extern "C" fn call_js_cb<T, V: NapiValue>(
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"

View file

@ -7,5 +7,5 @@ pub trait Task: Send {
fn compute(&mut self) -> Result<Self::Output>;
fn resolve(&self, env: &mut Env, output: Self::Output) -> Result<Self::JsValue>;
fn resolve(self, env: Env, output: Self::Output) -> Result<Self::JsValue>;
}

View file

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

View file

@ -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<JsNumber> {
let buffer = ctx.get::<JsBuffer>(0)?;
let buffer = ctx.get::<JsBuffer>(0)?.into_value()?;
ctx.env.create_uint32((&buffer).len() as u32)
}
#[js_function(1)]
pub fn buffer_to_string(ctx: CallContext) -> Result<JsString> {
let buffer = ctx.get::<JsBuffer>(0)?;
let buffer = ctx.get::<JsBuffer>(0)?.into_value()?;
ctx.env.create_string(
str::from_utf8(&buffer).map_err(|e| Error::new(Status::StringExpected, format!("{}", e)))?,
)

View file

@ -11,7 +11,7 @@ pub fn either_number_string(ctx: CallContext) -> Result<Either<JsNumber, JsStrin
ctx.env.create_uint32(n + 100).map(Either::A)
}
Either::B(s) => {
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)
}
}

View file

@ -10,7 +10,7 @@ fn test_throw_with_reason(ctx: CallContext) -> Result<JsUnknown> {
let reason = ctx.get::<JsString>(0)?;
Err(Error::new(
Status::GenericFailure,
reason.as_str()?.to_owned(),
reason.into_utf8()?.as_str()?.to_owned(),
))
}

View file

@ -9,7 +9,7 @@ use napi::{CallContext, Error, JsObject, JsString, Module, Result, Status};
pub fn uv_read_file(ctx: CallContext) -> Result<JsObject> {
let path = ctx.get::<JsString>(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<JsObject> {
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()),
)
}

View file

@ -85,22 +85,21 @@ impl ToJs for HandleBuffer {
async fn read_file_content(filepath: &Path) -> Result<Vec<u8>> {
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<JsUndefined> {
let js_filepath = ctx.get::<JsString>(0)?;
let js_func = ctx.get::<JsFunction>(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)

View file

@ -31,14 +31,18 @@ fn test_get_named_property(ctx: CallContext) -> Result<JsUnknown> {
fn test_has_named_property(ctx: CallContext) -> Result<JsBoolean> {
let obj = ctx.get::<JsObject>(0)?;
let key = ctx.get::<JsString>(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<JsBoolean> {
let obj = ctx.get::<JsObject>(0)?;
let key = ctx.get::<JsString>(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<JsBoolean> {
fn test_has_property(ctx: CallContext) -> Result<JsBoolean> {
let obj = ctx.get::<JsObject>(0)?;
let key = ctx.get::<JsString>(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<JsBoolean> {
let key = ctx.get::<JsString>(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)]

View file

@ -3,14 +3,17 @@ use napi::{CallContext, JsString, Module, Result};
#[js_function(1)]
fn concat_string(ctx: CallContext) -> Result<JsString> {
let in_string = ctx.get::<JsString>(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<JsString> {
let in_string = ctx.get::<JsString>(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)
}

View file

@ -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<Self::JsValue> {
fn resolve(self, env: Env, output: Self::Output) -> Result<Self::JsValue> {
env.create_uint32(output)
}
}
@ -41,7 +43,40 @@ fn test_spawn_thread(ctx: CallContext) -> Result<JsObject> {
Ok(async_promise.promise_object())
}
struct CountBufferLength {
data: Ref<JsBufferValue>,
}
impl CountBufferLength {
pub fn new(data: Ref<JsBufferValue>) -> Self {
Self { data }
}
}
impl Task for CountBufferLength {
type Output = usize;
type JsValue = JsNumber;
fn compute(&mut self) -> Result<Self::Output> {
Ok((&self.data).len())
}
fn resolve(self, env: Env, output: Self::Output) -> Result<Self::JsValue> {
self.data.unref(env)?;
env.create_uint32(output as _)
}
}
#[js_function(1)]
fn test_spawn_thread_with_ref(ctx: CallContext) -> Result<JsObject> {
let n = ctx.get::<JsBuffer>(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(())
}

View file

@ -5,20 +5,20 @@ use tokio;
#[js_function(1)]
pub fn test_execute_tokio_readfile(ctx: CallContext) -> Result<JsObject> {
let js_filepath = ctx.get::<JsString>(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<JsObject> {
let js_filepath = ctx.get::<JsString>(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::<Vec<u8>, Error>(Error::new(
@ -26,6 +26,6 @@ pub fn error_from_tokio_future(ctx: CallContext) -> Result<JsObject> {
"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()),
)
}