Skip to content

Commit ef4e8d3

Browse files
committed
util: recover from maximum call stack size
Using util.inspect should still return values in case the maximum call stack size is reached. This is important to inspect linked lists and similar.
1 parent bf12384 commit ef4e8d3

File tree

3 files changed

+44
-8
lines changed

3 files changed

+44
-8
lines changed

doc/api/util.md

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -360,6 +360,10 @@ stream.write('With ES6');
360360
<!-- YAML
361361
added: v0.3.0
362362
changes:
363+
- version: REPLACEME
364+
pr-url: https://github.com/nodejs/node/pull/REPLACEME
365+
description: Inspecting linked lists and similar objects is now possible
366+
up to the maximum call stack size.
363367
- version: v10.0.0
364368
pr-url: https://github.com/nodejs/node/pull/19259
365369
description: The `WeakMap` and `WeakSet` entries can now be inspected
@@ -388,8 +392,9 @@ changes:
388392
properties will be included in the formatted result as well as [`WeakMap`][]
389393
and [`WeakSet`][] entries. **Default:** `false`.
390394
* `depth` {number} Specifies the number of times to recurse while formatting
391-
the `object`. This is useful for inspecting large complicated objects.
392-
To make it recurse indefinitely pass `null`. **Default:** `2`.
395+
the `object`. This is useful for inspecting large complicated objects. To
396+
make it recurse up to the maximum call stack size pass `Infinity` or `null`.
397+
**Default:** `2`.
393398
* `colors` {boolean} If `true`, the output will be styled with ANSI color
394399
codes. Colors are customizable, see [Customizing `util.inspect` colors][].
395400
**Default:** `false`.

lib/util.js

Lines changed: 20 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -701,14 +701,32 @@ function formatValue(ctx, value, recurseTimes) {
701701
}
702702

703703
ctx.seen.push(value);
704-
const output = formatter(ctx, value, recurseTimes, keys);
705-
704+
let output;
705+
// This corresponds to a depth of at least 333 and likely 500.
706+
if (ctx.indentationLvl < 1000) {
707+
output = formatter(ctx, value, recurseTimes, keys);
708+
} else {
709+
try {
710+
output = formatter(ctx, value, recurseTimes, keys);
711+
} catch (err) {
712+
if (errors.isStackOverflowError(err)) {
713+
ctx.seen.pop();
714+
process.emitWarning(
715+
'Inspection reached the maximum call stack size. Incomplete ' +
716+
'inspected object returned.'
717+
);
718+
return ctx.stylize(`[${constructor || tag || 'Object'}]`, 'special');
719+
}
720+
throw err;
721+
}
722+
}
706723
if (extra !== undefined)
707724
output.unshift(extra);
708725

709726
for (var i = 0; i < symbols.length; i++) {
710727
output.push(formatProperty(ctx, value, recurseTimes, symbols[i], 0));
711728
}
729+
712730
ctx.seen.pop();
713731

714732
return reduceToSingleString(ctx, output, base, braces);

test/parallel/test-util-inspect.js

Lines changed: 17 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1410,10 +1410,23 @@ util.inspect(process);
14101410
// Test that a long linked list can be inspected without throwing an error.
14111411
const list = {};
14121412
let head = list;
1413-
// The real cutoff value is closer to 1400 stack frames as of May 2018,
1414-
// but let's be generous here – even a linked listed of length 100k should be
1415-
// inspectable in some way.
1413+
// A linked list of length 100k should be inspectable in some way, even though
1414+
// the real cutoff value is much lower than 100k.
14161415
for (let i = 0; i < 100000; i++)
14171416
head = head.next = {};
1418-
util.inspect(list);
1417+
assert.strictEqual(
1418+
util.inspect(list),
1419+
'{ next: { next: { next: [Object] } } }'
1420+
);
1421+
common.expectWarning({
1422+
Warning: [
1423+
'Inspection reached the maximum call stack size. ' +
1424+
'Incomplete inspected object returned.',
1425+
common.noWarnCode
1426+
]
1427+
});
1428+
const longList = util.inspect(list, { depth: Infinity });
1429+
const match = longList.match(/next/g);
1430+
assert(match.length > 1000 && match.length < 10000);
1431+
assert(longList.includes('[Object]'));
14191432
}

0 commit comments

Comments
 (0)