rquickjs_core/
value.rs

1use crate::{qjs, Ctx, Error, Result};
2use core::{fmt, hash::Hash, mem, ops::Deref, result::Result as StdResult, str};
3
4pub mod array;
5pub mod atom;
6mod bigint;
7pub mod convert;
8pub(crate) mod exception;
9pub mod function;
10pub mod module;
11pub mod object;
12pub mod promise;
13mod string;
14mod symbol;
15
16pub use array::Array;
17pub use atom::Atom;
18pub use bigint::BigInt;
19pub use convert::{Coerced, FromAtom, FromIteratorJs, FromJs, IntoAtom, IntoJs, IteratorJs};
20pub use exception::Exception;
21pub use function::{Constructor, Function};
22pub use module::{Module, WriteOptions, WriteOptionsEndianness};
23pub use object::{Filter, Object};
24pub use promise::Promise;
25pub use string::{CString, String};
26pub use symbol::Symbol;
27
28pub mod array_buffer;
29pub mod typed_array;
30
31pub use array_buffer::ArrayBuffer;
32pub use typed_array::TypedArray;
33
34/// Any JavaScript value
35pub struct Value<'js> {
36    pub(crate) ctx: Ctx<'js>,
37    pub(crate) value: qjs::JSValue,
38}
39
40impl<'js> PartialEq for Value<'js> {
41    fn eq(&self, other: &Self) -> bool {
42        let tag = unsafe { qjs::JS_VALUE_GET_TAG(self.value) };
43        let tag_other = unsafe { qjs::JS_VALUE_GET_TAG(other.value) };
44
45        let bits = unsafe { qjs::JS_VALUE_GET_FLOAT64(self.value).to_bits() };
46        let bits_other = unsafe { qjs::JS_VALUE_GET_FLOAT64(other.value).to_bits() };
47
48        tag == tag_other && bits == bits_other
49    }
50}
51
52impl<'js> Eq for Value<'js> {}
53
54impl<'js> Hash for Value<'js> {
55    fn hash<H: core::hash::Hasher>(&self, state: &mut H) {
56        let tag = unsafe { qjs::JS_VALUE_GET_TAG(self.value) };
57        let bits = unsafe { qjs::JS_VALUE_GET_FLOAT64(self.value).to_bits() };
58        state.write_i32(tag);
59        state.write_u64(bits)
60    }
61}
62
63impl<'js> Clone for Value<'js> {
64    fn clone(&self) -> Self {
65        let ctx = self.ctx.clone();
66        let value = unsafe { qjs::JS_DupValue(ctx.as_ptr(), self.value) };
67        Self { ctx, value }
68    }
69}
70
71impl<'js> Drop for Value<'js> {
72    fn drop(&mut self) {
73        unsafe {
74            qjs::JS_FreeValue(self.ctx.as_ptr(), self.value);
75        }
76    }
77}
78
79impl<'js> fmt::Debug for Value<'js> {
80    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
81        let type_ = self.type_of();
82        type_.fmt(f)?;
83        use Type::*;
84        match type_ {
85            Bool | Int | Float => {
86                write!(f, "(")?;
87                match type_ {
88                    Bool => unsafe { self.get_bool() }.fmt(f)?,
89                    Int => unsafe { self.get_int() }.fmt(f)?,
90                    Float => unsafe { self.get_float() }.fmt(f)?,
91                    _ => unreachable!(),
92                }
93                write!(f, ")")?;
94            }
95            String => {
96                write!(f, "(")?;
97                unsafe { self.ref_string() }.to_string().fmt(f)?;
98                write!(f, ")")?;
99            }
100            Symbol | Object | Array | Function | Constructor | Promise => {
101                write!(f, "(")?;
102                unsafe { self.get_ptr() }.fmt(f)?;
103                write!(f, ")")?;
104            }
105            Exception => {
106                writeln!(f, "(")?;
107                self.as_exception().unwrap().fmt(f)?;
108                writeln!(f, ")")?;
109            }
110            Null => "null".fmt(f)?,
111            Undefined => "undefined".fmt(f)?,
112            Uninitialized => "uninitialized".fmt(f)?,
113            Module => "module".fmt(f)?,
114            BigInt => "BigInt".fmt(f)?,
115            Unknown => "unknown".fmt(f)?,
116        }
117        Ok(())
118    }
119}
120
121impl<'js> Value<'js> {
122    // unsafe because the value must belong the context and the lifetime must be constrained by its lifetime
123    #[inline]
124    pub(crate) unsafe fn from_js_value(ctx: Ctx<'js>, value: qjs::JSValue) -> Self {
125        Self { ctx, value }
126    }
127
128    #[inline]
129    pub(crate) unsafe fn from_js_value_const(ctx: Ctx<'js>, value: qjs::JSValueConst) -> Self {
130        let value = qjs::JS_DupValue(ctx.as_ptr(), value);
131        Self { ctx, value }
132    }
133
134    #[inline]
135    pub(crate) fn as_js_value(&self) -> qjs::JSValueConst {
136        self.value
137    }
138
139    #[inline]
140    pub(crate) fn into_js_value(self) -> qjs::JSValue {
141        let value = self.value;
142        unsafe { qjs::JS_FreeContext(self.ctx.as_ptr()) };
143        mem::forget(self);
144        value
145    }
146
147    #[inline]
148    pub fn new_uninitialized(ctx: Ctx<'js>) -> Self {
149        let value = qjs::JS_UNINITIALIZED;
150        Self { ctx, value }
151    }
152
153    #[inline]
154    pub fn new_undefined(ctx: Ctx<'js>) -> Self {
155        let value = qjs::JS_UNDEFINED;
156        Self { ctx, value }
157    }
158
159    #[inline]
160    pub fn new_null(ctx: Ctx<'js>) -> Self {
161        let value = qjs::JS_NULL;
162        Self { ctx, value }
163    }
164
165    /// Create new boolean value
166    #[inline]
167    pub fn new_bool(ctx: Ctx<'js>, value: bool) -> Self {
168        let value = if value { qjs::JS_TRUE } else { qjs::JS_FALSE };
169        Self { ctx, value }
170    }
171
172    /// Returns the Ctx object associated with this value.
173    #[inline]
174    pub fn ctx(&self) -> &Ctx<'js> {
175        &self.ctx
176    }
177
178    // unsafe because no type checking
179    #[inline]
180    pub(crate) unsafe fn get_bool(&self) -> bool {
181        qjs::JS_VALUE_GET_BOOL(self.value)
182    }
183
184    /// Try get bool from value
185    pub fn as_bool(&self) -> Option<bool> {
186        if self.is_bool() {
187            Some(unsafe { self.get_bool() })
188        } else {
189            None
190        }
191    }
192
193    /// Create new int value
194    #[inline]
195    pub fn new_int(ctx: Ctx<'js>, value: i32) -> Self {
196        let value = qjs::JS_MKVAL(qjs::JS_TAG_INT, value);
197        Self { ctx, value }
198    }
199
200    #[inline]
201    pub(crate) unsafe fn get_int(&self) -> i32 {
202        qjs::JS_VALUE_GET_INT(self.value)
203    }
204
205    /// Try get int from value
206    pub fn as_int(&self) -> Option<i32> {
207        if self.is_int() {
208            Some(unsafe { self.get_int() })
209        } else {
210            None
211        }
212    }
213
214    /// Create new float value
215    #[inline]
216    pub fn new_float(ctx: Ctx<'js>, value: f64) -> Self {
217        let value = qjs::JS_NewFloat64(value);
218        Self { ctx, value }
219    }
220
221    #[inline]
222    pub(crate) unsafe fn get_float(&self) -> f64 {
223        qjs::JS_VALUE_GET_FLOAT64(self.value)
224    }
225
226    /// Try get float from value
227    pub fn as_float(&self) -> Option<f64> {
228        if self.is_float() {
229            Some(unsafe { self.get_float() })
230        } else {
231            None
232        }
233    }
234
235    /// Create a new number value
236    #[inline]
237    pub fn new_big_int(ctx: Ctx<'js>, value: i64) -> Self {
238        let value = unsafe { qjs::JS_NewBigInt64(ctx.as_ptr(), value) };
239        Self { ctx, value }
240    }
241
242    /// Create a new number value
243    #[inline]
244    pub fn new_number(ctx: Ctx<'js>, value: f64) -> Self {
245        let int = value as i32;
246        #[allow(clippy::float_cmp)]
247        // This is safe and fast in that case
248        let value = if value == int as f64 {
249            qjs::JS_MKVAL(qjs::JS_TAG_INT, int)
250        } else {
251            qjs::JS_NewFloat64(value)
252        };
253        Self { ctx, value }
254    }
255
256    /// Try get any number from value
257    pub fn as_number(&self) -> Option<f64> {
258        if self.is_int() {
259            Some(unsafe { self.get_int() as _ })
260        } else if self.is_float() {
261            Some(unsafe { self.get_float() })
262        } else {
263            None
264        }
265    }
266
267    #[allow(unused)]
268    #[inline]
269    pub(crate) fn new_ptr(ctx: Ctx<'js>, tag: qjs::c_int, ptr: *mut qjs::c_void) -> Self {
270        let value = qjs::JS_MKPTR(tag, ptr);
271        Self { ctx, value }
272    }
273
274    #[allow(unused)]
275    #[inline]
276    pub(crate) fn new_ptr_const(ctx: Ctx<'js>, tag: qjs::c_int, ptr: *mut qjs::c_void) -> Self {
277        let value = unsafe { qjs::JS_DupValue(ctx.as_ptr(), qjs::JS_MKPTR(tag, ptr)) };
278        Self { ctx, value }
279    }
280
281    #[inline]
282    pub(crate) unsafe fn get_ptr(&self) -> *mut qjs::c_void {
283        qjs::JS_VALUE_GET_PTR(self.value)
284    }
285
286    /// Returns if the value is the JavaScript null value.
287    #[inline]
288    pub fn is_null(&self) -> bool {
289        let tag = unsafe { qjs::JS_VALUE_GET_NORM_TAG(self.value) };
290        qjs::JS_TAG_NULL == tag
291    }
292
293    /// Returns if the value is the JavaScript undefined value.
294    #[inline]
295    pub fn is_undefined(&self) -> bool {
296        let tag = unsafe { qjs::JS_VALUE_GET_NORM_TAG(self.value) };
297        qjs::JS_TAG_UNDEFINED == tag
298    }
299
300    /// Check if the value is a bool
301    #[inline]
302    pub fn is_bool(&self) -> bool {
303        qjs::JS_TAG_BOOL == unsafe { qjs::JS_VALUE_GET_TAG(self.value) }
304    }
305
306    /// Check if the value is an int
307    #[inline]
308    pub fn is_int(&self) -> bool {
309        qjs::JS_TAG_INT == unsafe { qjs::JS_VALUE_GET_TAG(self.value) }
310    }
311
312    /// Check if the value is a float
313    #[inline]
314    pub fn is_float(&self) -> bool {
315        qjs::JS_TAG_FLOAT64 == unsafe { qjs::JS_VALUE_GET_NORM_TAG(self.value) }
316    }
317
318    /// Check if the value is an any number
319    #[inline]
320    pub fn is_number(&self) -> bool {
321        let tag = unsafe { qjs::JS_VALUE_GET_NORM_TAG(self.value) };
322        qjs::JS_TAG_INT == tag || qjs::JS_TAG_FLOAT64 == tag
323    }
324
325    /// Check if the value is a string
326    #[inline]
327    pub fn is_string(&self) -> bool {
328        qjs::JS_TAG_STRING == unsafe { qjs::JS_VALUE_GET_TAG(self.value) }
329    }
330
331    /// Check if the value is a symbol
332    #[inline]
333    pub fn is_symbol(&self) -> bool {
334        qjs::JS_TAG_SYMBOL == unsafe { qjs::JS_VALUE_GET_TAG(self.value) }
335    }
336
337    /// Check if the value is an object
338    #[inline]
339    pub fn is_object(&self) -> bool {
340        qjs::JS_TAG_OBJECT == unsafe { qjs::JS_VALUE_GET_TAG(self.value) }
341    }
342
343    /// Check if the value is a module
344    #[inline]
345    pub fn is_module(&self) -> bool {
346        qjs::JS_TAG_MODULE == unsafe { qjs::JS_VALUE_GET_TAG(self.value) }
347    }
348
349    /// Check if the value is an array
350    #[inline]
351    pub fn is_array(&self) -> bool {
352        unsafe { qjs::JS_IsArray(self.value) }
353    }
354
355    /// Check if the value is a function
356    #[inline]
357    pub fn is_function(&self) -> bool {
358        (unsafe { qjs::JS_IsFunction(self.ctx.as_ptr(), self.value) } as i32) != 0
359    }
360
361    /// Check if the value is a constructor function
362    #[inline]
363    pub fn is_constructor(&self) -> bool {
364        (unsafe { qjs::JS_IsConstructor(self.ctx.as_ptr(), self.value) } as i32) != 0
365    }
366
367    /// Check if the value is a promise.
368    #[inline]
369    pub fn is_promise(&self) -> bool {
370        (unsafe { qjs::JS_PromiseState(self.ctx.as_ptr(), self.value) } as core::ffi::c_int) >= 0
371    }
372
373    /// Check if the value is an exception
374    #[inline]
375    pub fn is_exception(&self) -> bool {
376        unsafe { qjs::JS_IsException(self.value) }
377    }
378
379    /// Check if the value is an error
380    #[inline]
381    pub fn is_error(&self) -> bool {
382        (unsafe { qjs::JS_IsError(self.value) } as i32) != 0
383    }
384
385    /// Check if the value is a BigInt
386    #[inline]
387    pub fn is_big_int(&self) -> bool {
388        unsafe { qjs::JS_IsBigInt(self.value) }
389    }
390
391    /// Reference as value
392    #[inline]
393    pub fn as_value(&self) -> &Self {
394        self
395    }
396
397    #[inline]
398    pub(crate) fn into_value(self) -> Self {
399        self
400    }
401
402    /// Convert from value to specified type
403    pub fn get<T: FromJs<'js>>(&self) -> Result<T> {
404        T::from_js(self.ctx(), self.clone())
405    }
406
407    /// Returns the raw C library JavaScript value.
408    pub fn as_raw(&self) -> qjs::JSValue {
409        self.value
410    }
411
412    /// Create a value from the C library JavaScript value.
413    ///
414    /// # Safety
415    /// The value cannot be from an unrelated runtime and the value must be owned.
416    /// QuickJS JavaScript values are reference counted. The drop implementation of this type
417    /// decrements the reference count so the value must have count which won't be decremented
418    /// elsewhere. Use [`qjs::JS_DupValue`] to increment the reference count of the value.
419    pub unsafe fn from_raw(ctx: Ctx<'js>, value: qjs::JSValue) -> Self {
420        Self::from_js_value(ctx, value)
421    }
422}
423
424impl<'js> AsRef<Value<'js>> for Value<'js> {
425    fn as_ref(&self) -> &Value<'js> {
426        self
427    }
428}
429
430macro_rules! type_impls {
431    // type: name => tag
432    ($($type:ident: $name:ident => $($tag:ident)|+,)*) => {
433        /// The type of JavaScript value
434        #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
435        #[repr(u8)]
436        pub enum Type {
437            $($type,)*
438            Unknown
439        }
440
441        impl Type {
442            /// Returns true if the type is one of `uninitialized`, `undefined` or `null`
443            pub const fn is_void(self) -> bool {
444                use Type::*;
445                matches!(self, Uninitialized | Undefined | Null)
446            }
447
448            /// Check the type for similarity
449            pub const fn interpretable_as(self, other: Self) -> bool {
450                use Type::*;
451
452                if (self as u8) == (other as u8){
453                    return true
454                }
455                match other{
456                    Float => matches!(self, Int),
457                    Object => matches!(self, Array | Function | Constructor | Exception | Promise),
458                    Function => matches!(self, Constructor),
459                    _ => false
460                }
461            }
462
463            /// Returns string representation of type
464            pub const fn as_str(self) -> &'static str {
465                match self {
466                    $(Type::$type => stringify!($name),)*
467                    Type::Unknown => "Unknown type",
468                }
469            }
470        }
471
472        impl AsRef<str> for Type {
473            fn as_ref(&self) -> &str {
474                self.as_str()
475            }
476        }
477
478        impl fmt::Display for Type {
479            fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
480                self.as_str().fmt(f)
481            }
482        }
483
484        impl str::FromStr for Type {
485            type Err = ();
486
487            fn from_str(s: &str) -> StdResult<Self, Self::Err> {
488                Ok(match s {
489                    $(stringify!($name) => Type::$type,)*
490                    _ => return Err(()),
491                })
492            }
493        }
494
495        impl<'js> Value<'js> {
496            /// Get the type of value
497            pub fn type_of(&self) -> Type {
498                let tag = unsafe { qjs::JS_VALUE_GET_NORM_TAG(self.value) };
499                match tag {
500                    $($(qjs::$tag)|+ if type_impls!(@cond $type self) => Type::$type,)*
501                    _ => Type::Unknown,
502                }
503            }
504
505            /// Get the name of type
506            pub fn type_name(&self) -> &'static str {
507                self.type_of().as_str()
508            }
509        }
510    };
511
512    (@cond Array $self:expr) => { $self.is_array() };
513    (@cond Constructor $self:expr) => { $self.is_constructor() };
514    (@cond Function $self:expr) => { $self.is_function() };
515    (@cond Promise $self:expr) => { $self.is_promise() };
516    (@cond Exception $self:expr) => { $self.is_error() };
517    (@cond $type:ident $self:expr) => { true };
518}
519
520type_impls! {
521    Uninitialized: uninitialized => JS_TAG_UNINITIALIZED,
522    Undefined: undefined => JS_TAG_UNDEFINED,
523    Null: null => JS_TAG_NULL,
524    Bool: bool => JS_TAG_BOOL,
525    Int: int => JS_TAG_INT,
526    Float: float => JS_TAG_FLOAT64,
527    String: string => JS_TAG_STRING,
528    Symbol: symbol => JS_TAG_SYMBOL,
529    Array: array => JS_TAG_OBJECT,
530    Constructor: constructor => JS_TAG_OBJECT,
531    Function: function => JS_TAG_OBJECT,
532    Promise: promise => JS_TAG_OBJECT,
533    Exception: exception => JS_TAG_OBJECT,
534    Object: object => JS_TAG_OBJECT,
535    Module: module => JS_TAG_MODULE,
536    BigInt: big_int => JS_TAG_BIG_INT | JS_TAG_SHORT_BIG_INT,
537}
538
539macro_rules! sub_types {
540    ($( $head:ident$(->$sub_type:ident)* $as:ident $ref:ident $into:ident $try_into:ident $from:ident,)*) => {
541        $(
542            impl<'js> $head<'js> {
543                /// Reference to value
544                #[inline]
545                pub fn as_value(&self) -> &Value<'js> {
546                    &self.0.as_value()
547                }
548
549                /// Convert into value
550                #[inline]
551                pub fn into_value(self) -> Value<'js> {
552                    self.0.into_value()
553                }
554
555                /// Returns the underlying super type.
556                pub fn into_inner(self) -> sub_types!(@head_ty $($sub_type),*) {
557                    self.0
558                }
559                /// Returns a reference to the underlying super type.
560                pub fn as_inner(&self) -> & sub_types!(@head_ty $($sub_type),*) {
561                    &self.0
562                }
563
564                /// Returns the [`Ctx`] object associated with this value
565                pub fn ctx(&self) -> &Ctx<'js>{
566                    self.0.ctx()
567                }
568
569                /// Convert from value
570                pub fn from_value(value: Value<'js>) -> Result<Self> {
571                    let type_ = value.type_of();
572                    if type_.interpretable_as(Type::$head) {
573                        Ok(sub_types!(@wrap $head$(->$sub_type)*  value))
574                    } else {
575                        Err(Error::new_from_js(type_.as_str(), Type::$head.as_str()))
576                    }
577                }
578
579                #[allow(unused)]
580                pub(crate) unsafe fn from_js_value_const(ctx: Ctx<'js>, value: qjs::JSValueConst) -> Self {
581                    let v = Value::from_js_value_const(ctx, value);
582                    debug_assert!(v.$as().is_some(),"tried to cource js value {:?} to the wrong type `{}`, this is a rquickjs bug",v, stringify!($head));
583                    sub_types!(@wrap $head$(->$sub_type)*  v)
584                }
585
586                #[allow(unused)]
587                pub(crate) unsafe fn from_js_value(ctx: Ctx<'js>, value: qjs::JSValue) -> Self {
588                    let v = Value::from_js_value(ctx, value);
589                    debug_assert!(v.$as().is_some(),"tried to cource js value {:?} to the wrong type `{}`, this is a rquickjs bug",v, stringify!($head));
590                    sub_types!(@wrap $head$(->$sub_type)*  v)
591                }
592
593                #[allow(unused)]
594                pub(crate) fn into_js_value(self) -> qjs::JSValue{
595                    self.0.into_js_value()
596                }
597
598                #[allow(unused)]
599                pub(crate) fn as_js_value(&self) -> qjs::JSValueConst{
600                    self.0.as_js_value()
601                }
602            }
603
604            impl<'js> Deref for $head<'js> {
605                type Target = sub_types!(@head_ty $($sub_type),*);
606
607                fn deref(&self) -> &Self::Target {
608                    &self.0
609                }
610            }
611
612            sub_types!(@imp_as_ref $head$(,$sub_type)*);
613
614            impl<'js> Value<'js> {
615                #[doc = concat!("Interpret as [`",stringify!($head),"`]")]
616                ///
617                /// # Safety
618                /// You should be sure that the value already is of required type before to do it.
619                #[inline]
620                pub unsafe fn $ref(&self) -> &$head<'js> {
621                    &*(self as *const _ as *const $head)
622                }
623
624                #[doc = concat!("Try reinterpret as [`",stringify!($head),"`]")]
625                pub fn $as(&self) -> Option<&$head<'js>> {
626                    if self.type_of().interpretable_as(Type::$head) {
627                        Some(unsafe { self.$ref() })
628                    } else {
629                        None
630                    }
631                }
632
633                #[doc = concat!("Try convert into [`",stringify!($head),"`]")]
634                pub fn $into(self) -> Option<$head<'js>> {
635                    if self.type_of().interpretable_as(Type::$head) {
636                        Some(sub_types!(@wrap $head$(->$sub_type)* self))
637                    } else {
638                        None
639                    }
640                }
641
642                #[doc = concat!("Try convert into [`",stringify!($head),"`] returning self if the conversion fails.")]
643                pub fn $try_into(self) -> core::result::Result<$head<'js>, Value<'js>> {
644                    if self.type_of().interpretable_as(Type::$head) {
645                        Ok(sub_types!(@wrap $head$(->$sub_type)* self))
646                    } else {
647                        Err(self)
648                    }
649                }
650
651                #[doc = concat!("Convert from [`",stringify!($head),"`]")]
652                pub fn $from(value: $head<'js>) -> Self {
653                    value.into_value()
654                }
655            }
656
657            impl<'js> From<$head<'js>> for Value<'js> {
658                fn from(value: $head<'js>) -> Self {
659                    value.into_value()
660                }
661            }
662
663            impl<'js> FromJs<'js> for $head<'js> {
664                fn from_js(_: &Ctx<'js>, value: Value<'js>) -> Result<Self> {
665                    Self::from_value(value)
666                }
667            }
668
669            impl<'js> IntoJs<'js> for $head<'js> {
670                fn into_js(self, _ctx: &Ctx<'js>) -> Result<Value<'js>> {
671                    Ok(self.into_value())
672                }
673            }
674
675            impl<'js> IntoAtom<'js> for $head<'js>{
676                fn into_atom(self, ctx: &Ctx<'js>) -> Result<Atom<'js>> {
677                    Atom::from_value(ctx.clone(), &self.into_value())
678                }
679            }
680        )*
681    };
682
683    (@type $type:ident) => { $type<'js> };
684
685    (@head $head:ident $(rem:ident)*)  => { $head };
686    (@head_ty $head:ident$(,$rem:ident)*)  => { $head<'js> };
687
688    (@wrap $type:ident$(->$rem:ident)+ $val:expr) => { $type(sub_types!(@wrap $($rem)->* $val)) };
689    (@wrap Value $val:expr) => { $val };
690
691    /*
692    (@into_inner $head:ident->$inner:ident$(->$rem:ident)*) => {
693        impl<'js> $head<'js>{
694            pub fn into_inner($
695        }
696    }*/
697
698    (@imp_as_ref $type:ident,Value) => {
699        impl<'js> AsRef<Value<'js>> for $type<'js> {
700            fn as_ref(&self) -> &Value<'js> {
701                &self.0
702            }
703        }
704    };
705    (@imp_as_ref $type:ident,$inner:ident$(,$rem:ident)*) => {
706        impl<'js> AsRef<$inner<'js>> for $type<'js> {
707            fn as_ref(&self) -> &$inner<'js> {
708                &self.0
709            }
710        }
711
712        impl<'js> AsRef<Value<'js>> for $type<'js> {
713            fn as_ref(&self) -> &Value<'js> {
714                self.0.as_ref()
715            }
716        }
717    };
718}
719
720sub_types! {
721    String->Value as_string ref_string into_string try_into_string from_string,
722    Symbol->Value as_symbol ref_symbol into_symbol try_into_symbol from_symbol,
723    Object->Value as_object ref_object into_object try_into_object from_object,
724    Function->Object->Value as_function ref_function into_function try_into_function from_function,
725    Constructor->Function->Object->Value as_constructor ref_constructor into_constructor try_into_constructor from_constructor,
726    Promise->Object->Value as_promise ref_promise into_promise try_into_promise from_promise,
727    Array->Object->Value as_array ref_array into_array try_into_array from_array,
728    Exception->Object->Value as_exception ref_exception into_exception try_into_exception from_exception,
729    BigInt->Value as_big_int ref_big_int into_big_int try_into_big_int from_big_int,
730}
731
732macro_rules! void_types {
733    ($($(#[$meta:meta])* $type:ident $new:ident;)*) => {
734        $(
735            $(#[$meta])*
736            #[derive(Debug, Copy, Clone, PartialEq, Eq)]
737            #[allow(dead_code)]
738            pub struct $type;
739
740            #[allow(dead_code)]
741            impl $type {
742                /// Convert into value
743                pub fn into_value<'js>(self, ctx: Ctx<'js>) -> Value<'js> {
744                    Value::$new(ctx)
745                }
746
747                /// Convert from value
748                pub fn from_value<'js>(value: Value<'js>) -> Result<Self> {
749                    if value.type_of() == Type::$type {
750                        Ok(Self)
751                    } else {
752                        Err(Error::new_from_js("value", Type::$type.as_str()))
753                    }
754                }
755            }
756
757            impl<'js> FromJs<'js> for $type {
758                fn from_js(_: &Ctx<'js>, value: Value<'js>) -> Result<Self> {
759                    Self::from_value(value)
760                }
761            }
762
763            impl<'js> IntoJs<'js> for $type {
764                fn into_js(self, ctx: &Ctx<'js>) -> Result<Value<'js>> {
765                    Ok(self.into_value(ctx.clone()))
766                }
767            }
768        )*
769    };
770}
771
772void_types! {
773    /// The placeholder which treated as uninitialized JS value
774    Uninitialized new_uninitialized;
775
776    /// The placeholder which treated as `undefined` value
777    Undefined new_undefined;
778
779    /// The placeholder which treated as `null` value
780    Null new_null;
781}
782
783#[cfg(test)]
784mod test {
785    use crate::*;
786
787    #[test]
788    fn type_matches() {
789        assert!(Type::Bool.interpretable_as(Type::Bool));
790
791        assert!(Type::Object.interpretable_as(Type::Object));
792        assert!(Type::Array.interpretable_as(Type::Object));
793        assert!(Type::Function.interpretable_as(Type::Object));
794
795        assert!(!Type::Object.interpretable_as(Type::Array));
796        assert!(!Type::Object.interpretable_as(Type::Function));
797
798        assert!(!Type::Bool.interpretable_as(Type::Int));
799    }
800
801    #[test]
802    fn big_int() {
803        test_with(|ctx| {
804            let val: Value = ctx.eval(r#"1n"#).unwrap();
805            assert_eq!(val.type_of(), Type::BigInt);
806            let val: Value = ctx.eval(r#"999999999999999999999n"#).unwrap();
807            assert_eq!(val.type_of(), Type::BigInt);
808            let val = Value::new_big_int(ctx.clone(), 1245);
809            assert_eq!(val.type_of(), Type::BigInt);
810            let val = Value::new_big_int(ctx, 9999999999999999);
811            assert_eq!(val.type_of(), Type::BigInt);
812        });
813    }
814}