-
Notifications
You must be signed in to change notification settings - Fork 29.6k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
repl: properly handle uncaughtException #27151
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,44 @@ | ||
'use strict'; | ||
|
||
// This verifies that adding an `uncaughtException` listener in an REPL instance | ||
// does not suppress errors in the whole application. Adding such listener | ||
// should throw. | ||
|
||
require('../common'); | ||
const ArrayStream = require('../common/arraystream'); | ||
const repl = require('repl'); | ||
const assert = require('assert'); | ||
|
||
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( | ||
'process.nextTick(() => {\n' + | ||
' process.on("uncaughtException", () => console.log("Foo"));\n' + | ||
' throw new TypeError("foobar");\n' + | ||
'});\n' | ||
); | ||
r.write( | ||
'setTimeout(() => {\n' + | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. If you go with |
||
' throw new RangeError("abc");\n' + | ||
'}, 1);console.log()\n' | ||
); | ||
r.close(); | ||
|
||
setTimeout(() => { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Any reason not to use There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I wanted to verify the timing. Using |
||
const len = process.listenerCount('uncaughtException'); | ||
process.removeAllListeners('uncaughtException'); | ||
assert.strictEqual(len, 0); | ||
assert(/ERR_INVALID_REPL_INPUT.*(?!Type)RangeError: abc/s.test(accum)); | ||
}, 2); | ||
BridgeAR marked this conversation as resolved.
Show resolved
Hide resolved
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,38 @@ | ||
'use strict'; | ||
const common = require('../common'); | ||
const assert = require('assert'); | ||
const cp = require('child_process'); | ||
const child = cp.spawn(process.execPath, ['-i']); | ||
let output = ''; | ||
|
||
child.stdout.setEncoding('utf8'); | ||
child.stdout.on('data', (data) => { | ||
output += data; | ||
}); | ||
|
||
child.on('exit', common.mustCall(() => { | ||
const results = output.split('\n'); | ||
results.shift(); | ||
assert.deepStrictEqual( | ||
results, | ||
[ | ||
'Type ".help" for more information.', | ||
// x\n | ||
'> Thrown:', | ||
'ReferenceError: x is not defined', | ||
// Added `uncaughtException` listener. | ||
'> short', | ||
'undefined', | ||
// x\n | ||
'> Foobar', | ||
'> ' | ||
] | ||
); | ||
})); | ||
|
||
child.stdin.write('x\n'); | ||
child.stdin.write( | ||
'process.on("uncaughtException", () => console.log("Foobar"));' + | ||
'console.log("short")\n'); | ||
child.stdin.write('x\n'); | ||
child.stdin.end(); |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,72 @@ | ||
'use strict'; | ||
require('../common'); | ||
const ArrayStream = require('../common/arraystream'); | ||
const assert = require('assert'); | ||
const repl = require('repl'); | ||
|
||
let count = 0; | ||
|
||
function run({ command, expected }) { | ||
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 | ||
}); | ||
|
||
r.write(`${command}\n`); | ||
if (typeof expected === 'string') { | ||
assert.strictEqual(accum, expected); | ||
} else { | ||
assert(expected.test(accum), accum); | ||
} | ||
|
||
// Verify that the repl is still working as expected. | ||
accum = ''; | ||
r.write('1 + 1\n'); | ||
assert.strictEqual(accum, '2\n'); | ||
r.close(); | ||
count++; | ||
} | ||
|
||
const tests = [ | ||
{ | ||
command: 'x', | ||
expected: 'Thrown:\n' + | ||
'ReferenceError: x is not defined\n' | ||
}, | ||
{ | ||
command: 'process.on("uncaughtException", () => console.log("Foobar"));\n', | ||
expected: /^Thrown:\nTypeError \[ERR_INVALID_REPL_INPUT]: Listeners for `/ | ||
}, | ||
{ | ||
command: 'x;\n', | ||
expected: 'Thrown:\n' + | ||
'ReferenceError: x is not defined\n' | ||
}, | ||
{ | ||
command: 'process.on("uncaughtException", () => console.log("Foobar"));' + | ||
'console.log("Baz");\n', | ||
expected: /^Thrown:\nTypeError \[ERR_INVALID_REPL_INPUT]: Listeners for `/ | ||
}, | ||
{ | ||
command: 'console.log("Baz");' + | ||
'process.on("uncaughtException", () => console.log("Foobar"));\n', | ||
expected: /^Baz\nThrown:\nTypeError \[ERR_INVALID_REPL_INPUT]:.*uncaughtException/ | ||
} | ||
]; | ||
|
||
process.on('exit', () => { | ||
// To actually verify that the test passed we have to make sure no | ||
// `uncaughtException` listeners exist anymore. | ||
process.removeAllListeners('uncaughtException'); | ||
assert.strictEqual(count, tests.length); | ||
}); | ||
|
||
tests.forEach(run); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nit: Instead of
maybe this?:
(Also, does it really?)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Or maybe this?:
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Two things are prohibited in the REPL:
uncaughtException
listeners in case the REPL is not running as standalone program andprocess.setUncaughtExceptionCaptureCallback()
. Both of these cases are documented. I think it's not possible to use some module features but that's just a technical limitation so far.