ReviewOS

zig-utils/zig-js

Intl.NumberFormat: remove forgeable lazy formatter state

#591
Closed chris opened this 9 days ago ·
9 days ago

Problem

Intl.NumberFormat already resolves an immutable native IntlNumberFormatData record at public construction, but the generic Intl constructor still publishes redundant forgeable \0intl and \0locale ordinary properties. Brand checks trust \0intl, and numberFormatDataFor can lazily manufacture native state from attacker-controlled \0locale / \0opts properties.

That means a plain object can counterfeit the NumberFormat brand and move observable option processing out of InitializeNumberFormat. It also duplicates locale storage, named-property/attribute work, GC cells, shape transitions, and property synchronization even though every production formatter already owns complete native state.

Parent work: #475, #460. Related native-state migrations: #528, #589, #590. Worst-case/bounded-latency context: #493.

Scope

  • Freeze a NumberFormat resolvedOptions/brand boundary, reusing the existing construction and steady consumer rows for cross-checks.
  • Make native IntlNumberFormatData the only NumberFormat brand and resolved-state source.
  • Remove public \0intl / \0locale publication and delete lazy state synthesis from hidden properties.
  • Eagerly construct native records for internal Number/DurationFormat helper formatters instead of building pseudo-formatters with hidden properties.
  • Preserve normative legacy ChainNumberFormat: the fallback symbol may wrap a genuinely native-branded inner formatter, but forged hidden properties never brand a receiver.
  • Preserve constructor option/coercion order and failure atomicity.

Acceptance

  • Plain objects with forged \0intl, \0locale, and \0opts properties are rejected by NumberFormat prototype accessors/methods and resolvedOptions without observing the forged options bag.
  • Public and internal NumberFormat consumers never lazily normalize state after construction.
  • Real instances publish no NUL-prefixed ordinary own properties; legacy ChainNumberFormat behavior remains conformant.
  • OOM, bounded-heap, moving-GC, shared/no-GIL, and TSan tests remain clean.
  • NumberFormat and full Intl402 conformance have no regressions; full sharded units pass.
  • Exact-parent allocation, GC-cell, lock, and timing evidence is reported without overstating noisy throughput.
9 days ago

Implementation is now at the full-suite gate.

The candidate makes the native IntlNumberFormatData sidecar the sole NumberFormat brand and resolved configuration source, removes public \0intl / \0locale / \0opts construction state, eagerly initializes internal Number/Duration formatter helpers, rejects forged receivers without observing forged option getters, and preserves ChainNumberFormat through the genuine fallback-symbol inner instance (including the format accessor).

Validation so far:

  • test/intl402/NumberFormat: 249/249
  • complete test/intl402: 3341/3341
  • focused units: 14/14, zero leaks
  • focused TSan: 14/14, zero leaks/race reports

Exact-parent 100-job attribution (same checksums):

  • default construction: property locks 623,937 -> 540,299 (-83,638 / -13.4%); backing allocations 206,215 -> 167,099 (-39,116 / -19.0%); GC cells 109,505 -> 91,216 (-18,289 / -16.7%)
  • uncommon construction: property locks 1,007,931 -> 924,299 (-83,632 / -8.3%); backing allocations 321,449 -> 282,401 (-39,048 / -12.1%); GC cells 181,134 -> 168,118 (-13,016 / -7.2%)

Acceptance clarification: construction publishes no NUL-prefixed ordinary own properties. Accessing .format may still install the existing private \0boundFormat identity cache on the genuine native instance; that cache is neither brand nor formatter configuration state.

The full sharded unit suite is next.

9 days ago

Landed on main in fix(intl): require native NumberFormat state (#591).

Final verification:

  • NumberFormat test262: 249/249
  • complete Intl402: 3341/3341
  • focused normal: 14/14, zero leaks
  • focused TSan: 14/14, zero leaks/race reports
  • full sharded units: 1705 passed, 1 skipped, 0 failed, 0 leaked

The native sidecar is now the sole NumberFormat brand/configuration state; forged hidden properties cannot trigger lazy state synthesis or deferred option reads. Internal helpers publish native state eagerly, while genuine ChainNumberFormat fallback-symbol behavior remains intact.

Sign in to comment on this issue.