Skip to content
Open
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 18 additions & 6 deletions src/js/internal/util/inspect.js
Original file line number Diff line number Diff line change
Expand Up @@ -344,12 +344,24 @@ function isURL(value) {

const SymbolToPrimitive = Symbol.toPrimitive;

const builtInObjects = new SafeSet(
ArrayPrototypeFilter(
ObjectGetOwnPropertyNames(globalThis),
e => RegExpPrototypeExec(/^[A-Z][a-zA-Z0-9]+$/, e) !== null,
),
);
// In Node.js this set is computed at bootstrap, before any Node/Web globals
// (Buffer, URL, Request, ...) are installed on globalThis. Bun already has
// those globals installed when this module loads, so we freeze the exact names
// Node v26 observes at bootstrap. A live `globalThis` scrape here would also
// over-include SharedArrayBuffer, WebAssembly, Float16Array, DisposableStack,
// AsyncDisposableStack and SuppressedError, which are absent from Node's
// bootstrap global and observably change `%s` and showHidden output.
// prettier-ignore
Comment thread
robobun marked this conversation as resolved.
Outdated
Comment thread
robobun marked this conversation as resolved.
const builtInObjects = new SafeSet([
"AggregateError", "Array", "ArrayBuffer", "Atomics", "BigInt", "BigInt64Array",
"BigUint64Array", "Boolean", "DataView", "Date", "Error", "EvalError",
"FinalizationRegistry", "Float32Array", "Float64Array", "Function", "Infinity",
"Int16Array", "Int32Array", "Int8Array", "Intl", "Iterator", "JSON", "Map",
"Math", "NaN", "Number", "Object", "Promise", "Proxy", "RangeError",
"ReferenceError", "Reflect", "RegExp", "Set", "String", "Symbol", "SyntaxError",
"TypeError", "URIError", "Uint16Array", "Uint32Array", "Uint8Array",
"Uint8ClampedArray", "WeakMap", "WeakRef", "WeakSet",
]);

// https://tc39.es/ecma262/#sec-IsHTMLDDA-internal-slot
const isUndetectableObject = v => typeof v === "undefined" && v !== undefined;
Expand Down
18 changes: 18 additions & 0 deletions test/js/node/util/node-inspect-tests/parallel/util-format.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -238,6 +238,24 @@
assert.strictEqual(util.format("%s", { __proto__: null }), "[Object: null prototype] {}");
}

// `%s` with values whose inherited `toString` comes from a non-ECMAScript
// built-in (Buffer, URL, ...) must call `String(value)`, not inspect it.
assert.strictEqual(util.format("%s", Buffer.from("ab")), "ab");
assert.strictEqual(util.format("%s", Buffer.from([0xe2, 0x82, 0xac])), "\u20ac");
assert.strictEqual(util.format("%s", new URL("http://a/b")), "http://a/b");
assert.strictEqual(util.format("%s", new URLSearchParams("a=1&b=2")), "a=1&b=2");
{
class MyBuffer extends Buffer {}
const sub = new MyBuffer(2);
sub[0] = 0x68;
sub[1] = 0x69;
assert.strictEqual(util.format("%s", sub), "hi");
}

Check warning on line 253 in test/js/node/util/node-inspect-tests/parallel/util-format.test.js

View check run for this annotation

Claude / Claude Code Review

MyBuffer subclass fixture does not exercise the subclass prototype path

The `MyBuffer` subclass fixture doesn't exercise the subclass prototype path — the deprecated `Buffer(size)` constructor doesn't honor `new.target`, so `new MyBuffer(2)` returns an object whose `[[Prototype]]` is `Buffer.prototype` directly (not `MyBuffer.prototype`), making this assertion redundant with the `Buffer.from("ab")` case four lines above. To actually cover the extra prototype-chain hop through `hasBuiltInToString`, use `Object.setPrototypeOf(Buffer.from([0x68, 0x69]), MyBuffer.protot
Comment thread
robobun marked this conversation as resolved.
// Uint8Array inherits toString from %TypedArray%.prototype, not Buffer.
assert.strictEqual(util.format("%s", new Uint8Array([65])), "65");
// ECMAScript built-ins with their own toString still take the inspect path.
assert.strictEqual(util.format("%s", [1, 2]), "[ 1, 2 ]");

// JSON format specifier
assert.strictEqual(util.format("%j"), "%j");
assert.strictEqual(util.format("%j", 42), "42");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@
// USE OR OTHER DEALINGS IN THE SOFTWARE.

import assert from "assert";
import { isWindows } from "harness";
import { isWindows, isDebug } from "harness";
import util, { inspect } from "util";
import vm from "vm";
import { MessageChannel } from "worker_threads";
Expand Down Expand Up @@ -236,9 +236,15 @@
);*/
});

// The surrogate-pair loop below runs ~10k util.inspect calls, which exceeds the
// default timeout on debug+ASAN builds; prettier-ignore keeps the 3rd test() arg
// from forcing a whole-body reindent.
// prettier-ignore
test("no assertion failures 2", () => {

Check warning on line 243 in test/js/node/util/node-inspect-tests/parallel/util-inspect.test.js

View check run for this annotation

Claude / Claude Code Review

prettier-ignore disables formatting on ~1500-line test body

The `// prettier-ignore` here applies to the next AST node — the entire `test("no assertion failures 2", ...)` expression statement — so all ~1580 lines of the callback body are now permanently exempt from auto-formatting. Rather than trading a one-time reindent for permanent format-exemption of a large ported test, consider extracting the slow surrogate-pair loop into its own `test()` with the debug timeout (which also keeps the timeout scoped to the code that actually needs it), or just accept
Comment thread
robobun marked this conversation as resolved.
Outdated
// Float16Array is intentionally absent: it is not in Node's bootstrap-time
// `builtInObjects`, so showHidden inspects its prototype getters and the
// output diverges from the other typed arrays in Node as well.
[
Float16Array,
Float32Array,
Float64Array,
Int16Array,
Expand Down Expand Up @@ -270,7 +276,6 @@

// Now check that declaring a TypedArray in a different context works the same.
[
Float16Array,
Float32Array,
Float64Array,
Int16Array,
Expand Down Expand Up @@ -1811,7 +1816,7 @@
})("a");
assert.strictEqual(util.inspect(args), "[Arguments] { '0': 'a' }");
}
});
}, isDebug ? 30_000 : undefined);
Comment thread
claude[bot] marked this conversation as resolved.
Outdated

test("util.inspect stack overflow handling", () => {
// Test that a long linked list can be inspected without throwing an error.
Expand Down
Loading