summaryrefslogtreecommitdiff
path: root/lib/repl.js
diff options
context:
space:
mode:
authorRuben Bridgewater <ruben@bridgewater.de>2018-05-17 16:25:36 +0200
committerRuben Bridgewater <ruben@bridgewater.de>2019-05-08 08:15:15 +0200
commit422e8f762873aef4a37185f3237c0d666c929d8e (patch)
treee56695393ab0bfe6445a76541502354506916f29 /lib/repl.js
parent9a174db3d869c8d5cde9e09db92d88d0d97e9513 (diff)
downloadandroid-node-v8-422e8f762873aef4a37185f3237c0d666c929d8e.tar.gz
android-node-v8-422e8f762873aef4a37185f3237c0d666c929d8e.tar.bz2
android-node-v8-422e8f762873aef4a37185f3237c0d666c929d8e.zip
repl: handle uncaughtException properly
When running the REPL as standalone program it's now possible to use `process.on('uncaughtException', listener)`. It is going to use those listeners from now on and the regular error output is suppressed. It also fixes the issue that REPL instances started inside of an application would silence all application errors. It is now prohibited to add the exception listener in such REPL instances. Trying to add such listeners throws an `ERR_INVALID_REPL_INPUT` error. Fixes: https://github.com/nodejs/node/issues/19998 PR-URL: https://github.com/nodejs/node/pull/27151 Fixes: https://github.com/nodejs/node/issues/19998 Reviewed-By: Lance Ball <lball@redhat.com> Reviewed-By: Rich Trott <rtrott@gmail.com>
Diffstat (limited to 'lib/repl.js')
-rw-r--r--lib/repl.js59
1 files changed, 47 insertions, 12 deletions
diff --git a/lib/repl.js b/lib/repl.js
index d439d205a4..e4a366664c 100644
--- a/lib/repl.js
+++ b/lib/repl.js
@@ -72,6 +72,7 @@ const {
ERR_CANNOT_WATCH_SIGINT,
ERR_INVALID_ARG_TYPE,
ERR_INVALID_REPL_EVAL_CONFIG,
+ ERR_INVALID_REPL_INPUT,
ERR_SCRIPT_EXECUTION_INTERRUPTED
} = require('internal/errors').codes;
const { sendInspectorCommand } = require('internal/util/inspector');
@@ -101,10 +102,13 @@ let processTopLevelAwait;
const parentModule = module;
const replMap = new WeakMap();
+const domainSet = new WeakSet();
const kBufferedCommandSymbol = Symbol('bufferedCommand');
const kContextId = Symbol('contextId');
+let addedNewListener = false;
+
try {
// Hack for require.resolve("./relative") to work properly.
module.filename = path.resolve('repl');
@@ -204,6 +208,28 @@ function REPLServer(prompt,
throw new ERR_INVALID_REPL_EVAL_CONFIG();
}
+ // Add this listener only once and use a WeakSet that contains the REPLs
+ // domains. Otherwise we'd have to add a single listener to each REPL instance
+ // and that could trigger the `MaxListenersExceededWarning`.
+ if (!options[kStandaloneREPL] && !addedNewListener) {
+ process.prependListener('newListener', (event, listener) => {
+ if (event === 'uncaughtException' &&
+ process.domain &&
+ listener.name !== 'domainUncaughtExceptionClear' &&
+ domainSet.has(process.domain)) {
+ // Throw an error so that the event will not be added and the current
+ // domain takes over. That way the user is notified about the error
+ // and the current code evaluation is stopped, just as any other code
+ // that contains an error.
+ throw new ERR_INVALID_REPL_INPUT(
+ 'Listeners for `uncaughtException` cannot be used in the REPL');
+ }
+ });
+ addedNewListener = true;
+ }
+
+ domainSet.add(this._domain);
+
let rli = this;
Object.defineProperty(this, 'rli', {
get: deprecate(() => rli,
@@ -264,7 +290,7 @@ function REPLServer(prompt,
// statement rather than an object literal. So, we first try
// to wrap it in parentheses, so that it will be interpreted as
// an expression. Note that if the above condition changes,
- // lib/internal/repl/recoverable.js needs to be changed to match.
+ // lib/internal/repl/utils.js needs to be changed to match.
code = `(${code.trim()})\n`;
wrappedCmd = true;
}
@@ -461,22 +487,31 @@ function REPLServer(prompt,
}
}
- if (errStack === '') {
- errStack = `Thrown: ${self.writer(e)}\n`;
- } else {
- const ln = errStack.endsWith('\n') ? '' : '\n';
- errStack = `Thrown:\n${errStack}${ln}`;
- }
-
if (!self.underscoreErrAssigned) {
self.lastError = e;
}
const top = replMap.get(self);
- top.outputStream.write(errStack);
- top.clearBufferedCommand();
- top.lines.level = [];
- top.displayPrompt();
+ if (options[kStandaloneREPL] &&
+ process.listenerCount('uncaughtException') !== 0) {
+ process.nextTick(() => {
+ process.emit('uncaughtException', e);
+ top.clearBufferedCommand();
+ top.lines.level = [];
+ top.displayPrompt();
+ });
+ } else {
+ if (errStack === '') {
+ errStack = `Thrown: ${self.writer(e)}\n`;
+ } else {
+ const ln = errStack.endsWith('\n') ? '' : '\n';
+ errStack = `Thrown:\n${errStack}${ln}`;
+ }
+ top.outputStream.write(errStack);
+ top.clearBufferedCommand();
+ top.lines.level = [];
+ top.displayPrompt();
+ }
});
self.resetContext();