napi_h/
env.rs

1use std::any::{type_name, TypeId};
2use std::convert::TryInto;
3use std::ffi::CString;
4#[cfg(all(feature = "tokio_rt", feature = "napi4"))]
5use std::future::Future;
6use std::mem;
7use std::os::raw::{c_char, c_void};
8use std::ptr;
9
10use crate::bindgen_runtime::FromNapiValue;
11#[cfg(feature = "napi4")]
12use crate::bindgen_runtime::ToNapiValue;
13use crate::{
14  async_work::{self, AsyncWorkPromise},
15  check_status,
16  js_values::*,
17  sys,
18  task::Task,
19  Error, ExtendedErrorInfo, NodeVersion, Result, Status, ValueType,
20};
21
22#[cfg(feature = "napi8")]
23use crate::async_cleanup_hook::AsyncCleanupHook;
24#[cfg(feature = "napi3")]
25use crate::cleanup_env::{CleanupEnvHook, CleanupEnvHookData};
26#[cfg(feature = "serde-json")]
27use crate::js_values::{De, Ser};
28#[cfg(feature = "napi4")]
29use crate::threadsafe_function::{ThreadSafeCallContext, ThreadsafeFunction};
30#[cfg(feature = "napi3")]
31use crate::JsError;
32#[cfg(feature = "serde-json")]
33use serde::de::DeserializeOwned;
34#[cfg(feature = "serde-json")]
35use serde::Serialize;
36
37pub type Callback = unsafe extern "C" fn(sys::napi_env, sys::napi_callback_info) -> sys::napi_value;
38
39pub(crate) static EMPTY_VEC: Vec<u8> = vec![];
40
41#[derive(Clone, Copy)]
42/// `Env` is used to represent a context that the underlying N-API implementation can use to persist VM-specific state.
43///
44/// Specifically, the same `Env` that was passed in when the initial native function was called must be passed to any subsequent nested N-API calls.
45///
46/// Caching the `Env` for the purpose of general reuse, and passing the `Env` between instances of the same addon running on different Worker threads is not allowed.
47///
48/// The `Env` becomes invalid when an instance of a native addon is unloaded.
49///
50/// Notification of this event is delivered through the callbacks given to `Env::add_env_cleanup_hook` and `Env::set_instance_data`.
51pub struct Env(pub(crate) sys::napi_env);
52
53impl From<sys::napi_env> for Env {
54  fn from(env: sys::napi_env) -> Self {
55    Env(env)
56  }
57}
58
59impl Env {
60  #[allow(clippy::missing_safety_doc)]
61  pub unsafe fn from_raw(env: sys::napi_env) -> Self {
62    Env(env)
63  }
64
65  pub fn get_boolean(&self, value: bool) -> Result<JsBoolean> {
66    let mut raw_value = ptr::null_mut();
67    check_status!(unsafe { sys::napi_get_boolean(self.0, value, &mut raw_value) })?;
68    Ok(unsafe { JsBoolean::from_raw_unchecked(self.0, raw_value) })
69  }
70
71  pub fn create_int32(&self, int: i32) -> Result<JsNumber> {
72    let mut raw_value = ptr::null_mut();
73    check_status!(unsafe {
74      sys::napi_create_int32(self.0, int, (&mut raw_value) as *mut sys::napi_value)
75    })?;
76    Ok(unsafe { JsNumber::from_raw_unchecked(self.0, raw_value) })
77  }
78
79  pub fn create_int64(&self, int: i64) -> Result<JsNumber> {
80    let mut raw_value = ptr::null_mut();
81    check_status!(unsafe {
82      sys::napi_create_int64(self.0, int, (&mut raw_value) as *mut sys::napi_value)
83    })?;
84    Ok(unsafe { JsNumber::from_raw_unchecked(self.0, raw_value) })
85  }
86
87  pub fn create_uint32(&self, number: u32) -> Result<JsNumber> {
88    let mut raw_value = ptr::null_mut();
89    check_status!(unsafe { sys::napi_create_uint32(self.0, number, &mut raw_value) })?;
90    Ok(unsafe { JsNumber::from_raw_unchecked(self.0, raw_value) })
91  }
92
93  pub fn create_double(&self, double: f64) -> Result<JsNumber> {
94    let mut raw_value = ptr::null_mut();
95    check_status!(unsafe {
96      sys::napi_create_double(self.0, double, (&mut raw_value) as *mut sys::napi_value)
97    })?;
98    Ok(unsafe { JsNumber::from_raw_unchecked(self.0, raw_value) })
99  }
100
101  /// [n_api_napi_create_bigint_int64](https://nodejs.org/api/n-api.html#n_api_napi_create_bigint_int64)
102  #[cfg(feature = "napi6")]
103  pub fn create_bigint_from_i64(&self, value: i64) -> Result<JsBigInt> {
104    let mut raw_value = ptr::null_mut();
105    check_status!(unsafe { sys::napi_create_bigint_int64(self.0, value, &mut raw_value) })?;
106    Ok(JsBigInt::from_raw_unchecked(self.0, raw_value, 1))
107  }
108
109  #[cfg(feature = "napi6")]
110  pub fn create_bigint_from_u64(&self, value: u64) -> Result<JsBigInt> {
111    let mut raw_value = ptr::null_mut();
112    check_status!(unsafe { sys::napi_create_bigint_uint64(self.0, value, &mut raw_value) })?;
113    Ok(JsBigInt::from_raw_unchecked(self.0, raw_value, 1))
114  }
115
116  #[cfg(feature = "napi6")]
117  pub fn create_bigint_from_i128(&self, value: i128) -> Result<JsBigInt> {
118    let mut raw_value = ptr::null_mut();
119    let sign_bit = i32::from(value <= 0);
120    let words = &value as *const i128 as *const u64;
121    check_status!(unsafe {
122      sys::napi_create_bigint_words(self.0, sign_bit, 2, words, &mut raw_value)
123    })?;
124    Ok(JsBigInt::from_raw_unchecked(self.0, raw_value, 1))
125  }
126
127  #[cfg(feature = "napi6")]
128  pub fn create_bigint_from_u128(&self, value: u128) -> Result<JsBigInt> {
129    let mut raw_value = ptr::null_mut();
130    let words = &value as *const u128 as *const u64;
131    check_status!(unsafe { sys::napi_create_bigint_words(self.0, 0, 2, words, &mut raw_value) })?;
132    Ok(JsBigInt::from_raw_unchecked(self.0, raw_value, 1))
133  }
134
135  /// [n_api_napi_create_bigint_words](https://nodejs.org/api/n-api.html#n_api_napi_create_bigint_words)
136  ///
137  /// The resulting BigInt will be negative when sign_bit is true.
138  #[cfg(feature = "napi6")]
139  pub fn create_bigint_from_words(&self, sign_bit: bool, words: Vec<u64>) -> Result<JsBigInt> {
140    let mut raw_value = ptr::null_mut();
141    let len = words.len();
142    check_status!(unsafe {
143      sys::napi_create_bigint_words(
144        self.0,
145        match sign_bit {
146          true => 1,
147          false => 0,
148        },
149        len,
150        words.as_ptr(),
151        &mut raw_value,
152      )
153    })?;
154    Ok(JsBigInt::from_raw_unchecked(self.0, raw_value, len))
155  }
156
157  pub fn create_string(&self, s: &str) -> Result<JsString> {
158    unsafe { self.create_string_from_c_char(s.as_ptr().cast(), s.len()) }
159  }
160
161  pub fn create_string_from_std(&self, s: String) -> Result<JsString> {
162    unsafe { self.create_string_from_c_char(s.as_ptr().cast(), s.len()) }
163  }
164
165  /// This API is used for C ffi scenario.
166  /// Convert raw *const c_char into JsString
167  ///
168  /// # Safety
169  ///
170  /// Create JsString from known valid utf-8 string
171  pub unsafe fn create_string_from_c_char(
172    &self,
173    data_ptr: *const c_char,
174    len: usize,
175  ) -> Result<JsString> {
176    let mut raw_value = ptr::null_mut();
177    check_status!(unsafe { sys::napi_create_string_utf8(self.0, data_ptr, len, &mut raw_value) })?;
178    Ok(unsafe { JsString::from_raw_unchecked(self.0, raw_value) })
179  }
180
181  pub fn create_string_utf16(&self, chars: &[u16]) -> Result<JsString> {
182    let mut raw_value = ptr::null_mut();
183    check_status!(unsafe {
184      sys::napi_create_string_utf16(self.0, chars.as_ptr(), chars.len(), &mut raw_value)
185    })?;
186    Ok(unsafe { JsString::from_raw_unchecked(self.0, raw_value) })
187  }
188
189  pub fn create_string_latin1(&self, chars: &[u8]) -> Result<JsString> {
190    let mut raw_value = ptr::null_mut();
191    check_status!(unsafe {
192      sys::napi_create_string_latin1(
193        self.0,
194        chars.as_ptr() as *const _,
195        chars.len(),
196        &mut raw_value,
197      )
198    })?;
199    Ok(unsafe { JsString::from_raw_unchecked(self.0, raw_value) })
200  }
201
202  pub fn create_symbol_from_js_string(&self, description: JsString) -> Result<JsSymbol> {
203    let mut result = ptr::null_mut();
204    check_status!(unsafe { sys::napi_create_symbol(self.0, description.0.value, &mut result) })?;
205    Ok(unsafe { JsSymbol::from_raw_unchecked(self.0, result) })
206  }
207
208  pub fn create_symbol(&self, description: Option<&str>) -> Result<JsSymbol> {
209    let mut result = ptr::null_mut();
210    check_status!(unsafe {
211      sys::napi_create_symbol(
212        self.0,
213        description
214          .and_then(|desc| self.create_string(desc).ok())
215          .map(|string| string.0.value)
216          .unwrap_or(ptr::null_mut()),
217        &mut result,
218      )
219    })?;
220    Ok(unsafe { JsSymbol::from_raw_unchecked(self.0, result) })
221  }
222
223  pub fn create_object(&self) -> Result<JsObject> {
224    let mut raw_value = ptr::null_mut();
225    check_status!(unsafe { sys::napi_create_object(self.0, &mut raw_value) })?;
226    Ok(unsafe { JsObject::from_raw_unchecked(self.0, raw_value) })
227  }
228
229  pub fn create_empty_array(&self) -> Result<JsObject> {
230    let mut raw_value = ptr::null_mut();
231    check_status!(unsafe { sys::napi_create_array(self.0, &mut raw_value) })?;
232    Ok(unsafe { JsObject::from_raw_unchecked(self.0, raw_value) })
233  }
234
235  pub fn create_array_with_length(&self, length: usize) -> Result<JsObject> {
236    let mut raw_value = ptr::null_mut();
237    check_status!(unsafe { sys::napi_create_array_with_length(self.0, length, &mut raw_value) })?;
238    Ok(unsafe { JsObject::from_raw_unchecked(self.0, raw_value) })
239  }
240
241  /// This API allocates a node::Buffer object. While this is still a fully-supported data structure, in most cases using a TypedArray will suffice.
242  pub fn create_buffer(&self, length: usize) -> Result<JsBufferValue> {
243    let mut raw_value = ptr::null_mut();
244    let mut data_ptr = ptr::null_mut();
245    check_status!(unsafe {
246      sys::napi_create_buffer(self.0, length, &mut data_ptr, &mut raw_value)
247    })?;
248
249    Ok(JsBufferValue::new(
250      JsBuffer(Value {
251        env: self.0,
252        value: raw_value,
253        value_type: ValueType::Object,
254      }),
255      mem::ManuallyDrop::new(unsafe { Vec::from_raw_parts(data_ptr as *mut _, length, length) }),
256    ))
257  }
258
259  /// This API allocates a node::Buffer object and initializes it with data backed by the passed in buffer.
260  ///
261  /// While this is still a fully-supported data structure, in most cases using a TypedArray will suffice.
262  pub fn create_buffer_with_data(&self, mut data: Vec<u8>) -> Result<JsBufferValue> {
263    let length = data.len();
264    let mut raw_value = ptr::null_mut();
265    let data_ptr = data.as_mut_ptr();
266    let hint_ptr = Box::into_raw(Box::new((length, data.capacity())));
267    check_status!(unsafe {
268      if length == 0 {
269        // Rust uses 0x1 as the data pointer for empty buffers,
270        // but NAPI/V8 only allows multiple buffers to have
271        // the same data pointer if it's 0x0.
272        sys::napi_create_buffer(self.0, length, ptr::null_mut(), &mut raw_value)
273      } else {
274        let status = sys::napi_create_external_buffer(
275          self.0,
276          length,
277          data_ptr.cast(),
278          Some(drop_buffer),
279          hint_ptr.cast(),
280          &mut raw_value,
281        );
282        // electron doesn't support external buffers
283        if status == sys::Status::napi_no_external_buffers_allowed {
284          drop(Box::from_raw(hint_ptr));
285          let mut dest_data_ptr = ptr::null_mut();
286          let status = sys::napi_create_buffer_copy(
287            self.0,
288            length,
289            data.as_ptr().cast(),
290            &mut dest_data_ptr,
291            &mut raw_value,
292          );
293          data = Vec::from_raw_parts(dest_data_ptr.cast(), length, length);
294          status
295        } else {
296          status
297        }
298      }
299    })?;
300    Ok(JsBufferValue::new(
301      JsBuffer(Value {
302        env: self.0,
303        value: raw_value,
304        value_type: ValueType::Object,
305      }),
306      mem::ManuallyDrop::new(data),
307    ))
308  }
309
310  /// # Safety
311  /// Mostly the same with `create_buffer_with_data`
312  ///
313  /// Provided `finalize_callback` will be called when `Buffer` got dropped.
314  ///
315  /// You can pass in `noop_finalize` if you have nothing to do in finalize phase.
316  ///
317  /// # Notes
318  ///
319  /// JavaScript may mutate the data passed in to this buffer when writing the buffer.
320  /// However, some JavaScript runtimes do not support external buffers (notably electron!)
321  /// in which case modifications may be lost.
322  ///
323  /// If you need to support these runtimes, you should create a buffer by other means and then
324  /// later copy the data back out.
325  pub unsafe fn create_buffer_with_borrowed_data<Hint, Finalize>(
326    &self,
327    mut data: *mut u8,
328    length: usize,
329    hint: Hint,
330    finalize_callback: Finalize,
331  ) -> Result<JsBufferValue>
332  where
333    Finalize: FnOnce(Hint, Env),
334  {
335    let mut raw_value = ptr::null_mut();
336    if data.is_null() || data as *const u8 == EMPTY_VEC.as_ptr() {
337      return Err(Error::new(
338        Status::InvalidArg,
339        "Borrowed data should not be null".to_owned(),
340      ));
341    }
342    let hint_ptr = Box::into_raw(Box::new((hint, finalize_callback)));
343    unsafe {
344      let status = sys::napi_create_external_buffer(
345        self.0,
346        length,
347        data as *mut c_void,
348        Some(
349          raw_finalize_with_custom_callback::<Hint, Finalize>
350            as unsafe extern "C" fn(
351              env: sys::napi_env,
352              finalize_data: *mut c_void,
353              finalize_hint: *mut c_void,
354            ),
355        ),
356        hint_ptr.cast(),
357        &mut raw_value,
358      );
359      if status == sys::Status::napi_no_external_buffers_allowed {
360        let (hint, finalize) = *Box::from_raw(hint_ptr);
361        let mut result_data = ptr::null_mut();
362        let status = sys::napi_create_buffer_copy(
363          self.0,
364          length,
365          data.cast(),
366          &mut result_data,
367          &mut raw_value,
368        );
369        data = result_data.cast();
370        finalize(hint, *self);
371        check_status!(status)?;
372      } else {
373        check_status!(status)?;
374      }
375    };
376    Ok(JsBufferValue::new(
377      JsBuffer(Value {
378        env: self.0,
379        value: raw_value,
380        value_type: ValueType::Object,
381      }),
382      mem::ManuallyDrop::new(unsafe { Vec::from_raw_parts(data, length, length) }),
383    ))
384  }
385
386  #[cfg(not(target_family = "wasm"))]
387  /// This function gives V8 an indication of the amount of externally allocated memory that is kept alive by JavaScript objects (i.e. a JavaScript object that points to its own memory allocated by a native module).
388  ///
389  /// Registering externally allocated memory will trigger global garbage collections more often than it would otherwise.
390  ///
391  /// ***ATTENTION ⚠️***, do not use this with `create_buffer_with_data/create_arraybuffer_with_data`, since these two functions already called the `adjust_external_memory` internal.
392  pub fn adjust_external_memory(&mut self, size: i64) -> Result<i64> {
393    let mut changed = 0i64;
394    check_status!(unsafe { sys::napi_adjust_external_memory(self.0, size, &mut changed) })?;
395    Ok(changed)
396  }
397
398  #[cfg(target_family = "wasm")]
399  #[allow(unused_variables)]
400  pub fn adjust_external_memory(&mut self, size: i64) -> Result<i64> {
401    Ok(0)
402  }
403
404  /// This API allocates a node::Buffer object and initializes it with data copied from the passed-in buffer.
405  ///
406  /// While this is still a fully-supported data structure, in most cases using a TypedArray will suffice.
407  pub fn create_buffer_copy<D>(&self, data_to_copy: D) -> Result<JsBufferValue>
408  where
409    D: AsRef<[u8]>,
410  {
411    let length = data_to_copy.as_ref().len();
412    let data_ptr = data_to_copy.as_ref().as_ptr();
413    let mut copy_data = ptr::null_mut();
414    let mut raw_value = ptr::null_mut();
415    check_status!(unsafe {
416      sys::napi_create_buffer_copy(
417        self.0,
418        length,
419        data_ptr as *mut c_void,
420        &mut copy_data,
421        &mut raw_value,
422      )
423    })?;
424    Ok(JsBufferValue::new(
425      JsBuffer(Value {
426        env: self.0,
427        value: raw_value,
428        value_type: ValueType::Object,
429      }),
430      mem::ManuallyDrop::new(unsafe { Vec::from_raw_parts(copy_data as *mut u8, length, length) }),
431    ))
432  }
433
434  pub fn create_arraybuffer(&self, length: usize) -> Result<JsArrayBufferValue> {
435    let mut raw_value = ptr::null_mut();
436    let mut data_ptr = ptr::null_mut();
437    check_status!(unsafe {
438      sys::napi_create_arraybuffer(self.0, length, &mut data_ptr, &mut raw_value)
439    })?;
440
441    Ok(JsArrayBufferValue::new(
442      unsafe { JsArrayBuffer::from_raw_unchecked(self.0, raw_value) },
443      data_ptr as *mut c_void,
444      length,
445    ))
446  }
447
448  pub fn create_arraybuffer_with_data(&self, mut data: Vec<u8>) -> Result<JsArrayBufferValue> {
449    let length = data.len();
450    let mut raw_value = ptr::null_mut();
451    let data_ptr = data.as_mut_ptr();
452    check_status!(unsafe {
453      if length == 0 {
454        // Rust uses 0x1 as the data pointer for empty buffers,
455        // but NAPI/V8 only allows multiple buffers to have
456        // the same data pointer if it's 0x0.
457        sys::napi_create_arraybuffer(self.0, length, ptr::null_mut(), &mut raw_value)
458      } else {
459        let hint_ptr = Box::into_raw(Box::new((length, data.capacity())));
460        let status = sys::napi_create_external_arraybuffer(
461          self.0,
462          data_ptr.cast(),
463          length,
464          Some(drop_buffer),
465          hint_ptr.cast(),
466          &mut raw_value,
467        );
468        if status == sys::Status::napi_no_external_buffers_allowed {
469          drop(Box::from_raw(hint_ptr));
470          let mut underlying_data = ptr::null_mut();
471          let status =
472            sys::napi_create_arraybuffer(self.0, length, &mut underlying_data, &mut raw_value);
473          ptr::copy_nonoverlapping(data_ptr, underlying_data.cast(), length);
474          status
475        } else {
476          status
477        }
478      }
479    })?;
480
481    mem::forget(data);
482    Ok(JsArrayBufferValue::new(
483      JsArrayBuffer(Value {
484        env: self.0,
485        value: raw_value,
486        value_type: ValueType::Object,
487      }),
488      data_ptr.cast(),
489      length,
490    ))
491  }
492
493  /// # Safety
494  /// Mostly the same with `create_arraybuffer_with_data`
495  ///
496  /// Provided `finalize_callback` will be called when `Buffer` got dropped.
497  ///
498  /// You can pass in `noop_finalize` if you have nothing to do in finalize phase.
499  ///
500  /// # Notes
501  ///
502  /// JavaScript may mutate the data passed in to this buffer when writing the buffer.
503  /// However, some JavaScript runtimes do not support external buffers (notably electron!)
504  /// in which case modifications may be lost.
505  ///
506  /// If you need to support these runtimes, you should create a buffer by other means and then
507  /// later copy the data back out.
508  pub unsafe fn create_arraybuffer_with_borrowed_data<Hint, Finalize>(
509    &self,
510    data: *mut u8,
511    length: usize,
512    hint: Hint,
513    finalize_callback: Finalize,
514  ) -> Result<JsArrayBufferValue>
515  where
516    Finalize: FnOnce(Hint, Env),
517  {
518    let mut raw_value = ptr::null_mut();
519    let hint_ptr = Box::into_raw(Box::new((hint, finalize_callback)));
520    unsafe {
521      let status = sys::napi_create_external_arraybuffer(
522        self.0,
523        if length == 0 {
524          // Rust uses 0x1 as the data pointer for empty buffers,
525          // but NAPI/V8 only allows multiple buffers to have
526          // the same data pointer if it's 0x0.
527          ptr::null_mut()
528        } else {
529          data as *mut c_void
530        },
531        length,
532        Some(
533          raw_finalize_with_custom_callback::<Hint, Finalize>
534            as unsafe extern "C" fn(
535              env: sys::napi_env,
536              finalize_data: *mut c_void,
537              finalize_hint: *mut c_void,
538            ),
539        ),
540        hint_ptr.cast(),
541        &mut raw_value,
542      );
543      if status == sys::Status::napi_no_external_buffers_allowed {
544        let (hint, finalize) = *Box::from_raw(hint_ptr);
545        let mut underlying_data = ptr::null_mut();
546        let status =
547          sys::napi_create_arraybuffer(self.0, length, &mut underlying_data, &mut raw_value);
548        ptr::copy_nonoverlapping(data, underlying_data.cast(), length);
549        finalize(hint, *self);
550        check_status!(status)?;
551      } else {
552        check_status!(status)?;
553      }
554    };
555    Ok(JsArrayBufferValue::new(
556      JsArrayBuffer(Value {
557        env: self.0,
558        value: raw_value,
559        value_type: ValueType::Object,
560      }),
561      data as *mut c_void,
562      length,
563    ))
564  }
565
566  /// This API allows an add-on author to create a function object in native code.
567  ///
568  /// This is the primary mechanism to allow calling into the add-on's native code from JavaScript.
569  ///
570  /// The newly created function is not automatically visible from script after this call.
571  ///
572  /// Instead, a property must be explicitly set on any object that is visible to JavaScript, in order for the function to be accessible from script.
573  pub fn create_function(&self, name: &str, callback: Callback) -> Result<JsFunction> {
574    let mut raw_result = ptr::null_mut();
575    let len = name.len();
576    let name = CString::new(name)?;
577    check_status!(unsafe {
578      sys::napi_create_function(
579        self.0,
580        name.as_ptr(),
581        len,
582        Some(callback),
583        ptr::null_mut(),
584        &mut raw_result,
585      )
586    })?;
587
588    Ok(unsafe { JsFunction::from_raw_unchecked(self.0, raw_result) })
589  }
590
591  #[cfg(feature = "napi5")]
592  pub fn create_function_from_closure<R, F>(&self, name: &str, callback: F) -> Result<JsFunction>
593  where
594    F: 'static + Fn(crate::CallContext<'_>) -> Result<R>,
595    R: ToNapiValue,
596  {
597    let closure_data_ptr = Box::into_raw(Box::new(callback));
598
599    let mut raw_result = ptr::null_mut();
600    let len = name.len();
601    let name = CString::new(name)?;
602    check_status!(unsafe {
603      sys::napi_create_function(
604        self.0,
605        name.as_ptr(),
606        len,
607        Some(trampoline::<R, F>),
608        closure_data_ptr.cast(), // We let it borrow the data here
609        &mut raw_result,
610      )
611    })?;
612
613    // Note: based on N-API docs, at this point, we have created an effective
614    // `&'static dyn Fn…` in Rust parlance, in that thanks to `Box::into_raw()`
615    // we are sure the context won't be freed, and thus the callback may use
616    // it to call the actual method thanks to the trampoline…
617    // But we thus have a data leak: there is nothing yet responsible for
618    // running the `drop(Box::from_raw(…))` cleanup code.
619    //
620    // To solve that, according to the docs, we need to attach a finalizer:
621    check_status!(unsafe {
622      sys::napi_add_finalizer(
623        self.0,
624        raw_result,
625        closure_data_ptr.cast(),
626        Some(finalize_box_trampoline::<F>),
627        ptr::null_mut(),
628        ptr::null_mut(),
629      )
630    })?;
631
632    Ok(unsafe { JsFunction::from_raw_unchecked(self.0, raw_result) })
633  }
634
635  /// This API retrieves a napi_extended_error_info structure with information about the last error that occurred.
636  ///
637  /// The content of the napi_extended_error_info returned is only valid up until an n-api function is called on the same env.
638  ///
639  /// Do not rely on the content or format of any of the extended information as it is not subject to SemVer and may change at any time. It is intended only for logging purposes.
640  ///
641  /// This API can be called even if there is a pending JavaScript exception.
642  pub fn get_last_error_info(&self) -> Result<ExtendedErrorInfo> {
643    let mut raw_extended_error = ptr::null();
644    check_status!(unsafe { sys::napi_get_last_error_info(self.0, &mut raw_extended_error) })?;
645    unsafe { ptr::read(raw_extended_error) }.try_into()
646  }
647
648  /// Throw any JavaScript value
649  pub fn throw<T: NapiRaw>(&self, value: T) -> Result<()> {
650    check_status!(unsafe { sys::napi_throw(self.0, value.raw()) })
651  }
652
653  /// This API throws a JavaScript Error with the text provided.
654  pub fn throw_error(&self, msg: &str, code: Option<&str>) -> Result<()> {
655    let code = code.and_then(|s| CString::new(s).ok());
656    let msg = CString::new(msg)?;
657    check_status!(unsafe {
658      sys::napi_throw_error(
659        self.0,
660        code.map(|s| s.as_ptr()).unwrap_or(ptr::null_mut()),
661        msg.as_ptr(),
662      )
663    })
664  }
665
666  /// This API throws a JavaScript RangeError with the text provided.
667  pub fn throw_range_error(&self, msg: &str, code: Option<&str>) -> Result<()> {
668    let code = code.and_then(|s| CString::new(s).ok());
669    let msg = CString::new(msg)?;
670    check_status!(unsafe {
671      sys::napi_throw_range_error(
672        self.0,
673        code.map(|s| s.as_ptr()).unwrap_or(ptr::null_mut()),
674        msg.as_ptr(),
675      )
676    })
677  }
678
679  /// This API throws a JavaScript TypeError with the text provided.
680  pub fn throw_type_error(&self, msg: &str, code: Option<&str>) -> Result<()> {
681    let code = code.and_then(|s| CString::new(s).ok());
682    let msg = CString::new(msg)?;
683    check_status!(unsafe {
684      sys::napi_throw_type_error(
685        self.0,
686        code.map(|s| s.as_ptr()).unwrap_or(ptr::null_mut()),
687        msg.as_ptr(),
688      )
689    })
690  }
691
692  /// This API throws a JavaScript SyntaxError with the text provided.
693  #[cfg(feature = "napi9")]
694  pub fn throw_syntax_error<S: AsRef<str>, C: AsRef<str>>(&self, msg: S, code: Option<C>) {
695    use crate::check_status_or_throw;
696
697    let code = code.as_ref().map(|c| c.as_ref()).unwrap_or("");
698    let c_code = CString::new(code).expect("code must be a valid utf-8 string");
699    let code_ptr = c_code.as_ptr();
700    let msg: CString = CString::new(msg.as_ref()).expect("msg must be a valid utf-8 string");
701    let msg_ptr = msg.as_ptr();
702    check_status_or_throw!(
703      self.0,
704      unsafe { sys::node_api_throw_syntax_error(self.0, code_ptr, msg_ptr,) },
705      "Throw syntax error failed"
706    );
707  }
708
709  #[allow(clippy::expect_fun_call)]
710  /// In the event of an unrecoverable error in a native module
711  ///
712  /// A fatal error can be thrown to immediately terminate the process.
713  pub fn fatal_error(self, location: &str, message: &str) {
714    let location_len = location.len();
715    let message_len = message.len();
716    let location =
717      CString::new(location).expect(format!("Convert [{}] to CString failed", location).as_str());
718    let message =
719      CString::new(message).expect(format!("Convert [{}] to CString failed", message).as_str());
720
721    unsafe {
722      sys::napi_fatal_error(
723        location.as_ptr(),
724        location_len,
725        message.as_ptr(),
726        message_len,
727      )
728    }
729  }
730
731  #[cfg(feature = "napi3")]
732  /// Trigger an 'uncaughtException' in JavaScript.
733  ///
734  /// Useful if an async callback throws an exception with no way to recover.
735  pub fn fatal_exception(&self, err: Error) {
736    unsafe {
737      let js_error = JsError::from(err).into_value(self.0);
738      debug_assert!(sys::napi_fatal_exception(self.0, js_error) == sys::Status::napi_ok);
739    };
740  }
741
742  /// Create JavaScript class
743  pub fn define_class(
744    &self,
745    name: &str,
746    constructor_cb: Callback,
747    properties: &[Property],
748  ) -> Result<JsFunction> {
749    let mut raw_result = ptr::null_mut();
750    let raw_properties = properties
751      .iter()
752      .map(|prop| prop.raw())
753      .collect::<Vec<sys::napi_property_descriptor>>();
754    let c_name = CString::new(name)?;
755    check_status!(unsafe {
756      sys::napi_define_class(
757        self.0,
758        c_name.as_ptr() as *const c_char,
759        name.len(),
760        Some(constructor_cb),
761        ptr::null_mut(),
762        raw_properties.len(),
763        raw_properties.as_ptr(),
764        &mut raw_result,
765      )
766    })?;
767
768    Ok(unsafe { JsFunction::from_raw_unchecked(self.0, raw_result) })
769  }
770
771  #[allow(clippy::needless_pass_by_ref_mut)]
772  pub fn wrap<T: 'static>(&self, js_object: &mut JsObject, native_object: T) -> Result<()> {
773    check_status!(unsafe {
774      sys::napi_wrap(
775        self.0,
776        js_object.0.value,
777        Box::into_raw(Box::new(TaggedObject::new(native_object))).cast(),
778        Some(raw_finalize::<T>),
779        ptr::null_mut(),
780        ptr::null_mut(),
781      )
782    })
783  }
784
785  pub fn unwrap<T: 'static>(&self, js_object: &JsObject) -> Result<&mut T> {
786    unsafe {
787      let mut unknown_tagged_object: *mut c_void = ptr::null_mut();
788      check_status!(sys::napi_unwrap(
789        self.0,
790        js_object.0.value,
791        &mut unknown_tagged_object,
792      ))?;
793
794      let type_id = unknown_tagged_object as *const TypeId;
795      if *type_id == TypeId::of::<T>() {
796        let tagged_object = unknown_tagged_object as *mut TaggedObject<T>;
797        (*tagged_object).object.as_mut().ok_or_else(|| {
798          Error::new(
799            Status::InvalidArg,
800            "Invalid argument, nothing attach to js_object".to_owned(),
801          )
802        })
803      } else {
804        Err(Error::new(
805          Status::InvalidArg,
806          format!(
807            "Invalid argument, {} on unwrap is not the type of wrapped object",
808            type_name::<T>()
809          ),
810        ))
811      }
812    }
813  }
814
815  pub fn drop_wrapped<T: 'static>(&self, js_object: &JsObject) -> Result<()> {
816    unsafe {
817      let mut unknown_tagged_object = ptr::null_mut();
818      check_status!(sys::napi_remove_wrap(
819        self.0,
820        js_object.0.value,
821        &mut unknown_tagged_object,
822      ))?;
823      let type_id = unknown_tagged_object as *const TypeId;
824      if *type_id == TypeId::of::<T>() {
825        drop(Box::from_raw(unknown_tagged_object as *mut TaggedObject<T>));
826        Ok(())
827      } else {
828        Err(Error::new(
829          Status::InvalidArg,
830          format!(
831            "Invalid argument, {} on unwrap is not the type of wrapped object",
832            type_name::<T>()
833          ),
834        ))
835      }
836    }
837  }
838
839  /// This API create a new reference with the initial 1 ref count to the Object passed in.
840  pub fn create_reference<T>(&self, value: T) -> Result<Ref<()>>
841  where
842    T: NapiRaw,
843  {
844    let mut raw_ref = ptr::null_mut();
845    let initial_ref_count = 1;
846    let raw_value = unsafe { value.raw() };
847    check_status!(unsafe {
848      sys::napi_create_reference(self.0, raw_value, initial_ref_count, &mut raw_ref)
849    })?;
850    Ok(Ref {
851      raw_ref,
852      count: 1,
853      inner: (),
854    })
855  }
856
857  /// This API create a new reference with the specified reference count to the Object passed in.
858  pub fn create_reference_with_refcount<T>(&self, value: T, ref_count: u32) -> Result<Ref<()>>
859  where
860    T: NapiRaw,
861  {
862    let mut raw_ref = ptr::null_mut();
863    let raw_value = unsafe { value.raw() };
864    check_status!(unsafe {
865      sys::napi_create_reference(self.0, raw_value, ref_count, &mut raw_ref)
866    })?;
867    Ok(Ref {
868      raw_ref,
869      count: ref_count,
870      inner: (),
871    })
872  }
873
874  /// Get reference value from `Ref` with type check
875  ///
876  /// Return error if the type of `reference` provided is mismatched with `T`
877  pub fn get_reference_value<T>(&self, reference: &Ref<()>) -> Result<T>
878  where
879    T: NapiValue,
880  {
881    let mut js_value = ptr::null_mut();
882    check_status!(unsafe {
883      sys::napi_get_reference_value(self.0, reference.raw_ref, &mut js_value)
884    })?;
885    unsafe { T::from_raw(self.0, js_value) }
886  }
887
888  /// Get reference value from `Ref` without type check
889  ///
890  /// Using this API if you are sure the type of `T` is matched with provided `Ref<()>`.
891  ///
892  /// If type mismatched, calling `T::method` would return `Err`.
893  pub fn get_reference_value_unchecked<T>(&self, reference: &Ref<()>) -> Result<T>
894  where
895    T: NapiValue,
896  {
897    let mut js_value = ptr::null_mut();
898    check_status!(unsafe {
899      sys::napi_get_reference_value(self.0, reference.raw_ref, &mut js_value)
900    })?;
901    Ok(unsafe { T::from_raw_unchecked(self.0, js_value) })
902  }
903
904  /// If `size_hint` provided, `Env::adjust_external_memory` will be called under the hood.
905  ///
906  /// If no `size_hint` provided, global garbage collections will be triggered less times than expected.
907  ///
908  /// If getting the exact `native_object` size is difficult, you can provide an approximate value, it's only effect to the GC.
909  pub fn create_external<T: 'static>(
910    &self,
911    native_object: T,
912    size_hint: Option<i64>,
913  ) -> Result<JsExternal> {
914    let mut object_value = ptr::null_mut();
915    check_status!(unsafe {
916      sys::napi_create_external(
917        self.0,
918        Box::into_raw(Box::new(TaggedObject::new(native_object))).cast(),
919        Some(raw_finalize::<T>),
920        Box::into_raw(Box::new(size_hint)).cast(),
921        &mut object_value,
922      )
923    })?;
924    if let Some(changed) = size_hint {
925      if changed != 0 {
926        let mut adjusted_value = 0i64;
927        check_status!(unsafe {
928          sys::napi_adjust_external_memory(self.0, changed, &mut adjusted_value)
929        })?;
930      }
931    };
932    Ok(unsafe { JsExternal::from_raw_unchecked(self.0, object_value) })
933  }
934
935  pub fn get_value_external<T: 'static>(&self, js_external: &JsExternal) -> Result<&mut T> {
936    unsafe {
937      let mut unknown_tagged_object = ptr::null_mut();
938      check_status!(sys::napi_get_value_external(
939        self.0,
940        js_external.0.value,
941        &mut unknown_tagged_object,
942      ))?;
943
944      let type_id = unknown_tagged_object as *const TypeId;
945      if *type_id == TypeId::of::<T>() {
946        let tagged_object = unknown_tagged_object as *mut TaggedObject<T>;
947        (*tagged_object).object.as_mut().ok_or_else(|| {
948          Error::new(
949            Status::InvalidArg,
950            "nothing attach to js_external".to_owned(),
951          )
952        })
953      } else {
954        Err(Error::new(
955          Status::InvalidArg,
956          "T on get_value_external is not the type of wrapped object".to_owned(),
957        ))
958      }
959    }
960  }
961
962  pub fn create_error(&self, e: Error) -> Result<JsObject> {
963    let reason = &e.reason;
964    let reason_string = self.create_string(reason.as_str())?;
965    let mut result = ptr::null_mut();
966    check_status!(unsafe {
967      sys::napi_create_error(self.0, ptr::null_mut(), reason_string.0.value, &mut result)
968    })?;
969    Ok(unsafe { JsObject::from_raw_unchecked(self.0, result) })
970  }
971
972  /// Run [Task](./trait.Task.html) in libuv thread pool, return [AsyncWorkPromise](./struct.AsyncWorkPromise.html)
973  pub fn spawn<T: 'static + Task>(&self, task: T) -> Result<AsyncWorkPromise> {
974    async_work::run(self.0, task, None)
975  }
976
977  pub fn run_in_scope<T, F>(&self, executor: F) -> Result<T>
978  where
979    F: FnOnce() -> Result<T>,
980  {
981    let mut handle_scope = ptr::null_mut();
982    check_status!(unsafe { sys::napi_open_handle_scope(self.0, &mut handle_scope) })?;
983
984    let result = executor();
985
986    check_status!(unsafe { sys::napi_close_handle_scope(self.0, handle_scope) })?;
987    result
988  }
989
990  /// Node-API provides an API for executing a string containing JavaScript using the underlying JavaScript engine.
991  /// This function executes a string of JavaScript code and returns its result with the following caveats:
992  /// - Unlike `eval`, this function does not allow the script to access the current lexical scope, and therefore also does not allow to access the [module scope](https://nodejs.org/api/modules.html#the-module-scope), meaning that pseudo-globals such as require will not be available.
993  /// - The script can access the [global scope](https://nodejs.org/api/globals.html). Function and `var` declarations in the script will be added to the [global](https://nodejs.org/api/globals.html#global) object. Variable declarations made using `let` and `const` will be visible globally, but will not be added to the global object.
994  /// - The value of this is [global](https://nodejs.org/api/globals.html) within the script.
995  pub fn run_script<S: AsRef<str>, V: FromNapiValue>(&self, script: S) -> Result<V> {
996    let s = self.create_string(script.as_ref())?;
997    let mut raw_value = ptr::null_mut();
998    check_status!(unsafe { sys::napi_run_script(self.0, s.raw(), &mut raw_value) })?;
999    unsafe { V::from_napi_value(self.0, raw_value) }
1000  }
1001
1002  /// `process.versions.napi`
1003  pub fn get_napi_version(&self) -> Result<u32> {
1004    let global = self.get_global()?;
1005    let process: JsObject = global.get_named_property("process")?;
1006    let versions: JsObject = process.get_named_property("versions")?;
1007    let napi_version: JsString = versions.get_named_property("napi")?;
1008    napi_version
1009      .into_utf8()?
1010      .as_str()?
1011      .parse()
1012      .map_err(|e| Error::new(Status::InvalidArg, format!("{}", e)))
1013  }
1014
1015  #[cfg(feature = "napi2")]
1016  pub fn get_uv_event_loop(&self) -> Result<*mut sys::uv_loop_s> {
1017    let mut uv_loop: *mut sys::uv_loop_s = ptr::null_mut();
1018    check_status!(unsafe { sys::napi_get_uv_event_loop(self.0, &mut uv_loop) })?;
1019    Ok(uv_loop)
1020  }
1021
1022  #[cfg(feature = "napi3")]
1023  pub fn add_env_cleanup_hook<T, F>(
1024    &mut self,
1025    cleanup_data: T,
1026    cleanup_fn: F,
1027  ) -> Result<CleanupEnvHook<T>>
1028  where
1029    T: 'static,
1030    F: 'static + FnOnce(T),
1031  {
1032    let hook = CleanupEnvHookData {
1033      data: cleanup_data,
1034      hook: Box::new(cleanup_fn),
1035    };
1036    let hook_ref = Box::leak(Box::new(hook));
1037    check_status!(unsafe {
1038      sys::napi_add_env_cleanup_hook(
1039        self.0,
1040        Some(cleanup_env::<T>),
1041        hook_ref as *mut CleanupEnvHookData<T> as *mut _,
1042      )
1043    })?;
1044    Ok(CleanupEnvHook(hook_ref))
1045  }
1046
1047  #[cfg(feature = "napi3")]
1048  pub fn remove_env_cleanup_hook<T>(&mut self, hook: CleanupEnvHook<T>) -> Result<()>
1049  where
1050    T: 'static,
1051  {
1052    check_status!(unsafe {
1053      sys::napi_remove_env_cleanup_hook(self.0, Some(cleanup_env::<T>), hook.0 as *mut _)
1054    })
1055  }
1056
1057  #[cfg(feature = "napi4")]
1058  pub fn create_threadsafe_function<
1059    T: Send,
1060    V: ToNapiValue,
1061    R: 'static + Send + FnMut(ThreadSafeCallContext<T>) -> Result<Vec<V>>,
1062  >(
1063    &self,
1064    func: &JsFunction,
1065    max_queue_size: usize,
1066    callback: R,
1067  ) -> Result<ThreadsafeFunction<T>> {
1068    ThreadsafeFunction::create(self.0, func.0.value, max_queue_size, callback)
1069  }
1070
1071  #[cfg(all(feature = "tokio_rt", feature = "napi4"))]
1072  pub fn execute_tokio_future<
1073    T: 'static + Send,
1074    V: 'static + ToNapiValue,
1075    F: 'static + Send + Future<Output = Result<T>>,
1076    R: 'static + FnOnce(&mut Env, T) -> Result<V>,
1077  >(
1078    &self,
1079    fut: F,
1080    resolver: R,
1081  ) -> Result<JsObject> {
1082    use crate::tokio_runtime;
1083
1084    let promise = tokio_runtime::execute_tokio_future(self.0, fut, |env, val| unsafe {
1085      resolver(&mut Env::from_raw(env), val).and_then(|v| ToNapiValue::to_napi_value(env, v))
1086    })?;
1087
1088    Ok(unsafe { JsObject::from_raw_unchecked(self.0, promise) })
1089  }
1090
1091  #[cfg(all(feature = "tokio_rt", feature = "napi4"))]
1092  pub fn spawn_future<
1093    T: 'static + Send + ToNapiValue,
1094    F: 'static + Send + Future<Output = Result<T>>,
1095  >(
1096    &self,
1097    fut: F,
1098  ) -> Result<JsObject> {
1099    use crate::tokio_runtime;
1100
1101    let promise = tokio_runtime::execute_tokio_future(self.0, fut, |env, val| unsafe {
1102      ToNapiValue::to_napi_value(env, val)
1103    })?;
1104
1105    Ok(unsafe { JsObject::from_raw_unchecked(self.0, promise) })
1106  }
1107
1108  /// Creates a deferred promise, which can be resolved or rejected from a background thread.
1109  #[cfg(feature = "napi4")]
1110  pub fn create_deferred<Data: ToNapiValue, Resolver: FnOnce(Env) -> Result<Data>>(
1111    &self,
1112  ) -> Result<(JsDeferred<Data, Resolver>, JsObject)> {
1113    JsDeferred::new(self.raw())
1114  }
1115
1116  /// This API does not observe leap seconds; they are ignored, as ECMAScript aligns with POSIX time specification.
1117  ///
1118  /// This API allocates a JavaScript Date object.
1119  ///
1120  /// JavaScript Date objects are described in [Section 20.3](https://tc39.github.io/ecma262/#sec-date-objects) of the ECMAScript Language Specification.
1121  #[cfg(feature = "napi5")]
1122  pub fn create_date(&self, time: f64) -> Result<JsDate> {
1123    let mut js_value = ptr::null_mut();
1124    check_status!(unsafe { sys::napi_create_date(self.0, time, &mut js_value) })?;
1125    Ok(unsafe { JsDate::from_raw_unchecked(self.0, js_value) })
1126  }
1127
1128  #[cfg(feature = "napi6")]
1129
1130  /// This API associates data with the currently running Agent. data can later be retrieved using `Env::get_instance_data()`.
1131  ///
1132  /// Any existing data associated with the currently running Agent which was set by means of a previous call to `Env::set_instance_data()` will be overwritten.
1133  ///
1134  /// If a `finalize_cb` was provided by the previous call, it will not be called.
1135  pub fn set_instance_data<T, Hint, F>(&self, native: T, hint: Hint, finalize_cb: F) -> Result<()>
1136  where
1137    T: 'static,
1138    Hint: 'static,
1139    F: FnOnce(FinalizeContext<T, Hint>),
1140  {
1141    check_status!(unsafe {
1142      sys::napi_set_instance_data(
1143        self.0,
1144        Box::leak(Box::new((TaggedObject::new(native), finalize_cb))) as *mut (TaggedObject<T>, F)
1145          as *mut c_void,
1146        Some(
1147          set_instance_finalize_callback::<T, Hint, F>
1148            as unsafe extern "C" fn(
1149              env: sys::napi_env,
1150              finalize_data: *mut c_void,
1151              finalize_hint: *mut c_void,
1152            ),
1153        ),
1154        Box::leak(Box::new(hint)) as *mut Hint as *mut c_void,
1155      )
1156    })
1157  }
1158
1159  /// This API retrieves data that was previously associated with the currently running Agent via `Env::set_instance_data()`.
1160  ///
1161  /// If no data is set, the call will succeed and data will be set to NULL.
1162  #[cfg(feature = "napi6")]
1163  pub fn get_instance_data<T>(&self) -> Result<Option<&'static mut T>>
1164  where
1165    T: 'static,
1166  {
1167    let mut unknown_tagged_object: *mut c_void = ptr::null_mut();
1168    unsafe {
1169      check_status!(sys::napi_get_instance_data(
1170        self.0,
1171        &mut unknown_tagged_object
1172      ))?;
1173      let type_id = unknown_tagged_object as *const TypeId;
1174      if unknown_tagged_object.is_null() {
1175        return Ok(None);
1176      }
1177      if *type_id == TypeId::of::<T>() {
1178        let tagged_object = unknown_tagged_object as *mut TaggedObject<T>;
1179        (*tagged_object).object.as_mut().map(Some).ok_or_else(|| {
1180          Error::new(
1181            Status::InvalidArg,
1182            "Invalid argument, nothing attach to js_object".to_owned(),
1183          )
1184        })
1185      } else {
1186        Err(Error::new(
1187          Status::InvalidArg,
1188          format!(
1189            "Invalid argument, {} on unwrap is not the type of wrapped object",
1190            type_name::<T>()
1191          ),
1192        ))
1193      }
1194    }
1195  }
1196
1197  /// Registers hook, which is a function of type `FnOnce(Arg)`, as a function to be run with the `arg` parameter once the current Node.js environment exits.
1198  ///
1199  /// Unlike [`add_env_cleanup_hook`](https://docs.rs/napi/latest/napi/struct.Env.html#method.add_env_cleanup_hook), the hook is allowed to be asynchronous.
1200  ///
1201  /// Otherwise, behavior generally matches that of [`add_env_cleanup_hook`](https://docs.rs/napi/latest/napi/struct.Env.html#method.add_env_cleanup_hook).
1202  #[cfg(feature = "napi8")]
1203  pub fn add_removable_async_cleanup_hook<Arg, F>(
1204    &self,
1205    arg: Arg,
1206    cleanup_fn: F,
1207  ) -> Result<AsyncCleanupHook>
1208  where
1209    F: FnOnce(Arg),
1210    Arg: 'static,
1211  {
1212    let mut handle = ptr::null_mut();
1213    check_status!(unsafe {
1214      sys::napi_add_async_cleanup_hook(
1215        self.0,
1216        Some(
1217          async_finalize::<Arg, F>
1218            as unsafe extern "C" fn(handle: sys::napi_async_cleanup_hook_handle, data: *mut c_void),
1219        ),
1220        Box::leak(Box::new((arg, cleanup_fn))) as *mut (Arg, F) as *mut c_void,
1221        &mut handle,
1222      )
1223    })?;
1224    Ok(AsyncCleanupHook(handle))
1225  }
1226
1227  /// This API is very similar to [`add_removable_async_cleanup_hook`](https://docs.rs/napi/latest/napi/struct.Env.html#method.add_removable_async_cleanup_hook)
1228  ///
1229  /// Use this one if you don't want remove the cleanup hook anymore.
1230  #[cfg(feature = "napi8")]
1231  pub fn add_async_cleanup_hook<Arg, F>(&self, arg: Arg, cleanup_fn: F) -> Result<()>
1232  where
1233    F: FnOnce(Arg),
1234    Arg: 'static,
1235  {
1236    check_status!(unsafe {
1237      sys::napi_add_async_cleanup_hook(
1238        self.0,
1239        Some(
1240          async_finalize::<Arg, F>
1241            as unsafe extern "C" fn(handle: sys::napi_async_cleanup_hook_handle, data: *mut c_void),
1242        ),
1243        Box::leak(Box::new((arg, cleanup_fn))) as *mut (Arg, F) as *mut c_void,
1244        ptr::null_mut(),
1245      )
1246    })
1247  }
1248
1249  #[cfg(feature = "napi9")]
1250  pub fn symbol_for(&self, description: &str) -> Result<JsSymbol> {
1251    let mut result = ptr::null_mut();
1252    let len = description.len();
1253    let description = CString::new(description)?;
1254    check_status!(unsafe {
1255      sys::node_api_symbol_for(self.0, description.as_ptr(), len, &mut result)
1256    })?;
1257
1258    Ok(unsafe { JsSymbol::from_raw_unchecked(self.0, result) })
1259  }
1260
1261  #[cfg(feature = "napi9")]
1262  /// This API retrieves the file path of the currently running JS module as a URL. For a file on
1263  /// the local file system it will start with `file://`.
1264  ///
1265  /// # Errors
1266  ///
1267  /// The retrieved string may be empty if the add-on loading process fails to establish the
1268  /// add-on's file name.
1269  pub fn get_module_file_name(&self) -> Result<String> {
1270    let mut char_ptr = ptr::null();
1271    check_status!(
1272      unsafe { sys::node_api_get_module_file_name(self.0, &mut char_ptr) },
1273      "call node_api_get_module_file_name failed"
1274    )?;
1275    // SAFETY: This is safe because `char_ptr` is guaranteed to not be `null`, and point to
1276    // null-terminated string data.
1277    let module_filename = unsafe { std::ffi::CStr::from_ptr(char_ptr) };
1278
1279    Ok(module_filename.to_string_lossy().into_owned())
1280  }
1281
1282  /// ### Serialize `Rust Struct` into `JavaScript Value`
1283  ///
1284  /// ```
1285  /// #[derive(Serialize, Debug, Deserialize)]
1286  /// struct AnObject {
1287  ///     a: u32,
1288  ///     b: Vec<f64>,
1289  ///     c: String,
1290  /// }
1291  ///
1292  /// #[js_function]
1293  /// fn serialize(ctx: CallContext) -> Result<JsUnknown> {
1294  ///     let value = AnyObject { a: 1, b: vec![0.1, 2.22], c: "hello" };
1295  ///     ctx.env.to_js_value(&value)
1296  /// }
1297  /// ```
1298  #[cfg(feature = "serde-json")]
1299  #[allow(clippy::wrong_self_convention)]
1300  pub fn to_js_value<T>(&self, node: &T) -> Result<JsUnknown>
1301  where
1302    T: Serialize,
1303  {
1304    let s = Ser(self);
1305    node.serialize(s).map(JsUnknown)
1306  }
1307
1308  /// ### Deserialize data from `JsValue`
1309  /// ```
1310  /// #[derive(Serialize, Debug, Deserialize)]
1311  /// struct AnObject {
1312  ///     a: u32,
1313  ///     b: Vec<f64>,
1314  ///     c: String,
1315  /// }
1316  ///
1317  /// #[js_function(1)]
1318  /// fn deserialize_from_js(ctx: CallContext) -> Result<JsUndefined> {
1319  ///     let arg0 = ctx.get::<JsUnknown>(0)?;
1320  ///     let de_serialized: AnObject = ctx.env.from_js_value(arg0)?;
1321  ///     ...
1322  /// }
1323  ///
1324  #[cfg(feature = "serde-json")]
1325  pub fn from_js_value<T, V>(&self, value: V) -> Result<T>
1326  where
1327    T: DeserializeOwned + ?Sized,
1328    V: NapiRaw,
1329  {
1330    let value = Value {
1331      env: self.0,
1332      value: unsafe { value.raw() },
1333      value_type: ValueType::Unknown,
1334    };
1335    let mut de = De(&value);
1336    T::deserialize(&mut de)
1337  }
1338
1339  /// This API represents the invocation of the Strict Equality algorithm as defined in [Section 7.2.14](https://tc39.es/ecma262/#sec-strict-equality-comparison) of the ECMAScript Language Specification.
1340  pub fn strict_equals<A: NapiRaw, B: NapiRaw>(&self, a: A, b: B) -> Result<bool> {
1341    let mut result = false;
1342    check_status!(unsafe { sys::napi_strict_equals(self.0, a.raw(), b.raw(), &mut result) })?;
1343    Ok(result)
1344  }
1345
1346  pub fn get_node_version(&self) -> Result<NodeVersion> {
1347    let mut result = ptr::null();
1348    check_status!(unsafe { sys::napi_get_node_version(self.0, &mut result) })?;
1349    let version = unsafe { *result };
1350    version.try_into()
1351  }
1352
1353  /// get raw env ptr
1354  pub fn raw(&self) -> sys::napi_env {
1355    self.0
1356  }
1357}
1358
1359/// This function could be used for `create_buffer_with_borrowed_data` and want do noting when Buffer finalized.
1360pub fn noop_finalize<Hint>(_hint: Hint, _env: Env) {}
1361
1362unsafe extern "C" fn drop_buffer(
1363  _env: sys::napi_env,
1364  finalize_data: *mut c_void,
1365  hint: *mut c_void,
1366) {
1367  let length_ptr = hint as *mut (usize, usize);
1368  let (length, cap) = unsafe { *Box::from_raw(length_ptr) };
1369  mem::drop(unsafe { Vec::from_raw_parts(finalize_data as *mut u8, length, cap) });
1370}
1371
1372pub(crate) unsafe extern "C" fn raw_finalize<T>(
1373  env: sys::napi_env,
1374  finalize_data: *mut c_void,
1375  finalize_hint: *mut c_void,
1376) {
1377  let tagged_object = finalize_data as *mut TaggedObject<T>;
1378  drop(unsafe { Box::from_raw(tagged_object) });
1379  if !finalize_hint.is_null() {
1380    let size_hint = unsafe { *Box::from_raw(finalize_hint as *mut Option<i64>) };
1381    if let Some(changed) = size_hint {
1382      if changed != 0 {
1383        let mut adjusted = 0i64;
1384        let status = unsafe { sys::napi_adjust_external_memory(env, -changed, &mut adjusted) };
1385        debug_assert!(
1386          status == sys::Status::napi_ok,
1387          "Calling napi_adjust_external_memory failed"
1388        );
1389      }
1390    };
1391  }
1392}
1393
1394#[cfg(feature = "napi6")]
1395unsafe extern "C" fn set_instance_finalize_callback<T, Hint, F>(
1396  raw_env: sys::napi_env,
1397  finalize_data: *mut c_void,
1398  finalize_hint: *mut c_void,
1399) where
1400  T: 'static,
1401  Hint: 'static,
1402  F: FnOnce(FinalizeContext<T, Hint>),
1403{
1404  let (value, callback) = unsafe { *Box::from_raw(finalize_data as *mut (TaggedObject<T>, F)) };
1405  let hint = unsafe { *Box::from_raw(finalize_hint as *mut Hint) };
1406  let env = unsafe { Env::from_raw(raw_env) };
1407  callback(FinalizeContext {
1408    value: value.object.unwrap(),
1409    hint,
1410    env,
1411  });
1412}
1413
1414#[cfg(feature = "napi3")]
1415unsafe extern "C" fn cleanup_env<T: 'static>(hook_data: *mut c_void) {
1416  let cleanup_env_hook = unsafe { Box::from_raw(hook_data as *mut CleanupEnvHookData<T>) };
1417  (cleanup_env_hook.hook)(cleanup_env_hook.data);
1418}
1419
1420unsafe extern "C" fn raw_finalize_with_custom_callback<Hint, Finalize>(
1421  env: sys::napi_env,
1422  _finalize_data: *mut c_void,
1423  finalize_hint: *mut c_void,
1424) where
1425  Finalize: FnOnce(Hint, Env),
1426{
1427  let (hint, callback) = unsafe { *Box::from_raw(finalize_hint as *mut (Hint, Finalize)) };
1428  callback(hint, unsafe { Env::from_raw(env) });
1429}
1430
1431#[cfg(feature = "napi8")]
1432unsafe extern "C" fn async_finalize<Arg, F>(
1433  handle: sys::napi_async_cleanup_hook_handle,
1434  data: *mut c_void,
1435) where
1436  Arg: 'static,
1437  F: FnOnce(Arg),
1438{
1439  let (arg, callback) = unsafe { *Box::from_raw(data as *mut (Arg, F)) };
1440  callback(arg);
1441  if !handle.is_null() {
1442    let status = unsafe { sys::napi_remove_async_cleanup_hook(handle) };
1443    assert!(
1444      status == sys::Status::napi_ok,
1445      "Remove async cleanup hook failed after async cleanup callback"
1446    );
1447  }
1448}
1449
1450#[cfg(feature = "napi5")]
1451pub(crate) unsafe extern "C" fn trampoline<
1452  R: ToNapiValue,
1453  F: Fn(crate::CallContext) -> Result<R>,
1454>(
1455  raw_env: sys::napi_env,
1456  cb_info: sys::napi_callback_info,
1457) -> sys::napi_value {
1458  use crate::CallContext;
1459
1460  let (raw_this, raw_args, closure_data_ptr, argc) = {
1461    // Fast path for 4 arguments or less.
1462    let mut argc = 4;
1463    let mut raw_args = Vec::with_capacity(4);
1464    let mut raw_this = ptr::null_mut();
1465    let mut closure_data_ptr = ptr::null_mut();
1466
1467    let status = unsafe {
1468      sys::napi_get_cb_info(
1469        raw_env,
1470        cb_info,
1471        &mut argc,
1472        raw_args.as_mut_ptr(),
1473        &mut raw_this,
1474        &mut closure_data_ptr,
1475      )
1476    };
1477    debug_assert!(
1478      Status::from(status) == Status::Ok,
1479      "napi_get_cb_info failed"
1480    );
1481
1482    // Arguments length greater than 4, resize the vector.
1483    if argc > 4 {
1484      raw_args = vec![ptr::null_mut(); argc];
1485      let status = unsafe {
1486        sys::napi_get_cb_info(
1487          raw_env,
1488          cb_info,
1489          &mut argc,
1490          raw_args.as_mut_ptr(),
1491          &mut raw_this,
1492          &mut closure_data_ptr,
1493        )
1494      };
1495      debug_assert!(
1496        Status::from(status) == Status::Ok,
1497        "napi_get_cb_info failed"
1498      );
1499    } else {
1500      unsafe { raw_args.set_len(argc) };
1501    }
1502
1503    (raw_this, raw_args, closure_data_ptr, argc)
1504  };
1505
1506  let closure: &F = Box::leak(unsafe { Box::from_raw(closure_data_ptr.cast()) });
1507  let mut env = unsafe { Env::from_raw(raw_env) };
1508  let call_context = CallContext::new(&mut env, cb_info, raw_this, raw_args.as_slice(), argc);
1509  closure(call_context)
1510    .and_then(|ret: R| unsafe { <R as ToNapiValue>::to_napi_value(env.0, ret) })
1511    .unwrap_or_else(|e| {
1512      unsafe { JsError::from(e).throw_into(raw_env) };
1513      ptr::null_mut()
1514    })
1515}
1516
1517#[cfg(feature = "napi5")]
1518pub(crate) unsafe extern "C" fn trampoline_setter<
1519  V: FromNapiValue,
1520  F: Fn(Env, crate::bindgen_runtime::Object, V) -> Result<()>,
1521>(
1522  raw_env: sys::napi_env,
1523  cb_info: sys::napi_callback_info,
1524) -> sys::napi_value {
1525  use crate::bindgen_runtime::Object;
1526
1527  let (raw_args, raw_this, closure_data_ptr) = {
1528    let mut argc = 1;
1529    let mut raw_args = vec![ptr::null_mut(); 1];
1530    let mut raw_this = ptr::null_mut();
1531    let mut data_ptr = ptr::null_mut();
1532
1533    let status = unsafe {
1534      sys::napi_get_cb_info(
1535        raw_env,
1536        cb_info,
1537        &mut argc,
1538        raw_args.as_mut_ptr(),
1539        &mut raw_this,
1540        &mut data_ptr,
1541      )
1542    };
1543    unsafe { raw_args.set_len(argc) };
1544    debug_assert!(
1545      Status::from(status) == Status::Ok,
1546      "napi_get_cb_info failed"
1547    );
1548
1549    let closure_data_ptr = unsafe { *(data_ptr as *mut PropertyClosures) }.setter_closure;
1550    (raw_args, raw_this, closure_data_ptr)
1551  };
1552
1553  let closure: &F = Box::leak(unsafe { Box::from_raw(closure_data_ptr.cast()) });
1554  let env = unsafe { Env::from_raw(raw_env) };
1555  raw_args
1556    .first()
1557    .ok_or_else(|| Error::new(Status::InvalidArg, "Missing argument in property setter"))
1558    .and_then(|value| unsafe { V::from_napi_value(raw_env, *value) })
1559    .and_then(|value| {
1560      closure(
1561        env,
1562        unsafe { Object::from_raw_unchecked(raw_env, raw_this) },
1563        value,
1564      )
1565    })
1566    .map(|_| std::ptr::null_mut())
1567    .unwrap_or_else(|e| {
1568      unsafe { JsError::from(e).throw_into(raw_env) };
1569      ptr::null_mut()
1570    })
1571}
1572
1573#[cfg(feature = "napi5")]
1574pub(crate) unsafe extern "C" fn trampoline_getter<
1575  R: ToNapiValue,
1576  F: Fn(Env, crate::bindgen_runtime::This) -> Result<R>,
1577>(
1578  raw_env: sys::napi_env,
1579  cb_info: sys::napi_callback_info,
1580) -> sys::napi_value {
1581  let (raw_this, closure_data_ptr) = {
1582    let mut raw_this = ptr::null_mut();
1583    let mut data_ptr = ptr::null_mut();
1584
1585    let status = unsafe {
1586      sys::napi_get_cb_info(
1587        raw_env,
1588        cb_info,
1589        &mut 0,
1590        ptr::null_mut(),
1591        &mut raw_this,
1592        &mut data_ptr,
1593      )
1594    };
1595    debug_assert!(
1596      Status::from(status) == Status::Ok,
1597      "napi_get_cb_info failed"
1598    );
1599
1600    let closure_data_ptr = unsafe { *(data_ptr as *mut PropertyClosures) }.getter_closure;
1601    (raw_this, closure_data_ptr)
1602  };
1603
1604  let closure: &F = Box::leak(unsafe { Box::from_raw(closure_data_ptr.cast()) });
1605  let env = unsafe { Env::from_raw(raw_env) };
1606  closure(env, unsafe {
1607    crate::bindgen_runtime::Object::from_raw_unchecked(raw_env, raw_this)
1608  })
1609  .and_then(|ret: R| unsafe { <R as ToNapiValue>::to_napi_value(env.0, ret) })
1610  .unwrap_or_else(|e| {
1611    unsafe { JsError::from(e).throw_into(raw_env) };
1612    ptr::null_mut()
1613  })
1614}
1615
1616#[cfg(feature = "napi5")]
1617pub(crate) unsafe extern "C" fn finalize_box_trampoline<F>(
1618  _raw_env: sys::napi_env,
1619  closure_data_ptr: *mut c_void,
1620  _finalize_hint: *mut c_void,
1621) {
1622  drop(unsafe { Box::<F>::from_raw(closure_data_ptr.cast()) })
1623}