util: add Map and Set inspection support
PR-URL: https://github.com/iojs/io.js/pull/1471 Reviewed-By: Chris Dickinson <christopher.s.dickinson@gmail.com>
This commit is contained in:
parent
5178f93bc0
commit
bf7ac08dd0
120
lib/util.js
120
lib/util.js
@ -1,6 +1,7 @@
|
||||
'use strict';
|
||||
|
||||
const uv = process.binding('uv');
|
||||
const Debug = require('vm').runInDebugContext('Debug');
|
||||
|
||||
const formatRegExp = /%[sdj%]/g;
|
||||
exports.format = function(f) {
|
||||
@ -192,6 +193,14 @@ function arrayToHash(array) {
|
||||
}
|
||||
|
||||
|
||||
function inspectPromise(p) {
|
||||
var mirror = Debug.MakeMirror(p, true);
|
||||
if (!mirror.isPromise())
|
||||
return null;
|
||||
return {status: mirror.status(), value: mirror.promiseValue().value_};
|
||||
}
|
||||
|
||||
|
||||
function formatValue(ctx, value, recurseTimes) {
|
||||
// Provide a hook for user-specified inspect functions.
|
||||
// Check that value is an object with an inspect function on it
|
||||
@ -276,14 +285,43 @@ function formatValue(ctx, value, recurseTimes) {
|
||||
}
|
||||
}
|
||||
|
||||
var base = '', array = false, braces = ['{', '}'];
|
||||
var base = '', empty = false, braces, formatter;
|
||||
|
||||
// Make Array say that they are Array
|
||||
if (Array.isArray(value)) {
|
||||
array = true;
|
||||
braces = ['[', ']'];
|
||||
empty = value.length === 0;
|
||||
formatter = formatArray;
|
||||
} else if (value instanceof Set) {
|
||||
braces = ['Set {', '}'];
|
||||
// With `showHidden`, `length` will display as a hidden property for
|
||||
// arrays. For consistency's sake, do the same for `size`, even though this
|
||||
// property isn't selected by Object.getOwnPropertyNames().
|
||||
if (ctx.showHidden)
|
||||
keys.unshift('size');
|
||||
empty = value.size === 0;
|
||||
formatter = formatSet;
|
||||
} else if (value instanceof Map) {
|
||||
braces = ['Map {', '}'];
|
||||
// Ditto.
|
||||
if (ctx.showHidden)
|
||||
keys.unshift('size');
|
||||
empty = value.size === 0;
|
||||
formatter = formatMap;
|
||||
} else {
|
||||
// Only create a mirror if the object superficially looks like a Promise.
|
||||
var promiseInternals = value instanceof Promise && inspectPromise(value);
|
||||
if (promiseInternals) {
|
||||
braces = ['Promise {', '}'];
|
||||
formatter = formatPromise;
|
||||
} else {
|
||||
braces = ['{', '}'];
|
||||
empty = true; // No other data than keys.
|
||||
formatter = formatObject;
|
||||
}
|
||||
}
|
||||
|
||||
empty = empty === true && keys.length === 0;
|
||||
|
||||
// Make functions say that they are functions
|
||||
if (typeof value === 'function') {
|
||||
var n = value.name ? ': ' + value.name : '';
|
||||
@ -323,7 +361,7 @@ function formatValue(ctx, value, recurseTimes) {
|
||||
base = ' ' + '[Boolean: ' + formatted + ']';
|
||||
}
|
||||
|
||||
if (keys.length === 0 && (!array || value.length === 0)) {
|
||||
if (empty === true) {
|
||||
return braces[0] + base + braces[1];
|
||||
}
|
||||
|
||||
@ -337,14 +375,7 @@ function formatValue(ctx, value, recurseTimes) {
|
||||
|
||||
ctx.seen.push(value);
|
||||
|
||||
var output;
|
||||
if (array) {
|
||||
output = formatArray(ctx, value, recurseTimes, visibleKeys, keys);
|
||||
} else {
|
||||
output = keys.map(function(key) {
|
||||
return formatProperty(ctx, value, recurseTimes, visibleKeys, key, array);
|
||||
});
|
||||
}
|
||||
var output = formatter(ctx, value, recurseTimes, visibleKeys, keys);
|
||||
|
||||
ctx.seen.pop();
|
||||
|
||||
@ -397,6 +428,13 @@ function formatError(value) {
|
||||
}
|
||||
|
||||
|
||||
function formatObject(ctx, value, recurseTimes, visibleKeys, keys) {
|
||||
return keys.map(function(key) {
|
||||
return formatProperty(ctx, value, recurseTimes, visibleKeys, key, false);
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
function formatArray(ctx, value, recurseTimes, visibleKeys, keys) {
|
||||
var output = [];
|
||||
for (var i = 0, l = value.length; i < l; ++i) {
|
||||
@ -417,6 +455,59 @@ function formatArray(ctx, value, recurseTimes, visibleKeys, keys) {
|
||||
}
|
||||
|
||||
|
||||
function formatSet(ctx, value, recurseTimes, visibleKeys, keys) {
|
||||
var output = [];
|
||||
value.forEach(function(v) {
|
||||
var nextRecurseTimes = recurseTimes === null ? null : recurseTimes - 1;
|
||||
var str = formatValue(ctx, v, nextRecurseTimes);
|
||||
output.push(str);
|
||||
});
|
||||
keys.forEach(function(key) {
|
||||
output.push(formatProperty(ctx, value, recurseTimes, visibleKeys,
|
||||
key, false));
|
||||
});
|
||||
return output;
|
||||
}
|
||||
|
||||
|
||||
function formatMap(ctx, value, recurseTimes, visibleKeys, keys) {
|
||||
var output = [];
|
||||
value.forEach(function(v, k) {
|
||||
var nextRecurseTimes = recurseTimes === null ? null : recurseTimes - 1;
|
||||
var str = formatValue(ctx, k, nextRecurseTimes);
|
||||
str += ' => ';
|
||||
str += formatValue(ctx, v, nextRecurseTimes);
|
||||
output.push(str);
|
||||
});
|
||||
keys.forEach(function(key) {
|
||||
output.push(formatProperty(ctx, value, recurseTimes, visibleKeys,
|
||||
key, false));
|
||||
});
|
||||
return output;
|
||||
}
|
||||
|
||||
function formatPromise(ctx, value, recurseTimes, visibleKeys, keys) {
|
||||
var output = [];
|
||||
var internals = inspectPromise(value);
|
||||
if (internals.status === 'pending') {
|
||||
output.push('<pending>');
|
||||
} else {
|
||||
var nextRecurseTimes = recurseTimes === null ? null : recurseTimes - 1;
|
||||
var str = formatValue(ctx, internals.value, nextRecurseTimes);
|
||||
if (internals.status === 'rejected') {
|
||||
output.push('<rejected> ' + str);
|
||||
} else {
|
||||
output.push(str);
|
||||
}
|
||||
}
|
||||
keys.forEach(function(key) {
|
||||
output.push(formatProperty(ctx, value, recurseTimes, visibleKeys,
|
||||
key, false));
|
||||
});
|
||||
return output;
|
||||
}
|
||||
|
||||
|
||||
function formatProperty(ctx, value, recurseTimes, visibleKeys, key, array) {
|
||||
var name, str, desc;
|
||||
desc = Object.getOwnPropertyDescriptor(value, key) || { value: value[key] };
|
||||
@ -488,7 +579,10 @@ function reduceToSingleString(output, base, braces) {
|
||||
|
||||
if (length > 60) {
|
||||
return braces[0] +
|
||||
(base === '' ? '' : base + '\n ') +
|
||||
// If the opening "brace" is too large, like in the case of "Set {",
|
||||
// we need to force the first item to be on the next line or the
|
||||
// items will not line up correctly.
|
||||
(base === '' && braces[0].length === 1 ? '' : base + '\n ') +
|
||||
' ' +
|
||||
output.join(',\n ') +
|
||||
' ' +
|
||||
|
@ -235,3 +235,68 @@ if (typeof Symbol !== 'undefined') {
|
||||
assert.equal(util.inspect(subject, options), '[ 1, 2, 3, [length]: 3, [Symbol(symbol)]: 42 ]');
|
||||
|
||||
}
|
||||
|
||||
// test Set
|
||||
assert.equal(util.inspect(new Set), 'Set {}');
|
||||
assert.equal(util.inspect(new Set([1, 2, 3])), 'Set { 1, 2, 3 }');
|
||||
var set = new Set(['foo']);
|
||||
set.bar = 42;
|
||||
assert.equal(util.inspect(set, true), 'Set { \'foo\', [size]: 1, bar: 42 }');
|
||||
|
||||
// test Map
|
||||
assert.equal(util.inspect(new Map), 'Map {}');
|
||||
assert.equal(util.inspect(new Map([[1, 'a'], [2, 'b'], [3, 'c']])),
|
||||
'Map { 1 => \'a\', 2 => \'b\', 3 => \'c\' }');
|
||||
var map = new Map([['foo', null]]);
|
||||
map.bar = 42;
|
||||
assert.equal(util.inspect(map, true),
|
||||
'Map { \'foo\' => null, [size]: 1, bar: 42 }');
|
||||
|
||||
// test Promise
|
||||
assert.equal(util.inspect(Promise.resolve(3)), 'Promise { 3 }');
|
||||
assert.equal(util.inspect(Promise.reject(3)), 'Promise { <rejected> 3 }');
|
||||
assert.equal(util.inspect(new Promise(function() {})), 'Promise { <pending> }');
|
||||
var promise = Promise.resolve('foo');
|
||||
promise.bar = 42;
|
||||
assert.equal(util.inspect(promise), 'Promise { \'foo\', bar: 42 }');
|
||||
|
||||
// Make sure it doesn't choke on polyfills. Unlike Set/Map, there is no standard
|
||||
// interface to synchronously inspect a Promise, so our techniques only work on
|
||||
// a bonafide native Promise.
|
||||
var oldPromise = Promise;
|
||||
global.Promise = function() { this.bar = 42; };
|
||||
assert.equal(util.inspect(new Promise), '{ bar: 42 }');
|
||||
global.Promise = oldPromise;
|
||||
|
||||
|
||||
// Test alignment of items in container
|
||||
// Assumes that the first numeric character is the start of an item.
|
||||
|
||||
function checkAlignment(container) {
|
||||
var lines = util.inspect(container).split('\n');
|
||||
var pos;
|
||||
lines.forEach(function(line) {
|
||||
var npos = line.search(/\d/);
|
||||
if (npos !== -1) {
|
||||
if (pos !== undefined)
|
||||
assert.equal(pos, npos, 'container items not aligned');
|
||||
pos = npos;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
var big_array = [];
|
||||
for (var i = 0; i < 100; i++) {
|
||||
big_array.push(i);
|
||||
}
|
||||
|
||||
checkAlignment(big_array);
|
||||
checkAlignment(function() {
|
||||
var obj = {};
|
||||
big_array.forEach(function(v) {
|
||||
obj[v] = null;
|
||||
});
|
||||
return obj;
|
||||
}());
|
||||
checkAlignment(new Set(big_array));
|
||||
checkAlignment(new Map(big_array.map(function (y) { return [y, null] })));
|
||||
|
Loading…
x
Reference in New Issue
Block a user