test(napi): add more object functions tests

This commit is contained in:
LongYinan 2020-09-03 00:35:47 +08:00 committed by LongYinan
parent 76d1258592
commit 1c3d9b4e9a
No known key found for this signature in database
GPG key ID: A3FFE134A3E20881
15 changed files with 705 additions and 107 deletions

View file

@ -16,8 +16,7 @@ impl<T: Task> AsyncWork<T> {
#[inline] #[inline]
pub fn run(env: sys::napi_env, task: T, deferred: sys::napi_deferred) -> Result<()> { pub fn run(env: sys::napi_env, task: T, deferred: sys::napi_deferred) -> Result<()> {
let mut raw_resource = ptr::null_mut(); let mut raw_resource = ptr::null_mut();
let status = unsafe { sys::napi_create_object(env, &mut raw_resource) }; check_status(unsafe { sys::napi_create_object(env, &mut raw_resource) })?;
check_status(status)?;
let mut raw_name = ptr::null_mut(); let mut raw_name = ptr::null_mut();
let s = "napi_rs_async"; let s = "napi_rs_async";
check_status(unsafe { check_status(unsafe {

View file

@ -379,13 +379,13 @@ impl Env {
&self, &self,
name: &str, name: &str,
constructor_cb: Callback, constructor_cb: Callback,
properties: &mut [Property], properties: &[Property],
) -> Result<JsFunction> { ) -> Result<JsFunction> {
let mut raw_result = ptr::null_mut(); let mut raw_result = ptr::null_mut();
let raw_properties = properties let raw_properties = properties
.iter_mut() .iter()
.map(|prop| prop.as_raw(self.0)) .map(|prop| prop.raw())
.collect::<Result<Vec<sys::napi_property_descriptor>>>()?; .collect::<Vec<sys::napi_property_descriptor>>();
check_status(unsafe { check_status(unsafe {
sys::napi_define_class( sys::napi_define_class(
@ -541,9 +541,9 @@ impl Env {
#[inline] #[inline]
pub fn get_napi_version(&self) -> Result<u32> { pub fn get_napi_version(&self) -> Result<u32> {
let global = self.get_global()?; let global = self.get_global()?;
let process = global.get_named_property::<JsObject>("process")?; let process: JsObject = global.get_named_property("process")?;
let versions = process.get_named_property::<JsObject>("versions")?; let versions: JsObject = process.get_named_property("versions")?;
let napi_version = versions.get_named_property::<JsString>("napi")?; let napi_version: JsString = versions.get_named_property("napi")?;
napi_version napi_version
.as_str()? .as_str()?
.parse() .parse()

View file

@ -1,7 +1,7 @@
use std::convert::TryFrom; use std::convert::TryFrom;
use std::ptr; use std::ptr;
use super::{JsObject, JsUnknown, NapiValue, Value, ValueType}; use super::{JsNumber, JsObject, JsString, JsUnknown, NapiValue, Value, ValueType};
use crate::error::check_status; use crate::error::check_status;
use crate::{sys, Error, Result}; use crate::{sys, Error, Result};
@ -24,6 +24,114 @@ impl JsArrayBuffer {
len: 0, len: 0,
} }
} }
#[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 NapiValue for JsArrayBuffer { impl NapiValue for JsArrayBuffer {
@ -34,8 +142,7 @@ impl NapiValue for JsArrayBuffer {
fn from_raw(env: sys::napi_env, value: sys::napi_value) -> Result<Self> { fn from_raw(env: sys::napi_env, value: sys::napi_value) -> Result<Self> {
let mut data = ptr::null_mut(); let mut data = ptr::null_mut();
let mut len: u64 = 0; let mut len: u64 = 0;
let status = unsafe { sys::napi_get_arraybuffer_info(env, value, &mut data, &mut len) }; check_status(unsafe { sys::napi_get_arraybuffer_info(env, value, &mut data, &mut len) })?;
check_status(status)?;
Ok(JsArrayBuffer { Ok(JsArrayBuffer {
value: JsObject(Value { value: JsObject(Value {
env, env,

View file

@ -35,9 +35,9 @@ impl JsBigint {
#[inline] #[inline]
pub fn coerce_to_number(self) -> Result<JsNumber> { pub fn coerce_to_number(self) -> Result<JsNumber> {
let mut new_raw_value = ptr::null_mut(); let mut new_raw_value = ptr::null_mut();
let status = check_status(unsafe {
unsafe { sys::napi_coerce_to_number(self.raw.env, self.raw.value, &mut new_raw_value) }; sys::napi_coerce_to_number(self.raw.env, self.raw.value, &mut new_raw_value)
check_status(status)?; })?;
Ok(JsNumber(Value { Ok(JsNumber(Value {
env: self.raw.env, env: self.raw.env,
value: new_raw_value, value: new_raw_value,
@ -48,9 +48,9 @@ impl JsBigint {
#[inline] #[inline]
pub fn coerce_to_string(self) -> Result<JsString> { pub fn coerce_to_string(self) -> Result<JsString> {
let mut new_raw_value = ptr::null_mut(); let mut new_raw_value = ptr::null_mut();
let status = check_status(unsafe {
unsafe { sys::napi_coerce_to_string(self.raw.env, self.raw.value, &mut new_raw_value) }; sys::napi_coerce_to_string(self.raw.env, self.raw.value, &mut new_raw_value)
check_status(status)?; })?;
Ok(JsString(Value { Ok(JsString(Value {
env: self.raw.env, env: self.raw.env,
value: new_raw_value, value: new_raw_value,
@ -60,9 +60,9 @@ impl JsBigint {
#[inline] #[inline]
pub fn coerce_to_object(self) -> Result<JsObject> { pub fn coerce_to_object(self) -> Result<JsObject> {
let mut new_raw_value = ptr::null_mut(); let mut new_raw_value = ptr::null_mut();
let status = check_status(unsafe {
unsafe { sys::napi_coerce_to_object(self.raw.env, self.raw.value, &mut new_raw_value) }; sys::napi_coerce_to_object(self.raw.env, self.raw.value, &mut new_raw_value)
check_status(status)?; })?;
Ok(JsObject(Value { Ok(JsObject(Value {
env: self.raw.env, env: self.raw.env,
value: new_raw_value, value: new_raw_value,
@ -74,8 +74,7 @@ impl JsBigint {
#[cfg(napi5)] #[cfg(napi5)]
pub fn is_date(&self) -> Result<bool> { pub fn is_date(&self) -> Result<bool> {
let mut is_date = true; let mut is_date = true;
let status = unsafe { sys::napi_is_date(self.raw.env, self.raw.value, &mut is_date) }; check_status(unsafe { sys::napi_is_date(self.raw.env, self.raw.value, &mut is_date) })?;
check_status(status)?;
Ok(is_date) Ok(is_date)
} }
@ -100,6 +99,20 @@ impl JsBigint {
Ok(result) Ok(result)
} }
#[inline]
pub fn is_array(&self) -> Result<bool> {
let mut is_array = false;
check_status(unsafe { sys::napi_is_array(self.raw.env, self.raw.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.raw.env, self.raw.value, &mut is_buffer) })?;
Ok(is_buffer)
}
#[inline] #[inline]
pub fn instanceof<Constructor: NapiValue>(&self, constructor: Constructor) -> Result<bool> { pub fn instanceof<Constructor: NapiValue>(&self, constructor: Constructor) -> Result<bool> {
let mut result = false; let mut result = false;

View file

@ -3,7 +3,7 @@ use std::ops::Deref;
use std::ptr; use std::ptr;
use std::slice; use std::slice;
use super::{JsObject, JsUnknown, NapiValue, Value, ValueType}; use super::{JsNumber, JsObject, JsString, JsUnknown, NapiValue, Value, ValueType};
use crate::error::check_status; use crate::error::check_status;
use crate::{sys, Error, Result}; use crate::{sys, Error, Result};
@ -30,8 +30,112 @@ impl JsBuffer {
} }
} }
#[inline]
pub fn into_unknown(self) -> Result<JsUnknown> { pub fn into_unknown(self) -> Result<JsUnknown> {
self.value.into_unknown() 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)
} }
} }

View file

@ -14,11 +14,11 @@ mod arraybuffer;
mod bigint; mod bigint;
mod boolean; mod boolean;
mod buffer; mod buffer;
mod class_property;
mod either; mod either;
mod function; mod function;
mod number; mod number;
mod object; mod object;
mod object_property;
mod string; mod string;
mod tagged_object; mod tagged_object;
mod undefined; mod undefined;
@ -31,13 +31,13 @@ pub use arraybuffer::JsArrayBuffer;
pub use bigint::JsBigint; pub use bigint::JsBigint;
pub use boolean::JsBoolean; pub use boolean::JsBoolean;
pub use buffer::JsBuffer; pub use buffer::JsBuffer;
pub use class_property::Property;
#[cfg(feature = "serde-json")] #[cfg(feature = "serde-json")]
pub(crate) use de::De; pub(crate) use de::De;
pub use either::Either; pub use either::Either;
pub use function::JsFunction; pub use function::JsFunction;
pub use number::JsNumber; pub use number::JsNumber;
pub use object::JsObject; pub use object::JsObject;
pub use object_property::Property;
#[cfg(feature = "serde-json")] #[cfg(feature = "serde-json")]
pub(crate) use ser::Ser; pub(crate) use ser::Ser;
pub use string::JsString; pub use string::JsString;
@ -145,6 +145,7 @@ macro_rules! impl_js_value_methods {
value_type: ValueType::String, value_type: ValueType::String,
})) }))
} }
#[inline] #[inline]
pub fn coerce_to_object(self) -> Result<JsObject> { pub fn coerce_to_object(self) -> Result<JsObject> {
let mut new_raw_value = ptr::null_mut(); let mut new_raw_value = ptr::null_mut();
@ -188,6 +189,20 @@ macro_rules! impl_js_value_methods {
Ok(result) Ok(result)
} }
#[inline]
pub fn is_array(&self) -> Result<bool> {
let mut is_array = false;
check_status(unsafe { sys::napi_is_array(self.0.env, self.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.0.env, self.0.value, &mut is_buffer) })?;
Ok(is_buffer)
}
#[inline] #[inline]
pub fn instanceof<Constructor: NapiValue>(&self, constructor: Constructor) -> Result<bool> { pub fn instanceof<Constructor: NapiValue>(&self, constructor: Constructor) -> Result<bool> {
let mut result = false; let mut result = false;

View file

@ -12,8 +12,7 @@ impl TryFrom<JsNumber> for usize {
fn try_from(value: JsNumber) -> Result<usize> { fn try_from(value: JsNumber) -> Result<usize> {
let mut result = 0; let mut result = 0;
let status = unsafe { sys::napi_get_value_int64(value.0.env, value.0.value, &mut result) }; check_status(unsafe { sys::napi_get_value_int64(value.0.env, value.0.value, &mut result) })?;
check_status(status)?;
Ok(result as usize) Ok(result as usize)
} }
} }
@ -23,8 +22,7 @@ impl TryFrom<JsNumber> for u32 {
fn try_from(value: JsNumber) -> Result<u32> { fn try_from(value: JsNumber) -> Result<u32> {
let mut result = 0; let mut result = 0;
let status = unsafe { sys::napi_get_value_uint32(value.0.env, value.0.value, &mut result) }; check_status(unsafe { sys::napi_get_value_uint32(value.0.env, value.0.value, &mut result) })?;
check_status(status)?;
Ok(result) Ok(result)
} }
} }
@ -34,8 +32,7 @@ impl TryFrom<JsNumber> for i32 {
fn try_from(value: JsNumber) -> Result<i32> { fn try_from(value: JsNumber) -> Result<i32> {
let mut result = 0; let mut result = 0;
let status = unsafe { sys::napi_get_value_int32(value.0.env, value.0.value, &mut result) }; check_status(unsafe { sys::napi_get_value_int32(value.0.env, value.0.value, &mut result) })?;
check_status(status)?;
Ok(result) Ok(result)
} }
} }
@ -45,8 +42,7 @@ impl TryFrom<JsNumber> for i64 {
fn try_from(value: JsNumber) -> Result<i64> { fn try_from(value: JsNumber) -> Result<i64> {
let mut result = 0; let mut result = 0;
let status = unsafe { sys::napi_get_value_int64(value.0.env, value.0.value, &mut result) }; check_status(unsafe { sys::napi_get_value_int64(value.0.env, value.0.value, &mut result) })?;
check_status(status)?;
Ok(result) Ok(result)
} }
} }
@ -56,8 +52,7 @@ impl TryFrom<JsNumber> for f64 {
fn try_from(value: JsNumber) -> Result<f64> { fn try_from(value: JsNumber) -> Result<f64> {
let mut result = 0_f64; let mut result = 0_f64;
let status = unsafe { sys::napi_get_value_double(value.0.env, value.0.value, &mut result) }; check_status(unsafe { sys::napi_get_value_double(value.0.env, value.0.value, &mut result) })?;
check_status(status)?;
Ok(result) Ok(result)
} }
} }

View file

@ -18,6 +18,18 @@ impl JsObject {
}) })
} }
pub fn get_property<K, T>(&self, key: &K) -> Result<T>
where
K: NapiValue,
T: NapiValue,
{
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)
})?;
T::from_raw(self.0.env, raw_value)
}
pub fn set_named_property<T>(&mut self, name: &str, value: T) -> Result<()> pub fn set_named_property<T>(&mut self, name: &str, value: T) -> Result<()>
where where
T: NapiValue, T: NapiValue,
@ -40,15 +52,42 @@ impl JsObject {
T::from_raw(self.0.env, raw_value) T::from_raw(self.0.env, raw_value)
} }
pub fn has_named_property(&self, name: &str) -> Result<bool> { pub fn has_named_property<S>(&self, name: S) -> Result<bool>
where
S: AsRef<str>,
{
let mut result = false; let mut result = false;
let key = CString::new(name)?; let key = CString::new(name.as_ref())?;
check_status(unsafe { check_status(unsafe {
sys::napi_has_named_property(self.0.env, self.0.value, key.as_ptr(), &mut result) sys::napi_has_named_property(self.0.env, self.0.value, key.as_ptr(), &mut result)
})?; })?;
Ok(result) Ok(result)
} }
pub fn delete_property<S>(&mut self, name: S) -> Result<bool>
where
S: NapiValue,
{
let mut result = false;
check_status(unsafe {
sys::napi_delete_property(self.0.env, self.0.value, name.raw_value(), &mut result)
})?;
Ok(result)
}
pub fn delete_named_property(&mut self, name: &str) -> Result<bool> {
let mut result = false;
let key_str = CString::new(name)?;
let mut js_key = ptr::null_mut();
check_status(unsafe {
sys::napi_create_string_utf8(self.0.env, key_str.as_ptr(), name.len() as _, &mut js_key)
})?;
check_status(unsafe {
sys::napi_delete_property(self.0.env, self.0.value, js_key, &mut result)
})?;
Ok(result)
}
pub fn has_own_property(&self, key: &str) -> Result<bool> { pub fn has_own_property(&self, key: &str) -> Result<bool> {
let mut result = false; let mut result = false;
let string = CString::new(key)?; let string = CString::new(key)?;
@ -95,18 +134,6 @@ impl JsObject {
Ok(result) Ok(result)
} }
pub fn get_property<K, T>(&self, key: &K) -> Result<T>
where
K: NapiValue,
T: NapiValue,
{
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)
})?;
T::from_raw(self.0.env, raw_value)
}
pub fn get_property_names<T>(&self) -> Result<T> pub fn get_property_names<T>(&self) -> Result<T>
where where
T: NapiValue, T: NapiValue,
@ -141,7 +168,7 @@ impl JsObject {
Ok(result) Ok(result)
} }
pub fn delete_element<T>(&mut self, index: u32) -> Result<bool> { pub fn delete_element(&mut self, index: u32) -> Result<bool> {
let mut result = false; let mut result = false;
check_status(unsafe { check_status(unsafe {
sys::napi_delete_element(self.0.env, self.0.value, index, &mut result) sys::napi_delete_element(self.0.env, self.0.value, index, &mut result)
@ -160,33 +187,21 @@ impl JsObject {
T::from_raw(self.0.env, raw_value) T::from_raw(self.0.env, raw_value)
} }
pub fn define_properties(&mut self, properties: &mut [Property]) -> Result<()> { pub fn define_properties(&mut self, properties: &[Property]) -> Result<()> {
check_status(unsafe { check_status(unsafe {
sys::napi_define_properties( sys::napi_define_properties(
self.0.env, self.0.env,
self.0.value, self.0.value,
properties.len() as _, properties.len() as _,
properties properties
.iter_mut() .iter()
.map(|property| property.as_raw(self.0.env)) .map(|property| property.raw())
.collect::<Result<Vec<sys::napi_property_descriptor>>>()? .collect::<Vec<sys::napi_property_descriptor>>()
.as_ptr(), .as_ptr(),
) )
}) })
} }
pub fn is_array(&self) -> Result<bool> {
let mut is_array = false;
check_status(unsafe { sys::napi_is_array(self.0.env, self.0.value, &mut is_array) })?;
Ok(is_array)
}
pub fn is_buffer(&self) -> Result<bool> {
let mut is_buffer = false;
check_status(unsafe { sys::napi_is_buffer(self.0.env, self.0.value, &mut is_buffer) })?;
Ok(is_buffer)
}
pub fn get_array_length(&self) -> Result<u32> { pub fn get_array_length(&self) -> Result<u32> {
if self.is_array()? != true { if self.is_array()? != true {
return Err(Error::new( return Err(Error::new(

View file

@ -2,7 +2,7 @@ use std::convert::From;
use std::ffi::CString; use std::ffi::CString;
use std::ptr; use std::ptr;
use crate::{error::check_status, sys, Callback, NapiValue, Result}; use crate::{error::check_status, sys, Callback, Env, NapiValue, Result};
#[derive(Debug, Clone, Copy)] #[derive(Debug, Clone, Copy)]
pub struct Property<'env> { pub struct Property<'env> {
@ -33,12 +33,17 @@ impl From<PropertyAttributes> for sys::napi_property_attributes {
} }
impl<'env> Property<'env> { impl<'env> Property<'env> {
pub fn new(name: &'env str) -> Self { pub fn new(env: &'env Env, name: &'env str) -> Result<Self> {
Property { let string_value = CString::new(name)?;
let mut result = ptr::null_mut();
check_status(unsafe {
sys::napi_create_string_utf8(env.0, string_value.as_ptr(), name.len() as _, &mut result)
})?;
Ok(Property {
name, name,
raw_descriptor: sys::napi_property_descriptor { raw_descriptor: sys::napi_property_descriptor {
utf8name: ptr::null_mut(), utf8name: ptr::null_mut(),
name: ptr::null_mut(), name: result,
method: None, method: None,
getter: None, getter: None,
setter: None, setter: None,
@ -46,7 +51,7 @@ impl<'env> Property<'env> {
attributes: sys::napi_property_attributes::napi_default, attributes: sys::napi_property_attributes::napi_default,
data: ptr::null_mut(), data: ptr::null_mut(),
}, },
} })
} }
pub fn with_value<T: NapiValue>(mut self, value: T) -> Self { pub fn with_value<T: NapiValue>(mut self, value: T) -> Self {
@ -73,18 +78,7 @@ impl<'env> Property<'env> {
self.raw_descriptor.attributes = attributes.into(); self.raw_descriptor.attributes = attributes.into();
} }
pub(crate) fn as_raw(&mut self, env: sys::napi_env) -> Result<sys::napi_property_descriptor> { pub(crate) fn raw(&self) -> sys::napi_property_descriptor {
let string_value = CString::new(self.name)?; self.raw_descriptor
let mut result = ptr::null_mut();
check_status(unsafe {
sys::napi_create_string_utf8(
env,
string_value.as_ptr(),
self.name.len() as _,
&mut result,
)
})?;
self.raw_descriptor.name = result;
Ok(self.raw_descriptor)
} }
} }

View file

@ -29,15 +29,13 @@ impl JsString {
let len = self.len()? + 1; let len = self.len()? + 1;
let mut result = Vec::with_capacity(len); let mut result = Vec::with_capacity(len);
unsafe { unsafe {
let status = sys::napi_get_value_string_utf8( check_status(sys::napi_get_value_string_utf8(
self.0.env, self.0.env,
self.0.value, self.0.value,
result.as_mut_ptr(), result.as_mut_ptr(),
len as u64, len as u64,
&mut written_char_count, &mut written_char_count,
); ))?;
check_status(status)?;
let ptr = result.as_ptr(); let ptr = result.as_ptr();
mem::forget(result); mem::forget(result);
Ok(slice::from_raw_parts( Ok(slice::from_raw_parts(
@ -53,15 +51,14 @@ impl JsString {
let len = self.len()? + 1; let len = self.len()? + 1;
let mut result = Vec::with_capacity(len); let mut result = Vec::with_capacity(len);
unsafe { unsafe {
let status = sys::napi_get_value_string_utf8( check_status(sys::napi_get_value_string_utf8(
self.0.env, self.0.env,
self.0.value, self.0.value,
result.as_mut_ptr(), result.as_mut_ptr(),
len as u64, len as u64,
&mut written_char_count, &mut written_char_count,
); ))?;
check_status(status)?;
let ptr = result.as_ptr(); let ptr = result.as_ptr();
mem::forget(result); mem::forget(result);
Ok(slice::from_raw_parts( Ok(slice::from_raw_parts(
@ -81,15 +78,14 @@ impl JsString {
let len = self.len()? + 1; let len = self.len()? + 1;
let mut result = Vec::with_capacity(len); let mut result = Vec::with_capacity(len);
unsafe { unsafe {
let status = sys::napi_get_value_string_utf8( check_status(sys::napi_get_value_string_utf8(
self.0.env, self.0.env,
self.0.value, self.0.value,
result.as_mut_ptr(), result.as_mut_ptr(),
len as u64, len as u64,
&mut written_char_count, &mut written_char_count,
); ))?;
check_status(status)?;
let ptr = result.as_ptr(); let ptr = result.as_ptr();
mem::forget(result); mem::forget(result);
Ok(slice::from_raw_parts_mut( Ok(slice::from_raw_parts_mut(
@ -108,14 +104,13 @@ impl TryFrom<JsString> for Vec<u16> {
unsafe { unsafe {
let mut written_char_count = 0; let mut written_char_count = 0;
let status = sys::napi_get_value_string_utf16( check_status(sys::napi_get_value_string_utf16(
value.0.env, value.0.env,
value.0.value, value.0.value,
result.as_mut_ptr(), result.as_mut_ptr(),
result.capacity() as u64, result.capacity() as u64,
&mut written_char_count, &mut written_char_count,
); ))?;
check_status(status)?;
result.set_len(written_char_count as usize); result.set_len(written_char_count as usize);
} }

View file

@ -9,6 +9,15 @@ test('setProperty', (t) => {
t.snapshot(obj[key]) t.snapshot(obj[key])
}) })
test('testGetProperty', (t) => {
const name = Symbol('JsSymbol')
const value = Symbol('JsValue')
const obj = {
[name]: value,
}
t.is(bindings.testGetProperty(obj, name), value)
})
test('setNamedProperty', (t) => { test('setNamedProperty', (t) => {
const obj = {} const obj = {}
const property = Symbol('JsSymbol') const property = Symbol('JsSymbol')
@ -37,3 +46,181 @@ test('testHasNamedProperty', (t) => {
t.true(bindings.testHasNamedProperty(obj, 'b')) t.true(bindings.testHasNamedProperty(obj, 'b'))
t.false(bindings.testHasNamedProperty(obj, 'c')) t.false(bindings.testHasNamedProperty(obj, 'c'))
}) })
test('testHasOwnProperty', (t) => {
const obj = {
a: '1',
b: undefined,
}
const child = Object.create(obj, {
d: {
value: 1,
enumerable: true,
configurable: true,
},
})
t.false(bindings.testHasOwnProperty(child, 'a'))
t.false(bindings.testHasOwnProperty(child, 'b'))
t.true(bindings.testHasOwnProperty(child, 'd'))
})
test('testHasOwnPropertyJs', (t) => {
const obj = {
a: '1',
b: undefined,
}
const child = Object.create(obj)
child.c = 'k1'
t.false(bindings.testHasOwnPropertyJs(child, 'a'))
t.false(bindings.testHasOwnPropertyJs(child, 'b'))
t.true(bindings.testHasOwnPropertyJs(child, 'c'))
})
test('testHasProperty', (t) => {
const obj = {
a: '1',
b: undefined,
}
const child = Object.create(obj)
child.c = 'k1'
t.true(bindings.testHasProperty(child, 'a'))
t.true(bindings.testHasProperty(child, 'b'))
t.true(bindings.testHasProperty(child, 'c'))
t.false(bindings.testHasProperty(child, '__NOT_EXISTED__'))
})
test('testHasPropertJs', (t) => {
const key = Symbol('JsString')
const obj = {
[key]: 1,
a: 0,
b: undefined,
2: 'c',
}
t.true(bindings.testHasPropertyJs(obj, key))
t.true(bindings.testHasPropertyJs(obj, 'a'))
t.true(bindings.testHasPropertyJs(obj, 'b'))
t.true(bindings.testHasPropertyJs(obj, 2))
t.false(bindings.testHasPropertyJs(obj, {}))
t.false(bindings.testHasPropertyJs(obj, Symbol('JsString')))
})
test('testDeleteProperty', (t) => {
const k1 = Symbol()
const k2 = 2
const k3 = 'foo'
const obj = {
[k1]: 1,
[k2]: 2,
k4: 4,
}
Object.defineProperty(obj, k3, {
configurable: false,
enumerable: true,
value: 'k3',
})
t.true(bindings.testDeleteProperty(obj, k1))
t.true(bindings.testDeleteProperty(obj, k2))
t.false(bindings.testDeleteProperty(obj, k3))
t.true(bindings.testDeleteProperty(obj, 'k4'))
t.true(bindings.testDeleteProperty(obj, '__NOT_EXISTED__'))
t.true(bindings.testDeleteProperty(obj, k1))
// @ts-expect-error
t.deepEqual(obj, { [k3]: 'k3' })
})
test('testDeleteNamedProperty', (t) => {
const k1 = 'k1'
const k2 = 'k2'
const k3 = 'foo'
const obj = {
[k1]: 1,
[k2]: 2,
k4: 4,
}
Object.defineProperty(obj, k3, {
configurable: false,
enumerable: true,
value: 'k3',
})
t.true(bindings.testDeleteNamedProperty(obj, k1))
t.true(bindings.testDeleteNamedProperty(obj, k2))
t.false(bindings.testDeleteNamedProperty(obj, k3))
t.true(bindings.testDeleteNamedProperty(obj, 'k4'))
t.true(bindings.testDeleteNamedProperty(obj, '__NOT_EXISTED__'))
t.true(bindings.testDeleteNamedProperty(obj, k1))
// @ts-expect-error
t.deepEqual(obj, { [k3]: 'k3' })
})
test('testGetPropertyNames', (t) => {
const k1 = Symbol()
const k2 = 2
const k3 = 'k3'
const obj = {
[k1]: 1,
[k2]: 1,
[k3]: 1,
}
t.snapshot(
bindings
.testGetPropertyNames(obj)
.map((v: string | number) => v.toString()),
)
})
test('testGetPrototype', (t) => {
class A {}
class B extends A {}
const obj = new B()
t.is(bindings.testGetPrototype(obj), Object.getPrototypeOf(obj))
})
test('testSetElement', (t) => {
const arr: any[] = []
bindings.testSetElement(arr, 1, 1)
bindings.testSetElement(arr, 5, 'foo')
t.snapshot(arr)
})
test('testHasElement', (t) => {
const arr: number[] = []
arr[1] = 1
arr[4] = 0
t.false(bindings.testHasElement(arr, 0))
t.true(bindings.testHasElement(arr, 1))
t.false(bindings.testHasElement(arr, 2))
t.false(bindings.testHasElement(arr, 3))
t.true(bindings.testHasElement(arr, 4))
})
test('testGetElement', (t) => {
const arr = [Symbol(), Symbol()]
t.is(bindings.testGetElement(arr, 0), arr[0])
t.is(bindings.testGetElement(arr, 1), arr[1])
})
test('testDeleteElement', (t) => {
const arr = [0, 1, 2, 3]
bindings.testDeleteElement(arr, 1)
bindings.testDeleteElement(arr, 2)
t.snapshot(arr)
})
test('testDefineProperties', (t) => {
const obj: any = {}
bindings.testDefineProperties(obj)
t.is(obj.count, 0)
obj.add(10)
t.is(obj.count, 10)
const descriptor = Object.getOwnPropertyDescriptor(obj, 'ro')
t.is(descriptor?.value ?? descriptor?.get?.(), 'readonly')
})

View file

@ -15,3 +15,36 @@ Generated by [AVA](https://avajs.dev).
> Snapshot 1 > Snapshot 1
'RustPropertyKey' 'RustPropertyKey'
## testGetPropertyNames
> Snapshot 1
[
'2',
'k3',
]
## testSetElement
> Snapshot 1
[
undefined,
1,
undefined,
undefined,
undefined,
'foo',
]
## testDeleteElement
> Snapshot 1
[
0,
undefined,
undefined,
3,
]

View file

@ -4,13 +4,11 @@ use napi::{CallContext, JsFunction, JsNumber, JsObject, JsUndefined, Module, Pro
#[js_function(1)] #[js_function(1)]
fn create_test_class(ctx: CallContext) -> Result<JsFunction> { fn create_test_class(ctx: CallContext) -> Result<JsFunction> {
let add_count_method = Property::new("addCount").with_method(add_count); let add_count_method = Property::new(&ctx.env, "addCount")?.with_method(add_count);
let mut properties = vec![add_count_method]; let properties = vec![add_count_method];
ctx.env.define_class( ctx
"TestClass", .env
test_class_constructor, .define_class("TestClass", test_class_constructor, properties.as_slice())
properties.as_mut_slice(),
)
} }
#[js_function(1)] #[js_function(1)]

View file

@ -1,4 +1,9 @@
use napi::{CallContext, JsBoolean, JsObject, JsString, JsUndefined, JsUnknown, Module, Result}; use std::convert::TryInto;
use napi::{
CallContext, JsBoolean, JsNumber, JsObject, JsString, JsUndefined, JsUnknown, Module, Property,
Result,
};
#[js_function(2)] #[js_function(2)]
fn test_set_property(ctx: CallContext) -> Result<JsUndefined> { fn test_set_property(ctx: CallContext) -> Result<JsUndefined> {
@ -29,10 +34,148 @@ fn test_has_named_property(ctx: CallContext) -> Result<JsBoolean> {
ctx.env.get_boolean(obj.has_named_property(key.as_str()?)?) ctx.env.get_boolean(obj.has_named_property(key.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()?)?)
}
#[js_function(2)]
fn test_has_own_property_js(ctx: CallContext) -> Result<JsBoolean> {
let obj = ctx.get::<JsObject>(0)?;
let key = ctx.get::<JsUnknown>(1)?;
ctx.env.get_boolean(obj.has_own_property_js(key)?)
}
#[js_function(2)]
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()?)?)
}
#[js_function(2)]
fn test_has_property_js(ctx: CallContext) -> Result<JsBoolean> {
let obj = ctx.get::<JsObject>(0)?;
let key = ctx.get::<JsUnknown>(1)?;
ctx.env.get_boolean(obj.has_property_js(key)?)
}
#[js_function(2)]
fn test_delete_property(ctx: CallContext) -> Result<JsBoolean> {
let mut obj = ctx.get::<JsObject>(0)?;
let key = ctx.get::<JsUnknown>(1)?;
ctx.env.get_boolean(obj.delete_property(key)?)
}
#[js_function(2)]
fn test_delete_named_property(ctx: CallContext) -> Result<JsBoolean> {
let mut obj = ctx.get::<JsObject>(0)?;
let key = ctx.get::<JsString>(1)?;
ctx
.env
.get_boolean(obj.delete_named_property(key.as_str()?)?)
}
#[js_function(2)]
fn test_get_property(ctx: CallContext) -> Result<JsUnknown> {
let obj = ctx.get::<JsObject>(0)?;
let key = ctx.get::<JsUnknown>(1)?;
obj.get_property(&key)
}
#[js_function(1)]
fn test_get_property_names(ctx: CallContext) -> Result<JsUnknown> {
let obj = ctx.get::<JsObject>(0)?;
obj.get_property_names()
}
#[js_function(1)]
fn test_get_prototype(ctx: CallContext) -> Result<JsUnknown> {
let obj = ctx.get::<JsObject>(0)?;
obj.get_prototype()
}
#[js_function(3)]
fn test_set_element(ctx: CallContext) -> Result<JsUndefined> {
let mut obj = ctx.get::<JsObject>(0)?;
let index = ctx.get::<JsNumber>(1)?;
let js_value = ctx.get::<JsUnknown>(2)?;
obj.set_element(index.try_into()?, js_value)?;
ctx.env.get_undefined()
}
#[js_function(2)]
fn test_has_element(ctx: CallContext) -> Result<JsBoolean> {
let obj = ctx.get::<JsObject>(0)?;
let index = ctx.get::<JsNumber>(1)?;
ctx.env.get_boolean(obj.has_element(index.try_into()?)?)
}
#[js_function(2)]
fn test_get_element(ctx: CallContext) -> Result<JsUnknown> {
let obj = ctx.get::<JsObject>(0)?;
let index = ctx.get::<JsNumber>(1)?;
obj.get_element(index.try_into()?)
}
#[js_function(2)]
fn test_delete_element(ctx: CallContext) -> Result<JsBoolean> {
let mut obj: JsObject = ctx.get(0)?;
let index = ctx.get::<JsNumber>(1)?;
ctx.env.get_boolean(obj.delete_element(index.try_into()?)?)
}
#[js_function(1)]
fn test_define_properties(ctx: CallContext) -> Result<JsUndefined> {
let mut obj = ctx.get::<JsObject>(0)?;
let add_method = Property::new(&ctx.env, "add")?.with_method(add);
let readonly_property = Property::new(&ctx.env, "ro")?.with_getter(readonly_getter);
let properties = vec![add_method, readonly_property];
obj.define_properties(&properties)?;
obj.set_named_property("count", ctx.env.create_int32(0)?)?;
ctx.env.get_undefined()
}
#[js_function(1)]
fn add(mut ctx: CallContext<JsObject>) -> Result<JsUndefined> {
let count: i32 = ctx
.this
.get_named_property::<JsNumber>("count")?
.try_into()?;
let value_to_add: i32 = ctx.get::<JsNumber>(0)?.try_into()?;
ctx
.this
.set_named_property("count", ctx.env.create_int32(count + value_to_add)?)?;
ctx.env.get_undefined()
}
#[js_function]
fn readonly_getter(ctx: CallContext) -> Result<JsString> {
ctx.env.create_string("readonly")
}
pub fn register_js(module: &mut Module) -> Result<()> { pub fn register_js(module: &mut Module) -> Result<()> {
module.create_named_method("testSetProperty", test_set_property)?; module.create_named_method("testSetProperty", test_set_property)?;
module.create_named_method("testGetProperty", test_get_property)?;
module.create_named_method("testSetNamedProperty", test_set_named_property)?; module.create_named_method("testSetNamedProperty", test_set_named_property)?;
module.create_named_method("testGetNamedProperty", test_get_named_property)?; module.create_named_method("testGetNamedProperty", test_get_named_property)?;
module.create_named_method("testHasNamedProperty", test_has_named_property)?; module.create_named_method("testHasNamedProperty", test_has_named_property)?;
module.create_named_method("testHasOwnProperty", test_has_own_property)?;
module.create_named_method("testHasOwnPropertyJs", test_has_own_property_js)?;
module.create_named_method("testHasProperty", test_has_property)?;
module.create_named_method("testHasPropertyJs", test_has_property_js)?;
module.create_named_method("testDeleteProperty", test_delete_property)?;
module.create_named_method("testDeleteNamedProperty", test_delete_named_property)?;
module.create_named_method("testGetPropertyNames", test_get_property_names)?;
module.create_named_method("testGetPrototype", test_get_prototype)?;
module.create_named_method("testSetElement", test_set_element)?;
module.create_named_method("testHasElement", test_has_element)?;
module.create_named_method("testGetElement", test_get_element)?;
module.create_named_method("testDeleteElement", test_delete_element)?;
module.create_named_method("testDefineProperties", test_define_properties)?;
Ok(()) Ok(())
} }