From 1c3d9b4e9a0fe1172e91a17a965f33d025ee641a Mon Sep 17 00:00:00 2001 From: LongYinan Date: Thu, 3 Sep 2020 00:35:47 +0800 Subject: [PATCH] test(napi): add more object functions tests --- napi/src/async_work.rs | 3 +- napi/src/env.rs | 14 +- napi/src/js_values/arraybuffer.rs | 113 ++++++++++- napi/src/js_values/bigint.rs | 35 ++-- napi/src/js_values/buffer.rs | 108 +++++++++- napi/src/js_values/mod.rs | 19 +- napi/src/js_values/number.rs | 15 +- napi/src/js_values/object.rs | 77 +++++--- .../{class_property.rs => object_property.rs} | 30 ++- napi/src/js_values/string.rs | 21 +- test_module/__test__/object.spec.ts | 187 ++++++++++++++++++ test_module/__test__/object.spec.ts.md | 33 ++++ test_module/__test__/object.spec.ts.snap | Bin 147 -> 358 bytes test_module/src/class.rs | 12 +- test_module/src/object.rs | 145 +++++++++++++- 15 files changed, 705 insertions(+), 107 deletions(-) rename napi/src/js_values/{class_property.rs => object_property.rs} (81%) diff --git a/napi/src/async_work.rs b/napi/src/async_work.rs index 390e3065..c4d98ccb 100644 --- a/napi/src/async_work.rs +++ b/napi/src/async_work.rs @@ -16,8 +16,7 @@ impl AsyncWork { #[inline] pub fn run(env: sys::napi_env, task: T, deferred: sys::napi_deferred) -> Result<()> { let mut raw_resource = ptr::null_mut(); - let status = unsafe { sys::napi_create_object(env, &mut raw_resource) }; - check_status(status)?; + check_status(unsafe { sys::napi_create_object(env, &mut raw_resource) })?; let mut raw_name = ptr::null_mut(); let s = "napi_rs_async"; check_status(unsafe { diff --git a/napi/src/env.rs b/napi/src/env.rs index 46594a9d..286a9804 100644 --- a/napi/src/env.rs +++ b/napi/src/env.rs @@ -379,13 +379,13 @@ impl Env { &self, name: &str, constructor_cb: Callback, - properties: &mut [Property], + properties: &[Property], ) -> Result { let mut raw_result = ptr::null_mut(); let raw_properties = properties - .iter_mut() - .map(|prop| prop.as_raw(self.0)) - .collect::>>()?; + .iter() + .map(|prop| prop.raw()) + .collect::>(); check_status(unsafe { sys::napi_define_class( @@ -541,9 +541,9 @@ impl Env { #[inline] pub fn get_napi_version(&self) -> Result { let global = self.get_global()?; - let process = global.get_named_property::("process")?; - let versions = process.get_named_property::("versions")?; - let napi_version = versions.get_named_property::("napi")?; + let process: JsObject = global.get_named_property("process")?; + let versions: JsObject = process.get_named_property("versions")?; + let napi_version: JsString = versions.get_named_property("napi")?; napi_version .as_str()? .parse() diff --git a/napi/src/js_values/arraybuffer.rs b/napi/src/js_values/arraybuffer.rs index d156f6ab..a9ef12a8 100644 --- a/napi/src/js_values/arraybuffer.rs +++ b/napi/src/js_values/arraybuffer.rs @@ -1,7 +1,7 @@ use std::convert::TryFrom; 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::{sys, Error, Result}; @@ -24,6 +24,114 @@ impl JsArrayBuffer { len: 0, } } + + #[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 NapiValue for JsArrayBuffer { @@ -34,8 +142,7 @@ impl NapiValue for JsArrayBuffer { 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_arraybuffer_info(env, value, &mut data, &mut len) }; - check_status(status)?; + check_status(unsafe { sys::napi_get_arraybuffer_info(env, value, &mut data, &mut len) })?; Ok(JsArrayBuffer { value: JsObject(Value { env, diff --git a/napi/src/js_values/bigint.rs b/napi/src/js_values/bigint.rs index 5f583104..3bb47a67 100644 --- a/napi/src/js_values/bigint.rs +++ b/napi/src/js_values/bigint.rs @@ -35,9 +35,9 @@ impl JsBigint { #[inline] pub fn coerce_to_number(self) -> Result { let mut new_raw_value = ptr::null_mut(); - let status = - unsafe { sys::napi_coerce_to_number(self.raw.env, self.raw.value, &mut new_raw_value) }; - check_status(status)?; + check_status(unsafe { + sys::napi_coerce_to_number(self.raw.env, self.raw.value, &mut new_raw_value) + })?; Ok(JsNumber(Value { env: self.raw.env, value: new_raw_value, @@ -48,9 +48,9 @@ impl JsBigint { #[inline] pub fn coerce_to_string(self) -> Result { let mut new_raw_value = ptr::null_mut(); - let status = - unsafe { sys::napi_coerce_to_string(self.raw.env, self.raw.value, &mut new_raw_value) }; - check_status(status)?; + check_status(unsafe { + sys::napi_coerce_to_string(self.raw.env, self.raw.value, &mut new_raw_value) + })?; Ok(JsString(Value { env: self.raw.env, value: new_raw_value, @@ -60,9 +60,9 @@ impl JsBigint { #[inline] pub fn coerce_to_object(self) -> Result { let mut new_raw_value = ptr::null_mut(); - let status = - unsafe { sys::napi_coerce_to_object(self.raw.env, self.raw.value, &mut new_raw_value) }; - check_status(status)?; + check_status(unsafe { + sys::napi_coerce_to_object(self.raw.env, self.raw.value, &mut new_raw_value) + })?; Ok(JsObject(Value { env: self.raw.env, value: new_raw_value, @@ -74,8 +74,7 @@ impl JsBigint { #[cfg(napi5)] pub fn is_date(&self) -> Result { let mut is_date = true; - let status = unsafe { sys::napi_is_date(self.raw.env, self.raw.value, &mut is_date) }; - check_status(status)?; + check_status(unsafe { sys::napi_is_date(self.raw.env, self.raw.value, &mut is_date) })?; Ok(is_date) } @@ -100,6 +99,20 @@ impl JsBigint { Ok(result) } + #[inline] + pub fn is_array(&self) -> Result { + 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 { + 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] pub fn instanceof(&self, constructor: Constructor) -> Result { let mut result = false; diff --git a/napi/src/js_values/buffer.rs b/napi/src/js_values/buffer.rs index 3b3dc9ac..6b461b3f 100644 --- a/napi/src/js_values/buffer.rs +++ b/napi/src/js_values/buffer.rs @@ -3,7 +3,7 @@ use std::ops::Deref; use std::ptr; 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::{sys, Error, Result}; @@ -30,8 +30,112 @@ impl JsBuffer { } } + #[inline] pub fn into_unknown(self) -> Result { - self.value.into_unknown() + 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) } } diff --git a/napi/src/js_values/mod.rs b/napi/src/js_values/mod.rs index f542ead8..d7f59773 100644 --- a/napi/src/js_values/mod.rs +++ b/napi/src/js_values/mod.rs @@ -14,11 +14,11 @@ mod arraybuffer; mod bigint; mod boolean; mod buffer; -mod class_property; mod either; mod function; mod number; mod object; +mod object_property; mod string; mod tagged_object; mod undefined; @@ -31,13 +31,13 @@ pub use arraybuffer::JsArrayBuffer; pub use bigint::JsBigint; pub use boolean::JsBoolean; pub use buffer::JsBuffer; -pub use class_property::Property; #[cfg(feature = "serde-json")] pub(crate) use de::De; pub use either::Either; 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; @@ -145,6 +145,7 @@ macro_rules! impl_js_value_methods { value_type: ValueType::String, })) } + #[inline] pub fn coerce_to_object(self) -> Result { let mut new_raw_value = ptr::null_mut(); @@ -188,6 +189,20 @@ macro_rules! impl_js_value_methods { Ok(result) } + #[inline] + pub fn is_array(&self) -> Result { + 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 { + 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] pub fn instanceof(&self, constructor: Constructor) -> Result { let mut result = false; diff --git a/napi/src/js_values/number.rs b/napi/src/js_values/number.rs index 81ae1abe..0561cb7c 100644 --- a/napi/src/js_values/number.rs +++ b/napi/src/js_values/number.rs @@ -12,8 +12,7 @@ impl TryFrom for usize { fn try_from(value: JsNumber) -> Result { let mut result = 0; - let status = unsafe { sys::napi_get_value_int64(value.0.env, value.0.value, &mut result) }; - check_status(status)?; + check_status(unsafe { sys::napi_get_value_int64(value.0.env, value.0.value, &mut result) })?; Ok(result as usize) } } @@ -23,8 +22,7 @@ impl TryFrom for u32 { fn try_from(value: JsNumber) -> Result { let mut result = 0; - let status = unsafe { sys::napi_get_value_uint32(value.0.env, value.0.value, &mut result) }; - check_status(status)?; + check_status(unsafe { sys::napi_get_value_uint32(value.0.env, value.0.value, &mut result) })?; Ok(result) } } @@ -34,8 +32,7 @@ impl TryFrom for i32 { fn try_from(value: JsNumber) -> Result { let mut result = 0; - let status = unsafe { sys::napi_get_value_int32(value.0.env, value.0.value, &mut result) }; - check_status(status)?; + check_status(unsafe { sys::napi_get_value_int32(value.0.env, value.0.value, &mut result) })?; Ok(result) } } @@ -45,8 +42,7 @@ impl TryFrom for i64 { fn try_from(value: JsNumber) -> Result { let mut result = 0; - let status = unsafe { sys::napi_get_value_int64(value.0.env, value.0.value, &mut result) }; - check_status(status)?; + check_status(unsafe { sys::napi_get_value_int64(value.0.env, value.0.value, &mut result) })?; Ok(result) } } @@ -56,8 +52,7 @@ impl TryFrom for f64 { fn try_from(value: JsNumber) -> Result { let mut result = 0_f64; - let status = unsafe { sys::napi_get_value_double(value.0.env, value.0.value, &mut result) }; - check_status(status)?; + check_status(unsafe { sys::napi_get_value_double(value.0.env, value.0.value, &mut result) })?; Ok(result) } } diff --git a/napi/src/js_values/object.rs b/napi/src/js_values/object.rs index e4731a7e..bd320253 100644 --- a/napi/src/js_values/object.rs +++ b/napi/src/js_values/object.rs @@ -18,6 +18,18 @@ impl JsObject { }) } + pub fn get_property(&self, key: &K) -> Result + 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(&mut self, name: &str, value: T) -> Result<()> where T: NapiValue, @@ -40,15 +52,42 @@ impl JsObject { T::from_raw(self.0.env, raw_value) } - pub fn has_named_property(&self, name: &str) -> Result { + pub fn has_named_property(&self, name: S) -> Result + where + S: AsRef, + { let mut result = false; - let key = CString::new(name)?; + let key = CString::new(name.as_ref())?; check_status(unsafe { sys::napi_has_named_property(self.0.env, self.0.value, key.as_ptr(), &mut result) })?; Ok(result) } + pub fn delete_property(&mut self, name: S) -> Result + 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 { + 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 { let mut result = false; let string = CString::new(key)?; @@ -95,18 +134,6 @@ impl JsObject { Ok(result) } - pub fn get_property(&self, key: &K) -> Result - 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(&self) -> Result where T: NapiValue, @@ -141,7 +168,7 @@ impl JsObject { Ok(result) } - pub fn delete_element(&mut self, index: u32) -> Result { + pub fn delete_element(&mut self, index: u32) -> Result { let mut result = false; check_status(unsafe { 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) } - pub fn define_properties(&mut self, properties: &mut [Property]) -> Result<()> { + pub fn define_properties(&mut self, properties: &[Property]) -> Result<()> { check_status(unsafe { sys::napi_define_properties( self.0.env, self.0.value, properties.len() as _, properties - .iter_mut() - .map(|property| property.as_raw(self.0.env)) - .collect::>>()? + .iter() + .map(|property| property.raw()) + .collect::>() .as_ptr(), ) }) } - pub fn is_array(&self) -> Result { - 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 { - 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 { if self.is_array()? != true { return Err(Error::new( diff --git a/napi/src/js_values/class_property.rs b/napi/src/js_values/object_property.rs similarity index 81% rename from napi/src/js_values/class_property.rs rename to napi/src/js_values/object_property.rs index c421929b..01d7d647 100644 --- a/napi/src/js_values/class_property.rs +++ b/napi/src/js_values/object_property.rs @@ -2,7 +2,7 @@ use std::convert::From; use std::ffi::CString; 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)] pub struct Property<'env> { @@ -33,12 +33,17 @@ impl From for sys::napi_property_attributes { } impl<'env> Property<'env> { - pub fn new(name: &'env str) -> Self { - Property { + pub fn new(env: &'env Env, name: &'env str) -> Result { + 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, raw_descriptor: sys::napi_property_descriptor { utf8name: ptr::null_mut(), - name: ptr::null_mut(), + name: result, method: None, getter: None, setter: None, @@ -46,7 +51,7 @@ impl<'env> Property<'env> { attributes: sys::napi_property_attributes::napi_default, data: ptr::null_mut(), }, - } + }) } pub fn with_value(mut self, value: T) -> Self { @@ -73,18 +78,7 @@ impl<'env> Property<'env> { self.raw_descriptor.attributes = attributes.into(); } - pub(crate) fn as_raw(&mut self, env: sys::napi_env) -> Result { - let string_value = CString::new(self.name)?; - 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) + pub(crate) fn raw(&self) -> sys::napi_property_descriptor { + self.raw_descriptor } } diff --git a/napi/src/js_values/string.rs b/napi/src/js_values/string.rs index e87e965b..d9ee38df 100644 --- a/napi/src/js_values/string.rs +++ b/napi/src/js_values/string.rs @@ -29,15 +29,13 @@ impl JsString { let len = self.len()? + 1; let mut result = Vec::with_capacity(len); unsafe { - let status = sys::napi_get_value_string_utf8( + 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, - ); - - check_status(status)?; + ))?; let ptr = result.as_ptr(); mem::forget(result); Ok(slice::from_raw_parts( @@ -53,15 +51,14 @@ impl JsString { let len = self.len()? + 1; let mut result = Vec::with_capacity(len); unsafe { - let status = sys::napi_get_value_string_utf8( + 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, - ); + ))?; - check_status(status)?; let ptr = result.as_ptr(); mem::forget(result); Ok(slice::from_raw_parts( @@ -81,15 +78,14 @@ impl JsString { let len = self.len()? + 1; let mut result = Vec::with_capacity(len); unsafe { - let status = sys::napi_get_value_string_utf8( + 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, - ); + ))?; - check_status(status)?; let ptr = result.as_ptr(); mem::forget(result); Ok(slice::from_raw_parts_mut( @@ -108,14 +104,13 @@ impl TryFrom for Vec { unsafe { 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.value, result.as_mut_ptr(), result.capacity() as u64, &mut written_char_count, - ); - check_status(status)?; + ))?; result.set_len(written_char_count as usize); } diff --git a/test_module/__test__/object.spec.ts b/test_module/__test__/object.spec.ts index b054a642..91c2d685 100644 --- a/test_module/__test__/object.spec.ts +++ b/test_module/__test__/object.spec.ts @@ -9,6 +9,15 @@ test('setProperty', (t) => { 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) => { const obj = {} const property = Symbol('JsSymbol') @@ -37,3 +46,181 @@ test('testHasNamedProperty', (t) => { t.true(bindings.testHasNamedProperty(obj, 'b')) 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') +}) diff --git a/test_module/__test__/object.spec.ts.md b/test_module/__test__/object.spec.ts.md index ae162d53..326e4412 100644 --- a/test_module/__test__/object.spec.ts.md +++ b/test_module/__test__/object.spec.ts.md @@ -15,3 +15,36 @@ Generated by [AVA](https://avajs.dev). > Snapshot 1 'RustPropertyKey' + +## testGetPropertyNames + +> Snapshot 1 + + [ + '2', + 'k3', + ] + +## testSetElement + +> Snapshot 1 + + [ + undefined, + 1, + undefined, + undefined, + undefined, + 'foo', + ] + +## testDeleteElement + +> Snapshot 1 + + [ + 0, + undefined, + undefined, + 3, + ] diff --git a/test_module/__test__/object.spec.ts.snap b/test_module/__test__/object.spec.ts.snap index a3727419da3cd4791916034127e3a88db82112ea..bb1d74fd755b58dc772c488cce5dde4bc54110b8 100644 GIT binary patch delta 324 zcmV-K0lWT_0ptJADU}cdqEq~(IivxA0Br&_VH%vb;@y~(^MzH8?AXfh`enRiI%B6$yyH~b{ z{ETJ+K4k=p9sy!z26nKWjI0cTj3Pm$#U%>)Nm;4M zB?<*a`30#(C6y>D_(3WHpfcX62uTh`W&Fz$bZP{SX7i)$;b#}@$>UD zFmf?4GBGgn097zCh%hn(jTK~MG=hpSGQxzIvW=0&nyxIG Result { - let add_count_method = Property::new("addCount").with_method(add_count); - let mut properties = vec![add_count_method]; - ctx.env.define_class( - "TestClass", - test_class_constructor, - properties.as_mut_slice(), - ) + let add_count_method = Property::new(&ctx.env, "addCount")?.with_method(add_count); + let properties = vec![add_count_method]; + ctx + .env + .define_class("TestClass", test_class_constructor, properties.as_slice()) } #[js_function(1)] diff --git a/test_module/src/object.rs b/test_module/src/object.rs index 638406ba..afa131ab 100644 --- a/test_module/src/object.rs +++ b/test_module/src/object.rs @@ -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)] fn test_set_property(ctx: CallContext) -> Result { @@ -29,10 +34,148 @@ fn test_has_named_property(ctx: CallContext) -> Result { ctx.env.get_boolean(obj.has_named_property(key.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()?)?) +} + +#[js_function(2)] +fn test_has_own_property_js(ctx: CallContext) -> Result { + let obj = ctx.get::(0)?; + let key = ctx.get::(1)?; + ctx.env.get_boolean(obj.has_own_property_js(key)?) +} + +#[js_function(2)] +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()?)?) +} + +#[js_function(2)] +fn test_has_property_js(ctx: CallContext) -> Result { + let obj = ctx.get::(0)?; + let key = ctx.get::(1)?; + ctx.env.get_boolean(obj.has_property_js(key)?) +} + +#[js_function(2)] +fn test_delete_property(ctx: CallContext) -> Result { + let mut obj = ctx.get::(0)?; + let key = ctx.get::(1)?; + ctx.env.get_boolean(obj.delete_property(key)?) +} + +#[js_function(2)] +fn test_delete_named_property(ctx: CallContext) -> Result { + let mut obj = ctx.get::(0)?; + let key = ctx.get::(1)?; + ctx + .env + .get_boolean(obj.delete_named_property(key.as_str()?)?) +} + +#[js_function(2)] +fn test_get_property(ctx: CallContext) -> Result { + let obj = ctx.get::(0)?; + let key = ctx.get::(1)?; + obj.get_property(&key) +} + +#[js_function(1)] +fn test_get_property_names(ctx: CallContext) -> Result { + let obj = ctx.get::(0)?; + obj.get_property_names() +} + +#[js_function(1)] +fn test_get_prototype(ctx: CallContext) -> Result { + let obj = ctx.get::(0)?; + obj.get_prototype() +} + +#[js_function(3)] +fn test_set_element(ctx: CallContext) -> Result { + let mut obj = ctx.get::(0)?; + let index = ctx.get::(1)?; + let js_value = ctx.get::(2)?; + obj.set_element(index.try_into()?, js_value)?; + ctx.env.get_undefined() +} + +#[js_function(2)] +fn test_has_element(ctx: CallContext) -> Result { + let obj = ctx.get::(0)?; + let index = ctx.get::(1)?; + ctx.env.get_boolean(obj.has_element(index.try_into()?)?) +} + +#[js_function(2)] +fn test_get_element(ctx: CallContext) -> Result { + let obj = ctx.get::(0)?; + let index = ctx.get::(1)?; + obj.get_element(index.try_into()?) +} + +#[js_function(2)] +fn test_delete_element(ctx: CallContext) -> Result { + let mut obj: JsObject = ctx.get(0)?; + let index = ctx.get::(1)?; + ctx.env.get_boolean(obj.delete_element(index.try_into()?)?) +} + +#[js_function(1)] +fn test_define_properties(ctx: CallContext) -> Result { + let mut obj = ctx.get::(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) -> Result { + let count: i32 = ctx + .this + .get_named_property::("count")? + .try_into()?; + let value_to_add: i32 = ctx.get::(0)?.try_into()?; + ctx + .this + .set_named_property("count", ctx.env.create_int32(count + value_to_add)?)?; + ctx.env.get_undefined() +} + +#[js_function] +fn readonly_getter(ctx: CallContext) -> Result { + ctx.env.create_string("readonly") +} + pub fn register_js(module: &mut Module) -> Result<()> { 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("testGetNamedProperty", test_get_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(()) }