Skip to main content

hermes_parser/json/
parser.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//! JSONParser: recursive-descent parser driving JSLexer.
9//!
10//! Value nesting is depth-limited, matching C++ `JSONParser` (`lib/Parser/
11//! JSONParser.cpp:202-211`, `JSONParser.h:636-659`): `parse_value` checks
12//! `MAX_RECURSION_DEPTH` and reports "Too many nested JSON values" instead
13//! of overflowing the native stack. Historically NEITHER side had a limit
14//! (parity by absence, and both died on e.g. 100000 `[`); upstream added one
15//! in `304c1533c` ("Add a recursion limit to compiler JSONParser") and this
16//! is the mirror of that fix. The limit is NOT the JS parser's: off Windows
17//! upstream set it to 4x `JSParserImpl::MAX_RECURSION_DEPTH`, because a JSON
18//! nesting level costs far less native stack than a JS one.
19
20use hermes_atom_table::AtomTable;
21use hermes_support::diag::Subsystem;
22use hermes_support::location::{SMRange, SourceId};
23use hermes_support::manager::SourceErrorManager;
24
25use crate::lexer::{GrammarContext, JSLexer};
26use crate::token::Token;
27use crate::token_kinds::TokenKind;
28
29use super::factory::Prop;
30use super::{JSONFactory, JSONValue};
31
32/// JSON grammar uses `/` as division (never regexp).
33const CTX: GrammarContext = GrammarContext::AllowDiv;
34
35/// The maximum depth of value nesting, to avoid stack overflow on deeply
36/// nested input. Port of `JSONParser::MAX_RECURSION_DEPTH`
37/// (JSONParser.h:639-659). Its `#ifdef` ladder has the same SHAPE as
38/// `JSParserImpl::MAX_RECURSION_DEPTH` but deliberately not the same VALUES:
39/// upstream `304c1533c` set the non-Windows arms to 4x the JS parser's,
40/// "since a nesting level here is cheaper than one in JSParserImpl" (the
41/// Windows arms, which the port does not model, still match JSParserImpl
42/// because the default stack there is 1MB).
43///
44/// The Rust mapping is the same as `crate::js::MAX_RECURSION_DEPTH`'s: key
45/// off `debug_assertions`, which pairs a DEBUG Rust build with the project's
46/// standard ASan C++ oracle (both take the `HERMES_LIMIT_STACK_DEPTH` arm,
47/// 512) and a RELEASE Rust build with C++'s default arm (4096). See
48/// `crate::js::MAX_RECURSION_DEPTH` for the full ladder and the
49/// profile-pairing caveat.
50const MAX_RECURSION_DEPTH: u32 = if cfg!(debug_assertions) { 512 } else { 4096 };
51
52/// Port of `JSONParser` (JSONParser.h:630). Drives `JSLexer`; errors go through
53/// the lexer's single `&mut SourceErrorManager`.
54pub struct JSONParser<'a> {
55    factory: &'a JSONFactory<'a>,
56    lexer: JSLexer<'a>,
57    /// The current depth of value nesting during parsing. Port of
58    /// `JSONParser::recursionDepth_` (JSONParser.h:637).
59    recursion_depth: u32,
60}
61
62impl<'a> JSONParser<'a> {
63    /// Construct a `JSONParser` over the source buffer identified by `buf_id` in
64    /// `sm`. Port of `JSONParser::JSONParser` (JSONParser.h:666-678).
65    pub fn new(
66        factory: &'a JSONFactory<'a>,
67        buf_id: SourceId,
68        sm: &'a mut SourceErrorManager,
69        atoms: &'a AtomTable,
70        convert_surrogates: bool,
71    ) -> JSONParser<'a> {
72        let lexer =
73            JSLexer::new_with_convert_surrogates(buf_id, sm, atoms, CTX, convert_surrogates);
74        JSONParser {
75            factory,
76            lexer,
77            recursion_depth: 0,
78        }
79    }
80
81    /// Returns the number of errors reported so far (via the shared
82    /// `SourceErrorManager`).
83    pub fn error_count(&self) -> u32 {
84        self.lexer.get_source_mgr().error_count()
85    }
86
87    /// Report an error at the current token's range. Port of JSONParser.h:691.
88    fn error(&mut self, msg: impl Into<String>) {
89        let range: SMRange = self.lexer.token().source_range();
90        self.lexer.get_source_mgr_mut().error_at(
91            range.start,
92            Some(range),
93            msg.into(),
94            Subsystem::Parser,
95        );
96    }
97
98    /// Return a reference to the current token (immutable borrow of lexer).
99    fn cur(&self) -> &Token {
100        self.lexer.token()
101    }
102
103    /// Advance the lexer and return the new current token.
104    fn advance(&mut self) -> &Token {
105        self.lexer.advance(CTX)
106    }
107
108    /// Parse the whole JSON input. Port of JSONParser.cpp:192.
109    pub fn parse(&mut self) -> Option<&'a JSONValue<'a>> {
110        self.advance();
111        let res = self.parse_value()?;
112        if self.lexer.get_source_mgr().error_count() != 0 {
113            return None;
114        }
115        Some(res)
116    }
117
118    /// Check and update the recursion depth, then parse any JSON value. Port of
119    /// `JSONParser::parseValue` (JSONParser.cpp:202-212).
120    fn parse_value(&mut self) -> Option<&'a JSONValue<'a>> {
121        if self.recursion_depth >= MAX_RECURSION_DEPTH {
122            self.error("Too many nested JSON values");
123            return None;
124        }
125        self.recursion_depth += 1;
126        let res = self.parse_value_impl();
127        self.recursion_depth -= 1;
128        res
129    }
130
131    /// Parse any JSON value, assuming the recursion depth has been checked.
132    /// Port of `JSONParser::parseValueImpl` (JSONParser.cpp:213).
133    fn parse_value_impl(&mut self) -> Option<&'a JSONValue<'a>> {
134        let mut needs_negation = false;
135        match self.cur().kind() {
136            TokenKind::string_literal => {
137                // Read the interned atom before advancing (borrow-checker: avoid
138                // holding &Token across the mutable advance() call).
139                let lit = self.cur().get_string_literal();
140                self.advance();
141                Some(self.factory.get_string(lit))
142            }
143            TokenKind::minus => {
144                needs_negation = true;
145                self.advance();
146                if self.cur().kind() != TokenKind::numeric_literal {
147                    self.error("No numeric literal following minus (-) token in value");
148                    return None;
149                }
150                self.parse_number(needs_negation)
151            }
152            TokenKind::numeric_literal => self.parse_number(needs_negation),
153            TokenKind::l_brace => {
154                self.advance();
155                self.parse_object()
156            }
157            TokenKind::l_square => {
158                self.advance();
159                self.parse_array()
160            }
161            TokenKind::rw_true => {
162                self.advance();
163                Some(self.factory.get_boolean(true))
164            }
165            TokenKind::rw_false => {
166                self.advance();
167                Some(self.factory.get_boolean(false))
168            }
169            TokenKind::rw_null => {
170                self.advance();
171                Some(self.factory.get_null())
172            }
173            _ => {
174                self.error("JSON object or array expected");
175                None
176            }
177        }
178    }
179
180    /// Parse a numeric literal (with optional leading negation).
181    /// Reads the f64 value before advancing to satisfy the borrow checker.
182    fn parse_number(&mut self, needs_negation: bool) -> Option<&'a JSONValue<'a>> {
183        let v = self.cur().get_numeric_literal();
184        let res = self.factory.get_number(if needs_negation { -v } else { v });
185        self.advance();
186        Some(res)
187    }
188
189    /// JSONParser.cpp:260 — parse `[ ... ]` (the `[` already consumed).
190    fn parse_array(&mut self) -> Option<&'a JSONValue<'a>> {
191        let mut storage: Vec<&'a JSONValue<'a>> = Vec::new();
192        if self.cur().kind() != TokenKind::r_square {
193            loop {
194                let val = self.parse_value()?;
195                storage.push(val);
196                if self.cur().kind() == TokenKind::comma {
197                    self.advance();
198                    if self.cur().kind() == TokenKind::r_square {
199                        break;
200                    }
201                } else {
202                    break;
203                }
204            }
205            if self.cur().kind() != TokenKind::r_square {
206                self.error("expected ']'");
207                return None;
208            }
209        }
210        self.advance(); // consume ']'
211        Some(self.factory.new_array(&storage))
212    }
213
214    /// JSONParser.cpp:289 — parse `{ ... }` (the `{` already consumed).
215    fn parse_object(&mut self) -> Option<&'a JSONValue<'a>> {
216        let mut pairs: Vec<Prop<'a>> = Vec::new();
217        if self.cur().kind() != TokenKind::r_brace {
218            loop {
219                if self.cur().kind() != TokenKind::string_literal {
220                    self.error("expected a string");
221                    return None;
222                }
223                let key = self.factory.get_string(self.cur().get_string_literal());
224                if self.advance().kind() != TokenKind::colon {
225                    self.error("expected ':'");
226                    return None;
227                }
228                self.advance();
229                let val = self.parse_value()?;
230                pairs.push((key, val));
231                if self.cur().kind() == TokenKind::comma {
232                    self.advance();
233                    if self.cur().kind() == TokenKind::r_brace {
234                        break;
235                    }
236                } else {
237                    break;
238                }
239            }
240            if self.cur().kind() != TokenKind::r_brace {
241                self.error("expected '}'");
242                return None;
243            }
244        }
245        self.advance(); // consume '}'
246
247        if let Some(dup) = self.factory.sort_props(&mut pairs) {
248            let name = String::from_utf8_lossy(self.factory.atoms().bytes(dup)).into_owned();
249            self.error(format!("key '{name}' is already present"));
250            return None;
251        }
252        // Already sorted + dup-checked: build directly.
253        self.factory.new_object_sorted(&pairs)
254    }
255}
256
257#[cfg(test)]
258mod parser_tests {
259    use super::super::*;
260    use bumpalo::Bump;
261    use hermes_atom_table::AtomTable;
262    use hermes_support::manager::SourceErrorManager;
263
264    /// Helper: parse `src` and return the JSON value (if successful).
265    /// `sm` must outlive the call so the returned `&'a JSONValue<'a>` is valid.
266    fn parse_ok<'a>(
267        arena: &'a Bump,
268        atoms: &'a AtomTable,
269        sm: &'a mut SourceErrorManager,
270        src: &str,
271    ) -> Option<&'a JSONValue<'a>> {
272        // Mirrors `JSONParser parser(factory, src, sm); parser.parse()`.
273        let f = arena.alloc(JSONFactory::new(arena, atoms));
274        let id = sm.add_buffer("json", src);
275        let mut p = JSONParser::new(f, id, sm, atoms, false);
276        p.parse()
277    }
278
279    #[test]
280    fn scalars() {
281        let arena = Bump::new();
282        let atoms = AtomTable::new();
283        let mut sm = SourceErrorManager::new();
284        assert_eq!(
285            parse_ok(&arena, &atoms, &mut sm, "true").and_then(|v| v.as_boolean()),
286            Some(true)
287        );
288        assert_eq!(
289            parse_ok(&arena, &atoms, &mut sm, "false").and_then(|v| v.as_boolean()),
290            Some(false)
291        );
292        assert_eq!(
293            parse_ok(&arena, &atoms, &mut sm, "null").map(|v| v.kind()),
294            Some(JSONKind::Null)
295        );
296        assert_eq!(
297            parse_ok(&arena, &atoms, &mut sm, "42").and_then(|v| v.as_number()),
298            Some(42.0)
299        );
300        assert_eq!(
301            parse_ok(&arena, &atoms, &mut sm, "-1.5").and_then(|v| v.as_number()),
302            Some(-1.5)
303        );
304        let s = parse_ok(&arena, &atoms, &mut sm, "'hi'")
305            .unwrap()
306            .as_string()
307            .unwrap();
308        assert_eq!(atoms.bytes(s), b"hi");
309    }
310
311    /// Parse `src` in a self-contained scope and run `f` on the result + atom
312    /// table while everything is still alive. Returns whatever `f` returns.
313    fn with_parse<R>(src: &str, f: impl FnOnce(Option<&JSONValue<'_>>, &AtomTable) -> R) -> R {
314        let arena = Bump::new();
315        let atoms = AtomTable::new();
316        let factory = arena.alloc(JSONFactory::new(&arena, &atoms));
317        let mut sm = SourceErrorManager::new();
318        let id = sm.add_buffer("json", src);
319        let mut parser = JSONParser::new(factory, id, &mut sm, &atoms, false);
320        let result = parser.parse();
321        f(result, &atoms)
322    }
323
324    #[test]
325    fn arrays() {
326        with_parse("[-1.0, -1, -0]", |r, _| {
327            let v = r.unwrap().as_array().unwrap();
328            assert_eq!(v.len(), 3);
329            assert_eq!(v.at(0).as_number(), Some(-1.0));
330            assert_eq!(v.at(2).as_number(), Some(-0.0));
331        });
332        with_parse("[]", |r, _| assert!(r.unwrap().as_array().unwrap().is_empty()));
333        // trailing comma is accepted (mirror C++: after a comma, ']' breaks the loop).
334        with_parse("[1,2,3,]", |r, _| assert!(r.is_some()));
335        // unterminated -> failure.
336        with_parse("[1,2", |r, _| assert!(r.is_none()));
337    }
338
339    #[test]
340    fn lone_minus_errors() {
341        // NegativeNumbers: "-" -> failure, error count 1.
342        let arena = Bump::new();
343        let atoms = AtomTable::new();
344        let f = arena.alloc(JSONFactory::new(&arena, &atoms));
345        let mut sm = SourceErrorManager::new();
346        let id = sm.add_buffer("json", "-");
347        let mut p = JSONParser::new(f, id, &mut sm, &atoms, false);
348        assert!(p.parse().is_none());
349        assert_eq!(p.error_count(), 1);
350    }
351}