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_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 properties_iter = properties.iter().map(|property| property.raw());
467    let env = self.value().env;
468    #[cfg(feature = "napi5")]
469    {
470      if !properties.is_empty() {
471        let mut closures = properties_iter
472          .clone()
473          .map(|p| p.data)
474          .filter(|data| !data.is_null())
475          .collect::<Vec<*mut std::ffi::c_void>>();
476        if !closures.is_empty() {
477          let finalize_hint = Box::into_raw(Box::new((closures.len(), closures.capacity())));
478          check_status!(
479            unsafe {
480              sys::napi_add_finalizer(
481                env,
482                self.value().value,
483                closures.as_mut_ptr().cast(),
484                Some(finalize_closures),
485                finalize_hint.cast(),
486                ptr::null_mut(),
487              )
488            },
489            "Failed to add finalizer"
490          )?;
491          std::mem::forget(closures);
492        }
493      }
494    }
495    check_status!(unsafe {
496      sys::napi_define_properties(
497        env,
498        self.value().value,
499        properties.len(),
500        properties_iter
501          .collect::<Vec<sys::napi_property_descriptor>>()
502          .as_ptr(),
503      )
504    })
505  }
506
507  /// Perform `is_array` check before get the length
508  ///
509  /// if `Object` is not array, `ArrayExpected` error returned
510  fn get_array_length(&self) -> Result<u32> {
511    if !(self.is_array()?) {
512      return Err(Error::new(
513        Status::ArrayExpected,
514        "Object is not array".to_owned(),
515      ));
516    }
517    self.get_array_length_unchecked()
518  }
519
520  /// use this API if you can ensure this `Object` is `Array`
521  fn get_array_length_unchecked(&self) -> Result<u32> {
522    let mut length: u32 = 0;
523    let env = self.value().env;
524    check_status!(unsafe { sys::napi_get_array_length(env, self.value().value, &mut length) })?;
525    Ok(length)
526  }
527
528  /// Wrap the native value `T` to this `Object`
529  /// the `T` will be dropped when this `Object` is finalized
530  fn wrap<T: 'static>(&mut self, native_object: T, size_hint: Option<usize>) -> Result<()> {
531    let env = self.value().env;
532    let value = self.raw();
533    check_status!(unsafe {
534      sys::napi_wrap(
535        env,
536        value,
537        Box::into_raw(Box::new(TaggedObject::new(native_object))).cast(),
538        Some(raw_finalize::<TaggedObject<T>>),
539        Box::into_raw(Box::new(size_hint.unwrap_or(0) as i64)).cast(),
540        ptr::null_mut(),
541      )
542    })
543  }
544
545  /// Get the wrapped native value from the `Object`
546  ///
547  /// Return the `InvalidArg` error if the `Object` is not wrapped the `T`
548  #[allow(clippy::mut_from_ref)]
549  fn unwrap<T: 'static>(&self) -> Result<&mut T> {
550    let env = self.value().env;
551    let value = self.raw();
552    unsafe {
553      let mut unknown_tagged_object: *mut c_void = ptr::null_mut();
554      check_status!(
555        sys::napi_unwrap(env, value, &mut unknown_tagged_object),
556        "Failed to unwrap value of the Object"
557      )?;
558
559      let type_id = unknown_tagged_object as *const TypeId;
560      if *type_id == TypeId::of::<T>() {
561        let tagged_object = unknown_tagged_object as *mut TaggedObject<T>;
562        (*tagged_object).object.as_mut().ok_or_else(|| {
563          Error::new(
564            Status::InvalidArg,
565            "Invalid argument, nothing attach to js_object".to_owned(),
566          )
567        })
568      } else {
569        Err(Error::new(
570          Status::InvalidArg,
571          format!(
572            "Invalid argument, {} on unwrap is not the type of wrapped object",
573            type_name::<T>()
574          ),
575        ))
576      }
577    }
578  }
579
580  /// Remove the wrapped native value from the `Object`
581  ///
582  /// Return the `InvalidArg` error if the `Object` is not wrapped the `T`
583  fn remove_wrapped<T: 'static>(&mut self) -> Result<()> {
584    let env = self.value().env;
585    let value = self.raw();
586    unsafe {
587      let mut unknown_tagged_object = ptr::null_mut();
588      check_status!(sys::napi_remove_wrap(
589        env,
590        value,
591        &mut unknown_tagged_object,
592      ))?;
593      let type_id = unknown_tagged_object as *const TypeId;
594      if *type_id == TypeId::of::<T>() {
595        drop(Box::from_raw(unknown_tagged_object as *mut TaggedObject<T>));
596        Ok(())
597      } else {
598        Err(Error::new(
599          Status::InvalidArg,
600          format!(
601            "Invalid argument, {} on unwrap is not the type of wrapped object",
602            type_name::<T>()
603          ),
604        ))
605      }
606    }
607  }
608
609  #[cfg(feature = "napi5")]
610  /// Adds a `finalize_cb` callback which will be called when the JavaScript object in js_object has been garbage-collected.
611  ///
612  /// This API can be called multiple times on a single JavaScript object.
613  fn add_finalizer<T, Hint, F>(
614    &mut self,
615    native: T,
616    finalize_hint: Hint,
617    finalize_cb: F,
618  ) -> Result<()>
619  where
620    T: 'static,
621    Hint: 'static,
622    F: FnOnce(FinalizeContext<T, Hint>) + 'static,
623  {
624    let mut maybe_ref = ptr::null_mut();
625    let env = self.value().env;
626    let value = self.raw();
627    let wrap_context = Box::leak(Box::new((native, finalize_cb, ptr::null_mut())));
628    check_status!(unsafe {
629      sys::napi_add_finalizer(
630        env,
631        value,
632        (wrap_context as *mut (T, F, sys::napi_ref)).cast(),
633        Some(finalize_callback::<T, Hint, F>),
634        Box::into_raw(Box::new(finalize_hint)).cast(),
635        &mut maybe_ref, // Note: this does not point to the boxed one…
636      )
637    })?;
638    wrap_context.2 = maybe_ref;
639    Ok(())
640  }
641
642  #[cfg(feature = "napi8")]
643  /// This method freezes a given object.
644  /// 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.
645  /// 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.
646  fn freeze(&mut self) -> Result<()> {
647    let env = self.value().env;
648    check_status!(unsafe { sys::napi_object_freeze(env, self.value().value) })
649  }
650
651  #[cfg(feature = "napi8")]
652  /// 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.
653  /// This is described in [Section 19.1.2.20](https://tc39.es/ecma262/#sec-object.seal) of the ECMA-262 specification.
654  fn seal(&mut self) -> Result<()> {
655    let env = self.value().env;
656    check_status!(unsafe { sys::napi_object_seal(env, self.value().value) })
657  }
658}
659
660#[derive(Clone, Copy)]
661pub struct Object<'env>(pub(crate) Value, pub(crate) PhantomData<&'env ()>);
662
663impl<'env> JsValue<'env> for Object<'env> {
664  fn value(&self) -> Value {
665    self.0
666  }
667}
668
669impl<'env> JsObjectValue<'env> for Object<'env> {}
670
671impl TypeName for Object<'_> {
672  fn type_name() -> &'static str {
673    "Object"
674  }
675
676  fn value_type() -> ValueType {
677    ValueType::Object
678  }
679}
680
681impl ValidateNapiValue for Object<'_> {}
682
683impl FromNapiValue for Object<'_> {
684  unsafe fn from_napi_value(env: sys::napi_env, napi_val: sys::napi_value) -> Result<Self> {
685    Ok(Self(
686      Value {
687        env,
688        value: napi_val,
689        value_type: ValueType::Object,
690      },
691      PhantomData,
692    ))
693  }
694}
695
696impl ToNapiValue for &Object<'_> {
697  unsafe fn to_napi_value(_env: sys::napi_env, val: Self) -> Result<sys::napi_value> {
698    Ok(val.0.value)
699  }
700}
701
702impl Object<'_> {
703  /// create a new `Object` from raw values
704  pub fn from_raw(env: sys::napi_env, value: sys::napi_value) -> Self {
705    Self(
706      Value {
707        env,
708        value,
709        value_type: ValueType::Object,
710      },
711      PhantomData,
712    )
713  }
714
715  /// create a new `Object` from a `Env`
716  pub fn new(env: &Env) -> Result<Self> {
717    let mut ptr = ptr::null_mut();
718    unsafe {
719      check_status!(
720        sys::napi_create_object(env.0, &mut ptr),
721        "Failed to create napi Object"
722      )?;
723    }
724
725    Ok(Self(
726      crate::Value {
727        env: env.0,
728        value: ptr,
729        value_type: ValueType::Object,
730      },
731      PhantomData,
732    ))
733  }
734
735  /// Get the property value from the `Object`, if the property is not found, `None` is returned
736  pub fn get<V: FromNapiValue>(&self, field: &str) -> Result<Option<V>> {
737    unsafe {
738      self
739        .get_inner(field)?
740        .map(|v| V::from_napi_value(self.0.env, v))
741        .transpose()
742    }
743  }
744
745  fn get_inner(&self, field: &str) -> Result<Option<sys::napi_value>> {
746    unsafe {
747      let mut property_key = std::ptr::null_mut();
748      check_status!(
749        sys::napi_create_string_utf8(
750          self.0.env,
751          field.as_ptr().cast(),
752          field.len() as isize,
753          &mut property_key,
754        ),
755        "Failed to create property key with `{field}`"
756      )?;
757
758      let mut ret = ptr::null_mut();
759
760      check_status!(
761        sys::napi_get_property(self.0.env, self.0.value, property_key, &mut ret),
762        "Failed to get property with field `{field}`",
763      )?;
764
765      let ty = type_of!(self.0.env, ret)?;
766
767      Ok(if ty == ValueType::Undefined {
768        None
769      } else {
770        Some(ret)
771      })
772    }
773  }
774
775  /// Set the property value to the `Object`
776  pub fn set<K: AsRef<str>, V: ToNapiValue>(&mut self, field: K, val: V) -> Result<()> {
777    unsafe { self.set_inner(field.as_ref(), V::to_napi_value(self.0.env, val)?) }
778  }
779
780  unsafe fn set_inner(&mut self, field: &str, napi_val: sys::napi_value) -> Result<()> {
781    let mut property_key = std::ptr::null_mut();
782    check_status!(
783      unsafe {
784        sys::napi_create_string_utf8(
785          self.0.env,
786          field.as_ptr().cast(),
787          field.len() as isize,
788          &mut property_key,
789        )
790      },
791      "Failed to create property key with `{field}`"
792    )?;
793
794    check_status!(
795      unsafe { sys::napi_set_property(self.0.env, self.0.value, property_key, napi_val) },
796      "Failed to set property with field `{field}`"
797    )?;
798    Ok(())
799  }
800
801  /// Get the string keys of the `Object`
802  pub fn keys(obj: &Object) -> Result<Vec<String>> {
803    let mut names = ptr::null_mut();
804    unsafe {
805      check_status!(
806        sys::napi_get_property_names(obj.0.env, obj.0.value, &mut names),
807        "Failed to get property names of given object"
808      )?;
809    }
810
811    let names = unsafe { Array::from_napi_value(obj.0.env, names)? };
812    let mut ret = vec![];
813
814    for i in 0..names.len() {
815      ret.push(names.get_element::<String>(i)?);
816    }
817
818    Ok(ret)
819  }
820
821  /// Create a reference to the object.
822  ///
823  /// Set the `LEAK_CHECK` to `false` to disable the leak check during the `Drop`
824  pub fn create_ref<const LEAK_CHECK: bool>(&self) -> Result<ObjectRef<LEAK_CHECK>> {
825    let mut ref_ = ptr::null_mut();
826    check_status!(
827      unsafe { sys::napi_create_reference(self.0.env, self.0.value, 1, &mut ref_) },
828      "Failed to create reference"
829    )?;
830    Ok(ObjectRef { inner: ref_ })
831  }
832}
833
834/// A reference to a JavaScript object.
835///
836/// You must call the `unref` method to release the reference, or the object under the hood will be leaked forever.
837///
838/// Set the `LEAK_CHECK` to `false` to disable the leak check during the `Drop`
839pub struct ObjectRef<const LEAK_CHECK: bool = true> {
840  pub(crate) inner: sys::napi_ref,
841}
842
843unsafe impl<const LEAK_CHECK: bool> Send for ObjectRef<LEAK_CHECK> {}
844
845impl<const LEAK_CHECK: bool> Drop for ObjectRef<LEAK_CHECK> {
846  fn drop(&mut self) {
847    if LEAK_CHECK && !self.inner.is_null() {
848      eprintln!("ObjectRef is not unref, it considered as a memory leak");
849    }
850  }
851}
852
853impl<const LEAK_CHECK: bool> ObjectRef<LEAK_CHECK> {
854  /// Get the object from the reference
855  pub fn get_value<'env>(&self, env: &'env Env) -> Result<Object<'env>> {
856    let mut result = ptr::null_mut();
857    check_status!(
858      unsafe { sys::napi_get_reference_value(env.0, self.inner, &mut result) },
859      "Failed to get reference value"
860    )?;
861    Ok(Object::from_raw(env.0, result))
862  }
863
864  /// Unref the reference
865  pub fn unref(mut self, env: &Env) -> Result<()> {
866    check_status!(
867      unsafe { sys::napi_delete_reference(env.0, self.inner) },
868      "delete Ref failed"
869    )?;
870    self.inner = ptr::null_mut();
871    Ok(())
872  }
873}
874
875impl<const LEAK_CHECK: bool> FromNapiValue for ObjectRef<LEAK_CHECK> {
876  unsafe fn from_napi_value(env: sys::napi_env, napi_val: sys::napi_value) -> Result<Self> {
877    let mut ref_ = ptr::null_mut();
878    check_status!(
879      unsafe { sys::napi_create_reference(env, napi_val, 1, &mut ref_) },
880      "Failed to create reference"
881    )?;
882    Ok(Self { inner: ref_ })
883  }
884}
885
886impl<const LEAK_CHECK: bool> ToNapiValue for &ObjectRef<LEAK_CHECK> {
887  unsafe fn to_napi_value(env: sys::napi_env, val: Self) -> Result<sys::napi_value> {
888    let mut result = ptr::null_mut();
889    check_status!(
890      unsafe { sys::napi_get_reference_value(env, val.inner, &mut result) },
891      "Failed to get reference value"
892    )?;
893    Ok(result)
894  }
895}
896
897impl<const LEAK_CHECK: bool> ToNapiValue for ObjectRef<LEAK_CHECK> {
898  unsafe fn to_napi_value(env: sys::napi_env, mut val: Self) -> Result<sys::napi_value> {
899    let mut result = ptr::null_mut();
900    check_status!(
901      unsafe { sys::napi_get_reference_value(env, val.inner, &mut result) },
902      "Failed to get reference value"
903    )?;
904    check_status!(
905      unsafe { sys::napi_delete_reference(env, val.inner) },
906      "delete Ref failed"
907    )?;
908    val.inner = ptr::null_mut();
909    drop(val);
910    Ok(result)
911  }
912}
913
914#[cfg(feature = "napi5")]
915pub struct FinalizeContext<T: 'static, Hint: 'static> {
916  pub env: Env,
917  pub value: T,
918  pub hint: Hint,
919}
920
921#[cfg(feature = "napi6")]
922pub enum KeyCollectionMode {
923  IncludePrototypes,
924  OwnOnly,
925}
926
927#[cfg(feature = "napi6")]
928impl TryFrom<sys::napi_key_collection_mode> for KeyCollectionMode {
929  type Error = Error;
930
931  fn try_from(value: sys::napi_key_collection_mode) -> Result<Self> {
932    match value {
933      sys::KeyCollectionMode::include_prototypes => Ok(Self::IncludePrototypes),
934      sys::KeyCollectionMode::own_only => Ok(Self::OwnOnly),
935      _ => Err(Error::new(
936        crate::Status::InvalidArg,
937        format!("Invalid key collection mode: {value}"),
938      )),
939    }
940  }
941}
942
943#[cfg(feature = "napi6")]
944impl From<KeyCollectionMode> for sys::napi_key_collection_mode {
945  fn from(value: KeyCollectionMode) -> Self {
946    match value {
947      KeyCollectionMode::IncludePrototypes => sys::KeyCollectionMode::include_prototypes,
948      KeyCollectionMode::OwnOnly => sys::KeyCollectionMode::own_only,
949    }
950  }
951}
952
953#[cfg(feature = "napi6")]
954pub enum KeyFilter {
955  AllProperties,
956  Writable,
957  Enumerable,
958  Configurable,
959  SkipStrings,
960  SkipSymbols,
961}
962
963#[cfg(feature = "napi6")]
964impl TryFrom<sys::napi_key_filter> for KeyFilter {
965  type Error = Error;
966
967  fn try_from(value: sys::napi_key_filter) -> Result<Self> {
968    match value {
969      sys::KeyFilter::all_properties => Ok(Self::AllProperties),
970      sys::KeyFilter::writable => Ok(Self::Writable),
971      sys::KeyFilter::enumerable => Ok(Self::Enumerable),
972      sys::KeyFilter::configurable => Ok(Self::Configurable),
973      sys::KeyFilter::skip_strings => Ok(Self::SkipStrings),
974      sys::KeyFilter::skip_symbols => Ok(Self::SkipSymbols),
975      _ => Err(Error::new(
976        crate::Status::InvalidArg,
977        format!("Invalid key filter [{value}]"),
978      )),
979    }
980  }
981}
982
983#[cfg(feature = "napi6")]
984impl From<KeyFilter> for sys::napi_key_filter {
985  fn from(value: KeyFilter) -> Self {
986    match value {
987      KeyFilter::AllProperties => sys::KeyFilter::all_properties,
988      KeyFilter::Writable => sys::KeyFilter::writable,
989      KeyFilter::Enumerable => sys::KeyFilter::enumerable,
990      KeyFilter::Configurable => sys::KeyFilter::configurable,
991      KeyFilter::SkipStrings => sys::KeyFilter::skip_strings,
992      KeyFilter::SkipSymbols => sys::KeyFilter::skip_symbols,
993    }
994  }
995}
996
997#[cfg(feature = "napi6")]
998pub enum KeyConversion {
999  KeepNumbers,
1000  NumbersToStrings,
1001}
1002
1003#[cfg(feature = "napi6")]
1004impl TryFrom<sys::napi_key_conversion> for KeyConversion {
1005  type Error = Error;
1006
1007  fn try_from(value: sys::napi_key_conversion) -> Result<Self> {
1008    match value {
1009      sys::KeyConversion::keep_numbers => Ok(Self::KeepNumbers),
1010      sys::KeyConversion::numbers_to_strings => Ok(Self::NumbersToStrings),
1011      _ => Err(Error::new(
1012        crate::Status::InvalidArg,
1013        format!("Invalid key conversion [{value}]"),
1014      )),
1015    }
1016  }
1017}
1018
1019#[cfg(feature = "napi6")]
1020impl From<KeyConversion> for sys::napi_key_conversion {
1021  fn from(value: KeyConversion) -> Self {
1022    match value {
1023      KeyConversion::KeepNumbers => sys::KeyConversion::keep_numbers,
1024      KeyConversion::NumbersToStrings => sys::KeyConversion::numbers_to_strings,
1025    }
1026  }
1027}
1028
1029#[cfg(feature = "napi5")]
1030unsafe extern "C" fn finalize_callback<T, Hint, F>(
1031  raw_env: sys::napi_env,
1032  finalize_data: *mut c_void,
1033  finalize_hint: *mut c_void,
1034) where
1035  T: 'static,
1036  Hint: 'static,
1037  F: FnOnce(FinalizeContext<T, Hint>),
1038{
1039  use crate::Env;
1040
1041  let (value, callback, raw_ref) =
1042    unsafe { *Box::from_raw(finalize_data as *mut (T, F, sys::napi_ref)) };
1043  let hint = unsafe { *Box::from_raw(finalize_hint as *mut Hint) };
1044  let env = Env::from_raw(raw_env);
1045  callback(FinalizeContext { env, value, hint });
1046  if !raw_ref.is_null() {
1047    check_status_or_throw!(
1048      raw_env,
1049      unsafe { sys::napi_delete_reference(raw_env, raw_ref) },
1050      "Delete reference in finalize callback failed"
1051    );
1052  }
1053}
1054
1055#[cfg(feature = "napi5")]
1056pub(crate) unsafe extern "C" fn finalize_closures(
1057  _env: sys::napi_env,
1058  data: *mut c_void,
1059  len: *mut c_void,
1060) {
1061  let (length, capacity): (usize, usize) = *unsafe { Box::from_raw(len.cast()) };
1062  let closures: Vec<*mut PropertyClosures> =
1063    unsafe { Vec::from_raw_parts(data.cast(), length, capacity) };
1064  for closure_ptr in closures.into_iter() {
1065    if !closure_ptr.is_null() {
1066      let closures = unsafe { Box::from_raw(closure_ptr) };
1067      // Free the actual closure functions using the stored drop functions
1068      if !closures.getter_closure.is_null() {
1069        if let Some(drop_fn) = closures.getter_drop_fn {
1070          unsafe { drop_fn(closures.getter_closure) };
1071        }
1072      }
1073      if !closures.setter_closure.is_null() {
1074        if let Some(drop_fn) = closures.setter_drop_fn {
1075          unsafe { drop_fn(closures.setter_closure) };
1076        }
1077      }
1078    }
1079  }
1080}