From a1f22b216ff66e0eb1a33ae01e4f8d2cdaa9464b Mon Sep 17 00:00:00 2001 From: cijiugechu Date: Sat, 28 Jun 2025 15:43:34 +0800 Subject: [PATCH] Resolve the problem of parser stack overflow --- Cargo.lock | 23 ++++++++++++++ Cargo.toml | 1 + core/parser/Cargo.toml | 1 + core/parser/src/parser/expression/mod.rs | 38 +++++++++++++++++++----- core/parser/src/parser/tests/mod.rs | 17 +++++++++++ 5 files changed, 72 insertions(+), 8 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 8a45cacabc8..ed986b1bdc4 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -525,6 +525,7 @@ dependencies = [ "num-traits", "regress", "rustc-hash 2.1.1", + "stacker", ] [[package]] @@ -2942,6 +2943,15 @@ dependencies = [ "unicode-ident", ] +[[package]] +name = "psm" +version = "0.1.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e944464ec8536cd1beb0bbfd96987eb5e3b72f2ecdafdc5c769a37f1fa2ae1f" +dependencies = [ + "cc", +] + [[package]] name = "quote" version = "1.0.40" @@ -3480,6 +3490,19 @@ version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3" +[[package]] +name = "stacker" +version = "0.1.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cddb07e32ddb770749da91081d8d0ac3a16f1a569a18b20348cd371f5dead06b" +dependencies = [ + "cc", + "cfg-if", + "libc", + "psm", + "windows-sys 0.59.0", +] + [[package]] name = "static_assertions" version = "1.1.0" diff --git a/Cargo.toml b/Cargo.toml index c3dc0bc6b29..97624b318c7 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -131,6 +131,7 @@ test-case = "3.3.1" url = "2.5.4" tokio = { version = "1.45.1", default-features = false } futures-concurrency = "7.6.3" +stacker = "0.1.21" # ICU4X diff --git a/core/parser/Cargo.toml b/core/parser/Cargo.toml index d1594e5d26e..b2751bcf981 100644 --- a/core/parser/Cargo.toml +++ b/core/parser/Cargo.toml @@ -21,6 +21,7 @@ bitflags.workspace = true num-bigint.workspace = true regress.workspace = true icu_properties.workspace = true +stacker.workspace = true [dev-dependencies] indoc.workspace = true diff --git a/core/parser/src/parser/expression/mod.rs b/core/parser/src/parser/expression/mod.rs index 2eca1bd90fa..8f1f8dfa09c 100644 --- a/core/parser/src/parser/expression/mod.rs +++ b/core/parser/src/parser/expression/mod.rs @@ -132,15 +132,12 @@ impl Expression { allow_await: allow_await.into(), } } -} -impl TokenParser for Expression -where - R: ReadChar, -{ - type Output = ast::Expression; - - fn parse(self, cursor: &mut Cursor, interner: &mut Interner) -> ParseResult { + fn parse_impl( + self, + cursor: &mut Cursor, + interner: &mut Interner, + ) -> ParseResult { let mut lhs = AssignmentExpression::new(self.allow_in, self.allow_yield, self.allow_await) .parse(cursor, interner)?; while let Some(tok) = cursor.peek(0, interner)? { @@ -182,6 +179,31 @@ where } } +/// See [Rustc](https://github.com/rust-lang/rust/blob/master/compiler/rustc_data_structures/src/stack.rs) +#[cfg(not(target_arch = "wasm32"))] +#[inline] +fn ensure_sufficient_stack R>(f: F) -> R { + const RED_ZONE: usize = 100 * 1024; // 100k + const STACK_PER_RECURSION: usize = 1024 * 1024; // 1MB + stacker::maybe_grow(RED_ZONE, STACK_PER_RECURSION, f) +} +#[cfg(target_arch = "wasm32")] +#[inline] +fn ensure_sufficient_stack R>(f: F) -> R { + f() +} + +impl TokenParser for Expression +where + R: ReadChar, +{ + type Output = ast::Expression; + + fn parse(self, cursor: &mut Cursor, interner: &mut Interner) -> ParseResult { + ensure_sufficient_stack(|| self.parse_impl(cursor, interner)) + } +} + /// Parses a logical expression expression. /// /// More information: diff --git a/core/parser/src/parser/tests/mod.rs b/core/parser/src/parser/tests/mod.rs index 526ced72a2a..b1acd13ca02 100644 --- a/core/parser/src/parser/tests/mod.rs +++ b/core/parser/src/parser/tests/mod.rs @@ -777,3 +777,20 @@ fn stress_test_operations() { .parse_script(&Scope::new_global(), &mut Interner::default()) .is_ok()); } + +#[test] +fn should_not_stack_overflow() { + let mut should_ok = String::new(); + let n = 32; + (0..n).for_each(|_| should_ok += "(function(){ "); + (0..n).for_each(|_| should_ok += " })()"); + let should_err = "+[4446<4444^/ /g/ /g[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[44444 ]"; + + assert!(Parser::new(Source::from_bytes(&should_ok)) + .parse_script(&Scope::new_global(), &mut Interner::default()) + .is_ok()); + + assert!(Parser::new(Source::from_bytes(&should_err)) + .parse_script(&Scope::new_global(), &mut Interner::default()) + .is_err()); +}