An indexed write that grows an array silently restores Array.prototype, discarding an explicit null [[Prototype]].
const n: any = [4, 5];
Object.setPrototypeOf(n, null);
console.log(Object.getPrototypeOf(n), Object.getPrototypeOf(n) === null); // null true
n[9] = 1; // grows past length
console.log(Object.getPrototypeOf(n), Object.getPrototypeOf(n) === null); // [] false <-- node: null true
console.log(n.length, n[9]); // 10 1 (correct)
|
before the write |
after the write |
| node 26.5.1 |
null, === null true |
null, === null true |
| perry |
null, === null true |
[], === null false |
The element write itself is correct — length becomes 10 and n[9] reads back 1. It is the [[Prototype]] that is silently re-linked to Array.prototype.
Two things make this worth more than a formatting curiosity:
- It is silent and it re-attaches a whole surface. After the write, the array inherits every
Array.prototype method again — so a null-prototype array used deliberately as a bare index container (the array analogue of Object.create(null) for maps) stops being bare the moment it grows, and n.map/n.constructor/n[Symbol.iterator] reappear.
- It is write-triggered, so it will not show up in a construction-time test.
Object.getPrototypeOf immediately after setPrototypeOf is correct; only a subsequent growing write moves it.
I did not narrow which write path does it — a growing write (n[9] past length 2) reproduces; I have not checked whether an in-bounds write, push, or a non-array null-prototype object are affected.
Confirmed pre-existing, not from #9297: reverting that PR's seven runtime files and rebuilding gives the identical [] false. Found while auditing #9297, which fixes three other divergences on the same probe (inherited index reads, a proto setter on a growing write, and both after GC) and takes it from four divergences to this one.
An indexed write that grows an array silently restores
Array.prototype, discarding an explicit null[[Prototype]].null,=== nulltruenull,=== nulltruenull,=== nulltrue[],=== nullfalseThe element write itself is correct —
lengthbecomes 10 andn[9]reads back 1. It is the[[Prototype]]that is silently re-linked toArray.prototype.Two things make this worth more than a formatting curiosity:
Array.prototypemethod again — so a null-prototype array used deliberately as a bare index container (the array analogue ofObject.create(null)for maps) stops being bare the moment it grows, andn.map/n.constructor/n[Symbol.iterator]reappear.Object.getPrototypeOfimmediately aftersetPrototypeOfis correct; only a subsequent growing write moves it.I did not narrow which write path does it — a growing write (
n[9]pastlength2) reproduces; I have not checked whether an in-bounds write,push, or a non-array null-prototype object are affected.Confirmed pre-existing, not from #9297: reverting that PR's seven runtime files and rebuilding gives the identical
[] false. Found while auditing #9297, which fixes three other divergences on the same probe (inherited index reads, a proto setter on a growing write, and both after GC) and takes it from four divergences to this one.