Skip to main content

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, ArrayBufferSource};
35pub use iterable::{Iterable, IterableFn, 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 big int value.
240    ///
241    /// Returns an error if the underlying QuickJS allocation fails.
242    #[inline]
243    pub fn new_big_int(ctx: Ctx<'js>, value: i64) -> Result<Self> {
244        let value = unsafe { ctx.handle_exception(qjs::JS_NewBigInt64(ctx.as_ptr(), value))? };
245        Ok(Self { ctx, value })
246    }
247
248    /// Create a new number value
249    #[inline]
250    pub fn new_number(ctx: Ctx<'js>, value: f64) -> Self {
251        let int = value as i32;
252        #[allow(clippy::float_cmp)]
253        // This is safe and fast in that case
254        let value = if value == int as f64 {
255            qjs::JS_MKVAL(qjs::JS_TAG_INT, int)
256        } else {
257            qjs::JS_NewFloat64(value)
258        };
259        Self { ctx, value }
260    }
261
262    /// Try get any number from value
263    pub fn as_number(&self) -> Option<f64> {
264        if self.is_int() {
265            Some(unsafe { self.get_int() as _ })
266        } else if self.is_float() {
267            Some(unsafe { self.get_float() })
268        } else {
269            None
270        }
271    }
272
273    #[allow(unused)]
274    #[inline]
275    pub(crate) fn new_ptr(ctx: Ctx<'js>, tag: qjs::c_int, ptr: *mut qjs::c_void) -> Self {
276        let value = qjs::JS_MKPTR(tag, ptr);
277        Self { ctx, value }
278    }
279
280    #[allow(unused)]
281    #[inline]
282    pub(crate) fn new_ptr_const(ctx: Ctx<'js>, tag: qjs::c_int, ptr: *mut qjs::c_void) -> Self {
283        let value = unsafe { qjs::JS_DupValue(ctx.as_ptr(), qjs::JS_MKPTR(tag, ptr)) };
284        Self { ctx, value }
285    }
286
287    #[inline]
288    pub(crate) unsafe fn get_ptr(&self) -> *mut qjs::c_void {
289        qjs::JS_VALUE_GET_PTR(self.value)
290    }
291
292    /// Returns if the value is the JavaScript null value.
293    #[inline]
294    pub fn is_null(&self) -> bool {
295        let tag = unsafe { qjs::JS_VALUE_GET_NORM_TAG(self.value) };
296        qjs::JS_TAG_NULL == tag
297    }
298
299    /// Returns if the value is the JavaScript undefined value.
300    #[inline]
301    pub fn is_undefined(&self) -> bool {
302        let tag = unsafe { qjs::JS_VALUE_GET_NORM_TAG(self.value) };
303        qjs::JS_TAG_UNDEFINED == tag
304    }
305
306    /// Check if the value is a bool
307    #[inline]
308    pub fn is_bool(&self) -> bool {
309        qjs::JS_TAG_BOOL == unsafe { qjs::JS_VALUE_GET_TAG(self.value) }
310    }
311
312    /// Check if the value is an int
313    #[inline]
314    pub fn is_int(&self) -> bool {
315        qjs::JS_TAG_INT == unsafe { qjs::JS_VALUE_GET_TAG(self.value) }
316    }
317
318    /// Check if the value is a float
319    #[inline]
320    pub fn is_float(&self) -> bool {
321        qjs::JS_TAG_FLOAT64 == unsafe { qjs::JS_VALUE_GET_NORM_TAG(self.value) }
322    }
323
324    /// Check if the value is an any number
325    #[inline]
326    pub fn is_number(&self) -> bool {
327        let tag = unsafe { qjs::JS_VALUE_GET_NORM_TAG(self.value) };
328        qjs::JS_TAG_INT == tag || qjs::JS_TAG_FLOAT64 == tag
329    }
330
331    /// Check if the value is a string
332    #[inline]
333    pub fn is_string(&self) -> bool {
334        let tag = unsafe { qjs::JS_VALUE_GET_TAG(self.value) };
335        tag == qjs::JS_TAG_STRING || tag == qjs::JS_TAG_STRING_ROPE
336    }
337
338    /// Check if the value is a symbol
339    #[inline]
340    pub fn is_symbol(&self) -> bool {
341        qjs::JS_TAG_SYMBOL == unsafe { qjs::JS_VALUE_GET_TAG(self.value) }
342    }
343
344    /// Check if the value is an object
345    #[inline]
346    pub fn is_object(&self) -> bool {
347        qjs::JS_TAG_OBJECT == unsafe { qjs::JS_VALUE_GET_TAG(self.value) }
348    }
349
350    /// Check if the value is a module
351    #[inline]
352    pub fn is_module(&self) -> bool {
353        qjs::JS_TAG_MODULE == unsafe { qjs::JS_VALUE_GET_TAG(self.value) }
354    }
355
356    /// Check if the value is an array
357    #[inline]
358    pub fn is_array(&self) -> bool {
359        unsafe { qjs::JS_IsArray(self.value) }
360    }
361
362    /// Check if the value is a function
363    #[inline]
364    pub fn is_function(&self) -> bool {
365        unsafe { qjs::JS_IsFunction(self.ctx.as_ptr(), self.value) }
366    }
367
368    /// Check if the value is a constructor function
369    #[inline]
370    pub fn is_constructor(&self) -> bool {
371        unsafe { qjs::JS_IsConstructor(self.ctx.as_ptr(), self.value) }
372    }
373
374    /// Check if the value is a promise.
375    #[inline]
376    pub fn is_promise(&self) -> bool {
377        unsafe { qjs::JS_PromiseState(self.ctx.as_ptr(), self.value) >= 0 }
378    }
379
380    /// Check if the value is an exception
381    #[inline]
382    pub fn is_exception(&self) -> bool {
383        unsafe { qjs::JS_IsException(self.value) }
384    }
385
386    /// Check if the value is an error
387    #[inline]
388    pub fn is_error(&self) -> bool {
389        unsafe { qjs::JS_IsError(self.value) }
390    }
391
392    /// Check if the value is an uncatchable error (e.g. from an interrupt handler)
393    #[inline]
394    pub fn is_uncatchable_error(&self) -> bool {
395        unsafe { qjs::JS_IsUncatchableError(self.value) }
396    }
397
398    /// Check if the value is a BigInt
399    #[inline]
400    pub fn is_big_int(&self) -> bool {
401        unsafe { qjs::JS_IsBigInt(self.value) }
402    }
403
404    /// Check if the value is a proxy
405    #[inline]
406    pub fn is_proxy(&self) -> bool {
407        unsafe { qjs::JS_IsProxy(self.value) }
408    }
409
410    /// Reference as value
411    #[inline]
412    pub fn as_value(&self) -> &Self {
413        self
414    }
415
416    #[inline]
417    pub(crate) fn into_value(self) -> Self {
418        self
419    }
420
421    /// Convert from value to specified type
422    pub fn get<T: FromJs<'js>>(&self) -> Result<T> {
423        T::from_js(self.ctx(), self.clone())
424    }
425
426    /// Returns the raw C library JavaScript value.
427    pub fn as_raw(&self) -> qjs::JSValue {
428        self.value
429    }
430
431    /// Create a value from the C library JavaScript value.
432    ///
433    /// # Safety
434    /// The value cannot be from an unrelated runtime and the value must be owned.
435    /// QuickJS JavaScript values are reference counted. The drop implementation of this type
436    /// decrements the reference count so the value must have count which won't be decremented
437    /// elsewhere. Use [`qjs::JS_DupValue`] to increment the reference count of the value.
438    pub unsafe fn from_raw(ctx: Ctx<'js>, value: qjs::JSValue) -> Self {
439        Self::from_js_value(ctx, value)
440    }
441}
442
443impl<'js> AsRef<Value<'js>> for Value<'js> {
444    fn as_ref(&self) -> &Value<'js> {
445        self
446    }
447}
448
449macro_rules! type_impls {
450    // type: name => tag
451    ($($type:ident: $name:ident => $($tag:ident)|+,)*) => {
452        /// The type of JavaScript value
453        #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
454        #[repr(u8)]
455        pub enum Type {
456            $($type,)*
457            Unknown
458        }
459
460        impl Type {
461            /// Returns true if the type is one of `uninitialized`, `undefined` or `null`
462            pub const fn is_void(self) -> bool {
463                use Type::*;
464                matches!(self, Uninitialized | Undefined | Null)
465            }
466
467            /// Check the type for similarity
468            pub const fn interpretable_as(self, other: Self) -> bool {
469                use Type::*;
470
471                if (self as u8) == (other as u8){
472                    return true
473                }
474                match other{
475                    Float => matches!(self, Int),
476                    Object => matches!(self, Array | Function | Constructor | Exception | Promise | Proxy),
477                    Function => matches!(self, Constructor),
478                    _ => false
479                }
480            }
481
482            /// Returns string representation of type
483            pub const fn as_str(self) -> &'static str {
484                match self {
485                    $(Type::$type => stringify!($name),)*
486                    Type::Unknown => "Unknown type",
487                }
488            }
489        }
490
491        impl AsRef<str> for Type {
492            fn as_ref(&self) -> &str {
493                self.as_str()
494            }
495        }
496
497        impl fmt::Display for Type {
498            fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
499                self.as_str().fmt(f)
500            }
501        }
502
503        impl str::FromStr for Type {
504            type Err = ();
505
506            fn from_str(s: &str) -> StdResult<Self, Self::Err> {
507                Ok(match s {
508                    $(stringify!($name) => Type::$type,)*
509                    _ => return Err(()),
510                })
511            }
512        }
513
514        impl<'js> Value<'js> {
515            /// Get the type of value
516            pub fn type_of(&self) -> Type {
517                let tag = unsafe { qjs::JS_VALUE_GET_NORM_TAG(self.value) };
518                match tag {
519                    $($(qjs::$tag)|+ if type_impls!(@cond $type self) => Type::$type,)*
520                    _ => Type::Unknown,
521                }
522            }
523
524            /// Get the name of type
525            pub fn type_name(&self) -> &'static str {
526                self.type_of().as_str()
527            }
528        }
529    };
530
531    (@cond Array $self:expr) => { $self.is_array() };
532    (@cond Constructor $self:expr) => { $self.is_constructor() };
533    (@cond Function $self:expr) => { $self.is_function() };
534    (@cond Promise $self:expr) => { $self.is_promise() };
535    (@cond Exception $self:expr) => { $self.is_error() };
536    (@cond Proxy $self:expr) => { $self.is_proxy() };
537    (@cond $type:ident $self:expr) => { true };
538}
539
540type_impls! {
541    Uninitialized: uninitialized => JS_TAG_UNINITIALIZED,
542    Undefined: undefined => JS_TAG_UNDEFINED,
543    Null: null => JS_TAG_NULL,
544    Bool: bool => JS_TAG_BOOL,
545    Int: int => JS_TAG_INT,
546    Float: float => JS_TAG_FLOAT64,
547    String: string => JS_TAG_STRING | JS_TAG_STRING_ROPE,
548    Symbol: symbol => JS_TAG_SYMBOL,
549    Array: array => JS_TAG_OBJECT,
550    Constructor: constructor => JS_TAG_OBJECT,
551    Function: function => JS_TAG_OBJECT,
552    Promise: promise => JS_TAG_OBJECT,
553    Exception: exception => JS_TAG_OBJECT,
554    Proxy: proxy => JS_TAG_OBJECT,
555    Object: object => JS_TAG_OBJECT,
556    Module: module => JS_TAG_MODULE,
557    BigInt: big_int => JS_TAG_BIG_INT | JS_TAG_SHORT_BIG_INT,
558}
559
560macro_rules! sub_types {
561    ($( $head:ident$(->$sub_type:ident)* $as:ident $ref:ident $into:ident $try_into:ident $from:ident,)*) => {
562        $(
563            impl<'js> $head<'js> {
564                /// Reference to value
565                #[inline]
566                pub fn as_value(&self) -> &Value<'js> {
567                    &self.0.as_value()
568                }
569
570                /// Convert into value
571                #[inline]
572                pub fn into_value(self) -> Value<'js> {
573                    self.0.into_value()
574                }
575
576                /// Returns the underlying super type.
577                pub fn into_inner(self) -> sub_types!(@head_ty $($sub_type),*) {
578                    self.0
579                }
580                /// Returns a reference to the underlying super type.
581                pub fn as_inner(&self) -> & sub_types!(@head_ty $($sub_type),*) {
582                    &self.0
583                }
584
585                /// Returns the [`Ctx`] object associated with this value
586                pub fn ctx(&self) -> &Ctx<'js>{
587                    self.0.ctx()
588                }
589
590                /// Convert from value
591                pub fn from_value(value: Value<'js>) -> Result<Self> {
592                    let type_ = value.type_of();
593                    if type_.interpretable_as(Type::$head) {
594                        Ok(sub_types!(@wrap $head$(->$sub_type)*  value))
595                    } else {
596                        Err(Error::new_from_js(type_.as_str(), Type::$head.as_str()))
597                    }
598                }
599
600                #[allow(unused)]
601                pub(crate) unsafe fn from_js_value_const(ctx: Ctx<'js>, value: qjs::JSValueConst) -> Self {
602                    let v = Value::from_js_value_const(ctx, value);
603                    debug_assert!(v.$as().is_some(),"tried to cource js value {:?} to the wrong type `{}`, this is a rquickjs bug",v, stringify!($head));
604                    sub_types!(@wrap $head$(->$sub_type)*  v)
605                }
606
607                #[allow(unused)]
608                pub(crate) unsafe fn from_js_value(ctx: Ctx<'js>, value: qjs::JSValue) -> Self {
609                    let v = Value::from_js_value(ctx, value);
610                    debug_assert!(v.$as().is_some(),"tried to cource js value {:?} to the wrong type `{}`, this is a rquickjs bug",v, stringify!($head));
611                    sub_types!(@wrap $head$(->$sub_type)*  v)
612                }
613
614                #[allow(unused)]
615                pub(crate) fn into_js_value(self) -> qjs::JSValue{
616                    self.0.into_js_value()
617                }
618
619                #[allow(unused)]
620                pub(crate) fn as_js_value(&self) -> qjs::JSValueConst{
621                    self.0.as_js_value()
622                }
623            }
624
625            impl<'js> Deref for $head<'js> {
626                type Target = sub_types!(@head_ty $($sub_type),*);
627
628                fn deref(&self) -> &Self::Target {
629                    &self.0
630                }
631            }
632
633            sub_types!(@imp_as_ref $head$(,$sub_type)*);
634
635            impl<'js> Value<'js> {
636                #[doc = concat!("Interpret as [`",stringify!($head),"`]")]
637                ///
638                /// # Safety
639                /// You should be sure that the value already is of required type before to do it.
640                #[inline]
641                pub unsafe fn $ref(&self) -> &$head<'js> {
642                    &*(self as *const _ as *const $head)
643                }
644
645                #[doc = concat!("Try reinterpret as [`",stringify!($head),"`]")]
646                pub fn $as(&self) -> Option<&$head<'js>> {
647                    if self.type_of().interpretable_as(Type::$head) {
648                        Some(unsafe { self.$ref() })
649                    } else {
650                        None
651                    }
652                }
653
654                #[doc = concat!("Try convert into [`",stringify!($head),"`]")]
655                pub fn $into(self) -> Option<$head<'js>> {
656                    if self.type_of().interpretable_as(Type::$head) {
657                        Some(sub_types!(@wrap $head$(->$sub_type)* self))
658                    } else {
659                        None
660                    }
661                }
662
663                #[doc = concat!("Try convert into [`",stringify!($head),"`] returning self if the conversion fails.")]
664                pub fn $try_into(self) -> core::result::Result<$head<'js>, Value<'js>> {
665                    if self.type_of().interpretable_as(Type::$head) {
666                        Ok(sub_types!(@wrap $head$(->$sub_type)* self))
667                    } else {
668                        Err(self)
669                    }
670                }
671
672                #[doc = concat!("Convert from [`",stringify!($head),"`]")]
673                pub fn $from(value: $head<'js>) -> Self {
674                    value.into_value()
675                }
676            }
677
678            impl<'js> From<$head<'js>> for Value<'js> {
679                fn from(value: $head<'js>) -> Self {
680                    value.into_value()
681                }
682            }
683
684            impl<'js> FromJs<'js> for $head<'js> {
685                fn from_js(_: &Ctx<'js>, value: Value<'js>) -> Result<Self> {
686                    Self::from_value(value)
687                }
688            }
689
690            impl<'js> IntoJs<'js> for $head<'js> {
691                fn into_js(self, _ctx: &Ctx<'js>) -> Result<Value<'js>> {
692                    Ok(self.into_value())
693                }
694            }
695
696            impl<'js> IntoAtom<'js> for $head<'js>{
697                fn into_atom(self, ctx: &Ctx<'js>) -> Result<Atom<'js>> {
698                    Atom::from_value(ctx.clone(), &self.into_value())
699                }
700            }
701        )*
702    };
703
704    (@type $type:ident) => { $type<'js> };
705
706    (@head $head:ident $(rem:ident)*)  => { $head };
707    (@head_ty $head:ident$(,$rem:ident)*)  => { $head<'js> };
708
709    (@wrap $type:ident$(->$rem:ident)+ $val:expr) => { $type(sub_types!(@wrap $($rem)->* $val)) };
710    (@wrap Value $val:expr) => { $val };
711
712    /*
713    (@into_inner $head:ident->$inner:ident$(->$rem:ident)*) => {
714        impl<'js> $head<'js>{
715            pub fn into_inner($
716        }
717    }*/
718
719    (@imp_as_ref $type:ident,Value) => {
720        impl<'js> AsRef<Value<'js>> for $type<'js> {
721            fn as_ref(&self) -> &Value<'js> {
722                &self.0
723            }
724        }
725    };
726    (@imp_as_ref $type:ident,$inner:ident$(,$rem:ident)*) => {
727        impl<'js> AsRef<$inner<'js>> for $type<'js> {
728            fn as_ref(&self) -> &$inner<'js> {
729                &self.0
730            }
731        }
732
733        impl<'js> AsRef<Value<'js>> for $type<'js> {
734            fn as_ref(&self) -> &Value<'js> {
735                self.0.as_ref()
736            }
737        }
738    };
739}
740
741sub_types! {
742    String->Value as_string ref_string into_string try_into_string from_string,
743    Symbol->Value as_symbol ref_symbol into_symbol try_into_symbol from_symbol,
744    Object->Value as_object ref_object into_object try_into_object from_object,
745    Function->Object->Value as_function ref_function into_function try_into_function from_function,
746    Constructor->Function->Object->Value as_constructor ref_constructor into_constructor try_into_constructor from_constructor,
747    Promise->Object->Value as_promise ref_promise into_promise try_into_promise from_promise,
748    Array->Object->Value as_array ref_array into_array try_into_array from_array,
749    Exception->Object->Value as_exception ref_exception into_exception try_into_exception from_exception,
750    BigInt->Value as_big_int ref_big_int into_big_int try_into_big_int from_big_int,
751    Proxy->Object->Value as_proxy ref_proxy into_proxy try_into_proxy from_proxy,
752}
753
754macro_rules! void_types {
755    ($($(#[$meta:meta])* $type:ident $new:ident;)*) => {
756        $(
757            $(#[$meta])*
758            #[derive(Debug, Copy, Clone, PartialEq, Eq)]
759            #[allow(dead_code)]
760            pub struct $type;
761
762            #[allow(dead_code)]
763            impl $type {
764                /// Convert into value
765                pub fn into_value<'js>(self, ctx: Ctx<'js>) -> Value<'js> {
766                    Value::$new(ctx)
767                }
768
769                /// Convert from value
770                pub fn from_value<'js>(value: Value<'js>) -> Result<Self> {
771                    if value.type_of() == Type::$type {
772                        Ok(Self)
773                    } else {
774                        Err(Error::new_from_js("value", Type::$type.as_str()))
775                    }
776                }
777            }
778
779            impl<'js> FromJs<'js> for $type {
780                fn from_js(_: &Ctx<'js>, value: Value<'js>) -> Result<Self> {
781                    Self::from_value(value)
782                }
783            }
784
785            impl<'js> IntoJs<'js> for $type {
786                fn into_js(self, ctx: &Ctx<'js>) -> Result<Value<'js>> {
787                    Ok(self.into_value(ctx.clone()))
788                }
789            }
790        )*
791    };
792}
793
794void_types! {
795    /// The placeholder which treated as uninitialized JS value
796    Uninitialized new_uninitialized;
797
798    /// The placeholder which treated as `undefined` value
799    Undefined new_undefined;
800
801    /// The placeholder which treated as `null` value
802    Null new_null;
803}
804
805#[cfg(test)]
806mod test {
807    use crate::*;
808
809    #[test]
810    fn type_matches() {
811        assert!(Type::Bool.interpretable_as(Type::Bool));
812
813        assert!(Type::Object.interpretable_as(Type::Object));
814        assert!(Type::Array.interpretable_as(Type::Object));
815        assert!(Type::Function.interpretable_as(Type::Object));
816
817        assert!(!Type::Object.interpretable_as(Type::Array));
818        assert!(!Type::Object.interpretable_as(Type::Function));
819
820        assert!(!Type::Bool.interpretable_as(Type::Int));
821    }
822
823    #[test]
824    fn big_int() {
825        test_with(|ctx| {
826            let val: Value = ctx.eval(r#"1n"#).unwrap();
827            assert_eq!(val.type_of(), Type::BigInt);
828            let val: Value = ctx.eval(r#"999999999999999999999n"#).unwrap();
829            assert_eq!(val.type_of(), Type::BigInt);
830            let val = Value::new_big_int(ctx.clone(), 1245).unwrap();
831            assert_eq!(val.type_of(), Type::BigInt);
832            let val = Value::new_big_int(ctx, 9999999999999999).unwrap();
833            assert_eq!(val.type_of(), Type::BigInt);
834        });
835    }
836
837    // Regression for https://github.com/DelSkayn/rquickjs/issues/585:
838    // `Value::new_big_int` must surface QuickJS allocation failures as
839    // `Err(Error::Exception)` rather than returning a `Value` that wraps a
840    // `JS_EXCEPTION` sentinel.
841    #[test]
842    fn big_int_oom_is_reported() {
843        let rt = crate::Runtime::new().unwrap();
844        let ctx = crate::Context::full(&rt).unwrap();
845        // Shrink the memory allowance *after* the context has been built so
846        // that the context itself can allocate, but new heap allocations
847        // like the one inside `JS_NewBigInt64` will fail.
848        rt.set_memory_limit(0x1000);
849        ctx.with(|ctx| {
850            // A value outside the short big-int range forces a heap
851            // allocation inside QuickJS, which will fail under the tiny
852            // memory limit set above.
853            let err = Value::new_big_int(ctx.clone(), i64::MAX)
854                .expect_err("expected allocation failure to be reported");
855            assert!(matches!(err, Error::Exception));
856        });
857    }
858}