-
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
Fragile/buggy implementation of ES module loading #18249
Comments
/cc @nodejs/esm (I just created the team) |
@iamstolis |
We do produce public builds but we do not have a public build of the version based on the lastest stable Node.js (v8.9.4) yet - we've just upgraded to v8.9.4 and found this issue. Anyway, this does not seem to be a problem of our engine. It seems to be a problem of the implementation of ES module loading in Node.js hidden by a bug in V8 (as I tried to explain above). It may as well occur with V8 as soon as issue 6007 is fixed and you updgrade to a version of V8 with the fix. Unfortunately, the specification of promises is complicated and it is hard to argue exactly about order of unchained promise jobs even for rather small examples (despite the fact that the order is defined by the specification). That's why the title of this issue starts with "Fragile/buggy" - even if I am wrong and your loading works even with issue 6007 fixed it still holds that the code is written such that it is hard to argue that it is correct (and I believe that it is not correct as it is described above). That's why I suggest to rewrite it such that there is a clear |
@iamstolis i'm working on a pretty simple fix for this atm (got the idea from my work on vm.Module) i'll let you know how it turns out. (waiting for the long build now 😄) @bmeck if we moved the resolved cache to the js it might make linking a little less hectic, any thoughts on that? |
@iamstolis this patch should make linked wait for all the promises to actually resolve, i think moving forward there's probably a nicer way to fix this though, i'll keep looking at it. diff --git a/lib/internal/loader/ModuleJob.js b/lib/internal/loader/ModuleJob.js
index 8aa1b4b051..2cc83c3273 100644
--- a/lib/internal/loader/ModuleJob.js
+++ b/lib/internal/loader/ModuleJob.js
@@ -31,13 +31,19 @@ class ModuleJob {
({ module: this.module,
reflect: this.reflect } = await this.modulePromise);
assert(this.module instanceof ModuleWrap);
- this.module.link(async (dependencySpecifier) => {
- const dependencyJobPromise =
+ const promises = [];
+ this.module.link((dependencySpecifier) => {
+ const p = (async () => {
+ const dependencyJobPromise =
this.loader.getModuleJob(dependencySpecifier, url);
- dependencyJobs.push(dependencyJobPromise);
- const dependencyJob = await dependencyJobPromise;
- return (await dependencyJob.modulePromise).module;
+ dependencyJobs.push(dependencyJobPromise);
+ const dependencyJob = await dependencyJobPromise;
+ return (await dependencyJob.modulePromise).module;
+ })();
+ promises.push(p);
+ return p;
});
+ await Promise.all(promises);
if (enableDebug) {
// Make sure all dependencies are entered into the list synchronously.
Object.freeze(dependencyJobs); |
@devsnek, thanks for the quick suggestion of a possible patch. It looks correct to me (i.e. it introduces a proper dependency between the problematic promises). I've applied the patch to our fork and I can confirm that all |
@iamstolis Nice find! This will likely help other non-V8 engines as well. |
just to keep people up to date, the current plan i have is to remove ModuleWrap::Link after vm.Module lands, and the new implementation i have will take care of this bug as a side-effect |
Cool!
Update: #18394 landed. |
This commit fixes up some issues in #18394. * Switch vm.Module internals to use the new link method properly * Fix bug with ModuleWrap::Link * Add tests for ModuleWrap::Link PR-URL: #18509 Fixes: #18249 Refs: #18394 Reviewed-By: Tiancheng "Timothy" Gu <[email protected]>
This commit fixes up some issues in nodejs#18394. * Switch vm.Module internals to use the new link method properly * Fix bug with ModuleWrap::Link * Add tests for ModuleWrap::Link PR-URL: nodejs#18509 Fixes: nodejs#18249 Refs: nodejs#18394 Reviewed-By: Tiancheng "Timothy" Gu <[email protected]>
This commit fixes up some issues in #18394. * Switch vm.Module internals to use the new link method properly * Fix bug with ModuleWrap::Link * Add tests for ModuleWrap::Link PR-URL: #18509 Fixes: #18249 Refs: #18394 Reviewed-By: Tiancheng "Timothy" Gu <[email protected]>
This commit fixes up some issues in #18394. * Switch vm.Module internals to use the new link method properly * Fix bug with ModuleWrap::Link * Add tests for ModuleWrap::Link PR-URL: #18509 Fixes: #18249 Refs: #18394 Reviewed-By: Tiancheng "Timothy" Gu <[email protected]>
This commit fixes up some issues in #18394. * Switch vm.Module internals to use the new link method properly * Fix bug with ModuleWrap::Link * Add tests for ModuleWrap::Link PR-URL: #18509 Fixes: #18249 Refs: #18394 Reviewed-By: Tiancheng "Timothy" Gu <[email protected]>
This commit fixes up some issues in nodejs#18394. * Switch vm.Module internals to use the new link method properly * Fix bug with ModuleWrap::Link * Add tests for ModuleWrap::Link PR-URL: nodejs#18509 Fixes: nodejs#18249 Refs: nodejs#18394 Reviewed-By: Tiancheng "Timothy" Gu <[email protected]>
The implementation of module loading depends on subtleties of the order of promise jobs (microtasks) in V8. In fact, I believe that some tests (like
test-esm-namespace
) pass because of issue 6007 in V8 only. I tried to run these tests on our alternative version of Node.js that embeds a different JavaScript engine and some ofes-module
tests failed withThe root of the problem seems to be in
ModuleJob.js
.ModuleWrap::ResolveCallback
assumes/checks that promises that correspond to dependencies are resolved. Unfortunately, the callback (invoked throughmodule.instantiate()
) is triggered whenmoduleJob.linked
promise is resolved. It is not triggered when all the promises created byModuleWrap::Link
are resolved. See the relevant part ofModuleJob.js
:moduleJob.linked
is resolved when alldependencyJobs
are resolved. Unfortunately, the promises created byModuleWrap::Link
do not correspond todependencyJobs
. They correspond to the body of the async function passed tomodule.link
. When the lastdependencyJob
(akadependencyJobPromise
) is resolved thenSafePromise.all(dependencyJob)
(akamoduleJob.linked
) is resolved (andcheckComplete
/module.instantiate
/ResolveCallback
is triggered). This can happen before the whole body of the async function is finished (and the promise checked byModuleWrap::ResolveCallback
is resolved).I think that the code should be rewritten such that there is a proper promise-based dependency (through
Promise.prototype.then
) betweenmodule.install
and the promises used byModuleWrap::ResolveCallback
.The text was updated successfully, but these errors were encountered: