-
Notifications
You must be signed in to change notification settings - Fork 52
JSModuleLoader: don't treat a TerminationException from resolve() as a resolution failure #309
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
Changes from all commits
a63be92
0006cf6
38ddd57
5113fb4
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 |
|---|---|---|
|
|
@@ -649,6 +649,11 @@ JSPromise* JSModuleLoader::hostLoadImportedModule(JSGlobalObject* globalObject, | |
| resolved = resolve(globalObject, specifier, referrerKey, scriptFetcher, useImportMap); | ||
| // 9. If the previous step threw an exception, then: | ||
| if (Exception* resolutionError = scope.exception()) { | ||
| // A TerminationException is not a resolution failure; bail without | ||
| // caching it or entering FinishLoadingImportedModule with it still | ||
| // pending on the VM. | ||
| if (vm.isTerminationException(resolutionError)) [[unlikely]] | ||
| return nullptr; | ||
| attachErrorInfo(globalObject, resolutionError, nullptr, specifier, moduleRequest.type(), ModuleFailure::Kind::Instantiation); | ||
| // Cache the resolution error so subsequent calls for the same specifier return the same error object. | ||
| JSValue errorValue = resolutionError->value(); | ||
|
|
@@ -1023,7 +1028,10 @@ void JSModuleLoader::continueDynamicImport(JSGlobalObject* globalObject, ModuleL | |
| // 1.a. Perform ! Call(promiseCapability.[[Reject]], undefined, « moduleCompletion.[[Value]] »). | ||
| promise->reject(vm, (*exception)->value()); | ||
| // 1.b. Return UNUSED. | ||
| scope.assertNoException(); | ||
| // The abrupt completion may be a resolution failure caused by the | ||
| // TerminationException, which rejectWithCaughtException cannot clear; | ||
| // it stays pending in the VM and the caller's exception check unwinds. | ||
| scope.assertNoExceptionExceptTermination(); | ||
|
Comment on lines
+1031
to
+1034
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. 🟡 nit: This comment references Extended reasoning...The new comment above
There are two accuracy issues with this wording in the context of the combined diff. 1. Wrong function referenced at this site. Line 1029 calls 2. The described scenario is now unreachable. The comment's causal chain is: if (vm.isTerminationException(resolutionError)) [[unlikely]]
return nullptr;which returns before Step-by-step (post-fix):
So the scenario the comment describes cannot happen anymore. Why the relaxed assertion is still correct. Context. Per the PR description, this comment was cherry-picked from #286, which was written before the Suggested fix. Reword to something like: // promise->reject() may enter JS and trip the NeedTermination trap; the
// TerminationException stays pending and the caller's exception check unwinds.This is comment-wording only; no behavioral defect. |
||
| return; | ||
| } | ||
| // 2. Let module be moduleCompletion.[[Value]]. | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -538,6 +538,11 @@ inline void reifyStaticProperty(VM& vm, const ClassInfo* classInfo, const Proper | |
|
|
||
| if (value.attributes() & PropertyAttribute::PropertyCallback) { | ||
| JSValue result = value.lazyPropertyCallback()(vm, &thisObj); | ||
| // A callback that enters JS may return empty with an exception pending; | ||
| // the two callers (setUpStaticFunctionSlot / reifyAllStaticProperties) | ||
| // check and propagate, so don't put an empty value in the slot here. | ||
| if (!result) [[unlikely]] | ||
| return; | ||
|
Comment on lines
+541
to
+545
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. 🟡 nit: the comment says "the two callers" but Extended reasoning...What the comment claims vs. what's in the fileThe new comment at
However,
Step-by-step: what would happen on the third pathIf a caller reached
Why this doesn't matter for Bun
Why it's still worth a nitThe comment lives right next to the Suggested fixReword to something like:
or simply "the two exception-checking callers". No code change needed — the |
||
| thisObj.putDirect(vm, propertyName, result, attributesForStructure(value.attributes())); | ||
| return; | ||
| } | ||
|
|
||
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.
📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win
Update the comment to describe the actual rejection path.
Lines [1031]-[1034] call
promise->reject(...), notrejectWithCaughtException(...). Additionally, termination is now returned before becoming a cached resolution failure. Clarify that promise rejection does not clear the ambientTerminationException; the caller’s exception check unwinds it.Suggested wording
📝 Committable suggestion
🤖 Prompt for AI Agents