napi/bindgen_runtime/js_values/
scope.rs

1use std::ptr;
2
3use crate::{bindgen_runtime::FromNapiValue, check_status, sys, Env, JsValue, Result};
4
5pub struct HandleScope {
6  pub(crate) scope: sys::napi_handle_scope,
7}
8
9impl HandleScope {
10  pub fn create(env: &Env) -> Result<Self> {
11    let mut scope = ptr::null_mut();
12    check_status!(
13      unsafe { sys::napi_open_handle_scope(env.0, &mut scope) },
14      "Failed to open handle scope"
15    )?;
16    Ok(Self { scope })
17  }
18
19  /// # Safety
20  ///
21  /// This function is unsafe because it will invalidate the JsValue created within the HandleScope.
22  ///
23  /// For example:
24  ///
25  /// ```no_run
26  /// #[napi]
27  /// pub fn shorter_scope(env: &Env, arr: Array) -> Result<Vec<u32>> {
28  ///   let len = arr.len();
29  ///   let mut result = Vec::with_capacity(len as usize);
30  ///   for i in 0..len {
31  ///     let scope = HandleScope::create(env)?;
32  ///     let value: Unknown = arr.get_element(i)?;
33  ///         ^^^ this will be invalidated after the scope is closed
34  ///     let len = unsafe { scope.close(value, |v| match v.get_type()? {
35  ///       ValueType::String => Ok(v.utf8_len()? as u32),
36  ///       _ => Ok(0),
37  ///     })? };
38  ///   }
39  /// }
40  /// ```
41  pub unsafe fn close<A, T>(self, arg: A, f: impl FnOnce(A) -> Result<T>) -> Result<T>
42  where
43    A: JsValuesTuple,
44  {
45    let env = arg.env();
46    let ret = f(arg);
47    check_status!(
48      unsafe { sys::napi_close_handle_scope(env, self.scope) },
49      "Failed to close handle scope"
50    )?;
51    ret
52  }
53}
54
55pub struct EscapableHandleScope<'env> {
56  pub(crate) scope: sys::napi_escapable_handle_scope,
57  pub(crate) env: sys::napi_env,
58  pub(crate) phantom: std::marker::PhantomData<&'env ()>,
59}
60
61impl<'env, 'scope: 'env> EscapableHandleScope<'scope> {
62  pub fn with<
63    T,
64    Args: JsValuesTuple,
65    F: 'env + FnOnce(EscapableHandleScope<'env>, Args) -> Result<T>,
66  >(
67    env: &'env Env,
68    args: Args,
69    scope_fn: F,
70  ) -> Result<T> {
71    let mut scope = ptr::null_mut();
72    check_status!(
73      unsafe { sys::napi_open_escapable_handle_scope(env.0, &mut scope) },
74      "Failed to open handle scope"
75    )?;
76    let scope: EscapableHandleScope<'env> = Self {
77      scope,
78      env: env.0,
79      phantom: std::marker::PhantomData,
80    };
81    scope_fn(scope, args)
82  }
83
84  pub fn escape<V: JsValue<'env> + FromNapiValue>(&self, value: V) -> Result<V> {
85    let mut result = ptr::null_mut();
86    check_status!(
87      unsafe { sys::napi_escape_handle(self.env, self.scope, value.raw(), &mut result) },
88      "Failed to escape handle"
89    )?;
90    unsafe { V::from_napi_value(self.env, result) }
91  }
92}
93
94impl Drop for EscapableHandleScope<'_> {
95  fn drop(&mut self) {
96    let status = unsafe { sys::napi_close_escapable_handle_scope(self.env, self.scope) };
97    if status != sys::Status::napi_ok {
98      panic!(
99        "Failed to close handle scope: {}",
100        crate::Status::from(status)
101      );
102    }
103  }
104}
105
106pub trait JsValuesTuple {
107  fn env(&self) -> sys::napi_env;
108}
109
110impl<'env, T: JsValue<'env>> JsValuesTuple for T {
111  fn env(&self) -> sys::napi_env {
112    self.value().env
113  }
114}
115
116impl<'env, T1: JsValue<'env>, T2: JsValue<'env>> JsValuesTuple for (T1, T2) {
117  fn env(&self) -> sys::napi_env {
118    self.0.value().env
119  }
120}
121
122impl<'env, T1: JsValue<'env>, T2: JsValue<'env>, T3: JsValue<'env>> JsValuesTuple for (T1, T2, T3) {
123  fn env(&self) -> sys::napi_env {
124    self.0.value().env
125  }
126}
127
128impl<'env, T1: JsValue<'env>, T2: JsValue<'env>, T3: JsValue<'env>, T4: JsValue<'env>> JsValuesTuple
129  for (T1, T2, T3, T4)
130{
131  fn env(&self) -> sys::napi_env {
132    self.0.value().env
133  }
134}
135
136impl<
137    'env,
138    T1: JsValue<'env>,
139    T2: JsValue<'env>,
140    T3: JsValue<'env>,
141    T4: JsValue<'env>,
142    T5: JsValue<'env>,
143  > JsValuesTuple for (T1, T2, T3, T4, T5)
144{
145  fn env(&self) -> sys::napi_env {
146    self.0.value().env
147  }
148}
149
150impl<
151    'env,
152    T1: JsValue<'env>,
153    T2: JsValue<'env>,
154    T3: JsValue<'env>,
155    T4: JsValue<'env>,
156    T5: JsValue<'env>,
157    T6: JsValue<'env>,
158  > JsValuesTuple for (T1, T2, T3, T4, T5, T6)
159{
160  fn env(&self) -> sys::napi_env {
161    self.0.value().env
162  }
163}
164
165impl<
166    'env,
167    T1: JsValue<'env>,
168    T2: JsValue<'env>,
169    T3: JsValue<'env>,
170    T4: JsValue<'env>,
171    T5: JsValue<'env>,
172    T6: JsValue<'env>,
173    T7: JsValue<'env>,
174  > JsValuesTuple for (T1, T2, T3, T4, T5, T6, T7)
175{
176  fn env(&self) -> sys::napi_env {
177    self.0.value().env
178  }
179}
180
181impl<
182    'env,
183    T1: JsValue<'env>,
184    T2: JsValue<'env>,
185    T3: JsValue<'env>,
186    T4: JsValue<'env>,
187    T5: JsValue<'env>,
188    T6: JsValue<'env>,
189    T7: JsValue<'env>,
190    T8: JsValue<'env>,
191  > JsValuesTuple for (T1, T2, T3, T4, T5, T6, T7, T8)
192{
193  fn env(&self) -> sys::napi_env {
194    self.0.value().env
195  }
196}
197
198impl<
199    'env,
200    T1: JsValue<'env>,
201    T2: JsValue<'env>,
202    T3: JsValue<'env>,
203    T4: JsValue<'env>,
204    T5: JsValue<'env>,
205    T6: JsValue<'env>,
206    T7: JsValue<'env>,
207    T8: JsValue<'env>,
208    T9: JsValue<'env>,
209  > JsValuesTuple for (T1, T2, T3, T4, T5, T6, T7, T8, T9)
210{
211  fn env(&self) -> sys::napi_env {
212    self.0.value().env
213  }
214}
215
216impl<
217    'env,
218    T1: JsValue<'env>,
219    T2: JsValue<'env>,
220    T3: JsValue<'env>,
221    T4: JsValue<'env>,
222    T5: JsValue<'env>,
223    T6: JsValue<'env>,
224    T7: JsValue<'env>,
225    T8: JsValue<'env>,
226    T9: JsValue<'env>,
227    T10: JsValue<'env>,
228  > JsValuesTuple for (T1, T2, T3, T4, T5, T6, T7, T8, T9, T10)
229{
230  fn env(&self) -> sys::napi_env {
231    self.0.value().env
232  }
233}
234
235impl<
236    'env,
237    T1: JsValue<'env>,
238    T2: JsValue<'env>,
239    T3: JsValue<'env>,
240    T4: JsValue<'env>,
241    T5: JsValue<'env>,
242    T6: JsValue<'env>,
243    T7: JsValue<'env>,
244    T8: JsValue<'env>,
245    T9: JsValue<'env>,
246    T10: JsValue<'env>,
247    T11: JsValue<'env>,
248  > JsValuesTuple for (T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11)
249{
250  fn env(&self) -> sys::napi_env {
251    self.0.value().env
252  }
253}
254
255impl<
256    'env,
257    T1: JsValue<'env>,
258    T2: JsValue<'env>,
259    T3: JsValue<'env>,
260    T4: JsValue<'env>,
261    T5: JsValue<'env>,
262    T6: JsValue<'env>,
263    T7: JsValue<'env>,
264    T8: JsValue<'env>,
265    T9: JsValue<'env>,
266    T10: JsValue<'env>,
267    T11: JsValue<'env>,
268    T12: JsValue<'env>,
269  > JsValuesTuple for (T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12)
270{
271  fn env(&self) -> sys::napi_env {
272    self.0.value().env
273  }
274}