Summary
For a class with an accessor and a symbol-keyed method, Object.getOwnPropertyNames on the prototype is wrong in two directions at once, and is internally inconsistent with getOwnPropertyDescriptor.
class C {
m() {}
get g() { return 1; }
set s(v) {}
[Symbol.iterator]() {}
}
Object.getOwnPropertyNames(C.prototype)
// node: ["constructor", "g", "m", "s"]
// perry: ["@@iterator", "constructor", "m"]
Object.getOwnPropertySymbols(C.prototype).length // node: 1 perry: 0
Reflect.ownKeys(C.prototype).length // node: 5 perry: 3
Three distinct defects:
- Accessors are missing —
g and s do not appear at all.
- A symbol key leaks into the string list as the literal
"@@iterator", where it should appear only in getOwnPropertySymbols.
getOwnPropertySymbols returns nothing, so the symbol is not merely misfiled — it is absent from the list it belongs in.
Internally inconsistent
The information is present; only the enumeration is wrong:
Object.prototype.hasOwnProperty.call(C.prototype, "g") // true
Object.getOwnPropertyDescriptor(C.prototype, "g") // the accessor descriptor
So this is an own-keys enumeration bug on class prototypes, not a missing property.
Impact
Silent and structural. Anything that reflects over a class prototype — decorators, DI containers, serializers, mixin/Object.assign-based composition, test frameworks discovering methods — sees a wrong key set: accessors vanish, and a "@@iterator" string appears that no correct program expects. Because hasOwnProperty and getOwnPropertyDescriptor disagree with the enumeration, a two-step "list then inspect" pattern gives self-contradictory results.
How it was found
A 104-assertion differential over class prototypes, run against node --experimental-strip-types on unmodified main (84185b5656) as a control before landing an unrelated change. It surfaced 8 pre-existing divergences; this is the sharpest of them. Minimal repro above; the full harness is on the #9214 branch.
Not related to #9214, #9192 or #9219 — reproduces on pristine main.
Fix direction
The own-keys enumeration for class prototypes needs to (a) include accessor-backed entries, (b) route symbol keys to getOwnPropertySymbols rather than stringifying them into the names list, and (c) keep Reflect.ownKeys as the union in spec order — integer-like keys ascending, then strings in insertion order, then symbols in insertion order. Whatever produces the "@@iterator" spelling is the tell: an internal symbol representation is reaching a string-key path unconverted.
Summary
For a class with an accessor and a symbol-keyed method,
Object.getOwnPropertyNameson the prototype is wrong in two directions at once, and is internally inconsistent withgetOwnPropertyDescriptor.Three distinct defects:
gandsdo not appear at all."@@iterator", where it should appear only ingetOwnPropertySymbols.getOwnPropertySymbolsreturns nothing, so the symbol is not merely misfiled — it is absent from the list it belongs in.Internally inconsistent
The information is present; only the enumeration is wrong:
So this is an own-keys enumeration bug on class prototypes, not a missing property.
Impact
Silent and structural. Anything that reflects over a class prototype — decorators, DI containers, serializers, mixin/
Object.assign-based composition, test frameworks discovering methods — sees a wrong key set: accessors vanish, and a"@@iterator"string appears that no correct program expects. BecausehasOwnPropertyandgetOwnPropertyDescriptordisagree with the enumeration, a two-step "list then inspect" pattern gives self-contradictory results.How it was found
A 104-assertion differential over class prototypes, run against
node --experimental-strip-typeson unmodifiedmain(84185b5656) as a control before landing an unrelated change. It surfaced 8 pre-existing divergences; this is the sharpest of them. Minimal repro above; the full harness is on the #9214 branch.Not related to #9214, #9192 or #9219 — reproduces on pristine
main.Fix direction
The own-keys enumeration for class prototypes needs to (a) include accessor-backed entries, (b) route symbol keys to
getOwnPropertySymbolsrather than stringifying them into the names list, and (c) keepReflect.ownKeysas the union in spec order — integer-like keys ascending, then strings in insertion order, then symbols in insertion order. Whatever produces the"@@iterator"spelling is the tell: an internal symbol representation is reaching a string-key path unconverted.