-
Notifications
You must be signed in to change notification settings - Fork 5k
bundler: fix ESMConditions capacity miscomputation with custom conditions #30535
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
Closed
Closed
Changes from all commits
Commits
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back 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.
🟣 Nit (pre-existing): the same if-else precedence footgun exists ~480 lines down in
loadersFromTransformOptions'stotal_capacityargument — whentarget.isBun()is true,default_loader_ext.len(and the browser term) get absorbed into theelsebranch and dropped. It's harmless there since only the user-provided extensions useputAssumeCapacityand the defaults usegetOrPutValuewhich grows the map, so it's just a missed pre-sizing — but since this PR is specifically about this pattern in this file, you may want to give it the same@as(usize, ...)treatment.Extended reasoning...
Summary
This PR fixes the Zig if-else precedence bug in
ESMConditions.initwhereA + if (cond) 1 else 0 + Bparses asA + (if (cond) 1 else (0 + B)). However, the identical anti-pattern still exists in the same file inloadersFromTransformOptions(around src/bundler/options.zig:1574-1577):How it parses
Because Zig's if-else binds tighter on the else-branch than the surrounding
+, the trailing additions are absorbed into the precedingelse. So whentarget.isBun()is true, the whole expression evaluates to justinput_loaders.extensions.len + default_loader_ext_bun.len(=extensions.len + 2), silently dropping bothdefault_loader_ext.len(16) and the browser term. Whentarget.isBun()is false andtarget == .browser, it evaluates toextensions.len + 0 + default_loader_ext_browser.len(=extensions.len + 1), again droppingdefault_loader_ext.len. Only the fall-through case (else 0 + else 0 + default_loader_ext.len) yields the +16 the author intended as the baseline.Why it isn't a correctness bug here
Unlike
ESMConditions.init, this miscomputation cannot corrupt memory or assert:stringHashMapFromArrays(src/bundler/options.zig:41-51) only callsputAssumeCapacityfor the user-provided keys (input_loaders.extensions). The miscomputedtotal_capacityis always>= input_loaders.extensions.lenbecause that term is unconditional and first, and every if-branch contributes a non-negative addend. So theputAssumeCapacityloop is always covered.default_loader_ext,default_loader_ext_bun,default_loader_ext_browser) all usetry loaders.getOrPutValue(...), which grows the map on demand.So the only effect is a missed pre-sizing optimization — the map will rehash a couple of times during the default-extension inserts instead of being sized up-front.
Step-by-step example
Take
target = .bun,input_loaders.extensions.len = 0:0 + 2 (bun) + 0 (browser) + 16 (default) = 18.0 + default_loader_ext_bun.len = 0 + 2 = 2. The entireelse 0 + if (...) ... + default_loader_ext.lentail is the unevaluated else-branch.stringHashMapFromArraysis called withtotal_capacity = 2andkeys.len = 0, soensureTotalCapacityisn't even called (gated onkeys.len > 0).getOrPutValuecalls fordefault_loader_extthen grow the map from empty — correct, just not pre-sized.Suggested fix
Apply the same treatment as in this PR:
This is pre-existing and nit-level — it has no correctness impact and shouldn't block the PR. It's only worth mentioning because the PR's entire purpose is fixing this exact precedence footgun in this exact file, so applying it consistently removes a known-misleading capacity computation.