From 73f3a1c4e632ec4a2eb5915b924094e538316d33 Mon Sep 17 00:00:00 2001 From: Ruben Bridgewater Date: Sun, 23 Dec 2018 17:16:14 +0100 Subject: [PATCH] util: make inspect aware of RegExp subclasses and null prototype This adds support for inspect to distinguish regular expression subclasses and ones with null prototype from "normal" regular expressions. PR-URL: https://github.com/nodejs/node/pull/25192 Reviewed-By: Anto Aravinth Reviewed-By: James M Snell --- lib/internal/util/inspect.js | 7 +++-- test/parallel/test-assert-deep.js | 2 +- test/parallel/test-util-inspect.js | 43 +++++++++++++++++++++++------- 3 files changed, 39 insertions(+), 13 deletions(-) diff --git a/lib/internal/util/inspect.js b/lib/internal/util/inspect.js index 256a4a8b06904b..b47ed5411f46fb 100644 --- a/lib/internal/util/inspect.js +++ b/lib/internal/util/inspect.js @@ -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) { diff --git a/test/parallel/test-assert-deep.js b/test/parallel/test-assert-deep.js index 68b3ee5497db6a..b16b0f3ffcb911 100644 --- a/test/parallel/test-assert-deep.js +++ b/test/parallel/test-assert-deep.js @@ -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- }" } ); diff --git a/test/parallel/test-util-inspect.js b/test/parallel/test-util-inspect.js index 0d9cbdbe9d25fe..9ee02720897505 100644 --- a/test/parallel/test-util-inspect.js +++ b/test/parallel/test-util-inspect.js @@ -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]'], @@ -1609,6 +1601,7 @@ assert.strictEqual(util.inspect('"\'${a}'), "'\"\\'${a}'"); [new Promise((resolve) => setTimeout(resolve, 10)), 'Promise { }'], [new WeakSet(), 'WeakSet { }'], [new WeakMap(), 'WeakMap { }'], + [/foobar/g, '/foobar/g'] ].forEach(([value, expected]) => { Object.defineProperty(value, 'valueOf', { get() { @@ -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 }'], @@ -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)), @@ -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]');