Skip to content

Commit b26506b

Browse files
BridgeARapapirovski
authored andcommitted
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. PR-URL: #20725 Reviewed-By: Anna Henningsen <[email protected]> Reviewed-By: Anatoli Papirovski <[email protected]> Reviewed-By: Tiancheng "Timothy" Gu <[email protected]> Reviewed-By: Matteo Collina <[email protected]> Reviewed-By: Ali Ijaz Sheikh <[email protected]> Reviewed-By: Michael Dawson <[email protected]> Reviewed-By: Daniel Bevenius <[email protected]> Reviewed-By: Sakthipriyan Vairamani <[email protected]> Reviewed-By: Jeremiah Senkpiel <[email protected]> Reviewed-By: Michaël Zasso <[email protected]> Reviewed-By: James M Snell <[email protected]>
1 parent 43a2091 commit b26506b

File tree

3 files changed

+38
-8
lines changed

3 files changed

+38
-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+
return ctx.stylize(
715+
`[${constructor || tag || 'Object'}: Inspection interrupted ` +
716+
'prematurely. Maximum call stack size exceeded.]',
717+
'special'
718+
);
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: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1410,10 +1410,17 @@ 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+
const longList = util.inspect(list, { depth: Infinity });
1422+
const match = longList.match(/next/g);
1423+
assert(match.length > 1000 && match.length < 10000);
1424+
assert(longList.includes('[Object: Inspection interrupted ' +
1425+
'prematurely. Maximum call stack size exceeded.]'));
14191426
}

0 commit comments

Comments
 (0)