feat(runtime): SharedArrayBuffer + non-agent Atomics spec parity (#4794) - #4803
Merged
Conversation
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).
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Closes #4794 (single-thread / non-agent scope).
Flips the "No SharedArrayBuffer or Atomics" non-goal to a goal. The single-realm surface —
SharedArrayBufferconstruction, typed-array views over it, and the non-blockingAtomicsops — now matches Node spec-for-spec.Results (test262, non-agent scope, pinned
4249661)0 compile-fails. The agent-coordinated cases (
$262.agent) remain out of scope and still auto-skip via the host-dep /CanBlockfilters.As a bonus, the shared slice + ToIndex fixes also fixed 3
built-ins/ArrayBuffertests (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
ToPrimitive. Index/value object args ({valueOf(){…}}) were cast straight toNaN→0, so bad indices didn't throwRangeErrorandstoresilently dropped its value. Now routed through the sharedjs_number_coerce(ToNumber) /to_bigint_for_store(ToBigInt).ToIndexchecked negativity before truncating, so an in-bounds fractional index like-0.9wrongly threwRangeError. Truncate first (same bug fixed inarray_buffer_to_index, which madenew SharedArrayBuffer(-0.1)throw).Atomics.storenow returns theToIntegervalue (normalizing-0→+0), not the element-narrowed read-back.isLockFreeusesToIntegerOrInfinitymembership.Atomics.wait/waitAsynccheck shared-backing before coercing args (spec step order — a poisonedvalueOfon a non-shared view must not run).Atomics[Symbol.toStringTag]; newSharedArrayBuffer.prototypearm: brand-checkingbyteLengthgetter,slice, andSymbol.toStringTag. ArrayBuffer/SABslicenow coercesstart/endviaToIntegerOrInfinity(enddefaults to len).Test runner
built-ins/Atomicsandbuilt-ins/SharedArrayBufferare no longer path-skipped inscripts/test262_subset.py; the agent cases still skip out via the$262.agent/CanBlockfilters.Deferred (documented in CLAUDE.md)
Atomics.wait/notifyblocking (needs theperry/threadmodel).slicespecies-constructor check (needs buffer expando support, currently dropped).SharedArrayBuffer()-without-newTypeError (a shared ArrayBuffer/SAB codegen gap —ArrayBuffer()has the same behavior).Per maintainer workflow, version bump + CHANGELOG are intentionally left for merge time.