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