Skip to content

appendSourceToErrorMessage: skip builtin code blocks - #369

Open
robobun wants to merge 1 commit into
mainfrom
farm/57165bac/no-builtin-source-in-errors
Open

appendSourceToErrorMessage: skip builtin code blocks#369
robobun wants to merge 1 commit into
mainfrom
farm/57165bac/no-builtin-source-in-errors

Conversation

@robobun

@robobun robobun commented Jul 28, 2026

Copy link
Copy Markdown
Collaborator

What

Skip source-text appending in appendSourceToErrorMessage() when the code block is a builtin function.

Why

Builtin JS functions use @-prefixed private identifiers in their source text. In release builds their code blocks carry no expression info (because BytecodeGenerator::emitExpressionInfo early-returns for isPrivateBuiltinFunction() under #if !ASSERT_ENABLED), so the existing hasExpressionInfo() check already short-circuits here.

ASSERT_ENABLED builds (debug and ASAN) do emit expression info for builtins. When a .@call() inside a builtin hits a non-callable value, the Call IC slow path (throwNotAFunctionErrorFromCallIC in RepatchInlines.h) passes the builtin code block and bytecode index straight into appendSourceToErrorMessage, which then splices the private-identifier source text into the user-visible TypeError message.

Repro

Iterator.from({}).next();

ASSERT_ENABLED (before):

TypeError: @getWrapForValidIteratorInternalField(this, @wrapForValidIteratorFieldIteratedNextMethod).@call is not a function. (In '@getWrapForValidIteratorInternalField(this, @wrapForValidIteratorFieldIteratedNextMethod).@call(@getWrapForValidIteratorInternalField(this, @wrapForValidIteratorFieldIteratedIterator))', '@getWrapForValidIteratorInternalField(this, @wrapForValidIteratorFieldIteratedNextMethod).@call' is undefined)

After (matches release output):

TypeError: undefined is not a function

Also affects Iterator.from({[Symbol.iterator]: 5}) (getIteratorFlattenable) and %WrapForValidIteratorPrototype%.return with a non-callable return, and any other builtin that reaches the Call IC slow path.

The FindFirstCallerFrameWithCodeblockFunctor path in Error.cpp already had a check that avoids setting bytecodeIndex for builtins; this covers the direct callLinkInfo->retrieveCaller() path used by the Call IC.

@coderabbitai

coderabbitai Bot commented Jul 28, 2026

Copy link
Copy Markdown

Warning

Review limit reached

@robobun, you've reached your PR review limit, so we couldn't start this review.

Next review available in: 16 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: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: a408ec3c-4343-483e-aa9a-e34bbfdcb544

📥 Commits

Reviewing files that changed from the base of the PR and between 6886cd2 and 61b90ca.

📒 Files selected for processing (1)
  • Source/JavaScriptCore/runtime/ErrorInstance.cpp

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

@claude claude Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

LGTM — small, targeted guard that mirrors the existing isBuiltinFunction() check in FindFirstCallerFrameWithCodeblockFunctor (Error.cpp:153), and only affects error-message text. The inline nit about isBuiltinFunction() vs isPrivateBuiltinFunction() is worth a look but either choice is safe.

Extended reasoning...

Overview

Adds a single early-return in appendSourceToErrorMessage() (ErrorInstance.cpp) so that builtin code blocks never have their source text spliced into user-visible TypeError messages. Two-line guard plus a four-line explanatory comment; no other changes.

Security risks

None. The change strictly reduces the amount of internal source text exposed in error messages — it removes the leak of @-prefixed private-identifier source from ASSERT_ENABLED builds. No new inputs, no allocation, no control-flow beyond an early return of the unmodified message string.

Level of scrutiny

Low. This is error-message cosmetics on a well-understood slow path (Call IC "not a function" errors). The predicate (codeBlock->unlinkedCodeBlock()->isBuiltinFunction()) is the same one already used by the other entry into this function via FindFirstCallerFrameWithCodeblockFunctor in Error.cpp, so the two paths are now consistent. Worst case for the raised nit is that Bun's non-private builtins (created via createBuiltinExecutable with a sourceURL) also stop getting source snippets appended in release builds — an error-message-text-only regression, and arguably desirable since those can also contain @-intrinsics.

Other factors

  • The PR description includes a concrete repro (Iterator.from({}).next()) and before/after output showing the fix aligns debug/ASAN behavior with release.
  • Verifiers cross-checked BytecodeGenerator::emitExpressionInfo gating and the isPrivateBuiltinFunction() definition under USE(BUN_JSC_ADDITIONS); the inline nit captures the one open question (predicate breadth + comment accuracy), which is the author's call and doesn't block merging.

Comment thread Source/JavaScriptCore/runtime/ErrorInstance.cpp Outdated
@github-actions

github-actions Bot commented Jul 28, 2026

Copy link
Copy Markdown

Preview Builds

Commit Release Date
61b90cae autobuild-preview-pr-369-61b90cae 2026-07-28 23:18:06 UTC
bce3c3e1 autobuild-preview-pr-369-bce3c3e1 2026-07-28 22:34:34 UTC

Builtin JS functions use @-prefixed private identifiers in their source
text. In release builds their code blocks carry no expression info
(BytecodeGenerator::emitExpressionInfo skips them) so the existing
hasExpressionInfo() check already short-circuits here. ASSERT_ENABLED
builds do emit expression info for builtins, and when a .@call() inside a
builtin hits a non-callable value the Call IC slow path
(throwNotAFunctionErrorFromCallIC) passes the builtin code block and
bytecode index straight into appendSourceToErrorMessage, which then
splices the private-identifier source text into the user-visible
TypeError message:

    Iterator.from({}).next()
    // TypeError: @getWrapForValidIteratorInternalField(this,
    //   @wrapForValidIteratorFieldIteratedNextMethod).@call is not a
    //   function. (In '...')

Bail out of source appending for builtin code blocks so ASSERT_ENABLED
builds match release output:

    TypeError: undefined is not a function
@robobun
robobun force-pushed the farm/57165bac/no-builtin-source-in-errors branch from bce3c3e to 61b90ca Compare July 28, 2026 22:44

@claude claude Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

LGTM — the updated comment now accurately covers the non-private-builtin case under USE(BUN_JSC_ADDITIONS), and keeping isBuiltinFunction() to match FindFirstCallerFrameWithCodeblockFunctor makes sense.

Extended reasoning...

Overview

Adds a single early-return in appendSourceToErrorMessage() (ErrorInstance.cpp) so builtin code blocks never get their source text spliced into user-visible TypeError messages. One conditional plus a comment; no other files touched.

Follow-up on prior review

My earlier nit was that the original comment misstated the release-build invariant under USE(BUN_JSC_ADDITIONS) (non-private builtins do carry expression info there). The author responded that isBuiltinFunction() is intentional — it matches the existing check at Error.cpp:153 in FindFirstCallerFrameWithCodeblockFunctor so both callers of this function behave identically, and Bun's non-private builtins can also contain @-intrinsics. The comment has been rewritten to state exactly that, so the concern is resolved.

Security risks

None. This only affects the text of thrown TypeError messages. If anything it slightly reduces exposure by preventing internal builtin source snippets from leaking into error strings. No auth, crypto, memory, or control-flow surface is touched.

Level of scrutiny

Low. The change is a cosmetic guard on an error-message helper. The accessor chain codeBlock->unlinkedCodeBlock()->isBuiltinFunction() is identical to the one already used in Error.cpp:153, so there is no new null-deref or lifetime concern beyond what the existing caller path already exercises. Worst case is a slightly less descriptive error message for Bun's non-private builtins in release builds, which the author has explicitly accepted.

Other factors

  • The inline review thread is resolved.
  • The bug hunting system found no issues on the updated revision.
  • Change is self-contained and mirrors an established pattern in the same subsystem.

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.

2 participants