Skip to content

fix(rowan): avoid stack overflow when dropping deep trees - #11368

Open
Austin1serb wants to merge 2 commits into
biomejs:mainfrom
Austin1serb:fix/6830-iterative-green-drop
Open

fix(rowan): avoid stack overflow when dropping deep trees#11368
Austin1serb wants to merge 2 commits into
biomejs:mainfrom
Austin1serb:fix/6830-iterative-green-drop

Conversation

@Austin1serb

Copy link
Copy Markdown
Contributor

Summary

Fixes #6830

Biome can successfully read the deeply nested JavaScript file from the issue, but then crashes while cleaning it up.

The cleanup was recursive, so every nested level left another unfinished function call on the stack. With deeply nested input, those calls kept piling up until the stack was exhausted and Biome crashed.

I had Codex run the reproduction against older versions to see if this was a recent regression. The same behavior goes back years, so it does not appear to come from a specific recent PR or release.

This changes cleanup to use a loop instead of recursion. The loop keeps track of the remaining cleanup work itself and handles it one item at a time, so deeply nested input no longer makes the stack grow.

This PR was created with AI assistance. The solution was reviewed and validated by me.

Test Plan

I added tests for very deep input, branching structures, and shared data.

The original reproduction from the issue now completes successfully with stack sizes down to 512 KiB.

The tests also verify that cleanup still happens correctly and that the fix does not simply avoid the crash by leaking memory.

I also ran the affected parser tests, just f, and just l.

Docs

Not applicable.

@changeset-bot

changeset-bot Bot commented Aug 16, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: 64e2f7d

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 13 packages
Name Type
@biomejs/biome Patch
@biomejs/cli-win32-x64 Patch
@biomejs/cli-win32-arm64 Patch
@biomejs/cli-darwin-x64 Patch
@biomejs/cli-darwin-arm64 Patch
@biomejs/cli-linux-x64 Patch
@biomejs/cli-linux-arm64 Patch
@biomejs/cli-linux-x64-musl Patch
@biomejs/cli-linux-arm64-musl Patch
@biomejs/wasm-web Patch
@biomejs/wasm-bundler Patch
@biomejs/wasm-nodejs Patch
@biomejs/backend-jsonrpc Patch

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@github-actions

Copy link
Copy Markdown
Contributor

✅ Organic activity

No automation signals detected in the analyzed events.

View full analysis →

This is an automated analysis by AgentScan

@github-actions github-actions Bot added the A-Core Area: core label Aug 16, 2026
@coderabbitai

coderabbitai Bot commented Aug 16, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: 4b216f9f-9658-4f3c-8578-c3cdd0a16dfb

📥 Commits

Reviewing files that changed from the base of the PR and between 52b5971 and 64e2f7d.

📒 Files selected for processing (2)
  • crates/biome_rowan/src/arc.rs
  • crates/biome_rowan/src/green/node.rs
🚧 Files skipped from review as they are similar to previous changes (1)
  • crates/biome_rowan/src/green/node.rs

Included review availability: Your plan includes up to 10 reviews per rolling hour; 8 remain after this review.


Walkthrough

GreenNode now stores its ThinArc in ManuallyDrop and releases nested nodes through iterative traversal. The implementation preserves shared-node reference counts and handles tokens and empty slots. Tests cover constrained-stack deep trees, shared children, and branching trees. A patch changeset records the stack-overflow fix.

Merge Risk: ⚪ Minimal · up to 64e2f

The change replaces recursive cleanup with iterative cleanup and adds coverage for deep and branching structures; no actionable merge-blocking risk remains beyond normal checks and review.

🚥 Pre-merge checks | ✅ 4
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly identifies the stack overflow fix for dropping deep trees.
Description check ✅ Passed The description explains the recursive cleanup fault, iterative fix, tests, and issue reference.
Linked Issues check ✅ Passed The changes implement iterative AST cleanup and add regression coverage for deep, shared, and branching trees.
Out of Scope Changes check ✅ Passed The changeset, mutable slice accessor, iterative drop logic, and tests directly support the linked issue.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@codspeed-hq

codspeed-hq Bot commented Aug 16, 2026

Copy link
Copy Markdown

Merging this PR will degrade performance by 5.27%

⚡ 3 improved benchmarks
❌ 41 regressed benchmarks
✅ 247 untouched benchmarks
⏩ 9 skipped benchmarks1

