Skip to content

Bump WebKit (oven-sh/WebKit#995 preview): a first lookup of a source position decodes one 256-word chapter - #47947

Draft
robobun wants to merge 4 commits into
mainfrom
robobun/83eea896/expression-info-chapters
Draft

robobun wants to merge 4 commits into
mainfrom
robobun/83eea896/expression-info-chapters

Conversation

@robobun

@robobun robobun commented Nov 4, 2026 •

Copy link
Copy Markdown
Collaborator

Problem

  • JavaScriptCore decodes a call site's source position from the start of a chapter of expression info (oven-sh/WebKit bytecode/ExpressionInfo.cpp:901). A chapter is 10,000 words, so N call sites in one function cost O(N) each.
  • bun test --reporter=junit asks for the line of each describe() call: +171,969 instructions each in a file of 2,000. No issue reports it. It is not a regression.

Fix

  • WEBKIT_VERSION is the preview build of [JSC] ExpressionInfo: start a chapter every 256 words. BytecodeRewriter: keep running sums WebKit#995: main's engine plus that change. Move it to a commit of oven-sh/WebKit main with that change before this lands.
  • The engine starts a chapter every 256 words. The 2,000 describe() calls take 13.1 ms, was 23.9 ms.
  • bun-jsc.test.ts checks the lines of 400 call sites in a function, a generator and an async function, and their 256-word chapters.
  • Verified: bun bd test test/js/bun/jsc/bun-jsc.test.ts (54 pass, 3 new tests fail on main). bundler_bytecode_portable.test.ts: 17 hashes and sizes re-blessed.

Background

Downsides

Notes

What is in the diff

  • scripts/build/deps/webkit.ts: the pin, the preview build of [JSC] ExpressionInfo: start a chapter every 256 words. BytecodeRewriter: keep running sums WebKit#995 at 0f1ec592ee. The old value appears nowhere else in the repo.
  • src/jsc/bindings/JSCTestingHelpers.cpp: jscInternals.expressionInfoStats() for bun:internal-for-testing. It reads the code block of its caller, because a generator or an async function keeps its expression info in the code block of its body, which no function object gives.
  • test/js/bun/jsc/bun-jsc.test.ts: three tests, 120 to 210 ms each on a debug ASan build.
  • test/bundler/bundler_bytecode_portable.test.ts: the snapshot. 17 jsc hashes and 17 sizes move and no js hash. The sizes grow by 48 to 4,488 bytes, 12,120 bytes over 44.0 MB. I ran bun install in test/ first: with a stale test/node_modules the libraries.js row measures other package versions. On main the old snapshot passes with the fresh install (22 pass).

The snapshot was written by a release build with JavaScriptCore built from the pull request's head, and it passes on bun bd with the preview build that CI made (22 pass with --timeout 300000: two of its tests take 7 to 16 seconds on a debug ASan build on this machine, over the default 5 seconds).

Numbers

All from oven-sh/WebKit#995, where the method is. Two release builds of Bun made the same way, main with the engine at 4d45755fed and with the pull request.

bun test --reporter=junit on a file of N lines describe("d<i>", () => {});, extra instructions per describe() over the run without the reporter, and the wall time of the run:

N before after before after
100 +12,741 +12,954 6.1 ms 6.3 ms
500 +42,989 +13,622 7.8 ms 7.1 ms
2,000 +171,969 +13,074 23.9 ms 13.1 ms
8,000 +266,979 +13,022 107.2 ms 38.4 ms

The same file with released builds and with Node (node --test --test-reporter=junit), median of 20 runs:

N Node 26.3 Bun 1.3.13 Bun 1.4.3 Bun 1.4.4 before after
100 85.7 ms 10.4 ms 4.6 ms 6.5 ms 5.6 ms 5.8 ms
2,000 356.2 ms 37.9 ms 24.3 ms 26.2 ms 24.6 ms 13.7 ms
8,000 1,335.6 ms 149.7 ms 104.8 ms 130.5 ms 106.4 ms 38.5 ms

Real test files: of the 3,543 test files in this repo, 11 have 500 or more describe, test or it calls. test/js/bun/xml/xml-test-suite.test.ts has the most (2,007). Under the junit reporter it goes from 99.4 ms to 97.8 ms (89.7M to 37.3M instructions over the run without the reporter).

A debugger with a breakpoint: a module of N lines x += <i>; under bun --inspect-wait, with a breakpoint by URL at the middle line. Debugger::pauseIfNeeded then asks for the line and column of each statement that runs. Instructions of the process: 1,590.8M to 155.9M at N = 5,000, 7,474.6M to 556.9M at N = 20,000 (279 ms to 69 ms with the client). A debugger that is only attached asks for no position: 291.5M before and after at N = 20,000. Without a debugger: 91.3M before and after.

Binary: text of a release bun grows by 1,344 bytes (136,784,361 to 136,785,705), the helper included. A run that asks for no position executes the same instructions: bun test of the file of 2,000 without the reporter runs 79.22M before and 79.15M after.

Not in this PR

  • About half of what a new call site still costs under the junit reporter is Bun's own source map lookup (InternalSourceMap::find, src/sourcemap/InternalSourceMap.rs), which walks up to 63 mappings.
  • bun test --aot reads call-site positions from its own table. This does not change it.

Self-review

The engine change was reviewed twice in oven-sh/WebKit#995: 45 concerns raised, 41 addressed. For this half the reviews asked for the fresh install before the re-bless, for an async function and a generator in the test, and for the pin of the amended head. The snapshot did not move with that head.


no test proof · iteration 0 · platform-specific test(s) that do not run on this machine, deferring to CI, which covers all platforms: test/js/bun/jsc/bun-jsc.test.ts, test/bundler/bundler_bytecode_portable.test.ts

…position decodes one chapter

JavaScriptCore finds the source position of a call site in the
expression info of its function, which it decodes from the start of a
chapter. A chapter was 10,000 words, so the first lookup at each of N
call sites in one function cost O(N). The engine of oven-sh/WebKit#995
starts a chapter every 256 words.

WEBKIT_VERSION is the preview build of that pull request: the engine
that main pins (4d45755fed) plus the two commits. Move it to a commit of
oven-sh/WebKit main with that change before this lands.

jscInternals.expressionInfoStats() (bun:internal-for-testing) returns the
words and the chapter starts of the expression info of the code that
calls it. bun-jsc.test.ts checks the line of 400 call sites in a
function, a generator and an async function, and that each of the three
has chapters of 256 words.

bundler_bytecode_portable.test.ts: 17 hashes and 17 sizes move, each
output 0.02% to 0.11% larger. A chapter is 16 bytes.
@github-actions github-actions Bot added the claude label Nov 4, 2026
@robobun

robobun commented Nov 4, 2026 •

Copy link
Copy Markdown
Collaborator Author
Updated 9:19 AM PT - Nov 5th, 2026

❌ @robobun, your commit f3d2005 has 39 failures in Build #137748 (All Failures):


🧪   To try this PR locally:

bunx bun-pr 47947

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

bun-47947 --bun

@robobun

robobun commented Nov 4, 2026 •

Copy link
Copy Markdown
Collaborator Author

Status. Reproduced with bun test --reporter=junit on a file of 2,000 lines describe("d<i>", () => {}); and one test. On main each describe() costs +171,969 instructions over the same run without the reporter, and the run takes 23.9 ms. On this branch each costs +13,074, and the run takes 13.1 ms. The engine change is oven-sh/WebKit#995. This pull request stays a draft until that merges.

The branch has main at e57d89b. The pin is the preview build of oven-sh/WebKit#995 at 0f1ec592ee: main's pin 4d45755fed plus the two commits of that pull request.

CI on f3d2005, build 137748: 129 of 188 jobs passed. The two test files that this pull request changes fail on no lane. 28 test files are red, and this change touches none of them:

  • 26 in test/aot/ and test/bundler/compile-aot-size.test.ts are red on main too.
  • test/bundler/compile-trim-capabilities.test.ts fails on Linux x64 for this build's layout. Its scan reports a call of fork at address 125979518. The bytes there are 41 80 bf e8 96 00 00 01, which is cmp byte ptr [r15+0x96e8], 1: the scan reads the e8 of the displacement as a call. compile-trim-capabilities.test.ts: report an x64 call only where an instruction begins #47841 has the fix for that scan.

Comment thread src/jsc/bindings/JSCTestingHelpers.cpp Resolved

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant