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
Prototype writes on a per-evaluation class expression leak across evaluations and vanish from Object.keys (regression, train 268 / #11043 fix) #11134
On origin/main36892b7 (merge train 268), a prototype property assigned on one evaluation of a function-body class expression is visible on instances of every other evaluation. The assigned key also no longer shows up in Object.keys(C.prototype) or hasOwnProperty. Node keeps each evaluation's prototype separate.
This breaks @redis/client's attachConfig, which does Class.prototype[name] = createCommand(...) once for the client class and once for its Multi class. The Multi command stubs become visible on the client, and the client commands on the Multi class.
A1 [] false 1 [ 'constructor' ]
A2 1 2 1 false
B 2 2 []
C 2 1
A heritage form (const C = class extends Base {}; C.prototype[k] = v) is also affected: Object.keys(C.prototype) is [] where Node gives [ 'qq' ].
Attribution
The computed-key cases (A and C) are correct on the same tree with 34cbef7 ("reflect ClassBody accessors on per-evaluation class prototypes", mongodb: TypeError "Cannot read properties of undefined (reading 'default')" on connect #11043) reverted. That commit makes class_id_for_decl_prototype_object recognize a per-evaluation prototype as the template's declared prototype. Every reflection and write site keyed on that lookup then treats per-evaluation writes as template-wide.
The literal-key case (B) goes through js_register_prototype_method(template_cid, …), confirmed with gdb. That side table is keyed by the shared template class id, so it is template-wide for any ClassExprFresh.
Found while rebasing PR #11122 (#11042) onto train 268. That PR's gap test test_gap_11042_class_expr_dynamic_heritage_per_evaluation.ts fails on origin/main because of this bug: typeof client.exec is function where Node gives undefined.
This regression is partly caused by 34cbef7 (#11113, merge train 266). Reverting that commit fixes the computed-key cases. The literal C.prototype.z = v case goes through js_register_prototype_method, which stores per shared template.
A forward fix is in progress on current main. Please prefer it over reverting 34cbef7, because a revert brings back mongodb's Cannot assign to read only property 'pathname' (#11043).
Also: #11122 is closed as superseded by 86cb666. Its gap test will land with the #11134 fix as coverage.
Summary
On
origin/main36892b7 (merge train 268), a prototype property assigned on one evaluation of a function-body class expression is visible on instances of every other evaluation. The assigned key also no longer shows up inObject.keys(C.prototype)orhasOwnProperty. Node keeps each evaluation's prototype separate.This breaks
@redis/client'sattachConfig, which doesClass.prototype[name] = createCommand(...)once for the client class and once for its Multi class. The Multi command stubs become visible on the client, and the client commands on the Multi class.Repro (no packages)
Node 26.5.1:
Perry
origin/main36892b7:A heritage form (
const C = class extends Base {}; C.prototype[k] = v) is also affected:Object.keys(C.prototype)is[]where Node gives[ 'qq' ].Attribution
class_id_for_decl_prototype_objectrecognize a per-evaluation prototype as the template's declared prototype. Every reflection and write site keyed on that lookup then treats per-evaluation writes as template-wide.js_register_prototype_method(template_cid, …), confirmed with gdb. That side table is keyed by the shared template class id, so it is template-wide for anyClassExprFresh.Found while rebasing PR #11122 (#11042) onto train 268. That PR's gap test
test_gap_11042_class_expr_dynamic_heritage_per_evaluation.tsfails onorigin/mainbecause of this bug:typeof client.execisfunctionwhere Node givesundefined.