Skip to content

Upgrade WebKit to 55d9d9007f - #40263

Closed
robobun wants to merge 10 commits into
mainfrom
farm/74ff7f04/webkit-upgrade-55d9d9007f
Closed

robobun wants to merge 10 commits into
mainfrom
farm/74ff7f04/webkit-upgrade-55d9d9007f

Conversation

@robobun

@robobun robobun commented Aug 23, 2026 •

Copy link
Copy Markdown
Collaborator

Problem

  • Bun's WebKit is pinned to the fork's aea1f010b6. Upstream WebKit main has moved 413 commits since the fork's merge base 47f7250137c6, 90 of them in JavaScriptCore, WTF or bmalloc.
  • Upstream removed the @newPromiseCapability private builtin (WebKit/WebKit 38027ff0ec). Six call sites in Bun's bundled modules use it, so those modules fail to compile against the new engine. Upstream also stopped including <wtf/HexNumber.h> through VM.h (314133b7a6), which src/jsc/bindings/EncodeURIComponent.cpp relied on for hex().

Fix

Background

  • Bun builds against a prebuilt JavaScriptCore from oven-sh/WebKit releases. scripts/build/deps/webkit.ts names the release tag; CI downloads the matching tarball per platform.
  • Bun's built-in JavaScript modules (src/js/) are compiled by JavaScriptCore's builtin compiler. A $name call becomes the private name @name, which has to exist in the engine. @newPromise is a bytecode intrinsic and @resolvePromise / @rejectPromise are link-time constants, so they are always available.
  • $resolvePromise and $rejectPromise require a pending promise (they assert on it in debug builds). The ...WithFirstResolvingFunctionCallCheck variants behave like the functions a Promise executor receives: the first call settles, later calls are ignored.
Notes
  • Every push to Upgrade to upstream WebKit 55d9d9007f WebKit#501 produces a new preview tag (autobuild-preview-pr-501-<first 8 of the head sha>), and this PR's WEBKIT_VERSION follows it. CI lanes that fetch the prebuilt fail on the download until that tag's Actions run has published the release.
  • Suites run on the local debug + ASAN build: test/js/bun/jsc, test/bundler/bundler_bun.test.ts, test/js/node/vm/vm.test.ts, test/js/node/events, test/js/node/util, test/js/node/dgram/node-dgram.test.js, test/js/node/http/node-http-connect.test.ts, test/js/bun/compile/standalone-madvise-tla.test.ts, test/regression/issue/26298.test.ts, test/js/web/atomics.test.ts, test/js/node/worker_threads, test/js/bun/wasm, test/js/web/url, test/js/node/module, test/js/bun/resolve, test/js/node/string_decoder, test/js/node/async_hooks, test/js/web/encoding. Also test/js/bun/jsc-stress and the new test/js/bun/jsc/webkit-upgrade-55d9d9007f.test.ts. Every failure is a test that also fails on a debug build of the current pin on the same machine: the 5 s local timeouts under debug + ASAN (domjit.test.ts, worker_destruction.test.ts, parse-args.test.mjs, util-inspect.test.js, the node-http-connect.test.ts child run, the resolve 2000-file load) and the IPv6 multicast ENODEV in node-dgram.test.js.
  • Behavior changes in this upstream range that are visible from JavaScript: Promise.try follows the updated spec (PromiseResolve instead of NewPromiseCapability); the module map no longer caches fetch failures, so a second import() of a specifier whose load failed re-runs Bun's module loader instead of rejecting with the cached error; Uint8Array.prototype.setFromBase64 on a zero-length target returns { read: 0, written: 0 } without validating the input; WebAssembly.Module.imports()/exports() descriptors drop the non-standard type field; re-exported imported Wasm globals and tags keep object identity; an overflow-checked ++/-- whose result is unused is no longer dead-code-eliminated by the DFG together with its overflow check (7711916200).
  • Performance and memory changes of note: SymbolTableEntry no longer allocates a WatchpointSet per stored closure variable and global until something watches it (cea233cede); Object.assign with 2 to 3 sources clones the first source's shape in one step (96ca975b2a); JSON.parse allocates arrays once at their final size; TypedArray.prototype.sort() without a comparator uses a radix sort for 2/4/8-byte element types; Map/Set forEach is inlined in the DFG and FTL; the bytecode cache emits CodeForConstruct for class constructors so cached programs stop reparsing them; UnlinkedFunctionCodeBlock shrinks from 216 to 192 bytes; RegExp cells shrink from 96 to 80 bytes.
  • src/jsc/bindings/NodeVMSyntheticModule.cpp calls SymbolTable::set(NoLockingNecessary, ...). After cea233cede only the locked overload exists; NoLockingNecessary converts to a ConcurrentJSLocker, so the call compiles unchanged.
  • The upstream change to Linux thread scheduling (per-QOS sched_setattr on every WTF thread, SCHED_BATCH compiler threads on hosts with 4 or fewer cores) is gated off for Bun in the fork: Bun's threads keep inheriting the process scheduling attributes. See Upgrade to upstream WebKit baf4a9a7ec0b WebKit#488 for the reasoning and how to drop that hunk.
  • The fork's Website has no icon #262 (setFetchError for a non-ErrorInstance fetch rejection) is superseded by upstream's module map change.

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/node/dgram/node-dgram.test.js

