ReviewOS

zig-utils/zig-js

GC: root binary operands across observable coercion

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

Parents: #359, #473 Related: #782, #783

Problem

Interpreter.applyBinary keeps both operands in raw Value locals while observable ToPrimitive coercion runs left-to-right. A user-defined left-side valueOf, toString, or Symbol.toPrimitive can enter baseline native code and consume a requested moving-GC safepoint before the right operand has been published in a precise root. The same pattern can leave the completed left primitive stale while coercing the right operand.

This was found during the #783 relocation audit. It is broader than String.prototype.concat and affects arithmetic, relational, bitwise, and loose-equality paths that perform user code during coercion.

Scope

  • Publish both original operands before the first observable coercion.
  • Replace each root slot with its completed primitive before coercing the next operand.
  • Preserve exact ECMA-262 left-to-right ordering, abrupt completion, Symbol, BigInt, and string-concatenation behavior.
  • Exercise a requested moving compaction from an earlier operand conversion while a later movable operand remains live.
  • Cover tree-walker and VM-shaped entry paths where applicable.

No-workaround rules

  • No disabling compaction, pinning cells, conservative native-stack dependency, source recognition, reordered coercion, or duplicated conversion.
  • No stacks, queues, jobs, Promise or microtask, Worker lifecycle, or Map or Set iterator changes.

Acceptance

  • Original operands and completed primitive results survive moving compaction through every observable coercion boundary.
  • Coercions still run once each, left-to-right, and abrupt completion suppresses later coercion.
  • String add, numeric, relational, bitwise, and loose-equality focused normal and TSan tests pass.
  • A deterministic moving-GC regression proves relocation rather than only ordinary collection.
  • Exact-parent Test262 accounting, threadfuzz, and the full unit gate report no regressions.

Completed integration

CI run 33113309062 completed successfully on main commit 8a1398ffc04e9e30987f968693667c7271ca3ce1, a descendant containing this implementation. All 53 executed jobs succeeded (unit and TSan-unit shards, Linux no-GIL TSan, functional/no-GIL corpus gates, fuzz profiles, ABI, Wasm, Test262 parallel, and docs); the scheduled-only nightly job was skipped. Together with the measured local evidence in the comments, this completes this issue's acceptance.

9 days ago

Landed 2285b232 on main.

Evidence:

  • focused normal relocation and semantic units: 2/2 passed, 0 leaked;
  • focused TSan relocation and semantic units: 2/2 passed, 0 leaked;
  • adjacent two-value concat regression passed in normal and TSan;
  • exact-parent Test262: addition 48/48, subtraction 38/38, less-than 45/45, bitwise-and 30/30, equals 47/47, does-not-equals 38/38 — 246/246 total, 0 flips;
  • deterministic threadfuzz: 400 programs from seed 1, 0 failures.

The final acceptance box remains open for the pushed full CI gate.

Sign in to comment on this issue.