From a532451fc0d1d49e0e34f18fd9f9594860e2bcb9 Mon Sep 17 00:00:00 2001 From: LongYinan Date: Fri, 2 Oct 2020 23:23:21 +0800 Subject: [PATCH 1/4] feat(napi): refactor Env::get_global --- napi/src/env.rs | 4 +- napi/src/js_values/arraybuffer.rs | 6 +- napi/src/js_values/buffer.rs | 6 +- napi/src/js_values/function.rs | 11 +- napi/src/js_values/global.rs | 29 ++++ napi/src/js_values/mod.rs | 216 +++++++++++++++++++++++++++++- napi/src/js_values/object.rs | 215 ----------------------------- 7 files changed, 256 insertions(+), 231 deletions(-) create mode 100644 napi/src/js_values/global.rs diff --git a/napi/src/env.rs b/napi/src/env.rs index 7a270583..26ffac17 100644 --- a/napi/src/env.rs +++ b/napi/src/env.rs @@ -494,10 +494,10 @@ impl Env { result } - pub fn get_global(&self) -> Result { + pub fn get_global(&self) -> Result { let mut raw_global = ptr::null_mut(); check_status(unsafe { sys::napi_get_global(self.0, &mut raw_global) })?; - Ok(JsObject::from_raw_unchecked(self.0, raw_global)) + Ok(JsGlobal::from_raw_unchecked(self.0, raw_global)) } pub fn get_napi_version(&self) -> Result { diff --git a/napi/src/js_values/arraybuffer.rs b/napi/src/js_values/arraybuffer.rs index 0e257696..c49a807f 100644 --- a/napi/src/js_values/arraybuffer.rs +++ b/napi/src/js_values/arraybuffer.rs @@ -4,7 +4,7 @@ use std::ptr; use super::Value; use crate::error::check_status; -use crate::{sys, JsUnknown, Ref, Result}; +use crate::{sys, JsUnknown, NapiValue, Ref, Result}; #[repr(transparent)] #[derive(Debug, Clone, Copy)] @@ -49,8 +49,8 @@ impl JsArrayBufferValue { self.value } - pub fn into_unknown(self) -> Result { - self.value.into_unknown() + pub fn into_unknown(self) -> JsUnknown { + JsUnknown::from_raw_unchecked(self.value.0.env, self.value.0.value) } } diff --git a/napi/src/js_values/buffer.rs b/napi/src/js_values/buffer.rs index 110d6ba6..b0eac81d 100644 --- a/napi/src/js_values/buffer.rs +++ b/napi/src/js_values/buffer.rs @@ -6,7 +6,7 @@ use super::Value; #[cfg(feature = "serde-json")] use super::ValueType; use crate::error::check_status; -use crate::{sys, JsUnknown, Ref, Result}; +use crate::{sys, JsUnknown, NapiValue, Ref, Result}; #[repr(transparent)] #[derive(Debug, Clone, Copy)] @@ -68,8 +68,8 @@ impl JsBufferValue { self.value } - pub fn into_unknown(self) -> Result { - self.value.into_unknown() + pub fn into_unknown(self) -> JsUnknown { + JsUnknown::from_raw_unchecked(self.value.0.env, self.value.0.value) } } diff --git a/napi/src/js_values/function.rs b/napi/src/js_values/function.rs index 66b35884..6d456260 100644 --- a/napi/src/js_values/function.rs +++ b/napi/src/js_values/function.rs @@ -1,4 +1,3 @@ -use std::mem; use std::ptr; use super::Value; @@ -38,10 +37,10 @@ impl JsFunction { Status::Unknown, "Get raw this failed".to_owned(), ))?; - let mut raw_args = unsafe { mem::MaybeUninit::<[sys::napi_value; 8]>::uninit().assume_init() }; - for (i, arg) in args.into_iter().enumerate() { - raw_args[i] = arg.0.value; - } + let raw_args = args + .iter() + .map(|arg| arg.0.value) + .collect::>(); let mut return_value = ptr::null_mut(); check_status(unsafe { sys::napi_call_function( @@ -49,7 +48,7 @@ impl JsFunction { raw_this, self.0.value, args.len() as u64, - &raw_args[0], + raw_args.as_ptr(), &mut return_value, ) })?; diff --git a/napi/src/js_values/global.rs b/napi/src/js_values/global.rs new file mode 100644 index 00000000..0d1f880b --- /dev/null +++ b/napi/src/js_values/global.rs @@ -0,0 +1,29 @@ +use std::convert::TryInto; + +use super::*; +use crate::Env; + +#[repr(transparent)] +#[derive(Debug)] +pub struct JsGlobal(pub(crate) Value); + +#[repr(transparent)] +#[derive(Debug)] +pub struct JsTimeout(pub(crate) Value); + +impl JsGlobal { + pub fn set_interval(&self, handler: JsFunction, interval: f64) -> Result { + let func: JsFunction = self.get_named_property("setInterval")?; + func + .call( + None, + &[ + handler.into_unknown(), + Env::from_raw(self.0.env) + .create_double(interval)? + .into_unknown(), + ], + ) + .and_then(|ret| ret.try_into()) + } +} diff --git a/napi/src/js_values/mod.rs b/napi/src/js_values/mod.rs index 5075f4ae..1eb7f065 100644 --- a/napi/src/js_values/mod.rs +++ b/napi/src/js_values/mod.rs @@ -1,4 +1,5 @@ use std::convert::{From, TryFrom}; +use std::ffi::CString; use std::ptr; use crate::error::check_status; @@ -17,6 +18,7 @@ mod buffer; mod either; mod escapable_handle_scope; mod function; +mod global; mod number; mod object; mod object_property; @@ -37,6 +39,7 @@ pub(crate) use de::De; pub use either::Either; pub use escapable_handle_scope::EscapableHandleScope; pub use function::JsFunction; +pub use global::*; pub use number::JsNumber; pub use object::JsObject; pub use object_property::Property; @@ -120,8 +123,8 @@ macro_rules! impl_js_value_methods { ($js_value:ident) => { impl $js_value { #[inline] - pub fn into_unknown(self) -> Result { - JsUnknown::from_raw(self.0.env, self.0.value) + pub fn into_unknown(self) -> JsUnknown { + JsUnknown::from_raw_unchecked(self.0.env, self.0.value) } #[inline] @@ -219,6 +222,206 @@ macro_rules! impl_js_value_methods { }; } +macro_rules! impl_object_methods { + ($js_value:ident) => { + impl $js_value { + 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()) + }) + } + 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(), &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, + { + let key = CString::new(name)?; + check_status(unsafe { + sys::napi_set_named_property(self.0.env, self.0.value, key.as_ptr(), value.raw()) + }) + } + 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 { + sys::napi_get_named_property(self.0.env, self.0.value, key.as_ptr(), &mut raw_value) + })?; + T::from_raw(self.0.env, raw_value) + } + pub fn has_named_property(&self, name: S) -> Result + where + S: AsRef, + { + let mut result = false; + 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(), &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)?; + 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(), &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(), &mut result) + })?; + Ok(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 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 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()) }) + } + 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) + })?; + T::from_raw(self.0.env, raw_value) + } + 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() + .map(|property| property.raw()) + .collect::>() + .as_ptr(), + ) + }) + } + pub fn get_array_length(&self) -> Result { + if self.is_array()? != true { + return Err(Error::new( + Status::ArrayExpected, + "Object is not array".to_owned(), + )); + } + self.get_array_length_unchecked() + } + #[inline] + pub fn get_array_length_unchecked(&self) -> Result { + let mut length: u32 = 0; + check_status(unsafe { sys::napi_get_array_length(self.0.env, self.raw(), &mut length) })?; + Ok(length) + } + } + }; +} + pub trait NapiValue: Sized { fn from_raw(env: sys::napi_env, value: sys::napi_value) -> Result; @@ -236,9 +439,16 @@ impl_js_value_methods!(JsBuffer); impl_js_value_methods!(JsNumber); impl_js_value_methods!(JsString); impl_js_value_methods!(JsObject); +impl_js_value_methods!(JsGlobal); impl_js_value_methods!(JsFunction); impl_js_value_methods!(JsExternal); impl_js_value_methods!(JsSymbol); +impl_js_value_methods!(JsTimeout); + +impl_object_methods!(JsObject); +impl_object_methods!(JsBuffer); +impl_object_methods!(JsArrayBuffer); +impl_object_methods!(JsGlobal); use ValueType::*; @@ -250,6 +460,8 @@ impl_napi_value_trait!(JsArrayBuffer, Object); impl_napi_value_trait!(JsNumber, Number); impl_napi_value_trait!(JsString, String); impl_napi_value_trait!(JsObject, Object); +impl_napi_value_trait!(JsGlobal, Object); +impl_napi_value_trait!(JsTimeout, Object); impl_napi_value_trait!(JsFunction, Function); impl_napi_value_trait!(JsExternal, External); impl_napi_value_trait!(JsSymbol, Symbol); diff --git a/napi/src/js_values/object.rs b/napi/src/js_values/object.rs index 0a248f0a..319b45ac 100644 --- a/napi/src/js_values/object.rs +++ b/napi/src/js_values/object.rs @@ -1,220 +1,5 @@ -use std::ffi::CString; -use std::ptr; - use super::Value; -use crate::error::check_status; -use crate::{sys, Error, JsString, NapiValue, Property, Result, Status}; #[repr(transparent)] #[derive(Debug)] pub struct JsObject(pub(crate) Value); - -impl JsObject { - 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()) - }) - } - - 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(), &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, - { - let key = CString::new(name)?; - check_status(unsafe { - sys::napi_set_named_property(self.0.env, self.0.value, key.as_ptr(), value.raw()) - }) - } - - 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 { - sys::napi_get_named_property(self.0.env, self.0.value, key.as_ptr(), &mut raw_value) - })?; - T::from_raw(self.0.env, raw_value) - } - - pub fn has_named_property(&self, name: S) -> Result - where - S: AsRef, - { - let mut result = false; - 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(), &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)?; - 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(), &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(), &mut result) - })?; - Ok(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 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 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()) }) - } - - 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) - })?; - T::from_raw(self.0.env, raw_value) - } - - 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() - .map(|property| property.raw()) - .collect::>() - .as_ptr(), - ) - }) - } - - pub fn get_array_length(&self) -> Result { - if self.is_array()? != true { - return Err(Error::new( - Status::ArrayExpected, - "Object is not array".to_owned(), - )); - } - self.get_array_length_unchecked() - } - - #[inline] - pub fn get_array_length_unchecked(&self) -> Result { - let mut length: u32 = 0; - check_status(unsafe { sys::napi_get_array_length(self.0.env, self.raw(), &mut length) })?; - Ok(length) - } -} From 43c01796ebc823db831de28c3f5f7347eb3732dd Mon Sep 17 00:00:00 2001 From: LongYinan Date: Sat, 3 Oct 2020 00:05:43 +0800 Subject: [PATCH 2/4] feat(napi): implement JsTimeout --- napi/src/js_values/global.rs | 29 ++++++ package.json | 23 ++++- test_module/__test__/get-napi-version.spec.ts | 1 - test_module/__test__/global.spec.ts | 17 ++++ test_module/src/function.rs | 4 +- test_module/src/global.rs | 18 ++++ test_module/src/lib.rs | 2 + test_module/src/napi4/tsfn.rs | 6 +- yarn.lock | 96 ++++++++++++++++++- 9 files changed, 183 insertions(+), 13 deletions(-) create mode 100644 test_module/__test__/global.spec.ts create mode 100644 test_module/src/global.rs diff --git a/napi/src/js_values/global.rs b/napi/src/js_values/global.rs index 0d1f880b..b67c1641 100644 --- a/napi/src/js_values/global.rs +++ b/napi/src/js_values/global.rs @@ -26,4 +26,33 @@ impl JsGlobal { ) .and_then(|ret| ret.try_into()) } + + pub fn clear_interval(&self, timer: JsTimeout) -> Result { + let func: JsFunction = self.get_named_property("clearInterval")?; + func + .call(None, &[timer.into_unknown()]) + .and_then(|ret| ret.try_into()) + } + + pub fn set_timeout(&self, handler: JsFunction, interval: f64) -> Result { + let func: JsFunction = self.get_named_property("setTimeout")?; + func + .call( + None, + &[ + handler.into_unknown(), + Env::from_raw(self.0.env) + .create_double(interval)? + .into_unknown(), + ], + ) + .and_then(|ret| ret.try_into()) + } + + pub fn clear_timeout(&self, timer: JsTimeout) -> Result { + let func: JsFunction = self.get_named_property("clearTimeout")?; + func + .call(None, &[timer.into_unknown()]) + .and_then(|ret| ret.try_into()) + } } diff --git a/package.json b/package.json index bce5b6fe..24da3b22 100644 --- a/package.json +++ b/package.json @@ -58,12 +58,23 @@ "trailingComma": "all", "arrowParens": "always" }, - "files": ["scripts", "LICENSE"], + "files": [ + "scripts", + "LICENSE" + ], "lint-staged": { - "*.js": ["prettier --write"], - "*.@(yml|yaml)": ["prettier --parser yaml --write"], - "*.json": ["prettier --parser json --write"], - "*.md": ["prettier --parser markdown --write"] + "*.js": [ + "prettier --write" + ], + "*.@(yml|yaml)": [ + "prettier --parser yaml --write" + ], + "*.json": [ + "prettier --parser json --write" + ], + "*.md": [ + "prettier --parser markdown --write" + ] }, "husky": { "hooks": { @@ -77,6 +88,7 @@ "@types/inquirer": "^7.3.1", "@types/lodash": "^4.14.161", "@types/node": "^14.11.2", + "@types/sinon": "^9.0.7", "@typescript-eslint/eslint-plugin": "^4.3.0", "@typescript-eslint/parser": "^4.3.0", "ava": "^3.13.0", @@ -91,6 +103,7 @@ "npm-run-all": "^4.1.5", "nyc": "^15.1.0", "prettier": "^2.1.2", + "sinon": "^9.1.0", "source-map-support": "^0.5.19", "ts-node": "^9.0.0", "typescript": "^4.0.3" diff --git a/test_module/__test__/get-napi-version.spec.ts b/test_module/__test__/get-napi-version.spec.ts index 2e51c1a5..62fa68b8 100644 --- a/test_module/__test__/get-napi-version.spec.ts +++ b/test_module/__test__/get-napi-version.spec.ts @@ -5,6 +5,5 @@ const bindings = require('../index.node') test('should get napi version', (t) => { const napiVersion = bindings.getNapiVersion() t.true(typeof napiVersion === 'number') - // @ts-expect-error t.is(`${napiVersion}`, process.versions.napi) }) diff --git a/test_module/__test__/global.spec.ts b/test_module/__test__/global.spec.ts new file mode 100644 index 00000000..b321b2d4 --- /dev/null +++ b/test_module/__test__/global.spec.ts @@ -0,0 +1,17 @@ +import test from 'ava' +import Sinon from 'sinon' + +const bindings = require('../index.node') + +function wait(delay: number) { + return new Promise((resolve) => setTimeout(resolve, delay)) +} + +test('should setTimeout', async (t) => { + const handler = Sinon.spy() + const delay = 100 + bindings.setTimeout(handler, delay) + t.is(handler.callCount, 0) + await wait(delay + 10) + t.is(handler.callCount, 1) +}) diff --git a/test_module/src/function.rs b/test_module/src/function.rs index 4307c609..94cd8c6b 100644 --- a/test_module/src/function.rs +++ b/test_module/src/function.rs @@ -3,8 +3,8 @@ use napi::{CallContext, JsFunction, JsNull, JsObject, Module, Result}; #[js_function(1)] pub fn call_function(ctx: CallContext) -> Result { let js_func = ctx.get::(0)?; - let js_string_hello = ctx.env.create_string("hello".as_ref())?.into_unknown()?; - let js_string_world = ctx.env.create_string("world".as_ref())?.into_unknown()?; + let js_string_hello = ctx.env.create_string("hello".as_ref())?.into_unknown(); + let js_string_world = ctx.env.create_string("world".as_ref())?.into_unknown(); js_func.call(None, &[js_string_hello, js_string_world])?; diff --git a/test_module/src/global.rs b/test_module/src/global.rs new file mode 100644 index 00000000..fb70053a --- /dev/null +++ b/test_module/src/global.rs @@ -0,0 +1,18 @@ +use std::convert::TryInto; + +use napi::{CallContext, JsFunction, JsNumber, JsTimeout, Module, Result}; + +#[js_function(2)] +pub fn set_timeout(ctx: CallContext) -> Result { + let handler: JsFunction = ctx.get(0)?; + let timeout: JsNumber = ctx.get(1)?; + ctx + .env + .get_global()? + .set_timeout(handler, timeout.try_into()?) +} + +pub fn register_js(module: &mut Module) -> Result<()> { + module.create_named_method("setTimeout", set_timeout)?; + Ok(()) +} diff --git a/test_module/src/lib.rs b/test_module/src/lib.rs index 25435cf9..4c0ccabc 100644 --- a/test_module/src/lib.rs +++ b/test_module/src/lib.rs @@ -26,6 +26,7 @@ mod env; mod error; mod external; mod function; +mod global; mod napi_version; mod object; mod serde; @@ -52,6 +53,7 @@ fn init(module: &mut Module) -> Result<()> { class::register_js(module)?; env::register_js(module)?; object::register_js(module)?; + global::register_js(module)?; #[cfg(napi4)] napi4::register_js(module)?; #[cfg(napi4)] diff --git a/test_module/src/napi4/tsfn.rs b/test_module/src/napi4/tsfn.rs index 2281f7ef..214363c9 100644 --- a/test_module/src/napi4/tsfn.rs +++ b/test_module/src/napi4/tsfn.rs @@ -16,7 +16,7 @@ impl ToJs for HandleNumber { fn resolve(&self, env: &mut Env, output: Self::Output) -> Result> { let mut items: Vec = vec![]; for item in output.iter() { - let value = env.create_uint32((*item) as u32)?.into_unknown()?; + let value = env.create_uint32((*item) as u32)?.into_unknown(); items.push(value); } Ok(items) @@ -75,9 +75,7 @@ impl ToJs for HandleBuffer { type Output = Vec; fn resolve(&self, env: &mut Env, output: Self::Output) -> Result> { - let value = env - .create_buffer_with_data(output.to_vec())? - .into_unknown()?; + let value = env.create_buffer_with_data(output.to_vec())?.into_unknown(); Ok(vec![value]) } } diff --git a/yarn.lock b/yarn.lock index 5673d60c..82907dae 100644 --- a/yarn.lock +++ b/yarn.lock @@ -423,6 +423,42 @@ resolved "https://registry.npmjs.org/@sindresorhus/is/-/is-0.14.0.tgz#9fb3a3cf3132328151f353de4632e01e52102bea" integrity sha512-9NET910DNaIPngYnLLPeg+Ogzqsi9uM4mSboU5y6p8S5DzMTVEsJZrawi+BoDNUVBa2DhJqQYUFvMDfgU062LQ== +"@sinonjs/commons@^1", "@sinonjs/commons@^1.6.0", "@sinonjs/commons@^1.7.0", "@sinonjs/commons@^1.7.2": + version "1.8.1" + resolved "https://registry.npmjs.org/@sinonjs/commons/-/commons-1.8.1.tgz#e7df00f98a203324f6dc7cc606cad9d4a8ab2217" + integrity sha512-892K+kWUUi3cl+LlqEWIDrhvLgdL79tECi8JZUyq6IviKy/DNhuzCRlbHUjxK89f4ypPMMaFnFuR9Ie6DoIMsw== + dependencies: + type-detect "4.0.8" + +"@sinonjs/fake-timers@^6.0.0", "@sinonjs/fake-timers@^6.0.1": + version "6.0.1" + resolved "https://registry.npmjs.org/@sinonjs/fake-timers/-/fake-timers-6.0.1.tgz#293674fccb3262ac782c7aadfdeca86b10c75c40" + integrity sha512-MZPUxrmFubI36XS1DI3qmI0YdN1gks62JtFZvxR67ljjSNCeK6U08Zx4msEWOXuofgqUt6zPHSi1H9fbjR/NRA== + dependencies: + "@sinonjs/commons" "^1.7.0" + +"@sinonjs/formatio@^5.0.1": + version "5.0.1" + resolved "https://registry.npmjs.org/@sinonjs/formatio/-/formatio-5.0.1.tgz#f13e713cb3313b1ab965901b01b0828ea6b77089" + integrity sha512-KaiQ5pBf1MpS09MuA0kp6KBQt2JUOQycqVG1NZXvzeaXe5LGFqAKueIS0bw4w0P9r7KuBSVdUk5QjXsUdu2CxQ== + dependencies: + "@sinonjs/commons" "^1" + "@sinonjs/samsam" "^5.0.2" + +"@sinonjs/samsam@^5.0.2", "@sinonjs/samsam@^5.1.0": + version "5.1.0" + resolved "https://registry.npmjs.org/@sinonjs/samsam/-/samsam-5.1.0.tgz#3afe719232b541bb6cf3411a4c399a188de21ec0" + integrity sha512-42nyaQOVunX5Pm6GRJobmzbS7iLI+fhERITnETXzzwDZh+TtDr/Au3yAvXVjFmZ4wEUaE4Y3NFZfKv0bV0cbtg== + dependencies: + "@sinonjs/commons" "^1.6.0" + lodash.get "^4.4.2" + type-detect "^4.0.8" + +"@sinonjs/text-encoding@^0.7.1": + version "0.7.1" + resolved "https://registry.npmjs.org/@sinonjs/text-encoding/-/text-encoding-0.7.1.tgz#8da5c6530915653f3a1f38fd5f101d8c3f8079c5" + integrity sha512-+iTbntw2IZPb/anVDbypzfQa+ay64MW0Zo8aJ8gZPWMMK6/OubMVb6lUPMagqjOPnmtauXnFCACVl3O7ogjeqQ== + "@swc-node/core@^1.0.0": version "1.0.0" resolved "https://registry.npmjs.org/@swc-node/core/-/core-1.0.0.tgz#d719abe9d5aa49bb169153d1f3b8ef5364f6f30d" @@ -529,6 +565,18 @@ resolved "https://registry.npmjs.org/@types/parse-json/-/parse-json-4.0.0.tgz#2f8bb441434d163b35fb8ffdccd7138927ffb8c0" integrity sha512-//oorEZjL6sbPcKUaCdIGlIUeH26mgzimjBB77G6XRgnDl/L5wOnpyBGRe/Mmf5CVW3PwEBE1NjiMZ/ssFh4wA== +"@types/sinon@^9.0.7": + version "9.0.7" + resolved "https://registry.npmjs.org/@types/sinon/-/sinon-9.0.7.tgz#c277e19cf9eb0c71106e785650f1e5c299262302" + integrity sha512-uyFiy2gp4P/FK9pmU3WIbT5ZzH54hCswwRkQFhxX7xl8jzhW3g+xOkVqk5YP4cIO//Few8UDAX0MtzFpqBEqwA== + dependencies: + "@types/sinonjs__fake-timers" "*" + +"@types/sinonjs__fake-timers@*": + version "6.0.2" + resolved "https://registry.npmjs.org/@types/sinonjs__fake-timers/-/sinonjs__fake-timers-6.0.2.tgz#3a84cf5ec3249439015e14049bd3161419bf9eae" + integrity sha512-dIPoZ3g5gcx9zZEszaxLSVTvMReD3xxyyDnQUjA6IYDG9Ba2AV0otMPs+77sG9ojB4Qr2N2Vk5RnKeuA0X/0bg== + "@types/through@*": version "0.0.30" resolved "https://registry.npmjs.org/@types/through/-/through-0.0.30.tgz#e0e42ce77e897bd6aead6f6ea62aeb135b8a3895" @@ -1349,7 +1397,7 @@ deprecation@^2.0.0, deprecation@^2.3.1: resolved "https://registry.npmjs.org/deprecation/-/deprecation-2.3.1.tgz#6368cbdb40abf3373b525ac87e4a260c3a700919" integrity sha512-xmHIy4F3scKVwMsQ4WnVaS8bHOx0DmVwRywosKhaILI0ywMDWPtBSku2HNxRvF7jtwDRsoEwYQSfbxj8b7RlJQ== -diff@^4.0.1: +diff@^4.0.1, diff@^4.0.2: version "4.0.2" resolved "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz#60f3aecb89d5fae520c11aa19efc2bb982aade7d" integrity sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A== @@ -2304,6 +2352,11 @@ is-yarn-global@^0.3.0: resolved "https://registry.npmjs.org/is-yarn-global/-/is-yarn-global-0.3.0.tgz#d502d3382590ea3004893746754c89139973e232" integrity sha512-VjSeb/lHmkoyd8ryPVIKvOCn4D1koMqY+vqyjjUfc3xyKtP4dYOxM44sZrnqQSzSds3xyOrUTLTC9LVCVgLngw== +isarray@0.0.1: + version "0.0.1" + resolved "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz#8a18acfca9a8f4177e09abfc6038939b05d1eedf" + integrity sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8= + isarray@^1.0.0: version "1.0.0" resolved "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz#bb935d48582cba168c06834957a54a3e07124f11" @@ -2458,6 +2511,11 @@ jsonparse@^1.3.1: resolved "https://registry.npmjs.org/jsonparse/-/jsonparse-1.3.1.tgz#3f4dae4a91fac315f71062f8521cc239f1366280" integrity sha1-P02uSpH6wxX3EGL4UhzCOfE2YoA= +just-extend@^4.0.2: + version "4.1.1" + resolved "https://registry.npmjs.org/just-extend/-/just-extend-4.1.1.tgz#158f1fdb01f128c411dc8b286a7b4837b3545282" + integrity sha512-aWgeGFW67BP3e5181Ep1Fv2v8z//iBJfrvyTnq8wG86vEESwmonn1zPBJ0VfmT9CJq2FIT0VsETtrNFm2a+SHA== + keyv@^3.0.0: version "3.1.0" resolved "https://registry.npmjs.org/keyv/-/keyv-3.1.0.tgz#ecc228486f69991e49e9476485a5be1e8fc5c4d9" @@ -2775,6 +2833,17 @@ nice-try@^1.0.4: resolved "https://registry.npmjs.org/nice-try/-/nice-try-1.0.5.tgz#a3378a7696ce7d223e88fc9b764bd7ef1089e366" integrity sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ== +nise@^4.0.4: + version "4.0.4" + resolved "https://registry.npmjs.org/nise/-/nise-4.0.4.tgz#d73dea3e5731e6561992b8f570be9e363c4512dd" + integrity sha512-bTTRUNlemx6deJa+ZyoCUTRvH3liK5+N6VQZ4NIw90AgDXY6iPnsqplNFf6STcj+ePk0H/xqxnP75Lr0J0Fq3A== + dependencies: + "@sinonjs/commons" "^1.7.0" + "@sinonjs/fake-timers" "^6.0.0" + "@sinonjs/text-encoding" "^0.7.1" + just-extend "^4.0.2" + path-to-regexp "^1.7.0" + node-fetch@^2.6.1: version "2.6.1" resolved "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.1.tgz#045bd323631f76ed2e2b55573394416b639a0052" @@ -3144,6 +3213,13 @@ path-parse@^1.0.6: resolved "https://registry.npmjs.org/path-parse/-/path-parse-1.0.6.tgz#d62dbb5679405d72c4737ec58600e9ddcf06d24c" integrity sha512-GSmOT2EbHrINBf9SR7CDELwlJ8AENk3Qn7OikK4nFYAu3Ote2+JYNVvkpAEQm3/TLNEJFD/xZJjzyxg3KBWOzw== +path-to-regexp@^1.7.0: + version "1.8.0" + resolved "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-1.8.0.tgz#887b3ba9d84393e87a0a0b9f4cb756198b53548a" + integrity sha512-n43JRhlUKUAlibEJhPeir1ncUID16QnEjNpwzNdO3Lm4ywrBpBZ5oLD0I6br9evr1Y9JTqwRtAh7JLoOzAQdVA== + dependencies: + isarray "0.0.1" + path-type@^2.0.0: version "2.0.0" resolved "https://registry.npmjs.org/path-type/-/path-type-2.0.0.tgz#f012ccb8415b7096fc2daa1054c3d72389594c73" @@ -3580,6 +3656,19 @@ signal-exit@^3.0.0, signal-exit@^3.0.2: resolved "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.3.tgz#a1410c2edd8f077b08b4e253c8eacfcaf057461c" integrity sha512-VUJ49FC8U1OxwZLxIbTTrDvLnf/6TDgxZcK8wxR8zs13xpx7xbG60ndBlhNrFi2EMuFRoeDoJO7wthSLq42EjA== +sinon@^9.1.0: + version "9.1.0" + resolved "https://registry.npmjs.org/sinon/-/sinon-9.1.0.tgz#4afc90707c8e360fe051398eed2d3b197980ffc3" + integrity sha512-9zQShgaeylYH6qtsnNXlTvv0FGTTckuDfHBi+qhgj5PvW2r2WslHZpgc3uy3e/ZAoPkqaOASPi+juU6EdYRYxA== + dependencies: + "@sinonjs/commons" "^1.7.2" + "@sinonjs/fake-timers" "^6.0.1" + "@sinonjs/formatio" "^5.0.1" + "@sinonjs/samsam" "^5.1.0" + diff "^4.0.2" + nise "^4.0.4" + supports-color "^7.1.0" + slash@^3.0.0: version "3.0.0" resolved "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz#6539be870c165adbd5240220dbe361f1bc4d4634" @@ -3950,6 +4039,11 @@ type-check@^0.4.0, type-check@~0.4.0: dependencies: prelude-ls "^1.2.1" +type-detect@4.0.8, type-detect@^4.0.8: + version "4.0.8" + resolved "https://registry.npmjs.org/type-detect/-/type-detect-4.0.8.tgz#7646fb5f18871cfbb7749e69bd39a6388eb7450c" + integrity sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g== + type-fest@^0.11.0: version "0.11.0" resolved "https://registry.npmjs.org/type-fest/-/type-fest-0.11.0.tgz#97abf0872310fed88a5c466b25681576145e33f1" From e713d647f546fd5142f1f37c8db11a51d234b7f5 Mon Sep 17 00:00:00 2001 From: LongYinan Date: Sat, 3 Oct 2020 14:57:19 +0800 Subject: [PATCH 3/4] test(napi): add JsTimeout specs --- test_module/__test__/global.spec.ts | 12 +++++++++++- test_module/src/global.rs | 9 ++++++++- 2 files changed, 19 insertions(+), 2 deletions(-) diff --git a/test_module/__test__/global.spec.ts b/test_module/__test__/global.spec.ts index b321b2d4..4a363a46 100644 --- a/test_module/__test__/global.spec.ts +++ b/test_module/__test__/global.spec.ts @@ -7,11 +7,21 @@ function wait(delay: number) { return new Promise((resolve) => setTimeout(resolve, delay)) } +const delay = 100 + test('should setTimeout', async (t) => { const handler = Sinon.spy() - const delay = 100 bindings.setTimeout(handler, delay) t.is(handler.callCount, 0) await wait(delay + 10) t.is(handler.callCount, 1) }) + +test('should clearTimeout', async (t) => { + const handler = Sinon.spy() + const timer = setTimeout(() => handler(), delay) + t.is(handler.callCount, 0) + bindings.clearTimeout(timer) + await wait(delay + 10) + t.is(handler.callCount, 0) +}) diff --git a/test_module/src/global.rs b/test_module/src/global.rs index fb70053a..d21e0b0a 100644 --- a/test_module/src/global.rs +++ b/test_module/src/global.rs @@ -1,6 +1,6 @@ use std::convert::TryInto; -use napi::{CallContext, JsFunction, JsNumber, JsTimeout, Module, Result}; +use napi::{CallContext, JsFunction, JsNumber, JsTimeout, JsUndefined, Module, Result}; #[js_function(2)] pub fn set_timeout(ctx: CallContext) -> Result { @@ -12,7 +12,14 @@ pub fn set_timeout(ctx: CallContext) -> Result { .set_timeout(handler, timeout.try_into()?) } +#[js_function(1)] +pub fn clear_timeout(ctx: CallContext) -> Result { + let timer: JsTimeout = ctx.get(0)?; + ctx.env.get_global()?.clear_timeout(timer) +} + pub fn register_js(module: &mut Module) -> Result<()> { module.create_named_method("setTimeout", set_timeout)?; + module.create_named_method("clearTimeout", clear_timeout)?; Ok(()) } From d1870d2be2f81a705efedd50fc864b513f1dab6d Mon Sep 17 00:00:00 2001 From: LongYinan Date: Sat, 3 Oct 2020 22:34:52 +0800 Subject: [PATCH 4/4] style: run fmt --- package.json | 21 +++++---------------- 1 file changed, 5 insertions(+), 16 deletions(-) diff --git a/package.json b/package.json index 24da3b22..8cbda682 100644 --- a/package.json +++ b/package.json @@ -58,23 +58,12 @@ "trailingComma": "all", "arrowParens": "always" }, - "files": [ - "scripts", - "LICENSE" - ], + "files": ["scripts", "LICENSE"], "lint-staged": { - "*.js": [ - "prettier --write" - ], - "*.@(yml|yaml)": [ - "prettier --parser yaml --write" - ], - "*.json": [ - "prettier --parser json --write" - ], - "*.md": [ - "prettier --parser markdown --write" - ] + "*.js": ["prettier --write"], + "*.@(yml|yaml)": ["prettier --parser yaml --write"], + "*.json": ["prettier --parser json --write"], + "*.md": ["prettier --parser markdown --write"] }, "husky": { "hooks": {