refactor(napi): split NapiRaw trait from NapiValue

This commit is contained in:
LongYinan 2021-04-01 18:50:38 +08:00
parent ba1d16759f
commit d4331e05df
No known key found for this signature in database
GPG key ID: C3666B7FC82ADAD7
9 changed files with 120 additions and 31 deletions

View file

@ -96,7 +96,7 @@ pub fn js_function(attr: TokenStream, input: TokenStream) -> TokenStream {
use std::ptr; use std::ptr;
use std::panic::{self, AssertUnwindSafe}; use std::panic::{self, AssertUnwindSafe};
use std::ffi::CString; use std::ffi::CString;
use napi::{Env, Error, Status, NapiValue, CallContext}; use napi::{Env, Error, Status, NapiValue, NapiRaw, CallContext};
let mut argc = #arg_len_span as usize; let mut argc = #arg_len_span as usize;
let mut raw_args: [napi::sys::napi_value; #arg_len_span] = [ptr::null_mut(); #arg_len_span]; let mut raw_args: [napi::sys::napi_value; #arg_len_span] = [ptr::null_mut(); #arg_len_span];
let mut raw_this = ptr::null_mut(); let mut raw_this = ptr::null_mut();
@ -145,7 +145,7 @@ pub fn contextless_function(_attr: TokenStream, input: TokenStream) -> TokenStre
use std::ptr; use std::ptr;
use std::panic::{self, AssertUnwindSafe}; use std::panic::{self, AssertUnwindSafe};
use std::ffi::CString; use std::ffi::CString;
use napi::{Env, NapiValue, Error, Status}; use napi::{Env, NapiValue, NapiRaw, Error, Status};
let ctx = unsafe { Env::from_raw(raw_env) }; let ctx = unsafe { Env::from_raw(raw_env) };
#execute_js_function #execute_js_function
@ -227,7 +227,7 @@ pub fn module_exports(_attr: TokenStream, input: TokenStream) -> TokenStream {
) -> napi::sys::napi_value { ) -> napi::sys::napi_value {
use std::ffi::CString; use std::ffi::CString;
use std::ptr; use std::ptr;
use napi::{Env, JsObject, NapiValue}; use napi::{Env, JsObject, NapiValue, NapiRaw};
#[cfg(all(feature = "tokio_rt", feature = "napi4"))] #[cfg(all(feature = "tokio_rt", feature = "napi4"))]
use napi::shutdown_tokio_rt; use napi::shutdown_tokio_rt;

View file

@ -2,7 +2,11 @@ use std::mem;
use std::os::raw::{c_char, c_void}; use std::os::raw::{c_char, c_void};
use std::ptr; use std::ptr;
use crate::{check_status, js_values::NapiValue, sys, Env, JsError, JsObject, Result, Task}; use crate::{
check_status,
js_values::{NapiRaw, NapiValue},
sys, Env, JsError, JsObject, Result, Task,
};
struct AsyncWork<T: Task> { struct AsyncWork<T: Task> {
inner_task: T, inner_task: T,

View file

@ -123,11 +123,19 @@ impl JsBigint {
} }
} }
impl NapiValue for JsBigint { impl NapiRaw for JsBigint {
unsafe fn raw(&self) -> sys::napi_value { unsafe fn raw(&self) -> sys::napi_value {
self.raw.value self.raw.value
} }
}
impl<'env> NapiRaw for &'env JsBigint {
unsafe fn raw(&self) -> sys::napi_value {
self.raw.value
}
}
impl NapiValue for JsBigint {
unsafe fn from_raw(env: sys::napi_env, value: sys::napi_value) -> Result<Self> { unsafe fn from_raw(env: sys::napi_env, value: sys::napi_value) -> Result<Self> {
let mut word_count = 0usize; let mut word_count = 0usize;
check_status!(sys::napi_get_value_bigint_words( check_status!(sys::napi_get_value_bigint_words(

View file

@ -1,4 +1,4 @@
use crate::{sys, JsUndefined, NapiValue, Result}; use crate::{sys, JsUndefined, NapiRaw, NapiValue, Result};
#[derive(Debug, Clone, Copy)] #[derive(Debug, Clone, Copy)]
pub enum Either<A: NapiValue, B: NapiValue> { pub enum Either<A: NapiValue, B: NapiValue> {
@ -25,7 +25,9 @@ impl<A: NapiValue, B: NapiValue> NapiValue for Either<A, B> {
unsafe fn from_raw_unchecked(env: sys::napi_env, value: sys::napi_value) -> Either<A, B> { unsafe fn from_raw_unchecked(env: sys::napi_env, value: sys::napi_value) -> Either<A, B> {
Self::from_raw(env, value).unwrap() Self::from_raw(env, value).unwrap()
} }
}
impl<A: NapiValue, B: NapiValue> NapiRaw for Either<A, B> {
unsafe fn raw(&self) -> sys::napi_value { unsafe fn raw(&self) -> sys::napi_value {
match self { match self {
Either::A(v) => v.raw(), Either::A(v) => v.raw(),

View file

@ -2,7 +2,7 @@ use std::ops::Deref;
use std::ptr; use std::ptr;
use crate::check_status; use crate::check_status;
use crate::{sys, Env, NapiValue, Result}; use crate::{sys, Env, NapiRaw, NapiValue, Result};
pub struct EscapableHandleScope<T: NapiValue> { pub struct EscapableHandleScope<T: NapiValue> {
handle_scope: sys::napi_escapable_handle_scope, handle_scope: sys::napi_escapable_handle_scope,
@ -16,7 +16,7 @@ impl<T: NapiValue> EscapableHandleScope<T> {
check_status!(unsafe { sys::napi_open_escapable_handle_scope(env, &mut handle_scope) })?; check_status!(unsafe { sys::napi_open_escapable_handle_scope(env, &mut handle_scope) })?;
let mut result = ptr::null_mut(); let mut result = ptr::null_mut();
check_status!(unsafe { check_status!(unsafe {
sys::napi_escape_handle(env, handle_scope, NapiValue::raw(&value), &mut result) sys::napi_escape_handle(env, handle_scope, NapiRaw::raw(&value), &mut result)
})?; })?;
Ok(Self { Ok(Self {
value, value,

View file

@ -2,7 +2,7 @@ use std::ptr;
use super::Value; use super::Value;
use crate::check_status; use crate::check_status;
use crate::{sys, Env, Error, JsObject, JsUnknown, NapiValue, Result, Status}; use crate::{sys, Env, Error, JsObject, JsUnknown, NapiRaw, NapiValue, Result, Status};
pub struct JsFunction(pub(crate) Value); pub struct JsFunction(pub(crate) Value);
@ -23,7 +23,10 @@ pub struct JsFunction(pub(crate) Value);
impl JsFunction { impl JsFunction {
/// [napi_call_function](https://nodejs.org/api/n-api.html#n_api_napi_call_function) /// [napi_call_function](https://nodejs.org/api/n-api.html#n_api_napi_call_function)
#[inline] #[inline]
pub fn call(&self, this: Option<&JsObject>, args: &[JsUnknown]) -> Result<JsUnknown> { pub fn call<V>(&self, this: Option<&JsObject>, args: &[V]) -> Result<JsUnknown>
where
V: NapiRaw,
{
let raw_this = this let raw_this = this
.map(|v| unsafe { v.raw() }) .map(|v| unsafe { v.raw() })
.or_else(|| { .or_else(|| {
@ -35,7 +38,7 @@ impl JsFunction {
.ok_or_else(|| Error::new(Status::GenericFailure, "Get raw this failed".to_owned()))?; .ok_or_else(|| Error::new(Status::GenericFailure, "Get raw this failed".to_owned()))?;
let raw_args = args let raw_args = args
.iter() .iter()
.map(|arg| arg.0.value) .map(|arg| unsafe { arg.raw() })
.collect::<Vec<sys::napi_value>>(); .collect::<Vec<sys::napi_value>>();
let mut return_value = ptr::null_mut(); let mut return_value = ptr::null_mut();
check_status!(unsafe { check_status!(unsafe {
@ -52,6 +55,34 @@ impl JsFunction {
unsafe { JsUnknown::from_raw(self.0.env, return_value) } unsafe { JsUnknown::from_raw(self.0.env, return_value) }
} }
/// [napi_call_function](https://nodejs.org/api/n-api.html#n_api_napi_call_function)
/// The same with `call`, but without arguments
#[inline]
pub fn call_without_args(&self, this: Option<&JsObject>) -> Result<JsUnknown> {
let raw_this = this
.map(|v| unsafe { v.raw() })
.or_else(|| {
unsafe { Env::from_raw(self.0.env) }
.get_undefined()
.ok()
.map(|u| unsafe { u.raw() })
})
.ok_or_else(|| Error::new(Status::GenericFailure, "Get raw this failed".to_owned()))?;
let mut return_value = ptr::null_mut();
check_status!(unsafe {
sys::napi_call_function(
self.0.env,
raw_this,
self.0.value,
0,
ptr::null_mut(),
&mut return_value,
)
})?;
unsafe { JsUnknown::from_raw(self.0.env, return_value) }
}
/// https://nodejs.org/api/n-api.html#n_api_napi_new_instance /// https://nodejs.org/api/n-api.html#n_api_napi_new_instance
/// ///
/// This method is used to instantiate a new `JavaScript` value using a given `JsFunction` that represents the constructor for the object. /// This method is used to instantiate a new `JavaScript` value using a given `JsFunction` that represents the constructor for the object.

View file

@ -101,10 +101,6 @@ macro_rules! impl_napi_value_trait {
} }
} }
unsafe fn raw(&self) -> sys::napi_value {
self.0.value
}
unsafe fn from_raw_unchecked(env: sys::napi_env, value: sys::napi_value) -> $js_value { unsafe fn from_raw_unchecked(env: sys::napi_env, value: sys::napi_value) -> $js_value {
$js_value(Value { $js_value(Value {
env, env,
@ -114,6 +110,18 @@ macro_rules! impl_napi_value_trait {
} }
} }
impl NapiRaw for $js_value {
unsafe fn raw(&self) -> sys::napi_value {
self.0.value
}
}
impl<'env> NapiRaw for &'env $js_value {
unsafe fn raw(&self) -> sys::napi_value {
self.0.value
}
}
impl TryFrom<JsUnknown> for $js_value { impl TryFrom<JsUnknown> for $js_value {
type Error = Error; type Error = Error;
fn try_from(value: JsUnknown) -> Result<$js_value> { fn try_from(value: JsUnknown) -> Result<$js_value> {
@ -221,7 +229,10 @@ macro_rules! impl_js_value_methods {
} }
#[inline] #[inline]
pub fn instanceof<Constructor: NapiValue>(&self, constructor: Constructor) -> Result<bool> { pub fn instanceof<Constructor>(&self, constructor: Constructor) -> Result<bool>
where
Constructor: NapiRaw,
{
let mut result = false; let mut result = false;
check_status!(unsafe { check_status!(unsafe {
sys::napi_instanceof(self.0.env, self.0.value, constructor.raw(), &mut result) sys::napi_instanceof(self.0.env, self.0.value, constructor.raw(), &mut result)
@ -238,7 +249,7 @@ macro_rules! impl_object_methods {
#[inline] #[inline]
pub fn set_property<V>(&mut self, key: JsString, value: V) -> Result<()> pub fn set_property<V>(&mut self, key: JsString, value: V) -> Result<()>
where where
V: NapiValue, V: NapiRaw,
{ {
check_status!(unsafe { check_status!(unsafe {
sys::napi_set_property(self.0.env, self.0.value, key.0.value, value.raw()) sys::napi_set_property(self.0.env, self.0.value, key.0.value, value.raw())
@ -246,9 +257,9 @@ macro_rules! impl_object_methods {
} }
#[inline] #[inline]
pub fn get_property<K, T>(&self, key: &K) -> Result<T> pub fn get_property<K, T>(&self, key: K) -> Result<T>
where where
K: NapiValue, K: NapiRaw,
T: NapiValue, T: NapiValue,
{ {
let mut raw_value = ptr::null_mut(); let mut raw_value = ptr::null_mut();
@ -259,9 +270,9 @@ macro_rules! impl_object_methods {
} }
#[inline] #[inline]
pub fn get_property_unchecked<K, T>(&self, key: &K) -> Result<T> pub fn get_property_unchecked<K, T>(&self, key: K) -> Result<T>
where where
K: NapiValue, K: NapiRaw,
T: NapiValue, T: NapiValue,
{ {
let mut raw_value = ptr::null_mut(); let mut raw_value = ptr::null_mut();
@ -274,7 +285,7 @@ macro_rules! impl_object_methods {
#[inline] #[inline]
pub fn set_named_property<T>(&mut self, name: &str, value: T) -> Result<()> pub fn set_named_property<T>(&mut self, name: &str, value: T) -> Result<()>
where where
T: NapiValue, T: NapiRaw,
{ {
let key = CString::new(name)?; let key = CString::new(name)?;
check_status!(unsafe { check_status!(unsafe {
@ -341,7 +352,7 @@ macro_rules! impl_object_methods {
#[inline] #[inline]
pub fn delete_property<S>(&mut self, name: S) -> Result<bool> pub fn delete_property<S>(&mut self, name: S) -> Result<bool>
where where
S: NapiValue, S: NapiRaw,
{ {
let mut result = false; let mut result = false;
check_status!(unsafe { check_status!(unsafe {
@ -381,7 +392,7 @@ macro_rules! impl_object_methods {
#[inline] #[inline]
pub fn has_own_property_js<K>(&self, key: K) -> Result<bool> pub fn has_own_property_js<K>(&self, key: K) -> Result<bool>
where where
K: NapiValue, K: NapiRaw,
{ {
let mut result = false; let mut result = false;
check_status!(unsafe { check_status!(unsafe {
@ -407,7 +418,7 @@ macro_rules! impl_object_methods {
#[inline] #[inline]
pub fn has_property_js<K>(&self, name: K) -> Result<bool> pub fn has_property_js<K>(&self, name: K) -> Result<bool>
where where
K: NapiValue, K: NapiRaw,
{ {
let mut result = false; let mut result = false;
check_status!(unsafe { check_status!(unsafe {
@ -473,7 +484,7 @@ macro_rules! impl_object_methods {
#[inline] #[inline]
pub fn set_element<T>(&mut self, index: u32, value: T) -> Result<()> pub fn set_element<T>(&mut self, index: u32, value: T) -> Result<()>
where where
T: NapiValue, T: NapiRaw,
{ {
check_status!(unsafe { check_status!(unsafe {
sys::napi_set_element(self.0.env, self.0.value, index, value.raw()) sys::napi_set_element(self.0.env, self.0.value, index, value.raw())
@ -565,15 +576,17 @@ macro_rules! impl_object_methods {
}; };
} }
pub trait NapiValue: Sized { pub trait NapiRaw {
#[allow(clippy::missing_safety_doc)]
unsafe fn raw(&self) -> sys::napi_value;
}
pub trait NapiValue: Sized + NapiRaw {
#[allow(clippy::missing_safety_doc)] #[allow(clippy::missing_safety_doc)]
unsafe fn from_raw(env: sys::napi_env, value: sys::napi_value) -> Result<Self>; unsafe fn from_raw(env: sys::napi_env, value: sys::napi_value) -> Result<Self>;
#[allow(clippy::missing_safety_doc)] #[allow(clippy::missing_safety_doc)]
unsafe fn from_raw_unchecked(env: sys::napi_env, value: sys::napi_value) -> Self; unsafe fn from_raw_unchecked(env: sys::napi_env, value: sys::napi_value) -> Self;
#[allow(clippy::missing_safety_doc)]
unsafe fn raw(&self) -> sys::napi_value;
} }
impl_js_value_methods!(JsUnknown); impl_js_value_methods!(JsUnknown);
@ -640,7 +653,17 @@ impl NapiValue for JsUnknown {
value_type: Unknown, value_type: Unknown,
}) })
} }
}
impl NapiRaw for JsUnknown {
/// get raw js value ptr
#[inline]
unsafe fn raw(&self) -> sys::napi_value {
self.0.value
}
}
impl<'env> NapiRaw for &'env JsUnknown {
/// get raw js value ptr /// get raw js value ptr
#[inline] #[inline]
unsafe fn raw(&self) -> sys::napi_value { unsafe fn raw(&self) -> sys::napi_value {

View file

@ -8,6 +8,12 @@ test('should call the function', (t) => {
}) })
}) })
test('should call function with ref args', (t) => {
bindings.testCallFunctionWithRefArguments((arg1: string, arg2: string) => {
t.is(`${arg1} ${arg2}`, 'hello world')
})
})
test('should set "this" properly', (t) => { test('should set "this" properly', (t) => {
const obj = {} const obj = {}
bindings.testCallFunctionWithThis.call(obj, function (this: typeof obj) { bindings.testCallFunctionWithThis.call(obj, function (this: typeof obj) {

View file

@ -11,18 +11,33 @@ pub fn call_function(ctx: CallContext) -> Result<JsNull> {
ctx.env.get_null() ctx.env.get_null()
} }
#[js_function(1)]
pub fn call_function_with_ref_arguments(ctx: CallContext) -> Result<JsNull> {
let js_func = ctx.get::<JsFunction>(0)?;
let js_string_hello = ctx.env.create_string("hello".as_ref())?;
let js_string_world = ctx.env.create_string("world".as_ref())?;
js_func.call(None, &[&js_string_hello, &js_string_world])?;
ctx.env.get_null()
}
#[js_function(1)] #[js_function(1)]
pub fn call_function_with_this(ctx: CallContext) -> Result<JsNull> { pub fn call_function_with_this(ctx: CallContext) -> Result<JsNull> {
let js_this: JsObject = ctx.this_unchecked(); let js_this: JsObject = ctx.this_unchecked();
let js_func = ctx.get::<JsFunction>(0)?; let js_func = ctx.get::<JsFunction>(0)?;
js_func.call(Some(&js_this), &[])?; js_func.call_without_args(Some(&js_this))?;
ctx.env.get_null() ctx.env.get_null()
} }
pub fn register_js(exports: &mut JsObject) -> Result<()> { pub fn register_js(exports: &mut JsObject) -> Result<()> {
exports.create_named_method("testCallFunction", call_function)?; exports.create_named_method("testCallFunction", call_function)?;
exports.create_named_method(
"testCallFunctionWithRefArguments",
call_function_with_ref_arguments,
)?;
exports.create_named_method("testCallFunctionWithThis", call_function_with_this)?; exports.create_named_method("testCallFunctionWithThis", call_function_with_this)?;
Ok(()) Ok(())
} }