Skip to content

feat(runtime): SharedArrayBuffer + non-agent Atomics spec parity (#4794) - #4803

Merged
proggeramlug merged 4 commits into
mainfrom
w4794
Jun 8, 2026
Merged

feat(runtime): SharedArrayBuffer + non-agent Atomics spec parity (#4794)#4803
proggeramlug merged 4 commits into
mainfrom
w4794

Conversation

@proggeramlug

Copy link
Copy Markdown
Contributor

Closes #4794 (single-thread / non-agent scope).

Flips the "No SharedArrayBuffer or Atomics" non-goal to a goal. The single-realm surface — SharedArrayBuffer construction, typed-array views over it, and the non-blocking Atomics ops — now matches Node spec-for-spec.

Results (test262, non-agent scope, pinned 4249661)

Suite before after
built-ins/Atomics 129/176 (73%) 176/176 (100%)
built-ins/SharedArrayBuffer 26/42 (62%) 40/42 (95%)
combined 155/218 (71.1%) 216/218 (99.1%)

0 compile-fails. The agent-coordinated cases ($262.agent) remain out of scope and still auto-skip via the host-dep / CanBlock filters.

As a bonus, the shared slice + ToIndex fixes also fixed 3 built-ins/ArrayBuffer tests (toindex-length, slice/end-default-if-undefined, slice/number-conversion) with zero regressions (verified by diffing an origin/main baseline build's ArrayBuffer sweep).

Root causes fixed

  • Argument coercion never ran ToPrimitive. Index/value object args ({valueOf(){…}}) were cast straight to NaN→0, so bad indices didn't throw RangeError and store silently dropped its value. Now routed through the shared js_number_coerce (ToNumber) / to_bigint_for_store (ToBigInt).
  • ToIndex checked negativity before truncating, so an in-bounds fractional index like -0.9 wrongly threw RangeError. Truncate first (same bug fixed in array_buffer_to_index, which made new SharedArrayBuffer(-0.1) throw).
  • Atomics.store now returns the ToInteger value (normalizing -0+0), not the element-narrowed read-back. isLockFree uses ToIntegerOrInfinity membership.
  • Atomics.wait/waitAsync check shared-backing before coercing args (spec step order — a poisoned valueOf on a non-shared view must not run).
  • Atomics[Symbol.toStringTag]; new SharedArrayBuffer.prototype arm: brand-checking byteLength getter, slice, and Symbol.toStringTag. ArrayBuffer/SAB slice now coerces start/end via ToIntegerOrInfinity (end defaults to len).

Test runner

built-ins/Atomics and built-ins/SharedArrayBuffer are no longer path-skipped in scripts/test262_subset.py; the agent cases still skip out via the $262.agent / CanBlock filters.

Deferred (documented in CLAUDE.md)

  • Cross-thread SAB aliasing + real Atomics.wait/notify blocking (needs the perry/thread model).
  • The slice species-constructor check (needs buffer expando support, currently dropped).
  • SharedArrayBuffer()-without-new TypeError (a shared ArrayBuffer/SAB codegen gap — ArrayBuffer() has the same behavior).

Per maintainer workflow, version bump + CHANGELOG are intentionally left for merge time.

Flip the "No SharedArrayBuffer or Atomics" non-goal to a goal. The single-realm
surface — SAB construction, typed-array views, and the non-blocking Atomics ops
— now matches Node spec-for-spec. test262 (non-agent scope) goes 155 -> 216 /
218 (71.1% -> 99.1%): built-ins/Atomics 100% (176/176), built-ins/SharedArray-
Buffer 95% (40/42). The agent-coordinated cases ($262.agent) stay out of scope.

Root causes fixed:
- atomics.rs argument coercion never ran ToPrimitive: index/value object args
  with valueOf/toString were cast straight to NaN->0 (so bad indices didn't
  throw RangeError, store dropped its value). Route through the shared
  js_number_coerce (ToNumber) / to_bigint_for_store (ToBigInt).
- ToIndex checked negativity BEFORE truncating, so an in-bounds fractional
  index like -0.9 wrongly threw RangeError. Truncate first (same bug in
  array_buffer_to_index, which made `new SharedArrayBuffer(-0.1)` throw).
- Atomics.store now returns the ToInteger value (normalizing -0 -> +0), not the
  element-narrowed read-back. isLockFree uses ToIntegerOrInfinity membership.
- Atomics.wait/waitAsync check shared-backing BEFORE coercing args, matching the
  spec step order (a poisoned valueOf on a non-shared view must not run).
- Atomics gets Symbol.toStringTag. New SharedArrayBuffer.prototype arm: a
  brand-checking byteLength getter + slice + Symbol.toStringTag. ArrayBuffer/SAB
  prototype slice now coerces start/end via ToIntegerOrInfinity (end defaults to
  len) instead of an as-i32 cast — also fixes 3 built-ins/ArrayBuffer tests.

Test runner: built-ins/Atomics and built-ins/SharedArrayBuffer are no longer
path-skipped; agent cases still skip out via the $262.agent / CanBlock filters.

Deferred (documented in CLAUDE.md): cross-thread SAB aliasing + real
Atomics.wait/notify blocking (needs the thread model), the slice species check
(needs buffer expando support), and the call-without-new TypeError (a shared
ArrayBuffer/SAB codegen gap).
@proggeramlug
proggeramlug merged commit 688d2b5 into main Jun 8, 2026
12 of 13 checks passed
@proggeramlug
proggeramlug deleted the w4794 branch June 8, 2026 15:22
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Implement SharedArrayBuffer + Atomics

1 participant