Conversation
|
Warning Review limit reached
Next review available in: 11 minutes Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available. How can I continue?After more reviews become available, a review can be triggered using the To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews. How do review limits work?CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability. For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window. Please refer docs for additional details. Review details⚙️ Run configurationConfiguration used: Path: .coderabbit.yaml Review profile: ASSERTIVE Plan: Pro Run ID: 📒 Files selected for processing (6)
Comment |
|
Updated 11:24 AM PT - Jul 6th, 2026
❌ @robobun, your commit 796e175 has some failures in 🧪 To try this PR locally: bunx bun-pr 33454That installs a local version of the PR into your bun-33454 --bun |
There was a problem hiding this comment.
I didn't find any bugs, but this changes user-visible Node compat behavior that was deliberately gated off (the Next.js payload issue), reworks refcount ownership in JSSourceMap, and makes a design call to re-encode payload from parsed mappings rather than the original JSON — worth a human look.
Extended reasoning...
Overview
This PR wires process.setSourceMapsEnabled() to a new per-VM source_maps_enabled flag on VirtualMachine, replacing a dead process-global AtomicBool and the unread Process::m_sourceMapsEnabled field. module.findSourceMap() now reads that flag and, when enabled, returns a SourceMap whose sources are resolved against the generated file's URL (matching Node's sourcesToAbsolute()) and whose payload getter re-encodes a conformant v3 document from Bun's already-parsed mappings. It also switches JSSourceMap::{sources,names} from bun.String (Copy, no Drop) to OwnedString to release the +1 each element carries on both GC finalize and ?-unwind paths. A new Bun__VM__setSourceMapsEnabled FFI export bridges the C++ process object to the Rust VM. Four new subprocess tests cover the gate, transpiled-file mapping, inline sourceMappingURL resolution, and miss cases.
Security risks
None identified. No auth, crypto, or untrusted-input parsing is touched; the sourcemap parser itself is pre-existing and the new code only re-encodes already-parsed data and does URL joining via existing helpers.
Level of scrutiny
This warrants maintainer review rather than auto-approval. The prior code contained an explicit comment explaining that findSourceMap() was left disabled because enabling it without a real payload broke Next.js on startup — so flipping this on is a deliberate behavior change for real-world tooling, and the claim that the new get_payload() resolves that concern should be validated by someone who knows the history. The refcount/ownership rework (String → OwnedString, dupe_ref vs get in resolve_sources, the file:// path replacement) is exactly the memory-safety category the repo's review guidelines flag as most-blocked.
Other factors
- Design tradeoff:
payloadis reconstructed from decoded mappings, so it dropssourcesContent,sourceRoot,file, and originalnamesfrom maps that had them. The PR argues it round-trips throughnew SourceMap(payload), but a maintainer should confirm this shape is sufficient for the tooling ecosystem (Next.js, coverage tools) the description cites. - Acknowledged divergence: the PR notes Bun still doesn't chain a transpiled file's own
//# sourceMappingURLthrough its generated map, sofindSourceMap()on a Bun-transpiled file returns Bun's map rather than the file's — a known Node-compat gap left for later. - VirtualMachine field addition and a new C++→Rust FFI export are small but touch core runtime plumbing.
- Test coverage is solid (per-process isolation, both path and file:// specifiers, payload round-trip, negative cases) and the description reports ASAN/GC stress is clean, but CI was still building at the time of review.
module.findSourceMap() always returned undefined because the registry lookup was gated on a static ENABLE_SOURCE_MAPS that nothing ever set. process.setSourceMapsEnabled() now stores the flag on the VM, which is what the lookup reads. A map returned by findSourceMap() is built from Bun's parsed mappings rather than the JSON they came from, so it had no payload and its only source was the generated file. Resolve the original sources against the generated file's URL (Node's sourcesToAbsolute) and re-encode the mappings into a conformant v3 payload on first access. JSSourceMap.sources/names held bun.String, which is Copy and has no Drop, so each element's refcount was leaked; hold OwnedString instead.
6f6519d to
c9507d1
Compare
|
Answering the three things the review flagged, with the evidence behind each. The gate and Next.jsThe default is unchanged: And the failure the old comment described, Refcount ownershipThe old code was leaking, which is what pushed me to change the field types.
3000 iterations of The payload shapeBun deliberately does not keep the source map JSON or its
Worth knowing: Bun's source map parser has no references to CIThe Windows failures on the first build were |
There was a problem hiding this comment.
I didn't find any bugs, but this changes user-visible Node compat behavior (findSourceMap() now returns real maps, which was previously gated off specifically because of Next.js breakage) and touches refcount handling and FFI wiring, so it's worth a human look at the semantics and the OwnedString lifetime changes.
Extended reasoning...
Overview
This PR wires process.setSourceMapsEnabled() through to a new per-VM source_maps_enabled flag and makes module.findSourceMap() honor it. It touches VirtualMachine.rs (new field), BunProcess.cpp/.h (removes dead m_sourceMapsEnabled, calls a new FFI export), virtual_machine_exports.rs (new Bun__VM__setSourceMapsEnabled), and JSSourceMap.rs (replaces the always-false ENABLE_SOURCE_MAPS static, adds resolve_sources() for URL-relative source resolution, implements get_payload() to re-encode a v3 sourcemap document, and switches sources/names from bun.String to OwnedString to fix a refcount leak). Four new subprocess tests are added.
Security risks
None identified. The flag defaults off (matching Node), so there's no new surface exposed by default. No auth/crypto/permissions code is touched. Inputs to the new resolve_sources/get_payload paths are already-parsed sourcemap data from Bun's own registry.
Level of scrutiny
Medium-high. The prior code had an explicit comment stating the gate was left off because enabling it broke Next.js (payload is not an Object). This PR addresses that by synthesizing a conformant payload, but that's a design decision (re-encode from parsed mappings rather than retain the original JSON) that a maintainer should sign off on — especially since the re-encoded payload drops names, sourcesContent, and any other fields the original map carried. The bun.String → OwnedString refcount change and the dupe_ref()/get() handling in resolve_sources also deserve a careful eye from someone familiar with the string ownership conventions.
Other factors
The PR is well-tested (four new subprocess tests covering the gate toggle, transpiled files, inline // @bun maps, and miss cases; payload round-trips through new SourceMap()), the description is thorough, and the bug-hunting system found no issues. It also cleanly deletes the dead m_sourceMapsEnabled field and ENABLE_SOURCE_MAPS static. But it's a ~200-line behavior change across the Rust/C++ FFI boundary with memory-management implications, not a mechanical fix, so I'm deferring rather than approving.
|
Same review re-run against the rebased commit, so I won't repeat the previous answer. One point is worth sharpening, though, because I checked it and my earlier framing undersold it.
|
darwin-26-aarch64-test-bun failed on 'buildkite-agent artifact download timed out after 120s for step darwin-aarch64-build-bun'. The build step passed, so the binary exists; the test shard could not fetch it.
There was a problem hiding this comment.
I didn't find any bugs, but this touches refcount ownership (bun.String → OwnedString), FFI plumbing, and Node-compat semantics in an area with a documented Next.js footgun, plus several explicitly-flagged design tradeoffs (reconstructed payload, deferred names support) — worth a maintainer's eyes.
Extended reasoning...
Overview
This PR wires process.setSourceMapsEnabled() to actually gate module.findSourceMap(), replacing a dead AtomicBool and an unread C++ field with a per-VM flag. It also substantially reworks JSSourceMap.rs: changes the sources/names field types from bun.String to OwnedString to fix a refcount leak, adds resolve_sources() to resolve relative source URLs against the generated file (matching Node's sourcesToAbsolute()), and implements get_payload() to re-encode a conformant v3 sourcemap document from the parsed mappings. Six files changed, ~200 lines of native code plus four new subprocess tests.
Security risks
None apparent. The change reads user-provided module specifiers but only routes them through existing URL helpers and the existing SavedSourceMap lookup; no new parsing of untrusted bytes, no filesystem/network effects.
Level of scrutiny
High. This is not a mechanical change:
- The
String→OwnedStringrefactor is a memory-safety fix touching refcount ownership across FFI. The author's analysis of which calls hand out a+1vs. borrow (dupe_ref,URL::join,.get()) is careful and looks correct, and they report a clean 3000-iteration ASAN/GC stress, but this is exactly the class of change CLAUDE.md flags as most-blocked and warrants a second pair of eyes. - The old gate carried an explicit comment about breaking Next.js. The author has argued convincingly that the default is unchanged and
payloadis now an object so the original failure can't recur, but a maintainer should confirm they're comfortable with that reasoning. - The reconstructed
payloaddeliberately omitsnames,file,sourceRoot, andsourcesContent, and re-encodesmappingsrather than returning them verbatim. The author has documented these as known gaps and explained why each is deferred, but whether to ship the partial implementation vs. land it together is a maintainer call the author has explicitly asked about.
Other factors
Test coverage is good: four new subprocess tests covering the gate toggle, transpiled-file mapping with payload round-trip, inline sourceMappingURL resolution, and miss cases. The PR description and follow-up comments are unusually thorough. No prior human review on the thread; the coderabbit review was rate-limited and never ran.
CI status: the red is infrastructure, not this diffTwo consecutive builds have exactly one hard failure, The artifact it cannot fetch is the Why this is not the diff:
Build #68967 has now finished. Final tally: 186 passed, 1 hard failure (that darwin shard), plus 94 jobs left in I have already spent my one re-roll ( The diff itselfUnchanged since it was verified, and green locally: Three of the four new tests fail against the released binary, which is the fail-before proof that they exercise the fix. A 3000-iteration GC stress over This is ready for a human. The open questions are the two design calls I flagged in the description rather than anything CI found: whether reconstructing |
|
Closing as part of a cleanup of stale pull requests. This PR has had no new commits since 2026-07-06, it conflicts with main, and its last CI run failed. This is not a judgment on the fix itself. If the problem still reproduces on a current build, reopen this PR after a rebase or open a new one against main. |
module.findSourceMap()always returnsundefined, even afterprocess.setSourceMapsEnabled(true), which is accepted and then ignored. Tooling that uses the API Node documents for stack remapping and coverage silently gets no mappings for every module.Cause
The registry lookup in
find_source_mapwas gated onand nothing ever stored
true.process.setSourceMapsEnabled()wrote toProcess::m_sourceMapsEnabled, which nothing read.Flipping the gate alone is not enough: a
SourceMapfromfindSourceMap()is built from Bun's already-parsed mappings, not from the JSON they came from, sopayloadwasundefinedand the only entry insourceswas the generated file rather than the original source. That missing payload is why the gate was left off: Next.js readssourceMap.payloadon startup and logsInvalid source map. Only conformant source maps can be used to filter stack frames. Cause: TypeError: payload is not an Object.Fix
process.setSourceMapsEnabled()stores the flag on theVirtualMachine(per-VM, like Node's per-Environment flag), andfind_source_mapreads it. Default stays off, matching Node.sourcesresolve against the generated file's URL, the same as Node'ssourcesToAbsolute(), sofindEntry().originalSourcepoints at the original file.payloadis re-encoded from the parsed map into a conformant v3 document on first access (version,sources,names,mappings). It round-trips throughnew SourceMap(payload).While in
JSSourceMap:sources/namesheldbun.String, which isCopyand has noDrop, so the+1each element carried was never released. They now holdOwnedString, which also covers the?unwind paths out of the constructor.Known gaps, not addressed here
Both predate this PR and are independent of it. Listing them so the tradeoffs are visible:
//# sourceMappingURLwith the map it generates while transpiling that file, so for a module Bun transpiles,findSourceMap()returns Bun's map rather than chaining to the file's. An already-bundled (// @bun) file uses its own map, and that case matches Node.SavedSourceMapare parsed withinclude_names: false(the onlyParseUrlResultHint::Allconstruction site,SavedSourceMap.rs:460), sofindEntry().nameis alreadyundefinedon this path no matter what the map declared.payload.namesis therefore[], which matches the 4-field mappingswrite_vlqsemits. Node returns the names. Making them work means parsing names into the cached map, teachingwrite_vlqsthe optional 5th field, and accepting the extra memory on the hot stack-remapping path, so it belongs in its own change.payloadomitsfile,sourceRoot, andsourcesContent. All three are optional in the v3 spec, and Bun deliberately does not keep source contents resident (see the comment onParsedSourceMap::underlying_provider). Bun's parser has no references tosourceRootanywhere, so stack remapping already ignores it.Verification
Three of the four new tests fail against the released binary;
bun bd test test/js/node/module/ test/js/bun/sourcemap/is 120 pass / 0 fail.After the fix
A GC/ASAN stress over 3000
new SourceMap()+findSourceMap()+payloaditerations (including the constructor's throwing path) is clean.