Skip to content

Upgrade to upstream WebKit 8c4fd56347 - #503

Merged
sosukesuzuki merged 1265 commits into
mainfrom
bun/upgrade-to-8c4fd56347
Aug 25, 2026
Merged

Upgrade to upstream WebKit 8c4fd56347#503
sosukesuzuki merged 1265 commits into
mainfrom
bun/upgrade-to-8c4fd56347

Conversation

@robobun

@robobun robobun commented Aug 23, 2026

Copy link
Copy Markdown
Collaborator

Merges upstream WebKit main at 8c4fd56347 (2026-08-23): 412 commits since the previous merge base 47f7250137c6 (2026-08-16), 90 of them touching JavaScriptCore, WTF or bmalloc.

This supersedes #488 (upstream baf4a9a7ec0b), which stopped merging once the bytecode cache rework landed on main (#490, #493, #494, #497). The branch is main, then a merge of #488's head d0fae3b3c9 (its conflict resolutions and its per-commit review are carried over below), then a merge of upstream/main. git merge-base origin/main upstream/main still reports 3722912ff800 because #455 was squash-merged; going through #488's merge commit (second parent baf4a9a7ec0b) gives the right base, so no graft was needed this time. The last merge commit of this PR has 8c4fd56347 as its second parent, so the next upgrade does not need one either as long as this lands as a merge commit rather than a squash.

Conflict resolutions

