napi/js_values/
object_property.rs

1use std::convert::From;
2#[cfg(feature = "napi5")]
3use std::ffi::c_void;
4use std::ffi::CString;
5use std::ptr;
6
7use bitflags::bitflags;
8
9#[cfg(feature = "napi5")]
10use crate::bindgen_runtime::{FromNapiValue, This};
11use crate::{bindgen_runtime::ToNapiValue, sys, Callback, Env, JsValue, Result};
12
13#[cfg(feature = "napi5")]
14#[derive(Copy, Clone)]
15pub struct PropertyClosures {
16  pub setter_closure: *mut c_void,
17  pub getter_closure: *mut c_void,
18  pub setter_drop_fn: Option<unsafe fn(*mut c_void)>,
19  pub getter_drop_fn: Option<unsafe fn(*mut c_void)>,
20}
21
22#[cfg(feature = "napi5")]
23impl Default for PropertyClosures {
24  fn default() -> Self {
25    Self {
26      setter_closure: ptr::null_mut(),
27      getter_closure: ptr::null_mut(),
28      setter_drop_fn: None,
29      getter_drop_fn: None,
30    }
31  }
32}
33
34#[derive(Clone)]
35pub struct Property {
36  utf8_name: Option<CString>,
37  name: sys::napi_value,
38  getter: sys::napi_callback,
39  setter: sys::napi_callback,
40  method: sys::napi_callback,
41  attrs: PropertyAttributes,
42  value: sys::napi_value,
43  pub(crate) is_ctor: bool,
44  #[cfg(feature = "napi5")]
45  pub(crate) closures: PropertyClosures,
46}
47
48impl Default for Property {
49  fn default() -> Self {
50    Property {
51      utf8_name: Default::default(),
52      name: ptr::null_mut(),
53      getter: Default::default(),
54      setter: Default::default(),
55      method: Default::default(),
56      attrs: Default::default(),
57      value: ptr::null_mut(),
58      is_ctor: Default::default(),
59      #[cfg(feature = "napi5")]
60      closures: PropertyClosures::default(),
61    }
62  }
63}
64
65bitflags! {
66  #[derive(Debug, Copy, Clone)]
67  pub struct PropertyAttributes: i32 {
68    const Default = sys::PropertyAttributes::default;
69    const Writable = sys::PropertyAttributes::writable;
70    const Enumerable = sys::PropertyAttributes::enumerable;
71    const Configurable = sys::PropertyAttributes::configurable;
72    const Static = sys::PropertyAttributes::static_;
73  }
74}
75
76impl Default for PropertyAttributes {
77  fn default() -> Self {
78    PropertyAttributes::Configurable | PropertyAttributes::Enumerable | PropertyAttributes::Writable
79  }
80}
81
82impl From<PropertyAttributes> for sys::napi_property_attributes {
83  fn from(value: PropertyAttributes) -> Self {
84    value.bits()
85  }
86}
87
88impl Property {
89  pub fn new() -> Self {
90    Default::default()
91  }
92
93  pub fn with_utf8_name(mut self, name: &str) -> Result<Self> {
94    self.utf8_name = Some(CString::new(name)?);
95    Ok(self)
96  }
97
98  pub fn with_name<T: ToNapiValue>(mut self, env: &Env, name: T) -> Result<Self> {
99    self.name = unsafe { T::to_napi_value(env.0, name)? };
100    Ok(self)
101  }
102
103  pub fn with_method(mut self, callback: Callback) -> Self {
104    self.method = Some(callback);
105    self
106  }
107
108  pub fn with_getter(mut self, callback: Callback) -> Self {
109    self.getter = Some(callback);
110    self
111  }
112
113  #[cfg(feature = "napi5")]
114  pub fn with_getter_closure<R, F>(mut self, callback: F) -> Self
115  where
116    F: 'static + Fn(Env, This) -> Result<R>,
117    R: ToNapiValue,
118  {
119    let boxed_callback = Box::new(callback);
120    let closure_data_ptr: *mut F = Box::into_raw(boxed_callback);
121    self.closures.getter_closure = closure_data_ptr.cast();
122    self.closures.getter_drop_fn = Some(|ptr: *mut c_void| unsafe {
123      drop(Box::from_raw(ptr as *mut F));
124    });
125
126    let fun = crate::trampoline_getter::<R, F>;
127    self.getter = Some(fun);
128    self
129  }
130
131  pub fn with_setter(mut self, callback: Callback) -> Self {
132    self.setter = Some(callback);
133    self
134  }
135
136  #[cfg(feature = "napi5")]
137  pub fn with_setter_closure<F, V>(mut self, callback: F) -> Self
138  where
139    F: 'static + Fn(crate::Env, This, V) -> Result<()>,
140    V: FromNapiValue,
141  {
142    let boxed_callback = Box::new(callback);
143    let closure_data_ptr: *mut F = Box::into_raw(boxed_callback);
144    self.closures.setter_closure = closure_data_ptr.cast();
145    self.closures.setter_drop_fn = Some(|ptr: *mut c_void| unsafe {
146      drop(Box::from_raw(ptr as *mut F));
147    });
148
149    let fun = crate::trampoline_setter::<V, F>;
150    self.setter = Some(fun);
151    self
152  }
153
154  pub fn with_property_attributes(mut self, attributes: PropertyAttributes) -> Self {
155    self.attrs = attributes;
156    self
157  }
158
159  pub fn with_value<'env, T: JsValue<'env>>(mut self, value: &T) -> Self {
160    self.value = T::raw(value);
161    self
162  }
163
164  pub fn with_napi_value<T: ToNapiValue>(mut self, env: &Env, value: T) -> Result<Self> {
165    self.value = unsafe { T::to_napi_value(env.0, value)? };
166    Ok(self)
167  }
168
169  pub(crate) fn raw(&self) -> sys::napi_property_descriptor {
170    #[cfg(feature = "napi5")]
171    let data = if self.closures.getter_closure.is_null() && self.closures.setter_closure.is_null() {
172      // No closures to allocate, avoid memory leak
173      ptr::null_mut()
174    } else {
175      // Only allocate when we actually have closures
176      Box::into_raw(Box::new(self.closures)).cast()
177    };
178
179    sys::napi_property_descriptor {
180      utf8name: match self.utf8_name {
181        Some(ref name) => name.as_ptr(),
182        None => ptr::null(),
183      },
184      name: self.name,
185      method: self.method,
186      getter: self.getter,
187      setter: self.setter,
188      value: self.value,
189      attributes: self.attrs.into(),
190      #[cfg(not(feature = "napi5"))]
191      data: ptr::null_mut(),
192      #[cfg(feature = "napi5")]
193      data,
194    }
195  }
196
197  pub fn with_ctor(mut self, callback: Callback) -> Self {
198    self.method = Some(callback);
199    self.is_ctor = true;
200    self
201  }
202}