Skip to content

A prototype method replaced after class registration is not seen by newly constructed instances #9239

Description

@proggeramlug

Replacing a method on a class prototype is observed by instances that already exist, but not by instances constructed afterwards.

class C { m() { return "orig"; } }
const c = new C();
console.log(c.m());                                    // orig
(C.prototype as any).m = function () { return "replaced"; };
console.log(c.m());                                    // replaced  ✓
console.log(new C().m());                              // orig      ✗  node: replaced
existing instance fresh instance
node 26.5.1 replaced replaced
perry replaced orig

The asymmetry is the useful clue: the existing instance picks the replacement up, so the guard invalidation on the shared prototype works. It is construction that reinstalls the original surface — a fresh new C() appears to take the class's declaration-prototype vtable as recorded at class-registration time rather than consulting the (now mutated) prototype object.

A 200-iteration loop calling c.m() returns replaced, and a second replacement followed by another 200 iterations returns again, so the inline-cache path re-checks correctly for an existing receiver.

Confirmed pre-existing on main by A/B — rebuilding with #9169's runtime files reverted gives the identical orig. Found while auditing #9169, which fixes two adjacent per-instance [[Prototype]] cases and does not address this one.

Monkey-patching a class prototype after construction is common in test doubles and instrumentation libraries, and the failure is silent: the call succeeds and returns the stale implementation.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugConfirmed defect or regression

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions