You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Own property named prototype is silently dropped when written through a function parameter (and after a dynamic-key write) #9365
The write is silently dropped — no throw, no warning. o simply never gains the property: hasOwnProperty is false, Object.keys(o) does not list it, and Object.getOwnPropertyDescriptor(o, "prototype") returns undefined.
What distinguishes the failing shape
Eleven variants, all writing an own prototype onto a plain object. Only the parameter write fails:
shape
perry
function f(){const o={};o.prototype=V;return o} (local)
This one fails for everyn, including n = 0 (the loop never runs). So it is not about property count or overflow slots — the mere presence of the dynamic-key write is enough to change how the later o.prototype = … is compiled.
The unifying pattern in both: when the receiver's static type is not known to be a plain object, x.prototype = v appears to be routed to the function/class prototype slot rather than to an ordinary own property, and for a plain object that store goes nowhere.
Why this matters beyond the fixture
This is the mechanism family behind #9341 (cc --help down on main). The diagnostic there shows the failing call is Object.defineProperty(undefined, "getContentType", …), where the undefined is z in axios's
i.e. a .prototype access on a receiver of imprecise static type yielding undefined instead of the prototype. The read side and the write side above are the same fragility. #9341's trigger is a regression inside 83754818e..main (this write-drop reproduces identically on the green 83754818e build, so it is not itself the regression) — but a .prototype access that silently answers undefined on an imprecisely-typed receiver is the machinery that regression is landing on, and hardening it would make that whole class of failure loud instead of silent.
At minimum, a store that cannot be honoured should not be discarded in silence.
Found while bisecting #9341; verified pre-existing on 83754818e (#9242) and a03be729c (#9336) — byte-identical failure on both.
So the trigger is precisely: one computed-key write ahead of it is enough to make a later o.prototype = v store go nowhere. That subsumes the parameter case in the issue body (a parameter is just another way for the receiver's static type to stop being a known plain object).
The read side is fine, for what it is worth — I checked .prototype reads through a function parameter, at a polymorphic call site, and inside a static method after a computed-key write, a symbol-keyed write, a chained symbol write and a plain static-property write on this; all eight are node-identical. It is specifically the store that is discarded.
Scoped precisely. After one computed-key write on a plain object, I stored under seven names, each both ways (statically named o.k = v and computed o[k] = v) — 14 stores. Exactly one is dropped:
So it is not "dynamic-shaped receivers lose stores" and not "special names are mishandled". It is specifically the statically-named .prototype store, which codegen presumably routes to the function/class prototype slot; when the receiver's shape is no longer statically a plain object, that store lands nowhere and the ordinary own-property path is never taken. The computed form escapes because it cannot take that specialised route.
That also explains why the parameter case in the issue body fails: a parameter is simply another receiver whose static type is not a known plain object.
Fixed by #9757, which landed on main via merge train #9798. The train was merged as its own branch, so the Fixes #9365 keyword in #9757 never fired — closing manually. Verified the fix is on main by patch-id (git cherry), not just by the PR being closed.
Activity
proggeramlug commentedon Sep 1, 2026
Minimised further — no function call and no parameter needed. Three lines:
Replacing the computed key with a static one makes it work:
So the trigger is precisely: one computed-key write ahead of it is enough to make a later
o.prototype = vstore go nowhere. That subsumes the parameter case in the issue body (a parameter is just another way for the receiver's static type to stop being a known plain object).The read side is fine, for what it is worth — I checked
.prototypereads through a function parameter, at a polymorphic call site, and inside a static method after a computed-key write, a symbol-keyed write, a chained symbol write and a plain static-property write onthis; all eight are node-identical. It is specifically the store that is discarded.proggeramlug commentedon Sep 1, 2026
Scoped precisely. After one computed-key write on a plain object, I stored under seven names, each both ways (statically named
o.k = vand computedo[k] = v) — 14 stores. Exactly one is dropped:o.prototype = v(static name)o["prototype"] = v(computed)o.foo/o.constructor/o.name/o.length/o.caller/o.arguments(static)So it is not "dynamic-shaped receivers lose stores" and not "special names are mishandled". It is specifically the statically-named
.prototypestore, which codegen presumably routes to the function/class prototype slot; when the receiver's shape is no longer statically a plain object, that store lands nowhere and the ordinary own-property path is never taken. The computed form escapes because it cannot take that specialised route.That also explains why the parameter case in the issue body fails: a parameter is simply another receiver whose static type is not a known plain object.
proggeramlug commentedon Sep 5, 2026
Fixed by #9757, which landed on
mainvia merge train #9798. The train was merged as its own branch, so theFixes #9365keyword in #9757 never fired — closing manually. Verified the fix is onmainby patch-id (git cherry), not just by the PR being closed.