ReviewOS

zig-utils/zig-js

Parser: classify reserved bindings with one static table

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

Parent: #496 Related: #479, #493, #608, #461

Problem

After #608 removes duplicate IdentifierStart decoding, the current-main parser-only representative_frontend_unicode_identifiers_4096 profile still records 654 top-of-stack samples in byte equality and 290 in Parser.isForbiddenBindingName. Every ordinary non-keyword binding walks up to 38 unconditional reserved spellings, then up to nine strict-only spellings, even when its length cannot match any keyword. Long ASCII and Unicode parameter names therefore pay repeated function/equality dispatch after lexing has already produced their exact slice.

The repository already has a standard-library compile-time static string map designed for small disparate key sets: it rejects outside the exact keyword length range and compares only equal-length candidates.

Scope

  • Replace the three duplicated linear reserved-word lists with one compile-time, immutable classification table carrying the exact unconditional, grammar-reserved, and strict-only memberships.
  • Perform one classification per binding/label decision and reuse its flags across active grammar modes.
  • Preserve escaped contextual-keyword handling, await/yield async-generator rules, strict eval/arguments, property-name permissions, labels, import/export bindings, and all early-error locations.
  • Add exhaustive equivalence coverage for every classified spelling plus ordinary long/Unicode controls.

Constraints

  • No lexer keyword tokenization change, hash seed/global mutable state, runtime allocation, generated-data edit, accepted/rejected grammar shortcut, or case folding.
  • Exact bytes decide classification; escaped-identifier context rules remain parser-owned.
  • Do not touch stacks, queues, jobs, Promise/microtasks, Worker lifecycle, or scheduler code.

Acceptance

  • The new table is exhaustively equivalent to all three current word sets and has no duplicate/missing entries.
  • Non-keyword identifiers outside keyword lengths reject in O(1) without byte comparisons.
  • ASCII/Unicode/escaped/private frontend rows retain exact checksums/allocations and show causal CPU-work reduction or the change is rejected.
  • Reserved-word, strict-mode, module, async/generator, label, property-name, OOM, TSan, full-unit, and affected test262 gates pass with quantified flips.
  • No process-global mutable state or cache is added.
8 days ago

#609 is complete on main via 0d5eb7a0.

The parser now has one immutable 45-key compile-time table carrying independent unconditional-binding, grammar-reserved, and strict-only flags. The test exhaustively checks unique keys and exact 36/36/9 membership counts. Binding, label, and escaped-reserved decisions reuse one lookup; contextual await/yield, escaped keyword rules, and strict eval/arguments remain explicit.

Formal exact-parent results (15 order-balanced pairs, no discards, diagnostic host):

rowwall ratioinstruction ratioallocationsallocated bytes
Unicode 1K0.801x0.8241x58 -> 58784,776 -> 784,776
Unicode 2K0.858x0.8249x63 -> 631,132,824 -> 1,132,824
Unicode 4K0.749x0.8258x69 -> 692,813,232 -> 2,813,232
ASCII strict params 4K0.730x0.7689x69 -> 692,813,232 -> 2,813,232
escaped params 4K0.729x0.8210x8,261 -> 8,2614,475,968 -> 4,475,968
private names 4K0.990x1.0003x53 -> 532,584,328 -> 2,584,328
escaped private names 4K1.016x0.9994x8,245 -> 8,2454,210,200 -> 4,210,200

The binding rows show stable CPU-work reduction. Private-name controls do not perform binding classification and remain instruction-neutral; their sub-millisecond wall medians are noisy, so no control wall claim is made. All identities/checksums matched and RSS remained effectively neutral.

Matched five-second Unicode profiles reduce byte-equality top-of-stack samples 654 -> 73 and isForbiddenBindingName 290 -> 28.

Validation:

  • exhaustive static-table uniqueness/membership test passes
  • all 46 parser unit tests and all 12 focused frontend cases pass
  • focused TSan passes
  • full sharded units: 1,725 pass, 1 skip, 0 fail/leak
  • future-reserved words: 55 -> 55
  • module code: 599 -> 599
  • labels: 24 -> 24
  • statement generators/async/async-generator: 641 -> 641
  • expression generators/async/async-generator: 1,006 -> 1,006
  • total affected test262: 2,325 -> 2,325, with 0 flips/regressions

No keyword tokenization change, runtime allocation, mutable/global cache, case folding, or jobs/Worker surface was introduced.

Sign in to comment on this issue.