Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 23 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
1 change: 1 addition & 0 deletions core/parser/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
38 changes: 30 additions & 8 deletions core/parser/src/parser/expression/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -132,15 +132,12 @@ impl Expression {
allow_await: allow_await.into(),
}
}
}

impl<R> TokenParser<R> for Expression
where
R: ReadChar,
{
type Output = ast::Expression;

fn parse(self, cursor: &mut Cursor<R>, interner: &mut Interner) -> ParseResult<Self::Output> {
fn parse_impl<R: ReadChar>(
self,
cursor: &mut Cursor<R>,
interner: &mut Interner,
) -> ParseResult<ast::Expression> {
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)? {
Expand Down Expand Up @@ -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: FnOnce() -> 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: FnOnce() -> R>(f: F) -> R {
f()
}

impl<R> TokenParser<R> for Expression
where
R: ReadChar,
{
type Output = ast::Expression;

fn parse(self, cursor: &mut Cursor<R>, interner: &mut Interner) -> ParseResult<Self::Output> {
ensure_sufficient_stack(|| self.parse_impl(cursor, interner))
}
}

/// Parses a logical expression expression.
///
/// More information:
Expand Down
17 changes: 17 additions & 0 deletions core/parser/src/parser/tests/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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());
}
Loading