Skip to main content

javascript_globals/
lib.rs

1//! # JavaScript Globals
2//!
3//! Global identifiers from different JavaScript environments
4//!
5//! Rust fork of <https://www.npmjs.com/package/globals>
6//!
7//! ## Representation
8//!
9//! Every unique global name has one shared `u16` ID. A [`GlobalSet`] stores one membership bit
10//! per ID and a sorted list of the writable IDs, so environments do not repeat names or hash-table
11//! metadata. Identical environments share the same static set.
12//!
13//! The generated `data.bin` contains the PHF tables, concatenated UTF-8 names, name offsets,
14//! membership bitsets, and writable-ID lists. The `generated` module converts those byte ranges
15//! into typed static arrays during const evaluation; no data is parsed or allocated at runtime.
16//!
17//! A name lookup uses the shared PHF to find a candidate ID, verifies the candidate string, checks
18//! the environment's membership bit, then binary-searches its writable IDs. Iteration scans set
19//! bits and uses a const-generated reference table into the shared name blob.
20//!
21//! Environment lookup uses a generated string match. The separate environment array is referenced
22//! only by iteration, allowing linkers to discard it when callers use only [`Globals::get`].
23
24pub use generated::{GLOBALS, GLOBALS_BUILTIN, GLOBALS_ES2026};
25
26/// A compact map of global names to their writability.
27pub struct GlobalSet {
28    /// One bit for every ID in the shared global-name index.
29    members: &'static [u8; generated::GLOBAL_NAME_BYTES],
30    /// Sorted IDs for the writable members of this environment; other members are read-only.
31    writable: &'static [u16],
32}
33
34impl GlobalSet {
35    /// Returns whether the map contains `key`.
36    pub fn contains_key(&self, key: &str) -> bool {
37        self.global_id(key).is_some()
38    }
39
40    /// Returns the writability of `key`.
41    pub fn get(&self, key: &str) -> Option<&'static bool> {
42        let id = self.global_id(key)?;
43        Some(self.writable_value(id))
44    }
45
46    /// Returns an iterator over names.
47    pub fn keys(&self) -> impl Iterator<Item = &'static &'static str> + '_ {
48        self.into_iter().map(|entry| entry.0)
49    }
50}
51
52impl<'a> IntoIterator for &'a GlobalSet {
53    type Item = (&'static &'static str, &'static bool);
54    type IntoIter = GlobalEntries<'a>;
55
56    fn into_iter(self) -> Self::IntoIter {
57        GlobalEntries { set: self, ids: 0..generated::GLOBAL_NAME_COUNT }
58    }
59}
60
61/// Iterator over a [`GlobalSet`]'s entries.
62pub struct GlobalEntries<'a> {
63    set: &'a GlobalSet,
64    ids: core::ops::Range<usize>,
65}
66
67impl Iterator for GlobalEntries<'_> {
68    type Item = (&'static &'static str, &'static bool);
69
70    fn next(&mut self) -> Option<Self::Item> {
71        while self.ids.start < self.ids.end {
72            let byte_index = self.ids.start / 8;
73            // Ignore IDs already visited in this byte, then jump directly to its next set bit.
74            let byte = self.set.members[byte_index] & (u8::MAX << (self.ids.start % 8));
75            if byte != 0 {
76                let id = byte_index * 8 + byte.trailing_zeros() as usize;
77                if id < self.ids.end {
78                    self.ids.start = id + 1;
79                    return Some(self.set.entry(id as u16));
80                }
81            }
82            self.ids.start = ((byte_index + 1) * 8).min(self.ids.end);
83        }
84        None
85    }
86}
87
88/// A map of environment names to their global variable maps.
89pub struct Globals;
90
91impl Globals {
92    /// Returns an iterator over the entries of the globals map.
93    pub fn entries(&self) -> impl Iterator<Item = (&'static str, &'static GlobalSet)> + '_ {
94        generated::ENVIRONMENTS.iter().copied()
95    }
96
97    /// Returns the globals map for the given environment name.
98    pub fn get(&self, key: &str) -> Option<&'static GlobalSet> {
99        generated::get_environment(key)
100    }
101}
102
103mod generated;
104
105/// Shared perfect-hash index and compact name storage.
106struct GlobalNames {
107    seed: u64,
108    pilots: &'static [u8],
109    remap: &'static [u32],
110    /// Every name concatenated in PHF slot order.
111    names: &'static [u8],
112    /// One byte offset per name plus a final sentinel.
113    offsets: &'static [u16],
114}
115
116impl GlobalNames {
117    /// Returns the ID only when the PHF candidate contains the exact input string.
118    fn get(&self, name: &str) -> Option<u16> {
119        let hash = phf_shared::ptrhash::hash(name, &self.seed);
120        let index = phf_shared::ptrhash::get_index(
121            self.seed,
122            hash,
123            self.pilots,
124            self.remap,
125            generated::GLOBAL_NAME_COUNT,
126        ) as usize;
127        (self.name(index) == name).then_some(index as u16)
128    }
129
130    fn name(&self, id: usize) -> &'static str {
131        // SAFETY: `offsets` is generated with one valid boundary per name plus a final sentinel,
132        // PHF always returns an ID below `GLOBAL_NAME_COUNT`, and the byte blob is built from
133        // valid UTF-8 strings.
134        unsafe {
135            let start = usize::from(*self.offsets.get_unchecked(id));
136            let end = usize::from(*self.offsets.get_unchecked(id + 1));
137            core::str::from_utf8_unchecked(self.names.get_unchecked(start..end))
138        }
139    }
140}
141
142impl GlobalSet {
143    /// Resolves `key` in the shared name index, then checks this environment's membership bit.
144    fn global_id(&self, key: &str) -> Option<u16> {
145        let id = generated::GLOBAL_NAMES.get(key)?;
146        self.contains_id(id).then_some(id)
147    }
148
149    fn contains_id(&self, id: u16) -> bool {
150        let id = usize::from(id);
151        self.members[id / 8] & (1 << (id % 8)) != 0
152    }
153
154    fn writable_value(&self, id: u16) -> &'static bool {
155        if self.writable.binary_search(&id).is_ok() { &true } else { &false }
156    }
157
158    fn entry(&self, id: u16) -> (&'static &'static str, &'static bool) {
159        (&generated::GLOBAL_NAME_REFS[usize::from(id)], self.writable_value(id))
160    }
161}