Skip to main content

hermes_parser/json/
factory.rs

1/*
2 * Copyright (c) Meta Platforms, Inc. and affiliates.
3 *
4 * This source code is licensed under the MIT license found in the
5 * LICENSE file in the root directory of this source tree.
6 */
7
8//! JSONFactory: owns the arena, uniques strings/numbers, shares hidden classes.
9
10use std::cell::RefCell;
11use std::collections::HashMap;
12
13use bumpalo::Bump;
14use hermes_atom_table::{AtomBytes, AtomTable};
15
16use super::{JSONHiddenClass, JSONValue};
17
18/// A single property: (key, value). The key is a `JSONValue::String`.
19pub type Prop<'a> = (&'a JSONValue<'a>, &'a JSONValue<'a>);
20
21/// Owns all JSON nodes (in the arena), uniques strings/numbers, and shares
22/// hidden classes. Port of `JSONFactory` (JSONParser.h:524). Accessors take
23/// `&self`; the returned `&'a JSONValue` lives in the arena, independent of the
24/// transient `RefCell` borrow, so a caller may interleave factory use with
25/// parsing (as `JSONParserTest::SmokeTest2` does).
26pub struct JSONFactory<'a> {
27    arena: &'a Bump,
28    atoms: &'a AtomTable,
29    strings: RefCell<HashMap<AtomBytes, &'a JSONValue<'a>>>,
30    numbers: RefCell<HashMap<u64, &'a JSONValue<'a>>>,
31    classes: RefCell<HashMap<Box<[AtomBytes]>, &'a JSONHiddenClass<'a>>>,
32    null_v: &'a JSONValue<'a>,
33    true_v: &'a JSONValue<'a>,
34    false_v: &'a JSONValue<'a>,
35}
36
37impl<'a> JSONFactory<'a> {
38    /// Create a new factory backed by `arena` and `atoms`.
39    pub fn new(arena: &'a Bump, atoms: &'a AtomTable) -> JSONFactory<'a> {
40        JSONFactory {
41            arena,
42            atoms,
43            strings: RefCell::new(HashMap::new()),
44            numbers: RefCell::new(HashMap::new()),
45            classes: RefCell::new(HashMap::new()),
46            null_v: arena.alloc(JSONValue::Null),
47            true_v: arena.alloc(JSONValue::Boolean(true)),
48            false_v: arena.alloc(JSONValue::Boolean(false)),
49        }
50    }
51
52    /// Returns the arena backing this factory.
53    pub fn arena(&self) -> &'a Bump {
54        self.arena
55    }
56
57    /// Returns the atom table used by this factory.
58    pub fn atoms(&self) -> &'a AtomTable {
59        self.atoms
60    }
61
62    /// Returns the singleton null value.
63    pub fn get_null(&self) -> &'a JSONValue<'a> {
64        self.null_v
65    }
66
67    /// Returns the singleton boolean value for `v`.
68    pub fn get_boolean(&self, v: bool) -> &'a JSONValue<'a> {
69        if v {
70            self.true_v
71        } else {
72            self.false_v
73        }
74    }
75
76    /// JSONParser.cpp:79 — unique a string by its interned handle.
77    pub fn get_string(&self, lit: AtomBytes) -> &'a JSONValue<'a> {
78        if let Some(found) = self.strings.borrow().get(&lit) {
79            return found;
80        }
81        let node: &'a JSONValue<'a> = self.arena.alloc(JSONValue::String(lit));
82        self.strings.borrow_mut().insert(lit, node);
83        node
84    }
85
86    /// JSONParser.cpp:92 — intern `str` then unique.
87    pub fn get_string_str(&self, s: &str) -> &'a JSONValue<'a> {
88        self.get_string(self.atoms.atom_bytes(s))
89    }
90
91    /// JSONParser.cpp:96 — unique a number by its bit pattern (so -0.0 != 0.0,
92    /// matching `JSONNumber::Profile` using DoubleToBits).
93    pub fn get_number(&self, value: f64) -> &'a JSONValue<'a> {
94        let bits = value.to_bits();
95        if let Some(found) = self.numbers.borrow().get(&bits) {
96            return found;
97        }
98        let node: &'a JSONValue<'a> = self.arena.alloc(JSONValue::Number(value));
99        self.numbers.borrow_mut().insert(bits, node);
100        node
101    }
102
103    fn key_bytes(&self, key: &'a JSONValue<'a>) -> AtomBytes {
104        key.as_string().expect("object key must be a JSON string")
105    }
106
107    /// JSONParser.cpp:120 — sort props by key content; return the first
108    /// duplicate key (by interned identity) if any, else None.
109    pub fn sort_props(&self, props: &mut [Prop<'a>]) -> Option<AtomBytes> {
110        props.sort_by(|a, b| {
111            self.atoms
112                .bytes(self.key_bytes(a.0))
113                .cmp(self.atoms.bytes(self.key_bytes(b.0)))
114        });
115        let mut last: Option<AtomBytes> = None;
116        for p in props.iter() {
117            let kb = self.key_bytes(p.0);
118            if last == Some(kb) {
119                return Some(kb);
120            }
121            last = Some(kb);
122        }
123        None
124    }
125
126    /// JSONParser.cpp:109 — look up or create the shared hidden class for `keys`
127    /// (already content-sorted).
128    pub fn get_hidden_class(&self, keys: &[AtomBytes]) -> &'a JSONHiddenClass<'a> {
129        if let Some(found) = self.classes.borrow().get(keys) {
130            return found;
131        }
132        let arena_keys: &'a [AtomBytes] = self.arena.alloc_slice_copy(keys);
133        let cls: &'a JSONHiddenClass<'a> =
134            self.arena.alloc(JSONHiddenClass { keys: arena_keys });
135        self.classes.borrow_mut().insert(keys.into(), cls);
136        cls
137    }
138
139    /// JSONParser.cpp:138 — create an object from props. Sorts + dedups; returns
140    /// None on a duplicate key.
141    pub fn new_object(&self, props: &mut [Prop<'a>]) -> Option<&'a JSONValue<'a>> {
142        if self.sort_props(props).is_some() {
143            return None;
144        }
145        let keys: Vec<AtomBytes> = props.iter().map(|p| self.key_bytes(p.0)).collect();
146        let cls = self.get_hidden_class(&keys);
147        let values: Vec<&'a JSONValue<'a>> = props.iter().map(|p| p.1).collect();
148        let values: &'a [&'a JSONValue<'a>] = self.arena.alloc_slice_copy(&values);
149        Some(self.arena.alloc(JSONValue::Object(cls, values)))
150    }
151
152    /// JSONParser.cpp:138 with propsAreSorted=true — props already sorted and
153    /// dup-checked.
154    pub fn new_object_sorted(&self, props: &[Prop<'a>]) -> Option<&'a JSONValue<'a>> {
155        let keys: Vec<AtomBytes> = props.iter().map(|p| self.key_bytes(p.0)).collect();
156        let cls = self.get_hidden_class(&keys);
157        let values: Vec<&'a JSONValue<'a>> = props.iter().map(|p| p.1).collect();
158        let values: &'a [&'a JSONValue<'a>] = self.arena.alloc_slice_copy(&values);
159        Some(self.arena.alloc(JSONValue::Object(cls, values)))
160    }
161
162    /// JSONParser.h:617 — create an array from values.
163    pub fn new_array(&self, values: &[&'a JSONValue<'a>]) -> &'a JSONValue<'a> {
164        let values: &'a [&'a JSONValue<'a>] = self.arena.alloc_slice_copy(values);
165        self.arena.alloc(JSONValue::Array(values))
166    }
167}
168
169#[cfg(test)]
170mod factory_tests {
171    use super::super::*;
172    use bumpalo::Bump;
173    use hermes_atom_table::AtomTable;
174
175    #[test]
176    fn objects_arrays_and_hidden_class_sharing() {
177        use super::super::JSONFactory;
178        let arena = Bump::new();
179        let atoms = AtomTable::new();
180        let f = JSONFactory::new(&arena, &atoms);
181
182        fn mk<'b>(f: &'b JSONFactory<'b>, k1: f64, k2: f64) -> &'b super::super::JSONValue<'b> {
183            // object {'key1': k1, 'key2': k2} via unsorted props
184            let p1 = (f.get_string_str("key1"), f.get_number(k1));
185            let p2 = (f.get_string_str("key2"), f.get_number(k2));
186            f.new_object(&mut [p2, p1]).unwrap() // intentionally unsorted
187        }
188        let o1 = mk(&f, 1.0, 2.0);
189        let o3 = mk(&f, 20.0, 10.0);
190
191        let v1 = o1.as_object().unwrap();
192        assert_eq!(v1.size(), 2);
193        // shared hidden class for same-shape objects (HiddenClassTest).
194        assert!(std::ptr::eq(
195            v1.get_hidden_class(),
196            o3.as_object().unwrap().get_hidden_class()
197        ));
198        // lookups
199        assert_eq!(v1.count("key1", &atoms), 1);
200        assert_eq!(v1.count("zzz", &atoms), 0);
201        assert_eq!(v1.get("key1", &atoms).and_then(|v| v.as_number()), Some(1.0));
202        // duplicate keys -> error
203        let dup = (f.get_string_str("k"), f.get_number(1.0));
204        assert!(f.new_object(&mut [dup, dup]).is_none());
205
206        // arrays
207        let a = f.new_array(&[f.get_number(5.0), f.get_null()]);
208        let av = a.as_array().unwrap();
209        assert_eq!(av.len(), 2);
210        assert_eq!(av.at(0).as_number(), Some(5.0));
211    }
212
213    #[test]
214    fn object_positional_accessors_and_iter_sorted_order() {
215        use super::super::JSONFactory;
216        let arena = Bump::new();
217        let atoms = AtomTable::new();
218        let f = JSONFactory::new(&arena, &atoms);
219        // Insert keys out of order; hidden class / iteration is SORTED order.
220        let obj = f
221            .new_object(&mut [
222                (f.get_string_str("b"), f.get_number(2.0)),
223                (f.get_string_str("a"), f.get_number(1.0)),
224                (f.get_string_str("c"), f.get_number(3.0)),
225            ])
226            .unwrap();
227        let o = obj.as_object().unwrap();
228        assert_eq!(o.size(), 3);
229        // value_at / key_at follow sorted key order: a,b,c
230        assert_eq!(atoms.bytes(o.key_at(0)), b"a");
231        assert_eq!(o.value_at(0).as_number(), Some(1.0));
232        assert_eq!(atoms.bytes(o.key_at(2)), b"c");
233        assert_eq!(o.value_at(2).as_number(), Some(3.0));
234        // iter yields (key, value) pairs in sorted order.
235        let collected: Vec<(Vec<u8>, f64)> = o
236            .iter()
237            .map(|(k, v)| (atoms.bytes(k).to_vec(), v.as_number().unwrap()))
238            .collect();
239        assert_eq!(
240            collected,
241            vec![(b"a".to_vec(), 1.0), (b"b".to_vec(), 2.0), (b"c".to_vec(), 3.0)]
242        );
243    }
244
245    #[test]
246    fn uniquing_and_singletons() {
247        let arena = Bump::new();
248        let atoms = AtomTable::new();
249        let f = JSONFactory::new(&arena, &atoms);
250
251        // Strings unique by content.
252        let a = f.get_string_str("key2");
253        let b = f.get_string_str("key2");
254        assert!(std::ptr::eq(a, b));
255        assert_eq!(a.as_string().map(|h| atoms.bytes(h).to_vec()), Some(b"key2".to_vec()));
256
257        // Numbers unique; -0.0 distinct from 0.0 (NegativeNumbers).
258        assert!(std::ptr::eq(f.get_number(1.0), f.get_number(1.0)));
259        assert!(!std::ptr::eq(f.get_number(0.0), f.get_number(-0.0)));
260
261        // Singletons.
262        assert!(std::ptr::eq(f.get_null(), f.get_null()));
263        assert!(std::ptr::eq(f.get_boolean(true), f.get_boolean(true)));
264        assert!(!std::ptr::eq(f.get_boolean(true), f.get_boolean(false)));
265    }
266}