From 6ead99aa73cc9df6137861a138e87e850a473f83 Mon Sep 17 00:00:00 2001 From: Dan Kaplun Date: Sun, 18 Mar 2018 00:36:53 -0400 Subject: [PATCH] console: don't swallow call stack exceeded errors Fixes test/parallel/test-console-no-swallow-stack-exceeded.js PR-URL: https://github.com/nodejs/node/pull/19423 Reviewed-By: Anna Henningsen Reviewed-By: Ruben Bridgewater Reviewed-By: Colin Ihrig --- lib/console.js | 17 +++++--------- lib/internal/errors.js | 22 +++++++++++++++++++ .../test-console-no-swallow-stack-overflow.js | 18 +++++++++++++++ 3 files changed, 45 insertions(+), 12 deletions(-) create mode 100644 test/parallel/test-console-no-swallow-stack-overflow.js diff --git a/lib/console.js b/lib/console.js index ad6276297f26f7..c89ab098d40a5e 100644 --- a/lib/console.js +++ b/lib/console.js @@ -21,15 +21,16 @@ 'use strict'; -const { ERR_CONSOLE_WRITABLE_STREAM } = require('internal/errors').codes; +const { + isStackOverflowError, + codes: { ERR_CONSOLE_WRITABLE_STREAM }, +} = require('internal/errors'); const util = require('util'); const kCounts = Symbol('counts'); // Track amount of indentation required via `console.group()`. const kGroupIndent = Symbol('groupIndent'); -let MAX_STACK_MESSAGE; - function Console(stdout, stderr, ignoreErrors = true) { if (!(this instanceof Console)) { return new Console(stdout, stderr, ignoreErrors); @@ -113,17 +114,9 @@ function write(ignoreErrors, stream, string, errorhandler, groupIndent) { stream.write(string, errorhandler); } catch (e) { - if (MAX_STACK_MESSAGE === undefined) { - try { - // eslint-disable-next-line no-unused-vars - function a() { a(); } - } catch (err) { - MAX_STACK_MESSAGE = err.message; - } - } // console is a debugging utility, so it swallowing errors is not desirable // even in edge cases such as low stack space. - if (e.message === MAX_STACK_MESSAGE && e.name === 'RangeError') + if (isStackOverflowError(e)) throw e; // Sorry, there's no proper way to pass along the error here. } finally { diff --git a/lib/internal/errors.js b/lib/internal/errors.js index d08b1bfad156c0..833246c8a5ff66 100644 --- a/lib/internal/errors.js +++ b/lib/internal/errors.js @@ -445,10 +445,32 @@ function dnsException(err, syscall, hostname) { return ex; } +let MAX_STACK_MESSAGE; +/** + * Returns true if `err` is a `RangeError` with an engine-specific message. + * "Maximum call stack size exceeded" in V8. + * + * @param {Error} err + * @returns {boolean} + */ +function isStackOverflowError(err) { + if (MAX_STACK_MESSAGE === undefined) { + try { + function overflowStack() { overflowStack(); } + overflowStack(); + } catch (err) { + MAX_STACK_MESSAGE = err.message; + } + } + + return err.name === 'RangeError' && err.message === MAX_STACK_MESSAGE; +} + module.exports = exports = { dnsException, errnoException, exceptionWithHostPort, + isStackOverflowError, message, Error: makeNodeError(Error), TypeError: makeNodeError(TypeError), diff --git a/test/parallel/test-console-no-swallow-stack-overflow.js b/test/parallel/test-console-no-swallow-stack-overflow.js new file mode 100644 index 00000000000000..f36ba4857e363b --- /dev/null +++ b/test/parallel/test-console-no-swallow-stack-overflow.js @@ -0,0 +1,18 @@ +'use strict'; +const common = require('../common'); +const { Console } = require('console'); +const { Writable } = require('stream'); + +for (const method of ['dir', 'log', 'warn']) { + common.expectsError(() => { + const out = new Writable({ + write: common.mustCall(function write(...args) { + // Exceeds call stack. + return write(...args); + }), + }); + const c = new Console(out, out, true); + + c[method]('Hello, world!'); + }, { name: 'RangeError' }); +}