Reapplies #2017, which #2038 reverted after effect's observability-otlp
fixture crashed in plain builds. The crash was the candidate-buffer
corruption that the previous commit fixes: the cached property sites
change when the collector runs, and with enough environment variables a
pass buffered a string box that was later pooled while still buffered.
TypedArray.prototype.set with a SharedArrayBuffer view as its source or
target converted every element through a double, even when both arrays
had the same element kind. That cost about 5 ns per byte, and it changed
NaN payloads: a Float32Array signaling NaN came back quieted (7fa00001
became 7fe00001), where Node copies the bytes.
The same element kind now copies the source's bytes with the relaxed
shared stores. A source that overlaps the target range, a view of the
same buffer in a program without workers, is copied out first. Different
element kinds still convert each element.
scr_dyn_release returned the last reference of a number or string box to
the free list without scr_cyc_dead, because the release paths never buffer
those boxes. A collector pass can: capture boxes trace their payload, and
scr_xg_release, which settles the cross-generation edges a restricted pass
skipped, calls scr_cyc_on_release on the target whatever its kind. The
pooled block then stayed in its candidate buffer, and the next box that
reused it carried the stale buffer position. Its death swapped another
object into that slot, which corrupted the buffers and crashed a later
scr_cyc_on_dead.
The fast path now takes a buffered box out of its buffer first. The
sanitized runtime does not pool boxes, so only plain builds were affected.
An untyped call of an array method went through the whole dispatch ladder
on every call: the receiver-kind tests for handles, proxies, typed refs,
objects, functions and primitives, then the array section's prologue
(the hole scan and the frozen, sealed and extensibility tables) before
the method itself.
A dense array without holes, named properties or a prototype of its
own, whose length and elements are writable, now runs push, pop, shift,
at, includes and indexOf directly from the call site's cached method id.
An argument that could run user code (a non-number index, a fromIndex)
still takes the ladder, as does every other receiver. A statement-position
push calls an entry that does not box the new length.
Calls on arrays that the direct path refuses now also follow Node:
- an array's own method, or one on its custom prototype chain
(Object.setPrototypeOf), runs instead of the builtin;
- push on a sealed or non-extensible array throws "Cannot add property
N, object is not extensible", and pop or shift on a sealed array
"Cannot delete property 'N' of [object Array]".
Object.getPrototypeOf of an Error answers Error.prototype when the error's view was built from its fields and its prototype is still the object the view was built with. After Object.setPrototypeOf(error, {}), it answers the object that was set.
A checked cast into a union whose discriminant has four or more string
literals reads the discriminant field once (scr_dyn_discriminant_case)
and then tests only the arms whose literal it holds and the arms without
a literal, in the union's arm order. Every other arm's literal test would
fail, so the arm chosen and the error for a value no arm matches are
unchanged. A typed capsule, an accessor or an inherited discriminant
still takes the tests arm by arm. Unions with fewer literals emit the
same code as before.
An Error's view had no prototype or a holder of its default name and
message, so Object.getPrototypeOf(new Error()) was Object.prototype or a
fresh object. A view of an error built as an Error, whose prototype is
still the one its view starts with, now answers Error.prototype.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
zValidator's default failure hook serializes zod 4's ZodError with
`name` after `message` natively, and with the first `name` zod's factory
assigns ("$ZodError") when the second assignment took the typed slot.
ZodError instances are views of a JavaScript class extending Error: each
view rebuilds `message` and `name` from the error slots at fixed
positions (after `stack`, and after every other property), and the
property bag that holds the dynamically added properties copied its
committed, possibly stale, values over them.
A JavaScript Error subclass with a property bag now marks its class
(errorBagOrder), and its view calls scr_error_dyn_follow_bag after the
bag is copied: `name` and `message` move to follow the property they
followed when the bag was committed, and data values come from the
slots. TypeScript classes keep their views.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
zod 4's z.record rejected every plain object natively ("expected record,
received object"): its isPlainObject reads o.constructor.prototype, which
was undefined, and asks whether that prototype has its own isPrototypeOf,
which Object.prototype answered false.
- The Object, Array and Error constructors held as values have their
builtin prototype as a non-writable, non-enumerable, non-configurable
own `prototype`; reading Function's constructor value's prototype
throws SC2020 instead of answering undefined.
- hasOwnProperty / Object.hasOwn on Object.prototype answer its builtin
members, and on Array.prototype its constructor;
Object.getOwnPropertyNames lists them in Node's order.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>