robobun added 10 commits August 23, 2026 21:54
Upstream JavaScriptCore removed the @newPromiseCapability private builtin
(WebKit/WebKit 38027ff0ec). The six call sites in node:events, node:util,
node:dgram and the HTTP server use @newPromise plus the @resolvePromise and
@rejectPromise family instead, which settles the promise without allocating
the capability record.

(cherry picked from commit 17c8bf7)
The abort listener unregistered the FinalizationRegistry entry with the
promise while the entry was registered with the listener function, so the
entry stayed alive until the resource was collected. The promise is now the
token on both sides.

(cherry picked from commit 7effb1d)
…ram disposal

These are the call sites that moved off @newPromiseCapability. Without the
port, node:events fails to load against the new WebKit (Private symbol not
found: newPromiseCapability).

(cherry picked from commit 1412855)
A listener defined inline in the request dispatcher or in Symbol.asyncDispose
keeps the whole enclosing scope alive for as long as the socket does. Bind the
promise to a module-level function instead.

(cherry picked from commit 8119ca7)
Upstream WebKit 314133b7a6 removed the UUID ObjectIdentifier, and with it the
<wtf/UUID.h> include that made hex() reachable through VM.h.
…sync

Promise.try returns a native promise as-is, setFromBase64 on a zero-length
target reads nothing, WebAssembly.Module.imports() descriptors drop the
js-types field, and the DFG keeps the overflow check of an unused ++ / --.
WEBKIT_VERSION points at the oven-sh/WebKit#501 preview build
(autobuild-preview-pr-501-fabdd1db). Move it to the autobuild-<sha> release of
the fork's main once that PR lands.
@robobun

robobun commented Aug 23, 2026

Copy link
Copy Markdown
Collaborator Author

Status: WEBKIT_VERSION points at autobuild-preview-pr-501-fabdd1db, the preview release of oven-sh/WebKit#501 (published 2026-08-23 22:22 UTC, all 42 platform tarballs). Local verification: a debug build against the merged tree passes the suites listed in the description; the new test/js/bun/jsc/webkit-upgrade-55d9d9007f.test.ts fails on the current pin and passes on this build.

Before merge: land oven-sh/WebKit#501 as a merge commit, then move WEBKIT_VERSION to the autobuild-<sha> release the fork's main produces.

@coderabbitai

coderabbitai Bot commented Aug 23, 2026 •

Copy link
Copy Markdown
Contributor

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: f88a2847-e340-4b71-86d4-123d5316bf8c

📥 Commits

Reviewing files that changed from the base of the PR and between a03a0ec and 6b3366c.

📒 Files selected for processing (11)
  • scripts/build/deps/webkit.ts
  • src/js/builtins.d.ts
  • src/js/node/_http_server.ts
  • src/js/node/dgram.ts
  • src/js/node/events.ts
  • src/js/node/util.ts
  • src/jsc/bindings/EncodeURIComponent.cpp
  • test/js/bun/jsc/webkit-upgrade-55d9d9007f.test.ts
  • test/js/node/dgram/node-dgram.test.js
  • test/js/node/events/event-emitter.test.ts
  • test/js/node/util/test-aborted.test.ts

