1 | use std::ptr; |
2 | |
3 | use super::{FromNapiValue, ToNapiValue, TypeName, ValidateNapiValue}; |
4 | |
5 | pub use crate::JsFunction; |
6 | use crate::{check_pending_exception, check_status, sys, Env, NapiRaw, Result, ValueType}; |
7 | |
8 | impl ValidateNapiValue for JsFunction {} |
9 | |
10 | pub 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. |
18 | pub 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 | |
26 | impl<'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 | |
38 | impl<'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 | |
46 | impl<'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 | |
60 | impl<'scope, Args: JsValuesTupleIntoVec, Return: FromNapiValue> ValidateNapiValue |
61 | for Function<'scope, Args, Return> |
62 | { |
63 | } |
64 | |
65 | impl<'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. |
135 | pub 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 | |
142 | unsafe impl<Args: JsValuesTupleIntoVec, Return: FromNapiValue> Sync for FunctionRef<Args, Return> {} |
143 | |
144 | impl<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 | |
161 | impl<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 | |
168 | impl<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 | |
178 | impl<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 | |
196 | impl<Args: JsValuesTupleIntoVec, Return: FromNapiValue> ValidateNapiValue |
197 | for FunctionRef<Args, Return> |
198 | { |
199 | } |
200 | |
201 | macro_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 | |
264 | impl 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 | |