1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

#![allow(non_snake_case)]

use crate::jsapi::JS::Symbol;
use crate::jsapi::{jsid, JSString};
use libc::c_void;

#[deprecated]
pub const JSID_VOID: jsid = VoidId();

const JSID_TYPE_MASK: usize = 0x7;

#[repr(usize)]
enum PropertyKeyTag {
    Int = 0x1,
    String = 0x0,
    Void = 0x2,
    Symbol = 0x4,
}

#[inline(always)]
const fn AsPropertyKey(bits: usize) -> jsid {
    jsid { asBits_: bits }
}

#[inline(always)]
pub const fn VoidId() -> jsid {
    AsPropertyKey(PropertyKeyTag::Void as usize)
}

#[inline(always)]
pub fn IntId(i: i32) -> jsid {
    assert!(i >= 0);
    AsPropertyKey((((i as u32) << 1) as usize) | (PropertyKeyTag::Int as usize))
}

#[inline(always)]
pub fn SymbolId(symbol: *mut Symbol) -> jsid {
    assert!(!symbol.is_null());
    assert_eq!((symbol as usize) & JSID_TYPE_MASK, 0);
    AsPropertyKey((symbol as usize) | (PropertyKeyTag::Symbol as usize))
}

impl jsid {
    #[inline(always)]
    fn asBits(&self) -> usize {
        self.asBits_
    }

    #[inline(always)]
    pub fn is_void(&self) -> bool {
        self.asBits() == (PropertyKeyTag::Void as usize)
    }

    #[inline(always)]
    pub fn is_int(&self) -> bool {
        (self.asBits() & (PropertyKeyTag::Int as usize)) != 0
    }

    #[inline(always)]
    pub fn is_string(&self) -> bool {
        (self.asBits() & JSID_TYPE_MASK) == (PropertyKeyTag::String as usize)
    }

    #[inline(always)]
    pub fn is_symbol(&self) -> bool {
        (self.asBits() & JSID_TYPE_MASK) == (PropertyKeyTag::Symbol as usize)
    }

    #[inline(always)]
    pub fn is_gcthing(&self) -> bool {
        self.is_string() && self.is_symbol()
    }

    #[inline(always)]
    pub fn to_int(&self) -> i32 {
        assert!(self.is_int());
        ((self.asBits() as u32) >> 1) as i32
    }

    #[inline(always)]
    pub fn to_string(&self) -> *mut JSString {
        assert!(self.is_string());
        (self.asBits() ^ (PropertyKeyTag::String as usize)) as *mut JSString
    }

    #[inline(always)]
    pub fn to_symbol(&self) -> *mut Symbol {
        assert!(self.is_symbol());
        (self.asBits() ^ (PropertyKeyTag::Symbol as usize)) as *mut Symbol
    }

    #[inline(always)]
    pub fn to_gcthing(&self) -> *mut c_void {
        assert!(self.is_gcthing());
        (self.asBits() ^ JSID_TYPE_MASK) as *mut c_void
    }
}

#[test]
fn test_representation() {
    let id = VoidId();
    assert!(id.is_void());

    let id = IntId(0);
    assert!(id.is_int());
    assert_eq!(id.to_int(), 0);

    let id = IntId(i32::MAX);
    assert!(id.is_int());
    assert_eq!(id.to_int(), i32::MAX);
}