Included review availability: Your plan provides up to 5 included reviews per hour; 3 remain after this review.


Walkthrough

Changes

The pull request replaces Promise capability usage with pending-promise and first-settlement intrinsics across Node HTTP, datagram, events, and abort utilities. It also updates the WebKit preview version, adds an encoding include, and adds JavaScriptCore and Node regression tests.

Promise settlement intrinsics

Layer / File(s) Summary
Promise intrinsic contract
src/js/builtins.d.ts
Adds $newPromise and guarded resolve/reject declarations. Removes $newPromiseCapability.
HTTP handoff and request promises
src/js/node/_http_server.ts
HTTP handoff and request completion paths create pending promises and resolve them through first-call guards.
Node API settlement and validation
src/js/node/dgram.ts, src/js/node/events.ts, src/js/node/util.ts, test/js/node/dgram/*, test/js/node/events/*, test/js/node/util/*
Datagram disposal, EventEmitter.once, and aborted() use guarded settlement. Tests cover close ordering, listener cleanup, repeated settlement, and multiple abort waiters.

WebKit upgrade coverage

Layer / File(s) Summary
WebKit version and regression coverage
scripts/build/deps/webkit.ts, src/jsc/bindings/EncodeURIComponent.cpp, test/js/bun/jsc/*
Updates the WebKit preview reference, adds the hexadecimal encoding header, and adds regression tests for Promise.try, base64 decoding, WebAssembly import descriptors, and JIT overflow checks.

Suggested reviewers: alii, jarred-sumner

🚥 Pre-merge checks | ✅ 4
✅ Passed checks (4 passed)
Check name Status Explanation
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Title check ✅ Passed The title clearly identifies the primary change: upgrading WebKit to commit 55d9d9007f.
Description check ✅ Passed The description explains the problem, fix, background, affected areas, tests, and verification results in sufficient detail.

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

* From https://github.com/oven-sh/WebKit releases.
*/
export const WEBKIT_VERSION = "aea1f010b69783c0fc1ff24ff663691abe642c16";
export const WEBKIT_VERSION = "autobuild-preview-pr-501-fabdd1db";

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.

🔴 WEBKIT_VERSION is pinned to autobuild-preview-pr-501-fabdd1db, an ephemeral per-push preview tag that will be pruned once oven-sh/WebKit#501 closes. As the PR description already notes, this must be updated to the fork-main autobuild-<sha> release once #501 lands, before this PR merges — flagging here as a merge-gate so it isn't accidentally landed in this state.

Extended reasoning...

What the issue is

scripts/build/deps/webkit.ts:6 sets WEBKIT_VERSION = "autobuild-preview-pr-501-fabdd1db". This is a per-push preview tag produced by oven-sh/WebKit#501's CI: every push to that PR creates a new autobuild-preview-pr-501-<head-sha-8> tag, and these preview releases are pruned once the PR merges or closes. The PR description itself is explicit about this:

Before this PR merges, WEBKIT_VERSION has to move to the autobuild-<sha> release that the fork's main produces once oven-sh/WebKit#501 lands.

This comment is not surfacing a defect the author missed — it is the merge-gate the author already asked for, so the PR cannot be accidentally approved/merged before the tag is swapped.

The code path that would break

prebuiltUrl() in the same file constructs the download URL as https://github.com/oven-sh/WebKit/releases/download/${tag}/bun-webkit-${os}-${arch}${suffix}.tar.gz, where tag is WEBKIT_VERSION verbatim when it starts with autobuild-. Every CI lane and every developer running with cfg.webkit === "prebuilt" (the default) fetches from that URL. prebuiltDestDir() also keys the extraction cache on the same string.

Step-by-step: what happens if this merges as-is

  1. This PR merges to main with WEBKIT_VERSION = "autobuild-preview-pr-501-fabdd1db".
  2. Upgrade to upstream WebKit 55d9d9007f WebKit#501 subsequently merges to the fork's main, producing the stable autobuild-<merge-sha> release.
  3. The fork's release automation prunes the now-obsolete autobuild-preview-pr-501-* releases (or a later push to Delete incorrect gitignore file #501 before it merges replaces fabdd1db with a new head sha).
  4. Every fresh Bun build — CI on every subsequent PR, and every developer without a cached webkit-preview-pr-501-fabdd1db* directory — hits a 404 on the tarball download and fails at the WebKit dependency step.
  5. Because prebuiltDestDir() keys on the tag string, developers who do have a cached extraction keep working, masking the breakage locally while CI is red for everyone.

