From c56ab91a7a3780a13e7341bbb9a14b1674ca4b54 Mon Sep 17 00:00:00 2001 From: robobun <117481402+robobun@users.noreply.github.com> Date: Sun, 16 Aug 2026 23:37:57 +0000 Subject: [PATCH 1/4] Upgrade WebKit to 47f7250137c6 Pins WEBKIT_VERSION at the preview build of oven-sh/WebKit#455 (upstream WebKit 47f7250137c6) and adapts the embedding: - root.h: HandleSet.h no longer exists (Strong slots moved to StrongSet). - ScriptFetchParameters::Type gained Text ahead of the fork's HostDefined, so the ordinal Bun's transpiler emits for host-defined import types is 5; the static_asserts pin Text and HostDefined. - SyntheticModuleRecord::create() takes the record's SourceProviderSourceType. - scriptFetchParametersToImportAttributes() covers Type::Text. - StackBounds::currentThreadStackBounds() is private upstream; use the fork's embedder accessor. - Test pinning the JS-visible changes of this range and the fork-side decisions (type: "text" stays host-defined, NUMBER_OF_PROCESSORS is not honored). --- scripts/build/deps/webkit.ts | 2 +- src/js_printer/lib.rs | 7 +- .../bindings/BunAnalyzeTranspiledModule.cpp | 3 +- src/jsc/bindings/NodeVM.cpp | 4 + src/jsc/bindings/NodeVMSyntheticModule.cpp | 4 +- src/jsc/bindings/root.h | 2 +- src/jsc/bindings/wtf-bindings.cpp | 2 +- .../bun/jsc/webkit-upgrade-47f72501.test.ts | 141 ++++++++++++++++++ 8 files changed, 158 insertions(+), 7 deletions(-) create mode 100644 test/js/bun/jsc/webkit-upgrade-47f72501.test.ts diff --git a/scripts/build/deps/webkit.ts b/scripts/build/deps/webkit.ts index 8a12eefec688..30b898d0a4ca 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 = "c6cfe90c6064bd80a1916b844c2f092735cfc720"; +export const WEBKIT_VERSION = "autobuild-preview-pr-455-8fc20b18"; /** * WebKit (JavaScriptCore) — the JS engine. diff --git a/src/js_printer/lib.rs b/src/js_printer/lib.rs index a465407cf3f4..e64591d3ff8c 100644 --- a/src/js_printer/lib.rs +++ b/src/js_printer/lib.rs @@ -166,14 +166,17 @@ pub mod analyze_transpiled_module { Self(value.0) } /// JSC `ScriptFetchParameters::Type` value. `None` maps to `JavaScript` - /// (NodesAnalyzeModule's no-attribute default). + /// (NodesAnalyzeModule's no-attribute default). JSC's `Text` (4) is never + /// produced: with BUN_JSC_ADDITIONS `type: "text"` is host-defined like + /// every other non-json/wasm type. Pinned by the static_asserts in + /// BunAnalyzeTranspiledModule.cpp. #[inline] pub fn to_script_fetch_parameters_type(self) -> u8 { match self { Self::None | Self::Javascript => 1, Self::Webassembly => 2, Self::Json => 3, - _ => 4, + _ => 5, } } } diff --git a/src/jsc/bindings/BunAnalyzeTranspiledModule.cpp b/src/jsc/bindings/BunAnalyzeTranspiledModule.cpp index ee7b03a1c6b6..fdfaadacaf1c 100644 --- a/src/jsc/bindings/BunAnalyzeTranspiledModule.cpp +++ b/src/jsc/bindings/BunAnalyzeTranspiledModule.cpp @@ -113,7 +113,8 @@ extern "C" void JSC_JSModuleRecord__addRequestedModuleHostDefined(JSModuleRecord static_assert(static_cast(JSC::ScriptFetchParameters::Type::JavaScript) == 1, "ScriptFetchParameters::Type tag drift vs to_script_fetch_parameters_type()"); static_assert(static_cast(JSC::ScriptFetchParameters::Type::WebAssembly) == 2, "ScriptFetchParameters::Type tag drift vs to_script_fetch_parameters_type()"); static_assert(static_cast(JSC::ScriptFetchParameters::Type::JSON) == 3, "ScriptFetchParameters::Type tag drift vs to_script_fetch_parameters_type()"); -static_assert(static_cast(JSC::ScriptFetchParameters::Type::HostDefined) == 4, "ScriptFetchParameters::Type tag drift vs to_script_fetch_parameters_type()"); +static_assert(static_cast(JSC::ScriptFetchParameters::Type::Text) == 4, "ScriptFetchParameters::Type tag drift vs to_script_fetch_parameters_type()"); +static_assert(static_cast(JSC::ScriptFetchParameters::Type::HostDefined) == 5, "ScriptFetchParameters::Type tag drift vs to_script_fetch_parameters_type()"); extern "C" void JSC_JSModuleRecord__addImportEntrySingle(JSModuleRecord* moduleRecord, Identifier* identifierArray, uint32_t importName, uint32_t localName, uint32_t moduleName, uint8_t moduleRequestType) { diff --git a/src/jsc/bindings/NodeVM.cpp b/src/jsc/bindings/NodeVM.cpp index 4ed1521cc4bd..6c07d03f3a02 100644 --- a/src/jsc/bindings/NodeVM.cpp +++ b/src/jsc/bindings/NodeVM.cpp @@ -97,6 +97,10 @@ static JSValue scriptFetchParametersToImportAttributes(JSGlobalObject* globalObj case JSC::ScriptFetchParameters::Type::JavaScript: obj->putDirect(vm, vm.propertyNames->type, jsNontrivialString(vm, "javascript"_s)); break; + case JSC::ScriptFetchParameters::Type::Text: + // Unreachable with BUN_JSC_ADDITIONS (`type: "text"` parses as HostDefined); kept so the switch stays exhaustive. + obj->putDirect(vm, vm.propertyNames->type, jsNontrivialString(vm, "text"_s)); + break; case JSC::ScriptFetchParameters::Type::HostDefined: obj->putDirect(vm, vm.propertyNames->type, jsString(vm, params->hostDefinedImportType())); break; diff --git a/src/jsc/bindings/NodeVMSyntheticModule.cpp b/src/jsc/bindings/NodeVMSyntheticModule.cpp index 42ef186a5069..dc64df20033c 100644 --- a/src/jsc/bindings/NodeVMSyntheticModule.cpp +++ b/src/jsc/bindings/NodeVMSyntheticModule.cpp @@ -94,7 +94,9 @@ void NodeVMSyntheticModule::createModuleRecord(JSGlobalObject* globalObject) { VM& vm = globalObject->vm(); - SyntheticModuleRecord* moduleRecord = SyntheticModuleRecord::create(globalObject, vm, globalObject->syntheticModuleRecordStructure(), Identifier::fromString(vm, identifier())); + // The source type only feeds AbstractModuleRecord::moduleType(), which the loader attaches to + // errors as the failing module's kind; a vm.SyntheticModule is a JavaScript module in that sense. + SyntheticModuleRecord* moduleRecord = SyntheticModuleRecord::create(globalObject, vm, globalObject->syntheticModuleRecordStructure(), Identifier::fromString(vm, identifier()), SourceProviderSourceType::Module); m_moduleRecord.set(vm, this, moduleRecord); diff --git a/src/jsc/bindings/root.h b/src/jsc/bindings/root.h index c101d7e9d0ca..3c62835febcd 100644 --- a/src/jsc/bindings/root.h +++ b/src/jsc/bindings/root.h @@ -78,7 +78,7 @@ #include #include #include -#include +#include #include #include #endif diff --git a/src/jsc/bindings/wtf-bindings.cpp b/src/jsc/bindings/wtf-bindings.cpp index f14c9efed6b5..5001ed5a67d4 100644 --- a/src/jsc/bindings/wtf-bindings.cpp +++ b/src/jsc/bindings/wtf-bindings.cpp @@ -297,7 +297,7 @@ static thread_local WTF::StackBounds stackBoundsForCurrentThread = WTF::StackBou extern "C" [[ZIG_EXPORT(nothrow)]] void Bun__StackCheck__initialize() { - stackBoundsForCurrentThread = WTF::StackBounds::currentThreadStackBounds(); + stackBoundsForCurrentThread = WTF::StackBounds::currentThreadStackBoundsForEmbedder(); } extern "C" [[ZIG_EXPORT(nothrow)]] __attribute__((__always_inline__)) void* Bun__StackCheck__getMaxStack() diff --git a/test/js/bun/jsc/webkit-upgrade-47f72501.test.ts b/test/js/bun/jsc/webkit-upgrade-47f72501.test.ts new file mode 100644 index 000000000000..f858cc410e1a --- /dev/null +++ b/test/js/bun/jsc/webkit-upgrade-47f72501.test.ts @@ -0,0 +1,141 @@ +import { describe, expect, test } from "bun:test"; +import { bunEnv, bunExe, tempDir } from "harness"; + +// Coverage for the WebKit 47f7250137c6 sync. The RegExp case pins a behavior +// that only the new WebKit has; the import attribute cases pin the fork-side +// resolution of upstream's new ScriptFetchParameters::Type::Text (with +// BUN_JSC_ADDITIONS `type: "text"` must stay a host-defined import type, on +// both the JSC ModuleAnalyzer path and the BunTranspiledModule path). + +describe.concurrent("WebKit 47f7250137c6 upgrade", () => { + test("Iterator.prototype.chunks / windows / join and Iterator.zip / zipKeyed are enabled by default (webkit.org/b/321272)", () => { + expect([1, 2, 3, 4, 5].values().chunks(2).toArray()).toEqual([[1, 2], [3, 4], [5]]); + expect([1, 2, 3].values().windows(2).toArray()).toEqual([ + [1, 2], + [2, 3], + ]); + expect(["a", undefined, "c"].values().join("-")).toBe("a--c"); + expect( + Iterator.zip([ + [1, 2], + ["a", "b"], + ]).toArray(), + ).toEqual([ + [1, "a"], + [2, "b"], + ]); + expect(Iterator.zipKeyed({ n: [1, 2], s: ["a", "b"] }).toArray()).toEqual([ + { n: 1, s: "a" }, + { n: 2, s: "b" }, + ]); + // Spec alignment that came with the flag flip: a non-integral size throws instead of being coerced. + expect(() => [1].values().chunks("2" as any)).toThrow(TypeError); + }); + + test("intl-era-monthcode: the calendar list is the proposal's fixed set, islamic / islamic-rgsa are gone (webkit.org/b/319855)", () => { + expect(Intl.supportedValuesOf("calendar")).toEqual([ + "buddhist", + "chinese", + "coptic", + "dangi", + "ethioaa", + "ethiopic", + "gregory", + "hebrew", + "indian", + "islamic-civil", + "islamic-tbla", + "islamic-umalqura", + "iso8601", + "japanese", + "persian", + "roc", + ]); + expect(() => Temporal.PlainDate.from({ year: 2024, month: 1, day: 1, calendar: "islamic" })).toThrow(RangeError); + expect(Temporal.PlainDate.from({ year: 2024, month: 1, day: 1, calendar: "islamic-civil" }).calendarId).toBe( + "islamic-civil", + ); + }); + + test("NUMBER_OF_PROCESSORS does not change the reported core count", async () => { + // Upstream WTF::numberOfProcessorCores() started honoring this variable; the fork keeps that out of + // Bun builds because navigator.hardwareConcurrency / os.availableParallelism() are derived from it. + await using proc = Bun.spawn({ + cmd: [bunExe(), "-p", `navigator.hardwareConcurrency + " " + require("os").availableParallelism()`], + env: { ...bunEnv, NUMBER_OF_PROCESSORS: "1234" }, + stderr: "pipe", + }); + const [stdout, stderr, exitCode] = await Promise.all([proc.stdout.text(), proc.stderr.text(), proc.exited]); + expect(stderr).toBe(""); + const [hardwareConcurrency, availableParallelism] = stdout.trim().split(" ").map(Number); + expect(hardwareConcurrency).toBeGreaterThan(0); + expect(hardwareConcurrency).not.toBe(1234); + expect(availableParallelism).not.toBe(1234); + expect(exitCode).toBe(0); + }); + + test("v-mode && / -- apply to class strings when the operand is an inverted property escape (webkit.org/b/321252)", () => { + // The complement has no strings, so an intersection must drop the strings + // accumulated on the left and a subtraction must keep them. + expect(/^[\q{ab|c|1}&&\P{L}]$/v.test("ab")).toBe(false); + expect(/^[\q{ab|c|1}&&\P{L}]$/v.test("c")).toBe(false); + expect(/^[\q{ab|c|1}&&\P{L}]$/v.test("1")).toBe(true); + expect(/^[\q{ab|c|1}--\P{L}]$/v.test("ab")).toBe(true); + expect(/^[\q{ab|c|1}--\P{L}]$/v.test("c")).toBe(true); + expect(/^[\q{ab|c|1}--\P{L}]$/v.test("1")).toBe(false); + expect(/^[\p{L}&&\P{Lu}]$/v.test("A")).toBe(false); + expect(/^[\p{L}&&\P{Lu}]$/v.test("a")).toBe(true); + }); + + test('import ... with { type: "text" } of a .js file still returns its source', async () => { + using dir = tempDir("wk-text-attr", { + "mod.js": `export default "evaluated";`, + "entry.mjs": ` + import source from "./mod.js" with { type: "text" }; + import evaluated from "./mod.js"; + process.stdout.write(JSON.stringify({ source, evaluated })); + `, + }); + await using proc = Bun.spawn({ + cmd: [bunExe(), "entry.mjs"], + env: bunEnv, + cwd: String(dir), + stderr: "pipe", + }); + const [stdout, stderr, exitCode] = await Promise.all([proc.stdout.text(), proc.stderr.text(), proc.exited]); + expect(stderr).toBe(""); + expect(JSON.parse(stdout)).toEqual({ source: `export default "evaluated";`, evaluated: "evaluated" }); + expect(exitCode).toBe(0); + }); + + test('import ... with { type: "text" } of a .js file under bun test --isolate', async () => { + // --isolate takes the BunTranspiledModule path, where Bun's transpiler + // emits the ScriptFetchParameters::Type ordinal itself (HostDefined moved + // behind upstream's new Text member). In debug builds a wrong ordinal fails + // the record comparison with "Imports different"; in release the import + // would not resolve. + using dir = tempDir("wk-text-attr-isolate", { + "mod.js": `export default "evaluated";`, + "text.test.ts": ` + import source from "./mod.js" with { type: "text" }; + import evaluated from "./mod.js"; + import { test, expect } from "bun:test"; + test("text attribute", () => { + expect(source).toBe('export default "evaluated";'); + expect(evaluated).toBe("evaluated"); + }); + `, + }); + await using proc = Bun.spawn({ + cmd: [bunExe(), "test", "--isolate", "text.test.ts"], + env: { ...bunEnv, BUN_RUNTIME_TRANSPILER_CACHE_PATH: "0" }, + cwd: String(dir), + stderr: "pipe", + stdout: "pipe", + }); + const [, stderr, exitCode] = await Promise.all([proc.stdout.text(), proc.stderr.text(), proc.exited]); + expect(stderr).toContain("1 pass"); + expect(stderr).not.toContain("Imports different"); + expect(exitCode).toBe(0); + }); +}); From 94c27b75109a4d9e91f6009dd8678ebfe06a49ca Mon Sep 17 00:00:00 2001 From: Jarred Sumner Date: Mon, 17 Aug 2026 17:15:14 -0700 Subject: [PATCH 2/4] Delete test/js/bun/jsc/webkit-upgrade-47f72501.test.ts --- .../bun/jsc/webkit-upgrade-47f72501.test.ts | 141 ------------------ 1 file changed, 141 deletions(-) delete mode 100644 test/js/bun/jsc/webkit-upgrade-47f72501.test.ts diff --git a/test/js/bun/jsc/webkit-upgrade-47f72501.test.ts b/test/js/bun/jsc/webkit-upgrade-47f72501.test.ts deleted file mode 100644 index f858cc410e1a..000000000000 --- a/test/js/bun/jsc/webkit-upgrade-47f72501.test.ts +++ /dev/null @@ -1,141 +0,0 @@ -import { describe, expect, test } from "bun:test"; -import { bunEnv, bunExe, tempDir } from "harness"; - -// Coverage for the WebKit 47f7250137c6 sync. The RegExp case pins a behavior -// that only the new WebKit has; the import attribute cases pin the fork-side -// resolution of upstream's new ScriptFetchParameters::Type::Text (with -// BUN_JSC_ADDITIONS `type: "text"` must stay a host-defined import type, on -// both the JSC ModuleAnalyzer path and the BunTranspiledModule path). - -describe.concurrent("WebKit 47f7250137c6 upgrade", () => { - test("Iterator.prototype.chunks / windows / join and Iterator.zip / zipKeyed are enabled by default (webkit.org/b/321272)", () => { - expect([1, 2, 3, 4, 5].values().chunks(2).toArray()).toEqual([[1, 2], [3, 4], [5]]); - expect([1, 2, 3].values().windows(2).toArray()).toEqual([ - [1, 2], - [2, 3], - ]); - expect(["a", undefined, "c"].values().join("-")).toBe("a--c"); - expect( - Iterator.zip([ - [1, 2], - ["a", "b"], - ]).toArray(), - ).toEqual([ - [1, "a"], - [2, "b"], - ]); - expect(Iterator.zipKeyed({ n: [1, 2], s: ["a", "b"] }).toArray()).toEqual([ - { n: 1, s: "a" }, - { n: 2, s: "b" }, - ]); - // Spec alignment that came with the flag flip: a non-integral size throws instead of being coerced. - expect(() => [1].values().chunks("2" as any)).toThrow(TypeError); - }); - - test("intl-era-monthcode: the calendar list is the proposal's fixed set, islamic / islamic-rgsa are gone (webkit.org/b/319855)", () => { - expect(Intl.supportedValuesOf("calendar")).toEqual([ - "buddhist", - "chinese", - "coptic", - "dangi", - "ethioaa", - "ethiopic", - "gregory", - "hebrew", - "indian", - "islamic-civil", - "islamic-tbla", - "islamic-umalqura", - "iso8601", - "japanese", - "persian", - "roc", - ]); - expect(() => Temporal.PlainDate.from({ year: 2024, month: 1, day: 1, calendar: "islamic" })).toThrow(RangeError); - expect(Temporal.PlainDate.from({ year: 2024, month: 1, day: 1, calendar: "islamic-civil" }).calendarId).toBe( - "islamic-civil", - ); - }); - - test("NUMBER_OF_PROCESSORS does not change the reported core count", async () => { - // Upstream WTF::numberOfProcessorCores() started honoring this variable; the fork keeps that out of - // Bun builds because navigator.hardwareConcurrency / os.availableParallelism() are derived from it. - await using proc = Bun.spawn({ - cmd: [bunExe(), "-p", `navigator.hardwareConcurrency + " " + require("os").availableParallelism()`], - env: { ...bunEnv, NUMBER_OF_PROCESSORS: "1234" }, - stderr: "pipe", - }); - const [stdout, stderr, exitCode] = await Promise.all([proc.stdout.text(), proc.stderr.text(), proc.exited]); - expect(stderr).toBe(""); - const [hardwareConcurrency, availableParallelism] = stdout.trim().split(" ").map(Number); - expect(hardwareConcurrency).toBeGreaterThan(0); - expect(hardwareConcurrency).not.toBe(1234); - expect(availableParallelism).not.toBe(1234); - expect(exitCode).toBe(0); - }); - - test("v-mode && / -- apply to class strings when the operand is an inverted property escape (webkit.org/b/321252)", () => { - // The complement has no strings, so an intersection must drop the strings - // accumulated on the left and a subtraction must keep them. - expect(/^[\q{ab|c|1}&&\P{L}]$/v.test("ab")).toBe(false); - expect(/^[\q{ab|c|1}&&\P{L}]$/v.test("c")).toBe(false); - expect(/^[\q{ab|c|1}&&\P{L}]$/v.test("1")).toBe(true); - expect(/^[\q{ab|c|1}--\P{L}]$/v.test("ab")).toBe(true); - expect(/^[\q{ab|c|1}--\P{L}]$/v.test("c")).toBe(true); - expect(/^[\q{ab|c|1}--\P{L}]$/v.test("1")).toBe(false); - expect(/^[\p{L}&&\P{Lu}]$/v.test("A")).toBe(false); - expect(/^[\p{L}&&\P{Lu}]$/v.test("a")).toBe(true); - }); - - test('import ... with { type: "text" } of a .js file still returns its source', async () => { - using dir = tempDir("wk-text-attr", { - "mod.js": `export default "evaluated";`, - "entry.mjs": ` - import source from "./mod.js" with { type: "text" }; - import evaluated from "./mod.js"; - process.stdout.write(JSON.stringify({ source, evaluated })); - `, - }); - await using proc = Bun.spawn({ - cmd: [bunExe(), "entry.mjs"], - env: bunEnv, - cwd: String(dir), - stderr: "pipe", - }); - const [stdout, stderr, exitCode] = await Promise.all([proc.stdout.text(), proc.stderr.text(), proc.exited]); - expect(stderr).toBe(""); - expect(JSON.parse(stdout)).toEqual({ source: `export default "evaluated";`, evaluated: "evaluated" }); - expect(exitCode).toBe(0); - }); - - test('import ... with { type: "text" } of a .js file under bun test --isolate', async () => { - // --isolate takes the BunTranspiledModule path, where Bun's transpiler - // emits the ScriptFetchParameters::Type ordinal itself (HostDefined moved - // behind upstream's new Text member). In debug builds a wrong ordinal fails - // the record comparison with "Imports different"; in release the import - // would not resolve. - using dir = tempDir("wk-text-attr-isolate", { - "mod.js": `export default "evaluated";`, - "text.test.ts": ` - import source from "./mod.js" with { type: "text" }; - import evaluated from "./mod.js"; - import { test, expect } from "bun:test"; - test("text attribute", () => { - expect(source).toBe('export default "evaluated";'); - expect(evaluated).toBe("evaluated"); - }); - `, - }); - await using proc = Bun.spawn({ - cmd: [bunExe(), "test", "--isolate", "text.test.ts"], - env: { ...bunEnv, BUN_RUNTIME_TRANSPILER_CACHE_PATH: "0" }, - cwd: String(dir), - stderr: "pipe", - stdout: "pipe", - }); - const [, stderr, exitCode] = await Promise.all([proc.stdout.text(), proc.stderr.text(), proc.exited]); - expect(stderr).toContain("1 pass"); - expect(stderr).not.toContain("Imports different"); - expect(exitCode).toBe(0); - }); -}); From 42d32febf1500415b4bc7ee59f91d35cac2f9c40 Mon Sep 17 00:00:00 2001 From: Jarred Sumner Date: Tue, 18 Aug 2026 00:16:40 +0000 Subject: [PATCH 3/4] Bump WebKit to eeab04040fa6 (47f7250137c6 upgrade merge + URLParser Unicode/percent host SIMD scan, oven-sh/WebKit#463) No-Verification-Needed: version pin bump --- 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 30b898d0a4ca..f8c9ad85d811 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-455-8fc20b18"; +export const WEBKIT_VERSION = "eeab04040fa61fd595695980f9d054b7fc0ed855"; /** * WebKit (JavaScriptCore) — the JS engine. From 95d581dd2f7cc9088e79d91320cb81c9f76d82eb Mon Sep 17 00:00:00 2001 From: robobun <117481402+robobun@users.noreply.github.com> Date: Tue, 18 Aug 2026 00:33:36 +0000 Subject: [PATCH 4/4] Acknowledge the binary size increase of the WebKit upgrade [skip size check] Stripped binaries grow 448 KB to 800 KB per target against main (0.6% to 0.9%). In the non-LTO linux-x64 WebKit prebuilts the object code of libJavaScriptCore.a grows a net 90 KB spread over 117 object files (StrongSet replacing HandleSet, typed array sort, intl-era-monthcode, memory64/table64, Air analyses, builtins) and libWTF.a 2 KB; the rest of the delta is LTO inlining of the changed engine headers into Bun's own objects. The zipped artifacts are smaller than main's.