Parent: #493 Related: #928 (whose benchmark could not see this), #931 (where it surfaced)
Status: measured end to end, root cause not yet located. No fix proposed.
Found while confirming that the #931 fix did not introduce a quadratic. It did not — but the shape it was tested on already was one, in the binary from before that fix.
Evidence
Median of 5 runs of test262 --eval, which covers process start, parse and function instantiation. Each shape nests to the given depth; the inside-a-function shapes wrap everything in function __never(){ … }, which is never called, so nothing executes.
| shape | depth 1000 | depth 2000 | depth 4000 | growth |
|---|---|---|---|---|
destructuring catch ([p]), inside a function, with vars | 123.2 ms | 481.8 ms | 1865.3 ms | x3.91, x3.87 |
destructuring catch ([p]), inside a function, no vars | 122.8 ms | 475.9 ms | 1863.3 ms | x3.88, x3.92 |
plain catch (p), inside a function | 7.4 ms | 8.3 ms | 10.4 ms | linear |
destructuring catch ([p]), top level, with vars | 8.6 ms | 10.5 ms | 16.0 ms | linear |
| plain blocks with vars, inside a function | 14.8 ms | 36.0 ms | 145.8 ms | x2.44, x4.05 |
The same measurement on the destructuring-with-vars shape gives nearly identical numbers before and after #931 (1871 ms vs 1977 ms at depth 4000), so this predates that fix.
What the isolation rules out
- Not the vars: removing every
varchanges nothing (1865 vs 1863 ms). - Not catch semantics in general: plain
catch (p)is linear in the same position. - Not the parser's handling of this syntax as such: the identical nesting is linear at script top level.
- Not #931's new check: present before it, and the fix adds only a few percent.
What remains is the conjunction: destructuring catch patterns, inside a function body. Something that processes function bodies does per-level work proportional to the nested destructuring patterns below it. Plausible places, unverified: a per-function pass that re-scans nested catch patterns at each level (scanSuperAndArgs does visit catch_param via scanSuperAndArgsInPattern), function-body eligibility or scope analysis at instantiation, or binding collection for catch patterns.
A caveat this exposes about #928
The #928 frozen workloads — like #926's — parse top-level scripts. They measured and fixed a real quadratic there, and that result stands. But a benchmark built on top-level programs cannot see an effect that only occurs inside function bodies, and the last row above suggests one exists for plain nested blocks too, still steepening at depth 4000. So "#928 removed the nesting quadratic" should be read as scoped to top-level code until function-body shapes are measured.
Suggested order
- Add function-body variants to the frozen growth runner — each #926/#928 shape wrapped in a function, plus the destructuring-catch shape here — so the effect is measured by the owned runner and not only by
--evalwall time. - Sample a depth-4000 run (
sample <pid> 3) to locate the hot frames before guessing. - Only then propose a fix.