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 00000000..a3727419 Binary files /dev/null and b/test_module/__test__/object.spec.ts.snap differ 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(()) +}