| 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 | |