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: *mut napi_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: i32 = 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: *mut napi_ref__ = 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}
317