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::panic::{self, AssertUnwindSafe};
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 raw_args: [napi::sys::napi_value; #arg_len_span] = [ptr::null_mut(); #arg_len_span];
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::panic::{self, AssertUnwindSafe};
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) };
#execute_js_function
@ -227,7 +227,7 @@ pub fn module_exports(_attr: TokenStream, input: TokenStream) -> TokenStream {
) -> napi::sys::napi_value {
use std::ffi::CString;
use std::ptr;
use napi::{Env, JsObject, NapiValue};
use napi::{Env, JsObject, NapiValue, NapiRaw};
#[cfg(all(feature = "tokio_rt", feature = "napi4"))]
use napi::shutdown_tokio_rt;

View file

@ -2,7 +2,11 @@ use std::mem;
use std::os::raw::{c_char, c_void};
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> {
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 {
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> {
let mut word_count = 0usize;
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)]
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> {
Self::from_raw(env, value).unwrap()
}
}
impl<A: NapiValue, B: NapiValue> NapiRaw for Either<A, B> {
unsafe fn raw(&self) -> sys::napi_value {
match self {
Either::A(v) => v.raw(),

View file

@ -2,7 +2,7 @@ use std::ops::Deref;
use std::ptr;
use crate::check_status;
use crate::{sys, Env, NapiValue, Result};
use crate::{sys, Env, NapiRaw, NapiValue, Result};
pub struct EscapableHandleScope<T: NapiValue> {
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) })?;
let mut result = ptr::null_mut();
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 {
value,

View file

@ -2,7 +2,7 @@ use std::ptr;
use super::Value;
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);
@ -23,7 +23,10 @@ pub struct JsFunction(pub(crate) Value);
impl JsFunction {
/// [napi_call_function](https://nodejs.org/api/n-api.html#n_api_napi_call_function)
#[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
.map(|v| unsafe { v.raw() })
.or_else(|| {
@ -35,7 +38,7 @@ impl JsFunction {
.ok_or_else(|| Error::new(Status::GenericFailure, "Get raw this failed".to_owned()))?;
let raw_args = args
.iter()
.map(|arg| arg.0.value)
.map(|arg| unsafe { arg.raw() })
.collect::<Vec<sys::napi_value>>();
let mut return_value = ptr::null_mut();
check_status!(unsafe {
@ -52,6 +55,34 @@ impl JsFunction {
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
///
/// 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 {
$js_value(Value {
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 {
type Error = Error;
fn try_from(value: JsUnknown) -> Result<$js_value> {
@ -221,7 +229,10 @@ macro_rules! impl_js_value_methods {
}
#[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;
check_status!(unsafe {
sys::napi_instanceof(self.0.env, self.0.value, constructor.raw(), &mut result)
@ -238,7 +249,7 @@ macro_rules! impl_object_methods {
#[inline]
pub fn set_property<V>(&mut self, key: JsString, value: V) -> Result<()>
where
V: NapiValue,
V: NapiRaw,
{
check_status!(unsafe {
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]
pub fn get_property<K, T>(&self, key: &K) -> Result<T>
pub fn get_property<K, T>(&self, key: K) -> Result<T>
where
K: NapiValue,
K: NapiRaw,
T: NapiValue,
{
let mut raw_value = ptr::null_mut();
@ -259,9 +270,9 @@ macro_rules! impl_object_methods {
}
#[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
K: NapiValue,
K: NapiRaw,
T: NapiValue,
{
let mut raw_value = ptr::null_mut();
@ -274,7 +285,7 @@ macro_rules! impl_object_methods {
#[inline]
pub fn set_named_property<T>(&mut self, name: &str, value: T) -> Result<()>
where
T: NapiValue,
T: NapiRaw,
{
let key = CString::new(name)?;
check_status!(unsafe {
@ -341,7 +352,7 @@ macro_rules! impl_object_methods {
#[inline]
pub fn delete_property<S>(&mut self, name: S) -> Result<bool>
where
S: NapiValue,
S: NapiRaw,
{
let mut result = false;
check_status!(unsafe {
@ -381,7 +392,7 @@ macro_rules! impl_object_methods {
#[inline]
pub fn has_own_property_js<K>(&self, key: K) -> Result<bool>
where
K: NapiValue,
K: NapiRaw,
{
let mut result = false;
check_status!(unsafe {
@ -407,7 +418,7 @@ macro_rules! impl_object_methods {
#[inline]
pub fn has_property_js<K>(&self, name: K) -> Result<bool>
where
K: NapiValue,
K: NapiRaw,
{
let mut result = false;
check_status!(unsafe {
@ -473,7 +484,7 @@ macro_rules! impl_object_methods {
#[inline]
pub fn set_element<T>(&mut self, index: u32, value: T) -> Result<()>
where
T: NapiValue,
T: NapiRaw,
{
check_status!(unsafe {
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)]
unsafe fn from_raw(env: sys::napi_env, value: sys::napi_value) -> Result<Self>;
#[allow(clippy::missing_safety_doc)]
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);
@ -640,7 +653,17 @@ impl NapiValue for JsUnknown {
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
#[inline]
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) => {
const 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()
}
#[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)]
pub fn call_function_with_this(ctx: CallContext) -> Result<JsNull> {
let js_this: JsObject = ctx.this_unchecked();
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()
}
pub fn register_js(exports: &mut JsObject) -> Result<()> {
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)?;
Ok(())
}