napi_h/bindgen_runtime/js_values/
function.rs

1use std::ptr;
2
3use super::{FromNapiValue, ToNapiValue, TypeName, ValidateNapiValue};
4
5pub use crate::JsFunction;
6use crate::{check_pending_exception, check_status, sys, Env, NapiRaw, Result, ValueType};
7
8impl ValidateNapiValue for JsFunction {}
9
10pub trait JsValuesTupleIntoVec {
11  fn into_vec(self, env: sys::napi_env) -> Result<Vec<sys::napi_value>>;
12}
13
14/// A JavaScript function.
15/// It can only live in the scope of a function call.
16/// If you want to use it outside the scope of a function call, you can turn it into a reference.
17/// By calling the `create_ref` method.
18pub struct Function<'scope, Args: JsValuesTupleIntoVec, Return: FromNapiValue> {
19  pub(crate) env: sys::napi_env,
20  pub(crate) value: sys::napi_value,
21  pub(crate) _args: std::marker::PhantomData<Args>,
22  pub(crate) _return: std::marker::PhantomData<Return>,
23  _scope: std::marker::PhantomData<&'scope ()>,
24}
25
26impl<'scope, Args: JsValuesTupleIntoVec, Return: FromNapiValue> TypeName
27  for Function<'scope, Args, Return>
28{
29  fn type_name() -> &'static str {
30    "Function"
31  }
32
33  fn value_type() -> crate::ValueType {
34    ValueType::Function
35  }
36}
37
38impl<'scope, Args: JsValuesTupleIntoVec, Return: FromNapiValue> NapiRaw
39  for Function<'scope, Args, Return>
40{
41  unsafe fn raw(&self) -> sys::napi_value {
42    self.value
43  }
44}
45
46impl<'scope, Args: JsValuesTupleIntoVec, Return: FromNapiValue> FromNapiValue
47  for Function<'scope, Args, Return>
48{
49  unsafe fn from_napi_value(env: sys::napi_env, value: sys::napi_value) -> Result<Self> {
50    Ok(Function {
51      env,
52      value,
53      _args: std::marker::PhantomData,
54      _return: std::marker::PhantomData,
55      _scope: std::marker::PhantomData,
56    })
57  }
58}
59
60impl<'scope, Args: JsValuesTupleIntoVec, Return: FromNapiValue> ValidateNapiValue
61  for Function<'scope, Args, Return>
62{
63}
64
65impl<'scope, Args: JsValuesTupleIntoVec, Return: FromNapiValue> Function<'scope, Args, Return> {
66  /// Call the JavaScript function.
67  /// `this` in the JavaScript function will be `undefined`.
68  /// If you want to specify `this`, you can use the `apply` method.
69  pub fn call(&self, args: Args) -> Result<Return> {
70    let mut raw_this = ptr::null_mut();
71    check_status!(
72      unsafe { sys::napi_get_undefined(self.env, &mut raw_this) },
73      "Get undefined value failed"
74    )?;
75    let args_ptr = args.into_vec(self.env)?;
76    let mut raw_return = ptr::null_mut();
77    check_pending_exception!(
78      self.env,
79      unsafe {
80        sys::napi_call_function(
81          self.env,
82          raw_this,
83          self.value,
84          args_ptr.len(),
85          args_ptr.as_ptr(),
86          &mut raw_return,
87        )
88      },
89      "Call Function failed"
90    )?;
91    unsafe { Return::from_napi_value(self.env, raw_return) }
92  }
93
94  /// Call the JavaScript function.
95  /// `this` in the JavaScript function will be the provided `this`.
96  pub fn apply<Context: ToNapiValue>(&self, this: Context, args: Args) -> Result<Return> {
97    let raw_this = unsafe { Context::to_napi_value(self.env, this) }?;
98    let args_ptr = args.into_vec(self.env)?;
99    let mut raw_return = ptr::null_mut();
100    check_pending_exception!(
101      self.env,
102      unsafe {
103        sys::napi_call_function(
104          self.env,
105          raw_this,
106          self.value,
107          args_ptr.len(),
108          args_ptr.as_ptr(),
109          &mut raw_return,
110        )
111      },
112      "Call Function failed"
113    )?;
114    unsafe { Return::from_napi_value(self.env, raw_return) }
115  }
116
117  /// Create a reference to the JavaScript function.
118  pub fn create_ref(&self) -> Result<FunctionRef<Args, Return>> {
119    let mut reference = ptr::null_mut();
120    check_status!(
121      unsafe { sys::napi_create_reference(self.env, self.value, 1, &mut reference) },
122      "Create reference failed"
123    )?;
124    Ok(FunctionRef {
125      inner: reference,
126      env: self.env,
127      _args: std::marker::PhantomData,
128      _return: std::marker::PhantomData,
129    })
130  }
131}
132
133/// A reference to a JavaScript function.
134/// It can be used to outlive the scope of the function.
135pub struct FunctionRef<Args: JsValuesTupleIntoVec, Return: FromNapiValue> {
136  pub(crate) inner: sys::napi_ref,
137  pub(crate) env: sys::napi_env,
138  _args: std::marker::PhantomData<Args>,
139  _return: std::marker::PhantomData<Return>,
140}
141
142unsafe impl<Args: JsValuesTupleIntoVec, Return: FromNapiValue> Sync for FunctionRef<Args, Return> {}
143
144impl<Args: JsValuesTupleIntoVec, Return: FromNapiValue> FunctionRef<Args, Return> {
145  pub fn borrow_back<'scope>(&self, env: &'scope Env) -> Result<Function<'scope, Args, Return>> {
146    let mut value = ptr::null_mut();
147    check_status!(
148      unsafe { sys::napi_get_reference_value(env.0, self.inner, &mut value) },
149      "Get reference value failed"
150    )?;
151    Ok(Function {
152      env: env.0,
153      value,
154      _args: std::marker::PhantomData,
155      _return: std::marker::PhantomData,
156      _scope: std::marker::PhantomData,
157    })
158  }
159}
160
161impl<Args: JsValuesTupleIntoVec, Return: FromNapiValue> Drop for FunctionRef<Args, Return> {
162  fn drop(&mut self) {
163    let status = unsafe { sys::napi_delete_reference(self.env, self.inner) };
164    debug_assert_eq!(status, sys::Status::napi_ok, "Drop FunctionRef failed");
165  }
166}
167
168impl<Args: JsValuesTupleIntoVec, Return: FromNapiValue> TypeName for FunctionRef<Args, Return> {
169  fn type_name() -> &'static str {
170    "Function"
171  }
172
173  fn value_type() -> crate::ValueType {
174    ValueType::Function
175  }
176}
177
178impl<Args: JsValuesTupleIntoVec, Return: FromNapiValue> FromNapiValue
179  for FunctionRef<Args, Return>
180{
181  unsafe fn from_napi_value(env: sys::napi_env, value: sys::napi_value) -> Result<Self> {
182    let mut reference = ptr::null_mut();
183    check_status!(
184      unsafe { sys::napi_create_reference(env, value, 1, &mut reference) },
185      "Create reference failed"
186    )?;
187    Ok(FunctionRef {
188      inner: reference,
189      env,
190      _args: std::marker::PhantomData,
191      _return: std::marker::PhantomData,
192    })
193  }
194}
195
196impl<Args: JsValuesTupleIntoVec, Return: FromNapiValue> ValidateNapiValue
197  for FunctionRef<Args, Return>
198{
199}
200
201macro_rules! impl_call_apply {
202  ($fn_call_name:ident, $fn_apply_name:ident, $($ident:ident),*) => {
203    #[allow(non_snake_case, clippy::too_many_arguments)]
204    pub fn $fn_call_name<$($ident: ToNapiValue),*, Return: FromNapiValue>(
205      &self,
206      $($ident: $ident),*
207    ) -> Result<Return> {
208      let raw_this = unsafe { Env::from_raw(self.0.env) }
209        .get_undefined()
210        .map(|u| unsafe { u.raw() })?;
211
212      let raw_args = vec![
213        $(
214          unsafe { $ident::to_napi_value(self.0.env, $ident) }?
215        ),*
216      ];
217
218      let mut return_value = ptr::null_mut();
219      check_pending_exception!(self.0.env, unsafe {
220        sys::napi_call_function(
221          self.0.env,
222          raw_this,
223          self.0.value,
224          raw_args.len(),
225          raw_args.as_ptr(),
226          &mut return_value,
227        )
228      })?;
229
230      unsafe { Return::from_napi_value(self.0.env, return_value) }
231    }
232
233    #[allow(non_snake_case, clippy::too_many_arguments)]
234    pub fn $fn_apply_name<$($ident: ToNapiValue),*, Context: ToNapiValue, Return: FromNapiValue>(
235      &self,
236      this: Context,
237      $($ident: $ident),*
238    ) -> Result<Return> {
239      let raw_this = unsafe { Context::to_napi_value(self.0.env, this) }?;
240
241      let raw_args = vec![
242        $(
243          unsafe { $ident::to_napi_value(self.0.env, $ident) }?
244        ),*
245      ];
246
247      let mut return_value = ptr::null_mut();
248      check_pending_exception!(self.0.env, unsafe {
249        sys::napi_call_function(
250          self.0.env,
251          raw_this,
252          self.0.value,
253          raw_args.len(),
254          raw_args.as_ptr(),
255          &mut return_value,
256        )
257      })?;
258
259      unsafe { Return::from_napi_value(self.0.env, return_value) }
260    }
261  };
262}
263
264impl JsFunction {
265  pub fn apply0<Return: FromNapiValue, Context: ToNapiValue>(
266    &self,
267    this: Context,
268  ) -> Result<Return> {
269    let raw_this = unsafe { Context::to_napi_value(self.0.env, this) }?;
270
271    let mut return_value = ptr::null_mut();
272    check_pending_exception!(self.0.env, unsafe {
273      sys::napi_call_function(
274        self.0.env,
275        raw_this,
276        self.0.value,
277        0,
278        ptr::null_mut(),
279        &mut return_value,
280      )
281    })?;
282
283    unsafe { Return::from_napi_value(self.0.env, return_value) }
284  }
285
286  pub fn call0<Return: FromNapiValue>(&self) -> Result<Return> {
287    let raw_this = unsafe { Env::from_raw(self.0.env) }
288      .get_undefined()
289      .map(|u| unsafe { u.raw() })?;
290
291    let mut return_value = ptr::null_mut();
292    check_pending_exception!(self.0.env, unsafe {
293      sys::napi_call_function(
294        self.0.env,
295        raw_this,
296        self.0.value,
297        0,
298        ptr::null_mut(),
299        &mut return_value,
300      )
301    })?;
302
303    unsafe { Return::from_napi_value(self.0.env, return_value) }
304  }
305
306  impl_call_apply!(call1, apply1, Arg1);
307  impl_call_apply!(call2, apply2, Arg1, Arg2);
308  impl_call_apply!(call3, apply3, Arg1, Arg2, Arg3);
309  impl_call_apply!(call4, apply4, Arg1, Arg2, Arg3, Arg4);
310  impl_call_apply!(call5, apply5, Arg1, Arg2, Arg3, Arg4, Arg5);
311  impl_call_apply!(call6, apply6, Arg1, Arg2, Arg3, Arg4, Arg5, Arg6);
312  impl_call_apply!(call7, apply7, Arg1, Arg2, Arg3, Arg4, Arg5, Arg6, Arg7);
313  impl_call_apply!(call8, apply8, Arg1, Arg2, Arg3, Arg4, Arg5, Arg6, Arg7, Arg8);
314  impl_call_apply!(call9, apply9, Arg1, Arg2, Arg3, Arg4, Arg5, Arg6, Arg7, Arg8, Arg9);
315  impl_call_apply!(call10, apply10, Arg1, Arg2, Arg3, Arg4, Arg5, Arg6, Arg7, Arg8, Arg9, Arg10);
316}