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