Skip to main content

mozjs_sys/
jsval.rs

1/* This Source Code Form is subject to the terms of the Mozilla Public
2 * License, v. 2.0. If a copy of the MPL was not distributed with this file,
3 * You can obtain one at http://mozilla.org/MPL/2.0/. */
4
5#![allow(non_camel_case_types)]
6#![allow(non_snake_case)]
7
8use crate::jsapi::JSContext;
9use crate::jsapi::JSObject;
10use crate::jsapi::JSString;
11use crate::jsapi::JSValueType;
12use crate::jsapi::JS::BigInt;
13use crate::jsapi::JS::Symbol;
14use crate::jsapi::JS::TraceKind;
15use crate::jsapi::JS::Value;
16
17use libc::c_void;
18use std::default::Default;
19
20pub type JSVal = Value;
21
22#[cfg(target_pointer_width = "64")]
23const JSVAL_TAG_SHIFT: usize = 47;
24
25#[cfg(target_pointer_width = "64")]
26const JSVAL_TAG_MAX_DOUBLE: u32 = 0x1FFF0u32;
27
28#[cfg(target_pointer_width = "32")]
29const JSVAL_TAG_CLEAR: u32 = 0xFFFFFF80;
30
31#[cfg(target_pointer_width = "64")]
32#[repr(u32)]
33#[allow(dead_code)]
34enum ValueTag {
35    INT32 = JSVAL_TAG_MAX_DOUBLE | (JSValueType::JSVAL_TYPE_INT32 as u32),
36    UNDEFINED = JSVAL_TAG_MAX_DOUBLE | (JSValueType::JSVAL_TYPE_UNDEFINED as u32),
37    STRING = JSVAL_TAG_MAX_DOUBLE | (JSValueType::JSVAL_TYPE_STRING as u32),
38    SYMBOL = JSVAL_TAG_MAX_DOUBLE | (JSValueType::JSVAL_TYPE_SYMBOL as u32),
39    BIGINT = JSVAL_TAG_MAX_DOUBLE | (JSValueType::JSVAL_TYPE_BIGINT as u32),
40    BOOLEAN = JSVAL_TAG_MAX_DOUBLE | (JSValueType::JSVAL_TYPE_BOOLEAN as u32),
41    MAGIC = JSVAL_TAG_MAX_DOUBLE | (JSValueType::JSVAL_TYPE_MAGIC as u32),
42    NULL = JSVAL_TAG_MAX_DOUBLE | (JSValueType::JSVAL_TYPE_NULL as u32),
43    OBJECT = JSVAL_TAG_MAX_DOUBLE | (JSValueType::JSVAL_TYPE_OBJECT as u32),
44}
45
46#[cfg(target_pointer_width = "32")]
47#[repr(u32)]
48#[allow(dead_code)]
49enum ValueTag {
50    PRIVATE = 0,
51    INT32 = JSVAL_TAG_CLEAR as u32 | (JSValueType::JSVAL_TYPE_INT32 as u32),
52    UNDEFINED = JSVAL_TAG_CLEAR as u32 | (JSValueType::JSVAL_TYPE_UNDEFINED as u32),
53    STRING = JSVAL_TAG_CLEAR as u32 | (JSValueType::JSVAL_TYPE_STRING as u32),
54    SYMBOL = JSVAL_TAG_CLEAR as u32 | (JSValueType::JSVAL_TYPE_SYMBOL as u32),
55    BIGINT = JSVAL_TAG_CLEAR as u32 | (JSValueType::JSVAL_TYPE_BIGINT as u32),
56    BOOLEAN = JSVAL_TAG_CLEAR as u32 | (JSValueType::JSVAL_TYPE_BOOLEAN as u32),
57    MAGIC = JSVAL_TAG_CLEAR as u32 | (JSValueType::JSVAL_TYPE_MAGIC as u32),
58    NULL = JSVAL_TAG_CLEAR as u32 | (JSValueType::JSVAL_TYPE_NULL as u32),
59    OBJECT = JSVAL_TAG_CLEAR as u32 | (JSValueType::JSVAL_TYPE_OBJECT as u32),
60}
61
62#[cfg(target_pointer_width = "64")]
63#[repr(u64)]
64#[allow(dead_code)]
65enum ValueShiftedTag {
66    MAX_DOUBLE = ((JSVAL_TAG_MAX_DOUBLE as u64) << JSVAL_TAG_SHIFT) | 0xFFFFFFFFu64,
67    INT32 = (ValueTag::INT32 as u64) << JSVAL_TAG_SHIFT,
68    UNDEFINED = (ValueTag::UNDEFINED as u64) << JSVAL_TAG_SHIFT,
69    STRING = (ValueTag::STRING as u64) << JSVAL_TAG_SHIFT,
70    SYMBOL = (ValueTag::SYMBOL as u64) << JSVAL_TAG_SHIFT,
71    BIGINT = (ValueTag::BIGINT as u64) << JSVAL_TAG_SHIFT,
72    BOOLEAN = (ValueTag::BOOLEAN as u64) << JSVAL_TAG_SHIFT,
73    MAGIC = (ValueTag::MAGIC as u64) << JSVAL_TAG_SHIFT,
74    NULL = (ValueTag::NULL as u64) << JSVAL_TAG_SHIFT,
75    OBJECT = (ValueTag::OBJECT as u64) << JSVAL_TAG_SHIFT,
76}
77
78const JSVAL_PAYLOAD_MASK: u64 = 0x00007FFFFFFFFFFF;
79
80#[inline(always)]
81fn AsJSVal(val: u64) -> JSVal {
82    JSVal { asBits_: val }
83}
84
85#[cfg(target_pointer_width = "64")]
86#[inline(always)]
87fn BuildJSVal(tag: ValueTag, payload: u64) -> JSVal {
88    AsJSVal(((tag as u32 as u64) << JSVAL_TAG_SHIFT) | payload)
89}
90
91#[cfg(target_pointer_width = "32")]
92#[inline(always)]
93fn BuildJSVal(tag: ValueTag, payload: u64) -> JSVal {
94    AsJSVal(((tag as u32 as u64) << 32) | payload)
95}
96
97#[inline(always)]
98pub fn NullValue() -> JSVal {
99    BuildJSVal(ValueTag::NULL, 0)
100}
101
102#[inline(always)]
103pub fn UndefinedValue() -> JSVal {
104    BuildJSVal(ValueTag::UNDEFINED, 0)
105}
106
107#[inline(always)]
108pub fn Int32Value(i: i32) -> JSVal {
109    BuildJSVal(ValueTag::INT32, i as u32 as u64)
110}
111
112#[cfg(target_pointer_width = "64")]
113#[inline(always)]
114pub fn DoubleValue(f: f64) -> JSVal {
115    let bits: u64 = f64::to_bits(f);
116    assert!(bits <= ValueShiftedTag::MAX_DOUBLE as u64);
117    AsJSVal(bits)
118}
119
120#[cfg(target_pointer_width = "32")]
121#[inline(always)]
122pub fn DoubleValue(f: f64) -> JSVal {
123    let bits: u64 = f64::to_bits(f);
124    let val = AsJSVal(bits);
125    assert!(val.is_double());
126    val
127}
128
129#[inline(always)]
130pub fn UInt32Value(ui: u32) -> JSVal {
131    if ui > 0x7fffffff {
132        DoubleValue(ui as f64)
133    } else {
134        Int32Value(ui as i32)
135    }
136}
137
138#[cfg(target_pointer_width = "64")]
139#[inline(always)]
140pub fn StringValue(s: &JSString) -> JSVal {
141    let bits = s as *const JSString as usize as u64;
142    assert!((bits >> JSVAL_TAG_SHIFT) == 0);
143    BuildJSVal(ValueTag::STRING, bits)
144}
145
146#[cfg(target_pointer_width = "32")]
147#[inline(always)]
148pub fn StringValue(s: &JSString) -> JSVal {
149    let bits = s as *const JSString as usize as u64;
150    BuildJSVal(ValueTag::STRING, bits)
151}
152
153#[inline(always)]
154pub fn BooleanValue(b: bool) -> JSVal {
155    BuildJSVal(ValueTag::BOOLEAN, b as u64)
156}
157
158#[cfg(target_pointer_width = "64")]
159#[inline(always)]
160pub fn ObjectValue(o: *mut JSObject) -> JSVal {
161    let bits = o as usize as u64;
162    assert!((bits >> JSVAL_TAG_SHIFT) == 0);
163    BuildJSVal(ValueTag::OBJECT, bits)
164}
165
166#[cfg(target_pointer_width = "32")]
167#[inline(always)]
168pub fn ObjectValue(o: *mut JSObject) -> JSVal {
169    let bits = o as usize as u64;
170    BuildJSVal(ValueTag::OBJECT, bits)
171}
172
173#[inline(always)]
174pub fn ObjectOrNullValue(o: *mut JSObject) -> JSVal {
175    if o.is_null() {
176        NullValue()
177    } else {
178        ObjectValue(o)
179    }
180}
181
182#[cfg(target_pointer_width = "64")]
183#[inline(always)]
184pub fn SymbolValue(s: &Symbol) -> JSVal {
185    let bits = s as *const Symbol as usize as u64;
186    assert!((bits >> JSVAL_TAG_SHIFT) == 0);
187    BuildJSVal(ValueTag::SYMBOL, bits)
188}
189
190#[cfg(target_pointer_width = "32")]
191#[inline(always)]
192pub fn SymbolValue(s: &Symbol) -> JSVal {
193    let bits = s as *const Symbol as usize as u64;
194    BuildJSVal(ValueTag::SYMBOL, bits)
195}
196
197#[cfg(target_pointer_width = "64")]
198#[inline(always)]
199pub fn BigIntValue(s: &BigInt) -> JSVal {
200    let bits = s as *const BigInt as usize as u64;
201    assert!((bits >> JSVAL_TAG_SHIFT) == 0);
202    BuildJSVal(ValueTag::BIGINT, bits)
203}
204
205#[cfg(target_pointer_width = "32")]
206#[inline(always)]
207pub fn BigIntValue(s: &BigInt) -> JSVal {
208    let bits = s as *const BigInt as usize as u64;
209    BuildJSVal(ValueTag::BIGINT, bits)
210}
211
212#[inline(always)]
213pub fn PrivateValue(o: *const c_void) -> JSVal {
214    let ptrBits = o as usize as u64;
215    #[cfg(target_pointer_width = "64")]
216    assert_eq!(ptrBits & 0xFFFF000000000000, 0);
217    AsJSVal(ptrBits)
218}
219
220impl JSVal {
221    #[inline(always)]
222    fn asBits(&self) -> u64 {
223        self.asBits_
224    }
225
226    #[inline(always)]
227    #[cfg(target_pointer_width = "64")]
228    pub fn is_undefined(&self) -> bool {
229        self.asBits() == ValueShiftedTag::UNDEFINED as u64
230    }
231
232    #[inline(always)]
233    #[cfg(target_pointer_width = "32")]
234    pub fn is_undefined(&self) -> bool {
235        (self.asBits() >> 32) == ValueTag::UNDEFINED as u64
236    }
237
238    #[inline(always)]
239    #[cfg(target_pointer_width = "64")]
240    pub fn is_null(&self) -> bool {
241        self.asBits() == ValueShiftedTag::NULL as u64
242    }
243
244    #[inline(always)]
245    #[cfg(target_pointer_width = "32")]
246    pub fn is_null(&self) -> bool {
247        (self.asBits() >> 32) == ValueTag::NULL as u64
248    }
249
250    #[inline(always)]
251    pub fn is_null_or_undefined(&self) -> bool {
252        self.is_null() || self.is_undefined()
253    }
254
255    #[inline(always)]
256    #[cfg(target_pointer_width = "64")]
257    pub fn is_boolean(&self) -> bool {
258        (self.asBits() >> JSVAL_TAG_SHIFT) == ValueTag::BOOLEAN as u64
259    }
260
261    #[inline(always)]
262    #[cfg(target_pointer_width = "32")]
263    pub fn is_boolean(&self) -> bool {
264        (self.asBits() >> 32) == ValueTag::BOOLEAN as u64
265    }
266
267    #[inline(always)]
268    #[cfg(target_pointer_width = "64")]
269    pub fn is_int32(&self) -> bool {
270        (self.asBits() >> JSVAL_TAG_SHIFT) == ValueTag::INT32 as u64
271    }
272
273    #[inline(always)]
274    #[cfg(target_pointer_width = "32")]
275    pub fn is_int32(&self) -> bool {
276        (self.asBits() >> 32) == ValueTag::INT32 as u64
277    }
278
279    #[inline(always)]
280    #[cfg(target_pointer_width = "64")]
281    pub fn is_double(&self) -> bool {
282        self.asBits() <= ValueShiftedTag::MAX_DOUBLE as u64
283    }
284
285    #[inline(always)]
286    #[cfg(target_pointer_width = "32")]
287    pub fn is_double(&self) -> bool {
288        (self.asBits() >> 32) <= JSVAL_TAG_CLEAR as u64
289    }
290
291    #[inline(always)]
292    #[cfg(target_pointer_width = "64")]
293    pub fn is_number(&self) -> bool {
294        const JSVAL_UPPER_EXCL_SHIFTED_TAG_OF_NUMBER_SET: u64 = ValueShiftedTag::BOOLEAN as u64;
295        self.asBits() < JSVAL_UPPER_EXCL_SHIFTED_TAG_OF_NUMBER_SET
296    }
297
298    #[inline(always)]
299    #[cfg(target_pointer_width = "32")]
300    pub fn is_number(&self) -> bool {
301        const JSVAL_UPPER_INCL_TAG_OF_NUMBER_SET: u64 = ValueTag::INT32 as u64;
302        (self.asBits() >> 32) <= JSVAL_UPPER_INCL_TAG_OF_NUMBER_SET
303    }
304
305    #[inline(always)]
306    #[cfg(target_pointer_width = "64")]
307    pub fn is_primitive(&self) -> bool {
308        const JSVAL_UPPER_EXCL_SHIFTED_TAG_OF_PRIMITIVE_SET: u64 = ValueShiftedTag::OBJECT as u64;
309        self.asBits() < JSVAL_UPPER_EXCL_SHIFTED_TAG_OF_PRIMITIVE_SET
310    }
311
312    #[inline(always)]
313    #[cfg(target_pointer_width = "32")]
314    pub fn is_primitive(&self) -> bool {
315        const JSVAL_UPPER_EXCL_TAG_OF_PRIMITIVE_SET: u64 = ValueTag::OBJECT as u64;
316        (self.asBits() >> 32) < JSVAL_UPPER_EXCL_TAG_OF_PRIMITIVE_SET
317    }
318
319    #[inline(always)]
320    #[cfg(target_pointer_width = "64")]
321    pub fn is_string(&self) -> bool {
322        (self.asBits() >> JSVAL_TAG_SHIFT) == ValueTag::STRING as u64
323    }
324
325    #[inline(always)]
326    #[cfg(target_pointer_width = "32")]
327    pub fn is_string(&self) -> bool {
328        (self.asBits() >> 32) == ValueTag::STRING as u64
329    }
330
331    #[inline(always)]
332    #[cfg(target_pointer_width = "64")]
333    pub fn is_object(&self) -> bool {
334        assert!((self.asBits() >> JSVAL_TAG_SHIFT) <= ValueTag::OBJECT as u64);
335        self.asBits() >= ValueShiftedTag::OBJECT as u64
336    }
337
338    #[inline(always)]
339    #[cfg(target_pointer_width = "32")]
340    pub fn is_object(&self) -> bool {
341        (self.asBits() >> 32) == ValueTag::OBJECT as u64
342    }
343
344    #[inline(always)]
345    #[cfg(target_pointer_width = "64")]
346    pub fn is_object_or_null(&self) -> bool {
347        const JSVAL_LOWER_INCL_SHIFTED_TAG_OF_OBJ_OR_NULL_SET: u64 = ValueShiftedTag::NULL as u64;
348        assert!((self.asBits() >> JSVAL_TAG_SHIFT) <= ValueTag::OBJECT as u64);
349        self.asBits() >= JSVAL_LOWER_INCL_SHIFTED_TAG_OF_OBJ_OR_NULL_SET
350    }
351
352    #[inline(always)]
353    #[cfg(target_pointer_width = "32")]
354    pub fn is_object_or_null(&self) -> bool {
355        const JSVAL_LOWER_INCL_TAG_OF_OBJ_OR_NULL_SET: u64 = ValueTag::NULL as u64;
356        assert!((self.asBits() >> 32) <= ValueTag::OBJECT as u64);
357        (self.asBits() >> 32) >= JSVAL_LOWER_INCL_TAG_OF_OBJ_OR_NULL_SET
358    }
359
360    #[inline(always)]
361    #[cfg(target_pointer_width = "64")]
362    pub fn is_magic(&self) -> bool {
363        (self.asBits() >> JSVAL_TAG_SHIFT) == ValueTag::MAGIC as u64
364    }
365
366    #[inline(always)]
367    #[cfg(target_pointer_width = "32")]
368    pub fn is_magic(&self) -> bool {
369        (self.asBits() >> 32) == ValueTag::MAGIC as u64
370    }
371
372    #[inline(always)]
373    #[cfg(target_pointer_width = "64")]
374    pub fn is_symbol(&self) -> bool {
375        (self.asBits() >> JSVAL_TAG_SHIFT) == ValueTag::SYMBOL as u64
376    }
377
378    #[inline(always)]
379    #[cfg(target_pointer_width = "32")]
380    pub fn is_symbol(&self) -> bool {
381        (self.asBits() >> 32) == ValueTag::SYMBOL as u64
382    }
383
384    #[inline(always)]
385    #[cfg(target_pointer_width = "64")]
386    pub fn is_bigint(&self) -> bool {
387        (self.asBits() >> JSVAL_TAG_SHIFT) == ValueTag::BIGINT as u64
388    }
389
390    #[inline(always)]
391    #[cfg(target_pointer_width = "32")]
392    pub fn is_bigint(&self) -> bool {
393        (self.asBits() >> 32) == ValueTag::BIGINT as u64
394    }
395
396    #[inline(always)]
397    #[cfg(target_pointer_width = "64")]
398    pub fn is_gcthing(&self) -> bool {
399        const JSVAL_LOWER_INCL_SHIFTED_TAG_OF_GCTHING_SET: u64 = ValueShiftedTag::STRING as u64;
400        self.asBits() >= JSVAL_LOWER_INCL_SHIFTED_TAG_OF_GCTHING_SET
401    }
402
403    #[inline(always)]
404    #[cfg(target_pointer_width = "32")]
405    pub fn is_gcthing(&self) -> bool {
406        const JSVAL_LOWER_INCL_TAG_OF_GCTHING_SET: u64 = ValueTag::STRING as u64;
407        (self.asBits() >> 32) >= JSVAL_LOWER_INCL_TAG_OF_GCTHING_SET
408    }
409
410    #[inline(always)]
411    #[cfg(target_pointer_width = "64")]
412    pub fn to_boolean(&self) -> bool {
413        assert!(self.is_boolean());
414        (self.asBits() & JSVAL_PAYLOAD_MASK) != 0
415    }
416
417    #[inline(always)]
418    #[cfg(target_pointer_width = "32")]
419    pub fn to_boolean(&self) -> bool {
420        (self.asBits() & 0x00000000FFFFFFFF) != 0
421    }
422
423    #[inline(always)]
424    pub fn to_int32(&self) -> i32 {
425        assert!(self.is_int32());
426        (self.asBits() & 0x00000000FFFFFFFF) as i32
427    }
428
429    #[inline(always)]
430    pub fn to_double(&self) -> f64 {
431        assert!(self.is_double());
432        f64::from_bits(self.asBits())
433    }
434
435    #[inline(always)]
436    pub fn to_number(&self) -> f64 {
437        assert!(self.is_number());
438        if self.is_double() {
439            self.to_double()
440        } else {
441            self.to_int32() as f64
442        }
443    }
444
445    #[inline(always)]
446    #[cfg(target_pointer_width = "64")]
447    pub fn to_string(&self) -> *mut JSString {
448        assert!(self.is_string());
449        let ptrBits = self.asBits() & JSVAL_PAYLOAD_MASK;
450        ptrBits as usize as *mut JSString
451    }
452
453    #[inline(always)]
454    #[cfg(target_pointer_width = "32")]
455    pub fn to_string(&self) -> *mut JSString {
456        assert!(self.is_string());
457        let ptrBits: u32 = (self.asBits() & 0x00000000FFFFFFFF) as u32;
458        ptrBits as *mut JSString
459    }
460
461    #[inline(always)]
462    pub fn to_object(&self) -> *mut JSObject {
463        assert!(self.is_object());
464        self.to_object_or_null()
465    }
466
467    #[inline(always)]
468    #[cfg(target_pointer_width = "64")]
469    pub fn to_object_or_null(&self) -> *mut JSObject {
470        assert!(self.is_object_or_null());
471        let ptrBits = self.asBits() & JSVAL_PAYLOAD_MASK;
472        assert!((ptrBits & 0x7) == 0);
473        ptrBits as usize as *mut JSObject
474    }
475
476    #[inline(always)]
477    #[cfg(target_pointer_width = "32")]
478    pub fn to_object_or_null(&self) -> *mut JSObject {
479        assert!(self.is_object_or_null());
480        let ptrBits: u32 = (self.asBits() & 0x00000000FFFFFFFF) as u32;
481        ptrBits as *mut JSObject
482    }
483
484    #[inline(always)]
485    pub fn to_symbol(&self) -> *mut Symbol {
486        assert!(self.is_symbol());
487        let ptrBits = self.asBits() & JSVAL_PAYLOAD_MASK;
488        assert!((ptrBits & 0x7) == 0);
489        ptrBits as usize as *mut Symbol
490    }
491
492    #[inline(always)]
493    pub fn to_bigint(&self) -> *mut BigInt {
494        assert!(self.is_bigint());
495        let ptrBits = self.asBits() & JSVAL_PAYLOAD_MASK;
496        assert!((ptrBits & 0x7) == 0);
497        ptrBits as usize as *mut BigInt
498    }
499
500    #[inline(always)]
501    pub fn to_private(&self) -> *const c_void {
502        assert!(self.is_double());
503        #[cfg(target_pointer_width = "64")]
504        assert_eq!(self.asBits() & 0xFFFF000000000000, 0);
505        self.asBits() as usize as *const c_void
506    }
507
508    #[inline(always)]
509    #[cfg(target_pointer_width = "64")]
510    pub fn to_gcthing(&self) -> *mut c_void {
511        assert!(self.is_gcthing());
512        let ptrBits = self.asBits() & JSVAL_PAYLOAD_MASK;
513        assert!((ptrBits & 0x7) == 0);
514        ptrBits as *mut c_void
515    }
516
517    #[inline(always)]
518    #[cfg(target_pointer_width = "32")]
519    pub fn to_gcthing(&self) -> *mut c_void {
520        assert!(self.is_gcthing());
521        let ptrBits: u32 = (self.asBits() & 0x00000000FFFFFFFF) as u32;
522        ptrBits as *mut c_void
523    }
524
525    #[inline(always)]
526    pub fn is_markable(&self) -> bool {
527        self.is_gcthing() && !self.is_null()
528    }
529
530    #[inline(always)]
531    pub fn trace_kind(&self) -> TraceKind {
532        assert!(self.is_markable());
533        if self.is_object() {
534            TraceKind::Object
535        } else if self.is_string() {
536            TraceKind::String
537        } else if self.is_symbol() {
538            TraceKind::Symbol
539        } else {
540            TraceKind::BigInt
541        }
542    }
543}
544
545impl Default for JSVal {
546    fn default() -> JSVal {
547        UndefinedValue()
548    }
549}
550
551#[inline(always)]
552pub unsafe fn JS_ARGV(_cx: *mut JSContext, vp: *mut JSVal) -> *mut JSVal {
553    vp.offset(2)
554}
555
556#[inline(always)]
557pub unsafe fn JS_CALLEE(_cx: *mut JSContext, vp: *mut JSVal) -> JSVal {
558    *vp
559}
560
561// These tests make sure that the Rust definitions agree with the C++ definitions.
562#[test]
563fn test_representation_agreement() {
564    // Annoyingly, we can't check JSObject, JSString, etc. without creating a runtime,
565    // since the constructor has checks that fail if we try mocking.  There are no-check
566    // versions of the setters, but they're private.
567    use crate::jsapi::glue::*;
568    let mut val1 = UndefinedValue();
569    let mut val2;
570
571    unsafe {
572        JS_ValueSetBoolean(&mut val1, true);
573    }
574    val2 = BooleanValue(true);
575    assert_agreement(val1, val2);
576
577    unsafe {
578        JS_ValueSetDouble(&mut val1, 3.14159);
579    }
580    val2 = DoubleValue(3.14159);
581    assert_agreement(val1, val2);
582
583    unsafe {
584        JS_ValueSetInt32(&mut val1, 37);
585    }
586    val2 = Int32Value(37);
587    assert_agreement(val1, val2);
588
589    unsafe {
590        JS_ValueSetNull(&mut val1);
591    }
592    val2 = NullValue();
593    assert_agreement(val1, val2);
594}
595
596#[cfg(test)]
597fn assert_agreement(val1: JSVal, val2: JSVal) {
598    use crate::jsapi::glue::*;
599
600    assert_eq!(val1.asBits(), val2.asBits());
601
602    assert_eq!(unsafe { JS_ValueIsBoolean(&val1) }, val2.is_boolean());
603    if val2.is_boolean() {
604        assert_eq!(unsafe { JS_ValueToBoolean(&val1) }, val2.to_boolean());
605    }
606
607    assert_eq!(unsafe { JS_ValueIsDouble(&val1) }, val2.is_double());
608    if val2.is_double() {
609        assert_eq!(unsafe { JS_ValueToDouble(&val1) }, val2.to_double());
610    }
611
612    assert_eq!(unsafe { JS_ValueIsInt32(&val1) }, val2.is_int32());
613    if val2.is_int32() {
614        assert_eq!(unsafe { JS_ValueToInt32(&val1) }, val2.to_int32());
615    }
616
617    assert_eq!(unsafe { JS_ValueIsNumber(&val1) }, val2.is_number());
618    if val2.is_number() {
619        assert_eq!(unsafe { JS_ValueToNumber(&val1) }, val2.to_number());
620    }
621
622    assert_eq!(unsafe { JS_ValueIsNull(&val1) }, val2.is_null());
623
624    assert_eq!(unsafe { JS_ValueIsUndefined(&val1) }, val2.is_undefined());
625}