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: *mut (usize, usize) = hint as *mut (usize, usize);
1368 let (length: usize, cap: usize) = unsafe { *Box::from_raw(length_ptr) };
1369 mem::drop(unsafe { Vec::from_raw_parts(ptr:finalize_data as *mut u8, length, capacity: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: *mut TaggedObject = finalize_data as *mut TaggedObject<T>;
1378 drop(unsafe { Box::from_raw(tagged_object) });
1379 if !finalize_hint.is_null() {
1380 let size_hint: Option = unsafe { *Box::from_raw(finalize_hint as *mut Option<i64>) };
1381 if let Some(changed: i64) = size_hint {
1382 if changed != 0 {
1383 let mut adjusted: i64 = 0i64;
1384 let status: i32 = 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: TaggedObject, callback: F) = unsafe { *Box::from_raw(finalize_data as *mut (TaggedObject<T>, F)) };
1405 let hint: Hint = unsafe { *Box::from_raw(finalize_hint as *mut Hint) };
1406 let env: 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: Box> = 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: Hint, callback: Finalize) = 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: Arg, callback: F) = unsafe { *Box::from_raw(data as *mut (Arg, F)) };
1440 callback(arg);
1441 if !handle.is_null() {
1442 let status: i32 = unsafe { sys::napi_remove_async_cleanup_hook(remove_handle: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}
1624