Skip to content

fix(core): keep the portable shell scanner linear in argument count - #59163

Open
LeizhenZhang wants to merge 1 commit into
anomalyco:devfrom
LeizhenZhang:shell-scan-linear
Open

LeizhenZhang wants to merge 1 commit into
anomalyco:devfrom
LeizhenZhang:shell-scan-linear

Conversation

@LeizhenZhang

@LeizhenZhang LeizhenZhang commented Oct 26, 2026 •

Copy link
Copy Markdown

Issue for this PR

Closes #59165

Type of change

  • Bug fix
  • New feature
  • Refactor / code improvement
  • Documentation

What does this PR do?

ShellScan.scan classified each word by walking the precommand chain again and copying every earlier operand for printf, set and declare. A command with many arguments therefore took quadratic time. Words only grow within a command, so the builtin lookup now resumes where it stopped, and each operand is read once into a few flags (dash option, [EFin] option, set -A, printf -v, print -v). The decisions are the same as before. Only the work per word changes.

How did you verify your code works?

  • New "bounded time" cases in test/shell-scan/adversarial.test.ts (printf and set with 32000 operands, declare with 8000 options, 8000 chained command words). On dev they take 8290, 6893, 1401 and 1073 ms and fail the 500 ms budget. With the fix all pass.
  • Timing for 32000 operands: printf 7735 ms becomes 35 ms, and set 6250 ms becomes 89 ms.
  • Differential check against the dev scanner on 200000 random commands built from precommands, builtins and options, each scanned as bash, zsh and posix (600000 comparisons): no result differs.
  • bun test test/shell-scan/ (2861 pass), the shell-parse and tool-shell tests (272 pass), core bun run typecheck, oxlint and prettier on the touched files.

Checklist

  • I have tested my changes locally
  • I have not included unrelated changes in this PR

Added by the opencode bot (AI-generated).

Fixes #59165

Each word re-walked the precommand chain and copied every earlier operand to classify printf, set and declare arguments, so one command with many arguments took quadratic time. The builtin lookup now resumes where it stopped and reads each operand once. printf with 32000 operands drops from about 7.7 s to about 35 ms.
@github-actions github-actions Bot added needs:compliance This means the issue will auto-close after 2 hours. and removed needs:compliance This means the issue will auto-close after 2 hours. labels Oct 26, 2026
@github-actions

Copy link
Copy Markdown
Contributor

Thanks for updating your PR! It now meets our contributing guidelines. 👍

@opencode-agent opencode-agent Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good. I compared the base and PR scanners directly. The quadratic cases from #59165 drop from 0.6–10.8 s to 8–31 ms. A differential check of 450k random bash/zsh/posix scans (precommand chains, exec -a, --, printf -v/print -v, set -A/+A, declare option clusters, separators and subshells) gave no differences. bun test test/shell-scan/ passes, and so does tsc through typecheck:tsc (the typecheck script itself fails on base and PR alike with Script not found "check", a local tooling issue). The cached lookup state is safe because state.words is only appended to within a command, and the cache is reset in finishBashCommand. The new time-limit tests run in about 10–30 ms against a 500 ms limit, so they shouldn't be flaky.

Public API is unchanged (internal scanner only).

Fixes #59165

This branch has not been deployed

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

core: portable shell scanner takes quadratic time in the argument count

1 participant