From 72fb0d68b228b3a944421ff5641d02d3abf8242c Mon Sep 17 00:00:00 2001 From: mansiverma897993 Date: Sun, 20 Sep 2026 18:29:33 +0530 Subject: [PATCH] cli: avoid Windows stack overflow on deep expressions --- cli/src/main.rs | 61 ++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 60 insertions(+), 1 deletion(-) diff --git a/cli/src/main.rs b/cli/src/main.rs index 7bab7112c27..635f7b76306 100644 --- a/cli/src/main.rs +++ b/cli/src/main.rs @@ -547,8 +547,36 @@ fn evaluate_files( Ok(()) } -#[expect(clippy::too_many_lines)] fn main() -> Result<()> { + #[cfg(windows)] + return run_with_larger_stack(cli_main)?; + + #[cfg(not(windows))] + cli_main() +} + +#[cfg(windows)] +const WINDOWS_MAIN_STACK_SIZE: usize = 16 * 1024 * 1024; + +/// Runs the CLI on a larger stack than the 1 `MiB` stack allocated for Windows executables. +/// +/// Large, valid JavaScript inputs can contain deeply nested expressions. Lowering those +/// expressions recursively requires more stack than the Windows executable entry point provides. +#[cfg(windows)] +fn run_with_larger_stack( + callback: impl FnOnce() -> T + Send + 'static, +) -> Result { + thread::Builder::new() + .name("boa".to_owned()) + .stack_size(WINDOWS_MAIN_STACK_SIZE) + .spawn(callback) + .wrap_err("failed to start Boa on a dedicated thread")? + .join() + .map_err(|_| eyre!("Boa's dedicated thread panicked")) +} + +#[expect(clippy::too_many_lines)] +fn cli_main() -> Result<()> { color_eyre::config::HookBuilder::default() .display_location_section(false) .display_env_section(false) @@ -826,6 +854,37 @@ fn start_readline_thread( ) } +#[cfg(all(test, windows))] +mod tests { + use super::*; + + #[test] + fn compiles_deeply_nested_expressions_on_the_cli_stack() { + let result = run_with_larger_stack(|| { + const DEPTH: usize = 128; + + let mut source = String::with_capacity(DEPTH * "new f()".len() + 1); + for _ in 0..DEPTH { + source.push_str("new f("); + } + source.push('0'); + for _ in 0..DEPTH { + source.push(')'); + } + source.push(';'); + + let mut context = Context::default(); + let script = Script::parse(Source::from_bytes(&source), None, &mut context) + .expect("deep expression must parse"); + script + .codeblock(&mut context) + .expect("deep expression must compile"); + }); + + assert!(result.is_ok()); + } +} + /// Adds the CLI runtime to the context with default options. fn add_runtime(printer: SharedExternalPrinterLogger, context: &mut Context) { boa_runtime::register(