Files
bun.sh/cmake/tools/SetupWebKit.cmake
Jarred Sumner 4d9752a1f0 Upgrade WebKit to f03492d0636f (#26922)
# JavaScriptCore Upstream Changes: 1b6f54d1c872..upstream/main

**Date range**: Feb 5 -- Feb 11, 2026
**Total JSC commits**: 38

---

## 1. New Features

### WebAssembly JSPI (JavaScript Promise Integration)
**Commit**: `53e97afd3421` -- [JSC] JSPI Implementation
**Files changed**: 59 files, +3879/-148 lines

Full implementation of the [WebAssembly JSPI
proposal](https://github.com/WebAssembly/js-promise-integration). Adds
two new APIs:

- `WebAssembly.promising(wasmFun)` -- wraps a wasm function so it
returns a Promise
- `new WebAssembly.Suspending(jsFun)` -- wraps a JS function so wasm can
suspend on its result

Controlled by `useJSPI` feature flag (disabled by default). This is a
large change that introduces:

- New classes: `EvacuatedStack`, `PinballCompletion`, `JSPIContext`,
`WebAssemblySuspending`, `WebAssemblyPromising`,
`JSWebAssemblySuspendError`
- New `JSPIContext` struct added to `VM` (field `topJSPIContext`)
- New `pinballCompletionStructure` in VM
- New `WebAssemblySuspendError` and `WebAssemblySuspending` lazy class
structures in JSGlobalObject
- New methods on VM: `addEvacuatedStackSlice`,
`removeEvacuatedStackSlice`, `gatherEvacuatedStackRoots`
- New iso subspace: `pinballCompletionSpace`

**Follow-up**: `f67441012b90` -- JSPI cages should only generate JIT
code when JITCage is enabled

### WebAssembly Memory64 in BBQ Tier
**Commit**: `7b98e4b17594` -- Add support for Memory64 in BBQ Tier

Extends Memory64 support from the interpreter to the BBQ JIT tier,
allowing wasm modules to address >4GB of memory.

### WebAssembly Multi-Memory (instantiation only)
**Commit**: `bdf26416947e` -- Support instantiating multiple wasm
memories

Adds support for instantiating multiple memories in wasm modules (but
not executing instructions that use memories other than index 0). Gated
behind `useWasmMultiMemory` flag.

### LOL (Lightweight Optimizing Language) JIT -- New Tier (In Progress)
A new JIT compilation tier "LOL" is being built incrementally across
multiple commits:

- `dd56e0f5b991` -- Add get_argument/argument_count bytecodes
- `fb43184a0a77` -- Add to_primitive-like bytecodes
- `f52fa4a30c76` -- Add switch/throw bytecodes
- `7fd04c82d291` -- Add support for this/prototype bytecodes
- `11127b8a61e0` -- Add support for op_ret

These commits add new files under `Source/JavaScriptCore/lol/`
(LOLJIT.cpp, LOLJIT.h, LOLRegisterAllocator.h). This appears to be a new
lightweight JIT tier between Baseline and DFG.

---

## 2. Performance Improvements

### Deep Rope String Slicing -- 168x faster
**Commit**: `93f2fd68619b` -- [JSC] Limit rope traversal depth in
`tryJSSubstringImpl`

Limits rope traversal depth to 16 in `tryJSSubstringImpl`. Deep rope
strings from repeated concatenation (`s += 'A'`) previously caused
O(n^2) behavior. When the depth limit is exceeded, it falls back to
`resolveRope` to flatten the string.

This directly addresses a performance issue reported against Bun where
it was significantly slower than Node.js.

### String#endsWith DFG/FTL Optimization -- up to 10.5x faster
**Commit**: `901865149859` -- [JSC] Optimize `String#endsWith` in
DFG/FTL

Adds a new DFG/FTL intrinsic `StringPrototypeEndsWithIntrinsic` for
`String.prototype.endsWith`. Constant folding case is 10.5x faster;
general case is 1.45x faster.

### RegExp Flag Getters in DFG/FTL and IC -- 1.6x faster
**Commit**: `1fe86a244d00` -- [JSC] Handle RegExp flag getters in
DFG/FTL and IC

Adds DFG/FTL and inline cache support for RegExp flag property getters
(`.global`, `.ignoreCase`, `.multiline`, `.dotAll`, `.sticky`,
`.unicode`, `.unicodeSets`, `.hasIndices`).

### String#@@iterator as NewInternalFieldObject in DFG/FTL
**Commit**: `44cba41d8eef` -- [JSC] Handle `String#@@iterator` as
`NewInternalFieldObject` in DFG/FTL

Optimizes string iterator creation in DFG/FTL by treating it as a
`NewInternalFieldObject`, enabling allocation sinking.

### ArithMod(Int52) in DFG/FTL
**Commit**: `49a21e7ff327` -- [JSC] Add ArithMod(Int52Use, Int52Use) in
DFG / FTL
**Follow-ups**: `f96be6066671` (constant folding fix), `c8f283248f3f`
(NaNOrInfinity fix)

Adds Int52 modulo support in DFG/FTL, avoiding expensive `fmod` double
operations when inputs are integer-like doubles.

### ArithDiv(Int52) in DFG/FTL -- REVERTED
**Commit**: `582cb0306b7c` -- [JSC] Add ArithDiv(Int52Use, Int52Use) in
DFG / FTL
**Revert**: `2e967edd1dc0` -- Reverted due to JetStream3 regressions

### Intl formatToParts Pre-built Structure -- up to 1.15x faster
**Commit**: `a5dd9753d23c` -- [JSC] Optimize Intl formatToParts methods
with pre-built Structure

Optimizes Intl's `formatToParts` methods by using pre-built Structures
for the returned part objects.

### JSBigInt Inline Storage
**Commit**: `dbc50284d4cf` -- [JSC] Inline storage into JSBigInt
**Related**: `304cf0713b9d` -- Remove JSBigInt::rightTrim and
JSBigInt::tryRightTrim

Major structural change to JSBigInt: digits are now stored inline
(trailing storage) instead of via a separate `CagedBarrierPtr`. This
eliminates the separate allocation and pointer indirection.
`tryRightTrim` and `rightTrim` removed; `createFrom` renamed to
`tryCreateFrom` with span-based API.

### WYHash Always Enabled for Strings
**Commit**: `14ba1421ca08` -- [JSC] Always use WYHash for strings

Previously, WYHash had a threshold (disabled for short strings on weak
devices). Now it's enabled regardless of string size.

### JIT Worklist Thread Count: 3 -> 4
**Commit**: `453c578cadf6` -- [JSC] Bump JIT worklist thread number from
3 to 4

### Greedy Register Allocator Improvements
**Commit**: `b642ae17aade` -- [JSC] Proactively coalesce spill slots
during register allocation
**Commit**: `a78705db7c08` -- [JSC] GreedyRegAlloc: amortize cost of
clearing the visited set in the evict loop

---

## 3. Bug Fixes

### RegExp (YARR) Fixes
- `dea6808af40e` -- **Fix stale captures in FixedCount groups in
MatchOnly mode**: `.test()` was not clearing captures between FixedCount
iterations, causing stale values visible to backreferences.

- `437e137889ea` -- **Fix infinite loop in JIT for non-greedy
backreference to zero-width capture**: A non-greedy backreference like
`\1*?` could loop forever when the referenced capture was undefined or
empty.

- `d4f884d21c0e` -- **Fix backtracking in NestedAlternativeEnd**: Wrong
jump target was used when backtracking from NestedAlternativeEnd.

### ARM64 Cached Immediate Fix
**Commit**: `8396ad321ad0` -- [JSC] Fix edge case issue of cached imm in
ARM64

TrustedImm32(-1) was not zero-extended when cached, causing wrong reuse
when followed by TrustedImm64(-1).

### Wasm Fixes
- `cea8513d43ef` -- **Fix RefCast/RefTest folding in B3**: The condition
was inverted, causing wrong cast results.
- `629632da96e1` -- **Fix debugger races with idle and active VMs in
stop-the-world**
- `e5391ad90f47` -- **Stack check on IPInt->BBQ loop OSR entry**
- `fd15e0d70ab1` -- **Use FrameTracer in wasm table_init operations**

### Structure Heap Alignment Assert Fix
**Commit**: `47b52526fd42` -- [JSC] Fix startOfStructureHeap alignment
RELEASE_ASSERT

---

## 4. Bun-side Changes

- Updated `WEBKIT_VERSION` to `f03492d0636f`
- Updated `SerializedScriptValue.cpp`: replaced removed
`JSBigInt::tryRightTrim` with `JSBigInt::tryCreateFrom` span-based API
- Fixed `CloneSerializer`/`CloneDeserializer` inheritance (`private` ->
`public CloneBase`)
- Explicitly disabled `ENABLE_MEDIA_SOURCE`, `ENABLE_MEDIA_STREAM`,
`ENABLE_WEB_RTC` in `build.ts` and `SetupWebKit.cmake` for JSCOnly port

---

## 5. Summary by Component

| Component | Commits | Key Changes |
|-----------|---------|-------------|
| **WASM** | 9 | JSPI implementation, Memory64 BBQ, multi-memory,
RefCast fix, stack checks, debugger races |
| **DFG/FTL** | 6 | String#endsWith opt, RegExp flags opt,
String@@iterator opt, ArithMod(Int52) |
| **YARR (RegExp)** | 3 | Stale captures fix, infinite loop fix,
NestedAlternativeEnd backtracking fix |
| **B3/Air** | 3 | Spill slot coalescing, generational set for eviction,
RefCast/RefTest fix |
| **LOL JIT** | 5 | New JIT tier being built incrementally |
| **Runtime** | 6 | BigInt inline storage, WYHash always-on, Intl
formatToParts opt, ARM64 imm fix, rope depth limit |

---------

Co-authored-by: Sosuke Suzuki <sosuke@bun.com>
2026-02-23 01:28:37 -08:00

263 lines
8.4 KiB
CMake

# NOTE: Changes to this file trigger QEMU JIT stress tests in CI.
# See scripts/verify-jit-stress-qemu.sh for details.
option(WEBKIT_VERSION "The version of WebKit to use")
option(WEBKIT_LOCAL "If a local version of WebKit should be used instead of downloading")
option(WEBKIT_BUILD_TYPE "The build type for local WebKit (defaults to CMAKE_BUILD_TYPE)")
if(NOT WEBKIT_VERSION)
set(WEBKIT_VERSION 4a6a32c32c11ffb9f5a94c310b10f50130bfe6de)
endif()
string(SUBSTRING ${WEBKIT_VERSION} 0 16 WEBKIT_VERSION_PREFIX)
string(SUBSTRING ${WEBKIT_VERSION} 0 8 WEBKIT_VERSION_SHORT)
if(WEBKIT_LOCAL)
if(NOT WEBKIT_BUILD_TYPE)
set(WEBKIT_BUILD_TYPE ${CMAKE_BUILD_TYPE})
endif()
set(DEFAULT_WEBKIT_PATH ${VENDOR_PATH}/WebKit/WebKitBuild/${WEBKIT_BUILD_TYPE})
else()
set(DEFAULT_WEBKIT_PATH ${CACHE_PATH}/webkit-${WEBKIT_VERSION_PREFIX})
endif()
option(WEBKIT_PATH "The path to the WebKit directory")
if(NOT WEBKIT_PATH)
set(WEBKIT_PATH ${DEFAULT_WEBKIT_PATH})
endif()
set(WEBKIT_INCLUDE_PATH ${WEBKIT_PATH}/include)
set(WEBKIT_LIB_PATH ${WEBKIT_PATH}/lib)
if(WEBKIT_LOCAL)
set(WEBKIT_SOURCE_DIR ${VENDOR_PATH}/WebKit)
if(WIN32)
# --- Build ICU from source (Windows only) ---
# On macOS, ICU is found automatically (Homebrew icu4c for headers, system for libs).
# On Linux, ICU is found automatically from system packages (e.g. libicu-dev).
# On Windows, there is no system ICU, so we build it from source.
set(ICU_LOCAL_ROOT ${VENDOR_PATH}/WebKit/WebKitBuild/icu)
if(NOT EXISTS ${ICU_LOCAL_ROOT}/lib/sicudt.lib)
message(STATUS "Building ICU from source...")
if(CMAKE_SYSTEM_PROCESSOR MATCHES "arm64|ARM64|aarch64|AARCH64")
set(ICU_PLATFORM "ARM64")
else()
set(ICU_PLATFORM "x64")
endif()
execute_process(
COMMAND powershell -ExecutionPolicy Bypass -File
${WEBKIT_SOURCE_DIR}/build-icu.ps1
-Platform ${ICU_PLATFORM}
-BuildType ${WEBKIT_BUILD_TYPE}
-OutputDir ${ICU_LOCAL_ROOT}
RESULT_VARIABLE ICU_BUILD_RESULT
)
if(NOT ICU_BUILD_RESULT EQUAL 0)
message(FATAL_ERROR "Failed to build ICU (exit code: ${ICU_BUILD_RESULT}).")
endif()
endif()
# Copy ICU libs to WEBKIT_LIB_PATH with the names BuildBun.cmake expects.
# Prebuilt WebKit uses 's' prefix (static) and 'd' suffix (debug).
file(MAKE_DIRECTORY ${WEBKIT_LIB_PATH})
if(WEBKIT_BUILD_TYPE STREQUAL "Debug")
set(ICU_SUFFIX "d")
else()
set(ICU_SUFFIX "")
endif()
file(COPY_FILE ${ICU_LOCAL_ROOT}/lib/sicudt.lib ${WEBKIT_LIB_PATH}/sicudt${ICU_SUFFIX}.lib ONLY_IF_DIFFERENT)
file(COPY_FILE ${ICU_LOCAL_ROOT}/lib/icuin.lib ${WEBKIT_LIB_PATH}/sicuin${ICU_SUFFIX}.lib ONLY_IF_DIFFERENT)
file(COPY_FILE ${ICU_LOCAL_ROOT}/lib/icuuc.lib ${WEBKIT_LIB_PATH}/sicuuc${ICU_SUFFIX}.lib ONLY_IF_DIFFERENT)
endif()
# --- Configure JSC ---
message(STATUS "Configuring JSC from local WebKit source at ${WEBKIT_SOURCE_DIR}...")
set(JSC_CMAKE_ARGS
-S ${WEBKIT_SOURCE_DIR}
-B ${WEBKIT_PATH}
-G ${CMAKE_GENERATOR}
-DPORT=JSCOnly
-DENABLE_STATIC_JSC=ON
-DUSE_THIN_ARCHIVES=OFF
-DENABLE_FTL_JIT=ON
-DCMAKE_EXPORT_COMPILE_COMMANDS=ON
-DUSE_BUN_JSC_ADDITIONS=ON
-DUSE_BUN_EVENT_LOOP=ON
-DENABLE_BUN_SKIP_FAILING_ASSERTIONS=ON
-DALLOW_LINE_AND_COLUMN_NUMBER_IN_BUILTINS=ON
-DCMAKE_BUILD_TYPE=${WEBKIT_BUILD_TYPE}
-DCMAKE_C_COMPILER=${CMAKE_C_COMPILER}
-DCMAKE_CXX_COMPILER=${CMAKE_CXX_COMPILER}
-DENABLE_REMOTE_INSPECTOR=ON
-DENABLE_MEDIA_SOURCE=OFF
-DENABLE_MEDIA_STREAM=OFF
-DENABLE_WEB_RTC=OFF
)
if(WIN32)
# ICU paths and Windows-specific compiler/linker settings
list(APPEND JSC_CMAKE_ARGS
-DICU_ROOT=${ICU_LOCAL_ROOT}
-DICU_LIBRARY=${ICU_LOCAL_ROOT}/lib
-DICU_INCLUDE_DIR=${ICU_LOCAL_ROOT}/include
-DCMAKE_LINKER=lld-link
)
# Static CRT and U_STATIC_IMPLEMENTATION
if(WEBKIT_BUILD_TYPE STREQUAL "Debug")
set(JSC_MSVC_RUNTIME "MultiThreadedDebug")
else()
set(JSC_MSVC_RUNTIME "MultiThreaded")
endif()
list(APPEND JSC_CMAKE_ARGS
-DCMAKE_MSVC_RUNTIME_LIBRARY=${JSC_MSVC_RUNTIME}
"-DCMAKE_C_FLAGS=/DU_STATIC_IMPLEMENTATION"
"-DCMAKE_CXX_FLAGS=/DU_STATIC_IMPLEMENTATION /clang:-fno-c++-static-destructors"
)
endif()
if(ENABLE_ASAN)
list(APPEND JSC_CMAKE_ARGS -DENABLE_SANITIZERS=address)
endif()
# Pass through ccache if available
if(CMAKE_C_COMPILER_LAUNCHER)
list(APPEND JSC_CMAKE_ARGS -DCMAKE_C_COMPILER_LAUNCHER=${CMAKE_C_COMPILER_LAUNCHER})
endif()
if(CMAKE_CXX_COMPILER_LAUNCHER)
list(APPEND JSC_CMAKE_ARGS -DCMAKE_CXX_COMPILER_LAUNCHER=${CMAKE_CXX_COMPILER_LAUNCHER})
endif()
execute_process(
COMMAND ${CMAKE_COMMAND} ${JSC_CMAKE_ARGS}
RESULT_VARIABLE JSC_CONFIGURE_RESULT
)
if(NOT JSC_CONFIGURE_RESULT EQUAL 0)
message(FATAL_ERROR "Failed to configure JSC (exit code: ${JSC_CONFIGURE_RESULT}). "
"Check the output above for errors.")
endif()
if(WIN32)
set(JSC_BYPRODUCTS
${WEBKIT_LIB_PATH}/JavaScriptCore.lib
${WEBKIT_LIB_PATH}/WTF.lib
${WEBKIT_LIB_PATH}/bmalloc.lib
)
else()
set(JSC_BYPRODUCTS
${WEBKIT_LIB_PATH}/libJavaScriptCore.a
${WEBKIT_LIB_PATH}/libWTF.a
${WEBKIT_LIB_PATH}/libbmalloc.a
)
endif()
if(WIN32)
add_custom_target(jsc ALL
COMMAND ${CMAKE_COMMAND} --build ${WEBKIT_PATH} --config ${WEBKIT_BUILD_TYPE} --target jsc
BYPRODUCTS ${JSC_BYPRODUCTS}
COMMENT "Building JSC (${WEBKIT_PATH})"
)
else()
add_custom_target(jsc ALL
COMMAND ${CMAKE_COMMAND} --build ${WEBKIT_PATH} --config ${WEBKIT_BUILD_TYPE} --target jsc
BYPRODUCTS ${JSC_BYPRODUCTS}
COMMENT "Building JSC (${WEBKIT_PATH})"
USES_TERMINAL
)
endif()
include_directories(
${WEBKIT_PATH}
${WEBKIT_PATH}/JavaScriptCore/Headers
${WEBKIT_PATH}/JavaScriptCore/Headers/JavaScriptCore
${WEBKIT_PATH}/JavaScriptCore/PrivateHeaders
${WEBKIT_PATH}/bmalloc/Headers
${WEBKIT_PATH}/WTF/Headers
${WEBKIT_PATH}/JavaScriptCore/PrivateHeaders/JavaScriptCore
)
# On Windows, add ICU headers from the local ICU build
if(WIN32)
include_directories(${ICU_LOCAL_ROOT}/include)
endif()
# After this point, only prebuilt WebKit is supported
return()
endif()
if(WIN32)
set(WEBKIT_OS "windows")
elseif(APPLE)
set(WEBKIT_OS "macos")
elseif(UNIX)
set(WEBKIT_OS "linux")
else()
message(FATAL_ERROR "Unsupported operating system: ${CMAKE_SYSTEM_NAME}")
endif()
if(CMAKE_SYSTEM_PROCESSOR MATCHES "arm64|ARM64|aarch64|AARCH64")
set(WEBKIT_ARCH "arm64")
elseif(CMAKE_SYSTEM_PROCESSOR MATCHES "amd64|x86_64|x64|AMD64")
set(WEBKIT_ARCH "amd64")
else()
message(FATAL_ERROR "Unsupported architecture: ${CMAKE_SYSTEM_PROCESSOR}")
endif()
if(LINUX AND ABI STREQUAL "musl")
set(WEBKIT_SUFFIX "-musl")
endif()
if(DEBUG)
set(WEBKIT_SUFFIX "${WEBKIT_SUFFIX}-debug")
elseif(ENABLE_LTO)
set(WEBKIT_SUFFIX "${WEBKIT_SUFFIX}-lto")
else()
set(WEBKIT_SUFFIX "${WEBKIT_SUFFIX}")
endif()
if(ENABLE_ASAN)
# We cannot mix and match ASan Bun + non-ASan WebKit, or vice versa, because some WebKit classes
# change their layout according to whether ASan is used, for example:
# https://github.com/oven-sh/WebKit/blob/eda8b0fb4fb1aa23db9c2b00933df8b58bcdd289/Source/WTF/wtf/Vector.h#L682
set(WEBKIT_SUFFIX "${WEBKIT_SUFFIX}-asan")
endif()
setx(WEBKIT_NAME bun-webkit-${WEBKIT_OS}-${WEBKIT_ARCH}${WEBKIT_SUFFIX})
set(WEBKIT_FILENAME ${WEBKIT_NAME}.tar.gz)
if(WEBKIT_VERSION MATCHES "^autobuild-")
set(WEBKIT_TAG ${WEBKIT_VERSION})
else()
set(WEBKIT_TAG autobuild-${WEBKIT_VERSION})
endif()
setx(WEBKIT_DOWNLOAD_URL https://github.com/oven-sh/WebKit/releases/download/${WEBKIT_TAG}/${WEBKIT_FILENAME})
if(EXISTS ${WEBKIT_PATH}/package.json)
file(READ ${WEBKIT_PATH}/package.json WEBKIT_PACKAGE_JSON)
if(WEBKIT_PACKAGE_JSON MATCHES ${WEBKIT_VERSION})
return()
endif()
endif()
file(
DOWNLOAD ${WEBKIT_DOWNLOAD_URL} ${CACHE_PATH}/${WEBKIT_FILENAME} SHOW_PROGRESS
STATUS WEBKIT_DOWNLOAD_STATUS
)
if(NOT "${WEBKIT_DOWNLOAD_STATUS}" MATCHES "^0;")
message(FATAL_ERROR "Failed to download WebKit: ${WEBKIT_DOWNLOAD_STATUS}")
endif()
file(ARCHIVE_EXTRACT INPUT ${CACHE_PATH}/${WEBKIT_FILENAME} DESTINATION ${CACHE_PATH} TOUCH)
file(REMOVE ${CACHE_PATH}/${WEBKIT_FILENAME})
file(REMOVE_RECURSE ${WEBKIT_PATH})
file(RENAME ${CACHE_PATH}/bun-webkit ${WEBKIT_PATH})
if(APPLE)
file(REMOVE_RECURSE ${WEBKIT_INCLUDE_PATH}/unicode)
endif()