From ebfe4d08c35913d7fba0f448cfb9dcd7ef7e91e7 Mon Sep 17 00:00:00 2001 From: Jarred Sumner Date: Tue, 28 Apr 2026 23:55:04 +0000 Subject: [PATCH 01/12] WebKit upgrade to 46525dbf2abb (oven-sh/WebKit@f7b52b08) Adopts upstream's libpas Windows is_symmetric design (WebKit/WebKit#63762) while keeping Bun's TLC suspender / shutdown-guard / lazy-commit patches. Binding updates: - JSType.zig: PromiseReaction split into Slim/Full (+1 enum shift) - bindings.cpp: JSPromiseReaction::context() -> tryGetContext() - NodeVM.cpp: promiseEmptyOnRejectedFunction() removed -> jsUndefined() - NodeVMSourceTextModule.cpp / BunAnalyzeTranspiledModule.cpp: status()/hasTLA() setters renamed to setStatus()/setHasTLA() Unrelated fixes surfaced while validating: - dns.zig: c-ares can flip a TCP socket between writable/readable; FilePoll is single-direction, so unregister before re-registering instead of stacking both flags (tripped unregister assertion on conn error) - test/harness.ts: expectMaxObjectTypeCount treated 'undefined' count as not-yet-collected (undefined <= n is false), looping the full wait - sourcetextmodule-leak.test: scale iterations on debug builds --- scripts/build/deps/webkit.ts | 2 +- src/bun.js/api/bun/dns.zig | 17 +- .../bindings/BunAnalyzeTranspiledModule.cpp | 2 +- src/bun.js/bindings/JSType.zig | 156 +++++++++--------- src/bun.js/bindings/NodeVM.cpp | 2 +- .../bindings/NodeVMSourceTextModule.cpp | 4 +- src/bun.js/bindings/bindings.cpp | 5 +- test/harness.ts | 6 +- test/js/node/vm/sourcetextmodule-leak.test.ts | 48 +++--- 9 files changed, 135 insertions(+), 107 deletions(-) diff --git a/scripts/build/deps/webkit.ts b/scripts/build/deps/webkit.ts index fc1894d4958..6f059cff5d3 100644 --- a/scripts/build/deps/webkit.ts +++ b/scripts/build/deps/webkit.ts @@ -3,7 +3,7 @@ * for local mode. Override via `--webkit-version=` to test a branch. * From https://github.com/oven-sh/WebKit releases. */ -export const WEBKIT_VERSION = "137c59666cfb02c45d04dcfd4b3852e27f78cffc"; +export const WEBKIT_VERSION = "f7b52b080538b5c2779a0b20db2e5f9c17fc807c"; /** * WebKit (JavaScriptCore) — the JS engine. diff --git a/src/bun.js/api/bun/dns.zig b/src/bun.js/api/bun/dns.zig index 4122c603f4a..4351b93199b 100644 --- a/src/bun.js/api/bun/dns.zig +++ b/src/bun.js/api/bun/dns.zig @@ -2545,10 +2545,23 @@ pub const Resolver = struct { var poll = poll_entry.value_ptr.*; - if (readable and !poll.flags.contains(.poll_readable)) + // FilePoll is single-direction; c-ares can flip a TCP socket between + // writable (connecting) and readable (response). If the requested + // direction set differs from what's registered, drop the old + // registration first so we never hold both .poll_readable and + // .poll_writable on the same poll (unregister asserts on that). + const want_readable = readable; + const want_writable = writable and !readable; + const have_readable = poll.flags.contains(.poll_readable); + const have_writable = poll.flags.contains(.poll_writable); + if ((have_readable and !want_readable) or (have_writable and !want_writable)) { + _ = poll.unregister(vm.event_loop_handle.?, false); + } + + if (want_readable and !poll.flags.contains(.poll_readable)) _ = poll.register(vm.event_loop_handle.?, .readable, false); - if (writable and !poll.flags.contains(.poll_writable)) + if (want_writable and !poll.flags.contains(.poll_writable)) _ = poll.register(vm.event_loop_handle.?, .writable, false); } } diff --git a/src/bun.js/bindings/BunAnalyzeTranspiledModule.cpp b/src/bun.js/bindings/BunAnalyzeTranspiledModule.cpp index dfa78f8b20b..f33f9469222 100644 --- a/src/bun.js/bindings/BunAnalyzeTranspiledModule.cpp +++ b/src/bun.js/bindings/BunAnalyzeTranspiledModule.cpp @@ -73,7 +73,7 @@ extern "C" JSModuleRecord* JSC_JSModuleRecord__create(JSGlobalObject* globalObje { JSModuleRecord* result = JSModuleRecord::create(globalObject, vm, globalObject->moduleRecordStructure(), *moduleKey, sourceCode, declaredVariables, lexicalVariables, hasImportMeta ? ImportMetaFeature : 0); result->m_isTypeScript = isTypescript; - result->hasTLA(hasTLA); + result->setHasTLA(hasTLA); return result; } diff --git a/src/bun.js/bindings/JSType.zig b/src/bun.js/bindings/JSType.zig index f43cd4768df..b111f9c6a94 100644 --- a/src/bun.js/bindings/JSType.zig +++ b/src/bun.js/bindings/JSType.zig @@ -187,51 +187,54 @@ pub const JSType = enum(u8) { JSCellButterfly = 21, JSSourceCode = 22, - /// Promise reaction object for tracking promise callbacks. + /// Slim promise reaction (no rejection handler / context payload). /// Internal object used in the promise resolution mechanism. - /// Note: Moved before ObjectType in recent WebKit. - PromiseReaction = 23, + SlimPromiseReaction = 23, + + /// Full promise reaction (carries onFulfilled/onRejected and async context). + /// Internal object used in the promise resolution mechanism. + FullPromiseReaction = 24, /// Context object for Promise.all() operations. /// Internal object used to track the state of Promise.all() resolution. /// Note: Moved before ObjectType in recent WebKit. - PromiseAllContext = 24, + PromiseAllContext = 25, /// Global context for Promise.all() (new in recent WebKit). - PromiseAllGlobalContext = 25, + PromiseAllGlobalContext = 26, /// Microtask dispatcher for promise/microtask queue management. - JSMicrotaskDispatcher = 26, + JSMicrotaskDispatcher = 27, /// Module loader registry entry (new C++ module loader). - ModuleRegistryEntry = 27, + ModuleRegistryEntry = 28, /// Module loading context (new C++ module loader). - ModuleLoadingContext = 28, + ModuleLoadingContext = 29, /// Module loader payload (new C++ module loader). - ModuleLoaderPayload = 29, + ModuleLoaderPayload = 30, /// Module graph loading state (new C++ module loader). - ModuleGraphLoadingState = 30, + ModuleGraphLoadingState = 31, /// JSModuleLoader cell type (new C++ module loader). - JSModuleLoader = 31, + JSModuleLoader = 32, /// Base JavaScript object type. /// ```js /// {} /// new Object() /// ``` - Object = 32, + Object = 33, /// Optimized object type for object literals with fixed properties. /// ```js /// { a: 1, b: 2 } /// ``` - FinalObject = 33, + FinalObject = 34, - JSCallee = 34, + JSCallee = 35, /// JavaScript function object created from JavaScript source code. /// ```js @@ -241,7 +244,7 @@ pub const JSType = enum(u8) { /// method() {} /// } /// ``` - JSFunction = 35, + JSFunction = 36, /// Built-in function implemented in native code. /// ```js @@ -250,23 +253,23 @@ pub const JSType = enum(u8) { /// parseInt /// console.log /// ``` - InternalFunction = 36, + InternalFunction = 37, - NullSetterFunction = 37, + NullSetterFunction = 38, /// Boxed Boolean object. /// ```js /// new Boolean(true) /// new Boolean(false) /// ``` - BooleanObject = 38, + BooleanObject = 39, /// Boxed Number object. /// ```js /// new Number(42) /// new Number(3.14) /// ``` - NumberObject = 39, + NumberObject = 40, /// JavaScript Error object and its subclasses. /// ```js @@ -274,9 +277,9 @@ pub const JSType = enum(u8) { /// new TypeError() /// throw new RangeError() /// ``` - ErrorInstance = 40, + ErrorInstance = 41, - GlobalProxy = 41, + GlobalProxy = 42, /// Arguments object for function parameters. /// ```js @@ -285,10 +288,10 @@ pub const JSType = enum(u8) { /// console.log(arguments.length); /// } /// ``` - DirectArguments = 42, + DirectArguments = 43, - ScopedArguments = 43, - ClonedArguments = 44, + ScopedArguments = 44, + ClonedArguments = 45, /// JavaScript Array object. /// ```js @@ -297,94 +300,94 @@ pub const JSType = enum(u8) { /// new Array(10) /// Array.from(iterable) /// ``` - Array = 45, + Array = 46, /// Array subclass created through class extension. /// ```js /// class MyArray extends Array {} /// const arr = new MyArray(); /// ``` - DerivedArray = 46, + DerivedArray = 47, /// ArrayBuffer for binary data storage. /// ```js /// new ArrayBuffer(1024) /// ``` - ArrayBuffer = 47, + ArrayBuffer = 48, /// Typed array for 8-bit signed integers. /// ```js /// new Int8Array(buffer) /// new Int8Array([1, -1, 127]) /// ``` - Int8Array = 48, + Int8Array = 49, /// Typed array for 8-bit unsigned integers. /// ```js /// new Uint8Array(buffer) /// new Uint8Array([0, 255]) /// ``` - Uint8Array = 49, + Uint8Array = 50, /// Typed array for 8-bit unsigned integers with clamping. /// ```js /// new Uint8ClampedArray([0, 300]) // 300 becomes 255 /// ``` - Uint8ClampedArray = 50, + Uint8ClampedArray = 51, /// Typed array for 16-bit signed integers. /// ```js /// new Int16Array(buffer) /// ``` - Int16Array = 51, + Int16Array = 52, /// Typed array for 16-bit unsigned integers. /// ```js /// new Uint16Array(buffer) /// ``` - Uint16Array = 52, + Uint16Array = 53, /// Typed array for 32-bit signed integers. /// ```js /// new Int32Array(buffer) /// ``` - Int32Array = 53, + Int32Array = 54, /// Typed array for 32-bit unsigned integers. /// ```js /// new Uint32Array(buffer) /// ``` - Uint32Array = 54, + Uint32Array = 55, /// Typed array for 16-bit floating point numbers. /// ```js /// new Float16Array(buffer) /// ``` - Float16Array = 55, + Float16Array = 56, /// Typed array for 32-bit floating point numbers. /// ```js /// new Float32Array(buffer) /// ``` - Float32Array = 56, + Float32Array = 57, /// Typed array for 64-bit floating point numbers. /// ```js /// new Float64Array(buffer) /// ``` - Float64Array = 57, + Float64Array = 58, /// Typed array for 64-bit signed BigInt values. /// ```js /// new BigInt64Array([123n, -456n]) /// ``` - BigInt64Array = 58, + BigInt64Array = 59, /// Typed array for 64-bit unsigned BigInt values. /// ```js /// new BigUint64Array([123n, 456n]) /// ``` - BigUint64Array = 59, + BigUint64Array = 60, /// DataView for flexible binary data access. /// ```js @@ -392,7 +395,7 @@ pub const JSType = enum(u8) { /// view.getInt32(0) /// view.setFloat64(8, 3.14) /// ``` - DataView = 60, + DataView = 61, /// Global object containing all global variables and functions. /// ```js @@ -400,12 +403,12 @@ pub const JSType = enum(u8) { /// window // in browsers /// global // in Node.js /// ``` - GlobalObject = 61, + GlobalObject = 62, - GlobalLexicalEnvironment = 62, - LexicalEnvironment = 63, - ModuleEnvironment = 64, - StrictEvalActivation = 65, + GlobalLexicalEnvironment = 63, + LexicalEnvironment = 64, + ModuleEnvironment = 65, + StrictEvalActivation = 66, /// Scope object for with statements. /// ```js @@ -413,19 +416,19 @@ pub const JSType = enum(u8) { /// prop; // looks up prop in obj first /// } /// ``` - WithScope = 66, + WithScope = 67, - AsyncDisposableStack = 67, - DisposableStack = 68, + AsyncDisposableStack = 68, + DisposableStack = 69, /// Namespace object for ES6 modules. /// ```js /// import * as ns from 'module'; /// ns.exportedFunction() /// ``` - ModuleNamespaceObject = 69, + ModuleNamespaceObject = 70, - ShadowRealm = 70, + ShadowRealm = 71, /// Regular expression object. /// ```js @@ -433,7 +436,7 @@ pub const JSType = enum(u8) { /// new RegExp('pattern', 'flags') /// /abc/gi /// ``` - RegExpObject = 71, + RegExpObject = 72, /// JavaScript Date object for date/time operations. /// ```js @@ -441,7 +444,7 @@ pub const JSType = enum(u8) { /// new Date('2023-01-01') /// Date.now() /// ``` - JSDate = 72, + JSDate = 73, /// Proxy object that intercepts operations on another object. /// ```js @@ -449,7 +452,7 @@ pub const JSType = enum(u8) { /// get(obj, prop) { return obj[prop]; } /// }) /// ``` - ProxyObject = 73, + ProxyObject = 74, /// Generator object created by generator functions. /// ```js @@ -457,7 +460,7 @@ pub const JSType = enum(u8) { /// const g = gen(); /// g.next() /// ``` - Generator = 74, + Generator = 75, /// Async generator object for asynchronous iteration. /// ```js @@ -465,17 +468,17 @@ pub const JSType = enum(u8) { /// yield await promise; /// } /// ``` - AsyncGenerator = 75, + AsyncGenerator = 76, /// Iterator for Array objects. /// ```js /// [1,2,3][Symbol.iterator]() /// for (const x of array) {} /// ``` - JSArrayIterator = 76, + JSArrayIterator = 77, - Iterator = 77, - IteratorHelper = 78, + Iterator = 78, + IteratorHelper = 79, /// Iterator for Map objects. /// ```js @@ -484,32 +487,32 @@ pub const JSType = enum(u8) { /// map.entries() /// for (const [k,v] of map) {} /// ``` - MapIterator = 79, + MapIterator = 80, /// Iterator for Set objects. /// ```js /// set.values() /// for (const value of set) {} /// ``` - SetIterator = 80, + SetIterator = 81, /// Iterator for String objects. /// ```js /// 'hello'[Symbol.iterator]() /// for (const char of string) {} /// ``` - StringIterator = 81, + StringIterator = 82, - WrapForValidIterator = 82, + WrapForValidIterator = 83, /// Iterator for RegExp string matching. /// ```js /// 'abc'.matchAll(/./g) /// for (const match of string.matchAll(regex)) {} /// ``` - RegExpStringIterator = 83, + RegExpStringIterator = 84, - AsyncFromSyncIterator = 84, + AsyncFromSyncIterator = 85, /// JavaScript Promise object for asynchronous operations. /// ```js @@ -517,7 +520,7 @@ pub const JSType = enum(u8) { /// Promise.resolve(42) /// async function foo() { await promise; } /// ``` - JSPromise = 85, + JSPromise = 86, /// JavaScript Map object for key-value storage. /// ```js @@ -525,7 +528,7 @@ pub const JSType = enum(u8) { /// map.set(key, value) /// map.get(key) /// ``` - Map = 86, + Map = 87, /// JavaScript Set object for unique value storage. /// ```js @@ -533,34 +536,34 @@ pub const JSType = enum(u8) { /// set.add(value) /// set.has(value) /// ``` - Set = 87, + Set = 88, /// WeakMap for weak key-value references. /// ```js /// new WeakMap() /// weakMap.set(object, value) /// ``` - WeakMap = 88, + WeakMap = 89, /// WeakSet for weak value references. /// ```js /// new WeakSet() /// weakSet.add(object) /// ``` - WeakSet = 89, + WeakSet = 90, - WebAssemblyModule = 90, - WebAssemblyInstance = 91, - WebAssemblyGCObject = 92, + WebAssemblyModule = 91, + WebAssemblyInstance = 92, + WebAssemblyGCObject = 93, /// Boxed String object. /// ```js /// new String("hello") /// ``` - StringObject = 93, + StringObject = 94, - DerivedStringObject = 94, - InternalFieldTuple = 95, + DerivedStringObject = 95, + InternalFieldTuple = 96, MaxJS = 0b11111111, Event = 0b11101111, @@ -719,7 +722,8 @@ pub const JSType = enum(u8) { .CodeBlock, .JSCellButterfly, .JSSourceCode, - .PromiseReaction, + .SlimPromiseReaction, + .FullPromiseReaction, .PromiseAllContext, .PromiseAllGlobalContext, => true, diff --git a/src/bun.js/bindings/NodeVM.cpp b/src/bun.js/bindings/NodeVM.cpp index 2e5528b7be7..dae4e540c9d 100644 --- a/src/bun.js/bindings/NodeVM.cpp +++ b/src/bun.js/bindings/NodeVM.cpp @@ -344,7 +344,7 @@ static JSPromise* importModuleInner(JSGlobalObject* globalObject, JSString* modu promise->fulfill(vm, globalObject, result); RETURN_IF_EXCEPTION(scope, nullptr); - JSObject* thenResult = promise->then(globalObject, transformer, globalObject->promiseEmptyOnRejectedFunction()); + JSObject* thenResult = promise->then(globalObject, transformer, jsUndefined()); RETURN_IF_EXCEPTION(scope, nullptr); // JSPromise::then() may return a non-JSPromise when Promise[Symbol.species] diff --git a/src/bun.js/bindings/NodeVMSourceTextModule.cpp b/src/bun.js/bindings/NodeVMSourceTextModule.cpp index cba4984e3fc..33ab43fd60b 100644 --- a/src/bun.js/bindings/NodeVMSourceTextModule.cpp +++ b/src/bun.js/bindings/NodeVMSourceTextModule.cpp @@ -352,7 +352,7 @@ JSValue NodeVMSourceTextModule::link(JSGlobalObject* globalObject, JSArray* spec // Unlinked for the whole graph), so do it for each dependency as // we wire it in. if (auto* cyclic = dynamicDowncast(resolvedRecord); cyclic && cyclic->status() == JSC::CyclicModuleRecord::Status::New) - cyclic->status(JSC::CyclicModuleRecord::Status::Unlinked); + cyclic->setStatus(JSC::CyclicModuleRecord::Status::Unlinked); // specifiers/moduleNatives were built from requestedModules() in // [kLink], so the indices line up — pass the original ModuleRequest @@ -378,7 +378,7 @@ JSValue NodeVMSourceTextModule::link(JSGlobalObject* globalObject, JSArray* spec // innerModuleLinking now would walk into a record whose loadedModules() // are still empty (cyclic case). if (record->status() == JSC::CyclicModuleRecord::Status::New) - record->status(JSC::CyclicModuleRecord::Status::Unlinked); + record->setStatus(JSC::CyclicModuleRecord::Status::Unlinked); UNUSED_PARAM(scriptFetcher); status(Status::Linked); diff --git a/src/bun.js/bindings/bindings.cpp b/src/bun.js/bindings/bindings.cpp index ee42eaa398e..7a345e0f7b3 100644 --- a/src/bun.js/bindings/bindings.cpp +++ b/src/bun.js/bindings/bindings.cpp @@ -2272,10 +2272,11 @@ static void collectAsyncStackFramesFromPromise(JSC::VM& vm, JSC::JSCell* owner, for (unsigned hops = 0; p && hops < 32; hops++) { if (p->status() != JSC::JSPromise::Status::Pending) return nullptr; + JSC::JSValue reactionsValue = p->reactionsOrResult(); JSC::JSPromiseReaction* reaction = nullptr; - if (!dynamicCastValue(p->reactionsOrResult(), &reaction)) + if (!dynamicCastValue(reactionsValue, &reaction)) return nullptr; - JSC::JSValue context = reaction->context(); + JSC::JSValue context = JSC::JSPromiseReaction::tryGetContext(reactionsValue); JSC::InternalFieldTuple* tuple = nullptr; if (dynamicCastValue(context, &tuple)) context = tuple->getInternalField(0); diff --git a/test/harness.ts b/test/harness.ts index 25366c636dc..98ca67455d9 100644 --- a/test/harness.ts +++ b/test/harness.ts @@ -142,14 +142,14 @@ export async function expectMaxObjectTypeCount( var { heapStats } = require("bun:jsc"); gc(); - if (heapStats().objectTypeCounts[type] <= count) return; + if ((heapStats().objectTypeCounts[type] ?? 0) <= count) return; gc(true); for (const wait = 20; maxWait > 0; maxWait -= wait) { - if (heapStats().objectTypeCounts[type] <= count) break; + if ((heapStats().objectTypeCounts[type] ?? 0) <= count) break; await Bun.sleep(wait); gc(); } - expect(heapStats().objectTypeCounts[type] || 0).toBeLessThanOrEqual(count); + expect(heapStats().objectTypeCounts[type] ?? 0).toBeLessThanOrEqual(count); } // we must ensure that finalizers are run diff --git a/test/js/node/vm/sourcetextmodule-leak.test.ts b/test/js/node/vm/sourcetextmodule-leak.test.ts index df1663b335c..e59974afaf9 100644 --- a/test/js/node/vm/sourcetextmodule-leak.test.ts +++ b/test/js/node/vm/sourcetextmodule-leak.test.ts @@ -1,30 +1,40 @@ const vm = require("vm"); const { describe, it, expect } = require("bun:test"); +const { isDebug } = require("harness"); + +// 50k×50KB ≈ 2.5 GB of source text — if module records leak their source we +// blow past the threshold. Debug builds parse/link ~50× slower, so scale down. +const ITERATIONS = isDebug ? 2_000 : 50_000; +const THRESHOLD_MB = isDebug ? 300 : 3000; describe("vm.SourceTextModule", () => { - it("shouldn't leak memory", async () => { - const initialUsage = process.memoryUsage.rss(); + it( + "shouldn't leak memory", + async () => { + const initialUsage = process.memoryUsage.rss(); - { - const source = `/*\n${Buffer.alloc(50_000, " * aaaaa\n").toString("utf8")}\n*/ Buffer.alloc(10, 'hello');`; + { + const source = `/*\n${Buffer.alloc(50_000, " * aaaaa\n").toString("utf8")}\n*/ Buffer.alloc(10, 'hello');`; - async function go(i) { - const mod = new vm.SourceTextModule(source + "//" + i, { - identifier: Buffer.alloc(64, i.toString()).toString("utf8"), - }); - await mod.link(() => {}); - await mod.evaluate(); - } + async function go(i) { + const mod = new vm.SourceTextModule(source + "//" + i, { + identifier: Buffer.alloc(64, i.toString()).toString("utf8"), + }); + await mod.link(() => {}); + await mod.evaluate(); + } - for (let i = 0; i < 50_000; ++i) { - await go(i); + for (let i = 0; i < ITERATIONS; ++i) { + await go(i); + } } - } - Bun.gc(true); + Bun.gc(true); - const finalUsage = process.memoryUsage.rss(); - const megabytes = Math.round(((finalUsage - initialUsage) / 1024 / 1024) * 100) / 100; - expect(megabytes).toBeLessThan(3000); - }); + const finalUsage = process.memoryUsage.rss(); + const megabytes = Math.round(((finalUsage - initialUsage) / 1024 / 1024) * 100) / 100; + expect(megabytes).toBeLessThan(THRESHOLD_MB); + }, + isDebug ? 60_000 : 30_000, + ); }); From f36e82a42c12a1fa2ae65f4b5253563b0e07ff98 Mon Sep 17 00:00:00 2001 From: Jarred Sumner Date: Wed, 29 Apr 2026 00:42:58 +0000 Subject: [PATCH 02/12] use WebKit preview build (pr-203) until WebKit#203 merges --- scripts/build/deps/webkit.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/build/deps/webkit.ts b/scripts/build/deps/webkit.ts index 6f059cff5d3..d804eb52f3e 100644 --- a/scripts/build/deps/webkit.ts +++ b/scripts/build/deps/webkit.ts @@ -3,7 +3,7 @@ * for local mode. Override via `--webkit-version=` to test a branch. * From https://github.com/oven-sh/WebKit releases. */ -export const WEBKIT_VERSION = "f7b52b080538b5c2779a0b20db2e5f9c17fc807c"; +export const WEBKIT_VERSION = "autobuild-preview-pr-203-f7b52b08"; /** * WebKit (JavaScriptCore) — the JS engine. From e17dbd331306f417aaeed5b46b2217d436919ed0 Mon Sep 17 00:00:00 2001 From: Jarred Sumner Date: Wed, 29 Apr 2026 02:01:16 +0000 Subject: [PATCH 03/12] bump WebKit preview to a5bf2e31 (pages_committed guard for symmetric decommit) --- scripts/build/deps/webkit.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/build/deps/webkit.ts b/scripts/build/deps/webkit.ts index d804eb52f3e..32d31a1c293 100644 --- a/scripts/build/deps/webkit.ts +++ b/scripts/build/deps/webkit.ts @@ -3,7 +3,7 @@ * for local mode. Override via `--webkit-version=` to test a branch. * From https://github.com/oven-sh/WebKit releases. */ -export const WEBKIT_VERSION = "autobuild-preview-pr-203-f7b52b08"; +export const WEBKIT_VERSION = "autobuild-preview-pr-203-a5bf2e31"; /** * WebKit (JavaScriptCore) — the JS engine. From 54b43d63308800da7fea70d8dd69c6a5ef5593aa Mon Sep 17 00:00:00 2001 From: Jarred Sumner Date: Wed, 29 Apr 2026 03:37:04 +0000 Subject: [PATCH 04/12] bump WebKit preview to 9dc26d0d (asymmetric decommit for TLC allocator pages) --- scripts/build/deps/webkit.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/build/deps/webkit.ts b/scripts/build/deps/webkit.ts index 32d31a1c293..dfde8f333d9 100644 --- a/scripts/build/deps/webkit.ts +++ b/scripts/build/deps/webkit.ts @@ -3,7 +3,7 @@ * for local mode. Override via `--webkit-version=` to test a branch. * From https://github.com/oven-sh/WebKit releases. */ -export const WEBKIT_VERSION = "autobuild-preview-pr-203-a5bf2e31"; +export const WEBKIT_VERSION = "autobuild-preview-pr-203-9dc26d0d"; /** * WebKit (JavaScriptCore) — the JS engine. From 67737fe6fe410bdeb0345256af0f5d7f6907163d Mon Sep 17 00:00:00 2001 From: Jarred Sumner Date: Wed, 29 Apr 2026 10:49:52 +0000 Subject: [PATCH 05/12] WebKit: switch from preview tag to merged main SHA 63bf3a4e (oven-sh/WebKit#203) --- scripts/build/deps/webkit.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/build/deps/webkit.ts b/scripts/build/deps/webkit.ts index dfde8f333d9..9b0d96e0a0c 100644 --- a/scripts/build/deps/webkit.ts +++ b/scripts/build/deps/webkit.ts @@ -3,7 +3,7 @@ * for local mode. Override via `--webkit-version=` to test a branch. * From https://github.com/oven-sh/WebKit releases. */ -export const WEBKIT_VERSION = "autobuild-preview-pr-203-9dc26d0d"; +export const WEBKIT_VERSION = "63bf3a4e22b9918994697fbafe32f185403de319"; /** * WebKit (JavaScriptCore) — the JS engine. From 0e7bbcd2cd7beb2e08fc5affbae6b2983cf46a8a Mon Sep 17 00:00:00 2001 From: Jarred Sumner Date: Wed, 29 Apr 2026 23:19:47 +0000 Subject: [PATCH 06/12] dns.zig: document intentional readable-preference when c-ares requests both directions --- src/bun.js/api/bun/dns.zig | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/bun.js/api/bun/dns.zig b/src/bun.js/api/bun/dns.zig index 255b17a2ef1..cc8518c0613 100644 --- a/src/bun.js/api/bun/dns.zig +++ b/src/bun.js/api/bun/dns.zig @@ -2559,6 +2559,12 @@ pub const Resolver = struct { // direction set differs from what's registered, drop the old // registration first so we never hold both .poll_readable and // .poll_writable on the same poll (unregister asserts on that). + // + // When c-ares asks for both, prefer readable. This intentionally + // degrades the rare both-flags case (queued TCP send that hit EAGAIN + // while a response is also pending) to readable-only — the send queue + // is driven on the next readable event when c-ares re-evaluates state. + // Matches the Windows path's precedence at AsyncDNSSocketCreate. const want_readable = readable; const want_writable = writable and !readable; const have_readable = poll.flags.contains(.poll_readable); From e054a4eff8b652e67b38a30fe58b6f4d8414d4c0 Mon Sep 17 00:00:00 2001 From: Jarred Sumner Date: Wed, 29 Apr 2026 23:25:03 +0000 Subject: [PATCH 07/12] =?UTF-8?q?Revert=20dns.zig=20FilePoll=20change=20?= =?UTF-8?q?=E2=80=94=20moving=20to=20separate=20PR=20with=20proper=20bidir?= =?UTF-8?q?ectional=20FilePoll=20support?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/bun.js/api/bun/dns.zig | 23 ++--------------------- 1 file changed, 2 insertions(+), 21 deletions(-) diff --git a/src/bun.js/api/bun/dns.zig b/src/bun.js/api/bun/dns.zig index cc8518c0613..0a393d7d593 100644 --- a/src/bun.js/api/bun/dns.zig +++ b/src/bun.js/api/bun/dns.zig @@ -2554,29 +2554,10 @@ pub const Resolver = struct { var poll = poll_entry.value_ptr.*; - // FilePoll is single-direction; c-ares can flip a TCP socket between - // writable (connecting) and readable (response). If the requested - // direction set differs from what's registered, drop the old - // registration first so we never hold both .poll_readable and - // .poll_writable on the same poll (unregister asserts on that). - // - // When c-ares asks for both, prefer readable. This intentionally - // degrades the rare both-flags case (queued TCP send that hit EAGAIN - // while a response is also pending) to readable-only — the send queue - // is driven on the next readable event when c-ares re-evaluates state. - // Matches the Windows path's precedence at AsyncDNSSocketCreate. - const want_readable = readable; - const want_writable = writable and !readable; - const have_readable = poll.flags.contains(.poll_readable); - const have_writable = poll.flags.contains(.poll_writable); - if ((have_readable and !want_readable) or (have_writable and !want_writable)) { - _ = poll.unregister(vm.event_loop_handle.?, false); - } - - if (want_readable and !poll.flags.contains(.poll_readable)) + if (readable and !poll.flags.contains(.poll_readable)) _ = poll.register(vm.event_loop_handle.?, .readable, false); - if (want_writable and !poll.flags.contains(.poll_writable)) + if (writable and !poll.flags.contains(.poll_writable)) _ = poll.register(vm.event_loop_handle.?, .writable, false); } } From 2c8c6619f643e991e48bd9469be74066bab6e51e Mon Sep 17 00:00:00 2001 From: Jarred Sumner Date: Thu, 30 Apr 2026 00:14:03 +0000 Subject: [PATCH 08/12] bump WebKit to preview pr-205 (upstream 7de78360 + WebKit#63906 IPInt call_indirect fix) --- scripts/build/deps/webkit.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/build/deps/webkit.ts b/scripts/build/deps/webkit.ts index 9b0d96e0a0c..0feb72e0eac 100644 --- a/scripts/build/deps/webkit.ts +++ b/scripts/build/deps/webkit.ts @@ -3,7 +3,7 @@ * for local mode. Override via `--webkit-version=` to test a branch. * From https://github.com/oven-sh/WebKit releases. */ -export const WEBKIT_VERSION = "63bf3a4e22b9918994697fbafe32f185403de319"; +export const WEBKIT_VERSION = "autobuild-preview-pr-205-361dace3"; /** * WebKit (JavaScriptCore) — the JS engine. From 63d06b1ac3886e98815ec2d9c4e2578b929d5d57 Mon Sep 17 00:00:00 2001 From: Jarred Sumner Date: Thu, 30 Apr 2026 23:30:48 +0000 Subject: [PATCH 09/12] bump WebKit to preview pr-205-fe9a377f (upstream 013da9aa + pas_process_is_shutting_down) --- scripts/build/deps/webkit.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/build/deps/webkit.ts b/scripts/build/deps/webkit.ts index 0feb72e0eac..b257ab1088f 100644 --- a/scripts/build/deps/webkit.ts +++ b/scripts/build/deps/webkit.ts @@ -3,7 +3,7 @@ * for local mode. Override via `--webkit-version=` to test a branch. * From https://github.com/oven-sh/WebKit releases. */ -export const WEBKIT_VERSION = "autobuild-preview-pr-205-361dace3"; +export const WEBKIT_VERSION = "autobuild-preview-pr-205-fe9a377f"; /** * WebKit (JavaScriptCore) — the JS engine. From 3ccad453dbcb7d287dc201fb87efa1d9eb930bc2 Mon Sep 17 00:00:00 2001 From: Jarred Sumner Date: Thu, 30 Apr 2026 23:35:47 +0000 Subject: [PATCH 10/12] bindings: MonotonicTime::approximateWallTime() -> approximate() (WebKit 23248aa3) --- src/bun.js/bindings/BunCPUProfiler.cpp | 8 ++++---- src/bun.js/bindings/BunObject.cpp | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/bun.js/bindings/BunCPUProfiler.cpp b/src/bun.js/bindings/BunCPUProfiler.cpp index b134af48a3b..3166d2ed9a8 100644 --- a/src/bun.js/bindings/BunCPUProfiler.cpp +++ b/src/bun.js/bindings/BunCPUProfiler.cpp @@ -49,7 +49,7 @@ void startCPUProfiler(JSC::VM& vm) { // Capture the wall clock time when profiling starts (before creating stopwatch) // This will be used as the profile's startTime - s_profilingStartTime = MonotonicTime::now().approximateWallTime().secondsSinceEpoch().value() * 1000000.0; + s_profilingStartTime = MonotonicTime::now().approximate().secondsSinceEpoch().value() * 1000000.0; // Create a stopwatch and start it auto stopwatch = WTF::Stopwatch::create(); @@ -364,7 +364,7 @@ void stopCPUProfiler(JSC::VM& vm, WTF::String* outJSON, WTF::String* outText) auto& stackTrace = stackTraces[idx]; if (stackTrace.frames.isEmpty()) { samples.append(1); - double currentTime = stackTrace.timestamp.approximateWallTime().secondsSinceEpoch().value() * 1000000.0; + double currentTime = stackTrace.timestamp.approximate().secondsSinceEpoch().value() * 1000000.0; double delta = std::max(0.0, currentTime - lastTime); timeDeltas.append(static_cast(delta)); lastTime = currentTime; @@ -538,7 +538,7 @@ void stopCPUProfiler(JSC::VM& vm, WTF::String* outJSON, WTF::String* outText) samples.append(currentParentId); - double currentTime = stackTrace.timestamp.approximateWallTime().secondsSinceEpoch().value() * 1000000.0; + double currentTime = stackTrace.timestamp.approximate().secondsSinceEpoch().value() * 1000000.0; double delta = std::max(0.0, currentTime - lastTime); timeDeltas.append(static_cast(delta)); lastTime = currentTime; @@ -629,7 +629,7 @@ void stopCPUProfiler(JSC::VM& vm, WTF::String* outJSON, WTF::String* outText) for (size_t idx : sortedIndices) { auto& stackTrace = stackTraces[idx]; - double currentTime = stackTrace.timestamp.approximateWallTime().secondsSinceEpoch().value() * 1000000.0; + double currentTime = stackTrace.timestamp.approximate().secondsSinceEpoch().value() * 1000000.0; long long deltaUs = static_cast(std::max(0.0, currentTime - lastTime)); totalTimeUs += deltaUs; lastTime = currentTime; diff --git a/src/bun.js/bindings/BunObject.cpp b/src/bun.js/bindings/BunObject.cpp index 9a65a7bbdc7..37e4bbb180f 100644 --- a/src/bun.js/bindings/BunObject.cpp +++ b/src/bun.js/bindings/BunObject.cpp @@ -666,7 +666,7 @@ JSC_DEFINE_HOST_FUNCTION(functionBunSleep, if (millisecondsValue.inherits()) { auto now = MonotonicTime::now(); - double milliseconds = uncheckedDowncast(millisecondsValue)->internalNumber() - now.approximateWallTime().secondsSinceEpoch().milliseconds(); + double milliseconds = uncheckedDowncast(millisecondsValue)->internalNumber() - now.approximate().secondsSinceEpoch().milliseconds(); millisecondsValue = JSC::jsNumber(milliseconds > 0 ? std::ceil(milliseconds) : 0); } From e1aaf4fc48a7cf1508edf8464b95ddf173a52e27 Mon Sep 17 00:00:00 2001 From: Jarred Sumner Date: Fri, 1 May 2026 09:26:13 +0000 Subject: [PATCH 11/12] bump WebKit to preview pr-205-2c2f4297 (truncateDoubleToInt UB fixes + GH-release gcc-13) --- scripts/build/deps/webkit.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/build/deps/webkit.ts b/scripts/build/deps/webkit.ts index b257ab1088f..571b78800f2 100644 --- a/scripts/build/deps/webkit.ts +++ b/scripts/build/deps/webkit.ts @@ -3,7 +3,7 @@ * for local mode. Override via `--webkit-version=` to test a branch. * From https://github.com/oven-sh/WebKit releases. */ -export const WEBKIT_VERSION = "autobuild-preview-pr-205-fe9a377f"; +export const WEBKIT_VERSION = "autobuild-preview-pr-205-2c2f4297"; /** * WebKit (JavaScriptCore) — the JS engine. From 039dd63bdf3d8a2c8a163444c476ac242301805e Mon Sep 17 00:00:00 2001 From: Jarred Sumner Date: Fri, 1 May 2026 11:37:07 +0000 Subject: [PATCH 12/12] WebKit: switch to merged main SHA 6ef83cb6 (oven-sh/WebKit#205) --- scripts/build/deps/webkit.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/build/deps/webkit.ts b/scripts/build/deps/webkit.ts index 571b78800f2..c2534855edb 100644 --- a/scripts/build/deps/webkit.ts +++ b/scripts/build/deps/webkit.ts @@ -3,7 +3,7 @@ * for local mode. Override via `--webkit-version=` to test a branch. * From https://github.com/oven-sh/WebKit releases. */ -export const WEBKIT_VERSION = "autobuild-preview-pr-205-2c2f4297"; +export const WEBKIT_VERSION = "6ef83cb658722ff1f33f4a4c9335fb094bed4c6b"; /** * WebKit (JavaScriptCore) — the JS engine.