Skip to content

Commit

Permalink
util: make inspect aware of RegExp subclasses and null prototype
Browse files Browse the repository at this point in the history
This adds support for inspect to distinguish regular expression
subclasses and ones with null prototype from "normal" regular
expressions.

PR-URL: nodejs#25192
Reviewed-By: Anto Aravinth <anto.aravinth.cse@gmail.com>
Reviewed-By: James M Snell <jasnell@gmail.com>
  • Loading branch information
BridgeAR committed Dec 27, 2018
1 parent 728777d commit bd13afb
Show file tree
Hide file tree
Showing 3 changed files with 39 additions and 13 deletions.
7 changes: 5 additions & 2 deletions lib/internal/util/inspect.js
Original file line number Diff line number Diff line change
Expand Up @@ -634,9 +634,12 @@ function formatRaw(ctx, value, recurseTimes) {
base = `[${name}]`;
} else if (isRegExp(value)) {
// Make RegExps say that they are RegExps
base = regExpToString(constructor !== null ? value : new RegExp(value));
const prefix = getPrefix(constructor, tag, 'RegExp');
if (prefix !== 'RegExp ')
base = `${prefix}${base}`;
if (keys.length === 0 || recurseTimes < 0)
return ctx.stylize(regExpToString(value), 'regexp');
base = `${regExpToString(value)}`;
return ctx.stylize(base, 'regexp');
} else if (isDate(value)) {
// Make dates with properties first say the date
if (keys.length === 0) {
Expand Down
2 changes: 1 addition & 1 deletion test/parallel/test-assert-deep.js
Original file line number Diff line number Diff line change
Expand Up @@ -149,7 +149,7 @@ assert.throws(
{
code: 'ERR_ASSERTION',
message: `${defaultMsgStartFull}\n\n` +
"+ /test/\n- /test/ {\n- '0': '1'\n- }"
"+ /test/\n- MyRegExp /test/ {\n- '0': '1'\n- }"
}
);

Expand Down
43 changes: 33 additions & 10 deletions test/parallel/test-util-inspect.js
Original file line number Diff line number Diff line change
Expand Up @@ -1580,15 +1580,7 @@ assert.strictEqual(util.inspect('"\''), '`"\'`');
// eslint-disable-next-line no-template-curly-in-string
assert.strictEqual(util.inspect('"\'${a}'), "'\"\\'${a}'");

{
assert.strictEqual(
util.inspect(Object.setPrototypeOf(/a/, null)),
'/undefined/undefined'
);
}

// Verify that throwing in valueOf and having no prototype still produces nice
// results.
// Verify that throwing in valueOf and toString still produces nice results.
[
[new String(55), "[String: '55']"],
[new Boolean(true), '[Boolean: true]'],
Expand All @@ -1609,6 +1601,7 @@ assert.strictEqual(util.inspect('"\'${a}'), "'\"\\'${a}'");
[new Promise((resolve) => setTimeout(resolve, 10)), 'Promise { <pending> }'],
[new WeakSet(), 'WeakSet { <items unknown> }'],
[new WeakMap(), 'WeakMap { <items unknown> }'],
[/foobar/g, '/foobar/g']
].forEach(([value, expected]) => {
Object.defineProperty(value, 'valueOf', {
get() {
Expand All @@ -1628,6 +1621,7 @@ assert.strictEqual(util.inspect('"\'${a}'), "'\"\\'${a}'");
assert.notStrictEqual(util.inspect(value), expected);
});

// Verify that having no prototype still produces nice results.
[
[[1, 3, 4], '[Array: null prototype] [ 1, 3, 4 ]'],
[new Set([1, 2]), '[Set: null prototype] { 1, 2 }'],
Expand All @@ -1652,7 +1646,8 @@ assert.strictEqual(util.inspect('"\'${a}'), "'\"\\'${a}'");
'[DataView: null prototype] {\n byteLength: undefined,\n ' +
'byteOffset: undefined,\n buffer: undefined }'],
[new SharedArrayBuffer(2), '[SharedArrayBuffer: null prototype] ' +
'{ byteLength: undefined }']
'{ byteLength: undefined }'],
[/foobar/, '[RegExp: null prototype] /foobar/']
].forEach(([value, expected]) => {
assert.strictEqual(
util.inspect(Object.setPrototypeOf(value, null)),
Expand All @@ -1665,6 +1660,34 @@ assert.strictEqual(util.inspect('"\'${a}'), "'\"\\'${a}'");
assert.notStrictEqual(util.inspect(value), expected);
});

// Verify that subclasses with and without prototype produce nice results.
[
[RegExp, ['foobar', 'g'], '/foobar/g']
].forEach(([base, input, rawExpected]) => {
class Foo extends base {}
const value = new Foo(...input);
const symbol = value[Symbol.toStringTag];
const expected = `Foo ${symbol ? `[${symbol}] ` : ''}${rawExpected}`;
const expectedWithoutProto = `[${base.name}: null prototype] ${rawExpected}`;
assert.strictEqual(util.inspect(value), expected);
value.foo = 'bar';
assert.notStrictEqual(util.inspect(value), expected);
delete value.foo;
assert.strictEqual(
util.inspect(Object.setPrototypeOf(value, null)),
expectedWithoutProto
);
value.foo = 'bar';
let res = util.inspect(value);
assert.notStrictEqual(res, expectedWithoutProto);
assert(/foo: 'bar'/.test(res), res);
delete value.foo;
value[Symbol('foo')] = 'yeah';
res = util.inspect(value);
assert.notStrictEqual(res, expectedWithoutProto);
assert(/\[Symbol\(foo\)]: 'yeah'/.test(res), res);
});

assert.strictEqual(inspect(1n), '1n');
assert.strictEqual(inspect(Object(-1n)), '[BigInt: -1n]');
assert.strictEqual(inspect(Object(13n)), '[BigInt: 13n]');
Expand Down

0 comments on commit bd13afb

Please sign in to comment.