Add missing writeBarrier to Array.unshift
Two array opcodes shared one case label in JSC's write-barrier phase.
Component: JSC DFG JIT | f641af0
JavaScriptCore's concurrent GC scans object storage while the mutator (JS execution) keeps running in parallel, so any code that moves a heap reference within an object's backing store must emit a write barrier — a notification to the collector that a slot may now hold something it hasn't seen. Array.prototype.push's DFG fast path predates this change; unshift's inline fast path was added in 313475@main, modeled on push's, and reused push's entry in DFGStoreBarrierInsertionPhase.cpp's switch statement.
Source/JavaScriptCore/dfg/DFGStoreBarrierInsertionPhase.cpp
The change splits ArrayUnshift out of the shared ArrayPush case and gives it its own barrier rule: only the single-element Contiguous shape is emitted inline (every other case routes through an operation that runs its own barrier), and for that shape the phase now calls considerBarrier on the prepended value's edge.
Significance
Push only ever appends past the end of storage, so the shared logic sufficed for it. Unshift shifts existing elements upward to make room for the prepended value, temporarily placing a live cell outside the scanned range until publicLength is updated — a case the shared entry never distinguished. The single-element Contiguous unshift fast path could move a live heap cell out of the collector's scanned range, letting the GC reclaim an object still reachable from the array. That is a use-after-free primitive triggerable from pure JS under GC pressure.
Audit directions
The reusable pattern is a DFG/FTL inline fast path that relocates or reinterprets heap references without a matching write barrier — especially when the new path was created by copy-pasting from a sibling opcode, as push→unshift was here. Narrow: audit other array/typed-array/string builtins that received recent DFG inline fast paths (splice, copyWithin, fill, sort) for the same asymmetry, specifically any operation that shifts existing elements within already-allocated storage rather than purely appending. Wider: re-read DFGStoreBarrierInsertionPhase.cpp's case grouping as a whole and ask which fall-through groups still conflate opcodes with genuinely different storage-mutation semantics — the grouping is the artifact that carried the bug, not the individual opcode. Widest: any concurrent-collector runtime where barrier insertion is keyed on opcode identity rather than on the storage effect of the operation is exposed to this shape; the review tell is a case A: case B: fall-through in a barrier phase where A and B differ in whether they move existing elements.