New in this PR (main's bytecode cache rework against upstream 3d0e6c14b1 and 0b92c2bcd0, and one hunk of cea233cede):

  • runtime/CachedTypes.cpp: main (Bytecode cache: smaller payloads that page in only what is decoded (alias mapped bytes, region layout, 32-bit offsets, sparse metadata table) #494, Bytecode cache: arrays-first code block records, strings deduplicated by contents, geometric encoder pages #497) writes a code block as one checksummed region with a varint tail of scalars and an optional CachedCodeBlockExtras; upstream moved m_features, m_lexicallyScopedFeatures, m_hasCapturedVariables, m_lineCount, m_endColumn and the two source-URL directives from UnlinkedCodeBlock to UnlinkedGlobalCodeBlock (3d0e6c14b1) and deleted UnlinkedCodeBlock::m_jumpTargets (0b92c2bcd0). Resolution: those five scalars leave the per-block varint tail (the flag word is renumbered), the two directives leave CachedCodeBlockExtras (which now holds only rareData and outOfLineJumpTargets), and a CachedGlobalCodeBlock<T> record sits between CachedCodeBlock<T> and the Program/Module/Eval records and writes those seven members as the first of the record's own members through the fork's encodeOwnMembers/decodeOwnMembers chain, so function records (nearly all of them) no longer carry them. The jumpTargets array is gone from Layout, packLayout/readTail, create, decode and regionIsIntact (three shareable arrays instead of four). The payload shape changes, which computeJSCBytecodeCacheVersion() already invalidates.
  • runtime/JSGlobalObject.cpp (cea233cede): a whitespace-only conflict in addStaticGlobals; upstream's InlineWatchpointSet* is taken.
  • CodeCache.cpp, UnlinkedCodeBlock.h, UnlinkedFunctionExecutable.cpp, BytecodeRewriter.cpp, jsc.cpp, OptionsList.h: main's and Upgrade to upstream WebKit baf4a9a7ec0b #488's edits merged without conflict; the result compiles and the disk cache round-trips (see Verification).

Merge of main 94c452c6e9 (2026-08-25, merge commit d2654c3b39), after #499, #502, #504, #506, #507 and #508 landed on main:

  • bytecode/UnlinkedCodeBlock.cpp: main reports the expression info to the GC through byteSizeForGCPacing(); upstream 0b92c2bcd0 deleted m_jumpTargets. Both taken.
  • runtime/CodeCache.cpp: main's depth bound on generateUnlinkedCodeBlockForFunctions (Bytecode cache: entry kind for an embedder's builtin function executables #502) is applied to upstream's c823243598 shape of that function (one generate lambda that picks CodeForConstruct through isClassConstructorFunction()). Same behaviour as main's version of the loop.
  • runtime/CachedTypes.cpp: main (Bytecode cache: numbered strings with stored hashes, so repeat lookups skip the atom table #506) made sourceURLDirective() and sourceMappingURLDirective() on CachedCodeBlock reuse the parsed tail; this branch had already moved the two directives to CachedGlobalCodeBlock (upstream 3d0e6c14b1), so those two accessors are gone. Everything else from main (numbered strings, inline and external strings, ActiveTailScope, BuiltinFunctionCacheEntry, the JSBigInt algorithms) merged without conflict.
  • Checked on the merged tree: bun run jsc:build:debug builds. The disk-cache round-trip described under Verification (4 bytecode-cache-*.js, the 105 top-level JSTests/modules tests, 400 random JSTests/stress tests, each run plain, with JSC_forceDiskCache=true, and with JSC_diskCachePayloadIsPersistentForTesting=true) passes 396 files and skips 105 that declare their own options. The 8 that fail are in the categories listed under Verification: forced-cache mode on a module served from the in-memory CodeCache (caching-should-not-make-ambiguous.js, namespace-ambiguous.js, ASSERTION FAILED: codeBlock in CodeCacheMap::fetchFromDisk), five module tests that check the fork's error wording (different-view.js, fallback-ambiguous.js, import-error.js, indirect-export-error.js, namespace-error.js, same failure without a cache), and stack-overflow-in-scope-with-catch.js, which needs more than 3 minutes under debug + ASAN. No decode failure or checksum mismatch. encodeBuiltinFunction / decodeBuiltinFunction (Bytecode cache: entry kind for an embedder's builtin function executables #502) round-trip a builtin with nested, class, async and generator functions through the jsc shell's builtinFromBytecodeCache at depths 0, 1, 2 and unbounded. Main's new JSBigInt tests (bigint-multiply-karatsuba.js, bigint-multiply-toom-fft.js, bigint-divide-burnikel-ziegler-barrett.js, bigint-parse-large.js, bigint-tostring-divide-and-conquer.js, the six bigint-terminate-*.js with their watchdog options) and the upstream tests listed under Verification pass. Bun was not rebuilt against this merge; the preview build this push produces is what the companion Bun PR should pin.

Carried over from #488 (47f7250..baf4a9a):

  • yarr/YarrJIT.cpp: kept at the fork's version (Yarr: JIT lookbehinds, first-code-point alternation dispatch, Boyer-Moore and /u·/v spec alignment, RegExp correctness fixes #299 policy, same as Upgrade to upstream WebKit 47f7250137c6 #455). Upstream's 01f04c4fb4 (JIT lookbehinds that contain backreferences) restructures the backreference codegen that the fork already reworked for mirrored frames; the fork compiles these patterns already. Upstream's three new tests (regexp-lookbehind-jit-backreferences.js, regexp-lookbehind-jit-backreferences-unicode.js, regexp-backreference-greedy-non-bmp-capture-restore-pos.js) pass on this build in JIT and --useRegExpJIT=false mode. 2165451fd3 (interpreter tryConsumeBackReference position restore) was already in the fork. de9231d86e (FixedVector ovector) merged cleanly.
  • WTF/URLParser.cpp and .h: kept at the fork's version. Upstream's eb44b6cfaa SIMD-scans path, query, fragment and opaque-path runs, which the fork's URLParser rewrite (0cbb4a1946, c6cfe90c60, eeab04040f) already does with its own table-driven scanners. Upstream's CodePointIterator::span()/advanceBy() additions and the two new TestWebKitAPI cases are in.
  • Atomics.wait termination (03e836de2f vs the fork's [JSC] Atomics.wait / memory.atomic.wait: wake up for a termination requested from another thread #432): WaiterListManager::waitSyncImpl keeps the fork's version (termination wake-up delivered under the waiter list lock, deferred termination respected, trap consumed and the TerminationException thrown before returning Terminated). The two callers take upstream's vm.throwTerminationExceptionIfNeeded(), which is a no-op when the exception is already pending. throwWasmToJSException's early-out for a pending TerminationException is in.
  • Module map (f324cca020): upstream no longer registers a module entry for a fetch failure and evicts a FetchFailed entry on the next loadModule. The fork's ModuleLoader: record a non-Error fetch rejection as a fetch failure #262 (setFetchError for a non-ErrorInstance fetch rejection in moduleLoadTopSettled) is dropped: that path no longer registers anything for any fetch failure, so the poisoned EvaluationFailed entry ModuleLoader: record a non-Error fetch rejection as a fetch failure #262 fixed cannot be created. Verified ModuleLoader: record a non-Error fetch rejection as a fetch failure #262's repro (import a file with a parse error, then import a file that statically imports it) completes on a Bun built against this tree. JSModuleLoader.h/.cpp: both sides' additions kept (removeFailedFetchEntry, public getRegisteredMayBeNull, the fork's registryEntry/removeEntry/clearAll/loadModuleSync).
  • CodeCache.cpp: upstream's c823243598 generates CodeForConstruct for class constructors via UnlinkedFunctionExecutable::isClassConstructorFunction(). The fork's "NOTE: We changed this in Bun" loop called isConstructor() on the UnlinkedFunctionExecutable, which resolves to JSCell::isConstructor() and is always false for a non-object cell, so it never generated anything but CodeForCall. Upstream's version replaces it. The fork's recordParseFromUnlinkedCodeBlock now takes an UnlinkedGlobalCodeBlock* because 3d0e6c14b1 moved the parse results off UnlinkedCodeBlock (follow-up commit d7ff6be).
  • SyntheticModuleRecord: db2d849cbe changes tryCreateWithExportNamesAndValues to take ArgList; the fork's lazy-export overloads (SyntheticModuleRecord: exports whose values are produced on first binding #408) follow. MarkedArgumentBuffer converts implicitly.
  • ThreadingPOSIX.cpp (9a18171a13, RT threads removed, per-QOS sched_setattr on Linux): the fork's !USE(BUN_JSC_ADDITIONS) main-thread detection branch keeps its guard and takes the new Thread(QOS, SchedulingPolicy, IsMain) constructor. Thread::updateSchedulingAttributes is a no-op under USE(BUN_JSC_ADDITIONS) on Linux: upstream now resets every WTF thread to SCHED_OTHER nice 0 with a 100% utilization clamp and puts JIT/Wasm compiler threads on SCHED_BATCH nice 10 when the host has 4 or fewer cores. Bun's threads keep inheriting the process's scheduling policy, nice level and clamps, as they did before this merge. Drop that hunk if following upstream is preferred. RealTimeThreads.cpp is deleted (the fork only added an include to it).
  • Options.h, JSWebAssemblyInstance.h: upstream's new includes (OSCheck.h, WasmLimits.h, <wtf/BitSet.h>) added in the fork's quoted-include style.
  • JSType.h, .github/workflows and the release tarball names are unchanged. PromiseOperations.js is gone upstream (see 38027ff0ec below): Bun's six $newPromiseCapability call sites move to $newPromise() plus $resolvePromise/$rejectPromise (or the ...WithFirstResolvingFunctionCallCheck variants) in the Bun PR.

Verification

  • bun run jsc:build:debug (Linux x64, debug + ASAN): builds, jsc shell runs.
  • Bytecode disk cache round-trip on the merged jsc: 509 JSTests files (every bytecode-cache-*.js, every top-level JSTests/modules test, 400 random JSTests/stress tests), each run plain with JSC_diskCachePath (writes the cache), then with JSC_forceDiskCache=true (must decode), then with JSC_diskCachePayloadIsPersistentForTesting=true as well (borrowed instruction bytes and metadata steps), outputs compared. 77 files declare their own options and were skipped; 414 pass all three ways; the 18 that fail do so identically on a jsc built from main 62f427b86f with the same configuration (cwd-relative load() paths, the fork's error-message wording in module tests, locale data the filtered libicudata only exposes through Bun's decompress hook, and forced-cache mode on modules whose code block was served from the in-memory CodeCache or never compiled because of a syntax error). No decode failure or checksum mismatch.
  • The tests upstream added in the new range pass (inc-dec-int32-overflow-dce.js with its requireOptions, object-assign-clone-multiple-sources.js, wasm/stress/omg-reduce-strength-select-exception-stackmap.js); inc-dec-int32-overflow-dce.js fails on main's jsc, as expected for a bug fixed in this range.
  • bun run build:local: Bun (main d43ddf309a plus the bun-side changes of the companion PR) links and runs against this tree. test/js/bun/jsc, bun/jsc-stress (116/116 incl. the FFI and Wasm BBQ/OMG fixtures), 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, regression/issue/26298: 3,548 pass; the only failures are 5-second timeouts under debug + ASAN (DOMJIT warm-up loops, worker termination, parseArgs stress), the IPv6 multicast ENODEV of this machine, and compile/HelloWorldWithProcessVersionsBun, which fails the same way on a debug build at the current pin (process.versions.bun keeps the -debug suffix).
  • The earlier checks from Upgrade to upstream WebKit baf4a9a7ec0b #488 (the 464 regexp/string/yarr stress tests in JIT and interpreter mode, Atomics.wait termination, ModuleLoader: record a non-Error fetch rejection as a fetch failure #262's module-map repro) were done on the baf4a9a7ec0b merge this PR carries; the upstream range since then touches none of those areas.

Upstream changes

Each remaining commit appears once, under the most specific heading that applies. The range is 47f7250137c6..8c4fd56347; the commits after baf4a9a7ec0b are marked with (new). The 18 commits in the range whose only engine-side footprint is a WebCore/WebKit preference entry, a WTF build-file list change, or Cocoa/GTK/WPE-only code the JSCOnly port does not compile are omitted (cb7daacd43, 7d86b2c8a0, 94f879afe6, 2f072fbfec, 3dfdbed8e4, 86a8860a42, 0c33544154, 722d0b38a9, cc7a6821b7, d8ad5b5f9a, b66aab8e21, 5e3cb10ac4, c2b3e55128, 2dce2395a4, d27b7fd89e, 6248fa7145, 8bd2ba0d3e, f0a1fd2b32).

API and ABI changes

  • cea233cede (new) — [PERF] [API] SymbolTableEntry no longer allocates a WatchpointSet when a variable is merely prepared for watching. Every closure variable its own function stores to (so every var/let/function/class binding and captured parameter) and every global variable gets prepareToWatch() when its CodeBlock is linked or the global is declared, which used to allocate a 16-byte FatEntry plus a 32-byte WatchpointSet per variable. The fat entry now holds an InlineWatchpointSet (one word that tracks ClearWatchpoint/IsWatched/IsInvalidated) that inflates to a real WatchpointSet only when the DFG adds a Watchpoint to it; LLInt, Baseline, DFG and FTL check the inline word first. Upstream measured a 250-module CommonJS server app going from 1,466 WatchpointSets at load to none, and only 376 of 1,752 inflating after DFG/FTL compilation. API: SymbolTableEntry's copy constructor and assignment are deleted (a copy is always the thin SymbolTableEntry::Fast, which SymbolTable::get()/inlineGet() and symbolTableGet() now return), watchpointSet() returns InlineWatchpointSet*, disableWatching() and the lockless SymbolTable::set(key, entry) overload are gone, and ResolveOp, ScopedArgumentsTable, symbolTablePutTouchWatchpointSet/symbolTablePutInvalidateWatchpointSet and SymbolTable::hasScopedWatchpointSet take InlineWatchpointSet*. Bun's one use (NodeVMSyntheticModule.cpp moves a fresh SymbolTableEntry(VarOffset(offset)) into set(NoLockingNecessary, ...)) is unaffected. The follow-up 0f924849f5 marks SymbolTableEntry::inflate JS_EXPORT_PRIVATE for the CMake (-fpch-codegen) build. Authored by Bun's Sosuke Suzuki.

  • 314133b7a6 (new) — [API] WTF: the UUID-backed flavour of ObjectIdentifier is removed (reverting 279402@main). ObjectIdentifierGenericBase and ObjectIdentifierGeneric lose their RawValue template parameter and are uint64_t-only again, UUIDObjectIdentifier is gone, the access-traits structs are no longer templates, and toRawValue() is folded back into toUInt64(); ObjectIdentifier<T>, AtomicObjectIdentifier<T> and the TextStream/CrossThreadCopier support keep their names and behaviour. Bun's ProcessIdentifier = ObjectIdentifier<ProcessIdentifierType> (src/jsc/bindings/ProcessIdentifier.h) and ScriptExecutionContext.h use the uint64_t form, so nothing changes on Bun's side. Also adds the <wtf/HexNumber.h> include that yarr/YarrJIT.cpp got through the removed header.

  • cffd5ed032 — [API][PERF] WTF::PriorityQueue now follows std::priority_queue conventions: the template takes a less-than functor type (default std::less<T>, function pointers are rejected by static_assert) instead of an isHigherPriority function pointer and serves the greatest element first; the isLessThan/isLessThanEqual/isGreaterThan/isGreaterThanEqual helpers were removed from wtf/MathExtras.h. This fixes Wasm::Worklist, whose comparator was never flipped when it moved off std::priority_queue (present in Bun's previous WebKit): compilation plans were served in reverse priority (Preparation before Compilation before Synchronous before Shutdown) and newest-first within a priority, and completePlanSynchronously bumped the plan with decreaseKey instead of increaseKey, so synchronous WebAssembly.Module/Instance compiles could wait behind queued async work. DFG inlining, Wasm inlining and the greedy register allocator were adapted to the new convention with no change in ordering; Bun has no direct PriorityQueue usage.

  • 9a18171a13 — [API][PERF] Linux thread scheduling in WTF is reworked: the RealTimeThreads/SCHED_RR machinery is removed (wtf/linux/RealTimeThreads.h renamed to HighPriorityThreads.h, rtkit nice-boosting only under USE(GLIB)), and every WTF::Thread now applies its QOS from its own entry point via sched_setattr: the default UserInitiated QOS maps to SCHED_OTHER, nice 0 plus SCHED_FLAG_UTIL_CLAMP_MIN = 1024 (a 100% DVFS frequency floor, with automatic fallback on kernels that reject uclamp), Default gets a 20% floor, Utility gets SCHED_BATCH/nice 10 and Background gets SCHED_IDLE/nice 19. JIT and Wasm worklist threads now pass compilerThreadQOS(), which is Utility on Linux hosts with <= 4 cores, so concurrent compilation is deprioritized on small machines/containers. API surface: the protected Thread ctor is now Thread(QOS, SchedulingPolicy, IsMain), AutomaticThread gains a ThreadQOS ctor overload, Thread::qos() is added and isRealtime() is derived from the scheduling policy, and ThreadingEnums.h adds ThreadSchedulingState, defaultThreadQOS, defaultThreadSchedulingPolicy, compilerThreadQOS(); Darwin and Windows behaviour is unchanged.

  • e1fdc80a34 — [API] WTF::weekDay(int32_t days) in wtf/DateMath.h is rewritten as a branchless unsigned multiply-and-shift (Ben Joffe's method) that is well-defined for the whole int32_t range (the old (days + 4) % 7 was signed overflow for days > INT32_MAX - 4, unreachable from JS since day counts are clipped to +-1e8), and the unused WTF::msToWeekDay(double) is removed. ISO8601::dayOfWeek (Temporal PlainDate/PlainDateTime dayOfWeek) drops its private Int128 copy of the modulo and uses daysFromYearMonth + WTF::weekDay. Results are unchanged (upstream checked all 2^32 inputs); authored by Bun's Sosuke Suzuki.

  • 75a9d414a4 — [API] (additive) Correctness fix for VM::deleteAllCode / Heap::deleteAllCodeBlocks: the VM-level m_syncResumeCallCache (the MicrotaskCallCache used to resume async functions/async generators from the microtask queue) lives outside any VMEntryScope and kept stale Executable->CodeBlock entry points after all code was detached, so a later resumption could call into a detached CodeBlock. Adds MicrotaskCall::clear(), MicrotaskCallCache::clear() and public VM::clearMicrotaskCallCaches(), which Heap::deleteAllCodeBlocks now calls; CodeCache::clear() also now calls write() first. Directly relevant to Bun, which calls vm.deleteAllCode(...) in WebWorker__teardownJSCVM, JSC__VM__deleteAllCode, and via the debugger's recompileAllJSFunctions path while async work may still be pending.

  • 3302d7a455 — [PERF][API] SourceProviderCache now pre-reserves its m_map hash map from the source length (sourceLength / 512 entries, capped at 64K) to avoid repeated rehash/realloc while a large script is parsed. The public default constructor is replaced by a private explicit SourceProviderCache(unsigned sourceLength) plus static Ref<SourceProviderCache> create(unsigned); VM::addSourceProviderCache is updated. Bun only includes the header (src/jsc/modules/BunJSCModule.h) and never constructs one directly, so no Bun changes are needed.

  • 1450e69522 — [API] WTF DequeIterator and DequeConstIterator gain defaulted default constructors (required by the C++20 bidirectional_iterator concept, and by MSVC's STL std::reverse_iterator, which value-initializes its stored iterator). Purely additive; Bun's many WTF::Deque uses (streams, JSBufferList, worker queues) are unaffected and this may help MSVC-STL builds that instantiate Deque::rbegin().

  • f2d69489c6 — Removes the unused tryAllocateBuffer helper from VectorBufferBase/VectorBuffer in wtf/Vector.h; allocateBuffer<FailureAction::Report> remains for the same purpose. [API] Technically a public-header removal, but Bun's src/ has no references to it, so no action needed.

  • 03e836de2f — [BEHAVIOR] Fixes a thread blocked in Atomics.wait / Wasm memory.atomic.wait never unblocking after VM::notifyNeedTermination(): WaiterListManager::waitSyncImpl now also observes the pending NeedTermination trap (not just hasTerminationRequest()), dequeues the waiter and returns Terminated, and the new VM::throwTerminationExceptionIfNeeded() materializes the TerminationException through handleTraps. This directly affects Bun, whose VmHandle::request_termination (used by worker.terminate()) fires exactly that trap, so a worker parked in Atomics.wait now terminates instead of hanging. [API] Adds VM::throwTerminationExceptionIfNeeded(); throwWasmToJSException now early-outs whenever a TerminationException is already pending.

  • 88b46e6bc7 — [API] wtf/BorrowedBytes.h is deleted and replaced by wtf/EscapableByteSpan.h: BorrowedBytes/BorrowedSpanScope/BorrowedVectorScope are gone, replaced by a stack-only, non-refcounted EscapableByteSpan (plus escapableSpan() helper and SpanUInt8/MutableSpanUInt8/VectorUInt8 aliases) that RELEASE_ASSERTs if a copy outlives the borrow. This is a Swift-interop type used only by PAL crypto on Cocoa; Bun has no references to either header, so no impact beyond the header rename in WTF_PUBLIC_HEADERS.

  • f324cca020 — [BEHAVIOR] [API] The module loader stops caching fetch failures: JSModuleLoader::loadModule now evicts a FetchFailed registry entry (new removeFailedFetchEntry) and re-fetches instead of re-rejecting with a duplicated copy of the old error, and the top-level rejection paths in JSMicrotask.cpp no longer call ensureRegistered just to stash an error (evaluation errors are still cached; maybeDuplicateFetchError is no longer used on that path). For Bun this means a second import() of a specifier whose fetch previously rejected (e.g. a missing file or failed loader) will invoke Bun's module-loader fetch hook again rather than rejecting immediately from the cache, so anything that relied on the cached rejection (tests, error identity) may see different behavior. JSModuleLoader::getRegisteredMayBeNull is also moved from private to public.

  • 905a225671 — [API] wtf/UniStdExtras.h is now wrapped in #if OS(UNIX), so setCloseOnExec/unsetCloseOnExec/dupCloseOnExec/closeWithRetry/setNonBlock are no longer declared on Windows (done to keep the Windows Swift C++ importer happy). Only matters if the embedder includes that header on Windows; Bun's bindings do not reference it.

  • 76b474b322 — [API] Refactors JSC::Wasm::TypeKind: the type-section forms Func, Struct, Array, Rec, Sub, Subfinal are removed from TypeKind and moved to a new generated Wasm::DefinedTypeKind enum (with isValidDefinedTypeKind / makeString(DefinedTypeKind)), and the section parser dispatches on that. Pure cleanup that removes dummy switch cases; no behavior change. Bun does not reference these enumerators, so nothing to update.

  • 218421554b — [PERF] [API] JSON.parse now buffers array elements on a MarkedArgumentBuffer element stack and allocates each JSArray once at its final length and indexing type (Int32/Double/Contiguous) instead of growing the butterfly via putDirectIndex per element; also skips two Structure loads for outOfLineCapacity when a property offset is still inline. Drive-by change to the inlined MarkedVector::append fast path (stays inline once heap-allocated and registered with a mark set, rather than calling slowAppend on every append) and an additive MarkedVector::shrink(size_t) — both are inlined into Bun's many MarkedArgumentBuffer users, but no signature changes. Also speeds up Bun's native JSONParse/JSONParseWithException callers (JSON module loading, .json(), SQL client, etc.).

  • 6446fdc8e0 — WebCore ISO-BMFF box serialization work; the only engine touch is JSC::DataView::get<T>() and DataView::read<T>() in runtime/DataView.h becoming const member functions. [API] Source-compatible (header-only inline templates, only adds const), so no embedder action needed.

  • 38027ff0ec — Removes the @newPromiseCapability private builtin/link-time constant from JSC (builtins/BuiltinNames.h, bytecode/LinkTimeConstant.h, host function in runtime/JSGlobalObject.cpp), since upstream builtins now use @newPromise + @resolvePromiseWithFirstResolvingFunctionCallCheck/@rejectPromiseWithFirstResolvingFunctionCallCheck (no tuple object allocation, no property lookups). [API] After this upgrade @newPromiseCapability no longer resolves as a private name. Bun's bundled modules called $newPromiseCapability(Promise) in src/js/node/dgram.ts, events.ts, _http_server.ts (3 sites) and util.ts; the Bun PR rewrites them to $newPromise() plus the $resolvePromise/$rejectPromise family. Note the preceding upstream commit 7f38ebbabf had already deleted builtins/PromiseOperations.js (the JS implementation); the C++ JSPromise::createNewPromiseCapability() used by src/jsc/bindings/JSMockFunction.cpp is unaffected.

  • 0b92c2bcd0 — [PERF] Deletes UnlinkedCodeBlock::m_jumpTargets (a per-label offset list that nothing in a shipping configuration read; DFG/liveness already recompute targets via computePreciseJumpTargets()), saving 4 bytes per label (~38 KB after loading typescript.js), shrinking UnlinkedFunctionCodeBlock from 216 to 208 bytes (GC cell 224 -> 208), and cutting the serialized bytecode cache by ~1.4%. [API] Removes CodeBlock::numberOfJumpTargets()/jumpTarget(), UnlinkedCodeBlock::numberOfJumpTargets()/jumpTarget()/lastJumpTarget(), UnlinkedCodeBlockGenerator::addJumpTarget() etc., and recomputePreciseJumpTargets(), adding a computePreciseJumpTargets(UnlinkedCodeBlock*, ...) overload — Bun's bindings use none of these. The CachedCodeBlock layout changes (field dropped from runtime/CachedTypes.cpp), but bun build --bytecode output is already keyed to the build via computeJSCBytecodeCacheVersion(), so old caches are rejected rather than misread.

  • 3d0e6c14b1 — [PERF][API] Memory: moves m_features, m_lexicallyScopedFeatures, m_hasCapturedVariables, m_lineCount, m_endColumn and the sourceURL/sourceMappingURL directive pointers from UnlinkedCodeBlock down to UnlinkedGlobalCodeBlock (only Program/Eval/Module code uses them), shrinking UnlinkedFunctionCodeBlock from 216 to 192 bytes (cell 224 -> 192; ~53 KB saved after loading typescript.js). The accessors codeFeatures(), allowDirectEvalCache(), lexicallyScopedFeatures(), hasCapturedVariables(), lineCount(), endColumn(), sourceURLDirective(), sourceMappingURLDirective() and recordParse() are removed from UnlinkedCodeBlock and now exist only on UnlinkedGlobalCodeBlock; UnlinkedCodeBlock::usesImportMeta() is deleted outright (Bun's bindings use none of these; Bun's sourceURLDirective() calls are on SourceProvider). The serialized CachedCodeBlock layout for the bytecode cache also changes (split into CachedCodeBlock + CachedGlobalCodeBlock), which is covered by Bun keying the cache version on BUN_WEBKIT_VERSION.

  • 0bbbac7b52 — [BEHAVIOR] [API] Wasm memory64 now works together with multi-memory: the WasmSectionParser check that rejected any module combining a 64-bit memory with multiple memories ("if using memory64 then multiple memories are illegal for now") is removed, so such modules now validate/compile/run instead of failing with CompileError. IPInt decides whether to zero-extend an i32 address per accessed memory using a new WTF::BitSet<Wasm::maxMemories> (JSWebAssemblyInstance::m_memoryIsMemory64Bits) instead of a single memory-0 flag. JSWebAssemblyInstance::cachedMemory0IsMemory64() and offsetOfCachedMemory0IsMemory64() are removed (replaced by offsetOfMemoryIsMemory64Bits()); Bun does not reference them.

  • 71144530c1 — [API] Bumps the vendored wtf/simdutf single-header from 8.2.0 to 9.0.0 (now requires C++17): simdutf::to_string(encoding_type) and implementation::name()/description() return std::string_view instead of std::string, full_result gains a padding_error field and the base64 decoders now report the = position as the error location for padding errors, the SIMDUTF_FEATURE_* macros become #ifndef-overridable, and the icelake (AVX-512) simdutf::find now masks its tail load so searching for NUL no longer false-matches past the end of the input. error_code values, struct result, and every function Bun's src/simdutf_sys shim (bun-simdutf.cpp, simdutf.rs Status constants) calls are unchanged, and Bun falls back to its scalar decoder on any base64 error rather than reading the error offset, so no Bun-side change is needed.

  • ee4b208267 — [PERF] [API] Generator/async function bodies whose wrapper has no parameters no longer allocate a 64-byte UnlinkedFunctionExecutable::RareData just to hold an empty parameter-name list (every async () => {...}, async function f() {}, function* g() {} paid this; ~6.4 MB saved per 100k such functions). UnlinkedFunctionExecutable::create and the BytecodeGenerator now take Vector<Identifier>&& instead of std::optional<Vector<Identifier>>&&; Bun does not call UnlinkedFunctionExecutable::create directly, so no action needed.

  • b1701b3489 — [BEHAVIOR] [API] Uint8Array.prototype.setFromBase64 on a zero-length target now returns { read: 0, written: 0 } without inspecting the input at all (even an invalid base64 string is accepted), matching step 3 of the proposal spec; Uint8Array.fromBase64 is unchanged. WTF::fromBase64() gains a required fifth parameter OutputSizeIsMaxLength (new enum in wtf/text/Base64.h); Bun does not call WTF::fromBase64 directly, so no source change is needed.

  • 870de4ab77 — WTF fix in wtf/text/TextBreakIterator.{h,cpp}: NonSharedCharacterBreakIterator and NonSharedSentenceBreakIterator leaked their ICU UBreakIterator when setTextForIterator() failed (reachable via a null StringView), and the 8-bit path skipped utext_close() on ubrk_setUText() failure. Ownership is now expressed with a new WTF::UBreakIteratorPtr (std::unique_ptr<UBreakIterator, ICUDeleter<ubrk_close>>) and m_iterator becomes that smart pointer; the public operator UBreakIterator*() is unchanged. [API] Only the private member type and the file-static helpers changed, so this is source-compatible for embedders, and Bun does not use these classes at all (JSC only references them from the offline CharacterPropertyDataGenerator tool), so it is a no-op for Bun in practice.

Behavior changes

  • 7711916200 (new) — [BEHAVIOR] (bug fix) DFG miscompile of ++/--: FixupPhase's Inc/Dec handler cleared NodeMustGenerate on the ArithAdd/ArithSub it produced in every case, so a CheckOverflow increment whose result was otherwise unused could be dead-code-eliminated together with its overflow check, and an int32 counter that wraps past 2^31 - 1 went unnoticed instead of deoptimizing to a double. The flag is now cleared only on the double-rep path. Test: JSTests/stress/inc-dec-int32-overflow-dce.js.

  • 9f07374e9e (new) — [BEHAVIOR] (bug fix) Wasm OMG: a call patchpoint emitted inside a try block carries a catch-restoration stackmap keyed by its CallSiteIndex, so it is now created with cloningForbidden(Patchpoint) like the throw/rethrow patchpoints already were, and B3's ReduceStrength Select specialization now bails when any value it would clone is cloning-forbidden (it previously only refused the Check itself). Before, Select specialization or DuplicateTails could leave two call sites sharing one exception stackmap with different live-value layouts. Test: JSTests/wasm/stress/omg-reduce-strength-select-exception-stackmap.js.

  • 3b9afb2b4f — [BEHAVIOR] Wasm correctness fix in BBQ and OMG (B3 lowering): ref.cast/ref.test on a nullable reference now only elides the explicit null check when Options::useWasmFaultSignalHandler() is on, so configurations running without the fault handler (e.g. Bun's Linux ASAN builds, where Options.cpp turns it off unless ASAN_OPTIONS contains allow_user_segv_handler=1) get a proper Wasm trap instead of dereferencing null. In signaling-memory mode, the decision to emit an explicit bounds check for large constant offsets now uses the last accessed byte (offset + size - 1) rather than the base offset; the old computation was slightly off but always still trapped via the PROT_NONE redzone.

  • b74760b182 — [BEHAVIOR] The non-ARM64 WTF::roundeven/roundevenf polyfill returned +0.0 for the tie that rounds to zero (roundeven(-0.5) gave +0.0 instead of -0.0); the result is now wrapped in copysign. In JSC this feeds BBQ's constant folding of Wasm f32.nearest/f64.nearest, so on x86_64 a constant -0.5 operand folded to the wrong sign of zero; ARM64 uses __builtin_roundeven (frintn) and was already correct.

  • 3c0f403d81 — [PERF] [BEHAVIOR] WasmGC array.fill / array.copy are reworked in BBQ and OMG: null and bounds checks are now emitted inline in JIT code, the runtime call is skipped entirely when size == 0 (observed to be very common), non-ref array.copy uses B3 MemoryCopy, and non-ref array.fill uses MemoryFill or new operationWasmArrayFill{2B,4B,8B,16B} (memset_pattern* on Darwin, std::ranges::fill elsewhere), while ref-typed arrays keep GC-safe loops (operationWasmArrayFillRefs / operationWasmArrayCopyRefs via gcSafeMemmove). Observable change: the trap message for a null array in these ops becomes the generic "access to a null reference" instead of "array.fill to a null reference" / "array.copy to a null reference" (the NullArrayFill / NullArrayCopy enumerators were removed from the internal Wasm::ExceptionType enum, which Bun does not reference). Old operationWasmArrayFill{,Vector} / operationWasmArrayCopy JIT operations are removed.

  • 2165451fd3 — [BEHAVIOR] Correctness fix in the YARR interpreter's tryConsumeBackReference: in Forward direction it speculatively advanced the input by matchSize before comparing, and when the captured character was non-BMP and the input at that offset was not a valid surrogate pair, the early errorCodePoint return only restored the position for Backward, so a failed greedy iteration consumed input. Fixes wrong results like /(\u{1F601})\1*$/u.exec("\u{1F601}ab") matching instead of returning null, and /(\u{1F601})(\1*)?/u splitting a surrogate pair; the check now falls through to the common mismatch path which restores the position in both directions. Only affects patterns that run in the interpreter (authored by Sosuke Suzuki from Bun).

  • e88b92e29f — [BEHAVIOR] (bug fix) Fixes a BBQ-tier miscompile of Wasm i64.mul_wide_s/i64.mul_wide_u on x86_64: the 128-bit product comes back in rdx:rax, and if the register allocator handed out rax/rdx as the result or scratch registers the halves were clobbered, producing wrong results. Now uses the same PREPARE_FOR_MOD_OR_DIV pattern as div/mod (clobber + lock rax/rdx). Only reachable when Options::useWasmWideArithmetic is on (default off, Bun does not enable it), so this matters only for users opting in via BUN_JSC_useWasmWideArithmetic=1.

  • 5cfff9fe44 — [BEHAVIOR] In JSC's Wasm ESM integration (WebAssemblyModuleRecord), immutable exported globals are now exposed in the module namespace as plain JS values instead of WebAssembly.Global wrappers, per the esm-integration spec/WPT; v128 and exnref globals stay TDZ, Instance.exports is unchanged, and wasm-to-wasm ESM imports fall back to the Instance.exports wrapper when the namespace binding is empty. No Bun-visible change today because Bun's .wasm loader (src/runtime/jsc_hooks.rs, L::Wasm arm) returns a file path / wasi-runner instead of creating a JSC Wasm module record; it would matter if Bun ever adopts JSC's native Wasm ESM path.

  • b7afce9416 — [BEHAVIOR] When several Wasm functions fail validation during concurrent compilation, the WebAssembly.CompileError message now deterministically reports the lowest-index failing function instead of whichever worker thread finished first; non-function failures (OOM/parse/cancel) still take precedence. Adds protected Plan::failAtFunction / EntryPlan::failFunctionCompilation and an m_errorFunctionIndex field; error messages become stable across runs (useful for snapshot tests).

  • 010e57fb4b — [BEHAVIOR] WebAssembly.Module.imports() / WebAssembly.Module.exports() descriptors no longer include the non-standard js-types-proposal type field unless Options::useWasmJSTypes() is enabled (default false, and Bun does not set it). At the merge base this field was emitted unconditionally and could throw for unrepresentable ref types; descriptors now have the spec shape {module, name, kind} / {name, kind}, matching V8/Node. Also tweaks the WasmJSTypesEnabled description in UnifiedWebPreferences.yaml.

  • 20c0bd7d62 — [BEHAVIOR] Wasm JS API spec fix: re-exporting an imported immutable WebAssembly.Global now yields the same JS object that was imported (instance.exports.g === importedGlobal), matching the WPT constructor-caching expectations; previously a fresh wrapper was created each time (mutable globals already preserved identity). JSWebAssemblyInstance gains an m_importedGlobalWrappers map (new setImportedGlobalWrapper/importedGlobalWrapper methods, visited by GC). Plain-number global imports still get a new wrapper.

  • 39de2d4eea — [BEHAVIOR] Wasm: importing a WebAssembly.Tag and re-exporting it now yields the same JS object (previously a fresh JSWebAssemblyTag wrapper was created on every export, so === identity failed), and locally defined tags are cached on first export so two export names share one wrapper. Implemented via a new m_tagWrappers map on JSWebAssemblyInstance that is visited by GC; no embedder-facing API change.

  • 7f38ebbabf — [BEHAVIOR] Promise.try follows the updated spec (Normative: make Promise.try use PromiseResolve in non-error case tc39/ecma262#3883): the result comes from PromiseResolve(C, value) / PromiseReject(C, error) instead of NewPromiseCapability(C), so Promise.try(() => p) now returns p itself when p is a native Promise, and a subclass constructor is invoked after the callback runs rather than before. builtins/PromiseOperations.js is deleted (@newPromiseCapabilitySlow gone) and @newPromiseCapability becomes a C++ link-time constant wrapping JSPromise::createNewPromiseCapability. Heads-up: follow-up upstream commit 38027ff0ec (also in this range) removes @newPromiseCapability entirely; see that entry under "API and ABI changes" for the Bun $newPromiseCapability(Promise) call sites that must be rewritten.

Performance

  • 96ca975b2a (new) — [PERF] Object.assign(target, a, b) with 2 to 4 sources goes through the batching path in objectConstructorAssign, which replayed the first source's shape one property at a time. It now tries objectCloneFast for the first source (the same structure-copy fast path a single-source Object.assign already used) and starts the per-source loop at the second source when that succeeds: 1.4x to 1.5x on upstream's object-assign-multiple-sources microbenchmarks. Tests: JSTests/stress/object-assign-clone-multiple-sources.js. Authored by Bun's Sosuke Suzuki.

  • 2cba652107 (new) — [PERF] Compile-time: Air::padInterference visited every instruction's arguments twice (needsPadding() then the early-def/late-use scan). Inst::paddingSummary() collects both answers in one forEachArg pass.

  • 958a13ecad — [PERF] Wasm BBQ emitIndirectCall computed the CallProfile address with addPtr(TrustedImm32, ...), which on large offsets (functions with many call profiles) silently spills through scratchRegister, clobbering the profile value loaded just before; it now does move + register addPtr. No observable behavior change, but affected call_indirect sites were left permanently un-optimized (polymorphic-callee detection never kicked in), so this restores expected tiering for large wasm functions.

  • 01f04c4fb4 — [PERF] YARR JIT now compiles backreferences inside lookbehinds (e.g. /(?<=(a)\1)b/); previously any such pattern forced the entire regex to the interpreter. The backward match compares the capture from its end down to its start, with checkNotEnoughInput becoming index >= length in Backward mode and case-insensitive, duplicate-named-group and surrogate-pair handling reused with the index direction flipped; forward codegen is unchanged. ~10.8x faster on the new regexp-lookbehind-backreference microbenchmark, no intended change in match results (authored by Sosuke Suzuki from Bun).

  • 7ef4708559 — [PERF] Parser: when parseFunctionInfo has already computed a function's free-variable list for the SourceProviderCache entry, that span is now handed to popScope, so Scope::collectFreeVariablesFrom (renamed from collectFreeVariables, internal to parser/Parser.h) no longer re-derives it by re-walking used/declared/lexical sets. Pure parse-time speedup for cacheable (>=16/8 byte) function bodies; SourceProviderCacheItemCreationParameters gains a freeVariableCount field and freeVariables() accessor. No behavior change.

  • 0ca8f08b89 — [PERF] Lexer::shift drops the 14-year-old "unconditionally zero m_current then overwrite" micro-optimization in favor of a plain if/else, which measures faster on current hardware. Semantics are identical; lexer hot-path only.

  • 24a6944b57 — [PERF] In Parser::parseMemberExpression, when the TreeBuilder is SyntaxChecker (the pre-parse/syntax-only pass), the new token start positions are now tracked by a counting placeholder instead of a Vector<JSTextPosition, 4>, since SyntaxChecker::createNewExpr ignores them anyway. Saves allocation/copy work for nested new chains during syntax checking; no behavior change.

  • a5fa580c84 — [PERF] B3 LICM (hoistLoopInvariantValues) can now hoist trapping loads (plain Load*, WasmStructGet, WasmArrayLength without fences) and control-dependent values out of loops that contain side exits, as long as no earlier "trap barrier" (write/fence/side-exit) precedes the value on the path from the loop header. Previously any side exit in the loop disabled hoisting of control-dependent values entirely; mainly benefits Wasm OMG and FTL code with bounds/null checks in loops. Adds Effects::isWrite()/isTrapBarrier().

  • 6fbae40455 — [PERF] FTL switch on a string that is an unresolved concatenation rope now calls a new noexcept operation (operationSwitchStringResolveRopeAndGetCharacters8) to resolve the rope, and if the result is 8-bit uses the inline binary-search StringSwitch on the resolved characters instead of falling back to the generic slow path. Resolution uses tryGetValue() so an OOM falls through to the slow path rather than throwing from the JIT operation.

  • de9231d86e — [PERF] RegExp::m_ovector and RegExp::RareData::m_captureGroupNames switch from Vector to FixedVector, shrinking the RegExp GC cell from 96 to 80 bytes and allocating exactly offsetVectorSize ints instead of Vector::resize's 16-element minimum capacity (64 bytes). Upstream measured per-RegExp cell+ovector going from 160 to 96 bytes and max RSS with 200k live RegExps dropping from 137.5 MB to 115.0 MB; estimatedSize now reports the exact ovector byteSize(). Authored by a Bun contributor.

  • 1c2c282d06 — [PERF] Options::computeNumberOfWorkerThreads now caps its result at 32 on non-Darwin platforms; in practice this only affects the default numberOfWasmCompilerThreads (previously cores - 1 with no cap; other callers already pass caps of 3 or 8), limiting it to 31 on Linux/Windows machines with more than 32 cores. Motivated by Wasm compilation OOMs on high-core-count, low-memory hosts; Bun does not override this option (it only sets numberOfGCMarkers), so large Linux servers will see fewer Wasm compiler threads and lower peak memory.

  • ce878e190f — [PERF] Map.prototype.forEach/Set.prototype.forEach get inline DFG and FTL lowering for MapIterationNext/MapIterationEntry/MapIterationEntryKey/MapIterationEntryValue, scanning the ordered hash table directly instead of making four C++ operation calls per element (the for-of MapIterator* nodes already did this and now share loadMapEntryData). Upstream microbenchmarks show 1.23x-1.65x speedups on forEach-heavy loops; the now-unreachable operationMap/SetIterationEntry* JIT operations are removed and the remaining operationMap/SetIterationNext become noexcept and only handle obsolete tables. Internal DFG operations only, no embedder-facing API or JS-visible behavior change (new stress tests cover mutation during iteration).

  • c823243598 — [PERF] The bytecode cache (generateUnlinkedCodeBlockForFunctions in CodeCache.cpp) now emits CodeForConstruct instead of CodeForCall for class constructors, so cached programs no longer reparse every class constructor body (and its nested functions) on the first new C(); upstream measured 253 -> 4 parses constructing 182 three.js classes and 133 -> 9 parses for babylon.js startup. Cache output grows by the previously missing constructor bodies (three.js +10.8%, a 50 MB bundle +0.5%), which is relevant to Bun's --bytecode builds and node:vm cached-data paths that use generateProgramBytecode/generateModuleBytecode. No API or JS behavior change.

  • 9516ed8a4f — [PERF] DFG/FTL switch on strings now handles 8-bit substring ropes inline by reading the base string's buffer at the substring offset, instead of bailing to the slow operationSwitchString call (which resolves the rope). Previously only flat 8-bit strings took the inline binary-switch path, so JIT'd code doing switch (s.slice(...)) / switch (s.substring(...)) gets faster. Internal-only signature rename in DFGSpeculativeJIT.h; no public API change.

  • a7a9e233ce — [PERF] The JSON.parse lexer accumulates the int32 value while scanning the digit run instead of re-walking the digits a second time in lexNumber; micro-optimization for integer-heavy JSON. No API or behavior change.

  • 6b13bfa7ba — [PERF] TypedArray.prototype.sort() without a comparator now uses an LSD radix sort (8-bit digits, stable passes, skipping digits with a single bucket) for 2/4/8-byte element types above measured thresholds (128 / 512 / 8192 elements), with a SIMD already-sorted pre-scan and a sampled few-distinct-values check that falls back to std::sort; upstream reports 2.3-4.6x on large Int16/Uint16/Int32/Uint32/Float32/Float64 arrays and 6.7x on presorted Int32 (geomean 1.5x). It temporarily allocates an O(n) scratch buffer (plus a snapshot copy for shared buffers) and falls back to std::sort on allocation failure; results are unchanged (NaN canonicalization matches the old sortFloat path). Also adds WTF::SIMD::bitXor/bitXor2 helpers to SIMDHelpers.h.

  • db2d849cbe — [PERF] Replaces heap-registering MarkedArgumentBuffer with a stack std::array<EncodedJSValue, N> wrapped in ArgList at every call site where the argument count is statically known (37 files: Promise resolve/reject/then/finally and microtask jobs, VM::callPromiseRejectionCallback, Proxy traps, JSON toJSON/reviver, RegExp/String Symbol.* protocol, iterator protocol, typed-array callbacks, Map/Set/WeakMap constructors, DFG slow paths). This trims per-call overhead and removes the never-hit hasOverflowed()/OOM checks whenever the engine calls back into JS from C++. No public API or JS-visible behavior change; the only signature change (SyntheticModuleRecord::tryCreateWithExportNamesAndValues now takes ArgList) is a private method Bun does not call.

  • eb44b6cfaa — [PERF] WTF::URLParser now SIMD-scans the path, opaque-path, query, and fragment states (via SIMD::find from wtf/SIMDHelpers.h) for the first delimiter, encode-set member, control, or non-ASCII code point and consumes the whole preceding run at once (skipped for canonical input, bulk-appended when building the output buffer) instead of looping per code point; upstream measures ~1.33x on 82k real-world URLs and 3-4x on ~100-char components. This directly speeds up every WTF::URL parse in Bun (new URL(), URLPattern, Request/fetch URL handling, module URL resolution). Additive-only header changes: CodePointIterator::span()/advanceBy() and a private URLParser::appendToASCIIBuffer(std::span<const char16_t>) overload; a consteval static_assert verifies each stop-character set covers its percent-encode set, so no parsing behavior changes.

  • 70eb5015ae — [PERF] Memory: the buffered-structures set, its lock and bufferingCountdown move from the PropertyInlineCache base class into RepatchingPropertyInlineCache, since handler ICs never buffer; sizeof(HandlerPropertyInlineCache) drops 112 -> 88 bytes per IC site in Baseline/DFG JITData (about -1.27 MB on Octane typescript). Side effect: handler ICs that fail to cache a structure now retry tryCache on the next few visits instead of skipping them; repatching ICs are unchanged. Internal JIT data structure only, not referenced by Bun.

  • 1969cf2d9e — [PERF] StringImpl::replace(char16_t, char16_t) (behind String::replace(char, char)) now finds the first occurrence with SIMD WTF::find() instead of a scalar byte-at-a-time loop, for both 8-bit and 16-bit strings; same fix already applied to makeStringByReplacingAll. No behavior change.

  • 2d0855229c — [PERF] WTF::copyElements (used throughout string building/concatenation) now handles 2..32-byte copies with inlined overlapping unaligned loads/stores instead of calling libc memcpy, whose size dispatch costs more than the copy itself for short runs. The B3 operationMemoryCopy fast path is hoisted into new WTF helpers copySmallMemory<min,max>(), copyOverlappingEnds, copyOverlappingEndPairs, WidestUnalignedUnit and maxSmallCopySize in wtf/UnalignedAccess.h, available for Bun's own C++ to use; no behavior change.

  • 7375405d8e — [PERF] base64EncodeInternal()/base64DecodeInternal() in WTF bound the chosen lookup table with auto, copying the whole 64-byte encode map or 128-byte decode map onto the stack on every call; now auto& references the constexpr table directly. Affects Bun's uses of WTF::base64Decode/base64URLDecode/base64URLEncode (WebCrypto JWK import/export in src/jsc/bindings/webcrypto/CryptoKey*.cpp); no behavior change.

Wasm

  • ce60d5d618 — WasmGC concurrent-GC correctness fix in the BBQ tier: struct.new/struct.new_default skipped the mutator fence when every stored field was a constant, but a struct with reference fields still needs the store-store fence before the new object is published to the concurrent marker. BBQ now emits emitMutatorFence() whenever the struct type has ref fields, matching IPInt/OMG; the fence is a no-op on x86_64, so this matters on ARM64.

  • c25fbbd346 — Debug-only: fixes a copy-paste typo in an ASSERT in Wasm BBQ emitModOrDiv (x64) that tested lhsLocation twice instead of rhsLocation != edx. No release-build or behavior impact.

  • 4cc12fa76b — Debug-build-only fix: assertCalleeIsReferenced in WasmOperations.cpp now returns early when the frame's callee is not a NativeCallee, which happens when a stack overflow during a JS↔Wasm transition reaches operationWasmToJSException with a JS callee still in the frame. Fixes a spurious asNativeCallee() crash under ASSERT_ENABLED; no release behavior change.

  • 3400ff6aa7 — Wasm: ModuleInformation now stores the importedStringConstants / qualifiedBuiltinSetNames compile options as UTF-8 byte vectors (Wasm::Name) instead of isolatedCopy()'d WTF::Strings, so no StringImpl is ever shared between the main thread and Wasm compiler threads (not all StringImpl fields are atomic). Import-name matching now compares raw bytes directly, dropping a makeString per import. Internal importedStringConstantsEquals()/builtinSetsInclude() signatures change to const Name& (Bun does not call them); only affects modules compiled with JS string builtins / importedStringConstants options.

  • 20d622b6d6 — Build fix: Wasm::CalleeGroup::BBQCalleeReference::operator= is replaced with a named set() method so clang 18's -Wthread-safety-precise analyzer can see the WTF_REQUIRES_LOCK requirement. No runtime behavior change; internal type only.

Other JSC

  • 9d03f48340 — Removes the dead CallLinkInfo::m_slowPathCount field (plus offsetOfSlowPathCount()/slowPathCount()) and its increments in the LLInt and JIT virtual-call thunks; CallLinkStatus::computeFromCallLinkInfo already bailed via takesSlowPath() for any m_clearedByVirtual site, so the reads always saw zero. Minor: one fewer memory increment per virtual call and 4 bytes less in CallLinkInfo. Internal-only, not used by Bun.

  • 3ddc7045b7 — DFG OSR-exit correctness fix: SpeculativeJIT::emitSwitchChar's StringUse path called op1.use() before speculateString(), so the operand's death preceded the BadType exit and baseline re-executed op_switch_char on a dead/poisoned scrutinee value when the speculation failed (e.g. a switch with single-character string cases later fed a non-string). Now speculates before releasing the operand, matching emitSwitchString; fixes a potential crash/miscompile in optimized code, no API change.

  • bdb7246737 — Adds an opt-in DFG/FTL "global inlining planner" (Options::useGlobalInliningPlanner, default false) that surveys and ranks all inlining candidates of a compilation up front (weighted by callee tier and inline depth) and spends a single compilation-wide budget, instead of deciding per call site in bytecode order. Adds nine new Options knobs (globalInliningPlanBudgetForDFG/FTL, maximumGlobalInliningPlanSites, inliningPlanTierBonus*, inliningPlanDepthPenalty). No effect unless enabled; upstream notes the current budget is high enough that it barely changes inlining decisions yet.

WTF and bmalloc

  • 386c27f080 — Build-only tweak: the ALWAYS_INLINE fast path of WTFCrashWithInfo (Darwin/PlayStation on x86_64/ARM64) is additionally disabled when compiling under the clang static analyzer (__clang_analyzer__) so the analyzer sees the out-of-line no-return version. No effect on normal Bun builds.

  • 390e2c91dc — Only engine-side change is a one-line #import#include of <wtf/Platform.h> in wtf/MachSendRightAnnotated.h (a Cocoa-only header); the rest is WebKit modulemap hygiene for Swift/Windows. No effect on Bun's JSCOnly build.

  • 1f7ab2fec8 — Fixes WTF::symbolize() in wtf/StackTrace.cpp under USE(LIBBACKTRACE): consults the symbol table (mangled names) before DWARF so clang debug builds get fully-qualified frame names, initializes the symbol pointer (previously read uninitialized on lookup miss), and passes a non-null error callback (libbacktrace always invokes it, so null crashed on the first failed lookup). No effect on Bun: USE_LIBBACKTRACE defaults to OFF for the JSCOnly port and Bun's build does not enable it.

  • f0148cf5ab — Changes Source/WTF/wtf/module.modulemap so the WTF Core clang module declares requires cplusplus23 instead of cplusplus, and bumps the module-cache touch count to force a rebuild. Only affects builds that consume WTF as a clang module (-fmodules, Apple/Swift-interop builds); Bun's build already uses -std=c++23 and does not enable clang modules, so no effect on Bun.

whsieh and others added 30 commits August 18, 2026 18:52
…undaries (to reduce false positives when redacting PII)

https://bugs.webkit.org/show_bug.cgi?id=322061
rdar://185254024

Reviewed by Abrar Rahman Protyasha.

Adjust the string replacement heuristics for text extractions, such that it only redacts text if it
spans word boundaries, in order to limit false positives (which may actually cause us to indirectly
and unintentionally leak information, which the agent would not have otherwise observed).

Test: TextExtractionTests.ReplacementStringsWordBoundaries

* Source/WebKit/Shared/TextExtractionToStringConversion.cpp:
(WebKit::isWordCharacter):
(WebKit::anchorsToWordBoundary):
(WebKit::matchIsWordBounded):
(WebKit::applyReplacements):
* Tools/TestWebKitAPI/Tests/WebKit/WKWebView/TextExtractionTests.mm:
(TestWebKitAPI::TEST(TextExtractionTests, ReplacementStringsWordBoundaries)):

Canonical link: https://commits.webkit.org/319423@main
…credentials

https://bugs.webkit.org/show_bug.cgi?id=322068

Reviewed by Michael Catanzaro.

nce an authentication challenge has been answered for a host, libsoup's
SoupAuthManager stamps the cached credentials onto every later request in that
protection space, replacing an Authorization header the page had set itself.
The Fetch standard uses the cached credential only "If httpRequest's header
list does not contain `Authorization`", and the Cocoa and curl ports already
behave that way.

Set SOUP_MESSAGE_DO_NOT_USE_AUTH_CACHE, libsoup's per-message opt-out, when the
request already carries an Authorization header. A challenge still reaches the
authentication handler and the retry authenticates as before.

Test: Tools/TestWebKitAPI/Tests/WebKit/WKWebView/glib/TestAuthentication.cpp

* Source/WebKit/NetworkProcess/soup/NetworkDataTaskSoup.cpp:
(WebKit::NetworkDataTaskSoup::createRequest):
* Tools/TestWebKitAPI/Tests/WebKit/WKWebView/glib/TestAuthentication.cpp:
(testWebViewAuthenticationPageProvidedAuthorizationHeader):
(serverCallback):
(beforeAll):

Canonical link: https://commits.webkit.org/319424@main
…s in establishLayerTreeScrollingRelations()

https://bugs.webkit.org/show_bug.cgi?id=321051
rdar://175191840

Reviewed by Matt Woodrow.

ScrollingTree keeps two active node sets, m_activePositionedNodes and
m_activeOverflowScrollProxyNodes, which hold Ref<>. Their only cleanup was a
frame-scoped removeIf in commitTreeStateInternal() that drops entries whose
frameIdentifier() matches the committing frame.

A node removed without its owning frame committing therefore leaked into those
sets permanently: the Ref keeps it alive while nodeForID() can no longer
resolve it. establishLayerTreeScrollingRelations() then walked the stale entry
and failed MESSAGE_CHECK_BASE. Under site isolation WebPageProxy sets
setShouldCrashOnMessageCheckFailure(true), so this terminated the UI process
rather than the web process.

The dominant path is hosted subtree teardown: every node in a hosted subtree is
registered as a hosted child, and ScrollingTreeFrameHostingNode::
removeHostedChildren() calls removeNode() for each one without the committing
frame ever being the iframe's own frame. Removing a cross-origin iframe,
navigating it cross-origin, or committing it empty all reach this.

Add ScrollingTree::removeFromActiveNodes(), called from removeNode(). It drops
the removed node itself, and also any active node that *references* a removed
overflow scrolling node -- the latter is what the failing check actually reads,
since it resolves the proxy's overflowScrollingNodeID() rather than the proxy
itself. removeAllNodes() clears both sets outright.

Two supporting changes were needed to make this testable at all:

- The active sets are only dumped under ScrollingStateTreeAsTextBehavior::
  IncludeNodeIDs, which no test-reachable API passed. Add
  scrollingTreeIncludingNodeIDsAsText() ->
  -[WKWebView _scrollingTreeIncludingNodeIDsAsText] ->
  uiController.scrollingTreeIncludingNodeIDsAsText.

- That dump sat inside #if ENABLE(SCROLLING_THREAD), which is mac-only, while
  both sets are declared and populated unconditionally. Remove the guard so the
  state is observable everywhere; without this the new layout test is a
  permanent no-op off mac. The unrelated SCROLLING_THREAD guard around
  synchronousScrollingNodes is left alone.

Note a single-process layout test cannot cover this: removing content from the
frame that owns the nodes makes that frame commit, so the frame-scoped removeIf
cleans up correctly. The test uses a cross-origin iframe so the removal happens
with no commit from the iframe's process.

Both tests were verified in both directions -- they pass with the fix and crash
without it. (On Release a MESSAGE_CHECK_BASE failure calls CRASH(), since
CRASH_IF_TESTING is only compiled out under ENABLE(IPC_TESTING_API).) Also
verified against the live repro: saratogasun.com -> Obituaries in a private
window with site isolation on crashed twice before the fix and survived eight
navigation cycles after.

* LayoutTests/http/tests/site-isolation/scrolling/remove-iframe-with-active-scroll-proxy-expected.txt: Added.
* LayoutTests/http/tests/site-isolation/scrolling/remove-iframe-with-active-scroll-proxy.html: Added.
* LayoutTests/http/tests/site-isolation/scrolling/resources/scroll-proxy-iframe.html: Added.
* Source/WebCore/page/scrolling/ScrollingTree.cpp:
(WebCore::ScrollingTree::removeNode):
(WebCore::ScrollingTree::removeFromActiveNodes):
(WebCore::ScrollingTree::removeAllNodes):
(WebCore::ScrollingTree::scrollingTreeAsText):
* Source/WebCore/page/scrolling/ScrollingTree.h:
* Source/WebKit/UIProcess/API/Cocoa/WKWebViewPrivateForTesting.h:
* Source/WebKit/UIProcess/API/Cocoa/WKWebViewTesting.mm:
(-[WKWebView _scrollingTreeIncludingNodeIDsAsText]):
* Source/WebKit/UIProcess/RemoteLayerTree/RemoteScrollingCoordinatorProxy.cpp:
(WebKit::RemoteScrollingCoordinatorProxy::scrollingTreeIncludingNodeIDsAsText const):
* Source/WebKit/UIProcess/RemoteLayerTree/RemoteScrollingCoordinatorProxy.h:
* Tools/TestRunnerShared/UIScriptContext/Bindings/UIScriptController.idl:
* Tools/TestRunnerShared/UIScriptContext/UIScriptController.h:
(WTR::UIScriptController::scrollingTreeIncludingNodeIDsAsText const):
* Tools/TestWebKitAPI/Tests/WebKit/WKWebView/SiteIsolation.mm:
(TestWebKitAPI::TEST(SiteIsolation, RemoveIframeWithActiveScrollProxyNodes)):
* Tools/WebKitTestRunner/cocoa/UIScriptControllerCocoa.h:
* Tools/WebKitTestRunner/cocoa/UIScriptControllerCocoa.mm:
(WTR::UIScriptControllerCocoa::scrollingTreeIncludingNodeIDsAsText const):

Canonical link: https://commits.webkit.org/319425@main
https://bugs.webkit.org/show_bug.cgi?id=322039

Reviewed by Darin Adler.

Remove a few unused overloads that take CSSToLengthConversionData.

* Source/WebCore/style/values/color/StyleColor.cpp:
* Source/WebCore/style/values/color/StyleColor.h:
* Source/WebCore/style/values/primitives/StyleLengthResolution.cpp:
* Source/WebCore/style/values/primitives/StyleLengthResolution.h:

Canonical link: https://commits.webkit.org/319426@main
https://bugs.webkit.org/show_bug.cgi?id=321692

Reviewed by Keith Miller.

The type field on WebAssembly.Module.imports and exports descriptors
comes from the unfinished js-types proposal, not the core JS API.
Prototype type() methods are already behind Options::useWasmJSTypes
(default false). Gate the remaining surface the same way so default
descriptors match the standard shape and do not throw on unrepresentable
ref types.

* Source/JavaScriptCore/wasm/js/WebAssemblyModuleConstructor.cpp:
* Source/WTF/Scripts/Preferences/UnifiedWebPreferences.yaml:
* JSTests/wasm/js-api/Module.exports.js:
* JSTests/wasm/js-api/Module.imports.js:
* JSTests/wasm/js-api/Module.imports-exports-no-type.js: Added.
* JSTests/wasm/js-api/type-reflection-concrete-types.js:
* JSTests/wasm/js-api/type-reflection-exports.js:
* JSTests/wasm/js-api/type-reflection-imports.js:
* JSTests/wasm/stress/table-oversized-initial-reflection.js:

Canonical link: https://commits.webkit.org/319427@main
https://bugs.webkit.org/show_bug.cgi?id=322049
rdar://185243406

Reviewed by Wenson Hsieh and Aditya Keerthi.

Add more comprehensive layout tests that cover this case:
- Starting from a page with a non-default interactive-widget
value, then navigating to a page that does not specify any
interactive-widget value. In this case, the page should revert
to default resizes-visual behavior instead of having a stale
value while ensuring the previous page had the correct
non-default value behavior.

* LayoutTests/TestExpectations:
* LayoutTests/http/tests/visual-viewport/ios/interactive-widget/overlays-content-process-swap-navigation-reset-expected.txt: Added.
* LayoutTests/http/tests/visual-viewport/ios/interactive-widget/overlays-content-process-swap-navigation-reset.html: Added.
* LayoutTests/http/tests/visual-viewport/ios/interactive-widget/resizes-content-process-swap-navigation-reset-expected.txt: Added.
* LayoutTests/http/tests/visual-viewport/ios/interactive-widget/resizes-content-process-swap-navigation-reset.html: Added.
* LayoutTests/http/tests/visual-viewport/ios/interactive-widget/resources/no-interactive-widget-after-overlays-content.html: Added.
* LayoutTests/http/tests/visual-viewport/ios/interactive-widget/resources/no-interactive-widget-after-resizes-content.html: Added.
* LayoutTests/platform/ios/TestExpectations:

Canonical link: https://commits.webkit.org/319428@main
…paths

https://bugs.webkit.org/show_bug.cgi?id=296371

Reviewed by Yusuke Suzuki.

Stack overflow during JS-to-Wasm or Wasm-to-JS can invoke
operationWasmToJSException while DECLARE_WASM_CALL_FRAME still points at
a JS callee. Debug then called asNativeCallee() and crashed.

Skip the callee-group check unless the frame is a NativeCallee.

* JSTests/wasm/stress/js-callee-stack-overflow.js: Added.
* Source/JavaScriptCore/wasm/WasmOperations.cpp:
(JSC::Wasm::assertCalleeIsReferenced):

Canonical link: https://commits.webkit.org/319429@main
https://bugs.webkit.org/show_bug.cgi?id=283476

Reviewed by Yusuke Suzuki.

When multiple functions fail validation under concurrent compilation,
keep the error message for the lowest function index instead of
whichever worker finishes first. Function-scoped failures record the
error without completing the plan immediately so later lower-index
failures can replace it. Non-function failures such as OOM are not
replaced by later function errors. EntryPlan stops assigning more work
and ThreadCountHolder completes when active workers drain.

* Source/JavaScriptCore/wasm/WasmPlan.h:
* Source/JavaScriptCore/wasm/WasmPlan.cpp:
* Source/JavaScriptCore/wasm/WasmEntryPlan.h:
* Source/JavaScriptCore/wasm/WasmEntryPlan.cpp:
* Source/JavaScriptCore/wasm/WasmIPIntPlan.cpp:
* JSTests/wasm/stress/consistent-compile-error-function-index.js: Added.

Canonical link: https://commits.webkit.org/319430@main
https://bugs.webkit.org/show_bug.cgi?id=261640

Reviewed by Sosuke Suzuki.

TypeKind mixed value types with type-section forms (func, struct, array,
rec, sub, subfinal). Those are not value types, so every TypeKind switch
needed dummy cases for them. Move them to DefinedTypeKind and dispatch
the type section on that enum.

* Source/JavaScriptCore/wasm/wasm.json:
* Source/JavaScriptCore/wasm/generateWasm.py:
* Source/JavaScriptCore/wasm/generateWasmOpsHeader.py:
* Source/JavaScriptCore/wasm/WasmSectionParser.cpp:
* Source/JavaScriptCore/wasm/WasmTypeDefinition.h:
* Source/JavaScriptCore/wasm/WasmBBQJIT.cpp:
* Source/JavaScriptCore/wasm/WasmBBQJIT64.cpp:
* Source/JavaScriptCore/wasm/WasmBBQJIT64.h:
* Source/JavaScriptCore/wasm/WasmOperations.cpp:
* Source/JavaScriptCore/wasm/js/WasmToJS.cpp:
* Source/JavaScriptCore/wasm/js/JSWebAssemblyStruct.cpp:
* JSTests/wasm/wasm.json:
* JSTests/wasm/WASM.js:
* LayoutTests/resources/wasm-builder.js:
* LayoutTests/http/tests/security/contentSecurityPolicy/resources/wasm-builder.js:
* LayoutTests/storage/indexeddb/resources/wasm-exceptions.js:
* LayoutTests/workers/wasm-references/test.js:
* LayoutTests/workers/wasm-resources/long-compile-worker.js:

Canonical link: https://commits.webkit.org/319431@main
…s out

https://bugs.webkit.org/show_bug.cgi?id=213874

Reviewed by Patrick Griffis.

GLIB ports have a custom implementation for increment/decrement actions,
which directly sets a value on the underlying backing object.

On the other hand, macOS and iOS ports use the increment/decrement methods
provided by 'AccessibilityNodeObject'. These operations are more complex than
simply updating the value of the backing object (for instance, in the case of
a slider, a keyboard event is emitted instead).

The test hangs on GLIB ports because their current implementation never emits
this keyboard event.

Since increment/decrement live in 'AccessibilityNodeObject', this patch modifies
the GLIB implementation to reuse the same general implementation used by the
macOS and iOS ports, which fixes the timeout in the targetting tests.

* LayoutTests/platform/glib/TestExpectations:
* Source/WebCore/accessibility/atspi/AccessibilityObjectAtspi.h:
* Source/WebCore/accessibility/atspi/AccessibilityObjectValueAtspi.cpp:
(WebCore::AccessibilityObjectAtspi::increment):
(WebCore::AccessibilityObjectAtspi::decrement):
* Tools/WebKitTestRunner/InjectedBundle/atspi/AccessibilityUIElementAtspi.cpp:
(WTR::AccessibilityUIElementAtspi::increment):
(WTR::AccessibilityUIElementAtspi::decrement):

Canonical link: https://commits.webkit.org/319432@main
https://bugs.webkit.org/show_bug.cgi?id=322078
rdar://185270122

Reviewed by Yijia Huang.

Like 313752@main, if rope is substring, we can access to the underlying
string data cheaply. This patch extends StringSwitch to substring 8-bit
ropes (previously only non-rope 8-bit string is supported).

Test: JSTests/stress/switch-string-substring-rope.js

* JSTests/stress/switch-string-substring-rope.js: Added.
(shouldBe):
(expectedFor):
(substringOfSubstring):
(check):
* Source/JavaScriptCore/dfg/DFGSpeculativeJIT.cpp:
(JSC::DFG::SpeculativeJIT::emitSwitchStringOnString):
* Source/JavaScriptCore/dfg/DFGSpeculativeJIT.h:
* Source/JavaScriptCore/ftl/FTLLowerDFGToB3.cpp:
(JSC::FTL::DFG::LowerDFGToB3::compileCompareStrictEq):

Canonical link: https://commits.webkit.org/319433@main
…d coordinate space

https://bugs.webkit.org/show_bug.cgi?id=321929

Reviewed by Aditya Keerthi.

updateIsSwitchVisuallyOnFromAbsoluteLocation took the track width and the thumb length
from absoluteBoundingBoxRect, which is wrong twice over.

Width and height are the wrong way around for a vertical switch, so the thumb moved once
the pointer passed roughly 29% of the track rather than the middle of it.

And the rect is in absolute coordinates while switchPointerTrackingLogicalLeftPosition
returns a local one, so a transformed switch compared a local position against
transform-inflated lengths. Under transform: scale(5) the change position sits five times
past the end of the track, so the thumb never moves and every drag ends up as an ordinary
click.

Tests: fast/forms/switch/pointer-tracking-transform.html
       fast/forms/switch/pointer-tracking-vertical-lr.html

Canonical link: https://commits.webkit.org/319434@main
… cap

https://bugs.webkit.org/show_bug.cgi?id=321824
rdar://184964977

Reviewed by Ryosuke Niwa.

Every top-level visit records FirstPartyVisit, so IsolatedSiteStore could grow without bound. Cap it at 5000 entries,
which take roughly 600 KB in UIProcess and cover on the order of a year of first-party browsing. The number is a
judgment rather than a measurement, and may be adjusted once there is more data.

Eviction priority is decided by signal first and then by last updated time:
1. VisitOnly - FirstPartyVisit and nothing else
2. Gestured - the user also did something deliberate there
3. CredentialEvidence - anything outside firstPartySignals, evicted last

lastUpdated is rounded down to a day, so entries recorded on the same day cannot be ordered by it. Breaking that tie
by insertion order would make eviction depend on an implementation artifact rather than a meaningful signal - e.g. a
bulk import would always evict whichever half of the batch happened to be processed first, every time it ran. Ties
are broken at random instead, the same as WebProcessCache's and NetworkCache's eviction.

Since the steady state evicts a single entry per navigation, eviction targets are picked with nth_element rather than a
full sort. Candidates hold pointers to the keys in the table to avoid copying every domain, which is only safe because
nothing mutates m_sites between building the candidates and reading them back.

Eviction runs on the write path, and again after the load merges the rows from the database. The tracking prevention
import defers it until its batch finishes, so the imported set is capped in one pass rather than evicting entries as
each domain is added.

Split isolatedSiteRecordForDisplayName into isolatedSiteRecords so the new tests can count records directly, and hold
the returned array in a local rather than enumerating the temporary: Objective-C fast enumeration destroys temporaries
in the collection expression before the loop body runs, which would leave the enumeration walking a released array.

Tests: IsolatedSiteStore.EvictsOneOfTwoTiedSitesWithinTier
       IsolatedSiteStore.EvictsVisitOnlySiteBeforeGesturedSite
       IsolatedSiteStore.EvictionIsPersisted
       IsolatedSiteStore.EvictsLoadedSitesOverTheCap

* Source/WebKit/UIProcess/API/Cocoa/WKWebsiteDataStore.mm:
(-[WKWebsiteDataStore _setMaximumIsolatedSiteCountForTesting:]):
* Source/WebKit/UIProcess/API/Cocoa/WKWebsiteDataStorePrivate.h:
* Source/WebKit/UIProcess/WebsiteData/IsolatedSiteStore.cpp:
(WebKit::IsolatedSiteStore::IsolatedSiteStore):
(WebKit::IsolatedSiteStore::didLoadSites):
(WebKit::IsolatedSiteStore::importUserInteractions):
(WebKit::IsolatedSiteStore::recordSignals):
(WebKit::IsolatedSiteStore::evictionTier):
(WebKit::IsolatedSiteStore::evictSitesIfNeeded):
(WebKit::IsolatedSiteStore::setMaximumSiteCountForTesting):
* Source/WebKit/UIProcess/WebsiteData/IsolatedSiteStore.h:
* Source/WebKit/UIProcess/WebsiteData/WebsiteDataStore.cpp:
(WebKit::WebsiteDataStore::setMaximumIsolatedSiteCountForTesting):
* Source/WebKit/UIProcess/WebsiteData/WebsiteDataStore.h:
* Tools/TestWebKitAPI/Tests/WebKit/WKWebView/WKWebsiteDatastore.mm:
(TestWebKitAPI::(IsolatedSiteStore, EvictsOneOfTwoTiedSitesWithinTier)):
(TestWebKitAPI::(IsolatedSiteStore, EvictsVisitOnlySiteBeforeGesturedSite)):
(TestWebKitAPI::(IsolatedSiteStore, EvictionIsPersisted)):
(TestWebKitAPI::(IsolatedSiteStore, EvictsLoadedSitesOverTheCap)):

Canonical link: https://commits.webkit.org/319435@main
https://bugs.webkit.org/show_bug.cgi?id=321678
rdar://184825762

Reviewed by Ian Grunert.

A variety of WebKit headers contained #import yet were included as
cross-platform within the modulemaps that were imported by Swift. This caused
difficulties under Windows where #import means something different.

* Source/WTF/wtf/MachSendRightAnnotated.h:
* Source/WebKit/Modules/Internal/WebKitInternalCxx.h:
* Source/WebKit/Modules/Internal/module.modulemap:

Canonical link: https://commits.webkit.org/319436@main
…bars

https://bugs.webkit.org/show_bug.cgi?id=321930

Reviewed by Nikolas Zimmermann.

This way we avoid creating a gl context for skia in the scrolling
thread. A new buffer type CoordinatedPlatformLayerBufferSkiaDeferredImage
receives the recorded display list that is replayed on the compositor
thread when painting the buffer for the first time.

* Source/WebCore/page/scrolling/coordinated/ScrollerCoordinated.cpp:
(WebCore::ScrollerCoordinated::updateValues):
* Source/WebCore/platform/TextureMapper.cmake:
* Source/WebCore/platform/graphics/skia/GraphicsContextSkia.cpp:
(WebCore::GraphicsContextSkia::beginRecording):
(WebCore::GraphicsContextSkia::endRecording):
* Source/WebCore/platform/graphics/skia/GraphicsContextSkia.h:
* Source/WebCore/platform/graphics/texmap/coordinated/CoordinatedPlatformLayer.cpp:
(WebCore::CoordinatedPlatformLayer::CoordinatedPlatformLayer):
(WebCore::CoordinatedPlatformLayer::threadSafeGrContext const): Deleted.
* Source/WebCore/platform/graphics/texmap/coordinated/CoordinatedPlatformLayer.h:
(WebCore::CoordinatedPlatformLayer::threadSafeGrContext const):
* Source/WebCore/platform/graphics/texmap/coordinated/CoordinatedPlatformLayerBuffer.h:
* Source/WebCore/platform/graphics/texmap/coordinated/CoordinatedPlatformLayerBufferSkiaDeferredImage.cpp: Added.
(WebCore::CoordinatedPlatformLayerBufferSkiaDeferredImage::create):
(WebCore::CoordinatedPlatformLayerBufferSkiaDeferredImage::CoordinatedPlatformLayerBufferSkiaDeferredImage):
(WebCore::m_displayList):
(WebCore::CoordinatedPlatformLayerBufferSkiaDeferredImage::skiaImage):
(WebCore::CoordinatedPlatformLayerBufferSkiaDeferredImage::paintToTextureMapper):
* Source/WebCore/platform/graphics/texmap/coordinated/CoordinatedPlatformLayerBufferSkiaDeferredImage.h: Added.

Canonical link: https://commits.webkit.org/319437@main
https://bugs.webkit.org/show_bug.cgi?id=322089

Reviewed by Chris Dumez.

Fix the warning in DatagramSink.cpp by marking WebTransportDatagramDuplexStream::maxDatagramSize
NODELETE since it just returns a constant number.

No new tests since there should be no behavioral changes.

* Source/WebCore/Modules/webtransport/WebTransportDatagramDuplexStream.h:
* Source/WebCore/SaferCPPExpectations/UncountedCallArgsCheckerExpectations:

Canonical link: https://commits.webkit.org/319438@main
…structors

https://bugs.webkit.org/show_bug.cgi?id=193823

Reviewed by Yusuke Suzuki.

generateUnlinkedCodeBlockForFunctions only generates the CodeForCall variant of every function.
For a class constructor that variant is just the "Cannot call a class constructor" throw, emitted
before the body, so the cache holds neither the construct code nor the functions nested in the
constructor body. Every `new C()` from a bytecode-cached program misses the cache and reparses
the constructor from source; the implicit default constructor is reparsed from the builtin source too.

Generate CodeForConstruct instead when the executable is a class constructor. Calling a class
constructor throws, so nothing is lost. The async-function early return becomes unreachable and is
removed. Ordinary functions still get CodeForCall only, since whether they are constructed is not
known at cache time.

Constructing 182 three.js classes from a cached program: 253 -> 4 parses, 14.5 ms -> 10.3 ms.
babylon.js ES6 startup: 133 -> 9 parses, 30.7 ms -> 27.8 ms. Cache size grows by the previously
missing constructor bodies (three.js +10.8%, a 50 MB application bundle +0.5%).

Test: Source/JavaScriptCore/API/tests/testapi.mm

* Source/JavaScriptCore/API/tests/testapi.mm:
(testBytecodeCachedClassConstructorsDontReparse):
(testObjectiveCAPI):
* Source/JavaScriptCore/runtime/CodeCache.cpp:
(JSC::generateUnlinkedCodeBlockForFunctions):

Canonical link: https://commits.webkit.org/319439@main
https://bugs.webkit.org/show_bug.cgi?id=322069

Reviewed by Yusuke Suzuki.

Map.prototype.forEach and Set.prototype.forEach are JS builtins that walk the
storage with @mapIterationNext, @mapIterationEntry, @mapIterationEntryKey and
@mapIterationEntryValue. The corresponding DFG nodes were lowered to C++ calls
in both DFG and FTL, so every element paid four operation calls whose bodies
are two or three butterfly loads. The for-of siblings (MapIteratorNext,
MapIteratorKey, MapIteratorValue) already walk the same table inline.

Lower the four nodes the same way. MapIterationNext checks for the sentinel,
falls back to the operation only when the table is obsolete, and otherwise
scans the data table for the next non-deleted key and stores its entry index
into the IterationEntry slot. MapIterationEntry is a single load of that
slot, and MapIterationEntryKey/Value compute the data index from it through
loadMapEntryData, which MapIteratorKey/Value now share as well; the for-of
lowering is unchanged.

The Entry/EntryKey/EntryValue operations are no longer reachable from JIT code
and are removed. The remaining IterationNext operations are only called for
obsolete tables, so they no longer handle the sentinel.

                                         Baseline                  Patched

map-for-each                          2.2298+-0.0516     ^      1.7644+-0.0487        ^ definitely 1.2638x faster
map-for-each-key-value               80.3508+-1.3258     ^     48.7157+-1.0237        ^ definitely 1.6494x faster
set-for-each-value                   68.4946+-1.5632     ^     46.6082+-1.1317        ^ definitely 1.4696x faster
set-for-each                          2.0780+-0.0679     ^      1.6924+-0.0448        ^ definitely 1.2278x faster

Tests: JSTests/microbenchmarks/map-for-each-deleted-entries.js
       JSTests/microbenchmarks/map-for-each-key-value.js
       JSTests/microbenchmarks/set-for-each-value.js
       JSTests/stress/map-for-each-mutation-during-iteration.js
       JSTests/stress/set-for-each-mutation-during-iteration.js

* JSTests/microbenchmarks/map-for-each-deleted-entries.js: Added.
(sumEntries):
* JSTests/microbenchmarks/map-for-each-key-value.js: Added.
(sumEntries):
* JSTests/microbenchmarks/set-for-each-value.js: Added.
(sumValues.set forEach):
* JSTests/stress/map-for-each-mutation-during-iteration.js: Added.
(shouldBe):
(makeMap):
(keys):
(deleteAhead):
(deleteAllAhead):
(addDuring):
(clearDuring):
(clearAndReadd):
* JSTests/stress/set-for-each-mutation-during-iteration.js: Added.
(shouldBe):
(set values.set forEach):
(deleteAhead.set forEach):
(deleteAllAhead.set forEach):
(addDuring.set forEach):
(clearDuring.set forEach):
(clearAndReadd.set forEach):
* Source/JavaScriptCore/dfg/DFGOperations.cpp:
(JSC::DFG::JSC_DEFINE_NOEXCEPT_JIT_OPERATION):
* Source/JavaScriptCore/dfg/DFGOperations.h:
* Source/JavaScriptCore/dfg/DFGSpeculativeJIT.cpp:
(JSC::DFG::SpeculativeJIT::loadMapEntryData):
(JSC::DFG::SpeculativeJIT::compileMapIteratorKey):
(JSC::DFG::SpeculativeJIT::compileMapIteratorValue):
(JSC::DFG::SpeculativeJIT::compileMapIterationNext):
(JSC::DFG::SpeculativeJIT::compileMapIterationEntry):
(JSC::DFG::SpeculativeJIT::compileMapIterationEntryData):
(JSC::DFG::SpeculativeJIT::compileMapIterationEntryKey):
(JSC::DFG::SpeculativeJIT::compileMapIterationEntryValue):
* Source/JavaScriptCore/dfg/DFGSpeculativeJIT.h:
* Source/JavaScriptCore/ftl/FTLLowerDFGToB3.cpp:
(JSC::FTL::DFG::LowerDFGToB3::compileCompareStrictEq):

Canonical link: https://commits.webkit.org/319440@main
https://bugs.webkit.org/show_bug.cgi?id=322007

Reviewed by Carlos Garcia Campos.

Similar to 319432@main.

ATSPI lacks an implementation of operation
'dismiss' which causes GLIB tests relying on this operation to time out.

The 'dismiss' operation can be implemented via
'performDismissAction', reaching the same entry point that macOS/iOS
ports eventually reach through their native a11y APIS ('AXDismissAction'
and 'accessibilityPerformEscape', respectively).

* LayoutTests/platform/glib/TestExpectations:
* Source/WebCore/accessibility/atspi/AccessibilityObjectAtspi.h:
* Source/WebCore/accessibility/atspi/AccessibilityObjectValueAtspi.cpp:
(WebCore::AccessibilityObjectAtspi::dismiss):
* Tools/WebKitTestRunner/InjectedBundle/atspi/AccessibilityUIElementAtspi.cpp:
(WTR::AccessibilityUIElementAtspi::dismiss):
* Tools/WebKitTestRunner/InjectedBundle/atspi/AccessibilityUIElementAtspi.h:

Canonical link: https://commits.webkit.org/319441@main
…e is treated as a progress timeline boundary

https://bugs.webkit.org/show_bug.cgi?id=320882
rdar://183902668

Reviewed by Antoine Quint.

Step 3 of [1] sets "unlimited current time" from the timeline time when the animation's
start time is resolved, and only otherwise falls back to the animation's current time.
We additionally required the timeline time to be resolved before taking the first
branch, so a resolved start time paired with an unresolved timeline time silently took
the fallback.

That is wrong for an animation holding at its effect end: its hold time equals the
timeline duration, so "effective timeline progress" is exactly 1 and we report a
boundary even though the timeline has no current time. Being at a boundary suppresses
the after phase, so a "none" fill mode animation stays active and keeps applying its
100% keyframe instead of reverting to its unanimated style. Per [1] an unresolved
timeline time instead propagates to "effective timeline progress", which is then
neither 0 nor 1, so the procedure returns false. Return false on that branch, leaving
the start-time-unresolved branch to use the animation's current time as [1] specifies.

This is why animation-inactive-outside-range-test.html is flaky. Its "After cover
phase" indicator is the only one of the four whose local time is held exactly on its
active-after boundary, so it is the only one whose phase hinges on this predicate
rather than on a strict inequality; one frame with an unresolved view timeline current
time paints it green instead of grey.

[1] https://drafts.csswg.org/web-animations-2/#at-progress-timeline-boundary

Test: imported/w3c/web-platform-tests/scroll-animations/css/view-timeline-unresolved-current-time-at-effect-end.html

* LayoutTests/imported/w3c/web-platform-tests/scroll-animations/css/view-timeline-unresolved-current-time-at-effect-end-expected.txt: Added.
* LayoutTests/imported/w3c/web-platform-tests/scroll-animations/css/view-timeline-unresolved-current-time-at-effect-end.html: Added.
* Source/WebCore/animation/AnimationEffectTiming.cpp:
(WebCore::AnimationEffectTiming::getBasicTiming const):

Canonical link: https://commits.webkit.org/319442@main
…eyframe offsets

https://bugs.webkit.org/show_bug.cgi?id=322009
rdar://185204758

Reviewed by Antoine Quint.

computedOffset() maps a keyframe's timeline range offset into the progress space of the
animation's attachment range by dividing by that range's length. It skipped the division
only when the attachment range isDefault(), which tests start.isNormal() && end.isNormal()
and so does not catch a range that is non-default but degenerate. With a range such as
"contain 50% contain 50%" both edges resolve to the same timeline offset, the length is
zero, and the division yielded +/-Infinity (or NaN when the numerator happened to be zero
as well).

The guard in updateComputedKeyframeOffsetsIfNeeded() does not help here: it bails out when
the timeline's currentTime is unresolved, but in this case the timeline range is perfectly
ordinary and only the attachment range is degenerate.

A zero-length attachment range gives the animation no interval to progress over, so there
is no offset to map onto. Return quiet_NaN() instead, which is how this code already
represents an unresolved computed offset -- see the named-range-without-a-timeline case
above it, and hasKeyframeWithUnresolvedComputedOffset(), which disables acceleration.
Computed offsets legitimately fall outside [0,1], so clamping would be wrong.

This matches Chrome, which already reports an unresolved computed offset here. Firefox
reports +/-Infinity, as WebKit did before this change.

While here, make BlendingKeyframes::updatedComputedOffsets() sort with an explicit
comparator. It sorted on BlendingKeyframe::offset() using std::less, which is not a strict
weak ordering over NaN: every NaN compares equivalent to every other offset, while those
offsets are not equivalent to each other. That is undefined behavior and trips hardened
libc++ comparator checks. It is reachable independently of the above, via range-offset
keyframes on an element with no scroll timeline at all. Order unresolved offsets last.

Test: imported/w3c/web-platform-tests/scroll-animations/css/timeline-offset-keyframes-zero-length-attachment-range.html

* LayoutTests/imported/w3c/web-platform-tests/scroll-animations/css/timeline-offset-keyframes-zero-length-attachment-range-expected.txt: Added.
* LayoutTests/imported/w3c/web-platform-tests/scroll-animations/css/timeline-offset-keyframes-zero-length-attachment-range.html: Added.
* Source/WebCore/animation/BlendingKeyframes.cpp:
(WebCore::BlendingKeyframes::updatedComputedOffsets):
* Source/WebCore/animation/KeyframeEffect.cpp:
(WebCore::computedOffset):

Canonical link: https://commits.webkit.org/319443@main
…i/the-audionode-interface/audionode-channel-rules.html is a flaky crash

https://bugs.webkit.org/show_bug.cgi?id=321961
rdar://185147134

Reviewed by Chris Dumez.

Remove each audio node from m_deferredBreakConnectionList before releasing its reference, since doing so may destroy
it re-entrantly. (Patch was generated with AI assistance.)

No new tests needed.

* Source/WebCore/Modules/webaudio/BaseAudioContext.cpp:
(WebCore::BaseAudioContext::handleDeferredDecrementConnectionCounts):
(WebCore::BaseAudioContext::handleDeferredDerefs):

Canonical link: https://commits.webkit.org/319444@main
…cument

https://bugs.webkit.org/show_bug.cgi?id=322064

Reviewed by Rob Buis.

SVG text is not painted at its specified font size. The font is scaled up to
the resolution the text is finally rendered at, and the context is scaled back
down by the same factor, so glyphs are rasterized at their on-screen size. The
factor comes from calculateScreenFontSizeScalingFactor(), and 'vector-effect:
non-scaling-stroke' on <text> divides the stroke thickness by it too.

It is derived from computeAccumulatedTransform(TrackSVGScreenCTMMatrix), which
follows getScreenCTM() semantics and is zoom independent on purpose: getCTM()
and getScreenCTM() need to report the same matrix at every zoom level. The zoom
was multiplied back in from style().usedZoom(), but only for SVG embedded in an
HTML document. In a standalone SVG document the factor kept its unzoomed value,
so at page zoom the text was rasterized for the wrong resolution and a
non-scaling stroke came out too thick by the zoom factor.

The accumulated matrix is the same in both cases, so drop the condition and
always apply the zoom. The text metrics of the zoomed documents below now match
the legacy SVG engine, which reaches the same factor by accumulating
localToParentTransform() through LegacyRenderSVGRoot, whose
localToBorderBoxTransform() carries the zoom.

Covered by existing tests.

* LayoutTests/platform/mac-tahoe-wk2-lbse-text/TestExpectations:
* LayoutTests/platform/mac-tahoe-wk2-lbse-text/svg/zoom/page/zoom-coords-viewattr-01-b-expected.png:
* LayoutTests/platform/mac-tahoe-wk2-lbse-text/svg/zoom/page/zoom-coords-viewattr-01-b-expected.txt:
* LayoutTests/platform/mac-tahoe-wk2-lbse-text/svg/zoom/page/zoom-mask-with-percentages-expected.txt:
* LayoutTests/platform/mac-tahoe-wk2-lbse-text/svg/zoom/page/zoom-svg-through-object-with-absolute-size-2-expected.txt:
* LayoutTests/platform/mac-tahoe-wk2-lbse-text/svg/zoom/page/zoom-svg-through-object-with-absolute-size-expected.txt:
* LayoutTests/platform/mac-tahoe-wk2-lbse-text/svg/zoom/page/zoom-svg-through-object-with-percentage-size-expected.txt:
* Source/WebCore/rendering/svg/SVGTransformComputation.h:
(WebCore::SVGTransformComputation::calculateScreenFontSizeScalingFactor const):

Canonical link: https://commits.webkit.org/319445@main
…cal Ref

https://bugs.webkit.org/show_bug.cgi?id=321827
rdar://184966612

Reviewed by Chris Dumez.

Location::ancestorOrigins() built its cached list by calling
protect(m_ancestorOrigins)->append(...) inside the ancestor walk, taking a
fresh RefPtr copy of the member on every iteration just to reach
DOMStringList::append(). Append through a Ref local instead.

This is a readability cleanup rather than a measurable win: the list is built
once per Location and then cached, ancestor chains are short, and
DOMStringList is non-atomically ref-counted, so the churn removed is a few
increments on a cold path. The copyRef() that publishes the member even adds
one ref/deref back in the common no-ancestor case.

The member is still assigned before the loop is entered, so the list is
published in the same order as before and a re-entrant read during the walk
observes the same partially-built list it does today.

No behavior change, so no new test.

* Source/WebCore/page/Location.cpp:
(WebCore::Location::ancestorOrigins const):

Canonical link: https://commits.webkit.org/319446@main
…ceProvider::decrementUseCount()

<https://bugs.webkit.org/show_bug.cgi?id=317082>
<rdar://179209792>

Reviewed by Zak Ridouh.

Guard against a missing entry before dereferencing the result of
`HashMap::find()` in `decrementUseCount()`.  The function relies on
`ASSERT(iterator != ...end())`, which compiles to nothing in release
builds, then reads `iterator->value` unconditionally.  When the
identifier is absent, `find()` returns `end()`, and reading
`end()->value` accesses memory one entry past the table's backing
buffer.

The absent-entry case became reachable in 259876@main, which replaced
the page-group-keyed owning map of providers with a single weakly-held
provider (`existingStorageNameSpaceProvider()`).  The provider is now
destroyed when the last page in a Web Content process goes away and
recreated empty for the next page, so a `WebPage` torn down after that
point decrements against a provider that never held its identifier.

Return early when the iterator is `end()`, matching the existing guard
in the sibling accessor `sessionStorageNamespace()`.  The
`ASSERT(sessionStorageNamespaces.useCount)` is retained so debug builds
still flag a use-count imbalance.

No new tests since this path is reached only during web page teardown
when the session storage namespace entry has already been removed, and
is not directly testable through public API.

* Source/WebKit/WebProcess/WebStorage/WebStorageNamespaceProvider.cpp:
(WebKit::WebStorageNamespaceProvider::decrementUseCount):

Originally-landed-as: 305413.965@safari-7624.4-branch (6254fe9). rdar://184744974
Canonical link: https://commits.webkit.org/319447@main
…ollbar have the wrong composite order

https://bugs.webkit.org/show_bug.cgi?id=322079
rdar://185270753

Reviewed by Antoine Quint.

Animatable.getAnimations() returns its list "sorted using the composite order
described for the associated animations of effects in § 5.4.2 The effect
stack" [1][2].

For two CSS Animations that comparison resolves entirely on step 2 of § 5.4.2,
the class-specific composite order. Since an owning element is an (element,
pseudo-element) pair, two animations on different pseudo-elements of a single
element take the "if the owning element of A and B differs" branch of
css-animations-2 § 2.2 [3], which orders pseudo-elements as:

    element, ::marker, ::before, any other pseudo-elements not mentioned
    specifically in this list, sorted in ascending order by the Unicode
    codepoints that make up each selector, ::after, element children

The SortingIndex enum in
compareStyleOriginatedAnimationOwningElementPositionsInDocumentTreeOrder()
deviated from that order in three ways, because each newly-supported
pseudo-element was appended to the enum instead of being inserted in codepoint
order:

    - PickerIcon sorted after After, so an animation on ::picker-icon was
      composited and returned after one on ::after.
    - WebKitScrollbar sorted after FirstLetter, FirstLine, GrammarError and
      Highlight, but "-" (U+002D) sorts before "f", "g" and "h".
    - Checkmark sorted after TargetText, but "c" sorts before "f".

Reorder the enum to match the order specified in [3], and spell out in the
comment above it that the enumerators are listed in sort order, so that a future
pseudo-element is inserted at its codepoint position rather than appended.

The ::view-transition pseudo-elements are left after After: their relative order
is additionally constrained by css-view-transitions [4] and is already
special-cased above by nameOrPart.

This is observable both through getAnimations() [1] and through the dispatch
order of animation and transition events, since
compareAnimationEventsByCompositeOrder() sorts with the same comparison. Chrome
already returns ::picker-icon before ::after; Firefox creates no animations for
any of the affected pseudo-elements. ::checkmark's position is corrected by the
same rule but is not yet observable, as no engine creates an animation for it.

The new test is a WebKit test rather than a web-platform-test because the
ordering ::-webkit-scrollbar takes part in cannot be tested cross-engine: it is
not a standardized pseudo-element, and Firefox has no equivalent. It reports the
subtests for the pseudo-elements WebKit creates no animation for — ::checkmark,
::selection, ::highlight(), ::grammar-error, ::spelling-error and ::target-text —
as unsupported rather than asserting on them, so that an unsupported
pseudo-element cannot mask a misordering.

[1] https://drafts.csswg.org/web-animations-1/#dom-animatable-getanimations
[2] https://drafts.csswg.org/web-animations-1/#the-effect-stack
[3] https://drafts.csswg.org/css-animations-2/#animation-composite-order
[4] https://drafts.csswg.org/css-view-transitions-1/

Test: webanimations/pseudo-element-composite-order.html

* LayoutTests/webanimations/pseudo-element-composite-order-expected.txt: Added.
* LayoutTests/webanimations/pseudo-element-composite-order.html: Added.
* Source/WebCore/animation/WebAnimationUtilities.cpp:
(WebCore::compareStyleOriginatedAnimationOwningElementPositionsInDocumentTreeOrder):

Canonical link: https://commits.webkit.org/319448@main
https://bugs.webkit.org/show_bug.cgi?id=322094

Reviewed by Carlos Garcia Campos.

Changeset 258327@main refactored properties 'FocusableAncestor',
'EditableAncestor' and 'HighestEditableAncestor' to resolve them on demand.
A working implementation for macOS/iOS port was added but an equivalent
implementation for GLIB ports was missing.

The core implementation for this functionality is shared across ports, living
in 'AXCoreObject.h'. The patch implements the required WKTR interface methods
on GLIB ('focusableAncestor', 'editableAncestor', etc), which end up
calling the 'AXCoreObject' methods via 'AccessibilityObjectAtspi'.

Also, the condition in 'accessibilityPlatformIncludesObject' that
ignores children of text controls has been relaxed for content-editable
controls, since they can contain valid non-text descendants (such as
a button). Otherwise, 'editableAncestor' would return an ignored object.

* LayoutTests/platform/glib/TestExpectations:
* Source/WebCore/accessibility/atspi/AccessibilityObjectAtspi.cpp:
(WebCore::AccessibilityObjectAtspi::focusableAncestor const):
(WebCore::AccessibilityObjectAtspi::editableAncestor const):
(WebCore::AccessibilityObjectAtspi::highestEditableAncestor const):
(WebCore::AccessibilityObject::accessibilityPlatformIncludesObject const):
(WebCore::AccessibilityObjectAtspi::states const):
* Source/WebCore/accessibility/atspi/AccessibilityObjectAtspi.h:
* Tools/WebKitTestRunner/InjectedBundle/atspi/AccessibilityUIElementAtspi.cpp:
(WTR::AccessibilityUIElementAtspi::focusableAncestor):
(WTR::AccessibilityUIElementAtspi::editableAncestor):
(WTR::AccessibilityUIElementAtspi::highestEditableAncestor):
* Tools/WebKitTestRunner/InjectedBundle/atspi/AccessibilityUIElementAtspi.h:

Canonical link: https://commits.webkit.org/319449@main
https://bugs.webkit.org/show_bug.cgi?id=319580

Reviewed by Nikolas Zimmermann and Carlos Garcia Campos.

The GTK EventSenderProxy touch methods were empty stubs, so
eventSender.addTouchPoint(), touchStart() and friends dispatched nothing
and the fast/events/touch tests timed out. Unlike mouse, key and wheel
events, WebKitWebViewBase had no touch synthesis entry point, and the
existing touch path is built around GdkEvent, which cannot be constructed
by application code under GTK4. Add webkitWebViewBaseSynthesizeTouchEvent(),
which builds the WebPlatformTouchPoint list and dispatches a
NativeWebTouchEvent directly, the same way the mouse path already bypasses
GdkEvent; the GTK EventSenderProxy keeps the touch point state and maps it
onto that entry point.

Update the touch event test expectations for the tests that now pass, were
unskipped, or changed to a deterministic failure, and add a GTK baseline
for basic-single-touch-events.html (touch point radius is unsupported, as
on WPE, but the touch modifiers are applied).

Covered by fast/events/touch tests.

* LayoutTests/platform/glib/TestExpectations:
* LayoutTests/platform/gtk/TestExpectations:
* LayoutTests/platform/gtk/fast/events/touch/basic-single-touch-events-expected.txt: Added.
* LayoutTests/platform/wpe/TestExpectations:
* Source/WebKit/Shared/NativeWebTouchEvent.h:
* Source/WebKit/Shared/gtk/NativeWebTouchEventGtk.cpp:
(WebKit::NativeWebTouchEvent::NativeWebTouchEvent):
(WebKit::m_nativeEvent):
* Source/WebKit/UIProcess/API/gtk/WebKitWebViewBase.cpp:
(toWebPlatformTouchPointState):
(webkitWebViewBaseSynthesizeTouchEvent):
* Source/WebKit/UIProcess/API/gtk/WebKitWebViewBaseInternal.h:
* Tools/WebKitTestRunner/EventSenderProxy.h:
* Tools/WebKitTestRunner/gtk/EventSenderProxyGtk.cpp:
(WTR::toSyntheticTouchPointState):
(WTR::EventSenderProxy::addTouchPoint):
(WTR::EventSenderProxy::updateTouchPoint):
(WTR::sendTouchEvent):
(WTR::markAllTouchPointsStationary):
(WTR::EventSenderProxy::touchStart):
(WTR::EventSenderProxy::touchMove):
(WTR::EventSenderProxy::touchEnd):
(WTR::EventSenderProxy::touchCancel):
(WTR::EventSenderProxy::clearTouchPoints):
(WTR::EventSenderProxy::releaseTouchPoint):
(WTR::EventSenderProxy::cancelTouchPoint):
(WTR::EventSenderProxy::setTouchModifier):

Canonical link: https://commits.webkit.org/319450@main
https://bugs.webkit.org/show_bug.cgi?id=322081
rdar://185276156

Reviewed by Dan Glastonbury.

This is adding a number of WPT tests for path data and SVG points error
processing to avoid regression in the future. It doesn't cover yet
everything. Some discussions are happening on
w3c/svgwg#764
w3c/svgwg#763
for the trailing part of the error.

This will be exported to WPT.

* LayoutTests/imported/w3c/web-platform-tests/svg/path/error-handling/path-data-error-geometry-expected.txt: Added.
* LayoutTests/imported/w3c/web-platform-tests/svg/path/error-handling/path-data-error-geometry.html: Added.
* LayoutTests/imported/w3c/web-platform-tests/svg/types/scripted/SVGPointList-error-handling-expected.txt: Added.
* LayoutTests/imported/w3c/web-platform-tests/svg/types/scripted/SVGPointList-error-handling.html: Added.

Canonical link: https://commits.webkit.org/319451@main
https://bugs.webkit.org/show_bug.cgi?id=321142

Reviewed by Carlos Garcia Campos.

Until now the GTK and WPE ports were built with whatever compiler CMake
picked up by default, which on Linux means GCC. This makes build-webkit
and build-jsc use Clang instead for the GTK and WPE ports, and also for
JSCOnly when building on Linux.

This follows a recent change were we made Clang the default compiler on
the CI. So this aligns the default developer build with the CI for the
Linux ports. See 318539@main

The compiler is selected by exporting CC and CXX rather than by passing
-DCMAKE_C_COMPILER to CMake. The reason is that shouldRemoveCMakeCache()
already keeps track of CC and CXX, so a change on the compiler removes
the CMake cache and reconfigures the build directory. Note that this
means that the first build after this change will reconfigure and
rebuild any existing GTK, WPE or JSCOnly build directory.

Both the clang and the clang++ binaries have to be available in the
PATH. If any of them is missing then a warning is printed and the build
continues using the default system compiler, so this doesn't break
systems where Clang is not installed.

Clang is also the new default for the cross-builds done with the
toolchains generated by cross-toolchain-helper (the ones selected with
the --cross-target flag). Those toolchains ship both compilers, and the
environment setup script generated by cross-toolchain-helper already
checks the value of CC and CXX to decide which one of the two to use, so
in that case CC and CXX are set simply as a hint for that script and the
binaries from the host PATH are not checked (the compiler used is the
cross-compiled one from the toolchain). When the toolchain has no Clang
support the setup script keeps using GCC as before.

Anyone can still choose a different compiler: setting CC and CXX in the
environment or passing the compiler via --cmakeargs takes precedence over
the default.

* Tools/Scripts/build-webkit:
* Tools/Scripts/cross-toolchain-helper:
(YoctoCrossBuilder.cross_dev_shell):
* Tools/Scripts/webkitdirs.pm:
(determineDefaultCompiler):
(buildCMakeProjectOrExit):
* Tools/Scripts/webkitperl/BuildSubproject.pm:
* Tools/Scripts/webkitperl/webkitdirs_unittest/determineDefaultCompilerCMakeArgs.pl: Added.
* Tools/Scripts/webkitperl/webkitdirs_unittest/determineDefaultCompilerClang.pl: Added.
* Tools/Scripts/webkitperl/webkitdirs_unittest/determineDefaultCompilerCrossTarget.pl: Added.
* Tools/Scripts/webkitperl/webkitdirs_unittest/determineDefaultCompilerNoClang.pl: Added.
* Tools/glib/dependencies/apt:
* Tools/glib/dependencies/dnf:
* Tools/glib/dependencies/pacman:
* Tools/yocto/README.md:

Canonical link: https://commits.webkit.org/319452@main
@robobun

robobun commented Aug 24, 2026

Copy link
Copy Markdown
Collaborator Author

The Bun side of this upgrade is oven-sh/bun#40276. #501 (upstream 55d9d9007f, one WebCore-only commit ahead of 8c4fd56347) was a parallel attempt with the same structure and the same CachedTypes.cpp resolution. It is closed in favor of this PR, together with oven-sh/bun#40263.

Main has moved since this branch was cut (#499, #504, #502, #506), so this PR needs a re-merge with main before it can land. The Bun PR's WEBKIT_VERSION moves to the autobuild-<sha> release of the merged commit afterwards.

Conflicts:
- Source/JavaScriptCore/bytecode/UnlinkedCodeBlock.cpp: main switched the
  expression info GC pacing to byteSizeForGCPacing(); upstream deleted
  m_jumpTargets. Both taken.
- Source/JavaScriptCore/runtime/CodeCache.cpp: main added a depth bound to
  generateUnlinkedCodeBlockForFunctions; upstream (c823243) replaced the
  fork's constructor-kind loop with isClassConstructorFunction(). The depth
  bound is applied to upstream's shape.
- Source/JavaScriptCore/runtime/CachedTypes.cpp: main reused the parsed
  tail for the two source-URL directive accessors on CachedCodeBlock; this
  branch moved those directives to CachedGlobalCodeBlock (upstream
  3d0e6c1), so the accessors are gone.

@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.

I reviewed this PR and the automated bug hunting system found no issues. Given the scale (412 upstream commits, 300+ files) and the hand-authored conflict resolutions in CachedTypes.cpp, CodeCache.cpp, the module map, and the Linux thread-scheduling opt-out, a human look is still warranted before this lands.

What was reviewed:

  • The CachedGlobalCodeBlock<T> split and the varint-tail/extras renumbering in CachedTypes.cpp against upstream 3d0e6c14b1/0b92c2bcd0.
  • The generateUnlinkedCodeBlockForFunctions merge in CodeCache.cpp (fork's depth bound applied to upstream's isClassConstructorFunction() shape).
  • The USE(BUN_JSC_ADDITIONS) no-op for Thread::updateSchedulingAttributes and the dropped #262 setFetchError path — both look intentional per the description.
Extended reasoning...

Overview

This PR merges upstream WebKit 8c4fd56347 into the Bun fork: 412 commits since the previous merge base, 90 of them touching JavaScriptCore/WTF/bmalloc, across 300+ files. The bulk is upstream code taken verbatim (tests, LayoutTests, WebCore/WebKit changes the JSCOnly port doesn't compile), but there are substantial hand-authored conflict resolutions where the fork's bytecode-cache rework (#490#497, #502, #506) collided with upstream's UnlinkedCodeBlock/UnlinkedGlobalCodeBlock split (3d0e6c14b1) and m_jumpTargets deletion (0b92c2bcd0), plus fork-policy "keep ours" decisions in YarrJIT.cpp, URLParser.cpp, WaiterListManager::waitSyncImpl, and Linux thread scheduling.

Security risks

The merge touches JIT codegen (DFG/FTL/BBQ/OMG), the Yarr regexp JIT, Wasm bounds checking and fault-signal handling, the bytecode disk cache (untrusted-input decode), and thread scheduling. None of these are new attack surface introduced by the conflict resolutions themselves — they're upstream changes plus fork-side re-plumbing — but a mistake in the CachedTypes.cpp layout renumbering or the regionIsIntact array-count change would be a memory-safety issue in the disk-cache decode path. The description reports the disk-cache round-trip passes on 400+ JSTests with no decode failure or checksum mismatch, and computeJSCBytecodeCacheVersion() invalidates old payloads.

Level of scrutiny

High. This is a production-critical engine upgrade with non-mechanical merge work: a new intermediate CachedGlobalCodeBlock<T> record was authored to reconcile the fork's checksummed-region encoder with upstream's field relocation; the fork deliberately diverges from upstream on Linux sched_setattr behaviour under USE(BUN_JSC_ADDITIONS); and #262's setFetchError was dropped on the reasoning that upstream's module-map change makes it dead. Each of these is a design decision a maintainer should sign off on, not something an automated review can bless.

Other factors

The PR description is unusually thorough — per-commit review, explicit conflict-resolution notes, and verification runs (jsc build, disk-cache round-trip, Bun test suites) — which raises confidence. The re-merge with main (d2654c3b39) that robobun flagged has been done. Still, the combination of scale, hand-authored cache-layout code, and intentional fork-policy divergences puts this well outside what should land without human review.

robobun added a commit to oven-sh/bun that referenced this pull request Aug 25, 2026
WEBKIT_VERSION points at the preview build of oven-sh/WebKit#503
(autobuild-preview-pr-503-311eab61). It has to move to the autobuild-<sha>
release of the merge commit once that PR lands on the fork's main.
robobun added a commit to oven-sh/bun 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
sosukesuzuki merged commit cb61607 into main Aug 25, 2026
47 checks passed
robobun added a commit to oven-sh/bun that referenced this pull request Aug 25, 2026
The preview build autobuild-preview-pr-503-d2654c3b is replaced by the
release the fork's main produced for the merge. The merge commit is
d2654c3b39 plus a0a80b2276 (a depth bound for
recursivelyGenerateUnlinkedCodeBlockForProgram/ForModuleProgram).
sosukesuzuki pushed a commit to oven-sh/bun 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 -->
robobun added a commit to oven-sh/bun that referenced this pull request Aug 25, 2026
Main moved WEBKIT_VERSION to cb61607f (the upstream WebKit merge in
oven-sh/WebKit#503). oven-sh/WebKit#268 is rebased onto that commit, so
its preview carries everything main's pin has plus the two async context
fixes.
robobun added a commit to oven-sh/bun that referenced this pull request Aug 25, 2026
Main moved WEBKIT_VERSION to cb61607f (the upstream WebKit merge in
oven-sh/WebKit#503). oven-sh/WebKit#268 is rebased onto that commit, so
its preview carries everything main's pin has plus the two async context
fixes.
robobun added a commit to oven-sh/bun that referenced this pull request Aug 26, 2026
Main moved WEBKIT_VERSION to cb61607f (the upstream WebKit merge in
oven-sh/WebKit#503). oven-sh/WebKit#268 is rebased onto that commit, so
its preview carries everything main's pin has plus the two async context
fixes.
robobun added a commit to oven-sh/bun that referenced this pull request Aug 26, 2026
Main moved WEBKIT_VERSION to cb61607f (the upstream WebKit merge in
oven-sh/WebKit#503). oven-sh/WebKit#268 is rebased onto that commit, so
its preview carries everything main's pin has plus the two async context
fixes.
robobun added a commit to oven-sh/bun that referenced this pull request Aug 28, 2026
Main moved WEBKIT_VERSION to cb61607f (the upstream WebKit merge in
oven-sh/WebKit#503). oven-sh/WebKit#268 is rebased onto that commit, so
its preview carries everything main's pin has plus the two async context
fixes.
robobun added a commit to oven-sh/bun that referenced this pull request Aug 28, 2026
Main moved WEBKIT_VERSION to cb61607f (the upstream WebKit merge in
oven-sh/WebKit#503). oven-sh/WebKit#268 is rebased onto that commit, so
its preview carries everything main's pin has plus the two async context
fixes.
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.