console: don't list prototype members as own properties
Fixes #4223.
Fixes #1713.
Fixes #12365.
Repro
class ImageSettings {
a = 123;
toJSON() { return { a: '456' }; }
}
console.log(new ImageSettings());
| before | after | node |
|---|---|---|
ImageSettings { a: 123, toJSON: [Function: toJSON] } | ImageSettings { a: 123 } | ImageSettings { a: 123 } |
Cause
JSC__JSValue__forEachPropertyImpl walks up to five prototype levels after the object's own slots and reports every entry it finds. For a plain class instance that pulls in every prototype method and class-level constant, so console.log / Bun.inspect list shared behavior (toJSON, addEventListener, NONE, CAPTURING_PHASE, ...) as if it were instance state.
Fix
Keep the prototype walk but filter what it yields:
- Fast path: the fast enumerator only runs on structures with no accessors, so every prototype slot it sees is a plain data property. Skip those when the current structure is not the original object.
- Slow path: when iterating a prototype, keep only
slot.isCustom()entries. Native custom accessors (URL.href,Event.type,AbortSignal.aborted) still surface per-instance state; user-defined getters and data/function slots are shared class members and are dropped, matching Node's default formatter.
Own properties are untouched, so { fn: () => {} } still prints fn: [Function: fn].
Effect on host objects (no custom-inspect hook needed):
URL { href, origin, protocol, ..., searchParams } // no more toJSON/toString
AbortSignal { aborted, reason, onabort } // no throwIfAborted/addEventListener
Event { isTrusted, type, target, ..., returnValue } // no composedPath/.../NONE/CAPTURING_PHASE
Verification
USE_SYSTEM_BUN=1 bun test test/js/bun/util/inspect.test.js -t "class instances show only own" # fails
bun bd test test/js/bun/util/inspect.test.js # 74 pass
bun bd test test/js/web/console/console-log.test.ts test/js/web/url/url.test.ts # 22 pass
bun bd test test/js/node/util/custom-inspect.test.js test/js/node/util/bun-inspect.test.ts test/js/bun/console/ # 141 pass, 1 skip
Updated snapshots in inspect.test.js (CloseEvent/CustomEvent), url.test.ts, and console-log.expected.txt drop only [Function: ...] entries and the Event phase constants.
no test proof · iteration 1 · Platform-specific test(s) that do not run on this machine. Deferring to CI, which covers all platforms: test/bundler/bundler_edgecase.test.ts