From e1aa066fe16e5feb2d5621ce0d2163703da1110c Mon Sep 17 00:00:00 2001 From: Yazhong Liu Date: Thu, 8 May 2014 00:05:39 +0800 Subject: [PATCH] readline: fix close event of readline.Interface() Not removing 'end' listeners for input and output on the 'close' event resulted in an EventEmitter related memory leak. This issue also might be reproduced at: https://github.com/npm/npm/issues/5203 Signed-off-by: Trevor Norris --- lib/readline.js | 14 +++++++++----- test/simple/test-readline-interface.js | 21 +++++++++++++++++++++ 2 files changed, 30 insertions(+), 5 deletions(-) diff --git a/lib/readline.js b/lib/readline.js index 1fa217c..19ef759 100644 --- a/lib/readline.js +++ b/lib/readline.js @@ -97,6 +97,13 @@ function Interface(input, output, completer, terminal) { self.close(); } + function ontermend() { + if (util.isString(self.line) && self.line.length > 0) { + self.emit('line', self.line); + } + self.close(); + } + function onkeypress(s, key) { self._ttyWrite(s, key); } @@ -121,11 +128,7 @@ function Interface(input, output, completer, terminal) { // input usually refers to stdin input.on('keypress', onkeypress); - input.on('end', function inputEnd() { - if (util.isString(self.line) && self.line.length > 0) - self.emit('line', self.line); - self.close(); - }); + input.on('end', ontermend); // Current line this.line = ''; @@ -142,6 +145,7 @@ function Interface(input, output, completer, terminal) { output.on('resize', onresize); self.once('close', function() { input.removeListener('keypress', onkeypress); + input.removeListener('end', ontermend); output.removeListener('resize', onresize); }); } diff --git a/test/simple/test-readline-interface.js b/test/simple/test-readline-interface.js index 930cf22..abd3681 100644 --- a/test/simple/test-readline-interface.js +++ b/test/simple/test-readline-interface.js @@ -35,6 +35,14 @@ FakeInput.prototype.pause = function() {}; FakeInput.prototype.write = function() {}; FakeInput.prototype.end = function() {}; +function isWarned(emitter) { + for (var name in emitter) { + var listeners = emitter[name]; + if (listeners.warned) return true; + } + return false; +} + [ true, false ].forEach(function(terminal) { var fi; var rli; @@ -262,4 +270,17 @@ FakeInput.prototype.end = function() {}; assert.equal(readline.getStringWidth('> '), 2); assert.deepEqual(fi.listeners(terminal ? 'keypress' : 'data'), []); + + // check EventEmitter memory leak + for (var i=0; i<12; i++) { + var rl = readline.createInterface({ + input: process.stdin, + output: process.stdout + }); + rl.close(); + assert.equal(isWarned(process.stdin._events), false); + assert.equal(isWarned(process.stdout._events), false); + } + }); + -- 2.7.4