-
Notifications
You must be signed in to change notification settings - Fork 55
JSC: evaluate dynamically imported modules under the importer's async context #274
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
base: main
Are you sure you want to change the base?
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 |
|---|---|---|
|
|
@@ -41,6 +41,7 @@ | |
| #include "SourceProfiler.h" | ||
| #include "SymbolTableInlines.h" | ||
| #if USE(BUN_JSC_ADDITIONS) | ||
| #include "AsyncContextSwapScope.h" | ||
| #include "SyntheticModuleRecord.h" | ||
| #endif | ||
| #include "UnlinkedModuleProgramCodeBlock.h" | ||
|
|
@@ -555,7 +556,16 @@ void CyclicModuleRecord::executeAsync(JSGlobalObject* globalObject) | |
| // 7. Let onRejected be CreateBuiltinFunction(rejectedClosure, 0, "", « »). | ||
| // Also handled in JSMicrotask.cpp. | ||
| // 8. Perform PerformPromiseThen(capability.[[Promise]], onFulfilled, onRejected). | ||
| #if USE(BUN_JSC_ADDITIONS) | ||
| // AsyncModuleExecutionFulfilled runs the bodies of the modules waiting on this one, so | ||
| // it has to run under the async context this evaluation was started under (for a | ||
| // dynamic import(), the importer's context installed by dynamicImportLoadSettled). | ||
| // Snapshot it alongside the module, as a promise reaction would; the | ||
| // AsyncModuleExecutionDone microtask unwraps the tuple and reinstalls it. | ||
| promise->performPromiseThenWithInternalMicrotask(vm, InternalMicrotask::AsyncModuleExecutionDone, nullptr, AsyncContextSwapScope::wrapWithCurrent(vm, globalObject, this)); | ||
|
Comment on lines
+559
to
+565
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. 🟡 Not a defect — just noting a newly observable edge case: when two concurrent Extended reasoning...What happensWhen two dynamic imports race for the same unevaluated top-level-await dependency, the second importer's module body runs under the first importer's async context rather than its own. Concretely: M1 prints Step-by-step trace
Result: M2's top-level body observes Why this is Node parity, not a defectThis is exactly what Node.js does, and matching Node is the PR's stated goal. V8 propagates AsyncLocalStorage via continuation-preserved embedder data, which is captured per promise reaction at This is also inherent to the ES module design: a TLA module has exactly one completion reaction, and Why it's still worth a noteBefore this PR M2 saw Addressing the objectionOne reviewer argued this should not be filed at all because it is intentional Node-matching behavior. That objection is correct on the substance — this is not a code defect and no code change is being requested. The comment is filed as a nit whose only actionable ask is documentation/test coverage of a corner case the PR newly makes observable, which is cheap and does not block merge. |
||
| #else | ||
| promise->performPromiseThenWithInternalMicrotask(vm, InternalMicrotask::AsyncModuleExecutionDone, nullptr, this); | ||
| #endif | ||
| // 9. Perform ! module.ExecuteModule(capability). | ||
| execute(globalObject, promise); | ||
| RETURN_IF_EXCEPTION(scope, void()); | ||
|
|
||
Uh oh!
There was an error while loading. Please reload this page.