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