summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRuben Bridgewater <ruben@bridgewater.de>2019-04-14 21:37:03 +0200
committerRuben Bridgewater <ruben@bridgewater.de>2019-05-02 23:22:38 +0200
commitd0667e814e8be53d329a9c7f4849996c192395c9 (patch)
treeb9bd25e8aac5995f464f732c98e27912979a6258
parent57fd70fc7d3918a6bd4c714fe479488391a563f6 (diff)
downloadandroid-node-v8-d0667e814e8be53d329a9c7f4849996c192395c9.tar.gz
android-node-v8-d0667e814e8be53d329a9c7f4849996c192395c9.tar.bz2
android-node-v8-d0667e814e8be53d329a9c7f4849996c192395c9.zip
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>
-rw-r--r--lib/internal/util/inspect.js37
-rw-r--r--test/parallel/test-assert.js4
-rw-r--r--test/parallel/test-console-table.js2
-rw-r--r--test/parallel/test-repl.js2
-rw-r--r--test/parallel/test-util-format.js6
-rw-r--r--test/parallel/test-util-inspect-proxy.js4
-rw-r--r--test/parallel/test-util-inspect.js76
7 files changed, 101 insertions, 30 deletions
diff --git a/lib/internal/util/inspect.js b/lib/internal/util/inspect.js
index fccb46085d..8ffa303268 100644
--- a/lib/internal/util/inspect.js
+++ b/lib/internal/util/inspect.js
@@ -49,6 +49,8 @@ const {
} = require('internal/errors');
const {
+ isAsyncFunction,
+ isGeneratorFunction,
isAnyArrayBuffer,
isArrayBuffer,
isArgumentsObject,
@@ -642,14 +644,9 @@ function formatRaw(ctx, value, recurseTimes, typedArray) {
return `${braces[0]}}`;
}
} else if (typeof value === 'function') {
- const type = constructor || tag || 'Function';
- let name = `${type}`;
- if (value.name && typeof value.name === 'string') {
- name += `: ${value.name}`;
- }
+ base = getFunctionBase(value, constructor, tag);
if (keys.length === 0)
- return ctx.stylize(`[${name}]`, 'special');
- base = `[${name}]`;
+ return ctx.stylize(base, 'special');
} else if (isRegExp(value)) {
// Make RegExps say that they are RegExps
base = RegExpPrototype.toString(
@@ -834,6 +831,32 @@ function getBoxedBase(value, ctx, keys, constructor, tag) {
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) {
// TODO(BridgeAR): Always show the error code if present.
let stack = err.stack || ErrorPrototype.toString(err);
diff --git a/test/parallel/test-assert.js b/test/parallel/test-assert.js
index 003e67b380..98f728acfa 100644
--- a/test/parallel/test-assert.js
+++ b/test/parallel/test-assert.js
@@ -289,7 +289,7 @@ testAssertionMessage(undefined, 'undefined');
testAssertionMessage(-Infinity, '-Infinity');
testAssertionMessage([1, 2, 3], '[\n+ 1,\n+ 2,\n+ 3\n+ ]');
testAssertionMessage(function f() {}, '[Function: f]');
-testAssertionMessage(function() {}, '[Function]');
+testAssertionMessage(function() {}, '[Function (anonymous)]');
testAssertionMessage(circular, '{\n+ x: [Circular],\n+ y: 1\n+ }');
testAssertionMessage({ a: undefined, b: null },
'{\n+ a: undefined,\n+ b: null\n+ }');
@@ -597,7 +597,7 @@ assert.throws(
'\n' +
'+ {}\n' +
'- {\n' +
- '- [Symbol(nodejs.util.inspect.custom)]: [Function],\n' +
+ '- [Symbol(nodejs.util.inspect.custom)]: [Function (anonymous)],\n' +
"- loop: 'forever'\n" +
'- }'
});
diff --git a/test/parallel/test-console-table.js b/test/parallel/test-console-table.js
index 3a4d6fefbb..98c6dd8776 100644
--- a/test/parallel/test-console-table.js
+++ b/test/parallel/test-console-table.js
@@ -32,7 +32,7 @@ test(undefined, 'undefined\n');
test(false, 'false\n');
test('hi', 'hi\n');
test(Symbol(), 'Symbol()\n');
-test(function() {}, '[Function]\n');
+test(function() {}, '[Function (anonymous)]\n');
test([1, 2, 3], `
┌─────────┬────────┐
diff --git a/test/parallel/test-repl.js b/test/parallel/test-repl.js
index c0869c84b3..05608bd5ee 100644
--- a/test/parallel/test-repl.js
+++ b/test/parallel/test-repl.js
@@ -308,7 +308,7 @@ const errorTests = [
// Functions should not evaluate twice (#2773)
{
send: 'var I = [1,2,3,function() {}]; I.pop()',
- expect: '[Function]'
+ expect: '[Function (anonymous)]'
},
// Multiline object
{
diff --git a/test/parallel/test-util-format.js b/test/parallel/test-util-format.js
index 206b66e17c..0869d53f22 100644
--- a/test/parallel/test-util-format.js
+++ b/test/parallel/test-util-format.js
@@ -288,7 +288,7 @@ assert.strictEqual(util.format('abc%', 1), 'abc% 1');
// Additional arguments after format specifiers
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 = {};
@@ -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, -0), '1 -0');
-assert.strictEqual(util.format('1', () => {}), '1 [Function]');
-assert.strictEqual(util.format(1, () => {}), '1 [Function]');
+assert.strictEqual(util.format('1', () => {}), '1 [Function (anonymous)]');
+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', 'number'), '1 number');
diff --git a/test/parallel/test-util-inspect-proxy.js b/test/parallel/test-util-inspect-proxy.js
index c20af7450a..da0512eda1 100644
--- a/test/parallel/test-util-inspect-proxy.js
+++ b/test/parallel/test-util-inspect-proxy.js
@@ -144,7 +144,7 @@ const proxy11 = new Proxy(() => {}, {
return proxy11;
}
});
-const expected10 = '[Function]';
-const expected11 = '[Function]';
+const expected10 = '[Function (anonymous)]';
+const expected11 = '[Function (anonymous)]';
assert.strictEqual(util.inspect(proxy10), expected10);
assert.strictEqual(util.inspect(proxy11), expected11);
diff --git a/test/parallel/test-util-inspect.js b/test/parallel/test-util-inspect.js
index 5c54654a73..1bab0fe0a7 100644
--- a/test/parallel/test-util-inspect.js
+++ b/test/parallel/test-util-inspect.js
@@ -33,11 +33,51 @@ assert.strictEqual(util.inspect(1), '1');
assert.strictEqual(util.inspect(false), 'false');
assert.strictEqual(util.inspect(''), "''");
assert.strictEqual(util.inspect('hello'), "'hello'");
-assert.strictEqual(util.inspect(function() {}), '[Function]');
-assert.strictEqual(util.inspect(() => {}), '[Function]');
-assert.strictEqual(util.inspect(async function() {}), '[AsyncFunction]');
-assert.strictEqual(util.inspect(async () => {}), '[AsyncFunction]');
-assert.strictEqual(util.inspect(function*() {}), '[GeneratorFunction]');
+assert.strictEqual(util.inspect(function abc() {}), '[Function: abc]');
+assert.strictEqual(util.inspect(() => {}), '[Function (anonymous)]');
+assert.strictEqual(
+ util.inspect(async function() {}),
+ '[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(null), 'null');
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: function() {} }), '{ a: [Function: a] }');
assert.strictEqual(util.inspect({ a: () => {} }), '{ a: [Function: a] }');
-assert.strictEqual(util.inspect({ a: async function() {} }),
- '{ a: [AsyncFunction: a] }');
+// eslint-disable-next-line func-name-matching
+assert.strictEqual(util.inspect({ a: async function abc() {} }),
+ '{ a: [AsyncFunction: abc] }');
assert.strictEqual(util.inspect({ a: async () => {} }),
'{ a: [AsyncFunction: a] }');
assert.strictEqual(util.inspect({ a: function*() {} }),
@@ -411,7 +452,10 @@ assert.strictEqual(
{
const value = (() => function() {})();
value.aprop = 42;
- assert.strictEqual(util.inspect(value), '[Function] { aprop: 42 }');
+ assert.strictEqual(
+ util.inspect(value),
+ '[Function (anonymous)] { aprop: 42 }'
+ );
}
// Regular expressions with properties.
@@ -1441,7 +1485,7 @@ util.inspect(process);
out = util.inspect(o, { compact: false, breakLength: 3 });
expect = [
'{',
- ' a: [Function],',
+ ' a: [Function (anonymous)],',
' b: [Number: 3]',
'}'
].join('\n');
@@ -1450,7 +1494,7 @@ util.inspect(process);
out = util.inspect(o, { compact: false, breakLength: 3, showHidden: true });
expect = [
'{',
- ' a: [Function] {',
+ ' a: [Function (anonymous)] {',
' [length]: 0,',
" [name]: ''",
' },',
@@ -1767,8 +1811,8 @@ assert.strictEqual(util.inspect('"\'${a}'), "'\"\\'${a}'");
[new Number(55), '[Number: 55]'],
[Object(BigInt(55)), '[BigInt: 55n]'],
[Object(Symbol('foo')), '[Symbol: Symbol(foo)]'],
- [function() {}, '[Function]'],
- [() => {}, '[Function]'],
+ [function() {}, '[Function (anonymous)]'],
+ [() => {}, '[Function (anonymous)]'],
[[1, 2], '[ 1, 2 ]'],
[[, , 5, , , , ], '[ <2 empty items>, 5, <3 empty items> ]'],
[{ a: 5 }, '{ a: 5 }'],
@@ -1957,10 +2001,14 @@ assert.strictEqual(
let value = (function() { return function() {}; })();
Object.setPrototypeOf(value, null);
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(
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 };