From 97d081709e65b6c702e283330779477124e2c640 Mon Sep 17 00:00:00 2001 From: Rich Trott Date: Tue, 13 Oct 2015 21:39:16 -0700 Subject: [PATCH] lib: avoid REPL exit on completion error If a tab completion is attempted on an undefined reference inside of a function, the REPL was exiting without reporting an error or anything else. This change results in the REPL reporting the ReferenceError and continuing. Fixes: https://github.com/nodejs/node/issues/3346 PR-URL: https://github.com/nodejs/node/pull/3358 Reviewed-By: James M Snell --- lib/repl.js | 15 ++++--- test/parallel/test-repl-tab-complete-crash.js | 41 +++++++++++++++++++ 2 files changed, 51 insertions(+), 5 deletions(-) create mode 100644 test/parallel/test-repl-tab-complete-crash.js diff --git a/lib/repl.js b/lib/repl.js index 81afd27a4dbd00..46288db5639c59 100644 --- a/lib/repl.js +++ b/lib/repl.js @@ -32,6 +32,8 @@ const Console = require('console').Console; const domain = require('domain'); const debug = util.debuglog('repl'); +const replMap = new WeakMap(); + try { // hack for require.resolve("./relative") to work properly. module.filename = path.resolve('repl'); @@ -189,11 +191,12 @@ function REPLServer(prompt, self._domain.on('error', function(e) { debug('domain error'); - self.outputStream.write((e.stack || e) + '\n'); - self._currentStringLiteral = null; - self.bufferedCommand = ''; - self.lines.level = []; - self.displayPrompt(); + const top = replMap.get(self); + top.outputStream.write((e.stack || e) + '\n'); + top._currentStringLiteral = null; + top.bufferedCommand = ''; + top.lines.level = []; + top.displayPrompt(); }); if (!input && !output) { @@ -472,6 +475,7 @@ exports.start = function(prompt, ignoreUndefined, replMode); if (!exports.repl) exports.repl = repl; + replMap.set(repl, repl); return repl; }; @@ -601,6 +605,7 @@ REPLServer.prototype.complete = function(line, callback) { // all this is only profitable if the nested REPL // does not have a bufferedCommand if (!magic.bufferedCommand) { + replMap.set(magic, replMap.get(this)); return magic.complete(line, callback); } } diff --git a/test/parallel/test-repl-tab-complete-crash.js b/test/parallel/test-repl-tab-complete-crash.js new file mode 100644 index 00000000000000..85ab0577eb8601 --- /dev/null +++ b/test/parallel/test-repl-tab-complete-crash.js @@ -0,0 +1,41 @@ +'use strict'; + +require('../common'); +const assert = require('assert'); +const util = require('util'); +const repl = require('repl'); + +var referenceErrorCount = 0; + +// A stream to push an array into a REPL +function ArrayStream() { + this.run = function(data) { + const self = this; + data.forEach(function(line) { + self.emit('data', line + '\n'); + }); + }; +} +util.inherits(ArrayStream, require('stream').Stream); +ArrayStream.prototype.readable = true; +ArrayStream.prototype.writable = true; +ArrayStream.prototype.resume = function() {}; +ArrayStream.prototype.write = function(msg) { + if (msg.startsWith('ReferenceError: ')) { + referenceErrorCount++; + } +}; + +const putIn = new ArrayStream(); +const testMe = repl.start('', putIn); + +// https://github.com/nodejs/node/issues/3346 +// Tab-completion for an undefined variable inside a function should report a +// ReferenceError. +putIn.run(['.clear']); +putIn.run(['function () {']); +testMe.complete('arguments.'); + +process.on('exit', function() { + assert.strictEqual(referenceErrorCount, 1); +});