feat(napi): implement all object functions

This commit is contained in:
LongYinan 2020-09-02 17:05:53 +08:00 committed by LongYinan
parent 06da68df0e
commit 76d1258592
No known key found for this signature in database
GPG key ID: C3666B7FC82ADAD7
31 changed files with 520 additions and 207 deletions

View file

@ -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,

View file

@ -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<Self> {
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<V>(&self) -> Result<V>
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)
}
}

View file

@ -379,12 +379,12 @@ impl Env {
&self,
name: &str,
constructor_cb: Callback,
properties: Vec<Property>,
properties: &mut [Property],
) -> Result<JsFunction> {
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::<Result<Vec<sys::napi_property_descriptor>>>()?;
check_status(unsafe {

View file

@ -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<PropertyAttributes> 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<sys::napi_property_descriptor> {
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<sys::napi_property_descriptor> {
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)
}
}

View file

@ -122,7 +122,7 @@ impl<'x, 'de, 'env> serde::de::Deserializer<'x> for &'de mut De<'env> {
),
))
} else {
let key = properties.get_index::<JsString>(0)?;
let key = properties.get_element::<JsString>(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::<JsUnknown>(self.idx)?;
let v = self.input.get_element::<JsUnknown>(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::<JsUnknown>(self.idx)?;
let prop_name = self.properties.get_element::<JsUnknown>(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::<JsString>(self.idx)?;
let prop_name = self.properties.get_element::<JsString>(self.idx)?;
let value: JsUnknown = self.value.get_property(&prop_name)?;
self.idx += 1;

View file

@ -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)
}

View file

@ -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<V: NapiValue>(&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<V: NapiValue>(
&mut self,
key: JsNumber,
value: V,
) -> Result<()> {
pub fn set_property<V>(&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<T: NapiValue>(&mut self, name: &str, value: T) -> Result<()> {
pub fn set_named_property<T>(&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<T: NapiValue>(&self, name: &str) -> Result<T> {
pub fn get_named_property<T>(&self, name: &str) -> Result<T>
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<K: NapiValue, T: NapiValue>(&self, key: &K) -> Result<T> {
pub fn has_named_property(&self, name: &str) -> Result<bool> {
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<bool> {
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<K>(&self, key: K) -> Result<bool>
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<bool> {
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<K>(&self, name: K) -> Result<bool>
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<K, T>(&self, key: &K) -> Result<T>
where
K: NapiValue,
T: NapiValue,
{
let mut raw_value = ptr::null_mut();
check_status(unsafe {
sys::napi_get_property(self.0.env, self.0.value, key.raw_value(), &mut raw_value)
@ -50,18 +107,52 @@ impl JsObject {
T::from_raw(self.0.env, raw_value)
}
pub fn get_property_names<T: NapiValue>(&self) -> Result<T> {
pub fn get_property_names<T>(&self) -> Result<T>
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<T: NapiValue>(&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<T>(&self) -> Result<T>
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<T: NapiValue>(&self, index: u32) -> Result<T> {
pub fn set_element<T>(&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<bool> {
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<T>(&mut self, index: u32) -> Result<bool> {
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<T>(&self, index: u32) -> Result<T>
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::<Result<Vec<sys::napi_property_descriptor>>>()?
.as_ptr(),
)
})
}
pub fn is_array(&self) -> Result<bool> {
let mut is_array = false;
check_status(unsafe { sys::napi_is_array(self.0.env, self.0.value, &mut is_array) })?;
@ -81,10 +187,6 @@ impl JsObject {
Ok(is_buffer)
}
pub fn to_buffer(&self) -> Result<JsBuffer> {
JsBuffer::from_raw(self.0.env, self.0.value)
}
pub fn get_array_length(&self) -> Result<u32> {
if self.is_array()? != true {
return Err(Error::new(

View file

@ -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;

View file

@ -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'))
})

View file

@ -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'

Binary file not shown.

View file

@ -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<JsNumber> {
@ -15,3 +15,9 @@ pub fn buffer_to_string(ctx: CallContext) -> Result<JsString> {
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(())
}

View file

@ -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<JsFunction> {
fn create_test_class(ctx: CallContext) -> Result<JsFunction> {
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<JsObject>) -> Result<JsUndefined> {
fn test_class_constructor(mut ctx: CallContext<JsObject>) -> Result<JsUndefined> {
let count = ctx.get::<JsNumber>(0)?;
ctx
.this
@ -21,7 +23,7 @@ pub fn test_class_constructor(mut ctx: CallContext<JsObject>) -> Result<JsUndefi
}
#[js_function(1)]
pub fn add_count(mut ctx: CallContext<JsObject>) -> Result<JsUndefined> {
fn add_count(mut ctx: CallContext<JsObject>) -> Result<JsUndefined> {
let add: i32 = ctx.get::<JsNumber>(0)?.try_into()?;
let count: i32 = ctx
.this
@ -32,3 +34,8 @@ pub fn add_count(mut ctx: CallContext<JsObject>) -> Result<JsUndefined> {
.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(())
}

View file

@ -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<Either<JsNumber, JsString>> {
@ -27,3 +27,9 @@ pub fn dynamic_argument_length(ctx: CallContext) -> Result<JsNumber> {
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(())
}

35
test_module/src/env.rs Normal file
View file

@ -0,0 +1,35 @@
use napi::{CallContext, JsBoolean, JsUnknown, Module, Result};
#[js_function(2)]
pub fn instanceof(ctx: CallContext) -> Result<JsBoolean> {
let object = ctx.get::<JsUnknown>(0)?;
let constructor = ctx.get::<JsUnknown>(1)?;
ctx.env.get_boolean(object.instanceof(constructor)?)
}
#[js_function(1)]
pub fn is_typedarray(ctx: CallContext) -> Result<JsBoolean> {
let js_value = ctx.get::<JsUnknown>(0)?;
ctx.env.get_boolean(js_value.is_typedarray()?)
}
#[js_function(1)]
pub fn is_dataview(ctx: CallContext) -> Result<JsBoolean> {
let js_value = ctx.get::<JsUnknown>(0)?;
ctx.env.get_boolean(js_value.is_dataview()?)
}
#[js_function(2)]
pub fn strict_equals(ctx: CallContext) -> Result<JsBoolean> {
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(())
}

33
test_module/src/error.rs Normal file
View file

@ -0,0 +1,33 @@
use napi::{CallContext, Error, JsBoolean, JsString, JsUnknown, Module, Result, Status};
#[js_function]
fn test_throw(_ctx: CallContext) -> Result<JsUnknown> {
Err(Error::from_status(Status::GenericFailure))
}
#[js_function(1)]
fn test_throw_with_reason(ctx: CallContext) -> Result<JsUnknown> {
let reason = ctx.get::<JsString>(0)?;
Err(Error::new(
Status::GenericFailure,
reason.as_str()?.to_owned(),
))
}
#[js_function]
pub fn test_throw_with_panic(_ctx: CallContext) -> Result<JsUnknown> {
panic!("don't panic.");
}
#[js_function(1)]
pub fn is_error(ctx: CallContext) -> Result<JsBoolean> {
let js_value = ctx.get::<JsUnknown>(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(())
}

View file

@ -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<JsNumber> {
let native_object = ctx.env.get_value_external::<NativeObject>(&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(())
}

View file

@ -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<JsNull> {
@ -20,3 +20,9 @@ pub fn call_function_with_this(ctx: CallContext<JsObject>) -> Result<JsNull> {
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(())
}

View file

@ -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<JsUnknown> {
Err(Error::from_status(Status::GenericFailure))
}
#[js_function(1)]
fn test_throw_with_reason(ctx: CallContext) -> Result<JsUnknown> {
let reason = ctx.get::<JsString>(0)?;
Err(Error::new(
Status::GenericFailure,
reason.as_str()?.to_owned(),
))
}
#[js_function]
pub fn test_throw_with_panic(_ctx: CallContext) -> Result<JsUnknown> {
panic!("don't panic.");
}
#[js_function(2)]
pub fn instanceof(ctx: CallContext) -> Result<JsBoolean> {
let object = ctx.get::<JsUnknown>(0)?;
let constructor = ctx.get::<JsUnknown>(1)?;
ctx.env.get_boolean(object.instanceof(constructor)?)
}
#[js_function(1)]
pub fn is_error(ctx: CallContext) -> Result<JsBoolean> {
let js_value = ctx.get::<JsUnknown>(0)?;
ctx.env.get_boolean(js_value.is_error()?)
}
#[js_function(1)]
pub fn is_typedarray(ctx: CallContext) -> Result<JsBoolean> {
let js_value = ctx.get::<JsUnknown>(0)?;
ctx.env.get_boolean(js_value.is_typedarray()?)
}
#[js_function(1)]
pub fn is_dataview(ctx: CallContext) -> Result<JsBoolean> {
let js_value = ctx.get::<JsUnknown>(0)?;
ctx.env.get_boolean(js_value.is_dataview()?)
}
#[js_function(2)]
pub fn strict_equals(ctx: CallContext) -> Result<JsBoolean> {
let a: JsUnknown = ctx.get(0)?;
let b: JsUnknown = ctx.get(1)?;
ctx.env.get_boolean(ctx.env.strict_equals(a, b)?)
}

View file

@ -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<JsObject> {
@ -14,7 +14,15 @@ pub fn uv_read_file(ctx: CallContext) -> Result<JsObject> {
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(())
}

View file

@ -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(())
}

View file

@ -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(())
}

View file

@ -49,13 +49,7 @@ pub fn test_get_bigint_words(ctx: CallContext) -> Result<JsObject> {
.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)
}

View file

@ -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(())
}

38
test_module/src/object.rs Normal file
View file

@ -0,0 +1,38 @@
use napi::{CallContext, JsBoolean, JsObject, JsString, JsUndefined, JsUnknown, Module, Result};
#[js_function(2)]
fn test_set_property(ctx: CallContext) -> Result<JsUndefined> {
let mut obj = ctx.get::<JsObject>(0)?;
let key = ctx.get::<JsString>(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<JsUndefined> {
let mut obj = ctx.get::<JsObject>(0)?;
let property = ctx.get::<JsUnknown>(1)?;
obj.set_named_property("RustPropertyKey", property)?;
ctx.env.get_undefined()
}
#[js_function(1)]
fn test_get_named_property(ctx: CallContext) -> Result<JsUnknown> {
let obj = ctx.get::<JsObject>(0)?;
obj.get_named_property("p")
}
#[js_function(2)]
fn test_has_named_property(ctx: CallContext) -> Result<JsBoolean> {
let obj = ctx.get::<JsObject>(0)?;
let key = ctx.get::<JsString>(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(())
}

View file

@ -162,7 +162,7 @@ fn roundtrip_object(ctx: CallContext) -> Result<JsUnknown> {
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)?;

View file

@ -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<JsString> {
@ -6,3 +6,8 @@ pub fn concat_string(ctx: CallContext) -> Result<JsString> {
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(())
}

View file

@ -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<JsSymbol> {
@ -15,3 +15,10 @@ pub fn create_symbol_from_js_string(ctx: CallContext) -> Result<JsSymbol> {
let name = ctx.get::<JsString>(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(())
}

View file

@ -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<JsObject> {
fn test_spawn_thread(ctx: CallContext) -> Result<JsObject> {
let n = ctx.get::<JsNumber>(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(())
}

View file

@ -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(())
}