Warning

Please fix the performance issues or acknowledge them on CodSpeed.

Performance Changes

Benchmark BASE HEAD Efficiency
uncached[simple_classes.txt] 183.6 µs 203.4 µs -9.72%
canada_639262044961882324.json[cached] 158.4 ms 174.2 ms -9.06%
tachyons_11778168428173736564.css[cached] 20.9 ms 22.8 ms -8.33%
synthetic/attribute-heavy.html[cached] 828.8 µs 901.9 µs -8.1%
synthetic/void-elements.html[cached] 1.2 ms 1.4 ms -7.68%
materialize_5526761731747548557.css[cached] 31.5 ms 34.1 ms -7.5%
jquery.min_16034160674129647344.js[cached] 25.3 ms 27.3 ms -7.35%
synthetic/wide-siblings.html[cached] 1.5 ms 1.6 ms -6.9%
pure_9395922602181450299.css[cached] 3.8 ms 4 ms -6.84%
synthetic/astro-components.astro[cached] 1.1 ms 1.2 ms -6.62%
parser_13571644119461115204.ts[cached] 5.6 ms 6 ms -6.54%
html_analyzer[real/wikipedia-fr-Guerre_de_Canudos.html] 1.1 s 1.2 s -6.42%
synthetic/svelte-control-flow.svelte[cached] 401.3 µs 428.6 µs -6.38%
html_analyzer[real/wikipedia-Unix.html] 440.7 ms 470.6 ms -6.37%
synthetic/svelte-directives.svelte[cached] 637.8 µs 680.5 µs -6.28%
schema_17790310256719731354.graphql[cached] 9.9 ms 10.6 ms -6.26%
synthetic/comments.html[cached] 484.1 µs 516.1 µs -6.2%
html_analyzer[real/wikipedia-JavaScript.html] 535 ms 569.9 ms -6.13%
pixi.min_8151088792683739606.js[cached] 117.5 ms 125.1 ms -6.03%
semantic_4685287698740288120.css[cached] 137.9 ms 146.7 ms -6.01%
... ... ... ... ...

ℹ️ Only the first 20 benchmarks are displayed. Go to the app to view all benchmarks.

Tip

Investigate this regression by commenting @codspeedbot fix this regression on this PR, or directly use the CodSpeed MCP with your agent.


Comparing Austin1serb:fix/6830-iterative-green-drop (64e2f7d) with main (6cd3263)

Open in CodSpeed

Footnotes

  1. 9 benchmarks were skipped, so the baseline results were used instead. If they were deleted from the codebase, click here and archive them to remove them from the performance reports.

@Austin1serb

Copy link
Copy Markdown
Contributor Author

I originally fixed this by adding a custom cleanup path that handled the final ThinArc release and memory cleanup.

In the second commit I simplified it. It still cleans up the tree iteratively, but now it only removes child nodes from uniquely owned parents and lets the existing Arc code handle the actual memory cleanup.

The simpler version removes quite a bit of low-level memory handling, but CodSpeed went from about a 3.5% slowdown on the first version to about 5.3% on the second.

I don’t want to keep changing the it and rerunning the whole CI.
But if this performance difference is a concern, I can look into whether there’s another way to keep the fix without it slowing down

@ematipico

ematipico commented Aug 16, 2026

Copy link
Copy Markdown
Member

Have you looked at upstream instead? Maybe it's been fixed there already

@Austin1serb

Copy link
Copy Markdown
Contributor Author

Have you looked at upstream instead? Maybe it's been fixed there already

I did check the current rust-analyzer/rowan master and it reproduces there as well.

@Austin1serb

Copy link
Copy Markdown
Contributor Author

I did a bit more research and found that serde_json documents the same Drop problem with deeply nested values. By default it limits nesting depth, but if that limit is disabled it warns that Drop itself can overflow the stack. The docs show a carefully_drop_nested_arrays example that pulls children into a Vec and cleans them up iteratively, similar to the approach in this PR.

https://docs.rs/serde_json/latest/serde_json/struct.Deserializer.html#method.disable_recursion_limit

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

A-Core Area: core

Projects

None yet

Development

Successfully merging this pull request may close these issues.

🐛 biome_js_parser::parse_script stack overflow

2 participants