napi_h/js_values/
mod.rs

1use std::convert::TryFrom;
2#[cfg(feature = "napi5")]
3use std::ffi::c_void;
4use std::ffi::CString;
5use std::ptr;
6
7use crate::{
8  bindgen_runtime::{FromNapiValue, ToNapiValue, TypeName, ValidateNapiValue},
9  check_status, sys, type_of, Callback, Error, Result, Status, ValueType,
10};
11
12#[cfg(feature = "serde-json")]
13mod de;
14#[cfg(feature = "serde-json")]
15mod ser;
16
17mod arraybuffer;
18#[cfg(feature = "napi6")]
19mod bigint;
20mod boolean;
21mod buffer;
22#[cfg(feature = "napi5")]
23mod date;
24#[cfg(feature = "napi4")]
25mod deferred;
26mod either;
27mod escapable_handle_scope;
28mod function;
29mod global;
30mod number;
31mod object;
32mod object_property;
33mod string;
34mod tagged_object;
35mod undefined;
36mod value;
37mod value_ref;
38
39pub use arraybuffer::*;
40#[cfg(feature = "napi6")]
41pub use bigint::JsBigInt;
42pub use boolean::JsBoolean;
43pub use buffer::*;
44#[cfg(feature = "napi5")]
45pub use date::*;
46#[cfg(feature = "serde-json")]
47pub(crate) use de::De;
48#[cfg(feature = "napi4")]
49pub use deferred::*;
50pub use either::Either;
51pub use escapable_handle_scope::EscapableHandleScope;
52pub use function::JsFunction;
53pub use global::*;
54pub use number::JsNumber;
55pub use object::*;
56pub use object_property::*;
57#[cfg(feature = "serde-json")]
58pub(crate) use ser::Ser;
59pub use string::*;
60pub(crate) use tagged_object::TaggedObject;
61pub use undefined::JsUndefined;
62pub(crate) use value::Value;
63pub use value_ref::*;
64
65// Value types
66
67pub struct JsUnknown(pub(crate) Value);
68
69#[derive(Clone, Copy)]
70pub struct JsNull(pub(crate) Value);
71
72impl TypeName for JsNull {
73  fn type_name() -> &'static str {
74    "null"
75  }
76
77  fn value_type() -> ValueType {
78    ValueType::Null
79  }
80}
81
82impl ValidateNapiValue for JsNull {}
83
84#[derive(Clone, Copy)]
85pub struct JsSymbol(pub(crate) Value);
86
87impl TypeName for JsSymbol {
88  fn type_name() -> &'static str {
89    "symbol"
90  }
91
92  fn value_type() -> ValueType {
93    ValueType::Symbol
94  }
95}
96
97impl ValidateNapiValue for JsSymbol {}
98
99pub struct JsExternal(pub(crate) Value);
100
101impl TypeName for JsExternal {
102  fn type_name() -> &'static str {
103    "external"
104  }
105
106  fn value_type() -> ValueType {
107    ValueType::External
108  }
109}
110
111impl ValidateNapiValue for JsExternal {}
112
113macro_rules! impl_napi_value_trait {
114  ($js_value:ident, $value_type:ident) => {
115    impl NapiValue for $js_value {
116      unsafe fn from_raw(env: sys::napi_env, value: sys::napi_value) -> Result<$js_value> {
117        let value_type = type_of!(env, value)?;
118        if value_type != $value_type {
119          Err(Error::new(
120            Status::InvalidArg,
121            format!("expect {:?}, got: {:?}", $value_type, value_type),
122          ))
123        } else {
124          Ok($js_value(Value {
125            env,
126            value,
127            value_type: $value_type,
128          }))
129        }
130      }
131
132      unsafe fn from_raw_unchecked(env: sys::napi_env, value: sys::napi_value) -> $js_value {
133        $js_value(Value {
134          env,
135          value,
136          value_type: $value_type,
137        })
138      }
139    }
140
141    impl NapiRaw for $js_value {
142      unsafe fn raw(&self) -> sys::napi_value {
143        self.0.value
144      }
145    }
146
147    impl<'env> NapiRaw for &'env $js_value {
148      unsafe fn raw(&self) -> sys::napi_value {
149        self.0.value
150      }
151    }
152
153    impl TryFrom<JsUnknown> for $js_value {
154      type Error = Error;
155      fn try_from(value: JsUnknown) -> Result<$js_value> {
156        unsafe { $js_value::from_raw(value.0.env, value.0.value) }
157      }
158    }
159  };
160}
161
162macro_rules! impl_js_value_methods {
163  ($js_value:ident) => {
164    impl $js_value {
165      pub fn into_unknown(self) -> JsUnknown {
166        unsafe { JsUnknown::from_raw_unchecked(self.0.env, self.0.value) }
167      }
168
169      pub fn coerce_to_bool(self) -> Result<JsBoolean> {
170        let mut new_raw_value = ptr::null_mut();
171        check_status!(unsafe {
172          sys::napi_coerce_to_bool(self.0.env, self.0.value, &mut new_raw_value)
173        })?;
174        Ok(JsBoolean(Value {
175          env: self.0.env,
176          value: new_raw_value,
177          value_type: ValueType::Boolean,
178        }))
179      }
180
181      pub fn coerce_to_number(self) -> Result<JsNumber> {
182        let mut new_raw_value = ptr::null_mut();
183        check_status!(unsafe {
184          sys::napi_coerce_to_number(self.0.env, self.0.value, &mut new_raw_value)
185        })?;
186        Ok(JsNumber(Value {
187          env: self.0.env,
188          value: new_raw_value,
189          value_type: ValueType::Number,
190        }))
191      }
192
193      pub fn coerce_to_string(self) -> Result<JsString> {
194        let mut new_raw_value = ptr::null_mut();
195        check_status!(unsafe {
196          sys::napi_coerce_to_string(self.0.env, self.0.value, &mut new_raw_value)
197        })?;
198        Ok(JsString(Value {
199          env: self.0.env,
200          value: new_raw_value,
201          value_type: ValueType::String,
202        }))
203      }
204
205      pub fn coerce_to_object(self) -> Result<JsObject> {
206        let mut new_raw_value = ptr::null_mut();
207        check_status!(unsafe {
208          sys::napi_coerce_to_object(self.0.env, self.0.value, &mut new_raw_value)
209        })?;
210        Ok(JsObject(Value {
211          env: self.0.env,
212          value: new_raw_value,
213          value_type: ValueType::Object,
214        }))
215      }
216
217      #[cfg(feature = "napi5")]
218      pub fn is_date(&self) -> Result<bool> {
219        let mut is_date = true;
220        check_status!(unsafe { sys::napi_is_date(self.0.env, self.0.value, &mut is_date) })?;
221        Ok(is_date)
222      }
223
224      pub fn is_promise(&self) -> Result<bool> {
225        let mut is_promise = true;
226        check_status!(unsafe { sys::napi_is_promise(self.0.env, self.0.value, &mut is_promise) })?;
227        Ok(is_promise)
228      }
229
230      pub fn is_error(&self) -> Result<bool> {
231        let mut result = false;
232        check_status!(unsafe { sys::napi_is_error(self.0.env, self.0.value, &mut result) })?;
233        Ok(result)
234      }
235
236      pub fn is_typedarray(&self) -> Result<bool> {
237        let mut result = false;
238        check_status!(unsafe { sys::napi_is_typedarray(self.0.env, self.0.value, &mut result) })?;
239        Ok(result)
240      }
241
242      pub fn is_dataview(&self) -> Result<bool> {
243        let mut result = false;
244        check_status!(unsafe { sys::napi_is_dataview(self.0.env, self.0.value, &mut result) })?;
245        Ok(result)
246      }
247
248      pub fn is_array(&self) -> Result<bool> {
249        let mut is_array = false;
250        check_status!(unsafe { sys::napi_is_array(self.0.env, self.0.value, &mut is_array) })?;
251        Ok(is_array)
252      }
253
254      pub fn is_buffer(&self) -> Result<bool> {
255        let mut is_buffer = false;
256        check_status!(unsafe { sys::napi_is_buffer(self.0.env, self.0.value, &mut is_buffer) })?;
257        Ok(is_buffer)
258      }
259
260      pub fn instanceof<Constructor>(&self, constructor: Constructor) -> Result<bool>
261      where
262        Constructor: NapiRaw,
263      {
264        let mut result = false;
265        check_status!(unsafe {
266          sys::napi_instanceof(self.0.env, self.0.value, constructor.raw(), &mut result)
267        })?;
268        Ok(result)
269      }
270    }
271  };
272}
273
274macro_rules! impl_object_methods {
275  ($js_value:ident) => {
276    impl $js_value {
277      pub fn set_property<K, V>(&mut self, key: K, value: V) -> Result<()>
278      where
279        K: NapiRaw,
280        V: NapiRaw,
281      {
282        check_status!(unsafe {
283          sys::napi_set_property(self.0.env, self.0.value, key.raw(), value.raw())
284        })
285      }
286
287      pub fn get_property<K, T>(&self, key: K) -> Result<T>
288      where
289        K: NapiRaw,
290        T: NapiValue,
291      {
292        let mut raw_value = ptr::null_mut();
293        check_status!(unsafe {
294          sys::napi_get_property(self.0.env, self.0.value, key.raw(), &mut raw_value)
295        })?;
296        unsafe { T::from_raw(self.0.env, raw_value) }
297      }
298
299      pub fn get_property_unchecked<K, T>(&self, key: K) -> Result<T>
300      where
301        K: NapiRaw,
302        T: NapiValue,
303      {
304        let mut raw_value = ptr::null_mut();
305        check_status!(unsafe {
306          sys::napi_get_property(self.0.env, self.0.value, key.raw(), &mut raw_value)
307        })?;
308        Ok(unsafe { T::from_raw_unchecked(self.0.env, raw_value) })
309      }
310
311      pub fn set_named_property<T>(&mut self, name: &str, value: T) -> Result<()>
312      where
313        T: ToNapiValue,
314      {
315        let key = CString::new(name)?;
316        check_status!(unsafe {
317          sys::napi_set_named_property(
318            self.0.env,
319            self.0.value,
320            key.as_ptr(),
321            T::to_napi_value(self.0.env, value)?,
322          )
323        })
324      }
325
326      pub fn create_named_method(&mut self, name: &str, function: Callback) -> Result<()> {
327        let mut js_function = ptr::null_mut();
328        let len = name.len();
329        let name = CString::new(name)?;
330        check_status!(unsafe {
331          sys::napi_create_function(
332            self.0.env,
333            name.as_ptr(),
334            len,
335            Some(function),
336            ptr::null_mut(),
337            &mut js_function,
338          )
339        })?;
340        check_status!(
341          unsafe {
342            sys::napi_set_named_property(self.0.env, self.0.value, name.as_ptr(), js_function)
343          },
344          "create_named_method error"
345        )
346      }
347
348      pub fn get_named_property<T>(&self, name: &str) -> Result<T>
349      where
350        T: FromNapiValue + ValidateNapiValue,
351      {
352        let key = CString::new(name)?;
353        let mut raw_value = ptr::null_mut();
354        check_status!(
355          unsafe {
356            sys::napi_get_named_property(self.0.env, self.0.value, key.as_ptr(), &mut raw_value)
357          },
358          "get_named_property error"
359        )?;
360        unsafe { <T as ValidateNapiValue>::validate(self.0.env, raw_value) }.map_err(
361          |mut err| {
362            err.reason = format!("Object property '{name}' type mismatch. {}", err.reason);
363            err
364          },
365        )?;
366        unsafe { <T as FromNapiValue>::from_napi_value(self.0.env, raw_value) }
367      }
368
369      pub fn get_named_property_unchecked<T: FromNapiValue>(&self, name: &str) -> Result<T>
370      where
371        T: FromNapiValue,
372      {
373        let key = CString::new(name)?;
374        let mut raw_value = ptr::null_mut();
375        check_status!(
376          unsafe {
377            sys::napi_get_named_property(self.0.env, self.0.value, key.as_ptr(), &mut raw_value)
378          },
379          "get_named_property_unchecked error"
380        )?;
381        unsafe { <T as FromNapiValue>::from_napi_value(self.0.env, raw_value) }
382      }
383
384      pub fn has_named_property<N: AsRef<str>>(&self, name: N) -> Result<bool> {
385        let mut result = false;
386        let key = CString::new(name.as_ref())?;
387        check_status!(
388          unsafe {
389            sys::napi_has_named_property(self.0.env, self.0.value, key.as_ptr(), &mut result)
390          },
391          "napi_has_named_property error"
392        )?;
393        Ok(result)
394      }
395
396      pub fn delete_property<S>(&mut self, name: S) -> Result<bool>
397      where
398        S: NapiRaw,
399      {
400        let mut result = false;
401        check_status!(unsafe {
402          sys::napi_delete_property(self.0.env, self.0.value, name.raw(), &mut result)
403        })?;
404        Ok(result)
405      }
406
407      pub fn delete_named_property(&mut self, name: &str) -> Result<bool> {
408        let mut result = false;
409        let key_str = CString::new(name)?;
410        let mut js_key = ptr::null_mut();
411        check_status!(unsafe {
412          sys::napi_create_string_utf8(self.0.env, key_str.as_ptr(), name.len(), &mut js_key)
413        })?;
414        check_status!(unsafe {
415          sys::napi_delete_property(self.0.env, self.0.value, js_key, &mut result)
416        })?;
417        Ok(result)
418      }
419
420      pub fn has_own_property(&self, key: &str) -> Result<bool> {
421        let mut result = false;
422        let string = CString::new(key)?;
423        let mut js_key = ptr::null_mut();
424        check_status!(unsafe {
425          sys::napi_create_string_utf8(self.0.env, string.as_ptr(), key.len(), &mut js_key)
426        })?;
427        check_status!(unsafe {
428          sys::napi_has_own_property(self.0.env, self.0.value, js_key, &mut result)
429        })?;
430        Ok(result)
431      }
432
433      pub fn has_own_property_js<K>(&self, key: K) -> Result<bool>
434      where
435        K: NapiRaw,
436      {
437        let mut result = false;
438        check_status!(unsafe {
439          sys::napi_has_own_property(self.0.env, self.0.value, key.raw(), &mut result)
440        })?;
441        Ok(result)
442      }
443
444      pub fn has_property(&self, name: &str) -> Result<bool> {
445        let string = CString::new(name)?;
446        let mut js_key = ptr::null_mut();
447        let mut result = false;
448        check_status!(unsafe {
449          sys::napi_create_string_utf8(self.0.env, string.as_ptr(), name.len(), &mut js_key)
450        })?;
451        check_status!(unsafe {
452          sys::napi_has_property(self.0.env, self.0.value, js_key, &mut result)
453        })?;
454        Ok(result)
455      }
456
457      pub fn has_property_js<K>(&self, name: K) -> Result<bool>
458      where
459        K: NapiRaw,
460      {
461        let mut result = false;
462        check_status!(unsafe {
463          sys::napi_has_property(self.0.env, self.0.value, name.raw(), &mut result)
464        })?;
465        Ok(result)
466      }
467
468      pub fn get_property_names(&self) -> Result<JsObject> {
469        let mut raw_value = ptr::null_mut();
470        check_status!(unsafe {
471          sys::napi_get_property_names(self.0.env, self.0.value, &mut raw_value)
472        })?;
473        Ok(unsafe { JsObject::from_raw_unchecked(self.0.env, raw_value) })
474      }
475
476      /// <https://nodejs.org/api/n-api.html#n_api_napi_get_all_property_names>
477      /// return `Array` of property names
478      #[cfg(feature = "napi6")]
479      pub fn get_all_property_names(
480        &self,
481        mode: KeyCollectionMode,
482        filter: KeyFilter,
483        conversion: KeyConversion,
484      ) -> Result<JsObject> {
485        let mut properties_value = ptr::null_mut();
486        check_status!(unsafe {
487          sys::napi_get_all_property_names(
488            self.0.env,
489            self.0.value,
490            mode.into(),
491            filter.into(),
492            conversion.into(),
493            &mut properties_value,
494          )
495        })?;
496        Ok(unsafe { JsObject::from_raw_unchecked(self.0.env, properties_value) })
497      }
498
499      /// This returns the equivalent of `Object.getPrototypeOf` (which is not the same as the function's prototype property).
500      pub fn get_prototype<T>(&self) -> Result<T>
501      where
502        T: NapiValue,
503      {
504        let mut result = ptr::null_mut();
505        check_status!(unsafe { sys::napi_get_prototype(self.0.env, self.0.value, &mut result) })?;
506        unsafe { T::from_raw(self.0.env, result) }
507      }
508
509      pub fn get_prototype_unchecked<T>(&self) -> Result<T>
510      where
511        T: NapiValue,
512      {
513        let mut result = ptr::null_mut();
514        check_status!(unsafe { sys::napi_get_prototype(self.0.env, self.0.value, &mut result) })?;
515        Ok(unsafe { T::from_raw_unchecked(self.0.env, result) })
516      }
517
518      pub fn set_element<T>(&mut self, index: u32, value: T) -> Result<()>
519      where
520        T: NapiRaw,
521      {
522        check_status!(unsafe {
523          sys::napi_set_element(self.0.env, self.0.value, index, value.raw())
524        })
525      }
526
527      pub fn has_element(&self, index: u32) -> Result<bool> {
528        let mut result = false;
529        check_status!(unsafe {
530          sys::napi_has_element(self.0.env, self.0.value, index, &mut result)
531        })?;
532        Ok(result)
533      }
534
535      pub fn delete_element(&mut self, index: u32) -> Result<bool> {
536        let mut result = false;
537        check_status!(unsafe {
538          sys::napi_delete_element(self.0.env, self.0.value, index, &mut result)
539        })?;
540        Ok(result)
541      }
542
543      pub fn get_element<T>(&self, index: u32) -> Result<T>
544      where
545        T: NapiValue,
546      {
547        let mut raw_value = ptr::null_mut();
548        check_status!(unsafe {
549          sys::napi_get_element(self.0.env, self.0.value, index, &mut raw_value)
550        })?;
551        unsafe { T::from_raw(self.0.env, raw_value) }
552      }
553
554      pub fn get_element_unchecked<T>(&self, index: u32) -> Result<T>
555      where
556        T: NapiValue,
557      {
558        let mut raw_value = ptr::null_mut();
559        check_status!(unsafe {
560          sys::napi_get_element(self.0.env, self.0.value, index, &mut raw_value)
561        })?;
562        Ok(unsafe { T::from_raw_unchecked(self.0.env, raw_value) })
563      }
564
565      /// This method allows the efficient definition of multiple properties on a given object.
566      pub fn define_properties(&mut self, properties: &[Property]) -> Result<()> {
567        let properties_iter = properties.iter().map(|property| property.raw());
568        #[cfg(feature = "napi5")]
569        {
570          let mut closures = properties_iter
571            .clone()
572            .map(|p| p.data)
573            .filter(|data| !data.is_null())
574            .collect::<Vec<*mut std::ffi::c_void>>();
575          let len = Box::into_raw(Box::new(closures.len()));
576          check_status!(unsafe {
577            sys::napi_add_finalizer(
578              self.0.env,
579              self.0.value,
580              closures.as_mut_ptr().cast(),
581              Some(finalize_closures),
582              len.cast(),
583              ptr::null_mut(),
584            )
585          })?;
586          std::mem::forget(closures);
587        }
588        check_status!(unsafe {
589          sys::napi_define_properties(
590            self.0.env,
591            self.0.value,
592            properties.len(),
593            properties_iter
594              .collect::<Vec<sys::napi_property_descriptor>>()
595              .as_ptr(),
596          )
597        })
598      }
599
600      /// Perform `is_array` check before get the length
601      /// if `Object` is not array, `ArrayExpected` error returned
602      pub fn get_array_length(&self) -> Result<u32> {
603        if self.is_array()? != true {
604          return Err(Error::new(
605            Status::ArrayExpected,
606            "Object is not array".to_owned(),
607          ));
608        }
609        self.get_array_length_unchecked()
610      }
611
612      /// use this API if you can ensure this `Object` is `Array`
613      pub fn get_array_length_unchecked(&self) -> Result<u32> {
614        let mut length: u32 = 0;
615        check_status!(unsafe {
616          sys::napi_get_array_length(self.0.env, self.0.value, &mut length)
617        })?;
618        Ok(length)
619      }
620
621      #[cfg(feature = "napi8")]
622      pub fn freeze(&mut self) -> Result<()> {
623        check_status!(unsafe { sys::napi_object_freeze(self.0.env, self.0.value) })
624      }
625
626      #[cfg(feature = "napi8")]
627      pub fn seal(&mut self) -> Result<()> {
628        check_status!(unsafe { sys::napi_object_seal(self.0.env, self.0.value) })
629      }
630    }
631  };
632}
633
634pub trait NapiRaw {
635  #[allow(clippy::missing_safety_doc)]
636  unsafe fn raw(&self) -> sys::napi_value;
637}
638
639pub trait NapiValue: Sized + NapiRaw {
640  #[allow(clippy::missing_safety_doc)]
641  unsafe fn from_raw(env: sys::napi_env, value: sys::napi_value) -> Result<Self>;
642
643  #[allow(clippy::missing_safety_doc)]
644  unsafe fn from_raw_unchecked(env: sys::napi_env, value: sys::napi_value) -> Self;
645}
646
647impl_js_value_methods!(JsUnknown);
648impl_js_value_methods!(JsUndefined);
649impl_js_value_methods!(JsNull);
650impl_js_value_methods!(JsBoolean);
651impl_js_value_methods!(JsBuffer);
652impl_js_value_methods!(JsArrayBuffer);
653impl_js_value_methods!(JsTypedArray);
654impl_js_value_methods!(JsDataView);
655impl_js_value_methods!(JsNumber);
656impl_js_value_methods!(JsString);
657impl_js_value_methods!(JsObject);
658impl_js_value_methods!(JsGlobal);
659#[cfg(feature = "napi5")]
660impl_js_value_methods!(JsDate);
661impl_js_value_methods!(JsFunction);
662impl_js_value_methods!(JsExternal);
663impl_js_value_methods!(JsSymbol);
664impl_js_value_methods!(JsTimeout);
665impl_js_value_methods!(JSON);
666
667impl_object_methods!(JsObject);
668impl_object_methods!(JsBuffer);
669impl_object_methods!(JsArrayBuffer);
670impl_object_methods!(JsTypedArray);
671impl_object_methods!(JsDataView);
672impl_object_methods!(JsGlobal);
673impl_object_methods!(JSON);
674
675use ValueType::*;
676
677impl_napi_value_trait!(JsUndefined, Undefined);
678impl_napi_value_trait!(JsNull, Null);
679impl_napi_value_trait!(JsBoolean, Boolean);
680impl_napi_value_trait!(JsBuffer, Object);
681impl_napi_value_trait!(JsArrayBuffer, Object);
682impl_napi_value_trait!(JsTypedArray, Object);
683impl_napi_value_trait!(JsDataView, Object);
684impl_napi_value_trait!(JsNumber, Number);
685impl_napi_value_trait!(JsString, String);
686impl_napi_value_trait!(JsObject, Object);
687impl_napi_value_trait!(JsGlobal, Object);
688#[cfg(feature = "napi5")]
689impl_napi_value_trait!(JsDate, Object);
690impl_napi_value_trait!(JsTimeout, Object);
691impl_napi_value_trait!(JsFunction, Function);
692impl_napi_value_trait!(JsExternal, External);
693impl_napi_value_trait!(JsSymbol, Symbol);
694
695impl NapiValue for JsUnknown {
696  unsafe fn from_raw(env: sys::napi_env, value: sys::napi_value) -> Result<Self> {
697    Ok(JsUnknown(Value {
698      env,
699      value,
700      value_type: Unknown,
701    }))
702  }
703
704  unsafe fn from_raw_unchecked(env: sys::napi_env, value: sys::napi_value) -> Self {
705    JsUnknown(Value {
706      env,
707      value,
708      value_type: Unknown,
709    })
710  }
711}
712
713impl NapiRaw for JsUnknown {
714  /// get raw js value ptr
715  unsafe fn raw(&self) -> sys::napi_value {
716    self.0.value
717  }
718}
719
720impl<'env> NapiRaw for &'env JsUnknown {
721  /// get raw js value ptr
722  unsafe fn raw(&self) -> sys::napi_value {
723    self.0.value
724  }
725}
726
727impl JsUnknown {
728  pub fn get_type(&self) -> Result<ValueType> {
729    type_of!(self.0.env, self.0.value)
730  }
731
732  /// # Safety
733  ///
734  /// This function should be called after `JsUnknown::get_type`
735  ///
736  /// And the `V` must be match with the return value of `get_type`
737  pub unsafe fn cast<V>(&self) -> V
738  where
739    V: NapiValue,
740  {
741    unsafe { V::from_raw_unchecked(self.0.env, self.0.value) }
742  }
743}
744
745#[cfg(feature = "napi5")]
746unsafe extern "C" fn finalize_closures(_env: sys::napi_env, data: *mut c_void, len: *mut c_void) {
747  let length: usize = *unsafe { Box::from_raw(len.cast()) };
748  let closures: Vec<*mut PropertyClosures> =
749    unsafe { Vec::from_raw_parts(data.cast(), length, length) };
750  for closure in closures.into_iter() {
751    drop(unsafe { Box::from_raw(closure) });
752  }
753}