Skip to main content

napi/bindgen_runtime/js_values/
object.rs

1use std::any::{type_name, TypeId};
2#[cfg(feature = "napi6")]
3use std::convert::TryFrom;
4use std::ffi::{c_char, c_void, CStr, CString};
5use std::marker::PhantomData;
6use std::ptr;
7
8use crate::{
9  bindgen_prelude::*, check_status, raw_finalize, sys, type_of, Callback, TaggedObject, Value,
10};
11#[cfg(feature = "napi5")]
12use crate::{Env, PropertyClosures};
13
14pub trait JsObjectValue<'env>: JsValue<'env> {
15  /// Set the property value to the `Object`
16  fn set_property<'k, 'v, K, V>(&mut self, key: K, value: V) -> Result<()>
17  where
18    K: JsValue<'k>,
19    V: JsValue<'v>,
20  {
21    let env = self.value().env;
22    check_status!(unsafe {
23      sys::napi_set_property(env, self.value().value, key.raw(), value.raw())
24    })
25  }
26
27  /// Get the property value from the `Object`
28  ///
29  /// Return the `InvalidArg` error if the property is not `T`
30  fn get_property<'k, K, T>(&self, key: K) -> Result<T>
31  where
32    K: JsValue<'k>,
33    T: FromNapiValue + ValidateNapiValue,
34  {
35    let mut raw_value = ptr::null_mut();
36    let env = self.value().env;
37    check_status!(unsafe {
38      sys::napi_get_property(env, self.value().value, key.raw(), &mut raw_value)
39    })?;
40    unsafe { T::validate(env, raw_value) }.map_err(|mut err| {
41      err.reason = format!(
42        "Object property '{:?}' type mismatch. {}",
43        key
44          .coerce_to_string()
45          .and_then(|s| s.into_utf8())
46          .and_then(|s| s.into_owned()),
47        err.reason
48      );
49      err
50    })?;
51    unsafe { T::from_napi_value(env, raw_value) }
52  }
53
54  /// Get the property value from the `Object` without validation
55  fn get_property_unchecked<'k, K, T>(&self, key: K) -> Result<T>
56  where
57    K: JsValue<'k>,
58    T: FromNapiValue,
59  {
60    let mut raw_value = ptr::null_mut();
61    let env = self.value().env;
62    check_status!(unsafe {
63      sys::napi_get_property(env, self.value().value, key.raw(), &mut raw_value)
64    })?;
65    unsafe { T::from_napi_value(env, raw_value) }
66  }
67
68  /// Set the property value to the `Object`
69  fn set_named_property<T>(&mut self, name: &str, value: T) -> Result<()>
70  where
71    T: ToNapiValue,
72  {
73    let key = CString::new(name)?;
74    let env = self.value().env;
75    check_status!(unsafe {
76      sys::napi_set_named_property(env, self.raw(), key.as_ptr(), T::to_napi_value(env, value)?)
77    })
78  }
79
80  /// Set the property value to the `Object`, the property name is a `CStr`
81  /// This is useful when the property name comes from a `C` library
82  fn set_c_named_property<T>(&mut self, name: &CStr, value: T) -> Result<()>
83  where
84    T: ToNapiValue,
85  {
86    let env = self.value().env;
87    check_status!(unsafe {
88      sys::napi_set_named_property(
89        env,
90        self.raw(),
91        name.as_ptr(),
92        T::to_napi_value(env, value)?,
93      )
94    })
95  }
96
97  /// Create a named method on the `Object`
98  fn create_named_method<K>(&mut self, name: K, function: Callback) -> Result<()>
99  where
100    K: AsRef<str>,
101  {
102    let mut js_function = ptr::null_mut();
103    let len = name.as_ref().len();
104    let name = CString::new(name.as_ref())?;
105    let env = self.value().env;
106    check_status!(unsafe {
107      sys::napi_create_function(
108        env,
109        name.as_ptr(),
110        len as isize,
111        Some(function),
112        ptr::null_mut(),
113        &mut js_function,
114      )
115    })?;
116    check_status!(
117      unsafe { sys::napi_set_named_property(env, self.value().value, name.as_ptr(), js_function) },
118      "create_named_method error"
119    )
120  }
121
122  /// Create a named method on the `Object`, the name is a `CStr`
123  /// This is useful when the method name comes from a `C` library
124  fn create_c_named_method(&mut self, name: &CStr, function: Callback) -> Result<()> {
125    let mut js_function = ptr::null_mut();
126    let len = name.count_bytes();
127    let env = self.value().env;
128    check_status!(unsafe {
129      sys::napi_create_function(
130        env,
131        name.as_ptr(),
132        len as isize,
133        Some(function),
134        ptr::null_mut(),
135        &mut js_function,
136      )
137    })?;
138    check_status!(
139      unsafe { sys::napi_set_named_property(env, self.value().value, name.as_ptr(), js_function) },
140      "create_named_method error"
141    )
142  }
143
144  /// Get the property value from the `Object`
145  ///
146  /// Return the `InvalidArg` error if the property is not `T`
147  fn get_named_property<T>(&self, name: &str) -> Result<T>
148  where
149    T: FromNapiValue + ValidateNapiValue,
150  {
151    let key = CString::new(name)?;
152    let mut raw_value = ptr::null_mut();
153    let env = self.value().env;
154    check_status!(
155      unsafe {
156        sys::napi_get_named_property(env, self.value().value, key.as_ptr(), &mut raw_value)
157      },
158      "get_named_property error"
159    )?;
160    unsafe { <T as ValidateNapiValue>::validate(env, raw_value) }.map_err(|mut err| {
161      err.reason = format!("Object property '{name}' type mismatch. {}", err.reason);
162      err
163    })?;
164    unsafe { <T as FromNapiValue>::from_napi_value(env, raw_value) }
165  }
166
167  /// Get the property value from the `Object`
168  ///
169  /// Return the `InvalidArg` error if the property is not `T`
170  ///
171  /// This is useful when the property name comes from a `C` library
172  fn get_c_named_property<T>(&self, name: &CStr) -> Result<T>
173  where
174    T: FromNapiValue + ValidateNapiValue,
175  {
176    let mut raw_value = ptr::null_mut();
177    let env = self.value().env;
178    check_status!(
179      unsafe {
180        sys::napi_get_named_property(env, self.value().value, name.as_ptr(), &mut raw_value)
181      },
182      "get_named_property error"
183    )?;
184    unsafe { <T as ValidateNapiValue>::validate(env, raw_value) }.map_err(|mut err| {
185      err.reason = format!(
186        "Object property '{}' type mismatch. {}",
187        name.to_string_lossy(),
188        err.reason
189      );
190      err
191    })?;
192    unsafe { <T as FromNapiValue>::from_napi_value(env, raw_value) }
193  }
194
195  /// Get the property value from the `Object` without validation
196  fn get_named_property_unchecked<T>(&self, name: &str) -> Result<T>
197  where
198    T: FromNapiValue,
199  {
200    let key = CString::new(name)?;
201    let mut raw_value = ptr::null_mut();
202    let env = self.value().env;
203    check_status!(
204      unsafe {
205        sys::napi_get_named_property(env, self.value().value, key.as_ptr(), &mut raw_value)
206      },
207      "get_named_property_unchecked error"
208    )?;
209    unsafe { <T as FromNapiValue>::from_napi_value(env, raw_value) }
210  }
211
212  /// Get the property value from the `Object` without validation
213  ///
214  /// This is useful when the property name comes from a `C` library
215  fn get_c_named_property_unchecked<T>(&self, name: &CStr) -> Result<T>
216  where
217    T: FromNapiValue,
218  {
219    let mut raw_value = ptr::null_mut();
220    let env = self.value().env;
221    check_status!(
222      unsafe {
223        sys::napi_get_named_property(env, self.value().value, name.as_ptr(), &mut raw_value)
224      },
225      "get_c_named_property_unchecked error"
226    )?;
227    unsafe { <T as FromNapiValue>::from_napi_value(env, raw_value) }
228  }
229
230  /// Check if the `Object` has the named property
231  fn has_named_property<N: AsRef<str>>(&self, name: N) -> Result<bool> {
232    let mut result = false;
233    let key = CString::new(name.as_ref())?;
234    let env = self.value().env;
235    check_status!(
236      unsafe { sys::napi_has_named_property(env, self.value().value, key.as_ptr(), &mut result) },
237      "has_named_property error"
238    )?;
239    Ok(result)
240  }
241
242  /// Check if the `Object` has the named property
243  ///
244  /// This is useful when the property name comes from a `C` library
245  fn has_c_named_property(&self, name: &CStr) -> Result<bool> {
246    let mut result = false;
247    let env = self.value().env;
248    check_status!(
249      unsafe { sys::napi_has_named_property(env, self.value().value, name.as_ptr(), &mut result) },
250      "has_c_named_property error"
251    )?;
252    Ok(result)
253  }
254
255  /// Delete the property from the `Object`, the property name can be a `JsValue`
256  fn delete_property<'s, S>(&mut self, name: S) -> Result<bool>
257  where
258    S: JsValue<'s>,
259  {
260    let mut result = false;
261    let env = self.value().env;
262    check_status!(unsafe {
263      sys::napi_delete_property(env, self.value().value, name.raw(), &mut result)
264    })?;
265    Ok(result)
266  }
267
268  /// Delete the property from the `Object`
269  fn delete_named_property<K: AsRef<str>>(&mut self, name: K) -> Result<bool> {
270    let name = name.as_ref();
271    let mut result = false;
272    let mut js_key = ptr::null_mut();
273    let env = self.value().env;
274    check_status!(unsafe {
275      sys::napi_create_string_utf8(env, name.as_ptr().cast(), name.len() as isize, &mut js_key)
276    })?;
277    check_status!(unsafe {
278      sys::napi_delete_property(env, self.value().value, js_key, &mut result)
279    })?;
280    Ok(result)
281  }
282
283  /// Delete the property from the `Object`
284  ///
285  /// This is useful when the property name comes from a `C` library
286  fn delete_c_named_property(&mut self, name: &CStr) -> Result<bool> {
287    let mut result = false;
288    let mut js_key = ptr::null_mut();
289    let env = self.value().env;
290    check_status!(unsafe {
291      sys::napi_create_string_utf8(env, name.as_ptr(), name.count_bytes() as isize, &mut js_key)
292    })?;
293    check_status!(unsafe {
294      sys::napi_delete_property(env, self.value().value, js_key, &mut result)
295    })?;
296    Ok(result)
297  }
298
299  /// Check if the `Object` has the own property
300  fn has_own_property(&self, key: &str) -> Result<bool> {
301    let mut result = false;
302    let mut js_key = ptr::null_mut();
303    let env = self.value().env;
304    check_status!(unsafe {
305      sys::napi_create_string_utf8(env, key.as_ptr().cast(), key.len() as isize, &mut js_key)
306    })?;
307    check_status!(unsafe {
308      sys::napi_has_own_property(env, self.value().value, js_key, &mut result)
309    })?;
310    Ok(result)
311  }
312
313  /// Check if the `Object` has the own property
314  ///
315  /// This is useful when the property name comes from a `C` library
316  fn has_c_own_property(&self, key: &CStr) -> Result<bool> {
317    let mut result = false;
318    let mut js_key = ptr::null_mut();
319    let env = self.value().env;
320    check_status!(unsafe {
321      sys::napi_create_string_utf8(env, key.as_ptr(), key.count_bytes() as isize, &mut js_key)
322    })?;
323    check_status!(unsafe {
324      sys::napi_has_own_property(env, self.value().value, js_key, &mut result)
325    })?;
326    Ok(result)
327  }
328
329  /// The same as `has_own_property`, but accepts a `JsValue` as the property name.
330  fn has_own_property_js<'k, K>(&self, key: K) -> Result<bool>
331  where
332    K: JsValue<'k>,
333  {
334    let mut result = false;
335    let env = self.value().env;
336    check_status!(unsafe {
337      sys::napi_has_own_property(env, self.value().value, key.raw(), &mut result)
338    })?;
339    Ok(result)
340  }
341
342  /// This API checks if the Object passed in has the named property.
343  fn has_property(&self, name: &str) -> Result<bool> {
344    let mut js_key = ptr::null_mut();
345    let mut result = false;
346    let env = self.value().env;
347    check_status!(unsafe {
348      sys::napi_create_string_utf8(env, name.as_ptr().cast(), name.len() as isize, &mut js_key)
349    })?;
350    check_status!(unsafe { sys::napi_has_property(env, self.value().value, js_key, &mut result) })?;
351    Ok(result)
352  }
353
354  /// This API is the same as `has_property`, but accepts a `JsValue` as the property name.
355  /// So you can pass the `JsNumber` or `JsSymbol` as the property name.
356  fn has_property_js<'k, K>(&self, name: K) -> Result<bool>
357  where
358    K: JsValue<'k>,
359  {
360    let mut result = false;
361    let env = self.value().env;
362    check_status!(unsafe {
363      sys::napi_has_property(env, self.value().value, name.raw(), &mut result)
364    })?;
365    Ok(result)
366  }
367
368  /// This API returns the names of the enumerable properties of object as an array of strings.
369  /// The properties of object whose key is a symbol will not be included.
370  fn get_property_names(&self) -> Result<Object<'env>> {
371    let mut raw_value = ptr::null_mut();
372    let env = self.value().env;
373    check_status!(unsafe {
374      sys::napi_get_property_names(env, self.value().value, &mut raw_value)
375    })?;
376    Ok(Object::from_raw(env, raw_value))
377  }
378
379  #[cfg(feature = "napi6")]
380  /// <https://nodejs.org/api/n-api.html#n_api_napi_get_all_property_names>
381  /// This API returns an array containing the names of the available properties of this object.
382  fn get_all_property_names(
383    &self,
384    mode: KeyCollectionMode,
385    filter: KeyFilter,
386    conversion: KeyConversion,
387  ) -> Result<Object<'env>> {
388    let mut properties_value = ptr::null_mut();
389    let env = self.value().env;
390    check_status!(unsafe {
391      sys::napi_get_all_property_names(
392        env,
393        self.value().value,
394        mode.into(),
395        filter.into(),
396        conversion.into(),
397        &mut properties_value,
398      )
399    })?;
400    Ok(Object::from_raw(env, properties_value))
401  }
402
403  /// This returns the equivalent of `Object.getPrototypeOf` (which is not the same as the function's prototype property).
404  fn get_prototype(&self) -> Result<Unknown<'env>> {
405    let mut result = ptr::null_mut();
406    let env = self.value().env;
407    check_status!(unsafe { sys::napi_get_prototype(env, self.value().value, &mut result) })?;
408    Ok(unsafe { Unknown::from_raw_unchecked(env, result) })
409  }
410
411  /// Get the prototype of the `Object`
412  fn get_prototype_unchecked<T>(&self) -> Result<T>
413  where
414    T: FromNapiValue,
415  {
416    let mut result = ptr::null_mut();
417    let env = self.value().env;
418    check_status!(unsafe { sys::napi_get_prototype(env, self.value().value, &mut result) })?;
419    unsafe { T::from_napi_value(env, result) }
420  }
421
422  /// Set the element at the given index
423  fn set_element<'t, T>(&mut self, index: u32, value: T) -> Result<()>
424  where
425    T: JsValue<'t>,
426  {
427    let env = self.value().env;
428    check_status!(unsafe { sys::napi_set_element(env, self.value().value, index, value.raw()) })
429  }
430
431  /// Check if the `Array` has the element at the given index
432  fn has_element(&self, index: u32) -> Result<bool> {
433    let mut result = false;
434    let env = self.value().env;
435    check_status!(unsafe { sys::napi_has_element(env, self.value().value, index, &mut result) })?;
436    Ok(result)
437  }
438
439  /// Delete the element at the given index
440  fn delete_element(&mut self, index: u32) -> Result<bool> {
441    let mut result = false;
442    let env = self.value().env;
443    check_status!(unsafe {
444      sys::napi_delete_element(env, self.value().value, index, &mut result)
445    })?;
446    Ok(result)
447  }
448
449  /// Get the element at the given index
450  ///
451  /// If the `Object` is not an array, `ArrayExpected` error returned
452  fn get_element<T>(&self, index: u32) -> Result<T>
453  where
454    T: FromNapiValue,
455  {
456    let mut raw_value = ptr::null_mut();
457    let env = self.value().env;
458    check_status!(unsafe {
459      sys::napi_get_element(env, self.value().value, index, &mut raw_value)
460    })?;
461    unsafe { T::from_napi_value(env, raw_value) }
462  }
463
464  /// This method allows the efficient definition of multiple properties on a given object.
465  fn define_properties(&mut self, properties: &[Property]) -> Result<()> {
466    let property_descriptors = properties
467      .iter()
468      .map(|property| property.raw())
469      .collect::<Vec<sys::napi_property_descriptor>>();
470    let env = self.value().env;
471    #[cfg(feature = "napi5")]
472    {
473      if !properties.is_empty() {
474        let mut closures = properties
475          .iter()
476          .zip(property_descriptors.iter())
477          .filter(|(property, _)| property.has_closure_data())
478          .map(|(_, descriptor)| descriptor.data)
479          .filter(|data| !data.is_null())
480          .collect::<Vec<*mut std::ffi::c_void>>();
481        if !closures.is_empty() {
482          let finalize_hint = Box::into_raw(Box::new((closures.len(), closures.capacity())));
483          check_status!(
484            unsafe {
485              sys::napi_add_finalizer(
486                env,
487                self.value().value,
488                closures.as_mut_ptr().cast(),
489                Some(finalize_closures),
490                finalize_hint.cast(),
491                ptr::null_mut(),
492              )
493            },
494            "Failed to add finalizer"
495          )?;
496          std::mem::forget(closures);
497        }
498      }
499    }
500    check_status!(unsafe {
501      sys::napi_define_properties(
502        env,
503        self.value().value,
504        properties.len(),
505        property_descriptors.as_ptr(),
506      )
507    })
508  }
509
510  /// Perform `is_array` check before get the length
511  ///
512  /// if `Object` is not array, `ArrayExpected` error returned
513  fn get_array_length(&self) -> Result<u32> {
514    if !(self.is_array()?) {
515      return Err(Error::new(
516        Status::ArrayExpected,
517        "Object is not array".to_owned(),
518      ));
519    }
520    self.get_array_length_unchecked()
521  }
522
523  /// use this API if you can ensure this `Object` is `Array`
524  fn get_array_length_unchecked(&self) -> Result<u32> {
525    let mut length: u32 = 0;
526    let env = self.value().env;
527    check_status!(unsafe { sys::napi_get_array_length(env, self.value().value, &mut length) })?;
528    Ok(length)
529  }
530
531  /// Wrap the native value `T` to this `Object`
532  /// the `T` will be dropped when this `Object` is finalized
533  fn wrap<T: 'static>(&mut self, native_object: T, size_hint: Option<usize>) -> Result<()> {
534    let env = self.value().env;
535    let value = self.raw();
536    check_status!(unsafe {
537      sys::napi_wrap(
538        env,
539        value,
540        Box::into_raw(Box::new(TaggedObject::new(native_object))).cast(),
541        Some(raw_finalize::<TaggedObject<T>>),
542        Box::into_raw(Box::new(size_hint.unwrap_or(0) as i64)).cast(),
543        ptr::null_mut(),
544      )
545    })
546  }
547
548  /// Get the wrapped native value from the `Object`
549  ///
550  /// Return the `InvalidArg` error if the `Object` is not wrapped the `T`
551  #[allow(clippy::mut_from_ref)]
552  fn unwrap<T: 'static>(&self) -> Result<&mut T> {
553    let env = self.value().env;
554    let value = self.raw();
555    unsafe {
556      let mut unknown_tagged_object: *mut c_void = ptr::null_mut();
557      check_status!(
558        sys::napi_unwrap(env, value, &mut unknown_tagged_object),
559        "Failed to unwrap value of the Object"
560      )?;
561
562      let type_id = unknown_tagged_object as *const TypeId;
563      if *type_id == TypeId::of::<T>() {
564        let tagged_object = unknown_tagged_object as *mut TaggedObject<T>;
565        (*tagged_object).object.as_mut().ok_or_else(|| {
566          Error::new(
567            Status::InvalidArg,
568            "Invalid argument, nothing attach to js_object".to_owned(),
569          )
570        })
571      } else {
572        Err(Error::new(
573          Status::InvalidArg,
574          format!(
575            "Invalid argument, {} on unwrap is not the type of wrapped object",
576            type_name::<T>()
577          ),
578        ))
579      }
580    }
581  }
582
583  /// Remove the wrapped native value from the `Object`
584  ///
585  /// Return the `InvalidArg` error if the `Object` is not wrapped the `T`
586  fn remove_wrapped<T: 'static>(&mut self) -> Result<()> {
587    let env = self.value().env;
588    let value = self.raw();
589    unsafe {
590      let mut unknown_tagged_object = ptr::null_mut();
591      check_status!(sys::napi_remove_wrap(
592        env,
593        value,
594        &mut unknown_tagged_object,
595      ))?;
596      let type_id = unknown_tagged_object as *const TypeId;
597      if *type_id == TypeId::of::<T>() {
598        drop(Box::from_raw(unknown_tagged_object as *mut TaggedObject<T>));
599        Ok(())
600      } else {
601        Err(Error::new(
602          Status::InvalidArg,
603          format!(
604            "Invalid argument, {} on unwrap is not the type of wrapped object",
605            type_name::<T>()
606          ),
607        ))
608      }
609    }
610  }
611
612  #[cfg(feature = "napi5")]
613  /// Adds a `finalize_cb` callback which will be called when the JavaScript object in js_object has been garbage-collected.
614  ///
615  /// This API can be called multiple times on a single JavaScript object.
616  fn add_finalizer<T, Hint, F>(
617    &mut self,
618    native: T,
619    finalize_hint: Hint,
620    finalize_cb: F,
621  ) -> Result<()>
622  where
623    T: 'static,
624    Hint: 'static,
625    F: FnOnce(FinalizeContext<T, Hint>) + 'static,
626  {
627    let mut maybe_ref = ptr::null_mut();
628    let env = self.value().env;
629    let value = self.raw();
630    let wrap_context = Box::leak(Box::new((native, finalize_cb, ptr::null_mut())));
631    check_status!(unsafe {
632      sys::napi_add_finalizer(
633        env,
634        value,
635        (wrap_context as *mut (T, F, sys::napi_ref)).cast(),
636        Some(finalize_callback::<T, Hint, F>),
637        Box::into_raw(Box::new(finalize_hint)).cast(),
638        &mut maybe_ref, // Note: this does not point to the boxed one…
639      )
640    })?;
641    wrap_context.2 = maybe_ref;
642    Ok(())
643  }
644
645  #[cfg(feature = "napi8")]
646  /// This method freezes a given object.
647  /// This prevents new properties from being added to it, existing properties from being removed, prevents changing the enumerability, configurability, or writability of existing properties, and prevents the values of existing properties from being changed.
648  /// It also prevents the object's prototype from being changed. This is described in [Section 19.1.2.6](https://tc39.es/ecma262/#sec-object.freeze) of the ECMA-262 specification.
649  fn freeze(&mut self) -> Result<()> {
650    let env = self.value().env;
651    check_status!(unsafe { sys::napi_object_freeze(env, self.value().value) })
652  }
653
654  #[cfg(feature = "napi8")]
655  /// This method seals a given object. This prevents new properties from being added to it, as well as marking all existing properties as non-configurable.
656  /// This is described in [Section 19.1.2.20](https://tc39.es/ecma262/#sec-object.seal) of the ECMA-262 specification.
657  fn seal(&mut self) -> Result<()> {
658    let env = self.value().env;
659    check_status!(unsafe { sys::napi_object_seal(env, self.value().value) })
660  }
661}
662
663#[derive(Clone, Copy)]
664pub struct Object<'env>(pub(crate) Value, pub(crate) PhantomData<&'env ()>);
665
666impl<'env> JsValue<'env> for Object<'env> {
667  fn value(&self) -> Value {
668    self.0
669  }
670}
671
672impl<'env> JsObjectValue<'env> for Object<'env> {}
673
674impl TypeName for Object<'_> {
675  fn type_name() -> &'static str {
676    "Object"
677  }
678
679  fn value_type() -> ValueType {
680    ValueType::Object
681  }
682}
683
684impl ValidateNapiValue for Object<'_> {}
685
686impl FromNapiValue for Object<'_> {
687  unsafe fn from_napi_value(env: sys::napi_env, napi_val: sys::napi_value) -> Result<Self> {
688    Ok(Self(
689      Value {
690        env,
691        value: napi_val,
692        value_type: ValueType::Object,
693      },
694      PhantomData,
695    ))
696  }
697}
698
699impl ToNapiValue for &Object<'_> {
700  unsafe fn to_napi_value(_env: sys::napi_env, val: Self) -> Result<sys::napi_value> {
701    Ok(val.0.value)
702  }
703}
704
705/// # Safety
706///
707/// The caller must ensure that `env` and `obj` are valid N-API handles, and
708/// `field` points to a valid nul-terminated C string.
709#[doc(hidden)]
710pub unsafe fn get_named_property_raw(
711  env: sys::napi_env,
712  obj: sys::napi_value,
713  field: *const c_char,
714) -> Result<Option<sys::napi_value>> {
715  let mut ret = ptr::null_mut();
716
717  check_status!(
718    unsafe { sys::napi_get_named_property(env, obj, field, &mut ret) },
719    "Failed to get property",
720  )?;
721
722  let ty = type_of!(env, ret)?;
723
724  Ok(if ty == ValueType::Undefined {
725    None
726  } else {
727    Some(ret)
728  })
729}
730
731/// # Safety
732///
733/// The caller must ensure that `env`, `obj`, and `value` are valid N-API
734/// handles, and `field` points to a valid nul-terminated C string.
735#[doc(hidden)]
736pub unsafe fn set_named_property_raw(
737  env: sys::napi_env,
738  obj: sys::napi_value,
739  field: *const c_char,
740  value: sys::napi_value,
741) -> Result<()> {
742  check_status!(
743    unsafe { sys::napi_set_named_property(env, obj, field, value) },
744    "Failed to set property"
745  )
746}
747
748#[doc(hidden)]
749#[inline]
750pub unsafe fn from_raw_required_field<T>(
751  env: sys::napi_env,
752  raw: Option<sys::napi_value>,
753  struct_name: &str,
754  field: &str,
755) -> Result<T>
756where
757  T: FromNapiValue,
758{
759  let Some(raw) = raw else {
760    return Err(crate::missing_field_error(field));
761  };
762
763  unsafe { T::from_napi_value(env, raw) }.map_err(|err| {
764    if struct_name.is_empty() {
765      err
766    } else {
767      crate::decorate_field_error(err, struct_name, field)
768    }
769  })
770}
771
772#[doc(hidden)]
773#[inline]
774pub unsafe fn from_raw_optional_field<T>(
775  env: sys::napi_env,
776  raw: Option<sys::napi_value>,
777  struct_name: &str,
778  field: &str,
779) -> Result<Option<T>>
780where
781  T: FromNapiValue,
782{
783  match raw {
784    Some(raw) => unsafe { T::from_napi_value(env, raw) }
785      .map(Some)
786      .map_err(|err| {
787        if struct_name.is_empty() {
788          err
789        } else {
790          crate::decorate_field_error(err, struct_name, field)
791        }
792      }),
793    None => Ok(None),
794  }
795}
796
797impl Object<'_> {
798  /// create a new `Object` from raw values
799  pub fn from_raw(env: sys::napi_env, value: sys::napi_value) -> Self {
800    Self(
801      Value {
802        env,
803        value,
804        value_type: ValueType::Object,
805      },
806      PhantomData,
807    )
808  }
809
810  /// create a new `Object` from a `Env`
811  pub fn new(env: &Env) -> Result<Self> {
812    let mut ptr = ptr::null_mut();
813    unsafe {
814      check_status!(
815        sys::napi_create_object(env.0, &mut ptr),
816        "Failed to create napi Object"
817      )?;
818    }
819
820    Ok(Self(
821      crate::Value {
822        env: env.0,
823        value: ptr,
824        value_type: ValueType::Object,
825      },
826      PhantomData,
827    ))
828  }
829
830  /// Get the property value from the `Object`, if the property is not found, `None` is returned
831  pub fn get<V: FromNapiValue>(&self, field: &str) -> Result<Option<V>> {
832    unsafe {
833      self
834        .get_inner(field)?
835        .map(|v| V::from_napi_value(self.0.env, v))
836        .transpose()
837    }
838  }
839
840  fn get_inner(&self, field: &str) -> Result<Option<sys::napi_value>> {
841    unsafe {
842      let mut property_key = std::ptr::null_mut();
843      check_status!(
844        sys::napi_create_string_utf8(
845          self.0.env,
846          field.as_ptr().cast(),
847          field.len() as isize,
848          &mut property_key,
849        ),
850        "Failed to create property key with `{field}`"
851      )?;
852
853      let mut ret = ptr::null_mut();
854
855      check_status!(
856        sys::napi_get_property(self.0.env, self.0.value, property_key, &mut ret),
857        "Failed to get property with field `{field}`",
858      )?;
859
860      let ty = type_of!(self.0.env, ret)?;
861
862      Ok(if ty == ValueType::Undefined {
863        None
864      } else {
865        Some(ret)
866      })
867    }
868  }
869
870  /// Set the property value to the `Object`
871  pub fn set<K: AsRef<str>, V: ToNapiValue>(&mut self, field: K, val: V) -> Result<()> {
872    unsafe { self.set_inner(field.as_ref(), V::to_napi_value(self.0.env, val)?) }
873  }
874
875  unsafe fn set_inner(&mut self, field: &str, napi_val: sys::napi_value) -> Result<()> {
876    let mut property_key = std::ptr::null_mut();
877    check_status!(
878      unsafe {
879        sys::napi_create_string_utf8(
880          self.0.env,
881          field.as_ptr().cast(),
882          field.len() as isize,
883          &mut property_key,
884        )
885      },
886      "Failed to create property key with `{field}`"
887    )?;
888
889    check_status!(
890      unsafe { sys::napi_set_property(self.0.env, self.0.value, property_key, napi_val) },
891      "Failed to set property with field `{field}`"
892    )?;
893    Ok(())
894  }
895
896  /// Get the string keys of the `Object`
897  pub fn keys(obj: &Object) -> Result<Vec<String>> {
898    let mut names = ptr::null_mut();
899    unsafe {
900      check_status!(
901        sys::napi_get_property_names(obj.0.env, obj.0.value, &mut names),
902        "Failed to get property names of given object"
903      )?;
904    }
905
906    let names = unsafe { Array::from_napi_value(obj.0.env, names)? };
907    let mut ret = vec![];
908
909    for i in 0..names.len() {
910      ret.push(names.get_element::<String>(i)?);
911    }
912
913    Ok(ret)
914  }
915
916  /// Create a reference to the object.
917  ///
918  /// Set the `LEAK_CHECK` to `false` to disable the leak check during the `Drop`
919  pub fn create_ref<const LEAK_CHECK: bool>(&self) -> Result<ObjectRef<LEAK_CHECK>> {
920    let mut ref_ = ptr::null_mut();
921    check_status!(
922      unsafe { sys::napi_create_reference(self.0.env, self.0.value, 1, &mut ref_) },
923      "Failed to create reference"
924    )?;
925    Ok(ObjectRef { inner: ref_ })
926  }
927}
928
929/// A reference to a JavaScript object.
930///
931/// You must call the `unref` method to release the reference, or the object under the hood will be leaked forever.
932///
933/// Set the `LEAK_CHECK` to `false` to disable the leak check during the `Drop`
934pub struct ObjectRef<const LEAK_CHECK: bool = true> {
935  pub(crate) inner: sys::napi_ref,
936}
937
938unsafe impl<const LEAK_CHECK: bool> Send for ObjectRef<LEAK_CHECK> {}
939
940impl<const LEAK_CHECK: bool> Drop for ObjectRef<LEAK_CHECK> {
941  fn drop(&mut self) {
942    if LEAK_CHECK && !self.inner.is_null() {
943      eprintln!("ObjectRef is not unref, it considered as a memory leak");
944    }
945  }
946}
947
948impl<const LEAK_CHECK: bool> ObjectRef<LEAK_CHECK> {
949  /// Get the object from the reference
950  pub fn get_value<'env>(&self, env: &'env Env) -> Result<Object<'env>> {
951    let mut result = ptr::null_mut();
952    check_status!(
953      unsafe { sys::napi_get_reference_value(env.0, self.inner, &mut result) },
954      "Failed to get reference value"
955    )?;
956    Ok(Object::from_raw(env.0, result))
957  }
958
959  /// Unref the reference
960  pub fn unref(mut self, env: &Env) -> Result<()> {
961    check_status!(
962      unsafe { sys::napi_delete_reference(env.0, self.inner) },
963      "delete Ref failed"
964    )?;
965    self.inner = ptr::null_mut();
966    Ok(())
967  }
968}
969
970impl<const LEAK_CHECK: bool> FromNapiValue for ObjectRef<LEAK_CHECK> {
971  unsafe fn from_napi_value(env: sys::napi_env, napi_val: sys::napi_value) -> Result<Self> {
972    let mut ref_ = ptr::null_mut();
973    check_status!(
974      unsafe { sys::napi_create_reference(env, napi_val, 1, &mut ref_) },
975      "Failed to create reference"
976    )?;
977    Ok(Self { inner: ref_ })
978  }
979}
980
981impl<const LEAK_CHECK: bool> ToNapiValue for &ObjectRef<LEAK_CHECK> {
982  unsafe fn to_napi_value(env: sys::napi_env, val: Self) -> Result<sys::napi_value> {
983    let mut result = ptr::null_mut();
984    check_status!(
985      unsafe { sys::napi_get_reference_value(env, val.inner, &mut result) },
986      "Failed to get reference value"
987    )?;
988    Ok(result)
989  }
990}
991
992impl<const LEAK_CHECK: bool> ToNapiValue for ObjectRef<LEAK_CHECK> {
993  unsafe fn to_napi_value(env: sys::napi_env, mut val: Self) -> Result<sys::napi_value> {
994    let mut result = ptr::null_mut();
995    check_status!(
996      unsafe { sys::napi_get_reference_value(env, val.inner, &mut result) },
997      "Failed to get reference value"
998    )?;
999    check_status!(
1000      unsafe { sys::napi_delete_reference(env, val.inner) },
1001      "delete Ref failed"
1002    )?;
1003    val.inner = ptr::null_mut();
1004    drop(val);
1005    Ok(result)
1006  }
1007}
1008
1009#[cfg(feature = "napi5")]
1010pub struct FinalizeContext<T: 'static, Hint: 'static> {
1011  pub env: Env,
1012  pub value: T,
1013  pub hint: Hint,
1014}
1015
1016#[cfg(feature = "napi6")]
1017pub enum KeyCollectionMode {
1018  IncludePrototypes,
1019  OwnOnly,
1020}
1021
1022#[cfg(feature = "napi6")]
1023impl TryFrom<sys::napi_key_collection_mode> for KeyCollectionMode {
1024  type Error = Error;
1025
1026  fn try_from(value: sys::napi_key_collection_mode) -> Result<Self> {
1027    match value {
1028      sys::KeyCollectionMode::include_prototypes => Ok(Self::IncludePrototypes),
1029      sys::KeyCollectionMode::own_only => Ok(Self::OwnOnly),
1030      _ => Err(Error::new(
1031        crate::Status::InvalidArg,
1032        format!("Invalid key collection mode: {value}"),
1033      )),
1034    }
1035  }
1036}
1037
1038#[cfg(feature = "napi6")]
1039impl From<KeyCollectionMode> for sys::napi_key_collection_mode {
1040  fn from(value: KeyCollectionMode) -> Self {
1041    match value {
1042      KeyCollectionMode::IncludePrototypes => sys::KeyCollectionMode::include_prototypes,
1043      KeyCollectionMode::OwnOnly => sys::KeyCollectionMode::own_only,
1044    }
1045  }
1046}
1047
1048#[cfg(feature = "napi6")]
1049pub enum KeyFilter {
1050  AllProperties,
1051  Writable,
1052  Enumerable,
1053  Configurable,
1054  SkipStrings,
1055  SkipSymbols,
1056}
1057
1058#[cfg(feature = "napi6")]
1059impl TryFrom<sys::napi_key_filter> for KeyFilter {
1060  type Error = Error;
1061
1062  fn try_from(value: sys::napi_key_filter) -> Result<Self> {
1063    match value {
1064      sys::KeyFilter::all_properties => Ok(Self::AllProperties),
1065      sys::KeyFilter::writable => Ok(Self::Writable),
1066      sys::KeyFilter::enumerable => Ok(Self::Enumerable),
1067      sys::KeyFilter::configurable => Ok(Self::Configurable),
1068      sys::KeyFilter::skip_strings => Ok(Self::SkipStrings),
1069      sys::KeyFilter::skip_symbols => Ok(Self::SkipSymbols),
1070      _ => Err(Error::new(
1071        crate::Status::InvalidArg,
1072        format!("Invalid key filter [{value}]"),
1073      )),
1074    }
1075  }
1076}
1077
1078#[cfg(feature = "napi6")]
1079impl From<KeyFilter> for sys::napi_key_filter {
1080  fn from(value: KeyFilter) -> Self {
1081    match value {
1082      KeyFilter::AllProperties => sys::KeyFilter::all_properties,
1083      KeyFilter::Writable => sys::KeyFilter::writable,
1084      KeyFilter::Enumerable => sys::KeyFilter::enumerable,
1085      KeyFilter::Configurable => sys::KeyFilter::configurable,
1086      KeyFilter::SkipStrings => sys::KeyFilter::skip_strings,
1087      KeyFilter::SkipSymbols => sys::KeyFilter::skip_symbols,
1088    }
1089  }
1090}
1091
1092#[cfg(feature = "napi6")]
1093pub enum KeyConversion {
1094  KeepNumbers,
1095  NumbersToStrings,
1096}
1097
1098#[cfg(feature = "napi6")]
1099impl TryFrom<sys::napi_key_conversion> for KeyConversion {
1100  type Error = Error;
1101
1102  fn try_from(value: sys::napi_key_conversion) -> Result<Self> {
1103    match value {
1104      sys::KeyConversion::keep_numbers => Ok(Self::KeepNumbers),
1105      sys::KeyConversion::numbers_to_strings => Ok(Self::NumbersToStrings),
1106      _ => Err(Error::new(
1107        crate::Status::InvalidArg,
1108        format!("Invalid key conversion [{value}]"),
1109      )),
1110    }
1111  }
1112}
1113
1114#[cfg(feature = "napi6")]
1115impl From<KeyConversion> for sys::napi_key_conversion {
1116  fn from(value: KeyConversion) -> Self {
1117    match value {
1118      KeyConversion::KeepNumbers => sys::KeyConversion::keep_numbers,
1119      KeyConversion::NumbersToStrings => sys::KeyConversion::numbers_to_strings,
1120    }
1121  }
1122}
1123
1124#[cfg(feature = "napi5")]
1125unsafe extern "C" fn finalize_callback<T, Hint, F>(
1126  raw_env: sys::napi_env,
1127  finalize_data: *mut c_void,
1128  finalize_hint: *mut c_void,
1129) where
1130  T: 'static,
1131  Hint: 'static,
1132  F: FnOnce(FinalizeContext<T, Hint>),
1133{
1134  use crate::Env;
1135
1136  let (value, callback, raw_ref) =
1137    unsafe { *Box::from_raw(finalize_data as *mut (T, F, sys::napi_ref)) };
1138  let hint = unsafe { *Box::from_raw(finalize_hint as *mut Hint) };
1139  let env = Env::from_raw(raw_env);
1140  callback(FinalizeContext { env, value, hint });
1141  if !raw_ref.is_null() {
1142    check_status_or_throw!(
1143      raw_env,
1144      unsafe { sys::napi_delete_reference(raw_env, raw_ref) },
1145      "Delete reference in finalize callback failed"
1146    );
1147  }
1148}
1149
1150#[cfg(feature = "napi5")]
1151pub(crate) unsafe extern "C" fn finalize_closures(
1152  _env: sys::napi_env,
1153  data: *mut c_void,
1154  len: *mut c_void,
1155) {
1156  let (length, capacity): (usize, usize) = *unsafe { Box::from_raw(len.cast()) };
1157  let closures: Vec<*mut PropertyClosures> =
1158    unsafe { Vec::from_raw_parts(data.cast(), length, capacity) };
1159  for closure_ptr in closures.into_iter() {
1160    if !closure_ptr.is_null() {
1161      let closures = unsafe { Box::from_raw(closure_ptr) };
1162      // Free the actual closure functions using the stored drop functions
1163      if !closures.getter_closure.is_null() {
1164        if let Some(drop_fn) = closures.getter_drop_fn {
1165          unsafe { drop_fn(closures.getter_closure) };
1166        }
1167      }
1168      if !closures.setter_closure.is_null() {
1169        if let Some(drop_fn) = closures.setter_drop_fn {
1170          unsafe { drop_fn(closures.setter_closure) };
1171        }
1172      }
1173    }
1174  }
1175}