ReviewOS

zig-utils/zig-js

Private ABI: preserve generated macro execution-context identifiers

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

Parent: #495 Related: #401, #867

Problem

Pinned Home revision 7ed99c02 passes INT32_MAX to Zig__GlobalObject__create as the macro-context sentinel. Its pinned ScriptExecutionContext constructor translates that sentinel through the process-wide identifier generator; the sentinel itself is never published as a context ID.

zig-js currently routes every execution_context_id > 1, including INT32_MAX, through explicit assignment. That exposes 2147483647 as the stable ID and advances the internal allocator past it. This diverges from the revision-pinned lifecycle contract and can desynchronize later lazy, worker-provided, and test-isolation identifiers.

Required design

  • Treat INT32_MAX as a request for a fresh process identifier, never as an externally visible identifier.
  • Keep generated macro IDs, lazy main/default IDs, explicit Home worker IDs, and test-isolation replacement IDs in one collision-free process namespace.
  • Preserve stable nonzero 32-bit identity, exact live lookup, natural/explicit retirement, and owner-thread teardown rules.
  • Make sentinel creation failure-atomic: allocation/entropy/exhaustion must not publish a global lifecycle record or partial identifier mapping.
  • Match the pinned Home constructor and isolation semantics without expanding the private ABI surface unless an exact consumer requirement proves necessary.

No-workaround rules

  • No literal sentinel publication, random externally visible IDs, independent allocators that can collide, ID reuse, predictable fallback, leaked tombstones, scan-for-a-gap loop, arbitrary cap, signature/layout drift, weakened teardown, or fixture-only special case.

Acceptance

  • A macro sentinel receives a fresh nonzero ID distinct from INT32_MAX, and repeated lookup is stable.
  • Main/default, explicit worker, macro, and isolation sequences remain collision-free in both relevant creation orders.
  • Retired IDs remain unresolvable and are never rebound to a newer context.
  • Entropy/allocation/exhaustion paths publish no partial registry or lifecycle state.
  • Focused lifecycle tests pass in Debug, ReleaseSafe, and TSan; exact Home and Bun fixtures plus public/private ABI audits remain green.
9 days ago

Implemented and shipped in 67cf1750.

INT32_MAX is now consumed as the Home macro-context generation request instead of becoming a live ID. Lazy IDs and isolation replacements share a central allocator that reserves the sentinel, explicit worker IDs advance the same monotonic namespace, retired/skipped IDs cannot be rebound, and true u32 exhaustion fails before registry or lifecycle publication.

Validation:

  • focused script-ID allocation/registry/stability tests: 3/3 in Debug, ReleaseSafe, and TSan during implementation
  • final-source internal lifecycle sequence: 1/1 in Debug, ReleaseSafe, and TSan, with no failures, leaks, or race report
  • final-source pinned Home+Bun lifecycle fixtures: Debug, ReleaseSafe, and TSan
  • fixture sequence covers explicit worker to isolation replacement to macro sentinel to explicit worker, stable exact lookup, retired-ID rejection, and skipped-ID rejection
  • exact clean full suite at the committed tree: 2,289 passed, 1 skipped, 0 failed, 0 leaked across 10 shards
  • public C audit: 117/117 declarations and symbols; Home public ABI: 50/50
  • Home private ABI: 447/447; Bun private ABI: 437/437
  • private JSType audit: Home 97, Bun 98, shared 97, Bun-only 1, renumbered 70
  • docs build: 50 pages; link check: 328/328

Sign in to comment on this issue.