1 | use std::ptr; |
2 | |
3 | use super::Value; |
4 | #[cfg (feature = "napi4" )] |
5 | use crate::{ |
6 | bindgen_runtime::ToNapiValue, |
7 | threadsafe_function::{ThreadSafeCallContext, ThreadsafeFunction}, |
8 | }; |
9 | use crate::{bindgen_runtime::TypeName, JsString}; |
10 | use crate::{check_pending_exception, ValueType}; |
11 | use crate::{sys, Env, Error, JsObject, JsUnknown, NapiRaw, NapiValue, Result, Status}; |
12 | |
13 | pub struct JsFunction(pub(crate) Value); |
14 | |
15 | impl 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 | /// ``` |
39 | impl 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 | |