From 1aaa6b72e775d71927701b56e3186ce007337c2b Mon Sep 17 00:00:00 2001 From: Ruben Bridgewater Date: Sun, 7 Apr 2019 15:36:11 +0200 Subject: [PATCH 1/3] test: add known issue Adding an `uncaughtException` listener in an REPL instance suppresses errors in the whole application. Closing the instance won't remove those listeners either. --- test/known_issues/known_issues.status | 1 + .../test-repl-uncaught-exception-error.js | 39 +++++++++++++++++++ 2 files changed, 40 insertions(+) create mode 100644 test/known_issues/test-repl-uncaught-exception-error.js diff --git a/test/known_issues/known_issues.status b/test/known_issues/known_issues.status index d7e0b546d43ff8..8da183080382f5 100644 --- a/test/known_issues/known_issues.status +++ b/test/known_issues/known_issues.status @@ -5,6 +5,7 @@ prefix known_issues # sample-test : SKIP [true] # This section applies to all platforms +test-repl-uncaught-exception-error: SKIP [$system==win32] test-fs-copyfile-respect-permissions: SKIP diff --git a/test/known_issues/test-repl-uncaught-exception-error.js b/test/known_issues/test-repl-uncaught-exception-error.js new file mode 100644 index 00000000000000..d496e156640df5 --- /dev/null +++ b/test/known_issues/test-repl-uncaught-exception-error.js @@ -0,0 +1,39 @@ +'use strict'; + +require('../common'); +const ArrayStream = require('../common/arraystream'); +const repl = require('repl'); + +// Adding an `uncaughtException` listener in an REPL instance suppresses errors +// in the whole application. +// Closing the instance won't remove those listeners either. + +let accum = ''; + +const output = new ArrayStream(); +output.write = (data) => accum += data.replace('\r', ''); + +const r = repl.start({ + prompt: '', + input: new ArrayStream(), + output, + terminal: false, + useColors: false, + global: false +}); + +r.write( + 'setTimeout(() => {\n' + + ' process.on("uncaughtException", () => console.log("Foo"));\n' + + '}, 1);\n' +); + +// Event listeners added to the global `process` won't be removed after +// closing the REPL instance! Those should be removed again, especially since +// the REPL's `global` option is set to `false`. +r.close(); + +setTimeout(() => { + // This should definitely not be silenced! + throw new Error('HU'); +}, 2); From 88306b21d1c9d87cc233a621534017f34dc349e3 Mon Sep 17 00:00:00 2001 From: Ruben Bridgewater Date: Thu, 17 May 2018 16:25:36 +0200 Subject: [PATCH 2/3] repl: emit uncaughtException The internal default repl will from now on trigger `uncaughtException` handlers instead of ignoring them. The regular error output is suppressed in that case. --- doc/api/errors.md | 11 ++- doc/api/repl.md | 34 ++++++++- lib/internal/errors.js | 1 + lib/repl.js | 71 +++++++++++++++---- .../parallel/test-repl-pretty-custom-stack.js | 3 - ...test-repl-uncaught-exception-standalone.js | 33 +++++++++ test/parallel/test-repl-uncaught-exception.js | 60 ++++++++++++++++ 7 files changed, 194 insertions(+), 19 deletions(-) create mode 100644 test/parallel/test-repl-uncaught-exception-standalone.js create mode 100644 test/parallel/test-repl-uncaught-exception.js diff --git a/doc/api/errors.md b/doc/api/errors.md index 547d1ff8d23609..0f84af35b87e8a 100644 --- a/doc/api/errors.md +++ b/doc/api/errors.md @@ -1308,8 +1308,14 @@ An invalid `options.protocol` was passed. ### ERR_INVALID_REPL_EVAL_CONFIG -Both `breakEvalOnSigint` and `eval` options were set in the REPL config, which -is not supported. +Both `breakEvalOnSigint` and `eval` options were set in the [`REPL`][] config, +which is not supported. + + +### ERR_INVALID_REPL_INPUT + +The input can or may not be used in the [`REPL`][]. All prohibited inputs are +documented in the [`REPL`][]'s documentation. ### ERR_INVALID_RETURN_PROPERTY @@ -2293,6 +2299,7 @@ such as `process.stdout.on('data')`. [`Class: assert.AssertionError`]: assert.html#assert_class_assert_assertionerror [`ERR_INVALID_ARG_TYPE`]: #ERR_INVALID_ARG_TYPE [`EventEmitter`]: events.html#events_class_eventemitter +[`REPL`]: repl.html [`Writable`]: stream.html#stream_class_stream_writable [`child_process`]: child_process.html [`cipher.getAuthTag()`]: crypto.html#crypto_cipher_getauthtag diff --git a/doc/api/repl.md b/doc/api/repl.md index cc7f3f96da15d7..10f281a2b673d7 100644 --- a/doc/api/repl.md +++ b/doc/api/repl.md @@ -138,16 +138,47 @@ global or scoped variable, the input `fs` will be evaluated on-demand as ``` #### Global Uncaught Exceptions + The REPL uses the [`domain`][] module to catch all uncaught exceptions for that REPL session. This use of the [`domain`][] module in the REPL has these side effects: -* Uncaught exceptions do not emit the [`'uncaughtException'`][] event. +* Uncaught exceptions only emit the [`'uncaughtException'`][] event if the + `repl` is used as standalone program. If the `repl` is included anywhere in + another application, adding this event synchronous will throw an + [`ERR_INVALID_REPL_INPUT`][] error! * Trying to use [`process.setUncaughtExceptionCaptureCallback()`][] throws an [`ERR_DOMAIN_CANNOT_SET_UNCAUGHT_EXCEPTION_CAPTURE`][] error. +As standalone program: + +```js +process.on('uncaughtException', () => console.log('Uncaught')); + +throw new Error('foobar'); +// Uncaught +``` + +When used in another application: + +```js +process.on('uncaughtException', () => console.log('Uncaught')); +// TypeError [ERR_INVALID_REPL_INPUT]: Unhandled exception listeners can not be +// used in the REPL + +throw new Error('foobar'); +// Thrown: +// Error: foobar +``` + #### Assignment of the `_` (underscore) variable