-
Notifications
You must be signed in to change notification settings - Fork 30.2k
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: handle unexpected error objects #12400
Conversation
} | ||
top.outputStream.write((e.stack || e) + '\n'); | ||
|
||
top.outputStream.write(out + '\n'); |
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.
It will fail if e
is a Symbol
, won't it?
@@ -282,17 +282,24 @@ function REPLServer(prompt, | |||
self._domain.on('error', function debugDomainError(e) { | |||
debug('domain error'); | |||
const top = replMap.get(self); | |||
var out; |
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.
let
?
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.
We haven't moved over to let
completely yet.
lib/repl.js
Outdated
|
||
if (!((e instanceof Error) && typeof e.stack === 'string')) { | ||
out = e; | ||
} else if (e instanceof SyntaxError) { |
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.
if there a chance e.stack != true || !('replace' in e.stack)
?
Maybe a custom error:
function MySillyError(a, b, c) { console.log(a) }
MySillyError.prototype = SyntaxError
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.
I don't think we need to jump through hoops for extreme edge cases like this. As long as they don't crash.
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.
Cool, just tested, no crash just TypeError: Function.prototype.toString is not generic
} | ||
top.outputStream.write((e.stack || e) + '\n'); | ||
|
||
top.outputStream.write(out + '\n'); |
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.
how about if delete e.toString
?
test/parallel/test-repl.js
Outdated
client: client_unix, | ||
send: 'var e = new Error(\'test error\'); delete e.stack; throw e;', | ||
expect: /^Error: test error/ | ||
}, |
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.
add this monstrosity:
send: 'function MySillyError() {}; MySillyError.prototype = SyntaxError; var e = new MySillyError(); throw e',
expect: /^TypeError: Function.prototype.toString is not generic/
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.
No crash, but INHO it's a good test
also @aqrln's
send: 'throw Symbol.for',
expect: /^function for() { [native code] }/
@cjihrig you're not a first time contributor 😁 kudos on the commitment... I added some horrible code to try and crash your cases... |
lib/repl.js
Outdated
if (e instanceof SyntaxError && e.stack) { | ||
|
||
if (!((e instanceof Error) && typeof e.stack === 'string')) { | ||
out = e; |
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.
Symbols and other bad inputs are taken care of here if I move to out = util.inspect(e);
. However, it causes test-repl-sigint.js
and test-repl-sigint-nested-eval.js
to fail. @addaleax any idea why that would be?
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.
that’s odd… I’ll have a look
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.
@addaleax I just pushed up the changes that include that and more tests, if you want to take a look.
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.
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.
Anyway I would "de-morgan" it to:
(!(e instanceof Error) || typeof e.stack !== 'string')
then add the original gourd🎃 guard
(typeof e === 'undefined' || !(e instanceof Error) || typeof e.stack !== 'string')
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.
Thanks @addaleax. The special snowflake that is the REPL. I added a length check for stack
, so errors with empty stacks will print with util.inspect()
.
mo' tests less trouble?! |
You could add the extreme edge cases as tests send: 'function MySillyError() {}; MySillyError.prototype = SyntaxError; var e = new MySillyError(); throw e',
expect: /^TypeError: Function.prototype.toString is not generic/ also @aqrln's send: 'throw Symbol.for',
expect: /^function for() { [native code] }/ |
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.
Changes LGTM and improve current status quo.
This commit allows the repl's domain error handler to process unexpected error formats, such as primitives.
// Throws Object with bad toString() method and prints out | ||
{ | ||
client: client_unix, | ||
send: 'var e = { toString() { throw new Error(\'test\'); } }; throw e;', |
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.
Even smarter! 👍
if (e instanceof SyntaxError && e.stack) { | ||
|
||
if (!((e instanceof Error) && typeof e.stack === 'string' && | ||
e.stack.length > 0)) { |
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.
Not sure how robust you want this code to be, but the typeof e.stack
will crash with
function FakeError() {}
FakeError.prototype = Object.create(Error.prototype, {
stack: { get() { throw new Error(); } }
});
var e = new FakeError();
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.
I don't know how others feel, but I'm OK with it, at least in the context of this PR. It seems like any Node application can be crashed with a well crafted getter.
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.
One thing that I would (personally) prefer is either using V8’s IsNativeError
over instanceof Error
or going full 🦆 here. Both would increase robustness in different ways :)
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.
Just noting that whatever is done here would need to factor in compatibility with internal/errors.js
Refs: nodejs#12400 PR-URL: nodejs#12546 Reviewed-By: Anna Henningsen <anna@addaleax.net> Reviewed-By: Sakthipriyan Vairamani <thechargingvolcano@gmail.com> Reviewed-By: James M Snell <jasnell@gmail.com>
@cjihrig Did you mean to close this? |
Sorry @addaleax. Yes, I did. The move to |
Can you elaborate? The Node.js error classes all extend from the native error classes, and generally V8's methods recognize subclasses. |
Hm, good question. I tested this before, but maybe I did something wrong. |
This commit allows the repl's domain error handler to process
unexpected error formats, such as primitives.
Fixes: #12373
Checklist
make -j4 test
(UNIX), orvcbuild test
(Windows) passesAffected core subsystem(s)
repl