napi/bindgen_runtime/js_values/
class.rs

1use std::any::type_name;
2use std::ffi::CString;
3use std::marker::PhantomData;
4use std::ops::{Deref, DerefMut};
5use std::ptr;
6
7use crate::{
8  bindgen_runtime::{
9    raw_finalize_unchecked, FromNapiValue, JsObjectValue, Object, ObjectFinalize, Reference,
10    Result, TypeName, ValidateNapiValue,
11  },
12  check_status, sys, Env, JsValue, Property, PropertyAttributes, Value, ValueType,
13};
14
15#[derive(Clone, Copy)]
16pub struct This<'env, T = Object<'env>> {
17  pub object: T,
18  _phantom: &'env PhantomData<()>,
19}
20
21impl<T> From<T> for This<'_, T> {
22  fn from(value: T) -> Self {
23    Self {
24      object: value,
25      _phantom: &PhantomData,
26    }
27  }
28}
29
30impl<T> Deref for This<'_, T> {
31  type Target = T;
32
33  fn deref(&self) -> &Self::Target {
34    &self.object
35  }
36}
37
38impl<T> DerefMut for This<'_, T> {
39  fn deref_mut(&mut self) -> &mut Self::Target {
40    &mut self.object
41  }
42}
43
44impl<'env, T: JsValue<'env>> JsValue<'env> for This<'_, T> {
45  fn value(&self) -> Value {
46    self.object.value()
47  }
48}
49
50impl<T: FromNapiValue> FromNapiValue for This<'_, T> {
51  unsafe fn from_napi_value(env: sys::napi_env, napi_val: sys::napi_value) -> Result<Self> {
52    Ok(Self {
53      object: T::from_napi_value(env, napi_val)?,
54      _phantom: &PhantomData,
55    })
56  }
57}
58
59#[derive(Clone, Copy)]
60pub struct ClassInstance<'env, T: 'env> {
61  pub value: sys::napi_value,
62  env: sys::napi_env,
63  inner: *mut T,
64  _phantom: &'env PhantomData<()>,
65}
66
67impl<'env, T: 'env> JsValue<'env> for ClassInstance<'env, T> {
68  fn value(&self) -> Value {
69    Value {
70      env: self.env,
71      value: self.value,
72      value_type: ValueType::Object,
73    }
74  }
75}
76
77impl<'env, T: 'env> JsObjectValue<'env> for ClassInstance<'env, T> {}
78
79impl<'env, T: 'env> ClassInstance<'env, T> {
80  #[doc(hidden)]
81  pub unsafe fn new(value: sys::napi_value, env: sys::napi_env, inner: *mut T) -> Self {
82    Self {
83      value,
84      env,
85      inner: unsafe { &mut *inner },
86      _phantom: &PhantomData,
87    }
88  }
89
90  pub fn as_object<'a>(&self, env: &'a Env) -> Object<'a> {
91    Object(
92      Value {
93        env: env.raw(),
94        value: self.value,
95        value_type: ValueType::Object,
96      },
97      PhantomData,
98    )
99  }
100
101  /// Assign this `ClassInstance` to another `This` object
102  ///
103  /// Extends the lifetime of `ClassInstance` to `This`.
104  pub fn assign_to_this<'a, 'this, U>(
105    &'a self,
106    name: &'a str,
107    this: &'a mut This<U>,
108  ) -> Result<ClassInstance<'this, T>>
109  where
110    'this: 'env,
111    U: FromNapiValue + JsValue<'this>,
112  {
113    let name = CString::new(name)?;
114    check_status!(
115      unsafe {
116        sys::napi_set_named_property(self.env, this.object.raw(), name.as_ptr(), self.value)
117      },
118      "Failed to assign ClassInstance<{}> to this",
119      std::any::type_name::<T>()
120    )?;
121    let val: ClassInstance<'this, T> = ClassInstance {
122      value: self.value,
123      env: self.env,
124      inner: self.inner,
125      _phantom: &PhantomData,
126    };
127    Ok(val)
128  }
129
130  /// Assign this `ClassInstance` to another `This` object with `PropertyAttributes`.
131  ///
132  /// Extends the lifetime of `ClassInsatnce` to `This`.
133  pub fn assign_to_this_with_attributes<'a, 'this, U>(
134    &'a self,
135    name: &'a str,
136    attributes: PropertyAttributes,
137    this: &'a mut This<U>,
138  ) -> Result<ClassInstance<'this, T>>
139  where
140    'this: 'env,
141    U: FromNapiValue + JsValue<'this>,
142  {
143    let property = Property::new()
144      .with_utf8_name(name)?
145      .with_value(self)
146      .with_property_attributes(attributes);
147
148    check_status!(
149      unsafe {
150        sys::napi_define_properties(
151          self.env,
152          this.object.value().value,
153          1,
154          [property.raw()].as_ptr(),
155        )
156      },
157      "Failed to define properties on This in `assign_to_this_with_attributes`"
158    )?;
159
160    let val: ClassInstance<'this, T> = ClassInstance {
161      value: self.value,
162      env: self.env,
163      inner: self.inner,
164      _phantom: &PhantomData,
165    };
166    Ok(val)
167  }
168}
169
170impl<'env, T: 'env> TypeName for ClassInstance<'env, T>
171where
172  &'env T: TypeName,
173{
174  fn type_name() -> &'static str {
175    type_name::<&T>()
176  }
177
178  fn value_type() -> ValueType {
179    <&T>::value_type()
180  }
181}
182
183impl<'env, T: 'env> ValidateNapiValue for ClassInstance<'env, T>
184where
185  &'env T: ValidateNapiValue,
186{
187  unsafe fn validate(
188    env: sys::napi_env,
189    napi_val: sys::napi_value,
190  ) -> crate::Result<sys::napi_value> {
191    unsafe { <&'env T>::validate(env, napi_val) }
192  }
193}
194
195impl<'env, T: 'env> FromNapiValue for ClassInstance<'env, T> {
196  unsafe fn from_napi_value(env: sys::napi_env, napi_val: sys::napi_value) -> crate::Result<Self> {
197    let mut value = ptr::null_mut();
198    check_status!(
199      unsafe { sys::napi_unwrap(env, napi_val, &mut value) },
200      "Unwrap value [{}] from class failed",
201      type_name::<T>(),
202    )?;
203    let value = unsafe { Box::from_raw(value as *mut T) };
204    Ok(Self {
205      value: napi_val,
206      inner: Box::leak(value),
207      env,
208      _phantom: &PhantomData,
209    })
210  }
211}
212
213impl<'env, T: 'env> Deref for ClassInstance<'env, T> {
214  type Target = T;
215
216  fn deref(&self) -> &Self::Target {
217    unsafe { &*self.inner }
218  }
219}
220
221impl<'env, T: 'env> DerefMut for ClassInstance<'env, T> {
222  fn deref_mut(&mut self) -> &mut Self::Target {
223    unsafe { &mut *self.inner }
224  }
225}
226
227impl<'env, T: 'env> AsRef<T> for ClassInstance<'env, T> {
228  fn as_ref(&self) -> &T {
229    unsafe { &*self.inner }
230  }
231}
232
233pub trait JavaScriptClassExt: Sized {
234  fn into_instance(self, env: &Env) -> Result<ClassInstance<'_, Self>>;
235  fn into_reference(self, env: Env) -> Result<Reference<Self>>;
236  fn instance_of<'env, V: JsValue<'env>>(env: &Env, value: &V) -> Result<bool>;
237}
238
239/// # Safety
240///
241/// create instance of class
242#[doc(hidden)]
243pub unsafe fn new_instance<T: 'static + ObjectFinalize>(
244  env: sys::napi_env,
245  wrapped_value: *mut std::ffi::c_void,
246  ctor_ref: sys::napi_ref,
247) -> Result<sys::napi_value> {
248  let mut ctor = std::ptr::null_mut();
249  check_status!(
250    sys::napi_get_reference_value(env, ctor_ref, &mut ctor),
251    "Failed to get constructor reference of class `{}`",
252    type_name::<T>(),
253  )?;
254
255  let mut result = std::ptr::null_mut();
256  crate::__private::___CALL_FROM_FACTORY.with(|inner| inner.set(true));
257  check_status!(
258    sys::napi_new_instance(env, ctor, 0, std::ptr::null_mut(), &mut result),
259    "Failed to construct class `{}`",
260    type_name::<T>(),
261  )?;
262  crate::__private::___CALL_FROM_FACTORY.with(|inner| inner.set(false));
263  let mut object_ref = std::ptr::null_mut();
264  let initial_finalize: Box<dyn FnOnce()> = Box::new(|| {});
265  let finalize_callbacks_ptr = std::rc::Rc::into_raw(std::rc::Rc::new(std::cell::Cell::new(
266    Box::into_raw(initial_finalize),
267  )));
268  check_status!(
269    sys::napi_wrap(
270      env,
271      result,
272      wrapped_value,
273      Some(raw_finalize_unchecked::<T>),
274      std::ptr::null_mut(),
275      &mut object_ref,
276    ),
277    "Failed to wrap native object of class `{}`",
278    type_name::<T>(),
279  )?;
280  Reference::<T>::add_ref(
281    env,
282    wrapped_value,
283    (wrapped_value, object_ref, finalize_callbacks_ptr),
284  );
285  Ok(result)
286}