napi/bindgen_runtime/js_values/
external.rs

1use std::{
2  any::TypeId,
3  ffi::c_void,
4  ops::{Deref, DerefMut},
5  ptr,
6};
7
8use crate::{
9  bindgen_runtime::{
10    sys, Env, FromNapiMutRef, FromNapiRef, FromNapiValue, Result, Status, ToNapiValue, TypeName,
11    Unknown, ValidateNapiValue,
12  },
13  check_status, check_status_or_throw, Error, JsExternal,
14};
15
16#[repr(C)]
17pub struct External<T: 'static> {
18  type_id: TypeId,
19  obj: T,
20  size_hint: usize,
21  pub adjusted_size: i64,
22}
23
24impl<T: 'static> TypeName for &External<T> {
25  fn type_name() -> &'static str {
26    "External"
27  }
28
29  fn value_type() -> crate::ValueType {
30    crate::ValueType::External
31  }
32}
33
34impl<T: 'static> TypeName for &mut External<T> {
35  fn type_name() -> &'static str {
36    "External"
37  }
38
39  fn value_type() -> crate::ValueType {
40    crate::ValueType::External
41  }
42}
43
44impl<T: 'static> From<T> for External<T> {
45  fn from(t: T) -> Self {
46    External::new(t)
47  }
48}
49
50impl<T: 'static> ValidateNapiValue for &External<T> {}
51
52impl<T: 'static> ValidateNapiValue for &mut External<T> {}
53
54impl<T: 'static> External<T> {
55  pub fn new(value: T) -> Self {
56    Self {
57      type_id: TypeId::of::<T>(),
58      obj: value,
59      size_hint: 0,
60      adjusted_size: 0,
61    }
62  }
63
64  /// Turn a raw pointer (from napi) pointing to an External into a reference to the inner object.
65  ///
66  /// # Safety
67  /// The `unknown_tagged_object` raw pointer must point to an `External<T>` struct.
68  pub(crate) unsafe fn from_raw_impl(
69    unknown_tagged_object: *mut c_void,
70  ) -> Option<&'static mut Self> {
71    let type_id = unknown_tagged_object as *const TypeId;
72    if unsafe { *type_id } == TypeId::of::<T>() {
73      let tagged_object = unknown_tagged_object as *mut External<T>;
74      Some(Box::leak(unsafe { Box::from_raw(tagged_object) }))
75    } else {
76      None
77    }
78  }
79
80  /// Turn a raw pointer (from napi) pointing to an External into a mutable reference to the inner object.
81  ///
82  /// # Safety
83  /// The `unknown_tagged_object` raw pointer must point to an `External<T>` struct.
84  pub unsafe fn inner_from_raw_mut(unknown_tagged_object: *mut c_void) -> Option<&'static mut T> {
85    Self::from_raw_impl(unknown_tagged_object).map(|external| &mut external.obj)
86  }
87
88  /// Turn a raw pointer (from napi) pointing to an External into a reference inner object.
89  ///
90  /// # Safety
91  /// The `unknown_tagged_object` raw pointer must point to an `External<T>` struct.
92  pub unsafe fn inner_from_raw(unknown_tagged_object: *mut c_void) -> Option<&'static T> {
93    Self::from_raw_impl(unknown_tagged_object).map(|external| &external.obj)
94  }
95
96  /// `size_hint` is a value to tell Node.js GC how much memory is used by this `External` object.
97  ///
98  /// If getting the exact `size_hint` is difficult, you can provide an approximate value, it's only effect to the GC.
99  ///
100  /// If your `External` object is not effect to GC, you can use `External::new` instead.
101  pub fn new_with_size_hint(value: T, size_hint: usize) -> Self {
102    Self {
103      type_id: TypeId::of::<T>(),
104      obj: value,
105      size_hint,
106      adjusted_size: 0,
107    }
108  }
109
110  /// convert `External<T>` to `Unknown`
111  pub fn into_unknown(self, env: &Env) -> Result<Unknown<'_>> {
112    let napi_value = unsafe { ToNapiValue::to_napi_value(env.0, self)? };
113    Ok(unsafe { Unknown::from_raw_unchecked(env.0, napi_value) })
114  }
115
116  /// Convert `External<T>` to `JsExternal`
117  pub fn into_js_external(self, env: &Env) -> Result<JsExternal<'_>> {
118    let napi_value = unsafe { ToNapiValue::to_napi_value(env.0, self)? };
119    unsafe { JsExternal::from_napi_value(env.0, napi_value) }
120  }
121
122  #[allow(clippy::wrong_self_convention)]
123  unsafe fn to_napi_value_impl(
124    self,
125    env: sys::napi_env,
126  ) -> Result<(sys::napi_value, *mut External<T>)> {
127    let mut napi_value = ptr::null_mut();
128    let size_hint = self.size_hint as i64;
129    let size_hint_ptr = Box::into_raw(Box::new(size_hint));
130    let obj_ptr = Box::into_raw(Box::new(self));
131    check_status!(
132      unsafe {
133        sys::napi_create_external(
134          env,
135          obj_ptr.cast(),
136          Some(crate::raw_finalize::<External<T>>),
137          size_hint_ptr.cast(),
138          &mut napi_value,
139        )
140      },
141      "Create external value failed"
142    )?;
143
144    #[cfg(not(target_family = "wasm"))]
145    {
146      let mut adjusted_external_memory_size = std::mem::MaybeUninit::new(0);
147
148      if size_hint != 0 {
149        check_status!(
150          unsafe {
151            sys::napi_adjust_external_memory(
152              env,
153              size_hint,
154              adjusted_external_memory_size.as_mut_ptr(),
155            )
156          },
157          "Adjust external memory failed"
158        )?;
159      };
160
161      (Box::leak(unsafe { Box::from_raw(obj_ptr) })).adjusted_size =
162        unsafe { adjusted_external_memory_size.assume_init() };
163    }
164
165    Ok((napi_value, obj_ptr))
166  }
167}
168
169impl<T: 'static> FromNapiMutRef for External<T> {
170  unsafe fn from_napi_mut_ref(
171    env: sys::napi_env,
172    napi_val: sys::napi_value,
173  ) -> crate::Result<&'static mut Self> {
174    let mut unknown_tagged_object = ptr::null_mut();
175    check_status!(
176      unsafe { sys::napi_get_value_external(env, napi_val, &mut unknown_tagged_object) },
177      "Failed to get external value"
178    )?;
179
180    match Self::from_raw_impl(unknown_tagged_object) {
181      Some(external) => Ok(external),
182      None => Err(Error::new(
183        Status::InvalidArg,
184        format!(
185          "<{}> on `External` is not the type of wrapped object",
186          std::any::type_name::<T>()
187        ),
188      )),
189    }
190  }
191}
192
193impl<T: 'static> FromNapiRef for External<T> {
194  unsafe fn from_napi_ref(
195    env: sys::napi_env,
196    napi_val: sys::napi_value,
197  ) -> crate::Result<&'static Self> {
198    unsafe { Self::from_napi_mut_ref(env, napi_val) }.map(|v| v as &Self)
199  }
200}
201
202impl<T: 'static> AsRef<T> for External<T> {
203  fn as_ref(&self) -> &T {
204    &self.obj
205  }
206}
207
208impl<T: 'static> AsMut<T> for External<T> {
209  fn as_mut(&mut self) -> &mut T {
210    &mut self.obj
211  }
212}
213
214impl<T: 'static> Deref for External<T> {
215  type Target = T;
216
217  fn deref(&self) -> &Self::Target {
218    self.as_ref()
219  }
220}
221
222impl<T: 'static> DerefMut for External<T> {
223  fn deref_mut(&mut self) -> &mut Self::Target {
224    self.as_mut()
225  }
226}
227
228impl<T: 'static> ToNapiValue for External<T> {
229  unsafe fn to_napi_value(env: sys::napi_env, val: Self) -> crate::Result<sys::napi_value> {
230    let (napi_value, _) = unsafe { val.to_napi_value_impl(env)? };
231    Ok(napi_value)
232  }
233}
234
235/// `ExternalRef` is a reference to an `External` object
236pub struct ExternalRef<T: 'static> {
237  pub(crate) obj: &'static mut External<T>,
238  pub(crate) raw: sys::napi_ref,
239  pub(crate) env: sys::napi_env,
240}
241
242unsafe impl<T: Sync + 'static> Sync for ExternalRef<T> {}
243
244impl<T: 'static> TypeName for ExternalRef<T> {
245  fn type_name() -> &'static str {
246    "External"
247  }
248
249  fn value_type() -> crate::ValueType {
250    crate::ValueType::External
251  }
252}
253
254impl<T: 'static> ValidateNapiValue for ExternalRef<T> {}
255
256impl<T: 'static> Drop for ExternalRef<T> {
257  fn drop(&mut self) {
258    check_status_or_throw!(
259      self.env,
260      unsafe { sys::napi_delete_reference(self.env, self.raw) },
261      "Failed to delete reference on external value"
262    );
263  }
264}
265
266impl<T: 'static> ExternalRef<T> {
267  pub fn new(env: &Env, value: T) -> Result<Self> {
268    let external = External::new(value);
269    let mut ref_ptr = ptr::null_mut();
270    let (napi_val, external) = unsafe { external.to_napi_value_impl(env.0)? };
271    check_status!(
272      unsafe { sys::napi_create_reference(env.0, napi_val, 1, &mut ref_ptr) },
273      "Failed to create reference on external value"
274    )?;
275    Ok(ExternalRef {
276      obj: Box::leak(unsafe { Box::from_raw(external) }),
277      raw: ref_ptr,
278      env: env.0,
279    })
280  }
281
282  /// Get the raw JsExternal value from the reference
283  pub fn get_value(&self) -> Result<JsExternal<'_>> {
284    let mut napi_val = ptr::null_mut();
285    check_status!(
286      unsafe { sys::napi_get_reference_value(self.env, self.raw, &mut napi_val) },
287      "Failed to get reference value on external value"
288    )?;
289    unsafe { JsExternal::from_napi_value(self.env, napi_val) }
290  }
291}
292
293impl<T: 'static> FromNapiValue for ExternalRef<T> {
294  unsafe fn from_napi_value(env: sys::napi_env, napi_val: sys::napi_value) -> crate::Result<Self> {
295    let mut unknown_tagged_object = ptr::null_mut();
296    check_status!(
297      unsafe { sys::napi_get_value_external(env, napi_val, &mut unknown_tagged_object) },
298      "Failed to get external value"
299    )?;
300
301    let type_id = unknown_tagged_object as *const TypeId;
302    let external = if unsafe { *type_id } == TypeId::of::<T>() {
303      let tagged_object = unknown_tagged_object as *mut External<T>;
304      Box::leak(unsafe { Box::from_raw(tagged_object) })
305    } else {
306      return Err(Error::new(
307        Status::InvalidArg,
308        format!(
309          "<{}> on `External` is not the type of wrapped object",
310          std::any::type_name::<T>()
311        ),
312      ));
313    };
314
315    let mut ref_ptr = ptr::null_mut();
316    check_status!(
317      unsafe { sys::napi_create_reference(env, napi_val, 1, &mut ref_ptr) },
318      "Failed to create reference on external value"
319    )?;
320
321    Ok(ExternalRef {
322      obj: external,
323      raw: ref_ptr,
324      env,
325    })
326  }
327}
328
329impl<T: 'static> ToNapiValue for ExternalRef<T> {
330  unsafe fn to_napi_value(env: sys::napi_env, val: Self) -> crate::Result<sys::napi_value> {
331    let mut value = ptr::null_mut();
332    check_status!(
333      unsafe { sys::napi_get_reference_value(env, val.raw, &mut value) },
334      "Failed to get reference value on external value"
335    )?;
336    Ok(value)
337  }
338}
339
340impl<T: 'static> ToNapiValue for &ExternalRef<T> {
341  unsafe fn to_napi_value(env: sys::napi_env, val: Self) -> crate::Result<sys::napi_value> {
342    let mut value = ptr::null_mut();
343    check_status!(
344      unsafe { sys::napi_get_reference_value(env, val.raw, &mut value) },
345      "Failed to get reference value on external value"
346    )?;
347    Ok(value)
348  }
349}
350
351impl<T: 'static> Deref for ExternalRef<T> {
352  type Target = T;
353
354  fn deref(&self) -> &Self::Target {
355    self.obj
356  }
357}
358
359impl<T: 'static> DerefMut for ExternalRef<T> {
360  fn deref_mut(&mut self) -> &mut Self::Target {
361    self.obj
362  }
363}