From 76d12585920574a6516ce53498bddd650158ba1d Mon Sep 17 00:00:00 2001 From: LongYinan Date: Wed, 2 Sep 2020 17:05:53 +0800 Subject: [PATCH 1/4] feat(napi): implement all object functions --- napi-derive/src/lib.rs | 2 +- napi/src/call_context.rs | 15 ++ napi/src/env.rs | 6 +- napi/src/js_values/class_property.rs | 63 ++++++-- napi/src/js_values/de.rs | 8 +- napi/src/js_values/function.rs | 5 +- napi/src/js_values/object.rs | 150 +++++++++++++++--- napi/src/js_values/ser.rs | 16 +- test_module/__test__/object.spec.ts | 39 +++++ test_module/__test__/object.spec.ts.md | 17 ++ test_module/__test__/object.spec.ts.snap | Bin 0 -> 147 bytes test_module/src/buffer.rs | 8 +- test_module/src/class.rs | 23 ++- test_module/src/either.rs | 8 +- test_module/src/env.rs | 35 ++++ test_module/src/error.rs | 33 ++++ test_module/src/external.rs | 8 +- test_module/src/function.rs | 8 +- test_module/src/lib.rs | 140 +++------------- test_module/src/libuv/read_file.rs | 20 ++- test_module/src/napi4/mod.rs | 11 +- test_module/src/napi5/{is_date.rs => date.rs} | 0 test_module/src/napi5/mod.rs | 9 +- test_module/src/napi6/bigint.rs | 10 +- test_module/src/napi6/mod.rs | 18 ++- test_module/src/object.rs | 38 +++++ test_module/src/serde.rs | 2 +- test_module/src/string.rs | 7 +- test_module/src/symbol.rs | 9 +- test_module/src/task.rs | 9 +- test_module/src/tokio_rt/mod.rs | 10 +- 31 files changed, 520 insertions(+), 207 deletions(-) create mode 100644 test_module/__test__/object.spec.ts create mode 100644 test_module/__test__/object.spec.ts.md create mode 100644 test_module/__test__/object.spec.ts.snap create mode 100644 test_module/src/env.rs create mode 100644 test_module/src/error.rs rename test_module/src/napi5/{is_date.rs => date.rs} (100%) create mode 100644 test_module/src/object.rs diff --git a/napi-derive/src/lib.rs b/napi-derive/src/lib.rs index 963f92bc..afd9d3c6 100644 --- a/napi-derive/src/lib.rs +++ b/napi-derive/src/lib.rs @@ -112,7 +112,7 @@ pub fn js_function(attr: TokenStream, input: TokenStream) -> TokenStream { } let mut env = Env::from_raw(raw_env); - let call_ctx = CallContext::new(&mut env, raw_this, &raw_args, #arg_len_span, argc as usize); + let call_ctx = CallContext::new(&mut env, cb_info, raw_this, &raw_args, #arg_len_span, argc as usize); let result = call_ctx.and_then(|ctx| { match panic::catch_unwind(AssertUnwindSafe(move || #new_fn_name(ctx))) { Ok(result) => result, diff --git a/napi/src/call_context.rs b/napi/src/call_context.rs index f8cccff1..1f10f99f 100644 --- a/napi/src/call_context.rs +++ b/napi/src/call_context.rs @@ -1,8 +1,12 @@ +use std::ptr; + +use crate::error::check_status; use crate::{sys, Either, Env, Error, JsUndefined, JsUnknown, NapiValue, Result, Status}; pub struct CallContext<'env, T: NapiValue = JsUnknown> { pub env: &'env Env, pub this: T, + callback_info: sys::napi_callback_info, args: &'env [sys::napi_value], arg_len: usize, actual_arg_length: usize, @@ -12,6 +16,7 @@ impl<'env, T: NapiValue> CallContext<'env, T> { #[inline] pub fn new( env: &'env Env, + callback_info: sys::napi_callback_info, this: sys::napi_value, args: &'env [sys::napi_value], arg_len: usize, @@ -19,6 +24,7 @@ impl<'env, T: NapiValue> CallContext<'env, T> { ) -> Result { Ok(Self { env, + callback_info, this: T::from_raw(env.0, this)?, args, arg_len, @@ -53,4 +59,13 @@ impl<'env, T: NapiValue> CallContext<'env, T> { } } } + + pub fn get_new_target(&self) -> Result + where + V: NapiValue, + { + let mut value = ptr::null_mut(); + check_status(unsafe { sys::napi_get_new_target(self.env.0, self.callback_info, &mut value) })?; + V::from_raw(self.env.0, value) + } } diff --git a/napi/src/env.rs b/napi/src/env.rs index ba2a2270..46594a9d 100644 --- a/napi/src/env.rs +++ b/napi/src/env.rs @@ -379,12 +379,12 @@ impl Env { &self, name: &str, constructor_cb: Callback, - properties: Vec, + properties: &mut [Property], ) -> Result { let mut raw_result = ptr::null_mut(); let raw_properties = properties - .into_iter() - .map(|prop| prop.into_raw(self)) + .iter_mut() + .map(|prop| prop.as_raw(self.0)) .collect::>>()?; check_status(unsafe { diff --git a/napi/src/js_values/class_property.rs b/napi/src/js_values/class_property.rs index 2f57eea6..c421929b 100644 --- a/napi/src/js_values/class_property.rs +++ b/napi/src/js_values/class_property.rs @@ -1,17 +1,41 @@ +use std::convert::From; +use std::ffi::CString; use std::ptr; -use crate::{sys, Callback, Env, NapiValue, Result}; +use crate::{error::check_status, sys, Callback, NapiValue, Result}; -#[derive(Debug)] -pub struct Property { - name: String, - raw_descriptor: sys::napi_property_descriptor, +#[derive(Debug, Clone, Copy)] +pub struct Property<'env> { + name: &'env str, + pub(crate) raw_descriptor: sys::napi_property_descriptor, } -impl Property { - pub fn new(name: &str) -> Self { +#[repr(u32)] +#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] +pub enum PropertyAttributes { + Default = sys::napi_property_attributes::napi_default as _, + Writable = sys::napi_property_attributes::napi_writable as _, + Enumerable = sys::napi_property_attributes::napi_enumerable as _, + Configurable = sys::napi_property_attributes::napi_configurable as _, + Static = sys::napi_property_attributes::napi_static as _, +} + +impl From for sys::napi_property_attributes { + fn from(value: PropertyAttributes) -> Self { + match value { + PropertyAttributes::Default => sys::napi_property_attributes::napi_default, + PropertyAttributes::Writable => sys::napi_property_attributes::napi_writable, + PropertyAttributes::Enumerable => sys::napi_property_attributes::napi_enumerable, + PropertyAttributes::Configurable => sys::napi_property_attributes::napi_configurable, + PropertyAttributes::Static => sys::napi_property_attributes::napi_static, + } + } +} + +impl<'env> Property<'env> { + pub fn new(name: &'env str) -> Self { Property { - name: String::from(name), + name, raw_descriptor: sys::napi_property_descriptor { utf8name: ptr::null_mut(), name: ptr::null_mut(), @@ -40,8 +64,27 @@ impl Property { self } - pub(crate) fn into_raw(mut self, env: &Env) -> Result { - self.raw_descriptor.name = env.create_string(&self.name)?.raw_value(); + pub fn with_setter(mut self, callback: Callback) -> Self { + self.raw_descriptor.setter = Some(callback); + self + } + + pub fn with_property_attributes(mut self, attributes: PropertyAttributes) { + 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) } } diff --git a/napi/src/js_values/de.rs b/napi/src/js_values/de.rs index 2026926d..a565448a 100644 --- a/napi/src/js_values/de.rs +++ b/napi/src/js_values/de.rs @@ -122,7 +122,7 @@ impl<'x, 'de, 'env> serde::de::Deserializer<'x> for &'de mut De<'env> { ), )) } else { - let key = properties.get_index::(0)?; + let key = properties.get_element::(0)?; let value: JsUnknown = js_object.get_property(&key)?; visitor.visit_enum(JsEnumAccess::new(key.as_str()?.to_owned(), Some(&value.0))) } @@ -289,7 +289,7 @@ impl<'de, 'env> SeqAccess<'de> for JsArrayAccess<'env> { if self.idx >= self.len { return Ok(None); } - let v = self.input.get_index::(self.idx)?; + let v = self.input.get_element::(self.idx)?; self.idx += 1; let mut de = De(&v.0); @@ -331,7 +331,7 @@ impl<'de, 'env> MapAccess<'de> for JsObjectAccess<'env> { return Ok(None); } - let prop_name = self.properties.get_index::(self.idx)?; + let prop_name = self.properties.get_element::(self.idx)?; let mut de = De(&prop_name.0); seed.deserialize(&mut de).map(Some) @@ -347,7 +347,7 @@ impl<'de, 'env> MapAccess<'de> for JsObjectAccess<'env> { format!("Index:{} out of range: {}", self.property_len, self.idx), )); } - let prop_name = self.properties.get_index::(self.idx)?; + let prop_name = self.properties.get_element::(self.idx)?; let value: JsUnknown = self.value.get_property(&prop_name)?; self.idx += 1; diff --git a/napi/src/js_values/function.rs b/napi/src/js_values/function.rs index c3497efa..a2190a3d 100644 --- a/napi/src/js_values/function.rs +++ b/napi/src/js_values/function.rs @@ -42,7 +42,7 @@ impl JsFunction { raw_args[i] = arg.0.value; } let mut return_value = ptr::null_mut(); - let status = unsafe { + check_status(unsafe { sys::napi_call_function( self.0.env, raw_this, @@ -51,8 +51,7 @@ impl JsFunction { &raw_args[0], &mut return_value, ) - }; - check_status(status)?; + })?; JsUnknown::from_raw(self.0.env, return_value) } diff --git a/napi/src/js_values/object.rs b/napi/src/js_values/object.rs index 4e4cdba3..e4731a7e 100644 --- a/napi/src/js_values/object.rs +++ b/napi/src/js_values/object.rs @@ -3,37 +3,35 @@ use std::ptr; use super::Value; use crate::error::check_status; -use crate::{sys, Env, Error, JsBuffer, JsNumber, JsString, NapiValue, Result, Status}; +use crate::{sys, Error, JsString, NapiValue, Property, Result, Status}; #[derive(Debug)] pub struct JsObject(pub(crate) Value); impl JsObject { - pub fn set_property(&mut self, key: JsString, value: V) -> Result<()> { - let status = - unsafe { sys::napi_set_property(self.0.env, self.0.value, key.0.value, value.raw_value()) }; - check_status(status)?; - Ok(()) - } - - pub fn set_number_indexed_property( - &mut self, - key: JsNumber, - value: V, - ) -> Result<()> { + pub fn set_property(&mut self, key: JsString, value: V) -> Result<()> + where + V: NapiValue, + { check_status(unsafe { sys::napi_set_property(self.0.env, self.0.value, key.0.value, value.raw_value()) }) } - pub fn set_named_property(&mut self, name: &str, value: T) -> Result<()> { + pub fn set_named_property(&mut self, name: &str, value: T) -> Result<()> + where + T: NapiValue, + { 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()) }) } - pub fn get_named_property(&self, name: &str) -> Result { + pub fn get_named_property(&self, name: &str) -> Result + where + T: NapiValue, + { let key = CString::new(name)?; let mut raw_value = ptr::null_mut(); check_status(unsafe { @@ -42,7 +40,66 @@ impl JsObject { T::from_raw(self.0.env, raw_value) } - pub fn get_property(&self, key: &K) -> Result { + pub fn has_named_property(&self, name: &str) -> Result { + let mut result = false; + let key = CString::new(name)?; + check_status(unsafe { + sys::napi_has_named_property(self.0.env, self.0.value, key.as_ptr(), &mut result) + })?; + Ok(result) + } + + pub fn has_own_property(&self, key: &str) -> Result { + let mut result = false; + let string = CString::new(key)?; + let mut js_key = ptr::null_mut(); + check_status(unsafe { + sys::napi_create_string_utf8(self.0.env, string.as_ptr(), key.len() as _, &mut js_key) + })?; + check_status(unsafe { + sys::napi_has_own_property(self.0.env, self.0.value, js_key, &mut result) + })?; + Ok(result) + } + + pub fn has_own_property_js(&self, key: K) -> Result + where + K: NapiValue, + { + let mut result = false; + check_status(unsafe { + sys::napi_has_own_property(self.0.env, self.0.value, key.raw_value(), &mut result) + })?; + Ok(result) + } + + pub fn has_property(&self, name: &str) -> Result { + let string = CString::new(name)?; + let mut js_key = ptr::null_mut(); + let mut result = false; + check_status(unsafe { + sys::napi_create_string_utf8(self.0.env, string.as_ptr(), name.len() as _, &mut js_key) + })?; + check_status(unsafe { sys::napi_has_property(self.0.env, self.0.value, js_key, &mut result) })?; + Ok(result) + } + + pub fn has_property_js(&self, name: K) -> Result + where + K: NapiValue, + { + let mut result = false; + check_status(unsafe { + sys::napi_has_property(self.0.env, self.0.value, name.raw_value(), &mut result) + })?; + 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) @@ -50,18 +107,52 @@ impl JsObject { T::from_raw(self.0.env, raw_value) } - pub fn get_property_names(&self) -> Result { + pub fn get_property_names(&self) -> Result + where + T: NapiValue, + { let mut raw_value = ptr::null_mut(); let status = unsafe { sys::napi_get_property_names(self.0.env, self.0.value, &mut raw_value) }; check_status(status)?; T::from_raw(self.0.env, raw_value) } - pub fn set_index(&mut self, index: usize, value: T) -> Result<()> { - self.set_number_indexed_property(Env::from_raw(self.0.env).create_int64(index as i64)?, value) + pub fn get_prototype(&self) -> Result + where + T: NapiValue, + { + let mut result = ptr::null_mut(); + check_status(unsafe { sys::napi_get_prototype(self.0.env, self.0.value, &mut result) })?; + T::from_raw(self.0.env, result) } - pub fn get_index(&self, index: u32) -> Result { + pub fn set_element(&mut self, index: u32, value: T) -> Result<()> + where + T: NapiValue, + { + check_status(unsafe { + sys::napi_set_element(self.0.env, self.0.value, index, value.raw_value()) + }) + } + + pub fn has_element(&self, index: u32) -> Result { + let mut result = false; + check_status(unsafe { sys::napi_has_element(self.0.env, self.0.value, index, &mut result) })?; + Ok(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) + })?; + Ok(result) + } + + pub fn get_element(&self, index: u32) -> Result + where + T: NapiValue, + { let mut raw_value = ptr::null_mut(); check_status(unsafe { sys::napi_get_element(self.0.env, self.0.value, index, &mut raw_value) @@ -69,6 +160,21 @@ impl JsObject { T::from_raw(self.0.env, raw_value) } + pub fn define_properties(&mut self, properties: &mut [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::>>()? + .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) })?; @@ -81,10 +187,6 @@ impl JsObject { Ok(is_buffer) } - pub fn to_buffer(&self) -> Result { - JsBuffer::from_raw(self.0.env, self.0.value) - } - pub fn get_array_length(&self) -> Result { if self.is_array()? != true { return Err(Error::new( diff --git a/napi/src/js_values/ser.rs b/napi/src/js_values/ser.rs index aff74e06..3552c106 100644 --- a/napi/src/js_values/ser.rs +++ b/napi/src/js_values/ser.rs @@ -310,8 +310,8 @@ impl ser::SerializeSeq for SeqSerializer { T: Serialize, { let env = Env::from_raw(self.array.0.env); - self.array.set_index( - self.current_index, + self.array.set_element( + self.current_index as _, JsUnknown(value.serialize(Ser::new(&env))?), )?; self.current_index = self.current_index + 1; @@ -334,8 +334,8 @@ impl ser::SerializeTuple for SeqSerializer { T: Serialize, { let env = Env::from_raw(self.array.0.env); - self.array.set_index( - self.current_index, + self.array.set_element( + self.current_index as _, JsUnknown(value.serialize(Ser::new(&env))?), )?; self.current_index = self.current_index + 1; @@ -359,8 +359,8 @@ impl ser::SerializeTupleStruct for SeqSerializer { T: Serialize, { let env = Env::from_raw(self.array.0.env); - self.array.set_index( - self.current_index, + self.array.set_element( + self.current_index as _, JsUnknown(value.serialize(Ser::new(&env))?), )?; self.current_index = self.current_index + 1; @@ -384,8 +384,8 @@ impl ser::SerializeTupleVariant for SeqSerializer { T: Serialize, { let env = Env::from_raw(self.array.0.env); - self.array.set_index( - self.current_index, + self.array.set_element( + self.current_index as _, JsUnknown(value.serialize(Ser::new(&env))?), )?; self.current_index = self.current_index + 1; diff --git a/test_module/__test__/object.spec.ts b/test_module/__test__/object.spec.ts new file mode 100644 index 00000000..b054a642 --- /dev/null +++ b/test_module/__test__/object.spec.ts @@ -0,0 +1,39 @@ +import test from 'ava' + +const bindings = require('../index.node') + +test('setProperty', (t) => { + const obj = {} + const key = 'jsPropertyKey' + bindings.testSetProperty(obj, key) + t.snapshot(obj[key]) +}) + +test('setNamedProperty', (t) => { + const obj = {} + const property = Symbol('JsSymbol') + bindings.testSetNamedProperty(obj, property) + const keys = Object.keys(obj) + const [key] = keys + t.is(keys.length, 1) + t.snapshot(key) + t.is(obj[key], property) +}) + +test('testGetNamedProperty', (t) => { + const obj = { + p: Symbol('JsSymbol'), + } + t.is(bindings.testGetNamedProperty(obj), obj.p) +}) + +test('testHasNamedProperty', (t) => { + const obj = { + a: 1, + b: undefined, + } + + t.true(bindings.testHasNamedProperty(obj, 'a')) + t.true(bindings.testHasNamedProperty(obj, 'b')) + t.false(bindings.testHasNamedProperty(obj, 'c')) +}) diff --git a/test_module/__test__/object.spec.ts.md b/test_module/__test__/object.spec.ts.md new file mode 100644 index 00000000..ae162d53 --- /dev/null +++ b/test_module/__test__/object.spec.ts.md @@ -0,0 +1,17 @@ +# Snapshot report for `test_module/__test__/object.spec.ts` + +The actual snapshot is saved in `object.spec.ts.snap`. + +Generated by [AVA](https://avajs.dev). + +## setProperty + +> Snapshot 1 + + 'Rust object property' + +## setNamedProperty + +> Snapshot 1 + + 'RustPropertyKey' diff --git a/test_module/__test__/object.spec.ts.snap b/test_module/__test__/object.spec.ts.snap new file mode 100644 index 0000000000000000000000000000000000000000..a3727419da3cd4791916034127e3a88db82112ea GIT binary patch literal 147 zcmV;E0Brw3RzVGbyBWnR(u%T_*{aUzhB0R)tQSj*(%(_>i|c^db8ePGUDAj1e2wFhEm z26nJ2MpgzvMv;u3}Yq^#8B5`}`I{DRb?l1da6{2&zpP#N#kN&vq5W%zjj008y- BKtBKg literal 0 HcmV?d00001 diff --git a/test_module/src/buffer.rs b/test_module/src/buffer.rs index a8884054..9b37edf0 100644 --- a/test_module/src/buffer.rs +++ b/test_module/src/buffer.rs @@ -1,6 +1,6 @@ use std::str; -use napi::{CallContext, Error, JsBuffer, JsNumber, JsString, Result, Status}; +use napi::{CallContext, Error, JsBuffer, JsNumber, JsString, Module, Result, Status}; #[js_function(1)] pub fn get_buffer_length(ctx: CallContext) -> Result { @@ -15,3 +15,9 @@ pub fn buffer_to_string(ctx: CallContext) -> Result { str::from_utf8(&buffer).map_err(|e| Error::new(Status::StringExpected, format!("{}", e)))?, ) } + +pub fn register_js(module: &mut Module) -> Result<()> { + module.create_named_method("getBufferLength", get_buffer_length)?; + module.create_named_method("bufferToString", buffer_to_string)?; + Ok(()) +} diff --git a/test_module/src/class.rs b/test_module/src/class.rs index f7e3af8c..ff276a43 100644 --- a/test_module/src/class.rs +++ b/test_module/src/class.rs @@ -1,18 +1,20 @@ use std::convert::TryInto; -use napi::{CallContext, JsFunction, JsNumber, JsObject, JsUndefined, Property, Result}; +use napi::{CallContext, JsFunction, JsNumber, JsObject, JsUndefined, Module, Property, Result}; #[js_function(1)] -pub fn create_test_class(ctx: CallContext) -> Result { +fn create_test_class(ctx: CallContext) -> Result { let add_count_method = Property::new("addCount").with_method(add_count); - let properties = vec![add_count_method]; - ctx - .env - .define_class("TestClass", test_class_constructor, properties) + let mut properties = vec![add_count_method]; + ctx.env.define_class( + "TestClass", + test_class_constructor, + properties.as_mut_slice(), + ) } #[js_function(1)] -pub fn test_class_constructor(mut ctx: CallContext) -> Result { +fn test_class_constructor(mut ctx: CallContext) -> Result { let count = ctx.get::(0)?; ctx .this @@ -21,7 +23,7 @@ pub fn test_class_constructor(mut ctx: CallContext) -> Result) -> Result { +fn add_count(mut ctx: CallContext) -> Result { let add: i32 = ctx.get::(0)?.try_into()?; let count: i32 = ctx .this @@ -32,3 +34,8 @@ pub fn add_count(mut ctx: CallContext) -> Result { .set_named_property("count", ctx.env.create_int32(count + add)?)?; ctx.env.get_undefined() } + +pub fn register_js(module: &mut Module) -> Result<()> { + module.create_named_method("createTestClass", create_test_class)?; + Ok(()) +} diff --git a/test_module/src/either.rs b/test_module/src/either.rs index 530bfbd5..195b1ab7 100644 --- a/test_module/src/either.rs +++ b/test_module/src/either.rs @@ -1,6 +1,6 @@ use std::convert::TryInto; -use napi::{CallContext, Either, JsNumber, JsString, Result}; +use napi::{CallContext, Either, JsNumber, JsString, Module, Result}; #[js_function(1)] pub fn either_number_string(ctx: CallContext) -> Result> { @@ -27,3 +27,9 @@ pub fn dynamic_argument_length(ctx: CallContext) -> Result { ctx.env.create_uint32(42) } } + +pub fn register_js(module: &mut Module) -> Result<()> { + module.create_named_method("eitherNumberString", either_number_string)?; + module.create_named_method("dynamicArgumentLength", dynamic_argument_length)?; + Ok(()) +} diff --git a/test_module/src/env.rs b/test_module/src/env.rs new file mode 100644 index 00000000..33aaf692 --- /dev/null +++ b/test_module/src/env.rs @@ -0,0 +1,35 @@ +use napi::{CallContext, JsBoolean, JsUnknown, Module, Result}; + +#[js_function(2)] +pub fn instanceof(ctx: CallContext) -> Result { + let object = ctx.get::(0)?; + let constructor = ctx.get::(1)?; + ctx.env.get_boolean(object.instanceof(constructor)?) +} + +#[js_function(1)] +pub fn is_typedarray(ctx: CallContext) -> Result { + let js_value = ctx.get::(0)?; + ctx.env.get_boolean(js_value.is_typedarray()?) +} + +#[js_function(1)] +pub fn is_dataview(ctx: CallContext) -> Result { + let js_value = ctx.get::(0)?; + ctx.env.get_boolean(js_value.is_dataview()?) +} + +#[js_function(2)] +pub fn strict_equals(ctx: CallContext) -> Result { + let a: JsUnknown = ctx.get(0)?; + let b: JsUnknown = ctx.get(1)?; + ctx.env.get_boolean(ctx.env.strict_equals(a, b)?) +} + +pub fn register_js(module: &mut Module) -> Result<()> { + module.create_named_method("instanceof", instanceof)?; + module.create_named_method("isTypedarray", is_typedarray)?; + module.create_named_method("isDataview", is_dataview)?; + module.create_named_method("strictEquals", strict_equals)?; + Ok(()) +} diff --git a/test_module/src/error.rs b/test_module/src/error.rs new file mode 100644 index 00000000..b833eb65 --- /dev/null +++ b/test_module/src/error.rs @@ -0,0 +1,33 @@ +use napi::{CallContext, Error, JsBoolean, JsString, JsUnknown, Module, Result, Status}; + +#[js_function] +fn test_throw(_ctx: CallContext) -> Result { + Err(Error::from_status(Status::GenericFailure)) +} + +#[js_function(1)] +fn test_throw_with_reason(ctx: CallContext) -> Result { + let reason = ctx.get::(0)?; + Err(Error::new( + Status::GenericFailure, + reason.as_str()?.to_owned(), + )) +} + +#[js_function] +pub fn test_throw_with_panic(_ctx: CallContext) -> Result { + panic!("don't panic."); +} + +#[js_function(1)] +pub fn is_error(ctx: CallContext) -> Result { + let js_value = ctx.get::(0)?; + ctx.env.get_boolean(js_value.is_error()?) +} + +pub fn register_js(module: &mut Module) -> Result<()> { + module.create_named_method("testThrow", test_throw)?; + module.create_named_method("testThrowWithReason", test_throw_with_reason)?; + module.create_named_method("isError", is_error)?; + Ok(()) +} diff --git a/test_module/src/external.rs b/test_module/src/external.rs index b69edfb5..c46a6be4 100644 --- a/test_module/src/external.rs +++ b/test_module/src/external.rs @@ -1,6 +1,6 @@ use std::convert::TryInto; -use napi::{CallContext, JsExternal, JsNumber, Result}; +use napi::{CallContext, JsExternal, JsNumber, Module, Result}; struct NativeObject { count: i32, @@ -19,3 +19,9 @@ pub fn get_external_count(ctx: CallContext) -> Result { let native_object = ctx.env.get_value_external::(&attached_obj)?; ctx.env.create_int32(native_object.count) } + +pub fn register_js(module: &mut Module) -> Result<()> { + module.create_named_method("createExternal", create_external)?; + module.create_named_method("getExternalCount", get_external_count)?; + Ok(()) +} diff --git a/test_module/src/function.rs b/test_module/src/function.rs index ef9a9596..f9e33845 100644 --- a/test_module/src/function.rs +++ b/test_module/src/function.rs @@ -1,4 +1,4 @@ -use napi::{CallContext, JsFunction, JsNull, JsObject, Result}; +use napi::{CallContext, JsFunction, JsNull, JsObject, Module, Result}; #[js_function(1)] pub fn call_function(ctx: CallContext) -> Result { @@ -20,3 +20,9 @@ pub fn call_function_with_this(ctx: CallContext) -> Result { ctx.env.get_null() } + +pub fn register_js(module: &mut Module) -> Result<()> { + module.create_named_method("testCallFunction", call_function)?; + module.create_named_method("testCallFunctionWithThis", call_function_with_this)?; + Ok(()) +} diff --git a/test_module/src/lib.rs b/test_module/src/lib.rs index e8ff8315..5d30ecc5 100644 --- a/test_module/src/lib.rs +++ b/test_module/src/lib.rs @@ -5,7 +5,7 @@ extern crate napi_derive; #[macro_use] extern crate serde_derive; -use napi::{CallContext, Error, JsBoolean, JsString, JsUnknown, Module, Result, Status}; +use napi::{Module, Result}; #[cfg(napi4)] mod libuv; @@ -21,142 +21,44 @@ mod tokio_rt; mod buffer; mod class; mod either; +mod env; +mod error; mod external; mod function; mod napi_version; +mod object; mod serde; mod string; mod symbol; mod task; -use buffer::{buffer_to_string, get_buffer_length}; -use either::{dynamic_argument_length, either_number_string}; -use external::{create_external, get_external_count}; -use function::{call_function, call_function_with_this}; -#[cfg(napi4)] -use libuv::read_file::uv_read_file; -#[cfg(napi4)] -use napi4::{test_threadsafe_function, test_tokio_readfile, test_tsfn_error}; -#[cfg(napi5)] -use napi5::is_date::test_object_is_date; -#[cfg(napi6)] -use napi6::bigint::{ - test_create_bigint_from_i128, test_create_bigint_from_i64, test_create_bigint_from_u128, - test_create_bigint_from_u64, test_create_bigint_from_words, test_get_bigint_i64, - test_get_bigint_u64, test_get_bigint_words, -}; use napi_version::get_napi_version; -use symbol::{create_named_symbol, create_symbol_from_js_string, create_unnamed_symbol}; -use task::test_spawn_thread; -#[cfg(napi4)] -use tokio_rt::{error_from_tokio_future, test_execute_tokio_readfile}; register_module!(test_module, init); fn init(module: &mut Module) -> Result<()> { - serde::register_serde_func(module)?; - module.create_named_method("testThrow", test_throw)?; - module.create_named_method("testThrowWithReason", test_throw_with_reason)?; - module.create_named_method("testSpawnThread", test_spawn_thread)?; - module.create_named_method("createExternal", create_external)?; - module.create_named_method("getExternalCount", get_external_count)?; - module.create_named_method("getBufferLength", get_buffer_length)?; - module.create_named_method("bufferToString", buffer_to_string)?; - module.create_named_method("createNamedSymbol", create_named_symbol)?; - module.create_named_method("createUnnamedSymbol", create_unnamed_symbol)?; - module.create_named_method("createSymbolFromJsString", create_symbol_from_js_string)?; module.create_named_method("getNapiVersion", get_napi_version)?; - module.create_named_method("testCallFunction", call_function)?; - module.create_named_method("testCallFunctionWithThis", call_function_with_this)?; - module.create_named_method("eitherNumberString", either_number_string)?; - module.create_named_method("dynamicArgumentLength", dynamic_argument_length)?; - module.create_named_method("createTestClass", class::create_test_class)?; - module.create_named_method("concatString", string::concat_string)?; - module.create_named_method("instanceof", instanceof)?; - module.create_named_method("isError", is_error)?; - module.create_named_method("isTypedarray", is_typedarray)?; - module.create_named_method("isDataview", is_dataview)?; - module.create_named_method("strictEquals", strict_equals)?; + error::register_js(module)?; + string::register_js(module)?; + serde::register_js(module)?; + task::register_js(module)?; + external::register_js(module)?; + buffer::register_js(module)?; + either::register_js(module)?; + symbol::register_js(module)?; + function::register_js(module)?; + class::register_js(module)?; + env::register_js(module)?; + object::register_js(module)?; #[cfg(napi4)] - module.create_named_method("testExecuteTokioReadfile", test_execute_tokio_readfile)?; + napi4::register_js(module)?; #[cfg(napi4)] - module.create_named_method("testTsfnError", test_tsfn_error)?; + tokio_rt::register_js(module)?; #[cfg(napi4)] - module.create_named_method("testThreadsafeFunction", test_threadsafe_function)?; - #[cfg(napi4)] - module.create_named_method("testTokioReadfile", test_tokio_readfile)?; - #[cfg(napi4)] - module.create_named_method("testTokioError", error_from_tokio_future)?; - #[cfg(napi4)] - module.create_named_method("uvReadFile", uv_read_file)?; + libuv::read_file::register_js(module)?; #[cfg(napi5)] - module.create_named_method("testObjectIsDate", test_object_is_date)?; + napi5::register_js(module)?; #[cfg(napi6)] - module.create_named_method("testCreateBigintFromI64", test_create_bigint_from_i64)?; - #[cfg(napi6)] - module.create_named_method("testCreateBigintFromU64", test_create_bigint_from_u64)?; - #[cfg(napi6)] - module.create_named_method("testCreateBigintFromI128", test_create_bigint_from_i128)?; - #[cfg(napi6)] - module.create_named_method("testCreateBigintFromU128", test_create_bigint_from_u128)?; - #[cfg(napi6)] - module.create_named_method("testCreateBigintFromWords", test_create_bigint_from_words)?; - #[cfg(napi6)] - module.create_named_method("testGetBigintI64", test_get_bigint_i64)?; - #[cfg(napi6)] - module.create_named_method("testGetBigintU64", test_get_bigint_u64)?; - #[cfg(napi6)] - module.create_named_method("testGetBigintWords", test_get_bigint_words)?; + napi6::register_js(module)?; Ok(()) } - -#[js_function] -fn test_throw(_ctx: CallContext) -> Result { - Err(Error::from_status(Status::GenericFailure)) -} - -#[js_function(1)] -fn test_throw_with_reason(ctx: CallContext) -> Result { - let reason = ctx.get::(0)?; - Err(Error::new( - Status::GenericFailure, - reason.as_str()?.to_owned(), - )) -} - -#[js_function] -pub fn test_throw_with_panic(_ctx: CallContext) -> Result { - panic!("don't panic."); -} - -#[js_function(2)] -pub fn instanceof(ctx: CallContext) -> Result { - let object = ctx.get::(0)?; - let constructor = ctx.get::(1)?; - ctx.env.get_boolean(object.instanceof(constructor)?) -} - -#[js_function(1)] -pub fn is_error(ctx: CallContext) -> Result { - let js_value = ctx.get::(0)?; - ctx.env.get_boolean(js_value.is_error()?) -} - -#[js_function(1)] -pub fn is_typedarray(ctx: CallContext) -> Result { - let js_value = ctx.get::(0)?; - ctx.env.get_boolean(js_value.is_typedarray()?) -} - -#[js_function(1)] -pub fn is_dataview(ctx: CallContext) -> Result { - let js_value = ctx.get::(0)?; - ctx.env.get_boolean(js_value.is_dataview()?) -} - -#[js_function(2)] -pub fn strict_equals(ctx: CallContext) -> Result { - let a: JsUnknown = ctx.get(0)?; - let b: JsUnknown = ctx.get(1)?; - ctx.env.get_boolean(ctx.env.strict_equals(a, b)?) -} diff --git a/test_module/src/libuv/read_file.rs b/test_module/src/libuv/read_file.rs index 04258f56..cb4b0e75 100644 --- a/test_module/src/libuv/read_file.rs +++ b/test_module/src/libuv/read_file.rs @@ -1,9 +1,9 @@ -use std::thread; use std::fs; +use std::thread; -use futures::prelude::*; use futures::channel::oneshot; -use napi::{CallContext, Result, JsString, JsObject, Status, Error}; +use futures::prelude::*; +use napi::{CallContext, Error, JsObject, JsString, Module, Result, Status}; #[js_function(1)] pub fn uv_read_file(ctx: CallContext) -> Result { @@ -14,7 +14,15 @@ pub fn uv_read_file(ctx: CallContext) -> Result { let res = fs::read(p).map_err(|e| Error::new(Status::Unknown, format!("{}", e))); sender.send(res).expect("Send data failed"); }); - ctx.env.execute(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) - }) + ctx.env.execute( + 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), + ) +} + +pub fn register_js(module: &mut Module) -> Result<()> { + module.create_named_method("uvReadFile", uv_read_file)?; + Ok(()) } diff --git a/test_module/src/napi4/mod.rs b/test_module/src/napi4/mod.rs index 832dd8ee..52296cae 100644 --- a/test_module/src/napi4/mod.rs +++ b/test_module/src/napi4/mod.rs @@ -1,3 +1,12 @@ +use napi::{Module, Result}; + mod tsfn; -pub use tsfn::*; +use tsfn::*; + +pub fn register_js(module: &mut Module) -> Result<()> { + module.create_named_method("testTsfnError", test_tsfn_error)?; + module.create_named_method("testThreadsafeFunction", test_threadsafe_function)?; + module.create_named_method("testTokioReadfile", test_tokio_readfile)?; + Ok(()) +} diff --git a/test_module/src/napi5/is_date.rs b/test_module/src/napi5/date.rs similarity index 100% rename from test_module/src/napi5/is_date.rs rename to test_module/src/napi5/date.rs diff --git a/test_module/src/napi5/mod.rs b/test_module/src/napi5/mod.rs index ca06b83a..2554cc82 100644 --- a/test_module/src/napi5/mod.rs +++ b/test_module/src/napi5/mod.rs @@ -1 +1,8 @@ -pub mod is_date; +use napi::{Module, Result}; + +mod date; + +pub fn register_js(module: &mut Module) -> Result<()> { + module.create_named_method("testObjectIsDate", date::test_object_is_date)?; + Ok(()) +} diff --git a/test_module/src/napi6/bigint.rs b/test_module/src/napi6/bigint.rs index c5340227..2bd6eada 100644 --- a/test_module/src/napi6/bigint.rs +++ b/test_module/src/napi6/bigint.rs @@ -49,13 +49,7 @@ pub fn test_get_bigint_words(ctx: CallContext) -> Result { .create_bigint_from_words(true, vec![i64::max_value() as u64, i64::max_value() as u64])?; let mut js_arr = ctx.env.create_array_with_length(2)?; let (_signed, words) = js_bigint.get_words()?; - js_arr.set_number_indexed_property( - ctx.env.create_int64(0)?, - ctx.env.create_bigint_from_u64(words[0])?, - )?; - js_arr.set_number_indexed_property( - ctx.env.create_int64(1)?, - ctx.env.create_bigint_from_u64(words[1])?, - )?; + js_arr.set_element(0, ctx.env.create_bigint_from_u64(words[0])?)?; + js_arr.set_element(1, ctx.env.create_bigint_from_u64(words[1])?)?; Ok(js_arr) } diff --git a/test_module/src/napi6/mod.rs b/test_module/src/napi6/mod.rs index 8f8d1485..7e4121ff 100644 --- a/test_module/src/napi6/mod.rs +++ b/test_module/src/napi6/mod.rs @@ -1 +1,17 @@ -pub mod bigint; +use napi::{Module, Result}; + +mod bigint; + +use bigint::*; + +pub fn register_js(module: &mut Module) -> Result<()> { + module.create_named_method("testCreateBigintFromI64", test_create_bigint_from_i64)?; + module.create_named_method("testCreateBigintFromU64", test_create_bigint_from_u64)?; + module.create_named_method("testCreateBigintFromI128", test_create_bigint_from_i128)?; + module.create_named_method("testCreateBigintFromU128", test_create_bigint_from_u128)?; + module.create_named_method("testCreateBigintFromWords", test_create_bigint_from_words)?; + module.create_named_method("testGetBigintI64", test_get_bigint_i64)?; + module.create_named_method("testGetBigintU64", test_get_bigint_u64)?; + module.create_named_method("testGetBigintWords", test_get_bigint_words)?; + Ok(()) +} diff --git a/test_module/src/object.rs b/test_module/src/object.rs new file mode 100644 index 00000000..638406ba --- /dev/null +++ b/test_module/src/object.rs @@ -0,0 +1,38 @@ +use napi::{CallContext, JsBoolean, JsObject, JsString, JsUndefined, JsUnknown, Module, Result}; + +#[js_function(2)] +fn test_set_property(ctx: CallContext) -> Result { + let mut obj = ctx.get::(0)?; + let key = ctx.get::(1)?; + obj.set_property(key, ctx.env.create_string("Rust object property")?)?; + ctx.env.get_undefined() +} + +#[js_function(2)] +fn test_set_named_property(ctx: CallContext) -> Result { + let mut obj = ctx.get::(0)?; + let property = ctx.get::(1)?; + obj.set_named_property("RustPropertyKey", property)?; + ctx.env.get_undefined() +} + +#[js_function(1)] +fn test_get_named_property(ctx: CallContext) -> Result { + let obj = ctx.get::(0)?; + obj.get_named_property("p") +} + +#[js_function(2)] +fn test_has_named_property(ctx: CallContext) -> Result { + let obj = ctx.get::(0)?; + let key = ctx.get::(1)?; + ctx.env.get_boolean(obj.has_named_property(key.as_str()?)?) +} + +pub fn register_js(module: &mut Module) -> Result<()> { + module.create_named_method("testSetProperty", test_set_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)?; + Ok(()) +} diff --git a/test_module/src/serde.rs b/test_module/src/serde.rs index ebc2ce14..6c99c87f 100644 --- a/test_module/src/serde.rs +++ b/test_module/src/serde.rs @@ -162,7 +162,7 @@ fn roundtrip_object(ctx: CallContext) -> Result { ctx.env.to_js_value(&de_serialized) } -pub fn register_serde_func(m: &mut Module) -> Result<()> { +pub fn register_js(m: &mut Module) -> Result<()> { m.create_named_method("make_num_77", make_num_77)?; m.create_named_method("make_num_32", make_num_32)?; m.create_named_method("make_str_hello", make_str_hello)?; diff --git a/test_module/src/string.rs b/test_module/src/string.rs index 8728146e..125f5c3f 100644 --- a/test_module/src/string.rs +++ b/test_module/src/string.rs @@ -1,4 +1,4 @@ -use napi::{CallContext, JsString, Result}; +use napi::{CallContext, JsString, Module, Result}; #[js_function(1)] pub fn concat_string(ctx: CallContext) -> Result { @@ -6,3 +6,8 @@ pub fn concat_string(ctx: CallContext) -> Result { let out_string = format!("{} + Rust 🦀 string!", in_string.as_str()?); ctx.env.create_string_from_std(out_string) } + +pub fn register_js(module: &mut Module) -> Result<()> { + module.create_named_method("concatString", concat_string)?; + Ok(()) +} diff --git a/test_module/src/symbol.rs b/test_module/src/symbol.rs index ef912ac1..e4770b29 100644 --- a/test_module/src/symbol.rs +++ b/test_module/src/symbol.rs @@ -1,4 +1,4 @@ -use napi::{CallContext, JsString, JsSymbol, Result}; +use napi::{CallContext, JsString, JsSymbol, Module, Result}; #[js_function] pub fn create_named_symbol(ctx: CallContext) -> Result { @@ -15,3 +15,10 @@ pub fn create_symbol_from_js_string(ctx: CallContext) -> Result { let name = ctx.get::(0)?; ctx.env.create_symbol_from_js_string(name) } + +pub fn register_js(module: &mut Module) -> Result<()> { + module.create_named_method("createNamedSymbol", create_named_symbol)?; + module.create_named_method("createUnnamedSymbol", create_unnamed_symbol)?; + module.create_named_method("createSymbolFromJsString", create_symbol_from_js_string)?; + Ok(()) +} diff --git a/test_module/src/task.rs b/test_module/src/task.rs index d570f545..c2ad5f66 100644 --- a/test_module/src/task.rs +++ b/test_module/src/task.rs @@ -1,6 +1,6 @@ use std::convert::TryInto; -use napi::{CallContext, Env, JsNumber, JsObject, Result, Task}; +use napi::{CallContext, Env, JsNumber, JsObject, Module, Result, Task}; struct ComputeFib { n: u32, @@ -34,8 +34,13 @@ fn fibonacci_native(n: u32) -> u32 { } #[js_function(1)] -pub fn test_spawn_thread(ctx: CallContext) -> Result { +fn test_spawn_thread(ctx: CallContext) -> Result { let n = ctx.get::(0)?; let task = ComputeFib::new(n.try_into()?); ctx.env.spawn(task) } + +pub fn register_js(module: &mut Module) -> Result<()> { + module.create_named_method("testSpawnThread", test_spawn_thread)?; + Ok(()) +} diff --git a/test_module/src/tokio_rt/mod.rs b/test_module/src/tokio_rt/mod.rs index 5e6f6d4f..7d34e233 100644 --- a/test_module/src/tokio_rt/mod.rs +++ b/test_module/src/tokio_rt/mod.rs @@ -1,3 +1,11 @@ +use napi::{Module, Result}; + mod read_file; -pub use read_file::*; +use read_file::*; + +pub fn register_js(module: &mut Module) -> Result<()> { + module.create_named_method("testExecuteTokioReadfile", test_execute_tokio_readfile)?; + module.create_named_method("testTokioError", error_from_tokio_future)?; + Ok(()) +} From 1c3d9b4e9a0fe1172e91a17a965f33d025ee641a Mon Sep 17 00:00:00 2001 From: LongYinan Date: Thu, 3 Sep 2020 00:35:47 +0800 Subject: [PATCH 2/4] 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(()) } From d45b436d1652ca74e7da14ed0760643fe5eb4520 Mon Sep 17 00:00:00 2001 From: LongYinan Date: Thu, 3 Sep 2020 15:12:31 +0800 Subject: [PATCH 3/4] chore: upgrade deps --- package.json | 4 ++-- yarn.lock | 38 ++++++++++++++++++++++---------------- 2 files changed, 24 insertions(+), 18 deletions(-) diff --git a/package.json b/package.json index cab1a1ee..63d33c68 100644 --- a/package.json +++ b/package.json @@ -60,11 +60,11 @@ "@istanbuljs/nyc-config-typescript": "^1.0.1", "@swc-node/register": "^0.4.5", "@types/inquirer": "^7.3.1", - "@types/node": "^14.6.2", + "@types/node": "^14.6.3", "@typescript-eslint/eslint-plugin": "^4.0.1", "@typescript-eslint/parser": "^4.0.1", "ava": "^3.12.1", - "eslint": "^7.8.0", + "eslint": "^7.8.1", "eslint-config-prettier": "^6.11.0", "eslint-plugin-import": "^2.22.0", "eslint-plugin-prettier": "^3.1.4", diff --git a/yarn.lock b/yarn.lock index 4931b625..145c8e9a 100644 --- a/yarn.lock +++ b/yarn.lock @@ -183,14 +183,20 @@ dependencies: arrify "^1.0.1" -"@eslint/eslintrc@^0.1.0": - version "0.1.0" - resolved "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-0.1.0.tgz#3d1f19fb797d42fb1c85458c1c73541eeb1d9e76" - integrity sha512-bfL5365QSCmH6cPeFT7Ywclj8C7LiF7sO6mUGzZhtAMV7iID1Euq6740u/SRi4C80NOnVz/CEfK8/HO+nCAPJg== +"@eslint/eslintrc@^0.1.3": + version "0.1.3" + resolved "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-0.1.3.tgz#7d1a2b2358552cc04834c0979bd4275362e37085" + integrity sha512-4YVwPkANLeNtRjMekzux1ci8hIaH5eGKktGqR0d3LWsKNn5B2X/1Z6Trxy7jQXl9EBGE6Yj02O+t09FMeRllaA== dependencies: ajv "^6.12.4" debug "^4.1.1" + espree "^7.3.0" + globals "^12.1.0" + ignore "^4.0.6" import-fresh "^3.2.1" + js-yaml "^3.13.1" + lodash "^4.17.19" + minimatch "^3.0.4" strip-json-comments "^3.1.1" "@istanbuljs/load-nyc-config@^1.0.0": @@ -340,10 +346,10 @@ resolved "https://registry.npmjs.org/@types/minimatch/-/minimatch-3.0.3.tgz#3dca0e3f33b200fc7d1139c0cd96c1268cadfd9d" integrity sha512-tHq6qdbT9U1IRSGf14CL0pUlULksvY9OZ+5eEgl1N7t+OA3tGvNpxJCzuKQlsNgCVwbAs670L1vcVQi8j9HjnA== -"@types/node@*", "@types/node@^14.6.2": - version "14.6.2" - resolved "https://registry.npmjs.org/@types/node/-/node-14.6.2.tgz#264b44c5a28dfa80198fc2f7b6d3c8a054b9491f" - integrity sha512-onlIwbaeqvZyniGPfdw/TEhKIh79pz66L1q06WUQqJLnAb6wbjvOtepLYTGHTqzdXgBYIE3ZdmqHDGsRsbBz7A== +"@types/node@*", "@types/node@^14.6.3": + version "14.6.3" + resolved "https://registry.npmjs.org/@types/node/-/node-14.6.3.tgz#cc4f979548ca4d8e7b90bc0180052ab99ee64224" + integrity sha512-pC/hkcREG6YfDfui1FBmj8e20jFU5Exjw4NYDm8kEdrW+mOh0T1Zve8DWKnS7ZIZvgncrctcNCXF4Q2I+loyww== "@types/normalize-package-data@^2.4.0": version "2.4.0" @@ -1318,13 +1324,13 @@ eslint-visitor-keys@^2.0.0: resolved "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-2.0.0.tgz#21fdc8fbcd9c795cc0321f0563702095751511a8" integrity sha512-QudtT6av5WXels9WjIM7qz1XD1cWGvX4gGXvp/zBn9nXG02D0utdU3Em2m/QjTnrsk6bBjmCygl3rmj118msQQ== -eslint@^7.8.0: - version "7.8.0" - resolved "https://registry.npmjs.org/eslint/-/eslint-7.8.0.tgz#9a3e2e6e4d0a3f8c42686073c25ebf2e91443e8a" - integrity sha512-qgtVyLZqKd2ZXWnLQA4NtVbOyH56zivOAdBFWE54RFkSZjokzNrcP4Z0eVWsZ+84ByXv+jL9k/wE1ENYe8xRFw== +eslint@^7.8.1: + version "7.8.1" + resolved "https://registry.npmjs.org/eslint/-/eslint-7.8.1.tgz#e59de3573fb6a5be8ff526c791571646d124a8fa" + integrity sha512-/2rX2pfhyUG0y+A123d0ccXtMm7DV7sH1m3lk9nk2DZ2LReq39FXHueR9xZwshE5MdfSf0xunSaMWRqyIA6M1w== dependencies: "@babel/code-frame" "^7.0.0" - "@eslint/eslintrc" "^0.1.0" + "@eslint/eslintrc" "^0.1.3" ajv "^6.10.0" chalk "^4.0.0" cross-spawn "^7.0.2" @@ -2127,9 +2133,9 @@ json-parse-better-errors@^1.0.1: integrity sha512-mrqyZKfX5EhL7hvqcV6WG1yYjnjeuYDzDhhcAAUrq8Po85NBQBJP+ZDUT75qZQ98IkUoBqdkExkukOU7Ts2wrw== json-parse-even-better-errors@^2.3.0: - version "2.3.0" - resolved "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.0.tgz#371873c5ffa44304a6ba12419bcfa95f404ae081" - integrity sha512-o3aP+RsWDJZayj1SbHNQAI8x0v3T3SKiGoZlNYfbUP1S3omJQ6i9CnqADqkSPaOAxwua4/1YWx5CM7oiChJt2Q== + version "2.3.1" + resolved "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz#7c47805a94319928e05777405dc12e1f7a4ee02d" + integrity sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w== json-schema-traverse@^0.4.1: version "0.4.1" From 87f75803f82e1b685c5f0065f5ac418b3540e36a Mon Sep 17 00:00:00 2001 From: LongYinan Date: Thu, 3 Sep 2020 15:14:23 +0800 Subject: [PATCH 4/4] docs: update readme --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 6f09dc08..17b91c81 100644 --- a/README.md +++ b/README.md @@ -169,7 +169,7 @@ yarn test | [napi_get_array_length](https://nodejs.org/api/n-api.html#n_api_napi_get_array_length) | 1 | v8.0.0 | ✅ | | [napi_get_arraybuffer_info](https://nodejs.org/api/n-api.html#n_api_napi_get_arraybuffer_info) | 1 | v8.0.0 | ✅ | | [napi_get_buffer_info](https://nodejs.org/api/n-api.html#n_api_napi_get_buffer_info) | 1 | v8.0.0 | ✅ | -| [napi_get_prototype](https://nodejs.org/api/n-api.html#n_api_napi_get_prototype) | 1 | v8.0.0 | ⛔️ | +| [napi_get_prototype](https://nodejs.org/api/n-api.html#n_api_napi_get_prototype) | 1 | v8.0.0 | ✅ | | [napi_get_typedarray_info](https://nodejs.org/api/n-api.html#n_api_napi_get_typedarray_info) | 1 | v8.0.0 | ⛔️ | | [napi_get_dataview_info](https://nodejs.org/api/n-api.html#n_api_napi_get_dataview_info) | 1 | v8.3.0 | ⛔️ | | [napi_get_date_value](https://nodejs.org/api/n-api.html#n_api_napi_get_date_value) | 5 | v11.11.0 | ⛔️ |