util: improve function inspection
This commit contains the following changes: 1) Add null prototype support for functions. 2) Safely detect async and generator functions. 3) Mark anonymous functions as such instead of just leaving out the name. PR-URL: https://github.com/nodejs/node/pull/27227 Reviewed-By: Joyee Cheung <joyeec9h3@gmail.com> Reviewed-By: John-David Dalton <john.david.dalton@gmail.com> Reviewed-By: Anto Aravinth <anto.aravinth.cse@gmail.com> Reviewed-By: Rich Trott <rtrott@gmail.com>
This commit is contained in:
parent
57fd70fc7d
commit
d0667e814e
@ -49,6 +49,8 @@ const {
|
|||||||
} = require('internal/errors');
|
} = require('internal/errors');
|
||||||
|
|
||||||
const {
|
const {
|
||||||
|
isAsyncFunction,
|
||||||
|
isGeneratorFunction,
|
||||||
isAnyArrayBuffer,
|
isAnyArrayBuffer,
|
||||||
isArrayBuffer,
|
isArrayBuffer,
|
||||||
isArgumentsObject,
|
isArgumentsObject,
|
||||||
@ -642,14 +644,9 @@ function formatRaw(ctx, value, recurseTimes, typedArray) {
|
|||||||
return `${braces[0]}}`;
|
return `${braces[0]}}`;
|
||||||
}
|
}
|
||||||
} else if (typeof value === 'function') {
|
} else if (typeof value === 'function') {
|
||||||
const type = constructor || tag || 'Function';
|
base = getFunctionBase(value, constructor, tag);
|
||||||
let name = `${type}`;
|
|
||||||
if (value.name && typeof value.name === 'string') {
|
|
||||||
name += `: ${value.name}`;
|
|
||||||
}
|
|
||||||
if (keys.length === 0)
|
if (keys.length === 0)
|
||||||
return ctx.stylize(`[${name}]`, 'special');
|
return ctx.stylize(base, 'special');
|
||||||
base = `[${name}]`;
|
|
||||||
} else if (isRegExp(value)) {
|
} else if (isRegExp(value)) {
|
||||||
// Make RegExps say that they are RegExps
|
// Make RegExps say that they are RegExps
|
||||||
base = RegExpPrototype.toString(
|
base = RegExpPrototype.toString(
|
||||||
@ -834,6 +831,32 @@ function getBoxedBase(value, ctx, keys, constructor, tag) {
|
|||||||
return ctx.stylize(base, type.toLowerCase());
|
return ctx.stylize(base, type.toLowerCase());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function getFunctionBase(value, constructor, tag) {
|
||||||
|
let type = 'Function';
|
||||||
|
if (isAsyncFunction(value)) {
|
||||||
|
type = 'AsyncFunction';
|
||||||
|
} else if (isGeneratorFunction(value)) {
|
||||||
|
type = 'GeneratorFunction';
|
||||||
|
}
|
||||||
|
let base = `[${type}`;
|
||||||
|
if (constructor === null) {
|
||||||
|
base += ' (null prototype)';
|
||||||
|
}
|
||||||
|
if (value.name === '') {
|
||||||
|
base += ' (anonymous)';
|
||||||
|
} else {
|
||||||
|
base += `: ${value.name}`;
|
||||||
|
}
|
||||||
|
base += ']';
|
||||||
|
if (constructor !== type && constructor !== null) {
|
||||||
|
base += ` ${constructor}`;
|
||||||
|
}
|
||||||
|
if (tag !== '' && constructor !== tag) {
|
||||||
|
base += ` [${tag}]`;
|
||||||
|
}
|
||||||
|
return base;
|
||||||
|
}
|
||||||
|
|
||||||
function formatError(err, constructor, tag, ctx) {
|
function formatError(err, constructor, tag, ctx) {
|
||||||
// TODO(BridgeAR): Always show the error code if present.
|
// TODO(BridgeAR): Always show the error code if present.
|
||||||
let stack = err.stack || ErrorPrototype.toString(err);
|
let stack = err.stack || ErrorPrototype.toString(err);
|
||||||
|
@ -289,7 +289,7 @@ testAssertionMessage(undefined, 'undefined');
|
|||||||
testAssertionMessage(-Infinity, '-Infinity');
|
testAssertionMessage(-Infinity, '-Infinity');
|
||||||
testAssertionMessage([1, 2, 3], '[\n+ 1,\n+ 2,\n+ 3\n+ ]');
|
testAssertionMessage([1, 2, 3], '[\n+ 1,\n+ 2,\n+ 3\n+ ]');
|
||||||
testAssertionMessage(function f() {}, '[Function: f]');
|
testAssertionMessage(function f() {}, '[Function: f]');
|
||||||
testAssertionMessage(function() {}, '[Function]');
|
testAssertionMessage(function() {}, '[Function (anonymous)]');
|
||||||
testAssertionMessage(circular, '{\n+ x: [Circular],\n+ y: 1\n+ }');
|
testAssertionMessage(circular, '{\n+ x: [Circular],\n+ y: 1\n+ }');
|
||||||
testAssertionMessage({ a: undefined, b: null },
|
testAssertionMessage({ a: undefined, b: null },
|
||||||
'{\n+ a: undefined,\n+ b: null\n+ }');
|
'{\n+ a: undefined,\n+ b: null\n+ }');
|
||||||
@ -597,7 +597,7 @@ assert.throws(
|
|||||||
'\n' +
|
'\n' +
|
||||||
'+ {}\n' +
|
'+ {}\n' +
|
||||||
'- {\n' +
|
'- {\n' +
|
||||||
'- [Symbol(nodejs.util.inspect.custom)]: [Function],\n' +
|
'- [Symbol(nodejs.util.inspect.custom)]: [Function (anonymous)],\n' +
|
||||||
"- loop: 'forever'\n" +
|
"- loop: 'forever'\n" +
|
||||||
'- }'
|
'- }'
|
||||||
});
|
});
|
||||||
|
@ -32,7 +32,7 @@ test(undefined, 'undefined\n');
|
|||||||
test(false, 'false\n');
|
test(false, 'false\n');
|
||||||
test('hi', 'hi\n');
|
test('hi', 'hi\n');
|
||||||
test(Symbol(), 'Symbol()\n');
|
test(Symbol(), 'Symbol()\n');
|
||||||
test(function() {}, '[Function]\n');
|
test(function() {}, '[Function (anonymous)]\n');
|
||||||
|
|
||||||
test([1, 2, 3], `
|
test([1, 2, 3], `
|
||||||
┌─────────┬────────┐
|
┌─────────┬────────┐
|
||||||
|
@ -308,7 +308,7 @@ const errorTests = [
|
|||||||
// Functions should not evaluate twice (#2773)
|
// Functions should not evaluate twice (#2773)
|
||||||
{
|
{
|
||||||
send: 'var I = [1,2,3,function() {}]; I.pop()',
|
send: 'var I = [1,2,3,function() {}]; I.pop()',
|
||||||
expect: '[Function]'
|
expect: '[Function (anonymous)]'
|
||||||
},
|
},
|
||||||
// Multiline object
|
// Multiline object
|
||||||
{
|
{
|
||||||
|
@ -288,7 +288,7 @@ assert.strictEqual(util.format('abc%', 1), 'abc% 1');
|
|||||||
|
|
||||||
// Additional arguments after format specifiers
|
// Additional arguments after format specifiers
|
||||||
assert.strictEqual(util.format('%i', 1, 'number'), '1 number');
|
assert.strictEqual(util.format('%i', 1, 'number'), '1 number');
|
||||||
assert.strictEqual(util.format('%i', 1, () => {}), '1 [Function]');
|
assert.strictEqual(util.format('%i', 1, () => {}), '1 [Function (anonymous)]');
|
||||||
|
|
||||||
{
|
{
|
||||||
const o = {};
|
const o = {};
|
||||||
@ -339,8 +339,8 @@ assert.strictEqual(util.format('1', '1'), '1 1');
|
|||||||
assert.strictEqual(util.format(1, '1'), '1 1');
|
assert.strictEqual(util.format(1, '1'), '1 1');
|
||||||
assert.strictEqual(util.format('1', 1), '1 1');
|
assert.strictEqual(util.format('1', 1), '1 1');
|
||||||
assert.strictEqual(util.format(1, -0), '1 -0');
|
assert.strictEqual(util.format(1, -0), '1 -0');
|
||||||
assert.strictEqual(util.format('1', () => {}), '1 [Function]');
|
assert.strictEqual(util.format('1', () => {}), '1 [Function (anonymous)]');
|
||||||
assert.strictEqual(util.format(1, () => {}), '1 [Function]');
|
assert.strictEqual(util.format(1, () => {}), '1 [Function (anonymous)]');
|
||||||
assert.strictEqual(util.format('1', "'"), "1 '");
|
assert.strictEqual(util.format('1', "'"), "1 '");
|
||||||
assert.strictEqual(util.format(1, "'"), "1 '");
|
assert.strictEqual(util.format(1, "'"), "1 '");
|
||||||
assert.strictEqual(util.format('1', 'number'), '1 number');
|
assert.strictEqual(util.format('1', 'number'), '1 number');
|
||||||
|
@ -144,7 +144,7 @@ const proxy11 = new Proxy(() => {}, {
|
|||||||
return proxy11;
|
return proxy11;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
const expected10 = '[Function]';
|
const expected10 = '[Function (anonymous)]';
|
||||||
const expected11 = '[Function]';
|
const expected11 = '[Function (anonymous)]';
|
||||||
assert.strictEqual(util.inspect(proxy10), expected10);
|
assert.strictEqual(util.inspect(proxy10), expected10);
|
||||||
assert.strictEqual(util.inspect(proxy11), expected11);
|
assert.strictEqual(util.inspect(proxy11), expected11);
|
||||||
|
@ -33,11 +33,51 @@ assert.strictEqual(util.inspect(1), '1');
|
|||||||
assert.strictEqual(util.inspect(false), 'false');
|
assert.strictEqual(util.inspect(false), 'false');
|
||||||
assert.strictEqual(util.inspect(''), "''");
|
assert.strictEqual(util.inspect(''), "''");
|
||||||
assert.strictEqual(util.inspect('hello'), "'hello'");
|
assert.strictEqual(util.inspect('hello'), "'hello'");
|
||||||
assert.strictEqual(util.inspect(function() {}), '[Function]');
|
assert.strictEqual(util.inspect(function abc() {}), '[Function: abc]');
|
||||||
assert.strictEqual(util.inspect(() => {}), '[Function]');
|
assert.strictEqual(util.inspect(() => {}), '[Function (anonymous)]');
|
||||||
assert.strictEqual(util.inspect(async function() {}), '[AsyncFunction]');
|
assert.strictEqual(
|
||||||
assert.strictEqual(util.inspect(async () => {}), '[AsyncFunction]');
|
util.inspect(async function() {}),
|
||||||
assert.strictEqual(util.inspect(function*() {}), '[GeneratorFunction]');
|
'[AsyncFunction (anonymous)]'
|
||||||
|
);
|
||||||
|
assert.strictEqual(util.inspect(async () => {}), '[AsyncFunction (anonymous)]');
|
||||||
|
|
||||||
|
// Special function inspection.
|
||||||
|
{
|
||||||
|
const fn = (() => function*() {})();
|
||||||
|
assert.strictEqual(
|
||||||
|
util.inspect(fn),
|
||||||
|
'[GeneratorFunction (anonymous)]'
|
||||||
|
);
|
||||||
|
Object.setPrototypeOf(fn, Object.getPrototypeOf(async () => {}));
|
||||||
|
assert.strictEqual(
|
||||||
|
util.inspect(fn),
|
||||||
|
'[GeneratorFunction (anonymous)] AsyncFunction'
|
||||||
|
);
|
||||||
|
Object.defineProperty(fn, 'name', { value: 5, configurable: true });
|
||||||
|
assert.strictEqual(
|
||||||
|
util.inspect(fn),
|
||||||
|
'[GeneratorFunction: 5] AsyncFunction'
|
||||||
|
);
|
||||||
|
Object.defineProperty(fn, Symbol.toStringTag, {
|
||||||
|
value: 'Foobar',
|
||||||
|
configurable: true
|
||||||
|
});
|
||||||
|
assert.strictEqual(
|
||||||
|
util.inspect({ ['5']: fn }),
|
||||||
|
"{ '5': [GeneratorFunction: 5] AsyncFunction [Foobar] }"
|
||||||
|
);
|
||||||
|
Object.defineProperty(fn, 'name', { value: '5', configurable: true });
|
||||||
|
Object.setPrototypeOf(fn, null);
|
||||||
|
assert.strictEqual(
|
||||||
|
util.inspect(fn),
|
||||||
|
'[GeneratorFunction (null prototype): 5] [Foobar]'
|
||||||
|
);
|
||||||
|
assert.strictEqual(
|
||||||
|
util.inspect({ ['5']: fn }),
|
||||||
|
"{ '5': [GeneratorFunction (null prototype): 5] [Foobar] }"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
assert.strictEqual(util.inspect(undefined), 'undefined');
|
assert.strictEqual(util.inspect(undefined), 'undefined');
|
||||||
assert.strictEqual(util.inspect(null), 'null');
|
assert.strictEqual(util.inspect(null), 'null');
|
||||||
assert.strictEqual(util.inspect(/foo(bar\n)?/gi), '/foo(bar\\n)?/gi');
|
assert.strictEqual(util.inspect(/foo(bar\n)?/gi), '/foo(bar\\n)?/gi');
|
||||||
@ -59,8 +99,9 @@ assert.strictEqual(util.inspect({}), '{}');
|
|||||||
assert.strictEqual(util.inspect({ a: 1 }), '{ a: 1 }');
|
assert.strictEqual(util.inspect({ a: 1 }), '{ a: 1 }');
|
||||||
assert.strictEqual(util.inspect({ a: function() {} }), '{ a: [Function: a] }');
|
assert.strictEqual(util.inspect({ a: function() {} }), '{ a: [Function: a] }');
|
||||||
assert.strictEqual(util.inspect({ a: () => {} }), '{ a: [Function: a] }');
|
assert.strictEqual(util.inspect({ a: () => {} }), '{ a: [Function: a] }');
|
||||||
assert.strictEqual(util.inspect({ a: async function() {} }),
|
// eslint-disable-next-line func-name-matching
|
||||||
'{ a: [AsyncFunction: a] }');
|
assert.strictEqual(util.inspect({ a: async function abc() {} }),
|
||||||
|
'{ a: [AsyncFunction: abc] }');
|
||||||
assert.strictEqual(util.inspect({ a: async () => {} }),
|
assert.strictEqual(util.inspect({ a: async () => {} }),
|
||||||
'{ a: [AsyncFunction: a] }');
|
'{ a: [AsyncFunction: a] }');
|
||||||
assert.strictEqual(util.inspect({ a: function*() {} }),
|
assert.strictEqual(util.inspect({ a: function*() {} }),
|
||||||
@ -411,7 +452,10 @@ assert.strictEqual(
|
|||||||
{
|
{
|
||||||
const value = (() => function() {})();
|
const value = (() => function() {})();
|
||||||
value.aprop = 42;
|
value.aprop = 42;
|
||||||
assert.strictEqual(util.inspect(value), '[Function] { aprop: 42 }');
|
assert.strictEqual(
|
||||||
|
util.inspect(value),
|
||||||
|
'[Function (anonymous)] { aprop: 42 }'
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Regular expressions with properties.
|
// Regular expressions with properties.
|
||||||
@ -1441,7 +1485,7 @@ util.inspect(process);
|
|||||||
out = util.inspect(o, { compact: false, breakLength: 3 });
|
out = util.inspect(o, { compact: false, breakLength: 3 });
|
||||||
expect = [
|
expect = [
|
||||||
'{',
|
'{',
|
||||||
' a: [Function],',
|
' a: [Function (anonymous)],',
|
||||||
' b: [Number: 3]',
|
' b: [Number: 3]',
|
||||||
'}'
|
'}'
|
||||||
].join('\n');
|
].join('\n');
|
||||||
@ -1450,7 +1494,7 @@ util.inspect(process);
|
|||||||
out = util.inspect(o, { compact: false, breakLength: 3, showHidden: true });
|
out = util.inspect(o, { compact: false, breakLength: 3, showHidden: true });
|
||||||
expect = [
|
expect = [
|
||||||
'{',
|
'{',
|
||||||
' a: [Function] {',
|
' a: [Function (anonymous)] {',
|
||||||
' [length]: 0,',
|
' [length]: 0,',
|
||||||
" [name]: ''",
|
" [name]: ''",
|
||||||
' },',
|
' },',
|
||||||
@ -1767,8 +1811,8 @@ assert.strictEqual(util.inspect('"\'${a}'), "'\"\\'${a}'");
|
|||||||
[new Number(55), '[Number: 55]'],
|
[new Number(55), '[Number: 55]'],
|
||||||
[Object(BigInt(55)), '[BigInt: 55n]'],
|
[Object(BigInt(55)), '[BigInt: 55n]'],
|
||||||
[Object(Symbol('foo')), '[Symbol: Symbol(foo)]'],
|
[Object(Symbol('foo')), '[Symbol: Symbol(foo)]'],
|
||||||
[function() {}, '[Function]'],
|
[function() {}, '[Function (anonymous)]'],
|
||||||
[() => {}, '[Function]'],
|
[() => {}, '[Function (anonymous)]'],
|
||||||
[[1, 2], '[ 1, 2 ]'],
|
[[1, 2], '[ 1, 2 ]'],
|
||||||
[[, , 5, , , , ], '[ <2 empty items>, 5, <3 empty items> ]'],
|
[[, , 5, , , , ], '[ <2 empty items>, 5, <3 empty items> ]'],
|
||||||
[{ a: 5 }, '{ a: 5 }'],
|
[{ a: 5 }, '{ a: 5 }'],
|
||||||
@ -1957,10 +2001,14 @@ assert.strictEqual(
|
|||||||
let value = (function() { return function() {}; })();
|
let value = (function() { return function() {}; })();
|
||||||
Object.setPrototypeOf(value, null);
|
Object.setPrototypeOf(value, null);
|
||||||
Object.setPrototypeOf(obj, value);
|
Object.setPrototypeOf(obj, value);
|
||||||
assert.strictEqual(util.inspect(obj), '<[Function]> { a: true }');
|
assert.strictEqual(
|
||||||
|
util.inspect(obj),
|
||||||
|
'<[Function (null prototype) (anonymous)]> { a: true }'
|
||||||
|
);
|
||||||
assert.strictEqual(
|
assert.strictEqual(
|
||||||
util.inspect(obj, { colors: true }),
|
util.inspect(obj, { colors: true }),
|
||||||
'<\u001b[36m[Function]\u001b[39m> { a: \u001b[33mtrue\u001b[39m }'
|
'<\u001b[36m[Function (null prototype) (anonymous)]\u001b[39m> ' +
|
||||||
|
'{ a: \u001b[33mtrue\u001b[39m }'
|
||||||
);
|
);
|
||||||
|
|
||||||
obj = { a: true };
|
obj = { a: true };
|
||||||
|
Loading…
x
Reference in New Issue
Block a user