-
Notifications
You must be signed in to change notification settings - Fork 30.5k
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
module: fix e.stack
error when throwing undefined or null
#19282
Conversation
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.
Looks good to me!
Could you add a regression test for this? I think test/es-module
would be the right place for it.
Sorry this is the first time I made a pull request to Node, I'm really not familiar with writing these tests. |
If you want, there’s a guide at https://github.com/nodejs/node/blob/master/doc/guides/writing-tests.md – but I wouldn’t consider it a blocker for this PR. :) |
I'm writing tests now. I want one module to throw an undefined exception. How to make this module itself not tested? // Flags: --experimental-modules
/* eslint-disable node-core/required-modules */
throw undefined; I just want another module that imports this module tested. |
@zhanzhenzhen If you don't want that module to be executed as a test, the best is to put it in the
|
@targos thanks! |
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 for your contribution!
Blocking until the modules team has had time to review this.
That is, I think the change is correct but I also think that the original behavior was intentional (probably?) so I want to understand why it was done before we change it. |
lib/internal/loader/ModuleJob.js
Outdated
@@ -105,7 +105,6 @@ class ModuleJob { | |||
try { | |||
module.evaluate(); | |||
} catch (e) { | |||
e.stack; |
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 believe if this is an error object, accessing the stack property ensures that the trace is generated?
You could change this to if (e) { e.stack; }
to ensure that path is still followed.
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 believe if this is an error object, accessing the stack property ensures that the trace is generated?
Yes.
You could change this to
if (e) { e.stack; }
to ensure that path is still followed.
Node has an isError
helper. Since e
could be any thrown value and .stack
could be any property value (even a getter) it's probably better to at least have an error check just to be on the up and up. Similar usage here.
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.
Can someone explain why it's important to generate the stack trace in advance?
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.
Can someone explain why it's important to generate the stack trace in advance?
Could be to generate the stack before being re-thrown and adding to the stack.
In many cases stacks can be snipped OK with Error.captureStackTrace(error, beforeFunc)
.
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.
Could be to generate the stack before being re-thrown and adding to the stack.
I'm not aware that re-throwing modifies the stack. Do you have an example?
There's an old V8 bug about it: https://bugs.chromium.org/p/chromium/issues/detail?id=60240. But it was fixed in 2012.
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 was able to repro this a while back but cannot now. +1 to removal, but now curious about why it was showing up while working on initial loader.
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.
@bmeck what version of node (and thus v8) were you using when working on it initially?
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.
muddy to see the actual commits due to rebases, but guessing from timeline and other stuff in my fork it was probably around V8 5.4 (which was still from 2016)?
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.
@bmeck Did you test the syntax error case? IIRC that's the one with bad/missing stacks (throw
and Promise.reject
should both ensure stacks already).
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.
@jkrems I did not.
import assert from 'assert'; | ||
|
||
async function doTest() { | ||
try { |
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.
you can use assert.rejects
here instead of a try/catch
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.
@devsnek OK. BTW, I can't find any async
and await
in existing es-module tests. I don't know if they can be used.
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.
async/await is safe to use. you can test your tests with python tools/test.py path/to/your/test
e.g. python tools/test.py test/es-module/test-esm-throw-undefined.mjs
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.
@devsnek I find in this case I can't use assert.rejects
. It has the same problem as assert.throws
, because when it comes to undefined
or null
, many results can be considered OK. See:
assert.rejects(async () => {throw Error;}, null)
assert.rejects(async () => {throw Error;}, undefined)
assert.rejects(async () => {throw undefined;}, null)
assert.rejects(async () => {throw null;}, undefined)
All of these 4 are considered as "rejected". So it will make the test too loose.
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.
@devsnek Sorry just aware that I can use function e => e === undefined
as the second parameter. Will modify test.
Should I change it to |
I would keep the line removed entirely unless somebody comes up with a non-artificial example for how this would make a difference for user code. |
@zhanzhenzhen this is your first contribution (thank you!) so I'll elabtorate a little. Basically, pull requests in Node.js take 48 hours (or 72h in weekends as is the case here) before landing - collaborators are given a chance to respond in the meantime. We're hoping @bmeck or another developer knows why this code is there (we have an educated guess but no "proof" yet). If after 72h we don't have any objections we'll land it (and I'll remove my "request changes" for the clarification). |
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'm in favor of removing the line entirely unless somebody has a regression test showing the need (and then we can add something back together with the test). It's in experimental code so I don't think we have to be too conservative about landing a change like this.
I think that removing it without understanding why it’s there is probably unwise; the root of this issue is a nullish e - the if would address that, rather than risking expanding the scope of this PR. |
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.
After clarification about e.stack
I'm +1 and changes LGTM
@zhanzhenzhen can you please rebase this? :-) |
@BridgeAR Sure |
@BridgeAR Rebased |
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.
Left two very minor comments. Otherwise, LGTM.
/* eslint-disable node-core/required-modules */ | ||
import common from '../common/index.js'; | ||
import assert from 'assert'; | ||
|
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: add a common.crashOnUnhandledRejection();
line here, and remove the .then(common.mustCall())
later.
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.
@TimothyGu Thank you. As this is the first time I make a pull request I'm not very familiar with the these functions. You can add it later :)
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.
@TimothyGu I added.
await import('../fixtures/es-module-loaders/throw-undefined'); | ||
}, | ||
(e) => e === undefined | ||
); |
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:
await assert.rejects(
import('../fixtures/es-module-loaders/throw-undefined'),
(e) => e === undefined);
The extra arrow function doesn't seem needed.
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 just followed the example here:
https://github.com/nodejs/node/blob/master/doc/api/assert.md
You mean, the first parameter can be a promise (not a function)? I don't know, but I think mine is more formal :)
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.
Oh oops, never mind that.
Note to whoever applies this: add a |
Adds a test for module loading when throwing undefined or null. PR-URL: nodejs#19282 Reviewed-By: Anna Henningsen <anna@addaleax.net> Reviewed-By: Michaël Zasso <targos@protonmail.com> Reviewed-By: Benjamin Gruenbaum <benjamingr@gmail.com> Reviewed-By: Colin Ihrig <cjihrig@gmail.com> Reviewed-By: Jan Krems <jan.krems@gmail.com> Reviewed-By: Ruben Bridgewater <ruben@bridgewater.de> Reviewed-By: Tiancheng "Timothy" Gu <timothygu99@gmail.com>
Landed in b34a1e1 🎉 |
Adds a test for module loading when throwing undefined or null. PR-URL: nodejs#19282 Reviewed-By: Anna Henningsen <anna@addaleax.net> Reviewed-By: Michaël Zasso <targos@protonmail.com> Reviewed-By: Benjamin Gruenbaum <benjamingr@gmail.com> Reviewed-By: Colin Ihrig <cjihrig@gmail.com> Reviewed-By: Jan Krems <jan.krems@gmail.com> Reviewed-By: Ruben Bridgewater <ruben@bridgewater.de> Reviewed-By: Tiancheng "Timothy" Gu <timothygu99@gmail.com>
Fix #19281
I just deleted one
e.stack
line. There's anothere.stack
line in DefaultResolve.js, with a comment. Seems that shouldn't be deleted.Checklist
make -j4 test
(UNIX), orvcbuild test
(Windows) passes