Skip to content
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

Closed
wants to merge 4 commits into from

Conversation

zhanzhenzhen
Copy link
Contributor

@zhanzhenzhen zhanzhenzhen commented Mar 11, 2018

Fix #19281

I just deleted one e.stack line. There's another e.stack line in DefaultResolve.js, with a comment. Seems that shouldn't be deleted.

Checklist
  • make -j4 test (UNIX), or vcbuild test (Windows) passes
  • tests and/or benchmarks are included
  • documentation is changed or added
  • commit message follows commit guidelines

Copy link
Member

@addaleax addaleax left a 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.

@zhanzhenzhen
Copy link
Contributor Author

Sorry this is the first time I made a pull request to Node, I'm really not familiar with writing these tests.

@addaleax
Copy link
Member

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. :)

@hiroppy
Copy link
Member

hiroppy commented Mar 11, 2018

@zhanzhenzhen
Copy link
Contributor Author

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.

@targos
Copy link
Member

targos commented Mar 11, 2018

@zhanzhenzhen If you don't want that module to be executed as a test, the best is to put it in the fixtures directory.

test/fixtures/es-module-loaders is a good place for it IMO.
Then from the test itself, you can import('../fixtures/es-module-loaders/xxx.mjs').

@zhanzhenzhen
Copy link
Contributor Author

@targos thanks!

Copy link
Member

@benjamingr benjamingr left a 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.

@benjamingr
Copy link
Member

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.

@@ -105,7 +105,6 @@ class ModuleJob {
try {
module.evaluate();
} catch (e) {
e.stack;
Copy link
Member

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.

Copy link
Member

@jdalton jdalton Mar 11, 2018

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.

Copy link
Member

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?

Copy link
Member

@jdalton jdalton Mar 11, 2018

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).

Copy link
Member

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.

Copy link
Member

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.

Copy link
Member

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?

Copy link
Member

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)?

Copy link
Contributor

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).

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@jkrems I did not.

@addaleax addaleax added the esm Issues and PRs related to the ECMAScript Modules implementation. label Mar 11, 2018
import assert from 'assert';

async function doTest() {
try {
Copy link
Member

@devsnek devsnek Mar 11, 2018

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

Copy link
Contributor Author

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.

Copy link
Member

@devsnek devsnek Mar 11, 2018

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

Copy link
Contributor Author

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.

Copy link
Contributor Author

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.

@zhanzhenzhen
Copy link
Contributor Author

Should I change it to if (e) { e.stack; }?

@addaleax
Copy link
Member

Should I change it to if (e) { e.stack; }?

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.

@benjamingr
Copy link
Member

@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).

Copy link
Contributor

@jkrems jkrems left a 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.

@BridgeAR
Copy link
Member

@ljharb
Copy link
Member

ljharb commented Mar 12, 2018

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.

Copy link
Member

@benjamingr benjamingr left a 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

@BridgeAR BridgeAR added the author ready PRs that have at least one approval, no pending requests for changes, and a CI started. label Mar 12, 2018
@MylesBorins MylesBorins added the modules-agenda Issues and PRs to discuss during the meetings of the Modules team. label Mar 12, 2018
@BridgeAR
Copy link
Member

@zhanzhenzhen can you please rebase this? :-)

@BridgeAR BridgeAR removed the author ready PRs that have at least one approval, no pending requests for changes, and a CI started. label Mar 27, 2018
@zhanzhenzhen
Copy link
Contributor Author

@BridgeAR Sure

@zhanzhenzhen
Copy link
Contributor Author

@BridgeAR Rebased

Copy link
Member

@TimothyGu TimothyGu left a 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';

Copy link
Member

@TimothyGu TimothyGu Mar 27, 2018

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.

Copy link
Contributor Author

@zhanzhenzhen zhanzhenzhen Mar 28, 2018

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 :)

Copy link
Contributor Author

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
);
Copy link
Member

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.

Copy link
Contributor Author

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 :)

Copy link
Member

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.

@TimothyGu
Copy link
Member

Note to whoever applies this: add a common.crashOnUnhandledRejection(); line. See #19282 (comment).

@BridgeAR BridgeAR added the author ready PRs that have at least one approval, no pending requests for changes, and a CI started. label Apr 9, 2018
@BridgeAR
Copy link
Member

BridgeAR commented Apr 9, 2018

BridgeAR pushed a commit to BridgeAR/node that referenced this pull request Apr 9, 2018
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>
@BridgeAR
Copy link
Member

BridgeAR commented Apr 9, 2018

Landed in b34a1e1 🎉

@BridgeAR BridgeAR closed this Apr 9, 2018
@targos targos added backport-requested-v9.x and removed author ready PRs that have at least one approval, no pending requests for changes, and a CI started. labels Apr 12, 2018
BridgeAR pushed a commit to BridgeAR/node that referenced this pull request May 1, 2018
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>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
esm Issues and PRs related to the ECMAScript Modules implementation. modules-agenda Issues and PRs to discuss during the meetings of the Modules team.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

ES module loading inconsistent with browser while handling undefined exceptions