Why nothing else prevents it

There is no fallback URL, no checksum pinning, and no guard in scripts/build/deps/webkit.ts that distinguishes a preview tag from a stable one. The only mechanism preventing this from landing is human review — hence this comment.

Fix

Once oven-sh/WebKit#501 lands on the fork's main, update this line to the resulting autobuild-<40-hex-sha> (or the full commit sha, per the file's own comment header) and re-push. No other change is needed; prebuiltUrl() and prebuiltDestDir() already handle both the autobuild--prefixed and bare-sha forms.

On severity

Two of three verifiers rated this normal; one rated it nit on the grounds that it restates what the author already wrote. Both are defensible. I'm keeping normal because the rubric reserves it for "merging without the fix causes a concrete failure" — which this does (every prebuilt download 404s) — and because the author's own description asks for exactly this gate. It is a reminder, not a criticism.

Comment on lines +59 to +61
test("node:dgram Symbol.asyncDispose closes the socket and resolves after 'close'", async () => {
const socket = dgram.createSocket("udp4");
await new Promise(resolve => socket.bind(0, "127.0.0.1", resolve));

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.

🟡 The bind promise has no rejection path: Socket.prototype.bind's callback only fires on 'listening', so if bind fails the awaited promise never settles and the test hangs to timeout with the socket leaked. Wire socket.once('error', reject) (or Promise.withResolvers() + both events). This matches the file's existing convention and bind(0, '127.0.0.1') failing is implausible, so not blocking — just the REVIEW.md "wire EVERY failure event to reject" rule.

Extended reasoning...

What the bug is

The new test at test/js/node/dgram/node-dgram.test.js:61 awaits bind completion via:

await new Promise(resolve => socket.bind(0, "127.0.0.1", resolve));

This promise has only a resolve path. Looking at Socket.prototype.bind in src/js/node/dgram.ts, when a callback is passed it registers two listeners:

function removeListeners() {
  this.removeListener("error", removeListeners);
  this.removeListener("listening", onListening);
}
function onListening() {
  removeListeners.$call(this);
  cb.$call(this);
}
this.on("error", removeListeners);
this.on("listening", onListening);

On 'error', removeListeners detaches both listeners but never invokes cb. Because an 'error' listener is installed, emit('error', ...) also does not throw — the error is silently absorbed and the callback is dropped.

The code path that triggers it

  1. socket.bind(0, "127.0.0.1", resolve) installs removeListeners on 'error' and onListening on 'listening'.
  2. Bind fails (e.g. startBunSocket rejects, or the DNS lookup errors) → state.bindState = BIND_STATE_UNBOUND; this.emit('error', err).
  3. removeListeners runs, detaches both listeners, returns. cb (i.e. resolve) is never called.
  4. The awaited promise never settles. The test hangs until the harness timeout fires.
  5. The socket is not guarded by using/try-finally, so it leaks on that path too.

Why existing code doesn't prevent it

The 'error' listener that bind() installs is precisely what prevents the emit from throwing synchronously — it swallows the error rather than propagating it. There is no socket.once('error', reject) in the test, and no cleanup guard around the socket.

Impact

If bind(0, '127.0.0.1') on a udp4 socket ever failed on a CI host, the test would hang to the harness timeout with no diagnostic (the error is absorbed, not surfaced), and the socket handle would leak into subsequent tests. REVIEW.md is explicit on this: "Wire EVERY failure event (error, close, abort, process exit) to reject the awaited promise" and "Release every resource via using/await using or try/finally registered BEFORE the assertions."

That said, the trigger is essentially implausible — binding a udp4 socket to loopback on an ephemeral port cannot realistically fail on any functioning host — and the identical pattern already exists in this file (the subprocess fixture at line ~18 does await new Promise(r => rx.bind(0, "127.0.0.1", r)), and the IPv6 tests at lines ~75/~87 never close their sockets at all). So this is a pattern-rule nit, not a merge blocker.

Step-by-step proof

Concrete walk-through with a forced failure:

  1. const socket = dgram.createSocket("udp4") — state.bindState = UNBOUND, handle present.
  2. new Promise(resolve => socket.bind(0, "127.0.0.1", resolve)) — bind() sets bindState = BINDING, installs on('error', removeListeners) and on('listening', onListening), then calls state.handle.lookup("127.0.0.1", ...).
  3. Suppose Bun.udpSocket({...}) rejects (e.g. EMFILE). startBunSocket's rejection handler runs: state.bindState = BIND_STATE_UNBOUND; self.emit('error', err).
  4. emit('error', err) finds one listener (removeListeners), calls it. removeListeners removes itself and onListening, returns. Because a listener existed, emit returns true — no throw.
  5. resolve was never called; reject was never wired. The await in the test never returns.
  6. Harness timeout fires; socket is still live (bindState UNBOUND, handle present, no close() in a finally).

Fix

const socket = dgram.createSocket("udp4");
const { promise, resolve, reject } = Promise.withResolvers();
socket.once("error", reject);
socket.bind(0, "127.0.0.1", resolve);
await promise;

or equivalently await new Promise((resolve, reject) => { socket.once('error', reject); socket.bind(0, '127.0.0.1', resolve); }), and/or wrap the socket in a try { ... } finally { socket.close?.() } so it's released on failure.

robobun added a commit that referenced this pull request Aug 24, 2026
…sync

Four cases that fail at the current pin and pass on the oven-sh/WebKit#503
build: Promise.try returns a native promise as-is, setFromBase64 on a
zero-length target reads nothing, WebAssembly.Module.imports() descriptors
have the spec shape, and the DFG keeps the overflow check of an unused
++ / --. Carried over from #40263.
@robobun

robobun commented Aug 24, 2026

Copy link
Copy Markdown
Collaborator Author

Closing as a duplicate of #40276. Both PRs carry the same Bun-side changes (the $newPromiseCapability port in src/js, the builtins.d.ts declarations, the <wtf/HexNumber.h> include, the events, util and dgram tests). The two upstream targets differ by one WebCore-only commit: 55d9d9007f is 8c4fd56347 plus the MediaElementAudioSourceNode use-after-free fix, which the JSCOnly port does not compile. For JavaScriptCore the two merges are the same.

#40276 keeps going because its CI run is green (Build #104490) and oven-sh/WebKit#503 is the WebKit PR that stays open. The behavior test file from this PR is carried over there as test/js/bun/jsc/webkit-upgrade-8c4fd56347.test.ts. oven-sh/WebKit#501 is closed for the same reason.

@robobun robobun closed this Aug 24, 2026
robobun added a commit that referenced this pull request Aug 25, 2026
…sync

Four cases that fail at the current pin and pass on the oven-sh/WebKit#503
build: Promise.try returns a native promise as-is, setFromBase64 on a
zero-length target reads nothing, WebAssembly.Module.imports() descriptors
have the spec shape, and the DFG keeps the overflow check of an unused
++ / --. Carried over from #40263.
sosukesuzuki pushed a commit that referenced this pull request Aug 25, 2026
### Problem
- Bun's WebKit pin `aea1f010b6` is 412 upstream commits behind
`8c4fd56347`, 90 of them in JavaScriptCore, WTF or bmalloc.
oven-sh/WebKit#503 merges that range into the fork.
- Upstream removed the `@newPromiseCapability` private builtin
(`38027ff0ec`). Six call sites in Bun's bundled modules use it, so
`node:events`, `node:util`, `node:dgram` and the HTTP server fail to
load (`Private symbol not found: newPromiseCapability`).
- #40263 and oven-sh/WebKit#501 were a parallel attempt at `55d9d9007f`,
one WebCore-only commit ahead. Both are closed in favor of this pair.

### Fix
- oven-sh/WebKit#503 is merged. `WEBKIT_VERSION` is
`cb61607f1a4bae79d7701965062634dee9efb349`, its merge commit on the
fork's main (release
`autobuild-cb61607f1a4bae79d7701965062634dee9efb349`, 42 tarballs). That
commit is the preview build this PR was tested against (`d2654c3b`) plus
oven-sh/WebKit `a0a80b2276` (an optional depth bound on
`recursivelyGenerateUnlinkedCodeBlockForProgram/ForModuleProgram`).
- The six call sites create their promise with `$newPromise()` and
settle it with `$resolvePromise` / `$rejectPromise`, or the
`...WithFirstResolvingFunctionCallCheck` variants where a second settle
is possible. `builtins.d.ts` follows.
- `EncodeURIComponent.cpp` includes `<wtf/HexNumber.h>` itself (upstream
`314133b7a6` no longer does).
- Verified: `test/js/bun/jsc/webkit-upgrade-8c4fd56347.test.ts` pins
four JavaScript-visible engine changes that fail at the current pin. The
events, util and dgram tests cover the ported settlement paths.

### Background
- Bun links a prebuilt JavaScriptCore from oven-sh/WebKit releases.
`scripts/build/deps/webkit.ts` names the release tag.
- Built-in modules (`src/js/`) go through JavaScriptCore's builtin
compiler. A `$name` call becomes the private name `@name`, which has to
exist in the engine.
- `$newPromise` creates a pending promise. `$resolvePromise` /
`$rejectPromise` settle it and require it to be pending. The
`...WithFirstResolvingFunctionCallCheck` variants ignore calls after the
first, like a Promise executor's functions.

<details><summary>Notes</summary>

- Duplicate resolution: oven-sh/WebKit#501 and #503 have the same
structure (main at `62f427b86f`, then #488's head `d0fae3b3c9`, then
upstream/main) and the same `CachedTypes.cpp` resolution (the two files
differ in comments and an unused alias). WTF and bmalloc are identical.
The only upstream difference is WebCore's `55d9d9007f`
(`MediaElementAudioSourceNode` use-after-free), which the JSCOnly port
does not compile. #503 was kept because this PR's CI run was green
(Build #104490). The test file `webkit-upgrade-8c4fd56347.test.ts` is
carried over from #40263.
- oven-sh/WebKit#488 (upstream `baf4a9a7ec0b`) stopped merging after the
fork's bytecode cache rework (#490, #493, #494, #497). The per-commit
review of the upstream range (API and ABI changes, behavior changes,
performance) and the conflict resolutions are in oven-sh/WebKit#503. The
new conflict in this round is `CachedTypes.cpp`: the fork's new code
block record layout against upstream moving the global-only fields
(`features`, `lineCount`, source URL directives) to
`UnlinkedGlobalCodeBlock` and deleting `m_jumpTargets`.
- The ported call sites (the changes of #40054, carried over):
`node:events` (`once`), `node:util` (`aborted`), `node:dgram`
(`Symbol.asyncDispose`) and the HTTP server (CONNECT, Upgrade, the
per-request completion promise). `builtins.d.ts` declares `$newPromise`,
`$resolvePromiseWithFirstResolvingFunctionCallCheck` and
`$rejectPromiseWithFirstResolvingFunctionCallCheck` and drops
`$newPromiseCapability`. `@newPromise` is a bytecode intrinsic and
`@resolvePromise` / `@rejectPromise` are link-time constants, so they
exist in every engine build. `util.aborted` registers and unregisters
its `FinalizationRegistry` entry with the same token (the promise).
- Behavior changes in the upstream range that are visible from
JavaScript: `Promise.try` follows the updated spec (`PromiseResolve`
instead of `NewPromiseCapability`); the module map no longer caches
fetch failures, so a second `import()` of a specifier whose load failed
re-runs Bun's module loader instead of rejecting with the cached error;
`Uint8Array.prototype.setFromBase64` on a zero-length target returns `{
read: 0, written: 0 }` without validating the input;
`WebAssembly.Module.imports()/exports()` descriptors drop the
non-standard `type` field; re-exported imported Wasm globals and tags
keep object identity; a DFG `++`/`--` on an `int32` that overflows with
an unused result now deoptimizes instead of wrapping (`7711916200`). The
first, third, fourth and last of these are pinned by
`test/js/bun/jsc/webkit-upgrade-8c4fd56347.test.ts`.
- Performance changes of note: `SymbolTableEntry` no longer allocates a
`WatchpointSet` per watched variable until the DFG watches it
(`cea233cede`); `Object.assign` with several sources clones the first
one through `objectCloneFast` (`96ca975b2a`); `JSON.parse` allocates
arrays once at their final size; `TypedArray.prototype.sort()` without a
comparator uses a radix sort for 2/4/8-byte element types; `Map`/`Set`
`forEach` is inlined in the DFG and FTL; `RegExp` cells shrink from 96
to 80 bytes; `UnlinkedFunctionCodeBlock` shrinks from 216 to 192 bytes.
- `src/jsc/bindings/NodeVMSyntheticModule.cpp` calls
`SymbolTable::set(NoLockingNecessary, ...)`. After `cea233cede` only the
locked overload exists. `NoLockingNecessary` converts to a
`ConcurrentJSLocker`, so the call compiles unchanged.
- The upstream change to Linux thread scheduling (per-QOS
`sched_setattr` on every WTF thread, `SCHED_BATCH` compiler threads on
hosts with 4 or fewer cores) is gated off for Bun in the fork: Bun's
threads keep inheriting the process scheduling attributes.
- Suites run on a local debug + ASAN build against the merged WebKit
(`bun run build:local`): `test/js/bun/jsc`, `bun/jsc-stress` (116/116),
`node/events`, `node/util`, `node/dgram`, `node/vm`, `node/module`,
`bun/resolve`, `node/worker_threads`, `bun/wasm`, `web/url`,
`web/atomics`, `node/http/node-http-connect`, `node/async_hooks`,
`node/string_decoder`, `bundler/bundler_compile`,
`bundler/bun-build-api`: 3,548 pass. The failures are 5 s timeouts under
debug + ASAN, this machine's IPv6 multicast `ENODEV`, and one test that
fails the same way at the current pin. `bun build --bytecode` output
from that build loads and runs. A debug + ASAN build against the
`autobuild-preview-pr-503-311eab61` prebuilt runs
`test/js/bun/jsc/webkit-upgrade-8c4fd56347.test.ts`,
`test/js/bun/jsc/webkit-upgrade-3722912f.test.ts`,
`node/events/event-emitter.test.ts` and
`node/util/test-aborted.test.ts`: 106 pass.
- Every push to oven-sh/WebKit#503 produces a new preview tag
(`autobuild-preview-pr-503-<first 8 of the head sha>`), and this PR's
`WEBKIT_VERSION` follows it. CI lanes that fetch the prebuilt fail on
the download until that tag's Actions run has published the release.
- Rebase over #40201: Bun main moved its pin to the fork's
`c148a12dd82b` and calls the bytecode APIs that release added
(`EncoderStringTable`, persistent payloads). The `311eab61` preview
predates them, so the branch could not rebase until oven-sh/WebKit#503
merged the fork's main (head `d2654c3b`, 0 commits behind). The rebase
itself conflicted only on the `WEBKIT_VERSION` line. A debug + ASAN
build against `autobuild-preview-pr-503-d2654c3b` passes
`webkit-upgrade-8c4fd56347.test.ts`,
`node/events/event-emitter.test.ts`, `node/util/test-aborted.test.ts`,
`node/dgram` (except the IPv6 multicast `ENODEV` of this machine),
`node/http/node-http-connect.test.ts`, `web/atomics`, `web/url`,
`node/string_decoder` and `test/js/bun/jsc`. The compiled-executable
bytecode paths of #40201 work against it: the aliasing run keeps 12 MB
of instruction streams out of anonymous memory and 45 internal modules
load from embedded bytecode. The failures on this machine are the DOMJIT
hot loops and two `bun-build-compile` tests that exceed their timeouts
under debug + ASAN (the compile alone takes 5 to 47 s here), and the
nested `node-http-connect.node.mts` run that takes 5.0 s against a 5 s
limit.
- The `$newPromiseCapability` call in `src/node-fallbacks/events.js`
(the browser polyfill, not a JSC builtin) is a pre-existing bug and was
reported separately by #40054.
</details>

<!-- robobun:evidence:begin -->

---

**no test proof** · iteration 2 · platform-specific test(s) that do not
run on this machine, deferring to CI, which covers all platforms:
test/js/node/dgram/node-dgram.test.js

<!-- robobun:evidence:end -->
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.

2 participants