Skip to content

Read Request's abort signal and JSArrayIterator's fast path through accessors - #37651

Merged
alii merged 1 commit into
mainfrom
ali/request-signal-iterator-fast-private
Aug 11, 2026
Merged

alii merged 1 commit into
mainfrom
ali/request-signal-iterator-fast-private

Conversation

@alii

@alii alii commented Aug 11, 2026

Copy link
Copy Markdown
Member

What does this PR do?

Two fields whose owner sets them up under a specific condition were writable from anywhere in the crate:

  • webcore::Request.signal is only ever set from construct_into (after AbortSignal::ref_from_js type-checks it), clone_into, and the lazy signal getter. Its one outside reader, fetch() extracting the signal from a Request argument, now goes through Request::abort_signal(), and the field is private.
  • JSArrayIterator.fast/.array are Some/set only when C++ reported a contiguous butterfly together with the len that next() bounds against; Blob's parts loop read fast to decide whether iteration can run JS. That is now iter.is_fast(), and both fields are private (i/len stay public, they are read widely and next() bounds-checks i).

No behavior change.

How did you verify your code works?

cargo check -p bun_jsc -p bun_runtime. With bun-debug: fetch(new Request(url, { signal })) still rejects with AbortError when the controller aborts mid-request, and new Blob([...]).text() is correct for both a plain array (fast path) and one containing an object with toString (slow path).

@coderabbitai

coderabbitai Bot commented Aug 11, 2026

Copy link
Copy Markdown
Contributor

Warning

Review limit reached

You’ve reached a temporary PR review limit under our Fair Usage Limits Policy.

Your recent review volume is higher than typical usage, so adaptive limits are currently applied.

Next review available in: 21 minutes

Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available.
You're only billed for reviews past your plan's rate limits ($0.25/file).

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

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 configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: 5d2a17ac-9ec5-48b2-b081-4c370d4a781f

📥 Commits

Reviewing files that changed from the base of the PR and between f598766 and 04acfa5.

📒 Files selected for processing (4)
  • src/jsc/JSArrayIterator.rs
  • src/runtime/webcore/Blob.rs
  • src/runtime/webcore/Request.rs
  • src/runtime/webcore/fetch.rs

Comment @coderabbitai help to get the list of available commands.

@robobun

robobun commented Aug 11, 2026 •

Copy link
Copy Markdown
Collaborator
Updated 3:52 PM PT - Aug 11th, 2026

✅ @alii, your commit 04acfa5acd6c9a6f36bbbfd267f417879743a613 passed in Build #92521! 🎉


🧪   To try this PR locally:

bunx bun-pr 37651

That installs a local version of the PR into your bun-37651 executable, so you can run:

bun-37651 --bun

@claude claude Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM — pure encapsulation refactor, no behavior change.

What was reviewed:

  • Request.signal → private + abort_signal() accessor: the sole external reader in fetch.rs gets identical semantics (JsCell::get().as_ref() → Option<&AbortSignalRef>, then .ref_()).
  • JSArrayIterator.fast/.array → private + is_fast(): Blob.rs swaps iter.fast.is_none() for !iter.is_fast(), which is definitionally equivalent.
  • Grepped for other crate-level readers/writers of both fields — none exist outside the owning modules.
Extended reasoning...

Overview

This PR tightens field visibility on two structs and routes their one external reader each through a new accessor:

  • src/runtime/webcore/Request.rs: signal: JsCell<Option<AbortSignalRef>> goes from pub(crate) to private; new pub(crate) fn abort_signal(&self) -> Option<&AbortSignalRef> returns self.signal.get().as_ref().
  • src/runtime/webcore/fetch.rs: req.signal.get() → req.abort_signal() at the one call site in 'extract_signal. The subsequent .ref_() and NonNull::new(...) are unchanged.
  • src/jsc/JSArrayIterator.rs: array and fast go from pub to private; new pub fn is_fast(&self) -> bool { self.fast.is_some() }.
  • src/runtime/webcore/Blob.rs: iter.fast.is_none() → !iter.is_fast() at the one call site in the parts prescan.

Security risks

None. This is a visibility narrowing with an accessor shim; no new code paths, no input handling changes, no allocation or refcount changes.

Level of scrutiny

Low. The change is mechanical: two pub→private field flips, two one-line accessor methods, and two call-site rewrites that are trivially equivalent by inspection (is_none() ↔ !is_some(); cell.get() pattern-match ↔ cell.get().as_ref()). I confirmed via JsCell::get in src/jsc/JSCell.rs that it returns &T, so self.signal.get().as_ref() on JsCell<Option<AbortSignalRef>> yields Option<&AbortSignalRef> as expected, and AbortSignalRef::ref_() still bumps the C++ refcount identically at the fetch call site.

Other factors

I grepped the workspace for other consumers of JSArrayIterator.{fast,array} and Request.signal:

  • JSArrayIterator is used in ~10 files (shell, sql, ConsoleObject, Macro, JSTranspiler, etc.) but only via init/next/.len/.i — none touch .fast or .array, so making those private compiles cleanly.
  • .signal.get()/.signal.set() hits in src/runtime/server/ are on RequestContext.signal or in comments, not Request.signal. The field's own module (Request.rs) still has full access for construct_into/clone_into/get_signal/finalize_without_deinit.

The PR description states cargo check -p bun_jsc -p bun_runtime passes and the debug build was smoke-tested for both affected paths. No new tests are shipped, but per REVIEW.md this is a no-behavior-change encapsulation refactor rather than a behavioral fix, so the existing coverage suffices.

@Jarred-Sumner

Copy link
Copy Markdown
Collaborator

I think i've seen this PR 3 times, duplicate flag should find things

@alii

alii commented Aug 11, 2026

Copy link
Copy Markdown
Member Author

Sorry for the noise, there were a few of these in a row today. This one isn't the same change as #37549 (that's fetch's own signal holder in FetchTasklet; this is Request.signal plus the JSArrayIterator fields), but agreed it's the same flavour. I'll batch anything else like this into a single PR rather than one per field.

@alii
alii merged commit 0e5d9df into main Aug 11, 2026
52 of 53 checks passed
@alii
alii deleted the ali/request-signal-iterator-fast-private branch August 11, 2026 22:56
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants