1use std::ptr;
2
3use super::Value;
4#[cfg(feature = "napi4")]
5use crate::{
6 bindgen_runtime::ToNapiValue,
7 threadsafe_function::{ThreadSafeCallContext, ThreadsafeFunction},
8};
9use crate::{bindgen_runtime::TypeName, JsString};
10use crate::{check_pending_exception, ValueType};
11use crate::{sys, Env, Error, JsObject, JsUnknown, NapiRaw, NapiValue, Result, Status};
12
13pub struct JsFunction(pub(crate) Value);
14
15impl TypeName for JsFunction {
16 fn type_name() -> &'static str {
17 "Function"
18 }
19
20 fn value_type() -> crate::ValueType {
21 ValueType::Function
22 }
23}
24
25/// See [Working with JavaScript Functions](https://nodejs.org/api/n-api.html#n_api_working_with_javascript_functions).
26///
27/// Example:
28/// ```
29/// use napi::{JsFunction, CallContext, JsNull, Result};
30///
31/// #[js_function(1)]
32/// pub fn call_function(ctx: CallContext) -> Result<JsNull> {
33/// let js_func = ctx.get::<JsFunction>(0)?;
34/// let js_string = ctx.env.create_string("hello".as_ref())?.into_unknown()?;
35/// js_func.call(None, &[js_string])?;
36/// Ok(ctx.env.get_null()?)
37/// }
38/// ```
39impl JsFunction {
40 /// [napi_call_function](https://nodejs.org/api/n-api.html#n_api_napi_call_function)
41 pub fn call<V>(&self, this: Option<&JsObject>, args: &[V]) -> Result<JsUnknown>
42 where
43 V: NapiRaw,
44 {
45 let raw_this = this
46 .map(|v| unsafe { v.raw() })
47 .or_else(|| {
48 unsafe { Env::from_raw(self.0.env) }
49 .get_undefined()
50 .ok()
51 .map(|u| unsafe { u.raw() })
52 })
53 .ok_or_else(|| Error::new(Status::GenericFailure, "Get raw this failed".to_owned()))?;
54 let raw_args = args
55 .iter()
56 .map(|arg| unsafe { arg.raw() })
57 .collect::<Vec<sys::napi_value>>();
58 let mut return_value = ptr::null_mut();
59 check_pending_exception!(self.0.env, unsafe {
60 sys::napi_call_function(
61 self.0.env,
62 raw_this,
63 self.0.value,
64 args.len(),
65 raw_args.as_ptr(),
66 &mut return_value,
67 )
68 })?;
69
70 unsafe { JsUnknown::from_raw(self.0.env, return_value) }
71 }
72
73 /// [napi_call_function](https://nodejs.org/api/n-api.html#n_api_napi_call_function)
74 /// The same with `call`, but without arguments
75 pub fn call_without_args(&self, this: Option<&JsObject>) -> Result<JsUnknown> {
76 let raw_this = this
77 .map(|v| unsafe { v.raw() })
78 .or_else(|| {
79 unsafe { Env::from_raw(self.0.env) }
80 .get_undefined()
81 .ok()
82 .map(|u| unsafe { u.raw() })
83 })
84 .ok_or_else(|| Error::new(Status::GenericFailure, "Get raw this failed".to_owned()))?;
85 let mut return_value = ptr::null_mut();
86 check_pending_exception!(self.0.env, unsafe {
87 sys::napi_call_function(
88 self.0.env,
89 raw_this,
90 self.0.value,
91 0,
92 ptr::null_mut(),
93 &mut return_value,
94 )
95 })?;
96
97 unsafe { JsUnknown::from_raw(self.0.env, return_value) }
98 }
99
100 /// <https://nodejs.org/api/n-api.html#n_api_napi_new_instance>
101 ///
102 /// This method is used to instantiate a new `JavaScript` value using a given `JsFunction` that represents the constructor for the object.
103 pub fn new_instance<V>(&self, args: &[V]) -> Result<JsObject>
104 where
105 V: NapiRaw,
106 {
107 let mut js_instance = ptr::null_mut();
108 let length = args.len();
109 let raw_args = args
110 .iter()
111 .map(|arg| unsafe { arg.raw() })
112 .collect::<Vec<sys::napi_value>>();
113 check_pending_exception!(self.0.env, unsafe {
114 sys::napi_new_instance(
115 self.0.env,
116 self.0.value,
117 length,
118 raw_args.as_ptr(),
119 &mut js_instance,
120 )
121 })?;
122 Ok(unsafe { JsObject::from_raw_unchecked(self.0.env, js_instance) })
123 }
124
125 /// function name
126 pub fn name(&self) -> Result<String> {
127 let mut name = ptr::null_mut();
128 check_pending_exception!(self.0.env, unsafe {
129 sys::napi_get_named_property(
130 self.0.env,
131 self.0.value,
132 "name\0".as_ptr().cast(),
133 &mut name,
134 )
135 })?;
136 let name_value = unsafe { JsString::from_raw_unchecked(self.0.env, name) };
137 Ok(name_value.into_utf8()?.as_str()?.to_owned())
138 }
139
140 #[cfg(feature = "napi4")]
141 pub fn create_threadsafe_function<T, V, F, ES>(
142 &self,
143 max_queue_size: usize,
144 callback: F,
145 ) -> Result<ThreadsafeFunction<T, ES>>
146 where
147 T: 'static,
148 V: ToNapiValue,
149 F: 'static + Send + FnMut(ThreadSafeCallContext<T>) -> Result<Vec<V>>,
150 ES: crate::threadsafe_function::ErrorStrategy::T,
151 {
152 ThreadsafeFunction::create(self.0.env, self.0.value, max_queue_size, callback)
153 }
154}
155