ReviewOS

zig-utils/zig-js

Parser: skip impossible parameter-body conflict indexes

#611
Closed chris opened this 8 days ago ·
8 days ago

Parent: #496 Related: #493, #520, #609, #610

Problem

Parser.checkParamBodyConflict enforced the FormalParameters/LexicallyDeclaredNames early error by first inserting every simple parameter into a hash table, then visiting the function body's direct lexical declarations. A body with no direct lexical declaration cannot conflict, but a 4K-parameter function still hashed, grew, and allocated the complete index before discovering that fact.

After #610 removed statement-location rescans, a current ReleaseFast profile made this redundant work explicit: checkParamBodyConflict owned StringHashMap.getOrPut, growth, and Wyhash samples for a frozen body containing only a directive expression and return. This was distinct from #520's required strict duplicate-parameter index.

Resolution

  • Benchmark contract: 62a340ef, bench(frontend): add parameter conflict controls (#611).
  • Runtime fix: bd24f7f2, perf(parser): skip impossible parameter conflicts (#611).

The parser now performs a bounded, non-recursive scan of only the function body's direct statement roots. Directive, return, expression, var-only, and nested-block bodies return before indexing parameters. Direct let, const, class, destructuring, and declaration-group shapes retain the existing exact indexed conflict algorithm.

Exact-parent evidence

Fifteen order-balanced pairs per row, all 150 fresh processes, no discarded samples. The host is diagnostic; wall values are supporting observations, while retired instructions and exact allocator accounting are the stable claims.

ReleaseFast 4K rowwallinstructionsallocationsallocated bytes
ASCII strict parameters0.722x0.8035x68 -> 572,813,224 -> 2,534,568
escaped identifiers0.825x0.8568x8,260 -> 8,2494,475,960 -> 4,197,304
Unicode identifiers0.725x0.8462x68 -> 572,813,224 -> 2,534,568
direct lexical slow-path control1.032x1.0013x72 -> 722,813,688 -> 2,813,688
nested-shadow control0.696x0.8031x75 -> 642,814,304 -> 2,535,648

All source hashes, binary identities, AST checksums, thermal states, and input identities matched. Energy was below counter resolution, so no energy claim is made. The direct control's exact allocation/byte result and 0.13% instruction delta show the required conflict path remains intact without material CPU regression.

Correctness

  • test262 matches exactly across ordinary functions 451, statement generators 266, expression generators 290, statement async generators 301, expression async generators 623, async arrows 60, async function expressions 93, async declarations 74, object methods 303, and class methods 65: 2,526 -> 2,526, zero failures or flips.
  • Zero-capacity allocator coverage proves empty and nested-only bodies allocate no conflict index while a direct lexical declaration still enters the indexed path.
  • Ordinary, async, generator, arrow, method, class, destructuring, and grouped direct conflicts remain rejected; nested lexical shadowing remains accepted.
  • Frontend gate: 12/12. Focused Debug and TSan: 2/2 each.
  • Four real-corpus witnesses pass in forced tree-walker and required-bytecode modes.
  • Full units: 1,728 passed, one expected skip, zero failures, zero leaks.

Acceptance

  • Zero-lexical bodies allocate/hash no parameter-body conflict index, including directive, return, expression, var, and nested-block controls.
  • Direct let, const, class, destructuring, and declaration-group conflicts remain SyntaxErrors for ordinary, async, generator, arrow, and method functions.
  • Nested lexical declarations remain outside the function body's direct LexicallyDeclaredNames and do not trigger false positives.
  • Exact-parent evidence covers Unicode and ASCII 4K strict parameters plus a direct lexical-conflict control and a no-conflict nested-declaration control.
  • Allocation and instruction reductions are causal; existing strict-parameter duplicate validation remains enabled and unchanged.
  • Focused frontend Debug/TSan, full units, parameter/function test262, and forced tree-walker/required-bytecode witnesses remain exact.

Security and no-workaround ruling

Parameter count, identifier bytes, body statement count, declaration grouping, and destructuring depth remain attacker-controlled. The new precheck is bounded to direct roots and mirrors every AST shape inspected by the existing conflict algorithm. No early error, strict duplicate validation, or declaration traversal was removed; there is no source/benchmark recognition, name limit, AST/token widening, or job/Promise/microtask/Worker lifecycle change.

8 days ago

Benchmark-first commit landed on main as 62a340ef: bench(frontend): add parameter conflict controls (#611).

The frozen 4K rows distinguish one non-conflicting direct lexical declaration (which must retain the slow path) from a nested parameter0 shadow (which is outside the function body's direct LexicallyDeclaredNames). Both validate exact AST structure and source identity. Checksums are 60,408 direct and 60,407 nested; the existing strict 4K checksum remains 4,103. No parser/runtime change is included.

8 days ago

Completed on main with benchmark contract 62a340ef and runtime fix bd24f7f2.

The issue body now records the five-row exact-parent matrix and completed acceptance checklist. Stable instruction results are 0.8035x ASCII, 0.8462x Unicode, 0.8568x escaped, and 0.8031x nested-shadow; the required direct-lexical slow path remains allocation/byte exact and 1.0013x instructions. Test262 is 2,526 -> 2,526, full units pass 1,728 plus one expected skip, and four corpus witnesses pass both forced tiers.

Sign in to comment on this issue.