napi/bindgen_runtime/js_values/
external.rs1use 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 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 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 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 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 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 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
235pub 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 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}