Skip to content

Commit

Permalink
util: handle circular maps and sets in inspect()
Browse files Browse the repository at this point in the history
Handle maps and sets with circular references in `util.inspect()` the
way objects and arrays are treated in this case.

Fixes: nodejs#14758
  • Loading branch information
aqrln committed Aug 11, 2017
1 parent 7307839 commit 566b5f5
Show file tree
Hide file tree
Showing 2 changed files with 49 additions and 9 deletions.
27 changes: 18 additions & 9 deletions lib/util.js
Original file line number Diff line number Diff line change
Expand Up @@ -737,9 +737,7 @@ function formatTypedArray(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);
output.push(formatMapSetValue(ctx, v, recurseTimes));
});
for (var n = 0; n < keys.length; n++) {
output.push(formatProperty(ctx, value, recurseTimes, visibleKeys,
Expand All @@ -752,11 +750,11 @@ function formatSet(ctx, value, recurseTimes, visibleKeys, keys) {
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);
output.push(
formatMapSetValue(ctx, k, recurseTimes) +
' => ' +
formatMapSetValue(ctx, v, recurseTimes)
);
});
for (var n = 0; n < keys.length; n++) {
output.push(formatProperty(ctx, value, recurseTimes, visibleKeys,
Expand All @@ -765,6 +763,14 @@ function formatMap(ctx, value, recurseTimes, visibleKeys, keys) {
return output;
}

function formatMapSetValue(ctx, value, recurseTimes) {
if (ctx.seen.includes(value)) {
return formatCircular(ctx);
}
const nextRecurseTimes = recurseTimes === null ? null : recurseTimes - 1;
return formatValue(ctx, value, nextRecurseTimes);
}

function formatCollectionIterator(ctx, value, recurseTimes, visibleKeys, keys) {
ensureDebugIsInitialized();
const mirror = Debug.MakeMirror(value, true);
Expand Down Expand Up @@ -836,7 +842,7 @@ function formatProperty(ctx, value, recurseTimes, visibleKeys, key, array) {
}
}
} else {
str = ctx.stylize('[Circular]', 'special');
str = formatCircular(ctx);
}
}
if (name === undefined) {
Expand All @@ -859,6 +865,9 @@ function formatProperty(ctx, value, recurseTimes, visibleKeys, key, array) {
return `${name}: ${str}`;
}

function formatCircular(ctx) {
return ctx.stylize('[Circular]', 'special');
}

function reduceToSingleString(output, base, braces, breakLength) {
var length = output.reduce(function(prev, cur) {
Expand Down
31 changes: 31 additions & 0 deletions test/parallel/test-util-inspect.js
Original file line number Diff line number Diff line change
Expand Up @@ -794,6 +794,37 @@ if (typeof Symbol !== 'undefined') {
'Map { \'foo\' => null, [size]: 1, bar: 42 }');
}

// Test maps and sets with circular references
{
const circularKeyMap = new Map();
circularKeyMap.set(circularKeyMap, 'value');
assert.strictEqual(
util.inspect(circularKeyMap),
'Map { [Circular] => \'value\' }'
);

const circularValueMap = new Map();
circularValueMap.set('key', circularValueMap);
assert.strictEqual(
util.inspect(circularValueMap),
'Map { \'key\' => [Circular] }'
);

const circularKeyValueMap = new Map();
circularKeyValueMap.set(circularKeyValueMap, circularKeyValueMap);
assert.strictEqual(
util.inspect(circularKeyValueMap),
'Map { [Circular] => [Circular] }'
);

const circularSet = new Set();
circularSet.add(circularSet);
assert.strictEqual(
util.inspect(circularSet),
'Set { [Circular] }'
);
}

// test Promise
{
const resolved = Promise.resolve(3);
Expand Down

0 comments on commit 566b5f5

Please sign in to comment.