Skip to content

Commit

Permalink
util: recover from maximum call stack size
Browse files Browse the repository at this point in the history
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]>
  • Loading branch information
BridgeAR authored and targos committed Jun 25, 2018
1 parent ba8ee17 commit 7cc979d
Show file tree
Hide file tree
Showing 3 changed files with 38 additions and 8 deletions.
9 changes: 7 additions & 2 deletions doc/api/util.md
Original file line number Diff line number Diff line change
Expand Up @@ -360,6 +360,10 @@ stream.write('With ES6');
<!-- YAML
added: v0.3.0
changes:
- version: REPLACEME
pr-url: https://github.com/nodejs/node/pull/REPLACEME
description: Inspecting linked lists and similar objects is now possible
up to the maximum call stack size.
- version: v10.0.0
pr-url: https://github.com/nodejs/node/pull/19259
description: The `WeakMap` and `WeakSet` entries can now be inspected.
Expand Down Expand Up @@ -387,8 +391,9 @@ changes:
properties will be included in the formatted result as well as [`WeakMap`][]
and [`WeakSet`][] entries. **Default:** `false`.
* `depth` {number} Specifies the number of times to recurse while formatting
the `object`. This is useful for inspecting large complicated objects.
To make it recurse indefinitely pass `null`. **Default:** `2`.
the `object`. This is useful for inspecting large complicated objects. To
make it recurse up to the maximum call stack size pass `Infinity` or `null`.
**Default:** `2`.
* `colors` {boolean} If `true`, the output will be styled with ANSI color
codes. Colors are customizable, see [Customizing `util.inspect` colors][].
**Default:** `false`.
Expand Down
22 changes: 20 additions & 2 deletions lib/util.js
Original file line number Diff line number Diff line change
Expand Up @@ -711,14 +711,32 @@ function formatValue(ctx, value, recurseTimes) {
}

ctx.seen.push(value);
const output = formatter(ctx, value, recurseTimes, keys);

let output;
// This corresponds to a depth of at least 333 and likely 500.
if (ctx.indentationLvl < 1000) {
output = formatter(ctx, value, recurseTimes, keys);
} else {
try {
output = formatter(ctx, value, recurseTimes, keys);
} catch (err) {
if (errors.isStackOverflowError(err)) {
ctx.seen.pop();
return ctx.stylize(
`[${constructor || tag || 'Object'}: Inspection interrupted ` +
'prematurely. Maximum call stack size exceeded.]',
'special'
);
}
throw err;
}
}
if (extra !== undefined)
output.unshift(extra);

for (var i = 0; i < symbols.length; i++) {
output.push(formatProperty(ctx, value, recurseTimes, symbols[i], 0));
}

ctx.seen.pop();

return reduceToSingleString(ctx, output, base, braces);
Expand Down
15 changes: 11 additions & 4 deletions test/parallel/test-util-inspect.js
Original file line number Diff line number Diff line change
Expand Up @@ -1459,10 +1459,17 @@ util.inspect(process);
// Test that a long linked list can be inspected without throwing an error.
const list = {};
let head = list;
// The real cutoff value is closer to 1400 stack frames as of May 2018,
// but let's be generous here – even a linked listed of length 100k should be
// inspectable in some way.
// A linked list of length 100k should be inspectable in some way, even though
// the real cutoff value is much lower than 100k.
for (let i = 0; i < 100000; i++)
head = head.next = {};
util.inspect(list);
assert.strictEqual(
util.inspect(list),
'{ next: { next: { next: [Object] } } }'
);
const longList = util.inspect(list, { depth: Infinity });
const match = longList.match(/next/g);
assert(match.length > 1000 && match.length < 10000);
assert(longList.includes('[Object: Inspection interrupted ' +
'prematurely. Maximum call stack size exceeded.]'));
}

0 comments on commit 7cc979d

Please sign in to comment.