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
38pub 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 #[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 #[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 #[inline]
178 pub fn ctx(&self) -> &Ctx<'js> {
179 &self.ctx
180 }
181
182 #[inline]
184 pub(crate) unsafe fn get_bool(&self) -> bool {
185 qjs::JS_VALUE_GET_BOOL(self.value)
186 }
187
188 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 #[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 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 #[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 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 #[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 #[inline]
250 pub fn new_number(ctx: Ctx<'js>, value: f64) -> Self {
251 let int = value as i32;
252 #[allow(clippy::float_cmp)]
253 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 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 #[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 #[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 #[inline]
308 pub fn is_bool(&self) -> bool {
309 qjs::JS_TAG_BOOL == unsafe { qjs::JS_VALUE_GET_TAG(self.value) }
310 }
311
312 #[inline]
314 pub fn is_int(&self) -> bool {
315 qjs::JS_TAG_INT == unsafe { qjs::JS_VALUE_GET_TAG(self.value) }
316 }
317
318 #[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 #[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 #[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 #[inline]
340 pub fn is_symbol(&self) -> bool {
341 qjs::JS_TAG_SYMBOL == unsafe { qjs::JS_VALUE_GET_TAG(self.value) }
342 }
343
344 #[inline]
346 pub fn is_object(&self) -> bool {
347 qjs::JS_TAG_OBJECT == unsafe { qjs::JS_VALUE_GET_TAG(self.value) }
348 }
349
350 #[inline]
352 pub fn is_module(&self) -> bool {
353 qjs::JS_TAG_MODULE == unsafe { qjs::JS_VALUE_GET_TAG(self.value) }
354 }
355
356 #[inline]
358 pub fn is_array(&self) -> bool {
359 unsafe { qjs::JS_IsArray(self.value) }
360 }
361
362 #[inline]
364 pub fn is_function(&self) -> bool {
365 unsafe { qjs::JS_IsFunction(self.ctx.as_ptr(), self.value) }
366 }
367
368 #[inline]
370 pub fn is_constructor(&self) -> bool {
371 unsafe { qjs::JS_IsConstructor(self.ctx.as_ptr(), self.value) }
372 }
373
374 #[inline]
376 pub fn is_promise(&self) -> bool {
377 unsafe { qjs::JS_PromiseState(self.ctx.as_ptr(), self.value) >= 0 }
378 }
379
380 #[inline]
382 pub fn is_exception(&self) -> bool {
383 unsafe { qjs::JS_IsException(self.value) }
384 }
385
386 #[inline]
388 pub fn is_error(&self) -> bool {
389 unsafe { qjs::JS_IsError(self.value) }
390 }
391
392 #[inline]
394 pub fn is_uncatchable_error(&self) -> bool {
395 unsafe { qjs::JS_IsUncatchableError(self.value) }
396 }
397
398 #[inline]
400 pub fn is_big_int(&self) -> bool {
401 unsafe { qjs::JS_IsBigInt(self.value) }
402 }
403
404 #[inline]
406 pub fn is_proxy(&self) -> bool {
407 unsafe { qjs::JS_IsProxy(self.value) }
408 }
409
410 #[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 pub fn get<T: FromJs<'js>>(&self) -> Result<T> {
423 T::from_js(self.ctx(), self.clone())
424 }
425
426 pub fn as_raw(&self) -> qjs::JSValue {
428 self.value
429 }
430
431 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:ident: $name:ident => $($tag:ident)|+,)*) => {
452 #[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 pub const fn is_void(self) -> bool {
463 use Type::*;
464 matches!(self, Uninitialized | Undefined | Null)
465 }
466
467 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 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 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 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 #[inline]
566 pub fn as_value(&self) -> &Value<'js> {
567 &self.0.as_value()
568 }
569
570 #[inline]
572 pub fn into_value(self) -> Value<'js> {
573 self.0.into_value()
574 }
575
576 pub fn into_inner(self) -> sub_types!(@head_ty $($sub_type),*) {
578 self.0
579 }
580 pub fn as_inner(&self) -> & sub_types!(@head_ty $($sub_type),*) {
582 &self.0
583 }
584
585 pub fn ctx(&self) -> &Ctx<'js>{
587 self.0.ctx()
588 }
589
590 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 #[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 (@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 pub fn into_value<'js>(self, ctx: Ctx<'js>) -> Value<'js> {
766 Value::$new(ctx)
767 }
768
769 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 Uninitialized new_uninitialized;
797
798 Undefined new_undefined;
800
801 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 #[test]
842 fn big_int_oom_is_reported() {
843 let rt = crate::Runtime::new().unwrap();
844 let ctx = crate::Context::full(&rt).unwrap();
845 rt.set_memory_limit(0x1000);
849 ctx.with(|ctx| {
850 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}